[
  {
    "path": ".github/workflows/build.yml",
    "content": "name: Build Bonita Community\non:\n  push:\n    branches:\n      - dev\n  pull_request:\n    types: [opened, synchronize, reopened]\njobs:\n  build:\n    name: Build Bonita Community\n    runs-on: ubuntu-latest\n    steps:\n\n      - uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n\n      - name: Set up JDK 17\n        uses: actions/setup-java@v4\n        with:\n          distribution: temurin\n          java-version: 17\n\n      - name: 🔧 Setup Gradle\n        uses: gradle/actions/setup-gradle@v3\n        with:\n          cache-read-only: false\n\n      - name: ⚙️ Run Community build & ✔️ unit tests\n        run: ./gradlew build\n\n      - name: Run Integration tests\n        run: ./gradlew iT\n\n      - name: 🚀 Publish Test Report\n        uses: EnricoMi/publish-unit-test-result-action@v2\n        if: always()\n        with:\n          files: '**/build/test-results/**/*.xml'\n"
  },
  {
    "path": ".gitignore",
    "content": "### virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml\nhs_err_pid*\n\n### OSX template\n.DS_Store\n.AppleDouble\n.LSOverride\n\n### Maven template\ntarget\npom.xml.tag\npom.xml.releaseBackup\npom.xml.versionsBackup\npom.xml.next\nrelease.properties\ndependency-reduced-pom.xml\nbuildNumber.properties\n.mvn/timing.properties\n\n### Intellij\n*.iml\n.idea\n*.ipr\n*.iws\n.shelf\n\n### Eclipse\n.project\n.settings\n.classpath\n\n### Temporary files\n*.orig\n**/bin\n*.tmp\n*~\n\n### bonita template\nengine-sp-it.exec\nFILEPATH.*.db\nTemporary*.java\ngenerated\nh2databasedir\n\n### gradle\nbuild\n.gradle\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "                  GNU LESSER GENERAL PUBLIC LICENSE\n                       Version 2.1, February 1999\n\n Copyright (C) 1991, 1999 Free Software Foundation, Inc.\n 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n[This is the first released version of the Lesser GPL.  It also counts\n as the successor of the GNU Library Public License, version 2, hence\n the version number 2.1.]\n\n                            Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicenses are intended to guarantee your freedom to share and change\nfree software--to make sure the software is free for all its users.\n\n  This license, the Lesser General Public License, applies to some\nspecially designated software packages--typically libraries--of the\nFree Software Foundation and other authors who decide to use it.  You\ncan use it too, but we suggest you first think carefully about whether\nthis license or the ordinary General Public License is the better\nstrategy to use in any particular case, based on the explanations below.\n\n  When we speak of free software, we are referring to freedom of use,\nnot price.  Our General Public Licenses are designed to make sure that\nyou have the freedom to distribute copies of free software (and charge\nfor this service if you wish); that you receive source code or can get\nit if you want it; that you can change the software and use pieces of\nit in new free programs; and that you are informed that you can do\nthese things.\n\n  To protect your rights, we need to make restrictions that forbid\ndistributors to deny you these rights or to ask you to surrender these\nrights.  These restrictions translate to certain responsibilities for\nyou if you distribute copies of the library or if you modify it.\n\n  For example, if you distribute copies of the library, whether gratis\nor for a fee, you must give the recipients all the rights that we gave\nyou.  You must make sure that they, too, receive or can get the source\ncode.  If you link other code with the library, you must provide\ncomplete object files to the recipients, so that they can relink them\nwith the library after making changes to the library and recompiling\nit.  And you must show them these terms so they know their rights.\n\n  We protect your rights with a two-step method: (1) we copyright the\nlibrary, and (2) we offer you this license, which gives you legal\npermission to copy, distribute and/or modify the library.\n\n  To protect each distributor, we want to make it very clear that\nthere is no warranty for the free library.  Also, if the library is\nmodified by someone else and passed on, the recipients should know\nthat what they have is not the original version, so that the original\nauthor's reputation will not be affected by problems that might be\nintroduced by others.\n\f\n  Finally, software patents pose a constant threat to the existence of\nany free program.  We wish to make sure that a company cannot\neffectively restrict the users of a free program by obtaining a\nrestrictive license from a patent holder.  Therefore, we insist that\nany patent license obtained for a version of the library must be\nconsistent with the full freedom of use specified in this license.\n\n  Most GNU software, including some libraries, is covered by the\nordinary GNU General Public License.  This license, the GNU Lesser\nGeneral Public License, applies to certain designated libraries, and\nis quite different from the ordinary General Public License.  We use\nthis license for certain libraries in order to permit linking those\nlibraries into non-free programs.\n\n  When a program is linked with a library, whether statically or using\na shared library, the combination of the two is legally speaking a\ncombined work, a derivative of the original library.  The ordinary\nGeneral Public License therefore permits such linking only if the\nentire combination fits its criteria of freedom.  The Lesser General\nPublic License permits more lax criteria for linking other code with\nthe library.\n\n  We call this license the \"Lesser\" General Public License because it\ndoes Less to protect the user's freedom than the ordinary General\nPublic License.  It also provides other free software developers Less\nof an advantage over competing non-free programs.  These disadvantages\nare the reason we use the ordinary General Public License for many\nlibraries.  However, the Lesser license provides advantages in certain\nspecial circumstances.\n\n  For example, on rare occasions, there may be a special need to\nencourage the widest possible use of a certain library, so that it becomes\na de-facto standard.  To achieve this, non-free programs must be\nallowed to use the library.  A more frequent case is that a free\nlibrary does the same job as widely used non-free libraries.  In this\ncase, there is little to gain by limiting the free library to free\nsoftware only, so we use the Lesser General Public License.\n\n  In other cases, permission to use a particular library in non-free\nprograms enables a greater number of people to use a large body of\nfree software.  For example, permission to use the GNU C Library in\nnon-free programs enables many more people to use the whole GNU\noperating system, as well as its variant, the GNU/Linux operating\nsystem.\n\n  Although the Lesser General Public License is Less protective of the\nusers' freedom, it does ensure that the user of a program that is\nlinked with the Library has the freedom and the wherewithal to run\nthat program using a modified version of the Library.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.  Pay close attention to the difference between a\n\"work based on the library\" and a \"work that uses the library\".  The\nformer contains code derived from the library, whereas the latter must\nbe combined with the library in order to run.\n\f\n                  GNU LESSER GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License Agreement applies to any software library or other\nprogram which contains a notice placed by the copyright holder or\nother authorized party saying it may be distributed under the terms of\nthis Lesser General Public License (also called \"this License\").\nEach licensee is addressed as \"you\".\n\n  A \"library\" means a collection of software functions and/or data\nprepared so as to be conveniently linked with application programs\n(which use some of those functions and data) to form executables.\n\n  The \"Library\", below, refers to any such software library or work\nwhich has been distributed under these terms.  A \"work based on the\nLibrary\" means either the Library or any derivative work under\ncopyright law: that is to say, a work containing the Library or a\nportion of it, either verbatim or with modifications and/or translated\nstraightforwardly into another language.  (Hereinafter, translation is\nincluded without limitation in the term \"modification\".)\n\n  \"Source code\" for a work means the preferred form of the work for\nmaking modifications to it.  For a library, complete source code means\nall the source code for all modules it contains, plus any associated\ninterface definition files, plus the scripts used to control compilation\nand installation of the library.\n\n  Activities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning a program using the Library is not restricted, and output from\nsuch a program is covered only if its contents constitute a work based\non the Library (independent of the use of the Library in a tool for\nwriting it).  Whether that is true depends on what the Library does\nand what the program that uses the Library does.\n\n  1. You may copy and distribute verbatim copies of the Library's\ncomplete source code as you receive it, in any medium, provided that\nyou conspicuously and appropriately publish on each copy an\nappropriate copyright notice and disclaimer of warranty; keep intact\nall the notices that refer to this License and to the absence of any\nwarranty; and distribute a copy of this License along with the\nLibrary.\n\n  You may charge a fee for the physical act of transferring a copy,\nand you may at your option offer warranty protection in exchange for a\nfee.\n\f\n  2. You may modify your copy or copies of the Library or any portion\nof it, thus forming a work based on the Library, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) The modified work must itself be a software library.\n\n    b) You must cause the files modified to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    c) You must cause the whole of the work to be licensed at no\n    charge to all third parties under the terms of this License.\n\n    d) If a facility in the modified Library refers to a function or a\n    table of data to be supplied by an application program that uses\n    the facility, other than as an argument passed when the facility\n    is invoked, then you must make a good faith effort to ensure that,\n    in the event an application does not supply such function or\n    table, the facility still operates, and performs whatever part of\n    its purpose remains meaningful.\n\n    (For example, a function in a library to compute square roots has\n    a purpose that is entirely well-defined independent of the\n    application.  Therefore, Subsection 2d requires that any\n    application-supplied function or table used by this function must\n    be optional: if the application does not supply it, the square\n    root function must still compute square roots.)\n\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Library,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Library, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote\nit.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Library.\n\nIn addition, mere aggregation of another work not based on the Library\nwith the Library (or with a work based on the Library) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may opt to apply the terms of the ordinary GNU General Public\nLicense instead of this License to a given copy of the Library.  To do\nthis, you must alter all the notices that refer to this License, so\nthat they refer to the ordinary GNU General Public License, version 2,\ninstead of to this License.  (If a newer version than version 2 of the\nordinary GNU General Public License has appeared, then you can specify\nthat version instead if you wish.)  Do not make any other change in\nthese notices.\n\f\n  Once this change is made in a given copy, it is irreversible for\nthat copy, so the ordinary GNU General Public License applies to all\nsubsequent copies and derivative works made from that copy.\n\n  This option is useful when you wish to copy part of the code of\nthe Library into a program that is not a library.\n\n  4. You may copy and distribute the Library (or a portion or\nderivative of it, under Section 2) in object code or executable form\nunder the terms of Sections 1 and 2 above provided that you accompany\nit with the complete corresponding machine-readable source code, which\nmust be distributed under the terms of Sections 1 and 2 above on a\nmedium customarily used for software interchange.\n\n  If distribution of object code is made by offering access to copy\nfrom a designated place, then offering equivalent access to copy the\nsource code from the same place satisfies the requirement to\ndistribute the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\n  5. A program that contains no derivative of any portion of the\nLibrary, but is designed to work with the Library by being compiled or\nlinked with it, is called a \"work that uses the Library\".  Such a\nwork, in isolation, is not a derivative work of the Library, and\ntherefore falls outside the scope of this License.\n\n  However, linking a \"work that uses the Library\" with the Library\ncreates an executable that is a derivative of the Library (because it\ncontains portions of the Library), rather than a \"work that uses the\nlibrary\".  The executable is therefore covered by this License.\nSection 6 states terms for distribution of such executables.\n\n  When a \"work that uses the Library\" uses material from a header file\nthat is part of the Library, the object code for the work may be a\nderivative work of the Library even though the source code is not.\nWhether this is true is especially significant if the work can be\nlinked without the Library, or if the work is itself a library.  The\nthreshold for this to be true is not precisely defined by law.\n\n  If such an object file uses only numerical parameters, data\nstructure layouts and accessors, and small macros and small inline\nfunctions (ten lines or less in length), then the use of the object\nfile is unrestricted, regardless of whether it is legally a derivative\nwork.  (Executables containing this object code plus portions of the\nLibrary will still fall under Section 6.)\n\n  Otherwise, if the work is a derivative of the Library, you may\ndistribute the object code for the work under the terms of Section 6.\nAny executables containing that work also fall under Section 6,\nwhether or not they are linked directly with the Library itself.\n\f\n  6. As an exception to the Sections above, you may also combine or\nlink a \"work that uses the Library\" with the Library to produce a\nwork containing portions of the Library, and distribute that work\nunder terms of your choice, provided that the terms permit\nmodification of the work for the customer's own use and reverse\nengineering for debugging such modifications.\n\n  You must give prominent notice with each copy of the work that the\nLibrary is used in it and that the Library and its use are covered by\nthis License.  You must supply a copy of this License.  If the work\nduring execution displays copyright notices, you must include the\ncopyright notice for the Library among them, as well as a reference\ndirecting the user to the copy of this License.  Also, you must do one\nof these things:\n\n    a) Accompany the work with the complete corresponding\n    machine-readable source code for the Library including whatever\n    changes were used in the work (which must be distributed under\n    Sections 1 and 2 above); and, if the work is an executable linked\n    with the Library, with the complete machine-readable \"work that\n    uses the Library\", as object code and/or source code, so that the\n    user can modify the Library and then relink to produce a modified\n    executable containing the modified Library.  (It is understood\n    that the user who changes the contents of definitions files in the\n    Library will not necessarily be able to recompile the application\n    to use the modified definitions.)\n\n    b) Use a suitable shared library mechanism for linking with the\n    Library.  A suitable mechanism is one that (1) uses at run time a\n    copy of the library already present on the user's computer system,\n    rather than copying library functions into the executable, and (2)\n    will operate properly with a modified version of the library, if\n    the user installs one, as long as the modified version is\n    interface-compatible with the version that the work was made with.\n\n    c) Accompany the work with a written offer, valid for at\n    least three years, to give the same user the materials\n    specified in Subsection 6a, above, for a charge no more\n    than the cost of performing this distribution.\n\n    d) If distribution of the work is made by offering access to copy\n    from a designated place, offer equivalent access to copy the above\n    specified materials from the same place.\n\n    e) Verify that the user has already received a copy of these\n    materials or that you have already sent this user a copy.\n\n  For an executable, the required form of the \"work that uses the\nLibrary\" must include any data and utility programs needed for\nreproducing the executable from it.  However, as a special exception,\nthe materials to be distributed need not include anything that is\nnormally distributed (in either source or binary form) with the major\ncomponents (compiler, kernel, and so on) of the operating system on\nwhich the executable runs, unless that component itself accompanies\nthe executable.\n\n  It may happen that this requirement contradicts the license\nrestrictions of other proprietary libraries that do not normally\naccompany the operating system.  Such a contradiction means you cannot\nuse both them and the Library together in an executable that you\ndistribute.\n\f\n  7. You may place library facilities that are a work based on the\nLibrary side-by-side in a single library together with other library\nfacilities not covered by this License, and distribute such a combined\nlibrary, provided that the separate distribution of the work based on\nthe Library and of the other library facilities is otherwise\npermitted, and provided that you do these two things:\n\n    a) Accompany the combined library with a copy of the same work\n    based on the Library, uncombined with any other library\n    facilities.  This must be distributed under the terms of the\n    Sections above.\n\n    b) Give prominent notice with the combined library of the fact\n    that part of it is a work based on the Library, and explaining\n    where to find the accompanying uncombined form of the same work.\n\n  8. You may not copy, modify, sublicense, link with, or distribute\nthe Library except as expressly provided under this License.  Any\nattempt otherwise to copy, modify, sublicense, link with, or\ndistribute the Library is void, and will automatically terminate your\nrights under this License.  However, parties who have received copies,\nor rights, from you under this License will not have their licenses\nterminated so long as such parties remain in full compliance.\n\n  9. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Library or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Library (or any work based on the\nLibrary), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Library or works based on it.\n\n  10. Each time you redistribute the Library (or any work based on the\nLibrary), the recipient automatically receives a license from the\noriginal licensor to copy, distribute, link with or modify the Library\nsubject to these terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties with\nthis License.\n\f\n  11. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Library at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Library by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Library.\n\nIf any portion of this section is held invalid or unenforceable under any\nparticular circumstance, the balance of the section is intended to apply,\nand the section as a whole is intended to apply in other circumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\n  12. If the distribution and/or use of the Library is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Library under this License may add\nan explicit geographical distribution limitation excluding those countries,\nso that distribution is permitted only in or among countries not thus\nexcluded.  In such case, this License incorporates the limitation as if\nwritten in the body of this License.\n\n  13. The Free Software Foundation may publish revised and/or new\nversions of the Lesser General Public License from time to time.\nSuch new versions will be similar in spirit to the present version,\nbut may differ in detail to address new problems or concerns.\n\nEach version is given a distinguishing version number.  If the Library\nspecifies a version number of this License which applies to it and\n\"any later version\", you have the option of following the terms and\nconditions either of that version or of any later version published by\nthe Free Software Foundation.  If the Library does not specify a\nlicense version number, you may choose any version ever published by\nthe Free Software Foundation.\n\f\n  14. If you wish to incorporate parts of the Library into other free\nprograms whose distribution conditions are incompatible with these,\nwrite to the author to ask for permission.  For software which is\ncopyrighted by the Free Software Foundation, write to the Free\nSoftware Foundation; we sometimes make exceptions for this.  Our\ndecision will be guided by the two goals of preserving the free status\nof all derivatives of our free software and of promoting the sharing\nand reuse of software generally.\n\n                            NO WARRANTY\n\n  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO\nWARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.\nEXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR\nOTHER PARTIES PROVIDE THE LIBRARY \"AS IS\" WITHOUT WARRANTY OF ANY\nKIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE\nLIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME\nTHE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN\nWRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY\nAND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU\nFOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR\nCONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE\nLIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING\nRENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A\nFAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF\nSUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH\nDAMAGES.\n\n                     END OF TERMS AND CONDITIONS\n\f\n           How to Apply These Terms to Your New Libraries\n\n  If you develop a new library, and you want it to be of the greatest\npossible use to the public, we recommend making it free software that\neveryone can redistribute and change.  You can do so by permitting\nredistribution under these terms (or, alternatively, under the terms of the\nordinary General Public License).\n\n  To apply these terms, attach the following notices to the library.  It is\nsafest to attach them to the start of each source file to most effectively\nconvey the exclusion of warranty; and each file should have at least the\n\"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the library's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This library is free software; you can redistribute it and/or\n    modify it under the terms of the GNU Lesser General Public\n    License as published by the Free Software Foundation; either\n    version 2.1 of the License, or (at your option) any later version.\n\n    This library is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n    Lesser General Public License for more details.\n\n    You should have received a copy of the GNU Lesser General Public\n    License along with this library; if not, write to the Free Software\n    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA\n\nAlso add information on how to contact you by electronic and paper mail.\n\nYou should also get your employer (if you work as a programmer) or your\nschool, if any, to sign a \"copyright disclaimer\" for the library, if\nnecessary.  Here is a sample; alter the names:\n\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the\n  library `Frob' (a library for tweaking knobs) written by James Random Hacker.\n\n  <signature of Ty Coon>, 1 April 1990\n  Ty Coon, President of Vice\n\nThat's all there is to it!\n"
  },
  {
    "path": "README.md",
    "content": "# Bonita Engine\n\n[![Build](https://github.com/bonitasoft/bonita-engine/actions/workflows/build.yml/badge.svg)](https://github.com/bonitasoft/bonita-engine/actions/workflows/build.yml)\n\n[![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.bonitasoft.engine/bonita-server/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.bonitasoft.engine/bonita-server)\n\nDeploy, execute, manage applications made with Bonita Studio.\n\n\n## Using the Engine\n\nThe engine is included as part of either [Bonita Studio][downloads] or [Bonita Runtime][downloads], and executes the BPMN process logic.\nThe engine can however be included as a standalone dependency in a custom Application, as explained [here][standalone]\n\n## Running the Project\n\n### Prerequisites\n>     Java JDK 17 (to compile), and 17 (to run)\n\nThis project bundles the [Gradle Wrapper][wrapper], so the `gradlew` script is available at\nthe project root.\n\n### Compiling\n\nJust run the following Gradle command:\n```\n./gradlew build\n```\n\nTo be able to successfully build other Bonita components that use the Engine, run:\n```\n ./gradlew publishToMavenLocal\n```\nAmong other things, it also generates the javadoc used by Bonita Studio.\n\nThe command above runs all unit tests. To skip them, add the `-x test`\noption.\n\n### Version\nVersion is declared in gradle.properties\n\nTo override the current version on build, use the parameter **-Pversion** like:\n\n```\n ./gradlew -Pversion=7.9.3 <tasks>\n```\n\n### Extra repositories\n\nrepositories can be added using comma separated list of repositories\nusing property `extraRepositories` in format `repo_name::repo_url`\n\ncredentials can be passed using properties `repo_nameUsername` and\n`repo_namePassword`\n\nit can be configured using `-PextraRepositories=` or gradle.properties\nfile.\n\nexample of gradle properties set in `~/.gradle/gradle.properties`\n\n```properties\nextraRepositories=releases::https://repo1/releases,snapshots::https://repo2/snapshots/\nreleasesUsername=username\nreleasesPassword=password\nsnapshotsUsername=username\nsnapshotsPassword=password\n```\n\nThe same can be done for publishing repository (single repo) using property `altDeploymentRepository`\n\n\n### Running unit / integration tests\n\nTo run all **unit + integration tests** (on the default embedded H2\ndatabase), run the following command:\n```bash\n./gradlew test integrationTest\n```\n\n### Test Retry Configuration\n\nThe project uses the [Gradle Test Retry Plugin](https://plugins.gradle.org/plugin/org.gradle.test-retry) to automatically retry failed tests and detect flaky tests.\n\n#### Default Retry Behavior\n\nBy default:\n- Failed tests are retried up to **2 more times**\n- Build fails if more than **6 tests** fail in one round\n- Flaky tests (pass on retry) fail the build locally but not in CI\n- Only applies to '*IT' test classes (tasks `integrationTest` and database's: `postgresDatabaseTest`, etc.)\n\n#### Overriding Retry Settings\n\n```bash\n# Retry up to 5 times instead of 2\n./gradlew integrationTest -PtestRetryMaxRetries=5\n\n# Retry and allow up to 20 failures before stopping\n./gradlew integrationTest -PtestRetryMaxRetries=5 -PtestRetryMaxFailures=20\n\n# Disable retry (set to 0)\n./gradlew integrationTest -PtestRetryMaxRetries=0\n\n# Don't fail on flaky tests (tests that pass on retry). Example for database tests:\n./gradlew postgresDatabaseTest -PtestRetryFailOnFlaky=false\n```\n\n#### Important Note\n\nThe test-retry plugin is designed to **detect** flaky tests, not to mask them. Always investigate and fix the root cause of flaky tests rather than just relying on retries.\n\n\n## Project Structure\nThe project is composed of several modules. Unit tests are contained in the modules, integration tests are regrouped in bonita-integration-tests.\n\n* `bonita-engine-spring-boot-starter` : Run the engine in standalone mode using Spring boot, see [documentation][standalone]\n* `bonita-engine-standalone` : Run the engine in standalone programmatically, see [documentation][standalone]\n* `bonita-test-api` : Junit Rule to include the engine in your tests\n* `bpm` : Services related to bpm process execution\n* `buildSrc` : Internal Gradle plugins used to build Bonita Engine\n* `platform` : Services that handle the platform creation/configuration\n* `services` : Generic services used by the engine\n\n## How to contribute\nIn order to contribute to the project, read the [guide][guide].\nTo report an issue use the official [bugtracker][bugtracker].\n\n\n\n\n[downloads]: https://www.ofelia.com/downloads\n[standalone]: https://documentation.ofelia.com/bonita/latest/runtime/embed-engine\n[guide]: https://github.com/bonitasoft/bonita-developer-resources/blob/master/CONTRIBUTING.MD\n[wrapper]: https://docs.gradle.org/current/userguide/gradle_wrapper.html\n[bugtracker]: https://bonita.atlassian.net/projects/BBPMC/issues\n"
  },
  {
    "path": "bonita-engine/build.gradle",
    "content": "import org.bonitasoft.engine.gradle.PomUtils\n\n//this module is published to be imported as dependency management\nplugins {\n    id \"io.spring.dependency-management\" version \"1.1.0\"\n}\ndependencyManagement {\n    dependencies {\n        imports {\n            mavenBom libs.jacksonBom.get() as String\n            mavenBom libs.groovyBom.get() as String\n            mavenBom libs.bonitaArtifactsModelBom.get() as String\n        }\n        dependencySet(group: 'org.slf4j', version: libs.versions.slf4jVersion.get()) {\n            entry 'slf4j-api'\n            entry 'slf4j-jdk14'\n        }\n        dependency libs.h2.get() as String\n        dependency libs.ehCache.get() as String\n\n        dependencySet(group: 'org.springframework', version: libs.versions.springVersion.get()) {\n            entry 'spring-context'\n            entry 'spring-core'\n            entry 'spring-web'\n        }\n        dependencySet(group: \"org.springframework.boot\", version: libs.versions.springBootVersion.get()) {\n            entry \"spring-boot-starter-jdbc\"\n            entry \"spring-boot-autoconfigure\"\n        }\n        dependency(libs.hibernateCore.get() as String) {\n            exclude 'org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec'\n        }\n        dependency(libs.hibernateJCache.get() as String)\n        dependency(libs.jcache.get() as String)\n        dependency(libs.javaxPersistenceApi.get() as String)\n        dependency libs.commonsIO.get() as String\n        dependency libs.commonsFileUpload.get() as String\n        dependency(libs.commonsBeanUtils.get() as String) {\n            exclude 'commons-collections:commons-collections'\n        }\n        dependency(libs.commonsCLI.get() as String)\n        dependency libs.commonsLang.get() as String\n        dependency libs.commonsCollections.get() as String\n        dependency libs.jakartaTransactionApi.get() as String\n        dependency libs.javassist.get() as String\n        dependencySet(group: 'org.apache.httpcomponents', version: libs.versions.httpComponentsVersion.get()) {\n            entry 'httpclient'\n            entry 'httpmime'\n        }\n        dependency libs.xstream.get() as String\n        dependencySet(group: 'org.apache.tomcat', version: libs.versions.tomcatVersion.get()) {\n            entry 'tomcat-dbcp'\n            entry 'tomcat'\n            entry 'tomcat-catalina'\n        }\n        dependencySet(group: 'org.apache.tomcat.embed', version: libs.versions.tomcatVersion.get()) {\n            entry 'tomcat-embed-core'\n        }\n        dependency libs.narayanaJta.get() as String\n        dependency libs.jakartaActivation.get() as String\n        dependency(libs.quartz.get() as String)\n        dependency(libs.eclipseCompiler.get() as String)\n        dependency(libs.javaxAnnotations.get() as String)\n        dependency(libs.micrometerCore.get() as String)\n        dependency(libs.micrometerRegistryJmx.get() as String)\n        dependency(libs.micrometerRegistryPrometheus.get() as String)\n        dependency(libs.hazelcast.get() as String)\n        dependency(libs.hazelcastSpring.get() as String)\n        // declared here because it is used by web-extension and by distrib\n        dependency(libs.jakartaServletApi.get() as String)\n        // To be removed only when client projects (rest api extension) have all moved their deps to jakarta\n        // Just keep a version managed to prevent client build breaks\n        dependency(libs.javaxServletApi.get() as String)\n\n        // Bonita engine dependencies:\n        dependency \"org.bonitasoft.engine:bonita-common:${project.version}\"\n        dependency \"org.bonitasoft.engine:bonita-server:${project.version}\"\n        dependency \"org.bonitasoft.engine:bonita-client:${project.version}\"\n        dependency \"org.bonitasoft.engine:bonita-test-api:${project.version}\"\n        dependency \"com.bonitasoft.engine:bonita-common-sp:${project.version}\"\n        dependency \"com.bonitasoft.engine:bonita-server-sp:${project.version}\"\n        dependency \"com.bonitasoft.engine:bonita-client-sp:${project.version}\"\n\n        // Web extensions dependencies:\n        dependency \"org.bonitasoft.console:bonita-web-server:${project.version}\"\n        dependency \"org.bonitasoft.console:bonita-web-server-sp:${project.version}\"\n        dependency \"org.bonitasoft.web:bonita-web-extensions:${project.version}\"\n        dependency \"com.bonitasoft.web:bonita-web-extensions-sp:${project.version}\"\n\n        // Web layer specific dependencies:\n        dependency(libs.xbeanClassloader.get() as String)\n        dependency(libs.jakartaJstl.get() as String)\n        dependency(libs.jakartaJstlApi.get() as String)\n        dependency(libs.jgettext.get() as String)\n        dependency(libs.urlrewritefilter.get() as String)\n        dependency(libs.woodstoxCore.get() as String)\n        dependency(libs.jsonSimple.get() as String)\n        dependency(libs.keycloakSamlAdapterApiPublic.get() as String)\n        dependency(libs.keycloakSamlServletFilterAdapter.get() as String) {\n            exclude \"org.bouncycastle:bcprov-jdk15on\"\n            exclude \"org.bouncycastle:bcpkix-jdk15on\"\n            exclude \"org.bouncycastle:bcutil-jdk15on\"\n            exclude \"org.apache.santuario:xmlsec\"\n        }\n        dependency(libs.keycloakAdapterCore.get() as String) {\n            exclude \"org.bouncycastle:bcprov-jdk15on\"\n        }\n        dependency(libs.keycloakServletFilterAdapter.get() as String) {\n            exclude \"org.bouncycastle:bcprov-jdk15on\"\n        }\n        dependency(libs.xmlsec.get() as String)\n        dependency(libs.bouncyCastleBcprov.get() as String)\n        dependency(libs.bouncyCastleBcpkix.get() as String)\n        dependency(libs.bouncyCastleBcutil.get() as String)\n        dependency(libs.spnego.get() as String)\n    }\n}\npublishing {\n    publications {\n        maven(MavenPublication) {\n            artifactId = 'bonita-engine'\n            pom { pom ->\n                name = \"Bonita Engine\"\n                description = \"Bonita Engine is a workflow engine which can be embedded inside your own applications\"\n                PomUtils.pomCommunityPublication(pom)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "bonita-engine-spring-boot-starter/build.gradle",
    "content": "import org.bonitasoft.engine.gradle.PomUtils\n\nplugins {\n    id 'java-library'\n    id 'bonita-tests'\n}\n\ndependencies {\n    api(project(\":bpm:bonita-common\"))\n    api(project(\":bpm:bonita-client\"))\n\n    api(libs.springBootStarter) {\n        exclude(module: 'snakeyaml')\n    }\n\n    implementation(project(\":bonita-engine-standalone\"))\n    implementation(project(\":bpm:bonita-server\"))\n\n    annotationProcessor(libs.springBootConfigurationProcessor)\n\n    testImplementation(libs.springBootTest)\n    testImplementation(libs.assertj)\n}\n\ntasks.register(\"sourcesJar\", Jar) {\n    from sourceSets.main.allJava\n    archiveClassifier = 'sources'\n}\n\ntasks.register(\"javadocJar\", Jar) {\n    from javadoc\n    archiveClassifier = 'javadoc'\n}\n\npublishing {\n    publications {\n        mavenJava(MavenPublication) {\n            from project.components.java\n            artifact project.sourcesJar\n            artifact project.javadocJar\n            pom { pom ->\n                name = \"bonita-engine-spring-boot-starter\"\n                description = \"bonita-engine-spring-boot-starter\"\n                PomUtils.pomCommunityPublication(pom)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "bonita-engine-spring-boot-starter/src/main/java/org/bonitasoft/engine/spring/autoconfigure/BonitaEngineCommonAutoConfiguration.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.spring.autoconfigure;\n\nimport org.bonitasoft.engine.spring.autoconfigure.properties.BonitaEngineProperties;\nimport org.springframework.boot.context.properties.EnableConfigurationProperties;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\n@ComponentScan\n@EnableConfigurationProperties(BonitaEngineProperties.class)\npublic class BonitaEngineCommonAutoConfiguration {\n\n}\n"
  },
  {
    "path": "bonita-engine-spring-boot-starter/src/main/java/org/bonitasoft/engine/spring/autoconfigure/BonitaEngineEventListener.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.spring.autoconfigure;\n\nimport org.bonitasoft.engine.BonitaEngine;\nimport org.bonitasoft.engine.event.PlatformStartedEvent;\nimport org.springframework.beans.BeansException;\nimport org.springframework.boot.context.event.ApplicationReadyEvent;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.context.event.ContextClosedEvent;\nimport org.springframework.context.event.EventListener;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@Component\npublic class BonitaEngineEventListener implements ApplicationContextAware {\n\n    private ApplicationContext context;\n\n    private BonitaEngine bonitaEngine;\n\n    @EventListener\n    public void handleApplicationReadyEvent(final ApplicationReadyEvent applicationReadyEvent) throws Exception {\n        bonitaEngine = applicationReadyEvent.getApplicationContext().getBean(BonitaEngine.class);\n        bonitaEngine.start();\n        // notify any Bonita application that the runtime is ready to accept requests:\n        applicationReadyEvent.getApplicationContext().publishEvent(new PlatformStartedEvent());\n    }\n\n    @EventListener\n    public void handleContextStoppedEvent(final ContextClosedEvent contextClosedEvent) throws Exception {\n        if (bonitaEngine != null && context == contextClosedEvent.getApplicationContext()) {\n            bonitaEngine.stop();\n        }\n    }\n\n    @Override\n    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\n        this.context = applicationContext;\n    }\n}\n"
  },
  {
    "path": "bonita-engine-spring-boot-starter/src/main/java/org/bonitasoft/engine/spring/autoconfigure/BonitaEngineServerAutoConfiguration.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.spring.autoconfigure;\n\nimport org.bonitasoft.engine.BonitaEngine;\nimport org.bonitasoft.engine.api.APIClient;\nimport org.bonitasoft.engine.spring.autoconfigure.properties.BonitaEngineProperties;\nimport org.springframework.boot.autoconfigure.AutoConfigureBefore;\nimport org.springframework.boot.autoconfigure.AutoConfigureOrder;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.Ordered;\n\n@Configuration\n@AutoConfigureBefore(BonitaEngineCommonAutoConfiguration.class)\n@AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)\npublic class BonitaEngineServerAutoConfiguration {\n\n    @Bean\n    @ConditionalOnMissingBean\n    BonitaEngine bonitaEngine(BonitaEngineProperties properties) {\n        BonitaEngine instance = new BonitaEngine();\n        instance.setBonitaDatabaseConfiguration(properties.getDatabase().getBonita());\n        instance.setBusinessDataDatabaseConfiguration(properties.getDatabase().getBusinessData());\n        return instance;\n    }\n\n    @Bean\n    @ConditionalOnMissingBean\n    APIClient bonitaClient() {\n        return new APIClient();\n    }\n\n}\n"
  },
  {
    "path": "bonita-engine-spring-boot-starter/src/main/java/org/bonitasoft/engine/spring/autoconfigure/properties/BonitaDatabasesConfiguration.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.spring.autoconfigure.properties;\n\nimport org.bonitasoft.engine.BonitaDatabaseConfiguration;\nimport org.springframework.boot.context.properties.NestedConfigurationProperty;\n\npublic class BonitaDatabasesConfiguration {\n\n    @NestedConfigurationProperty\n    private BonitaDatabaseConfiguration bonita = new BonitaDatabaseConfiguration();\n    @NestedConfigurationProperty\n    private BonitaDatabaseConfiguration businessData = new BonitaDatabaseConfiguration();\n\n    public BonitaDatabaseConfiguration getBonita() {\n        return bonita;\n    }\n\n    public BonitaDatabaseConfiguration getBusinessData() {\n        return businessData;\n    }\n}\n"
  },
  {
    "path": "bonita-engine-spring-boot-starter/src/main/java/org/bonitasoft/engine/spring/autoconfigure/properties/BonitaEngineProperties.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.spring.autoconfigure.properties;\n\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.boot.context.properties.NestedConfigurationProperty;\n\n@ConfigurationProperties(prefix = \"org.bonitasoft.engine\")\npublic class BonitaEngineProperties {\n\n    @NestedConfigurationProperty\n    private BonitaDatabasesConfiguration database = new BonitaDatabasesConfiguration();\n\n    public BonitaDatabasesConfiguration getDatabase() {\n        return database;\n    }\n}\n"
  },
  {
    "path": "bonita-engine-spring-boot-starter/src/main/resources/META-INF/spring.factories",
    "content": "org.springframework.boot.autoconfigure.EnableAutoConfiguration=\\\n  org.bonitasoft.engine.spring.autoconfigure.BonitaEngineServerAutoConfiguration,\\\n  org.bonitasoft.engine.spring.autoconfigure.BonitaEngineCommonAutoConfiguration"
  },
  {
    "path": "bonita-engine-spring-boot-starter/src/test/java/org/bonitasoft/engine/spring/autoconfigure/BonitaEngineAutoConfigurationTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.spring.autoconfigure;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.BonitaDatabaseConfiguration;\nimport org.bonitasoft.engine.BonitaEngine;\nimport org.junit.Test;\nimport org.springframework.boot.autoconfigure.AutoConfigurations;\nimport org.springframework.boot.test.context.runner.ApplicationContextRunner;\n\npublic class BonitaEngineAutoConfigurationTest {\n\n    private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()\n            .withConfiguration(AutoConfigurations.of(BonitaEngineCommonAutoConfiguration.class,\n                    BonitaEngineServerAutoConfiguration.class));\n\n    @Test\n    public void should_configure_database_using_properties() {\n        this.contextRunner\n                .withPropertyValues(\n                        \"org.bonitasoft.engine.database.bonita.db-vendor=postgres\",\n                        \"org.bonitasoft.engine.database.bonita.url=myServerUrl\",\n                        \"org.bonitasoft.engine.database.bonita.driver-class-name=my.Driver\",\n                        \"org.bonitasoft.engine.database.bonita.user=myUser\",\n                        \"org.bonitasoft.engine.database.bonita.password=secret\",\n                        \"org.bonitasoft.engine.database.bonita.datasource.max-pool-size=3\",\n                        \"org.bonitasoft.engine.database.bonita.xa-datasource.max-pool-size=4\",\n                        \"org.bonitasoft.engine.database.business-data.datasource.max-pool-size=5\",\n                        \"org.bonitasoft.engine.database.business-data.xa-datasource.max-pool-size=6\",\n                        \"org.bonitasoft.engine.database.business-data.db-vendor=mysql\")\n                .run((context) -> {\n                    BonitaEngine engine = context.getBean(BonitaEngine.class);\n                    BonitaDatabaseConfiguration bonitaDatabaseConfiguration = engine.getBonitaDatabaseConfiguration();\n                    assertThat(bonitaDatabaseConfiguration.getDbVendor()).isEqualTo(\"postgres\");\n                    assertThat(bonitaDatabaseConfiguration.getUrl()).isEqualTo(\"myServerUrl\");\n                    assertThat(bonitaDatabaseConfiguration.getDriverClassName()).isEqualTo(\"my.Driver\");\n                    assertThat(bonitaDatabaseConfiguration.getUser()).isEqualTo(\"myUser\");\n                    assertThat(bonitaDatabaseConfiguration.getPassword()).isEqualTo(\"secret\");\n                    assertThat(bonitaDatabaseConfiguration.getDatasource().getMaxPoolSize()).isEqualTo(3);\n                    assertThat(bonitaDatabaseConfiguration.getXaDatasource().getMaxPoolSize()).isEqualTo(4);\n                    BonitaDatabaseConfiguration businessDataDatabaseConfiguration = engine\n                            .getBusinessDataDatabaseConfiguration();\n                    assertThat(businessDataDatabaseConfiguration.getDatasource().getMaxPoolSize()).isEqualTo(5);\n                    assertThat(businessDataDatabaseConfiguration.getXaDatasource().getMaxPoolSize()).isEqualTo(6);\n                    assertThat(\n                            businessDataDatabaseConfiguration.getDbVendor())\n                            .isEqualTo(\"mysql\");\n                });\n    }\n\n}\n"
  },
  {
    "path": "bonita-engine-spring-boot-starter/src/test/java/org/bonitasoft/engine/spring/autoconfigure/BonitaEngineSpringBootStarterIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.spring.autoconfigure;\n\nimport org.bonitasoft.engine.api.APIClient;\nimport org.bonitasoft.engine.platform.LoginException;\nimport org.junit.Test;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.context.ConfigurableApplicationContext;\n\npublic class BonitaEngineSpringBootStarterIT {\n\n    @Test\n    public void should_start_engine_when_application_starts() throws LoginException {\n        final ConfigurableApplicationContext context = SpringApplication.run(ClientTestApplication.class);\n        context.getBean(APIClient.class).login(\"install\", \"install\");\n        context.stop();\n    }\n\n}\n"
  },
  {
    "path": "bonita-engine-spring-boot-starter/src/test/java/org/bonitasoft/engine/spring/autoconfigure/ClientTestApplication.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.spring.autoconfigure;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@SpringBootApplication\npublic class ClientTestApplication {\n\n}\n"
  },
  {
    "path": "bonita-engine-standalone/build.gradle",
    "content": "import org.bonitasoft.engine.gradle.PomUtils\n\nplugins {\n    id \"java-library\"\n}\n\ndependencies {\n\n    api(project(\":bpm:bonita-client\"))\n\n    implementation(project(\":bpm:bonita-server\"))\n    implementation(project(':platform:platform-resources'))\n\n    implementation libs.springContext\n    implementation libs.narayanaJta\n    implementation libs.tomcatDbcp\n    implementation libs.slf4jApi\n    runtimeOnly libs.h2\n\n\n    annotationProcessor libs.lombok\n    compileOnly libs.lombok\n\n    testImplementation libs.assertj\n    testImplementation libs.systemRules\n\n    // These 3 drivers are used for some specific tests:\n    testImplementation(libs.postgresql)\n    testImplementation(libs.mysql)\n    testImplementation(libs.h2)\n\n}\n\ntasks.register(\"sourcesJar\", Jar) {\n    from sourceSets.main.allJava\n    archiveClassifier = 'sources'\n}\n\ntasks.register(\"javadocJar\", Jar) {\n    from javadoc\n    archiveClassifier = 'javadoc'\n}\n\npublishing {\n    publications {\n        mavenJava(MavenPublication) {\n            from project.components.java\n            artifact project.sourcesJar\n            artifact project.javadocJar\n            pom { pom ->\n                name = \"Bonita Engine Standalone\"\n                description = \"Bonita Engine Standalone is the library to easily embed Bonita Engine is your applications\"\n                PomUtils.pomCommunityPublication(pom)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "bonita-engine-standalone/src/main/java/org/bonitasoft/engine/BonitaDataSourceInitializer.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\nimport static org.bonitasoft.engine.xa.XADataSourceIsSameRMOverride.overrideSameRM;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport javax.naming.Reference;\nimport javax.naming.StringRefAddr;\nimport javax.naming.spi.ObjectFactory;\nimport javax.sql.XADataSource;\nimport javax.transaction.TransactionManager;\n\nimport org.apache.tomcat.dbcp.dbcp2.BasicDataSource;\nimport org.apache.tomcat.dbcp.dbcp2.managed.BasicManagedDataSource;\n\nclass BonitaDataSourceInitializer {\n\n    private static final String H2 = \"h2\";\n    private static final String MYSQL = \"mysql\";\n    private static final String ORACLE = \"oracle\";\n    private static final String POSTGRES = \"postgres\";\n    private static final String SQLSERVER = \"sqlserver\";\n\n    private static final Map<String, String> defaultDriver;\n    private static final Map<String, String> defaultXADataSourceNames;\n    private static final Map<String, String> defaultXADataSourceFactories;\n    private static final Map<String, String> defaultTestQueries;\n\n    static {\n        HashMap<String, String> drivers = new HashMap<>();\n        drivers.put(H2, \"org.h2.Driver\");\n        drivers.put(MYSQL, \"com.mysql.cj.jdbc.Driver\");\n        drivers.put(ORACLE, \"oracle.jdbc.OracleDriver\");\n        drivers.put(POSTGRES, \"org.postgresql.Driver\");\n        drivers.put(SQLSERVER, \"com.microsoft.sqlserver.jdbc.SQLServerDriver\");\n        defaultDriver = Collections.unmodifiableMap(drivers);\n        HashMap<String, String> xaDataSourceNames = new HashMap<>();\n        xaDataSourceNames.put(H2, \"org.h2.jdbcx.JdbcDataSource\");\n        xaDataSourceNames.put(MYSQL, \"com.mysql.cj.jdbc.MysqlXADataSource\");\n        xaDataSourceNames.put(ORACLE, \"oracle.jdbc.xa.client.OracleXADataSource\");\n        xaDataSourceNames.put(POSTGRES, \"org.postgresql.xa.PGXADataSource\");\n        xaDataSourceNames.put(SQLSERVER, \"com.microsoft.sqlserver.jdbc.SQLServerXADataSource\");\n        defaultXADataSourceNames = Collections.unmodifiableMap(xaDataSourceNames);\n        HashMap<String, String> xaDataSourceFactories = new HashMap<>();\n        xaDataSourceFactories.put(H2, \"org.h2.jdbcx.JdbcDataSourceFactory\");\n        xaDataSourceFactories.put(MYSQL, \"com.mysql.cj.jdbc.MysqlDataSourceFactory\");\n        xaDataSourceFactories.put(ORACLE, \"oracle.jdbc.pool.OracleDataSourceFactory\");\n        xaDataSourceFactories.put(POSTGRES, \"org.postgresql.xa.PGXADataSourceFactory\");\n        xaDataSourceFactories.put(SQLSERVER, \"com.microsoft.sqlserver.jdbc.SQLServerDataSourceObjectFactory\");\n        defaultXADataSourceFactories = Collections.unmodifiableMap(xaDataSourceFactories);\n        HashMap<String, String> testQueries = new HashMap<>();\n        testQueries.put(H2, \"SELECT 1\");\n        testQueries.put(MYSQL, \"SELECT 1\");\n        testQueries.put(ORACLE, \"SELECT 1 FROM DUAL\");\n        testQueries.put(POSTGRES, \"SELECT 1\");\n        testQueries.put(SQLSERVER, \"SELECT 1\");\n        defaultTestQueries = Collections.unmodifiableMap(testQueries);\n    }\n\n    BasicManagedDataSource createManagedDataSource(BonitaDatabaseConfiguration configuration,\n            TransactionManager transactionManager) throws Exception {\n        validate(configuration);\n        String dbVendor = configuration.getDbVendor();\n        String xaDatasourceClass = defaultXADataSourceNames.get(dbVendor);\n        String xaDatasourceFactoryClass = defaultXADataSourceFactories.get(dbVendor);\n        //create a 'native' xa datasources\n\n        ObjectFactory datasourceFactory = (ObjectFactory) Class.forName(xaDatasourceFactoryClass).getConstructor()\n                .newInstance();\n        Reference reference = new Reference(xaDatasourceClass);\n        String description = \"RawDataSource of \" + dbVendor;\n        reference.add(new StringRefAddr(\"description\", description));\n        reference.add(new StringRefAddr(\"closeMethod\", \"close\"));\n        reference.add(new StringRefAddr(\"loginTimeout\", \"0\"));\n        if (dbVendor.equals(POSTGRES)) {\n            DatabaseUrlParser.DatabaseMetadata metadata = DatabaseUrlParser.parsePostgresUrl(configuration.getUrl());\n            reference.add(new StringRefAddr(\"serverName\", metadata.getServerName()));\n            reference.add(new StringRefAddr(\"portNumber\", metadata.getPort()));\n            reference.add(new StringRefAddr(\"databaseName\", metadata.getDatabaseName()));\n        } else if (dbVendor.equals(SQLSERVER)) {\n            reference.add(new StringRefAddr(\"dataSourceURL\", configuration.getUrl()));\n            reference.add(new StringRefAddr(\"dataSourceDescription\", description));\n            reference.add(new StringRefAddr(\"class\", xaDatasourceClass));\n        } else {\n            reference.add(new StringRefAddr(\"explicitUrl\", \"true\"));\n            reference.add(new StringRefAddr(\"url\", configuration.getUrl()));\n        }\n        reference.add(new StringRefAddr(\"user\", configuration.getUser()));\n        reference.add(new StringRefAddr(\"password\", configuration.getPassword()));\n\n        XADataSource xaDataSource = (XADataSource) datasourceFactory.getObjectInstance(reference, null, null, null);\n\n        BasicManagedDataSource bonitaDataSource = new BasicManagedDataSource();\n        bonitaDataSource.setDefaultAutoCommit(false);\n        bonitaDataSource.setRemoveAbandonedOnBorrow(true);\n        bonitaDataSource.setRemoveAbandonedOnMaintenance(true);\n        bonitaDataSource.setLogAbandoned(false);\n        bonitaDataSource.setTestOnBorrow(true);\n        bonitaDataSource.setValidationQuery(defaultTestQueries.get(dbVendor));\n        bonitaDataSource.setTransactionManager(transactionManager);\n        bonitaDataSource.setInitialSize(1);\n        bonitaDataSource.setTestWhileIdle(false);\n        bonitaDataSource.setTimeBetweenEvictionRunsMillis(60000);\n        bonitaDataSource.setMinEvictableIdleTimeMillis(600000);\n        if (dbVendor.equals(ORACLE)) {\n            bonitaDataSource.setXaDataSourceInstance(overrideSameRM(xaDataSource));\n        } else {\n            bonitaDataSource.setXaDataSourceInstance(xaDataSource);\n\n        }\n        configureDatasource(configuration.getXaDatasource(), bonitaDataSource);\n        return bonitaDataSource;\n    }\n\n    private String getDriverClassName(BonitaDatabaseConfiguration configuration, String driver) {\n        String driverClassName;\n        if (driver == null || driver.isEmpty()) {\n            driverClassName = defaultDriver.get(configuration.getDbVendor());\n        } else {\n            driverClassName = driver;\n        }\n        return driverClassName;\n    }\n\n    private void validate(BonitaDatabaseConfiguration configuration) {\n        checkNullOrEmpty(configuration.getDbVendor(), \"dbVendor\");\n        if (!defaultDriver.containsKey(configuration.getDbVendor())) {\n            throw new IllegalArgumentException(String.format(\"Database db vendor %s is invalid ( should be one of %s )\",\n                    configuration.getDbVendor(), defaultDriver.keySet()));\n        }\n        checkNullOrEmpty(configuration.getUrl(), \"url\");\n        checkNullOrEmpty(configuration.getUser(), \"user\");\n    }\n\n    private static void checkNullOrEmpty(String field, String fieldName) {\n        if (field == null || field.isEmpty()) {\n            throw new IllegalArgumentException(\"Database \" + fieldName + \" not set\");\n        }\n    }\n\n    BasicDataSource createDataSource(BonitaDatabaseConfiguration configuration) {\n        validate(configuration);\n        BasicDataSource dataSource = new BasicDataSource();\n        configureDatasource(configuration.getDatasource(), dataSource);\n        dataSource.setInitialSize(1);\n        dataSource.setDriverClassName(getDriverClassName(configuration, configuration.getDriverClassName()));\n        dataSource.setUrl(configuration.getUrl());\n        dataSource.setUsername(configuration.getUser());\n        dataSource.setPassword(configuration.getPassword());\n        return dataSource;\n    }\n\n    private void configureDatasource(DatasourceConfiguration configuration, BasicDataSource dataSource) {\n        if (configuration != null && configuration.getMaxPoolSize() > 0) {\n            dataSource.setMaxTotal(configuration.getMaxPoolSize());\n        } else {\n            dataSource.setMaxTotal(7);\n        }\n    }\n}\n"
  },
  {
    "path": "bonita-engine-standalone/src/main/java/org/bonitasoft/engine/BonitaDatabaseConfiguration.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.ToString;\n\n@Data\n@ToString(exclude = \"password\")\n@NoArgsConstructor\n@AllArgsConstructor\n@Builder(toBuilder = true)\npublic class BonitaDatabaseConfiguration {\n\n    private String driverClassName;\n    private String url;\n    private String dbVendor;\n    private String user;\n    private String password;\n    private DatasourceConfiguration xaDatasource;\n    private DatasourceConfiguration datasource;\n\n    public boolean isEmpty() {\n        return isNullOrEmpty(driverClassName) &&\n                isNullOrEmpty(url) &&\n                isNullOrEmpty(dbVendor) &&\n                isNullOrEmpty(user) &&\n                isNullOrEmpty(password);\n    }\n\n    private boolean isNullOrEmpty(String s) {\n        return s == null || s.isEmpty();\n    }\n\n    /**\n     * The name of the driver class, if the default one does not suit your needs\n     *\n     * @param driverClassName the name of the driver class to use\n     */\n    public void setDriverClassName(String driverClassName) {\n        this.driverClassName = driverClassName;\n    }\n\n    /**\n     * The URL to connect to the database\n     *\n     * @param url the URL to connect to the database\n     */\n    public void setUrl(String url) {\n        this.url = url;\n    }\n\n    /**\n     * The database vendor to use with Bonita Engine.\n     * Supported values are h2, mysql, postgres, sqlserver, oracle. Default value, if not specified, is h2.\n     *\n     * @param dbVendor the database vendor to use with Bonita Engine\n     */\n    public void setDbVendor(String dbVendor) {\n        this.dbVendor = dbVendor;\n    }\n\n    /**\n     * The connection user name to use to access the Bonita database\n     *\n     * @param user the connection user name to use to access the Bonita database\n     */\n    public void setUser(String user) {\n        this.user = user;\n    }\n\n    /**\n     * The connection password corresponding to the specified user name\n     *\n     * @param password the connection password corresponding to the specified user name\n     */\n    public void setPassword(String password) {\n        this.password = password;\n    }\n}\n"
  },
  {
    "path": "bonita-engine-standalone/src/main/java/org/bonitasoft/engine/BonitaEngine.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport javax.naming.NamingException;\n\nimport com.arjuna.ats.jta.TransactionManager;\nimport com.arjuna.ats.jta.UserTransaction;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.tomcat.dbcp.dbcp2.BasicDataSource;\nimport org.apache.tomcat.dbcp.dbcp2.managed.BasicManagedDataSource;\nimport org.bonitasoft.engine.api.ApiAccessType;\nimport org.bonitasoft.engine.api.PlatformAPI;\nimport org.bonitasoft.engine.api.PlatformAPIAccessor;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.platform.PlatformLoginException;\nimport org.bonitasoft.engine.platform.PlatformLogoutException;\nimport org.bonitasoft.engine.session.PlatformSession;\nimport org.bonitasoft.engine.session.SessionNotFoundException;\nimport org.bonitasoft.engine.util.APITypeManager;\nimport org.bonitasoft.platform.setup.PlatformSetup;\nimport org.bonitasoft.platform.setup.PlatformSetupAccessor;\nimport org.springframework.jndi.JndiTemplate;\n\n@Slf4j\npublic class BonitaEngine {\n\n    private final BonitaDataSourceInitializer bonitaDataSourceInitializer = new BonitaDataSourceInitializer();\n    private boolean initialized;\n    private BonitaDatabaseConfiguration bonitaDatabaseConfiguration;\n    private BonitaDatabaseConfiguration businessDataDatabaseConfiguration;\n    private MemoryJNDISetup memoryJNDISetup;\n    private BasicManagedDataSource bonitaDataSource;\n    private BasicManagedDataSource businessDataDataSource;\n    private BasicDataSource bonitaSequenceManagerDataSource;\n    private BasicDataSource notManagedBizDataSource;\n    private javax.transaction.UserTransaction userTransaction;\n    private javax.transaction.TransactionManager arjunaTransactionManager;\n    public static final String BONITA_BDM_DB_VENDOR = PlatformSetup.BONITA_BDM_DB_VENDOR_PROPERTY;\n    public static final String BONITA_DB_VENDOR = PlatformSetup.BONITA_DB_VENDOR_PROPERTY;\n\n    public void initializeEnvironment() throws Exception {\n        if (!initialized) {\n            initialized = true;\n            APITypeManager.setAPITypeAndParams(ApiAccessType.LOCAL, Collections.emptyMap());\n            initializeBonitaDatabaseConfiguration();\n            initializeBusinessDataDatabaseConfiguration();\n            arjunaTransactionManager = TransactionManager.transactionManager();\n            userTransaction = UserTransaction.userTransaction();\n            bonitaDataSource = bonitaDataSourceInitializer.createManagedDataSource(bonitaDatabaseConfiguration,\n                    arjunaTransactionManager);\n            businessDataDataSource = bonitaDataSourceInitializer\n                    .createManagedDataSource(businessDataDatabaseConfiguration, arjunaTransactionManager);\n            bonitaSequenceManagerDataSource = bonitaDataSourceInitializer.createDataSource(bonitaDatabaseConfiguration);\n            notManagedBizDataSource = bonitaDataSourceInitializer.createDataSource(businessDataDatabaseConfiguration);\n            initializeJNDI();\n        }\n    }\n\n    private void initializeBonitaDatabaseConfiguration() {\n        if (bonitaDatabaseConfiguration == null || bonitaDatabaseConfiguration.isEmpty()) {\n            bonitaDatabaseConfiguration = DefaultBonitaDatabaseConfigurations\n                    .defaultConfiguration(System.getProperty(BONITA_DB_VENDOR, \"h2\"), \"bonita\");\n        }\n        setSystemPropertyIfNotSet(BONITA_DB_VENDOR, bonitaDatabaseConfiguration.getDbVendor());\n        log.info(\"Using database configuration for bonita {}\", bonitaDatabaseConfiguration);\n    }\n\n    private void initializeBusinessDataDatabaseConfiguration() {\n        if (businessDataDatabaseConfiguration == null || businessDataDatabaseConfiguration.isEmpty()) {\n            businessDataDatabaseConfiguration = DefaultBonitaDatabaseConfigurations\n                    .defaultConfiguration(System.getProperty(BONITA_BDM_DB_VENDOR, \"h2\"), \"business_data\");\n        }\n        setSystemPropertyIfNotSet(BONITA_BDM_DB_VENDOR, businessDataDatabaseConfiguration.getDbVendor());\n        log.info(\"Using database configuration for business data {}\", businessDataDatabaseConfiguration);\n    }\n\n    private void setSystemPropertyIfNotSet(String systemPropertyName, String defaultValue) {\n        String value = System.getProperty(systemPropertyName);\n        if (value != null) {\n            return;\n        }\n        System.setProperty(systemPropertyName, defaultValue);\n    }\n\n    private void initializeJNDI() throws NamingException {\n        Map<String, Object> jndiMapping = new HashMap<>();\n        jndiMapping.put(\"java:comp/env/bonitaDS\", bonitaDataSource);\n        jndiMapping.put(\"java:comp/env/bonitaSequenceManagerDS\", bonitaSequenceManagerDataSource);\n        jndiMapping.put(\"java:comp/env/BusinessDataDS\", businessDataDataSource);\n        jndiMapping.put(\"java:comp/env/NotManagedBizDataDS\", notManagedBizDataSource);\n        jndiMapping.put(\"java:comp/env/TransactionManager\", arjunaTransactionManager);\n        jndiMapping.put(\"java:comp/UserTransaction\", userTransaction);\n        JndiTemplate jndiTemplate = new JndiTemplate();\n        memoryJNDISetup = new MemoryJNDISetup(jndiTemplate, jndiMapping);\n        memoryJNDISetup.init();\n    }\n\n    public void start() throws Exception {\n        initializeEnvironment();\n        PlatformSetup platformSetup = getPlatformSetup();\n        platformSetup.init();\n\n        PlatformSession platformSession = loginOnPlatform();\n\n        final PlatformAPI platformAPI = PlatformAPIAccessor.getPlatformAPI(platformSession);\n\n        platformAPI.startNode();\n        logoutFromPlatform(platformSession);\n    }\n\n    protected PlatformSetup getPlatformSetup() throws NamingException {\n        return PlatformSetupAccessor.getInstance().getPlatformSetup();\n    }\n\n    private void logoutFromPlatform(PlatformSession platformSession)\n            throws PlatformLogoutException, SessionNotFoundException, BonitaHomeNotSetException, ServerAPIException,\n            UnknownAPITypeException {\n        PlatformAPIAccessor.getPlatformLoginAPI().logout(platformSession);\n    }\n\n    private PlatformSession loginOnPlatform() throws PlatformLoginException {\n        return new LocalLoginMechanism().login();\n    }\n\n    public void stop() throws Exception {\n        PlatformSession platformSession = loginOnPlatform();\n        final PlatformAPI platformAPI = PlatformAPIAccessor.getPlatformAPI(platformSession);\n        if (platformAPI.isNodeStarted()) {\n            platformAPI.stopNode();\n        }\n        logoutFromPlatform(platformSession);\n        memoryJNDISetup.clean();\n    }\n\n    public BonitaDatabaseConfiguration getBonitaDatabaseConfiguration() {\n        return bonitaDatabaseConfiguration;\n    }\n\n    public void setBonitaDatabaseConfiguration(BonitaDatabaseConfiguration database) {\n        this.bonitaDatabaseConfiguration = database;\n    }\n\n    public BonitaDatabaseConfiguration getBusinessDataDatabaseConfiguration() {\n        return businessDataDatabaseConfiguration;\n    }\n\n    public void setBusinessDataDatabaseConfiguration(BonitaDatabaseConfiguration businessDataDatabaseConfiguration) {\n        this.businessDataDatabaseConfiguration = businessDataDatabaseConfiguration;\n    }\n\n    BasicManagedDataSource getBonitaDataSource() {\n        return bonitaDataSource;\n    }\n\n    BasicManagedDataSource getBusinessDataDataSource() {\n        return businessDataDataSource;\n    }\n}\n"
  },
  {
    "path": "bonita-engine-standalone/src/main/java/org/bonitasoft/engine/DatabaseUrlParser.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\n\npublic class DatabaseUrlParser {\n\n    @Data\n    @AllArgsConstructor\n    public static class DatabaseMetadata {\n\n        private String serverName;\n        private String port;\n        private String databaseName;\n\n    }\n\n    public static DatabaseMetadata parsePostgresUrl(String url) {\n        String regex = \"jdbc:postgresql://([\\\\w\\\\d\\\\.-]+):(\\\\d+)/([\\\\w\\\\-_\\\\d]+).*\";\n        Pattern pattern = Pattern.compile(regex);\n        Matcher matcher = pattern.matcher(url);\n        if (!matcher.find()) {\n            throw new IllegalArgumentException(\n                    \"Unable to parse postgres url (no groups found): \" + url + \" using regex \" + regex);\n        }\n        return new DatabaseMetadata(matcher.group(1), matcher.group(2), matcher.group(3));\n    }\n}\n"
  },
  {
    "path": "bonita-engine-standalone/src/main/java/org/bonitasoft/engine/DatasourceConfiguration.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class DatasourceConfiguration {\n\n    /**\n     * Maximum number of connections in the pool\n     * Must be more than 0\n     * Default value is 7\n     */\n    private int maxPoolSize;\n\n}\n"
  },
  {
    "path": "bonita-engine-standalone/src/main/java/org/bonitasoft/engine/DefaultBonitaDatabaseConfigurations.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\npublic class DefaultBonitaDatabaseConfigurations {\n\n    private static BonitaDatabaseConfiguration defaultH2Configuration(String schemaName) {\n        return BonitaDatabaseConfiguration.builder()\n                .dbVendor(\"h2\")\n                .url(\"jdbc:h2:file:\" + System.getProperty(\"org.bonitasoft.h2.database.dir\", \"./h2databasedir\")\n                        + \"/\" + schemaName + \";DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTO_SERVER=TRUE\")\n                .user(\"bonita\")\n                .password(\"bpm\").build();\n    }\n\n    private static BonitaDatabaseConfiguration defaultPostgresConfiguration(String schemaName) {\n        return BonitaDatabaseConfiguration.builder()\n                .dbVendor(\"postgres\")\n                .url(\"jdbc:postgresql://localhost:5432/\" + schemaName)\n                .user(\"bonita\")\n                .password(\"bpm\").build();\n    }\n\n    private static BonitaDatabaseConfiguration defaultMysqlConfiguration(String schemaName) {\n        return BonitaDatabaseConfiguration.builder()\n                .dbVendor(\"mysql\")\n                .url(\"jdbc:mysql://localhost:3306/\" + schemaName\n                        + \"?useUnicode=true&characterEncoding=UTF-8&useSSL=false&allowPublicKeyRetrieval=true\")\n                .user(\"bonita\")\n                .password(\"bpm\").build();\n    }\n\n    private static BonitaDatabaseConfiguration defaultOracleConfiguration(String schemaName) {\n        return BonitaDatabaseConfiguration.builder()\n                .dbVendor(\"oracle\")\n                .url(\"jdbc:oracle:thin:@//localhost:1521/FREEPDB1?oracle.net.disableOob=true\")\n                .user(schemaName)\n                .password(\"bpm\").build();\n    }\n\n    private static BonitaDatabaseConfiguration defaultSqlserverConfiguration(String schemaName) {\n        return BonitaDatabaseConfiguration.builder()\n                .dbVendor(\"sqlserver\")\n                .url(\"jdbc:sqlserver://localhost:1433;database=\" + schemaName)\n                .user(\"bonita\")\n                .password(\"bpm\").build();\n    }\n\n    public static BonitaDatabaseConfiguration defaultConfiguration(String dbVendor, String schemaName) {\n        switch (dbVendor) {\n            case \"h2\":\n                //clone it\n                return defaultH2Configuration(schemaName);\n            case \"postgres\":\n                return defaultPostgresConfiguration(schemaName);\n            case \"mysql\":\n                return defaultMysqlConfiguration(schemaName);\n            case \"oracle\":\n                return defaultOracleConfiguration(schemaName);\n            case \"sqlserver\":\n                return defaultSqlserverConfiguration(schemaName);\n            default:\n                throw new IllegalArgumentException(\"dbVendor \" + dbVendor + \" is not valid\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "bonita-engine-standalone/src/main/java/org/bonitasoft/engine/MemoryJNDISetup.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\nimport java.util.Map;\n\nimport javax.naming.Context;\nimport javax.naming.NameAlreadyBoundException;\nimport javax.naming.NamingException;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.jndi.JndiTemplate;\n\npublic class MemoryJNDISetup {\n\n    private final JndiTemplate jndiTemplate;\n\n    private final Map<String, Object> jndiMapping;\n\n    private final Logger logger = LoggerFactory.getLogger(MemoryJNDISetup.class.getSimpleName());\n\n    public MemoryJNDISetup(final JndiTemplate jndiTemplate, final Map<String, Object> jndiMapping) {\n        super();\n        if (System.getProperty(Context.INITIAL_CONTEXT_FACTORY) == null) {\n            System.setProperty(Context.INITIAL_CONTEXT_FACTORY, \"org.bonitasoft.engine.SimpleMemoryContextFactory\");\n        }\n        System.setProperty(Context.URL_PKG_PREFIXES, \"org.bonitasoft.engine\");\n        this.jndiTemplate = jndiTemplate;\n        this.jndiMapping = jndiMapping;\n    }\n\n    public void init() throws NamingException {\n        for (final Map.Entry<String, Object> addToJndi : jndiMapping.entrySet()) {\n            logger.info(\"Binding \" + addToJndi.getKey() + \" @ \" + addToJndi.getValue());\n            try {\n\n                jndiTemplate.bind(addToJndi.getKey(), addToJndi.getValue());\n            } catch (NameAlreadyBoundException ignored) {\n                logger.info(addToJndi.getKey() + \" @ \" + addToJndi.getValue() + \" was already bound\");\n            }\n        }\n    }\n\n    public void clean() throws NamingException {\n        for (final Map.Entry<String, Object> removeFromJndi : jndiMapping.entrySet()) {\n            logger.info(\"Unbinding \" + removeFromJndi.getKey());\n            jndiTemplate.unbind(removeFromJndi.getKey());\n        }\n    }\n\n}\n"
  },
  {
    "path": "bonita-engine-standalone/src/main/java/org/bonitasoft/engine/SimpleMemoryContext.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\nimport java.util.Hashtable;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport javax.naming.Binding;\nimport javax.naming.Context;\nimport javax.naming.Name;\nimport javax.naming.NameAlreadyBoundException;\nimport javax.naming.NameClassPair;\nimport javax.naming.NameNotFoundException;\nimport javax.naming.NameParser;\nimport javax.naming.NamingEnumeration;\nimport javax.naming.NamingException;\n\n/**\n * A JNDI context implementation that uses the memory as a dictionary of objects.\n */\npublic class SimpleMemoryContext implements Context {\n\n    private static final String NOT_SUPPORTED_YET = \"Not supported yet.\";\n\n    private final Map<String, Object> dictionary = new ConcurrentHashMap<String, Object>();\n\n    public void clear() {\n        dictionary.clear();\n    }\n\n    @Override\n    public Object lookup(final Name name) throws NamingException {\n        return lookup(name.toString());\n    }\n\n    @Override\n    public Object lookup(final String name) throws NamingException {\n        if (dictionary.containsKey(name)) {\n            return dictionary.get(name);\n        }\n        throw new NameNotFoundException(\"Name \" + name + \" is not bound !\");\n    }\n\n    @Override\n    public void bind(final Name name, final Object o) throws NamingException {\n        bind(name.toString(), o);\n    }\n\n    @Override\n    public void bind(final String name, final Object o) throws NamingException {\n        if (dictionary.containsKey(name)) {\n            throw new NameAlreadyBoundException(\"Name \" + name + \" already bound!\");\n        }\n        rebind(name, o);\n    }\n\n    @Override\n    public void rebind(final Name name, final Object o) {\n        rebind(name.toString(), o);\n    }\n\n    @Override\n    public void rebind(final String name, final Object o) {\n        dictionary.put(name, o);\n    }\n\n    @Override\n    public void unbind(final Name name) throws NamingException {\n        unbind(name.toString());\n    }\n\n    @Override\n    public void unbind(final String name) throws NamingException {\n        if (!dictionary.containsKey(name)) {\n            throw new NameNotFoundException(\"No such name \" + name + \" is bound!\");\n        }\n        dictionary.remove(name);\n    }\n\n    @Override\n    public void rename(final Name oldName, final Name newName) throws NamingException {\n        rename(oldName.toString(), newName.toString());\n    }\n\n    @Override\n    public void rename(final String oldName, final String newName) throws NamingException {\n        final Object object = lookup(oldName);\n        bind(newName, object);\n        unbind(oldName);\n    }\n\n    @Override\n    public NamingEnumeration<NameClassPair> list(final Name name) {\n        throw new UnsupportedOperationException(NOT_SUPPORTED_YET);\n    }\n\n    @Override\n    public NamingEnumeration<NameClassPair> list(final String string) {\n        throw new UnsupportedOperationException(NOT_SUPPORTED_YET);\n    }\n\n    @Override\n    public NamingEnumeration<Binding> listBindings(final Name name) {\n        throw new UnsupportedOperationException(NOT_SUPPORTED_YET);\n    }\n\n    @Override\n    public NamingEnumeration<Binding> listBindings(final String string) {\n        throw new UnsupportedOperationException(NOT_SUPPORTED_YET);\n    }\n\n    @Override\n    public void destroySubcontext(final Name name) {\n        destroySubcontext(name.toString());\n    }\n\n    @Override\n    public void destroySubcontext(final String name) {\n        dictionary.remove(name);\n    }\n\n    @Override\n    public Context createSubcontext(final Name name) throws NamingException {\n        return createSubcontext(name.toString());\n    }\n\n    @Override\n    public Context createSubcontext(final String name) throws NamingException {\n        final Context subContext = new SimpleMemoryContext();\n        bind(name, subContext);\n        return subContext;\n    }\n\n    @Override\n    public Object lookupLink(final Name name) {\n        throw new UnsupportedOperationException(NOT_SUPPORTED_YET);\n    }\n\n    @Override\n    public Object lookupLink(final String string) {\n        throw new UnsupportedOperationException(NOT_SUPPORTED_YET);\n    }\n\n    @Override\n    public NameParser getNameParser(final Name name) {\n        throw new UnsupportedOperationException(NOT_SUPPORTED_YET);\n    }\n\n    @Override\n    public NameParser getNameParser(final String name) {\n        return new SimpleNameParser(name);\n    }\n\n    @Override\n    public Name composeName(final Name name, final Name name1) {\n        throw new UnsupportedOperationException(NOT_SUPPORTED_YET);\n    }\n\n    @Override\n    public String composeName(final String string, final String string1) {\n        throw new UnsupportedOperationException(NOT_SUPPORTED_YET);\n    }\n\n    @Override\n    public Object addToEnvironment(final String string, final Object o) {\n        throw new UnsupportedOperationException(NOT_SUPPORTED_YET);\n    }\n\n    @Override\n    public Object removeFromEnvironment(final String string) {\n        throw new UnsupportedOperationException(NOT_SUPPORTED_YET);\n    }\n\n    @Override\n    public Hashtable<?, ?> getEnvironment() {\n        throw new UnsupportedOperationException(NOT_SUPPORTED_YET);\n    }\n\n    @Override\n    public void close() {\n        // Thread.dumpStack();\n        // clear();\n    }\n\n    @Override\n    public String getNameInNamespace() {\n        throw new UnsupportedOperationException(NOT_SUPPORTED_YET);\n    }\n}\n"
  },
  {
    "path": "bonita-engine-standalone/src/main/java/org/bonitasoft/engine/SimpleMemoryContextFactory.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\nimport java.util.Hashtable;\n\nimport javax.naming.Context;\nimport javax.naming.spi.InitialContextFactory;\n\n/**\n * A factory of a naming context that uses the memory as dictionary of objects. Useful to tests\n * objects using JNDI to get dependencies.\n */\npublic class SimpleMemoryContextFactory implements InitialContextFactory {\n\n    private static final SimpleMemoryContext context = new SimpleMemoryContext();\n\n    @Override\n    public Context getInitialContext(final Hashtable<?, ?> environment) {\n        return context;\n    }\n}\n"
  },
  {
    "path": "bonita-engine-standalone/src/main/java/org/bonitasoft/engine/SimpleNameParser.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\nimport javax.naming.CompositeName;\nimport javax.naming.Name;\nimport javax.naming.NameParser;\nimport javax.naming.NamingException;\n\npublic class SimpleNameParser implements NameParser {\n\n    public SimpleNameParser(final String name) {\n    }\n\n    @Override\n    public Name parse(final String name) throws NamingException {\n        return new CompositeName(name);\n    }\n\n}\n"
  },
  {
    "path": "bonita-engine-standalone/src/main/java/org/bonitasoft/engine/xa/XAConnectionIsSameRMOverride.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.xa;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\n\nimport javax.sql.ConnectionEventListener;\nimport javax.sql.StatementEventListener;\nimport javax.sql.XAConnection;\nimport javax.transaction.xa.XAResource;\n\npublic class XAConnectionIsSameRMOverride implements XAConnection {\n\n    private XAConnection xaConnection;\n\n    private XAConnectionIsSameRMOverride(XAConnection xaConnection) {\n        this.xaConnection = xaConnection;\n    }\n\n    static XAConnection overrideSameRM(XAConnection xaConnection) {\n        return new XAConnectionIsSameRMOverride(xaConnection);\n    }\n\n    @Override\n    public XAResource getXAResource() throws SQLException {\n        return XAResourceIsSameRMOverride.overrideSameRM(xaConnection.getXAResource());\n    }\n\n    @Override\n    public Connection getConnection() throws SQLException {\n        return xaConnection.getConnection();\n    }\n\n    @Override\n    public void close() throws SQLException {\n        xaConnection.close();\n    }\n\n    @Override\n    public void addConnectionEventListener(ConnectionEventListener listener) {\n        xaConnection.addConnectionEventListener(listener);\n    }\n\n    @Override\n    public void removeConnectionEventListener(ConnectionEventListener listener) {\n        xaConnection.removeConnectionEventListener(listener);\n    }\n\n    @Override\n    public void addStatementEventListener(StatementEventListener listener) {\n        xaConnection.addStatementEventListener(listener);\n\n    }\n\n    @Override\n    public void removeStatementEventListener(StatementEventListener listener) {\n        xaConnection.removeStatementEventListener(listener);\n\n    }\n}\n"
  },
  {
    "path": "bonita-engine-standalone/src/main/java/org/bonitasoft/engine/xa/XADataSourceIsSameRMOverride.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.xa;\n\nimport java.io.PrintWriter;\nimport java.sql.SQLException;\nimport java.sql.SQLFeatureNotSupportedException;\nimport java.util.logging.Logger;\n\nimport javax.sql.XAConnection;\nimport javax.sql.XADataSource;\n\npublic class XADataSourceIsSameRMOverride implements XADataSource {\n\n    private XADataSource xaDataSource;\n\n    private XADataSourceIsSameRMOverride(XADataSource xaDataSource) {\n        this.xaDataSource = xaDataSource;\n    }\n\n    public static XADataSource overrideSameRM(XADataSource xaDataSource) {\n        return new XADataSourceIsSameRMOverride(xaDataSource);\n    }\n\n    @Override\n    public XAConnection getXAConnection() throws SQLException {\n        return XAConnectionIsSameRMOverride.overrideSameRM(xaDataSource.getXAConnection());\n    }\n\n    @Override\n    public XAConnection getXAConnection(String user, String password) throws SQLException {\n        return XAConnectionIsSameRMOverride.overrideSameRM(xaDataSource.getXAConnection(user, password));\n    }\n\n    @Override\n    public PrintWriter getLogWriter() throws SQLException {\n        return xaDataSource.getLogWriter();\n    }\n\n    @Override\n    public void setLogWriter(PrintWriter out) throws SQLException {\n        xaDataSource.setLogWriter(out);\n    }\n\n    @Override\n    public void setLoginTimeout(int seconds) throws SQLException {\n        xaDataSource.setLoginTimeout(seconds);\n    }\n\n    @Override\n    public int getLoginTimeout() throws SQLException {\n        return xaDataSource.getLoginTimeout();\n    }\n\n    @Override\n    public Logger getParentLogger() throws SQLFeatureNotSupportedException {\n        return xaDataSource.getParentLogger();\n    }\n}\n"
  },
  {
    "path": "bonita-engine-standalone/src/main/java/org/bonitasoft/engine/xa/XAResourceIsSameRMOverride.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.xa;\n\nimport javax.transaction.xa.XAException;\nimport javax.transaction.xa.XAResource;\nimport javax.transaction.xa.Xid;\n\npublic class XAResourceIsSameRMOverride implements XAResource {\n\n    private XAResource xaResource;\n\n    private XAResourceIsSameRMOverride(XAResource xaResource) {\n        this.xaResource = xaResource;\n    }\n\n    static XAResource overrideSameRM(XAResource xaResource) {\n        return new XAResourceIsSameRMOverride(xaResource);\n    }\n\n    @Override\n    public void commit(Xid xid, boolean b) throws XAException {\n        xaResource.commit(xid, b);\n    }\n\n    @Override\n    public void end(Xid xid, int i) throws XAException {\n        xaResource.end(xid, i);\n    }\n\n    @Override\n    public void forget(Xid xid) throws XAException {\n        xaResource.forget(xid);\n    }\n\n    @Override\n    public int getTransactionTimeout() throws XAException {\n        return xaResource.getTransactionTimeout();\n    }\n\n    @Override\n    public boolean isSameRM(XAResource xaResource) throws XAException {\n        // always returns false to make it work on oracle\n        return false;\n    }\n\n    @Override\n    public int prepare(Xid xid) throws XAException {\n        return xaResource.prepare(xid);\n    }\n\n    @Override\n    public Xid[] recover(int i) throws XAException {\n        return xaResource.recover(i);\n    }\n\n    @Override\n    public void rollback(Xid xid) throws XAException {\n        xaResource.rollback(xid);\n    }\n\n    @Override\n    public boolean setTransactionTimeout(int i) throws XAException {\n        return xaResource.setTransactionTimeout(i);\n    }\n\n    @Override\n    public void start(Xid xid, int i) throws XAException {\n        xaResource.start(xid, i);\n    }\n}\n"
  },
  {
    "path": "bonita-engine-standalone/src/main/resources/jbossts-properties.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE properties SYSTEM \"http://java.sun.com/dtd/properties.dtd\">\n<!--\n  JBoss, Home of Professional Open Source\n  Copyright 2009, Red Hat Middleware LLC, and individual contributors\n  as indicated by the @author tags.\n  See the copyright.txt in the distribution for a\n  full listing of individual contributors.\n  This copyrighted material is made available to anyone wishing to use,\n  modify, copy, or redistribute it subject to the terms and conditions\n  of the GNU Lesser General Public License, v. 2.1.\n  This program is distributed in the hope that it will be useful, but WITHOUT A\n  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A\n  PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.\n  You should have received a copy of the GNU Lesser General Public License,\n  v.2.1 along with this distribution; if not, write to the Free Software\n  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,\n  MA  02110-1301, USA.\n\n  (C) 2009,\n  @author JBoss, a division of Red Hat.\n-->\n<properties>\n    <!--\n    This is the JBossTS configuration file for running ArjunaJTA.\n    It should be called jbossts-properties.xml.\n    You need a different version for ArjunaCore or JTS usage.\n\n    ***************************\n\n    Property values may be literals or be tokens of the form ${p1[,p2][:v]}\n    in which case the token values are substituted for the values of the corresponding system\n    properties as follows:\n\n    - Any occurrence of ${p} with the System.getProperty(p) value.\n    If there is no such property p defined, then the ${p} reference will remain unchanged.\n\n    - If the property reference is of the form ${p:v} and there is no such property p,\n    then the default value v will be returned.\n\n    - If the property reference is of the form ${p1,p2} or ${p1,p2:v} then\n    the primary and the secondary properties will be tried in turn, before\n    returning either the unchanged input, or the default value.\n\n    The property ${/} is replaced with System.getProperty(\"file.separator\")\n    value and the property ${:} is replaced with System.getProperty(\"path.separator\").\n\n    Note this substitution applies to property values only at the point they are read from\n    the config file. Tokens in system properties won't be substituted.\n    -->\n\n    <!-- (default is YES) -->\n    <entry key=\"CoordinatorEnvironmentBean.commitOnePhase\">YES</entry>\n\n    <!-- (default is 180 seconds) -->\n    <entry key=\"com.arjuna.ats.arjuna.coordinator.defaultTimeout\">${bonita.runtime.transaction.xa-timeout:180}</entry>\n\n    <!-- default is under user.home - must be writable!) -->\n    <entry key=\"com.arjuna.ats.arjuna.objectstore.objectStoreDir\">${bonita.runtime.transaction.xa-store-directory:build/tx-object-store}</entry>\n\n    <!-- (default is ON) -->\n    <entry key=\"ObjectStoreEnvironmentBean.transactionSync\">ON</entry>\n\n    <!-- (Must be unique across all Arjuna instances.) -->\n    <entry key=\"CoreEnvironmentBean.nodeIdentifier\">1</entry>\n\n    <!-- Which Xid types to recover -->\n    <entry key=\"JTAEnvironmentBean.xaRecoveryNodes\">1</entry>\n\n\n    <!-- All of the 3 ways below to configure \"userTransaction JNDI name\" work: -->\n    <entry key=\"JTAEnvironmentBean.userTransactionJNDIContext\">java:comp/UserTransaction</entry>\n    <!--<entry key=\"com.arjuna.ats.jta.utils.UTJNDIContext\">java:comp/UserTransaction</entry>-->\n    <!--<entry key=\"com.arjuna.ats.jta.common.JTAEnvironmentBean.userTransactionJNDIContext\">java:comp/UserTransaction</entry>-->\n\n\n    <entry key=\"JTAEnvironmentBean.xaResourceOrphanFilterClassNames\">\n        com.arjuna.ats.internal.jta.recovery.arjunacore.JTATransactionLogXAResourceOrphanFilter\n        com.arjuna.ats.internal.jta.recovery.arjunacore.JTANodeNameXAResourceOrphanFilter\n        com.arjuna.ats.internal.jta.recovery.arjunacore.SubordinationManagerXAResourceOrphanFilter\n    </entry>\n\n    <!--\n      Base port number for determining a unique number to associate with an instance of the transaction service\n      (which is needed in order to support multiple instances on the same machine).\n      Use the value 0 to allow the system to select the first available port number.\n      If the port number is non-zero and the port is in use then the value will be incremented until either a successful binding\n      to the loopback address is created or until the the maximum number of ports (specified by the\n      CoreEnvironmentBean.socketProcessIdMaxPorts property) have been tried or until the port number\n      reaches the maximum possible port number.\n    -->\n    <entry key=\"CoreEnvironmentBean.socketProcessIdPort\">0</entry>\n\n\n    <!--\n      Periodic recovery modules to use.  Invoked in the order they appear in the list.\n         Check http://www.jboss.org/community/docs/DOC-10788 for more information\n         on recovery modules and their configuration when running in various\n         deployments.\n    -->\n    <entry key=\"RecoveryEnvironmentBean.recoveryModuleClassNames\">\n        com.arjuna.ats.internal.arjuna.recovery.AtomicActionRecoveryModule\n        com.arjuna.ats.internal.txoj.recovery.TORecoveryModule\n        com.arjuna.ats.internal.jta.recovery.arjunacore.SubordinateAtomicActionRecoveryModule\n        com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule\n    </entry>\n\n    <!-- Expiry scanners to use (order of invocation is random). -->\n    <entry key=\"RecoveryEnvironmentBean.expiryScannerClassNames\">\n        com.arjuna.ats.internal.arjuna.recovery.ExpiredTransactionStatusManagerScanner\n    </entry>\n\n    <!--\n        Add the following to the set of expiryScannerClassNames above to move logs that cannot be completed by failure recovery.\n            But be sure you know what you are doing and why!\n             com.arjuna.ats.internal.arjuna.recovery.AtomicActionExpiryScanner\n    -->\n\n    <!--\n      The address and port number on which the recovery manager listens\n      If running within an AS then the address the AS is bound to (jboss.bind.address) takes precedence\n    -->\n    <entry key=\"RecoveryEnvironmentBean.recoveryPort\">4712</entry>\n\n    <entry key=\"RecoveryEnvironmentBean.recoveryAddress\"></entry>\n\n    <!--\n      Use this to fix the port on which the TransactionStatusManager listens,\n      The default behaviour is to use any free port.\n    -->\n    <entry key=\"RecoveryEnvironmentBean.transactionStatusManagerPort\">0</entry>\n\n    <!--\n      Use this to fix the address on which the TransactionStatusManager binds,\n      The default behaviour is to use the loopback address (ie localhost).\n      If running within an AS then the address the AS is bound to (jboss.bind.address) takes precedence\n    -->\n    <entry key=\"RecoveryEnvironmentBean.transactionStatusManagerAddress\"></entry>\n\n    <!--\n      For cases where the recovery manager is in process with the transaction manager and nothing else uses\n      the ObjectStore, it is possible to disable the socket based recovery listener by setting this to NO.\n      Caution: use of this property can allow multiple recovery processes to run on the same ObjectStore\n      if you are not careful. That in turn can lead to incorrect transaction processing. Use with care.\n    -->\n    <entry key=\"RecoveryEnvironmentBean.recoveryListener\">NO</entry>\n\n</properties>\n"
  },
  {
    "path": "bonita-engine-standalone/src/test/java/org/bonitasoft/engine/BonitaDataSourceInitializerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.apache.tomcat.dbcp.dbcp2.BasicDataSource;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\n\npublic class BonitaDataSourceInitializerTest {\n\n    @Rule\n    public ExpectedException expectedExceptions = ExpectedException.none();\n    private BonitaDataSourceInitializer bonitaDataSourceInitializer = new BonitaDataSourceInitializer();\n\n    @Test\n    public void should_fail_to_create_datasource_if_some_field_are_not_set() {\n        BonitaDatabaseConfiguration configuration = BonitaDatabaseConfiguration.builder()\n                .dbVendor(\"postgres\")\n                .url(\"jdbc::postgres\")\n                .build();\n\n        expectedExceptions.expect(IllegalArgumentException.class);\n        expectedExceptions.expectMessage(\"Database user not se\");\n\n        bonitaDataSourceInitializer.createDataSource(configuration);\n    }\n\n    @Test\n    public void should_fail_to_create_datasource_configuration_is_empty() {\n        expectedExceptions.expect(IllegalArgumentException.class);\n        expectedExceptions.expectMessage(\"Database dbVendor not set\");\n\n        bonitaDataSourceInitializer.createDataSource(new BonitaDatabaseConfiguration());\n    }\n\n    @Test\n    public void should_fail_if_db_vendor_is_not_supported() {\n        BonitaDatabaseConfiguration configuration = BonitaDatabaseConfiguration.builder()\n                .dbVendor(\"toto\")\n                .url(\"jdbc::postgres\")\n                .build();\n\n        expectedExceptions.expect(IllegalArgumentException.class);\n        expectedExceptions.expectMessage(\"Database db vendor toto is invalid\");\n\n        bonitaDataSourceInitializer.createDataSource(configuration);\n    }\n\n    @Test\n    public void should_create_datasource_with_configuration() {\n        BonitaDatabaseConfiguration configuration = BonitaDatabaseConfiguration.builder()\n                .dbVendor(\"postgres\")\n                .driverClassName(\"org.postgres.MyCustomDriver\")\n                .url(\"jdbc::postgres\")\n                .user(\"myUser\")\n                .password(\"secret\")\n                .build();\n\n        BasicDataSource dataSource = bonitaDataSourceInitializer.createDataSource(configuration);\n\n        assertThat(dataSource.getUrl()).isEqualTo(\"jdbc::postgres\");\n        assertThat(dataSource.getUsername()).isEqualTo(\"myUser\");\n        assertThat(dataSource.getDriverClassName()).isEqualTo(\"org.postgres.MyCustomDriver\");\n        assertThat(dataSource.getPassword()).isEqualTo(\"secret\");\n    }\n\n    @Test\n    public void should_provide_default_driver_according_to_dbVendor() {\n        BonitaDatabaseConfiguration configuration = BonitaDatabaseConfiguration.builder()\n                .dbVendor(\"mysql\")\n                .url(\"jdbc::localhost\")\n                .user(\"myUser\")\n                .build();\n\n        BasicDataSource dataSource = bonitaDataSourceInitializer.createDataSource(configuration);\n\n        assertThat(dataSource.getUrl()).isEqualTo(\"jdbc::localhost\");\n        assertThat(dataSource.getDriverClassName()).isEqualTo(\"com.mysql.cj.jdbc.Driver\");\n    }\n\n    @Test\n    public void should_set_pool_size_according_to_configuration() throws Exception {\n        BonitaDatabaseConfiguration configuration = BonitaDatabaseConfiguration.builder()\n                .dbVendor(\"mysql\")\n                .url(\"jdbc::localhost\")\n                .user(\"myUser\")\n                .xaDatasource(DatasourceConfiguration.builder().maxPoolSize(10).build())\n                .datasource(DatasourceConfiguration.builder().maxPoolSize(5).build())\n                .build();\n\n        BasicDataSource dataSource = bonitaDataSourceInitializer.createDataSource(configuration);\n        BasicDataSource managedDataSource = bonitaDataSourceInitializer.createManagedDataSource(configuration, null);\n\n        assertThat(dataSource.getMaxTotal()).isEqualTo(5);\n        assertThat(managedDataSource.getMaxTotal()).isEqualTo(10);\n    }\n\n}\n"
  },
  {
    "path": "bonita-engine-standalone/src/test/java/org/bonitasoft/engine/BonitaEngineTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.h2.jdbcx.JdbcDataSource;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.RestoreSystemProperties;\nimport org.junit.rules.TestRule;\n\npublic class BonitaEngineTest {\n\n    @Rule\n    public final TestRule restoreSystemProperties = new RestoreSystemProperties();\n\n    @Test\n    public void should_set_db_vendor_system_property_according_to_database_configuration() throws Exception {\n        BonitaEngine bonitaEngine = newBonitaEngine();\n        bonitaEngine.setBonitaDatabaseConfiguration(BonitaDatabaseConfiguration.builder()\n                .dbVendor(\"postgres\")\n                .url(\"jdbc:postgresql://someServer:5433/bonitadb\")\n                .user(\"user\")\n                .build());\n\n        bonitaEngine.initializeEnvironment();\n\n        assertThat(System.getProperty(BonitaEngine.BONITA_DB_VENDOR)).isEqualTo(\"postgres\");\n    }\n\n    @Test\n    public void should_set_db_vendor_system_property_to_h2_by_default() throws Exception {\n        BonitaEngine bonitaEngine = newBonitaEngine();\n\n        bonitaEngine.initializeEnvironment();\n\n        assertThat(System.getProperty(BonitaEngine.BONITA_DB_VENDOR)).isEqualTo(\"h2\");\n    }\n\n    @Test\n    public void should_configure_bonita_datasource_to_h2_by_default() throws Exception {\n        BonitaEngine bonitaEngine = newBonitaEngine();\n\n        bonitaEngine.initializeEnvironment();\n\n        JdbcDataSource xaDataSourceInstance = (JdbcDataSource) bonitaEngine.getBonitaDataSource()\n                .getXaDataSourceInstance();\n        assertThat(xaDataSourceInstance.getURL()).contains(\"h2\");\n        assertThat(xaDataSourceInstance.getDescription()).contains(\"RawDataSource of h2\");\n\n    }\n\n    private BonitaEngine newBonitaEngine() {\n        return new BonitaEngine();\n    }\n\n}\n"
  },
  {
    "path": "bonita-engine-standalone/src/test/java/org/bonitasoft/engine/DatabaseUrlParserTest.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.Test;\n\npublic class DatabaseUrlParserTest {\n\n    @Test\n    public void should_parse_postgres_url() {\n        String url = \"jdbc:postgresql://localhost:32782/bonita\";\n\n        DatabaseUrlParser.DatabaseMetadata metadata = DatabaseUrlParser.parsePostgresUrl(url);\n\n        assertThat(metadata.getDatabaseName()).isEqualTo(\"bonita\");\n        assertThat(metadata.getPort()).isEqualTo(\"32782\");\n        assertThat(metadata.getServerName()).isEqualTo(\"localhost\");\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void should_throw_exception_when_there_is_no_match() {\n        String url = \"jdbc:otherDb://localhost:32782/bonita\";\n\n        DatabaseUrlParser.parsePostgresUrl(url);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/benchmarks/README.md",
    "content": "Micro benchmarks\n================\n\n\nThis module contains micro benchmark tests that can be written and run to verify the performance of precise parts of the code.\n\n\n## Write a test\n\nsee example `org.bonitasoft.engine.benchmarks.TransactionSynchronizationBenchmark`\n\n## Run tests\n\n```\n./gradlew jmh\n```\n"
  },
  {
    "path": "bonita-integration-tests/benchmarks/build.gradle",
    "content": "plugins {\n    id(\"me.champeau.jmh\") version \"0.7.3\"\n}\n\ndependencies {\n    api libs.assertj\n    api libs.mockitoCore\n    api project(':bonita-integration-tests:bonita-integration-tests-client')\n    api project(':bpm:bonita-server')\n    testRuntimeOnly libs.logback\n}\n\njmh {\n    duplicateClassesStrategy = DuplicatesStrategy.WARN\n    iterations = 2\n    threads = 1\n    fork = 1\n}"
  },
  {
    "path": "bonita-integration-tests/benchmarks/src/jmh/java/org/bonitasoft/engine/benchmarks/PermissionCachingBenchmark.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.benchmarks;\n\nimport org.bonitasoft.engine.authorization.properties.ConfigurationFilesManager;\nimport org.bonitasoft.engine.authorization.properties.ResourcesPermissionsMapping;\nimport org.bonitasoft.engine.cache.CacheService;\nimport org.bonitasoft.engine.service.ServiceAccessorSingleton;\nimport org.bonitasoft.engine.test.TestEngine;\nimport org.bonitasoft.engine.test.TestEngineImpl;\nimport org.openjdk.jmh.annotations.Benchmark;\nimport org.openjdk.jmh.annotations.Scope;\nimport org.openjdk.jmh.annotations.Setup;\nimport org.openjdk.jmh.annotations.State;\nimport org.openjdk.jmh.annotations.TearDown;\n\n@State(Scope.Benchmark)\npublic class PermissionCachingBenchmark {\n\n    private TestEngine engine;\n    private ResourcesPermissionsMapping resourcesPermissionsMapping;\n    private CacheService cacheService;\n\n    @Setup\n    public void setup() throws Exception {\n        engine = TestEngineImpl.getInstance();\n        engine.start();\n        cacheService = ServiceAccessorSingleton.getInstance().getCacheService();\n        resourcesPermissionsMapping = new ResourcesPermissionsMapping(cacheService,\n                new ConfigurationFilesManager());\n    }\n\n    @TearDown\n    public void tearDown() throws Exception {\n        engine.stop();\n    }\n\n    @Benchmark\n    public void callPermissionWithCache() {\n        resourcesPermissionsMapping.getResourcePermissions(\"GET\", \"bpm\", \"case\");\n    }\n\n    @Benchmark\n    public void callPermissionFromCache50times() {\n        for (int i = 0; i < 50; i++) {\n            resourcesPermissionsMapping.getResourcePermissions(\"GET\", \"bpm\", \"case\");\n        }\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/benchmarks/src/jmh/java/org/bonitasoft/engine/benchmarks/TransactionSynchronizationBenchmark.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.benchmarks;\n\nimport java.util.Map;\nimport java.util.concurrent.CompletableFuture;\n\nimport org.bonitasoft.engine.execution.work.BPMWorkFactory;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.ServiceAccessorSingleton;\nimport org.bonitasoft.engine.test.TestEngine;\nimport org.bonitasoft.engine.test.TestEngineImpl;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.bonitasoft.engine.work.BonitaWork;\nimport org.bonitasoft.engine.work.WorkDescriptor;\nimport org.bonitasoft.engine.work.WorkService;\nimport org.openjdk.jmh.annotations.Benchmark;\nimport org.openjdk.jmh.annotations.Scope;\nimport org.openjdk.jmh.annotations.Setup;\nimport org.openjdk.jmh.annotations.State;\nimport org.openjdk.jmh.annotations.TearDown;\n\n@State(Scope.Benchmark)\npublic class TransactionSynchronizationBenchmark {\n\n    private TestEngine engine;\n    private UserTransactionService userTransactionService;\n    private WorkService workService;\n\n    @Setup\n    public void setup() throws Exception {\n        engine = TestEngineImpl.getInstance();\n        engine.start();\n        final ServiceAccessor serviceAccessor = ServiceAccessorSingleton.getInstance();\n        userTransactionService = serviceAccessor.getUserTransactionService();\n        workService = serviceAccessor.getWorkService();\n        BPMWorkFactory bpmWorkFactory = serviceAccessor.getBPMWorkFactory();\n        bpmWorkFactory.addExtension(\"BENCHMARK_WORK\", workDescriptor -> new BonitaWork() {\n\n            @Override\n            public String getDescription() {\n                return \"BENCHMARK_WORK\";\n            }\n\n            @Override\n            public CompletableFuture<Void> work(Map<String, Object> context) throws Exception {\n                return CompletableFuture.completedFuture(null);\n            }\n\n            @Override\n            public void handleFailure(Throwable e, Map<String, Object> context) throws Exception {\n\n            }\n        });\n    }\n\n    @TearDown\n    public void tearDown() throws Exception {\n        engine.stop();\n    }\n\n    @Benchmark\n    public void register1000Works() throws Exception {\n        userTransactionService.executeInTransaction(() -> {\n            for (int i = 0; i < 1000; i++) {\n                workService.registerWork(WorkDescriptor.create(\"BENCHMARK_WORK\"));\n            }\n            return null;\n        });\n    }\n\n    @Benchmark\n    public void register1Works() throws Exception {\n        userTransactionService.executeInTransaction(() -> {\n            for (int i = 0; i < 1; i++) {\n                workService.registerWork(WorkDescriptor.create(\"BENCHMARK_WORK\"));\n            }\n            return null;\n        });\n    }\n\n    @Benchmark\n    public void register2Works() throws Exception {\n        userTransactionService.executeInTransaction(() -> {\n            for (int i = 0; i < 2; i++) {\n                workService.registerWork(WorkDescriptor.create(\"BENCHMARK_WORK\"));\n            }\n            return null;\n        });\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/bonita-client-http.properties",
    "content": "org.bonitasoft.engine.api-type=HTTP\norg.bonitasoft.engine.api-type.server.url=127.255.0.123\norg.bonitasoft.engine.api-type.application.name=MyBonitaInstallation\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/bonita-client-invalid.properties",
    "content": "org.bonitasoft.engine.api-type = InvalidType\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/bonita-client-local.properties",
    "content": "org.bonitasoft.engine.api-type = LOCAL\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/build.gradle",
    "content": "plugins {\n    id(\"bonita-http-test\")\n    id(\"bonita-docker-database\")\n    id(\"bonita-tests\")\n}\ndependencies {\n    api project(':bonita-test-api')\n    api libs.commonsIO\n    api project(':bpm:bonita-common')\n    api project(':bpm:bonita-client')\n    api project(':bonita-integration-tests:bonita-test-utils')\n    api libs.assertj\n    api(libs.jsonUnit) {\n        exclude(group: \"org.slf4j\", module: \"slf4j-api\")\n    }\n    api libs.systemRules\n    api libs.concurrentUnit\n    runtimeOnly libs.logback\n    testImplementation project(':bpm:bonita-server')\n    testImplementation testFixtures(project(':bpm:bonita-common'))\n    testImplementation libs.awaitility\n    testImplementation libs.xmlunit\n    testImplementation libs.mockitoCore\n    testImplementation libs.logback\n}\n\ntest.doFirst {\n    def emptyFile = new File(\"$project.buildDir/bonita_home_client_HTTP/engine-client/conf/bonita-client-custom.properties\")\n    emptyFile.getParentFile().mkdirs()\n    emptyFile.text = \"\"\n    copy {\n        from file(\"$projectDir/bonita-client-http.properties\")\n        into file(\"$project.buildDir/bonita_home_client_HTTP/engine-client/work\")\n        rename 'bonita-client-http.properties', 'bonita-client-community.properties'\n    }\n    def invalidFile = new File(\"$project.buildDir/bonita_home_client_invalidAPIType/engine-client/conf/bonita-client-custom.properties\")\n    invalidFile.getParentFile().mkdirs()\n    invalidFile.text = \"\"\n    copy {\n        from file(\"$projectDir/bonita-client-invalid.properties\")\n        into file(\"$project.buildDir/bonita_home_client_invalidAPIType/engine-client/work\")\n        rename 'bonita-client-invalid.properties', 'bonita-client-community.properties'\n    }\n}\ngroup = 'org.bonitasoft.engine.test'\npublishing {\n    publications {\n        mavenJava(MavenPublication) { from project.components.java }\n    }\n}\n\nhttpTests {\n    integrationTestsSuite = \"**/*IT.class\"\n}\n\ndatabaseIntegrationTest { include \"**/*IT.class\" }"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/CommonAPIIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\nimport static org.junit.Assert.assertNotNull;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.apache.commons.io.IOUtils;\nimport org.assertj.core.api.Assertions;\nimport org.bonitasoft.engine.bpm.bar.BarResource;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.connectors.TestConnector;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.filter.user.GroupUserFilter;\nimport org.bonitasoft.engine.filter.user.TestFilter;\nimport org.bonitasoft.engine.filter.user.TestFilterThatThrowException;\nimport org.bonitasoft.engine.filter.user.TestFilterUsingActorName;\nimport org.bonitasoft.engine.filter.user.TestFilterWithAutoAssign;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.io.IOUtil;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.test.APITestUtil;\nimport org.bonitasoft.engine.test.junit.BonitaEngineRule;\nimport org.junit.Rule;\nimport org.junit.rules.TestRule;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic abstract class CommonAPIIT extends APITestUtil {\n\n    @Rule\n    public BonitaEngineRule bonitaEngineRule = BonitaEngineRule.create();\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(CommonAPIIT.class);\n\n    @Rule\n    public TestRule testWatcher = new PrintTestsStatusRule(LOGGER) {\n\n        @Override\n        public void clean() throws Exception {\n            CommonAPIIT.this.clean();\n        }\n    };\n\n    private void clean() throws BonitaException {\n        loginWithTechnicalUser();\n        cleanCommands();\n        cleanProcessInstances();\n        cleanApplications();\n        cleanPages();\n        cleanArchiveProcessInstances();\n        cleanProcessDefinitions();\n        cleanCategories();\n        cleanUsers();\n        cleanGroups();\n        cleanRoles();\n        cleanSupervisors();\n        checkThereAreNoWaitingEventsLeft();\n        cleanBdm();\n        logout();\n    }\n\n    private void checkThereAreNoWaitingEventsLeft() throws BonitaException {\n        SearchResult<?> searchResult = (SearchResult<?>) getCommandAPI()\n                .execute(\"searchWaitingEventsCommand\",\n                        Collections.singletonMap(\"searchOptions\", new SearchOptionsBuilder(0, 100).done()));\n        Assertions.assertThat(searchResult.getResult()).hasSize(0);\n    }\n\n    public BarResource getResource(final String path, final String name) throws IOException {\n        return getBarResource(path, name, CommonAPIIT.class);\n    }\n\n    public void addResource(final List<BarResource> resources, final String path, final String name)\n            throws IOException {\n        final BarResource barResource = getResource(path, name);\n        resources.add(barResource);\n    }\n\n    protected ProcessDefinition deployProcessWithTestFilter(final ProcessDefinitionBuilder processDefinitionBuilder,\n            final String actorName, final User user,\n            final String filterName) throws BonitaException, IOException {\n        final List<BarResource> userFilters = generateFilterImplementations(filterName);\n        final List<BarResource> generateFilterDependencies = generateFilterDependencies();\n        return deployAndEnableProcessWithActorAndUserFilter(processDefinitionBuilder, actorName, user,\n                generateFilterDependencies, userFilters);\n    }\n\n    private List<BarResource> generateFilterImplementations(final String filterName) throws IOException {\n        final List<BarResource> resources = new ArrayList<>(1);\n        final InputStream inputStream = TestConnector.class.getClassLoader()\n                .getResourceAsStream(\"org/bonitasoft/engine/filter/user/\" + filterName + \".impl\");\n        final byte[] data = IOUtil.getAllContentFrom(inputStream);\n        inputStream.close();\n        resources.add(new BarResource(filterName + \".impl\", data));\n        return resources;\n    }\n\n    private List<BarResource> generateFilterDependencies() throws IOException {\n        final List<BarResource> resources = new ArrayList<>(1);\n        byte[] data = IOUtil.generateJar(TestFilterThatThrowException.class);\n        resources.add(new BarResource(\"TestFilterThatThrowException.jar\", data));\n        data = IOUtil.generateJar(TestFilter.class);\n        resources.add(new BarResource(\"TestFilter.jar\", data));\n        data = IOUtil.generateJar(TestFilterWithAutoAssign.class);\n        resources.add(new BarResource(\"TestFilterWithAutoAssign.jar\", data));\n        data = IOUtil.generateJar(TestFilterUsingActorName.class);\n        resources.add(new BarResource(\"TestFilterUsingActorName.jar\", data));\n        data = IOUtil.generateJar(GroupUserFilter.class);\n        resources.add(new BarResource(\"TestGroupUserFilter.jar\", data));\n        return resources;\n    }\n\n    protected String getContentOfResource(final String name) {\n        final InputStream stream = this.getClass().getResourceAsStream(name);\n        assertNotNull(stream);\n        try {\n            try {\n                return IOUtils.toString(stream);\n            } finally {\n                stream.close();\n            }\n        } catch (final IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    @Override\n    public BarResource getBarResource(String path, String name, Class<?> clazz) throws IOException {\n        return super.getBarResource(path, name, clazz);\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/PrintTestsStatusRule.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\nimport org.junit.rules.TestWatcher;\nimport org.junit.runner.Description;\nimport org.slf4j.Logger;\n\n/**\n * @author Baptiste Mesta\n */\npublic abstract class PrintTestsStatusRule extends TestWatcher {\n\n    private final Logger logger;\n\n    public PrintTestsStatusRule(Logger logger) {\n\n        this.logger = logger;\n    }\n\n    @Override\n    public void starting(final Description d) {\n        logger.warn(\"Starting test: {}.{}\", d.getClassName(), d.getMethodName());\n    }\n\n    @Override\n    public void failed(final Throwable e, final Description d) {\n        logger.warn(\"Failed test: {}.{}\", d.getClassName(), d.getMethodName(), e);\n        try {\n            clean();\n        } catch (final Exception be) {\n            logger.error(\"unable to clean db\", be);\n        } finally {\n            logger.warn(\"------------------------------------------------------\");\n        }\n    }\n\n    @Override\n    public void succeeded(final Description d) {\n        logger.warn(\"Succeeded test: {}.{}\", d.getClassName(), d.getMethodName());\n        try {\n            clean();\n        } catch (final Exception e) {\n            throw new BonitaRuntimeException(e);\n        } finally {\n            logger.warn(\"------------------------------------------------------\");\n        }\n    }\n\n    public abstract void clean() throws Exception;\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/TestWithTechnicalUser.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\nimport org.junit.After;\nimport org.junit.Before;\n\n/**\n * @author Celine Souchet\n * @version 6.4.1\n * @since 6.4.1\n */\npublic class TestWithTechnicalUser extends CommonAPIIT {\n\n    @Before\n    public void before() throws Exception {\n        loginWithTechnicalUser();\n    }\n\n    @After\n    public void after() throws Exception {\n        logout();\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/TestWithUser.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\nimport org.bonitasoft.engine.identity.User;\nimport org.junit.After;\nimport org.junit.Before;\n\n/**\n * @author Celine Souchet\n * @version 6.4.1\n * @since 6.4.1\n */\npublic class TestWithUser extends TestWithTechnicalUser {\n\n    protected User user;\n\n    @Override\n    @Before\n    public void before() throws Exception {\n        super.before();\n        user = createUser(USERNAME, PASSWORD);\n        logout();\n        loginOnDefaultTenantWith(USERNAME, PASSWORD);\n    }\n\n    @Override\n    @After\n    public void after() throws Exception {\n        logout();\n        loginWithTechnicalUser();\n        deleteUser(USERNAME);\n        super.after();\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/business/application/TestWithCustomPage.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application;\n\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.engine.page.PageCreator;\nimport org.junit.After;\nimport org.junit.Before;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class TestWithCustomPage extends TestWithLivingApplication {\n\n    private Page page;\n\n    @Before\n    public void setUp() throws Exception {\n        super.setUp();\n        page = createPage(\"custompage_MyPage\");\n    }\n\n    @After\n    public void tearDown() throws Exception {\n        super.tearDown();\n        loginWithTechnicalUser();\n        if (page != null) {\n            getPageAPI().deletePage(page.getId());\n        }\n        logout();\n    }\n\n    public Page getPage() {\n        return page;\n    }\n\n    protected Page createPage(final String pageName) throws Exception {\n        return getPageAPI().createPage(new PageCreator(pageName, \"content.zip\").setDisplayName(pageName),\n                createTestPageContent(pageName, \"no display name\", \"empty desc\"));\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/business/application/TestWithLivingApplication.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipOutputStream;\n\nimport org.bonitasoft.engine.CommonAPIIT;\nimport org.bonitasoft.engine.api.ImportStatus;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.engine.page.PageCreator;\nimport org.bonitasoft.engine.profile.Profile;\nimport org.bonitasoft.engine.profile.ProfileSearchDescriptor;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.junit.After;\nimport org.junit.Before;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class TestWithLivingApplication extends CommonAPIIT {\n\n    static final String DEFAULT_LAYOUT_NAME = \"custompage_layoutBonita\";\n    static final String DEFAULT_THEME_NAME = \"custompage_themeBonita\";\n    private User user;\n\n    @Before\n    public void setUp() throws Exception {\n        loginWithTechnicalUser();\n        user = createUser(\"john\", \"bpm\");\n        logout();\n        loginOnDefaultTenantWith(\"john\", \"bpm\");\n    }\n\n    @After\n    public void tearDown() throws Exception {\n        final SearchResult<IApplication> searchResult = getLivingApplicationAPI()\n                .searchIApplications(new SearchOptionsBuilder(0, 1000).done());\n        for (final IApplication app : searchResult.getResult()) {\n            getLivingApplicationAPI().deleteApplication(app.getId());\n        }\n        logoutThenlogin();\n        deleteUser(user);\n        logout();\n    }\n\n    protected Profile getProfileUser() throws SearchException {\n        return getProfile(\"User\");\n    }\n\n    Profile getProfileAdmin() throws SearchException {\n        return getProfile(\"Administrator\");\n    }\n\n    private Profile getProfile(String profileName) throws SearchException {\n        SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 1);\n        builder.filter(ProfileSearchDescriptor.NAME, profileName);\n        SearchResult<Profile> profileSearchResult = getProfileAPI().searchProfiles(builder.done());\n        assertThat(profileSearchResult.getCount()).isEqualTo(1);\n        Profile profile = profileSearchResult.getResult().get(0);\n        return profile;\n    }\n\n    public User getUser() {\n        return user;\n    }\n\n    protected Page createPage(final String pageName) throws Exception {\n        return getPageAPI().createPage(new PageCreator(pageName, \"content.zip\").setDisplayName(pageName),\n                createPageContent(pageName));\n    }\n\n    private byte[] createPageContent(final String pageName)\n            throws BonitaException {\n        try {\n            final ByteArrayOutputStream baos = new ByteArrayOutputStream();\n            final ZipOutputStream zos = new ZipOutputStream(baos);\n            zos.putNextEntry(new ZipEntry(\"Index.groovy\"));\n            zos.write(\"return \\\"\\\";\".getBytes());\n\n            zos.putNextEntry(new ZipEntry(\"page.properties\"));\n            String s = \"name=\" + pageName + \"\\n\"\n                    + \"displayName=no display name\\n\"\n                    + \"description=empty desc\\n\";\n            zos.write(s.getBytes(\"UTF-8\"));\n\n            zos.closeEntry();\n            return baos.toByteArray();\n        } catch (final IOException e) {\n            throw new BonitaException(e);\n        }\n    }\n\n    protected SearchOptionsBuilder getAppSearchBuilderOrderByToken(final int startIndex, final int maxResults) {\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(startIndex, maxResults);\n        builder.sort(ApplicationSearchDescriptor.TOKEN, Order.ASC);\n        return builder;\n    }\n\n    protected void assertIsAddOkStatus(final ImportStatus importStatus, String expectedToken) {\n        assertThat(importStatus.getName()).isEqualTo(expectedToken);\n        assertThat(importStatus.getStatus()).isEqualTo(ImportStatus.Status.ADDED);\n        assertThat(importStatus.getErrors()).isEmpty();\n    }\n\n    protected SearchOptionsBuilder getAppSearchBuilderOrderById(final int startIndex, final int maxResults) {\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(startIndex, maxResults);\n        builder.sort(ApplicationSearchDescriptor.ID, Order.ASC);\n        return builder;\n    }\n\n    protected void assertIsMarketingApplication(final Application app) {\n        assertThat(app.getToken()).isEqualTo(\"My\");\n        assertThat(app.getVersion()).isEqualTo(\"2.0\");\n        assertThat(app.getDisplayName()).isEqualTo(\"Marketing\");\n        assertThat(app.getDescription()).isNull();\n        assertThat(app.getIconPath()).isNull();\n        assertThat(app.getState()).isEqualTo(\"ACTIVATED\");\n        assertThat(app.getProfileId()).isNull();\n    }\n\n    protected void assertIsHRApplication(final Profile profile, final Page layout, final Page theme,\n            final Application app) {\n        assertThat(app.getToken()).isEqualTo(\"HR-dashboard\");\n        assertThat(app.getVersion()).isEqualTo(\"2.0\");\n        assertThat(app.getDisplayName()).isEqualTo(\"My HR dashboard\");\n        assertThat(app.getDescription()).isEqualTo(\"This is the HR dashboard.\");\n        assertThat(app.getIconPath()).isEqualTo(\"/icon.jpg\");\n        assertThat(app.getState()).isEqualTo(\"ACTIVATED\");\n        assertThat(app.getProfileId()).isEqualTo(profile.getId());\n        assertThat(app.getLayoutId()).isEqualTo(layout.getId());\n        assertThat(app.getThemeId()).isEqualTo(theme.getId());\n    }\n\n    protected void assertIsMyNewCustomPage(final Page myPage, final Application hrApp,\n            final ApplicationPage applicationPage) {\n        assertThat(applicationPage.getApplicationId()).isEqualTo(hrApp.getId());\n        assertThat(applicationPage.getToken()).isEqualTo(\"my-new-custom-page\");\n        assertThat(applicationPage.getPageId()).isEqualTo(myPage.getId());\n    }\n\n    protected void assertIsHrFollowUpMenu(final ApplicationMenu applicationMenu) {\n        assertThat(applicationMenu.getParentId()).isNull();\n        assertThat(applicationMenu.getDisplayName()).isEqualTo(\"HR follow-up\");\n        assertThat(applicationMenu.getApplicationPageId()).isNull();\n    }\n\n    protected void assertIsDailyHrFollowUpMenu(final ApplicationMenu applicationMenu, ApplicationMenu hrFollowUpMenu,\n            ApplicationPage myNewCustomPage) {\n        assertThat(applicationMenu.getParentId()).isEqualTo(hrFollowUpMenu.getId());\n        assertThat(applicationMenu.getDisplayName()).isEqualTo(\"Daily HR follow-up\");\n        assertThat(applicationMenu.getApplicationPageId()).isEqualTo(myNewCustomPage.getId());\n    }\n\n    protected void assertIsEmptyMenu(final ApplicationMenu applicationMenu) {\n        assertThat(applicationMenu.getParentId()).isNull();\n        assertThat(applicationMenu.getDisplayName()).isEqualTo(\"Empty menu\");\n        assertThat(applicationMenu.getApplicationPageId()).isNull();\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/business/data/BusinessDataUpdateConnector.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data;\n\nimport java.lang.reflect.Method;\n\nimport org.bonitasoft.engine.connector.AbstractConnector;\nimport org.bonitasoft.engine.connector.ConnectorException;\n\npublic class BusinessDataUpdateConnector extends AbstractConnector {\n\n    @Override\n    public void validateInputParameters() {\n    }\n\n    @Override\n    protected void executeBusinessLogic() throws ConnectorException {\n        final Object bizData = getInputParameter(\"bizData\");\n        try {\n            Method method = bizData.getClass().getMethod(\"addToPhoneNumbers\", String.class);\n            method.invoke(bizData, \"48665421\");\n\n            method = bizData.getClass().getMethod(\"setLastName\", String.class);\n            method.invoke(bizData, \"Hakkinen\");\n            setOutputParameter(\"output1\", bizData);\n        } catch (final Exception e) {\n            throw new ConnectorException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/business/data/ClassloaderRefresher.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.apache.commons.io.FileUtils;\nimport org.bonitasoft.engine.io.IOUtils;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class ClassloaderRefresher {\n\n    /**\n     * @param clientZipContent\n     * @param contextClassLoader\n     * @param modelClass\n     * @param fsFolderToPutJars\n     * @return the newly created classloader with newly loaded class, if found.\n     * @throws java.io.IOException\n     * @throws java.net.MalformedURLException\n     */\n    public ClassLoader loadClientModelInClassloader(final byte[] clientZipContent, final ClassLoader contextClassLoader,\n            final String modelClass,\n            final File fsFolderToPutJars) throws IOException {\n        final Map<String, byte[]> ressources = IOUtils.unzip(clientZipContent);\n        final List<URL> urls = new ArrayList<>();\n        for (final Entry<String, byte[]> e : ressources.entrySet()) {\n            final File file = new File(fsFolderToPutJars, e.getKey());\n            if (file.getName().endsWith(\".jar\")) {\n                if (file.getName().contains(\"model\")) {\n                    try {\n                        contextClassLoader.loadClass(modelClass);\n                    } catch (final ClassNotFoundException e1) {\n                        FileUtils.writeByteArrayToFile(file, e.getValue());\n                        urls.add(file.toURI().toURL());\n                    }\n                }\n                if (file.getName().contains(\"dao\")) {\n                    try {\n                        contextClassLoader.loadClass(modelClass + \"DAOImpl\");\n                    } catch (final ClassNotFoundException e1) {\n                        FileUtils.writeByteArrayToFile(file, e.getValue());\n                        urls.add(file.toURI().toURL());\n                    }\n                }\n                if (file.getName().contains(\"javassist\")) {\n                    try {\n                        contextClassLoader.loadClass(\"javassist.util.proxy.MethodFilter\");\n                    } catch (final ClassNotFoundException e1) {\n                        FileUtils.writeByteArrayToFile(file, e.getValue());\n                        urls.add(file.toURI().toURL());\n                    }\n                }\n            }\n        }\n        ClassLoader classLoaderWithBDM = contextClassLoader;\n        if (!urls.isEmpty()) {\n            classLoaderWithBDM = new URLClassLoader(urls.toArray(new URL[0]), contextClassLoader);\n        }\n        return classLoaderWithBDM;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/ProcessDeployer.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.helper;\n\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.command.helper.designer.SimpleProcessDesigner;\nimport org.bonitasoft.engine.exception.BonitaException;\n\n/**\n * Created by Vincent Elcrin\n * Date: 16/12/13\n * Time: 11:40\n */\npublic abstract class ProcessDeployer {\n\n    ProcessDefinition processDefinition;\n\n    public ProcessDefinition deploy(SimpleProcessDesigner design) throws BonitaException {\n        try {\n            return processDefinition = deploy(design.done());\n        } catch (InvalidProcessDefinitionException e) {\n            throw new BonitaException(e);\n        }\n    }\n\n    public abstract ProcessDefinition deploy(DesignProcessDefinition design) throws BonitaException;\n\n    public void clean() throws BonitaException {\n        clean(processDefinition);\n    }\n\n    public abstract void clean(ProcessDefinition processDefinition) throws BonitaException;\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/designer/BoundaryEvent.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.helper.designer;\n\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder;\n\n/**\n * @author Vincent Elcrin\n */\npublic class BoundaryEvent {\n\n    private final String name;\n\n    private final Fragment flowsOut;\n\n    private Trigger trigger;\n\n    Transition transition = new Transition();\n\n    public BoundaryEvent(String name, Fragment flowsOut) {\n        this.name = name;\n        this.flowsOut = flowsOut;\n    }\n\n    public void attach(UserTaskDefinitionBuilder task, ProcessDefinitionBuilder builder) {\n        trigger.listen(task.addBoundaryEvent(name));\n        flowsOut.build(builder);\n        transition.bind(name, flowsOut.getName(), builder);\n    }\n\n    public BoundaryEvent triggeredBy(Trigger trigger) {\n        this.trigger = trigger;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/designer/Branch.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.helper.designer;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\n\n/**\n * @author Vincent Elcrin\n */\npublic class Branch extends Fragment {\n\n    private List<List<Fragment>> sequence = new ArrayList<List<Fragment>>();\n\n    private Fragment origin;\n\n    private String name;\n\n    public Branch start(Fragment origin) {\n        this.origin = origin;\n        name = origin.getName();\n        return this;\n    }\n\n    public Branch then(Fragment... targets) {\n        assert origin != null : \"Branch need to be started by calling start method\";\n        sequence.add(Arrays.asList(targets));\n        name = targets[0].getName();\n        return this;\n    }\n\n    @Override\n    public String getName() {\n        assert name != null;\n        return name;\n    }\n\n    @Override\n    public void bind(List<Fragment> sources, ProcessDefinitionBuilder builder) {\n        super.bind(sources, builder);\n    }\n\n    @Override\n    public void bind(String source, Transition transition, ProcessDefinitionBuilder builder) {\n        transition.bind(source, origin.getName(), builder);\n    }\n\n    @Override\n    public void build(ProcessDefinitionBuilder builder) {\n        List<Fragment> sources = new ArrayList<Fragment>();\n        origin.build(builder);\n        sources.add(origin);\n        for (List<Fragment> targets : sequence) {\n            link(sources, targets, builder);\n        }\n    }\n\n    private void link(List<Fragment> sources, List<Fragment> targets, ProcessDefinitionBuilder builder) {\n        for (Fragment target : targets) {\n            target.build(builder);\n            target.bind(sources, builder);\n        }\n        sources.clear();\n        sources.addAll(targets);\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/designer/ConditionalTransition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.helper.designer;\n\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.expression.Expression;\n\n/**\n * Created by vince on 1/28/14.\n */\npublic class ConditionalTransition extends Transition {\n\n    private Fragment otherwise;\n\n    private Expression expression;\n\n    public ConditionalTransition(Expression expression) {\n        this.expression = expression;\n    }\n\n    @Override\n    public void bind(String source, String target, ProcessDefinitionBuilder builder) {\n        builder.addTransition(source, target, expression);\n\n        if (otherwise != null) {\n            otherwise.build(builder);\n            otherwise.bind(source, new DefaultTransition(), builder);\n        }\n    }\n\n    public ConditionalTransition otherwise(Fragment otherwise) {\n        this.otherwise = otherwise;\n        return this;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/designer/DefaultTransition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.helper.designer;\n\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.expression.Expression;\n\npublic class DefaultTransition extends Transition {\n\n    private ConditionalBranch branch;\n\n    @Override\n    public void bind(String source, String target, ProcessDefinitionBuilder builder) {\n        builder.addDefaultTransition(source, target);\n        if (branch != null) {\n            branch.build(source, builder);\n        }\n    }\n\n    public interface ConditionalBranch {\n\n        void build(String source, ProcessDefinitionBuilder builder);\n\n        DefaultTransition goingTo(Fragment then);\n    }\n\n    public ConditionalBranch toMeet(final Expression expression) {\n        branch = new ConditionalBranch() {\n\n            private Fragment then;\n\n            public void build(String source, ProcessDefinitionBuilder builder) {\n                then.build(builder);\n                then.bind(source, new ConditionalTransition(expression), builder);\n            }\n\n            public DefaultTransition goingTo(Fragment then) {\n                this.then = then;\n                return DefaultTransition.this;\n            }\n        };\n        return branch;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/designer/EndEvent.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.helper.designer;\n\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\n\n/**\n * Created by Vincent Elcrin\n * Date: 16/12/13\n * Time: 11:46\n */\npublic class EndEvent extends FlowNode {\n\n    public EndEvent(String name) {\n        super(name);\n    }\n\n    @Override\n    public void build(ProcessDefinitionBuilder builder) {\n        builder.addEndEvent(getName());\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/designer/FlowNode.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.helper.designer;\n\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\n\n/**\n * @author Vincent Elcrin\n */\npublic abstract class FlowNode extends Fragment {\n\n    private String name;\n\n    protected FlowNode(String name) {\n        this.name = name;\n    }\n\n    @Override\n    public void bind(String source, Transition transition, ProcessDefinitionBuilder builder) {\n        transition.bind(source, name, builder);\n    }\n\n    @Override\n    public String getName() {\n        return name;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/designer/Fragment.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.helper.designer;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\n\n/**\n * Created by Vincent Elcrin\n * Date: 16/12/13\n * Time: 14:20\n */\npublic abstract class Fragment {\n\n    private final Map<String, Transition> transitions = new HashMap<String, Transition>();\n\n    public Fragment when(final String step, final Transition transition) {\n        transitions.put(step, transition);\n        return this;\n    }\n\n    public void bind(final List<Fragment> sources, final ProcessDefinitionBuilder builder) {\n        for (final Fragment source : sources) {\n            Transition transition = transitions.get(source.getName());\n            if (transition == null) {\n                transition = new Transition();\n            }\n            bind(source.getName(), transition, builder);\n        }\n    }\n\n    public abstract void bind(String source, Transition transition, ProcessDefinitionBuilder builder);\n\n    public abstract void build(ProcessDefinitionBuilder builder);\n\n    public abstract String getName();\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/designer/Gateway.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.helper.designer;\n\nimport org.bonitasoft.engine.bpm.flownode.GatewayType;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\n\n/**\n * Created by Vincent Elcrin\n * Date: 16/12/13\n * Time: 12:20\n */\npublic class Gateway extends FlowNode {\n\n    private GatewayType type;\n\n    public Gateway(String name, GatewayType type) {\n        super(name);\n        this.type = type;\n    }\n\n    @Override\n    public void build(ProcessDefinitionBuilder builder) {\n        builder.addGateway(getName(), type);\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/designer/Signal.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.helper.designer;\n\nimport org.bonitasoft.engine.bpm.process.impl.BoundaryEventDefinitionBuilder;\n\n/**\n * @author Vincent Elcrin\n */\npublic class Signal implements Trigger {\n\n    private String name;\n\n    public Signal(String name) {\n        this.name = name;\n    }\n\n    @Override\n    public void listen(BoundaryEventDefinitionBuilder builder) {\n        builder.addSignalEventTrigger(name);\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/designer/SimpleProcessDesigner.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.helper.designer;\n\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\n\n/**\n * Created by Vincent Elcrin\n * Date: 16/12/13\n * Time: 18:38\n */\npublic class SimpleProcessDesigner {\n\n    private final Branch branch;\n\n    private ProcessDefinitionBuilder builder;\n\n    public SimpleProcessDesigner(ProcessDefinitionBuilder builder) {\n        this.builder = builder;\n        this.branch = new Branch();\n    }\n\n    public SimpleProcessDesigner start() {\n        startWith(new StartEvent(\"start\"));\n        return this;\n    }\n\n    public SimpleProcessDesigner startWith(StartEvent start) {\n        branch.start(start);\n        return this;\n    }\n\n    public SimpleProcessDesigner then(Fragment... targets) {\n        branch.then(targets);\n        return this;\n    }\n\n    public SimpleProcessDesigner end() {\n        return then(new EndEvent(\"end\"));\n    }\n\n    public DesignProcessDefinition done() throws InvalidProcessDefinitionException {\n        branch.build(builder);\n        return builder.done();\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/designer/StartEvent.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.helper.designer;\n\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\n\n/**\n * Created by Vincent Elcrin\n * Date: 16/12/13\n * Time: 11:45\n */\npublic class StartEvent extends FlowNode {\n\n    public StartEvent(String name) {\n        super(name);\n    }\n\n    @Override\n    public void build(ProcessDefinitionBuilder builder) {\n        builder.addStartEvent(getName());\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/designer/Transition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.helper.designer;\n\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.expression.Expression;\n\n/**\n * @author Vincent Elcrin\n */\npublic class Transition {\n\n    public void bind(String source, String target, ProcessDefinitionBuilder builder) {\n        builder.addTransition(source, target);\n    }\n\n    public static ConditionalTransition meet(Expression expression) {\n        return new ConditionalTransition(expression);\n    }\n\n    public static DefaultTransition fails() {\n        return new DefaultTransition();\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/designer/Trigger.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.helper.designer;\n\nimport org.bonitasoft.engine.bpm.process.impl.BoundaryEventDefinitionBuilder;\n\n/**\n * @author Vincent Elcrin\n */\npublic interface Trigger {\n\n    void listen(BoundaryEventDefinitionBuilder builder);\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/designer/UserTask.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.helper.designer;\n\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder;\n\n/**\n * Created by Vincent Elcrin\n * Date: 16/12/13\n * Time: 11:43\n */\npublic class UserTask extends FlowNode {\n\n    private String actor = \"actor\";\n\n    private BoundaryEvent event;\n\n    public UserTask(String name) {\n        super(name);\n    }\n\n    @Override\n    public void build(ProcessDefinitionBuilder builder) {\n        UserTaskDefinitionBuilder task = builder.addUserTask(getName(), actor);\n        if (event != null) {\n            event.attach(task, builder);\n        }\n    }\n\n    public UserTask with(BoundaryEvent event) {\n        this.event = event;\n        return this;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/expectation/DocumentExpectation.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.helper.expectation;\n\nimport static org.junit.Assert.assertEquals;\n\nimport org.bonitasoft.engine.CommonAPIIT;\nimport org.bonitasoft.engine.bpm.document.Document;\nimport org.bonitasoft.engine.bpm.document.DocumentNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class DocumentExpectation {\n\n    private CommonAPIIT testCase;\n\n    private ProcessInstance process;\n\n    private String name;\n\n    public DocumentExpectation(CommonAPIIT testCase, ProcessInstance process, String name) {\n        this.testCase = testCase;\n        this.process = process;\n        this.name = name;\n    }\n\n    public void toBe(String variable) throws DocumentNotFoundException {\n        Document document = testCase.getProcessAPI().getLastDocument(process.getId(), name);\n        String storageId = document.getContentStorageId();\n        assertEquals(variable, new String(testCase.getProcessAPI().getDocumentContent(storageId)));\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/expectation/ProcessExpectation.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.helper.expectation;\n\nimport org.bonitasoft.engine.CommonAPIIT;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\n\n/**\n * @author Vincent Elcrin\n */\npublic class ProcessExpectation {\n\n    private CommonAPIIT testCase;\n\n    private ProcessInstance process;\n\n    public ProcessExpectation(CommonAPIIT testCase, ProcessInstance process) {\n        this.testCase = testCase;\n        this.process = process;\n    }\n\n    public void toFinish() throws Exception {\n        testCase.waitForProcessToFinish(process.getId());\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/expectation/StepExpectation.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.helper.expectation;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.fail;\n\nimport java.util.concurrent.TimeoutException;\n\nimport org.bonitasoft.engine.CommonAPIIT;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\n\n/**\n * @author Vincent Elcrin\n */\npublic class StepExpectation {\n\n    private CommonAPIIT testCase;\n\n    private ProcessInstance process;\n\n    private String[] steps;\n\n    public StepExpectation(CommonAPIIT testCase, ProcessInstance process, String... steps) {\n        this.testCase = testCase;\n        this.process = process;\n        this.steps = steps;\n    }\n\n    public void toBeReady() throws Exception {\n        for (String step : steps) {\n            isReady(step);\n        }\n    }\n\n    public void toNotHaveArchives() throws Exception {\n        for (String step : steps) {\n            assertEquals(0, getArchives(step, false, null).getCount());\n        }\n    }\n\n    public void toBeExecuted(int times) throws SearchException {\n        for (String step : steps) {\n            assertEquals(times, getArchives(step, true, \"completed\").getCount());\n        }\n    }\n\n    public void toBeAborted() throws SearchException {\n        for (String step : steps) {\n            assertEquals(1, getArchives(step, true, \"aborted\").getCount());\n        }\n    }\n\n    private void isReady(String step) throws Exception {\n        try {\n            testCase.waitForFlowNodeInReadyState(process, step, false);\n        } catch (TimeoutException e) {\n            fail(step + \" is expected to be started.\");\n        }\n    }\n\n    private SearchResult<ArchivedFlowNodeInstance> getArchives(String step, boolean terminal, String state)\n            throws SearchException {\n        SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 0);\n        builder.filter(ArchivedFlowNodeInstanceSearchDescriptor.NAME, step);\n        builder.filter(ArchivedFlowNodeInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID, process.getId());\n        if (terminal) {\n            builder.filter(ArchivedFlowNodeInstanceSearchDescriptor.TERMINAL, true);\n        }\n        if (state != null) {\n            builder.filter(ArchivedFlowNodeInstanceSearchDescriptor.STATE_NAME, state);\n        }\n        return testCase.getProcessAPI().searchArchivedFlowNodeInstances(builder.done());\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/expectation/TestUtils.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.helper.expectation;\n\nimport org.bonitasoft.engine.CommonAPIIT;\nimport org.bonitasoft.engine.bpm.flownode.SendEventException;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.identity.User;\n\n/**\n * Created by Vincent Elcrin\n * Date: 17/12/13\n * Time: 10:46\n */\npublic class TestUtils {\n\n    private final CommonAPIIT testCase;\n\n    public TestUtils(final CommonAPIIT testCase) {\n        this.testCase = testCase;\n    }\n\n    public Process wrap(final ProcessInstance process) {\n        return new Process(process);\n    }\n\n    public class Process {\n\n        private final ProcessInstance process;\n\n        Process(final ProcessInstance process) {\n            this.process = process;\n        }\n\n        public void execute(final User behalf, final String... steps) throws Exception {\n            for (final String step : steps) {\n                testCase.waitForUserTaskAndExecuteIt(process, step, behalf);\n            }\n        }\n\n        public StepExpectation expect(final String... steps) {\n            return new StepExpectation(testCase, process, steps);\n        }\n\n        public ProcessExpectation isExpected() {\n            return new ProcessExpectation(testCase, process);\n        }\n\n        public VariableExpectation expectVariable(final String name) {\n            return new VariableExpectation(testCase, process, name);\n        }\n\n        public DocumentExpectation expectDocument(final String name) {\n            return new DocumentExpectation(testCase, process, name);\n        }\n\n        public void sendSignal(final String name) throws SendEventException {\n            testCase.getProcessAPI().sendSignal(name);\n        }\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/expectation/VariableExpectation.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.helper.expectation;\n\nimport static org.junit.Assert.assertEquals;\n\nimport java.io.Serializable;\n\nimport org.bonitasoft.engine.CommonAPIIT;\nimport org.bonitasoft.engine.bpm.data.DataNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\n\n/**\n * @author Vincent Elcrin\n */\npublic class VariableExpectation {\n\n    private CommonAPIIT testCase;\n\n    private ProcessInstance process;\n\n    private String name;\n\n    public VariableExpectation(CommonAPIIT testCase, ProcessInstance process, String name) {\n        this.testCase = testCase;\n        this.process = process;\n        this.name = name;\n    }\n\n    public void toBe(Serializable variable) throws DataNotFoundException {\n        assertEquals(variable, testCase.getProcessAPI().getProcessDataInstance(name, process.getId()).getValue());\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/connectors/ConnectorExecutionIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connectors;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.bar.BarResource;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.filter.user.TestFilterWithAutoAssign;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.io.IOUtil;\nimport org.bonitasoft.engine.test.BuildTestUtil;\nimport org.junit.After;\nimport org.junit.Before;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic abstract class ConnectorExecutionIT extends TestWithUser {\n\n    protected long userId;\n\n    public static final String DEFAULT_EXTERNAL_CONNECTOR_ID = \"org.bonitasoft.connector.testExternalConnector\";\n\n    public static final String DEFAULT_EXTERNAL_CONNECTOR_VERSION = \"1.0\";\n\n    @Before\n    public void beforeTest() {\n        userId = user.getId();\n    }\n\n    @After\n    public void afterTest() {\n        VariableStorage.clearAll();\n    }\n\n    private BarResource buildBarResourceForFilterWithAutoAssign() throws IOException {\n        final InputStream inputStream = TestConnector.class.getClassLoader().getResourceAsStream(\n                \"org/bonitasoft/engine/filter/user/TestFilterWithAutoAssign.impl\");\n        final byte[] data = IOUtil.getAllContentFrom(inputStream);\n        inputStream.close();\n        return new BarResource(\"TestFilter.impl\", data);\n    }\n\n    public ProcessDefinition deployProcessWithActorAndTestConnectorEngineExecutionContextAndFilterWithAutoAssign(\n            final ProcessDefinitionBuilder processDefinitionBuilder, final String actorName, final User user)\n            throws IOException, BonitaException {\n        final List<BarResource> connectorImplementations = Arrays.asList(BuildTestUtil.getContentAndBuildBarResource(\n                \"TestConnectorEngineExecutionContext.impl\",\n                TestConnectorEngineExecutionContext.class));\n        final List<BarResource> userFilters = Arrays.asList(buildBarResourceForFilterWithAutoAssign());\n        final List<BarResource> generateConnectorDependencies = Arrays.asList(\n                BuildTestUtil.generateJarAndBuildBarResource(TestConnectorEngineExecutionContext.class,\n                        \"TestConnectorEngineExecutionContext.jar\"),\n                BuildTestUtil.generateJarAndBuildBarResource(TestFilterWithAutoAssign.class,\n                        \"TestFilterWithAutoAssign.jar\"));\n        return deployAndEnableProcessWithActorAndConnectorAndUserFilter(processDefinitionBuilder, actorName, user,\n                connectorImplementations,\n                generateConnectorDependencies, userFilters);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithTestConnectorWithAPICall(\n            final ProcessDefinitionBuilder processDefinitionBuilder)\n            throws BonitaException, IOException {\n        return deployAndEnableProcessWithConnector(processDefinitionBuilder, \"TestConnectorWithAPICall.impl\",\n                TestConnectorWithAPICall.class,\n                \"TestConnectorWithAPICall.jar\");\n    }\n\n    public ProcessDefinition deployProcessWithExternalTestConnector(\n            final ProcessDefinitionBuilder processDefinitionBuilder, final String actorName,\n            final User user) throws BonitaException, IOException {\n        return deployAndEnableProcessWithActorAndConnector(processDefinitionBuilder, actorName, user,\n                \"TestExternalConnector.impl\",\n                TestExternalConnector.class, \"TestExternalConnector.jar\");\n    }\n\n    public ProcessDefinition deployProcessWithActorAndTestConnectorWithConnectedResource(\n            final ProcessDefinitionBuilder processDefinitionBuilder,\n            final String actorName, final User user) throws BonitaException, IOException {\n        return deployAndEnableProcessWithActorAndConnector(processDefinitionBuilder, actorName, user,\n                \"TestConnectorWithConnectedResource.impl\",\n                TestConnectorWithConnectedResource.class, \"TestConnectorWithConnectedResource.jar\");\n    }\n\n    public ProcessDefinition deployProcessWithActorAndTestConnectorWithNotSerializableOutput(\n            final ProcessDefinitionBuilder processDefinitionBuilder,\n            final String actorName, final User user) throws BonitaException, IOException {\n        return deployAndEnableProcessWithActorAndConnector(processDefinitionBuilder, actorName, user,\n                \"TestConnectorWithNotSerializableOutput.impl\",\n                TestConnectorWithNotSerializableOutput.class, \"TestConnectorWithNotSerializableOutput.jar\");\n    }\n\n    public ProcessDefinition deployProcessWithActorAndTestConnector3(\n            final ProcessDefinitionBuilder processDefinitionBuilder, final String actorName,\n            final User user) throws BonitaException, IOException {\n        final List<BarResource> connectorImplementations = Collections\n                .singletonList(BuildTestUtil.getContentAndBuildBarResource(\"TestConnector3.impl\",\n                        TestConnector3.class));\n        final List<BarResource> generateConnectorDependencies = Arrays.asList(\n                BuildTestUtil.generateJarAndBuildBarResource(TestConnector3.class, \"TestConnector3.jar\"),\n                BuildTestUtil.generateJarAndBuildBarResource(VariableStorage.class, \"VariableStorage.jar\"));\n        return deployAndEnableProcessWithActorAndConnectorAndParameter(processDefinitionBuilder, actorName, user,\n                connectorImplementations,\n                generateConnectorDependencies, null);\n    }\n\n    public ProcessDefinition deployProcessWithActorAndTestConnectorEngineExecutionContext(\n            final ProcessDefinitionBuilder processDefinitionBuilder,\n            final String actorName, final User user) throws BonitaException, IOException {\n        return deployAndEnableProcessWithActorAndConnector(processDefinitionBuilder, actorName, user,\n                \"TestConnectorEngineExecutionContext.impl\",\n                TestConnectorEngineExecutionContext.class, \"TestConnectorEngineExecutionContext.jar\");\n    }\n\n    public ProcessDefinition deployProcessWithActorAndTestConnectorThatThrowException(\n            final ProcessDefinitionBuilder processDefinitionBuilder,\n            final String actor, final User user) throws BonitaException, IOException {\n        return deployProcessWithActorAndTestConnectorThatThrowExceptionAndParameter(processDefinitionBuilder, actor,\n                user, null);\n    }\n\n    public ProcessDefinition deployProcessWithActorAndTestConnectorThatThrowExceptionAndParameter(\n            final ProcessDefinitionBuilder processDefinitionBuilder,\n            final String actorName, final User user, final Map<String, String> parameters)\n            throws BonitaException, IOException {\n        return deployAndEnableProcessWithActorAndConnectorAndParameter(processDefinitionBuilder, actorName, user,\n                parameters,\n                \"TestConnectorThatThrowException.impl\", TestConnectorThatThrowException.class,\n                \"TestConnectorThatThrowException.jar\");\n    }\n\n    public ProcessDefinition deployProcessWithActorAndTestConnector(\n            final ProcessDefinitionBuilder processDefinitionBuilder,\n            final String actor, final User user) throws BonitaException, IOException {\n        return deployProcessWithActorAndTestConnectorAndParameter(processDefinitionBuilder, actor, user, null);\n    }\n\n    public ProcessDefinition deployProcessWithActorAndTestConnectorAndParameter(\n            final ProcessDefinitionBuilder processDefinitionBuilder,\n            final String actorName, final User user, final Map<String, String> parameters)\n            throws BonitaException, IOException {\n        return deployProcessWithActorAndTestConnectorAndParameter(processDefinitionBuilder, actorName, user, parameters,\n                \"TestConnector.impl\", \"TestConnector.jar\");\n    }\n\n    private ProcessDefinition deployProcessWithActorAndTestConnectorAndParameter(\n            final ProcessDefinitionBuilder processDefinitionBuilder,\n            final String actorName, final User user, final Map<String, String> parameters, final String name,\n            final String jarName)\n            throws IOException, BonitaException {\n        final List<BarResource> connectorImplementations = Collections\n                .singletonList(BuildTestUtil.getContentAndBuildBarResource(name, TestConnector.class));\n        final List<BarResource> generateConnectorDependencies = Arrays.asList(\n                BuildTestUtil.generateJarAndBuildBarResource(TestConnector.class, jarName),\n                BuildTestUtil.generateJarAndBuildBarResource(VariableStorage.class, \"VariableStorage.jar\"));\n        return deployAndEnableProcessWithActorAndConnectorAndParameter(processDefinitionBuilder, actorName, user,\n                connectorImplementations,\n                generateConnectorDependencies, parameters);\n    }\n\n    public ProcessDefinition deployProcessWithActorAndTestConnectorWithOutput(\n            final ProcessDefinitionBuilder processDefinitionBuilder,\n            final String actor, final User user) throws BonitaException, IOException {\n        return deployProcessWithActorAndTestConnectorWithOutputAndParameter(processDefinitionBuilder, actor, user,\n                null);\n    }\n\n    public ProcessDefinition deployProcessWithActorAndTestConnectorWithOutputAndParameter(\n            final ProcessDefinitionBuilder processDefinitionBuilder,\n            final String actorName, final User user, final Map<String, String> parameters)\n            throws BonitaException, IOException {\n        return deployAndEnableProcessWithActorAndConnectorAndParameter(processDefinitionBuilder, actorName, user,\n                parameters, \"TestConnectorWithOutput.impl\",\n                TestConnectorWithOutput.class, \"TestConnectorWithOutput.jar\");\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/connectors/DoNothingConnector.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connectors;\n\nimport org.bonitasoft.engine.connector.AbstractConnector;\nimport org.bonitasoft.engine.connector.ConnectorException;\nimport org.bonitasoft.engine.connector.ConnectorValidationException;\n\npublic class DoNothingConnector extends AbstractConnector {\n\n    @Override\n    protected void executeBusinessLogic() throws ConnectorException {\n\n    }\n\n    @Override\n    public void validateInputParameters() throws ConnectorValidationException {\n\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/connectors/FailingConnector.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connectors;\n\nimport org.bonitasoft.engine.connector.AbstractConnector;\nimport org.bonitasoft.engine.connector.ConnectorException;\nimport org.bonitasoft.engine.connector.ConnectorValidationException;\n\npublic class FailingConnector extends AbstractConnector {\n\n    @Override\n    protected void executeBusinessLogic() throws ConnectorException {\n        throw new ConnectorException(\"\");\n    }\n\n    @Override\n    public void validateInputParameters() throws ConnectorValidationException {\n\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/connectors/TestConnector.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connectors;\n\nimport org.bonitasoft.engine.connector.AbstractConnector;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class TestConnector extends AbstractConnector {\n\n    public static final String INPUT1 = \"input1\";\n\n    @Override\n    public void validateInputParameters() {\n\n    }\n\n    @Override\n    protected void executeBusinessLogic() {\n        final String input1Value = (String) getInputParameter(INPUT1);\n        VariableStorage.getInstance().setVariable(INPUT1, input1Value);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/connectors/TestConnector2.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connectors;\n\nimport org.bonitasoft.engine.connector.AbstractConnector;\n\n/**\n * @author Yanyan Liu\n */\npublic class TestConnector2 extends AbstractConnector {\n\n    @Override\n    public void validateInputParameters() {\n    }\n\n    @Override\n    protected void executeBusinessLogic() {\n        final String input1Value = (String) getInputParameter(\"input1\");\n        VariableStorage.getInstance().setVariable(\"input1\", input1Value + 2);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/connectors/TestConnector3.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connectors;\n\nimport org.bonitasoft.engine.connector.AbstractConnector;\n\n/**\n * @author Sebastien Chevassu\n * @author Celine Souchet\n */\npublic class TestConnector3 extends AbstractConnector {\n\n    public static final String INPUT1 = \"input1\";\n\n    public static final String INPUT2 = \"input2\";\n\n    public static final String INPUT3 = \"input3\";\n\n    public static final String INPUT4 = \"input4\";\n\n    @Override\n    public void validateInputParameters() {\n\n    }\n\n    @Override\n    protected void executeBusinessLogic() {\n\n        final String input1Value = (String) getInputParameter(INPUT1);\n        if (input1Value != null) {\n            VariableStorage.getInstance().setVariable(INPUT1, input1Value);\n        }\n\n        final String input2Value = (String) getInputParameter(INPUT2);\n        if (input2Value != null) {\n            VariableStorage.getInstance().setVariable(INPUT2, input2Value);\n        }\n\n        final String input3Value = (String) getInputParameter(INPUT3);\n        if (input3Value != null) {\n            VariableStorage.getInstance().setVariable(INPUT3, input3Value);\n        }\n\n        final String input4Value = (String) getInputParameter(INPUT4);\n        if (input4Value != null) {\n            VariableStorage.getInstance().setVariable(INPUT4, input4Value);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/connectors/TestConnectorLongToExecute.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connectors;\n\nimport org.bonitasoft.engine.connector.AbstractConnector;\nimport org.bonitasoft.engine.connector.ConnectorException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class TestConnectorLongToExecute extends AbstractConnector {\n\n    @Override\n    public void validateInputParameters() {\n    }\n\n    @Override\n    protected void executeBusinessLogic() throws ConnectorException {\n        final Long timeToWait = (Long) getInputParameter(\"timeout\");\n        try {\n            Thread.sleep(timeToWait);\n        } catch (final InterruptedException e) {\n            throw new ConnectorException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/connectors/TestConnectorThatThrowException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connectors;\n\nimport org.bonitasoft.engine.connector.AbstractConnector;\nimport org.bonitasoft.engine.connector.ConnectorException;\nimport org.bonitasoft.engine.connector.ConnectorValidationException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class TestConnectorThatThrowException extends AbstractConnector {\n\n    public static final String BUSINESS_LOGIC_EXCEPTION_MESSAGE = \"unexpected ══════════════════════════ during connector execution\";\n\n    public static final String DISCONNECT = \"disconnect\";\n\n    public static final String CONNECT = \"connect\";\n\n    public static final String NONE = \"none\";\n\n    public static final String RUNTIME = \"runtime\";\n\n    public static final String KIND = \"kind\";\n\n    public static final String NORMAL = \"normal\";\n\n    @Override\n    public void validateInputParameters() throws ConnectorValidationException {\n        final Object inputParameter = getInputParameter(KIND);\n        if (!NORMAL.equals(inputParameter) && !RUNTIME.equals(inputParameter) && !NONE.equals(inputParameter)\n                && !CONNECT.equals(inputParameter)) {\n            throw new ConnectorValidationException(\"bad kind of exception\");\n        }\n    }\n\n    @Override\n    protected void executeBusinessLogic() throws ConnectorException {\n        final String kind = (String) getInputParameter(KIND);\n        if (kind.equals(NORMAL)) {\n            throw new ConnectorException(BUSINESS_LOGIC_EXCEPTION_MESSAGE);\n        } else if (kind.equals(RUNTIME)) {\n            throw new RuntimeException(\"unexpected\");\n        }\n    }\n\n    @Override\n    public void connect() {\n        final String kind = (String) getInputParameter(KIND);\n        if (kind.equals(CONNECT)) {\n            throw new RuntimeException(\"unexpected error in connect\");\n        }\n    }\n\n    @Override\n    public void disconnect() {\n        final String kind = (String) getInputParameter(KIND);\n        if (kind.equals(DISCONNECT)) {\n            throw new RuntimeException(\"unexpected error in connect\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/connectors/TestConnectorWithAPICall.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connectors;\n\nimport org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.connector.AbstractConnector;\n\n/**\n * @author Charles Souillard\n */\npublic class TestConnectorWithAPICall extends AbstractConnector {\n\n    @Override\n    public void validateInputParameters() {\n    }\n\n    @Override\n    protected void executeBusinessLogic() {\n        final String name = (String) getInputParameter(\"processName\");\n        final String version = (String) getInputParameter(\"processVersion\");\n        long processId = -1;\n        try {\n            processId = getAPIAccessor().getProcessAPI().getProcessDefinitionId(name, version);\n        } catch (ProcessDefinitionNotFoundException e) {\n            throw new RuntimeException(\"Unable to get Process with name and version: \" + name + \", \" + version);\n        } finally {\n            setOutputParameter(\"processId\", processId);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/connectors/TestConnectorWithConnectedResource.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connectors;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.connector.AbstractConnector;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class TestConnectorWithConnectedResource extends AbstractConnector {\n\n    private final List<String> output;\n\n    public TestConnectorWithConnectedResource() {\n        output = new ArrayList<String>(2);\n    }\n\n    @Override\n    public void validateInputParameters() {\n\n    }\n\n    @Override\n    protected void executeBusinessLogic() {\n        setOutputParameter(\"output\", output);\n    }\n\n    @Override\n    public void connect() {\n        output.add(\"connect\");\n    }\n\n    @Override\n    public void disconnect() {\n        output.add(\"disconect\");\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/connectors/TestConnectorWithModifiedOutput.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connectors;\n\nimport org.bonitasoft.engine.connector.AbstractConnector;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class TestConnectorWithModifiedOutput extends AbstractConnector {\n\n    @Override\n    public void validateInputParameters() {\n\n    }\n\n    @Override\n    protected void executeBusinessLogic() {\n        final String input1Value = (String) getInputParameter(\"input1\");\n        setOutputParameter(\"output1\", input1Value + \"->modified\");\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/connectors/TestConnectorWithNotSerializableOutput.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connectors;\n\nimport org.bonitasoft.engine.connector.AbstractConnector;\n\n/**\n * @author Celine Souchet\n */\npublic class TestConnectorWithNotSerializableOutput extends AbstractConnector {\n\n    @Override\n    public void validateInputParameters() {\n\n    }\n\n    @Override\n    protected void executeBusinessLogic() {\n        setOutputParameter(\"output1\", new Object());\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/connectors/TestConnectorWithOutput.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connectors;\n\nimport org.bonitasoft.engine.connector.AbstractConnector;\n\n/**\n * @author Baptiste Mesta\n */\npublic class TestConnectorWithOutput extends AbstractConnector {\n\n    @Override\n    public void validateInputParameters() {\n\n    }\n\n    @Override\n    protected void executeBusinessLogic() {\n        Object input1Value = getInputParameter(\"input1\");\n        setOutputParameter(\"output1\", input1Value);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/connectors/TestExternalConnector.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connectors;\n\nimport org.bonitasoft.engine.connector.AbstractConnector;\n\n/**\n * @author Nicolas Chabanoles\n */\npublic class TestExternalConnector extends AbstractConnector {\n\n    private boolean hasBeenValidated = false;\n\n    @Override\n    public void validateInputParameters() {\n        hasBeenValidated = true;\n    }\n\n    @Override\n    protected void executeBusinessLogic() {\n        final String input1Value = (String) getInputParameter(\"param1\");\n        setOutputParameter(\"param1\", input1Value);\n        setOutputParameter(\"hasBeenValidated\", hasBeenValidated);\n        final Object returnNotSerializableOutput = getInputParameter(\"returnNotSerializableOutput\");\n        if (returnNotSerializableOutput != null) {\n            setOutputParameter(\"notSerializable\", new Object());\n        }\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/connectors/VariableStorage.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connectors;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic final class VariableStorage implements Serializable {\n\n    private static final long serialVersionUID = -4195221111626812999L;\n\n    private final Map<String, Object> variables;\n\n    private VariableStorage() {\n        variables = new HashMap<String, Object>();\n    }\n\n    private static class VariableStorageHolder {\n\n        public static final VariableStorage INSTANCE = new VariableStorage();\n\n        public static final Map<Long, VariableStorage> instances = new HashMap<Long, VariableStorage>();\n    }\n\n    public static VariableStorage getInstance() {\n        return VariableStorageHolder.INSTANCE;\n    }\n\n    public static VariableStorage getInstance(final long tenantId) {\n        VariableStorage variableStorage = VariableStorageHolder.instances.get(tenantId);\n        if (variableStorage == null) {\n            variableStorage = new VariableStorage();\n            VariableStorageHolder.instances.put(tenantId, variableStorage);\n        }\n        return variableStorage;\n    }\n\n    public synchronized void setVariable(final String name, final Object value) {\n        variables.put(name, value);\n    }\n\n    public Object getVariableValue(final String name) {\n        return variables.get(name);\n    }\n\n    public Map<String, Object> getVariables() {\n        return variables;\n    }\n\n    public static void clearAll() {\n        VariableStorageHolder.INSTANCE.clear();\n        for (final Entry<Long, VariableStorage> entry : VariableStorageHolder.instances.entrySet()) {\n            entry.getValue().clear();\n        }\n        VariableStorageHolder.instances.clear();\n    }\n\n    public void clear() {\n        variables.clear();\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/event/AbstractEventIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.event;\n\nimport static org.junit.Assert.assertEquals;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.actor.ActorCriterion;\nimport org.bonitasoft.engine.bpm.actor.ActorInstance;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\nimport org.bonitasoft.engine.bpm.flownode.GatewayType;\nimport org.bonitasoft.engine.bpm.flownode.TimerType;\nimport org.bonitasoft.engine.bpm.process.ActivationState;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.CallActivityBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.CatchMessageEventTriggerDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.IntermediateThrowEventDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.StartEventDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.SubProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.ThrowMessageEventTriggerBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder;\nimport org.bonitasoft.engine.connectors.TestConnectorThatThrowException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.expression.InvalidExpressionException;\nimport org.bonitasoft.engine.operation.Operation;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.test.BuildTestUtil;\n\n/**\n * @author Celine Souchet\n * @version 6.4.0\n * @since 6.4.0\n */\npublic abstract class AbstractEventIT extends TestWithUser {\n\n    public static final String EXCEPTION_STEP = \"exceptionStep\";\n\n    public static final String DATE_FORMAT_WITH_MS = \"yyyyy-mm-dd hh:mm:ss SSSSS\";\n\n    public static final String BOUNDARY_NAME = \"waitMessage\";\n\n    public static final String INT_DATA_NAME = \"count\";\n\n    public static final String SHORT_DATA_NAME = \"short_data_name\";\n\n    public static final String SUB_PROCESS_START_NAME = \"subProcessStart\";\n\n    public static final String SUB_PROCESS_USER_TASK_NAME = \"subStep\";\n\n    public static final String SUB_PROCESS_NAME = \"eventSubProcess\";\n\n    public static final String PARENT_PROCESS_USER_TASK_NAME = \"step1\";\n\n    public static final String PARENT_END = \"end\";\n\n    public static final String THROW_MESSAGE_TASK_NAME = \"messageTask\";\n\n    public static final String MESSAGE_NAME = \"canStart\";\n\n    public static final String START_WITH_MESSAGE_STEP1_NAME = \"userStart1\";\n\n    public static final String START_WITH_MESSAGE_PROCESS_NAME = \"Start from message\";\n\n    public static final String CATCH_EVENT_NAME = \"waitForMessage\";\n\n    public static final String CATCH_MESSAGE_PROCESS_NAME = \"Catch a message\";\n\n    public static final String CATCH_MESSAGE_STEP1_NAME = \"step1\";\n\n    public static final String SEND_MESSAGE_PROCESS_NAME = \"Send a message\";\n\n    public static final String AFTER_SIGNAL = \"after_signal\";\n\n    public static final String SIGNAL_NAME = \"canStart\";\n\n    public static final String START_EVENT_NAME = \"startEvent\";\n\n    /**\n     * Deploy and enable a process with a human task having a timer boundary event followed by another human task\n     * without boundary\n     *\n     * @param timerValue\n     *        after how long time the boundary will be triggered\n     * @param interrupting\n     *        define whether the boundary is interrupting or not\n     * @param taskWithBoundaryName\n     *        the name of user task containing the boundary event\n     * @param exceptionTaskName\n     *        the name of human task reached by exception flow\n     * @param normalFlowTaskName\n     *        name of human task following the task containing the boundary (normal flow)\n     * @return\n     * @throws Exception\n     * @throws InvalidProcessDefinitionException\n     */\n    public ProcessDefinition deployAndEnableProcessWithBoundaryTimerEvent(final long timerValue,\n            final boolean interrupting, final String taskWithBoundaryName,\n            final String exceptionTaskName, final String normalFlowTaskName) throws Exception {\n        return deployAndEnableProcessWithBoundaryTimerEvent(TimerType.DURATION, timerValue, interrupting,\n                taskWithBoundaryName, exceptionTaskName,\n                normalFlowTaskName);\n    }\n\n    /**\n     * Deploy and enable a process with a human task having a timer boundary event followed by another human task\n     * without boundary\n     *\n     * @param timerType\n     *        the timer time\n     * @param timerValue\n     *        the timer value\n     * @param interrupting\n     *        define whether the boundary is interrupting or not\n     * @param taskWithBoundaryName\n     *        the name of user task containing the boundary event\n     * @param exceptionTaskName\n     *        the name of human task reached by exception flow\n     * @param normalFlowTaskName\n     *        name of human task following the task containing the boundary (normal flow)\n     * @return\n     * @throws InvalidProcessDefinitionException\n     * @since 6.0\n     */\n    public ProcessDefinition deployAndEnableProcessWithBoundaryTimerEvent(final TimerType timerType,\n            final long timerValue, final boolean interrupting,\n            final String taskWithBoundaryName, final String exceptionTaskName, final String normalFlowTaskName)\n            throws Exception {\n        final Expression timerExpr = new ExpressionBuilder().createConstantLongExpression(timerValue);\n        return deployAndEnableProcessWithBoundaryTimerEvent(timerType, timerExpr, interrupting, taskWithBoundaryName,\n                exceptionTaskName, normalFlowTaskName);\n    }\n\n    /**\n     * Deploy and enable a process with a human task having a timer boundary event followed by another human task\n     * without boundary\n     *\n     * @param timerType\n     *        the timer time\n     * @param timerExpr\n     *        the timer value\n     * @param interrupting\n     *        define whether the boundary is interrupting or not\n     * @param taskWithBoundaryName\n     *        the name of user task containing the boundary event\n     * @param exceptionTaskName\n     *        the name of human task reached by exception flow\n     * @param normalFlowTaskName\n     *        name of human task following the task containing the boundary (normal flow)\n     * @return\n     * @throws InvalidProcessDefinitionException\n     * @since 6.0\n     */\n    public ProcessDefinition deployAndEnableProcessWithBoundaryTimerEvent(final TimerType timerType,\n            final Expression timerExpr, final boolean interrupting,\n            final String taskWithBoundaryName, final String exceptionTaskName, final String normalFlowTaskName)\n            throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"pTimerBoundary\", \"2.0\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addStartEvent(\"start\");\n        final UserTaskDefinitionBuilder userTaskDefinitionBuilder = processDefinitionBuilder\n                .addUserTask(taskWithBoundaryName, ACTOR_NAME);\n        userTaskDefinitionBuilder.addBoundaryEvent(\"timer\", interrupting).addTimerEventTriggerDefinition(timerType,\n                timerExpr);\n        processDefinitionBuilder.addUserTask(exceptionTaskName, ACTOR_NAME);\n        processDefinitionBuilder.addUserTask(normalFlowTaskName, ACTOR_NAME);\n        processDefinitionBuilder.addEndEvent(\"end\");\n        processDefinitionBuilder.addTransition(\"start\", taskWithBoundaryName);\n        processDefinitionBuilder.addTransition(taskWithBoundaryName, normalFlowTaskName);\n        processDefinitionBuilder.addTransition(\"timer\", exceptionTaskName);\n\n        return deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, user);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithBoundaryTimerEventOnHumanTask(final long timerValue,\n            final boolean withData) throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"pTimerBoundary\", \"2.0\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addStartEvent(\"start\");\n        final UserTaskDefinitionBuilder userTaskDefinitionBuilder = processDefinitionBuilder.addUserTask(\"step1\",\n                ACTOR_NAME);\n\n        final Expression timerExpr;\n        if (withData) {\n            processDefinitionBuilder.addData(\"timer\", Long.class.getName(),\n                    new ExpressionBuilder().createConstantLongExpression(timerValue));\n            timerExpr = new ExpressionBuilder().createDataExpression(\"timer\", Long.class.getName());\n        } else {\n            timerExpr = new ExpressionBuilder().createConstantLongExpression(timerValue);\n        }\n        userTaskDefinitionBuilder.addBoundaryEvent(\"Boundary timer\").addTimerEventTriggerDefinition(TimerType.DURATION,\n                timerExpr);\n        userTaskDefinitionBuilder.addUserTask(EXCEPTION_STEP, ACTOR_NAME);\n        userTaskDefinitionBuilder.addUserTask(\"step2\", ACTOR_NAME);\n        processDefinitionBuilder.addEndEvent(\"end\");\n        processDefinitionBuilder.addTransition(\"start\", \"step1\");\n        processDefinitionBuilder.addTransition(\"step1\", \"step2\");\n        processDefinitionBuilder.addTransition(\"Boundary timer\", EXCEPTION_STEP);\n        processDefinitionBuilder.addTransition(EXCEPTION_STEP, \"end\");\n\n        return deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, user);\n    }\n\n    /**\n     * Deploy and enable a process containing two timer boundary events attached to a call activity\n     *\n     * @param timerDuration\n     *        after how long time the boundary will be triggered\n     * @param interrupting\n     *        define whether the boundary is interrupting or not\n     * @param targetProcessName\n     *        the name of called process\n     * @return\n     * @throws InvalidExpressionException\n     * @throws InvalidProcessDefinitionException\n     * @since 6.0\n     */\n    public ProcessDefinition deployAndEnableProcessWithBoundaryTimerEventOnCallActivity(final long timerDuration,\n            final boolean interrupting,\n            final String targetProcessName) throws Exception {\n        final Expression timerExpr = new ExpressionBuilder().createConstantLongExpression(timerDuration);\n        final Expression timerExpr2 = new ExpressionBuilder().createConstantLongExpression(timerDuration + 1000L);\n        final Expression targetProcessNameExpr = new ExpressionBuilder()\n                .createConstantStringExpression(targetProcessName);\n        final Expression targetProcessVersionExpr = new ExpressionBuilder().createConstantStringExpression(\"1.0\");\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance(\n                \"processWithCallActivityAndBoundaryEvent\",\n                \"2.0\");\n        processDefinitionBuilder.addActor(ACTOR_NAME).addStartEvent(\"start\");\n        final CallActivityBuilder callActivityBuilder = processDefinitionBuilder.addCallActivity(\"callActivity\",\n                targetProcessNameExpr,\n                targetProcessVersionExpr);\n        callActivityBuilder.addBoundaryEvent(\"timer\", interrupting).addTimerEventTriggerDefinition(TimerType.DURATION,\n                timerExpr);\n\n        callActivityBuilder.addBoundaryEvent(\"timer2\", interrupting).addTimerEventTriggerDefinition(TimerType.DURATION,\n                timerExpr2);\n\n        processDefinitionBuilder.addUserTask(EXCEPTION_STEP, ACTOR_NAME)\n                .addUserTask(PARENT_PROCESS_USER_TASK_NAME, ACTOR_NAME).addEndEvent(\"end\")\n                .addTransition(\"start\", \"callActivity\").addTransition(\"callActivity\", PARENT_PROCESS_USER_TASK_NAME)\n                .addTransition(PARENT_PROCESS_USER_TASK_NAME, \"end\").addTransition(\"timer\", EXCEPTION_STEP)\n                .addTransition(\"timer2\", EXCEPTION_STEP);\n\n        return deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, user);\n    }\n\n    /**\n     * Deploy and enable a process with a timer boundary event attached to a multi-instance\n     *\n     * @param timerValue\n     *        after how long time the boundary will be triggered\n     * @param interrupting\n     *        define whether the boundary is interrupting or not\n     * @param multiTaskName\n     *        the multi-instance name\n     * @param loopCardinality\n     *        the multi-instance cardinality\n     * @param isSequential\n     *        define whether the multi-instance is sequential or parallel\n     * @param normalFlowTaskName\n     *        the name of user task following the multi-instance in the normal flow\n     * @param exceptionFlowTaskName\n     *        the name of the user task reached by the exception flow\n     * @return\n     * @throws InvalidProcessDefinitionException\n     * @since 6.0\n     */\n    public ProcessDefinition deployAndEnableProcessMultiInstanceWithBoundaryEvent(final long timerValue,\n            final boolean interrupting,\n            final String multiTaskName, final int loopCardinality, final boolean isSequential,\n            final String normalFlowTaskName,\n            final String exceptionFlowTaskName) throws Exception {\n        final Expression timerExpr = new ExpressionBuilder().createConstantLongExpression(timerValue);\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithMultiInstanceAndBoundaryEvent\", \"1.0\");\n        processBuilder.addActor(ACTOR_NAME).addStartEvent(\"start\");\n\n        final UserTaskDefinitionBuilder userTaskBuilder = processBuilder.addUserTask(multiTaskName, ACTOR_NAME);\n        userTaskBuilder.addMultiInstance(isSequential,\n                new ExpressionBuilder().createConstantIntegerExpression(loopCardinality));\n        userTaskBuilder.addBoundaryEvent(\"timer\", interrupting).addTimerEventTriggerDefinition(TimerType.DURATION,\n                timerExpr);\n\n        processBuilder.addUserTask(normalFlowTaskName, ACTOR_NAME).addUserTask(exceptionFlowTaskName, ACTOR_NAME)\n                .addEndEvent(\"end\");\n        processBuilder.addTransition(\"start\", multiTaskName);\n        processBuilder.addTransition(multiTaskName, normalFlowTaskName);\n        processBuilder.addTransition(normalFlowTaskName, \"end\");\n        processBuilder.addTransition(\"timer\", exceptionFlowTaskName);\n\n        return deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user);\n    }\n\n    /**\n     * Deploy and enable a process with a timer boundary event attached to a loop activity\n     *\n     * @param timerValue\n     *        after how long time the boundary will be triggered\n     * @param interrupting\n     *        define whether the boundary is interrupting or not\n     * @param loopMax\n     *        how many loops will be executed\n     * @param loopActivityName\n     *        the name of the loop activity\n     * @param normalFlowStepName\n     *        the name of the user task following the loop activity in the normal flow\n     * @param exceptionFlowStepName\n     *        the name of the user task reached by the exception flow\n     * @return\n     * @throws InvalidProcessDefinitionException\n     * @since 6.0\n     */\n    public ProcessDefinition deployAndEnableProcessWithBoundaryTimerEventOnLoopActivity(final long timerValue,\n            final boolean interrupting, final int loopMax,\n            final String loopActivityName, final String normalFlowStepName, final String exceptionFlowStepName)\n            throws Exception {\n        final Expression timerExpr = new ExpressionBuilder().createConstantLongExpression(timerValue);\n        final Expression condition = new ExpressionBuilder().createConstantBooleanExpression(true);\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithMultiInstanceAndBoundaryEvent\", \"1.0\");\n        processBuilder.addActor(ACTOR_NAME).addStartEvent(\"start\");\n\n        final UserTaskDefinitionBuilder userTaskBuilder = processBuilder.addUserTask(loopActivityName, ACTOR_NAME);\n        userTaskBuilder.addLoop(false, condition, new ExpressionBuilder().createConstantIntegerExpression(loopMax));\n        userTaskBuilder.addBoundaryEvent(\"timer\", interrupting).addTimerEventTriggerDefinition(TimerType.DURATION,\n                timerExpr);\n\n        processBuilder.addUserTask(normalFlowStepName, ACTOR_NAME).addUserTask(exceptionFlowStepName, ACTOR_NAME)\n                .addEndEvent(\"end\")\n                .addTransition(\"start\", loopActivityName).addTransition(loopActivityName, normalFlowStepName)\n                .addTransition(normalFlowStepName, \"end\")\n                .addTransition(\"timer\", exceptionFlowStepName);\n\n        return deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user);\n    }\n\n    protected void waitForUserTasksAndExecuteIt(final String taskName, final ProcessInstance processInstance,\n            final int nbOfRemainingInstances)\n            throws Exception {\n        for (int i = 0; i < nbOfRemainingInstances; i++) {\n            waitForUserTaskAndExecuteIt(processInstance, taskName, user);\n        }\n    }\n\n    protected void executeRemainingParallelMultiInstances(final String taskName, final ProcessInstance processInstance,\n            final int nbOfRemainingInstances)\n            throws Exception {\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, Math.max(10, nbOfRemainingInstances));\n        builder.filter(ActivityInstanceSearchDescriptor.NAME, taskName);\n        builder.filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, processInstance.getId());\n        builder.filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.USER_TASK);\n        final SearchResult<ActivityInstance> searchResult = getProcessAPI().searchActivities(builder.done());\n        assertEquals(nbOfRemainingInstances, searchResult.getCount());\n        for (final ActivityInstance activity : searchResult.getResult()) {\n            assignAndExecuteStep(activity, user.getId());\n        }\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithInterruptingAndNonInterruptingTimer(final long interruptTimer,\n            final long nonInterruptingTimer,\n            final String taskWithBoundaryName, final String interruptExceptionTaskName,\n            final String nonInterruptExceptionTaskName,\n            final String interruptTimerName,\n            final String nonInterruptTimerName) throws Exception {\n        final String normalFlowTaskName = \"normalFlow\";\n        final Expression interruptTimerExpr = new ExpressionBuilder().createConstantLongExpression(interruptTimer);\n        final Expression nonInterruptTimerExpr = new ExpressionBuilder()\n                .createConstantLongExpression(nonInterruptingTimer);\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"pTimerBoundary\", \"2.0\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addStartEvent(\"start\");\n        final UserTaskDefinitionBuilder userTaskDefinitionBuilder = processDefinitionBuilder\n                .addUserTask(taskWithBoundaryName, ACTOR_NAME);\n        userTaskDefinitionBuilder.addBoundaryEvent(interruptTimerName, true)\n                .addTimerEventTriggerDefinition(TimerType.DURATION, interruptTimerExpr);\n        userTaskDefinitionBuilder.addBoundaryEvent(nonInterruptTimerName, false)\n                .addTimerEventTriggerDefinition(TimerType.DURATION, nonInterruptTimerExpr);\n        processDefinitionBuilder.addUserTask(interruptExceptionTaskName, ACTOR_NAME);\n        processDefinitionBuilder.addUserTask(nonInterruptExceptionTaskName, ACTOR_NAME);\n        processDefinitionBuilder.addUserTask(normalFlowTaskName, ACTOR_NAME);\n        processDefinitionBuilder.addEndEvent(\"end\");\n        processDefinitionBuilder.addTransition(\"start\", taskWithBoundaryName);\n        processDefinitionBuilder.addTransition(taskWithBoundaryName, normalFlowTaskName);\n        processDefinitionBuilder.addTransition(interruptTimerName, interruptExceptionTaskName);\n        processDefinitionBuilder.addTransition(nonInterruptTimerName, nonInterruptExceptionTaskName);\n\n        return deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, user);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithEndThrowErrorEvent(final String processName,\n            final String errorCode)\n            throws Exception {\n        final ProcessDefinitionBuilder calledProcess = new ProcessDefinitionBuilder().createNewInstance(processName,\n                \"1.0\");\n        calledProcess.addActor(ACTOR_NAME);\n        calledProcess.addStartEvent(\"start\");\n        calledProcess.addUserTask(\"calledStep1\", ACTOR_NAME);\n        calledProcess.addUserTask(\"calledStep2\", ACTOR_NAME);\n        calledProcess.addEndEvent(\"endError\").addErrorEventTrigger(errorCode);\n        calledProcess.addEndEvent(\"end\").addTerminateEventTrigger();\n        calledProcess.addTransition(\"start\", \"calledStep1\");\n        calledProcess.addTransition(\"start\", \"calledStep2\");\n        calledProcess.addTransition(\"calledStep1\", \"endError\");\n        calledProcess.addTransition(\"calledStep2\", \"end\");\n        return deployAndEnableProcessWithActor(calledProcess.done(), ACTOR_NAME, user);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithBoundaryErrorEventOnCallActivity(final String processName,\n            final String targetProcessName,\n            final String callActivityName, final String errorCode, final String ACTOR_NAME) throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(processName, \"1.0\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addStartEvent(\"start\");\n        final CallActivityBuilder callActivityBuilder = processDefinitionBuilder.addCallActivity(callActivityName,\n                new ExpressionBuilder().createConstantStringExpression(targetProcessName),\n                new ExpressionBuilder().createConstantStringExpression(\"1.0\"));\n        callActivityBuilder.addBoundaryEvent(\"error\", true).addErrorEventTrigger(errorCode);\n        processDefinitionBuilder.addUserTask(EXCEPTION_STEP, ACTOR_NAME);\n        processDefinitionBuilder.addUserTask(\"step2\", ACTOR_NAME);\n        processDefinitionBuilder.addEndEvent(\"end\");\n        processDefinitionBuilder.addTransition(\"start\", callActivityName);\n        processDefinitionBuilder.addTransition(callActivityName, \"step2\");\n        processDefinitionBuilder.addTransition(\"error\", EXCEPTION_STEP);\n        processDefinitionBuilder.addTransition(EXCEPTION_STEP, \"end\");\n        processDefinitionBuilder.addTransition(\"step2\", \"end\");\n        return deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, user);\n    }\n\n    public ProcessDefinition deployAndEnableSubProcessWhichThrowsAnErrorEvent(final String processName,\n            final String errorCode)\n            throws Exception {\n        final ProcessDefinitionBuilder process = new ProcessDefinitionBuilder().createNewInstance(processName, \"1.0\");\n        process.addStartEvent(\"start\");\n        process.addEndEvent(\"end\").addErrorEventTrigger(errorCode);\n        process.addTransition(\"start\", \"end\");\n        return deployAndEnableProcess(process.done());\n    }\n\n    public ProcessDefinition deployAndEnableMidProcessWhichContainsACallActivity(final String processName,\n            final String targetProcessName)\n            throws Exception {\n        final ProcessDefinitionBuilder process = new ProcessDefinitionBuilder().createNewInstance(processName, \"1.0\");\n        process.addStartEvent(\"start\");\n        process.addCallActivity(\"ca\",\n                new ExpressionBuilder().createConstantStringExpression(targetProcessName),\n                new ExpressionBuilder().createConstantStringExpression(\"1.0\"));\n        process.addEndEvent(\"end\");\n        process.addTransition(\"start\", \"ca\");\n        process.addTransition(\"ca\", \"end\");\n        return deployAndEnableProcess(process.done());\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithBoundaryErrorEventOnMICallActivity(final String processName,\n            final String targetProcessName,\n            final String errorCode, final String ACTOR_NAME) throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(processName, \"1.0\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addStartEvent(\"start\");\n        final CallActivityBuilder callActivityBuilder = processDefinitionBuilder.addCallActivity(\"step1\",\n                new ExpressionBuilder().createConstantStringExpression(targetProcessName),\n                new ExpressionBuilder().createConstantStringExpression(\"1.0\"));\n        callActivityBuilder.addBoundaryEvent(\"error\", true).addErrorEventTrigger(errorCode);\n        callActivityBuilder.addMultiInstance(false, new ExpressionBuilder().createConstantIntegerExpression(1));\n        processDefinitionBuilder.addUserTask(\"step2\", ACTOR_NAME);\n        processDefinitionBuilder.addUserTask(EXCEPTION_STEP, ACTOR_NAME);\n        processDefinitionBuilder.addTransition(\"start\", \"step1\");\n        processDefinitionBuilder.addTransition(\"step1\", \"step2\");\n        processDefinitionBuilder.addTransition(\"error\", EXCEPTION_STEP);\n        return deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, user);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithTimerEventSubProcess(final long timerDuration)\n            throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"ProcessWithEventSubProcess\", \"1.0\");\n        builder.addActor(ACTOR_NAME);\n        builder.addStartEvent(\"start\");\n        builder.addUserTask(\"step1\", ACTOR_NAME);\n        builder.addEndEvent(\"end\");\n        builder.addTransition(\"start\", \"step1\");\n        builder.addTransition(\"step1\", \"end\");\n        final SubProcessDefinitionBuilder subProcessBuilder = builder.addSubProcess(\"eventSubProcess\", true)\n                .getSubProcessBuilder();\n        subProcessBuilder.addStartEvent(\"timerStart\").addTimerEventTriggerDefinition(TimerType.DURATION,\n                new ExpressionBuilder().createConstantLongExpression(timerDuration));\n        subProcessBuilder.addUserTask(\"subStep\", ACTOR_NAME);\n        subProcessBuilder.addEndEvent(\"endSubProcess\");\n        subProcessBuilder.addTransition(\"timerStart\", \"subStep\");\n        subProcessBuilder.addTransition(\"subStep\", \"endSubProcess\");\n        return deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithTimerEventSubProcessAndData(final long timerDuration)\n            throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"ProcessWithEventSubProcess\", \"1.0\");\n        builder.addShortTextData(\"content\", new ExpressionBuilder().createConstantStringExpression(\"parentVar\"));\n        builder.addIntegerData(\"count\", new ExpressionBuilder().createConstantIntegerExpression(1));\n        builder.addActor(ACTOR_NAME);\n        builder.addStartEvent(\"start\");\n        builder.addUserTask(\"step1\", ACTOR_NAME);\n        builder.addEndEvent(\"end\");\n        builder.addTransition(\"start\", \"step1\");\n        builder.addTransition(\"step1\", \"end\");\n        final SubProcessDefinitionBuilder subProcessBuilder = builder.addSubProcess(\"eventSubProcess\", true)\n                .getSubProcessBuilder();\n        subProcessBuilder.addShortTextData(\"content\",\n                new ExpressionBuilder().createConstantStringExpression(\"childVar\"));\n        subProcessBuilder.addDoubleData(\"value\", new ExpressionBuilder().createConstantDoubleExpression(10.0));\n        subProcessBuilder.addStartEvent(\"timerStart\").addTimerEventTriggerDefinition(TimerType.DURATION,\n                new ExpressionBuilder().createConstantLongExpression(timerDuration));\n        subProcessBuilder.addUserTask(\"subStep\", ACTOR_NAME).addShortTextData(\"content\",\n                new ExpressionBuilder().createConstantStringExpression(\"childActivityVar\"));\n        subProcessBuilder.addEndEvent(\"endSubProcess\");\n        subProcessBuilder.addTransition(\"timerStart\", \"subStep\");\n        subProcessBuilder.addTransition(\"subStep\", \"endSubProcess\");\n        final DesignProcessDefinition processDefinition = builder.done();\n        return deployAndEnableProcessWithActor(processDefinition, ACTOR_NAME, user);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithErrorEventSubProcessAndData(final String catchErrorCode,\n            final String throwErroCode,\n            final String subProcStartEventName) throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"ProcessWithEventSubProcess\", \"1.0\");\n        builder.addShortTextData(\"content\", new ExpressionBuilder().createConstantStringExpression(\"parentVar\"));\n        builder.addIntegerData(\"count\", new ExpressionBuilder().createConstantIntegerExpression(1));\n        builder.addActor(ACTOR_NAME);\n        builder.addStartEvent(\"start\");\n        builder.addUserTask(\"step1\", ACTOR_NAME);\n        builder.addUserTask(\"step2\", ACTOR_NAME);\n        builder.addEndEvent(\"end\");\n        builder.addEndEvent(\"endError\").addErrorEventTrigger(throwErroCode);\n        builder.addTransition(\"start\", \"step1\");\n        builder.addTransition(\"start\", \"step2\");\n        builder.addTransition(\"step1\", \"end\");\n        builder.addTransition(\"step2\", \"endError\");\n        final SubProcessDefinitionBuilder subProcessBuilder = builder\n                .addSubProcess(BuildTestUtil.EVENT_SUB_PROCESS_NAME, true).getSubProcessBuilder();\n        subProcessBuilder.addShortTextData(\"content\",\n                new ExpressionBuilder().createConstantStringExpression(\"childVar\"));\n        subProcessBuilder.addDoubleData(\"value\", new ExpressionBuilder().createConstantDoubleExpression(10.0));\n        subProcessBuilder.addStartEvent(subProcStartEventName).addErrorEventTrigger(catchErrorCode);\n        subProcessBuilder.addUserTask(\"subStep\", ACTOR_NAME).addShortTextData(\"content\",\n                new ExpressionBuilder().createConstantStringExpression(\"childActivityVar\"));\n        subProcessBuilder.addEndEvent(\"endSubProcess\");\n        subProcessBuilder.addTransition(subProcStartEventName, \"subStep\");\n        subProcessBuilder.addTransition(\"subStep\", \"endSubProcess\");\n        return deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithErrorEventSubProcessAndDataOnlyInRoot(final String errorCode,\n            final String subProcStartEventName,\n            final String rootUserTaskName, final String subProcUserTaskName, final String dataName,\n            final String dataValue) throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"ProcessWithEventSubProcess\", \"1.0\");\n        builder.addShortTextData(dataName, new ExpressionBuilder().createConstantStringExpression(dataValue));\n        builder.addActor(ACTOR_NAME);\n        builder.addStartEvent(\"start\");\n        builder.addUserTask(rootUserTaskName, ACTOR_NAME);\n        builder.addEndEvent(\"end\");\n        builder.addEndEvent(\"endError\").addErrorEventTrigger(errorCode);\n        builder.addTransition(\"start\", rootUserTaskName);\n        builder.addTransition(rootUserTaskName, \"endError\");\n        final SubProcessDefinitionBuilder subProcessBuilder = builder\n                .addSubProcess(BuildTestUtil.EVENT_SUB_PROCESS_NAME, true).getSubProcessBuilder();\n        subProcessBuilder.addStartEvent(subProcStartEventName).addErrorEventTrigger(errorCode);\n        subProcessBuilder.addUserTask(subProcUserTaskName, ACTOR_NAME);\n        subProcessBuilder.addEndEvent(\"endSubProcess\");\n        subProcessBuilder.addTransition(subProcStartEventName, subProcUserTaskName);\n        subProcessBuilder.addTransition(subProcUserTaskName, \"endSubProcess\");\n        return deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithErrorEventSubProcessAndDataOnlyInSubProc(final String errorCode,\n            final String subProcStartEventName,\n            final String rootUserTaskName, final String subProcUserTaskName, final String dataName,\n            final String dataValue) throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"ProcessWithEventSubProcess\", \"1.0\");\n        builder.addActor(ACTOR_NAME);\n        builder.addStartEvent(\"start\");\n        builder.addUserTask(rootUserTaskName, ACTOR_NAME);\n        builder.addEndEvent(\"end\");\n        builder.addEndEvent(\"endError\").addErrorEventTrigger(errorCode);\n        builder.addTransition(\"start\", rootUserTaskName);\n        builder.addTransition(rootUserTaskName, \"endError\");\n        final SubProcessDefinitionBuilder subProcessBuilder = builder\n                .addSubProcess(BuildTestUtil.EVENT_SUB_PROCESS_NAME, true).getSubProcessBuilder();\n        subProcessBuilder.addShortTextData(dataName, new ExpressionBuilder().createConstantStringExpression(dataValue));\n        subProcessBuilder.addStartEvent(subProcStartEventName).addErrorEventTrigger(errorCode);\n        subProcessBuilder.addUserTask(subProcUserTaskName, ACTOR_NAME);\n        subProcessBuilder.addEndEvent(\"endSubProcess\");\n        subProcessBuilder.addTransition(subProcStartEventName, subProcUserTaskName);\n        subProcessBuilder.addTransition(subProcUserTaskName, \"endSubProcess\");\n        return deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithErrorEventSubProcess(final String catchErrorCode,\n            final String throwErrorCode,\n            final String subProcStartEventName) throws Exception {\n        final Expression transitionCondition = new ExpressionBuilder().createDataExpression(\"throwException\",\n                Boolean.class.getName());\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"ProcessWithEventSubProcess\", \"1.0\");\n        builder.addActor(ACTOR_NAME);\n        builder.addBooleanData(\"throwException\", new ExpressionBuilder().createConstantBooleanExpression(true));\n        builder.addStartEvent(\"start\");\n        builder.addUserTask(\"step1\", ACTOR_NAME);\n        builder.addUserTask(\"step2\", ACTOR_NAME);\n        builder.addEndEvent(\"end\");\n        builder.addEndEvent(\"endError\").addErrorEventTrigger(throwErrorCode);\n        builder.addTransition(\"start\", \"step1\");\n        builder.addTransition(\"start\", \"step2\");\n        builder.addTransition(\"step1\", \"end\");\n        builder.addTransition(\"step2\", \"endError\", transitionCondition);\n        builder.addDefaultTransition(\"step2\", \"end\");\n        final SubProcessDefinitionBuilder subProcessBuilder = builder\n                .addSubProcess(BuildTestUtil.EVENT_SUB_PROCESS_NAME, true).getSubProcessBuilder();\n        if (catchErrorCode == null) {\n            subProcessBuilder.addStartEvent(subProcStartEventName).addErrorEventTrigger();\n        } else {\n            subProcessBuilder.addStartEvent(subProcStartEventName).addErrorEventTrigger(catchErrorCode);\n        }\n        subProcessBuilder.addUserTask(\"subStep\", ACTOR_NAME);\n        subProcessBuilder.addEndEvent(\"endSubProcess\");\n        subProcessBuilder.addTransition(subProcStartEventName, \"subStep\");\n        subProcessBuilder.addTransition(\"subStep\", \"endSubProcess\");\n        return deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithTestConnectorThatThrowException(\n            final ProcessDefinitionBuilder processDefinitionBuilder)\n            throws BonitaException, IOException {\n        return deployAndEnableProcessWithActorAndConnector(processDefinitionBuilder, ACTOR_NAME, user,\n                \"TestConnectorThatThrowException.impl\",\n                TestConnectorThatThrowException.class, \"TestConnectorThatThrowException.jar\");\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithBoundaryMessageEvent(final String message)\n            throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"pMessageBoundary\", \"2.0\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addStartEvent(\"start\");\n        final UserTaskDefinitionBuilder userTaskDefinitionBuilder = processDefinitionBuilder.addUserTask(\"step1\",\n                ACTOR_NAME);\n        userTaskDefinitionBuilder.addBoundaryEvent(BOUNDARY_NAME, true).addMessageEventTrigger(message);\n        final String otherBoundaryNotTriggered = \"otherBoundaryNotTriggered\";\n        userTaskDefinitionBuilder.addBoundaryEvent(otherBoundaryNotTriggered, true).addSignalEventTrigger(\"bat signal\");\n        userTaskDefinitionBuilder.addUserTask(EXCEPTION_STEP, ACTOR_NAME);\n        userTaskDefinitionBuilder.addUserTask(\"step2\", ACTOR_NAME);\n        processDefinitionBuilder.addEndEvent(\"end\");\n        processDefinitionBuilder.addTransition(\"start\", \"step1\");\n        processDefinitionBuilder.addTransition(\"step1\", \"step2\");\n        processDefinitionBuilder.addTransition(BOUNDARY_NAME, EXCEPTION_STEP);\n        processDefinitionBuilder.addTransition(otherBoundaryNotTriggered, \"end\");\n\n        return deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, user);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithBoundarySignalEvent(final String signalName)\n            throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"pSignalBoundary\", \"2.0\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addStartEvent(\"start\");\n        final UserTaskDefinitionBuilder userTaskDefinitionBuilder = processDefinitionBuilder.addUserTask(\"step1\",\n                ACTOR_NAME);\n        userTaskDefinitionBuilder.addBoundaryEvent(\"waitSignal\", true).addSignalEventTrigger(signalName);\n        userTaskDefinitionBuilder.addUserTask(EXCEPTION_STEP, ACTOR_NAME);\n        userTaskDefinitionBuilder.addUserTask(\"step2\", ACTOR_NAME);\n        processDefinitionBuilder.addEndEvent(\"end\");\n        processDefinitionBuilder.addTransition(\"start\", \"step1\");\n        processDefinitionBuilder.addTransition(\"step1\", \"step2\");\n        processDefinitionBuilder.addTransition(\"waitSignal\", EXCEPTION_STEP);\n        return deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, user);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithBoundaryMessageEventOnLoopActivity(final int loopMax)\n            throws Exception {\n        final Expression condition = new ExpressionBuilder().createConstantBooleanExpression(true);\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithLoopActivityAndBoundaryEvent\", \"1.0\");\n        processBuilder.addActor(ACTOR_NAME).addStartEvent(\"start\");\n\n        final UserTaskDefinitionBuilder userTaskBuilder = processBuilder.addUserTask(\"step1\", ACTOR_NAME);\n        userTaskBuilder.addLoop(false, condition, new ExpressionBuilder().createConstantIntegerExpression(loopMax));\n        userTaskBuilder.addBoundaryEvent(BOUNDARY_NAME, true).addMessageEventTrigger(\"MyMessage\");\n\n        processBuilder.addUserTask(\"step2\", ACTOR_NAME).addUserTask(EXCEPTION_STEP, ACTOR_NAME).addEndEvent(\"end\")\n                .addTransition(\"start\", \"step1\")\n                .addTransition(\"step1\", \"step2\").addTransition(\"step2\", \"end\")\n                .addTransition(BOUNDARY_NAME, EXCEPTION_STEP);\n\n        return deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user);\n    }\n\n    /**\n     * Deploy and enable a simple process: start event -> user task -> end event\n     *\n     * @param processName\n     *        the process name\n     * @param userTaskName\n     *        the user task name\n     * @return\n     * @throws InvalidProcessDefinitionException\n     * @since 6.0\n     */\n    public ProcessDefinition deployAndEnableSimpleProcess(final String processName, final String userTaskName)\n            throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(processName,\n                \"1.0\");\n        processBuilder.addActor(ACTOR_NAME).addStartEvent(\"startCA\").addUserTask(userTaskName, ACTOR_NAME)\n                .addEndEvent(\"endCA\")\n                .addTransition(\"startCA\", userTaskName).addTransition(userTaskName, \"endCA\");\n        return deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithBoundaryMessageEventOnCallActivity() throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"pMessageBoundary\", \"2.0\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addStartEvent(\"start\");\n        final CallActivityBuilder callActivityBuilder = processDefinitionBuilder.addCallActivity(\"step1\",\n                new ExpressionBuilder().createConstantStringExpression(\"calledProcess\"),\n                new ExpressionBuilder().createConstantStringExpression(\"1.0\"));\n        callActivityBuilder.addBoundaryEvent(BOUNDARY_NAME, true).addMessageEventTrigger(\"MyMessage\");\n        processDefinitionBuilder.addUserTask(EXCEPTION_STEP, ACTOR_NAME);\n        processDefinitionBuilder.addUserTask(\"step2\", ACTOR_NAME);\n        processDefinitionBuilder.addEndEvent(\"end\");\n        processDefinitionBuilder.addTransition(\"start\", \"step1\");\n        processDefinitionBuilder.addTransition(\"step1\", \"step2\");\n        processDefinitionBuilder.addTransition(BOUNDARY_NAME, EXCEPTION_STEP);\n        return deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, user);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithBoundaryMessageEventOnMultiInstance(final int loopCardinality,\n            final boolean isSequential)\n            throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(\n                \"processWithBoundaryMessageEventAndMultiInstance\",\n                \"1.0\");\n        processBuilder.addActor(ACTOR_NAME).addStartEvent(\"start\");\n\n        final UserTaskDefinitionBuilder userTaskBuilder = processBuilder.addUserTask(\"step1\", ACTOR_NAME);\n        userTaskBuilder.addMultiInstance(isSequential,\n                new ExpressionBuilder().createConstantIntegerExpression(loopCardinality));\n        userTaskBuilder.addBoundaryEvent(BOUNDARY_NAME, true).addMessageEventTrigger(\"MyMessage\");\n\n        processBuilder.addUserTask(\"step2\", ACTOR_NAME).addUserTask(EXCEPTION_STEP, ACTOR_NAME).addEndEvent(\"end\")\n                .addTransition(\"start\", \"step1\")\n                .addTransition(\"step1\", \"step2\").addTransition(\"step2\", \"end\")\n                .addTransition(BOUNDARY_NAME, EXCEPTION_STEP);\n\n        return deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithCallActivity(final String targetProcessName,\n            final String targetVersion) throws Exception {\n        final Expression targetProcessExpr = new ExpressionBuilder().createConstantStringExpression(targetProcessName);\n        final Expression targetVersionExpr = new ExpressionBuilder().createConstantStringExpression(targetVersion);\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"ProcessWithCallActivity\", \"1.0\");\n        builder.addActor(ACTOR_NAME);\n        builder.addStartEvent(\"start\");\n        builder.addCallActivity(\"callActivity\", targetProcessExpr, targetVersionExpr);\n        builder.addUserTask(\"step2\", ACTOR_NAME);\n        builder.addEndEvent(PARENT_END);\n        builder.addTransition(\"start\", \"callActivity\");\n        builder.addTransition(\"callActivity\", \"step2\");\n        builder.addTransition(\"step2\", PARENT_END);\n        return deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithMessageEventSubProcess() throws Exception {\n        final ProcessDefinitionBuilder builder = buildParentProcessDefinition(false, false);\n        buildSubProcessDefinition(builder, false, null, false);\n        return deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithMessageEventSubProcessAndData(\n            final List<Entry<Expression, Expression>> correlations)\n            throws Exception {\n        final ProcessDefinitionBuilder builder = buildParentProcessDefinition(false, true);\n        buildSubProcessDefinition(builder, true, correlations, false);\n        return deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user);\n    }\n\n    public ProcessDefinitionBuilder buildParentProcessDefinition(final boolean withIntermediateThrowEvent,\n            final boolean withData)\n            throws InvalidExpressionException {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"ProcessWithEventSubProcess\", \"1.0\");\n        builder.addActor(ACTOR_NAME);\n        builder.addStartEvent(\"start\");\n        builder.addUserTask(PARENT_PROCESS_USER_TASK_NAME, ACTOR_NAME);\n        builder.addEndEvent(PARENT_END);\n        builder.addTransition(\"start\", PARENT_PROCESS_USER_TASK_NAME);\n\n        if (withIntermediateThrowEvent) {\n            builder.addIntermediateThrowEvent(SIGNAL_NAME).addSignalEventTrigger(SIGNAL_NAME);\n            builder.addUserTask(AFTER_SIGNAL, ACTOR_NAME);\n            builder.addTransition(PARENT_PROCESS_USER_TASK_NAME, SIGNAL_NAME);\n            builder.addTransition(SIGNAL_NAME, AFTER_SIGNAL);\n            builder.addTransition(SIGNAL_NAME, PARENT_END);\n        } else {\n            builder.addTransition(PARENT_PROCESS_USER_TASK_NAME, PARENT_END);\n        }\n\n        if (withData) {\n            builder.addShortTextData(SHORT_DATA_NAME,\n                    new ExpressionBuilder().createConstantStringExpression(\"parentVar\"));\n            builder.addIntegerData(INT_DATA_NAME, new ExpressionBuilder().createConstantIntegerExpression(1));\n        }\n        return builder;\n    }\n\n    public void buildSubProcessDefinition(final ProcessDefinitionBuilder builder, final boolean withData,\n            final List<Entry<Expression, Expression>> correlations, final boolean isSignal)\n            throws InvalidExpressionException {\n        final SubProcessDefinitionBuilder subProcessBuilder = builder.addSubProcess(SUB_PROCESS_NAME, true)\n                .getSubProcessBuilder();\n        final StartEventDefinitionBuilder startEventDefinitionBuilder = subProcessBuilder\n                .addStartEvent(SUB_PROCESS_START_NAME);\n        if (isSignal) {\n            startEventDefinitionBuilder.addSignalEventTrigger(SIGNAL_NAME);\n        } else {\n            final CatchMessageEventTriggerDefinitionBuilder messageEventTrigger = startEventDefinitionBuilder\n                    .addMessageEventTrigger(MESSAGE_NAME);\n            if (withData) {\n                addCorrelations(messageEventTrigger, correlations);\n            }\n        }\n\n        final UserTaskDefinitionBuilder userTask = subProcessBuilder.addUserTask(SUB_PROCESS_USER_TASK_NAME,\n                ACTOR_NAME);\n        subProcessBuilder.addEndEvent(\"endSubProcess\");\n        subProcessBuilder.addTransition(SUB_PROCESS_START_NAME, SUB_PROCESS_USER_TASK_NAME);\n        subProcessBuilder.addTransition(SUB_PROCESS_USER_TASK_NAME, \"endSubProcess\");\n\n        if (withData) {\n            subProcessBuilder.addShortTextData(SHORT_DATA_NAME,\n                    new ExpressionBuilder().createConstantStringExpression(\"childVar\"));\n            subProcessBuilder.addDoubleData(\"value\", new ExpressionBuilder().createConstantDoubleExpression(10.0));\n            userTask.addShortTextData(SHORT_DATA_NAME,\n                    new ExpressionBuilder().createConstantStringExpression(\"childActivityVar\"));\n        }\n    }\n\n    public void addCorrelations(final CatchMessageEventTriggerDefinitionBuilder messageEventTrigger,\n            final List<Entry<Expression, Expression>> correlations) {\n        if (correlations != null) {\n            for (final Entry<Expression, Expression> correlation : correlations) {\n                messageEventTrigger.addCorrelation(correlation.getKey(), correlation.getValue());\n            }\n        }\n    }\n\n    public void addCorrelations(final ThrowMessageEventTriggerBuilder throwMessageEventTriggerBuilder,\n            final List<Entry<Expression, Expression>> correlations) {\n        if (correlations != null) {\n            for (final Entry<Expression, Expression> entry : correlations) {\n                throwMessageEventTriggerBuilder.addCorrelation(entry.getKey(), entry.getValue());\n            }\n        }\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithEndMessageEvent(final String targetProcess,\n            final String targetFlowNode) throws Exception {\n        return deployAndEnableProcessWithEndMessageEvent(targetProcess, targetFlowNode, null, null, null, null);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithEndMessageEvent(final String targetProcess,\n            final String targetFlowNode,\n            final List<Entry<Expression, Expression>> correlations, final Map<String, String> processData,\n            final Map<String, String> messageData,\n            final Map<String, String> dataInputMapping) throws Exception {\n        return deployAndEnableProcessWithEndMessageEvent(\"Send message in the end\", MESSAGE_NAME, targetProcess,\n                targetFlowNode, correlations, processData,\n                messageData, dataInputMapping);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithEndMessageEvent(final String processName,\n            final String messageName, final String targetProcess,\n            final String targetFlowNode, final List<Entry<Expression, Expression>> correlations,\n            final Map<String, String> processData,\n            final Map<String, String> messageData, final Map<String, String> dataInputMapping) throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder();\n        processBuilder.createNewInstance(processName, PROCESS_VERSION);\n        addProcessData(processData, processBuilder);\n        processBuilder.addStartEvent(START_EVENT_NAME);\n        processBuilder.addAutomaticTask(\"auto1\");\n        // create expression for target process/flowNode\n        final Expression targetProcessExpression = new ExpressionBuilder()\n                .createConstantStringExpression(targetProcess);\n        final ThrowMessageEventTriggerBuilder throwMessageEventTriggerBuilder;\n        if (targetFlowNode != null) {\n            final Expression targetFlowNodeExpression = new ExpressionBuilder()\n                    .createConstantStringExpression(targetFlowNode);\n            throwMessageEventTriggerBuilder = processBuilder.addEndEvent(\"endEvent\").addMessageEventTrigger(messageName,\n                    targetProcessExpression,\n                    targetFlowNodeExpression);\n        } else {\n            throwMessageEventTriggerBuilder = processBuilder.addEndEvent(\"endEvent\").addMessageEventTrigger(messageName,\n                    targetProcessExpression);\n        }\n\n        addCorrelations(throwMessageEventTriggerBuilder, correlations);\n        addMessageData(messageData, dataInputMapping, throwMessageEventTriggerBuilder);\n        processBuilder.addTransition(START_EVENT_NAME, \"auto1\");\n        processBuilder.addTransition(\"auto1\", \"endEvent\");\n        final DesignProcessDefinition designProcessDefinition = processBuilder.done();\n\n        final ProcessDefinition sendMessageProcess = deployAndEnableProcess(designProcessDefinition);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(sendMessageProcess.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        return sendMessageProcess;\n    }\n\n    public void addMessageData(final Map<String, String> messageData, final Map<String, String> dataInputMapping,\n            final ThrowMessageEventTriggerBuilder throwMessageEventTriggerBuilder) throws InvalidExpressionException {\n        if (messageData != null) {\n            for (final Entry<String, String> entry : messageData.entrySet()) {\n                final Expression displayName = new ExpressionBuilder().createConstantStringExpression(entry.getKey());\n\n                Expression defaultValue = null;\n                if (dataInputMapping.containsKey(entry.getKey())) {\n                    defaultValue = new ExpressionBuilder().createDataExpression(dataInputMapping.get(entry.getKey()),\n                            entry.getValue());\n                }\n                throwMessageEventTriggerBuilder.addMessageContentExpression(displayName, defaultValue);\n            }\n        }\n\n    }\n\n    public void addProcessData(final Map<String, String> data, final ProcessDefinitionBuilder processBuilder) {\n        if (data != null) {\n            for (final Entry<String, String> entry : data.entrySet()) {\n                processBuilder.addData(entry.getKey(), entry.getValue(), null);\n            }\n        }\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithIntermediateThrowMessageEvent(final String targetProcess,\n            final String targetFlowNode)\n            throws Exception {\n        return deployAndEnableProcessWithIntermediateThrowMessageEvent(Collections.singletonList(MESSAGE_NAME),\n                Collections.singletonList(targetProcess),\n                Collections.singletonList(targetFlowNode));\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithIntermediateThrowMessageEvent(final List<String> messageNames,\n            final List<String> targetProcesses,\n            final List<String> targetFlowNodes) throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder();\n        processBuilder.createNewInstance(SEND_MESSAGE_PROCESS_NAME, PROCESS_VERSION);\n        processBuilder.addStartEvent(START_EVENT_NAME);\n        processBuilder.addAutomaticTask(\"auto1\");\n\n        final IntermediateThrowEventDefinitionBuilder intermediateThrowEvent = processBuilder\n                .addIntermediateThrowEvent(\"sendMessage\");\n        for (final String targetProcess : targetProcesses) {\n            // create expression for target process/flowNode\n            final Expression targetProcessExpression = new ExpressionBuilder()\n                    .createConstantStringExpression(targetProcess);\n            final int indexOfTargetProcess = targetProcesses.indexOf(targetProcess);\n            final Expression targetFlowNodeExpression = new ExpressionBuilder()\n                    .createConstantStringExpression(targetFlowNodes.get(indexOfTargetProcess));\n            intermediateThrowEvent.addMessageEventTrigger(messageNames.get(indexOfTargetProcess),\n                    targetProcessExpression, targetFlowNodeExpression);\n        }\n        processBuilder.addEndEvent(\"endEvent\");\n        processBuilder.addTransition(START_EVENT_NAME, \"auto1\");\n        processBuilder.addTransition(\"auto1\", \"sendMessage\");\n        processBuilder.addTransition(\"sendMessage\", \"endEvent\");\n        final DesignProcessDefinition designProcessDefinition = processBuilder.done();\n\n        final ProcessDefinition sendMessageProcess = deployAndEnableProcess(designProcessDefinition);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(sendMessageProcess.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        return sendMessageProcess;\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithStartMessageEvent(final Map<String, String> data,\n            final List<Operation> catchMessageOperations)\n            throws Exception {\n        return deployAndEnableProcessWithStartMessageEvent(START_WITH_MESSAGE_PROCESS_NAME, MESSAGE_NAME, data,\n                catchMessageOperations);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithStartMessageEvent(final String processName,\n            final String messageName, final Map<String, String> data,\n            final List<Operation> catchMessageOperations) throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder();\n        processBuilder.createNewInstance(processName, PROCESS_VERSION);\n        addProcessData(data, processBuilder);\n        final CatchMessageEventTriggerDefinitionBuilder messageEventTrigger = processBuilder\n                .addStartEvent(START_EVENT_NAME)\n                .addMessageEventTrigger(messageName);\n        addOperations(messageEventTrigger, catchMessageOperations);\n        processBuilder.addActor(ACTOR_NAME);\n        processBuilder.addUserTask(START_WITH_MESSAGE_STEP1_NAME, ACTOR_NAME);\n        processBuilder.addEndEvent(\"endEvent\");\n        processBuilder.addTransition(START_EVENT_NAME, START_WITH_MESSAGE_STEP1_NAME);\n        processBuilder.addTransition(START_WITH_MESSAGE_STEP1_NAME, \"endEvent\");\n        final DesignProcessDefinition designProcessDefinition = processBuilder.done();\n\n        final BusinessArchiveBuilder archiveBuilder = new BusinessArchiveBuilder();\n        archiveBuilder.createNewBusinessArchive().setProcessDefinition(designProcessDefinition);\n        final BusinessArchive receiveMessageArchive = archiveBuilder.done();\n        final ProcessDefinition receiveMessageProcess = deployProcess(receiveMessageArchive);\n\n        final List<ActorInstance> actors = getProcessAPI().getActors(receiveMessageProcess.getId(), 0, 1,\n                ActorCriterion.NAME_ASC);\n        getProcessAPI().addUserToActor(actors.get(0).getId(), user.getId());\n\n        getProcessAPI().enableProcess(receiveMessageProcess.getId());\n\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(receiveMessageProcess.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        return receiveMessageProcess;\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithStartMessageEvent(final String processName,\n            final String messageName) throws Exception {\n        return deployAndEnableProcessWithStartMessageEvent(processName, messageName,\n                Collections.<String, String> emptyMap(),\n                Collections.<Operation> emptyList());\n    }\n\n    public void addOperations(final CatchMessageEventTriggerDefinitionBuilder messageEventTrigger,\n            final List<Operation> catchMessageOperations) {\n        if (catchMessageOperations != null) {\n            for (final Operation operation : catchMessageOperations) {\n                messageEventTrigger.addOperation(operation);\n            }\n        }\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithIntermediateCatchMessageEvent(\n            final List<Entry<Expression, Expression>> correlations,\n            final Map<String, String> processData, final List<Operation> operations) throws Exception {\n        return deployAndEnableProcessWithIntermediateCatchMessageEvent(CATCH_MESSAGE_PROCESS_NAME, MESSAGE_NAME,\n                correlations, processData, operations);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithIntermediateCatchMessageEvent(final String processName,\n            final String messageName,\n            final List<Entry<Expression, Expression>> correlations, final Map<String, String> processData,\n            final List<Operation> operations)\n            throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder();\n        processBuilder.createNewInstance(processName, PROCESS_VERSION);\n        addProcessData(processData, processBuilder);\n        processBuilder.addStartEvent(START_EVENT_NAME);\n        processBuilder.addAutomaticTask(\"auto1\");\n        final CatchMessageEventTriggerDefinitionBuilder catchMessageEventTriggerDefinitionBuilder = processBuilder\n                .addIntermediateCatchEvent(CATCH_EVENT_NAME)\n                .addMessageEventTrigger(messageName);\n        addCorrelations(catchMessageEventTriggerDefinitionBuilder, correlations);\n        addOperations(catchMessageEventTriggerDefinitionBuilder, operations);\n        processBuilder.addActor(ACTOR_NAME);\n        processBuilder.addUserTask(CATCH_MESSAGE_STEP1_NAME, ACTOR_NAME);\n        processBuilder.addEndEvent(\"endEvent\");\n        processBuilder.addTransition(START_EVENT_NAME, \"auto1\");\n        processBuilder.addTransition(\"auto1\", CATCH_EVENT_NAME);\n        processBuilder.addTransition(CATCH_EVENT_NAME, CATCH_MESSAGE_STEP1_NAME);\n        processBuilder.addTransition(CATCH_MESSAGE_STEP1_NAME, \"endEvent\");\n        final DesignProcessDefinition designProcessDefinition = processBuilder.done();\n\n        final BusinessArchiveBuilder archiveBuilder = new BusinessArchiveBuilder();\n        archiveBuilder.createNewBusinessArchive().setProcessDefinition(designProcessDefinition);\n        final BusinessArchive receiveMessaceArchive = archiveBuilder.done();\n\n        final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithActor(receiveMessaceArchive,\n                ACTOR_NAME, user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(receiveMessageProcess.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        return receiveMessageProcess;\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithIntraMessageEvent(final String targetProcess,\n            final String targetFlowNode) throws Exception {\n        return deployAndEnableProcessWithIntraMessageEvent(\"sendAndReceiveMessageProcess\", MESSAGE_NAME, targetProcess,\n                targetFlowNode);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithIntraMessageEvent(final String processName,\n            final String messageName, final String targetProcess,\n            final String targetFlowNode) throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder();\n        // create expression for target process/flowNode\n        final Expression targetProcessExpression = new ExpressionBuilder()\n                .createConstantStringExpression(targetProcess);\n        final Expression targetFlowNodeExpression = new ExpressionBuilder()\n                .createConstantStringExpression(targetFlowNode);\n        processBuilder.createNewInstance(processName, PROCESS_VERSION);\n        processBuilder.addStartEvent(START_EVENT_NAME);\n        processBuilder.addAutomaticTask(\"auto1\");\n        processBuilder.addGateway(\"gateway1\", GatewayType.PARALLEL);\n        processBuilder.addIntermediateThrowEvent(\"sendMessage\").addMessageEventTrigger(messageName,\n                targetProcessExpression, targetFlowNodeExpression);\n        processBuilder.addIntermediateCatchEvent(targetFlowNode).addMessageEventTrigger(messageName);\n        processBuilder.addGateway(\"gateway2\", GatewayType.PARALLEL);\n        processBuilder.addActor(ACTOR_NAME);\n        processBuilder.addUserTask(\"userTask1\", ACTOR_NAME).addUserTask(\"userTask2\", ACTOR_NAME)\n                .addUserTask(\"userTask3\", ACTOR_NAME);\n        processBuilder.addEndEvent(\"endEvent\");\n        processBuilder.addTransition(START_EVENT_NAME, \"auto1\");\n        processBuilder.addTransition(\"auto1\", \"gateway1\");\n        processBuilder.addTransition(\"gateway1\", \"userTask2\");\n        processBuilder.addTransition(\"userTask2\", \"sendMessage\");\n        processBuilder.addTransition(\"sendMessage\", \"gateway2\");\n        processBuilder.addTransition(\"gateway1\", \"userTask1\");\n        processBuilder.addTransition(\"userTask1\", \"gateway2\");\n        processBuilder.addTransition(\"gateway2\", \"endEvent\");\n        processBuilder.addTransition(\"auto1\", targetFlowNode);\n        processBuilder.addTransition(targetFlowNode, \"userTask3\");\n        processBuilder.addTransition(\"userTask3\", \"endEvent\");\n        final DesignProcessDefinition designProcessDefinition = processBuilder.done();\n\n        final BusinessArchiveBuilder archiveBuilder = new BusinessArchiveBuilder();\n        archiveBuilder.createNewBusinessArchive().setProcessDefinition(designProcessDefinition);\n        final BusinessArchive sendAndReceiveMessaceArchive = archiveBuilder.done();\n        final ProcessDefinition receiveMessageProcess = getProcessAPI().deploy(sendAndReceiveMessaceArchive);\n\n        final List<ActorInstance> actors = getProcessAPI().getActors(receiveMessageProcess.getId(), 0, 1,\n                ActorCriterion.NAME_ASC);\n        getProcessAPI().addUserToActor(actors.get(0).getId(), user.getId());\n\n        getProcessAPI().enableProcess(receiveMessageProcess.getId());\n\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(receiveMessageProcess.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        return receiveMessageProcess;\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithIntermediateCatchMessageEventAnd1Correlation()\n            throws Exception {\n        final Map<String, String> data = new HashMap<>();\n        data.put(\"docRef\", Integer.class.getName());\n        final ArrayList<Entry<Expression, Expression>> correlations = new ArrayList<Entry<Expression, Expression>>(1);\n        final Expression docCorrelationKey = new ExpressionBuilder().createConstantStringExpression(\"docKey\");\n        final Expression docCorrelationValue = new ExpressionBuilder().createDataExpression(\"docRef\",\n                Integer.class.getName());\n        correlations.add(Map.entry(docCorrelationKey, docCorrelationValue));\n        return deployAndEnableProcessWithIntermediateCatchMessageEvent(correlations, data, null);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithIntermediateCatchMessageEventAnd2Correlations()\n            throws Exception {\n        final Map<String, String> data = new HashMap<>();\n        data.put(\"docRef\", Integer.class.getName());\n        data.put(\"name\", String.class.getName());\n        final Expression docCorrelationKey = new ExpressionBuilder().createConstantStringExpression(\"docKey\");\n        final Expression docCorrelationValue = new ExpressionBuilder().createDataExpression(\"docRef\",\n                Integer.class.getName());\n        final Expression nameCorrelationKey = new ExpressionBuilder().createConstantStringExpression(\"nameKey\");\n        final Expression nameCorrelationValue = new ExpressionBuilder().createDataExpression(\"name\",\n                String.class.getName());\n        final ArrayList<Entry<Expression, Expression>> correlations = new ArrayList<Entry<Expression, Expression>>(2);\n        correlations.add(Map.entry(docCorrelationKey, docCorrelationValue));\n        correlations.add(Map.entry(nameCorrelationKey, nameCorrelationValue));\n        return deployAndEnableProcessWithIntermediateCatchMessageEvent(correlations, data, null);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithEndMessageEventAndCorrelation() throws Exception {\n        final Map<String, String> data = new HashMap<>();\n        data.put(\"docNumber\", Integer.class.getName());\n        data.put(\"lastName\", String.class.getName());\n\n        final ArrayList<Entry<Expression, Expression>> correlations = new ArrayList<Entry<Expression, Expression>>(2);\n        final Expression docCorrelationKey = new ExpressionBuilder().createConstantStringExpression(\"docKey\");\n        final Expression docCorrelationValue = new ExpressionBuilder().createDataExpression(\"docNumber\",\n                Integer.class.getName());\n        final Expression nameCorrelationKey = new ExpressionBuilder().createConstantStringExpression(\"nameKey\");\n        final Expression nameCorrelationValue = new ExpressionBuilder().createDataExpression(\"lastName\",\n                String.class.getName());\n        correlations.add(Map.entry(docCorrelationKey, docCorrelationValue));\n        correlations.add(Map.entry(nameCorrelationKey, nameCorrelationValue));\n\n        return deployAndEnableProcessWithEndMessageEvent(CATCH_MESSAGE_PROCESS_NAME, CATCH_EVENT_NAME, correlations,\n                data, null, null);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithBoundarySignalEventOnLoopActivity(final int loopMax)\n            throws Exception {\n        final Expression condition = new ExpressionBuilder().createConstantBooleanExpression(true);\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithMultiInstanceAndBoundaryEvent\", \"1.0\");\n        processBuilder.addActor(ACTOR_NAME).addStartEvent(\"start\");\n\n        final UserTaskDefinitionBuilder userTaskBuilder = processBuilder.addUserTask(\"step1\", ACTOR_NAME);\n        userTaskBuilder.addLoop(false, condition, new ExpressionBuilder().createConstantIntegerExpression(loopMax));\n        userTaskBuilder.addBoundaryEvent(\"signal\", true).addSignalEventTrigger(\"MySignal\");\n\n        processBuilder.addUserTask(\"step2\", ACTOR_NAME).addUserTask(EXCEPTION_STEP, ACTOR_NAME).addEndEvent(\"end\")\n                .addTransition(\"start\", \"step1\")\n                .addTransition(\"step1\", \"step2\").addTransition(\"step2\", \"end\").addTransition(\"signal\", EXCEPTION_STEP);\n\n        return deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithBoundarySignalEventOnCallActivity(final String signalName)\n            throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"pSignalBoundary\", \"2.0\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addStartEvent(\"start\");\n        final CallActivityBuilder callActivityBuilder = processDefinitionBuilder.addCallActivity(\"step1\",\n                new ExpressionBuilder().createConstantStringExpression(\"calledProcess\"),\n                new ExpressionBuilder().createConstantStringExpression(\"1.0\"));\n        callActivityBuilder.addBoundaryEvent(\"signal\", true).addSignalEventTrigger(signalName);\n        processDefinitionBuilder.addUserTask(EXCEPTION_STEP, ACTOR_NAME);\n        processDefinitionBuilder.addUserTask(\"step2\", ACTOR_NAME);\n        processDefinitionBuilder.addEndEvent(\"end\");\n        processDefinitionBuilder.addTransition(\"start\", \"step1\");\n        processDefinitionBuilder.addTransition(\"step1\", \"step2\");\n        processDefinitionBuilder.addTransition(\"step2\", \"end\");\n        processDefinitionBuilder.addTransition(\"signal\", EXCEPTION_STEP);\n        return deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, user);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithBoundarySignalEventOnMultiInstance(final int loopCardinality,\n            final boolean isSequential)\n            throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(\n                \"processWithBoundarySignalEventAndMultiInstance\",\n                \"1.0\");\n        processBuilder.addActor(ACTOR_NAME).addStartEvent(\"start\");\n\n        final UserTaskDefinitionBuilder userTaskBuilder = processBuilder.addUserTask(\"step1\", ACTOR_NAME);\n        userTaskBuilder.addMultiInstance(isSequential,\n                new ExpressionBuilder().createConstantIntegerExpression(loopCardinality));\n        userTaskBuilder.addBoundaryEvent(\"signal\", true).addSignalEventTrigger(\"MySignal\");\n\n        processBuilder.addUserTask(\"step2\", ACTOR_NAME).addUserTask(EXCEPTION_STEP, ACTOR_NAME).addEndEvent(\"end\")\n                .addTransition(\"start\", \"step1\")\n                .addTransition(\"step1\", \"step2\").addTransition(\"step2\", \"end\").addTransition(\"signal\", EXCEPTION_STEP);\n\n        return deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithSignalEventSubProcess(final boolean withIntermediateThrowEvent,\n            final boolean withData)\n            throws Exception {\n        final ProcessDefinitionBuilder builder = buildParentProcessDefinition(withIntermediateThrowEvent, withData);\n        buildSubProcessDefinition(builder, withData, null, true);\n        final DesignProcessDefinition processDefinition = builder.done();\n        return deployAndEnableProcessWithActor(processDefinition, ACTOR_NAME, user);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithIntermediateCatchTimerEventAndUserTask(final TimerType timerType,\n            final Expression timerValue,\n            final String step1Name, final String step2Name) throws Exception {\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My Process with start event\", \"1.0\")\n                .addActor(ACTOR_NAME).addDescription(\"Delivery all day and night long\").addStartEvent(START_EVENT_NAME)\n                .addUserTask(step1Name, ACTOR_NAME)\n                .addIntermediateCatchEvent(\"intermediateCatchEvent\")\n                .addTimerEventTriggerDefinition(timerType, timerValue).addUserTask(step2Name, ACTOR_NAME)\n                .addEndEvent(\"endEvent\").addTransition(START_EVENT_NAME, step1Name)\n                .addTransition(step1Name, \"intermediateCatchEvent\")\n                .addTransition(\"intermediateCatchEvent\", step2Name).addTransition(step2Name, \"endEvent\").getProcess();\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(definition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n        return definition;\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithStartTimerEventAndUserTask(final TimerType timerType,\n            final Expression timerValue, final String stepName)\n            throws Exception {\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My Process with start event\", \"1.0\")\n                .addActor(ACTOR_NAME).addDescription(\"Delivery all day and night long\").addStartEvent(START_EVENT_NAME)\n                .addTimerEventTriggerDefinition(timerType, timerValue).addUserTask(stepName, ACTOR_NAME)\n                .addEndEvent(\"endEvent\")\n                .addTransition(START_EVENT_NAME, stepName).addTransition(stepName, \"endEvent\").getProcess();\n\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(definition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n        return definition;\n    }\n\n    public ProcessDefinition deployAndEnableProcessSendingMessageUsingVariableAsTarget(final String targetProcessName,\n            final String targetFlowNode,\n            final String messageName) throws Exception {\n        Expression targetProcessExpr = new ExpressionBuilder().createConstantStringExpression(targetProcessName);\n        Expression targetFlowNodeExpr = new ExpressionBuilder().createConstantStringExpression(targetFlowNode);\n        Expression targetProcessVarExpr = new ExpressionBuilder().createDataExpression(\"targetProcess\",\n                String.class.getName());\n        Expression targetFlowNodeVarExpr = new ExpressionBuilder().createDataExpression(\"targetFlowNode\",\n                String.class.getName());\n        ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"sendMsgProcess\", \"5.1\");\n        builder.addShortTextData(\"targetProcess\", targetProcessExpr);\n        builder.addShortTextData(\"targetFlowNode\", targetFlowNodeExpr);\n        builder.addStartEvent(\"start\");\n        builder.addIntermediateThrowEvent(\"sendMsg\").addMessageEventTrigger(messageName, targetProcessVarExpr,\n                targetFlowNodeVarExpr);\n        builder.addEndEvent(\"end\");\n        builder.addTransition(\"start\", \"sendMsg\");\n        builder.addTransition(\"sendMsg\", \"end\");\n\n        return deployAndEnableProcess(builder.done());\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/filter/user/GroupUserFilter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.filter.user;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.filter.AbstractUserFilter;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.identity.UserCriterion;\n\npublic class GroupUserFilter extends AbstractUserFilter {\n\n    @Override\n    public void validateInputParameters() {\n        // Nothing to validate\n    }\n\n    @Override\n    public List<Long> filter(final String actorName) {\n        final Long groupId = (Long) getInputParameter(\"groupId\");\n        final List<User> users = getAPIAccessor().getIdentityAPI().getUsersInGroup(groupId, 0, 10,\n                UserCriterion.USER_NAME_DESC);\n        final List<Long> userIds = new ArrayList<Long>();\n        for (final User user : users) {\n            userIds.add(user.getId());\n        }\n        return userIds;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/filter/user/TestFilter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.filter.user;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.filter.AbstractUserFilter;\n\n/**\n * @author Baptiste Mesta\n */\npublic class TestFilter extends AbstractUserFilter {\n\n    @Override\n    public List<Long> filter(final String actorName) {\n        return Collections.singletonList((Long) getInputParameter(\"userId\"));\n    }\n\n    @Override\n    public void validateInputParameters() {\n\n    }\n\n    @Override\n    public boolean shouldAutoAssignTaskIfSingleResult() {\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/filter/user/TestFilterThatThrowException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.filter.user;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.filter.AbstractUserFilter;\nimport org.bonitasoft.engine.filter.UserFilterException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class TestFilterThatThrowException extends AbstractUserFilter {\n\n    public TestFilterThatThrowException() {\n    }\n\n    @Override\n    public List<Long> filter(final String actorName) throws UserFilterException {\n        if (getInputParameter(\"exception\").equals(\"runtime\")) {\n            throw new RuntimeException(\"unexpected\");\n        }\n        throw new UserFilterException(\"unexpected\");\n    }\n\n    @Override\n    public void validateInputParameters() {\n\n    }\n\n    @Override\n    public boolean shouldAutoAssignTaskIfSingleResult() {\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/filter/user/TestFilterThatThrowNoClassDef.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.filter.user;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.filter.AbstractUserFilter;\nimport org.bonitasoft.engine.filter.UserFilterException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class TestFilterThatThrowNoClassDef extends AbstractUserFilter {\n\n    public TestFilterThatThrowNoClassDef() {\n        throw new NoClassDefFoundError();\n    }\n\n    @Override\n    public List<Long> filter(final String actorName) throws UserFilterException {\n        // no need for real implementation as the constructor throws Exception:\n        return null;\n    }\n\n    @Override\n    public void validateInputParameters() {\n    }\n\n    @Override\n    public boolean shouldAutoAssignTaskIfSingleResult() {\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/filter/user/TestFilterUsingActorName.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.filter.user;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.filter.AbstractUserFilter;\n\n/**\n * @author Baptiste Mesta\n */\npublic class TestFilterUsingActorName extends AbstractUserFilter {\n\n    @Override\n    public List<Long> filter(final String actorName) {\n        @SuppressWarnings(\"unchecked\")\n        final Map<String, Long> map = (Map<String, Long>) getInputParameter(\"userIds\");\n        return Arrays.asList(map.get(actorName));\n    }\n\n    @Override\n    public void validateInputParameters() {\n\n    }\n\n    @Override\n    public boolean shouldAutoAssignTaskIfSingleResult() {\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/filter/user/TestFilterWithAutoAssign.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.filter.user;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.filter.AbstractUserFilter;\n\n/**\n * @author Baptiste Mesta\n */\npublic class TestFilterWithAutoAssign extends AbstractUserFilter {\n\n    @Override\n    public List<Long> filter(final String actorName) {\n        return Collections.singletonList((Long) getInputParameter(\"userId\"));\n    }\n\n    @Override\n    public void validateInputParameters() {\n\n    }\n\n    @Override\n    public boolean shouldAutoAssignTaskIfSingleResult() {\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/page/PageAssert.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport static java.lang.String.format;\n\nimport java.util.Date;\n\nimport org.assertj.core.api.AbstractAssert;\n\n/**\n * {@link Page} specific assertions - Generated by CustomAssertionGenerator.\n */\npublic class PageAssert extends AbstractAssert<PageAssert, Page> {\n\n    /**\n     * Creates a new </code>{@link PageAssert}</code> to make assertions on actual Page.\n     *\n     * @param actual the Page we want to make assertions on.\n     */\n    public PageAssert(Page actual) {\n        super(actual, PageAssert.class);\n    }\n\n    /**\n     * An entry point for PageAssert to follow AssertJ standard <code>assertThat()</code> statements.<br>\n     * With a static import, one's can write directly : <code>assertThat(myPage)</code> and get specific assertion with\n     * code completion.\n     *\n     * @param actual the Page we want to make assertions on.\n     * @return a new </code>{@link PageAssert}</code>\n     */\n    public static PageAssert assertThat(Page actual) {\n        return new PageAssert(actual);\n    }\n\n    /**\n     * Verifies that the actual Page's contentName is equal to the given one.\n     *\n     * @param contentName the given contentName to compare the actual Page's contentName to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual Page's contentName is not equal to the given one.\n     */\n    public PageAssert hasContentName(String contentName) {\n        // check that actual Page we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> contentName to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                contentName, actual.getContentName());\n\n        // check\n        if (!actual.getContentName().equals(contentName)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual Page's contentType is equal to the given one.\n     *\n     * @param contentType the given contentType to compare the actual Page's contentType to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual Page's contentType is not equal to the given one.\n     */\n    public PageAssert hasContentType(String contentType) {\n        // check that actual Page we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> contentType to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                contentType, actual.getContentType());\n\n        // check\n        if (!actual.getContentType().equals(contentType)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual Page's description is equal to the given one.\n     *\n     * @param description the given description to compare the actual Page's description to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual Page's description is not equal to the given one.\n     */\n    public PageAssert hasDescription(String description) {\n        // check that actual Page we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> description to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                description, actual.getDescription());\n\n        // check\n        if (!actual.getDescription().equals(description)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual Page's displayName is equal to the given one.\n     *\n     * @param displayName the given displayName to compare the actual Page's displayName to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual Page's displayName is not equal to the given one.\n     */\n    public PageAssert hasDisplayName(String displayName) {\n        // check that actual Page we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> displayName to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                displayName, actual.getDisplayName());\n\n        // check\n        if (!actual.getDisplayName().equals(displayName)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual Page's installationDate is equal to the given one.\n     *\n     * @param installationDate the given installationDate to compare the actual Page's installationDate to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual Page's installationDate is not equal to the given one.\n     */\n    public PageAssert hasInstallationDate(Date installationDate) {\n        // check that actual Page we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> installationDate to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                installationDate,\n                actual.getInstallationDate());\n\n        // check\n        if (!actual.getInstallationDate().equals(installationDate)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual Page's installedBy is equal to the given one.\n     *\n     * @param installedBy the given installedBy to compare the actual Page's installedBy to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual Page's installedBy is not equal to the given one.\n     */\n    public PageAssert hasInstalledBy(long installedBy) {\n        // check that actual Page we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> installedBy to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                installedBy, actual.getInstalledBy());\n\n        // check\n        if (actual.getInstalledBy() != installedBy) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual Page's lastUpdatedBy is equal to the given one.\n     *\n     * @param lastUpdatedBy the given lastUpdatedBy to compare the actual Page's lastUpdatedBy to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual Page's lastUpdatedBy is not equal to the given one.\n     */\n    public PageAssert hasLastUpdatedBy(long lastUpdatedBy) {\n        // check that actual Page we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> lastUpdatedBy to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                lastUpdatedBy, actual.getLastUpdatedBy());\n\n        // check\n        if (actual.getLastUpdatedBy() != lastUpdatedBy) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual Page's name is equal to the given one.\n     *\n     * @param name the given name to compare the actual Page's name to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual Page's name is not equal to the given one.\n     */\n    public PageAssert hasName(String name) {\n        // check that actual Page we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> name to be:\\n  <%s>\\n but was:\\n  <%s>\", actual, name,\n                actual.getName());\n\n        // check\n        if (!actual.getName().equals(name)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual Page's processDefinitionId is equal to the given one.\n     *\n     * @param processDefinitionId the given processDefinitionId to compare the actual Page's processDefinitionId to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual Page's processDefinitionId is not equal to the given one.\n     */\n    public PageAssert hasProcessDefinitionId(Long processDefinitionId) {\n        // check that actual Page we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> processDefinitionId to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                processDefinitionId,\n                actual.getProcessDefinitionId());\n\n        // check\n        if (!actual.getProcessDefinitionId().equals(processDefinitionId)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/process/Employee.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.process;\n\nimport java.io.Serializable;\n\npublic class Employee implements Comparable<Object>, Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    private final String name;\n\n    private final String firstName;\n\n    private final int experience;\n\n    public Employee(final String name, final String firstName) {\n        this.name = name;\n        this.firstName = firstName;\n        experience = 0;\n    }\n\n    public Employee(final String name, final String firstName, final int experience) {\n        this.name = name;\n        this.firstName = firstName;\n        this.experience = experience;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public String getFirstName() {\n        return firstName;\n    }\n\n    public int getExperience() {\n        return experience;\n    }\n\n    @Override\n    public int compareTo(final Object obj) {\n        final Employee empl = (Employee) obj;\n        if (experience != empl.experience) {\n            return experience - empl.experience;\n        }\n        if (!name.equals(empl.name)) {\n            return name.compareTo(empl.name);\n        }\n        return firstName.compareTo(empl.firstName);\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + experience;\n        result = prime * result + (firstName == null ? 0 : firstName.hashCode());\n        result = prime * result + (name == null ? 0 : name.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (!(obj instanceof Employee other)) {\n            return false;\n        }\n        if (experience != other.experience) {\n            return false;\n        }\n        if (firstName == null) {\n            if (other.firstName != null) {\n                return false;\n            }\n        } else if (!firstName.equals(other.firstName)) {\n            return false;\n        }\n        if (name == null) {\n            if (other.name != null) {\n                return false;\n            }\n        } else if (!name.equals(other.name)) {\n            return false;\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/process/Secretary.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.process;\n\npublic class Secretary extends Employee {\n\n    private static final long serialVersionUID = 1L;\n\n    public Secretary(String name, String firstName) {\n        super(name, firstName);\n    }\n\n    public Secretary(String name, String firstName, int experience) {\n        super(name, firstName, experience);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/process/SetDueDateConnector.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.process;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.connector.AbstractConnector;\nimport org.bonitasoft.engine.connector.ConnectorException;\nimport org.bonitasoft.engine.connector.ConnectorValidationException;\nimport org.bonitasoft.engine.exception.UpdateException;\n\npublic class SetDueDateConnector extends AbstractConnector {\n\n    static Date dueDate = new Date(1000000);\n\n    @Override\n    protected void executeBusinessLogic() throws ConnectorException {\n        long activityInstanceId = getExecutionContext().getActivityInstanceId();\n        try {\n            getAPIAccessor().getProcessAPI().updateDueDateOfTask(activityInstanceId, dueDate);\n        } catch (UpdateException e) {\n            throw new ConnectorException(e);\n        }\n    }\n\n    @Override\n    public void validateInputParameters() throws ConnectorValidationException {\n\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/search/SlowConnector.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport java.util.concurrent.TimeUnit;\n\nimport org.bonitasoft.engine.connector.AbstractConnector;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class SlowConnector extends AbstractConnector {\n\n    private static final Logger LOG = LoggerFactory.getLogger(SlowConnector.class);\n\n    @Override\n    public void validateInputParameters() {\n        // do nothing\n    }\n\n    @Override\n    protected void executeBusinessLogic() {\n        try {\n            final long sleepingTime = 15;\n            LOG.info(\"SlowConnector instance is going to sleep for {} seconds\", sleepingTime);\n            TimeUnit.SECONDS.sleep(sleepingTime);\n            LOG.info(\"SlowConnector instance sleeping done\");\n        } catch (InterruptedException e) {\n            LOG.error(\"Interruption while sleeping\", e);\n        }\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/util/AssertionsUtils.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.util;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class AssertionsUtils {\n\n    private static final Logger logger = LoggerFactory.getLogger(AssertionsUtils.class);\n\n    @FunctionalInterface\n    public interface Assertion {\n\n        void assertThat() throws Exception;\n\n    }\n\n    @FunctionalInterface\n    public interface ExceptionHandler {\n\n        void handle(Exception e) throws Exception;\n\n    }\n\n    public static void assertNoErrorAfterXAttemps(int attempts, Assertion assertThat, ExceptionHandler onError)\n            throws Exception {\n        for (int attempt = 1; attempt <= attempts; attempt++) {\n            logger.info(\"Attempt {} on {}\", attempt, attempts);\n            try {\n                assertThat.assertThat();\n            } catch (Exception e) {\n                logger.error(\"Attempt {} on {} failed\", attempt, attempts);\n                onError.handle(e);\n                throw e;\n            }\n            logger.info(\"Completed attempt {} on {}\", attempt, attempts);\n        }\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/util/FunctionalMatcher.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.util;\n\nimport org.hamcrest.Description;\nimport org.hamcrest.Matcher;\n\npublic interface FunctionalMatcher<T> extends Matcher {\n\n    boolean isMatchting(T item);\n\n    @Override\n    default boolean matches(Object item) {\n        return isMatchting(((T) item));\n    }\n\n    @Override\n    default void describeMismatch(Object item, Description mismatchDescription) {\n        mismatchDescription.appendText(\"was \").appendValue(item);\n    }\n\n    @Override\n    default void _dont_implement_Matcher___instead_extend_BaseMatcher_() {\n\n    }\n\n    @Override\n    default void describeTo(Description description) {\n\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/business/application/applicationWithUnavailableInfo.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n  ~ Copyright (C) 2014 BonitaSoft S.A.\n  ~ BonitaSoft is a trademark of BonitaSoft SA.\n  ~ This software file is BONITASOFT CONFIDENTIAL. Not For Distribution.\n  ~ For commercial licensing information, contact:\n  ~ BonitaSoft, 32 rue Gustave Eiffel – 38000 Grenoble\n  ~ or BonitaSoft US, 51 Federal Street, Suite 305, San Francisco, CA 94107\n  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->\n\n<applications xmlns=\"http://documentation.bonitasoft.com/application-xml-schema/1.1\">\n    <application profile=\"ThisProfileDoesNotExist\" state=\"ACTIVATED\" token=\"HR-dashboard\" version=\"2.0\" layout=\"custompage_layoutBonita\" theme=\"custompage_themeBonita\">\n        <displayName>My HR dashboard</displayName>\n        <description>This is the HR dashboard.</description>\n        <iconPath>/icon.jpg</iconPath>\n        <applicationPages>\n            <applicationPage customPage=\"custompage_notexists\" token=\"will-not-be-imported\" />\n            <applicationPage customPage=\"custompage_mynewcustompage\" token=\"my-new-custom-page\" />\n        </applicationPages>\n        <applicationMenus>\n            <applicationMenu>\n                <displayName>HR follow-up</displayName>\n                <applicationMenus>\n                    <applicationMenu applicationPage=\"will-not-be-imported\">\n                        <displayName>Wrong menu 1</displayName>\n                    </applicationMenu>\n                    <applicationMenu applicationPage=\"my-new-custom-page\">\n                        <displayName>Daily HR follow-up</displayName>\n                    </applicationMenu>\n                    <applicationMenu applicationPage=\"never-existed\">\n                        <displayName>Wrong menu 2</displayName>\n                    </applicationMenu>\n                </applicationMenus>\n            </applicationMenu>\n            <applicationMenu>\n                <displayName>Empty menu</displayName>\n            </applicationMenu>\n        </applicationMenus>\n    </application>\n</applications>"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/business/application/applications.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n  ~ Copyright (C) 2015 BonitaSoft S.A.\n  ~ BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n  ~ This library is free software; you can redistribute it and/or modify it under the terms\n  ~ of the GNU Lesser General Public License as published by the Free Software Foundation\n  ~ version 2.1 of the License.\n  ~ This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n  ~ without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n  ~ See the GNU Lesser General Public License for more details.\n  ~ You should have received a copy of the GNU Lesser General Public License along with this\n  ~ program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n  ~ Floor, Boston, MA 02110-1301, USA.\n  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->\n\n<applications xmlns=\"http://documentation.bonitasoft.com/application-xml-schema/1.1\">\n    <application profile=\"User\" state=\"ACTIVATED\" token=\"HR-dashboard\" version=\"2.0\" homePage=\"my-new-custom-page\" layout=\"custompage_layoutBonita\" theme=\"custompage_themeBonita\">\n        <displayName>My HR dashboard</displayName>\n        <description>This is the HR dashboard.</description>\n        <iconPath>/icon.jpg</iconPath>\n        <applicationPages>\n            <applicationPage customPage=\"custompage_mynewcustompage\" token=\"my-new-custom-page\" />\n        </applicationPages>\n        <applicationMenus>\n            <applicationMenu>\n                <displayName>HR follow-up</displayName>\n                <applicationMenus>\n                    <applicationMenu applicationPage=\"my-new-custom-page\">\n                        <displayName>Daily HR follow-up</displayName>\n                    </applicationMenu>\n                </applicationMenus>\n            </applicationMenu>\n            <applicationMenu>\n                <displayName>Empty menu</displayName>\n            </applicationMenu>\n        </applicationMenus>\n    </application>\n    <application state=\"ACTIVATED\" token=\"My\" version=\"2.0\" layout=\"custompage_layoutBonita\" theme=\"custompage_themeBonita\">\n        <displayName>Marketing</displayName>\n        <applicationPages/>\n        <applicationMenus/>\n    </application>\n</applications>"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/business/data/BusinessDataUpdateConnector.def",
    "content": "<connectorDefinition>\n\t<version>1.0</version>\n\t<connectorId>org.bonitasoft.connector.BusinessDataUpdateConnector</connectorId>\n\t<icon>xxx.png</icon>\n\t<categories>\n\t\t<category>\n\t\t\t<name>other</name>\n\t\t\t<icon>org/bonitasoft/engine/bdr/BusinessDataUpdateConnector.png</icon>\n\t\t</category>\n\t</categories>\n\t<inputs>\n\t\t<input type=\"String\">bizData</input>\n\t\t<required />\n\t\t<parameters>\n\t\t\t<string />\n\t\t</parameters>\n\t</inputs>\n\t<outputs>\n\t\t<output type=\"String\">output1</output>\n\t</outputs>\n</connectorDefinition>"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/business/data/BusinessDataUpdateConnector.impl",
    "content": "<connectorImplementation>\n\n\t<definitionId>org.bonitasoft.connector.BusinessDataUpdateConnector</definitionId>\n\t<definitionVersion>1.0</definitionVersion>\n\t<implementationClassname>org.bonitasoft.engine.business.data.BusinessDataUpdateConnector</implementationClassname>\n\t<implementationId>org.bonitasoft.connector.BusinessDataUpdateConnector</implementationId>\n\t<implementationVersion>1.0</implementationVersion>\n\n\t<jarDependencies>\n\t\t<jarDependency>BusinessDataUpdateConnector.jar</jarDependency>\n\t</jarDependencies>\n</connectorImplementation>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/business/data/findByFirstNameAndLastNameNewOrder.json",
    "content": "[\n  {\n    \"persistenceId\": 1,\n    \"persistenceVersion\": 0,\n    \"persistenceId_string\": \"1\",\n    \"persistenceVersion_string\": \"0\",\n    \"firstName\": \"Alphonse\",\n    \"lastName\": \"Dupond\",\n    \"hireDate\": \"123456789\",\n    \"birthDate\": null,\n    \"phoneNumbers\": [\n      \"123456789\"\n    ],\n    \"addresses\": [\n      {\n        \"persistenceId\": 2,\n        \"persistenceVersion\": 0,\n        \"persistenceId_string\": \"2\",\n        \"persistenceVersion_string\": \"0\",\n        \"street\": \"32, rue Gustave Eiffel\",\n        \"city\": \"Grenoble\",\n        \"links\": [\n          {\n            \"rel\": \"country\",\n            \"href\": \"/businessdata/com.company.model.Address/2/country\"\n          }\n        ]\n      }\n    ],\n    \"booleanField\": null,\n    \"dog\": null,\n    \"links\": [\n      {\n        \"rel\": \"address\",\n        \"href\": \"/businessdata/com.company.model.Employee/1/address\"\n      },\n      {\n        \"rel\": \"Cat\",\n        \"href\": \"/businessdata/com.company.model.Employee/1/Cat\"\n      }\n    ]\n  }\n]"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/business/data/findByFirstNameFetchAddresses.json",
    "content": "[\n  {\n    \"persistenceId\": 1,\n    \"persistenceVersion\": 0,\n    \"persistenceId_string\": \"1\",\n    \"persistenceVersion_string\": \"0\",\n    \"firstName\": \"Alphonse\",\n    \"lastName\": \"Dupond\",\n    \"hireDate\": \"123456789\",\n    \"booleanField\": true,\n    \"birthDate\":null,\n    \"phoneNumbers\": [\n      \"123456789\"\n    ],\n    \"addresses\": [\n      {\n        \"persistenceId\": 2,\n        \"persistenceVersion\": 0,\n        \"persistenceId_string\": \"2\",\n        \"persistenceVersion_string\": \"0\",\n        \"street\": \"32, rue Gustave Eiffel\",\n        \"city\": \"Grenoble\",\n        \"links\": [\n          {\n            \"rel\": \"country\",\n            \"href\": \"/businessdata/org.bonita.pojo.Address/2/country\"\n          }\n        ]\n      }\n    ],\n    \"dog\": null,\n    \"links\": [\n      {\n        \"rel\": \"address\",\n        \"href\": \"/businessdata/org.bonita.pojo.Employee/1/address\"\n      },\n      {\n        \"rel\": \"cat\",\n        \"href\": \"/businessdata/com.company.model.Employee/1/cat\"\n      }\n    ]\n  }\n]"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/business/data/getBusinessDataByIdAddress.json",
    "content": "{\n  \"persistenceId\": 2,\n  \"persistenceVersion\": 0,\n  \"persistenceId_string\": \"2\",\n  \"persistenceVersion_string\": \"0\",\n  \"street\": \"32, rue Gustave Eiffel\",\n  \"city\": \"Grenoble\",\n  \"links\": [\n    {\n      \"rel\": \"country\",\n      \"href\": \"/businessdata/org.bonita.pojo.Address/2/country\"\n    }\n  ]\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/business/data/getBusinessDataByIdEmployee.json",
    "content": "{\n  \"persistenceId\": 1,\n  \"persistenceVersion\": 0,\n  \"persistenceId_string\": \"1\",\n  \"persistenceVersion_string\": \"0\",\n  \"firstName\": \"Alphonse\",\n  \"lastName\": \"Dupond\",\n  \"hireDate\": \"123456789\",\n  \"booleanField\": true,\n  \"birthDate\":null,\n  \"phoneNumbers\": [\n    \"123456789\"\n  ],\n  \"addresses\": [\n    {\n      \"persistenceId\": 2,\n      \"persistenceVersion\": 0,\n      \"persistenceId_string\": \"2\",\n      \"persistenceVersion_string\": \"0\",\n      \"street\": \"32, rue Gustave Eiffel\",\n      \"city\": \"Grenoble\",\n      \"links\": [\n        {\n          \"rel\": \"country\",\n          \"href\": \"/businessdata/org.bonita.pojo.Address/2/country\"\n        }\n      ]\n    }\n  ],\n  \"dog\": null,\n  \"links\": [\n    {\n      \"rel\": \"address\",\n      \"href\": \"/businessdata/org.bonita.pojo.Employee/1/address\"\n    },\n    {\n      \"rel\": \"cat\",\n      \"href\": \"/businessdata/com.company.model.Employee/1/cat\"\n    }\n  ]\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/business/data/getEmployeeByPhoneNumber.json",
    "content": "[\n  {\n    \"persistenceId\": 1,\n    \"persistenceVersion\": 0,\n    \"persistenceId_string\": \"1\",\n    \"persistenceVersion_string\": \"0\",\n    \"firstName\": \"Alphonse\",\n    \"lastName\": \"Dupond\",\n    \"hireDate\": \"123456789\",\n    \"booleanField\": true,\n    \"birthDate\":null,\n    \"phoneNumbers\": [\n      \"123456789\"\n    ],\n    \"addresses\": [\n      {\n        \"persistenceId\": 2,\n        \"persistenceVersion\": 0,\n        \"persistenceId_string\": \"2\",\n        \"persistenceVersion_string\": \"0\",\n        \"street\": \"32, rue Gustave Eiffel\",\n        \"city\": \"Grenoble\",\n        \"links\": [\n          {\n            \"rel\": \"country\",\n            \"href\": \"/businessdata/com.company.model.Address/2/country\"\n          }\n        ]\n      }\n    ],\n    \"dog\": null,\n    \"links\": [\n      {\n        \"rel\": \"address\",\n        \"href\": \"/businessdata/org.bonita.pojo.Employee/1/address\"\n      },\n      {\n        \"rel\": \"cat\",\n        \"href\": \"/businessdata/com.company.model.Employee/1/cat\"\n      }\n    ]\n  }\n]"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/command/Employee.find.2.1.json",
    "content": "[\n  {\n    \"persistenceId\": \"${json-unit.ignore}\",\n    \"persistenceId_string\": \"${json-unit.ignore}\",\n    \"persistenceVersion\": 0,\n    \"persistenceVersion_string\": \"0\",\n    \"firstName\": \"Matthieu\",\n    \"lastName\": \"Chaffotte\",\n    \"birthdate\": null,\n    \"links\": [\n      {\n        \"rel\": \"addresses\",\n        \"href\": \"${json-unit.ignore}\"\n      }\n    ]\n  }\n]\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/command/web/profile/AllProfiles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<profiles:profiles xmlns:profiles=\"http://documentation.bonitasoft.com/profile-xml-schema/1.0\">\n\t<profile name=\"Administrator\" isDefault=\"true\">\n\t\t<description>Administrator profile</description>\n\t\t<profileMapping>\n\t\t\t<users>\n\t\t\t\t<user>userName1</user>\n\t\t\t\t<user>userName2</user>\n\t\t\t</users>\n\t\t</profileMapping>\n\t</profile>\n\t<profile name=\"Team manager\" isDefault=\"true\">\n\t\t<description>Team Manager profile</description>\n\n\t\t<profileMapping>\n\t\t\t<users>\n\t\t\t\t<user>userName3</user>\n\t\t\t\t<user>userName4</user>\n\t\t\t\t<user>userName5</user>\n\t\t\t</users>\n\t\t</profileMapping>\n\t</profile>\n\t<profile name=\"Process owner\" isDefault=\"false\">\n\t\t<description>Process owner profile</description>\n\n\t</profile>\n\t<profile name=\"User\" isDefault=\"false\">\n\t\t<description>User profile</description>\n\t</profile>\n</profiles:profiles>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/command/web/profile/RestoreDefaultProfiles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<profiles:profiles xmlns:profiles=\"http://documentation.bonitasoft.com/profile-xml-schema/1.0\">\n\t<profile name=\"Administrator\" isDefault=\"true\">\n\t\t<description>Administrator profile</description>\n\t\t<profileMapping>\n\t\t\t<users>\n\t\t\t\t<user>userName1</user>\n\t\t\t\t<user>userName2</user>\n\t\t\t</users>\n\t\t</profileMapping>\n\t</profile>\n\t<profile name=\"User\" isDefault=\"false\">\n\t\t<description>User profile</description>\n\t</profile>\n</profiles:profiles>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/command/web/profile/deleteExistingProfile.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<profiles:profiles xmlns:profiles=\"http://documentation.bonitasoft.com/profile-xml-schema/1.0\">\n\t<profile name=\"Team Manager\" isDefault=\"true\">\n\t\t<description>TM profile</description>\n\t\t<profileMapping>\n\t\t\t<users>\n\t\t\t\t<user>userName1</user>\n\t\t\t\t<user>userName2</user>\n\t\t\t</users>\n\t\t</profileMapping>\n\t</profile>\n</profiles:profiles>"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/APIAccessorConnector.impl",
    "content": "<connectorImplementation>\n\n\t<definitionId>org.bonitasoft.connector.APIAccessorConnector</definitionId>\n\t<definitionVersion>1.0</definitionVersion>\n\t<implementationClassname>org.bonitasoft.engine.connectors.APIAccessorConnector</implementationClassname>\n\t<implementationId>APIAccessorConnector</implementationId>\n\t<implementationVersion>1.0</implementationVersion>\n\n\t<jarDependencies>\n\t\t<jarDependency>APIAccessorConnector.jar</jarDependency>\n\t</jarDependencies>\n\n</connectorImplementation>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/TestConnector.def",
    "content": "<connectorDefinition>\n\t<version>1.0</version>\n\t<connectorId>org.bonitasoft.connector.testConnector</connectorId>\n\t<icon>xxx.png</icon>\n\t<categories>\n\t\t<category>\n\t\t\t<name>other</name>\n\t\t\t<icon>org/bonitasoft/engine/connectors/TestConnector.png</icon>\n\t\t</category>\n\t</categories>\n\t<inputs>\n\t\t<input type=\"String\">input1</input>\n\t\t<required />\n\t\t<parameters>\n\t\t\t<string />\n\t\t</parameters>\n\t</inputs>\n\t<outputs>\n\t</outputs>\n</connectorDefinition>"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/TestConnector.impl",
    "content": "<connectorImplementation>\n\t\n\t<definitionId>org.bonitasoft.connector.testConnector</definitionId>\n\t<definitionVersion>1.0</definitionVersion>\n\t<implementationClassname>org.bonitasoft.engine.connectors.TestConnector</implementationClassname>\n\t<implementationId>org.bonitasoft.connector.testConnector</implementationId>\n\t<implementationVersion>1.0</implementationVersion>\n\t\n\t<jarDependencies>\n\t\t<jarDependency>TestConnector.jar</jarDependency>\n\t\t<jarDependency>VariableStorage.jar</jarDependency>\n\t</jarDependencies>\n\t\n</connectorImplementation>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/TestConnector2.impl",
    "content": "<connectorImplementation>\n\t\n\t<definitionId>org.bonitasoft.connector.testConnector</definitionId>\n\t<definitionVersion>1.0</definitionVersion>\n\t<implementationClassname>org.bonitasoft.engine.connectors.TestConnector2</implementationClassname>\n\t<implementationId>org.bonitasoft.connector.testConnector2</implementationId>\n\t<implementationVersion>1.0</implementationVersion>\n\t\n\t<jarDependencies>\n\t\t<jarDependency>TestConnector2.jar</jarDependency>\n\t\t<jarDependency>VariableStorage.jar</jarDependency>\n\t</jarDependencies>\n\t\n</connectorImplementation>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/TestConnector3.def",
    "content": "<connectorDefinition>\n\t<version>1.0</version>\n\t<connectorId>org.bonitasoft.connector.testConnector3</connectorId>\n\t<icon>xxx.png</icon>\n\t<categories>\n\t\t<category>\n\t\t\t<name>other</name>\n\t\t\t<icon>org/bonitasoft/engine/connectors/TestConnector3.png</icon>\n\t\t</category>\n\t</categories>\n\t<inputs>\n\t\t<input type=\"String\">input1</input>\n\t\t<input type=\"String\">input2</input>\n\t\t<input type=\"String\">input3</input>\n\t\t<input type=\"String\">input4</input>\n\t\t<required />\n\t\t<parameters>\n\t\t\t<string />\n\t\t</parameters>\n\t</inputs>\n\t<outputs>\n\t</outputs>\n</connectorDefinition>"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/TestConnector3.impl",
    "content": "<connectorImplementation>\n\t\n\t<definitionId>org.bonitasoft.connector.testConnector3</definitionId>\n\t<definitionVersion>1.0</definitionVersion>\n\t<implementationClassname>org.bonitasoft.engine.connectors.TestConnector3</implementationClassname>\n\t<implementationId>org.bonitasoft.connector.testConnector3</implementationId>\n\t<implementationVersion>1.0</implementationVersion>\n\t\n\t<jarDependencies>\n\t\t<jarDependency>TestConnector3.jar</jarDependency>\n\t\t<jarDependency>VariableStorage.jar</jarDependency>\n\t</jarDependencies>\n\t\n</connectorImplementation>"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/TestConnectorInJar.impl",
    "content": "<connectorImplementation>\n\t\n\t<definitionId>connectorInJar</definitionId>\n\t<definitionVersion>1.0.0</definitionVersion>\n\t<implementationClassname>ConnectorInJar</implementationClassname>\n\t<implementationId>connectorInJarImpl</implementationId>\n\t<implementationVersion>1.0.0</implementationVersion>\n\t\n</connectorImplementation>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/TestConnectorLongToExecute.impl",
    "content": "<connectorImplementation>\n\t\n\t<definitionId>testConnectorLongToExecute</definitionId>\n\t<definitionVersion>1.0.0</definitionVersion>\n\t<implementationClassname>org.bonitasoft.engine.connectors.TestConnectorLongToExecute</implementationClassname>\n\t<implementationId>testConnectorLongToExecuteImpl</implementationId>\n\t<implementationVersion>1.0.0</implementationVersion>\n\t\n</connectorImplementation>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/TestConnectorThatThrowException.impl",
    "content": "<connectorImplementation>\n\t\n\t<definitionId>testConnectorThatThrowException</definitionId>\n\t<definitionVersion>1.0</definitionVersion>\n\t<implementationClassname>org.bonitasoft.engine.connectors.TestConnectorThatThrowException</implementationClassname>\n\t<implementationId>testConnectorThatThrowExceptionImpl</implementationId>\n\t<implementationVersion>1.0</implementationVersion>\n\t\n\t<jarDependencies>\n\t\t<jarDependency>TestConnectorThatThrowException.jar</jarDependency>\n\t</jarDependencies>\n\t\n</connectorImplementation>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/TestConnectorWithAPICall.impl",
    "content": "<connectorImplementation>\n\t\n\t<definitionId>org.bonitasoft.engine.connectors.TestConnectorWithAPICall</definitionId>\n\t<definitionVersion>1.0</definitionVersion>\n\t<implementationClassname>org.bonitasoft.engine.connectors.TestConnectorWithAPICall</implementationClassname>\n\t<implementationId>org.bonitasoft.engine.connectors.TestConnectorWithAPICall</implementationId>\n\t<implementationVersion>1.0</implementationVersion>\n\t\n\t<jarDependencies>\n\t\t<jarDependency>TestConnectorWithAPICall.jar</jarDependency>\n\t</jarDependencies>\n\t\n</connectorImplementation>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/TestConnectorWithConnectedResource.def",
    "content": "<connectorDefinition>\n\t<version>1.0</version>\n\t<connectorId>org.bonitasoft.connector.testConnectorWithConnectedResource</connectorId>\n\t<icon>xxx.png</icon>\n\t<categories>\n\t\t<category>\n\t\t\t<name>other</name>\n\t\t\t<icon>org/bonitasoft/engine/connectors/TestConnectorWithConnectedResource.png</icon>\n\t\t</category>\n\t</categories>\n\t<inputs>\n\t</inputs>\n\t<outputs>\n\t\t<output type=\"String\">output</output>\n\t</outputs>\n</connectorDefinition>"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/TestConnectorWithConnectedResource.impl",
    "content": "<connectorImplementation>\n\n\t<definitionId>org.bonitasoft.connector.testConnectorWithConnectedResource</definitionId>\n\t<definitionVersion>1.0</definitionVersion>\n\t<implementationClassname>org.bonitasoft.engine.connectors.TestConnectorWithConnectedResource</implementationClassname>\n\t<implementationId>org.bonitasoft.connector.testConnectorWithConnectedResource</implementationId>\n\t<implementationVersion>1.0</implementationVersion>\n\n\t<jarDependencies>\n\t\t<jarDependency>TestConnectorWithConnectedResource.jar</jarDependency>\n\t</jarDependencies>\n</connectorImplementation>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/TestConnectorWithCustomType.impl",
    "content": "<connectorImplementation>\n\t\n\t<definitionId>connectorWithCustomType</definitionId>\n\t<definitionVersion>1.0.0</definitionVersion>\n\t<implementationClassname>org.connector.custom.ConnectorWithCustomType</implementationClassname>\n\t<implementationId>connectorWithCustomTypeImpl</implementationId>\n\t<implementationVersion>1.0.0</implementationVersion>\n\t\n</connectorImplementation>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/TestConnectorWithModifiedOutput.impl",
    "content": "<connectorImplementation>\n\n\t<definitionId>org.bonitasoft.connector.testConnectorWithOutput</definitionId>\n\t<definitionVersion>1.0</definitionVersion>\n\t<implementationClassname>org.bonitasoft.engine.connectors.TestConnectorWithModifiedOutput</implementationClassname>\n\t<implementationId>org.bonitasoft.connector.testConnectorWithModifiedOutput</implementationId>\n\t<implementationVersion>1.0</implementationVersion>\n\n\t<jarDependencies>\n\t\t<jarDependency>TestConnectorWithModifiedOutput.jar</jarDependency>\n\t</jarDependencies>\n</connectorImplementation>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/TestConnectorWithNotSerializableOutput.def",
    "content": "<connectorDefinition>\n\t<version>1.0</version>\n\t<connectorId>org.bonitasoft.connector.testConnectorWithNotSerializableOutput</connectorId>\n\t<icon>xxx.png</icon>\n\t<categories>\n\t\t<category>\n\t\t\t<name>other</name>\n\t\t\t<icon>org/bonitasoft/engine/connectors/TestConnectorWithNotSerializableOutput.png</icon>\n\t\t</category>\n\t</categories>\n\t<inputs>\n\t</inputs>\n\t<outputs>\n\t\t<output name=\"output1\" type=\"java.lang.Object\" />\n\t</outputs>\n</connectorDefinition>"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/TestConnectorWithNotSerializableOutput.impl",
    "content": "<connectorImplementation>\n\t<definitionId>org.bonitasoft.connector.testConnectorWithNotSerializableOutput</definitionId>\n\t<definitionVersion>1.0</definitionVersion>\n\t<implementationClassname>org.bonitasoft.engine.connectors.TestConnectorWithNotSerializableOutput</implementationClassname>\n\t<implementationId>org.bonitasoft.connector.testConnectorWithNotSerializableOutput</implementationId>\n\t<implementationVersion>1.0</implementationVersion>\n\n\t<jarDependencies>\n\t\t<jarDependency>TestConnectorWithNotSerializableOutput.jar</jarDependency>\n\t</jarDependencies>\n</connectorImplementation>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/TestConnectorWithOutput.def",
    "content": "<connectorDefinition>\n\t<version>1.0</version>\n\t<connectorId>org.bonitasoft.connector.testConnectorWithOutput</connectorId>\n\t<icon>xxx.png</icon>\n\t<categories>\n\t\t<category>\n\t\t\t<name>other</name>\n\t\t\t<icon>org/bonitasoft/engine/connectors/TestConnectorWithOutput.png</icon>\n\t\t</category>\n\t</categories>\n\t<inputs>\n\t\t<input type=\"String\">input1</input>\n\t\t<required />\n\t\t<parameters>\n\t\t\t<string />\n\t\t</parameters>\n\t</inputs>\n\t<outputs>\n\t\t<output type=\"String\">output1</output>\n\t</outputs>\n</connectorDefinition>"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/TestConnectorWithOutput.impl",
    "content": "<connectorImplementation>\n\n\t<definitionId>org.bonitasoft.connector.testConnectorWithOutput</definitionId>\n\t<definitionVersion>1.0</definitionVersion>\n\t<implementationClassname>org.bonitasoft.engine.connectors.TestConnectorWithOutput</implementationClassname>\n\t<implementationId>org.bonitasoft.connector.testConnectorWithOutput</implementationId>\n\t<implementationVersion>1.0</implementationVersion>\n\n\t<jarDependencies>\n\t\t<jarDependency>TestConnectorWithOutput.jar</jarDependency>\n\t</jarDependencies>\n</connectorImplementation>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/TestExternalConnector.impl",
    "content": "<connectorImplementation>\n\n\t<definitionId>org.bonitasoft.connector.testExternalConnector</definitionId>\n\t<definitionVersion>1.0</definitionVersion>\n\t<implementationClassname>org.bonitasoft.engine.connectors.TestExternalConnector</implementationClassname>\n\t<implementationId>org.bonitasoft.connector.testExternalConnector</implementationId>\n\t<implementationVersion>1.0</implementationVersion>\n\n\t<jarDependencies>\n\t\t<jarDependency>TestExternalConnector.jar</jarDependency>\n\t</jarDependencies>\n</connectorImplementation>"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/UnknownClassConnector.impl",
    "content": "<connectorImplementation>\n\n\t<definitionId>unkownClassConnectorDef</definitionId>\n\t<definitionVersion>1.0.0</definitionVersion>\n\t<implementationClassname>org.unknown.MyUnknownClass</implementationClassname>\n\t<implementationId>unkownClassConnectorId</implementationId>\n\t<implementationVersion>1.0.0</implementationVersion>\n\n</connectorImplementation>"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/filter/user/GroupUserFilter.impl",
    "content": "<connectorImplementation>\n\t<definitionId>org.bonitasoft.engine.filter.user.GroupUserFilter</definitionId>\n\t<definitionVersion>1.0</definitionVersion>\n\t<implementationClassname>org.bonitasoft.engine.filter.user.GroupUserFilter</implementationClassname>\n\t<implementationId>org.bonitasoft.engine.filter.user.GroupUserFilter</implementationId>\n\t<implementationVersion>1.0</implementationVersion>\n\t<jarDependencies>\n\t\t<jarDependency>GroupUserFilter.jar</jarDependency>\n\t</jarDependencies>\n</connectorImplementation>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/filter/user/TestFilter.def",
    "content": "<connectorDefinition>\n\t<version>1.0</version>\n\t<connectorId>org.bonitasoft.engine.filter.user.testFilter</connectorId>\n\t<icon>xxx.png</icon>\n\t<categories>\n\t\t<category>\n\t\t\t<name>other</name>\n\t\t\t<icon>org/bonitasoft/engine/filter/user/TestFilter.png</icon>\n\t\t</category>\n\t</categories>\n\t<inputs>\n\t\t<input type=\"String\">userId</input>\n\t\t<required />\n\t\t<parameters>\n\t\t\t<string />\n\t\t</parameters>\n\t</inputs>\n\t<outputs>\n\t</outputs>\n</connectorDefinition>"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/filter/user/TestFilter.impl",
    "content": "<connectorImplementation>\n\t<definitionId>org.bonitasoft.engine.filter.user.testFilter</definitionId>\n\t<definitionVersion>1.0</definitionVersion>\n\t<implementationClassname>org.bonitasoft.engine.filter.user.TestFilter</implementationClassname>\n\t<implementationId>org.bonitasoft.engine.filter.user.testFilter</implementationId>\n\t<implementationVersion>1.0</implementationVersion>\n\t<jarDependencies>\n\t\t<jarDependency>TestFilter.jar</jarDependency>\n\t</jarDependencies>\n</connectorImplementation>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/filter/user/TestFilterThatThrowException.impl",
    "content": "<connectorImplementation id=\"org.bonitasoft.engine.filter.user.testFilterThatThrowException\" version=\"1.0\"\n\tdefinitionId=\"org.bonitasoft.engine.filter.user.testFilterThatThrowException\" definitionVersion=\"1.0\"\n\timplementationClassname=\"org.bonitasoft.engine.filter.user.TestFilterThatThrowException\">\n\t<jarDependencies>\n\t</jarDependencies>\n</connectorImplementation>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/filter/user/TestFilterThatThrowNoClassDef.impl",
    "content": "<connectorImplementation id=\"org.bonitasoft.engine.filter.user.testFilterThatThrowNoClassDef\" version=\"1.0\"\n\tdefinitionId=\"org.bonitasoft.engine.filter.user.testFilterThatThrowNoClassDef\" definitionVersion=\"1.0\"\n\timplementationClassname=\"org.bonitasoft.engine.filter.user.TestFilterThatThrowNoClassDef\">\n\t<jarDependencies>\n\t</jarDependencies>\n</connectorImplementation>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/filter/user/TestFilterUsingActorName.impl",
    "content": "<connectorImplementation>\n\t<definitionId>org.bonitasoft.engine.filter.user.testFilterUsingActorName</definitionId>\n\t<definitionVersion>1.0</definitionVersion>\n\t<implementationClassname>org.bonitasoft.engine.filter.user.TestFilterUsingActorName</implementationClassname>\n\t<implementationId>org.bonitasoft.engine.filter.user.testFilterUsingActorName</implementationId>\n\t<implementationVersion>1.0</implementationVersion>\n\t<jarDependencies>\n\t\t<jarDependency>TestFilterUsingActorName.jar</jarDependency>\n\t</jarDependencies>\n</connectorImplementation>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/filter/user/TestFilterWithAutoAssign.impl",
    "content": "<connectorImplementation>\n\t<definitionId>org.bonitasoft.engine.filter.user.testFilterWithAutoAssign</definitionId>\n\t<definitionVersion>1.0</definitionVersion>\n\t<implementationClassname>org.bonitasoft.engine.filter.user.TestFilterWithAutoAssign</implementationClassname>\n\t<implementationId>org.bonitasoft.engine.filter.user.testFilterWithAutoAssign</implementationId>\n\t<implementationVersion>1.0</implementationVersion>\n\t<jarDependencies>\n\t\t<jarDependency>TestFilterWithAutoAssign.jar</jarDependency>\n\t</jarDependencies>\n</connectorImplementation>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/filter/user/TestFilterWithClassNotFound.impl",
    "content": "<connectorImplementation id=\"org.bonitasoft.engine.filter.user.testFilterWithClassNotFound\" version=\"1.0\"\n\tdefinitionId=\"org.bonitasoft.engine.filter.user.testFilterWithClassNotFound\" definitionVersion=\"1.0\" implementationClassname=\"org.bonitasoft.engine.filter.user.Unknown\">\n\t<jarDependencies>\n\t</jarDependencies>\n</connectorImplementation>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/identity/ACME.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<organization:Organization xmlns:organization=\"http://documentation.bonitasoft.com/organization-xml-schema\">\n  <customUserInfoDefinitions>\n    <customUserInfoDefinition>\n      <name>Office location</name>\n    </customUserInfoDefinition>\n    <customUserInfoDefinition>\n      <name>Skills</name>\n      <description>The user skills</description>\n    </customUserInfoDefinition>\n  </customUserInfoDefinitions>\n  <users>\n    <user userName=\"william.jobs\">\n      <password encrypted=\"false\">bpm</password>\n      <firstName>William</firstName>\n      <lastName>Jobs</lastName>\n      <title>Mr</title>\n      <jobTitle>Chief Executive Officer</jobTitle>\n      <professionalData>\n        <email>william.jobs@acme.com</email>\n        <phoneNumber>484-302-5985</phoneNumber>\n        <faxNumber>484-302-0985</faxNumber>\n        <building>70</building>\n        <address>Renwick Drive</address>\n        <zipCode>19108</zipCode>\n        <city>Philadelphia</city>\n        <state>PA</state>\n        <country>United States</country>\n      </professionalData>\n      <metaDatas>\n        <metaData name=\"Skype ID\"/>\n        <metaData name=\"Twitter\"/>\n        <metaData name=\"Facebook\"/>\n      </metaDatas>\n       <customUserInfoValues>\n\t    <customUserInfoValue>\n\t      <name>Office location</name>\n\t      <value>Engineering</value>\n\t    </customUserInfoValue>\n\t    <customUserInfoValue>\n\t      <name>Skills</name>\n\t      <value>Java</value>\n\t    </customUserInfoValue>\n\t  </customUserInfoValues>\n      \n    </user>\n    <user userName=\"april.sanchez\">\n      <password encrypted=\"false\">bpm</password>\n      <firstName>April</firstName>\n      <lastName>Sanchez</lastName>\n      <title>Mrs</title>\n      <jobTitle>Compensation specialist</jobTitle>\n      <manager>helen.kelly</manager>\n      <professionalData>\n        <email>april.sanchez@acme.com</email>\n        <phoneNumber>484-302-5561</phoneNumber>\n        <faxNumber>484-302-0561</faxNumber>\n        <building>70</building>\n        <address>Renwick Drive</address>\n        <zipCode>19108</zipCode>\n        <city>Philadelphia</city>\n        <state>PA</state>\n        <country>United States</country>\n      </professionalData>\n      \n    </user>\n    <user userName=\"helen.kelly\">\n      <password encrypted=\"false\">bpm</password>\n      <firstName>Helen</firstName>\n      <lastName>Kelly</lastName>\n      <title>Mrs</title>\n      <jobTitle>Human resource manager</jobTitle>\n      <manager>william.jobs</manager>\n      <professionalData>\n        <email>helen.kelly@acme.com</email>\n        <phoneNumber>484-302-5561</phoneNumber>\n        <faxNumber>484-302-0561</faxNumber>\n        <building>70</building>\n        <address>Renwick Drive</address>\n        <zipCode>19108</zipCode>\n        <city>Philadelphia</city>\n        <state>PA</state>\n        <country>United States</country>\n      </professionalData>\n      \n    </user>\n    <user userName=\"walter.bates\">\n      <password encrypted=\"false\">bpm</password>\n      <firstName>Walter</firstName>\n      <lastName>Bates</lastName>\n      <title>Mr</title>\n      <jobTitle>Human resources benefits</jobTitle>\n      <manager>helen.kelly</manager>\n      <professionalData>\n        <email>walter.bates@acme.com</email>\n        <phoneNumber>484-302-5561</phoneNumber>\n        <faxNumber>484-302-0561</faxNumber>\n        <building>70</building>\n        <address>Renwick Drive</address>\n        <zipCode>19108</zipCode>\n        <city>Philadelphia</city>\n        <state>PA</state>\n        <country>United States</country>\n      </professionalData>\n      \n    </user>\n    <user userName=\"zachary.williamson\">\n      <password encrypted=\"false\">bpm</password>\n      <firstName>Zachary</firstName>\n      <lastName>Williamson</lastName>\n      <title>Mr</title>\n      <jobTitle>Chief Financial Officer</jobTitle>\n      <manager>william.jobs</manager>\n      <professionalData>\n        <email>zachary.williamson@acme.com</email>\n        <phoneNumber>484-302-5921</phoneNumber>\n        <faxNumber>484-302-0921</faxNumber>\n        <building>70</building>\n        <address>Renwick Drive</address>\n        <zipCode>19108</zipCode>\n        <city>Philadelphia</city>\n        <state>PA</state>\n        <country>United States</country>\n      </professionalData>\n      \n    </user>\n    <user userName=\"patrick.gardenier\">\n      <password encrypted=\"false\">bpm</password>\n      <firstName>Patrick</firstName>\n      <lastName>Gardenier</lastName>\n      <title>Mr</title>\n      <jobTitle>Financial controller</jobTitle>\n      <manager>zachary.williamson</manager>\n      <professionalData>\n        <email>patrick.gardenier@acme.com</email>\n        <phoneNumber>484-302-5921</phoneNumber>\n        <faxNumber>484-302-0921</faxNumber>\n        <building>70</building>\n        <address>Renwick Drive</address>\n        <zipCode>19108</zipCode>\n        <city>Philadelphia</city>\n        <state>PA</state>\n        <country>United States</country>\n      </professionalData>\n      \n    </user>\n    <user userName=\"virginie.jomphe\">\n      <password encrypted=\"false\">bpm</password>\n      <firstName>Virgine</firstName>\n      <lastName>Jomphe</lastName>\n      <title>Mrs</title>\n      <jobTitle>Accountant</jobTitle>\n      <manager>zachary.williamson</manager>\n      <professionalData>\n        <email>virginie.jomphe@acme.com</email>\n        <phoneNumber>484-302-5921</phoneNumber>\n        <faxNumber>484-302-0921</faxNumber>\n        <building>70</building>\n        <address>Renwick Drive</address>\n        <zipCode>19108</zipCode>\n        <city>Philadelphia</city>\n        <state>PA</state>\n        <country>United States</country>\n      </professionalData>\n      \n    </user>\n    <user userName=\"thorsten.hartmann\">\n      <password encrypted=\"false\">bpm</password>\n      <firstName>Thorsten</firstName>\n      <lastName>Hartmann</lastName>\n      <title>Mr</title>\n      <jobTitle>Financial planning manager</jobTitle>\n      <manager>zachary.williamson</manager>\n      <professionalData>\n        <email>thorsten.hartmann@acme.com</email>\n        <phoneNumber>484-302-5921</phoneNumber>\n        <faxNumber>484-302-0921</faxNumber>\n        <building>70</building>\n        <address>Renwick Drive</address>\n        <zipCode>19108</zipCode>\n        <city>Philadelphia</city>\n        <state>PA</state>\n        <country>United States</country>\n      </professionalData>\n      \n    </user>\n    <user userName=\"jan.fisher\">\n      <password encrypted=\"false\">bpm</password>\n      <firstName>Jan</firstName>\n      <lastName>Fisher</lastName>\n      <title>Mr</title>\n      <jobTitle>Infrastucture specialist</jobTitle>\n      <manager>favio.riviera</manager>\n      <professionalData>\n        <email>jan.fisher@acme.com</email>\n        <phoneNumber>484-302-5921</phoneNumber>\n        <faxNumber>484-302-0921</faxNumber>\n        <building>70</building>\n        <address>Renwick Drive</address>\n        <zipCode>19108</zipCode>\n        <city>Philadelphia</city>\n        <state>PA</state>\n        <country>United States</country>\n      </professionalData>\n      \n    </user>\n    <user userName=\"isabel.bleasdale\">\n      <password encrypted=\"false\">bpm</password>\n      <firstName>Isabel</firstName>\n      <lastName>Bleasdale</lastName>\n      <title>Mrs</title>\n      <jobTitle>Product marketing manager</jobTitle>\n      <manager>favio.riviera</manager>\n      <professionalData>\n        <email>isabel.bleasdale@acme.com</email>\n        <phoneNumber>484-302-5921</phoneNumber>\n        <faxNumber>484-302-0921</faxNumber>\n        <building>70</building>\n        <address>Renwick Drive</address>\n        <zipCode>19108</zipCode>\n        <city>Philadelphia</city>\n        <state>PA</state>\n        <country>United States</country>\n      </professionalData>\n      \n    </user>\n    <user userName=\"favio.riviera\">\n      <password encrypted=\"false\">bpm</password>\n      <firstName>Favio</firstName>\n      <lastName>Rivera</lastName>\n      <title>Mr</title>\n      <jobTitle>Vice President of Marketing</jobTitle>\n      <manager>william.jobs</manager>\n      <professionalData>\n        <email>favio.riviera@acme.com</email>\n        <phoneNumber>484-302-5921</phoneNumber>\n        <faxNumber>484-302-0921</faxNumber>\n        <building>70</building>\n        <address>Renwick Drive</address>\n        <zipCode>19108</zipCode>\n        <city>Philadelphia</city>\n        <state>PA</state>\n        <country>United States</country>\n      </professionalData>\n      \n    </user>\n    <user userName=\"michael.morrison\">\n      <password encrypted=\"false\">bpm</password>\n      <firstName>Michael</firstName>\n      <lastName>Morrison</lastName>\n      <title>Mr</title>\n      <jobTitle>Chief Technical Officer</jobTitle>\n      <manager>william.jobs</manager>\n      <professionalData>\n        <email>michael.morrison@acme.com</email>\n        <phoneNumber>484-302-5921</phoneNumber>\n        <faxNumber>484-302-0921</faxNumber>\n        <building>70</building>\n        <address>Renwick Drive</address>\n        <zipCode>19108</zipCode>\n        <city>Philadelphia</city>\n        <state>PA</state>\n        <country>United States</country>\n      </professionalData>\n      \n    </user>\n    <user userName=\"marc.marseau\">\n      <password encrypted=\"false\">bpm</password>\n      <firstName>Marc</firstName>\n      <lastName>Marseau</lastName>\n      <title>Mr</title>\n      <jobTitle>Engineer</jobTitle>\n      <manager>michael.morrison</manager>\n      <professionalData>\n        <email>marc.marseau@acme.com</email>\n        <phoneNumber>484-302-5921</phoneNumber>\n        <faxNumber>484-302-0921</faxNumber>\n        <building>70</building>\n        <address>Renwick Drive</address>\n        <zipCode>19108</zipCode>\n        <city>Philadelphia</city>\n        <state>PA</state>\n        <country>United States</country>\n      </professionalData>\n      \n    </user>\n    <user userName=\"joseph.hovell\">\n      <password encrypted=\"false\">bpm</password>\n      <firstName>Joseph</firstName>\n      <lastName>Hovell</lastName>\n      <title>Mr</title>\n      <jobTitle>Engineer</jobTitle>\n      <manager>michael.morrison</manager>\n      <professionalData>\n        <email>joseph.hovell@acme.com</email>\n        <phoneNumber>484-302-5921</phoneNumber>\n        <faxNumber>484-302-0921</faxNumber>\n        <building>70</building>\n        <address>Renwick Drive</address>\n        <zipCode>19108</zipCode>\n        <city>Philadelphia</city>\n        <state>PA</state>\n        <country>United States</country>\n      </professionalData>\n      \n    </user>\n    <user userName=\"mauro.zetticci\">\n      <password encrypted=\"false\">bpm</password>\n      <firstName>Mauro</firstName>\n      <lastName>Zetticci</lastName>\n      <title>Mr</title>\n      <jobTitle>Consultant</jobTitle>\n      <manager>michael.morrison</manager>\n      <professionalData>\n        <email>mauro.zetticci@acme.com</email>\n        <phoneNumber>484-302-5793</phoneNumber>\n        <faxNumber>484-302-0793</faxNumber>\n        <building>70</building>\n        <address>Renwick Drive</address>\n        <zipCode>19108</zipCode>\n        <city>Philadelphia</city>\n        <state>PA</state>\n        <country>United States</country>\n      </professionalData>\n      \n    </user>\n    <user userName=\"thomas.wallis\">\n      <password encrypted=\"false\">bpm</password>\n      <firstName>Thomas</firstName>\n      <lastName>Wallis</lastName>\n      <title>Mr</title>\n      <jobTitle>Consultant</jobTitle>\n      <manager>michael.morrison</manager>\n      <professionalData>\n        <email>thomas.wallis@acme.com</email>\n        <phoneNumber>484-302-5793</phoneNumber>\n        <faxNumber>484-302-0793</faxNumber>\n        <building>70</building>\n        <address>Renwick Drive</address>\n        <zipCode>19108</zipCode>\n        <city>Philadelphia</city>\n        <state>PA</state>\n        <country>United States</country>\n      </professionalData>\n      \n    </user>\n    <user userName=\"daniela.angelo\">\n      <password encrypted=\"false\">bpm</password>\n      <firstName>Daniela</firstName>\n      <lastName>Angelo</lastName>\n      <title>Mrs</title>\n      <jobTitle>Vice President of Sales</jobTitle>\n      <manager>william.jobs</manager>\n      <professionalData>\n        <email>daniela.angelo@acme.com</email>\n        <phoneNumber>484-302-5793</phoneNumber>\n        <faxNumber>484-302-0793</faxNumber>\n        <building>70</building>\n        <address>Renwick Drive</address>\n        <zipCode>19108</zipCode>\n        <city>Philadelphia</city>\n        <state>PA</state>\n        <country>United States</country>\n      </professionalData>\n      \n    </user>\n    <user userName=\"anthony.nichols\">\n      <password encrypted=\"false\">bpm</password>\n      <firstName>Anthony</firstName>\n      <lastName>Nichols</lastName>\n      <title>Mr</title>\n      <jobTitle>Account manager</jobTitle>\n      <manager>daniela.angelo</manager>\n      <professionalData>\n        <email>anthony.nichols@acme.com</email>\n        <phoneNumber>484-302-5793</phoneNumber>\n        <faxNumber>484-302-0793</faxNumber>\n        <building>70</building>\n        <address>Renwick Drive</address>\n        <zipCode>19108</zipCode>\n        <city>Philadelphia</city>\n        <state>PA</state>\n        <country>United States</country>\n      </professionalData>\n      \n    </user>\n    <user userName=\"misa.kumagai\">\n      <password encrypted=\"false\">bpm</password>\n      <firstName>Misa</firstName>\n      <lastName>Kumagai</lastName>\n      <title>Mrs</title>\n      <jobTitle>Account manager</jobTitle>\n      <manager>daniela.angelo</manager>\n      <professionalData>\n        <email>misa.kumagai@acme.com</email>\n        <phoneNumber>484-302-5793</phoneNumber>\n        <faxNumber>484-302-0793</faxNumber>\n        <building>70</building>\n        <address>Renwick Drive</address>\n        <zipCode>19108</zipCode>\n        <city>Philadelphia</city>\n        <state>PA</state>\n        <country>United States</country>\n      </professionalData>\n      \n    </user>\n    <user userName=\"norio.yamazaki\">\n      <password encrypted=\"false\">bpm</password>\n      <firstName>Norio</firstName>\n      <lastName>Yamazaki</lastName>\n      <title>Mr</title>\n      <jobTitle>Account manager</jobTitle>\n      <manager>daniela.angelo</manager>\n      <professionalData>\n        <email>norio.yamazaki@acme.com</email>\n        <phoneNumber>484-302-5793</phoneNumber>\n        <faxNumber>484-302-0793</faxNumber>\n        <building>70</building>\n        <address>Renwick Drive</address>\n        <zipCode>19108</zipCode>\n        <city>Philadelphia</city>\n        <state>PA</state>\n        <country>United States</country>\n      </professionalData>\n      \n    </user>\n    <user userName=\"giovanna.almeida\">\n      <password encrypted=\"false\">bpm</password>\n      <firstName>Giovanna</firstName>\n      <lastName>Almeida</lastName>\n      <title>Mrs</title>\n      <jobTitle>Account manager</jobTitle>\n      <manager>daniela.angelo</manager>\n      <professionalData>\n        <email>giovanna.almeida@acme.com</email>\n        <phoneNumber>484-302-5793</phoneNumber>\n        <faxNumber>484-302-0793</faxNumber>\n        <building>70</building>\n        <address>Renwick Drive</address>\n        <zipCode>19108</zipCode>\n        <city>Philadelphia</city>\n        <state>PA</state>\n        <country>United States</country>\n      </professionalData>\n      \n    </user>\n  </users>\n  <roles>\n    <role name=\"member\">\n      <displayName>Member</displayName>\n      <description></description>\n    </role>\n  </roles>\n  <groups>\n    <group name=\"headquarters\">\n      <displayName>Headquarters</displayName>\n    </group>\n    <group name=\"hr\" parentPath=\"/headquarters\">\n      <displayName>Human Resources</displayName>\n    </group>\n    <group name=\"finance\" parentPath=\"/headquarters\">\n      <displayName>Finance</displayName>\n    </group>\n    <group name=\"it\" parentPath=\"/headquarters\">\n      <displayName>Infrastructure</displayName>\n    </group>\n    <group name=\"marketing\" parentPath=\"/headquarters\">\n      <displayName>Marketing</displayName>\n    </group>\n    <group name=\"production\" parentPath=\"/headquarters\">\n      <displayName>Production</displayName>\n    </group>\n    <group name=\"sales\" parentPath=\"/headquarters\">\n      <displayName>Sales</displayName>\n    </group>\n    <group name=\"europe\" parentPath=\"/headquarters/sales\">\n      <displayName>Europe</displayName>\n    </group>\n    <group name=\"asia\" parentPath=\"/headquarters/sales\">\n      <displayName>Asia</displayName>\n    </group>\n    <group name=\"latin_america\" parentPath=\"/headquarters/sales\">\n      <displayName>Latin America</displayName>\n    </group>\n    <group name=\"north_america\" parentPath=\"/headquarters/sales\">\n      <displayName>North America</displayName>\n    </group>\n    <group name=\"rd\" parentPath=\"/headquarters/production\">\n      <displayName>Research &amp; Development</displayName>\n    </group>\n    <group name=\"services\" parentPath=\"/headquarters/production\">\n      <displayName>Services</displayName>\n    </group>\n  </groups>\n  <memberships>\n    <membership>\n      <userName>william.jobs</userName>\n      <roleName>member</roleName>\n      <groupName>headquarters</groupName>\n    </membership>\n    <membership>\n      <userName>april.sanchez</userName>\n      <roleName>member</roleName>\n      <groupName>hr</groupName>\n      <groupParentPath>/headquarters</groupParentPath>\n    </membership>\n    <membership>\n      <userName>helen.kelly</userName>\n      <roleName>member</roleName>\n      <groupName>hr</groupName>\n      <groupParentPath>/headquarters</groupParentPath>\n    </membership>\n    <membership>\n      <userName>walter.bates</userName>\n      <roleName>member</roleName>\n      <groupName>hr</groupName>\n      <groupParentPath>/headquarters</groupParentPath>\n    </membership>\n    <membership>\n      <userName>zachary.williamson</userName>\n      <roleName>member</roleName>\n      <groupName>finance</groupName>\n      <groupParentPath>/headquarters</groupParentPath>\n    </membership>\n    <membership>\n      <userName>patrick.gardenier</userName>\n      <roleName>member</roleName>\n      <groupName>finance</groupName>\n      <groupParentPath>/headquarters</groupParentPath>\n    </membership>\n    <membership>\n      <userName>virginie.jomphe</userName>\n      <roleName>member</roleName>\n      <groupName>finance</groupName>\n      <groupParentPath>/headquarters</groupParentPath>\n    </membership>\n    <membership>\n      <userName>thorsten.hartmann</userName>\n      <roleName>member</roleName>\n      <groupName>finance</groupName>\n      <groupParentPath>/headquarters</groupParentPath>\n    </membership>\n    <membership>\n      <userName>jan.fisher</userName>\n      <roleName>member</roleName>\n      <groupName>it</groupName>\n      <groupParentPath>/headquarters</groupParentPath>\n    </membership>\n    <membership>\n      <userName>isabel.bleasdale</userName>\n      <roleName>member</roleName>\n      <groupName>marketing</groupName>\n      <groupParentPath>/headquarters</groupParentPath>\n    </membership>\n    <membership>\n      <userName>favio.riviera</userName>\n      <roleName>member</roleName>\n      <groupName>marketing</groupName>\n      <groupParentPath>/headquarters</groupParentPath>\n    </membership>\n    <membership>\n      <userName>michael.morrison</userName>\n      <roleName>member</roleName>\n      <groupName>production</groupName>\n      <groupParentPath>/headquarters</groupParentPath>\n    </membership>\n    <membership>\n      <userName>marc.marseau</userName>\n      <roleName>member</roleName>\n      <groupName>rd</groupName>\n      <groupParentPath>/headquarters/production</groupParentPath>\n    </membership>\n    <membership>\n      <userName>joseph.hovell</userName>\n      <roleName>member</roleName>\n      <groupName>rd</groupName>\n      <groupParentPath>/headquarters/production</groupParentPath>\n    </membership>\n    <membership>\n      <userName>mauro.zetticci</userName>\n      <roleName>member</roleName>\n      <groupName>services</groupName>\n      <groupParentPath>/headquarters/production</groupParentPath>\n    </membership>\n    <membership>\n      <userName>thomas.wallis</userName>\n      <roleName>member</roleName>\n      <groupName>services</groupName>\n      <groupParentPath>/headquarters/production</groupParentPath>\n    </membership>\n    <membership>\n      <userName>daniela.angelo</userName>\n      <roleName>member</roleName>\n      <groupName>europe</groupName>\n      <groupParentPath>/headquarters/sales</groupParentPath>\n    </membership>\n    <membership>\n      <userName>misa.kumagai</userName>\n      <roleName>member</roleName>\n      <groupName>asia</groupName>\n      <groupParentPath>/headquarters/sales</groupParentPath>\n    </membership>\n    <membership>\n      <userName>norio.yamazaki</userName>\n      <roleName>member</roleName>\n      <groupName>asia</groupName>\n      <groupParentPath>/headquarters/sales</groupParentPath>\n    </membership>\n    <membership>\n      <userName>giovanna.almeida</userName>\n      <roleName>member</roleName>\n      <groupName>latin_america</groupName>\n      <groupParentPath>/headquarters/sales</groupParentPath>\n    </membership>\n    <membership>\n      <userName>anthony.nichols</userName>\n      <roleName>member</roleName>\n      <groupName>north_america</groupName>\n      <groupParentPath>/headquarters/sales</groupParentPath>\n    </membership>\n  </memberships>\n</organization:Organization>"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/identity/OrganizationWithSpecialCharacters.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<organization:Organization\n\txmlns:organization=\"http://documentation.bonitasoft.com/organization-xml-schema\">\n\t<users>\n\t\t<user userName=\"ééééé\">\n\t\t\t<password encrypted=\"false\">é</password>\n\t\t\t<firstName>éé</firstName>\n\t\t\t<lastName>ééééééééééééééééééééééééé</lastName>\n\t\t\t<enabled>false</enabled>\n\t\t\t<personalData />\n\t\t\t<professionalData />\n\t\t</user>\n\t</users>\n\t<roles>\n\t\t<role name=\"éééééééé\">\n\t\t\t<displayName>ééé</displayName>\n\t\t\t<description>éééé</description>\n\t\t</role>\n\t</roles>\n\t<groups>\n\t\t<group name=\"éééééééééééééééééééé\" parentPath=\"ééééééééééééééé\">\n\t\t\t<displayName>ééééééééé</displayName>\n\t\t</group>\n\t</groups>\n\t<memberships />\n</organization:Organization>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/identity/complexOrganization.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<organization:Organization xmlns:organization=\"http://documentation.bonitasoft.com/organization-xml-schema\">\n\t<users>\n\t\t<user userName=\"john\">\n\t\t\t<password encrypted=\"false\">bpm</password>\n\t\t\t<firstName>John</firstName>\n\t\t\t<lastName>Doe</lastName>\n\t\t\t<iconName>john.png</iconName>\n\t\t\t<iconPath>/icons/</iconPath>\n\t\t\t<title>M</title>\n\t\t\t<manager>jack</manager>\n\t\t\t<jobTitle>Web Team Manager</jobTitle>\n\t\t\t<personalData>\n\t\t\t\t<email>emailValue</email>\n\t\t\t\t<phoneNumber>phoneNumberValue</phoneNumber>\n\t\t\t\t<mobileNumber>mobileNumberValue</mobileNumber>\n\t\t\t\t<faxNumber>faxNumberValue</faxNumber>\n\t\t\t\t<building>buildingValue</building>\n\t\t\t\t<room>roomValue</room>\n\t\t\t\t<address>addressValue</address>\n\t\t\t\t<zipCode>zipCodeValue</zipCode>\n\t\t\t\t<city>cityValue</city>\n\t\t\t\t<state>stateValue</state>\n\t\t\t\t<country>countryValue</country>\n\t\t\t\t<website>websiteValue</website>\n\t\t\t</personalData>\n\t\t\t<professionalData>\n\t\t\t\t<email>emailProfessionalValue</email>\n\t\t\t\t<phoneNumber>phoneNumberProfessionalValue</phoneNumber>\n\t\t\t\t<mobileNumber>mobileNumberProfessionalValue</mobileNumber>\n\t\t\t\t<faxNumber>faxNumberProfessionalValue</faxNumber>\n\t\t\t\t<building>buildingProfessionalValue</building>\n\t\t\t\t<room>roomProfessionalValue</room>\n\t\t\t\t<address>addressProfessionalValue</address>\n\t\t\t\t<zipCode>zipCodeProfessionalValue</zipCode>\n\t\t\t\t<city>cityProfessionalValue</city>\n\t\t\t\t<state>stateProfessionalValue</state>\n\t\t\t\t<country>countryProfessionalValue</country>\n\t\t\t\t<website>websiteProfessionalValue</website>\n\t\t\t</professionalData>\n\t\t</user>\n\t\t<user userName=\"jack\">\n\t\t\t<password encrypted=\"false\">bpm</password>\n\t\t\t<jobTitle>Web Team Manager</jobTitle>\n\t\t\t<personalData />\n\t\t\t<professionalData />\n\t\t</user>\n\t\t<user userName=\"james\">\n\t\t\t<password encrypted=\"false\">bpm</password>\n\t\t\t<personalData />\n\t\t\t<professionalData />\n\t\t</user>\n\t</users>\n\t<roles>\n\t\t<role name=\"Developer\">\n\t\t\t<displayName>Bonita developer</displayName>\n\t\t</role>\n\t\t<role name=\"Manager\">\n\t\t\t<displayName>Bonita Manager</displayName>\n\t\t</role>\n\t</roles>\n\t<groups>\n\t\t<group name=\"BonitaSoft\">\n\t\t\t<displayName>Bonitasoft S.A.</displayName>\n\t\t\t<description>The best BPM compagny</description>\n\t\t</group>\n\t\t<group name=\"RD\" parentPath=\"/BonitaSoft\">\n\t\t\t<displayName>R&amp;D team</displayName>\n\t\t\t<description>All Rd of BonitaSoft</description>\n\t\t</group>\n\t\t<group name=\"RD\">\n\t\t\t<displayName>R&amp;D team</displayName>\n\t\t\t<description>All Rd of all compagnies</description>\n\t\t</group>\n\t\t<group name=\"Support\" parentPath=\"/BonitaSoft\">\n\t\t\t<displayName>Support team</displayName>\n\t\t</group>\n\t</groups>\n\t<memberships>\n\t\t<membership>\n\t\t\t<userName>john</userName>\n\t\t\t<roleName>Developer</roleName>\n\t\t\t<groupName>RD</groupName>\n\t\t\t<groupParentPath>/BonitaSoft</groupParentPath>\n\t\t\t<assignedBy>jack</assignedBy>\n\t\t\t<assignedDate>1331142448365</assignedDate>\n\t\t</membership>\n\t\t<membership>\n\t\t\t<userName>jack</userName>\n\t\t\t<roleName>Developer</roleName>\n\t\t\t<groupName>RD</groupName>\n\t\t</membership>\n\t</memberships>\n</organization:Organization>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/identity/complexOrganizationWithBadGroup.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<organization:Organization xmlns:organization=\"http://documentation.bonitasoft.com/organization-xml-schema\">\n<users>\n    <user userName=\"john\">\n        <password encrypted=\"false\">bpm</password>\n        <firstName>John</firstName>\n        <lastName>Doe</lastName>\n        <iconName>john.png</iconName>\n        <iconPath>/icons/</iconPath>\n        <title>M</title>\n        <manager>jack</manager>\n        <jobTitle>Web Team Manager</jobTitle>\n        <personalData>\n            <email>emailValue</email>\n            <phoneNumber>phoneNumberValue</phoneNumber>\n            <mobileNumber>mobileNumberValue</mobileNumber>\n            <faxNumber>faxNumberValue</faxNumber>\n            <building>buildingValue</building>\n            <room>roomValue</room>\n            <address>addressValue</address>\n            <zipCode>zipCodeValue</zipCode>\n            <city>cityValue</city>\n            <state>stateValue</state>\n            <country>countryValue</country>\n            <website>websiteValue</website>\n        </personalData>\n        <professionalData>\n            <email>emailProfessionalValue</email>\n            <phoneNumber>phoneNumberProfessionalValue</phoneNumber>\n            <mobileNumber>mobileNumberProfessionalValue</mobileNumber>\n            <faxNumber>faxNumberProfessionalValue</faxNumber>\n            <building>buildingProfessionalValue</building>\n            <room>roomProfessionalValue</room>\n            <address>addressProfessionalValue</address>\n            <zipCode>zipCodeProfessionalValue</zipCode>\n            <city>cityProfessionalValue</city>\n            <state>stateProfessionalValue</state>\n            <country>countryProfessionalValue</country>\n            <website>websiteProfessionalValue</website>\n        </professionalData>\n    </user>\n    <user userName=\"jack\">\n        <password encrypted=\"false\">bpm</password>\n        <jobTitle>Web Team Manager</jobTitle>\n        <personalData />\n        <professionalData />\n    </user>\n    <user userName=\"james\">\n        <password encrypted=\"false\">bpm</password>\n        <personalData />\n        <professionalData />\n    </user>\n</users>\n<roles>\n    <role name=\"Developer\">\n        <displayName>Bonita developer</displayName>\n    </role>\n    <role name=\"Manager\">\n        <displayName>Bonita Manager</displayName>\n    </role>\n</roles>\n<groups>\n    <group name=\"BonitaSoft\">\n        <displayName>Bonitasoft S.A.</displayName>\n        <description>The best BPM compagny</description>\n    </group>\n    <group name=\"RD\" parentPath=\"/BonitaSoft\">\n        <displayName>R&amp;D team</displayName>\n        <description>All Rd of BonitaSoft</description>\n    </group>\n    <group name=\"RD/Studio\">\n        <displayName>R&amp;D team</displayName>\n        <description>All Rd of all compagnies</description>\n    </group>\n    <group name=\"Support\" parentPath=\"/BonitaSoft\">\n        <displayName>Support team</displayName>\n    </group>\n</groups>\n<memberships>\n    <membership>\n        <userName>john</userName>\n        <roleName>Developer</roleName>\n        <groupName>RD</groupName>\n        <groupParentPath>/BonitaSoft</groupParentPath>\n        <assignedBy>jack</assignedBy>\n        <assignedDate>1331142448365</assignedDate>\n    </membership>\n    <membership>\n        <userName>jack</userName>\n        <roleName>Developer</roleName>\n        <groupName>RD</groupName>\n    </membership>\n</memberships>\n</organization:Organization>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/identity/hugeOrganization.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<organization:Organization xmlns:organization=\"http://documentation.bonitasoft.com/organization-xml-schema\">\n  <users>\n    <user userName=\"john0\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john0.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john0.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john1\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john1.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john1.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john10\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john10.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john10.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john100\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john100.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john100.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john101\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john101.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john101.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john102\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john102.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john102.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john103\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john103.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john103.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john104\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john104.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john104.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john105\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john105.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john105.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john106\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john106.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john106.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john107\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john107.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john107.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john108\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john108.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john108.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john109\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john109.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john109.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john11\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john11.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john11.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john110\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john110.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john110.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john111\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john111.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john111.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john112\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john112.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john112.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john113\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john113.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john113.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john114\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john114.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john114.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john115\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john115.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john115.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john116\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john116.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john116.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john117\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john117.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john117.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john118\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john118.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john118.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john119\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john119.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john119.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john12\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john12.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john12.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john120\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john120.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john120.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john121\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john121.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john121.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john122\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john122.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john122.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john123\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john123.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john123.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john124\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john124.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john124.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john125\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john125.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john125.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john126\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john126.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john126.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john127\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john127.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john127.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john128\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john128.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john128.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john129\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john129.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john129.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john13\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john13.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john13.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john130\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john130.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john130.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john131\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john131.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john131.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john132\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john132.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john132.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john133\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john133.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john133.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john134\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john134.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john134.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john135\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john135.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john135.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john136\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john136.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john136.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john137\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john137.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john137.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john138\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john138.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john138.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john139\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john139.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john139.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john14\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john14.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john14.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john140\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john140.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john140.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john141\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john141.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john141.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john142\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john142.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john142.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john143\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john143.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john143.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john144\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john144.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john144.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john145\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john145.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john145.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john146\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john146.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john146.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john147\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john147.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john147.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john148\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john148.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john148.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john149\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john149.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john149.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john15\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john15.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john15.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john150\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john150.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john150.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john151\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john151.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john151.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john152\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john152.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john152.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john153\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john153.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john153.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john154\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john154.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john154.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john155\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john155.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john155.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john156\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john156.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john156.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john157\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john157.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john157.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john158\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john158.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john158.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john159\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john159.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john159.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john16\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john16.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john16.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john160\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john160.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john160.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john161\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john161.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john161.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john162\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john162.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john162.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john163\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john163.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john163.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john164\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john164.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john164.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john165\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john165.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john165.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john166\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john166.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john166.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john167\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john167.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john167.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john168\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john168.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john168.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john169\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john169.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john169.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john17\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john17.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john17.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john170\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john170.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john170.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john171\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john171.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john171.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john172\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john172.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john172.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john173\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john173.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john173.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john174\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john174.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john174.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john175\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john175.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john175.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john176\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john176.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john176.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john177\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john177.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john177.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john178\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john178.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john178.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john179\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john179.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john179.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john18\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john18.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john18.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john180\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john180.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john180.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john181\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john181.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john181.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john182\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john182.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john182.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john183\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john183.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john183.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john184\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john184.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john184.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john185\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john185.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john185.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john186\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john186.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john186.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john187\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john187.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john187.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john188\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john188.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john188.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john189\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john189.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john189.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john19\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john19.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john19.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john190\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john190.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john190.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john191\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john191.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john191.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john192\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john192.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john192.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john193\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john193.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john193.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john194\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john194.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john194.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john195\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john195.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john195.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john196\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john196.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john196.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john197\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john197.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john197.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john198\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john198.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john198.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john199\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john199.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john199.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john2\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john2.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john2.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john20\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john20.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john20.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john200\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john200.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john200.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john201\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john201.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john201.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john202\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john202.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john202.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john203\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john203.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john203.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john204\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john204.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john204.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john205\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john205.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john205.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john206\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john206.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john206.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john207\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john207.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john207.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john208\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john208.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john208.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john209\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john209.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john209.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john21\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john21.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john21.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john210\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john210.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john210.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john211\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john211.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john211.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john212\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john212.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john212.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john213\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john213.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john213.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john214\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john214.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john214.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john215\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john215.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john215.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john216\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john216.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john216.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john217\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john217.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john217.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john218\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john218.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john218.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john219\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john219.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john219.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john22\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john22.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john22.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john220\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john220.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john220.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john221\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john221.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john221.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john222\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john222.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john222.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john223\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john223.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john223.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john224\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john224.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john224.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john225\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john225.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john225.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john226\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john226.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john226.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john227\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john227.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john227.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john228\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john228.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john228.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john229\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john229.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john229.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john23\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john23.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john23.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john230\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john230.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john230.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john231\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john231.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john231.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john232\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john232.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john232.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john233\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john233.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john233.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john234\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john234.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john234.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john235\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john235.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john235.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john236\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john236.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john236.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john237\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john237.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john237.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john238\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john238.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john238.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john239\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john239.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john239.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john24\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john24.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john24.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john240\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john240.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john240.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john241\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john241.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john241.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john242\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john242.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john242.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john243\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john243.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john243.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john244\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john244.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john244.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john245\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john245.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john245.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john246\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john246.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john246.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john247\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john247.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john247.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john248\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john248.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john248.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john249\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john249.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john249.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john25\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john25.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john25.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john250\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john250.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john250.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john251\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john251.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john251.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john252\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john252.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john252.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john253\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john253.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john253.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john254\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john254.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john254.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john255\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john255.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john255.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john256\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john256.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john256.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john257\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john257.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john257.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john258\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john258.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john258.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john259\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john259.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john259.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john26\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john26.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john26.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john260\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john260.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john260.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john261\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john261.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john261.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john262\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john262.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john262.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john263\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john263.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john263.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john264\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john264.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john264.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john265\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john265.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john265.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john266\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john266.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john266.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john267\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john267.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john267.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john268\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john268.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john268.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john269\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john269.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john269.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john27\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john27.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john27.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john270\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john270.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john270.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john271\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john271.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john271.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john272\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john272.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john272.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john273\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john273.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john273.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john274\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john274.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john274.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john275\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john275.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john275.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john276\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john276.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john276.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john277\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john277.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john277.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john278\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john278.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john278.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john279\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john279.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john279.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john28\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john28.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john28.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john280\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john280.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john280.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john281\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john281.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john281.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john282\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john282.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john282.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john283\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john283.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john283.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john284\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john284.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john284.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john285\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john285.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john285.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john286\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john286.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john286.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john287\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john287.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john287.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john288\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john288.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john288.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john289\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john289.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john289.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john29\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john29.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john29.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john290\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john290.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john290.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john291\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john291.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john291.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john292\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john292.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john292.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john293\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john293.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john293.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john294\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john294.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john294.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john295\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john295.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john295.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john296\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john296.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john296.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john297\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john297.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john297.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john298\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john298.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john298.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john299\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john299.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john299.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john3\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john3.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john3.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john30\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john30.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john30.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john300\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john300.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john300.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john301\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john301.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john301.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john302\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john302.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john302.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john303\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john303.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john303.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john304\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john304.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john304.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john305\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john305.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john305.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john306\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john306.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john306.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john307\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john307.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john307.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john308\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john308.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john308.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john309\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john309.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john309.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john31\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john31.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john31.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john310\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john310.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john310.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john311\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john311.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john311.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john312\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john312.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john312.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john313\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john313.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john313.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john314\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john314.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john314.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john315\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john315.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john315.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john316\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john316.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john316.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john317\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john317.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john317.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john318\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john318.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john318.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john319\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john319.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john319.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john32\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john32.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john32.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john320\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john320.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john320.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john321\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john321.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john321.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john322\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john322.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john322.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john323\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john323.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john323.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john324\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john324.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john324.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john325\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john325.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john325.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john326\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john326.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john326.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john327\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john327.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john327.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john328\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john328.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john328.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john329\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john329.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john329.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john33\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john33.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john33.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john330\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john330.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john330.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john331\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john331.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john331.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john332\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john332.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john332.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john333\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john333.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john333.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john334\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john334.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john334.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john335\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john335.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john335.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john336\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john336.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john336.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john337\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john337.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john337.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john338\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john338.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john338.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john339\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john339.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john339.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john34\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john34.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john34.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john340\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john340.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john340.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john341\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john341.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john341.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john342\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john342.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john342.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john343\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john343.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john343.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john344\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john344.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john344.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john345\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john345.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john345.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john346\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john346.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john346.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john347\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john347.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john347.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john348\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john348.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john348.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john349\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john349.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john349.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john35\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john35.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john35.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john350\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john350.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john350.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john351\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john351.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john351.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john352\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john352.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john352.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john353\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john353.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john353.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john354\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john354.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john354.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john355\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john355.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john355.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john356\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john356.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john356.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john357\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john357.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john357.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john358\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john358.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john358.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john359\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john359.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john359.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john36\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john36.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john36.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john360\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john360.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john360.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john361\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john361.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john361.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john362\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john362.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john362.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john363\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john363.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john363.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john364\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john364.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john364.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john365\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john365.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john365.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john366\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john366.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john366.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john367\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john367.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john367.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john368\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john368.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john368.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john369\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john369.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john369.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john37\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john37.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john37.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john370\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john370.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john370.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john371\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john371.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john371.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john372\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john372.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john372.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john373\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john373.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john373.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john374\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john374.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john374.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john375\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john375.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john375.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john376\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john376.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john376.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john377\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john377.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john377.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john378\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john378.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john378.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john379\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john379.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john379.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john38\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john38.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john38.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john380\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john380.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john380.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john381\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john381.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john381.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john382\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john382.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john382.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john383\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john383.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john383.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john384\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john384.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john384.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john385\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john385.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john385.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john386\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john386.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john386.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john387\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john387.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john387.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john388\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john388.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john388.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john389\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john389.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john389.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john39\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john39.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john39.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john390\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john390.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john390.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john391\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john391.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john391.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john392\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john392.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john392.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john393\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john393.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john393.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john394\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john394.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john394.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john395\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john395.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john395.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john396\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john396.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john396.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john397\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john397.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john397.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john398\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john398.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john398.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john399\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john399.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john399.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john4\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john4.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john4.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john40\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john40.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john40.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john400\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john400.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john400.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john401\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john401.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john401.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john402\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john402.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john402.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john403\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john403.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john403.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john404\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john404.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john404.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john405\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john405.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john405.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john406\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john406.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john406.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john407\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john407.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john407.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john408\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john408.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john408.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john409\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john409.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john409.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john41\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john41.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john41.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john410\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john410.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john410.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john411\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john411.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john411.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john412\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john412.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john412.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john413\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john413.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john413.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john414\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john414.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john414.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john415\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john415.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john415.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john416\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john416.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john416.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john417\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john417.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john417.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john418\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john418.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john418.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john419\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john419.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john419.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john42\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john42.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john42.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john420\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john420.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john420.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john421\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john421.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john421.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john422\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john422.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john422.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john423\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john423.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john423.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john424\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john424.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john424.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john425\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john425.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john425.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john426\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john426.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john426.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john427\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john427.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john427.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john428\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john428.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john428.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john429\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john429.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john429.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john43\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john43.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john43.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john430\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john430.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john430.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john431\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john431.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john431.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john432\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john432.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john432.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john433\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john433.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john433.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john434\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john434.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john434.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john435\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john435.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john435.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john436\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john436.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john436.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john437\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john437.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john437.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john438\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john438.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john438.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john439\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john439.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john439.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john44\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john44.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john44.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john440\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john440.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john440.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john441\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john441.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john441.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john442\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john442.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john442.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john443\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john443.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john443.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john444\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john444.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john444.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john445\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john445.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john445.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john446\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john446.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john446.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john447\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john447.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john447.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john448\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john448.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john448.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john449\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john449.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john449.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john45\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john45.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john45.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john450\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john450.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john450.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john451\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john451.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john451.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john452\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john452.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john452.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john453\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john453.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john453.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john454\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john454.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john454.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john455\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john455.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john455.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john456\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john456.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john456.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john457\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john457.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john457.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john458\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john458.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john458.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john459\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john459.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john459.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john46\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john46.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john46.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john460\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john460.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john460.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john461\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john461.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john461.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john462\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john462.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john462.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john463\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john463.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john463.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john464\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john464.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john464.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john465\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john465.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john465.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john466\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john466.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john466.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john467\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john467.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john467.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john468\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john468.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john468.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john469\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john469.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john469.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john47\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john47.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john47.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john470\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john470.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john470.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john471\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john471.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john471.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john472\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john472.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john472.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john473\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john473.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john473.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john474\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john474.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john474.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john475\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john475.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john475.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john476\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john476.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john476.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john477\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john477.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john477.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john478\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john478.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john478.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john479\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john479.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john479.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john48\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john48.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john48.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john480\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john480.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john480.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john481\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john481.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john481.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john482\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john482.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john482.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john483\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john483.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john483.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john484\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john484.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john484.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john485\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john485.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john485.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john486\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john486.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john486.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john487\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john487.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john487.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john488\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john488.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john488.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john489\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john489.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john489.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john49\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john49.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john49.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john490\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john490.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john490.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john491\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john491.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john491.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john492\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john492.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john492.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john493\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john493.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john493.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john494\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john494.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john494.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john495\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john495.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john495.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john496\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john496.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john496.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john497\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john497.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john497.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john498\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john498.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john498.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john499\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john499.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john499.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john5\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john5.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john5.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john50\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john50.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john50.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john500\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john500.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john500.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john501\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john501.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john501.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john502\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john502.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john502.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john503\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john503.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john503.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john504\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john504.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john504.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john505\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john505.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john505.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john506\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john506.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john506.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john507\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john507.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john507.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john508\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john508.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john508.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john509\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john509.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john509.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john51\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john51.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john51.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john510\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john510.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john510.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john511\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john511.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john511.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john512\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john512.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john512.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john513\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john513.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john513.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john514\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john514.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john514.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john515\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john515.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john515.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john516\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john516.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john516.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john517\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john517.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john517.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john518\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john518.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john518.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john519\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john519.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john519.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john52\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john52.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john52.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john520\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john520.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john520.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john521\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john521.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john521.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john522\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john522.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john522.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john523\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john523.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john523.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john524\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john524.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john524.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john525\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john525.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john525.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john526\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john526.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john526.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john527\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john527.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john527.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john528\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john528.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john528.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john529\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john529.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john529.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john53\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john53.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john53.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john530\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john530.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john530.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john531\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john531.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john531.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john532\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john532.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john532.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john533\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john533.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john533.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john534\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john534.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john534.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john535\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john535.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john535.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john536\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john536.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john536.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john537\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john537.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john537.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john538\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john538.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john538.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john539\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john539.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john539.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john54\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john54.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john54.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john540\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john540.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john540.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john541\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john541.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john541.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john542\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john542.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john542.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john543\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john543.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john543.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john544\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john544.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john544.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john545\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john545.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john545.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john546\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john546.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john546.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john547\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john547.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john547.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john548\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john548.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john548.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john549\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john549.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john549.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john55\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john55.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john55.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john550\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john550.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john550.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john551\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john551.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john551.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john552\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john552.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john552.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john553\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john553.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john553.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john554\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john554.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john554.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john555\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john555.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john555.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john556\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john556.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john556.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john557\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john557.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john557.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john558\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john558.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john558.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john559\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john559.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john559.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john56\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john56.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john56.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john560\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john560.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john560.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john561\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john561.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john561.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john562\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john562.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john562.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john563\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john563.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john563.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john564\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john564.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john564.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john565\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john565.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john565.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john566\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john566.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john566.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john567\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john567.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john567.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john568\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john568.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john568.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john569\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john569.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john569.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john57\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john57.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john57.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john570\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john570.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john570.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john571\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john571.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john571.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john572\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john572.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john572.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john573\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john573.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john573.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john574\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john574.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john574.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john575\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john575.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john575.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john576\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john576.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john576.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john577\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john577.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john577.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john578\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john578.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john578.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john579\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john579.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john579.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john58\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john58.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john58.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john580\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john580.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john580.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john581\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john581.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john581.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john582\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john582.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john582.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john583\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john583.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john583.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john584\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john584.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john584.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john585\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john585.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john585.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john586\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john586.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john586.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john587\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john587.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john587.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john588\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john588.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john588.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john589\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john589.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john589.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john59\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john59.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john59.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john590\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john590.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john590.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john591\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john591.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john591.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john592\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john592.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john592.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john593\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john593.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john593.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john594\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john594.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john594.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john595\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john595.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john595.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john596\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john596.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john596.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john597\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john597.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john597.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john598\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john598.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john598.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john599\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john599.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john599.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john6\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john6.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john6.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john60\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john60.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john60.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john600\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john600.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john600.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john601\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john601.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john601.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john602\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john602.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john602.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john603\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john603.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john603.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john604\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john604.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john604.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john605\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john605.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john605.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john606\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john606.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john606.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john607\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john607.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john607.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john608\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john608.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john608.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john609\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john609.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john609.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john61\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john61.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john61.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john610\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john610.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john610.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john611\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john611.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john611.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john612\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john612.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john612.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john613\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john613.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john613.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john614\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john614.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john614.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john615\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john615.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john615.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john616\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john616.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john616.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john617\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john617.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john617.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john618\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john618.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john618.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john619\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john619.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john619.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john62\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john62.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john62.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john620\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john620.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john620.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john621\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john621.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john621.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john622\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john622.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john622.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john623\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john623.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john623.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john624\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john624.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john624.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john625\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john625.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john625.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john626\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john626.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john626.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john627\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john627.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john627.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john628\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john628.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john628.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john629\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john629.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john629.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john63\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john63.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john63.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john630\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john630.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john630.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john631\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john631.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john631.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john632\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john632.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john632.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john633\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john633.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john633.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john634\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john634.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john634.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john635\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john635.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john635.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john636\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john636.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john636.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john637\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john637.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john637.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john638\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john638.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john638.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john639\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john639.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john639.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john64\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john64.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john64.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john640\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john640.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john640.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john641\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john641.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john641.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john642\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john642.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john642.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john643\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john643.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john643.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john644\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john644.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john644.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john645\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john645.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john645.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john646\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john646.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john646.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john647\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john647.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john647.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john648\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john648.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john648.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john649\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john649.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john649.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john65\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john65.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john65.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john650\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john650.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john650.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john651\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john651.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john651.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john652\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john652.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john652.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john653\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john653.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john653.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john654\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john654.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john654.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john655\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john655.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john655.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john656\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john656.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john656.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john657\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john657.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john657.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john658\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john658.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john658.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john659\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john659.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john659.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john66\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john66.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john66.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john660\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john660.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john660.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john661\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john661.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john661.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john662\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john662.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john662.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john663\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john663.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john663.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john664\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john664.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john664.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john665\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john665.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john665.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john666\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john666.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john666.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john667\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john667.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john667.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john668\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john668.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john668.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john669\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john669.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john669.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john67\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john67.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john67.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john670\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john670.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john670.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john671\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john671.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john671.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john672\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john672.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john672.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john673\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john673.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john673.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john674\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john674.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john674.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john675\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john675.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john675.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john676\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john676.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john676.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john677\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john677.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john677.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john678\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john678.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john678.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john679\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john679.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john679.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john68\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john68.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john68.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john680\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john680.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john680.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john681\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john681.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john681.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john682\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john682.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john682.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john683\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john683.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john683.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john684\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john684.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john684.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john685\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john685.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john685.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john686\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john686.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john686.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john687\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john687.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john687.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john688\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john688.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john688.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john689\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john689.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john689.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john69\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john69.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john69.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john690\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john690.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john690.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john691\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john691.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john691.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john692\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john692.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john692.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john693\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john693.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john693.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john694\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john694.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john694.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john695\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john695.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john695.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john696\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john696.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john696.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john697\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john697.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john697.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john698\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john698.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john698.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john699\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john699.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john699.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john7\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john7.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john7.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john70\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john70.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john70.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john700\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john700.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john700.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john701\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john701.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john701.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john702\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john702.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john702.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john703\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john703.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john703.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john704\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john704.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john704.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john705\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john705.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john705.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john706\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john706.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john706.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john707\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john707.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john707.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john708\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john708.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john708.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john709\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john709.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john709.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john71\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john71.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john71.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john710\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john710.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john710.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john711\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john711.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john711.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john712\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john712.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john712.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john713\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john713.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john713.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john714\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john714.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john714.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john715\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john715.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john715.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john716\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john716.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john716.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john717\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john717.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john717.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john718\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john718.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john718.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john719\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john719.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john719.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john72\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john72.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john72.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john720\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john720.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john720.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john721\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john721.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john721.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john722\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john722.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john722.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john723\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john723.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john723.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john724\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john724.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john724.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john725\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john725.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john725.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john726\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john726.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john726.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john727\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john727.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john727.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john728\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john728.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john728.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john729\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john729.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john729.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john73\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john73.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john73.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john730\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john730.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john730.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john731\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john731.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john731.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john732\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john732.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john732.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john733\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john733.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john733.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john734\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john734.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john734.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john735\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john735.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john735.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john736\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john736.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john736.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john737\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john737.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john737.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john738\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john738.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john738.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john739\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john739.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john739.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john74\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john74.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john74.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john740\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john740.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john740.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john741\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john741.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john741.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john742\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john742.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john742.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john743\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john743.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john743.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john744\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john744.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john744.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john745\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john745.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john745.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john746\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john746.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john746.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john747\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john747.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john747.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john748\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john748.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john748.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john749\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john749.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john749.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john75\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john75.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john75.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john750\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john750.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john750.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john751\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john751.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john751.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john752\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john752.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john752.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john753\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john753.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john753.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john754\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john754.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john754.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john755\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john755.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john755.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john756\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john756.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john756.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john757\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john757.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john757.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john758\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john758.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john758.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john759\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john759.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john759.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john76\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john76.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john76.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john760\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john760.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john760.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john761\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john761.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john761.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john762\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john762.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john762.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john763\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john763.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john763.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john764\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john764.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john764.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john765\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john765.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john765.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john766\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john766.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john766.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john767\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john767.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john767.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john768\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john768.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john768.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john769\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john769.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john769.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john77\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john77.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john77.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john770\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john770.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john770.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john771\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john771.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john771.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john772\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john772.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john772.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john773\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john773.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john773.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john774\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john774.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john774.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john775\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john775.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john775.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john776\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john776.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john776.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john777\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john777.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john777.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john778\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john778.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john778.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john779\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john779.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john779.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john78\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john78.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john78.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john780\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john780.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john780.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john781\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john781.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john781.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john782\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john782.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john782.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john783\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john783.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john783.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john784\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john784.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john784.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john785\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john785.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john785.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john786\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john786.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john786.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john787\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john787.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john787.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john788\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john788.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john788.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john789\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john789.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john789.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john79\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john79.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john79.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john790\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john790.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john790.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john791\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john791.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john791.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john792\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john792.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john792.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john793\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john793.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john793.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john794\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john794.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john794.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john795\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john795.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john795.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john796\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john796.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john796.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john797\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john797.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john797.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john798\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john798.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john798.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john799\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john799.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john799.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john8\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john8.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john8.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john80\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john80.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john80.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john800\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john800.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john800.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john801\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john801.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john801.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john802\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john802.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john802.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john803\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john803.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john803.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john804\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john804.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john804.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john805\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john805.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john805.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john806\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john806.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john806.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john807\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john807.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john807.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john808\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john808.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john808.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john809\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john809.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john809.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john81\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john81.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john81.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john810\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john810.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john810.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john811\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john811.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john811.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john812\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john812.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john812.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john813\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john813.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john813.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john814\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john814.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john814.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john815\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john815.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john815.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john816\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john816.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john816.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john817\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john817.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john817.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john818\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john818.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john818.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john819\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john819.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john819.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john82\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john82.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john82.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john820\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john820.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john820.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john821\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john821.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john821.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john822\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john822.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john822.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john823\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john823.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john823.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john824\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john824.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john824.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john825\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john825.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john825.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john826\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john826.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john826.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john827\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john827.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john827.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john828\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john828.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john828.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john829\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john829.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john829.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john83\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john83.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john83.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john830\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john830.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john830.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john831\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john831.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john831.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john832\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john832.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john832.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john833\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john833.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john833.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john834\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john834.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john834.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john835\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john835.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john835.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john836\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john836.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john836.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john837\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john837.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john837.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john838\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john838.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john838.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john839\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john839.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john839.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john84\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john84.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john84.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john840\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john840.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john840.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john841\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john841.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john841.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john842\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john842.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john842.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john843\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john843.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john843.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john844\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john844.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john844.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john845\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john845.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john845.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john846\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john846.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john846.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john847\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john847.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john847.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john848\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john848.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john848.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john849\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john849.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john849.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john85\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john85.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john85.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john850\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john850.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john850.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john851\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john851.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john851.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john852\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john852.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john852.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john853\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john853.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john853.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john854\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john854.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john854.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john855\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john855.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john855.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john856\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john856.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john856.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john857\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john857.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john857.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john858\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john858.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john858.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john859\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john859.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john859.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john86\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john86.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john86.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john860\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john860.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john860.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john861\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john861.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john861.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john862\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john862.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john862.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john863\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john863.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john863.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john864\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john864.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john864.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john865\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john865.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john865.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john866\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john866.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john866.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john867\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john867.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john867.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john868\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john868.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john868.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john869\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john869.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john869.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john87\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john87.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john87.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john870\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john870.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john870.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john871\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john871.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john871.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john872\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john872.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john872.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john873\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john873.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john873.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john874\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john874.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john874.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john875\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john875.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john875.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john876\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john876.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john876.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john877\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john877.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john877.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john878\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john878.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john878.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john879\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john879.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john879.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john88\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john88.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john88.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john880\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john880.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john880.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john881\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john881.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john881.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john882\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john882.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john882.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john883\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john883.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john883.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john884\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john884.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john884.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john885\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john885.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john885.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john886\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john886.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john886.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john887\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john887.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john887.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john888\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john888.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john888.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john889\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john889.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john889.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john89\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john89.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john89.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john890\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john890.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john890.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john891\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john891.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john891.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john892\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john892.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john892.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john893\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john893.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john893.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john894\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john894.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john894.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john895\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john895.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john895.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john896\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john896.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john896.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john897\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john897.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john897.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john898\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john898.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john898.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john899\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john899.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john899.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john9\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john9.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john9.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john90\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john90.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john90.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john900\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john900.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john900.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john901\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john901.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john901.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john902\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john902.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john902.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john903\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john903.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john903.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john904\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john904.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john904.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john905\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john905.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john905.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john906\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john906.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john906.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john907\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john907.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john907.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john908\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john908.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john908.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john909\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john909.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john909.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john91\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john91.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john91.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john910\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john910.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john910.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john911\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john911.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john911.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john912\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john912.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john912.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john913\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john913.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john913.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john914\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john914.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john914.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john915\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john915.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john915.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john916\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john916.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john916.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john917\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john917.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john917.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john918\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john918.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john918.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john919\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john919.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john919.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john92\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john92.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john92.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john920\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john920.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john920.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john921\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john921.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john921.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john922\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john922.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john922.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john923\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john923.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john923.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john924\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john924.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john924.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john925\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john925.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john925.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john926\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john926.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john926.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john927\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john927.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john927.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john928\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john928.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john928.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john929\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john929.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john929.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john93\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john93.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john93.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john930\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john930.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john930.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john931\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john931.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john931.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john932\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john932.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john932.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john933\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john933.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john933.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john934\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john934.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john934.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john935\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john935.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john935.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john936\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john936.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john936.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john937\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john937.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john937.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john938\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john938.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john938.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john939\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john939.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john939.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john94\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john94.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john94.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john940\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john940.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john940.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john941\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john941.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john941.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john942\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john942.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john942.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john943\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john943.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john943.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john944\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john944.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john944.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john945\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john945.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john945.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john946\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john946.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john946.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john947\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john947.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john947.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john948\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john948.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john948.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john949\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john949.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john949.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john95\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john95.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john95.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john950\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john950.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john950.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john951\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john951.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john951.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john952\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john952.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john952.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john953\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john953.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john953.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john954\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john954.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john954.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john955\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john955.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john955.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john956\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john956.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john956.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john957\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john957.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john957.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john958\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john958.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john958.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john959\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john959.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john959.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john96\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john96.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john96.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john960\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john960.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john960.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john961\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john961.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john961.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john962\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john962.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john962.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john963\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john963.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john963.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john964\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john964.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john964.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john965\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john965.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john965.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john966\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john966.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john966.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john967\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john967.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john967.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john968\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john968.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john968.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john969\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john969.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john969.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john97\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john97.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john97.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john970\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john970.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john970.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john971\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john971.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john971.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john972\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john972.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john972.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john973\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john973.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john973.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john974\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john974.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john974.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john975\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john975.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john975.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john976\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john976.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john976.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john977\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john977.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john977.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john978\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john978.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john978.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john979\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john979.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john979.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john98\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john98.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john98.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john980\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john980.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john980.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john981\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john981.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john981.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john982\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john982.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john982.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john983\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john983.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john983.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john984\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john984.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john984.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john985\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john985.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john985.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john986\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john986.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john986.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john987\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john987.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john987.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john988\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john988.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john988.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john989\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john989.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john989.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john99\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john99.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john99.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john990\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john990.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john990.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john991\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john991.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john991.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john992\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john992.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john992.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john993\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john993.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john993.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john994\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john994.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john994.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john995\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john995.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john995.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john996\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john996.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john996.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john997\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john997.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john997.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john998\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john998.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john998.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n    <user userName=\"john999\">\n      <password encrypted=\"true\">LcTkpvvquKf4KO+prsfXrQ==</password>\n      <firstName>John</firstName>\n      <lastName>Doe</lastName>\n      <enabled>false</enabled>\n      <personalData>\n        <email>john999.doe@anywhere.com</email>\n      </personalData>\n      <professionalData>\n        <email>john999.doe@bonitasoft.com</email>\n      </professionalData>\n    </user>\n  </users>\n  <roles>\n    <role name=\"role0\"/>\n    <role name=\"role1\"/>\n    <role name=\"role10\"/>\n    <role name=\"role100\"/>\n    <role name=\"role101\"/>\n    <role name=\"role102\"/>\n    <role name=\"role103\"/>\n    <role name=\"role104\"/>\n    <role name=\"role105\"/>\n    <role name=\"role106\"/>\n    <role name=\"role107\"/>\n    <role name=\"role108\"/>\n    <role name=\"role109\"/>\n    <role name=\"role11\"/>\n    <role name=\"role110\"/>\n    <role name=\"role111\"/>\n    <role name=\"role112\"/>\n    <role name=\"role113\"/>\n    <role name=\"role114\"/>\n    <role name=\"role115\"/>\n    <role name=\"role116\"/>\n    <role name=\"role117\"/>\n    <role name=\"role118\"/>\n    <role name=\"role119\"/>\n    <role name=\"role12\"/>\n    <role name=\"role120\"/>\n    <role name=\"role121\"/>\n    <role name=\"role122\"/>\n    <role name=\"role123\"/>\n    <role name=\"role124\"/>\n    <role name=\"role125\"/>\n    <role name=\"role126\"/>\n    <role name=\"role127\"/>\n    <role name=\"role128\"/>\n    <role name=\"role129\"/>\n    <role name=\"role13\"/>\n    <role name=\"role130\"/>\n    <role name=\"role131\"/>\n    <role name=\"role132\"/>\n    <role name=\"role133\"/>\n    <role name=\"role134\"/>\n    <role name=\"role135\"/>\n    <role name=\"role136\"/>\n    <role name=\"role137\"/>\n    <role name=\"role138\"/>\n    <role name=\"role139\"/>\n    <role name=\"role14\"/>\n    <role name=\"role140\"/>\n    <role name=\"role141\"/>\n    <role name=\"role142\"/>\n    <role name=\"role143\"/>\n    <role name=\"role144\"/>\n    <role name=\"role145\"/>\n    <role name=\"role146\"/>\n    <role name=\"role147\"/>\n    <role name=\"role148\"/>\n    <role name=\"role149\"/>\n    <role name=\"role15\"/>\n    <role name=\"role150\"/>\n    <role name=\"role151\"/>\n    <role name=\"role152\"/>\n    <role name=\"role153\"/>\n    <role name=\"role154\"/>\n    <role name=\"role155\"/>\n    <role name=\"role156\"/>\n    <role name=\"role157\"/>\n    <role name=\"role158\"/>\n    <role name=\"role159\"/>\n    <role name=\"role16\"/>\n    <role name=\"role160\"/>\n    <role name=\"role161\"/>\n    <role name=\"role162\"/>\n    <role name=\"role163\"/>\n    <role name=\"role164\"/>\n    <role name=\"role165\"/>\n    <role name=\"role166\"/>\n    <role name=\"role167\"/>\n    <role name=\"role168\"/>\n    <role name=\"role169\"/>\n    <role name=\"role17\"/>\n    <role name=\"role170\"/>\n    <role name=\"role171\"/>\n    <role name=\"role172\"/>\n    <role name=\"role173\"/>\n    <role name=\"role174\"/>\n    <role name=\"role175\"/>\n    <role name=\"role176\"/>\n    <role name=\"role177\"/>\n    <role name=\"role178\"/>\n    <role name=\"role179\"/>\n    <role name=\"role18\"/>\n    <role name=\"role180\"/>\n    <role name=\"role181\"/>\n    <role name=\"role182\"/>\n    <role name=\"role183\"/>\n    <role name=\"role184\"/>\n    <role name=\"role185\"/>\n    <role name=\"role186\"/>\n    <role name=\"role187\"/>\n    <role name=\"role188\"/>\n    <role name=\"role189\"/>\n    <role name=\"role19\"/>\n    <role name=\"role190\"/>\n    <role name=\"role191\"/>\n    <role name=\"role192\"/>\n    <role name=\"role193\"/>\n    <role name=\"role194\"/>\n    <role name=\"role195\"/>\n    <role name=\"role196\"/>\n    <role name=\"role197\"/>\n    <role name=\"role198\"/>\n    <role name=\"role199\"/>\n    <role name=\"role2\"/>\n    <role name=\"role20\"/>\n    <role name=\"role200\"/>\n    <role name=\"role201\"/>\n    <role name=\"role202\"/>\n    <role name=\"role203\"/>\n    <role name=\"role204\"/>\n    <role name=\"role205\"/>\n    <role name=\"role206\"/>\n    <role name=\"role207\"/>\n    <role name=\"role208\"/>\n    <role name=\"role209\"/>\n    <role name=\"role21\"/>\n    <role name=\"role210\"/>\n    <role name=\"role211\"/>\n    <role name=\"role212\"/>\n    <role name=\"role213\"/>\n    <role name=\"role214\"/>\n    <role name=\"role215\"/>\n    <role name=\"role216\"/>\n    <role name=\"role217\"/>\n    <role name=\"role218\"/>\n    <role name=\"role219\"/>\n    <role name=\"role22\"/>\n    <role name=\"role220\"/>\n    <role name=\"role221\"/>\n    <role name=\"role222\"/>\n    <role name=\"role223\"/>\n    <role name=\"role224\"/>\n    <role name=\"role225\"/>\n    <role name=\"role226\"/>\n    <role name=\"role227\"/>\n    <role name=\"role228\"/>\n    <role name=\"role229\"/>\n    <role name=\"role23\"/>\n    <role name=\"role230\"/>\n    <role name=\"role231\"/>\n    <role name=\"role232\"/>\n    <role name=\"role233\"/>\n    <role name=\"role234\"/>\n    <role name=\"role235\"/>\n    <role name=\"role236\"/>\n    <role name=\"role237\"/>\n    <role name=\"role238\"/>\n    <role name=\"role239\"/>\n    <role name=\"role24\"/>\n    <role name=\"role240\"/>\n    <role name=\"role241\"/>\n    <role name=\"role242\"/>\n    <role name=\"role243\"/>\n    <role name=\"role244\"/>\n    <role name=\"role245\"/>\n    <role name=\"role246\"/>\n    <role name=\"role247\"/>\n    <role name=\"role248\"/>\n    <role name=\"role249\"/>\n    <role name=\"role25\"/>\n    <role name=\"role250\"/>\n    <role name=\"role251\"/>\n    <role name=\"role252\"/>\n    <role name=\"role253\"/>\n    <role name=\"role254\"/>\n    <role name=\"role255\"/>\n    <role name=\"role256\"/>\n    <role name=\"role257\"/>\n    <role name=\"role258\"/>\n    <role name=\"role259\"/>\n    <role name=\"role26\"/>\n    <role name=\"role260\"/>\n    <role name=\"role261\"/>\n    <role name=\"role262\"/>\n    <role name=\"role263\"/>\n    <role name=\"role264\"/>\n    <role name=\"role265\"/>\n    <role name=\"role266\"/>\n    <role name=\"role267\"/>\n    <role name=\"role268\"/>\n    <role name=\"role269\"/>\n    <role name=\"role27\"/>\n    <role name=\"role270\"/>\n    <role name=\"role271\"/>\n    <role name=\"role272\"/>\n    <role name=\"role273\"/>\n    <role name=\"role274\"/>\n    <role name=\"role275\"/>\n    <role name=\"role276\"/>\n    <role name=\"role277\"/>\n    <role name=\"role278\"/>\n    <role name=\"role279\"/>\n    <role name=\"role28\"/>\n    <role name=\"role280\"/>\n    <role name=\"role281\"/>\n    <role name=\"role282\"/>\n    <role name=\"role283\"/>\n    <role name=\"role284\"/>\n    <role name=\"role285\"/>\n    <role name=\"role286\"/>\n    <role name=\"role287\"/>\n    <role name=\"role288\"/>\n    <role name=\"role289\"/>\n    <role name=\"role29\"/>\n    <role name=\"role290\"/>\n    <role name=\"role291\"/>\n    <role name=\"role292\"/>\n    <role name=\"role293\"/>\n    <role name=\"role294\"/>\n    <role name=\"role295\"/>\n    <role name=\"role296\"/>\n    <role name=\"role297\"/>\n    <role name=\"role298\"/>\n    <role name=\"role299\"/>\n    <role name=\"role3\"/>\n    <role name=\"role30\"/>\n    <role name=\"role300\"/>\n    <role name=\"role301\"/>\n    <role name=\"role302\"/>\n    <role name=\"role303\"/>\n    <role name=\"role304\"/>\n    <role name=\"role305\"/>\n    <role name=\"role306\"/>\n    <role name=\"role307\"/>\n    <role name=\"role308\"/>\n    <role name=\"role309\"/>\n    <role name=\"role31\"/>\n    <role name=\"role310\"/>\n    <role name=\"role311\"/>\n    <role name=\"role312\"/>\n    <role name=\"role313\"/>\n    <role name=\"role314\"/>\n    <role name=\"role315\"/>\n    <role name=\"role316\"/>\n    <role name=\"role317\"/>\n    <role name=\"role318\"/>\n    <role name=\"role319\"/>\n    <role name=\"role32\"/>\n    <role name=\"role320\"/>\n    <role name=\"role321\"/>\n    <role name=\"role322\"/>\n    <role name=\"role323\"/>\n    <role name=\"role324\"/>\n    <role name=\"role325\"/>\n    <role name=\"role326\"/>\n    <role name=\"role327\"/>\n    <role name=\"role328\"/>\n    <role name=\"role329\"/>\n    <role name=\"role33\"/>\n    <role name=\"role330\"/>\n    <role name=\"role331\"/>\n    <role name=\"role332\"/>\n    <role name=\"role333\"/>\n    <role name=\"role334\"/>\n    <role name=\"role335\"/>\n    <role name=\"role336\"/>\n    <role name=\"role337\"/>\n    <role name=\"role338\"/>\n    <role name=\"role339\"/>\n    <role name=\"role34\"/>\n    <role name=\"role340\"/>\n    <role name=\"role341\"/>\n    <role name=\"role342\"/>\n    <role name=\"role343\"/>\n    <role name=\"role344\"/>\n    <role name=\"role345\"/>\n    <role name=\"role346\"/>\n    <role name=\"role347\"/>\n    <role name=\"role348\"/>\n    <role name=\"role349\"/>\n    <role name=\"role35\"/>\n    <role name=\"role350\"/>\n    <role name=\"role351\"/>\n    <role name=\"role352\"/>\n    <role name=\"role353\"/>\n    <role name=\"role354\"/>\n    <role name=\"role355\"/>\n    <role name=\"role356\"/>\n    <role name=\"role357\"/>\n    <role name=\"role358\"/>\n    <role name=\"role359\"/>\n    <role name=\"role36\"/>\n    <role name=\"role360\"/>\n    <role name=\"role361\"/>\n    <role name=\"role362\"/>\n    <role name=\"role363\"/>\n    <role name=\"role364\"/>\n    <role name=\"role365\"/>\n    <role name=\"role366\"/>\n    <role name=\"role367\"/>\n    <role name=\"role368\"/>\n    <role name=\"role369\"/>\n    <role name=\"role37\"/>\n    <role name=\"role370\"/>\n    <role name=\"role371\"/>\n    <role name=\"role372\"/>\n    <role name=\"role373\"/>\n    <role name=\"role374\"/>\n    <role name=\"role375\"/>\n    <role name=\"role376\"/>\n    <role name=\"role377\"/>\n    <role name=\"role378\"/>\n    <role name=\"role379\"/>\n    <role name=\"role38\"/>\n    <role name=\"role380\"/>\n    <role name=\"role381\"/>\n    <role name=\"role382\"/>\n    <role name=\"role383\"/>\n    <role name=\"role384\"/>\n    <role name=\"role385\"/>\n    <role name=\"role386\"/>\n    <role name=\"role387\"/>\n    <role name=\"role388\"/>\n    <role name=\"role389\"/>\n    <role name=\"role39\"/>\n    <role name=\"role390\"/>\n    <role name=\"role391\"/>\n    <role name=\"role392\"/>\n    <role name=\"role393\"/>\n    <role name=\"role394\"/>\n    <role name=\"role395\"/>\n    <role name=\"role396\"/>\n    <role name=\"role397\"/>\n    <role name=\"role398\"/>\n    <role name=\"role399\"/>\n    <role name=\"role4\"/>\n    <role name=\"role40\"/>\n    <role name=\"role400\"/>\n    <role name=\"role401\"/>\n    <role name=\"role402\"/>\n    <role name=\"role403\"/>\n    <role name=\"role404\"/>\n    <role name=\"role405\"/>\n    <role name=\"role406\"/>\n    <role name=\"role407\"/>\n    <role name=\"role408\"/>\n    <role name=\"role409\"/>\n    <role name=\"role41\"/>\n    <role name=\"role410\"/>\n    <role name=\"role411\"/>\n    <role name=\"role412\"/>\n    <role name=\"role413\"/>\n    <role name=\"role414\"/>\n    <role name=\"role415\"/>\n    <role name=\"role416\"/>\n    <role name=\"role417\"/>\n    <role name=\"role418\"/>\n    <role name=\"role419\"/>\n    <role name=\"role42\"/>\n    <role name=\"role420\"/>\n    <role name=\"role421\"/>\n    <role name=\"role422\"/>\n    <role name=\"role423\"/>\n    <role name=\"role424\"/>\n    <role name=\"role425\"/>\n    <role name=\"role426\"/>\n    <role name=\"role427\"/>\n    <role name=\"role428\"/>\n    <role name=\"role429\"/>\n    <role name=\"role43\"/>\n    <role name=\"role430\"/>\n    <role name=\"role431\"/>\n    <role name=\"role432\"/>\n    <role name=\"role433\"/>\n    <role name=\"role434\"/>\n    <role name=\"role435\"/>\n    <role name=\"role436\"/>\n    <role name=\"role437\"/>\n    <role name=\"role438\"/>\n    <role name=\"role439\"/>\n    <role name=\"role44\"/>\n    <role name=\"role440\"/>\n    <role name=\"role441\"/>\n    <role name=\"role442\"/>\n    <role name=\"role443\"/>\n    <role name=\"role444\"/>\n    <role name=\"role445\"/>\n    <role name=\"role446\"/>\n    <role name=\"role447\"/>\n    <role name=\"role448\"/>\n    <role name=\"role449\"/>\n    <role name=\"role45\"/>\n    <role name=\"role450\"/>\n    <role name=\"role451\"/>\n    <role name=\"role452\"/>\n    <role name=\"role453\"/>\n    <role name=\"role454\"/>\n    <role name=\"role455\"/>\n    <role name=\"role456\"/>\n    <role name=\"role457\"/>\n    <role name=\"role458\"/>\n    <role name=\"role459\"/>\n    <role name=\"role46\"/>\n    <role name=\"role460\"/>\n    <role name=\"role461\"/>\n    <role name=\"role462\"/>\n    <role name=\"role463\"/>\n    <role name=\"role464\"/>\n    <role name=\"role465\"/>\n    <role name=\"role466\"/>\n    <role name=\"role467\"/>\n    <role name=\"role468\"/>\n    <role name=\"role469\"/>\n    <role name=\"role47\"/>\n    <role name=\"role470\"/>\n    <role name=\"role471\"/>\n    <role name=\"role472\"/>\n    <role name=\"role473\"/>\n    <role name=\"role474\"/>\n    <role name=\"role475\"/>\n    <role name=\"role476\"/>\n    <role name=\"role477\"/>\n    <role name=\"role478\"/>\n    <role name=\"role479\"/>\n    <role name=\"role48\"/>\n    <role name=\"role480\"/>\n    <role name=\"role481\"/>\n    <role name=\"role482\"/>\n    <role name=\"role483\"/>\n    <role name=\"role484\"/>\n    <role name=\"role485\"/>\n    <role name=\"role486\"/>\n    <role name=\"role487\"/>\n    <role name=\"role488\"/>\n    <role name=\"role489\"/>\n    <role name=\"role49\"/>\n    <role name=\"role490\"/>\n    <role name=\"role491\"/>\n    <role name=\"role492\"/>\n    <role name=\"role493\"/>\n    <role name=\"role494\"/>\n    <role name=\"role495\"/>\n    <role name=\"role496\"/>\n    <role name=\"role497\"/>\n    <role name=\"role498\"/>\n    <role name=\"role499\"/>\n    <role name=\"role5\"/>\n    <role name=\"role50\"/>\n    <role name=\"role500\"/>\n    <role name=\"role501\"/>\n    <role name=\"role502\"/>\n    <role name=\"role503\"/>\n    <role name=\"role504\"/>\n    <role name=\"role505\"/>\n    <role name=\"role506\"/>\n    <role name=\"role507\"/>\n    <role name=\"role508\"/>\n    <role name=\"role509\"/>\n    <role name=\"role51\"/>\n    <role name=\"role510\"/>\n    <role name=\"role511\"/>\n    <role name=\"role512\"/>\n    <role name=\"role513\"/>\n    <role name=\"role514\"/>\n    <role name=\"role515\"/>\n    <role name=\"role516\"/>\n    <role name=\"role517\"/>\n    <role name=\"role518\"/>\n    <role name=\"role519\"/>\n    <role name=\"role52\"/>\n    <role name=\"role520\"/>\n    <role name=\"role521\"/>\n    <role name=\"role522\"/>\n    <role name=\"role523\"/>\n    <role name=\"role524\"/>\n    <role name=\"role525\"/>\n    <role name=\"role526\"/>\n    <role name=\"role527\"/>\n    <role name=\"role528\"/>\n    <role name=\"role529\"/>\n    <role name=\"role53\"/>\n    <role name=\"role530\"/>\n    <role name=\"role531\"/>\n    <role name=\"role532\"/>\n    <role name=\"role533\"/>\n    <role name=\"role534\"/>\n    <role name=\"role535\"/>\n    <role name=\"role536\"/>\n    <role name=\"role537\"/>\n    <role name=\"role538\"/>\n    <role name=\"role539\"/>\n    <role name=\"role54\"/>\n    <role name=\"role540\"/>\n    <role name=\"role541\"/>\n    <role name=\"role542\"/>\n    <role name=\"role543\"/>\n    <role name=\"role544\"/>\n    <role name=\"role545\"/>\n    <role name=\"role546\"/>\n    <role name=\"role547\"/>\n    <role name=\"role548\"/>\n    <role name=\"role549\"/>\n    <role name=\"role55\"/>\n    <role name=\"role550\"/>\n    <role name=\"role551\"/>\n    <role name=\"role552\"/>\n    <role name=\"role553\"/>\n    <role name=\"role554\"/>\n    <role name=\"role555\"/>\n    <role name=\"role556\"/>\n    <role name=\"role557\"/>\n    <role name=\"role558\"/>\n    <role name=\"role559\"/>\n    <role name=\"role56\"/>\n    <role name=\"role560\"/>\n    <role name=\"role561\"/>\n    <role name=\"role562\"/>\n    <role name=\"role563\"/>\n    <role name=\"role564\"/>\n    <role name=\"role565\"/>\n    <role name=\"role566\"/>\n    <role name=\"role567\"/>\n    <role name=\"role568\"/>\n    <role name=\"role569\"/>\n    <role name=\"role57\"/>\n    <role name=\"role570\"/>\n    <role name=\"role571\"/>\n    <role name=\"role572\"/>\n    <role name=\"role573\"/>\n    <role name=\"role574\"/>\n    <role name=\"role575\"/>\n    <role name=\"role576\"/>\n    <role name=\"role577\"/>\n    <role name=\"role578\"/>\n    <role name=\"role579\"/>\n    <role name=\"role58\"/>\n    <role name=\"role580\"/>\n    <role name=\"role581\"/>\n    <role name=\"role582\"/>\n    <role name=\"role583\"/>\n    <role name=\"role584\"/>\n    <role name=\"role585\"/>\n    <role name=\"role586\"/>\n    <role name=\"role587\"/>\n    <role name=\"role588\"/>\n    <role name=\"role589\"/>\n    <role name=\"role59\"/>\n    <role name=\"role590\"/>\n    <role name=\"role591\"/>\n    <role name=\"role592\"/>\n    <role name=\"role593\"/>\n    <role name=\"role594\"/>\n    <role name=\"role595\"/>\n    <role name=\"role596\"/>\n    <role name=\"role597\"/>\n    <role name=\"role598\"/>\n    <role name=\"role599\"/>\n    <role name=\"role6\"/>\n    <role name=\"role60\"/>\n    <role name=\"role600\"/>\n    <role name=\"role601\"/>\n    <role name=\"role602\"/>\n    <role name=\"role603\"/>\n    <role name=\"role604\"/>\n    <role name=\"role605\"/>\n    <role name=\"role606\"/>\n    <role name=\"role607\"/>\n    <role name=\"role608\"/>\n    <role name=\"role609\"/>\n    <role name=\"role61\"/>\n    <role name=\"role610\"/>\n    <role name=\"role611\"/>\n    <role name=\"role612\"/>\n    <role name=\"role613\"/>\n    <role name=\"role614\"/>\n    <role name=\"role615\"/>\n    <role name=\"role616\"/>\n    <role name=\"role617\"/>\n    <role name=\"role618\"/>\n    <role name=\"role619\"/>\n    <role name=\"role62\"/>\n    <role name=\"role620\"/>\n    <role name=\"role621\"/>\n    <role name=\"role622\"/>\n    <role name=\"role623\"/>\n    <role name=\"role624\"/>\n    <role name=\"role625\"/>\n    <role name=\"role626\"/>\n    <role name=\"role627\"/>\n    <role name=\"role628\"/>\n    <role name=\"role629\"/>\n    <role name=\"role63\"/>\n    <role name=\"role630\"/>\n    <role name=\"role631\"/>\n    <role name=\"role632\"/>\n    <role name=\"role633\"/>\n    <role name=\"role634\"/>\n    <role name=\"role635\"/>\n    <role name=\"role636\"/>\n    <role name=\"role637\"/>\n    <role name=\"role638\"/>\n    <role name=\"role639\"/>\n    <role name=\"role64\"/>\n    <role name=\"role640\"/>\n    <role name=\"role641\"/>\n    <role name=\"role642\"/>\n    <role name=\"role643\"/>\n    <role name=\"role644\"/>\n    <role name=\"role645\"/>\n    <role name=\"role646\"/>\n    <role name=\"role647\"/>\n    <role name=\"role648\"/>\n    <role name=\"role649\"/>\n    <role name=\"role65\"/>\n    <role name=\"role650\"/>\n    <role name=\"role651\"/>\n    <role name=\"role652\"/>\n    <role name=\"role653\"/>\n    <role name=\"role654\"/>\n    <role name=\"role655\"/>\n    <role name=\"role656\"/>\n    <role name=\"role657\"/>\n    <role name=\"role658\"/>\n    <role name=\"role659\"/>\n    <role name=\"role66\"/>\n    <role name=\"role660\"/>\n    <role name=\"role661\"/>\n    <role name=\"role662\"/>\n    <role name=\"role663\"/>\n    <role name=\"role664\"/>\n    <role name=\"role665\"/>\n    <role name=\"role666\"/>\n    <role name=\"role667\"/>\n    <role name=\"role668\"/>\n    <role name=\"role669\"/>\n    <role name=\"role67\"/>\n    <role name=\"role670\"/>\n    <role name=\"role671\"/>\n    <role name=\"role672\"/>\n    <role name=\"role673\"/>\n    <role name=\"role674\"/>\n    <role name=\"role675\"/>\n    <role name=\"role676\"/>\n    <role name=\"role677\"/>\n    <role name=\"role678\"/>\n    <role name=\"role679\"/>\n    <role name=\"role68\"/>\n    <role name=\"role680\"/>\n    <role name=\"role681\"/>\n    <role name=\"role682\"/>\n    <role name=\"role683\"/>\n    <role name=\"role684\"/>\n    <role name=\"role685\"/>\n    <role name=\"role686\"/>\n    <role name=\"role687\"/>\n    <role name=\"role688\"/>\n    <role name=\"role689\"/>\n    <role name=\"role69\"/>\n    <role name=\"role690\"/>\n    <role name=\"role691\"/>\n    <role name=\"role692\"/>\n    <role name=\"role693\"/>\n    <role name=\"role694\"/>\n    <role name=\"role695\"/>\n    <role name=\"role696\"/>\n    <role name=\"role697\"/>\n    <role name=\"role698\"/>\n    <role name=\"role699\"/>\n    <role name=\"role7\"/>\n    <role name=\"role70\"/>\n    <role name=\"role700\"/>\n    <role name=\"role701\"/>\n    <role name=\"role702\"/>\n    <role name=\"role703\"/>\n    <role name=\"role704\"/>\n    <role name=\"role705\"/>\n    <role name=\"role706\"/>\n    <role name=\"role707\"/>\n    <role name=\"role708\"/>\n    <role name=\"role709\"/>\n    <role name=\"role71\"/>\n    <role name=\"role710\"/>\n    <role name=\"role711\"/>\n    <role name=\"role712\"/>\n    <role name=\"role713\"/>\n    <role name=\"role714\"/>\n    <role name=\"role715\"/>\n    <role name=\"role716\"/>\n    <role name=\"role717\"/>\n    <role name=\"role718\"/>\n    <role name=\"role719\"/>\n    <role name=\"role72\"/>\n    <role name=\"role720\"/>\n    <role name=\"role721\"/>\n    <role name=\"role722\"/>\n    <role name=\"role723\"/>\n    <role name=\"role724\"/>\n    <role name=\"role725\"/>\n    <role name=\"role726\"/>\n    <role name=\"role727\"/>\n    <role name=\"role728\"/>\n    <role name=\"role729\"/>\n    <role name=\"role73\"/>\n    <role name=\"role730\"/>\n    <role name=\"role731\"/>\n    <role name=\"role732\"/>\n    <role name=\"role733\"/>\n    <role name=\"role734\"/>\n    <role name=\"role735\"/>\n    <role name=\"role736\"/>\n    <role name=\"role737\"/>\n    <role name=\"role738\"/>\n    <role name=\"role739\"/>\n    <role name=\"role74\"/>\n    <role name=\"role740\"/>\n    <role name=\"role741\"/>\n    <role name=\"role742\"/>\n    <role name=\"role743\"/>\n    <role name=\"role744\"/>\n    <role name=\"role745\"/>\n    <role name=\"role746\"/>\n    <role name=\"role747\"/>\n    <role name=\"role748\"/>\n    <role name=\"role749\"/>\n    <role name=\"role75\"/>\n    <role name=\"role750\"/>\n    <role name=\"role751\"/>\n    <role name=\"role752\"/>\n    <role name=\"role753\"/>\n    <role name=\"role754\"/>\n    <role name=\"role755\"/>\n    <role name=\"role756\"/>\n    <role name=\"role757\"/>\n    <role name=\"role758\"/>\n    <role name=\"role759\"/>\n    <role name=\"role76\"/>\n    <role name=\"role760\"/>\n    <role name=\"role761\"/>\n    <role name=\"role762\"/>\n    <role name=\"role763\"/>\n    <role name=\"role764\"/>\n    <role name=\"role765\"/>\n    <role name=\"role766\"/>\n    <role name=\"role767\"/>\n    <role name=\"role768\"/>\n    <role name=\"role769\"/>\n    <role name=\"role77\"/>\n    <role name=\"role770\"/>\n    <role name=\"role771\"/>\n    <role name=\"role772\"/>\n    <role name=\"role773\"/>\n    <role name=\"role774\"/>\n    <role name=\"role775\"/>\n    <role name=\"role776\"/>\n    <role name=\"role777\"/>\n    <role name=\"role778\"/>\n    <role name=\"role779\"/>\n    <role name=\"role78\"/>\n    <role name=\"role780\"/>\n    <role name=\"role781\"/>\n    <role name=\"role782\"/>\n    <role name=\"role783\"/>\n    <role name=\"role784\"/>\n    <role name=\"role785\"/>\n    <role name=\"role786\"/>\n    <role name=\"role787\"/>\n    <role name=\"role788\"/>\n    <role name=\"role789\"/>\n    <role name=\"role79\"/>\n    <role name=\"role790\"/>\n    <role name=\"role791\"/>\n    <role name=\"role792\"/>\n    <role name=\"role793\"/>\n    <role name=\"role794\"/>\n    <role name=\"role795\"/>\n    <role name=\"role796\"/>\n    <role name=\"role797\"/>\n    <role name=\"role798\"/>\n    <role name=\"role799\"/>\n    <role name=\"role8\"/>\n    <role name=\"role80\"/>\n    <role name=\"role800\"/>\n    <role name=\"role801\"/>\n    <role name=\"role802\"/>\n    <role name=\"role803\"/>\n    <role name=\"role804\"/>\n    <role name=\"role805\"/>\n    <role name=\"role806\"/>\n    <role name=\"role807\"/>\n    <role name=\"role808\"/>\n    <role name=\"role809\"/>\n    <role name=\"role81\"/>\n    <role name=\"role810\"/>\n    <role name=\"role811\"/>\n    <role name=\"role812\"/>\n    <role name=\"role813\"/>\n    <role name=\"role814\"/>\n    <role name=\"role815\"/>\n    <role name=\"role816\"/>\n    <role name=\"role817\"/>\n    <role name=\"role818\"/>\n    <role name=\"role819\"/>\n    <role name=\"role82\"/>\n    <role name=\"role820\"/>\n    <role name=\"role821\"/>\n    <role name=\"role822\"/>\n    <role name=\"role823\"/>\n    <role name=\"role824\"/>\n    <role name=\"role825\"/>\n    <role name=\"role826\"/>\n    <role name=\"role827\"/>\n    <role name=\"role828\"/>\n    <role name=\"role829\"/>\n    <role name=\"role83\"/>\n    <role name=\"role830\"/>\n    <role name=\"role831\"/>\n    <role name=\"role832\"/>\n    <role name=\"role833\"/>\n    <role name=\"role834\"/>\n    <role name=\"role835\"/>\n    <role name=\"role836\"/>\n    <role name=\"role837\"/>\n    <role name=\"role838\"/>\n    <role name=\"role839\"/>\n    <role name=\"role84\"/>\n    <role name=\"role840\"/>\n    <role name=\"role841\"/>\n    <role name=\"role842\"/>\n    <role name=\"role843\"/>\n    <role name=\"role844\"/>\n    <role name=\"role845\"/>\n    <role name=\"role846\"/>\n    <role name=\"role847\"/>\n    <role name=\"role848\"/>\n    <role name=\"role849\"/>\n    <role name=\"role85\"/>\n    <role name=\"role850\"/>\n    <role name=\"role851\"/>\n    <role name=\"role852\"/>\n    <role name=\"role853\"/>\n    <role name=\"role854\"/>\n    <role name=\"role855\"/>\n    <role name=\"role856\"/>\n    <role name=\"role857\"/>\n    <role name=\"role858\"/>\n    <role name=\"role859\"/>\n    <role name=\"role86\"/>\n    <role name=\"role860\"/>\n    <role name=\"role861\"/>\n    <role name=\"role862\"/>\n    <role name=\"role863\"/>\n    <role name=\"role864\"/>\n    <role name=\"role865\"/>\n    <role name=\"role866\"/>\n    <role name=\"role867\"/>\n    <role name=\"role868\"/>\n    <role name=\"role869\"/>\n    <role name=\"role87\"/>\n    <role name=\"role870\"/>\n    <role name=\"role871\"/>\n    <role name=\"role872\"/>\n    <role name=\"role873\"/>\n    <role name=\"role874\"/>\n    <role name=\"role875\"/>\n    <role name=\"role876\"/>\n    <role name=\"role877\"/>\n    <role name=\"role878\"/>\n    <role name=\"role879\"/>\n    <role name=\"role88\"/>\n    <role name=\"role880\"/>\n    <role name=\"role881\"/>\n    <role name=\"role882\"/>\n    <role name=\"role883\"/>\n    <role name=\"role884\"/>\n    <role name=\"role885\"/>\n    <role name=\"role886\"/>\n    <role name=\"role887\"/>\n    <role name=\"role888\"/>\n    <role name=\"role889\"/>\n    <role name=\"role89\"/>\n    <role name=\"role890\"/>\n    <role name=\"role891\"/>\n    <role name=\"role892\"/>\n    <role name=\"role893\"/>\n    <role name=\"role894\"/>\n    <role name=\"role895\"/>\n    <role name=\"role896\"/>\n    <role name=\"role897\"/>\n    <role name=\"role898\"/>\n    <role name=\"role899\"/>\n    <role name=\"role9\"/>\n    <role name=\"role90\"/>\n    <role name=\"role900\"/>\n    <role name=\"role901\"/>\n    <role name=\"role902\"/>\n    <role name=\"role903\"/>\n    <role name=\"role904\"/>\n    <role name=\"role905\"/>\n    <role name=\"role906\"/>\n    <role name=\"role907\"/>\n    <role name=\"role908\"/>\n    <role name=\"role909\"/>\n    <role name=\"role91\"/>\n    <role name=\"role910\"/>\n    <role name=\"role911\"/>\n    <role name=\"role912\"/>\n    <role name=\"role913\"/>\n    <role name=\"role914\"/>\n    <role name=\"role915\"/>\n    <role name=\"role916\"/>\n    <role name=\"role917\"/>\n    <role name=\"role918\"/>\n    <role name=\"role919\"/>\n    <role name=\"role92\"/>\n    <role name=\"role920\"/>\n    <role name=\"role921\"/>\n    <role name=\"role922\"/>\n    <role name=\"role923\"/>\n    <role name=\"role924\"/>\n    <role name=\"role925\"/>\n    <role name=\"role926\"/>\n    <role name=\"role927\"/>\n    <role name=\"role928\"/>\n    <role name=\"role929\"/>\n    <role name=\"role93\"/>\n    <role name=\"role930\"/>\n    <role name=\"role931\"/>\n    <role name=\"role932\"/>\n    <role name=\"role933\"/>\n    <role name=\"role934\"/>\n    <role name=\"role935\"/>\n    <role name=\"role936\"/>\n    <role name=\"role937\"/>\n    <role name=\"role938\"/>\n    <role name=\"role939\"/>\n    <role name=\"role94\"/>\n    <role name=\"role940\"/>\n    <role name=\"role941\"/>\n    <role name=\"role942\"/>\n    <role name=\"role943\"/>\n    <role name=\"role944\"/>\n    <role name=\"role945\"/>\n    <role name=\"role946\"/>\n    <role name=\"role947\"/>\n    <role name=\"role948\"/>\n    <role name=\"role949\"/>\n    <role name=\"role95\"/>\n    <role name=\"role950\"/>\n    <role name=\"role951\"/>\n    <role name=\"role952\"/>\n    <role name=\"role953\"/>\n    <role name=\"role954\"/>\n    <role name=\"role955\"/>\n    <role name=\"role956\"/>\n    <role name=\"role957\"/>\n    <role name=\"role958\"/>\n    <role name=\"role959\"/>\n    <role name=\"role96\"/>\n    <role name=\"role960\"/>\n    <role name=\"role961\"/>\n    <role name=\"role962\"/>\n    <role name=\"role963\"/>\n    <role name=\"role964\"/>\n    <role name=\"role965\"/>\n    <role name=\"role966\"/>\n    <role name=\"role967\"/>\n    <role name=\"role968\"/>\n    <role name=\"role969\"/>\n    <role name=\"role97\"/>\n    <role name=\"role970\"/>\n    <role name=\"role971\"/>\n    <role name=\"role972\"/>\n    <role name=\"role973\"/>\n    <role name=\"role974\"/>\n    <role name=\"role975\"/>\n    <role name=\"role976\"/>\n    <role name=\"role977\"/>\n    <role name=\"role978\"/>\n    <role name=\"role979\"/>\n    <role name=\"role98\"/>\n    <role name=\"role980\"/>\n    <role name=\"role981\"/>\n    <role name=\"role982\"/>\n    <role name=\"role983\"/>\n    <role name=\"role984\"/>\n    <role name=\"role985\"/>\n    <role name=\"role986\"/>\n    <role name=\"role987\"/>\n    <role name=\"role988\"/>\n    <role name=\"role989\"/>\n    <role name=\"role99\"/>\n    <role name=\"role990\"/>\n    <role name=\"role991\"/>\n    <role name=\"role992\"/>\n    <role name=\"role993\"/>\n    <role name=\"role994\"/>\n    <role name=\"role995\"/>\n    <role name=\"role996\"/>\n    <role name=\"role997\"/>\n    <role name=\"role998\"/>\n    <role name=\"role999\"/>\n  </roles>\n  <groups>\n    <group name=\"group0\"/>\n    <group name=\"group1\"/>\n    <group name=\"group10\"/>\n    <group name=\"group100\"/>\n    <group name=\"group101\"/>\n    <group name=\"group102\"/>\n    <group name=\"group103\"/>\n    <group name=\"group104\"/>\n    <group name=\"group105\"/>\n    <group name=\"group106\"/>\n    <group name=\"group107\"/>\n    <group name=\"group108\"/>\n    <group name=\"group109\"/>\n    <group name=\"group11\"/>\n    <group name=\"group110\"/>\n    <group name=\"group111\"/>\n    <group name=\"group112\"/>\n    <group name=\"group113\"/>\n    <group name=\"group114\"/>\n    <group name=\"group115\"/>\n    <group name=\"group116\"/>\n    <group name=\"group117\"/>\n    <group name=\"group118\"/>\n    <group name=\"group119\"/>\n    <group name=\"group12\"/>\n    <group name=\"group120\"/>\n    <group name=\"group121\"/>\n    <group name=\"group122\"/>\n    <group name=\"group123\"/>\n    <group name=\"group124\"/>\n    <group name=\"group125\"/>\n    <group name=\"group126\"/>\n    <group name=\"group127\"/>\n    <group name=\"group128\"/>\n    <group name=\"group129\"/>\n    <group name=\"group13\"/>\n    <group name=\"group130\"/>\n    <group name=\"group131\"/>\n    <group name=\"group132\"/>\n    <group name=\"group133\"/>\n    <group name=\"group134\"/>\n    <group name=\"group135\"/>\n    <group name=\"group136\"/>\n    <group name=\"group137\"/>\n    <group name=\"group138\"/>\n    <group name=\"group139\"/>\n    <group name=\"group14\"/>\n    <group name=\"group140\"/>\n    <group name=\"group141\"/>\n    <group name=\"group142\"/>\n    <group name=\"group143\"/>\n    <group name=\"group144\"/>\n    <group name=\"group145\"/>\n    <group name=\"group146\"/>\n    <group name=\"group147\"/>\n    <group name=\"group148\"/>\n    <group name=\"group149\"/>\n    <group name=\"group15\"/>\n    <group name=\"group150\"/>\n    <group name=\"group151\"/>\n    <group name=\"group152\"/>\n    <group name=\"group153\"/>\n    <group name=\"group154\"/>\n    <group name=\"group155\"/>\n    <group name=\"group156\"/>\n    <group name=\"group157\"/>\n    <group name=\"group158\"/>\n    <group name=\"group159\"/>\n    <group name=\"group16\"/>\n    <group name=\"group160\"/>\n    <group name=\"group161\"/>\n    <group name=\"group162\"/>\n    <group name=\"group163\"/>\n    <group name=\"group164\"/>\n    <group name=\"group165\"/>\n    <group name=\"group166\"/>\n    <group name=\"group167\"/>\n    <group name=\"group168\"/>\n    <group name=\"group169\"/>\n    <group name=\"group17\"/>\n    <group name=\"group170\"/>\n    <group name=\"group171\"/>\n    <group name=\"group172\"/>\n    <group name=\"group173\"/>\n    <group name=\"group174\"/>\n    <group name=\"group175\"/>\n    <group name=\"group176\"/>\n    <group name=\"group177\"/>\n    <group name=\"group178\"/>\n    <group name=\"group179\"/>\n    <group name=\"group18\"/>\n    <group name=\"group180\"/>\n    <group name=\"group181\"/>\n    <group name=\"group182\"/>\n    <group name=\"group183\"/>\n    <group name=\"group184\"/>\n    <group name=\"group185\"/>\n    <group name=\"group186\"/>\n    <group name=\"group187\"/>\n    <group name=\"group188\"/>\n    <group name=\"group189\"/>\n    <group name=\"group19\"/>\n    <group name=\"group190\"/>\n    <group name=\"group191\"/>\n    <group name=\"group192\"/>\n    <group name=\"group193\"/>\n    <group name=\"group194\"/>\n    <group name=\"group195\"/>\n    <group name=\"group196\"/>\n    <group name=\"group197\"/>\n    <group name=\"group198\"/>\n    <group name=\"group199\"/>\n    <group name=\"group2\"/>\n    <group name=\"group20\"/>\n    <group name=\"group200\"/>\n    <group name=\"group201\"/>\n    <group name=\"group202\"/>\n    <group name=\"group203\"/>\n    <group name=\"group204\"/>\n    <group name=\"group205\"/>\n    <group name=\"group206\"/>\n    <group name=\"group207\"/>\n    <group name=\"group208\"/>\n    <group name=\"group209\"/>\n    <group name=\"group21\"/>\n    <group name=\"group210\"/>\n    <group name=\"group211\"/>\n    <group name=\"group212\"/>\n    <group name=\"group213\"/>\n    <group name=\"group214\"/>\n    <group name=\"group215\"/>\n    <group name=\"group216\"/>\n    <group name=\"group217\"/>\n    <group name=\"group218\"/>\n    <group name=\"group219\"/>\n    <group name=\"group22\"/>\n    <group name=\"group220\"/>\n    <group name=\"group221\"/>\n    <group name=\"group222\"/>\n    <group name=\"group223\"/>\n    <group name=\"group224\"/>\n    <group name=\"group225\"/>\n    <group name=\"group226\"/>\n    <group name=\"group227\"/>\n    <group name=\"group228\"/>\n    <group name=\"group229\"/>\n    <group name=\"group23\"/>\n    <group name=\"group230\"/>\n    <group name=\"group231\"/>\n    <group name=\"group232\"/>\n    <group name=\"group233\"/>\n    <group name=\"group234\"/>\n    <group name=\"group235\"/>\n    <group name=\"group236\"/>\n    <group name=\"group237\"/>\n    <group name=\"group238\"/>\n    <group name=\"group239\"/>\n    <group name=\"group24\"/>\n    <group name=\"group240\"/>\n    <group name=\"group241\"/>\n    <group name=\"group242\"/>\n    <group name=\"group243\"/>\n    <group name=\"group244\"/>\n    <group name=\"group245\"/>\n    <group name=\"group246\"/>\n    <group name=\"group247\"/>\n    <group name=\"group248\"/>\n    <group name=\"group249\"/>\n    <group name=\"group25\"/>\n    <group name=\"group250\"/>\n    <group name=\"group251\"/>\n    <group name=\"group252\"/>\n    <group name=\"group253\"/>\n    <group name=\"group254\"/>\n    <group name=\"group255\"/>\n    <group name=\"group256\"/>\n    <group name=\"group257\"/>\n    <group name=\"group258\"/>\n    <group name=\"group259\"/>\n    <group name=\"group26\"/>\n    <group name=\"group260\"/>\n    <group name=\"group261\"/>\n    <group name=\"group262\"/>\n    <group name=\"group263\"/>\n    <group name=\"group264\"/>\n    <group name=\"group265\"/>\n    <group name=\"group266\"/>\n    <group name=\"group267\"/>\n    <group name=\"group268\"/>\n    <group name=\"group269\"/>\n    <group name=\"group27\"/>\n    <group name=\"group270\"/>\n    <group name=\"group271\"/>\n    <group name=\"group272\"/>\n    <group name=\"group273\"/>\n    <group name=\"group274\"/>\n    <group name=\"group275\"/>\n    <group name=\"group276\"/>\n    <group name=\"group277\"/>\n    <group name=\"group278\"/>\n    <group name=\"group279\"/>\n    <group name=\"group28\"/>\n    <group name=\"group280\"/>\n    <group name=\"group281\"/>\n    <group name=\"group282\"/>\n    <group name=\"group283\"/>\n    <group name=\"group284\"/>\n    <group name=\"group285\"/>\n    <group name=\"group286\"/>\n    <group name=\"group287\"/>\n    <group name=\"group288\"/>\n    <group name=\"group289\"/>\n    <group name=\"group29\"/>\n    <group name=\"group290\"/>\n    <group name=\"group291\"/>\n    <group name=\"group292\"/>\n    <group name=\"group293\"/>\n    <group name=\"group294\"/>\n    <group name=\"group295\"/>\n    <group name=\"group296\"/>\n    <group name=\"group297\"/>\n    <group name=\"group298\"/>\n    <group name=\"group299\"/>\n    <group name=\"group3\"/>\n    <group name=\"group30\"/>\n    <group name=\"group300\"/>\n    <group name=\"group301\"/>\n    <group name=\"group302\"/>\n    <group name=\"group303\"/>\n    <group name=\"group304\"/>\n    <group name=\"group305\"/>\n    <group name=\"group306\"/>\n    <group name=\"group307\"/>\n    <group name=\"group308\"/>\n    <group name=\"group309\"/>\n    <group name=\"group31\"/>\n    <group name=\"group310\"/>\n    <group name=\"group311\"/>\n    <group name=\"group312\"/>\n    <group name=\"group313\"/>\n    <group name=\"group314\"/>\n    <group name=\"group315\"/>\n    <group name=\"group316\"/>\n    <group name=\"group317\"/>\n    <group name=\"group318\"/>\n    <group name=\"group319\"/>\n    <group name=\"group32\"/>\n    <group name=\"group320\"/>\n    <group name=\"group321\"/>\n    <group name=\"group322\"/>\n    <group name=\"group323\"/>\n    <group name=\"group324\"/>\n    <group name=\"group325\"/>\n    <group name=\"group326\"/>\n    <group name=\"group327\"/>\n    <group name=\"group328\"/>\n    <group name=\"group329\"/>\n    <group name=\"group33\"/>\n    <group name=\"group330\"/>\n    <group name=\"group331\"/>\n    <group name=\"group332\"/>\n    <group name=\"group333\"/>\n    <group name=\"group334\"/>\n    <group name=\"group335\"/>\n    <group name=\"group336\"/>\n    <group name=\"group337\"/>\n    <group name=\"group338\"/>\n    <group name=\"group339\"/>\n    <group name=\"group34\"/>\n    <group name=\"group340\"/>\n    <group name=\"group341\"/>\n    <group name=\"group342\"/>\n    <group name=\"group343\"/>\n    <group name=\"group344\"/>\n    <group name=\"group345\"/>\n    <group name=\"group346\"/>\n    <group name=\"group347\"/>\n    <group name=\"group348\"/>\n    <group name=\"group349\"/>\n    <group name=\"group35\"/>\n    <group name=\"group350\"/>\n    <group name=\"group351\"/>\n    <group name=\"group352\"/>\n    <group name=\"group353\"/>\n    <group name=\"group354\"/>\n    <group name=\"group355\"/>\n    <group name=\"group356\"/>\n    <group name=\"group357\"/>\n    <group name=\"group358\"/>\n    <group name=\"group359\"/>\n    <group name=\"group36\"/>\n    <group name=\"group360\"/>\n    <group name=\"group361\"/>\n    <group name=\"group362\"/>\n    <group name=\"group363\"/>\n    <group name=\"group364\"/>\n    <group name=\"group365\"/>\n    <group name=\"group366\"/>\n    <group name=\"group367\"/>\n    <group name=\"group368\"/>\n    <group name=\"group369\"/>\n    <group name=\"group37\"/>\n    <group name=\"group370\"/>\n    <group name=\"group371\"/>\n    <group name=\"group372\"/>\n    <group name=\"group373\"/>\n    <group name=\"group374\"/>\n    <group name=\"group375\"/>\n    <group name=\"group376\"/>\n    <group name=\"group377\"/>\n    <group name=\"group378\"/>\n    <group name=\"group379\"/>\n    <group name=\"group38\"/>\n    <group name=\"group380\"/>\n    <group name=\"group381\"/>\n    <group name=\"group382\"/>\n    <group name=\"group383\"/>\n    <group name=\"group384\"/>\n    <group name=\"group385\"/>\n    <group name=\"group386\"/>\n    <group name=\"group387\"/>\n    <group name=\"group388\"/>\n    <group name=\"group389\"/>\n    <group name=\"group39\"/>\n    <group name=\"group390\"/>\n    <group name=\"group391\"/>\n    <group name=\"group392\"/>\n    <group name=\"group393\"/>\n    <group name=\"group394\"/>\n    <group name=\"group395\"/>\n    <group name=\"group396\"/>\n    <group name=\"group397\"/>\n    <group name=\"group398\"/>\n    <group name=\"group399\"/>\n    <group name=\"group4\"/>\n    <group name=\"group40\"/>\n    <group name=\"group400\"/>\n    <group name=\"group401\"/>\n    <group name=\"group402\"/>\n    <group name=\"group403\"/>\n    <group name=\"group404\"/>\n    <group name=\"group405\"/>\n    <group name=\"group406\"/>\n    <group name=\"group407\"/>\n    <group name=\"group408\"/>\n    <group name=\"group409\"/>\n    <group name=\"group41\"/>\n    <group name=\"group410\"/>\n    <group name=\"group411\"/>\n    <group name=\"group412\"/>\n    <group name=\"group413\"/>\n    <group name=\"group414\"/>\n    <group name=\"group415\"/>\n    <group name=\"group416\"/>\n    <group name=\"group417\"/>\n    <group name=\"group418\"/>\n    <group name=\"group419\"/>\n    <group name=\"group42\"/>\n    <group name=\"group420\"/>\n    <group name=\"group421\"/>\n    <group name=\"group422\"/>\n    <group name=\"group423\"/>\n    <group name=\"group424\"/>\n    <group name=\"group425\"/>\n    <group name=\"group426\"/>\n    <group name=\"group427\"/>\n    <group name=\"group428\"/>\n    <group name=\"group429\"/>\n    <group name=\"group43\"/>\n    <group name=\"group430\"/>\n    <group name=\"group431\"/>\n    <group name=\"group432\"/>\n    <group name=\"group433\"/>\n    <group name=\"group434\"/>\n    <group name=\"group435\"/>\n    <group name=\"group436\"/>\n    <group name=\"group437\"/>\n    <group name=\"group438\"/>\n    <group name=\"group439\"/>\n    <group name=\"group44\"/>\n    <group name=\"group440\"/>\n    <group name=\"group441\"/>\n    <group name=\"group442\"/>\n    <group name=\"group443\"/>\n    <group name=\"group444\"/>\n    <group name=\"group445\"/>\n    <group name=\"group446\"/>\n    <group name=\"group447\"/>\n    <group name=\"group448\"/>\n    <group name=\"group449\"/>\n    <group name=\"group45\"/>\n    <group name=\"group450\"/>\n    <group name=\"group451\"/>\n    <group name=\"group452\"/>\n    <group name=\"group453\"/>\n    <group name=\"group454\"/>\n    <group name=\"group455\"/>\n    <group name=\"group456\"/>\n    <group name=\"group457\"/>\n    <group name=\"group458\"/>\n    <group name=\"group459\"/>\n    <group name=\"group46\"/>\n    <group name=\"group460\"/>\n    <group name=\"group461\"/>\n    <group name=\"group462\"/>\n    <group name=\"group463\"/>\n    <group name=\"group464\"/>\n    <group name=\"group465\"/>\n    <group name=\"group466\"/>\n    <group name=\"group467\"/>\n    <group name=\"group468\"/>\n    <group name=\"group469\"/>\n    <group name=\"group47\"/>\n    <group name=\"group470\"/>\n    <group name=\"group471\"/>\n    <group name=\"group472\"/>\n    <group name=\"group473\"/>\n    <group name=\"group474\"/>\n    <group name=\"group475\"/>\n    <group name=\"group476\"/>\n    <group name=\"group477\"/>\n    <group name=\"group478\"/>\n    <group name=\"group479\"/>\n    <group name=\"group48\"/>\n    <group name=\"group480\"/>\n    <group name=\"group481\"/>\n    <group name=\"group482\"/>\n    <group name=\"group483\"/>\n    <group name=\"group484\"/>\n    <group name=\"group485\"/>\n    <group name=\"group486\"/>\n    <group name=\"group487\"/>\n    <group name=\"group488\"/>\n    <group name=\"group489\"/>\n    <group name=\"group49\"/>\n    <group name=\"group490\"/>\n    <group name=\"group491\"/>\n    <group name=\"group492\"/>\n    <group name=\"group493\"/>\n    <group name=\"group494\"/>\n    <group name=\"group495\"/>\n    <group name=\"group496\"/>\n    <group name=\"group497\"/>\n    <group name=\"group498\"/>\n    <group name=\"group499\"/>\n    <group name=\"group5\"/>\n    <group name=\"group50\"/>\n    <group name=\"group500\"/>\n    <group name=\"group501\"/>\n    <group name=\"group502\"/>\n    <group name=\"group503\"/>\n    <group name=\"group504\"/>\n    <group name=\"group505\"/>\n    <group name=\"group506\"/>\n    <group name=\"group507\"/>\n    <group name=\"group508\"/>\n    <group name=\"group509\"/>\n    <group name=\"group51\"/>\n    <group name=\"group510\"/>\n    <group name=\"group511\"/>\n    <group name=\"group512\"/>\n    <group name=\"group513\"/>\n    <group name=\"group514\"/>\n    <group name=\"group515\"/>\n    <group name=\"group516\"/>\n    <group name=\"group517\"/>\n    <group name=\"group518\"/>\n    <group name=\"group519\"/>\n    <group name=\"group52\"/>\n    <group name=\"group520\"/>\n    <group name=\"group521\"/>\n    <group name=\"group522\"/>\n    <group name=\"group523\"/>\n    <group name=\"group524\"/>\n    <group name=\"group525\"/>\n    <group name=\"group526\"/>\n    <group name=\"group527\"/>\n    <group name=\"group528\"/>\n    <group name=\"group529\"/>\n    <group name=\"group53\"/>\n    <group name=\"group530\"/>\n    <group name=\"group531\"/>\n    <group name=\"group532\"/>\n    <group name=\"group533\"/>\n    <group name=\"group534\"/>\n    <group name=\"group535\"/>\n    <group name=\"group536\"/>\n    <group name=\"group537\"/>\n    <group name=\"group538\"/>\n    <group name=\"group539\"/>\n    <group name=\"group54\"/>\n    <group name=\"group540\"/>\n    <group name=\"group541\"/>\n    <group name=\"group542\"/>\n    <group name=\"group543\"/>\n    <group name=\"group544\"/>\n    <group name=\"group545\"/>\n    <group name=\"group546\"/>\n    <group name=\"group547\"/>\n    <group name=\"group548\"/>\n    <group name=\"group549\"/>\n    <group name=\"group55\"/>\n    <group name=\"group550\"/>\n    <group name=\"group551\"/>\n    <group name=\"group552\"/>\n    <group name=\"group553\"/>\n    <group name=\"group554\"/>\n    <group name=\"group555\"/>\n    <group name=\"group556\"/>\n    <group name=\"group557\"/>\n    <group name=\"group558\"/>\n    <group name=\"group559\"/>\n    <group name=\"group56\"/>\n    <group name=\"group560\"/>\n    <group name=\"group561\"/>\n    <group name=\"group562\"/>\n    <group name=\"group563\"/>\n    <group name=\"group564\"/>\n    <group name=\"group565\"/>\n    <group name=\"group566\"/>\n    <group name=\"group567\"/>\n    <group name=\"group568\"/>\n    <group name=\"group569\"/>\n    <group name=\"group57\"/>\n    <group name=\"group570\"/>\n    <group name=\"group571\"/>\n    <group name=\"group572\"/>\n    <group name=\"group573\"/>\n    <group name=\"group574\"/>\n    <group name=\"group575\"/>\n    <group name=\"group576\"/>\n    <group name=\"group577\"/>\n    <group name=\"group578\"/>\n    <group name=\"group579\"/>\n    <group name=\"group58\"/>\n    <group name=\"group580\"/>\n    <group name=\"group581\"/>\n    <group name=\"group582\"/>\n    <group name=\"group583\"/>\n    <group name=\"group584\"/>\n    <group name=\"group585\"/>\n    <group name=\"group586\"/>\n    <group name=\"group587\"/>\n    <group name=\"group588\"/>\n    <group name=\"group589\"/>\n    <group name=\"group59\"/>\n    <group name=\"group590\"/>\n    <group name=\"group591\"/>\n    <group name=\"group592\"/>\n    <group name=\"group593\"/>\n    <group name=\"group594\"/>\n    <group name=\"group595\"/>\n    <group name=\"group596\"/>\n    <group name=\"group597\"/>\n    <group name=\"group598\"/>\n    <group name=\"group599\"/>\n    <group name=\"group6\"/>\n    <group name=\"group60\"/>\n    <group name=\"group600\"/>\n    <group name=\"group601\"/>\n    <group name=\"group602\"/>\n    <group name=\"group603\"/>\n    <group name=\"group604\"/>\n    <group name=\"group605\"/>\n    <group name=\"group606\"/>\n    <group name=\"group607\"/>\n    <group name=\"group608\"/>\n    <group name=\"group609\"/>\n    <group name=\"group61\"/>\n    <group name=\"group610\"/>\n    <group name=\"group611\"/>\n    <group name=\"group612\"/>\n    <group name=\"group613\"/>\n    <group name=\"group614\"/>\n    <group name=\"group615\"/>\n    <group name=\"group616\"/>\n    <group name=\"group617\"/>\n    <group name=\"group618\"/>\n    <group name=\"group619\"/>\n    <group name=\"group62\"/>\n    <group name=\"group620\"/>\n    <group name=\"group621\"/>\n    <group name=\"group622\"/>\n    <group name=\"group623\"/>\n    <group name=\"group624\"/>\n    <group name=\"group625\"/>\n    <group name=\"group626\"/>\n    <group name=\"group627\"/>\n    <group name=\"group628\"/>\n    <group name=\"group629\"/>\n    <group name=\"group63\"/>\n    <group name=\"group630\"/>\n    <group name=\"group631\"/>\n    <group name=\"group632\"/>\n    <group name=\"group633\"/>\n    <group name=\"group634\"/>\n    <group name=\"group635\"/>\n    <group name=\"group636\"/>\n    <group name=\"group637\"/>\n    <group name=\"group638\"/>\n    <group name=\"group639\"/>\n    <group name=\"group64\"/>\n    <group name=\"group640\"/>\n    <group name=\"group641\"/>\n    <group name=\"group642\"/>\n    <group name=\"group643\"/>\n    <group name=\"group644\"/>\n    <group name=\"group645\"/>\n    <group name=\"group646\"/>\n    <group name=\"group647\"/>\n    <group name=\"group648\"/>\n    <group name=\"group649\"/>\n    <group name=\"group65\"/>\n    <group name=\"group650\"/>\n    <group name=\"group651\"/>\n    <group name=\"group652\"/>\n    <group name=\"group653\"/>\n    <group name=\"group654\"/>\n    <group name=\"group655\"/>\n    <group name=\"group656\"/>\n    <group name=\"group657\"/>\n    <group name=\"group658\"/>\n    <group name=\"group659\"/>\n    <group name=\"group66\"/>\n    <group name=\"group660\"/>\n    <group name=\"group661\"/>\n    <group name=\"group662\"/>\n    <group name=\"group663\"/>\n    <group name=\"group664\"/>\n    <group name=\"group665\"/>\n    <group name=\"group666\"/>\n    <group name=\"group667\"/>\n    <group name=\"group668\"/>\n    <group name=\"group669\"/>\n    <group name=\"group67\"/>\n    <group name=\"group670\"/>\n    <group name=\"group671\"/>\n    <group name=\"group672\"/>\n    <group name=\"group673\"/>\n    <group name=\"group674\"/>\n    <group name=\"group675\"/>\n    <group name=\"group676\"/>\n    <group name=\"group677\"/>\n    <group name=\"group678\"/>\n    <group name=\"group679\"/>\n    <group name=\"group68\"/>\n    <group name=\"group680\"/>\n    <group name=\"group681\"/>\n    <group name=\"group682\"/>\n    <group name=\"group683\"/>\n    <group name=\"group684\"/>\n    <group name=\"group685\"/>\n    <group name=\"group686\"/>\n    <group name=\"group687\"/>\n    <group name=\"group688\"/>\n    <group name=\"group689\"/>\n    <group name=\"group69\"/>\n    <group name=\"group690\"/>\n    <group name=\"group691\"/>\n    <group name=\"group692\"/>\n    <group name=\"group693\"/>\n    <group name=\"group694\"/>\n    <group name=\"group695\"/>\n    <group name=\"group696\"/>\n    <group name=\"group697\"/>\n    <group name=\"group698\"/>\n    <group name=\"group699\"/>\n    <group name=\"group7\"/>\n    <group name=\"group70\"/>\n    <group name=\"group700\"/>\n    <group name=\"group701\"/>\n    <group name=\"group702\"/>\n    <group name=\"group703\"/>\n    <group name=\"group704\"/>\n    <group name=\"group705\"/>\n    <group name=\"group706\"/>\n    <group name=\"group707\"/>\n    <group name=\"group708\"/>\n    <group name=\"group709\"/>\n    <group name=\"group71\"/>\n    <group name=\"group710\"/>\n    <group name=\"group711\"/>\n    <group name=\"group712\"/>\n    <group name=\"group713\"/>\n    <group name=\"group714\"/>\n    <group name=\"group715\"/>\n    <group name=\"group716\"/>\n    <group name=\"group717\"/>\n    <group name=\"group718\"/>\n    <group name=\"group719\"/>\n    <group name=\"group72\"/>\n    <group name=\"group720\"/>\n    <group name=\"group721\"/>\n    <group name=\"group722\"/>\n    <group name=\"group723\"/>\n    <group name=\"group724\"/>\n    <group name=\"group725\"/>\n    <group name=\"group726\"/>\n    <group name=\"group727\"/>\n    <group name=\"group728\"/>\n    <group name=\"group729\"/>\n    <group name=\"group73\"/>\n    <group name=\"group730\"/>\n    <group name=\"group731\"/>\n    <group name=\"group732\"/>\n    <group name=\"group733\"/>\n    <group name=\"group734\"/>\n    <group name=\"group735\"/>\n    <group name=\"group736\"/>\n    <group name=\"group737\"/>\n    <group name=\"group738\"/>\n    <group name=\"group739\"/>\n    <group name=\"group74\"/>\n    <group name=\"group740\"/>\n    <group name=\"group741\"/>\n    <group name=\"group742\"/>\n    <group name=\"group743\"/>\n    <group name=\"group744\"/>\n    <group name=\"group745\"/>\n    <group name=\"group746\"/>\n    <group name=\"group747\"/>\n    <group name=\"group748\"/>\n    <group name=\"group749\"/>\n    <group name=\"group75\"/>\n    <group name=\"group750\"/>\n    <group name=\"group751\"/>\n    <group name=\"group752\"/>\n    <group name=\"group753\"/>\n    <group name=\"group754\"/>\n    <group name=\"group755\"/>\n    <group name=\"group756\"/>\n    <group name=\"group757\"/>\n    <group name=\"group758\"/>\n    <group name=\"group759\"/>\n    <group name=\"group76\"/>\n    <group name=\"group760\"/>\n    <group name=\"group761\"/>\n    <group name=\"group762\"/>\n    <group name=\"group763\"/>\n    <group name=\"group764\"/>\n    <group name=\"group765\"/>\n    <group name=\"group766\"/>\n    <group name=\"group767\"/>\n    <group name=\"group768\"/>\n    <group name=\"group769\"/>\n    <group name=\"group77\"/>\n    <group name=\"group770\"/>\n    <group name=\"group771\"/>\n    <group name=\"group772\"/>\n    <group name=\"group773\"/>\n    <group name=\"group774\"/>\n    <group name=\"group775\"/>\n    <group name=\"group776\"/>\n    <group name=\"group777\"/>\n    <group name=\"group778\"/>\n    <group name=\"group779\"/>\n    <group name=\"group78\"/>\n    <group name=\"group780\"/>\n    <group name=\"group781\"/>\n    <group name=\"group782\"/>\n    <group name=\"group783\"/>\n    <group name=\"group784\"/>\n    <group name=\"group785\"/>\n    <group name=\"group786\"/>\n    <group name=\"group787\"/>\n    <group name=\"group788\"/>\n    <group name=\"group789\"/>\n    <group name=\"group79\"/>\n    <group name=\"group790\"/>\n    <group name=\"group791\"/>\n    <group name=\"group792\"/>\n    <group name=\"group793\"/>\n    <group name=\"group794\"/>\n    <group name=\"group795\"/>\n    <group name=\"group796\"/>\n    <group name=\"group797\"/>\n    <group name=\"group798\"/>\n    <group name=\"group799\"/>\n    <group name=\"group8\"/>\n    <group name=\"group80\"/>\n    <group name=\"group800\"/>\n    <group name=\"group801\"/>\n    <group name=\"group802\"/>\n    <group name=\"group803\"/>\n    <group name=\"group804\"/>\n    <group name=\"group805\"/>\n    <group name=\"group806\"/>\n    <group name=\"group807\"/>\n    <group name=\"group808\"/>\n    <group name=\"group809\"/>\n    <group name=\"group81\"/>\n    <group name=\"group810\"/>\n    <group name=\"group811\"/>\n    <group name=\"group812\"/>\n    <group name=\"group813\"/>\n    <group name=\"group814\"/>\n    <group name=\"group815\"/>\n    <group name=\"group816\"/>\n    <group name=\"group817\"/>\n    <group name=\"group818\"/>\n    <group name=\"group819\"/>\n    <group name=\"group82\"/>\n    <group name=\"group820\"/>\n    <group name=\"group821\"/>\n    <group name=\"group822\"/>\n    <group name=\"group823\"/>\n    <group name=\"group824\"/>\n    <group name=\"group825\"/>\n    <group name=\"group826\"/>\n    <group name=\"group827\"/>\n    <group name=\"group828\"/>\n    <group name=\"group829\"/>\n    <group name=\"group83\"/>\n    <group name=\"group830\"/>\n    <group name=\"group831\"/>\n    <group name=\"group832\"/>\n    <group name=\"group833\"/>\n    <group name=\"group834\"/>\n    <group name=\"group835\"/>\n    <group name=\"group836\"/>\n    <group name=\"group837\"/>\n    <group name=\"group838\"/>\n    <group name=\"group839\"/>\n    <group name=\"group84\"/>\n    <group name=\"group840\"/>\n    <group name=\"group841\"/>\n    <group name=\"group842\"/>\n    <group name=\"group843\"/>\n    <group name=\"group844\"/>\n    <group name=\"group845\"/>\n    <group name=\"group846\"/>\n    <group name=\"group847\"/>\n    <group name=\"group848\"/>\n    <group name=\"group849\"/>\n    <group name=\"group85\"/>\n    <group name=\"group850\"/>\n    <group name=\"group851\"/>\n    <group name=\"group852\"/>\n    <group name=\"group853\"/>\n    <group name=\"group854\"/>\n    <group name=\"group855\"/>\n    <group name=\"group856\"/>\n    <group name=\"group857\"/>\n    <group name=\"group858\"/>\n    <group name=\"group859\"/>\n    <group name=\"group86\"/>\n    <group name=\"group860\"/>\n    <group name=\"group861\"/>\n    <group name=\"group862\"/>\n    <group name=\"group863\"/>\n    <group name=\"group864\"/>\n    <group name=\"group865\"/>\n    <group name=\"group866\"/>\n    <group name=\"group867\"/>\n    <group name=\"group868\"/>\n    <group name=\"group869\"/>\n    <group name=\"group87\"/>\n    <group name=\"group870\"/>\n    <group name=\"group871\"/>\n    <group name=\"group872\"/>\n    <group name=\"group873\"/>\n    <group name=\"group874\"/>\n    <group name=\"group875\"/>\n    <group name=\"group876\"/>\n    <group name=\"group877\"/>\n    <group name=\"group878\"/>\n    <group name=\"group879\"/>\n    <group name=\"group88\"/>\n    <group name=\"group880\"/>\n    <group name=\"group881\"/>\n    <group name=\"group882\"/>\n    <group name=\"group883\"/>\n    <group name=\"group884\"/>\n    <group name=\"group885\"/>\n    <group name=\"group886\"/>\n    <group name=\"group887\"/>\n    <group name=\"group888\"/>\n    <group name=\"group889\"/>\n    <group name=\"group89\"/>\n    <group name=\"group890\"/>\n    <group name=\"group891\"/>\n    <group name=\"group892\"/>\n    <group name=\"group893\"/>\n    <group name=\"group894\"/>\n    <group name=\"group895\"/>\n    <group name=\"group896\"/>\n    <group name=\"group897\"/>\n    <group name=\"group898\"/>\n    <group name=\"group899\"/>\n    <group name=\"group9\"/>\n    <group name=\"group90\"/>\n    <group name=\"group900\"/>\n    <group name=\"group901\"/>\n    <group name=\"group902\"/>\n    <group name=\"group903\"/>\n    <group name=\"group904\"/>\n    <group name=\"group905\"/>\n    <group name=\"group906\"/>\n    <group name=\"group907\"/>\n    <group name=\"group908\"/>\n    <group name=\"group909\"/>\n    <group name=\"group91\"/>\n    <group name=\"group910\"/>\n    <group name=\"group911\"/>\n    <group name=\"group912\"/>\n    <group name=\"group913\"/>\n    <group name=\"group914\"/>\n    <group name=\"group915\"/>\n    <group name=\"group916\"/>\n    <group name=\"group917\"/>\n    <group name=\"group918\"/>\n    <group name=\"group919\"/>\n    <group name=\"group92\"/>\n    <group name=\"group920\"/>\n    <group name=\"group921\"/>\n    <group name=\"group922\"/>\n    <group name=\"group923\"/>\n    <group name=\"group924\"/>\n    <group name=\"group925\"/>\n    <group name=\"group926\"/>\n    <group name=\"group927\"/>\n    <group name=\"group928\"/>\n    <group name=\"group929\"/>\n    <group name=\"group93\"/>\n    <group name=\"group930\"/>\n    <group name=\"group931\"/>\n    <group name=\"group932\"/>\n    <group name=\"group933\"/>\n    <group name=\"group934\"/>\n    <group name=\"group935\"/>\n    <group name=\"group936\"/>\n    <group name=\"group937\"/>\n    <group name=\"group938\"/>\n    <group name=\"group939\"/>\n    <group name=\"group94\"/>\n    <group name=\"group940\"/>\n    <group name=\"group941\"/>\n    <group name=\"group942\"/>\n    <group name=\"group943\"/>\n    <group name=\"group944\"/>\n    <group name=\"group945\"/>\n    <group name=\"group946\"/>\n    <group name=\"group947\"/>\n    <group name=\"group948\"/>\n    <group name=\"group949\"/>\n    <group name=\"group95\"/>\n    <group name=\"group950\"/>\n    <group name=\"group951\"/>\n    <group name=\"group952\"/>\n    <group name=\"group953\"/>\n    <group name=\"group954\"/>\n    <group name=\"group955\"/>\n    <group name=\"group956\"/>\n    <group name=\"group957\"/>\n    <group name=\"group958\"/>\n    <group name=\"group959\"/>\n    <group name=\"group96\"/>\n    <group name=\"group960\"/>\n    <group name=\"group961\"/>\n    <group name=\"group962\"/>\n    <group name=\"group963\"/>\n    <group name=\"group964\"/>\n    <group name=\"group965\"/>\n    <group name=\"group966\"/>\n    <group name=\"group967\"/>\n    <group name=\"group968\"/>\n    <group name=\"group969\"/>\n    <group name=\"group97\"/>\n    <group name=\"group970\"/>\n    <group name=\"group971\"/>\n    <group name=\"group972\"/>\n    <group name=\"group973\"/>\n    <group name=\"group974\"/>\n    <group name=\"group975\"/>\n    <group name=\"group976\"/>\n    <group name=\"group977\"/>\n    <group name=\"group978\"/>\n    <group name=\"group979\"/>\n    <group name=\"group98\"/>\n    <group name=\"group980\"/>\n    <group name=\"group981\"/>\n    <group name=\"group982\"/>\n    <group name=\"group983\"/>\n    <group name=\"group984\"/>\n    <group name=\"group985\"/>\n    <group name=\"group986\"/>\n    <group name=\"group987\"/>\n    <group name=\"group988\"/>\n    <group name=\"group989\"/>\n    <group name=\"group99\"/>\n    <group name=\"group990\"/>\n    <group name=\"group991\"/>\n    <group name=\"group992\"/>\n    <group name=\"group993\"/>\n    <group name=\"group994\"/>\n    <group name=\"group995\"/>\n    <group name=\"group996\"/>\n    <group name=\"group997\"/>\n    <group name=\"group998\"/>\n    <group name=\"group999\"/>\n  </groups>\n  <memberships/>\n</organization:Organization>"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/identity/mixOrganization.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<organization:Organization xmlns:organization=\"http://documentation.bonitasoft.com/organization-xml-schema\">\n  <users>\n    <user userName=\"matti\">\n      <password encrypted=\"true\">bpm</password>\n      <enabled>true</enabled>\n      <personalData/>\n      <professionalData/>\n    </user>\n    <user userName=\"petteri\">\n      <password encrypted=\"false\">bpm</password>\n      <enabled>true</enabled>\n      <personalData/>\n      <professionalData/>\n    </user>\n  </users>\n</organization:Organization>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/identity/organizationFailOnDuplicates.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<organization:Organization xmlns:organization=\"http://documentation.bonitasoft.com/organization-xml-schema\">\n  <users>\n    <user userName=\"james\">\n      <password encrypted=\"false\">passwordOfJames</password>\n      <personalData/>\n      <professionalData/>\n    </user>\n    <user userName=\"john\">\n      <password encrypted=\"false\">bpm</password>\n      <firstname>NotJohn</firstname>\n      <personalData/>\n      <professionalData/>\n    </user>\n  </users>\n  <roles/>\n  <groups/>\n  <memberships/>\n</organization:Organization>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/identity/organizationWithCycle.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<organization:Organization xmlns:organization=\"http://documentation.bonitasoft.com/organization-xml-schema\">\n  <users>\n    <user userName=\"user1\">\n      <password encrypted=\"false\">bpm</password>\n      <manager>user2</manager>\n      <personalData/>\n      <professionalData/>\n    </user>\n    <user userName=\"user2\">\n      <password encrypted=\"false\">bpm</password>\n      <manager>user3</manager>\n      <personalData/>\n      <professionalData/>\n    </user>\n    <user userName=\"user3\">\n      <password encrypted=\"false\">bpm</password>\n       <manager>user1</manager>\n      <personalData/>\n      <professionalData/>\n    </user>\n  </users>\n  <roles/>\n  <groups/>\n  <memberships/>\n</organization:Organization>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/identity/simpleOrganization.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<organization:Organization xmlns:organization=\"http://documentation.bonitasoft.com/organization-xml-schema\">\n  <customUserInfoDefinitions>\n    <customUserInfoDefinition>\n      <name>Office location</name>\n    </customUserInfoDefinition>\n    <customUserInfoDefinition>\n      <name>Skills</name>\n      <description>The user skills</description>\n    </customUserInfoDefinition>\n  </customUserInfoDefinitions>\n  <users>\n    <user userName=\"anthony.birembault\">\n      <password encrypted=\"false\">bpm</password>\n      <jobTitle>Web Team Manager</jobTitle>\n      <enabled>false</enabled>\n      <personalData/>\n      <professionalData/>\n      <customUserInfoValues>\n\t    <customUserInfoValue>\n\t      <name>Office location</name>\n\t      <value>Engineering</value>\n\t    </customUserInfoValue>\n\t    <customUserInfoValue>\n\t      <name>Skills</name>\n\t      <value>Java</value>\n\t    </customUserInfoValue>\n\t  </customUserInfoValues>\n    </user>\n    <user userName=\"liuyanyan\">\n      <password encrypted=\"false\">bpm</password>\n      <enabled>true</enabled>\n      <personalData/>\n      <professionalData/>\n      <customUserInfoValues>\n\t    <customUserInfoValue>\n\t      <name>Skills</name>\n\t      <value>Java</value>\n\t    </customUserInfoValue>\n\t  </customUserInfoValues>\n    </user>\n  </users>\n  <roles>\n    <role name=\"Developer\">\n      <displayName>Bonita developer</displayName>\n    </role>\n    <role name=\"Manager\">\n      <displayName>Bonita Manager</displayName>\n    </role>\n  </roles>\n  <groups>\n    <group name=\"Engine\">\n      <displayName>engine team</displayName>\n    </group>\n    <group name=\"Web\">\n      <displayName>web team</displayName>\n    </group>\n  </groups>\n  <memberships>\n    <membership>\n      <userName>liuyanyan</userName>\n      <roleName>Developer</roleName>\n      <groupName>Engine</groupName>\n    </membership>\n    <membership>\n      <userName>anthony.birembault</userName>\n      <roleName>Manager</roleName>\n      <groupName>Web</groupName>\n    </membership>\n  </memberships>\n</organization:Organization>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/identity/simpleOrganizationDuplicates1.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<organization:Organization xmlns:organization=\"http://documentation.bonitasoft.com/organization-xml-schema\">\n\t<customUserInfoDefinitions>\n\t\t<customUserInfoDefinition>\n\t\t\t<name>Office location</name>\n\t\t</customUserInfoDefinition>\n\t\t<customUserInfoDefinition>\n\t\t\t<name>Skills</name>\n\t\t\t<description>The user skills</description>\n\t\t</customUserInfoDefinition>\n\t</customUserInfoDefinitions>\n  <users>\n    <user userName=\"anthony.birembault\">\n      <password encrypted=\"false\">bpm</password>\n      <jobTitle>Web Team Manager</jobTitle>\n      <personalData/>\n      <professionalData/>\n      <customUserInfoValues>\n\t    <customUserInfoValue>\n\t      <name>Office location</name>\n\t      <value>Engineering</value>\n\t    </customUserInfoValue>\n\t    <customUserInfoValue>\n\t      <name>Skills</name>\n\t      <value>Java</value>\n\t    </customUserInfoValue>\n\t  </customUserInfoValues>\n    </user>\n    <user userName=\"liuyanyan\">\n      <password encrypted=\"false\">bpm</password>\n      <personalData/>\n      <professionalData/>\n      <customUserInfoValues>\n\t    <customUserInfoValue>\n\t      <name>Skills</name>\n\t      <value>Java</value>\n\t    </customUserInfoValue>\n\t  </customUserInfoValues>\n    </user>\n  </users>\n  <roles>\n    <role name=\"Developer\">\n      <displayName>Bonita developer</displayName>\n    </role>\n    <role name=\"Manager\">\n      <displayName>Bonita Manager</displayName>\n    </role>\n  </roles>\n  <groups>\n    <group name=\"Engine\">\n      <displayName>engine team</displayName>\n    </group>\n    <group name=\"Web\">\n      <displayName>web team</displayName>\n    </group>\n  </groups>\n  <memberships>\n    <membership>\n      <userName>liuyanyan</userName>\n      <roleName>Developer</roleName>\n      <groupName>Engine</groupName>\n    </membership>\n    <membership>\n      <userName>anthony.birembault</userName>\n      <roleName>Manager</roleName>\n      <groupName>Web</groupName>\n    </membership>\n  </memberships>\n</organization:Organization>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/identity/simpleOrganizationDuplicates2.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<organization:Organization xmlns:organization=\"http://documentation.bonitasoft.com/organization-xml-schema\">\n\t<customUserInfoDefinitions>\n\t\t<customUserInfoDefinition>\n\t\t\t<name>Office location</name>\n\t\t\t<description>The office location</description>\n\t\t</customUserInfoDefinition>\n\t\t<customUserInfoDefinition>\n\t\t\t<name>Skills</name>\n\t\t\t<description>The user skills were updated</description>\n\t\t</customUserInfoDefinition>\n\t</customUserInfoDefinitions>\n  <users>\n    <user userName=\"johnnyfootball\">\n      <password encrypted=\"false\">bpm</password>\n      <jobTitle>QA Analyst</jobTitle>\n      <enabled>false</enabled>\n      <personalData/>\n      <professionalData/>\n    </user>\n    <user userName=\"liuyanyan\">\n      <password encrypted=\"false\">bpm2</password>\n      <enabled>true</enabled>\n      <personalData/>\n      <professionalData/>\n      <customUserInfoValues>\n\t    <customUserInfoValue>\n\t      <name>Office location</name>\n\t      <value>Engineering</value>\n\t    </customUserInfoValue>\n\t    <customUserInfoValue>\n\t      <name>Skills</name>\n\t      <value>Java, Groovy</value>\n\t    </customUserInfoValue>\n\t  </customUserInfoValues>\n    </user>\n  </users>\n  <roles>\n    <role name=\"Developer\">\n      <displayName>Bonitasoft developer</displayName>\n    </role>\n    <role name=\"Tester\">\n      <displayName>Bonita tester</displayName>\n    </role>\n  </roles>\n  <groups>\n    <group name=\"Engine\">\n      <displayName>RD engine team</displayName>\n    </group>\n    <group name=\"QA\">\n      <displayName>QA team</displayName>\n    </group>\n  </groups>\n  <memberships>\n    <membership>\n      <userName>liuyanyan</userName>\n      <roleName>Developer</roleName>\n      <groupName>Engine</groupName>\n    </membership>\n    <membership>\n      <userName>johnnyfootball</userName>\n      <roleName>Tester</roleName>\n      <groupName>QA</groupName>\n    </membership>\n  </memberships>\n</organization:Organization>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/identity/simpleOrganizationNoDuplicates.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<organization:Organization xmlns:organization=\"http://documentation.bonitasoft.com/organization-xml-schema\">\n  <users>\n    <user userName=\"johnnyfootball\">\n      <password encrypted=\"false\">bpm</password>\n      <jobTitle>QA Analyst</jobTitle>\n      <personalData/>\n      <professionalData/>\n    </user>\n  </users>\n  <roles>\n    <role name=\"Tester\">\n      <displayName>Bonita tester</displayName>\n    </role>\n  </roles>\n  <groups>\n    <group name=\"QA\">\n      <displayName>QA team</displayName>\n    </group>\n  </groups>\n  <memberships>\n    <membership>\n      <userName>johnnyfootball</userName>\n      <roleName>Tester</roleName>\n      <groupName>QA</groupName>\n    </membership>\n  </memberships>\n</organization:Organization>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/process/actor/actorMappingWithException.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<actormappings:actorMappings xmlns:actormappings=\"http://www.bonitasoft.org/ns/actormapping/6.0\">\n\t<actorMapping name=\"Employee actor\">\n\t\t<users>\n\t\t\t<user>john</user>\n\t\t</users>\n\t\t<plop></plop>\n\t</actorMapping>\n</actormappings:actorMappings>"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/process/actor/complexActorMapping.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<actorMappings:actorMappings xmlns:actorMappings=\"http://www.bonitasoft.org/ns/actormapping/6.0\">\n    <actorMapping name=\"Employee actor\">\n        <users>\n            <user>john</user>\n        </users>\n        <groups>\n            <group>/RD</group>\n        </groups>\n        <roles>\n            <role>dev</role>\n        </roles>\n        <memberships>\n            <membership>\n                <role>dev</role>\n                <group>/RD</group>\n            </membership>\n        </memberships>\n    </actorMapping>\n</actorMappings:actorMappings>"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/process/actor/complexActorMapping2.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<actormappings:actorMappings xmlns:actormappings=\"http://www.bonitasoft.org/ns/actormapping/6.0\">\n\t<actorMapping name=\"Employee actor\">\n\t\t<roles>\n\t\t\t<role>dev</role>\n\t\t</roles>\n\t\t<groups>\n\t\t\t<group>/RD</group>\n\t\t</groups>\n\t\t<memberships>\n\t\t\t<membership>\n\t\t\t\t<role>dev</role>\n\t\t\t\t<group>/RD</group>\n\t\t\t</membership>\n\t\t</memberships>\n\t</actorMapping>\n</actormappings:actorMappings>"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/process/actor/complexActorMappingWithUnkownGroup.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<actormappings:actorMappings xmlns:actormappings=\"http://www.bonitasoft.org/ns/actormapping/6.0\">\n\t<actorMapping name=\"Employee actor\">\n\t\t<users>\n\t\t\t<user>john</user>\n\t\t</users>\n\t\t<roles>\n\t\t\t<role>dev</role>\n\t\t</roles>\n\n\t\t<memberships>\n\t\t\t<membership>\n\t\t\t\t<role>dev</role>\n\t\t\t\t<group>/RD</group>\n\t\t\t</membership>\n\t\t</memberships>\n\t</actorMapping>\n</actormappings:actorMappings>"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/process/actor/simpleActorMapping.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<actorMappings:actorMappings xmlns:actorMappings=\"http://www.bonitasoft.org/ns/actormapping/6.0\">\n    <actorMapping name=\"Employee actor\">\n        <users>\n            <user>william.jobs</user>\n        </users>\n        <groups />\n        <roles />\n        <memberships />\n    </actorMapping>\n</actorMappings:actorMappings>"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/MultiThreadCallsIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.test.APITestUtil;\nimport org.junit.Assert;\nimport org.junit.Test;\n\npublic class MultiThreadCallsIT extends CommonAPIIT {\n\n    class CallAPIMethodsThread extends Thread {\n\n        private Exception exception;\n\n        private final APITestUtil apiTestUtil = new APITestUtil();\n\n        public CallAPIMethodsThread() {\n        }\n\n        @Override\n        public void run() {\n            super.run();\n            try {\n                apiTestUtil.loginWithTechnicalUser();\n                apiTestUtil.getIdentityAPI().getNumberOfUsers();\n                apiTestUtil.getIdentityAPI().getNumberOfGroups();\n                apiTestUtil.logout();\n            } catch (final Exception e) {\n                exception = e;\n                e.printStackTrace();\n            }\n        }\n\n        public Exception getException() {\n            return exception;\n        }\n\n        public String getMessage() {\n            if (exception != null) {\n                return exception.getMessage();\n            }\n            return \"\";\n        }\n    }\n\n    @Test\n    public void supportMultiThreadingClients() throws Exception {\n        final int nbOfThreads = 5;\n        final List<CallAPIMethodsThread> threads = new ArrayList<>(nbOfThreads);\n        for (int i = 0; i < nbOfThreads; i++) {\n            threads.add(new CallAPIMethodsThread());\n        }\n        for (final CallAPIMethodsThread thread : threads) {\n            thread.start();\n        }\n        for (final CallAPIMethodsThread thread : threads) {\n            thread.join();\n        }\n        for (final CallAPIMethodsThread thread : threads) {\n            Assert.assertNull(thread.getMessage(), thread.getException());\n        }\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/RemoteEngineIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.fail;\n\nimport org.bonitasoft.engine.api.ApiAccessType;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstanceNotFoundException;\nimport org.bonitasoft.engine.util.APITypeManager;\nimport org.junit.Test;\n\n/**\n * Test all things related to remote connection\n *\n * @author Baptiste Mesta\n */\npublic class RemoteEngineIT extends TestWithTechnicalUser {\n\n    /*\n     * check that we return the stack server exception but not the server exception itself that is not known to the\n     * client\n     */\n    @Test\n    public void check_remote_exception_is_given_to_client() throws Exception {\n        try {\n            getProcessAPI().getFlowNodeInstance(123456789L);\n            fail(\"should fail\");\n        } catch (final FlowNodeInstanceNotFoundException e) {\n            //in local, check root cause is here\n            if (APITypeManager.getAPIType() == ApiAccessType.LOCAL) {\n                Throwable rootCause = e;\n                while (rootCause.getCause() != null) {\n                    rootCause = rootCause.getCause();\n                }\n                assertThat(rootCause.getClass().getSimpleName()).isEqualTo(\"SFlowNodeNotFoundException\");\n            } else {\n                //in remote, check the stack trace is preserved\n                assertThat(e.getStackTrace()).anyMatch(s -> s.getClassName().contains(\"SFlowNodeNotFoundException\"));\n                assertThat(e.getCause()).isNull();\n            }\n        }\n\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/StringIndexIT.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.operation.OperatorType.ASSIGNMENT;\n\nimport org.assertj.core.api.Assertions;\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.model.field.FieldType;\nimport org.bonitasoft.engine.bdm.model.field.SimpleField;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.SubProcessDefinitionBuilder;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.expression.InvalidExpressionException;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.operation.LeftOperand;\nimport org.bonitasoft.engine.operation.LeftOperandBuilder;\nimport org.bonitasoft.engine.test.APITestUtil;\nimport org.junit.Test;\n\n/**\n * @author Baptiste Mesta.\n */\npublic class StringIndexIT extends CommonAPIIT {\n\n    @Test\n    public void should_set_string_index_of_current_process_using_operation_in_called_process() throws Exception {\n        loginWithTechnicalUser();\n        User user = createUser(\"john\", \"bpm\");\n        ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"mainProcess\", \"1.0\");\n        builder.addCallActivity(\"call\", stringConstant(\"calledProcess\"), stringConstant(\"1.0\"));\n        builder.setStringIndex(1, \"index1\", stringConstant(\"initial value\"));\n        builder.setStringIndex(2, \"index2\", stringConstant(\"initial value\"));\n        ProcessDefinition mainProcess = deployAndEnableProcess(builder.done());\n\n        builder = new ProcessDefinitionBuilder().createNewInstance(\"calledProcess\", \"1.0\");\n        builder.addAutomaticTask(\"task1\").addOperation(theStringIndex(1), ASSIGNMENT, \"\",\n                stringConstant(\"value from called process\"));\n        builder.addUserTask(\"userTask\", \"john\");\n        builder.addTransition(\"task1\", \"userTask\");\n        builder.addActor(\"john\");\n        builder.setStringIndex(1, \"index1\", stringConstant(\"initial value\"));\n        builder.setStringIndex(2, \"index2\", stringConstant(\"initial value\"));\n        SubProcessDefinitionBuilder subProcessBuilder = builder.addSubProcess(\"signalSubProcess\", true)\n                .getSubProcessBuilder();\n        subProcessBuilder.addStartEvent(\"signalStart\").addSignalEventTrigger(\"mySignal\")\n                .addAutomaticTask(\"subTask\")\n                .addOperation(theStringIndex(2), ASSIGNMENT, \"\", stringConstant(\"value from sub process\"))\n                .addUserTask(\"subUserTask\", \"john\")\n                .addTransition(\"subTask\", \"subUserTask\")\n                .addTransition(\"signalStart\", \"subTask\");\n\n        ProcessDefinition calledProcess = deployAndEnableProcessWithActor(builder.done(), \"john\", user);\n\n        ProcessInstance processInstance = getProcessAPI().startProcess(mainProcess.getId());\n        HumanTaskInstance userTask = waitForUserTaskAndGetIt(\"userTask\");\n        getProcessAPI().sendSignal(\"mySignal\");\n        waitForUserTaskAndGetIt(\"subUserTask\");\n\n        Thread.sleep(2000);\n        Assertions.assertThat(getProcessAPI().getProcessInstance(processInstance.getId()).getStringIndex1())\n                .isEqualTo(\"initial value\");\n        Assertions.assertThat(getProcessAPI().getProcessInstance(processInstance.getId()).getStringIndex2())\n                .isEqualTo(\"initial value\");\n        Assertions\n                .assertThat(getProcessAPI().getProcessInstance(userTask.getParentProcessInstanceId()).getStringIndex1())\n                .isEqualTo(\"value from called process\");\n        Assertions\n                .assertThat(getProcessAPI().getProcessInstance(userTask.getParentProcessInstanceId()).getStringIndex2())\n                .isEqualTo(\"value from sub process\");\n        disableAndDeleteProcess(mainProcess, calledProcess);\n        deleteUser(user);\n    }\n\n    private LeftOperand theStringIndex(int index) {\n        return new LeftOperandBuilder().createSearchIndexLeftOperand(index);\n    }\n\n    @Test\n    public void should_initialize_string_index_in_call_activity() throws Exception {\n        loginWithTechnicalUser();\n        User user = createUser(\"john\", \"bpm\");\n        ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"mainProcess\", \"1.0\");\n        builder.addCallActivity(\"call\", stringConstant(\"calledProcess\"), stringConstant(\"1.0\"));\n        builder.setStringIndex(1, \"index1\", stringConstant(\"value from main process\"));\n        ProcessDefinition mainProcess = deployAndEnableProcess(builder.done());\n\n        builder = new ProcessDefinitionBuilder().createNewInstance(\"calledProcess\", \"1.0\");\n        builder.addAutomaticTask(\"task1\");\n        builder.addUserTask(\"userTask\", \"john\");\n        builder.addTransition(\"task1\", \"userTask\");\n        builder.addActor(\"john\");\n        builder.setStringIndex(1, \"index1\", stringConstant(\"value from sub process\"));\n        ProcessDefinition calledProcess = deployAndEnableProcessWithActor(builder.done(), \"john\", user);\n\n        ProcessInstance processInstance = getProcessAPI().startProcess(mainProcess.getId());\n        HumanTaskInstance userTask = waitForUserTaskAndGetIt(\"userTask\");\n\n        Assertions.assertThat(getProcessAPI().getProcessInstance(processInstance.getId()).getStringIndex1())\n                .isEqualTo(\"value from main process\");\n        Assertions\n                .assertThat(getProcessAPI().getProcessInstance(userTask.getParentProcessInstanceId()).getStringIndex1())\n                .isEqualTo(\"value from sub process\");\n        disableAndDeleteProcess(mainProcess, calledProcess);\n        deleteUser(user);\n    }\n\n    private Expression stringConstant(String value) throws InvalidExpressionException {\n        return new ExpressionBuilder().createConstantStringExpression(value);\n    }\n\n    @Test\n    public void should_be_able_to_initialize_a_search_index_using_a_business_data() throws Exception {\n        loginWithTechnicalUser();\n        final String qualifiedName = \"com.company.test.Bo\";\n        final BusinessObjectModel bom = buildSimpleBom(qualifiedName);\n        assertThat(installBusinessDataModel(bom)).as(\"should have deployed BDM\").isNotNull();\n\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"test\", \"1.2-alpha\");\n        processDefinitionBuilder.addActor(APITestUtil.ACTOR_NAME);\n        final String bizDataName = \"myBizData\";\n\n        final Expression defaultBizDataValue = new ExpressionBuilder().createGroovyScriptExpression(\"createNewBo\",\n                \"new com.company.test.Bo(aField:'Julius')\",\n                qualifiedName);\n        processDefinitionBuilder.addBusinessData(bizDataName, qualifiedName, defaultBizDataValue);\n\n        Expression businessDataExpression = new ExpressionBuilder().createBusinessDataExpression(bizDataName,\n                qualifiedName);\n        Expression javaMethodCallExpression = new ExpressionBuilder().createJavaMethodCallExpression(\"call_java_method\",\n                \"getAField\", \"java.lang.String\",\n                businessDataExpression);\n        processDefinitionBuilder.setStringIndex(1, \"param0\", javaMethodCallExpression);\n\n        processDefinitionBuilder.addUserTask(\"step1\", APITestUtil.ACTOR_NAME);\n        User testUser = createUser(\"john\", \"bpm\");\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),\n                APITestUtil.ACTOR_NAME, testUser);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance, \"step1\");\n\n        assertThat(processInstance.getStringIndex1())\n                .as(\"String index 1 should be initialized with the business data value\").isEqualTo(\"Julius\");\n\n        disableAndDeleteProcess(processDefinition);\n        deleteUser(testUser);\n    }\n\n    private BusinessObjectModel buildSimpleBom(final String boQualifiedName) {\n        final BusinessObject bo = new BusinessObject();\n        bo.setQualifiedName(boQualifiedName);\n        final SimpleField field = new SimpleField();\n        field.setName(\"aField\");\n        field.setType(FieldType.STRING);\n        bo.addField(field);\n        final BusinessObjectModel model = new BusinessObjectModel();\n        model.addBusinessObject(bo);\n        return model;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/activity/CallActivityIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.activity;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.fail;\n\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.commons.io.IOUtils;\nimport org.bonitasoft.engine.CommonAPIIT;\nimport org.bonitasoft.engine.TestWithTechnicalUser;\nimport org.bonitasoft.engine.bpm.bar.BarResource;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.bpm.contract.Type;\nimport org.bonitasoft.engine.bpm.data.DataInstance;\nimport org.bonitasoft.engine.bpm.data.DataNotFoundException;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\nimport org.bonitasoft.engine.bpm.flownode.GatewayType;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceCriterion;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.bpm.process.impl.CallActivityBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.EndEventDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.IntermediateCatchEventDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.supervisor.ProcessSupervisorSearchDescriptor;\nimport org.bonitasoft.engine.connectors.TestConnectorWithOutput;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.ProcessInstanceHierarchicalDeletionException;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.expression.ExpressionConstants;\nimport org.bonitasoft.engine.expression.ExpressionType;\nimport org.bonitasoft.engine.expression.InvalidExpressionException;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.io.IOUtil;\nimport org.bonitasoft.engine.operation.LeftOperandBuilder;\nimport org.bonitasoft.engine.operation.Operation;\nimport org.bonitasoft.engine.operation.OperationBuilder;\nimport org.bonitasoft.engine.operation.OperatorType;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.test.BuildTestUtil;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.bonitasoft.engine.test.wait.WaitForFinalArchivedActivity;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\n@SuppressWarnings(\"javadoc\")\npublic class CallActivityIT extends TestWithTechnicalUser {\n\n    private static final String CONNECTOR_OUTPUT_NAME = \"output1\";\n\n    private User cebolinha;\n\n    private User cascao;\n\n    @Override\n    @Before\n    public void before() throws Exception {\n        super.before();\n        cebolinha = createUser(\"cebolinha\", \"bpm\");\n        cascao = createUser(\"cascao\", \"bpm\");\n    }\n\n    @Override\n    @After\n    public void after() throws Exception {\n        deleteUsers(cebolinha, cascao);\n        super.after();\n    }\n\n    private ProcessDefinition getSimpleProcess(final String ACTOR_NAME, final String processName,\n            final String processVersion, final boolean terminateEnd)\n            throws Exception {\n        final Expression clientNumberExpr = new ExpressionBuilder().createConstantIntegerExpression(10);\n        final Expression protocolNumberExpr = new ExpressionBuilder().createConstantIntegerExpression(305020);\n\n        final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder().createNewInstance(processName,\n                processVersion);\n        processDefBuilder.addActor(ACTOR_NAME);\n        processDefBuilder.addShortTextData(\"firstName\", null);\n        processDefBuilder.addShortTextData(\"lastName\", null);\n        processDefBuilder.addShortTextData(\"calledProcessData\", null);\n        processDefBuilder.addIntegerData(\"clientNumber\", clientNumberExpr);\n        processDefBuilder.addIntegerData(\"protocolNumber\", protocolNumberExpr);\n        processDefBuilder.addStartEvent(\"tStart\");\n        processDefBuilder.addUserTask(\"tStep1\", ACTOR_NAME);\n        final EndEventDefinitionBuilder endEvent = processDefBuilder.addEndEvent(\"tEnd\");\n        if (terminateEnd) {\n            endEvent.addTerminateEventTrigger();\n        }\n        processDefBuilder.addTransition(\"tStart\", \"tStep1\");\n        processDefBuilder.addTransition(\"tStep1\", \"tEnd\");\n\n        final ProcessDefinition targetProcessDefinition = deployAndEnableProcessWithActor(processDefBuilder.done(),\n                ACTOR_NAME, cebolinha);\n\n        return targetProcessDefinition;\n    }\n\n    private ProcessDefinition buildProcessWithCallActivity(final boolean addInputOperations,\n            final boolean addOutputOperations,\n            final String processName, final String targetProcessName, final int loopNb, final String strTargetVersion)\n            throws Exception {\n\n        final Expression targetProcessNameExpr = new ExpressionBuilder()\n                .createConstantStringExpression(targetProcessName);\n        Expression targetProcessVersionExpr = null;\n        if (strTargetVersion != null) {\n            targetProcessVersionExpr = new ExpressionBuilder().createConstantStringExpression(strTargetVersion);\n        }\n        final Expression expressionTrue = new ExpressionBuilder().createConstantBooleanExpression(true);\n\n        final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder().createNewInstance(processName,\n                PROCESS_VERSION);\n        processDefBuilder.addShortTextData(\"fName\", null);\n        processDefBuilder.addShortTextData(\"lName\", null);\n        processDefBuilder.addIntegerData(\"cNumber\", null);\n        processDefBuilder.addIntegerData(\"pNumber\", null);\n        processDefBuilder.addActor(ACTOR_NAME);\n        processDefBuilder.addStartEvent(\"start\");\n        final CallActivityBuilder callActivityBuilder = processDefBuilder.addCallActivity(\"callActivity\",\n                targetProcessNameExpr, targetProcessVersionExpr);\n        addDataInputOperationsIfNeed(addInputOperations, callActivityBuilder);\n        callActivityBuilder.addShortTextData(\"callActivityData\",\n                new ExpressionBuilder().createConstantStringExpression(\"defaultValue\"));\n        if (loopNb > 0) {\n            callActivityBuilder.addLoop(false, expressionTrue,\n                    new ExpressionBuilder().createConstantIntegerExpression(loopNb));\n        }\n        addDataOutputOperationIfNeed(addOutputOperations, callActivityBuilder);\n        processDefBuilder.addUserTask(\"step1\", ACTOR_NAME);\n        processDefBuilder.addEndEvent(\"end\");\n        processDefBuilder.addTransition(\"start\", \"callActivity\");\n        processDefBuilder.addTransition(\"callActivity\", \"step1\");\n        processDefBuilder.addTransition(\"step1\", \"end\");\n\n        return deployAndEnableProcessWithActor(processDefBuilder.done(), ACTOR_NAME, cascao);\n    }\n\n    private ProcessDefinition buildProcessWithCallActivity(final String processName, final String targetProcessName,\n            final String strTargetVersion)\n            throws Exception {\n        return buildProcessWithCallActivity(false, false, processName, targetProcessName, 0, strTargetVersion);\n    }\n\n    private ProcessDefinition buildProcessWithCallActivity(final String processName, final String targetProcessName,\n            int loopNb, final String strTargetVersion)\n            throws Exception {\n        return buildProcessWithCallActivity(false, false, processName, targetProcessName, loopNb, strTargetVersion);\n    }\n\n    private void addDataOutputOperationIfNeed(final boolean addOutputOperations,\n            final CallActivityBuilder callActivityBuilder)\n            throws InvalidExpressionException {\n        if (addOutputOperations) {\n            final Operation setClientNumber = BuildTestUtil.buildAssignOperation(\"cNumber\", \"clientNumber\",\n                    ExpressionType.TYPE_VARIABLE,\n                    Integer.class.getName());\n            final Operation setProtocalNumber = BuildTestUtil.buildAssignOperation(\"pNumber\", \"protocolNumber\",\n                    ExpressionType.TYPE_VARIABLE,\n                    Integer.class.getName());\n            callActivityBuilder.addDataOutputOperation(setClientNumber);\n            callActivityBuilder.addDataOutputOperation(setProtocalNumber);\n        }\n    }\n\n    private void addDataInputOperationsIfNeed(final boolean addInputOperations,\n            final CallActivityBuilder callActivityBuilder)\n            throws InvalidExpressionException {\n        if (addInputOperations) {\n            final Operation setFirstName = BuildTestUtil.buildAssignOperation(\"firstName\", \"fName\",\n                    ExpressionType.TYPE_VARIABLE, String.class.getName());\n            final Operation setLastName = BuildTestUtil.buildAssignOperation(\"lastName\", \"lName\",\n                    ExpressionType.TYPE_VARIABLE, String.class.getName());\n            final Operation mapFromCallActivity = BuildTestUtil.buildAssignOperation(\"calledProcessData\",\n                    \"callActivityData\", ExpressionType.TYPE_VARIABLE,\n                    String.class.getName());\n            callActivityBuilder.addDataInputOperation(setFirstName);\n            callActivityBuilder.addDataInputOperation(setLastName);\n            callActivityBuilder.addDataInputOperation(mapFromCallActivity);\n        }\n    }\n\n    /*\n     * Most simple case :\n     * No Inputs or Outputs for the callActivity\n     * See executeCallAtivityUntilEndOfProcess for details.\n     */\n    @Test\n    public void callActivity() throws Exception {\n        executeCallAtivityUntilEndOfProcess(false, false, PROCESS_VERSION, false);\n\n    }\n\n    /*\n     * Only Inputs for the callActivity\n     * See executeCallAtivityUntilEndOfProcess for details.\n     */\n    @Test\n    public void callActivityWithDataInputOperations() throws Exception {\n        executeCallAtivityUntilEndOfProcess(true, false, PROCESS_VERSION, false);\n    }\n\n    /*\n     * Only Outputs for the callActivity\n     * See executeCallAtivityUntilEndOfProcess for details.\n     */\n    @Test\n    public void callActivityWithDataOutputOperations() throws Exception {\n        executeCallAtivityUntilEndOfProcess(false, true, PROCESS_VERSION, false);\n    }\n\n    /*\n     * Only Outputs for the callActivity\n     * See executeCallAtivityUntilEndOfProcess for details.\n     */\n    @Test\n    public void callActivityWithDataOutputOperationsAndTerminateEnd() throws Exception {\n        executeCallAtivityUntilEndOfProcess(false, true, PROCESS_VERSION, true);\n    }\n\n    /*\n     * Only Outputs for the callActivity\n     * See executeCallAtivityUntilEndOfProcess for details.\n     */\n    @Test\n    public void callActivityWithDataInputAndOutputOperationsAndVersion2() throws Exception {\n        executeCallAtivityUntilEndOfProcess(true, true, \"2.0\", false);\n    }\n\n    @Test\n    public void callActivityAndGatewayAndMessageAndIntermediateEvent() throws Exception {\n        ProcessDefinition mainProcessDefinition = null;\n        ProcessDefinition receiveProcessDefinition = null;\n        ProcessDefinition sendProcessDefinition = null;\n        try {\n            final Expression processVersionExpr = new ExpressionBuilder()\n                    .createConstantStringExpression(PROCESS_VERSION);\n\n            // Main process\n            final ProcessDefinitionBuilder mainProcessDefinitionBuilder = new ProcessDefinitionBuilder()\n                    .createNewInstance(\"Main\", PROCESS_VERSION);\n            mainProcessDefinitionBuilder.addActor(ACTOR_NAME);\n            mainProcessDefinitionBuilder.addStartEvent(\"MainStart\").addEndEvent(\"MainEnd\");\n            mainProcessDefinitionBuilder.addGateway(\"Gateway1\", GatewayType.PARALLEL).addGateway(\"Gateway2\",\n                    GatewayType.PARALLEL);\n            // Send callActivity\n            final Expression sendExpr = new ExpressionBuilder().createConstantStringExpression(\"Send\");\n            mainProcessDefinitionBuilder.addCallActivity(\"Send\", sendExpr, processVersionExpr);\n            // Receive callActivity\n            final Expression receiveExpr = new ExpressionBuilder().createConstantStringExpression(\"Receive\");\n            mainProcessDefinitionBuilder.addCallActivity(\"Receive\", receiveExpr, processVersionExpr);\n            // Transitions\n            mainProcessDefinitionBuilder.addTransition(\"MainStart\", \"Gateway1\").addTransition(\"Gateway1\", \"Send\")\n                    .addTransition(\"Gateway1\", \"Receive\")\n                    .addTransition(\"Send\", \"Gateway2\").addTransition(\"Receive\", \"Gateway2\")\n                    .addTransition(\"Gateway2\", \"MainEnd\");\n            mainProcessDefinition = deployAndEnableProcessWithActor(mainProcessDefinitionBuilder.done(), ACTOR_NAME,\n                    cascao);\n\n            // Receive process\n            final ProcessDefinitionBuilder receiveProcessDefinitionBuilder = new ProcessDefinitionBuilder()\n                    .createNewInstance(\"Receive\", PROCESS_VERSION);\n            receiveProcessDefinitionBuilder.addActor(ACTOR_NAME);\n            receiveProcessDefinitionBuilder.addStartEvent(\"ReceiveStart\").addEndEvent(\"ReceiveEnd\");\n            receiveProcessDefinitionBuilder.addUserTask(\"Read\", ACTOR_NAME);\n            final Operation operation = BuildTestUtil.buildAssignOperation(\"copymsg\", \"MSG\",\n                    ExpressionType.TYPE_VARIABLE, String.class.getName());\n            final IntermediateCatchEventDefinitionBuilder intermediateCatchEvent = receiveProcessDefinitionBuilder\n                    .addIntermediateCatchEvent(\"ReceiveMsg\");\n            intermediateCatchEvent.addMessageEventTrigger(\"ping\").addOperation(operation);\n            intermediateCatchEvent.addLongTextData(\"copymsg\", null);\n            // Transitions\n            receiveProcessDefinitionBuilder.addTransition(\"ReceiveStart\", \"ReceiveMsg\")\n                    .addTransition(\"ReceiveMsg\", \"Read\").addTransition(\"Read\", \"ReceiveEnd\");\n            receiveProcessDefinition = deployAndEnableProcessWithActor(receiveProcessDefinitionBuilder.done(),\n                    ACTOR_NAME, cascao);\n\n            // Send process\n            final ProcessDefinitionBuilder sendProcessDefinitionBuilder = new ProcessDefinitionBuilder()\n                    .createNewInstance(\"Send\", PROCESS_VERSION);\n            sendProcessDefinitionBuilder.addActor(ACTOR_NAME);\n            sendProcessDefinitionBuilder.addStartEvent(\"SendStart\");\n            final Expression msgData = new ExpressionBuilder().createConstantStringExpression(\"data\");\n            sendProcessDefinitionBuilder.addLongTextData(\"msg\", msgData);\n            sendProcessDefinitionBuilder.addUserTask(\"Step1\", ACTOR_NAME);\n            final Expression targetProcess = new ExpressionBuilder().createConstantStringExpression(\"Receive\");\n            final Expression targetFlowNode = new ExpressionBuilder().createConstantStringExpression(\"ReceiveMsg\");\n            final Expression displayName = new ExpressionBuilder().createConstantStringExpression(\"MSG\");\n            final Expression messageContent = new ExpressionBuilder().createDataExpression(\"msg\", \"java.lang.String\");\n            sendProcessDefinitionBuilder.addEndEvent(\"SendMsgEnd\")\n                    .addMessageEventTrigger(\"ping\", targetProcess, targetFlowNode)\n                    .addMessageContentExpression(displayName, messageContent);\n            // Transitions\n            sendProcessDefinitionBuilder.addTransition(\"SendStart\", \"Step1\").addTransition(\"Step1\", \"SendMsgEnd\");\n            sendProcessDefinition = deployAndEnableProcessWithActor(sendProcessDefinitionBuilder.done(), ACTOR_NAME,\n                    cascao);\n\n            assertThat(getProcessAPI().getNumberOfProcessInstances()).isEqualTo(0L);\n            final ProcessInstance mainProcessInstance = getProcessAPI().startProcess(cascao.getId(),\n                    mainProcessDefinition.getId());\n\n            // No need to verify anything, if no exception, query exists\n            getProcessAPI().searchActivities(\n                    new SearchOptionsBuilder(0, 10)\n                            .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, mainProcessInstance.getId())\n                            .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.CALL_ACTIVITY).done());\n\n            getProcessAPI().searchActivities(\n                    new SearchOptionsBuilder(0, 10)\n                            .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, mainProcessInstance.getId())\n                            .filter(ProcessSupervisorSearchDescriptor.USER_ID, cascao.getId()).done());\n\n            getProcessAPI().searchActivities(\n                    new SearchOptionsBuilder(0, 10)\n                            .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.CALL_ACTIVITY)\n                            .filter(ProcessSupervisorSearchDescriptor.USER_ID, cascao.getId()).done());\n\n            waitForUserTaskAndExecuteIt(\"Step1\", cascao);\n\n            getProcessAPI().searchActivities(new SearchOptionsBuilder(0, 10)\n                    .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.CALL_ACTIVITY).done());\n\n            final ActivityInstance read = waitForUserTaskAndGetIt(\"Read\");\n            final DataInstance copyMsg = getProcessAPI().getProcessDataInstance(\"copymsg\",\n                    read.getParentProcessInstanceId());\n            assertThat(copyMsg.getValue()).isEqualTo(\"data\");\n            assignAndExecuteStep(read, cascao.getId());\n\n            waitForProcessToFinish(mainProcessInstance);\n        } finally {\n            disableAndDeleteProcess(mainProcessDefinition, receiveProcessDefinition, sendProcessDefinition);\n        }\n    }\n\n    @Test\n    public void callActivityAndGatewayAndMessageAndTask() throws Exception {\n        ProcessDefinition mainProcessDefinition = null;\n        ProcessDefinition receiveProcessDefinition = null;\n        ProcessDefinition sendProcessDefinition = null;\n        try {\n            final Expression processVersionExpr = new ExpressionBuilder()\n                    .createConstantStringExpression(PROCESS_VERSION);\n            // Main process\n            final ProcessDefinitionBuilder mainProcessDefinitionBuilder = new ProcessDefinitionBuilder()\n                    .createNewInstance(\"Main\", PROCESS_VERSION);\n            mainProcessDefinitionBuilder.addActor(ACTOR_NAME);\n            mainProcessDefinitionBuilder.addStartEvent(\"MainStart\").addEndEvent(\"MainEnd\");\n            mainProcessDefinitionBuilder.addGateway(\"Gateway1\", GatewayType.PARALLEL).addGateway(\"Gateway2\",\n                    GatewayType.PARALLEL);\n            // Send callActivity\n            final Expression sendExpr = new ExpressionBuilder().createConstantStringExpression(\"Send\");\n            mainProcessDefinitionBuilder.addCallActivity(\"Send\", sendExpr, processVersionExpr);\n            // Receive callActivity\n            final Expression receiveExpr = new ExpressionBuilder().createConstantStringExpression(\"Receive\");\n            mainProcessDefinitionBuilder.addCallActivity(\"Receive\", receiveExpr, processVersionExpr);\n            // Transitions\n            mainProcessDefinitionBuilder.addTransition(\"MainStart\", \"Gateway1\").addTransition(\"Gateway1\", \"Send\")\n                    .addTransition(\"Gateway1\", \"Receive\")\n                    .addTransition(\"Send\", \"Gateway2\").addTransition(\"Receive\", \"Gateway2\")\n                    .addTransition(\"Gateway2\", \"MainEnd\");\n            mainProcessDefinition = deployAndEnableProcessWithActor(mainProcessDefinitionBuilder.done(), ACTOR_NAME,\n                    cascao);\n            // Receive process\n            final ProcessDefinitionBuilder receiveProcessDefinitionBuilder = new ProcessDefinitionBuilder()\n                    .createNewInstance(\"Receive\", PROCESS_VERSION);\n            receiveProcessDefinitionBuilder.addActor(ACTOR_NAME);\n            receiveProcessDefinitionBuilder.addStartEvent(\"ReceiveStart\").addEndEvent(\"ReceiveEnd\");\n            receiveProcessDefinitionBuilder.addLongTextData(\"copymsg\", null);\n            receiveProcessDefinitionBuilder.addUserTask(\"Read\", ACTOR_NAME);\n            final Operation operation = BuildTestUtil.buildAssignOperation(\"copymsg\", \"MSG2\",\n                    ExpressionType.TYPE_VARIABLE, String.class.getName());\n            receiveProcessDefinitionBuilder.addReceiveTask(\"ReceiveMsg\", \"ping2\").addMessageOperation(operation);\n            // Transitions\n            receiveProcessDefinitionBuilder.addTransition(\"ReceiveStart\", \"ReceiveMsg\")\n                    .addTransition(\"ReceiveMsg\", \"Read\").addTransition(\"Read\", \"ReceiveEnd\");\n            receiveProcessDefinition = deployAndEnableProcessWithActor(receiveProcessDefinitionBuilder.done(),\n                    ACTOR_NAME, cascao);\n            // Send process\n            final ProcessDefinitionBuilder sendProcessDefinitionBuilder = new ProcessDefinitionBuilder()\n                    .createNewInstance(\"Send\", PROCESS_VERSION);\n            sendProcessDefinitionBuilder.addActor(ACTOR_NAME);\n            sendProcessDefinitionBuilder.addStartEvent(\"SendStart\");\n            final Expression msgData = new ExpressionBuilder().createConstantStringExpression(\"data\");\n            sendProcessDefinitionBuilder.addLongTextData(\"msg\", msgData);\n            sendProcessDefinitionBuilder.addUserTask(\"Step1\", ACTOR_NAME);\n            final Expression targetProcess = new ExpressionBuilder().createConstantStringExpression(\"Receive\");\n            final Expression targetFlowNode = new ExpressionBuilder().createConstantStringExpression(\"ReceiveMsg\");\n            final Expression displayName = new ExpressionBuilder().createConstantStringExpression(\"MSG2\");\n            final Expression messageContent = new ExpressionBuilder().createDataExpression(\"msg\", \"java.lang.String\");\n            sendProcessDefinitionBuilder.addSendTask(\"SendMsgEnd\", \"ping2\", targetProcess)\n                    .setTargetFlowNode(targetFlowNode)\n                    .addMessageContentExpression(displayName, messageContent);\n            // Transitions\n            sendProcessDefinitionBuilder.addTransition(\"SendStart\", \"Step1\").addTransition(\"Step1\", \"SendMsgEnd\");\n            sendProcessDefinition = deployAndEnableProcessWithActor(sendProcessDefinitionBuilder.done(), ACTOR_NAME,\n                    cascao);\n            assertThat(getProcessAPI().getNumberOfProcessInstances()).isEqualTo(0L);\n\n            final ProcessInstance mainProcessInstance = getProcessAPI().startProcess(cascao.getId(),\n                    mainProcessDefinition.getId());\n            waitForUserTaskAndExecuteIt(\"Step1\", cascao);\n            final ActivityInstance read = waitForUserTaskAndGetIt(\"Read\");\n            final DataInstance copyMsg = getProcessAPI().getProcessDataInstance(\"copymsg\",\n                    read.getParentProcessInstanceId());\n            assertThat(copyMsg.getValue()).isEqualTo(\"data\");\n            assignAndExecuteStep(read, cascao.getId());\n            waitForProcessToFinish(mainProcessInstance);\n        } finally {\n            disableAndDeleteProcess(mainProcessDefinition, receiveProcessDefinition, sendProcessDefinition);\n        }\n    }\n\n    /*\n     * Create a call activity which map some data between child and parent.\n     * Execute an operation using one of this data.\n     * -> operation must be executed after the data mapping is executed\n     */\n    @Test\n    public void callActivityWithDataOutputAndOperationAreExecutedInTheGoodOrder() throws Exception {\n\n        final ProcessDefinition targetProcessDef = getSimpleProcess(ACTOR_NAME, \"targetProcess\", PROCESS_VERSION,\n                false);\n        final Expression targetProcessNameExpr = new ExpressionBuilder()\n                .createConstantStringExpression(\"targetProcess\");\n        final Expression targetProcessVersionExpr = new ExpressionBuilder()\n                .createConstantStringExpression(PROCESS_VERSION);\n        final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithCallActivity\", PROCESS_VERSION);\n        processDefBuilder.addIntegerData(\"cNumber\", new ExpressionBuilder().createConstantIntegerExpression(125));\n        processDefBuilder.addIntegerData(\"pNumber\", null);\n        processDefBuilder.addIntegerData(\"dataInitWithCNumber\",\n                new ExpressionBuilder().createConstantIntegerExpression(12));\n        processDefBuilder.addActor(ACTOR_NAME);\n        processDefBuilder.addStartEvent(\"start\");\n        final CallActivityBuilder callActivityBuilder = processDefBuilder.addCallActivity(\"callActivity\",\n                targetProcessNameExpr, targetProcessVersionExpr);\n        addDataOutputOperationIfNeed(true, callActivityBuilder);\n        callActivityBuilder.addOperation(new OperationBuilder().createSetDataOperation(\"dataInitWithCNumber\",\n                new ExpressionBuilder().createDataExpression(\"cNumber\", Integer.class.getName())));\n        processDefBuilder.addUserTask(\"step1\", ACTOR_NAME);\n        processDefBuilder.addEndEvent(\"end\");\n        processDefBuilder.addTransition(\"start\", \"callActivity\");\n        processDefBuilder.addTransition(\"callActivity\", \"step1\");\n        processDefBuilder.addTransition(\"step1\", \"end\");\n        final ProcessDefinition callingProcessDef = deployAndEnableProcessWithActor(processDefBuilder.done(),\n                ACTOR_NAME, cascao);\n        final ProcessInstance callingProcessInstance = getProcessAPI().startProcess(callingProcessDef.getId());\n\n        // execute process until step1\n        waitForUserTaskAndExecuteIt(callingProcessInstance, \"tStep1\", cebolinha);\n        waitForUserTask(callingProcessInstance, \"step1\");\n\n        /*\n         * check the data have the value of the clientNumber of the called process\n         * the set of data should be like this:\n         * targetProcess:clientNumber(10) -> callingProcess:cNumber(was 125)\n         * callingProcess:cNumber(10) -> callingProcess:dataInitWithCNumber(was 12)\n         */\n        assertThat(getProcessAPI().getProcessDataInstance(\"dataInitWithCNumber\", callingProcessInstance.getId())\n                .getValue()).isEqualTo(Integer.valueOf(10));\n\n        disableAndDeleteProcess(callingProcessDef, targetProcessDef);\n    }\n\n    /*\n     * 1 parent process : callingProcess : startEvent(start) -> callActivity -> userTask(step1) -> endEvent(end).\n     * 1 called process : targetProcess : startEvent(tStart) -> userTask(tStep1) -> endEvent(tEnd).\n     * checks : No instances of a process exist, 2 process instances (callingProcess starts and set off targetProcess of\n     * call activity),\n     * callingProcess stopped on the call activity, targetProcess is executing, callingProcess is the root process of\n     * targetProcess,\n     * call activity called the callProcess, callingProcess continues and reaches user task, targetProcess is finished,\n     * the user task is\n     * the one from the callingProcess, calling Process is finished.\n     */\n    private void executeCallAtivityUntilEndOfProcess(final boolean addInputOperations,\n            final boolean addOutputOperations, final String strTargetVersion,\n            final boolean terminateEnd) throws Exception {\n        final ProcessDefinition targetProcessDef1 = getSimpleProcess(ACTOR_NAME, \"targetProcess\", PROCESS_VERSION,\n                terminateEnd);\n        final ProcessDefinition targetProcessDef3 = getSimpleProcess(ACTOR_NAME, \"targetProcess\", \"3.0\", terminateEnd);\n        final ProcessDefinition targetProcessDef2 = getSimpleProcess(ACTOR_NAME, \"targetProcess\", \"2.0\", terminateEnd);\n\n        final ProcessDefinition callingProcessDef = buildProcessWithCallActivity(addInputOperations,\n                addOutputOperations, \"callingProcess\",\n                \"targetProcess\", 0, strTargetVersion);\n\n        assertThat(getProcessAPI().getNumberOfProcessInstances()).isEqualTo(0L);\n\n        final List<Operation> operations = getStartOperations();\n        final ProcessInstance callingProcessInstance = getProcessAPI().startProcess(callingProcessDef.getId(),\n                operations, null);\n\n        if (strTargetVersion != null && strTargetVersion.contentEquals(\"4.0\")) {\n            checkNbOfProcessInstances(2, ProcessInstanceCriterion.NAME_DESC);\n            final List<ProcessInstance> processInstances = getProcessAPI().getProcessInstances(0, 10,\n                    ProcessInstanceCriterion.NAME_DESC);\n            assertThat(processInstances).hasSize(1);\n\n            waitForFlowNodeInFailedState(callingProcessInstance, \"callActivity\");\n            return;\n        }\n\n        checkNbOfProcessInstances(2, ProcessInstanceCriterion.NAME_DESC);\n        final List<ProcessInstance> processInstances = getProcessAPI().getProcessInstances(0, 10,\n                ProcessInstanceCriterion.NAME_DESC);\n        assertThat(processInstances).hasSize(2); // two instances are expected calling and target processes\n        final ProcessInstance targetPI = processInstances.get(0);\n\n        // System.out.println(\"Version of the definition of targetProcess : \"\n        // + getProcessAPI().getProcessDefinition(targetPI.getProcessDefinitionId()).getVersion());\n\n        if (strTargetVersion == null || strTargetVersion.contentEquals(\"2.0\")) {\n            assertThat(targetPI.getProcessDefinitionId()).isEqualTo(targetProcessDef2.getId());\n        } else if (strTargetVersion.contentEquals(PROCESS_VERSION)) {\n            assertThat(targetPI.getProcessDefinitionId()).isEqualTo(targetProcessDef1.getId());\n        }\n\n        final FlowNodeInstance callActivityInstance = waitForFlowNodeInExecutingState(callingProcessInstance,\n                \"callActivity\", true);\n        assertThat(targetPI.getRootProcessInstanceId()).isEqualTo(callingProcessInstance.getId());\n        assertThat(targetPI.getCallerId()).isEqualTo(callActivityInstance.getId());\n\n        ActivityInstance activityInstance = waitForUserTaskAndGetIt(callingProcessInstance, \"tStep1\");\n        assertThat(activityInstance.getParentProcessInstanceId()).isEqualTo(targetPI.getId());\n        assertThat(activityInstance.getRootContainerId()).isEqualTo(callingProcessInstance.getId());\n        checkDataInputOperations(addInputOperations, targetPI);\n\n        // execute step in the target process\n        assignAndExecuteStep(activityInstance, cebolinha.getId());\n\n        activityInstance = waitForUserTaskAndGetIt(callingProcessInstance, \"step1\");\n        checkOutputOperations(addOutputOperations, callingProcessInstance);\n        waitForProcessToFinish(targetPI);\n        assertThat(activityInstance.getParentProcessInstanceId()).isEqualTo(callingProcessInstance.getId());\n\n        assignAndExecuteStep(activityInstance, cascao.getId());\n\n        waitForProcessToFinish(callingProcessInstance);\n\n        disableAndDeleteProcess(callingProcessDef, targetProcessDef1, targetProcessDef2, targetProcessDef3);\n    }\n\n    private List<Operation> getStartOperations() throws InvalidExpressionException {\n        final ArrayList<Operation> operations = new ArrayList<>(2);\n        operations.add(BuildTestUtil.buildAssignOperation(\"fName\", \"Fulano\", ExpressionType.TYPE_CONSTANT,\n                String.class.getName()));\n        operations.add(BuildTestUtil.buildAssignOperation(\"lName\", \"de Tal\", ExpressionType.TYPE_CONSTANT,\n                String.class.getName()));\n        return operations;\n    }\n\n    private void checkOutputOperations(final boolean addOutputOperations, final ProcessInstance callingProcessInstance)\n            throws Exception {\n        if (addOutputOperations) {\n            final DataInstance clientNumberData = getProcessAPI().getProcessDataInstance(\"cNumber\",\n                    callingProcessInstance.getId());\n            final DataInstance protocolNumberData = getProcessAPI().getProcessDataInstance(\"pNumber\",\n                    callingProcessInstance.getId());\n            assertThat(clientNumberData.getValue()).isEqualTo(10);\n            assertThat(protocolNumberData.getValue()).isEqualTo(305020);\n        }\n    }\n\n    private void checkDataInputOperations(final boolean addInputOperations, final ProcessInstance targetPI)\n            throws DataNotFoundException {\n        if (addInputOperations) {\n            final DataInstance firstNameData = getProcessAPI().getProcessDataInstance(\"firstName\", targetPI.getId());\n            final DataInstance lastNameData = getProcessAPI().getProcessDataInstance(\"lastName\", targetPI.getId());\n            final DataInstance calledProcessData = getProcessAPI().getProcessDataInstance(\"calledProcessData\",\n                    targetPI.getId());\n            assertThat(firstNameData.getValue()).isEqualTo(\"Fulano\");\n            assertThat(lastNameData.getValue()).isEqualTo(\"de Tal\");\n            assertThat(calledProcessData.getValue()).isEqualTo(\"defaultValue\");\n        }\n    }\n\n    private void variableMultiLevelCallActivity(final int nbLevel) throws Exception {\n        final ProcessDefinition[] processDefLevels = new ProcessDefinition[nbLevel];\n\n        for (int i = 0; i < nbLevel - 1; i++) {\n            processDefLevels[i] = buildProcessWithCallActivity(\"processLevel\" + i, \"processLevel\" + (i + 1),\n                    PROCESS_VERSION);\n        }\n        processDefLevels[nbLevel - 1] = getSimpleProcess(ACTOR_NAME, \"processLevel\" + (nbLevel - 1), PROCESS_VERSION,\n                false);\n\n        final List<Operation> operations = getStartOperations();\n        assertThat(getProcessAPI().getNumberOfProcessInstances()).isEqualTo(0L);\n\n        final ProcessInstance[] procInstLevels = new ProcessInstance[nbLevel];\n        procInstLevels[0] = getProcessAPI().startProcess(processDefLevels[0].getId(), operations, null);\n        checkNbOfProcessInstances(nbLevel, ProcessInstanceCriterion.NAME_DESC);\n        final List<ProcessInstance> processInstances = getProcessAPI().getProcessInstances(0, nbLevel,\n                ProcessInstanceCriterion.NAME_ASC); // if nbLevel>10 use\n        assertThat(processInstances).hasSize(nbLevel);\n\n        for (int i = 0; i < nbLevel; i++) {\n            procInstLevels[i] = processInstances.get(i);\n            assertThat(procInstLevels[i].getProcessDefinitionId()).as(\"Level of process : \" + i)\n                    .isEqualTo(processDefLevels[i].getId());\n        }\n\n        waitForUserTaskAndExecuteIt(procInstLevels[0], \"tStep1\", cebolinha);\n        waitForProcessToFinish(procInstLevels[nbLevel - 1]);\n        for (int i = nbLevel - 2; i >= 0; i--) {\n            waitForUserTaskAndExecuteIt(procInstLevels[0], \"step1\", cascao);\n            waitForProcessToFinish(procInstLevels[i]);\n        }\n\n        disableAndDeleteProcess(processDefLevels);\n    }\n\n    /*\n     * Tested until 200, works !\n     */\n    @Test\n    public void multiLevelCallActivity() throws Exception {\n        variableMultiLevelCallActivity(10);\n    }\n\n    @Test\n    public void callUndeployedProcess() throws Exception {\n\n        final ProcessDefinition processDef = buildProcessWithCallActivity(\"callingProcess\", \"unDeployedProcess\",\n                PROCESS_VERSION);\n        assertThat(getProcessAPI().getNumberOfProcessInstances()).isEqualTo(0L);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDef.getId());\n        final List<ProcessInstance> processInstances = getProcessAPI().getProcessInstances(0, 10,\n                ProcessInstanceCriterion.NAME_DESC);\n        assertThat(processInstances).hasSize(1);\n\n        final ActivityInstance waitForTaskToFail = waitForTaskToFail(processInstance);\n        assertThat(waitForTaskToFail.getState()).isEqualTo(TestStates.FAILED.getStateName());\n        disableAndDeleteProcess(processDef);\n    }\n\n    private void callActivityInALoop(final int nbLoop) throws Exception {\n        final ProcessDefinition targetProcessDef = getSimpleProcess(ACTOR_NAME, \"targetProcess\", PROCESS_VERSION,\n                false);\n        final ProcessDefinition callingProcessDef = buildProcessWithCallActivity(\"callingProcess\", \"targetProcess\",\n                nbLoop,\n                PROCESS_VERSION);\n\n        assertThat(getProcessAPI().getNumberOfProcessInstances()).isEqualTo(0);\n        final ProcessInstance callingProcessInstance = getProcessAPI().startProcess(callingProcessDef.getId(), null,\n                null);\n        waitForFlowNodeInExecutingState(callingProcessInstance, \"callActivity\", true);\n\n        for (int i = 0; i < nbLoop; i++) {\n            long tStep1 = waitForUserTask(\"tStep1\");\n            FlowNodeInstance flowNodeInstance = getProcessAPI().getFlowNodeInstance(tStep1);\n            assignAndExecuteStep(tStep1, cebolinha);\n            waitForProcessToFinish(flowNodeInstance.getParentProcessInstanceId());\n        }\n\n        waitForUserTaskAndExecuteIt(callingProcessInstance, \"step1\", cascao);\n        waitForProcessToFinish(callingProcessInstance);\n\n        disableAndDeleteProcess(callingProcessDef, targetProcessDef);\n    }\n\n    /*\n     * Tested until 200, works !\n     * Don't use 0 as argument though.\n     */\n    @Test\n    public void callActivityInALoop() throws Exception {\n        callActivityInALoop(10);\n    }\n\n    private ProcessInstance getTargetProcessInstance(final ProcessDefinition targetProcessDef) throws Exception {\n        final List<ProcessInstance> processInstances = checkNbOfProcessInstances(2, ProcessInstanceCriterion.NAME_DESC)\n                .getResult();\n        assertThat(processInstances).hasSize(2);// two instances are expected calling and target processes\n        final ProcessInstance targetPI = processInstances.get(0);\n        assertThat(targetPI.getProcessDefinitionId()).isEqualTo(targetProcessDef.getId());\n        return targetPI;\n    }\n\n    @Test\n    public void getArchivedCallActivityInstance() throws Exception {\n        final ProcessDefinition targetProcessDef = getSimpleProcess(ACTOR_NAME, \"targetProcess\", PROCESS_VERSION,\n                false);\n        final ProcessDefinition callingProcessDef = buildProcessWithCallActivity(\"callingProcess\", \"targetProcess\",\n                PROCESS_VERSION);\n\n        assertThat(getProcessAPI().getNumberOfProcessInstances()).isEqualTo(0L);\n        final ProcessInstance callingProcessInstance = getProcessAPI().startProcess(callingProcessDef.getId());\n        final ProcessInstance targetPI = getTargetProcessInstance(targetProcessDef);\n\n        final FlowNodeInstance callActivityInstance = waitForFlowNodeInExecutingState(callingProcessInstance,\n                \"callActivity\", true);\n\n        waitForUserTaskAndExecuteIt(callingProcessInstance, \"tStep1\", cebolinha); // first loop execution\n        waitForProcessToFinish(targetPI);\n\n        final WaitForFinalArchivedActivity waitForFinalArchivedActivity = waitForFinalArchivedActivity(\"callActivity\",\n                callingProcessInstance);\n        assertThat(waitForFinalArchivedActivity.getResult().getType()).isEqualTo(FlowNodeType.CALL_ACTIVITY);\n        final List<ArchivedProcessInstance> archivedProcessInstanceList = getProcessAPI()\n                .getArchivedProcessInstances(targetPI.getId(), 0, 20);\n\n        final ArchivedProcessInstance firstProcessInstanceArchive = archivedProcessInstanceList.get(0);\n        assertThat(firstProcessInstanceArchive.getRootProcessInstanceId()).isEqualTo(callingProcessInstance.getId());\n        assertThat(firstProcessInstanceArchive.getCallerId()).isEqualTo(callActivityInstance.getId());\n\n        disableAndDeleteProcess(callingProcessDef, targetProcessDef);\n    }\n\n    @Test\n    public void callActivityUsingLastestVersion() throws Exception {\n        executeCallAtivityUntilEndOfProcess(false, false, null, false);\n    }\n\n    @Test\n    public void callActivityUsingUndeployedVersion() throws Exception {\n        final ProcessDefinition callingProcessDef = buildProcessWithCallActivity(\"callingProcess\", \"targetProcess\",\n                \"unexisting_version_4.0\");\n        final ProcessInstance callingProcessInstance = getProcessAPI().startProcess(callingProcessDef.getId());\n\n        final ActivityInstance failedTask = waitForTaskToFail(callingProcessInstance);\n        assertThat(failedTask.getName()).isEqualTo(\"callActivity\");\n\n        disableAndDeleteProcess(callingProcessDef);\n    }\n\n    @Test\n    public void callActivityUsingUndeployedProcess() throws Exception {\n        final ProcessDefinition callingProcessDef = buildProcessWithCallActivity(\"callingProcess\", \"targetProcess\",\n                null);\n        final ProcessInstance callingProcessInstance = getProcessAPI().startProcess(callingProcessDef.getId());\n\n        final ActivityInstance failedTask = waitForTaskToFail(callingProcessInstance);\n        assertThat(failedTask.getName()).isEqualTo(\"callActivity\");\n\n        disableAndDeleteProcess(callingProcessDef);\n    }\n\n    @Test\n    public void callActivityUsingDisabledProcess() throws Exception {\n        final ProcessDefinition targetProcessDef = getSimpleProcess(ACTOR_NAME, \"targetProcess\", PROCESS_VERSION,\n                false);\n        getProcessAPI().disableProcess(targetProcessDef.getId());\n        final ProcessDefinition callingProcessDef = buildProcessWithCallActivity(\"callingProcess\", \"targetProcess\",\n                PROCESS_VERSION);\n        final ProcessInstance callingProcessInstance = getProcessAPI().startProcess(callingProcessDef.getId());\n\n        final ActivityInstance failedTask = waitForTaskToFail(callingProcessInstance);\n        assertThat(failedTask.getName()).isEqualTo(\"callActivity\");\n\n        deleteProcess(targetProcessDef.getId());\n        disableAndDeleteProcess(callingProcessDef);\n    }\n\n    @Test\n    public void deleteProcessInstanceThatIsCalledByCallActivity() throws Exception {\n        final ProcessDefinition targetProcessDef1 = getSimpleProcess(ACTOR_NAME, \"targetProcess\", PROCESS_VERSION,\n                false);\n        final ProcessDefinition callingProcessDef = buildProcessWithCallActivity(\"callingProcess\", \"targetProcess\",\n                PROCESS_VERSION);\n\n        assertThat(getProcessAPI().getNumberOfProcessInstances()).isEqualTo(0L);\n\n        final List<Operation> operations = getStartOperations();\n        final ProcessInstance callingProcessInstance = getProcessAPI().startProcess(callingProcessDef.getId(),\n                operations, null);\n        final ActivityInstance tStep1 = waitForUserTaskAndGetIt(callingProcessInstance, \"tStep1\");\n\n        try {\n            getProcessAPI().deleteProcessInstance(tStep1.getParentProcessInstanceId());\n            fail(\"Should not be able to delete process instance that is called by an other process\");\n        } catch (final ProcessInstanceHierarchicalDeletionException e) {\n            getProcessAPI().deleteProcessInstance(e.getProcessInstanceId());\n            // should work now\n            try {\n                getProcessAPI().getProcessInstance(tStep1.getParentProcessInstanceId());\n                fail(\"process should be deleted\");\n            } catch (final ProcessInstanceNotFoundException e1) {\n                // ok\n            }\n        } finally {\n            disableAndDeleteProcess(callingProcessDef, targetProcessDef1);\n        }\n    }\n\n    @Test(expected = DeletionException.class)\n    public void deleteProcessDefinitionWithProcessInstanceThatIsCalledByCallActivity() throws Exception {\n        final ProcessDefinition targetProcessDef1 = getSimpleProcess(ACTOR_NAME, \"targetProcess\", PROCESS_VERSION,\n                false);\n        final ProcessDefinition callingProcessDef = buildProcessWithCallActivity(\"callingProcess\", \"targetProcess\",\n                PROCESS_VERSION);\n\n        assertThat(getProcessAPI().getNumberOfProcessInstances()).isEqualTo(0L);\n\n        final List<Operation> operations = getStartOperations();\n        final ProcessInstance callingProcessInstance = getProcessAPI().startProcess(callingProcessDef.getId(),\n                operations, null);\n        waitForUserTask(callingProcessInstance, \"tStep1\");\n\n        getProcessAPI().disableProcess(targetProcessDef1.getId());\n        try {\n            deleteProcess(targetProcessDef1);\n            fail(\"Should not be able to delete process instance that is called by an other process\");\n        } finally {\n            disableAndDeleteProcess(callingProcessDef);\n            deleteProcess(targetProcessDef1);\n        }\n    }\n\n    @Test\n    public void callActivityWithDependencies() throws Exception {\n        ProcessDefinition targetProcessDef = null;\n        ProcessDefinition callingProcessDef = null;\n        try {\n            targetProcessDef = getSimpleProcess(ACTOR_NAME, \"targetProcess\", PROCESS_VERSION, false);\n            final Expression targetProcessNameExpr = new ExpressionBuilder()\n                    .createConstantStringExpression(\"targetProcess\");\n            final Expression vExpression = new ExpressionBuilder().createDataExpression(\"v\", String.class.getName());\n            final List<Expression> dependencies = new ArrayList<>(1);\n            dependencies.add(vExpression);\n            final Expression targetProcessVersionExpr = new ExpressionBuilder().createGroovyScriptExpression(\"version\",\n                    \"v\", String.class.getName(),\n                    dependencies);\n            final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder()\n                    .createNewInstance(\"callingProcess\", PROCESS_VERSION);\n            processDefBuilder.addShortTextData(\"v\",\n                    new ExpressionBuilder().createConstantStringExpression(PROCESS_VERSION));\n            processDefBuilder.addShortTextData(\"lName\", null);\n            processDefBuilder.addIntegerData(\"cNumber\", null);\n            processDefBuilder.addIntegerData(\"pNumber\", null);\n            processDefBuilder.addActor(ACTOR_NAME);\n            processDefBuilder.addStartEvent(\"start\");\n            processDefBuilder.addCallActivity(\"callActivity\", targetProcessNameExpr, targetProcessVersionExpr);\n            processDefBuilder.addUserTask(\"step1\", ACTOR_NAME);\n            processDefBuilder.addEndEvent(\"end\");\n            processDefBuilder.addTransition(\"start\", \"callActivity\");\n            processDefBuilder.addTransition(\"callActivity\", \"step1\");\n            processDefBuilder.addTransition(\"step1\", \"end\");\n            callingProcessDef = deployAndEnableProcessWithActor(processDefBuilder.done(), ACTOR_NAME, cascao);\n            assertThat(getProcessAPI().getNumberOfProcessInstances()).isEqualTo(0L);\n            final ProcessInstance callingProcessInstance = getProcessAPI().startProcess(callingProcessDef.getId(), null,\n                    null);\n            checkNbOfProcessInstances(2, ProcessInstanceCriterion.NAME_DESC);\n            final List<ProcessInstance> processInstances = getProcessAPI().getProcessInstances(0, 10,\n                    ProcessInstanceCriterion.NAME_DESC);\n            assertThat(processInstances).hasSize(2); // two instances are expected calling and target process\n            final ProcessInstance targetPI = processInstances.get(0);\n            assertThat(targetProcessDef.getId()).isEqualTo(targetPI.getProcessDefinitionId());\n            assertThat(targetProcessDef.getVersion()).isEqualTo(PROCESS_VERSION);\n            final FlowNodeInstance callActivityInstance = waitForFlowNodeInExecutingState(callingProcessInstance,\n                    \"callActivity\", true);\n            assertThat(targetPI.getRootProcessInstanceId()).isEqualTo(callingProcessInstance.getId());\n            assertThat(targetPI.getCallerId()).isEqualTo(callActivityInstance.getId());\n            final HumanTaskInstance tStep1 = waitForUserTaskAndGetIt(callingProcessInstance, \"tStep1\");\n            assertThat(tStep1.getParentProcessInstanceId()).isEqualTo(targetPI.getId());\n            assertThat(tStep1.getRootContainerId()).isEqualTo(callingProcessInstance.getId());\n            // execute step in the target process\n            assignAndExecuteStep(tStep1, cebolinha.getId());\n            final HumanTaskInstance step1 = waitForUserTaskAndGetIt(callingProcessInstance, \"step1\");\n            waitForProcessToFinish(targetPI);\n            assertThat(step1.getParentProcessInstanceId()).isEqualTo(callingProcessInstance.getId());\n            assignAndExecuteStep(step1, cascao.getId());\n            waitForProcessToFinish(callingProcessInstance);\n        } finally {\n            disableAndDeleteProcess(callingProcessDef, targetProcessDef);\n        }\n    }\n\n    @Test\n    public void callActivityCheckAttributes() throws Exception {\n        ProcessDefinition targetProcessDef = null;\n        ProcessDefinition callingProcessDef = null;\n        try {\n            targetProcessDef = getSimpleProcess(ACTOR_NAME, \"targetProcess\", PROCESS_VERSION, false);\n            final Expression targetProcessNameExpr = new ExpressionBuilder()\n                    .createConstantStringExpression(\"targetProcess\");\n            final Expression targetProcessVersionExpr = new ExpressionBuilder()\n                    .createConstantStringExpression(PROCESS_VERSION);\n            final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder()\n                    .createNewInstance(\"callingProcess\", PROCESS_VERSION);\n            processDefBuilder.addShortTextData(\"lName\", null);\n            processDefBuilder.addIntegerData(\"cNumber\", null);\n            processDefBuilder.addIntegerData(\"pNumber\", null);\n            processDefBuilder.addActor(ACTOR_NAME);\n            processDefBuilder.addStartEvent(\"start\");\n            processDefBuilder.addCallActivity(\"callActivity\", targetProcessNameExpr, targetProcessVersionExpr)\n                    .addDisplayName(new ExpressionBuilder().createConstantStringExpression(\"callActivityDisplayName\"))\n                    .addDescription(\"callActivityDescription\")\n                    .addDisplayDescription(\n                            new ExpressionBuilder().createConstantStringExpression(\"callActivityDisplayDescription\"));\n            processDefBuilder.addUserTask(\"step1\", ACTOR_NAME);\n            processDefBuilder.addEndEvent(\"end\");\n            processDefBuilder.addTransition(\"start\", \"callActivity\");\n            processDefBuilder.addTransition(\"callActivity\", \"step1\");\n            processDefBuilder.addTransition(\"step1\", \"end\");\n            callingProcessDef = deployAndEnableProcessWithActor(processDefBuilder.done(), ACTOR_NAME, cascao);\n            assertThat(getProcessAPI().getNumberOfProcessInstances()).isEqualTo(0L);\n            final ProcessInstance callingProcessInstance = getProcessAPI().startProcess(callingProcessDef.getId(), null,\n                    null);\n            checkNbOfProcessInstances(2, ProcessInstanceCriterion.NAME_DESC);\n            final List<ProcessInstance> processInstances = getProcessAPI().getProcessInstances(0, 10,\n                    ProcessInstanceCriterion.NAME_DESC);\n            assertThat(processInstances).hasSize(2); // two instances are expected calling and target process\n            final ProcessInstance targetPI = processInstances.get(0);\n            final FlowNodeInstance callActivityInstance = waitForFlowNodeInExecutingState(callingProcessInstance,\n                    \"callActivity\", true);\n            assertThat(targetPI.getRootProcessInstanceId()).isEqualTo(callingProcessInstance.getId());\n            assertThat(targetPI.getCallerId()).isEqualTo(callActivityInstance.getId());\n            assertThat(callActivityInstance.getDisplayName()).isEqualTo(\"callActivityDisplayName\");\n            assertThat(callActivityInstance.getDescription()).isEqualTo(\"callActivityDescription\");\n            assertThat(callActivityInstance.getDisplayDescription()).isEqualTo(\"callActivityDisplayDescription\");\n            final HumanTaskInstance tStep1 = waitForUserTaskAndGetIt(callingProcessInstance, \"tStep1\");\n            assertThat(tStep1.getParentProcessInstanceId()).isEqualTo(targetPI.getId());\n            assertThat(tStep1.getRootContainerId()).isEqualTo(callingProcessInstance.getId());\n            // execute step in the target process\n            assignAndExecuteStep(tStep1, cebolinha.getId());\n            final HumanTaskInstance step1 = waitForUserTaskAndGetIt(callingProcessInstance, \"step1\");\n            waitForProcessToFinish(targetPI);\n            assertThat(step1.getParentProcessInstanceId()).isEqualTo(callingProcessInstance.getId());\n            assignAndExecuteStep(step1, cascao.getId());\n            waitForProcessToFinish(callingProcessInstance);\n        } finally {\n            disableAndDeleteProcess(callingProcessDef, targetProcessDef);\n        }\n    }\n\n    @Test(expected = InvalidProcessDefinitionException.class)\n    public void callActivityTargetProcessExprIsNull() throws Exception {\n        final Expression targetProcessNameExpr = null;\n        final Expression targetProcessVersionExpr = new ExpressionBuilder()\n                .createConstantStringExpression(PROCESS_VERSION);\n        final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"callingProcess\", PROCESS_VERSION);\n        processDefBuilder.addShortTextData(\"lName\", null);\n        processDefBuilder.addIntegerData(\"cNumber\", null);\n        processDefBuilder.addIntegerData(\"pNumber\", null);\n        processDefBuilder.addActor(ACTOR_NAME);\n        processDefBuilder.addStartEvent(\"start\");\n        processDefBuilder.addCallActivity(\"callActivity\", targetProcessNameExpr, targetProcessVersionExpr);\n        processDefBuilder.addUserTask(\"step1\", ACTOR_NAME);\n        processDefBuilder.addEndEvent(\"end\");\n        processDefBuilder.addTransition(\"start\", \"callActivity\");\n        processDefBuilder.addTransition(\"callActivity\", \"step1\");\n        processDefBuilder.addTransition(\"step1\", \"end\");\n        deployAndEnableProcessWithActor(processDefBuilder.done(), ACTOR_NAME, cascao);\n    }\n\n    @Test\n    public void callActivityTargetProcessWithJustHumanTask() throws Exception {\n        ProcessDefinition targetProcessDefinition = null;\n        ProcessDefinition callingProcessDefinition = null;\n        try {\n            // Build target process\n            final ProcessDefinitionBuilder targetProcessDefBuilder = new ProcessDefinitionBuilder()\n                    .createNewInstance(\"targetProcess\", PROCESS_VERSION);\n            targetProcessDefBuilder.addActor(ACTOR_NAME);\n            targetProcessDefBuilder.addStartEvent(\"tStart\");\n            targetProcessDefBuilder.addUserTask(\"tStep1\", ACTOR_NAME);\n            targetProcessDefBuilder.addEndEvent(\"tEnd\");\n            targetProcessDefBuilder.addTransition(\"tStart\", \"tStep1\");\n            targetProcessDefBuilder.addTransition(\"tStep1\", \"tEnd\");\n            targetProcessDefinition = deployAndEnableProcessWithActor(targetProcessDefBuilder.done(), ACTOR_NAME,\n                    cebolinha);\n            // Build and start calling process\n            final Expression targetProcessNameExpr = new ExpressionBuilder()\n                    .createConstantStringExpression(\"targetProcess\");\n            final Expression targetProcessVersionExpr = new ExpressionBuilder()\n                    .createConstantStringExpression(PROCESS_VERSION);\n            final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder()\n                    .createNewInstance(\"callingProcess\", PROCESS_VERSION);\n            processDefBuilder.addActor(ACTOR_NAME);\n            processDefBuilder.addStartEvent(\"start\");\n            processDefBuilder.addCallActivity(\"callActivity\", targetProcessNameExpr, targetProcessVersionExpr)\n                    .addDisplayName(new ExpressionBuilder().createConstantStringExpression(\"callActivityDisplayName\"))\n                    .addDescription(\"callActivityDescription\")\n                    .addDisplayDescription(\n                            new ExpressionBuilder().createConstantStringExpression(\"callActivityDisplayDescription\"));\n            processDefBuilder.addEndEvent(\"end\");\n            processDefBuilder.addTransition(\"start\", \"callActivity\");\n            processDefBuilder.addTransition(\"callActivity\", \"end\");\n            callingProcessDefinition = deployAndEnableProcessWithActor(processDefBuilder.done(), ACTOR_NAME, cascao);\n            final ProcessInstance callingProcessInstance = getProcessAPI()\n                    .startProcess(callingProcessDefinition.getId());\n            // Execute step in the target process\n            waitForUserTaskAndExecuteIt(callingProcessInstance, \"tStep1\", cebolinha);\n            waitForProcessToFinish(callingProcessInstance);\n            // Search archived process\n            assertThat(getProcessAPI().getNumberOfArchivedProcessInstances()).isEqualTo(1L);\n            final SearchOptionsBuilder searchOptions = new SearchOptionsBuilder(0, 10);\n            searchOptions.sort(ArchivedProcessInstancesSearchDescriptor.NAME, Order.ASC);\n            final List<ArchivedProcessInstance> archivedProcessInstances = getProcessAPI()\n                    .searchArchivedProcessInstances(searchOptions.done()).getResult();\n            assertThat(archivedProcessInstances).hasSize(1);\n            assertThat(archivedProcessInstances.get(0).getSourceObjectId()).isEqualTo(callingProcessInstance.getId());\n        } finally {\n            disableAndDeleteProcess(callingProcessDefinition, targetProcessDefinition);\n        }\n    }\n\n    @Test\n    public void getProcessDefinitionIdFromActivityInstanceId() throws Exception {\n        // check that real root process definition is retrieved (taken from parent process instance)\n        // Build target process\n        final ProcessDefinitionBuilder targetProcessDefBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"targetProcess\", PROCESS_VERSION);\n        targetProcessDefBuilder.addActor(ACTOR_NAME);\n        targetProcessDefBuilder.addStartEvent(\"tStart\");\n        targetProcessDefBuilder.addUserTask(\"tStep1\", ACTOR_NAME);\n        targetProcessDefBuilder.addEndEvent(\"tEnd\");\n        targetProcessDefBuilder.addTransition(\"tStart\", \"tStep1\");\n        targetProcessDefBuilder.addTransition(\"tStep1\", \"tEnd\");\n        final ProcessDefinition targetProcessDefinition = deployAndEnableProcessWithActor(\n                targetProcessDefBuilder.done(), ACTOR_NAME, cebolinha);\n\n        // Build and start calling process\n        final Expression targetProcessNameExpr = new ExpressionBuilder()\n                .createConstantStringExpression(\"targetProcess\");\n        final Expression targetProcessVersionExpr = new ExpressionBuilder()\n                .createConstantStringExpression(PROCESS_VERSION);\n        final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"callingProcess\", PROCESS_VERSION);\n        processDefBuilder.addActor(ACTOR_NAME);\n        processDefBuilder.addStartEvent(\"start\");\n        processDefBuilder.addCallActivity(\"callActivity\", targetProcessNameExpr, targetProcessVersionExpr)\n                .addDisplayName(new ExpressionBuilder().createConstantStringExpression(\"callActivityDisplayName\"))\n                .addDescription(\"callActivityDescription\")\n                .addDisplayDescription(\n                        new ExpressionBuilder().createConstantStringExpression(\"callActivityDisplayDescription\"));\n        processDefBuilder.addEndEvent(\"end\");\n        processDefBuilder.addTransition(\"start\", \"callActivity\");\n        processDefBuilder.addTransition(\"callActivity\", \"end\");\n        final ProcessDefinition callingProcessDefinition = deployAndEnableProcessWithActor(processDefBuilder.done(),\n                ACTOR_NAME, cascao);\n        final ProcessInstance callingProcessInstance = getProcessAPI().startProcess(callingProcessDefinition.getId());\n        final long tStep1Id = waitForUserTask(callingProcessInstance.getId(), \"tStep1\");\n\n        final long processDefinitionId = getProcessAPI().getProcessDefinitionIdFromActivityInstanceId(tStep1Id);\n        assertThat(processDefinitionId).isEqualTo(targetProcessDefinition.getId());\n\n        disableAndDeleteProcess(callingProcessDefinition, targetProcessDefinition);\n    }\n\n    @Test\n    public void callActivityWithTaskUsingEngineExpressions() throws Exception {\n        ProcessDefinition targetProcessDefinition = null;\n        ProcessDefinition callingProcessDefinition = null;\n        try {\n            // Build target process\n            final ProcessDefinitionBuilder targetProcessDefBuilder = new ProcessDefinitionBuilder()\n                    .createNewInstance(\"targetProcess\", PROCESS_VERSION);\n            targetProcessDefBuilder.addActor(ACTOR_NAME);\n            targetProcessDefBuilder.addStartEvent(\"tStart\");\n            targetProcessDefBuilder.addUserTask(\"tStep1\", ACTOR_NAME).addData(\"rootProcId\", Long.class.getName(),\n                    new ExpressionBuilder().createEngineConstant(ExpressionConstants.ROOT_PROCESS_INSTANCE_ID));\n            targetProcessDefBuilder.addEndEvent(\"tEnd\");\n            targetProcessDefBuilder.addTransition(\"tStart\", \"tStep1\");\n            targetProcessDefBuilder.addTransition(\"tStep1\", \"tEnd\");\n            targetProcessDefinition = deployAndEnableProcessWithActor(targetProcessDefBuilder.done(), ACTOR_NAME,\n                    cebolinha);\n            // Build and start calling process\n            final Expression targetProcessNameExpr = new ExpressionBuilder()\n                    .createConstantStringExpression(\"targetProcess\");\n            final Expression targetProcessVersionExpr = new ExpressionBuilder()\n                    .createConstantStringExpression(PROCESS_VERSION);\n            final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder()\n                    .createNewInstance(\"callingProcess\", PROCESS_VERSION);\n            processDefBuilder.addActor(ACTOR_NAME);\n            processDefBuilder.addStartEvent(\"start\");\n            processDefBuilder.addCallActivity(\"callActivity\", targetProcessNameExpr, targetProcessVersionExpr);\n            processDefBuilder.addEndEvent(\"end\");\n            processDefBuilder.addTransition(\"start\", \"callActivity\");\n            processDefBuilder.addTransition(\"callActivity\", \"end\");\n            callingProcessDefinition = deployAndEnableProcessWithActor(processDefBuilder.done(), ACTOR_NAME, cascao);\n            final ProcessInstance callingProcessInstance = getProcessAPI()\n                    .startProcess(callingProcessDefinition.getId());\n            final long tStep1Id = waitForUserTask(callingProcessInstance, \"tStep1\");\n            assertThat(getProcessAPI().getActivityDataInstance(\"rootProcId\", tStep1Id).getValue())\n                    .isEqualTo(callingProcessInstance.getId());\n        } finally {\n            disableAndDeleteProcess(callingProcessDefinition, targetProcessDefinition);\n        }\n    }\n\n    @Test\n    public void callActivityWithDataMappingAndConnectors() throws Exception {\n        ProcessDefinition targetProcessDefinition = null;\n        ProcessDefinition callingProcessDefinition = null;\n        try {\n            // Build target process\n            final ProcessDefinitionBuilder targetProcessDefBuilder = new ProcessDefinitionBuilder()\n                    .createNewInstance(\"targetProcess\", PROCESS_VERSION);\n            targetProcessDefBuilder.addActor(ACTOR_NAME);\n            targetProcessDefBuilder.addData(\"subProcessData\", String.class.getName(),\n                    new ExpressionBuilder().createConstantStringExpression(\"subDefault\"));\n            targetProcessDefBuilder.addAutomaticTask(\"tStep1\").addOperation(\n                    new OperationBuilder().createSetDataOperation(\"subProcessData\",\n                            new ExpressionBuilder().createConstantStringExpression(\"subModified\")));\n            targetProcessDefinition = deployAndEnableProcessWithActor(targetProcessDefBuilder.done(), ACTOR_NAME,\n                    cebolinha);\n            // Build and start calling process\n            final Expression targetProcessNameExpr = new ExpressionBuilder()\n                    .createConstantStringExpression(\"targetProcess\");\n            final Expression targetProcessVersionExpr = new ExpressionBuilder()\n                    .createConstantStringExpression(PROCESS_VERSION);\n            final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder()\n                    .createNewInstance(\"callingProcess\", PROCESS_VERSION);\n            processDefBuilder.addActor(ACTOR_NAME);\n            processDefBuilder.addData(\"parentProcessData\", String.class.getName(),\n                    new ExpressionBuilder().createConstantStringExpression(\"parentDefault\"));\n            processDefBuilder.addData(\"valueOnCallOnEnter\", String.class.getName(),\n                    new ExpressionBuilder().createConstantStringExpression(\"none\"));\n            processDefBuilder.addData(\"valueOnCallOnFinish\", String.class.getName(),\n                    new ExpressionBuilder().createConstantStringExpression(\"none\"));\n            final CallActivityBuilder callActivityBuilder = processDefBuilder.addCallActivity(\"callActivity\",\n                    targetProcessNameExpr, targetProcessVersionExpr);\n            callActivityBuilder\n                    .addConnector(\"onEnterConnector\", \"org.bonitasoft.connector.testConnectorWithOutput\",\n                            PROCESS_VERSION, ConnectorEvent.ON_ENTER)\n                    .addInput(\"input1\",\n                            new ExpressionBuilder().createDataExpression(\"parentProcessData\", String.class.getName()))\n                    .addOutput(new LeftOperandBuilder().createNewInstance().setName(\"valueOnCallOnEnter\").done(),\n                            OperatorType.ASSIGNMENT, \"=\", \"\",\n                            new ExpressionBuilder().createInputExpression(CONNECTOR_OUTPUT_NAME,\n                                    String.class.getName()));\n            callActivityBuilder\n                    .addConnector(\"onFinishConnector\", \"org.bonitasoft.connector.testConnectorWithOutput\",\n                            PROCESS_VERSION, ConnectorEvent.ON_FINISH)\n                    .addInput(\"input1\",\n                            new ExpressionBuilder().createDataExpression(\"parentProcessData\", String.class.getName()))\n                    .addOutput(new LeftOperandBuilder().createNewInstance().setName(\"valueOnCallOnFinish\").done(),\n                            OperatorType.ASSIGNMENT, \"=\", \"\",\n                            new ExpressionBuilder().createInputExpression(CONNECTOR_OUTPUT_NAME,\n                                    String.class.getName()));\n            callActivityBuilder\n                    .addDataOutputOperation(new OperationBuilder().createSetDataOperation(\"parentProcessData\",\n                            new ExpressionBuilder().createDataExpression(\"subProcessData\", String.class.getName())));\n            processDefBuilder.addUserTask(\"end\", ACTOR_NAME);\n            processDefBuilder.addTransition(\"callActivity\", \"end\");\n            final BusinessArchiveBuilder bizArchive = new BusinessArchiveBuilder();\n            bizArchive.createNewBusinessArchive();\n            bizArchive.setProcessDefinition(processDefBuilder.done());\n            bizArchive.addConnectorImplementation(\n                    new BarResource(\"TestConnectorWithOutput.impl\", IOUtils.toByteArray(CommonAPIIT.class\n                            .getResourceAsStream(\"/org/bonitasoft/engine/connectors/TestConnectorWithOutput.impl\"))));\n            bizArchive.addClasspathResource(\n                    new BarResource(\"TestConnectorWithOutput.jar\", IOUtil.generateJar(TestConnectorWithOutput.class)));\n            callingProcessDefinition = deployAndEnableProcessWithActor(bizArchive.done(), ACTOR_NAME, cascao);\n            final ProcessInstance callingProcessInstance = getProcessAPI()\n                    .startProcess(callingProcessDefinition.getId());\n            final long endId = waitForUserTask(callingProcessInstance, \"end\");\n            assertThat(getProcessAPI().getActivityDataInstance(\"valueOnCallOnEnter\", endId).getValue())\n                    .isEqualTo(\"parentDefault\");\n            assertThat(getProcessAPI().getActivityDataInstance(\"valueOnCallOnFinish\", endId).getValue())\n                    .isEqualTo(\"subModified\");\n        } finally {\n            disableAndDeleteProcess(callingProcessDefinition, targetProcessDefinition);\n        }\n    }\n\n    @Test\n    public void transfert_a_custom_data_and_contract_input_from_a_parent_process_to_a_child_and_vice_versa()\n            throws Exception {\n        final ExpressionBuilder expressionBuilder = new ExpressionBuilder();\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"child\", \"1.0\");\n        builder.addActor(ACTOR_NAME);\n        builder.addContract().addInput(\"isOk\", Type.BOOLEAN, \"isOk is the process contract input\");\n        builder.addBooleanData(\"dataOk\",\n                new ExpressionBuilder().createContractInputExpression(\"isOk\", Boolean.class.getName()));\n        builder.addData(\"data1\", \"org.bonitasoft.complextypes.MyType\", null);\n        builder.addUserTask(\"childTask\", ACTOR_NAME).addOperation(\n                new OperationBuilder().createJavaMethodOperation(\"data1\", \"setName\", String.class.getName(),\n                        expressionBuilder.createConstantStringExpression(\"World\")));\n        final BusinessArchive childBA = buildBusinessArchive(builder.done());\n\n        builder.createNewInstance(\"parent\", \"1.0\");\n        builder.addActor(ACTOR_NAME);\n        builder.addData(\"data1\", \"org.bonitasoft.complextypes.MyType\",\n                expressionBuilder.createGroovyScriptExpression(\"initMyType\",\n                        \"import org.bonitasoft.complextypes.MyType; new MyType()\",\n                        \"org.bonitasoft.complextypes.MyType\"));\n        builder.addShortTextData(\"name\", null);\n        final CallActivityBuilder callActivityBuilder = builder.addCallActivity(\"caTask\",\n                expressionBuilder.createConstantStringExpression(\"child\"),\n                expressionBuilder.createConstantStringExpression(\"1.0\"));\n        callActivityBuilder.addDataInputOperation(\n                BuildTestUtil.buildAssignOperation(\"data1\", \"data1\", ExpressionType.TYPE_VARIABLE,\n                        \"org.bonitasoft.complextypes.MyType\"));\n\n        callActivityBuilder.addProcessStartContractInput(\"isOk\",\n                new ExpressionBuilder().createConstantBooleanExpression(true));\n\n        callActivityBuilder.addDataOutputOperation(\n                BuildTestUtil.buildAssignOperation(\"data1\", \"data1\", ExpressionType.TYPE_VARIABLE,\n                        \"org.bonitasoft.complextypes.MyType\"));\n        final Expression initIndex = expressionBuilder.createGroovyScriptExpression(\"initIndex\", \"data1.getName()\",\n                String.class.getName(),\n                expressionBuilder.createDataExpression(\"data1\", \"org.bonitasoft.complextypes.MyType\"));\n        builder.addAutomaticTask(\"setData\")\n                .addOperation(new OperationBuilder().createSetDataOperation(\"name\", initIndex));\n        builder.addUserTask(\"parentTask\", ACTOR_NAME);\n        builder.addTransition(\"caTask\", \"setData\");\n        builder.addTransition(\"setData\", \"parentTask\");\n        final BusinessArchive parentBA = buildBusinessArchive(builder.done());\n\n        final ProcessDefinition childProcDefinition = deployAndEnableProcessWithActor(childBA, ACTOR_NAME, cascao);\n        final ProcessDefinition parentProcDefinition = deployAndEnableProcessWithActor(parentBA, ACTOR_NAME, cascao);\n\n        final ProcessInstance instance = getProcessAPI().startProcess(parentProcDefinition.getId());\n\n        // Check that contract input has been correctly passed by CallActivity:\n        final long childTask = waitForUserTask(\"childTask\");\n        final HumanTaskInstance humanTaskInstance = getProcessAPI().getHumanTaskInstance(childTask);\n        final DataInstance dataOk = getProcessAPI().getActivityDataInstance(\"dataOk\", humanTaskInstance.getId());\n        assertThat(((Boolean) dataOk.getValue())).isTrue();\n\n        assignAndExecuteStep(childTask, cascao);\n        waitForUserTask(\"parentTask\");\n\n        final DataInstance dataInstance = getProcessAPI().getProcessDataInstance(\"name\", instance.getId());\n        assertThat(dataInstance.getValue()).isEqualTo(\"World\");\n\n        disableAndDeleteProcess(parentProcDefinition, childProcDefinition);\n    }\n\n    private BusinessArchive buildBusinessArchive(final DesignProcessDefinition process) throws Exception {\n        InputStream inputStream = null;\n        try {\n            inputStream = this.getClass().getResourceAsStream(\"/org.bonitasoft.complextypes.jar-bak\");\n            final byte[] content = IOUtils.toByteArray(inputStream);\n            final BarResource resource = new BarResource(\"org.bonitasoft.complextypes.jar\", content);\n            final BusinessArchiveBuilder baBuilder = new BusinessArchiveBuilder();\n            baBuilder.createNewBusinessArchive().setProcessDefinition(process).addClasspathResource(resource);\n            return baBuilder.done();\n        } finally {\n            if (inputStream != null) {\n                inputStream.close();\n            }\n        }\n    }\n\n    @Test\n    public void callActivity_classloader_handling_on_child_process() throws Exception {\n        /*\n         * BS-12674: issue when archiving called process, it's done in the wrong classloader\n         */\n        ProcessDefinition targetProcessDefinition = null;\n        ProcessDefinition callingProcessDefinition = null;\n        try {\n\n            // Build target process\n            final ProcessDefinitionBuilder targetProcessDefBuilder = new ProcessDefinitionBuilder()\n                    .createNewInstance(\"targetProcess\", PROCESS_VERSION);\n            targetProcessDefBuilder.addActor(ACTOR_NAME);\n            targetProcessDefBuilder.addData(\"subProcessData\", String.class.getName(),\n                    new ExpressionBuilder().createConstantStringExpression(\"subDefault\"));\n            targetProcessDefBuilder\n                    .addData(\n                            \"dataActivity\",\n                            java.lang.Object.class.getName(),\n                            new ExpressionBuilder().createGroovyScriptExpression(\"myScript\",\n                                    \"new org.bonitasoft.dfgdfg.Restaurant()\",\n                                    java.lang.Object.class.getName()));\n            targetProcessDefBuilder.addAutomaticTask(\"tStep1\").addOperation(\n                    new OperationBuilder().createSetDataOperation(\"subProcessData\",\n                            new ExpressionBuilder().createConstantStringExpression(\"subModified\")));\n            BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive();\n            businessArchiveBuilder.setProcessDefinition(targetProcessDefBuilder.done());\n            //add a jar that is not known from the parent process\n            businessArchiveBuilder\n                    .addClasspathResource(getResource(\"/org.bonitasoft.dfgdfg.bak\", \"org.bonitasoft.dfgdfg.jar\"));\n            targetProcessDefinition = deployAndEnableProcessWithActor(businessArchiveBuilder.done(), ACTOR_NAME,\n                    cebolinha);\n            // Build and start calling process\n            final Expression targetProcessNameExpr = new ExpressionBuilder()\n                    .createConstantStringExpression(\"targetProcess\");\n            final Expression targetProcessVersionExpr = new ExpressionBuilder()\n                    .createConstantStringExpression(PROCESS_VERSION);\n            final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder()\n                    .createNewInstance(\"callingProcess\", PROCESS_VERSION);\n            processDefBuilder.addActor(ACTOR_NAME);\n            processDefBuilder.addData(\"parentProcessData\", String.class.getName(),\n                    new ExpressionBuilder().createConstantStringExpression(\"parentDefault\"));\n            final CallActivityBuilder callActivityBuilder = processDefBuilder.addCallActivity(\"callActivity\",\n                    targetProcessNameExpr, targetProcessVersionExpr);\n            callActivityBuilder\n                    .addDataOutputOperation(new OperationBuilder().createSetDataOperation(\"parentProcessData\",\n                            new ExpressionBuilder().createDataExpression(\"subProcessData\", String.class.getName())));\n            processDefBuilder.addUserTask(\"end\", ACTOR_NAME);\n            processDefBuilder.addTransition(\"callActivity\", \"end\");\n            final BusinessArchiveBuilder bizArchive = new BusinessArchiveBuilder();\n            bizArchive.createNewBusinessArchive();\n            bizArchive.setProcessDefinition(processDefBuilder.done());\n            callingProcessDefinition = deployAndEnableProcessWithActor(bizArchive.done(), ACTOR_NAME, cascao);\n\n            final ProcessInstance callingProcessInstance = getProcessAPI()\n                    .startProcess(callingProcessDefinition.getId());\n            waitForUserTask(callingProcessInstance, \"end\");\n        } finally {\n            disableAndDeleteProcess(callingProcessDefinition);\n            disableAndDeleteProcess(targetProcessDefinition);\n        }\n    }\n\n    @Test\n    public void should_abort_call_activity_when_sub_process_is_triggered() throws Exception {\n\n        ProcessDefinition mainProcess = getProcessAPI()\n                .deployAndEnableProcess(\n                        new ProcessDefinitionBuilder().createNewInstance(\"processWithCallActivityAndTimerESB\", \"1.0\")\n                                .addStartEvent(\"start\")\n                                .addCallActivity(\"call\",\n                                        new ExpressionBuilder().createConstantStringExpression(\"calledProcess\"),\n                                        new ExpressionBuilder().createConstantStringExpression(\"1.0\"))\n                                .addTransition(\"start\", \"call\")\n                                .getProcess());\n        ProcessDefinition calledProcess = getProcessAPI()\n                .deployAndEnableProcess(new ProcessDefinitionBuilder().createNewInstance(\"calledProcess\", \"1.0\")\n                        //not the normal flow: no elements started\n                        .addStartEvent(\"signalStart\").addSignalEventTrigger(\"calledProcSignal\")\n                        .addAutomaticTask(\"auto2\")\n                        .addTransition(\"signalStart\", \"auto2\").getProcess());\n\n        ProcessInstance processInstance = getProcessAPI().startProcess(mainProcess.getId());\n\n        waitForProcessToBeInState(processInstance, ProcessInstanceState.COMPLETED);\n\n        disableAndDeleteProcess(mainProcess, calledProcess);\n    }\n\n    @Test\n    public void should_be_able_to_cancel_process_with_call_activity_calling_unknown_process() throws Exception {\n        //given\n        ProcessDefinition processDefinition = getProcessAPI()\n                .deployAndEnableProcess(\n                        new ProcessDefinitionBuilder().createNewInstance(\"processThatCallUnknownProcess\", \"1.0\")\n                                .addStartEvent(\"start\")\n                                .addCallActivity(\"call\",\n                                        new ExpressionBuilder().createConstantStringExpression(\"unknownProcess\"),\n                                        new ExpressionBuilder().createConstantStringExpression(\"1.0\"))\n                                .addTransition(\"start\", \"call\")\n                                .getProcess());\n        //when\n        ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        ActivityInstance activityInstance = waitForTaskToFail(processInstance);\n        assertThat(activityInstance.getName()).isEqualTo(\"call\");\n        getProcessAPI().cancelProcessInstance(processInstance.getId());\n        waitForProcessToBeInState(processInstance, ProcessInstanceState.CANCELLED);\n\n        //then\n        try {\n            getProcessAPI().getProcessInstance(processInstance.getId());\n            fail(\"process should not exist anymore\");\n        } catch (ProcessInstanceNotFoundException ignored) {\n        }\n        //verify the call activity was archived in cancelled state:\n        SearchResult<ArchivedFlowNodeInstance> archivedFlowNodeInstances = getProcessAPI()\n                .searchArchivedFlowNodeInstances(new SearchOptionsBuilder(0, 10)\n                        .filter(ArchivedFlowNodeInstanceSearchDescriptor.NAME, \"call\")\n                        .filter(ArchivedFlowNodeInstanceSearchDescriptor.TERMINAL, true).done());\n        assertThat(archivedFlowNodeInstances.getResult()).hasSize(1);\n        assertThat(archivedFlowNodeInstances.getResult().get(0).getState()).isEqualTo(\"cancelled\");\n        disableAndDeleteProcess(processDefinition);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/activity/ContractIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.activity;\n\nimport static org.assertj.core.api.Assertions.*;\n\nimport java.io.IOException;\nimport java.io.Serializable;\nimport java.math.BigDecimal;\nimport java.math.BigInteger;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.OffsetDateTime;\nimport java.time.ZoneOffset;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.CommonAPIIT;\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.bpm.contract.ConstraintDefinition;\nimport org.bonitasoft.engine.bpm.contract.ContractDefinition;\nimport org.bonitasoft.engine.bpm.contract.ContractViolationException;\nimport org.bonitasoft.engine.bpm.contract.FileInputValue;\nimport org.bonitasoft.engine.bpm.contract.InputDefinition;\nimport org.bonitasoft.engine.bpm.contract.Type;\nimport org.bonitasoft.engine.bpm.contract.impl.InputDefinitionImpl;\nimport org.bonitasoft.engine.bpm.data.ArchivedDataInstance;\nimport org.bonitasoft.engine.bpm.data.DataInstance;\nimport org.bonitasoft.engine.bpm.document.Document;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeExecutionException;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.UserTaskNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ActivationState;\nimport org.bonitasoft.engine.bpm.process.ConfigurationState;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.bpm.process.impl.ContractDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.SubProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder;\nimport org.bonitasoft.engine.connectors.TestConnectorWithAPICall;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.operation.OperationBuilder;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class ContractIT extends CommonAPIIT {\n\n    private static final String TASK2 = \"task2\";\n    private static final String TASK1 = \"task1\";\n    private User matti;\n\n    @Before\n    public void beforeTest() throws BonitaException {\n        loginWithTechnicalUser();\n        matti = createUser(\"matti\", \"bpm\");\n        logout();\n        loginOnDefaultTenantWith(matti.getUserName(), \"bpm\");\n    }\n\n    @After\n    public void afterTest() throws BonitaException {\n        deleteUser(matti);\n        logout();\n    }\n\n    @Test\n    public void shouldHandleContractInputsAtProcessLevel() throws Exception {\n        //given\n        final String numberOfDaysProcessContractData = \"numberOfDaysProcessContractData\";\n\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"contract\", \"1.0\");\n        // have a initial data value using process input, so that we ensure inputs are treated before data at process instantiation:\n        builder.addData(\"nbDaysProcessData\", Integer.class.getName(),\n                new ExpressionBuilder().createContractInputExpression(numberOfDaysProcessContractData,\n                        Integer.class.getName()));\n        builder.addData(\"idData\", Long.class.getName(),\n                new ExpressionBuilder().createContractInputExpression(\"id\", Long.class.getName()));\n        builder.addData(\"multipleTextData\", List.class.getName(),\n                new ExpressionBuilder().createContractInputExpression(\"multipleText\", List.class.getName()));\n        builder.addData(\"complexData\", Map.class.getName(),\n                new ExpressionBuilder().createContractInputExpression(\"complex\", Map.class.getName()));\n        builder.addActor(ACTOR_NAME);\n        builder.addUserTask(TASK1, ACTOR_NAME);\n        final ContractDefinitionBuilder contract = builder.addContract();\n        contract.addInput(numberOfDaysProcessContractData, Type.INTEGER, null);\n        contract.addInput(\"id\", Type.LONG, null);\n        contract.addInput(\"multipleText\", Type.TEXT, \"a multiple text\", true);\n        contract.addComplexInput(\"complex\", \"a complex input\").addInput(\"text\", Type.TEXT, \"text in complex\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, matti);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertThat(processDeploymentInfo.getConfigurationState()).isEqualTo(ConfigurationState.RESOLVED);\n        assertThat(processDeploymentInfo.getActivationState()).isEqualTo(ActivationState.ENABLED);\n\n        final Map<String, Serializable> inputs = new HashMap<>(1);\n        final int value = 14;\n        inputs.put(numberOfDaysProcessContractData, value);\n        final ArrayList<String> multiples = new ArrayList<>(Arrays.asList(\"String1\", \"String2\"));\n        inputs.put(\"multipleText\", multiples);\n        inputs.put(\"id\", 1L);\n        final HashMap<Object, Object> map = new HashMap<>();\n        map.put(\"text\", \"textValue\");\n        inputs.put(\"complex\", map);\n        //check exception on contract violation\n        try {\n            getProcessAPI().startProcessWithInputs(processDefinition.getId(),\n                    Collections.<String, Serializable> emptyMap());\n            fail(\"Should throw a contract violation exception\");\n        } catch (final ContractViolationException e) {\n            assertThat(e.getMessage()).contains(\"multipleText\", \"complex\");\n        }\n        //start with right inputs\n        final ProcessInstance processInstance = getProcessAPI().startProcessWithInputs(processDefinition.getId(),\n                inputs);\n        waitForUserTask(processInstance, TASK1);\n        final DataInstance processDataValueInitializedFromIntInput = getProcessAPI()\n                .getProcessDataInstance(\"nbDaysProcessData\", processInstance.getId());\n        final DataInstance processDataValueInitializedFromLongInput = getProcessAPI().getProcessDataInstance(\"idData\",\n                processInstance.getId());\n        assertThat(getProcessAPI().getProcessDataInstance(\"multipleTextData\", processInstance.getId()).getValue())\n                .isEqualTo(multiples);\n        assertThat(getProcessAPI().getProcessDataInstance(\"complexData\", processInstance.getId()).getValue())\n                .isEqualTo(map);\n        assertThat(processDataValueInitializedFromIntInput.getValue()).isEqualTo(value);\n        assertThat(processDataValueInitializedFromLongInput.getValue()).isEqualTo(1L);\n\n        final Serializable processInstanciationInputValue = getProcessAPI().getProcessInputValueAfterInitialization(\n                processInstance.getId(),\n                numberOfDaysProcessContractData);\n        assertThat(processInstanciationInputValue).isEqualTo(value);\n\n        //clean up\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void should_process_contract_work_with_event_sub_process() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"contractAndEventSub\",\n                \"1.0\");\n        builder.addShortTextData(\"name\",\n                new ExpressionBuilder().createContractInputExpression(\"nameInput\", String.class.getName()));\n        builder.addActor(ACTOR_NAME);\n        builder.addUserTask(TASK1, ACTOR_NAME);\n        final ContractDefinitionBuilder contract = builder.addContract();\n        contract.addInput(\"nameInput\", Type.TEXT, \"the name\", false);\n        final SubProcessDefinitionBuilder signalEventSubProcess = builder.addSubProcess(\"SignalEventSubProcess\", true)\n                .getSubProcessBuilder();\n        signalEventSubProcess.addStartEvent(\"signalStart\").addSignalEventTrigger(\"*Bip Bip*\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, matti);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcessWithInputs(processDefinition.getId(),\n                Collections.<String, Serializable> singletonMap(\"nameInput\", \"john\"));\n        assertThat(getProcessAPI().getProcessDataInstance(\"name\", processInstance.getId()).getValue())\n                .as(\"value of the data 'name' of the process\")\n                .isEqualTo(\"john\");\n        waitForUserTask(TASK1);\n\n        getProcessAPI().sendSignal(\"*Bip Bip*\");\n\n        waitForProcessToBeInState(processInstance, ProcessInstanceState.ABORTED);\n\n        //clean up\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void should_getUserTaskContract_return_the_contract() throws Exception {\n        //given\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"contract\", \"1.0\");\n        builder.addActor(ACTOR_NAME);\n        builder.addUserTask(TASK1, ACTOR_NAME).addContract().addInput(\"numberOfDays\", Type.INTEGER, null)\n                .addConstraint(\"Mystical constraint\", \"true\", null, \"numberOfDays\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, matti);\n        getProcessAPI().startProcess(processDefinition.getId());\n        final HumanTaskInstance userTask = waitForUserTaskAndGetIt(TASK1);\n\n        //when\n        final ContractDefinition contract = getProcessAPI().getUserTaskContract(userTask.getId());\n\n        //then\n        assertThat(contract.getInputs()).hasSize(1);\n        final InputDefinition input = contract.getInputs().get(0);\n        assertThat(input.getName()).isEqualTo(\"numberOfDays\");\n        assertThat(input.getType()).isEqualTo(Type.INTEGER);\n        assertThat(input.getDescription()).isNull();\n\n        assertThat(contract.getConstraints()).hasSize(1);\n        final ConstraintDefinition mysticRule = contract.getConstraints().get(0);\n        assertThat(mysticRule.getName()).isEqualTo(\"Mystical constraint\");\n        assertThat(mysticRule.getInputNames()).containsExactly(\"numberOfDays\");\n        assertThat(mysticRule.getExplanation()).isNull();\n\n        //clean up\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void should_getUserTaskContract_return_contract_with_complex_inputs() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"contract\", \"1.0\");\n        builder.addActor(ACTOR_NAME);\n        builder.addDocumentDefinition(\"myDoc\")\n                .addInitialValue(new ExpressionBuilder().createContractInputExpression(\"reportInit\",\n                        FileInputValue.class.getName()));\n        //given\n        ContractDefinitionBuilder contractDefinitionBuilder = builder.addUserTask(TASK1, ACTOR_NAME).addContract();\n        contractDefinitionBuilder\n                .addComplexInput(\"expenseLine\", \"expense report line\", true)\n                .addInput(\"date\", Type.DATE, \"expense date\")\n                .addInput(\"amount\", Type.DECIMAL, \"expense amount\")\n                .addComplexInput(\"date\", \"expense date\").addInput(\"expenseType\", Type.TEXT, \"describe expense type\");\n        contractDefinitionBuilder.addFileInput(\"report\", \"myReport\");\n        contractDefinitionBuilder = builder.addContract();\n        contractDefinitionBuilder.addFileInput(\"reportInit\", \"myReport\");\n        //when\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, matti);\n        final ProcessInstance processInstance = getProcessAPI().startProcessWithInputs(processDefinition.getId(),\n                Collections.<String, Serializable> singletonMap(\"reportInit\",\n                        new FileInputValue(\"theFile\", \"\", \"theContent\".getBytes())));\n        final HumanTaskInstance userTask = waitForUserTaskAndGetIt(TASK1);\n\n        //then\n        final ContractDefinition contract = getProcessAPI().getUserTaskContract(userTask.getId());\n        assertThat(contract.getInputs()).hasSize(2);\n        final InputDefinition complexInput = contract.getInputs().get(0);\n        assertThat(complexInput.getName()).isEqualTo(\"expenseLine\");\n        assertThat(complexInput.isMultiple()).as(\"should be multiple\").isTrue();\n        assertThat(complexInput.getDescription()).isEqualTo(\"expense report line\");\n        assertThat(complexInput.getInputs()).as(\"should have 3 inputs\").hasSize(3);\n        final InputDefinition fileInput = contract.getInputs().get(1);\n        assertThat(fileInput.getInputs()).hasSize(2);\n        assertThat(fileInput.getInputs()).containsExactly(new InputDefinitionImpl(\"filename\", \"Name of the file\"),\n                new InputDefinitionImpl(\"content\", \"Content of the file\"));\n        final Document myDoc = getProcessAPI().getLastDocument(processInstance.getId(), \"myDoc\");\n        final byte[] documentContent = getProcessAPI().getDocumentContent(myDoc.getContentStorageId());\n        assertThat(new String(documentContent)).isEqualTo(\"theContent\");\n\n        //clean up\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void should_create_a_contract_with_special_char() throws Exception {\n        final long[] badValues = { 0, 366 };\n        for (final long badValue : badValues) {\n            check_invalid_contract_with_special_char(badValue);\n        }\n    }\n\n    @Test\n    public void should_execute_a_contract_with_xml_tag_in_rule() throws Exception {\n        //given\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"contract\", \"1.0\");\n        builder.addActor(ACTOR_NAME);\n        final String expectedExplanation = \"numberOfDays must between one day and one year\";\n        builder.addUserTask(TASK1, ACTOR_NAME).addContract().addInput(\"comment\", Type.TEXT, null)\n                .addConstraint(\"mandatory\", \"comment.equals(\\\"<tag>\\\")\", expectedExplanation, \"comment\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, matti);\n        getProcessAPI().startProcess(processDefinition.getId());\n\n        //when\n        final HumanTaskInstance task = waitForUserTaskAndGetIt(TASK1);\n        getProcessAPI().assignUserTask(task.getId(), matti.getId());\n\n        //then no exceptions\n        final Map<String, Serializable> map = new HashMap<>();\n        map.put(\"comment\", \"<tag>\");\n        getProcessAPI().executeUserTask(task.getId(), map);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void should_execute_a_contract_with_integer_in_decimal_or_long() throws Exception {\n        //given\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"contract\", \"1.0\");\n        builder.addActor(ACTOR_NAME);\n        final UserTaskDefinitionBuilder userTask = builder.addUserTask(TASK1, ACTOR_NAME);\n        userTask.addContract().addInput(\"decimal\", Type.DECIMAL, null);\n        userTask.addContract().addInput(\"long\", Type.LONG, null);\n        userTask.addData(\"decimalVariable\", Number.class.getName(), null);\n        userTask.addData(\"longVariable\", Number.class.getName(), null);\n        userTask.addOperation(new OperationBuilder().createSetDataOperation(\"decimalVariable\",\n                new ExpressionBuilder().createContractInputExpression(\"decimal\", Number.class.getName())));\n        userTask.addOperation(new OperationBuilder().createSetDataOperation(\"longVariable\",\n                new ExpressionBuilder().createContractInputExpression(\"long\", Number.class.getName())));\n\n        //when\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, matti);\n        getProcessAPI().startProcess(processDefinition.getId());\n        final HumanTaskInstance task = waitForUserTaskAndGetIt(TASK1);\n        getProcessAPI().assignUserTask(task.getId(), matti.getId());\n\n        //then\n        final Map<String, Serializable> map = new HashMap<>();\n        map.put(\"decimal\", 2);\n        map.put(\"long\", 100);\n        getProcessAPI().executeUserTask(task.getId(), map);\n\n        //clean up\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void should_invalid_contract_throw_ContractViolationException() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"contract\", \"1.0\");\n        builder.addActor(ACTOR_NAME);\n        builder.addUserTask(TASK1, ACTOR_NAME).addContract().addInput(\"numberOfDays\", Type.INTEGER, null);\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, matti);\n        getProcessAPI().startProcess(processDefinition.getId());\n        final HumanTaskInstance userTask = waitForUserTaskAndGetIt(TASK1);\n\n        try {\n            getProcessAPI().assignAndExecuteUserTask(matti.getId(), userTask.getId(),\n                    new HashMap<String, Serializable>());\n            fail(\"The contract is not enforced\");\n        } catch (final ContractViolationException e) {\n            final String state = getProcessAPI().getActivityInstanceState(userTask.getId());\n            assertThat(state).isEqualTo(\"ready\");\n            assertThat(e.getExplanations()).containsExactly(\"Expected input [numberOfDays] is missing\");\n            assertThat(userTask.getAssigneeId()).isNotEqualTo(matti.getId());\n        }\n\n        getProcessAPI().assignUserTask(userTask.getId(), matti.getId());\n        try {\n            getProcessAPI().executeFlowNode(userTask.getId());\n            fail(\"The contract is not enforced\");\n        } catch (final FlowNodeExecutionException e) {\n            final String state = getProcessAPI().getActivityInstanceState(userTask.getId());\n            assertThat(state).isEqualTo(\"ready\");\n        }\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void should_valid_contract_with_invalid_operation_do_not_throw_exception() throws Exception {\n        /*\n         * the operation should be executed asynchronously, not in the same call as the contract input\n         */\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"contract\", \"1.0\");\n        builder.addActor(ACTOR_NAME);\n        builder.addShortTextData(\"dataName\", null);\n        UserTaskDefinitionBuilder userTaskDefinitionBuilder = builder.addUserTask(TASK1, ACTOR_NAME);\n        userTaskDefinitionBuilder.addContract().addInput(\"numberOfDays\", Type.INTEGER, null);\n        userTaskDefinitionBuilder.addOperation(new OperationBuilder().createSetDataOperation(\"dataName\",\n                new ExpressionBuilder().createGroovyScriptExpression(\"script\", \"throw new java.lang.RuntimeException()\",\n                        String.class.getName())));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, matti);\n        ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final HumanTaskInstance userTask = waitForUserTaskAndGetIt(TASK1);\n        getProcessAPI().assignUserTask(userTask.getId(), matti.getId());\n\n        getProcessAPI().executeUserTask(userTask.getId(),\n                Collections.<String, Serializable> singletonMap(\"numberOfDays\", 123));\n        waitForFlowNodeInFailedState(processInstance, TASK1);\n        //no api method to check that the contract data are persisted (it only gets the archived version)\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Test\n    public void use_a_multiple_complex_input_in_user_tasks() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"contract\", \"1.0\");\n        builder.addActor(ACTOR_NAME);\n        builder.addDocumentDefinition(\"reportAsDoc\");\n        builder.addDocumentListDefinition(\"receiptsAsDoc\");\n        //given\n        final UserTaskDefinitionBuilder userTaskDefinitionBuilder = builder.addUserTask(TASK1, ACTOR_NAME);\n        final ContractDefinitionBuilder contractDefinitionBuilder = userTaskDefinitionBuilder.addContract();\n        contractDefinitionBuilder\n                .addComplexInput(\"expenseReport\", \"expense report with several expense lines\", true)\n                .addInput(\"expenseType\", Type.TEXT, \"describe expense type\")\n                .addInput(\"expenseAmount\", Type.DECIMAL, \"expense amount\")\n                .addInput(\"expenseDate\", Type.DATE, \"expense date\")\n                .addInput(\"expenseProof\", Type.BYTE_ARRAY, \"expense proof\");\n        contractDefinitionBuilder.addFileInput(\"report\", \"the report\")\n                .addFileInput(\"receipts\", \"the receipts\", true)\n                .addConstraint(\"report content not empty\", \"report.content.length > 1\", \"report content is empty\",\n                        \"report\");\n\n        userTaskDefinitionBuilder.addData(\"expenseData\", List.class.getName(), null);\n        userTaskDefinitionBuilder.addData(\"reportData\", List.class.getName(), null);\n        userTaskDefinitionBuilder.addData(\"receiptsData\", List.class.getName(), null);\n        userTaskDefinitionBuilder.addOperation(new OperationBuilder().createSetDataOperation(\"expenseData\",\n                new ExpressionBuilder().createContractInputExpression(\"expenseReport\", List.class.getName())));\n        userTaskDefinitionBuilder.addOperation(new OperationBuilder().createSetDataOperation(\"reportData\",\n                new ExpressionBuilder().createContractInputExpression(\"report\", FileInputValue.class.getName())));\n        userTaskDefinitionBuilder.addOperation(new OperationBuilder().createSetDataOperation(\"receiptsData\",\n                new ExpressionBuilder().createContractInputExpression(\"receipts\", List.class.getName())));\n        userTaskDefinitionBuilder.addOperation(new OperationBuilder().createSetDocument(\"reportAsDoc\",\n                new ExpressionBuilder().createContractInputExpression(\"report\", FileInputValue.class.getName())));\n        userTaskDefinitionBuilder.addOperation(new OperationBuilder().createSetDocumentList(\"receiptsAsDoc\",\n                new ExpressionBuilder().createContractInputExpression(\"receipts\", List.class.getName())));\n        builder.addUserTask(TASK2, ACTOR_NAME).addTransition(TASK1, TASK2);\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, matti);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        //when\n        final HumanTaskInstance userTask = waitForUserTaskAndGetIt(TASK1);\n        getProcessAPI().assignUserTask(userTask.getId(), matti.getId());\n\n        final List<Map<String, Serializable>> expenseReport = new ArrayList<>();\n        expenseReport.add(createExpenseLine(\"hotel\", 150.3f, new Date(System.currentTimeMillis()), new byte[0]));\n        expenseReport.add(createExpenseLine(\"taxi\", 25, new Date(System.currentTimeMillis()), new byte[0]));\n        expenseReport.add(createExpenseLine(\"plane\", 500, new Date(System.currentTimeMillis()),\n                new byte[] { 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1 }));\n\n        final Map<String, Serializable> taskInput = new HashMap<>();\n        taskInput.put(\"expenseReport\", (Serializable) expenseReport);\n        final FileInputValue reportFile = new FileInputValue(\"report.pdf\", \"\", new byte[] { 0, 1, 2, 3 });\n        taskInput.put(\"report\", reportFile);\n        final FileInputValue receipt1 = new FileInputValue(\"receipt1.pdf\", \"\", new byte[] { 0, 1, 2, 4 });\n        final FileInputValue receipt2 = new FileInputValue(\"receipt2.pdf\", \"\", new byte[] { 0, 1, 2, 5 });\n        taskInput.put(\"receipts\", (Serializable) Arrays.asList(receipt1, receipt2));\n\n        getProcessAPI().executeUserTask(userTask.getId(), taskInput);\n\n        //then\n        waitForUserTaskAndGetIt(TASK2);\n        assertThat((List<Map<String, Object>>) getProcessAPI()\n                .getArchivedActivityDataInstance(\"expenseData\", userTask.getId()).getValue()).as(\n                        \"should have my expense report data\")\n                .hasSize(3);\n        final Serializable reportData = getProcessAPI().getArchivedActivityDataInstance(\"reportData\", userTask.getId())\n                .getValue();\n        assertThat(reportData).as(\"should have single file\").isEqualTo(reportFile);\n        assertThat((List<Object>) getProcessAPI().getArchivedActivityDataInstance(\"receiptsData\", userTask.getId())\n                .getValue()).as(\"should have multiple file\")\n                .containsExactly(receipt1, receipt2);\n        final List<Document> receiptsAsDoc = getProcessAPI().getDocumentList(processInstance.getId(), \"receiptsAsDoc\",\n                0, 100);\n        final Document reportAsDoc = getProcessAPI().getLastDocument(processInstance.getId(), \"reportAsDoc\");\n\n        assertThat(reportAsDoc.getContentFileName()).as(\"document file name\").isEqualTo(\"report.pdf\");\n        assertThat(getProcessAPI().getDocumentContent(reportAsDoc.getContentStorageId())).as(\"document content\")\n                .isEqualTo(new byte[] { 0, 1, 2, 3 });\n        assertThat(receiptsAsDoc).hasSize(2);\n        assertThat(receiptsAsDoc).extracting(\"contentFileName\", \"index\").containsExactly(tuple(\"receipt1.pdf\", 0),\n                tuple(\"receipt2.pdf\", 1));\n        assertThat(getProcessAPI().getDocumentContent(receiptsAsDoc.get(0).getContentStorageId()))\n                .as(\"document content\").isEqualTo(new byte[] { 0, 1, 2, 4 });\n        assertThat(getProcessAPI().getDocumentContent(receiptsAsDoc.get(1).getContentStorageId()))\n                .as(\"document content\").isEqualTo(new byte[] { 0, 1, 2, 5 });\n\n        //clean up\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    private Map<String, Serializable> createExpenseLine(final String expenseType, final float expenseAmount,\n            final Date expenseDate,\n            final byte[] expenseProof) {\n        final Map<String, Serializable> expenseLine = new HashMap<>();\n        expenseLine.put(\"expenseType\", expenseType);\n        expenseLine.put(\"expenseAmount\", expenseAmount);\n        expenseLine.put(\"expenseDate\", expenseDate);\n        expenseLine.put(\"expenseProof\", expenseProof);\n        return expenseLine;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Test\n    public void use_a_multiple_simple_input_in_user_tasks() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"contract\", \"1.0\");\n        builder.addActor(ACTOR_NAME);\n\n        //given\n        final UserTaskDefinitionBuilder userTaskDefinitionBuilder = builder.addUserTask(TASK1, ACTOR_NAME);\n        userTaskDefinitionBuilder.addContract()\n                .addInput(\"input\", Type.TEXT, \"multiple input\", true);\n        final List<String> inputs = new ArrayList<>();\n        userTaskDefinitionBuilder.addData(\"inputListData\", inputs.getClass().getName(), null);\n        userTaskDefinitionBuilder.addOperation(new OperationBuilder().createSetDataOperation(\"inputListData\",\n                new ExpressionBuilder().createContractInputExpression(\"input\", inputs.getClass().getName())));\n        builder.addUserTask(TASK2, ACTOR_NAME).addTransition(TASK1, TASK2);\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, matti);\n        getProcessAPI().startProcess(processDefinition.getId());\n\n        //when\n        final HumanTaskInstance userTask = waitForUserTaskAndGetIt(TASK1);\n        getProcessAPI().assignUserTask(userTask.getId(), matti.getId());\n\n        final Map<String, Serializable> taskInputs = new HashMap<>();\n        final List<String> inputList = new ArrayList<>();\n        inputList.add(\"input1\");\n        inputList.add(\"input2\");\n        inputList.add(\"input3\");\n        inputList.add(\"input4\");\n        inputList.add(\"input5\");\n        taskInputs.put(\"input\", (Serializable) inputList);\n        getProcessAPI().executeUserTask(userTask.getId(), taskInputs);\n\n        //then\n        waitForUserTaskAndGetIt(TASK2);\n\n        final ArchivedDataInstance archivedResult = getProcessAPI().getArchivedActivityDataInstance(\"inputListData\",\n                userTask.getId());\n        assertThat((List<String>) archivedResult.getValue()).as(\"should have a list of simple input\")\n                .hasSameSizeAs(inputList);\n\n        //clean up\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void should_execute_user_task_when_contract_is_valid() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"contract\", \"1.0\");\n        builder.addActor(ACTOR_NAME);\n        final UserTaskDefinitionBuilder userTaskBuilder = builder.addUserTask(TASK1, ACTOR_NAME);\n        userTaskBuilder.addData(\"result\", BigDecimal.class.getName(), null);\n        userTaskBuilder.addOperation(new OperationBuilder().createSetDataOperation(\"result\",\n                new ExpressionBuilder().createContractInputExpression(\"numberOfDays\", Long.class.getName())));\n        builder.addUserTask(TASK2, ACTOR_NAME).addTransition(TASK1, TASK2);\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, matti);\n        getProcessAPI().startProcess(processDefinition.getId());\n        final HumanTaskInstance userTask = waitForUserTaskAndGetIt(TASK1);\n        getProcessAPI().assignUserTask(userTask.getId(), matti.getId());\n\n        final long expectedValue = 8l;\n        final Map<String, Serializable> inputs = new HashMap<>();\n        inputs.put(\"numberOfDays\", expectedValue);\n\n        getProcessAPI().executeUserTask(userTask.getId(), inputs);\n        waitForUserTaskAndGetIt(TASK2);\n\n        final ArchivedDataInstance archivedResult = getProcessAPI().getArchivedActivityDataInstance(\"result\",\n                userTask.getId());\n        assertThat(archivedResult.getValue()).isEqualTo(expectedValue);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void execute_user_task_should_throw_UserTaskNotFoundException() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"contract\", \"1.0\");\n        builder.addActor(ACTOR_NAME);\n        final UserTaskDefinitionBuilder userTaskBuilder = builder.addAutomaticTask(\"automaticTask\").addUserTask(TASK1,\n                ACTOR_NAME);\n        userTaskBuilder.addContract().addInput(\"numberOfDays\", Type.INTEGER, null)\n                .addConstraint(\"mandatory\", \"numberOfDays != null\", \"numberOfDays must be set\", \"numberOfDays\");\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, matti);\n        getProcessAPI().startProcess(processDefinition.getId());\n        final HumanTaskInstance userTask = waitForUserTaskAndGetIt(TASK1);\n        getProcessAPI().assignUserTask(userTask.getId(), matti.getId());\n\n        try {\n            final Map<String, Serializable> inputs = new HashMap<>();\n            inputs.put(\"numberOfDays\", 8);\n\n            //when\n            getProcessAPI().executeUserTask(-1l, inputs);\n            fail(\"should have a UserTaskNotFoundException \");\n        } catch (final UserTaskNotFoundException e) {\n            //then\n        } finally {\n            disableAndDeleteProcess(processDefinition);\n        }\n    }\n\n    @Test\n    public void should_ContractIsNotValidException_keep_task_ready() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"contract\", \"1.0\");\n        builder.addActor(ACTOR_NAME);\n        builder.addUserTask(TASK1, ACTOR_NAME).addContract().addInput(\"numberOfDays\", Type.INTEGER, null)\n                .addConstraint(\"mandatory\", \"numberOfDays != null\", \"numberOfDays must be set\", \"numberOfDays\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, matti);\n        getProcessAPI().startProcess(processDefinition.getId());\n        final HumanTaskInstance userTask = waitForUserTaskAndGetIt(TASK1);\n        getProcessAPI().assignUserTask(userTask.getId(), matti.getId());\n\n        try {\n            final Map<String, Serializable> inputs = new HashMap<>();\n            inputs.put(\"numberOfDays\", null);\n\n            getProcessAPI().executeUserTask(userTask.getId(), inputs);\n            fail(\"The contract is not enforced\");\n        } catch (final ContractViolationException e) {\n            final String state = getProcessAPI().getActivityInstanceState(userTask.getId());\n            assertThat(state).isEqualTo(\"ready\");\n            assertThat(e.getExplanations()).as(\"should get explanations\").isNotEmpty();\n            assertThat(e.getMessage()).contains(\"numberOfDays\");\n        }\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void should_connector_use_input_values() throws Exception {\n        final Expression processNameExpression = new ExpressionBuilder().createConstantStringExpression(PROCESS_NAME);\n        final Expression processVersionExpression = new ExpressionBuilder()\n                .createContractInputExpression(\"inputVersion\", String.class.getName());\n        final Expression outputExpression = new ExpressionBuilder().createContractInputExpression(\"processInputId\",\n                BigInteger.class.getName());\n\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION);\n        designProcessDefinition.addActor(ACTOR_NAME);\n        final UserTaskDefinitionBuilder userTaskBuilder = designProcessDefinition.addUserTask(\"task3\", ACTOR_NAME);\n        userTaskBuilder\n                .addConnector(\"myConnector\", \"org.bonitasoft.engine.connectors.TestConnectorWithAPICall\", \"1.0\",\n                        ConnectorEvent.ON_FINISH)\n                .addInput(\"processName\", processNameExpression).addInput(\"processVersion\", processVersionExpression);\n        userTaskBuilder.addContract().addInput(\"inputVersion\", Type.TEXT, null)\n                .addInput(\"processInputId\", Type.INTEGER, null)\n                .addInput(\"LocalDateInput\", Type.LOCALDATE, null)\n                .addInput(\"LocalDateTimeInput\", Type.LOCALDATETIME, null)\n                .addInput(\"LocalDateTimeNullInput\", Type.LOCALDATETIME, null)\n                .addInput(\"OffsetDateTimeInput\", Type.OFFSETDATETIME, null);\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithTestConnectorWithAPICall(\n                designProcessDefinition);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final HumanTaskInstance userTask = waitForUserTaskAndGetIt(\"task3\");\n\n        getProcessAPI().assignUserTask(userTask.getId(), matti.getId());\n        final Map<String, Serializable> inputs = new HashMap<>();\n        inputs.put(\"inputVersion\", PROCESS_VERSION);\n        inputs.put(\"processInputId\", BigInteger.valueOf(45L));\n        inputs.put(\"LocalDateInput\", LocalDate.of(2017, 4, 12));\n        inputs.put(\"LocalDateTimeInput\", LocalDateTime.of(2017, 4, 12, 19, 45, 22));\n        inputs.put(\"LocalDateTimeNullInput\", null);\n        inputs.put(\"OffsetDateTimeInput\",\n                OffsetDateTime.of(LocalDateTime.of(1973, 10, 17, 13, 42, 0), ZoneOffset.ofHours(-4)));\n        getProcessAPI().executeUserTask(userTask.getId(), inputs);\n\n        waitForProcessToBeInState(processInstance, ProcessInstanceState.COMPLETED);\n        LocalDateTime localDateTime = (LocalDateTime) getProcessAPI().getUserTaskContractVariableValue(userTask.getId(),\n                \"LocalDateTimeInput\");\n        LocalDate localDate = (LocalDate) getProcessAPI().getUserTaskContractVariableValue(userTask.getId(),\n                \"LocalDateInput\");\n        LocalDateTime nullLocalDateTime = (LocalDateTime) getProcessAPI()\n                .getUserTaskContractVariableValue(userTask.getId(), \"LocalDateTimeNullInput\");\n        OffsetDateTime offsetDateTime = (OffsetDateTime) getProcessAPI()\n                .getUserTaskContractVariableValue(userTask.getId(), \"OffsetDateTimeInput\");\n        final BigInteger processInputId = (BigInteger) getProcessAPI()\n                .getUserTaskContractVariableValue(userTask.getId(), \"processInputId\");\n        assertThat(processInputId).isEqualTo(BigInteger.valueOf(45L));\n        assertThat(localDate).isNotNull().isEqualTo(LocalDate.of(2017, 4, 12));\n        assertThat(localDateTime).isNotNull().isEqualTo(LocalDateTime.of(2017, 4, 12, 19, 45, 22));\n        assertThat(nullLocalDateTime).isNull();\n        // Dates must always be converted to UTC:\n        assertThat(offsetDateTime)\n                .isEqualTo(OffsetDateTime.of(LocalDateTime.of(1973, 10, 17, 17, 42, 0), ZoneOffset.UTC));\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    private ProcessDefinition deployAndEnableProcessWithTestConnectorWithAPICall(\n            final ProcessDefinitionBuilder processDefinitionBuilder)\n            throws BonitaException, IOException {\n        return deployAndEnableProcessWithActorAndConnector(processDefinitionBuilder, ACTOR_NAME, matti,\n                \"TestConnectorWithAPICall.impl\",\n                TestConnectorWithAPICall.class, \"TestConnectorWithAPICall.jar\");\n    }\n\n    private void check_invalid_contract_with_special_char(final long inputValue) throws Exception {\n        //given\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"contract\", \"1.0\");\n        builder.addActor(ACTOR_NAME);\n        final String expectedExplanation = \"numberOfDays must between one day and one year\";\n        builder.addUserTask(TASK1, ACTOR_NAME).addContract().addInput(\"numberOfDays\", Type.INTEGER, null)\n                .addConstraint(\"mandatory\", \"numberOfDays>1 && numberOfDays<365\", expectedExplanation, \"numberOfDays\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, matti);\n        getProcessAPI().startProcess(processDefinition.getId());\n\n        //when\n        final HumanTaskInstance task = waitForUserTaskAndGetIt(TASK1);\n        final Map<String, Serializable> inputs = new HashMap<>();\n        inputs.put(\"numberOfDays\", inputValue);\n        try {\n            getProcessAPI().executeUserTask(task.getId(), inputs);\n            fail(\"should throw ContractViolationException\");\n        } catch (final ContractViolationException e) {\n            //then\n            assertThat(e.getExplanations()).as(\"rule should be violated\").isNotEmpty().contains(expectedExplanation);\n        } finally {\n            disableAndDeleteProcess(processDefinition);\n\n        }\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/activity/GetPossibleUsersOfPendingHumanTaskIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.activity;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.assertEquals;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.TestWithTechnicalUser;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder;\nimport org.bonitasoft.engine.connectors.VariableStorage;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.identity.Group;\nimport org.bonitasoft.engine.identity.Role;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.test.BuildTestUtil;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class GetPossibleUsersOfPendingHumanTaskIT extends TestWithTechnicalUser {\n\n    private static final String JOHN = \"john\";\n\n    private static final String JACK = \"jack\";\n\n    private User john;\n\n    private User jack;\n\n    private Group group;\n\n    private Role role;\n\n    @Override\n    @Before\n    public void before() throws Exception {\n        super.before();\n        john = createUser(JOHN, \"bpm\");\n        jack = createUser(JACK, \"bpm\");\n        group = createGroup(\"group\");\n        role = createRole(\"role\");\n        loginOnDefaultTenantWith(JOHN, \"bpm\");\n    }\n\n    @Override\n    @After\n    public void after() throws Exception {\n        deleteUser(JOHN);\n        deleteUser(JACK);\n        deleteGroups(group);\n        deleteRoles(role);\n        VariableStorage.clearAll();\n        super.after();\n    }\n\n    @Test\n    public void getPossibleUsersOfTaskUserActor() throws Exception {\n        final long userMembershipId = getIdentityAPI().addUserMembership(jack.getId(), group.getId(), role.getId())\n                .getId();\n\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList(\"step1\"),\n                        Arrays.asList(true));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                jack);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n\n        final List<User> possibleUsers = getProcessAPI().getPossibleUsersOfPendingHumanTask(step1Id, 0, 10);\n        assertEquals(1, possibleUsers.size());\n        assertEquals(jack, possibleUsers.get(0));\n\n        // cleanup:\n        getIdentityAPI().deleteUserMembership(userMembershipId);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getPossibleUsersOfTaskUserActorWithoutMembership() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList(\"step1\"),\n                        Arrays.asList(true));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                jack);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n\n        final List<User> possibleUsers = getProcessAPI().getPossibleUsersOfPendingHumanTask(step1Id, 0, 10);\n        assertEquals(1, possibleUsers.size());\n        assertEquals(jack, possibleUsers.get(0));\n\n        // cleanup:\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getPossibleUsersOfTaskRoleActor() throws Exception {\n        final long userMembershipId = getIdentityAPI().addUserMembership(jack.getId(), group.getId(), role.getId())\n                .getId();\n\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList(\"step1\"),\n                        Arrays.asList(true));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                role);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n\n        final List<User> possibleUsers = getProcessAPI().getPossibleUsersOfPendingHumanTask(step1Id, 0, 10);\n        assertEquals(1, possibleUsers.size());\n        assertEquals(jack, possibleUsers.get(0));\n\n        // cleanup:\n        getIdentityAPI().deleteUserMembership(userMembershipId);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getPossibleUsersOfTaskGroupActor() throws Exception {\n        final long userMembershipId = getIdentityAPI().addUserMembership(jack.getId(), group.getId(), role.getId())\n                .getId();\n\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList(\"step1\"),\n                        Arrays.asList(true));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                group);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n\n        final List<User> possibleUsers = getProcessAPI().getPossibleUsersOfPendingHumanTask(step1Id, 0, 10);\n        assertEquals(1, possibleUsers.size());\n        assertEquals(jack, possibleUsers.get(0));\n\n        // cleanup:\n        getIdentityAPI().deleteUserMembership(userMembershipId);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getPossibleUsersOfTaskShouldReturnAllUsersInThePaginationRange() throws Exception {\n        final int nbUsers = 21;\n        final List<User> users = new ArrayList<>(nbUsers);\n        final List<Long> userMembershipIds = new ArrayList<>(nbUsers);\n        for (int i = 0; i < nbUsers; i++) {\n            final User newUser = createUser(\"user_\" + i, \"pwd\");\n            users.add(newUser);\n            userMembershipIds.add(createUserMembership(newUser.getUserName(), role.getName(), group.getName()).getId());\n        }\n\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList(\"step1\"),\n                        Arrays.asList(true));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                users);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n\n        final List<User> possibleUsers = getProcessAPI().getPossibleUsersOfPendingHumanTask(step1Id, 0, 30);\n        // make sure the list is not limited to 20:\n        assertEquals(21, possibleUsers.size());\n\n        // cleanup:\n        disableAndDeleteProcess(processDefinition);\n        for (final Long userMembershipId : userMembershipIds) {\n            getIdentityAPI().deleteUserMembership(userMembershipId);\n        }\n        deleteUsers(users);\n    }\n\n    @Test\n    public void getPossibleUsersOfTaskSubGroupActor() throws Exception {\n        final Group group2 = createGroup(\"gr\", group.getPath());\n        final long userMembershipId1 = getIdentityAPI().addUserMembership(jack.getId(), group.getId(), role.getId())\n                .getId();\n        final long userMembershipId2 = getIdentityAPI().addUserMembership(john.getId(), group2.getId(), role.getId())\n                .getId();\n\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList(\"step1\"),\n                        Arrays.asList(true));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                group);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n\n        List<User> possibleUsers = getProcessAPI().getPossibleUsersOfPendingHumanTask(step1Id, 0, 10);\n        assertEquals(2, possibleUsers.size());\n        assertEquals(jack, possibleUsers.get(0));\n        assertEquals(john, possibleUsers.get(1));\n\n        possibleUsers = getProcessAPI().getPossibleUsersOfPendingHumanTask(step1Id, 1, 10);\n        assertEquals(1, possibleUsers.size());\n        assertEquals(john, possibleUsers.get(0));\n\n        // cleanup:\n        getIdentityAPI().deleteUserMembership(userMembershipId1);\n        getIdentityAPI().deleteUserMembership(userMembershipId2);\n        deleteGroups(group2);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getPossibleUsersOfFilteredTask() throws Exception {\n        final Group group2 = createGroup(\"gr\", group.getPath());\n        final long userMembershipId1 = getIdentityAPI().addUserMembership(jack.getId(), group2.getId(), role.getId())\n                .getId();\n        final long userMembershipId2 = getIdentityAPI().addUserMembership(john.getId(), group2.getId(), role.getId())\n                .getId();\n\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"assign\", \"5.2\");\n        designProcessDefinition.addActor(ACTOR_NAME);\n        final UserTaskDefinitionBuilder taskDefinitionBuilder = designProcessDefinition.addUserTask(\"step1\",\n                ACTOR_NAME);\n        taskDefinitionBuilder.addUserFilter(\"test\", \"org.bonitasoft.engine.filter.user.testFilter\", \"1.0\").addInput(\n                \"userId\",\n                new ExpressionBuilder().createConstantLongExpression(john.getId()));\n        final UserTaskDefinitionBuilder definitionBuilder = designProcessDefinition.addUserTask(\"step2\", ACTOR_NAME);\n        definitionBuilder.addUserFilter(\"test\", \"org.bonitasoft.engine.filter.user.testFilter\", \"1.0\").addInput(\n                \"userId\",\n                new ExpressionBuilder().createConstantLongExpression(jack.getId()));\n        final ProcessDefinition processDefinition = deployProcessWithTestFilter(designProcessDefinition, ACTOR_NAME,\n                john, \"TestFilter\");\n        getProcessAPI().addUserToActor(ACTOR_NAME, processDefinition, jack.getId());\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n        waitForUserTask(processInstance, \"step2\");\n        final List<User> possibleUsers = getProcessAPI().getPossibleUsersOfPendingHumanTask(step1Id, 0, 2);\n        assertEquals(1, possibleUsers.size());\n        assertEquals(john, possibleUsers.get(0));\n\n        // cleanup:\n        getIdentityAPI().deleteUserMembership(userMembershipId1);\n        getIdentityAPI().deleteUserMembership(userMembershipId2);\n        deleteGroups(group2);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getPossibleUsersOfUnknownTask() {\n        final List<User> possibleUsers = getProcessAPI().getPossibleUsersOfPendingHumanTask(-156L, 0, 10);\n        assertEquals(0, possibleUsers.size());\n    }\n\n    @Test\n    public void getPossibleUsersOfTaskDefinitionUserActor() throws Exception {\n        final long userMembershipId = getIdentityAPI().addUserMembership(jack.getId(), group.getId(), role.getId())\n                .getId();\n\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"assign\", \"5.0\");\n        designProcessDefinition.addActor(ACTOR_NAME);\n        designProcessDefinition.addActor(\"emca\");\n        designProcessDefinition.addUserTask(\"step1\", ACTOR_NAME);\n        designProcessDefinition.addUserTask(\"step2\", \"emca\");\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(),\n                Arrays.asList(ACTOR_NAME, \"emca\"),\n                Arrays.asList(john, jack));\n\n        final List<User> possibleUsers = getProcessAPI().getPossibleUsersOfHumanTask(processDefinition.getId(), \"step1\",\n                0, 10);\n        assertEquals(1, possibleUsers.size());\n        assertEquals(john, possibleUsers.get(0));\n\n        // cleanup:\n        getIdentityAPI().deleteUserMembership(userMembershipId);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getPossibleUsersOfTaskDefinitionRoleActor() throws Exception {\n        final long userMembershipId = getIdentityAPI().addUserMembership(jack.getId(), group.getId(), role.getId())\n                .getId();\n\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList(\"step1\"),\n                        Arrays.asList(true));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                role);\n\n        final List<User> possibleUsers = getProcessAPI().getPossibleUsersOfHumanTask(processDefinition.getId(), \"step1\",\n                0, 10);\n        assertEquals(1, possibleUsers.size());\n        assertEquals(jack, possibleUsers.get(0));\n\n        // cleanup:\n        getIdentityAPI().deleteUserMembership(userMembershipId);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getPossibleUsersOfTaskDefinitionGroupActor() throws Exception {\n        //given\n        final long userMembershipId1 = getIdentityAPI().addUserMembership(jack.getId(), group.getId(), role.getId())\n                .getId();\n        final long userMembershipId2 = getIdentityAPI().addUserMembership(john.getId(), group.getId(), role.getId())\n                .getId();\n\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList(\"step1\"),\n                        Arrays.asList(true));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                group);\n\n        //when\n        final List<User> possibleUsers = getProcessAPI().getPossibleUsersOfHumanTask(processDefinition.getId(), \"step1\",\n                0, 10);\n        final List<Long> userIdsForActor = getProcessAPI().getUserIdsForActor(processDefinition.getId(), ACTOR_NAME, 0,\n                10);\n\n        //then\n        assertThat(possibleUsers).containsOnly(jack, john);\n        assertThat(userIdsForActor).containsOnly(jack.getId(), john.getId());\n\n        // cleanup:\n        getIdentityAPI().deleteUserMembership(userMembershipId1);\n        getIdentityAPI().deleteUserMembership(userMembershipId2);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getPossibleUsersOfTaskDefinitionSubGroupActor() throws Exception {\n        final Group group2 = createGroup(\"gr\", group.getPath());\n        final long userMembershipId1 = getIdentityAPI().addUserMembership(jack.getId(), group.getId(), role.getId())\n                .getId();\n        final long userMembershipId2 = getIdentityAPI().addUserMembership(john.getId(), group2.getId(), role.getId())\n                .getId();\n\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList(\"step1\"),\n                        Arrays.asList(true));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                group);\n\n        List<User> possibleUsers = getProcessAPI().getPossibleUsersOfHumanTask(processDefinition.getId(), \"step1\", 0,\n                10);\n        assertEquals(2, possibleUsers.size());\n        assertEquals(jack, possibleUsers.get(0));\n        assertEquals(john, possibleUsers.get(1));\n\n        possibleUsers = getProcessAPI().getPossibleUsersOfHumanTask(processDefinition.getId(), \"step1\", 1, 10);\n        assertEquals(1, possibleUsers.size());\n        assertEquals(john, possibleUsers.get(0));\n\n        // cleanup:\n        getIdentityAPI().deleteUserMembership(userMembershipId1);\n        getIdentityAPI().deleteUserMembership(userMembershipId2);\n        deleteGroups(group2);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getPossibleUsersOfUnknownProcessDefinition() {\n        final List<User> possibleUsers = getProcessAPI().getPossibleUsersOfHumanTask(-156L, \"step1\", 0, 10);\n        assertEquals(0, possibleUsers.size());\n    }\n\n    @Test\n    public void getPossibleUsersOfUnknownTaskDefinition() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList(\"step1\"),\n                        Arrays.asList(true));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                jack);\n\n        final List<User> possibleUsers = getProcessAPI().getPossibleUsersOfHumanTask(processDefinition.getId(),\n                \"step83\", 0, 10);\n        assertEquals(0, possibleUsers.size());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getPossibleUsersOfSystemTaskDefinition() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList(\"auto\"),\n                        Arrays.asList(false));\n        final ProcessDefinition processDefinition = deployAndEnableProcess(designProcessDefinition);\n\n        final List<User> possibleUsers = getProcessAPI().getPossibleUsersOfHumanTask(processDefinition.getId(), \"auto\",\n                0, 10);\n        assertEquals(0, possibleUsers.size());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getPossibleUsersOfTaskDefinitionWithAFilter() throws Exception {\n        final Group group2 = createGroup(\"gr\", group.getPath());\n        final long userMembershipId1 = getIdentityAPI().addUserMembership(john.getId(), group2.getId(), role.getId())\n                .getId();\n        final long userMembershipId2 = getIdentityAPI().addUserMembership(jack.getId(), group2.getId(), role.getId())\n                .getId();\n\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"assign\", \"5.2\");\n        designProcessDefinition.addActor(ACTOR_NAME);\n        final UserTaskDefinitionBuilder taskDefinitionBuilder = designProcessDefinition.addUserTask(\"step1\",\n                ACTOR_NAME);\n        taskDefinitionBuilder.addUserFilter(\"test\", \"org.bonitasoft.engine.filter.user.testFilter\", \"1.0\").addInput(\n                \"userId\",\n                new ExpressionBuilder().createConstantLongExpression(john.getId()));\n        final UserTaskDefinitionBuilder definitionBuilder = designProcessDefinition.addUserTask(\"step2\", ACTOR_NAME);\n        definitionBuilder.addUserFilter(\"test\", \"org.bonitasoft.engine.filter.user.testFilter\", \"1.0\").addInput(\n                \"userId\",\n                new ExpressionBuilder().createConstantLongExpression(jack.getId()));\n        final ProcessDefinition processDefinition = deployProcessWithTestFilter(designProcessDefinition, ACTOR_NAME,\n                john, \"TestFilter\");\n        getProcessAPI().addUserToActor(ACTOR_NAME, processDefinition, jack.getId());\n\n        List<User> possibleUsers = getProcessAPI().getPossibleUsersOfHumanTask(processDefinition.getId(), \"step1\", 0,\n                2);\n        assertEquals(2, possibleUsers.size());\n        assertEquals(jack, possibleUsers.get(0));\n        assertEquals(john, possibleUsers.get(1));\n\n        possibleUsers = getProcessAPI().getPossibleUsersOfHumanTask(processDefinition.getId(), \"step1\", 2, 4);\n        assertEquals(0, possibleUsers.size());\n\n        // cleanup:\n        getIdentityAPI().deleteUserMembership(userMembershipId1);\n        getIdentityAPI().deleteUserMembership(userMembershipId2);\n        deleteGroups(group2);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/activity/HumanTasksIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.activity;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.bpm.flownode.EventCriterion.NAME_DESC;\nimport static org.bonitasoft.engine.bpm.flownode.TimerType.DURATION;\nimport static org.junit.Assert.*;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.awaitility.Awaitility;\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.ActivityStates;\nimport org.bonitasoft.engine.bpm.flownode.EventInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeExecutionException;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.TaskPriority;\nimport org.bonitasoft.engine.bpm.process.ActivationState;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.supervisor.ProcessSupervisorSearchDescriptor;\nimport org.bonitasoft.engine.exception.NotFoundException;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.expression.ExpressionConstants;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.operation.OperationBuilder;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.test.BuildTestUtil;\nimport org.junit.Test;\n\npublic class HumanTasksIT extends TestWithUser {\n\n    final static int INITIALIZING_STATE_ID = 32;\n\n    @Test\n    public void cannotGetHumanTaskInstances() throws Exception {\n        // First process def with 2 instances:\n        final DesignProcessDefinition designProcessDef1 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList(\"initTask1\"),\n                        Arrays.asList(true));\n        final ProcessDefinition processDef1 = deployAndEnableProcessWithActor(designProcessDef1, ACTOR_NAME, user);\n        getProcessAPI().startProcess(processDef1.getId());\n        final ProcessInstance pi2 = getProcessAPI().startProcess(processDef1.getId());\n\n        final List<HumanTaskInstance> humanTaskInstances = getProcessAPI().getHumanTaskInstances(pi2.getId(),\n                \"initTsk2\", 0, 10);\n        assertTrue(humanTaskInstances.isEmpty());\n        disableAndDeleteProcess(processDef1);\n    }\n\n    @Test\n    public void can_creatte_FlowNodeInstance_with_several_non_ascii_characters() throws Exception {\n        final String taskDisplayName = \"àéò€çhahaאת ארץ בדפים מוסיקה לעברית. בקר גם טיפול פיסיקה, דת מתן בישול רומנית תחבורה. אל בידור מדויקים ואלקטרוניקה זאת נפלו.أملاً النزاع الصعداء بل الى. ان اتفاقية بالمطالبة ويكيبيديا، جُل. في كان بالجانب والديون الإتفاقية. لها المسرح وبولندا وبلجيكا، أي.\";\n        final String taskName = \"paraiškos teikėjas Žcheck this out! 新フリぶ番高ホアリメ医意治せ羽装セヱ青済ルよじえ痛楽ぜは性位加嶋ケナ日者コ球量ミルシ異本たてふ火8在な日治じわあひ掲図トおぜ発審員ルをさレ。小スユヱイ写多キクオラ里数ンたレだ異分準ヲ念67券ド角続構ソ打政リレウテ社式築ワカエ川顔せー料開みドょわ北真メヲ齢記ヒチ山抱診露えっン。載ル定出へえぴ勝上ふのこ八抹なおぞ現負よッや新4省1開盛ょべせ東87令計のご導応ヘユレ対純霊真接スさほ。\";\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        processBuilder.addUserTask(taskName, ACTOR_NAME)\n                .addDisplayName(new ExpressionBuilder().createConstantStringExpression(taskDisplayName))\n                .addDescription(\"description\");\n\n        final ProcessDefinition processDef1 = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user);\n        getProcessAPI().startProcess(processDef1.getId());\n        final HumanTaskInstance task1 = waitForUserTaskAndExecuteAndGetIt(taskName, user);\n        assertEquals(taskDisplayName, task1.getDisplayName());\n\n        disableAndDeleteProcess(processDef1);\n    }\n\n    @Test\n    public void getLastHumanTaskInstance() throws Exception {\n        // First process def with 2 instances:\n        final DesignProcessDefinition designProcessDef1 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList(\"initTask1\"),\n                        Arrays.asList(true));\n        final ProcessDefinition processDef1 = deployAndEnableProcessWithActor(designProcessDef1, ACTOR_NAME, user);\n        final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDef1.getId());\n        waitForUserTask(processInstance1, \"initTask1\");\n        final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDef1.getId());\n        waitForUserTask(processInstance2, \"initTask1\");\n\n        final ProcessDefinitionBuilder definitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME + 2, PROCESS_VERSION);\n        definitionBuilder.addStartEvent(\"start\");\n        definitionBuilder.addActor(ACTOR_NAME);\n        definitionBuilder.addUserTask(\"initTask2\", ACTOR_NAME);\n        definitionBuilder.addEndEvent(\"end\");\n        definitionBuilder.addTransition(\"start\", \"initTask2\");\n        definitionBuilder.addTransition(\"initTask2\", \"end\");\n        final DesignProcessDefinition designProcessDef2 = definitionBuilder.done();\n\n        final ProcessDefinition processDef2 = deployAndEnableProcessWithActor(designProcessDef2, ACTOR_NAME, user);\n        final ProcessInstance processInstance3 = getProcessAPI().startProcess(processDef2.getId());\n        waitForUserTask(processInstance3, \"initTask2\");\n        final ProcessInstance processInstance4 = getProcessAPI().startProcess(processDef2.getId());\n        waitForUserTask(processInstance4, \"initTask2\");\n        final ProcessInstance processInstance5 = getProcessAPI().startProcess(processDef2.getId());\n        waitForUserTask(processInstance5, \"initTask2\");\n\n        final HumanTaskInstance taskInstance = getProcessAPI().getLastStateHumanTaskInstance(processInstance3.getId(),\n                \"initTask2\");\n        assertNotNull(taskInstance);\n        assertEquals(\"initTask2\", taskInstance.getName());\n\n        disableAndDeleteProcess(processDef1);\n        disableAndDeleteProcess(processDef2);\n    }\n\n    @Test\n    public void taskExecutionFailureLogsPrettyMessage() throws Exception {\n        final ProcessDefinitionBuilder definitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"taskExecutionFailureLogsPrettyMessage\", \"1.01\");\n        definitionBuilder.addStartEvent(\"start\");\n        definitionBuilder.addActor(ACTOR_NAME);\n        final UserTaskDefinitionBuilder userTaskDef = definitionBuilder.addUserTask(\"initTask\", ACTOR_NAME);\n        userTaskDef.addData(\"aData\", String.class.getName(),\n                new ExpressionBuilder().createConstantStringExpression(\"initValue\"));\n        userTaskDef.addOperation(new OperationBuilder().createSetDataOperation(\"aData\",\n                new ExpressionBuilder().createConstantIntegerExpression(18)));\n        final DesignProcessDefinition designProcessDef = definitionBuilder.done();\n\n        final ProcessDefinition processDef = deployAndEnableProcessWithActor(designProcessDef, ACTOR_NAME, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDef.getId());\n        logout();\n        loginOnDefaultTenantWith(USERNAME, PASSWORD);\n        final ActivityInstance task = waitForUserTaskAndAssignIt(processInstance, \"initTask\", user);\n        try {\n            getProcessAPI().executeFlowNode(task.getId());\n        } catch (final FlowNodeExecutionException e) {\n            assertTrue(\"wrong exception message\", e.getMessage().contains(\"Incompatible assignment operation type\"));\n        }\n        disableAndDeleteProcess(processDef);\n    }\n\n    @Test(expected = NotFoundException.class)\n    public void cannotGetLastHumanTaskInstance() throws Exception {\n        // First process def with 2 instances:\n        final DesignProcessDefinition designProcessDef1 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList(\"initTask1\"),\n                        Arrays.asList(true));\n        final ProcessDefinition processDef1 = deployAndEnableProcessWithActor(designProcessDef1, ACTOR_NAME, user);\n        final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDef1.getId());\n        final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDef1.getId());\n        waitForUserTask(processInstance1, \"initTask1\");\n        waitForUserTask(processInstance2, \"initTask1\");\n\n        try {\n            getProcessAPI().getLastStateHumanTaskInstance(processInstance2.getId(), \"initTsk2\");\n        } finally {\n            // Clean up\n            disableAndDeleteProcess(processDef1);\n        }\n    }\n\n    @Test\n    public void getHumanTaskInstances() throws Exception {\n        // First process def with 2 instances:\n        final DesignProcessDefinition designProcessDef1 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList(\"initTask1\"),\n                        Arrays.asList(true));\n        final ProcessDefinition processDef1 = deployAndEnableProcessWithActor(designProcessDef1, ACTOR_NAME, user);\n        final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDef1.getId());\n        waitForUserTask(processInstance1, \"initTask1\");\n        final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDef1.getId());\n        waitForUserTask(processInstance2, \"initTask1\");\n\n        final ProcessDefinitionBuilder definitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME + 2, PROCESS_VERSION);\n        definitionBuilder.addStartEvent(\"start\");\n        definitionBuilder.addActor(ACTOR_NAME);\n        definitionBuilder.addUserTask(\"initTask2\", ACTOR_NAME);\n        definitionBuilder.addEndEvent(\"end\");\n        definitionBuilder.addTransition(\"start\", \"initTask2\");\n        definitionBuilder.addTransition(\"initTask2\", \"end\");\n        final DesignProcessDefinition designProcessDef2 = definitionBuilder.done();\n\n        final ProcessDefinition processDef2 = deployAndEnableProcessWithActor(designProcessDef2, ACTOR_NAME, user);\n        final ProcessInstance processInstance3 = getProcessAPI().startProcess(processDef2.getId());\n        waitForUserTask(processInstance3, \"initTask2\");\n        final ProcessInstance processInstance4 = getProcessAPI().startProcess(processDef2.getId());\n        waitForUserTask(processInstance4, \"initTask2\");\n        final ProcessInstance processInstance5 = getProcessAPI().startProcess(processDef2.getId());\n        waitForUserTask(processInstance5, \"initTask2\");\n\n        // No need to verify anything, if no exception, query exists\n        getProcessAPI().searchActivities(\n                new SearchOptionsBuilder(0, 10)\n                        .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, processInstance3.getId())\n                        .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.HUMAN_TASK).done());\n\n        getProcessAPI().searchActivities(\n                new SearchOptionsBuilder(0, 10)\n                        .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, processInstance3.getId())\n                        .filter(ProcessSupervisorSearchDescriptor.USER_ID, user.getId()).done());\n\n        getProcessAPI().searchActivities(\n                new SearchOptionsBuilder(0, 10)\n                        .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.HUMAN_TASK)\n                        .filter(ProcessSupervisorSearchDescriptor.USER_ID, user.getId()).done());\n\n        final List<HumanTaskInstance> taskInstances = getProcessAPI().getHumanTaskInstances(processInstance3.getId(),\n                \"initTask2\", 0, 10);\n        assertEquals(1, taskInstances.size());\n\n        disableAndDeleteProcess(processDef1);\n        disableAndDeleteProcess(processDef2);\n    }\n\n    @Test\n    public void getOneAssignedHumanTaskInstance() throws Exception {\n        final DesignProcessDefinition processDef = createProcessWithActorAndHumanTaskAndStringData();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, ACTOR_NAME, user);\n        final ActivityInstance activityInstance = createProcessAndAssignUserTask(user, processDefinition);\n\n        final HumanTaskInstance humanTask = getProcessAPI().getAssignedHumanTaskInstances(user.getId(), 0, 1, null)\n                .get(0);\n        assertEquals(activityInstance.getId(), humanTask.getId());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getAssignedHumanTaskInstances() throws Exception {\n        final ProcessDefinitionBuilder definitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION);\n        definitionBuilder.addStartEvent(\"start\");\n        definitionBuilder.addActor(ACTOR_NAME);\n        definitionBuilder.addUserTask(\"initTask1\", ACTOR_NAME).addPriority(TaskPriority.NORMAL.name());\n        definitionBuilder.addUserTask(\"initTask2\", ACTOR_NAME).addPriority(TaskPriority.LOWEST.name());\n        definitionBuilder.addUserTask(\"initTask3\", ACTOR_NAME).addPriority(TaskPriority.HIGHEST.name());\n        definitionBuilder.addUserTask(\"initTask4\", ACTOR_NAME).addPriority(TaskPriority.NORMAL.name());\n        definitionBuilder.addUserTask(\"initTask5\", ACTOR_NAME);\n        definitionBuilder.addEndEvent(\"end\");\n        definitionBuilder.addTransition(\"start\", \"initTask1\");\n        definitionBuilder.addTransition(\"start\", \"initTask2\");\n        definitionBuilder.addTransition(\"start\", \"initTask3\");\n        definitionBuilder.addTransition(\"start\", \"initTask4\");\n        definitionBuilder.addTransition(\"initTask4\", \"initTask5\");\n        final DesignProcessDefinition designProcessDef = definitionBuilder.done();\n\n        final ProcessDefinition processDef = deployAndEnableProcessWithActor(designProcessDef, ACTOR_NAME, user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDef.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDef.getId());\n        waitForUserTaskAndAssignIt(processInstance, \"initTask1\", user);\n        waitForUserTaskAndAssignIt(processInstance, \"initTask2\", user);\n        waitForUserTaskAndAssignIt(processInstance, \"initTask3\", user);\n        waitForUserTaskAndAssignIt(processInstance, \"initTask4\", user);\n\n        // The task with the lowest priority is \"initTask2\"\n        HumanTaskInstance humanTask = getProcessAPI()\n                .getAssignedHumanTaskInstances(user.getId(), 0, 1, ActivityInstanceCriterion.PRIORITY_ASC).get(0);\n        assertEquals(\"initTask2\", humanTask.getName());\n\n        // The task with the highest priority is \"initTask3\"\n        humanTask = getProcessAPI()\n                .getAssignedHumanTaskInstances(user.getId(), 0, 1, ActivityInstanceCriterion.PRIORITY_DESC).get(0);\n        assertEquals(\"initTask3\", humanTask.getName());\n\n        // The task with the highest priority is \"initTask3\"\n        humanTask = getProcessAPI().getAssignedHumanTaskInstances(user.getId(), 0, 1, ActivityInstanceCriterion.DEFAULT)\n                .get(0);\n        assertEquals(\"initTask3\", humanTask.getName());\n\n        // The task expected is \"initTask1\"\n        humanTask = getProcessAPI()\n                .getAssignedHumanTaskInstances(user.getId(), 0, 1, ActivityInstanceCriterion.NAME_ASC).get(0);\n        assertEquals(\"initTask1\", humanTask.getName());\n\n        // The task expected is \"initTask4\"\n        humanTask = getProcessAPI()\n                .getAssignedHumanTaskInstances(user.getId(), 0, 1, ActivityInstanceCriterion.NAME_DESC).get(0);\n        assertEquals(\"initTask4\", humanTask.getName());\n\n        disableAndDeleteProcess(processDef);\n    }\n\n    @Test\n    public void getAssignedHumanTaskInstancesOrderByDates() throws Exception {\n        final ProcessDefinitionBuilder definitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION);\n        definitionBuilder.addStartEvent(\"start\");\n        definitionBuilder.addActor(ACTOR_NAME);\n        definitionBuilder.addUserTask(\"initTask1\", ACTOR_NAME);\n        definitionBuilder.addUserTask(\"initTask4\", ACTOR_NAME);\n        definitionBuilder.addUserTask(\"initTask5\", ACTOR_NAME);\n        definitionBuilder.addEndEvent(\"end\");\n        definitionBuilder.addTransition(\"start\", \"initTask1\");\n        definitionBuilder.addTransition(\"start\", \"initTask4\");\n        definitionBuilder.addTransition(\"initTask4\", \"initTask5\");\n        final DesignProcessDefinition designProcessDef = definitionBuilder.done();\n\n        final ProcessDefinition processDef = deployAndEnableProcessWithActor(designProcessDef, ACTOR_NAME, user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDef.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDef.getId());\n        waitForUserTaskAndAssignIt(processInstance, \"initTask1\", user);\n        waitForUserTaskAndAssignIt(processInstance, \"initTask4\", user);\n\n        HumanTaskInstance humanTask = getProcessAPI()\n                .getAssignedHumanTaskInstances(user.getId(), 0, 1, ActivityInstanceCriterion.NAME_DESC).get(0);\n        assertEquals(\"initTask4\", humanTask.getName());\n\n        assignAndExecuteStep(humanTask, user.getId());\n        waitForUserTaskAndAssignIt(processInstance, \"initTask5\", user);\n\n        humanTask = getProcessAPI()\n                .getAssignedHumanTaskInstances(user.getId(), 0, 1, ActivityInstanceCriterion.REACHED_STATE_DATE_ASC)\n                .get(0);\n        assertEquals(\"initTask1\", humanTask.getName());\n\n        humanTask = getProcessAPI()\n                .getAssignedHumanTaskInstances(user.getId(), 0, 1, ActivityInstanceCriterion.REACHED_STATE_DATE_DESC)\n                .get(0);\n        assertEquals(\"initTask5\", humanTask.getName());\n\n        humanTask = getProcessAPI()\n                .getAssignedHumanTaskInstances(user.getId(), 0, 1, ActivityInstanceCriterion.LAST_UPDATE_ASC).get(0);\n        assertEquals(\"initTask1\", humanTask.getName());\n\n        humanTask = getProcessAPI()\n                .getAssignedHumanTaskInstances(user.getId(), 0, 1, ActivityInstanceCriterion.LAST_UPDATE_DESC).get(0);\n        assertEquals(\"initTask5\", humanTask.getName());\n\n        disableAndDeleteProcess(processDef);\n    }\n\n    @Test\n    public void getHumanTaskInstance() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId());\n        final List<ActivityInstance> activityInstances = getProcessAPI().getActivities(pi0.getId(), 0, 10);\n        final long activityInstanceId = activityInstances.get(0).getId();\n        final HumanTaskInstance userTaskInstance = getProcessAPI().getHumanTaskInstance(activityInstanceId);\n        assertEquals(\"step1\", userTaskInstance.getName());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    private DesignProcessDefinition createProcessWithActorAndHumanTaskAndStringData() throws Exception {\n        return new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addDescription(\"Delivery all day and night long\").addUserTask(\"step1\", ACTOR_NAME)\n                .addShortTextData(\"dataName\", new ExpressionBuilder().createConstantStringExpression(\"beforeUpdate\"))\n                .getProcess();\n    }\n\n    private ActivityInstance createProcessAndAssignUserTask(final User user, final ProcessDefinition processDefinition)\n            throws Exception {\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n        waitForUserTask(processInstance, \"step1\");\n\n        final List<ActivityInstance> activityInstances = new ArrayList<>(\n                getProcessAPI().getActivities(processInstance.getId(), 0, 20));\n        final ActivityInstance activityInstance = activityInstances.get(activityInstances.size() - 1);\n\n        assertEquals(\"ready\", activityInstance.getState());\n\n        getProcessAPI().assignUserTask(activityInstance.getId(), user.getId());\n        return activityInstance;\n    }\n\n    @Test\n    public void setTaskPriority() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask(\"step1\", ACTOR_NAME)\n                .getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        HumanTaskInstance step1 = waitForUserTaskAndGetIt(processInstance, \"step1\");\n        assertEquals(TaskPriority.NORMAL, step1.getPriority());\n\n        final long step1Id = step1.getId();\n        getProcessAPI().setTaskPriority(step1Id, TaskPriority.HIGHEST);\n        step1 = getProcessAPI().getHumanTaskInstance(step1Id);\n        assertEquals(TaskPriority.HIGHEST, step1.getPriority());\n\n        getProcessAPI().setTaskPriority(step1Id, TaskPriority.LOWEST);\n        step1 = getProcessAPI().getHumanTaskInstance(step1Id);\n        assertEquals(TaskPriority.LOWEST, step1.getPriority());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void setState() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask(\"step1\", ACTOR_NAME)\n                .getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        HumanTaskInstance step1 = waitForUserTaskAndGetIt(processInstance, \"step1\");\n\n        final long activityInstanceId = step1.getId();\n        getProcessAPI().setActivityStateById(activityInstanceId, INITIALIZING_STATE_ID);\n        step1 = getProcessAPI().getHumanTaskInstance(activityInstanceId);\n        assertEquals(ActivityStates.INITIALIZING_STATE, step1.getState());\n\n        // test set state by stateName\n        getProcessAPI().setActivityStateByName(activityInstanceId, ActivityStates.EXECUTING_STATE);\n        step1 = getProcessAPI().getHumanTaskInstance(activityInstanceId);\n        assertEquals(ActivityStates.EXECUTING_STATE, step1.getState());\n\n        getProcessAPI().setActivityStateByName(activityInstanceId, ActivityStates.SKIPPED_STATE);\n        // will skip task and finish process\n        waitForProcessToFinish(processInstance.getId());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void setStateShouldTerminateATaskWithBoundaryEvent() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        processBuilder.addUserTask(\"step1\", ACTOR_NAME).addBoundaryEvent(\"theBoundaryEvent\")\n                .addTimerEventTriggerDefinition(DURATION,\n                        new ExpressionBuilder().createConstantLongExpression(9000L));\n        processBuilder.addEndEvent(\"boundaryEnd\");\n        processBuilder.addEndEvent(\"TheEnd\");\n        processBuilder.addTransition(\"step1\", \"TheEnd\");\n        processBuilder.addTransition(\"theBoundaryEvent\", \"boundaryEnd\");\n        final DesignProcessDefinition designProcessDefinition = processBuilder.getProcess();\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        HumanTaskInstance step1 = waitForUserTaskAndGetIt(processInstance, \"step1\");\n        List<EventInstance> eventInstances = getProcessAPI().getEventInstances(processInstance.getId(), 0, 1000,\n                NAME_DESC);\n        assertThat(eventInstances.size()).isEqualTo(1);\n\n        final long activityInstanceId = step1.getId();\n        getProcessAPI().setActivityStateById(activityInstanceId, INITIALIZING_STATE_ID);\n        step1 = getProcessAPI().getHumanTaskInstance(activityInstanceId);\n        assertEquals(ActivityStates.INITIALIZING_STATE, step1.getState());\n\n        getProcessAPI().setActivityStateByName(activityInstanceId, ActivityStates.SKIPPED_STATE);\n        // skip task and finish process\n        waitForProcessToFinish(processInstance.getId());\n\n        //verify that the BD has been cleaned\n        eventInstances = getProcessAPI().getEventInstances(processInstance.getId(), 0, 1000, NAME_DESC);\n        assertThat(eventInstances).isEmpty();\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void setStateByName_should_remove_time_of_linked_boundary_events() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        processBuilder.addUserTask(\"step1\", ACTOR_NAME).addBoundaryEvent(\"theBoundaryEvent\")\n                .addTimerEventTriggerDefinition(DURATION,\n                        new ExpressionBuilder().createConstantLongExpression(9000L));\n        processBuilder.addUserTask(\"step2\", ACTOR_NAME);\n        processBuilder.addTransition(\"step1\", \"step2\");\n        processBuilder.addTransition(\"theBoundaryEvent\", \"step2\");\n        final DesignProcessDefinition designProcessDefinition = processBuilder.getProcess();\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        getProcessAPI().setActivityStateByName(waitForUserTaskAndGetIt(processInstance, \"step1\").getId(),\n                ActivityStates.SKIPPED_STATE);\n        waitForUserTask(processInstance, \"step2\");\n\n        Awaitility.await().until(() -> getProcessAPI()\n                .searchTimerEventTriggerInstances(processInstance.getId(), new SearchOptionsBuilder(0, 100).done())\n                .getResult().isEmpty());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void should_use_expression_to_compute_human_task_due_date() throws Exception {\n        //given\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME);\n\n        ExpressionBuilder oneHourExpressionBuilder = new ExpressionBuilder();\n        oneHourExpressionBuilder.createGroovyScriptExpression(\"dueDate expression\", \"3600000L\", Long.class.getName());\n\n        ExpressionBuilder nullExpressionBuilder = new ExpressionBuilder();\n        nullExpressionBuilder.createGroovyScriptExpression(\"dueDate expression\", \"null\", Long.class.getName());\n\n        ExpressionBuilder failExpressionBuilder = new ExpressionBuilder();\n        failExpressionBuilder.createGroovyScriptExpression(\"dueDate expression\", \"not a Long\", Long.class.getName());\n\n        builder.addUserTask(\"userTask\", ACTOR_NAME).addExpectedDuration(oneHourExpressionBuilder.done());\n        builder.addManualTask(\"manualTask\", ACTOR_NAME).addExpectedDuration(oneHourExpressionBuilder.done());\n        builder.addUserTask(\"userTaskNullExpression\", ACTOR_NAME).addExpectedDuration(nullExpressionBuilder.done());\n        builder.addManualTask(\"manualTaskNullExpression\", ACTOR_NAME).addExpectedDuration(nullExpressionBuilder.done());\n        builder.addUserTask(\"failUserTask\", ACTOR_NAME).addExpectedDuration(failExpressionBuilder.done());\n\n        builder.addTransition(\"userTask\", \"userTaskNullExpression\");\n        builder.addTransition(\"userTaskNullExpression\", \"manualTask\");\n        builder.addTransition(\"manualTask\", \"manualTaskNullExpression\");\n        builder.addTransition(\"manualTaskNullExpression\", \"failUserTask\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.getProcess(), ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        //when\n        final HumanTaskInstance userTask = waitForUserTaskAssignAndExecuteIt(processInstance, \"userTask\", user, null);\n\n        final HumanTaskInstance userTaskNullDueDate = waitForUserTaskAssignAndExecuteIt(processInstance,\n                \"userTaskNullExpression\", user, null);\n\n        final HumanTaskInstance manualTask = waitForUserTaskAndAssignIt(processInstance, \"manualTask\", user);\n        getProcessAPI().executeUserTask(manualTask.getId(), null);\n\n        final HumanTaskInstance manualTaskNullDueDate = waitForUserTaskAndAssignIt(processInstance,\n                \"manualTaskNullExpression\", user);\n        getProcessAPI().executeUserTask(manualTaskNullDueDate.getId(), null);\n\n        final ActivityInstance taskToFail = waitForTaskToFail(processInstance);\n\n        //then\n        assertThat(userTask.getExpectedEndDate()).as(\"should expression set expected end date\").isNotNull();\n        assertThat(userTaskNullDueDate.getExpectedEndDate()).as(\"should have no due date\").isNull();\n        assertThat(manualTask.getExpectedEndDate()).as(\"should expression set expected end date\").isNotNull();\n        assertThat(manualTaskNullDueDate.getExpectedEndDate()).as(\"should have no due date\").isNull();\n        assertThat(taskToFail.getState()).isEqualTo(ActivityStates.FAILED_STATE);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void should_use_activity_expression_context_when_evaluating_default_value_expression() throws Exception {\n        //given\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME);\n\n        ExpressionBuilder activityInstanceIdExpressionBuiler = new ExpressionBuilder();\n        activityInstanceIdExpressionBuiler.createGroovyScriptExpression(\"activityInstanceId\", \"activityInstanceId\",\n                Long.class.getName(),\n                new ExpressionBuilder().createEngineConstant(ExpressionConstants.ACTIVITY_INSTANCE_ID));\n\n        builder.addUserTask(\"userTask\", ACTOR_NAME).addLongData(\"myId\", activityInstanceIdExpressionBuiler.done());\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.getProcess(), ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        //when\n        final HumanTaskInstance userTask = waitForUserTaskAndAssignIt(processInstance, \"userTask\", user);\n\n        //then\n        assertThat(getProcessAPI().getActivityDataInstance(\"myId\", userTask.getId()).getValue())\n                .as(\"should expression set activity instance id\").isEqualTo(userTask.getId());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/activity/LoopIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.activity;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\nimport java.io.Serializable;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedLoopActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\nimport org.bonitasoft.engine.bpm.flownode.GatewayType;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.supervisor.ProcessSupervisorSearchDescriptor;\nimport org.bonitasoft.engine.connectors.VariableStorage;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.expression.ExpressionConstants;\nimport org.bonitasoft.engine.operation.LeftOperandBuilder;\nimport org.bonitasoft.engine.operation.OperationBuilder;\nimport org.bonitasoft.engine.operation.OperatorType;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.junit.After;\nimport org.junit.Test;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class LoopIT extends TestWithUser {\n\n    @After\n    public void afterTest() {\n        VariableStorage.clearAll();\n    }\n\n    @Test\n    public void executeAStandardLoopUserTaskWhichDoesNotLoop() throws Exception {\n        final Expression condition = new ExpressionBuilder().createConstantBooleanExpression(false);\n\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"executeAStandardLoopUserTaskWhichDoesNotLoop\", \"1.0\");\n        builder.addActor(ACTOR_NAME).addDescription(\"ACTOR_NAME all day and night long\");\n        builder.addUserTask(\"step1\", ACTOR_NAME).addLoop(true, condition);\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user);\n        final ProcessInstance instance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForProcessToFinish(instance);\n        final List<ArchivedActivityInstance> archivedActivityInstances = getProcessAPI().getArchivedActivityInstances(\n                instance.getId(), 0, 100,\n                ActivityInstanceCriterion.NAME_ASC);\n        assertEquals(2, archivedActivityInstances.size());\n        for (final ArchivedActivityInstance archivedActivityInstance : archivedActivityInstances) {\n            assertTrue(ArchivedLoopActivityInstance.class.isInstance(archivedActivityInstance));\n        }\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeAStandardLoopUserTask() throws Exception {\n        final Expression condition = new ExpressionBuilder().createConstantBooleanExpression(true);\n\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"executeAStandardLoopUserTask\", \"1.0\");\n        builder.addActor(ACTOR_NAME).addDescription(\"ACTOR_NAME all day and night long\");\n        builder.addUserTask(\"step1\", ACTOR_NAME).addLoop(false, condition);\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(processInstance, \"step1\", user);\n        waitForUserTask(\"step1\");\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void evaluateExpressionsOnLoopUserTask() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"evaluateExpressionsOnLoopUserTask\", \"1.0\");\n        builder.addActor(ACTOR_NAME).addDescription(\"For Golf players only\");\n        final String activityName = \"launch\";\n        builder.addStartEvent(\"dummy\");\n        builder.addUserTask(activityName, ACTOR_NAME).addLoop(false,\n                new ExpressionBuilder().createConstantBooleanExpression(true));\n        builder.addTransition(\"dummy\", activityName);\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user);\n        try {\n            final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n            final long userTaskId = waitForUserTask(processInstance, activityName);\n            final Map<Expression, Map<String, Serializable>> expressions = new HashMap<>();\n            expressions.put(new ExpressionBuilder().createConstantBooleanExpression(true),\n                    new HashMap<String, Serializable>(0));\n            getProcessAPI().evaluateExpressionsOnActivityInstance(userTaskId, expressions);\n        } finally {\n            disableAndDeleteProcess(processDefinition);\n        }\n    }\n\n    @Test\n    public void executeAStandardLoopWithMaxIteration() throws Exception {\n        final Expression condition = new ExpressionBuilder().createConstantBooleanExpression(true);\n        final int loopMax = 3;\n\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"executeAStandardLoopUserTask\", \"1.0\");\n        builder.addActor(ACTOR_NAME).addDescription(\"ACTOR_NAME all day and night long\");\n        builder.addIntegerData(\"loopMax\", new ExpressionBuilder().createConstantIntegerExpression(loopMax));\n        builder.addUserTask(\"step1\", ACTOR_NAME).addLoop(false, condition,\n                new ExpressionBuilder().createDataExpression(\"loopMax\", Integer.class.getName()));\n        builder.addUserTask(\"step2\", ACTOR_NAME).addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        for (int i = 0; i < loopMax; i++) {\n            final long step1Id = waitForUserTaskAndcheckPendingHumanTaskInstances(\"step1\", processInstance);\n            assignAndExecuteStep(step1Id, user);\n        }\n        waitForUserTaskAndcheckPendingHumanTaskInstances(\"step2\", processInstance);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeAStandardLoopWithConditionUsingLoopCounter() throws Exception {\n        final Expression condition = new ExpressionBuilder().createGroovyScriptExpression(\n                \"executeAStandardLoopWithConditionUsingLoopCounter\",\n                \"loopCounter < 3\", Boolean.class.getName(),\n                Arrays.asList(new ExpressionBuilder().createEngineConstant(ExpressionConstants.LOOP_COUNTER)));\n\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"executeAStandardLoopUserTask\", \"1.0\");\n        builder.addActor(ACTOR_NAME).addDescription(\"ACTOR_NAME all day and night long\");\n        builder.addUserTask(\"step1\", ACTOR_NAME).addLoop(false, condition);\n        builder.addUserTask(\"step2\", ACTOR_NAME).addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        for (int i = 0; i < 3; i++) {\n            final long step1Id = waitForUserTaskAndcheckPendingHumanTaskInstances(\"step1\", processInstance);\n            assignAndExecuteStep(step1Id, user);\n        }\n        waitForUserTaskAndcheckPendingHumanTaskInstances(\"step2\", processInstance);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeAStandardLoopWithConditionUsingDataUsingLoopCounter() throws Exception {\n        final Expression condition = new ExpressionBuilder().createGroovyScriptExpression(\n                \"executeAStandardLoopWithConditionUsingLoopCounter\",\n                \"pData + loopCounter < 6\", Boolean.class.getName(),\n                Arrays.asList(new ExpressionBuilder().createDataExpression(\"pData\", Integer.class.getName()),\n                        new ExpressionBuilder().createEngineConstant(ExpressionConstants.LOOP_COUNTER)));\n\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"executeAStandardLoopUserTask\", \"1.0\");\n        builder.addActor(ACTOR_NAME).addDescription(\"ACTOR_NAME all day and night long\");\n        builder.addData(\"pData\", Integer.class.getName(), null);\n        UserTaskDefinitionBuilder step1 = builder.addUserTask(\"step1\", ACTOR_NAME);\n        step1.addLoop(false, condition);\n        step1.addData(\"theData\", Integer.class.getName(),\n                new ExpressionBuilder().createEngineConstant(ExpressionConstants.LOOP_COUNTER));\n        step1.addOperation(\n                new OperationBuilder().createSetDataOperation(\"pData\",\n                        new ExpressionBuilder().createEngineConstant(ExpressionConstants.LOOP_COUNTER)));\n        builder.addUserTask(\"step2\", ACTOR_NAME).addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        // No need to verify anything, if no exception, query exists\n        getProcessAPI().searchActivities(\n                new SearchOptionsBuilder(0, 10)\n                        .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, processInstance.getId())\n                        .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.LOOP_ACTIVITY).done());\n\n        getProcessAPI().searchActivities(\n                new SearchOptionsBuilder(0, 10)\n                        .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, processInstance.getId())\n                        .filter(ProcessSupervisorSearchDescriptor.USER_ID, user.getId()).done());\n\n        getProcessAPI().searchActivities(\n                new SearchOptionsBuilder(0, 10)\n                        .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.LOOP_ACTIVITY)\n                        .filter(ProcessSupervisorSearchDescriptor.USER_ID, user.getId()).done());\n\n        for (int i = 0; i < 3; i++) {\n            final long step1Id = waitForUserTaskAndcheckPendingHumanTaskInstances(\"step1\", processInstance);\n            assignAndExecuteStep(step1Id, user);\n        }\n        waitForUserTaskAndcheckPendingHumanTaskInstances(\"step2\", processInstance);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    private long waitForUserTaskAndcheckPendingHumanTaskInstances(final String userTaskName,\n            final ProcessInstance processInstance)\n            throws Exception {\n        final long pendingTaskId = waitForUserTask(processInstance, userTaskName);\n        final List<HumanTaskInstance> pendingTasks = getProcessAPI().getPendingHumanTaskInstances(user.getId(), 0, 10,\n                null);\n        assertEquals(1, pendingTasks.size());\n        return pendingTaskId;\n    }\n\n    @Test\n    public void executeAStandardLoopWithConditionUsingData() throws Exception {\n        final Expression condition = new ExpressionBuilder().createGroovyScriptExpression(\n                \"executeAStandardLoopWithConditionUsingData1\", \"myData < 3\",\n                Boolean.class.getName(),\n                Arrays.asList(new ExpressionBuilder().createDataExpression(\"myData\", Integer.class.getName())));\n\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"executeAStandardLoopUserTaskWithData\", \"1.0\");\n        builder.addActor(ACTOR_NAME).addDescription(\"ACTOR_NAME all day and night long\");\n        builder.addData(\"myData\", Integer.class.getName(), new ExpressionBuilder().createConstantIntegerExpression(0));\n        builder.addUserTask(\"step1\", ACTOR_NAME)\n                .addLoop(false, condition)\n                .addOperation(\n                        new LeftOperandBuilder().createNewInstance(\"myData\").done(),\n                        OperatorType.ASSIGNMENT,\n                        \"=\",\n                        null,\n                        new ExpressionBuilder().createGroovyScriptExpression(\n                                \"executeAStandardLoopWithConditionUsingData1\", \"myData + 1\",\n                                Integer.class.getName(), Arrays.asList(new ExpressionBuilder()\n                                        .createDataExpression(\"myData\", Integer.class.getName()))));\n        builder.addUserTask(\"step2\", ACTOR_NAME).addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        for (int i = 0; i < 3; i++) {\n            final long step1Id = waitForUserTaskAndcheckPendingHumanTaskInstances(\"step1\", processInstance);\n            assignAndExecuteStep(step1Id, user);\n        }\n        waitForUserTaskAndcheckPendingHumanTaskInstances(\"step2\", processInstance);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void abortProcessWithActiveLoopActivity() throws Exception {\n        // given\n        final String loopName = \"step1\";\n        final String userTaskName = \"step2\";\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithLoopAndUserTaskInPararallelAndTerminateEvent(\n                loopName, userTaskName);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        waitForUserTask(processInstance.getId(), loopName);\n        // when\n        waitForUserTaskAndExecuteIt(processInstance, userTaskName, user);\n\n        // then\n        // executing the user task will terminate the process: the loop activity must be aborted\n        waitForFlowNodeInState(processInstance, loopName, TestStates.ABORTED, true);\n        waitForProcessToFinish(processInstance);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    private ProcessDefinition deployAndEnableProcessWithLoopAndUserTaskInPararallelAndTerminateEvent(\n            final String loopName, final String parallelTaskName)\n            throws Exception {\n        final Expression condition = new ExpressionBuilder().createConstantBooleanExpression(true);\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"My proc\", \"1.0\");\n        builder.addActor(ACTOR_NAME).addDescription(\"ACTOR_NAME all day and night long\");\n        builder.addStartEvent(\"start\");\n        builder.addUserTask(loopName, ACTOR_NAME).addLoop(false, condition);\n        builder.addUserTask(parallelTaskName, ACTOR_NAME);\n        builder.addGateway(\"gateway\", GatewayType.PARALLEL);\n        builder.addEndEvent(\"terminate\").addTerminateEventTrigger();\n        builder.addTransition(\"start\", \"gateway\");\n        builder.addTransition(\"gateway\", loopName);\n        builder.addTransition(\"gateway\", parallelTaskName);\n        builder.addTransition(loopName, \"terminate\");\n        builder.addTransition(parallelTaskName, \"terminate\");\n\n        return deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/activity/ManualTasksIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.activity;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.ManualTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.UserTaskInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.supervisor.ProcessSupervisorSearchDescriptor;\nimport org.bonitasoft.engine.connectors.VariableStorage;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.junit.After;\nimport org.junit.Test;\n\npublic class ManualTasksIT extends TestWithUser {\n\n    @Override\n    @After\n    public void after() throws Exception {\n        VariableStorage.clearAll();\n        super.after();\n    }\n\n    @Test\n    public void executeProcessWithManualTask() throws Exception {\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder().createNewInstance(\n                \"executeConnectorOnStartOfAnAutomaticActivity\", \"1.0\");\n        designProcessDefinition.addActor(ACTOR_NAME).addDescription(\"Delivery all day and night long\");\n        designProcessDefinition.addAutomaticTask(\"step1\");\n        designProcessDefinition.addManualTask(\"step2\", ACTOR_NAME);\n        designProcessDefinition.addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(),\n                ACTOR_NAME, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndAssignIt(processInstance, \"step2\", user);\n        final List<HumanTaskInstance> toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(user.getId(), 0, 10,\n                null);\n        assertEquals(1, toDoTasks.size());\n        final HumanTaskInstance humanTaskInstance = toDoTasks.get(0);\n        assertEquals(user.getId(), humanTaskInstance.getAssigneeId());\n\n        // No need to verify anything, if no exception, query exists\n        getProcessAPI().searchActivities(\n                new SearchOptionsBuilder(0, 10)\n                        .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, processInstance.getId())\n                        .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.MANUAL_TASK).done());\n\n        getProcessAPI().searchActivities(\n                new SearchOptionsBuilder(0, 10)\n                        .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, processInstance.getId())\n                        .filter(ProcessSupervisorSearchDescriptor.USER_ID, user.getId()).done());\n\n        getProcessAPI().searchActivities(\n                new SearchOptionsBuilder(0, 10)\n                        .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.MANUAL_TASK)\n                        .filter(ProcessSupervisorSearchDescriptor.USER_ID, user.getId()).done());\n\n        getProcessAPI().executeFlowNode(humanTaskInstance.getId());\n        waitForProcessToFinish(processInstance.getId());\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeProcessWithManualTaskAndUserTask() throws Exception {\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder().createNewInstance(\n                \"executeConnectorOnStartOfAnAutomaticActivity\", \"1.0\");\n        designProcessDefinition.addActor(ACTOR_NAME).addDescription(\"Delivery all day and night long\");\n        designProcessDefinition.addAutomaticTask(\"step1\");\n        designProcessDefinition.addManualTask(\"step2\", ACTOR_NAME);\n        designProcessDefinition.addUserTask(\"step3\", ACTOR_NAME);\n        designProcessDefinition.addTransition(\"step1\", \"step2\");\n        designProcessDefinition.addTransition(\"step1\", \"step3\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(),\n                ACTOR_NAME, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndAssignIt(processInstance, \"step2\", user);\n        waitForUserTaskAndAssignIt(processInstance, \"step3\", user);\n\n        final List<HumanTaskInstance> toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(user.getId(), 0, 10,\n                null);\n        assertEquals(2, toDoTasks.size());\n        final HumanTaskInstance humanTaskInstance1 = toDoTasks.get(0);\n        final HumanTaskInstance humanTaskInstance2 = toDoTasks.get(1);\n        HumanTaskInstance step3;\n        HumanTaskInstance step2;\n        if (humanTaskInstance1.getName().equals(\"step3\")) {\n            step3 = humanTaskInstance1;\n            step2 = humanTaskInstance2;\n        } else {\n            step3 = humanTaskInstance2;\n            step2 = humanTaskInstance1;\n        }\n        assertEquals(user.getId(), humanTaskInstance1.getAssigneeId());\n        assertEquals(user.getId(), humanTaskInstance2.getAssigneeId());\n        assertTrue(step2 instanceof ManualTaskInstance);\n        assertTrue(step3 instanceof UserTaskInstance);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/activity/MultiInstanceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.activity;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.awaitility.Awaitility.await;\nimport static org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID;\nimport static org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstanceSearchDescriptor.STATE_NAME;\nimport static org.bonitasoft.engine.expression.ExpressionConstants.*;\nimport static org.bonitasoft.engine.operation.OperatorType.ASSIGNMENT;\nimport static org.bonitasoft.engine.test.TestStates.ABORTED;\nimport static org.junit.Assert.*;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.actor.ActorCriterion;\nimport org.bonitasoft.engine.bpm.actor.ActorInstance;\nimport org.bonitasoft.engine.bpm.bar.BarResource;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.comment.Comment;\nimport org.bonitasoft.engine.bpm.comment.SearchCommentsDescriptor;\nimport org.bonitasoft.engine.bpm.data.DataInstance;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.ActivityStates;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedAutomaticTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedMultiInstanceActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\nimport org.bonitasoft.engine.bpm.flownode.GatewayType;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.MultiInstanceActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.FlowElementContainerDefinitionImpl;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.bpm.process.impl.CallActivityBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.supervisor.ProcessSupervisorSearchDescriptor;\nimport org.bonitasoft.engine.connectors.TestConnector;\nimport org.bonitasoft.engine.connectors.VariableStorage;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.expression.ExpressionConstants;\nimport org.bonitasoft.engine.filter.user.TestFilterWithAutoAssign;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.io.IOUtil;\nimport org.bonitasoft.engine.operation.LeftOperandBuilder;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class MultiInstanceIT extends TestWithUser {\n\n    private static final String JACK = \"jack\";\n\n    private static final String JOHN = \"john\";\n\n    private static final String JENNY = \"jenny\";\n\n    private User john;\n\n    private User jack;\n\n    private User jenny;\n\n    @Override\n    @Before\n    public void before() throws Exception {\n        super.before();\n        john = createUser(JOHN, \"bpm\");\n        jack = createUser(JACK, \"bpm\");\n        jenny = createUser(JENNY, \"bpm\");\n        logout();\n        loginOnDefaultTenantWith(JOHN, \"bpm\");\n    }\n\n    @Override\n    @After\n    public void after() throws Exception {\n        deleteUser(JOHN);\n        deleteUser(JACK);\n        deleteUser(JENNY);\n        VariableStorage.clearAll();\n        super.after();\n    }\n\n    @Test\n    public void executeAMultiInstanceUserTaskWhichCreate0Task() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\n                \"executeAMultiInstanceUserTaskWhichCreate0Task\",\n                PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME);\n        builder.addAutomaticTask(\"autostep\").addUserTask(\"step1\", ACTOR_NAME)\n                .addMultiInstance(false, new ExpressionBuilder().createConstantIntegerExpression(0));\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john);\n        final ProcessInstance instance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForProcessToFinish(instance);\n        final List<ArchivedActivityInstance> archivedActivityInstances = getProcessAPI().getArchivedActivityInstances(\n                instance.getId(), 0, 100,\n                ActivityInstanceCriterion.NAME_ASC);\n        assertEquals(4, archivedActivityInstances.size());\n        for (final ArchivedActivityInstance archivedActivityInstance : archivedActivityInstances) {\n            assertTrue(ArchivedAutomaticTaskInstance.class.isInstance(archivedActivityInstance)\n                    && archivedActivityInstance.getName().contains(\"auto\")\n                    || ArchivedMultiInstanceActivityInstance.class.isInstance(archivedActivityInstance));\n        }\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeAMultiInstanceUserTask() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"executeAMultiInstanceUserTask\", PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME);\n        builder.addUserTask(\"step1\", ACTOR_NAME).addMultiInstance(true,\n                new ExpressionBuilder().createConstantIntegerExpression(2));\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(processInstance, \"step1\", john);\n        waitForUserTask(processInstance, \"step1\");\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void searchMultiInstance() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"executeAMultiInstanceUserTask\", PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME);\n        builder.addUserTask(\"step1\", ACTOR_NAME).addMultiInstance(true,\n                new ExpressionBuilder().createConstantIntegerExpression(1));\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n\n        // No need to verify anything, if no exception, query exists\n        getProcessAPI().searchActivities(\n                new SearchOptionsBuilder(0, 10)\n                        .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, processInstance.getId())\n                        .filter(ProcessSupervisorSearchDescriptor.USER_ID, john.getId()).done());\n\n        getProcessAPI().searchActivities(\n                new SearchOptionsBuilder(0, 10)\n                        .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.MULTI_INSTANCE_ACTIVITY)\n                        .filter(ProcessSupervisorSearchDescriptor.USER_ID, john.getId()).done());\n\n        final SearchResult<ActivityInstance> searchActivities = getProcessAPI().searchActivities(\n                new SearchOptionsBuilder(0, 10)\n                        .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, processInstance.getId())\n                        .filter(ActivityInstanceSearchDescriptor.STATE_NAME, \"executing\")\n                        .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.MULTI_INSTANCE_ACTIVITY)\n                        .done());\n        assertThat(searchActivities.getResult().get(0).getType())\n                .isEqualByComparingTo(FlowNodeType.MULTI_INSTANCE_ACTIVITY);\n        final MultiInstanceActivityInstance activityInstance = (MultiInstanceActivityInstance) searchActivities\n                .getResult().get(0);\n        assertEquals(1, activityInstance.getNumberOfActiveInstances());\n\n        final SearchResult<FlowNodeInstance> searchFlowNode = getProcessAPI().searchFlowNodeInstances(\n                new SearchOptionsBuilder(0, 10)\n                        .filter(FlowNodeInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID, processInstance.getId())\n                        .filter(FlowNodeInstanceSearchDescriptor.STATE_NAME, \"executing\").done());\n        final MultiInstanceActivityInstance flowNode = (MultiInstanceActivityInstance) searchFlowNode.getResult()\n                .get(0);\n        assertEquals(1, flowNode.getNumberOfActiveInstances());\n\n        assignAndExecuteStep(step1Id, john);\n        waitForProcessToFinish(processInstance);\n\n        final SearchResult<ArchivedActivityInstance> searchArchivedActivities = getProcessAPI()\n                .searchArchivedActivities(\n                        new SearchOptionsBuilder(0, 10)\n                                .filter(ArchivedActivityInstanceSearchDescriptor.SOURCE_OBJECT_ID, flowNode.getId())\n                                .done());\n        assertTrue(ArchivedMultiInstanceActivityInstance.class.isInstance(searchArchivedActivities.getResult().get(0)));\n        assertEquals(flowNode.getId(), searchArchivedActivities.getResult().get(0).getSourceObjectId());\n\n        final SearchResult<ArchivedFlowNodeInstance> searchArchivedFlowNode = getProcessAPI()\n                .searchArchivedFlowNodeInstances(\n                        new SearchOptionsBuilder(0, 10)\n                                .filter(ArchivedFlowNodeInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID,\n                                        processInstance.getId())\n                                .filter(ArchivedFlowNodeInstanceSearchDescriptor.STATE_NAME, \"executing\").done());\n        assertTrue(ArchivedMultiInstanceActivityInstance.class.isInstance(searchArchivedFlowNode.getResult().get(0)));\n        assertEquals(flowNode.getId(), searchArchivedFlowNode.getResult().get(0).getSourceObjectId());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeAMultiInstanceWithLoopDataInputAndOutput() throws Exception {\n        final List<?> outputList = executeAMultiInstanceWithLoopDataAs(\"[58,26,12]\", \"[1,2,3]\");\n        assertEquals(3, outputList.size());\n        assertEquals(59, outputList.get(0));\n        assertEquals(27, outputList.get(1));\n        assertEquals(13, outputList.get(2));\n    }\n\n    private List<?> executeAMultiInstanceWithLoopDataAs(final String inputListScript, final String outputListScript)\n            throws Exception {\n        final String anotherActor = \"anotherActor\";\n\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"executeAMultiInstanceWithMaxIteration\", PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME);\n        builder.addActor(anotherActor);\n\n        final String loopDataInputName = \"loopDataInput_\";\n        String loopDataOutputName = \"loopDataOutput_\";\n        builder.addData(loopDataInputName, List.class.getName(),\n                new ExpressionBuilder().createGroovyScriptExpression(\"executeAMultiInstanceWithLoopDataInputAndOutput1\",\n                        inputListScript,\n                        List.class.getName()));\n        if (outputListScript != null) {\n            builder.addData(\n                    loopDataOutputName,\n                    List.class.getName(),\n                    new ExpressionBuilder().createGroovyScriptExpression(\n                            \"executeAMultiInstanceWithLoopDataInputAndOutput2\", outputListScript,\n                            List.class.getName()));\n        } else {\n            loopDataOutputName = loopDataInputName;\n        }\n        final UserTaskDefinitionBuilder userTask = builder.addUserTask(\"step1\", ACTOR_NAME);\n        userTask.addData(\"dataInputItem_\", Integer.class.getName(),\n                new ExpressionBuilder().createConstantIntegerExpression(0));\n        userTask.addData(\"dataOutputItem_\", Integer.class.getName(),\n                new ExpressionBuilder().createConstantIntegerExpression(0));\n        userTask.addOperation(\n                new LeftOperandBuilder().createNewInstance(\"dataOutputItem_\").done(),\n                ASSIGNMENT,\n                \"=\",\n                null,\n                new ExpressionBuilder().createGroovyScriptExpression(\"executeAMultiInstanceWithLoopDataInputAndOutput3\",\n                        \"dataInputItem_ + 1\",\n                        Integer.class.getName(),\n                        new ExpressionBuilder().createDataExpression(\"dataInputItem_\", Integer.class.getName())));\n        userTask.addMultiInstance(true, loopDataInputName).addDataInputItemRef(\"dataInputItem_\")\n                .addDataOutputItemRef(\"dataOutputItem_\")\n                .addLoopDataOutputRef(loopDataOutputName);\n        builder.addUserTask(\"lastTask\", anotherActor);\n        builder.addTransition(\"step1\", \"lastTask\");\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(),\n                Arrays.asList(ACTOR_NAME, anotherActor),\n                Arrays.asList(john, jack));\n        final ProcessInstance process = getProcessAPI().startProcess(processDefinition.getId());\n        final DataInstance processDataInstance2 = getProcessAPI().getProcessDataInstance(loopDataInputName,\n                process.getId());\n        final List<?> value = (List<?>) processDataInstance2.getValue();\n        final int loopMax = value.size();\n        checkPendingTaskSequentially(loopMax, process, false);\n        waitForUserTask(process, \"lastTask\");\n        final DataInstance processDataInstance = getProcessAPI().getProcessDataInstance(loopDataOutputName,\n                process.getId());\n        assertNotNull(\"unable to find the loop data output on the process\", processDataInstance);\n        final List<?> list = (List<?>) processDataInstance.getValue();\n        disableAndDeleteProcess(processDefinition);\n        return list;\n    }\n\n    @Test\n    public void executeAMultiInstanceWithMaxIteration() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"executeAMultiInstanceWithMaxIteration\", PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME);\n        builder.addIntegerData(\"a\", new ExpressionBuilder().createConstantIntegerExpression(1));\n        builder.addIntegerData(\"b\", new ExpressionBuilder().createConstantIntegerExpression(2));\n        final int loopMax = 3;\n        builder.addUserTask(\"step1\", ACTOR_NAME).addMultiInstance(\n                true,\n                new ExpressionBuilder().createGroovyScriptExpression(\"executeAMultiInstanceWithMaxIteration\", \"a + b\",\n                        Integer.class.getName(),\n                        new ExpressionBuilder().createDataExpression(\"a\", Integer.class.getName()),\n                        new ExpressionBuilder().createDataExpression(\"b\", Integer.class.getName())));\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        checkPendingTaskSequentially(loopMax, processInstance, true);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeAMultiInstanceWithLoopDataOutputEmpty() throws Exception {\n        final List<?> list = executeAMultiInstanceWithLoopDataAs(\"[58,26,12]\", \"[]\");\n        assertEquals(3, list.size());\n        assertEquals(59, list.get(0));\n        assertEquals(27, list.get(1));\n        assertEquals(13, list.get(2));\n    }\n\n    @Test\n    public void executeAMultiInstanceWithLoopDataOutputNull() throws Exception {\n        final List<?> list = executeAMultiInstanceWithLoopDataAs(\"[58,26,12]\", \"null\");\n        assertEquals(3, list.size());\n        assertEquals(59, list.get(0));\n        assertEquals(27, list.get(1));\n        assertEquals(13, list.get(2));\n    }\n\n    @Test\n    public void executeAMultiInstanceWithLoopDataOutputTooShort() throws Exception {\n        final List<?> list = executeAMultiInstanceWithLoopDataAs(\"[58,26,12]\", \"[1,2]\");\n        assertEquals(3, list.size());\n        assertEquals(59, list.get(0));\n        assertEquals(27, list.get(1));\n        assertEquals(13, list.get(2));\n    }\n\n    @Test\n    public void executeAMultiInstanceWithLoopDataOutputTooLong() throws Exception {\n        final List<?> list = executeAMultiInstanceWithLoopDataAs(\"[58,26,12]\", \"[1,2,3,4]\");\n        assertEquals(4, list.size());\n        assertEquals(59, list.get(0));\n        assertEquals(27, list.get(1));\n        assertEquals(13, list.get(2));\n        assertEquals(4, list.get(3));\n    }\n\n    @Test\n    public void executeAMultiInstanceWithSameLoopDataAsInputAndOutput() throws Exception {\n        final List<?> list = executeAMultiInstanceWithLoopDataAs(\"[58,26,12]\", null);\n        assertEquals(3, list.size());\n        assertEquals(59, list.get(0));\n        assertEquals(27, list.get(1));\n        assertEquals(13, list.get(2));\n    }\n\n    @Test\n    public void executeAMultiInstanceParallelWithLoopCardinalityUsingGroovyAndData() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\n                \"executeAMultiInstanceParallelWithMaxIteration\",\n                PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME);\n        builder.addIntegerData(\"a\", new ExpressionBuilder().createConstantIntegerExpression(1));\n        builder.addIntegerData(\"b\", new ExpressionBuilder().createConstantIntegerExpression(2));\n        final int loopMax = 3;\n        builder.addUserTask(\"step1\", ACTOR_NAME).addMultiInstance(\n                false,\n                new ExpressionBuilder().createGroovyScriptExpression(\n                        \"executeAMultiInstanceParallelWithLoopCardinalityUsingGroovyAndData\", \"a + b\",\n                        Integer.class.getName(),\n                        new ExpressionBuilder().createDataExpression(\"a\", Integer.class.getName()),\n                        new ExpressionBuilder().createDataExpression(\"b\", Integer.class.getName())));\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        checkPendingTaskInParallel(loopMax, loopMax, processInstance);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeAMultiInstanceParallelWithLoopCardinalityMoreThan20() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\n                \"executeAMultiInstanceParallelWithMaxIteration\",\n                PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME);\n        builder.addIntegerData(\"a\", new ExpressionBuilder().createConstantIntegerExpression(16));\n        builder.addIntegerData(\"b\", new ExpressionBuilder().createConstantIntegerExpression(14));\n        final int loopMax = 30;\n        builder.addUserTask(\"step1\", ACTOR_NAME).addMultiInstance(\n                false,\n                new ExpressionBuilder().createGroovyScriptExpression(\n                        \"executeAMultiInstanceParallelWithLoopCardinalityUsingGroovyAndData\", \"a + b\",\n                        Integer.class.getName(),\n                        new ExpressionBuilder().createDataExpression(\"a\", Integer.class.getName()),\n                        new ExpressionBuilder().createDataExpression(\"b\", Integer.class.getName())));\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        checkPendingTaskInParallel(loopMax, loopMax, processInstance);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeAMultiInstanceParallelWithCompletionCondition() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\n                \"executeAMultiInstanceParallelWithCompletionCondition\",\n                PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME);\n        final int loopMax = 3;\n        builder.addUserTask(\"step1\", ACTOR_NAME)\n                .addMultiInstance(false, new ExpressionBuilder().createConstantIntegerExpression(3))\n                .addCompletionCondition(\n                        new ExpressionBuilder().createGroovyScriptExpression(\n                                \"executeAMultiInstanceParallelWithCompletionCondition\",\n                                NUMBER_OF_COMPLETED_INSTANCES.getEngineConstantName() + \" >= 2 \",\n                                Boolean.class.getName(),\n                                new ExpressionBuilder().createEngineConstant(NUMBER_OF_COMPLETED_INSTANCES)));\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        checkPendingTaskInParallel(loopMax, 2, processInstance);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void remainingInstancesAreAbortedAfterCompletionCondition() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\n                \"remainingInstancesAreAbortedAfterCompletionCondition\",\n                PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME);\n        final int loopMax = 3;\n        builder.addUserTask(\"step1\", ACTOR_NAME)\n                .addMultiInstance(false, new ExpressionBuilder().createConstantIntegerExpression(loopMax))\n                .addCompletionCondition(\n                        new ExpressionBuilder().createGroovyScriptExpression(\n                                \"remainingInstancesAreAbortedAfterCompletionCondition\",\n                                NUMBER_OF_COMPLETED_INSTANCES.getEngineConstantName() + \" == 1 \",\n                                Boolean.class.getName(),\n                                new ExpressionBuilder().createEngineConstant(NUMBER_OF_COMPLETED_INSTANCES)));\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        checkPendingTaskInParallel(loopMax, 1, processInstance);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    /**\n     * Test of completion condition (Sequential multi-instance - Number of completed instances).\n     *\n     * @throws Exception\n     * @since 6.0\n     */\n    @Test\n    public void executeAMultiInstanceSequentialWithCompletionCondition() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\n                \"executeAMultiInstanceSequentialWithCompletionCondition\",\n                PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME);\n        builder.addUserTask(\"step1\", ACTOR_NAME)\n                .addMultiInstance(true, new ExpressionBuilder().createConstantIntegerExpression(20))\n                .addCompletionCondition(\n                        new ExpressionBuilder().createGroovyScriptExpression(\n                                \"executeAMultiInstanceSequentialWithCompletionCondition\",\n                                NUMBER_OF_COMPLETED_INSTANCES.getEngineConstantName() + \" >= 15 \",\n                                Boolean.class.getName(),\n                                new ExpressionBuilder().createEngineConstant(NUMBER_OF_COMPLETED_INSTANCES)));\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        checkPendingTaskSequentially(15, processInstance, true);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void abortAMultiInstanceSequential() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME);\n        builder.addStartEvent(\"Start\").addEndEvent(\"End\").addTerminateEventTrigger();\n        builder.addUserTask(\"Step1\", ACTOR_NAME)\n                .addMultiInstance(true, new ExpressionBuilder().createConstantIntegerExpression(20))\n                .addCompletionCondition(new ExpressionBuilder().createConstantBooleanExpression(false));\n        builder.addGateway(\"Gateway\", GatewayType.PARALLEL).addUserTask(\"Step2\", ACTOR_NAME);\n        builder.addTransition(\"Start\", \"Gateway\").addTransition(\"Gateway\", \"Step1\").addTransition(\"Step1\", \"End\")\n                .addTransition(\"Gateway\", \"Step2\")\n                .addTransition(\"Step2\", \"End\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(processInstance, \"Step1\");\n        waitForUserTaskAndExecuteIt(processInstance, \"Step2\", john);\n        waitForProcessToFinish(processInstance);\n        final ArchivedActivityInstance archivedStep1 = getProcessAPI().getArchivedActivityInstance(step1Id);\n        assertEquals(ActivityStates.ABORTED_STATE, archivedStep1.getState());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    /**\n     * Test with completion condition equal to true (sequential multi-instance).\n     *\n     * @throws Exception\n     * @since 6.0\n     */\n    @Test\n    public void executeAMultiInstanceSequentialWithConpletionConditionTrue() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\n                \"executeAMultiInstanceSequentialWithCompletionConditionTrue\",\n                PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME).addDescription(\"Deliver all day and night long\");\n        builder.addUserTask(\"step1\", ACTOR_NAME)\n                .addMultiInstance(true, new ExpressionBuilder().createConstantIntegerExpression(4))\n                .addCompletionCondition(\n                        new ExpressionBuilder().createGroovyScriptExpression(\n                                \"executeAMultiInstanceSequentialWithCompletionConditionTrue\", \"true\",\n                                Boolean.class.getName()));\n        builder.addUserTask(\"step2\", ACTOR_NAME).addUserTask(\"step3\", ACTOR_NAME).addTransition(\"step1\", \"step2\")\n                .addTransition(\"step2\", \"step3\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        checkPendingTaskSequentially(3, processInstance, true);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    /**\n     * Test with completion condition equal to true (parallel multi-instance).\n     *\n     * @throws Exception\n     * @since 6.0\n     */\n    @Test\n    public void executeAMultiInstanceParallelWithConpletionConditionTrue() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\n                \"executeAMultiInstanceSequentialWithCompletionConditionTrue\",\n                PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME).addDescription(\"Deliver all day and night long\");\n        builder.addUserTask(\"step1\", ACTOR_NAME)\n                .addMultiInstance(false, new ExpressionBuilder().createConstantIntegerExpression(4))\n                .addCompletionCondition(\n                        new ExpressionBuilder().createGroovyScriptExpression(\n                                \"executeAMultiInstanceSequentialWithCompletionConditionTrue\", \"true\",\n                                Boolean.class.getName()));\n        builder.addAutomaticTask(\"step2\").addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance, \"step1\");\n        waitForUserTask(processInstance, \"step1\");\n        waitForUserTask(processInstance, \"step1\");\n        waitForUserTaskAndExecuteIt(processInstance, \"step1\", john);\n\n        waitForProcessToFinish(processInstance);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    /**\n     * Test of completion condition (Sequential multi-instance - Number of completed instances).\n     *\n     * @throws Exception\n     * @since 6.0\n     */\n    @Test\n    public void executeAMultiInstanceWithCompletionConditionNumberOfCompletedInstances() throws Exception {\n        final String condition = \" >= 2\";\n        final int loopMax = 3;\n        final int numberOfExecutedActivities = loopMax - 1;\n\n        withMultiInstanceAttribute(condition, NUMBER_OF_COMPLETED_INSTANCES, numberOfExecutedActivities);\n    }\n\n    /**\n     * Test of completion condition (Sequential multi-instance - Number of instances).\n     *\n     * @throws Exception\n     * @since 6.0\n     */\n    @Test\n    public void executeAMultiInstanceWithCompletionConditionNumberOfInstances() throws Exception {\n        final String condition = \" >= 2\";\n        final int loopMax = 3;\n        final int numberOfExecutedActivities = loopMax - 1;\n\n        withMultiInstanceAttribute(condition, ExpressionConstants.NUMBER_OF_INSTANCES, numberOfExecutedActivities);\n    }\n\n    /**\n     * Test of completion condition (Sequential multi-instance - Number of terminated instances).\n     *\n     * @throws Exception\n     * @since 6.0\n     */\n    @Test\n    public void executeAMultiInstanceWithCompletionConditionNumberOfTerminatedInstances() throws Exception {\n        final String condition = \" >= 2\";\n        final int loopMax = 3;\n        final int numberOfExecutedActivities = loopMax;// numberOfTerminated instance is never >= 2 here\n\n        withMultiInstanceAttribute(condition, ExpressionConstants.NUMBER_OF_TERMINATED_INSTANCES,\n                numberOfExecutedActivities);\n    }\n\n    /**\n     * Test of completion condition (Sequential multi-instance - Number of actives instances).\n     *\n     * @throws Exception\n     * @since 6.0\n     */\n    @Test\n    public void executeAMultiInstanceWithCompletionConditionNumberOfActiveInstances() throws Exception {\n        final String condition = \" == 2\";\n        final int loopMax = 3;\n        final int numberOfExecutedActivities = loopMax;// will never be true when is in sequence\n\n        withMultiInstanceAttribute(condition, ExpressionConstants.NUMBER_OF_ACTIVE_INSTANCES,\n                numberOfExecutedActivities);\n    }\n\n    /**\n     * Test of completion condition (Parallel multi-instance - Number of completed instances).\n     *\n     * @throws Exception\n     * @since 6.0\n     */\n    @Test\n    public void executeAMultiInstanceParallelWithCompletionConditionNumberOfCompletedInstances() throws Exception {\n        final String condition = \" >= 2 \";\n        final int loopMax = 3;\n        final int numberOfExecutedActivities = loopMax;\n        final int numberOfTaskToComplete = 2;\n\n        withParallelMultiInstanceAttribute(condition, NUMBER_OF_COMPLETED_INSTANCES, numberOfExecutedActivities,\n                numberOfTaskToComplete);\n    }\n\n    /**\n     * Test of completion condition (Parallel multi-instance - Number of instances).\n     *\n     * @throws Exception\n     * @since 6.0\n     */\n    @Test\n    public void executeAMultiInstanceParallelWithCompletionConditionNumberOfInstances() throws Exception {\n        final String condition = \" >= 2\";\n        final int loopMax = 3;\n        final int numberOfExecutedActivities = loopMax;\n        final int numberOfTaskToComplete = 1;\n\n        withParallelMultiInstanceAttribute(condition, ExpressionConstants.NUMBER_OF_INSTANCES,\n                numberOfExecutedActivities, numberOfTaskToComplete);\n    }\n\n    /**\n     * Test of completion condition (Parallel multi-instance - Number of terminated instances).\n     *\n     * @throws Exception\n     * @since 6.0\n     */\n    @Test\n    public void executeAMultiInstanceParallelWithCompletionConditionNumberOfTerminatedInstances() throws Exception {\n        final String condition = \" >= 2\";\n        final int loopMax = 3;\n        final int numberOfExecutedActivities = loopMax;\n        final int numberOfTaskToComplete = 3;\n\n        withParallelMultiInstanceAttribute(condition, ExpressionConstants.NUMBER_OF_TERMINATED_INSTANCES,\n                numberOfExecutedActivities, numberOfTaskToComplete);\n    }\n\n    /**\n     * Test of completion condition (Parallel multi-instance - Number of actives instances).\n     *\n     * @throws Exception\n     * @since 6.0\n     */\n    @Test\n    public void executeAMultiInstanceParallelWithCompletionConditionNumberOfActiveInstances() throws Exception {\n        final String condition = \" == 2\";\n        final int loopMax = 3;\n        final int numberOfExecutedActivities = loopMax;\n        final int numberOfTaskToComplete = 1;\n\n        withParallelMultiInstanceAttribute(condition, ExpressionConstants.NUMBER_OF_ACTIVE_INSTANCES,\n                numberOfExecutedActivities, numberOfTaskToComplete);\n    }\n\n    /**\n     * Test of task execution after a sequential multi-instance with user tasks.\n     *\n     * @throws Exception\n     * @since 6.0\n     */\n    @Test\n    public void executeTaskAfterMultiInstanceSequential() throws Exception {\n        final int loopMax = 3;\n        final int numberOfExecutedActivities = loopMax + 1;\n\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"executeTaskAfterMultiInstanceSequential\", PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME);\n        builder.addUserTask(\"step1\", ACTOR_NAME).addMultiInstance(true,\n                new ExpressionBuilder().createConstantIntegerExpression(loopMax));\n        builder.addAutomaticTask(\"step2\").addUserTask(\"step3\", ACTOR_NAME).addTransition(\"step1\", \"step2\")\n                .addTransition(\"step2\", \"step3\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        checkPendingTaskSequentially(numberOfExecutedActivities, processInstance, true);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    /**\n     * Test of task execution after a sequential multi-instance with automatics tasks.\n     *\n     * @throws Exception\n     * @since 6.0\n     */\n    @Test\n    public void executeTaskAfterMultiInstanceSequentialAuto() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\n                \"executeTaskAfterMultiInstanceSequentialAuto\",\n                PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME);\n        builder.addAutomaticTask(\"step1\").addMultiInstance(true,\n                new ExpressionBuilder().createConstantIntegerExpression(3));\n        builder.addAutomaticTask(\"step2\").addUserTask(\"step3\", ACTOR_NAME).addTransition(\"step1\", \"step2\")\n                .addTransition(\"step2\", \"step3\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(processInstance, \"step3\", john);\n\n        waitForProcessToFinish(processInstance);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    /**\n     * Test of task execution after a parallel multi-instance with user tasks.\n     *\n     * @throws Exception\n     * @since 6.0\n     */\n    @Test\n    public void executeTaskAfterMultiInstanceParallel() throws Exception {\n        final int loopMax = 3;\n        final int numberOfTaskToComplete = 3;\n\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"executeTaskAfterMultiInstanceSequential\", PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME);\n        builder.addUserTask(\"step1\", ACTOR_NAME).addMultiInstance(false,\n                new ExpressionBuilder().createConstantIntegerExpression(loopMax));\n        builder.addAutomaticTask(\"step2\").addAutomaticTask(\"step3\").addTransition(\"step1\", \"step2\")\n                .addTransition(\"step2\", \"step3\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        checkPendingTaskInParallel(loopMax, numberOfTaskToComplete, processInstance);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    /**\n     * Test of task execution after a parallel multi-instance with automatics tasks.\n     *\n     * @throws Exception\n     * @since 6.0\n     */\n    @Test\n    public void executeTaskAfterMultiInstanceParallelAuto() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\n                \"executeTaskAfterMultiInstanceSequentialAuto\",\n                PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME);\n        builder.addAutomaticTask(\"step1\").addMultiInstance(false,\n                new ExpressionBuilder().createConstantIntegerExpression(3));\n        builder.addAutomaticTask(\"step2\").addUserTask(\"step3\", ACTOR_NAME).addTransition(\"step1\", \"step2\")\n                .addTransition(\"step2\", \"step3\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(processInstance, \"step3\", john);\n\n        waitForProcessToFinish(processInstance);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    /**\n     * Test of parallel multi-instance with several users.\n     *\n     * @throws Exception\n     * @since 6.0\n     */\n    @Test\n    public void multiInstanceParallelWithSeveralUsers() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"executeMultiInstanceWithActors\", PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME).addDescription(\"Survey\");\n        builder.addUserTask(\"step1\", ACTOR_NAME).addMultiInstance(false,\n                new ExpressionBuilder().createConstantIntegerExpression(3));\n        builder.addAutomaticTask(\"step2\").addTransition(\"step1\", \"step2\");\n\n        final List<User> listUsers = new ArrayList<>();\n        final List<String> listActors = new ArrayList<>();\n        listUsers.add(john);\n        listActors.add(ACTOR_NAME);\n        listUsers.add(jack);\n        listActors.add(ACTOR_NAME);\n        listUsers.add(jenny);\n        listActors.add(ACTOR_NAME);\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), listActors,\n                listUsers);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        checkNbPendingTaskOf(3, john);\n        checkNbPendingTaskOf(3, jack);\n        checkNbPendingTaskOf(3, jenny);\n\n        // Execute task of multi-instance for John\n        final List<HumanTaskInstance> pendingTasks1 = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10,\n                null);\n        final HumanTaskInstance pendingTask1 = pendingTasks1.get(0);\n        assignAndExecuteStep(pendingTask1, john.getId());\n\n        // Execute task of multi-instance for Jack\n        final List<HumanTaskInstance> pendingTasks2 = getProcessAPI().getPendingHumanTaskInstances(jack.getId(), 0, 10,\n                null);\n        final HumanTaskInstance pendingTask2 = pendingTasks2.get(0);\n        assignAndExecuteStep(pendingTask2, jack.getId());\n\n        // Execute task of multi-instance for Jenny\n        final List<HumanTaskInstance> pendingTasks3 = getProcessAPI().getPendingHumanTaskInstances(jenny.getId(), 0, 10,\n                null);\n        final HumanTaskInstance pendingTask3 = pendingTasks3.get(0);\n        assignAndExecuteStep(pendingTask3, jenny.getId());\n\n        waitForProcessToFinish(processInstance);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    /**\n     * Test of sequential multi-instance with several users.\n     */\n    @Test\n    public void multiInstanceSequentialWithSeveralUsers() throws Exception {\n        final int loopMax = 3;\n\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"executeMultiInstanceWithActors\", PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME).addDescription(\"Survey\");\n        builder.addIntegerData(\"numberOfAlreadyFinishedTaskInstances\",\n                new ExpressionBuilder().createConstantIntegerExpression(120));\n        builder.addIntegerData(\"totalNumberOfTaskInstances\",\n                new ExpressionBuilder().createConstantIntegerExpression(120));\n        builder.addIntegerData(\"numberOfActiveInstances\",\n                new ExpressionBuilder().createConstantIntegerExpression(120));\n        builder.addIntegerData(\"numberOfTerminatedInstances\",\n                new ExpressionBuilder().createConstantIntegerExpression(120));\n        UserTaskDefinitionBuilder step1 = builder.addUserTask(\"step1\", ACTOR_NAME);\n        step1.addOperation(new LeftOperandBuilder().createDataLeftOperand(\"numberOfAlreadyFinishedTaskInstances\"),\n                ASSIGNMENT,\n                \"=\",\n                new ExpressionBuilder().createGroovyScriptExpression(\"CompletedInstancesCountScript\",\n                        \"numberOfCompletedInstances\",\n                        \"java.lang.Integer\",\n                        new ExpressionBuilder().createEngineConstant(NUMBER_OF_COMPLETED_INSTANCES)));\n        step1.addOperation(new LeftOperandBuilder().createDataLeftOperand(\"totalNumberOfTaskInstances\"), ASSIGNMENT,\n                \"=\",\n                new ExpressionBuilder().createGroovyScriptExpression(\"TotalInstancesCountScript\", \"numberOfInstances\",\n                        \"java.lang.Integer\",\n                        new ExpressionBuilder().createEngineConstant(NUMBER_OF_INSTANCES)));\n        step1.addOperation(new LeftOperandBuilder().createDataLeftOperand(\"numberOfActiveInstances\"), ASSIGNMENT,\n                \"=\",\n                new ExpressionBuilder().createGroovyScriptExpression(\"numberOfActiveInstancesScript\",\n                        \"numberOfActiveInstances\",\n                        \"java.lang.Integer\",\n                        new ExpressionBuilder().createEngineConstant(NUMBER_OF_ACTIVE_INSTANCES)));\n        step1.addOperation(new LeftOperandBuilder().createDataLeftOperand(\"numberOfTerminatedInstances\"), ASSIGNMENT,\n                \"=\",\n                new ExpressionBuilder().createGroovyScriptExpression(\"numberOfTerminatedInstancesScript\",\n                        \"numberOfTerminatedInstances\",\n                        \"java.lang.Integer\",\n                        new ExpressionBuilder().createEngineConstant(NUMBER_OF_TERMINATED_INSTANCES)));\n\n        step1.addMultiInstance(true, new ExpressionBuilder().createConstantIntegerExpression(loopMax));\n        builder.addAutomaticTask(\"step2\").addTransition(\"step1\", \"step2\");\n\n        final List<User> listUsers = new ArrayList<>();\n        final List<String> listActors = new ArrayList<>();\n        listUsers.add(john);\n        listActors.add(ACTOR_NAME);\n        listUsers.add(jack);\n        listActors.add(ACTOR_NAME);\n        listUsers.add(jenny);\n        listActors.add(ACTOR_NAME);\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), listActors,\n                listUsers);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        // Execute task of multi-instance for John\n        final List<HumanTaskInstance> pendingTasks1 = checkNbPendingTaskOf(1, john).getPendingHumanTaskInstances();\n        final HumanTaskInstance pendingTask1 = pendingTasks1.get(0);\n        assignAndExecuteStep(pendingTask1, john.getId());\n\n        // Execute task of multi-instance for Jack\n        final List<HumanTaskInstance> pendingTasks2 = checkNbPendingTaskOf(1, jack).getPendingHumanTaskInstances();\n        final HumanTaskInstance pendingTask2 = pendingTasks2.get(0);\n        assignAndExecuteStep(pendingTask2, jack.getId());\n\n        final SearchOptionsBuilder sob = new SearchOptionsBuilder(0, 0);\n        sob.filter(ArchivedHumanTaskInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID, processInstance.getId());\n        sob.filter(ArchivedHumanTaskInstanceSearchDescriptor.NAME, pendingTask2.getName());\n        sob.filter(ArchivedHumanTaskInstanceSearchDescriptor.STATE_NAME, \"completed\");\n        final SearchOptions searchOptions = sob.done();\n\n        await().until(() -> getProcessAPI().searchArchivedHumanTasks(searchOptions).getCount() == 2);\n\n        int numberOfFinishedTaskInstances = (int) getProcessAPI()\n                .getProcessDataInstance(\"numberOfAlreadyFinishedTaskInstances\", processInstance.getId())\n                .getValue();\n        assertThat(numberOfFinishedTaskInstances).isEqualTo(1);\n\n        // Execute task of multi-instance for Jenny\n        checkNbPendingTaskOf(1, jenny);\n        final List<HumanTaskInstance> pendingTasks3 = getProcessAPI().getPendingHumanTaskInstances(jenny.getId(), 0, 10,\n                null);\n        final HumanTaskInstance pendingTask3 = pendingTasks3.get(0);\n        assignAndExecuteStep(pendingTask3, jenny.getId());\n        waitForProcessToFinish(processInstance);\n\n        numberOfFinishedTaskInstances = (int) getProcessAPI()\n                .getArchivedProcessDataInstance(\"numberOfAlreadyFinishedTaskInstances\", processInstance.getId())\n                .getValue();\n        int totalNumberOfTaskInstances = (int) getProcessAPI()\n                .getArchivedProcessDataInstance(\"totalNumberOfTaskInstances\", processInstance.getId())\n                .getValue();\n        int numberOfActiveInstances = (int) getProcessAPI()\n                .getArchivedProcessDataInstance(\"numberOfActiveInstances\", processInstance.getId())\n                .getValue();\n        int numberOfTerminatedInstances = (int) getProcessAPI()\n                .getArchivedProcessDataInstance(\"numberOfTerminatedInstances\", processInstance.getId())\n                .getValue();\n        assertThat(numberOfFinishedTaskInstances).isEqualTo(2);\n        assertThat(totalNumberOfTaskInstances).isEqualTo(3);\n        assertThat(numberOfActiveInstances).isEqualTo(1);\n        assertThat(numberOfTerminatedInstances).isEqualTo(0);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    /**\n     * Test of sequential multi-instance with sub-process.\n     *\n     * @throws Exception\n     * @since 6.0\n     */\n    @Test\n    public void multiInstanceSequentialWithSubProcess() throws Exception {\n        final int loopMax = 3;\n\n        // Sub-process definition\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"SubProcessInAMultiInstance\", PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME).addAutomaticTask(\"step1\").addAutomaticTask(\"step2\").addTransition(\"step1\",\n                \"step2\");\n\n        final ProcessDefinition subProcess = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john);\n\n        final Expression targetProcessNameExpr = new ExpressionBuilder()\n                .createConstantStringExpression(subProcess.getName());\n        final Expression targetProcessVersionExpr = new ExpressionBuilder()\n                .createConstantStringExpression(subProcess.getVersion());\n\n        final ProcessDefinitionBuilder builderProc = new ProcessDefinitionBuilder()\n                .createNewInstance(\"executeMultiInstanceSequentialWithSubProcess\", \"1.1\");\n        builderProc.addActor(ACTOR_NAME).addStartEvent(\"start\")\n                .addCallActivity(\"callActivity\", targetProcessNameExpr, targetProcessVersionExpr)\n                .addMultiInstance(false, new ExpressionBuilder().createConstantIntegerExpression(loopMax));\n        builderProc.addAutomaticTask(\"step3\").addEndEvent(\"end\").addTransition(\"start\", \"callActivity\")\n                .addTransition(\"callActivity\", \"step3\")\n                .addTransition(\"step3\", \"end\");\n\n        final ProcessDefinition mainProcess = deployAndEnableProcessWithActor(builderProc.done(), ACTOR_NAME, john);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(mainProcess.getId());\n\n        waitForProcessToFinish(processInstance);\n        disableAndDeleteProcess(mainProcess);\n        disableAndDeleteProcess(subProcess);\n    }\n\n    private void withMultiInstanceAttribute(final String condition, final ExpressionConstants expressionConstant,\n            final int numberOfExecutedActivities)\n            throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"executeAStandardLoopUserTask\" + condition, PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME);\n        builder.addIntegerData(\"a\", new ExpressionBuilder().createConstantIntegerExpression(1));\n        builder.addIntegerData(\"b\", new ExpressionBuilder().createConstantIntegerExpression(2));\n        builder.addUserTask(\"step1\", ACTOR_NAME)\n                .addMultiInstance(\n                        true,\n                        new ExpressionBuilder().createGroovyScriptExpression(\"testWithMultiInstanceAttribute1\", \"a + b\",\n                                Integer.class.getName(),\n                                new ExpressionBuilder().createDataExpression(\"a\", Integer.class.getName()),\n                                new ExpressionBuilder().createDataExpression(\"b\", Integer.class.getName())))\n                .addCompletionCondition(\n                        new ExpressionBuilder().createGroovyScriptExpression(\"testWithMultiInstanceAttribute2\",\n                                expressionConstant.getEngineConstantName()\n                                        + condition,\n                                Boolean.class.getName(),\n                                new ExpressionBuilder().createEngineConstant(expressionConstant)));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        checkPendingTaskSequentially(numberOfExecutedActivities, processInstance, true);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    private void withParallelMultiInstanceAttribute(final String condition,\n            final ExpressionConstants expressionConstant, final int numberOfExecutedActivities,\n            final int numberOfTaskToComplete) throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"executeParallelUserTask\" + condition, PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME);\n        builder.addIntegerData(\"a\", new ExpressionBuilder().createConstantIntegerExpression(1));\n        builder.addIntegerData(\"b\", new ExpressionBuilder().createConstantIntegerExpression(2));\n        builder.addUserTask(\"step1\", ACTOR_NAME)\n                .addMultiInstance(\n                        false,\n                        new ExpressionBuilder().createGroovyScriptExpression(\"testWithMultiInstanceAttribute1\", \"a + b\",\n                                Integer.class.getName(),\n                                new ExpressionBuilder().createDataExpression(\"a\", Integer.class.getName()),\n                                new ExpressionBuilder().createDataExpression(\"b\", Integer.class.getName())))\n                .addCompletionCondition(\n                        new ExpressionBuilder().createGroovyScriptExpression(\"testWithMultiInstanceAttribute2\",\n                                expressionConstant.getEngineConstantName()\n                                        + condition,\n                                Boolean.class.getName(),\n                                new ExpressionBuilder().createEngineConstant(expressionConstant)));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        checkPendingTaskInParallel(numberOfExecutedActivities, numberOfTaskToComplete, processInstance);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    private void checkPendingTaskSequentially(final int loopMax, final ProcessInstance processInstance,\n            final boolean mustBeFinished) throws Exception {\n        for (int i = 0; i < loopMax; i++) {\n            waitForPendingTasks(john.getId(), 1);\n            final List<HumanTaskInstance> pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0,\n                    10, null);\n            final HumanTaskInstance pendingTask = pendingTasks.get(0);\n\n            assignAndExecuteStep(pendingTask, john.getId());\n        }\n        if (mustBeFinished) {\n            waitForProcessToFinish(processInstance);\n        }\n    }\n\n    private void checkPendingTaskInParallel(final int numberOfTask, final int numberOfTaskToCompleteMI,\n            final ProcessInstance processInstance)\n            throws Exception {\n        final List<HumanTaskInstance> pendingTasks = waitForPendingTasks(john.getId(), numberOfTask);\n\n        for (int i = 0; i < numberOfTaskToCompleteMI; i++) {\n            assignAndExecuteStep(pendingTasks.get(i), john.getId());\n        }\n        waitForProcessToFinish(processInstance);\n        final int nbAbortedActivities = numberOfTask - numberOfTaskToCompleteMI;\n        checkNbOfArchivedActivities(processInstance, nbAbortedActivities);\n    }\n\n    @Test\n    public void multiInstanceVoteUseCase() throws Exception {\n        // Build process definition\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"executeMultiInstanceWithSeveralActors\", PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME).addDescription(\"Survey\");\n        final String loopDataInputName = \"listOfUserId\";\n        final String exprListUserIds = \"[\" + john.getId() + \"l,\" + jack.getId() + \"l,\" + jenny.getId() + \"l]\";\n        builder.addData(loopDataInputName, List.class.getName(),\n                new ExpressionBuilder().createGroovyScriptExpression(\"createListUserId\", exprListUserIds,\n                        List.class.getName()));\n        final UserTaskDefinitionBuilder userTaskBuilder = new UserTaskDefinitionBuilder(builder,\n                (FlowElementContainerDefinitionImpl) builder.getProcess()\n                        .getProcessContainer(),\n                \"step1\", ACTOR_NAME);\n        userTaskBuilder\n                .addUserFilter(\"test\", \"org.bonitasoft.engine.filter.user.testFilterWithAutoAssign\", PROCESS_VERSION)\n                .addInput(\"userId\",\n                        new ExpressionBuilder().createDataExpression(\"userIdValue\", Long.class.getName()));\n        userTaskBuilder.addData(\"userIdValue\", Long.class.getName(), null);\n        userTaskBuilder.addMultiInstance(false, loopDataInputName).addDataInputItemRef(\"userIdValue\");\n        userTaskBuilder.addDisplayName(new ExpressionBuilder().createConstantStringExpression(\"displayName\"));\n        builder.addAutomaticTask(\"step2\").addTransition(\"step1\", \"step2\");\n        final ProcessDefinition processDefinition = deployProcessWithTestFilter(ACTOR_NAME, builder);\n\n        // Start process, and wait all multiinstancied user tasks\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance, \"step1\");\n        waitForUserTask(processInstance, \"step1\");\n        waitForUserTask(processInstance, \"step1\");\n\n        // Get comments and check it\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.filter(SearchCommentsDescriptor.PROCESS_INSTANCE_ID, processInstance.getId());\n        searchOptionsBuilder.sort(SearchCommentsDescriptor.CONTENT, Order.ASC);\n        final List<Comment> comments = getProcessAPI().searchComments(searchOptionsBuilder.done()).getResult();\n        assertEquals(\"The task \\\"displayName\\\" is now assigned to \" + jack.getUserName(), comments.get(0).getContent());\n        assertEquals(\"The task \\\"displayName\\\" is now assigned to \" + jenny.getUserName(),\n                comments.get(1).getContent());\n        assertEquals(\"The task \\\"displayName\\\" is now assigned to \" + john.getUserName(), comments.get(2).getContent());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    private ProcessDefinition deployProcessWithTestFilter(final String actorName,\n            final ProcessDefinitionBuilder designProcessDefinition)\n            throws Exception {\n        final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(\n                        designProcessDefinition.done());\n        final List<BarResource> impl = generateFilterImplementations(\"TestFilterWithAutoAssign\");\n        for (final BarResource barResource : impl) {\n            businessArchiveBuilder.addUserFilters(barResource);\n        }\n        final List<BarResource> generateFilterDependencies = new ArrayList<>(1);\n        final byte[] data = IOUtil.generateJar(TestFilterWithAutoAssign.class);\n        generateFilterDependencies.add(new BarResource(\"TestFilterWithAutoAssign.jar\", data));\n\n        for (final BarResource barResource : generateFilterDependencies) {\n            businessArchiveBuilder.addClasspathResource(barResource);\n        }\n\n        final ProcessDefinition processDefinition = deployProcess(businessArchiveBuilder.done());\n        getProcessAPI().addUserToActor(actorName, processDefinition, john.getId());\n        getProcessAPI().addUserToActor(actorName, processDefinition, jack.getId());\n        getProcessAPI().addUserToActor(actorName, processDefinition, jenny.getId());\n        getProcessAPI().enableProcess(processDefinition.getId());\n        return processDefinition;\n    }\n\n    private List<BarResource> generateFilterImplementations(final String filterName) throws IOException {\n        final List<BarResource> resources = new ArrayList<>(1);\n        final InputStream inputStream = TestConnector.class.getClassLoader()\n                .getResourceAsStream(\"org/bonitasoft/engine/filter/user/\" + filterName + \".impl\");\n        final byte[] data = IOUtil.getAllContentFrom(inputStream);\n        inputStream.close();\n        resources.add(new BarResource(filterName + \".impl\", data));\n        return resources;\n    }\n\n    @Test\n    public void multiInstanceWithANullList_should_put_the_task_in_failed_state() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"multiInstanceWithAnEmptyList\", PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME);\n        builder.addData(\"list\", List.class.getName(), null);\n        final UserTaskDefinitionBuilder taskDefinitionBuilder = builder.addUserTask(\"step1\", ACTOR_NAME);\n        taskDefinitionBuilder.addData(\"listValue\", String.class.getName(), null);\n        taskDefinitionBuilder.addMultiInstance(true, \"list\").addDataInputItemRef(\"listValue\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForFlowNodeInFailedState(processInstance);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void should_not_create_instance_when_multi_instantiated_on_empty_list() throws Exception {\n        //given\n        ProcessDefinitionBuilder p1 = new ProcessDefinitionBuilder().createNewInstance(\"p1\", \"1\");\n        p1.addData(\"loop\", List.class.getName(),\n                new ExpressionBuilder().createGroovyScriptExpression(\"list\", \"[]\", List.class.getName()));\n        p1.addUserTask(\"step1\", \"john\").addMultiInstance(true, \"loop\");\n        p1.addUserTask(\"step3\", \"john\").addMultiInstance(false, \"loop\");\n        p1.addActor(\"john\");\n        p1.addUserTask(\"step2\", \"john\");\n        p1.addUserTask(\"step4\", \"john\");\n        p1.addTransition(\"step1\", \"step2\");\n        p1.addTransition(\"step3\", \"step4\");\n        ProcessDefinition deploy = getProcessAPI().deploy(p1.done());\n        List<ActorInstance> actors = getProcessAPI().getActors(deploy.getId(), 0, 1, ActorCriterion.NAME_ASC);\n        getProcessAPI().addUserToActor(actors.get(0).getId(), getIdentityAPI().getUserByUserName(\"john\").getId());\n        getProcessAPI().enableProcess(deploy.getId());\n        //when\n        ProcessInstance processInstance = getProcessAPI().startProcess(deploy.getId());\n        //no task step1\n        //then\n        waitForUserTask(processInstance.getId(), \"step2\");\n        waitForUserTask(processInstance.getId(), \"step4\");\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.filter(ArchivedHumanTaskInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID, processInstance.getId());\n        builder.leftParenthesis().filter(ArchivedHumanTaskInstanceSearchDescriptor.NAME, \"step1\").or()\n                .filter(ArchivedHumanTaskInstanceSearchDescriptor.NAME, \"step3\").rightParenthesis();\n        builder.filter(ArchivedHumanTaskInstanceSearchDescriptor.STATE_NAME, ActivityStates.COMPLETED_STATE);\n        SearchResult<ArchivedHumanTaskInstance> archivedHumanTaskInstanceSearchResult = getProcessAPI()\n                .searchArchivedHumanTasks(builder.done());\n        assertThat(archivedHumanTaskInstanceSearchResult.getResult()).isEmpty();\n        //clean up\n        disableAndDeleteProcess(deploy);\n    }\n\n    @Test\n    public void multiInstance_with_a_define_instance_number_should_be_able_to_save_value_into_a_data_output_list()\n            throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"multiInstanceProcess\", PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME);\n        builder.addData(\"list\", List.class.getName(),\n                new ExpressionBuilder().createGroovyScriptExpression(\"EmptyList\", \"[]\", List.class.getName()));\n        final UserTaskDefinitionBuilder taskDefinitionBuilder = builder.addUserTask(\"step1\", ACTOR_NAME);\n        taskDefinitionBuilder.addData(\"listValue\", String.class.getName(), null);\n        taskDefinitionBuilder.addMultiInstance(false, new ExpressionBuilder().createConstantIntegerExpression(1))\n                .addDataOutputItemRef(\"listValue\")\n                .addLoopDataOutputRef(\"list\");\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        long step1 = waitForUserTask(processInstance.getId(), \"step1\");\n        getProcessAPI().assignUserTask(step1, john.getId());\n        getProcessAPI().executeUserTask(john.getId(), step1, null);\n        waitForProcessToFinish(processInstance);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void should_abort_children_of_multi_instance_when_skipping_the_multi_instance() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"skippedMultiInstance\", PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME);\n        builder.addUserTask(\"step1\", ACTOR_NAME).addMultiInstance(false,\n                new ExpressionBuilder().createConstantIntegerExpression(2));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance, \"step1\");\n        HumanTaskInstance step1 = waitForUserTaskAndGetIt(processInstance, \"step1\");\n\n        FlowNodeInstance multiInstance = getProcessAPI().getFlowNodeInstance(step1.getParentContainerId());\n        assertThat(multiInstance.getType()).isEqualTo(FlowNodeType.MULTI_INSTANCE_ACTIVITY);\n        getProcessAPI().setActivityStateByName(multiInstance.getId(), ActivityStates.SKIPPED_STATE);\n\n        waitForProcessToFinish(processInstance);\n        SearchResult<ArchivedActivityInstance> archivedActivityInstance = getProcessAPI()\n                .searchArchivedActivities(new SearchOptionsBuilder(0, 10)\n                        .filter(ROOT_PROCESS_INSTANCE_ID, processInstance.getId())\n                        .filter(STATE_NAME, ABORTED.getStateName()).done());\n        assertThat(archivedActivityInstance.getResult()).hasSize(2);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void should_cancel_multi_instance_with_call_activity_and_boundary() throws Exception {\n        ProcessDefinition subProcessDefinition = deployAndEnableProcessWithActor(new ProcessDefinitionBuilder()\n                .createNewInstance(\"subProcess\", \"1.0\")\n                .addActor(\"actor\")\n                .addAutomaticTask(\"task1\")\n                .addUserTask(\"task2\", \"actor\")\n                .addTransition(\"task1\", \"task2\").getProcess(), \"actor\", user);\n\n        ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"multi+call+boundary\", \"1.0\");\n        CallActivityBuilder callActivityBuilder = processDefinitionBuilder\n                .addCallActivity(\"call\", new ExpressionBuilder().createConstantStringExpression(\"subProcess\"),\n                        new ExpressionBuilder().createConstantStringExpression(\"1.0\"));\n        callActivityBuilder.addMultiInstance(true, new ExpressionBuilder().createConstantIntegerExpression(10));\n        callActivityBuilder.addBoundaryEvent(\"boundary\").addSignalEventTrigger(\"signal\");\n        processDefinitionBuilder.addAutomaticTask(\"auto1\");\n        processDefinitionBuilder.addTransition(\"boundary\", \"auto1\");\n\n        ProcessDefinition processDefinition = deployAndEnableProcess(processDefinitionBuilder.getProcess());\n\n        ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        waitForUserTaskAndExecuteIt(\"task2\", user);\n        waitForUserTask(\"task2\");\n\n        getProcessAPI().cancelProcessInstance(processInstance.getId());\n\n        waitForProcessToBeInState(processInstance, ProcessInstanceState.CANCELLED);\n        await().until(() -> getProcessAPI().searchProcessInstances(new SearchOptionsBuilder(0, 100).done())\n                .getResult().isEmpty());\n\n        deleteProcessInstanceAndArchived(subProcessDefinition, processDefinition);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/activity/PendingTasksIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.activity;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.*;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Comparator;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.bonitasoft.engine.TestWithTechnicalUser;\nimport org.bonitasoft.engine.bpm.actor.ActorCriterion;\nimport org.bonitasoft.engine.bpm.actor.ActorInstance;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.TaskPriority;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder;\nimport org.bonitasoft.engine.connectors.VariableStorage;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.identity.Group;\nimport org.bonitasoft.engine.identity.Role;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.identity.UserMembership;\nimport org.bonitasoft.engine.identity.UserNotFoundException;\nimport org.bonitasoft.engine.identity.UserSearchDescriptor;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.springframework.util.CollectionUtils;\n\npublic class PendingTasksIT extends TestWithTechnicalUser {\n\n    private static final String JACK = \"jack\";\n\n    private User john;\n\n    private User jack;\n\n    @Override\n    @Before\n    public void before() throws Exception {\n        super.before();\n        john = createUser(USERNAME, PASSWORD);\n        jack = createUser(JACK, PASSWORD);\n    }\n\n    @Override\n    @After\n    public void after() throws Exception {\n        deleteUser(USERNAME);\n        deleteUser(JACK);\n        VariableStorage.clearAll();\n        super.after();\n    }\n\n    @Test\n    public void searchMyAvailableHumanTasks() throws Exception {\n        final User user = createUser(\"Barnabooth\", \"Strongwood\");\n        final User user2 = createUser(\"Unknown\", \"Stranger\");\n        final Group group = createGroup(\"un_used\");\n\n        // Process def with 2 instances:\n        final String otherActor = \"NotForMe\";\n        final ProcessDefinitionBuilder processBuilder1 = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process_with_branches\", \"1.0\");\n        processBuilder1.addActor(ACTOR_NAME).addActor(otherActor);\n        processBuilder1.addUserTask(\"step1\", ACTOR_NAME).addUserTask(\"step2\", ACTOR_NAME)\n                .addUserTask(\"step3\", otherActor).addUserTask(\"step4\", otherActor);\n        final ProcessDefinition processDefinition = deployProcess(\n                new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(processBuilder1.done())\n                        .done());\n        final List<ActorInstance> actors = getProcessAPI().getActors(processDefinition.getId(), 0, 2,\n                ActorCriterion.NAME_ASC);\n        getProcessAPI().addUserToActor(actors.get(0).getId(), user.getId());\n        getProcessAPI().addGroupToActor(actors.get(1).getId(), group.getId());\n        getProcessAPI().enableProcess(processDefinition.getId());\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinition.getId());\n\n        // Wait for tasks in READY state:\n        waitForUserTask(processInstance, \"step1\");\n        final long step2Id = waitForUserTask(processInstance, \"step2\");\n        final long step3Id = waitForUserTask(processInstance, \"step3\");\n        waitForUserTask(processInstance, \"step4\");\n\n        waitForUserTask(processInstance2, \"step1\");\n        waitForUserTask(processInstance2, \"step2\");\n        waitForUserTask(processInstance2, \"step3\");\n        waitForUserTask(processInstance2, \"step4\");\n\n        // 4 tasks should already be pending for me:\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID, Order.DESC);\n        SearchResult<HumanTaskInstance> humanTasksSearch = getProcessAPI().searchMyAvailableHumanTasks(user.getId(),\n                searchOptionsBuilder.done());\n        assertEquals(4, humanTasksSearch.getCount());\n\n        List<HumanTaskInstance> searchResult = humanTasksSearch.getResult();\n        assertThat(searchResult).extracting(HumanTaskInstance::getRootContainerId)\n                .isSortedAccordingTo(Comparator.reverseOrder());\n\n        // Force assigning 'task3' (DESC name sort) to me (event though I am not an actor for it):\n        getProcessAPI().assignUserTask(step3Id, user.getId());\n\n        // 5 tasks should now be available for me:\n        humanTasksSearch = getProcessAPI().searchMyAvailableHumanTasks(user.getId(), searchOptionsBuilder.done());\n        assertEquals(5, humanTasksSearch.getCount());\n\n        // Force assigning 'task2' (DESC name sort) to someone else than me (event though he is not an actor for it):\n        getProcessAPI().assignUserTask(step2Id, user2.getId());\n\n        // 4 tasks should now be available for me:\n        humanTasksSearch = getProcessAPI().searchMyAvailableHumanTasks(user.getId(), searchOptionsBuilder.done());\n        assertEquals(4, humanTasksSearch.getCount());\n\n        // 5 tasks should be available for me when calling searchPendingOrAssignedToUserOrTakenTasks\n        // because I can see tasks assigned to others\n        humanTasksSearch = getProcessAPI().searchPendingOrAssignedToUserOrAssignedToOthersTasks(user.getId(),\n                searchOptionsBuilder.done());\n        assertEquals(5, humanTasksSearch.getCount());\n\n        disableAndDeleteProcess(processDefinition);\n        deleteUsers(user, user2);\n        deleteGroups(group);\n    }\n\n    @Test\n    public void searchMyAvailableHumanTasksWithActorFilters() throws Exception {\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        final User user = createUser(\"Barnabooth\", \"Strongwood\");\n\n        //Build 2 processes with actor filters\n        final ProcessDefinitionBuilder jackDesignProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithUserFilter\", \"1.0\");\n        jackDesignProcessDefinition.addActor(ACTOR_NAME).addDescription(\"ACTOR_NAME all day and night long\");\n        jackDesignProcessDefinition.addAutomaticTask(\"step1\");\n        final UserTaskDefinitionBuilder addUserTask = jackDesignProcessDefinition.addUserTask(\"step2\", ACTOR_NAME);\n        addUserTask.addUserFilter(\"test\", \"org.bonitasoft.engine.filter.user.testFilter\", \"1.0\").addInput(\"userId\",\n                // filter selects \"jack\" as candidate:\n                new ExpressionBuilder().createConstantLongExpression(jack.getId()));\n        jackDesignProcessDefinition.addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinitionBuilder userDesignProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithUserFilter\", \"2.0\");\n        userDesignProcessDefinition.addActor(ACTOR_NAME).addDescription(\"ACTOR_NAME all day and night long\");\n        userDesignProcessDefinition.addAutomaticTask(\"step1\");\n        userDesignProcessDefinition.addUserTask(\"step2\", ACTOR_NAME)\n                .addUserFilter(\"test2\", \"org.bonitasoft.engine.filter.user.testFilter\", \"1.0\").addInput(\"userId\",\n                        // filter selects \"user\" as candidate:\n                        new ExpressionBuilder().createConstantLongExpression(user.getId()));\n        userDesignProcessDefinition.addTransition(\"step1\", \"step2\");\n\n        // Only john is member of the actor, for both processes:\n        final ProcessDefinition jackProcessDefinition = deployProcessWithTestFilter(jackDesignProcessDefinition,\n                ACTOR_NAME, john, \"TestFilter\");\n        final ProcessDefinition userProcessDefinition = deployProcessWithTestFilter(userDesignProcessDefinition,\n                ACTOR_NAME, john, \"TestFilter\");\n\n        // Start first process\n        ProcessInstance jackProcessInstance = getProcessAPI().startProcess(jackProcessDefinition.getId());\n        long stepIdProc1 = waitForUserTask(jackProcessInstance, \"step2\");\n\n        // actor filter allows jack to see it\n        SearchResult<HumanTaskInstance> humanTasksSearch = getProcessAPI()\n                .searchPendingOrAssignedToUserOrAssignedToOthersTasks(jack.getId(),\n                        searchOptionsBuilder.done());\n        assertEquals(1, humanTasksSearch.getCount());\n        // but not john\n        humanTasksSearch = getProcessAPI().searchPendingOrAssignedToUserOrAssignedToOthersTasks(john.getId(),\n                searchOptionsBuilder.done());\n        assertEquals(0, humanTasksSearch.getCount());\n\n        //john takes the task. This overrides the actor filter\n        getProcessAPI().assignUserTask(stepIdProc1, john.getId());\n        // jack still sees the task\n        humanTasksSearch = getProcessAPI().searchPendingOrAssignedToUserOrAssignedToOthersTasks(jack.getId(),\n                searchOptionsBuilder.done());\n        assertEquals(1, humanTasksSearch.getCount());\n        // john sees the task because he is now assigned to it\n        humanTasksSearch = getProcessAPI().searchPendingOrAssignedToUserOrAssignedToOthersTasks(john.getId(),\n                searchOptionsBuilder.done());\n        assertEquals(1, humanTasksSearch.getCount());\n\n        // assign jack\n        getProcessAPI().assignUserTask(stepIdProc1, jack.getId());\n        // jack sees the task\n        humanTasksSearch = getProcessAPI().searchPendingOrAssignedToUserOrAssignedToOthersTasks(jack.getId(),\n                searchOptionsBuilder.done());\n        assertEquals(1, humanTasksSearch.getCount());\n        // john does not see the task anymore\n        humanTasksSearch = getProcessAPI().searchPendingOrAssignedToUserOrAssignedToOthersTasks(john.getId(),\n                searchOptionsBuilder.done());\n        assertEquals(0, humanTasksSearch.getCount());\n\n        //start proc 2\n        ProcessInstance userProcessInstance = getProcessAPI().startProcess(userProcessDefinition.getId());\n        waitForUserTask(userProcessInstance, \"step2\");\n\n        // Proc2\n        // Non-actor user sees the task, because actor filter allows him specifically\n        humanTasksSearch = getProcessAPI().searchPendingOrAssignedToUserOrAssignedToOthersTasks(user.getId(),\n                searchOptionsBuilder.done());\n        assertEquals(1, humanTasksSearch.getCount());\n        // actor member does not see it, because actor filter does not allow him to\n        humanTasksSearch = getProcessAPI().searchPendingOrAssignedToUserOrAssignedToOthersTasks(john.getId(),\n                searchOptionsBuilder.done());\n        assertEquals(0, humanTasksSearch.getCount());\n\n        disableAndDeleteProcess(jackProcessDefinition);\n        disableAndDeleteProcess(userProcessDefinition);\n        deleteUser(user);\n    }\n\n    @Test\n    public void getPendingHumanTaskInstancesInTwoProcesses() throws Exception {\n        // 1. create a user 'test'\n        User test;\n        try {\n            test = getIdentityAPI().getUserByUserName(JACK);\n        } catch (final UserNotFoundException e) {\n            test = getIdentityAPI().createUser(JACK, PASSWORD);\n        }\n        final long userId = test.getId();\n        // 2. install two processes\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(\"firstProcess\",\n                \"1.0\");\n        processBuilder.addActor(\"myActor\");\n        processBuilder.addUserTask(\"Request\", \"myActor\");\n        processBuilder.addUserTask(\"Approval\", \"myActor\");\n        processBuilder.addTransition(\"Request\", \"Approval\");\n        final DesignProcessDefinition designProcessDefinition = processBuilder.done();\n        final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive();\n        final BusinessArchive businessArchive = businessArchiveBuilder.setProcessDefinition(designProcessDefinition)\n                .done();\n\n        final ProcessDefinitionBuilder processBuilder2 = new ProcessDefinitionBuilder()\n                .createNewInstance(\"SecontProcess\", \"1.0\");\n        processBuilder2.addActor(\"myActor\").addUserTask(\"Request\", \"myActor\").addUserTask(\"Approval\", \"myActor\")\n                .addTransition(\"Request\", \"Approval\");\n        final DesignProcessDefinition designProcessDefinition2 = processBuilder2.done();\n        final BusinessArchive businessArchive2 = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(designProcessDefinition2).done();\n\n        final ProcessDefinition processDefinition1 = deployProcess(businessArchive);\n        final ProcessDefinition processDefinition2 = deployProcess(businessArchive2);\n        // 3. assign user 'test' to actor of both processes\n        ActorInstance processActor = getProcessAPI()\n                .getActors(processDefinition1.getId(), 0, 1, ActorCriterion.NAME_ASC).get(0);\n        getProcessAPI().addUserToActor(processActor.getId(), test.getId());\n        processActor = getProcessAPI().getActors(processDefinition2.getId(), 0, 1, ActorCriterion.NAME_ASC).get(0);\n        getProcessAPI().addUserToActor(processActor.getId(), test.getId());\n        // 4. enable both processes\n        getProcessAPI().enableProcess(processDefinition1.getId());\n        getProcessAPI().enableProcess(processDefinition2.getId());\n        // 5. start both processes\n        final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition1.getId());\n        waitForUserTask(processInstance1, \"Request\");\n        final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinition2.getId());\n        waitForUserTask(processInstance2, \"Request\");\n        // 6.check Pending tasks list. The below exception appears.\n        final List<HumanTaskInstance> userTaskInstances = getProcessAPI().getPendingHumanTaskInstances(userId, 0, 10,\n                ActivityInstanceCriterion.NAME_ASC);\n        assertNotNull(userTaskInstances);\n        assertEquals(2, userTaskInstances.size());\n\n        // Clean up\n        deleteUser(test);\n        disableAndDeleteProcess(processDefinition1, processDefinition2);\n    }\n\n    @Test\n    public void getPendingHumanTaskInstancePriorityAndExpectedEndDate() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        final TaskPriority priority = TaskPriority.HIGHEST;\n        final int oneDay = 24 * 60 * 60 * 1000;\n        processBuilder.addActor(ACTOR_NAME).addDescription(\"Delivery all day and night long\")\n                .addUserTask(\"deliver\", ACTOR_NAME).addPriority(priority.name())\n                .addExpectedDuration(oneDay);\n        final DesignProcessDefinition processDesignDefinition = processBuilder.done();\n\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(processDesignDefinition).done();\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchive, ACTOR_NAME, john);\n        final Date before = new Date();\n        Thread.sleep(20);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance, \"deliver\");\n        final List<HumanTaskInstance> activityInstances = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0,\n                10, ActivityInstanceCriterion.DEFAULT);\n        Thread.sleep(20);\n        final Date after = new Date();\n        assertEquals(1, activityInstances.size());\n        final HumanTaskInstance humanTaskInstance = activityInstances.get(0);\n        assertEquals(priority, humanTaskInstance.getPriority());\n        final long time = humanTaskInstance.getExpectedEndDate().getTime();\n        assertTrue(before.getTime() + oneDay < time && time < after.getTime() + oneDay);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void taskAlreadyClaimedByMe() throws Exception {\n        final User user = createUser(\"login\", \"password\");\n        final ProcessDefinition processDefinition = deployProcessWithUserTask(user);\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId());\n        final long taskId = waitForUserTask(startProcess, \"Request\");\n        getProcessAPI().assignUserTask(taskId, user.getId());\n        getProcessAPI().assignUserTask(taskId, user.getId());\n        assertEquals(1, getProcessAPI()\n                .getAssignedHumanTaskInstances(user.getId(), 0, 10, ActivityInstanceCriterion.DEFAULT).size());\n        deleteUser(user);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void taskAlreadyClaimedByOther() throws Exception {\n        final User user1 = createUser(\"login1\", \"password\");\n        final User user2 = createUser(\"login2\", \"password\");\n        final ProcessDefinition processDefinition = deployProcessWithUserTask(user1);\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId());\n        final long taskId = waitForUserTask(startProcess, \"Request\");\n        getProcessAPI().assignUserTask(taskId, user1.getId());\n        getProcessAPI().assignUserTask(taskId, user2.getId());\n        logout();\n        loginOnDefaultTenantWith(\"login1\", \"password\");\n        assertEquals(1, getProcessAPI()\n                .getAssignedHumanTaskInstances(user2.getId(), 0, 10, ActivityInstanceCriterion.DEFAULT).size());\n        assertEquals(0, getProcessAPI()\n                .getAssignedHumanTaskInstances(user1.getId(), 0, 10, ActivityInstanceCriterion.DEFAULT).size());\n        deleteUser(user1);\n        deleteUser(user2);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void searchTasksWithSpecialCharsInFreeText() throws Exception {\n        final User user = createUser(\"login\", \"password\");\n        final String taskName = \"étape\";\n        final String processName = \"никола\";\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(processName,\n                \"1\");\n        processBuilder.addActor(\"myActor\");\n        processBuilder.addUserTask(taskName, \"myActor\");\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.done(), \"myActor\",\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance, taskName);\n\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.searchTerm(taskName);\n        final SearchResult<HumanTaskInstance> taskRes = getProcessAPI().searchHumanTaskInstances(builder.done());\n        assertEquals(1, taskRes.getCount());\n        assertEquals(taskName, taskRes.getResult().get(0).getName());\n\n        builder.searchTerm(processName);\n        final SearchResult<ProcessDeploymentInfo> procDefRes = getProcessAPI()\n                .searchProcessDeploymentInfos(builder.done());\n        assertEquals(1, procDefRes.getCount());\n        assertEquals(processName, procDefRes.getResult().get(0).getName());\n\n        deleteUser(user);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    private ProcessDefinition deployProcessWithUserTask(final User user1) throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(\"firstProcess\",\n                \"1.0\");\n        processBuilder.addActor(\"myActor\");\n        processBuilder.addUserTask(\"Request\", \"myActor\");\n        return deployAndEnableProcessWithActor(processBuilder.done(), \"myActor\", user1);\n    }\n\n    @Test\n    public void taskAlreadyReleased() throws Exception {\n        final User user = createUser(\"login1\", \"password\");\n        final ProcessDefinition processDefinition = deployProcessWithUserTask(user);\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId());\n        final long taskId = waitForUserTask(startProcess, \"Request\");\n        getProcessAPI().assignUserTask(taskId, user.getId());\n        getProcessAPI().releaseUserTask(taskId);\n        getProcessAPI().releaseUserTask(taskId);\n        deleteUser(user);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void actorMappedToGroup() throws Exception {\n        final Group mainGroup = createGroup(\"main\");\n        final Role member = createRole(\"member\");\n        final UserMembership m1 = getIdentityAPI().addUserMembership(john.getId(), mainGroup.getId(), member.getId());\n        final UserMembership m2 = getIdentityAPI().addUserMembership(jack.getId(), mainGroup.getId(), member.getId());\n\n        final ProcessDefinition processDefinition = deployProcessMappedToGroup(mainGroup);\n        getProcessAPI().startProcess(processDefinition.getId());\n        waitForPendingTasks(john.getId(), 1);\n        waitForPendingTasks(jack.getId(), 1);\n\n        disableAndDeleteProcess(processDefinition);\n        deleteUserMembership(m1.getId());\n        deleteUserMembership(m2.getId());\n        deleteGroups(mainGroup);\n        deleteRoles(member);\n    }\n\n    @Test\n    public void actorMappedToDifferrentGroup() throws Exception {\n        final Group mainGroup = createGroup(\"main\");\n        final Group secondGroup = createGroup(\"second\");\n        final Role member = createRole(\"member\");\n        final UserMembership m1 = getIdentityAPI().addUserMembership(john.getId(), mainGroup.getId(), member.getId());\n        final UserMembership m2 = getIdentityAPI().addUserMembership(jack.getId(), secondGroup.getId(), member.getId());\n\n        final ProcessDefinition processDefinition = deployProcessMappedToGroup(mainGroup);\n        getProcessAPI().startProcess(processDefinition.getId());\n        waitForPendingTasks(john.getId(), 1);\n        final List<HumanTaskInstance> pendingHumanTaskInstances = getProcessAPI()\n                .getPendingHumanTaskInstances(jack.getId(), 0, 10, null);\n        assertTrue(pendingHumanTaskInstances.isEmpty());\n\n        disableAndDeleteProcess(processDefinition);\n        deleteUserMembership(m1.getId());\n        deleteUserMembership(m2.getId());\n        deleteGroups(secondGroup, mainGroup);\n        deleteRoles(member);\n    }\n\n    @Test\n    public void actorMappedToGrandChildGroup() throws Exception {\n        final Group mainGroup = createGroup(\"main\");\n        final Group childGroup = createGroup(\"child\", \"/main\");\n        final Group grandChildGroup = createGroup(\"gChild\", \"/main/child\");\n        assertEquals(childGroup.getPath(), grandChildGroup.getParentPath());\n        assertEquals(mainGroup.getPath(), childGroup.getParentPath());\n        final Role member = createRole(\"member\");\n        final UserMembership m1 = getIdentityAPI().addUserMembership(john.getId(), mainGroup.getId(), member.getId());\n        final UserMembership m2 = getIdentityAPI().addUserMembership(jack.getId(), grandChildGroup.getId(),\n                member.getId());\n\n        final ProcessDefinition processDefinition = deployProcessMappedToGroup(mainGroup);\n        try {\n            getProcessAPI().startProcess(processDefinition.getId());\n            waitForPendingTasks(john.getId(), 1);\n            waitForPendingTasks(jack.getId(), 1);\n        } finally {\n            disableAndDeleteProcess(processDefinition);\n            deleteUserMembership(m1.getId());\n            deleteUserMembership(m2.getId());\n            deleteGroups(grandChildGroup, childGroup, mainGroup);\n            deleteRoles(member);\n        }\n    }\n\n    @Test\n    public void actorMappedToChildGroup() throws Exception {\n        final Group mainGroup = createGroup(\"main\");\n        final Group secondGroup = createGroup(\"second\", \"/main\");\n        final Role member = createRole(\"member\");\n        final UserMembership m1 = getIdentityAPI().addUserMembership(john.getId(), mainGroup.getId(), member.getId());\n        final UserMembership m2 = getIdentityAPI().addUserMembership(jack.getId(), secondGroup.getId(), member.getId());\n\n        final ProcessDefinition processDefinition = deployProcessMappedToGroup(mainGroup);\n        try {\n            getProcessAPI().startProcess(processDefinition.getId());\n            waitForPendingTasks(john.getId(), 1);\n            waitForPendingTasks(jack.getId(), 1);\n        } finally {\n            disableAndDeleteProcess(processDefinition);\n            deleteUserMembership(m1.getId());\n            deleteUserMembership(m2.getId());\n            deleteGroups(secondGroup, mainGroup);\n            deleteRoles(member);\n        }\n    }\n\n    private ProcessDefinition deployProcessMappedToGroup(final Group mainGroup) throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(\"firstProcess\",\n                \"1.0\");\n        processBuilder.addActor(\"myActor\");\n        processBuilder.addUserTask(\"Request\", \"myActor\");\n        final DesignProcessDefinition designProcessDefinition = processBuilder.done();\n        final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive();\n        final BusinessArchive businessArchive = businessArchiveBuilder.setProcessDefinition(designProcessDefinition)\n                .done();\n\n        final ProcessDefinition processDefinition = deployProcess(businessArchive);\n        // map actor to group\n        final ActorInstance processActor = getProcessAPI()\n                .getActors(processDefinition.getId(), 0, 1, ActorCriterion.NAME_ASC).get(0);\n        getProcessAPI().addGroupToActor(processActor.getId(), mainGroup.getId());\n        getProcessAPI().enableProcess(processDefinition.getId());\n        return processDefinition;\n    }\n\n    @Test\n    public void searchPossibleUsersOfTaskUserActor() throws Exception {\n        loginOnDefaultTenantWith(USERNAME, PASSWORD);\n        final Group group = createGroup(\"group\");\n        final Role role = createRole(\"role\");\n        final User jaakko = createUser(\"jaakko\", PASSWORD);\n        createUserMembership(jaakko.getUserName(), role.getName(), group.getPath());\n\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"assign\", \"5.0\");\n        designProcessDefinition.addActor(\"acme\");\n        designProcessDefinition.addUserTask(\"step1\", \"acme\");\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(),\n                \"acme\", jaakko);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.sort(UserSearchDescriptor.LAST_NAME, Order.DESC);\n\n        final List<User> possibleUsers = getProcessAPI()\n                .searchUsersWhoCanExecutePendingHumanTask(step1Id, builder.done()).getResult();\n        assertEquals(1, possibleUsers.size());\n        assertEquals(jaakko, possibleUsers.get(0));\n\n        // cleanup:\n        deleteGroups(group);\n        deleteRoles(role);\n        deleteUser(jaakko);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void searchPossibleUsersOfTaskUserActorWithoutMembership() throws Exception {\n        loginOnDefaultTenantWith(USERNAME, PASSWORD);\n        final Group group = createGroup(\"group\");\n        final Role role = createRole(\"role\");\n        final User jaakko = createUser(\"jaakko\", PASSWORD);\n        // createUserMembership(jack.getUserName(), role.getName(), group.getName());\n\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"assign\", \"5.0\");\n        designProcessDefinition.addActor(\"acme\");\n        designProcessDefinition.addUserTask(\"step1\", \"acme\");\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(),\n                \"acme\", jaakko);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.sort(UserSearchDescriptor.LAST_NAME, Order.DESC);\n\n        final SearchResult<User> searchResult = getProcessAPI().searchUsersWhoCanExecutePendingHumanTask(step1Id,\n                builder.done());\n        assertEquals(1, searchResult.getCount());\n        final List<User> possibleUsers = searchResult.getResult();\n        assertEquals(1, possibleUsers.size());\n        assertEquals(jaakko, possibleUsers.get(0));\n\n        // cleanup:\n        deleteGroups(group);\n        deleteRoles(role);\n        deleteUser(jaakko);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void searchPossibleUsersOfTaskRoleActor() throws Exception {\n        loginOnDefaultTenantWith(USERNAME, PASSWORD);\n        final Group group = createGroup(\"group\");\n        final Role role = createRole(\"role\");\n        final User jaakko = createUser(\"jaakko\", PASSWORD);\n        createUserMembership(jaakko.getUserName(), role.getName(), group.getPath());\n\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"assign\", \"5.0\");\n        designProcessDefinition.addActor(\"acme\");\n        designProcessDefinition.addUserTask(\"step1\", \"acme\");\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(),\n                \"acme\", role);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.sort(UserSearchDescriptor.LAST_NAME, Order.DESC);\n\n        final SearchResult<User> searchResult = getProcessAPI().searchUsersWhoCanExecutePendingHumanTask(step1Id,\n                builder.done());\n        assertEquals(1, searchResult.getCount());\n        final List<User> possibleUsers = searchResult.getResult();\n        assertEquals(1, possibleUsers.size());\n        assertEquals(jaakko, possibleUsers.get(0));\n\n        // cleanup:\n        deleteGroups(group);\n        deleteRoles(role);\n        deleteUser(jaakko);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void searchPossibleUsersOfTaskGroupActor() throws Exception {\n        loginOnDefaultTenantWith(USERNAME, PASSWORD);\n        final Group group = createGroup(\"group\");\n        final Role role = createRole(\"role\");\n        final User jaakko = createUser(\"jaakko\", PASSWORD);\n        createUserMembership(jaakko.getUserName(), role.getName(), group.getPath());\n\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"assign\", \"5.0\");\n        designProcessDefinition.addActor(\"acme\");\n        designProcessDefinition.addUserTask(\"step1\", \"acme\");\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(),\n                \"acme\", group);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.sort(UserSearchDescriptor.LAST_NAME, Order.DESC);\n\n        final SearchResult<User> searchResult = getProcessAPI().searchUsersWhoCanExecutePendingHumanTask(step1Id,\n                builder.done());\n        assertEquals(1, searchResult.getCount());\n        final List<User> possibleUsers = searchResult.getResult();\n        assertEquals(1, possibleUsers.size());\n        assertEquals(jaakko, possibleUsers.get(0));\n\n        // cleanup:\n        deleteGroups(group);\n        deleteRoles(role);\n        deleteUser(jaakko);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void searchPossibleUsersOfTaskShouldReturnAllUsersInThePaginationRange() throws Exception {\n        loginOnDefaultTenantWith(USERNAME, PASSWORD);\n        final Group group = createGroup(\"group\");\n        final Role role = createRole(\"role\");\n        final int USER_LIST_SIZE = 21;\n        final List<User> users = new ArrayList<>(USER_LIST_SIZE);\n        for (int i = 0; i < USER_LIST_SIZE; i++) {\n            final User newUser = createUser(\"user_\" + i, \"pwd\");\n            users.add(newUser);\n            createUserMembership(newUser.getUserName(), role.getName(), group.getName());\n        }\n\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"getPossible_pagination\", \"1.1\");\n        final String activityName = \"step1\";\n        designProcessDefinition.addActor(ACTOR_NAME);\n        designProcessDefinition.addUserTask(activityName, ACTOR_NAME);\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(),\n                ACTOR_NAME, users);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(processInstance, activityName);\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 30);\n        builder.sort(UserSearchDescriptor.LAST_NAME, Order.DESC);\n\n        final SearchResult<User> searchResult = getProcessAPI().searchUsersWhoCanExecutePendingHumanTask(step1Id,\n                builder.done());\n        assertEquals(21, searchResult.getCount());\n        final List<User> possibleUsers = searchResult.getResult();\n        // make sure the list is not limited to 20:\n        assertEquals(21, possibleUsers.size());\n\n        // cleanup:\n        deleteGroups(group);\n        deleteRoles(role);\n        deleteUsers(users);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void searchPossibleUsersOfTaskSubGroupActor() throws Exception {\n        loginOnDefaultTenantWith(USERNAME, PASSWORD);\n        final Group group = createGroup(\"group\");\n        final Group group2 = createGroup(\"gr\", group.getPath());\n        final Role role = createRole(\"role\");\n        final User jaakko = createUser(\"jaakko\", PASSWORD);\n        createUserMembership(jack.getUserName(), role.getName(), group.getPath());\n        createUserMembership(jaakko.getUserName(), role.getName(), group2.getPath());\n\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"assign\", \"5.0\");\n        designProcessDefinition.addActor(\"acme\");\n        designProcessDefinition.addUserTask(\"step1\", \"acme\");\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(),\n                \"acme\", group);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n\n        SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.sort(UserSearchDescriptor.USER_NAME, Order.ASC);\n\n        SearchResult<User> searchResult = getProcessAPI().searchUsersWhoCanExecutePendingHumanTask(step1Id,\n                builder.done());\n        assertEquals(2, searchResult.getCount());\n        List<User> possibleUsers = searchResult.getResult();\n\n        assertEquals(2, possibleUsers.size());\n        assertEquals(jaakko, possibleUsers.get(0));\n        assertEquals(jack, possibleUsers.get(1));\n\n        builder = new SearchOptionsBuilder(1, 10);\n        builder.sort(UserSearchDescriptor.USER_NAME, Order.ASC);\n\n        searchResult = getProcessAPI().searchUsersWhoCanExecutePendingHumanTask(step1Id, builder.done());\n        assertEquals(2, searchResult.getCount());\n        possibleUsers = searchResult.getResult();\n        assertEquals(1, possibleUsers.size());\n        assertEquals(jack, possibleUsers.get(0));\n\n        // cleanup:\n        deleteGroups(group);\n        deleteRoles(role);\n        deleteUser(jaakko);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void searchPossibleUsersOfFilteredTask() throws Exception {\n        loginOnDefaultTenantWith(USERNAME, PASSWORD);\n        final Group group = createGroup(\"group\");\n        final Group group2 = createGroup(\"gr\", group.getPath());\n        final Role role = createRole(\"role\");\n        final User jaakko = createUser(\"jaakko\", PASSWORD);\n        createUserMembership(jaakko.getUserName(), role.getName(), group2.getPath());\n        createUserMembership(jack.getUserName(), role.getName(), group2.getPath());\n\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"assign\", \"5.2\");\n        designProcessDefinition.addActor(\"acme\");\n        final UserTaskDefinitionBuilder taskDefinitionBuilder = designProcessDefinition.addUserTask(\"step1\", \"acme\");\n        taskDefinitionBuilder.addUserFilter(\"test\", \"org.bonitasoft.engine.filter.user.testFilter\", \"1.0\").addInput(\n                \"userId\",\n                new ExpressionBuilder().createConstantLongExpression(jaakko.getId()));\n        final UserTaskDefinitionBuilder definitionBuilder = designProcessDefinition.addUserTask(\"step2\", \"acme\");\n        definitionBuilder.addUserFilter(\"test\", \"org.bonitasoft.engine.filter.user.testFilter\", \"1.0\").addInput(\n                \"userId\",\n                new ExpressionBuilder().createConstantLongExpression(jack.getId()));\n        final ProcessDefinition processDefinition = deployProcessWithTestFilter(designProcessDefinition, \"acme\", jaakko,\n                \"TestFilter\");\n        getProcessAPI().addUserToActor(\"acme\", processDefinition, jack.getId());\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n        waitForUserTask(processInstance, \"step2\");\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 2);\n        builder.sort(UserSearchDescriptor.LAST_NAME, Order.DESC);\n\n        final SearchResult<User> searchResult = getProcessAPI().searchUsersWhoCanExecutePendingHumanTask(step1Id,\n                builder.done());\n        assertEquals(1, searchResult.getCount());\n        final List<User> possibleUsers = searchResult.getResult();\n\n        assertEquals(1, possibleUsers.size());\n        assertEquals(jaakko, possibleUsers.get(0));\n\n        // cleanup:\n        deleteGroups(group);\n        deleteRoles(role);\n        deleteUser(jaakko);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void searchPossibleUsersShouldReturnThoseStartingWithSearchedNamed() throws Exception {\n        loginOnDefaultTenantWith(USERNAME, PASSWORD);\n        final Group group = createGroup(\"group\");\n        final Group group2 = createGroup(\"gr\", group.getPath());\n        final Role role = createRole(\"role\");\n        final User jaakko = createUser(\"jaakko\", PASSWORD);\n        createUserMembership(jack.getUserName(), role.getName(), group.getPath());\n        createUserMembership(jaakko.getUserName(), role.getName(), group2.getPath());\n\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"assign\", \"5.0\");\n        designProcessDefinition.addActor(\"acme\");\n        designProcessDefinition.addUserTask(\"step1\", \"acme\");\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(),\n                \"acme\", group);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n\n        SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.sort(UserSearchDescriptor.USER_NAME, Order.ASC);\n        builder.searchTerm(\"jac\");\n\n        SearchResult<User> searchResult = getProcessAPI().searchUsersWhoCanExecutePendingHumanTask(step1Id,\n                builder.done());\n        assertEquals(1, searchResult.getCount());\n        List<User> possibleUsers = searchResult.getResult();\n\n        assertEquals(1, possibleUsers.size());\n        assertEquals(jack, possibleUsers.get(0));\n\n        builder = new SearchOptionsBuilder(0, 1);\n        builder.sort(UserSearchDescriptor.USER_NAME, Order.ASC);\n        builder.searchTerm(\"jaa\");\n\n        searchResult = getProcessAPI().searchUsersWhoCanExecutePendingHumanTask(step1Id, builder.done());\n        assertEquals(1, searchResult.getCount());\n        possibleUsers = searchResult.getResult();\n        assertEquals(1, possibleUsers.size());\n        assertEquals(jaakko, possibleUsers.get(0));\n\n        // cleanup:\n        deleteGroups(group);\n        deleteRoles(role);\n        deleteUser(jaakko);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getPossibleUsersOfUnknownTask() throws Exception {\n        loginOnDefaultTenantWith(USERNAME, PASSWORD);\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.sort(UserSearchDescriptor.LAST_NAME, Order.DESC);\n\n        final SearchResult<User> searchResult = getProcessAPI().searchUsersWhoCanExecutePendingHumanTask(-156L,\n                builder.done());\n        assertEquals(0, searchResult.getCount());\n        final List<User> possibleUsers = searchResult.getResult();\n        assertTrue(CollectionUtils.isEmpty(possibleUsers));\n    }\n\n    @Test\n    public void test() throws Exception {\n        final Group mainGroup = createGroup(\"main\");\n        final Role member = createRole(\"member\");\n        final UserMembership m1 = getIdentityAPI().addUserMembership(john.getId(), mainGroup.getId(), member.getId());\n\n        final ProcessDefinition processDefinition = deployProcessMappedToGroup(mainGroup);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance.getId(), \"Request\");\n\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(startProcess, \"Request\", john);\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"secondProcess\", \"1.0\");\n        processBuilder.addActor(\"myActor\").addActor(\"myActor2\");\n        processBuilder.addUserTask(\"Request1\", \"myActor2\");\n        processBuilder.addUserTask(\"Request2\", \"myActor\");\n        processBuilder.addTransition(\"Request2\", \"Request1\");\n\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(processBuilder.done(),\n                Arrays.asList(\"myActor\", \"myActor2\"),\n                Arrays.asList(john, jack));\n        final ProcessInstance instance = getProcessAPI().startProcess(definition.getId());\n        waitForUserTaskAndAssignIt(instance, \"Request2\", john);\n\n        List<HumanTaskInstance> pendingHumanTaskInstances = getProcessAPI().getPendingHumanTaskInstances(john.getId(),\n                0, 10, null);\n        assertThat(pendingHumanTaskInstances).hasSize(1);\n\n        getProcessAPI().disableProcess(processDefinition.getId());\n        pendingHumanTaskInstances = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null);\n        assertThat(pendingHumanTaskInstances).hasSize(1);\n\n        disableAndDeleteProcess(definition);\n        deleteProcess(processDefinition);\n        deleteUserMembership(m1.getId());\n        deleteGroups(mainGroup);\n        deleteRoles(member);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/activity/ReceiveTasksIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.activity;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\nimport java.io.Serializable;\nimport java.util.*;\nimport java.util.Map.Entry;\n\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.actor.ActorCriterion;\nimport org.bonitasoft.engine.bpm.actor.ActorInstance;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.data.DataInstance;\nimport org.bonitasoft.engine.bpm.flownode.*;\nimport org.bonitasoft.engine.bpm.process.*;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.ReceiveTaskDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.ThrowMessageEventTriggerBuilder;\nimport org.bonitasoft.engine.bpm.supervisor.ProcessSupervisorSearchDescriptor;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.expression.ExpressionType;\nimport org.bonitasoft.engine.expression.InvalidExpressionException;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.operation.*;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.junit.Test;\n\n/**\n * @author Julien Molinaro\n */\npublic class ReceiveTasksIT extends TestWithUser {\n\n    private static final String SEARCH_WAITING_EVENTS_COMMAND = \"searchWaitingEventsCommand\";\n\n    private static final String SEARCH_OPTIONS_KEY = \"searchOptions\";\n\n    /*\n     * 1 receiveProcess, no message sent\n     * dynamic -> deployAndEnable(receiveProcess), startProcess(receiveProcess)\n     * checks : receiveProcess wait on receive task and don't and halt on the user task.\n     */\n    @SuppressWarnings(\"unchecked\")\n    @Test\n    public void noMessageSentSoReceiveProcessIsWaiting() throws Exception {\n        final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithReceivedTask(\"receiveMessageProcess\",\n                \"waitForMessage\", \"userTask1\",\n                \"delivery\", user, \"m1\", null, null, null);\n\n        final ProcessInstance receiveMessageProcessInstance = getProcessAPI()\n                .startProcess(receiveMessageProcess.getId());\n        waitForFlowNodeInState(receiveMessageProcessInstance, \"waitForMessage\", TestStates.WAITING, true);\n\n        // we check after that the waiting event is still here\n\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.filter(WaitingEventSearchDescriptor.ROOT_PROCESS_INSTANCE_ID,\n                receiveMessageProcessInstance.getId());\n\n        final Map<String, Serializable> parameters = new HashMap<>(1);\n        parameters.put(SEARCH_OPTIONS_KEY, searchOptionsBuilder.done());\n\n        final SearchResult<WaitingEvent> searchResult = (SearchResult<WaitingEvent>) getCommandAPI()\n                .execute(SEARCH_WAITING_EVENTS_COMMAND, parameters);\n        assertEquals(1, searchResult.getCount());\n\n        disableAndDeleteProcess(receiveMessageProcess);\n    }\n\n    /*\n     * 1 receiveProcess, 1 sendProcess, Message goes from EndEvent to ReceiveTask\n     * dynamic -> deployAndEnable(sendProcess), deployAndEnable(receiveProcess), startProcess(receiveProcess),\n     * startProcess(sendProcess)\n     * checks : receiveProcess wait on receive task, sendProcess is finished, receiveProcess continue and halt on the\n     * user task, receive task is archived\n     */\n    @SuppressWarnings(\"unchecked\")\n    @Test\n    public void receiveMessageSentAfterReceiveProcessIsWaiting() throws Exception {\n        final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithEndMessageEvent(\"sendMessageProcess\",\n                \"m2\", \"receiveMessageProcess\",\n                \"waitForMessage\", null, null, null, null);\n        final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithReceivedTask(\"receiveMessageProcess\",\n                \"waitForMessage\", \"userTask1\",\n                \"delivery\", user, \"m2\", null, null, null);\n\n        final ProcessInstance receiveMessageProcessInstance = getProcessAPI()\n                .startProcess(receiveMessageProcess.getId());\n        waitForFlowNodeInState(receiveMessageProcessInstance, \"waitForMessage\", TestStates.WAITING, true);\n\n        SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.filter(WaitingEventSearchDescriptor.ROOT_PROCESS_INSTANCE_ID,\n                receiveMessageProcessInstance.getId());\n\n        final Map<String, Serializable> parameters = new HashMap<>(1);\n        parameters.put(SEARCH_OPTIONS_KEY, searchOptionsBuilder.done());\n\n        SearchResult<WaitingEvent> searchResult = (SearchResult<WaitingEvent>) getCommandAPI()\n                .execute(SEARCH_WAITING_EVENTS_COMMAND, parameters);\n        assertEquals(1, searchResult.getCount());\n\n        final ProcessInstance sendMessageProcessInstance = getProcessAPI().startProcess(sendMessageProcess.getId());\n        waitForProcessToFinish(sendMessageProcessInstance);\n        waitForUserTask(receiveMessageProcessInstance, \"userTask1\");\n\n        searchResult = (SearchResult<WaitingEvent>) getCommandAPI().execute(SEARCH_WAITING_EVENTS_COMMAND, parameters);\n        assertEquals(0, searchResult.getCount());\n\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 20);\n        searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID,\n                receiveMessageProcessInstance.getId());\n        searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.RECEIVE_TASK);\n        final SearchResult<ArchivedActivityInstance> archivedActivityInstancesSearch = getProcessAPI()\n                .searchArchivedActivities(searchOptionsBuilder.done());\n        assertEquals(1, archivedActivityInstancesSearch.getCount());\n        assertTrue(archivedActivityInstancesSearch.getResult().get(0) instanceof ArchivedReceiveTaskInstance);\n\n        disableAndDeleteProcess(sendMessageProcess);\n        disableAndDeleteProcess(receiveMessageProcess);\n    }\n\n    /*\n     * Verify receiveProcess receive message targeting it, even if sent before its existence.\n     * 1 receiveProcess, 1 sendProcess, Message goes from EndEvent to ReceiveTask\n     * dynamic -> deployAndEnable(sendProcess), startProcess(sendProcess), deployAndEnable(receiveProcess),\n     * startProcess(receiveProcess)\n     * checks : sendProcess is finished, receiveProcess goes through receive task (found message sent by sendProcess)\n     * and reaches user task.\n     */\n    @Test\n    public void receiveMessageSentBeforeReceiveProcessIsEnabled() throws Exception {\n        final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithEndMessageEvent(\"sendMessageProcess\",\n                \"m3\", \"receiveMessageProcess\",\n                \"waitForMessage\", null, null, null, null);\n\n        final ProcessInstance sendMessageProcessInstance = getProcessAPI().startProcess(sendMessageProcess.getId());\n        waitForProcessToFinish(sendMessageProcessInstance);\n\n        final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithReceivedTask(\"receiveMessageProcess\",\n                \"waitForMessage\", \"userTask1\",\n                \"delivery\", user, \"m3\", null, null, null);\n\n        final ProcessInstance receiveMessageProcessInstance = getProcessAPI()\n                .startProcess(receiveMessageProcess.getId());\n\n        // No need to verify anything, if no exception, query exists\n        getProcessAPI().searchActivities(\n                new SearchOptionsBuilder(0, 10)\n                        .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID,\n                                receiveMessageProcessInstance.getId())\n                        .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.RECEIVE_TASK).done());\n\n        getProcessAPI().searchActivities(\n                new SearchOptionsBuilder(0, 10)\n                        .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID,\n                                receiveMessageProcessInstance.getId())\n                        .filter(ProcessSupervisorSearchDescriptor.USER_ID, user.getId()).done());\n\n        getProcessAPI().searchActivities(\n                new SearchOptionsBuilder(0, 10)\n                        .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.RECEIVE_TASK)\n                        .filter(ProcessSupervisorSearchDescriptor.USER_ID, user.getId()).done());\n\n        waitForUserTask(receiveMessageProcessInstance, \"userTask1\");\n\n        disableAndDeleteProcess(sendMessageProcess);\n        disableAndDeleteProcess(receiveMessageProcess);\n    }\n\n    /*\n     * 1 receiveProcess, 2 sendProcess, 2 Messages go from EndEvent to ReceiveTask\n     * dynamic -> deployAndEnable(sendProcesses), startProcess(sendProcesses), deployAndEnable(receiveProcess),\n     * startProcess(receiveProcess)\n     * checks : sendProcesses are finished, receiveProcess goes through receive task (found one message sent by\n     * sendProcess) and reaches user task.\n     */\n    @Test\n    public void receiveMessageSentTwice() throws Exception {\n        ProcessDefinition sendMessageProcess1 = null;\n        ProcessDefinition sendMessageProcess2 = null;\n        ProcessDefinition receiveMessageProcess = null;\n        try {\n            sendMessageProcess1 = deployAndEnableProcessWithEndMessageEvent(\"sendMessageProcess1\", \"m4\",\n                    \"receiveMessageProcess\",\n                    \"waitForMessage\", null, null, null, null);\n            sendMessageProcess2 = deployAndEnableProcessWithEndMessageEvent(\"sendMessageProcess2\", \"m4\",\n                    \"receiveMessageProcess\",\n                    \"waitForMessage\", null, null, null, null);\n            final ProcessInstance sendMessageProcessInstance1 = getProcessAPI()\n                    .startProcess(sendMessageProcess1.getId());\n            final ProcessInstance sendMessageProcessInstance2 = getProcessAPI()\n                    .startProcess(sendMessageProcess2.getId());\n            waitForProcessToFinish(sendMessageProcessInstance1);\n            waitForProcessToFinish(sendMessageProcessInstance2);\n\n            receiveMessageProcess = deployAndEnableProcessWithReceivedTask(\"receiveMessageProcess\", \"waitForMessage\",\n                    \"userTask1\",\n                    \"delivery\", user, \"m4\", null, null, null);\n            final ProcessInstance receiveMessageProcessInstance = getProcessAPI()\n                    .startProcess(receiveMessageProcess.getId());\n            waitForUserTask(receiveMessageProcessInstance, \"userTask1\");\n            final ProcessInstance receiveMessageProcessInstance2 = getProcessAPI()\n                    .startProcess(receiveMessageProcess.getId());\n            waitForUserTask(receiveMessageProcessInstance2, \"userTask1\");\n        } finally {\n            disableAndDeleteProcess(sendMessageProcess1);\n            disableAndDeleteProcess(sendMessageProcess2);\n            disableAndDeleteProcess(receiveMessageProcess);\n        }\n    }\n\n    /*\n     * 1 receiveProcess, 1 sendProcess, Message contains data goes from EndEvent to ReceiveTask\n     * dynamic -> deployAndEnable(sendProcess), deployAndEnable(receiveProcess), startProcess(receiveProcess),\n     * startProcess(sendProcess)\n     * checks : receiveProcess wait on receive task, sendProcess is finished, receiveProcess goes through receive task\n     * (found message sent by\n     * sendProcess) and reaches user task, data is transmitted to\n     * the receiveProcess.\n     */\n    @Test\n    public void receiveMessageWithData() throws Exception {\n        final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithEndMessageEvent(\"sendMessageProcess\",\n                \"m5\", \"receiveMessageProcess\",\n                \"waitForMessage\", null, Collections.singletonMap(\"lastName\", String.class.getName()),\n                Collections.singletonMap(\"lName\", String.class.getName()),\n                Collections.singletonMap(\"lName\", \"lastName\"));\n        final List<Operation> receiveMessageOperations = Collections\n                .singletonList(buildAssignOperation(\"name\", \"lName\", String.class.getName(),\n                        ExpressionType.TYPE_VARIABLE));\n        final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithReceivedTask(\"receiveMessageProcess\",\n                \"waitForMessage\", \"userTask1\",\n                \"delivery\", user, \"m5\", null, Collections.singletonMap(\"name\", String.class.getName()),\n                receiveMessageOperations);\n\n        final ProcessInstance receiveMessageProcessInstance = getProcessAPI()\n                .startProcess(receiveMessageProcess.getId());\n        waitForFlowNodeInState(receiveMessageProcessInstance, \"waitForMessage\", TestStates.WAITING, true);\n\n        final ProcessInstance sendMessageProcessInstance = getProcessAPI().startProcess(sendMessageProcess.getId(),\n                Arrays.asList(\n                        buildAssignOperation(\"lastName\", \"Doe\", String.class.getName(), ExpressionType.TYPE_CONSTANT)),\n                null);\n        waitForProcessToFinish(sendMessageProcessInstance);\n        final HumanTaskInstance step1 = waitForUserTaskAndGetIt(receiveMessageProcessInstance, \"userTask1\");\n\n        final DataInstance dataInstance = getProcessAPI().getProcessDataInstance(\"name\", step1.getRootContainerId());\n        assertEquals(\"Doe\", dataInstance.getValue());\n\n        disableAndDeleteProcess(sendMessageProcess);\n        disableAndDeleteProcess(receiveMessageProcess);\n    }\n\n    /*\n     * dynamic -> deployAndEnable(receiveProcess), startProcess(receiveProcess), cancelProcessInstance(receiveProcess)\n     * checks : receiveProcess wait on receive task, 1 waiting event, receiveProcess is cancelled, receiveProcess is\n     * archived, no more waiting event\n     */\n    @SuppressWarnings(\"unchecked\")\n    @Test\n    public void cancelInstanceShouldDeleteWaitingEvents() throws Exception {\n        final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithReceivedTask(\"receiveMessageProcess\",\n                \"waitForMessage\", \"userTask1\",\n                \"delivery\", user, \"m1\", null, null, null);\n\n        final ProcessInstance receiveMessageProcessInstance = getProcessAPI()\n                .startProcess(receiveMessageProcess.getId());\n        waitForFlowNodeInState(receiveMessageProcessInstance, \"waitForMessage\", TestStates.WAITING, true);\n\n        SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.filter(WaitingEventSearchDescriptor.ROOT_PROCESS_INSTANCE_ID,\n                receiveMessageProcessInstance.getId());\n\n        final Map<String, Serializable> parameters = new HashMap<>(1);\n        parameters.put(SEARCH_OPTIONS_KEY, searchOptionsBuilder.done());\n\n        SearchResult<WaitingEvent> searchResult = (SearchResult<WaitingEvent>) getCommandAPI()\n                .execute(SEARCH_WAITING_EVENTS_COMMAND, parameters);\n        assertEquals(1, searchResult.getCount());\n\n        getProcessAPI().cancelProcessInstance(receiveMessageProcessInstance.getId());\n        waitForProcessToBeInState(receiveMessageProcessInstance, ProcessInstanceState.CANCELLED);\n\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID,\n                receiveMessageProcessInstance.getId());\n        searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.RECEIVE_TASK);\n        final SearchResult<ArchivedActivityInstance> archivedActivityInstancesSearch = getProcessAPI()\n                .searchArchivedActivities(searchOptionsBuilder.done());\n        assertEquals(1, archivedActivityInstancesSearch.getCount());\n        assertTrue(archivedActivityInstancesSearch.getResult().get(0) instanceof ArchivedReceiveTaskInstance);\n        assertEquals(TestStates.CANCELLED.getStateName(),\n                archivedActivityInstancesSearch.getResult().get(0).getState());\n\n        searchResult = (SearchResult<WaitingEvent>) getCommandAPI().execute(SEARCH_WAITING_EVENTS_COMMAND, parameters);\n        assertEquals(0, searchResult.getCount());\n\n        disableAndDeleteProcess(receiveMessageProcess);\n    }\n\n    private ProcessDefinition deployAndEnableProcessWithEndMessageEvent(final String processName,\n            final String messageName, final String targetProcess,\n            final String targetFlowNode, final List<Entry<Expression, Expression>> correlations,\n            final Map<String, String> processData,\n            final Map<String, String> messageData, final Map<String, String> dataInputMapping) throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder();\n        processBuilder.createNewInstance(processName, \"1.0\");\n        addProcessData(processData, processBuilder);\n        processBuilder.addStartEvent(\"startEvent\");\n        processBuilder.addAutomaticTask(\"auto1\");\n        // create expression for target process/flowNode\n        final Expression targetProcessExpression = new ExpressionBuilder()\n                .createConstantStringExpression(targetProcess);\n        final Expression targetFlowNodeExpression = new ExpressionBuilder()\n                .createConstantStringExpression(targetFlowNode);\n        final ThrowMessageEventTriggerBuilder throwMessageEventTriggerBuilder = processBuilder.addEndEvent(\"endEvent\")\n                .addMessageEventTrigger(messageName,\n                        targetProcessExpression, targetFlowNodeExpression);\n        addCorrelations(correlations, throwMessageEventTriggerBuilder);\n        addMessageData(messageData, dataInputMapping, throwMessageEventTriggerBuilder);\n        processBuilder.addTransition(\"startEvent\", \"auto1\");\n        processBuilder.addTransition(\"auto1\", \"endEvent\");\n        final DesignProcessDefinition designProcessDefinition = processBuilder.done();\n\n        final ProcessDefinition sendMessageProcess = deployAndEnableProcess(designProcessDefinition);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(sendMessageProcess.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        return sendMessageProcess;\n    }\n\n    private ProcessDefinition deployAndEnableProcessWithReceivedTask(final String processName,\n            final String receiveTaskName, final String userTaskName,\n            final String actorName, final User user, final String messageName,\n            final List<Entry<Expression, Expression>> correlations,\n            final Map<String, String> processData, final List<Operation> operations) throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder();\n        processBuilder.createNewInstance(processName, \"1.0\");\n        addProcessData(processData, processBuilder);\n        processBuilder.addStartEvent(\"startEvent\");\n        final ReceiveTaskDefinitionBuilder receiveTaskBuilder = processBuilder.addReceiveTask(receiveTaskName,\n                messageName);\n        if (correlations != null) {\n            for (final Entry<Expression, Expression> entry : correlations) {\n                receiveTaskBuilder.addCorrelation(entry.getKey(), entry.getValue());\n            }\n        }\n        if (operations != null) {\n            for (final Operation operation : operations) {\n                receiveTaskBuilder.addMessageOperation(operation);\n            }\n        }\n\n        processBuilder.addActor(actorName);\n        processBuilder.addUserTask(userTaskName, actorName);\n        processBuilder.addEndEvent(\"endEvent\");\n        processBuilder.addTransition(\"startEvent\", receiveTaskName);\n        processBuilder.addTransition(receiveTaskName, userTaskName);\n        processBuilder.addTransition(userTaskName, \"endEvent\");\n        final DesignProcessDefinition designProcessDefinition = processBuilder.done();\n\n        final BusinessArchiveBuilder archiveBuilder = new BusinessArchiveBuilder();\n        archiveBuilder.createNewBusinessArchive().setProcessDefinition(designProcessDefinition);\n        final BusinessArchive receiveMessageArchive = archiveBuilder.done();\n        final ProcessDefinition receiveMessageProcess = deployProcess(receiveMessageArchive);\n\n        final List<ActorInstance> actors = getProcessAPI().getActors(receiveMessageProcess.getId(), 0, 1,\n                ActorCriterion.NAME_ASC);\n        getProcessAPI().addUserToActor(actors.get(0).getId(), user.getId());\n\n        getProcessAPI().enableProcess(receiveMessageProcess.getId());\n\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(receiveMessageProcess.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        return receiveMessageProcess;\n    }\n\n    private void addMessageData(final Map<String, String> messageData, final Map<String, String> dataInputMapping,\n            final ThrowMessageEventTriggerBuilder throwMessageEventTriggerBuilder) throws InvalidExpressionException {\n        if (messageData != null) {\n            for (final Entry<String, String> entry : messageData.entrySet()) {\n                final Expression displayName = new ExpressionBuilder().createConstantStringExpression(entry.getKey());\n\n                Expression defaultValue = null;\n                if (dataInputMapping.containsKey(entry.getKey())) {\n                    defaultValue = new ExpressionBuilder().createDataExpression(dataInputMapping.get(entry.getKey()),\n                            entry.getValue());\n                }\n                throwMessageEventTriggerBuilder.addMessageContentExpression(displayName, defaultValue);\n            }\n        }\n\n    }\n\n    private void addProcessData(final Map<String, String> data, final ProcessDefinitionBuilder processBuilder) {\n        if (data != null) {\n            for (final Entry<String, String> entry : data.entrySet()) {\n                processBuilder.addData(entry.getKey(), entry.getValue(), null);\n            }\n        }\n    }\n\n    private void addCorrelations(final List<Entry<Expression, Expression>> correlations,\n            final ThrowMessageEventTriggerBuilder throwMessageEventTriggerBuilder) {\n        if (correlations != null) {\n            for (final Entry<Expression, Expression> entry : correlations) {\n                throwMessageEventTriggerBuilder.addCorrelation(entry.getKey(), entry.getValue());\n            }\n        }\n    }\n\n    private Operation buildAssignOperation(final String dataInstanceName, final String newConstantValue,\n            final String className,\n            final ExpressionType expressionType) throws InvalidExpressionException {\n        final LeftOperand leftOperand = new LeftOperandBuilder().createNewInstance().setName(dataInstanceName).done();\n        final Expression expression = new ExpressionBuilder().createNewInstance(dataInstanceName)\n                .setContent(newConstantValue)\n                .setExpressionType(expressionType.name()).setReturnType(className).done();\n        final Operation operation;\n        operation = new OperationBuilder().createNewInstance().setOperator(\"=\").setLeftOperand(leftOperand)\n                .setType(OperatorType.ASSIGNMENT)\n                .setRightOperand(expression).done();\n        return operation;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/activity/SendTaskIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.activity;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.bonitasoft.engine.bpm.data.DataInstance;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedSendTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.process.ActivationState;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.SendTaskDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.supervisor.ProcessSupervisorSearchDescriptor;\nimport org.bonitasoft.engine.event.AbstractEventIT;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.expression.ExpressionType;\nimport org.bonitasoft.engine.expression.InvalidExpressionException;\nimport org.bonitasoft.engine.operation.LeftOperand;\nimport org.bonitasoft.engine.operation.LeftOperandBuilder;\nimport org.bonitasoft.engine.operation.Operation;\nimport org.bonitasoft.engine.operation.OperationBuilder;\nimport org.bonitasoft.engine.operation.OperatorType;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.junit.Test;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SendTaskIT extends AbstractEventIT {\n\n    public static final String DATA_INPUT_ITEM_REF_NAME = \"lastName\";\n\n    private ProcessDefinition deployAndEnableProcessWithSendTask(final String processName, final String messageName,\n            final String targetProcess,\n            final String targetFlowNode, final Map<String, String> processData,\n            final Map<String, String> messageData, final Map<String, String> dataInputMapping) throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder();\n        processBuilder.createNewInstance(processName, \"1.0\");\n        addProcessData(processData, processBuilder);\n        processBuilder.addStartEvent(\"startEvent\");\n        // create expression for target process/flowNode\n        final Expression targetProcessExpression = new ExpressionBuilder()\n                .createConstantStringExpression(targetProcess);\n        final SendTaskDefinitionBuilder sendTaskDefinitionBuilder;\n        sendTaskDefinitionBuilder = processBuilder.addSendTask(\"sendMessage\", messageName, targetProcessExpression);\n        if (targetFlowNode != null) {\n            sendTaskDefinitionBuilder\n                    .setTargetFlowNode(new ExpressionBuilder().createConstantStringExpression(targetFlowNode));\n        }\n        processBuilder.addEndEvent(\"endEvent\");\n        addMessageData(messageData, dataInputMapping, sendTaskDefinitionBuilder);\n        processBuilder.addTransition(\"startEvent\", \"sendMessage\");\n        processBuilder.addTransition(\"sendMessage\", \"endEvent\");\n        final DesignProcessDefinition designProcessDefinition = processBuilder.done();\n\n        final ProcessDefinition sendMessageProcess = deployAndEnableProcess(designProcessDefinition);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(sendMessageProcess.getId());\n        assertThat(processDeploymentInfo.getActivationState()).isEqualTo(ActivationState.ENABLED);\n\n        return sendMessageProcess;\n    }\n\n    private void addMessageData(final Map<String, String> messageData, final Map<String, String> dataInputMapping,\n            final SendTaskDefinitionBuilder sendTaskDefinitionBuilder) throws InvalidExpressionException {\n        if (messageData != null) {\n            for (final Entry<String, String> entry : messageData.entrySet()) {\n                final Expression displayName = new ExpressionBuilder().createConstantStringExpression(entry.getKey());\n\n                Expression defaultValue = null;\n                if (dataInputMapping.containsKey(entry.getKey())) {\n                    defaultValue = new ExpressionBuilder().createDataExpression(dataInputMapping.get(entry.getKey()),\n                            entry.getValue());\n                }\n                sendTaskDefinitionBuilder.addMessageContentExpression(displayName, defaultValue);\n            }\n        }\n    }\n\n    private ProcessDefinition deployAndEnableProcessWithMultiInstantiatedSendTask(final String processName,\n            final String messageName,\n            final String targetProcess,\n            final String targetFlowNode, String inputListScript, final Map<String, String> messageData,\n            final Map<String, String> dataInputMapping)\n            throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder();\n        processBuilder.createNewInstance(processName, \"1.0\");\n        processBuilder.addStartEvent(\"startEvent\");\n        String loopDataInput = \"loopDataInput\";\n        processBuilder\n                .addData(\n                        loopDataInput,\n                        List.class.getName(),\n                        new ExpressionBuilder().createGroovyScriptExpression(\n                                \"executeAMultiInstanceWithLoopDataInputAndOutput1\", inputListScript,\n                                List.class.getName()));\n        // create expression for target process/flowNode\n        final Expression targetProcessExpression = new ExpressionBuilder()\n                .createConstantStringExpression(targetProcess);\n        final SendTaskDefinitionBuilder sendTaskDefinitionBuilder;\n        sendTaskDefinitionBuilder = processBuilder.addSendTask(\"sendMessage\", messageName, targetProcessExpression);\n        if (targetFlowNode != null) {\n            sendTaskDefinitionBuilder\n                    .setTargetFlowNode(new ExpressionBuilder().createConstantStringExpression(targetFlowNode));\n        }\n        sendTaskDefinitionBuilder.addShortTextData(DATA_INPUT_ITEM_REF_NAME, null);\n        sendTaskDefinitionBuilder.addMultiInstance(false, loopDataInput).addDataInputItemRef(DATA_INPUT_ITEM_REF_NAME);\n        processBuilder.addEndEvent(\"endEvent\");\n        addMessageData(messageData, dataInputMapping, sendTaskDefinitionBuilder);\n        processBuilder.addTransition(\"startEvent\", \"sendMessage\");\n        processBuilder.addTransition(\"sendMessage\", \"endEvent\");\n        final DesignProcessDefinition designProcessDefinition = processBuilder.done();\n\n        final ProcessDefinition sendMessageProcess = deployAndEnableProcess(designProcessDefinition);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(sendMessageProcess.getId());\n        assertThat(processDeploymentInfo.getActivationState()).isEqualTo(ActivationState.ENABLED);\n\n        return sendMessageProcess;\n    }\n\n    private Operation buildAssignOperation(final String dataInstanceName, final String newConstantValue,\n            final String className,\n            final ExpressionType expressionType) throws InvalidExpressionException {\n        final LeftOperand leftOperand = new LeftOperandBuilder().createNewInstance().setName(dataInstanceName).done();\n        final Expression expression = new ExpressionBuilder().createNewInstance(dataInstanceName)\n                .setContent(newConstantValue)\n                .setExpressionType(expressionType).setReturnType(className).done();\n        final Operation operation;\n        operation = new OperationBuilder().createNewInstance().setOperator(\"=\").setLeftOperand(leftOperand)\n                .setType(OperatorType.ASSIGNMENT)\n                .setRightOperand(expression).done();\n        return operation;\n    }\n\n    /*\n     * 1 receiveProcess, 1 sendProcess, Message contains data goes from SendTask to IntermediateEvent\n     * dynamic -> deployAndEnable(sendProcess), deployAndEnable(receiveProcess), startProcess(sendProcess)\n     * checks : receiveProcess start and stop on catchEvent, sendProcess is finished, , receiveProcess continues and\n     * reaches user task , data is transmitted to\n     * the receiveProcess.\n     */\n    @Test\n    public void dataTransferFromSendTaskToMessageIntermediateCatchEventWithTargetFlowNode() throws Exception {\n        final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithSendTask(\"sendMessageProcess\", \"m14\",\n                \"receiveMessageProcess\",\n                \"waitForMessage\", Collections.singletonMap(\"lastName\", String.class.getName()),\n                Collections.singletonMap(\"lName\", String.class.getName()),\n                Collections.singletonMap(\"lName\", \"lastName\"));\n\n        final List<Operation> catchMessageOperations = Collections\n                .singletonList(buildAssignOperation(\"name\", \"lName\", String.class.getName(),\n                        ExpressionType.TYPE_VARIABLE));\n        final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithIntermediateCatchMessageEvent(\n                \"receiveMessageProcess\", \"m14\", null,\n                Collections.singletonMap(\"name\", String.class.getName()), catchMessageOperations);\n\n        // start a instance of a receive message process\n        final ProcessInstance receiveMessageProcessInstance = getProcessAPI()\n                .startProcess(receiveMessageProcess.getId());\n\n        // wait the event node instance\n        waitForEventInWaitingState(receiveMessageProcessInstance, CATCH_EVENT_NAME);\n\n        DataInstance dataInstance = getProcessAPI().getProcessDataInstance(\"name\",\n                receiveMessageProcessInstance.getId());\n        assertThat(dataInstance.getValue()).isNull();\n\n        final ProcessInstance sendMessageProcessInstance = getProcessAPI().startProcess(sendMessageProcess.getId(),\n                Arrays.asList(\n                        buildAssignOperation(\"lastName\", \"Doe\", String.class.getName(), ExpressionType.TYPE_CONSTANT)),\n                null);\n\n        // No need to verify anything, if no exception, query exists\n        getProcessAPI().searchActivities(\n                new SearchOptionsBuilder(0, 10)\n                        .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID,\n                                sendMessageProcessInstance.getId())\n                        .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.SEND_TASK).done());\n\n        getProcessAPI().searchActivities(\n                new SearchOptionsBuilder(0, 10)\n                        .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID,\n                                sendMessageProcessInstance.getId())\n                        .filter(ProcessSupervisorSearchDescriptor.USER_ID, user.getId()).done());\n\n        getProcessAPI().searchActivities(\n                new SearchOptionsBuilder(0, 10)\n                        .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.SEND_TASK)\n                        .filter(ProcessSupervisorSearchDescriptor.USER_ID, user.getId()).done());\n\n        waitForProcessToFinish(sendMessageProcessInstance);\n        final List<ArchivedActivityInstance> archivedActivityInstances = getProcessAPI().getArchivedActivityInstances(\n                sendMessageProcessInstance.getId(), 0,\n                10, ActivityInstanceCriterion.LAST_UPDATE_DESC);\n        assertThat(archivedActivityInstances.get(0)).isInstanceOf(ArchivedSendTaskInstance.class);\n        waitForUserTask(receiveMessageProcessInstance, CATCH_MESSAGE_STEP1_NAME);\n\n        dataInstance = getProcessAPI().getProcessDataInstance(\"name\", receiveMessageProcessInstance.getId());\n        assertThat(dataInstance.getValue()).isEqualTo(\"Doe\");\n\n        disableAndDeleteProcess(sendMessageProcess);\n        disableAndDeleteProcess(receiveMessageProcess);\n    }\n\n    /*\n     * 1 receiveProcess, 1 sendProcess, Message contains data goes from SendTask to IntermediateEvent\n     * dynamic -> deployAndEnable(sendProcess), deployAndEnable(receiveProcess), startProcess(sendProcess)\n     * checks : receiveProcess start and stop on catchEvent, sendProcess is finished, , receiveProcess continues and\n     * reaches user task , data is transmitted to\n     * the receiveProcess.\n     */\n    @Test\n    public void dataTransferFromSendTaskToMessageIntermediateCatchEvent() throws Exception {\n        final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithSendTask(\"sendMessageProcess\", \"m14\",\n                \"receiveMessageProcess\",\n                null, Collections.singletonMap(\"lastName\", String.class.getName()),\n                Collections.singletonMap(\"lName\", String.class.getName()),\n                Collections.singletonMap(\"lName\", \"lastName\"));\n\n        final List<Operation> catchMessageOperations = Collections\n                .singletonList(buildAssignOperation(\"name\", \"lName\", String.class.getName(),\n                        ExpressionType.TYPE_VARIABLE));\n        final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithIntermediateCatchMessageEvent(\n                \"receiveMessageProcess\", \"m14\", null,\n                Collections.singletonMap(\"name\", String.class.getName()), catchMessageOperations);\n\n        // start a instance of a receive message process\n        final ProcessInstance receiveMessageProcessInstance = getProcessAPI()\n                .startProcess(receiveMessageProcess.getId());\n\n        // wait the event node instance\n        waitForEventInWaitingState(receiveMessageProcessInstance, CATCH_EVENT_NAME);\n\n        DataInstance dataInstance = getProcessAPI().getProcessDataInstance(\"name\",\n                receiveMessageProcessInstance.getId());\n        assertThat(dataInstance.getValue()).isNull();\n\n        final ProcessInstance sendMessageProcessInstance = getProcessAPI().startProcess(sendMessageProcess.getId(),\n                Arrays.asList(\n                        buildAssignOperation(\"lastName\", \"Doe\", String.class.getName(), ExpressionType.TYPE_CONSTANT)),\n                null);\n        waitForProcessToFinish(sendMessageProcessInstance);\n        waitForUserTask(receiveMessageProcessInstance, CATCH_MESSAGE_STEP1_NAME);\n\n        dataInstance = getProcessAPI().getProcessDataInstance(\"name\", receiveMessageProcessInstance.getId());\n        assertThat(dataInstance.getValue()).isEqualTo(\"Doe\");\n\n        disableAndDeleteProcess(sendMessageProcess);\n        disableAndDeleteProcess(receiveMessageProcess);\n    }\n\n    /*\n     * 1 receiveProcess, 1 sendProcess, Message contains data goes from multi instantiated SendTask to StartEvent\n     * dynamic -> deployAndEnable(sendProcess), deployAndEnable(receiveProcess), startProcess(sendProcess)\n     * checks : receiveProcess start and reaches user task , data is transmitted to the receiveProcess.\n     */\n    @Test\n    public void can_transfer_taskData_from_multi_instance_sendTask_to_messageStartEvent() throws Exception {\n        //given\n        final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithMultiInstantiatedSendTask(\n                \"sendMessageProcess\", \"m15\", \"receiveMessageProcess\",\n                START_EVENT_NAME, \"[\\\"Doe\\\", \\\"Smith\\\"]\", Collections.singletonMap(\"lName\", String.class.getName()),\n                Collections.singletonMap(\"lName\", DATA_INPUT_ITEM_REF_NAME));\n\n        final List<Operation> catchMessageOperations = Collections\n                .singletonList(buildAssignOperation(\"name\", \"lName\", String.class.getName(),\n                        ExpressionType.TYPE_VARIABLE));\n        final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithStartMessageEvent(\n                \"receiveMessageProcess\", \"m15\",\n                Collections.singletonMap(\"name\", String.class.getName()), catchMessageOperations);\n\n        //when\n        final ProcessInstance sendMessageProcessInstance = getProcessAPI().startProcess(sendMessageProcess.getId());\n\n        //then\n        waitForProcessToFinish(sendMessageProcessInstance);\n\n        //two instances should be created\n        //step of first instance\n        HumanTaskInstance step1I1 = waitForUserTaskAndGetIt(START_WITH_MESSAGE_STEP1_NAME);\n        assertThat(step1I1.getProcessDefinitionId()).isEqualTo(receiveMessageProcess.getId());\n\n        //step of second instance\n        HumanTaskInstance step1I2 = waitForUserTaskAndGetIt(START_WITH_MESSAGE_STEP1_NAME);\n        assertThat(step1I2.getProcessDefinitionId()).isEqualTo(receiveMessageProcess.getId());\n\n        assertThat(step1I1.getParentProcessInstanceId()).isNotEqualTo(step1I2.getParentProcessInstanceId());\n\n        //data of first instance\n        DataInstance dataInstanceI1 = getProcessAPI().getProcessDataInstance(\"name\",\n                step1I1.getParentProcessInstanceId());\n        //data of second instance\n        DataInstance dataInstanceI2 = getProcessAPI().getProcessDataInstance(\"name\",\n                step1I2.getParentProcessInstanceId());\n\n        assertThat(Arrays.asList(dataInstanceI1.getValue(), dataInstanceI2.getValue())).contains(\"Doe\", \"Smith\");\n\n        //clean up\n        disableAndDeleteProcess(sendMessageProcess);\n        disableAndDeleteProcess(receiveMessageProcess);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/activity/UserTaskAssignationIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.activity;\n\nimport static org.junit.Assert.*;\n\nimport java.util.Arrays;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\n\nimport net.jodah.concurrentunit.Waiter;\nimport org.bonitasoft.engine.TestWithTechnicalUser;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeExecutionException;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.connectors.VariableStorage;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.test.BuildTestUtil;\nimport org.bonitasoft.engine.test.check.CheckNbAssignedTaskOf;\nimport org.bonitasoft.engine.test.check.CheckNbPendingTaskOf;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Ignore;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\n\npublic class UserTaskAssignationIT extends TestWithTechnicalUser {\n\n    @Rule\n    public ExpectedException thrown = ExpectedException.none();\n\n    private static final String JOHN = \"john\";\n\n    private static final String JACK = \"jack\";\n\n    private User john;\n\n    private User jack;\n\n    private ProcessDefinition processDefinition;\n\n    private ProcessInstance processInstance;\n\n    private HumanTaskInstance step2;\n\n    @Override\n    @Before\n    public void before() throws Exception {\n        super.before();\n        john = createUser(JOHN, \"bpm\");\n        jack = createUser(JACK, \"bpm\");\n        loginOnDefaultTenantWith(JOHN, \"bpm\");\n\n        processDefinition = deployAndEnableSimpleProcess();\n        processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        step2 = waitForUserTaskAndGetIt(processInstance, \"step2\");\n    }\n\n    @Override\n    @After\n    public void after() throws Exception {\n        disableAndDeleteProcess(processDefinition);\n\n        deleteUser(JOHN);\n        deleteUser(JACK);\n        VariableStorage.clearAll();\n        super.after();\n    }\n\n    @Test\n    public void getAssignedHumanTasksWithStartedState() throws Exception {\n        assignAndExecuteStep(step2, john);\n\n        final List<HumanTaskInstance> toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(john.getId(), 0, 10,\n                null);\n\n        // Task is in STARTED state so should not be retrieved:\n        assertEquals(0, toDoTasks.size());\n        waitForProcessToFinish(processInstance);\n    }\n\n    @Test(expected = FlowNodeExecutionException.class)\n    public void cannotExecuteAnUnassignedTask() throws Exception {\n        // execute activity without assign it before, an exception is expected\n        getProcessAPI().executeFlowNode(step2.getId());\n    }\n\n    @Test\n    public void assignUserTask() throws Exception {\n        getProcessAPI().assignUserTask(step2.getId(), john.getId());\n\n        final List<HumanTaskInstance> toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(john.getId(), 0, 10,\n                null);\n        assertEquals(1, toDoTasks.size());\n        assertEquals(john.getId(), toDoTasks.get(0).getAssigneeId());\n    }\n\n    @Test\n    public void canAssignTask2Times() throws Exception {\n        getProcessAPI().assignUserTask(step2.getId(), john.getId());\n\n        // No exception expected\n        getProcessAPI().assignUserTask(step2.getId(), john.getId());\n    }\n\n    @Test\n    public void assignUserTaskSeveralTimes() throws Exception {\n        // after assign\n        getProcessAPI().assignUserTask(step2.getId(), john.getId());\n        List<HumanTaskInstance> toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(john.getId(), 0, 10, null);\n        assertEquals(1, toDoTasks.size());\n        assertEquals(john.getId(), toDoTasks.get(0).getAssigneeId());\n        List<HumanTaskInstance> pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null);\n        assertEquals(0, pendingTasks.size());\n        // after release\n        getProcessAPI().releaseUserTask(toDoTasks.get(0).getId());\n        toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(john.getId(), 0, 10, null);\n        assertEquals(0, toDoTasks.size());\n        pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null);\n        assertEquals(1, pendingTasks.size());\n        assertEquals(0, pendingTasks.get(0).getAssigneeId());\n        // re assign\n        getProcessAPI().assignUserTask(step2.getId(), jack.getId());\n        toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(jack.getId(), 0, 10, null);\n        assertEquals(1, toDoTasks.size());\n        assertEquals(jack.getId(), toDoTasks.get(0).getAssigneeId());\n        pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null);\n        assertEquals(0, pendingTasks.size());\n\n        getProcessAPI().releaseUserTask(toDoTasks.get(0).getId());\n        pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null);\n        assertEquals(1, pendingTasks.size());\n        assertEquals(0, pendingTasks.get(0).getAssigneeId());\n        // re assign\n        getProcessAPI().assignUserTask(step2.getId(), john.getId());\n        toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(john.getId(), 0, 10, null);\n        assertEquals(1, toDoTasks.size());\n        assertEquals(john.getId(), toDoTasks.get(0).getAssigneeId());\n        pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null);\n        assertEquals(0, pendingTasks.size());\n    }\n\n    @Test\n    public void releaseUserTask() throws Exception {\n        // after assign\n        getProcessAPI().assignUserTask(step2.getId(), john.getId());\n        List<HumanTaskInstance> toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(john.getId(), 0, 10, null);\n        assertEquals(1, toDoTasks.size());\n        assertEquals(john.getId(), toDoTasks.get(0).getAssigneeId());\n        List<HumanTaskInstance> pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null);\n        assertEquals(0, pendingTasks.size());\n        // after release\n        getProcessAPI().releaseUserTask(toDoTasks.get(0).getId());\n        toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(john.getId(), 0, 10, null);\n        assertEquals(0, toDoTasks.size());\n        pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null);\n        assertEquals(1, pendingTasks.size());\n    }\n\n    @Test\n    public void assignUserTaskSeveralTimesByChangingLogin() throws Exception {\n        // login as jack\n        logout();\n        loginOnDefaultTenantWith(JACK, \"bpm\");\n\n        // assign\n        getProcessAPI().assignUserTask(step2.getId(), jack.getId());\n        List<HumanTaskInstance> toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(jack.getId(), 0, 10, null);\n        assertEquals(1, toDoTasks.size());\n        assertEquals(jack.getId(), toDoTasks.get(0).getAssigneeId());\n        List<HumanTaskInstance> pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null);\n        assertEquals(0, pendingTasks.size());\n        // release\n        getProcessAPI().releaseUserTask(toDoTasks.get(0).getId());\n        toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(jack.getId(), 0, 10, null);\n        assertEquals(0, toDoTasks.size());\n        pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null);\n        assertEquals(1, pendingTasks.size());\n        // re assign\n        getProcessAPI().assignUserTask(pendingTasks.get(0).getId(), jack.getId());\n        toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(jack.getId(), 0, 10, null);\n        assertEquals(1, toDoTasks.size());\n        assertEquals(jack.getId(), toDoTasks.get(0).getAssigneeId());\n        pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null);\n        assertEquals(0, pendingTasks.size());\n    }\n\n    @Test\n    public void assignedDateUpdate() throws Exception {\n        final Long taskId = step2.getId();\n\n        // First assign\n        getProcessAPI().assignUserTask(taskId, john.getId());\n        assertTrue(\"Fail to claim task\", new CheckNbAssignedTaskOf(getProcessAPI(), 30, 2000,\n                false, 1, john).waitUntil());\n        final Date firstClaimedDate = getProcessAPI().getHumanTaskInstance(taskId).getClaimedDate();\n        assertNotNull(\"Claimed date not set during first assignment\", firstClaimedDate);\n\n        // Release\n        getProcessAPI().releaseUserTask(taskId);\n        assertTrue(\"Fail to release task\", new CheckNbPendingTaskOf(getProcessAPI(), 30, 2000,\n                false, 1, john).waitUntil());\n        assertNull(\"Claimed date not unset during release\",\n                getProcessAPI().getHumanTaskInstance(taskId).getClaimedDate());\n\n        // Second assign\n        getProcessAPI().assignUserTask(taskId, john.getId());\n        assertTrue(\"Fail to claim task for the second time\", new CheckNbAssignedTaskOf(getProcessAPI(), 30,\n                2000, false, 1, john).waitUntil());\n        final HumanTaskInstance task = getProcessAPI().getHumanTaskInstance(taskId);\n        assertNotNull(\"Claimed date not set during first assignment\", task.getClaimedDate());\n        assertNotEquals(\"Claimed date not updated\", firstClaimedDate, task.getClaimedDate());\n\n    }\n\n    @Test\n    @Ignore(\"lastUpdateDate should be removed (not used)\")\n    public void lastUpdateDateUpdate() throws Exception {\n        final Long taskId = step2.getId();\n        Date previousUpdateDate = step2.getLastUpdateDate();\n\n        // First assign\n        getProcessAPI().assignUserTask(taskId, john.getId());\n        if (!new CheckNbAssignedTaskOf(getProcessAPI(), 50, 5000, false, 1, john).waitUntil()) {\n            fail(\"Fail to claim task\");\n        }\n        HumanTaskInstance task = getProcessAPI().getHumanTaskInstance(taskId);\n        assertNotNull(\"Last update date not set during first assignment\", task.getLastUpdateDate());\n        assertFalse(\"Last update date not updated during first assignment\",\n                task.getLastUpdateDate().equals(previousUpdateDate));\n        previousUpdateDate = task.getLastUpdateDate();\n\n        // Release\n        getProcessAPI().releaseUserTask(taskId);\n        task = waitForUserTaskAndGetIt(processInstance, \"step2\");\n        assertFalse(\"Last update date not updated during release\", previousUpdateDate.equals(task.getLastUpdateDate()));\n        previousUpdateDate = task.getLastUpdateDate();\n\n        // Second assign\n        getProcessAPI().assignUserTask(taskId, john.getId());\n        if (!new CheckNbAssignedTaskOf(getProcessAPI(), 50, 5000, false, 1, john).waitUntil()) {\n            fail(\"Fail to claim task for the second time\");\n        }\n        task = getProcessAPI().getHumanTaskInstance(taskId);\n        assertFalse(\"Last update date not updated during second assignment\",\n                previousUpdateDate.equals(task.getLastUpdateDate()));\n    }\n\n    private ProcessDefinition deployAndEnableSimpleProcess()\n            throws BonitaException, InvalidProcessDefinitionException {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(false, true));\n        return deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, john);\n    }\n\n    @Test\n    public void assignUserTaskIfNotAssigned() throws Exception {\n        getProcessAPI().assignUserTaskIfNotAssigned(step2.getId(), john.getId());\n\n        final List<HumanTaskInstance> toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(john.getId(),\n                0, 10, null);\n        assertEquals(1, toDoTasks.size());\n        assertEquals(john.getId(), toDoTasks.get(0).getAssigneeId());\n    }\n\n    @Test\n    public void shouldAssignAndUnassign() throws Exception {\n        getProcessAPI().assignUserTaskIfNotAssigned(step2.getId(), john.getId());\n\n        getProcessAPI().assignUserTaskIfNotAssigned(step2.getId(), 0L);\n\n    }\n\n    @Test\n    public void shouldAssignAndAssignAgainToSameUser() throws Exception {\n        getProcessAPI().assignUserTaskIfNotAssigned(step2.getId(), john.getId());\n\n        getProcessAPI().assignUserTaskIfNotAssigned(step2.getId(), john.getId());\n\n    }\n\n    @Test\n    public void canNotAssignTask2TimestoDifferentUserIfAssigned() throws Exception {\n        getProcessAPI().assignUserTaskIfNotAssigned(step2.getId(), john.getId());\n        thrown.expect(UpdateException.class);\n        getProcessAPI().assignUserTaskIfNotAssigned(step2.getId(), jack.getId());\n\n    }\n\n    @Test\n    public void shouldAssignTaskSeveralTimesToDifferentUsers() throws Exception {\n\n        getProcessAPI().assignUserTaskIfNotAssigned(step2.getId(), john.getId());\n        List<HumanTaskInstance> toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(john.getId(), 0, 10, null);\n        assertEquals(1, toDoTasks.size());\n        assertEquals(john.getId(), toDoTasks.get(0).getAssigneeId());\n        List<HumanTaskInstance> pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null);\n        assertEquals(0, pendingTasks.size());\n\n        getProcessAPI().releaseUserTask(toDoTasks.get(0).getId());\n        toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(john.getId(), 0, 10, null);\n        assertEquals(0, toDoTasks.size());\n        pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null);\n        assertEquals(1, pendingTasks.size());\n        assertEquals(0, pendingTasks.get(0).getAssigneeId());\n\n        getProcessAPI().assignUserTaskIfNotAssigned(step2.getId(), jack.getId());\n        toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(jack.getId(), 0, 10, null);\n        assertEquals(1, toDoTasks.size());\n        assertEquals(jack.getId(), toDoTasks.get(0).getAssigneeId());\n        pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null);\n        assertEquals(0, pendingTasks.size());\n\n        getProcessAPI().releaseUserTask(toDoTasks.get(0).getId());\n        pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null);\n        assertEquals(1, pendingTasks.size());\n        assertEquals(0, pendingTasks.get(0).getAssigneeId());\n\n        getProcessAPI().assignUserTaskIfNotAssigned(step2.getId(), john.getId());\n        toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(john.getId(), 0, 10, null);\n        assertEquals(1, toDoTasks.size());\n        assertEquals(john.getId(), toDoTasks.get(0).getAssigneeId());\n        pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null);\n        assertEquals(0, pendingTasks.size());\n    }\n\n    @Test\n    public void shouldAssignUserTaskSeveralTimesByChangingLogin() throws Exception {\n        logout();\n        loginOnDefaultTenantWith(JACK, \"bpm\");\n\n        getProcessAPI().assignUserTaskIfNotAssigned(step2.getId(), jack.getId());\n        List<HumanTaskInstance> toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(jack.getId(), 0, 10, null);\n        assertEquals(1, toDoTasks.size());\n        assertEquals(jack.getId(), toDoTasks.get(0).getAssigneeId());\n        List<HumanTaskInstance> pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null);\n        assertEquals(0, pendingTasks.size());\n\n        getProcessAPI().releaseUserTask(toDoTasks.get(0).getId());\n        toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(jack.getId(), 0, 10, null);\n        assertEquals(0, toDoTasks.size());\n        pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null);\n        assertEquals(1, pendingTasks.size());\n\n        getProcessAPI().assignUserTaskIfNotAssigned(pendingTasks.get(0).getId(), jack.getId());\n        toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(jack.getId(), 0, 10, null);\n        assertEquals(1, toDoTasks.size());\n        assertEquals(jack.getId(), toDoTasks.get(0).getAssigneeId());\n        pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null);\n        assertEquals(0, pendingTasks.size());\n    }\n\n    @Test\n    public void assignedDateUpdateIfNotAssigned() throws Exception {\n        final Long taskId = step2.getId();\n\n        getProcessAPI().assignUserTaskIfNotAssigned(taskId, john.getId());\n        assertTrue(\"Fail to claim task\", new CheckNbAssignedTaskOf(getProcessAPI(), 30, 2000,\n                false, 1, john).waitUntil());\n        final Date firstClaimedDate = getProcessAPI().getHumanTaskInstance(taskId).getClaimedDate();\n        assertNotNull(\"Claimed date not set during first assignment\", firstClaimedDate);\n\n        getProcessAPI().releaseUserTask(taskId);\n        assertTrue(\"Fail to release task\", new CheckNbPendingTaskOf(getProcessAPI(), 30, 2000,\n                false, 1, john).waitUntil());\n        assertNull(\"Claimed date not unset during release\",\n                getProcessAPI().getHumanTaskInstance(taskId).getClaimedDate());\n\n        getProcessAPI().assignUserTaskIfNotAssigned(taskId, john.getId());\n        assertTrue(\"Fail to claim task for the second time\",\n                new CheckNbAssignedTaskOf(getProcessAPI(), 30, 2000, false, 1, john).waitUntil());\n        final HumanTaskInstance task = getProcessAPI().getHumanTaskInstance(taskId);\n        assertNotNull(\"Claimed date not set during first assignment\", task.getClaimedDate());\n        assertNotEquals(\"Claimed date not updated\", firstClaimedDate, task.getClaimedDate());\n    }\n\n    @Test\n    public void assignUserTaskSeveralThreads() throws Throwable {\n        final Long taskId = step2.getId();\n        loginOnDefaultTenantWith(JACK, \"bpm\");\n        ProcessAPI pAPI = getProcessAPI();\n        Waiter waiter = new Waiter();\n        Boolean[] exceptionThreads = new Boolean[] { false, false };\n        Thread thread1 = new Thread() {\n\n            public void run() {\n                try {\n                    pAPI.assignUserTaskIfNotAssigned(taskId, john.getId());\n                } catch (Exception e) {\n                    exceptionThreads[0] = true;\n                } finally {\n                    waiter.resume();\n                }\n            }\n        };\n        Thread thread2 = new Thread() {\n\n            public void run() {\n                try {\n                    pAPI.assignUserTaskIfNotAssigned(taskId, jack.getId());\n                } catch (Exception e) {\n                    exceptionThreads[0] = true;\n                } finally {\n                    waiter.resume();\n                }\n            }\n        };\n\n        thread1.start();\n        thread2.start();\n        waiter.await(1, TimeUnit.SECONDS, 2);\n        //Assert only one failed;\n        assertNotEquals(\"Both assigments failed\", exceptionThreads[0] || exceptionThreads[1], false);\n        assertNotEquals(\"Both assigments took place\", exceptionThreads[0] && exceptionThreads[1], true);\n\n    }\n\n    @Test\n    public void assignUserTaskSeveralThreadsWithoutCheck() throws Throwable {\n        final Long taskId = step2.getId();\n        loginOnDefaultTenantWith(JACK, \"bpm\");\n        ProcessAPI pAPI = getProcessAPI();\n        Waiter waiter = new Waiter();\n        Boolean[] exceptionThreads = new Boolean[] { false, false };\n        Thread thread1 = new Thread() {\n\n            public void run() {\n                try {\n                    pAPI.assignUserTask(taskId, john.getId());\n                } catch (Exception e) {\n                    exceptionThreads[0] = true;\n                } finally {\n                    waiter.resume();\n                }\n            }\n        };\n        Thread thread2 = new Thread() {\n\n            public void run() {\n                try {\n                    pAPI.assignUserTask(taskId, jack.getId());\n                } catch (Exception e) {\n                    exceptionThreads[0] = true;\n                } finally {\n                    waiter.resume();\n                }\n            }\n        };\n\n        thread1.start();\n        thread2.start();\n        waiter.await(1, TimeUnit.SECONDS, 2);\n        //Assert only one failed;\n        assertEquals(\"Both assigments took place\", exceptionThreads[0] || exceptionThreads[1], false);\n\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/activity/work/RetryWorkIT.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.activity.work;\n\nimport org.bonitasoft.engine.CommonAPIIT;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Baptiste Mesta.\n */\npublic class RetryWorkIT extends CommonAPIIT {\n\n    @Before\n    public void before() throws Exception {\n        loginWithTechnicalUser();\n    }\n\n    @Test\n    public void should_retry_exception_in_groovy_scripts() throws Exception {\n        String script = \"public class AScriptThatFailsOneTime{\\n\" +\n                \"   static alreadyExecutedOnce = false\\n\" +\n                \"\\n\" +\n                \"   String exec(){\\n\" +\n                \"       if(alreadyExecutedOnce){\\n\" +\n                \"           return \\\"ok\\\"\\n\" +\n                \"       }\\n\" +\n                \"      alreadyExecutedOnce = true\\n\" +\n                \"       throw new org.bonitasoft.engine.commons.exceptions.SRetryableException('something')\\n\" +\n                \"   }\\n\" +\n                \"}\\n\" +\n                \"new AScriptThatFailsOneTime().exec()\";\n        DesignProcessDefinition process = new ProcessDefinitionBuilder().createNewInstance(\"processToRetry\", \"1.0\")\n                .addAutomaticTask(\"theTask\").addDisplayName(new ExpressionBuilder()\n                        .createGroovyScriptExpression(\"failonetime\", script, String.class.getName()))\n                .getProcess();\n        ProcessDefinition processDefinition = getProcessAPI().deployAndEnableProcess(process);\n\n        ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        //the task should fail one but be retried\n        waitForProcessToFinish(processInstance);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/application/ApplicationIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.application;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.List;\n\nimport org.apache.commons.io.IOUtils;\nimport org.bonitasoft.engine.TestWithTechnicalUser;\nimport org.bonitasoft.engine.api.ImportStatus;\nimport org.bonitasoft.engine.api.permission.APICallContext;\nimport org.bonitasoft.engine.business.application.*;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.profile.Profile;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.junit.Test;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class ApplicationIT extends TestWithTechnicalUser {\n\n    @Test\n    public void searchApplications_can_filter_by_user_id() throws Exception {\n        //given\n        final User user1 = createUser(\"walter.bates\", \"bpm\");\n        final User user2 = createUser(\"helen.kelly\", \"bpm\");\n        final User user3 = createUser(\"daniela.angelo\", \"bpm\");\n        final User user4 = createUser(\"jan.fisher\", \"bpm\");\n\n        final List<Profile> profiles = getProfileAPI().searchProfiles(new SearchOptionsBuilder(0, 10).done())\n                .getResult();\n\n        getProfileAPI().createProfileMember(profiles.get(0).getId(), user1.getId(), null, null);\n        getProfileAPI().createProfileMember(profiles.get(1).getId(), user2.getId(), null, null);\n        getProfileAPI().createProfileMember(profiles.get(0).getId(), user3.getId(), null, null);\n        getProfileAPI().createProfileMember(profiles.get(1).getId(), user3.getId(), null, null);\n\n        final ApplicationCreator hrCreator = new ApplicationCreator(\"HR-dashboard\", \"HR dashboard\", \"1.0\")\n                .setProfileId(profiles.get(0).getId());\n        final ApplicationCreator engineeringCreator = new ApplicationCreator(\"Engineering-dashboard\",\n                \"Engineering dashboard\", \"1.0\").setProfileId(profiles.get(0).getId());\n        final ApplicationCreator marketingCreator = new ApplicationCreator(\"Marketing-dashboard\", \"Marketing dashboard\",\n                \"1.0\").setProfileId(profiles.get(1).getId());\n\n        final Application hr = getApplicationAPI().createApplication(hrCreator);\n        final Application engineering = getApplicationAPI().createApplication(engineeringCreator);\n        final Application marketing = getApplicationAPI().createApplication(marketingCreator);\n\n        //when\n        final SearchOptionsBuilder builderUser1 = new SearchOptionsBuilder(0, 10);\n        builderUser1.filter(ApplicationSearchDescriptor.USER_ID, user1.getId());\n        builderUser1.sort(ApplicationSearchDescriptor.DISPLAY_NAME, Order.ASC);\n        final SearchResult<IApplication> applicationsUser1 = getApplicationAPI()\n                .searchIApplications(builderUser1.done());\n        assertThat(applicationsUser1).isNotNull();\n        assertThat(applicationsUser1.getResult()).contains(engineering, hr).doesNotContain(marketing);\n\n        final SearchOptionsBuilder builderUser2 = new SearchOptionsBuilder(0, 10);\n        builderUser2.filter(ApplicationSearchDescriptor.USER_ID, user2.getId());\n        builderUser2.sort(ApplicationSearchDescriptor.DISPLAY_NAME, Order.ASC);\n        final SearchResult<IApplication> applicationsUser2 = getApplicationAPI()\n                .searchIApplications(builderUser2.done());\n        assertThat(applicationsUser2).isNotNull();\n        assertThat(applicationsUser2.getResult()).contains(marketing).doesNotContain(engineering, hr);\n\n        final SearchOptionsBuilder builderUser3 = new SearchOptionsBuilder(0, 10);\n        builderUser3.filter(ApplicationSearchDescriptor.USER_ID, user3.getId());\n        builderUser3.sort(ApplicationSearchDescriptor.DISPLAY_NAME, Order.ASC);\n        final SearchResult<IApplication> applicationsUser3 = getApplicationAPI()\n                .searchIApplications(builderUser3.done());\n        assertThat(applicationsUser3).isNotNull();\n        assertThat(applicationsUser3.getResult()).contains(engineering, hr, marketing);\n\n        final SearchOptionsBuilder builderUser4 = new SearchOptionsBuilder(0, 10);\n        builderUser4.filter(ApplicationSearchDescriptor.USER_ID, user4.getId());\n        builderUser4.sort(ApplicationSearchDescriptor.DISPLAY_NAME, Order.ASC);\n        final SearchResult<IApplication> applicationsUser4 = getApplicationAPI()\n                .searchIApplications(builderUser4.done());\n        assertThat(applicationsUser4.getResult()).isEmpty();\n\n        // Let's check SAM (=tenant admin), has access to applications mapped to \"_BONITA_INTERNAL_PROFILE_SUPER_ADMIN\":\n        final List<ImportStatus> importStatus = getLivingApplicationAPI().importApplications(\n                IOUtils.toByteArray(ApplicationIT.class.getResourceAsStream(\"superAdminApp.xml\")),\n                ApplicationImportPolicy.FAIL_ON_DUPLICATES);\n        assertThat(importStatus).isNotEmpty().allMatch(status -> status.getStatus().equals(ImportStatus.Status.ADDED));\n        final SearchOptionsBuilder soSystemAdmin = new SearchOptionsBuilder(0, 10);\n        soSystemAdmin.filter(ApplicationSearchDescriptor.USER_ID, \"-1\"); // -1 is userId for SAM (= tenant admin)\n        final SearchResult<IApplication> samApplications = getApplicationAPI()\n                .searchIApplications(soSystemAdmin.done());\n        final List<IApplication> applications = samApplications.getResult();\n        assertThat(applications).hasSize(1);\n        assertThat(applications.get(0).getToken()).isEqualTo(\"superAdminAppBonita\");\n\n        getApplicationAPI().deleteApplication(applications.get(0).getId());\n    }\n\n    @Test\n    public void searchApplications_can_filter_by_user_id_and_process_display_name() throws Exception {\n        //given\n        final User user1 = createUser(\"walter.bates\", \"bpm\");\n\n        final List<Profile> profiles = getProfileAPI().searchProfiles(new SearchOptionsBuilder(0, 10).done())\n                .getResult();\n\n        getProfileAPI().createProfileMember(profiles.get(0).getId(), user1.getId(), null, null);\n\n        final ApplicationCreator hrCreator = new ApplicationCreator(\"HR-dashboard\", \"HR dashboard\", \"1.0\")\n                .setProfileId(profiles.get(0).getId());\n        final ApplicationCreator engineeringCreator = new ApplicationCreator(\"Engineering-dashboard\",\n                \"Engineering dashboard\", \"1.0\").setProfileId(profiles.get(0).getId());\n        final ApplicationCreator marketingCreator = new ApplicationCreator(\"Marketing-dashboard\", \"Marketing dashboard\",\n                \"1.0\").setProfileId(profiles.get(1).getId());\n\n        final Application hr = getApplicationAPI().createApplication(hrCreator);\n        final Application engineering = getApplicationAPI().createApplication(engineeringCreator);\n        final Application marketing = getApplicationAPI().createApplication(marketingCreator);\n\n        //when\n        final SearchOptionsBuilder builderUser1 = new SearchOptionsBuilder(0, 10);\n        builderUser1.filter(ApplicationSearchDescriptor.USER_ID, user1.getId());\n        builderUser1.filter(ApplicationSearchDescriptor.DISPLAY_NAME, \"Engineering dashboard\");\n        final SearchResult<IApplication> applicationsUser1 = getApplicationAPI()\n                .searchIApplications(builderUser1.done());\n        assertThat(applicationsUser1).isNotNull();\n        assertThat(applicationsUser1.getCount()).isEqualTo(1);\n        assertThat(applicationsUser1.getResult()).containsExactly(engineering);\n    }\n\n    @Test\n    public void should_access_identity_api_using_default_application_permissions() throws Exception {\n        // Given\n        var testPage = getPageAPI().createPage(\"page-to-test-permissions.zip\",\n                IOUtils.toByteArray(ApplicationIT.class.getResourceAsStream(\"/page-to-test-permissions.zip\")));\n        var testApp = getApplicationAPI().importApplications(\n                IOUtils.toByteArray(ApplicationIT.class.getResourceAsStream(\"/application-to-test-permissions.xml\")),\n                ApplicationImportPolicy.FAIL_ON_DUPLICATES);\n        // Uses page-to-test-permissions.zip\n        // Uses application-to-test-permissions.xml\n        User user = createUser(\"baptiste\", \"bpm\");\n        loginOnDefaultTenantWith(\"baptiste\", \"bpm\");\n\n        assertThat(getPermissionAPI().isAuthorized(new APICallContext(\"GET\", \"identity\", \"user\", null))).isFalse();\n\n        getProfileAPI().createProfileMember(\n                getProfileAPI().searchProfiles(new SearchOptionsBuilder(0, 1).searchTerm(\"User\").done()).getResult()\n                        .get(0).getId(),\n                user.getId(),\n                -1L,\n                -1L);\n\n        loginOnDefaultTenantWith(\"baptiste\", \"bpm\");\n\n        long userId = user.getId();\n        assertThat(\n                getPermissionAPI().isAuthorized(new APICallContext(\"GET\", \"identity\", \"user\", String.valueOf(userId))))\n                .isTrue();\n        assertThat(getPermissionAPI()\n                .isAuthorized(new APICallContext(\"GET\", \"identity\", \"user\", String.valueOf(userId + 1)))).isFalse();\n        assertThat(getPermissionAPI().isAuthorized(new APICallContext(\"GET\", \"identity\", \"user\", null))).isFalse();\n\n        getProfileAPI().createProfileMember(\n                getProfileAPI().searchProfiles(new SearchOptionsBuilder(0, 1).searchTerm(\"Admin\").done()).getResult()\n                        .get(0).getId(),\n                user.getId(),\n                -1L,\n                -1L);\n\n        loginOnDefaultTenantWith(\"baptiste\", \"bpm\");\n\n        assertThat(getPermissionAPI().isAuthorized(new APICallContext(\"GET\", \"identity\", \"user\", null))).isTrue();\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/business/application/LivingApplicationIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\nimport static org.junit.Assert.fail;\n\nimport org.bonitasoft.engine.exception.NotFoundException;\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.engine.profile.Profile;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.junit.Test;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class LivingApplicationIT extends TestWithLivingApplication {\n\n    @Test\n    public void createApplication_returns_application_based_on_ApplicationCreator_information() throws Exception {\n        //given\n        final Profile profile = getProfileUser();\n        Page defaultLayout = getPageAPI().getPageByName(DEFAULT_LAYOUT_NAME);\n        Page defaultTheme = getPageAPI().getPageByName(DEFAULT_THEME_NAME);\n        final ApplicationCreator creator = new ApplicationCreator(\"My-Application\", \"My application display name\",\n                \"1.0\");\n        creator.setDescription(\"This is my application\");\n        creator.setIconPath(\"/icon.jpg\");\n        creator.setProfileId(profile.getId());\n\n        //when\n        final Application application = getLivingApplicationAPI().createApplication(creator);\n\n        //then\n        assertThat(application).isNotNull();\n        assertThat(application.getToken()).isEqualTo(\"My-Application\");\n        assertThat(application.getDisplayName()).isEqualTo(\"My application display name\");\n        assertThat(application.getVersion()).isEqualTo(\"1.0\");\n        assertThat(application.getId()).isGreaterThan(0);\n        assertThat(application.getDescription()).isEqualTo(\"This is my application\");\n        assertThat(application.getIconPath()).isEqualTo(\"/icon.jpg\");\n        assertThat(application.getCreatedBy()).isEqualTo(getUser().getId());\n        assertThat(application.getUpdatedBy()).isEqualTo(getUser().getId());\n        assertThat(application.getHomePageId()).isNull();\n        assertThat(application.getProfileId()).isEqualTo(profile.getId());\n        assertThat(application.getLayoutId()).isEqualTo(defaultLayout.getId());\n        assertThat(application.getThemeId()).isEqualTo(defaultTheme.getId());\n\n        getLivingApplicationAPI().deleteApplication(application.getId());\n    }\n\n    @Test\n    public void createApplicationLink_returns_application_based_on_ApplicationCreator_information() throws Exception {\n        //given\n        final Profile profile = getProfileUser();\n        final ApplicationLinkCreator creator = new ApplicationLinkCreator(\"My-Application\",\n                \"My application display name\",\n                \"1.0\");\n        creator.setDescription(\"This is my application\");\n        creator.setIconPath(\"/icon.jpg\");\n        creator.setProfileId(profile.getId());\n\n        //when\n        final ApplicationLink application = getLivingApplicationAPI().createApplicationLink(creator);\n\n        //then\n        assertThat(application).isNotNull();\n        assertThat(application.getToken()).isEqualTo(\"My-Application\");\n        assertThat(application.getDisplayName()).isEqualTo(\"My application display name\");\n        assertThat(application.getVersion()).isEqualTo(\"1.0\");\n        assertThat(application.getId()).isGreaterThan(0);\n        assertThat(application.getDescription()).isEqualTo(\"This is my application\");\n        assertThat(application.getIconPath()).isEqualTo(\"/icon.jpg\");\n        assertThat(application.getCreatedBy()).isEqualTo(getUser().getId());\n        assertThat(application.getUpdatedBy()).isEqualTo(getUser().getId());\n        assertThat(application.getProfileId()).isEqualTo(profile.getId());\n\n        getLivingApplicationAPI().deleteApplication(application.getId());\n    }\n\n    @Test\n    public void createApplication_without_profile_should_have_null_profileId() throws Exception {\n        //given\n        final ApplicationCreator creator = new ApplicationCreator(\"My-Application\", \"My application display name\",\n                \"1.0\");\n\n        //when\n        final Application application = getLivingApplicationAPI().createApplication(creator);\n\n        //then\n        assertThat(application).isNotNull();\n        assertThat(application.getProfileId()).isNull();\n    }\n\n    @Test\n    public void updateApplication_should_return_application_up_to_date() throws Exception {\n        //given\n        final Profile profile = getProfileUser();\n        final ApplicationCreator creator = new ApplicationCreator(\"My-Application\", \"My application display name\",\n                \"1.0\");\n        final Application application = getLivingApplicationAPI().createApplication(creator);\n\n        final ApplicationUpdater updater = new ApplicationUpdater();\n        updater.setToken(\"My-updated-app\");\n        updater.setDisplayName(\"Updated display name\");\n        updater.setVersion(\"1.1\");\n        updater.setDescription(\"Up description\");\n        updater.setIconPath(\"/newIcon.jpg\");\n        updater.setProfileId(profile.getId());\n        updater.setState(ApplicationState.ACTIVATED.name());\n\n        //when\n        final Application updatedApplication = getLivingApplicationAPI().updateApplication(application.getId(),\n                updater);\n\n        //then\n        assertThat(updatedApplication).isNotNull();\n        assertThat(updatedApplication.getToken()).isEqualTo(\"My-updated-app\");\n        assertThat(updatedApplication.getDisplayName()).isEqualTo(\"Updated display name\");\n        assertThat(updatedApplication.getVersion()).isEqualTo(\"1.1\");\n        assertThat(updatedApplication.getDescription()).isEqualTo(\"Up description\");\n        assertThat(updatedApplication.getIconPath()).isEqualTo(\"/newIcon.jpg\");\n        assertThat(updatedApplication.getProfileId()).isEqualTo(profile.getId());\n        assertThat(updatedApplication.getState()).isEqualTo(ApplicationState.ACTIVATED.name());\n        assertThat(updatedApplication).isEqualTo(getLivingApplicationAPI().getApplication(application.getId()));\n\n        getLivingApplicationAPI().deleteApplication(application.getId());\n    }\n\n    @Test\n    public void updateApplicationLink_should_return_application_up_to_date() throws Exception {\n        //given\n        final Profile profile = getProfileUser();\n        final ApplicationLinkCreator creator = new ApplicationLinkCreator(\"My-Application\",\n                \"My application display name\",\n                \"1.0\");\n        final ApplicationLink application = getLivingApplicationAPI().createApplicationLink(creator);\n\n        final ApplicationLinkUpdater updater = new ApplicationLinkUpdater();\n        updater.setToken(\"My-updated-app\");\n        updater.setDisplayName(\"Updated display name\");\n        updater.setVersion(\"1.1\");\n        updater.setDescription(\"Up description\");\n        updater.setIconPath(\"/newIcon.jpg\");\n        updater.setProfileId(profile.getId());\n        updater.setState(ApplicationState.ACTIVATED.name());\n\n        //when\n        final ApplicationLink updatedApplication = getLivingApplicationAPI().updateApplicationLink(application.getId(),\n                updater);\n\n        //then\n        assertThat(updatedApplication).isNotNull();\n        assertThat(updatedApplication.getToken()).isEqualTo(\"My-updated-app\");\n        assertThat(updatedApplication.getDisplayName()).isEqualTo(\"Updated display name\");\n        assertThat(updatedApplication.getVersion()).isEqualTo(\"1.1\");\n        assertThat(updatedApplication.getDescription()).isEqualTo(\"Up description\");\n        assertThat(updatedApplication.getIconPath()).isEqualTo(\"/newIcon.jpg\");\n        assertThat(updatedApplication.getProfileId()).isEqualTo(profile.getId());\n        assertThat(updatedApplication.getState()).isEqualTo(ApplicationState.ACTIVATED.name());\n        assertThat(updatedApplication).isEqualTo(getLivingApplicationAPI().getIApplication(application.getId()));\n\n        getLivingApplicationAPI().deleteApplication(application.getId());\n    }\n\n    @Test\n    public void getApplication_returns_application_with_the_given_id() throws Exception {\n        //given\n        final ApplicationCreator creator = new ApplicationCreator(\"My-Application\", \"My application display name\",\n                \"1.0\");\n        final Application createdApp = getLivingApplicationAPI().createApplication(creator);\n        assertThat(createdApp).isNotNull();\n\n        //when\n        final IApplication retrievedApp = getLivingApplicationAPI().getIApplication(createdApp.getId());\n\n        //then\n        assertThat(retrievedApp).isEqualTo(createdApp);\n    }\n\n    @Test\n    public void getApplicationByToken_returns_application_with_the_given_token() throws Exception {\n        //given\n        final ApplicationCreator creator = new ApplicationCreator(\"My-Application\", \"My application display name\",\n                \"1.0\");\n        final Application createdApp = getLivingApplicationAPI().createApplication(creator);\n        assertThat(createdApp).isNotNull();\n\n        //when\n        final IApplication retrievedApp = getLivingApplicationAPI().getIApplicationByToken(createdApp.getToken());\n\n        //then\n        assertThat(retrievedApp).isEqualTo(createdApp);\n    }\n\n    @Test\n    public void deleteApplication_should_delete_application_with_the_given_id() throws Exception {\n        //given\n        final ApplicationCreator creator = new ApplicationCreator(\"My-Application\", \"My application display name\",\n                \"1.0\");\n        final Application createdApp = getLivingApplicationAPI().createApplication(creator);\n        assertThat(createdApp).isNotNull();\n\n        //when\n        getLivingApplicationAPI().deleteApplication(createdApp.getId());\n\n        //then\n        try {\n            getLivingApplicationAPI().getApplication(createdApp.getId());\n            fail(\"Not found exception\");\n        } catch (final NotFoundException e) {\n            //ok\n        }\n    }\n\n    @Test\n    public void searchApplications_without_filter_return_all_elements_based_on_pagination() throws Exception {\n        //given\n        final ApplicationCreator hrCreator = new ApplicationCreator(\"AAA_HR-dashboard\", \"HR dash board\", \"1.0\");\n        final ApplicationCreator engineeringCreator = new ApplicationCreator(\"AAA_Engineering-dashboard\",\n                \"Engineering dashboard\", \"1.0\");\n        final ApplicationCreator marketingCreator = new ApplicationCreator(\"AAA_Marketing-dashboard\",\n                \"Marketing dashboard\",\n                \"1.0\");\n\n        final Application hr = getLivingApplicationAPI().createApplication(hrCreator);\n        final Application engineering = getLivingApplicationAPI().createApplication(engineeringCreator);\n        final Application marketing = getLivingApplicationAPI().createApplication(marketingCreator);\n\n        //when\n        final SearchResult<IApplication> firstPage = getLivingApplicationAPI()\n                .searchIApplications(buildSearchOptions(\"AAA\", 0, 2));\n\n        //then\n        assertThat(firstPage).isNotNull();\n        assertThat(firstPage.getCount()).isEqualTo(3);\n        assertThat(firstPage.getResult()).containsExactly(engineering, hr);\n\n        //when\n        final SearchResult<IApplication> secondPage = getLivingApplicationAPI()\n                .searchIApplications(buildSearchOptions(\"AAA\", 2, 2));\n\n        //then\n        assertThat(secondPage).isNotNull();\n        assertThat(secondPage.getCount()).isEqualTo(3);\n        assertThat(secondPage.getResult()).containsExactly(marketing);\n    }\n\n    @Test\n    public void searchApplications_can_filter_on_name() throws Exception {\n        //given\n        final ApplicationCreator hrCreator = new ApplicationCreator(\"HR-dashboard\", \"HR dash board\", \"1.0\");\n        final ApplicationCreator engineeringCreator = new ApplicationCreator(\"Engineering-dashboard\",\n                \"Engineering dashboard\", \"1.0\");\n        final ApplicationCreator marketingCreator = new ApplicationCreator(\"Marketing-dashboard\", \"Marketing dashboard\",\n                \"1.0\");\n\n        getLivingApplicationAPI().createApplication(hrCreator);\n        final Application engineering = getLivingApplicationAPI().createApplication(engineeringCreator);\n        getLivingApplicationAPI().createApplication(marketingCreator);\n\n        //when\n        final SearchOptionsBuilder builder = getAppSearchBuilderOrderByToken(0, 10);\n        builder.filter(ApplicationSearchDescriptor.TOKEN, \"Engineering-dashboard\");\n\n        final SearchResult<IApplication> applications = getLivingApplicationAPI().searchIApplications(builder.done());\n        assertThat(applications).isNotNull();\n        assertThat(applications.getCount()).isEqualTo(1);\n        assertThat(applications.getResult()).containsExactly(engineering);\n    }\n\n    @Test\n    public void searchApplications_can_filter_on_display_name() throws Exception {\n        //given\n        final ApplicationCreator hrCreator = new ApplicationCreator(\"HR-dashboard\", \"HR dashboard\", \"1.0\");\n        final ApplicationCreator engineeringCreator = new ApplicationCreator(\"Engineering-dashboard\",\n                \"Engineering dashboard\", \"1.0\");\n        final ApplicationCreator marketingCreator = new ApplicationCreator(\"Marketing-dashboard\", \"Marketing dashboard\",\n                \"1.0\");\n\n        final Application hr = getLivingApplicationAPI().createApplication(hrCreator);\n        getLivingApplicationAPI().createApplication(engineeringCreator);\n        getLivingApplicationAPI().createApplication(marketingCreator);\n\n        //when\n        final SearchOptionsBuilder builder = getAppSearchBuilderOrderByToken(0, 10);\n        builder.filter(ApplicationSearchDescriptor.DISPLAY_NAME, \"HR dashboard\");\n\n        final SearchResult<IApplication> applications = getLivingApplicationAPI().searchIApplications(builder.done());\n        assertThat(applications).isNotNull();\n        assertThat(applications.getCount()).isEqualTo(1);\n        assertThat(applications.getResult()).containsExactly(hr);\n    }\n\n    @Test\n    public void searchApplications_can_filter_on_version() throws Exception {\n        //given\n        final ApplicationCreator hrCreator = new ApplicationCreator(\"HR-dashboard\", \"HR dash board\", \"2.0\");\n        final ApplicationCreator engineeringCreator = new ApplicationCreator(\"Engineering-dashboard\",\n                \"Engineering dashboard\", \"1.0\");\n        final ApplicationCreator marketingCreator = new ApplicationCreator(\"Marketing-dashboard\", \"Marketing dashboard\",\n                \"2.0\");\n\n        final Application hr = getLivingApplicationAPI().createApplication(hrCreator);\n        getLivingApplicationAPI().createApplication(engineeringCreator);\n        final Application marketing = getLivingApplicationAPI().createApplication(marketingCreator);\n\n        //when\n        final SearchOptionsBuilder builder = getAppSearchBuilderOrderByToken(0, 10);\n        builder.filter(ApplicationSearchDescriptor.VERSION, \"2.0\");\n\n        final SearchResult<IApplication> applications = getLivingApplicationAPI().searchIApplications(builder.done());\n        assertThat(applications).isNotNull();\n        assertThat(applications.getCount()).isEqualTo(2);\n        assertThat(applications.getResult()).containsExactly(hr, marketing);\n\n    }\n\n    @Test\n    public void searchApplications_can_filter_on_profileId() throws Exception {\n        //given\n        final Profile profile = getProfileUser();\n        final SearchOptionsBuilder builder = getAppSearchBuilderOrderByToken(0, 10);\n        long initialCount = getLivingApplicationAPI().searchIApplications(builder.done()).getCount();\n        builder.filter(ApplicationSearchDescriptor.PROFILE_ID, profile.getId());\n        final ApplicationCreator hrCreator = new ApplicationCreator(\"HR-dashboard\", \"HR dash board\", \"1.0\");\n        final ApplicationCreator engineeringCreator = new ApplicationCreator(\"Engineering-dashboard\",\n                \"Engineering dashboard\", \"1.0\");\n        engineeringCreator.setProfileId(profile.getId());\n        final ApplicationCreator marketingCreator = new ApplicationCreator(\"Marketing-dashboard\", \"Marketing dashboard\",\n                \"1.0\");\n\n        final Application hr = getLivingApplicationAPI().createApplication(hrCreator);\n        final Application engineering = getLivingApplicationAPI().createApplication(engineeringCreator);\n        final Application marketing = getLivingApplicationAPI().createApplication(marketingCreator);\n\n        //when\n\n        final SearchResult<IApplication> applications = getLivingApplicationAPI().searchIApplications(builder.done());\n        assertThat(applications).isNotNull();\n        assertThat(applications.getCount()).isEqualTo(initialCount + 1);\n        assertThat(applications.getResult()).contains(engineering);\n\n        getLivingApplicationAPI().deleteApplication(hr.getId());\n        getLivingApplicationAPI().deleteApplication(engineering.getId());\n        getLivingApplicationAPI().deleteApplication(marketing.getId());\n    }\n\n    @Test\n    public void searchApplications_can_use_search_term() throws Exception {\n        //given\n        final ApplicationCreator hrCreator = new ApplicationCreator(\"HR-dashboard\", \"My HR dashboard\", \"2.0\");\n        final ApplicationCreator engineeringCreator = new ApplicationCreator(\"Engineering-dashboard\",\n                \"Engineering dashboard\", \"1.0\");\n        final ApplicationCreator marketingCreator = new ApplicationCreator(\"My\", \"Marketing\", \"2.0\");\n\n        final Application hr = getLivingApplicationAPI().createApplication(hrCreator);\n        getLivingApplicationAPI().createApplication(engineeringCreator);\n        final Application marketing = getLivingApplicationAPI().createApplication(marketingCreator);\n\n        //when\n        final SearchOptionsBuilder builder = getAppSearchBuilderOrderByToken(0, 10);\n        builder.searchTerm(\"My\");\n\n        final SearchResult<IApplication> applications = getLivingApplicationAPI().searchIApplications(builder.done());\n        assertThat(applications).isNotNull();\n        assertThat(applications.getCount()).isEqualTo(2);\n        assertThat(applications.getResult()).containsExactly(hr, marketing);\n    }\n\n    @Test\n    public void should_create_update_replace_icon_of_application() throws Exception {\n        //Create application with icon\n        ApplicationCreator creator = new ApplicationCreator(\"appWithIcon\", \"An application having an icon\", \"1.0\");\n        creator.setIcon(\"icon.png\", \"PNG\\n\\t some png content\".getBytes());\n\n        Application applicationCreated = getLivingApplicationAPI().createApplication(creator);\n        assertThat(applicationCreated.hasIcon()).isTrue();\n        assertThat(getLivingApplicationAPI().getIconOfApplication(applicationCreated.getId())).satisfies(icon -> {\n            assertThat(new String(icon.getContent())).isEqualTo(\"PNG\\n\\t some png content\");\n            assertThat(icon.getMimeType()).isEqualTo(\"image/png\");\n        });\n\n        //replace icon of application\n        Thread.sleep(10);\n        Application applicationUpdated = getLivingApplicationAPI().updateApplication(applicationCreated.getId(),\n                new ApplicationUpdater().setIcon(\"toto.jpg\", \"jpeg content\".getBytes()));\n\n        assertThat(applicationUpdated.hasIcon()).isTrue();\n        assertThat(applicationUpdated.getLastUpdateDate()).isAfter(applicationCreated.getLastUpdateDate());\n        assertThat(getLivingApplicationAPI().getIconOfApplication(applicationUpdated.getId())).satisfies(icon -> {\n            assertThat(new String(icon.getContent())).isEqualTo(\"jpeg content\");\n            assertThat(icon.getMimeType()).isEqualTo(\"image/jpeg\");\n        });\n\n        // remove icon\n        Thread.sleep(10);\n        Application applicationWithIconRemoved = getLivingApplicationAPI().updateApplication(applicationCreated.getId(),\n                new ApplicationUpdater().setIcon(null, null));\n\n        assertThat(applicationWithIconRemoved.hasIcon()).isFalse();\n        assertThat(applicationWithIconRemoved.getLastUpdateDate()).isAfter(applicationUpdated.getLastUpdateDate());\n        assertThat(getLivingApplicationAPI().getIconOfApplication(applicationWithIconRemoved.getId())).isNull();\n    }\n\n    @Test\n    public void should_throw_not_found_when_getting_icon_of_unknown_application() {\n        assertThatThrownBy(() -> getLivingApplicationAPI().getIconOfApplication(543256432L))\n                .isInstanceOf(ApplicationNotFoundException.class);\n    }\n\n    private SearchOptions buildSearchOptions(String prefix, final int startIndex, final int maxResults) {\n        final SearchOptionsBuilder builder = getAppSearchBuilderOrderByToken(startIndex, maxResults);\n        builder.searchTerm(prefix);\n        return builder.done();\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/business/application/LivingApplicationImportExportIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.List;\n\nimport org.apache.commons.io.IOUtils;\nimport org.assertj.core.util.xml.XmlStringPrettyFormatter;\nimport org.bonitasoft.engine.api.ImportError;\nimport org.bonitasoft.engine.api.ImportStatus;\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.engine.profile.Profile;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.junit.Test;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class LivingApplicationImportExportIT extends TestWithLivingApplication {\n\n    private SearchOptions buildSearchOptions(final int startIndex, final int maxResults) {\n        return getAppSearchBuilderOrderById(startIndex, maxResults).done();\n    }\n\n    @Test\n    public void exportApplications_should_return_the_byte_content_of_xml_file_containing_selected_applications()\n            throws Exception {\n        //given\n        Profile userProfile = getProfileUser();\n\n        final byte[] applicationsByteArray = IOUtils\n                .toByteArray(LivingApplicationIT.class.getResourceAsStream(\"applications.xml\"));\n        final String xmlPrettyFormatExpected = XmlStringPrettyFormatter\n                .xmlPrettyFormat(new String(applicationsByteArray));\n\n        final ApplicationCreator hrCreator = new ApplicationCreator(\"HR-dashboard\", \"My HR dashboard\", \"2.0\");\n        hrCreator.setDescription(\"This is the HR dashboard.\");\n        hrCreator.setIconPath(\"/icon.jpg\");\n        hrCreator.setProfileId(userProfile.getId());\n\n        final ApplicationCreator engineeringCreator = new ApplicationCreator(\"Engineering-dashboard\",\n                \"Engineering dashboard\", \"1.0\");\n        final ApplicationCreator marketingCreator = new ApplicationCreator(\"My\", \"Marketing\", \"2.0\");\n        final Application hr = getLivingApplicationAPI().createApplication(hrCreator);\n\n        // Associate a new page to application hr (real page name is defined in zip/page.properties):\n        final Page myPage = getPageAPI().createPage(\"not_used\",\n                IOUtils.toByteArray(LivingApplicationIT.class.getResourceAsStream(\"dummy-bizapp-page.zip\")));\n        final ApplicationPage appPage = getLivingApplicationAPI().createApplicationPage(hr.getId(), myPage.getId(),\n                \"my-new-custom-page\");\n\n        // Add menus:\n        final ApplicationMenu menu = getLivingApplicationAPI()\n                .createApplicationMenu(new ApplicationMenuCreator(hr.getId(), \"HR follow-up\"));\n        final ApplicationMenuCreator subMenuCreator = new ApplicationMenuCreator(hr.getId(), \"Daily HR follow-up\",\n                appPage.getId());\n        subMenuCreator.setParentId(menu.getId());\n        getLivingApplicationAPI().createApplicationMenu(subMenuCreator);\n\n        getLivingApplicationAPI().createApplicationMenu(new ApplicationMenuCreator(hr.getId(), \"Empty menu\"));\n\n        // Add home page:\n        getLivingApplicationAPI().setApplicationHomePage(hr.getId(), appPage.getId());\n\n        getLivingApplicationAPI().createApplication(engineeringCreator);\n        final Application marketing = getLivingApplicationAPI().createApplication(marketingCreator);\n\n        //when\n        final byte[] exportedBytes = getLivingApplicationAPI().exportApplications(hr.getId(), marketing.getId());\n        final String xmlPrettyFormatExported = XmlStringPrettyFormatter.xmlPrettyFormat(new String(exportedBytes));\n\n        //then\n        assertThatXmlHaveNoDifferences(xmlPrettyFormatExpected, xmlPrettyFormatExported);\n\n        getLivingApplicationAPI().deleteApplication(hr.getId());\n        getPageAPI().deletePage(myPage.getId());\n    }\n\n    @Test\n    public void importApplications_should_create_all_applications_contained_by_xml_file_and_return_status_ok()\n            throws Exception {\n        //given\n        final Profile profile = getProfileUser();\n        long initialCount = getLivingApplicationAPI()\n                .searchIApplications(buildSearchOptions(0, 10)).getCount();\n\n        // create page necessary to import application hr (real page name is defined in zip/page.properties):\n        final Page myPage = getPageAPI().createPage(\"not_used\",\n                IOUtils.toByteArray(LivingApplicationIT.class.getResourceAsStream(\"dummy-bizapp-page.zip\")));\n        final Page defaultLayout = getPageAPI().getPageByName(DEFAULT_LAYOUT_NAME);\n        final Page defaultTheme = getPageAPI().getPageByName(DEFAULT_THEME_NAME);\n\n        final byte[] applicationsByteArray = IOUtils\n                .toByteArray(LivingApplicationIT.class.getResourceAsStream(\"applications.xml\"));\n\n        //when\n        final List<ImportStatus> importStatus = getLivingApplicationAPI().importApplications(applicationsByteArray,\n                ApplicationImportPolicy.FAIL_ON_DUPLICATES);\n\n        //then\n        assertThat(importStatus).hasSize(2);\n        assertIsAddOkStatus(importStatus.get(0), \"HR-dashboard\");\n        assertIsAddOkStatus(importStatus.get(1), \"My\");\n\n        // check applications were created\n        final SearchResult<IApplication> searchResult = getLivingApplicationAPI()\n                .searchIApplications(buildSearchOptions(0, 10));\n        assertThat(searchResult.getCount()).isEqualTo(initialCount + 2);\n        Application hrApp = (Application) searchResult.getResult().stream().filter(a -> a.getToken().contains(\"HR\"))\n                .findFirst().orElseThrow();\n        assertIsHRApplication(profile, defaultLayout, defaultTheme, hrApp);\n        assertIsMarketingApplication(\n                (Application) searchResult.getResult().stream().filter(a -> a.getToken().contains(\"My\"))\n                        .findFirst().orElseThrow());\n\n        //check pages were created\n        SearchOptionsBuilder builder = getAppSearchBuilderOrderById(0, 10);\n        builder.filter(ApplicationPageSearchDescriptor.APPLICATION_ID, hrApp.getId());\n        SearchResult<ApplicationPage> pageSearchResult = getLivingApplicationAPI()\n                .searchApplicationPages(builder.done());\n        assertThat(pageSearchResult.getCount()).isEqualTo(1);\n        ApplicationPage myNewCustomPage = pageSearchResult.getResult().get(0);\n        assertIsMyNewCustomPage(myPage, hrApp, myNewCustomPage);\n\n        //check home page\n        assertThat(hrApp.getHomePageId()).isEqualTo(myNewCustomPage.getId());\n\n        //check menu is created\n        builder = getAppSearchBuilderOrderById(0, 10);\n        builder.filter(ApplicationMenuSearchDescriptor.APPLICATION_ID, hrApp.getId());\n        SearchResult<ApplicationMenu> menuSearchResult = getLivingApplicationAPI()\n                .searchApplicationMenus(builder.done());\n        assertThat(menuSearchResult.getCount()).isEqualTo(3);\n        ApplicationMenu hrFollowUpMenu = menuSearchResult.getResult().get(0);\n        assertIsHrFollowUpMenu(hrFollowUpMenu);\n        assertIsDailyHrFollowUpMenu(menuSearchResult.getResult().get(1), hrFollowUpMenu, myNewCustomPage);\n        assertIsEmptyMenu(menuSearchResult.getResult().get(2));\n\n        getLivingApplicationAPI().deleteApplication(hrApp.getId());\n        getPageAPI().deletePage(myPage.getId());\n    }\n\n    @Test\n    public void importApplications_should_create_applications_contained_by_xml_file_and_return_error_if_there_is_unavailable_info()\n            throws Exception {\n        long initialCount = getLivingApplicationAPI()\n                .searchIApplications(buildSearchOptions(0, 10)).getCount();\n        //given\n        final byte[] applicationsByteArray = IOUtils.toByteArray(LivingApplicationIT.class\n                .getResourceAsStream(\"/org/bonitasoft/engine/business/application/applicationWithUnavailableInfo.xml\"));\n\n        // create page necessary to import application hr (real page name is defined in zip/page.properties):\n        final Page myPage = getPageAPI().createPage(\"not_used\",\n                IOUtils.toByteArray(LivingApplicationIT.class.getResourceAsStream(\"dummy-bizapp-page.zip\")));\n\n        //when\n        final List<ImportStatus> importStatus = getLivingApplicationAPI().importApplications(applicationsByteArray,\n                ApplicationImportPolicy.FAIL_ON_DUPLICATES);\n\n        //then\n        assertThat(importStatus).hasSize(1);\n        assertThat(importStatus.get(0).getName()).isEqualTo(\"HR-dashboard\");\n        assertThat(importStatus.get(0).getStatus()).isEqualTo(ImportStatus.Status.ADDED);\n        ImportError profileError = new ImportError(\"ThisProfileDoesNotExist\", ImportError.Type.PROFILE);\n        ImportError customPageError = new ImportError(\"custompage_notexists\", ImportError.Type.PAGE);\n        ImportError appPageError1 = new ImportError(\"will-not-be-imported\", ImportError.Type.APPLICATION_PAGE);\n        ImportError appPageError2 = new ImportError(\"never-existed\", ImportError.Type.APPLICATION_PAGE);\n        assertThat(importStatus.get(0).getErrors()).containsExactly(profileError, customPageError, appPageError1,\n                appPageError2);\n\n        // check applications were created\n        final SearchResult<IApplication> searchResult = getLivingApplicationAPI()\n                .searchIApplications(buildSearchOptions(0, 10));\n        assertThat(searchResult.getCount()).isEqualTo(initialCount + 1);\n        final Application app1 = (Application) searchResult.getResult().stream()\n                .filter(a -> a.getToken().contains(\"HR\"))\n                .findFirst().orElseThrow();\n        assertThat(app1.getToken()).isEqualTo(\"HR-dashboard\");\n        assertThat(app1.getVersion()).isEqualTo(\"2.0\");\n        assertThat(app1.getDisplayName()).isEqualTo(\"My HR dashboard\");\n        assertThat(app1.getDescription()).isEqualTo(\"This is the HR dashboard.\");\n        assertThat(app1.getIconPath()).isEqualTo(\"/icon.jpg\");\n        assertThat(app1.getState()).isEqualTo(\"ACTIVATED\");\n        assertThat(app1.getProfileId()).isNull();\n\n        //check only one application page was created\n        SearchOptionsBuilder builder = getAppSearchBuilderOrderById(0, 10);\n        builder.filter(ApplicationPageSearchDescriptor.APPLICATION_ID, app1.getId());\n        SearchResult<ApplicationPage> pageSearchResult = getLivingApplicationAPI()\n                .searchApplicationPages(builder.done());\n        assertThat(pageSearchResult.getCount()).isEqualTo(1);\n        assertThat(pageSearchResult.getResult().get(0).getToken()).isEqualTo(\"my-new-custom-page\");\n\n        builder = getAppSearchBuilderOrderById(0, 10);\n        builder.filter(ApplicationMenuSearchDescriptor.APPLICATION_ID, app1.getId());\n\n        //check three menus were created\n        SearchResult<ApplicationMenu> menuSearchResult = getLivingApplicationAPI()\n                .searchApplicationMenus(builder.done());\n        assertThat(menuSearchResult.getCount()).isEqualTo(3);\n        assertThat(menuSearchResult.getResult().get(0).getDisplayName()).isEqualTo(\"HR follow-up\");\n        assertThat(menuSearchResult.getResult().get(0).getIndex()).isEqualTo(1);\n        assertThat(menuSearchResult.getResult().get(1).getDisplayName()).isEqualTo(\"Daily HR follow-up\");\n        assertThat(menuSearchResult.getResult().get(1).getIndex()).isEqualTo(1);\n        assertThat(menuSearchResult.getResult().get(2).getDisplayName()).isEqualTo(\"Empty menu\");\n        assertThat(menuSearchResult.getResult().get(2).getIndex()).isEqualTo(2);\n\n        getLivingApplicationAPI().deleteApplication(app1.getId());\n        getPageAPI().deletePage(myPage.getId());\n    }\n\n    @Test\n    public void export_after_import_should_return_the_same_xml_file() throws Exception {\n        //given\n        long initialCount = getLivingApplicationAPI()\n                .searchIApplications(buildSearchOptions(0, 10)).getCount();\n        // create page necessary to import application hr (real page name is defined in zip/page.properties):\n        final Page myPage = getPageAPI().createPage(\"not_used\",\n                IOUtils.toByteArray(LivingApplicationIT.class.getResourceAsStream(\"dummy-bizapp-page.zip\")));\n\n        final byte[] importedByteArray = IOUtils.toByteArray(LivingApplicationIT.class\n                .getResourceAsStream(\"applications.xml\"));\n\n        getLivingApplicationAPI().importApplications(importedByteArray, ApplicationImportPolicy.FAIL_ON_DUPLICATES);\n        final SearchResult<IApplication> searchResult = getLivingApplicationAPI()\n                .searchIApplications(buildSearchOptions(0, 10));\n        assertThat(searchResult.getCount()).isEqualTo(initialCount + 2);\n\n        //when\n        Application hrApplication = (Application) searchResult.getResult().stream()\n                .filter(a -> a.getToken().contains(\"HR\"))\n                .findFirst().orElseThrow();\n        byte[] exportedByteArray = getLivingApplicationAPI().exportApplications(hrApplication.getId(),\n                searchResult.getResult().stream().filter(a -> a.getToken().contains(\"My\")).findFirst().orElseThrow()\n                        .getId());\n\n        //then\n        final String xmlPrettyFormatExpected = XmlStringPrettyFormatter.xmlPrettyFormat(new String(importedByteArray));\n        final String xmlPrettyFormatActual = XmlStringPrettyFormatter.xmlPrettyFormat(new String(exportedByteArray));\n        assertThatXmlHaveNoDifferences(xmlPrettyFormatExpected, xmlPrettyFormatActual);\n\n        getLivingApplicationAPI().deleteApplication(hrApplication.getId());\n        getPageAPI().deletePage(myPage.getId());\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/business/application/LivingApplicationMenuIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.fail;\n\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class LivingApplicationMenuIT extends TestWithCustomPage {\n\n    private Application application;\n\n    private ApplicationPage appPage;\n\n    @Override\n    @Before\n    public void setUp() throws Exception {\n        super.setUp();\n        application = getLivingApplicationAPI().createApplication(new ApplicationCreator(\"app\", \"My app\", \"1.0\"));\n        appPage = getLivingApplicationAPI().createApplicationPage(application.getId(), getPage().getId(), \"myPage\");\n    }\n\n    @Override\n    @After\n    public void tearDown() throws Exception {\n        getLivingApplicationAPI().deleteApplication(application.getId());\n        application = null;\n        appPage = null;\n        super.tearDown();\n    }\n\n    @Test\n    public void createApplicationMenu_ApplicationPage_should_return_applicationMenu_based_on_creator_and_should_manage_indexes()\n            throws Exception {\n        //given\n        final ApplicationMenuCreator creator = new ApplicationMenuCreator(application.getId(), \"Main\");\n\n        //when\n        final ApplicationMenu createdAppMenu = getLivingApplicationAPI().createApplicationMenu(creator);\n\n        //then\n        assertThat(createdAppMenu).isNotNull();\n        assertThat(createdAppMenu.getDisplayName()).isEqualTo(\"Main\");\n        assertThat(createdAppMenu.getApplicationId()).isEqualTo(application.getId());\n        assertThat(createdAppMenu.getApplicationPageId()).isNull();\n        assertThat(createdAppMenu.getIndex()).isEqualTo(1);\n        assertThat(createdAppMenu.getParentId()).isNull();\n        assertThat(createdAppMenu.getId()).isPositive();\n\n        //when\n        //create a second menu\n        ApplicationMenu index2Menu = getLivingApplicationAPI()\n                .createApplicationMenu(new ApplicationMenuCreator(application.getId(), \"second menu\"));\n\n        //then\n        // should have a index incremented\n        assertThat(index2Menu.getIndex()).isEqualTo(createdAppMenu.getIndex() + 1);\n\n    }\n\n    @Test\n    public void createApplicationMenu_with_applicationPage_should_return_ApplicationMenu_with_applicationPageId()\n            throws Exception {\n        //given\n        final ApplicationMenuCreator creator = new ApplicationMenuCreator(application.getId(), \"Main\", appPage.getId());\n\n        //when\n        final ApplicationMenu createdAppMenu = getLivingApplicationAPI().createApplicationMenu(creator);\n\n        //then\n        assertThat(createdAppMenu).isNotNull();\n        assertThat(createdAppMenu.getApplicationPageId()).isEqualTo(appPage.getId());\n    }\n\n    @Test\n    public void createApplicationMenu_with_parent_menu_should_return_ApplicationMenu_with_parentId_and_should_manage_indexes()\n            throws Exception {\n        //given\n        final ApplicationMenuCreator mainCreator = new ApplicationMenuCreator(application.getId(), \"Main\");\n        final ApplicationMenu mainMenu = getLivingApplicationAPI().createApplicationMenu(mainCreator);\n\n        final ApplicationMenuCreator childCreator = new ApplicationMenuCreator(application.getId(), \"Child\",\n                appPage.getId());\n        childCreator.setParentId(mainMenu.getId());\n\n        //when\n        final ApplicationMenu createdAppMenu = getLivingApplicationAPI().createApplicationMenu(childCreator);\n\n        //then\n        assertThat(createdAppMenu).isNotNull();\n        assertThat(createdAppMenu.getParentId()).isEqualTo(mainMenu.getId());\n        assertThat(createdAppMenu.getIndex()).isEqualTo(1);\n\n        //when\n        //create a child menu second menu\n        ApplicationMenuCreator secondChildCreator = new ApplicationMenuCreator(application.getId(), \"second child\",\n                appPage.getId());\n        secondChildCreator.setParentId(mainMenu.getId());\n        ApplicationMenu secondChild = getLivingApplicationAPI().createApplicationMenu(secondChildCreator);\n\n        //then\n        //should have incremented index\n        assertThat(secondChild.getIndex()).isEqualTo(2);\n\n    }\n\n    @Test\n    public void updateApplicationMenu_should_update_application_menu_based_on_updater() throws Exception {\n        //given\n        final ApplicationMenuCreator parentCreator = new ApplicationMenuCreator(application.getId(), \"Main\");\n        final ApplicationMenuCreator childCreator = new ApplicationMenuCreator(application.getId(), \"Child\");\n        final ApplicationMenu parentAppMenu = getLivingApplicationAPI().createApplicationMenu(parentCreator);\n        final ApplicationMenu childCreatedAppMenu = getLivingApplicationAPI().createApplicationMenu(childCreator);\n\n        ApplicationMenuUpdater updater = new ApplicationMenuUpdater();\n        updater.setApplicationPageId(appPage.getId());\n        updater.setParentId(parentAppMenu.getId());\n        updater.setDisplayName(\"Updated child\");\n\n        //when\n        ApplicationMenu updatedChildMenu = getLivingApplicationAPI().updateApplicationMenu(childCreatedAppMenu.getId(),\n                updater);\n\n        //then\n        assertThat(updatedChildMenu).isNotNull();\n        //updated:\n        assertThat(updatedChildMenu.getDisplayName()).isEqualTo(\"Updated child\");\n        assertThat(updatedChildMenu.getApplicationPageId()).isEqualTo(appPage.getId());\n        assertThat(updatedChildMenu.getParentId()).isEqualTo(parentAppMenu.getId());\n        assertThat(updatedChildMenu.getIndex()).isEqualTo(1); //because parent changed\n        //not changed:\n        assertThat(updatedChildMenu.getApplicationId()).isEqualTo(application.getId());\n\n        //given\n        //check it's possible to clean parent and application page\n        updater = new ApplicationMenuUpdater();\n        updater.setApplicationPageId(null);\n        updater.setParentId(null);\n\n        //when\n        updatedChildMenu = getLivingApplicationAPI().updateApplicationMenu(childCreatedAppMenu.getId(), updater);\n\n        //then\n        assertThat(updatedChildMenu).isNotNull();\n        assertThat(updatedChildMenu).isEqualTo(getLivingApplicationAPI().getApplicationMenu(updatedChildMenu.getId()));\n        // updated:\n        assertThat(updatedChildMenu.getApplicationPageId()).isNull();\n        assertThat(updatedChildMenu.getParentId()).isNull();\n        assertThat(updatedChildMenu.getIndex()).isEqualTo(2); //because parent changed\n        //not changed:\n        assertThat(updatedChildMenu.getDisplayName()).isEqualTo(\"Updated child\");\n        assertThat(updatedChildMenu.getApplicationId()).isEqualTo(application.getId());\n\n    }\n\n    @Test\n    public void updateApplicationMenu_index_should_organize_indexes_for_elements_having_the_same_parent()\n            throws Exception {\n        //given\n        final ApplicationMenu parentAppMenu1 = getLivingApplicationAPI()\n                .createApplicationMenu(new ApplicationMenuCreator(application.getId(), \"Main1\"));\n        final ApplicationMenu parentAppMenu2 = getLivingApplicationAPI()\n                .createApplicationMenu(new ApplicationMenuCreator(application.getId(), \"Main2\"));\n\n        final ApplicationMenuCreator childCreator1 = new ApplicationMenuCreator(application.getId(), \"Child\");\n        childCreator1.setParentId(parentAppMenu1.getId());\n        final ApplicationMenuCreator childCreator2 = new ApplicationMenuCreator(application.getId(), \"Child\");\n        childCreator2.setParentId(parentAppMenu1.getId());\n        final ApplicationMenuCreator childCreator3 = new ApplicationMenuCreator(application.getId(), \"Child\");\n        childCreator3.setParentId(parentAppMenu1.getId());\n\n        final ApplicationMenu childCreatedAppMenu1 = getLivingApplicationAPI().createApplicationMenu(childCreator1);\n        final ApplicationMenu childCreatedAppMenu2 = getLivingApplicationAPI().createApplicationMenu(childCreator2);\n        final ApplicationMenu childCreatedAppMenu3 = getLivingApplicationAPI().createApplicationMenu(childCreator3);\n\n        //when move up\n        ApplicationMenu updatedChildMenu = getLivingApplicationAPI().updateApplicationMenu(childCreatedAppMenu3.getId(),\n                new ApplicationMenuUpdater().setIndex(1));\n\n        //then\n        assertThat(updatedChildMenu.getIndex()).isEqualTo(1);\n        assertThat(getLivingApplicationAPI().getApplicationMenu(childCreatedAppMenu1.getId()).getIndex()).isEqualTo(2);\n        assertThat(getLivingApplicationAPI().getApplicationMenu(childCreatedAppMenu2.getId()).getIndex()).isEqualTo(3);\n\n        //when move down\n        updatedChildMenu = getLivingApplicationAPI().updateApplicationMenu(childCreatedAppMenu3.getId(),\n                new ApplicationMenuUpdater().setIndex(2));\n\n        //then\n        assertThat(updatedChildMenu.getIndex()).isEqualTo(2);\n        assertThat(getLivingApplicationAPI().getApplicationMenu(childCreatedAppMenu1.getId()).getIndex()).isEqualTo(1);\n        assertThat(getLivingApplicationAPI().getApplicationMenu(childCreatedAppMenu2.getId()).getIndex()).isEqualTo(3);\n\n        //when change parent\n        updatedChildMenu = getLivingApplicationAPI().updateApplicationMenu(childCreatedAppMenu3.getId(),\n                new ApplicationMenuUpdater().setParentId(parentAppMenu2.getId()));\n\n        //then\n        assertThat(updatedChildMenu.getIndex()).isEqualTo(1);\n        assertThat(getLivingApplicationAPI().getApplicationMenu(childCreatedAppMenu1.getId()).getIndex()).isEqualTo(1);\n        assertThat(getLivingApplicationAPI().getApplicationMenu(childCreatedAppMenu2.getId()).getIndex()).isEqualTo(2);\n\n    }\n\n    @Test\n    public void getApplicationMenu_should_return_the_applicationMenu_identified_by_the_given_id() throws Exception {\n        //given\n        final ApplicationMenuCreator creator = new ApplicationMenuCreator(application.getId(), \"Main\", appPage.getId());\n        final ApplicationMenu createdAppMenu = getLivingApplicationAPI().createApplicationMenu(creator);\n\n        //when\n        final ApplicationMenu retrievedMenu = getLivingApplicationAPI().getApplicationMenu(createdAppMenu.getId());\n\n        //then\n        assertThat(retrievedMenu).isNotNull();\n        assertThat(retrievedMenu).isEqualTo(createdAppMenu);\n\n    }\n\n    @Test\n    public void deleteApplicationMenu_should_remove_the_applicationMenu_identified_by_the_given_id() throws Exception {\n        //given\n        final ApplicationMenuCreator creator = new ApplicationMenuCreator(application.getId(), \"Main\", appPage.getId());\n        final ApplicationMenu createdAppMenu = getLivingApplicationAPI().createApplicationMenu(creator);\n\n        //when\n        getLivingApplicationAPI().deleteApplicationMenu(createdAppMenu.getId());\n\n        //then\n        verifyNotExists(createdAppMenu);\n    }\n\n    @Test\n    public void deleteApplicationMenu_should_remove_sub_menus() throws Exception {\n        //given\n        Application application = getLivingApplicationAPI()\n                .createApplication(new ApplicationCreator(\"app2\", \"My secpond app\", \"1.0\"));\n        ApplicationPage appPage = getLivingApplicationAPI().createApplicationPage(application.getId(),\n                getPage().getId(), \"myPage\");\n        ApplicationMenu mainMenu = getLivingApplicationAPI()\n                .createApplicationMenu(new ApplicationMenuCreator(application.getId(), \"Main\"));\n        ApplicationMenu mainMenu2 = getLivingApplicationAPI()\n                .createApplicationMenu(new ApplicationMenuCreator(application.getId(), \"Main2\"));\n        ApplicationMenu subMenu = getLivingApplicationAPI().createApplicationMenu(\n                new ApplicationMenuCreator(application.getId(), \"Main\", appPage.getId()).setParentId(mainMenu.getId()));\n\n        //when\n        getLivingApplicationAPI().deleteApplicationMenu(mainMenu.getId());\n\n        //then\n        verifyNotExists(mainMenu);\n        verifyNotExists(subMenu);\n        verifyExists(mainMenu2);\n        verifyExists(appPage);\n    }\n\n    @Test\n    public void deleteApplication_also_deletes_application_pages_and_applicationMenu() throws Exception {\n        //given\n        final Application application = getLivingApplicationAPI()\n                .createApplication(new ApplicationCreator(\"app2\", \"My secpond app\", \"1.0\"));\n        final ApplicationPage appPage = getLivingApplicationAPI().createApplicationPage(application.getId(),\n                getPage().getId(), \"myPage\");\n        final ApplicationMenu mainMenu = getLivingApplicationAPI()\n                .createApplicationMenu(new ApplicationMenuCreator(application.getId(), \"Main\"));\n        ApplicationMenuCreator subMenuCreator = new ApplicationMenuCreator(application.getId(), \"Main\",\n                appPage.getId());\n        subMenuCreator.setParentId(mainMenu.getId());\n        final ApplicationMenu subMenu = getLivingApplicationAPI().createApplicationMenu(subMenuCreator);\n\n        //when\n        getLivingApplicationAPI().deleteApplication(application.getId());\n\n        //then\n        verifyNotExists(mainMenu);\n        verifyNotExists(subMenu);\n        verifyNotExists(appPage);\n\n    }\n\n    @Test\n    public void deleteApplicationPage_also_deletes_related_applicationMenu() throws Exception {\n        //given\n        final ApplicationPage pageToDelete = getLivingApplicationAPI().createApplicationPage(application.getId(),\n                getPage().getId(), \"pageToDelete\");\n        final ApplicationPage pageToKeep = getLivingApplicationAPI().createApplicationPage(application.getId(),\n                getPage().getId(), \"pageToKeep\");\n        final ApplicationMenu menuToDelete = getLivingApplicationAPI().createApplicationMenu(\n                new ApplicationMenuCreator(application.getId(), \"Main\", pageToDelete.getId()));\n        final ApplicationMenu menuToKeep = getLivingApplicationAPI().createApplicationMenu(\n                new ApplicationMenuCreator(application.getId(), \"Main\", pageToKeep.getId()));\n        final ApplicationMenu containerMenu = getLivingApplicationAPI()\n                .createApplicationMenu(new ApplicationMenuCreator(application.getId(), \"Main\"));\n\n        //when\n        getLivingApplicationAPI().deleteApplicationPage(pageToDelete.getId());\n\n        //then\n        // container menu and menu related to another page are keep\n        verifyExists(containerMenu);\n        verifyExists(menuToKeep);\n\n        // menu related application is deleted\n        verifyNotExists(menuToDelete);\n    }\n\n    private void verifyExists(ApplicationMenu applicationMenu) throws ApplicationMenuNotFoundException {\n        ApplicationMenu retrievedMenu = getLivingApplicationAPI().getApplicationMenu(applicationMenu.getId());\n        assertThat(retrievedMenu).isNotNull();\n    }\n\n    private void verifyExists(ApplicationPage applicationPage) throws ApplicationPageNotFoundException {\n        ApplicationPage retrievedAppPage = getLivingApplicationAPI().getApplicationPage(applicationPage.getId());\n        assertThat(retrievedAppPage).isNotNull();\n    }\n\n    private void verifyNotExists(ApplicationMenu applicationMenu) {\n        try {\n            getLivingApplicationAPI().getApplicationMenu(applicationMenu.getId()); //throws exception\n            fail(\"exception expected\");\n        } catch (ApplicationMenuNotFoundException e) {\n            //OK\n        }\n    }\n\n    private void verifyNotExists(ApplicationPage applicationPage) {\n        try {\n            getLivingApplicationAPI().getApplicationPage(applicationPage.getId()); //throws exception\n            fail(\"exception expected\");\n        } catch (ApplicationPageNotFoundException e) {\n            //OK\n        }\n    }\n\n    @Test\n    public void searchApplicationMenus_without_filters_without_search_term_should_return_all_applicationMenues_pagged()\n            throws Exception {\n        //given\n        final ApplicationMenu menu1 = getLivingApplicationAPI()\n                .createApplicationMenu(new ApplicationMenuCreator(application.getId(), \"first\", appPage.getId()));\n        final ApplicationMenu menu2 = getLivingApplicationAPI()\n                .createApplicationMenu(new ApplicationMenuCreator(application.getId(), \"second\", appPage.getId()));\n        final ApplicationMenu menu3 = getLivingApplicationAPI()\n                .createApplicationMenu(new ApplicationMenuCreator(application.getId(), \"third\", appPage.getId()));\n\n        //when\n        final SearchResult<ApplicationMenu> firstPage = getLivingApplicationAPI()\n                .searchApplicationMenus(buildSearchOptions(0, 2).filter(\"applicationId\", application.getId()).done());\n        final SearchResult<ApplicationMenu> secondPage = getLivingApplicationAPI()\n                .searchApplicationMenus(buildSearchOptions(2, 2).filter(\"applicationId\", application.getId()).done());\n\n        //then\n        assertThat(firstPage).isNotNull();\n        assertThat(firstPage.getCount()).isEqualTo(3);\n        assertThat(firstPage.getResult()).containsExactly(menu1, menu2);\n        assertThat(secondPage).isNotNull();\n        assertThat(secondPage.getCount()).isEqualTo(3);\n        assertThat(secondPage.getResult()).containsExactly(menu3);\n    }\n\n    @Test\n    public void searchApplicationMenus_can_filter_on_displayname() throws Exception {\n        //given\n        getLivingApplicationAPI()\n                .createApplicationMenu(new ApplicationMenuCreator(application.getId(), \"first\", appPage.getId()));\n        final ApplicationMenu menu2 = getLivingApplicationAPI()\n                .createApplicationMenu(new ApplicationMenuCreator(application.getId(), \"second\", appPage.getId()));\n        getLivingApplicationAPI()\n                .createApplicationMenu(new ApplicationMenuCreator(application.getId(), \"third\", appPage.getId()));\n\n        //when\n        final SearchOptionsBuilder builder = getApplicationMenuSearchBuilder(0, 10);\n        builder.filter(ApplicationMenuSearchDescriptor.DISPLAY_NAME, \"second\");\n        final SearchResult<ApplicationMenu> searchResult = getLivingApplicationAPI()\n                .searchApplicationMenus(builder.done());\n\n        //then\n        assertThat(searchResult).isNotNull();\n        assertThat(searchResult.getCount()).isEqualTo(1);\n        assertThat(searchResult.getResult()).containsExactly(menu2);\n    }\n\n    @Test\n    public void searchApplicationMenus_can_filter_on_index() throws Exception {\n        //given\n        getLivingApplicationAPI()\n                .createApplicationMenu(new ApplicationMenuCreator(application.getId(), \"first\", appPage.getId()));\n        getLivingApplicationAPI()\n                .createApplicationMenu(new ApplicationMenuCreator(application.getId(), \"second\", appPage.getId()));\n        final ApplicationMenu menu3 = getLivingApplicationAPI()\n                .createApplicationMenu(new ApplicationMenuCreator(application.getId(), \"third\", appPage.getId()));\n\n        //when\n        final SearchOptionsBuilder builder = getApplicationMenuSearchBuilder(0, 10);\n        builder.filter(ApplicationMenuSearchDescriptor.INDEX, menu3.getIndex());\n        final SearchResult<ApplicationMenu> searchResult = getLivingApplicationAPI()\n                .searchApplicationMenus(builder.done());\n\n        //then\n        assertThat(searchResult).isNotNull();\n        assertThat(searchResult.getCount()).isEqualTo(1);\n        assertThat(searchResult.getResult()).containsExactly(menu3);\n    }\n\n    @Test\n    public void searchApplicationMenus_can_filter_on_applicationPageId() throws Exception {\n        //given\n        final ApplicationPage appPage2 = getLivingApplicationAPI().createApplicationPage(application.getId(),\n                getPage().getId(), \"mySecondPage\");\n        final ApplicationMenu menu1 = getLivingApplicationAPI()\n                .createApplicationMenu(new ApplicationMenuCreator(application.getId(), \"first\", appPage.getId()));\n        getLivingApplicationAPI()\n                .createApplicationMenu(new ApplicationMenuCreator(application.getId(), \"second\", appPage2.getId()));\n        final ApplicationMenu menu3 = getLivingApplicationAPI()\n                .createApplicationMenu(new ApplicationMenuCreator(application.getId(), \"third\", appPage.getId()));\n\n        //when\n        final SearchOptionsBuilder builder = getApplicationMenuSearchBuilder(0, 10);\n        builder.filter(ApplicationMenuSearchDescriptor.APPLICATION_PAGE_ID, appPage.getId());\n        final SearchResult<ApplicationMenu> searchResult = getLivingApplicationAPI()\n                .searchApplicationMenus(builder.done());\n\n        //then\n        assertThat(searchResult).isNotNull();\n        assertThat(searchResult.getCount()).isEqualTo(2);\n        assertThat(searchResult.getResult()).containsExactly(menu1, menu3);\n    }\n\n    @Test\n    public void searchApplicationMenus_can_filter_on_applicationId() throws Exception {\n        //given\n        final Application application2 = getLivingApplicationAPI()\n                .createApplication(new ApplicationCreator(\"app2\", \"My second app\", \"1.0\"));\n        final ApplicationPage appPage2 = getLivingApplicationAPI().createApplicationPage(application2.getId(),\n                getPage().getId(), \"mySecondPage\");\n\n        final ApplicationMenu menu1 = getLivingApplicationAPI()\n                .createApplicationMenu(new ApplicationMenuCreator(application2.getId(), \"first\", appPage2.getId()));\n        getLivingApplicationAPI()\n                .createApplicationMenu(new ApplicationMenuCreator(application.getId(), \"second\", appPage.getId()));\n        final ApplicationMenu menu3 = getLivingApplicationAPI()\n                .createApplicationMenu(new ApplicationMenuCreator(application2.getId(), \"third\", appPage2.getId()));\n\n        //when\n        final SearchOptionsBuilder builder = getApplicationMenuSearchBuilder(0, 10);\n        builder.filter(ApplicationMenuSearchDescriptor.APPLICATION_ID, application2.getId());\n        final SearchResult<ApplicationMenu> searchResult = getLivingApplicationAPI()\n                .searchApplicationMenus(builder.done());\n\n        //then\n        assertThat(searchResult).isNotNull();\n        assertThat(searchResult.getCount()).isEqualTo(2);\n        assertThat(searchResult.getResult()).containsExactly(menu1, menu3);\n    }\n\n    @Test\n    public void searchApplicationMenus_can_filter_on_parentId() throws Exception {\n        //given\n        final ApplicationMenu parentMenu = getLivingApplicationAPI()\n                .createApplicationMenu(new ApplicationMenuCreator(application.getId(), \"parent\"));\n\n        final ApplicationMenuCreator creator = new ApplicationMenuCreator(application.getId(), \"child\",\n                appPage.getId());\n        creator.setParentId(parentMenu.getId());\n        final ApplicationMenu childMenu = getLivingApplicationAPI().createApplicationMenu(creator);\n\n        getLivingApplicationAPI()\n                .createApplicationMenu(new ApplicationMenuCreator(application.getId(), \"third\", appPage.getId()));\n\n        //when\n        final SearchOptionsBuilder builder = getApplicationMenuSearchBuilder(0, 10);\n        builder.filter(ApplicationMenuSearchDescriptor.PARENT_ID, parentMenu.getId());\n        final SearchResult<ApplicationMenu> searchResult = getLivingApplicationAPI()\n                .searchApplicationMenus(builder.done());\n\n        //then\n        assertThat(searchResult).isNotNull();\n        assertThat(searchResult.getCount()).isEqualTo(1);\n        assertThat(searchResult.getResult()).containsExactly(childMenu);\n    }\n\n    @Test\n    public void searchApplicationMenus_can_use_searchTerm() throws Exception {\n        //given\n        getLivingApplicationAPI()\n                .createApplicationMenu(new ApplicationMenuCreator(application.getId(), \"first\", appPage.getId()));\n        final ApplicationMenu menu2 = getLivingApplicationAPI()\n                .createApplicationMenu(new ApplicationMenuCreator(application.getId(), \"second\", appPage.getId()));\n        getLivingApplicationAPI()\n                .createApplicationMenu(new ApplicationMenuCreator(application.getId(), \"third\", appPage.getId()));\n\n        //when\n        final SearchOptionsBuilder builder = getApplicationMenuSearchBuilder(0, 10);\n        builder.searchTerm(\"second\");\n        final SearchResult<ApplicationMenu> searchResult = getLivingApplicationAPI()\n                .searchApplicationMenus(builder.done());\n\n        //then\n        assertThat(searchResult).isNotNull();\n        assertThat(searchResult.getCount()).isEqualTo(1);\n        assertThat(searchResult.getResult()).containsExactly(menu2);\n    }\n\n    private SearchOptionsBuilder buildSearchOptions(final int startIndex, final int maxResults) {\n        final SearchOptionsBuilder builder = getApplicationMenuSearchBuilder(startIndex, maxResults);\n        return builder;\n    }\n\n    private SearchOptionsBuilder getApplicationMenuSearchBuilder(final int startIndex, final int maxResults) {\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(startIndex, maxResults);\n        builder.sort(ApplicationMenuSearchDescriptor.INDEX, Order.ASC);\n        return builder;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/business/application/LivingApplicationPageIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.fail;\n\nimport java.util.List;\n\nimport org.assertj.core.api.Assertions;\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.engine.profile.Profile;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.junit.Test;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Baptiste Mesta\n */\npublic class LivingApplicationPageIT extends TestWithCustomPage {\n\n    @Test\n    public void createApplicationPage_returns_applicationPage_based_on_the_given_parameters() throws Exception {\n        //given\n        final Application application = getLivingApplicationAPI()\n                .createApplication(new ApplicationCreator(\"app\", \"My app\", \"1.0\"));\n\n        //when\n        final ApplicationPage appPage = getLivingApplicationAPI().createApplicationPage(application.getId(),\n                getPage().getId(), \"firstPage\");\n\n        //then\n        assertThat(appPage.getId()).isGreaterThan(0);\n        assertThat(appPage.getApplicationId()).isEqualTo(application.getId());\n        assertThat(appPage.getPageId()).isEqualTo(getPage().getId());\n        assertThat(appPage.getToken()).isEqualTo(\"firstPage\");\n\n        getLivingApplicationAPI().deleteApplication(application.getId());\n\n    }\n\n    @Test\n    public void setApplicationHomePage_should_update_the_application_homePage() throws Exception {\n        //given\n        final Application application = getLivingApplicationAPI()\n                .createApplication(new ApplicationCreator(\"app\", \"My app\", \"1.0\"));\n        final ApplicationPage appPage = getLivingApplicationAPI().createApplicationPage(application.getId(),\n                getPage().getId(), \"firstPage\");\n\n        //when\n        getLivingApplicationAPI().setApplicationHomePage(application.getId(), appPage.getId());\n\n        //then\n        final Application upToDateApp = (Application) getLivingApplicationAPI().getApplication(application.getId());\n        assertThat(upToDateApp.getHomePageId()).isEqualTo(appPage.getId());\n\n        getLivingApplicationAPI().deleteApplication(application.getId());\n\n    }\n\n    @Test\n    public void updateApplication_should_update_home_page() throws Exception {\n        //given\n        final ApplicationCreator creator = new ApplicationCreator(\"My-Application\", \"My application display name\",\n                \"1.0\");\n        final Application application = getLivingApplicationAPI().createApplication(creator);\n        final ApplicationPage appPage = getLivingApplicationAPI().createApplicationPage(application.getId(),\n                getPage().getId(), \"firstPage\");\n\n        final ApplicationUpdater updater = new ApplicationUpdater();\n        updater.setHomePageId(appPage.getId());\n\n        //when\n        final Application updatedApplication = getLivingApplicationAPI().updateApplication(application.getId(),\n                updater);\n\n        //then\n        assertThat(updatedApplication).isNotNull();\n        assertThat(updatedApplication.getHomePageId()).isEqualTo(appPage.getId());\n\n        getLivingApplicationAPI().deleteApplication(application.getId());\n    }\n\n    @Test\n    public void getApplicationPage_byNameAndAppName_returns_the_applicationPage_corresponding_to_the_given_parameters()\n            throws Exception {\n        //given\n        final Application application = getLivingApplicationAPI()\n                .createApplication(new ApplicationCreator(\"app\", \"My app\", \"1.0\"));\n        final ApplicationPage appPage = getLivingApplicationAPI().createApplicationPage(application.getId(),\n                getPage().getId(), \"firstPage\");\n\n        //when\n        final ApplicationPage retrievedAppPage = getLivingApplicationAPI().getApplicationPage(application.getToken(),\n                appPage.getToken());\n\n        //then\n        assertThat(retrievedAppPage).isEqualTo(appPage);\n        getLivingApplicationAPI().deleteApplication(application.getId());\n    }\n\n    @Test\n    public void getApplicationPage_byId_returns_the_applicationPage_corresponding_to_the_given_Id() throws Exception {\n        //given\n        final Application application = getLivingApplicationAPI()\n                .createApplication(new ApplicationCreator(\"app\", \"My app\", \"1.0\"));\n        final ApplicationPage appPage = getLivingApplicationAPI().createApplicationPage(application.getId(),\n                getPage().getId(), \"firstPage\");\n\n        //when\n        final ApplicationPage retrievedAppPage = getLivingApplicationAPI().getApplicationPage(appPage.getId());\n\n        //then\n        assertThat(retrievedAppPage).isEqualTo(appPage);\n        getLivingApplicationAPI().deleteApplication(application.getId());\n    }\n\n    @Test\n    public void deleteApplication_should_also_delete_related_applicationPage() throws Exception {\n        //given\n        final Application application = getLivingApplicationAPI()\n                .createApplication(new ApplicationCreator(\"app\", \"My app\", \"1.0\"));\n        final ApplicationPage homePage = getLivingApplicationAPI().createApplicationPage(application.getId(),\n                getPage().getId(), \"firstPage\");\n        getLivingApplicationAPI().setApplicationHomePage(application.getId(), homePage.getId());\n        final ApplicationPage aAppPage = getLivingApplicationAPI().createApplicationPage(application.getId(),\n                getPage().getId(), \"secondPage\");\n\n        //when\n        getLivingApplicationAPI().deleteApplication(application.getId());\n\n        //then\n        verifyNotExists(homePage);\n        verifyNotExists(aAppPage);\n    }\n\n    private void verifyNotExists(ApplicationPage applicationPage) {\n        try {\n            getLivingApplicationAPI().getApplicationPage(applicationPage.getId()); //throws exception\n            Assertions.fail(\"exception expected\");\n        } catch (ApplicationPageNotFoundException e) {\n            //OK\n        }\n    }\n\n    @Test\n    public void deleteApplicationPage_should_delete_applicationPage_with_the_given_id() throws Exception {\n        //given\n        final Application application = getLivingApplicationAPI()\n                .createApplication(new ApplicationCreator(\"app\", \"My app\", \"1.0\"));\n        final ApplicationPage appPage = getLivingApplicationAPI().createApplicationPage(application.getId(),\n                getPage().getId(), \"firstPage\");\n\n        //when\n        getLivingApplicationAPI().deleteApplicationPage(appPage.getId());\n\n        //then\n        try {\n            getLivingApplicationAPI().getApplicationPage(appPage.getId());\n            fail(\"Not found expected\");\n        } catch (final ApplicationPageNotFoundException e) {\n            //OK\n        }\n    }\n\n    @Test\n    public void getApplicationHomePage_should_return_application_homePage() throws Exception {\n        //given\n        final Application application = getLivingApplicationAPI()\n                .createApplication(new ApplicationCreator(\"app\", \"My app\", \"1.0\"));\n        final ApplicationPage appPage = getLivingApplicationAPI().createApplicationPage(application.getId(),\n                getPage().getId(), \"firstPage\");\n        getLivingApplicationAPI().setApplicationHomePage(application.getId(), appPage.getId());\n\n        //when\n        final ApplicationPage homePage = getLivingApplicationAPI().getApplicationHomePage(application.getId());\n\n        //then\n        assertThat(homePage).isEqualTo(appPage);\n\n        getLivingApplicationAPI().deleteApplication(application.getId());\n\n    }\n\n    @Test\n    public void searchApplicationPages_without_filters_and_search_term_should_return_all_applicationPages_pagged()\n            throws Exception {\n        //given\n        final Application application = getLivingApplicationAPI()\n                .createApplication(new ApplicationCreator(\"app\", \"My app\", \"1.0\"));\n        final ApplicationPage appPage1 = getLivingApplicationAPI().createApplicationPage(application.getId(),\n                getPage().getId(), \"AAA_1\");\n        final ApplicationPage appPage2 = getLivingApplicationAPI().createApplicationPage(application.getId(),\n                getPage().getId(), \"AAA_2\");\n        final ApplicationPage appPage3 = getLivingApplicationAPI().createApplicationPage(application.getId(),\n                getPage().getId(), \"AAA_3\");\n\n        //when\n        final SearchResult<ApplicationPage> searchResultPage1 = getLivingApplicationAPI()\n                .searchApplicationPages(buildSearchOptions(\"AAA\", 0, 2));\n        final SearchResult<ApplicationPage> searchResultPage2 = getLivingApplicationAPI()\n                .searchApplicationPages(buildSearchOptions(\"AAA\", 2, 2));\n\n        //then\n        assertThat(searchResultPage1).isNotNull();\n        assertThat(searchResultPage1.getCount()).isEqualTo(3);\n        assertThat(searchResultPage1.getResult()).containsExactly(appPage1, appPage2);\n\n        assertThat(searchResultPage2).isNotNull();\n        assertThat(searchResultPage2.getCount()).isEqualTo(3);\n        assertThat(searchResultPage2.getResult()).containsExactly(appPage3);\n\n        getLivingApplicationAPI().deleteApplication(application.getId());\n    }\n\n    @Test\n    public void searchApplicationPages_can_filter_on_name() throws Exception {\n        //given\n        final Application application = getLivingApplicationAPI()\n                .createApplication(new ApplicationCreator(\"app\", \"My app\", \"1.0\"));\n        getLivingApplicationAPI().createApplicationPage(application.getId(), getPage().getId(), \"firstPage\");\n        final ApplicationPage appPage2 = getLivingApplicationAPI().createApplicationPage(application.getId(),\n                getPage().getId(), \"secondPage\");\n        getLivingApplicationAPI().createApplicationPage(application.getId(), getPage().getId(), \"thirdPage\");\n\n        //when\n        final SearchOptionsBuilder builder = getAppSearchBuilderOrderByToken(0, 10);\n        builder.filter(ApplicationPageSearchDescriptor.TOKEN, \"secondPage\");\n        final SearchResult<ApplicationPage> searchResult = getLivingApplicationAPI()\n                .searchApplicationPages(builder.done());\n\n        //then\n        assertThat(searchResult).isNotNull();\n        assertThat(searchResult.getCount()).isEqualTo(1);\n        assertThat(searchResult.getResult()).containsExactly(appPage2);\n\n        getLivingApplicationAPI().deleteApplication(application.getId());\n    }\n\n    @Test\n    public void searchApplicationPages_can_filter_on_applicationId() throws Exception {\n        //given\n        final Application application1 = getLivingApplicationAPI()\n                .createApplication(new ApplicationCreator(\"app1\", \"My app 1\", \"1.0\"));\n        final Application application2 = getLivingApplicationAPI()\n                .createApplication(new ApplicationCreator(\"app2\", \"My app 2\", \"1.0\"));\n        final ApplicationPage appPage1 = getLivingApplicationAPI().createApplicationPage(application1.getId(),\n                getPage().getId(), \"firstPage\");\n        getLivingApplicationAPI().createApplicationPage(application2.getId(), getPage().getId(), \"secondPage\");\n        final ApplicationPage appPage3 = getLivingApplicationAPI().createApplicationPage(application1.getId(),\n                getPage().getId(), \"thirdPage\");\n\n        //when\n        final SearchOptionsBuilder builder = getAppSearchBuilderOrderByToken(0, 10);\n        builder.filter(ApplicationPageSearchDescriptor.APPLICATION_ID, application1.getId());\n        final SearchResult<ApplicationPage> searchResult = getLivingApplicationAPI()\n                .searchApplicationPages(builder.done());\n\n        //then\n        assertThat(searchResult).isNotNull();\n        assertThat(searchResult.getCount()).isEqualTo(2);\n        assertThat(searchResult.getResult()).containsExactly(appPage1, appPage3);\n\n        getLivingApplicationAPI().deleteApplication(application1.getId());\n    }\n\n    @Test\n    public void searchApplicationPages_can_filter_on_pageId() throws Exception {\n        //given\n        final Page page2 = createPage(\"custompage_MyPage2\");\n        final Application application = getLivingApplicationAPI()\n                .createApplication(new ApplicationCreator(\"app\", \"My app\", \"1.0\"));\n        getLivingApplicationAPI().createApplicationPage(application.getId(), getPage().getId(), \"firstPage\");\n        final ApplicationPage appPage2 = getLivingApplicationAPI().createApplicationPage(application.getId(),\n                page2.getId(), \"secondPage\");\n        final ApplicationPage appPage3 = getLivingApplicationAPI().createApplicationPage(application.getId(),\n                page2.getId(), \"thirdPage\");\n\n        //when\n        final SearchOptionsBuilder builder = getAppSearchBuilderOrderByToken(0, 10);\n        builder.filter(ApplicationPageSearchDescriptor.PAGE_ID, page2.getId());\n        final SearchResult<ApplicationPage> searchResult = getLivingApplicationAPI()\n                .searchApplicationPages(builder.done());\n\n        //then\n        assertThat(searchResult).isNotNull();\n        assertThat(searchResult.getCount()).isEqualTo(2);\n        assertThat(searchResult.getResult()).containsExactly(appPage2, appPage3);\n\n        //clean\n        getLivingApplicationAPI().deleteApplication(application.getId());\n        getPageAPI().deletePage(page2.getId());\n    }\n\n    @Test\n    public void searchApplicationPages_can_filter_on_id() throws Exception {\n        //given\n        final Application application = getLivingApplicationAPI()\n                .createApplication(new ApplicationCreator(\"app\", \"My app\", \"1.0\"));\n        getLivingApplicationAPI().createApplicationPage(application.getId(), getPage().getId(), \"firstPage\");\n        final ApplicationPage appPage2 = getLivingApplicationAPI().createApplicationPage(application.getId(),\n                getPage().getId(), \"secondPage\");\n        getLivingApplicationAPI().createApplicationPage(application.getId(), getPage().getId(), \"thirdPage\");\n\n        //when\n        final SearchOptionsBuilder builder = getAppSearchBuilderOrderByToken(0, 10);\n        builder.filter(ApplicationPageSearchDescriptor.ID, appPage2.getId());\n        final SearchResult<ApplicationPage> searchResult = getLivingApplicationAPI()\n                .searchApplicationPages(builder.done());\n\n        //then\n        assertThat(searchResult).isNotNull();\n        assertThat(searchResult.getCount()).isEqualTo(1);\n        assertThat(searchResult.getResult()).containsExactly(appPage2);\n\n        getLivingApplicationAPI().deleteApplication(application.getId());\n    }\n\n    private SearchOptions buildSearchOptions(String prefix, final int startIndex, final int maxResults) {\n        return getAppSearchBuilderOrderByToken(startIndex, maxResults)\n                .searchTerm(prefix).done();\n    }\n\n    @Test\n    public void getAllAccessiblePageForAProfile() throws Exception {\n        //given\n        //profile1\n        Profile profile1 = getProfileUser();\n        //app1\n        final Application app1 = getLivingApplicationAPI()\n                .createApplication(new ApplicationCreator(\"app1\", \"My app1\", \"1.0\").setProfileId(profile1.getId()));\n        final Page page1 = createPage(\"custompage_page1\");\n        getLivingApplicationAPI().createApplicationPage(app1.getId(), page1.getId(), \"appPage1\");\n\n        final Page page2 = createPage(\"custompage_page2\");\n        getLivingApplicationAPI().createApplicationPage(app1.getId(), page2.getId(), \"appPage2\");\n\n        //app2\n        final Application app2 = getLivingApplicationAPI()\n                .createApplication(new ApplicationCreator(\"app2\", \"My app2\", \"1.0\").setProfileId(profile1.getId()));\n        final Page page3 = createPage(\"custompage_page3\");\n        getLivingApplicationAPI().createApplicationPage(app2.getId(), page3.getId(), \"appPage1\");\n\n        //profile2\n        Profile profile2 = getProfileAdmin();\n        //app3\n        final Application app3 = getLivingApplicationAPI()\n                .createApplication(new ApplicationCreator(\"app3\", \"My app3\", \"1.0\").setProfileId(profile2.getId()));\n        final Page page4 = createPage(\"custompage_page4\");\n        getLivingApplicationAPI().createApplicationPage(app3.getId(), page4.getId(), \"appPage1\");\n\n        //when\n        List<String> allPagesForProfile1 = getLivingApplicationAPI().getAllPagesForProfile(profile1.getId());\n        List<String> allPagesForProfile2 = getLivingApplicationAPI().getAllPagesForProfile(profile2.getId());\n\n        //then\n        assertThat(allPagesForProfile1).containsExactlyInAnyOrder(\"custompage_themeBonita\", \"custompage_layoutBonita\",\n                \"custompage_page1\", \"custompage_page2\", \"custompage_page3\");\n        assertThat(getLivingApplicationAPI().getAllPagesForProfile(profile1.getName())).containsExactlyInAnyOrder(\n                \"custompage_themeBonita\", \"custompage_layoutBonita\", \"custompage_page1\", \"custompage_page2\",\n                \"custompage_page3\");\n        assertThat(allPagesForProfile2).containsExactlyInAnyOrder(\"custompage_themeBonita\", \"custompage_layoutBonita\",\n                \"custompage_page4\");\n        assertThat(getLivingApplicationAPI().getAllPagesForProfile(profile2.getName()))\n                .containsExactlyInAnyOrder(\"custompage_themeBonita\", \"custompage_layoutBonita\", \"custompage_page4\");\n\n        //clean\n        getLivingApplicationAPI().deleteApplication(app1.getId());\n        getLivingApplicationAPI().deleteApplication(app2.getId());\n        getLivingApplicationAPI().deleteApplication(app3.getId());\n        getPageAPI().deletePage(page1.getId());\n        getPageAPI().deletePage(page2.getId());\n        getPageAPI().deletePage(page3.getId());\n        getPageAPI().deletePage(page4.getId());\n\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/business/data/BDMPostgreSQLTextFieldIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assume.assumeTrue;\n\nimport java.sql.Connection;\nimport java.sql.DatabaseMetaData;\nimport java.sql.ResultSet;\n\nimport javax.naming.Context;\nimport javax.sql.DataSource;\n\nimport org.bonitasoft.engine.CommonAPIIT;\nimport org.bonitasoft.engine.api.TenantAdministrationAPI;\nimport org.bonitasoft.engine.bdm.BusinessObjectModelConverter;\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.model.field.FieldType;\nimport org.bonitasoft.engine.bdm.model.field.SimpleField;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * Integration test to verify that TEXT fields in BDM are correctly mapped to PostgreSQL TEXT columns\n * instead of OID columns (which was the default behavior in Hibernate 5.6.2+).\n * This test validates the fix for the PostgreSQL TEXT field handling in EntityCodeGenerator\n * which uses @Type(type=\"org.bonitasoft.engine.persistence.PostgresMaterializedClobType) annotation\n * to force TEXT column type.\n */\npublic class BDMPostgreSQLTextFieldIT extends CommonAPIIT {\n\n    private static final String BDM_PACKAGE_PREFIX = \"com.company.model\";\n    private static final String DOCUMENT_BO = \"Document\";\n    private TenantAdministrationAPI tenantAdministrationAPI;\n\n    @Before\n    public void setUp() throws Exception {\n        loginWithTechnicalUser();\n        tenantAdministrationAPI = getTenantAdministrationAPI();\n\n        // Only run this test on PostgreSQL\n        String dbVendor = System.getProperty(\"sysprop.bonita.bdm.db.vendor\", \"h2\");\n        assumeTrue(\"This test only runs on PostgreSQL\", dbVendor.toLowerCase().contains(\"postgres\"));\n    }\n\n    @After\n    public void cleanup() throws Exception {\n        cleanAndUninstallBusinessDataModel();\n        logout();\n    }\n\n    @Test\n    public void should_create_TEXT_column_for_TEXT_fields_in_postgresql() throws Exception {\n        // given: a BDM with a TEXT field\n        final BusinessObject documentBO = new BusinessObject();\n        documentBO.setQualifiedName(BDM_PACKAGE_PREFIX + \".\" + DOCUMENT_BO);\n\n        final SimpleField contentField = new SimpleField();\n        contentField.setName(\"content\");\n        contentField.setType(FieldType.TEXT);\n        contentField.setNullable(true);\n        documentBO.addField(contentField);\n\n        final SimpleField descriptionField = new SimpleField();\n        descriptionField.setName(\"description\");\n        descriptionField.setType(FieldType.TEXT);\n        descriptionField.setNullable(false);\n        documentBO.addField(descriptionField);\n\n        final BusinessObjectModel businessObjectModel = new BusinessObjectModel();\n        businessObjectModel.addBusinessObject(documentBO);\n\n        // when: deploy the BDM\n        final byte[] zip = new BusinessObjectModelConverter().zip(businessObjectModel);\n        pauseTenantIfNeeded();\n        tenantAdministrationAPI.cleanAndUninstallBusinessDataModel();\n        final String businessDataModelVersion = tenantAdministrationAPI.updateBusinessDataModel(zip);\n        resumeTenant();\n\n        assertThat(businessDataModelVersion).as(\"should have deployed BDM\").isNotNull();\n\n        // then: verify column types in PostgreSQL database\n        verifyColumnType(\"document\", \"content\", \"text\");\n        verifyColumnType(\"document\", \"description\", \"text\");\n    }\n\n    private void verifyColumnType(String tableName, String columnName, String expectedType) throws Exception {\n        // Get BDM datasource from JNDI\n        Context initialContext = new javax.naming.InitialContext();\n        DataSource dataSource = (DataSource) initialContext.lookup(\"java:comp/env/NotManagedBizDataDS\");\n\n        try (Connection connection = dataSource.getConnection()) {\n            DatabaseMetaData metaData = connection.getMetaData();\n\n            // Query column information from PostgreSQL\n            try (ResultSet rs = metaData.getColumns(null, null, tableName, columnName)) {\n                assertThat(rs.next())\n                        .as(\"Column \" + columnName + \" should exist in table \" + tableName)\n                        .isTrue();\n\n                String typeName = rs.getString(\"TYPE_NAME\");\n                assertThat(typeName.toLowerCase())\n                        .as(\"Column \" + columnName + \" should be of type TEXT, not OID\")\n                        .isEqualTo(expectedType);\n            }\n        }\n    }\n\n    private void cleanAndUninstallBusinessDataModel() throws Exception {\n        pauseTenantIfNeeded();\n        final String version = tenantAdministrationAPI.getBusinessDataModelVersion();\n        if (version != null) {\n            tenantAdministrationAPI.cleanAndUninstallBusinessDataModel();\n        }\n        resumeTenant();\n        assertThat(tenantAdministrationAPI.getBusinessDataModelVersion()).as(\"should remove BDM\").isNull();\n    }\n\n    private void pauseTenantIfNeeded() throws Exception {\n        if (!tenantAdministrationAPI.isPaused()) {\n            tenantAdministrationAPI.pause();\n        }\n    }\n\n    private void resumeTenant() throws Exception {\n        if (tenantAdministrationAPI.isPaused()) {\n            tenantAdministrationAPI.resume();\n        }\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/business/data/BDMUpdateIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.time.OffsetDateTime;\n\nimport org.bonitasoft.engine.CommonAPIIT;\nimport org.bonitasoft.engine.api.TenantAdministrationAPI;\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.model.field.FieldType;\nimport org.bonitasoft.engine.bdm.model.field.RelationField;\nimport org.bonitasoft.engine.bdm.model.field.SimpleField;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.tenant.TenantResource;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * Those tests fail on MySQL and SQLServer because after installing the second BDM version, Hibernate is not aware of\n * previous FK_ that stay in database, so it does not drop them, and then the 'drop table' fails on test cleanup.\n * Those cases should be handled when we support more \"BDM update\" scenarios, leveraging Hibernate poor level of\n * support on HBM2DDL.\n */\npublic class BDMUpdateIT extends CommonAPIIT {\n\n    public static final String DOT = \".\";\n    public static final String PARENT_BO = \"ParentBO\";\n    public static final String CHILD_BO = \"ChildBO\";\n    public static final String OTHER_CHILD_BO = \"OtherChildBO\";\n    private static final String BDM_PACKAGE_PREFIX = \"com.company.model\";\n    private TenantAdministrationAPI tenantAdministrationAPI;\n\n    @Before\n    public void setUp() throws Exception {\n        loginWithTechnicalUser();\n        tenantAdministrationAPI = getTenantAdministrationAPI();\n    }\n\n    // No @After needed: CommonAPIIT.clean() handles BDM and logout\n\n    @Test\n    public void should_change_single_aggregation_relation() throws Exception {\n        checkSingleRelation(RelationField.Type.AGGREGATION);\n    }\n\n    @Test\n    public void should_change_single_composition_relation() throws Exception {\n        checkSingleRelation(RelationField.Type.COMPOSITION);\n    }\n\n    @Test\n    public void should_change_multiple_aggregation_relation() throws Exception {\n        checkMultipleRelation(RelationField.Type.AGGREGATION);\n    }\n\n    @Test\n    public void should_change_multiple_composition_relation() throws Exception {\n        checkMultipleRelation(RelationField.Type.COMPOSITION);\n    }\n\n    protected void checkSingleRelation(RelationField.Type relationType) throws Exception {\n        // given\n        final BusinessObject businessObject = getBusinessObject(PARENT_BO);\n        businessObject.addField(getSingleRelationField(getBusinessObject(CHILD_BO), relationType));\n        final String version = installBusinessDataModel(getBusinessObjectModel(businessObject));\n        ensureBDMIsInstalled(version);\n\n        // when\n        uninstallBusinessDataModel();\n        final BusinessObject newBusinessObject = getBusinessObject(PARENT_BO);\n        newBusinessObject.addField(getSingleRelationField(getBusinessObject(OTHER_CHILD_BO), relationType));\n        final String newVersion = installBusinessDataModel(getBusinessObjectModel(newBusinessObject));\n\n        // then\n        assertThat(newVersion).isNotNull().isNotEqualTo(version);\n    }\n\n    protected void checkMultipleRelation(RelationField.Type relationType) throws Exception {\n        // given\n        final BusinessObject businessObject = getBusinessObject(PARENT_BO);\n        businessObject.addField(getMultipleRelationField(getBusinessObject(CHILD_BO), relationType));\n        final String version = installBusinessDataModel(getBusinessObjectModel(businessObject));\n        ensureBDMIsInstalled(version);\n\n        // when\n        uninstallBusinessDataModel();\n\n        final BusinessObject newBusinessObject = getBusinessObject(PARENT_BO);\n        newBusinessObject.addField(getMultipleRelationField(getBusinessObject(OTHER_CHILD_BO), relationType));\n        final String newVersion = installBusinessDataModel(getBusinessObjectModel(newBusinessObject));\n\n        // then\n        assertThat(newVersion).isNotNull().isNotEqualTo(version);\n    }\n\n    protected void resumeTenant() throws Exception {\n        if (tenantAdministrationAPI.isPaused()) {\n            tenantAdministrationAPI.resume();\n        }\n    }\n\n    protected BusinessObjectModel getBusinessObjectModel(BusinessObject parentBusinessObject) {\n        final BusinessObjectModel businessObjectModel = new BusinessObjectModel();\n        businessObjectModel.addBusinessObject(parentBusinessObject);\n        addChildBusinessObjects(businessObjectModel);\n        return businessObjectModel;\n    }\n\n    protected void addChildBusinessObjects(BusinessObjectModel businessObjectModel1) {\n        businessObjectModel1.addBusinessObject(getBusinessObject(BDMUpdateIT.CHILD_BO));\n        businessObjectModel1.addBusinessObject(getBusinessObject(BDMUpdateIT.OTHER_CHILD_BO));\n    }\n\n    private BusinessObject getBusinessObject(String boName) {\n        final BusinessObject businessObject = new BusinessObject();\n        businessObject.setQualifiedName(BDM_PACKAGE_PREFIX + DOT + boName);\n        businessObject.addField(getSimpleField());\n        return businessObject;\n    }\n\n    private RelationField getSingleRelationField(BusinessObject businessObject, RelationField.Type relationType) {\n        return getRelationField(businessObject, relationType, \"single\", Boolean.FALSE);\n    }\n\n    private RelationField getMultipleRelationField(BusinessObject businessObject, RelationField.Type relationType) {\n        return getRelationField(businessObject, relationType, \"multiple\", Boolean.TRUE);\n    }\n\n    private RelationField getRelationField(BusinessObject businessObject, RelationField.Type relationType, String name,\n            Boolean isCollection) {\n        final RelationField relationField = new RelationField();\n        relationField.setType(relationType);\n        relationField.setFetchType(RelationField.FetchType.LAZY);\n        relationField.setName(name);\n        relationField.setCollection(isCollection);\n        relationField.setNullable(Boolean.TRUE);\n        relationField.setReference(businessObject);\n        return relationField;\n    }\n\n    private SimpleField getSimpleField() {\n        SimpleField simpleField = new SimpleField();\n        simpleField.setName(\"name\");\n        simpleField.setType(FieldType.STRING);\n        return simpleField;\n    }\n\n    private void uninstallBusinessDataModel() throws Exception {\n        loginWithTechnicalUser();\n        pauseTenantIfNeeded();\n        final String version = tenantAdministrationAPI.getBusinessDataModelVersion();\n        if (version != null) {\n            tenantAdministrationAPI.uninstallBusinessDataModel();\n        }\n        resumeTenant();\n        assertThat(tenantAdministrationAPI.getBusinessDataModelVersion()).as(\"should uninstall BusinessDataModel\")\n                .isNull();\n\n    }\n\n    private void pauseTenantIfNeeded() throws UpdateException {\n        if (!tenantAdministrationAPI.isPaused()) {\n            tenantAdministrationAPI.pause();\n        }\n    }\n\n    private void ensureBDMIsInstalled(String bdmVersion) {\n        assertThat(bdmVersion).as(\"should have deployed BDM\").isNotNull();\n        TenantResource tenantResource = tenantAdministrationAPI.getBusinessDataModelResource();\n        assertThat(tenantResource.getLastUpdateDate()).isAfter(OffsetDateTime.now().minusMinutes(1));\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/business/data/BDRepositoryIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data;\n\nimport static java.util.Collections.singletonList;\nimport static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;\nimport static org.apache.commons.lang3.StringUtils.substringAfter;\nimport static org.apache.commons.lang3.StringUtils.substringBefore;\nimport static org.assertj.core.api.Assertions.*;\nimport static org.bonitasoft.engine.test.BDMTestUtil.*;\nimport static org.bonitasoft.engine.test.BuildTestUtil.generateConnectorImplementation;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.Serializable;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Set;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicReference;\nimport java.util.stream.IntStream;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipInputStream;\n\nimport net.javacrumbs.jsonunit.core.Option;\nimport org.apache.commons.io.FileUtils;\nimport org.apache.commons.io.IOUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.bonitasoft.engine.CommonAPIIT;\nimport org.bonitasoft.engine.api.APIClient;\nimport org.bonitasoft.engine.bdm.BusinessObjectDAOFactory;\nimport org.bonitasoft.engine.bdm.BusinessObjectModelConverter;\nimport org.bonitasoft.engine.bdm.dao.BusinessObjectDAO;\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.model.field.FieldType;\nimport org.bonitasoft.engine.bdm.model.field.RelationField;\nimport org.bonitasoft.engine.bdm.model.field.SimpleField;\nimport org.bonitasoft.engine.bpm.bar.BarResource;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.businessdata.BusinessDataQueryMetadata;\nimport org.bonitasoft.engine.bpm.businessdata.BusinessDataQueryResult;\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.bpm.contract.Type;\nimport org.bonitasoft.engine.bpm.data.DataInstance;\nimport org.bonitasoft.engine.bpm.document.DocumentNotFoundException;\nimport org.bonitasoft.engine.bpm.document.DocumentValue;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.TimerType;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ConfigurationState;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.Problem;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessEnablementException;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.bpm.process.impl.CallActivityBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.CatchMessageEventTriggerDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.IntermediateThrowEventDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.StartEventDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.SubProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.ThrowMessageEventTriggerBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder;\nimport org.bonitasoft.engine.command.CommandExecutionException;\nimport org.bonitasoft.engine.command.CommandNotFoundException;\nimport org.bonitasoft.engine.command.CommandParameterizationException;\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\nimport org.bonitasoft.engine.exception.UnavailableLockException;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.expression.ExpressionConstants;\nimport org.bonitasoft.engine.expression.ExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.ExpressionType;\nimport org.bonitasoft.engine.expression.InvalidExpressionException;\nimport org.bonitasoft.engine.expression.impl.ExpressionImpl;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.io.IOUtil;\nimport org.bonitasoft.engine.operation.LeftOperandBuilder;\nimport org.bonitasoft.engine.operation.Operation;\nimport org.bonitasoft.engine.operation.OperationBuilder;\nimport org.bonitasoft.engine.operation.OperatorType;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.test.APITestUtil;\nimport org.bonitasoft.engine.test.BuildTestUtil;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.rules.TemporaryFolder;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class BDRepositoryIT extends CommonAPIIT {\n\n    private static final Logger log = LoggerFactory.getLogger(BDRepositoryIT.class);\n    private static final String BUSINESS_DATA_CLASS_NAME_ID_FIELD = \"/businessdata/{className}/{id}/{field}\";\n    private static final String ENTITY_CLASS_NAME = \"entityClassName\";\n    private static String bdmDeployedVersion = \"0\";\n    private static int iterator = 1;\n\n    @Rule\n    public TemporaryFolder temporaryFolder = new TemporaryFolder();\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n    private User testUser;\n    private File clientFolder;\n    private BusinessObjectModel model;\n\n    @Before\n    public void setUp() throws Exception {\n        clientFolder = temporaryFolder.newFolder();\n        loginWithTechnicalUser();\n        testUser = createUser(\"testUser\", \"bpm\");\n\n        assertThat(getTenantAdministrationAPI().isPaused()).as(\"should not have tenant is paused mode\").isFalse();\n\n        model = buildBOM();\n\n        installAndVerifyBusinessDataModel(model);\n\n        assertThat(getTenantAdministrationAPI().isPaused())\n                .as(\"should have resume tenant after installing Business Object Model\").isFalse();\n    }\n\n    @After\n    public void tearDown() throws Exception {\n        try {\n            FileUtils.deleteDirectory(clientFolder);\n        } catch (final Exception e) {\n            clientFolder.deleteOnExit();\n        }\n        if (!getTenantAdministrationAPI().isPaused()) {\n            getTenantAdministrationAPI().pause();\n        }\n        getTenantAdministrationAPI().cleanAndUninstallBusinessDataModel();\n        getTenantAdministrationAPI().resume();\n\n        deleteUser(testUser);\n        logout();\n    }\n\n    @Test\n    public void deploying_bdm_with_invalid_query_should_throw_a_BDM_deployment_exception() throws Exception {\n        getTenantAdministrationAPI().pause();\n        getTenantAdministrationAPI().cleanAndUninstallBusinessDataModel();\n        assertThatThrownBy(\n                () -> getTenantAdministrationAPI().updateBusinessDataModel(getZip(buildBOMWithInvalidQuery())))\n                .isInstanceOf(BusinessDataRepositoryDeploymentException.class);\n    }\n\n    @Test\n    public void deploying_a_bdm_with_unique_constraint_on_multiple_field_should_fail_and_allow_to_deploy_a_clean_bdm_afterwards()\n            throws Exception {\n        // create bom - invalid\n        final SimpleField name = new SimpleField();\n        name.setName(\"name\");\n        name.setCollection(true);\n        name.setType(FieldType.STRING);\n\n        final BusinessObject countryBO = new BusinessObject();\n        countryBO.setQualifiedName(COUNTRY_QUALIFIED_NAME);\n        countryBO.addField(name);\n        countryBO.addUniqueConstraint(\"uk_name\", \"name\");\n\n        final BusinessObjectModel bom = new BusinessObjectModel();\n        bom.addBusinessObject(countryBO);\n\n        //test sequence - install should fail (Invalid BDM)\n        getTenantAdministrationAPI().pause();\n        getTenantAdministrationAPI().cleanAndUninstallBusinessDataModel();\n        // the exception should be InvalidBusinessDataModelException\n        assertThatThrownBy(() -> getTenantAdministrationAPI().updateBusinessDataModel(getZip(bom)))\n                .isInstanceOf(BusinessDataRepositoryDeploymentException.class)\n                .hasMessageContaining(\"Unable to create unique key constraint\");\n        assertThat(getTenantAdministrationAPI().getBusinessDataModelVersion()).isNull();\n\n        // remove unique constraint & try to re-install - should work\n        countryBO.setUniqueConstraints(Collections.emptyList());\n        getTenantAdministrationAPI().updateBusinessDataModel(getZip(bom));\n        assertThat(getTenantAdministrationAPI().getBusinessDataModelVersion()).isNotNull();\n        getTenantAdministrationAPI().resume();\n    }\n\n    @Test\n    public void deploying_bdm_after_process_should_put_process_in_resolved_state() throws Exception {\n        final String qualifiedName = \"com.company.test.Bo\";\n        final BusinessObjectModel bom = buildSimpleBom(qualifiedName);\n\n        final ProcessDefinition processDefinition = deploySimpleProcessWithBusinessData(qualifiedName);\n\n        ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertThat(processDeploymentInfo.getConfigurationState()).isEqualTo(ConfigurationState.UNRESOLVED);\n\n        installAndVerifyBusinessDataModel(bom);\n\n        processDeploymentInfo = getProcessAPI().getProcessDeploymentInfo(processDefinition.getId());\n        assertThat(processDeploymentInfo.getConfigurationState()).isEqualTo(ConfigurationState.RESOLVED);\n\n        deleteProcess(processDefinition);\n    }\n\n    @Test\n    public void should_keep_old_bdm_in_case_of_update_bdm_error() throws Exception {\n        final AddressRef ref1 = new AddressRef(\"newYorkAddr\", \"33, corner street\", \"NY\");\n        final AddressRef ref2 = new AddressRef(\"romeAddr\", \"2, plaza del popolo\", \"Roma\");\n        addEmployee(\"Marcel\", \"Pagnol\", ref1, ref2);\n\n        final BusinessObjectModelConverter businessObjectConverter = new BusinessObjectModelConverter();\n\n        final String processContractInputName = \"name_input\";\n\n        final Expression countryExpression = new ExpressionBuilder().createGroovyScriptExpression(\n                \"createNewCountry\",\n                \"import \" +\n                        COUNTRY_QUALIFIED_NAME +\n                        \"; Country c = new Country(); c.name = \" + processContractInputName + \"; return c;\",\n                COUNTRY_QUALIFIED_NAME,\n                new ExpressionBuilder().createContractInputExpression(processContractInputName,\n                        String.class.getName()));\n\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance(\n                \"shouldKeepOldBdmInCaseOfUpdateBdmError\", \"6.3-beta\");\n        final String businessDataName = \"countryName\";\n        final String newCountryName = \"France\";\n        processDefinitionBuilder.addBusinessData(businessDataName, COUNTRY_QUALIFIED_NAME, countryExpression);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addStartEvent(\"start\");\n        processDefinitionBuilder.addUserTask(\"step0\", ACTOR_NAME);\n        processDefinitionBuilder.addEndEvent(\"end\");\n        processDefinitionBuilder.addTransition(\"start\", \"step0\");\n        processDefinitionBuilder.addTransition(\"step0\", \"end\");\n\n        processDefinitionBuilder.addContract().addInput(processContractInputName, Type.TEXT, null);\n\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),\n                ACTOR_NAME, testUser);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcessWithInputs(definition.getId(),\n                Collections.singletonMap(processContractInputName, newCountryName));\n\n        waitForUserTask(\"step0\");\n\n        Map<Expression, Map<String, Serializable>> expressions = new HashMap<>(1);\n\n        getTenantAdministrationAPI().pause();\n\n        final String modelVersionInDatabase = getTenantAdministrationAPI().getBusinessDataModelVersion();\n\n        final BusinessObject countryBO = model.getBusinessObjects().stream()\n                .filter(bo -> bo.getQualifiedName().equals(COUNTRY_QUALIFIED_NAME)).findFirst().get();\n\n        final SimpleField population = new SimpleField();\n        population.setName(\"population\");\n        population.setType(FieldType.STRING);\n        countryBO.addField(population);\n\n        final BusinessObject employeeBO = model.getBusinessObjects().stream()\n                .filter(bo -> bo.getQualifiedName().equals(EMPLOYEE_QUALIFIED_NAME)).findFirst().get();\n\n        ((RelationField) employeeBO.getField(\"addresses\")).setType(RelationField.Type.COMPOSITION);\n\n        try {\n            getTenantAdministrationAPI().updateBusinessDataModel(businessObjectConverter.zip(model));\n            fail(\"should not be able to update the bdm\");\n        } catch (BusinessDataRepositoryDeploymentException ignored) {\n            log.error(\"ignored \", ignored);\n        }\n\n        assertThat(modelVersionInDatabase).isEqualTo(getTenantAdministrationAPI().getBusinessDataModelVersion());\n\n        getTenantAdministrationAPI().resume();\n\n        final String expressionPopulation = \"bizDataExprName\";\n        expressions.put(new ExpressionBuilder().createGroovyScriptExpression(expressionPopulation,\n                businessDataName + \".population\", String.class.getName(),\n                new ExpressionBuilder().createBusinessDataExpression(businessDataName, COUNTRY_QUALIFIED_NAME)), null);\n\n        try {\n            getProcessAPI()\n                    .evaluateExpressionsOnProcessInstance(processInstance.getId(), expressions);\n            fail(\"population field should not exist\");\n        } catch (ExpressionEvaluationException ignored) {\n            log.error(\"ignored population field\", ignored);\n        }\n\n        disableAndDeleteProcess(definition.getId());\n    }\n\n    @Test\n    public void should_not_fail_when_resuming_tenant_during_install_of_bdm() throws Exception {\n        getTenantAdministrationAPI().pause();\n        getTenantAdministrationAPI().cleanAndUninstallBusinessDataModel();\n        Future<String> installOfTheBDM = Executors.newSingleThreadExecutor().submit(() -> {\n            try {\n                return getTenantAdministrationAPI().updateBusinessDataModel(getZip(businessObjectModel(bom -> {\n                    bom.addBusinessObject(businessObject(\"com.company.ExampleBusinessObject\", (businessObject) -> {\n                        businessObject.addField(stringField(\"aField\"));\n                        businessObject.addQuery(\"findExampleByAField\", \"SELECT e FROM ExampleBusinessObject e\",\n                                \"com.company.ExampleBusinessObject\");\n                    }));\n                })));\n            } catch (Exception e) {\n                throw new RuntimeException(e);\n            }\n        });\n        log.info(\"Sleeping 80 ms to let installBusinessDataModel() begin before trying to resume tenant\");\n        Thread.sleep(80);\n        getTenantAdministrationAPI().resume();\n        installOfTheBDM.get(10, TimeUnit.SECONDS);\n        assertThatNoException().isThrownBy(() -> getBusinessDataByQuery(Collections.emptyMap(), 0, 10,\n                \"findExampleByAField\", \"com.company.ExampleBusinessObject\"));\n    }\n\n    private void installAndVerifyBusinessDataModel(final BusinessObjectModel bom) throws Exception {\n        var businessDataModelVersion = installBusinessDataModel(bom);\n        assertThat(businessDataModelVersion).as(\"should have deployed BDM\").isNotNull();\n        verifyBdmIsWellDeployed();\n    }\n\n    private void verifyBdmIsWellDeployed() throws Exception {\n        final String businessDataModelVersion = getTenantAdministrationAPI().getBusinessDataModelVersion();\n        APITestUtil.LOGGER.warn(\"previous businessDataModelVersion:\" + bdmDeployedVersion);\n        APITestUtil.LOGGER.warn(\"new businessDataModelVersion     :\" + businessDataModelVersion);\n        assertThat(businessDataModelVersion).as(\"should have deployed a new version of BDM\")\n                .isNotEqualTo(bdmDeployedVersion);\n        bdmDeployedVersion = businessDataModelVersion;\n    }\n\n    private ProcessDefinition deploySimpleProcessWithBusinessData(final String aQualifiedName) throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"test\", \"1.2-alpha\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        final String bizDataName = \"myBizData\";\n        processDefinitionBuilder.addBusinessData(bizDataName, aQualifiedName, null);\n\n        final ProcessDefinition processDefinition = deployProcess(\n                new BusinessArchiveBuilder().createNewBusinessArchive()\n                        .setProcessDefinition(processDefinitionBuilder.done()).done());\n        getProcessAPI().addUserToActor(ACTOR_NAME, processDefinition, testUser.getId());\n        return processDefinition;\n    }\n\n    @Test\n    public void shouldBeAbleToUpdateBusinessDataUsingBizDataJavaSetterOperation() throws Exception {\n        final String processContractInputName = \"lastName_input\";\n        final String initialLastNameValue = \"Trebi\";\n        final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression(\n                \"createNewEmployee\",\n                \"import \" +\n                        EMPLOYEE_QUALIFIED_NAME +\n                        \"; Employee e = new Employee(); e.firstName = 'Jules'; e.lastName = \"\n                        + processContractInputName + \"; return e;\",\n                EMPLOYEE_QUALIFIED_NAME, new ExpressionBuilder().createContractInputExpression(processContractInputName,\n                        String.class.getName()));\n\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance(\n                \"shouldBeAbleToUpdateBusinessDataUsingJavaSetterOperation\", \"6.3-beta\");\n        final String businessDataName = \"newBornBaby\";\n        final String newEmployeeFirstName = \"Manon\";\n        final String newEmployeeLastName = \"Péuigrec\";\n        processDefinitionBuilder.addBusinessData(businessDataName, EMPLOYEE_QUALIFIED_NAME, employeeExpression);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addUserTask(\"step0\", ACTOR_NAME);\n        processDefinitionBuilder\n                .addAutomaticTask(\"step1\")\n                .addOperation(\n                        new OperationBuilder().createBusinessDataSetAttributeOperation(businessDataName, \"setFirstName\",\n                                String.class.getName(),\n                                new ExpressionBuilder().createConstantStringExpression(newEmployeeFirstName)))\n                .addOperation(\n                        new OperationBuilder().createBusinessDataSetAttributeOperation(businessDataName, \"setLastName\",\n                                String.class.getName(),\n                                new ExpressionBuilder().createConstantStringExpression(newEmployeeLastName)));\n        processDefinitionBuilder.addUserTask(\"step2\", ACTOR_NAME);\n        processDefinitionBuilder.addTransition(\"step0\", \"step1\");\n        processDefinitionBuilder.addTransition(\"step1\", \"step2\");\n        processDefinitionBuilder.addContract().addInput(processContractInputName, Type.TEXT, null);\n\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),\n                ACTOR_NAME, testUser);\n        final ProcessInstance processInstance = getProcessAPI().startProcessWithInputs(definition.getId(),\n                Collections.singletonMap(processContractInputName, initialLastNameValue));\n\n        final long step0 = waitForUserTask(processInstance, \"step0\");\n\n        // Check that initial BizData value used process contract input:\n        Map<Expression, Map<String, Serializable>> expressions = new HashMap<>(1);\n        final String expressionName = \"bizDataExprName\";\n        expressions.put(new ExpressionBuilder().createGroovyScriptExpression(expressionName,\n                businessDataName + \".lastName\", String.class.getName(),\n                new ExpressionBuilder().createBusinessDataExpression(businessDataName, EMPLOYEE_QUALIFIED_NAME)), null);\n        final String returnedInitialLastName = (String) getProcessAPI()\n                .evaluateExpressionsOnProcessInstance(processInstance.getId(), expressions).get(\n                        expressionName);\n        assertThat(returnedInitialLastName).isEqualTo(initialLastNameValue);\n\n        assignAndExecuteStep(step0, testUser);\n        final long step2 = waitForUserTask(processInstance, \"step2\");\n\n        // Let's check the updated firstName + lastName values by calling an expression:\n        expressions = new HashMap<>(2);\n        final String expressionFirstName = \"retrieve_FirstName\";\n        expressions.put(new ExpressionBuilder().createGroovyScriptExpression(expressionFirstName,\n                businessDataName + \".firstName\", String.class.getName(),\n                new ExpressionBuilder().createBusinessDataExpression(businessDataName, EMPLOYEE_QUALIFIED_NAME)), null);\n        final String expressionLastName = \"retrieve_new_lastName\";\n        expressions.put(new ExpressionBuilder().createGroovyScriptExpression(expressionLastName,\n                businessDataName + \".lastName\", String.class.getName(),\n                new ExpressionBuilder().createBusinessDataExpression(businessDataName, EMPLOYEE_QUALIFIED_NAME)), null);\n        final Map<String, Serializable> evaluatedExpressions = getProcessAPI()\n                .evaluateExpressionsOnProcessInstance(processInstance.getId(), expressions);\n        final String returnedFirstName = (String) evaluatedExpressions.get(expressionFirstName);\n        final String returnedLastName = (String) evaluatedExpressions.get(expressionLastName);\n        assertThat(returnedFirstName).isEqualTo(newEmployeeFirstName);\n        assertThat(returnedLastName).isEqualTo(newEmployeeLastName);\n\n        assertCount(processInstance.getId());\n\n        assignAndExecuteStep(step2, testUser);\n\n        disableAndDeleteProcess(definition.getId());\n    }\n\n    @Test\n    public void deployABDRAndCreateADefaultBusinessDataAndReuseReference() throws Exception {\n        final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression(\"createNewEmployee\",\n                \"import \" +\n                        EMPLOYEE_QUALIFIED_NAME +\n                        \"; Employee e = new Employee(); e.firstName = 'Jane'; e.lastName = 'Doe'; return e;\",\n                EMPLOYEE_QUALIFIED_NAME);\n\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"test\", \"1.2-alpha\");\n        processDefinitionBuilder.addBusinessData(\"myEmployee\", EMPLOYEE_QUALIFIED_NAME, employeeExpression);\n        final String secondBizData = \"people\";\n        processDefinitionBuilder.addBusinessData(secondBizData, EMPLOYEE_QUALIFIED_NAME, null);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addUserTask(\"step1\", ACTOR_NAME).addOperation(\n                new OperationBuilder().attachBusinessDataSetAttributeOperation(secondBizData,\n                        new ExpressionBuilder().createQueryBusinessDataExpression(\n                                \"oneEmployee\", \"Employee.\" + GET_EMPLOYEE_BY_LAST_NAME_QUERY_NAME,\n                                EMPLOYEE_QUALIFIED_NAME,\n                                new ExpressionBuilder().createConstantStringExpression(\"lastName\", \"Doe\"))));\n        processDefinitionBuilder.addUserTask(\"step2\", ACTOR_NAME);\n        processDefinitionBuilder.addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),\n                ACTOR_NAME, testUser);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId());\n\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n        final String employeeToString = getEmployeeToString(\"myEmployee\", processInstance.getId());\n        assertThat(employeeToString).isEqualTo(\"Employee [firstName=Jane, lastName=Doe]\");\n\n        assignAndExecuteStep(step1Id, testUser);\n        waitForUserTask(processInstance, \"step2\");\n        final String people = getEmployeeToString(secondBizData, processInstance.getId());\n        assertThat(people).isEqualTo(\"Employee [firstName=Jane, lastName=Doe]\");\n\n        disableAndDeleteProcess(definition.getId());\n    }\n\n    @Test\n    public void deployABDRAndCreateABOAndUpdateThroughAGroovyScript() throws Exception {\n        final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression(\"createNewEmployee\",\n                \"import \" +\n                        EMPLOYEE_QUALIFIED_NAME +\n                        \"; Employee e = new Employee(); e.firstName = 'John'; e.lastName = 'Doe'; return e;\",\n                EMPLOYEE_QUALIFIED_NAME);\n\n        final Expression getEmployeeExpression = new ExpressionBuilder().createBusinessDataExpression(\"myEmployee\",\n                EMPLOYEE_QUALIFIED_NAME);\n        // try to modify the business data\n        final Expression scriptExpression = new ExpressionBuilder().createGroovyScriptExpression(\"updateBizData\",\n                \"myEmployee.lastName = 'BPM'; return 'BPM'\",\n                String.class.getName(), getEmployeeExpression);\n\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"test\", \"1.2-alpha\");\n        processDefinitionBuilder.addBusinessData(\"myEmployee\", EMPLOYEE_QUALIFIED_NAME, employeeExpression);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addUserTask(\"step1\", ACTOR_NAME).addDisplayDescription(scriptExpression);\n\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),\n                ACTOR_NAME, testUser);\n        final ProcessInstance instance = getProcessAPI().startProcess(definition.getId());\n        waitForUserTask(instance, \"step1\");\n\n        Thread.sleep(100); // this is a small wait in order to avoid issue on sql server when transaction has more than one XA resource\n        final String employeeToString = getEmployeeToString(\"myEmployee\", instance.getId());\n        assertThat(employeeToString).isEqualTo(\"Employee [firstName=John, lastName=BPM]\");\n\n        disableAndDeleteProcess(definition.getId());\n    }\n\n    @Test\n    public void updatingABOThroughAGroovyScriptShouldUpdateTheBO() throws Exception {\n        String businessDataName = \"myEmployee\";\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"BS-18402\", \"7.7.2\");\n        processDefinitionBuilder.addBusinessData(businessDataName, EMPLOYEE_QUALIFIED_NAME,\n                new ExpressionBuilder().createGroovyScriptExpression(\"createNewEmployee\",\n                        \"import \" + EMPLOYEE_QUALIFIED_NAME\n                                + \"; Employee e = new Employee(); e.firstName = 'John'; e.lastName = 'Doe'; return e;\",\n                        EMPLOYEE_QUALIFIED_NAME));\n        processDefinitionBuilder.addBusinessData(\"myAddress\", ADDRESS_QUALIFIED_NAME,\n                new ExpressionBuilder().createGroovyScriptExpression(\"CreateNewAddress\", \"import \" +\n                        ADDRESS_QUALIFIED_NAME\n                        + \"; Address a = new Address(); a.street='32, rue Gustave Eiffel'; a.city='Grenoble'; return a;\",\n                        ADDRESS_QUALIFIED_NAME));\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addAutomaticTask(\"step1\")\n                .addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand(businessDataName),\n                        OperatorType.ASSIGNMENT, null, null,\n                        new ExpressionBuilder().createGroovyScriptExpression(\"setEmployee\", \"import \" +\n                                EMPLOYEE_QUALIFIED_NAME + \"; myEmployee.setAddress(myAddress); return myEmployee;\",\n                                EMPLOYEE_QUALIFIED_NAME,\n                                new ExpressionBuilder().createBusinessDataExpression(\"myAddress\",\n                                        ADDRESS_QUALIFIED_NAME),\n                                new ExpressionBuilder().createBusinessDataExpression(businessDataName,\n                                        EMPLOYEE_QUALIFIED_NAME)));\n        processDefinitionBuilder.addUserTask(\"step2\", ACTOR_NAME);\n        processDefinitionBuilder.addStartEvent(\"Start\");\n        processDefinitionBuilder.addEndEvent(\"End\");\n        processDefinitionBuilder.addTransition(\"Start\", \"step1\");\n        processDefinitionBuilder.addTransition(\"step1\", \"step2\");\n        processDefinitionBuilder.addTransition(\"step2\", \"End\");\n        processDefinitionBuilder.addContextEntry(\"myEmployee_context_key\",\n                new ExpressionBuilder().createGroovyScriptExpression(\"retrieveEmployeeAddressBasicInfo\",\n                        \"\\\"Employee [ address street = \\\" + \" + businessDataName + \".address.street + \\\" ]\\\";\",\n                        String.class.getName(),\n                        new ExpressionBuilder().createBusinessDataExpression(businessDataName,\n                                EMPLOYEE_QUALIFIED_NAME)));\n\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),\n                ACTOR_NAME, testUser);\n        final ProcessInstance instance = getProcessAPI().startProcess(definition.getId());\n        waitForUserTask(instance.getId(), \"step2\");\n\n        final Serializable employeeFromContext = getProcessAPI().getProcessInstanceExecutionContext(instance.getId())\n                .get(\"myEmployee_context_key\");\n        assertThat(employeeFromContext).isEqualTo(\"Employee [ address street = 32, rue Gustave Eiffel ]\");\n\n        disableAndDeleteProcess(definition.getId());\n    }\n\n    @Test(expected = ProcessEnablementException.class)\n    public void deployProcessWithWrongBusinessDataTypeShouldNotBeDeployable() throws Exception {\n        final User user = createUser(\"login1\", \"password\");\n        ProcessDefinition processDefinition = null;\n        try {\n            final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                    .createNewInstance(\"firstProcess\", \"1.0\");\n            processBuilder.addActor(\"myActor\");\n            processBuilder.addBusinessData(\"myBizData\", Long.class.getName(),\n                    new ExpressionBuilder().createConstantLongExpression(12L));\n            processBuilder.addUserTask(\"Request\", \"myActor\");\n            processDefinition = getProcessAPI().deploy(processBuilder.done());\n            addUserToFirstActorOfProcess(user.getId(), processDefinition);\n            getProcessAPI().enableProcess(processDefinition.getId());\n            // Should not fail here, if the Server process model is valid:\n        } finally {\n            deleteProcess(processDefinition);\n            deleteUser(user);\n        }\n    }\n\n    @Test\n    public void deployABDRAndExecuteAGroovyScriptWhichContainsAPOJOFromTheBDR() throws Exception {\n        final Expression stringExpression = new ExpressionBuilder()\n                .createGroovyScriptExpression(\n                        \"alive\",\n                        \"import \" +\n                                EMPLOYEE_QUALIFIED_NAME +\n                                \"; Employee e = new Employee(); e.firstName = 'John'; e.lastName = 'Doe'; return \\\"Employee [firstName=\\\" + e.firstName + \\\", lastName=\\\" + e.lastName + \\\"]\\\"\",\n                        String.class.getName());\n        final Map<Expression, Map<String, Serializable>> expressions = new HashMap<>();\n        expressions.put(stringExpression, new HashMap<String, Serializable>());\n\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"test\", \"1.2-alpha\");\n        processDefinitionBuilder.addAutomaticTask(\"stepO\");\n        final ProcessDefinition processDefinition = getProcessAPI().deploy(processDefinitionBuilder.done());\n        getProcessAPI().enableProcess(processDefinition.getId());\n        final Map<String, Serializable> result = getProcessAPI()\n                .evaluateExpressionsOnProcessDefinition(processDefinition.getId(), expressions);\n        assertThat(result).hasSize(1);\n\n        final Set<Entry<String, Serializable>> entrySet = result.entrySet();\n        final Entry<String, Serializable> entry = entrySet.iterator().next();\n        assertThat(entry.getValue()).isEqualTo(\"Employee [firstName=John, lastName=Doe]\");\n\n        disableAndDeleteProcess(processDefinition.getId());\n    }\n\n    @Test(expected = BonitaRuntimeException.class)\n    public void createAnEmployeeWithARequiredFieldAtNullThrowsAnException() throws Exception {\n        final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression(\"createNewEmployee\",\n                \"import \" + EMPLOYEE_QUALIFIED_NAME +\n                        \"; Employee e = new Employee(); e.firstName = 'John'; return e;\",\n                EMPLOYEE_QUALIFIED_NAME);\n\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"test\", \"1.2-alpha\");\n        processDefinitionBuilder.addBusinessData(\"myEmployee\", EMPLOYEE_QUALIFIED_NAME, employeeExpression);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addUserTask(\"step1\", ACTOR_NAME);\n\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),\n                ACTOR_NAME, testUser);\n        try {\n            getProcessAPI().startProcess(definition.getId());\n        } finally {\n            disableAndDeleteProcess(definition.getId());\n        }\n    }\n\n    @Test(expected = BonitaRuntimeException.class)\n    public void createAnEmployeeWithATooSmallFieldAtNullThrowsAnException() throws Exception {\n        final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression(\"createNewEmployee\",\n                \"import \" + EMPLOYEE_QUALIFIED_NAME +\n                        \"; Employee e = new Employee(); e.firstName = 'John124578/'; e.lastName = 'Doe'; return e;\",\n                EMPLOYEE_QUALIFIED_NAME);\n\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"test\", \"1.2-alpha\");\n        processDefinitionBuilder.addBusinessData(\"myEmployee\", EMPLOYEE_QUALIFIED_NAME, employeeExpression);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addUserTask(\"step1\", ACTOR_NAME);\n\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),\n                ACTOR_NAME, testUser);\n        try {\n            getProcessAPI().startProcess(definition.getId());\n        } finally {\n            disableAndDeleteProcess(definition.getId());\n        }\n    }\n\n    @Test\n    public void updateBusinessDataShouldWorkOutsideATransaction() throws Exception {\n        final String taskName = \"step\";\n\n        final ProcessDefinition definition = buildProcessThatUpdateBizDataInsideConnector(taskName);\n        final ProcessInstance instance = getProcessAPI().startProcess(definition.getId());\n        waitForUserTask(instance, taskName);\n\n        final String employeeToString = getEmployeeToString(\"myEmployee\", instance.getId());\n        assertThat(employeeToString).isEqualTo(\"Employee [firstName=John, lastName=Hakkinen]\");\n\n        assertCount(instance.getId());\n        disableAndDeleteProcess(definition);\n    }\n\n    @Test\n    public void should_deploy_generate_client_bdm_zip() throws Exception {\n        ArrayList<String> entries = listEntries(getTenantAdministrationAPI().getClientBDMZip());\n\n        assertThat(entries).containsOnly(\"README.md\", \"example-pom.xml\", \"bdm-dao.jar\", \"bdm-model.jar\", \"bom.zip\");\n    }\n\n    private ArrayList<String> listEntries(byte[] clientBDMZip) throws IOException {\n        ArrayList<String> entries = new ArrayList<>();\n        ZipInputStream zipInputStream = new ZipInputStream(new ByteArrayInputStream(clientBDMZip));\n        ZipEntry nextEntry;\n        while ((nextEntry = zipInputStream.getNextEntry()) != null) {\n            entries.add(nextEntry.getName());\n        }\n        zipInputStream.close();\n        return entries;\n    }\n\n    @Test\n    public void should_undeploy_delete_generate_client_bdm_zip() throws Exception {\n        loginWithTechnicalUser();\n        getTenantAdministrationAPI().pause();\n        getTenantAdministrationAPI().cleanAndUninstallBusinessDataModel();\n        getTenantAdministrationAPI().resume();\n        expectedException.expect(BusinessDataRepositoryException.class);\n        getTenantAdministrationAPI().getClientBDMZip();\n    }\n\n    @Test\n    public void shouldBeAbleToRunDAOCallThroughGroovy() throws Exception {\n        final String firstName = \"FlofFlof\";\n        final String lastName = \"Boudin\";\n        final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression(\"createNewEmployee\",\n                \"import \" +\n                        EMPLOYEE_QUALIFIED_NAME + \"; import \" + ADDRESS_QUALIFIED_NAME +\n                        \"; Employee e = new Employee(); e.firstName = '\" +\n                        firstName + \"'; e.lastName = '\" + lastName +\n                        \"'; e.addToAddresses(myAddress); return e;\",\n                EMPLOYEE_QUALIFIED_NAME,\n                new ExpressionBuilder().createBusinessDataExpression(\"myAddress\", ADDRESS_QUALIFIED_NAME));\n        final Expression addressExpression = new ExpressionBuilder().createGroovyScriptExpression(\"createNewAddress\",\n                \"import \" +\n                        ADDRESS_QUALIFIED_NAME +\n                        \"; Address a = new Address(); a.street='32, rue Gustave Eiffel'; a.city='Grenoble'; return a;\",\n                ADDRESS_QUALIFIED_NAME);\n\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance(\n                \"shouldBeAbleToRunDAOCallThroughGroovy\", \"6.3.1\");\n        final String employeeDAOName = \"employeeDAO\";\n        final String bizDataName = \"myEmployee\";\n        processDefinitionBuilder.addBusinessData(bizDataName, EMPLOYEE_QUALIFIED_NAME, null);\n        processDefinitionBuilder.addBusinessData(\"myAddress\", ADDRESS_QUALIFIED_NAME, null);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addAutomaticTask(\"step1\")\n                .addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand(\"myAddress\"),\n                        OperatorType.ASSIGNMENT, null, null, addressExpression)\n                .addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand(bizDataName),\n                        OperatorType.ASSIGNMENT, null, null, employeeExpression);\n        processDefinitionBuilder.addUserTask(\"step2\", ACTOR_NAME);\n        processDefinitionBuilder.addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),\n                ACTOR_NAME, testUser);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId());\n        waitForUserTask(processInstance, \"step2\");\n\n        // Let's check we can retrieve firstName using DAO call:\n        final long processInstanceId = processInstance.getId();\n        final Map<Expression, Map<String, Serializable>> expressions = new HashMap<>(1);\n        final String getLastNameWithDAOExpression = \"retrieveEmployeeByFirstName\";\n        expressions.put(\n                new ExpressionBuilder().createGroovyScriptExpression(getLastNameWithDAOExpression,\n                        \"import \" + EMPLOYEE_QUALIFIED_NAME + \"; Employee e = \"\n                                + employeeDAOName + \".findByFirstName('\" + firstName\n                                + \"', 0, 10).get(0); e.getAddresses().get(0).city\",\n                        String.class.getName(),\n                        new ExpressionBuilder().buildBusinessObjectDAOExpression(employeeDAOName,\n                                EMPLOYEE_QUALIFIED_NAME + \"DAO\")),\n                null);\n        expressions.put(new ExpressionBuilder().createQueryBusinessDataExpression(COUNT_ADDRESS,\n                \"Address.\" + COUNT_ADDRESS, Long.class.getName()), null);\n        expressions.put(new ExpressionBuilder().createQueryBusinessDataExpression(COUNT_EMPLOYEE,\n                \"Employee.\" + COUNT_EMPLOYEE, Long.class.getName()), null);\n        Map<String, Serializable> evaluatedExpressions = getProcessAPI()\n                .evaluateExpressionsOnProcessInstance(processInstanceId, expressions);\n        String returnedLastName = (String) evaluatedExpressions.get(getLastNameWithDAOExpression);\n        assertThat(returnedLastName).isEqualTo(\"Grenoble\");\n\n        final Serializable nbOfAddress = evaluatedExpressions.get(COUNT_ADDRESS);\n        final Serializable nbOfEmployee = evaluatedExpressions.get(COUNT_EMPLOYEE);\n\n        assertThat(nbOfAddress).isEqualTo(1L);\n        assertThat(nbOfEmployee).isEqualTo(1L);\n\n        logout();\n\n        loginWithTechnicalUser();\n        getTenantAdministrationAPI().pause();\n        getTenantAdministrationAPI().resume();\n        logout();\n\n        loginOnDefaultTenantWith(\"testUser\", \"bpm\");\n\n        evaluatedExpressions = getProcessAPI().evaluateExpressionsOnProcessInstance(processInstanceId, expressions);\n        returnedLastName = (String) evaluatedExpressions.get(getLastNameWithDAOExpression);\n        assertThat(returnedLastName).isEqualTo(\"Grenoble\");\n        logout();\n        loginWithTechnicalUser();\n\n        assertCount(processInstanceId);\n\n        disableAndDeleteProcess(definition.getId());\n    }\n\n    /**\n     * {@link BDRepositoryIT#should_use_apiClient_to_instantiate_dao_on_client_side}\n     *\n     * @throws Exception\n     */\n    @Test\n    @Deprecated\n    public void should_use_factory_to_instantiate_dao_on_client_side() throws Exception {\n        final AddressRef ref1 = new AddressRef(\"newYorkAddr\", \"33, corner street\", \"NY\");\n        final AddressRef ref2 = new AddressRef(\"romeAddr\", \"2, plaza del popolo\", \"Roma\");\n        addEmployee(\"Marcel\", \"Pagnol\", ref1, ref2);\n        final APISession apiSession = getSession();\n        final byte[] clientBDMZip = getTenantAdministrationAPI().getClientBDMZip();\n\n        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n\n        final ClassLoader classLoaderWithBDM = new ClassloaderRefresher().loadClientModelInClassloader(clientBDMZip,\n                contextClassLoader,\n                EMPLOYEE_QUALIFIED_NAME, clientFolder);\n\n        try {\n            Thread.currentThread().setContextClassLoader(classLoaderWithBDM);\n\n            @SuppressWarnings(\"unchecked\")\n            final Class<? extends BusinessObjectDAO> daoInterface = (Class<? extends BusinessObjectDAO>) Class.forName(\n                    EMPLOYEE_QUALIFIED_NAME + \"DAO\", true,\n                    classLoaderWithBDM);\n            final BusinessObjectDAOFactory businessObjectDAOFactory = new BusinessObjectDAOFactory();\n            final BusinessObjectDAO daoImpl = businessObjectDAOFactory.createDAO(apiSession, daoInterface);\n            assertThat(daoImpl.getClass().getName()).isEqualTo(EMPLOYEE_QUALIFIED_NAME + \"DAOImpl\");\n\n            Method daoMethod = daoImpl.getClass().getMethod(\"findByLastName\", String.class, int.class, int.class);\n            assertThat(daoMethod).isNotNull();\n            assertThat(daoMethod.getReturnType().getName()).isEqualTo(List.class.getName());\n            List<?> result = (List<?>) daoMethod.invoke(daoImpl, \"Pagnol\", 0, 10);\n            assertThat(result).isNotEmpty().hasSize(1);\n\n            result = (List<?>) daoMethod.invoke(daoImpl, \"Hanin\", 0, 10);\n            assertThat(result).isEmpty();\n\n            daoMethod = daoImpl.getClass().getMethod(\"findByFirstNameAndLastName\", String.class, String.class);\n            assertThat(daoMethod).isNotNull();\n            assertThat(daoMethod.getReturnType().getName()).isEqualTo(EMPLOYEE_QUALIFIED_NAME);\n            final Object employee = daoMethod.invoke(daoImpl, \"Marcel\", \"Pagnol\");\n            assertThat(employee).isNotNull();\n            final List<?> lazyAddresses = (List<?>) employee.getClass().getMethod(\"getAddresses\", new Class[0])\n                    .invoke(employee);\n            assertThat(lazyAddresses).hasSize(2);\n        } finally {\n            Thread.currentThread().setContextClassLoader(contextClassLoader);\n        }\n    }\n\n    @Test\n    public void should_use_apiClient_to_instantiate_dao_on_client_side() throws Exception {\n        final AddressRef ref1 = new AddressRef(\"newYorkAddr\", \"33, corner street\", \"NY\");\n        final AddressRef ref2 = new AddressRef(\"romeAddr\", \"2, plaza del popolo\", \"Roma\");\n        addEmployee(\"Marcel\", \"Pagnol\", ref1, ref2);\n        final APISession apiSession = getSession();\n        final byte[] clientBDMZip = getTenantAdministrationAPI().getClientBDMZip();\n\n        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n\n        final ClassLoader classLoaderWithBDM = new ClassloaderRefresher().loadClientModelInClassloader(clientBDMZip,\n                contextClassLoader,\n                EMPLOYEE_QUALIFIED_NAME, clientFolder);\n\n        try {\n            Thread.currentThread().setContextClassLoader(classLoaderWithBDM);\n\n            @SuppressWarnings(\"unchecked\")\n            final Class<? extends BusinessObjectDAO> daoInterface = (Class<? extends BusinessObjectDAO>) Class.forName(\n                    EMPLOYEE_QUALIFIED_NAME + \"DAO\", true,\n                    classLoaderWithBDM);\n            final APIClient client = new APIClient(apiSession);\n            final BusinessObjectDAO daoImpl = client.getDAO(daoInterface);\n            assertThat(daoImpl.getClass().getName()).isEqualTo(EMPLOYEE_QUALIFIED_NAME + \"DAOImpl\");\n\n            Method daoMethod = daoImpl.getClass().getMethod(\"countForFindByLastName\", String.class);\n            assertThat(daoMethod).isNotNull();\n            assertThat(daoMethod.getReturnType().getName()).isEqualTo(Long.class.getName());\n            final Long count = (Long) daoMethod.invoke(daoImpl, \"Pagnol\");\n            assertThat(count).isEqualTo(1);\n\n            daoMethod = daoImpl.getClass().getMethod(\"findByLastName\", String.class, int.class, int.class);\n            assertThat(daoMethod).isNotNull();\n            assertThat(daoMethod.getReturnType().getName()).isEqualTo(List.class.getName());\n            List<?> result = (List<?>) daoMethod.invoke(daoImpl, \"Pagnol\", 0, 10);\n            assertThat(result).isNotEmpty().hasSize(1);\n\n            result = (List<?>) daoMethod.invoke(daoImpl, \"Hanin\", 0, 10);\n            assertThat(result).isEmpty();\n\n            daoMethod = daoImpl.getClass().getMethod(\"findByFirstNameAndLastName\", String.class, String.class);\n            assertThat(daoMethod).isNotNull();\n            assertThat(daoMethod.getReturnType().getName()).isEqualTo(EMPLOYEE_QUALIFIED_NAME);\n            final Object employee = daoMethod.invoke(daoImpl, \"Marcel\", \"Pagnol\");\n            assertThat(employee).isNotNull();\n            final List<?> lazyAddresses = (List<?>) employee.getClass().getMethod(\"getAddresses\", new Class[0])\n                    .invoke(employee);\n            assertThat(lazyAddresses).hasSize(2);\n        } finally {\n            Thread.currentThread().setContextClassLoader(contextClassLoader);\n        }\n    }\n\n    @Test\n    public void should_retrieve_bdm_object_with_lazy_and_non_lazy_composition_objects_using_dao() throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"test\", \"1.2-alpha\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addBusinessData(\"myEmployee\", EMPLOYEE_QUALIFIED_NAME,\n                new ExpressionBuilder().createGroovyScriptExpression(\"createNewEmployee\",\n                        \"import \" + EMPLOYEE_QUALIFIED_NAME + \"\\n\" +\n                                \"import \" + DOG_QUALIFIED_NAME + \"\\n\" +\n                                \"import \" + CAT_QUALIFIED_NAME + \"\\n\" +\n                                \"Employee e = new Employee()\\n\" +\n                                \"e.firstName ='john'\\n\" +\n                                \"e.lastName ='doe'\\n\" +\n                                \"def d = new Dog()\\n\" +\n                                \"d.name = 'kiki'\\n\" +\n                                \"d.age = 2\\n\" +\n                                \"e.setDog(d)\\n\" +\n                                \"def c = new Cat()\\n\" +\n                                \"c.name = 'fifi'\\n\" +\n                                \"c.age = 5\\n\" +\n                                \"e.setCat(c)\\n\" +\n                                \"return e\",\n                        EMPLOYEE_QUALIFIED_NAME));\n        processDefinitionBuilder.addUserTask(\"step1\", ACTOR_NAME);\n\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),\n                ACTOR_NAME, testUser);\n        final ProcessInstance instance = getProcessAPI().startProcess(definition.getId());\n        waitForUserTask(instance, \"step1\");\n\n        disableAndDeleteProcess(definition.getId());\n        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        final ClassLoader classLoaderWithBDM = new ClassloaderRefresher().loadClientModelInClassloader(\n                getTenantAdministrationAPI().getClientBDMZip(),\n                contextClassLoader,\n                EMPLOYEE_QUALIFIED_NAME, clientFolder);\n\n        try {\n            Thread.currentThread().setContextClassLoader(classLoaderWithBDM);\n            final BusinessObjectDAO daoImpl = new APIClient(getSession())\n                    .getDAO((Class<? extends BusinessObjectDAO>) Class.forName(EMPLOYEE_QUALIFIED_NAME + \"DAO\", true,\n                            classLoaderWithBDM));\n\n            List<?> employees = (List<?>) daoImpl.getClass().getMethod(\"find\", int.class, int.class).invoke(daoImpl, 0,\n                    100);\n            assertThat(invokeMethod(invokeMethod(employees.get(0), \"getCat\"), \"getName\")).isEqualTo(\"fifi\");\n            assertThat(invokeMethod(invokeMethod(employees.get(0), \"getDog\"), \"getName\")).isEqualTo(\"kiki\");\n            Object employee = daoImpl.getClass().getMethod(\"findByFirstNameAndLastName\", String.class, String.class)\n                    .invoke(daoImpl, \"john\", \"doe\");\n            assertThat(invokeMethod(invokeMethod(employee, \"getCat\"), \"getName\")).isEqualTo(\"fifi\");\n            assertThat(invokeMethod(invokeMethod(employee, \"getDog\"), \"getName\")).isEqualTo(\"kiki\");\n        } finally {\n            Thread.currentThread().setContextClassLoader(contextClassLoader);\n        }\n    }\n\n    private Object invokeMethod(Object object, String method)\n            throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {\n        return object.getClass().getMethod(method, new Class[0]).invoke(object);\n    }\n\n    private void addEmployee(final String firstName, final String lastName, final AddressRef... addresses)\n            throws Exception {\n        final List<Expression> dependencies = new ArrayList<>();\n        for (final AddressRef ref : addresses) {\n            dependencies.add(ref.getExpression());\n        }\n        final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression(\"createNewEmployee\",\n                createNewEmployeeScriptContent(firstName, lastName, addresses),\n                EMPLOYEE_QUALIFIED_NAME, dependencies);\n\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"test\", \"1.2-alpha\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addBusinessData(\"myEmployee\", EMPLOYEE_QUALIFIED_NAME, null);\n        final UserTaskDefinitionBuilder task = processDefinitionBuilder.addUserTask(\"step1\", ACTOR_NAME);\n        for (final AddressRef ref : addresses) {\n            processDefinitionBuilder.addBusinessData(ref.getVarName(), ADDRESS_QUALIFIED_NAME, null);\n            task.addOperation(ref.getCreationOperation());\n        }\n        task.addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand(\"myEmployee\"),\n                OperatorType.ASSIGNMENT, null, null, employeeExpression);\n\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),\n                ACTOR_NAME, testUser);\n        final ProcessInstance instance = getProcessAPI().startProcess(definition.getId());\n        waitForUserTaskAndExecuteIt(instance, \"step1\", testUser);\n        waitForProcessToFinish(instance);\n\n        disableAndDeleteProcess(definition.getId());\n    }\n\n    private String createNewEmployeeScriptContent(final String firstName, final String lastName,\n            final AddressRef... addresses) {\n        final StringBuilder sb = new StringBuilder();\n        sb.append(\"import \");\n        sb.append(EMPLOYEE_QUALIFIED_NAME);\n        sb.append(\"\\n\");\n        sb.append(\"import \");\n        sb.append(ADDRESS_QUALIFIED_NAME);\n        sb.append(\"\\n\");\n        sb.append(\"Employee e = new Employee();\");\n        sb.append(\"\\n\");\n        sb.append(\"e.firstName =\");\n        sb.append(\"'\" + firstName + \"'\");\n        sb.append(\"\\n\");\n        sb.append(\"e.lastName =\");\n        sb.append(\"'\" + lastName + \"'\");\n        sb.append(\"\\n\");\n        if (addresses != null) {\n            for (AddressRef address : addresses) {\n                sb.append(\"e.addToAddresses(\" + address.getVarName() + \")\");\n                sb.append(\"\\n\");\n            }\n        }\n\n        sb.append(\"return e;\");\n        return sb.toString();\n    }\n\n    private ProcessDefinition buildProcessThatUpdateBizDataInsideConnector(final String taskName) throws Exception {\n        final Expression getEmployeeExpression = new ExpressionBuilder().createBusinessDataExpression(\"myEmployee\",\n                EMPLOYEE_QUALIFIED_NAME);\n\n        final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression(\n                \"createNewEmployee\",\n                \"import \" + EMPLOYEE_QUALIFIED_NAME +\n                        \"; Employee e = new Employee(); e.firstName = 'John'; e.lastName = 'Doe'; e.addToPhoneNumbers('78945612'); return e;\",\n                EMPLOYEE_QUALIFIED_NAME);\n\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"BizDataAndConnector\", \"1.0\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addBusinessData(\"myEmployee\", EMPLOYEE_QUALIFIED_NAME, employeeExpression);\n        processDefinitionBuilder\n                .addUserTask(taskName, ACTOR_NAME)\n                .addConnector(\"updateBusinessData\", \"org.bonitasoft.connector.BusinessDataUpdateConnector\", \"1.0\",\n                        ConnectorEvent.ON_ENTER)\n                .addInput(\"bizData\", getEmployeeExpression)\n                .addOutput(\n                        new OperationBuilder().createBusinessDataSetAttributeOperation(\"myEmployee\", \"setLastName\",\n                                String.class.getName(),\n                                new ExpressionBuilder().createGroovyScriptExpression(\n                                        \"retrieve modified lastname from connector\", \"output1.getLastName()\",\n                                        String.class.getName(), new ExpressionBuilder()\n                                                .createBusinessDataExpression(\"output1\", EMPLOYEE_QUALIFIED_NAME))));\n\n        final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(\n                        processDefinitionBuilder.done());\n        BarResource barResource = getBarResource(\n                \"/org/bonitasoft/engine/business/data/BusinessDataUpdateConnector.impl\",\n                \"BusinessDataUpdateConnector.impl\",\n                BDRepositoryIT.class);\n        businessArchiveBuilder.addConnectorImplementation(barResource);\n\n        barResource = BuildTestUtil.generateJarAndBuildBarResource(BusinessDataUpdateConnector.class,\n                \"BusinessDataUpdateConnector.jar\");\n        businessArchiveBuilder.addClasspathResource(barResource);\n\n        return deployAndEnableProcessWithActor(businessArchiveBuilder.done(), ACTOR_NAME, testUser);\n    }\n\n    private String getEmployeeToString(final String businessDataName, final long processInstanceId)\n            throws InvalidExpressionException {\n        final Map<Expression, Map<String, Serializable>> expressions = new HashMap<>(5);\n        final String expressionEmployee = \"retrieve_Employee\";\n        expressions.put(\n                new ExpressionBuilder().createGroovyScriptExpression(expressionEmployee,\n                        \"\\\"Employee [firstName=\\\" + \" + businessDataName\n                                + \".firstName + \\\", lastName=\\\" + \" + businessDataName + \".lastName + \\\"]\\\";\",\n                        String.class.getName(),\n                        new ExpressionBuilder().createBusinessDataExpression(businessDataName,\n                                EMPLOYEE_QUALIFIED_NAME)),\n                null);\n        try {\n            final Map<String, Serializable> evaluatedExpressions = getProcessAPI()\n                    .evaluateExpressionsOnProcessInstance(processInstanceId, expressions);\n            return (String) evaluatedExpressions.get(expressionEmployee);\n        } catch (final ExpressionEvaluationException eee) {\n            System.err.println(eee.getMessage());\n            return null;\n        }\n    }\n\n    @Test\n    public void shouldBeAbleToCreate2BusinessDataUsingIntermixedBizDataJavaSetterOperations() throws Exception {\n        final Expression countryQueryNameParameter = new ExpressionBuilder().createExpression(\"name\", \"France\",\n                String.class.getName(),\n                ExpressionType.TYPE_CONSTANT);\n        final Expression countryQueryExpression = new ExpressionBuilder().createQueryBusinessDataExpression(\"country\",\n                \"Country.findByName\",\n                COUNTRY_QUALIFIED_NAME, countryQueryNameParameter);\n        final Expression createNewAddressExpression = new ExpressionBuilder().createGroovyScriptExpression(\n                \"createNewAddress\",\n                \"import \" + ADDRESS_QUALIFIED_NAME\n                        + \"; Address a = new Address(street:'32, rue Gustave Eiffel', city:'Grenoble'); a;\",\n                ADDRESS_QUALIFIED_NAME);\n        final Expression createNewCountryExpression = new ExpressionBuilder().createGroovyScriptExpression(\n                \"createNewCountry\",\n                \"import \" + COUNTRY_QUALIFIED_NAME + \"; Country c = new Country(name:'France'); c;\",\n                COUNTRY_QUALIFIED_NAME);\n\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance(\n                \"shouldBeAbleToUpdateBusinessDataUsingJavaSetterOperation\", PROCESS_VERSION);\n        final String businessDataName = \"newBornBaby\";\n        final String businessDataName2 = \"data2\";\n        processDefinitionBuilder.addBusinessData(businessDataName, EMPLOYEE_QUALIFIED_NAME, null);\n        processDefinitionBuilder.addBusinessData(businessDataName2, EMPLOYEE_QUALIFIED_NAME, null);\n        processDefinitionBuilder.addBusinessData(\"address\", ADDRESS_QUALIFIED_NAME, createNewAddressExpression);\n        processDefinitionBuilder.addBusinessData(\"country\", COUNTRY_QUALIFIED_NAME, createNewCountryExpression);\n        final String retrievedCountryData = \"retrievedCountry\";\n        processDefinitionBuilder.addBusinessData(retrievedCountryData, COUNTRY_QUALIFIED_NAME, null);\n        processDefinitionBuilder.addBusinessData(\"noneAddress\", ADDRESS_QUALIFIED_NAME, null);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder\n                .addAutomaticTask(\"step1\")\n                .addOperation(\n                        new OperationBuilder().createBusinessDataSetAttributeOperation(businessDataName, \"setFirstName\",\n                                String.class.getName(),\n                                new ExpressionBuilder().createConstantStringExpression(\"Manon\")))\n                .addOperation(\n                        new OperationBuilder().createBusinessDataSetAttributeOperation(businessDataName2,\n                                \"setFirstName\", String.class.getName(),\n                                new ExpressionBuilder().createConstantStringExpression(\"Plop\")))\n                .addOperation(\n                        new OperationBuilder().createBusinessDataSetAttributeOperation(businessDataName, \"setLastName\",\n                                String.class.getName(),\n                                new ExpressionBuilder().createConstantStringExpression(\"Péuigrec\")))\n                .addOperation(\n                        new OperationBuilder().createBusinessDataSetAttributeOperation(businessDataName2, \"setLastName\",\n                                String.class.getName(),\n                                new ExpressionBuilder().createConstantStringExpression(\"Plip\")))\n                .addOperation(\n                        new OperationBuilder().createBusinessDataSetAttributeOperation(\"address\", \"setCountry\",\n                                COUNTRY_QUALIFIED_NAME,\n                                countryQueryExpression))\n                .addOperation(\n                        new OperationBuilder().createBusinessDataSetAttributeOperation(businessDataName2, \"setAddress\",\n                                ADDRESS_QUALIFIED_NAME,\n                                new ExpressionBuilder().createBusinessDataExpression(\"noneAddress\",\n                                        ADDRESS_QUALIFIED_NAME)))\n                .addAutomaticTask(\"step2\")\n                .addOperation(\n                        new OperationBuilder().attachBusinessDataSetAttributeOperation(retrievedCountryData,\n                                countryQueryExpression))\n                .addOperation(\n                        new OperationBuilder().createBusinessDataSetAttributeOperation(retrievedCountryData, \"setName\",\n                                String.class.getName(),\n                                new ExpressionBuilder().createConstantStringExpression(\"FRANCE\")));\n        processDefinitionBuilder.addUserTask(\"step3\", ACTOR_NAME);\n        processDefinitionBuilder.addTransition(\"step1\", \"step2\");\n        processDefinitionBuilder.addTransition(\"step2\", \"step3\");\n\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),\n                ACTOR_NAME, testUser);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId());\n        waitForUserTask(processInstance, \"step3\");\n\n        final Expression getEmployeeAddressExpression = new ExpressionBuilder().createGroovyScriptExpression(\n                \"getEmployeeAddress\",\n                \"if (\" + businessDataName2 + \".address == null) return \\\"null\\\"\\n\" + businessDataName2\n                        + \".address.city\",\n                String.class.getName(),\n                new ExpressionBuilder().createBusinessDataExpression(businessDataName2, EMPLOYEE_QUALIFIED_NAME));\n        final Expression getCountryExpression = new ExpressionBuilder().createGroovyScriptExpression(\"getCountry\",\n                \"if (\" + retrievedCountryData + \" == null) return \\\"null\\\"\\n\" + retrievedCountryData + \".name\",\n                String.class.getName(),\n                new ExpressionBuilder().createBusinessDataExpression(retrievedCountryData, COUNTRY_QUALIFIED_NAME));\n\n        final Map<Expression, Map<String, Serializable>> expressions = new HashMap<>(2);\n        expressions.put(getEmployeeAddressExpression, null);\n        expressions.put(getCountryExpression, null);\n        final Map<String, Serializable> result = getProcessAPI().evaluateExpressionsOnProcessInstance(\n                processInstance.getId(),\n                expressions);\n        assertThat(result.get(getEmployeeAddressExpression.getName())).isEqualTo(\"null\");\n        assertThat(result.get(getCountryExpression.getName())).isEqualTo(\"FRANCE\");\n\n        disableAndDeleteProcess(definition.getId());\n    }\n\n    @Test\n    public void shouldBeAbleToDeleteABusinessDataUsingOperation() throws Exception {\n        final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression(\"createNewEmployee\",\n                \"import \" +\n                        EMPLOYEE_QUALIFIED_NAME +\n                        \"; Employee e = new Employee(); e.firstName = 'John'; e.lastName = 'Doe'; return e;\",\n                EMPLOYEE_QUALIFIED_NAME);\n\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance(\n                \"shouldBeAbleToUpdateBusinessDataUsingJavaSetterOperation\", \"6.3-beta\");\n        final String businessDataName = \"employee\";\n        processDefinitionBuilder.addBusinessData(businessDataName, EMPLOYEE_QUALIFIED_NAME, employeeExpression);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addUserTask(\"step1\", ACTOR_NAME)\n                .addOperation(new OperationBuilder().deleteBusinessDataOperation(businessDataName));\n        processDefinitionBuilder.addUserTask(\"step2\", ACTOR_NAME);\n        processDefinitionBuilder.addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),\n                ACTOR_NAME, testUser);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId());\n\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n        final Map<Expression, Map<String, Serializable>> expressions = new HashMap<>(2);\n        expressions.put(\n                new ExpressionBuilder().createQueryBusinessDataExpression(\"countEmployee\", \"Employee.countEmployee\",\n                        Long.class.getName()),\n                Collections.emptyMap());\n\n        final long processInstanceId = processInstance.getId();\n        Map<String, Serializable> result = getProcessAPI().evaluateExpressionsOnProcessInstance(processInstanceId,\n                expressions);\n        assertThat(result.get(\"countEmployee\")).isEqualTo(1L);\n\n        assignAndExecuteStep(step1Id, testUser);\n        waitForUserTask(processInstance, \"step2\");\n        result = getProcessAPI().evaluateExpressionsOnProcessInstance(processInstanceId, expressions);\n        assertThat(result.get(\"countEmployee\")).isEqualTo(0L);\n\n        disableAndDeleteProcess(definition.getId());\n    }\n\n    public void assertCount(final long processInstanceId) throws Exception {\n        final Map<Expression, Map<String, Serializable>> expressions = new HashMap<>(2);\n        expressions.put(\n                new ExpressionBuilder().createQueryBusinessDataExpression(\"countEmployee\", \"Employee.countEmployee\",\n                        Long.class.getName()),\n                Collections.emptyMap());\n\n        final Map<String, Serializable> result = getProcessAPI().evaluateExpressionsOnProcessInstance(processInstanceId,\n                expressions);\n        assertThat(result.get(\"countEmployee\")).isEqualTo(1L);\n    }\n\n    @Test\n    public void deployABDRAndCreateAndUpdateAMultipleBusinessData() throws Exception {\n        final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression(\n                \"createNewEmployees\",\n                \"import \" + EMPLOYEE_QUALIFIED_NAME +\n                        \"; Employee john = new Employee(); john.firstName = 'John'; john.lastName = '저는 7년 동안 한국에서 살았어요';\"\n                        +\n                        \" Employee jane = new Employee(); jane.firstName = 'Jane'; jane.lastName = 'Doe'; return [jane, john];\",\n                List.class.getName());\n\n        final Expression jackExpression = new ExpressionBuilder().createGroovyScriptExpression(\"createJack\", \"import \"\n                + EMPLOYEE_QUALIFIED_NAME\n                + \"; Employee jack = new Employee(); jack.firstName = 'Jack'; jack.lastName = 'Doe'; return jack;\",\n                EMPLOYEE_QUALIFIED_NAME);\n\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"test\", \"1.2-alpha\");\n        processDefinitionBuilder.addBusinessData(\"myEmployees\", EMPLOYEE_QUALIFIED_NAME, employeeExpression)\n                .setMultiple(true);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addUserTask(\"step1\", ACTOR_NAME)\n                .addOperation(new OperationBuilder().createBusinessDataSetAttributeOperation(\"myEmployees\", \"add\",\n                        Object.class.getName(), jackExpression));\n        processDefinitionBuilder.addUserTask(\"step2\", ACTOR_NAME);\n        processDefinitionBuilder.addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),\n                ACTOR_NAME, testUser);\n        final ProcessInstance instance = getProcessAPI().startProcess(definition.getId());\n\n        final long step1Id = waitForUserTask(instance, \"step1\");\n        String employeeToString = getEmployeesToString(\"myEmployees\", instance.getId());\n        assertThat(employeeToString).isEqualTo(\"Employee [firstName=[Jane, John], lastName=[Doe, 저는 7년 동안 한국에서 살았어요]]\");\n\n        assignAndExecuteStep(step1Id, testUser);\n        waitForUserTask(instance, \"step2\");\n        employeeToString = getEmployeesToString(\"myEmployees\", instance.getId());\n        assertThat(employeeToString)\n                .isEqualTo(\"Employee [firstName=[Jane, John, Jack], lastName=[Doe, 저는 7년 동안 한국에서 살았어요, Doe]]\");\n\n        disableAndDeleteProcess(definition.getId());\n    }\n\n    @Test\n    public void deployBDRAndCreateAndUpdateAInitiallyEmptyMultipleBusinessData() throws Exception {\n        final Expression employeeExpression1 = new ExpressionBuilder().createGroovyScriptExpression(\n                \"createNewEmployees\",\n                \"import \" + EMPLOYEE_QUALIFIED_NAME +\n                        \"; return new ArrayList<>();\",\n                List.class.getName());\n        final Expression myEmployeesDependency = new ExpressionBuilder().createBusinessDataExpression(\"myEmployees\",\n                List.class.getName());\n        final Expression jackExpression = new ExpressionBuilder().createGroovyScriptExpression(\"createJack\", \"import \"\n                + EMPLOYEE_QUALIFIED_NAME\n                + \"; Employee jack = new Employee(); jack.firstName = 'Jack'; jack.lastName = 'Doe'; myEmployees.add(jack) ; return myEmployees;\",\n                List.class.getName(), myEmployeesDependency);\n\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"test\", \"1.2-alpha\");\n        processDefinitionBuilder.addBusinessData(\"myEmployees\", EMPLOYEE_QUALIFIED_NAME, employeeExpression1)\n                .setMultiple(true);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addUserTask(\"step1\", ACTOR_NAME)\n                .addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand(\"myEmployees\"),\n                        OperatorType.ASSIGNMENT, null, null, jackExpression);\n        processDefinitionBuilder.addUserTask(\"step2\", ACTOR_NAME);\n        processDefinitionBuilder.addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),\n                ACTOR_NAME, testUser);\n        final ProcessInstance instance = getProcessAPI().startProcess(definition.getId());\n\n        final long step1Id = waitForUserTask(instance, \"step1\");\n        String employeeToString = getEmployeesToString(\"myEmployees\", instance.getId());\n        assertThat(employeeToString).isEqualTo(\"Employee [firstName=[], lastName=[]]\");\n\n        assignAndExecuteStep(step1Id, testUser);\n        waitForUserTask(instance, \"step2\");\n        employeeToString = getEmployeesToString(\"myEmployees\", instance.getId());\n        assertThat(employeeToString)\n                .isEqualTo(\"Employee [firstName=[Jack], lastName=[Doe]]\");\n\n        disableAndDeleteProcess(definition.getId());\n    }\n\n    private String getEmployeesToString(final String businessDataName, final long processInstanceId)\n            throws InvalidExpressionException {\n        final Map<Expression, Map<String, Serializable>> expressions = new HashMap<>(5);\n        final String expressionEmployee = \"retrieve_Employee\";\n        expressions.put(\n                new ExpressionBuilder().createGroovyScriptExpression(expressionEmployee,\n                        \"\\\"Employee [firstName=\\\" + \" + businessDataName\n                                + \".firstName + \\\", lastName=\\\" + \" + businessDataName + \".lastName + \\\"]\\\";\",\n                        String.class.getName(),\n                        new ExpressionBuilder().createBusinessDataExpression(businessDataName, List.class.getName())),\n                null);\n        try {\n            final Map<String, Serializable> evaluatedExpressions = getProcessAPI()\n                    .evaluateExpressionsOnProcessInstance(processInstanceId, expressions);\n            return (String) evaluatedExpressions.get(expressionEmployee);\n        } catch (final ExpressionEvaluationException eee) {\n            System.err.println(eee.getMessage());\n            return null;\n        }\n    }\n\n    @Test\n    public void useMultipleBusinessDataInAUserTaskWithMultiInstance() throws Exception {\n        final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression(\n                \"createNewEmployees\",\n                \"import \" + EMPLOYEE_QUALIFIED_NAME +\n                        \"; Employee john = new Employee(); john.firstName = 'John'; john.lastName = 'Doe';\" +\n                        \" Employee jane = new Employee(); jane.firstName = 'Jane'; jane.lastName = 'Doe'; [jane, john]\",\n                List.class.getName());\n\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"MBIMI\", \"1.2-beta\");\n        builder.addBusinessData(\"myEmployees\", EMPLOYEE_QUALIFIED_NAME, employeeExpression).setMultiple(true);\n        builder.addActor(ACTOR_NAME);\n        final UserTaskDefinitionBuilder userTaskBuilder = builder.addUserTask(\"step1\", ACTOR_NAME);\n        userTaskBuilder.addBusinessData(\"employee\", EMPLOYEE_QUALIFIED_NAME);\n        userTaskBuilder.addMultiInstance(false, \"myEmployees\").addDataInputItemRef(\"employee\");\n        userTaskBuilder.addOperation(new OperationBuilder().createBusinessDataSetAttributeOperation(\"employee\",\n                \"setLastName\", String.class.getName(),\n                new ExpressionBuilder().createConstantStringExpression(\"Smith\")));\n        builder.addUserTask(\"step2\", ACTOR_NAME);\n        builder.addTransition(\"step1\", \"step2\");\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME,\n                testUser);\n\n        final ProcessInstance instance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(instance, \"step1\", testUser);\n        waitForUserTaskAndExecuteIt(instance, \"step1\", testUser);\n\n        waitForUserTask(instance, \"step2\");\n        final String employeeToString = getEmployeesToString(\"myEmployees\", instance.getId());\n        assertThat(firstNames(employeeToString)).containsOnlyOnce(\"Jane\", \"John\");\n        assertThat(lastNames(employeeToString)).containsExactly(\"Smith\", \"Smith\");\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    private String[] firstNames(final String employeeToString) {\n        String firstNames = substringAfter(employeeToString, \"firstName=[\");\n        firstNames = substringBefore(firstNames, \"], lastName=\");\n        return StringUtils.split(firstNames, \", \");\n    }\n\n    private String[] lastNames(final String employeeToString) {\n        String lastNames = substringAfter(employeeToString, \"lastName=[\");\n        lastNames = substringBefore(lastNames, \"]]\");\n        return StringUtils.split(lastNames, \", \");\n    }\n\n    @Test\n    public void useMultipleBusinessDataInACallActivityWithSequentialMultiInstance() throws Exception {\n        ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"UpdateEmployee\",\n                \"1.2-beta\");\n        builder.addActor(ACTOR_NAME);\n        builder.addBusinessData(\"employee\", EMPLOYEE_QUALIFIED_NAME, null);\n        final OperationBuilder operationBuilder = new OperationBuilder();\n        builder.addUserTask(\"step1\", ACTOR_NAME)\n                .addOperation(operationBuilder.createBusinessDataSetAttributeOperation(\"employee\", \"setLastName\",\n                        String.class.getName(),\n                        new ExpressionBuilder().createConstantStringExpression(\"Smith\")));\n        final ProcessDefinition subProcessDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME,\n                testUser);\n\n        final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression(\n                \"createNewEmployees\",\n                \"import \" + EMPLOYEE_QUALIFIED_NAME +\n                        \"; Employee john = new Employee(); john.firstName = 'John'; john.lastName = 'Doe';\" +\n                        \" Employee jane = new Employee(); jane.firstName = 'Jane'; jane.lastName = 'Doe'; return [jane, john];\",\n                List.class.getName());\n\n        builder = new ProcessDefinitionBuilder().createNewInstance(\"MBIMI\", \"1.2-beta\");\n        builder.addBusinessData(\"myEmployees\", EMPLOYEE_QUALIFIED_NAME, employeeExpression).setMultiple(true);\n        builder.addActor(ACTOR_NAME);\n        final CallActivityBuilder callActivity = builder.addCallActivity(\"step1\",\n                new ExpressionBuilder().createConstantStringExpression(subProcessDefinition.getName()),\n                new ExpressionBuilder().createConstantStringExpression(subProcessDefinition.getVersion()));\n        callActivity.addBusinessData(\"miEmployee\", EMPLOYEE_QUALIFIED_NAME);\n        callActivity.addDataInputOperation(\n                operationBuilder.createNewInstance()\n                        .attachBusinessDataSetAttributeOperation(\"employee\",\n                                new ExpressionBuilder().createBusinessDataExpression(\"miEmployee\",\n                                        EMPLOYEE_QUALIFIED_NAME)))\n                .addMultiInstance(true, \"myEmployees\").addDataInputItemRef(\"miEmployee\");\n        builder.addUserTask(\"step2\", ACTOR_NAME);\n        builder.addTransition(\"step1\", \"step2\");\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME,\n                testUser);\n\n        final ProcessInstance instance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(instance, \"step1\", testUser);\n        waitForUserTaskAndExecuteIt(instance, \"step1\", testUser);\n        waitForUserTask(instance, \"step2\");\n\n        final String employeeToString = getEmployeesToString(\"myEmployees\", instance.getId());\n        assertThat(employeeToString).contains(\"Jane\", \"John\", \"Smith\").doesNotContain(\"Doe\");\n\n        disableAndDeleteProcess(processDefinition);\n        disableAndDeleteProcess(subProcessDefinition);\n    }\n\n    @Test\n    public void useMultipleBusinessDataInACallActivityWithInDataMultiInstance() throws Exception {\n        ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"UpdateEmployee\",\n                \"1.2-beta\");\n        builder.addActor(ACTOR_NAME);\n        builder.addBusinessData(\"employee\", EMPLOYEE_QUALIFIED_NAME, null);\n        builder.addUserTask(\"step1\", ACTOR_NAME)\n                .addOperation(new OperationBuilder().createBusinessDataSetAttributeOperation(\"employee\", \"setLastName\",\n                        String.class.getName(),\n                        new ExpressionBuilder().createConstantStringExpression(\"Smith\")));\n        final ProcessDefinition subProcessDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME,\n                testUser);\n\n        final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression(\n                \"createNewEmployees\",\n                \"import \" + EMPLOYEE_QUALIFIED_NAME +\n                        \"; Employee john = new Employee(); john.firstName = 'John'; john.lastName = 'Doe';\" +\n                        \" Employee jane = new Employee(); jane.firstName = 'Jane'; jane.lastName = 'Doe'; [jane, john];\",\n                List.class.getName());\n\n        builder = new ProcessDefinitionBuilder().createNewInstance(\"MBIMI\", \"1.2-beta\");\n        builder.addBusinessData(\"myEmployees\", EMPLOYEE_QUALIFIED_NAME, employeeExpression).setMultiple(true);\n        builder.addBusinessData(\"myNewEmployees\", EMPLOYEE_QUALIFIED_NAME, null).setMultiple(true);\n        builder.addActor(ACTOR_NAME);\n        final CallActivityBuilder callActivityBuilder = builder.addCallActivity(\"step1\",\n                new ExpressionBuilder().createConstantStringExpression(subProcessDefinition.getName()),\n                new ExpressionBuilder().createConstantStringExpression(subProcessDefinition.getVersion()));\n        callActivityBuilder.addBusinessData(\"miEmployee\", EMPLOYEE_QUALIFIED_NAME);\n        callActivityBuilder.addBusinessData(\"newEmployee\", EMPLOYEE_QUALIFIED_NAME);\n        callActivityBuilder.addDataInputOperation(\n                new OperationBuilder().attachBusinessDataSetAttributeOperation(\"employee\",\n                        new ExpressionBuilder().createBusinessDataExpression(\"miEmployee\", EMPLOYEE_QUALIFIED_NAME)));\n        callActivityBuilder.addDataOutputOperation(\n                new OperationBuilder().attachBusinessDataSetAttributeOperation(\"newEmployee\",\n                        new ExpressionBuilder().createBusinessDataExpression(\"employee\", EMPLOYEE_QUALIFIED_NAME)));\n        callActivityBuilder.addMultiInstance(false, \"myEmployees\").addDataInputItemRef(\"miEmployee\")\n                .addDataOutputItemRef(\"newEmployee\").addLoopDataOutputRef(\"myNewEmployees\");\n        builder.addUserTask(\"step2\", ACTOR_NAME);\n        builder.addTransition(\"step1\", \"step2\");\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME,\n                testUser);\n\n        final ProcessInstance instance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(instance, \"step1\", testUser);\n        waitForUserTaskAndExecuteIt(instance, \"step1\", testUser);\n        waitForUserTask(instance, \"step2\");\n\n        final String employeeToString = getEmployeesToString(\"myNewEmployees\", instance.getId());\n        assertThat(employeeToString).contains(\"Jane\", \"John\", \"Smith\").doesNotContain(\"Doe\");\n\n        disableAndDeleteProcess(processDefinition);\n        disableAndDeleteProcess(subProcessDefinition);\n    }\n\n    @Test\n    public void useMultipleBusinessDataInACallActivityWithOutDataMultiInstance() throws Exception {\n        final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression(\n                \"createNewEmployee\",\n                \"import \" + EMPLOYEE_QUALIFIED_NAME +\n                        \"; Employee john = new Employee(); john.firstName = 'John' + activityInstanceId; john.lastName = 'Doe'; john;\",\n                EMPLOYEE_QUALIFIED_NAME,\n                new ExpressionBuilder().createEngineConstant(ExpressionConstants.ACTIVITY_INSTANCE_ID));\n        ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"createEmployee\",\n                \"1.2-beta\");\n        builder.addActor(ACTOR_NAME);\n        builder.addBusinessData(\"employee\", EMPLOYEE_QUALIFIED_NAME, null);\n        builder.addUserTask(\"step1\", ACTOR_NAME)\n                .addOperation(new OperationBuilder().attachBusinessDataSetAttributeOperation(\"employee\",\n                        employeeExpression));\n        final ProcessDefinition subProcessDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME,\n                testUser);\n\n        builder = new ProcessDefinitionBuilder().createNewInstance(\"MBIMI\", \"1.2-beta\");\n        builder.addBusinessData(\"myEmployees\", EMPLOYEE_QUALIFIED_NAME, null).setMultiple(true);\n        builder.addActor(ACTOR_NAME);\n        final CallActivityBuilder callActivityBuilder = builder.addCallActivity(\"step1\",\n                new ExpressionBuilder().createConstantStringExpression(subProcessDefinition.getName()),\n                new ExpressionBuilder().createConstantStringExpression(subProcessDefinition.getVersion()));\n        callActivityBuilder.addBusinessData(\"newEmployee\", EMPLOYEE_QUALIFIED_NAME);\n        callActivityBuilder.addDataOutputOperation(\n                new OperationBuilder().attachBusinessDataSetAttributeOperation(\"newEmployee\",\n                        new ExpressionBuilder().createBusinessDataExpression(\"employee\", EMPLOYEE_QUALIFIED_NAME)));\n        callActivityBuilder.addMultiInstance(false, new ExpressionBuilder().createConstantIntegerExpression(2))\n                .addDataOutputItemRef(\"newEmployee\")\n                .addLoopDataOutputRef(\"myEmployees\");\n        builder.addUserTask(\"step2\", ACTOR_NAME);\n        builder.addTransition(\"step1\", \"step2\");\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME,\n                testUser);\n\n        final ProcessInstance instance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(instance, \"step1\", testUser);\n        waitForUserTaskAndExecuteIt(instance, \"step1\", testUser);\n        waitForUserTask(instance, \"step2\");\n\n        final String employeeToString = getEmployeesToString(\"myEmployees\", instance.getId());\n        assertThat(employeeToString).contains(\"John\", \"Doe\");\n\n        disableAndDeleteProcess(processDefinition);\n        disableAndDeleteProcess(subProcessDefinition);\n    }\n\n    //BS-13803\n    @Test\n    public void initializeBusinessDataInCalledProcessWithContractInput() throws Exception {\n        final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression(\n                \"createNewEmployee\",\n                \"import \" + EMPLOYEE_QUALIFIED_NAME\n                        + \"; Employee john = new Employee(); john.firstName = theInput; john.lastName = 'Doe'; john;\",\n                EMPLOYEE_QUALIFIED_NAME,\n                new ExpressionBuilder().createContractInputExpression(\"theInput\", String.class.getName()));\n        ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"createEmployeeInCallActivity\", \"1.2-beta\");\n        builder.addActor(ACTOR_NAME);\n        builder.addContract().addInput(\"theInput\", Type.TEXT, \"the input\");\n        builder.addBusinessData(\"employee\", EMPLOYEE_QUALIFIED_NAME, employeeExpression);\n        builder.addUserTask(\"step1\", ACTOR_NAME);\n        final ProcessDefinition subProcessDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME,\n                testUser);\n\n        builder = new ProcessDefinitionBuilder().createNewInstance(\"createEmployeeInCallActivityMaster\", \"1.2-beta\");\n        builder.addActor(ACTOR_NAME);\n        builder.addCallActivity(\"call\",\n                new ExpressionBuilder().createConstantStringExpression(subProcessDefinition.getName()),\n                new ExpressionBuilder().createConstantStringExpression(subProcessDefinition.getVersion()))\n                .addProcessStartContractInput(\"theInput\",\n                        new ExpressionBuilder().createConstantStringExpression(\"theValue\"));\n        builder.addUserTask(\"step2\", ACTOR_NAME);\n        builder.addTransition(\"call\", \"step2\");\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME,\n                testUser);\n\n        final ProcessInstance instance = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(\"step1\");\n        final Expression employee = new ExpressionBuilder().createGroovyScriptExpression(\"script\", \"employee.firstName\",\n                String.class.getName(),\n                new ExpressionBuilder().createBusinessDataExpression(\"employee\", EMPLOYEE_QUALIFIED_NAME));\n        final Serializable employeeResult = getProcessAPI().evaluateExpressionsOnActivityInstance(step1Id,\n                Collections.singletonMap(employee, null)).get(\"script\");\n        assertThat(employeeResult).isEqualTo(\"theValue\");\n        getProcessAPI().assignUserTask(step1Id, testUser.getId());\n        getProcessAPI().executeFlowNode(step1Id);\n        waitForUserTask(instance, \"step2\");\n\n        disableAndDeleteProcess(processDefinition);\n        disableAndDeleteProcess(subProcessDefinition);\n    }\n\n    @Test\n    public void should_return_the_list_of_entities_from_the_multiple_instance() throws Exception {\n        final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression(\n                \"createNewEmployees\",\n                \"import \" + EMPLOYEE_QUALIFIED_NAME +\n                        \"; Employee john = new Employee(); john.firstName = 'John'; john.lastName = 'Doe';\" +\n                        \" Employee jane = new Employee(); jane.firstName = 'Jane'; jane.lastName = 'Doe';\" +\n                        \" Employee rambo = new Employee(); rambo.firstName = 'John'; rambo.lastName = 'Rambo'; [jane, john, rambo]\",\n                List.class.getName());\n\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"MBIMI\", \"1.2-beta\");\n        builder.addBusinessData(\"myEmployees\", EMPLOYEE_QUALIFIED_NAME, employeeExpression).setMultiple(true);\n        builder.addData(\"names\", List.class.getName(), null);\n        builder.addBusinessData(\"firstNames\", EMPLOYEE_QUALIFIED_NAME, null).setMultiple(true);\n        builder.addContextEntry(\"firstNames_ref\",\n                new ExpressionBuilder().createBusinessDataReferenceExpression(\"firstNames\"));\n        builder.addActor(ACTOR_NAME);\n        final UserTaskDefinitionBuilder userTaskBuilder = builder.addUserTask(\"step1\", ACTOR_NAME);\n        userTaskBuilder.addBusinessData(\"employee\", EMPLOYEE_QUALIFIED_NAME);\n        userTaskBuilder.addShortTextData(\"name\", null);\n        userTaskBuilder.addMultiInstance(false, \"myEmployees\").addDataInputItemRef(\"employee\")\n                .addDataOutputItemRef(\"name\").addLoopDataOutputRef(\"names\");\n        userTaskBuilder.addOperation(new OperationBuilder().createSetDataOperation(\"name\",\n                new ExpressionBuilder().createConstantStringExpression(\"Doe\")));\n        UserTaskDefinitionBuilder step2Builder = builder.addUserTask(\"step2\", ACTOR_NAME);\n        step2Builder.addOperation(new OperationBuilder().attachBusinessDataSetAttributeOperation(\"firstNames\",\n                new ExpressionBuilder()\n                        .createQueryBusinessDataExpression(\"findFirstNames\",\n                                \"Employee.\" + FIND_EMPLOYEE_WITH_FIRSTNAMES,\n                                List.class.getName(),\n                                new ExpressionBuilder().createGroovyScriptExpression(\"firstNames\",\n                                        \"['John'] as String[]\",\n                                        String[].class.getName()),\n                                new ExpressionBuilder().createExpression(\"startIndex\", \"0\", Integer.class.getName(),\n                                        ExpressionType.TYPE_CONSTANT),\n                                new ExpressionBuilder().createExpression(\"maxResults\", \"10\", Integer.class.getName(),\n                                        ExpressionType.TYPE_CONSTANT))));\n        builder.addUserTask(\"step3\", ACTOR_NAME);\n        builder.addTransition(\"step1\", \"step2\");\n        builder.addTransition(\"step2\", \"step3\");\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME,\n                testUser);\n\n        final ProcessInstance instance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(instance, \"step1\", testUser);\n        waitForUserTaskAndExecuteIt(instance, \"step1\", testUser);\n        waitForUserTaskAndExecuteIt(instance, \"step1\", testUser);\n        waitForUserTaskAndExecuteIt(instance, \"step2\", testUser);\n        waitForUserTask(instance, \"step3\");\n\n        final DataInstance namesDataInstance = getProcessAPI().getProcessDataInstance(\"names\", instance.getId());\n        assertThat(namesDataInstance.getValue().toString()).isEqualTo(\"[Doe, Doe, Doe]\");\n\n        final Serializable firstNamesDataInstance = getProcessAPI().getProcessInstanceExecutionContext(instance.getId())\n                .get(\"firstNames_ref\");\n\n        //Only employee with firstname == john\n        assertThat(((MultipleBusinessDataReference) firstNamesDataInstance).getStorageIds()).hasSize(2);\n\n        final Map<String, Serializable> employee = getProcessAPI().evaluateExpressionsOnProcessInstance(\n                instance.getId(),\n                Collections.singletonMap(new ExpressionBuilder().createBusinessDataReferenceExpression(\"myEmployees\"),\n                        Collections.emptyMap()));\n        assertThat(employee).hasSize(1);\n        assertThat(employee.get(\"myEmployees\")).isInstanceOf(MultipleBusinessDataReference.class);\n        final MultipleBusinessDataReference myEmployees = (MultipleBusinessDataReference) employee.get(\"myEmployees\");\n        assertThat(myEmployees.getName()).isEqualTo(\"myEmployees\");\n        assertThat(myEmployees.getType()).isEqualTo(EMPLOYEE_QUALIFIED_NAME);\n        assertThat(myEmployees.getStorageIds()).hasSize(3);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getProcessBusinessDataReferencesShoulReturnTheListOfReferences() throws Exception {\n        final String taskName = \"step\";\n        final ProcessDefinition definition = buildProcessThatUpdateBizDataInsideConnector(taskName);\n        final ProcessInstance instance = getProcessAPI().startProcess(definition.getId());\n        waitForUserTask(instance, taskName);\n\n        final List<BusinessDataReference> references = getBusinessDataAPI()\n                .getProcessBusinessDataReferences(instance.getId(), 0, 10);\n\n        assertThat(references).hasSize(1);\n        assertThat(((SimpleBusinessDataReference) references.get(0)).getStorageId()).isNotNull();\n\n        disableAndDeleteProcess(definition);\n    }\n\n    @Test\n    public void getBusinessDataCommand_should_return_json_entities() throws Exception {\n        final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression(\n                \"createNewEmployee\",\n                \"import \" + EMPLOYEE_QUALIFIED_NAME + \"; import \" +\n                        ADDRESS_QUALIFIED_NAME +\n                        \"; Employee e = new Employee(); e.firstName = 'Alphonse';\" +\n                        \" e.hireDate=new Date(1422742559000L); \" +\n                        \" e.lastName = 'Dupond'; e.addToPhoneNumbers('123456789'); e.setAddress(myAddress);e.addToAddresses(myAddress); return e;\",\n                EMPLOYEE_QUALIFIED_NAME,\n                new ExpressionBuilder().createBusinessDataExpression(\"myAddress\", ADDRESS_QUALIFIED_NAME));\n        final Expression addressExpression = new ExpressionBuilder().createGroovyScriptExpression(\"createNewAddress\",\n                \"import \" + ADDRESS_QUALIFIED_NAME + \"; import \" +\n                        COUNTRY_QUALIFIED_NAME + \"; \" +\n                        \"Address a = new Address(); a.street='32, rue Gustave Eiffel'; a.city='Grenoble'; a.country = myCountry ; a;\",\n                ADDRESS_QUALIFIED_NAME);\n        final Expression countryExpression = new ExpressionBuilder().createGroovyScriptExpression(\"createNewCountry\",\n                \"import \" + COUNTRY_QUALIFIED_NAME + \"; \" +\n                        \"Country c = new Country(); c.name='France'; \" +\n                        \" c;\",\n                COUNTRY_QUALIFIED_NAME);\n\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance(\n                \"rest\", \"1.0\");\n        final String bizDataName = \"myEmployee\";\n        processDefinitionBuilder.addBusinessData(\"myCountry\", COUNTRY_QUALIFIED_NAME, null);\n        processDefinitionBuilder.addBusinessData(\"myAddress\", ADDRESS_QUALIFIED_NAME, null);\n        processDefinitionBuilder.addBusinessData(bizDataName, EMPLOYEE_QUALIFIED_NAME, null);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addAutomaticTask(\"step1\")\n                .addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand(\"myCountry\"),\n                        OperatorType.ASSIGNMENT, null, null, countryExpression)\n                .addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand(\"myAddress\"),\n                        OperatorType.ASSIGNMENT, null, null, addressExpression)\n                .addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand(bizDataName),\n                        OperatorType.ASSIGNMENT, null, null, employeeExpression);\n        processDefinitionBuilder.addUserTask(\"step2\", ACTOR_NAME);\n        processDefinitionBuilder.addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),\n                ACTOR_NAME, testUser);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance, \"step2\");\n\n        final SimpleBusinessDataReference businessDataReference = (SimpleBusinessDataReference) getBusinessDataAPI()\n                .getProcessBusinessDataReference(\n                        bizDataName, processInstance.getId());\n\n        verifyCommandGetBusinessDataById(businessDataReference);\n        verifyCommandGetBusinessDataByIds(businessDataReference);\n        verifyCommandGetQuery_findByFirstNameAndLastNameNewOrder();\n        verifyCommandGetQuery_getEmployeeByPhoneNumber();\n        verifyCommandGetQuery_findByFirstNameFetchAddresses();\n        verifyCommandGetQuery_countEmployee();\n        verifyCommandGetQuery_findByHireDate();\n\n        disableAndDeleteProcess(processDefinition.getId());\n    }\n\n    private void verifyCommandGetBusinessDataByIds(final SimpleBusinessDataReference businessDataReference)\n            throws Exception {\n        final List<Long> ids = new ArrayList<>();\n        ids.add(businessDataReference.getStorageId());\n        final Map<String, Serializable> parameters = new HashMap<>();\n        parameters.put(\"businessDataIds\", (Serializable) ids);\n        parameters.put(\"entityClassName\", EMPLOYEE_QUALIFIED_NAME);\n        parameters.put(\"businessDataURIPattern\", \"/businessdata/{className}/{id}/{field}\");\n\n        // when\n        final String lazyAddressResultWithChildName = (String) getCommandAPI().execute(\"getBusinessDataByIds\",\n                parameters);\n\n        // then\n        assertThatJson(lazyAddressResultWithChildName).as(\"should get address with lazy link to country\")\n                .node(\"[0].addresses[0].links[0]\")\n                .isObject()\n                .containsEntry(\"rel\", \"country\")\n                .containsKey(\"href\");\n    }\n\n    private void verifyCommandGetBusinessDataById(final SimpleBusinessDataReference businessDataReference)\n            throws Exception {\n        final Map<String, Serializable> parameters = new HashMap<>();\n        parameters.put(\"businessDataId\", businessDataReference.getStorageId());\n        parameters.put(\"entityClassName\", EMPLOYEE_QUALIFIED_NAME);\n        parameters.put(\"businessDataChildName\", \"address\");\n        parameters.put(\"businessDataURIPattern\", \"/businessdata/{className}/{id}/{field}\");\n\n        // when\n        final String lazyAddressResultWithChildName = (String) getCommandAPI().execute(\"getBusinessDataById\",\n                parameters);\n\n        // then\n        assertThatJson(lazyAddressResultWithChildName).as(\"should get address with lazy link to country\")\n                .when(Option.IGNORING_VALUES)\n                .isEqualTo(getJsonContent(\"getBusinessDataByIdAddress.json\"));\n\n        // when\n        parameters.remove(\"businessDataChildName\");\n        final String employeeResultWithAddress = (String) getCommandAPI().execute(\"getBusinessDataById\", parameters);\n\n        // then\n        assertThatJson(employeeResultWithAddress).as(\"should get employee with lazy link to country in addresses\")\n                .node(\"addresses[0].links[0]\")\n                .isObject()\n                .containsEntry(\"rel\", \"country\")\n                .containsKey(\"href\");\n\n    }\n\n    private void verifyCommandGetQuery_findByFirstNameAndLastNameNewOrder() throws Exception {\n        final Map<String, Serializable> parameters = new HashMap<>();\n        final Map<String, Serializable> queryParameters = new HashMap<>();\n\n        queryParameters.put(\"firstName\", \"Alphonse\");\n        queryParameters.put(\"lastName\", \"Dupond\");\n\n        parameters.put(\"queryName\", FIND_BY_FIRST_NAME_AND_LAST_NAME_NEW_ORDER);\n        parameters.put(ENTITY_CLASS_NAME, EMPLOYEE_QUALIFIED_NAME);\n        parameters.put(\"startIndex\", 0);\n        parameters.put(\"maxResults\", 10);\n        parameters.put(\"businessDataURIPattern\", \"/businessdata/{className}/{id}/{field}\");\n        parameters.put(\"queryParameters\", (Serializable) queryParameters);\n\n        // when\n        ((BusinessDataQueryResult) getCommandAPI().execute(\"getBusinessDataByQueryCommand\", parameters))\n                .getJsonResults();\n        getCommandAPI().addDependency(\"temporaryDeps\" + iterator, new byte[] { 0, 1 });\n        iterator++;\n        final Serializable jsonResult = ((BusinessDataQueryResult) getCommandAPI()\n                .execute(\"getBusinessDataByQueryCommand\", parameters)).getJsonResults();\n\n        // then\n        assertThatJson(jsonResult).as(\"should get employee\")\n                .when(Option.IGNORING_VALUES)\n                .whenIgnoringPaths(\"[0].hireDate\")\n                .isEqualTo(getJsonContent(\"findByFirstNameAndLastNameNewOrder.json\"));\n\n    }\n\n    private void verifyCommandGetQuery_getEmployeeByPhoneNumber() throws Exception {\n        final Map<String, Serializable> parameters = new HashMap<>();\n        final Map<String, Serializable> queryParameters = new HashMap<>();\n\n        queryParameters.put(\"phoneNumber\", \"123456789\");\n\n        parameters.put(\"queryName\", GET_EMPLOYEE_BY_PHONE_NUMBER_QUERY_NAME);\n        parameters.put(ENTITY_CLASS_NAME, EMPLOYEE_QUALIFIED_NAME);\n        parameters.put(\"startIndex\", 0);\n        parameters.put(\"maxResults\", 10);\n        parameters.put(\"businessDataURIPattern\", BUSINESS_DATA_CLASS_NAME_ID_FIELD);\n        parameters.put(\"queryParameters\", (Serializable) queryParameters);\n\n        // when\n        final BusinessDataQueryResult businessDataQueryResult = (BusinessDataQueryResult) getCommandAPI()\n                .execute(\"getBusinessDataByQueryCommand\",\n                        parameters);\n        final String jsonResult = (String) businessDataQueryResult.getJsonResults();\n\n        // then\n        assertThatJson(jsonResult).as(\"should get employee\")\n                .when(Option.IGNORING_VALUES)\n                .whenIgnoringPaths(\"[0].hireDate\", \"[0].booleanField\")\n                .isEqualTo(getJsonContent(\"getEmployeeByPhoneNumber.json\"));\n\n    }\n\n    private void verifyCommandGetQuery_findByFirstNameFetchAddresses() throws Exception {\n\n        final BusinessDataQueryResult businessDataQueryResult = getBusinessDataByQuery(\n                Collections.singletonMap(\"firstName\", \"Alphonse\"),\n                0, 10, FIND_BY_FIRST_NAME_FETCH_ADDRESSES, EMPLOYEE_QUALIFIED_NAME);\n\n        // then\n        assertThat(businessDataQueryResult.getBusinessDataQueryMetadata())\n                .as(\"should have no metadata when custom countFor is not here\").isNull();\n        assertThatJson(businessDataQueryResult.getJsonResults()).as(\"should get employee\")\n                .when(Option.IGNORING_VALUES)\n                .whenIgnoringPaths(\"[0].hireDate\", \"[0].booleanField\")\n                .isEqualTo(getJsonContent(\"findByFirstNameFetchAddresses.json\"));\n\n    }\n\n    private BusinessDataQueryResult getBusinessDataByQuery(Map<String, Serializable> queryParameters,\n            int startIndex, int maxResult, String queryName, String entityClassName)\n            throws CommandNotFoundException, CommandParameterizationException, CommandExecutionException {\n        Map<String, Serializable> parameters = new HashMap<>();\n        parameters.put(\"queryName\", queryName);\n        parameters.put(ENTITY_CLASS_NAME, entityClassName);\n        parameters.put(\"startIndex\", startIndex);\n        parameters.put(\"maxResults\", maxResult);\n        parameters.put(\"businessDataURIPattern\", BUSINESS_DATA_CLASS_NAME_ID_FIELD);\n        parameters.put(\"queryParameters\", (Serializable) queryParameters);\n\n        // when\n        return (BusinessDataQueryResult) getCommandAPI()\n                .execute(\"getBusinessDataByQueryCommand\", parameters);\n    }\n\n    private void verifyCommandGetQuery_countEmployee() throws Exception {\n        final Map<String, Serializable> parameters = new HashMap<>();\n\n        parameters.put(\"queryName\", COUNT_EMPLOYEE);\n        parameters.put(ENTITY_CLASS_NAME, EMPLOYEE_QUALIFIED_NAME);\n        parameters.put(\"startIndex\", 0);\n        parameters.put(\"maxResults\", 10);\n        parameters.put(\"businessDataURIPattern\", BUSINESS_DATA_CLASS_NAME_ID_FIELD);\n\n        // when\n        final BusinessDataQueryResult businessDataQueryResult = (BusinessDataQueryResult) getCommandAPI()\n                .execute(\"getBusinessDataByQueryCommand\", parameters);\n\n        // then - Standard shape returns { \"value\": n }\n        assertThatJson(businessDataQueryResult.getJsonResults()).as(\"should get employee count\")\n                .isEqualTo(\"{ \\\"value\\\": 1 }\");\n\n    }\n\n    private void verifyCommandGetQuery_findByHireDate() throws Exception {\n        final Map<String, Serializable> queryParameters = new HashMap<>();\n        queryParameters.put(\"date1\", \"1930-01-15\");\n        queryParameters.put(\"date2\", \"2050-12-31\");\n\n        final BusinessDataQueryResult businessDataQueryResult = getBusinessDataByQuery(queryParameters, 0, 10,\n                FIND_BY_HIRE_DATE_RANGE, EMPLOYEE_QUALIFIED_NAME);\n\n        // then\n        assertThatJson(businessDataQueryResult.getJsonResults()).as(\"should get employee\")\n                .isArray()\n                .isNotEmpty();\n        final BusinessDataQueryMetadata businessDataQueryMetadata = businessDataQueryResult\n                .getBusinessDataQueryMetadata();\n        assertThat(businessDataQueryMetadata).as(\"should have metadata\").isNotNull();\n        assertThat(businessDataQueryMetadata.getCount()).isEqualTo(1L);\n        assertThat(businessDataQueryMetadata.getStartIndex()).isZero();\n        assertThat(businessDataQueryMetadata.getMaxResults()).isEqualTo(10);\n\n    }\n\n    @Override\n    public BarResource getResource(final String path, final String name) throws IOException {\n        return super.getResource(path, name);\n    }\n\n    private String getJsonContent(final String jsonFileName) throws IOException {\n        final String json;\n        json = new String(IOUtils.toByteArray(this.getClass().getResourceAsStream(jsonFileName)));\n        return json;\n    }\n\n    @Test\n    public void deployABDRAndCreateInOperationAMultipleBusinessData() throws Exception {\n        final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression(\n                \"createNewEmployees\",\n                \"import \" + EMPLOYEE_QUALIFIED_NAME +\n                        \"; Employee john = new Employee(); john.firstName = 'John'; john.lastName = 'Doe';\" +\n                        \" Employee jane = new Employee(); jane.firstName = 'Jane'; jane.lastName = 'Doe'; return [jane, john];\",\n                List.class.getName());\n\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"test\", \"1.2-alpha\");\n        processDefinitionBuilder.addBusinessData(\"myEmployees\", EMPLOYEE_QUALIFIED_NAME, null).setMultiple(true);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addUserTask(\"step1\", ACTOR_NAME).addOperation(\n                new OperationBuilder().createBusinessDataSetAttributeOperation(\"myEmployees\", \"addAll\",\n                        \"java.util.Collection\", employeeExpression));\n        processDefinitionBuilder.addUserTask(\"step2\", ACTOR_NAME);\n        processDefinitionBuilder.addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),\n                ACTOR_NAME, testUser);\n        final ProcessInstance instance = getProcessAPI().startProcess(definition.getId());\n\n        final long step1Id = waitForUserTask(instance, \"step1\");\n        String employeeToString = getEmployeesToString(\"myEmployees\", instance.getId());\n        assertThat(firstNames(employeeToString)).isEmpty();\n        assertThat(lastNames(employeeToString)).isEmpty();\n\n        assignAndExecuteStep(step1Id, testUser);\n        waitForUserTask(instance, \"step2\");\n        employeeToString = getEmployeesToString(\"myEmployees\", instance.getId());\n        assertThat(firstNames(employeeToString)).containsOnlyOnce(\"Jane\", \"John\");\n        assertThat(lastNames(employeeToString)).containsExactly(\"Doe\", \"Doe\");\n\n        disableAndDeleteProcess(definition.getId());\n    }\n\n    @Test\n    public void should_subprocess_get_bdm_from_parent_process_instance_when_using_task_loop() throws Exception {\n        final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression(\n                \"createNewEmployees\",\n                \"import \" + EMPLOYEE_QUALIFIED_NAME +\n                        \"; Employee john = new Employee(); john.firstName = 'John'; john.lastName = 'Doe';\" +\n                        \" Employee jane = new Employee(); jane.firstName = 'Jane'; jane.lastName = 'Doe'; return [jane, john];\",\n                List.class.getName());\n\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"test\", \"1.2-alpha\");\n        processDefinitionBuilder.addBusinessData(\"myEmployees\", EMPLOYEE_QUALIFIED_NAME, employeeExpression)\n                .setMultiple(true);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addUserTask(\"step1\", ACTOR_NAME);\n\n        //setup expressions\n        processDefinitionBuilder.addContextEntry(\"retrieve_Employee\",\n                new ExpressionBuilder().createGroovyScriptExpression(\"retrieve_Employee\",\n                        \"\\\"Employee [firstName=\\\" + myEmployees\"\n                                + \".firstName + \\\", lastName=\\\" + myEmployees.lastName + \\\"]\\\";\",\n                        String.class.getName(),\n                        new ExpressionBuilder().createBusinessDataExpression(\"myEmployees\", List.class.getName())));\n\n        //setup subprocess with loop\n        SubProcessDefinitionBuilder subProcessDefinitionBuilder = processDefinitionBuilder\n                .addSubProcess(\"subProcess\", true).getSubProcessBuilder();\n        subProcessDefinitionBuilder.addStartEvent(\"subStart\")\n                .addTimerEventTriggerDefinition(TimerType.DURATION,\n                        new ExpressionBuilder().createConstantLongExpression(100L));\n        subProcessDefinitionBuilder.addManualTask(\"subStep1\", ACTOR_NAME)\n                .addBusinessData(\"employee\", EMPLOYEE_QUALIFIED_NAME)\n                .addOperation(new OperationBuilder().createBusinessDataSetAttributeOperation(\"employee\", \"setLastName\",\n                        String.class.getName(), new ExpressionBuilder().createConstantStringExpression(\"Smith\")))\n                .addMultiInstance(false, \"myEmployees\").addDataInputItemRef(\"employee\");\n        subProcessDefinitionBuilder.addEndEvent(\"subEnd\");\n        subProcessDefinitionBuilder.addTransition(\"subStart\", \"subStep1\");\n        subProcessDefinitionBuilder.addTransition(\"subStep1\", \"subEnd\");\n\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),\n                ACTOR_NAME, testUser);\n        final ProcessInstance instance = getProcessAPI().startProcess(definition.getId());\n\n        // wait for the subprocess user task - first time\n        long activityInstanceId1 = waitForUserTask(\"subStep1\");\n        // execute the task\n        getProcessAPI().assignUserTask(activityInstanceId1, testUser.getId());\n        getProcessAPI().executeFlowNode(activityInstanceId1);\n\n        // wait for the subprocess user task - second time\n        long activityInstanceId2 = waitForUserTask(\"subStep1\");\n        ActivityInstance activityInstance2 = getProcessAPI().getActivityInstance(activityInstanceId2);\n        // execute the task\n        getProcessAPI().assignUserTask(activityInstanceId2, testUser.getId());\n        getProcessAPI().executeFlowNode(activityInstanceId2);\n\n        // wait for the subprocess to end\n        waitForProcessToFinish(activityInstance2.getParentProcessInstanceId());\n        waitForProcessToBeInState(instance.getId(), ProcessInstanceState.ABORTED);\n\n        // get ArchivedProcessInstanceExecutionContext\n        long archivedProcessInstanceId = getProcessAPI().getArchivedProcessInstances(instance.getId(), 0, 10).get(0)\n                .getId();\n        String employeeToString = (String) getProcessAPI()\n                .getArchivedProcessInstanceExecutionContext(archivedProcessInstanceId).get(\"retrieve_Employee\");\n        assertThat(lastNames(employeeToString)).containsExactly(\"Smith\", \"Smith\");\n\n        disableAndDeleteProcess(definition.getId());\n    }\n\n    @Test\n    public void should_get_the_lazy_list_in_a_multiple_business_data() throws Exception {\n        final Expression initProducts = new ExpressionBuilder().createGroovyScriptExpression(\"initProducts\",\n                \"import \" + PRODUCT_QUALIFIED_NAME + \";\" +\n                        \" Product p1 = new Product(); p1.name = 'Rock'; \" +\n                        \" Product p2 = new Product(); p2.name = 'Paper'; \" + \" return [p1, p2];\",\n                List.class.getName());\n\n        final Expression productDependency = new ExpressionBuilder().createBusinessDataExpression(\"products\",\n                List.class.getName());\n\n        final Expression initCatalogs = new ExpressionBuilder().createGroovyScriptExpression(\n                \"initCatalogs\",\n                \"import \" + PRODUCT_CATALOG_QUALIFIED_NAME + \";\" +\n                        \" ProductCatalog pc = new ProductCatalog(); pc.name = 'MyFirstCatalog'; pc.setProducts(products);\"\n                        +\n                        \" return [pc];\",\n                List.class.getName(), productDependency);\n\n        final Expression catalogDependency = new ExpressionBuilder().createBusinessDataExpression(\"productCatalogs\",\n                List.class.getName());\n\n        final Expression nbOfProducts = new ExpressionBuilder().createGroovyScriptExpression(\"nbOfProducts\",\n                \"import \" + PRODUCT_CATALOG_QUALIFIED_NAME + \";\" +\n                        \" productCatalogs.get(0).getProducts().size()\",\n                Integer.class.getName(), catalogDependency);\n\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"def\", \"6.3-beta\");\n        builder.addActor(ACTOR_NAME);\n        builder.addBusinessData(\"products\", PRODUCT_QUALIFIED_NAME, initProducts).setMultiple(true);\n        builder.addBusinessData(\"productCatalogs\", PRODUCT_CATALOG_QUALIFIED_NAME, null).setMultiple(true);\n        builder.addIntegerData(\"count\", null);\n        builder.addAutomaticTask(\"initCatalogs\")\n                .addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand(\"productCatalogs\"),\n                        OperatorType.ASSIGNMENT, null, null, initCatalogs);\n        builder.addUserTask(\"next\", ACTOR_NAME)\n                .addOperation(new OperationBuilder().createSetDataOperation(\"count\", nbOfProducts));\n        builder.addTransition(\"initCatalogs\", \"next\");\n\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, testUser);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId());\n        waitForUserTaskAndExecuteIt(processInstance, \"next\", testUser);\n\n        disableAndDeleteProcess(definition.getId());\n    }\n\n    @Test\n    public void should_update_composition_entities() throws Exception {\n        String initCatalog = \"import \" + PRODUCT_CATALOG_QUALIFIED_NAME + \"\\n\" +\n                \"import \" + \"com.company.model.Edition\" + \"\\n\" +\n                \"Edition edition = new Edition() \\n\" +\n                \"edition.releaseYear = '2015' \\n\" +\n                \"ProductCatalog pc = new ProductCatalog() \\n\" +\n                \"pc.name = 'MyFirstCatalog' \\n\" +\n                \"pc.setEditions([edition]) \\n\" +\n                \"pc\\n\";\n\n        final Expression initCatalogExpression = new ExpressionBuilder().createGroovyScriptExpression(\"initCatalog\",\n                initCatalog,\n                PRODUCT_CATALOG_QUALIFIED_NAME);\n\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"compo\", \"8.2\");\n        builder.addActor(ACTOR_NAME);\n        builder.addBusinessData(\"productCatalog\", PRODUCT_CATALOG_QUALIFIED_NAME, initCatalogExpression);\n        builder.addUserTask(\"updateCatalog\", ACTOR_NAME)\n                .addOperation(\n                        new OperationBuilder().createBusinessDataSetAttributeOperation(\"productCatalog\", \"setName\",\n                                String.class.getName(),\n                                new ExpressionBuilder().createConstantStringExpression(\"myUdaptedCatalog\")));\n        builder.addUserTask(\"unreferenceCatalog\", ACTOR_NAME)\n                .addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand(\"productCatalog\"),\n                        OperatorType.ASSIGNMENT, null, null,\n                        new ExpressionBuilder().createGroovyScriptExpression(\"nullExpression\", \"null\",\n                                PRODUCT_CATALOG_QUALIFIED_NAME));\n        builder.addUserTask(\"result\", ACTOR_NAME);\n        builder.addTransition(\"updateCatalog\", \"unreferenceCatalog\");\n        builder.addTransition(\"unreferenceCatalog\", \"result\");\n\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, testUser);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId());\n        waitForUserTaskAndExecuteIt(\"updateCatalog\", testUser);\n        waitForUserTaskAndExecuteIt(\"unreferenceCatalog\", testUser);\n        waitForUserTask(processInstance, \"result\");\n\n        final SimpleBusinessDataReference businessDataReference = (SimpleBusinessDataReference) getBusinessDataAPI()\n                .getProcessBusinessDataReference(\n                        \"productCatalog\", processInstance.getId());\n        assertThat(businessDataReference.getStorageId()).isNull();\n        assertThat(businessDataReference.getStorageIdAsString()).isNull();\n\n        disableAndDeleteProcess(definition.getId());\n    }\n\n    @Test\n    public void should_associate_the_right_address() throws Exception {\n        final Expression addressExpression = new ExpressionBuilder().createGroovyScriptExpression(\"createNewAddress\",\n                \"import \" + ADDRESS_QUALIFIED_NAME + \"; new Address(street:'32, rue Gustave Eiffel', city:'Grenoble')\",\n                ADDRESS_QUALIFIED_NAME);\n        final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression(\"createNewEmployee\",\n                \"import \" + EMPLOYEE_QUALIFIED_NAME\n                        + \"; new Employee(firstName:'John', lastName:'Doe', address:myAddress)\",\n                EMPLOYEE_QUALIFIED_NAME,\n                new ExpressionBuilder().createBusinessDataExpression(\"myAddress\", ADDRESS_QUALIFIED_NAME));\n\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance(\n                \"theProcess\", \"6.3.1\");\n        final String bizDataName = \"myEmployee\";\n        processDefinitionBuilder.addBusinessData(bizDataName, EMPLOYEE_QUALIFIED_NAME, null);\n        processDefinitionBuilder.addBusinessData(\"myAddress\", ADDRESS_QUALIFIED_NAME, addressExpression);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addAutomaticTask(\"step1\")\n                .addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand(bizDataName),\n                        OperatorType.ASSIGNMENT, null, null, employeeExpression);\n        processDefinitionBuilder.addUserTask(\"step2\", ACTOR_NAME);\n        processDefinitionBuilder.addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),\n                ACTOR_NAME, testUser);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId());\n        waitForUserTask(processInstance, \"step2\");\n\n        final Long numberOfAddresses = getNumberOfAddresses(processInstance.getId());\n        assertThat(numberOfAddresses).isEqualTo(1L);\n        final String address = getAddressAsAString(\"myAddress\", processInstance.getId());\n        assertThat(address).isEqualTo(\"Address [street=32, rue Gustave Eiffel, city=Grenoble]\");\n\n        final SimpleBusinessDataReference businessDataReference = (SimpleBusinessDataReference) getBusinessDataAPI()\n                .getProcessBusinessDataReference(\n                        bizDataName, processInstance.getId());\n\n        final Expression idExpression = new ExpressionBuilder().createExpression(\"id\",\n                String.valueOf(businessDataReference.getStorageId()),\n                Long.class.getName(), ExpressionType.TYPE_CONSTANT);\n        final Expression getEmployeeExpression = new ExpressionBuilder().createQueryBusinessDataExpression(\n                \"getEmployee\", \"Employee.findByPersistId\",\n                EMPLOYEE_QUALIFIED_NAME, idExpression);\n        final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder().createNewInstance(\"init\",\n                \"3.2\");\n        processDefBuilder.addBusinessData(bizDataName, EMPLOYEE_QUALIFIED_NAME, getEmployeeExpression);\n        processDefBuilder.addActor(ACTOR_NAME);\n        processDefBuilder.addUserTask(\"step2\", ACTOR_NAME);\n\n        final ProcessDefinition definition2 = deployAndEnableProcessWithActor(processDefBuilder.done(), ACTOR_NAME,\n                testUser);\n        final ProcessInstance processInstance2 = getProcessAPI().startProcess(definition2.getId());\n        waitForUserTask(processInstance2, \"step2\");\n\n        disableAndDeleteProcess(definition.getId());\n        disableAndDeleteProcess(definition2.getId());\n    }\n\n    @Test\n    public void should_associate_the_right_addresses() throws Exception {\n        final Expression addressExpression = new ExpressionBuilder().createGroovyScriptExpression(\"createNewAddress\",\n                \"import \" + ADDRESS_QUALIFIED_NAME + \"; new Address(street:'32, rue Gustave Eiffel', city:'Grenoble')\",\n                ADDRESS_QUALIFIED_NAME);\n        final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression(\"createNewEmployee\",\n                \"import \" + EMPLOYEE_QUALIFIED_NAME\n                        + \";import java.time.LocalDate;LocalDate localDate = LocalDate.of(1984,10,24); Employee e = new Employee(firstName:'John', lastName:'Doe', addresses:[myAddress]);e.birthDate = localDate; return e; \",\n                EMPLOYEE_QUALIFIED_NAME,\n                new ExpressionBuilder().createBusinessDataExpression(\"myAddress\", ADDRESS_QUALIFIED_NAME));\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance(\n                \"theProcess\", \"6.3.1\");\n        final String bizDataName = \"myEmployee\";\n        processDefinitionBuilder.addBusinessData(bizDataName, EMPLOYEE_QUALIFIED_NAME, null);\n        processDefinitionBuilder.addBusinessData(\"myAddress\", ADDRESS_QUALIFIED_NAME, addressExpression);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addAutomaticTask(\"step1\")\n                .addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand(bizDataName),\n                        OperatorType.ASSIGNMENT, null, null, employeeExpression);\n        processDefinitionBuilder.addUserTask(\"step2\", ACTOR_NAME)\n                .addOperation(\n                        new OperationBuilder().createBusinessDataSetAttributeOperation(bizDataName, \"addToAddresses\",\n                                ADDRESS_QUALIFIED_NAME,\n                                new ExpressionBuilder().createBusinessDataExpression(\"myAddress\",\n                                        ADDRESS_QUALIFIED_NAME)))\n                .addOperation(\n                        new OperationBuilder().createBusinessDataSetAttributeOperation(bizDataName, \"setLastName\",\n                                String.class.getName(),\n                                new ExpressionBuilder().createConstantStringExpression(\"Smith\")));\n        processDefinitionBuilder.addUserTask(\"step3\", ACTOR_NAME);\n        processDefinitionBuilder.addTransition(\"step1\", \"step2\");\n        processDefinitionBuilder.addTransition(\"step2\", \"step3\");\n\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),\n                ACTOR_NAME, testUser);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId());\n        final long userTaskId = waitForUserTask(processInstance, \"step2\");\n\n        Long numberOfAddresses = getNumberOfAddresses(processInstance.getId());\n        assertThat(numberOfAddresses).isEqualTo(1L);\n        String address = getAddressAsAString(\"myAddress\", processInstance.getId());\n        assertThat(address).isEqualTo(\"Address [street=32, rue Gustave Eiffel, city=Grenoble]\");\n\n        assignAndExecuteStep(userTaskId, testUser);\n        waitForUserTask(processInstance, \"step3\");\n\n        numberOfAddresses = getNumberOfAddresses(processInstance.getId());\n        assertThat(numberOfAddresses).isEqualTo(1L);\n        address = getAddressAsAString(\"myAddress\", processInstance.getId());\n        assertThat(address).isEqualTo(\"Address [street=32, rue Gustave Eiffel, city=Grenoble]\");\n        final String employee = getEmployeeAsAString(bizDataName, processInstance.getId());\n        assertThat(employee).isEqualTo(\n                \"Employee [firstName=John, lastName=Smith, address=null, addresses.count=2, birthDate = 1984-10-24 ]\");\n        disableAndDeleteProcess(definition.getId());\n    }\n\n    @Test\n    public void should_install_bdm_reevaluate_process_resolutions() throws Exception {\n        getTenantAdministrationAPI().pause();\n        getTenantAdministrationAPI().cleanAndUninstallBusinessDataModel();\n        getTenantAdministrationAPI().resume();\n\n        ProcessDefinition deploy = getProcessAPI()\n                .deploy(new ProcessDefinitionBuilder().createNewInstance(\"catClinicProcess\", \"1.0\")\n                        .addBusinessData(\"catPatient\", \"com.acme.Cat\", null).getProcess());\n        // this should have resolution problem: no BDM deployed\n        assertThat(getProcessAPI().getProcessResolutionProblems(deploy.getId())).hasSize(1).anySatisfy(p -> {\n            assertThat(p.getLevel()).isEqualTo(Problem.Level.ERROR);\n            assertThat(p.getResource()).isEqualTo(\"business data\");\n            assertThat(p.getResourceId()).isEqualTo(\"catPatient\");// the catPatient business data is not handled by the bdm (no bdm deployed)\n        });\n\n        //fix the BDM\n        installAndVerifyBusinessDataModel(businessObjectModel(\n                bom -> bom.addBusinessObject(businessObject(\"com.acme.Cat\",\n                        bo -> {\n                            bo.addField(stringField(\"name\"));\n                            bo.addField(stringField(\"color\"));\n                            bo.addField(stringField(\"furType\"));\n                            bo.addQuery(\"findCatByColor\", \"SELECT c from com.acme.Cat c WHERE c.color = :color\",\n                                    \"com.acme.Cat\");\n                        }))));\n\n        // process should be ok\n        assertThat(getProcessAPI().getProcessResolutionProblems(deploy.getId())).isEmpty();\n    }\n\n    /**\n     * In case the users call the install BDM multiple times, the API must handle correctly consecutive calls.\n     */\n    @Test\n    public void should_handle_consecutive_calls() throws Exception {\n        getTenantAdministrationAPI().pause();\n        getTenantAdministrationAPI().cleanAndUninstallBusinessDataModel();\n        getTenantAdministrationAPI().resume();\n\n        final byte[] zip = getZip(businessObjectModel(bom -> {\n            IntStream.range(1, 8).forEach(i -> {\n                bom.addBusinessObject(businessObject(\"com.acme.Dwarf\" + i, bo -> {\n                    bo.addField(stringField(\"name\"));\n                    bo.addField(stringField(\"personality\"));\n                    bo.addField(stringField(\"color\"));\n                    bo.addQuery(\"findDwarf\" + i + \"ByColor\",\n                            \"SELECT d from com.acme.Dwarf\" + i + \" d WHERE d.color = :color\", \"com.acme.Dwarf\" + i);\n                }));\n            });\n        }));\n        // pause only once\n        getTenantAdministrationAPI().pause();\n        //install the BDM twice with concurrent calls\n        AtomicReference<Exception> firstException = new AtomicReference<>();\n        Thread first = new Thread(() -> {\n            try {\n                final String businessDataModelVersion = getTenantAdministrationAPI().updateBusinessDataModel(zip);\n                assertThat(businessDataModelVersion).as(\"should have deployed BDM\").isNotNull();\n            } catch (Exception e) {\n                firstException.set(e);\n            }\n        });\n        AtomicReference<Exception> secondException = new AtomicReference<>(null);\n        Thread second = new Thread(() -> {\n            try {\n                Thread.sleep(200);\n                final String businessDataModelVersion = getTenantAdministrationAPI().updateBusinessDataModel(zip);\n                assertThat(businessDataModelVersion).as(\"should have deployed BDM\").isNotNull();\n            } catch (Exception e) {\n                secondException.set(e);\n            }\n        });\n        first.start();\n        second.start();\n        first.join();\n        second.join();\n        // resume and check deployment\n        getTenantAdministrationAPI().resume();\n        verifyBdmIsWellDeployed();\n        Exception e1 = firstException.get();\n        if (e1 != null) {\n            throw e1;\n        }\n        Exception e2 = secondException.get();\n        // 2nd request should throw an update exception and operation should abort before starting a transaction\n        assertThat(e2).isNotNull();\n        assertThat(e2).satisfiesAnyOf(\n                eParam -> assertThat(eParam).isInstanceOf(UnavailableLockException.class),\n                eParam -> assertThat(eParam).getCause().isInstanceOf(UnavailableLockException.class));\n    }\n\n    @Test\n    public void should_initialize_a_bo_with_empty_query_result() throws Exception {\n        //given\n        final Expression queryBusinessDataExpression = new ExpressionBuilder().createQueryBusinessDataExpression(\n                \"findQuery\",\n                \"Employee.\" + GET_EMPLOYEE_BY_LAST_NAME_QUERY_NAME, EMPLOYEE_QUALIFIED_NAME,\n                new ExpressionBuilder().createConstantStringExpression(\"lastName\", \"notExists\"));\n\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance(\n                \"emptyQueryResult\", \"1.0\");\n        final String bizDataName = \"myEmployee\";\n        processDefinitionBuilder.addBusinessData(bizDataName, EMPLOYEE_QUALIFIED_NAME, queryBusinessDataExpression);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addUserTask(\"step1\", ACTOR_NAME);\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),\n                ACTOR_NAME, testUser);\n\n        //when\n        final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId());\n        waitForUserTask(processInstance, \"step1\");\n\n        //then\n        final Map<Expression, Map<String, Serializable>> expressions = new HashMap<>();\n        final String expressionEmployee = \"retrieve_Employee\";\n        expressions.put(\n                new ExpressionBuilder().createGroovyScriptExpression(expressionEmployee,\n                        \" if (\" + bizDataName + \"==null) { return Boolean.TRUE } else {return Boolean.FALSE} \",\n                        Boolean.class.getName(),\n                        new ExpressionBuilder().createBusinessDataExpression(bizDataName, EMPLOYEE_QUALIFIED_NAME)),\n                null);\n\n        final Map<String, Serializable> evaluatedExpressions = getProcessAPI()\n                .evaluateExpressionsOnProcessInstance(processInstance.getId(), expressions);\n        assertThat(evaluatedExpressions).as(\"should not have a reference to business data\")\n                .contains(entry(expressionEmployee, Boolean.TRUE));\n\n        disableAndDeleteProcess(definition.getId());\n    }\n\n    @Test\n    public void should_initialize_a_multiple_bo_with_empty_query_result() throws Exception {\n        //given\n        final ExpressionImpl dependencyStartIndex = new ExpressionImpl();\n        dependencyStartIndex.setExpressionType(ExpressionType.TYPE_CONSTANT.name());\n        dependencyStartIndex.setName(\"startIndex\");\n        dependencyStartIndex.setReturnType(Integer.class.getName());\n        dependencyStartIndex.setContent(\"0\");\n\n        final ExpressionImpl dependencyMaxResults = new ExpressionImpl();\n        dependencyMaxResults.setExpressionType(ExpressionType.TYPE_CONSTANT.name());\n        dependencyMaxResults.setName(\"maxResults\");\n        dependencyMaxResults.setReturnType(Integer.class.getName());\n        dependencyMaxResults.setContent(\"10\");\n\n        final Expression queryBusinessDataExpression = new ExpressionBuilder().createQueryBusinessDataExpression(\n                \"findQuery\",\n                \"Employee.find\", List.class.getName(),\n                dependencyStartIndex, dependencyMaxResults);\n\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance(\n                \"multipleEmptyQueryResult\", \"1.0\");\n        final String bizDataName = \"myEmployees\";\n        processDefinitionBuilder.addBusinessData(bizDataName, EMPLOYEE_QUALIFIED_NAME, queryBusinessDataExpression)\n                .setMultiple(true);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addUserTask(\"step1\", ACTOR_NAME);\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),\n                ACTOR_NAME, testUser);\n\n        //when\n        final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId());\n        waitForUserTask(processInstance, \"step1\");\n\n        //then\n        final Map<Expression, Map<String, Serializable>> expressions = new HashMap<>();\n        final String expressionEmployee = \"retrieve_Employee\";\n        expressions.put(\n                new ExpressionBuilder().createGroovyScriptExpression(expressionEmployee,\n                        bizDataName + \".toString()\",\n                        String.class.getName(),\n                        new ExpressionBuilder().createBusinessDataExpression(bizDataName, List.class.getName())),\n                null);\n\n        final Map<String, Serializable> evaluatedExpressions = getProcessAPI()\n                .evaluateExpressionsOnProcessInstance(processInstance.getId(), expressions);\n        assertThat(evaluatedExpressions).as(\"should not have a reference to business data\")\n                .contains(entry(expressionEmployee, \"[]\"));\n\n        disableAndDeleteProcess(definition.getId());\n    }\n\n    public Long getNumberOfAddresses(final long processInstanceId) throws Exception {\n        final Map<Expression, Map<String, Serializable>> expressions = new HashMap<>(2);\n        expressions.put(\n                new ExpressionBuilder().createQueryBusinessDataExpression(\"countAddresses\", \"Address.countAddress\",\n                        Long.class.getName()),\n                Collections.emptyMap());\n\n        final Map<String, Serializable> result = getProcessAPI().evaluateExpressionsOnProcessInstance(processInstanceId,\n                expressions);\n        return (Long) result.get(\"countAddresses\");\n    }\n\n    public String getAddressAsAString(final String addressName, final long processInstanceId) throws Exception {\n        final Map<Expression, Map<String, Serializable>> expressions = new HashMap<>(2);\n        expressions.put(\n                new ExpressionBuilder().createGroovyScriptExpression(\"getAddress\",\n                        \"\\\"Address [street=\\\" + \" + addressName\n                                + \".street + \\\", city=\\\" + \" + addressName + \".city + \\\"]\\\";\",\n                        String.class.getName(),\n                        new ExpressionBuilder().createBusinessDataExpression(addressName, ADDRESS_QUALIFIED_NAME)),\n                null);\n        final Map<String, Serializable> result = getProcessAPI().evaluateExpressionsOnProcessInstance(processInstanceId,\n                expressions);\n        return (String) result.get(\"getAddress\");\n    }\n\n    private String getEmployeeAsAString(final String businessDataName, final long processInstanceId)\n            throws InvalidExpressionException {\n        final Map<Expression, Map<String, Serializable>> expressions = new HashMap<>(5);\n        final String expressionEmployee = \"retrieve_Employee\";\n        expressions.put(\n                new ExpressionBuilder().createGroovyScriptExpression(expressionEmployee,\n                        \"\\\"Employee [firstName=\\\" + \" + businessDataName + \".firstName + \\\", lastName=\\\" + \"\n                                + businessDataName\n                                + \".lastName + \\\", address=\\\" + \" + businessDataName\n                                + \".address + \\\", addresses.count=\\\" + \"\n                                + businessDataName + \".addresses.size() + \\\", birthDate = \\\"+\" + businessDataName\n                                + \".birthDate + \\\" ]\\\";\",\n                        String.class.getName(),\n                        new ExpressionBuilder().createBusinessDataExpression(businessDataName,\n                                EMPLOYEE_QUALIFIED_NAME)),\n                null);\n        try {\n            final Map<String, Serializable> evaluatedExpressions = getProcessAPI()\n                    .evaluateExpressionsOnProcessInstance(processInstanceId, expressions);\n            return (String) evaluatedExpressions.get(expressionEmployee);\n        } catch (final ExpressionEvaluationException eee) {\n            System.err.println(eee.getMessage());\n            return null;\n        }\n    }\n\n    @Test\n    public void evaluate_context_on_process_and_task() throws Exception {\n        final ProcessDefinitionBuilder p1Builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"ProcessWithContext\", \"1.0\");\n        final Expression bizDataValue = new ExpressionBuilder()\n                .createGroovyScriptExpression(\"createNewEmployee\",\n                        \"import \" + EMPLOYEE_QUALIFIED_NAME\n                                + \"; Employee e = new Employee(); e.firstName = 'Jane'; e.lastName = 'Doe'; return e;\",\n                        EMPLOYEE_QUALIFIED_NAME);\n        p1Builder.addBusinessData(\"bizData\", EMPLOYEE_QUALIFIED_NAME, bizDataValue);\n        p1Builder.addDocumentDefinition(\"myDoc\").addFile(\"myDoc.txt\").addContentFileName(\"myDoc.txt\");\n\n        final Expression bizData = new ExpressionBuilder().createBusinessDataExpression(\"bizData\",\n                EMPLOYEE_QUALIFIED_NAME);\n        p1Builder.addContextEntry(\"process_key1\",\n                new ExpressionBuilder().createGroovyScriptExpression(\"retrieve_firstname\", \"bizData.firstName\",\n                        String.class.getName(), bizData));\n        final UserTaskDefinitionBuilder task1 = p1Builder.addUserTask(\"step1\", \"actor\");\n        task1.addShortTextData(\"task1Data\", new ExpressionBuilder().createConstantStringExpression(\"task1DataValue\"));\n        task1.addContextEntry(\"task_key1\",\n                new ExpressionBuilder().createDataExpression(\"task1Data\", String.class.getName()));\n        task1.addContextEntry(\"task_key2\", new ExpressionBuilder().createConstantStringExpression(\"constantValue\"));\n        task1.addContextEntry(\"processBizDataFromTask1\",\n                new ExpressionBuilder().createGroovyScriptExpression(\"retrieve_firstname\", \"bizData.lastName\",\n                        String.class.getName(), bizData));\n        task1.addContextEntry(\"doc_key\",\n                new ExpressionBuilder().createGroovyScriptExpression(\"doc.name\", \"myDoc.fileName\",\n                        String.class.getName(),\n                        new ExpressionBuilder().createDocumentReferenceExpression(\"myDoc\")));\n        UserTaskDefinitionBuilder task2 = p1Builder.addUserTask(\"step2\", \"actor\");\n        task2.addShortTextData(\"task2Data\", new ExpressionBuilder().createConstantStringExpression(\"task2DataValue\"));\n        p1Builder.addActor(\"actor\");\n        final String myDocumentContent = \"Some document content\";\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(p1Builder.getProcess())\n                .addDocumentResource(new BarResource(\"myDoc.txt\", myDocumentContent.getBytes())).done();\n        ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchive, \"actor\", testUser);\n        ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition.getId());\n        long step1 = waitForUserTask(processInstance1.getId(), \"step1\");\n        long step2 = waitForUserTask(processInstance1.getId(), \"step2\");\n\n        assertThat(getProcessAPI().getProcessInstanceExecutionContext(processInstance1.getId()))\n                .containsOnly(entry(\"process_key1\", \"Jane\"));\n        assertThat(getProcessAPI().getUserTaskExecutionContext(step1)).containsOnly(\n                entry(\"task_key1\", \"task1DataValue\"), entry(\"task_key2\", \"constantValue\"),\n                entry(\"processBizDataFromTask1\", \"Doe\"), entry(\"doc_key\", \"myDoc.txt\"));\n        assertThat(getProcessAPI().getUserTaskExecutionContext(step2)).isEmpty();\n\n        assignAndExecuteStep(step1, testUser.getId());\n        assignAndExecuteStep(step2, testUser.getId());\n        waitForProcessToFinish(processInstance1);\n        Thread.sleep(10);\n        final ArchivedProcessInstance finalArchivedProcessInstance = getProcessAPI()\n                .getFinalArchivedProcessInstance(processInstance1.getId());\n        final ArchivedActivityInstance archivedStep1 = getProcessAPI().getArchivedActivityInstance(step1);\n        final ArchivedActivityInstance archivedStep2 = getProcessAPI().getArchivedActivityInstance(step2);\n        assertThat(getProcessAPI().getArchivedProcessInstanceExecutionContext(finalArchivedProcessInstance.getId()))\n                .containsOnly(entry(\"process_key1\", \"Jane\"));\n        assertThat(getProcessAPI().getArchivedUserTaskExecutionContext(archivedStep1.getId())).containsOnly(\n                entry(\"task_key1\", \"task1DataValue\"),\n                entry(\"task_key2\", \"constantValue\"), entry(\"processBizDataFromTask1\", \"Doe\"),\n                entry(\"doc_key\", \"myDoc.txt\"));\n        assertThat(getProcessAPI().getArchivedUserTaskExecutionContext(archivedStep2.getId())).isEmpty();\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void should_event_sub_process_only_start_element_in_the_event_sub_process() throws Exception {\n        /*\n         * We test here that an event sub process instantiation do nothing on the parent process\n         * see bug BS-15123 and BS-15275\n         */\n        //given\n        ProcessDefinitionBuilder parentProcessBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"ParentProcessWithSignalEventSubProcess\", \"1.0\");\n        parentProcessBuilder.addActor(ACTOR_NAME);\n        parentProcessBuilder.addAutomaticTask(\"updateTask\").addOperation(new OperationBuilder().createSetDocument(\n                \"myDoc\",\n                new ExpressionBuilder().createGroovyScriptExpression(\"updateDocContent\",\n                        \"import org.bonitasoft.engine.bpm.document.DocumentValue;return new DocumentValue('updatedContents'.getBytes(),'plain/text','myDoc.txt');\",\n                        DocumentValue.class.getName())));\n        parentProcessBuilder.addUserTask(\"userTask\", ACTOR_NAME);\n        parentProcessBuilder.addTransition(\"updateTask\", \"userTask\");\n        parentProcessBuilder.addContract().addInput(\"employeeName\", Type.TEXT, \"the name of the business data\");\n        parentProcessBuilder.addBusinessData(\"myBusinessData\", EMPLOYEE_QUALIFIED_NAME,\n                new ExpressionBuilder().createGroovyScriptExpression(\"initBD\",\n                        \"import \" + EMPLOYEE_QUALIFIED_NAME + \"\\n\" +\n                                \"Employee e = new Employee(); e.firstName = 'Jules'; e.lastName = employeeName; return e;\",\n                        EMPLOYEE_QUALIFIED_NAME,\n                        new ExpressionBuilder().createContractInputExpression(\"employeeName\", String.class.getName())));\n        parentProcessBuilder.addShortTextData(\"textData\",\n                new ExpressionBuilder().createConstantStringExpression(\"parentVar\"));\n        parentProcessBuilder.addIntegerData(\"intData\", new ExpressionBuilder().createConstantIntegerExpression(1));\n        parentProcessBuilder.addDocumentDefinition(\"myDoc\")\n                .addInitialValue(new ExpressionBuilder().createGroovyScriptExpression(\"updateDocContent\",\n                        \"import org.bonitasoft.engine.bpm.document.DocumentValue;return new DocumentValue('initialContent'.getBytes(),'plain/text','myDoc.txt');\",\n                        DocumentValue.class.getName()));\n        parentProcessBuilder.addDocumentListDefinition(\"MyList\")\n                .addInitialValue(new ExpressionBuilder().createGroovyScriptExpression(\"updateDocContent\",\n                        \"import org.bonitasoft.engine.bpm.document.DocumentValue;return [new DocumentValue('initialContent'.getBytes(),'plain/text','myDoc1.txt'), new DocumentValue('initialContent'.getBytes(),'plain/text','myDoc2.txt')];\",\n                        List.class.getName()));\n        //construct sub process\n        SubProcessDefinitionBuilder subProcessBuilder = parentProcessBuilder\n                .addSubProcess(\"interruptWithSignalProcess\", true).getSubProcessBuilder();\n        StartEventDefinitionBuilder startEventDefinitionBuilder = subProcessBuilder.addStartEvent(\"signalStart\");\n        startEventDefinitionBuilder.addSignalEventTrigger(\"theSignal\");\n        subProcessBuilder.addUserTask(\"userTaskInSubProcess\", ACTOR_NAME);\n        subProcessBuilder.addEndEvent(\"endSubProcess\");\n        subProcessBuilder.addTransition(\"signalStart\", \"userTaskInSubProcess\");\n        subProcessBuilder.addTransition(\"userTaskInSubProcess\", \"endSubProcess\");\n        subProcessBuilder.addShortTextData(\"textDataInSub\",\n                new ExpressionBuilder().createConstantStringExpression(\"childVar\"));\n        subProcessBuilder.addDoubleData(\"value\", new ExpressionBuilder().createConstantDoubleExpression(10.0));\n        DesignProcessDefinition processDefinition1 = parentProcessBuilder.done();\n        BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(processDefinition1);\n        ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchiveBuilder.done(), ACTOR_NAME,\n                testUser);\n        ProcessInstance processInstance = getProcessAPI().startProcessWithInputs(processDefinition.getId(),\n                Collections.singletonMap(\"employeeName\", \"Doe\"));\n        //when\n        waitForUserTask(\"userTask\");\n        assertThat(new String(getProcessAPI().getDocumentContent(\n                getProcessAPI().getLastDocument(processInstance.getId(), \"myDoc\").getContentStorageId())))\n                .isEqualTo(\"updatedContents\");\n\n        getProcessAPI().sendSignal(\"theSignal\");\n        //then\n        ActivityInstance eventSubProcessActivity = getProcessAPI()\n                .getActivityInstance(waitForUserTask(\"userTaskInSubProcess\"));\n        //instantiation of the event sub process work and did not reinitialized elements\n        assertThat(new String(getProcessAPI().getDocumentContent(\n                getProcessAPI().getLastDocument(processInstance.getId(), \"myDoc\").getContentStorageId())))\n                .isEqualTo(\"updatedContents\");\n        assertThat(getProcessAPI().getDocumentList(processInstance.getId(), \"MyList\", 0, 100)).hasSize(2);\n        try {\n            getProcessAPI().getLastDocument(eventSubProcessActivity.getParentProcessInstanceId(), \"myDoc\");\n            fail(\"should not be found\");\n        } catch (DocumentNotFoundException ignored) {\n        }\n        assertThat(\n                getProcessAPI().getDocumentList(eventSubProcessActivity.getParentProcessInstanceId(), \"MyList\", 0, 100))\n                .isEmpty();\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void should_be_able_to_update_business_object_in_event_sub_process() throws Exception {\n        //given\n        ProcessDefinitionBuilder parentProcessBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"UpdateBusinessDataInEventSubProcess\", \"1.0\");\n        parentProcessBuilder.addActor(ACTOR_NAME);\n        parentProcessBuilder.addUserTask(\"userTask\", ACTOR_NAME);\n        parentProcessBuilder.addContextEntry(\"ref_myBusinessData\",\n                new ExpressionBuilder().createBusinessDataReferenceExpression(\"myBusinessData\"));\n        parentProcessBuilder.addContract().addInput(\"employeeName\", Type.TEXT, \"the name of the business data\");\n        parentProcessBuilder.addBusinessData(\"myBusinessData\", EMPLOYEE_QUALIFIED_NAME,\n                new ExpressionBuilder().createGroovyScriptExpression(\"initBD\",\n                        \"import \" + EMPLOYEE_QUALIFIED_NAME + \"\\n\" +\n                                \"Employee e = new Employee(); e.firstName = 'Jules'; e.lastName = employeeName; return e;\",\n                        EMPLOYEE_QUALIFIED_NAME,\n                        new ExpressionBuilder().createContractInputExpression(\"employeeName\", String.class.getName())));\n        //construct sub process\n        SubProcessDefinitionBuilder subProcessBuilder = parentProcessBuilder.addSubProcess(\"updateBusinessData\", true)\n                .getSubProcessBuilder();\n        StartEventDefinitionBuilder startEventDefinitionBuilder = subProcessBuilder.addStartEvent(\"signalStart\");\n        startEventDefinitionBuilder.addSignalEventTrigger(\"theSignal\");\n        subProcessBuilder.addAutomaticTask(\"updateBD\")\n                .addOperation(new OperationBuilder().createBusinessDataSetAttributeOperation(\"myBusinessData\",\n                        \"setLastName\", String.class.getName(),\n                        new ExpressionBuilder().createConstantStringExpression(\"newName\")));\n        subProcessBuilder.addUserTask(\"userTaskInSubProcess\", ACTOR_NAME);\n        subProcessBuilder.addEndEvent(\"endSubProcess\");\n        subProcessBuilder.addTransition(\"signalStart\", \"updateBD\");\n        subProcessBuilder.addTransition(\"updateBD\", \"userTaskInSubProcess\");\n        subProcessBuilder.addTransition(\"userTaskInSubProcess\", \"endSubProcess\");\n        DesignProcessDefinition processDefinition1 = parentProcessBuilder.done();\n        BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(processDefinition1);\n        ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchiveBuilder.done(), ACTOR_NAME,\n                testUser);\n        ProcessInstance processInstance = getProcessAPI().startProcessWithInputs(processDefinition.getId(),\n                Collections.singletonMap(\"employeeName\", \"Doe\"));\n        waitForUserTask(\"userTask\");\n        assertThatJson(\n                getBusinessDataAsJson((SimpleBusinessDataReference) getProcessAPI().getProcessInstanceExecutionContext(\n                        processInstance.getId()).get(\"ref_myBusinessData\")))\n                .node(\"lastName\").isEqualTo(\"\\\"Doe\\\"\");\n        //when\n        getProcessAPI().sendSignal(\"theSignal\");\n        waitForUserTask(\"userTaskInSubProcess\");\n\n        //then\n        assertThatJson(\n                getBusinessDataAsJson((SimpleBusinessDataReference) getProcessAPI().getProcessInstanceExecutionContext(\n                        processInstance.getId()).get(\"ref_myBusinessData\")))\n                .node(\"lastName\").isEqualTo(\"\\\"newName\\\"\");\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    private String getBusinessDataAsJson(SimpleBusinessDataReference myBusinessData)\n            throws CommandNotFoundException, CommandParameterizationException, CommandExecutionException {\n        final Map<String, Serializable> parameters = new HashMap<>();\n        parameters.put(\"businessDataId\", myBusinessData.getStorageId());\n        parameters.put(\"entityClassName\", EMPLOYEE_QUALIFIED_NAME);\n        parameters.put(\"businessDataURIPattern\", \"/businessdata/{className}/{id}/{field}\");\n        return (String) getCommandAPI().execute(\"getBusinessDataById\", parameters);\n    }\n\n    @Test\n    public void shouldRetrieveBDMObjectsInLeftOperandsInCatchMessages() throws Exception {\n\n        ProcessDefinitionBuilder throwProcessBuilder = new ProcessDefinitionBuilder().createNewInstance(\"MSG\", \"1.0\");\n        throwProcessBuilder.addStartEvent(\"startEvent\");\n        throwProcessBuilder.addActor(ACTOR_NAME);\n        IntermediateThrowEventDefinitionBuilder intermediateThrowEvent = throwProcessBuilder\n                .addIntermediateThrowEvent(\"sendMessage\");\n        Expression targetProcessExpression = new ExpressionBuilder().createConstantStringExpression(\"BDM\");\n        Expression targetFlowNodeExpression = new ExpressionBuilder().createConstantStringExpression(\"message1\");\n        ThrowMessageEventTriggerBuilder messageEventTriggerBuilder = intermediateThrowEvent.addMessageEventTrigger(\n                \"msg_name\", targetProcessExpression,\n                targetFlowNodeExpression);\n        messageEventTriggerBuilder.addMessageContentExpression(\n                new ExpressionBuilder().createConstantStringExpression(\"msg_name\"),\n                new ExpressionBuilder().createConstantStringExpression(\"fabrice\"));\n        throwProcessBuilder.addTransition(\"startEvent\", \"sendMessage\");\n        DesignProcessDefinition designThrowProcessDefinition = throwProcessBuilder.done();\n        ProcessDefinitionBuilder catchProcessBuilder = new ProcessDefinitionBuilder().createNewInstance(\"BDM\", \"1.0\");\n        catchProcessBuilder.addStartEvent(\"startEvent\");\n        catchProcessBuilder.addActor(ACTOR_NAME);\n        catchProcessBuilder.addBusinessData(\"myBusinessData\", EMPLOYEE_QUALIFIED_NAME,\n                new ExpressionBuilder().createGroovyScriptExpression(\"initBD\",\n                        \"import \" + EMPLOYEE_QUALIFIED_NAME + \"\\n\" +\n                                \"Employee e = new Employee(); e.firstName = 'Jules'; e.lastName = 'employeeName'; return e;\",\n                        EMPLOYEE_QUALIFIED_NAME));\n        CatchMessageEventTriggerDefinitionBuilder catchMessageEventTriggerDefinitionBuilder = catchProcessBuilder\n                .addIntermediateCatchEvent(\"message1\")\n                .addMessageEventTrigger(\"msg_name\");\n        catchMessageEventTriggerDefinitionBuilder\n                .addOperation(new OperationBuilder().createBusinessDataSetAttributeOperation(\"myBusinessData\",\n                        \"setFirstName\", String.class.getName(),\n                        new ExpressionBuilder().createDataExpression(\"msg_name\", String.class.getName())));\n        catchProcessBuilder.addTransition(\"startEvent\", \"message1\");\n        DesignProcessDefinition designCatchProcessDefinition = catchProcessBuilder.done();\n\n        BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(designCatchProcessDefinition);\n        ProcessDefinition catchProcessDefinition = deployAndEnableProcessWithActor(businessArchiveBuilder.done(),\n                ACTOR_NAME, testUser);\n        ProcessInstance catchProcessInstance = getProcessAPI().startProcessWithInputs(catchProcessDefinition.getId(),\n                Collections.emptyMap());\n        businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(designThrowProcessDefinition);\n        ProcessDefinition throwProcessDefinition = deployAndEnableProcessWithActor(businessArchiveBuilder.done(),\n                ACTOR_NAME, testUser);\n        waitForEventInWaitingState(catchProcessInstance, \"message1\");\n        ProcessInstance throwProcessInstance = getProcessAPI().startProcess(throwProcessDefinition.getId());\n\n        // Message should have been received and process should have finished:\n        waitForProcessToFinish(throwProcessInstance);\n        waitForProcessToFinish(catchProcessInstance);\n\n        disableAndDeleteProcess(catchProcessDefinition);\n        disableAndDeleteProcess(throwProcessDefinition);\n    }\n\n    @Test\n    public void should_connector_using_bdm_still_work_after_bdm_update() throws Exception {\n\n        installAndVerifyBusinessDataModel(bomMyObjectWith1Field());\n\n        //connector that call setter on the bdm object\n        byte[] setNameConnectorJar = IOUtil.generateJar(singletonList(retrieveClientBDMModelJar()),\n                \"com.acme.SetNameOfBDM\",\n                \"package com.acme;\",\n                \"public class SetNameOfBDM extends org.bonitasoft.engine.connector.AbstractConnector {\",\n                \"    public void validateInputParameters() {}\",\n                \"    protected void executeBusinessLogic() {\",\n                \"       System.out.println(\\\"setting the name of a bdm object in a connector:\\\"+getInputParameter(\\\"objectToUpdate\\\"));\",\n                \"       ((com.acme.MyObject)getInputParameter(\\\"objectToUpdate\\\")).setName(\\\"someName\\\");\",\n                \"       System.out.println(\\\"Done!\\\"+getInputParameter(\\\"objectToUpdate\\\"));\",\n                \"   }\",\n                \"}\");\n\n        //process that call the connector on the bdm object\n        ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"ProcessThatUpdateBDM\", \"1.0\");\n        processBuilder.addBusinessData(\"myObject\", \"com.acme.MyObject\",\n                new ExpressionBuilder().createGroovyScriptExpression(\"init\",\n                        \"new com.acme.MyObject()\", \"com.acme.MyObject\"));\n        processBuilder.addAutomaticTask(\"task1\")\n                .addConnector(\"setNameOfBDM\", \"setNameOfBDM\", \"1.0\", ConnectorEvent.ON_ENTER)\n                .addInput(\"objectToUpdate\",\n                        new ExpressionBuilder().createBusinessDataExpression(\"myObject\", \"com.acme.MyObject\"));\n        BusinessArchive bar = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(processBuilder.done())\n                .addClasspathResource(new BarResource(\"setNameConnectorJar.jar\", setNameConnectorJar))\n                .addConnectorImplementation(generateConnectorImplementation(\"setNameOfBDM\", \"1.0\",\n                        \"com.acme.SetNameOfBDM\", \"setNameConnectorJar.jar\"))\n                .done();\n        ProcessDefinition processDefinition = getProcessAPI().deployAndEnableProcess(bar);\n        //        log.\n        //start and wait for finish, it should work\n        log.info(\"start process 1\");\n        ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForProcessToFinish(processInstance);\n\n        log.info(\"install new BDM\");\n        //deploy a new version of the bdm that is compatible\n        installAndVerifyBusinessDataModel(bomMyObjectWith2Fields());\n\n        //connector should still work\n\n        log.info(\"start process 2\");\n        ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinition.getId());\n        waitForProcessToFinish(processInstance2);\n    }\n\n    private Path retrieveClientBDMModelJar() throws BusinessDataRepositoryException, IOException {\n        byte[] clientBDMZip = getTenantAdministrationAPI().getClientBDMZip();\n        byte[] bdmModelJar = org.bonitasoft.engine.io.IOUtils.unzip(clientBDMZip).get(\"bdm-model.jar\");\n        Path tmpBdmJar = Files.createTempFile(\"tmpbdmJar\", \".jar\");\n        Files.write(tmpBdmJar, bdmModelJar);\n        return tmpBdmJar;\n    }\n\n    private BusinessObjectModel bomMyObjectWith2Fields() {\n        BusinessObjectModel bom2 = new BusinessObjectModel();\n        BusinessObject businessObject2 = new BusinessObject();\n        SimpleField name = new SimpleField();\n        name.setName(\"name\");\n        name.setType(FieldType.STRING);\n        SimpleField age = new SimpleField();\n        age.setName(\"age\");\n        age.setType(FieldType.INTEGER);\n        businessObject2.addField(name);\n        businessObject2.addField(age);\n        businessObject2.setQualifiedName(\"com.acme.MyObject\");\n        bom2.addBusinessObject(businessObject2);\n        return bom2;\n    }\n\n    private BusinessObjectModel bomMyObjectWith1Field() {\n        BusinessObjectModel bom1 = new BusinessObjectModel();\n        BusinessObject businessObject1 = new BusinessObject();\n        SimpleField name = new SimpleField();\n        name.setName(\"name\");\n        name.setType(FieldType.STRING);\n        businessObject1.addField(name);\n        businessObject1.setQualifiedName(\"com.acme.MyObject\");\n        bom1.addBusinessObject(businessObject1);\n        return bom1;\n    }\n\n    class AddressRef {\n\n        private final String varName;\n\n        private final String street;\n\n        private final String city;\n\n        AddressRef(final String varName, final String street, final String city) {\n            this.varName = varName;\n            this.street = street;\n            this.city = city;\n        }\n\n        public Expression getExpression() throws InvalidExpressionException {\n            return new ExpressionBuilder().createBusinessDataExpression(getVarName(), ADDRESS_QUALIFIED_NAME);\n        }\n\n        public String getVarName() {\n            return varName;\n        }\n\n        public String getStreet() {\n            return street;\n        }\n\n        public String getCity() {\n            return city;\n        }\n\n        public Operation getCreationOperation() throws InvalidExpressionException {\n            String sb = \"import \" + ADDRESS_QUALIFIED_NAME + \"\\n\" +\n                    \"Address a = new Address();\\n\" +\n                    \"a.street ='\" + street + \"'\\n\" +\n                    \"a.city ='\" + city + \"'\\n\" +\n                    \"return a;\";\n            final Expression addressExpression = new ExpressionBuilder().createGroovyScriptExpression(\n                    \"createAddress\" + varName,\n                    sb,\n                    ADDRESS_QUALIFIED_NAME);\n            return new OperationBuilder().createNewInstance()\n                    .setLeftOperand(new LeftOperandBuilder().createBusinessDataLeftOperand(varName))\n                    .setType(OperatorType.ASSIGNMENT)\n                    .setRightOperand(addressExpression).done();\n        }\n\n    }\n\n    private BusinessObjectModel buildBOMWithInvalidQuery() {\n\n        return businessObjectModel(\n                bom -> bom.addBusinessObject(businessObject(\"com.acme.Cat\",\n                        bo -> {\n                            bo.addField(stringField(\"name\"));\n                            bo.addField(stringField(\"color\"));\n                            bo.addField(stringField(\"furType\"));\n                            bo.addQuery(\"findCatByColor\", \"SELECT c from com.acme.Cat \\\" c WHERE c.color = :color\",\n                                    \"com.acme.Cat\");\n                        })));\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/client/BonitaClientXMLTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.client;\n\nimport static org.junit.Assert.assertEquals;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.ApiAccessType;\nimport org.bonitasoft.engine.api.PlatformAPIAccessor;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.home.BonitaHome;\nimport org.bonitasoft.engine.util.APITypeManager;\nimport org.junit.After;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.RestoreSystemProperties;\nimport org.junit.rules.TestRule;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class BonitaClientXMLTest {\n\n    private static final String BONITA_HOME_CLIENT_INVALID_API_TYPE = \"build/bonita_home_client_invalidAPIType\";\n\n    private static final String BONITA_HOME_CLIENT_HTTP = \"build/bonita_home_client_HTTP\";\n\n    @Rule\n    public final TestRule restoreSystemProperties = new RestoreSystemProperties();\n\n    @After\n    public void tearDown() {\n        TenantAPIAccessor.refresh();\n    }\n\n    @Test\n    public void testGetAPIType() throws Exception {\n        ApiAccessType apiType = APITypeManager.getAPIType();\n        assertEquals(ApiAccessType.LOCAL, apiType);\n    }\n\n    @Test\n    public void testGetAPITypeParameters() throws Exception {\n        Map<String, String> parameters = APITypeManager.getAPITypeParameters();\n        final Map<String, String> expectedParameters = new HashMap<>();\n        assertEquals(expectedParameters, parameters);\n\n        TenantAPIAccessor.refresh();\n        System.setProperty(BonitaHome.BONITA_HOME, BONITA_HOME_CLIENT_HTTP);\n        parameters = APITypeManager.getAPITypeParameters();\n        expectedParameters.put(\"org.bonitasoft.engine.api-type.server.url\", \"127.255.0.123\");\n        expectedParameters.put(\"org.bonitasoft.engine.api-type.application.name\", \"MyBonitaInstallation\");\n        assertEquals(expectedParameters, parameters);\n    }\n\n    @Test(expected = UnknownAPITypeException.class)\n    public void testCannotUseAnInvalidAPITypePlatForm() throws Exception {\n        System.setProperty(BonitaHome.BONITA_HOME, BONITA_HOME_CLIENT_INVALID_API_TYPE);\n        PlatformAPIAccessor.getPlatformLoginAPI();\n    }\n\n    @Test(expected = UnknownAPITypeException.class)\n    public void testCannotUseAnInvalidAPITypeTenants() throws Exception {\n        System.setProperty(BonitaHome.BONITA_HOME, BONITA_HOME_CLIENT_INVALID_API_TYPE);\n        TenantAPIAccessor.getLoginAPI();\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/command/AdvancedStartProcessCommandIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport static org.bonitasoft.engine.command.helper.designer.Transition.fails;\nimport static org.bonitasoft.engine.command.helper.designer.Transition.meet;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.bpm.document.DocumentValue;\nimport org.bonitasoft.engine.bpm.flownode.GatewayType;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.command.helper.ProcessDeployer;\nimport org.bonitasoft.engine.command.helper.designer.Gateway;\nimport org.bonitasoft.engine.command.helper.designer.SimpleProcessDesigner;\nimport org.bonitasoft.engine.command.helper.designer.UserTask;\nimport org.bonitasoft.engine.command.helper.expectation.TestUtils;\nimport org.bonitasoft.engine.connectors.TestConnectorWithOutput;\nimport org.bonitasoft.engine.connectors.VariableStorage;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.expression.InvalidExpressionException;\nimport org.bonitasoft.engine.operation.LeftOperandBuilder;\nimport org.bonitasoft.engine.operation.Operation;\nimport org.bonitasoft.engine.operation.OperationBuilder;\nimport org.bonitasoft.engine.operation.OperatorType;\nimport org.junit.After;\nimport org.junit.Test;\n\n/**\n * Created by Vincent Elcrin\n * Date: 11/12/13\n * Time: 09:45\n */\npublic class AdvancedStartProcessCommandIT extends TestWithUser {\n\n    private static final String CONNECTOR_OUTPUT_NAME = \"output1\";\n\n    private static final String CONNECTOR_INPUT_NAME = \"input1\";\n\n    private static final String CONNECTOR_WITH_OUTPUT_ID = \"org.bonitasoft.connector.testConnectorWithOutput\";\n\n    private final SimpleProcessDesigner designer = new SimpleProcessDesigner(getProcessDefinitionBuilder());\n\n    private final TestUtils wrapper = new TestUtils(this);\n\n    private final ProcessDeployer processDeployer = getProcessDeployer();\n\n    @After\n    public void afterTest() throws BonitaException {\n        processDeployer.clean();\n        VariableStorage.clearAll();\n    }\n\n    @Test\n    public void should_start_a_sequential_process() throws Exception {\n        final ProcessDefinition processDefinition = processDeployer.deploy(designer\n                .start()\n                .then(new UserTask(\"step 1\"))\n                .then(new UserTask(\"step 2\"))\n                .then(new UserTask(\"step 3\"))\n                .end());\n\n        final TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(), \"step 2\");\n\n        process.expect(\"step 2\").toBeReady();\n        process.expect(\"start\", \"step 1\").toNotHaveArchives();\n    }\n\n    @Test\n    public void should_start_a_sequential_process_with_variables() throws Exception {\n        final ProcessDefinitionBuilder builder = getProcessDefinitionBuilder();\n        builder.addShortTextData(\"variable\", new ExpressionBuilder().createConstantStringExpression(\"default\"));\n        final SimpleProcessDesigner designer = new SimpleProcessDesigner(builder);\n        final ProcessDefinition processDefinition = processDeployer.deploy(designer\n                .start()\n                .then(new UserTask(\"step 1\"))\n                .then(new UserTask(\"step 2\"))\n                .then(new UserTask(\"step 3\"))\n                .end());\n\n        final TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(),\n                \"step 2\",\n                Arrays.asList(createSetDataOperation(\"variable\", \"Done!\")),\n                Collections.<String, Serializable> emptyMap());\n\n        process.expectVariable(\"variable\").toBe(\"Done!\");\n    }\n\n    @Test\n    public void should_be_able_to_start_a_sequential_process_with_a_document() throws Exception {\n        final ProcessDefinitionBuilder builder = getProcessDefinitionBuilder();\n        builder.addDocumentDefinition(\"document\");\n        final ProcessDefinition processDefinition = processDeployer.deploy(designer\n                .start()\n                .then(new UserTask(\"step 1\"))\n                .then(new UserTask(\"step 2\"))\n                .then(new UserTask(\"step 3\"))\n                .end());\n\n        final TestUtils.Process process = startProcess(user.getId(),\n                processDefinition.getId(),\n                \"step 2\",\n                Collections.singletonList(new OperationBuilder().createSetDocument(\"document\",\n                        new ExpressionBuilder().createInputExpression(\"value\", DocumentValue.class.getName()))),\n                Collections.<String, Serializable> singletonMap(\n                        \"value\", new DocumentValue(\"content\".getBytes(), \"plain/text\", \"document\")));\n\n        process.expectDocument(\"document\").toBe(\"content\");\n    }\n\n    @Test\n    public void should_be_able_to_start_a_process_with_a_parallel_split() throws Exception {\n        final ProcessDefinition processDefinition = processDeployer.deploy(designer\n                .start()\n                .then(new UserTask(\"step 1\"))\n                .then(new Gateway(\"split\", GatewayType.PARALLEL))\n                .then(new UserTask(\"step 2\"), new UserTask(\"step 3\"))\n                .end());\n\n        final TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(), \"step 1\");\n        process.execute(user, \"step 1\", \"step 2\", \"step 3\");\n\n        process.isExpected().toFinish();\n        process.expect(\"start\").toNotHaveArchives();\n    }\n\n    @Test\n    public void should_be_able_to_start_a_process_with_an_exclusive_merge() throws Exception {\n        final ProcessDefinition processDefinition = processDeployer.deploy(designer\n                .start()\n                .then(new UserTask(\"step 1\"), new UserTask(\"step 2\"))\n                .then(new Gateway(\"exclusive\", GatewayType.EXCLUSIVE))\n                .then(new UserTask(\"step 3\"))\n                .end());\n\n        final TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(), \"step 2\");\n        process.execute(user, \"step 2\", \"step 3\");\n\n        process.isExpected().toFinish();\n        process.expect(\"start\").toNotHaveArchives();\n        process.expect(\"step 3\").toBeExecuted(1);\n    }\n\n    @Test\n    public void should_be_able_to_start_a_process_with_an_exclusive_split() throws Exception {\n        final Expression condition = new ExpressionBuilder().createConstantBooleanExpression(true);\n        final ProcessDefinition processDefinition = processDeployer.deploy(designer\n                .start()\n                .then(new UserTask(\"step 1\"))\n                .then(new Gateway(\"exclusive\", GatewayType.EXCLUSIVE))\n                .then(\n                        new UserTask(\"step 2\").when(\"exclusive\", fails()),\n                        new UserTask(\"step 3\").when(\"exclusive\", meet(condition)))\n                .end());\n\n        final TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(), \"step 1\");\n        process.execute(user, \"step 1\", \"step 3\");\n\n        process.isExpected().toFinish();\n        process.expect(\"start\").toNotHaveArchives();\n        process.expect(\"step 3\").toBeExecuted(1);\n    }\n\n    @Test\n    public void should_be_able_to_start_a_process_before_an_inclusive() throws Exception {\n        final ProcessDefinition processDefinition = processDeployer.deploy(designer\n                .start()\n                .then(new UserTask(\"step 1\"))\n                .then(new Gateway(\"inclusive 1\", GatewayType.INCLUSIVE))\n                .then(new UserTask(\"step 2\"), new UserTask(\"step 3\"))\n                .then(new Gateway(\"inclusive 2\", GatewayType.INCLUSIVE))\n                .then(new UserTask(\"step 4\"))\n                .end());\n\n        final TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(), \"step 1\");\n        process.execute(user, \"step 1\", \"step 2\", \"step 3\", \"step 4\");\n\n        process.isExpected().toFinish();\n        process.expect(\"start\").toNotHaveArchives();\n    }\n\n    private Operation createSetDataOperation(final String name, final String value) throws InvalidExpressionException {\n        return new OperationBuilder().createSetDataOperation(name,\n                new ExpressionBuilder().createConstantStringExpression(value));\n    }\n\n    private ProcessDefinitionBuilder getProcessDefinitionBuilder() {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder();\n        builder.createNewInstance(\"Designed by designer\", \"1.0\")\n                .addActor(\"actor\")\n                .addDescription(\"Coding all-night-long\");\n        return builder;\n    }\n\n    private TestUtils.Process startProcess(final long startedBy, final long processDefinitionId,\n            final String activityName) throws Exception {\n        return startProcess(startedBy, processDefinitionId, activityName, null, null);\n    }\n\n    private TestUtils.Process startProcess(final long startedBy, final long processDefinitionId,\n            final String activityName, final List<Operation> operations,\n            final Map<String, Serializable> context) throws Exception {\n        final Map<String, Serializable> parameters = new HashMap<>();\n        parameters.put(\"started_by\", startedBy);\n        parameters.put(\"process_definition_id\", processDefinitionId);\n        parameters.put(\"activity_name\", activityName);\n        if (operations != null) {\n            parameters.put(\"operations\", new ArrayList<>(operations));\n        }\n        if (context != null) {\n            parameters.put(\"context\", new HashMap<>(context));\n        }\n\n        return wrapper.wrap((ProcessInstance) getCommandAPI().execute(\"advancedStartProcessCommand\", parameters));\n    }\n\n    private ProcessDeployer getProcessDeployer() {\n        return new ProcessDeployer() {\n\n            @Override\n            public ProcessDefinition deploy(final DesignProcessDefinition design) throws BonitaException {\n                return deployAndEnableProcessWithActor(design, \"actor\", user);\n            }\n\n            @Override\n            public void clean(final ProcessDefinition processDefinition) throws BonitaException {\n                disableAndDeleteProcess(processDefinition);\n            }\n        };\n    }\n\n    @Test\n    public void advancedStartProcessCommandWithConnectorOnEnterOnProcess() throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addShortTextData(\"outputOfConnector\", null);\n        processDefinitionBuilder.addUserTask(\"step1\", ACTOR_NAME).addUserTask(\"step2\", ACTOR_NAME);\n        processDefinitionBuilder.addConnector(\"myConnector\", CONNECTOR_WITH_OUTPUT_ID, \"1.0\", ConnectorEvent.ON_ENTER)\n                .addInput(CONNECTOR_INPUT_NAME, new ExpressionBuilder().createConstantStringExpression(\"a\"))\n                .addOutput(new LeftOperandBuilder().createNewInstance().setName(\"outputOfConnector\").done(),\n                        OperatorType.ASSIGNMENT, \"=\", \"\",\n                        new ExpressionBuilder().createInputExpression(CONNECTOR_OUTPUT_NAME, String.class.getName()));\n        processDefinitionBuilder.addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActorAndConnector(\n                processDefinitionBuilder, ACTOR_NAME, user,\n                \"TestConnectorWithOutput.impl\", TestConnectorWithOutput.class, \"TestConnectorWithOutput.jar\");\n\n        // Start the process with the command on the step2\n        final Map<String, Serializable> parametersCommand = new HashMap<>();\n        parametersCommand.put(\"started_by\", user.getId());\n        parametersCommand.put(\"process_definition_id\", processDefinition.getId());\n        parametersCommand.put(\"activity_name\", \"step2\");\n        // command API execution\n        getCommandAPI().execute(\"advancedStartProcessCommand\", parametersCommand);\n\n        waitForUserTask(\"step2\");\n\n        // Clean\n        disableAndDeleteProcess(processDefinition);\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/command/CommandIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport static org.bonitasoft.engine.commons.io.IOUtil.generateJar;\nimport static org.junit.Assert.*;\n\nimport java.io.IOException;\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.TestWithTechnicalUser;\nimport org.bonitasoft.engine.api.CommandAPI;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.junit.Test;\n\npublic class CommandIT extends TestWithTechnicalUser {\n\n    @Test(expected = AlreadyExistsException.class)\n    public void commandAlreadyExistsException() throws Exception {\n        getCommandAPI().addDependency(\"commands\", \"jar\".getBytes());\n        getCommandAPI().register(\"command1\", \"command description\", \"implementation\");\n        try {\n            getCommandAPI().register(\"command1\", \"command description\", \"implementation\");\n        } finally {\n            getCommandAPI().unregister(\"command1\");\n            getCommandAPI().removeDependency(\"commands\");\n        }\n    }\n\n    @Test(expected = CommandNotFoundException.class)\n    public void commandNotFoundException() throws BonitaException {\n        getCommandAPI().getCommand(\"b\");\n    }\n\n    @Test(expected = CommandNotFoundException.class)\n    public void executeUnknownCommand() throws BonitaException {\n        final Map<String, Serializable> parameters = new HashMap<>();\n        parameters.put(\"n1\", \"v1\");\n        getCommandAPI().execute(\"com\", parameters);\n    }\n\n    @Test\n    public void executeCommandWithParameters() throws BonitaException, IOException {\n        final byte[] byteArray = generateJar(IntegerCommand.class);\n        getCommandAPI().addDependency(\"commands\", byteArray);\n        getCommandAPI().register(\"intReturn\", \"Retrieving the integer value\",\n                \"org.bonitasoft.engine.command.IntegerCommand\");\n        final Map<String, Serializable> parameters = new HashMap<>();\n        parameters.put(\"int\", 83);\n        final Integer actual = (Integer) getCommandAPI().execute(\"intReturn\", parameters);\n        assertEquals(Integer.valueOf(83), actual);\n        getCommandAPI().unregister(\"intReturn\");\n        getCommandAPI().removeDependency(\"commands\");\n    }\n\n    @Test(expected = CommandParameterizationException.class)\n    public void commandThrowsCommandParameterizationException() throws BonitaException, IOException {\n        final byte[] byteArray = generateJar(ParameterizationExceptionCommand.class);\n        getCommandAPI().addDependency(\"commands\", byteArray);\n        getCommandAPI().register(\"except\", \"Throws ParameterizationException\",\n                \"org.bonitasoft.engine.command.ParameterizationExceptionCommand\");\n        final Map<String, Serializable> parameters = new HashMap<>();\n        parameters.put(\"key\", 83);\n        try {\n            getCommandAPI().execute(\"except\", parameters);\n        } finally {\n            getCommandAPI().unregister(\"except\");\n            getCommandAPI().removeDependency(\"commands\");\n        }\n    }\n\n    @Test(expected = CommandExecutionException.class)\n    public void commandThrowsCommandExecutionException() throws BonitaException, IOException {\n        final byte[] byteArray = generateJar(ExecutionExceptionCommand.class);\n        getCommandAPI().addDependency(\"commands\", byteArray);\n        getCommandAPI().register(\"except\", \"Throws ExecutionExceptionCommand\",\n                \"org.bonitasoft.engine.command.ExecutionExceptionCommand\");\n        final Map<String, Serializable> parameters = new HashMap<>();\n        parameters.put(\"key\", 83);\n        try {\n            getCommandAPI().execute(\"except\", parameters);\n        } finally {\n            getCommandAPI().unregister(\"except\");\n            getCommandAPI().removeDependency(\"commands\");\n        }\n    }\n\n    @Test\n    public void createCommand() throws BonitaException {\n        try {\n            getCommandAPI().addDependency(\"commands\", \"jar\".getBytes());\n            final CommandDescriptor command = getCommandAPI().register(\"command1\", \"command description\",\n                    \"implementation\");\n            assertNotNull(command);\n            assertEquals(\"command1\", command.getName());\n            assertEquals(\"command description\", command.getDescription());\n        } finally {\n            getCommandAPI().unregister(\"command1\");\n            getCommandAPI().removeDependency(\"commands\");\n        }\n    }\n\n    @Test\n    public void deleteCommand() throws BonitaException {\n        // delete command by name\n        getCommandAPI().addDependency(\"commands\", \"jar\".getBytes());\n        CommandDescriptor command = getCommandAPI().register(\"command1\", \"command description\", \"implementation\");\n        assertNotNull(command);\n        getCommandAPI().unregister(\"command1\");\n        try {\n            getCommandAPI().getCommand(\"command1\");\n            fail(\"Command does not exist anymore\");\n        } catch (final CommandNotFoundException e) {\n            getCommandAPI().removeDependency(\"commands\");\n        }\n        // delete command by id\n        getCommandAPI().addDependency(\"commands\", \"jar\".getBytes());\n        command = getCommandAPI().register(\"command1\", \"command description\", \"implementation\");\n        assertNotNull(command);\n        getCommandAPI().unregister(command.getId());\n        try {\n            getCommandAPI().getCommand(\"command1\");\n            fail(\"Command does not exist anymore\");\n        } catch (final CommandNotFoundException e) {\n            getCommandAPI().removeDependency(\"commands\");\n        }\n    }\n\n    @Test\n    public void getCommandByName() throws BonitaException {\n        final String commandName = \"command1\";\n        try {\n            getCommandAPI().addDependency(\"commands\", \"jar\".getBytes());\n            getCommandAPI().register(commandName, \"command description\", \"implementation\");\n            final CommandDescriptor command = getCommandAPI().getCommand(commandName);\n            assertEquals(\"command1\", command.getName());\n        } finally {\n            getCommandAPI().unregister(commandName);\n            getCommandAPI().removeDependency(\"commands\");\n        }\n    }\n\n    @Test\n    public void updateCommand() throws BonitaException {\n        getCommandAPI().addDependency(\"commands\", \"jar\".getBytes());\n        final CommandDescriptor oldCommand = getCommandAPI().register(\"command\", \"old description\", \"implementation\");\n        final long commandId = oldCommand.getId();\n        assertEquals(\"command\", oldCommand.getName());\n        assertEquals(\"old description\", oldCommand.getDescription());\n        // test update name specified command\n        final CommandUpdater commandUpdateDescriptor = new CommandUpdater();\n        commandUpdateDescriptor.setDescription(\"new description\");\n        getCommandAPI().update(\"command\", commandUpdateDescriptor);\n        CommandDescriptor newCommand = getCommandAPI().getCommand(\"command\");\n        assertEquals(commandId, newCommand.getId());\n        assertEquals(\"command\", newCommand.getName());\n        assertEquals(\"new description\", newCommand.getDescription());\n\n        // test update id specified command\n        commandUpdateDescriptor.setName(\"updatedCommandName for the id specified command\");\n        commandUpdateDescriptor.setDescription(\"updatedDescription for the id specified command\");\n        getCommandAPI().update(commandId, commandUpdateDescriptor);\n        newCommand = getCommandAPI().get(commandId);\n        assertEquals(\"updatedCommandName for the id specified command\", newCommand.getName());\n        assertEquals(\"updatedDescription for the id specified command\", newCommand.getDescription());\n\n        getCommandAPI().unregister(commandId);\n        getCommandAPI().removeDependency(\"commands\");\n    }\n\n    @Test\n    public void getCommandsWithCommandCriterion() throws BonitaException {\n        try {\n            getCommandAPI().addDependency(\"commands\", \"jar\".getBytes());\n            getCommandAPI().register(\"aaaCommand2\", \"command description\", \"implementation\");\n            getCommandAPI().register(\"aaaCommand3\", \"command description\", \"implementation\");\n            getCommandAPI().register(\"aaaCommand1\", \"command description\", \"implementation\");\n\n            final List<CommandDescriptor> commandsPage1 = getCommandAPI().getAllCommands(0, 2,\n                    CommandCriterion.NAME_ASC);\n            assertEquals(2, commandsPage1.size());\n            assertEquals(\"aaaCommand1\", commandsPage1.get(0).getName());\n            assertEquals(\"aaaCommand2\", commandsPage1.get(1).getName());\n\n            final List<CommandDescriptor> commandsPage2 = getCommandAPI().getAllCommands(2, 1,\n                    CommandCriterion.NAME_ASC);\n            assertEquals(1, commandsPage2.size());\n            assertEquals(\"aaaCommand3\", commandsPage2.get(0).getName());\n        } finally {\n            getCommandAPI().unregister(\"aaaCommand2\");\n            getCommandAPI().unregister(\"aaaCommand3\");\n            getCommandAPI().unregister(\"aaaCommand1\");\n            getCommandAPI().removeDependency(\"commands\");\n        }\n    }\n\n    @Test\n    public void testGetCommands() throws BonitaException {\n        // Create and register Commands as System is false\n        getCommandAPI().addDependency(\"commands\", \"jar\".getBytes());\n        getCommandAPI().register(\"command1\", \"GetCommands description\", \"implementation\");\n        getCommandAPI().register(\"command2\", \"GetCommands description\", \"implementation\");\n        getCommandAPI().register(\"command3\", \"GetCommands description\", \"implementation\");\n\n        // Search and test the result\n        final List<CommandDescriptor> commands = getCommandAPI().getUserCommands(0, 3, CommandCriterion.NAME_ASC);\n        assertEquals(3, commands.size());\n        assertEquals(false, commands.get(0).isSystemCommand());\n        assertEquals(false, commands.get(1).isSystemCommand());\n        assertEquals(false, commands.get(2).isSystemCommand());\n\n        // Clean Commands\n        getCommandAPI().unregister(\"command1\");\n        getCommandAPI().unregister(\"command2\");\n        getCommandAPI().unregister(\"command3\");\n        getCommandAPI().removeDependency(\"commands\");\n    }\n\n    @Test\n    public void getCommandById() throws BonitaException {\n        final String commandName = \"command1\";\n        try {\n            getCommandAPI().addDependency(\"commands\", \"jar\".getBytes());\n            final CommandDescriptor registeredCommand = getCommandAPI().register(commandName, \"command description\",\n                    \"implementation\");\n            final CommandDescriptor command = getCommandAPI().get(registeredCommand.getId());\n            assertEquals(\"command1\", command.getName());\n        } finally {\n            getCommandAPI().unregister(commandName);\n            getCommandAPI().removeDependency(\"commands\");\n        }\n    }\n\n    @Test\n    public void searchCommands() throws BonitaException {\n        final CommandAPI commandAPI = getCommandAPI();\n        commandAPI.addDependency(\"commands\", \"jar\".getBytes());\n        final CommandDescriptor command1 = commandAPI.register(\"testCommand1\", \"SearchCommands description1\",\n                \"implementation\");\n        final CommandDescriptor command2 = commandAPI.register(\"testCommand2\", \"GetCommands description2\",\n                \"implementation\");\n        final CommandDescriptor command3 = commandAPI.register(\"testCommand3\", \"GetCommands description3\",\n                \"implementation\");\n        try {\n            // search paging with order ASC\n            SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 2);\n            builder.searchTerm(\"testCommand\");\n            builder.sort(CommandSearchDescriptor.NAME, Order.ASC);\n            SearchResult<CommandDescriptor> searchCommands = commandAPI.searchCommands(builder.done());\n            assertNotNull(searchCommands);\n            assertEquals(3, searchCommands.getCount());\n            List<CommandDescriptor> commands = searchCommands.getResult();\n            assertEquals(2, commands.size());\n            assertEquals(command1, commands.get(0));\n            assertEquals(command2, commands.get(1));\n\n            builder = new SearchOptionsBuilder(2, 2);\n            builder.searchTerm(\"testCommand\");\n            builder.sort(CommandSearchDescriptor.NAME, Order.ASC);\n            searchCommands = commandAPI.searchCommands(builder.done());\n            assertNotNull(searchCommands);\n            assertEquals(3, searchCommands.getCount());\n            commands = searchCommands.getResult();\n            assertEquals(1, commands.size());\n            assertEquals(command3, commands.get(0));\n\n            builder = new SearchOptionsBuilder(3, 2);\n            builder.searchTerm(\"testCommand\");\n            builder.sort(CommandSearchDescriptor.NAME, Order.ASC);\n            searchCommands = commandAPI.searchCommands(builder.done());\n            assertEquals(0, searchCommands.getResult().size());\n\n            // test Desc\n            builder = new SearchOptionsBuilder(0, 2);\n            builder.searchTerm(\"testCommand\");\n            builder.sort(CommandSearchDescriptor.NAME, Order.DESC);\n            searchCommands = commandAPI.searchCommands(builder.done());\n            assertNotNull(searchCommands);\n            assertEquals(3, searchCommands.getCount());\n            commands = searchCommands.getResult();\n            assertEquals(2, commands.size());\n            assertEquals(command3, commands.get(0));\n            assertEquals(command2, commands.get(1));\n\n            // test search with filter\n            builder = new SearchOptionsBuilder(0, 10);\n            builder.filter(CommandSearchDescriptor.NAME, \"testCommand1\");\n            searchCommands = commandAPI.searchCommands(builder.done());\n            assertNotNull(searchCommands);\n            assertEquals(1, searchCommands.getCount());\n            commands = searchCommands.getResult();\n            assertEquals(1, commands.size());\n            assertEquals(command1, commands.get(0));\n\n            // test search with term\n            builder = new SearchOptionsBuilder(0, 10);\n            builder.searchTerm(\"testCommand\");\n            builder.sort(CommandSearchDescriptor.NAME, Order.ASC);\n            searchCommands = commandAPI.searchCommands(builder.done());\n            assertNotNull(searchCommands);\n            assertEquals(3, searchCommands.getCount());\n            commands = searchCommands.getResult();\n            assertEquals(3, commands.size());\n            assertEquals(command1, commands.get(0));\n            assertEquals(command2, commands.get(1));\n            assertEquals(command3, commands.get(2));\n\n        } finally {\n            // Clean Commands\n            commandAPI.unregister(\"testCommand1\");\n            commandAPI.unregister(\"testCommand2\");\n            commandAPI.unregister(\"testCommand3\");\n            commandAPI.removeDependency(\"commands\");\n        }\n    }\n\n    @Test\n    public void searchCommandsWithApostrophe() throws BonitaException {\n        getCommandAPI().addDependency(\"commands\", \"jar\".getBytes());\n        final CommandDescriptor command1 = getCommandAPI().register(\"'command'1\", \"SearchCommands description1\",\n                \"implementation\");\n        final CommandDescriptor command2 = getCommandAPI().register(\"command2\", \"'command'1\", \"implementation\");\n        final CommandDescriptor command3 = getCommandAPI().register(\"command3\", \"'SearchCommands description1\",\n                \"command'tation\");\n\n        // test search with filter\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.sort(CommandSearchDescriptor.NAME, Order.ASC);\n        builder.searchTerm(\"'\");\n        final CommandAPI commandAPI = getCommandAPI();\n        final SearchResult<CommandDescriptor> searchCommands = commandAPI.searchCommands(builder.done());\n        assertNotNull(searchCommands);\n        assertEquals(3, searchCommands.getCount());\n        final List<CommandDescriptor> commands = searchCommands.getResult();\n        assertEquals(3, commands.size());\n        assertEquals(command1, commands.get(0));\n        assertEquals(command2, commands.get(1));\n        assertEquals(command3, commands.get(2));\n\n        // Clean Commands\n        getCommandAPI().unregister(\"'command'1\");\n        getCommandAPI().unregister(\"command2\");\n        getCommandAPI().unregister(\"command3\");\n        getCommandAPI().removeDependency(\"commands\");\n    }\n\n    @Test\n    public void executeCommandById() throws BonitaException, IOException {\n        final byte[] byteArray = generateJar(IntegerCommand.class);\n        getCommandAPI().addDependency(\"commands\", byteArray);\n        final CommandDescriptor command = getCommandAPI()\n                .register(\"intReturn\", \"Retrieving the integer value\", \"org.bonitasoft.engine.command.IntegerCommand\");\n\n        final CommandDescriptor commandById = getCommandAPI().get(command.getId());\n        assertEquals(commandById.getId(), command.getId());\n\n        final Map<String, Serializable> parameters = new HashMap<>();\n        parameters.put(\"int\", 83);\n        final Integer actual = (Integer) getCommandAPI().execute(commandById.getId(), parameters);\n        assertEquals(Integer.valueOf(83), actual);\n\n        getCommandAPI().unregister(\"intReturn\");\n        getCommandAPI().removeDependency(\"commands\");\n    }\n\n    @Test(expected = BonitaRuntimeException.class)\n    public void executeCommandThrowsANPE() throws BonitaException, IOException {\n        final byte[] byteArray = generateJar(NPECommand.class);\n        getCommandAPI().addDependency(\"commands\", byteArray);\n        getCommandAPI().register(\"NPEReturns\", \"Throws a NPE\", \"org.bonitasoft.engine.command.NPECommand\");\n        try {\n            getCommandAPI().execute(\"NPEReturns\", null);\n        } finally {\n            getCommandAPI().unregister(\"NPEReturns\");\n            getCommandAPI().removeDependency(\"commands\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/command/ExecuteBDMQueryCommandIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.bdm.builder.BusinessObjectBuilder.aBO;\nimport static org.bonitasoft.engine.bdm.builder.BusinessObjectModelBuilder.aBOM;\nimport static org.bonitasoft.engine.bdm.builder.FieldBuilder.aRelationField;\nimport static org.bonitasoft.engine.bdm.builder.FieldBuilder.aSimpleField;\nimport static org.bonitasoft.engine.bdm.builder.QueryBuilder.aQuery;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.Serializable;\nimport java.time.LocalDate;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.commons.io.FileUtils;\nimport org.bonitasoft.engine.CommonAPIIT;\nimport org.bonitasoft.engine.bdm.BusinessObjectModelConverter;\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.model.field.FieldType;\nimport org.bonitasoft.engine.bdm.model.field.RelationField.Type;\nimport org.bonitasoft.engine.bdm.serialization.BusinessDataObjectMapper;\nimport org.bonitasoft.engine.bpm.businessdata.BusinessDataQueryResult;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.business.data.ClassloaderRefresher;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.io.IOUtils;\nimport org.bonitasoft.engine.operation.LeftOperandBuilder;\nimport org.bonitasoft.engine.operation.OperatorType;\nimport org.junit.After;\nimport org.junit.AfterClass;\nimport org.junit.Before;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\n\n/**\n * @author Romain Bioteau\n */\npublic class ExecuteBDMQueryCommandIT extends CommonAPIIT {\n\n    private static final String EXECUTE_BDM_QUERY_COMMAND = \"executeBDMQuery\";\n\n    public static final String GET_BUSINESS_DATA_BY_QUERY_COMMAND = \"getBusinessDataByQueryCommand\";\n\n    private static final String EMPLOYEE_QUALIF_CLASSNAME = \"org.bonita.pojo.BonitaEmployee\";\n\n    private static final String ADDRESS_QUALIF_CLASSNAME = \"org.bonita.pojo.BonitaAddress\";\n\n    private static final String RETURNS_LIST = \"returnsList\";\n\n    private static final String QUERY_PARAMETERS = \"queryParameters\";\n\n    public static final String ENTITY_CLASS_NAME = \"entityClassName\";\n\n    private static final String RETURN_TYPE = \"returnType\";\n\n    private static final String START_INDEX = \"startIndex\";\n\n    private static final String MAX_RESULTS = \"maxResults\";\n\n    private static final String QUERY_NAME = \"queryName\";\n\n    protected User businessUser;\n\n    private ClassLoader contextClassLoader;\n\n    private static File clientFolder;\n    private LocalDate birthdate;\n\n    private BusinessObjectModel buildCustomBOM() {\n        final BusinessObject addressBO = aBO(ADDRESS_QUALIF_CLASSNAME)\n                .withField(aSimpleField().withName(\"street\").ofType(FieldType.STRING).build()).build();\n        final BusinessObject employee = aBO(EMPLOYEE_QUALIF_CLASSNAME)\n                .withDescription(\"Describe final a simple employee\")\n                .withField(aSimpleField().withName(\"firstName\").ofType(FieldType.STRING).withLength(10).build())\n                .withField(aSimpleField().withName(\"lastName\").ofType(FieldType.STRING).notNullable().build())\n                .withField(aSimpleField().withName(\"birthdate\").ofType(FieldType.LOCALDATE).nullable().build())\n                .withField(aRelationField().withName(\"addresses\").ofType(Type.COMPOSITION).referencing(addressBO)\n                        .multiple().lazy().build())\n                .withQuery(\n                        aQuery().withName(\"getNoEmployees\")\n                                .withContent(\"SELECT e FROM BonitaEmployee e WHERE e.firstName = 'INEXISTANT'\")\n                                .withReturnType(List.class.getName()).build())\n                .withQuery(\n                        aQuery().withName(\"getEmployeeByFirstNameAndLastName\")\n                                .withContent(\n                                        \"SELECT e FROM BonitaEmployee e WHERE e.firstName=:firstName AND e.lastName=:lastName\")\n                                .withReturnType(EMPLOYEE_QUALIF_CLASSNAME)\n                                .withQueryParameter(\"firstName\", String.class.getName())\n                                .withQueryParameter(\"lastName\", String.class.getName()).build())\n                .withQuery(\n                        aQuery().withName(\"customQuery\")\n                                .withContent(\"SELECT e FROM BonitaEmployee e\")\n                                .withReturnType(List.class.getName())\n                                .build())\n                .withQuery(\n                        aQuery().withName(\"countForCustomQuery\")\n                                .withContent(\"SELECT COUNT(e) FROM BonitaEmployee e\")\n                                .withReturnType(Long.class.getName())\n                                .build())\n                .build();\n        return aBOM().withBOs(addressBO, employee).build();\n    }\n\n    @BeforeClass\n    public static void initTestClass() throws IOException {\n        clientFolder = IOUtils.createTempDirectory(\"ExecuteBDMQueryCommandIT_client\");\n        clientFolder.mkdirs();\n    }\n\n    @AfterClass\n    public static void cleanTestClass() {\n        try {\n            FileUtils.deleteDirectory(clientFolder);\n        } catch (final Exception e) {\n            clientFolder.deleteOnExit();\n        }\n    }\n\n    @Before\n    public void beforeTest() throws Exception {\n        loginWithTechnicalUser();\n        businessUser = createUser(USERNAME, PASSWORD);\n        logout();\n        loginWithTechnicalUser();\n\n        final BusinessObjectModelConverter converter = new BusinessObjectModelConverter();\n        final byte[] zip = converter.zip(buildCustomBOM());\n\n        assertThat(getTenantAdministrationAPI().isPaused()).as(\"Tenant is paused?\").isFalse();\n        getTenantAdministrationAPI().pause();\n        getTenantAdministrationAPI().cleanAndUninstallBusinessDataModel();\n        getTenantAdministrationAPI().updateBusinessDataModel(zip);\n        getTenantAdministrationAPI().resume();\n        logout();\n        loginOnDefaultTenantWith(USERNAME, PASSWORD);\n\n        loadClientJars();\n\n        birthdate = LocalDate.of(1982, 3, 17);\n        addEmployee(\"Romain\", \"Bioteau\", birthdate, \"54, Grand Rue\", \"38 , Gabrile Péri\");\n        addEmployee(\"Jules\", \"Bioteau\", null, \"78 , Colonel Bougault\");\n        addEmployee(\"Matthieu\", \"Chaffotte\", null);\n    }\n\n    private void loadClientJars() throws Exception {\n        contextClassLoader = Thread.currentThread().getContextClassLoader();\n        final byte[] clientBDMZip = getTenantAdministrationAPI().getClientBDMZip();\n        final ClassLoader classLoaderWithBDM = new ClassloaderRefresher().loadClientModelInClassloader(clientBDMZip,\n                contextClassLoader,\n                EMPLOYEE_QUALIF_CLASSNAME, clientFolder);\n        Thread.currentThread().setContextClassLoader(classLoaderWithBDM);\n    }\n\n    @After\n    public void cleanClassLoader_and_uninstall_bdm() throws BonitaException {\n        // reset previous classloader:\n        if (contextClassLoader != null) {\n            Thread.currentThread().setContextClassLoader(contextClassLoader);\n        }\n\n        logout();\n        loginWithTechnicalUser();\n        if (!getTenantAdministrationAPI().isPaused()) {\n            getTenantAdministrationAPI().pause();\n            getTenantAdministrationAPI().cleanAndUninstallBusinessDataModel();\n            getTenantAdministrationAPI().resume();\n        }\n        deleteUser(businessUser);\n        logout();\n    }\n\n    @Test\n    public void should_execute_returns_empty_list() throws Exception {\n        final Map<String, Serializable> parameters = new HashMap<>();\n        parameters.put(QUERY_NAME, \"BonitaEmployee.getNoEmployees\");\n        parameters.put(RETURNS_LIST, true);\n        parameters.put(RETURN_TYPE, EMPLOYEE_QUALIF_CLASSNAME);\n        parameters.put(START_INDEX, 0);\n        parameters.put(MAX_RESULTS, 10);\n        final byte[] result = (byte[]) getCommandAPI().execute(EXECUTE_BDM_QUERY_COMMAND, parameters);\n\n        assertThat(deserializeListResult(result)).isNotNull().isInstanceOf(List.class).isEmpty();\n    }\n\n    @Test\n    public void should_execute_returns_employee_list() throws Exception {\n        final Map<String, Serializable> parameters = new HashMap<>();\n        parameters.put(QUERY_NAME, \"BonitaEmployee.find\");\n        parameters.put(RETURNS_LIST, true);\n        parameters.put(RETURN_TYPE, EMPLOYEE_QUALIF_CLASSNAME);\n        parameters.put(START_INDEX, 0);\n        parameters.put(MAX_RESULTS, 10);\n        final byte[] result = (byte[]) getCommandAPI().execute(EXECUTE_BDM_QUERY_COMMAND, parameters);\n\n        assertThat(deserializeListResult(result)).isNotNull().isInstanceOf(List.class).hasSize(3);\n    }\n\n    @Test\n    public void getListFromQueryShouldLimitToMaxResults() throws Exception {\n        final Map<String, Serializable> parameters = new HashMap<>();\n        parameters.put(QUERY_NAME, \"BonitaEmployee.find\");\n        parameters.put(RETURNS_LIST, true);\n        parameters.put(RETURN_TYPE, EMPLOYEE_QUALIF_CLASSNAME);\n        parameters.put(START_INDEX, 0);\n        parameters.put(MAX_RESULTS, 2);\n        final byte[] result = (byte[]) getCommandAPI().execute(EXECUTE_BDM_QUERY_COMMAND, parameters);\n\n        assertThat(deserializeListResult(result)).isNotNull().isInstanceOf(List.class).hasSize(2);\n    }\n\n    @Test\n    public void should_have_a_count_query() throws Exception {\n        final Map<String, Serializable> parameters = new HashMap<>();\n        parameters.put(QUERY_NAME, \"BonitaEmployee.countForFind\");\n        parameters.put(RETURNS_LIST, false);\n        parameters.put(RETURN_TYPE, Long.class.getName());\n        parameters.put(START_INDEX, 0);\n        parameters.put(MAX_RESULTS, 1);\n        final byte[] result = (byte[]) getCommandAPI().execute(EXECUTE_BDM_QUERY_COMMAND, parameters);\n\n        final BusinessDataObjectMapper mapper = new BusinessDataObjectMapper();\n        final Long count = mapper.readValue(result, Long.class);\n        assertThat(count).isEqualTo(3L);\n    }\n\n    @Test\n    public void should_have_query_metadata_on_auto_generated_queries() throws Exception {\n        //given\n        final BusinessDataQueryResult businessDataQueryResult = executeQuery(\"find\", 2, 1);\n\n        // Standard shape returns single object for single result from List query\n        assertThatJson(businessDataQueryResult.getJsonResults()).as(\"should get json results\")\n                .isEqualTo(getJsonContent(\"Employee.find.2.1.json\"));\n\n        assertThat(businessDataQueryResult.getBusinessDataQueryMetadata().getCount()).isEqualTo(3L);\n        assertThat(businessDataQueryResult.getBusinessDataQueryMetadata().getStartIndex()).isEqualTo(2);\n        assertThat(businessDataQueryResult.getBusinessDataQueryMetadata().getMaxResults()).isEqualTo(1);\n    }\n\n    @Test\n    public void should_return_count_result_when_executing_count_query() throws Exception {\n        //given\n        final BusinessDataQueryResult businessDataQueryResult = executeQuery(\"countForCustomQuery\", 0, 1);\n\n        // Standard shape returns object with value property\n        assertThatJson(businessDataQueryResult.getJsonResults()).as(\"should get json results\")\n                .isEqualTo(\"{\\\"value\\\":3}\");\n    }\n\n    @Test\n    public void should_have_query_metadata_on_custom_queries_with_related_count() throws Exception {\n        //given\n        int startIndex = 2;\n        int maxResults = 1;\n        final BusinessDataQueryResult businessDataQueryResult = executeQuery(\"customQuery\", startIndex, maxResults);\n\n        // Standard shape returns array for List-returning query\n        assertThatJson(businessDataQueryResult.getJsonResults()).as(\"should get json results\")\n                .isEqualTo(getJsonContent(\"Employee.find.2.1.json\"));\n\n        assertThat(businessDataQueryResult.getBusinessDataQueryMetadata()).isNotNull();\n        assertThat(businessDataQueryResult.getBusinessDataQueryMetadata().getCount()).isEqualTo(3L);\n        assertThat(businessDataQueryResult.getBusinessDataQueryMetadata().getStartIndex()).isEqualTo(startIndex);\n        assertThat(businessDataQueryResult.getBusinessDataQueryMetadata().getMaxResults()).isEqualTo(maxResults);\n    }\n\n    private BusinessDataQueryResult executeQuery(String queryName, int startIndex, int maxResults)\n            throws CommandNotFoundException, CommandParameterizationException, CommandExecutionException {\n        final Map<String, Serializable> parameters = new HashMap<>();\n        HashMap<String, Serializable> queryParameters = new HashMap<>();\n\n        parameters.put(QUERY_NAME, queryName);\n        parameters.put(ENTITY_CLASS_NAME, EMPLOYEE_QUALIF_CLASSNAME);\n        parameters.put(QUERY_PARAMETERS, queryParameters);\n        parameters.put(START_INDEX, startIndex);\n        parameters.put(MAX_RESULTS, maxResults);\n        parameters.put(\"businessDataURIPattern\", \"/businessdata/{className}/{id}/{field}\");\n\n        //when\n        return (BusinessDataQueryResult) getCommandAPI().execute(GET_BUSINESS_DATA_BY_QUERY_COMMAND,\n                parameters);\n    }\n\n    @Test\n    public void should_have_results_but_no_query_metadata_when_count_is_not_available()\n            throws Exception {\n        //given\n        final BusinessDataQueryResult businessDataQueryResult = executeQuery(\"getNoEmployees\", 2, 1);\n\n        //then\n        // Returns empty array for List queries with no results\n        assertThatJson(businessDataQueryResult.getJsonResults()).as(\"should get json results\").isEqualTo(\"[]\");\n        assertThat(businessDataQueryResult.getBusinessDataQueryMetadata()).isNull();\n    }\n\n    @Test\n    public void should_execute_returns_a_single_employee() throws Exception {\n        final Map<String, Serializable> parameters = new HashMap<>();\n        parameters.put(QUERY_NAME, \"BonitaEmployee.getEmployeeByFirstNameAndLastName\");\n        parameters.put(RETURN_TYPE, EMPLOYEE_QUALIF_CLASSNAME);\n        final Map<String, Serializable> queryParameters = new HashMap<>();\n        queryParameters.put(\"firstName\", \"Romain\");\n        queryParameters.put(\"lastName\", \"Bioteau\");\n        parameters.put(QUERY_PARAMETERS, (Serializable) queryParameters);\n\n        final byte[] result = (byte[]) getCommandAPI().execute(EXECUTE_BDM_QUERY_COMMAND, parameters);\n        final Serializable employee = deserializeSimpleResult(result);\n        assertThat(employee).isNotNull().isInstanceOf(Entity.class);\n        assertThat(employee.getClass().getName()).isEqualTo(EMPLOYEE_QUALIF_CLASSNAME);\n        assertThat(employee.getClass().getMethod(\"getFirstName\", new Class[0]).invoke(employee)).isEqualTo(\"Romain\");\n        assertThat(employee.getClass().getMethod(\"getLastName\", new Class[0]).invoke(employee)).isEqualTo(\"Bioteau\");\n        assertThat(employee.getClass().getMethod(\"getBirthdate\", new Class[0]).invoke(employee)).isEqualTo(birthdate);\n        final Object invoke = employee.getClass().getMethod(\"getAddresses\", new Class[0]).invoke(employee);\n        assertThat(invoke).isInstanceOf(List.class);\n        assertThat((List<?>) invoke).isEmpty();\n    }\n\n    @Test(expected = CommandExecutionException.class)\n    public void should_execute_throw_a_CommandExecutionException_if_result_is_not_single() throws Exception {\n        final Map<String, Serializable> parameters = new HashMap<>();\n        parameters.put(QUERY_NAME, \"BonitaEmployee.findByLastName\");\n        parameters.put(RETURN_TYPE, EMPLOYEE_QUALIF_CLASSNAME);\n        final Map<String, Serializable> queryParameters = new HashMap<>();\n        queryParameters.put(\"lastName\", \"Bioteau\");\n        parameters.put(QUERY_PARAMETERS, (Serializable) queryParameters);\n        getCommandAPI().execute(EXECUTE_BDM_QUERY_COMMAND, parameters);\n    }\n\n    @Test(expected = BonitaRuntimeException.class)\n    public void should_execute_throw_BonitaRuntimeException_if_query_not_exists() throws Exception {\n        final Map<String, Serializable> parameters = new HashMap<>();\n        parameters.put(QUERY_NAME, \"unknownQuery\");\n        parameters.put(RETURN_TYPE, EMPLOYEE_QUALIF_CLASSNAME);\n        getCommandAPI().execute(EXECUTE_BDM_QUERY_COMMAND, parameters);\n    }\n\n    public void addEmployee(final String firstName, final String lastName, final LocalDate birthdate,\n            final String... addresses) throws Exception {\n        final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression(\"createNewEmployee\",\n                createNewEmployeeScriptContent(firstName, lastName, birthdate, addresses),\n                EMPLOYEE_QUALIF_CLASSNAME);\n\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"test\", \"1.2-alpha\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addBusinessData(\"myEmployee\", EMPLOYEE_QUALIF_CLASSNAME, null);\n        processDefinitionBuilder.addUserTask(\"step1\", ACTOR_NAME).addOperation(\n                new LeftOperandBuilder().createBusinessDataLeftOperand(\"myEmployee\"),\n                OperatorType.ASSIGNMENT, null, null, employeeExpression);\n\n        final DesignProcessDefinition designProcessDefinition = processDefinitionBuilder.done();\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                businessUser);\n        final ProcessInstance instance = getProcessAPI().startProcess(definition.getId());\n        waitForUserTaskAndExecuteIt(instance, \"step1\", businessUser);\n        checkProcessInstanceIsArchived(instance);\n\n        disableAndDeleteProcess(definition.getId());\n    }\n\n    private String createNewEmployeeScriptContent(final String firstName, final String lastName, LocalDate birthdate,\n            final String... addresses) {\n        final StringBuilder sb = new StringBuilder();\n        sb.append(\"import \");\n        sb.append(EMPLOYEE_QUALIF_CLASSNAME);\n        sb.append(\"\\n\");\n        sb.append(\"import \");\n        sb.append(ADDRESS_QUALIF_CLASSNAME);\n        sb.append(\"\\n\");\n        sb.append(\"BonitaEmployee e = new BonitaEmployee();\");\n        sb.append(\"\\n\");\n        sb.append(\"e.firstName =\");\n        sb.append(\"'\" + firstName + \"'\");\n        sb.append(\"\\n\");\n        sb.append(\"e.lastName =\");\n        sb.append(\"'\" + lastName + \"'\");\n        sb.append(\"\\n\");\n        sb.append(\"e.birthdate = \");\n        if (birthdate != null) {\n            sb.append(\"java.time.LocalDate.parse(\\\"\" + birthdate.toString() + \"\\\")\");\n        } else {\n            sb.append(\"null\");\n        }\n        sb.append(\"\\n\");\n        if (addresses != null) {\n            for (int i = 0; i < addresses.length; i++) {\n                final String addressVar = \"a\" + String.valueOf(i);\n                sb.append(\"BonitaAddress \" + addressVar + \" = new BonitaAddress();\");\n                sb.append(\"\\n\");\n                sb.append(addressVar + \".street =\");\n                sb.append(\"'\" + addresses[i] + \"'\");\n                sb.append(\"\\n\");\n                sb.append(\"e.addToAddresses(\" + addressVar + \")\");\n                sb.append(\"\\n\");\n            }\n        }\n\n        sb.append(\"return e;\");\n        return sb.toString();\n    }\n\n    private Serializable deserializeSimpleResult(final byte[] result) throws Exception {\n        return new BusinessDataObjectMapper().readValue(result,\n                (Class<Serializable>) Thread.currentThread().getContextClassLoader()\n                        .loadClass(EMPLOYEE_QUALIF_CLASSNAME));\n    }\n\n    private List<?> deserializeListResult(final byte[] result) throws Exception {\n        return new BusinessDataObjectMapper().readListValue(result,\n                (Class<Serializable>) Thread.currentThread().getContextClassLoader()\n                        .loadClass(EMPLOYEE_QUALIF_CLASSNAME));\n    }\n\n    private String getJsonContent(final String jsonFileName) throws IOException {\n        return new String(org.apache.commons.io.IOUtils.toByteArray(this.getClass().getResourceAsStream(jsonFileName)));\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/command/ExecutionExceptionCommand.java",
    "content": "/**\n * Copyright (C) 2011 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.service.ServiceAccessor;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class ExecutionExceptionCommand extends RuntimeCommand {\n\n    @Override\n    public Serializable execute(final Map<String, Serializable> parameters, final ServiceAccessor serviceAccessor)\n            throws SCommandParameterizationException, SCommandExecutionException {\n        throw new SCommandExecutionException(\"fail\");\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/command/IntegerCommand.java",
    "content": "/**\n * Copyright (C) 2012 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.service.ServiceAccessor;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class IntegerCommand extends RuntimeCommand {\n\n    @Override\n    public Serializable execute(final Map<String, Serializable> parameters, final ServiceAccessor serviceAccessor)\n            throws SCommandParameterizationException, SCommandExecutionException {\n        return parameters.get(\"int\");\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/command/MultipleStartPointsProcessCommandIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport static org.bonitasoft.engine.command.helper.designer.Transition.fails;\nimport static org.bonitasoft.engine.command.helper.designer.Transition.meet;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.assertj.core.api.Assertions;\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.bpm.contract.Type;\nimport org.bonitasoft.engine.bpm.document.DocumentValue;\nimport org.bonitasoft.engine.bpm.flownode.GatewayType;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.command.helper.ProcessDeployer;\nimport org.bonitasoft.engine.command.helper.designer.BoundaryEvent;\nimport org.bonitasoft.engine.command.helper.designer.Branch;\nimport org.bonitasoft.engine.command.helper.designer.Gateway;\nimport org.bonitasoft.engine.command.helper.designer.Signal;\nimport org.bonitasoft.engine.command.helper.designer.SimpleProcessDesigner;\nimport org.bonitasoft.engine.command.helper.designer.UserTask;\nimport org.bonitasoft.engine.command.helper.expectation.TestUtils;\nimport org.bonitasoft.engine.connectors.TestConnectorWithOutput;\nimport org.bonitasoft.engine.connectors.VariableStorage;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.expression.InvalidExpressionException;\nimport org.bonitasoft.engine.operation.LeftOperandBuilder;\nimport org.bonitasoft.engine.operation.Operation;\nimport org.bonitasoft.engine.operation.OperationBuilder;\nimport org.bonitasoft.engine.operation.OperatorType;\nimport org.junit.After;\nimport org.junit.Test;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class MultipleStartPointsProcessCommandIT extends TestWithUser {\n\n    private static final String CONNECTOR_OUTPUT_NAME = \"output1\";\n\n    private static final String CONNECTOR_INPUT_NAME = \"input1\";\n\n    private static final String CONNECTOR_WITH_OUTPUT_ID = \"org.bonitasoft.connector.testConnectorWithOutput\";\n\n    private final SimpleProcessDesigner designer = new SimpleProcessDesigner(getProcessDefinitionBuilder());\n\n    private final TestUtils wrapper = new TestUtils(this);\n\n    private final ProcessDeployer processDeployer = getProcessDeployer();\n\n    @After\n    public void afterTest() throws BonitaException {\n        processDeployer.clean();\n        VariableStorage.clearAll();\n    }\n\n    @Test\n    public void should_start_a_sequential_process() throws Exception {\n        final ProcessDefinition processDefinition = processDeployer.deploy(designer\n                .start()\n                .then(new UserTask(\"step 1\"))\n                .then(new UserTask(\"step 2\"))\n                .then(new UserTask(\"step 3\"))\n                .end());\n\n        final TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(),\n                Collections.singletonList(\"step 2\"));\n\n        process.expect(\"step 2\").toBeReady();\n        process.expect(\"start\", \"step 1\").toNotHaveArchives();\n    }\n\n    @Test\n    public void should_start_a_sequential_process_with_variables() throws Exception {\n        final ProcessDefinitionBuilder builder = getProcessDefinitionBuilder();\n        builder.addShortTextData(\"variable\", new ExpressionBuilder().createConstantStringExpression(\"default\"));\n        final SimpleProcessDesigner designer = new SimpleProcessDesigner(builder);\n        final ProcessDefinition processDefinition = processDeployer.deploy(designer\n                .start()\n                .then(new UserTask(\"step 1\"))\n                .then(new UserTask(\"step 2\"))\n                .then(new UserTask(\"step 3\"))\n                .end());\n\n        final TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(),\n                Collections.singletonList(\"step 2\"),\n                Collections.singletonList(createSetDataOperation(\"variable\", \"Done!\")),\n                Collections.<String, Serializable> emptyMap());\n\n        process.expectVariable(\"variable\").toBe(\"Done!\");\n    }\n\n    @Test\n    public void should_be_able_to_start_a_sequential_process_with_a_document() throws Exception {\n        final ProcessDefinitionBuilder builder = getProcessDefinitionBuilder();\n        builder.addDocumentDefinition(\"document\");\n        final ProcessDefinition processDefinition = processDeployer.deploy(designer\n                .start()\n                .then(new UserTask(\"step 1\"))\n                .then(new UserTask(\"step 2\"))\n                .then(new UserTask(\"step 3\"))\n                .end());\n\n        final TestUtils.Process process = startProcess(user.getId(),\n                processDefinition.getId(),\n                Collections.singletonList(\"step 2\"),\n                Collections.singletonList(new OperationBuilder().createSetDocument(\"document\",\n                        new ExpressionBuilder().createInputExpression(\"value\", DocumentValue.class.getName()))),\n                Collections.<String, Serializable> singletonMap(\n                        \"value\", new DocumentValue(\"content\".getBytes(), \"plain/text\", \"document\")));\n\n        process.expectDocument(\"document\").toBe(\"content\");\n    }\n\n    @Test\n    public void should_be_able_to_start_a_process_before_a_parallel_merge() throws Exception {\n        ProcessDefinition processDefinition = processDeployer.deploy(designer\n                .start()\n                .then(new Gateway(\"split\", GatewayType.PARALLEL))\n                .then(new UserTask(\"step 1\"), new UserTask(\"step 2\"))\n                .then(new Gateway(\"merge\", GatewayType.PARALLEL))\n                .then(new UserTask(\"step 3\"))\n                .end());\n\n        TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(),\n                Arrays.asList(\"step 1\", \"step 2\"));\n        process.execute(user, \"step 1\", \"step 2\");\n        process.execute(user, \"step 3\");\n\n        process.isExpected().toFinish();\n        process.expect(\"start\", \"split\").toNotHaveArchives();\n        process.expect(\"merge\").toBeExecuted(1);\n    }\n\n    @Test\n    public void should_be_able_to_start_a_process_before_consecutive_parallel_merges() throws Exception {\n        ProcessDefinition processDefinition = processDeployer.deploy(designer\n                .start()\n                .then(\n                        new UserTask(\"step 1\"),\n                        new Branch().start(new Gateway(\"parallel 1\", GatewayType.PARALLEL))\n                                .then(new UserTask(\"step 2\"), new UserTask(\"step 3\"))\n                                .then(new Gateway(\"parallel 2\", GatewayType.PARALLEL)))\n                .then(new Gateway(\"parallel 3\", GatewayType.PARALLEL))\n                .then(new UserTask(\"step 4\"))\n                .end());\n\n        TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(),\n                Arrays.asList(\"step 1\", \"step 2\", \"step 3\"));\n        process.execute(user, \"step 1\", \"step 2\", \"step 3\", \"step 4\");\n\n        process.isExpected().toFinish();\n        process.expect(\"step 1\", \"step 2\", \"step 3\", \"step 4\").toBeExecuted(1);\n        process.expect(\"start\", \"parallel 1\").toNotHaveArchives();\n    }\n\n    @Test\n    public void should_be_able_to_start_a_process_before_a_parallel_split() throws Exception {\n        final ProcessDefinition processDefinition = processDeployer.deploy(designer\n                .start()\n                .then(new UserTask(\"step 1\"))\n                .then(new Gateway(\"split\", GatewayType.PARALLEL))\n                .then(new UserTask(\"step 2\"), new UserTask(\"step 3\"))\n                .end());\n\n        final TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(),\n                Collections.singletonList(\"step 1\"));\n        process.execute(user, \"step 1\", \"step 2\", \"step 3\");\n\n        process.isExpected().toFinish();\n        process.expect(\"start\").toNotHaveArchives();\n    }\n\n    @Test\n    public void should_be_able_to_start_a_process_before_an_exclusive_merge_with_only_one_active_branch()\n            throws Exception {\n        final ProcessDefinition processDefinition = processDeployer.deploy(designer\n                .start()\n                .then(new UserTask(\"step 1\"), new UserTask(\"step 2\"))\n                .then(new Gateway(\"exclusive\", GatewayType.EXCLUSIVE))\n                .then(new UserTask(\"step 3\"))\n                .end());\n\n        final TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(),\n                Collections.singletonList(\"step 2\"));\n        process.execute(user, \"step 2\", \"step 3\");\n\n        process.isExpected().toFinish();\n        process.expect(\"start\").toNotHaveArchives();\n        process.expect(\"step 3\").toBeExecuted(1);\n    }\n\n    @Test\n    public void should_be_able_to_start_a_process_before_an_exclusive_merge_with_execute_all_incoming_branches()\n            throws Exception {\n        ProcessDefinition processDefinition = processDeployer.deploy(designer\n                .start()\n                .then(new UserTask(\"step 1\"), new UserTask(\"step 2\"))\n                .then(new Gateway(\"exclusive\", GatewayType.EXCLUSIVE))\n                .then(new UserTask(\"step 3\"))\n                .end());\n\n        TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(),\n                Arrays.asList(\"step 1\", \"step 2\"));\n        process.execute(user, \"step 1\", \"step 2\", \"step 3\", \"step 3\");\n\n        process.isExpected().toFinish();\n        process.expect(\"start\").toNotHaveArchives();\n        process.expect(\"step 3\").toBeExecuted(2);\n    }\n\n    @Test\n    public void should_be_able_to_start_a_process_before_an_exclusive_merge_with_conditions() throws Exception {\n        Expression condition = new ExpressionBuilder().createConstantBooleanExpression(true);\n        ProcessDefinition processDefinition = processDeployer.deploy(designer\n                .start()\n                .then(new UserTask(\"step 1\"), new UserTask(\"step 2\"))\n                .then(new Gateway(\"exclusive\", GatewayType.EXCLUSIVE)\n                        .when(\"step 1\", fails().toMeet(condition).goingTo(new UserTask(\"step 3\")))\n                        .when(\"step 2\", meet(condition).otherwise(new UserTask(\"step 4\"))))\n                .then(new UserTask(\"step 5\"))\n                .end());\n\n        TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(),\n                Arrays.asList(\"step 1\", \"step 2\"));\n        process.execute(user, \"step 1\", \"step 2\", \"step 3\", \"step 5\");\n\n        process.isExpected().toFinish();\n        process.expect(\"start\").toNotHaveArchives();\n        process.expect(\"step 3\", \"step 5\").toBeExecuted(1);\n    }\n\n    @Test\n    public void should_be_able_to_start_a_process_before_an_exclusive_split() throws Exception {\n        final Expression condition = new ExpressionBuilder().createConstantBooleanExpression(true);\n        final ProcessDefinition processDefinition = processDeployer.deploy(designer\n                .start()\n                .then(new UserTask(\"step 1\"))\n                .then(new Gateway(\"exclusive\", GatewayType.EXCLUSIVE))\n                .then(\n                        new UserTask(\"step 2\").when(\"exclusive\", fails()),\n                        new UserTask(\"step 3\").when(\"exclusive\", meet(condition)))\n                .end());\n\n        final TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(),\n                Collections.singletonList(\"step 1\"));\n        process.execute(user, \"step 1\", \"step 3\");\n\n        process.isExpected().toFinish();\n        process.expect(\"start\").toNotHaveArchives();\n        process.expect(\"step 3\").toBeExecuted(1);\n    }\n\n    @Test\n    public void should_be_able_to_start_a_process_before_an_inclusive() throws Exception {\n        final ProcessDefinition processDefinition = processDeployer.deploy(designer\n                .start()\n                .then(new UserTask(\"step 1\"))\n                .then(new Gateway(\"inclusive 1\", GatewayType.INCLUSIVE))\n                .then(new UserTask(\"step 2\"), new UserTask(\"step 3\"))\n                .then(new Gateway(\"inclusive 2\", GatewayType.INCLUSIVE))\n                .then(new UserTask(\"step 4\"))\n                .end());\n\n        final TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(),\n                Collections.singletonList(\"step 1\"));\n        process.execute(user, \"step 1\", \"step 2\", \"step 3\", \"step 4\");\n\n        process.isExpected().toFinish();\n        process.expect(\"start\").toNotHaveArchives();\n    }\n\n    @Test\n    public void should_be_able_to_start_before_an_inclusive_merge_with_all_incoming_branches() throws Exception {\n        ProcessDefinition processDefinition = processDeployer.deploy(designer\n                .start()\n                .then(new UserTask(\"step 1\"))\n                .then(new Gateway(\"inclusive 1\", GatewayType.INCLUSIVE))\n                .then(new UserTask(\"step 2\"), new UserTask(\"step 3\"))\n                .then(new Gateway(\"inclusive 2\", GatewayType.INCLUSIVE))\n                .then(new UserTask(\"step 4\"))\n                .end());\n\n        TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(),\n                Arrays.asList(\"step 2\", \"step 3\"));\n        process.execute(user, \"step 2\", \"step 3\", \"step 4\");\n\n        process.isExpected().toFinish();\n        process.expect(\"step 1\").toNotHaveArchives();\n        process.expect(\"step 4\").toBeExecuted(1);\n    }\n\n    @Test\n    public void should_be_able_to_start_before_an_inclusive_with_sub_set_of_incoming_branches() throws Exception {\n        ProcessDefinition processDefinition = processDeployer.deploy(designer\n                .start()\n                .then(new UserTask(\"step 1\"))\n                .then(new Gateway(\"inclusive 1\", GatewayType.INCLUSIVE))\n                .then(new UserTask(\"step 2\"), new UserTask(\"step 3\"))\n                .then(new Gateway(\"inclusive 2\", GatewayType.INCLUSIVE))\n                .then(new UserTask(\"step 4\"))\n                .end());\n\n        TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(),\n                Collections.singletonList(\"step 2\"));\n        process.execute(user, \"step 2\", \"step 4\");\n\n        process.isExpected().toFinish();\n        process.expect(\"step 1\").toNotHaveArchives();\n        process.expect(\"step 4\").toBeExecuted(1);\n    }\n\n    @Test\n    public void should_be_able_to_start_an_inclusive_with_boundary() throws Exception {\n        ProcessDefinition processDefinition = processDeployer.deploy(designer\n                .start()\n                .then(new UserTask(\"step 1\"))\n                .then(new Gateway(\"inclusive 1\", GatewayType.INCLUSIVE))\n                .then(\n                        new UserTask(\"step 2\"),\n                        new UserTask(\"step 3\").with(\n                                new BoundaryEvent(\"boundary\", new UserTask(\"step 4\")).triggeredBy(new Signal(\"foo\"))))\n                .then(new Gateway(\"inclusive 2\", GatewayType.INCLUSIVE))\n                .then(new UserTask(\"step 5\"))\n                .end());\n\n        TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(),\n                Arrays.asList(\"step 2\", \"step 3\"));\n        process.expect(\"step 3\").toBeReady();\n        process.execute(user, \"step 2\");\n        process.sendSignal(\"foo\");\n        process.execute(user, \"step 4\", \"step 5\");\n\n        process.isExpected().toFinish();\n        process.expect(\"step 2\", \"step 4\", \"step 5\").toBeExecuted(1);\n        process.expect(\"step 3\").toBeAborted();\n    }\n\n    private Operation createSetDataOperation(final String name, final String value) throws InvalidExpressionException {\n        return new OperationBuilder().createSetDataOperation(name,\n                new ExpressionBuilder().createConstantStringExpression(value));\n    }\n\n    private ProcessDefinitionBuilder getProcessDefinitionBuilder() {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder();\n        builder.createNewInstance(\"Designed by designer\", \"1.0\")\n                .addActor(\"actor\")\n                .addDescription(\"Coding all-night-long\");\n        return builder;\n    }\n\n    private TestUtils.Process startProcess(final long startedBy, final long processDefinitionId,\n            final List<String> activityNames) throws Exception {\n        return startProcess(startedBy, processDefinitionId, activityNames, null, null);\n    }\n\n    private TestUtils.Process startProcess(final long startedBy, final long processDefinitionId,\n            final List<String> activityNames,\n            final List<Operation> operations,\n            final Map<String, Serializable> context) throws Exception {\n        return startProcess(startedBy, processDefinitionId, activityNames, operations, context, null);\n    }\n\n    private TestUtils.Process startProcess(final long startedBy, final long processDefinitionId,\n            final List<String> activityNames,\n            final List<Operation> operations,\n            final Map<String, Serializable> context, Map<String, Serializable> processContractInputs) throws Exception {\n        final Map<String, Serializable> parameters = new HashMap<>();\n        parameters.put(\"started_by\", startedBy);\n        parameters.put(\"process_definition_id\", processDefinitionId);\n        parameters.put(\"activity_names\", new ArrayList<>(activityNames));\n        if (operations != null) {\n            parameters.put(\"operations\", new ArrayList<>(operations));\n        }\n        if (context != null) {\n            parameters.put(\"context\", new HashMap<>(context));\n        }\n        if (processContractInputs != null) {\n            parameters.put(\"process_contract_inputs\", new HashMap<>(processContractInputs));\n        }\n        return wrapper.wrap((ProcessInstance) getCommandAPI().execute(\"multipleStartPointsProcessCommand\", parameters));\n    }\n\n    private ProcessDeployer getProcessDeployer() {\n        return new ProcessDeployer() {\n\n            @Override\n            public ProcessDefinition deploy(final DesignProcessDefinition design) throws BonitaException {\n                return deployAndEnableProcessWithActor(design, \"actor\", user);\n            }\n\n            @Override\n            public void clean(final ProcessDefinition processDefinition) throws BonitaException {\n                disableAndDeleteProcess(processDefinition);\n            }\n        };\n    }\n\n    @Test\n    public void advancedStartProcessCommandWithConnectorOnEnterOnProcess() throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addShortTextData(\"outputOfConnector\", null);\n        processDefinitionBuilder.addUserTask(\"step1\", ACTOR_NAME).addUserTask(\"step2\", ACTOR_NAME);\n        processDefinitionBuilder.addConnector(\"myConnector\", CONNECTOR_WITH_OUTPUT_ID, \"1.0\", ConnectorEvent.ON_ENTER)\n                .addInput(CONNECTOR_INPUT_NAME, new ExpressionBuilder().createConstantStringExpression(\"a\"))\n                .addOutput(new LeftOperandBuilder().createNewInstance().setName(\"outputOfConnector\").done(),\n                        OperatorType.ASSIGNMENT, \"=\", \"\",\n                        new ExpressionBuilder().createInputExpression(CONNECTOR_OUTPUT_NAME, String.class.getName()));\n        processDefinitionBuilder.addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActorAndConnector(\n                processDefinitionBuilder, ACTOR_NAME, user,\n                \"TestConnectorWithOutput.impl\", TestConnectorWithOutput.class, \"TestConnectorWithOutput.jar\");\n\n        // Start the process with the command on the step2\n        final Map<String, Serializable> parametersCommand = new HashMap<>();\n        parametersCommand.put(\"started_by\", user.getId());\n        parametersCommand.put(\"process_definition_id\", processDefinition.getId());\n        parametersCommand.put(\"activity_names\", new ArrayList<>(Collections.singletonList(\"step2\")));\n        // command API execution\n        getCommandAPI().execute(\"multipleStartPointsProcessCommand\", parametersCommand);\n\n        waitForUserTask(\"step2\");\n\n        // Clean\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void should_start_process_with_process_contract_input() throws Exception {\n        ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"process1\", \"1.0\");\n        builder.addContract().addInput(\"input1\", Type.TEXT, \"text input\");\n        builder.addContract().addInput(\"input2\", Type.INTEGER, \"int input\");\n        builder.addUserTask(\"user1\", \"actor\").addDisplayName(\n                new ExpressionBuilder().createDataExpression(\"calculatedTaskName\", String.class.getName()));\n        builder.addUserTask(\"user2\", \"actor\");\n        builder.addActor(\"actor\", true);\n        builder.addLongTextData(\"calculatedTaskName\", null);\n        ProcessDefinition processDefinition = processDeployer.deploy(builder.done());\n\n        Map<String, Serializable> processContractInputs = new HashMap<>();\n        processContractInputs.put(\"input1\", \"the Input1 value\");\n        processContractInputs.put(\"input2\", 145325);\n        List<Operation> operations = Collections\n                .singletonList(new OperationBuilder().createSetDataOperation(\"calculatedTaskName\",\n                        new ExpressionBuilder().createContractInputExpression(\"input1\", String.class.getName())));\n        TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(),\n                Collections.singletonList(\"user1\"),\n                operations,\n                null,\n                processContractInputs);\n\n        long user1 = waitForUserTask(\"user1\");\n        String taskDisplayName = getProcessAPI().getFlowNodeInstance(user1).getDisplayName();\n        Assertions.assertThat(taskDisplayName).isEqualTo(\"the Input1 value\");\n        getProcessAPI().assignUserTask(user1, user.getId());\n        getProcessAPI().executeUserTask(user1, Collections.<String, Serializable> emptyMap());\n\n        process.isExpected().toFinish();\n        process.expect(\"user1\").toBeExecuted(1);\n        process.expect(\"user2\").toNotHaveArchives();\n        disableAndDeleteProcess(processDefinition);\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/command/NPECommand.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.service.ServiceAccessor;\n\npublic class NPECommand extends RuntimeCommand {\n\n    public Serializable execute(Map<String, Serializable> parameters, ServiceAccessor serviceAccessor)\n            throws SCommandParameterizationException, SCommandExecutionException {\n        throw new NullPointerException();\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/command/ParameterizationExceptionCommand.java",
    "content": "/**\n * Copyright (C) 2012 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.service.ServiceAccessor;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class ParameterizationExceptionCommand extends RuntimeCommand {\n\n    @Override\n    public Serializable execute(final Map<String, Serializable> parameters, final ServiceAccessor serviceAccessor)\n            throws SCommandParameterizationException, SCommandExecutionException {\n        throw new SCommandParameterizationException(\"parameters are null\");\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/command/helper/designer/BranchTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.helper.designer;\n\nimport static org.mockito.Mockito.verify;\n\nimport java.util.Arrays;\n\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Vincent Elcrin\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class BranchTest {\n\n    @Mock\n    ProcessDefinitionBuilder builder;\n\n    @Test\n    public void bind_should_link_first_fragment_of_the_branch() {\n        Branch branch = new Branch().start(new UserTask(\"2\")).then(new UserTask(\"3\"));\n\n        branch.bind(Arrays.<Fragment> asList(new UserTask(\"1\")), builder);\n\n        verify(builder).addTransition(\"1\", \"2\");\n    }\n\n    @Test\n    public void bind_should_link_last_fragment_of_the_branch() {\n        Branch branch = new Branch().start(new UserTask(\"1\")).then(new UserTask(\"2\"));\n        UserTask task = new UserTask(\"3\");\n\n        task.bind(Arrays.<Fragment> asList(branch), builder);\n\n        verify(builder).addTransition(\"2\", \"3\");\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/command/helper/designer/DesignerTestUtils.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.helper.designer;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.flownode.ActivityDefinition;\nimport org.bonitasoft.engine.bpm.flownode.GatewayDefinition;\nimport org.bonitasoft.engine.bpm.flownode.TransitionDefinition;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\n\n/**\n * @author Vincent Elcrin\n */\npublic class DesignerTestUtils {\n\n    interface Stringifier<O> {\n\n        String stringify(O object);\n    }\n\n    public static <O> String stringify(Collection<O> items, Stringifier<O> stringifier) {\n        List<String> strings = new ArrayList<>(items.size());\n        for (O item : items) {\n            strings.add(stringifier.stringify(item));\n        }\n        Collections.sort(strings);\n        return strings.toString();\n    }\n\n    public static String getGateways(final DesignProcessDefinition design) {\n        return stringify(design.getFlowElementContainer().getGatewaysList(), new Stringifier<GatewayDefinition>() {\n\n            @Override\n            public String stringify(GatewayDefinition gateway) {\n                String text = gateway.getName();\n                if (gateway.getDefaultTransition() != null) {\n                    text += \" (\" + getTransitionName(gateway.getDefaultTransition(), design) + \")\";\n                }\n                return text;\n            }\n        });\n    }\n\n    public static String getActivities(final DesignProcessDefinition design) {\n        return stringify(design.getFlowElementContainer().getActivities(), new Stringifier<ActivityDefinition>() {\n\n            @Override\n            public String stringify(ActivityDefinition activity) {\n                String text = activity.getName();\n                if (activity.getDefaultTransition() != null) {\n                    text += \" (\" + getTransitionName(activity.getDefaultTransition(), design) + \")\";\n                }\n                return text;\n            }\n        });\n    }\n\n    public static String getTransitions(final DesignProcessDefinition design) {\n        return stringify(design.getFlowElementContainer().getTransitions(), new Stringifier<TransitionDefinition>() {\n\n            @Override\n            public String stringify(TransitionDefinition transition) {\n                String text = getTransitionName(transition, design);\n                if (transition.getCondition() != null) {\n                    text += \" (\" + transition.getCondition().getName() + \")\";\n                }\n                return text;\n            }\n        });\n    }\n\n    private static String getTransitionName(TransitionDefinition transition, DesignProcessDefinition design) {\n        String text = design.getFlowElementContainer().getFlowNode(transition.getSource()).getName();\n        text += \"_->_\";\n        text += design.getFlowElementContainer().getFlowNode(transition.getTarget()).getName();\n        return text;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/command/helper/designer/SimpleProcessDesignerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.helper.designer;\n\nimport static org.bonitasoft.engine.command.helper.designer.Transition.fails;\nimport static org.bonitasoft.engine.command.helper.designer.Transition.meet;\nimport static org.junit.Assert.assertEquals;\n\nimport org.bonitasoft.engine.bpm.flownode.GatewayType;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.junit.Test;\n\n/**\n * Created by Vincent Elcrin\n * Date: 16/12/13\n * Time: 18:44\n */\npublic class SimpleProcessDesignerTest {\n\n    ProcessDefinitionBuilder builder = createProcessDefinitionBuilder();\n\n    @Test\n    public void should_be_able_to_design_a_linear_process() throws Exception {\n        DesignProcessDefinition design = new SimpleProcessDesigner(builder)\n                .startWith(new StartEvent(\"A\"))\n                .then(new UserTask(\"B\"))\n                .then(new UserTask(\"C\"))\n                .then(new EndEvent(\"D\"))\n                .done();\n\n        assertEquals(\"[B, C]\", DesignerTestUtils.getActivities(design));\n        assertEquals(\"[A_->_B, B_->_C, C_->_D]\", DesignerTestUtils.getTransitions(design));\n    }\n\n    @Test\n    public void should_be_able_to_design_a_process_with_a_gate() throws Exception {\n        DesignProcessDefinition design = new SimpleProcessDesigner(builder)\n                .startWith(new StartEvent(\"A\"))\n                .then(new UserTask(\"B\"))\n                .then(new Gateway(\"C\", GatewayType.EXCLUSIVE))\n                .then(new UserTask(\"D\"))\n                .then(new EndEvent(\"E\"))\n                .done();\n\n        assertEquals(\"[B, D]\", DesignerTestUtils.getActivities(design));\n        assertEquals(\"[C]\", DesignerTestUtils.getGateways(design));\n        assertEquals(\"[A_->_B, B_->_C, C_->_D, D_->_E]\", DesignerTestUtils.getTransitions(design));\n    }\n\n    @Test\n    public void should_be_able_to_design_a_process_with_parallel_activities() throws Exception {\n        DesignProcessDefinition design = new SimpleProcessDesigner(builder)\n                .startWith(new StartEvent(\"A\"))\n                .then(new UserTask(\"B1\"), new UserTask(\"B2\"))\n                .then(new EndEvent(\"C\"))\n                .done();\n\n        assertEquals(\"[B1, B2]\", DesignerTestUtils.getActivities(design));\n        assertEquals(\"[A_->_B1, A_->_B2, B1_->_C, B2_->_C]\", DesignerTestUtils.getTransitions(design));\n    }\n\n    @Test\n    public void should_be_able_to_design_a_process_multiple_incoming_conditions() throws Exception {\n        Expression condition = new ExpressionBuilder().createConstantBooleanExpression(true);\n        DesignProcessDefinition design = new SimpleProcessDesigner(builder)\n                .startWith(new StartEvent(\"A\"))\n                .then(new UserTask(\"B\"))\n                .then(new UserTask(\"C1\"), new UserTask(\"C2\"))\n                .then(new Gateway(\"D\", GatewayType.EXCLUSIVE)\n                        .when(\"C1\", fails())\n                        .when(\"C2\", meet(condition)))\n                .then(new EndEvent(\"F\"))\n                .done();\n\n        assertEquals(\"[B, C1 (C1_->_D), C2]\", DesignerTestUtils.getActivities(design));\n        assertEquals(\"[D]\", DesignerTestUtils.getGateways(design));\n        assertEquals(\"[A_->_B, B_->_C1, B_->_C2, C1_->_D, C2_->_D (true), D_->_F]\",\n                DesignerTestUtils.getTransitions(design));\n    }\n\n    @Test\n    public void should_be_able_to_design_a_process_with_multiple_outgoing_conditions() throws Exception {\n        Expression condition = new ExpressionBuilder().createConstantBooleanExpression(true);\n        DesignProcessDefinition design = new SimpleProcessDesigner(builder)\n                .startWith(new StartEvent(\"A\"))\n                .then(new UserTask(\"B\"))\n                .then(new Gateway(\"C\", GatewayType.EXCLUSIVE))\n                .then(\n                        new UserTask(\"D1\").when(\"C\", fails()),\n                        new UserTask(\"D2\").when(\"C\", meet(condition)))\n                .then(new EndEvent(\"E\"))\n                .done();\n\n        assertEquals(\"[B, D1, D2]\", DesignerTestUtils.getActivities(design));\n        assertEquals(\"[C (C_->_D1)]\", DesignerTestUtils.getGateways(design));\n        assertEquals(\"[A_->_B, B_->_C, C_->_D1, C_->_D2 (true), D1_->_E, D2_->_E]\",\n                DesignerTestUtils.getTransitions(design));\n    }\n\n    @Test\n    public void should_be_able_to_design_a_process_with_complex_branch() throws Exception {\n        DesignProcessDefinition design = new SimpleProcessDesigner(builder)\n                .startWith(new StartEvent(\"A\"))\n                .then(new UserTask(\"B\"))\n                .then(new Gateway(\"C\", GatewayType.PARALLEL))\n                .then(\n                        new Branch().start(new UserTask(\"D1\")).then(new UserTask(\"D2\")),\n                        new UserTask(\"E\"))\n                .then(new Gateway(\"F\", GatewayType.PARALLEL))\n                .then(new EndEvent(\"G\"))\n                .done();\n\n        assertEquals(\"[B, D1, D2, E]\", DesignerTestUtils.getActivities(design));\n        assertEquals(\"[C, F]\", DesignerTestUtils.getGateways(design));\n        assertEquals(\"[A_->_B, B_->_C, C_->_D1, C_->_E, D1_->_D2, D2_->_F, E_->_F, F_->_G]\",\n                DesignerTestUtils.getTransitions(design));\n    }\n\n    @Test\n    public void should_be_able_to_design_a_process_with_complex_() throws Exception {\n        DesignProcessDefinition design = new SimpleProcessDesigner(builder)\n                .startWith(new StartEvent(\"A\"))\n                .then(\n                        new UserTask(\"B\"),\n                        new Branch()\n                                .start(new Gateway(\"C\", GatewayType.PARALLEL))\n                                .then(new UserTask(\"D\"), new UserTask(\"E\"))\n                                .then(new Gateway(\"F\", GatewayType.PARALLEL)))\n                .then(new Gateway(\"G\", GatewayType.PARALLEL))\n                .then(new UserTask(\"H\"))\n                .then(new EndEvent(\"I\"))\n                .done();\n\n        assertEquals(\"[A_->_B, A_->_C, B_->_G, C_->_D, C_->_E, D_->_F, E_->_F, F_->_G, G_->_H, H_->_I]\",\n                DesignerTestUtils.getTransitions(design));\n    }\n\n    private ProcessDefinitionBuilder createProcessDefinitionBuilder() {\n        ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder();\n        builder.createNewInstance(\"Designed by designer\", \"1.0\");\n        return builder;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/connectors/RemoteConnectorExecutionIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connectors;\n\nimport static org.assertj.core.api.Assertions.tuple;\nimport static org.bonitasoft.engine.test.BuildTestUtil.generateConnectorImplementation;\nimport static org.junit.Assert.*;\n\nimport java.io.IOException;\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.assertj.core.api.Assertions;\nimport org.bonitasoft.engine.bpm.bar.BarResource;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.connector.ArchiveConnectorInstancesSearchDescriptor;\nimport org.bonitasoft.engine.bpm.connector.ArchivedConnectorInstance;\nimport org.bonitasoft.engine.bpm.connector.ConnectorCriterion;\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.bpm.connector.ConnectorImplementationDescriptor;\nimport org.bonitasoft.engine.bpm.connector.ConnectorInstance;\nimport org.bonitasoft.engine.bpm.connector.ConnectorInstancesSearchDescriptor;\nimport org.bonitasoft.engine.bpm.connector.ConnectorState;\nimport org.bonitasoft.engine.bpm.connector.FailAction;\nimport org.bonitasoft.engine.bpm.data.DataInstance;\nimport org.bonitasoft.engine.bpm.document.Document;\nimport org.bonitasoft.engine.bpm.document.DocumentsSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstance;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.bpm.process.impl.ActivityDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.AutomaticTaskDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.BoundaryEventDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.CallActivityBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.ConnectorDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.ReceiveTaskDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.SendTaskDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.SubProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.expression.ExpressionConstants;\nimport org.bonitasoft.engine.expression.ExpressionType;\nimport org.bonitasoft.engine.expression.InvalidExpressionException;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.io.IOUtil;\nimport org.bonitasoft.engine.operation.LeftOperand;\nimport org.bonitasoft.engine.operation.LeftOperandBuilder;\nimport org.bonitasoft.engine.operation.Operation;\nimport org.bonitasoft.engine.operation.OperationBuilder;\nimport org.bonitasoft.engine.operation.OperatorType;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.test.BuildTestUtil;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\n\n@SuppressWarnings(\"javadoc\")\npublic class RemoteConnectorExecutionIT extends ConnectorExecutionIT {\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    private static final String CONNECTOR_OUTPUT_NAME = \"output1\";\n\n    private static final String CONNECTOR_INPUT_NAME = \"input1\";\n\n    private static final String CONNECTOR_WITH_OUTPUT_ID = \"org.bonitasoft.connector.testConnectorWithOutput\";\n\n    private static final String FLOWNODE = \"flowNode\";\n\n    private static final String PROCESS = \"process\";\n\n    public static final String TEST_CONNECTOR_THAT_THROW_EXCEPTION_ID = \"testConnectorThatThrowException\";\n\n    @Test\n    public void executeConnectorWithJNDILookupAndAPICall() throws Exception {\n        final Expression localDataExpression = new ExpressionBuilder().createConstantLongExpression(0L);\n        final Expression processNameExpression = new ExpressionBuilder().createConstantStringExpression(PROCESS_NAME);\n        final Expression processVersionExpression = new ExpressionBuilder().createConstantStringExpression(\"1.0\");\n        final Expression outputExpression = new ExpressionBuilder().createInputExpression(\"processId\",\n                Long.class.getName());\n\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION);\n        designProcessDefinition.addLongData(\"processId\", localDataExpression);\n        designProcessDefinition.addAutomaticTask(\"start\");\n        designProcessDefinition.addAutomaticTask(\"step1\")\n                .addConnector(\"myConnector\", \"org.bonitasoft.engine.connectors.TestConnectorWithAPICall\", \"1.0\",\n                        ConnectorEvent.ON_FINISH)\n                .addInput(\"processName\", processNameExpression).addInput(\"processVersion\", processVersionExpression)\n                .addOutput(new OperationBuilder().createSetDataOperation(\"processId\", outputExpression));\n        designProcessDefinition.addAutomaticTask(\"end\");\n        designProcessDefinition.addTransition(\"start\", \"step1\");\n        designProcessDefinition.addTransition(\"step1\", \"end\");\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithTestConnectorWithAPICall(\n                designProcessDefinition);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForProcessToBeInState(processInstance, ProcessInstanceState.COMPLETED);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeConnectorOnFinishOfAnAutomaticActivityWithDataAsOutput() throws Exception {\n        final String valueOfInput1 = \"valueOfInput1\";\n        final String defaultValue = \"default\";\n        final String dataName = \"myData1\";\n\n        final Expression dataDefaultValue = new ExpressionBuilder().createConstantStringExpression(defaultValue);\n        final Expression input1Expression = new ExpressionBuilder().createConstantStringExpression(valueOfInput1);\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance(\n                \"executeConnectorOnFinishOfAnAutomaticActivityWithDataAsOutput\", \"1.0\");\n        processDefinitionBuilder.addShortTextData(dataName, dataDefaultValue);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addUserTask(\"step0\", ACTOR_NAME);\n        processDefinitionBuilder\n                .addAutomaticTask(\"step1\")\n                .addConnector(\"myConnector\", CONNECTOR_WITH_OUTPUT_ID, \"1.0\", ConnectorEvent.ON_FINISH)\n                .addInput(CONNECTOR_INPUT_NAME, input1Expression)\n                .addOutput(new LeftOperandBuilder().createNewInstance().setName(dataName).done(),\n                        OperatorType.ASSIGNMENT, \"=\", \"\",\n                        new ExpressionBuilder().createInputExpression(CONNECTOR_OUTPUT_NAME, String.class.getName()));\n        processDefinitionBuilder.addUserTask(\"step2\", ACTOR_NAME);\n        processDefinitionBuilder.addTransition(\"step0\", \"step1\");\n        processDefinitionBuilder.addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorWithOutput(\n                processDefinitionBuilder, ACTOR_NAME, user);\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId());\n        assertEquals(defaultValue, getProcessAPI().getProcessDataInstance(dataName, startProcess.getId()).getValue());\n        waitForUserTaskAndExecuteIt(startProcess, \"step0\", user);\n\n        waitForUserTask(startProcess, \"step2\");\n\n        assertEquals(valueOfInput1, getProcessAPI().getProcessDataInstance(dataName, startProcess.getId()).getValue());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeConnectorOnFinishOfMultiInstancedActivity() throws Exception {\n        // deploy the process\n        final String globalDataName = \"globalData\";\n        final String userTaskName = \"step2\";\n        final String multiTaskName = \"multi\";\n        final ProcessDefinition processDefinition = deployProcessWithConnectorOnMutiInstance(globalDataName,\n                userTaskName, multiTaskName, true, 2,\n                ConnectorEvent.ON_FINISH);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        // wait for the first Multi-instance and execute it: the connector must be executed\n        waitForUserTaskAndExecuteIt(processInstance, multiTaskName, user);\n\n        // wait for the second Multi-instance\n        final long multiInstanceId = waitForUserTask(processInstance, multiTaskName);\n\n        // check the data value\n        DataInstance globalData = getProcessAPI().getProcessDataInstance(globalDataName, processInstance.getId());\n        assertEquals(1L, globalData.getValue());\n\n        // execute the second Multi-instance: the connector must be executed again\n        assignAndExecuteStep(multiInstanceId, userId);\n\n        // wait for user task that follows the multi task\n        waitForUserTask(processInstance, userTaskName);\n\n        // check the data value\n        globalData = getProcessAPI().getProcessDataInstance(globalDataName, processInstance.getId());\n        assertEquals(2L, globalData.getValue());\n\n        // delete process\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    private ProcessDefinition deployProcessWithConnectorOnMutiInstance(final String globalDataName,\n            final String userTaskName, final String multiTaskName,\n            final boolean isSequential, final int nbOfInstances, final ConnectorEvent activationEvent)\n            throws Exception {\n        final String localDataName = \"localData\";\n\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"connectorOnMultiInstance\", \"1.0\");\n        builder.addActor(ACTOR_NAME);\n        builder.addLongData(globalDataName, new ExpressionBuilder().createConstantLongExpression(0L));\n        builder.addStartEvent(\"start\");\n\n        final UserTaskDefinitionBuilder taskBuilder = builder.addUserTask(multiTaskName, ACTOR_NAME);\n        taskBuilder.addMultiInstance(isSequential,\n                new ExpressionBuilder().createConstantIntegerExpression(nbOfInstances));\n        taskBuilder.addLongData(localDataName, new ExpressionBuilder().createConstantLongExpression(1L));\n        taskBuilder\n                .addConnector(\"connector\", \"connectorInJar\", \"1.0.0\", activationEvent)\n                .addInput(CONNECTOR_INPUT_NAME,\n                        new ExpressionBuilder().createDataExpression(localDataName, Long.class.getName()))\n                .addOutput(\n                        new LeftOperandBuilder().createDataLeftOperand(globalDataName),\n                        OperatorType.ASSIGNMENT,\n                        \"=\",\n                        \"\",\n                        new ExpressionBuilder().createGroovyScriptExpression(\"Script\", globalDataName + \" + 1\",\n                                Long.class.getName(),\n                                new ExpressionBuilder().createDataExpression(globalDataName, Long.class.getName())));\n\n        // final UserTaskDefinitionBuilder taskBuilder = builder.addUserTask(multiTaskName, ACTOR_NAME);\n        // taskBuilder.addMultiInstance(isSequential, new ExpressionBuilder().createConstantIntegerExpression(nbOfInstances));\n        // taskBuilder.addLongData(localDataName, new ExpressionBuilder().createConstantLongExpression(1L));\n        // taskBuilder\n        // .addConnector(\"connector\", CONNECTOR_WITH_OUTPUT_ID, \"1.0\", activationEvent)\n        // .addSimpleInput(CONNECTOR_INPUT_NAME, new ExpressionBuilder().createDataExpression(localDataName, Long.class.getName()))\n        // .addOutput(\n        // new LeftOperandBuilder().createDataLeftOperand(globalDataName),\n        // OperatorType.ASSIGNMENT, \"=\", \"\",\n        // new ExpressionBuilder().createGroovyScriptExpression(\"Script\", globalDataName + \" + 1\", Long.class.getName(),\n        // new ExpressionBuilder().createDataExpression(globalDataName, Long.class.getName())));\n\n        builder.addUserTask(userTaskName, ACTOR_NAME);\n        builder.addEndEvent(\"end\");\n        builder.addTransition(\"start\", multiTaskName);\n        builder.addTransition(multiTaskName, userTaskName);\n        builder.addTransition(userTaskName, \"end\");\n\n        final List<BarResource> resources = new ArrayList<>();\n        addResource(resources, \"/org/bonitasoft/engine/connectors/TestConnectorInJar.impl\", \"TestConnectorInJar.impl\");\n        addResource(resources, \"/org/bonitasoft/engine/connectors/connector-in-jar.jar.bak\", \"connector-in-jar.jar\");\n        final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive();\n        businessArchiveBuilder.addConnectorImplementation(resources.get(0));\n        businessArchiveBuilder.addClasspathResource(resources.get(1));\n        businessArchiveBuilder.setProcessDefinition(builder.done());\n\n        return deployAndEnableProcessWithActor(businessArchiveBuilder.done(), ACTOR_NAME, user);\n    }\n\n    @Test\n    public void executeConnectorMultipleConnectorsOnOneActivity() throws Exception {\n        final String defaultValue = \"a\";\n        final String dataName = \"myData1\";\n        final Expression dataDefaultValue = new ExpressionBuilder().createConstantStringExpression(defaultValue);\n        final Expression input1Expression = new ExpressionBuilder().createConstantStringExpression(\"a\");\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance(\n                \"executeConnectorOnFinishOfAnAutomaticActivityWithDataAsOutput\", \"1.0\");\n        processDefinitionBuilder.addShortTextData(dataName, dataDefaultValue);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        final String inputName = CONNECTOR_INPUT_NAME;\n        processDefinitionBuilder.addUserTask(\"step0\", ACTOR_NAME);\n        final AutomaticTaskDefinitionBuilder addAutomaticTask = processDefinitionBuilder.addAutomaticTask(\"step1\");\n        final int nbOfConnectors = 25;\n        for (int i = 0; i < nbOfConnectors; i++) {\n            addAutomaticTask\n                    .addConnector(\"myConnector\" + i, CONNECTOR_WITH_OUTPUT_ID, \"1.0\", ConnectorEvent.ON_FINISH)\n                    .addInput(inputName, input1Expression)\n                    .addOutput(\n                            new LeftOperandBuilder().createNewInstance().setName(dataName).done(),\n                            OperatorType.ASSIGNMENT,\n                            \"=\",\n                            \"\",\n                            new ExpressionBuilder().createGroovyScriptExpression(\"concat\", \"myData1+output1\",\n                                    String.class.getName(),\n                                    new ExpressionBuilder().createInputExpression(CONNECTOR_OUTPUT_NAME,\n                                            String.class.getName()),\n                                    new ExpressionBuilder().createDataExpression(\"myData1\", String.class.getName())));\n        }\n        processDefinitionBuilder.addUserTask(\"step2\", ACTOR_NAME);\n        processDefinitionBuilder.addTransition(\"step0\", \"step1\");\n        processDefinitionBuilder.addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorWithOutput(\n                processDefinitionBuilder, ACTOR_NAME, user);\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId());\n        assertEquals(defaultValue, getProcessAPI().getProcessDataInstance(dataName, startProcess.getId()).getValue());\n        waitForUserTaskAndExecuteIt(startProcess, \"step0\", user);\n        waitForUserTask(startProcess, \"step2\");\n\n        final String value = (String) getProcessAPI().getProcessDataInstance(dataName, startProcess.getId()).getValue();\n        assertEquals(nbOfConnectors + 1, value.length());\n        for (int i = 0; i < value.length(); i++) {\n            assertEquals('a', value.charAt(i));\n        }\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void redeployProcessWithNoConnectorImplem() throws Exception {\n        final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"executeConnectorOnActivityInstance\", \"1.0\");\n        processDefBuilder.addActor(ACTOR_NAME);\n        processDefBuilder.addUserTask(\"step0\", ACTOR_NAME);\n\n        final ProcessDefinition processDefinition1 = deployProcessWithExternalTestConnector(processDefBuilder,\n                ACTOR_NAME, user);\n        List<ConnectorImplementationDescriptor> connectorImplems = getProcessAPI()\n                .getConnectorImplementations(processDefinition1.getId(), 0, 100, null);\n        assertEquals(1, getProcessAPI().getNumberOfConnectorImplementations(processDefinition1.getId()));\n        assertEquals(1, connectorImplems.size());\n        disableAndDeleteProcess(processDefinition1);\n\n        // redeploy same process:\n        final ProcessDefinition processDefinition2 = deployAndEnableProcessWithActor(processDefBuilder.done(),\n                ACTOR_NAME, user);\n        connectorImplems = getProcessAPI().getConnectorImplementations(processDefinition2.getId(), 0, 100, null);\n        assertEquals(0, connectorImplems.size());\n\n        disableAndDeleteProcess(processDefinition2);\n    }\n\n    @Test\n    public void executeConnectorOnProcessDefinition() throws Exception {\n        final String valueOfInput1 = \"Lily\";\n        final String valueOfInput2 = \"Lucy\";\n        final String mainExpContent = \"'welcome '+valueOfInput1+' and '+valueOfInput2\";\n        final String inputName1 = \"valueOfInput1\";\n        final String inputName2 = \"valueOfInput2\";\n        final String mainInputName1 = \"param1\";\n        final String resContent = \"welcome Lily and Lucy\";\n\n        // Input expression\n        final Expression input1Expression = new ExpressionBuilder().createInputExpression(inputName1,\n                String.class.getName());\n        final Expression input2Expression = new ExpressionBuilder().createInputExpression(inputName2,\n                String.class.getName());\n\n        // Main Expression\n        final Expression mainExp = new ExpressionBuilder().createExpression(mainInputName1, mainExpContent,\n                ExpressionType.TYPE_READ_ONLY_SCRIPT.toString(),\n                String.class.getName(), \"GROOVY\", Arrays.asList(input1Expression, input2Expression));\n\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"executeConnectorOnActivityInstance\", \"1.0\");\n        final String actorName = \"Isabelle Adjani\";\n        designProcessDefinition.addActor(actorName);\n\n        final ProcessDefinition processDefinition = deployProcessWithExternalTestConnector(designProcessDefinition,\n                actorName, user);\n\n        final Map<String, Expression> connectorInputParameters = getConnectorInputParameters(mainInputName1, mainExp);\n        final Map<String, Map<String, Serializable>> inputValues = getInputValues(mainInputName1,\n                Arrays.asList(inputName1, inputName2),\n                Arrays.asList(valueOfInput1, valueOfInput2));\n\n        final Map<String, Serializable> res = getProcessAPI().executeConnectorOnProcessDefinition(\n                ConnectorExecutionIT.DEFAULT_EXTERNAL_CONNECTOR_ID,\n                ConnectorExecutionIT.DEFAULT_EXTERNAL_CONNECTOR_VERSION, connectorInputParameters, inputValues,\n                processDefinition.getId());\n\n        assertEquals(resContent, res.get(mainInputName1));\n        assertTrue((Boolean) res.get(\"hasBeenValidated\"));\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    private Map<String, Expression> getConnectorInputParameters(final String mainName, final Expression mainExp) {\n        final Map<String, Expression> connectorInputParameters = new HashMap<>();\n        connectorInputParameters.put(mainName, mainExp);\n        return connectorInputParameters;\n    }\n\n    private Map<String, Map<String, Serializable>> getInputValues(final String mainName, final List<String> names,\n            final List<String> vars) {\n        final Map<String, Map<String, Serializable>> inputValues = new HashMap<>();\n        final Map<String, Serializable> values = new HashMap<>();\n        if (names != null && !names.isEmpty() && vars != null && !vars.isEmpty() && names.size() == vars.size()) {\n            for (int i = 0; i < names.size(); i++) {\n                values.put(names.get(i), vars.get(i));\n            }\n        }\n        inputValues.put(mainName, values);\n        return inputValues;\n    }\n\n    @Test\n    public void executeConnectorInJar() throws Exception {\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"testConnectorWithExecutionTooLong\", \"1.0\");\n        designProcessDefinition.addActor(ACTOR_NAME);\n        designProcessDefinition.addAutomaticTask(\"step1\").addConnector(\"myConnector1\", \"connectorInJar\", \"1.0.0\",\n                ConnectorEvent.ON_ENTER);\n\n        final List<BarResource> resources = new ArrayList<>();\n        addResource(resources, \"/org/bonitasoft/engine/connectors/TestConnectorInJar.impl\", \"TestConnectorInJar.impl\");\n        addResource(resources, \"/org/bonitasoft/engine/connectors/connector-in-jar.jar.bak\", \"connector-in-jar.jar\");\n        final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive();\n        businessArchiveBuilder.addConnectorImplementation(resources.get(0));\n        businessArchiveBuilder.addClasspathResource(resources.get(1));\n        businessArchiveBuilder.setProcessDefinition(designProcessDefinition.done());\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchiveBuilder.done(),\n                ACTOR_NAME, user);\n        final ProcessInstance process = getProcessAPI().startProcess(processDefinition.getId());\n        waitForProcessToFinish(process);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getNumberOfConnectorImplementationsWhenProcessDoesNotExists() {\n        assertEquals(0, getProcessAPI().getNumberOfConnectorImplementations(123L));\n    }\n\n    @Test\n    public void getConnectorImplementations() throws Exception {\n        // connector information\n        final String connectorId = \"org.bonitasoft.connector.testConnector\";\n        final Expression input1Expression = new ExpressionBuilder().createConstantStringExpression(\"valueOfInput\");\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithConnector\", \"1.0\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addAutomaticTask(\"step1\")\n                .addConnector(\"myConnector\", connectorId, \"1.0\", ConnectorEvent.ON_ENTER)\n                .addInput(CONNECTOR_INPUT_NAME, input1Expression);\n        processDefinitionBuilder.addUserTask(\"step2\", ACTOR_NAME);\n        processDefinitionBuilder.addTransition(\"step1\", \"step2\");\n\n        final List<BarResource> connectorImplementations = Arrays.asList(\n                BuildTestUtil.getContentAndBuildBarResource(\"TestConnector.impl\", TestConnector.class),\n                BuildTestUtil.getContentAndBuildBarResource(\"TestConnectorWithOutput.impl\",\n                        TestConnectorWithOutput.class),\n                BuildTestUtil.getContentAndBuildBarResource(\"TestConnector3.impl\", TestConnector3.class));\n        final List<BarResource> generateConnectorDependencies = Arrays.asList(\n                BuildTestUtil.generateJarAndBuildBarResource(TestConnector.class, \"TestConnector.jar\"),\n                BuildTestUtil.generateJarAndBuildBarResource(VariableStorage.class, \"VariableStorage.jar\"));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActorAndConnectorAndParameter(\n                processDefinitionBuilder, ACTOR_NAME, user,\n                connectorImplementations, generateConnectorDependencies, null);\n        final long processDefinitionId = processDefinition.getId();\n        assertEquals(3, getProcessAPI().getNumberOfConnectorImplementations(processDefinitionId));\n\n        // test ASC\n        List<ConnectorImplementationDescriptor> connectorImplementationDescriptors = getProcessAPI()\n                .getConnectorImplementations(processDefinitionId, 0, 3,\n                        ConnectorCriterion.DEFINITION_ID_ASC);\n        assertNotNull(connectorImplementationDescriptors);\n        assertEquals(3, connectorImplementationDescriptors.size());\n        assertEquals(connectorId, connectorImplementationDescriptors.get(0).getDefinitionId());\n        assertEquals(CONNECTOR_WITH_OUTPUT_ID, connectorImplementationDescriptors.get(2).getDefinitionId());\n\n        connectorImplementationDescriptors = getProcessAPI().getConnectorImplementations(processDefinitionId, 0, 1,\n                ConnectorCriterion.DEFINITION_ID_ASC);\n        assertEquals(1, connectorImplementationDescriptors.size());\n        assertEquals(connectorId, connectorImplementationDescriptors.get(0).getDefinitionId());\n        connectorImplementationDescriptors = getProcessAPI().getConnectorImplementations(processDefinitionId, 2, 1,\n                ConnectorCriterion.DEFINITION_ID_ASC);\n        assertEquals(1, connectorImplementationDescriptors.size());\n        assertEquals(CONNECTOR_WITH_OUTPUT_ID, connectorImplementationDescriptors.get(0).getDefinitionId());\n        // test DESC\n        connectorImplementationDescriptors = getProcessAPI().getConnectorImplementations(processDefinitionId, 2, 1,\n                ConnectorCriterion.DEFINITION_ID_DESC);\n        assertNotNull(connectorImplementationDescriptors);\n        assertEquals(1, connectorImplementationDescriptors.size());\n        assertEquals(connectorId, connectorImplementationDescriptors.get(0).getDefinitionId());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void longConnectorOutputStoredInProcessVariableShouldThrowANiceException() throws Exception {\n        // connector information\n        final Expression outputOfConnectorExpression = new ExpressionBuilder()\n                .createConstantStringExpression(\"outputExpression\");\n        final Expression groovyExpression = new ExpressionBuilder().createGroovyScriptExpression(\"generateLongOutput\",\n                \"'a'*1000\", String.class.getName());\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithConnector\", \"1.0\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addShortTextData(\"outputOfConnector\", outputOfConnectorExpression);\n        processDefinitionBuilder\n                .addAutomaticTask(\"step1\")\n                .addConnector(\"theConnector\", CONNECTOR_WITH_OUTPUT_ID, \"1.0\", ConnectorEvent.ON_ENTER)\n                .addInput(CONNECTOR_INPUT_NAME, groovyExpression)\n                .addOutput(new LeftOperandBuilder().createNewInstance().setName(\"outputOfConnector\").done(),\n                        OperatorType.ASSIGNMENT, \"=\", \"\",\n                        new ExpressionBuilder().createInputExpression(CONNECTOR_OUTPUT_NAME, String.class.getName()));\n\n        final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorWithOutput(\n                processDefinitionBuilder, ACTOR_NAME, user);\n        final long processDefinitionId = processDefinition.getId();\n\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinitionId);\n        waitForTaskToFail(startProcess);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeConnectorThatThrowExceptionFailUserTask() throws Exception {\n        final ProcessDefinition processDefinition = getProcessWithConnectorThatThrowErrorOnUserTaskOnEnter(\"normal\",\n                FailAction.FAIL, false);\n        final long processDefinitionId = processDefinition.getId();\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinitionId);\n        waitForTaskToFail(startProcess);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeConnectorThatThrowExceptionFailAutomaticTaskOnEnter() throws Exception {\n        final ProcessDefinition processDefinition = getProcessWithConnectorThatThrowErrorOnAutomaticTaskOnEnter(\n                \"normal\", FailAction.FAIL, false);\n        final long processDefinitionId = processDefinition.getId();\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinitionId);\n        waitForTaskToFail(startProcess);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeConnectorThatThrowExceptionFailAutomaticTaskOnFinish() throws Exception {\n        final ProcessDefinition processDefinition = getProcessWithConnectorThatThrowErrorOnAutomaticTaskOnFinish(\n                \"normal\", FailAction.FAIL, false);\n        final long processDefinitionId = processDefinition.getId();\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinitionId);\n        waitForTaskToFail(startProcess);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void connectorThatThrowExceptionFailPolicyOnTaskInput() throws Exception {\n        final Expression outputOfConnectorExpression = new ExpressionBuilder()\n                .createConstantStringExpression(\"outputExpression\");\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithConnector\", \"1.0\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addShortTextData(\"outputOfConnector\", outputOfConnectorExpression);\n        final UserTaskDefinitionBuilder userTaskBuilder = processDefinitionBuilder.addUserTask(\"step1\", ACTOR_NAME);\n        final ConnectorDefinitionBuilder addConnector = userTaskBuilder.addConnector(\"testConnectorThatThrowException\",\n                \"testConnectorThatThrowException\",\n                \"1.0\", ConnectorEvent.ON_ENTER);\n        addConnector.addInput(\"kind\",\n                new ExpressionBuilder().createGroovyScriptExpression(\"fails\", \"8/0\", String.class.getName()));\n\n        final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorThatThrowException(\n                processDefinitionBuilder, ACTOR_NAME, user);\n\n        final long processDefinitionId = processDefinition.getId();\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinitionId);\n        final ActivityInstance activityInstance = waitForTaskToFail(startProcess);\n        final SearchOptions searchOptions = getFirst100ConnectorInstanceSearchOptions(activityInstance.getId(),\n                FLOWNODE).done();\n        final SearchResult<ConnectorInstance> connectorInstances = getProcessAPI()\n                .searchConnectorInstances(searchOptions);\n        assertEquals(1, connectorInstances.getCount());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void connectorThatThrowExceptionIgnorePolicyOnTaskInput() throws Exception {\n        final Expression outputOfConnectorExpression = new ExpressionBuilder()\n                .createConstantStringExpression(\"outputExpression\");\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithConnector\", \"1.0\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addShortTextData(\"outputOfConnector\", outputOfConnectorExpression);\n        final UserTaskDefinitionBuilder userTaskBuilder = processDefinitionBuilder.addUserTask(\"step1\", ACTOR_NAME);\n        final ConnectorDefinitionBuilder addConnector = userTaskBuilder.addConnector(\"testConnectorThatThrowException\",\n                \"testConnectorThatThrowException\",\n                \"1.0\", ConnectorEvent.ON_ENTER);\n        addConnector.addInput(\"kind\",\n                new ExpressionBuilder().createGroovyScriptExpression(\"fails\", \"8/0\", String.class.getName()));\n        addConnector.ignoreError();\n        final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorThatThrowException(\n                processDefinitionBuilder, ACTOR_NAME, user);\n\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(startProcess, \"step1\");\n        final SearchOptions searchOptions = getFirst100ConnectorInstanceSearchOptions(step1Id, FLOWNODE).done();\n        final SearchResult<ConnectorInstance> connectorInstances = getProcessAPI()\n                .searchConnectorInstances(searchOptions);\n        assertEquals(1, connectorInstances.getCount());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void connectorThatThrowExceptionFailPolicyOnProcessInput() throws Exception {\n        final Expression outputOfConnectorExpression = new ExpressionBuilder()\n                .createConstantStringExpression(\"outputExpression\");\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithConnector\", \"1.0\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addShortTextData(\"outputOfConnector\", outputOfConnectorExpression);\n        processDefinitionBuilder.addUserTask(\"step1\", ACTOR_NAME);\n        final ConnectorDefinitionBuilder addConnector = processDefinitionBuilder.addConnector(\n                \"testConnectorThatThrowException\",\n                \"testConnectorThatThrowException\", \"1.0\",\n                ConnectorEvent.ON_ENTER);\n        addConnector.addInput(\"kind\",\n                new ExpressionBuilder().createGroovyScriptExpression(\"fails\", \"8/0\", String.class.getName()));\n        final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorThatThrowException(\n                processDefinitionBuilder, ACTOR_NAME, user);\n\n        final long processDefinitionId = processDefinition.getId();\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinitionId);\n        waitForProcessToBeInState(startProcess, ProcessInstanceState.ERROR);\n        final SearchOptions searchOptions = getFirst100ConnectorInstanceSearchOptions(startProcess.getId(), PROCESS)\n                .done();\n        final SearchResult<ConnectorInstance> connectorInstances = getProcessAPI()\n                .searchConnectorInstances(searchOptions);\n        assertEquals(1, connectorInstances.getCount());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void connectorThatThrowExceptionErrorEventPolicyBoundaryOnTaskInput() throws Exception {\n        final Expression outputOfConnectorExpression = new ExpressionBuilder()\n                .createConstantStringExpression(\"outputExpression\");\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithConnector\", \"1.0\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addShortTextData(\"outputOfConnector\", outputOfConnectorExpression);\n        final UserTaskDefinitionBuilder userTaskBuilder = processDefinitionBuilder.addUserTask(\"step1inputfail\",\n                ACTOR_NAME);\n        final ConnectorDefinitionBuilder addConnector = userTaskBuilder.addConnector(\"testConnectorThatThrowException\",\n                \"testConnectorThatThrowException\",\n                \"1.0\", ConnectorEvent.ON_ENTER);\n        addConnector.addInput(\"kind\",\n                new ExpressionBuilder().createGroovyScriptExpression(\"fails\", \"8/0\", String.class.getName()));\n        addConnector.throwErrorEventWhenFailed(\"error\");\n        final BoundaryEventDefinitionBuilder boundaryEvent = userTaskBuilder.addBoundaryEvent(\"errorBoundary\", true);\n        boundaryEvent.addErrorEventTrigger(\"error\");\n        processDefinitionBuilder.addUserTask(\"errorTask\", ACTOR_NAME);\n        processDefinitionBuilder.addTransition(\"errorBoundary\", \"errorTask\");\n        final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorThatThrowException(\n                processDefinitionBuilder, ACTOR_NAME, user);\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId());\n        // the connector must trigger this exception step\n        waitForUserTaskAndExecuteIt(startProcess, \"errorTask\", user);\n        waitForProcessToFinish(startProcess);\n        // clean up\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void connectorThatThrowExceptionErrorEventPolicyBoundaryOnAutomaticTask() throws Exception {\n        final Expression outputOfConnectorExpression = new ExpressionBuilder()\n                .createConstantStringExpression(\"outputExpression\");\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithConnector\", \"1.0\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addShortTextData(\"outputOfConnector\", outputOfConnectorExpression);\n        final AutomaticTaskDefinitionBuilder taskBuilder = processDefinitionBuilder.addAutomaticTask(\"step1inputfail\");\n        final ConnectorDefinitionBuilder addConnector = taskBuilder.addConnector(\"testConnectorThatThrowException\",\n                \"testConnectorThatThrowException\", \"1.0\",\n                ConnectorEvent.ON_ENTER);\n        addConnector.addInput(\"kind\",\n                new ExpressionBuilder().createGroovyScriptExpression(\"fails\", \"8/0\", String.class.getName()));\n        addConnector.throwErrorEventWhenFailed(\"error\");\n        final BoundaryEventDefinitionBuilder boundaryEvent = taskBuilder.addBoundaryEvent(\"errorBoundary\");\n        boundaryEvent.addErrorEventTrigger(\"error\");\n        processDefinitionBuilder.addUserTask(\"errorTask\", ACTOR_NAME);\n        processDefinitionBuilder.addTransition(\"errorBoundary\", \"errorTask\");\n        final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorThatThrowException(\n                processDefinitionBuilder, ACTOR_NAME, user);\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId());\n        // the connector must trigger this exception step\n        waitForUserTaskAndExecuteIt(startProcess, \"errorTask\", user);\n        waitForProcessToFinish(startProcess);\n        // clean up\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void connectorThatThrowExceptionErrorEventPolicyBoundaryOnReceiveTask() throws Exception {\n        final Expression outputOfConnectorExpression = new ExpressionBuilder()\n                .createConstantStringExpression(\"outputExpression\");\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithConnector\", \"1.0\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addShortTextData(\"outputOfConnector\", outputOfConnectorExpression);\n        final ReceiveTaskDefinitionBuilder taskBuilder = processDefinitionBuilder.addReceiveTask(\"step1inputfail\",\n                \"m1\");\n        final ConnectorDefinitionBuilder addConnector = taskBuilder.addConnector(\"testConnectorThatThrowException\",\n                \"testConnectorThatThrowException\", \"1.0\",\n                ConnectorEvent.ON_ENTER);\n        addConnector.addInput(\"kind\",\n                new ExpressionBuilder().createGroovyScriptExpression(\"fails\", \"8/0\", String.class.getName()));\n        addConnector.throwErrorEventWhenFailed(\"error\");\n        final BoundaryEventDefinitionBuilder boundaryEvent = taskBuilder.addBoundaryEvent(\"errorBoundary\");\n        boundaryEvent.addErrorEventTrigger(\"error\");\n        processDefinitionBuilder.addUserTask(\"errorTask\", ACTOR_NAME);\n        processDefinitionBuilder.addTransition(\"errorBoundary\", \"errorTask\");\n\n        final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorThatThrowException(\n                processDefinitionBuilder, ACTOR_NAME, user);\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId());\n        // the connector must trigger this exception step\n        waitForUserTaskAndExecuteIt(startProcess, \"errorTask\", user);\n        waitForProcessToFinish(startProcess);\n        // clean up\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void connectorThatThrowExceptionErrorEventPolicyBoundaryOnSendTask() throws Exception {\n        final Expression outputOfConnectorExpression = new ExpressionBuilder()\n                .createConstantStringExpression(\"outputExpression\");\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithConnector\", \"1.0\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addShortTextData(\"outputOfConnector\", outputOfConnectorExpression);\n        final SendTaskDefinitionBuilder taskBuilder = processDefinitionBuilder.addSendTask(\"step1inputfail\", \"m1\",\n                new ExpressionBuilder().createConstantStringExpression(\"p2\"));\n        final ConnectorDefinitionBuilder addConnector = taskBuilder.addConnector(\"testConnectorThatThrowException\",\n                \"testConnectorThatThrowException\", \"1.0\",\n                ConnectorEvent.ON_ENTER);\n        addConnector.addInput(\"kind\",\n                new ExpressionBuilder().createGroovyScriptExpression(\"fails\", \"8/0\", String.class.getName()));\n        addConnector.throwErrorEventWhenFailed(\"error\");\n        final BoundaryEventDefinitionBuilder boundaryEvent = taskBuilder.addBoundaryEvent(\"errorBoundary\");\n        boundaryEvent.addErrorEventTrigger(\"error\");\n        processDefinitionBuilder.addUserTask(\"errorTask\", ACTOR_NAME);\n        processDefinitionBuilder.addTransition(\"errorBoundary\", \"errorTask\");\n        final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorThatThrowException(\n                processDefinitionBuilder, ACTOR_NAME, user);\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId());\n        // the connector must trigger this exception step\n        waitForUserTaskAndExecuteIt(startProcess, \"errorTask\", user);\n        waitForProcessToFinish(startProcess);\n        // clean up\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void connectorThatThrowExceptionErrorEventPolicyBoundaryOnTaskOutput() throws Exception {\n        final Expression outputOfConnectorExpression = new ExpressionBuilder()\n                .createConstantStringExpression(\"outputExpression\");\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithConnector\", \"1.0\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addShortTextData(\"outputOfConnector\", outputOfConnectorExpression);\n        final UserTaskDefinitionBuilder userTaskBuilder = processDefinitionBuilder.addUserTask(\"step1inputfail\",\n                ACTOR_NAME);\n        final ConnectorDefinitionBuilder addConnector = userTaskBuilder.addConnector(\"myConnector\",\n                CONNECTOR_WITH_OUTPUT_ID, \"1.0\", ConnectorEvent.ON_ENTER);\n        addConnector.addInput(CONNECTOR_INPUT_NAME, new ExpressionBuilder().createConstantStringExpression(\"a\"));\n        addConnector.addOutput(new LeftOperandBuilder().createNewInstance().setName(\"outputOfConnector\").done(),\n                OperatorType.ASSIGNMENT, \"=\", \"\",\n                new ExpressionBuilder().createGroovyScriptExpression(\"concat\", \"8/0\", String.class.getName()));\n        addConnector.throwErrorEventWhenFailed(\"error\");\n        final BoundaryEventDefinitionBuilder boundaryEvent = userTaskBuilder.addBoundaryEvent(\"errorBoundary\", true);\n        boundaryEvent.addErrorEventTrigger(\"error\");\n        processDefinitionBuilder.addUserTask(\"errorTask\", ACTOR_NAME);\n        processDefinitionBuilder.addTransition(\"errorBoundary\", \"errorTask\");\n        final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorWithOutput(\n                processDefinitionBuilder, ACTOR_NAME, user);\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId());\n        // the connector must trigger this exception step\n        waitForUserTaskAndExecuteIt(startProcess, \"errorTask\", user);\n        waitForProcessToFinish(startProcess);\n        // clean up\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void ignoreErrorConnectorOnBoundaryWhenInputFail() throws Exception {\n        final Expression outputOfConnectorExpression = new ExpressionBuilder()\n                .createConstantStringExpression(\"outputExpression\");\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithConnector\", \"1.0\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addShortTextData(\"outputOfConnector\", outputOfConnectorExpression);\n        final UserTaskDefinitionBuilder userTaskBuilder = processDefinitionBuilder.addUserTask(\"step1\", ACTOR_NAME);\n        final ConnectorDefinitionBuilder addConnector = userTaskBuilder.addConnector(\"testConnectorThatThrowException\",\n                \"testConnectorThatThrowException\",\n                \"1.0\", ConnectorEvent.ON_ENTER);\n        addConnector.addInput(\"kind\",\n                new ExpressionBuilder().createGroovyScriptExpression(\"fails\", \"8/0\", String.class.getName()));\n        addConnector.ignoreError();\n        final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorThatThrowException(\n                processDefinitionBuilder, ACTOR_NAME, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        // the connector must trigger this exception step\n        waitForUserTaskAndExecuteIt(processInstance, \"step1\", user);\n        waitForProcessToFinish(processInstance);\n        // clean up\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void connectorThatThrowExceptionIgnorePolicyOnTaskOutput() throws Exception {\n        final Expression outputOfConnectorExpression = new ExpressionBuilder()\n                .createConstantStringExpression(\"outputExpression\");\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithConnector\", \"1.0\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addShortTextData(\"outputOfConnector\", outputOfConnectorExpression);\n        final UserTaskDefinitionBuilder userTaskBuilder = processDefinitionBuilder.addUserTask(\"step1\", ACTOR_NAME);\n        final ConnectorDefinitionBuilder addConnector = userTaskBuilder.addConnector(\"myConnector\",\n                CONNECTOR_WITH_OUTPUT_ID, \"1.0\", ConnectorEvent.ON_ENTER);\n        addConnector.addInput(CONNECTOR_INPUT_NAME, new ExpressionBuilder().createConstantStringExpression(\"a\"));\n        addConnector.addOutput(new LeftOperandBuilder().createNewInstance().setName(\"outputOfConnector\").done(),\n                OperatorType.ASSIGNMENT, \"=\", \"\",\n                new ExpressionBuilder().createGroovyScriptExpression(\"concat\", \"8/0\", String.class.getName()));\n        addConnector.ignoreError();\n        final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorWithOutput(\n                processDefinitionBuilder, ACTOR_NAME, user);\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId());\n        // the connector must trigger this exception step\n        waitForUserTaskAndExecuteIt(startProcess, \"step1\", user);\n        waitForProcessToFinish(startProcess);\n        // clean up\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void connectorThatThrowExceptionFailPolicyOnTaskOutput() throws Exception {\n        final Expression dataDefaultValue = new ExpressionBuilder().createConstantStringExpression(\"NaN\");\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithConnector\", \"1.0\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addShortTextData(\"result\", dataDefaultValue);\n        final UserTaskDefinitionBuilder userTaskBuilder = processDefinitionBuilder.addUserTask(\"step1\", ACTOR_NAME);\n        final ConnectorDefinitionBuilder addConnector = userTaskBuilder.addConnector(\"myConnector\",\n                CONNECTOR_WITH_OUTPUT_ID, \"1.0\", ConnectorEvent.ON_ENTER);\n        addConnector.addInput(CONNECTOR_INPUT_NAME, new ExpressionBuilder().createConstantStringExpression(\"a\"));\n        addConnector.addOutput(new LeftOperandBuilder().createNewInstance().setName(\"outputOfConnector\").done(),\n                OperatorType.ASSIGNMENT, \"=\", \"\",\n                new ExpressionBuilder().createGroovyScriptExpression(\"concat\", \"8/0\", String.class.getName()));\n\n        final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorWithOutput(\n                processDefinitionBuilder, ACTOR_NAME, user);\n        final long processDefinitionId = processDefinition.getId();\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinitionId);\n        final ActivityInstance activityInstance = waitForTaskToFail(startProcess);\n        final SearchOptions searchOptions = getFirst100ConnectorInstanceSearchOptions(activityInstance.getId(),\n                FLOWNODE).done();\n        final SearchResult<ConnectorInstance> connectorInstances = getProcessAPI()\n                .searchConnectorInstances(searchOptions);\n        assertEquals(1, connectorInstances.getCount());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    private SearchOptionsBuilder getFirst100ConnectorInstanceSearchOptions(final long containerId,\n            final String containerType) {\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 100);\n        searchOptionsBuilder.filter(ConnectorInstancesSearchDescriptor.CONTAINER_ID, containerId);\n        searchOptionsBuilder.filter(ConnectorInstancesSearchDescriptor.CONTAINER_TYPE, containerType);\n        searchOptionsBuilder.filter(ConnectorInstancesSearchDescriptor.STATE, ConnectorState.FAILED.name());\n        searchOptionsBuilder.sort(ConnectorInstancesSearchDescriptor.NAME, Order.ASC);\n        return searchOptionsBuilder;\n    }\n\n    @Test\n    public void executeConnectorThatThrowRuntimeExceptionFailUserTaskOnEnter() throws Exception {\n        final ProcessDefinition processDefinition = getProcessWithConnectorThatThrowErrorOnUserTaskOnEnter(\"runtime\",\n                FailAction.FAIL, false);\n        final long processDefinitionId = processDefinition.getId();\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinitionId);\n        waitForTaskToFail(startProcess);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeConnectorThatThrowRuntimeExceptionInConnectFailUserTaskOnEnter() throws Exception {\n        final ProcessDefinition processDefinition = getProcessWithConnectorThatThrowErrorOnUserTaskOnEnter(\"connect\",\n                FailAction.FAIL, false);\n        final long processDefinitionId = processDefinition.getId();\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinitionId);\n        waitForTaskToFail(startProcess);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeConnectorThatThrowRuntimeExceptionInDisconnectFailUserTaskOnEnter() throws Exception {\n        final ProcessDefinition processDefinition = getProcessWithConnectorThatThrowErrorOnUserTaskOnEnter(\"disconnect\",\n                FailAction.FAIL, false);\n        final long processDefinitionId = processDefinition.getId();\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinitionId);\n        waitForTaskToFail(startProcess);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeConnectorThatThrowRuntimeExceptionFailAutomaticTaskOnEnter() throws Exception {\n        final ProcessDefinition processDefinition = getProcessWithConnectorThatThrowErrorOnAutomaticTaskOnEnter(\n                \"runtime\", FailAction.FAIL, false);\n        final long processDefinitionId = processDefinition.getId();\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinitionId);\n        waitForTaskToFail(startProcess);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void connectorThatThrowRuntimeExceptionFailTask() throws Exception {\n        final ProcessDefinition processDefinition = getProcessWithConnectorThatThrowErrorOnAutomaticTaskOnFinish(\n                \"runtime\", FailAction.FAIL, false);\n        final long processDefinitionId = processDefinition.getId();\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinitionId);\n        waitForTaskToFail(startProcess);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void connectorThatThrowRuntimeExceptionFailProcess() throws Exception {\n        final ProcessDefinition processDefinition = getProcessWithConnectorThatThrowErrorOnUserTaskOnEnter(\"runtime\",\n                FailAction.FAIL, true);\n        final long processDefinitionId = processDefinition.getId();\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinitionId);\n        waitForProcessToBeInState(startProcess, ProcessInstanceState.ERROR);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void connectorThatThrowExceptionFailProcess() throws Exception {\n        final ProcessDefinition processDefinition = getProcessWithConnectorThatThrowErrorOnUserTaskOnEnter(\"normal\",\n                FailAction.FAIL, true);\n        final long processDefinitionId = processDefinition.getId();\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinitionId);\n        waitForProcessToBeInState(startProcess, ProcessInstanceState.ERROR);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void connectorThatThrowExceptionIgnorePolicyOnTask() throws Exception {\n        final ProcessDefinition processDefinition = getProcessWithConnectorThatThrowErrorOnUserTaskOnEnter(\"normal\",\n                FailAction.IGNORE, false);\n        final long processDefinitionId = processDefinition.getId();\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinitionId);\n        waitForUserTask(startProcess, \"step1\");\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void connectorThatThrowExceptionIgnorePolicyOnProcess() throws Exception {\n        final ProcessDefinition processDefinition = getProcessWithConnectorThatThrowErrorOnUserTaskOnEnter(\"normal\",\n                FailAction.IGNORE, true);\n        final long processDefinitionId = processDefinition.getId();\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinitionId);\n        waitForUserTask(startProcess, \"step1\");\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void connectorThatThrowExceptionErrorEventPolicyWithEventSubProcessOnTask() throws Exception {\n        final ProcessDefinition processDefinition = getProcessWithConnectorThatThrowErrorOnUserTaskOnEnter(\"normal\",\n                FailAction.ERROR_EVENT, false);\n        final long processDefinitionId = processDefinition.getId();\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinitionId);\n        waitForUserTask(startProcess, \"errorTask\");\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void connectorThatThrowExceptionErrorEventPolicyWithEventSubProcessOnProcess() throws Exception {\n        final ProcessDefinition processDefinition = getProcessWithConnectorThatThrowErrorOnUserTaskOnEnter(\"normal\",\n                FailAction.ERROR_EVENT, true);\n        final long processDefinitionId = processDefinition.getId();\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinitionId);\n        waitForUserTask(startProcess, \"errorTask\");\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    private ProcessDefinition getProcessWithConnectorThatThrowError(final String exceptionType,\n            final FailAction failAction, final boolean onProcess,\n            final boolean withUserTask, final boolean onEnter) throws Exception {\n        final Expression outputOfConnectorExpression = new ExpressionBuilder()\n                .createConstantStringExpression(\"outputExpression\");\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithConnector\", \"1.0\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addShortTextData(\"outputOfConnector\", outputOfConnectorExpression);\n\n        // Add a task\n        final ActivityDefinitionBuilder activityDefinitionBuilder;\n        if (withUserTask) {\n            activityDefinitionBuilder = processDefinitionBuilder.addUserTask(\"step1\", ACTOR_NAME);\n        } else {\n            activityDefinitionBuilder = processDefinitionBuilder.addAutomaticTask(\"step1\");\n        }\n\n        // Connector to add on enter or on finish\n        final ConnectorEvent connectorEvent;\n        if (onEnter) {\n            connectorEvent = ConnectorEvent.ON_ENTER;\n        } else {\n            connectorEvent = ConnectorEvent.ON_FINISH;\n        }\n\n        // Add connector on process or task\n        final ConnectorDefinitionBuilder addConnector;\n        if (onProcess) {\n            addConnector = processDefinitionBuilder.addConnector(\"testConnectorThatThrowException\",\n                    \"testConnectorThatThrowException\", \"1.0\", connectorEvent);\n        } else {\n            addConnector = activityDefinitionBuilder.addConnector(\"testConnectorThatThrowException\",\n                    \"testConnectorThatThrowException\", \"1.0\", connectorEvent);\n        }\n        addConnector.addInput(\"kind\", new ExpressionBuilder().createConstantStringExpression(exceptionType));\n        switch (failAction) {\n            case ERROR_EVENT:\n                addConnector.throwErrorEventWhenFailed(\"error\");\n                final SubProcessDefinitionBuilder subProcessBuilder = processDefinitionBuilder\n                        .addSubProcess(\"errorSub\", true).getSubProcessBuilder();\n                subProcessBuilder.addStartEvent(\"errorstart\").addErrorEventTrigger(\"error\");\n                subProcessBuilder.addUserTask(\"errorTask\", ACTOR_NAME);\n                subProcessBuilder.addTransition(\"errorstart\", \"errorTask\");\n                break;\n            case IGNORE:\n                addConnector.ignoreError();\n                break;\n            case FAIL:\n            default:\n                break;\n        }\n\n        return deployProcessWithActorAndTestConnectorThatThrowException(processDefinitionBuilder, ACTOR_NAME, user);\n    }\n\n    private ProcessDefinition getProcessWithConnectorThatThrowErrorOnUserTaskOnEnter(final String exceptionType,\n            final FailAction failAction,\n            final boolean onProcess) throws Exception {\n        return getProcessWithConnectorThatThrowError(exceptionType, failAction, onProcess, true, true);\n    }\n\n    private ProcessDefinition getProcessWithConnectorThatThrowErrorOnAutomaticTaskOnEnter(final String exceptionType,\n            final FailAction failAction,\n            final boolean onProcess) throws Exception {\n        return getProcessWithConnectorThatThrowError(exceptionType, failAction, onProcess, false, true);\n    }\n\n    private ProcessDefinition getProcessWithConnectorThatThrowErrorOnAutomaticTaskOnFinish(final String exceptionType,\n            final FailAction failAction,\n            final boolean onProcess) throws Exception {\n        return getProcessWithConnectorThatThrowError(exceptionType, failAction, onProcess, false, false);\n    }\n\n    @Test\n    public void connectorThatThrowExceptionErrorEventPolicyBoundaryOnTaskShouldAbortCurrentTask() throws Exception {\n        final Expression outputOfConnectorExpression = new ExpressionBuilder()\n                .createConstantStringExpression(\"outputExpression\");\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithConnector\", \"1.0\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addShortTextData(\"outputOfConnector\", outputOfConnectorExpression);\n        final UserTaskDefinitionBuilder userTaskBuilder = processDefinitionBuilder.addUserTask(\"step1\", ACTOR_NAME);\n        final ConnectorDefinitionBuilder addConnector;\n        addConnector = userTaskBuilder.addConnector(\"testConnectorThatThrowException\",\n                \"testConnectorThatThrowException\", \"1.0\", ConnectorEvent.ON_ENTER);\n        addConnector.addInput(\"kind\", new ExpressionBuilder().createConstantStringExpression(\"normal\"));\n        addConnector.throwErrorEventWhenFailed(\"error\");\n        final BoundaryEventDefinitionBuilder boundaryEvent = userTaskBuilder.addBoundaryEvent(\"errorBoundary\", true);\n        boundaryEvent.addErrorEventTrigger(\"error\");\n        processDefinitionBuilder.addUserTask(\"errorTask\", ACTOR_NAME);\n        processDefinitionBuilder.addTransition(\"errorBoundary\", \"errorTask\");\n        final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorThatThrowException(\n                processDefinitionBuilder, ACTOR_NAME, user);\n        try {\n            final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId());\n            // the connector must trigger this exception step\n            waitForUserTask(startProcess, \"errorTask\");\n\n            waitForFlowNodeInState(startProcess, \"step1\", TestStates.ABORTED, false);\n        } finally {\n            // clean up\n            disableAndDeleteProcess(processDefinition);\n        }\n    }\n\n    @Test\n    public void connectorThatThrowExceptionErrorEventPolicyNotCatchOnTask() throws Exception {\n        final Expression outputOfConnectorExpression = new ExpressionBuilder()\n                .createConstantStringExpression(\"outputExpression\");\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithConnector\", \"1.0\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addShortTextData(\"outputOfConnector\", outputOfConnectorExpression);\n        final UserTaskDefinitionBuilder userTaskBuilder = processDefinitionBuilder.addUserTask(\"step1\", ACTOR_NAME);\n        final ConnectorDefinitionBuilder addConnector;\n        addConnector = userTaskBuilder.addConnector(\"testConnectorThatThrowException\",\n                \"testConnectorThatThrowException\", \"1.0\", ConnectorEvent.ON_ENTER);\n        addConnector.addInput(\"kind\", new ExpressionBuilder().createConstantStringExpression(\"normal\"));\n        addConnector.throwErrorEventWhenFailed(\"error\");\n        final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorThatThrowException(\n                processDefinitionBuilder, ACTOR_NAME, user);\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId());\n\n        // the connector must trigger this exception step\n        waitForTaskToFail(startProcess);\n\n        // clean up\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void connectorThatThrowExceptionErrorEventPolicyCallActivityOnTask() throws Exception {\n        // create the process with connector throwing error\n        final Expression outputOfConnectorExpression = new ExpressionBuilder()\n                .createConstantStringExpression(\"outputExpression\");\n        ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithConnector\", \"1.0\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addShortTextData(\"outputOfConnector\", outputOfConnectorExpression).addUserTask(\"step1\",\n                ACTOR_NAME);\n        final UserTaskDefinitionBuilder userTaskBuilder = processDefinitionBuilder.addUserTask(\"step2\", ACTOR_NAME);\n        final ConnectorDefinitionBuilder addConnector = userTaskBuilder.addConnector(\"testConnectorThatThrowException\",\n                \"testConnectorThatThrowException\",\n                \"1.0\", ConnectorEvent.ON_ENTER);\n        addConnector.addInput(\"kind\", new ExpressionBuilder().createConstantStringExpression(\"normal\"));\n        addConnector.throwErrorEventWhenFailed(\"error\");\n        processDefinitionBuilder.addTransition(\"step1\", \"step2\");\n        final ProcessDefinition calledProcess = deployProcessWithActorAndTestConnectorThatThrowException(\n                processDefinitionBuilder, ACTOR_NAME, user);\n\n        // create parent process with call activity and boundary\n        processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance(\"parentProcess\", \"1.0\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        final CallActivityBuilder callActivityBuilder = processDefinitionBuilder.addCallActivity(\"processWithConnector\",\n                new ExpressionBuilder().createConstantStringExpression(\"processWithConnector\"),\n                new ExpressionBuilder().createConstantStringExpression(\"1.0\"));\n        final BoundaryEventDefinitionBuilder boundaryEvent = callActivityBuilder.addBoundaryEvent(\"errorBoundary\",\n                true);\n        boundaryEvent.addErrorEventTrigger(\"error\");\n        processDefinitionBuilder.addUserTask(\"errorTask\", ACTOR_NAME);\n        processDefinitionBuilder.addTransition(\"errorBoundary\", \"errorTask\");\n        final ProcessDefinition callingProcess = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),\n                ACTOR_NAME, user);\n        // start parent\n        final ProcessInstance processInstance = getProcessAPI().startProcess(callingProcess.getId());\n\n        final HumanTaskInstance step1 = waitForUserTaskAndExecuteAndGetIt(\"step1\", user);\n        // the connector must trigger this exception step of the calling process\n        waitForUserTaskAndExecuteIt(processInstance, \"errorTask\", user);\n        waitForProcessToFinish(processInstance);\n        waitForProcessToBeInState(step1.getParentProcessInstanceId(), ProcessInstanceState.ABORTED);\n\n        // clean up\n        disableAndDeleteProcess(callingProcess, calledProcess);\n    }\n\n    @Test\n    public void should_order_connectors_by_execution_order_when_no_filter_are_set() throws Exception {\n        AutomaticTaskDefinitionBuilder automaticTask = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithConnectors\", \" 1.0\")\n                .addAutomaticTask(\"step\");\n        automaticTask.addConnector(\"enter1\", \"connectorDef1\", \"1.0\", ConnectorEvent.ON_ENTER);\n        automaticTask.addConnector(\"enter2\", \"failingConnector\", \"1.0\", ConnectorEvent.ON_ENTER);\n        automaticTask.addConnector(\"enter3\", \"connectorDef1\", \"1.0\", ConnectorEvent.ON_ENTER);\n        automaticTask.addConnector(\"finish1\", \"connectorDef1\", \"1.0\", ConnectorEvent.ON_FINISH);\n        automaticTask.addConnector(\"finish2\", \"connectorDef1\", \"1.0\", ConnectorEvent.ON_FINISH);\n        BusinessArchive bar = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(automaticTask\n                        .getProcess())\n                .addConnectorImplementation(\n                        generateConnectorImplementation(\"connectorDef1\", \"1.0\", DoNothingConnector.class))\n                .addConnectorImplementation(\n                        generateConnectorImplementation(\"failingConnector\", \"1.0\", FailingConnector.class))\n                .done();\n        ProcessDefinition processDefinition = getProcessAPI().deploy(bar);\n        getProcessAPI().enableProcess(processDefinition.getId());\n        ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        FlowNodeInstance failedTask = waitForFlowNodeInFailedState(processInstance, \"step\");\n\n        SearchResult<ConnectorInstance> connectorInstances = getProcessAPI().searchConnectorInstances(\n                new SearchOptionsBuilder(0, 100)\n                        .filter(ConnectorInstancesSearchDescriptor.CONTAINER_ID, failedTask.getId())\n                        .done());\n\n        Assertions.assertThat(connectorInstances.getResult()).extracting(\"name\", \"state\")\n                .containsExactly(\n                        tuple(\"enter1\", ConnectorState.DONE),\n                        tuple(\"enter2\", ConnectorState.FAILED),\n                        tuple(\"enter3\", ConnectorState.TO_BE_EXECUTED),\n                        tuple(\"finish1\", ConnectorState.TO_BE_EXECUTED),\n                        tuple(\"finish2\", ConnectorState.TO_BE_EXECUTED));\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void searchArchivedConnectorInstance() throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"executeConnectorOnActivityInstance\", \"1.0\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder\n                .addConnector(\"myConnectorOnProcess\", CONNECTOR_WITH_OUTPUT_ID, \"1.0\", ConnectorEvent.ON_ENTER)\n                .addInput(CONNECTOR_INPUT_NAME,\n                        new ExpressionBuilder().createConstantStringExpression(\"value1\"));\n        processDefinitionBuilder.addAutomaticTask(\"step1\")\n                .addConnector(\"myConnectorOnStep\", CONNECTOR_WITH_OUTPUT_ID, \"1.0\", ConnectorEvent.ON_ENTER)\n                .addInput(CONNECTOR_INPUT_NAME, new ExpressionBuilder().createConstantStringExpression(\"value1\"));\n        processDefinitionBuilder.addUserTask(\"step2\", ACTOR_NAME);\n        processDefinitionBuilder.addTransition(\"step1\", \"step2\");\n        final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorWithOutput(\n                processDefinitionBuilder, ACTOR_NAME, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long step2Id = waitForUserTask(processInstance, \"step2\");\n        // search with filter on name\n        SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 100);\n        searchOptionsBuilder.filter(ArchiveConnectorInstancesSearchDescriptor.NAME, \"myConnectorOnStep\");\n        searchOptionsBuilder.sort(ArchiveConnectorInstancesSearchDescriptor.NAME, Order.ASC);\n        SearchOptions searchOptions = searchOptionsBuilder.done();\n        SearchResult<ArchivedConnectorInstance> connectorInstances = getProcessAPI()\n                .searchArchivedConnectorInstances(searchOptions);\n        Assertions.assertThat(connectorInstances.getResult()).extracting(\"name\").containsExactly(\"myConnectorOnStep\");\n\n        // finish process\n        assignAndExecuteStep(step2Id, userId);\n        waitForProcessToFinish(processInstance);\n\n        // search for archived connector instances\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 100);\n        searchOptionsBuilder.filter(ArchiveConnectorInstancesSearchDescriptor.CONTAINER_ID, processInstance.getId());\n        searchOptionsBuilder.filter(ArchiveConnectorInstancesSearchDescriptor.CONTAINER_TYPE, PROCESS);\n        searchOptionsBuilder.sort(ArchiveConnectorInstancesSearchDescriptor.NAME, Order.ASC);\n        searchOptions = searchOptionsBuilder.done();\n        connectorInstances = getProcessAPI().searchArchivedConnectorInstances(searchOptions);\n        Assertions.assertThat(connectorInstances.getResult()).extracting(\"name\")\n                .containsExactly(\"myConnectorOnProcess\");\n\n        // search for archived connector instances using SOURCE_OBJECT_ID\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 100);\n        searchOptionsBuilder.filter(ArchiveConnectorInstancesSearchDescriptor.SOURCE_OBJECT_ID,\n                connectorInstances.getResult().get(0).getSourceObjectId());\n        searchOptions = searchOptionsBuilder.done();\n        Assertions.assertThat(getProcessAPI().searchArchivedConnectorInstances(searchOptions).getResult())\n                .extracting(\"name\")\n                .containsOnly(\"myConnectorOnProcess\");\n\n        // now also connector of process is archived\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 100);\n        searchOptionsBuilder.sort(ArchiveConnectorInstancesSearchDescriptor.NAME, Order.ASC);\n        searchOptions = searchOptionsBuilder.done();\n        connectorInstances = getProcessAPI().searchArchivedConnectorInstances(searchOptions);\n        Assertions.assertThat(connectorInstances.getResult()).extracting(\"name\").containsExactly(\"myConnectorOnProcess\",\n                \"myConnectorOnStep\");\n\n        disableAndDeleteProcess(processDefinition);\n\n        // check all archived connectors are deleted\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 100);\n        searchOptions = searchOptionsBuilder.done();\n        connectorInstances = getProcessAPI().searchArchivedConnectorInstances(searchOptions);\n        assertTrue(\"there should be no archived connector anymore\", connectorInstances.getResult().isEmpty());\n    }\n\n    @Test\n    public void connectorWithExternalLibraryInInputAndOutput() throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder();\n        final ProcessDefinitionBuilder pBuilder = processDefinitionBuilder.createNewInstance(\"emptyProcess\",\n                String.valueOf(System.currentTimeMillis()));\n        final UserTaskDefinitionBuilder addUserTask = pBuilder.addActor(ACTOR_NAME).addUserTask(\"step1\", ACTOR_NAME);\n        addUserTask.addData(\"dataActivity\", java.lang.Object.class.getName(),\n                new ExpressionBuilder().createGroovyScriptExpression(\"myScript\",\n                        \"new org.bonitasoft.dfgdfg.Restaurant()\", java.lang.Object.class.getName()));\n        addUserTask\n                .addConnector(\"myConnector1\", \"connectorInJar\", \"1.0.0\", ConnectorEvent.ON_ENTER)\n                .addInput(\"input\",\n                        new ExpressionBuilder().createGroovyScriptExpression(\"myScript\",\n                                \"new org.bonitasoft.dfgdfg.Restaurant()\", Object.class.getName()))\n                .addOutput(\n                        new OperationBuilder().createSetDataOperation(\"dataActivity\",\n                                new ExpressionBuilder().createGroovyScriptExpression(\"myScript\",\n                                        \"new org.bonitasoft.dfgdfg.Restaurant()\", \"org.bonitasoft.dfgdfg.Restaurant\")));\n        final DesignProcessDefinition done = pBuilder.done();\n        final BusinessArchiveBuilder builder = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(done);\n        builder.addConnectorImplementation(\n                getResource(\"/org/bonitasoft/engine/connectors/TestConnectorInJar.impl\", \"TestConnectorInJar.impl\"));\n        builder.addClasspathResource(\n                getResource(\"/org/bonitasoft/engine/connectors/connector-in-jar.jar.bak\", \"connector-in-jar.jar\"));\n        builder.addClasspathResource(getResource(\"/org.bonitasoft.dfgdfg.bak\", \"org.bonitasoft.dfgdfg.jar\"));\n        final BusinessArchive businessArchive = builder.done();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchive, ACTOR_NAME, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance, \"step1\");\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeConnectorOnProcessDefinitionWithOperations() throws Exception {\n        final Expression input1Expression = new ExpressionBuilder().createInputExpression(\"valueOfInput1\",\n                String.class.getName());\n        final Expression input2Expression = new ExpressionBuilder().createInputExpression(\"valueOfInput2\",\n                String.class.getName());\n        final Expression mainExp = new ExpressionBuilder().createExpression(\"param1\",\n                \"'welcome '+valueOfInput1+' and '+valueOfInput2\",\n                ExpressionType.TYPE_READ_ONLY_SCRIPT.toString(), String.class.getName(), \"GROOVY\",\n                Arrays.asList(input1Expression, input2Expression));\n\n        // process with data \"Mett\"\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"executeConnectorOnActivityInstance\", \"1.0\");\n        designProcessDefinition.addActor(ACTOR_NAME);\n        designProcessDefinition.addUserTask(\"step1\", ACTOR_NAME);\n        final ProcessDefinition processDefinition = deployProcessWithExternalTestConnector(designProcessDefinition,\n                ACTOR_NAME, user);\n\n        // execute connector with operations:\n        // connector return param1=\"welcome Lily and Lucy and Mett\"\n        // operations: put \"Jack\" in data valueOfInput3, param1 in \"externalData\" and \"John\" in \"externalDataConst\"\n        // Create Operation map:\n        final List<Operation> operations = new ArrayList<>(2);\n        // set valueOfInput3\n        final Map<String, Serializable> contexts = new HashMap<>();\n        operations.add(new OperationBuilder().createNewInstance()\n                .setLeftOperand(\"externalData\", LeftOperand.TYPE_EXTERNAL_DATA)\n                .setRightOperand(new ExpressionBuilder().createInputExpression(\"param1\", String.class.getName()))\n                .setType(OperatorType.ASSIGNMENT).done());\n        operations.add(new OperationBuilder().createNewInstance()\n                .setLeftOperand(\"externalDataConst\", LeftOperand.TYPE_EXTERNAL_DATA)\n                .setRightOperand(new ExpressionBuilder().createConstantStringExpression(\"John\"))\n                .setType(OperatorType.ASSIGNMENT).done());\n        final Map<String, Expression> connectorInputParameters = getConnectorInputParameters(\"param1\", mainExp);\n        final Map<String, Map<String, Serializable>> inputValues = getInputValues(\"param1\",\n                Arrays.asList(\"valueOfInput1\", \"valueOfInput2\"),\n                Arrays.asList(\"Lily\", \"Lucy\"));\n\n        final Map<String, Serializable> res = getProcessAPI().executeConnectorOnProcessDefinition(\n                ConnectorExecutionIT.DEFAULT_EXTERNAL_CONNECTOR_ID,\n                ConnectorExecutionIT.DEFAULT_EXTERNAL_CONNECTOR_VERSION, connectorInputParameters, inputValues,\n                operations, contexts,\n                processDefinition.getId());\n        assertEquals(\"welcome Lily and Lucy\", res.get(\"externalData\"));\n        assertEquals(\"John\", res.get(\"externalDataConst\"));\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    // The connector output is list to which an element is added when the method connect or disconnect is called.\n    // when the output operations are executed only the method is connect is supposed to be called, that is, the list must contain only one element.\n    public void executeConnectorWithConnectedResouce() throws Exception {\n        final String dataName = \"isConnected\";\n        final String userTaskName = \"step1\";\n        final ProcessDefinition processDefinition = deployProcessWithConnectorWithConnectedResources(dataName,\n                userTaskName);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance, userTaskName);\n\n        final DataInstance dataInstance = getProcessAPI().getProcessDataInstance(dataName, processInstance.getId());\n        assertEquals(1, dataInstance.getValue());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    private ProcessDefinition deployProcessWithConnectorWithConnectedResources(final String dataName,\n            final String userTaskName)\n            throws InvalidExpressionException, BonitaException, IOException {\n        // expression to get the connector output\n        final Expression connectorOutPutExpr = new ExpressionBuilder().createInputExpression(\"output\",\n                List.class.getName());\n        // expression to get the list size\n        final Expression rightSideOperation = new ExpressionBuilder().createJavaMethodCallExpression(\"getOutPut\",\n                \"size\", Integer.class.getName(),\n                connectorOutPutExpr);\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"proc\", \"1.0\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addIntegerData(dataName, null);// data to store the list size\n        processDefinitionBuilder.addUserTask(userTaskName, ACTOR_NAME)\n                .addConnector(\"myConnector\", \"org.bonitasoft.connector.testConnectorWithConnectedResource\", \"1.0\",\n                        ConnectorEvent.ON_ENTER)\n                .addOutput(new OperationBuilder().createSetDataOperation(dataName, rightSideOperation));\n\n        return deployProcessWithActorAndTestConnectorWithConnectedResource(processDefinitionBuilder, ACTOR_NAME, user);\n    }\n\n    @Test\n    public void executeConnectorOnEnterAfterUserFilter() throws Exception {\n        final String dataName = \"taskAssignee\";\n\n        final User jack = createUser(\"jack\", \"bpm\");\n\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithConnector\", \"1.0\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addLongData(dataName, null);\n\n        // expression to get the connector output\n        final Expression connectorOutPutExpr = new ExpressionBuilder().createInputExpression(\n                ExpressionConstants.TASK_ASSIGNEE_ID.getEngineConstantName(),\n                Long.class.getName());\n\n        final UserTaskDefinitionBuilder addUserTask = processDefinitionBuilder.addUserTask(\"step1\", ACTOR_NAME);\n        addUserTask.addConnector(\"myConnector\", \"org.bonitasoft.connector.testConnectorEngineExecutionContext\", \"1.0\",\n                ConnectorEvent.ON_ENTER).addOutput(\n                        new OperationBuilder().createSetDataOperation(dataName, connectorOutPutExpr));\n        addUserTask.addUserFilter(\"test\", \"org.bonitasoft.engine.filter.user.testFilterWithAutoAssign\", \"1.0\").addInput(\n                \"userId\",\n                new ExpressionBuilder().createConstantLongExpression(userId));\n\n        processDefinitionBuilder.addUserTask(\"step2\", ACTOR_NAME);\n        processDefinitionBuilder.addTransition(\"step1\", \"step2\");\n\n        final List<User> userIds = new ArrayList<>();\n        userIds.add(user);\n        userIds.add(jack);\n        final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorEngineExecutionContextAndFilterWithAutoAssign(\n                processDefinitionBuilder, ACTOR_NAME, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(processInstance, \"step1\", jack);\n        waitForUserTask(processInstance, \"step2\");\n\n        final DataInstance processDataInstance = getProcessAPI().getProcessDataInstance(dataName,\n                processInstance.getId());\n        assertEquals(Long.valueOf(userId), processDataInstance.getValue());\n\n        disableAndDeleteProcess(processDefinition);\n        deleteUser(jack);\n    }\n\n    @Test\n    public void getEngineExecutionContext() throws Exception {\n        final String dataName = \"taskAssignee\";\n        final String step1 = \"step1\";\n        final String step2 = \"step2\";\n        final ProcessDefinition processDefinition = deployProcWithConnectorEngineExecContext(dataName, step1, step2);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(processInstance, step1, user);\n        waitForUserTask(processInstance, step2);\n\n        final DataInstance dataInstance = getProcessAPI().getProcessDataInstance(dataName, processInstance.getId());\n        assertEquals(userId, dataInstance.getValue());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    private ProcessDefinition deployProcWithConnectorEngineExecContext(final String dataName, final String step1,\n            final String step2)\n            throws InvalidExpressionException, BonitaException, IOException {\n        // expression to get the connector output\n        final Expression connectorOutPutExpr = new ExpressionBuilder().createInputExpression(\n                ExpressionConstants.TASK_ASSIGNEE_ID.getEngineConstantName(),\n                Long.class.getName());\n\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"proc\", \"1.0\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addLongData(dataName, null);// data to store the connector output\n        processDefinitionBuilder.addUserTask(step1, ACTOR_NAME)\n                .addConnector(\"myConnector\", \"org.bonitasoft.connector.testConnectorEngineExecutionContext\", \"1.0\",\n                        ConnectorEvent.ON_FINISH)\n                .addOutput(new OperationBuilder().createSetDataOperation(dataName, connectorOutPutExpr));\n        processDefinitionBuilder.addUserTask(step2, ACTOR_NAME);\n        processDefinitionBuilder.addTransition(step1, step2);\n\n        return deployProcessWithActorAndTestConnectorEngineExecutionContext(processDefinitionBuilder, ACTOR_NAME, user);\n    }\n\n    @Test\n    public void executeConnectorWithInputExpressionUsingAPI() throws Exception {\n        final String definitionId = \"connectorThatUseAPI\";\n        final String definitionVersion = \"1.0\";\n        final byte[] connectorImplementationFile = BuildTestUtil.buildConnectorImplementationFile(definitionId,\n                definitionVersion, \"impl1\", \"1.0\",\n                TestConnectorWithOutput.class.getName());\n\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"ProcessWithConnector\", \"1.12\");\n        builder.addActor(ACTOR_NAME);\n        builder.addLongData(\"numberOfUser\", new ExpressionBuilder().createConstantLongExpression(-1));\n        final ConnectorDefinitionBuilder connector = builder.addAutomaticTask(\"step1\").addConnector(\"theConnector\",\n                definitionId, definitionVersion,\n                ConnectorEvent.ON_ENTER);\n\n        String script = \"org.bonitasoft.engine.identity.User createUser = apiAccessor.getIdentityAPI().createUser(new org.bonitasoft.engine.identity.UserCreator(\\\"myUser\\\", \\\"password\\\"));\\n\";\n        script += \"apiAccessor.getProcessAPI().attachDocument(processInstanceId, \\\"a\\\", \\\"a\\\", \\\"application/pdf\\\",\\\"test\\\".getBytes());\";\n        script += \"return apiAccessor.getIdentityAPI().getNumberOfUsers();\";\n\n        connector.addInput(\n                CONNECTOR_INPUT_NAME,\n                new ExpressionBuilder().createGroovyScriptExpression(\"script\", script, Long.class.getName(),\n                        new ExpressionBuilder().createEngineConstant(ExpressionConstants.API_ACCESSOR),\n                        new ExpressionBuilder().createEngineConstant(ExpressionConstants.PROCESS_INSTANCE_ID)));\n        connector.addOutput(new OperationBuilder().createSetDataOperation(\"numberOfUser\",\n                new ExpressionBuilder().createInputExpression(CONNECTOR_OUTPUT_NAME, Long.class.getName())));\n        builder.addUserTask(\"step2\", ACTOR_NAME);\n        builder.addTransition(\"step1\", \"step2\");\n\n        final BusinessArchiveBuilder barBuilder = new BusinessArchiveBuilder().createNewBusinessArchive();\n        barBuilder.addConnectorImplementation(new BarResource(\"connector.impl\", connectorImplementationFile));\n        barBuilder.addClasspathResource(\n                new BarResource(\"connector.jar\", IOUtil.generateJar(TestConnectorWithOutput.class)));\n        barBuilder.setProcessDefinition(builder.done());\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(barBuilder.done(), ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance, \"step2\");\n        final DataInstance numberOfUser = getProcessAPI().getProcessDataInstance(\"numberOfUser\",\n                processInstance.getId());\n        assertEquals(2L, numberOfUser.getValue());\n        final SearchResult<Document> documents = getProcessAPI().searchDocuments(\n                new SearchOptionsBuilder(0, 10)\n                        .filter(DocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId()).done());\n        assertEquals(1, documents.getCount());\n        disableAndDeleteProcess(processDefinition);\n        getIdentityAPI().deleteUser(\"myUser\");\n\n    }\n\n    @Test\n    public void getConnectorWithFailureInformationOnConnectorExecution() throws Exception {\n        //given\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithFailingConnector();\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final ActivityInstance failedTask = waitForTaskToFail(processInstance);\n        final SearchResult<ConnectorInstance> searchResult = getProcessAPI().searchConnectorInstances(\n                getFirst100ConnectorInstanceSearchOptions(failedTask.getId(), FLOWNODE).done());\n        Assertions.assertThat(searchResult.getCount()).isEqualTo(1);\n        Assertions.assertThat(searchResult.getResult().get(0).getState()).isEqualTo(ConnectorState.FAILED);\n\n        //when\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.sort(ArchivedFlowNodeInstanceSearchDescriptor.ORIGINAL_FLOW_NODE_ID, Order.ASC);\n        builder.filter(ArchivedFlowNodeInstanceSearchDescriptor.ORIGINAL_FLOW_NODE_ID, failedTask.getId());\n        builder.filter(ArchivedFlowNodeInstanceSearchDescriptor.STATE_NAME, \"executing\");\n        final SearchResult<ArchivedFlowNodeInstance> archivedFlowNodeInstanceSearchResult = getProcessAPI()\n                .searchArchivedFlowNodeInstances(builder.done());\n\n        //then\n        Assertions.assertThat(archivedFlowNodeInstanceSearchResult.getCount()).isEqualTo(1);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    private ProcessDefinition deployAndEnableProcessWithFailingConnector() throws Exception {\n        final Expression normal = new ExpressionBuilder()\n                .createConstantStringExpression(TestConnectorThatThrowException.NORMAL);\n        final Expression defaultValueExpression = new ExpressionBuilder().createConstantStringExpression(\"initial\");\n        final String dataName = \"myVar\";\n\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION);\n        designProcessDefinition.addActor(ACTOR_NAME);\n        designProcessDefinition.addShortTextData(dataName, defaultValueExpression);\n        designProcessDefinition\n                .addAutomaticTask(\"step1\")\n                .addConnector(\"myConnector\", TEST_CONNECTOR_THAT_THROW_EXCEPTION_ID, \"1.0\", ConnectorEvent.ON_ENTER)\n                .addInput(\"kind\", normal)\n                .addOutput(new LeftOperandBuilder().createNewInstance().setName(dataName).done(),\n                        OperatorType.ASSIGNMENT, \"=\", \"\",\n                        new ExpressionBuilder().createInputExpression(\"output1\", String.class.getName()));\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActorAndTestConnectorThatThrowException(\n                designProcessDefinition, ACTOR_NAME,\n                user);\n        return processDefinition;\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithActorAndTestConnectorThatThrowException(\n            final ProcessDefinitionBuilder processDefinitionBuilder,\n            final String actor, final User user) throws BonitaException, IOException {\n        return deployAndEnableProcessWithActorAndConnectorAndParameter(processDefinitionBuilder, actor, user, null,\n                \"TestConnectorThatThrowException.impl\", TestConnectorThatThrowException.class,\n                \"TestConnectorThatThrowException.jar\");\n    }\n\n    @Test\n    public void should_be_able_to_execute_process_connector_without_flownode() throws Exception {\n\n        ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"ProcessWithoutFlownodes\", \"1.0\");\n        processBuilder.addActor(ACTOR_NAME);\n        processBuilder\n                .addConnector(\"myConnector\", \"org.bonitasoft.connector.testConnector\", \"1.0\", ConnectorEvent.ON_ENTER)\n                .addInput(TestConnector.INPUT1,\n                        new ExpressionBuilder().createConstantStringExpression(\"valueOfInput1\"));\n\n        ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector(processBuilder, ACTOR_NAME, user);\n\n        ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        waitForProcessToFinish(processInstance);\n    }\n\n    @Test\n    public void should_put_connector_in_failed_even_when_retryable_exception_happens_while_evaluating_output_operations()\n            throws Exception {\n\n        ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"ProcessWithoutFlownodes\", \"1.0\");\n        processBuilder.addActor(ACTOR_NAME);\n        processBuilder.addData(\"output\", \"java.lang.String\", null);\n        processBuilder\n                .addAutomaticTask(\"task\")\n                .addConnector(\"myConnector\", \"org.bonitasoft.connector.testConnector\", \"1.0\", ConnectorEvent.ON_ENTER)\n                .addInput(TestConnector.INPUT1,\n                        new ExpressionBuilder().createConstantStringExpression(\"valueOfInput1\"))\n                .addOutput(new OperationBuilder().createSetDataOperation(\"output\",\n                        new ExpressionBuilder().createGroovyScriptExpression(\"throw retryable\",\n                                \"throw new org.bonitasoft.engine.commons.exceptions.SRetryableException('a retryable exception')\",\n                                \"java.lang.String\")));\n\n        ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector(processBuilder, ACTOR_NAME, user);\n\n        ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        ActivityInstance failedTask = waitForTaskToFail(processInstance);\n        Assertions.assertThat(getProcessAPI()\n                .searchConnectorInstances(\n                        getFirst100ConnectorInstanceSearchOptions(failedTask.getId(), FLOWNODE).done())\n                .getResult().get(0)).satisfies(connector -> {\n                    Assertions.assertThat(connector.getState()).isEqualTo(ConnectorState.FAILED);\n                });\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/event/AbstractWaitingEventIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.event;\n\nimport static org.junit.Assert.assertEquals;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.assertj.core.api.Assertions;\nimport org.bonitasoft.engine.bpm.flownode.WaitingEvent;\nimport org.bonitasoft.engine.bpm.flownode.WaitingEventSearchDescriptor;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic abstract class AbstractWaitingEventIT extends AbstractEventIT {\n\n    private static final String SEARCH_WAITING_EVENTS_COMMAND = \"searchWaitingEventsCommand\";\n\n    private static final String SEARCH_OPTIONS_KEY = \"searchOptions\";\n\n    protected void checkNumberOfWaitingEvents(final String errorMessage, final String flowNodeName,\n            final long expectedNbOfWaitingEvents)\n            throws BonitaException {\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.filter(WaitingEventSearchDescriptor.FLOW_NODE_NAME, flowNodeName);\n\n        final Map<String, Serializable> parameters = new HashMap<>(1);\n        parameters.put(SEARCH_OPTIONS_KEY, searchOptionsBuilder.done());\n\n        @SuppressWarnings(\"unchecked\")\n        final SearchResult<WaitingEvent> searchResult = (SearchResult<WaitingEvent>) getCommandAPI()\n                .execute(SEARCH_WAITING_EVENTS_COMMAND, parameters);\n        assertEquals(errorMessage, expectedNbOfWaitingEvents, searchResult.getCount());\n    }\n\n    protected void checkNumberOfWaitingEvents(final String flowNodeName, final int expectedNbOfWaitingEvents)\n            throws BonitaException {\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.filter(WaitingEventSearchDescriptor.FLOW_NODE_NAME, flowNodeName);\n\n        final Map<String, Serializable> parameters = new HashMap<>(1);\n        parameters.put(SEARCH_OPTIONS_KEY, searchOptionsBuilder.done());\n\n        @SuppressWarnings(\"unchecked\")\n        final SearchResult<WaitingEvent> searchResult = (SearchResult<WaitingEvent>) getCommandAPI()\n                .execute(SEARCH_WAITING_EVENTS_COMMAND, parameters);\n        Assertions.assertThat(searchResult.getResult()).hasSize(expectedNbOfWaitingEvents);\n    }\n\n    protected void checkNumberOfWaitingEventsInProcess(final String processName, final long expectedNbOfWaitingEvents)\n            throws BonitaException {\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.filter(WaitingEventSearchDescriptor.PROCESS_NAME, processName);\n\n        final Map<String, Serializable> parameters = new HashMap<>(1);\n        parameters.put(SEARCH_OPTIONS_KEY, searchOptionsBuilder.done());\n\n        @SuppressWarnings(\"unchecked\")\n        final SearchResult<WaitingEvent> searchResult = (SearchResult<WaitingEvent>) getCommandAPI()\n                .execute(SEARCH_WAITING_EVENTS_COMMAND, parameters);\n        assertEquals(expectedNbOfWaitingEvents, searchResult.getCount());\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/event/EndEventIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.event;\n\nimport static org.junit.Assert.assertEquals;\n\nimport org.bonitasoft.engine.bpm.process.ActivationState;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.junit.Test;\n\npublic class EndEventIT extends AbstractEventIT {\n\n    @Test\n    public void executeStartAndEndEvents() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process\", \"1.0\")\n                .addStartEvent(\"startEvent\").addAutomaticTask(\"step1\").addEndEvent(\"endEvent\")\n                .addTransition(\"startEvent\", \"step1\")\n                .addTransition(\"step1\", \"endEvent\").getProcess();\n\n        final ProcessDefinition definition = deployAndEnableProcess(designProcessDefinition);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(definition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n        waitForProcessToFinish(processInstance);\n\n        disableAndDeleteProcess(definition);\n    }\n\n    @Test(expected = InvalidProcessDefinitionException.class)\n    public void startEventCannotHaveIncomingTransition() throws Exception {\n        new ProcessDefinitionBuilder().createNewInstance(\"My_Process\", \"1.0\").addStartEvent(\"startEvent\")\n                .addAutomaticTask(\"step1\")\n                .addTransition(\"step1\", \"startEvent\").getProcess();\n    }\n\n    @Test(expected = InvalidProcessDefinitionException.class)\n    public void endEventCannotHaveOutgoingTransition() throws Exception {\n        new ProcessDefinitionBuilder().createNewInstance(\"My_Process\", \"1.0\").addAutomaticTask(\"step1\")\n                .addEndEvent(\"endEvent\")\n                .addTransition(\"endEvent\", \"step1\").getProcess();\n    }\n\n    @Test\n    public void terminateEndEventAlone() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"Proc\", \"1.0\");\n        builder.addEndEvent(\"stop\").addTerminateEventTrigger();\n\n        final ProcessDefinition process = deployAndEnableProcess(builder.done());\n\n        final ProcessInstance startProcess = getProcessAPI().startProcess(process.getId());\n        waitForProcessToFinish(startProcess);\n        disableAndDeleteProcess(process);\n    }\n\n    @Test\n    public void executeStartAndEndEventWithTask() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"executeStartAndEndEventWithTask\", \"1.0\");\n        builder.addStartEvent(\"start\").addAutomaticTask(\"step1\").addEndEvent(\"stop\").addTerminateEventTrigger()\n                .addTransition(\"start\", \"step1\")\n                .addTransition(\"step1\", \"stop\");\n        final ProcessDefinition process = deployAndEnableProcess(builder.done());\n        final ProcessInstance startProcess = getProcessAPI().startProcess(process.getId());\n        waitForProcessToFinish(startProcess);\n        disableAndDeleteProcess(process);\n    }\n\n    @Test\n    public void terminateEndEventWithTasks() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"Proc\", \"1.0\");\n        builder.addActor(ACTOR_NAME);\n        builder.addUserTask(\"step1\", ACTOR_NAME);\n        builder.addEndEvent(\"stop\").addTerminateEventTrigger();\n        builder.addTransition(\"step1\", \"stop\");\n        final ProcessDefinition process = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user);\n        final ProcessInstance startProcess = getProcessAPI().startProcess(process.getId());\n        waitForUserTaskAndExecuteIt(startProcess, \"step1\", user);\n        waitForProcessToFinish(startProcess);\n        disableAndDeleteProcess(process);\n    }\n\n    @Test\n    public void terminateEndEventWithNotFinishedBranch() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"Proc\", \"1.0\");\n        builder.addActor(ACTOR_NAME);\n        builder.addUserTask(\"step1\", ACTOR_NAME);\n        builder.addUserTask(\"step2\", ACTOR_NAME);\n        builder.addEndEvent(\"stop\").addTerminateEventTrigger();\n        builder.addTransition(\"step1\", \"stop\");\n        builder.addTransition(\"step2\", \"stop\");\n        final ProcessDefinition process = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(process.getId());\n        waitForUserTask(processInstance.getId(), \"step2\");\n        waitForUserTaskAndExecuteIt(processInstance, \"step1\", user);\n        // should finish even if we don't execute step2\n        waitForFlowNodeInState(processInstance, \"step2\", TestStates.ABORTED, true);\n        waitForProcessToFinish(processInstance);\n        disableAndDeleteProcess(process);\n    }\n\n    @Test\n    public void terminateEndEvendWithNotFinishedBranch2() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"Proc\", \"1.0\");\n        builder.addActor(ACTOR_NAME);\n        builder.addUserTask(\"step1\", ACTOR_NAME);\n        builder.addUserTask(\"step2\", ACTOR_NAME);\n        builder.addUserTask(\"step3\", ACTOR_NAME);\n        builder.addEndEvent(\"stop\").addTerminateEventTrigger();\n        builder.addTransition(\"step1\", \"stop\");\n        builder.addTransition(\"step2\", \"step3\");\n        builder.addTransition(\"step3\", \"stop\");\n        final ProcessDefinition process = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(process.getId());\n        waitForUserTask(processInstance.getId(), \"step2\");\n        waitForUserTaskAndExecuteIt(processInstance, \"step1\", user);\n        // should finish even if we don't execute step2\n        waitForFlowNodeInState(processInstance, \"step2\", TestStates.ABORTED, true);\n        waitForProcessToFinish(processInstance);\n        disableAndDeleteProcess(process);\n    }\n\n    // @Ignore(\"Currently ignored because it cause timeout lock on data base: need to refactor transactions and so on\")\n    @Test\n    public void terminateEndEvendWithNotFinishedMultipleBranch() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"Proc\", \"1.0\");\n        builder.addActor(ACTOR_NAME);\n        builder.addUserTask(\"step1\", ACTOR_NAME);\n        builder.addEndEvent(\"stop\").addTerminateEventTrigger();\n        builder.addTransition(\"step1\", \"stop\");\n        for (int i = 2; i < 6; i++) {\n            builder.addUserTask(\"step\" + i, ACTOR_NAME);\n            builder.addTransition(\"step\" + i, \"stop\");\n        }\n        final ProcessDefinition process = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user);\n        final ProcessInstance startProcess = getProcessAPI().startProcess(process.getId());\n        waitForUserTask(startProcess, \"step2\");\n        waitForUserTask(startProcess, \"step3\");\n        waitForUserTask(startProcess, \"step4\");\n        waitForUserTask(startProcess, \"step5\");\n        waitForUserTaskAndExecuteIt(startProcess, \"step1\", user);\n        // should finish even if we don't execute step2\n        waitForProcessToFinish(startProcess);\n        disableAndDeleteProcess(process);\n    }\n\n    @Test\n    public void terminateEventWithMultiInstanceParallel() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"terminateEventWithMultiInstance\", \"1.0\");\n        builder.addAutomaticTask(\"step1\").addMultiInstance(false,\n                new ExpressionBuilder().createConstantIntegerExpression(3));\n        builder.addEndEvent(\"stop\").addTerminateEventTrigger().addTransition(\"step1\", \"stop\");\n\n        final ProcessDefinition process = deployAndEnableProcess(builder.done());\n        final ProcessInstance processInstance = getProcessAPI().startProcess(process.getId());\n\n        waitForProcessToFinish(processInstance);\n        disableAndDeleteProcess(process);\n\n    }\n\n    @Test\n    public void terminateEventWithMultiInstanceSequential() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"terminateEventWithMultiInstance\", \"1.0\");\n        builder.addActor(ACTOR_NAME).addUserTask(\"step1\", ACTOR_NAME).addMultiInstance(true,\n                new ExpressionBuilder().createConstantIntegerExpression(3));\n        builder.addEndEvent(\"stop\").addTerminateEventTrigger().addTransition(\"step1\", \"stop\");\n\n        final ProcessDefinition process = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(process.getId());\n\n        for (int i = 0; i < 3; i++) {\n            waitForUserTaskAndExecuteIt(processInstance, \"step1\", user);\n        }\n\n        waitForProcessToFinish(processInstance);\n        disableAndDeleteProcess(process);\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/event/ErrorBoundaryEventIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.event;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.test.BuildTestUtil;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.junit.Test;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ErrorBoundaryEventIT extends AbstractEventIT {\n\n    @Test\n    public void errorBoundaryEventTriggeredNamedError() throws Exception {\n        executionWitherrorEventTriggered(\"error1\");\n    }\n\n    @Test\n    public void errorBoundaryEventTriggeredCatchAllError() throws Exception {\n        executionWitherrorEventTriggered(null);\n    }\n\n    protected void executionWitherrorEventTriggered(final String catchErrorCode) throws Exception {\n        final ProcessDefinition calledProcDef = deployAndEnableProcessWithEndThrowErrorEvent(\"calledProcess\", \"error1\");\n        final ProcessDefinition callerProcDef = deployAndEnableProcessWithBoundaryErrorEventOnCallActivity(\n                \"pErrorBoundary\", \"calledProcess\", \"callStep\",\n                catchErrorCode, \"delivery\");\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(callerProcDef.getId());\n        final FlowNodeInstance callActivity = waitForFlowNodeInExecutingState(processInstance, \"callStep\", false);\n        final ActivityInstance calledStep1 = waitForUserTaskAndGetIt(processInstance, \"calledStep1\");\n        final long calledStep2Id = waitForUserTask(processInstance, \"calledStep2\");\n        final ProcessInstance calledProcessInstance = getProcessAPI()\n                .getProcessInstance(calledStep1.getParentProcessInstanceId());\n        assignAndExecuteStep(calledStep1, user);\n\n        waitForProcessToFinish(calledProcessInstance);\n        try {\n            waitForArchivedActivity(calledStep2Id, TestStates.ABORTED);\n        } catch (final Exception e) {\n            final List<ArchivedActivityInstance> archivedActivityInstances = getProcessAPI()\n                    .getArchivedActivityInstances(processInstance.getId(), 0, 100,\n                            ActivityInstanceCriterion.DEFAULT);\n            System.out.println(\"After completion of the called process\");\n            for (final ArchivedActivityInstance archivedActivityInstance : archivedActivityInstances) {\n                System.out.println(\"name=\" + archivedActivityInstance.getName() + \", state=\"\n                        + archivedActivityInstance.getState() + \", archivedDate=\"\n                        + archivedActivityInstance.getArchiveDate().getTime());\n            }\n            throw new Exception(archivedActivityInstances.toString(), e);\n        }\n        waitForUserTaskAndExecuteIt(processInstance, EXCEPTION_STEP, user);\n\n        waitForProcessToFinish(processInstance);\n        waitForArchivedActivity(callActivity.getId(), TestStates.ABORTED);\n        checkWasntExecuted(processInstance, \"step2\");\n\n        disableAndDeleteProcess(calledProcDef, callerProcDef);\n    }\n\n    @Test\n    public void errorBoundaryEventNotTriggered() throws Exception {\n        final ProcessDefinition calledProcDef = deployAndEnableProcessWithEndThrowErrorEvent(\"calledProcess\", \"error1\");\n        final ProcessDefinition callerProcDef = deployAndEnableProcessWithBoundaryErrorEventOnCallActivity(\n                \"pErrorBoundary\", \"calledProcess\", \"callStep\",\n                \"error1\", \"delivery\");\n\n        try {\n            final ProcessInstance processInstance = getProcessAPI().startProcess(callerProcDef.getId());\n            final ActivityInstance calledStep1 = waitForUserTaskAndGetIt(processInstance, \"calledStep1\");\n            final ProcessInstance calledProcessInstance = getProcessAPI()\n                    .getProcessInstance(calledStep1.getParentProcessInstanceId());\n            waitForUserTaskAndExecuteIt(processInstance, \"calledStep2\", user);\n            waitForFlowNodeInState(processInstance, \"calledStep1\", TestStates.ABORTED, true);\n            waitForProcessToFinish(calledProcessInstance);\n\n            waitForUserTaskAndExecuteIt(processInstance, \"step2\", user);\n            waitForProcessToFinish(processInstance);\n            checkWasntExecuted(processInstance, EXCEPTION_STEP);\n        } finally {\n            disableAndDeleteProcess(calledProcDef, callerProcDef);\n        }\n    }\n\n    @Test\n    public void uncaughtThrowErrorEvent() throws Exception {\n        final ProcessDefinition calledProcDef = deployAndEnableProcessWithEndThrowErrorEvent(\"calledProcess\", \"error1\");\n        // catch a different error\n        final ProcessDefinition callerProcDef = deployAndEnableProcessWithBoundaryErrorEventOnCallActivity(\n                \"pErrorBoundary\", \"calledProcess\", \"callStep\",\n                \"error2\", \"delivery\");\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(callerProcDef.getId());\n        waitForFlowNodeInExecutingState(processInstance, \"callStep\", false);\n        final long calledStep2Id = waitForUserTask(processInstance, \"calledStep2\");\n        waitForUserTaskAndExecuteIt(processInstance, \"calledStep1\", user);\n\n        waitForArchivedActivity(calledStep2Id, TestStates.ABORTED);\n        // if there are no catch error able to handle the thrown error, the throw error event has the same behavior as a terminate event.\n        waitForUserTaskAndExecuteIt(processInstance, \"step2\", user);\n\n        waitForProcessToFinish(processInstance);\n\n        checkWasntExecuted(processInstance, EXCEPTION_STEP);\n\n        disableAndDeleteProcess(calledProcDef, callerProcDef);\n    }\n\n    @Test\n    public void errorEventCaughtAtParentLevel2() throws Exception {\n        final ProcessDefinition procDefLevel0 = deployAndEnableProcessWithEndThrowErrorEvent(\"procDefLevel0\", \"error1\");\n        final ProcessDefinition procDefLevel1 = deployAndEnableProcessWithBoundaryErrorEventOnCallActivity(\n                \"procDefLevel1\", \"procDefLevel0\", \"callStepL1\",\n                \"error2\", \"delivery\");\n        final ProcessDefinition procDefLevel2 = deployAndEnableProcessWithBoundaryErrorEventOnCallActivity(\n                \"procDefLevel2\", \"procDefLevel1\", \"callStepL2\",\n                \"error1\", \"delivery\");\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(procDefLevel2.getId());\n        final FlowNodeInstance callActivityL2 = waitForFlowNodeInExecutingState(processInstance, \"callStepL2\", false);\n        final FlowNodeInstance callActivityL1 = waitForFlowNodeInExecutingState(processInstance, \"callStepL1\", true);\n        final long calledStep2Id = waitForUserTask(processInstance, \"calledStep2\");\n        final ActivityInstance calledStep1 = waitForUserTaskAndGetIt(processInstance, \"calledStep1\");\n\n        final ProcessInstance calledProcessInstanceL0 = getProcessAPI()\n                .getProcessInstance(calledStep1.getParentProcessInstanceId());\n        final ProcessInstance calledProcessInstanceL1 = getProcessAPI()\n                .getProcessInstance(callActivityL1.getParentProcessInstanceId());\n\n        assignAndExecuteStep(calledStep1, user);\n\n        waitForArchivedActivity(calledStep2Id, TestStates.ABORTED);\n        final FlowNodeInstance executionStep = waitForFlowNodeInReadyState(processInstance, EXCEPTION_STEP, false);\n        waitForProcessToFinish(calledProcessInstanceL0);\n        waitForProcessToBeInState(calledProcessInstanceL1, ProcessInstanceState.ABORTED);\n\n        assignAndExecuteStep(executionStep.getId(), user.getId());\n        waitForProcessToFinish(processInstance);\n        waitForArchivedActivity(callActivityL1.getId(), TestStates.ABORTED);\n        waitForArchivedActivity(callActivityL2.getId(), TestStates.ABORTED);\n        checkWasntExecuted(processInstance, \"step2\");\n\n        disableAndDeleteProcess(procDefLevel0, procDefLevel1, procDefLevel2);\n    }\n\n    @Test\n    public void errorEventTwoCatchErrorMatching() throws Exception {\n        final ProcessDefinition procDefLevel0 = deployAndEnableProcessWithEndThrowErrorEvent(\"procDefLevel0\", \"error1\");\n        final ProcessDefinition procDefLevel1 = deployAndEnableProcessWithBoundaryErrorEventOnCallActivity(\n                \"procDefLevel1\", \"procDefLevel0\", \"callStepL1\",\n                \"error1\", \"delivery\");\n        final ProcessDefinition procDefLevel2 = deployAndEnableProcessWithBoundaryErrorEventOnCallActivity(\n                \"procDefLevel2\", \"procDefLevel1\", \"callStepL2\",\n                \"error1\", \"delivery\");\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(procDefLevel2.getId());\n        final FlowNodeInstance callActivityL2 = waitForFlowNodeInExecutingState(processInstance, \"callStepL2\", false);\n        final FlowNodeInstance callActivityL1 = waitForFlowNodeInExecutingState(processInstance, \"callStepL1\", true);\n        final ActivityInstance calledStep1 = waitForUserTaskAndGetIt(processInstance, \"calledStep1\");\n        final long calledStep2Id = waitForUserTask(processInstance, \"calledStep2\");\n        final ProcessInstance calledProcessInstanceL0 = getProcessAPI()\n                .getProcessInstance(calledStep1.getParentProcessInstanceId());\n        final ProcessInstance calledProcessInstanceL1 = getProcessAPI()\n                .getProcessInstance(callActivityL1.getParentProcessInstanceId());\n        assignAndExecuteStep(calledStep1, user.getId());\n\n        waitForArchivedActivity(calledStep2Id, TestStates.ABORTED);\n        final FlowNodeInstance executionStep = waitForFlowNodeInReadyState(calledProcessInstanceL1, EXCEPTION_STEP,\n                false);\n        waitForProcessToFinish(calledProcessInstanceL0);\n\n        assignAndExecuteStep(executionStep.getId(), user.getId());\n        waitForProcessToFinish(calledProcessInstanceL1);\n\n        waitForUserTaskAndExecuteIt(processInstance, \"step2\", user);\n        waitForProcessToFinish(processInstance);\n\n        waitForArchivedActivity(callActivityL1.getId(), TestStates.ABORTED);\n        waitForArchivedActivity(callActivityL2.getId(), TestStates.NORMAL_FINAL);\n        checkWasntExecuted(calledProcessInstanceL1, \"step2\");\n\n        disableAndDeleteProcess(procDefLevel0, procDefLevel1, procDefLevel2);\n    }\n\n    @Test\n    public void errorCodeThrownBySubProcessShouldBeCatchByMainProcess() throws Exception {\n        final ProcessDefinition subProcess = deployAndEnableSubProcessWhichThrowsAnErrorEvent(\"SubProcess\", \"Mistake\");\n        final ProcessDefinition midProcess = deployAndEnableMidProcessWhichContainsACallActivity(\"MidProcess\",\n                \"SubProcess\");\n        final ProcessDefinition mainProcess = deployAndEnableProcessWithBoundaryErrorEventOnMICallActivity(\"Process\",\n                \"MidProcess\", \"Mistake\", \"acme\");\n\n        final ProcessInstance instance = getProcessAPI().startProcess(mainProcess.getId());\n        waitForFlowNodeInReadyState(instance, EXCEPTION_STEP, true);\n        waitForFlowNodeInState(instance, \"step1\", TestStates.ABORTED, false);\n\n        disableAndDeleteProcess(mainProcess, midProcess, subProcess);\n    }\n\n    @Test\n    public void processWithMIUserTaskWithErrorEvent_should_take_the_error_flow() throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = BuildTestUtil\n                .buildProcessDefinitionWithMultiInstanceUserTaskAndFailedConnector(PROCESS_NAME,\n                        \"step1\");\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithTestConnectorThatThrowException(\n                processDefinitionBuilder);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(processInstance, \"step1\", user);\n        waitForUserTaskAndExecuteIt(processInstance, \"errorFlow\", user);\n        waitForProcessToFinish(processInstance);\n\n        disableAndDeleteProcess(processDefinition.getId());\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/event/ErrorEventSubProcessIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.event;\n\nimport static org.junit.Assert.assertEquals;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.data.DataInstance;\nimport org.bonitasoft.engine.bpm.data.DataNotFoundException;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.expression.ExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.InvalidExpressionException;\nimport org.bonitasoft.engine.test.BuildTestUtil;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Baptiste Mesta\n * @author Elias Ricken de Medeiros\n */\npublic class ErrorEventSubProcessIT extends AbstractWaitingEventIT {\n\n    private List<ProcessDefinition> processDefinitions;\n\n    @Override\n    @Before\n    public void before() throws Exception {\n        super.before();\n        processDefinitions = new ArrayList<>();\n    }\n\n    @Override\n    @After\n    public void after() throws Exception {\n        disableAndDeleteProcess(processDefinitions);\n        super.after();\n    }\n\n    @Test\n    public void errorEventSubProcessTriggeredNamedError() throws Exception {\n        executeProcessTriggeringEventSubProcess(\"e1\", \"e1\");\n    }\n\n    @Test\n    public void errorEventSubProcessTriggeredCatchAllErrors() throws Exception {\n        executeProcessTriggeringEventSubProcess(null, \"e1\");\n    }\n\n    private void executeProcessTriggeringEventSubProcess(final String catchErrorCode, final String throwErrorCode)\n            throws Exception {\n        final String subProcStartEventName = \"errorStart\";\n        processDefinitions.add(\n                deployAndEnableProcessWithErrorEventSubProcess(catchErrorCode, throwErrorCode, subProcStartEventName));\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinitions.get(0).getId());\n        waitForUserTask(processInstance.getId(), \"step1\");\n        final long step2Id = waitForUserTask(processInstance, \"step2\");\n        List<ActivityInstance> activities = getProcessAPI().getActivities(processInstance.getId(), 0, 10);\n        assertEquals(2, activities.size());\n        checkNumberOfWaitingEvents(subProcStartEventName, 1);\n\n        // throw error\n        assignAndExecuteStep(step2Id, user);\n        waitForArchivedActivity(step2Id, TestStates.NORMAL_FINAL);\n\n        waitForFlowNodeInExecutingState(processInstance, BuildTestUtil.EVENT_SUB_PROCESS_NAME, false);\n        final ActivityInstance subStep = waitForUserTaskAndGetIt(processInstance, \"subStep\");\n        final ProcessInstance subProcInst = getProcessAPI().getProcessInstance(subStep.getParentProcessInstanceId());\n\n        activities = getProcessAPI().getActivities(processInstance.getId(), 0, 10);\n        assertEquals(2, activities.size());\n        // the parent process instance is supposed to be aborted, so no more waiting events are expected\n        checkNumberOfWaitingEvents(subProcStartEventName, 0);\n\n        assignAndExecuteStep(subStep, user);\n\n        waitForProcessToFinish(subProcInst);\n        waitForProcessToBeInState(processInstance, ProcessInstanceState.ABORTED);\n\n        // check that the transition wasn't taken\n        checkWasntExecuted(processInstance, \"end\");\n    }\n\n    @Test\n    public void errorEventSubProcessNotTriggered() throws Exception {\n        final String subProcStartEventName = \"errorStart\";\n        processDefinitions\n                .add(deployAndEnableProcessWithErrorEventSubProcess(\"error 1\", \"error 1\", subProcStartEventName));\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinitions.get(0).getId());\n        final long step1Id = waitForUserTask(processInstance.getId(), \"step1\");\n        final long step2Id = waitForUserTask(processInstance.getId(), \"step2\");\n        getProcessAPI().getProcessDataInstance(\"throwException\", processInstance.getId());\n        getProcessAPI().updateProcessDataInstance(\"throwException\", processInstance.getId(), false);\n        final List<ActivityInstance> activities = getProcessAPI().getActivities(processInstance.getId(), 0, 10);\n        assertEquals(2, activities.size());\n        checkNumberOfWaitingEvents(subProcStartEventName, 1);\n\n        assignAndExecuteStep(step1Id, user);\n        assignAndExecuteStep(step2Id, user);\n\n        waitForArchivedActivity(step1Id, TestStates.NORMAL_FINAL);\n        waitForProcessToFinish(processInstance);\n\n        // the parent process instance has completed, so no more waiting events are expected\n        checkNumberOfWaitingEvents(subProcStartEventName, 0);\n    }\n\n    @Test\n    public void createSeveralInstances() throws Exception {\n        processDefinitions.add(deployAndEnableProcessWithErrorEventSubProcess(\"e2\", \"e2\", \"errorStart\"));\n        final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinitions.get(0).getId());\n        final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinitions.get(0).getId());\n\n        // throw error\n        waitForUserTask(processInstance1.getId(), \"step1\");\n        waitForUserTaskAndExecuteIt(processInstance1, \"step2\", user);\n\n        waitForUserTask(processInstance2.getId(), \"step1\");\n        waitForUserTaskAndExecuteIt(processInstance2, \"step2\", user);\n\n        waitForUserTask(processInstance1.getId(), \"subStep\");\n        waitForUserTask(processInstance2.getId(), \"subStep\");\n    }\n\n    @Test\n    public void subProcessCanAccessParentData() throws Exception {\n        processDefinitions.add(deployAndEnableProcessWithErrorEventSubProcessAndData(\"error1\", \"error1\", \"errorStart\"));\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinitions.get(0).getId());\n        waitForUserTask(processInstance.getId(), \"step1\");\n        waitForUserTaskAndExecuteIt(processInstance, \"step2\", user);\n\n        final ActivityInstance subStep = waitForUserTaskAndGetIt(processInstance, \"subStep\");\n        final ProcessInstance subProcInst = getProcessAPI().getProcessInstance(subStep.getParentProcessInstanceId());\n        checkRetrieveDataInstances(processInstance, subStep, subProcInst);\n        checkEvaluateExpression(subStep, \"count\", Integer.class, 1);\n\n        assignAndExecuteStep(subStep, user);\n        waitForProcessToFinish(subProcInst);\n        waitForProcessToBeInState(processInstance, ProcessInstanceState.ABORTED);\n    }\n\n    @Test\n    public void subProcessCanAccessParentDataEvenIfItDoesntHaveLocalData() throws Exception {\n        final String rootUserTaskName = \"step1\";\n        final String subProcUserTaskName = \"subStep\";\n        final String dataName = \"content\";\n        final String dataValue = \"default\";\n        processDefinitions.add(deployAndEnableProcessWithErrorEventSubProcessAndDataOnlyInRoot(\"error1\", \"errorStart\",\n                rootUserTaskName,\n                subProcUserTaskName, dataName, dataValue));\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinitions.get(0).getId());\n        waitForUserTaskAndExecuteIt(processInstance, rootUserTaskName, user);\n\n        final ActivityInstance subStep = waitForUserTaskAndGetIt(processInstance, subProcUserTaskName);\n        final ProcessInstance subProcInst = getProcessAPI().getProcessInstance(subStep.getParentProcessInstanceId());\n        checkProcessDataInstance(dataName, subProcInst.getId(), dataValue);\n        checkProcessDataInstance(dataName, processInstance.getId(), dataValue);\n\n        checkEvaluateExpression(subStep, dataName, String.class, dataValue);\n\n        assignAndExecuteStep(subStep, user);\n        waitForProcessToFinish(subProcInst);\n        waitForProcessToBeInState(processInstance, ProcessInstanceState.ABORTED);\n    }\n\n    @Test\n    public void eventSubProcessWithDataAndRootProcessWithNoData() throws Exception {\n        final String rootUserTaskName = \"step1\";\n        final String subProcUserTaskName = \"subStep\";\n        final String dataName = \"content\";\n        final String dataValue = \"default\";\n        processDefinitions.add(deployAndEnableProcessWithErrorEventSubProcessAndDataOnlyInSubProc(\"error1\",\n                \"errorStart\", rootUserTaskName,\n                subProcUserTaskName, dataName, dataValue));\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinitions.get(0).getId());\n        waitForUserTaskAndExecuteIt(processInstance, rootUserTaskName, user);\n\n        final ActivityInstance subStep = waitForUserTaskAndGetIt(processInstance, subProcUserTaskName);\n        final ProcessInstance subProcInst = getProcessAPI().getProcessInstance(subStep.getParentProcessInstanceId());\n        checkProcessDataInstance(dataName, subProcInst.getId(), dataValue);\n\n        checkEvaluateExpression(subStep, dataName, String.class, dataValue);\n\n        assignAndExecuteStep(subStep, user);\n        waitForProcessToFinish(subProcInst);\n        waitForProcessToBeInState(processInstance, ProcessInstanceState.ABORTED);\n    }\n\n    private void checkEvaluateExpression(final ActivityInstance subStep, final String dataName,\n            final Class<?> expressionType, final Serializable expectedValue)\n            throws InvalidExpressionException, ExpressionEvaluationException {\n        final Map<Expression, Map<String, Serializable>> expressions = new HashMap<>(1);\n        final Expression contVarExpr = new ExpressionBuilder().createDataExpression(dataName, expressionType.getName());\n        expressions.put(contVarExpr, null);\n\n        final Map<String, Serializable> expressionResults = getProcessAPI()\n                .evaluateExpressionsOnActivityInstance(subStep.getId(), expressions);\n        assertEquals(expectedValue, expressionResults.get(contVarExpr.getName()));\n    }\n\n    private void checkRetrieveDataInstances(final ProcessInstance processInstance, final ActivityInstance subStep,\n            final ProcessInstance subProcInst)\n            throws DataNotFoundException {\n        checkProcessDataInstance(\"count\", subProcInst.getId(), 1);\n        checkProcessDataInstance(\"content\", subProcInst.getId(), \"childVar\");\n        checkProcessDataInstance(\"value\", subProcInst.getId(), 10.0);\n        checkProcessDataInstance(\"content\", processInstance.getId(), \"parentVar\");\n        checkActivityDataInstance(\"content\", subStep.getId(), \"childActivityVar\");\n    }\n\n    private void checkProcessDataInstance(final String dataName, final long processInstanceId,\n            final Serializable expectedValue) throws DataNotFoundException {\n        final DataInstance processDataInstance = getProcessAPI().getProcessDataInstance(dataName, processInstanceId);\n        assertEquals(expectedValue, processDataInstance.getValue());\n    }\n\n    private void checkActivityDataInstance(final String dataName, final long activityInstanceId,\n            final Serializable expectedValue)\n            throws DataNotFoundException {\n        final DataInstance activityDataInstance = getProcessAPI().getActivityDataInstance(dataName, activityInstanceId);\n        assertEquals(expectedValue, activityDataInstance.getValue());\n    }\n\n    @Test\n    public void errorEventSubProcInsideTargetCallActivity() throws Exception {\n        processDefinitions.add(deployAndEnableProcessWithErrorEventSubProcess(\"e1\", \"e1\", \"errorStart\"));\n        processDefinitions.add(deployAndEnableProcessWithCallActivity(processDefinitions.get(0).getName(),\n                processDefinitions.get(0).getVersion()));\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinitions.get(1).getId());\n        final ActivityInstance step1 = waitForUserTaskAndGetIt(processInstance, \"step1\");\n        waitForUserTaskAndExecuteIt(processInstance, \"step2\", user);\n\n        final ActivityInstance subStep = waitForUserTaskAndGetIt(processInstance, \"subStep\");\n        final ProcessInstance calledProcInst = getProcessAPI().getProcessInstance(step1.getParentProcessInstanceId());\n        final ProcessInstance subProcInst = getProcessAPI().getProcessInstance(subStep.getParentProcessInstanceId());\n\n        waitForArchivedActivity(step1.getId(), TestStates.ABORTED);\n        assignAndExecuteStep(subStep, user);\n        waitForProcessToFinish(subProcInst);\n        waitForProcessToBeInState(calledProcInst, ProcessInstanceState.ABORTED);\n\n        waitForUserTaskAndExecuteIt(processInstance, \"step2\", user);\n        waitForProcessToFinish(processInstance);\n    }\n\n    @Test\n    public void processWithErrorEventSubProcAndCallActivity_must_be_finished_when_subProcess_is_finished()\n            throws Exception {\n        // Create the target process\n        processDefinitions.add(deployAndEnableProcessWithTestConnectorThatThrowException(BuildTestUtil\n                .buildProcessDefinitionWithUserTaskAndFailedConnector(PROCESS_NAME)));\n\n        // Create the caller process\n        final Expression targetProcessExpr = new ExpressionBuilder()\n                .createConstantStringExpression(BuildTestUtil.PROCESS_NAME);\n        final Expression targetVersionExpr = new ExpressionBuilder()\n                .createConstantStringExpression(BuildTestUtil.PROCESS_VERSION);\n\n        final ProcessDefinitionBuilder builder = BuildTestUtil.buildProcessDefinitionWithCallActivity(\n                \"ProcessWithEventSubProcessAndCallActivity\",\n                targetProcessExpr, targetVersionExpr);\n        BuildTestUtil.buildErrorEventSubProcessWithUserTask(\"SubStep\", builder);\n        processDefinitions.add(deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user));\n\n        // Start the caller process\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinitions.get(1).getId());\n        final HumanTaskInstance stepBeforeFailedConnector = waitForUserTaskAndExecuteAndGetIt(\n                \"StepBeforeFailedConnector\", user);\n        final ActivityInstance subStep = waitForUserTaskAndExecuteAndGetIt(\"SubStep\", user);\n\n        waitForProcessToFinish(subStep.getParentProcessInstanceId());\n        waitForProcessToBeInState(processInstance, ProcessInstanceState.ABORTED);\n        waitForProcessToBeInState(stepBeforeFailedConnector.getParentProcessInstanceId(), ProcessInstanceState.ABORTED);\n    }\n\n    @Test\n    public void should_catch_error_in_event_subprocess_when_process_is_disabled_and_then_cancel_it() throws Exception {\n        //given: a process with error event subprocess catching an error\n        ProcessDefinition parent = deployAndEnableProcessWithActor(new ProcessDefinitionBuilder()\n                .createNewInstance(\"Parent process with error event subprocess\", \"1.0\")\n                .addActor(\"actor\", true)\n                .addStartEvent(\"start\")\n                .addCallActivity(\"call\", new ExpressionBuilder().createConstantStringExpression(\"sendError\"),\n                        new ExpressionBuilder().createConstantStringExpression(\"1.0\"))\n                .addTransition(\"start\", \"call\")\n                .addSubProcess(\"compensateEventSubProcess\", true).getSubProcessBuilder()\n                .addStartEvent(\"error\").addErrorEventTrigger(\"theError\")\n                .addUserTask(\"eventSubProcessTask\", \"actor\")\n                .addTransition(\"error\", \"eventSubProcessTask\").getProcess(), \"actor\", user);\n\n        // a called process sending an error\n        ProcessDefinition child = deployAndEnableProcessWithActor(\n                new ProcessDefinitionBuilder().createNewInstance(\"sendError\", \"1.0\")\n                        .addActor(\"actor\", true)\n                        .addStartEvent(\"start\")\n                        .addUserTask(\"task\", \"actor\")\n                        .addEndEvent(\"sendError\").addErrorEventTrigger(\"theError\")\n                        .addTransition(\"start\", \"task\")\n                        .addTransition(\"task\", \"sendError\").getProcess(),\n                \"actor\", user);\n\n        processDefinitions.add(parent);\n        processDefinitions.add(child);\n\n        ProcessInstance processInstance = getProcessAPI().startProcess(parent.getId());\n\n        // we wait for the task in the called process\n        long task = waitForUserTask(\"task\");\n\n        //when: we disable the parent process and execute the sub process that sends the error\n        getProcessAPI().disableProcess(parent.getId());\n        getProcessAPI().assignAndExecuteUserTask(user.getId(), task, Collections.emptyMap());\n\n        //then: the error event sub process should be triggered\n        waitForUserTask(\"eventSubProcessTask\");\n\n        // then: we should be able to cancel that instance\n        getProcessAPI().cancelProcessInstance(processInstance.getId());\n\n        waitForProcessToBeInState(processInstance, ProcessInstanceState.CANCELLED);\n\n        // to allow proper clean-up:\n        getProcessAPI().enableProcess(parent.getId());\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/event/EventTriggerIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.event;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\nimport java.time.Duration;\nimport java.time.LocalDateTime;\nimport java.time.ZoneId;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.flownode.TimerEventTriggerInstance;\nimport org.bonitasoft.engine.bpm.flownode.TimerEventTriggerInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.junit.Test;\n\n/**\n * @author Celine Souchet\n * @version 6.4.0\n * @since 6.4.0\n */\npublic class EventTriggerIT extends AbstractEventIT {\n\n    @Test\n    public void searchTimerEventTriggerInstances() throws Exception {\n        final ProcessDefinition process1 = deployAndEnableSimpleProcess(\"Toto\", \"moi\");\n        final ProcessDefinition process2 = deployAndEnableProcessWithBoundaryTimerEventOnCallActivity(7200000L, false,\n                \"Toto\");\n        final ProcessDefinition process3 = deployAndEnableProcessWithMessageEventSubProcess();\n        final ProcessDefinition process4 = deployAndEnableProcessWithBoundarySignalEvent(\"signal\");\n\n        final ProcessInstance processInstance2 = getProcessAPI().startProcess(process2.getId());\n        final ProcessInstance processInstance3 = getProcessAPI().startProcess(process3.getId());\n        final ProcessInstance processInstance4 = getProcessAPI().startProcess(process4.getId());\n\n        waitForFlowNodeInState(processInstance2, \"timer\", TestStates.WAITING, true);\n        waitForUserTask(processInstance3, PARENT_PROCESS_USER_TASK_NAME);\n        waitForUserTask(processInstance4, \"step1\");\n\n        // Return only timer event trigger\n        SearchOptions options = new SearchOptionsBuilder(0, 10).done();\n        SearchResult<TimerEventTriggerInstance> searchTimerEventTriggerInstances = getProcessAPI()\n                .searchTimerEventTriggerInstances(processInstance3.getId(),\n                        options);\n        assertEquals(0, searchTimerEventTriggerInstances.getCount());\n        assertTrue(searchTimerEventTriggerInstances.getResult().isEmpty());\n\n        searchTimerEventTriggerInstances = getProcessAPI().searchTimerEventTriggerInstances(processInstance4.getId(),\n                options);\n        assertEquals(0, searchTimerEventTriggerInstances.getCount());\n        assertTrue(searchTimerEventTriggerInstances.getResult().isEmpty());\n\n        searchTimerEventTriggerInstances = getProcessAPI().searchTimerEventTriggerInstances(processInstance2.getId(),\n                options);\n        assertEquals(2, searchTimerEventTriggerInstances.getCount());\n\n        options = new SearchOptionsBuilder(0, 10)\n                .filter(TimerEventTriggerInstanceSearchDescriptor.EVENT_INSTANCE_NAME, \"timer\").done();\n        searchTimerEventTriggerInstances = getProcessAPI().searchTimerEventTriggerInstances(processInstance2.getId(),\n                options);\n        assertEquals(1, searchTimerEventTriggerInstances.getCount());\n        final List<TimerEventTriggerInstance> result = searchTimerEventTriggerInstances.getResult();\n        assertEquals(1, result.size());\n        assertEquals(\"timer\", result.get(0).getEventInstanceName());\n\n        disableAndDeleteProcess(process2, process1, process3, process4);\n    }\n\n    @Test\n    public void updateTimerEventTriggerInstance() throws Exception {\n        final ProcessDefinition process1 = deployAndEnableSimpleProcess(\"Toto2\", \"moi\");\n        final ProcessDefinition process2 = deployAndEnableProcessWithBoundaryTimerEventOnCallActivity(7200000L, false,\n                \"Toto2\");\n        final ProcessInstance processInstance2 = getProcessAPI().startProcess(process2.getId());\n        try {\n            waitForFlowNodeInState(processInstance2, \"timer\", TestStates.WAITING, true);\n\n            final SearchOptions options = new SearchOptionsBuilder(0, 10).done();\n            final List<TimerEventTriggerInstance> result = getProcessAPI()\n                    .searchTimerEventTriggerInstances(processInstance2.getId(), options).getResult();\n            assertThat(result).hasSize(2);\n\n            final Date date = new Date();\n            final Date newDate = getProcessAPI().updateExecutionDateOfTimerEventTriggerInstance(result.get(0).getId(),\n                    date);\n            assertTrue(newDate.equals(date) || newDate.after(date));\n\n            waitForUserTask(processInstance2, EXCEPTION_STEP);\n\n            // When using optimisation, we notify quartz of a change in triggers and help it trigger any change in triggers quickly.\n            // Without optimisations, it takes around 30 seconds\n            assertThat(Duration.between(LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()),\n                    LocalDateTime.now(ZoneId.systemDefault())))\n                    .isLessThanOrEqualTo(Duration.ofSeconds(10));\n            assertThat(getProcessAPI().searchTimerEventTriggerInstances(processInstance2.getId(), options).getResult())\n                    .hasSize(1);\n\n        } finally {\n            disableAndDeleteProcess(process2, process1);\n        }\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/event/InterruptingTimerBoundaryEventIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.event;\n\nimport static org.junit.Assert.assertEquals;\n\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.TimerType;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.junit.Test;\n\npublic class InterruptingTimerBoundaryEventIT extends AbstractEventIT {\n\n    @Test\n    public void timerBoundaryEventTriggered() throws Exception {\n        final int timerDuration = 1000;\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryTimerEvent(timerDuration, true,\n                \"step1\", \"exceptionStep\", \"step2\");\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance.getId(), \"step1\");\n\n        // wait timer trigger\n        waitForUserTaskAndExecuteIt(processInstance, \"exceptionStep\", user);\n        waitForFlowNodeInState(processInstance, \"step1\", TestStates.ABORTED, true);\n        waitForProcessToFinish(processInstance);\n\n        checkFlowNodeWasntExecuted(processInstance.getId(), \"step2\");\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void timerBoundaryEventWithScriptThatFail() throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"pTimerBoundary\", \"2.0\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addStartEvent(\"start\");\n        final UserTaskDefinitionBuilder userTaskDefinitionBuilder = processDefinitionBuilder.addUserTask(\"step1\",\n                ACTOR_NAME);\n        userTaskDefinitionBuilder.addBoundaryEvent(\"timer\", true).addTimerEventTriggerDefinition(TimerType.DURATION,\n                new ExpressionBuilder().createGroovyScriptExpression(\"script\", \"throw new java.lang.RuntimeException()\",\n                        Long.class.getName()));\n        processDefinitionBuilder.addAutomaticTask(\"timerStep\");\n        processDefinitionBuilder.addTransition(\"timer\", \"timerStep\");\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),\n                ACTOR_NAME, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForFlowNodeInFailedState(processInstance, \"step1\");\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void timerBoundaryEventTriggeredOnCallActivity() throws Exception {\n        final int timerDuration = 2000;\n        final String simpleProcessName = \"targetProcess\";\n        final String simpleTaskName = \"stepCA\";\n\n        // deploy a simple process p1\n        final ProcessDefinition targetProcessDefinition = deployAndEnableSimpleProcess(simpleProcessName,\n                simpleTaskName);\n\n        // deploy a process, p2, with a call activity calling p1. The call activity has an interrupting timer boundary event\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryTimerEventOnCallActivity(\n                timerDuration, true, simpleProcessName);\n\n        // start the root process and wait for boundary event trigger\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long stepCAId = waitForUserTask(processInstance, simpleTaskName);\n        // wait timer trigger\n        // check that the exception flow was taken\n        waitForUserTaskAndExecuteIt(processInstance, \"exceptionStep\", user);\n        waitForProcessToFinish(processInstance);\n\n        final ArchivedActivityInstance archActivityInst = getProcessAPI().getArchivedActivityInstance(stepCAId);\n        assertEquals(TestStates.ABORTED.getStateName(), archActivityInst.getState());\n\n        checkFlowNodeWasntExecuted(processInstance.getId(), PARENT_PROCESS_USER_TASK_NAME);\n\n        disableAndDeleteProcess(processDefinition);\n        disableAndDeleteProcess(targetProcessDefinition);\n    }\n\n    @Test\n    public void timerBoundaryEventTriggeredOnSequentialMultiInstance() throws Exception {\n        // deploy a process with a interrupting timer boundary event attached to a sequential multi-instance\n        final int timerDuration = 1000;\n        final String multiTaskName = \"step1\";\n        final ProcessDefinition processDefinition = deployAndEnableProcessMultiInstanceWithBoundaryEvent(timerDuration,\n                true, multiTaskName, 4, true, \"step2\",\n                \"exceptionStep\");\n\n        // start the process and wait the timer to trigger\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance.getId(), multiTaskName);\n        // wait timer trigger\n        // check that the exception flow was taken\n        waitForUserTaskAndExecuteIt(processInstance, \"exceptionStep\", user);\n        waitForFlowNodeInState(processInstance, multiTaskName, TestStates.ABORTED, true);\n        waitForProcessToFinish(processInstance);\n\n        checkFlowNodeWasntExecuted(processInstance.getId(), \"step2\");\n\n        disableAndDeleteProcess(processDefinition.getId());\n    }\n\n    @Test\n    public void timerBoundaryEventTriggeredOnParallelMultiInstance() throws Exception {\n        final int timerDuration = 1000;\n        final int loopCardinality = 4;\n        final boolean isSequential = false;\n\n        // deploy a process with a interrupting timer boundary event attached to a parallel multi-instance\n        final ProcessDefinition processDefinition = deployAndEnableProcessMultiInstanceWithBoundaryEvent(timerDuration,\n                true, \"step1\", loopCardinality,\n                isSequential, \"step2\", \"exceptionStep\");\n\n        // start the process and wait for process to be triggered\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n        // wait timer trigger\n        waitForUserTaskAndExecuteIt(processInstance, \"exceptionStep\", user);\n        waitForProcessToFinish(processInstance);\n\n        final ArchivedActivityInstance archActivityInst = getProcessAPI().getArchivedActivityInstance(step1Id);\n        assertEquals(TestStates.ABORTED.getStateName(), archActivityInst.getState());\n\n        checkFlowNodeWasntExecuted(processInstance.getId(), \"step2\");\n\n        disableAndDeleteProcess(processDefinition.getId());\n    }\n\n    @Test\n    public void timerBoundaryEventTriggeredOnLoopActivity() throws Exception {\n        final long timerDuration = 1000;\n        final int loopMax = 2;\n        final String loopActivityName = \"step1\";\n        final String normalFlowStepName = \"step2\";\n        final String exceptionFlowStepName = \"exceptionStep\";\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryTimerEventOnLoopActivity(\n                timerDuration, true, loopMax, loopActivityName,\n                normalFlowStepName, exceptionFlowStepName);\n\n        // start the process and wait timer to trigger\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance.getId(), loopActivityName);\n        Thread.sleep(timerDuration); // wait timer trigger\n\n        // verify that the exception flow was taken\n        waitForUserTaskAndExecuteIt(processInstance, exceptionFlowStepName, user);\n        // verify that the normal flow was aborted\n        waitForFlowNodeInState(processInstance, loopActivityName, TestStates.ABORTED, true);\n        waitForProcessToFinish(processInstance);\n\n        checkFlowNodeWasntExecuted(processInstance.getId(), normalFlowStepName);\n\n        disableAndDeleteProcess(processDefinition.getId());\n    }\n\n    @Test\n    public void timerBoundaryEventTriggeredAndLongData() throws Exception {\n        final int timerDuration = 1000;\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryTimerEventOnHumanTask(\n                timerDuration, true);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        // Wait and execute the step1 with a timer boundary event\n        waitForUserTaskAndAssignIt(processInstance, \"step1\", user);\n\n        // wait timer trigger\n        waitForUserTaskAndExecuteIt(processInstance, \"exceptionStep\", user);\n        waitForFlowNodeInState(processInstance, \"step1\", TestStates.ABORTED, true);\n        waitForProcessToFinish(processInstance);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/event/MessageBoundaryEventIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.event;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.awaitility.Awaitility.await;\nimport static org.bonitasoft.engine.util.AssertionsUtils.assertNoErrorAfterXAttemps;\nimport static org.hamcrest.Matchers.hasSize;\nimport static org.junit.Assert.assertEquals;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceCriterion;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.junit.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class MessageBoundaryEventIT extends AbstractEventIT {\n\n    private static final Logger logger = LoggerFactory.getLogger(MessageBoundaryEventIT.class);\n\n    @Test\n    public void messageBoundaryEventTriggered() throws Exception {\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryMessageEvent(\"MyMessage\");\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(processInstance.getId(), \"step1\");\n\n        // Check that boundary event has been created:\n        long otherBoundaries = getProcessAPI()\n                .searchFlowNodeInstances(new SearchOptionsBuilder(0, 10)\n                        .filter(FlowNodeInstanceSearchDescriptor.NAME, \"otherBoundaryNotTriggered\").done())\n                .getCount();\n        assertThat(otherBoundaries).isEqualTo(1);\n\n        getProcessAPI().sendMessage(\"MyMessage\",\n                new ExpressionBuilder().createConstantStringExpression(\"pMessageBoundary\"),\n                new ExpressionBuilder().createConstantStringExpression(BOUNDARY_NAME), null);\n        waitForUserTaskAndExecuteIt(processInstance, EXCEPTION_STEP, user);\n\n        waitForProcessToFinish(processInstance);\n\n        waitForArchivedActivity(step1Id, TestStates.ABORTED);\n\n        checkWasntExecuted(processInstance, \"step2\");\n\n        // Boundary events are not archived, so there is no way to check if the second boundary has been properly aborted (otherBoundaryNotTriggered).\n        // For that matter, only check that boundary is deleted:\n        otherBoundaries = getProcessAPI()\n                .searchFlowNodeInstances(new SearchOptionsBuilder(0, 10)\n                        .filter(FlowNodeInstanceSearchDescriptor.NAME, \"otherBoundaryNotTriggered\").done())\n                .getCount();\n        assertThat(otherBoundaries).isEqualTo(0);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void messageBoundaryEventNotTriggered() throws Exception {\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryMessageEvent(\"MyMessage1\");\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(processInstance, \"step1\", user);\n        final long step2Id = waitForUserTask(processInstance.getId(), \"step2\");\n\n        getProcessAPI().sendMessage(\"MyMessage1\",\n                new ExpressionBuilder().createConstantStringExpression(\"pMessageBoundary\"),\n                new ExpressionBuilder().createConstantStringExpression(\"step1\"), null);\n\n        assignAndExecuteStep(step2Id, user);\n        checkWasntExecuted(processInstance, \"exceptionStep\");\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void messageBoundaryEventOnCallActivityTriggered() throws Exception {\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryMessageEventOnCallActivity();\n        final ProcessDefinition calledProcessDefinition = deployAndEnableSimpleProcess(\"calledProcess\", \"calledTask\");\n\n        try {\n            final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n            final ActivityInstance calledStep = waitForUserTaskAndGetIt(processInstance.getId(), \"calledTask\");\n            final ProcessInstance calledProcessInstance = getProcessAPI()\n                    .getProcessInstance(calledStep.getParentProcessInstanceId());\n\n            getProcessAPI().sendMessage(\"MyMessage\",\n                    new ExpressionBuilder().createConstantStringExpression(\"pMessageBoundary\"),\n                    new ExpressionBuilder().createConstantStringExpression(BOUNDARY_NAME), null);\n\n            waitForUserTaskAndExecuteIt(processInstance, \"exceptionStep\", user);\n            waitForProcessToBeInState(calledProcessInstance, ProcessInstanceState.ABORTED);\n            waitForProcessToFinish(processInstance);\n\n            waitForArchivedActivity(calledStep.getId(), TestStates.ABORTED);\n\n            checkWasntExecuted(processInstance, \"step2\");\n        } finally {\n            disableAndDeleteProcess(processDefinition, calledProcessDefinition);\n        }\n    }\n\n    @Test\n    public void messageBoundaryEventOnCallActivityNotTriggered() throws Exception {\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryMessageEventOnCallActivity();\n        final ProcessDefinition calledProcessDefinition = deployAndEnableSimpleProcess(\"calledProcess\", \"calledTask\");\n\n        try {\n            final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n            final ActivityInstance calledStep = waitForUserTaskAndGetIt(processInstance.getId(), \"calledTask\");\n            final ProcessInstance calledProcessInstance = getProcessAPI()\n                    .getProcessInstance(calledStep.getParentProcessInstanceId());\n            assignAndExecuteStep(calledStep, user.getId());\n\n            final long step2Id = waitForUserTask(processInstance.getId(), \"step2\");\n\n            getProcessAPI().sendMessage(\"MyMessage\",\n                    new ExpressionBuilder().createConstantStringExpression(\"pMessageBoundary\"),\n                    new ExpressionBuilder().createConstantStringExpression(BOUNDARY_NAME), null);\n\n            waitForProcessToFinish(calledProcessInstance);\n            assignAndExecuteStep(step2Id, user);\n            waitForProcessToFinish(processInstance);\n\n            checkWasntExecuted(processInstance, \"exceptionStep\");\n        } finally {\n            disableAndDeleteProcess(processDefinition, calledProcessDefinition);\n        }\n    }\n\n    @Test\n    public void messageBoundaryEventTriggeredOnSequentialMultiInstance() throws Exception {\n        final int loopCardinality = 4;\n        final boolean isSequential = true;\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryMessageEventOnMultiInstance(\n                loopCardinality, isSequential);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(processInstance.getId(), \"step1\");\n\n        getProcessAPI().sendMessage(\"MyMessage\",\n                new ExpressionBuilder()\n                        .createConstantStringExpression(\"processWithBoundaryMessageEventAndMultiInstance\"),\n                new ExpressionBuilder().createConstantStringExpression(BOUNDARY_NAME), null);\n        waitForUserTaskAndExecuteIt(processInstance, EXCEPTION_STEP, user);\n\n        waitForProcessToFinish(processInstance);\n\n        waitForArchivedActivity(step1Id, TestStates.ABORTED);\n\n        checkWasntExecuted(processInstance, \"step2\");\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void messageBoundaryEventNotTriggeredOnSequentialMultiInstance() throws Exception {\n        final int loopCardinality = 3;\n        final boolean isSequential = true;\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryMessageEventOnMultiInstance(\n                loopCardinality, isSequential);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        for (int i = 0; i < loopCardinality; i++) {\n            waitForUserTaskAndExecuteIt(processInstance, \"step1\", user);\n        }\n        final long step2Id = waitForUserTask(processInstance.getId(), \"step2\");\n\n        getProcessAPI().sendMessage(\"MyMessage1\",\n                new ExpressionBuilder().createConstantStringExpression(\"pMessageBoundary\"),\n                new ExpressionBuilder().createConstantStringExpression(\"step1\"), null);\n\n        assignAndExecuteStep(step2Id, user);\n        waitForProcessToFinish(processInstance);\n        checkWasntExecuted(processInstance, \"exceptionStep\");\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void messageBoundaryEventTriggeredOnParallelMultiInstance() throws Exception {\n        final int loopCardinality = 4;\n        final boolean isSequential = false;\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryMessageEventOnMultiInstance(\n                loopCardinality, isSequential);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final List<HumanTaskInstance> pendingTasks = waitForPendingTasks(user, loopCardinality);\n        for (final HumanTaskInstance humanTaskInstance : pendingTasks) {\n            assertEquals(\"step1\", humanTaskInstance.getName());\n        }\n\n        getProcessAPI().sendMessage(\"MyMessage\",\n                new ExpressionBuilder()\n                        .createConstantStringExpression(\"processWithBoundaryMessageEventAndMultiInstance\"),\n                new ExpressionBuilder().createConstantStringExpression(BOUNDARY_NAME), null);\n        waitForUserTaskAndExecuteIt(processInstance, EXCEPTION_STEP, user);\n\n        waitForProcessToFinish(processInstance);\n\n        for (final HumanTaskInstance humanTaskInstance : pendingTasks) {\n            waitForArchivedActivity(humanTaskInstance.getId(), TestStates.ABORTED);\n        }\n\n        checkWasntExecuted(processInstance, \"step2\");\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void messageBoundaryEventNotTriggeredOnParallelMultiInstance() throws Exception {\n        final int loopCardinality = 3;\n        final boolean isSequential = false;\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryMessageEventOnMultiInstance(\n                loopCardinality, isSequential);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        for (int i = 0; i < loopCardinality; i++) {\n            waitForUserTaskAndExecuteIt(processInstance, \"step1\", user);\n        }\n        final long step2Id = waitForUserTask(processInstance.getId(), \"step2\");\n\n        getProcessAPI().sendMessage(\"MyMessage1\",\n                new ExpressionBuilder().createConstantStringExpression(\"processWithMultiInstanceAndBoundaryEvent\"),\n                new ExpressionBuilder().createConstantStringExpression(\"step1\"), null);\n\n        assignAndExecuteStep(step2Id, user);\n        checkWasntExecuted(processInstance, \"exceptionStep\");\n\n        waitForProcessToFinish(processInstance);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void messageBoundaryEventTriggeredOnLoopActivity() throws Exception {\n        final int loopMax = 3;\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryMessageEventOnLoopActivity(\n                loopMax);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(processInstance.getId(), \"step1\");\n\n        getProcessAPI().sendMessage(\"MyMessage\",\n                new ExpressionBuilder().createConstantStringExpression(\"processWithLoopActivityAndBoundaryEvent\"),\n                new ExpressionBuilder().createConstantStringExpression(BOUNDARY_NAME), null);\n        waitForUserTaskAndExecuteIt(processInstance, EXCEPTION_STEP, user);\n\n        waitForProcessToFinish(processInstance);\n\n        waitForArchivedActivity(step1Id, TestStates.ABORTED);\n\n        checkWasntExecuted(processInstance, \"step2\");\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void messageBoundaryEventNotTriggeredOnLoopActivity() throws Exception {\n        final int loopMax = 2;\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryMessageEventOnLoopActivity(\n                loopMax);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        for (int i = 0; i < loopMax; i++) {\n            waitForUserTaskAndExecuteIt(processInstance, \"step1\", user);\n        }\n        final long step2Id = waitForUserTask(processInstance.getId(), \"step2\");\n\n        getProcessAPI().sendMessage(\"MyMessage1\",\n                new ExpressionBuilder().createConstantStringExpression(\"processWithLoopActivityAndBoundaryEvent\"),\n                new ExpressionBuilder().createConstantStringExpression(\"step1\"), null);\n\n        assignAndExecuteStep(step2Id, user);\n        waitForProcessToFinish(processInstance);\n        checkWasntExecuted(processInstance, \"exceptionStep\");\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void process_with_call_activity_aborted_by_boundary_event_should_complete_along_with_its_target_process()\n            throws Exception {\n        // given:\n        final DesignProcessDefinition subProcess = new ProcessDefinitionBuilder()\n                .createNewInstance(\"SubProcessWith2AutomaticTasks\", \"2.7\")\n                .addAutomaticTask(\"sub1\").addAutomaticTask(\"sub2\")\n                .addTransition(\"sub1\", \"sub2\")\n                .getProcess();\n        final DesignProcessDefinition parentProcess = new ProcessDefinitionBuilder()\n                .createNewInstance(\"ProcessWithCallActivityAborted\", \"7.3\")\n                .addCallActivity(\"call\",\n                        new ExpressionBuilder().createConstantStringExpression(subProcess.getName()),\n                        new ExpressionBuilder().createConstantStringExpression(subProcess.getVersion()))\n                .addBoundaryEvent(\"boundary\", true).addMessageEventTrigger(\"abortCallActivity\")\n                .addEndEvent(\"end\")\n                .addTransition(\"boundary\", \"end\")\n                .getProcess();\n        getProcessAPI().deployAndEnableProcess(subProcess);\n        ProcessDefinition parentProcessDefinition = getProcessAPI().deployAndEnableProcess(parentProcess);\n        assertNoErrorAfterXAttemps(5, () -> {\n            getProcessAPI().startProcess(parentProcessDefinition.getId());\n            getProcessAPI().sendMessage(\"abortCallActivity\",\n                    new ExpressionBuilder().createConstantStringExpression(\"ProcessWithCallActivityAborted\"),\n                    new ExpressionBuilder().createConstantStringExpression(\"boundary\"), Collections.emptyMap());\n            await().until(\n                    () -> getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.LAST_UPDATE_ASC),\n                    hasSize(0));\n        }, e -> logAllProcesses());\n    }\n\n    @Test\n    public void process_with_call_activity_aborted_by_terminate_end_event_should_complete_along_with_its_target_process()\n            throws Exception {\n        // given:\n        final DesignProcessDefinition subProcess = new ProcessDefinitionBuilder()\n                .createNewInstance(\"SubProcessWith2AutomaticTasks\", \"2.7\")\n                .addAutomaticTask(\"sub1\").addAutomaticTask(\"sub2\")\n                .addTransition(\"sub1\", \"sub2\")\n                .getProcess();\n        final DesignProcessDefinition parentProcess = new ProcessDefinitionBuilder()\n                .createNewInstance(\"ProcessWithCallActivityAborted\", \"7.3\")\n                .addStartEvent(\"start\").addCallActivity(\"call\",\n                        new ExpressionBuilder().createConstantStringExpression(subProcess.getName()),\n                        new ExpressionBuilder().createConstantStringExpression(subProcess.getVersion()))\n                // will loop forever until the terminate and event aborts it:\n                .addLoop(false, new ExpressionBuilder().createConstantBooleanExpression(true)).addAutomaticTask(\"auto1\")\n                .addAutomaticTask(\"auto2\").addEndEvent(\"end\").addTerminateEventTrigger()\n                .addTransition(\"start\", \"call\")\n                .addTransition(\"start\", \"auto1\")\n                .addTransition(\"auto1\", \"auto2\")\n                .addTransition(\"auto2\", \"end\").getProcess();\n        getProcessAPI().deployAndEnableProcess(subProcess);\n        ProcessDefinition parentProcessDefinition = getProcessAPI().deployAndEnableProcess(parentProcess);\n\n        assertNoErrorAfterXAttemps(5, () -> {\n            getProcessAPI().startProcess(parentProcessDefinition.getId());\n            await().until(\n                    () -> getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.LAST_UPDATE_ASC),\n                    hasSize(0));\n        }, e -> logAllProcesses());\n    }\n\n    protected void logAllProcesses() throws SearchException {\n        logger.error(\"Found processes instances\");\n        for (ProcessInstance p : getProcessAPI().getProcessInstances(0, 10,\n                ProcessInstanceCriterion.LAST_UPDATE_ASC)) {\n            logger.error(\"  * \" + p);\n        }\n        logger.error(\"Found flow nodes\");\n        for (FlowNodeInstance p : getProcessAPI()\n                .searchFlowNodeInstances(new SearchOptionsBuilder(0, 100).done()).getResult()) {\n            logger.error(\"  * \" + p);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/event/MessageEventIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.event;\n\nimport static java.util.Collections.emptyMap;\nimport static java.util.Collections.singletonMap;\nimport static org.hamcrest.CoreMatchers.is;\nimport static org.junit.Assert.*;\nimport static org.junit.Assume.assumeNotNull;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.concurrent.TimeUnit;\n\nimport org.assertj.core.api.Assertions;\nimport org.bonitasoft.engine.bpm.data.DataInstance;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion;\nimport org.bonitasoft.engine.bpm.flownode.ActivityStates;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.GatewayType;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.SendEventException;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.CatchMessageEventTriggerDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.ThrowMessageEventTriggerBuilder;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.expression.ExpressionConstants;\nimport org.bonitasoft.engine.expression.ExpressionType;\nimport org.bonitasoft.engine.expression.InvalidExpressionException;\nimport org.bonitasoft.engine.operation.LeftOperand;\nimport org.bonitasoft.engine.operation.LeftOperandBuilder;\nimport org.bonitasoft.engine.operation.Operation;\nimport org.bonitasoft.engine.operation.OperationBuilder;\nimport org.bonitasoft.engine.operation.OperatorType;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.test.StartProcessUntilStep;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.bonitasoft.engine.test.wait.WaitForEvent;\nimport org.bonitasoft.engine.test.wait.WaitForStep;\nimport org.junit.Test;\n\n@SuppressWarnings(\"javadoc\")\npublic class MessageEventIT extends AbstractEventIT {\n\n    /*\n     * 1 receiveProcess, 1 sendProcess, Message goes from EndEvent to StartEvent\n     * dynamic -> deployAndEnable(sendProcess), deployAndEnable(receiveProcess), startProcess(sendProcess)\n     * check receiveProcess has started and halt on the user task.\n     */\n    @Test\n    public void messageStartEventMessageSentAfterEnable() throws Exception {\n        final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithEndMessageEvent(\n                START_WITH_MESSAGE_PROCESS_NAME, \"startEvent\");\n        final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithStartMessageEvent(\n                Collections.<String, String> emptyMap(),\n                Collections.<Operation> emptyList());\n\n        final ProcessInstance sendMessageProcessInstance = getProcessAPI().startProcess(sendMessageProcess.getId());\n        waitForProcessToFinish(sendMessageProcessInstance);\n        waitForUserTask(START_WITH_MESSAGE_STEP1_NAME);\n\n        disableAndDeleteProcess(sendMessageProcess);\n        disableAndDeleteProcess(receiveMessageProcess);\n    }\n\n    /*\n     * 1 receiveProcess, 1 sendProcess, Message goes from EndEvent to StartEvent\n     * dynamic -> deployAndEnable(sendProcess), deployAndEnable(receiveProcess), startProcess(sendProcess)\n     * check receiveProcess has started and halt on the user task.\n     */\n    @Test\n    public void messageStartEventMessageSentAfterEnableWithNoTargetFlowNode() throws Exception {\n        final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithEndMessageEvent(\n                START_WITH_MESSAGE_PROCESS_NAME, null);\n        final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithStartMessageEvent(\n                Collections.<String, String> emptyMap(),\n                Collections.<Operation> emptyList());\n\n        final ProcessInstance sendMessageProcessInstance = getProcessAPI().startProcess(sendMessageProcess.getId());\n        waitForProcessToFinish(sendMessageProcessInstance);\n        waitForUserTask(START_WITH_MESSAGE_STEP1_NAME);\n\n        disableAndDeleteProcess(sendMessageProcess);\n        disableAndDeleteProcess(receiveMessageProcess);\n    }\n\n    /*\n     * Differs only by the dynamic with messageStartEventMessageSentAfterEnable (Before instead of After)\n     * dynamic -> deployAndEnable(sendProcess), startProcess(sendProcess), deployAndEnable(receiveProcess)\n     */\n    @Test\n    public void messageStartEventMessageSentBeforeEnable() throws Exception {\n        final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithEndMessageEvent(\n                START_WITH_MESSAGE_PROCESS_NAME, \"startEvent\");\n        // the message will be send before the target process is deployed\n        final ProcessInstance sendMessageProcessInstance = getProcessAPI().startProcess(sendMessageProcess.getId());\n        waitForProcessToFinish(sendMessageProcessInstance);\n\n        final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithStartMessageEvent(\n                Collections.<String, String> emptyMap(),\n                Collections.<Operation> emptyList());\n        waitForUserTask(START_WITH_MESSAGE_STEP1_NAME);\n\n        disableAndDeleteProcess(sendMessageProcess);\n        disableAndDeleteProcess(receiveMessageProcess);\n    }\n\n    /*\n     * 1 receiveProcess, 1 sendProcess, Message goes from EndEvent to IntermediateEvent\n     * dynamic -> deployAndEnable(sendProcess), deployAndEnable(receiveProcess), startProcess(receiveProcess),\n     * startProcess(sendProcess)\n     * checks : receiveProcess stop on catchEvent, sendProcess is finished, receiveProcess continue and reaches user\n     * task.\n     */\n    @Test\n    public void messageIntermediateCatchEventMessageSentAfterCatch() throws Exception {\n        final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithEndMessageEvent(\n                CATCH_MESSAGE_PROCESS_NAME, CATCH_EVENT_NAME);\n        final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithIntermediateCatchMessageEvent(null,\n                null, null);\n\n        final ProcessInstance receiveMessageProcessInstance = getProcessAPI()\n                .startProcess(receiveMessageProcess.getId());\n        waitForEventInWaitingState(receiveMessageProcessInstance, CATCH_EVENT_NAME);\n\n        final ProcessInstance sendMessageProcessInstance = getProcessAPI().startProcess(sendMessageProcess.getId());\n        waitForProcessToFinish(sendMessageProcessInstance);\n        waitForUserTask(CATCH_MESSAGE_STEP1_NAME);\n\n        disableAndDeleteProcess(sendMessageProcess);\n        disableAndDeleteProcess(receiveMessageProcess);\n    }\n\n    @Test\n    public void messageIntermediateCatchEventMessageSentBeforeCatch() throws Exception {\n        final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithEndMessageEvent(\n                CATCH_MESSAGE_PROCESS_NAME, CATCH_EVENT_NAME);\n        final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithIntermediateCatchMessageEvent(null,\n                null, null);\n        final ProcessInstance sendMessageProcessInstance = getProcessAPI().startProcess(sendMessageProcess.getId());\n        waitForProcessToFinish(sendMessageProcessInstance);\n\n        final ProcessInstance receiveMessageProcessInstance = getProcessAPI()\n                .startProcess(receiveMessageProcess.getId());\n        Thread.sleep(100);// small sleep but don't wait for the event to be waiting, it might happen that that event is already matched at this point\n        waitForUserTask(receiveMessageProcessInstance, CATCH_MESSAGE_STEP1_NAME);\n\n        disableAndDeleteProcess(receiveMessageProcess);\n        disableAndDeleteProcess(sendMessageProcess);\n    }\n\n    /*\n     * Verify that correlation to determine the targeted process work well (Both receiveProcess are instance of the same\n     * processDefinition)\n     * 2 receiveProcess, 2 sendProcess, sendProcess1[1, Doe] -> receiveProcess1[1], sendProcess2[2,Doe Doe] ->\n     * receiveProcess2[2]\n     * checks : receiveProcesses stop on catchEvent, sendProcess1 is finished.\n     */\n    @Test\n    public void messageIntermediateCatchEventWithCorrelations() throws Exception {\n        final Map<String, String> data = new HashMap<>();\n        data.put(\"docNumber\", Integer.class.getName());\n        data.put(\"lastName\", String.class.getName());\n\n        final ArrayList<Entry<Expression, Expression>> correlations = new ArrayList<>(1);\n        final Expression docCorrelationKey = new ExpressionBuilder().createConstantStringExpression(\"docKey\");\n        final Expression docCorrelationValue = new ExpressionBuilder().createDataExpression(\"docNumber\",\n                Integer.class.getName());\n        correlations.add(Map.entry(docCorrelationKey, docCorrelationValue));\n\n        final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithEndMessageEvent(\n                CATCH_MESSAGE_PROCESS_NAME, CATCH_EVENT_NAME, correlations,\n                data, null, null);\n        final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithIntermediateCatchMessageEventAnd1Correlation();\n\n        // start two instances of a receive message process\n        final ProcessInstance receiveMessageProcessInstance1 = getProcessAPI().startProcess(\n                receiveMessageProcess.getId(),\n                Arrays.asList(\n                        buildAssignOperation(\"docRef\", \"1\", Integer.class.getName(), ExpressionType.TYPE_CONSTANT)),\n                null);\n        final ProcessInstance receiveMessageProcessInstance2 = getProcessAPI().startProcess(\n                receiveMessageProcess.getId(),\n                Arrays.asList(\n                        buildAssignOperation(\"docRef\", \"2\", Integer.class.getName(), ExpressionType.TYPE_CONSTANT)),\n                null);\n\n        // wait the event node instance\n        waitForEvent(receiveMessageProcessInstance1, CATCH_EVENT_NAME, TestStates.WAITING);\n        waitForEvent(receiveMessageProcessInstance2, CATCH_EVENT_NAME, TestStates.WAITING);\n\n        // instantiate a process containing correlations matching with receiveMessageProcessInstance1\n        final ProcessInstance sendMessageProcessInstance1 = getProcessAPI().startProcess(\n                sendMessageProcess.getId(),\n                Arrays.asList(\n                        buildAssignOperation(\"docNumber\", \"1\", Integer.class.getName(), ExpressionType.TYPE_CONSTANT),\n                        buildAssignOperation(\"lastName\", \"Doe\", String.class.getName(), ExpressionType.TYPE_CONSTANT)),\n                null);\n        waitForProcessToFinish(sendMessageProcessInstance1);\n        assertNotNull(waitForUserTask(receiveMessageProcessInstance1.getId(), CATCH_MESSAGE_STEP1_NAME));\n        waitForEventInWaitingState(receiveMessageProcessInstance2, CATCH_EVENT_NAME);\n\n        // instantiate a process containing correlations matching with receiveMessageProcessInstance2\n        final ProcessInstance sendMessageProcessInstance2 = getProcessAPI().startProcess(\n                sendMessageProcess.getId(),\n                Arrays.asList(\n                        buildAssignOperation(\"docNumber\", \"2\", Integer.class.getName(), ExpressionType.TYPE_CONSTANT),\n                        buildAssignOperation(\"lastName\", \"Doe Doe\", String.class.getName(),\n                                ExpressionType.TYPE_CONSTANT)),\n                null);\n        waitForProcessToFinish(sendMessageProcessInstance2);\n\n        assertNotNull(waitForUserTask(receiveMessageProcessInstance2.getId(), CATCH_MESSAGE_STEP1_NAME));\n\n        disableAndDeleteProcess(sendMessageProcess);\n        disableAndDeleteProcess(receiveMessageProcess);\n\n    }\n\n    /*\n     * Verify that if a sendProcess has for targets 2 instances of the same ProcessDefinition and no correlation key is\n     * defined\n     * (equivalent to matching keys), exactly one of the receiveProcess catches the message.\n     * 2 receiveProcess, 1 sendProcess, sendProcess1 -> receiveProcess1, sendProcess2 -> receiveProcess2[2]\n     * checks : receiveProcesses stop on catchEvent, sendProcess1 is finished.\n     */\n    @Test\n    public void messageIntermediateCatchEventWithoutCorrelations() throws Exception {\n        final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithEndMessageEvent(\n                CATCH_MESSAGE_PROCESS_NAME, CATCH_EVENT_NAME);\n        final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithIntermediateCatchMessageEvent(null,\n                null, null);\n\n        // start two instances of a receive message process\n        final ProcessInstance receiveMessageProcessInstance1 = getProcessAPI()\n                .startProcess(receiveMessageProcess.getId());\n        final ProcessInstance receiveMessageProcessInstance2 = getProcessAPI()\n                .startProcess(receiveMessageProcess.getId());\n\n        // wait the event node instance\n        waitForEvent(receiveMessageProcessInstance1, CATCH_EVENT_NAME, TestStates.WAITING);\n        waitForEvent(receiveMessageProcessInstance2, CATCH_EVENT_NAME, TestStates.WAITING);\n\n        // instantiate a process containing whom the targetProcess is of the ProcessDefinition receiveMessageProcess\n        final ProcessInstance sendMessageProcessInstance1 = getProcessAPI().startProcess(sendMessageProcess.getId());\n        waitForProcessToFinish(sendMessageProcessInstance1);\n\n        final HumanTaskInstance waitForUserTask = waitForUserTaskAndGetIt(CATCH_MESSAGE_STEP1_NAME);\n        final long processInstance = waitForUserTask.getRootContainerId();\n        assertTrue(processInstance == receiveMessageProcessInstance1.getId()\n                || processInstance == receiveMessageProcessInstance2.getId());\n        assertEquals(1, getProcessAPI()\n                .getPendingHumanTaskInstances(user.getId(), 0, 10, ActivityInstanceCriterion.DEFAULT).size());\n\n        disableAndDeleteProcess(sendMessageProcess);\n        disableAndDeleteProcess(receiveMessageProcess);\n    }\n\n    /*\n     * Verify that even if matching correlations keys are not in the same order in the receiveProcess and sendProcess,\n     * the message is transmitted.\n     * 1 receiveProcess [value1,value2], 1 sendProcesss[value1,value2], message goes from endEvent to intermediateEvent\n     * checks : receiveProcess stop on catchEvent , sendProcess is finished, receiveProcess continues and reaches user\n     * task.\n     */\n    @Test\n    public void correlationKeyInWrongOrderShouldWork() throws Exception {\n        final ArrayList<Entry<Expression, Expression>> correlations = new ArrayList<>(2);\n        correlations\n                .add(Map.entry(new ExpressionBuilder().createConstantStringExpression(\"aKey\"), new ExpressionBuilder()\n                        .createConstantStringExpression(\"value1\")));\n        correlations.add(\n                Map.entry(new ExpressionBuilder().createConstantStringExpression(\"bKey\"), new ExpressionBuilder()\n                        .createConstantStringExpression(\"value2\")));\n\n        final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithEndMessageEvent(\n                CATCH_MESSAGE_PROCESS_NAME, CATCH_EVENT_NAME,\n                correlations, null, null, null);\n\n        final ArrayList<Entry<Expression, Expression>> correlationsReceive = new ArrayList<>(2);\n        correlationsReceive.add(\n                Map.entry(new ExpressionBuilder().createConstantStringExpression(\"bKey\"), new ExpressionBuilder()\n                        .createConstantStringExpression(\"value2\")));\n        correlationsReceive.add(\n                Map.entry(new ExpressionBuilder().createConstantStringExpression(\"aKey\"), new ExpressionBuilder()\n                        .createConstantStringExpression(\"value1\")));\n\n        final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithIntermediateCatchMessageEvent(\n                correlationsReceive, null, null);\n\n        // start two instances of a receive message process\n        final ProcessInstance receiveMessageProcessInstance1 = getProcessAPI()\n                .startProcess(receiveMessageProcess.getId());\n\n        // wait the event node instance\n        waitForEvent(receiveMessageProcessInstance1, CATCH_EVENT_NAME, TestStates.WAITING);\n\n        // instantiate a process containing correlations matching with receiveMessageProcessInstance1\n        final ProcessInstance sendMessageProcessInstance1 = getProcessAPI().startProcess(sendMessageProcess.getId());\n        waitForProcessToFinish(sendMessageProcessInstance1);\n\n        waitForUserTask(receiveMessageProcessInstance1, CATCH_MESSAGE_STEP1_NAME);\n        // waitForStep(100, 5000, \"userTask1\", receiveMessageProcessInstance1);\n\n        disableAndDeleteProcess(sendMessageProcess);\n        disableAndDeleteProcess(receiveMessageProcess);\n    }\n\n    /*\n     * Verify that a sendProcess must have at least all correlation keys of the receiveProcess for the message to be\n     * transmitted.\n     * 1 receiveProcess, 2 sendProcess, receiveProcess1, sendProcess1, sendProcess2\n     * dynamic -> deployAndEnable(sendProcess), deployAndEnable(receiveProcess), startProcess(sendProcess)\n     * checks : sendProcess is finished, , receiveProcess start and stop on user task, data is transmitted to the\n     * receiveProcess\n     */\n    @Test\n    public void multipleCorrelationsKeys() throws Exception {\n        final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithEndMessageEventAndCorrelation();\n        final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithIntermediateCatchMessageEventAnd2Correlations();\n\n        // start a instance of a receive message process\n        final ProcessInstance receiveMessageProcessInstance1 = getProcessAPI().startProcess(\n                receiveMessageProcess.getId(),\n                Arrays.asList(\n                        buildAssignOperation(\"docRef\", \"1\", Integer.class.getName(), ExpressionType.TYPE_CONSTANT),\n                        buildAssignOperation(\"name\", \"Doe Doe\", String.class.getName(), ExpressionType.TYPE_CONSTANT)),\n                null);\n\n        // wait the event node instance\n        waitForEvent(receiveMessageProcessInstance1, CATCH_EVENT_NAME, TestStates.WAITING);\n\n        checkUserHasNoPendingTasks();\n\n        // instantiate a process having one one correlation key matching, the process must not go further\n        final ProcessInstance sendMessageProcessInstance1 = getProcessAPI().startProcess(\n                sendMessageProcess.getId(),\n                Arrays.asList(\n                        buildAssignOperation(\"docNumber\", \"1\", Integer.class.getName(), ExpressionType.TYPE_CONSTANT),\n                        buildAssignOperation(\"lastName\", \"Doe 2\", String.class.getName(),\n                                ExpressionType.TYPE_CONSTANT)),\n                null);\n        waitForProcessToFinish(sendMessageProcessInstance1);\n        // 1 sec because it's an assert false and we forced matching of event\n        assertFalse(new WaitForStep(DEFAULT_REPEAT_EACH, 500, CATCH_MESSAGE_STEP1_NAME,\n                receiveMessageProcessInstance1.getId(), getProcessAPI()).waitUntil());\n\n        // instantiate a process having both two correlation keys matching, the process must go further\n        final ProcessInstance sendMessageProcessInstance2 = getProcessAPI().startProcess(\n                sendMessageProcess.getId(),\n                Arrays.asList(\n                        buildAssignOperation(\"docNumber\", \"1\", Integer.class.getName(), ExpressionType.TYPE_CONSTANT),\n                        buildAssignOperation(\"lastName\", \"Doe Doe\", String.class.getName(),\n                                ExpressionType.TYPE_CONSTANT)),\n                null);\n        waitForProcessToFinish(sendMessageProcessInstance2);\n        waitForUserTask(receiveMessageProcessInstance1, CATCH_MESSAGE_STEP1_NAME);\n\n        disableAndDeleteProcess(sendMessageProcess);\n        disableAndDeleteProcess(receiveMessageProcess);\n    }\n\n    private Operation buildAssignOperation(final String dataInstanceName, final String newConstantValue,\n            final String className,\n            final ExpressionType expressionType) throws InvalidExpressionException {\n        final LeftOperand leftOperand = new LeftOperandBuilder().createNewInstance().setName(dataInstanceName).done();\n        final Expression expression = new ExpressionBuilder().createNewInstance(dataInstanceName)\n                .setContent(newConstantValue)\n                .setExpressionType(expressionType.name()).setReturnType(className).done();\n        final Operation operation;\n        operation = new OperationBuilder().createNewInstance().setOperator(\"=\").setLeftOperand(leftOperand)\n                .setType(OperatorType.ASSIGNMENT)\n                .setRightOperand(expression).done();\n        return operation;\n    }\n\n    /*\n     * 1 receiveProcess, 1 sendProcess, Message goes from IntermediateEvent to StartEvent\n     * dynamic -> deployAndEnable(sendProcess), deployAndEnable(receiveProcess), startProcess(sendProcess)\n     * checks : sendProcess is finished, receiveProcess start and stop on user task.\n     */\n    @Test\n    public void messageIntermediateThrowEventMessageSentAfterEnable() throws Exception {\n        final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithIntermediateThrowMessageEvent(\n                START_WITH_MESSAGE_PROCESS_NAME, \"startEvent\");\n        final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithStartMessageEvent(\n                Collections.<String, String> emptyMap(),\n                Collections.<Operation> emptyList());\n\n        final ProcessInstance sendMessageProcessInstance = getProcessAPI().startProcess(sendMessageProcess.getId());\n        waitForProcessToFinish(sendMessageProcessInstance);\n        waitForUserTask(START_WITH_MESSAGE_STEP1_NAME);\n\n        disableAndDeleteProcess(sendMessageProcess);\n        disableAndDeleteProcess(receiveMessageProcess);\n    }\n\n    @Test\n    public void messageIntermediateThrow2EventMessages() throws Exception {\n        final List<String> messages = new ArrayList<>();\n        messages.add(\"catchMessage\");\n        messages.add(\"startMessage\");\n        final List<String> targetProcesses = new ArrayList<>();\n        targetProcesses.add(CATCH_MESSAGE_PROCESS_NAME);\n        targetProcesses.add(START_WITH_MESSAGE_PROCESS_NAME);\n        final List<String> targetFlowNodes = new ArrayList<>();\n        targetFlowNodes.add(CATCH_EVENT_NAME);\n        targetFlowNodes.add(\"startEvent\");\n        final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithIntermediateThrowMessageEvent(messages,\n                targetProcesses, targetFlowNodes);\n\n        final ProcessDefinition startWithMessageProcess = deployAndEnableProcessWithStartMessageEvent(\n                START_WITH_MESSAGE_PROCESS_NAME, \"startMessage\", null,\n                null);\n        final ProcessDefinition catchMessageProcess = deployAndEnableProcessWithIntermediateCatchMessageEvent(\n                CATCH_MESSAGE_PROCESS_NAME, \"catchMessage\", null,\n                null, null);\n\n        final ProcessInstance catchMessageProcessInstance = getProcessAPI().startProcess(catchMessageProcess.getId());\n        waitForEventInWaitingState(catchMessageProcessInstance, CATCH_EVENT_NAME);\n\n        final ProcessInstance sendMessageProcessInstance = getProcessAPI().startProcess(sendMessageProcess.getId());\n        waitForProcessToFinish(sendMessageProcessInstance);\n        checkNbPendingTaskOf(2, user);\n\n        final List<HumanTaskInstance> taskInstances = getProcessAPI()\n                .getPendingHumanTaskInstances(user.getId(), 0, 10, ActivityInstanceCriterion.NAME_ASC);\n        assertEquals(2, taskInstances.size());\n        assertEquals(CATCH_MESSAGE_STEP1_NAME, taskInstances.get(0).getName());\n        assertEquals(START_WITH_MESSAGE_STEP1_NAME, taskInstances.get(1).getName());\n\n        disableAndDeleteProcess(sendMessageProcess);\n        disableAndDeleteProcess(startWithMessageProcess);\n        disableAndDeleteProcess(catchMessageProcess);\n    }\n\n    /*\n     * 1 receiveProcess, 1 sendProcess, Message contains datas goes from EndEvent to StartEvent\n     * dynamic -> deployAndEnable(sendProcess), deployAndEnable(receiveProcess), startProcess(sendProcess)\n     * checks : sendProcess is finished, , receiveProcess start and stop on user task, data is transmitted to the\n     * receiveProcess\n     */\n    @Test\n    public void dataTransferFromMessageEndEventToStartMessageEvent() throws Exception {\n        final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithEndMessageEvent(\n                START_WITH_MESSAGE_PROCESS_NAME, \"startEvent\", null,\n                Collections.singletonMap(\"lastName\", String.class.getName()),\n                Collections.singletonMap(\"lName\", String.class.getName()),\n                Collections.singletonMap(\"lName\", \"lastName\"));\n        final List<Operation> catchMessageOperations = Collections\n                .singletonList(buildAssignOperation(\"name\", \"lName\", String.class.getName(),\n                        ExpressionType.TYPE_VARIABLE));\n        final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithStartMessageEvent(\n                Collections.singletonMap(\"name\", String.class.getName()),\n                catchMessageOperations);\n\n        final ProcessInstance sendMessageProcessInstance = getProcessAPI().startProcess(sendMessageProcess.getId(),\n                Arrays.asList(\n                        buildAssignOperation(\"lastName\", \"Doe\", String.class.getName(), ExpressionType.TYPE_CONSTANT)),\n                null);\n        waitForProcessToFinish(sendMessageProcessInstance);\n\n        // at the first test some time the cron job time some time before executing\n        final HumanTaskInstance userTask = waitForUserTaskAndGetIt(START_WITH_MESSAGE_STEP1_NAME);\n\n        final DataInstance dataInstance = getProcessAPI().getProcessDataInstance(\"name\", userTask.getRootContainerId());\n        assertEquals(\"Doe\", dataInstance.getValue());\n\n        disableAndDeleteProcess(sendMessageProcess);\n        disableAndDeleteProcess(receiveMessageProcess);\n    }\n\n    /*\n     * 1 receiveProcess, 1 sendProcess, Message contains datas goes from EndEvent to IntermediateEvent\n     * dynamic -> deployAndEnable(sendProcess), deployAndEnable(receiveProcess), startProcess(sendProcess)\n     * checks : receiveProcess start and stop on catchEvent, sendProcess is finished, , receiveProcess continues and\n     * reaches user task , data is transmitted to\n     * the receiveProcess.\n     */\n    @Test\n    public void dataTransferFromMessageEndEventToMessageIntermediateCatchEvent() throws Exception {\n        final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithEndMessageEvent(\n                CATCH_MESSAGE_PROCESS_NAME, CATCH_EVENT_NAME, null,\n                Collections.singletonMap(\"lastName\", String.class.getName()),\n                Collections.singletonMap(\"lName\", String.class.getName()),\n                Collections.singletonMap(\"lName\", \"lastName\"));\n\n        final List<Operation> catchMessageOperations = Collections\n                .singletonList(buildAssignOperation(\"name\", \"lName\", String.class.getName(),\n                        ExpressionType.TYPE_VARIABLE));\n        final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithIntermediateCatchMessageEvent(null,\n                Collections.singletonMap(\"name\", String.class.getName()), catchMessageOperations);\n\n        // start a instance of a receive message process\n        final ProcessInstance receiveMessageProcessInstance = getProcessAPI()\n                .startProcess(receiveMessageProcess.getId());\n\n        // wait the event node instance\n        waitForEventInWaitingState(receiveMessageProcessInstance, CATCH_EVENT_NAME);\n\n        DataInstance dataInstance = getProcessAPI().getProcessDataInstance(\"name\",\n                receiveMessageProcessInstance.getId());\n        assertNull(\"Data is not null\", dataInstance.getValue());\n\n        final ProcessInstance sendMessageProcessInstance = getProcessAPI().startProcess(sendMessageProcess.getId(),\n                Arrays.asList(\n                        buildAssignOperation(\"lastName\", \"Doe\", String.class.getName(), ExpressionType.TYPE_CONSTANT)),\n                null);\n        waitForProcessToFinish(sendMessageProcessInstance);\n        waitForUserTask(receiveMessageProcessInstance, CATCH_MESSAGE_STEP1_NAME);\n\n        dataInstance = getProcessAPI().getProcessDataInstance(\"name\", receiveMessageProcessInstance.getId());\n        assertEquals(\"Doe\", dataInstance.getValue());\n\n        disableAndDeleteProcess(sendMessageProcess);\n        disableAndDeleteProcess(receiveMessageProcess);\n    }\n\n    /*\n     * Verify receiveProcess receive message targeting it, even if sent before its existence.\n     * 1 receiveProcess, 1 sendProcess, Message goes from IntermediateEvent to IntermediateEvent\n     * dynamic -> deployAndEnable(sendProcess), startProcess(sendProcess), deployAndEnable(receiveProcess),\n     * startProcess(receiveProcess)\n     * checks : sendProcess is finished, receiveProcess reaches catchEvent and continue (found message sent by\n     * sendProcess) and reaches user task.\n     */\n    @Test\n    public void messageSentProcessFinishBeforeReceiveProcessIsEnabled() throws Exception {\n        final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithIntermediateThrowMessageEvent(\n                CATCH_MESSAGE_PROCESS_NAME, CATCH_EVENT_NAME);\n        final ProcessInstance sendMessageProcessInstance = getProcessAPI().startProcess(sendMessageProcess.getId());\n        waitForProcessToFinish(sendMessageProcessInstance);\n\n        final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithIntermediateCatchMessageEvent(null,\n                null, null);\n\n        final ProcessInstance receiveMessageProcessInstance = getProcessAPI()\n                .startProcess(receiveMessageProcess.getId());\n        Thread.sleep(100);// small sleep but don't wait for the event to be waiting, it might happen that that event is already matched at this point\n        waitForUserTask(receiveMessageProcessInstance, CATCH_MESSAGE_STEP1_NAME);\n\n        disableAndDeleteProcess(sendMessageProcess);\n        disableAndDeleteProcess(receiveMessageProcess);\n    }\n\n    /*\n     * Test where the message goes from a throwEvent to a catchEvent belonging to the same process hence the same pool\n     * (forbidden by BPMN 2.0)\n     * But the studio forbid this case, so this should never happen in the Engine.\n     */\n    @Test\n    public void messageEventIntraProcess() throws Exception {\n        final ProcessDefinition sendAndReceiveMessageProcess = deployAndEnableProcessWithIntraMessageEvent(\n                \"sendAndReceiveMessageProcess\", CATCH_EVENT_NAME);\n        final ProcessInstance sendAndReceiveMessageProcessInstance = getProcessAPI()\n                .startProcess(sendAndReceiveMessageProcess.getId());\n\n        final long step2Id = waitForUserTask(sendAndReceiveMessageProcessInstance, \"userTask2\");\n        waitForEventInWaitingState(sendAndReceiveMessageProcessInstance, CATCH_EVENT_NAME);\n        assignAndExecuteStep(step2Id, user);\n        waitForUserTask(sendAndReceiveMessageProcessInstance, \"userTask3\");\n\n        disableAndDeleteProcess(sendAndReceiveMessageProcess);\n    }\n\n    /*\n     * 1 receiveProcess, 2 sendProcess, 2 Messages go from EndEvent to IntermediateEvent\n     * dynamic -> deployAndEnable(sendProcess), deployAndEnable(receiveProcess), startProcess(sendProcess),\n     * startProcess(receiveProcess)\n     * checks : sendProcess is finished, receiveProcess reaches catchEvent and continue (found message sent by\n     * sendProcess) and reaches user task.\n     */\n    @Test\n    public void messageIntermediateCatchEventMessageMultiSend() throws Exception {\n        final ProcessDefinition sendMessageProcess1 = deployAndEnableProcessWithEndMessageEvent(\"sendMessageProcess1\",\n                MESSAGE_NAME,\n                CATCH_MESSAGE_PROCESS_NAME, CATCH_EVENT_NAME, null, null, null, null);\n        final ProcessDefinition sendMessageProcess2 = deployAndEnableProcessWithEndMessageEvent(\"sendMessageProcess2\",\n                MESSAGE_NAME,\n                CATCH_MESSAGE_PROCESS_NAME, CATCH_EVENT_NAME, null, null, null, null);\n        final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithIntermediateCatchMessageEvent(null,\n                null, null);\n\n        final ProcessInstance receiveMessageProcessInstance = getProcessAPI()\n                .startProcess(receiveMessageProcess.getId());\n\n        waitForEventInWaitingState(receiveMessageProcessInstance, CATCH_EVENT_NAME);\n\n        final ProcessInstance sendMessageProcessInstance1 = getProcessAPI().startProcess(sendMessageProcess1.getId());\n        waitForProcessToFinish(sendMessageProcessInstance1);\n        waitForUserTask(CATCH_MESSAGE_STEP1_NAME);\n\n        disableAndDeleteProcess(sendMessageProcess1);\n        disableAndDeleteProcess(sendMessageProcess2);\n        disableAndDeleteProcess(receiveMessageProcess);\n    }\n\n    @Test\n    public void deleteProcessInstanceShouldDeleteWaitingEvents() throws Exception {\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithIntermediateCatchMessageEvent(null, null,\n                null);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        assumeNotNull(processInstance);\n\n        waitForEventInWaitingState(processInstance, CATCH_EVENT_NAME);\n\n        final long processInstanceId = processInstance.getId();\n        getProcessAPI().deleteProcessInstance(processInstanceId);\n        assertThat(new WaitForEvent(50, 1000, CATCH_EVENT_NAME, processInstanceId, getProcessAPI()).waitUntil(),\n                is(false));\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void sendMessageViaAPIToStartMessageEvent() throws Exception {\n        final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithStartMessageEvent(\n                Collections.<String, String> emptyMap(),\n                Collections.<Operation> emptyList());\n\n        // send message\n        sendMessage(MESSAGE_NAME, START_WITH_MESSAGE_PROCESS_NAME, \"startEvent\", null);\n        waitForUserTask(START_WITH_MESSAGE_STEP1_NAME);\n\n        disableAndDeleteProcess(receiveMessageProcess);\n    }\n\n    protected void sendMessage(final String messageName, final String targetProcessName,\n            final String targetFlowNodeName,\n            final Map<Expression, Expression> messageContent) throws Exception {\n        final Expression targetProcessExpression = new ExpressionBuilder()\n                .createConstantStringExpression(targetProcessName);\n        final Expression targetFlowNodeExpression = new ExpressionBuilder()\n                .createConstantStringExpression(targetFlowNodeName);\n        getProcessAPI().sendMessage(messageName, targetProcessExpression, targetFlowNodeExpression, messageContent);\n    }\n\n    private void sendMessage(final String messageName, final String targetProcessName, final String targetFlowNodeName,\n            final Map<Expression, Expression> messageContent, final Map<Expression, Expression> correlations)\n            throws Exception {\n        final Expression targetProcessExpression = new ExpressionBuilder()\n                .createConstantStringExpression(targetProcessName);\n        final Expression targetFlowNodeExpression = new ExpressionBuilder()\n                .createConstantStringExpression(targetFlowNodeName);\n        getProcessAPI().sendMessage(messageName, targetProcessExpression, targetFlowNodeExpression, messageContent,\n                correlations);\n    }\n\n    @Test\n    public void sendMessageViaAPIToIntermediateMessageEvent() throws Exception {\n        final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithIntermediateCatchMessageEvent(null,\n                null, null);\n\n        final ProcessInstance receiveMessageProcessInstance = getProcessAPI()\n                .startProcess(receiveMessageProcess.getId());\n        waitForEventInWaitingState(receiveMessageProcessInstance, CATCH_EVENT_NAME);\n\n        checkUserHasNoPendingTasks();\n\n        // send message\n        sendMessage(MESSAGE_NAME, CATCH_MESSAGE_PROCESS_NAME, CATCH_EVENT_NAME, null);\n        waitForUserTask(CATCH_MESSAGE_STEP1_NAME);\n\n        disableAndDeleteProcess(receiveMessageProcess);\n    }\n\n    @Test\n    public void sendMessageWithDataViaAPIToStartMessageEvent() throws Exception {\n        final Expression lastNameDisplay = new ExpressionBuilder().createConstantStringExpression(\"lName\");\n        final Expression lastNameValue = new ExpressionBuilder().createConstantStringExpression(\"Doe\");\n\n        final List<Operation> catchMessageOperations = Collections\n                .singletonList(buildAssignOperation(\"name\", \"lName\", String.class.getName(),\n                        ExpressionType.TYPE_VARIABLE));\n        final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithStartMessageEvent(\n                Collections.singletonMap(\"name\", String.class.getName()),\n                catchMessageOperations);\n\n        checkUserHasNoPendingTasks();\n\n        sendMessage(MESSAGE_NAME, START_WITH_MESSAGE_PROCESS_NAME, \"startEvent\",\n                Collections.singletonMap(lastNameDisplay, lastNameValue));\n        // at the first test some time the cron job time some time before executing\n        final HumanTaskInstance taskInstance = waitForUserTaskAndGetIt(START_WITH_MESSAGE_STEP1_NAME);\n\n        final DataInstance dataInstance = getProcessAPI().getProcessDataInstance(\"name\",\n                taskInstance.getRootContainerId());\n        assertEquals(\"Doe\", dataInstance.getValue());\n\n        // Clean up\n        disableAndDeleteProcess(receiveMessageProcess);\n    }\n\n    @Test\n    public void sendMessageTwiceTriggersTwoStartMessageEvents() throws Exception {\n        final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithStartMessageEvent(\n                Collections.<String, String> emptyMap(),\n                Collections.<Operation> emptyList());\n        sendMessage(MESSAGE_NAME, START_WITH_MESSAGE_PROCESS_NAME, \"startEvent\",\n                Collections.<Expression, Expression> emptyMap());\n        final ActivityInstance taskFirstProcInst = waitForUserTaskAndGetIt(START_WITH_MESSAGE_STEP1_NAME);\n\n        sendMessage(MESSAGE_NAME, START_WITH_MESSAGE_PROCESS_NAME, \"startEvent\",\n                Collections.<Expression, Expression> emptyMap());\n        final ActivityInstance taskSecondProcInst = waitForUserTaskAndGetIt(START_WITH_MESSAGE_STEP1_NAME);\n        assertNotEquals(taskFirstProcInst.getId(), taskSecondProcInst.getId());\n        assertNotEquals(taskFirstProcInst.getParentProcessInstanceId(),\n                taskSecondProcInst.getParentProcessInstanceId());\n\n        disableAndDeleteProcess(receiveMessageProcess);\n    }\n\n    private void checkUserHasNoPendingTasks() {\n        final List<HumanTaskInstance> taskInstances = getProcessAPI()\n                .getPendingHumanTaskInstances(user.getId(), 0, 10, ActivityInstanceCriterion.NAME_ASC);\n        assertEquals(0, taskInstances.size());\n    }\n\n    @Test\n    public void sendMessageWithCorrelationViaAPIToIntermediateMessageEvent() throws Exception {\n        final Expression docCorrelationKey = new ExpressionBuilder().createConstantStringExpression(\"docKey\");\n        final Expression docCorrelationValue = new ExpressionBuilder().createConstantIntegerExpression(1);\n        final Expression nameCorrelationKey = new ExpressionBuilder().createConstantStringExpression(\"nameKey\");\n        final Expression nameCorrelationValue1 = new ExpressionBuilder().createConstantStringExpression(\"Doe 2\");\n        final Expression nameCorrelationValue2 = new ExpressionBuilder().createConstantStringExpression(\"Doe Doe\");\n\n        final Map<Expression, Expression> correlations1 = new HashMap<>(2);\n        correlations1.put(docCorrelationKey, docCorrelationValue);\n        correlations1.put(nameCorrelationKey, nameCorrelationValue1); // don't match\n\n        final Map<Expression, Expression> correlations2 = new HashMap<>(2);\n        correlations2.put(docCorrelationKey, docCorrelationValue);\n        correlations2.put(nameCorrelationKey, nameCorrelationValue2);\n\n        final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithIntermediateCatchMessageEventAnd2Correlations();\n\n        // start a instance of a receive message process\n        final ProcessInstance receiveMessageProcessInstance1 = getProcessAPI().startProcess(\n                receiveMessageProcess.getId(),\n                Arrays.asList(\n                        buildAssignOperation(\"docRef\", \"1\", Integer.class.getName(), ExpressionType.TYPE_CONSTANT),\n                        buildAssignOperation(\"name\", \"Doe Doe\", String.class.getName(), ExpressionType.TYPE_CONSTANT)),\n                null);\n\n        // wait the event node instance\n        waitForEvent(receiveMessageProcessInstance1, CATCH_EVENT_NAME, TestStates.WAITING);\n\n        // send a message having only one correlation key matching, the process must not go further\n        sendMessage(MESSAGE_NAME, CATCH_MESSAGE_PROCESS_NAME, CATCH_EVENT_NAME,\n                Collections.<Expression, Expression> emptyMap(), correlations1);\n        assertFalse(new WaitForStep(50, 1000, \"userTask1\", receiveMessageProcessInstance1.getId(), getProcessAPI())\n                .waitUntil());\n\n        // send a message having both two correlations keys matching, the process must go further\n        sendMessage(MESSAGE_NAME, CATCH_MESSAGE_PROCESS_NAME, CATCH_EVENT_NAME,\n                Collections.<Expression, Expression> emptyMap(), correlations2);\n        waitForUserTask(receiveMessageProcessInstance1, CATCH_MESSAGE_STEP1_NAME);\n\n        disableAndDeleteProcess(receiveMessageProcess);\n    }\n\n    @Test\n    public void sendMessageWithDataViaAPIToIntermediateCatchMessageEvent() throws Exception {\n        final List<Operation> catchMessageOperations = Collections\n                .singletonList(buildAssignOperation(\"name\", \"lName\", String.class.getName(),\n                        ExpressionType.TYPE_VARIABLE));\n        final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithIntermediateCatchMessageEvent(null,\n                Collections.singletonMap(\"name\", String.class.getName()), catchMessageOperations);\n\n        // start a instance of a receive message process\n        final ProcessInstance receiveMessageProcessInstance = getProcessAPI()\n                .startProcess(receiveMessageProcess.getId());\n\n        // wait the event node instance\n        waitForEventInWaitingState(receiveMessageProcessInstance, CATCH_EVENT_NAME);\n\n        DataInstance dataInstance = getProcessAPI().getProcessDataInstance(\"name\",\n                receiveMessageProcessInstance.getId());\n        assertNull(\"Data is not null\", dataInstance.getValue());\n\n        final Expression lastNameDisplay = new ExpressionBuilder().createConstantStringExpression(\"lName\");\n        final Expression lastNameValue = new ExpressionBuilder().createConstantStringExpression(\"Doe\");\n        sendMessage(MESSAGE_NAME, CATCH_MESSAGE_PROCESS_NAME, CATCH_EVENT_NAME,\n                Collections.singletonMap(lastNameDisplay, lastNameValue));\n        waitForUserTask(receiveMessageProcessInstance, CATCH_MESSAGE_STEP1_NAME);\n\n        dataInstance = getProcessAPI().getProcessDataInstance(\"name\", receiveMessageProcessInstance.getId());\n        assertEquals(\"Doe\", dataInstance.getValue());\n\n        disableAndDeleteProcess(receiveMessageProcess);\n    }\n\n    @Test\n    public void should_be_able_to_send_a_good_message_even_after_sending_a_bad_one() throws Exception {\n        DesignProcessDefinition process = new ProcessDefinitionBuilder()\n                .createNewInstance(CATCH_MESSAGE_PROCESS_NAME, PROCESS_VERSION)\n                .addActor(ACTOR_NAME)\n                .addShortTextData(\"processData\", null)\n                .addIntermediateCatchEvent(\"waitingMessage\")\n                .addMessageEventTrigger(\"aMessage\")\n                .addOperation(new OperationBuilder().createSetDataOperation(\"processData\",\n                        new ExpressionBuilder().createDataExpression(\"messageData\", String.class.getName())))\n                .addUserTask(\"step1\", ACTOR_NAME)\n                .addTransition(\"waitingMessage\", \"step1\").getProcess();\n\n        final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithActor(process, ACTOR_NAME, user);\n        final ProcessInstance receiveMessageProcessInstance = getProcessAPI()\n                .startProcess(receiveMessageProcess.getId());\n\n        // send a message that match the waiting message but with missing data\n        sendMessage(\"aMessage\", CATCH_MESSAGE_PROCESS_NAME, \"waitingMessage\", emptyMap());\n        Thread.sleep(200);\n        // the message should not be handled (no way to check that using the API)\n        Assertions.assertThat(getProcessAPI().getOpenActivityInstances(receiveMessageProcessInstance.getId(), 0, 10,\n                ActivityInstanceCriterion.DEFAULT)).isEmpty();\n\n        // send the same message but  with the missing data\n        sendMessage(\"aMessage\", CATCH_MESSAGE_PROCESS_NAME, \"waitingMessage\",\n                singletonMap(string(\"messageData\"), string(\"Doe\")));\n        // the waiting message is now correctly triggered\n        waitForUserTask(receiveMessageProcessInstance, CATCH_MESSAGE_STEP1_NAME);\n\n        // we verify that it was really the second message that matched\n        DataInstance dataInstance = getProcessAPI().getProcessDataInstance(\"processData\",\n                receiveMessageProcessInstance.getId());\n        Assertions.assertThat(dataInstance.getValue()).isEqualTo(\"Doe\");\n\n        disableAndDeleteProcess(receiveMessageProcess);\n    }\n\n    private Expression string(String messageData) throws InvalidExpressionException {\n        return new ExpressionBuilder().createConstantStringExpression(messageData);\n    }\n\n    @Test(expected = SendEventException.class)\n    public void sendMessageWithTooManyCorrelations() throws Exception {\n        final Map<Expression, Expression> correlations = new HashMap<>(6);\n        correlations.put(new ExpressionBuilder().createConstantStringExpression(\"key1\"),\n                new ExpressionBuilder().createConstantIntegerExpression(1));\n        correlations.put(new ExpressionBuilder().createConstantStringExpression(\"key2\"),\n                new ExpressionBuilder().createConstantStringExpression(\"label\"));\n        correlations.put(new ExpressionBuilder().createConstantStringExpression(\"key3\"),\n                new ExpressionBuilder().createConstantStringExpression(\"2\"));\n        correlations.put(new ExpressionBuilder().createConstantStringExpression(\"key4\"),\n                new ExpressionBuilder().createConstantStringExpression(\"Doe 2\"));\n        correlations.put(new ExpressionBuilder().createConstantStringExpression(\"key5\"),\n                new ExpressionBuilder().createConstantStringExpression(\"Doe 2\"));\n        correlations.put(new ExpressionBuilder().createConstantStringExpression(\"key6\"),\n                new ExpressionBuilder().createConstantStringExpression(\"Doe 2\"));\n\n        final Expression targetProcessExpression = new ExpressionBuilder().createConstantStringExpression(\"p1\");\n        final Expression targetFlowNodeExpression = new ExpressionBuilder().createConstantStringExpression(\"step1\");\n\n        getProcessAPI().sendMessage(MESSAGE_NAME, targetProcessExpression, targetFlowNodeExpression, null,\n                correlations);\n    }\n\n    @Test\n    public void sendMessageToTerminateProcessWithLoop() throws Exception {\n        ProcessDefinition processToKillDefinition = null;\n        ProcessDefinition killerProcessDefinition = null;\n        try {\n            // Process to kill\n            final Expression falseExpr = new ExpressionBuilder().createConstantBooleanExpression(false);\n            final Expression dataExpr = new ExpressionBuilder().createDataExpression(\"endtask\",\n                    Boolean.class.getName());\n            final Expression condition = new ExpressionBuilder().createGroovyScriptExpression(\"check\", \"!endtask\",\n                    Boolean.class.getName(), dataExpr);\n            final Expression loopMax = new ExpressionBuilder().createConstantIntegerExpression(10);\n            final ProcessDefinitionBuilder processToKillDefinitionBuilder = new ProcessDefinitionBuilder()\n                    .createNewInstance(\"ProcessToKill\", PROCESS_VERSION);\n            processToKillDefinitionBuilder.addActor(ACTOR_NAME).addStartEvent(\"ToKillStart\").addEndEvent(\"ToKillEnd\")\n                    .addTerminateEventTrigger();\n            processToKillDefinitionBuilder.addData(\"endtask\", Boolean.class.getName(), falseExpr);\n            processToKillDefinitionBuilder.addUserTask(\"Step1\", ACTOR_NAME).addLoop(false, condition, loopMax);\n            processToKillDefinitionBuilder.addUserTask(\"Step2\", ACTOR_NAME);\n            processToKillDefinitionBuilder.addGateway(\"Gateway\", GatewayType.PARALLEL);\n            // Catch Message Event\n            final CatchMessageEventTriggerDefinitionBuilder catchMessageEventTriggerDefinitionBuilder = processToKillDefinitionBuilder\n                    .addIntermediateCatchEvent(CATCH_EVENT_NAME).addMessageEventTrigger(\"msgKiller\");\n            final ArrayList<Entry<Expression, Expression>> correlations = new ArrayList<>(1);\n            final Expression correlationKey = new ExpressionBuilder().createConstantStringExpression(\"key\");\n            final Expression correlationValue = new ExpressionBuilder().createGroovyScriptExpression(\"getId\",\n                    \"processInstanceId\", Long.class.getName(),\n                    new ExpressionBuilder().createEngineConstant(ExpressionConstants.PROCESS_INSTANCE_ID));\n            correlations.add(Map.entry(correlationKey, correlationValue));\n            addCorrelations(catchMessageEventTriggerDefinitionBuilder, correlations);\n            // Transitions\n            processToKillDefinitionBuilder.addTransition(\"ToKillStart\", \"Gateway\");\n            processToKillDefinitionBuilder.addTransition(\"Gateway\", \"Step1\");\n            processToKillDefinitionBuilder.addTransition(\"Step1\", \"Step2\");\n            processToKillDefinitionBuilder.addTransition(\"Step2\", \"ToKillEnd\");\n            processToKillDefinitionBuilder.addTransition(\"Gateway\", CATCH_EVENT_NAME);\n            processToKillDefinitionBuilder.addTransition(CATCH_EVENT_NAME, \"ToKillEnd\");\n            processToKillDefinition = deployAndEnableProcessWithActor(processToKillDefinitionBuilder.done(), ACTOR_NAME,\n                    user);\n\n            final StartProcessUntilStep toKillStartProcessAndWaitForTask = startProcessAndWaitForTask(\n                    processToKillDefinition.getId(), \"Step1\");\n            final ProcessInstance processToKillInstance = toKillStartProcessAndWaitForTask.getProcessInstance();\n\n            // Killer process\n            final ProcessDefinitionBuilder killerProcessDefinitionBuilder = new ProcessDefinitionBuilder()\n                    .createNewInstance(\"KillerProcess\", PROCESS_VERSION);\n            killerProcessDefinitionBuilder.addActor(ACTOR_NAME).addStartEvent(\"KillerStart\");\n            killerProcessDefinitionBuilder.addUserTask(\"Step3\", ACTOR_NAME);\n            // Throw Message Event\n            final Expression targetProcess = new ExpressionBuilder().createConstantStringExpression(\"ProcessToKill\");\n            final Expression targetFlowNode = new ExpressionBuilder().createConstantStringExpression(CATCH_EVENT_NAME);\n            final ThrowMessageEventTriggerBuilder throwMessageEventTriggerBuilder = killerProcessDefinitionBuilder\n                    .addEndEvent(\"KillerEnd\")\n                    .addMessageEventTrigger(\"msgKiller\", targetProcess, targetFlowNode);\n            final ArrayList<Entry<Expression, Expression>> endCorrelations = new ArrayList<>(1);\n            final Expression endCorrelationKey = new ExpressionBuilder().createConstantStringExpression(\"key\");\n            final Expression endCorrelationValue = new ExpressionBuilder()\n                    .createConstantLongExpression(processToKillInstance.getId());\n            endCorrelations.add(Map.entry(endCorrelationKey, endCorrelationValue));\n            addCorrelations(throwMessageEventTriggerBuilder, endCorrelations);\n            // Transitions\n            killerProcessDefinitionBuilder.addTransition(\"KillerStart\", \"Step3\");\n            killerProcessDefinitionBuilder.addTransition(\"Step3\", \"KillerEnd\");\n            killerProcessDefinition = deployAndEnableProcessWithActor(killerProcessDefinitionBuilder.done(), ACTOR_NAME,\n                    user);\n\n            final StartProcessUntilStep killerStartProcessAndWaitForTask = startProcessAndWaitForTask(\n                    killerProcessDefinition.getId(), \"Step3\");\n            assignAndExecuteStep(killerStartProcessAndWaitForTask.getActivityInstance(), user);\n            waitForProcessToFinish(killerStartProcessAndWaitForTask.getProcessInstance());\n\n            // Check that process to kill is terminated\n            waitForProcessToFinish(processToKillInstance);\n            final ArchivedActivityInstance step1 = getProcessAPI()\n                    .getArchivedActivityInstance(toKillStartProcessAndWaitForTask.getActivityInstance().getId());\n            assertEquals(ActivityStates.ABORTED_STATE, step1.getState());\n        } finally {\n            // Clean up\n            disableAndDeleteProcess(processToKillDefinition, killerProcessDefinition);\n        }\n    }\n\n    @Test\n    public void can_use_a_variable_to_define_target_process() throws Exception {\n        //given\n        ProcessDefinition receiveMsgProcess = deployAndEnableProcessWithStartMessageEvent(\"receiveMsgProcess\", \"go\");\n        ProcessDefinition sendMessageProcess = deployAndEnableProcessSendingMessageUsingVariableAsTarget(\n                \"receiveMsgProcess\", \"startEvent\", \"go\");\n\n        //when\n        ProcessInstance processInstance = getProcessAPI().startProcess(sendMessageProcess.getId());\n\n        //then\n        waitForProcessToFinish(processInstance);\n        long taskId = waitForUserTask(START_WITH_MESSAGE_STEP1_NAME);\n        HumanTaskInstance taskInstance = getProcessAPI().getHumanTaskInstance(taskId);\n        Assertions.assertThat(taskInstance.getProcessDefinitionId()).isEqualTo(receiveMsgProcess.getId());\n\n        //clean up\n        disableAndDeleteProcess(receiveMsgProcess, sendMessageProcess);\n    }\n\n    @Test\n    public void can_delete_message_by_creationDate() throws Exception {\n        //clean any existing messages\n        getProcessAPI().deleteMessageByCreationDate(System.currentTimeMillis(), null);\n\n        final Expression targetProcessExpression = new ExpressionBuilder().createConstantStringExpression(\"p1\");\n        final Expression targetFlowNodeExpression = new ExpressionBuilder().createConstantStringExpression(\"step1\");\n\n        getProcessAPI().sendMessage(\"go\", targetProcessExpression, targetFlowNodeExpression, null, null);\n        long untilDate = System.currentTimeMillis();\n\n        TimeUnit.MILLISECONDS.sleep(5);\n\n        getProcessAPI().sendMessage(\"go2\", targetProcessExpression, targetFlowNodeExpression, null, null);\n\n        getProcessAPI().sendMessage(\"go3\", targetProcessExpression, targetFlowNodeExpression, null, null);\n\n        int nbMessageDeleted = getProcessAPI().deleteMessageByCreationDate(untilDate, null);\n        Assertions.assertThat(nbMessageDeleted).isEqualTo(1);\n\n        SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 2);\n        searchOptionsBuilder.filter(\"messageName\", \"go2\");\n\n        nbMessageDeleted = getProcessAPI().deleteMessageByCreationDate(System.currentTimeMillis(),\n                searchOptionsBuilder.done());\n        Assertions.assertThat(nbMessageDeleted).isEqualTo(1);\n\n        //clean up\n        nbMessageDeleted = getProcessAPI().deleteMessageByCreationDate(System.currentTimeMillis(), null);\n        Assertions.assertThat(nbMessageDeleted).isEqualTo(1);\n\n        nbMessageDeleted = getProcessAPI().deleteMessageByCreationDate(System.currentTimeMillis(), null);\n        Assertions.assertThat(nbMessageDeleted).isEqualTo(0);\n\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/event/MessageEventSubProcessIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.event;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.assertEquals;\n\nimport java.io.Serializable;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.data.DataInstance;\nimport org.bonitasoft.engine.bpm.data.DataNotFoundException;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.SubProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.ThrowMessageEventTriggerBuilder;\nimport org.bonitasoft.engine.bpm.supervisor.ProcessSupervisorSearchDescriptor;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.operation.OperationBuilder;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.junit.Test;\n\n/**\n * @author Baptiste Mesta\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class MessageEventSubProcessIT extends AbstractWaitingEventIT {\n\n    @Test\n    public void messageEventSubProcessTransmitData() throws Exception {\n        // create a process with a user step and an event subprocess that start with a start message event having an operation updating the data\n        final ProcessDefinitionBuilder receiveProcessBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"ProcessWithEventSubProcess\", \"1.0\");\n        receiveProcessBuilder.addActor(ACTOR_NAME);\n        receiveProcessBuilder.addShortTextData(\"aData\",\n                new ExpressionBuilder().createConstantStringExpression(\"defaultValue\"));\n        receiveProcessBuilder.addUserTask(\"waitHere\", ACTOR_NAME);\n        final SubProcessDefinitionBuilder subProcessBuilder = receiveProcessBuilder\n                .addSubProcess(\"startWithMessage\", true).getSubProcessBuilder();\n        subProcessBuilder.addUserTask(\"stepInSubProcess\", ACTOR_NAME);\n        subProcessBuilder\n                .addStartEvent(\"start\")\n                .addMessageEventTrigger(\"msg\")\n                .addOperation(\n                        new OperationBuilder().createSetDataOperation(\"aData\",\n                                new ExpressionBuilder().createDataExpression(\"msgData\", String.class.getName())));\n        subProcessBuilder.addTransition(\"start\", \"stepInSubProcess\");\n        final ProcessDefinition receiveProcess = deployAndEnableProcessWithActor(receiveProcessBuilder.done(),\n                ACTOR_NAME, user);\n\n        // create an other process that send a message\n        final ProcessDefinitionBuilder sendProcessBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"SendMsgProcess\", \"1.0\");\n        final ThrowMessageEventTriggerBuilder addMessageEventTrigger = sendProcessBuilder\n                .addIntermediateThrowEvent(\"send\").addMessageEventTrigger(\"msg\",\n                        new ExpressionBuilder().createConstantStringExpression(\"ProcessWithEventSubProcess\"));\n        addMessageEventTrigger.addMessageContentExpression(\n                new ExpressionBuilder().createConstantStringExpression(\"msgData\"),\n                new ExpressionBuilder().createGroovyScriptExpression(\"msgVariable\", \"\\\"message variable OK\\\"\",\n                        String.class.getName()));\n        final ProcessDefinition sendProcess = deployAndEnableProcess(sendProcessBuilder.done());\n        ProcessInstance receiveProcessInstance = getProcessAPI().startProcess(receiveProcess.getId());\n        waitForUserTask(\"waitHere\");\n\n        ProcessInstance processInstance = getProcessAPI().startProcess(sendProcess.getId());\n        final long stepInSubProcessId = waitForUserTask(\"stepInSubProcess\");\n\n        // No need to verify anything, if no exception, query exists\n        getProcessAPI().searchActivities(\n                new SearchOptionsBuilder(0, 10)\n                        .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, processInstance.getId())\n                        .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.SUB_PROCESS).done());\n\n        getProcessAPI().searchActivities(\n                new SearchOptionsBuilder(0, 10)\n                        .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, processInstance.getId())\n                        .filter(ProcessSupervisorSearchDescriptor.USER_ID, user.getId()).done());\n\n        getProcessAPI().searchActivities(\n                new SearchOptionsBuilder(0, 10)\n                        .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.SUB_PROCESS)\n                        .filter(ProcessSupervisorSearchDescriptor.USER_ID, user.getId()).done());\n\n        getProcessAPI().searchActivities(\n                new SearchOptionsBuilder(0, 10)\n                        .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, receiveProcessInstance.getId())\n                        .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.SUB_PROCESS).done());\n\n        // data should be transmit from the message\n        assertEquals(\"message variable OK\",\n                getProcessAPI().getActivityDataInstance(\"aData\", stepInSubProcessId).getValue());\n\n        disableAndDeleteProcess(sendProcess, receiveProcess);\n    }\n\n    @Test\n    public void messageEventSubProcessTriggered() throws Exception {\n        final ProcessDefinition process = deployAndEnableProcessWithMessageEventSubProcess();\n        final ProcessInstance processInstance = getProcessAPI().startProcess(process.getId());\n        final long step1Id = waitForUserTask(processInstance, PARENT_PROCESS_USER_TASK_NAME);\n        final List<ActivityInstance> activities = getProcessAPI().getActivities(processInstance.getId(), 0, 10);\n        assertEquals(1, activities.size());\n        checkNumberOfWaitingEvents(SUB_PROCESS_START_NAME, 1);\n\n        // send message to start event sub process\n        getProcessAPI().sendMessage(MESSAGE_NAME,\n                new ExpressionBuilder().createConstantStringExpression(process.getName()),\n                new ExpressionBuilder().createConstantStringExpression(SUB_PROCESS_START_NAME), null);\n\n        final FlowNodeInstance eventSubProcessActivity = waitForFlowNodeInExecutingState(processInstance,\n                \"eventSubProcess\", false);\n        final ActivityInstance subStep = waitForUserTaskAndGetIt(processInstance, SUB_PROCESS_USER_TASK_NAME);\n        final ProcessInstance subProcInst = getProcessAPI().getProcessInstance(subStep.getParentProcessInstanceId());\n\n        checkNumberOfWaitingEvents(\n                \"The parent process instance is supposed to be aborted, so no more waiting events are expected.\",\n                SUB_PROCESS_START_NAME, 0);\n\n        waitForArchivedActivity(step1Id, TestStates.ABORTED);\n        assignAndExecuteStep(subStep, user);\n        waitForArchivedActivity(eventSubProcessActivity.getId(), TestStates.NORMAL_FINAL);\n        waitForProcessToFinish(subProcInst);\n        waitForProcessToBeInState(processInstance, ProcessInstanceState.ABORTED);\n\n        // check that the transition wasn't taken\n        checkWasntExecuted(processInstance, \"end\");\n\n        disableAndDeleteProcess(process.getId());\n    }\n\n    @Test\n    public void messageEventSubProcessTriggeredWithIntermediateThrowEvent() throws Exception {\n        final String receiverProcessName = \"ReceiverEndMessageEvent\";\n        final String startName = \"start\";\n        final String endName = \"end\";\n\n        // Create and deploy Sender process\n        final Expression targetReceiverProcessExpression = new ExpressionBuilder()\n                .createConstantStringExpression(receiverProcessName);\n        final ProcessDefinitionBuilder senderBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"SenderEndMessageEvent\", \"1.0\");\n        senderBuilder.addActor(ACTOR_NAME);\n        senderBuilder.addStartEvent(startName);\n        senderBuilder.addIntermediateThrowEvent(THROW_MESSAGE_TASK_NAME).addMessageEventTrigger(MESSAGE_NAME,\n                targetReceiverProcessExpression);\n        senderBuilder.addUserTask(PARENT_PROCESS_USER_TASK_NAME, ACTOR_NAME);\n        senderBuilder.addEndEvent(endName).addMessageEventTrigger(MESSAGE_NAME + 1, targetReceiverProcessExpression);\n        senderBuilder.addTransition(startName, THROW_MESSAGE_TASK_NAME);\n        senderBuilder.addTransition(THROW_MESSAGE_TASK_NAME, PARENT_PROCESS_USER_TASK_NAME);\n        senderBuilder.addTransition(PARENT_PROCESS_USER_TASK_NAME, endName);\n        final ProcessDefinition senderProcessDefinition = deployAndEnableProcessWithActor(senderBuilder.done(),\n                ACTOR_NAME, user);\n\n        // Create and deploy Receiver process with SubProcess\n        final ProcessDefinitionBuilder receiverBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(receiverProcessName, \"1.0\");\n        receiverBuilder.addActor(ACTOR_NAME);\n        receiverBuilder.addStartEvent(startName).addMessageEventTrigger(MESSAGE_NAME);\n        receiverBuilder.addUserTask(receiverProcessName + PARENT_PROCESS_USER_TASK_NAME, ACTOR_NAME);\n        receiverBuilder.addTransition(startName, receiverProcessName + PARENT_PROCESS_USER_TASK_NAME);\n\n        final SubProcessDefinitionBuilder subProcessBuilder = receiverBuilder.addSubProcess(\"EventSubProcess\", true)\n                .getSubProcessBuilder();\n        subProcessBuilder.addStartEvent(SUB_PROCESS_START_NAME).addMessageEventTrigger(MESSAGE_NAME + 1);\n        subProcessBuilder.addUserTask(SUB_PROCESS_USER_TASK_NAME, ACTOR_NAME);\n        subProcessBuilder.addTransition(SUB_PROCESS_START_NAME, SUB_PROCESS_USER_TASK_NAME);\n        final ProcessDefinition receiverProcessDefinition = deployAndEnableProcessWithActor(receiverBuilder.done(),\n                ACTOR_NAME, user);\n\n        // Start and execute the Sender process\n        final ProcessInstance senderProcessInstance = getProcessAPI().startProcess(senderProcessDefinition.getId());\n        final long step1Id = waitForUserTask(senderProcessInstance, PARENT_PROCESS_USER_TASK_NAME);\n        waitForPendingTasks(user.getId(), 2);\n        checkNumberOfWaitingEventsInProcess(receiverProcessName, 2);\n        assignAndExecuteStep(step1Id, user);\n        waitForProcessToFinish(senderProcessInstance);\n        checkNumberOfWaitingEvents(\n                \"The parent process instance is supposed to be finished, so no more waiting events are expected.\",\n                startName, 1);\n\n        // Execute the Receiver process\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.filter(ProcessInstanceSearchDescriptor.NAME, receiverProcessName);\n        final ProcessInstance receiverProcessInstance = getProcessAPI()\n                .searchOpenProcessInstances(searchOptionsBuilder.done()).getResult().get(0);\n        waitForUserTask(receiverProcessInstance.getId(), SUB_PROCESS_USER_TASK_NAME);\n        var pendingHumanTaskInstances = getProcessAPI().getPendingHumanTaskInstances(user.getId(), 0, 10,\n                ActivityInstanceCriterion.DEFAULT);\n        try {\n            assertThat(pendingHumanTaskInstances).hasSize(1);\n        } catch (AssertionError e) {\n            System.err.println(\"Too many humanTaskInstances found: \" + pendingHumanTaskInstances.size());\n            for (HumanTaskInstance humanTaskInstance : pendingHumanTaskInstances) {\n                System.err.println(\"humanTaskInstance = \" + humanTaskInstance);\n            }\n            Thread.sleep(10_000);\n            pendingHumanTaskInstances = getProcessAPI().getPendingHumanTaskInstances(user.getId(), 0, 10,\n                    ActivityInstanceCriterion.DEFAULT);\n            if (pendingHumanTaskInstances.size() == 1) {\n                System.err.println(\"Waiting longer solved the problem!\");\n            }\n\n            throw e;\n        } finally {\n            // Clean-up\n            disableAndDeleteProcess(senderProcessDefinition.getId());\n            disableAndDeleteProcess(receiverProcessDefinition.getId());\n        }\n\n    }\n\n    @Test\n    public void messageEventSubProcessNotTriggered() throws Exception {\n        final ProcessDefinition process = deployAndEnableProcessWithMessageEventSubProcess();\n        final ProcessInstance processInstance = getProcessAPI().startProcess(process.getId());\n        final long step1Id = waitForUserTask(processInstance, PARENT_PROCESS_USER_TASK_NAME);\n        final List<ActivityInstance> activities = getProcessAPI().getActivities(processInstance.getId(), 0, 10);\n        assertEquals(1, activities.size());\n        checkNumberOfWaitingEvents(SUB_PROCESS_START_NAME, 1);\n\n        assignAndExecuteStep(step1Id, user);\n\n        waitForArchivedActivity(step1Id, TestStates.NORMAL_FINAL);\n        waitForProcessToFinish(processInstance);\n\n        // the parent process instance has completed, so no more waiting events are expected\n        checkNumberOfWaitingEvents(SUB_PROCESS_START_NAME, 0);\n\n        disableAndDeleteProcess(process);\n    }\n\n    @Test\n    public void messageEventSubProcessTriggeredWithCorrelation() throws Exception {\n        final Expression correlationKey = new ExpressionBuilder().createConstantStringExpression(\"productName\");\n        final Expression catchCorrelationValue = new ExpressionBuilder().createDataExpression(SHORT_DATA_NAME,\n                String.class.getName());\n\n        final ProcessDefinition process = deployAndEnableProcessWithMessageEventSubProcessAndData(\n                Collections.singletonList(Map.entry(\n                        correlationKey, catchCorrelationValue)));\n        final ProcessInstance processInstance = getProcessAPI().startProcess(process.getId());\n        waitForUserTask(processInstance, PARENT_PROCESS_USER_TASK_NAME);\n\n        // send message to start event sub process\n        final Expression throwCorrelationValue = new ExpressionBuilder().createConstantStringExpression(\"parentVar\");// the default data value\n        getProcessAPI().sendMessage(MESSAGE_NAME,\n                new ExpressionBuilder().createConstantStringExpression(process.getName()),\n                new ExpressionBuilder().createConstantStringExpression(SUB_PROCESS_START_NAME), null,\n                Collections.singletonMap(correlationKey, throwCorrelationValue));\n\n        waitForUserTask(processInstance.getId(), SUB_PROCESS_USER_TASK_NAME);\n\n        disableAndDeleteProcess(process.getId());\n    }\n\n    @Test\n    public void messageEventSubProcessTriggeredWithCorrelationAndIntermediateThrowEvent() throws Exception {\n        // Correlation\n        final Expression correlationKey = new ExpressionBuilder().createConstantStringExpression(\"productName\");\n        final Expression correlationValue = new ExpressionBuilder().createDataExpression(SHORT_DATA_NAME,\n                String.class.getName());\n\n        // Sender\n        final Expression targetReceiverProcessExpression = new ExpressionBuilder()\n                .createConstantStringExpression(\"ProcessWithEventSubProcess\");\n        final ProcessDefinitionBuilder senderBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"SenderEndMessageEvent\", \"1.0\");\n        senderBuilder.addActor(ACTOR_NAME).addShortTextData(SHORT_DATA_NAME,\n                new ExpressionBuilder().createConstantStringExpression(\"parentVar\"));\n        senderBuilder.addIntermediateThrowEvent(THROW_MESSAGE_TASK_NAME)\n                .addMessageEventTrigger(MESSAGE_NAME, targetReceiverProcessExpression)\n                .addCorrelation(correlationKey, correlationValue);\n        final ProcessDefinition senderProcessDefinition = deployAndEnableProcessWithActor(senderBuilder.done(),\n                ACTOR_NAME, user);\n\n        // Receiver\n        final ProcessDefinition receiverProcessDefinition = deployAndEnableProcessWithMessageEventSubProcessAndData(\n                Collections\n                        .singletonList(Map.entry(correlationKey, correlationValue)));\n        final ProcessInstance receiverProcessInstance = getProcessAPI().startProcess(receiverProcessDefinition.getId());\n        waitForUserTask(receiverProcessInstance, PARENT_PROCESS_USER_TASK_NAME);\n\n        // send message to start event sub process\n        getProcessAPI().startProcess(senderProcessDefinition.getId());\n        waitForUserTask(receiverProcessInstance.getId(), SUB_PROCESS_USER_TASK_NAME);\n\n        disableAndDeleteProcess(senderProcessDefinition, receiverProcessDefinition);\n    }\n\n    @Test\n    public void createSeveralInstances() throws Exception {\n        final ProcessDefinition process = deployAndEnableProcessWithMessageEventSubProcess();\n        final ProcessInstance processInstance1 = getProcessAPI().startProcess(process.getId());\n        final ProcessInstance processInstance2 = getProcessAPI().startProcess(process.getId());\n\n        // start the first event sub-process\n        getProcessAPI().sendMessage(MESSAGE_NAME,\n                new ExpressionBuilder().createConstantStringExpression(process.getName()),\n                new ExpressionBuilder().createConstantStringExpression(SUB_PROCESS_START_NAME), null);\n        // start the second event sub-process\n        getProcessAPI().sendMessage(MESSAGE_NAME,\n                new ExpressionBuilder().createConstantStringExpression(process.getName()),\n                new ExpressionBuilder().createConstantStringExpression(SUB_PROCESS_START_NAME), null);\n\n        waitForUserTask(processInstance1, SUB_PROCESS_USER_TASK_NAME);\n        waitForUserTask(processInstance2, SUB_PROCESS_USER_TASK_NAME);\n\n        disableAndDeleteProcess(process.getId());\n    }\n\n    @Test\n    public void subProcessCanAccessParentData() throws Exception {\n        final ProcessDefinition process = deployAndEnableProcessWithMessageEventSubProcessAndData(null);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(process.getId());\n        waitForUserTask(processInstance.getId(), PARENT_PROCESS_USER_TASK_NAME);\n\n        getProcessAPI().sendMessage(MESSAGE_NAME,\n                new ExpressionBuilder().createConstantStringExpression(process.getName()),\n                new ExpressionBuilder().createConstantStringExpression(SUB_PROCESS_START_NAME), null);\n\n        final ActivityInstance subStep = waitForUserTaskAndGetIt(processInstance.getId(), SUB_PROCESS_USER_TASK_NAME);\n        final ProcessInstance subProcInst = getProcessAPI().getProcessInstance(subStep.getParentProcessInstanceId());\n        checkProcessDataInstance(INT_DATA_NAME, subProcInst.getId(), 1);\n        checkProcessDataInstance(SHORT_DATA_NAME, subProcInst.getId(), \"childVar\");\n        checkProcessDataInstance(\"value\", subProcInst.getId(), 10.0);\n        checkProcessDataInstance(SHORT_DATA_NAME, processInstance.getId(), \"parentVar\");\n        checkActivityDataInstance(SHORT_DATA_NAME, subStep.getId(), \"childActivityVar\");\n\n        assignAndExecuteStep(subStep, user);\n        waitForProcessToFinish(subProcInst);\n        waitForProcessToBeInState(processInstance, ProcessInstanceState.ABORTED);\n\n        disableAndDeleteProcess(process.getId());\n    }\n\n    private void checkProcessDataInstance(final String dataName, final long processInstanceId,\n            final Serializable expectedValue) throws DataNotFoundException {\n        final DataInstance processDataInstance;\n        processDataInstance = getProcessAPI().getProcessDataInstance(dataName, processInstanceId);\n        assertEquals(expectedValue, processDataInstance.getValue());\n    }\n\n    private void checkActivityDataInstance(final String dataName, final long activityInstanceId,\n            final Serializable expectedValue)\n            throws DataNotFoundException {\n        final DataInstance activityDataInstance;\n        activityDataInstance = getProcessAPI().getActivityDataInstance(dataName, activityInstanceId);\n        assertEquals(expectedValue, activityDataInstance.getValue());\n    }\n\n    @Test\n    public void messageEventSubProcInsideTargetCallActivity() throws Exception {\n        final ProcessDefinition targetProcess = deployAndEnableProcessWithMessageEventSubProcess();\n        final ProcessDefinition callerProcess = deployAndEnableProcessWithCallActivity(targetProcess.getName(),\n                targetProcess.getVersion());\n        final ProcessInstance processInstance = getProcessAPI().startProcess(callerProcess.getId());\n        try {\n            final ActivityInstance step1 = waitForUserTaskAndGetIt(processInstance, PARENT_PROCESS_USER_TASK_NAME);\n\n            getProcessAPI().sendMessage(MESSAGE_NAME,\n                    new ExpressionBuilder().createConstantStringExpression(targetProcess.getName()),\n                    new ExpressionBuilder().createConstantStringExpression(SUB_PROCESS_START_NAME), null);\n\n            final ActivityInstance subStep = waitForUserTaskAndGetIt(processInstance, SUB_PROCESS_USER_TASK_NAME);\n            final ProcessInstance calledProcInst = getProcessAPI()\n                    .getProcessInstance(step1.getParentProcessInstanceId());\n            final ProcessInstance subProcInst = getProcessAPI()\n                    .getProcessInstance(subStep.getParentProcessInstanceId());\n\n            waitForArchivedActivity(step1.getId(), TestStates.ABORTED);\n            assignAndExecuteStep(subStep, user.getId());\n            waitForProcessToFinish(subProcInst);\n            waitForProcessToBeInState(calledProcInst, ProcessInstanceState.ABORTED);\n\n            waitForUserTaskAndExecuteIt(processInstance, \"step2\", user);\n            waitForProcessToFinish(processInstance);\n        } finally {\n            disableAndDeleteProcess(callerProcess);\n            disableAndDeleteProcess(targetProcess);\n        }\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/event/NonInterruptingTimerBoundaryEventIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.event;\n\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.junit.Test;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class NonInterruptingTimerBoundaryEventIT extends AbstractEventIT {\n\n    @Test\n    public void nonInterruptTimerBoundaryEventTriggered() throws Exception {\n        // deploy process with non-interrupting boundary event\n        final long timerDuration = 1000;\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryTimerEvent(timerDuration, false,\n                \"step1\", \"exceptionStep\", \"step2\");\n\n        // start the process and wait for timer to trigger\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(processInstance.getId(), \"step1\");\n        Thread.sleep(timerDuration); // wait timer trigger\n\n        // check that the exception flow was taken\n        final long exceptionFlowStepId = waitForUserTask(processInstance, \"exceptionStep\");\n\n        // execute the task containing the boundary and verify that the normal flow continues\n        assignAndExecuteStep(step1Id, user);\n        final long normalFlowStepId = waitForUserTask(processInstance, \"step2\");\n\n        // execute exception flow step and normal flow step and verify that the process has finished\n        assignAndExecuteStep(exceptionFlowStepId, user);\n        assignAndExecuteStep(normalFlowStepId, user);\n        waitForProcessToFinish(processInstance);\n\n        // clean up\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void nonInterruptTimerBoundaryEventTriggeredOnCallActivity() throws Exception {\n        final long timerDuration = 2000;\n        final String simpleProcessName = \"targetProcess\";\n        final String simpleTaskName = \"stepCA\";\n\n        // deploy a simple process p1\n        final ProcessDefinition targetProcessDefinition = deployAndEnableSimpleProcess(simpleProcessName,\n                simpleTaskName);\n\n        // deploy a process, p2, with a call activity calling p1. The call activity has a non-interrupting timer boundary event\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryTimerEventOnCallActivity(\n                timerDuration, false, simpleProcessName);\n\n        // start the root process and wait for boundary event triggering\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long stepCAId = waitForUserTask(processInstance.getId(), simpleTaskName);\n        Thread.sleep(timerDuration); // wait timer trigger\n\n        // check that the exception flow was taken\n        final long exceptionFlowStepId = waitForUserTask(processInstance.getId(), EXCEPTION_STEP);\n\n        // execute the user task of p1 and check that the normal flow also was taken\n        assignAndExecuteStep(stepCAId, user.getId());\n        final long normalFlowStepId = waitForUserTask(processInstance.getId(), PARENT_PROCESS_USER_TASK_NAME);\n\n        // execute exception flow and normal flow and verify that the process completes\n        assignAndExecuteStep(exceptionFlowStepId, user);\n        assignAndExecuteStep(normalFlowStepId, user);\n        waitForProcessToFinish(processInstance);\n\n        // clean up\n        disableAndDeleteProcess(processDefinition);\n        disableAndDeleteProcess(targetProcessDefinition);\n    }\n\n    @Test\n    public void nonInterruptTimerBoundaryEventTriggeredOnSequentialMultiInstance() throws Exception {\n        // deploy a process with a non-interrupting timer boundary event attached to a sequential multi-instance\n        final long timerDuration = 1000;\n        final String multiTaskName = \"step1\";\n        final String exceptionFlowTaskName = \"exceptionStep\";\n        final int loopCardinality = 2;\n        final String normalFlowTaskName = \"step2\";\n        final ProcessDefinition processDefinition = deployAndEnableProcessMultiInstanceWithBoundaryEvent(timerDuration,\n                false, multiTaskName, loopCardinality,\n                true,\n                normalFlowTaskName, exceptionFlowTaskName);\n\n        // start the process and wait the timer to trigger\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long multiInstanceId = waitForUserTask(processInstance, multiTaskName);\n        Thread.sleep(timerDuration); // wait timer trigger\n\n        // check that the exception flow was taken\n        final long exceptionFlowStepId = waitForUserTask(processInstance, exceptionFlowTaskName);\n\n        // execute multi-instances and verify that normal flow continues\n        assignAndExecuteStep(multiInstanceId, user);\n        waitForUserTasksAndExecuteIt(multiTaskName, processInstance, loopCardinality - 1);\n        final long normalFlowStepId = waitForUserTask(processInstance, normalFlowTaskName);\n\n        // execute exception flow and normal flow and verify that the process completes\n        assignAndExecuteStep(exceptionFlowStepId, user);\n        assignAndExecuteStep(normalFlowStepId, user);\n        waitForProcessToFinish(processInstance);\n\n        // clean up\n        disableAndDeleteProcess(processDefinition.getId());\n    }\n\n    @Test\n    public void nonInterruptTimerBoundaryEventTriggeredOnParallelMultiInstance() throws Exception {\n        final long timerDuration = 1000;\n        final int loopCardinality = 2;\n        // deploy a process with a interrupting timer boundary event attached to a parallel multi-instance\n        final String multiTaskName = \"step1\";\n        final String normalTaskName = \"step2\";\n        final ProcessDefinition processDefinition = deployAndEnableProcessMultiInstanceWithBoundaryEvent(timerDuration,\n                false, multiTaskName, loopCardinality,\n                false, normalTaskName, \"exceptionStep\");\n\n        // start the process and wait for process to be triggered\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance, multiTaskName);\n\n        // verify that the exception flow was taken\n        final long exceptionFlowStepId = waitForUserTask(processInstance, \"exceptionStep\");\n\n        // execute multi-instance and verify that normal flow continues\n        executeRemainingParallelMultiInstances(multiTaskName, processInstance, loopCardinality);\n        final long normalTaskId = waitForUserTask(processInstance, normalTaskName);\n\n        // execute exception flow and normal flow and verify the process completes\n        assignAndExecuteStep(exceptionFlowStepId, user);\n        assignAndExecuteStep(normalTaskId, user);\n        waitForProcessToFinish(processInstance);\n\n        // clean up\n        disableAndDeleteProcess(processDefinition.getId());\n    }\n\n    @Test\n    public void nonInterruptTimerBoundaryEventTriggeredOnLoopActivity() throws Exception {\n        final long timerDuration = 1000;\n        final int loopMax = 2;\n        final String loopActivityName = \"step1\";\n        final String normalFlowStepName = \"step2\";\n        final String exceptionFlowStepName = \"exceptionStep\";\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryTimerEventOnLoopActivity(\n                timerDuration, false, loopMax, loopActivityName,\n                normalFlowStepName, exceptionFlowStepName);\n\n        // start the process and wait timer to trigger\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long loopId = waitForUserTask(processInstance, loopActivityName);\n        Thread.sleep(timerDuration); // wait timer trigger\n\n        // verify that the exception flow was taken\n        final long exceptionStepId = waitForUserTask(processInstance, exceptionFlowStepName);\n\n        // execute all loop activities and verify that the nomal flow continues\n        assignAndExecuteStep(loopId, user);\n        waitForUserTasksAndExecuteIt(loopActivityName, processInstance, loopMax - 1);\n        final long normalFlowStepId = waitForUserTask(processInstance, normalFlowStepName);\n\n        // execute the exception flow and the normal flow and verify that the process completes\n        assignAndExecuteStep(exceptionStepId, user);\n        assignAndExecuteStep(normalFlowStepId, user);\n        waitForProcessToFinish(processInstance);\n\n        // clean up\n        disableAndDeleteProcess(processDefinition.getId());\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/event/SignalBoundaryEventIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.event;\n\nimport static org.junit.Assert.assertFalse;\n\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.bonitasoft.engine.test.wait.WaitForStep;\nimport org.junit.Test;\n\npublic class SignalBoundaryEventIT extends AbstractEventIT {\n\n    @Test\n    public void signalBoundaryEventTriggered() throws Exception {\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundarySignalEvent(\"MySignal\");\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n        getProcessAPI().sendSignal(\"MySignal\");\n\n        waitForUserTaskAndExecuteIt(processInstance, EXCEPTION_STEP, user);\n\n        waitForProcessToFinish(processInstance);\n        waitForArchivedActivity(step1Id, TestStates.ABORTED);\n\n        checkWasntExecuted(processInstance, \"step2\");\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void signalBoundaryEventNotTriggered() throws Exception {\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryMessageEvent(\"MySignal1\");\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(processInstance, \"step1\", user);\n        final long step2 = waitForUserTask(processInstance, \"step2\");\n\n        getProcessAPI().sendSignal(\"MySignal1\");\n\n        final WaitForStep waitForExceptionStep = new WaitForStep(50, 1000, EXCEPTION_STEP, processInstance.getId(),\n                TestStates.READY,\n                getProcessAPI());\n        assertFalse(waitForExceptionStep.waitUntil());\n\n        assignAndExecuteStep(step2, user);\n\n        waitForProcessToFinish(processInstance);\n        checkWasntExecuted(processInstance, EXCEPTION_STEP);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void signalBoundaryEventOnCallActivityTriggered() throws Exception {\n        final String signalName = \"MySignal\";\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundarySignalEventOnCallActivity(\n                signalName);\n        final ProcessDefinition calledProcessDefinition = deployAndEnableSimpleProcess(\"calledProcess\", \"calledStep\");\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final ActivityInstance calledStep = waitForUserTaskAndGetIt(processInstance, \"calledStep\");\n        final ProcessInstance calledProcessInstance = getProcessAPI()\n                .getProcessInstance(calledStep.getParentProcessInstanceId());\n\n        getProcessAPI().sendSignal(\"MySignal\");\n        waitForUserTaskAndExecuteIt(processInstance, EXCEPTION_STEP, user);\n\n        waitForProcessToBeInState(calledProcessInstance, ProcessInstanceState.ABORTED);\n        waitForProcessToFinish(processInstance);\n\n        waitForArchivedActivity(calledStep.getId(), TestStates.ABORTED);\n\n        checkWasntExecuted(processInstance, \"step2\");\n\n        disableAndDeleteProcess(processDefinition);\n        disableAndDeleteProcess(calledProcessDefinition);\n    }\n\n    @Test\n    public void signalBoundaryEventOnCallActivityNotTriggered() throws Exception {\n        final String signalName = \"MySignal\";\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundarySignalEventOnCallActivity(\n                signalName);\n        final ProcessDefinition calledProcessDefinition = deployAndEnableSimpleProcess(\"calledProcess\", \"calledStep\");\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final ActivityInstance calledStep = waitForUserTaskAndGetIt(processInstance, \"calledStep\");\n        final ProcessInstance calledProcessInstance = getProcessAPI()\n                .getProcessInstance(calledStep.getParentProcessInstanceId());\n        assignAndExecuteStep(calledStep, user);\n\n        final long step2Id = waitForUserTask(processInstance, \"step2\");\n        waitForProcessToFinish(calledProcessInstance);\n\n        getProcessAPI().sendSignal(\"MySignal\");\n\n        final WaitForStep waitForExceptionStep = new WaitForStep(50, 1000, EXCEPTION_STEP, processInstance.getId(),\n                TestStates.READY,\n                getProcessAPI());\n        assertFalse(waitForExceptionStep.waitUntil());\n\n        assignAndExecuteStep(step2Id, user);\n        waitForProcessToFinish(processInstance);\n\n        checkWasntExecuted(processInstance, EXCEPTION_STEP);\n\n        disableAndDeleteProcess(processDefinition);\n        disableAndDeleteProcess(calledProcessDefinition);\n    }\n\n    @Test\n    public void signalBoundaryEventTriggeredOnSequentialMultiInstance() throws Exception {\n        final int loopCardinality = 4;\n        final boolean isSequential = true;\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundarySignalEventOnMultiInstance(\n                loopCardinality, isSequential);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n        getProcessAPI().sendSignal(\"MySignal\");\n        waitForUserTaskAndExecuteIt(processInstance, EXCEPTION_STEP, user);\n\n        waitForProcessToFinish(processInstance);\n        waitForArchivedActivity(step1Id, TestStates.ABORTED);\n\n        checkWasntExecuted(processInstance, \"step2\");\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void signalBoundaryEventNotTriggeredOnSequentialMultiInstance() throws Exception {\n        final int loopCardinality = 3;\n        final boolean isSequential = true;\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundarySignalEventOnMultiInstance(\n                loopCardinality, isSequential);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        for (int i = 0; i < loopCardinality; i++) {\n            waitForUserTaskAndExecuteIt(processInstance, \"step1\", user);\n        }\n        final long step2Id = waitForUserTask(processInstance, \"step2\");\n\n        getProcessAPI().sendSignal(\"MySignal1\");\n\n        final WaitForStep waitForExceptionStep = new WaitForStep(50, 1000, EXCEPTION_STEP, processInstance.getId(),\n                TestStates.READY,\n                getProcessAPI());\n        assertFalse(waitForExceptionStep.waitUntil());\n\n        assignAndExecuteStep(step2Id, user);\n\n        waitForProcessToFinish(processInstance);\n        checkWasntExecuted(processInstance, EXCEPTION_STEP);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void signalBoundaryEventTriggeredOnParallelMultiInstance() throws Exception {\n        final int loopCardinality = 4;\n        final boolean isSequential = false;\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundarySignalEventOnMultiInstance(\n                loopCardinality, isSequential);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n        getProcessAPI().sendSignal(\"MySignal\");\n        waitForUserTaskAndExecuteIt(processInstance, EXCEPTION_STEP, user);\n\n        waitForProcessToFinish(processInstance);\n        waitForArchivedActivity(step1Id, TestStates.ABORTED);\n\n        checkWasntExecuted(processInstance, \"step2\");\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void signalBoundaryEventNotTriggeredOnParallelMultiInstance() throws Exception {\n        final int loopCardinality = 3;\n        final boolean isSequential = false;\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundarySignalEventOnMultiInstance(\n                loopCardinality, isSequential);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        for (int i = 0; i < loopCardinality; i++) {\n            waitForUserTaskAndExecuteIt(processInstance, \"step1\", user);\n        }\n        final long step2 = waitForUserTask(processInstance, \"step2\");\n\n        getProcessAPI().sendSignal(\"MySignal1\");\n\n        final WaitForStep waitForExceptionStep = new WaitForStep(50, 1000, EXCEPTION_STEP, processInstance.getId(),\n                TestStates.READY,\n                getProcessAPI());\n        assertFalse(waitForExceptionStep.waitUntil());\n\n        assignAndExecuteStep(step2, user);\n\n        waitForProcessToFinish(processInstance);\n        checkWasntExecuted(processInstance, EXCEPTION_STEP);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void signalBoundaryEventTriggeredOnLoopActivity() throws Exception {\n        final int loopMax = 3;\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundarySignalEventOnLoopActivity(\n                loopMax);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n        getProcessAPI().sendSignal(\"MySignal\");\n        waitForUserTaskAndExecuteIt(processInstance, EXCEPTION_STEP, user);\n\n        waitForProcessToFinish(processInstance);\n        waitForArchivedActivity(step1Id, TestStates.ABORTED);\n\n        checkWasntExecuted(processInstance, \"step2\");\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void signalBoundaryEventNotTriggeredOnLoopActivity() throws Exception {\n        final int loopMax = 2;\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundarySignalEventOnLoopActivity(\n                loopMax);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        for (int i = 0; i < loopMax; i++) {\n            waitForUserTaskAndExecuteIt(processInstance, \"step1\", user);\n        }\n        final long step2Id = waitForUserTask(processInstance, \"step2\");\n\n        getProcessAPI().sendSignal(\"MySignal1\");\n\n        final WaitForStep waitForExceptionStep = new WaitForStep(50, 1000, EXCEPTION_STEP, processInstance.getId(),\n                TestStates.READY,\n                getProcessAPI());\n        assertFalse(waitForExceptionStep.waitUntil());\n\n        assignAndExecuteStep(step2Id, user);\n\n        waitForProcessToFinish(processInstance);\n        checkWasntExecuted(processInstance, EXCEPTION_STEP);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/event/SignalEventIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.event;\n\nimport static org.junit.Assert.assertEquals;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.junit.Test;\n\npublic class SignalEventIT extends AbstractEventIT {\n\n    @Test\n    public void sendSignal() throws Exception {\n        final BusinessArchiveBuilder archiveBuilder = new BusinessArchiveBuilder();\n\n        ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder();\n        builder.createNewInstance(\"SayGO\", \"1.0\").addActor(ACTOR_NAME).addStartEvent(\"Start\")\n                .addUserTask(\"step1\", ACTOR_NAME).addEndEvent(\"End\")\n                .addSignalEventTrigger(\"GO\").addTransition(\"Start\", \"step1\").addTransition(\"step1\", \"End\");\n        archiveBuilder.createNewBusinessArchive().setProcessDefinition(builder.done());\n        final BusinessArchive endSignalArchive = archiveBuilder.done();\n\n        builder = new ProcessDefinitionBuilder();\n        builder.createNewInstance(\"GetGO\", \"1.0\").addActor(ACTOR_NAME).addStartEvent(\"StartOnSignal\")\n                .addSignalEventTrigger(\"GO\")\n                .addUserTask(\"Task1\", ACTOR_NAME).addTransition(\"StartOnSignal\", \"Task1\");\n        archiveBuilder.createNewBusinessArchive().setProcessDefinition(builder.done());\n        final BusinessArchive startSignalArchive = archiveBuilder.done();\n\n        final ProcessDefinition processDefinitionWithStartSignal = deployAndEnableProcessWithActor(startSignalArchive,\n                ACTOR_NAME, user);\n        final ProcessDefinition processDefinitionWithEndSignal = deployAndEnableProcessWithActor(endSignalArchive,\n                ACTOR_NAME, user);\n\n        logout();\n        loginOnDefaultTenantWith(USERNAME, PASSWORD);\n\n        // Check that the process with trigger signal on start is not started, before send signal\n        final ProcessInstance processInstanceWithEndSignal = getProcessAPI()\n                .startProcess(processDefinitionWithEndSignal.getId());\n        waitForUserTask(processInstanceWithEndSignal, \"step1\");\n        checkNbOfProcessInstances(1);\n\n        List<HumanTaskInstance> taskInstances = getProcessAPI().getPendingHumanTaskInstances(user.getId(), 0, 10,\n                ActivityInstanceCriterion.NAME_ASC);\n        assertEquals(1, taskInstances.size());\n        assertEquals(\"step1\", taskInstances.get(0).getName());\n\n        // Send signal\n        assignAndExecuteStep(taskInstances.get(0), user);\n        waitForProcessToFinish(processInstanceWithEndSignal.getId());\n\n        // Check that the process with trigger signal on start is started, after send signal\n        waitForUserTask(\"Task1\");\n        taskInstances = getProcessAPI().getPendingHumanTaskInstances(user.getId(), 0, 10,\n                ActivityInstanceCriterion.NAME_ASC);\n        assertEquals(1, taskInstances.size());\n        assertEquals(\"Task1\", taskInstances.get(0).getName());\n\n        disableAndDeleteProcess(processDefinitionWithStartSignal, processDefinitionWithEndSignal);\n    }\n\n    @Test\n    public void sendIntermediateCatchSignal() throws Exception {\n        final BusinessArchiveBuilder archiveBuilder = new BusinessArchiveBuilder();\n\n        ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder();\n        builder.createNewInstance(\"SayGO\", \"1.0\").addStartEvent(\"Start\").addEndEvent(\"End\").addSignalEventTrigger(\"GO\")\n                .addTransition(\"Start\", \"End\");\n        archiveBuilder.createNewBusinessArchive().setProcessDefinition(builder.done());\n        final BusinessArchive endSignalArchive = archiveBuilder.done();\n\n        builder = new ProcessDefinitionBuilder();\n        builder.createNewInstance(\"GetGO\", \"1.0\").addActor(ACTOR_NAME).addStartEvent(\"Start\")\n                .addIntermediateCatchEvent(\"OnSignal\").addSignalEventTrigger(\"GO\")\n                .addUserTask(\"Task1\", ACTOR_NAME).addTransition(\"Start\", \"OnSignal\").addTransition(\"OnSignal\", \"Task1\");\n        archiveBuilder.createNewBusinessArchive().setProcessDefinition(builder.done());\n        final BusinessArchive startSignalArchive = archiveBuilder.done();\n\n        final ProcessDefinition startSignal = deployAndEnableProcessWithActor(startSignalArchive, ACTOR_NAME, user);\n        final ProcessDefinition endSignal = deployAndEnableProcess(endSignalArchive);\n\n        logout();\n        loginOnDefaultTenantWith(USERNAME, PASSWORD);\n        final ProcessInstance instance = getProcessAPI().startProcess(startSignal.getId());\n        waitForEvent(instance, \"OnSignal\", TestStates.WAITING);\n\n        getProcessAPI().startProcess(endSignal.getId());\n        waitForUserTask(\"Task1\");\n\n        disableAndDeleteProcess(startSignal, endSignal);\n    }\n\n    @Test\n    public void sendIntermadiateThrowSignal() throws Exception {\n        final BusinessArchiveBuilder archiveBuilder = new BusinessArchiveBuilder();\n\n        ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder();\n        final String intermediate = \"Intermediate\";\n        builder.createNewInstance(\"SayGO\", \"1.0\").addStartEvent(\"Start\").addIntermediateThrowEvent(intermediate)\n                .addSignalEventTrigger(\"GO\").addEndEvent(\"End\")\n                .addTransition(\"Start\", intermediate).addTransition(intermediate, \"End\");\n        archiveBuilder.createNewBusinessArchive().setProcessDefinition(builder.done());\n        final BusinessArchive endSignalArchive = archiveBuilder.done();\n\n        builder = new ProcessDefinitionBuilder();\n        builder.createNewInstance(\"GetGO\", \"1.0\").addActor(ACTOR_NAME).addStartEvent(\"StartOnSignal\")\n                .addSignalEventTrigger(\"GO\")\n                .addUserTask(\"Task1\", ACTOR_NAME).addTransition(\"StartOnSignal\", \"Task1\");\n        archiveBuilder.createNewBusinessArchive().setProcessDefinition(builder.done());\n        final BusinessArchive startSignalArchive = archiveBuilder.done();\n\n        final ProcessDefinition startSignal = deployAndEnableProcessWithActor(startSignalArchive, ACTOR_NAME, user);\n        final ProcessDefinition endSignal = deployAndEnableProcess(endSignalArchive);\n\n        logout();\n        loginOnDefaultTenantWith(USERNAME, PASSWORD);\n\n        getProcessAPI().startProcess(endSignal.getId());\n        waitForUserTask(\"Task1\");\n\n        disableAndDeleteProcess(startSignal, endSignal);\n    }\n\n    @Test\n    public void sendSignalViaAPIToStartSignalEvent() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder();\n        builder.createNewInstance(\"GetGO\", \"1.0\").addActor(ACTOR_NAME).addStartEvent(\"StartOnSignal\")\n                .addSignalEventTrigger(\"GO\")\n                .addUserTask(\"Task1\", ACTOR_NAME).addTransition(\"StartOnSignal\", \"Task1\");\n        final DesignProcessDefinition startSignalDef = builder.done();\n\n        final ProcessDefinition startSignal = deployAndEnableProcessWithActor(startSignalDef, ACTOR_NAME, user);\n        logout();\n        loginOnDefaultTenantWith(USERNAME, PASSWORD);\n\n        getProcessAPI().sendSignal(\"GO\");\n        waitForUserTask(\"Task1\");\n\n        disableAndDeleteProcess(startSignal);\n    }\n\n    @Test\n    public void sendSignalViaAPIToIntermediateSignalEvent() throws Exception {\n\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder();\n        builder.createNewInstance(\"GetGO\", \"1.0\").addActor(ACTOR_NAME).addStartEvent(\"Start\")\n                .addIntermediateCatchEvent(\"OnSignal\").addSignalEventTrigger(\"GO\")\n                .addUserTask(\"Task1\", ACTOR_NAME).addTransition(\"Start\", \"OnSignal\").addTransition(\"OnSignal\", \"Task1\");\n        final ProcessDefinition intermediateSignal = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user);\n        logout();\n        loginOnDefaultTenantWith(USERNAME, PASSWORD);\n\n        final ProcessInstance instance = getProcessAPI().startProcess(intermediateSignal.getId());\n        waitForEvent(instance, \"OnSignal\", TestStates.WAITING);\n\n        getProcessAPI().sendSignal(\"GO\");\n        waitForUserTask(\"Task1\");\n\n        disableAndDeleteProcess(intermediateSignal);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/event/SignalEventSubProcessIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.event;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.assertEquals;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.data.DataInstance;\nimport org.bonitasoft.engine.bpm.data.DataNotFoundException;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.GatewayType;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.SubProcessDefinitionBuilder;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.junit.Test;\n\n/**\n * @author Baptiste Mesta\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class SignalEventSubProcessIT extends AbstractWaitingEventIT {\n\n    @Test\n    public void evaluateExpressionsOnLoopUserTaskInSupProcess() throws Exception {\n        final ProcessDefinition process = deployAndEnableProcessWithSignalEventSubProcess(false, true);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(process.getId());\n        waitForUserTask(processInstance, PARENT_PROCESS_USER_TASK_NAME);\n        checkNumberOfWaitingEvents(SUB_PROCESS_START_NAME, 1);\n\n        // send signal to start event sub process\n        getProcessAPI().sendSignal(SIGNAL_NAME);\n\n        waitForFlowNodeInExecutingState(processInstance, SUB_PROCESS_NAME, false);\n        final long subStepId = waitForUserTask(processInstance, SUB_PROCESS_USER_TASK_NAME);\n\n        final Map<Expression, Map<String, Serializable>> expressions = new HashMap<>();\n        expressions.put(new ExpressionBuilder().createDataExpression(SHORT_DATA_NAME, String.class.getName()),\n                new HashMap<String, Serializable>(0));\n        final Map<String, Serializable> expressionResults = getProcessAPI()\n                .evaluateExpressionsOnActivityInstance(subStepId, expressions);\n        assertEquals(\"childActivityVar\", expressionResults.get(SHORT_DATA_NAME));\n\n        disableAndDeleteProcess(process);\n    }\n\n    @Test\n    public void signalEventSubProcessTriggered() throws Exception {\n        // given\n        final ProcessDefinition process = deployAndEnableProcessWithSignalEventSubProcess(false, false);\n\n        // when\n        final ProcessInstance processInstance = getProcessAPI().startProcess(process.getId());\n        final long step1Id = waitForUserTask(processInstance, PARENT_PROCESS_USER_TASK_NAME);\n\n        // then\n        List<ActivityInstance> activities = getProcessAPI().getActivities(processInstance.getId(), 0, 10);\n        assertThat(activities).as(\"should have 1 activity\").hasSize(1);\n        checkNumberOfWaitingEvents(SUB_PROCESS_START_NAME, 1);\n\n        // when\n        getProcessAPI().sendSignal(SIGNAL_NAME);\n        waitForArchivedActivity(step1Id, TestStates.ABORTED);\n        final ActivityInstance subStep = waitForUserTaskAndGetIt(processInstance, SUB_PROCESS_USER_TASK_NAME);\n\n        // then\n        checkNumberOfWaitingEvents(SUB_PROCESS_START_NAME, 0);\n        activities = getProcessAPI().getActivities(processInstance.getId(), 0, 10);\n        assertThat(activities).as(\"should have 2 activities: sub-process flow node and user task\").hasSize(2);\n        assertEquals(SUB_PROCESS_NAME, activities.get(0).getName());\n        assertEquals(SUB_PROCESS_USER_TASK_NAME, activities.get(1).getName());\n\n        // when\n        final ProcessInstance subProcInst = getProcessAPI().getProcessInstance(subStep.getParentProcessInstanceId());\n        assignAndExecuteStep(subStep, user);\n        waitForProcessToFinish(subProcInst);\n        waitForProcessToBeInState(processInstance, ProcessInstanceState.ABORTED);\n\n        // then\n        // check that the transition wasn't taken\n        checkWasntExecuted(processInstance, PARENT_END);\n\n        disableAndDeleteProcess(process);\n    }\n\n    @Test\n    public void signalEventSubProcessTriggeredWithIntermediateThrowEvent() throws Exception {\n        final ProcessDefinition process = deployAndEnableProcessWithSignalEventSubProcess(true, false);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(process.getId());\n        final long step1Id = waitForUserTask(processInstance, PARENT_PROCESS_USER_TASK_NAME);\n        checkNumberOfWaitingEvents(SUB_PROCESS_START_NAME, 1);\n\n        assignAndExecuteStep(step1Id, user);\n\n        waitForUserTask(processInstance, SUB_PROCESS_USER_TASK_NAME);\n\n        disableAndDeleteProcess(process);\n    }\n\n    @Test\n    public void signalEventSubProcessNotTriggered() throws Exception {\n        final ProcessDefinition process = deployAndEnableProcessWithSignalEventSubProcess(false, false);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(process.getId());\n        final long step1Id = waitForUserTask(processInstance, PARENT_PROCESS_USER_TASK_NAME);\n        final List<ActivityInstance> activities = getProcessAPI().getActivities(processInstance.getId(), 0, 10);\n        assertEquals(1, activities.size());\n        checkNumberOfWaitingEvents(SUB_PROCESS_START_NAME, 1);\n\n        assignAndExecuteStep(step1Id, user);\n\n        waitForArchivedActivity(step1Id, TestStates.NORMAL_FINAL);\n        waitForProcessToFinish(processInstance);\n\n        // the parent process instance has completed, so no more waiting events are expected\n        checkNumberOfWaitingEvents(SUB_PROCESS_START_NAME, 0);\n\n        disableAndDeleteProcess(process);\n    }\n\n    @Test\n    public void createSeveralInstances() throws Exception {\n        final ProcessDefinition process = deployAndEnableProcessWithSignalEventSubProcess(false, false);\n        final ProcessInstance processInstance1 = getProcessAPI().startProcess(process.getId());\n        final ProcessInstance processInstance2 = getProcessAPI().startProcess(process.getId());\n\n        waitForUserTask(processInstance1, PARENT_PROCESS_USER_TASK_NAME);\n        waitForUserTask(processInstance2, PARENT_PROCESS_USER_TASK_NAME);\n\n        // send signal to start event sub processes: one signal must start the event sub-processes in the two process instances\n        getProcessAPI().sendSignal(SIGNAL_NAME);\n\n        waitForUserTask(processInstance1, SUB_PROCESS_USER_TASK_NAME);\n        waitForUserTask(processInstance2, SUB_PROCESS_USER_TASK_NAME);\n        Thread.sleep(50);\n\n        disableAndDeleteProcess(process);\n    }\n\n    @Test\n    public void subProcessCanAccessParentData() throws Exception {\n        final ProcessDefinition process = deployAndEnableProcessWithSignalEventSubProcess(false, true);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(process.getId());\n        waitForUserTask(processInstance, PARENT_PROCESS_USER_TASK_NAME);\n\n        getProcessAPI().sendSignal(SIGNAL_NAME);\n\n        final ActivityInstance subStep = waitForUserTaskAndGetIt(processInstance, SUB_PROCESS_USER_TASK_NAME);\n        final ProcessInstance subProcInst = getProcessAPI().getProcessInstance(subStep.getParentProcessInstanceId());\n        checkProcessDataInstance(\"count\", subProcInst.getId(), 1);\n        checkProcessDataInstance(SHORT_DATA_NAME, subProcInst.getId(), \"childVar\");\n        checkProcessDataInstance(\"value\", subProcInst.getId(), 10.0);\n        checkProcessDataInstance(SHORT_DATA_NAME, processInstance.getId(), \"parentVar\");\n        checkActivityDataInstance(SHORT_DATA_NAME, subStep.getId(), \"childActivityVar\");\n\n        assignAndExecuteStep(subStep, user);\n        waitForProcessToFinish(subProcInst);\n        waitForProcessToBeInState(processInstance, ProcessInstanceState.ABORTED);\n\n        disableAndDeleteProcess(process);\n    }\n\n    private void checkProcessDataInstance(final String dataName, final long processInstanceId,\n            final Serializable expectedValue) throws DataNotFoundException {\n        final DataInstance processDataInstance;\n        processDataInstance = getProcessAPI().getProcessDataInstance(dataName, processInstanceId);\n        assertEquals(expectedValue, processDataInstance.getValue());\n    }\n\n    private void checkActivityDataInstance(final String dataName, final long activityInstanceId,\n            final Serializable expectedValue)\n            throws DataNotFoundException {\n        final DataInstance activityDataInstance;\n        activityDataInstance = getProcessAPI().getActivityDataInstance(dataName, activityInstanceId);\n        assertEquals(expectedValue, activityDataInstance.getValue());\n    }\n\n    @Test\n    public void signalEventSubProcInsideTargetCallActivity() throws Exception {\n        final ProcessDefinition targetProcess = deployAndEnableProcessWithSignalEventSubProcess(false, false);\n        final ProcessDefinition callerProcess = deployAndEnableProcessWithCallActivity(targetProcess.getName(),\n                targetProcess.getVersion());\n        final ProcessInstance processInstance = getProcessAPI().startProcess(callerProcess.getId());\n        final ActivityInstance step1 = waitForUserTaskAndGetIt(processInstance, PARENT_PROCESS_USER_TASK_NAME);\n\n        getProcessAPI().sendSignal(SIGNAL_NAME);\n\n        final ActivityInstance subStep = waitForUserTaskAndGetIt(processInstance, SUB_PROCESS_USER_TASK_NAME);\n        final ProcessInstance calledProcInst = getProcessAPI().getProcessInstance(step1.getParentProcessInstanceId());\n        final ProcessInstance subProcInst = getProcessAPI().getProcessInstance(subStep.getParentProcessInstanceId());\n\n        waitForArchivedActivity(step1.getId(), TestStates.ABORTED);\n        assignAndExecuteStep(subStep, user);\n        waitForProcessToFinish(subProcInst);\n        waitForProcessToBeInState(calledProcInst, ProcessInstanceState.ABORTED);\n\n        waitForUserTaskAndExecuteIt(processInstance, \"step2\", user);\n        waitForProcessToFinish(processInstance);\n\n        disableAndDeleteProcess(callerProcess.getId());\n        disableAndDeleteProcess(targetProcess.getId());\n    }\n\n    @Test\n    public void should_process_with_gateway_be_canceled_by_event_subprocess() throws Exception {\n        //given\n        ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"ProcessInterrupted\", \"1.0\");\n        processDefinitionBuilder.addActor(\"john\", true);\n        processDefinitionBuilder.addStartEvent(\"start\");\n        processDefinitionBuilder.addAutomaticTask(\"auto1\");\n        processDefinitionBuilder.addGateway(\"p1\", GatewayType.PARALLEL);\n        processDefinitionBuilder.addUserTask(\"user1\", \"john\");\n        processDefinitionBuilder.addAutomaticTask(\"auto2\");\n        processDefinitionBuilder.addGateway(\"p2\", GatewayType.PARALLEL);\n        processDefinitionBuilder.addEndEvent(\"end\").addTerminateEventTrigger();\n        processDefinitionBuilder.addTransition(\"start\", \"auto1\");\n        processDefinitionBuilder.addTransition(\"auto1\", \"p1\");\n        processDefinitionBuilder.addTransition(\"p1\", \"auto2\");\n        processDefinitionBuilder.addTransition(\"p1\", \"user1\");\n        processDefinitionBuilder.addTransition(\"auto2\", \"p2\");\n        processDefinitionBuilder.addTransition(\"user1\", \"p2\");\n        processDefinitionBuilder.addTransition(\"p2\", \"end\");\n        SubProcessDefinitionBuilder eventSub = processDefinitionBuilder.addSubProcess(\"eventSub\", true)\n                .getSubProcessBuilder();\n        eventSub.addStartEvent(\"signalStart\").addSignalEventTrigger(\"bip_bip\");\n        eventSub.addAutomaticTask(\"subAuto1\");\n        eventSub.addEndEvent(\"subEnd\").addTerminateEventTrigger();\n        eventSub.addTransition(\"signalStart\", \"subAuto1\").addTransition(\"subAuto1\", \"subEnd\");\n        ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), \"john\",\n                user);\n        //when\n        ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance.getId(), \"user1\");\n        getProcessAPI().sendSignal(\"bip_bip\");\n        //then\n        waitForProcessToBeInState(processInstance, ProcessInstanceState.ABORTED);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/event/StartEventIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.event;\n\nimport static org.junit.Assert.*;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.TimerType;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.junit.Test;\n\npublic class StartEventIT extends TestWithUser {\n\n    @Test\n    public void executeSeveralStartEventsInSameProcessDefinition() throws Exception {\n        final int timerValue = 10000;\n        final Expression timerExpression = new ExpressionBuilder().createConstantLongExpression(timerValue);\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addStartEvent(\"startEvent\").addUserTask(\"step1\", ACTOR_NAME)\n                .addTransition(\"startEvent\", \"step1\");\n        processDefinitionBuilder.addStartEvent(\"startEventWithSignal\").addSignalEventTrigger(\"signalName\")\n                .addUserTask(\"step1WithSignal\", ACTOR_NAME)\n                .addTransition(\"startEventWithSignal\", \"step1WithSignal\");\n        processDefinitionBuilder.addStartEvent(\"startEventWithTimer\")\n                .addTimerEventTriggerDefinition(TimerType.DURATION, timerExpression)\n                .addUserTask(\"step1WithTimer\", ACTOR_NAME).addTransition(\"startEventWithTimer\", \"step1WithTimer\");\n        processDefinitionBuilder.addStartEvent(\"startEventWithMessage\").addMessageEventTrigger(\"message\")\n                .addUserTask(\"step1WithMessage\", ACTOR_NAME)\n                .addTransition(\"startEventWithMessage\", \"step1WithMessage\");\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(\n                processDefinitionBuilder.getProcess(), ACTOR_NAME, user);\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId());\n        final HumanTaskInstance step1 = waitForUserTaskAndGetIt(\"step1\");\n\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10)\n                .sort(HumanTaskInstanceSearchDescriptor.PROCESS_INSTANCE_ID, Order.ASC);\n        List<HumanTaskInstance> humanTaskInstances = getProcessAPI()\n                .searchPendingTasksForUser(user.getId(), searchOptionsBuilder.done()).getResult();\n        assertNotNull(\n                \"searchPendingTasksForUser give a null result for userId:\" + user.getId() + \" search options:\"\n                        + searchOptionsBuilder.done(),\n                humanTaskInstances);\n\n        // timerValue is slower than startProcess time\n        // then we have 2 tasks\n        assertTrue(humanTaskInstances.size() > 0);\n\n        for (final HumanTaskInstance humanTaskInstance : humanTaskInstances) {\n            if (humanTaskInstance.getRootContainerId() == step1.getRootContainerId()) {\n                // even if step1WithTimer is fired\n                // the start event should be the first one\n                assertEquals(\"step1\", humanTaskInstance.getName());\n            }\n        }\n\n        // Verify that the start without trigger, and the start with a timer are started\n        // wait for process instance creation\n        waitForUserTask(\"step1WithTimer\");\n        humanTaskInstances = getProcessAPI().searchPendingTasksForUser(user.getId(), searchOptionsBuilder.done())\n                .getResult();\n        assertEquals(2, humanTaskInstances.size());\n        assertEquals(\"step1\", humanTaskInstances.get(0).getName());\n        assertEquals(\"step1WithTimer\", humanTaskInstances.get(1).getName());\n        final ProcessInstance processInstanceWithTimer = getProcessAPI()\n                .getProcessInstance(humanTaskInstances.get(1).getRootContainerId());\n        assertEquals(0, processInstanceWithTimer.getStartedBy());\n        assertNotEquals(startProcess.getId(), processInstanceWithTimer.getId());\n\n        // Verify that the start without trigger, the start with a timer, and the start with signal are started\n        getProcessAPI().sendSignal(\"signalName\");\n        waitForUserTask(\"step1WithSignal\");\n\n        humanTaskInstances = getProcessAPI().searchPendingTasksForUser(user.getId(), searchOptionsBuilder.done())\n                .getResult();\n        assertEquals(3, humanTaskInstances.size());\n        assertEquals(\"step1\", humanTaskInstances.get(0).getName());\n        assertEquals(\"step1WithTimer\", humanTaskInstances.get(1).getName());\n        assertEquals(\"step1WithSignal\", humanTaskInstances.get(2).getName());\n        final ProcessInstance processInstanceWithSignal = getProcessAPI()\n                .getProcessInstance(humanTaskInstances.get(2).getRootContainerId());\n        assertEquals(0, processInstanceWithSignal.getStartedBy());\n        assertNotEquals(processInstanceWithTimer.getId(), processInstanceWithSignal.getId());\n\n        // Verify all start are started\n        getProcessAPI().sendMessage(\"message\", new ExpressionBuilder().createConstantStringExpression(PROCESS_NAME),\n                new ExpressionBuilder().createConstantStringExpression(\"startEventWithMessage\"), null);\n        waitForUserTask(\"step1WithMessage\");\n        humanTaskInstances = getProcessAPI().searchPendingTasksForUser(user.getId(), searchOptionsBuilder.done())\n                .getResult();\n        assertEquals(4, humanTaskInstances.size());\n        assertEquals(\"step1\", humanTaskInstances.get(0).getName());\n        assertEquals(\"step1WithTimer\", humanTaskInstances.get(1).getName());\n        assertEquals(\"step1WithSignal\", humanTaskInstances.get(2).getName());\n        assertEquals(\"step1WithMessage\", humanTaskInstances.get(3).getName());\n        final ProcessInstance processInstanceWithMessage = getProcessAPI()\n                .getProcessInstance(humanTaskInstances.get(3).getRootContainerId());\n        assertEquals(0, processInstanceWithMessage.getStartedBy());\n        assertNotEquals(processInstanceWithSignal.getId(), processInstanceWithMessage.getId());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/event/TimerBoundaryEventIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.event;\n\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.junit.Test;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class TimerBoundaryEventIT extends AbstractEventIT {\n\n    @Test\n    public void nonInterruptingNotTrigerAfterInterruptingTriggering() throws Exception {\n        // deploy process\n        final String taskWithBoundaryName = \"taskWithBoundary\";\n        final int nonInterruptingTimer = 30 * 1000; // the timer will be aborted, so no problem if it's a hight value\n        final String interruptExceptionTaskName = \"afterInterrupt\";\n        final String nonInterruptExceptionTaskName = \"afterNonInterrupt\";\n        final String interruptTimerName = \"interruptTimer\";\n        final String nonInterruptTimerName = \"nonInterruptTimer\";\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithInterruptingAndNonInterruptingTimer(100,\n                nonInterruptingTimer,\n                taskWithBoundaryName,\n                interruptExceptionTaskName, nonInterruptExceptionTaskName, interruptTimerName, nonInterruptTimerName);\n\n        // start process and wait for task in aborted state\n        final ProcessInstance processInstance = getProcessAPI().startProcess(user.getId(), processDefinition.getId());\n        waitForFlowNodeInState(processInstance, taskWithBoundaryName, TestStates.ABORTED, false);\n\n        // verify that non-interrupting timer was aborted\n        waitForFlowNodeInState(processInstance, nonInterruptTimerName, TestStates.ABORTED, false);\n\n        // verify that exception flow was taken\n        waitForUserTask(interruptExceptionTaskName);\n\n        getProcessAPI().deleteProcessInstance(processInstance.getId());\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void interruptingTrigerAfterNonInterruptingTriggering() throws Exception {\n        // deploy process\n        final String taskWithBoundaryName = \"taskWithBoundary\";\n        final String interruptExceptionTaskName = \"afterInterrupt\";\n        final String nonInterruptExceptionTaskName = \"afterNonInterrupt\";\n        final String interruptTimerName = \"interruptTimer\";\n        final String nonInterruptTimerName = \"nonInterruptTimer\";\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithInterruptingAndNonInterruptingTimer(1000,\n                100, taskWithBoundaryName,\n                interruptExceptionTaskName,\n                nonInterruptExceptionTaskName, interruptTimerName, nonInterruptTimerName);\n\n        // start process and wait for task in ready state\n        final ProcessInstance processInstance = getProcessAPI().startProcess(user.getId(), processDefinition.getId());\n        waitForUserTask(taskWithBoundaryName);\n\n        // verify that exception flow was taken for non-interrupting event\n        waitForUserTask(nonInterruptExceptionTaskName);\n\n        // wait for aborted state\n        waitForFlowNodeInState(processInstance, taskWithBoundaryName, TestStates.ABORTED, false);\n        // verify that interrupting event was triggered\n        waitForUserTask(interruptExceptionTaskName);\n\n        getProcessAPI().deleteProcessInstance(processInstance.getId());\n        disableAndDeleteProcess(processDefinition);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/event/TimerEventIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.event;\n\nimport static org.junit.Assert.*;\n\nimport java.util.Date;\nimport java.util.Iterator;\nimport java.util.List;\n\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.flownode.EventCriterion;\nimport org.bonitasoft.engine.bpm.flownode.EventInstance;\nimport org.bonitasoft.engine.bpm.flownode.IntermediateCatchEventInstance;\nimport org.bonitasoft.engine.bpm.flownode.TimerType;\nimport org.bonitasoft.engine.bpm.process.ActivationState;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceCriterion;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.exception.RetrieveException;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.junit.Test;\n\npublic class TimerEventIT extends TestWithUser {\n\n    @Test\n    public void timerIntermediateCatchEventDuration() throws Exception {\n        final String step1Name = \"step1\";\n        final String step2Name = \"step2\";\n        final Expression timerExpression = new ExpressionBuilder().createConstantLongExpression(1000); // the timer intermediate catch event will wait one\n                                                                                                       // second\n        final ProcessDefinition definition = deployProcessWithTimerIntermediateCatchEventAndUserTask(TimerType.DURATION,\n                timerExpression, step1Name,\n                step2Name);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId());\n        waitForUserTaskAndExecuteIt(processInstance, step1Name, user);\n\n        waitForEventInWaitingState(processInstance, \"intermediateCatchEvent\");\n        final long processInstanceId = processInstance.getId();\n        EventInstance eventInstance = getEventInstance(processInstanceId, \"intermediateCatchEvent\");\n        checkIntermediateCatchEventInstance(eventInstance, \"intermediateCatchEvent\", TestStates.WAITING);\n        // wait trigger activation\n        int cnt = 0;\n\n        // BS-9586 : for mysql, we wait longer\n        while (cnt < 10 && eventInstance != null) {\n            Thread.sleep(1000);\n            eventInstance = getEventInstance(processInstanceId, \"intermediateCatchEvent\");\n            cnt++;\n        }\n        assertNull(eventInstance);// finished\n\n        waitForUserTask(processInstance, step2Name);\n\n        disableAndDeleteProcess(definition);\n    }\n\n    @Test\n    public void timerIntermediateCatchEventDate() throws Exception {\n        final String step1Name = \"step1\";\n        final String step2Name = \"step2\";\n        final long expectedDate = System.currentTimeMillis() + 5000;\n        final Expression timerExpression = new ExpressionBuilder()\n                .createGroovyScriptExpression(\"testTimerIntermediateCatchEventDate\", \"return new Date(\"\n                        + expectedDate + \"l)\", Date.class.getName()); // the timer intermediate catch\n        // event will wait one second\n        final ProcessDefinition definition = deployProcessWithTimerIntermediateCatchEventAndUserTask(TimerType.DATE,\n                timerExpression, step1Name,\n                step2Name);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId());\n        waitForUserTaskAndExecuteIt(processInstance, step1Name, user);\n\n        waitForFlowNodeInState(processInstance, \"intermediateCatchEvent\", TestStates.WAITING, true);\n        final EventInstance eventInstance = getEventInstance(processInstance.getId(), \"intermediateCatchEvent\");\n        checkIntermediateCatchEventInstance(eventInstance, \"intermediateCatchEvent\", TestStates.WAITING);\n        // wait trigger activation\n        waitForUserTask(processInstance, step2Name);\n        final long now = System.currentTimeMillis();\n        assertTrue(\"Event has triggered too early !\" + (now - expectedDate), expectedDate <= now);\n\n        disableAndDeleteProcess(definition);\n    }\n\n    @Test\n    public void timerStartEventDate() throws Exception {\n        final String stepName = \"step1\";\n        final long expectedDate = System.currentTimeMillis() + 1000;\n        final Expression timerExpression = new ExpressionBuilder()\n                .createGroovyScriptExpression(\"testTimerStartEventDate\", \"return new Date(\" + expectedDate\n                        + \"l);\", Date.class.getName()); // the new instance must be\n        // created in one second\n        final ProcessDefinition definition = deployProcessWithTimerStartEventAndUserTask(TimerType.DATE,\n                timerExpression, stepName);\n\n        final List<ProcessInstance> processInstances = getProcessAPI().getProcessInstances(0, 10,\n                ProcessInstanceCriterion.CREATION_DATE_DESC);\n        assertTrue(processInstances.isEmpty());\n\n        // wait for process instance creation\n        waitForUserTask(stepName);\n\n        disableAndDeleteProcess(definition);\n    }\n\n    @Test\n    public void timerStartEventDuration() throws Exception {\n        final String stepName = \"step1\";\n        final Expression timerExpression = new ExpressionBuilder().createConstantLongExpression(1500); // the new instance must be created in one second\n        final ProcessDefinition definition = deployProcessWithTimerStartEventAndUserTask(TimerType.DURATION,\n                timerExpression, stepName);\n        waitForInitializingProcess();\n\n        waitForUserTask(stepName);\n        disableAndDeleteProcess(definition);\n    }\n\n    private EventInstance getEventInstance(final long processInstanceId, final String eventName)\n            throws RetrieveException {\n        final List<EventInstance> eventInstances = getProcessAPI().getEventInstances(processInstanceId, 0, 10,\n                EventCriterion.NAME_ASC);\n        EventInstance searchedEventInstance = null;\n        final Iterator<EventInstance> iterator = eventInstances.iterator();\n        while (iterator.hasNext() && searchedEventInstance == null) {\n            final EventInstance eventInstance = iterator.next();\n            if (eventInstance.getName().equals(eventName)) {\n                searchedEventInstance = eventInstance;\n            }\n        }\n        return searchedEventInstance;\n    }\n\n    private void checkIntermediateCatchEventInstance(final EventInstance eventInstance, final String eventName,\n            final TestStates state) {\n        assertTrue(eventInstance instanceof IntermediateCatchEventInstance);\n        checkEventInstance(eventInstance, eventName, state);\n    }\n\n    private void checkEventInstance(final EventInstance eventInstance, final String eventName, final TestStates state) {\n        assertEquals(eventName, eventInstance.getName());\n        assertEquals(state.getStateName(), eventInstance.getState());\n        // if(TestStates.getNormalFinalState(eventInstance).equals(state)) {\n        // assertTrue(eventInstance.getEndDate() > 0);\n        // }\n    }\n\n    private ProcessDefinition deployProcessWithTimerIntermediateCatchEventAndUserTask(final TimerType timerType,\n            final Expression timerValue,\n            final String step1Name, final String step2Name) throws Exception {\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My Process with start event\", \"1.0\")\n                .addActor(ACTOR_NAME).addDescription(\"Delivery all day and night long\").addStartEvent(\"startEvent\")\n                .addUserTask(step1Name, ACTOR_NAME)\n                .addIntermediateCatchEvent(\"intermediateCatchEvent\")\n                .addTimerEventTriggerDefinition(timerType, timerValue).addUserTask(step2Name, ACTOR_NAME)\n                .addEndEvent(\"endEvent\").addTransition(\"startEvent\", step1Name)\n                .addTransition(step1Name, \"intermediateCatchEvent\")\n                .addTransition(\"intermediateCatchEvent\", step2Name).addTransition(step2Name, \"endEvent\").getProcess();\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(definition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n        return definition;\n    }\n\n    private ProcessDefinition deployProcessWithTimerStartEventAndUserTask(final TimerType timerType,\n            final Expression timerValue, final String stepName)\n            throws Exception {\n        final String delivery = \"Delivery men\";\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My Process with start event\", \"1.0\")\n                .addActor(delivery).addDescription(\"Delivery all day and night long\").addStartEvent(\"startEvent\")\n                .addTimerEventTriggerDefinition(timerType, timerValue).addUserTask(stepName, delivery)\n                .addEndEvent(\"endEvent\")\n                .addTransition(\"startEvent\", stepName).addTransition(stepName, \"endEvent\").getProcess();\n\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(designProcessDefinition, delivery, user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(definition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n        return definition;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/event/TimerEventSubProcessIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.event;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.assertEquals;\n\nimport java.io.Serializable;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\n\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.data.DataInstance;\nimport org.bonitasoft.engine.bpm.data.DataNotFoundException;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.TimerType;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.StartEventDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.SubProcessDefinitionBuilder;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.junit.Test;\n\n/**\n * @author Baptiste Mesta\n * @author Elias Ricken de Medeiros\n */\npublic class TimerEventSubProcessIT extends AbstractEventIT {\n\n    @Test\n    public void timerEventSubProcessTriggered() throws Exception {\n        // given\n        final int timerDuration = 2000;\n        final ProcessDefinition process = deployAndEnableProcessWithTimerEventSubProcess(timerDuration);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(process.getId());\n\n        // when\n        final ActivityInstance subStep = waitForUserTaskAndGetIt(processInstance, \"subStep\");\n        final ProcessInstance subProcInst = getProcessAPI().getProcessInstance(subStep.getParentProcessInstanceId());\n\n        final Date processStartDate = processInstance.getStartDate();\n        assertThat(subProcInst.getStartDate()).as(\n                String.format(\"process started at %s should trigger subprocess at %s (+ %d ms) \",\n                        formatedDate(processStartDate),\n                        formatedDate(subProcInst.getStartDate()), timerDuration))\n                .isAfter(processStartDate);\n\n        // cleanup\n        assignAndExecuteStep(subStep, user);\n        waitForProcessToFinish(subProcInst);\n        disableAndDeleteProcess(process.getId());\n    }\n\n    private String formatedDate(final Date date) {\n        final SimpleDateFormat dt = new SimpleDateFormat(DATE_FORMAT_WITH_MS);\n        return dt.format(date);\n    }\n\n    @Test\n    public void timerEventSubProcessNotTriggered() throws Exception {\n        final int timerDuration = 6000;\n        final ProcessDefinition process = deployAndEnableProcessWithTimerEventSubProcess(timerDuration);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(process.getId());\n        waitForUserTaskAndExecuteIt(processInstance, \"step1\", user);\n        waitForProcessToFinish(processInstance);\n        Thread.sleep(timerDuration);\n\n        disableAndDeleteProcess(process.getId());\n    }\n\n    // added after problems with job name\n    @Test\n    public void testCreateSeveralInstances() throws Exception {\n        final int timerDuration = 500;\n        final ProcessDefinition process = deployAndEnableProcessWithTimerEventSubProcess(timerDuration);\n        final ProcessInstance processInstance1 = getProcessAPI().startProcess(process.getId());\n        final ProcessInstance processInstance2 = getProcessAPI().startProcess(process.getId());\n        waitForUserTask(processInstance1, \"subStep\");\n        waitForUserTask(processInstance2, \"subStep\");\n\n        disableAndDeleteProcess(process.getId());\n    }\n\n    @Test\n    public void subProcessCanAccessParentData() throws Exception {\n        final int timerDuration = 2000;\n        final ProcessDefinition process = deployAndEnableProcessWithTimerEventSubProcessAndData(timerDuration);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(process.getId());\n        final ActivityInstance subStep = waitForUserTaskAndGetIt(processInstance, \"subStep\");\n\n        final ProcessInstance subProcInst = getProcessAPI().getProcessInstance(subStep.getParentProcessInstanceId());\n        checkProcessDataInstance(\"count\", subProcInst.getId(), 1);\n        checkProcessDataInstance(\"content\", subProcInst.getId(), \"childVar\");\n        checkProcessDataInstance(\"value\", subProcInst.getId(), 10.0);\n        checkProcessDataInstance(\"content\", processInstance.getId(), \"parentVar\");\n        checkActivityDataInstance(\"content\", subStep.getId(), \"childActivityVar\");\n\n        assignAndExecuteStep(subStep, user.getId());\n        waitForProcessToFinish(subProcInst);\n        waitForProcessToBeInState(processInstance, ProcessInstanceState.ABORTED);\n\n        disableAndDeleteProcess(process.getId());\n    }\n\n    private void checkProcessDataInstance(final String dataName, final long processInstanceId,\n            final Serializable expectedValue) throws DataNotFoundException {\n        final DataInstance processDataInstance;\n        processDataInstance = getProcessAPI().getProcessDataInstance(dataName, processInstanceId);\n        assertEquals(expectedValue, processDataInstance.getValue());\n    }\n\n    private void checkActivityDataInstance(final String dataName, final long activityInstanceId,\n            final Serializable expectedValue)\n            throws DataNotFoundException {\n        final DataInstance activityDataInstance;\n        activityDataInstance = getProcessAPI().getActivityDataInstance(dataName, activityInstanceId);\n        assertEquals(expectedValue, activityDataInstance.getValue());\n    }\n\n    @Test\n    public void timerEventSubProcInsideTargetCallActivity() throws Exception {\n        final ProcessDefinition targetProcess = deployAndEnableProcessWithTimerEventSubProcess(2000);\n        final ProcessDefinition callerProcess = deployAndEnableProcessWithCallActivity(targetProcess.getName(),\n                targetProcess.getVersion());\n        final ProcessInstance processInstance = getProcessAPI().startProcess(callerProcess.getId());\n        final ActivityInstance step1 = waitForUserTaskAndGetIt(processInstance, \"step1\");\n        final ActivityInstance subStep = waitForUserTaskAndGetIt(processInstance, \"subStep\");\n        final ProcessInstance calledProcInst = getProcessAPI().getProcessInstance(step1.getParentProcessInstanceId());\n        final ProcessInstance subProcInst = getProcessAPI().getProcessInstance(subStep.getParentProcessInstanceId());\n\n        waitForFlowNodeInState(processInstance, \"step1\", TestStates.ABORTED, true);\n        assignAndExecuteStep(subStep, user);\n        waitForProcessToFinish(subProcInst);\n        waitForProcessToBeInState(calledProcInst, ProcessInstanceState.ABORTED);\n\n        waitForUserTaskAndExecuteIt(processInstance, \"step2\", user);\n        waitForProcessToFinish(processInstance);\n\n        disableAndDeleteProcess(callerProcess.getId());\n        disableAndDeleteProcess(targetProcess.getId());\n    }\n\n    @Test\n    public void should_be_able_to_init_timer_from_data_in_parent() throws Exception {\n        //given\n        ProcessDefinitionBuilder parentProcessBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"ProcessWithESPTimerBasedOnData\", \"1.0\");\n        parentProcessBuilder.addActor(ACTOR_NAME);\n        parentProcessBuilder.addUserTask(\"userTask\", ACTOR_NAME);\n        parentProcessBuilder.addLongData(\"timeToWait\", new ExpressionBuilder().createConstantLongExpression(1000));\n        //construct sub process\n        SubProcessDefinitionBuilder subProcessBuilder = parentProcessBuilder\n                .addSubProcess(\"subProcessToStartWithTimer\", true).getSubProcessBuilder();\n        StartEventDefinitionBuilder startEventDefinitionBuilder = subProcessBuilder.addStartEvent(\"timerStart\");\n        startEventDefinitionBuilder.addTimerEventTriggerDefinition(TimerType.DURATION,\n                new ExpressionBuilder().createDataExpression(\"timeToWait\", Long.class.getName()));\n        subProcessBuilder.addUserTask(\"userTaskInSubProcess\", ACTOR_NAME);\n        subProcessBuilder.addEndEvent(\"endSubProcess\");\n        subProcessBuilder.addTransition(\"timerStart\", \"userTaskInSubProcess\");\n        subProcessBuilder.addTransition(\"userTaskInSubProcess\", \"endSubProcess\");\n        DesignProcessDefinition processDefinition1 = parentProcessBuilder.done();\n        BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(processDefinition1);\n        ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchiveBuilder.done(), ACTOR_NAME,\n                user);\n        ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        //when\n        waitForUserTask(\"userTaskInSubProcess\");\n        //then\n        disableAndDeleteProcess(processDefinition);\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/filter/user/UserFilterIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.filter.user;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.TestWithTechnicalUser;\nimport org.bonitasoft.engine.bpm.comment.Comment;\nimport org.bonitasoft.engine.bpm.comment.SearchCommentsDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstance;\nimport org.bonitasoft.engine.bpm.flownode.GatewayType;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.process.ActivationState;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder;\nimport org.bonitasoft.engine.connectors.VariableStorage;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.identity.Group;\nimport org.bonitasoft.engine.identity.Role;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.test.check.CheckNbAssignedTaskOf;\nimport org.bonitasoft.engine.test.check.CheckNbPendingTaskOf;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Baptiste Mesta\n * @author Yanyan Liu\n */\npublic class UserFilterIT extends TestWithTechnicalUser {\n\n    private static final String JOHN = \"john\";\n\n    private static final String JACK = \"jack\";\n\n    private static final String JAMES = \"james\";\n\n    private User john;\n\n    private User jack;\n\n    private User james;\n\n    @Override\n    @Before\n    public void before() throws Exception {\n        super.before();\n        john = createUser(JOHN, \"bpm\");\n        jack = createUser(JACK, \"bpm\");\n        james = createUser(JAMES, \"bpm\");\n        logout();\n        loginOnDefaultTenantWith(JOHN, \"bpm\");\n    }\n\n    @Override\n    @After\n    public void after() throws Exception {\n        deleteUser(JOHN);\n        deleteUser(JACK);\n        deleteUser(JAMES);\n        VariableStorage.clearAll();\n        super.after();\n    }\n\n    @Test\n    public void filterTask() throws Exception {\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithUserFilter\", \"1.0\");\n        designProcessDefinition.addActor(ACTOR_NAME).addDescription(\"ACTOR_NAME all day and night long\");\n        designProcessDefinition.addAutomaticTask(\"step1\");\n        final UserTaskDefinitionBuilder addUserTask = designProcessDefinition.addUserTask(\"step2\", ACTOR_NAME);\n        addUserTask.addUserFilter(\"test\", \"org.bonitasoft.engine.filter.user.testFilter\", \"1.0\").addInput(\"userId\",\n                new ExpressionBuilder().createConstantLongExpression(jack.getId()));\n        designProcessDefinition.addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition processDefinition = deployProcessWithTestFilter(designProcessDefinition, ACTOR_NAME,\n                john, \"TestFilter\");\n\n        getProcessAPI().startProcess(processDefinition.getId());\n\n        assertTrue(new CheckNbPendingTaskOf(getProcessAPI(), 50, 5000, false, 1, jack).waitUntil());\n        List<HumanTaskInstance> pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null);\n        assertEquals(0, pendingTasks.size());\n        pendingTasks = getProcessAPI().getPendingHumanTaskInstances(jack.getId(), 0, 10, null);\n        assertEquals(1, pendingTasks.size());\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    /*\n     * Task must be in failed state even for any exception thrown is the user filter\n     */\n    @Test\n    public void filterTaskWithUserFilterNotFound() throws Exception {\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithUserFilter\", \"1.0\");\n        designProcessDefinition.addActor(ACTOR_NAME).addDescription(\"ACTOR_NAME all day and night long\");\n        designProcessDefinition.addAutomaticTask(\"step1\");\n        final UserTaskDefinitionBuilder addUserTask = designProcessDefinition.addUserTask(\"step2\", ACTOR_NAME);\n        addUserTask.addUserFilter(\"test\", \"org.bonitasoft.engine.filter.user.testFilterWithClassNotFound\", \"1.0\")\n                .addInput(\"userId\",\n                        new ExpressionBuilder().createConstantLongExpression(jack.getId()));\n        designProcessDefinition.addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition processDefinition = deployProcessWithTestFilter(designProcessDefinition, ACTOR_NAME,\n                john, \"TestFilterWithClassNotFound\");\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        waitForTaskToFail(processInstance);\n\n        disableAndDeleteProcess(processDefinition.getId());\n    }\n\n    /*\n     * Task must be in failed state even for any exception thrown is the user filter\n     */\n    @Test\n    public void filterTaskWithUserFilterThatThrowException() throws Exception {\n        final ProcessDefinitionBuilder processWithRuntime = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithUserFilterRuntimeException\", \"1.0\");\n        processWithRuntime.addActor(ACTOR_NAME).addDescription(\"ACTOR_NAME all day and night long\");\n        processWithRuntime.addAutomaticTask(\"step1\");\n        final UserTaskDefinitionBuilder addUserTask = processWithRuntime.addUserTask(\"step2\", ACTOR_NAME);\n        addUserTask.addUserFilter(\"test\", \"org.bonitasoft.engine.filter.user.testFilterThatThrowException\", \"1.0\")\n                .addInput(\"exception\",\n                        new ExpressionBuilder().createConstantStringExpression(\"runtime\"));\n        processWithRuntime.addTransition(\"step1\", \"step2\");\n        final ProcessDefinition processDefinitionWithRuntime = deployProcessWithTestFilter(processWithRuntime,\n                ACTOR_NAME, john,\n                \"TestFilterThatThrowException\");\n        final ProcessDefinitionBuilder processWithException = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithUserFilterNormalException\", \"1.0\");\n        processWithException.addActor(ACTOR_NAME).addDescription(\"ACTOR_NAME all day and night long\");\n        processWithException.addAutomaticTask(\"step1\");\n        final UserTaskDefinitionBuilder addUserTask2 = processWithException.addUserTask(\"step2\", ACTOR_NAME);\n        addUserTask2.addUserFilter(\"test\", \"org.bonitasoft.engine.filter.user.testFilterThatThrowException\", \"1.0\")\n                .addInput(\"exception\",\n                        new ExpressionBuilder().createConstantStringExpression(\"normal\"));\n        processWithException.addTransition(\"step1\", \"step2\");\n        final ProcessDefinition processDefinitionWithException = deployProcessWithTestFilter(processWithException,\n                ACTOR_NAME, john,\n                \"TestFilterThatThrowException\");\n        final ProcessDefinitionBuilder processWithNoClassDef = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithUserFilterNoClassDef\", \"1.0\");\n        processWithNoClassDef.addActor(ACTOR_NAME).addDescription(\"ACTOR_NAME all day and night long\");\n        processWithNoClassDef.addAutomaticTask(\"step1\");\n        final UserTaskDefinitionBuilder addUserTask3 = processWithNoClassDef.addUserTask(\"step2\", ACTOR_NAME);\n        addUserTask3.addUserFilter(\"test\", \"org.bonitasoft.engine.filter.user.testFilterThatThrowNoClassDef\", \"1.0\")\n                .addInput(\"exception\",\n                        new ExpressionBuilder().createConstantStringExpression(\"normal\"));\n        processWithNoClassDef.addTransition(\"step1\", \"step2\");\n        final ProcessDefinition processDefinitionNoClassDef = deployProcessWithTestFilter(processWithNoClassDef,\n                ACTOR_NAME, john,\n                \"TestFilterThatThrowNoClassDef\");\n\n        final ProcessInstance processInstanceException = getProcessAPI()\n                .startProcess(processDefinitionWithException.getId());\n        final ProcessInstance processInstanceRuntime = getProcessAPI()\n                .startProcess(processDefinitionWithRuntime.getId());\n        final ProcessInstance processInstanceNoClassDef = getProcessAPI()\n                .startProcess(processDefinitionNoClassDef.getId());\n\n        waitForTaskToFail(processInstanceRuntime);\n        waitForTaskToFail(processInstanceException);\n        waitForTaskToFail(processInstanceNoClassDef);\n\n        disableAndDeleteProcess(processDefinitionWithRuntime);\n        disableAndDeleteProcess(processDefinitionWithException);\n        disableAndDeleteProcess(processDefinitionNoClassDef);\n    }\n\n    @Test\n    public void filterTaskWithAutoAssign() throws Exception {\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithUserFilterWithAutoAssign\", \"1.0\");\n        designProcessDefinition.addActor(ACTOR_NAME).addDescription(\"ACTOR_NAME all day and night long\");\n        designProcessDefinition.addAutomaticTask(\"step1\");\n        final UserTaskDefinitionBuilder addUserTask = designProcessDefinition.addUserTask(\"step2\", ACTOR_NAME);\n        addUserTask.addUserFilter(\"test\", \"org.bonitasoft.engine.filter.user.testFilterWithAutoAssign\", \"1.0\").addInput(\n                \"userId\",\n                new ExpressionBuilder().createConstantLongExpression(jack.getId()));\n        addUserTask\n                .addDisplayName(new ExpressionBuilder().createConstantStringExpression(\"A task to test user filter\"));\n        designProcessDefinition.addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition processDefinition = deployProcessWithTestFilter(designProcessDefinition, ACTOR_NAME,\n                john, \"TestFilterWithAutoAssign\");\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        assertTrue(new CheckNbAssignedTaskOf(getProcessAPI(), 50, 5000, false, 1, jack).waitUntil());\n        List<HumanTaskInstance> tasks = getProcessAPI().getAssignedHumanTaskInstances(john.getId(), 0, 10, null);\n        assertEquals(0, tasks.size());\n        tasks = getProcessAPI().getAssignedHumanTaskInstances(jack.getId(), 0, 10, null);\n        assertEquals(1, tasks.size());\n        final SearchResult<Comment> commentSearchResult = getProcessAPI().searchComments(\n                new SearchOptionsBuilder(0, 10)\n                        .filter(SearchCommentsDescriptor.PROCESS_INSTANCE_ID, processInstance.getId()).done());\n        assertThat(commentSearchResult.getResult()).hasSize(1);\n        assertThat(commentSearchResult.getResult().get(0).getContent())\n                .isEqualTo(\"The task \\\"A task to test user filter\\\" is now assigned to jack\");\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void filterTaskWithNullInput() throws Exception {\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithUserFilterUsingActorName\", \"1.0\");\n        designProcessDefinition.addActor(ACTOR_NAME).addDescription(\"ACTOR_NAME all day and night long\");\n        designProcessDefinition.addAutomaticTask(\"step1\");\n        final UserTaskDefinitionBuilder addUserTask = designProcessDefinition.addUserTask(\"step2\", ACTOR_NAME);\n        addUserTask.addUserFilter(\"test\", \"org.bonitasoft.engine.filter.user.testFilterUsingActorName\", \"1.0\")\n                .addInput(\"userIds\", null);\n        designProcessDefinition.addTransition(\"step1\", \"step2\");\n        assertThatExceptionOfType(BonitaException.class)\n                .isThrownBy(() -> deployProcessWithTestFilter(designProcessDefinition, ACTOR_NAME, john,\n                        \"TestFilterUsingActorName\"))\n                .withRootCauseInstanceOf(InvalidProcessDefinitionException.class);\n    }\n\n    @Test\n    public void filterTaskUsingFilterName() throws Exception {\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithUserFilterUsingActorName\", \"1.0\");\n        designProcessDefinition.addActor(ACTOR_NAME).addDescription(\"ACTOR_NAME all day and night long\");\n        designProcessDefinition.addAutomaticTask(\"step1\");\n        final UserTaskDefinitionBuilder addUserTask = designProcessDefinition.addUserTask(\"step2\", ACTOR_NAME);\n        addUserTask.addUserFilter(\"test\", \"org.bonitasoft.engine.filter.user.testFilterUsingActorName\", \"1.0\").addInput(\n                \"userIds\",\n                new ExpressionBuilder().createGroovyScriptExpression(\"myScript\",\n                        \"['\" + ACTOR_NAME + \"':\" + james.getId() + \"l,'notACTOR_NAME':\" + jack.getId()\n                                + \"l]\",\n                        Map.class.getName()));\n        designProcessDefinition.addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition processDefinition = deployProcessWithTestFilter(designProcessDefinition, ACTOR_NAME,\n                john, \"TestFilterUsingActorName\");\n\n        getProcessAPI().startProcess(processDefinition.getId());\n\n        assertTrue(new CheckNbPendingTaskOf(getProcessAPI(), 50, 6000, false, 1, james).waitUntil());\n        List<HumanTaskInstance> pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null);\n        assertEquals(0, pendingTasks.size());\n        pendingTasks = getProcessAPI().getPendingHumanTaskInstances(jack.getId(), 0, 10, null);\n        assertEquals(0, pendingTasks.size());\n        pendingTasks = getProcessAPI().getPendingHumanTaskInstances(james.getId(), 0, 10, null);\n        assertEquals(1, pendingTasks.size());\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void updateUserFilterAfterAUserDeletion() throws Exception {\n        final Group group = createGroup(\"group1\");\n        final Role role = createRole(\"role1\");\n        createUserMembership(jack.getUserName(), \"role1\", \"group1\");\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithUserFilterWithAutoAssign\", \"1.0\");\n        processBuilder.addActor(ACTOR_NAME);\n        processBuilder.addAutomaticTask(\"step1\");\n        final UserTaskDefinitionBuilder addUserTask = processBuilder.addUserTask(\"step2\", ACTOR_NAME);\n        addUserTask.addUserFilter(\"test\", \"org.bonitasoft.engine.filter.user.GroupUserFilter\", \"1.0\").addInput(\n                \"groupId\",\n                new ExpressionBuilder().createConstantLongExpression(group.getId()));\n        processBuilder.addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition processDefinition = deployProcessWithTestFilter(processBuilder, ACTOR_NAME, john,\n                \"GroupUserFilter\");\n        getProcessAPI().startProcess(processDefinition.getId());\n\n        assertTrue(new CheckNbAssignedTaskOf(getProcessAPI(), 50, 5000, false, 1, jack).waitUntil());\n        List<HumanTaskInstance> tasks = getProcessAPI().getAssignedHumanTaskInstances(jack.getId(), 0, 10, null);\n        assertEquals(1, tasks.size());\n        getIdentityAPI().deleteUser(jack.getId());\n        createUserMembership(john.getUserName(), \"role1\", \"group1\");\n        tasks = getProcessAPI().getAssignedHumanTaskInstances(jack.getId(), 0, 10, null);\n        assertEquals(1, tasks.size());\n        getProcessAPI().updateActorsOfUserTask(tasks.get(0).getId());\n        tasks = getProcessAPI().getAssignedHumanTaskInstances(jack.getId(), 0, 10, null);\n        assertEquals(0, tasks.size());\n        tasks = getProcessAPI().getAssignedHumanTaskInstances(john.getId(), 0, 10, null);\n        assertEquals(1, tasks.size());\n\n        deleteGroups(group);\n        deleteRoles(role);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test(expected = UpdateException.class)\n    public void unableToUpdateActorsOnAGateway() throws Exception {\n        final Expression scriptExpression = new ExpressionBuilder().createGroovyScriptExpression(\"mycondition\",\n                \"fzdfsdfsdfsdfsdf\", Boolean.class.getName());\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process_with_exclusive_gateway\", PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addAutomaticTask(\"step1\").addUserTask(\"step2\", ACTOR_NAME).addUserTask(\"step3\", ACTOR_NAME)\n                .addGateway(\"gateway1\", GatewayType.EXCLUSIVE)\n                .addTransition(\"step1\", \"gateway1\").addTransition(\"gateway1\", \"step2\", scriptExpression)\n                .addDefaultTransition(\"gateway1\", \"step3\").getProcess();\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                john);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n        final FlowNodeInstance failFlowNodeInstance = waitForFlowNodeInFailedState(processInstance);\n        assertEquals(\"gateway1\", failFlowNodeInstance.getName());\n        try {\n            getProcessAPI().updateActorsOfUserTask(failFlowNodeInstance.getId());\n        } finally {\n            disableAndDeleteProcess(processDefinition);\n        }\n    }\n\n    @Test\n    public void doNotUpateAHumanTaskIfNoUserFilterIsDefined() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"doNotUpateAHumanTaskIfNoUserFilterIsDefined\", \"1.0\");\n        processBuilder.addActor(ACTOR_NAME);\n        processBuilder.addAutomaticTask(\"step1\");\n        processBuilder.addUserTask(\"step2\", ACTOR_NAME);\n        processBuilder.addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME,\n                john);\n        getProcessAPI().startProcess(processDefinition.getId());\n\n        waitForUserTask(\"step2\");\n        List<HumanTaskInstance> tasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10,\n                ActivityInstanceCriterion.DEFAULT);\n        assertEquals(1, tasks.size());\n        final HumanTaskInstance taskBefore = tasks.get(0);\n        getProcessAPI().updateActorsOfUserTask(tasks.get(0).getId());\n        tasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, ActivityInstanceCriterion.DEFAULT);\n        assertEquals(1, tasks.size());\n        assertEquals(taskBefore, tasks.get(0));\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void updateUserFilterAfterAUserAdd() throws Exception {\n        final Group group = createGroup(\"group1\");\n        final Role role = createRole(\"role1\");\n        createUserMembership(jack.getUserName(), \"role1\", \"group1\");\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"updateUserFilterAfterAUserAdd\", \"1.0\");\n        processBuilder.addActor(ACTOR_NAME);\n        processBuilder.addAutomaticTask(\"step1\");\n        final UserTaskDefinitionBuilder addUserTask = processBuilder.addUserTask(\"step2\", ACTOR_NAME);\n        addUserTask.addUserFilter(\"test\", \"org.bonitasoft.engine.filter.user.GroupUserFilter\", \"1.0\").addInput(\n                \"groupId\",\n                new ExpressionBuilder().createConstantLongExpression(group.getId()));\n        processBuilder.addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition processDefinition = deployProcessWithTestFilter(processBuilder, ACTOR_NAME, john,\n                \"GroupUserFilter\");\n        getProcessAPI().startProcess(processDefinition.getId());\n\n        assertTrue(new CheckNbAssignedTaskOf(getProcessAPI(), 50, 5000, false, 1, jack).waitUntil());\n        List<HumanTaskInstance> tasks = getProcessAPI().getAssignedHumanTaskInstances(jack.getId(), 0, 10, null);\n        assertEquals(1, tasks.size());\n        createUserMembership(john.getUserName(), \"role1\", \"group1\");\n        tasks = getProcessAPI().getAssignedHumanTaskInstances(jack.getId(), 0, 10, null);\n        assertEquals(1, tasks.size());\n        getProcessAPI().updateActorsOfUserTask(tasks.get(0).getId());\n\n        tasks = getProcessAPI().getPendingHumanTaskInstances(jack.getId(), 0, 10, ActivityInstanceCriterion.DEFAULT);\n        assertEquals(1, tasks.size());\n        assertEquals(0, tasks.get(0).getAssigneeId());\n        tasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, ActivityInstanceCriterion.DEFAULT);\n        assertEquals(1, tasks.size());\n        assertEquals(0, tasks.get(0).getAssigneeId());\n\n        deleteGroups(group);\n        deleteRoles(role);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/form/FormMappingIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.form;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.io.Serializable;\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.bar.BarResource;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.form.FormMappingModelBuilder;\nimport org.bonitasoft.engine.bpm.process.ConfigurationState;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.V6FormDeployException;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.SubProcessDefinitionBuilder;\nimport org.bonitasoft.engine.exception.NotFoundException;\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.engine.page.PageURL;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class FormMappingIT extends TestWithUser {\n\n    private final Map<String, Serializable> context = Collections.emptyMap();\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void deployProcessesWithFormMappings() throws Exception {\n        ProcessDefinitionBuilder p1Builder = new ProcessDefinitionBuilder().createNewInstance(\"P1\", \"1.0\");\n        p1Builder.addUserTask(\"step1\", \"actor\").addUserTask(\"step2\", \"actor\");\n        p1Builder.addActor(\"actor\");\n        BusinessArchiveBuilder bar1 = new BusinessArchiveBuilder()\n                .createNewBusinessArchive()\n                .setProcessDefinition(p1Builder.done())\n                .setFormMappings(\n                        FormMappingModelBuilder.buildFormMappingModel()\n                                .addProcessStartForm(\"processStartForm\", FormMappingTarget.URL)\n                                .addTaskForm(\"task1Form\", FormMappingTarget.INTERNAL, \"step1\")\n                                .addProcessOverviewForm(\"process1OverviewForm\", FormMappingTarget.INTERNAL).build());\n\n        ProcessDefinitionBuilder p2Builder = new ProcessDefinitionBuilder().createNewInstance(\"P2\", \"1.0\");\n        p2Builder.addUserTask(\"step1\", \"actor\").addUserTask(\"step2\", \"actor\");\n        p2Builder.addActor(\"actor\");\n        BusinessArchiveBuilder bar2 = new BusinessArchiveBuilder()\n                .createNewBusinessArchive()\n                .setProcessDefinition(p2Builder.done())\n                .setFormMappings(\n                        FormMappingModelBuilder.buildFormMappingModel()\n                                .addProcessStartForm(\"processStartForm\", FormMappingTarget.URL)\n                                .addTaskForm(\"task2Form\", FormMappingTarget.URL, \"step1\")\n                                .addProcessOverviewForm(null, FormMappingTarget.LEGACY).build());\n\n        ProcessDefinition p1 = getProcessAPI().deploy(bar1.done());\n\n        ProcessAPI processConfigurationAPI = getProcessAPI();\n\n        //get\n        FormMapping processStartForm1 = processConfigurationAPI.searchFormMappings(new SearchOptionsBuilder(0, 10)\n                .filter(FormMappingSearchDescriptor.TYPE, FormMappingType.PROCESS_START)\n                .filter(FormMappingSearchDescriptor.PROCESS_DEFINITION_ID, p1.getId())\n                .done()).getResult().get(0);\n        FormMapping processOverviewForm1 = processConfigurationAPI\n                .searchFormMappings(\n                        new SearchOptionsBuilder(0, 10)\n                                .filter(FormMappingSearchDescriptor.TYPE, FormMappingType.PROCESS_OVERVIEW)\n                                .filter(FormMappingSearchDescriptor.PROCESS_DEFINITION_ID, p1.getId())\n                                .done())\n                .getResult().get(0);\n        FormMapping step1Form1 = processConfigurationAPI.searchFormMappings(new SearchOptionsBuilder(0, 10)\n                .filter(FormMappingSearchDescriptor.TYPE, FormMappingType.TASK)\n                .filter(FormMappingSearchDescriptor.PROCESS_DEFINITION_ID, p1.getId())\n                .filter(FormMappingSearchDescriptor.TASK, \"step1\").done()).getResult().get(0);\n        FormMapping step2Form1 = processConfigurationAPI.searchFormMappings(new SearchOptionsBuilder(0, 10)\n                .filter(FormMappingSearchDescriptor.TYPE, FormMappingType.TASK)\n                .filter(FormMappingSearchDescriptor.PROCESS_DEFINITION_ID, p1.getId())\n                .filter(FormMappingSearchDescriptor.TASK, \"step2\").done()).getResult().get(0);\n\n        assertThat(processStartForm1.getProcessDefinitionId()).isEqualTo(p1.getId());\n        assertThat(processStartForm1.getPageId()).isNull();\n        assertThat(processStartForm1.getURL()).isEqualTo(\"processStartForm\");\n        assertThat(processStartForm1.getTarget()).isEqualTo(FormMappingTarget.URL);\n        assertThat(processOverviewForm1.getProcessDefinitionId()).isEqualTo(p1.getId());\n        assertThat(processOverviewForm1.getPageId()).isNull();\n        assertThat(processOverviewForm1.getURL()).isNull();\n        assertThat(processOverviewForm1.getTarget()).isEqualTo(FormMappingTarget.INTERNAL); // referenced page does not exists\n        assertThat(step1Form1.getProcessDefinitionId()).isEqualTo(p1.getId());\n        assertThat(step1Form1.getPageId()).isNull();\n        assertThat(step2Form1.getProcessDefinitionId()).isEqualTo(p1.getId());\n        assertThat(step2Form1.getURL()).isNull();\n        assertThat(step2Form1.getTarget()).isEqualTo(FormMappingTarget.NONE);\n\n        //search\n        SearchResult<FormMapping> formMappingSearchResult = processConfigurationAPI\n                .searchFormMappings(new SearchOptionsBuilder(0, 100).sort(\n                        FormMappingSearchDescriptor.ID, Order.DESC).done());\n        assertThat(formMappingSearchResult.getCount()).isEqualTo(4);\n        assertThat(formMappingSearchResult.getResult()).extracting(\"processDefinitionId\").containsExactly(\n                p1.getId(), p1.getId(), p1.getId(), p1.getId());\n        assertThat(formMappingSearchResult.getCount()).isEqualTo(4);\n        formMappingSearchResult = processConfigurationAPI\n                .searchFormMappings(new SearchOptionsBuilder(0, 100).sort(FormMappingSearchDescriptor.ID, Order.DESC)\n                        .filter(FormMappingSearchDescriptor.TASK, \"step1\").done());\n        assertThat(formMappingSearchResult.getCount()).isEqualTo(1);\n        formMappingSearchResult = processConfigurationAPI\n                .searchFormMappings(new SearchOptionsBuilder(0, 100).sort(FormMappingSearchDescriptor.ID, Order.DESC)\n                        .filter(FormMappingSearchDescriptor.TYPE, FormMappingType.PROCESS_START).done());\n        assertThat(formMappingSearchResult.getCount()).isEqualTo(1);\n\n        // to be allowed to access URL:\n        getProcessAPI().createProcessSupervisorForUser(p1.getId(), user.getId());\n\n        //resolve urls:\n        PageURL p1Instanciation = getPageAPI().resolvePageOrURL(\"process/P1/1.0\", context, true);\n        PageURL p1Overview = getPageAPI().resolvePageOrURL(\"processInstance/P1/1.0\", context, true);\n        PageURL p1step1Instanciation = getPageAPI().resolvePageOrURL(\"taskInstance/P1/1.0/step1\", context, true);\n        assertThat(p1Instanciation.getUrl()).isEqualTo(\"processStartForm\");\n        assertThat(p1Overview.getPageId()).isNull();\n        assertThat(p1step1Instanciation.getUrl()).isEqualTo(null);\n\n        getProcessAPI().deleteProcessDefinition(p1.getId());\n        assertThat(\n                processConfigurationAPI\n                        .searchFormMappings(new SearchOptionsBuilder(0, 100)\n                                .sort(FormMappingSearchDescriptor.ID, Order.DESC).done())\n                        .getResult())\n                .isEmpty();\n\n    }\n\n    @Test\n    public void deployProcessesWithV6FormMappingsFails() throws Exception {\n        // given:\n        ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(\"P2\", \"1.0\");\n        processBuilder.addUserTask(\"step1\", \"actor\").addUserTask(\"step2\", \"actor\");\n        BusinessArchiveBuilder bar = new BusinessArchiveBuilder()\n                .createNewBusinessArchive()\n                .setProcessDefinition(processBuilder.done())\n                .setFormMappings(\n                        FormMappingModelBuilder.buildFormMappingModel()\n                                .addProcessStartForm(\"processStartForm\", FormMappingTarget.URL)\n                                .addTaskForm(\"task2Form\", FormMappingTarget.URL, \"step1\")\n                                .addProcessOverviewForm(null, FormMappingTarget.LEGACY).build());\n\n        // then:\n        expectedException.expect(V6FormDeployException.class);\n        expectedException.expectMessage(\"The process contains v6 forms\");\n\n        // when:\n        getProcessAPI().deploy(bar.done());\n    }\n\n    @Test\n    public void resolvePageOrURLThrowsNotFoundExceptionForUndefinedFormMapping() throws Exception {\n        ProcessDefinitionBuilder p1Builder = new ProcessDefinitionBuilder().createNewInstance(\"CustomerSupport\", \"1.0\");\n        p1Builder.addActor(\"actor\").addUserTask(\"step\", \"actor\");\n        BusinessArchiveBuilder bar = new BusinessArchiveBuilder()\n                .createNewBusinessArchive().setProcessDefinition(p1Builder.done())\n                .setFormMappings(FormMappingModelBuilder.buildFormMappingModel()\n                        .addTaskForm(null, FormMappingTarget.UNDEFINED, \"step\").build());\n\n        ProcessDefinition processDefinition = deployProcess(bar.done());\n\n        expectedException.expect(NotFoundException.class);\n\n        // try to resolve url:\n        try {\n            getPageAPI().resolvePageOrURL(\"taskInstance/CustomerSupport/1.0/step\", Collections.emptyMap(), true);\n        } finally {\n            deleteProcess(processDefinition);\n        }\n    }\n\n    @Test\n    public void resolvePageOrURL_should_return_null_mapping_for_NONE() throws Exception {\n        ProcessDefinitionBuilder pBuilder = new ProcessDefinitionBuilder().createNewInstance(\"CustomerSupport\", \"1.0\");\n        pBuilder.addActor(\"actor\").addUserTask(\"step\", \"actor\");\n        BusinessArchiveBuilder bar = new BusinessArchiveBuilder()\n                .createNewBusinessArchive().setProcessDefinition(pBuilder.done())\n                .setFormMappings(FormMappingModelBuilder.buildFormMappingModel()\n                        .addTaskForm(null, FormMappingTarget.NONE, \"step\").build());\n\n        ProcessDefinition processDefinition = deployProcess(bar.done());\n\n        // to be allowed to access URL:\n        getProcessAPI().createProcessSupervisorForUser(processDefinition.getId(), user.getId());\n\n        // try to resolve url:\n        PageURL pageURL = getPageAPI().resolvePageOrURL(\"taskInstance/CustomerSupport/1.0/step\", context, true);\n\n        assertThat(pageURL.getPageId()).isNull();\n        assertThat(pageURL.getUrl()).isNull();\n\n        deleteProcess(processDefinition);\n    }\n\n    @Test\n    public void deployProcessWithInternalPagesIncludedShouldBeResolved() throws Exception {\n        Page custompage_globalpage = getPageAPI().createPage(\"globalPage.zip\",\n                createTestPageContent(\"custompage_globalpage\", \"Global page\", \"a global page\"));\n        ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(\"CustomerSupport\",\n                \"1.12\");\n        final String custompage_startProcessForm = \"custompage_startProcessForm\";\n        BusinessArchiveBuilder bar = new BusinessArchiveBuilder()\n                .createNewBusinessArchive()\n                .setProcessDefinition(processBuilder.done())\n                .setFormMappings(\n                        FormMappingModelBuilder.buildFormMappingModel()\n                                .addProcessStartForm(\"custompage_startProcessForm\", FormMappingTarget.INTERNAL)\n                                .addProcessOverviewForm(\"custompage_globalpage\", FormMappingTarget.INTERNAL).build())\n                .addExternalResource(\n                        new BarResource(\"customPages/custompage_startProcessForm.zip\",\n                                createTestPageContent(custompage_startProcessForm, \"kikoo\", \"LOL\")));\n\n        final ProcessDefinition processDefinition = deployProcess(bar.done());\n        final Page page = getPageAPI().getPageByNameAndProcessDefinitionId(custompage_startProcessForm,\n                processDefinition.getId());\n        assertThat(page.getId()).isNotNull();\n        assertThat(getProcessAPI().getProcessResolutionProblems(processDefinition.getId())).isEmpty();\n        getProcessAPI().enableProcess(processDefinition.getId());\n\n        // Should not throw Exception\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertThat(processDeploymentInfo.getConfigurationState()).isEqualTo(ConfigurationState.RESOLVED);\n\n        // to be allowed to access URL:\n        getProcessAPI().createProcessSupervisorForUser(processDefinition.getId(), user.getId());\n\n        final PageURL pageURLStart = getPageAPI().resolvePageOrURL(\"process/CustomerSupport/1.12\", context, true);\n        final PageURL pageURLOverview = getPageAPI().resolvePageOrURL(\"processInstance/CustomerSupport/1.12\", context,\n                true);\n        assertThat(pageURLStart.getPageId()).isNotNull();\n        assertThat(page.getId()).isEqualTo(pageURLStart.getPageId());\n        assertThat(pageURLOverview.getPageId()).isNotNull();\n        assertThat(custompage_globalpage.getId()).isEqualTo(pageURLOverview.getPageId());\n\n        getPageAPI().deletePage(page.getId());\n        assertThat(getProcessAPI().getProcessResolutionProblems(processDefinition.getId())).hasSize(1);\n\n        getPageAPI().deletePage(custompage_globalpage.getId());\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void processWithEventSubProcess() throws Exception {\n        ProcessDefinitionBuilder p1Builder = new ProcessDefinitionBuilder().createNewInstance(\"P1\", \"1.0\");\n        p1Builder.addUserTask(\"step1\", \"actor\").addUserTask(\"step2\", \"actor\");\n        p1Builder.addActor(\"actor\");\n        final SubProcessDefinitionBuilder eventSubProc = p1Builder.addSubProcess(\"eventSubProc\", true)\n                .getSubProcessBuilder();\n        eventSubProc.addUserTask(\"subTask\", \"actor\");\n        eventSubProc.addStartEvent(\"start\").addSignalEventTrigger(\"theSignal\");\n        eventSubProc.addTransition(\"start\", \"subTask\");\n\n        BusinessArchiveBuilder bar1 = new BusinessArchiveBuilder()\n                .createNewBusinessArchive()\n                .setProcessDefinition(p1Builder.done())\n                .setFormMappings(\n                        FormMappingModelBuilder.buildFormMappingModel()\n                                .addProcessStartForm(\"processStartForm\", FormMappingTarget.URL)\n                                .addTaskForm(\"task1Form\", FormMappingTarget.INTERNAL, \"step1\")\n                                .addTaskForm(\"urlForThesubTask\", FormMappingTarget.URL, \"subTask\")\n                                .addProcessOverviewForm(\"process1OverviewForm\", FormMappingTarget.INTERNAL).build());\n\n        ProcessDefinition p1 = getProcessAPI().deploy(bar1.done());\n\n        SearchResult<FormMapping> formMappingSearchResult = getProcessAPI()\n                .searchFormMappings(new SearchOptionsBuilder(0, 100).sort(\n                        FormMappingSearchDescriptor.ID, Order.DESC).filter(FormMappingSearchDescriptor.TASK, \"subTask\")\n                        .done());\n        assertThat(formMappingSearchResult.getCount()).isEqualTo(1);\n        assertThat(formMappingSearchResult.getResult().get(0).getURL()).isEqualTo(\"urlForThesubTask\");\n\n        deleteProcess(p1);\n\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/identity/CustomUserInfoIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.junit.After;\nimport org.junit.Test;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class CustomUserInfoIT extends TestWithUser {\n\n    private static String DEFAULT_NAME = \"Skills\";\n\n    @Override\n    @After\n    public void after() throws Exception {\n        final int pageSize = 20;\n        List<CustomUserInfoDefinition> definitions;\n        do {\n            definitions = getIdentityAPI().getCustomUserInfoDefinitions(0, pageSize);\n            deleteUserInfo(definitions);\n        } while (definitions.size() == pageSize);\n        super.after();\n    }\n\n    @Test\n    public void createCustomUserInfoDefinition_should_return_the_new_created_object() throws Exception {\n        // given\n        final CustomUserInfoDefinitionCreator creator = new CustomUserInfoDefinitionCreator(DEFAULT_NAME,\n                \"The user skills.\");\n\n        // when\n        final CustomUserInfoDefinition info = getIdentityAPI().createCustomUserInfoDefinition(creator);\n\n        // then\n        assertThat(info.getName()).isEqualTo(DEFAULT_NAME);\n        assertThat(info.getDescription()).isEqualTo(\"The user skills.\");\n    }\n\n    @Test\n    public void getCustomUserInfoDefinitions_return_objects_according_to_pagination_size_and_ordered_by_name_asc()\n            throws Exception {\n        // given\n        final CustomUserInfoDefinition skills = getIdentityAPI()\n                .createCustomUserInfoDefinition(new CustomUserInfoDefinitionCreator(DEFAULT_NAME));\n        final CustomUserInfoDefinition a = getIdentityAPI()\n                .createCustomUserInfoDefinition(new CustomUserInfoDefinitionCreator(\"A\"));\n        final CustomUserInfoDefinition b = getIdentityAPI()\n                .createCustomUserInfoDefinition(new CustomUserInfoDefinitionCreator(\"B\"));\n        final int pagegSize = 2;\n\n        // when\n        List<CustomUserInfoDefinition> definitions = getIdentityAPI().getCustomUserInfoDefinitions(0, pagegSize);\n\n        // then (first page)\n        assertThat(definitions).containsExactly(a, b);\n\n        // when\n        definitions = getIdentityAPI().getCustomUserInfoDefinitions(2, pagegSize);\n\n        // then (second page)\n        assertThat(definitions).containsExactly(skills);\n    }\n\n    @Test\n    public void getCustomUserInfo_should_return_all_info_even_when_value_is_null() throws Exception {\n        // given\n        final CustomUserInfoDefinition job = getIdentityAPI()\n                .createCustomUserInfoDefinition(new CustomUserInfoDefinitionCreator(\"job\"));\n        final CustomUserInfoDefinition skill = getIdentityAPI()\n                .createCustomUserInfoDefinition(new CustomUserInfoDefinitionCreator(\"skill\"));\n        getIdentityAPI().setCustomUserInfoValue(skill.getId(), user.getId(), \"java\");\n\n        // when\n        final List<CustomUserInfo> infoPage1 = getIdentityAPI().getCustomUserInfo(user.getId(), 0, 1);\n        final List<CustomUserInfo> infoPage2 = getIdentityAPI().getCustomUserInfo(user.getId(), 1, 1);\n\n        // then\n        assertThat(infoPage1.get(0).getDefinition()).isEqualTo(job);\n        assertThat(infoPage1.get(0).getValue()).isEqualTo(null);\n        assertThat(infoPage2.get(0).getDefinition()).isEqualTo(skill);\n        assertThat(infoPage2.get(0).getValue()).isEqualTo(\"java\");\n    }\n\n    @Test\n    public void setCustomUserInfoValue_should_delete_CustomUserInfoValue_when_set_to_null() throws Exception {\n        // given\n        final CustomUserInfoDefinition job = getIdentityAPI()\n                .createCustomUserInfoDefinition(new CustomUserInfoDefinitionCreator(\"job\"));\n        getIdentityAPI().setCustomUserInfoValue(job.getId(), user.getId(), \"code slayer\");\n        final CustomUserInfoDefinition skill = getIdentityAPI()\n                .createCustomUserInfoDefinition(new CustomUserInfoDefinitionCreator(\"skill\"));\n        getIdentityAPI().setCustomUserInfoValue(skill.getId(), user.getId(), \"java\");\n\n        // when\n        getIdentityAPI().setCustomUserInfoValue(skill.getId(), user.getId(), null);\n\n        // then\n        final List<CustomUserInfoValue> values = getIdentityAPI()\n                .searchCustomUserInfoValues(new SearchOptionsBuilder(0, 10).done()).getResult();\n        assertThat(values.get(0).getDefinitionId()).isEqualTo(job.getId());\n        assertThat(values.get(0).getUserId()).isEqualTo(user.getId());\n        assertThat(values.get(0).getValue()).isEqualTo(\"code slayer\");\n    }\n\n    @Test\n    public void setCustomUserInfoValue_should_update_CustomUserInfoValue_when_one_already_exist() throws Exception {\n        // given\n        final CustomUserInfoDefinition job = getIdentityAPI()\n                .createCustomUserInfoDefinition(new CustomUserInfoDefinitionCreator(\"job\"));\n        getIdentityAPI().setCustomUserInfoValue(job.getId(), user.getId(), \"code slayer\");\n\n        // when\n        getIdentityAPI().setCustomUserInfoValue(job.getId(), user.getId(), \"or not\");\n\n        // then\n        final List<CustomUserInfoValue> values = getIdentityAPI()\n                .searchCustomUserInfoValues(new SearchOptionsBuilder(0, 10).done()).getResult();\n        assertThat(values.get(0).getDefinitionId()).isEqualTo(job.getId());\n        assertThat(values.get(0).getUserId()).isEqualTo(user.getId());\n        assertThat(values.get(0).getValue()).isEqualTo(\"or not\");\n    }\n\n    private void deleteUserInfo(final List<CustomUserInfoDefinition> definitions) throws DeletionException {\n        for (final CustomUserInfoDefinition definition : definitions) {\n            getIdentityAPI().deleteCustomUserInfoDefinition(definition.getId());\n        }\n    }\n\n    @Test\n    public void deleteCustomUserInfoDefinition_should_delete_definition_and_values_from_database() throws Exception {\n        // given\n        final CustomUserInfoDefinition info = createDefinition(DEFAULT_NAME);\n        getIdentityAPI().setCustomUserInfoValue(info.getId(), user.getId(), \"Java\");\n        assertThat(getIdentityAPI().getCustomUserInfoDefinitions(0, 10)).containsExactly(info);\n        final SearchOptions searchOptions = new SearchOptionsBuilder(0, 1).done();\n        assertThat(getIdentityAPI().searchCustomUserInfoValues(searchOptions).getCount()).isEqualTo(1);\n\n        // when\n        getIdentityAPI().deleteCustomUserInfoDefinition(info.getId());\n        final List<CustomUserInfoDefinition> definitions = getIdentityAPI().getCustomUserInfoDefinitions(0, 10);\n        final SearchResult<CustomUserInfoValue> values = getIdentityAPI().searchCustomUserInfoValues(searchOptions);\n\n        // then\n        assertThat(definitions).isEmpty();\n        assertThat(values.getCount()).isEqualTo(0);\n    }\n\n    @Test\n    public void deleteUser_should_delete_related_custom_user_info_value_from_database() throws Exception {\n        // given\n        final CustomUserInfoDefinition info = createDefinition(DEFAULT_NAME);\n        final User user = createUser(\"user.with.custom.user.info\", \"bpm\");\n        getIdentityAPI().setCustomUserInfoValue(info.getId(), user.getId(), \"Java\");\n        assertThat(getIdentityAPI().getCustomUserInfoDefinitions(0, 10)).containsExactly(info);\n        final SearchOptions searchOptions = new SearchOptionsBuilder(0, 1).done();\n        assertThat(getIdentityAPI().searchCustomUserInfoValues(searchOptions).getCount()).isEqualTo(1);\n\n        // when\n        getIdentityAPI().deleteUser(user.getId());\n        final List<CustomUserInfoDefinition> definitions = getIdentityAPI().getCustomUserInfoDefinitions(0, 10);\n        final SearchResult<CustomUserInfoValue> values = getIdentityAPI().searchCustomUserInfoValues(searchOptions);\n\n        // then\n        assertThat(definitions.size()).isEqualTo(1);\n        assertThat(values.getCount()).isEqualTo(0);\n    }\n\n    private CustomUserInfoDefinition createDefinition(final String name) throws CreationException {\n        final CustomUserInfoDefinitionCreator creator = new CustomUserInfoDefinitionCreator(name);\n        return getIdentityAPI().createCustomUserInfoDefinition(creator);\n    }\n\n    @Test\n    public void getUserIdsWithCustomUserInfo_should_return_only_users_with_the_given_user_info() throws Exception {\n        //given\n        final User user2 = createUser(\"jack\", \"bpm\");\n        final CustomUserInfoDefinition info = createDefinition(DEFAULT_NAME);\n        getIdentityAPI().setCustomUserInfoValue(info.getId(), user.getId(), \"Java\");\n        getIdentityAPI().setCustomUserInfoValue(info.getId(), user2.getId(), \"C++\");\n\n        //when\n        final List<Long> userIdsExactMatch = getIdentityAPI().getUserIdsWithCustomUserInfo(DEFAULT_NAME, \"C++\", false,\n                0, 10);\n        final List<Long> userIdsExactMatchNoResults = getIdentityAPI().getUserIdsWithCustomUserInfo(DEFAULT_NAME, \"av\",\n                false, 0, 10);\n        final List<Long> userIdsPartialMatch = getIdentityAPI().getUserIdsWithCustomUserInfo(DEFAULT_NAME, \"av\", true,\n                0, 10);\n\n        //then\n        assertThat(userIdsExactMatch).containsExactly(user2.getId());\n        assertThat(userIdsExactMatchNoResults).isEmpty();\n        assertThat(userIdsPartialMatch).containsExactly(user.getId());\n\n        //clean\n        deleteUser(user2);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/identity/GroupIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.hamcrest.CoreMatchers.instanceOf;\nimport static org.hamcrest.CoreMatchers.is;\nimport static org.junit.Assert.*;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.TestWithTechnicalUser;\nimport org.bonitasoft.engine.api.ApiAccessType;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.InvalidGroupNameException;\nimport org.bonitasoft.engine.exception.NotFoundException;\nimport org.bonitasoft.engine.identity.impl.IconImpl;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.util.APITypeManager;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\n\npublic class GroupIT extends TestWithTechnicalUser {\n\n    private Group defaultGroup;\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Override\n    @Before\n    public void before() throws Exception {\n        super.before();\n        final GroupCreator groupCreator = new GroupCreator(\"test\");\n        groupCreator.setDescription(\"description\").setDisplayName(\"label\");\n        defaultGroup = getIdentityAPI().createGroup(groupCreator);\n    }\n\n    @Override\n    @After\n    public void after() throws Exception {\n        getIdentityAPI().deleteGroup(defaultGroup.getId());\n        defaultGroup = null;\n        super.after();\n    }\n\n    @Test\n    public void getGroup() throws BonitaException {\n        final Group group = getIdentityAPI().getGroup(defaultGroup.getId());\n        assertNotNull(group);\n        assertEquals(\"test\", group.getName());\n        assertEquals(\"label\", group.getDisplayName());\n        assertEquals(\"description\", group.getDescription());\n    }\n\n    @Test(expected = GroupNotFoundException.class)\n    public void getGroupByGroupNotFound() throws BonitaException {\n        getIdentityAPI().getGroup(0);\n    }\n\n    @Test\n    public void getNumberOfGroups() throws BonitaException {\n        assertEquals(1, getIdentityAPI().getNumberOfGroups());\n        final Group newGroup = getIdentityAPI().createGroup(\"NewGroup\", null);\n        assertEquals(2, getIdentityAPI().getNumberOfGroups());\n        getIdentityAPI().deleteGroup(newGroup.getId());\n    }\n\n    @Test(expected = AlreadyExistsException.class)\n    public void createGroupBygroupWithGroupAlreadyExistException() throws BonitaException {\n        Group group = getIdentityAPI().createGroup(\"NewGroup\", null);\n        try {\n            group = getIdentityAPI().createGroup(\"NewGroup\", null);\n        } finally {\n            getIdentityAPI().deleteGroup(group.getId());\n        }\n    }\n\n    @Test\n    public void getGroupByGroupName() throws BonitaException {\n        final String groupName = \"group111\";\n        final Group groupM = getIdentityAPI().createGroup(groupName, null);\n        final Group group = getIdentityAPI().getGroupByPath(groupName);\n        assertNotNull(group);\n        assertEquals(groupName, group.getName());\n        assertEquals(groupM.getId(), group.getId());\n        getIdentityAPI().deleteGroup(group.getId());\n    }\n\n    @Test\n    public void getGroups() throws BonitaException {\n        final Group groupA = createGroup(\"testA\", \"labelA\", \"descrtptionA\");\n        final Group groupB = createGroup(\"testB\", \"labelB\", \"descrtptionB\");\n        final List<Group> listGroups = getIdentityAPI().getGroups(0, 5000, GroupCriterion.NAME_ASC);\n\n        assertNotNull(listGroups);\n        assertEquals(3, listGroups.size());\n        assertEquals(\"testA\", listGroups.get(1).getName());\n        assertEquals(\"labelA\", listGroups.get(1).getDisplayName());\n        assertEquals(\"descrtptionA\", listGroups.get(1).getDescription());\n        assertEquals(\"testB\", listGroups.get(2).getName());\n        assertEquals(\"labelB\", listGroups.get(2).getDisplayName());\n        assertEquals(\"descrtptionB\", listGroups.get(2).getDescription());\n        getIdentityAPI().deleteGroup(groupA.getId());\n        getIdentityAPI().deleteGroup(groupB.getId());\n    }\n\n    @Test\n    public void getGroupsByIDs() throws BonitaException {\n        final String group1 = \"Group1\";\n        final Group groupCreated1 = getIdentityAPI().createGroup(group1, null);\n        final String group2 = \"Group2\";\n        final Group groupCreated2 = getIdentityAPI().createGroup(group2, null);\n\n        final List<Long> groupIds = new ArrayList<>();\n        groupIds.add(groupCreated1.getId());\n        groupIds.add(groupCreated2.getId());\n\n        final Map<Long, Group> groups = getIdentityAPI().getGroups(groupIds);\n        assertNotNull(groups);\n        assertEquals(2, groups.size());\n        assertEquals(group1, groups.get(groupCreated1.getId()).getName());\n        assertEquals(group2, groups.get(groupCreated2.getId()).getName());\n\n        getIdentityAPI().deleteGroup(groups.get(groupCreated1.getId()).getId());\n        getIdentityAPI().deleteGroup(groups.get(groupCreated2.getId()).getId());\n    }\n\n    public void getGroupsByIDsWithoutGroupNotFoundException() throws BonitaException {\n        final String group1 = \"Group1\";\n        final Group groupCreated1 = getIdentityAPI().createGroup(group1, null);\n        final String group2 = \"Group2\";\n        final Group groupCreated2 = getIdentityAPI().createGroup(group2, null);\n\n        final List<Long> groupIds = new ArrayList<>();\n        groupIds.add(groupCreated1.getId());\n        groupIds.add(groupCreated2.getId() + 100);\n\n        final Map<Long, Group> groups = getIdentityAPI().getGroups(groupIds);\n        assertNotNull(groups);\n        assertEquals(1, groups.size());\n        assertEquals(group1, groups.get(0).getName());\n\n        getIdentityAPI().deleteGroup(groupCreated1.getId());\n        getIdentityAPI().deleteGroup(groupCreated2.getId());\n    }\n\n    @Test(expected = AlreadyExistsException.class)\n    public void createGroupExistException() throws BonitaException {\n        getIdentityAPI().createGroup(\"test\", null);\n    }\n\n    @Test(expected = AlreadyExistsException.class)\n    public void createSubGroupExistException() throws BonitaException {\n        final Group group = getIdentityAPI().createGroup(\"r&d\", \"bonita\");\n        try {\n            getIdentityAPI().createGroup(\"r&d\", \"bonita\");\n        } finally {\n            deleteGroups(group);\n        }\n    }\n\n    @Test\n    public void deleteGroup() throws BonitaException {\n        final long numberOfGroups = getIdentityAPI().getNumberOfGroups();\n        final Group group = getIdentityAPI().createGroup(\"groupName\", null);\n        assertEquals(numberOfGroups + 1, getIdentityAPI().getNumberOfGroups());\n\n        getIdentityAPI().deleteGroup(group.getId());\n        assertEquals(numberOfGroups, getIdentityAPI().getNumberOfGroups());\n    }\n\n    @Test\n    public void deleteGroupDeleteChildGroups() throws BonitaException {\n        final long numberOfGroups = getIdentityAPI().getNumberOfGroups();\n        final Group parentGroup = getIdentityAPI().createGroup(\"parentGroup\", null);\n        final Group notParentGroup = getIdentityAPI().createGroup(\"notParentGroup\", null);\n        final Group subGroup = getIdentityAPI().createGroup(\"subGroup\", parentGroup.getPath());\n        assertEquals(numberOfGroups + 3, getIdentityAPI().getNumberOfGroups());\n\n        getIdentityAPI().deleteGroup(parentGroup.getId());\n        try {\n            getIdentityAPI().getGroup(subGroup.getId());\n            fail(\"child group should not exists anymore\");\n        } catch (final GroupNotFoundException e) {\n            // ok\n        }\n        getIdentityAPI().getGroup(notParentGroup.getId());\n        assertEquals(numberOfGroups + 1, getIdentityAPI().getNumberOfGroups());\n        getIdentityAPI().deleteGroup(notParentGroup.getId());\n\n    }\n\n    @Test\n    public void deleteGroupDeleteChildGroupsRecursivly() throws BonitaException {\n        final long numberOfGroups = getIdentityAPI().getNumberOfGroups();\n        final Group parentGroup = getIdentityAPI().createGroup(\"parentGroup\", null);\n        for (int i = 0; i < 25; i++) {\n            final Group sub = getIdentityAPI().createGroup(\"subGroup\" + i, parentGroup.getPath());\n            for (int j = 0; j < 25; j++) {\n                getIdentityAPI().createGroup(\"subSubGroup\" + j, sub.getPath());\n            }\n        }\n        assertEquals(numberOfGroups + 1 + 25 + 25 * 25, getIdentityAPI().getNumberOfGroups());\n        getIdentityAPI().deleteGroup(parentGroup.getId());\n        assertEquals(numberOfGroups, getIdentityAPI().getNumberOfGroups());\n    }\n\n    @Test\n    public void deleteGroupsChildrenAndParent() throws BonitaException {\n        final long numberOfGroups = getIdentityAPI().getNumberOfGroups();\n        final Group parentGroup = getIdentityAPI().createGroup(\"parentGroup\", null);\n        final Group sub0 = getIdentityAPI().createGroup(\"subGroup0\", parentGroup.getPath());\n        final Group sub01 = getIdentityAPI().createGroup(\"subSubGroup0\", sub0.getPath());\n        final Group sub1 = getIdentityAPI().createGroup(\"subGroup1\", parentGroup.getPath());\n        final Group sub11 = getIdentityAPI().createGroup(\"subSubGroup1\", sub1.getPath());\n        assertEquals(numberOfGroups + 5, getIdentityAPI().getNumberOfGroups());\n\n        getIdentityAPI().deleteGroups(Arrays.asList(sub01.getId(), parentGroup.getId(), sub11.getId()));\n        assertEquals(numberOfGroups, getIdentityAPI().getNumberOfGroups());\n    }\n\n    @Test\n    public void deleteGroupNotFoundException() throws Exception {\n        expectedException.expect(DeletionException.class);\n        //Exception causes are not serialized if API type is HTTP\n        if (ApiAccessType.LOCAL.equals(APITypeManager.getAPIType())) {\n            expectedException.expectCause(is(instanceOf(GroupNotFoundException.class)));\n        }\n        getIdentityAPI().deleteGroup(0);\n    }\n\n    @Test\n    public void deleteGroups() throws BonitaException {\n        assertNotNull(getIdentityAPI().getNumberOfGroups());\n        assertEquals(1, getIdentityAPI().getNumberOfGroups());\n        final List<Long> groupIdList = new ArrayList<>();\n\n        final Group group1 = getIdentityAPI().createGroup(\"testName1\", null);\n        groupIdList.add(group1.getId());\n        assertEquals(2, getIdentityAPI().getNumberOfGroups());\n\n        final Group group2 = getIdentityAPI().createGroup(\"testName2\", null);\n        groupIdList.add(group2.getId());\n        assertEquals(3, getIdentityAPI().getNumberOfGroups());\n\n        assertEquals(2, groupIdList.size());\n        getIdentityAPI().deleteGroups(groupIdList);\n        assertEquals(1, getIdentityAPI().getNumberOfGroups());\n    }\n\n    @Test(expected = DeletionException.class)\n    public void deleteGroupsWithNotExistId() throws BonitaException {\n        assertNotNull(getIdentityAPI().getNumberOfGroups());\n        assertEquals(1, getIdentityAPI().getNumberOfGroups());\n        final List<Long> groupIdList = new ArrayList<>();\n\n        final Group group1 = getIdentityAPI().createGroup(\"testName1\", null);\n        groupIdList.add(group1.getId());\n        assertEquals(2, getIdentityAPI().getNumberOfGroups());\n\n        groupIdList.add((long) 0);\n        assertEquals(2, groupIdList.size());\n\n        getIdentityAPI().deleteGroup(group1.getId());\n        getIdentityAPI().deleteGroups(groupIdList);\n    }\n\n    @Test\n    public void updateGroup() throws BonitaException {\n        final Group group1 = getIdentityAPI().getGroup(defaultGroup.getId());\n        assertEquals(\"test\", group1.getName());\n\n        final GroupUpdater updateDescriptor = new GroupUpdater();\n        updateDescriptor.updateName(\"newtest\");\n        updateDescriptor.updateDisplayName(\"newlabel\");\n        updateDescriptor.updateDescription(\"newdescription\");\n\n        getIdentityAPI().updateGroup(group1.getId(), updateDescriptor);\n        final Group group2 = getIdentityAPI().getGroup(group1.getId());\n        assertNotNull(group2);\n        assertEquals(\"newtest\", group2.getName());\n        assertEquals(\"newlabel\", group2.getDisplayName());\n        assertEquals(\"newdescription\", group2.getDescription());\n    }\n\n    @Test\n    public void updateParentGroupPath() throws BonitaException {\n        final Group newRootGroup = createGroup(\"BonitaSoft\", \"BonitaSoft\", \"BonitaSoft company\");\n\n        final String groupL2Name = \"France\";\n        Group groupL2 = getIdentityAPI().createGroup(groupL2Name, defaultGroup.getPath());\n        assertEquals(defaultGroup.getPath(), groupL2.getParentPath());\n\n        final String groupL3Name = \"Grenoble\";\n        Group groupL3 = getIdentityAPI().createGroup(groupL3Name, groupL2.getPath());\n        assertEquals(groupL2.getPath(), groupL3.getParentPath());\n\n        final GroupUpdater updateDescriptor = new GroupUpdater();\n        updateDescriptor.updateParentPath(newRootGroup.getPath());\n\n        // update parent path\n        getIdentityAPI().updateGroup(groupL2.getId(), updateDescriptor);\n        groupL2 = getIdentityAPI().getGroup(groupL2.getId());\n        assertEquals(newRootGroup.getPath(), groupL2.getParentPath());\n\n        // assert children are also updated\n        groupL3 = getIdentityAPI().getGroup(groupL3.getId());\n        assertEquals(groupL2.getPath(), groupL3.getParentPath());\n\n        getIdentityAPI().deleteGroup(groupL3.getId());\n        getIdentityAPI().deleteGroup(groupL2.getId());\n        getIdentityAPI().deleteGroup(newRootGroup.getId());\n    }\n\n    @Test\n    public void when_update_group_with_empty_parent_path_it_is_set_to_null() throws BonitaException {\n        final String parentGroupPath = \"/parentPath\";\n        final Group group = createGroup(\"BonitaSoft\", parentGroupPath);\n        Group result = getIdentityAPI().getGroup(group.getId());\n        assertEquals(\"The parent path must be equals to \" + parentGroupPath + \".\", parentGroupPath,\n                result.getParentPath());\n\n        // update parent path\n        final GroupUpdater updateDescriptor = new GroupUpdater();\n        updateDescriptor.updateParentPath(\"\");\n        getIdentityAPI().updateGroup(group.getId(), updateDescriptor);\n        result = getIdentityAPI().getGroup(group.getId());\n        assertNull(\"The parent path must be null.\", result.getParentPath());\n\n        getIdentityAPI().deleteGroup(group.getId());\n    }\n\n    @Test\n    public void when_create_group_with_empty_parent_path_it_is_set_to_null() throws BonitaException {\n        final Group group = createGroup(\"BonitaSoft\", \"\");\n        final Group result = getIdentityAPI().getGroup(group.getId());\n        assertNull(\"The parent path must be null.\", result.getParentPath());\n\n        getIdentityAPI().deleteGroup(group.getId());\n    }\n\n    @Test\n    public void updateGroupNameAlsoUpdateChildren() throws BonitaException {\n        final Group newRootGroup = createGroup(\"BonitaSoft\", \"BonitaSoft\", \"BonitaSoft company\");\n\n        final String groupL2Name = \"France\";\n        Group groupL2 = getIdentityAPI().createGroup(groupL2Name, defaultGroup.getPath());\n        assertEquals(defaultGroup.getPath(), groupL2.getParentPath());\n\n        final String groupL3Name = \"Grenoble\";\n        Group groupL3 = getIdentityAPI().createGroup(groupL3Name, groupL2.getPath());\n        assertEquals(groupL2.getPath(), groupL3.getParentPath());\n\n        final GroupUpdater updateDescriptor = new GroupUpdater();\n        updateDescriptor.updateName(\"Germany\");\n\n        // update parent path\n        getIdentityAPI().updateGroup(groupL2.getId(), updateDescriptor);\n        groupL2 = getIdentityAPI().getGroup(groupL2.getId());\n        assertEquals(\"Germany\", groupL2.getName());\n\n        // assert children are also updated\n        groupL3 = getIdentityAPI().getGroup(groupL3.getId());\n        assertEquals(\"/\" + defaultGroup.getName() + \"/Germany\", groupL3.getParentPath());\n\n        getIdentityAPI().deleteGroup(groupL3.getId());\n        getIdentityAPI().deleteGroup(groupL2.getId());\n        getIdentityAPI().deleteGroup(newRootGroup.getId());\n    }\n\n    @Test(expected = AlreadyExistsException.class)\n    public void should_throw_AlreadyExistException_when_update_group_with_group_name_already_exist()\n            throws BonitaException {\n        Group groupToUpdate = getIdentityAPI().createGroup(\"England\", defaultGroup.getPath());\n        final GroupUpdater groupUpdater = new GroupUpdater();\n        groupUpdater.updateName(\"test\");\n\n        getIdentityAPI().updateGroup(groupToUpdate.getId(), groupUpdater);\n\n        getIdentityAPI().deleteGroup(groupToUpdate.getId());\n    }\n\n    @Test\n    public void should_updated_group_when_name_already_exist_but_path_dont_exist() throws BonitaException {\n        final Group newRootGroup = createGroup(\"Country\", \"Country\", \"Country company\");\n        final String groupFranceName = \"France\";\n        Group groupFrance = getIdentityAPI().createGroup(groupFranceName, newRootGroup.getPath());\n\n        final String groupEnglandName = \"England\";\n        Group groupEngland = getIdentityAPI().createGroup(groupEnglandName, newRootGroup.getPath());\n\n        final GroupUpdater groupUpdater = new GroupUpdater();\n        groupUpdater.updateName(\"France\");\n        groupUpdater.updateParentPath(null);\n\n        getIdentityAPI().updateGroup(groupEngland.getId(), groupUpdater);\n\n        // Asset\n        Group group = getIdentityAPI().getGroup(groupEngland.getId());\n        assertEquals(\"France\", group.getName());\n        assertEquals(\"/France\", group.getPath());\n\n        // Clean\n        getIdentityAPI().deleteGroup(groupFrance.getId());\n        getIdentityAPI().deleteGroup(groupEngland.getId());\n        getIdentityAPI().deleteGroup(newRootGroup.getId());\n    }\n\n    @Test\n    public void updateGroupNameAndParenthAlsoUpdateAllChildrenInfos() throws BonitaException {\n        // arrange\n        Group parentGroup = getIdentityAPI().createGroup(\"France\", defaultGroup.getPath());\n        Group childGroup = getIdentityAPI().createGroup(\"Grenoble\", parentGroup.getPath());\n\n        // act\n        final GroupUpdater group2Updater = new GroupUpdater();\n        group2Updater.updateParentPath(\"/WorldCompany\");\n        group2Updater.updateName(\"Germany\");\n        getIdentityAPI().updateGroup(parentGroup.getId(), group2Updater);\n\n        // assert\n        parentGroup = getIdentityAPI().getGroup(parentGroup.getId());\n        childGroup = getIdentityAPI().getGroup(childGroup.getId());\n        assertEquals(\"/WorldCompany/Germany\", childGroup.getParentPath());\n        assertEquals(\"/WorldCompany/Germany/Grenoble\", childGroup.getPath());\n\n        // clean-up:\n        getIdentityAPI().deleteGroup(childGroup.getId());\n        getIdentityAPI().deleteGroup(parentGroup.getId());\n    }\n\n    @Test(expected = GroupNotFoundException.class)\n    public void updateGroupsNotFoundException() throws BonitaException {\n        final GroupUpdater updateDescriptor = new GroupUpdater();\n        updateDescriptor.updateName(\"newtest\");\n        updateDescriptor.updateDisplayName(\"newlabel\");\n        updateDescriptor.updateDescription(\"newdescription\");\n        getIdentityAPI().updateGroup(0, updateDescriptor);\n    }\n\n    @Test\n    public void getUsersInGroup() throws BonitaException {\n        final User aUserInRoleA = getIdentityAPI().createUser(\"testnameA\", \"bpm\");\n        final User bUserInRoleA = getIdentityAPI().createUser(\"testnameB\", \"bpm\");\n        final User cUserInRoleB = getIdentityAPI().createUser(\"testnameC\", \"bpm\");\n        final User dUser = getIdentityAPI().createUser(\"testnameD\", \"bpm\");\n\n        final Group group = createGroup(\"group\", \"testLabel\", \"description\");\n        final List<Long> userIds = new ArrayList<>();\n        userIds.add(aUserInRoleA.getId());\n        userIds.add(bUserInRoleA.getId());\n        final RoleCreator roleCreatorA = new RoleCreator(\"RoleA\");\n        roleCreatorA.setDisplayName(\"LabelA\").setDescription(\"DescriptionA\");\n        final Role testRoleA = getIdentityAPI().createRole(roleCreatorA);\n        getIdentityAPI().addUserMemberships(userIds, defaultGroup.getId(), testRoleA.getId());\n\n        final List<Long> testIds = new ArrayList<>();\n        testIds.add(cUserInRoleB.getId());\n        final RoleCreator roleCreatorB = new RoleCreator(\"RoleB\");\n        roleCreatorB.setDisplayName(\"LabelB\").setDescription(\"DescriptionB\");\n        final Role testRoleB = getIdentityAPI().createRole(roleCreatorB);\n        getIdentityAPI().addUserMemberships(testIds, group.getId(), testRoleB.getId());\n\n        final List<User> users = getIdentityAPI().getUsersInGroup(defaultGroup.getId(), 0, 5000,\n                UserCriterion.USER_NAME_ASC);\n        assertNotNull(users);\n        assertEquals(2, users.size());\n        assertEquals(\"testnameA\", users.get(0).getUserName());\n        assertEquals(\"testnameB\", users.get(1).getUserName());\n\n        getIdentityAPI().deleteUserMemberships(userIds, defaultGroup.getId(), testRoleA.getId());\n        getIdentityAPI().deleteUserMemberships(testIds, group.getId(), testRoleB.getId());\n        getIdentityAPI().deleteUser(aUserInRoleA.getId());\n        getIdentityAPI().deleteUser(bUserInRoleA.getId());\n        getIdentityAPI().deleteUser(cUserInRoleB.getId());\n        getIdentityAPI().deleteUser(dUser.getId());\n        getIdentityAPI().deleteRole(testRoleA.getId());\n        getIdentityAPI().deleteRole(testRoleB.getId());\n        getIdentityAPI().deleteGroup(group.getId());\n    }\n\n    @Test\n    public void getActiveUsersInGroup() throws BonitaException {\n        final User aUserInRoleA = getIdentityAPI().createUser(new UserCreator(\"testnameA\", \"bpm\").setEnabled(true));\n        final User bUserInRoleA = getIdentityAPI().createUser(new UserCreator(\"testnameB\", \"bpm\").setEnabled(false));\n        final User cUserInRoleB = getIdentityAPI().createUser(new UserCreator(\"testnameC\", \"bpm\").setEnabled(true));\n        final User dUser = getIdentityAPI().createUser(new UserCreator(\"testnameD\", \"bpm\").setEnabled(true));\n\n        final Group group = createGroup(\"group\", \"testLabel\", \"description\");\n        final List<Long> userIds = new ArrayList<>();\n        userIds.add(aUserInRoleA.getId());\n        userIds.add(bUserInRoleA.getId());\n        final RoleCreator roleCreatorA = new RoleCreator(\"RoleA\");\n        roleCreatorA.setDisplayName(\"LabelA\").setDescription(\"DescriptionA\");\n        final Role testRoleA = getIdentityAPI().createRole(roleCreatorA);\n        getIdentityAPI().addUserMemberships(userIds, defaultGroup.getId(), testRoleA.getId());\n\n        final List<Long> testIds = new ArrayList<>();\n        testIds.add(cUserInRoleB.getId());\n        final RoleCreator roleCreatorB = new RoleCreator(\"RoleB\");\n        roleCreatorB.setDisplayName(\"LabelB\").setDescription(\"DescriptionB\");\n        final Role testRoleB = getIdentityAPI().createRole(roleCreatorB);\n        getIdentityAPI().addUserMemberships(testIds, group.getId(), testRoleB.getId());\n\n        final List<User> users = getIdentityAPI().getActiveUsersInGroup(defaultGroup.getId(), 0, 5000,\n                UserCriterion.USER_NAME_ASC);\n        assertNotNull(users);\n        assertEquals(1, users.size());\n        assertEquals(\"testnameA\", users.get(0).getUserName());\n\n        getIdentityAPI().deleteUserMemberships(userIds, defaultGroup.getId(), testRoleA.getId());\n        getIdentityAPI().deleteUserMemberships(testIds, group.getId(), testRoleB.getId());\n        getIdentityAPI().deleteUser(aUserInRoleA.getId());\n        getIdentityAPI().deleteUser(bUserInRoleA.getId());\n        getIdentityAPI().deleteUser(cUserInRoleB.getId());\n        getIdentityAPI().deleteUser(dUser.getId());\n        getIdentityAPI().deleteRole(testRoleA.getId());\n        getIdentityAPI().deleteRole(testRoleB.getId());\n        getIdentityAPI().deleteGroup(group.getId());\n    }\n\n    @Test\n    public void getInactiveUsersInGroup() throws BonitaException {\n        final User aUserInRoleA = getIdentityAPI().createUser(new UserCreator(\"testnameA\", \"bpm\").setEnabled(true));\n        final User bUserInRoleA = getIdentityAPI().createUser(new UserCreator(\"testnameB\", \"bpm\").setEnabled(false));\n        final User cUserInRoleA = getIdentityAPI().createUser(new UserCreator(\"testnameE\", \"bpm\").setEnabled(false));\n        final User cUserInRoleB = getIdentityAPI().createUser(new UserCreator(\"testnameC\", \"bpm\").setEnabled(true));\n        final User dUser = getIdentityAPI().createUser(new UserCreator(\"testnameD\", \"bpm\").setEnabled(true));\n\n        final Group group = createGroup(\"group\", \"testLabel\", \"description\");\n        final List<Long> userIds = new ArrayList<>();\n        userIds.add(aUserInRoleA.getId());\n        userIds.add(bUserInRoleA.getId());\n        userIds.add(cUserInRoleA.getId());\n        final RoleCreator roleCreatorA = new RoleCreator(\"RoleA\");\n        roleCreatorA.setDisplayName(\"LabelA\").setDescription(\"DescriptionA\");\n        final Role testRoleA = getIdentityAPI().createRole(roleCreatorA);\n        getIdentityAPI().addUserMemberships(userIds, defaultGroup.getId(), testRoleA.getId());\n\n        final List<Long> testIds = new ArrayList<>();\n        testIds.add(cUserInRoleB.getId());\n        final RoleCreator roleCreatorB = new RoleCreator(\"RoleB\");\n        roleCreatorB.setDisplayName(\"LabelB\").setDescription(\"DescriptionB\");\n        final Role testRoleB = getIdentityAPI().createRole(roleCreatorB);\n        getIdentityAPI().addUserMemberships(testIds, group.getId(), testRoleB.getId());\n\n        final List<User> users = getIdentityAPI().getInactiveUsersInGroup(defaultGroup.getId(), 0, 5000,\n                UserCriterion.USER_NAME_ASC);\n        assertNotNull(users);\n        assertEquals(2, users.size());\n        assertEquals(\"testnameB\", users.get(0).getUserName());\n        assertEquals(\"testnameE\", users.get(1).getUserName());\n\n        getIdentityAPI().deleteUserMemberships(userIds, defaultGroup.getId(), testRoleA.getId());\n        getIdentityAPI().deleteUserMemberships(testIds, group.getId(), testRoleB.getId());\n        getIdentityAPI().deleteUser(aUserInRoleA.getId());\n        getIdentityAPI().deleteUser(bUserInRoleA.getId());\n        getIdentityAPI().deleteUser(cUserInRoleB.getId());\n        getIdentityAPI().deleteUser(dUser.getId());\n        getIdentityAPI().deleteRole(testRoleA.getId());\n        getIdentityAPI().deleteRole(testRoleB.getId());\n        getIdentityAPI().deleteGroup(group.getId());\n    }\n\n    @Test\n    public void getNumberOfUsersInGroup() throws BonitaException {\n        final User aUser = getIdentityAPI().createUser(\"testnameA\", \"bpm\");\n        final User bUser = getIdentityAPI().createUser(\"testnameB\", \"bpm\");\n        final List<Long> userIds = new ArrayList<>();\n        userIds.add(aUser.getId());\n        userIds.add(bUser.getId());\n        final Role testRole = getIdentityAPI().createRole(\"testRole\");\n\n        getIdentityAPI().addUserMemberships(userIds, defaultGroup.getId(), testRole.getId());\n        final List<User> users = getIdentityAPI().getUsersInGroup(defaultGroup.getId(), 0, 5000,\n                UserCriterion.USER_NAME_ASC);\n        final long count = getIdentityAPI().getNumberOfUsersInGroup(defaultGroup.getId());\n\n        assertNotNull(users);\n        assertNotNull(count);\n        assertEquals(count, users.size());\n        assertEquals(\"testnameA\", users.get(0).getUserName());\n        assertEquals(\"testnameB\", users.get(1).getUserName());\n\n        getIdentityAPI().deleteUserMemberships(userIds, defaultGroup.getId(), testRole.getId());\n        getIdentityAPI().deleteUser(aUser.getId());\n        getIdentityAPI().deleteUser(bUser.getId());\n        getIdentityAPI().deleteRole(testRole.getId());\n    }\n\n    @Test\n    public void getPaginatedGroupsWithGroupCriterion() throws BonitaException {\n        final Group groupA = createGroup(\"testA\", \"labelA\", \"descrtptionA\");\n        final Group groupB = createGroup(\"testB\", \"labelB\", \"descrtptionB\");\n        final Group groupC = createGroup(\"testc\", \"labelC\", \"descrtptionC\");\n        final Group groupD = createGroup(\"testd\", \"labelD\", \"descrtptionD\");\n        final List<Group> groupNameASCPage1 = getIdentityAPI().getGroups(0, 3, GroupCriterion.NAME_ASC);\n        assertEquals(3, groupNameASCPage1.size());\n        assertEquals(\"testA\", groupNameASCPage1.get(1).getName());\n        assertEquals(\"testB\", groupNameASCPage1.get(2).getName());\n\n        final List<Group> groupNameASCPage2 = getIdentityAPI().getGroups(3, 3, GroupCriterion.NAME_ASC);\n        assertEquals(2, groupNameASCPage2.size());\n        assertEquals(\"testc\", groupNameASCPage2.get(0).getName());\n        assertEquals(\"testd\", groupNameASCPage2.get(1).getName());\n\n        final List<Group> groupNameDESC = getIdentityAPI().getGroups(0, 3, GroupCriterion.NAME_DESC);\n        assertEquals(3, groupNameDESC.size());\n        assertEquals(\"testd\", groupNameDESC.get(0).getName());\n        assertEquals(\"testc\", groupNameDESC.get(1).getName());\n\n        final List<Group> groupLabelASC = getIdentityAPI().getGroups(0, 3, GroupCriterion.LABEL_ASC);\n        assertEquals(3, groupLabelASC.size());\n        assertEquals(\"labelA\", groupLabelASC.get(1).getDisplayName());\n        assertEquals(\"labelB\", groupLabelASC.get(2).getDisplayName());\n\n        final List<Group> groupLabelDESC = getIdentityAPI().getGroups(0, 3, GroupCriterion.LABEL_DESC);\n        assertEquals(3, groupLabelDESC.size());\n        assertEquals(\"labelD\", groupLabelDESC.get(0).getDisplayName());\n        assertEquals(\"labelC\", groupLabelDESC.get(1).getDisplayName());\n\n        getIdentityAPI().deleteGroup(groupA.getId());\n        getIdentityAPI().deleteGroup(groupB.getId());\n        getIdentityAPI().deleteGroup(groupC.getId());\n        getIdentityAPI().deleteGroup(groupD.getId());\n    }\n\n    @Test\n    public void searchGroupUsingFilter() throws BonitaException {\n        final Group groupA = createGroup(\"testA\", \"labelA\", \"desc\");\n        final Group groupB = createGroup(\"testB\", \"labelB\", \"Bbb\");\n        final Group groupC = createGroup(\"c\", \"labelC\", \"descrtptionC\");\n        final Group groupD = createGroup(\"d\", \"labelD\", \"descrtptionD\");\n\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.filter(GroupSearchDescriptor.ID, groupC.getId());\n        final SearchResult<Group> searchGroups = getIdentityAPI().searchGroups(builder.done());\n        assertNotNull(searchGroups);\n        assertEquals(1, searchGroups.getCount());\n        final List<Group> groups = searchGroups.getResult();\n        assertEquals(groupC, groups.get(0));\n\n        getIdentityAPI().deleteGroup(groupA.getId());\n        getIdentityAPI().deleteGroup(groupB.getId());\n        getIdentityAPI().deleteGroup(groupC.getId());\n        getIdentityAPI().deleteGroup(groupD.getId());\n    }\n\n    @Test\n    public void searchGroupWithApostrophe() throws BonitaException {\n        final Group groupA = createGroup(\"test'A\", \"labelA\", \"desc\");\n        final Group groupB = createGroup(\"testB\", \"test'B\", \"Bbb\");\n        final Group groupC = createGroup(\"testc\", \"labelC\", \"test'C\");\n\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.sort(GroupSearchDescriptor.NAME, Order.ASC);\n        builder.searchTerm(\"test'\");\n        final SearchResult<Group> searchGroups = getIdentityAPI().searchGroups(builder.done());\n        assertNotNull(searchGroups);\n        assertEquals(3, searchGroups.getCount());\n        final List<Group> groups = searchGroups.getResult();\n        assertEquals(groupA, groups.get(0));\n        assertEquals(groupB, groups.get(1));\n        assertEquals(groupC, groups.get(2));\n\n        getIdentityAPI().deleteGroup(groupA.getId());\n        getIdentityAPI().deleteGroup(groupB.getId());\n        getIdentityAPI().deleteGroup(groupC.getId());\n    }\n\n    @Test\n    public void checkCreatedByForGroup() throws BonitaException {\n        final Group group = createGroup(\"group1\", \"myGroup\", \"descrtption\");\n        assertNotNull(group);\n        assertNotNull(group.getCreatedBy());\n        assertEquals(getSession().getUserId(), group.getCreatedBy());\n        getIdentityAPI().deleteGroup(group.getId());\n    }\n\n    @Test\n    public void should_be_able_to_create_big_groups_hierarchy() throws BonitaException {\n        // this should work\n        // acme -> Site -> Service -> Departement -> Back Office & Logistique\n        createGroup(\"acme\");\n        createGroup(\"Site\", \"/acme\");\n        createGroup(\"Service\", \"/acme/Site\");\n        createGroup(\"Departement\", \"/acme/Site/Service\");\n        createGroup(\"Back Office & Logistique\", \"/acme/Site/Service/Departement\");\n        createGroup(\"Administration Titres\", \"/acme/Site/Service/Departement/Back Office & Logistique\");\n\n        assertEquals(\"Administration Titres\",\n                getIdentityAPI()\n                        .getGroupByPath(\"/acme/Site/Service/Departement/Back Office & Logistique/Administration Titres\")\n                        .getName());\n        deleteGroups(getIdentityAPI().getGroupByPath(\"/acme\"));\n    }\n\n    @Test\n    public void should_createGroup_with_icon_create_the_icon() throws Exception {\n        //given\n        Group mainGroup = getIdentityAPI()\n                .createGroup(new GroupCreator(\"mainGroup\").setIcon(\"main.png\", new byte[] { 1, 2, 3 }));\n        //when\n        Icon icon = getIdentityAPI().getIcon(mainGroup.getIconId());\n        //then\n        assertThat(icon).isEqualTo(new IconImpl(icon.getId(), \"image/png\", new byte[] { 1, 2, 3 }));\n        //clean up\n        deleteGroups(getIdentityAPI().getGroupByPath(\"/mainGroup\"));\n    }\n\n    @Test\n    public void should_updateGroup_with_new_icon_create_a_new_icon() throws Exception {\n        //given\n        Group mainGroup = getIdentityAPI()\n                .createGroup(new GroupCreator(\"mainGroup\").setIcon(\"main.png\", new byte[] { 1, 2, 3 }));\n        //when\n        Group group = getIdentityAPI().updateGroup(mainGroup.getId(),\n                new GroupUpdater().updateIcon(\"newIcon.jpg\", new byte[] { 3, 4, 5 }));\n        //then\n        Icon icon = getIdentityAPI().getIcon(group.getIconId());\n        assertThat(icon.getId()).isNotEqualTo(mainGroup.getIconId());\n        assertThat(icon.getMimeType()).isEqualTo(\"image/jpeg\");\n        assertThat(icon.getContent()).isEqualTo(new byte[] { 3, 4, 5 });\n        //clean up\n        deleteGroups(getIdentityAPI().getGroupByPath(\"/mainGroup\"));\n    }\n\n    @Test\n    public void should_deleteGroup_with_icon_delete_the_icon() throws Exception {\n        //given\n        Group mainGroup = getIdentityAPI()\n                .createGroup(new GroupCreator(\"mainGroup\").setIcon(\"main.png\", new byte[] { 1, 2, 3 }));\n        //when\n        getIdentityAPI().deleteGroup(mainGroup.getId());\n        //then\n        expectedException.expect(NotFoundException.class);\n        getIdentityAPI().getIcon(mainGroup.getIconId());\n    }\n\n    @Test\n    public void should_update_childrens_group_ParentPath_with_correct_value() throws BonitaException {\n        //given\n        Group acme = getIdentityAPI().createGroup(\"Acme\", null);\n        Group site = getIdentityAPI().createGroup(\"Site\", acme.getPath());\n\n        //when\n        final GroupUpdater group2Updater = new GroupUpdater();\n        group2Updater.updateDescription(\"laalalala\");\n        group2Updater.updateName(\"Acme2\");\n        getIdentityAPI().updateGroup(acme.getId(), group2Updater);\n\n        //then\n        assertThat(getIdentityAPI().getGroup(acme.getId()).getDescription()).contains(\"laalalala\");\n        assertThat(getIdentityAPI().getGroup(site.getId()).getParentPath()).isEqualTo(\"/Acme2\");\n        deleteGroups(getIdentityAPI().getGroup(acme.getId()));\n\n    }\n\n    @Test(expected = InvalidGroupNameException.class)\n    public void should_not_create_group_when_given_invalid_name() throws CreationException {\n        //when\n        Group acme = getIdentityAPI().createGroup(\"/Acme\", null);\n    }\n\n    @Test(expected = InvalidGroupNameException.class)\n    public void should_not_create_group_when_given_invalid_name_in_creator() throws CreationException {\n        //when\n        Group acme = getIdentityAPI().createGroup(new GroupCreator(\"Bon/ta\"));\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/identity/MembershipIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport static org.junit.Assert.*;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport org.bonitasoft.engine.TestWithTechnicalUser;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Bole Zhang\n * @author Baptiste Mesta\n */\npublic class MembershipIT extends TestWithTechnicalUser {\n\n    private static User user1;\n\n    private static User user2;\n\n    private static User user3;\n\n    private static User user4;\n\n    private static Role role1;\n\n    private static Role role2;\n\n    private static Role role3;\n\n    private static Role role4;\n\n    private static Group group1;\n\n    private static Group group2;\n\n    private static Group group3;\n\n    private static Group group4;\n\n    @Override\n    @Before\n    public void before() throws Exception {\n        super.before();\n        createTestedUserMemberships();\n    }\n\n    @Override\n    @After\n    public void after() throws Exception {\n        deleteTestedUserMemberships();\n        super.after();\n    }\n\n    @Test\n    public void getUserMembershipsWithPageOutOfRangeException() throws BonitaException {\n        final User u = getIdentityAPI().createUser(\"u\", \"engine\");\n        final List<UserMembership> userMemberships = getIdentityAPI().getUserMemberships(u.getId(), 0, 3,\n                UserMembershipCriterion.ROLE_NAME_ASC);\n        assertTrue(userMemberships.isEmpty());\n        getIdentityAPI().deleteUser(u.getId());\n    }\n\n    @Test(expected = CreationException.class)\n    public void createUserMembershipWithoutUser() throws BonitaException {\n        final RoleCreator roleCreator = new RoleCreator(\"roleM\");\n        final Role roleM = getIdentityAPI().createRole(roleCreator);\n        final Group groupM = getIdentityAPI().createGroup(\"groupM\", null);\n        try {\n            getIdentityAPI().addUserMembership(-2, groupM.getId(), roleM.getId());\n        } finally {\n            getIdentityAPI().deleteRole(roleM.getId());\n            getIdentityAPI().deleteGroup(groupM.getId());\n        }\n    }\n\n    @Test(expected = CreationException.class)\n    public void createUserMembershipWithoutRole() throws BonitaException {\n        final User userM = getIdentityAPI().createUser(\"userM\", \"engine\");\n        final Group groupM = getIdentityAPI().createGroup(\"groupM\", null);\n        try {\n            getIdentityAPI().addUserMembership(userM.getId(), groupM.getId(), -4);\n        } finally {\n            getIdentityAPI().deleteUser(userM.getId());\n            getIdentityAPI().deleteGroup(groupM.getId());\n        }\n    }\n\n    @Test(expected = CreationException.class)\n    public void createUserMembershipWithoutGroup() throws BonitaException {\n        final User userM = getIdentityAPI().createUser(\"userM\", \"engine\");\n        final RoleCreator roleCreator = new RoleCreator(\"roleM\");\n        final Role roleM = getIdentityAPI().createRole(roleCreator);\n        try {\n            getIdentityAPI().addUserMembership(userM.getId(), -83, roleM.getId());\n        } finally {\n            getIdentityAPI().deleteUser(userM.getId());\n            getIdentityAPI().deleteRole(roleM.getId());\n        }\n    }\n\n    @Test\n    public void addUserMembership() throws BonitaException {\n        final User userM = getIdentityAPI().createUser(\"userM\", \"engine\");\n        final Role roleM = getIdentityAPI().createRole(\"roleM\");\n        final Group groupM = getIdentityAPI().createGroup(\"groupM\", null);\n        final UserMembership userMembership = getIdentityAPI().addUserMembership(userM.getId(), groupM.getId(),\n                roleM.getId());\n        try {\n            assertNotNull(userMembership);\n            assertEquals(getSession().getUserId(), userMembership.getAssignedBy());\n            assertEquals(userM.getId(), userMembership.getUserId());\n            assertEquals(roleM.getId(), userMembership.getRoleId());\n            assertEquals(groupM.getId(), userMembership.getGroupId());\n            // check assignedBy for membership\n            assertEquals(getSession().getUserId(), userMembership.getAssignedBy());\n\n        } finally {\n            getIdentityAPI().deleteUserMembership(userM.getId(), groupM.getId(), roleM.getId());\n            getIdentityAPI().deleteUser(userM.getId());\n            getIdentityAPI().deleteRole(roleM.getId());\n            getIdentityAPI().deleteGroup(groupM.getId());\n        }\n    }\n\n    @Test(expected = AlreadyExistsException.class)\n    public void addTwiceSameUserMembership() throws BonitaException {\n        final User userM = getIdentityAPI().createUser(\"userM\", \"engine\");\n        final Role roleM = getIdentityAPI().createRole(\"roleM\");\n        final Group groupM = getIdentityAPI().createGroup(\"groupM\", null);\n\n        // Add first time the userMembership\n        getIdentityAPI().addUserMembership(userM.getId(), groupM.getId(), roleM.getId());\n        try {\n            // Add twice the same userMembership\n            getIdentityAPI().addUserMembership(userM.getId(), groupM.getId(), roleM.getId());\n        } finally {\n            getIdentityAPI().deleteUserMembership(userM.getId(), groupM.getId(), roleM.getId());\n            getIdentityAPI().deleteUser(userM.getId());\n            getIdentityAPI().deleteRole(roleM.getId());\n            getIdentityAPI().deleteGroup(groupM.getId());\n        }\n    }\n\n    @Test\n    public void getRoleIdAndGroupIdFromMembership() throws BonitaException {\n        final User userM = getIdentityAPI().createUser(\"userM\", \"engine\");\n        final Role roleM = getIdentityAPI().createRole(\"roleM\");\n        final Group groupM = getIdentityAPI().createGroup(\"groupM\", null);\n        final UserMembership userMembership = getIdentityAPI().addUserMembership(userM.getId(), groupM.getId(),\n                roleM.getId());\n\n        final Group group = getIdentityAPI().getGroup(userMembership.getGroupId());\n        final Role role = getIdentityAPI().getRole(userMembership.getRoleId());\n        try {\n            assertEquals(\"groupM\", group.getName());\n            assertEquals(\"roleM\", role.getName());\n        } finally {\n            getIdentityAPI().deleteUserMembership(userM.getId(), groupM.getId(), roleM.getId());\n            getIdentityAPI().deleteUser(userM.getId());\n            getIdentityAPI().deleteRole(roleM.getId());\n            getIdentityAPI().deleteGroup(groupM.getId());\n        }\n    }\n\n    @Test\n    public void getMembershipByMembershipId() throws BonitaException {\n        final User userM = getIdentityAPI().createUser(\"userM\", \"engine\");\n        final Role roleM = getIdentityAPI().createRole(\"roleM\");\n        final Group groupM = getIdentityAPI().createGroup(\"groupM\", null);\n        final UserMembership userMembership = getIdentityAPI().addUserMembership(userM.getId(), groupM.getId(),\n                roleM.getId());\n        try {\n            final UserMembership membership2 = getIdentityAPI().getUserMembership(userMembership.getId());\n            assertNotNull(membership2);\n            assertEquals(userM.getId(), membership2.getUserId());\n            assertEquals(roleM.getId(), membership2.getRoleId());\n            assertEquals(groupM.getId(), membership2.getGroupId());\n            assertEquals(userM.getUserName(), membership2.getUsername());\n            assertEquals(roleM.getName(), membership2.getRoleName());\n            assertEquals(groupM.getName(), membership2.getGroupName());\n        } finally {\n            getIdentityAPI().deleteUserMembership(userM.getId(), groupM.getId(), roleM.getId());\n            getIdentityAPI().deleteUser(userM.getId());\n            getIdentityAPI().deleteRole(roleM.getId());\n            getIdentityAPI().deleteGroup(groupM.getId());\n        }\n    }\n\n    @Test\n    public void getAssignedByOnUserMembership() throws BonitaException, InterruptedException {\n        final User userM = createUser(\"aTest\", \"engine\");\n        final User plop = createUser(\"plop\", \"bpm\");\n        final Role roleM = getIdentityAPI().createRole(\"roleM\");\n        final Group groupM = getIdentityAPI().createGroup(\"groupM\", null);\n        logout();\n        loginOnDefaultTenantWith(plop.getUserName(), \"bpm\");\n        final Date beforeDate = new Date();\n        Thread.sleep(10);\n        final UserMembership userMembership = getIdentityAPI().addUserMembership(userM.getId(), groupM.getId(),\n                roleM.getId());\n        Thread.sleep(10);\n        final Date afterDate = new Date();\n        try {\n            final UserMembership membership2 = getIdentityAPI().getUserMembership(userMembership.getId());\n            assertNotNull(membership2);\n            assertEquals(plop.getId(), membership2.getAssignedBy());\n            assertTrue(\n                    \"exprected date between <\" + beforeDate + \"> and <\" + afterDate + \"> but was <\"\n                            + membership2.getAssignedDate() + \">\",\n                    membership2\n                            .getAssignedDate().compareTo(beforeDate) > 0\n                            && membership2.getAssignedDate().compareTo(afterDate) < 0);\n            assertEquals(userM.getId(), membership2.getUserId());\n            assertEquals(roleM.getId(), membership2.getRoleId());\n            assertEquals(groupM.getId(), membership2.getGroupId());\n        } finally {\n            getIdentityAPI().deleteUserMembership(userM.getId(), groupM.getId(), roleM.getId());\n            getIdentityAPI().deleteUser(userM.getId());\n            getIdentityAPI().deleteUser(plop.getId());\n            getIdentityAPI().deleteRole(roleM.getId());\n            getIdentityAPI().deleteGroup(groupM.getId());\n        }\n    }\n\n    @Test\n    public void getNumberOfUserMembershipsByUserId() {\n        assertEquals(4, getIdentityAPI().getNumberOfUserMemberships(user1.getId()));\n        assertEquals(1, getIdentityAPI().getNumberOfUserMemberships(user4.getId()));\n    }\n\n    @Test\n    public void updateUserMemberships() throws BonitaException {\n        final User userM = getIdentityAPI().createUser(\"userM\", \"engine\");\n        final Role roleM = getIdentityAPI().createRole(\"roleM\");\n        final Group groupM = getIdentityAPI().createGroup(\"groupM\", null);\n        final UserMembership userMembership = getIdentityAPI().addUserMembership(userM.getId(), groupM.getId(),\n                roleM.getId());\n        assertEquals(groupM.getId(), userMembership.getGroupId());\n        assertEquals(roleM.getId(), userMembership.getRoleId());\n\n        final UserMembership newMembership = getIdentityAPI().updateUserMembership(userMembership.getId(),\n                group4.getId(), role3.getId());\n        assertEquals(group4.getId(), newMembership.getGroupId());\n        assertEquals(role3.getId(), newMembership.getRoleId());\n        getIdentityAPI().deleteUserMembership(newMembership.getId());\n        getIdentityAPI().deleteRole(roleM.getId());\n        getIdentityAPI().deleteGroup(groupM.getId());\n        getIdentityAPI().deleteUser(userM.getId());\n    }\n\n    @Test\n    public void getUserMembershipsByRole() {\n        List<UserMembership> userMemberships = getIdentityAPI().getUserMembershipsByRole(role1.getId(), 0, 500);\n        assertEquals(2, userMemberships.size());\n\n        userMemberships = getIdentityAPI().getUserMembershipsByRole(role1.getId(), 0, 1);\n        assertEquals(1, userMemberships.size());\n\n        userMemberships = getIdentityAPI().getUserMembershipsByRole(role1.getId(), 1, 1);\n        assertEquals(1, userMemberships.size());\n\n        userMemberships = getIdentityAPI().getUserMembershipsByRole(role1.getId(), 20, 500);\n        assertEquals(0, userMemberships.size());\n    }\n\n    @Test\n    public void getUserMembershipsByGroup() {\n        List<UserMembership> userMemberships = getIdentityAPI().getUserMembershipsByGroup(group1.getId(), 0, 500);\n        assertEquals(1, userMemberships.size());\n        userMemberships = getIdentityAPI().getUserMembershipsByGroup(group3.getId(), 0, 500);\n        assertEquals(3, userMemberships.size());\n        userMemberships = getIdentityAPI().getUserMembershipsByGroup(group3.getId(), 0, 2);\n        assertEquals(2, userMemberships.size());\n        userMemberships = getIdentityAPI().getUserMembershipsByGroup(group3.getId(), 1, 2);\n        assertEquals(2, userMemberships.size());\n        userMemberships = getIdentityAPI().getUserMembershipsByGroup(group3.getId(), 20, 2);\n        assertEquals(0, userMemberships.size());\n    }\n\n    @Test\n    public void getUserMembershipsOrderByRoleNameAsc() {\n        final List<UserMembership> userMemberships = getIdentityAPI().getUserMemberships(user1.getId(), 0, 3,\n                UserMembershipCriterion.ROLE_NAME_ASC);\n        assertEquals(3, userMemberships.size());\n        assertEquals(role1.getName(), userMemberships.get(0).getRoleName());\n        assertEquals(role2.getName(), userMemberships.get(1).getRoleName());\n        assertEquals(role3.getName(), userMemberships.get(2).getRoleName());\n    }\n\n    @Test\n    public void getUserMembershipsOrderByRoleNameDesc() {\n        final List<UserMembership> userMemberships = getIdentityAPI().getUserMemberships(user1.getId(), 0, 3,\n                UserMembershipCriterion.ROLE_NAME_DESC);\n        assertEquals(3, userMemberships.size());\n        assertEquals(role4.getName(), userMemberships.get(0).getRoleName());\n        assertEquals(role3.getName(), userMemberships.get(1).getRoleName());\n        assertEquals(role2.getName(), userMemberships.get(2).getRoleName());\n    }\n\n    @Test\n    public void getUserMembershipsByGroupNameAsc() {\n        final List<UserMembership> userMemberships = getIdentityAPI().getUserMemberships(user1.getId(), 0, 3,\n                UserMembershipCriterion.GROUP_NAME_ASC);\n        assertEquals(3, userMemberships.size());\n        assertEquals(group1.getName(), userMemberships.get(0).getGroupName());\n        assertEquals(group2.getName(), userMemberships.get(1).getGroupName());\n        assertEquals(group3.getName(), userMemberships.get(2).getGroupName());\n    }\n\n    @Test\n    public void getUserMembershipsByGroupNameDesc() {\n        final List<UserMembership> userMemberships = getIdentityAPI().getUserMemberships(user1.getId(), 0, 3,\n                UserMembershipCriterion.GROUP_NAME_DESC);\n        assertEquals(3, userMemberships.size());\n        assertEquals(group4.getName(), userMemberships.get(0).getGroupName());\n        assertEquals(group3.getName(), userMemberships.get(1).getGroupName());\n        assertEquals(group2.getName(), userMemberships.get(2).getGroupName());\n    }\n\n    @Test\n    public void getAssignedBy() {\n        final List<UserMembership> userMemberships = getIdentityAPI().getUserMemberships(user1.getId(), 0, 3,\n                UserMembershipCriterion.ASSIGNED_DATE_DESC);\n        assertEquals(user1.getId(), userMemberships.get(0).getAssignedBy());\n    }\n\n    @Test\n    public void getUserMembershipsByAssignedDateAsc() {\n        final List<UserMembership> userMemberships = getIdentityAPI().getUserMemberships(user1.getId(), 0, 3,\n                UserMembershipCriterion.ASSIGNED_DATE_ASC);\n        assertEquals(3, userMemberships.size());\n        assertEquals(role1.getName(), userMemberships.get(0).getRoleName());\n        assertEquals(role2.getName(), userMemberships.get(1).getRoleName());\n        assertEquals(role3.getName(), userMemberships.get(2).getRoleName());\n    }\n\n    @Test\n    public void getUserMembershipsByAssignedDateDesc() {\n        final List<UserMembership> userMemberships = getIdentityAPI().getUserMemberships(user1.getId(), 0, 3,\n                UserMembershipCriterion.ASSIGNED_DATE_DESC);\n        assertEquals(3, userMemberships.size());\n        assertEquals(role4.getName(), userMemberships.get(0).getRoleName());\n        assertEquals(role3.getName(), userMemberships.get(1).getRoleName());\n        assertEquals(role2.getName(), userMemberships.get(2).getRoleName());\n    }\n\n    @Test\n    public void deleteUserMemberships() throws BonitaException {\n        final User userM = getIdentityAPI().createUser(\"userM\", \"engine\");\n        final Role roleM = getIdentityAPI().createRole(\"roleM\");\n        final Group groupM = getIdentityAPI().createGroup(\"groupM\", null);\n        getIdentityAPI().addUserMembership(userM.getId(), groupM.getId(), roleM.getId());\n\n        assertEquals(1, getIdentityAPI().getNumberOfUserMemberships(userM.getId()));\n        getIdentityAPI().deleteUserMembership(userM.getId(), groupM.getId(), roleM.getId());\n        assertEquals(0, getIdentityAPI().getNumberOfUserMemberships(userM.getId()));\n        getIdentityAPI().deleteUser(userM.getId());\n        getIdentityAPI().deleteRole(roleM.getId());\n        getIdentityAPI().deleteGroup(groupM.getId());\n    }\n\n    @Test\n    public void deleteUserMembership() throws BonitaException {\n        final User userT = getIdentityAPI().createUser(\"userT\", \"engine\");\n        final Role roleT = getIdentityAPI().createRole(\"roleT\");\n        final Group groupT = getIdentityAPI().createGroup(\"groupT\", null);\n        final UserMembership membership1 = getIdentityAPI().addUserMembership(userT.getId(), groupT.getId(),\n                roleT.getId());\n        assertEquals(1, getIdentityAPI().getNumberOfUserMemberships(userT.getId()));\n\n        final UserMembership membership2 = getIdentityAPI().getUserMembership(membership1.getId());\n        getIdentityAPI().deleteUserMembership(membership2.getId());\n        assertEquals(0, getIdentityAPI().getNumberOfUserMemberships(userT.getId()));\n        getIdentityAPI().deleteUser(userT.getId());\n        getIdentityAPI().deleteRole(roleT.getId());\n        getIdentityAPI().deleteGroup(groupT.getId());\n    }\n\n    @Test\n    public void deleteUserMembershipsByDeleteRole() throws BonitaException {\n        final User userM = getIdentityAPI().createUser(\"userR\", \"engine\");\n        final Role roleM = getIdentityAPI().createRole(\"roleR\");\n        final Group groupM1 = getIdentityAPI().createGroup(\"groupR1\", null);\n        final Group groupM2 = getIdentityAPI().createGroup(\"groupR2\", null);\n        getIdentityAPI().addUserMembership(userM.getId(), groupM1.getId(), roleM.getId());\n        getIdentityAPI().addUserMembership(userM.getId(), groupM2.getId(), roleM.getId());\n\n        assertEquals(2, getIdentityAPI().getNumberOfUserMemberships(userM.getId()));\n        getIdentityAPI().deleteRole(roleM.getId());\n        assertEquals(0, getIdentityAPI().getNumberOfUserMemberships(userM.getId()));\n\n        getIdentityAPI().deleteUser(userM.getId());\n        getIdentityAPI().deleteGroup(groupM1.getId());\n        getIdentityAPI().deleteGroup(groupM2.getId());\n    }\n\n    @Test\n    public void deleteUserMembershipsByDeleteGroup() throws BonitaException {\n        final User userM = getIdentityAPI().createUser(\"userR\", \"engine\");\n        final Role roleM1 = getIdentityAPI().createRole(\"roleR1\");\n        final Role roleM2 = getIdentityAPI().createRole(\"roleR2\");\n        final Group groupM = getIdentityAPI().createGroup(\"groupR\", null);\n        getIdentityAPI().addUserMembership(userM.getId(), groupM.getId(), roleM1.getId());\n        getIdentityAPI().addUserMembership(userM.getId(), groupM.getId(), roleM2.getId());\n\n        assertEquals(2, getIdentityAPI().getNumberOfUserMemberships(userM.getId()));\n        getIdentityAPI().deleteGroup(groupM.getId());\n        assertEquals(0, getIdentityAPI().getNumberOfUserMemberships(userM.getId()));\n\n        getIdentityAPI().deleteUser(userM.getId());\n        getIdentityAPI().deleteRole(roleM1.getId());\n        getIdentityAPI().deleteRole(roleM2.getId());\n    }\n\n    private void createTestedUserMemberships() throws BonitaException, InterruptedException {\n        user1 = createUser(\"userM1\", \"engine1\");\n        logout();\n        loginOnDefaultTenantWith(user1.getUserName(), \"engine1\");\n        user2 = createUser(\"userM2\", \"engine2\");\n        user3 = createUser(\"userM3\", \"engine3\");\n        user4 = createUser(\"userM4\", \"engine4\");\n        final RoleCreator roleCreator1 = new RoleCreator(\"roleM1\");\n        roleCreator1.setDisplayName(\"roleLabel\").setDescription(\"create role for userMembership\");\n        role1 = getIdentityAPI().createRole(roleCreator1);\n        final RoleCreator roleCreator2 = new RoleCreator(\"roleM2\");\n        roleCreator2.setDisplayName(\"roleLabel\").setDescription(\"create role for userMembership\");\n        role2 = getIdentityAPI().createRole(roleCreator2);\n        final RoleCreator roleCreator3 = new RoleCreator(\"roleM3\");\n        roleCreator3.setDisplayName(\"roleLabel\").setDescription(\"create role for userMembership\");\n        role3 = getIdentityAPI().createRole(roleCreator3);\n        final RoleCreator roleCreator4 = new RoleCreator(\"roleM4\");\n        roleCreator4.setDisplayName(\"roleLabel\").setDescription(\"create role for userMembership\");\n        role4 = getIdentityAPI().createRole(roleCreator4);\n        group1 = createGroup(\"groupM1\", \"grouplabel\", \"create group for userMembership\");\n        group2 = createGroup(\"groupM2\", \"grouplabel\", \"create group for userMembership\");\n        group3 = createGroup(\"groupM3\", \"grouplabel\", \"create group for userMembership\");\n        group4 = createGroup(\"groupM4\", \"grouplabel\", \"create group for userMembership\");\n        getIdentityAPI().addUserMembership(user1.getId(), group1.getId(), role1.getId());\n        Thread.sleep(10);\n        getIdentityAPI().addUserMembership(user1.getId(), group2.getId(), role2.getId());\n        Thread.sleep(10);\n        getIdentityAPI().addUserMembership(user1.getId(), group3.getId(), role3.getId());\n        Thread.sleep(10);\n        getIdentityAPI().addUserMembership(user1.getId(), group4.getId(), role4.getId());\n        Thread.sleep(10);\n        getIdentityAPI().addUserMembership(user2.getId(), group3.getId(), role4.getId());\n        getIdentityAPI().addUserMembership(user3.getId(), group4.getId(), role2.getId());\n        getIdentityAPI().addUserMembership(user4.getId(), group3.getId(), role1.getId());\n    }\n\n    private void deleteTestedUserMemberships() throws BonitaException {\n        getIdentityAPI().deleteUserMembership(user1.getId(), group1.getId(), role1.getId());\n        getIdentityAPI().deleteUserMembership(user1.getId(), group2.getId(), role2.getId());\n        getIdentityAPI().deleteUserMembership(user1.getId(), group3.getId(), role3.getId());\n        getIdentityAPI().deleteUserMembership(user1.getId(), group4.getId(), role4.getId());\n        getIdentityAPI().deleteUserMembership(user2.getId(), group3.getId(), role4.getId());\n        getIdentityAPI().deleteUserMembership(user3.getId(), group4.getId(), role2.getId());\n        getIdentityAPI().deleteUserMembership(user4.getId(), group3.getId(), role1.getId());\n        getIdentityAPI().deleteUser(user1.getId());\n        getIdentityAPI().deleteUser(user2.getId());\n        getIdentityAPI().deleteUser(user3.getId());\n        getIdentityAPI().deleteUser(user4.getId());\n        getIdentityAPI().deleteRole(role1.getId());\n        getIdentityAPI().deleteRole(role2.getId());\n        getIdentityAPI().deleteRole(role3.getId());\n        getIdentityAPI().deleteRole(role4.getId());\n        getIdentityAPI().deleteGroup(group1.getId());\n        getIdentityAPI().deleteGroup(group2.getId());\n        getIdentityAPI().deleteGroup(group3.getId());\n        getIdentityAPI().deleteGroup(group4.getId());\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/identity/OrganizationIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport static java.nio.charset.StandardCharsets.UTF_8;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.tuple;\nimport static org.junit.Assert.*;\n\nimport java.io.*;\nimport java.util.*;\nimport java.util.Map.Entry;\n\nimport javax.xml.parsers.DocumentBuilder;\nimport javax.xml.parsers.DocumentBuilderFactory;\nimport javax.xml.transform.Transformer;\nimport javax.xml.transform.TransformerFactory;\nimport javax.xml.transform.dom.DOMSource;\nimport javax.xml.transform.stream.StreamResult;\n\nimport org.apache.commons.io.IOUtils;\nimport org.bonitasoft.engine.TestWithTechnicalUser;\nimport org.bonitasoft.engine.bpm.process.ConfigurationState;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.identity.GroupCreator.GroupField;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.test.StartProcessUntilStep;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.SystemOutRule;\nimport org.w3c.dom.Document;\nimport org.w3c.dom.Node;\nimport org.xml.sax.InputSource;\n\npublic class OrganizationIT extends TestWithTechnicalUser {\n\n    private static final String SKILLS_VALUE = \"Java\";\n\n    private static final String SKILLS_UPDATED_VALUE = \"Java, Groovy\";\n\n    private static final String LOCATION_VALUE = \"Engineering\";\n\n    private static final String QA = \"QA\";\n\n    private static final String ENGINE = \"Engine\";\n\n    private static final String WEB_TEAM_MANAGER = \"Web Team Manager\";\n\n    private static final String JOHNNYFOOTBALL = \"johnnyfootball\";\n\n    private static final String DEVELOPER = \"Developer\";\n\n    private static final String WEB_TEAM = \"web team\";\n\n    private static final String BONITA_MANAGER = \"Bonita Manager\";\n\n    private static final String MANAGER = \"Manager\";\n\n    private static final String WEB_GROUP_NAME = \"Web\";\n\n    private static final String LIUYANYAN_USERNAME = \"liuyanyan\";\n\n    private static final String ANTHONY_USERNAME = \"anthony.birembault\";\n\n    private static final String SKILLS_DESCRIPTION = \"The user skills\";\n\n    private static final String SKILLS_NAME = \"Skills\";\n\n    private static final String LOCATION_NAME = \"Office location\";\n\n    private static Date defaultUserLastUpdateDate = null;\n\n    private static Date defaultGroupLastUpdateDate = null;\n\n    private static Date defaultRoleLastUpdateDate = null;\n\n    @Rule\n    public final SystemOutRule systemOutRule = new SystemOutRule();\n\n    @Test\n    public void importOrganization() throws Exception {\n        // when\n        importOrganization(\"simpleOrganization.xml\");\n\n        // then\n        final Map<String, CustomUserInfoDefinition> userInfoDefinitonsMap = checkDefaultCustomUserInfoDefinitions();\n        checkDefaultUsers();\n        checkDefaultCustomUserInfoValues(userInfoDefinitonsMap);\n        checkDefaultGroups();\n        checkDefaultRoles();\n        checkDefaultMembership();\n\n        // clean-up\n        getIdentityAPI().deleteOrganization();\n    }\n\n    private void checkDefaultCustomUserInfoValues(final Map<String, CustomUserInfoDefinition> userInfoDefinitonsMap)\n            throws Exception {\n        checkDefaultCustomUserInfoValueForFirstUser(userInfoDefinitonsMap);\n        checkDefaultCustomUserInfoValueForSecondUser(userInfoDefinitonsMap);\n    }\n\n    private void checkCustomUserInfoValuesAfterUpdate(final Map<String, CustomUserInfoDefinition> userInfoDefinitonsMap)\n            throws Exception {\n        // the first user is not present in the second file to import, so his information keep the same\n        checkDefaultCustomUserInfoValueForFirstUser(userInfoDefinitonsMap);\n        checkCustomUserInfoValueForSecondUserAfterUpdate(userInfoDefinitonsMap);\n    }\n\n    private void checkDefaultCustomUserInfoValueForSecondUser(\n            final Map<String, CustomUserInfoDefinition> userInfoDefinitonsMap) throws UserNotFoundException {\n        final User user = getIdentityAPI().getUserByUserName(LIUYANYAN_USERNAME);\n        final SearchOptions searchOptions = getCustomUserInfoValueSearchOptions(user);\n        final SearchResult<CustomUserInfoValue> searchResult = getIdentityAPI()\n                .searchCustomUserInfoValues(searchOptions);\n        assertThat(searchResult.getCount()).isEqualTo(1);\n        checkCustomUserInfo(searchResult.getResult().get(0), SKILLS_NAME, SKILLS_VALUE, userInfoDefinitonsMap);\n    }\n\n    private void checkCustomUserInfoValueForSecondUserAfterUpdate(\n            final Map<String, CustomUserInfoDefinition> userInfoDefinitonsMap)\n            throws UserNotFoundException {\n        final User user = getIdentityAPI().getUserByUserName(LIUYANYAN_USERNAME);\n        final SearchOptions searchOptions = getCustomUserInfoValueSearchOptions(user);\n        final SearchResult<CustomUserInfoValue> searchResult = getIdentityAPI()\n                .searchCustomUserInfoValues(searchOptions);\n        assertThat(searchResult.getCount()).isEqualTo(2);\n        checkCustomUserInfo(searchResult.getResult().get(0), LOCATION_NAME, LOCATION_VALUE, userInfoDefinitonsMap);\n        checkCustomUserInfo(searchResult.getResult().get(1), SKILLS_NAME, SKILLS_UPDATED_VALUE, userInfoDefinitonsMap);\n    }\n\n    private void checkDefaultCustomUserInfoValueForFirstUser(\n            final Map<String, CustomUserInfoDefinition> userInfoDefinitonsMap) throws UserNotFoundException {\n        final User user = getIdentityAPI().getUserByUserName(ANTHONY_USERNAME);\n        final SearchOptions searchOptions = getCustomUserInfoValueSearchOptions(user);\n\n        final SearchResult<CustomUserInfoValue> searchResult = getIdentityAPI()\n                .searchCustomUserInfoValues(searchOptions);\n        assertThat(searchResult.getCount()).isEqualTo(2);\n\n        checkCustomUserInfo(searchResult.getResult().get(0), LOCATION_NAME, LOCATION_VALUE, userInfoDefinitonsMap);\n        checkCustomUserInfo(searchResult.getResult().get(1), SKILLS_NAME, SKILLS_VALUE, userInfoDefinitonsMap);\n    }\n\n    private SearchOptions getCustomUserInfoValueSearchOptions(final User user) {\n        final SearchOptionsBuilder optionsBuilder = new SearchOptionsBuilder(0, 10);\n        optionsBuilder.filter(CustomUserInfoValueSearchDescriptor.USER_ID, user.getId());\n        optionsBuilder.sort(CustomUserInfoValueSearchDescriptor.DEFINITION_ID, Order.ASC);\n        return optionsBuilder.done();\n    }\n\n    private void checkCustomUserInfo(final CustomUserInfoValue customUserInfoValue, final String expectedName,\n            final String expectedValue,\n            final Map<String, CustomUserInfoDefinition> userInfoDefinitonsMap) {\n        assertThat(customUserInfoValue.getDefinitionId()).isEqualTo(userInfoDefinitonsMap.get(expectedName).getId());\n        assertThat(customUserInfoValue.getValue()).isEqualTo(expectedValue);\n    }\n\n    private Map<String, CustomUserInfoDefinition> checkDefaultCustomUserInfoDefinitions() {\n        final List<CustomUserInfoDefinition> customUserInfoDefinitions = getIdentityAPI()\n                .getCustomUserInfoDefinitions(0, 10);\n        assertThat(customUserInfoDefinitions.size()).isEqualTo(2);\n\n        final CustomUserInfoDefinition firstDefinition = customUserInfoDefinitions.get(0);\n        final CustomUserInfoDefinition secondDefinition = customUserInfoDefinitions.get(1);\n\n        checkCustomUserInfoDefinition(LOCATION_NAME, null, firstDefinition);\n        checkCustomUserInfoDefinition(SKILLS_NAME, SKILLS_DESCRIPTION, secondDefinition);\n\n        final Map<String, CustomUserInfoDefinition> userInfoDefMap = new HashMap<>(2);\n        userInfoDefMap.put(firstDefinition.getName(), firstDefinition);\n        userInfoDefMap.put(secondDefinition.getName(), secondDefinition);\n        return userInfoDefMap;\n    }\n\n    private Map<String, CustomUserInfoDefinition> checkCustomUserInfoDefinitionsAfterUpdate() {\n        final List<CustomUserInfoDefinition> customUserInfoDefinitions = getIdentityAPI()\n                .getCustomUserInfoDefinitions(0, 10);\n        assertThat(customUserInfoDefinitions.size()).isEqualTo(2);\n\n        final CustomUserInfoDefinition firstDefinition = customUserInfoDefinitions.get(0);\n        final CustomUserInfoDefinition secondDefinition = customUserInfoDefinitions.get(1);\n\n        checkCustomUserInfoDefinition(LOCATION_NAME, \"The office location\", firstDefinition);\n        checkCustomUserInfoDefinition(SKILLS_NAME, \"The user skills were updated\", secondDefinition);\n\n        final Map<String, CustomUserInfoDefinition> userInfoDefMap = new HashMap<>(2);\n        userInfoDefMap.put(firstDefinition.getName(), firstDefinition);\n        userInfoDefMap.put(secondDefinition.getName(), secondDefinition);\n        return userInfoDefMap;\n    }\n\n    private void checkCustomUserInfoDefinition(final String expectedName, final String expectedDescription,\n            final CustomUserInfoDefinition customUserInfoDefinition) {\n        assertThat(customUserInfoDefinition.getName()).isEqualTo(expectedName);\n        assertThat(customUserInfoDefinition.getDescription()).isEqualTo(expectedDescription);\n    }\n\n    @Test\n    public void reImportUserMembershipDeleted() throws Exception {\n        final String userName = ANTHONY_USERNAME;\n\n        importOrganization(\"simpleOrganization.xml\");\n        final User persistedUser = getIdentityAPI().getUserByUserName(userName);\n        final List<UserMembership> userMemberships = getIdentityAPI().getUserMemberships(persistedUser.getId(), 0, 10,\n                UserMembershipCriterion.ASSIGNED_DATE_ASC);\n        assertEquals(1, userMemberships.size());\n\n        getIdentityAPI().deleteUserMembership(userMemberships.get(0).getId());\n\n        // Re-import organization\n        importOrganization(\"simpleOrganization.xml\");\n        final User persistedUser2 = getIdentityAPI().getUserByUserName(userName);\n        final List<UserMembership> userMemberships2 = getIdentityAPI().getUserMemberships(persistedUser2.getId(), 0, 10,\n                UserMembershipCriterion.ASSIGNED_DATE_ASC);\n        assertEquals(1, userMemberships2.size());\n\n        // clean-up\n        getIdentityAPI().deleteOrganization();\n    }\n\n    private void importOrganization(final String fileName) throws IOException, OrganizationImportException {\n        try (InputStream xmlStream = OrganizationIT.class.getResourceAsStream(fileName)) {\n            getIdentityAPI().importOrganization(IOUtils.toString(xmlStream, UTF_8));\n        }\n    }\n\n    @Test\n    public void importOrganizationWithEnabledAndDisabledUsers() throws Exception {\n        // create XML file\n        importOrganization(\"simpleOrganization.xml\");\n\n        final User persistedUser = getIdentityAPI().getUserByUserName(ANTHONY_USERNAME);\n        assertEquals(WEB_TEAM_MANAGER, persistedUser.getJobTitle());\n        assertFalse(persistedUser.isEnabled());\n        // check createdBy for user\n        assertEquals(getSession().getUserId(), persistedUser.getCreatedBy());\n\n        final User persistedUser1 = getIdentityAPI().getUserByUserName(LIUYANYAN_USERNAME);\n        assertNotNull(persistedUser1);\n        assertTrue(persistedUser1.isEnabled());\n\n        // clean-up\n        getIdentityAPI().deleteUser(persistedUser.getId());\n        getIdentityAPI().deleteUser(persistedUser1.getId());\n\n        final Role persistedRole = getIdentityAPI().getRoleByName(MANAGER);\n        assertNotNull(persistedRole);\n        getIdentityAPI().deleteRole(persistedRole.getId());\n        final Role persistedRole1 = getIdentityAPI().getRoleByName(DEVELOPER);\n        assertNotNull(persistedRole1);\n        getIdentityAPI().deleteRole(persistedRole1.getId());\n\n        final Group persistedGroup = getIdentityAPI().getGroupByPath(WEB_GROUP_NAME);\n        assertNotNull(persistedGroup);\n        getIdentityAPI().deleteGroup(persistedGroup.getId());\n        final Group persistedGroup1 = getIdentityAPI().getGroupByPath(ENGINE);\n        assertNotNull(persistedGroup1);\n        getIdentityAPI().deleteGroup(persistedGroup1.getId());\n    }\n\n    @Test\n    public void importComplexOrganization() throws Exception {\n        // create XML file\n        try (InputStream xmlStream = OrganizationIT.class.getResourceAsStream(\"complexOrganization.xml\")) {\n            final byte[] organisationContent = IOUtils.toByteArray(xmlStream);\n            getIdentityAPI().importOrganization(new String(organisationContent));\n        }\n        final User jack = getIdentityAPI().getUserByUserName(\"jack\");\n        final User john = getIdentityAPI().getUserByUserName(\"john\");\n        assertEquals(\"John\", john.getFirstName());\n        assertEquals(\"Doe\", john.getLastName());\n        assertEquals(\"M\", john.getTitle());\n        assertEquals(jack.getId(), john.getManagerUserId());\n        assertEquals(WEB_TEAM_MANAGER, john.getJobTitle());\n        final ContactData johnPersoContactData = getIdentityAPI().getUserContactData(john.getId(), true);\n        assertEquals(\"emailValue\", johnPersoContactData.getEmail());\n        assertEquals(\"phoneNumberValue\", johnPersoContactData.getPhoneNumber());\n        assertEquals(\"mobileNumberValue\", johnPersoContactData.getMobileNumber());\n        assertEquals(\"faxNumberValue\", johnPersoContactData.getFaxNumber());\n        assertEquals(\"buildingValue\", johnPersoContactData.getBuilding());\n        assertEquals(\"roomValue\", johnPersoContactData.getRoom());\n        assertEquals(\"addressValue\", johnPersoContactData.getAddress());\n        assertEquals(\"zipCodeValue\", johnPersoContactData.getZipCode());\n        assertEquals(\"cityValue\", johnPersoContactData.getCity());\n        assertEquals(\"stateValue\", johnPersoContactData.getState());\n        assertEquals(\"countryValue\", johnPersoContactData.getCountry());\n        assertEquals(\"websiteValue\", johnPersoContactData.getWebsite());\n        final ContactData johnProfessionalContactData = getIdentityAPI().getUserContactData(john.getId(), false);\n        assertEquals(\"emailProfessionalValue\", johnProfessionalContactData.getEmail());\n        assertEquals(\"phoneNumberProfessionalValue\", johnProfessionalContactData.getPhoneNumber());\n        assertEquals(\"mobileNumberProfessionalValue\", johnProfessionalContactData.getMobileNumber());\n        assertEquals(\"faxNumberProfessionalValue\", johnProfessionalContactData.getFaxNumber());\n        assertEquals(\"buildingProfessionalValue\", johnProfessionalContactData.getBuilding());\n        assertEquals(\"roomProfessionalValue\", johnProfessionalContactData.getRoom());\n        assertEquals(\"addressProfessionalValue\", johnProfessionalContactData.getAddress());\n        assertEquals(\"zipCodeProfessionalValue\", johnProfessionalContactData.getZipCode());\n        assertEquals(\"cityProfessionalValue\", johnProfessionalContactData.getCity());\n        assertEquals(\"stateProfessionalValue\", johnProfessionalContactData.getState());\n        assertEquals(\"countryProfessionalValue\", johnProfessionalContactData.getCountry());\n        assertEquals(\"websiteProfessionalValue\", johnProfessionalContactData.getWebsite());\n\n        getIdentityAPI().getUserByUserName(\"james\");\n\n        final Group bonitaRD = getIdentityAPI().getGroupByPath(\"/BonitaSoft/RD\");\n        final Group rd = getIdentityAPI().getGroupByPath(\"/RD\");\n        getIdentityAPI().getGroupByPath(\"/BonitaSoft/RD\");\n\n        List<UserMembership> userMemberships = getIdentityAPI().getUserMemberships(john.getId(), 0, 10,\n                UserMembershipCriterion.GROUP_NAME_ASC);\n        assertEquals(1, userMemberships.size());\n        UserMembership userMembership = userMemberships.get(0);\n        assertEquals(bonitaRD.getId(), userMembership.getGroupId());\n        assertEquals(jack.getId(), userMembership.getAssignedBy());\n        assertEquals(new Date(1331142448365L), userMembership.getAssignedDate());\n        userMemberships = getIdentityAPI().getUserMemberships(jack.getId(), 0, 10,\n                UserMembershipCriterion.GROUP_NAME_ASC);\n        assertEquals(1, userMemberships.size());\n        userMembership = userMemberships.get(0);\n        assertEquals(rd.getId(), userMembership.getGroupId());\n\n        // clean-up\n        getIdentityAPI().deleteOrganization();\n    }\n\n    @Test\n    public void importOrganizationWithWarnings_return_no_warnings_on_good_XML()\n            throws IOException, OrganizationImportException, DeletionException {\n        //given\n        List<String> warnings;\n        try (final InputStream xmlStream = OrganizationIT.class.getResourceAsStream(\"complexOrganization.xml\")) {\n            final byte[] organisationContent = IOUtils.toByteArray(xmlStream);\n\n            //when\n            warnings = getIdentityAPI().importOrganizationWithWarnings(new String(organisationContent),\n                    ImportPolicy.IGNORE_DUPLICATES);\n        }\n\n        //then\n        assertThat(warnings).isEmpty();\n\n        // clean-up\n        getIdentityAPI().deleteOrganization();\n    }\n\n    @Test(expected = GroupNotFoundException.class)\n    public void importOrganizationWithWarnings_return_warnings_on_faulty_group_names()\n            throws DeletionException, OrganizationImportException, IOException, GroupNotFoundException {\n        //given\n        List<String> warnings;\n        try (final InputStream xmlStream = OrganizationIT.class\n                .getResourceAsStream(\"complexOrganizationWithBadGroup.xml\")) {\n            final byte[] organisationContent = IOUtils.toByteArray(xmlStream);\n\n            //when\n            warnings = getIdentityAPI().importOrganizationWithWarnings(new String(organisationContent),\n                    ImportPolicy.IGNORE_DUPLICATES);\n        }\n        //then\n        try {\n            assertThat(warnings).hasSize(1);\n            assertThat(warnings).contains(\n                    \"The group name RD/Studio contains the character '/' which is not supported. The group has not been imported\");\n            getIdentityAPI().getGroupByPath(\"/RD/Studio\");\n        } finally {\n            // clean-up\n            getIdentityAPI().deleteOrganization();\n        }\n    }\n\n    @Test\n    public void importOrganizationWithWarnings_imports_the_correct_groups_if_the_incorrect_one_is_present()\n            throws DeletionException, OrganizationImportException, IOException, GroupNotFoundException {\n        //given\n        try (final InputStream xmlStream = OrganizationIT.class\n                .getResourceAsStream(\"complexOrganizationWithBadGroup.xml\")) {\n            final byte[] organisationContent = IOUtils.toByteArray(xmlStream);\n\n            //when\n            getIdentityAPI().importOrganizationWithWarnings(new String(organisationContent),\n                    ImportPolicy.IGNORE_DUPLICATES);\n        }\n        //then\n        //Should not throw any exception\n        getIdentityAPI().getGroupByPath(\"/BonitaSoft\");\n        getIdentityAPI().getGroupByPath(\"/BonitaSoft/RD\");\n        getIdentityAPI().getGroupByPath(\"/BonitaSoft/Support\");\n        assertThat(getIdentityAPI().getNumberOfGroups()).isEqualTo(3);\n\n        // clean-up\n        getIdentityAPI().deleteOrganization();\n    }\n\n    @Test\n    public void importACMEOrganizationTwiceWithDefaultProfile() throws Exception {\n        try (InputStream xmlStream = OrganizationIT.class.getResourceAsStream(\"ACME.xml\")) {\n            final byte[] organisationContent = IOUtils.toByteArray(xmlStream);\n            getIdentityAPI().importOrganization(new String(organisationContent));\n            getIdentityAPI().importOrganization(new String(organisationContent));\n        }\n\n        // clean-up\n        getIdentityAPI().deleteOrganization();\n    }\n\n    @Test\n    public void should_not_update_existing_user_when_importing_organization_in_IGNORE_DUPLICATES() throws Exception {\n        User walter, oldManager, newManager;\n\n        // first upload of ACME organization\n        try (InputStream xmlStream = OrganizationIT.class.getResourceAsStream(\"ACME.xml\")) {\n            getIdentityAPI().importOrganizationWithWarnings(new String(IOUtils.toByteArray(xmlStream)),\n                    ImportPolicy.IGNORE_DUPLICATES);\n        }\n\n        //change walter.bates manager\n        walter = getIdentityAPI().getUserByUserName(\"walter.bates\");\n        //set new new manager\n        oldManager = getIdentityAPI().getUser(walter.getManagerUserId());\n        newManager = getIdentityAPI().getUserByUserName(\"william.jobs\");\n\n        //set new manager\n        assertThat(oldManager.getId()).isNotEqualTo(newManager.getId());\n        getIdentityAPI().updateUser(walter.getId(), new UserUpdater()\n                .setLastName(\"Bates-Jobs\")\n                .setManagerId(newManager.getId()));\n\n        //import again ACME orga\n        try (InputStream xmlStream = OrganizationIT.class.getResourceAsStream(\"ACME.xml\")) {\n            getIdentityAPI().importOrganizationWithWarnings(new String(IOUtils.toByteArray(xmlStream)),\n                    ImportPolicy.IGNORE_DUPLICATES);\n        }\n\n        //check if user still have the newManager as Manager\n        assertThat(getIdentityAPI().getUserByUserName(\"walter.bates\")).satisfies(u -> {\n            assertThat(u.getManagerUserId()).isEqualTo(newManager.getId());\n            assertThat(u.getLastName()).isEqualTo(\"Bates-Jobs\");\n        });\n\n        // clean-up\n        getIdentityAPI().deleteOrganization();\n    }\n\n    @Test\n    public void importACMEOrganizationTwiceButRemoveGroupsAndRole() throws Exception {\n        // create XML file\n\n        final byte[] organisationContent;\n        try (InputStream xmlStream = OrganizationIT.class.getResourceAsStream(\"ACME.xml\")) {\n            organisationContent = IOUtils.toByteArray(xmlStream);\n        }\n        getIdentityAPI().importOrganization(new String(organisationContent));\n        final long numberOfGroups = getIdentityAPI().getNumberOfGroups();\n        final long numberOfRoles = getIdentityAPI().getNumberOfRoles();\n        // remove some groups and roles\n        final List<Group> groups = getIdentityAPI().getGroups(1, 3, GroupCriterion.NAME_ASC);\n        final List<Long> groupIds = new ArrayList<>(groups.size());\n        for (final Group group : groups) {\n            groupIds.add(group.getId());\n        }\n        getIdentityAPI().deleteGroups(groupIds);\n        final List<Role> roles = getIdentityAPI().getRoles(0, 2, RoleCriterion.NAME_ASC);\n        final List<Long> roleIds = new ArrayList<>(roles.size());\n        for (final Role role : roles) {\n            roleIds.add(role.getId());\n        }\n        getIdentityAPI().deleteRoles(roleIds);\n\n        getIdentityAPI().importOrganization(new String(organisationContent));\n        assertEquals(numberOfGroups, getIdentityAPI().getNumberOfGroups());\n        assertEquals(numberOfRoles, getIdentityAPI().getNumberOfRoles());\n\n        // clean-up\n        getIdentityAPI().deleteOrganization();\n    }\n\n    @Test(expected = InvalidOrganizationFileFormatException.class)\n    public void importOrganizationWithOrganizationImportException() throws Exception {\n        final String xmlOrganization = \"\";\n        final User createUser = getIdentityAPI().createUser(\"john\", \"bpm\");\n        try {\n            getIdentityAPI().importOrganization(xmlOrganization);\n        } catch (final OrganizationImportException e) {\n            // check john was not deleted:\n            assertNotNull(\"import organization with a bad file made the organization to be deleted!\",\n                    getIdentityAPI().getUserByUserName(\"john\"));\n            deleteUser(createUser);\n            throw e;\n        }\n    }\n\n    @Test(expected = OrganizationImportException.class)\n    public void importOrganizationFailRollBackToOldOrganization() throws Exception {\n        try (InputStream xmlStream = OrganizationIT.class.getResourceAsStream(\"organizationFailOnDuplicates.xml\")) {\n            byte[] organisationContent = IOUtils.toByteArray(xmlStream);\n            final String xmlOrganization = new String(organisationContent);\n            getIdentityAPI().importOrganization(xmlOrganization);\n\n            // check john is replaced before james is imported (in the xml)\n            assertTrue(xmlOrganization, xmlOrganization.indexOf(\"james\") < xmlOrganization.indexOf(\"john\"));\n\n            // clear organization\n            getIdentityAPI().deleteOrganization();\n\n            // ensure james does not exist anymore\n            try {\n                getIdentityAPI().getUserByUserName(\"james\");\n                fail(\"james should not be found.\");\n            } catch (final UserNotFoundException unfe) {\n                // ok\n            }\n\n            // we have only \"john\" as user\n            final User createUser = getIdentityAPI().createUser(\"john\", \"bpm\", \"John\", null);\n\n            try {\n                organisationContent = IOUtils.toByteArray(xmlStream);\n                getIdentityAPI().importOrganizationWithWarnings(new String(organisationContent),\n                        ImportPolicy.FAIL_ON_DUPLICATES);\n            } catch (final OrganizationImportException e) {\n                // check john was not deleted and have old username\n                try {\n                    getIdentityAPI().getUserByUserName(\"james\");\n                    fail(\"old organisation should be restored\");\n                } catch (final UserNotFoundException unfe) {\n                    deleteUser(createUser);\n                    throw e;\n                }\n            }\n        }\n\n    }\n\n    @Test\n    public void importOrganizationWithCycle() throws Exception {\n        try (InputStream xmlStream = OrganizationIT.class.getResourceAsStream(\"organizationWithCycle.xml\")) {\n            final byte[] bs = IOUtils.toByteArray(xmlStream);\n            final String organizationContent = new String(bs);\n            getIdentityAPI().importOrganization(organizationContent);\n\n            // clean-up\n            getIdentityAPI().deleteOrganization();\n            getIdentityAPI().importOrganization(organizationContent);\n        }\n\n        final List<User> users = getIdentityAPI().getUsers(0, 10, UserCriterion.USER_NAME_ASC);\n        assertThat(users).extracting(\"userName\", \"managerUserId\").containsExactly(tuple(\"user1\", users.get(1).getId()),\n                tuple(\"user2\", users.get(2).getId()),\n                tuple(\"user3\", users.get(0).getId()));\n        assertEquals(3, users.size());\n\n        getIdentityAPI().deleteOrganization();\n    }\n\n    @Test\n    public void deleteOrganization() throws Exception {\n        // Create records for user role, group and membership\n        final User persistedUser1 = getIdentityAPI().createUser(LIUYANYAN_USERNAME, \"bpm\");\n        final User persistedUser2 = getIdentityAPI().createUser(ANTHONY_USERNAME, \"bpm\");\n\n        final RoleCreator rc1 = new RoleCreator(DEVELOPER);\n        rc1.setDisplayName(\"roleDisplayName\");\n        final Role persistedRole1 = getIdentityAPI().createRole(rc1);\n        final RoleCreator rc2 = new RoleCreator(MANAGER);\n        rc2.setDisplayName(\"roleDisplayName\");\n        final Role persistedRole2 = getIdentityAPI().createRole(rc2);\n\n        final GroupCreator groupCreator1 = new GroupCreator(ENGINE);\n        groupCreator1.setDisplayName(\"engine team\");\n        final Group persistedGroup1 = getIdentityAPI().createGroup(groupCreator1);\n\n        final GroupCreator groupCreator2 = new GroupCreator(WEB_GROUP_NAME);\n        groupCreator2.setDisplayName(WEB_TEAM);\n        final Group persistedGroup2 = getIdentityAPI().createGroup(groupCreator2);\n\n        getIdentityAPI().addUserMembership(persistedUser1.getId(), persistedGroup1.getId(), persistedRole1.getId());\n        getIdentityAPI().addUserMembership(persistedUser2.getId(), persistedGroup2.getId(), persistedRole2.getId());\n\n        getIdentityAPI().createCustomUserInfoDefinition(new CustomUserInfoDefinitionCreator(\"ToDelete\"));\n\n        assertEquals(1, getIdentityAPI().getNumberOfUserMemberships(persistedUser1.getId()));\n        assertEquals(2, getIdentityAPI().getNumberOfGroups());\n        assertEquals(2, getIdentityAPI().getNumberOfUsers());\n        assertEquals(2, getIdentityAPI().getNumberOfRoles());\n\n        // Create process that is mapped to user of organization\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder();\n        builder.createNewInstance(\"deleteAllHuman\", \"1.1\").addActor(ACTOR_NAME, true).addUserTask(\"human\", ACTOR_NAME);\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME,\n                persistedUser1);\n        final StartProcessUntilStep startProcessUntilStep = startProcessAndWaitForTask(processDefinition.getId(),\n                \"human\");\n        assignAndExecuteStep(startProcessUntilStep.getActivityInstance(), persistedUser1.getId());\n        waitForProcessToFinish(startProcessUntilStep.getProcessInstance());\n\n        // delete organization and do check\n        getIdentityAPI().deleteOrganization();\n        assertEquals(0, getIdentityAPI().getNumberOfGroups());\n        assertEquals(0, getIdentityAPI().getNumberOfUsers());\n        assertEquals(0, getIdentityAPI().getNumberOfRoles());\n        assertEquals(0, getIdentityAPI().getNumberOfUserMemberships(persistedUser1.getId()));\n        assertThat(getIdentityAPI().getCustomUserInfoDefinitions(0, 10)).isEmpty();\n\n        // reload the process deploy info:\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ConfigurationState.UNRESOLVED, processDeploymentInfo.getConfigurationState());\n\n        // Clean up\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test(expected = DeletionException.class)\n    public void cantDeleteOrganizationWhenProcessInstanceIsActive() throws Exception {\n        // Create records for user role, group and membership\n        final User persistedUser1 = getIdentityAPI().createUser(LIUYANYAN_USERNAME, \"bpm\");\n        final User persistedUser2 = getIdentityAPI().createUser(ANTHONY_USERNAME, \"bpm\");\n\n        final RoleCreator rc1 = new RoleCreator(DEVELOPER);\n        rc1.setDisplayName(\"roleDisplayName\");\n        final Role persistedRole1 = getIdentityAPI().createRole(rc1);\n        final RoleCreator rc2 = new RoleCreator(MANAGER);\n        rc2.setDisplayName(\"roleDisplayName\");\n        final Role persistedRole2 = getIdentityAPI().createRole(rc2);\n\n        final GroupCreator groupCreator1 = new GroupCreator(ENGINE);\n        groupCreator1.setDisplayName(\"engine team\");\n        final Group persistedGroup1 = getIdentityAPI().createGroup(groupCreator1);\n\n        final GroupCreator groupCreator2 = new GroupCreator(WEB_GROUP_NAME);\n        groupCreator2.setDisplayName(WEB_TEAM);\n        final Group persistedGroup2 = getIdentityAPI().createGroup(groupCreator2);\n\n        getIdentityAPI().addUserMembership(persistedUser1.getId(), persistedGroup1.getId(), persistedRole1.getId());\n        getIdentityAPI().addUserMembership(persistedUser2.getId(), persistedGroup2.getId(), persistedRole2.getId());\n\n        assertEquals(1, getIdentityAPI().getNumberOfUserMemberships(persistedUser1.getId()));\n        assertEquals(2, getIdentityAPI().getNumberOfGroups());\n        assertEquals(2, getIdentityAPI().getNumberOfUsers());\n        assertEquals(2, getIdentityAPI().getNumberOfRoles());\n\n        // Create process that is mapped to user of organization\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder();\n        builder.createNewInstance(\"deleteAllHuman\", \"1.1\").addActor(ACTOR_NAME, true).addUserTask(\"human\", ACTOR_NAME);\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME,\n                persistedUser1);\n        final StartProcessUntilStep startProcessUntilStep = startProcessAndWaitForTask(processDefinition.getId(),\n                \"human\");\n\n        // delete organization and do check\n        try {\n            getIdentityAPI().deleteOrganization();\n        } finally {\n            // Clean up\n            assignAndExecuteStep(startProcessUntilStep.getActivityInstance(), persistedUser1.getId());\n            waitForProcessToFinish(startProcessUntilStep.getProcessInstance());\n            getIdentityAPI().deleteOrganization();\n            disableAndDeleteProcess(processDefinition);\n        }\n    }\n\n    @Test(expected = OrganizationImportException.class)\n    public void importOrganizationFailOnDuplicates() throws Exception {\n        // create XML file\n        final ImportPolicy policy = ImportPolicy.FAIL_ON_DUPLICATES;\n\n        final String userName = ANTHONY_USERNAME;\n        final String roleName = MANAGER;\n        final String groupName = WEB_GROUP_NAME;\n\n        final String userName1 = LIUYANYAN_USERNAME;\n        final String roleName1 = DEVELOPER;\n        final String roleDisplayName1 = \"Bonita developer\";\n        final String groupName1 = ENGINE;\n        final String groupDisplayName1 = \"engine team\";\n\n        importAndCheckFirstSimpleOrganization();\n        final User userToDisable = getIdentityAPI().getUserByUserName(userName);\n        final UserUpdater updateDescriptor = new UserUpdater();\n        updateDescriptor.setEnabled(false);\n        getIdentityAPI().updateUser(userToDisable.getId(), updateDescriptor);\n        assertEquals(2, getIdentityAPI().getNumberOfUsers());\n        try {\n            importOrganizationWithPolicy(\"simpleOrganizationDuplicates2.xml\", policy);\n        } catch (final OrganizationImportException e) {\n\n            assertEquals(2, getIdentityAPI().getNumberOfUsers());\n            assertEquals(2, getIdentityAPI().getNumberOfGroups());\n            assertEquals(2, getIdentityAPI().getNumberOfRoles());\n\n            final User persistedUser = getIdentityAPI().getUserByUserName(userName);\n            assertNotNull(persistedUser);\n            assertEquals(WEB_TEAM_MANAGER, persistedUser.getJobTitle());\n            assertFalse(persistedUser.isEnabled());\n            final Role persistedRole = getIdentityAPI().getRoleByName(roleName);\n            assertNotNull(persistedRole);\n            assertEquals(BONITA_MANAGER, persistedRole.getDisplayName());\n            final Group persistedGroup = getIdentityAPI().getGroupByPath(groupName);\n            assertNotNull(persistedGroup);\n            assertEquals(WEB_TEAM, persistedGroup.getDisplayName());\n            final UserMembership persistedMembership = getIdentityAPI()\n                    .getUserMemberships(persistedUser.getId(), 0, 10, UserMembershipCriterion.GROUP_NAME_ASC).get(0);\n            assertEquals(groupName, persistedMembership.getGroupName());\n            assertEquals(roleName, persistedMembership.getRoleName());\n            assertEquals(userName, persistedMembership.getUsername());\n            // check assignedBy for membership\n            assertEquals(getSession().getUserId(), persistedMembership.getAssignedBy());\n\n            final User persistedUser1 = getIdentityAPI().getUserByUserName(userName1);\n            assertNotNull(persistedUser1);\n            assertTrue(persistedUser1.isEnabled());\n            final Role persistedRole1 = getIdentityAPI().getRoleByName(roleName1);\n            assertNotNull(persistedRole1);\n            assertEquals(roleDisplayName1, persistedRole1.getDisplayName());\n            final Group persistedGroup1 = getIdentityAPI().getGroupByPath(groupName1);\n            assertNotNull(persistedGroup1);\n            assertEquals(groupDisplayName1, persistedGroup1.getDisplayName());\n            final UserMembership persistedMembership1 = getIdentityAPI()\n                    .getUserMemberships(persistedUser1.getId(), 0, 10,\n                            UserMembershipCriterion.GROUP_NAME_ASC)\n                    .get(0);\n            assertEquals(groupName1, persistedMembership1.getGroupName());\n            assertEquals(roleName1, persistedMembership1.getRoleName());\n            assertEquals(userName1, persistedMembership1.getUsername());\n            // check assignedBy for membership\n            assertEquals(getSession().getUserId(), persistedMembership1.getAssignedBy());\n\n            // clean-up\n            getIdentityAPI().deleteUser(persistedUser.getId());\n            getIdentityAPI().deleteRole(persistedRole.getId());\n            getIdentityAPI().deleteGroup(persistedGroup.getId());\n\n            getIdentityAPI().deleteUser(persistedUser1.getId());\n            getIdentityAPI().deleteRole(persistedRole1.getId());\n            getIdentityAPI().deleteGroup(persistedGroup1.getId());\n            throw e;\n        }\n\n        fail(\"This statement shouldn't be reached.\");\n    }\n\n    @Test\n    public void importOrganizationFailOnDuplicatesNoDuplicates() throws Exception {\n        // create XML file\n        final ImportPolicy policy = ImportPolicy.FAIL_ON_DUPLICATES;\n\n        final String userName = ANTHONY_USERNAME;\n        final String roleName = MANAGER;\n        final String groupName = WEB_GROUP_NAME;\n\n        final String userName1 = LIUYANYAN_USERNAME;\n        final String roleName1 = DEVELOPER;\n        final String roleDisplayName1 = \"Bonita developer\";\n        final String groupName1 = ENGINE;\n        final String groupDisplayName1 = \"engine team\";\n\n        final String userName2 = JOHNNYFOOTBALL;\n        final String roleName2 = \"Tester\";\n        final String groupName2 = QA;\n\n        importAndCheckFirstSimpleOrganization();\n        importOrganizationWithPolicy(\"simpleOrganizationNoDuplicates.xml\", policy);\n\n        assertEquals(3, getIdentityAPI().getNumberOfUsers());\n        assertEquals(3, getIdentityAPI().getNumberOfGroups());\n        assertEquals(3, getIdentityAPI().getNumberOfRoles());\n\n        final User persistedUser = getIdentityAPI().getUserByUserName(userName);\n        assertEquals(WEB_TEAM_MANAGER, persistedUser.getJobTitle());\n        // check createdBy for user\n        assertEquals(getSession().getUserId(), persistedUser.getCreatedBy());\n        final Group persistedGroup = getIdentityAPI().getGroupByPath(groupName);\n        assertEquals(WEB_TEAM, persistedGroup.getDisplayName());\n        // check createdBy for group\n        assertEquals(getSession().getUserId(), persistedGroup.getCreatedBy());\n        final Role persistedRole = getIdentityAPI().getRoleByName(roleName);\n        assertEquals(BONITA_MANAGER, persistedRole.getDisplayName());\n        // check createdBy for role\n        assertEquals(getSession().getUserId(), persistedRole.getCreatedBy());\n        final UserMembership persistedMembership = getIdentityAPI()\n                .getUserMemberships(persistedUser.getId(), 0, 10, UserMembershipCriterion.GROUP_NAME_ASC)\n                .get(0);\n        assertEquals(groupName, persistedMembership.getGroupName());\n        assertEquals(roleName, persistedMembership.getRoleName());\n        assertEquals(userName, persistedMembership.getUsername());\n        // check assignedBy for membership\n        assertEquals(getSession().getUserId(), persistedMembership.getAssignedBy());\n\n        final User persistedUser1 = getIdentityAPI().getUserByUserName(userName1);\n        assertNotNull(persistedUser1);\n        final Role persistedRole1 = getIdentityAPI().getRoleByName(roleName1);\n        assertNotNull(persistedRole1);\n        assertEquals(roleDisplayName1, persistedRole1.getDisplayName());\n        final Group persistedGroup1 = getIdentityAPI().getGroupByPath(groupName1);\n        assertNotNull(persistedGroup1);\n        assertEquals(groupDisplayName1, persistedGroup1.getDisplayName());\n        final UserMembership persistedMembership1 = getIdentityAPI()\n                .getUserMemberships(persistedUser1.getId(), 0, 10, UserMembershipCriterion.GROUP_NAME_ASC)\n                .get(0);\n        assertEquals(groupName1, persistedMembership1.getGroupName());\n        assertEquals(roleName1, persistedMembership1.getRoleName());\n        assertEquals(userName1, persistedMembership1.getUsername());\n        // check assignedBy for membership\n        assertEquals(getSession().getUserId(), persistedMembership1.getAssignedBy());\n\n        final User persistedUser2 = getIdentityAPI().getUserByUserName(userName2);\n        assertNotNull(persistedUser2);\n        final Role persistedRole2 = getIdentityAPI().getRoleByName(roleName2);\n        assertNotNull(persistedRole2);\n        final Group persistedGroup2 = getIdentityAPI().getGroupByPath(groupName2);\n        assertNotNull(persistedGroup2);\n        final UserMembership persistedMembership2 = getIdentityAPI()\n                .getUserMemberships(persistedUser2.getId(), 0, 10, UserMembershipCriterion.GROUP_NAME_ASC)\n                .get(0);\n        assertEquals(groupName2, persistedMembership2.getGroupName());\n        assertEquals(roleName2, persistedMembership2.getRoleName());\n        assertEquals(userName2, persistedMembership2.getUsername());\n        // check assignedBy for membership\n        assertEquals(getSession().getUserId(), persistedMembership2.getAssignedBy());\n\n        // clean-up\n        getIdentityAPI().deleteOrganization();\n\n    }\n\n    @Test\n    public void importOrganizationMergeDuplicates() throws Exception {\n        // given\n        final ImportPolicy policy = ImportPolicy.MERGE_DUPLICATES;\n        importAndCheckFirstSimpleOrganization();\n\n        // when\n        importOrganizationWithPolicy(\"simpleOrganizationDuplicates2.xml\", policy);\n\n        // then\n        final Map<String, CustomUserInfoDefinition> infoDefinitonsAfterUpdate = checkCustomUserInfoDefinitionsAfterUpdate();\n        checkUsersAfterUpdate();\n        checkCustomUserInfoValuesAfterUpdate(infoDefinitonsAfterUpdate);\n        checkGroupsAfterUpdate();\n        checkRolesAfterUpdate();\n        checkMembershipAfterUpdate();\n\n        // clean-up\n        getIdentityAPI().deleteOrganization();\n    }\n\n    private void checkMembershipAfterUpdate() throws UserNotFoundException {\n        final User persistedUser = getIdentityAPI().getUserByUserName(ANTHONY_USERNAME);\n        final UserMembership persistedMembership = getIdentityAPI()\n                .getUserMemberships(persistedUser.getId(), 0, 10, UserMembershipCriterion.GROUP_NAME_ASC)\n                .get(0);\n        assertEquals(WEB_GROUP_NAME, persistedMembership.getGroupName());\n        assertEquals(MANAGER, persistedMembership.getRoleName());\n        assertEquals(ANTHONY_USERNAME, persistedMembership.getUsername());\n        // check assignedBy for membership\n        assertEquals(getSession().getUserId(), persistedMembership.getAssignedBy());\n\n        final User persistedUser1 = getIdentityAPI().getUserByUserName(LIUYANYAN_USERNAME);\n        final UserMembership persistedMembership1 = getIdentityAPI()\n                .getUserMemberships(persistedUser1.getId(), 0, 10, UserMembershipCriterion.GROUP_NAME_ASC)\n                .get(0);\n        assertEquals(ENGINE, persistedMembership1.getGroupName());\n        assertEquals(DEVELOPER, persistedMembership1.getRoleName());\n        assertEquals(LIUYANYAN_USERNAME, persistedMembership1.getUsername());\n        // check assignedBy for membership\n        assertEquals(getSession().getUserId(), persistedMembership1.getAssignedBy());\n\n        final User persistedUser2 = getIdentityAPI().getUserByUserName(JOHNNYFOOTBALL);\n        final UserMembership persistedMembership2 = getIdentityAPI()\n                .getUserMemberships(persistedUser2.getId(), 0, 10, UserMembershipCriterion.GROUP_NAME_ASC)\n                .get(0);\n        assertEquals(QA, persistedMembership2.getGroupName());\n        assertEquals(\"Tester\", persistedMembership2.getRoleName());\n        assertEquals(JOHNNYFOOTBALL, persistedMembership2.getUsername());\n        // check assignedBy for membership\n        assertEquals(getSession().getUserId(), persistedMembership2.getAssignedBy());\n    }\n\n    private void checkRolesAfterUpdate() throws RoleNotFoundException {\n        final Role persistedRole = getIdentityAPI().getRoleByName(MANAGER);\n        assertEquals(BONITA_MANAGER, persistedRole.getDisplayName());\n        // check createdBy for role\n        assertEquals(getSession().getUserId(), persistedRole.getCreatedBy());\n\n        final Role persistedRole1 = getIdentityAPI().getRoleByName(DEVELOPER);\n        assertNotNull(persistedRole1);\n        assertEquals(\"Bonitasoft developer\", persistedRole1.getDisplayName());\n        assertTrue(persistedRole1.getLastUpdate().after(defaultRoleLastUpdateDate));\n\n        final Role persistedRole2 = getIdentityAPI().getRoleByName(\"Tester\");\n        assertNotNull(persistedRole2);\n    }\n\n    private void checkGroupsAfterUpdate() throws GroupNotFoundException {\n        final Group persistedGroup = getIdentityAPI().getGroupByPath(WEB_GROUP_NAME);\n        assertEquals(WEB_TEAM, persistedGroup.getDisplayName());\n        assertEquals(getSession().getUserId(), persistedGroup.getCreatedBy());\n\n        final Group persistedGroup1 = getIdentityAPI().getGroupByPath(ENGINE);\n        assertNotNull(persistedGroup1);\n        assertEquals(\"RD engine team\", persistedGroup1.getDisplayName());\n        assertTrue(persistedGroup1.getLastUpdate().after(defaultGroupLastUpdateDate));\n\n        final Group persistedGroup2 = getIdentityAPI().getGroupByPath(QA);\n        assertNotNull(persistedGroup2);\n    }\n\n    private void checkUsersAfterUpdate() throws UserNotFoundException {\n        assertEquals(3, getIdentityAPI().getNumberOfGroups());\n        assertEquals(3, getIdentityAPI().getNumberOfRoles());\n\n        assertEquals(3, getIdentityAPI().getNumberOfUsers());\n        final User persistedUser = getIdentityAPI().getUserByUserName(ANTHONY_USERNAME);\n        assertEquals(WEB_TEAM_MANAGER, persistedUser.getJobTitle());\n        assertEquals(getSession().getUserId(), persistedUser.getCreatedBy());\n\n        final User persistedUser1 = getIdentityAPI().getUserByUserName(LIUYANYAN_USERNAME);\n        assertNotNull(persistedUser1);\n        assertTrue(persistedUser1.getLastUpdate().after(defaultUserLastUpdateDate));\n\n        final User persistedUser2 = getIdentityAPI().getUserByUserName(JOHNNYFOOTBALL);\n        assertNotNull(persistedUser2);\n    }\n\n    @Test\n    public void importOrganizationMergeDuplicatesWithEnabledAndDisabledUsers() throws Exception {\n        // create XML file\n        importAndCheckFirstSimpleOrganization();\n        importOrganizationWithPolicy(\"simpleOrganizationDuplicates2.xml\", ImportPolicy.MERGE_DUPLICATES);\n\n        assertEquals(3, getIdentityAPI().getNumberOfUsers());\n        assertEquals(3, getIdentityAPI().getNumberOfGroups());\n        assertEquals(3, getIdentityAPI().getNumberOfRoles());\n\n        final User persistedUser = getIdentityAPI().getUserByUserName(JOHNNYFOOTBALL);\n        assertNotNull(persistedUser);\n        assertFalse(persistedUser.isEnabled());\n\n        final User persistedUser1 = getIdentityAPI().getUserByUserName(LIUYANYAN_USERNAME);\n        assertNotNull(persistedUser1);\n        assertTrue(persistedUser1.isEnabled());\n\n        // clean-up\n        getIdentityAPI().deleteUser(persistedUser.getId());\n        getIdentityAPI().deleteUser(persistedUser1.getId());\n\n        getIdentityAPI().deleteRole(getIdentityAPI().getRoleByName(\"Tester\").getId());\n        getIdentityAPI().deleteGroup(getIdentityAPI().getGroupByPath(QA).getId());\n\n        getIdentityAPI().deleteRole(getIdentityAPI().getRoleByName(DEVELOPER).getId());\n        getIdentityAPI().deleteGroup(getIdentityAPI().getGroupByPath(ENGINE).getId());\n\n        getIdentityAPI().deleteUser(getIdentityAPI().getUserByUserName(ANTHONY_USERNAME).getId());\n        getIdentityAPI().deleteRole(getIdentityAPI().getRoleByName(MANAGER).getId());\n        getIdentityAPI().deleteGroup(getIdentityAPI().getGroupByPath(WEB_GROUP_NAME).getId());\n    }\n\n    @Test\n    public void importOrganizationIgnoreDuplicates() throws Exception {\n        // create XML file\n        final ImportPolicy policy = ImportPolicy.IGNORE_DUPLICATES;\n\n        final String userName = ANTHONY_USERNAME;\n        final String roleName = MANAGER;\n        final String groupName = WEB_GROUP_NAME;\n\n        final String userName1 = LIUYANYAN_USERNAME;\n        final String roleName1 = DEVELOPER;\n        final String roleDisplayName1 = \"Bonita developer\";\n        final String groupName1 = ENGINE;\n        final String groupDisplayName1 = \"engine team\";\n\n        final String userName2 = JOHNNYFOOTBALL;\n        final String roleName2 = \"Tester\";\n        final String groupName2 = QA;\n\n        importAndCheckFirstSimpleOrganization();\n        importOrganizationWithPolicy(\"simpleOrganizationDuplicates2.xml\", policy);\n\n        assertEquals(3, getIdentityAPI().getNumberOfUsers());\n        assertEquals(3, getIdentityAPI().getNumberOfGroups());\n        assertEquals(3, getIdentityAPI().getNumberOfRoles());\n\n        final User persistedUser = getIdentityAPI().getUserByUserName(userName);\n        assertEquals(WEB_TEAM_MANAGER, persistedUser.getJobTitle());\n        // check createdBy for user\n        assertEquals(getSession().getUserId(), persistedUser.getCreatedBy());\n        final Group persistedGroup = getIdentityAPI().getGroupByPath(groupName);\n        assertEquals(WEB_TEAM, persistedGroup.getDisplayName());\n        // check createdBy for group\n        assertEquals(getSession().getUserId(), persistedGroup.getCreatedBy());\n        final Role persistedRole = getIdentityAPI().getRoleByName(roleName);\n        assertEquals(BONITA_MANAGER, persistedRole.getDisplayName());\n        // check createdBy for role\n        assertEquals(getSession().getUserId(), persistedRole.getCreatedBy());\n        final UserMembership persistedMembership = getIdentityAPI()\n                .getUserMemberships(persistedUser.getId(), 0, 10, UserMembershipCriterion.GROUP_NAME_ASC)\n                .get(0);\n        assertEquals(groupName, persistedMembership.getGroupName());\n        assertEquals(roleName, persistedMembership.getRoleName());\n        assertEquals(userName, persistedMembership.getUsername());\n        // check assignedBy for membership\n        assertEquals(getSession().getUserId(), persistedMembership.getAssignedBy());\n\n        final User persistedUser1 = getIdentityAPI().getUserByUserName(userName1);\n        assertNotNull(persistedUser1);\n        final Role persistedRole1 = getIdentityAPI().getRoleByName(roleName1);\n        assertNotNull(persistedRole1);\n        assertEquals(roleDisplayName1, persistedRole1.getDisplayName());\n        final Group persistedGroup1 = getIdentityAPI().getGroupByPath(groupName1);\n        assertNotNull(persistedGroup1);\n        assertEquals(groupDisplayName1, persistedGroup1.getDisplayName());\n        final UserMembership persistedMembership1 = getIdentityAPI()\n                .getUserMemberships(persistedUser1.getId(), 0, 10, UserMembershipCriterion.GROUP_NAME_ASC)\n                .get(0);\n        assertEquals(groupName1, persistedMembership1.getGroupName());\n        assertEquals(roleName1, persistedMembership1.getRoleName());\n        assertEquals(userName1, persistedMembership1.getUsername());\n        // check assignedBy for membership\n        assertEquals(getSession().getUserId(), persistedMembership1.getAssignedBy());\n\n        final User persistedUser2 = getIdentityAPI().getUserByUserName(userName2);\n        assertNotNull(persistedUser2);\n        final Role persistedRole2 = getIdentityAPI().getRoleByName(roleName2);\n        assertNotNull(persistedRole2);\n        final Group persistedGroup2 = getIdentityAPI().getGroupByPath(groupName2);\n        assertNotNull(persistedGroup2);\n        final UserMembership persistedMembership2 = getIdentityAPI()\n                .getUserMemberships(persistedUser2.getId(), 0, 10, UserMembershipCriterion.GROUP_NAME_ASC)\n                .get(0);\n        assertEquals(groupName2, persistedMembership2.getGroupName());\n        assertEquals(roleName2, persistedMembership2.getRoleName());\n        assertEquals(userName2, persistedMembership2.getUsername());\n        // check assignedBy for membership\n        assertEquals(getSession().getUserId(), persistedMembership2.getAssignedBy());\n\n        // clean-up\n        getIdentityAPI().deleteUser(persistedUser.getId());\n        getIdentityAPI().deleteRole(persistedRole.getId());\n        getIdentityAPI().deleteGroup(persistedGroup.getId());\n\n        getIdentityAPI().deleteUser(persistedUser1.getId());\n        getIdentityAPI().deleteRole(persistedRole1.getId());\n        getIdentityAPI().deleteGroup(persistedGroup1.getId());\n\n        getIdentityAPI().deleteUser(persistedUser2.getId());\n        getIdentityAPI().deleteRole(persistedRole2.getId());\n        getIdentityAPI().deleteGroup(persistedGroup2.getId());\n    }\n\n    @Test\n    public void importOrganizationIgnoreDuplicatesWithEnabledAndDisabledUsers() throws Exception {\n        // create XML file\n        importAndCheckFirstSimpleOrganization();\n        importOrganizationWithPolicy(\"simpleOrganizationDuplicates2.xml\", ImportPolicy.IGNORE_DUPLICATES);\n\n        assertEquals(3, getIdentityAPI().getNumberOfUsers());\n        assertEquals(3, getIdentityAPI().getNumberOfGroups());\n        assertEquals(3, getIdentityAPI().getNumberOfRoles());\n\n        final User persistedUser = getIdentityAPI().getUserByUserName(JOHNNYFOOTBALL);\n        assertNotNull(persistedUser);\n        assertFalse(persistedUser.isEnabled());\n\n        final User persistedUser1 = getIdentityAPI().getUserByUserName(LIUYANYAN_USERNAME);\n        assertNotNull(persistedUser1);\n        assertTrue(persistedUser1.isEnabled());\n\n        // clean-up\n        getIdentityAPI().deleteUser(persistedUser.getId());\n        getIdentityAPI().deleteUser(persistedUser1.getId());\n\n        getIdentityAPI().deleteRole(getIdentityAPI().getRoleByName(\"Tester\").getId());\n        getIdentityAPI().deleteGroup(getIdentityAPI().getGroupByPath(QA).getId());\n\n        getIdentityAPI().deleteRole(getIdentityAPI().getRoleByName(DEVELOPER).getId());\n        getIdentityAPI().deleteGroup(getIdentityAPI().getGroupByPath(ENGINE).getId());\n\n        getIdentityAPI().deleteUser(getIdentityAPI().getUserByUserName(ANTHONY_USERNAME).getId());\n        getIdentityAPI().deleteRole(getIdentityAPI().getRoleByName(MANAGER).getId());\n        getIdentityAPI().deleteGroup(getIdentityAPI().getGroupByPath(WEB_GROUP_NAME).getId());\n    }\n\n    private void importOrganizationWithPolicy(final String xmlFile, final ImportPolicy policy) throws Exception {\n        try (InputStream xmlStream = OrganizationIT.class.getResourceAsStream(xmlFile)) {\n            final String organizationContent = IOUtils.toString(xmlStream, UTF_8);\n            getIdentityAPI().importOrganizationWithWarnings(organizationContent, policy);\n        }\n    }\n\n    private void importAndCheckFirstSimpleOrganization() throws Exception {\n        // when\n        importOrganization(\"simpleOrganizationDuplicates1.xml\");\n\n        // then\n        final Map<String, CustomUserInfoDefinition> userInfoDefinitions = checkDefaultCustomUserInfoDefinitions();\n        checkDefaultUsers();\n        checkDefaultCustomUserInfoValues(userInfoDefinitions);\n        checkDefaultGroups();\n        checkDefaultRoles();\n        checkDefaultMembership();\n    }\n\n    private void checkDefaultMembership() throws UserNotFoundException {\n        final User persistedUser = getIdentityAPI().getUserByUserName(ANTHONY_USERNAME);\n        final UserMembership persistedMembership = getIdentityAPI()\n                .getUserMemberships(persistedUser.getId(), 0, 10, UserMembershipCriterion.GROUP_NAME_ASC)\n                .get(0);\n        assertEquals(WEB_GROUP_NAME, persistedMembership.getGroupName());\n        assertEquals(MANAGER, persistedMembership.getRoleName());\n        assertEquals(ANTHONY_USERNAME, persistedMembership.getUsername());\n        assertEquals(getSession().getUserId(), persistedMembership.getAssignedBy());\n    }\n\n    private void checkDefaultRoles() throws RoleNotFoundException {\n        final Role persistedRole = getIdentityAPI().getRoleByName(MANAGER);\n        assertEquals(BONITA_MANAGER, persistedRole.getDisplayName());\n        assertEquals(getSession().getUserId(), persistedRole.getCreatedBy());\n\n        final Role persistedRole1 = getIdentityAPI().getRoleByName(DEVELOPER);\n        assertNotNull(persistedRole1);\n        if (defaultRoleLastUpdateDate == null) {\n            defaultRoleLastUpdateDate = persistedRole1.getLastUpdate();\n        }\n    }\n\n    private void checkDefaultGroups() throws GroupNotFoundException {\n        final Group persistedGroup = getIdentityAPI().getGroupByPath(WEB_GROUP_NAME);\n        assertEquals(WEB_TEAM, persistedGroup.getDisplayName());\n        assertEquals(getSession().getUserId(), persistedGroup.getCreatedBy());\n\n        final Group persistedGroup1 = getIdentityAPI().getGroupByPath(ENGINE);\n        assertNotNull(persistedGroup1);\n        if (defaultGroupLastUpdateDate == null) {\n            defaultGroupLastUpdateDate = persistedGroup1.getLastUpdate();\n        }\n    }\n\n    private void checkDefaultUsers() throws UserNotFoundException {\n        final User persistedUser = getIdentityAPI().getUserByUserName(ANTHONY_USERNAME);\n        assertEquals(WEB_TEAM_MANAGER, persistedUser.getJobTitle());\n        assertEquals(getSession().getUserId(), persistedUser.getCreatedBy());\n\n        final User persistedUser1 = getIdentityAPI().getUserByUserName(LIUYANYAN_USERNAME);\n        assertNotNull(persistedUser1);\n        if (defaultUserLastUpdateDate == null) {\n            defaultUserLastUpdateDate = persistedUser1.getLastUpdate();\n        }\n    }\n\n    @Test\n    public void exportOrganization() throws Exception {\n        // create records for user role, group and membership\n        // users\n        final User persistedUser1 = getIdentityAPI().createUser(LIUYANYAN_USERNAME, \"bpm\");\n\n        final UserCreator creator = new UserCreator(ANTHONY_USERNAME, \"bpm\");\n        creator.setJobTitle(WEB_TEAM_MANAGER);\n        final User persistedUser2 = getIdentityAPI().createUser(creator);\n\n        // roles\n        final RoleCreator rc1 = new RoleCreator(DEVELOPER);\n        rc1.setDisplayName(\"Bonita developer\");\n        rc1.setIcon(\"myIcon.jpg\", new byte[] { 1, 2, 3 });\n        final Role persistedRole1 = getIdentityAPI().createRole(rc1);\n        final RoleCreator rc2 = new RoleCreator(MANAGER);\n        rc2.setDisplayName(BONITA_MANAGER);\n        final Role persistedRole2 = getIdentityAPI().createRole(rc2);\n\n        // groups\n        final GroupCreator groupCreator1 = new GroupCreator(ENGINE);\n        groupCreator1.setDisplayName(\"engine team\");\n        final Group persistedGroup1 = getIdentityAPI().createGroup(groupCreator1);\n\n        final GroupCreator groupCreator2 = new GroupCreator(WEB_GROUP_NAME);\n        groupCreator2.setDisplayName(WEB_TEAM);\n        final Group persistedGroup2 = getIdentityAPI().createGroup(groupCreator2);\n\n        // membership\n        final UserMembership membership1 = getIdentityAPI().addUserMembership(persistedUser1.getId(),\n                persistedGroup1.getId(), persistedRole1.getId());\n        final UserMembership membership2 = getIdentityAPI().addUserMembership(persistedUser2.getId(),\n                persistedGroup2.getId(), persistedRole2.getId());\n\n        // custom user info definition\n        final CustomUserInfoDefinition skills = getIdentityAPI().createCustomUserInfoDefinition(\n                new CustomUserInfoDefinitionCreator(SKILLS_NAME, SKILLS_DESCRIPTION));\n        getIdentityAPI().createCustomUserInfoDefinition(new CustomUserInfoDefinitionCreator(LOCATION_NAME));\n\n        // custom user info value\n        getIdentityAPI().setCustomUserInfoValue(skills.getId(), persistedUser1.getId(), SKILLS_VALUE);\n\n        // export and check\n        final String organizationContent = getIdentityAPI().exportOrganization();\n\n        assertThat(organizationContent).contains(DEVELOPER);\n        assertThat(organizationContent).contains(\"Bonita developer\");\n        assertThat(organizationContent).contains(ENGINE);\n        //Icon name and Icon path are not exported (deprecated)\n        assertThat(organizationContent).doesNotContain(\"<iconName/>\");\n        assertThat(organizationContent).doesNotContain(\"<iconPath/>\");\n        assertThat(organizationContent).contains(\"engine team\");\n        assertThat(organizationContent)\n                .contains(getIdentityAPI().getUserMembership(membership1.getId()).getGroupName());\n        assertThat(organizationContent)\n                .contains(getIdentityAPI().getUserMembership(membership2.getId()).getGroupName());\n        assertThat(organizationContent).contains(SKILLS_NAME);\n        assertThat(organizationContent).contains(SKILLS_DESCRIPTION);\n        assertThat(organizationContent).contains(LOCATION_NAME);\n        assertThat(organizationContent).contains(SKILLS_VALUE);\n\n        // clean-up\n        getIdentityAPI().deleteOrganization();\n    }\n\n    @Test\n    public void exportOrganizationWithSpecialCharacters() throws Exception {\n        // create records for user role, group and membership\n        final User user = getIdentityAPI().createUser(\"Céline*^$\", \"ploµ¨µ%§\");\n        final User user2 = getIdentityAPI().createUser(\"ééééééééééééééééééé\", \"éééééééééééééééé\");\n\n        final RoleCreator roleCreator = new RoleCreator(\"Développeur\");\n        roleCreator.setDisplayName(\"'(-è\");\n        roleCreator.setDescription(\"è-__ç_\");\n        roleCreator.setIconName(\"(-è_\");\n        roleCreator.setIconPath(\"^*_ç\");\n        final Role role = getIdentityAPI().createRole(roleCreator);\n\n        final GroupCreator groupCreator = new GroupCreator(\"µ£¨µ\");\n        groupCreator.setDisplayName(\".?/5434%¨%¨%\");\n        groupCreator.setDescription(\"è-__ç_2\");\n        groupCreator.setIconName(\"(-è_2\");\n        groupCreator.setIconPath(\"^*_ç2\");\n        groupCreator.setParentPath(\"$*ù$^ù\");\n        final Group group = getIdentityAPI().createGroup(groupCreator);\n\n        final UserMembership membership = getIdentityAPI().addUserMembership(user.getId(), group.getId(), role.getId());\n\n        // export and check\n        final String organizationContent = getIdentityAPI().exportOrganization();\n\n        // Role\n        for (final Entry<RoleCreator.RoleField, Serializable> entry : roleCreator.getFields().entrySet()) {\n            assertThat(organizationContent).contains((String) entry.getValue());\n        }\n\n        // Group\n        for (final Entry<GroupField, Serializable> entry : groupCreator.getFields().entrySet()) {\n            assertThat(organizationContent).contains((String) entry.getValue());\n        }\n\n        // User\n        assertThat(organizationContent).contains(\"Céline*^$\");\n        assertThat(organizationContent).contains(\"ééééééééééééééééééé\");\n\n        // UserMembership\n        assertThat(organizationContent).contains(getIdentityAPI().getUserMembership(membership.getId()).getGroupName());\n\n        // Verify all tags\n        assertThat(organizationContent).contains(\"<organization:Organization\");\n        assertThat(organizationContent).contains(\"<users>\");\n        assertThat(organizationContent).contains(\"<user\");\n        assertThat(organizationContent).contains(\"</user>\");\n        assertThat(organizationContent).contains(\"</users>\");\n        assertThat(organizationContent).contains(\"<roles>\");\n        assertThat(organizationContent).contains(\"<role\");\n        assertThat(organizationContent).contains(\"</role>\");\n        assertThat(organizationContent).contains(\"</roles>\");\n        assertThat(organizationContent).contains(\"<groups>\");\n        assertThat(organizationContent).contains(\"<group\");\n        assertThat(organizationContent).contains(\"</group>\");\n        assertThat(organizationContent).contains(\" </groups>\");\n        assertThat(organizationContent).contains(\"<memberships\");\n        assertThat(organizationContent).contains(\"</memberships>\");\n        assertThat(organizationContent).contains(\"</organization:Organization>\");\n\n        // clean-up\n        deleteUsers(user, user2);\n        getIdentityAPI().deleteRole(role.getId());\n        getIdentityAPI().deleteGroup(group.getId());\n    }\n\n    @Test\n    public void importAndExportOrganizationWithSpecialCharacters() throws Exception {\n        importOrganization(\"OrganizationWithSpecialCharacters.xml\");\n\n        // export and check\n        final String organizationContent = getIdentityAPI().exportOrganization();\n\n        // Role\n        assertThat(organizationContent).contains(\"ééé\");\n\n        // Group\n        assertThat(organizationContent).contains(\"ééééééééé\");\n\n        // User\n        assertThat(organizationContent).contains(\"éé\");\n\n        // Verify all tags\n        assertThat(organizationContent).contains(\"<organization:Organization\");\n        assertThat(organizationContent).contains(\"<users>\");\n        assertThat(organizationContent).contains(\"<user\");\n        assertThat(organizationContent).contains(\"</user>\");\n        assertThat(organizationContent).contains(\"</users>\");\n        assertThat(organizationContent).contains(\"<roles>\");\n        assertThat(organizationContent).contains(\"<role\");\n        assertThat(organizationContent).contains(\"</role>\");\n        assertThat(organizationContent).contains(\"</roles>\");\n        assertThat(organizationContent).contains(\"<groups>\");\n        assertThat(organizationContent).contains(\"<group\");\n        assertThat(organizationContent).contains(\"</group>\");\n        assertThat(organizationContent).contains(\" </groups>\");\n        assertThat(organizationContent).contains(\"<memberships/>\");\n        assertThat(organizationContent).contains(\"</organization:Organization>\");\n\n        // clean-up\n        getIdentityAPI().deleteOrganization();\n    }\n\n    @Test\n    public void exportAndImportOrganization() throws Exception {\n        // create records for user role, group and membership\n        final String password = \"bpm\";\n        final User persistedUser1 = getIdentityAPI().createUser(LIUYANYAN_USERNAME, password);\n\n        final UserCreator creator = new UserCreator(ANTHONY_USERNAME, password);\n        creator.setJobTitle(WEB_TEAM_MANAGER);\n        final User persistedUser2 = getIdentityAPI().createUser(creator);\n\n        final RoleCreator rc1 = new RoleCreator(DEVELOPER);\n        rc1.setDisplayName(\"Bonita developer\");\n        final Role persistedRole1 = getIdentityAPI().createRole(rc1);\n        final RoleCreator rc2 = new RoleCreator(MANAGER);\n        rc2.setDisplayName(BONITA_MANAGER);\n        final Role persistedRole2 = getIdentityAPI().createRole(rc2);\n\n        final GroupCreator groupCreator1 = new GroupCreator(ENGINE);\n        groupCreator1.setDisplayName(\"engine team\");\n        final Group persistedGroup1 = getIdentityAPI().createGroup(groupCreator1);\n\n        final GroupCreator groupCreator2 = new GroupCreator(WEB_GROUP_NAME);\n        groupCreator2.setDisplayName(WEB_TEAM);\n        final Group persistedGroup2 = getIdentityAPI().createGroup(groupCreator2);\n\n        final UserMembership membership1 = getIdentityAPI().addUserMembership(persistedUser1.getId(),\n                persistedGroup1.getId(), persistedRole1.getId());\n        final UserMembership membership2 = getIdentityAPI().addUserMembership(persistedUser2.getId(),\n                persistedGroup2.getId(), persistedRole2.getId());\n\n        // export and check\n        final String organizationContent = getIdentityAPI().exportOrganization();\n\n        assertThat(organizationContent).contains(DEVELOPER);\n        assertThat(organizationContent).contains(\"Bonita developer\");\n        assertThat(organizationContent).contains(ENGINE);\n        assertThat(organizationContent).contains(\"engine team\");\n        assertThat(organizationContent)\n                .contains(getIdentityAPI().getUserMembership(membership1.getId()).getGroupName());\n        assertThat(organizationContent)\n                .contains(getIdentityAPI().getUserMembership(membership2.getId()).getGroupName());\n\n        // clean-up\n        getIdentityAPI().deleteUser(persistedUser1.getId());\n        getIdentityAPI().deleteUser(persistedUser2.getId());\n        getIdentityAPI().deleteRole(persistedRole1.getId());\n        getIdentityAPI().deleteRole(persistedRole2.getId());\n        getIdentityAPI().deleteGroup(persistedGroup1.getId());\n        getIdentityAPI().deleteGroup(persistedGroup2.getId());\n\n        getIdentityAPI().importOrganization(organizationContent);\n        final List<User> users = getIdentityAPI().getUsers(0, 10, UserCriterion.FIRST_NAME_ASC);\n        for (final User user : users) {\n            getIdentityAPI().deleteUser(user.getId());\n        }\n        assertEquals(2, users.size());\n        final List<Role> roles = getIdentityAPI().getRoles(0, 10, RoleCriterion.NAME_ASC);\n        for (final Role role : roles) {\n            getIdentityAPI().deleteRole(role.getId());\n        }\n        final List<Group> groups = getIdentityAPI().getGroups(0, 10, GroupCriterion.NAME_ASC);\n        for (final Group group : groups) {\n            getIdentityAPI().deleteGroup(group.getId());\n        }\n    }\n\n    @Test\n    public void exportOrganizationWithDisabledUsers() throws Exception {\n        // create records for user\n        final User persistedUser = getIdentityAPI().createUser(LIUYANYAN_USERNAME, \"bpm\");\n        final UserUpdater updater = new UserUpdater();\n        updater.setEnabled(false);\n        getIdentityAPI().updateUser(persistedUser.getId(), updater);\n\n        // export and check\n        final String organizationContent = getIdentityAPI().exportOrganization();\n        assertThat(organizationContent).contains(\"false\");\n\n        // clean-up\n        getIdentityAPI().deleteUser(persistedUser.getId());\n    }\n\n    @Test\n    public void exportOrganizationWithEnabledUsers() throws Exception {\n        // create records for user\n        final UserCreator creator = new UserCreator(ANTHONY_USERNAME, \"bpm\");\n        creator.setEnabled(true);\n        final User persistedUser = getIdentityAPI().createUser(creator);\n\n        // export and check\n        final String organizationContent = getIdentityAPI().exportOrganization();\n        assertThat(organizationContent).contains(\"true\");\n\n        // clean-up\n        getIdentityAPI().deleteUser(persistedUser.getId());\n    }\n\n    @Test\n    public void should_import_manager_of_user() throws Exception {\n        //given\n        User john = getIdentityAPI().createUser(\"john\", \"bpm\");\n        //manager is created after the user to ensure it does not depend of user import order\n        User johnManager = getIdentityAPI().createUser(\"johnManager\", \"bpm\");\n        getIdentityAPI().updateUser(john.getId(), new UserUpdater().setManagerId(johnManager.getId()));\n        String organization = getIdentityAPI().exportOrganization();\n        getIdentityAPI().deleteOrganization();\n        //when\n        getIdentityAPI().importOrganization(organization);\n        //then\n        assertThat(getIdentityAPI().getUserByUserName(\"john\").getManagerUserId())\n                .as(\"manager id of john\").isEqualTo(getIdentityAPI().getUserByUserName(\"johnManager\").getId());\n        //clean\n        getIdentityAPI().deleteOrganization();\n    }\n\n    @Test\n    public void should_import_update_manager_of_user() throws Exception {\n        //given\n        User john = getIdentityAPI().createUser(\"john\", \"bpm\");\n        //manager is created after the user to ensure it does not depend of user import order\n        User johnManager = getIdentityAPI().createUser(\"johnManager\", \"bpm\");\n        getIdentityAPI().updateUser(john.getId(), new UserUpdater().setManagerId(johnManager.getId()));\n        User newJohnManager = getIdentityAPI().createUser(\"newjohnManager\", \"bpm\");\n        String organization = getIdentityAPI().exportOrganization();\n        getIdentityAPI().updateUser(john.getId(), new UserUpdater().setManagerId(newJohnManager.getId()));\n        getIdentityAPI().deleteUser(johnManager.getId());\n        //when\n        getIdentityAPI().importOrganization(organization);\n        //then\n        assertThat(getIdentityAPI().getUserByUserName(\"john\").getManagerUserId())\n                .as(\"manager id of john\").isEqualTo(getIdentityAPI().getUserByUserName(\"johnManager\").getId());\n        //clean\n        getIdentityAPI().deleteOrganization();\n    }\n\n    @Test\n    public void should_import_user_even_if_manager_is_unkown() throws Exception {\n        //given\n        User john = getIdentityAPI().createUser(\"john\", \"bpm\");\n        //manager is created after the user to ensure it does not depend of user import order\n        User johnManager = getIdentityAPI().createUser(\"johnManager\", \"bpm\");\n        getIdentityAPI().updateUser(john.getId(), new UserUpdater().setManagerId(johnManager.getId()));\n        String organization = getIdentityAPI().exportOrganization();\n        getIdentityAPI().deleteOrganization();\n        //when\n        systemOutRule.enableLog();\n        getIdentityAPI().importOrganization(removeJohnManagerNode(organization));\n        //then\n        assertThat(getIdentityAPI().getUserByUserName(\"john\").getManagerUserId())\n                .as(\"manager id of john\").isEqualTo(0);\n        assertThat(systemOutRule.getLog())\n                .contains(\n                        \"The user john has a manager with username johnManager, but this one does not exist. Please set it manually.\");\n        //clean\n        getIdentityAPI().deleteOrganization();\n    }\n\n    private String removeJohnManagerNode(String organization) throws Exception {\n        DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();\n        DocumentBuilder docBuilder = docFactory.newDocumentBuilder();\n        Document doc = docBuilder.parse(new InputSource(new StringReader(organization)));\n        Node organizationNode = doc.getFirstChild();\n        Node users = organizationNode.getChildNodes().item(3);\n        if (users.getChildNodes().item(3).getAttributes().item(0).getNodeValue().equals(\"johnManager\")) {\n            users.removeChild(users.getChildNodes().item(3));\n        } else {\n            users.removeChild(users.getChildNodes().item(1));\n        }\n        TransformerFactory transformerFactory = TransformerFactory.newInstance();\n        Transformer transformer = transformerFactory.newTransformer();\n        DOMSource source = new DOMSource(doc);\n        StringWriter writer = new StringWriter();\n        StreamResult result = new StreamResult(writer);\n        transformer.transform(source, result);\n        return writer.toString();\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/identity/RoleIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.*;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.TestWithTechnicalUser;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.NotFoundException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.identity.impl.IconImpl;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.junit.Test;\n\npublic class RoleIT extends TestWithTechnicalUser {\n\n    @Test\n    public void createRoleUsingTheRoleBuilder() throws BonitaException {\n        final String manager = \"manager\";\n        final Role role = getIdentityAPI().createRole(manager);\n        assertNotNull(role);\n        assertEquals(manager, role.getName());\n        getIdentityAPI().deleteRole(role.getId());\n    }\n\n    @Test(expected = AlreadyExistsException.class)\n    public void cannotCreateARoleWhichAlreadyExists() throws BonitaException {\n        final String manager = \"manager\";\n        final Role role = getIdentityAPI().createRole(manager);\n        final long roleId = role.getId();\n        try {\n            getIdentityAPI().createRole(manager);\n        } finally {\n            getIdentityAPI().deleteRole(roleId);\n        }\n    }\n\n    @Test(expected = CreationException.class)\n    public void cannotCreateANullRole() throws BonitaException {\n        getIdentityAPI().createRole((RoleCreator) null);\n    }\n\n    @Test(expected = CreationException.class)\n    public void createARoleFail() throws BonitaException {\n        getIdentityAPI().createRole((String) null);\n    }\n\n    @Test\n    public void getRoleByName() throws BonitaException {\n        final String manager = \"manager\";\n        final Role createdRole = getIdentityAPI().createRole(manager);\n        final Role searchedRole = getIdentityAPI().getRoleByName(manager);\n        assertEquals(manager, searchedRole.getName());\n        assertEquals(createdRole.getId(), searchedRole.getId());\n\n        getIdentityAPI().deleteRole(searchedRole.getId());\n    }\n\n    @Test(expected = RoleNotFoundException.class)\n    public void cannotGetARoleByAnUnexistingName() throws BonitaException {\n        getIdentityAPI().getRoleByName(\"manager\");\n    }\n\n    @Test\n    public void getAnEmptyListWhenNoRolesAreDefined() {\n        final List<Role> roles = getIdentityAPI().getRoles(0, 10, RoleCriterion.NAME_ASC);\n        assertEquals(0, roles.size());\n    }\n\n    @Test\n    public void getARole() throws Exception {\n        final String manager = \"manager\";\n        final Role role = getIdentityAPI().createRole(manager);\n        final List<Role> roles = getIdentityAPI().getRoles(0, 10, RoleCriterion.NAME_ASC);\n        assertEquals(1, roles.size());\n\n        getIdentityAPI().deleteRole(role.getId());\n    }\n\n    @Test\n    public void getRole() throws BonitaException {\n        final String manager = \"manager\";\n        final Role createdRole = getIdentityAPI().createRole(manager);\n\n        final Role role = getIdentityAPI().getRole(createdRole.getId());\n        assertNotNull(role);\n        assertEquals(manager, role.getName());\n\n        getIdentityAPI().deleteRole(role.getId());\n    }\n\n    @Test\n    public void roleNameAndDisplayNameShouldAccept255Chars() throws BonitaException {\n        final String stringIndex_255_chars = \"ईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईऑऑऑऑऑ\";\n        final Role role = getIdentityAPI()\n                .createRole(new RoleCreator(stringIndex_255_chars).setDisplayName(stringIndex_255_chars));\n\n        // Should be no exception:\n\n        getIdentityAPI().deleteRole(role.getId());\n    }\n\n    @Test(expected = Exception.class)\n    public void roleNameShouldNotAccept256Chars() throws BonitaException {\n        final String stringIndex_256_chars = \"ッッッシシジジッシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシ\";\n        getIdentityAPI().createRole(stringIndex_256_chars);\n    }\n\n    @Test(expected = Exception.class)\n    public void roleDisplayNameShouldNotAccept256Chars() throws BonitaException {\n        final String stringIndex_256_chars = \"ッッッツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツヅ\";\n        getIdentityAPI().createRole(new RoleCreator(\"someName\").setDisplayName(stringIndex_256_chars));\n    }\n\n    @Test\n    public void getRolesByIDs() throws BonitaException {\n        final String manager = \"manager\";\n        final Role roleCreated1 = getIdentityAPI().createRole(manager);\n        final String teamManager = \"teamManager\";\n        final Role roleCreated2 = getIdentityAPI().createRole(teamManager);\n\n        final List<Long> roleIds = new ArrayList<>();\n        roleIds.add(roleCreated1.getId());\n        roleIds.add(roleCreated2.getId());\n\n        final Map<Long, Role> roles = getIdentityAPI().getRoles(roleIds);\n        assertNotNull(roles);\n        assertEquals(2, roles.size());\n        assertEquals(manager, roles.get(roleCreated1.getId()).getName());\n        assertEquals(teamManager, roles.get(roleCreated2.getId()).getName());\n\n        getIdentityAPI().deleteRole(roles.get(roleCreated1.getId()).getId());\n        getIdentityAPI().deleteRole(roles.get(roleCreated2.getId()).getId());\n    }\n\n    public void getRolesByIDsWithoutRoleNotFoundException() throws BonitaException {\n        final String manager = \"manager\";\n        final Role roleCreated1 = getIdentityAPI().createRole(manager);\n        final String teamManager = \"teamManager\";\n        final Role roleCreated2 = getIdentityAPI().createRole(teamManager);\n\n        final List<Long> roleIds = new ArrayList<>();\n        roleIds.add(roleCreated1.getId());\n        roleIds.add(roleCreated2.getId() + 100);\n\n        final Map<Long, Role> roles = getIdentityAPI().getRoles(roleIds);\n        assertNotNull(roles);\n        assertEquals(1, roles.size());\n        assertEquals(teamManager, roles.get(roleCreated1.getId()).getName());\n\n        getIdentityAPI().deleteRole(roleCreated1.getId());\n        getIdentityAPI().deleteRole(roleCreated2.getId());\n    }\n\n    @Test(expected = RoleNotFoundException.class)\n    public void cannotGetAnUnexistingRole() throws BonitaException {\n        getIdentityAPI().getRole(45);\n    }\n\n    @Test\n    public void getNumberOfRoles() throws BonitaException {\n        final String manager = \"manager\";\n        final String developer = \"developer\";\n        final Role role1 = getIdentityAPI().createRole(manager);\n        final Role role2 = getIdentityAPI().createRole(developer);\n        assertEquals(manager, role1.getName());\n        assertEquals(developer, role2.getName());\n\n        final long numberOfRoles = getIdentityAPI().getNumberOfRoles();\n        assertEquals(2, numberOfRoles);\n\n        getIdentityAPI().deleteRole(role1.getId());\n        getIdentityAPI().deleteRole(role2.getId());\n    }\n\n    @Test\n    public void noRolesWhenThePageIndexIsOutOfRange() {\n        final List<Role> roles = getIdentityAPI().getRoles(5, 10, RoleCriterion.NAME_ASC);\n        assertTrue(roles.isEmpty());\n    }\n\n    @Test\n    public void deleteARole() throws BonitaException {\n        final String manager = \"manager\";\n        final Role role = getIdentityAPI().createRole(manager);\n        getIdentityAPI().deleteRole(role.getId());\n    }\n\n    @Test\n    public void deleteRoles() throws BonitaException {\n        final String manager = \"manager\";\n        final String developer = \"developer\";\n        final Role role1 = getIdentityAPI().createRole(manager);\n        final Role role2 = getIdentityAPI().createRole(developer);\n        final List<Long> roleIds = new ArrayList<>();\n        roleIds.add(role2.getId());\n        roleIds.add(role1.getId());\n        getIdentityAPI().deleteRoles(roleIds);\n\n        final long numberOfRoles = getIdentityAPI().getNumberOfRoles();\n        assertEquals(0, numberOfRoles);\n    }\n\n    @Test\n    public void cannotDeleteARoleTwice() throws BonitaException {\n        final String developer = \"developer\";\n        final Role role = getIdentityAPI().createRole(developer);\n        getIdentityAPI().deleteRole(role.getId());\n        getIdentityAPI().deleteRole(role.getId());\n    }\n\n    @Test\n    public void canDeleteNoRole() throws BonitaException {\n        final List<Long> roleIds = new ArrayList<>();\n        getIdentityAPI().deleteRoles(roleIds);\n    }\n\n    @Test(expected = DeletionException.class)\n    public void cannotDeleteNullRoles() throws BonitaException {\n        getIdentityAPI().deleteRoles(null);\n    }\n\n    @Test\n    public void updateARole() throws Exception {\n        final String developer = \"developer\";\n        final Role role = getIdentityAPI().createRole(developer);\n        assertEquals(developer, role.getName());\n\n        final String manager = \"manager\";\n        final RoleUpdater updateDescriptor = new RoleUpdater();\n        updateDescriptor.setName(manager);\n        final Role updatedRole = getIdentityAPI().updateRole(role.getId(), updateDescriptor);\n        assertNotNull(updatedRole);\n        assertEquals(manager, updatedRole.getName());\n\n        getIdentityAPI().deleteRole(updatedRole.getId());\n    }\n\n    @Test(expected = UpdateException.class)\n    public void canontUpdateARoleWithANullUpdateDescriptor() throws Exception {\n        final String developer = \"developer\";\n        final Role role = getIdentityAPI().createRole(developer);\n        try {\n            getIdentityAPI().updateRole(role.getId(), null);\n        } finally {\n            getIdentityAPI().deleteRole(role.getId());\n        }\n    }\n\n    @Test(expected = RoleNotFoundException.class)\n    public void cannotUpdateARoleWithAnUnexistingRoleIdentifier() throws Exception {\n        final String manager = \"manager\";\n        final RoleUpdater updateDescriptor = new RoleUpdater();\n        updateDescriptor.setName(manager);\n        getIdentityAPI().updateRole(83, updateDescriptor);\n    }\n\n    @Test\n    public void getUsersOfARole() throws BonitaException {\n        final String developer = \"developer\";\n        final Role role = getIdentityAPI().createRole(developer);\n\n        final User user1 = getIdentityAPI().createUser(\"user1\", \"bpm\");\n        final User user2 = getIdentityAPI().createUser(\"user2\", \"bpm\");\n        final User user3 = getIdentityAPI().createUser(\"user3\", \"bpm\");\n        final List<Long> userIds = new ArrayList<>();\n        userIds.add(user1.getId());\n        userIds.add(user2.getId());\n\n        final Group group = getIdentityAPI().createGroup(\"R&D\", null);\n        getIdentityAPI().addUserMemberships(userIds, group.getId(), role.getId());\n\n        final List<User> users = getIdentityAPI().getUsersInRole(role.getId(), 0, 10, UserCriterion.USER_NAME_ASC);\n        assertNotNull(users);\n        assertEquals(2, users.size());\n        assertEquals(user1, users.get(0));\n        assertEquals(user2, users.get(1));\n\n        getIdentityAPI().deleteUserMemberships(userIds, group.getId(), role.getId());\n        getIdentityAPI().deleteRole(role.getId());\n        getIdentityAPI().deleteGroup(group.getId());\n\n        getIdentityAPI().deleteUsers(userIds);\n        getIdentityAPI().deleteUser(user3.getId());\n    }\n\n    @Test\n    public void getActiveUsersOfARole_should_return_all_active_users_of_the_role() throws BonitaException {\n        final String developer = \"developer\";\n        final Role role = getIdentityAPI().createRole(developer);\n\n        final User user1 = getIdentityAPI().createUser(new UserCreator(\"user1\", \"bpm\").setEnabled(true));\n        final User user2 = getIdentityAPI().createUser(new UserCreator(\"user2\", \"bpm\").setEnabled(true));\n        final User user3 = getIdentityAPI().createUser(new UserCreator(\"user3\", \"bpm\").setEnabled(false));\n        final User user4 = getIdentityAPI().createUser(new UserCreator(\"user4\", \"bpm\").setEnabled(true));\n        final List<Long> userIds = new ArrayList<>();\n        userIds.add(user1.getId());\n        userIds.add(user2.getId());\n        userIds.add(user3.getId());\n\n        final Group group = getIdentityAPI().createGroup(\"R&D\", null);\n        getIdentityAPI().addUserMemberships(userIds, group.getId(), role.getId());\n\n        final List<User> users = getIdentityAPI().getActiveUsersInRole(role.getId(), 0, 10,\n                UserCriterion.USER_NAME_ASC);\n        assertNotNull(users);\n        assertEquals(2, users.size());\n        assertEquals(user1, users.get(0));\n        assertEquals(user2, users.get(1));\n\n        getIdentityAPI().deleteUserMemberships(userIds, group.getId(), role.getId());\n        getIdentityAPI().deleteRole(role.getId());\n        getIdentityAPI().deleteGroup(group.getId());\n\n        getIdentityAPI().deleteUsers(userIds);\n        getIdentityAPI().deleteUser(user3.getId());\n        getIdentityAPI().deleteUser(user4.getId());\n    }\n\n    @Test\n    public void getInactiveUsersOfARole_should_return_all_inactive_users_of_the_role() throws BonitaException {\n        final String developer = \"developer\";\n        final Role role = getIdentityAPI().createRole(developer);\n\n        final User user1 = getIdentityAPI().createUser(new UserCreator(\"user1\", \"bpm\").setEnabled(true));\n        final User user2 = getIdentityAPI().createUser(new UserCreator(\"user2\", \"bpm\").setEnabled(true));\n        final User user3 = getIdentityAPI().createUser(new UserCreator(\"user3\", \"bpm\").setEnabled(false));\n        final User user4 = getIdentityAPI().createUser(new UserCreator(\"user4\", \"bpm\").setEnabled(false));\n        final List<Long> userIds = new ArrayList<>();\n        userIds.add(user1.getId());\n        userIds.add(user2.getId());\n        userIds.add(user3.getId());\n\n        final Group group = getIdentityAPI().createGroup(\"R&D\", null);\n        getIdentityAPI().addUserMemberships(userIds, group.getId(), role.getId());\n\n        final List<User> users = getIdentityAPI().getInactiveUsersInRole(role.getId(), 0, 10,\n                UserCriterion.USER_NAME_ASC);\n        assertNotNull(users);\n        assertEquals(1, users.size());\n        assertEquals(user3, users.get(0));\n\n        getIdentityAPI().deleteUserMemberships(userIds, group.getId(), role.getId());\n        getIdentityAPI().deleteRole(role.getId());\n        getIdentityAPI().deleteGroup(group.getId());\n\n        getIdentityAPI().deleteUsers(userIds);\n        getIdentityAPI().deleteUser(user3.getId());\n        getIdentityAPI().deleteUser(user4.getId());\n    }\n\n    @Test\n    public void getNumberOfUsersInRole() throws BonitaException {\n        final String developer = \"developer\";\n        final Role role = getIdentityAPI().createRole(developer);\n\n        final User user1 = getIdentityAPI().createUser(\"user1\", \"bpm\");\n        final User user2 = getIdentityAPI().createUser(\"user2\", \"bpm\");\n        final List<Long> userIds = new ArrayList<>();\n        userIds.add(user1.getId());\n        userIds.add(user2.getId());\n\n        final Group group = getIdentityAPI().createGroup(\"R&D\", null);\n        getIdentityAPI().addUserMemberships(userIds, group.getId(), role.getId());\n\n        final long count = getIdentityAPI().getNumberOfUsersInRole(role.getId());\n        assertEquals(2, count);\n\n        getIdentityAPI().deleteUserMemberships(userIds, group.getId(), role.getId());\n        getIdentityAPI().deleteRole(role.getId());\n        getIdentityAPI().deleteGroup(group.getId());\n        getIdentityAPI().deleteUser(user1.getId());\n        getIdentityAPI().deleteUser(user2.getId());\n    }\n\n    @Test\n    public void getPaginatedRolesWithRoleCriterion() throws BonitaException {\n        final String developer = \"developer\";\n        final String manager = \"manager\";\n        final String cto = \"chief technology officer\";\n\n        final RoleCreator roleCreator1 = new RoleCreator(developer);\n        roleCreator1.setDisplayName(developer);\n        final Role aRole = getIdentityAPI().createRole(roleCreator1);\n        final RoleCreator roleCreator2 = new RoleCreator(manager);\n        roleCreator2.setDisplayName(manager);\n        final Role bRole = getIdentityAPI().createRole(roleCreator2);\n        final RoleCreator roleCreator3 = new RoleCreator(cto);\n        roleCreator3.setDisplayName(cto);\n        final Role cRole = getIdentityAPI().createRole(roleCreator3);\n\n        final List<Role> rolesNameAsc = getIdentityAPI().getRoles(0, 3, RoleCriterion.NAME_ASC);\n        assertEquals(3, rolesNameAsc.size());\n        assertEquals(cto, rolesNameAsc.get(0).getName());\n        assertEquals(developer, rolesNameAsc.get(1).getName());\n        assertEquals(manager, rolesNameAsc.get(2).getName());\n\n        final List<Role> rolesNameDesc = getIdentityAPI().getRoles(0, 3, RoleCriterion.NAME_DESC);\n        assertEquals(3, rolesNameDesc.size());\n        assertEquals(manager, rolesNameDesc.get(0).getName());\n        assertEquals(developer, rolesNameDesc.get(1).getName());\n        assertEquals(cto, rolesNameDesc.get(2).getName());\n\n        final List<Role> rolesLabelAsc = getIdentityAPI().getRoles(0, 3, RoleCriterion.DISPLAY_NAME_ASC);\n        assertEquals(3, rolesLabelAsc.size());\n        assertEquals(cto, rolesLabelAsc.get(0).getDisplayName());\n        assertEquals(developer, rolesLabelAsc.get(1).getDisplayName());\n        assertEquals(manager, rolesLabelAsc.get(2).getDisplayName());\n\n        final List<Role> rolesLabelDesc = getIdentityAPI().getRoles(0, 3, RoleCriterion.DISPLAY_NAME_DESC);\n        assertEquals(3, rolesLabelDesc.size());\n        assertEquals(manager, rolesLabelDesc.get(0).getDisplayName());\n        assertEquals(developer, rolesLabelDesc.get(1).getDisplayName());\n        assertEquals(cto, rolesLabelDesc.get(2).getDisplayName());\n\n        getIdentityAPI().deleteRole(aRole.getId());\n        getIdentityAPI().deleteRole(bRole.getId());\n        getIdentityAPI().deleteRole(cRole.getId());\n    }\n\n    @Test\n    public void getRolesOnMultiPages() throws BonitaException {\n        final String roleName1 = \"role1\";\n        final String roleName2 = \"role2\";\n        final String roleName3 = \"role3\";\n        final String roleName4 = \"role4\";\n        final String roleName5 = \"role5\";\n        final Role role1 = getIdentityAPI().createRole(roleName1);\n        final Role role2 = getIdentityAPI().createRole(roleName2);\n        final Role role3 = getIdentityAPI().createRole(roleName3);\n        final Role role4 = getIdentityAPI().createRole(roleName4);\n        final Role role5 = getIdentityAPI().createRole(roleName5);\n\n        List<Role> rolesNameAsc = getIdentityAPI().getRoles(0, 2, RoleCriterion.NAME_ASC);\n        assertEquals(2, rolesNameAsc.size());\n        assertEquals(roleName1, rolesNameAsc.get(0).getName());\n        assertEquals(roleName2, rolesNameAsc.get(1).getName());\n\n        rolesNameAsc = getIdentityAPI().getRoles(2, 2, RoleCriterion.NAME_ASC);\n        assertEquals(2, rolesNameAsc.size());\n        assertEquals(roleName3, rolesNameAsc.get(0).getName());\n        assertEquals(roleName4, rolesNameAsc.get(1).getName());\n\n        rolesNameAsc = getIdentityAPI().getRoles(4, 2, RoleCriterion.NAME_ASC);\n        assertEquals(1, rolesNameAsc.size());\n        assertEquals(roleName5, rolesNameAsc.get(0).getName());\n        final List<Role> roles = getIdentityAPI().getRoles(5, 2, RoleCriterion.NAME_ASC);\n        assertTrue(roles.isEmpty());\n\n        getIdentityAPI().deleteRole(role1.getId());\n        getIdentityAPI().deleteRole(role2.getId());\n        getIdentityAPI().deleteRole(role3.getId());\n        getIdentityAPI().deleteRole(role4.getId());\n        getIdentityAPI().deleteRole(role5.getId());\n    }\n\n    @Test(expected = AlreadyExistsException.class)\n    public void cannotCreateTwoRoleWithTheSameName() throws BonitaException {\n        final String role = \"role\";\n        final Role role1 = getIdentityAPI().createRole(role);\n        try {\n            getIdentityAPI().createRole(role);\n        } finally {\n            getIdentityAPI().deleteRole(role1.getId());\n        }\n    }\n\n    @Test\n    public void searchRoleUsingFilter() throws BonitaException {\n        final RoleCreator roleCreator1 = new RoleCreator(\"manager\");\n        roleCreator1.setDisplayName(\"Man\");\n        final Role mananger = getIdentityAPI().createRole(roleCreator1);\n        final RoleCreator roleCreator2 = new RoleCreator(\"developer\");\n        roleCreator2.setDisplayName(\"Dev\");\n        final Role dev = getIdentityAPI().createRole(roleCreator2);\n\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.filter(RoleSearchDescriptor.DISPLAY_NAME, \"Dev\");\n        final SearchResult<Role> searchRoles = getIdentityAPI().searchRoles(builder.done());\n        assertNotNull(searchRoles);\n        assertEquals(1, searchRoles.getCount());\n        final List<Role> roles = searchRoles.getResult();\n        assertEquals(dev, roles.get(0));\n\n        getIdentityAPI().deleteRole(mananger.getId());\n        getIdentityAPI().deleteRole(dev.getId());\n    }\n\n    @Test\n    public void searchRoleWithApostrophe() throws BonitaException {\n        final RoleCreator roleCreator1 = new RoleCreator(\"mana'ger\");\n        roleCreator1.setDisplayName(\"A\");\n        final Role mananger = getIdentityAPI().createRole(roleCreator1);\n        final RoleCreator roleCreator2 = new RoleCreator(\"developer\");\n        roleCreator2.setDisplayName(\"mana'B\");\n        final Role dev = getIdentityAPI().createRole(roleCreator2);\n\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.sort(RoleSearchDescriptor.DISPLAY_NAME, Order.ASC);\n        builder.searchTerm(\"mana'\");\n        final SearchResult<Role> searchRoles = getIdentityAPI().searchRoles(builder.done());\n        assertNotNull(searchRoles);\n        assertEquals(2, searchRoles.getCount());\n        final List<Role> roles = searchRoles.getResult();\n        assertEquals(mananger, roles.get(0));\n        assertEquals(dev, roles.get(1));\n\n        getIdentityAPI().deleteRole(mananger.getId());\n        getIdentityAPI().deleteRole(dev.getId());\n    }\n\n    @Test\n    public void getRolesFromIds() throws BonitaException {\n        final RoleCreator roleCreator1 = new RoleCreator(\"managerA\");\n        roleCreator1.setDisplayName(\"managerA\");\n        final Role RoleA = getIdentityAPI().createRole(roleCreator1);\n        final RoleCreator roleCreator2 = new RoleCreator(\"managerB\");\n        roleCreator2.setDisplayName(\"managerB\");\n        final Role RoleB = getIdentityAPI().createRole(roleCreator2);\n        final RoleCreator roleCreator3 = new RoleCreator(\"managerC\");\n        roleCreator3.setDisplayName(\"managerC\");\n        final Role RoleC = getIdentityAPI().createRole(roleCreator3);\n        final RoleCreator roleCreator4 = new RoleCreator(\"managerD\");\n        roleCreator4.setDisplayName(\"managerD\");\n        final Role RoleD = getIdentityAPI().createRole(roleCreator4);\n\n        final long roleAId = RoleA.getId();\n        final long roleBId = RoleB.getId();\n        final long roleCId = RoleC.getId();\n        final long roleDId = RoleD.getId();\n\n        final List<Long> roleIds = new ArrayList<>();\n        roleIds.add(roleAId);\n        roleIds.add(roleBId);\n        roleIds.add(roleCId);\n        roleIds.add(roleDId);\n\n        final Map<Long, Role> roles = getIdentityAPI().getRoles(roleIds);\n        assertNotNull(roles);\n        assertEquals(4, roles.size());\n        assertEquals(RoleA, roles.get(roleAId));\n        assertEquals(RoleB, roles.get(roleBId));\n        assertEquals(RoleC, roles.get(roleCId));\n        assertEquals(RoleD, roles.get(roleDId));\n\n        getIdentityAPI().deleteRoles(roleIds);\n    }\n\n    @Test\n    public void checkCreatedByForRole() throws BonitaException {\n        final String manager = \"manager\";\n        final Role createdRole = getIdentityAPI().createRole(manager);\n        // check createdBy for role\n        assertNotNull(createdRole.getCreatedBy());\n        assertEquals(getSession().getUserId(), createdRole.getCreatedBy());\n        getIdentityAPI().deleteRole(createdRole.getId());\n    }\n\n    @Test\n    public void should_createRole_with_icon_creates_the_icon() throws Exception {\n        //given\n        Role aRole = getIdentityAPI().createRole(new RoleCreator(\"aRole\").setIcon(\"main.png\", new byte[] { 1, 2, 3 }));\n        //when\n        Icon icon = getIdentityAPI().getIcon(aRole.getIconId());\n        //then\n        assertThat(icon).isEqualTo(new IconImpl(icon.getId(), \"image/png\", new byte[] { 1, 2, 3 }));\n        //clean up\n        deleteRoles(aRole);\n    }\n\n    @Test\n    public void should_updateRole_with_new_icon_create_a_new_icon() throws Exception {\n        //given\n        Role aRole = getIdentityAPI().createRole(new RoleCreator(\"aRole\").setIcon(\"main.png\", new byte[] { 1, 2, 3 }));\n        //when\n        Role role = getIdentityAPI().updateRole(aRole.getId(),\n                new RoleUpdater().setIcon(\"newIcon.jpg\", new byte[] { 3, 4, 5 }));\n        //then\n        Icon icon = getIdentityAPI().getIcon(role.getIconId());\n        assertThat(icon.getId()).isNotEqualTo(aRole.getIconId());\n        assertThat(icon.getMimeType()).isEqualTo(\"image/jpeg\");\n        assertThat(icon.getContent()).isEqualTo(new byte[] { 3, 4, 5 });\n        //clean up\n        deleteRoles(aRole);\n    }\n\n    @Test(expected = NotFoundException.class)\n    public void should_deleteRole_with_icon_delete_the_icon() throws Exception {\n        //given\n        Role aRole = getIdentityAPI().createRole(new RoleCreator(\"aRole\").setIcon(\"main.png\", new byte[] { 1, 2, 3 }));\n        //when\n        getIdentityAPI().deleteRole(aRole.getId());\n        //then\n        getIdentityAPI().getIcon(aRole.getIconId());\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/identity/UserIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport static java.util.Arrays.asList;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.*;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport org.bonitasoft.engine.TestWithTechnicalUser;\nimport org.bonitasoft.engine.api.PlatformAPI;\nimport org.bonitasoft.engine.api.PlatformAPIAccessor;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.NotFoundException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.identity.impl.IconImpl;\nimport org.bonitasoft.engine.platform.LoginException;\nimport org.bonitasoft.engine.platform.NodeNotStartedException;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.PlatformSession;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\n\npublic class UserIT extends TestWithTechnicalUser {\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    /**\n     * This test is here for arbitrary reason: it has to be tested on ANY API call.\n     */\n    @Test(expected = NodeNotStartedException.class)\n    public void unableToCallPlatformMethodOnStoppedNode() throws Exception {\n        logout();\n        PlatformSession session = loginOnPlatform();\n        PlatformAPI platformAPI = PlatformAPIAccessor.getPlatformAPI(session);\n        platformAPI.stopNode();\n        logoutOnPlatform(session);\n        try {\n            loginWithTechnicalUser();\n        } finally {\n            session = loginOnPlatform();\n            platformAPI = PlatformAPIAccessor.getPlatformAPI(session);\n            platformAPI.startNode();\n            logoutOnPlatform(session);\n            loginWithTechnicalUser();\n        }\n    }\n\n    @Test(expected = CreationException.class)\n    public void cannotCreateAUserWithAnEmptyPassword() throws BonitaException {\n        final String userName = \"matti\";\n        final String password = \"\";\n        createUser(userName, password);\n    }\n\n    @Test(expected = CreationException.class)\n    public void cannotCreateAUserWithAnEmptyUserName() throws BonitaException {\n        final String userName = \"\";\n        final String password = \"revontuli\";\n        createUser(userName, password);\n    }\n\n    @Test(expected = CreationException.class)\n    public void cannotCreateAUserWithANullPassword() throws BonitaException {\n        final String userName = \"matti\";\n        createUser(userName, null);\n    }\n\n    @Test(expected = CreationException.class)\n    public void cannotCreateAUserWithANullUserName() throws BonitaException {\n        final String password = \"revontuli\";\n        createUser(null, password);\n    }\n\n    @Test\n    public void createUserByUsernameAndPassword() throws BonitaException {\n        getIdentityAPI().getNumberOfUsers();\n        final User userCreated = getIdentityAPI().createUser(\"bonitasoft\", \"123456\");\n        assertNotNull(userCreated);\n        assertEquals(\"bonitasoft\", userCreated.getUserName());\n        final User user = getIdentityAPI().getUserByUserName(\"bonitasoft\");\n        assertNotNull(user);\n        getIdentityAPI().deleteUser(\"bonitasoft\");\n    }\n\n    @Test\n    public void createEnabledUserByUsernameAndPassword() throws BonitaException {\n        final User userCreated = getIdentityAPI().createUser(\"bonitasoft\", \"123456\");\n        assertNotNull(userCreated);\n\n        final User user = getIdentityAPI().getUserByUserName(\"bonitasoft\");\n        assertNotNull(user);\n        assertTrue(user.isEnabled());\n\n        getIdentityAPI().deleteUser(\"bonitasoft\");\n    }\n\n    @Test(expected = AlreadyExistsException.class)\n    public void createUserByUsernameAndPasswordException() throws BonitaException {\n        final User userCreated = getIdentityAPI().createUser(\"bonitasoft\", \"123456\");\n        assertNotNull(userCreated);\n        assertEquals(\"bonitasoft\", userCreated.getUserName());\n        final User user = getIdentityAPI().getUserByUserName(\"bonitasoft\");\n        assertNotNull(user);\n        assertEquals(\"bonitasoft\", userCreated.getUserName());\n        try {\n            getIdentityAPI().createUser(\"bonitasoft\", \"123456\");\n        } finally {\n            getIdentityAPI().deleteUser(\"bonitasoft\");\n        }\n    }\n\n    public void getFirstPageWithNoResult() {\n        getIdentityAPI().getUsers(0, 10, UserCriterion.USER_NAME_ASC);\n    }\n\n    @Test(expected = CreationException.class)\n    public void createUserFailed() throws BonitaException {\n        getIdentityAPI().createUser(null, null);\n    }\n\n    @Test(expected = CreationException.class)\n    public void createUserUsingNullUser() throws BonitaException {\n        getIdentityAPI().createUser(null);\n    }\n\n    @Test\n    public void createUserByAUser() throws BonitaException {\n        final String userName = \"spring\";\n        final UserCreator creator = new UserCreator(userName, \"bpm\");\n        creator.setTitle(\"wwwwwwwwwwwwwwwwwwwwwwwwwwww\");\n        final User user = getIdentityAPI().createUser(creator);\n        assertNotNull(user);\n        assertEquals(userName, user.getUserName());\n\n        getIdentityAPI().deleteUser(userName);\n    }\n\n    @Test\n    public void createEnabledUserByAUser() throws BonitaException {\n        final User user = getIdentityAPI().createUser(\"bonitasoft\", \"bpm\");\n        assertNotNull(user);\n        assertTrue(user.isEnabled());\n\n        getIdentityAPI().deleteUser(\"bonitasoft\");\n    }\n\n    @Test\n    public void createDisabledUserByAUser() throws BonitaException {\n        final UserCreator creator = new UserCreator(\"bonitasoft\", \"bpm\");\n        creator.setEnabled(false);\n        final User user = getIdentityAPI().createUser(creator);\n        assertNotNull(user);\n        assertFalse(user.isEnabled());\n\n        getIdentityAPI().deleteUser(\"bonitasoft\");\n    }\n\n    @Test(expected = CreationException.class)\n    public void cannotCreateAUserWithANullUserNameUsingBuilder() throws BonitaException {\n        getIdentityAPI().createUser(null, \"revontuli\");\n    }\n\n    @Test(expected = CreationException.class)\n    public void cannotCreateAUserWithANullPasswordUsingBuilder() throws BonitaException {\n        getIdentityAPI().createUser(\"matti\", null);\n    }\n\n    @Test(expected = CreationException.class)\n    public void cannotCreateAUserWithAnEmptyUserNameUsingBuilder() throws BonitaException {\n        getIdentityAPI().createUser(\"\", \"revontuli\");\n    }\n\n    @Test(expected = CreationException.class)\n    public void cannotCreateAUserWithAnEmptyPasswordUsingBuilder() throws BonitaException {\n        getIdentityAPI().createUser(\"matti\", \"\");\n    }\n\n    @Test(expected = UserNotFoundException.class)\n    public void getUserByUsernameWithException() throws BonitaException {\n        getIdentityAPI().getUserByUserName(\"Bonita\");\n    }\n\n    @Test\n    public void getUserByUsername() throws BonitaException {\n        getIdentityAPI().createUser(\"bonita\", \"password\");\n        final User user = getIdentityAPI().getUserByUserName(\"bonita\");\n        assertEquals(\"bonita\", user.getUserName());\n\n        getIdentityAPI().deleteUser(\"bonita\");\n    }\n\n    @Test\n    public void getNumberOfUsers() throws BonitaException {\n        getIdentityAPI().createUser(\"jane\", \"bpm\");\n        getIdentityAPI().createUser(\"paul\", \"bpm\");\n\n        final long usersCount = getIdentityAPI().getNumberOfUsers();\n        assertEquals(2, usersCount);\n\n        getIdentityAPI().deleteUser(\"jane\");\n        getIdentityAPI().deleteUser(\"paul\");\n    }\n\n    @Test\n    public void getUserById() throws BonitaException {\n        final User userCreated = getIdentityAPI().createUser(\"zhang\", \"engine\");\n        final User user = getIdentityAPI().getUser(userCreated.getId());\n        assertNotNull(user);\n        assertEquals(\"zhang\", user.getUserName());\n\n        getIdentityAPI().deleteUser(\"zhang\");\n    }\n\n    @Test(expected = UserNotFoundException.class)\n    public void cannotGetTechUser() throws BonitaException {\n        getIdentityAPI().getUser(-1);\n    }\n\n    @Test\n    public void cannotGetTechUserInList() {\n        final Map<Long, User> users = getIdentityAPI().getUsers(asList(-1L));\n        assertNull(users.get(-1));\n    }\n\n    @Test(expected = UserNotFoundException.class)\n    public void getUserByIDWithUserNotFoundException() throws BonitaException {\n        final User userCreated = getIdentityAPI().createUser(\"zhang\", \"engine\");\n        try {\n            getIdentityAPI().getUser(userCreated.getId() + 100);\n        } finally {\n            getIdentityAPI().deleteUser(\"zhang\");\n        }\n    }\n\n    @Test\n    public void getUsersByIDs() throws BonitaException {\n        final User userCreated1 = getIdentityAPI().createUser(\"zhang\", \"engine\");\n        final User userCreated2 = getIdentityAPI().createUser(\"jmege\", \"engine\");\n\n        final List<Long> userIds = new ArrayList<>();\n        userIds.add(userCreated1.getId());\n        userIds.add(userCreated2.getId());\n\n        final Map<Long, User> users = getIdentityAPI().getUsers(userIds);\n        assertNotNull(users);\n        assertEquals(2, users.size());\n\n        assertEquals(\"zhang\", users.get(userCreated1.getId()).getUserName());\n\n        assertEquals(\"jmege\", users.get(userCreated2.getId()).getUserName());\n\n        deleteUsers(userCreated1, userCreated2);\n    }\n\n    public void getUsersByIDsWithoutUserNotFoundException() throws BonitaException {\n        final User userCreated1 = getIdentityAPI().createUser(\"zhang\", \"engine\");\n        final User userCreated2 = getIdentityAPI().createUser(\"jmege\", \"engine\");\n\n        final List<Long> userIds = new ArrayList<>();\n        userIds.add(userCreated1.getId());\n        userIds.add(userCreated2.getId() + 100);\n\n        final Map<Long, User> users = getIdentityAPI().getUsers(userIds);\n        assertNotNull(users);\n        assertEquals(1, users.size());\n\n        assertEquals(\"zhang\", users.get(userCreated1.getId()).getUserName());\n\n        deleteUsers(userCreated1, userCreated2);\n    }\n\n    @Test\n    public void deleteUserByUserName() throws BonitaException {\n        getIdentityAPI().createUser(\"testDelete\", \"engine\");\n        getIdentityAPI().deleteUser(\"testDelete\");\n        assertEquals(0, getIdentityAPI().getNumberOfUsers());\n    }\n\n    @Test\n    public void deleteNonExistingUserByUserName() throws BonitaException {\n        final String userName = \"testDelete\";\n        getIdentityAPI().createUser(userName, \"engine\");\n        getIdentityAPI().deleteUser(userName);\n        getIdentityAPI().deleteUser(userName);\n    }\n\n    @Test(expected = DeletionException.class)\n    public void deleteUserByUserNameWithUserDeletionException() throws BonitaException {\n        getIdentityAPI().deleteUser(null);\n    }\n\n    @Test\n    public void deleteUser() throws BonitaException {\n        final User user = getIdentityAPI().createUser(\"testDelete\", \"engine\");\n        getIdentityAPI().deleteUser(user.getId());\n        assertEquals(0, getIdentityAPI().getNumberOfUsers());\n    }\n\n    @Test\n    public void testUserContactInfos() throws BonitaException {\n        final UserCreator creator = new UserCreator(\"john\", \"bpm\");\n        creator.setFirstName(\"John\").setLastName(\"Doe\");\n        final ContactDataCreator persoCreator = new ContactDataCreator();\n        // BS-7711\n        persoCreator.setEmail(\"anemailwithmorethanfifteencharacter@extremellylongdomainname.com\");\n        final ContactDataCreator proCreator = new ContactDataCreator();\n        proCreator.setEmail(\"john.doe@bonitasoft.com\");\n        creator.setPersonalContactData(persoCreator);\n        creator.setProfessionalContactData(proCreator);\n        final User john = getIdentityAPI().createUser(creator);\n\n        final ContactData persoData = getIdentityAPI().getUserContactData(john.getId(), true);\n        assertEquals(\"anemailwithmorethanfifteencharacter@extremellylongdomainname.com\", persoData.getEmail());\n        final ContactData prooData = getIdentityAPI().getUserContactData(john.getId(), false);\n        assertEquals(\"john.doe@bonitasoft.com\", prooData.getEmail());\n\n        getIdentityAPI().deleteUser(john.getId());\n        assertEquals(0, getIdentityAPI().getNumberOfUsers());\n        assertNull(getIdentityAPI().getUserContactData(john.getId(), true));\n    }\n\n    @Test\n    public void deleteNonExistingUser() throws BonitaException {\n        final User user = getIdentityAPI().createUser(\"testDelete\", \"engine\");\n        getIdentityAPI().deleteUser(user.getId());\n        getIdentityAPI().deleteUser(user.getId());\n    }\n\n    @Test\n    public void deleteUsers() throws BonitaException {\n        final List<Long> userIds = new ArrayList<>();\n        userIds.add(getIdentityAPI().createUser(\"user1\", \"engine\").getId());\n        userIds.add(getIdentityAPI().createUser(\"user2\", \"engine\").getId());\n        userIds.add(getIdentityAPI().createUser(\"user3\", \"engine\").getId());\n        userIds.add(getIdentityAPI().createUser(\"user4\", \"engine\").getId());\n        userIds.add(getIdentityAPI().createUser(\"user5\", \"engine\").getId());\n\n        getIdentityAPI().deleteUsers(userIds);\n        assertEquals(0, getIdentityAPI().getNumberOfUsers());\n    }\n\n    @Test\n    public void deleteNonExistingUsers() throws BonitaException {\n        getIdentityAPI().deleteUsers(null);\n    }\n\n    @Test\n    public void deleteUsersDeleteAllExistingOnesAndIgnoresOthers() throws BonitaException {\n        final List<Long> userIds = new ArrayList<>();\n        userIds.add(getIdentityAPI().createUser(\"user1\", \"engine\").getId());\n        userIds.add(getIdentityAPI().createUser(\"user2\", \"engine\").getId());\n        userIds.add(152458L);\n\n        getIdentityAPI().deleteUsers(userIds);\n        assertEquals(0, getIdentityAPI().getNumberOfUsers());\n    }\n\n    @Test\n    public void updateUser() throws BonitaException {\n        final String james = \"james\";\n        final User user = getIdentityAPI().createUser(james, \"mbp\");\n\n        final UserUpdater updateDescriptor = buildUserUpdaterWithProAndPersoContact();\n        final User updatedUser = getIdentityAPI().updateUser(user.getId(), updateDescriptor);\n\n        checkUser(updatedUser);\n\n        final ContactData persoData = getIdentityAPI().getUserContactData(updatedUser.getId(), true);\n        checkPersoUserContactData(persoData);\n\n        final ContactData proData = getIdentityAPI().getUserContactData(updatedUser.getId(), false);\n        checkProUserContactData(proData);\n\n        getIdentityAPI().deleteUser(updatedUser.getId());\n    }\n\n    private UserUpdater buildUserUpdater() {\n        final UserUpdater updateDescriptor = new UserUpdater();\n        updateDescriptor.setFirstName(\"Changed first name\");\n        updateDescriptor.setIconName(\"new icon name\");\n        updateDescriptor.setIconPath(\"new_icon_path\");\n        updateDescriptor.setJobTitle(\"New job title\");\n        updateDescriptor.setLastName(\"Modified Last name\");\n        updateDescriptor.setManagerId(12354L);\n        updateDescriptor.setPassword(\"Ch4n63D_P455W0RD\");\n        updateDescriptor.setUserName(\"new_user_name\");\n        updateDescriptor.setTitle(\"titre\");\n        return updateDescriptor;\n    }\n\n    private void checkUser(final User updatedUser) {\n        assertNotNull(updatedUser);\n        assertEquals(\"new_user_name\", updatedUser.getUserName());\n        assertEquals(\"Changed first name\", updatedUser.getFirstName());\n        assertEquals(\"New job title\", updatedUser.getJobTitle());\n        assertEquals(\"Modified Last name\", updatedUser.getLastName());\n        assertEquals(12354L, updatedUser.getManagerUserId());\n        assertEquals(\"titre\", updatedUser.getTitle());\n    }\n\n    private UserUpdater buildUserUpdaterWithProContact() {\n        final ContactDataUpdater proDataUpDescr = buildProContactDataUpdater();\n        final UserUpdater updateDescriptor = buildUserUpdater();\n        updateDescriptor.setProfessionalContactData(proDataUpDescr);\n        return updateDescriptor;\n    }\n\n    private UserUpdater buildUserUpdaterWithProAndPersoContact() {\n        final ContactDataUpdater proDataUpDescr = buildProContactDataUpdater();\n        final ContactDataUpdater persoDataUpDescr = buildPersoContactDataUpdater();\n        final UserUpdater updateDescriptor = buildUserUpdater();\n        updateDescriptor.setProfessionalContactData(proDataUpDescr);\n        updateDescriptor.setPersonalContactData(persoDataUpDescr);\n        return updateDescriptor;\n    }\n\n    private ContactDataUpdater buildProContactDataUpdater() {\n        final ContactDataUpdater proDataUpDescr = new ContactDataUpdater();\n        proDataUpDescr.setAddress(\"34 Gustave Eiffel\");\n        proDataUpDescr.setBuilding(\"BigBlock\");\n        proDataUpDescr.setCity(\"L.A.\");\n        proDataUpDescr.setCountry(\"Spain\");\n        proDataUpDescr.setEmail(\"noreply@bonitasoft.com\");\n        proDataUpDescr.setFaxNumber(\"01-356743254\");\n        proDataUpDescr.setMobileNumber(\"06-02-1111111\");\n        proDataUpDescr.setPhoneNumber(\"04-76-111111\");\n        proDataUpDescr.setRoom(\"A304\");\n        proDataUpDescr.setState(\"Isere\");\n        proDataUpDescr.setWebsite(\"http://www.ofelia.com\");\n        proDataUpDescr.setZipCode(\"38000\");\n        return proDataUpDescr;\n    }\n\n    private void checkProUserContactData(final ContactData proData) {\n        assertEquals(\"34 Gustave Eiffel\", proData.getAddress());\n        assertEquals(\"BigBlock\", proData.getBuilding());\n        assertEquals(\"L.A.\", proData.getCity());\n        assertEquals(\"Spain\", proData.getCountry());\n        assertEquals(\"noreply@bonitasoft.com\", proData.getEmail());\n        assertEquals(\"01-356743254\", proData.getFaxNumber());\n        assertEquals(\"06-02-1111111\", proData.getMobileNumber());\n        assertEquals(\"04-76-111111\", proData.getPhoneNumber());\n        assertEquals(\"A304\", proData.getRoom());\n        assertEquals(\"Isere\", proData.getState());\n        assertEquals(\"http://www.ofelia.com\", proData.getWebsite());\n        assertEquals(\"38000\", proData.getZipCode());\n    }\n\n    private ContactDataUpdater buildPersoContactDataUpdater() {\n        final ContactDataUpdater persoDataUpDescr = new ContactDataUpdater();\n        persoDataUpDescr.setAddress(\"3 rue des lilas\");\n        persoDataUpDescr.setBuilding(\"SkyScrapper\");\n        persoDataUpDescr.setCity(\"Lyon\");\n        persoDataUpDescr.setCountry(\"Lichtenstein\");\n        persoDataUpDescr.setEmail(\"noreply@yahoo.es\");\n        persoDataUpDescr.setFaxNumber(\"01-020013021452\");\n        persoDataUpDescr.setMobileNumber(\"06-02-000000\");\n        persoDataUpDescr.setPhoneNumber(\"04-76-000000\");\n        persoDataUpDescr.setRoom(\"Home\");\n        persoDataUpDescr.setState(\"Rhône\");\n        persoDataUpDescr.setWebsite(\"http://perso.bonitasoft.com\");\n        persoDataUpDescr.setZipCode(\"69000\");\n        return persoDataUpDescr;\n    }\n\n    private void checkPersoUserContactData(final ContactData persoData) {\n        assertEquals(\"3 rue des lilas\", persoData.getAddress());\n        assertEquals(\"SkyScrapper\", persoData.getBuilding());\n        assertEquals(\"Lyon\", persoData.getCity());\n        assertEquals(\"Lichtenstein\", persoData.getCountry());\n        assertEquals(\"noreply@yahoo.es\", persoData.getEmail());\n        assertEquals(\"01-020013021452\", persoData.getFaxNumber());\n        assertEquals(\"06-02-000000\", persoData.getMobileNumber());\n        assertEquals(\"04-76-000000\", persoData.getPhoneNumber());\n        assertEquals(\"Home\", persoData.getRoom());\n        assertEquals(\"Rhône\", persoData.getState());\n        assertEquals(\"http://perso.bonitasoft.com\", persoData.getWebsite());\n        assertEquals(\"69000\", persoData.getZipCode());\n    }\n\n    @Test\n    public void updateUserToBeEnabled() throws BonitaException {\n        // Create user, and updateDescriptor\n        final User user = getIdentityAPI().createUser(\"bonitasoft\", \"123456\");\n        assertNotNull(user);\n        assertTrue(user.isEnabled());\n        final UserUpdater updateDescriptor = new UserUpdater();\n        updateDescriptor.setEnabled(true);\n\n        // Update user\n        final User updatedUser = getIdentityAPI().updateUser(user.getId(), updateDescriptor);\n        assertNotNull(updatedUser);\n        assertTrue(updatedUser.isEnabled());\n\n        // Clean\n        getIdentityAPI().deleteUser(\"bonitasoft\");\n    }\n\n    @Test\n    public void updateUserToBeDisabled() throws BonitaException {\n        // Create user, and updateDescriptor\n        final User user = getIdentityAPI().createUser(\"bonitasoft\", \"123456\");\n        assertNotNull(user);\n        assertTrue(user.isEnabled());\n        final UserUpdater updateDescriptor = new UserUpdater();\n        updateDescriptor.setEnabled(false);\n\n        // Update user\n        final User updatedUser = getIdentityAPI().updateUser(user.getId(), updateDescriptor);\n        assertFalse(updatedUser.isEnabled());\n\n        // Clean\n        getIdentityAPI().deleteUser(\"bonitasoft\");\n    }\n\n    @Test\n    public void updateUserManager() throws BonitaException {\n        final User matti = getIdentityAPI().createUser(\"matti\", \"bpm\");\n        final User james = getIdentityAPI().createUser(\"james\", \"bpm\");\n        assertEquals(0, james.getManagerUserId());\n        final UserUpdater updateDescriptor = new UserUpdater();\n        updateDescriptor.setManagerId(matti.getId());\n        final User updatedUser = getIdentityAPI().updateUser(james.getId(), updateDescriptor);\n        assertNotNull(updatedUser);\n        assertEquals(matti.getId(), updatedUser.getManagerUserId());\n\n        deleteUsers(james, matti);\n    }\n\n    @Test(expected = UserNotFoundException.class)\n    public void updateUserWithUserNotFoundException() throws BonitaException {\n        final UserUpdater updateDescriptor = new UserUpdater();\n        updateDescriptor.setUserName(\"john\");\n        updateDescriptor.setPassword(\"bpm\");\n        final int userId = 100;\n        getIdentityAPI().updateUser(userId, updateDescriptor);\n    }\n\n    @Test(expected = UpdateException.class)\n    public void updateUserWithUserUpdateException() throws BonitaException {\n        final User oldUser = getIdentityAPI().createUser(\"old\", \"oldPassword\");\n        try {\n            getIdentityAPI().updateUser(oldUser.getId(), null);\n        } finally {\n            getIdentityAPI().deleteUser(oldUser.getId());\n        }\n    }\n\n    @Test\n    public void updateUserWithOnlyDataChanging() throws BonitaException {\n        final User user = getIdentityAPI().createUser(\"james\", \"mbp\");\n\n        final ContactDataUpdater persoDataUpDescr = buildPersoContactDataUpdater();\n        final ContactDataUpdater proDataUpDescr = buildProContactDataUpdater();\n\n        final UserUpdater updater = new UserUpdater();\n        updater.setPersonalContactData(persoDataUpDescr);\n        updater.setProfessionalContactData(proDataUpDescr);\n        final User updatedUser = getIdentityAPI().updateUser(user.getId(), updater);\n        assertNotNull(updatedUser);\n\n        final ContactData persoData = getIdentityAPI().getUserContactData(updatedUser.getId(), true);\n        checkPersoUserContactData(persoData);\n\n        final ContactData proData = getIdentityAPI().getUserContactData(updatedUser.getId(), false);\n        checkProUserContactData(proData);\n\n        getIdentityAPI().deleteUser(updatedUser.getId());\n    }\n\n    @Test\n    public void getPaginatedUsersWithUserCriterion() throws BonitaException {\n        final User user1 = getIdentityAPI().createUser(\"auser1o\", \"bpm\", \"a\", \"a\");\n        final User user2 = getIdentityAPI().createUser(\"cuser2o\", \"bpm\", \"c\", \"c\");\n        final User user3 = getIdentityAPI().createUser(\"buser3o\", \"bpm\", \"b\", \"b\");\n        final User user4 = getIdentityAPI().createUser(\"euser4o\", \"bpm\", \"e\", \"e\");\n        final User user5 = getIdentityAPI().createUser(\"duser5o\", \"bpm\", \"d\", \"d\");\n\n        List<User> usersASC = getIdentityAPI().getUsers(0, 3, UserCriterion.USER_NAME_ASC);\n        assertEquals(3, usersASC.size());\n        assertEquals(\"auser1o\", usersASC.get(0).getUserName());\n        assertEquals(\"buser3o\", usersASC.get(1).getUserName());\n        assertEquals(\"cuser2o\", usersASC.get(2).getUserName());\n        usersASC = getIdentityAPI().getUsers(3, 3, UserCriterion.USER_NAME_ASC);\n        assertEquals(2, usersASC.size());\n        assertEquals(\"duser5o\", usersASC.get(0).getUserName());\n        assertEquals(\"euser4o\", usersASC.get(1).getUserName());\n        final List<User> users = getIdentityAPI().getUsers(6, 3, UserCriterion.USER_NAME_ASC);\n        assertTrue(users.isEmpty());\n        final List<User> usersDESC = getIdentityAPI().getUsers(0, 3, UserCriterion.USER_NAME_DESC);\n        assertEquals(3, usersDESC.size());\n        assertEquals(\"euser4o\", usersDESC.get(0).getUserName());\n        assertEquals(\"duser5o\", usersDESC.get(1).getUserName());\n        assertEquals(\"cuser2o\", usersDESC.get(2).getUserName());\n\n        final List<User> usersFirstNameASC = getIdentityAPI().getUsers(0, 3, UserCriterion.FIRST_NAME_ASC);\n        assertEquals(3, usersFirstNameASC.size());\n        assertEquals(\"a\", usersFirstNameASC.get(0).getFirstName());\n        assertEquals(\"b\", usersFirstNameASC.get(1).getFirstName());\n        assertEquals(\"c\", usersFirstNameASC.get(2).getFirstName());\n\n        final List<User> usersFirstNameDESC = getIdentityAPI().getUsers(0, 3, UserCriterion.FIRST_NAME_DESC);\n        assertEquals(3, usersFirstNameDESC.size());\n        assertEquals(\"e\", usersFirstNameDESC.get(0).getFirstName());\n        assertEquals(\"d\", usersFirstNameDESC.get(1).getFirstName());\n        assertEquals(\"c\", usersFirstNameDESC.get(2).getFirstName());\n\n        final List<User> usersLastNameASC = getIdentityAPI().getUsers(0, 3, UserCriterion.LAST_NAME_ASC);\n        assertEquals(3, usersLastNameASC.size());\n        assertEquals(\"a\", usersLastNameASC.get(0).getLastName());\n        assertEquals(\"b\", usersLastNameASC.get(1).getLastName());\n        assertEquals(\"c\", usersLastNameASC.get(2).getLastName());\n\n        final List<User> usersLastNameDESC = getIdentityAPI().getUsers(0, 3, UserCriterion.LAST_NAME_DESC);\n        assertEquals(3, usersLastNameDESC.size());\n        assertEquals(\"e\", usersLastNameDESC.get(0).getLastName());\n        assertEquals(\"d\", usersLastNameDESC.get(1).getLastName());\n        assertEquals(\"c\", usersLastNameDESC.get(2).getLastName());\n\n        deleteUsers(user1, user2, user3, user4, user5);\n    }\n\n    @Test(expected = AlreadyExistsException.class)\n    public void cannotCreateTwoUserWithTheSameUserName() throws BonitaException {\n        final String username = \"install\";\n\n        final User user1 = getIdentityAPI().createUser(username, \"bpm\");\n        try {\n            getIdentityAPI().createUser(username, \"bos\");\n        } finally {\n            getIdentityAPI().deleteUser(user1.getId());\n        }\n    }\n\n    @Test(expected = SearchException.class)\n    public void searchUserWithWrongSortKey() throws BonitaException {\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.filter(UserSearchDescriptor.FIRST_NAME, \"Jean-Gustave\");\n        builder.sort(\"WRONG_SORT_KEY\", Order.ASC);\n        getIdentityAPI().searchUsers(builder.done());\n    }\n\n    @Test\n    public void searchUser() throws BonitaException {\n        final List<User> users = new ArrayList<>();\n        users.add(getIdentityAPI().createUser(\"jgrGF[|00\", \"bpm\", \"John\", \"Taylor\"));\n        users.add(getIdentityAPI().createUser(\"45èDG'fgb\", \"bpm\", \"Jack\", \"Jack\"));\n        users.add(getIdentityAPI().createUser(\"à\\\"(èg\", \"bpm\", \"John\", \"Smith\"));\n        users.add(getIdentityAPI().createUser(\"^^jhg\", \"bpm\", \"Paul\", \"Taylor\"));\n        users.add(getIdentityAPI().createUser(\"مرحبا!\", \"bpm\", \"Jack\", \"Jack\"));\n        users.add(getIdentityAPI().createUser(\"user02\", \"bpm\", \"Pierre\", \"Smith\"));\n        users.add(getIdentityAPI().createUser(\"User00\", \"bpm\", \"Marie\", \"Taylor\"));\n\n        // Filter and order test\n        SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.filter(UserSearchDescriptor.FIRST_NAME, \"John\");\n        builder.sort(UserSearchDescriptor.LAST_NAME, Order.ASC);\n        final SearchResult<User> searchUsers = getIdentityAPI().searchUsers(builder.done());\n        assertNotNull(searchUsers);\n        assertEquals(2, searchUsers.getCount());\n        List<User> usersResult = searchUsers.getResult();\n        assertEquals(users.get(2), usersResult.get(0));\n        assertEquals(users.get(0), usersResult.get(1));\n\n        // Pagination test\n        builder = new SearchOptionsBuilder(0, 10);\n        builder.sort(UserSearchDescriptor.LAST_NAME, Order.ASC);\n        final List<User> allUsers = getIdentityAPI().searchUsers(builder.done()).getResult();\n        assertEquals(7, allUsers.size());\n\n        builder = new SearchOptionsBuilder(0, 5);\n        builder.sort(UserSearchDescriptor.LAST_NAME, Order.ASC);\n        usersResult = getIdentityAPI().searchUsers(builder.done()).getResult();\n        assertEquals(5, usersResult.size());\n        for (int i = 0; i < 5; i++) {\n            assertEquals(allUsers.get(i), usersResult.get(i));\n        }\n\n        builder = new SearchOptionsBuilder(5, 10);\n        builder.sort(UserSearchDescriptor.LAST_NAME, Order.ASC);\n        usersResult = getIdentityAPI().searchUsers(builder.done()).getResult();\n        assertEquals(2, usersResult.size());\n        for (int i = 0; i < 2; i++) {\n            assertEquals(allUsers.get(i + 5), usersResult.get(i));\n        }\n\n        deleteUsers(users);\n    }\n\n    @Test\n    public void searchUserSortedById() throws BonitaException {\n        final List<User> users = new ArrayList<>();\n        users.add(getIdentityAPI().createUser(\"jgrGF[|00\", \"bpm\", \"John\", \"Taylor\"));\n        users.add(getIdentityAPI().createUser(\"user02\", \"bpm\", \"Pierre\", \"Smith\"));\n        users.add(getIdentityAPI().createUser(\"User00\", \"bpm\", \"Marie\", \"Taylor\"));\n\n        // Search ordered by id\n        SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.sort(UserSearchDescriptor.ID, Order.ASC);\n        List<User> usersResult = getIdentityAPI().searchUsers(builder.done()).getResult();\n\n        assertEquals(3, usersResult.size());\n        assertThat(usersResult.get(0).getId()).isLessThan(usersResult.get(1).getId());\n        assertThat(usersResult.get(1).getId()).isLessThan(usersResult.get(2).getId());\n\n        deleteUsers(users);\n    }\n\n    /*\n     * Relates to https://bonitasoft.atlassian.net/browse/RUNTIME-590\n     * All search should be insensitive\n     */\n    @Test\n    public void should_search_user_case_insensitively() throws BonitaException {\n        List<User> users = asList(\n                getIdentityAPI().createUser(\"Jean_Michel\", \"bpm\", \"Jean Michel\", \"Jarre\"),\n                getIdentityAPI().createUser(\"michel.mimi\", \"bpm\", \"Michel\", \"Mimi\"));\n\n        assertThat(getIdentityAPI().searchUsers(\n                new SearchOptionsBuilder(0, 10).searchTerm(\"jean\").sort(UserSearchDescriptor.ID, Order.ASC).done())\n                .getResult())\n                .hasSize(1).allMatch(user -> user.getUserName().equals(\"Jean_Michel\"));\n        assertThat(getIdentityAPI().searchUsers(\n                new SearchOptionsBuilder(0, 10).searchTerm(\"Jean\").sort(UserSearchDescriptor.ID, Order.ASC).done())\n                .getResult())\n                .hasSize(1).allMatch(user -> user.getUserName().equals(\"Jean_Michel\"));\n\n        deleteUsers(users);\n    }\n\n    @Test\n    public void searchEnabledDisabledUsers() throws BonitaException {\n        // Create users\n        final User john = getIdentityAPI().createUser(\"john002\", \"bpm\", \"John\", \"Taylor\");\n        final User jack = getIdentityAPI().createUser(\"jack001\", \"bpm\", \"Jack\", \"Doe\");\n\n        // Disabled jack\n        final UserUpdater updateDescriptor = new UserUpdater();\n        updateDescriptor.setEnabled(false);\n        final User updatedJack = getIdentityAPI().updateUser(jack.getId(), updateDescriptor);\n\n        // Search enabled users\n        SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.filter(UserSearchDescriptor.ENABLED, true);\n        SearchResult<User> searchUsers = getIdentityAPI().searchUsers(builder.done());\n        assertNotNull(searchUsers);\n        assertEquals(1, searchUsers.getCount());\n        List<User> users = searchUsers.getResult();\n        assertEquals(john, users.get(0));\n\n        // Search disabled users\n        builder = new SearchOptionsBuilder(0, 10);\n        builder.filter(UserSearchDescriptor.ENABLED, false);\n        searchUsers = getIdentityAPI().searchUsers(builder.done());\n        assertNotNull(searchUsers);\n        assertEquals(1, searchUsers.getCount());\n        users = searchUsers.getResult();\n        assertEquals(updatedJack, users.get(0));\n\n        // Clean up\n        deleteUsers(john, jack);\n    }\n\n    @Test\n    public void searchUserUsingTerm() throws BonitaException {\n        final User john2 = getIdentityAPI().createUser(\"john002\", \"bpm\", \"John\", \"Taylor\");\n        final User jack = getIdentityAPI().createUser(\"jack001\", \"bpm\", \"Jack\", \"Doe\");\n        final User john1 = getIdentityAPI().createUser(\"john001\", \"bpm\", \"John\", \"Smith\");\n\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.searchTerm(\"Jo\");\n        builder.sort(UserSearchDescriptor.LAST_NAME, Order.ASC);\n        final SearchResult<User> searchUsers = getIdentityAPI().searchUsers(builder.done());\n        assertNotNull(searchUsers);\n        assertEquals(2, searchUsers.getCount());\n        final List<User> users = searchUsers.getResult();\n        assertEquals(john1, users.get(0));\n        assertEquals(john2, users.get(1));\n\n        deleteUsers(john1, john2, jack);\n    }\n\n    @Test\n    public void searchUserWithApostrophe() throws BonitaException {\n        final User user1 = getIdentityAPI().createUser(\"'john'002\", \"bpm\", \"John\", \"A\");\n        final User user3 = getIdentityAPI().createUser(\"c\", \"bpm\", \"'john'n\", \"C\");\n        final User user4 = getIdentityAPI().createUser(\"d\", \"bpm\", \"'d\", \"John'\");\n\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.searchTerm(\"'\");\n        builder.sort(UserSearchDescriptor.LAST_NAME, Order.DESC);\n        final SearchResult<User> searchUsers = getIdentityAPI().searchUsers(builder.done());\n        assertNotNull(searchUsers);\n        assertEquals(3, searchUsers.getCount());\n        final List<User> users = searchUsers.getResult();\n        assertEquals(user4, users.get(0));\n        assertEquals(user3, users.get(1));\n        assertEquals(user1, users.get(2));\n\n        deleteUsers(user1, user3, user4);\n    }\n\n    @Test\n    public void searchTermWithSpecialChars() throws BonitaException {\n\n        List<User> users = asList(\n                getIdentityAPI().createUser(\"Sébastien\", \"ENCRYPTED\", \"Sébation\", \"Martin\"),\n                getIdentityAPI().createUser(\"房子\", \"ENCRYPTED\", \"\\uD83D\\uDC7A\", \"龜\"));\n\n        assertThat(getIdentityAPI().searchUsers(new SearchOptionsBuilder(0, 10).searchTerm(\"Séba\").done()).getResult())\n                .hasSize(1)\n                .allMatch(user -> user.getUserName().equals(\"Sébastien\"));\n        assertThat(getIdentityAPI().searchUsers(new SearchOptionsBuilder(0, 10).searchTerm(\"龜\").done()).getResult())\n                .hasSize(1)\n                .allMatch(user -> user.getUserName().equals(\"房子\"));\n\n        getIdentityAPI().deleteUsers(users.stream().map(User::getId).collect(Collectors.toList()));\n    }\n\n    @Test\n    public void searchUsersInGroup() throws BonitaException {\n        final User john1 = createUser(\"john001\", \"bpm\", \"John\", \"Smith\");\n        final User jack = createUser(\"jack001\", \"bpm\", \"Jack\", \"Doe\");\n        final User john2 = createUser(\"john002\", \"bpm\", \"John\", \"Taylor\");\n\n        final Group group = createGroup(\"group1\");\n        final Role role = createRole(\"manager\");\n\n        getIdentityAPI().addUserMemberships(asList(john1.getId(), jack.getId()), group.getId(), role.getId());\n\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.filter(UserSearchDescriptor.GROUP_ID, (Long) (group.getId()));\n        builder.sort(UserSearchDescriptor.USER_NAME, Order.DESC);\n        final SearchResult<User> searchUsers = getIdentityAPI().searchUsers(builder.done());\n        assertNotNull(searchUsers);\n        assertEquals(2, searchUsers.getCount());\n        final List<User> users = searchUsers.getResult();\n        assertEquals(john1, users.get(0));\n        assertEquals(jack, users.get(1));\n\n        getIdentityAPI().deleteGroup(group.getId());\n        getIdentityAPI().deleteRole(role.getId());\n        deleteUsers(john1, john2, jack);\n    }\n\n    @Test\n    public void searchUsersInRole() throws BonitaException {\n        final User john1 = createUser(\"john001\", \"bpm\", \"John\", \"Smith\");\n        final User jack = createUser(\"jack001\", \"bpm\", \"Jack\", \"Doe\");\n        final User john2 = createUser(\"john002\", \"bpm\", \"John\", \"Taylor\");\n\n        final Group group = createGroup(\"group1\");\n        final Role role = createRole(\"manager\");\n\n        getIdentityAPI().addUserMemberships(asList(john1.getId(), jack.getId()), group.getId(), role.getId());\n\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.filter(UserSearchDescriptor.ROLE_ID, (Long) (role.getId()));\n        builder.sort(UserSearchDescriptor.USER_NAME, Order.ASC);\n        final SearchResult<User> searchUsers = getIdentityAPI().searchUsers(builder.done());\n        assertNotNull(searchUsers);\n        assertEquals(2, searchUsers.getCount());\n        final List<User> users = searchUsers.getResult();\n        assertEquals(jack, users.get(0));\n        assertEquals(john1, users.get(1));\n\n        getIdentityAPI().deleteGroup(group.getId());\n        getIdentityAPI().deleteRole(role.getId());\n        deleteUsers(john1, john2, jack);\n    }\n\n    @Test\n    public void searchUsersInRoleAndGroup() throws BonitaException {\n        final User john1 = createUser(\"john001\", \"bpm\", \"John\", \"Smith\");\n        final User jack = createUser(\"jack001\", \"bpm\", \"Jack\", \"Doe\");\n        final User john2 = createUser(\"john002\", \"bpm\", \"John\", \"Taylor\");\n\n        final Group group1 = createGroup(\"group1\");\n        final Group group2 = createGroup(\"group2\");\n        final Role role1 = createRole(\"manager\");\n        final Role role2 = createRole(\"delivery\");\n\n        getIdentityAPI().addUserMemberships(asList(john1.getId(), jack.getId()), group1.getId(), role1.getId());\n        getIdentityAPI().addUserMemberships(asList(john2.getId()), group2.getId(), role1.getId());\n        getIdentityAPI().addUserMemberships(asList(john2.getId(), jack.getId()), group1.getId(), role2.getId());\n\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.filter(UserSearchDescriptor.GROUP_ID, group2.getId());\n        builder.filter(UserSearchDescriptor.ROLE_ID, role1.getId());\n        builder.sort(UserSearchDescriptor.USER_NAME, Order.ASC);\n        final SearchResult<User> searchUsers = getIdentityAPI().searchUsers(builder.done());\n        assertNotNull(searchUsers);\n        assertEquals(1, searchUsers.getCount());\n        final List<User> users = searchUsers.getResult();\n        assertEquals(john2, users.get(0));\n\n        deleteGroups(group1, group2);\n        deleteRoles(role1, role2);\n        deleteUsers(john1, john2, jack);\n    }\n\n    @Test\n    public void searchTeamMembers() throws BonitaException {\n        final User manager = getIdentityAPI().createUser(\"john002\", \"bpm\", \"John\", \"Taylor\");\n        final UserCreator creator = new UserCreator(\"jack001\", \"bpm\");\n        creator.setFirstName(\"Jack\").setLastName(\"Doe\").setManagerUserId(manager.getId());\n        final User jack = getIdentityAPI().createUser(creator);\n        final User john = getIdentityAPI().createUser(\"john001\", \"bpm\", \"John\", \"Smith\");\n\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.filter(UserSearchDescriptor.MANAGER_USER_ID, (Long) manager.getId());\n        final SearchResult<User> searchUsers = getIdentityAPI().searchUsers(builder.done());\n        assertNotNull(searchUsers);\n        assertEquals(1, searchUsers.getCount());\n        final List<User> users = searchUsers.getResult();\n        assertEquals(jack, users.get(0));\n\n        deleteUsers(john, manager, jack);\n    }\n\n    @Test\n    public void searchUsersNotInTeam() throws BonitaException {\n        // Manager\n        final User manager = getIdentityAPI().createUser(\"john002\", \"bpm\", \"John\", \"Taylor\");\n\n        // User with manager\n        final UserCreator creator = new UserCreator(\"jack001\", \"bpm\");\n        creator.setFirstName(\"Jack\").setLastName(\"Doe\").setManagerUserId(manager.getId());\n        final User jack = getIdentityAPI().createUser(creator);\n\n        // User without manager\n        final User john = getIdentityAPI().createUser(\"john003\", \"bpm\", \"John\", \"Smith\");\n\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.differentFrom(UserSearchDescriptor.MANAGER_USER_ID, manager.getId())\n                .differentFrom(UserSearchDescriptor.USER_NAME, manager.getUserName());\n        final SearchResult<User> searchUsers = getIdentityAPI().searchUsers(builder.done());\n        assertNotNull(searchUsers);\n        assertEquals(1, searchUsers.getCount());\n        final List<User> users = searchUsers.getResult();\n        assertEquals(john, users.get(0));\n\n        deleteUsers(john, manager, jack);\n    }\n\n    @Test\n    public void getUsersFromIdsShouldReturnUsersInTheRightOrder() throws BonitaException {\n        final List<User> expectedUsers = new ArrayList<>();\n        expectedUsers.add(getIdentityAPI().createUser(\"zhao\", \"engine\"));\n        expectedUsers.add(getIdentityAPI().createUser(\"qian\", \"engine\"));\n        expectedUsers.add(getIdentityAPI().createUser(\"sun\", \"engine\"));\n        expectedUsers.add(getIdentityAPI().createUser(\"li\", \"engine\"));\n        expectedUsers.add(getIdentityAPI().createUser(\"zhou\", \"engine\"));\n        final List<Long> userIds = new ArrayList<>(5);\n        userIds.add(expectedUsers.get(4).getId());\n        userIds.add(expectedUsers.get(0).getId());\n        userIds.add(expectedUsers.get(2).getId());\n        userIds.add(expectedUsers.get(1).getId());\n        userIds.add(expectedUsers.get(3).getId());\n\n        final Map<Long, User> usersResult = getIdentityAPI().getUsers(userIds);\n        assertNotNull(usersResult);\n        assertEquals(5, usersResult.size());\n        assertEquals(expectedUsers.get(0), usersResult.get(expectedUsers.get(0).getId()));\n        assertEquals(expectedUsers.get(1), usersResult.get(expectedUsers.get(1).getId()));\n        assertEquals(expectedUsers.get(2), usersResult.get(expectedUsers.get(2).getId()));\n        assertEquals(expectedUsers.get(3), usersResult.get(expectedUsers.get(3).getId()));\n        assertEquals(expectedUsers.get(4), usersResult.get(expectedUsers.get(4).getId()));\n\n        deleteUsers(expectedUsers);\n    }\n\n    @Test\n    public void checkCreatedByForCreatedUser() throws BonitaException {\n        final User userCreated = getIdentityAPI().createUser(\"bonitasoft\", \"123456\");\n        assertNotNull(userCreated);\n        assertEquals(\"bonitasoft\", userCreated.getUserName());\n        final User user = getIdentityAPI().getUserByUserName(\"bonitasoft\");\n        assertNotNull(user);\n        assertNotNull(user.getCreatedBy());\n        assertEquals(getSession().getUserId(), user.getCreatedBy());\n        getIdentityAPI().deleteUser(\"bonitasoft\");\n    }\n\n    @Test\n    public void getUserIds() throws BonitaException {\n        final User matti = getIdentityAPI().createUser(\"matti\", \"bpm\");\n        final User jani = getIdentityAPI().createUser(\"jani\", \"bpm\");\n\n        final List<String> userNames = new ArrayList<>(3);\n        userNames.add(\"jani\");\n        userNames.add(\"liisa\");\n        userNames.add(\"matti\");\n\n        final Map<String, User> userIds = getIdentityAPI().getUsersByUsernames(userNames);\n        assertEquals(2, userIds.size());\n        assertEquals(jani, userIds.get(\"jani\"));\n        assertEquals(matti, userIds.get(\"matti\"));\n\n        deleteUsers(matti, jani);\n    }\n\n    @Test\n    public void getUserWithProContactData() throws BonitaException {\n        final String james = \"james\";\n        final User user = getIdentityAPI().createUser(james, \"mbp\");\n        final UserUpdater updateDescriptor = buildUserUpdaterWithProContact();\n        final User updatedUser = getIdentityAPI().updateUser(user.getId(), updateDescriptor);\n        final ContactData userContactData = getIdentityAPI().getUserContactData(updatedUser.getId(), false);\n\n        final UserWithContactData proUser = getIdentityAPI().getUserWithProfessionalDetails(updatedUser.getId());\n        assertEquals(userContactData, proUser.getContactData());\n        assertEquals(updatedUser, proUser.getUser());\n\n        getIdentityAPI().deleteUser(updatedUser.getId());\n    }\n\n    @Test\n    public void getUserWithoutProContactData() throws BonitaException {\n        final String james = \"james\";\n        final User user = getIdentityAPI().createUser(james, \"mbp\");\n        final UserUpdater updateDescriptor = buildUserUpdater();\n        final User updatedUser = getIdentityAPI().updateUser(user.getId(), updateDescriptor);\n\n        final UserWithContactData proUser = getIdentityAPI().getUserWithProfessionalDetails(updatedUser.getId());\n        assertNull(proUser.getContactData());\n        assertEquals(updatedUser, proUser.getUser());\n\n        getIdentityAPI().deleteUser(updatedUser.getId());\n    }\n\n    @Test(expected = UserNotFoundException.class)\n    public void throwExceptionWhenGettingUnknownUserWithProContactData() throws BonitaException {\n        getIdentityAPI().getUserWithProfessionalDetails(-45L);\n    }\n\n    @Test(expected = UserNotFoundException.class)\n    public void throwExceptionWhenGettingTechnicalUserWithProContactData() throws BonitaException {\n        getIdentityAPI().getUserWithProfessionalDetails(-1L);\n    }\n\n    @Test\n    public void can_create_user_with_255_char_in_fields() throws Exception {\n        final UserCreator creator = new UserCreator(completeWithZeros(\"user\"), \"bpm\");\n        creator.setJobTitle(completeWithZeros(\"Engineer\"));\n        creator.setFirstName(completeWithZeros(\"First\"));\n        creator.setLastName(completeWithZeros(\"Last\"));\n\n        final ContactDataCreator contactDataCreator = new ContactDataCreator();\n        contactDataCreator.setAddress(completeWithZeros(\"32 Rue Gustave Eiffel\"));\n        creator.setProfessionalContactData(contactDataCreator);\n\n        //when\n        final User user = getIdentityAPI().createUser(creator);\n\n        //then\n        assertThat(user).isNotNull();\n        assertThat(user.getUserName()).hasSize(255);\n        assertThat(user.getFirstName()).hasSize(255);\n        assertThat(user.getLastName()).hasSize(255);\n        assertThat(user.getJobTitle()).hasSize(255);\n\n        //when\n        final UserWithContactData userWithContactData = getIdentityAPI().getUserWithProfessionalDetails(user.getId());\n\n        //then\n        assertThat(userWithContactData).isNotNull();\n        assertThat(userWithContactData.getContactData().getAddress()).hasSize(255);\n\n        //clean\n        getIdentityAPI().deleteUser(user.getId());\n\n    }\n\n    private String completeWithZeros(final String prefix) {\n        return prefix + \"0\".repeat(Math.max(0, 255 - prefix.length()));\n    }\n\n    @Test\n    public void login_should_work_with_specific_characters() throws Exception {\n        final User jyri = createUser(\"Jyri\", \"1234\");\n\n        final UserUpdater updater = new UserUpdater();\n        updater.setPassword(\"ñ1234\");\n        getIdentityAPI().updateUser(jyri.getId(), updater);\n\n        loginOnDefaultTenantWith(\"Jyri\", \"ñ1234\");\n        assertThat(getApiClient().getSession()).isNotNull();\n\n        getIdentityAPI().deleteUser(jyri.getId());\n    }\n\n    @Test\n    public void theTest() throws Exception {\n        loginWithTechnicalUser();\n        User john = createUser(\"john\", \"bpm\");\n        User jack = createUser(\"jack\", \"bpm\");\n        Date connection1 = getIdentityAPI().getUserByUserName(\"john\").getLastConnection();\n        Thread.sleep(20);\n        logout();\n        loginOnDefaultTenantWith(\"john\", \"bpm\");\n        Date connection2 = getIdentityAPI().getUserByUserName(\"john\").getLastConnection();\n        getIdentityAPI().getUserByUserName(\"john\").getLastConnection();\n        Thread.sleep(20);\n        logout();\n        loginOnDefaultTenantWith(\"john\", \"bpm\");\n        Date connection3 = getIdentityAPI().getUserByUserName(\"john\").getLastConnection();\n        Thread.sleep(20);\n        logout();\n        loginOnDefaultTenantWith(\"jack\", \"bpm\");\n        Date connection4 = getIdentityAPI().getUserByUserName(\"john\").getLastConnection();\n        logout();\n        loginWithTechnicalUser();\n\n        deleteUsers(john, jack);\n\n        assertThat(connection1).isNull();\n        assertThat(connection2).isBefore(connection3);\n        assertThat(connection3).isEqualTo(connection4);\n\n    }\n\n    @Test\n    public void should_create_user_with_icon_create_the_icon() throws Exception {\n        //given\n        User user = getIdentityAPI().createUser(\n                new UserCreator(\"userWithIcon\", \"thePassword\").setIcon(\"myAvatar.jpg\", \"avatarContent\".getBytes()));\n        //when\n        Icon icon = getIdentityAPI().getIcon(user.getIconId());\n        //then\n        assertThat(icon).isEqualTo(new IconImpl(icon.getId(), \"image/jpeg\", \"avatarContent\".getBytes()));\n        //cleanup\n        getIdentityAPI().deleteUser(\"userWithIcon\");\n    }\n\n    @Test\n    public void should_delete_user_delete_the_icon() throws Exception {\n        //given\n        User user = getIdentityAPI().createUser(\n                new UserCreator(\"userWithIcon\", \"thePassword\").setIcon(\"myAvatar.jpg\", \"avatarContent\".getBytes()));\n        //when\n        getIdentityAPI().deleteUser(user.getId());\n        //then\n        expectedException.expect(NotFoundException.class);\n        expectedException.expectMessage(\"unable to find icon with id \" + user.getIconId());\n        try {\n            getIdentityAPI().getIcon(user.getIconId());\n        } finally {\n            getIdentityAPI().deleteUser(\"userWithIcon\");\n        }\n\n    }\n\n    @Test\n    public void should_update_user_create_a_new_icon() throws Exception {\n        //given\n        User user = getIdentityAPI().createUser(new UserCreator(\"userWithIcon\", \"thePassword\"));\n        //when\n        User updatedUser = getIdentityAPI().updateUser(user.getId(),\n                new UserUpdater().setIcon(\"myFile.png\", \"content\".getBytes()));\n        //then\n        Icon icon = getIdentityAPI().getIcon(updatedUser.getIconId());\n        assertThat(icon).isEqualTo(new IconImpl(icon.getId(), \"image/png\", \"content\".getBytes()));\n        //cleanup\n        getIdentityAPI().deleteUser(\"userWithIcon\");\n    }\n\n    @Test\n    public void should_update_user_with_new_icon_create_a_new_icon() throws Exception {\n        //given\n        User user = getIdentityAPI().createUser(\n                new UserCreator(\"userWithIcon\", \"thePassword\").setIcon(\"myAvatar.png\", \"avatarContent\".getBytes()));\n        //when\n        User updatedUser = getIdentityAPI().updateUser(user.getId(),\n                new UserUpdater().setIcon(\"myFile.jpg\", \"content\".getBytes()));\n        //then\n        Icon newIcon = getIdentityAPI().getIcon(updatedUser.getIconId());\n        assertThat(newIcon.getId()).isNotEqualTo(user.getIconId());\n        assertThat(newIcon.getMimeType()).isEqualTo(\"image/jpeg\");\n        assertThat(newIcon.getContent()).isEqualTo(\"content\".getBytes());\n        //cleanup\n        getIdentityAPI().deleteUser(\"userWithIcon\");\n    }\n\n    @Test\n    public void should_update_user_delete_the_icon() throws Exception {\n        //given\n        User user = getIdentityAPI().createUser(\n                new UserCreator(\"userWithIcon\", \"thePassword\").setIcon(\"myAvatar.jpg\", \"avatarContent\".getBytes()));\n        Icon icon = getIdentityAPI().getIcon(user.getIconId());\n        //when\n        User updateUser = getIdentityAPI().updateUser(user.getId(), new UserUpdater().setIcon(null, null));\n        //then\n        try {\n            getIdentityAPI().getIcon(icon.getId());\n            fail();\n        } catch (NotFoundException ignored) {\n        }\n        assertThat(updateUser.getIconId()).isNull();\n        //cleanup\n        getIdentityAPI().deleteUser(\"userWithIcon\");\n    }\n\n    @Test(expected = LoginException.class)\n    public void loginFailsUsingWrongUser() throws BonitaException {\n        final String userName = \"hannu\";\n        final String password = \"install\";\n        TenantAPIAccessor.getLoginAPI().login(userName, password);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/login/PlatformLoginAPIIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.login;\n\nimport java.io.IOException;\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.CommonAPIIT;\nimport org.bonitasoft.engine.api.PlatformAPIAccessor;\nimport org.bonitasoft.engine.api.PlatformCommandAPI;\nimport org.bonitasoft.engine.api.PlatformLoginAPI;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.session.PlatformSession;\nimport org.bonitasoft.engine.session.SessionNotFoundException;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class PlatformLoginAPIIT extends CommonAPIIT {\n\n    private static final String COMMAND_NAME = \"deletePlatformSessionCommand\";\n\n    private static PlatformLoginAPI platformLoginAPI;\n\n    @Before\n    public void before() throws BonitaException, IOException {\n        platformLoginAPI = PlatformAPIAccessor.getPlatformLoginAPI();\n    }\n\n    @Test(expected = SessionNotFoundException.class)\n    public void testSessionNotFoundExceptionIsThrownAfterSessionDeletion() throws Exception {\n        // login to create a session\n        final PlatformSession sessionToDelete = platformLoginAPI.login(\"platformAdmin\", \"platform\");\n\n        // delete the session created by the login\n        deleteSession(sessionToDelete.getId());\n\n        // will throw SessionNotFoundException\n        platformLoginAPI.logout(sessionToDelete);\n    }\n\n    private void deleteSession(final long sessionId) throws Exception {\n        // create a new session to deploy and execute commands\n        final PlatformSession session = platformLoginAPI.login(\"platformAdmin\", \"platform\");\n        final PlatformCommandAPI platformCommandAPI = PlatformAPIAccessor.getPlatformCommandAPI(session);\n\n        // register and execute a command to delete a session\n        platformCommandAPI.register(COMMAND_NAME, \"Deletes a platform session based on its sessionId\",\n                \"org.bonitasoft.engine.command.DeletePlatformSessionCommand\");\n        final Map<String, Serializable> parameters = new HashMap<>();\n        parameters.put(\"sessionId\", sessionId);\n        platformCommandAPI.execute(COMMAND_NAME, parameters);\n        platformCommandAPI.unregister(COMMAND_NAME);\n\n        // logout\n        platformLoginAPI.logout(session);\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/mdc/APICallLogIT.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.mdc;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.CommonAPIIT;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.identity.UserCreator;\nimport org.bonitasoft.engine.identity.UserCreator.UserField;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.SystemOutRule;\n\npublic class APICallLogIT extends CommonAPIIT {\n\n    private User bill;\n\n    @Before\n    public void beforeTest() throws BonitaException {\n        loginWithTechnicalUser();\n        bill = createUser(USERNAME, \"bpm\");\n    }\n\n    @Rule\n    public SystemOutRule systemOutRule = new SystemOutRule().enableLog();\n\n    @Test\n    public void apiCallAndInspectLogs() throws Exception {\n        // given\n        loginWithTechnicalUser();\n\n        logout();\n        // when\n        loginOnDefaultTenantWith(USERNAME, \"bpm\");\n        systemOutRule.clearLog();\n        try {\n            var creator = new UserCreator(\"johny\", \"bpm\");\n            // using the deprecated method should log a warning\n            creator.getFields().put(UserField.ICON_NAME, \"test.png\");\n            getIdentityAPI().createUser(creator);\n            // then and error is logged with user id in context\n            LogITUtil.checkLogEntryContains(systemOutRule.getLog(),\n                    Map.of(MDCConstants.USER_ID, Long.toString(bill.getId())));\n        } finally {\n            getIdentityAPI().deleteUser(\"johny\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/mdc/ConnectorExecutionLogIT.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.mdc;\n\nimport static org.bonitasoft.engine.test.BuildTestUtil.generateConnectorImplementation;\n\nimport java.util.Map;\n\nimport ch.qos.logback.classic.Level;\nimport ch.qos.logback.classic.Logger;\nimport org.bonitasoft.engine.CommonAPIIT;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.AutomaticTaskDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.connectors.DoNothingConnector;\nimport org.bonitasoft.engine.connectors.FailingConnector;\nimport org.bonitasoft.engine.execution.ProcessExecutorImpl;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.SystemOutRule;\nimport org.slf4j.LoggerFactory;\n\npublic class ConnectorExecutionLogIT extends CommonAPIIT {\n\n    @Rule\n    public SystemOutRule systemOutRule = new SystemOutRule().enableLog();\n\n    @After\n    public void afterTest() throws Exception {\n        logout();\n    }\n\n    @Before\n    public void beforeTest() throws Exception {\n        loginWithTechnicalUser();\n    }\n\n    @Test\n    public void executeConnectorAndInspectLogs() throws Exception {\n        AutomaticTaskDefinitionBuilder automaticTask = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithConnectors\", \" 1.0\")\n                .addAutomaticTask(\"step\");\n        automaticTask.addConnector(\"enter1\", \"connectorDef1\", \"1.0\", ConnectorEvent.ON_ENTER);\n\n        BusinessArchive bar = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(automaticTask\n                        .getProcess())\n                .addConnectorImplementation(\n                        generateConnectorImplementation(\"connectorDef1\", \"1.0\", DoNothingConnector.class))\n                .done();\n        ProcessDefinition processDefinition = getProcessAPI().deploy(bar);\n        getProcessAPI().enableProcess(processDefinition.getId());\n\n        final Logger exeLogger = (Logger) LoggerFactory.getLogger(ProcessExecutorImpl.class);\n        Level oldLogLevel = exeLogger.getLevel();\n        try {\n            exeLogger.setLevel(Level.DEBUG);\n            // when\n            systemOutRule.clearLog();\n            ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n            waitForProcessToFinish(processInstance);\n            // then we must have context for exception, even if it was logged outside of the initial throwing scope\n            checkLogEntryContains(Map.of(MDCConstants.PROCESS_INSTANCE_ID, Long.toString(processInstance.getId()),\n                    MDCConstants.PROCESS_DEFINITION_ID, Long.toString(processDefinition.getId()),\n                    MDCConstants.ROOT_PROCESS_INSTANCE_ID, Long.toString(processInstance.getRootProcessInstanceId())));\n\n        } finally {\n            disableAndDeleteProcess(processDefinition);\n            // restore old log level\n            exeLogger.setLevel(oldLogLevel);\n        }\n\n    }\n\n    @Test\n    public void executeFailedConnectorAndInspectLogs() throws Exception {\n        AutomaticTaskDefinitionBuilder automaticTask = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithFailedConnectors\", \" 1.0\")\n                .addAutomaticTask(\"step\");\n        automaticTask.addConnector(\"enter1\", \"connectorDef1\", \"1.0\", ConnectorEvent.ON_ENTER);\n        automaticTask.addConnector(\"enter2\", \"failingConnector\", \"1.0\", ConnectorEvent.ON_ENTER);\n\n        BusinessArchive bar = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(automaticTask\n                        .getProcess())\n                .addConnectorImplementation(\n                        generateConnectorImplementation(\"connectorDef1\", \"1.0\", DoNothingConnector.class))\n                .addConnectorImplementation(\n                        generateConnectorImplementation(\"failingConnector\", \"1.0\", FailingConnector.class))\n                .done();\n        ProcessDefinition processDefinition = getProcessAPI().deploy(bar);\n        getProcessAPI().enableProcess(processDefinition.getId());\n\n        try {\n            // when\n            systemOutRule.clearLog();\n            ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n            FlowNodeInstance failedTask = waitForFlowNodeInFailedState(processInstance, \"step\");\n            // then we must have context for exception, even if it was logged outside of the initial throwing scope\n            checkLogEntryContains(Map.of(MDCConstants.PROCESS_INSTANCE_ID, Long.toString(processInstance.getId()),\n                    MDCConstants.PROCESS_DEFINITION_ID, Long.toString(processDefinition.getId()),\n                    MDCConstants.ROOT_PROCESS_INSTANCE_ID, Long.toString(processInstance.getRootProcessInstanceId()),\n                    MDCConstants.FLOW_NODE_INSTANCE_ID, Long.toString(failedTask.getId())));\n\n        } finally {\n            disableAndDeleteProcess(processDefinition);\n        }\n    }\n\n    /**\n     * Check there is a line in the log which contain context variables\n     *\n     * @param context context variables to check\n     */\n    private void checkLogEntryContains(Map<String, String> context) {\n        var log = systemOutRule.getLogWithNormalizedLineSeparator();\n        LogITUtil.checkLogEntryContains(log, context);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/mdc/LogITUtil.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.mdc;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.text.MessageFormat;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Map;\n\npublic class LogITUtil {\n\n    /**\n     * Check there is a line in the log which contain context variables\n     *\n     * @param log the log text\n     * @param context context variables to check\n     */\n    public static void checkLogEntryContains(String log, Map<String, String> context) {\n        checkLogEntryContains(log, context, Collections.emptyList());\n    }\n\n    /**\n     * Check there is a line in the log which contain context variables\n     *\n     * @param log the log text\n     * @param context context variables to check\n     * @param forbiddenKeys keys that must not appear in the context\n     */\n    public static void checkLogEntryContains(String log, Map<String, String> context,\n            Collection<String> forbiddenKeys) {\n        var lines = log.split(\"\\n\\\\|\");\n        assertThat(lines).anyMatch(l -> {\n            var entries = context.entrySet().stream();\n            return entries.allMatch(e -> {\n                // we use the '| %X' in the pattern, so that every variable is preceded by a blank space\n                var valueLog = MessageFormat.format(\" {0}={1}\", e.getKey(), e.getValue());\n                return l.contains(valueLog);\n            }) && forbiddenKeys.stream().noneMatch(k -> {\n                var keyLog = MessageFormat.format(\" {0}=\", k);\n                return l.contains(keyLog);\n            });\n        });\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/mdc/ProcessExecutionLogIT.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.mdc;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.function.BiConsumer;\nimport java.util.function.Function;\nimport java.util.regex.Pattern;\n\nimport ch.qos.logback.classic.Level;\nimport ch.qos.logback.classic.Logger;\nimport org.bonitasoft.engine.CommonAPIIT;\nimport org.bonitasoft.engine.api.impl.ProcessAPIImpl;\nimport org.bonitasoft.engine.api.impl.ProcessManagementAPIImplDelegate;\nimport org.bonitasoft.engine.api.impl.ProcessStarter;\nimport org.bonitasoft.engine.api.impl.resolver.BusinessArchiveArtifactsManager;\nimport org.bonitasoft.engine.api.impl.transaction.process.DisableProcess;\nimport org.bonitasoft.engine.api.impl.transaction.process.EnableProcess;\nimport org.bonitasoft.engine.bar.BusinessArchiveServiceImpl;\nimport org.bonitasoft.engine.bpm.process.impl.internal.DesignProcessDefinitionImpl;\nimport org.bonitasoft.engine.classloader.ClassLoaderServiceImpl;\nimport org.bonitasoft.engine.execution.ProcessExecutorImpl;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.operation.Operation;\nimport org.bonitasoft.engine.operation.OperationBuilder;\nimport org.bonitasoft.engine.test.BuildTestUtil;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.SystemOutRule;\nimport org.slf4j.LoggerFactory;\n\npublic class ProcessExecutionLogIT extends CommonAPIIT {\n\n    @Rule\n    public SystemOutRule systemOutRule = new SystemOutRule().enableLog();\n\n    @After\n    public void afterTest() throws Exception {\n        logout();\n    }\n\n    @Before\n    public void beforeTest() throws Exception {\n        loginWithTechnicalUser();\n    }\n\n    @Test\n    public void inspectTransactionIdInLogs() throws Exception {\n        var txIdPattern = Pattern.compile(\" \" + MDCConstants.TRANSACTION_ID + \"=([0-9a-f:]+)\");\n        Function<String, Optional<String>> getTxId = l -> {\n            var matcher = txIdPattern.matcher(l);\n            return matcher.find() ? Optional.of(matcher.group(1)) : Optional.empty();\n        };\n\n        BiConsumer<String, Map<String, Integer>> checkLogHasTxIds = (log, classPatternsWithSameTx) -> {\n            var classesToFind = new HashMap<String, Integer>(classPatternsWithSameTx);\n            var txIdFoundByClass = new HashMap<String, String>(classPatternsWithSameTx.size());\n            var lines = log.split(\"\\n\\\\|\");\n            for (var l : lines) {\n                var classFound = classesToFind.keySet().stream().filter(c -> {\n                    var linePattern = String.format(\"(?s).*(%s)\\\\|.*\", c);\n                    return l.matches(linePattern);\n                }).findAny();\n                classFound.ifPresent(c -> {\n                    // should have a tx id\n                    var txId = getTxId.apply(l);\n                    assertThat(txId).as(\"Log line %s should contain %s\", l, MDCConstants.TRANSACTION_ID).isPresent();\n\n                    var toFind = classesToFind.get(c);\n                    assertThat(toFind).as(\"Log line %s was found, but we expected only %d lines for class pattern %s\",\n                            l, classPatternsWithSameTx.get(c), c)\n                            .isGreaterThan(0);\n                    classesToFind.put(c, toFind - 1);\n\n                    var previousTxId = txIdFoundByClass.put(c, txId.get());\n                    if (previousTxId != null) {\n                        assertThat(previousTxId)\n                                .as(\"Transaction id should be consistent for class pattern %s. It has changed in line %s\",\n                                        c, l)\n                                .isEqualTo(txId.get());\n                    }\n                });\n                // there may be other log lines with or without transaction id...\n            }\n\n            assertThat(classesToFind).allSatisfy((c, i) -> assertThat(i)\n                    .as(\"We expected %2$d log lines for class pattern %1$s but found only %3$d.\",\n                            c, classPatternsWithSameTx.get(c), classPatternsWithSameTx.get(c) - i)\n                    .isEqualTo(0));\n        };\n\n        var designProcessDef = BuildTestUtil.buildProcessDefinitionWithHumanAndAutomaticSteps(List.of(\"H1\"),\n                List.of(true));\n        var user = createUser(USERNAME, PASSWORD);\n        // when\n        systemOutRule.clearLog();\n        var processDef = deployAndEnableProcessWithActor(designProcessDef, BuildTestUtil.ACTOR_NAME, user);\n\n        // then, check logs have transaction id for the given classes\n        var businessArchivePattern = BusinessArchiveArtifactsManager.class.getSimpleName() + \"|\"\n                + BusinessArchiveServiceImpl.class.getSimpleName();\n        checkLogHasTxIds.accept(systemOutRule.getLogWithNormalizedLineSeparator(),\n                Map.of(businessArchivePattern, 2,\n                        ClassLoaderServiceImpl.class.getSimpleName(), 2,\n                        EnableProcess.class.getSimpleName(), 1));\n\n        long userId = user.getId();\n        try {\n            // when\n            systemOutRule.clearLog();\n            getProcessAPI().startProcess(userId, processDef.getId());\n\n            // then consecutive logs contain the same transaction id or a unique new one\n            checkLogHasTxIds.accept(systemOutRule.getLogWithNormalizedLineSeparator(),\n                    Map.of(ProcessStarter.class.getSimpleName(), 1));\n        } finally {\n            // when\n            systemOutRule.clearLog();\n            disableAndDeleteProcess(processDef);\n            // then there should be a transaction id once again\n            checkLogHasTxIds.accept(systemOutRule.getLogWithNormalizedLineSeparator(),\n                    Map.of(DisableProcess.class.getSimpleName(), 1,\n                            ProcessManagementAPIImplDelegate.class.getSimpleName(), 1));\n        }\n\n    }\n\n    @Test\n    public void failProcessWithExceptionAndInspectLogs() throws Exception {\n        var designProcessDef = BuildTestUtil.buildProcessDefinitionWithHumanAndAutomaticSteps(List.of(\"S1\", \"H1\"),\n                List.of(false, true));\n        ((DesignProcessDefinitionImpl) designProcessDef).setStringIndex(1, \"i1\",\n                new ExpressionBuilder().createConstantStringExpression(\"a\"));\n        for (var act : designProcessDef.getFlowElementContainer().getActivities()) {\n            switch (act.getName()) {\n                case \"S1\":\n                    // make S1 fail explicitly\n                    var script = \"throw new Exception(\\\"Expected fail=OK\\\")\";\n                    Expression lastingExpr = new ExpressionBuilder().createGroovyScriptExpression(\"script\", script,\n                            String.class.getName());\n                    Operation operation = new OperationBuilder().createSetStringIndexOperation(1, lastingExpr);\n                    act.getOperations().add(operation);\n                    break;\n                default:\n                    break;\n            }\n        }\n        var user = createUser(USERNAME, PASSWORD);\n        var processDef = deployAndEnableProcessWithActor(designProcessDef, BuildTestUtil.ACTOR_NAME, user);\n        long userId = user.getId();\n        try {\n            // when\n            systemOutRule.clearLog();\n            var processInstance = getProcessAPI().startProcess(userId, processDef.getId());\n            var s1 = waitForFlowNodeInFailedState(processInstance, \"S1\");\n            // then we must have context for exception, even if it was logged outside of the initial throwing scope\n            checkLogEntryContains(Map.of(MDCConstants.PROCESS_INSTANCE_ID, Long.toString(processInstance.getId()),\n                    MDCConstants.PROCESS_DEFINITION_ID, Long.toString(processDef.getId()),\n                    MDCConstants.FLOW_NODE_INSTANCE_ID, Long.toString(s1.getId()),\n                    \"Expected fail\", \"OK\"));\n\n        } finally {\n            disableAndDeleteProcess(processDef);\n        }\n\n    }\n\n    @Test\n    public void executeProcessAndInspectLogs() throws Exception {\n        var designProcessDef = BuildTestUtil.buildProcessDefinitionWithHumanAndAutomaticSteps(List.of(\"H1\", \"S2\", \"H3\"),\n                List.of(true, false, true));\n        ((DesignProcessDefinitionImpl) designProcessDef).setStringIndex(1, \"i1\",\n                new ExpressionBuilder().createConstantStringExpression(\"a\"));\n        for (var act : designProcessDef.getFlowElementContainer().getActivities()) {\n            switch (act.getName()) {\n                case \"S2\":\n                    // make S2 last a bit\n                    var script = \"\"\"\n                            Thread.sleep(500)\n                            return 'b'\n                            \"\"\";\n                    Expression lastingExpr = new ExpressionBuilder().createGroovyScriptExpression(\"script\", script,\n                            String.class.getName());\n                    Operation operation = new OperationBuilder().createSetStringIndexOperation(1, lastingExpr);\n                    act.getOperations().add(operation);\n                    break;\n                default:\n                    break;\n            }\n        }\n        var user = createUser(USERNAME, PASSWORD);\n        var processDef = deployAndEnableProcessWithActor(designProcessDef, BuildTestUtil.ACTOR_NAME, user);\n        long userId = user.getId();\n        long substituteId = getSession().getUserId();\n\n        // set debug level to get execution logs on service tasks and process end\n        final Logger exeLogger = (Logger) LoggerFactory.getLogger(ProcessExecutorImpl.class);\n        Level oldExeLogLevel = exeLogger.getLevel();\n        final Logger apiLogger = (Logger) LoggerFactory.getLogger(ProcessAPIImpl.class);\n        Level oldApiLogLevel = apiLogger.getLevel();\n        try {\n            exeLogger.setLevel(Level.DEBUG);\n            apiLogger.setLevel(Level.INFO);\n\n            // when\n            systemOutRule.clearLog();\n            var processInstance = getProcessAPI().startProcess(userId, processDef.getId());\n            // then\n            checkLogEntryContains(Map.of(MDCConstants.PROCESS_INSTANCE_ID, Long.toString(processInstance.getId()),\n                    MDCConstants.PROCESS_DEFINITION_ID, Long.toString(processDef.getId()),\n                    MDCConstants.USER_ID, Long.toString(userId),\n                    MDCConstants.SUBSTITUTE_USER_ID, Long.toString(substituteId)));\n\n            // when\n            var h1 = waitForUserTaskAndGetIt(processInstance, \"H1\");\n            systemOutRule.clearLog();\n            getProcessAPI().assignAndExecuteUserTask(userId, h1.getId(), Collections.emptyMap());\n            // then\n            checkLogEntryContains(Map.of(MDCConstants.PROCESS_INSTANCE_ID, Long.toString(processInstance.getId()),\n                    MDCConstants.PROCESS_DEFINITION_ID, Long.toString(processDef.getId()),\n                    MDCConstants.FLOW_NODE_INSTANCE_ID, Long.toString(h1.getId()),\n                    MDCConstants.USER_ID, Long.toString(userId),\n                    MDCConstants.SUBSTITUTE_USER_ID, Long.toString(substituteId)));\n\n            // when\n            systemOutRule.clearLog();\n            // give S2 the required time to process\n            var h3 = waitForUserTaskAndGetIt(processInstance, \"H3\");\n            var s2Id = waitForFlowNodeInState(processInstance, \"S2\", TestStates.NORMAL_FINAL, false);\n            // then\n            checkLogEntryContains(Map.of(MDCConstants.PROCESS_INSTANCE_ID, Long.toString(processInstance.getId()),\n                    MDCConstants.PROCESS_DEFINITION_ID, Long.toString(processDef.getId()),\n                    MDCConstants.FLOW_NODE_INSTANCE_ID, Long.toString(s2Id)));\n\n            //when\n            systemOutRule.clearLog();\n            getProcessAPI().assignAndExecuteUserTask(userId, h3.getId(), Collections.emptyMap());\n            // then\n            checkLogEntryContains(Map.of(MDCConstants.PROCESS_INSTANCE_ID, Long.toString(processInstance.getId()),\n                    MDCConstants.PROCESS_DEFINITION_ID, Long.toString(processDef.getId()),\n                    MDCConstants.FLOW_NODE_INSTANCE_ID, Long.toString(h3.getId()),\n                    MDCConstants.USER_ID, Long.toString(userId),\n                    MDCConstants.SUBSTITUTE_USER_ID, Long.toString(substituteId)));\n\n            // when\n            waitForProcessToFinish(processInstance);\n            // then\n            checkLogEntryContains(Map.of(MDCConstants.PROCESS_INSTANCE_ID, Long.toString(processInstance.getId()),\n                    MDCConstants.PROCESS_DEFINITION_ID, Long.toString(processDef.getId())),\n                    // distinguish process end from human task...\n                    Collections.singletonList(MDCConstants.FLOW_NODE_INSTANCE_ID));\n\n        } finally {\n            disableAndDeleteProcess(processDef);\n            // restore old log level\n            exeLogger.setLevel(oldExeLogLevel);\n            apiLogger.setLevel(oldApiLogLevel);\n        }\n\n    }\n\n    /**\n     * Check there is a line in the log which contain context variables\n     *\n     * @param context context variables to check\n     */\n    private void checkLogEntryContains(Map<String, String> context) {\n        var log = systemOutRule.getLogWithNormalizedLineSeparator();\n        LogITUtil.checkLogEntryContains(log, context);\n    }\n\n    /**\n     * Check there is a line in the log which contain context variables\n     *\n     * @param context context variables to check\n     * @param forbiddenKeys keys that must not appear in the context\n     */\n    private void checkLogEntryContains(Map<String, String> context, Collection<String> forbiddenKeys) {\n        var log = systemOutRule.getLogWithNormalizedLineSeparator();\n        LogITUtil.checkLogEntryContains(log, context, forbiddenKeys);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/operation/OperationIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.operation;\n\nimport static java.util.Collections.singletonList;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map.Entry;\n\nimport org.assertj.core.api.SoftAssertions;\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.contract.FileInputValue;\nimport org.bonitasoft.engine.bpm.data.DataInstance;\nimport org.bonitasoft.engine.bpm.document.DocumentValue;\nimport org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.AutomaticTaskDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.expression.ExpressionConstants;\nimport org.bonitasoft.engine.xml.DocumentManager;\nimport org.custommonkey.xmlunit.XMLUnit;\nimport org.junit.Test;\nimport org.w3c.dom.Document;\nimport org.w3c.dom.Node;\n\n/**\n * @author Baptiste Mesta\n */\npublic class OperationIT extends TestWithUser {\n\n    @Test\n    public void executeStringOperationOnData() throws Exception {\n        createAndExecuteProcessWithOperations(\"executeStringOperationOnData\", singletonList(\"myData1\"),\n                singletonList(String.class.getName()),\n                singletonList(new LeftOperandBuilder().createNewInstance().setName(\"myData1\").done()),\n                singletonList(OperatorType.ASSIGNMENT), singletonList(\"=\"),\n                singletonList(new ExpressionBuilder().createConstantStringExpression(\"val1\")),\n                singletonList(new ExpressionBuilder().createConstantStringExpression(\"val2\")),\n                singletonList(\"val1\"),\n                singletonList(\"val2\"));\n    }\n\n    @Test\n    public void executeBooleanOperationOnData() throws Exception {\n        createAndExecuteProcessWithOperations(\"executeBooleanOperationOnData\", singletonList(\"myData1\"),\n                singletonList(Boolean.class.getName()),\n                singletonList(new LeftOperandBuilder().createNewInstance().setName(\"myData1\").done()),\n                singletonList(OperatorType.ASSIGNMENT), singletonList(\"=\"),\n                singletonList(new ExpressionBuilder().createConstantBooleanExpression(true)),\n                singletonList(new ExpressionBuilder().createConstantBooleanExpression(false)),\n                singletonList(true),\n                singletonList(false));\n    }\n\n    @Test\n    public void executeStringIndexOperation() throws Exception {\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"procWithStringIndexes\", \"1.0\");\n        final String actorName = \"doctor\";\n        designProcessDefinition.addActor(actorName).addDescription(\"The doc'\");\n        designProcessDefinition.addUserTask(\"step1\", actorName);\n        designProcessDefinition.addUserTask(\"step3\", actorName);\n        final AutomaticTaskDefinitionBuilder addAutomaticTask = designProcessDefinition.addAutomaticTask(\"step2\");\n        addAutomaticTask.addOperation(new OperationBuilder().createSetStringIndexOperation(1,\n                new ExpressionBuilder().createConstantStringExpression(\"newValue1\")));\n        addAutomaticTask.addOperation(new OperationBuilder().createSetStringIndexOperation(2,\n                new ExpressionBuilder().createConstantStringExpression(\"newValue2\")));\n        addAutomaticTask.addOperation(new OperationBuilder().createSetStringIndexOperation(3,\n                new ExpressionBuilder().createConstantStringExpression(\"newValue3\")));\n        addAutomaticTask.addOperation(new OperationBuilder().createSetStringIndexOperation(4,\n                new ExpressionBuilder().createConstantStringExpression(\"newValue4\")));\n        addAutomaticTask.addOperation(new OperationBuilder().createSetStringIndexOperation(5,\n                new ExpressionBuilder().createConstantStringExpression(\"newValue5\")));\n        designProcessDefinition.addTransition(\"step1\", \"step2\");\n        designProcessDefinition.addTransition(\"step2\", \"step3\");\n        designProcessDefinition.setStringIndex(1, \"label1\",\n                new ExpressionBuilder().createConstantStringExpression(\"value1\"));\n        designProcessDefinition.setStringIndex(2, \"label2\",\n                new ExpressionBuilder().createConstantStringExpression(\"value2\"));\n        designProcessDefinition.setStringIndex(3, \"label3\",\n                new ExpressionBuilder().createConstantStringExpression(\"value3\"));\n        designProcessDefinition.setStringIndex(4, \"label4\",\n                new ExpressionBuilder().createConstantStringExpression(\"value4\"));\n        designProcessDefinition.setStringIndex(5, \"label5\",\n                new ExpressionBuilder().createConstantStringExpression(\"value5\"));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(),\n                actorName, user);\n\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(startProcess, \"step1\");\n        ProcessInstance processInstance = getProcessAPI().getProcessInstance(startProcess.getId());\n        assertEquals(\"value1\", processInstance.getStringIndex1());\n        assertEquals(\"value2\", processInstance.getStringIndex2());\n        assertEquals(\"value3\", processInstance.getStringIndex3());\n        assertEquals(\"value4\", processInstance.getStringIndex4());\n        assertEquals(\"value5\", processInstance.getStringIndex5());\n        assignAndExecuteStep(step1Id, user);\n        waitForUserTask(startProcess, \"step3\");\n        processInstance = getProcessAPI().getProcessInstance(startProcess.getId());\n        assertEquals(\"newValue1\", processInstance.getStringIndex1());\n        assertEquals(\"newValue2\", processInstance.getStringIndex2());\n        assertEquals(\"newValue3\", processInstance.getStringIndex3());\n        assertEquals(\"newValue4\", processInstance.getStringIndex4());\n        assertEquals(\"newValue5\", processInstance.getStringIndex5());\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeStringIndexOperationUsingData() throws Exception {\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"procWithStringIndexes\", \"1.0\");\n        final String actorName = \"doctor\";\n        designProcessDefinition.addData(\"baseData\", String.class.getName(),\n                new ExpressionBuilder().createConstantStringExpression(\"baseValue\"));\n        designProcessDefinition.addActor(actorName).addDescription(\"The doc'\");\n        designProcessDefinition.addUserTask(\"step1\", actorName);\n        designProcessDefinition.addUserTask(\"step3\", actorName);\n        final AutomaticTaskDefinitionBuilder addAutomaticTask = designProcessDefinition.addAutomaticTask(\"step2\");\n        addAutomaticTask.addOperation(new LeftOperandBuilder().createDataLeftOperand(\"baseData\"),\n                OperatorType.ASSIGNMENT, null, null,\n                new ExpressionBuilder().createConstantStringExpression(\"changedData\"));\n        addAutomaticTask.addOperation(new OperationBuilder().createSetStringIndexOperation(1,\n                new ExpressionBuilder().createDataExpression(\"baseData\", String.class.getName())));\n        designProcessDefinition.addTransition(\"step1\", \"step2\");\n        designProcessDefinition.addTransition(\"step2\", \"step3\");\n        designProcessDefinition.setStringIndex(1, \"label1\",\n                new ExpressionBuilder().createDataExpression(\"baseData\", String.class.getName()));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(),\n                actorName, user);\n\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(startProcess, \"step1\");\n        ProcessInstance processInstance = getProcessAPI().getProcessInstance(startProcess.getId());\n        assertEquals(\"baseValue\", processInstance.getStringIndex1());\n        assignAndExecuteStep(step1Id, user);\n        waitForUserTask(startProcess, \"step3\");\n        processInstance = getProcessAPI().getProcessInstance(startProcess.getId());\n        assertEquals(\"changedData\", processInstance.getStringIndex1());\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test(expected = InvalidProcessDefinitionException.class)\n    public void executeOperationWithNoExpression() throws Exception {\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"procWithStringIndexes\", \"1.0\");\n        final String actorName = \"doctor\";\n        designProcessDefinition.addActor(actorName).addDescription(\"The doc'\");\n        designProcessDefinition.addUserTask(\"step1\", actorName);\n        designProcessDefinition.addUserTask(\"step3\", actorName);\n        final AutomaticTaskDefinitionBuilder addAutomaticTask = designProcessDefinition.addAutomaticTask(\"step2\");\n        addAutomaticTask.addOperation(new OperationBuilder().createSetStringIndexOperation(1, null));\n        designProcessDefinition.addTransition(\"step1\", \"step2\");\n        designProcessDefinition.addTransition(\"step2\", \"step3\");\n        designProcessDefinition.done();\n    }\n\n    @Test\n    public void executeIntegerOperationOnData() throws Exception {\n        createAndExecuteProcessWithOperations(\"executeIntegerOperationOnData\", singletonList(\"myData12\"),\n                singletonList(Integer.class.getName()),\n                singletonList(new LeftOperandBuilder().createNewInstance().setName(\"myData12\").done()),\n                singletonList(OperatorType.ASSIGNMENT), singletonList(\"=\"),\n                singletonList(new ExpressionBuilder().createConstantIntegerExpression(1)),\n                singletonList(new ExpressionBuilder().createConstantIntegerExpression(2)),\n                singletonList(1),\n                singletonList(2));\n    }\n\n    @Test\n    public void executeLongOperationOnData() throws Exception {\n        createAndExecuteProcessWithOperations(\"executeLongOperationOnData\", singletonList(\"myData12\"),\n                singletonList(Long.class.getName()),\n                singletonList(new LeftOperandBuilder().createNewInstance().setName(\"myData12\").done()),\n                singletonList(OperatorType.ASSIGNMENT), singletonList(\"=\"),\n                singletonList(new ExpressionBuilder().createConstantLongExpression(1234567891234567891L)),\n                singletonList(new ExpressionBuilder().createConstantLongExpression(1234567891234567892L)),\n                singletonList(1234567891234567891L),\n                singletonList(1234567891234567892L));\n    }\n\n    @Test\n    public void executeMultipleOperations() throws Exception {\n        createAndExecuteProcessWithOperations(\"executeMultipleOperations\",\n                Arrays.asList(\"myData1\", \"myData2\", \"myData3\"), Arrays.asList(\n                        String.class.getName(), Boolean.class.getName(), Long.class.getName()),\n                Arrays.asList(\n                        new LeftOperandBuilder().createNewInstance().setName(\"myData1\").done(),\n                        new LeftOperandBuilder().createNewInstance().setName(\"myData2\").done(),\n                        new LeftOperandBuilder().createNewInstance().setName(\"myData3\").done()),\n                Arrays.asList(OperatorType.ASSIGNMENT, OperatorType.ASSIGNMENT,\n                        OperatorType.ASSIGNMENT),\n                Arrays.asList(\"=\", \"=\", \"=\"),\n                Arrays.asList(new ExpressionBuilder().createConstantStringExpression(\"test1\"),\n                        new ExpressionBuilder().createConstantBooleanExpression(false),\n                        new ExpressionBuilder().createConstantLongExpression(1234567891234567891L)),\n                Arrays.asList(new ExpressionBuilder().createConstantStringExpression(\"test2\"),\n                        new ExpressionBuilder().createConstantBooleanExpression(true),\n                        new ExpressionBuilder().createConstantLongExpression(1234567891234567892L)),\n                Arrays.asList(\"test1\", false,\n                        1234567891234567891L),\n                Arrays.asList(\"test2\", true, 1234567891234567892L));\n    }\n\n    @Test\n    public void executeMultipleOperationsWithSameData() throws Exception {\n        createAndExecuteProcessWithOperations(\"executeMultipleOperationsWithSameData\",\n                Arrays.asList(\"myData1\", \"myData2\", \"myData3\", \"myData1\"),\n                Arrays.asList(String.class.getName(), Boolean.class.getName(), Long.class.getName(),\n                        String.class.getName()),\n                Arrays.asList(\n                        new LeftOperandBuilder().createNewInstance().setName(\"myData1\").done(),\n                        new LeftOperandBuilder().createNewInstance().setName(\"myData2\")\n                                .done(),\n                        new LeftOperandBuilder().createNewInstance().setName(\"myData3\").done(),\n                        new LeftOperandBuilder().createNewInstance()\n                                .setName(\"myData1\").done()),\n                Arrays.asList(OperatorType.ASSIGNMENT, OperatorType.ASSIGNMENT, OperatorType.ASSIGNMENT,\n                        OperatorType.ASSIGNMENT),\n                Arrays.asList(\"=\", \"=\", \"=\", \"=\"), Arrays.asList(\n                        new ExpressionBuilder().createConstantStringExpression(\"test1\"),\n                        new ExpressionBuilder().createConstantBooleanExpression(false),\n                        new ExpressionBuilder().createConstantLongExpression(1234567891234567891L),\n                        new ExpressionBuilder().createConstantStringExpression(\"test1\")),\n                Arrays.asList(\n                        new ExpressionBuilder().createConstantStringExpression(\"test2\"),\n                        new ExpressionBuilder().createConstantBooleanExpression(true),\n                        new ExpressionBuilder().createConstantLongExpression(1234567891234567892L),\n                        new ExpressionBuilder().createConstantStringExpression(\"test3\")),\n                Arrays.asList(\"test1\", false,\n                        1234567891234567891L, \"test1\"),\n                Arrays.asList(\"test3\", true, 1234567891234567892L,\n                        \"test3\"));\n    }\n\n    @Test\n    public void should_set_documents_with_operations() throws Exception {\n        final String actorName = \"Document supplier\";\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"should_set_documents_with_operations\", \"1.0\");\n        processDefinitionBuilder.addActor(actorName).addDescription(\"Process documents\");\n        processDefinitionBuilder.addDocumentDefinition(\"doc1\");\n        processDefinitionBuilder.addDocumentDefinition(\"doc2\").addDescription(\"Will receive the content of doc1\");\n        processDefinitionBuilder.addDocumentListDefinition(\"docList1\");\n        processDefinitionBuilder.addDocumentListDefinition(\"docList2\")\n                .addDescription(\"Will receive the content of docList1\");\n\n        processDefinitionBuilder.addUserTask(\"step0\", actorName);\n        processDefinitionBuilder\n                .addAutomaticTask(\"step1\")\n                .addOperation(new OperationBuilder().createSetDocumentList(\"docList1\",\n                        new ExpressionBuilder().createGroovyScriptExpression(\"setDocumentList1()\",\n                                \"[new org.bonitasoft.engine.bpm.contract.FileInputValue('doc1.txt', 'content of doc1'.getBytes('UTF-8'))\"\n                                        + \",new org.bonitasoft.engine.bpm.contract.FileInputValue('doc2.txt', 'content of doc2'.getBytes('UTF-8'))]\",\n                                List.class.getName())))\n                .addOperation(new OperationBuilder().createSetDocumentList(\"docList2\",\n                        new ExpressionBuilder().createGroovyScriptExpression(\"setDocList1ValueToDocList2()\",\n                                \"return docList1\",\n                                List.class.getName(),\n                                // mimic what we have in BS-18741, force usage of document list reference as this is done\n                                // in the Studio (automatic dependencies resolution)\n                                new ExpressionBuilder().createDocumentListExpression(\"docList1\"))))\n                .addOperation(new OperationBuilder().createSetDocument(\"doc1\",\n                        new ExpressionBuilder().createGroovyScriptExpression(\"setDoc1()\",\n                                \"new org.bonitasoft.engine.bpm.contract.FileInputValue('doc1.txt', 'content of doc1'.getBytes('UTF-8'))\",\n                                FileInputValue.class.getName())))\n                .addOperation(new OperationBuilder().createSetDocument(\"doc2\",\n                        new ExpressionBuilder().createGroovyScriptExpression(\"setDoc1ValueToDoc2()\",\n                                \"doc1\",\n                                DocumentValue.class.getName()\n                                // mimic what we have in BS-18741, force usage of document reference as this is done\n                                // in the Studio (automatic dependencies resolution)\n                                , new ExpressionBuilder().createDocumentReferenceExpression(\"doc1\"))));\n\n        processDefinitionBuilder.addUserTask(\"step2\", actorName);\n        processDefinitionBuilder.addTransition(\"step0\", \"step1\");\n        processDefinitionBuilder.addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),\n                actorName, user);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(processInstance, \"step0\", user);\n        waitForUserTask(processInstance, \"step2\");\n\n        SoftAssertions softly = new SoftAssertions();\n\n        final List<org.bonitasoft.engine.bpm.document.Document> docList1AfterExecution = getProcessAPI()\n                .getDocumentList(processInstance.getId(), \"docList1\", 0, 100);\n        softly.assertThat(docList1AfterExecution).as(\"docList1 after execution\")\n                .hasSize(2)\n                .extracting(org.bonitasoft.engine.bpm.document.Document::getContentFileName)\n                .containsOnly(\"doc1.txt\", \"doc2.txt\");\n        final List<org.bonitasoft.engine.bpm.document.Document> docList2AfterExecution = getProcessAPI()\n                .getDocumentList(processInstance.getId(), \"docList2\", 0, 100);\n        softly.assertThat(docList2AfterExecution).as(\"docList2 after execution\")\n                .hasSize(2)\n                .extracting(org.bonitasoft.engine.bpm.document.Document::getContentFileName)\n                .containsOnly(\"doc1.txt\", \"doc2.txt\");\n\n        final org.bonitasoft.engine.bpm.document.Document doc1AfterExecution = getProcessAPI()\n                .getLastDocument(processInstance.getId(), \"doc1\");\n        softly.assertThat(doc1AfterExecution.hasContent()).isTrue();\n        softly.assertThat(doc1AfterExecution.getContentFileName()).isEqualTo(\"doc1.txt\");\n\n        final org.bonitasoft.engine.bpm.document.Document doc2AfterExecution = getProcessAPI()\n                .getLastDocument(processInstance.getId(), \"doc2\");\n        softly.assertThat(doc2AfterExecution.hasContent()).isTrue();\n        softly.assertThat(doc2AfterExecution.getContentFileName()).isEqualTo(\"doc1.txt\");\n\n        disableAndDeleteProcess(processDefinition);\n        softly.assertAll();\n    }\n\n    protected void createAndExecuteProcessWithOperations(final String procName, final List<String> dataName,\n            final List<String> className,\n            final List<LeftOperand> leftOperand, final List<OperatorType> assignment, final List<String> operator,\n            final List<Expression> dataDefaultValue,\n            final List<Expression> expression, final List<Object> valueBefore, final List<Object> valueAfter)\n            throws Exception {\n        final String delivery = \"Delivery men\";\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(procName, \"1.0\");\n        final HashSet<String> dataSet = new HashSet<>(dataName.size());\n        for (int i = 0; i < dataName.size(); i++) {\n            final String name = dataName.get(i);\n            if (!dataSet.contains(name)) {\n                designProcessDefinition.addData(name, className.get(i), dataDefaultValue.get(i));\n                dataSet.add(name);\n            }\n        }\n        designProcessDefinition.addActor(delivery).addDescription(\"Delivery all day and night long\");\n        designProcessDefinition.addUserTask(\"step0\", delivery);\n        final AutomaticTaskDefinitionBuilder addAutomaticTask = designProcessDefinition.addAutomaticTask(\"step1\");\n        for (int i = 0; i < leftOperand.size(); i++) {\n            // No Java operation, so empty string passed:\n            addAutomaticTask.addOperation(leftOperand.get(i), assignment.get(i), operator.get(i), \"\",\n                    expression.get(i));\n        }\n        designProcessDefinition.addUserTask(\"step2\", delivery);\n        designProcessDefinition.addTransition(\"step0\", \"step1\");\n        designProcessDefinition.addTransition(\"step1\", \"step2\");\n        designProcessDefinition.setStringIndex(1, \"label1\",\n                new ExpressionBuilder().createConstantStringExpression(\"value1\"));\n        designProcessDefinition.setStringIndex(2, \"label2\",\n                new ExpressionBuilder().createConstantStringExpression(\"value2\"));\n        designProcessDefinition.setStringIndex(3, \"label3\",\n                new ExpressionBuilder().createConstantStringExpression(\"value3\"));\n        designProcessDefinition.setStringIndex(4, \"label4\",\n                new ExpressionBuilder().createConstantStringExpression(\"value4\"));\n        designProcessDefinition.setStringIndex(5, \"label5\",\n                new ExpressionBuilder().createConstantStringExpression(\"value5\"));\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(),\n                delivery, user);\n\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId());\n        final HashMap<String, Integer> inverDataOrder = new HashMap<>(dataName.size());\n        for (int i = dataName.size() - 1; i >= 0; i--) {\n            final String name = dataName.get(i);\n            if (!inverDataOrder.containsKey(name)) {\n                inverDataOrder.put(name, i);\n            }\n        }\n        for (final Entry<String, Integer> dataIndex : inverDataOrder.entrySet()) {\n            assertEquals(\"before execution of operation \" + dataIndex.getKey(), valueBefore.get(dataIndex.getValue()),\n                    getProcessAPI().getProcessDataInstance(dataIndex.getKey(), startProcess.getId()).getValue());\n        }\n\n        waitForUserTaskAndExecuteIt(startProcess, \"step0\", user);\n        waitForUserTask(startProcess, \"step2\");\n\n        for (final Entry<String, Integer> dataIndex : inverDataOrder.entrySet()) {\n            assertEquals(\"after execution of operation \" + dataIndex.getKey(), valueAfter.get(dataIndex.getValue()),\n                    getProcessAPI().getProcessDataInstance(dataIndex.getKey(), startProcess.getId()).getValue());\n        }\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void updateAnAttributeOfAnXMLNodeOfADataUsingXPath() throws Exception {\n        final String defaultValue = \"<root name=\\\"before\\\"/>\";\n        final String updatedValue = \"<root name=\\\"after\\\"/>\";\n        final String xPathExpression = \"/root/@name\";\n        final String updatedContent = \"after\";\n        executeProcessAndUpdateData(defaultValue, xPathExpression, updatedContent, updatedValue);\n    }\n\n    @Test\n    public void setTheContentOfAnXMLNodeOfADataUsingXPath() throws Exception {\n        final String defaultValue = \"<root />\";\n        final String updatedValue = \"<root>after</root>\";\n        final String xPathExpression = \"/root/text()\";\n        final String updatedContent = \"after\";\n        executeProcessAndUpdateData(defaultValue, xPathExpression, updatedContent, updatedValue);\n    }\n\n    @Test\n    public void updateTheContentOfAnXMLNodeOfADataUsingXPath() throws Exception {\n        final String defaultValue = \"<root>before</root>\";\n        final String updatedValue = \"<root>after</root>\";\n        final String xPathExpression = \"/root/text()\";\n        final String updatedContent = \"after\";\n        executeProcessAndUpdateData(defaultValue, xPathExpression, updatedContent, updatedValue);\n    }\n\n    @Test\n    public void updateTheContentOfASubNodeOfAnXMLNodeOfADataUsingXPath() throws Exception {\n        final String defaultValue = \"<node><subnode>before</subnode></node>\";\n        final String updatedValue = \"<node><subnode>after</subnode></node>\";\n        final String xPathExpression = \"/node/subnode[1]/text()\";\n        final String updatedContent = \"after\";\n        executeProcessAndUpdateData(defaultValue, xPathExpression, updatedContent, updatedValue);\n    }\n\n    @Test\n    public void removeAndCreateANewNodeOfAnXMLNodeOfADataUsingXPath() throws Exception {\n        final String defaultValue = \"<node><before/></node>\";\n        final String updatedValue = \"<node><after/></node>\";\n        final String xPathExpression = \"/node/before\";\n        final String updatedContent = \"after\";\n        final ExpressionBuilder expressionBuilder = new ExpressionBuilder();\n        final Expression rightOperand = expressionBuilder.createGroovyScriptExpression(\n                \"removeAndCreateANewNodeOfAnXMLNodeOfADataUsingXPath\",\n                \"import javax.xml.parsers.DocumentBuilder;\"\n                        + \"import javax.xml.parsers.DocumentBuilderFactory;import org.w3c.dom.Node;\"\n                        + \"DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); return builder.newDocument().createElement(\\\"\"\n                        + updatedContent + \"\\\");\",\n                Node.class.getName());\n        executeProcessAndUpdateData(defaultValue, xPathExpression, rightOperand, updatedValue);\n    }\n\n    @Test\n    public void updateTheContentOfAnXMLNodeOfADataWithIntegerValueUsingXPath() throws Exception {\n        updateAttributeAndContentWithNumericValueUsingXPath(\n                new ExpressionBuilder().createConstantIntegerExpression(123), \"123\");\n    }\n\n    private void updateAttributeAndContentWithNumericValueUsingXPath(final Expression rightOperand, final String result)\n            throws Exception {\n        executeProcessAndUpdateData(Arrays.asList(\"<root>before</root>\", \"<root name=\\\"before\\\"/>\"),\n                Arrays.asList(\"/root/text()\", \"/root/@name\"),\n                Arrays.asList(rightOperand, rightOperand),\n                Arrays.asList(\"<root>\" + result + \"</root>\", \"<root name=\\\"\" + result + \"\\\"/>\"));\n    }\n\n    @Test\n    public void updateTheContentOfAnXMLNodeOfADataWithBooleanValueUsingXPath() throws Exception {\n        updateAttributeAndContentWithNumericValueUsingXPath(\n                new ExpressionBuilder().createConstantBooleanExpression(true), \"true\");\n    }\n\n    @Test\n    public void updateTheContentOfAnXMLNodeOfADataWithLongValueUsingXPath() throws Exception {\n        updateAttributeAndContentWithNumericValueUsingXPath(new ExpressionBuilder().createConstantLongExpression(123),\n                \"123\");\n    }\n\n    @Test\n    public void updateTheContentOfAnXMLNodeOfADataWithDoubleValueUsingXPath() throws Exception {\n        updateAttributeAndContentWithNumericValueUsingXPath(\n                new ExpressionBuilder().createConstantDoubleExpression(123.123), \"123.123\");\n    }\n\n    private void executeProcessAndUpdateData(final String defaultValue, final String xPathExpression,\n            final String updatedContent, final String updatedValue)\n            throws Exception {\n        final ExpressionBuilder expressionBuilder = new ExpressionBuilder();\n        final Expression rightOperand = expressionBuilder.createConstantStringExpression(updatedContent);\n        executeProcessAndUpdateData(defaultValue, xPathExpression, rightOperand, updatedValue);\n    }\n\n    private void executeProcessAndUpdateData(final List<String> defaultValues, final List<String> xPathExpressions,\n            final List<Expression> rightOperands,\n            final List<String> updatedValues) throws Exception {\n        final ExpressionBuilder expressionBuilder = new ExpressionBuilder();\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"XPathEvaluation\", \"1.0\");\n        final String variableBaseName = \"var\";\n        int i = 0;\n        designProcessDefinition.addActor(\"Workers\");\n        final UserTaskDefinitionBuilder userTask = designProcessDefinition.addAutomaticTask(\"start\")\n                .addUserTask(\"step1\", \"Workers\");\n        final Iterator<String> xPathExpression = xPathExpressions.iterator();\n        final Iterator<Expression> rightOperand = rightOperands.iterator();\n        for (final String defaultValue : defaultValues) {\n            final String variableName = variableBaseName + i;\n            i++;\n            final Expression defaultExpression = expressionBuilder.createConstantStringExpression(defaultValue);\n            designProcessDefinition.addXMLData(variableName, defaultExpression).setNamespace(\"http://www.w3c.org\");\n            userTask.addOperation(new OperationBuilder().createXPathOperation(variableName, xPathExpression.next(),\n                    rightOperand.next()));\n        }\n\n        designProcessDefinition.addUserTask(\"step2\", \"Workers\").addTransition(\"start\", \"step1\").addTransition(\"step1\",\n                \"step2\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(),\n                \"Workers\", user);\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(startProcess, \"step1\");\n\n        i = 0;\n        for (final String defaultValue : defaultValues) {\n            final String variableName = variableBaseName + i;\n            i++;\n            assertEquals(defaultValue,\n                    getProcessAPI().getProcessDataInstance(variableName, startProcess.getId()).getValue());\n        }\n        assignAndExecuteStep(step1Id, user);\n\n        waitForUserTask(startProcess, \"step2\");\n        i = 0;\n        for (final String updatedValue : updatedValues) {\n            final String variableName = variableBaseName + i;\n            i++;\n            final Document expected = DocumentManager.generateDocument(updatedValue);\n            final Document actual = DocumentManager.generateDocument(\n                    (String) getProcessAPI().getProcessDataInstance(variableName, startProcess.getId())\n                            .getValue());\n\n            XMLUnit.setIgnoreWhitespace(true);\n            assertTrue(XMLUnit.compareXML(expected, actual).identical());\n        }\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    private void executeProcessAndUpdateData(final String defaultValue, final String xPathExpression,\n            final Expression rightOperand, final String updatedValue) throws Exception {\n        executeProcessAndUpdateData(singletonList(defaultValue), singletonList(xPathExpression),\n                singletonList(rightOperand), singletonList(updatedValue));\n    }\n\n    @Test\n    public void initializeAVariableUsingAPIAccessor() throws Exception {\n        final ExpressionBuilder expressionBuilder = new ExpressionBuilder();\n        final Expression apiAccessor = expressionBuilder.createAPIAccessorExpression();\n        final List<Expression> dependencies = new ArrayList<>(1);\n        dependencies.add(apiAccessor);\n        final Expression defaultExpression = expressionBuilder.createGroovyScriptExpression(\n                \"initializeAVariableUsingAPIAccessor\",\n                \"apiAccessor.getIdentityAPI().getNumberOfUsers()\", Long.class.getName(), dependencies);\n\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"APIAccessorInGroovyScript\", \"1.0\");\n        designProcessDefinition.addLongData(\"users\", defaultExpression);\n        designProcessDefinition.addActor(\"Workers\").addUserTask(\"step1\", \"Workers\");\n        designProcessDefinition.addAutomaticTask(\"start\").addTransition(\"start\", \"step1\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(),\n                \"Workers\", user);\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(startProcess, \"step1\");\n\n        final long numberOfUsers = getIdentityAPI().getNumberOfUsers();\n        assertEquals(numberOfUsers, getProcessAPI().getProcessDataInstance(\"users\", startProcess.getId()).getValue());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void updateAVariableUsingLoggedUser() throws Exception {\n        final LeftOperandBuilder leftOperandBuilder = new LeftOperandBuilder();\n        final LeftOperand leftOperand = leftOperandBuilder.createDataLeftOperand(\"userId\");\n\n        final ExpressionBuilder expressionBuilder = new ExpressionBuilder();\n        final Expression loggedUser = expressionBuilder.createEngineConstant(ExpressionConstants.LOGGED_USER_ID);\n\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"APIAccessorInGroovyScript\", \"1.0\");\n        designProcessDefinition.addLongData(\"userId\", new ExpressionBuilder().createConstantLongExpression(123L));\n        designProcessDefinition.addActor(\"Workers\").addUserTask(\"step1\", \"Workers\").addOperation(leftOperand,\n                OperatorType.ASSIGNMENT, \"=\", null, loggedUser);\n        designProcessDefinition.addAutomaticTask(\"start\").addUserTask(\"step2\", \"Workers\")\n                .addTransition(\"start\", \"step1\").addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(),\n                \"Workers\", user);\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(startProcess, \"step1\", user);\n        waitForUserTask(startProcess, \"step2\");\n        assertEquals(user.getId(), getProcessAPI().getProcessDataInstance(\"userId\", startProcess.getId()).getValue());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void updateAVariableUsingAPIAccessor() throws Exception {\n        final LeftOperandBuilder leftOperandBuilder = new LeftOperandBuilder();\n        final LeftOperand leftOperand = leftOperandBuilder.createNewInstance(\"users\").done();\n\n        final ExpressionBuilder expressionBuilder = new ExpressionBuilder();\n        final Expression apiAccessor = expressionBuilder.createAPIAccessorExpression();\n        final List<Expression> dependencies = new ArrayList<>(1);\n        dependencies.add(apiAccessor);\n        final Expression defaultExpression = expressionBuilder.createGroovyScriptExpression(\n                \"updateAVariableUsingAPIAccessor\",\n                \"apiAccessor.getIdentityAPI().getNumberOfUsers()\", Long.class.getName(), dependencies);\n\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"APIAccessorInGroovyScript\", \"1.0\");\n        designProcessDefinition.addLongData(\"users\", null);\n        designProcessDefinition.addActor(\"Workers\").addUserTask(\"step1\", \"Workers\")\n                .addOperation(leftOperand, OperatorType.ASSIGNMENT, \"=\", null, defaultExpression);\n        designProcessDefinition.addAutomaticTask(\"start\").addUserTask(\"step2\", \"Workers\")\n                .addTransition(\"start\", \"step1\").addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(),\n                \"Workers\", user);\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(startProcess, \"step1\", user);\n        waitForUserTask(startProcess, \"step2\");\n\n        final long numberOfUsers = getIdentityAPI().getNumberOfUsers();\n        assertEquals(numberOfUsers, getProcessAPI().getProcessDataInstance(\"users\", startProcess.getId()).getValue());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void takeTransitionUsingAPIAccessor() throws Exception {\n        final ExpressionBuilder expressionBuilder = new ExpressionBuilder();\n        final Expression apiAccessor = expressionBuilder.createAPIAccessorExpression();\n        final List<Expression> dependencies = new ArrayList<>(1);\n        dependencies.add(apiAccessor);\n        final Expression defaultExpression = expressionBuilder.createGroovyScriptExpression(\n                \"takeTransitionUsingAPIAccessor\",\n                \"apiAccessor.getIdentityAPI().getNumberOfUsers() < 100\", Boolean.class.getName(), dependencies);\n\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"APIAccessorInGroovyScript\", \"1.0\");\n        designProcessDefinition.addActor(\"Workers\").addUserTask(\"step1\", \"Workers\");\n        designProcessDefinition.addAutomaticTask(\"start\").addUserTask(\"step2\", \"Workers\")\n                .addTransition(\"start\", \"step1\", defaultExpression)\n                .addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(),\n                \"Workers\", user);\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(startProcess, \"step1\");\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void javaMethodOperationWithPrimitiveParameters() throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance(\n                \"javaMethodOperationWithPrimitiveParameters\",\n                String.valueOf(System.currentTimeMillis()));\n        final AutomaticTaskDefinitionBuilder task1Def = processDefinitionBuilder\n                .addActor(ACTOR_NAME)\n                .addData(\n                        \"myDatum\",\n                        \"java.lang.StringBuilder\",\n                        new ExpressionBuilder().createGroovyScriptExpression(\"myInitGroovyScript\",\n                                \"new java.lang.StringBuilder(\\\"_\\\")\",\n                                \"java.lang.StringBuilder\"))\n                .addAutomaticTask(\"step1\");\n        task1Def.addOperation(new OperationBuilder().createJavaMethodOperation(\"myDatum\", \"append\", \"int\",\n                new ExpressionBuilder().createConstantIntegerExpression(55)));\n        task1Def.addUserTask(\"step2\", ACTOR_NAME).addTransition(\"step1\", \"step2\");\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),\n                ACTOR_NAME, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long step2Id = waitForUserTask(processInstance, \"step2\");\n        final DataInstance activityDataInstance = getProcessAPI().getActivityDataInstance(\"myDatum\", step2Id);\n\n        assertEquals(StringBuilder.class, activityDataInstance.getValue().getClass());\n        assertEquals(\"_55\", activityDataInstance.getValue().toString());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeJavaOperationWithCustomType() throws Exception {\n        final BusinessArchiveBuilder builder = new BusinessArchiveBuilder().createNewBusinessArchive();\n        builder.addClasspathResource(getResource(\"/custom-0.1.jar.bak\", \"custom-0.1.jar\"));\n        builder.addClasspathResource(getResource(\"/org.bonitasoft.dfgdfg.bak\", \"org.bonitasoft.dfgdfg.jar\"));\n\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"ProcessWithCustomData\", \"1.0\");\n        designProcessDefinition.addData(\"adress\", \"org.bonitasoft.custom.Address\",\n                new ExpressionBuilder().createGroovyScriptExpression(\"create adress\",\n                        \"new org.bonitasoft.custom.Address(\\\"name1\\\",\\\"Rue ampère\\\",\\\"38000\\\",\\\"Grenoble\\\",\\\"France\\\")\",\n                        \"org.bonitasoft.custom.Address\"));\n        designProcessDefinition.addData(\"contact\", \"org.bonitasoft.dfgdfg.Contact\",\n                new ExpressionBuilder().createGroovyScriptExpression(\"create contact\",\n                        \"new org.bonitasoft.dfgdfg.Contact()\", \"org.bonitasoft.dfgdfg.Contact\"));\n        designProcessDefinition\n                .addActor(\"Workers\")\n                .addUserTask(\"step1\", \"Workers\")\n                .addOperation(\n                        new OperationBuilder().createJavaMethodOperation(\"adress\", \"setName\", String.class.getName(),\n                                new ExpressionBuilder().createConstantStringExpression(\"myAddress\")))\n                .addOperation(\n                        new OperationBuilder().createJavaMethodOperation(\"contact\", \"setSite\", Long.class.getName(),\n                                new ExpressionBuilder().createConstantLongExpression(12)));\n        designProcessDefinition.addAutomaticTask(\"start\").addUserTask(\"step2\", \"Workers\")\n                .addTransition(\"start\", \"step1\").addTransition(\"step1\", \"step2\");\n        builder.setProcessDefinition(designProcessDefinition.done());\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), \"Workers\", user);\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(startProcess, \"step1\", user);\n        waitForUserTask(startProcess, \"step2\");\n        disableAndDeleteProcess(processDefinition);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/page/PageAPIIT.java",
    "content": "/**\n * Copyright (C) 2015 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport static java.nio.charset.StandardCharsets.UTF_8;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.fail;\nimport static org.bonitasoft.engine.io.FileAndContentUtils.file;\nimport static org.bonitasoft.engine.io.FileAndContentUtils.zip;\nimport static org.bonitasoft.engine.page.PageAssert.assertThat;\nimport static org.junit.Assert.assertThrows;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.StringReader;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipInputStream;\n\nimport org.apache.commons.io.IOUtils;\nimport org.assertj.core.api.Assertions;\nimport org.bonitasoft.engine.CommonAPIIT;\nimport org.bonitasoft.engine.api.permission.APICallContext;\nimport org.bonitasoft.engine.application.ApplicationIT;\nimport org.bonitasoft.engine.business.application.ApplicationImportPolicy;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.InvalidPageTokenException;\nimport org.bonitasoft.engine.exception.InvalidPageZipMissingIndexException;\nimport org.bonitasoft.engine.exception.UpdatingWithInvalidPageZipContentException;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.io.IOUtil;\nimport org.bonitasoft.engine.profile.Profile;\nimport org.bonitasoft.engine.profile.ProfileSearchDescriptor;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.test.CommonTestUtil;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class PageAPIIT extends CommonAPIIT {\n\n    public static final long PROCESS_DEFINITION_ID = 5846L;\n\n    private static final String DISPLAY_NAME = \"My P\\u00e4ge\";\n\n    private static final String CONTENT_NAME = \"content.zip\";\n\n    private static final String PAGE_DESCRIPTION = \"page description\";\n\n    private static final String PAGE_NAME2 = \"custompage_page2\";\n\n    private static final String PAGE_NAME1 = \"custompage_page1\";\n\n    @Before\n    public void before() throws Exception {\n        loginWithTechnicalUser();\n        final SearchResult<Page> searchPages = getPageAPI()\n                .searchPages(new SearchOptionsBuilder(0, Integer.MAX_VALUE).done());\n        for (final Page page : searchPages.getResult()) {\n            if (!page.isProvided()) {\n                getPageAPI().deletePage(page.getId());\n            }\n        }\n    }\n\n    @After\n    public void after() throws Exception {\n        logout();\n    }\n\n    @Test\n    public void should_getPage_return_the_page() throws Exception {\n        // given\n        final String name = generateUniquePageName(0);\n        final byte[] pageContent = createTestPageContent(name, DISPLAY_NAME, PAGE_DESCRIPTION);\n        final Page page = getPageAPI().createPage(\n                new PageCreator(name, CONTENT_NAME).setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME)\n                        .setContentType(ContentType.FORM)\n                        .setProcessDefinitionId(PROCESS_DEFINITION_ID),\n                pageContent);\n\n        // when\n        final Page returnedPage = getPageAPI().getPage(page.getId());\n\n        // then\n        Assertions.assertThat(returnedPage).usingRecursiveComparison().isEqualTo(page);\n        PageAssert.assertThat(returnedPage)\n                .hasProcessDefinitionId(PROCESS_DEFINITION_ID)\n                .hasContentType(ContentType.FORM);\n    }\n\n    @Test\n    public void updatePage_should_set_provided_field_to_false_if_provided_pages_are_modified() throws Exception {\n        //given:\n        try (var is = PageAPIIT.class.getResourceAsStream(\"/provided_page_ready_to_update.zip\")) {\n            getPageAPI().createPage(\"provided_page_ready_to_update.zip\", is.readAllBytes());\n        }\n\n        // when\n        final PageUpdater pageUpdater = new PageUpdater();\n        final String newDisplayName = \"new display name\";\n        pageUpdater.setDisplayName(newDisplayName);\n\n        final Page providedPageToUpdate = getPageAPI()\n                .searchPages(new SearchOptionsBuilder(0, 1)\n                        .filter(PageSearchDescriptor.NAME, \"custompage_providedpagetoupdate\").done())\n                .getResult().get(0);\n        final Page returnedPage = getPageAPI().updatePage(providedPageToUpdate.getId(), pageUpdater);\n\n        //then\n        assertThat(returnedPage.isProvided()).isFalse();\n    }\n\n    @Test\n    public void updatePage_should_return_the_modified_page() throws Exception {\n        // given\n        final User john = createUser(\"john\", \"bpm\");\n        final User jack = createUser(\"jack\", \"bpm\");\n\n        logout();\n        loginOnDefaultTenantWith(\"john\", \"bpm\");\n        final String pageName = generateUniquePageName(0);\n        final byte[] pageContent = CommonTestUtil.createTestPageContent(pageName, DISPLAY_NAME, PAGE_DESCRIPTION);\n        final Page pageBeforeUpdate = getPageAPI().createPage(\n                new PageCreator(pageName, CONTENT_NAME).setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME),\n                pageContent);\n        Thread.sleep(10);\n        assertThat(pageBeforeUpdate.getInstalledBy()).isEqualTo(john.getId());\n        assertThat(pageBeforeUpdate.getLastUpdatedBy()).isEqualTo(john.getId());\n        logout();\n        loginOnDefaultTenantWith(\"jack\", \"bpm\");\n        // when\n        final PageUpdater pageUpdater = new PageUpdater();\n        final String newDescription = \"new description\";\n        final String newDisplayName = \"new display name\";\n        final String newContentName = \"new_content.zip\";\n        pageUpdater.setDescription(newDescription);\n        pageUpdater.setDisplayName(newDisplayName);\n        pageUpdater.setName(\"newName\");\n        pageUpdater.setContentName(newContentName);\n        pageUpdater.setContentType(ContentType.FORM);\n        pageUpdater.setProcessDefinitionId(5L);\n\n        final Page returnedPage = getPageAPI().updatePage(pageBeforeUpdate.getId(), pageUpdater);\n\n        // then\n        assertThat(returnedPage)\n                .hasInstalledBy(john.getId())\n                .hasInstalledBy(pageBeforeUpdate.getInstalledBy())\n                .hasLastUpdatedBy(jack.getId())\n                .hasName(pageBeforeUpdate.getName())\n                .hasInstallationDate(pageBeforeUpdate.getInstallationDate())\n                .hasDisplayName(newDisplayName)\n                .hasContentName(newContentName)\n                .hasDescription(newDescription)\n                .hasContentType(ContentType.FORM)\n                .hasProcessDefinitionId(5L);\n\n        assertThat(returnedPage.getLastModificationDate()).as(\"last modification time should be updated\")\n                .isAfter(pageBeforeUpdate.getLastModificationDate());\n\n        logout();\n        loginWithTechnicalUser();\n        deleteUser(john);\n        deleteUser(jack);\n\n    }\n\n    @Test(expected = AlreadyExistsException.class)\n    public void updateForm_with_existing_process_definitionId_should_fail() throws Exception {\n        final PageUpdater pageUpdater = new PageUpdater();\n\n        // given\n        getPageAPI().createPage(\n                new PageCreator(PAGE_NAME1, CONTENT_NAME).setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME)\n                        .setProcessDefinitionId(PROCESS_DEFINITION_ID),\n                CommonTestUtil.createTestPageContent(PAGE_NAME1, DISPLAY_NAME, PAGE_DESCRIPTION));\n        final Page page2 = getPageAPI().createPage(\n                new PageCreator(PAGE_NAME1, CONTENT_NAME).setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME),\n                CommonTestUtil.createTestPageContent(PAGE_NAME1, DISPLAY_NAME, PAGE_DESCRIPTION));\n\n        // when\n        pageUpdater.setProcessDefinitionId(PROCESS_DEFINITION_ID);\n        getPageAPI().updatePage(page2.getId(), pageUpdater);\n\n        // then\n        // exception\n\n    }\n\n    @Test(expected = UpdatingWithInvalidPageZipContentException.class)\n    public void updatePageContent_with_bad_content_should_fail() throws Exception {\n        // given\n        final Page createPage = getPageAPI().createPage(\n                new PageCreator(PAGE_NAME1, CONTENT_NAME).setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME),\n                CommonTestUtil.createTestPageContent(PAGE_NAME1, DISPLAY_NAME, PAGE_DESCRIPTION));\n\n        // when\n        getPageAPI().updatePageContent(createPage.getId(),\n                IOUtil.zip(Collections.singletonMap(\"README.md\", \"empty file\".getBytes())));\n\n        // then\n        // exception\n\n    }\n\n    @Test\n    public void updatePageContent_should_update_page_from_properties_except_name() throws Exception {\n        // given\n        final Page pageBefore = getPageAPI().createPage(\n                new PageCreator(PAGE_NAME1, CONTENT_NAME).setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME),\n                CommonTestUtil.createTestPageContent(PAGE_NAME1, DISPLAY_NAME, PAGE_DESCRIPTION));\n\n        // when\n        final String newDescription = \"new description\";\n        final String newDisplayName = \"new display name\";\n        final byte[] updatedPageContent = CommonTestUtil.createTestPageContent(PAGE_NAME2, newDisplayName,\n                newDescription);\n        getPageAPI().updatePageContent(pageBefore.getId(), updatedPageContent);\n\n        // then\n        final Page pageAfter = getPageAPI().getPage(pageBefore.getId());\n        assertThat(pageAfter.getName()).as(\"should not update page name\").isEqualTo(PAGE_NAME1);\n        assertThat(pageAfter.getDisplayName()).as(\"should update page display name\").isEqualTo(newDisplayName);\n        assertThat(pageAfter.getDescription()).as(\"should update page name\").isEqualTo(newDescription);\n\n    }\n\n    @Test\n    public void should_update_content_return_the_modified_content() throws Exception {\n        // given\n        final Date createTimeMillis = new Date(System.currentTimeMillis());\n        final String pageName = generateUniquePageName(0);\n        final byte[] oldContent = CommonTestUtil.createTestPageContent(pageName, DISPLAY_NAME, PAGE_DESCRIPTION);\n        final Page page = getPageAPI().createPage(\n                new PageCreator(pageName, CONTENT_NAME).setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME),\n                oldContent);\n        final long pageId = page.getId();\n\n        // when\n\n        // wait to see modified last update time\n        Thread.sleep(1000);\n\n        final Date updateTimeMillis = new Date(System.currentTimeMillis());\n        assertThat(updateTimeMillis).as(\"should wait 1 second\").isAfter(createTimeMillis);\n\n        final byte[] newContent = CommonTestUtil.createTestPageContent(pageName, DISPLAY_NAME, PAGE_DESCRIPTION);\n        getPageAPI().updatePageContent(pageId, newContent);\n        final byte[] returnedPageContent = getPageAPI().getPageContent(pageId);\n        final Page returnedPage = getPageAPI().getPage(pageId);\n\n        // then\n        checkPageContentContainsProperties(returnedPageContent, DISPLAY_NAME, PAGE_DESCRIPTION);\n        assertThat(returnedPage.getLastModificationDate()).as(\"last modification date should be modified \").isAfter(\n                page.getLastModificationDate());\n    }\n\n    @Test\n    public void should_create_page_use_content_type_in_properties() throws Exception {\n        // given\n        final String pageName1 = generateUniquePageName(0);\n        final byte[] pageContent1 = CommonTestUtil.createTestPageContent(pageName1, DISPLAY_NAME,\n                \"with content \" + PAGE_DESCRIPTION,\n                \"contentType=WillBeIgnored\", \"apiExtensions=myGetResource\", \"myGetResource.method=POST\",\n                \"myGetResource.pathTemplate=helloWorld\",\n                \"myGetResource.classFileName=Index.groovy\", \"myGetResource.permissions=application_visualization\");\n\n        final String pageName2 = generateUniquePageName(1);\n        final byte[] pageContent2 = CommonTestUtil.createTestPageContent(pageName2, DISPLAY_NAME,\n                \"with page creator \" + PAGE_DESCRIPTION, \"contentType=\"\n                        + ContentType.API_EXTENSION,\n                \"apiExtensions=myGetResource\", \"myGetResource.method=GET\", \"myGetResource.pathTemplate=helloWorld\",\n                \"myGetResource.classFileName=Index.groovy\", \"myGetResource.permissions=application_visualization\");\n\n        // when\n        final Page pageWithCreator = getPageAPI().createPage(\n                new PageCreator(pageName1, CONTENT_NAME).setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME)\n                        .setContentType(ContentType.API_EXTENSION),\n                pageContent1);\n        final Page pageWithContent = getPageAPI().createPage(pageName2, pageContent2);\n\n        // then\n        assertThat(pageWithContent).hasContentType(ContentType.API_EXTENSION);\n        assertThat(pageWithCreator).hasContentType(ContentType.API_EXTENSION);\n    }\n\n    @Test\n    public void should_create_a_page_and_delete_it_and_recreate_it_with_same_values() throws Exception {\n        // given\n        final String pageName = generateUniquePageName(1);\n        final byte[] pageContent = CommonTestUtil.createTestPageContent(pageName, DISPLAY_NAME,\n                \"with page creator \" + PAGE_DESCRIPTION, \"contentType=\"\n                        + ContentType.API_EXTENSION,\n                \"apiExtensions=myGetResource, myPostResource\", \"myGetResource.method=GET\",\n                \"myGetResource.pathTemplate=helloWorld\", \"myGetResource.classFileName=Index.groovy\",\n                \"myGetResource.permissions=newPermission\",\n                \"myPostResource.method=POST\",\n                \"myPostResource.pathTemplate=helloWorld\", \"myPostResource.classFileName=Index.groovy\",\n                \"myPostResource.permissions=newPermission\");\n\n        // when\n        Page pageWithContent = getPageAPI().createPage(pageName, pageContent);\n        getPageAPI().deletePage(pageWithContent.getId());\n\n        pageWithContent = getPageAPI().createPage(pageName, pageContent);\n\n        // then\n        assertThat(pageWithContent).hasContentType(ContentType.API_EXTENSION);\n    }\n\n    @Test\n    public void should_getPage_by_name_return_the_page() throws Exception {\n        // given\n        final String pageName = generateUniquePageName(0);\n        final byte[] pageContent = CommonTestUtil.createTestPageContent(pageName, DISPLAY_NAME, PAGE_DESCRIPTION);\n        final Page page = getPageAPI().createPage(\n                new PageCreator(pageName, CONTENT_NAME).setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME),\n                pageContent);\n\n        // when\n        final Page returnedPage = getPageAPI().getPageByName(page.getName());\n\n        // then\n        Assertions.assertThat(returnedPage).usingRecursiveComparison().isEqualTo(page);\n    }\n\n    @Test(expected = AlreadyExistsException.class)\n    public void should_createPage_with_same_name_throw_already_exists() throws Exception {\n        // , \"content.zip\"given\n        final String pageName = generateUniquePageName(0);\n        final byte[] pageContent = CommonTestUtil.createTestPageContent(pageName, DISPLAY_NAME, PAGE_DESCRIPTION);\n        getPageAPI().createPage(\n                new PageCreator(pageName, CONTENT_NAME).setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME),\n                pageContent);\n\n        // when\n        getPageAPI().createPage(\n                new PageCreator(pageName, CONTENT_NAME).setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME),\n                pageContent);\n\n        // then: expected exception\n    }\n\n    @Test(expected = InvalidPageTokenException.class)\n    public void should_createPage_with_invalid_name_InvalidPageTokenException() throws Exception {\n        // , \"content.zip\"given\n        final String pageName = \"plop\";\n        final byte[] pageContent = CommonTestUtil.createTestPageContent(pageName, DISPLAY_NAME, PAGE_DESCRIPTION);\n        getPageAPI().createPage(\n                new PageCreator(pageName, CONTENT_NAME).setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME),\n                pageContent);\n\n        // when\n        getPageAPI().createPage(\n                new PageCreator(pageName, CONTENT_NAME).setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME),\n                pageContent);\n\n        // then: expected exception\n    }\n\n    @Test(expected = InvalidPageTokenException.class)\n    public void should_createPage_with_no_name_InvalidPageTokenException() throws Exception {\n        // , \"content.zip\"given\n        final String pageName = \"\";\n        final byte[] pageContent = CommonTestUtil.createTestPageContent(pageName, DISPLAY_NAME, PAGE_DESCRIPTION);\n        getPageAPI().createPage(\n                new PageCreator(pageName, CONTENT_NAME).setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME),\n                pageContent);\n\n        // when\n        getPageAPI().createPage(\n                new PageCreator(pageName, CONTENT_NAME).setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME),\n                pageContent);\n\n        // then: expected exception\n    }\n\n    @Test\n    public void should_createPage_with_invalid_content_InvalidPageZipContentException() throws Exception {\n        // given\n        final String pageName = generateUniquePageName(0);\n        final byte[] pageContent = zip(file(\"README.md\", \"empty file\".getBytes()),\n                file(\"page.properties\", String.format(\"name=%s\\ncontentType=page\", pageName).getBytes()));\n\n        // when\n        assertThrows(InvalidPageZipMissingIndexException.class, () -> getPageAPI().createPage(\n                new PageCreator(pageName, CONTENT_NAME).setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME),\n                pageContent));\n\n        // then: expected exception\n    }\n\n    @Test\n    public void should_getPageContent_return_the_content() throws Exception {\n        // given\n        final String pageName = generateUniquePageName(0);\n        final String pageDescription = \"a verry long page description, maybe the longest description you will ever see, check that:\"\n                + \" Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut\"\n                + \" labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris\"\n                + \" nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit \"\n                + \"esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in \"\n                + \"culpa qui officia deserunt mollit anim id est laborum.\";\n        final byte[] bytes = CommonTestUtil.createTestPageContent(pageName, DISPLAY_NAME, pageDescription);\n        final Page page = getPageAPI().createPage(\n                new PageCreator(pageName, CONTENT_NAME).setDescription(pageDescription).setDisplayName(DISPLAY_NAME),\n                bytes);\n\n        // when\n        final byte[] pageContent = getPageAPI().getPageContent(page.getId());\n        // then\n        checkPageContentContainsProperties(pageContent, DISPLAY_NAME, pageDescription);\n    }\n\n    @Test\n    public void should_throw_an_exception_if_the_page_content_does_not_exist() {\n        // when\n        final PageNotFoundException exception = assertThrows(PageNotFoundException.class,\n                () -> getPageAPI().getPageContent(995464654654L));\n\n        // then\n        assertThat(exception.getMessage()).contains(\"Page with id 995464654654 not found\");\n    }\n\n    private void checkPageContentContainsProperties(final byte[] content, final String displayName,\n            final String description) throws Exception {\n        try {\n            Map<String, String> contentAsMap = unzip(content);\n            assertThat(contentAsMap.keySet()).as(\"should contains page.properties\").contains(\"page.properties\");\n            final String string = contentAsMap.get(\"page.properties\");\n            final Properties props = new Properties();\n            props.load(new StringReader(string));\n            assertThat(props.getProperty(\"description\")).as(\"should have same description\").isEqualTo(description);\n            assertThat(props.getProperty(\"displayName\")).as(\"should have same displayName\").isEqualTo(displayName);\n        } catch (final IOException e) {\n            fail(\"unzip error\", e);\n        }\n\n    }\n\n    private Map<String, String> unzip(final byte[] zipFile) throws Exception {\n        final ByteArrayInputStream bais = new ByteArrayInputStream(zipFile);\n        ZipEntry zipEntry;\n        final Map<String, String> zipMap = new HashMap<>();\n        try (ZipInputStream zipInputstream = new ZipInputStream(bais)) {\n            while ((zipEntry = zipInputstream.getNextEntry()) != null) {\n                final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();\n                int bytesRead;\n                final byte[] buffer = new byte[4096];\n                while ((bytesRead = zipInputstream.read(buffer)) > -1) {\n                    byteArrayOutputStream.write(buffer, 0, bytesRead);\n                }\n                zipMap.put(zipEntry.getName(), byteArrayOutputStream.toString(UTF_8));\n            }\n        }\n        return zipMap;\n    }\n\n    @Test(expected = PageNotFoundException.class)\n    public void deletePage_should_delete_the_page() throws Exception {\n        // given\n        final String pageName = generateUniquePageName(0);\n        final byte[] bytes = CommonTestUtil.createTestPageContent(pageName, DISPLAY_NAME, PAGE_DESCRIPTION);\n        final Page page = getPageAPI().createPage(\n                new PageCreator(pageName, CONTENT_NAME).setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME),\n                bytes);\n\n        // when\n        getPageAPI().deletePage(page.getId());\n\n        // then\n        getPageAPI().getPage(page.getId());\n    }\n\n    @Test(expected = AlreadyExistsException.class)\n    public void should_duplicates_with_same_name_and_process_definitionId_throw_exception() throws Exception {\n        // given\n        final String pageName = generateUniquePageName(0);\n        final byte[] bytes = createTestPageContent(pageName, DISPLAY_NAME, PAGE_DESCRIPTION);\n        getPageAPI().createPage(\n                new PageCreator(pageName, CONTENT_NAME, ContentType.FORM, PROCESS_DEFINITION_ID)\n                        .setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME),\n                bytes);\n\n        // when then exception\n        getPageAPI().createPage(\n                new PageCreator(pageName, CONTENT_NAME, ContentType.FORM, PROCESS_DEFINITION_ID)\n                        .setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME),\n                bytes);\n\n    }\n\n    @Test\n    public void should_search_with_search_term() throws Exception {\n        final String description = \"description\";\n        final String matchingValue = \"Cool\";\n        final String matchingDisplayName = matchingValue + \" page!\";\n\n        // given\n        final int noneMatchingCount = 8;\n        for (int i = 0; i < noneMatchingCount; i++) {\n            final String generateUniquePageName = generateUniquePageName(i) + i;\n            getPageAPI().createPage(\n                    new PageCreator(generateUniquePageName, CONTENT_NAME).setDescription(description)\n                            .setDisplayName(DISPLAY_NAME),\n                    CommonTestUtil.createTestPageContent(generateUniquePageName, DISPLAY_NAME, PAGE_DESCRIPTION));\n        }\n        final String generateUniquePageName = generateUniquePageName(9);\n        final Page pageWithMatchingSearchTerm = getPageAPI().createPage(\n                new PageCreator(generateUniquePageName, CONTENT_NAME).setDescription(description)\n                        .setDisplayName(matchingDisplayName),\n                CommonTestUtil.createTestPageContent(generateUniquePageName, DISPLAY_NAME, PAGE_DESCRIPTION));\n\n        // when\n        final SearchResult<Page> searchPages = getPageAPI()\n                .searchPages(new SearchOptionsBuilder(0, 5).searchTerm(matchingValue).done());\n\n        // then\n        final List<Page> results = searchPages.getResult();\n        assertThat(results.size()).as(\"should have only one matching page\").isEqualTo(1);\n        Assertions.assertThat(results.get(0)).as(\"should get the page with matching search term\")\n                .usingRecursiveComparison().isEqualTo(pageWithMatchingSearchTerm);\n    }\n\n    private String generateUniquePageName(final int i) {\n        return (\"custompage_unique\" + i) + System.currentTimeMillis();\n    }\n\n    @Test\n    public void should_8_pages_search_5_first_results_give_5_first_results() throws Exception {\n        // given\n        final int expectedResultSize = 5;\n        for (int i = 0; i < expectedResultSize + 3; i++) {\n            final String generateUniquePageName = generateUniquePageName(i) + 1;\n            final byte[] pageContent = CommonTestUtil.createTestPageContent(generateUniquePageName, DISPLAY_NAME,\n                    PAGE_DESCRIPTION);\n            getPageAPI().createPage(\n                    new PageCreator(generateUniquePageName, CONTENT_NAME).setDescription(PAGE_DESCRIPTION)\n                            .setDisplayName(DISPLAY_NAME),\n                    pageContent);\n        }\n\n        // when\n        final SearchResult<Page> searchPages = getPageAPI().searchPages(new SearchOptionsBuilder(0, 5).done());\n\n        // then\n        final List<Page> results = searchPages.getResult();\n        String stringBuilder = \"should have only \" + expectedResultSize + \" results\";\n        assertThat(results.size()).as(stringBuilder).isEqualTo(expectedResultSize);\n\n    }\n\n    @Test\n    public void should_search_by_display_name() throws Exception {\n        // given\n        final String description = PAGE_DESCRIPTION;\n        final String matchingDisplayName = DISPLAY_NAME;\n        final String noneMatchingDisplayName = \"aaa\";\n\n        // given\n        final int expectedMatchingResults = 3;\n        for (int i = 0; i < expectedMatchingResults; i++) {\n            final String generateUniquePageName = generateUniquePageName(i);\n            final byte[] pageContent = CommonTestUtil.createTestPageContent(generateUniquePageName, matchingDisplayName,\n                    description);\n            getPageAPI().createPage(\n                    new PageCreator(generateUniquePageName, CONTENT_NAME).setDescription(description)\n                            .setDisplayName(matchingDisplayName),\n                    pageContent);\n        }\n        final String anOtherName = generateUniquePageName(4);\n        getPageAPI().createPage(\n                new PageCreator(anOtherName, CONTENT_NAME).setDescription(\"an awesome page!!!!!!!\")\n                        .setDisplayName(noneMatchingDisplayName),\n                CommonTestUtil.createTestPageContent(anOtherName, noneMatchingDisplayName, \"an awesome page!!!!!!!\"));\n\n        // when\n        final SearchResult<Page> searchPages = getPageAPI().searchPages(\n                new SearchOptionsBuilder(0, expectedMatchingResults + 2)\n                        .filter(PageSearchDescriptor.DISPLAY_NAME, matchingDisplayName).done());\n        // then\n        final List<Page> results = searchPages.getResult();\n        assertThat(results.size()).as(\"should have \"\n                + expectedMatchingResults + \" results\").isEqualTo(expectedMatchingResults);\n\n    }\n\n    @Test\n    public void should_search_by_content_type() throws Exception {\n        // given\n        final String matchingDisplayName = DISPLAY_NAME;\n        final String noneMatchingDisplayName = \"aaa\";\n\n        // given\n        final int expectedMatchingResults = 3;\n        for (int i = 0; i < expectedMatchingResults; i++) {\n            final String generateUniquePageName = generateUniquePageName(i);\n            final byte[] pageContent = createTestPageContent(generateUniquePageName, matchingDisplayName,\n                    PAGE_DESCRIPTION);\n            getPageAPI().createPage(\n                    new PageCreator(generateUniquePageName, CONTENT_NAME, ContentType.FORM, PROCESS_DEFINITION_ID + i)\n                            .setDescription(\n                                    \"should be excluded from results\")\n                            .setDisplayName(matchingDisplayName),\n                    pageContent);\n            getPageAPI().createPage(\n                    new PageCreator(generateUniquePageName, CONTENT_NAME).setDescription(\"should be in search results\")\n                            .setDisplayName(matchingDisplayName),\n                    pageContent);\n        }\n        final String anOtherName = generateUniquePageName(4);\n        getPageAPI().createPage(\n                new PageCreator(anOtherName, CONTENT_NAME).setDescription(\"should be excluded from results\")\n                        .setDisplayName(noneMatchingDisplayName),\n                createTestPageContent(anOtherName, noneMatchingDisplayName, \"an awesome page!!!!!!!\"));\n\n        // when\n        final SearchResult<Page> searchPages = getPageAPI().searchPages(\n                new SearchOptionsBuilder(0, expectedMatchingResults + 2)\n                        .filter(PageSearchDescriptor.DISPLAY_NAME, matchingDisplayName)\n                        .filter(PageSearchDescriptor.CONTENT_TYPE, ContentType.PAGE).done());\n        // then\n        final List<Page> results = searchPages.getResult();\n        assertThat(results.size()).as(\"should have \"\n                + expectedMatchingResults + \" results\").isEqualTo(expectedMatchingResults);\n\n    }\n\n    @Test\n    public void should_search_work_on_desc_order() throws Exception {\n        final String displayName = DISPLAY_NAME;\n        final String description = PAGE_DESCRIPTION;\n        final String firstPageNameInDescOrder = \"custompage_zPageName\";\n\n        // given\n        final int numberOfNonsMatchingPage = 5;\n        for (int i = 0; i < numberOfNonsMatchingPage; i++) {\n            final String generateUniquePageName = generateUniquePageName(i) + i;\n            final byte[] pageContent = CommonTestUtil.createTestPageContent(generateUniquePageName, displayName,\n                    description);\n            getPageAPI().createPage(\n                    new PageCreator(generateUniquePageName, CONTENT_NAME).setDescription(description)\n                            .setDisplayName(displayName),\n                    pageContent);\n        }\n        final Page expectedMatchingPage = getPageAPI().createPage(\n                new PageCreator(firstPageNameInDescOrder, CONTENT_NAME).setDescription(description)\n                        .setDisplayName(displayName),\n                CommonTestUtil.createTestPageContent(firstPageNameInDescOrder, displayName, description));\n\n        // when\n        final SearchResult<Page> searchPages = getPageAPI().searchPages(\n                new SearchOptionsBuilder(0, 1).sort(PageSearchDescriptor.NAME, Order.DESC).done());\n\n        // then\n        final List<Page> results = searchPages.getResult();\n        Assertions.assertThat(results.get(0)).usingRecursiveComparison().isEqualTo(expectedMatchingPage);\n    }\n\n    @Test\n    public void updatePageContent_and_deletePage_should_update_permissions() throws Exception {\n        // setup\n        final User jack = createUser(\"jack\", \"bpm\");\n        Profile profile = getProfileAPI()\n                .searchProfiles(new SearchOptionsBuilder(0, 1).filter(ProfileSearchDescriptor.NAME, \"User\").done())\n                .getResult().get(0);\n        getProfileAPI().createProfileMember(profile.getId(), jack.getId(), null, null);\n\n        getLivingApplicationAPI().importApplications(\n                IOUtils.toByteArray(ApplicationIT.class.getResourceAsStream(\"testApp.xml\")),\n                ApplicationImportPolicy.FAIL_ON_DUPLICATES);\n\n        // given\n        final String apiExtensionName = generateUniquePageName(0);\n        final byte[] apiExtensionContent1 = CommonTestUtil.createTestPageContent(apiExtensionName, DISPLAY_NAME,\n                \"with content \" + PAGE_DESCRIPTION, \"contentType=\" + ContentType.API_EXTENSION,\n                \"apiExtensions=myGetResource, myPostResource\", \"myGetResource.method=GET\",\n                \"myGetResource.pathTemplate=helloWorld\",\n                \"myGetResource.classFileName=Index.groovy\", \"myGetResource.permissions=application_visualization\",\n                \"myPostResource.method=POST\",\n                \"myPostResource.pathTemplate=helloWorld\", \"myPostResource.classFileName=Index.groovy\",\n                \"myPostResource.permissions = application_visualization\");\n\n        // when\n        final Page apiExtension = getPageAPI().createPage(apiExtensionName, apiExtensionContent1);\n\n        // need to log back in to actualize permissions:\n        logout();\n        loginOnDefaultTenantWith(\"jack\", \"bpm\");\n\n        // Check that we are not authorized before update of the page properties content:\n        APICallContext apiCallContext = new APICallContext(\"GET\", \"extension\", \"helloWorld_v2\", null);\n        assertThat(getPermissionAPI().isAuthorized(apiCallContext)).isFalse();\n\n        final byte[] apiExtensionContent2 = CommonTestUtil.createTestPageContent(apiExtensionName, DISPLAY_NAME,\n                \"with content \" + PAGE_DESCRIPTION, \"contentType=\" + ContentType.API_EXTENSION,\n                \"apiExtensions=myGetResource, myPutResource\", \"myGetResource.method=GET\",\n                \"myGetResource.pathTemplate=helloWorld_v2\",\n                \"myGetResource.classFileName=Index.groovy\", \"myGetResource.permissions=application_visualization\",\n                \"myPutResource.method=PUT\",\n                \"myPutResource.pathTemplate=helloWorld\", \"myPutResource.classFileName=Index.groovy\",\n                \"myPutResource.permissions = application_visualization\");\n        getPageAPI().updatePageContent(apiExtension.getId(), apiExtensionContent2);\n\n        // Application testApp.xml references layout custompage_layoutBonita that declares resources bound to\n        // the 'application_visualization' permission. So this new apiExtension, accessible through 'GET|extension/helloWorld_v2', is accessible\n        // to anyone having 'application_visualization' permission.\n\n        // Check that permission has been update to file through user permission check:\n        assertThat(getPermissionAPI().isAuthorized(apiCallContext)).isTrue();\n        // Check that previous version has indeed been removed:\n        assertThat(getPermissionAPI().isAuthorized(new APICallContext(\"GET\", \"extension\", \"helloWorld\", null)))\n                .isFalse();\n\n        getPageAPI().deletePage(apiExtension.getId());\n\n        // Check that permissions have been removed with the page deletion:\n        assertThat(getPermissionAPI().isAuthorized(apiCallContext)).isFalse();\n\n        //cleanup\n        logout();\n        loginWithTechnicalUser();\n        deleteUser(jack);\n    }\n\n    @Test\n    public void should_search_work_with_processDefinitionId_set_to_null() throws Exception {\n        // given\n        String name = generateUniquePageName(345);\n        getPageAPI().createPage(\n                new PageCreator(name, CONTENT_NAME).setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME),\n                CommonTestUtil.createTestPageContent(name, DISPLAY_NAME, PAGE_DESCRIPTION));\n\n        // when\n        final SearchResult<Page> searchPages = getPageAPI().searchPages(\n                new SearchOptionsBuilder(0, 1).filter(PageSearchDescriptor.NAME, name)\n                        .filter(PageSearchDescriptor.PROCESS_DEFINITION_ID, null).done());\n\n        // then\n        final List<Page> results = searchPages.getResult();\n        assertThat(results.get(0).getName()).isEqualTo(name);\n\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/platform/PlatformIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform;\n\nimport static org.awaitility.Awaitility.await;\nimport static org.junit.Assert.*;\n\nimport java.time.Duration;\n\nimport org.assertj.core.api.Assertions;\nimport org.bonitasoft.engine.CommonAPIIT;\nimport org.bonitasoft.engine.PrintTestsStatusRule;\nimport org.bonitasoft.engine.api.APIClient;\nimport org.bonitasoft.engine.api.IdentityAPI;\nimport org.bonitasoft.engine.api.LoginAPI;\nimport org.bonitasoft.engine.api.PlatformAPI;\nimport org.bonitasoft.engine.api.PlatformAPIAccessor;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.flownode.TimerType;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.bonitasoft.engine.session.PlatformSession;\nimport org.bonitasoft.engine.test.PlatformTestUtil;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.TestRule;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class PlatformIT extends CommonAPIIT {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(PlatformIT.class);\n\n    private static PlatformAPI platformAPI;\n\n    private static PlatformSession session;\n\n    private static final PlatformTestUtil platformTestUtil = new PlatformTestUtil();\n\n    @After\n    public void after() throws BonitaException {\n        if (!platformAPI.isNodeStarted()) {\n            platformAPI.startNode();\n        }\n        platformTestUtil.logoutOnPlatform(session);\n        try {\n            platformTestUtil.deployCommandsOnDefaultTenant();\n        } catch (AlreadyExistsException ignored) {\n\n        }\n    }\n\n    @Before\n    public void before() throws BonitaException {\n        session = platformTestUtil.loginOnPlatform();\n        platformAPI = PlatformAPIAccessor.getPlatformAPI(session);\n        if (!platformAPI.isNodeStarted()) {\n            platformAPI.startNode();\n        }\n    }\n\n    @Rule\n    public TestRule testWatcher = new PrintTestsStatusRule(LOGGER) {\n\n        @Override\n        public void clean() {\n        }\n    };\n\n    @Test\n    public void isPlatformCreated() throws BonitaException {\n        assertTrue(platformAPI.isPlatformCreated());\n    }\n\n    @Test\n    public void getPlatformState() throws Exception {\n        // test started state\n        PlatformState state = platformAPI.getPlatformState();\n        assertEquals(PlatformState.STARTED, state);\n        // test stopped state\n        platformAPI.stopNode();\n        state = platformAPI.getPlatformState();\n        assertEquals(PlatformState.STOPPED, state);\n        // test exception:PlatformNotFoundException\n    }\n\n    @Test\n    public void callStopNodeTwice() throws Exception {\n        platformAPI.stopNode();\n        platformAPI.stopNode();\n    }\n\n    @Test\n    public void stopNodeAndStartNode() throws Exception {\n        final LoginAPI loginAPI = TenantAPIAccessor.getLoginAPI();\n        final APISession tenantSession = loginAPI.login(\"install\", \"install\");\n        final IdentityAPI identityAPI = TenantAPIAccessor.getIdentityAPI(tenantSession);\n        identityAPI.getNumberOfUsers();\n        restartPlatform();\n        try {\n            identityAPI.getNumberOfUsers();\n            fail(\"session should not work\");\n        } catch (final InvalidSessionException e) {\n            // ok\n        }\n    }\n\n    private static void restartPlatform() throws StopNodeException, StartNodeException {\n        platformAPI.stopNode();\n        platformAPI.startNode();\n    }\n\n    @Test\n    public void should_have_processes_with_duration_timer_still_work_after_restart() throws Exception {\n        APIClient apiClient = new APIClient();\n        apiClient.login(\"install\", \"install\");\n        ProcessDefinition wait2Sec = apiClient.getProcessAPI()\n                .deployAndEnableProcess(new ProcessDefinitionBuilder()\n                        .createNewInstance(\"a process with 2 sec intermediate timer\", \"1.0\")\n                        .addIntermediateCatchEvent(\"wait2Sec\").addTimerEventTriggerDefinition(TimerType.DURATION,\n                                new ExpressionBuilder().createConstantLongExpression(2000))\n                        .getProcess());\n        for (int i = 0; i < 20; i++) {\n            apiClient.getProcessAPI().startProcess(wait2Sec.getId());\n        }\n\n        await().until(() -> apiClient.getProcessAPI().getNumberOfProcessInstances(), nb -> nb > 0L);\n        Assertions.assertThat(apiClient.getProcessAPI().getNumberOfProcessInstances()).isGreaterThan(0);\n        restartPlatform();\n        apiClient.login(\"install\", \"install\");\n        await().atMost(Duration.ofMinutes(4)).until(() -> apiClient.getProcessAPI().getNumberOfProcessInstances(),\n                nb -> nb == 0L);\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/platform/PlatformLoginIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform;\n\nimport org.bonitasoft.engine.CommonAPIIT;\nimport org.bonitasoft.engine.api.PlatformAPIAccessor;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.session.SessionNotFoundException;\nimport org.bonitasoft.engine.session.impl.PlatformSessionImpl;\nimport org.junit.Test;\n\npublic class PlatformLoginIT extends CommonAPIIT {\n\n    @Test(expected = InvalidPlatformCredentialsException.class)\n    public void login_with_bad_credentials_should_throw_InvalidPlatformCredentialsException() throws BonitaException {\n        PlatformAPIAccessor.getPlatformLoginAPI().login(\"bad\", \"bad\");\n    }\n\n    @Test(expected = SessionNotFoundException.class)\n    public void logout_with_bad_session_should_throw_SessionNotFound() throws BonitaException {\n        PlatformAPIAccessor.getPlatformLoginAPI().logout(new PlatformSessionImpl(123L, null, -1L, null, -1L));\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/platform/command/PlatformCommandIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.command;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.fail;\n\nimport java.io.IOException;\n\nimport org.bonitasoft.engine.CommonAPIIT;\nimport org.bonitasoft.engine.api.PlatformAPIAccessor;\nimport org.bonitasoft.engine.api.PlatformCommandAPI;\nimport org.bonitasoft.engine.command.CommandDescriptor;\nimport org.bonitasoft.engine.command.CommandNotFoundException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.session.PlatformSession;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class PlatformCommandIT extends CommonAPIIT {\n\n    private static PlatformCommandAPI platformCommandAPI;\n\n    private static PlatformSession session;\n\n    @Before\n    public void before() throws BonitaException, IOException {\n        session = loginOnPlatform();\n        platformCommandAPI = PlatformAPIAccessor.getPlatformCommandAPI(session);\n    }\n\n    @After\n    public void after() throws BonitaException {\n        logoutOnPlatform(session);\n    }\n\n    @Test\n    public void createPlatformCommand() throws BonitaException {\n        try {\n            platformCommandAPI.addDependency(\"commands\", \"jar\".getBytes());\n            final CommandDescriptor command = platformCommandAPI.register(\"testPlatformCommand\", \"command description\",\n                    \"implementation\");\n            assertNotNull(command);\n            assertEquals(\"testPlatformCommand\", command.getName());\n            assertEquals(\"command description\", command.getDescription());\n        } finally {\n            platformCommandAPI.unregister(\"testPlatformCommand\");\n            platformCommandAPI.removeDependency(\"commands\");\n        }\n    }\n\n    @Test\n    public void deletePlatformCommand() throws BonitaException {\n        platformCommandAPI.addDependency(\"commands\", \"jar\".getBytes());\n        platformCommandAPI.register(\"platformCommand1\", \"command description\", \"implementation\");\n        final CommandDescriptor command = platformCommandAPI.getCommand(\"platformCommand1\");\n        assertNotNull(command);\n        platformCommandAPI.unregister(\"platformCommand1\");\n        platformCommandAPI.removeDependency(\"commands\");\n        try {\n            platformCommandAPI.getCommand(\"platformCommand1\");\n            fail(\"command should be deleted\");\n        } catch (final CommandNotFoundException ignored) {\n        }\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/EvaluateExpressionIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.process;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.process.ActivationState;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ActivityDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.TransitionDefinitionBuilder;\nimport org.bonitasoft.engine.connector.EngineExecutionContext;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.expression.ExpressionConstants;\nimport org.bonitasoft.engine.expression.ExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.ExpressionType;\nimport org.bonitasoft.engine.identity.User;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Zhao Na\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class EvaluateExpressionIT extends TestWithUser {\n\n    private static final String STEP2_NAME = \"Approval\";\n\n    private static final String STEP1_NAME = \"Request\";\n    public static final String EXPECTED_EVALUATED_DATE = \"2013-07-18 14:49:26 GMT+02:00plop\";\n\n    private ProcessDefinition processDefinition = null;\n\n    private ProcessInstance processInstance = null;\n\n    private Map<Expression, Map<String, Serializable>> expressions;\n\n    @Override\n    @Before\n    public void before() throws Exception {\n        super.before();\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION);\n        processDefinitionBuilder.addData(\"stringData\", String.class.getName(),\n                new ExpressionBuilder().createConstantStringExpression(\"Word\"));\n        processDefinitionBuilder.addDateData(\"dateData\",\n                new ExpressionBuilder().createConstantDateExpression(\"2013-07-18T14:49:26.86+02:00\"));\n        processDefinitionBuilder.addData(\"doubleData\", Double.class.getName(),\n                new ExpressionBuilder().createConstantDoubleExpression(2D));\n        processDefinitionBuilder.addData(\"longData\", Long.class.getName(),\n                new ExpressionBuilder().createConstantLongExpression(1L));\n        processDefinitionBuilder.addData(\"booleanData\", Boolean.class.getName(),\n                new ExpressionBuilder().createConstantBooleanExpression(true));\n        processDefinitionBuilder.addData(\"floatData\", Float.class.getName(),\n                new ExpressionBuilder().createConstantFloatExpression(100F));\n        processDefinitionBuilder.addData(\"integerData\", Integer.class.getName(),\n                new ExpressionBuilder().createConstantIntegerExpression(4));\n        processDefinitionBuilder.addData(\"processData\", String.class.getName(),\n                new ExpressionBuilder().createConstantStringExpression(\"processData\"));\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addUserTask(STEP1_NAME, ACTOR_NAME);\n        processDefinitionBuilder.addUserTask(STEP2_NAME, ACTOR_NAME);\n        processDefinitionBuilder.addTransition(STEP1_NAME, STEP2_NAME);\n        processDefinition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, user);\n        processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        final List<Expression> stringDependencies = new ArrayList<>();\n        stringDependencies.add(new ExpressionBuilder().createDataExpression(\"stringData\", String.class.getName()));\n        stringDependencies.add(new ExpressionBuilder().createInputExpression(\"field_string\", String.class.getName()));\n        final Expression stringExpression = new ExpressionBuilder().createGroovyScriptExpression(\"StringScript\",\n                \"stringData + \\\"-\\\" + field_string\",\n                String.class.getName(), stringDependencies);\n        final Map<String, Serializable> fieldValues = new HashMap<>();\n        fieldValues.put(\"field_string\", \"Excel\");\n\n        final List<Expression> dateDependencies = Collections\n                .singletonList(new ExpressionBuilder().createDataExpression(\"dateData\", Date.class.getName()));\n        final Expression dateExpression = new ExpressionBuilder().createGroovyScriptExpression(\"DateScript\",\n                \"import java.text.SimpleDateFormat;import java.util.TimeZone;\"\n                        + \"SimpleDateFormat dateFormat = new SimpleDateFormat(\\\"yyyy-MM-dd HH:mm:ss zzz\\\"); dateFormat.setTimeZone(TimeZone.getTimeZone(\\\"GMT+2\\\"));\"\n                        + \"dateFormat.format(dateData) + \\\"plop\\\"\",\n                String.class.getName(), dateDependencies);\n\n        final List<Expression> longDependencies = Collections\n                .singletonList(new ExpressionBuilder().createDataExpression(\"longData\", Long.class.getName()));\n        final Expression longExpression = new ExpressionBuilder().createGroovyScriptExpression(\"LongScript\",\n                \"String.valueOf(longData)+ \\\"plop\\\"\",\n                String.class.getName(), longDependencies);\n\n        final List<Expression> doubleDependencies = Collections\n                .singletonList(new ExpressionBuilder().createDataExpression(\"doubleData\", Double.class.getName()));\n        final Expression doubleExpression = new ExpressionBuilder().createGroovyScriptExpression(\"DoubleScript\",\n                \"String.valueOf(doubleData) + \\\"plop\\\"\",\n                String.class.getName(), doubleDependencies);\n\n        final List<Expression> booleanDependencies = Collections\n                .singletonList(new ExpressionBuilder().createDataExpression(\"booleanData\",\n                        Boolean.class.getName()));\n        final Expression booleanExpression = new ExpressionBuilder().createGroovyScriptExpression(\"BooleanScript\",\n                \"booleanData && false\",\n                Boolean.class.getName(), booleanDependencies);\n\n        final List<Expression> floatDependencies = Collections\n                .singletonList(new ExpressionBuilder().createDataExpression(\"floatData\", Float.class.getName()));\n        final Expression floatExpression = new ExpressionBuilder().createGroovyScriptExpression(\"FloatScript\",\n                \"String.valueOf(floatData) + \\\"plop\\\"\",\n                String.class.getName(), floatDependencies);\n\n        final List<Expression> integerDependencies = Collections\n                .singletonList(new ExpressionBuilder().createDataExpression(\"integerData\",\n                        Integer.class.getName()));\n        final Expression integerExpression = new ExpressionBuilder().createGroovyScriptExpression(\"IntegerScript\",\n                \"String.valueOf(integerData) + \\\"plop\\\"\",\n                String.class.getName(), integerDependencies);\n\n        final Expression constantStringExpression = new ExpressionBuilder().createNewInstance(\"Word\").setContent(\"\")\n                .setExpressionType(ExpressionType.TYPE_CONSTANT).setReturnType(String.class.getName()).done();\n\n        expressions = new HashMap<>();\n        expressions.put(stringExpression, fieldValues);\n        expressions.put(dateExpression, new HashMap<String, Serializable>());\n        expressions.put(longExpression, new HashMap<String, Serializable>());\n        expressions.put(doubleExpression, new HashMap<String, Serializable>());\n        expressions.put(booleanExpression, new HashMap<String, Serializable>());\n        expressions.put(floatExpression, new HashMap<String, Serializable>());\n        expressions.put(integerExpression, new HashMap<String, Serializable>());\n        expressions.put(constantStringExpression, new HashMap<String, Serializable>());\n        expressions.put(new ExpressionBuilder().createDataExpression(\"processData\", String.class.getName()),\n                new HashMap<String, Serializable>());\n    }\n\n    @Override\n    @After\n    public void after() throws Exception {\n        disableAndDeleteProcess(processDefinition);\n        super.after();\n    }\n\n    private static ProcessDefinitionBuilder createProcessDefinitionBuilderWithHumanAndAutomaticSteps(\n            final String processName, final String processVersion,\n            final List<String> stepNames, final List<Boolean> isHuman) {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(processName,\n                processVersion);\n        processBuilder.addActor(\"Actor1\");\n        ActivityDefinitionBuilder activityDefinitionBuilder = null;\n        for (int i = 0; i < stepNames.size(); i++) {\n            final String stepName = stepNames.get(i);\n            if (isHuman.get(i)) {\n                if (activityDefinitionBuilder != null) {\n                    activityDefinitionBuilder = activityDefinitionBuilder.addUserTask(stepName, \"Actor1\");\n                } else {\n                    activityDefinitionBuilder = processBuilder.addUserTask(stepName, \"Actor1\");\n                }\n            } else {\n                if (activityDefinitionBuilder != null) {\n                    activityDefinitionBuilder = activityDefinitionBuilder.addAutomaticTask(stepName);\n                } else {\n                    activityDefinitionBuilder = processBuilder.addAutomaticTask(stepName);\n                }\n            }\n        }\n        TransitionDefinitionBuilder transitionDefinitionBuilder = null;\n        for (int i = 0; i < stepNames.size() - 1; i++) {\n            if (transitionDefinitionBuilder != null) {\n                transitionDefinitionBuilder = transitionDefinitionBuilder.addTransition(stepNames.get(i),\n                        stepNames.get(i + 1));\n            } else {\n                transitionDefinitionBuilder = activityDefinitionBuilder.addTransition(stepNames.get(i),\n                        stepNames.get(i + 1));\n            }\n        }\n        return processBuilder;\n    }\n\n    private void cleanup(final long processDefinitionId) throws BonitaException {\n        disableAndDeleteProcess(processDefinitionId);\n    }\n\n    private ProcessDefinition createAndDeployProcessDefinitionAndInstance(final String dataName, final int value,\n            final boolean isHuman, final User user)\n            throws Exception {\n        // create data expression\n        final Expression dataDefaultExp = new ExpressionBuilder().createConstantIntegerExpression(value);\n\n        // create a processDefinition with data and parameter...\n        final DesignProcessDefinition processDef = createProcessDefinitionBuilderWithHumanAndAutomaticSteps(\n                \"My_Process\", \"1.0\",\n                Collections.singletonList(\"step1\"), Collections.singletonList(isHuman))\n                .addIntegerData(dataName, dataDefaultExp)\n                .addDescription(\"Delivery all day and night long\").getProcess();\n\n        return deployAndEnableProcessWithActor(processDef, \"Actor1\", user);\n    }\n\n    @Test\n    public void evaluateExpressionsAtProcessInstantiation() throws Exception {\n        waitForPendingTasks(getSession().getUserId(), 1);\n\n        final Map<String, Serializable> result = getProcessAPI()\n                .evaluateExpressionsAtProcessInstanciation(processInstance.getId(), expressions);\n        assertEquals(\"Word-Excel\", result.get(\"StringScript\"));\n        assertEquals(EXPECTED_EVALUATED_DATE, result.get(\"DateScript\"));\n        assertEquals(\"1plop\", result.get(\"LongScript\"));\n        assertEquals(\"2.0plop\", result.get(\"DoubleScript\"));\n        assertEquals(false, result.get(\"BooleanScript\"));\n        assertEquals(\"100.0plop\", result.get(\"FloatScript\"));\n        assertEquals(\"4plop\", result.get(\"IntegerScript\"));\n    }\n\n    @Test(expected = ExpressionEvaluationException.class)\n    public void evaluateExpressionsAtProcessInstantiationWithWrongProcessInstanceId() throws Exception {\n        getProcessAPI().evaluateExpressionsAtProcessInstanciation(36, expressions);\n    }\n\n    @Test\n    public void evaluateExpressionsOnCompletedActivityInstance() throws Exception {\n        final long step1Id = waitForUserTaskAndExecuteIt(processInstance, STEP1_NAME, user);\n        final Map<String, Serializable> result = getProcessAPI().evaluateExpressionsOnCompletedActivityInstance(step1Id,\n                expressions);\n        assertEquals(\"Word-Excel\", result.get(\"StringScript\"));\n        assertEquals(EXPECTED_EVALUATED_DATE, result.get(\"DateScript\"));\n        assertEquals(\"1plop\", result.get(\"LongScript\"));\n        assertEquals(\"2.0plop\", result.get(\"DoubleScript\"));\n        assertEquals(false, result.get(\"BooleanScript\"));\n        assertEquals(\"100.0plop\", result.get(\"FloatScript\"));\n        assertEquals(\"4plop\", result.get(\"IntegerScript\"));\n        assertEquals(\"processData\", result.get(\"processData\"));\n    }\n\n    @Test\n    public void evaluateExpressionsOnCompletedActivityInstanceWithArchivedProcess() throws Exception {\n        final long step1Id = waitForUserTaskAndExecuteIt(processInstance, STEP1_NAME, user);\n        waitForUserTaskAndExecuteIt(processInstance, STEP2_NAME, user);\n        waitForProcessToFinish(processInstance);\n        final Map<String, Serializable> result = getProcessAPI().evaluateExpressionsOnCompletedActivityInstance(step1Id,\n                expressions);\n        assertEquals(\"Word-Excel\", result.get(\"StringScript\"));\n        assertEquals(EXPECTED_EVALUATED_DATE, result.get(\"DateScript\"));\n        assertEquals(\"1plop\", result.get(\"LongScript\"));\n        assertEquals(\"2.0plop\", result.get(\"DoubleScript\"));\n        assertEquals(false, result.get(\"BooleanScript\"));\n        assertEquals(\"100.0plop\", result.get(\"FloatScript\"));\n        assertEquals(\"4plop\", result.get(\"IntegerScript\"));\n        assertEquals(\"processData\", result.get(\"processData\"));\n    }\n\n    @Test\n    public void evaluateExpressionsOnCompletedActivityInstanceInSubProcess() throws Exception {\n        ProcessDefinitionBuilder caller = new ProcessDefinitionBuilder().createNewInstance(\"Caller\", \"1\");\n        caller.addCallActivity(\"callActivity\", new ExpressionBuilder().createConstantStringExpression(PROCESS_NAME),\n                new ExpressionBuilder().createConstantStringExpression(PROCESS_VERSION));\n        ProcessDefinition callerProcess = deployAndEnableProcess(caller.done());\n        ProcessInstance callerInstance = getProcessAPI().startProcess(callerProcess.getId());\n\n        final long step1Id = waitForUserTaskAndExecuteIt(callerInstance, STEP1_NAME, user);\n        waitForUserTaskAndExecuteIt(callerInstance, STEP2_NAME, user);\n        waitForProcessToFinish(callerInstance);\n        final Map<String, Serializable> result = getProcessAPI().evaluateExpressionsOnCompletedActivityInstance(step1Id,\n                expressions);\n        assertEquals(\"Word-Excel\", result.get(\"StringScript\"));\n        assertEquals(EXPECTED_EVALUATED_DATE, result.get(\"DateScript\"));\n        assertEquals(\"1plop\", result.get(\"LongScript\"));\n        assertEquals(\"2.0plop\", result.get(\"DoubleScript\"));\n        assertEquals(false, result.get(\"BooleanScript\"));\n        assertEquals(\"100.0plop\", result.get(\"FloatScript\"));\n        assertEquals(\"4plop\", result.get(\"IntegerScript\"));\n        assertEquals(\"processData\", result.get(\"processData\"));\n        disableAndDeleteProcess(callerProcess);\n    }\n\n    @Test(expected = ExpressionEvaluationException.class)\n    public void evaluateExpressionsOnCompletedActivityInstanceWithWrongActivityInstanceId() throws Exception {\n        getProcessAPI().evaluateExpressionsOnCompletedActivityInstance(36, expressions);\n    }\n\n    @Test\n    public void evaluateExpressionsOnCompletedProcessInstance() throws Exception {\n        waitForUserTaskAndExecuteIt(processInstance, STEP1_NAME, user);\n        waitForUserTaskAndExecuteIt(processInstance, STEP2_NAME, user);\n        waitForProcessToFinish(processInstance);\n\n        final Map<String, Serializable> result = getProcessAPI()\n                .evaluateExpressionOnCompletedProcessInstance(processInstance.getId(), expressions);\n        assertEquals(\"Word-Excel\", result.get(\"StringScript\"));\n        assertEquals(EXPECTED_EVALUATED_DATE, result.get(\"DateScript\"));\n        assertEquals(\"1plop\", result.get(\"LongScript\"));\n        assertEquals(\"2.0plop\", result.get(\"DoubleScript\"));\n        assertEquals(false, result.get(\"BooleanScript\"));\n        assertEquals(\"100.0plop\", result.get(\"FloatScript\"));\n        assertEquals(\"4plop\", result.get(\"IntegerScript\"));\n        assertEquals(\"processData\", result.get(\"processData\"));\n    }\n\n    @Test\n    public void evaluateExpressionsOnCompletedProcessInstanceAfterVariableUpdate() throws Exception {\n        waitForUserTaskAndExecuteIt(processInstance, STEP1_NAME, user);\n        getProcessAPI().updateProcessDataInstance(\"stringData\", processInstance.getId(), \"Plop\");\n        waitForUserTaskAndExecuteIt(processInstance, STEP2_NAME, user);\n        waitForProcessToFinish(processInstance);\n\n        final Map<String, Serializable> result = getProcessAPI()\n                .evaluateExpressionOnCompletedProcessInstance(processInstance.getId(), expressions);\n        assertEquals(\n                \"if Word-Excel is returned, it means the values of the variable used are the latest ones whereas it should be the ones of when the activity was submited\",\n                \"Plop-Excel\", result.get(\"StringScript\"));\n        assertEquals(EXPECTED_EVALUATED_DATE, result.get(\"DateScript\"));\n        assertEquals(\"1plop\", result.get(\"LongScript\"));\n        assertEquals(\"2.0plop\", result.get(\"DoubleScript\"));\n        assertEquals(false, result.get(\"BooleanScript\"));\n        assertEquals(\"100.0plop\", result.get(\"FloatScript\"));\n        assertEquals(\"4plop\", result.get(\"IntegerScript\"));\n        assertEquals(\"processData\", result.get(\"processData\"));\n    }\n\n    @Test\n    public void evaluateExpressionsOnActivityInstance() throws Exception {\n        final long step1Id = waitForUserTask(processInstance, STEP1_NAME);\n        final Map<String, Serializable> result = getProcessAPI().evaluateExpressionsOnActivityInstance(step1Id,\n                expressions);\n        assertEquals(\"Word-Excel\", result.get(\"StringScript\"));\n        assertEquals(EXPECTED_EVALUATED_DATE, result.get(\"DateScript\"));\n        assertEquals(\"1plop\", result.get(\"LongScript\"));\n        assertEquals(\"2.0plop\", result.get(\"DoubleScript\"));\n        assertEquals(false, result.get(\"BooleanScript\"));\n        assertEquals(\"100.0plop\", result.get(\"FloatScript\"));\n        assertEquals(\"4plop\", result.get(\"IntegerScript\"));\n        assertEquals(\"\", result.get(\"Word\"));\n        assertEquals(\"processData\", result.get(\"processData\"));\n    }\n\n    @Test(expected = ExpressionEvaluationException.class)\n    public void evaluateExpressionsOnActivityInstanceWithWrongActivityInstanceId() throws Exception {\n        getProcessAPI().evaluateExpressionsOnActivityInstance(36, expressions);\n    }\n\n    @Test\n    public void evaluateAssigneeId() throws Exception {\n        final ActivityInstance userTaskInstance = waitForUserTaskAndAssignIt(processInstance, STEP1_NAME, user);\n\n        final Expression taskAssigneeExpr = new ExpressionBuilder()\n                .createEngineConstant(ExpressionConstants.TASK_ASSIGNEE_ID);\n        final Expression engineExecContextExpr = new ExpressionBuilder()\n                .createEngineConstant(ExpressionConstants.ENGINE_EXECUTION_CONTEXT);\n        final Map<Expression, Map<String, Serializable>> engineExpresssions = new HashMap<>();\n        engineExpresssions.put(taskAssigneeExpr, Collections.<String, Serializable> emptyMap());\n        engineExpresssions.put(engineExecContextExpr, Collections.<String, Serializable> emptyMap());\n\n        final Map<String, Serializable> result = getProcessAPI()\n                .evaluateExpressionsOnActivityInstance(userTaskInstance.getId(), engineExpresssions);\n        assertEquals(user.getId(), result.get(taskAssigneeExpr.getContent()));\n        assertEquals(user.getId(),\n                ((EngineExecutionContext) result.get(engineExecContextExpr.getContent())).getTaskAssigneeId());\n    }\n\n    @Test\n    public void evaluateExpressionsOnProcessInstanceWithInitialValues() throws Exception {\n        waitForUserTaskAndExecuteIt(processInstance, STEP1_NAME, user);\n        getProcessAPI().updateProcessDataInstance(\"stringData\", processInstance.getId(), \"Excel\");\n\n        final Map<String, Serializable> result = getProcessAPI()\n                .evaluateExpressionsOnProcessInstance(processInstance.getId(), expressions);\n        assertFalse(\"Result should not be empty\", result.isEmpty());\n        assertEquals(\"Excel-Excel\", result.get(\"StringScript\"));\n        assertEquals(EXPECTED_EVALUATED_DATE, result.get(\"DateScript\"));\n        assertEquals(\"1plop\", result.get(\"LongScript\"));\n        assertEquals(\"2.0plop\", result.get(\"DoubleScript\"));\n        assertEquals(false, result.get(\"BooleanScript\"));\n        assertEquals(\"100.0plop\", result.get(\"FloatScript\"));\n        assertEquals(\"4plop\", result.get(\"IntegerScript\"));\n        assertEquals(\"processData\", result.get(\"processData\"));\n    }\n\n    @Test\n    public void evaluateExpressionsOnProcessInstanceWithCurrentValues() throws Exception {\n        waitForUserTaskAndExecuteIt(processInstance, STEP1_NAME, user);\n        getProcessAPI().updateProcessDataInstance(\"stringData\", processInstance.getId(), \"Excel\");\n\n        final Map<String, Serializable> result = getProcessAPI()\n                .evaluateExpressionsOnProcessInstance(processInstance.getId(), expressions);\n        assertEquals(\"Excel-Excel\", result.get(\"StringScript\"));\n        assertEquals(EXPECTED_EVALUATED_DATE, result.get(\"DateScript\"));\n        assertEquals(\"1plop\", result.get(\"LongScript\"));\n        assertEquals(\"2.0plop\", result.get(\"DoubleScript\"));\n        assertEquals(false, result.get(\"BooleanScript\"));\n        assertEquals(\"100.0plop\", result.get(\"FloatScript\"));\n        assertEquals(\"4plop\", result.get(\"IntegerScript\"));\n        assertEquals(\"processData\", result.get(\"processData\"));\n    }\n\n    @Test(expected = ExpressionEvaluationException.class)\n    public void evaluateExpressionsOnProcessInstanceWithWrongProcessInstanceId() throws Exception {\n        getProcessAPI().evaluateExpressionsOnProcessInstance(36, expressions);\n    }\n\n    @Test(expected = ExpressionEvaluationException.class)\n    public void evaluateExpressionsOnProcessInstanceWithNoExpressions() throws Exception {\n        getProcessAPI().evaluateExpressionsOnProcessInstance(1, null);\n    }\n\n    @Test(expected = ExpressionEvaluationException.class)\n    public void evaluateExpressionsOnProcessDefinition() throws Exception {\n        final Map<String, Serializable> result = getProcessAPI()\n                .evaluateExpressionsOnProcessDefinition(processDefinition.getId(), expressions);\n        assertFalse(\"Result should not be empty\", result.isEmpty());\n        assertEquals(\"Word\", result.get(\"StringScript\"));\n        assertEquals(EXPECTED_EVALUATED_DATE, result.get(\"DateScript\"));\n        assertEquals(\"1plop\", result.get(\"LongScript\"));\n        assertEquals(\"2.0plop\", result.get(\"DoubleScript\"));\n        assertEquals(false, result.get(\"BooleanScript\"));\n        assertEquals(\"100.0plop\", result.get(\"FloatScript\"));\n        assertEquals(\"4plop\", result.get(\"IntegerScript\"));\n        assertEquals(\"processData\", result.get(\"processData\"));\n    }\n\n    @Test(expected = ExpressionEvaluationException.class)\n    public void evaluateExpressionsOnProcessDefinitionWithWrongProcessDefinitionId() throws Exception {\n        getProcessAPI().evaluateExpressionsOnProcessDefinition(36, expressions);\n    }\n\n    @Test\n    public void evaluatePatternExpression() throws Exception {\n        final String dataName = \"birthYear\";\n        // get processInstance\n        final ProcessDefinition processDefinition = createAndDeployProcessDefinitionAndInstance(dataName, 1977, true,\n                user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n\n        final Expression expData = new ExpressionBuilder().createDataExpression(dataName, Integer.class.getName());\n        final Expression expConstantExpression = new ExpressionBuilder().createConstantStringExpression(\"year\");\n\n        final String messagePattern = \"My birth ${year} is ${birthYear}\";\n        final Expression expPattern = new ExpressionBuilder().createPatternExpression(\"TestEvaluatePatternExpression\",\n                messagePattern, expData,\n                expConstantExpression);\n\n        final Map<Expression, Map<String, Serializable>> expressions = new HashMap<>();\n        expressions.put(expPattern, null);\n        final Map<String, Serializable> result = getProcessAPI()\n                .evaluateExpressionsOnProcessInstance(processInstance.getId(), expressions);\n        assertEquals(\"My birth year is 1977\", result.get(messagePattern));\n\n        cleanup(processDefinition.getId());\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/FlowPatternsIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.process;\n\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.flownode.GatewayType;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.junit.Test;\n\n/**\n * Test common use case of flow pattern in bpmn\n *\n * @author Baptiste Mesta\n */\npublic class FlowPatternsIT extends TestWithUser {\n\n    @Test\n    public void process_with_inclusive() throws Exception {\n        final ProcessDefinition processDefinition = deployProcessWithInclusiveGateway();\n\n        //all conditions are true\n        ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        long step0 = waitForUserTask(\"Step0\");\n        getProcessAPI().updateProcessDataInstance(\"cond1\", processInstance.getId(), true);\n        getProcessAPI().updateProcessDataInstance(\"cond2\", processInstance.getId(), true);\n        getProcessAPI().updateProcessDataInstance(\"cond3\", processInstance.getId(), true);\n        getProcessAPI().updateProcessDataInstance(\"cond4\", processInstance.getId(), true);\n        assignAndExecuteStep(step0, user);\n        waitForUserTaskAndExecuteIt(processInstance, \"Step1\", user);\n        waitForUserTaskAndExecuteIt(processInstance, \"Step2\", user);\n        waitForUserTaskAndExecuteIt(processInstance, \"Step3\", user);\n        waitForUserTaskAndExecuteIt(processInstance, \"Step4\", user);\n        waitForUserTaskAndExecuteIt(processInstance, \"Step6\", user);\n        waitForProcessToFinish(processInstance.getId());\n        //all conditions are false\n        processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        step0 = waitForUserTask(\"Step0\");\n        getProcessAPI().updateProcessDataInstance(\"cond1\", processInstance.getId(), false);\n        getProcessAPI().updateProcessDataInstance(\"cond2\", processInstance.getId(), false);\n        getProcessAPI().updateProcessDataInstance(\"cond3\", processInstance.getId(), false);\n        getProcessAPI().updateProcessDataInstance(\"cond4\", processInstance.getId(), false);\n        assignAndExecuteStep(step0, user);\n        waitForUserTaskAndExecuteIt(processInstance, \"Step5\", user);\n        waitForUserTaskAndExecuteIt(processInstance, \"Step6\", user);\n        waitForProcessToFinish(processInstance.getId());\n        //some conditions are false\n        processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        step0 = waitForUserTask(\"Step0\");\n        getProcessAPI().updateProcessDataInstance(\"cond1\", processInstance.getId(), false);\n        getProcessAPI().updateProcessDataInstance(\"cond2\", processInstance.getId(), true);\n        getProcessAPI().updateProcessDataInstance(\"cond3\", processInstance.getId(), false);\n        getProcessAPI().updateProcessDataInstance(\"cond4\", processInstance.getId(), true);\n        assignAndExecuteStep(step0, user);\n        waitForUserTaskAndExecuteIt(processInstance, \"Step2\", user);\n        waitForUserTaskAndExecuteIt(processInstance, \"Step4\", user);\n        waitForUserTaskAndExecuteIt(processInstance, \"Step6\", user);\n        waitForProcessToFinish(processInstance.getId());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    ProcessDefinition deployProcessWithInclusiveGateway() throws Exception {\n        ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"InclusiveProcess\",\n                PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME);\n        builder.addBooleanData(\"cond1\", new ExpressionBuilder().createConstantBooleanExpression(true));\n        builder.addBooleanData(\"cond2\", new ExpressionBuilder().createConstantBooleanExpression(true));\n        builder.addBooleanData(\"cond3\", new ExpressionBuilder().createConstantBooleanExpression(true));\n        builder.addBooleanData(\"cond4\", new ExpressionBuilder().createConstantBooleanExpression(true));\n        builder.addStartEvent(\"Start\");\n        builder.addUserTask(\"Step0\", ACTOR_NAME);\n        builder.addUserTask(\"Step1\", ACTOR_NAME);\n        builder.addUserTask(\"Step2\", ACTOR_NAME);\n        builder.addUserTask(\"Step3\", ACTOR_NAME);\n        builder.addUserTask(\"Step4\", ACTOR_NAME);\n        builder.addUserTask(\"Step5\", ACTOR_NAME);\n        builder.addGateway(\"Gateway1\", GatewayType.INCLUSIVE);\n        builder.addGateway(\"Gateway2\", GatewayType.INCLUSIVE);\n        builder.addUserTask(\"Step6\", ACTOR_NAME);\n        builder.addEndEvent(\"End\");\n        builder.addTransition(\"Start\", \"Step0\");\n        builder.addTransition(\"Step0\", \"Gateway1\");\n        builder.addTransition(\"Gateway1\", \"Step1\",\n                new ExpressionBuilder().createDataExpression(\"cond1\", Boolean.class.getName()));\n        builder.addTransition(\"Gateway1\", \"Step2\",\n                new ExpressionBuilder().createDataExpression(\"cond2\", Boolean.class.getName()));\n        builder.addTransition(\"Gateway1\", \"Step3\",\n                new ExpressionBuilder().createDataExpression(\"cond3\", Boolean.class.getName()));\n        builder.addTransition(\"Gateway1\", \"Step4\",\n                new ExpressionBuilder().createDataExpression(\"cond4\", Boolean.class.getName()));\n        builder.addDefaultTransition(\"Gateway1\", \"Step5\");\n        builder.addTransition(\"Step1\", \"Gateway2\");\n        builder.addTransition(\"Step2\", \"Gateway2\");\n        builder.addTransition(\"Step3\", \"Gateway2\");\n        builder.addTransition(\"Step4\", \"Gateway2\");\n        builder.addTransition(\"Step5\", \"Gateway2\");\n        builder.addTransition(\"Gateway2\", \"Step6\");\n        builder.addTransition(\"Step6\", \"End\");\n        final DesignProcessDefinition designProcessDefinition = builder.getProcess();\n        return deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user);\n    }\n\n    @Test\n    public void process_that_merge_different_branches() throws Exception {\n        /*\n         * step 1 and 2 are merged into step 4 by gateway 2 and step 3 and step4 are merged by gateway3\n         */\n        ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"MultipleMergeProcess\",\n                PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME);\n        builder.addStartEvent(\"Start\");\n        builder.addUserTask(\"Step1\", ACTOR_NAME);\n        builder.addUserTask(\"Step2\", ACTOR_NAME);\n        builder.addUserTask(\"Step3\", ACTOR_NAME);\n        builder.addUserTask(\"Step4\", ACTOR_NAME);\n        builder.addUserTask(\"Step5\", ACTOR_NAME);\n        builder.addGateway(\"Gateway1\", GatewayType.PARALLEL);\n        builder.addGateway(\"Gateway2\", GatewayType.PARALLEL);\n        builder.addGateway(\"Gateway3\", GatewayType.INCLUSIVE);\n        builder.addEndEvent(\"End\");\n        builder.addTransition(\"Start\", \"Gateway1\");\n        builder.addTransition(\"Gateway1\", \"Step1\");\n        builder.addTransition(\"Gateway1\", \"Step2\");\n        builder.addTransition(\"Gateway1\", \"Step3\");\n        builder.addTransition(\"Step1\", \"Gateway2\");\n        builder.addTransition(\"Step2\", \"Gateway2\");\n        builder.addTransition(\"Gateway2\", \"Step4\");\n        builder.addTransition(\"Step3\", \"Gateway3\");\n        builder.addTransition(\"Step4\", \"Gateway3\");\n        builder.addTransition(\"Gateway3\", \"Step5\");\n        builder.addTransition(\"Step5\", \"End\");\n        final DesignProcessDefinition designProcessDefinition = builder.getProcess();\n        ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        waitForUserTaskAndExecuteIt(\"Step1\", user);\n        waitForUserTaskAndExecuteIt(\"Step2\", user);\n        waitForUserTaskAndExecuteIt(\"Step4\", user);\n        waitForUserTaskAndExecuteIt(\"Step3\", user);\n        waitForUserTaskAndExecuteIt(\"Step5\", user);\n        waitForProcessToFinish(processInstance);\n\n        disableAndDeleteProcess(processDefinition);\n\n    }\n\n    @Test\n    public void process_with_branch_out() throws Exception {\n        ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"MultipleMergeProcess\",\n                PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME);\n        builder.addStartEvent(\"Start\");\n        builder.addUserTask(\"Step1\", ACTOR_NAME);\n        builder.addUserTask(\"Step2\", ACTOR_NAME);\n        builder.addUserTask(\"Step3\", ACTOR_NAME);\n        builder.addUserTask(\"Step4\", ACTOR_NAME);\n        builder.addGateway(\"Gateway1\", GatewayType.PARALLEL);\n        builder.addGateway(\"Gateway2\", GatewayType.PARALLEL);\n        builder.addEndEvent(\"End1\");\n        builder.addEndEvent(\"End2\");\n        builder.addTransition(\"Start\", \"Gateway1\");\n        builder.addTransition(\"Gateway1\", \"Step1\");\n        builder.addTransition(\"Gateway1\", \"Step2\");\n        builder.addTransition(\"Step1\", \"Gateway2\");\n        builder.addTransition(\"Step2\", \"Gateway2\");\n        builder.addTransition(\"Step2\", \"Step3\");\n        builder.addTransition(\"Gateway2\", \"Step4\");\n        builder.addTransition(\"Step4\", \"End1\");\n        builder.addTransition(\"Step3\", \"End2\");\n        final DesignProcessDefinition designProcessDefinition = builder.getProcess();\n        ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        waitForUserTaskAndExecuteIt(\"Step2\", user);\n        waitForUserTaskAndExecuteIt(\"Step1\", user);\n        waitForUserTaskAndExecuteIt(\"Step4\", user);\n        waitForUserTaskAndExecuteIt(\"Step3\", user);\n        waitForProcessToFinish(processInstance);\n\n        disableAndDeleteProcess(processDefinition);\n\n    }\n\n    @Test\n    public void inclusive_merge_with_no_start() throws Exception {\n        ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"MultipleMergeProcess\",\n                PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME);\n        builder.addUserTask(\"Step1\", ACTOR_NAME);\n        builder.addUserTask(\"Step2\", ACTOR_NAME);\n        builder.addUserTask(\"Step3\", ACTOR_NAME);\n        builder.addGateway(\"Gateway1\", GatewayType.INCLUSIVE);\n        builder.addTransition(\"Step1\", \"Gateway1\");\n        builder.addTransition(\"Step2\", \"Gateway1\");\n        builder.addTransition(\"Gateway1\", \"Step3\");\n        final DesignProcessDefinition designProcessDefinition = builder.getProcess();\n        ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        waitForUserTaskAndExecuteIt(\"Step1\", user);\n        waitForUserTaskAndExecuteIt(\"Step2\", user);\n        waitForUserTaskAndExecuteIt(\"Step3\", user);\n        waitForProcessToFinish(processInstance);\n\n        disableAndDeleteProcess(processDefinition);\n\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/GatewayExecutionIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.process;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.*;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.api.PlatformAPI;\nimport org.bonitasoft.engine.api.PlatformAPIAccessor;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\nimport org.bonitasoft.engine.bpm.flownode.GatewayInstance;\nimport org.bonitasoft.engine.bpm.flownode.GatewayType;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.TimerType;\nimport org.bonitasoft.engine.bpm.process.ActivationState;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.expression.ComparisonOperator;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.expression.InvalidExpressionException;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.PlatformSession;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.bonitasoft.engine.test.WaitUntil;\nimport org.bonitasoft.engine.test.check.CheckNbPendingTaskOf;\nimport org.junit.Test;\n\npublic class GatewayExecutionIT extends TestWithUser {\n\n    private Expression trueExpression;\n\n    private Expression falseExpression;\n\n    @Test\n    public void archiveGatewayInstance() throws Exception {\n        createTrueAndFalseExpression();\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"My_Process\",\n                PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME).addDescription(\"description\");\n        builder.addAutomaticTask(\"step1\");\n        builder.addUserTask(\"step2\", ACTOR_NAME);\n        builder.addUserTask(\"step3\", ACTOR_NAME);\n        builder.addUserTask(\"step4\", ACTOR_NAME);\n        builder.addGateway(\"d'stàp\", GatewayType.INCLUSIVE)\n                .addDisplayDescriptionAfterCompletion(\n                        new ExpressionBuilder().createConstantStringExpression(\"description after completion\"))\n                .addDisplayName(new ExpressionBuilder().createConstantStringExpression(\"display name\"));\n        builder.addTransition(\"step1\", \"d'stàp\");\n        builder.addTransition(\"d'stàp\", \"step2\", falseExpression);\n        builder.addTransition(\"d'stàp\", \"step3\", falseExpression)\n                .addDefaultTransition(\"d'stàp\", \"step4\");\n        final DesignProcessDefinition designProcessDefinition = builder.getProcess();\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        // create gateway instance and transition instance and archive them\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n\n        waitForUserTask(processInstance, \"step4\");\n\n        // test gateway instance, gateway instance has been deleted after archive\n        final SearchOptionsBuilder builder0 = new SearchOptionsBuilder(0, 10);\n        builder0.filter(FlowNodeInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID, processInstance.getId());\n        builder0.filter(FlowNodeInstanceSearchDescriptor.STATE_NAME, \"completed\");\n        builder0.filter(FlowNodeInstanceSearchDescriptor.NAME, \"d'stàp\");\n        final SearchResult<FlowNodeInstance> searchResult0 = getProcessAPI().searchFlowNodeInstances(builder0.done());\n        assertEquals(0, searchResult0.getCount());\n\n        // search archive gateway instances:\n        SearchOptionsBuilder builder1 = new SearchOptionsBuilder(0, 10);\n        builder1.filter(ArchivedFlowNodeInstanceSearchDescriptor.FLOW_NODE_TYPE, \"gate\");\n        builder1.filter(ArchivedFlowNodeInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID, processInstance.getId());\n        final SearchResult<ArchivedFlowNodeInstance> searchResult1 = getProcessAPI()\n                .searchArchivedFlowNodeInstances(builder1.done());\n        // we expect all normal gateway states to be archived:\n        assertEquals(getProcessAPI().getSupportedStates(FlowNodeType.GATEWAY).size(), searchResult1.getCount());\n\n        // check display name/description\n        builder1 = new SearchOptionsBuilder(0, 10);\n        builder1.filter(ArchivedFlowNodeInstanceSearchDescriptor.FLOW_NODE_TYPE, \"gate\");\n        builder1.filter(ArchivedFlowNodeInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID, processInstance.getId());\n        builder1.filter(ArchivedFlowNodeInstanceSearchDescriptor.NAME, \"d'stàp\");\n        builder1.filter(ArchivedFlowNodeInstanceSearchDescriptor.STATE_NAME, \"completed\");\n        final ArchivedFlowNodeInstance gatewayOne = getProcessAPI().searchArchivedFlowNodeInstances(builder1.done())\n                .getResult().get(0);\n        // we expect all normal gateway states to be archived:\n        assertThat(gatewayOne.getDisplayName()).isEqualTo(\"display name\");\n        assertThat(gatewayOne.getDisplayDescription()).isEqualTo(\"description after completion\");\n\n        // clean\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    /**\n     * Parallel gateway with\n     * 1 automatic step in input\n     * 2 user tasks in output\n     * > 2 tasks for john expected\n     */\n    @Test\n    public void processWithParallelGatewaySplit() throws Exception {\n        // test initialization (will be extracted in other methods\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process_with_parallel_gateway\", PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addAutomaticTask(\"step1\").addUserTask(\"step2\", ACTOR_NAME).addUserTask(\"step3\", ACTOR_NAME)\n                .addGateway(\"gateway1\", GatewayType.PARALLEL)\n                .addTransition(\"step1\", \"gateway1\").addTransition(\"gateway1\", \"step2\")\n                .addTransition(\"gateway1\", \"step3\").getProcess();\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        // test execution\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n        // we should have 2 elements ready:\n        final long step2Id = waitForUserTask(processInstance, \"step2\");\n        final long step3Id = waitForUserTask(processInstance, \"step3\");\n\n        assignAndExecuteStep(step2Id, user);\n        assignAndExecuteStep(step3Id, user);\n        waitForProcessToFinish(processInstance);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    /**\n     * Parallel gateway with\n     * 2 automatic step in input\n     * 1 user task in output\n     * > 1 task for john expected\n     */\n    @Test\n    public void processWithParallelGatewayMerge() throws Exception {\n        //given\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process_with_parallel_gateway\", PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addAutomaticTask(\"step1\").addAutomaticTask(\"step2\").addAutomaticTask(\"step3\")\n                .addUserTask(\"step4\", ACTOR_NAME)\n                .addGateway(\"gateway1\", GatewayType.PARALLEL).addGateway(\"gateway2\", GatewayType.PARALLEL)\n                .addTransition(\"step1\", \"gateway1\")\n                .addTransition(\"gateway1\", \"step2\").addTransition(\"gateway1\", \"step3\")\n                .addTransition(\"step2\", \"gateway2\").addTransition(\"step3\", \"gateway2\")\n                .addTransition(\"gateway2\", \"step4\").getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n\n        // when\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n\n        // then\n        waitForUserTaskAndExecuteIt(processInstance, \"step4\", user);\n        waitForProcessToFinish(processInstance);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    /**\n     * Parallel gateway with\n     * 2 automatic step in input\n     * 1 user task in output\n     * > 1 task for john expected\n     */\n    @Test\n    public void processMultiMerge() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process_with_parallel_gateway\", PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addAutomaticTask(\"step1\").addAutomaticTask(\"step2\").addAutomaticTask(\"step3\")\n                .addUserTask(\"step4\", ACTOR_NAME)\n                .addGateway(\"gateway1\", GatewayType.PARALLEL).addTransition(\"step1\", \"gateway1\")\n                .addTransition(\"gateway1\", \"step2\")\n                .addTransition(\"gateway1\", \"step3\").addTransition(\"step2\", \"step4\").addTransition(\"step3\", \"step4\")\n                .getProcess();\n        assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, \"step4\", \"step4\");\n    }\n\n    /**\n     * Parallel gateway with\n     * used as a XOR Gateway\n     * Exception expected\n     */\n    @Test(expected = InvalidProcessDefinitionException.class)\n    public void parallelWithConditionalTransitions() throws Exception {\n        createTrueAndFalseExpression();\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process_with_parallel_gateway\", PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addAutomaticTask(\"step1\")\n                .addUserTask(\"step2\", ACTOR_NAME).addUserTask(\"step3\", ACTOR_NAME)\n                .addGateway(\"gateway1\", GatewayType.PARALLEL)\n                .addTransition(\"step1\", \"gateway1\").addTransition(\"gateway1\", \"step2\", trueExpression)\n                .addTransition(\"gateway1\", \"step3\", falseExpression)\n                .getProcess();\n        assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, \"step2\", \"step3\");\n    }\n\n    /*\n     * Exclusive gateway with\n     * unconditionnal output transitions\n     * Expected : step 2\n     */\n    @Test\n    public void exclusiveWithUnconditionalTransitions() throws Exception {\n        createTrueAndFalseExpression();\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process_with_exclusive_gateway\", PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addAutomaticTask(\"step1\")\n                .addUserTask(\"step2\", ACTOR_NAME).addUserTask(\"step3\", ACTOR_NAME)\n                .addGateway(\"gateway1\", GatewayType.EXCLUSIVE)\n                .addTransition(\"step1\", \"gateway1\").addTransition(\"gateway1\", \"step2\")\n                .addTransition(\"gateway1\", \"step3\").getProcess();\n        assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, \"step2\");\n    }\n\n    /*\n     * Inclusive gateway with\n     * unconditionnal output transitions\n     * Expected : step2, step3\n     */\n    @Test\n    public void inclusiveWithUnconditionalTransitions() throws Exception {\n        createTrueAndFalseExpression();\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process_with_inclusive_gateway\", PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addAutomaticTask(\"step1\")\n                .addUserTask(\"step2\", ACTOR_NAME).addUserTask(\"step3\", ACTOR_NAME)\n                .addGateway(\"gateway1\", GatewayType.INCLUSIVE)\n                .addTransition(\"step1\", \"gateway1\").addTransition(\"gateway1\", \"step2\")\n                .addTransition(\"gateway1\", \"step3\").getProcess();\n        assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, \"step2\", \"step3\");\n    }\n\n    /**\n     * Linear process\n     * with auto task -> user task -> auto task -> user task\n     * 1 task pending + 1 task pending for John\n     */\n    @Test\n    public void linearProcessWith2UserTasks() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process_with_parallel_gateway\", PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addAutomaticTask(\"step1\")\n                .addUserTask(\"step2\", ACTOR_NAME).addAutomaticTask(\"step3\").addUserTask(\"step4\", ACTOR_NAME)\n                .addTransition(\"step1\", \"step2\")\n                .addTransition(\"step2\", \"step3\").addTransition(\"step3\", \"step4\").getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n        // we should have 2 elements ready:\n        final CheckNbPendingTaskOf checkNbPendingTaskOf = new CheckNbPendingTaskOf(getProcessAPI(), 300, 5000, true, 1,\n                user);\n        assertTrue(\"there was no pending task for john (expected step2)\", checkNbPendingTaskOf.waitUntil());\n        final List<HumanTaskInstance> pendingTasks = getProcessAPI().getPendingHumanTaskInstances(user.getId(), 0, 10,\n                null);\n        assertEquals(1, pendingTasks.size());\n        final HumanTaskInstance humanTaskInstance = pendingTasks.get(0);\n        assertEquals(\"step2\", humanTaskInstance.getName());\n        assignAndExecuteStep(humanTaskInstance, user.getId());\n\n        assertTrue(\"there was no pending task for john (expected step4)\", checkNbPendingTaskOf.waitUntil());\n        final List<HumanTaskInstance> pendingTasks2 = getProcessAPI().getPendingHumanTaskInstances(user.getId(), 0, 10,\n                null);\n        assertEquals(1, pendingTasks2.size());\n        final HumanTaskInstance humanTaskInstance2 = pendingTasks2.get(0);\n        assertEquals(\"step4\", humanTaskInstance2.getName());\n        assignAndExecuteStep(humanTaskInstance2, user.getId());\n\n        waitForProcessToFinish(processInstance);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    /**\n     * Exclusive gateway with\n     * 1 automatic step in input\n     * 1 user task in output\n     * > 1 task for john expected\n     */\n    @Test\n    public void processWithExclusiveGatewayWith1InputAnd1Output() throws Exception {\n        createTrueAndFalseExpression();\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process_with_parallel_gateway\", PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addAutomaticTask(\"step1\").addUserTask(\"step2\", ACTOR_NAME)\n                .addGateway(\"gateway1\", GatewayType.EXCLUSIVE).addTransition(\"step1\", \"gateway1\")\n                .addTransition(\"gateway1\", \"step2\", trueExpression).getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n        // test execution\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n        // we should have 1 elements ready:\n        waitForUserTaskAndExecuteIt(processInstance, \"step2\", user);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void processWithExclusiveAndInclusive() throws Exception {\n        createTrueAndFalseExpression();\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process_with_parallel_gateway\", PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addStartEvent(\"start\")\n                .addUserTask(\"step1\", ACTOR_NAME).addGateway(\"para1\", GatewayType.PARALLEL).addAutomaticTask(\"step2\")\n                .addGateway(\"inclu1\", GatewayType.INCLUSIVE).addAutomaticTask(\"step3\").addAutomaticTask(\"step4\")\n                .addGateway(\"inclu2\", GatewayType.INCLUSIVE)\n                .addEndEvent(\"end\").addTerminateEventTrigger().addGateway(\"para2\", GatewayType.PARALLEL)\n                .addTransition(\"start\", \"step1\")\n                .addTransition(\"step1\", \"para1\").addTransition(\"para1\", \"step2\").addTransition(\"para1\", \"inclu1\")\n                .addTransition(\"inclu1\", \"step3\", new ExpressionBuilder().createConstantBooleanExpression(true))\n                .addTransition(\"inclu1\", \"step4\", new ExpressionBuilder().createConstantBooleanExpression(false))\n                .addTransition(\"step4\", \"inclu2\")\n                .addTransition(\"step3\", \"inclu2\").addTransition(\"inclu2\", \"para2\").addTransition(\"step2\", \"para2\")\n                .addTransition(\"para2\", \"end\").getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n        waitForUserTaskAndExecuteIt(startProcess, \"step1\", user);\n        waitForProcessToFinish(startProcess);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    /**\n     * exclusive gateway\n     * 3 output:\n     * step2 condition = true\n     * step3 condition = true\n     * step4 = default\n     * expected step2\n     */\n    @Test\n    public void exclusiveSplit1() throws Exception {\n        createTrueAndFalseExpression();\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process_with_parallel_gateway\", PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addAutomaticTask(\"step1\").addUserTask(\"step2\", ACTOR_NAME).addUserTask(\"step3\", ACTOR_NAME)\n                .addUserTask(\"step4\", ACTOR_NAME)\n                .addGateway(\"gateway1\", GatewayType.EXCLUSIVE).addTransition(\"step1\", \"gateway1\")\n                .addTransition(\"gateway1\", \"step2\", trueExpression)\n                .addTransition(\"gateway1\", \"step3\", trueExpression).addDefaultTransition(\"gateway1\", \"step4\")\n                .getProcess();\n        assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, \"step2\");\n    }\n\n    /**\n     * exclusive gateway\n     * 3 output:\n     * step3 condition = true\n     * step2 condition = true\n     * step4 = default\n     * expected step3\n     */\n    @Test\n    public void exclusiveSplit1bis() throws Exception {\n        createTrueAndFalseExpression();\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process_with_parallel_gateway\", PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addAutomaticTask(\"step1\").addUserTask(\"step2\", ACTOR_NAME).addUserTask(\"step3\", ACTOR_NAME)\n                .addUserTask(\"step4\", ACTOR_NAME)\n                .addGateway(\"gateway1\", GatewayType.EXCLUSIVE).addTransition(\"step1\", \"gateway1\")\n                .addTransition(\"gateway1\", \"step3\", trueExpression)\n                .addTransition(\"gateway1\", \"step2\", trueExpression).addDefaultTransition(\"gateway1\", \"step4\")\n                .getProcess();\n        assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, \"step3\");\n    }\n\n    /**\n     * exclusive gateway\n     * 3 output:\n     * step2 condition = false\n     * step3 condition = true\n     * step4 = default\n     * expected step3\n     */\n    @Test\n    public void exclusiveSplit2() throws Exception {\n        createTrueAndFalseExpression();\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process_with_parallel_gateway\", PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addAutomaticTask(\"step1\").addUserTask(\"step2\", ACTOR_NAME).addUserTask(\"step3\", ACTOR_NAME)\n                .addUserTask(\"step4\", ACTOR_NAME)\n                .addGateway(\"gateway1\", GatewayType.EXCLUSIVE).addTransition(\"step1\", \"gateway1\")\n                .addTransition(\"gateway1\", \"step2\", falseExpression)\n                .addTransition(\"gateway1\", \"step3\", trueExpression).addDefaultTransition(\"gateway1\", \"step4\")\n                .getProcess();\n        assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, \"step3\");\n    }\n\n    /**\n     * exclusive gateway\n     * 3 output:\n     * step2 condition = false\n     * step3 condition = false\n     * step4 = default\n     * expected step4\n     */\n    @Test\n    public void exclusiveSplit3() throws Exception {\n        createTrueAndFalseExpression();\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process_with_parallel_gateway\", PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addAutomaticTask(\"step1\").addUserTask(\"step2\", ACTOR_NAME).addUserTask(\"step3\", ACTOR_NAME)\n                .addUserTask(\"step4\", ACTOR_NAME)\n                .addGateway(\"gateway1\", GatewayType.EXCLUSIVE).addTransition(\"step1\", \"gateway1\")\n                .addTransition(\"gateway1\", \"step2\", falseExpression)\n                .addTransition(\"gateway1\", \"step3\", falseExpression).addDefaultTransition(\"gateway1\", \"step4\")\n                .getProcess();\n        assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, \"step4\");\n    }\n\n    /**\n     * exclusive gateway\n     * 3 output:\n     * step2 condition = false\n     * step3 condition = false\n     * expected exception: no default gateway is defined\n     */\n    @Test\n    public void exclusiveSplitWithNoDefaultTransition() throws Exception {\n        createTrueAndFalseExpression();\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process_with_parallel_gateway\", PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addAutomaticTask(\"step1\").addUserTask(\"step2\", ACTOR_NAME).addUserTask(\"step3\", ACTOR_NAME)\n                .addGateway(\"gateway1\", GatewayType.EXCLUSIVE)\n                .addTransition(\"step1\", \"gateway1\").addTransition(\"gateway1\", \"step2\", falseExpression)\n                .addTransition(\"gateway1\", \"step3\", falseExpression)\n                .getProcess();\n        assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, \"\");\n    }\n\n    /**\n     * exclusive gateway\n     * 1 output:\n     * step2 condition = false\n     * gateway fail\n     * fix and restart gateway\n     * expected step2 ready\n     */\n    @Test\n    public void exclusiveSplitWithNoDefaultTransitionFailThenRestart() throws Exception {\n        createTrueAndFalseExpression();\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance(\n                \"My_Process_with_parallel_gateway\",\n                PROCESS_VERSION);\n        processDefinitionBuilder.addActor(\"actor\").addAutomaticTask(\"step1\").addUserTask(\"step2\", \"actor\")\n                .addGateway(\"gateway1\", GatewayType.EXCLUSIVE)\n                .addTransition(\"step1\", \"gateway1\")\n                .addTransition(\"gateway1\", \"step2\",\n                        new ExpressionBuilder().createDataExpression(\"condition\", Boolean.class.getName()))\n                .getProcess();\n        processDefinitionBuilder.addData(\"condition\", Boolean.class.getName(), falseExpression);\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),\n                \"actor\", user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        // the gateway failed\n        final FlowNodeInstance gateway = waitForFlowNodeInFailedState(processInstance);\n        final SearchOptions searchOptions = new SearchOptionsBuilder(0, 1)\n                .filter(FlowNodeInstanceSearchDescriptor.NAME, \"gateway1\").done();\n        final SearchResult<FlowNodeInstance> searchFlowNodeInstances = getProcessAPI()\n                .searchFlowNodeInstances(searchOptions);\n        final FlowNodeInstance flowNodeInstance = searchFlowNodeInstances.getResult().get(0);\n        assertTrue(flowNodeInstance instanceof GatewayInstance);\n        // retry the gateway\n        getProcessAPI().retryTask(gateway.getId());\n        waitForFlowNodeInFailedState(processInstance);\n        // should still be in failed\n        final SearchResult<FlowNodeInstance> searchFlowNodeInstances2 = getProcessAPI()\n                .searchFlowNodeInstances(searchOptions);\n        assertEquals(\"failed\", searchFlowNodeInstances2.getResult().get(0).getState());\n\n        // change value of condition to make it work\n        getProcessAPI().updateProcessDataInstance(\"condition\", processInstance.getId(), true);\n        // retry the gateway\n        getProcessAPI().retryTask(gateway.getId());\n        // we should have step2 ready\n        waitForUserTask(processInstance, \"step2\");\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    /**\n     * inclusive gateway\n     * 3 output:\n     * step2 condition = true\n     * step3 condition = true\n     * step4 = default\n     * expected step2 + step3\n     */\n    @Test\n    public void inclusiveSplit1() throws Exception {\n        createTrueAndFalseExpression();\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process_with_parallel_gateway\", PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addAutomaticTask(\"step1\")\n                .addUserTask(\"step2\", ACTOR_NAME).addUserTask(\"step3\", ACTOR_NAME).addUserTask(\"step4\", ACTOR_NAME)\n                .addGateway(\"gateway1\", GatewayType.INCLUSIVE).addTransition(\"step1\", \"gateway1\")\n                .addTransition(\"gateway1\", \"step2\", trueExpression)\n                .addTransition(\"gateway1\", \"step3\", trueExpression).addDefaultTransition(\"gateway1\", \"step4\")\n                .getProcess();\n        assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, \"step2\", \"step3\");\n    }\n\n    /**\n     * inclusive gateway\n     * 3 output:\n     * step2 condition = false\n     * step3 condition = true\n     * step4 = default\n     * expected step3\n     */\n    @Test\n    public void inclusiveSplit2() throws Exception {\n        createTrueAndFalseExpression();\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process_with_parallel_gateway\", PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addAutomaticTask(\"step1\").addUserTask(\"step2\", ACTOR_NAME).addUserTask(\"step3\", ACTOR_NAME)\n                .addUserTask(\"step4\", ACTOR_NAME)\n                .addGateway(\"gateway1\", GatewayType.INCLUSIVE).addTransition(\"step1\", \"gateway1\")\n                .addTransition(\"gateway1\", \"step2\", falseExpression)\n                .addTransition(\"gateway1\", \"step3\", trueExpression).addDefaultTransition(\"gateway1\", \"step4\")\n                .getProcess();\n        assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, \"step3\");\n    }\n\n    /**\n     * inclusive gateway\n     * 3 output:\n     * step2 condition = false\n     * step3 condition = true\n     * step4 = default\n     * expected step3\n     */\n    @Test\n    public void inclusiveSplit2WithDataAsCondition() throws Exception {\n        createTrueAndFalseExpression();\n        final Expression trueData = new ExpressionBuilder().createDataExpression(\"trueData\", Boolean.class.getName());\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance(\n                \"My_Process_with_parallel_gateway\",\n                PROCESS_VERSION);\n        processDefinitionBuilder.addBooleanData(\"trueData\", trueExpression);\n        final DesignProcessDefinition designProcessDefinition = processDefinitionBuilder\n                .addActor(ACTOR_NAME)\n\n                .addAutomaticTask(\"step1\")\n                .addUserTask(\"step2\", ACTOR_NAME)\n                .addUserTask(\"step3\", ACTOR_NAME)\n                .addUserTask(\"step4\", ACTOR_NAME)\n                .addGateway(\"gateway1\", GatewayType.INCLUSIVE)\n                .addTransition(\"step1\", \"gateway1\")\n                .addTransition(\n                        \"gateway1\",\n                        \"step2\",\n                        new ExpressionBuilder().createGroovyScriptExpression(\"inclusiveSplit2WithDataAsCondition\",\n                                \"!trueData\", Boolean.class.getName(),\n                                Arrays.asList(trueData)))\n                .addTransition(\"gateway1\", \"step3\", trueData).addDefaultTransition(\"gateway1\", \"step4\").getProcess();\n        assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, \"step3\");\n    }\n\n    /**\n     * exclusive gateway\n     * 3 output:\n     * step2 condition = false\n     * step3 condition = false\n     * step4 = default\n     * expected step4\n     */\n    @Test\n    public void inclusiveSplit3() throws Exception {\n        createTrueAndFalseExpression();\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process_with_parallel_gateway\", PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addAutomaticTask(\"step1\").addUserTask(\"step2\", ACTOR_NAME).addUserTask(\"step3\", ACTOR_NAME)\n                .addUserTask(\"step4\", ACTOR_NAME)\n                .addGateway(\"gateway1\", GatewayType.INCLUSIVE).addTransition(\"step1\", \"gateway1\")\n                .addTransition(\"gateway1\", \"step2\", falseExpression)\n                .addTransition(\"gateway1\", \"step3\", falseExpression).addDefaultTransition(\"gateway1\", \"step4\")\n                .getProcess();\n        assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, \"step4\");\n    }\n\n    /**\n     * exclusive gateway\n     * 3 output:\n     * step2 condition = false\n     * step3 condition = false\n     * expected Exception: no default transition on gateway\n     */\n    @Test\n    public void inclusiveSplitWithNoDefaultTransition() throws Exception {\n        createTrueAndFalseExpression();\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process_with_parallel_gateway\", PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addAutomaticTask(\"step1\").addUserTask(\"step2\", ACTOR_NAME).addUserTask(\"step3\", ACTOR_NAME)\n                .addGateway(\"gateway1\", GatewayType.INCLUSIVE)\n                .addTransition(\"step1\", \"gateway1\").addTransition(\"gateway1\", \"step2\", falseExpression)\n                .addTransition(\"gateway1\", \"step3\", falseExpression)\n                .getProcess();\n        assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, \"\");\n    }\n\n    /**\n     * Inclusive then Inclusive Gateway\n     * expected step5\n     */\n    @Test\n    public void inclusiveSplitThenInclusiveMerge() throws Exception {\n        createTrueAndFalseExpression();\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process_with_parallel_gateway\", PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addAutomaticTask(\"step1\")\n                .addAutomaticTask(\"step2\").addAutomaticTask(\"step3\").addUserTask(\"step4\", ACTOR_NAME)\n                .addUserTask(\"step5\", ACTOR_NAME)\n                .addGateway(\"gateway1\", GatewayType.INCLUSIVE).addGateway(\"gateway2\", GatewayType.INCLUSIVE)\n                .addTransition(\"step1\", \"gateway1\")\n                .addTransition(\"gateway1\", \"step2\", trueExpression).addTransition(\"gateway1\", \"step3\", trueExpression)\n                .addDefaultTransition(\"gateway1\", \"step4\").addTransition(\"step2\", \"gateway2\")\n                .addTransition(\"step3\", \"gateway2\")\n                .addTransition(\"step4\", \"gateway2\").addTransition(\"gateway2\", \"step5\").getProcess();\n        assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, \"step5\");\n    }\n\n    /**\n     * Parallel then Inclusive Gateway\n     * step 1 -> parallel gateway1 -> ( step 2, step3 ) -> inclusive gateway2 -> step4\n     * Dynamic expected : step2 & step3 pending, exec step2, step3 pending , exec step3, step4 pending , exec step4, end\n     * process.\n     */\n    @Test\n    public void parallelSplitThenInclusiveMerge() throws Exception {\n        createTrueAndFalseExpression();\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process_with_parallel_gateway\", PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addAutomaticTask(\"step1\").addUserTask(\"step2\", ACTOR_NAME).addUserTask(\"step3\", ACTOR_NAME)\n                .addUserTask(\"step4\", ACTOR_NAME)\n                .addGateway(\"gateway1\", GatewayType.PARALLEL).addGateway(\"gateway2\", GatewayType.INCLUSIVE)\n                .addTransition(\"step1\", \"gateway1\")\n                .addTransition(\"gateway1\", \"step2\").addTransition(\"gateway1\", \"step3\")\n                .addTransition(\"step2\", \"gateway2\").addTransition(\"step3\", \"gateway2\")\n                .addTransition(\"gateway2\", \"step4\").getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n        // test execution\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n\n        // we should have 2 elements ready:\n        final long step3Id = waitForUserTask(processInstance, \"step3\");\n        waitForUserTaskAndExecuteIt(processInstance, \"step2\", user);\n\n        final CheckNbPendingTaskOf checkNbPendingTaskOf2 = new CheckNbPendingTaskOf(getProcessAPI(), 50, 2000, true, 2,\n                user);\n        assertFalse(\"there was no pending task for john (expected step3)\", checkNbPendingTaskOf2.waitUntil());\n\n        assignAndExecuteStep(step3Id, user);\n        waitForUserTaskAndExecuteIt(processInstance, \"step4\", user);\n        waitForProcessToFinish(processInstance);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    /**\n     * Notify inclusive gateway that a branch died\n     * inclusive1 -> step1+step2 -> inclusive2 -> step3\n     * complete step2\n     * step1 is interrupted by a boundary\n     * exception path of boundary is finished\n     * expected: step3 is active because the inclusive2 was notified\n     */\n    @Test\n    public void notifyInclusiveGatewayThatABranchDied() throws Exception {\n        createTrueAndFalseExpression();\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"NotifiedBranchDeadProcess\", PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME);\n        builder.addStartEvent(\"start\");\n        builder.addGateway(\"inclusive1\", GatewayType.INCLUSIVE);\n        builder.addUserTask(\"step1\", ACTOR_NAME).addBoundaryEvent(\"signal\", true).addSignalEventTrigger(\"bip\");\n        builder.addUserTask(\"exceptionStep\", ACTOR_NAME);\n        builder.addUserTask(\"step2\", ACTOR_NAME);\n        builder.addGateway(\"inclusive2\", GatewayType.INCLUSIVE);\n        builder.addUserTask(\"step3\", ACTOR_NAME);\n        builder.addTransition(\"start\", \"inclusive1\");\n        builder.addTransition(\"inclusive1\", \"step1\");\n        builder.addTransition(\"inclusive1\", \"step2\");\n        builder.addTransition(\"step1\", \"inclusive2\");\n        builder.addTransition(\"signal\", \"exceptionStep\");\n        builder.addTransition(\"step2\", \"inclusive2\");\n        builder.addTransition(\"inclusive2\", \"step3\");\n        final DesignProcessDefinition designProcessDefinition = builder.getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n\n        // execute step2\n        waitForUserTask(processInstance, \"step1\");\n        waitForUserTaskAndExecuteIt(processInstance, \"step2\", user);\n\n        // send signal to trigger boundary\n        getProcessAPI().sendSignal(\"bip\");\n        // wait and execute exceptionStep\n        waitForUserTaskAndExecuteIt(processInstance, \"exceptionStep\", user);\n\n        // step3 should be ready\n        waitForUserTaskAndExecuteIt(processInstance, \"step3\", user);\n        waitForProcessToFinish(processInstance);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    /**\n     * Notify inclusive gateway that a branch died\n     * inclusive1 -> step1+step2 -> inclusive2 -> step3\n     * complete step2\n     * step1 is interrupted by a boundary\n     * exception path of boundary is finished\n     * expected: step3 is active because the inclusive2 was notified\n     */\n    @Test\n    public void inclusiveGatewayWithNonInterruptingBoundary() throws Exception {\n        createTrueAndFalseExpression();\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"NotifiedBranchDeadProcess\", PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME);\n        builder.addStartEvent(\"start\");\n        builder.addGateway(\"inclusive1\", GatewayType.INCLUSIVE);\n        builder.addUserTask(\"step1\", ACTOR_NAME).addBoundaryEvent(\"timer\", false)\n                .addTimerEventTriggerDefinition(TimerType.DURATION,\n                        new ExpressionBuilder().createConstantLongExpression(1));\n        builder.addUserTask(\"exceptionStep\", ACTOR_NAME);\n        builder.addUserTask(\"step2\", ACTOR_NAME);\n        builder.addGateway(\"inclusive2\", GatewayType.INCLUSIVE);\n        builder.addUserTask(\"step3\", ACTOR_NAME);\n        builder.addTransition(\"start\", \"inclusive1\");\n        builder.addTransition(\"inclusive1\", \"step1\");\n        builder.addTransition(\"inclusive1\", \"step2\");\n        builder.addTransition(\"step1\", \"inclusive2\");\n        builder.addTransition(\"timer\", \"exceptionStep\");\n        builder.addTransition(\"step2\", \"inclusive2\");\n        builder.addTransition(\"inclusive2\", \"step3\");\n        final DesignProcessDefinition designProcessDefinition = builder.getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n\n        // execute step2\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n        waitForUserTaskAndExecuteIt(processInstance, \"step2\", user);\n\n        waitForUserTask(processInstance, \"exceptionStep\");\n        // step1 should still be here\n        assignAndExecuteStep(step1Id, user);\n\n        // step3 should be ready event if exceptionStep is not\n        waitForUserTask(processInstance, \"step3\");\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    /**\n     * Inclusive then Exclusive Gateway\n     * Expected : step5\n     */\n    @Test\n    public void inclusiveSplitThenExclusiveMerge() throws Exception {\n        createTrueAndFalseExpression();\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process_with_parallel_gateway\", PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addAutomaticTask(\"step1\")\n                .addAutomaticTask(\"step2\").addAutomaticTask(\"step3\").addAutomaticTask(\"step4\")\n                .addUserTask(\"step5\", ACTOR_NAME)\n                .addGateway(\"gateway1\", GatewayType.INCLUSIVE).addGateway(\"gateway2\", GatewayType.EXCLUSIVE)\n                .addTransition(\"step1\", \"gateway1\")\n                .addTransition(\"gateway1\", \"step2\", trueExpression).addTransition(\"gateway1\", \"step3\", trueExpression)\n                .addDefaultTransition(\"gateway1\", \"step4\").addTransition(\"step2\", \"gateway2\")\n                .addTransition(\"step3\", \"gateway2\")\n                .addTransition(\"step4\", \"gateway2\").addTransition(\"gateway2\", \"step5\").getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n\n        // test execution\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        // we should have 2 elements ready:\n        waitForUserTaskAndExecuteIt(processInstance, \"step5\", user);\n        waitForUserTaskAndExecuteIt(processInstance, \"step5\", user);\n\n        waitForProcessToFinish(processInstance);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    /**\n     * Exclusive with default path then Inclusive Gateway\n     * Expected : step5\n     * BS-13596\n     */\n    @Test\n    public void exclusiveThenInclusive() throws Exception {\n        createTrueAndFalseExpression();\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process_with_exclusive_inclusive_gateways\", PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addAutomaticTask(\"step1\")\n                .addAutomaticTask(\"step2\").addAutomaticTask(\"step3\").addAutomaticTask(\"step4\")\n                .addUserTask(\"step5\", ACTOR_NAME).addAutomaticTask(\"step6\")\n                .addGateway(\"gateway1\", GatewayType.EXCLUSIVE).addGateway(\"gateway2\", GatewayType.INCLUSIVE)\n                .addGateway(\"gateway3\", GatewayType.INCLUSIVE)\n                .addTransition(\"step1\", \"gateway1\")\n                .addTransition(\"gateway1\", \"step6\", falseExpression)\n                .addDefaultTransition(\"gateway1\", \"gateway2\").addTransition(\"gateway2\", \"step2\", trueExpression)\n                .addTransition(\"gateway2\", \"step3\", trueExpression)\n                .addDefaultTransition(\"gateway2\", \"step4\").addTransition(\"step2\", \"gateway3\")\n                .addTransition(\"step3\", \"gateway3\")\n                .addTransition(\"step4\", \"gateway3\").addTransition(\"gateway3\", \"step5\").getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n\n        // test execution\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        // we should have 2 elements ready:\n        waitForUserTaskAndExecuteIt(processInstance, \"step5\", user);\n\n        waitForProcessToFinish(processInstance);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    /**\n     * Exclusive then Exclusive Gateway\n     * expected : step4\n     */\n    @Test\n    public void exclusiveSplitThenExclusiveMerge() throws Exception {\n        createTrueAndFalseExpression();\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process_with_parallel_gateway\", PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addAutomaticTask(\"step1\")\n                .addAutomaticTask(\"step2\").addAutomaticTask(\"step3\").addAutomaticTask(\"step4\")\n                .addUserTask(\"step5\", ACTOR_NAME)\n                .addGateway(\"gateway1\", GatewayType.EXCLUSIVE).addGateway(\"gateway2\", GatewayType.EXCLUSIVE)\n                .addTransition(\"step1\", \"gateway1\")\n                .addTransition(\"gateway1\", \"step2\", trueExpression).addTransition(\"gateway1\", \"step3\", trueExpression)\n                .addDefaultTransition(\"gateway1\", \"step4\").addTransition(\"step2\", \"gateway2\")\n                .addTransition(\"step3\", \"gateway2\")\n                .addTransition(\"step4\", \"gateway2\").addTransition(\"gateway2\", \"step5\").getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        // test execution\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n        // we should have 1 elements ready:\n        waitForUserTaskAndExecuteIt(processInstance, \"step5\", user);\n\n        waitForProcessToFinish(processInstance);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    /**\n     * Parallel then Exclusive Gateway\n     * expected : step5\n     * Fails sometimes\n     */\n    @Test\n    public void parallelSplitThenExclusiveMerge() throws Exception {\n        createTrueAndFalseExpression();\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process_with_parallel_gateway\", PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addAutomaticTask(\"step1\")\n                .addAutomaticTask(\"step2\").addAutomaticTask(\"step3\").addAutomaticTask(\"step4\")\n                .addUserTask(\"step5\", ACTOR_NAME)\n                .addGateway(\"gateway1\", GatewayType.PARALLEL).addGateway(\"gateway2\", GatewayType.EXCLUSIVE)\n                .addTransition(\"step1\", \"gateway1\")\n                .addTransition(\"gateway1\", \"step2\").addTransition(\"gateway1\", \"step3\")\n                .addTransition(\"gateway1\", \"step4\")\n                .addTransition(\"step2\", \"gateway2\")\n                .addTransition(\"step3\", \"gateway2\").addTransition(\"step4\", \"gateway2\")\n                .addTransition(\"gateway2\", \"step5\").getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        // test execution\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n        // we should have 3 elements ready:\n        waitForUserTaskAndExecuteIt(processInstance, \"step5\", user);\n        waitForUserTaskAndExecuteIt(processInstance, \"step5\", user);\n        waitForUserTaskAndExecuteIt(processInstance, \"step5\", user);\n\n        waitForProcessToFinish(processInstance);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    /**\n     * Inclusive then Parallel Gateway\n     * Expected : Process is blocked on gateway2 (parallel gateway waiting for inactive transition)\n     */\n    @Test\n    public void inclusiveSplitThenParallelMerge() throws Exception {\n        createTrueAndFalseExpression();\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process_with_parallel_gateway\", PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addAutomaticTask(\"step1\")\n                .addAutomaticTask(\"step2\").addAutomaticTask(\"step3\").addUserTask(\"step4\", ACTOR_NAME)\n                .addUserTask(\"step5\", ACTOR_NAME)\n                .addGateway(\"gateway1\", GatewayType.INCLUSIVE).addGateway(\"gateway2\", GatewayType.PARALLEL)\n                .addTransition(\"step1\", \"gateway1\")\n                .addTransition(\"gateway1\", \"step2\", trueExpression).addTransition(\"gateway1\", \"step3\", trueExpression)\n                .addDefaultTransition(\"gateway1\", \"step4\").addTransition(\"step2\", \"gateway2\")\n                .addTransition(\"step3\", \"gateway2\")\n                .addTransition(\"step4\", \"gateway2\").addTransition(\"gateway2\", \"step5\").getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n        waitForGateway(processInstance, \"gateway2\");\n\n        final List<HumanTaskInstance> pendingHumanTaskInstances = getProcessAPI().getPendingHumanTaskInstances(\n                user.getId(), 0, 10,\n                ActivityInstanceCriterion.NAME_ASC);\n        assertEquals(0, pendingHumanTaskInstances.size());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    /**\n     * Inclusive then Parallel Gateway\n     * Expected : Process is blocked on gateway2 (parallel gateway waiting for inactive transition)\n     */\n    @Test\n    public void exclusiveSplitThenParallelMerge() throws Exception {\n        createTrueAndFalseExpression();\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Processy_exclusiveSplitThenParallelMerge\", PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addAutomaticTask(\"step1\")\n                .addAutomaticTask(\"step2\").addUserTask(\"step3\", ACTOR_NAME).addUserTask(\"step4\", ACTOR_NAME)\n                .addUserTask(\"step5\", ACTOR_NAME)\n                .addGateway(\"gateway1\", GatewayType.EXCLUSIVE).addGateway(\"gateway2\", GatewayType.PARALLEL)\n                .addTransition(\"step1\", \"gateway1\")\n                .addTransition(\"gateway1\", \"step2\", trueExpression).addTransition(\"gateway1\", \"step3\", trueExpression)\n                .addDefaultTransition(\"gateway1\", \"step4\").addTransition(\"step2\", \"gateway2\")\n                .addTransition(\"step3\", \"gateway2\")\n                .addTransition(\"step4\", \"gateway2\").addTransition(\"gateway2\", \"step5\").getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        // test execution\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n        waitForGateway(processInstance, \"gateway2\");\n\n        final List<HumanTaskInstance> pendingHumanTaskInstances = getProcessAPI().getPendingHumanTaskInstances(\n                user.getId(), 0, 10,\n                ActivityInstanceCriterion.NAME_ASC);\n        assertEquals(0, pendingHumanTaskInstances.size());\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void activitySplitsToTrueTransitionEvenIfADefaultTransitionIsSet() throws Exception {\n        createTrueAndFalseExpression();\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"splitActivity\", PROCESS_VERSION)\n                .addActor(ACTOR_NAME).addAutomaticTask(\"step1\").addUserTask(\"step2\", ACTOR_NAME)\n                .addUserTask(\"step3\", ACTOR_NAME)\n                .addUserTask(\"step4\", ACTOR_NAME).addTransition(\"step1\", \"step2\", trueExpression)\n                .addTransition(\"step1\", \"step3\", falseExpression)\n                .addDefaultTransition(\"step1\", \"step4\").getProcess();\n        assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, \"step2\");\n    }\n\n    @Test\n    public void activitySplitsToDefaultTransitionWhenAllTransitionsAreConditionalAndFalse() throws Exception {\n        createTrueAndFalseExpression();\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"splitActivity\", PROCESS_VERSION)\n                .addActor(ACTOR_NAME).addAutomaticTask(\"step1\").addUserTask(\"step2\", ACTOR_NAME)\n                .addUserTask(\"step3\", ACTOR_NAME)\n                .addUserTask(\"step4\", ACTOR_NAME).addTransition(\"step1\", \"step2\", falseExpression)\n                .addTransition(\"step1\", \"step3\", falseExpression)\n                .addDefaultTransition(\"step1\", \"step4\").getProcess();\n        assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, \"step4\");\n    }\n\n    @Test\n    public void activitySplitsToDefaultTransitionAndNormalTransition() throws Exception {\n        createTrueAndFalseExpression();\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"splitActivity\", PROCESS_VERSION)\n                .addActor(ACTOR_NAME).addAutomaticTask(\"step1\").addUserTask(\"step2\", ACTOR_NAME)\n                .addUserTask(\"step3\", ACTOR_NAME)\n                .addUserTask(\"step4\", ACTOR_NAME).addTransition(\"step1\", \"step2\")\n                .addTransition(\"step1\", \"step3\", falseExpression)\n                .addDefaultTransition(\"step1\", \"step4\").getProcess();\n        assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, \"step2\", \"step4\");\n    }\n\n    @Test\n    public void activitySplitsToTrueConditionTransitionAndNormalTransition() throws Exception {\n        createTrueAndFalseExpression();\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"splitActivity\", PROCESS_VERSION)\n                .addActor(ACTOR_NAME).addAutomaticTask(\"step1\").addUserTask(\"step2\", ACTOR_NAME)\n                .addUserTask(\"step3\", ACTOR_NAME)\n                .addUserTask(\"step4\", ACTOR_NAME).addTransition(\"step1\", \"step2\")\n                .addTransition(\"step1\", \"step3\", trueExpression)\n                .addDefaultTransition(\"step1\", \"step4\").getProcess();\n        assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, \"step2\", \"step3\");\n    }\n\n    @Test\n    public void activitySplitsTo2NormalTransitions() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process_with_parallel_gateway\", PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addAutomaticTask(\"step1\").addUserTask(\"step2\", ACTOR_NAME).addUserTask(\"step3\", ACTOR_NAME)\n                .addTransition(\"step1\", \"step2\")\n                .addTransition(\"step1\", \"step3\").getProcess();\n        assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, \"step2\", \"step3\");\n    }\n\n    @Test\n    public void activitySplitsTo2TrueTransitions() throws Exception {\n        createTrueAndFalseExpression();\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"splitActivity\", PROCESS_VERSION)\n                .addActor(ACTOR_NAME).addAutomaticTask(\"step1\").addUserTask(\"step2\", ACTOR_NAME)\n                .addUserTask(\"step3\", ACTOR_NAME)\n                .addUserTask(\"step4\", ACTOR_NAME).addTransition(\"step1\", \"step2\", trueExpression)\n                .addTransition(\"step1\", \"step3\", trueExpression)\n                .addDefaultTransition(\"step1\", \"step4\").getProcess();\n        assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, \"step2\", \"step3\");\n    }\n\n    @Test\n    public void activitySplitsWithNormalAndFalseConditionnal() throws Exception {\n        createTrueAndFalseExpression();\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"splitActivity\", PROCESS_VERSION)\n                .addActor(ACTOR_NAME).addAutomaticTask(\"step1\").addUserTask(\"step2\", ACTOR_NAME)\n                .addUserTask(\"step3\", ACTOR_NAME)\n                .addTransition(\"step1\", \"step2\").addTransition(\"step1\", \"step3\", falseExpression).getProcess();\n        assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, \"\");\n    }\n\n    @Test\n    public void activitySplitsWith2FalseConditionnal() throws Exception {\n        createTrueAndFalseExpression();\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"splitActivity\", PROCESS_VERSION)\n                .addActor(ACTOR_NAME).addAutomaticTask(\"step1\").addUserTask(\"step2\", ACTOR_NAME)\n                .addUserTask(\"step3\", ACTOR_NAME)\n                .addTransition(\"step1\", \"step2\", falseExpression).addTransition(\"step1\", \"step3\", falseExpression)\n                .getProcess();\n        assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, \"\");\n    }\n\n    @Test\n    public void inputAndOutputTransitionsOfInclusiveGatewayShouldBeEvaluatedToTrue() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"testInputTransitionOfInclusiveShouldBeEvaluatedToTrue\", PROCESS_VERSION)\n                .addActor(ACTOR_NAME).addAutomaticTask(\"step1\")\n                .addGateway(\"gateway\", GatewayType.INCLUSIVE).addUserTask(\"step2\", ACTOR_NAME)\n                .addTransition(\"step1\", \"gateway\")\n                .addTransition(\"gateway\", \"step2\").getProcess();\n        assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, \"step2\");\n    }\n\n    private void assertJohnHasGotTheExpectedTaskPending(final String actorName,\n            final DesignProcessDefinition designProcessDefinition,\n            final String... expected) throws Exception {\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, actorName,\n                user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n        // we should have 2 elements ready:\n        if (expected.length == 1 && expected[0].isEmpty()) {\n            final WaitUntil waitUntil = new WaitUntil(DEFAULT_REPEAT_EACH, DEFAULT_TIMEOUT) {\n\n                @Override\n                protected boolean check() throws Exception {\n                    final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n                    searchOptionsBuilder.filter(ActivityInstanceSearchDescriptor.PROCESS_DEFINITION_ID,\n                            processDefinition.getId());\n                    searchOptionsBuilder.filter(ActivityInstanceSearchDescriptor.STATE_NAME, \"failed\");\n                    final SearchResult<FlowNodeInstance> searchActivities = getProcessAPI()\n                            .searchFlowNodeInstances(searchOptionsBuilder.done());\n                    return searchActivities.getCount() == 1;\n                }\n            };\n            assertTrue(\"Expected a task in fail state, there was none or more than one\", waitUntil.waitUntil());\n        } else {\n            final CheckNbPendingTaskOf checkNbPendingTaskOf = new CheckNbPendingTaskOf(getProcessAPI(),\n                    DEFAULT_REPEAT_EACH, DEFAULT_TIMEOUT, true,\n                    expected.length, user);\n            // assertTrue(\"there was no pending task for john\", checkNbPendingTaskOf.waitUntil());\n            checkNbPendingTaskOf.waitUntil();\n            assertEquals(expected.length, checkNbPendingTaskOf.getPendingHumanTaskInstances().size());\n            final List<HumanTaskInstance> pendingTasks = getProcessAPI().getPendingHumanTaskInstances(user.getId(), 0,\n                    expected.length,\n                    ActivityInstanceCriterion.NAME_ASC);\n            for (int i = 0; i < expected.length; i++) {\n                assertEquals(expected[i], pendingTasks.get(i).getName());// TODO check order\n            }\n            for (final HumanTaskInstance humanTaskInstance : pendingTasks) {\n                getProcessAPI().assignUserTask(humanTaskInstance.getId(), user.getId());\n            }\n            for (final HumanTaskInstance humanTaskInstance : pendingTasks) {\n                getProcessAPI().executeFlowNode(humanTaskInstance.getId());\n            }\n            waitForProcessToFinish(processInstance);\n        }\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    protected void createTrueAndFalseExpression() throws InvalidExpressionException {\n        trueExpression = new ExpressionBuilder().createConstantBooleanExpression(true);\n        falseExpression = new ExpressionBuilder().createConstantBooleanExpression(false);\n    }\n\n    @Test\n    public void manyTransitionsToGateway() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder();\n        builder.createNewInstance(\"tooLong\", \"255\");\n        builder.addActor(ACTOR_NAME);\n        builder.addStartEvent(\"start\");\n        builder.addGateway(\"sem\", GatewayType.PARALLEL);\n        for (int j = 0; j < 50; j++) {\n            builder.addAutomaticTask(\"automatic-automatic-automatic-automatic\" + j);\n            builder.addTransition(\"start\", \"automatic-automatic-automatic-automatic\" + j);\n            builder.addTransition(\"automatic-automatic-automatic-automatic\" + j, \"sem\");\n        }\n        builder.addUserTask(\"step\", ACTOR_NAME);\n        builder.addEndEvent(\"end\");\n        builder.addTransition(\"sem\", \"step\");\n        builder.addTransition(\"step\", \"end\");\n        builder.done();\n        assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, builder.getProcess(), \"step\");\n    }\n\n    @Test\n    public void tooManyTokens() throws Exception {\n        ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder();\n        processDefinitionBuilder = processDefinitionBuilder.createNewInstance(\"tooManyTokens\", PROCESS_VERSION);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addStartEvent(\"Start\");\n        processDefinitionBuilder.addGateway(\"Gateway1\", GatewayType.PARALLEL);\n        processDefinitionBuilder.addUserTask(\"Step1\", ACTOR_NAME);\n        processDefinitionBuilder.addEndEvent(\"End1\");\n        processDefinitionBuilder.addUserTask(\"Step3\", ACTOR_NAME);\n        processDefinitionBuilder.addGateway(\"Gateway2\", GatewayType.PARALLEL);\n        processDefinitionBuilder.addUserTask(\"Step2\", ACTOR_NAME);\n        processDefinitionBuilder.addEndEvent(\"Terminate\").addTerminateEventTrigger();\n        processDefinitionBuilder.addTransition(\"Start\", \"Gateway1\");\n        processDefinitionBuilder.addTransition(\"Gateway1\", \"Step1\");\n        processDefinitionBuilder.addTransition(\"Gateway1\", \"Step3\");\n        processDefinitionBuilder.addTransition(\"Step3\", \"Gateway2\");\n        processDefinitionBuilder.addTransition(\"Step1\", \"Gateway2\");\n        processDefinitionBuilder.addTransition(\"Step1\", \"End1\");\n        processDefinitionBuilder.addTransition(\"Gateway2\", \"Step2\");\n        processDefinitionBuilder.addTransition(\"Step2\", \"Terminate\");\n        final DesignProcessDefinition designProcessDefinition = processDefinitionBuilder.getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(processInstance, \"Step3\", user);\n        waitForUserTaskAndExecuteIt(processInstance, \"Step1\", user);\n        waitForUserTask(\"Step2\");\n        // should also get the exception...not yet in the task\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void processWithDiedBranchInclusive() throws Exception {\n        ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder();\n        processDefinitionBuilder = processDefinitionBuilder.createNewInstance(\"processWithDiedBranch\", PROCESS_VERSION);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addStartEvent(\"Start\");\n        processDefinitionBuilder.addGateway(\"Gateway1\", GatewayType.PARALLEL);\n        processDefinitionBuilder.addUserTask(\"Step1\", ACTOR_NAME);\n        processDefinitionBuilder.addUserTask(\"Step2\", ACTOR_NAME);\n        processDefinitionBuilder.addUserTask(\"Step3\", ACTOR_NAME);\n        processDefinitionBuilder.addUserTask(\"Step4\", ACTOR_NAME);\n        processDefinitionBuilder.addGateway(\"Gateway2\", GatewayType.INCLUSIVE);\n        processDefinitionBuilder.addGateway(\"Exclu\", GatewayType.EXCLUSIVE);\n        processDefinitionBuilder.addTransition(\"Start\", \"Gateway1\");\n        processDefinitionBuilder.addTransition(\"Gateway1\", \"Step1\");\n        processDefinitionBuilder.addTransition(\"Gateway1\", \"Step2\");\n        processDefinitionBuilder.addTransition(\"Step2\", \"Exclu\");\n        processDefinitionBuilder.addTransition(\"Exclu\", \"Step3\", trueExpression);\n        processDefinitionBuilder.addTransition(\"Exclu\", \"Gateway2\", falseExpression);\n        processDefinitionBuilder.addTransition(\"Step1\", \"Gateway2\");\n        processDefinitionBuilder.addTransition(\"Gateway2\", \"Step4\");\n        final DesignProcessDefinition designProcessDefinition = processDefinitionBuilder.getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        //branch 1 is executed\n        waitForUserTaskAndExecuteIt(processInstance, \"Step1\", user);\n        //execute branch 2 that split to step3\n        waitForUserTaskAndExecuteIt(processInstance, \"Step2\", user);\n        //step3 should be ready (branch does not go to the inclusive anymore\n        waitForUserTask(\"Step3\");\n        //gateway 2 should have merged after the step killed the branch\n        waitForUserTask(\"Step4\");\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void processWithAlternativePathUsingExclusiveGatewayMergedByInclusive() throws Exception {\n\n        /*\n         * Diagram: one step5 is executed, step6 should be ready\n         * ╭────❲Step2❳────────────────────╮\n         * ○─❲Step1❳─❬o❭ ❬o❭─❲Step6❳─○\n         * ╰────❲Step3❳──❬x❭──❲Step4❳────╯\n         * ╰────❲Step5❳──○\n         */\n        ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder();\n        processDefinitionBuilder = processDefinitionBuilder.createNewInstance(\"processWithDiedBranch\", PROCESS_VERSION);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addStartEvent(\"Start\");\n        processDefinitionBuilder.addUserTask(\"Step1\", ACTOR_NAME);\n        processDefinitionBuilder.addGateway(\"Gateway1\", GatewayType.INCLUSIVE);\n        processDefinitionBuilder.addUserTask(\"Step2\", ACTOR_NAME);\n        processDefinitionBuilder.addUserTask(\"Step3\", ACTOR_NAME);\n        processDefinitionBuilder.addGateway(\"Exclu\", GatewayType.EXCLUSIVE);\n        processDefinitionBuilder.addUserTask(\"Step4\", ACTOR_NAME);\n        processDefinitionBuilder.addUserTask(\"Step5\", ACTOR_NAME);\n        processDefinitionBuilder.addGateway(\"Gateway2\", GatewayType.INCLUSIVE);\n        processDefinitionBuilder.addUserTask(\"Step6\", ACTOR_NAME);\n        processDefinitionBuilder.addTransition(\"Start\", \"Gateway1\");\n        processDefinitionBuilder.addTransition(\"Gateway1\", \"Step2\");\n        processDefinitionBuilder.addTransition(\"Gateway1\", \"Step3\");\n        processDefinitionBuilder.addTransition(\"Step3\", \"Exclu\");\n        processDefinitionBuilder.addDefaultTransition(\"Exclu\", \"Step4\");\n        processDefinitionBuilder.addTransition(\"Exclu\", \"Step5\", trueExpression);\n        processDefinitionBuilder.addTransition(\"Step4\", \"Gateway2\");\n        processDefinitionBuilder.addTransition(\"Step2\", \"Gateway2\");\n        processDefinitionBuilder.addTransition(\"Gateway2\", \"Step6\");\n        final DesignProcessDefinition designProcessDefinition = processDefinitionBuilder.getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(processInstance, \"Step1\", user);\n        waitForUserTaskAndExecuteIt(processInstance, \"Step2\", user);\n        waitForUserTaskAndExecuteIt(processInstance, \"Step3\", user);\n        waitForUserTask(\"Step5\");\n        waitForUserTask(\"Step6\");\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void processWithLoopAndInclusive() throws Exception {\n        ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder();\n        processDefinitionBuilder = processDefinitionBuilder.createNewInstance(\"processWithDiedBranch\", PROCESS_VERSION);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addStartEvent(\"Start\");\n        processDefinitionBuilder.addGateway(\"Gateway1\", GatewayType.PARALLEL);\n        processDefinitionBuilder.addUserTask(\"Step1\", ACTOR_NAME);\n        processDefinitionBuilder.addUserTask(\"Step2\", ACTOR_NAME);\n        processDefinitionBuilder.addGateway(\"Gateway2\", GatewayType.INCLUSIVE);\n        processDefinitionBuilder.addUserTask(\"Step3\", ACTOR_NAME);\n        processDefinitionBuilder.addTransition(\"Start\", \"Gateway1\");\n        processDefinitionBuilder.addTransition(\"Gateway1\", \"Step1\");\n        processDefinitionBuilder.addTransition(\"Step1\", \"Step1\");\n        processDefinitionBuilder.addTransition(\"Gateway1\", \"Step2\");\n        processDefinitionBuilder.addTransition(\"Step1\", \"Gateway2\");\n        processDefinitionBuilder.addTransition(\"Step2\", \"Gateway2\");\n        processDefinitionBuilder.addTransition(\"Gateway2\", \"Step3\");\n        final DesignProcessDefinition designProcessDefinition = processDefinitionBuilder.getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        //branch 1 is executed\n        waitForUserTaskAndExecuteIt(processInstance, \"Step1\", user);\n        waitForUserTaskAndExecuteIt(processInstance, \"Step1\", user);\n        waitForUserTaskAndExecuteIt(processInstance, \"Step1\", user);\n        waitForUserTaskAndExecuteIt(processInstance, \"Step1\", user);\n        waitForUserTaskAndExecuteIt(processInstance, \"Step2\", user);\n        waitForUserTask(\"Step3\");\n        waitForUserTask(\"Step3\");\n        waitForUserTask(\"Step3\");\n        waitForUserTask(\"Step3\");\n        waitForUserTaskAndExecuteIt(processInstance, \"Step1\", user);\n        waitForUserTask(\"Step3\");\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void restartNodeShouldNotRestartGatewaysWithNotFullfilledMergingCondition() throws Exception {\n        final ProcessDefinitionBuilder processDesignBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"process_with_join_gateway\", PROCESS_VERSION);\n        processDesignBuilder.addStartEvent(\"goForIt\");\n        processDesignBuilder.addEndEvent(\"terminated\");\n        final DesignProcessDefinition designProcessDefinition = processDesignBuilder.addActor(ACTOR_NAME)\n                .addGateway(\"split\", GatewayType.PARALLEL)\n                .addAutomaticTask(\"autoTask\").addUserTask(\"manualTask\", ACTOR_NAME)\n                .addGateway(\"join\", GatewayType.PARALLEL).addTransition(\"goForIt\", \"split\")\n                .addTransition(\"split\", \"autoTask\").addTransition(\"split\", \"manualTask\")\n                .addTransition(\"autoTask\", \"join\").addTransition(\"manualTask\", \"join\")\n                .addTransition(\"join\", \"terminated\").getProcess();\n\n        loginOnDefaultTenantWith(USERNAME, PASSWORD);\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        FlowNodeInstance joinGateway = waitForFlowNodeInExecutingState(processInstance, \"join\", false);\n\n        logout();\n        final PlatformSession loginPlatform = loginOnPlatform();\n        final PlatformAPI platformAPI = PlatformAPIAccessor.getPlatformAPI(loginPlatform);\n        platformAPI.stopNode();\n        platformAPI.startNode();\n        logoutOnPlatform(loginPlatform);\n        loginOnDefaultTenantWith(USERNAME, PASSWORD);\n\n        // To be sure asynchronous restart works have been executed:\n        Thread.sleep(200);\n\n        joinGateway = getProcessAPI().getFlowNodeInstance(joinGateway.getId());\n        assertEquals(TestStates.EXECUTING.getStateName(), joinGateway.getState());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void can_evaluate_transitions_with_logical_complement_expression_using_null_variable() throws Exception {\n        //given\n\n        Expression booleanVarExpr = new ExpressionBuilder().createDataExpression(\"b\", Boolean.class.getName());\n        Expression notB = new ExpressionBuilder().createLogicalComplementExpression(\"notB\", booleanVarExpr);\n\n        String start = \"start\";\n        String gateway = \"gateway\";\n        String step1 = \"step1\";\n        String step2 = \"step2\";\n\n        ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"proc\", \"1.0\");\n        builder.addActor(\"delivery\");\n        builder.addBooleanData(\"b\", null);\n        builder.addStartEvent(start);\n        builder.addGateway(gateway, GatewayType.EXCLUSIVE);\n        builder.addUserTask(step1, \"delivery\");\n        builder.addUserTask(step2, \"delivery\");\n        builder.addTransition(start, gateway);\n        builder.addDefaultTransition(gateway, step1);\n        builder.addTransition(gateway, step2, notB);\n\n        ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), \"delivery\", user);\n\n        //when\n        ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        //then\n        waitForFlowNodeInReadyState(processInstance, \"step1\", true);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void can_evaluate_transitions_with_comparison_expression_using_null_variable() throws Exception {\n        //given\n\n        Expression integerVarExpr = new ExpressionBuilder().createDataExpression(\"count\", Integer.class.getName());\n        Expression fiveExpr = new ExpressionBuilder().createConstantIntegerExpression(5);\n        Expression greaterThan = new ExpressionBuilder().createComparisonExpression(\"greaterThan\", integerVarExpr,\n                ComparisonOperator.GREATER_THAN, fiveExpr);\n\n        String start = \"start\";\n        String gateway = \"gateway\";\n        String step1 = \"step1\";\n        String step2 = \"step2\";\n\n        ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"proc\", \"1.0\");\n        builder.addActor(\"delivery\");\n        builder.addIntegerData(\"count\", null);\n        builder.addStartEvent(start);\n        builder.addGateway(gateway, GatewayType.EXCLUSIVE);\n        builder.addUserTask(step1, \"delivery\");\n        builder.addUserTask(step2, \"delivery\");\n        builder.addTransition(start, gateway);\n        builder.addDefaultTransition(gateway, step1);\n        builder.addTransition(gateway, step2, greaterThan);\n\n        ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), \"delivery\", user);\n\n        //when\n        ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        //then\n        waitForFlowNodeInReadyState(processInstance, \"step1\", true);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void link_events_following_a_parallel_gateway_and_going_into_one_should_properly_activate_it()\n            throws Exception {\n\n        ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"proc\", \"1.0\");\n        String task2 = \"task2\";\n        String gateway = \"gateway\";\n        String gateway1 = \"gateway1\";\n        String theEnd = \"theEnd\";\n        builder.addEndEvent(theEnd);\n        builder.addActor(\"user\");\n        builder.addGateway(gateway1, GatewayType.PARALLEL);\n        builder.addUserTask(task2, \"user\");\n        builder.addGateway(gateway, GatewayType.PARALLEL);\n        builder.addTransition(gateway, gateway1);\n        builder.addTransition(gateway, gateway1);\n        builder.addTransition(gateway, gateway1);\n        builder.addTransition(gateway1, task2);\n        builder.addTransition(task2, theEnd);\n        DesignProcessDefinition designProcessDefinition = builder.done();\n        ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, \"user\", user);\n        ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(processInstance, task2, user);\n        waitForProcessToFinish(processInstance);\n        disableAndDeleteProcess(processDefinition);\n\n    }\n\n    @Test\n    public void link_events_coming_from_the_same_node_and_going_into_an_inclusive_gateway_should_activate_it_only_once()\n            throws Exception {\n\n        ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"proc\", \"1.0\");\n        String task1 = \"task1\";\n        String gateway = \"gateway\";\n        String gateway1 = \"gateway1\";\n        String theEnd = \"theEnd\";\n        String task2 = \"task2\";\n        String start = \"start\";\n        builder.addEndEvent(theEnd);\n        builder.addStartEvent(start);\n        builder.addActor(\"user\");\n        builder.addGateway(gateway, GatewayType.INCLUSIVE);\n        builder.addGateway(gateway1, GatewayType.PARALLEL);\n        builder.addUserTask(task2, \"user\");\n        builder.addAutomaticTask(task1);\n        builder.addTransition(task1, gateway1);\n        builder.addTransition(gateway1, gateway);\n        builder.addTransition(gateway1, gateway);\n        builder.addTransition(gateway1, gateway);\n        builder.addTransition(gateway, task2);\n        builder.addTransition(task2, theEnd);\n        builder.addTransition(start, task1);\n        DesignProcessDefinition designProcessDefinition = builder.done();\n        ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, \"user\", user);\n        ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(processInstance, task2, user);\n        waitForProcessToFinish(processInstance);\n        disableAndDeleteProcess(processDefinition);\n\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/GetProcessDefinitionIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.process;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.TestWithTechnicalUser;\nimport org.bonitasoft.engine.bpm.actor.ActorInstance;\nimport org.bonitasoft.engine.bpm.bar.BarResource;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.category.Category;\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.bpm.flownode.GatewayType;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.connectors.TestConnector;\nimport org.bonitasoft.engine.connectors.TestConnectorThatThrowException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.filter.user.TestFilter;\nimport org.bonitasoft.engine.identity.Group;\nimport org.bonitasoft.engine.identity.Role;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.identity.UserMembership;\nimport org.bonitasoft.engine.io.IOUtil;\nimport org.bonitasoft.engine.test.BuildTestUtil;\nimport org.junit.Test;\n\npublic class GetProcessDefinitionIT extends TestWithTechnicalUser {\n\n    private List<ProcessDefinition> enabledProcessDefinitions;\n\n    private ProcessDefinition processDefinition4;\n\n    private List<User> users = null;\n\n    private List<Category> categories = null;\n\n    private List<Group> groups = null;\n\n    private List<Role> roles = null;\n\n    private List<UserMembership> userMemberships = null;\n\n    @Test\n    public void getProcessDefinitionsUnrelatedToCategoryUserCanStart() throws Exception {\n        beforeSearchProcessDefinitionsUserCanStart();\n\n        // test differentFrom process with one category\n        List<ProcessDeploymentInfo> result = getProcessAPI().getProcessDeploymentInfosUnrelatedToCategory(\n                categories.get(2).getId(), 0, 10,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertEquals(7, result.size());\n        assertEquals(enabledProcessDefinitions.get(0).getId(), result.get(0).getProcessId());\n        assertEquals(enabledProcessDefinitions.get(1).getId(), result.get(1).getProcessId());\n        assertEquals(enabledProcessDefinitions.get(2).getId(), result.get(2).getProcessId());\n        assertEquals(enabledProcessDefinitions.get(3).getId(), result.get(3).getProcessId());\n        assertEquals(enabledProcessDefinitions.get(4).getId(), result.get(4).getProcessId());\n        assertEquals(enabledProcessDefinitions.get(5).getId(), result.get(5).getProcessId());\n        assertEquals(enabledProcessDefinitions.get(6).getId(), result.get(6).getProcessId());\n\n        // test differentFrom process with 2 categories, one of which is to extract\n        result = getProcessAPI().getProcessDeploymentInfosUnrelatedToCategory(categories.get(0).getId(), 0, 10,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertEquals(7, result.size());\n        assertEquals(enabledProcessDefinitions.get(0).getId(), result.get(0).getProcessId());\n        assertEquals(enabledProcessDefinitions.get(1).getId(), result.get(1).getProcessId());\n        assertEquals(processDefinition4.getId(), result.get(2).getProcessId());\n        assertEquals(enabledProcessDefinitions.get(3).getId(), result.get(3).getProcessId());\n        assertEquals(enabledProcessDefinitions.get(4).getId(), result.get(4).getProcessId());\n        assertEquals(enabledProcessDefinitions.get(5).getId(), result.get(5).getProcessId());\n        assertEquals(enabledProcessDefinitions.get(6).getId(), result.get(6).getProcessId());\n\n        afterSearchProcessDefinitionsUserCanStart();\n    }\n\n    private void beforeSearchProcessDefinitionsUserCanStart() throws Exception {\n        // create users\n        users = new ArrayList<>(2);\n        final User chico = createUser(\"chicobento\", \"bpm\");\n        final User cebolinha = createUser(\"cebolinha\", \"bpm\");\n        final User cascao = createUser(\"cascao\", \"bpm\");\n        final User magali = createUser(\"magali\", \"bpm\");\n        final User monica = createUser(\"monica\", \"bpm\");\n        final User dorinha = createUser(\"dorinha\", \"bpm\");\n        users.add(chico);\n        users.add(cebolinha);\n        users.add(cascao);\n        users.add(magali);\n        users.add(monica);\n        users.add(dorinha);\n\n        // create groups\n        groups = new ArrayList<>(2);\n        final Group group1 = createGroup(\"group1\");\n        groups.add(group1);\n        final Group group2 = createGroup(\"group2\");\n        groups.add(group2);\n\n        // create roles\n        roles = new ArrayList<>(2);\n        final Role role1 = createRole(\"role1\");\n        final Role role2 = createRole(\"role2\");\n        roles.add(role1);\n        roles.add(role2);\n\n        // create user memberships\n        userMemberships = new ArrayList<>(3);\n        userMemberships.add(getIdentityAPI().addUserMembership(magali.getId(), group1.getId(), role1.getId()));\n        userMemberships.add(getIdentityAPI().addUserMembership(monica.getId(), group1.getId(), role2.getId()));\n        userMemberships.add(getIdentityAPI().addUserMembership(dorinha.getId(), group2.getId(), role1.getId()));\n\n        // create processes\n        createProcessesDefForSearchProcessUserCanStart();\n\n        categories = new ArrayList<>(3);\n        final Category category1 = getProcessAPI().createCategory(\"category1\", \"the first known category\");\n        final Category category2 = getProcessAPI().createCategory(\"category2\", \"the second known category\");\n        final Category category3 = getProcessAPI().createCategory(\"category3\", \"the third known category\");\n        categories.add(category1);\n        categories.add(category2);\n        categories.add(category3);\n        getProcessAPI().addProcessDefinitionToCategory(category1.getId(), enabledProcessDefinitions.get(2).getId());\n        getProcessAPI().addProcessDefinitionToCategory(category2.getId(), enabledProcessDefinitions.get(2).getId());\n        getProcessAPI().addProcessDefinitionToCategory(category2.getId(), enabledProcessDefinitions.get(1).getId());\n        getProcessAPI().addProcessDefinitionToCategory(category3.getId(), processDefinition4.getId());\n    }\n\n    private void createProcessesDefForSearchProcessUserCanStart() throws Exception {\n        enabledProcessDefinitions = new ArrayList<>(4);\n        final String actor1 = ACTOR_NAME;\n        final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process1\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor1, true);\n        final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition1, actor1,\n                users.get(0));\n        enabledProcessDefinitions.add(processDefinition1);\n\n        // create process2\n        final String actor2 = \"Actor2\";\n        final DesignProcessDefinition designProcessDefinition2 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process2\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor2, true);\n        final ProcessDefinition processDefinition2 = deployAndEnableProcessWithActor(designProcessDefinition2, actor2,\n                users.get(1));\n        enabledProcessDefinitions.add(processDefinition2);\n\n        final DesignProcessDefinition designProcessDefinition3 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process3\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor2, true);\n        final ProcessDefinition processDefinition3 = deployAndEnableProcessWithActor(designProcessDefinition3, actor2,\n                users.get(1));\n        enabledProcessDefinitions.add(processDefinition3);\n\n        // process not enabled\n        final DesignProcessDefinition designProcessDefinition4 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process4\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor2, true);\n        processDefinition4 = getProcessAPI().deploy(designProcessDefinition4);\n        getProcessAPI().addUserToActor(actor2, processDefinition4, users.get(1).getId());\n\n        // process without actor initiator\n        final DesignProcessDefinition designProcessDefinition5 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process5\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor2, false);\n        final ProcessDefinition processDefinition5 = deployAndEnableProcessWithActor(designProcessDefinition5, actor2,\n                users.get(2));\n        enabledProcessDefinitions.add(processDefinition5);\n\n        // actor initiator is a group\n        final DesignProcessDefinition designProcessDefinition6 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process6\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor2, true);\n        final ProcessDefinition processDefinition6 = deployAndEnableProcessWithActor(designProcessDefinition6, actor2,\n                groups.get(0));\n        enabledProcessDefinitions.add(processDefinition6);\n\n        // actor initiator is a role\n        final DesignProcessDefinition designProcessDefinition7 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process7\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor2, true);\n        final ProcessDefinition processDefinition7 = deployAndEnableProcessWithActor(designProcessDefinition7, actor2,\n                roles.get(0));\n        enabledProcessDefinitions.add(processDefinition7);\n\n        // actor initiator is a membership\n        final DesignProcessDefinition designProcessDefinition8 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process8\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor2, true);\n        final ProcessDefinition processDefinition8 = deployAndEnableProcessWithActor(designProcessDefinition8, actor2,\n                roles.get(0), groups.get(0));\n        enabledProcessDefinitions.add(processDefinition8);\n    }\n\n    private void afterSearchProcessDefinitionsUserCanStart() throws BonitaException {\n        disableAndDeleteProcess(enabledProcessDefinitions);\n        deleteProcess(processDefinition4);\n        deleteUserMemberships(userMemberships);\n        deleteUsers(users);\n        deleteCategories(categories);\n        deleteGroups(groups);\n        deleteRoles(roles);\n    }\n\n    @Test\n    public void getProcessesWithActorOnlyForUser() throws Exception {\n        final String actorName1 = \"ITAccountCreator\";\n        final String actorName2 = \"ITAccountValidator\";\n        final User user1 = createUser(\"any\", \"contrasena\");\n        final User user2 = createUser(\"bob\", \"smith\");\n        final DesignProcessDefinition design1 = BuildTestUtil.buildProcessDefinitionWithHumanAndAutomaticSteps(\n                \"MyProcess1\", \"1.0\",\n                Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actorName1, false);\n        final ProcessDefinition pDef1 = deployAndEnableProcessWithActor(design1, actorName1, user1);\n        final DesignProcessDefinition design2 = BuildTestUtil.buildProcessDefinitionWithHumanAndAutomaticSteps(\n                \"MyProcess2\", \"1.1\",\n                Arrays.asList(\"mi_etapa\"),\n                Arrays.asList(true), actorName2, false);\n        final ProcessDefinition pDef2 = deployAndEnableProcessWithActor(design2, actorName2, user1);\n\n        final int startIndex = 0;\n        final int maxResults = 10;\n        List<ProcessDeploymentInfo> processes = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForUser(\n                user1.getId(), startIndex, maxResults,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertEquals(2, processes.size());\n        assertEquals(processes.get(0).getProcessId(), pDef1.getId());\n        assertEquals(processes.get(1).getProcessId(), pDef2.getId());\n        // assertThat(\"Process definition Ids are not the expected ones\", processes, idAre(pDef1.getId(), pDef2.getId()));\n\n        final ActorInstance actorInstance = getActor(actorName2, pDef2);\n        if (actorInstance != null) {\n            // So that we have more than one user member of actorName2 in pDef2:\n            getProcessAPI().addUserToActor(actorInstance.getId(), user2.getId());\n        }\n        processes = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForUser(user1.getId(), startIndex, maxResults,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertEquals(1, processes.size());\n        assertEquals(processes.get(0).getProcessId(), pDef1.getId());\n\n        disableAndDeleteProcess(pDef1);\n        disableAndDeleteProcess(pDef2);\n\n        deleteUser(user1);\n        deleteUser(user2);\n    }\n\n    @Test\n    public void getProcessesWithActorOnlyForUsers() throws Exception {\n        final String actorName1 = \"ITAccountCreator\";\n        final String actorName2 = \"ITAccountValidator\";\n        final User user1 = createUser(\"any\", \"contrasena\");\n        final User user2 = createUser(\"bob\", \"smith\");\n        final User user3 = createUser(\"mark\", \"sampaio\");\n        final DesignProcessDefinition design1 = BuildTestUtil.buildProcessDefinitionWithHumanAndAutomaticSteps(\n                \"MyProcess1\", \"1.0\",\n                Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actorName1, false);\n        final ProcessDefinition pDef1 = deployAndEnableProcessWithActor(design1, actorName1, user1);\n        final DesignProcessDefinition design2 = BuildTestUtil.buildProcessDefinitionWithHumanAndAutomaticSteps(\n                \"MyProcess2\", \"1.1\",\n                Arrays.asList(\"mi_etapa\"), Arrays.asList(true), actorName2, false);\n        final ProcessDefinition pDef2 = deployAndEnableProcessWithActor(design2, actorName2, user2);\n\n        List<ProcessDeploymentInfo> processes = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForUsers(\n                Arrays.asList(user1.getId(), user2.getId()), 0,\n                10, ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertEquals(2, processes.size());\n        assertEquals(processes.get(0).getProcessId(), pDef1.getId());\n        assertEquals(processes.get(1).getProcessId(), pDef2.getId());\n        // assertThat(\"Process definition Ids are not the expected ones\", processes, idAre(pDef1.getId(), pDef2.getId()));\n\n        final ActorInstance actorInstance = getActor(actorName2, pDef2);\n        if (actorInstance != null) {\n            // So that we have more than one user member of actorName2 in pDef2:\n            getProcessAPI().addUserToActor(actorInstance.getId(), user3.getId());\n        }\n        final ActorInstance actorInstance2 = getActor(actorName1, pDef1);\n        if (actorInstance2 != null) {\n            // So that we have more than one user member of actorName1 in pDef1:\n            getProcessAPI().addUserToActor(actorInstance2.getId(), user3.getId());\n        }\n        processes = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForUsers(\n                Arrays.asList(user1.getId(), user2.getId()), 0, 10,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertEquals(0, processes.size());\n\n        processes = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForUsers(\n                Arrays.asList(user1.getId(), user3.getId()), 0, 10,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertEquals(1, processes.size());\n        assertEquals(pDef1.getId(), processes.get(0).getProcessId());\n\n        processes = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForUsers(\n                Arrays.asList(user2.getId(), user3.getId()), 0, 10,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertEquals(1, processes.size());\n        assertEquals(pDef2.getId(), processes.get(0).getProcessId());\n\n        processes = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForUser(user3.getId(), 0, 10,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertEquals(0, processes.size());\n\n        disableAndDeleteProcess(pDef1);\n        disableAndDeleteProcess(pDef2);\n\n        deleteUser(user1);\n        deleteUser(user2);\n        deleteUser(user3);\n    }\n\n    @Test\n    public void getProcessesWithActorOnlyRole() throws Exception {\n        final String actorName = \"actor\";\n\n        // create user\n        final User manu = createUser(USERNAME, PASSWORD);\n        loginOnDefaultTenantWith(USERNAME, PASSWORD);\n        final Role roleToDelete = createRole(\"roleToDelete\");\n        final Role roleToKeep = createRole(\"roleToKeep\");\n\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcess1\", \"1.0\").addActor(actorName)\n                .addUserTask(\"userTask\", actorName).getProcess();\n\n        final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition, actorName,\n                roleToDelete);\n\n        final DesignProcessDefinition designProcessDefinition2 = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcess2\", \"1.0\").addActor(actorName)\n                .addUserTask(\"userTask\", actorName).getProcess();\n\n        final ProcessDefinition processDefinition2 = deployAndEnableProcessWithActor(designProcessDefinition2,\n                actorName, roleToDelete, roleToKeep);\n\n        List<ProcessDeploymentInfo> procDepInfos = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForRole(\n                roleToDelete.getId(), 0, 10,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertEquals(1, procDepInfos.size());\n        final ProcessDeploymentInfo firstPDInfos = procDepInfos.get(0);\n        assertEquals(processDefinition1.getName(), firstPDInfos.getName());\n        assertEquals(processDefinition1.getId(), firstPDInfos.getProcessId());\n\n        final ActorInstance actorInstance = getActor(actorName, processDefinition1);\n        if (actorInstance != null) {\n            // So that we have more than one user member of actorName in processDefinition1:\n            getProcessAPI().addRoleToActor(actorInstance.getId(), roleToKeep.getId());\n        }\n\n        procDepInfos = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForRole(roleToDelete.getId(), 0, 10,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertEquals(0, procDepInfos.size());\n\n        deleteRoles(roleToKeep);\n        deleteRoles(roleToDelete);\n        deleteUser(manu);\n        disableAndDeleteProcess(processDefinition1);\n        disableAndDeleteProcess(processDefinition2);\n    }\n\n    @Test\n    public void getProcessesWithActorOnlyRoles() throws Exception {\n        final String actorName = \"actor\";\n\n        // create user\n        final User manu = createUser(USERNAME, PASSWORD);\n        loginOnDefaultTenantWith(USERNAME, PASSWORD);\n        final Role roleToDelete1 = createRole(\"roleToDelete1\");\n        final Role roleToKeep1 = createRole(\"roleToKeep1\");\n        final Role roleToDelete2 = createRole(\"roleToDelete2\");\n        final Role roleToKeep2 = createRole(\"roleToKeep2\");\n\n        final DesignProcessDefinition designProcessDefinition11 = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcess11\", \"1.0\").addActor(actorName)\n                .addUserTask(\"userTask\", actorName).getProcess();\n\n        final ProcessDefinition processDefinition11 = deployAndEnableProcessWithActor(designProcessDefinition11,\n                actorName, roleToDelete1);\n\n        final DesignProcessDefinition designProcessDefinition12 = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcess12\", \"1.0\").addActor(actorName)\n                .addUserTask(\"userTask\", actorName).getProcess();\n\n        final ProcessDefinition processDefinition12 = deployAndEnableProcessWithActor(designProcessDefinition12,\n                actorName, roleToDelete1, roleToKeep1);\n\n        final DesignProcessDefinition designProcessDefinition21 = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcess21\", \"1.0\").addActor(actorName)\n                .addUserTask(\"userTask\", actorName).getProcess();\n\n        final ProcessDefinition processDefinition21 = deployAndEnableProcessWithActor(designProcessDefinition21,\n                actorName, roleToDelete2);\n\n        final DesignProcessDefinition designProcessDefinition22 = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcess22\", \"1.0\").addActor(actorName)\n                .addUserTask(\"userTask\", actorName).getProcess();\n\n        final ProcessDefinition processDefinition22 = deployAndEnableProcessWithActor(designProcessDefinition22,\n                actorName, roleToDelete2, roleToKeep2);\n\n        List<ProcessDeploymentInfo> pDepInfos = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForRoles(\n                Arrays.asList(roleToDelete1.getId(), roleToDelete2.getId()), 0, 10,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n\n        assertEquals(2, pDepInfos.size());\n        ProcessDeploymentInfo pDepInfo1 = pDepInfos.get(0);\n        final ProcessDeploymentInfo pDepInfo2 = pDepInfos.get(1);\n\n        assertEquals(processDefinition11.getId(), pDepInfo1.getProcessId());\n        assertEquals(processDefinition11.getName(), pDepInfo1.getName());\n        assertEquals(processDefinition21.getId(), pDepInfo2.getProcessId());\n        assertEquals(processDefinition21.getName(), pDepInfo2.getName());\n\n        final ActorInstance actorInstance = getActor(actorName, processDefinition11);\n        if (actorInstance != null) {\n            // So that we have more than one user member of actorName in processDefinition11:\n            getProcessAPI().addRoleToActor(actorInstance.getId(), roleToKeep1.getId());\n        }\n\n        pDepInfos = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForRoles(\n                Arrays.asList(roleToDelete1.getId(), roleToDelete2.getId()), 0, 10,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n\n        assertEquals(1, pDepInfos.size());\n        pDepInfo1 = pDepInfos.get(0);\n\n        assertEquals(processDefinition21.getId(), pDepInfo1.getProcessId());\n        assertEquals(processDefinition21.getName(), pDepInfo1.getName());\n\n        deleteRoles(roleToKeep1, roleToDelete1, roleToKeep2, roleToDelete2);\n        deleteUser(manu);\n        disableAndDeleteProcess(processDefinition11, processDefinition12, processDefinition21, processDefinition22);\n    }\n\n    @Test\n    public void getProcessesWithActorOnlyGroup() throws Exception {\n        final String actorName = \"actor\";\n\n        // create user\n        final User manu = createUser(USERNAME, PASSWORD);\n        loginOnDefaultTenantWith(USERNAME, PASSWORD);\n        final Group groupToDelete = createGroup(\"groupToDelete\");\n        final Group groupSonToDelete = createGroup(\"sonToDelete\", \"/groupToDelete\");\n        final Group groupToKeep = createGroup(\"groupToDeleteNot\");\n\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcess1\", \"1.0\").addActor(actorName)\n                .addUserTask(\"userTask\", actorName).getProcess();\n        final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition, actorName,\n                groupToDelete);\n\n        final DesignProcessDefinition designProcessDefinition2 = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcess2\", \"1.0\").addActor(actorName)\n                .addUserTask(\"userTask\", actorName).getProcess();\n        final ProcessDefinition processDefinition2 = deployAndEnableProcessWithActor(designProcessDefinition2,\n                actorName, groupToDelete, groupToKeep);\n\n        final DesignProcessDefinition designProcessDefinition3 = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcess3\", \"1.0\").addActor(actorName)\n                .addUserTask(\"userTask\", actorName).getProcess();\n        final ProcessDefinition processDefinition3 = deployAndEnableProcessWithActor(designProcessDefinition3,\n                actorName, groupSonToDelete);\n\n        List<ProcessDeploymentInfo> procDepInfos = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForGroup(\n                groupToDelete.getId(), 0, 10,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertEquals(2, procDepInfos.size());\n\n        ProcessDeploymentInfo firstPDInfos = procDepInfos.get(0);\n        final ProcessDeploymentInfo secondPDInfos = procDepInfos.get(1);\n\n        assertEquals(processDefinition1.getName(), firstPDInfos.getName());\n        assertEquals(processDefinition1.getId(), firstPDInfos.getProcessId());\n\n        assertEquals(processDefinition3.getName(), secondPDInfos.getName());\n        assertEquals(processDefinition3.getId(), secondPDInfos.getProcessId());\n\n        final ActorInstance actorInstance = getActor(actorName, processDefinition3);\n        if (actorInstance != null) {\n            // So that we have more than one user member of actorName in processDefinition3:\n            getProcessAPI().addGroupToActor(actorInstance.getId(), groupToKeep.getId());\n        }\n\n        procDepInfos = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForGroup(groupToDelete.getId(), 0, 10,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n\n        assertEquals(1, procDepInfos.size());\n\n        firstPDInfos = procDepInfos.get(0);\n        assertEquals(processDefinition1.getName(), firstPDInfos.getName());\n        assertEquals(processDefinition1.getId(), firstPDInfos.getProcessId());\n\n        deleteGroups(groupToKeep);\n        deleteGroups(groupToDelete);\n        deleteUser(manu);\n        disableAndDeleteProcess(processDefinition1);\n        disableAndDeleteProcess(processDefinition2);\n        disableAndDeleteProcess(processDefinition3);\n    }\n\n    @Test\n    public void getProcessesWithActorOnlyForGroups() throws Exception {\n        final String actorName = \"actor\";\n\n        // create user\n        final User manu = createUser(USERNAME, PASSWORD);\n        loginOnDefaultTenantWith(USERNAME, PASSWORD);\n        final Group groupToDelete = createGroup(\"groupToDelete\");\n        final Group groupSonToDelete = createGroup(\"sonToDelete\", \"/groupToDeleteNot\");\n        final Group groupToKeep = createGroup(\"groupToDeleteNot\");\n\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcess1\", \"1.0\").addActor(actorName)\n                .addUserTask(\"userTask\", actorName).getProcess();\n\n        final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition, actorName,\n                groupToDelete);\n\n        final DesignProcessDefinition designProcessDefinition2 = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcess2\", \"1.0\").addActor(actorName)\n                .addUserTask(\"userTask\", actorName).getProcess();\n        final ProcessDefinition processDefinition2 = deployAndEnableProcessWithActor(designProcessDefinition2,\n                actorName, groupToDelete, groupToKeep);\n\n        final DesignProcessDefinition designProcessDefinition3 = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcess3\", \"1.0\").addActor(actorName)\n                .addUserTask(\"userTask\", actorName).getProcess();\n        final ProcessDefinition processDefinition3 = deployAndEnableProcessWithActor(designProcessDefinition3,\n                actorName, groupSonToDelete);\n\n        List<ProcessDeploymentInfo> procDepInfos = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForGroups(\n                Arrays.asList(groupSonToDelete.getId(), groupToDelete.getId()), 0, 10,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertEquals(2, procDepInfos.size());\n\n        final ProcessDeploymentInfo firstPDInfos = procDepInfos.get(0);\n        final ProcessDeploymentInfo secondPDInfos = procDepInfos.get(1);\n\n        assertEquals(processDefinition1.getName(), firstPDInfos.getName());\n        assertEquals(processDefinition1.getId(), firstPDInfos.getProcessId());\n\n        assertEquals(processDefinition3.getName(), secondPDInfos.getName());\n        assertEquals(processDefinition3.getId(), secondPDInfos.getProcessId());\n\n        final ActorInstance actorInstance = getActor(actorName, processDefinition1);\n        if (actorInstance != null) {\n            // So that we have more than one user member of actorName in processDefinition1:\n            getProcessAPI().addGroupToActor(actorInstance.getId(), groupSonToDelete.getId());\n        }\n\n        procDepInfos = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForGroups(\n                Arrays.asList(groupToKeep.getId(), groupToDelete.getId()), 0, 10,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n\n        assertEquals(3, procDepInfos.size());\n\n        deleteGroups(groupToKeep, groupToDelete);\n        deleteUser(manu);\n        disableAndDeleteProcess(processDefinition1, processDefinition2, processDefinition3);\n    }\n\n    @Test\n    public void getProcessesWithActorWithParticularCases() throws Exception {\n        final String actorName = \"actor\";\n\n        final Role roleToDelete1 = createRole(\"roleToDelete1\");\n        final Role roleToDelete2 = createRole(\"roleToDelete2\");\n        final Group groupToDelete1 = createGroup(\"groupToDelete1\");\n        final Group groupToDelete2 = createGroup(\"groupToDelete2\");\n        final User userToDelete1 = createUser(\"userToDelete1\", \"pwd\");\n        final User userToDelete2 = createUser(\"userToDelete2\", \"pwd\");\n\n        final DesignProcessDefinition designProcessDefinition1 = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcess1\", \"1.0\").addActor(actorName)\n                .addUserTask(\"userTask\", actorName).getProcess();\n        final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition1,\n                actorName, roleToDelete1);\n\n        final DesignProcessDefinition designProcessDefinition2 = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcess2\", \"1.0\").addActor(actorName)\n                .addUserTask(\"userTask\", actorName).getProcess();\n        final ProcessDefinition processDefinition2 = deployAndEnableProcessWithActor(designProcessDefinition2,\n                actorName, groupToDelete1);\n\n        final DesignProcessDefinition designProcessDefinition3 = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcess3\", \"1.0\").addActor(actorName)\n                .addUserTask(\"userTask\", actorName).getProcess();\n        final ProcessDefinition processDefinition3 = deployAndEnableProcessWithActor(designProcessDefinition3,\n                actorName, userToDelete1);\n\n        List<ProcessDeploymentInfo> pDepInfos1 = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForRoles(\n                Arrays.asList(roleToDelete1.getId()), 0, 10,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertEquals(1, pDepInfos1.size());\n        assertEquals(processDefinition1.getName(), pDepInfos1.get(0).getName());\n        assertEquals(processDefinition1.getId(), pDepInfos1.get(0).getProcessId());\n        pDepInfos1 = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForRoles(\n                Arrays.asList(roleToDelete2.getId()), 0, 10,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertEquals(0, pDepInfos1.size());\n        pDepInfos1 = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForRoles(\n                Arrays.asList(roleToDelete1.getId(), roleToDelete2.getId()), 0, 10,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertEquals(1, pDepInfos1.size());\n\n        List<ProcessDeploymentInfo> pDepInfos2 = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForGroups(\n                Arrays.asList(groupToDelete1.getId()), 0, 10,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertEquals(1, pDepInfos2.size());\n        assertEquals(processDefinition2.getName(), pDepInfos2.get(0).getName());\n        assertEquals(processDefinition2.getId(), pDepInfos2.get(0).getProcessId());\n        pDepInfos2 = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForGroups(\n                Arrays.asList(groupToDelete2.getId()), 0, 10,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertEquals(0, pDepInfos2.size());\n        pDepInfos2 = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForGroups(\n                Arrays.asList(groupToDelete1.getId(), groupToDelete2.getId()), 0, 10,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertEquals(1, pDepInfos2.size());\n\n        List<ProcessDeploymentInfo> pDepInfos3 = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForUsers(\n                Arrays.asList(userToDelete1.getId()), 0, 10,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertEquals(1, pDepInfos3.size());\n        assertEquals(processDefinition3.getName(), pDepInfos3.get(0).getName());\n        assertEquals(processDefinition3.getId(), pDepInfos3.get(0).getProcessId());\n        pDepInfos3 = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForUsers(\n                Arrays.asList(userToDelete2.getId()), 0, 10,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertEquals(0, pDepInfos3.size());\n        pDepInfos3 = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForUsers(\n                Arrays.asList(userToDelete1.getId(), userToDelete2.getId()), 0, 10,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertEquals(1, pDepInfos3.size());\n\n        ActorInstance actorInstance = getActor(actorName, processDefinition1);\n        if (actorInstance != null) {\n            // So that we have more than one user member of actorName in processDefinition1:\n            getProcessAPI().addRoleToActor(actorInstance.getId(), roleToDelete2.getId());\n        }\n        actorInstance = getActor(actorName, processDefinition2);\n        if (actorInstance != null) {\n            // So that we have more than one user member of actorName in processDefinition2:\n            getProcessAPI().addGroupToActor(actorInstance.getId(), groupToDelete2.getId());\n        }\n        actorInstance = getActor(actorName, processDefinition3);\n        if (actorInstance != null) {\n            // So that we have more than one user member of actorName in processDefinition3:\n            getProcessAPI().addUserToActor(actorInstance.getId(), userToDelete2.getId());\n        }\n\n        pDepInfos1 = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForRoles(\n                Arrays.asList(roleToDelete1.getId()), 0, 10,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertEquals(0, pDepInfos1.size());\n        pDepInfos1 = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForRoles(\n                Arrays.asList(roleToDelete2.getId()), 0, 10,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertEquals(0, pDepInfos1.size());\n        pDepInfos1 = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForRoles(\n                Arrays.asList(roleToDelete1.getId(), roleToDelete2.getId()), 0, 10,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertEquals(1, pDepInfos1.size());\n        assertEquals(processDefinition1.getId(), pDepInfos1.get(0).getProcessId());\n\n        pDepInfos1 = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForGroups(\n                Arrays.asList(groupToDelete1.getId()), 0, 10,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertEquals(0, pDepInfos1.size());\n        pDepInfos1 = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForGroups(\n                Arrays.asList(groupToDelete2.getId()), 0, 10,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertEquals(0, pDepInfos1.size());\n        pDepInfos1 = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForGroups(\n                Arrays.asList(groupToDelete1.getId(), groupToDelete2.getId()), 0, 10,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertEquals(1, pDepInfos1.size());\n        assertEquals(processDefinition2.getId(), pDepInfos1.get(0).getProcessId());\n\n        pDepInfos1 = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForUsers(\n                Arrays.asList(userToDelete1.getId()), 0, 10,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertEquals(0, pDepInfos1.size());\n        pDepInfos1 = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForUsers(\n                Arrays.asList(userToDelete2.getId()), 0, 10,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertEquals(0, pDepInfos1.size());\n        pDepInfos1 = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForUsers(\n                Arrays.asList(userToDelete1.getId(), userToDelete2.getId()), 0, 10,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertEquals(1, pDepInfos1.size());\n        assertEquals(processDefinition3.getId(), pDepInfos1.get(0).getProcessId());\n\n        deleteRoles(roleToDelete1, roleToDelete2);\n        deleteGroups(groupToDelete1, groupToDelete2);\n        deleteUsers(userToDelete1, userToDelete2);\n\n        disableAndDeleteProcess(processDefinition1);\n        disableAndDeleteProcess(processDefinition2);\n        disableAndDeleteProcess(processDefinition3);\n    }\n\n    @Test\n    public void getPaginatedProcessesWithActorOnlyForUser() throws Exception {\n        final String actorName1 = \"ITAccountCreator\";\n        final String actorName2 = \"ITAccountValidator\";\n        final User user1 = createUser(\"any\", \"contrasena\");\n        final User user2 = createUser(\"bob\", \"smith\");\n        final DesignProcessDefinition design1 = BuildTestUtil.buildProcessDefinitionWithHumanAndAutomaticSteps(\n                \"MyProcess1\", \"1.0\",\n                Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actorName1, false);\n        final ProcessDefinition pDef1 = deployAndEnableProcessWithActor(design1, actorName1, user1);\n        final DesignProcessDefinition design2 = BuildTestUtil.buildProcessDefinitionWithHumanAndAutomaticSteps(\n                \"MyProcess2\", \"1.1\",\n                Arrays.asList(\"mi_etapa\"), Arrays.asList(true), actorName2, false);\n        final ProcessDefinition pDef2 = deployAndEnableProcessWithActor(design2, actorName2, user1);\n\n        final int startIndex = 0;\n        final int maxResults = 1;\n        final List<ProcessDeploymentInfo> processes = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForUser(\n                user1.getId(), startIndex, maxResults,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertEquals(1, processes.size());\n        assertEquals(processes.get(0).getProcessId(), pDef1.getId());\n\n        disableAndDeleteProcess(pDef1);\n        disableAndDeleteProcess(pDef2);\n\n        deleteUser(user1);\n        deleteUser(user2);\n    }\n\n    @Test\n    public void getExistingDesignProcessDefinition() throws Exception {\n        final User user = createUser(\"any\", \"contrasena\");\n\n        final Expression targetProcessNameExpr = new ExpressionBuilder().createConstantStringExpression(PROCESS_NAME);\n        final Expression targetProcessVersionExpr = new ExpressionBuilder()\n                .createConstantStringExpression(PROCESS_VERSION);\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME, true);\n        processBuilder.addActor(\"actor2\");\n        processBuilder.addAutomaticTask(\"AutomaticTask\")\n                .addCallActivity(\"CallActivity\", targetProcessNameExpr, targetProcessVersionExpr)\n                .addManualTask(\"ManualTask\", ACTOR_NAME)\n                .addBoundaryEvent(\"BoundaryEvent\").addSignalEventTrigger(\"signalName\");\n        processBuilder.addUserTask(\"UserTask\", ACTOR_NAME)\n                .addUserFilter(\"test\", \"org.bonitasoft.engine.filter.user.testFilter\", \"1.0\")\n                .addInput(\"userId\", new ExpressionBuilder().createConstantLongExpression(3));\n        processBuilder.addConnector(\"testConnectorThatThrowException\", \"testConnectorThatThrowException\", \"1.0\",\n                ConnectorEvent.ON_ENTER);\n        processBuilder.addDocumentDefinition(\"Doc\").addUrl(\"plop\");\n        processBuilder.addGateway(\"Gateway\", GatewayType.PARALLEL);\n        processBuilder.addBlobData(\"BlobData\", null).addDescription(\"blolbDescription\").addBooleanData(\"BooleanData\",\n                null);\n        processBuilder.addDisplayName(\"plop\").addDisplayDescription(\"plop2\").addEndEvent(\"EndEvent\");\n        processBuilder.addIntermediateCatchEvent(\"IntermediateCatchEvent\")\n                .addIntermediateThrowEvent(\"IntermediateThrowEvent\");\n        processBuilder.addReceiveTask(\"ReceiveTask\", \"messageName\");\n        processBuilder.addSendTask(\"SendTask\", \"messageName\", targetProcessNameExpr);\n        processBuilder.addTransition(\"BoundaryEvent\", \"ManualTask\");\n\n        final DesignProcessDefinition designProcessDefinition = processBuilder.done();\n        final ProcessDefinition processDefinition = deployProcessWithTestFilter(designProcessDefinition, user);\n        assertNotNull(processDefinition);\n\n        final DesignProcessDefinition resultDesignProcessDefinition = getProcessAPI()\n                .getDesignProcessDefinition(processDefinition.getId());\n        assertEquals(designProcessDefinition, resultDesignProcessDefinition);\n\n        disableAndDeleteProcess(processDefinition);\n        deleteUser(user);\n    }\n\n    @Test(expected = ProcessDefinitionNotFoundException.class)\n    public void getNotExistingDesignProcessDefinition() throws Exception {\n        getProcessAPI().getDesignProcessDefinition(16548654L);\n    }\n\n    private ProcessDefinition deployProcessWithTestFilter(final DesignProcessDefinition designProcessDefinition,\n            final User user)\n            throws Exception {\n        final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(\n                        designProcessDefinition);\n        final List<BarResource> impl = generateFilterImplementations(\"TestFilter\");\n        for (final BarResource barResource : impl) {\n            businessArchiveBuilder.addUserFilters(barResource);\n        }\n        final List<BarResource> generateFilterDependencies = new ArrayList<>(1);\n        final byte[] data = IOUtil.generateJar(TestFilter.class);\n        generateFilterDependencies.add(new BarResource(\"TestFilter.jar\", data));\n        for (final BarResource barResource : generateFilterDependencies) {\n            businessArchiveBuilder.addClasspathResource(barResource);\n        }\n\n        businessArchiveBuilder.addConnectorImplementation(\n                getResource(\"/org/bonitasoft/engine/connectors/TestConnectorThatThrowException.impl\",\n                        \"TestConnectorThatThrowException.impl\"));\n        businessArchiveBuilder\n                .addClasspathResource(BuildTestUtil\n                        .generateJarAndBuildBarResource(TestConnectorThatThrowException.class,\n                                \"TestConnectorThatThrowException.jar\"));\n\n        final ProcessDefinition processDefinition = deployProcess(businessArchiveBuilder.done());\n        getProcessAPI().addUserToActor(ACTOR_NAME, processDefinition, user.getId());\n        getProcessAPI().addUserToActor(\"actor2\", processDefinition, user.getId());\n\n        getProcessAPI().enableProcess(processDefinition.getId());\n        return processDefinition;\n    }\n\n    private List<BarResource> generateFilterImplementations(final String filterName) throws IOException {\n        final List<BarResource> resources = new ArrayList<>(1);\n        final InputStream inputStream = TestConnector.class.getClassLoader()\n                .getResourceAsStream(\"org/bonitasoft/engine/filter/user/\" + filterName + \".impl\");\n        final byte[] data = IOUtil.getAllContentFrom(inputStream);\n        inputStream.close();\n        resources.add(new BarResource(\"TestFilter.impl\", data));\n        return resources;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/ProcessCategoryIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.process;\n\nimport static org.junit.Assert.*;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.TestWithTechnicalUser;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.bar.InvalidBusinessArchiveFormatException;\nimport org.bonitasoft.engine.bpm.category.Category;\nimport org.bonitasoft.engine.bpm.category.CategoryCriterion;\nimport org.bonitasoft.engine.bpm.category.CategoryNotFoundException;\nimport org.bonitasoft.engine.bpm.category.CategoryUpdater;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeployException;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.test.BuildTestUtil;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class ProcessCategoryIT extends TestWithTechnicalUser {\n\n    protected static final String USERNAME = \"dwight\";\n\n    protected static final String PASSWORD = \"Schrute\";\n\n    private final String name = \"category\";\n\n    private final String description = \"description\";\n\n    List<Category> categories;\n\n    List<ProcessDefinition> processDefinitions;\n\n    @Override\n    @Before\n    public void before() throws Exception {\n        super.before();\n        categories = new ArrayList<>();\n        processDefinitions = new ArrayList<>();\n    }\n\n    @Override\n    @After\n    public void after() throws Exception {\n        for (final Category category : categories) {\n            getProcessAPI().deleteCategory(category.getId());\n        }\n        for (final ProcessDefinition processDefinitionId : processDefinitions) {\n            deleteProcess(processDefinitionId);\n        }\n        super.after();\n    }\n\n    @Test\n    public void createCategory() throws Exception {\n        final Category category = getProcessAPI().createCategory(name, description);\n        categories.add(category);\n        assertNotNull(category);\n        assertEquals(name, category.getName());\n        final long categoryId = category.getId();\n        final Category rCategory = getProcessAPI().getCategory(categoryId);\n        assertNotNull(rCategory);\n        assertEquals(name, rCategory.getName());\n    }\n\n    @Test\n    public void createCategoryWithCreatorAsAnID() throws Exception {\n        final User user = createUser(USERNAME, PASSWORD);\n        logout();\n        loginOnDefaultTenantWith(USERNAME, PASSWORD);\n\n        final Category category = getProcessAPI().createCategory(name, description);\n        categories.add(category);\n        assertNotNull(category);\n        assertEquals(name, category.getName());\n        assertEquals(user.getId(), category.getCreator());\n\n        deleteUser(user);\n    }\n\n    @Test(expected = AlreadyExistsException.class)\n    public void createCategoryWithCategoryAlreadyExistException() throws Exception {\n        final Category category = getProcessAPI().createCategory(name, description);\n        categories.add(category);\n        assertNotNull(category);\n        assertEquals(name, category.getName());\n        getProcessAPI().createCategory(name, description);\n    }\n\n    @Test(expected = CreationException.class)\n    public void createCategoryWithCategoryCreationException() throws Exception {\n        getProcessAPI().createCategory(null, description);\n    }\n\n    @Test\n    public void getNumberOfCategories() throws Exception {\n        final Category category1 = getProcessAPI().createCategory(name + 1, description);\n        categories.add(category1);\n        final Category category2 = getProcessAPI().createCategory(name + 2, description);\n        categories.add(category2);\n\n        final long categoriesCount = getProcessAPI().getNumberOfCategories();\n        assertEquals(2, categoriesCount);\n    }\n\n    @Test\n    public void getCategory() throws Exception {\n        final Category category = getProcessAPI().createCategory(name, description);\n        categories.add(category);\n        assertNotNull(category);\n        assertEquals(name, category.getName());\n        final long categoryId = category.getId();\n        final Category rCategory = getProcessAPI().getCategory(categoryId);\n        assertNotNull(rCategory);\n        assertEquals(categoryId, rCategory.getId());\n        assertEquals(name, rCategory.getName());\n        assertEquals(description, rCategory.getDescription());\n    }\n\n    @Test(expected = CategoryNotFoundException.class)\n    public void getCategoryWithCategoryNotFoundException() throws Exception {\n        final Category category = getProcessAPI().createCategory(name, description);\n        categories.add(category);\n        assertNotNull(category);\n        assertEquals(name, category.getName());\n        getProcessAPI().getCategory(category.getId() + 1);\n    }\n\n    @Test\n    public void getCategories() throws Exception {\n        final Category category1 = getProcessAPI().createCategory(\"category1\", description);\n        categories.add(category1);\n        final Category category2 = getProcessAPI().createCategory(\"category2\", description);\n        categories.add(category2);\n        final Category category3 = getProcessAPI().createCategory(\"category3\", description);\n        categories.add(category3);\n        final Category category4 = getProcessAPI().createCategory(\"category4\", description);\n        categories.add(category4);\n        final Category category5 = getProcessAPI().createCategory(\"category5\", description);\n        categories.add(category5);\n\n        List<Category> categoriesNameAsc = getProcessAPI().getCategories(0, 2, CategoryCriterion.NAME_ASC);\n        assertEquals(2, categoriesNameAsc.size());\n        assertEquals(category1.getName(), categoriesNameAsc.get(0).getName());\n        assertEquals(category2.getName(), categoriesNameAsc.get(1).getName());\n\n        categoriesNameAsc = getProcessAPI().getCategories(2, 2, CategoryCriterion.NAME_ASC);\n        assertEquals(2, categoriesNameAsc.size());\n        assertEquals(category3.getName(), categoriesNameAsc.get(0).getName());\n        assertEquals(category4.getName(), categoriesNameAsc.get(1).getName());\n\n        categoriesNameAsc = getProcessAPI().getCategories(4, 2, CategoryCriterion.NAME_ASC);\n        assertEquals(1, categoriesNameAsc.size());\n        assertEquals(category5.getName(), categoriesNameAsc.get(0).getName());\n\n        categoriesNameAsc = getProcessAPI().getCategories(6, 2, CategoryCriterion.NAME_ASC);\n        assertEquals(0, categoriesNameAsc.size());\n    }\n\n    @Test\n    public void updateCategory() throws Exception {\n        final Category oldCategory = getProcessAPI().createCategory(name, description);\n        categories.add(oldCategory);\n\n        final String newName = \"updatedName\";\n        final String newDescription = \"updatedDescription\";\n        final CategoryUpdater updater = new CategoryUpdater();\n        updater.setName(newName).setDescription(newDescription);\n        getProcessAPI().updateCategory(oldCategory.getId(), updater);\n\n        final Category categoryUpdated = getProcessAPI().getCategory(oldCategory.getId());\n        assertNotNull(categoryUpdated);\n        assertEquals(newName, categoryUpdated.getName());\n        assertEquals(newDescription, categoryUpdated.getDescription());\n    }\n\n    @Test(expected = CategoryNotFoundException.class)\n    public void updateCategoryWithCategoryNotFoundException() throws Exception {\n        final Category category = getProcessAPI().createCategory(name, description);\n        categories.add(category);\n        final CategoryUpdater updater = new CategoryUpdater();\n        updater.setName(\"updatedName\").setDescription(\"updatedDescription\");\n        getProcessAPI().updateCategory(category.getId() + 1, updater);\n    }\n\n    @Test(expected = UpdateException.class)\n    public void updateCategoryWithCategoryUpdateException() throws Exception {\n        getProcessAPI().updateCategory(0, null);\n    }\n\n    @Test(expected = CategoryNotFoundException.class)\n    public void deleteCategory() throws Exception {\n        final Category category = getProcessAPI().createCategory(name, description);\n        getProcessAPI().deleteCategory(category.getId());\n        getProcessAPI().getCategory(category.getId());\n    }\n\n    @Test(expected = DeletionException.class)\n    public void deleteCategoryWithCategoryNotFoundException() throws Exception {\n        getProcessAPI().deleteCategory(Long.MAX_VALUE);\n    }\n\n    @Test(expected = DeletionException.class)\n    public void deleteCategoryWithCategoryDeletionException() throws Exception {\n        getProcessAPI().deleteCategory(0);\n    }\n\n    @Test\n    public void processDefinitionWithCategoryPagination() throws Exception {\n        final Category category = getProcessAPI().createCategory(name, description);\n        categories.add(category);\n        // test\n        final List<ProcessDeploymentInfo> processDeploymentInfos = getProcessAPI().getProcessDeploymentInfosOfCategory(\n                category.getId(), 0, 10,\n                ProcessDeploymentInfoCriterion.ACTIVATION_STATE_ASC);\n        assertEquals(0, processDeploymentInfos.size());\n    }\n\n    @Test\n    public void removeSeveralCategoriesToProcessDefinition() throws Exception {\n        // generate category id;\n        final Category category1 = getProcessAPI().createCategory(\"Human resources\",\n                \"Category for personnel Management\");\n        categories.add(category1);\n        final Category category2 = getProcessAPI().createCategory(\"Travel Service\",\n                \"Category for all related travel matters\");\n        categories.add(category2);\n        final Category category3 = getProcessAPI().createCategory(\"Cleaning Service\",\n                \"Category for all related travel matters\");\n        categories.add(category3);\n        // generate process definition id\n        final ProcessDefinition processDefinition = generateProcessDefinition(1, \"processName\", \"version\").get(0);\n        processDefinitions.add(processDefinition);\n        final long processDefinitionId = processDefinition.getId();\n\n        // test number of current categories form this process:\n        getProcessAPI().addCategoriesToProcess(processDefinitionId,\n                Arrays.asList(new Long[] { category1.getId(), category2.getId(), category3.getId() }));\n        List<Category> categories = getProcessAPI().getCategoriesOfProcessDefinition(processDefinitionId, 0, 10,\n                CategoryCriterion.NAME_DESC);\n        assertEquals(3, categories.size());\n\n        getProcessAPI().removeCategoriesFromProcess(processDefinitionId,\n                Arrays.asList(new Long[] { category1.getId(), category3.getId() }));\n        // test number of current categories form this process:\n        categories = getProcessAPI().getCategoriesOfProcessDefinition(processDefinitionId, 0, 10,\n                CategoryCriterion.NAME_DESC);\n        assertEquals(1, categories.size());\n        // check if the remaining one is the good one:\n        assertEquals(category2.getId(), categories.get(0).getId());\n    }\n\n    @Test\n    public void addSeveralCategoriesToProcessDefinition() throws Exception {\n        final Category category1 = getProcessAPI().createCategory(\"Human resources\",\n                \"Category for personnel Management\");\n        categories.add(category1);\n        final Category category2 = getProcessAPI().createCategory(\"Travel Service\",\n                \"Category for all related travel matters\");\n        categories.add(category2);\n        final ProcessDefinition processDefinition = generateProcessDefinition(1, \"processName\", \"version\").get(0);\n        processDefinitions.add(processDefinition);\n        // test\n        getProcessAPI().addCategoriesToProcess(processDefinition.getId(),\n                Arrays.asList(new Long[] { category1.getId(), category2.getId() }));\n        final List<Category> categories = getProcessAPI().getCategoriesOfProcessDefinition(processDefinition.getId(), 0,\n                10, CategoryCriterion.NAME_DESC);\n        assertEquals(2, categories.size());\n        assertEquals(category1.getId(), categories.get(1).getId());\n        assertEquals(category2.getId(), categories.get(0).getId());\n    }\n\n    @Test\n    public void addProcessDefinitionToCategory() throws Exception {\n        // generate category id;\n        final Category category = getProcessAPI().createCategory(name, description);\n        categories.add(category);\n        final long categoryId = category.getId();\n        // generate process definition id\n        final ProcessDefinition processDefinition = generateProcessDefinition(1, \"processName\", \"version\").get(0);\n        processDefinitions.add(processDefinition);\n        // test\n        getProcessAPI().addProcessDefinitionToCategory(categoryId, processDefinition.getId());\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfosOfCategory(categoryId, 0, 10,\n                        ProcessDeploymentInfoCriterion.ACTIVATION_STATE_ASC)\n                .get(0);\n        assertEquals(processDefinition.getName(), processDeploymentInfo.getName());\n        assertEquals(processDefinition.getVersion(), processDeploymentInfo.getVersion());\n    }\n\n    @Test(expected = CreationException.class)\n    public void addProcessDefinitionToUnknownCategory() throws Exception {\n        // generate process definition id\n        final ProcessDefinition processDefinition = generateProcessDefinition(1, \"processName\", \"version\").get(0);\n        processDefinitions.add(processDefinition);\n        // test\n        getProcessAPI().addProcessDefinitionToCategory(0, processDefinition.getId());\n    }\n\n    @Test(expected = CreationException.class)\n    public void addUnknowProcessDefinitionToCategory() throws Exception {\n        // generate category id;\n        final Category category = getProcessAPI().createCategory(name, description);\n        categories.add(category);\n        // test\n        getProcessAPI().addProcessDefinitionToCategory(category.getId(), 0);\n    }\n\n    @Test\n    public void addProcessDefinitionosToCategory() throws Exception {\n        // generate category id;\n        final Category category = getProcessAPI().createCategory(name, description);\n        categories.add(category);\n        final long categoryId = category.getId();\n        // generate process definition id\n        processDefinitions = generateProcessDefinition(3, \"process\", \"version\");\n        final List<Long> processDefinitionIds = new ArrayList<>();\n        for (final ProcessDefinition processDefinition : processDefinitions) {\n            processDefinitionIds.add(processDefinition.getId());\n        }\n        // test\n        getProcessAPI().addProcessDefinitionsToCategory(categoryId, processDefinitionIds);\n        final List<ProcessDeploymentInfo> processDeploymentInfoList = getProcessAPI()\n                .getProcessDeploymentInfosOfCategory(categoryId, 0, 10,\n                        ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertNotNull(processDeploymentInfoList);\n        assertEquals(3, processDeploymentInfoList.size());\n    }\n\n    @Test\n    public void getNumberOfCategoriesByProcessDefinition() throws Exception {\n        // generate categories\n        categories = generateCategory(3, \"categoryName\", \"test get number of categories by process definition\");\n        // generate process definition\n        final ProcessDefinition processDefinition = generateProcessDefinition(1, \"processName\", \"version\").get(0);\n        processDefinitions.add(processDefinition);\n        final long processDefinitionId = processDefinition.getId();\n        // test\n        for (final Category category : categories) {\n            getProcessAPI().addProcessDefinitionToCategory(category.getId(), processDefinitionId);\n        }\n        final long categoryNumber = getProcessAPI().getNumberOfCategories(processDefinitionId);\n        assertEquals(3, categoryNumber);\n    }\n\n    @Test\n    public void getNumberOfCategoriesOfInexistentProcess() {\n        final long numberOfCategories = getProcessAPI().getNumberOfCategories(Long.MAX_VALUE);\n        assertEquals(0, numberOfCategories);\n    }\n\n    @Test\n    public void getNumberOfProcessesInCategory() throws Exception {\n        // generate categories\n        final Category category = generateCategory(1, \"categoryName\", \"test get number of processes in category\")\n                .get(0);\n        categories.add(category);\n        // generate process definition\n        processDefinitions = generateProcessDefinition(3, \"processName\", \"version\");\n        // test\n        for (final ProcessDefinition processDefinition : processDefinitions) {\n            getProcessAPI().addProcessDefinitionToCategory(category.getId(), processDefinition.getId());\n        }\n        final long processNumber = getProcessAPI().getNumberOfProcessDefinitionsOfCategory(category.getId());\n        assertEquals(3, processNumber);\n    }\n\n    @Test\n    public void getNumberOfProcessesInCategoryWithInexistentCategory() {\n        final long processesInCategory = getProcessAPI().getNumberOfProcessDefinitionsOfCategory(Long.MAX_VALUE);\n        assertEquals(0, processesInCategory);\n    }\n\n    @Test\n    public void getProcessDeploymentInfosOfCategory() throws Exception {\n        // generate category\n        final Category category = getProcessAPI().createCategory(name, description);\n        categories.add(category);\n        final long categoryId = category.getId();\n        // generate process definitions\n        processDefinitions = generateProcessDefinition(3, \"process\", \"version\");\n        final List<Long> processDefinitionIds = new ArrayList<>();\n        for (final ProcessDefinition processDefinition : processDefinitions) {\n            processDefinitionIds.add(processDefinition.getId());\n        }\n        // add process definitions to category\n        getProcessAPI().addProcessDefinitionsToCategory(categoryId, processDefinitionIds);\n        // test\n        final List<ProcessDeploymentInfo> processDeploymentInfoList_NameASC = getProcessAPI()\n                .getProcessDeploymentInfosOfCategory(categoryId, 0, 3,\n                        ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertNotNull(processDeploymentInfoList_NameASC);\n        assertEquals(3, processDeploymentInfoList_NameASC.size());\n        assertEquals(\"process1\", processDeploymentInfoList_NameASC.get(0).getName());\n        assertEquals(\"process2\", processDeploymentInfoList_NameASC.get(1).getName());\n        assertEquals(\"process3\", processDeploymentInfoList_NameASC.get(2).getName());\n\n        final List<ProcessDeploymentInfo> processDeploymentInfoList_NameDESC = getProcessAPI()\n                .getProcessDeploymentInfosOfCategory(categoryId, 0, 3,\n                        ProcessDeploymentInfoCriterion.NAME_DESC);\n        assertNotNull(processDeploymentInfoList_NameDESC);\n        assertEquals(3, processDeploymentInfoList_NameDESC.size());\n        assertEquals(\"process3\", processDeploymentInfoList_NameDESC.get(0).getName());\n        assertEquals(\"process2\", processDeploymentInfoList_NameDESC.get(1).getName());\n        assertEquals(\"process1\", processDeploymentInfoList_NameDESC.get(2).getName());\n\n        final List<ProcessDeploymentInfo> outOfRangeList = getProcessAPI().getProcessDeploymentInfosOfCategory(\n                categoryId, 3, 3,\n                ProcessDeploymentInfoCriterion.NAME_DESC);\n        assertEquals(0, outOfRangeList.size());\n    }\n\n    @Test\n    public void getCategoriesOfProcessDefinition() throws Exception {\n        // generate categories\n        categories = generateCategory(3, \"category\", \"test get categories of process definition \");\n        // generate process definition\n        final ProcessDefinition processDefinition = generateProcessDefinition(1, \"process\", \"version\").get(0);\n        processDefinitions.add(processDefinition);\n        final long processDefinitionId = processDefinition.getId();\n\n        // add\n        for (final Category category : categories) {\n            getProcessAPI().addProcessDefinitionToCategory(category.getId(), processDefinitionId);\n        }\n        // test\n        final List<Category> categoryList_ASC = getProcessAPI().getCategoriesOfProcessDefinition(processDefinitionId, 0,\n                10, CategoryCriterion.NAME_ASC);\n        assertNotNull(categoryList_ASC);\n        assertEquals(3, categoryList_ASC.size());\n        assertEquals(\"category1\", categoryList_ASC.get(0).getName());\n        assertEquals(\"category2\", categoryList_ASC.get(1).getName());\n        assertEquals(\"category3\", categoryList_ASC.get(2).getName());\n\n        final List<Category> categoryList_DESC = getProcessAPI().getCategoriesOfProcessDefinition(processDefinitionId,\n                0, 10, CategoryCriterion.NAME_DESC);\n        assertNotNull(categoryList_DESC);\n        assertEquals(3, categoryList_DESC.size());\n        assertEquals(\"category3\", categoryList_DESC.get(0).getName());\n        assertEquals(\"category2\", categoryList_DESC.get(1).getName());\n        assertEquals(\"category1\", categoryList_DESC.get(2).getName());\n\n        final List<Category> outOfRangeCategories = getProcessAPI()\n                .getCategoriesOfProcessDefinition(processDefinitionId, 3, 10, CategoryCriterion.NAME_ASC);\n        assertEquals(0, outOfRangeCategories.size());\n    }\n\n    @Test\n    public void getCategoriesUnrelatedToProcessDefinition() throws Exception {\n        // generate categories\n        categories = generateCategory(3, \"category\", \"test get categories of process definition \");\n        // generate process definition\n        processDefinitions = generateProcessDefinition(3, \"process\", \"version\");\n        final long processDefinitionId = processDefinitions.get(0).getId();\n\n        // add\n        getProcessAPI().addProcessDefinitionToCategory(categories.get(1).getId(), processDefinitions.get(1).getId());\n        getProcessAPI().addProcessDefinitionToCategory(categories.get(1).getId(), processDefinitions.get(2).getId());\n        getProcessAPI().addProcessDefinitionToCategory(categories.get(2).getId(), processDefinitions.get(2).getId());\n\n        // Test : No category related\n        final List<Category> categoryList_ASC = getProcessAPI().getCategoriesUnrelatedToProcessDefinition(\n                processDefinitionId, 0, 10,\n                CategoryCriterion.NAME_ASC);\n        assertNotNull(categoryList_ASC);\n        assertEquals(3, categoryList_ASC.size());\n        assertEquals(\"category1\", categoryList_ASC.get(0).getName());\n        assertEquals(\"category2\", categoryList_ASC.get(1).getName());\n        assertEquals(\"category3\", categoryList_ASC.get(2).getName());\n\n        // Test : 2 related categories\n        final List<Category> categoryList_DESC = getProcessAPI().getCategoriesUnrelatedToProcessDefinition(\n                processDefinitions.get(2).getId(), 0, 10,\n                CategoryCriterion.NAME_DESC);\n        assertNotNull(categoryList_DESC);\n        assertEquals(1, categoryList_DESC.size());\n        assertEquals(\"category1\", categoryList_DESC.get(0).getName());\n    }\n\n    @Test\n    public void searchCategoriesNotOfProcessDefinition() throws Exception {\n        // generate categories\n        categories = generateCategory(3, \"category\", \"test get categories of process definition \");\n        // generate process definition\n        final ProcessDefinition processDefinition = generateProcessDefinition(1, \"process\", \"version\").get(0);\n        processDefinitions.add(processDefinition);\n        final long processDefinitionId = processDefinition.getId();\n\n        // add\n        for (final Category category : categories) {\n            getProcessAPI().addProcessDefinitionToCategory(category.getId(), processDefinitionId);\n        }\n\n        // test\n        final List<Category> categoryList_ASC = getProcessAPI().getCategoriesOfProcessDefinition(processDefinitionId, 0,\n                10, CategoryCriterion.NAME_ASC);\n        assertNotNull(categoryList_ASC);\n        assertEquals(3, categoryList_ASC.size());\n        assertEquals(\"category1\", categoryList_ASC.get(0).getName());\n        assertEquals(\"category2\", categoryList_ASC.get(1).getName());\n        assertEquals(\"category3\", categoryList_ASC.get(2).getName());\n\n        final List<Category> categoryList_DESC = getProcessAPI().getCategoriesOfProcessDefinition(processDefinitionId,\n                0, 10, CategoryCriterion.NAME_DESC);\n        assertNotNull(categoryList_DESC);\n        assertEquals(3, categoryList_DESC.size());\n        assertEquals(\"category3\", categoryList_DESC.get(0).getName());\n        assertEquals(\"category2\", categoryList_DESC.get(1).getName());\n        assertEquals(\"category1\", categoryList_DESC.get(2).getName());\n\n        final List<Category> outOfRangeCategories = getProcessAPI()\n                .getCategoriesOfProcessDefinition(processDefinitionId, 3, 10, CategoryCriterion.NAME_ASC);\n        assertEquals(0, outOfRangeCategories.size());\n    }\n\n    @Test\n    public void removeProcessDefinitionsOfCategory() throws Exception {\n        // generate category\n        final Category category = getProcessAPI().createCategory(name, description);\n        categories.add(category);\n        // generate process definitions\n        processDefinitions = generateProcessDefinition(3, \"process\", \"version\");\n        final List<Long> processDefinitionIds = new ArrayList<>();\n        for (final ProcessDefinition processDefinition : processDefinitions) {\n            processDefinitionIds.add(processDefinition.getId());\n        }\n        // add process definitions to category\n        getProcessAPI().addProcessDefinitionsToCategory(category.getId(), processDefinitionIds);\n        // test\n        getProcessAPI().removeProcessDefinitionsFromCategory(category.getId(), 0, 2);\n        final List<ProcessDeploymentInfo> processDeploymentInfos = getProcessAPI().getProcessDeploymentInfosOfCategory(\n                category.getId(), 0, 10,\n                ProcessDeploymentInfoCriterion.ACTIVATION_STATE_ASC);\n        assertNotNull(processDeploymentInfos);\n        assertEquals(1, processDeploymentInfos.size());\n    }\n\n    @Test\n    public void removeProcessDefinitionsOfUnknownCategoryDoesntThrowException() throws Exception {\n        // generate process definitions\n        processDefinitions = generateProcessDefinition(3, \"process\", \"version\");\n        // test\n        getProcessAPI().removeProcessDefinitionsFromCategory(Long.MAX_VALUE, 0, 0);\n    }\n\n    @Test\n    public void removeProcessDefinitionFromCategory() throws Exception {\n        // generate categories\n        categories = generateCategory(3, \"category\",\n                \"test remove one specified process definition from all categories \");\n        // generate process definition\n        final ProcessDefinition processDefinition = generateProcessDefinition(1, \"process\", \"version\").get(0);\n        processDefinitions.add(processDefinition);\n        final long processDefinitionId = processDefinition.getId();\n\n        // add\n        for (final Category category : categories) {\n            getProcessAPI().addProcessDefinitionToCategory(category.getId(), processDefinitionId);\n        }\n        long count = getProcessAPI().getNumberOfCategories(processDefinitionId);\n        assertEquals(3, count);\n        // test\n        getProcessAPI().removeCategoriesFromProcessDefinition(processDefinitionId, 0, 2);\n        count = getProcessAPI().getNumberOfCategories(processDefinitionId);\n        assertEquals(1, count);\n    }\n\n    @Test\n    public void removeUnknowProcessDefinitionFromCategoryDontThrowsException() throws Exception {\n        getProcessAPI().removeCategoriesFromProcessDefinition(0, 0, 1);\n    }\n\n    @Test\n    public void getNumberOfUnCategoriedProcessesDefinitions() throws Exception {\n        long processDefinitionCount = getProcessAPI().getNumberOfUncategorizedProcessDefinitions();\n        assertEquals(0, processDefinitionCount);\n\n        final ProcessDefinition processDefinition = generateProcessDefinition(1, \"processName\", \"version\").get(0);\n        processDefinitions.add(processDefinition);\n        final long processDefinitionId = processDefinition.getId();\n        processDefinitionCount = getProcessAPI().getNumberOfUncategorizedProcessDefinitions();\n        assertEquals(1, processDefinitionCount);\n    }\n\n    @Test\n    public void getUnCategoriedProcesses() throws Exception {\n        processDefinitions = generateProcessDefinition(3, \"process\", \"version\");\n        final List<ProcessDeploymentInfo> processDeploymentInfoList_NameASC = getProcessAPI()\n                .getUncategorizedProcessDeploymentInfos(0, 3,\n                        ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertNotNull(processDeploymentInfoList_NameASC);\n        assertEquals(3, processDeploymentInfoList_NameASC.size());\n        assertEquals(\"process1\", processDeploymentInfoList_NameASC.get(0).getName());\n        assertEquals(\"process2\", processDeploymentInfoList_NameASC.get(1).getName());\n        assertEquals(\"process3\", processDeploymentInfoList_NameASC.get(2).getName());\n\n        final List<ProcessDeploymentInfo> processDeploymentInfoList_NameDESC = getProcessAPI()\n                .getUncategorizedProcessDeploymentInfos(0, 2,\n                        ProcessDeploymentInfoCriterion.NAME_DESC);\n        assertNotNull(processDeploymentInfoList_NameDESC);\n        assertEquals(2, processDeploymentInfoList_NameDESC.size());\n        assertEquals(\"process3\", processDeploymentInfoList_NameDESC.get(0).getName());\n        assertEquals(\"process2\", processDeploymentInfoList_NameDESC.get(1).getName());\n\n        final List<ProcessDeploymentInfo> outOfRangeProcesses = getProcessAPI().getUncategorizedProcessDeploymentInfos(\n                3, 3,\n                ProcessDeploymentInfoCriterion.NAME_DESC);\n        assertEquals(0, outOfRangeProcesses.size());\n    }\n\n    private List<Category> generateCategory(final int count, final String categoryName, final String description)\n            throws AlreadyExistsException,\n            CreationException {\n        final List<Category> categoryList = new ArrayList<>();\n        for (int i = 1; i <= count; i++) {\n            categoryList.add(getProcessAPI().createCategory(categoryName + i, description + i));\n        }\n        return categoryList;\n    }\n\n    private List<ProcessDefinition> generateProcessDefinition(final int count, final String processName,\n            final String version)\n            throws InvalidBusinessArchiveFormatException, ProcessDeployException, InvalidProcessDefinitionException,\n            AlreadyExistsException {\n        final List<ProcessDefinition> processDefinitionList = new ArrayList<>();\n        for (int i = 1; i <= count; i++) {\n            final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                    .buildProcessDefinitionWithHumanAndAutomaticSteps(processName + i,\n                            version + i, Arrays.asList(\"step1\"), Arrays.asList(true));\n            final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                    .setProcessDefinition(designProcessDefinition)\n                    .done();\n            processDefinitionList.add(getProcessAPI().deploy(businessArchive));\n        }\n        return processDefinitionList;\n    }\n\n    @Test(expected = AlreadyExistsException.class)\n    public void cannotAddTheSameCategoryToAProcess() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder();\n        final DesignProcessDefinition definition = processBuilder.createNewInstance(\"category\", \"0.9\")\n                .addAutomaticTask(\"step1\").getProcess();\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(definition).done();\n        final ProcessDefinition processDefinition = getProcessAPI().deploy(businessArchive);\n        processDefinitions.add(processDefinition);\n        final Category category = getProcessAPI().createCategory(\"Human resources\",\n                \"Category for personnel Management\");\n        categories.add(category);\n        getProcessAPI().addCategoriesToProcess(processDefinition.getId(), Arrays.asList(category.getId()));\n        getProcessAPI().addCategoriesToProcess(processDefinition.getId(), Arrays.asList(category.getId()));\n        fail(\"It is not allowed to add twice the same category to a process\");\n    }\n\n    @Test(expected = AlreadyExistsException.class)\n    public void cannotAddTheSameCategoryToAProcessList() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder();\n        final DesignProcessDefinition definition = processBuilder.createNewInstance(\"category\", \"0.9\")\n                .addAutomaticTask(\"step1\").getProcess();\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(definition).done();\n        final ProcessDefinition processDefinition = getProcessAPI().deploy(businessArchive);\n        processDefinitions.add(processDefinition);\n        final Category category = getProcessAPI().createCategory(\"Human resources\",\n                \"Category for personnel Management\");\n        categories.add(category);\n        getProcessAPI().addProcessDefinitionsToCategory(category.getId(), Arrays.asList(processDefinition.getId()));\n        getProcessAPI().addProcessDefinitionsToCategory(category.getId(), Arrays.asList(processDefinition.getId()));\n        fail(\"It is not allowed to add twice the same category to a process\");\n    }\n\n    @Test(expected = AlreadyExistsException.class)\n    public void cannotAssociateTheSameCategoryTwiceWitAProcess() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder();\n        final DesignProcessDefinition definition = processBuilder.createNewInstance(\"category\", \"0.9\")\n                .addAutomaticTask(\"step1\").getProcess();\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(definition).done();\n        final ProcessDefinition processDefinition = getProcessAPI().deploy(businessArchive);\n        processDefinitions.add(processDefinition);\n        final Category category = getProcessAPI().createCategory(\"Human resources\",\n                \"Category for personnel Management\");\n        categories.add(category);\n        getProcessAPI().addProcessDefinitionToCategory(category.getId(), processDefinition.getId());\n        getProcessAPI().addProcessDefinitionToCategory(category.getId(), processDefinition.getId());\n        fail(\"It is not allowed to add twice the same category to a process\");\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/ProcessDeletionIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.process;\n\nimport static java.util.Collections.singletonList;\nimport static org.junit.Assert.*;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.category.Category;\nimport org.bonitasoft.engine.bpm.category.CategoryCriterion;\nimport org.bonitasoft.engine.bpm.comment.ArchivedComment;\nimport org.bonitasoft.engine.bpm.comment.Comment;\nimport org.bonitasoft.engine.bpm.document.Document;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstance;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoSearchDescriptor;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceCriterion;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.process.impl.DocumentDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.SubProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.supervisor.ProcessSupervisorSearchDescriptor;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.test.BuildTestUtil;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Celine Souchet\n */\npublic class ProcessDeletionIT extends TestWithUser {\n\n    private List<ProcessDefinition> processDefinitions;\n\n    @Override\n    @Before\n    public void before() throws Exception {\n        super.before();\n        processDefinitions = new ArrayList<>();\n    }\n\n    @Override\n    @After\n    public void after() throws Exception {\n        disableAndDeleteProcess(processDefinitions);\n        super.after();\n    }\n\n    private ProcessDefinition deployProcessWithSeveralOutGoingTransitions() throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"process To Delete\", \"2.5\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addUserTask(\"step1\", ACTOR_NAME);\n        for (int i = 0; i < 10; i++) {\n            final String activityName = \"step2\" + i;\n            processDefinitionBuilder.addUserTask(activityName, ACTOR_NAME);\n            processDefinitionBuilder.addTransition(\"step1\", activityName);\n        }\n        return deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, user);\n    }\n\n    @Test\n    public void deleteProcessInstanceStopsCreatingNewActivities() throws Exception {\n        final ProcessDefinition processDefinition = deployProcessWithSeveralOutGoingTransitions();\n        processDefinitions.add(processDefinition); // To clean in the end\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(processInstance, \"step1\", user);\n        getProcessAPI().deleteProcessInstance(processInstance.getId());\n\n        Thread.sleep(1500);\n\n        final List<HumanTaskInstance> taskInstances = getProcessAPI().getPendingHumanTaskInstances(user.getId(), 0, 100,\n                ActivityInstanceCriterion.DEFAULT);\n        assertEquals(0, taskInstances.size());\n    }\n\n    @Test\n    public void deleteProcessInstanceAlsoDeleteArchivedElements() throws Exception {\n        final ProcessDefinition processDefinition = deployProcessWithSeveralOutGoingTransitions();\n        processDefinitions.add(processDefinition); // To clean in the end\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(processInstance, \"step1\", user);\n        for (int i = 0; i < 10; i++) {\n            waitForUserTask(processInstance, \"step2\" + i);\n        }\n\n        getProcessAPI().deleteProcessInstance(processInstance.getId());\n\n        final List<ArchivedActivityInstance> taskInstances = getProcessAPI().getArchivedActivityInstances(\n                processInstance.getId(), 0, 100,\n                ActivityInstanceCriterion.DEFAULT);\n        assertEquals(0, taskInstances.size());\n    }\n\n    @Test\n    public void deleteArchivedProcessInstanceAlsoDeleteArchivedElements() throws Exception {\n        final ProcessDefinition processDefinition = deployProcessWithSeveralOutGoingTransitions();\n        processDefinitions.add(processDefinition); // To clean in the end\n        final ProcessInstance processInstance = startAndFinishProcess(processDefinition);\n        getProcessAPI().deleteArchivedProcessInstances(processDefinition.getId(), 0, 50);\n\n        final List<ArchivedActivityInstance> taskInstances = getProcessAPI().getArchivedActivityInstances(\n                processInstance.getId(), 0, 100,\n                ActivityInstanceCriterion.DEFAULT);\n        assertEquals(0, taskInstances.size());\n    }\n\n    @Test\n    public void deleteArchivedProcessInstancesInAllStatesByIdsAlsoDeleteArchivedElements() throws Exception {\n        final ProcessDefinition processDefinition = deployProcessWithSeveralOutGoingTransitions();\n        processDefinitions.add(processDefinition); // To clean in the end\n        final ProcessInstance processInstance1 = startAndFinishProcess(processDefinition);\n        startAndFinishProcess(processDefinition);\n        startAndFinishProcess(processDefinition);\n\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.sort(ProcessInstanceSearchDescriptor.NAME, Order.ASC);\n        final List<ArchivedProcessInstance> archivedProcessInstances = getProcessAPI()\n                .searchArchivedProcessInstances(searchOptionsBuilder.done())\n                .getResult();\n        getProcessAPI().deleteArchivedProcessInstancesInAllStates(\n                Arrays.asList(archivedProcessInstances.get(0).getSourceObjectId(),\n                        archivedProcessInstances.get(2).getSourceObjectId()));\n\n        final List<ArchivedActivityInstance> taskInstances = getProcessAPI().getArchivedActivityInstances(\n                processInstance1.getId(), 0, 100,\n                ActivityInstanceCriterion.DEFAULT);\n        assertEquals(0, taskInstances.size());\n        final long numberOfArchivedProcessInstancesAfterDelete = getProcessAPI()\n                .searchArchivedProcessInstancesInAllStates(searchOptionsBuilder.done())\n                .getCount();\n        assertEquals(3, numberOfArchivedProcessInstancesAfterDelete);\n    }\n\n    @Test\n    public void deleteArchivedProcessInstancesInAllStatesByIdAlsoDeleteArchivedElements() throws Exception {\n        final ProcessDefinition processDefinition = deployProcessWithSeveralOutGoingTransitions();\n        processDefinitions.add(processDefinition); // To clean in the end\n        final ProcessInstance processInstance = startAndFinishProcess(processDefinition);\n\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.sort(ProcessInstanceSearchDescriptor.NAME, Order.ASC);\n        final List<ArchivedProcessInstance> archivedProcessInstances = getProcessAPI()\n                .searchArchivedProcessInstancesInAllStates(searchOptionsBuilder.done())\n                .getResult();\n        getProcessAPI().deleteArchivedProcessInstancesInAllStates(archivedProcessInstances.get(0).getSourceObjectId());\n\n        final List<ArchivedActivityInstance> taskInstances = getProcessAPI().getArchivedActivityInstances(\n                processInstance.getId(), 0, 100,\n                ActivityInstanceCriterion.DEFAULT);\n        assertEquals(0, taskInstances.size());\n        final long numberOfArchivedProcessInstancesAfterDelete = getProcessAPI()\n                .searchArchivedProcessInstancesInAllStates(searchOptionsBuilder.done())\n                .getCount();\n        assertEquals(archivedProcessInstances.size() - 3, numberOfArchivedProcessInstancesAfterDelete);\n    }\n\n    private ProcessInstance startAndFinishProcess(final ProcessDefinition processDefinition) throws Exception {\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(processInstance, \"step1\", user);\n        for (int i = 0; i < 10; i++) {\n            waitForUserTaskAndExecuteIt(processInstance, \"step2\" + i, user);\n        }\n        waitForProcessToFinish(processInstance);\n        return processInstance;\n    }\n\n    @Test\n    public void deleteProcessDefinitionStopsCreatingNewActivities() throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"process To Delete\", \"2.5\");\n        final String actorName = \"delivery\";\n        processDefinitionBuilder.addActor(actorName);\n        processDefinitionBuilder.addUserTask(\"step1\", actorName);\n        for (int i = 0; i < 30; i++) {\n            final String activityName = \"step2\" + i;\n            processDefinitionBuilder.addUserTask(activityName, actorName);\n            processDefinitionBuilder.addTransition(\"step1\", activityName);\n\n        }\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),\n                actorName, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(processInstance, \"step1\", user);\n        disableAndDeleteProcess(processDefinition.getId()); // will fail in CommonAPITest.succeeded if activities are created after delete\n        Thread.sleep(1500);\n    }\n\n    @Test\n    public void deleteProcessInstanceAlsoDeleteChildrenProcesses() throws Exception {\n        // deploy a simple process P1\n        final String simpleStepName = \"simpleStep\";\n        final ProcessDefinition simpleProcess = deployAndEnableSimpleProcess(\"simpleProcess\", simpleStepName);\n        processDefinitions.add(simpleProcess); // To clean in the end\n\n        // deploy a process P2 containing a call activity calling P1\n        final String intermediateStepName = \"intermediateStep1\";\n        final String intermediateCallActivityName = \"intermediateCall\";\n        final ProcessDefinition intermediateProcess = deployAndEnableProcessWithCallActivity(\"intermediateProcess\",\n                simpleProcess.getName(),\n                intermediateStepName, intermediateCallActivityName);\n        processDefinitions.add(intermediateProcess); // To clean in the end\n\n        // deploy a process P3 containing a call activity calling P2\n        final String rootStepName = \"rootStep1\";\n        final String rootCallActivityName = \"rootCall\";\n        final ProcessDefinition rootProcess = deployAndEnableProcessWithCallActivity(\"rootProcess\",\n                intermediateProcess.getName(), rootStepName,\n                rootCallActivityName);\n        processDefinitions.add(rootProcess); // To clean in the end\n\n        // start P3, the call activities will start instances of P2 a and P1\n        final ProcessInstance rootProcessInstance = getProcessAPI().startProcess(rootProcess.getId());\n        waitForUserTask(rootProcessInstance, simpleStepName);\n\n        // check that the instances of p1, p2 and p3 were created\n        List<ProcessInstance> processInstances = getProcessAPI().getProcessInstances(0, 10,\n                ProcessInstanceCriterion.NAME_ASC);\n        assertEquals(3, processInstances.size());\n\n        // check that archived flow nodes\n        List<ArchivedActivityInstance> taskInstances = getProcessAPI().getArchivedActivityInstances(\n                rootProcessInstance.getId(), 0, 100,\n                ActivityInstanceCriterion.DEFAULT);\n        assertTrue(taskInstances.size() > 0);\n\n        // delete the root process instance\n        getProcessAPI().deleteProcessInstance(rootProcessInstance.getId());\n\n        // check that the instances of p1 and p2 were deleted\n        processInstances = getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.NAME_ASC);\n        assertEquals(0, processInstances.size());\n\n        // check that archived flow nodes were not deleted.\n        taskInstances = getProcessAPI().getArchivedActivityInstances(rootProcessInstance.getId(), 0, 100,\n                ActivityInstanceCriterion.DEFAULT);\n        assertEquals(0, taskInstances.size());\n    }\n\n    private ProcessDefinition deployAndEnableSimpleProcess(final String processName, final String userTaskName)\n            throws Exception {\n        final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder().createNewInstance(processName,\n                \"1.0\");\n        processDefBuilder.addActor(ACTOR_NAME);\n        processDefBuilder.addStartEvent(\"tStart\");\n        processDefBuilder.addUserTask(userTaskName, ACTOR_NAME);\n        processDefBuilder.addEndEvent(\"tEnd\");\n        processDefBuilder.addTransition(\"tStart\", userTaskName);\n        processDefBuilder.addTransition(userTaskName, \"tEnd\");\n        return deployAndEnableProcessWithActor(processDefBuilder.done(), ACTOR_NAME, user);\n    }\n\n    private ProcessDefinition deployAndEnableProcessWithCallActivity(final String processName,\n            final String targetProcessName, final String userTaskName,\n            final String callActivityName) throws Exception {\n        final Expression targetProcessNameExpr = new ExpressionBuilder()\n                .createConstantStringExpression(targetProcessName);\n\n        final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder().createNewInstance(processName,\n                \"1.0\");\n        processDefBuilder.addActor(ACTOR_NAME);\n        processDefBuilder.addStartEvent(\"start\");\n        processDefBuilder.addCallActivity(callActivityName, targetProcessNameExpr, null);\n        processDefBuilder.addUserTask(userTaskName, ACTOR_NAME);\n        processDefBuilder.addEndEvent(\"end\");\n        processDefBuilder.addTransition(\"start\", callActivityName);\n        processDefBuilder.addTransition(callActivityName, userTaskName);\n        processDefBuilder.addTransition(userTaskName, \"end\");\n        return deployAndEnableProcessWithActor(processDefBuilder.done(), ACTOR_NAME, user);\n    }\n\n    @Test\n    public void deleteProcessInstanceAlsoDeleteArchivedChildrenProcesses() throws Exception {\n        // deploy a simple process P1\n        final String simpleStepName = \"simpleStep\";\n        final ProcessDefinition simpleProcess = deployAndEnableSimpleProcess(\"simpleProcess\", simpleStepName);\n        processDefinitions.add(simpleProcess); // To clean in the end\n\n        // deploy a process P2 containing a call activity calling P1\n        final String rootStepName = \"rootStep1\";\n        final String rootCallActivityName = \"rootCall\";\n        final ProcessDefinition rootProcess = deployAndEnableProcessWithCallActivity(\"rootProcess\",\n                simpleProcess.getName(), rootStepName,\n                rootCallActivityName);\n        processDefinitions.add(rootProcess); // To clean in the end\n\n        // start P2, the call activities will start an instance of P1\n        final ProcessInstance rootProcessInstance = getProcessAPI().startProcess(rootProcess.getId());\n        final ActivityInstance simpleTask = waitForUserTaskAndGetIt(rootProcessInstance, simpleStepName);\n        final ProcessInstance simpleProcessInstance = getProcessAPI()\n                .getProcessInstance(simpleTask.getParentProcessInstanceId());\n        assignAndExecuteStep(simpleTask, user.getId());\n        waitForProcessToFinish(simpleProcessInstance);\n        waitForUserTask(rootProcessInstance, rootStepName);\n\n        // check that only one instance (p2) is in the journal: p1 is supposed to be archived\n        List<ProcessInstance> processInstances = getProcessAPI().getProcessInstances(0, 10,\n                ProcessInstanceCriterion.NAME_ASC);\n        assertEquals(1, processInstances.size());\n\n        // check that there are archived instances of p1\n        List<ArchivedProcessInstance> archivedProcessInstanceList = getProcessAPI()\n                .getArchivedProcessInstances(simpleProcessInstance.getId(), 0, 10);\n        assertTrue(archivedProcessInstanceList.size() > 0);\n\n        // delete the root process instance\n        getProcessAPI().deleteProcessInstance(rootProcessInstance.getId());\n\n        // check that the instance of p2 was deleted\n        processInstances = getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.NAME_ASC);\n        assertEquals(0, processInstances.size());\n\n        // check that the archived instances of p1 were not deleted\n        archivedProcessInstanceList = getProcessAPI().getArchivedProcessInstances(simpleProcessInstance.getId(), 0, 10);\n        assertEquals(0, archivedProcessInstanceList.size());\n\n        // check that archived flow node were not deleted.\n        final List<ArchivedActivityInstance> taskInstances = getProcessAPI().getArchivedActivityInstances(\n                rootProcessInstance.getId(), 0, 10,\n                ActivityInstanceCriterion.DEFAULT);\n        assertEquals(0, taskInstances.size());\n    }\n\n    @Test\n    public void deleteArchivedProcessInstanceAndChildrenProcesses() throws Exception {\n        // deploy a simple process P1\n        final String simpleStepName = \"simpleStep\";\n        final ProcessDefinition simpleProcess = deployAndEnableSimpleProcess(\"simpleProcess\", simpleStepName);\n        processDefinitions.add(simpleProcess); // To clean in the end\n\n        // deploy a process P2 containing a call activity calling P1\n        final String rootStepName = \"rootStep1\";\n        final String rootCallActivityName = \"rootCall\";\n        final ProcessDefinition rootProcess = deployAndEnableProcessWithCallActivity(\"rootProcess\",\n                simpleProcess.getName(), rootStepName,\n                rootCallActivityName);\n        processDefinitions.add(rootProcess); // To clean in the end\n\n        // start P2, the call activities will start an instance of P1\n        final ProcessInstance rootProcessInstance = getProcessAPI().startProcess(rootProcess.getId());\n        final ActivityInstance simpleTask = waitForUserTaskAndAssignIt(rootProcessInstance, simpleStepName, user);\n        final ProcessInstance simpleProcessInstance = getProcessAPI()\n                .getProcessInstance(simpleTask.getParentProcessInstanceId());\n        getProcessAPI().executeFlowNode(simpleTask.getId());\n\n        waitForUserTask(rootProcessInstance, rootStepName);\n        waitForProcessToFinish(simpleProcessInstance);\n\n        // check that only one instance (p2) is in the journal: p1 is supposed to be archived\n        List<ProcessInstance> processInstances = getProcessAPI().getProcessInstances(0, 10,\n                ProcessInstanceCriterion.NAME_ASC);\n        assertEquals(1, processInstances.size());\n\n        // check that there are archived instances of p1\n        List<ArchivedProcessInstance> archivedProcessInstanceList = getProcessAPI()\n                .getArchivedProcessInstances(simpleProcessInstance.getId(), 0, 10);\n        assertTrue(archivedProcessInstanceList.size() > 0);\n\n        // delete archived root process instances\n        getProcessAPI().deleteArchivedProcessInstances(rootProcess.getId(), 0, 30);\n\n        // check that the instance of p2 was not deleted\n        processInstances = getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.NAME_ASC);\n        assertEquals(1, processInstances.size());\n\n        // check that the archived instances of p1 were deleted\n        archivedProcessInstanceList = getProcessAPI().getArchivedProcessInstances(simpleProcessInstance.getId(), 0,\n                1000);\n        assertEquals(0, archivedProcessInstanceList.size());\n\n        // check that archived flow node were deleted.\n        final List<ArchivedActivityInstance> taskInstances = getProcessAPI().getArchivedActivityInstances(\n                rootProcessInstance.getId(), 0, 10,\n                ActivityInstanceCriterion.DEFAULT);\n        assertEquals(0, taskInstances.size());\n    }\n\n    @Test\n    public void deleteProcessInstanceAlsoDeleteEventSubProcesses() throws Exception {\n        final String parentTaskName = \"step1\";\n        final String childTaskName = \"subStep\";\n        final String signalName = \"go\";\n        // deploy and create a instance of a process containing an event sub-process\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithSignalEventSubProcess(parentTaskName,\n                childTaskName, signalName);\n        processDefinitions.add(processDefinition); // To clean in the end\n        final ProcessInstance rootProcessInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        // wait for the first step in the parent process before sending signal the launch the event sub-process\n        waitForUserTask(rootProcessInstance, parentTaskName);\n        getProcessAPI().sendSignal(signalName);\n\n        // wait for first step in the event sub-process\n        waitForUserTask(rootProcessInstance, childTaskName);\n\n        // check the number of process instances: 2 expected the root process instance and the event subprocess\n        List<ProcessInstance> processInstances = getProcessAPI().getProcessInstances(0, 10,\n                ProcessInstanceCriterion.DEFAULT);\n        assertEquals(2, processInstances.size());\n\n        // delete the root process instance: the event subprocess must be deleted at same time\n        getProcessAPI().deleteProcessInstance(rootProcessInstance.getId());\n\n        // check the number of proces instances\n        processInstances = getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.DEFAULT);\n        assertEquals(0, processInstances.size());\n    }\n\n    private ProcessDefinition deployAndEnableProcessWithSignalEventSubProcess(final String parentTaskName,\n            final String childTaskName, final String signalName)\n            throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"ProcessWithEventSubProcess\", \"1.0\");\n        builder.addActor(\"mainActor\");\n        builder.addStartEvent(\"start\");\n        builder.addUserTask(parentTaskName, \"mainActor\");\n        builder.addEndEvent(\"end\");\n        builder.addTransition(\"start\", parentTaskName);\n        builder.addTransition(parentTaskName, \"end\");\n        final SubProcessDefinitionBuilder subProcessBuilder = builder.addSubProcess(\"eventSubProcess\", true)\n                .getSubProcessBuilder();\n        subProcessBuilder.addStartEvent(\"startSub\").addSignalEventTrigger(signalName);\n        subProcessBuilder.addUserTask(childTaskName, \"mainActor\");\n        subProcessBuilder.addEndEvent(\"endSubProcess\");\n        subProcessBuilder.addTransition(\"startSub\", childTaskName);\n        subProcessBuilder.addTransition(childTaskName, \"endSubProcess\");\n        final DesignProcessDefinition processDefinition = builder.done();\n        return deployAndEnableProcessWithActor(processDefinition, \"mainActor\", user);\n    }\n\n    @Test(expected = DeletionException.class)\n    public void deleteJustProcessDefinition() throws Exception {\n        // deploy a simple process\n        final String userTaskName = \"step1\";\n        final ProcessDefinition processDefinition = deployAndEnableSimpleProcess(\"myProcess\", userTaskName);\n        processDefinitions.add(processDefinition); // To clean in the end\n\n        // start a process and execute it until end\n        final ProcessInstance processInstanceToArchive = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(processInstanceToArchive, userTaskName, user);\n        waitForProcessToFinish(processInstanceToArchive);\n\n        // start a process non completed process\n        final ProcessInstance activeProcessInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(activeProcessInstance, userTaskName);\n\n        // check number of process instances and archived process instances\n        List<ProcessInstance> processInstances = getProcessAPI().getProcessInstances(0, 10,\n                ProcessInstanceCriterion.DEFAULT);\n        final List<ArchivedProcessInstance> archivedProcessInstances = getProcessAPI().getArchivedProcessInstances(0,\n                10, ProcessInstanceCriterion.DEFAULT);\n        assertEquals(1, processInstances.size());\n        assertEquals(1, archivedProcessInstances.size());\n\n        // delete definition\n        try {\n            getProcessAPI().disableAndDeleteProcessDefinition(processDefinition.getId());\n        } finally {\n            // check number of process instances and archived process instances\n            processInstances = getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.DEFAULT);\n            assertFalse(processInstances.isEmpty());\n\n            List<ArchivedProcessInstance> archivedProcessInstanceList = getProcessAPI()\n                    .getArchivedProcessInstances(processInstanceToArchive.getId(), 0, 10);\n            assertFalse(archivedProcessInstanceList.isEmpty());\n\n            archivedProcessInstanceList = getProcessAPI().getArchivedProcessInstances(activeProcessInstance.getId(), 0,\n                    10);\n            assertFalse(archivedProcessInstanceList.isEmpty());\n        }\n    }\n\n    @Test\n    public void deleteProcessDefinition() throws Exception {\n        // deploy a simple process\n        final String userTaskName = \"step1\";\n        final ProcessDefinition processDefinition = deployAndEnableSimpleProcess(\"myProcess\", userTaskName);\n\n        // delete definition\n        getProcessAPI().disableAndDeleteProcessDefinition(processDefinition.getId());\n\n        final SearchOptionsBuilder optsBuilder = new SearchOptionsBuilder(0, 5);\n        optsBuilder.sort(ProcessDeploymentInfoSearchDescriptor.DEPLOYMENT_DATE, Order.DESC);\n        final SearchResult<ProcessDeploymentInfo> searchResult = getProcessAPI()\n                .searchProcessDeploymentInfos(optsBuilder.done());\n        assertEquals(0, searchResult.getCount());\n        assertTrue(searchResult.getResult().isEmpty());\n    }\n\n    @Test\n    public void should_delete_archived_version_of_process_instance() throws Exception {\n        // deploy a simple process\n        final String userTaskName = \"step1\";\n        final ProcessDefinition processDefinition = deployAndEnableSimpleProcess(\"myProcess\", userTaskName);\n        processDefinitions.add(processDefinition); // To clean in the end\n\n        // start a process non completed process\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance, userTaskName);\n\n        // delete the process instance\n        getProcessAPI().deleteProcessInstance(processInstance.getId());\n\n        // check that all archived process instance related to this process were deleted\n        final List<ArchivedProcessInstance> archivedProcessInstanceList = getProcessAPI()\n                .getArchivedProcessInstances(processInstance.getId(), 0, 10);\n        assertEquals(0, archivedProcessInstanceList.size());\n    }\n\n    @Test\n    public void deleteArchivedProcessInstances() throws Exception {\n        // deploy a simple process\n        final String userTaskName = \"step1\";\n        final ProcessDefinition processDefinition = deployAndEnableSimpleProcess(\"myProcess\", userTaskName);\n        processDefinitions.add(processDefinition); // To clean in the end\n\n        // start a process non completed process\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance, userTaskName);\n\n        // delete archived process instances\n        getProcessAPI().deleteArchivedProcessInstances(processDefinition.getId(), 0, 1000);\n\n        // check that all archived process instance related to this process were deleted\n        final List<ArchivedProcessInstance> archivedProcessInstanceList = getProcessAPI()\n                .getArchivedProcessInstances(processInstance.getId(), 0, 10);\n        assertEquals(0, archivedProcessInstanceList.size());\n    }\n\n    @Test\n    public void deleteProcessInstanceAlsoDeleteDocuments() throws Exception {\n        // deploy and instantiate a process containing data and documents\n        final String userTaskName = \"step1\";\n        final String url = \"http://intranet.bonitasoft.com/private/docStorage/anyValue\";\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithDocument(\"myProcess\", userTaskName, \"Doc\",\n                url);\n        processDefinitions.add(processDefinition); // To clean in the end\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance, userTaskName);\n\n        // check the number of data and documents\n        SearchResult<Document> documentsSearchResult = getProcessAPI()\n                .searchDocuments(new SearchOptionsBuilder(0, 10).done());\n        assertEquals(1, documentsSearchResult.getCount());\n\n        // delete process instance\n        getProcessAPI().deleteProcessInstance(processInstance.getId());\n\n        // check the number of data and documents\n        documentsSearchResult = getProcessAPI().searchDocuments(new SearchOptionsBuilder(0, 10).done());\n        assertEquals(0, documentsSearchResult.getCount());\n    }\n\n    private ProcessDefinition deployAndEnableProcessWithDocument(final String processName, final String userTaskName,\n            final String docName, final String url)\n            throws Exception {\n        final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder().createNewInstance(processName,\n                \"1.0\");\n        processDefBuilder.addActor(ACTOR_NAME);\n        final DocumentDefinitionBuilder documentDefinitionBuilder = processDefBuilder.addDocumentDefinition(docName);\n        documentDefinitionBuilder.addUrl(url);\n        processDefBuilder.addStartEvent(\"tStart\");\n        processDefBuilder.addUserTask(userTaskName, ACTOR_NAME);\n        processDefBuilder.addEndEvent(\"tEnd\");\n        processDefBuilder.addTransition(\"tStart\", userTaskName);\n        processDefBuilder.addTransition(userTaskName, \"tEnd\");\n        return deployAndEnableProcessWithActor(processDefBuilder.done(), ACTOR_NAME, user);\n    }\n\n    @Test\n    public void deleteProcessInstanceAlsoDeleteComments() throws Exception {\n        // deploy and start a simple process\n        final String userTaskName = \"step1\";\n        final ProcessDefinition processDefinition = deployAndEnableSimpleProcess(\"myProcess\", userTaskName);\n        processDefinitions.add(processDefinition); // To clean in the end\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance, userTaskName);\n\n        // add a comment\n        getProcessAPI().addProcessComment(processInstance.getId(), \"just do it.\");\n\n        SearchResult<Comment> searchResult = getProcessAPI().searchComments(new SearchOptionsBuilder(0, 10).done());\n        assertTrue(searchResult.getCount() > 0);\n\n        // delete process instance\n        getProcessAPI().deleteProcessInstance(processInstance.getId());\n\n        // check all comments were deleted\n        searchResult = getProcessAPI().searchComments(new SearchOptionsBuilder(0, 10).done());\n        assertEquals(0, searchResult.getCount());\n    }\n\n    @Test\n    public void deleteProcessInstanceAndComments() throws Exception {\n        // deploy and start a simple process\n        final String userTaskName = \"etapa1\";\n        final ProcessDefinition processDefinition = deployAndEnableSimpleProcess(\"ArchivedCommentsDeletion\",\n                userTaskName);\n        processDefinitions.add(processDefinition); // To clean in the end\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long userTaskId = waitForUserTask(processInstance, userTaskName);\n\n        // add a comment\n        getProcessAPI().addProcessComment(processInstance.getId(), \"just do it.\");\n        assignAndExecuteStep(userTaskId, user);\n        waitForProcessToFinish(processInstance);\n\n        SearchResult<ArchivedComment> searchResult = getProcessAPI()\n                .searchArchivedComments(new SearchOptionsBuilder(0, 10).done());\n        assertTrue(searchResult.getCount() > 0);\n\n        getProcessAPI().deleteProcessInstances(processDefinition.getId(), 0, 10);\n\n        // check all archived comments were deleted along with process instance:\n        searchResult = getProcessAPI().searchArchivedComments(new SearchOptionsBuilder(0, 10).done());\n        assertEquals(2, searchResult.getCount()); // \"just do it.\" && technical comment\n    }\n\n    @Test\n    public void deleteArchivedProcessInstanceAndComments() throws Exception {\n        // deploy and start a simple process\n        final String userTaskName = \"etapa1\";\n        final ProcessDefinition processDefinition = deployAndEnableSimpleProcess(\"ArchivedCommentsDeletion\",\n                userTaskName);\n        processDefinitions.add(processDefinition); // To clean in the end\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long userTaskId = waitForUserTask(processInstance, userTaskName);\n\n        // add a comment\n        getProcessAPI().addProcessComment(processInstance.getId(), \"just do it2.\");\n        assignAndExecuteStep(userTaskId, user);\n        waitForProcessToFinish(processInstance);\n\n        SearchResult<ArchivedComment> searchResult = getProcessAPI()\n                .searchArchivedComments(new SearchOptionsBuilder(0, 10).done());\n        assertTrue(searchResult.getCount() > 0);\n\n        getProcessAPI().deleteArchivedProcessInstances(processDefinition.getId(), 0, 10);\n\n        // check all archived comments were deleted along with process instance:\n        searchResult = getProcessAPI().searchArchivedComments(new SearchOptionsBuilder(0, 10).done());\n        assertEquals(0, searchResult.getCount());\n    }\n\n    @Test\n    public void deleteShouldNotKeepResources() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process\", \"1.0\",\n                        Arrays.asList(\"step1\"), Arrays.asList(true));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n\n        Category category = getProcessAPI().createCategory(\"processCategory\", \"category assigned to process\");\n        getProcessAPI().addCategoriesToProcess(processDefinition.getId(), singletonList(category.getId()));\n\n        assertEquals(1, getProcessAPI()\n                .getCategoriesOfProcessDefinition(processDefinition.getId(), 0, 5, CategoryCriterion.NAME_ASC).size());\n\n        getProcessAPI().createProcessSupervisorForUser(processDefinition.getId(), user.getId());\n        getProcessAPI().createProcessSupervisorForGroup(processDefinition.getId(),\n                createGroup(\"supervisingGroup\").getId());\n        getProcessAPI().createProcessSupervisorForRole(processDefinition.getId(),\n                createRole(\"supervisingRole\").getId());\n\n        SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 5);\n        builder.filter(ProcessSupervisorSearchDescriptor.PROCESS_DEFINITION_ID, processDefinition.getId());\n        assertEquals(3, getProcessAPI().searchProcessSupervisors(builder.done()).getCount());\n\n        disableAndDeleteProcess(processDefinition);\n\n        assertEquals(0, getProcessAPI()\n                .getCategoriesOfProcessDefinition(processDefinition.getId(), 0, 5, CategoryCriterion.NAME_ASC).size());\n        assertEquals(0, getProcessAPI().searchProcessSupervisors(builder.done()).getCount());\n\n        assertNotNull(getProcessAPI().getCategory(category.getId()));\n    }\n\n    @Test\n    public void deleteShouldNotModifyAnotherProcess() throws Exception {\n        final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process1\", \"1.0\",\n                        Arrays.asList(\"step1\"), Arrays.asList(true));\n        final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition1,\n                ACTOR_NAME,\n                user);\n\n        final DesignProcessDefinition designProcessDefinition2 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process2\", \"1.0\",\n                        Arrays.asList(\"step1\"), Arrays.asList(true));\n        final ProcessDefinition processDefinition2 = deployAndEnableProcessWithActor(designProcessDefinition2,\n                ACTOR_NAME,\n                user);\n\n        Category category1 = getProcessAPI().createCategory(\"processCategory1\", \"category assigned to both processes\");\n        getProcessAPI().addCategoriesToProcess(processDefinition1.getId(), singletonList(category1.getId()));\n        getProcessAPI().addCategoriesToProcess(processDefinition2.getId(), singletonList(category1.getId()));\n        Category category2 = getProcessAPI().createCategory(\"processCategory2\",\n                \"category assigned only to second process\");\n        getProcessAPI().addCategoriesToProcess(processDefinition2.getId(), singletonList(category2.getId()));\n\n        assertEquals(1, getProcessAPI()\n                .getCategoriesOfProcessDefinition(processDefinition1.getId(), 0, 5, CategoryCriterion.NAME_ASC).size());\n        assertEquals(2, getProcessAPI()\n                .getCategoriesOfProcessDefinition(processDefinition2.getId(), 0, 5, CategoryCriterion.NAME_ASC).size());\n\n        getProcessAPI().createProcessSupervisorForUser(processDefinition1.getId(), user.getId());\n        getProcessAPI().createProcessSupervisorForUser(processDefinition2.getId(), user.getId());\n\n        SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 5);\n        builder.filter(ProcessSupervisorSearchDescriptor.USER_ID, user.getId());\n        assertEquals(2, getProcessAPI().searchProcessSupervisors(builder.done()).getCount());\n\n        disableAndDeleteProcess(processDefinition1);\n\n        SearchOptionsBuilder builder2 = new SearchOptionsBuilder(0, 5);\n        builder2.filter(ProcessSupervisorSearchDescriptor.PROCESS_DEFINITION_ID, processDefinition1.getId());\n\n        assertEquals(0, getProcessAPI()\n                .getCategoriesOfProcessDefinition(processDefinition1.getId(), 0, 5, CategoryCriterion.NAME_ASC).size());\n        assertEquals(0, getProcessAPI().searchProcessSupervisors(builder2.done()).getCount());\n\n        SearchOptionsBuilder builder3 = new SearchOptionsBuilder(0, 5);\n        builder3.filter(ProcessSupervisorSearchDescriptor.PROCESS_DEFINITION_ID, processDefinition2.getId());\n\n        assertEquals(2, getProcessAPI().getCategoriesOfProcessDefinition(processDefinition2.getId(), 0, 5,\n                CategoryCriterion.NAME_ASC).size());\n        assertEquals(1, getProcessAPI().searchProcessSupervisors(builder3.done()).getCount());\n\n        assertNotNull(getProcessAPI().getCategory(category1.getId()));\n        assertNotNull(getProcessAPI().getCategory(category2.getId()));\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/ProcessDeploymentIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.process;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.assertEquals;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.File;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Random;\n\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.bar.BarResource;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveFactory;\nimport org.bonitasoft.engine.bpm.bar.actorMapping.Actor;\nimport org.bonitasoft.engine.bpm.bar.actorMapping.ActorMapping;\nimport org.bonitasoft.engine.bpm.bar.form.model.FormMappingDefinition;\nimport org.bonitasoft.engine.bpm.bar.form.model.FormMappingModel;\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.bpm.process.*;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.connectors.TestConnectorWithOutput;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.form.FormMappingTarget;\nimport org.bonitasoft.engine.form.FormMappingType;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.test.APITestUtil;\nimport org.bonitasoft.engine.test.BuildTestUtil;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.TemporaryFolder;\n\n/**\n * @author Baptiste Mesta\n * @author Elias Ricken de Medeiros\n * @author Frédéric Bouquet\n * @author Céline Souchet\n */\npublic class ProcessDeploymentIT extends TestWithUser {\n\n    @Rule\n    public TemporaryFolder temporaryFolder = new TemporaryFolder();\n\n    @Test\n    public void deployProcessInDisabledState() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\n                        Arrays.asList(\"step1\", \"step2\"),\n                        Arrays.asList(true, true));\n\n        final ProcessDefinition processDefinition = deployProcess(\n                new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(designProcessDefinition)\n                        .done());\n        addUserToFirstActorOfProcess(user.getId(), processDefinition);\n\n        ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ConfigurationState.RESOLVED, processDeploymentInfo.getConfigurationState());\n        getProcessAPI().enableProcess(processDefinition.getId());\n        processDeploymentInfo = getProcessAPI().getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        // Clean up\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void deployProcessFromFile() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true));\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(designProcessDefinition).done();\n        File folder = temporaryFolder.newFolder();\n        File tempFile = new File(folder, \"tempFile\");\n        BusinessArchiveFactory.writeBusinessArchiveToFile(businessArchive, tempFile);\n\n        // read from the file\n        final BusinessArchive readBusinessArchive = BusinessArchiveFactory.readBusinessArchive(tempFile);\n\n        final ProcessDefinition processDefinition = deployProcess(readBusinessArchive);\n        addUserToFirstActorOfProcess(user.getId(), processDefinition);\n\n        ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ConfigurationState.RESOLVED, processDeploymentInfo.getConfigurationState());\n        getProcessAPI().enableProcess(processDefinition.getId());\n        processDeploymentInfo = getProcessAPI().getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        // Clean up\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void deployProcessWithUTF8Characteres() throws Exception {\n        // Create process\n        final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList(\"step1_1\"),\n                        Arrays.asList(false));\n        final ProcessDefinition processDefinition1 = deployAndEnableProcess(designProcessDefinition1);\n\n        assertEquals(APITestUtil.PROCESS_NAME, processDefinition1.getName());\n        // delete data for test\n        disableAndDeleteProcess(processDefinition1);\n    }\n\n    @Test\n    public void deployBigProcess() throws Exception {\n        // Create process\n        final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList(\"step1_1\"),\n                        Arrays.asList(false));\n        final byte[] bigContent = new byte[1024 * 1024 * 5];\n        new Random().nextBytes(bigContent);\n\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(designProcessDefinition1)\n                .addClasspathResource(new BarResource(\"bigRessource\", bigContent)).done();\n        final ProcessDefinition processDefinition = deployProcess(businessArchive);\n        getProcessAPI().enableProcess(processDefinition.getId());\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test(expected = AlreadyExistsException.class)\n    public void deployProcess2Times() throws Exception {\n        // First process def with 2 instances:\n        final DesignProcessDefinition designProcessDef1 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList(\"initTask1\"),\n                        Arrays.asList(true));\n        final ProcessDefinition processDef1 = deployAndEnableProcessWithActor(designProcessDef1, APITestUtil.ACTOR_NAME,\n                user);\n        try {\n            deployAndEnableProcessWithActor(designProcessDef1, APITestUtil.ACTOR_NAME, user);\n        } finally {\n            disableAndDeleteProcess(processDef1);\n        }\n    }\n\n    @Test\n    public void exportBusinessArchiveWithAllArtifacts() throws Exception {\n        final User john = createUser(\"john\", \"bpm\");\n        final User jack = createUser(\"jack\", \"bpm\");\n        //create the process\n        final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive();\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcess\", \"1.0\");\n        processDefinitionBuilder.addConnector(\"connectorName\", \"theConnectorId\", \"theConnectorVersion\",\n                ConnectorEvent.ON_ENTER);\n        processDefinitionBuilder.addActor(\"actor\");\n        processDefinitionBuilder.addUserTask(\"step1\", \"actor\").addUserFilter(\"userFilterName\", \"theUserFilterId\",\n                \"theUserFilterVersion\");\n        processDefinitionBuilder.addParameter(\"param1\", String.class.getName());\n        processDefinitionBuilder.addDocumentDefinition(\"myDoc\").addContentFileName(\"myPdfModifiedName.pdf\")\n                .addDescription(\"a cool pdf document\")\n                .addMimeType(\"application/pdf\")\n                .addFile(\"myPdf.pdf\").addDescription(\"my description\");\n        businessArchiveBuilder.setProcessDefinition(processDefinitionBuilder.done());\n\n        //create the business archive\n        businessArchiveBuilder.setParameters(Collections.singletonMap(\"param1\", \"theValue\"));\n        final FormMappingModel formMappingModel = new FormMappingModel();\n        formMappingModel.addFormMapping(\n                new FormMappingDefinition(\"theUrl\", FormMappingType.TASK, FormMappingTarget.URL, \"step1\"));\n        businessArchiveBuilder.setFormMappings(formMappingModel);\n        final Actor actor = new Actor(\"actor\");\n        actor.addUser(\"john\");\n        final ActorMapping actorMapping = new ActorMapping();\n        actorMapping.addActor(actor);\n        businessArchiveBuilder.setActorMapping(actorMapping);\n        final byte[] connectorImplementationFile = BuildTestUtil.buildConnectorImplementationFile(\"theConnectorId\",\n                \"theConnectorVersion\", \"impl1\", \"1.0\",\n                TestConnectorWithOutput.class.getName());\n        businessArchiveBuilder\n                .addConnectorImplementation(new BarResource(\"theConnctor.impl\", connectorImplementationFile));\n        final byte[] userFilterImpl = BuildTestUtil.buildConnectorImplementationFile(\"theConnectorId\",\n                \"theConnectorVersion\", \"impl1\", \"1.0\",\n                TestConnectorWithOutput.class.getName());\n        businessArchiveBuilder.addUserFilters(new BarResource(\"theUserFilter.impl\", userFilterImpl));\n        final byte[] pdfContent = new byte[] { 5, 0, 1, 4, 6, 5, 2, 3, 1, 5, 6, 8, 4, 6, 6, 3, 2, 4, 5 };\n        businessArchiveBuilder.addDocumentResource(new BarResource(\"myPdf.pdf\", pdfContent));\n        businessArchiveBuilder\n                .addClasspathResource(BuildTestUtil.generateJarAndBuildBarResource(ProcessAPI.class, \"myJar,jar\"));\n        businessArchiveBuilder.addExternalResource(new BarResource(\"index.html\", \"<html>\".getBytes()));\n        businessArchiveBuilder.addExternalResource(new BarResource(\"content/other.html\", \"<html>1\".getBytes()));\n\n        //deploy\n        final BusinessArchive businessArchive = businessArchiveBuilder.done();\n        final ProcessDefinition processDefinition = getProcessAPI().deploy(businessArchive);\n        assertThat(getProcessAPI().getProcessResolutionProblems(processDefinition.getId())).isEmpty();\n        getProcessAPI().enableProcess(processDefinition.getId());\n\n        //modify\n\n        //export\n        final byte[] bytes = getProcessAPI().exportBarProcessContentUnderHome(processDefinition.getId());\n        final BusinessArchive exportedBAR = BusinessArchiveFactory.readBusinessArchive(new ByteArrayInputStream(bytes));\n\n        //check\n        assertThat(exportedBAR.getResources().keySet()).containsAll(businessArchive.getResources().keySet());\n        assertThat(exportedBAR.getFormMappingModel().getFormMappings())\n                .containsAll(businessArchive.getFormMappingModel().getFormMappings());\n        assertThat(exportedBAR.getParameters()).isEqualTo(businessArchive.getParameters());\n        assertThat(exportedBAR.getProcessDefinition()).isEqualTo(businessArchive.getProcessDefinition());\n\n        deleteUsers(john, jack);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/ProcessDescriptionIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.process;\n\nimport static org.junit.Assert.assertEquals;\n\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.data.DataInstance;\nimport org.bonitasoft.engine.bpm.flownode.GatewayType;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.junit.Test;\n\npublic class ProcessDescriptionIT extends TestWithUser {\n\n    @Test\n    public void allInstanceDescriptions() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(\"descProcess\",\n                \"1.0\");\n        processBuilder.addDescription(\"processDescription\");\n        processBuilder.addActor(ACTOR_NAME).addDescription(\"actorDescription\");\n        processBuilder.addBooleanData(\"booleanProcessData\", null).addDescription(\"descBooleanProcessData\");\n        processBuilder.addStartEvent(\"start\");\n        processBuilder.addGateway(\"gateway\", GatewayType.PARALLEL).addDescription(\"descGateway\");\n        processBuilder.addUserTask(\"userTask\", ACTOR_NAME).addDescription(\"descUserTask\")\n                .addBooleanData(\"booleanUserTaskData\", null)\n                .addDescription(\"descBooleanUserTaskData\");\n        processBuilder.addTransition(\"start\", \"gateway\");\n        processBuilder.addTransition(\"gateway\", \"userTask\");\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        assertEquals(\"processDescription\", processInstance.getDescription());\n\n        final DataInstance processDataInstance = getProcessAPI().getProcessDataInstance(\"booleanProcessData\",\n                processInstance.getId());\n        assertEquals(\"descBooleanProcessData\", processDataInstance.getDescription());\n\n        final long userTaskId = waitForUserTask(processInstance, \"userTask\");\n        final DataInstance activityDataInstance = getProcessAPI().getActivityDataInstance(\"booleanUserTaskData\",\n                userTaskId);\n        assertEquals(\"descBooleanUserTaskData\", activityDataInstance.getDescription());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/ProcessExecutionIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.process;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.test.BuildTestUtil.generateConnectorImplementation;\nimport static org.bonitasoft.engine.test.BuildTestUtil.generateJarAndBuildBarResource;\nimport static org.junit.Assert.*;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.apache.commons.lang3.time.DateUtils;\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.comment.Comment;\nimport org.bonitasoft.engine.bpm.comment.SearchCommentsDescriptor;\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.UserTaskInstance;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.test.APITestUtil;\nimport org.bonitasoft.engine.test.BuildTestUtil;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class ProcessExecutionIT extends TestWithUser {\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(ProcessExecutionIT.class);\n\n    /**\n     * there was an issue on deploy when the classloader needed to be refreshed\n     * (because of Schemafactory was loading parser not in transaction)\n     *\n     * @throws Exception\n     */\n    @Test\n    public void ensureADeployWorksAfterAChangeInDependencies() throws Exception {\n        final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process123\", \"1.0\",\n                        Arrays.asList(\"step1\"), Arrays.asList(true));\n        final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition1,\n                ACTOR_NAME, user);\n        getCommandAPI().addDependency(\"kikoo\", new byte[] { 0, 2, 3 });\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process\", \"1.0\",\n                        Arrays.asList(\"step1\"), Arrays.asList(true));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        disableAndDeleteProcess(processDefinition);\n        disableAndDeleteProcess(processDefinition1);\n        getCommandAPI().removeDependency(\"kikoo\");\n    }\n\n    @Test\n    public void startProcessWithCurrentUser() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process\", \"1.0\",\n                        Arrays.asList(\"step1\"), Arrays.asList(true));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        // Check that the current getSession() user name is the one used to start the process:\n        LOGGER.debug(\"current getSession() user name used to start the process: \" + processInstance.getStartedBy());\n        assertEquals(getSession().getUserId(), processInstance.getStartedBy());\n\n        // Clean up\n        waitForUserTask(processInstance, \"step1\");\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void startProcessFor() throws Exception {\n        final User jack = createUser(\"jack\", PASSWORD);\n\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process\", \"1.0\",\n                        Arrays.asList(\"step1\"), Arrays.asList(true));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(jack.getId(), processDefinition.getId());\n\n        try {\n            waitForUserTask(processInstance, \"step1\");\n            // Check that the given user name is the one used to start the process:\n            assertEquals(jack.getId(), processInstance.getStartedBy());\n            assertEquals(user.getId(), processInstance.getStartedBySubstitute());\n\n            // Check system comment\n            final SearchOptions searchOptions = new SearchOptionsBuilder(0, 100)\n                    .filter(SearchCommentsDescriptor.PROCESS_INSTANCE_ID, processInstance.getId())\n                    .done();\n            final List<Comment> comments = getProcessAPI().searchComments(searchOptions).getResult();\n            boolean haveCommentForDelegate = false;\n            for (final Comment comment : comments) {\n                haveCommentForDelegate = haveCommentForDelegate\n                        || comment.getContent().contains(\n                                \"The user \" + USERNAME + \" acting as delegate of the user jack has started the case.\");\n            }\n            assertTrue(haveCommentForDelegate);\n        } finally {\n            // Clean up\n            disableAndDeleteProcess(processDefinition);\n            deleteUsers(jack);\n        }\n    }\n\n    @Test\n    public void createAndExecuteProcessWithAutomaticSteps() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process\", \"1.1\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(false, false));\n\n        final ProcessDefinition processDefinition = deployAndEnableProcess(designProcessDefinition);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(user.getId(), processDefinition.getId());\n        waitForProcessToFinish(processInstance);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void deleteUnknownProcess() throws Exception {\n        expectedException.expect(DeletionException.class);\n        getProcessAPI().deleteProcessDefinition(123456789);\n    }\n\n    @Test\n    public void executeProcessWithNoActivities() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process\", \"1.3\",\n                        Collections.<String> emptyList(), Collections.<Boolean> emptyList());\n        final ProcessDefinition processDefinition = deployAndEnableProcess(designProcessDefinition);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        final List<ActivityInstance> activities = getProcessAPI().getActivities(processDefinition.getId(), 0, 200);\n        assertEquals(0, activities.size());\n        assertTrue(\"Process instance should be completed\",\n                containsState(getProcessAPI().getArchivedProcessInstances(processInstance.getId(), 0, 10),\n                        TestStates.NORMAL_FINAL));// FIXME\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void checkStartAndEndDate() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_ProcessToCheckDate\",\n                        \"1.0\", Arrays.asList(\"step1\"), Arrays.asList(true));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n\n        long before = new Date().getTime();\n        Thread.sleep(10);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        Thread.sleep(10);\n        long after = new Date().getTime();\n        final long startDate = processInstance.getStartDate().getTime();\n        assertTrue(\"The process instance must start between \" + before + \" and \" + after + \", but was \" + startDate,\n                after >= startDate && startDate >= before);\n        assertEquals(getSession().getUserId(), processInstance.getStartedBy());\n\n        final Long step1Id = waitForUserTask(\"step1\");\n        before = new Date().getTime();\n        assignAndExecuteStep(step1Id, user);\n        waitForProcessToFinish(processInstance);\n        after = new Date().getTime();\n        final long endDate = getProcessAPI().getFinalArchivedProcessInstance(processInstance.getId()).getEndDate()\n                .getTime();\n        assertTrue(\"The process instance must finish between \" + before + \" and \" + after + \", but was \" + endDate,\n                after >= endDate && endDate >= before);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void checkLastUpdateDateOfAnArchivedProcess() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_ProcessToCheckDate\",\n                        \"1.0\", Arrays.asList(\"step1\"), Arrays.asList(true));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n\n        long before = new Date().getTime();\n        Thread.sleep(10);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        Thread.sleep(10);\n        long after = new Date().getTime();\n        final long processStartDate = processInstance.getStartDate().getTime();\n        assertTrue(\"The process instance \" + processInstance.getName() + \" must start between <\" + before + \"> and <\"\n                + after + \">, but was <\"\n                + processStartDate + \">\", after >= processStartDate && processStartDate >= before);\n        assertEquals(getSession().getUserId(), processInstance.getStartedBy());\n\n        final Long step1Id = waitForUserTask(\"step1\");\n        before = new Date().getTime();\n        assignAndExecuteStep(step1Id, user);\n        waitForProcessToFinish(processInstance);\n        after = new Date().getTime();\n        final long lastUpdate = getProcessAPI().getFinalArchivedProcessInstance(processInstance.getId()).getLastUpdate()\n                .getTime();\n        assertTrue(\"The process instance \" + processInstance.getName() + \" must update in last between <\" + before\n                + \"> and <\" + after + \">, but was <\"\n                + lastUpdate + \">\", after >= lastUpdate && lastUpdate >= before);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void checkProcessIsArchived() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME);\n        builder.addStartEvent(\"start\");\n        builder.addUserTask(\"step1\", ACTOR_NAME);\n        builder.addEndEvent(\"end\");\n        builder.addTransition(\"start\", \"step1\").addTransition(\"step1\", \"end\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final Long step1Id = waitForUserTask(processInstance, \"step1\");\n\n        final List<ArchivedProcessInstance> archs = getProcessAPI().getArchivedProcessInstances(processInstance.getId(),\n                0, 100);\n        assertEquals(1, archs.size());\n        assertEquals(TestStates.INITIALIZING.getStateName(), archs.get(0).getState());\n\n        assignAndExecuteStep(step1Id, user);\n        waitForProcessToFinish(processInstance);\n\n        // Verify if the process instance is archived\n        final ArchivedProcessInstance archivedProcessInstance = getProcessAPI()\n                .getFinalArchivedProcessInstance(processInstance.getId());\n        assertNotNull(archivedProcessInstance);\n        try {\n            getProcessAPI().getProcessInstance(processInstance.getId());\n            fail(\"A ProcessInstanceNotFoundException should have been raised\");\n        } catch (final ProcessInstanceNotFoundException e) {\n            // ok\n        }\n\n        // Verify if the flow node instances are archived\n        final SearchOptionsBuilder optionsBuilder = new SearchOptionsBuilder(0, 20);\n        optionsBuilder.sort(ArchivedFlowNodeInstanceSearchDescriptor.NAME, Order.ASC);\n        optionsBuilder.filter(ArchivedFlowNodeInstanceSearchDescriptor.STATE_NAME, \"completed\");\n        final List<ArchivedFlowNodeInstance> archivedFlowNodeInstances = getProcessAPI()\n                .searchArchivedFlowNodeInstances(optionsBuilder.done()).getResult();\n        // To uncomment if need to fix BS-11970\n        //        assertEquals(3, archivedFlowNodeInstances.size());\n        //        assertEquals(\"end\", archivedFlowNodeInstances.get(0).getName());\n        //        assertEquals(\"start\", archivedFlowNodeInstances.get(1).getName());\n        //        assertEquals(\"step1\", archivedFlowNodeInstances.get(2).getName());\n        // To remove if need to fix BS-11970\n        assertEquals(1, archivedFlowNodeInstances.size());\n        assertEquals(\"step1\", archivedFlowNodeInstances.get(0).getName());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getArchivedProcessInstanceById() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"ProcessToArchive\", \"1.0\",\n                        Arrays.asList(\"step1\"), Arrays.asList(true));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition,\n                APITestUtil.ACTOR_NAME, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance, \"step1\");\n        final List<ArchivedProcessInstance> archs = getProcessAPI().getArchivedProcessInstances(processInstance.getId(),\n                0, 100);\n        assertEquals(1, archs.size());\n        final ArchivedProcessInstance archivedProcessInstance = archs.get(0);\n        assertEquals(archivedProcessInstance,\n                getProcessAPI().getArchivedProcessInstance(archivedProcessInstance.getId()));\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test(expected = ArchivedProcessInstanceNotFoundException.class)\n    public void getArchivedProcessInstanceByIdNotFound() throws Exception {\n        getProcessAPI().getArchivedProcessInstance(123456789L);\n    }\n\n    @Test\n    public void checkArchiveDate() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_ProcessToCheckDate\",\n                        \"1.0\", Arrays.asList(\"step1\"), Arrays.asList(true));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n        final long before = new Date().getTime();\n        assignAndExecuteStep(step1Id, user.getId());\n        waitForProcessToFinish(processInstance);\n        final long after = new Date().getTime();\n        final ArchivedProcessInstance archivedProcessInstance = getProcessAPI()\n                .getFinalArchivedProcessInstance(processInstance.getId());\n        assertNotNull(archivedProcessInstance);\n        long archiveDate = archivedProcessInstance.getArchiveDate().getTime();\n        assertTrue(\"The process must be archived between \" + before + \" and \" + after + \", but was \" + archiveDate,\n                after >= archiveDate\n                        && archiveDate >= before);\n\n        final ArchivedActivityInstance archivedActivityInstance = getProcessAPI().getArchivedActivityInstance(step1Id);\n        assertNotNull(archivedActivityInstance);\n        assertEquals(step1Id, archivedActivityInstance.getSourceObjectId());\n        archiveDate = archivedActivityInstance.getArchiveDate().getTime();\n        assertTrue(\"The step1 must be archived between \" + before + \" and \" + after + \", but was \" + archiveDate,\n                after >= archiveDate && archiveDate >= before);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void checkArchiveStartedBy() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_ProcessToCheckDate\",\n                        \"1.0\", Arrays.asList(\"step1\"), Arrays.asList(true));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        assertEquals(user.getId(), processInstance.getStartedBy());\n\n        waitForUserTaskAndExecuteIt(processInstance, \"step1\", user);\n        waitForProcessToFinish(processInstance);\n        final ArchivedProcessInstance archivedProcessInstance = getProcessAPI()\n                .getFinalArchivedProcessInstance(processInstance.getId());\n        assertNotNull(archivedProcessInstance);\n        assertEquals(user.getId(), archivedProcessInstance.getStartedBy());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void activityDisplayDescriptionUndefined() throws Exception {\n        // create process definition;\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION)\n                .addActor(ACTOR_NAME).addAutomaticTask(\"auto1\").addUserTask(\"task1\", ACTOR_NAME).getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessInstance pi = getProcessAPI().startProcess(processDefinition.getId());\n\n        final ArchivedActivityInstance auto1 = waitForActivityInCompletedState(pi, \"auto1\", true);\n        assertEquals(null, auto1.getDisplayDescription());\n\n        final ActivityInstance activityInstance = waitForTaskInState(pi, \"task1\", TestStates.READY);\n        assertEquals(null, activityInstance.getDisplayDescription());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void should_update_and_sort_due_date_with_null_values() throws Exception {\n        //given\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithNullDueDate\", \"7.4\");\n        builder.addActor(ACTOR_NAME);\n        builder.addUserTask(\"step1\", ACTOR_NAME).addExpectedDuration(3600L);\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user);\n        final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance processInstance3 = getProcessAPI().startProcess(processDefinition.getId());\n        final UserTaskInstance todayTask = (UserTaskInstance) waitForUserTaskAndAssignIt(processInstance1, \"step1\",\n                user);\n        final UserTaskInstance nextWeekTask = (UserTaskInstance) waitForUserTaskAndAssignIt(processInstance2, \"step1\",\n                user);\n        final UserTaskInstance nullDueDate = (UserTaskInstance) waitForUserTaskAndAssignIt(processInstance3, \"step1\",\n                user);\n\n        //when\n        getProcessAPI().updateDueDateOfTask(nullDueDate.getId(), null);\n        final Date nextWeekDueDate = DateUtils.addDays(nextWeekTask.getExpectedEndDate(), 7);\n        getProcessAPI().updateDueDateOfTask(nextWeekTask.getId(), nextWeekDueDate);\n\n        //new business logic for sort to be used in portal\n        final SearchResult<HumanTaskInstance> ascNullsLast = getHumanTaskInstanceSearchResult(Order.ASC_NULLS_LAST);\n        final SearchResult<HumanTaskInstance> descNullsFirst = getHumanTaskInstanceSearchResult(Order.DESC_NULLS_FIRST);\n\n        //not required by business logic but tested to verify orderBy sql keyword are valid SQL\n        final SearchResult<HumanTaskInstance> ascNullsFirst = getHumanTaskInstanceSearchResult(Order.ASC_NULLS_FIRST);\n        final SearchResult<HumanTaskInstance> descNullsLast = getHumanTaskInstanceSearchResult(Order.DESC_NULLS_LAST);\n\n        //then\n        assertThat(ascNullsLast.getResult()).extracting(\"id\")\n                .as(\"should have null as last value\")\n                .containsExactly(todayTask.getId(), nextWeekTask.getId(), nullDueDate.getId());\n        assertThat(ascNullsFirst.getResult()).extracting(\"id\")\n                .as(\"should have null as first value\")\n                .containsExactly(nullDueDate.getId(), todayTask.getId(), nextWeekTask.getId());\n        assertThat(descNullsFirst.getResult()).extracting(\"id\")\n                .as(\"should have null as first value\")\n                .containsExactly(nullDueDate.getId(), nextWeekTask.getId(), todayTask.getId());\n        assertThat(descNullsLast.getResult()).extracting(\"id\")\n                .as(\"should have null as first value\")\n                .containsExactly(nextWeekTask.getId(), todayTask.getId(), nullDueDate.getId());\n\n        assertThat(getProcessAPI().getHumanTaskInstance(nullDueDate.getId()).getExpectedEndDate())\n                .as(\"should have updated expected date to null\").isNull();\n        assertThat(getProcessAPI().getHumanTaskInstance(nextWeekTask.getId()).getExpectedEndDate())\n                .as(\"should have updated expected date to next week\")\n                .isEqualTo(nextWeekDueDate);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    public SearchResult<HumanTaskInstance> getHumanTaskInstanceSearchResult(Order order) throws SearchException {\n        return getProcessAPI()\n                .searchHumanTaskInstances(new SearchOptionsBuilder(0, 100)\n                        .sort(HumanTaskInstanceSearchDescriptor.DUE_DATE, order).done());\n    }\n\n    @Test(expected = UpdateException.class)\n    public void updateDueDateOfUnknownTask() throws Exception {\n        getProcessAPI().updateDueDateOfTask(123456789L, new Date());\n    }\n\n    @Test\n    public void should_be_able_to_set_due_date_with_connector() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"processWithDueDate\",\n                \"1.0\");\n        builder.addActor(ACTOR_NAME);\n        builder.addUserTask(\"step1\", ACTOR_NAME)\n                .addConnector(\"setDueDate\", \"dueDateConnector\", \"1.0\", ConnectorEvent.ON_ENTER);\n\n        BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(builder.done())\n                .addConnectorImplementation(\n                        generateConnectorImplementation(\"dueDateConnector\", \"1.0\", SetDueDateConnector.class))\n                .addClasspathResource(generateJarAndBuildBarResource(SetDueDateConnector.class, \"dueDate.jar\")).done();\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchive, ACTOR_NAME, user);\n        final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition.getId());\n        final HumanTaskInstance step1 = waitForUserTaskAndGetIt(processInstance1, \"step1\");\n\n        assertThat(step1.getExpectedEndDate()).isEqualTo(SetDueDateConnector.dueDate);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeTaskFor() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true));\n        final User jack = createUser(\"jack\", PASSWORD);\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition,\n                APITestUtil.ACTOR_NAME, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        try {\n            // execute step 1 using john\n            final ActivityInstance step1 = waitForUserTaskAndGetIt(processInstance, \"step1\");\n            assertEquals(0, step1.getExecutedBy());\n\n            getProcessAPI().assignUserTask(step1.getId(), jack.getId());\n            getProcessAPI().executeFlowNode(jack.getId(), step1.getId());\n            waitForUserTask(processInstance, \"step2\");\n\n            // check that the step1 was executed by john\n            final ArchivedActivityInstance step1Archived = getProcessAPI().getArchivedActivityInstance(step1.getId());\n            assertEquals(jack.getId(), step1Archived.getExecutedBy());\n            assertEquals(user.getId(), step1Archived.getExecutedBySubstitute());\n\n            // Check system comment\n            final SearchOptions searchOptions = new SearchOptionsBuilder(0, 100)\n                    .filter(SearchCommentsDescriptor.PROCESS_INSTANCE_ID, processInstance.getId())\n                    .done();\n            final List<Comment> comments = getProcessAPI().searchComments(searchOptions).getResult();\n            boolean haveCommentForDelegate = false;\n            for (final Comment comment : comments) {\n                haveCommentForDelegate = haveCommentForDelegate\n                        || comment.getContent().contains(\"The user \" + USERNAME\n                                + \" acting as delegate of the user jack has done the task \\\"step1\\\".\");\n            }\n            assertTrue(haveCommentForDelegate);\n        } finally {\n            // clean\n            disableAndDeleteProcess(processDefinition);\n            deleteUsers(jack);\n        }\n    }\n\n    @Test\n    public void systemCommentsShouldBeAutoAddedAtTaskAssignment() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"ProcessToArchive\",\n                \"1.0\");\n        builder.addActor(ACTOR_NAME);\n        builder.addUserTask(\"step1\", ACTOR_NAME)\n                .addDisplayName(new ExpressionBuilder().createConstantStringExpression(\"Step1 display name\"));\n        builder.addUserTask(\"step2\", ACTOR_NAME);\n        builder.addTransition(\"step1\", \"step2\");\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(),\n                APITestUtil.ACTOR_NAME, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(processInstance, \"step1\", user);\n        waitForUserTask(processInstance, \"step2\");\n\n        final SearchResult<Comment> searchResult = getProcessAPI()\n                .searchComments(new SearchOptionsBuilder(0, 5).done());\n        final List<Comment> commentList = searchResult.getResult();\n        assertEquals(1, commentList.size());\n        assertEquals(\"The task \\\"Step1 display name\\\" is now assigned to \" + USERNAME, commentList.get(0).getContent());\n\n        assertEquals(1, getProcessAPI().countComments(new SearchOptionsBuilder(0, 5).done()));\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/ProcessManagementIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.process;\n\nimport static java.util.concurrent.TimeUnit.SECONDS;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.awaitility.Awaitility.await;\nimport static org.hamcrest.collection.IsCollectionWithSize.hasSize;\nimport static org.junit.Assert.*;\n\nimport java.io.Serializable;\nimport java.math.BigDecimal;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.assertj.core.api.Assertions;\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.bar.BarResource;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.connector.ArchiveConnectorInstancesSearchDescriptor;\nimport org.bonitasoft.engine.bpm.connector.ArchivedConnectorInstance;\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.bpm.connector.ConnectorState;\nimport org.bonitasoft.engine.bpm.data.DataInstance;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion;\nimport org.bonitasoft.engine.bpm.flownode.ActivityStates;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.TaskPriority;\nimport org.bonitasoft.engine.bpm.process.ActivationState;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ConfigurationState;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException;\nimport org.bonitasoft.engine.bpm.process.ProcessActivationException;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoUpdater;\nimport org.bonitasoft.engine.bpm.process.ProcessEnablementException;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceCriterion;\nimport org.bonitasoft.engine.bpm.process.impl.AutomaticTaskDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.StartEventDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.SubProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder;\nimport org.bonitasoft.engine.connectors.TestConnectorThatThrowException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.ExecutionException;\nimport org.bonitasoft.engine.exception.NotFoundException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.expression.ExpressionConstants;\nimport org.bonitasoft.engine.expression.InvalidExpressionException;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.operation.Operation;\nimport org.bonitasoft.engine.operation.OperationBuilder;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.test.APITestUtil;\nimport org.bonitasoft.engine.test.BuildTestUtil;\nimport org.bonitasoft.engine.test.StartProcessUntilStep;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.junit.Test;\n\n/**\n * @author Baptiste Mesta\n * @author Elias Ricken de Medeiros\n * @author Frédéric Bouquet\n * @author Céline Souchet\n */\npublic class ProcessManagementIT extends TestWithUser {\n\n    @Test\n    public void getProcessDeployInfo() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true));\n        final Date before = new Date();\n        Thread.sleep(10);\n        final ProcessDefinition processDefinition = deployProcess(\n                new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(designProcessDefinition)\n                        .done());\n        addUserToFirstActorOfProcess(user.getId(), processDefinition);\n        Thread.sleep(10);\n        final Date after = new Date();\n\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        deleteProcess(processDefinition);\n        assertNotNull(processDeploymentInfo);\n        assertEquals(ConfigurationState.RESOLVED, processDeploymentInfo.getConfigurationState());\n        assertEquals(APITestUtil.PROCESS_NAME, processDeploymentInfo.getName());\n        assertEquals(APITestUtil.PROCESS_VERSION, processDeploymentInfo.getVersion());\n        assertEquals(getSession().getUserId(), processDeploymentInfo.getDeployedBy());\n        final Date deployDate = processDeploymentInfo.getDeploymentDate();\n        assertTrue(\n                \"deploy date was too soon (expected > \" + before.getTime() + \" but was \" + deployDate.getTime() + \")\",\n                deployDate.after(before));\n        assertTrue(\"deploy date was too late (expected < \" + after.getTime() + \" but was \" + deployDate.getTime() + \")\",\n                deployDate.before(after));\n    }\n\n    @Test(expected = ProcessActivationException.class)\n    public void runDisabledProcess() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true));\n\n        final ProcessDefinition processDefinition = deployProcess(\n                new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(designProcessDefinition)\n                        .done());\n        addUserToFirstActorOfProcess(user.getId(), processDefinition);\n\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ConfigurationState.RESOLVED, processDeploymentInfo.getConfigurationState());\n\n        try {\n            getProcessAPI().startProcess(user.getId(), processDefinition.getId());\n        } finally {\n            deleteProcess(processDefinition);\n        }\n    }\n\n    @Test\n    public void disableProcess() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true));\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n        getProcessAPI().disableProcess(processDefinition.getId());\n        processDeploymentInfo = getProcessAPI().getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ConfigurationState.RESOLVED, processDeploymentInfo.getConfigurationState());\n\n        deleteProcess(processDefinition);\n    }\n\n    @Test(expected = ProcessActivationException.class)\n    public void disableDisabledProcess() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true));\n\n        final ProcessDefinition processDefinition = deployProcess(\n                new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(designProcessDefinition)\n                        .done());\n        try {\n            getProcessAPI().disableProcess(processDefinition.getId());\n            getProcessAPI().getProcessDeploymentInfo(processDefinition.getId());\n        } finally {\n            deleteProcess(processDefinition);\n        }\n    }\n\n    @Test\n    public void enableEnabledProcess() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true));\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        try {\n            getProcessAPI().enableProcess(processDefinition.getId());\n            final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                    .getProcessDeploymentInfo(processDefinition.getId());\n            assertEquals(ConfigurationState.RESOLVED, processDeploymentInfo.getConfigurationState());\n            fail(\"expected a ProcessEnablementException\");\n        } catch (final ProcessEnablementException e) {\n            // ok\n        } finally {\n            getProcessAPI().disableProcess(processDefinition.getId());\n            deleteProcess(processDefinition);\n        }\n    }\n\n    @Test(expected = DeletionException.class)\n    public void deleteEnabledProcess() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true));\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        try {\n            deleteProcess(processDefinition);\n        } finally {\n            getProcessAPI().disableProcess(processDefinition.getId());\n            deleteProcess(processDefinition);\n        }\n    }\n\n    @Test\n    public void deleteProcess() throws Exception {\n        final long numberOfProcesses = getProcessAPI().getNumberOfProcessDeploymentInfos();\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true));\n\n        final ProcessDefinition processDefinition = deployProcess(\n                new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(designProcessDefinition)\n                        .done());\n        assertNotNull(getProcessAPI().getProcessDefinition(processDefinition.getId()));\n        assertNotNull(getProcessAPI().getProcessDeploymentInfo(processDefinition.getId()));\n        assertEquals(numberOfProcesses + 1, getProcessAPI().getNumberOfProcessDeploymentInfos());\n        deleteProcess(processDefinition);\n        assertEquals(numberOfProcesses, getProcessAPI().getNumberOfProcessDeploymentInfos());\n        try {\n            getProcessAPI().getProcessDefinition(processDefinition.getId());\n            fail(\"Should throw ProcessDefinitionNotFoundException\");\n        } catch (final ProcessDefinitionNotFoundException e) {\n            // ok\n        }\n        try {\n            getProcessAPI().getProcessDeploymentInfo(processDefinition.getId());\n            fail(\"Should throw ProcessDefinitionNotFoundException\");\n        } catch (final ProcessDefinitionNotFoundException e) {\n            // ok\n        }\n    }\n\n    @Test\n    public void getProcessesList() throws Exception {\n        assertEquals(0, getProcessAPI().getNumberOfProcessDeploymentInfos());\n        final List<Long> ids = createProcessDefinitionWithTwoHumanStepsAndDeployBusinessArchive(89);\n\n        assertEquals(89, getProcessAPI().getNumberOfProcessDeploymentInfos());\n        final List<ProcessDeploymentInfo> processes = getProcessAPI().getProcessDeploymentInfos(0, 10,\n                ProcessDeploymentInfoCriterion.NAME_DESC);\n        assertEquals(10, processes.size());\n        assertEquals(\"ProcessName88\", processes.get(0).getName());\n        assertEquals(\"ProcessName79\", processes.get(9).getName());\n        final List<ProcessDeploymentInfo> processes1 = getProcessAPI().getProcessDeploymentInfos(0, 10,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertEquals(10, processes1.size());\n        assertEquals(\"ProcessName00\", processes1.get(0).getName());\n        assertEquals(\"ProcessName09\", processes1.get(9).getName());\n        final List<ProcessDeploymentInfo> processes2 = getProcessAPI().getProcessDeploymentInfos(20, 10,\n                ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertEquals(10, processes2.size());\n        assertEquals(\"ProcessName20\", processes2.get(0).getName());\n        assertEquals(\"ProcessName29\", processes2.get(9).getName());\n        getProcessAPI().deleteProcessDefinitions(ids);\n        assertEquals(0, getProcessAPI().getNumberOfProcessDeploymentInfos());\n    }\n\n    @Test(expected = InvalidProcessDefinitionException.class)\n    public void createProcessWithNoName() throws Exception {\n        final List<String> emptyList = Collections.emptyList();\n        final List<Boolean> emptyList2 = Collections.emptyList();\n        final DesignProcessDefinition processDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(null, null, emptyList,\n                        emptyList2);\n        deployProcess(\n                new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(processDefinition).done());\n    }\n\n    @Test\n    public void getArchivedActivityInstances() throws Exception {\n        final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(PROCESS_NAME + \"1\",\n                        PROCESS_VERSION, Arrays.asList(\"step1\", \"step2\"), Arrays.asList(false, false));\n        final ProcessDefinition processDefinition1 = deployAndEnableProcess(designProcessDefinition1);\n        final DesignProcessDefinition designProcessDefinition2 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(PROCESS_NAME + \"2\",\n                        PROCESS_VERSION, Arrays.asList(\"task1\", \"task2\", \"task3\"), Arrays.asList(false, false, false));\n        final ProcessDefinition processDefinition2 = deployAndEnableProcess(designProcessDefinition2);\n        final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition1.getId());\n        final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinition2.getId());\n        // one archive for each change in the activity state. For automatic tasks we have initializingAndExecuting, completed\n        await().atMost(20, SECONDS).until(() -> getProcessAPI().getArchivedActivityInstances(processInstance1.getId(),\n                0, 100, ActivityInstanceCriterion.DEFAULT), hasSize(2 * 2));\n        await().atMost(20, SECONDS).until(() -> getProcessAPI().getArchivedActivityInstances(processInstance2.getId(),\n                0, 100, ActivityInstanceCriterion.DEFAULT), hasSize(3 * 2));\n        waitForProcessToFinish(processInstance1);\n        waitForProcessToFinish(processInstance2);\n        disableAndDeleteProcess(processDefinition1, processDefinition2);\n    }\n\n    @Test\n    public void getArchivedActivityInstancesOrderByName() throws Exception {\n        getArchivedActivityInstancesOrderByPagingCriterion(ActivityInstanceCriterion.NAME_ASC, 0, 1, 2,\n                ActivityInstanceCriterion.NAME_DESC, 2, 1, 0);\n    }\n\n    private void getArchivedActivityInstancesOrderByPagingCriterion(final ActivityInstanceCriterion criterionAsc,\n            final int asc1, final int asc2,\n            final int asc3, final ActivityInstanceCriterion criterionDsc, final int desc1, final int desc2,\n            final int desc3) throws Exception {\n\n        final DesignProcessDefinition designProcessDefinition2 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(PROCESS_NAME\n                        + criterionAsc, PROCESS_VERSION, Arrays.asList(\"task1\", \"task2\", \"task3\"),\n                        Arrays.asList(false, false, false));\n        final ProcessDefinition processDefinition2 = deployAndEnableProcess(designProcessDefinition2);\n        final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinition2.getId());\n        final int nbActivities = 3; // task1, task2, task3\n        final int nbOfStates = 2; // executing, completed\n        checkNbOfArchivedActivityInstances(processInstance2, 3 * nbOfStates);\n        List<ArchivedActivityInstance> archivedActivityInstances = getProcessAPI()\n                .getArchivedActivityInstances(processInstance2.getId(), 0, 100, criterionAsc);\n        for (int i = 0; i < nbOfStates; i++) {\n            if (criterionAsc.equals(ActivityInstanceCriterion.REACHED_STATE_DATE_ASC)\n                    || criterionAsc.equals(ActivityInstanceCriterion.LAST_UPDATE_ASC)) {\n                assertEquals(\"task1\", archivedActivityInstances.get(asc1 + i * nbActivities).getName());\n                assertEquals(\"task2\", archivedActivityInstances.get(asc2 + i * nbActivities).getName());\n                assertEquals(\"task3\", archivedActivityInstances.get(asc3 + i * nbActivities).getName());\n            }\n            if (criterionAsc.equals(ActivityInstanceCriterion.NAME_ASC)) {\n                assertEquals(\"task1\", archivedActivityInstances.get(asc1 * nbOfStates + i).getName());\n                assertEquals(\"task2\", archivedActivityInstances.get(asc2 * nbOfStates + i).getName());\n                assertEquals(\"task3\", archivedActivityInstances.get(asc3 * nbOfStates + i).getName());\n            }\n        }\n\n        archivedActivityInstances = getProcessAPI().getArchivedActivityInstances(processInstance2.getId(), 0, 100,\n                criterionDsc);\n        for (int i = 0; i < nbOfStates; i++) {\n            if (criterionDsc.equals(ActivityInstanceCriterion.REACHED_STATE_DATE_DESC)\n                    || criterionDsc.equals(ActivityInstanceCriterion.LAST_UPDATE_DESC)) {\n                assertEquals(\"task1\", archivedActivityInstances.get(desc1 + i * nbActivities).getName());\n                assertEquals(\"task2\", archivedActivityInstances.get(desc2 + i * nbActivities).getName());\n                assertEquals(\"task3\", archivedActivityInstances.get(desc3 + i * nbActivities).getName());\n            }\n            if (criterionDsc.equals(ActivityInstanceCriterion.NAME_DESC)) {\n                assertEquals(\"task1\", archivedActivityInstances.get(desc1 * nbOfStates + i).getName());\n                assertEquals(\"task2\", archivedActivityInstances.get(desc2 * nbOfStates + i).getName());\n                assertEquals(\"task3\", archivedActivityInstances.get(desc3 * nbOfStates + i).getName());\n            }\n        }\n        waitForProcessToFinish(processInstance2);\n        disableAndDeleteProcess(processDefinition2);\n    }\n\n    @Test\n    public void getArchivedActivityInstancesOfAnUnknownProcess() {\n        final List<ArchivedActivityInstance> archivedActivityInstances = getProcessAPI().getArchivedActivityInstances(\n                456213846564L, 0, 100,\n                ActivityInstanceCriterion.REACHED_STATE_DATE_ASC);\n        assertEquals(0, archivedActivityInstances.size());\n    }\n\n    /**\n     * checks that\n     * {@link org.bonitasoft.engine.api.ProcessRuntimeAPI#getOpenActivityInstances(long, int, int, ActivityInstanceCriterion)}\n     * returns the good list\n     * of open activities\n     * only.\n     * An open activity is an activity with state NON-FINAL and STABLE.\n     */\n    @Test\n    public void checkActivityInstancesWithOpenState() throws Exception {\n        final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil\n                .buildProcessDefinitionWithActorAndThreeHumanStepsAndThreeTransition();\n\n        final BusinessArchive businessArchive1 = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(designProcessDefinition1).done();\n        final ProcessDefinition processDefinition1 = deployProcess(businessArchive1);\n\n        addUserToFirstActorOfProcess(1, processDefinition1);\n\n        getProcessAPI().enableProcess(processDefinition1.getId());\n        final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition1.getId());\n\n        // 1 instance of process def 2:\n        final ProcessDefinitionBuilder processBuilder2 = new ProcessDefinitionBuilder().createNewInstance(\n                \"checkActivityInstancesWithOpenState\",\n                PROCESS_VERSION);\n        processBuilder2.addActor(ACTOR_NAME);\n\n        final DesignProcessDefinition designProcessDefinition2 = processBuilder2.addAutomaticTask(\"step1\")\n                .addUserTask(\"step2\", ACTOR_NAME)\n                .addUserTask(\"step3\", ACTOR_NAME).addTransition(\"step1\", \"step2\").addTransition(\"step1\", \"step3\")\n                .getProcess();\n        final BusinessArchive businessArchive2 = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(designProcessDefinition2).done();\n        final ProcessDefinition processDefinition2 = deployProcess(businessArchive2);\n\n        addUserToFirstActorOfProcess(1, processDefinition2);\n\n        getProcessAPI().enableProcess(processDefinition2.getId());\n        final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinition2.getId());\n\n        // Check the size returned, and the state of each one:\n        waitForUserTask(processInstance1, \"step2\");\n        waitForUserTask(processInstance1, \"step3\");\n        waitForUserTask(processInstance1, \"step4\");\n        List<ActivityInstance> openedActivityInstances = getProcessAPI().getOpenActivityInstances(\n                processInstance1.getId(), 0, 3000,\n                ActivityInstanceCriterion.DEFAULT);\n        for (final ActivityInstance activityInstance : openedActivityInstances) {\n            // Check that all TestStates are Open (Ready):\n            assertEquals(activityInstance.getState(), TestStates.READY.getStateName());\n        }\n\n        waitForUserTask(processInstance2, \"step2\");\n        waitForUserTask(processInstance2, \"step3\");\n        openedActivityInstances = getProcessAPI().getOpenActivityInstances(processInstance1.getId(), 0, 200,\n                ActivityInstanceCriterion.DEFAULT);\n        for (final ActivityInstance activityInstance : openedActivityInstances) {\n            // Check that all TestStates are Open (Ready):\n            assertEquals(activityInstance.getState(), TestStates.READY.getStateName());\n        }\n\n        disableAndDeleteProcess(processDefinition1, processDefinition2);\n    }\n\n    @Test\n    public void openActivityInstancesOrder() throws Exception {\n        checkOpenActivityInstanceOrder(ActivityInstanceCriterion.NAME_ASC, ActivityInstanceCriterion.NAME_DESC);\n        // checkOpenActivityInstanceOrder(ActivityInstanceCriterion.PRIORITY_ASC, ActivityInstanceCriterion.PRIORITY_DESC);\n    }\n\n    @Test\n    public void openActivityInstancesOrderByLastUpdate() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithActorAndThreeHumanStepsAndThreeTransition();\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(designProcessDefinition).done();\n        final ProcessDefinition processDefinition = deployProcess(businessArchive);\n        addUserToFirstActorOfProcess(1, processDefinition);\n        getProcessAPI().enableProcess(processDefinition.getId());\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        // We check the size first, to be sure to wait long enough before retrieving the list:\n        waitForUserTask(processInstance, \"step2\");\n        waitForUserTask(processInstance, \"step3\");\n        waitForUserTask(processInstance, \"step4\");\n\n        List<ActivityInstance> openedActivityInstances;\n        openedActivityInstances = getProcessAPI().getOpenActivityInstances(processInstance.getId(), 0, 200,\n                ActivityInstanceCriterion.LAST_UPDATE_ASC);\n        assertTrue(openedActivityInstances.get(0).getLastUpdateDate()\n                .compareTo(openedActivityInstances.get(1).getLastUpdateDate()) <= 0);\n        assertTrue(openedActivityInstances.get(1).getLastUpdateDate()\n                .compareTo(openedActivityInstances.get(2).getLastUpdateDate()) <= 0);\n\n        openedActivityInstances = getProcessAPI().getOpenActivityInstances(processInstance.getId(), 0, 200,\n                ActivityInstanceCriterion.LAST_UPDATE_DESC);\n        assertTrue(openedActivityInstances.get(0).getLastUpdateDate()\n                .compareTo(openedActivityInstances.get(1).getLastUpdateDate()) >= 0);\n        assertTrue(openedActivityInstances.get(1).getLastUpdateDate()\n                .compareTo(openedActivityInstances.get(2).getLastUpdateDate()) >= 0);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    private void checkOpenActivityInstanceOrder(final ActivityInstanceCriterion ascendingCriterion,\n            final ActivityInstanceCriterion descendingCriterion)\n            throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithActorAndThreeHumanStepsAndThreeTransition();\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(designProcessDefinition).done();\n        final ProcessDefinition processDefinition = deployProcess(businessArchive);\n\n        addUserToFirstActorOfProcess(1, processDefinition);\n\n        getProcessAPI().enableProcess(processDefinition.getId());\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final String step2Name = \"step2\";\n        waitForUserTask(processInstance, step2Name);\n        final String step3Name = \"step3\";\n        waitForUserTask(processInstance, step3Name);\n        final String step4Name = \"step4\";\n        waitForUserTask(processInstance, step4Name);\n\n        List<ActivityInstance> openedActivityInstances;\n        openedActivityInstances = getProcessAPI().getOpenActivityInstances(processInstance.getId(), 0, 200,\n                ascendingCriterion);\n        assertEquals(step2Name, openedActivityInstances.get(0).getName());\n        assertEquals(step3Name, openedActivityInstances.get(1).getName());\n        assertEquals(step4Name, openedActivityInstances.get(2).getName());\n\n        openedActivityInstances = getProcessAPI().getOpenActivityInstances(processInstance.getId(), 0, 200,\n                descendingCriterion);\n        assertEquals(step2Name, openedActivityInstances.get(2).getName());\n        assertEquals(step3Name, openedActivityInstances.get(1).getName());\n        assertEquals(step4Name, openedActivityInstances.get(0).getName());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getNumberOfOpenedActivityInstances() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithActorAndThreeHumanStepsAndThreeTransition();\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(designProcessDefinition).done();\n        final ProcessDefinition processDefinition = deployProcess(businessArchive);\n\n        addUserToFirstActorOfProcess(1, processDefinition);\n\n        getProcessAPI().enableProcess(processDefinition.getId());\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance, \"step2\");\n        waitForUserTask(processInstance, \"step3\");\n        waitForUserTask(processInstance, \"step4\");\n\n        assertEquals(3, getProcessAPI().getNumberOfOpenedActivityInstances(processInstance.getId()));\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getProcessDefinitionIdFromProcessInstanceId() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\n                        Arrays.asList(\"step1\", \"step2\"),\n                        Arrays.asList(true, true));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId());\n\n        final long processDefinitionId = getProcessAPI().getProcessDefinitionIdFromProcessInstanceId(pi0.getId());\n        assertEquals(processDefinition.getId(), processDefinitionId);\n\n        // Clean up\n        waitForUserTask(pi0, \"step1\");\n        disableAndDeleteProcess(processDefinition);\n        assertEquals(0, getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.DEFAULT).size());\n    }\n\n    @Test\n    public void getProcessDefinitionIdFromActivityInstanceId() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId());\n\n        final List<ActivityInstance> activityInstances = getProcessAPI().getActivities(pi0.getId(), 0, 10);\n        for (final ActivityInstance activityInstance : activityInstances) {\n            final long processDefinitionId = getProcessAPI()\n                    .getProcessDefinitionIdFromActivityInstanceId(activityInstance.getId());\n            assertEquals(processDefinition.getId(), processDefinitionId);\n        }\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getProcessResources() throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION);\n        final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(\n                        processDefinitionBuilder.done());\n\n        // Add a resource to the biz archive:\n        businessArchiveBuilder.addExternalResource(new BarResource(\"dummy.txt\", \"DUMMY\".getBytes()));\n        businessArchiveBuilder\n                .addExternalResource(new BarResource(\"folder/document.txt\", \"SOME DOCUMENT TEXT\".getBytes()));\n        businessArchiveBuilder.addExternalResource(new BarResource(\"folder/image.jpg\", \"UNUSED\".getBytes()));\n        businessArchiveBuilder.addExternalResource(new BarResource(\"someFolder/bin.sh\", \"some script\".getBytes()));\n        businessArchiveBuilder.addExternalResource(new BarResource(\"ajar.jar\", \"a jar in resources\".getBytes()));\n        businessArchiveBuilder\n                .addClasspathResource(new BarResource(\"xsds/general-xsd-jar.jar\", \"a jar in a subfolder\".getBytes()));\n        businessArchiveBuilder\n                .addClasspathResource(new BarResource(\"acme-lib-001.jar\", \"a jar at the root\".getBytes()));\n\n        // deploy the process to unzip the .bar in BONITA_HOME:\n        final ProcessDefinition processDefinition = deployProcess(businessArchiveBuilder.done());\n        //check resources\n        assertThat(getProcessAPI().getProcessResources(processDefinition.getId(), \".*/.*\\\\.txt\"))\n                .hasSize(2)\n                .containsEntry(\"resources/dummy.txt\", \"DUMMY\".getBytes())\n                .containsEntry(\"resources/folder/document.txt\", \"SOME DOCUMENT TEXT\".getBytes());\n        //check jars\n        assertThat(getProcessAPI().getProcessResources(processDefinition.getId(), \"^classpath/.*\\\\.jar$\"))\n                .hasSize(2)\n                .containsEntry(\"classpath/xsds/general-xsd-jar.jar\", \"a jar in a subfolder\".getBytes())\n                .containsEntry(\"classpath/acme-lib-001.jar\", \"a jar at the root\".getBytes());\n        //check both\n        assertThat(getProcessAPI().getProcessResources(processDefinition.getId(), \"^.*/.*\\\\.jar$\"))\n                .hasSize(3)\n                .containsEntry(\"classpath/xsds/general-xsd-jar.jar\", \"a jar in a subfolder\".getBytes())\n                .containsEntry(\"classpath/acme-lib-001.jar\", \"a jar at the root\".getBytes())\n                .containsEntry(\"resources/ajar.jar\", \"a jar in resources\".getBytes());\n        deleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getLatestProcessDefinitionId() throws Exception {\n        // create two process definitions\n        final String processName = \"getLatestProcessDefinitionId\";\n        final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(processName, \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true));\n        final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(\n                new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(designProcessDefinition1)\n                        .done(),\n                ACTOR_NAME, user);\n\n        final DesignProcessDefinition designProcessDefinition2 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(processName, \"2.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true));\n        final ProcessDefinition processDefinition2 = deployAndEnableProcessWithActor(\n                new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(designProcessDefinition2)\n                        .done(),\n                ACTOR_NAME, user);\n\n        // test and do assert\n        final long latestProcessDefinitionId = getProcessAPI().getLatestProcessDefinitionId(processName);\n        assertEquals(processDefinition2.getId(), latestProcessDefinitionId);\n\n        disableAndDeleteProcess(processDefinition1, processDefinition2);\n    }\n\n    @Test(expected = ProcessDefinitionNotFoundException.class)\n    public void getLatestProcessDefinitionIdWithProcessInstanceNotFoundException() throws Exception {\n        final String PROCESS_NAME = String.valueOf(System.currentTimeMillis());\n        getProcessAPI().getLatestProcessDefinitionId(PROCESS_NAME);\n    }\n\n    @Test\n    public void getSupportedStates() {\n        Set<String> TestStatesName = getProcessAPI().getSupportedStates(FlowNodeType.USER_TASK);\n        assertEquals(5, TestStatesName.size());\n        TestStatesName = getProcessAPI().getSupportedStates(FlowNodeType.AUTOMATIC_TASK);\n        assertEquals(3, TestStatesName.size());\n        TestStatesName = getProcessAPI().getSupportedStates(FlowNodeType.START_EVENT);\n        assertEquals(2, TestStatesName.size());\n        TestStatesName = getProcessAPI().getSupportedStates(FlowNodeType.END_EVENT);\n        assertEquals(2, TestStatesName.size());\n        TestStatesName = getProcessAPI().getSupportedStates(FlowNodeType.GATEWAY);\n        assertEquals(2, TestStatesName.size());\n        TestStatesName = getProcessAPI().getSupportedStates(FlowNodeType.INTERMEDIATE_CATCH_EVENT);\n        assertEquals(4, TestStatesName.size());\n        TestStatesName = getProcessAPI().getSupportedStates(FlowNodeType.CALL_ACTIVITY);\n        assertEquals(5, TestStatesName.size());\n\n    }\n\n    @Test\n    public void getActivityInstanceState() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId());\n        final List<ActivityInstance> activityInstances = getProcessAPI().getActivities(pi0.getId(), 0, 10);\n        for (final ActivityInstance activityInstance : activityInstances) {\n            final String stateName = getProcessAPI().getActivityInstanceState(activityInstance.getId());\n            assertTrue(\"initializing\".equals(stateName) || \"ready\".equals(stateName));\n        }\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getActivityInstancePaginated() throws Exception {\n        final ProcessDefinitionBuilder definitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION);\n        definitionBuilder.addStartEvent(\"start\");\n        definitionBuilder.addActor(ACTOR_NAME);\n        definitionBuilder.addUserTask(\"initTask1\", ACTOR_NAME);\n        definitionBuilder.addUserTask(\"initTask2\", ACTOR_NAME);\n        definitionBuilder.addUserTask(\"initTask3\", ACTOR_NAME);\n        definitionBuilder.addEndEvent(\"end\");\n        definitionBuilder.addTransition(\"start\", \"initTask1\");\n        definitionBuilder.addTransition(\"start\", \"initTask2\");\n        definitionBuilder.addTransition(\"start\", \"initTask3\");\n        final DesignProcessDefinition designProcessDefinition = definitionBuilder.done();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance, \"initTask1\");\n        waitForUserTask(processInstance, \"initTask2\");\n        waitForUserTask(processInstance, \"initTask3\");\n\n        List<ActivityInstance> activityInstances = getProcessAPI().getActivities(processInstance.getId(), 0, 2);\n        assertEquals(2, activityInstances.size());\n        activityInstances = getProcessAPI().getActivities(processInstance.getId(), 2, 2);\n        assertEquals(1, activityInstances.size());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getProcessDefinitionIdByNameAndVersion() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true));\n        final ProcessDefinition processDefinition = deployProcess(\n                new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(designProcessDefinition)\n                        .done());\n        // do test and assert\n        final long processDefId = getProcessAPI().getProcessDefinitionId(APITestUtil.PROCESS_NAME,\n                APITestUtil.PROCESS_VERSION);\n        assertEquals(processDefId, processDefinition.getId());\n\n        deleteProcess(processDefinition);\n    }\n\n    @Test(expected = ProcessDefinitionNotFoundException.class)\n    public void getProcessDefinitionIdByNameAndVersionWithExcepton() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true));\n        final ProcessDefinition processDefinition = deployProcess(\n                new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(designProcessDefinition)\n                        .done());\n        // do test and assert\n        try {\n            getProcessAPI().getProcessDefinitionId(APITestUtil.PROCESS_NAME + \"_wrongName\",\n                    APITestUtil.PROCESS_VERSION);\n        } finally {\n            deleteProcess(processDefinition);\n        }\n    }\n\n    @Test\n    public void severalActivityUpdates() throws Exception {\n        final UserTaskDefinitionBuilder userTask = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addDescription(\"Delivery all day and night long\").addUserTask(\"step1\", ACTOR_NAME);\n        final String dataName1 = \"dataName\";\n        final String dataName2 = \"myHero\";\n        userTask.addShortTextData(dataName1, new ExpressionBuilder().createConstantStringExpression(\"beforeUpdate\"));\n        userTask.addShortTextData(dataName2, new ExpressionBuilder().createConstantStringExpression(\"Actarus\"));\n        final DesignProcessDefinition processDef = userTask.getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, ACTOR_NAME, user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        final StartProcessUntilStep startProcessAndWaitForTask = startProcessAndWaitForTask(\n                processDeploymentInfo.getProcessId(), \"step1\");\n\n        final long activityInstanceId = startProcessAndWaitForTask.getActivityInstance().getId();\n\n        DataInstance dataInstance = getProcessAPI().getActivityDataInstance(dataName1, activityInstanceId);\n        final String newConstantValue1 = \"afterUpdate\";\n        final Operation stringOperation = BuildTestUtil.buildStringOperation(dataInstance.getName(), newConstantValue1,\n                false);\n\n        final String newConstantValue2 = \"GOLDORAK\";\n        dataInstance = getProcessAPI().getActivityDataInstance(dataName2, activityInstanceId);\n        final Operation stringOperation2 = BuildTestUtil.buildStringOperation(dataInstance.getName(), newConstantValue2,\n                false);\n        final List<Operation> operations = new ArrayList<>();\n        operations.add(stringOperation);\n        operations.add(stringOperation2);\n        getProcessAPI().updateActivityInstanceVariables(operations, activityInstanceId, null);\n\n        DataInstance dataI = getProcessAPI().getActivityDataInstance(dataName1, activityInstanceId);\n        assertEquals(newConstantValue1, dataI.getValue());\n\n        dataI = getProcessAPI().getActivityDataInstance(dataName2, activityInstanceId);\n        assertEquals(newConstantValue2, dataI.getValue());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void updateActivityInstanceVariables() throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION);\n        final UserTaskDefinitionBuilder addUserTask = processDefinitionBuilder\n                .addActor(ACTOR_NAME)\n                .addDescription(\"Delivery all day and night long\")\n                .addUserTask(\"step1\", ACTOR_NAME);\n        processDefinitionBuilder.addShortTextData(\"a\",\n                new ExpressionBuilder().createConstantStringExpression(\"aprocess\"));\n        processDefinitionBuilder.addShortTextData(\"b\",\n                new ExpressionBuilder().createConstantStringExpression(\"bprocess\"));\n        processDefinitionBuilder.addShortTextData(\"c\",\n                new ExpressionBuilder().createConstantStringExpression(\"cprocess\"));\n        processDefinitionBuilder.addShortTextData(\"d\",\n                new ExpressionBuilder().createConstantStringExpression(\"dprocess\"));\n        processDefinitionBuilder.addShortTextData(\"e\",\n                new ExpressionBuilder().createConstantStringExpression(\"eprocess\"));\n        addUserTask.addShortTextData(\"a\", new ExpressionBuilder().createConstantStringExpression(\"aacti\"));\n        addUserTask.addShortTextData(\"b\", new ExpressionBuilder().createConstantStringExpression(\"bacti\"))\n                .isTransient();\n        addUserTask.addShortTextData(\"f\", new ExpressionBuilder().createConstantStringExpression(\"facti\"));\n        addUserTask.addShortTextData(\"g\", new ExpressionBuilder().createConstantStringExpression(\"gacti\"))\n                .isTransient();\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(\n                processDefinitionBuilder.getProcess(), ACTOR_NAME, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n\n        List<DataInstance> dataInstances = getProcessAPI().getActivityDataInstances(step1Id, 0, 10);\n        assertThat(dataInstances).hasSize(6);\n        final ArrayList<String> names = new ArrayList<>(6);\n        ArrayList<String> values = new ArrayList<>(6);\n        for (final DataInstance dataInstance2 : dataInstances) {\n            names.add(dataInstance2.getName());\n            values.add((String) dataInstance2.getValue());\n        }\n        assertThat(names).contains(\"a\", \"b\", \"c\", \"d\", \"e\", \"f\");\n        assertThat(values).contains(\"aacti\", \"bprocess\", \"cprocess\", \"dprocess\", \"eprocess\", \"facti\");\n        final List<Operation> operations = new ArrayList<>();\n        for (final DataInstance dataInstance2 : dataInstances) {\n            final Operation stringOperation = BuildTestUtil.buildStringOperation(dataInstance2.getName(),\n                    dataInstance2.getValue() + \"+up\", false);\n            operations.add(stringOperation);\n        }\n        getProcessAPI().updateActivityInstanceVariables(operations, step1Id, null);\n\n        dataInstances = getProcessAPI().getActivityDataInstances(step1Id, 0, 10);\n        assertThat(dataInstances).hasSize(6);\n        values = new ArrayList<>(6);\n        for (final DataInstance dataInstance2 : dataInstances) {\n            values.add((String) dataInstance2.getValue());\n        }\n        assertThat(values).contains(\"aacti+up\", \"bprocess+up\", \"cprocess+up\", \"dprocess+up\", \"eprocess+up\", \"facti+up\");\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void updateActivityInstanceVariable() throws Exception {\n        final DesignProcessDefinition processDef = createProcessWithActorAndHumanTaskAndStringData();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, ACTOR_NAME, user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n        waitForUserTask(processInstance, \"step1\");\n\n        final List<ActivityInstance> activityInstances = getProcessAPI().getActivities(processInstance.getId(), 0, 10);\n        final long activityInstanceId = activityInstances.get(0).getId();\n        final String updatedValue = \"afterUpdate\";\n\n        final Map<String, Serializable> variables = new HashMap<>(2);\n        variables.put(\"dataName\", updatedValue);\n        getProcessAPI().updateActivityInstanceVariables(activityInstanceId, variables);\n\n        final DataInstance dataInstance = getProcessAPI().getActivityDataInstance(\"dataName\", activityInstanceId);\n        assertEquals(updatedValue, dataInstance.getValue());\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test(expected = UpdateException.class)\n    public void cannotUpdateAnActivityInstanceVariable() throws Exception {\n        final DesignProcessDefinition processDef = createProcessWithActorAndHumanTaskAndStringData();\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, ACTOR_NAME, user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n        waitForUserTask(processInstance, \"step1\");\n\n        final List<ActivityInstance> activityInstances = getProcessAPI().getActivities(processInstance.getId(), 0, 10);\n        final long activityInstanceId = activityInstances.get(0).getId();\n        final String updatedValue = \"afterUpdate\";\n\n        final Map<String, Serializable> variables = new HashMap<>(2);\n        variables.put(\"dataName1\", updatedValue);\n        try {\n            getProcessAPI().updateActivityInstanceVariables(activityInstanceId, variables);\n        } finally {\n            disableAndDeleteProcess(processDefinition);\n        }\n    }\n\n    @Test\n    public void canExecuteTask() throws Exception {\n        final long userId = user.getId();\n\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList(\"step1\"),\n                        Arrays.asList(true));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long activityInstanceId = waitForUserTask(processInstance, \"step1\");\n        assertFalse(\"The user \" + USERNAME + \" shouldn't be able to execute the task step1.\",\n                getProcessAPI().canExecuteTask(activityInstanceId, userId));\n\n        getProcessAPI().assignUserTask(activityInstanceId, userId);\n        Thread.sleep(100);\n        assertTrue(\"The user \" + USERNAME + \" should be able to execute the task step1.\",\n                getProcessAPI().canExecuteTask(activityInstanceId, userId));\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getOneAssignedUserTaskInstanceOfProcessInstance() throws Exception {\n        final DesignProcessDefinition processDef = createProcessWithActorAndHumanTaskAndStringData();\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, ACTOR_NAME, user);\n        final ActivityInstance activityInstance = createProcessAndAssignUserTask(user, processDefinition);\n\n        final long userTaskId = getProcessAPI()\n                .getOneAssignedUserTaskInstanceOfProcessInstance(activityInstance.getParentContainerId(), user.getId());\n        assertEquals(activityInstance.getId(), userTaskId);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getOneAssignedUserTaskInstanceOfProcessDefinition() throws Exception {\n        final DesignProcessDefinition processDef = createProcessWithActorAndHumanTaskAndStringData();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, ACTOR_NAME, user);\n        final ActivityInstance activityInstance = createProcessAndAssignUserTask(user, processDefinition);\n\n        final long userTaskId = getProcessAPI()\n                .getOneAssignedUserTaskInstanceOfProcessDefinition(processDefinition.getId(), user.getId());\n        assertEquals(activityInstance.getId(), userTaskId);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    private ActivityInstance createProcessAndAssignUserTask(final User user, final ProcessDefinition processDefinition)\n            throws Exception {\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n        waitForUserTask(processInstance, \"step1\");\n\n        final List<ActivityInstance> activityInstances = new ArrayList<>(\n                getProcessAPI().getActivities(processInstance.getId(), 0, 20));\n        final ActivityInstance activityInstance = activityInstances.get(activityInstances.size() - 1);\n\n        assertEquals(\"ready\", activityInstance.getState());\n\n        getProcessAPI().assignUserTask(activityInstance.getId(), user.getId());\n        return activityInstance;\n    }\n\n    @Test\n    public void updateProcessDeploymentInfo() throws Exception {\n        // create process definition;\n        final String processDescription = \"myProcessDisplayName\";\n        final String processDisplayName = \"myProcessDescription\";\n        final String processDisplayDescription = \"myProcessDisplayDescription\";\n\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION)\n                .addDisplayName(processDisplayName).addDisplayDescription(processDisplayDescription)\n                .addDescription(processDescription)\n                .addUserTask(\"task1\", \"actor\").getProcess();\n        final ProcessDefinition processDefinition = deployProcess(\n                new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(designProcessDefinition)\n                        .done());\n        final long processId = processDefinition.getId();\n        // before update, display name should be the same as process name\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI().getProcessDeploymentInfo(processId);\n        assertEquals(PROCESS_NAME, processDeploymentInfo.getName());\n        assertEquals(processDisplayName, processDeploymentInfo.getDisplayName());\n        assertEquals(processDisplayDescription, processDeploymentInfo.getDisplayDescription());\n        assertEquals(processDescription, processDeploymentInfo.getDescription());\n        assertEquals(null, processDeploymentInfo.getIconPath());\n\n        // update and do assert\n        final ProcessDeploymentInfoUpdater processDeploymentInfoUpdateDescriptor = new ProcessDeploymentInfoUpdater();\n        final String updatedDisplayName = \"updatedDisplayName\";\n        final String updatedDisplayDescription = \"updatedDisplayDescription\";\n        final String iconPath = \"iconPathOne\";\n        processDeploymentInfoUpdateDescriptor.setDisplayName(updatedDisplayName);\n        processDeploymentInfoUpdateDescriptor.setDisplayDescription(updatedDisplayDescription);\n        processDeploymentInfoUpdateDescriptor.setIconPath(iconPath);\n        getProcessAPI().updateProcessDeploymentInfo(processId, processDeploymentInfoUpdateDescriptor);\n\n        final ProcessDeploymentInfo updatedProcessDeploymentInfo = getProcessAPI().getProcessDeploymentInfo(processId);\n        assertEquals(updatedDisplayName, updatedProcessDeploymentInfo.getDisplayName());\n        assertEquals(updatedDisplayDescription, updatedProcessDeploymentInfo.getDisplayDescription());\n        assertEquals(iconPath, updatedProcessDeploymentInfo.getIconPath());\n\n        deleteProcess(processDefinition);\n    }\n\n    @Test\n    public void updateProcessDisplayDescriptionToNull() throws Exception {\n        // create process definition;\n        final String processDescription = \"myProcessDisplayName\";\n        final String processDisplayName = \"myProcessDescription\";\n        final String processDisplayDescription = \"myProcessDisplayDescription\";\n\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION)\n                .addDisplayName(processDisplayName).addDisplayDescription(processDisplayDescription)\n                .addDescription(processDescription)\n                .addUserTask(\"task1\", \"actor\").getProcess();\n        final ProcessDefinition processDefinition = deployProcess(\n                new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(designProcessDefinition)\n                        .done());\n        final long processId = processDefinition.getId();\n        // before update, display name should be the same as process name\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI().getProcessDeploymentInfo(processId);\n        assertEquals(PROCESS_NAME, processDeploymentInfo.getName());\n        assertEquals(processDisplayName, processDeploymentInfo.getDisplayName());\n        assertEquals(processDisplayDescription, processDeploymentInfo.getDisplayDescription());\n        assertEquals(processDescription, processDeploymentInfo.getDescription());\n\n        // update and do assert\n        final ProcessDeploymentInfoUpdater processDeploymentInfoUpdateDescriptor = new ProcessDeploymentInfoUpdater();\n        final String updatedDisplayDescription = null;\n        processDeploymentInfoUpdateDescriptor.setDisplayDescription(updatedDisplayDescription);\n        getProcessAPI().updateProcessDeploymentInfo(processId, processDeploymentInfoUpdateDescriptor);\n\n        final ProcessDeploymentInfo updatedProcessDeploymentInfo = getProcessAPI().getProcessDeploymentInfo(processId);\n        assertEquals(updatedDisplayDescription, updatedProcessDeploymentInfo.getDisplayDescription());\n\n        deleteProcess(processDefinition);\n    }\n\n    @Test(expected = ProcessDefinitionNotFoundException.class)\n    public void updateProcessDeploymentInfoWithProcessDefinitionNotFoundException() throws Exception {\n        // create process definition;\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true));\n        final ProcessDefinition processDefinition = deployProcess(\n                new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(designProcessDefinition)\n                        .done());\n        final long processId = processDefinition.getId();\n        // update with wrong processId\n        final ProcessDeploymentInfoUpdater processDeploymentInfoUpdateDescriptor = new ProcessDeploymentInfoUpdater();\n        final String updatedDisplayName = \"updatedDisplayName\";\n        processDeploymentInfoUpdateDescriptor.setDisplayName(updatedDisplayName);\n        try {\n            getProcessAPI().updateProcessDeploymentInfo(processId + 1, processDeploymentInfoUpdateDescriptor);\n        } finally {\n            deleteProcess(processDefinition);\n        }\n    }\n\n    @Test(expected = UpdateException.class)\n    public void updateProcessDeploymentInfoWithException() throws Exception {\n        // create process definition;\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true));\n        final ProcessDefinition processDefinition = deployProcess(\n                new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(designProcessDefinition)\n                        .done());\n        final long processId = processDefinition.getId();\n        // update and do assert\n        final ProcessDeploymentInfoUpdater processDeploymentInfoUpdateDescriptor = new ProcessDeploymentInfoUpdater();\n        try {\n            getProcessAPI().updateProcessDeploymentInfo(processId, processDeploymentInfoUpdateDescriptor);\n        } finally {\n            deleteProcess(processDefinition);\n        }\n    }\n\n    @Test\n    public void checkDataNameInProcess() throws Exception {\n        // create process definition with integer data;\n        final String dataName1 = \"$nAéç_mèE\";\n        final String dataName2 = \"refhbh bgrtg\";\n        final String dataName3 = \"refhbh-bgrtg\";\n        final String dataName4 = \"refhbh+bgrtg\";\n        final String dataName5 = \"refhbh?bgrtg\";\n        try {\n            new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION)\n                    .addIntegerData(dataName1, new ExpressionBuilder().createConstantIntegerExpression(1))\n                    .addIntegerData(dataName2, new ExpressionBuilder().createConstantIntegerExpression(2))\n                    .addIntegerData(dataName3, new ExpressionBuilder().createConstantIntegerExpression(3))\n                    .addIntegerData(dataName4, new ExpressionBuilder().createConstantIntegerExpression(4))\n                    .addIntegerData(dataName5, new ExpressionBuilder().createConstantIntegerExpression(5)).getProcess();\n\n            fail(\"This test should not reach this statement\");\n        } catch (final InvalidProcessDefinitionException ipde) {\n            // System.out.println(ipde.getMessage());\n            assertTrue(!ipde.getMessage().contains(dataName1));\n            assertTrue(ipde.getMessage().contains(dataName2));\n            assertTrue(ipde.getMessage().contains(dataName3));\n            assertTrue(ipde.getMessage().contains(dataName4));\n            assertTrue(ipde.getMessage().contains(dataName5));\n        }\n    }\n\n    @Test\n    public void checkProcessInstanceDataValue() throws Exception {\n        // create process definition with integer data;\n        final String dataName = \"var1\";\n        final DesignProcessDefinition processDef = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addIntegerData(dataName, new ExpressionBuilder().createConstantIntegerExpression(1))\n                .addUserTask(\"step1\", ACTOR_NAME)\n                .addAutomaticTask(\"step2\").addTransition(\"step1\", \"step2\").getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, ACTOR_NAME, user);\n\n        // create Operation keyed map\n        final Operation integerOperation = BuildTestUtil.buildIntegerOperation(dataName, 2);\n        final Map<String, Serializable> context = new HashMap<>();\n        context.put(\"page\", \"1\");\n        final long processDefinitionId = processDefinition.getId();\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinitionId,\n                Arrays.asList(integerOperation), context);\n        final DataInstance dataInstance = getProcessAPI().getProcessDataInstance(dataName, processInstance.getId());\n        assertEquals(2, dataInstance.getValue());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getActivityReachedStateDate() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\n                        Arrays.asList(\"step one\", \"step two\"), Arrays.asList(true, true));\n        final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition,\n                ACTOR_NAME, user);\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition1.getId());\n\n        HumanTaskInstance humanTaskInstance = waitForUserTaskAndGetIt(startProcess, \"step one\");\n        final Date reachedDate = humanTaskInstance.getReachedStateDate();\n        assertNotNull(reachedDate);\n        humanTaskInstance = getProcessAPI().getHumanTaskInstance(humanTaskInstance.getId());\n        final long readyDate = humanTaskInstance.getReachedStateDate().getTime();\n        final long processStartDate = startProcess.getStartDate().getTime();\n        assertTrue(\n                \"The process start at \" + processStartDate + \", and the user task \" + humanTaskInstance.getName()\n                        + \" reached state at \" + readyDate,\n                processStartDate <= readyDate);\n        getProcessAPI().assignUserTask(humanTaskInstance.getId(), user.getId());\n        getProcessAPI().executeFlowNode(humanTaskInstance.getId());\n        long userTask = waitForUserTask(\"step two\");\n        assignAndExecuteStep(userTask, user);\n        waitForProcessToFinish(startProcess);\n        // look in archive\n        assertEquals(reachedDate, getProcessAPI().getActivityReachedStateDate(humanTaskInstance.getId(),\n                TestStates.READY.getStateName()));\n        disableAndDeleteProcess(processDefinition1);\n    }\n\n    @Test\n    public void checkOrderPriorityEnum() {\n        assertEquals(0, TaskPriority.LOWEST.ordinal());\n        assertEquals(1, TaskPriority.UNDER_NORMAL.ordinal());\n        assertEquals(2, TaskPriority.NORMAL.ordinal());\n        assertEquals(3, TaskPriority.ABOVE_NORMAL.ordinal());\n        assertEquals(4, TaskPriority.HIGHEST.ordinal());\n    }\n\n    @Test\n    public void activityWithDisplayNameAndDescription() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        final String displayName = \"display name\";\n        final String displayDescriptionAfterCompletion = \"display description after completion\";\n        final String displayDescription = \"display description\";\n        final Expression dispDescAfterCompletionExpression = new ExpressionBuilder().createGroovyScriptExpression(\n                \"dynGroovyScriptWithLongDep\", \"return '\"\n                        + displayDescriptionAfterCompletion + \"' + rootProcessInstanceId\",\n                String.class.getName(),\n                new ExpressionBuilder().createEngineConstant(ExpressionConstants.ROOT_PROCESS_INSTANCE_ID));\n        final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask(\"step1\", ACTOR_NAME)\n                .addDisplayName(new ExpressionBuilder().createConstantStringExpression(displayName))\n                .addDisplayDescriptionAfterCompletion(dispDescAfterCompletionExpression)\n                .addDisplayDescription(new ExpressionBuilder().createConstantStringExpression(displayDescription))\n                .getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId());\n\n        final HumanTaskInstance userTaskInstance = waitForUserTaskAndGetIt(pi0, \"step1\");\n        assertEquals(displayName, userTaskInstance.getDisplayName());\n        assertEquals(displayDescription, userTaskInstance.getDisplayDescription());\n        assignAndExecuteStep(userTaskInstance, user.getId());\n        waitForCompletedArchivedStep(\"step1\", processDefinition.getId(), displayName,\n                displayDescriptionAfterCompletion + pi0.getId());\n\n        // Clean up\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void activityWithDisplayNameAndDescriptionAndNoAfterCompletion() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        final String displayName = \"display name\";\n        final String displayDescription = \"display description\";\n        final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask(\"step1\", ACTOR_NAME)\n                .addDisplayName(new ExpressionBuilder().createConstantStringExpression(displayName))\n                .addDisplayDescription(new ExpressionBuilder().createConstantStringExpression(displayDescription))\n                .getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(pi0, \"step1\");\n\n        final HumanTaskInstance userTaskInstance = getProcessAPI().getHumanTaskInstance(step1Id);\n        assertEquals(displayName, userTaskInstance.getDisplayName());\n        assertEquals(displayDescription, userTaskInstance.getDisplayDescription());\n        assignAndExecuteStep(userTaskInstance, user.getId());\n        waitForCompletedArchivedStep(\"step1\", processDefinition.getId(), displayName, displayDescription);\n\n        // Clean up\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void activityWithNoDisplayNameAndDescription() throws Exception {\n        final String stepName = \"staticName\";\n        final String stepDescription = \"staticDescription\";\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask(stepName, ACTOR_NAME)\n                .addDescription(stepDescription).getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId());\n        final HumanTaskInstance userTaskInstance = waitForUserTaskAndGetIt(pi0, stepName);\n        assertEquals(stepName, userTaskInstance.getDisplayName());\n        assertEquals(stepDescription, userTaskInstance.getDisplayDescription());\n        assertEquals(stepDescription, userTaskInstance.getDescription());\n        assignAndExecuteStep(userTaskInstance, user.getId());\n        waitForCompletedArchivedStep(stepName, processDefinition.getId(), stepName, stepDescription);\n\n        // Clean up\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getNumberOfOpenTasksForUsers() throws Exception {\n        final String username1 = \"jack\";\n        final String username2 = \"john\";\n        final String username3 = \"lucy\";\n\n        final User jack = createUser(username1, PASSWORD);\n        final User john = createUser(username2, PASSWORD);\n        final User lucy = createUser(username3, PASSWORD);\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME).addDescription(\"Coding all scrum-sprint-long\")\n                .addUserTask(\"userTask1\", ACTOR_NAME)\n                .addUserTask(\"userTask2\", ACTOR_NAME)\n                .addUserTask(\"userTask3\", ACTOR_NAME);\n        final DesignProcessDefinition processDefinition = processBuilder.done();\n\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinition, ACTOR_NAME, jack);\n\n        final ProcessInstance startedProcess = getProcessAPI().startProcess(definition.getId());\n        final long step1Id = waitForUserTask(startedProcess, \"userTask1\");\n        final long step2Id = waitForUserTask(startedProcess, \"userTask2\");\n        waitForUserTask(startedProcess, \"userTask3\");\n\n        // add lucy to actor\n        getProcessAPI().addUserToActor(ACTOR_NAME, definition, lucy.getId());\n\n        // assign first user task to jack, second one to john, leaving the third pending\n        getProcessAPI().assignUserTask(step1Id, jack.getId());\n        getProcessAPI().assignUserTask(step2Id, john.getId());\n\n        // check\n        final List<Long> userIds = new ArrayList<>();\n        userIds.add(jack.getId());\n        userIds.add(john.getId());\n        userIds.add(lucy.getId());\n        final Map<Long, Long> myAssignedTasksNb = getProcessAPI().getNumberOfOpenTasks(userIds);\n        assertNotNull(myAssignedTasksNb);\n        assertEquals(3, myAssignedTasksNb.size());\n        assertEquals(2L, (long) myAssignedTasksNb.get(jack.getId())); // jack has one assigned task and one pending task\n        assertEquals(1L, (long) myAssignedTasksNb.get(john.getId())); // john has one assigned task\n        assertEquals(1L, (long) myAssignedTasksNb.get(lucy.getId())); // lucy has one pending task\n\n        disableAndDeleteProcess(definition);\n        deleteUsers(jack, john, lucy);\n    }\n\n    @Test\n    public void getNumberOfOverdueTasksForUsers() throws Exception {\n        final String username1 = \"jack\";\n        final String username2 = \"john\";\n        final String username3 = \"lucy\";\n\n        final User jack = createUser(username1, PASSWORD);\n        final User john = createUser(username2, PASSWORD);\n        final User lucy = createUser(username3, PASSWORD);\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME).addDescription(\"Coding all scrum-sprint-long\")\n                .addUserTask(\"userTask1\", ACTOR_NAME).addExpectedDuration(0L)\n                .addUserTask(\"userTask2\", ACTOR_NAME).addExpectedDuration(0L)\n                .addUserTask(\"userTask3\", ACTOR_NAME).addExpectedDuration(0L);\n        final DesignProcessDefinition processDefinition = processBuilder.done();\n\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinition, ACTOR_NAME, jack);\n\n        final ProcessInstance startedProcess = getProcessAPI().startProcess(definition.getId());\n        final long step1Id = waitForUserTask(startedProcess, \"userTask1\");\n        final long step2Id = waitForUserTask(startedProcess, \"userTask2\");\n        waitForUserTask(startedProcess, \"userTask3\");\n\n        // add lucy to actor\n        getProcessAPI().addUserToActor(ACTOR_NAME, definition, lucy.getId());\n\n        // assign first user task to jack, second one to john, leaving the third pending\n        getProcessAPI().assignUserTask(step1Id, jack.getId());\n        getProcessAPI().assignUserTask(step2Id, john.getId());\n\n        // check\n        final List<Long> userIds = new ArrayList<>();\n        userIds.add(jack.getId());\n        userIds.add(john.getId());\n        userIds.add(lucy.getId());\n        final Map<Long, Long> myAssignedTasksNb = getProcessAPI().getNumberOfOverdueOpenTasks(userIds);\n        assertNotNull(myAssignedTasksNb);\n        assertEquals(3, myAssignedTasksNb.size());\n        assertEquals(2L, (long) myAssignedTasksNb.get(jack.getId())); // jack has one assigned overdue task and one pending overdue task\n        assertEquals(1L, (long) myAssignedTasksNb.get(john.getId())); // john has one assigned overdue task\n        assertEquals(1L, (long) myAssignedTasksNb.get(lucy.getId())); // lucy has one pending overdue task\n\n        disableAndDeleteProcess(definition);\n        deleteUsers(jack, john, lucy);\n    }\n\n    @Test\n    public void getProcessDefinitionsDeployInfo() throws Exception {\n        // create process1\n        final DesignProcessDefinition designProcessDefinition1 = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, \"1.1\")\n                .addDescription(\"My process 1\").addDisplayName(\"Process 1\")\n                .addDisplayDescription(\"The process definition that is cool\").done();\n        final ProcessDefinition processDefinition1 = deployAndEnableProcess(designProcessDefinition1);\n\n        assertNotNull(processDefinition1);\n        assertEquals(PROCESS_NAME, processDefinition1.getName());\n        assertEquals(\"1.1\", processDefinition1.getVersion());\n        assertEquals(\"My process 1\", processDefinition1.getDescription());\n        // put all processDefinitionId to a list as parameter\n        final long processDefinitionIdA = processDefinition1.getId();\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinitionIdA);\n        assertNotNull(processDeploymentInfo);\n        assertEquals(PROCESS_NAME, processDeploymentInfo.getName());\n        assertEquals(\"1.1\", processDeploymentInfo.getVersion());\n        assertEquals(\"My process 1\", processDeploymentInfo.getDescription());\n        assertEquals(\"The process definition that is cool\", processDeploymentInfo.getDisplayDescription());\n        assertEquals(\"Process 1\", processDeploymentInfo.getDisplayName());\n        disableAndDeleteProcess(processDefinition1);\n    }\n\n    @Test\n    public void getProcessDefinitionsFromIds() throws Exception {\n        // create process1\n        final String PROCESS_NAME1 = \"processDefinition1\";\n        final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(PROCESS_NAME1, \"1.1\",\n                        Arrays.asList(\"step1_1\", \"step1_2\"), Arrays.asList(true, true));\n        final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition1,\n                ACTOR_NAME, user);\n\n        // create process2\n        final String PROCESS_NAME2 = \"processDefinition2\";\n        final DesignProcessDefinition designProcessDefinition2 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(PROCESS_NAME2, \"1.2\",\n                        Arrays.asList(\"step2_1\", \"step2_2\"), Arrays.asList(true, true));\n        final ProcessDefinition processDefinition2 = deployAndEnableProcessWithActor(designProcessDefinition2,\n                ACTOR_NAME, user);\n\n        // create process3\n        final String PROCESS_NAME3 = \"processDefinition3\";\n        final DesignProcessDefinition designProcessDefinition3 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(PROCESS_NAME3, \"1.3\",\n                        Arrays.asList(\"step2_1\", \"step2_2\"), Arrays.asList(true, true));\n        final ProcessDefinition processDefinition3 = deployAndEnableProcessWithActor(designProcessDefinition3,\n                ACTOR_NAME, user);\n\n        // create process4\n        final String PROCESS_NAME4 = \"processDefinition4\";\n        final DesignProcessDefinition designProcessDefinition4 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(PROCESS_NAME4, \"1.4\",\n                        Arrays.asList(\"step2_1\", \"step2_2\"), Arrays.asList(true, true));\n        final ProcessDefinition processDefinition4 = deployAndEnableProcessWithActor(designProcessDefinition4,\n                ACTOR_NAME, user);\n\n        // put all processDefinitionId to a list as parameter\n        final long processDefinitionIdA = processDefinition1.getId();\n        final long processDefinitionIdB = processDefinition2.getId();\n        final long processDefinitionIdC = processDefinition3.getId();\n        final long processDefinitionIdD = processDefinition4.getId();\n        final List<Long> processDefinitionIds = new ArrayList<>();\n        processDefinitionIds.add(processDefinitionIdA);\n        processDefinitionIds.add(processDefinitionIdB);\n        processDefinitionIds.add(processDefinitionIdC);\n        processDefinitionIds.add(processDefinitionIdD);\n\n        // do search and assert\n        final Map<Long, ProcessDeploymentInfo> processDeploymentInfos = getProcessAPI()\n                .getProcessDeploymentInfosFromIds(processDefinitionIds);\n        assertNotNull(processDeploymentInfos);\n        assertEquals(4, processDeploymentInfos.size());\n        assertEquals(processDefinition1.getId(), processDeploymentInfos.get(processDefinitionIdA).getProcessId());\n        assertEquals(processDefinition2.getId(), processDeploymentInfos.get(processDefinitionIdB).getProcessId());\n        assertEquals(processDefinition3.getId(), processDeploymentInfos.get(processDefinitionIdC).getProcessId());\n        assertEquals(processDefinition4.getId(), processDeploymentInfos.get(processDefinitionIdD).getProcessId());\n\n        // delete data for test\n        disableAndDeleteProcess(processDefinition1);\n        disableAndDeleteProcess(processDefinition2);\n        disableAndDeleteProcess(processDefinition3);\n        disableAndDeleteProcess(processDefinition4);\n    }\n\n    @Test\n    public void getProcessDefinitionsFromProcessInstanceIds() throws Exception {\n        // create process1\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addDescription(\"description\");\n        processBuilder.addDisplayDescription(\"displayDescription\");\n        processBuilder.addActor(ACTOR_NAME);\n        final DesignProcessDefinition designProcessDefinition1 = processBuilder.addUserTask(\"step1\", ACTOR_NAME)\n                .getProcess();\n\n        final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition1,\n                ACTOR_NAME, user);\n        final ProcessInstance pi1 = getProcessAPI().startProcess(processDefinition1.getId());\n\n        // create process2\n        final String processName2 = \"processDefinition2\";\n        final DesignProcessDefinition designProcessDefinition2 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(processName2, \"1.2\",\n                        Arrays.asList(\"step2_1\", \"step2_2\"), Arrays.asList(true, true));\n        final ProcessDefinition processDefinition2 = deployAndEnableProcessWithActor(designProcessDefinition2,\n                ACTOR_NAME, user);\n        final ProcessInstance pi2 = getProcessAPI().startProcess(processDefinition2.getId());\n\n        // create process3\n        final String processName3 = \"processDefinition3\";\n        final DesignProcessDefinition designProcessDefinition3 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(processName3, \"1.3\",\n                        Arrays.asList(\"step3_1\", \"step3_2\"), Arrays.asList(true, true));\n        final ProcessDefinition processDefinition3 = deployAndEnableProcessWithActor(designProcessDefinition3,\n                ACTOR_NAME, user);\n        final ProcessInstance pi3 = getProcessAPI().startProcess(processDefinition3.getId());\n\n        // create process4\n        final String processName4 = \"processDefinition4\";\n        final DesignProcessDefinition designProcessDefinition4 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(processName4, \"1.4\",\n                        Arrays.asList(\"step4_1\", \"step4_2\"), Arrays.asList(true, true));\n        final ProcessDefinition processDefinition4 = deployAndEnableProcessWithActor(designProcessDefinition4,\n                ACTOR_NAME, user);\n        final ProcessInstance pi4 = getProcessAPI().startProcess(processDefinition4.getId());\n\n        // put all processInstantsId to a list as parameter\n        final long processInstantsIdA = pi1.getId();\n        final long processInstantsIdB = pi2.getId();\n        final long processInstantsIdC = pi3.getId();\n        final long processInstantsIdD = pi4.getId();\n        final List<Long> processInstantsIds = new ArrayList<>();\n        processInstantsIds.add(processInstantsIdA);\n        processInstantsIds.add(processInstantsIdB);\n        processInstantsIds.add(processInstantsIdC);\n        processInstantsIds.add(processInstantsIdD);\n\n        // do search and assert\n        final Map<Long, ProcessDeploymentInfo> processDeploymentInfos = getProcessAPI()\n                .getProcessDeploymentInfosFromProcessInstanceIds(processInstantsIds);\n        assertNotNull(processDeploymentInfos);\n        assertEquals(4, processDeploymentInfos.size());\n        assertEquals(processDefinition1.getId(), processDeploymentInfos.get(processInstantsIdA).getProcessId());\n        assertEquals(processDefinition2.getId(), processDeploymentInfos.get(processInstantsIdB).getProcessId());\n        assertEquals(processDefinition3.getId(), processDeploymentInfos.get(processInstantsIdC).getProcessId());\n        assertEquals(processDefinition4.getId(), processDeploymentInfos.get(processInstantsIdD).getProcessId());\n\n        // delete data for test\n        disableAndDeleteProcess(processDefinition1, processDefinition2, processDefinition3, processDefinition4);\n    }\n\n    @Test\n    public void retryTask() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask(\"step1\", ACTOR_NAME)\n                .getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n\n        final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId());\n        final long activityInstanceId = waitForUserTaskAndAssignIt(pi0, \"step1\", user).getId();\n        getProcessAPI().setActivityStateById(activityInstanceId, 3);\n\n        final HumanTaskInstance userTaskInstance = getProcessAPI().getHumanTaskInstance(activityInstanceId);\n        assertEquals(ActivityStates.FAILED_STATE, userTaskInstance.getState());\n\n        getProcessAPI().retryTask(activityInstanceId);\n\n        waitForProcessToFinish(pi0);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    /**\n     * Scenario in the same task:\n     * 1 -> connector on_enter fails\n     * 2 -> fix issue + retryTask\n     * 3 -> operation fails\n     * 4 -> fix issue + retryTask\n     * 5 -> connector on_finish fails\n     * 6 -> fix issue + retryTask\n     * 7 -> transition fails\n     * 8 -> fix issue + retryTask\n     * 9 -> task is finally completed\n     */\n    @Test\n    public void retryTask_should_retry_failed_connectors_and_operations() throws Exception {\n        //given\n        String watchingOnEnterVar = \"watchingOnEnterVar\";\n        String watchingOnFinishVar = \"watchingOnFinishVar\";\n        String watchingOperationsVar = \"watchingOperationsVar\";\n        String watchingTransitionVar = \"watchingTransitionVar\";\n        String countVar = \"count\";\n\n        String firstStepName = \"auto\";\n        String secondStepName = \"auto1\";\n        final ProcessDefinition processDefinition = deployProcessWithConnectorsAndOperationsThrowingException(\n                watchingOnEnterVar, watchingOnFinishVar,\n                watchingOperationsVar, watchingTransitionVar, countVar, firstStepName, secondStepName);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        //will fail on connector on enter\n        FlowNodeInstance flowNodeInstance = waitForFlowNodeInFailedState(processInstance, firstStepName);\n\n        //when\n        //set variable to a value different of 1: operation will execute with success\n        getProcessAPI().updateProcessDataInstance(watchingOnEnterVar, processInstance.getId(), \"none\");\n        getProcessAPI().retryTask(flowNodeInstance.getId());\n\n        //then will fail on operation\n        waitForFlowNodeInFailedState(processInstance, firstStepName);\n\n        //when\n        getProcessAPI().updateProcessDataInstance(watchingOperationsVar, processInstance.getId(), 1);\n        getProcessAPI().retryTask(flowNodeInstance.getId());\n\n        //then will fail on connector on finish\n        waitForFlowNodeInFailedState(processInstance, firstStepName);\n\n        //when\n        getProcessAPI().updateProcessDataInstance(watchingOnFinishVar, processInstance.getId(), \"none\");\n        getProcessAPI().retryTask(flowNodeInstance.getId());\n\n        //then will fail due to exception on transition\n        waitForFlowNodeInFailedState(processInstance, firstStepName);\n\n        //when\n        getProcessAPI().updateProcessDataInstance(watchingTransitionVar, processInstance.getId(), 1);\n        System.out.println(\"------------------- retrying after expression -------------\");\n        getProcessAPI().retryTask(flowNodeInstance.getId());\n\n        //finally completed\n        waitForActivityInCompletedState(processInstance, firstStepName, true);\n        waitForActivityInCompletedState(processInstance, secondStepName, true);\n        waitForProcessToFinish(processInstance);\n\n        //check connectors\n        List<ArchivedConnectorInstance> connectorInstances = getArchivedConnectorInstances(flowNodeInstance);\n        assertThat(connectorInstances).extracting(\"state\").containsExactly(ConnectorState.DONE, ConnectorState.DONE);\n        assertThat(connectorInstances).extracting(\"name\").containsExactly(\"throwsExceptionOnEnter\",\n                \"throwsExceptionOnFinish\");\n\n        //check data instance\n        DataInstance count = getProcessAPI().getArchivedActivityDataInstance(countVar, flowNodeInstance.getId());\n        assertThat(count.getValue()).isEqualTo(1);\n\n        //clean\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    private ProcessDefinition deployProcessWithConnectorsAndOperationsThrowingException(final String watchingOnEnterVar,\n            final String watchingOnFinishVar,\n            final String watchingOperationsVar, final String watchingTransitionVar, final String countVar,\n            final String firstStepName,\n            final String secondStepName) throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        //global data\n        processBuilder.addShortTextData(watchingOnEnterVar,\n                new ExpressionBuilder().createConstantStringExpression(TestConnectorThatThrowException.NORMAL));\n        processBuilder.addShortTextData(watchingOnFinishVar,\n                new ExpressionBuilder().createConstantStringExpression(\"normal\"));\n        processBuilder.addIntegerData(watchingOperationsVar,\n                new ExpressionBuilder().createConstantIntegerExpression(0));\n        processBuilder.addIntegerData(countVar, new ExpressionBuilder().createConstantIntegerExpression(0));\n        processBuilder.addIntegerData(watchingTransitionVar,\n                new ExpressionBuilder().createConstantIntegerExpression(0));\n        AutomaticTaskDefinitionBuilder taskBuilder = processBuilder.addAutomaticTask(firstStepName);\n        //local data\n        taskBuilder.addShortTextData(\"localData\", new ExpressionBuilder().createConstantStringExpression(\"default\"));\n\n        addConnectors(watchingOnEnterVar, watchingOnFinishVar, taskBuilder);\n        addOperation(watchingOperationsVar, countVar, taskBuilder);\n\n        processBuilder.addAutomaticTask(secondStepName);\n        addTransition(firstStepName, secondStepName, watchingTransitionVar, processBuilder);\n        return deployAndEnableProcessWithConnector(processBuilder, \"TestConnectorThatThrowException.impl\",\n                TestConnectorThatThrowException.class,\n                \"TestConnectorThatThrowException.jar\");\n    }\n\n    private void addTransition(final String firstStepName, final String secondStepName,\n            final String watchingTransitionVar,\n            final ProcessDefinitionBuilder processBuilder) throws InvalidExpressionException {\n        Expression transitionCondition = new ExpressionBuilder().createGroovyScriptExpression(\"throwExceptionIfZero\",\n                \"if (\" + watchingTransitionVar\n                        + \" == 0) {\\nthrow new RuntimeException(\\\" was zero\\\");\\n} \\nreturn true;\",\n                Boolean.class.getName(),\n                new ExpressionBuilder().createDataExpression(watchingTransitionVar, Integer.class.getName()));\n        processBuilder.addTransition(firstStepName, secondStepName, transitionCondition);\n    }\n\n    private void addOperation(final String watchingOperationsVar, final String countVar,\n            final AutomaticTaskDefinitionBuilder taskBuilder)\n            throws InvalidExpressionException {\n        List<Expression> dependencies = Arrays.asList(\n                new ExpressionBuilder().createDataExpression(watchingOperationsVar, Integer.class.getName()),\n                new ExpressionBuilder().createDataExpression(countVar, Integer.class.getName()));\n        Expression leftOperationExpr = new ExpressionBuilder().createGroovyScriptExpression(\"throwExceptionIfZero\",\n                \"if (\" + watchingOperationsVar\n                        + \"== 0) {\\nthrow new RuntimeException(\\\" was zero\\\");\\n} \\nreturn \" + countVar + \" + 1;\",\n                Integer.class.getName(), dependencies);\n        taskBuilder.addOperation(new OperationBuilder().createSetDataOperation(countVar, leftOperationExpr));\n    }\n\n    private void addConnectors(final String watchingOnEnterVar, final String watchingOnFinishVar,\n            final AutomaticTaskDefinitionBuilder taskBuilder)\n            throws InvalidExpressionException {\n        //connector on enter\n        taskBuilder\n                .addConnector(\"throwsExceptionOnEnter\", \"testConnectorThatThrowException\", \"1.0\",\n                        ConnectorEvent.ON_ENTER)\n                .addInput(\"kind\",\n                        new ExpressionBuilder().createDataExpression(watchingOnEnterVar, String.class.getName()));\n        //connector on finish\n        taskBuilder\n                .addConnector(\"throwsExceptionOnFinish\", \"testConnectorThatThrowException\", \"1.0\",\n                        ConnectorEvent.ON_FINISH)\n                .addInput(\"kind\",\n                        new ExpressionBuilder().createDataExpression(watchingOnFinishVar, String.class.getName()));\n    }\n\n    @Test\n    public void can_retryTask_twice() throws Exception {\n        //given\n        Expression dataExpression = new ExpressionBuilder().createDataExpression(\"watchingVar\", String.class.getName());\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addShortTextData(\"watchingVar\",\n                new ExpressionBuilder().createConstantStringExpression(\"normal\"));\n        processBuilder.addAutomaticTask(\"auto\").addConnector(\"testConnectorThatThrowException\",\n                \"testConnectorThatThrowException\", \"1.0\", ConnectorEvent.ON_ENTER)\n                .addInput(\"kind\", dataExpression);\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithConnector(processBuilder,\n                \"TestConnectorThatThrowException.impl\",\n                TestConnectorThatThrowException.class, \"TestConnectorThatThrowException.jar\");\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        //first fail\n        FlowNodeInstance flowNodeInstance = waitForFlowNodeInFailedState(processInstance, \"auto\");\n\n        //when\n        getProcessAPI().retryTask(flowNodeInstance.getId());\n\n        //then fail again\n        waitForFlowNodeInFailedState(processInstance, \"auto\");\n\n        //when\n        //set variable to a value different of 1: operation will execute with success\n        getProcessAPI().updateProcessDataInstance(\"watchingVar\", processInstance.getId(), \"none\");\n        getProcessAPI().retryTask(flowNodeInstance.getId());\n\n        //then success\n        waitForActivityInCompletedState(processInstance, \"auto\", true);\n        waitForProcessToFinish(processInstance);\n\n        List<ArchivedConnectorInstance> connectorInstances = getArchivedConnectorInstances(flowNodeInstance);\n        assertThat(connectorInstances).hasSize(1);\n        assertThat(connectorInstances.get(0).getState()).isEqualTo(ConnectorState.DONE);\n\n        //clean\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    private List<ArchivedConnectorInstance> getArchivedConnectorInstances(final FlowNodeInstance flowNodeInstance)\n            throws SearchException {\n        SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 100);\n        builder.filter(ArchiveConnectorInstancesSearchDescriptor.CONTAINER_ID, flowNodeInstance.getId());\n        builder.filter(ArchiveConnectorInstancesSearchDescriptor.CONTAINER_TYPE, \"flowNode\");\n        builder.sort(ArchiveConnectorInstancesSearchDescriptor.NAME, Order.ASC);\n        SearchResult<ArchivedConnectorInstance> searchResult = getProcessAPI()\n                .searchArchivedConnectorInstances(builder.done());\n        return searchResult.getResult();\n    }\n\n    @Test(expected = NotFoundException.class)\n    public void getArchiveCommentNotFound() throws Exception {\n        getProcessAPI().getArchivedComment(123456789L);\n    }\n\n    @Test\n    public void getProcessDeploymentInfosFromArchivedProcessInstanceIds() throws Exception {\n        final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList(\"step1_1\"),\n                        Arrays.asList(false));\n        final ProcessDefinition processDefinition1 = deployAndEnableProcess(designProcessDefinition1);\n\n        final ProcessInstance pi1 = getProcessAPI().startProcess(processDefinition1.getId());\n        checkProcessInstanceIsArchived(pi1);\n\n        // get archived process instances. It will have only the state completed.\n        final List<ArchivedProcessInstance> aProcessInstances = getProcessAPI().getArchivedProcessInstances(0, 10,\n                ProcessInstanceCriterion.ARCHIVE_DATE_DESC);\n        assertEquals(1, aProcessInstances.size());\n        //\n        final ArchivedProcessInstance archivedProcessInstance1 = aProcessInstances.get(0);\n\n        // We check that the retrieved process are the good one :\n        assertEquals(pi1.getId(), archivedProcessInstance1.getSourceObjectId());\n\n        // put processInstantsId to a list as parameter\n        final List<Long> archivedProcessInstantsIds = new ArrayList<>();\n        archivedProcessInstantsIds.add(archivedProcessInstance1.getId());\n\n        // do search and assert\n        final Map<Long, ProcessDeploymentInfo> processDeploymentInfos = getProcessAPI()\n                .getProcessDeploymentInfosFromArchivedProcessInstanceIds(\n                        archivedProcessInstantsIds);\n        assertNotNull(processDeploymentInfos);\n        assertEquals(1, processDeploymentInfos.size());\n        assertEquals(processDefinition1.getId(),\n                processDeploymentInfos.get(archivedProcessInstance1.getId()).getProcessId());\n\n        // delete data for test\n        disableAndDeleteProcess(processDefinition1);\n    }\n\n    @Test\n    public void cantResolveDataInExpressionInDataDefaultValue() throws Exception {\n        final Expression aExpression = new ExpressionBuilder().createDataExpression(\"name\", String.class.getName());\n        final Expression aScript = new ExpressionBuilder().createGroovyScriptExpression(\"script\", \"return name\",\n                String.class.getName(), aExpression);\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"cantResolveDataInExpressionInDataDefaultValue\", \"1\");\n        processBuilder.addActor(ACTOR_NAME).addDescription(\"Process to test archiving mechanism\");\n        processBuilder.addShortTextData(\"name\", new ExpressionBuilder().createConstantStringExpression(\"a value\"));\n        final AutomaticTaskDefinitionBuilder automaticTaskDefinitionBuilder = processBuilder\n                .addAutomaticTask(\"activity\");\n        automaticTaskDefinitionBuilder.addShortTextData(\"taskData\", aExpression);\n        automaticTaskDefinitionBuilder.addShortTextData(\"taskDataFromString\", aScript);\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME,\n                user);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(user.getId(), processDefinition.getId());\n        try {\n            waitForProcessToFinish(processInstance);\n        } catch (final Exception e) {\n            fail(\"Process should finish\");\n        }\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void errorMessageWhileStartingProcessForClassCastToInteger() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"cantResolveDataInExpressionInDataDefaultValue\", \"1\");\n        processBuilder.addActor(ACTOR_NAME).addDescription(\"Process to test archiving mechanism\");\n        processBuilder.addIntegerData(\"aData\", null);\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME,\n                user);\n\n        final Operation stringOperation = BuildTestUtil.buildStringOperation(\"aData\", \"15\", false);\n        final Map<String, Serializable> context = new HashMap<>();\n        context.put(\"page\", \"1\");\n\n        try {\n            getProcessAPI().startProcess(processDefinition.getId(), Arrays.asList(stringOperation), context);\n        } catch (final ExecutionException e) {\n            assertThat(e.getMessage()).contains(\"aData\");\n            assertThat(e.getMessage()).contains(\"incompatible type\");\n        }\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void errorMessageWhileStartingProcessForClassCastToLong() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"cantResolveDataInExpressionInDataDefaultValue\", \"1\");\n        processBuilder.addActor(ACTOR_NAME).addDescription(\"Process to test archiving mechanism\");\n        processBuilder.addLongData(\"aData\", null);\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME,\n                user);\n\n        final Operation stringOperation = BuildTestUtil.buildStringOperation(\"aData\", \"15\", false);\n        final Map<String, Serializable> context = new HashMap<>();\n        context.put(\"page\", \"1\");\n\n        try {\n            getProcessAPI().startProcess(processDefinition.getId(), Arrays.asList(stringOperation), context);\n        } catch (final ExecutionException e) {\n            assertThat(e.getMessage()).contains(\"aData\");\n            assertThat(e.getMessage()).contains(\"incompatible type\");\n        }\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void errorMessageWhileStartingProcessForClassCastToDouble() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"cantResolveDataInExpressionInDataDefaultValue\", \"1\");\n        processBuilder.addActor(ACTOR_NAME).addDescription(\"Process to test archiving mechanism\");\n        processBuilder.addDoubleData(\"aData\", null);\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME,\n                user);\n\n        final Operation stringOperation = BuildTestUtil.buildStringOperation(\"aData\", \"15\", false);\n        final ArrayList<Operation> operations = new ArrayList<>(1);\n        operations.add(stringOperation);\n        final Map<String, Serializable> context = new HashMap<>();\n        context.put(\"page\", \"1\");\n\n        try {\n            getProcessAPI().startProcess(processDefinition.getId(), operations, context);\n        } catch (final ExecutionException e) {\n            assertThat(e.getMessage()).contains(\"aData\");\n            assertThat(e.getMessage()).contains(\"incompatible type\");\n        }\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    private List<Long> createProcessDefinitionWithTwoHumanStepsAndDeployBusinessArchive(final int nbProcess)\n            throws Exception {\n        final List<Long> ids = new ArrayList<>();\n        for (int i = 0; i < nbProcess; i++) {\n            String processName = PROCESS_NAME;\n            if (i >= 0 && i < 10) {\n                processName += \"0\";\n            }\n            final DesignProcessDefinition processDefinition = BuildTestUtil\n                    .buildProcessDefinitionWithHumanAndAutomaticSteps(processName + i,\n                            PROCESS_VERSION\n                                    + i,\n                            Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true));\n            ids.add(deployProcess(new BusinessArchiveBuilder().createNewBusinessArchive()\n                    .setProcessDefinition(processDefinition).done()).getId());\n        }\n        return ids;\n    }\n\n    private DesignProcessDefinition createProcessWithActorAndHumanTaskAndStringData() throws Exception {\n        return new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addDescription(\"Delivery all day and night long\").addUserTask(\"step1\", ACTOR_NAME)\n                .addShortTextData(\"dataName\", new ExpressionBuilder().createConstantStringExpression(\"beforeUpdate\"))\n                .getProcess();\n    }\n\n    @Test\n    public void startProcessUsingInitialVariableValues() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"cantResolveDataInExpressionInDataDefaultValue\", \"1\");\n        processBuilder.addActor(ACTOR_NAME).addDescription(\"Process to test archiving mechanism\");\n        processBuilder.addDoubleData(\"D\", new ExpressionBuilder().createConstantDoubleExpression(3.14));\n        processBuilder.addData(\"bigD\", BigDecimal.class.getName(), null);\n        processBuilder.addUserTask(\"step1\", ACTOR_NAME);\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME,\n                user);\n\n        final Map<String, Serializable> variables = new HashMap<>();\n        variables.put(\"bigD\", new BigDecimal(\"3.141592653589793\"));\n        final ProcessInstance instance = getProcessAPI().startProcess(processDefinition.getId(), variables);\n\n        DataInstance dataInstance = getProcessAPI().getProcessDataInstance(\"bigD\", instance.getId());\n        assertEquals(new BigDecimal(\"3.141592653589793\"), dataInstance.getValue());\n        dataInstance = getProcessAPI().getProcessDataInstance(\"D\", instance.getId());\n        assertEquals(Double.valueOf(3.14), dataInstance.getValue());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void purgeClassLoader_should_clean_the_classloader_of_the_process_definition_when_it_is_disabled_without_a_running_instance()\n            throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"cantResolveDataInExpressionInDataDefaultValue\", \"1\");\n        processBuilder.addActor(ACTOR_NAME);\n        processBuilder.addUserTask(\"step1\", ACTOR_NAME);\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME,\n                user);\n        getProcessAPI().disableProcess(processDefinition.getId());\n\n        getProcessAPI().purgeClassLoader(processDefinition.getId());\n\n        deleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getProcessInstancesWithLabelOnStringIndex() throws Exception {\n        ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"1\" + PROCESS_NAME, PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        processBuilder.addUserTask(\"step1\", ACTOR_NAME);\n        processBuilder.setStringIndex(1, \"Label1\", null);\n        processBuilder.setStringIndex(2, \"Label2\", null);\n        processBuilder.setStringIndex(3, \"Label3\", null);\n        processBuilder.setStringIndex(4, \"Label4\", null);\n        processBuilder.setStringIndex(5, \"Label5\", null);\n        final ProcessDefinition process1 = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user);\n        processBuilder = new ProcessDefinitionBuilder().createNewInstance(\"2\" + PROCESS_NAME, PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        processBuilder.addUserTask(\"step1\", ACTOR_NAME);\n        processBuilder.setStringIndex(1, \"LabelBis1\", null);\n        processBuilder.setStringIndex(2, \"LabelBis2\", null);\n        processBuilder.setStringIndex(3, \"LabelBis3\", null);\n        processBuilder.setStringIndex(4, \"LabelBis4\", null);\n        processBuilder.setStringIndex(5, \"LabelBis5\", null);\n        final ProcessDefinition process2 = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user);\n        startProcessAndWaitForTask(process1.getId(), \"step1\");\n        startProcessAndWaitForTask(process2.getId(), \"step1\");\n        final List<ProcessInstance> processInstances = getProcessAPI().getProcessInstances(0, 10,\n                ProcessInstanceCriterion.NAME_ASC);\n        assertEquals(2, processInstances.size());\n        ProcessInstance processInstance = processInstances.get(0);\n        assertEquals(\"1\" + PROCESS_NAME, processInstance.getName());\n        assertEquals(\"Label1\", processInstance.getStringIndexLabel(1));\n        assertEquals(\"Label2\", processInstance.getStringIndexLabel(2));\n        assertEquals(\"Label3\", processInstance.getStringIndexLabel(3));\n        assertEquals(\"Label4\", processInstance.getStringIndexLabel(4));\n        assertEquals(\"Label5\", processInstance.getStringIndexLabel(5));\n        processInstance = processInstances.get(1);\n        assertEquals(\"2\" + PROCESS_NAME, processInstance.getName());\n        assertEquals(\"LabelBis1\", processInstance.getStringIndexLabel(1));\n        assertEquals(\"LabelBis2\", processInstance.getStringIndexLabel(2));\n        assertEquals(\"LabelBis3\", processInstance.getStringIndexLabel(3));\n        assertEquals(\"LabelBis4\", processInstance.getStringIndexLabel(4));\n        assertEquals(\"LabelBis5\", processInstance.getStringIndexLabel(5));\n        disableAndDeleteProcess(process1);\n        disableAndDeleteProcess(process2);\n    }\n\n    @Test\n    public void should_event_sub_process_do_not_have_search_index_of_parent() throws Exception {\n        //given\n        /*\n         * We test here that an event sub process instantiation do nothing on the parent process\n         * see bug BS-15123 and BS-15275\n         */\n        //given\n        ProcessDefinitionBuilder parentProcessBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"ParentProcessWithSignalEventSubProcess\", \"1.0\");\n        parentProcessBuilder.addActor(ACTOR_NAME);\n        parentProcessBuilder.addUserTask(\"userTask\", ACTOR_NAME);\n        parentProcessBuilder.setStringIndex(1, \"index1\",\n                new ExpressionBuilder().createConstantStringExpression(\"index1Value\"));\n        parentProcessBuilder.addShortTextData(\"textData\",\n                new ExpressionBuilder().createConstantStringExpression(\"parentVar\"));\n        //construct sub process\n        SubProcessDefinitionBuilder subProcessBuilder = parentProcessBuilder\n                .addSubProcess(\"interruptWithSignalProcess\", true).getSubProcessBuilder();\n        StartEventDefinitionBuilder startEventDefinitionBuilder = subProcessBuilder.addStartEvent(\"signalStart\");\n        startEventDefinitionBuilder.addSignalEventTrigger(\"theSignal\");\n        subProcessBuilder.addUserTask(\"userTaskInSubProcess\", ACTOR_NAME);\n        subProcessBuilder.addEndEvent(\"endSubProcess\");\n        subProcessBuilder.addTransition(\"signalStart\", \"userTaskInSubProcess\");\n        subProcessBuilder.addTransition(\"userTaskInSubProcess\", \"endSubProcess\");\n        ProcessDefinition processDefinition = deployAndEnableProcessWithActor(parentProcessBuilder.done(), ACTOR_NAME,\n                user);\n        getProcessAPI().startProcess(processDefinition.getId());\n        //when\n        waitForUserTask(\"userTask\");\n        getProcessAPI().sendSignal(\"theSignal\");\n        //then\n        ActivityInstance eventSubProcessActivity = getProcessAPI()\n                .getActivityInstance(waitForUserTask(\"userTaskInSubProcess\"));\n        Assertions\n                .assertThat(getProcessAPI().getProcessInstance(eventSubProcessActivity.getRootContainerId())\n                        .getStringIndex1())\n                .isEqualTo(\"index1Value\");\n        Assertions\n                .assertThat(getProcessAPI().getProcessInstance(eventSubProcessActivity.getParentProcessInstanceId())\n                        .getStringIndex1())\n                .isNull();\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getProcessInstancesWithStringIndex() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"1\" + PROCESS_NAME, PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        processBuilder.addUserTask(\"step1\", ACTOR_NAME);\n        final ExpressionBuilder expressionBuilder = new ExpressionBuilder();\n        processBuilder.setStringIndex(1, \"Label1\", expressionBuilder.createConstantStringExpression(\"Value1\"));\n        processBuilder.setStringIndex(2, \"Label2\", expressionBuilder.createGroovyScriptExpression(\"script\",\n                \"return \\\"a\\\"+\\\"b\\\";\", String.class.getName()));\n        processBuilder.setStringIndex(3, \"Label3\", null);\n        final ProcessDefinition process1 = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user);\n        final ProcessInstance processInstance = startProcessAndWaitForTask(process1.getId(), \"step1\")\n                .getProcessInstance();\n        assertEquals(\"Label1\", processInstance.getStringIndexLabel(1));\n        assertEquals(\"Label2\", processInstance.getStringIndexLabel(2));\n        assertEquals(\"Label3\", processInstance.getStringIndexLabel(3));\n        assertNull(processInstance.getStringIndexLabel(4));\n        assertNull(processInstance.getStringIndexLabel(5));\n        assertEquals(\"Value1\", processInstance.getStringIndex1());\n        assertEquals(\"ab\", processInstance.getStringIndex2());\n        assertNull(processInstance.getStringIndex3());\n        assertNull(processInstance.getStringIndex4());\n        assertNull(processInstance.getStringIndex5());\n\n        disableAndDeleteProcess(process1);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/ProcessParameterIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.process;\n\nimport static org.hamcrest.CoreMatchers.is;\nimport static org.junit.Assert.*;\n\nimport java.io.Serializable;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.CommonAPIIT;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.data.DataInstance;\nimport org.bonitasoft.engine.bpm.form.FormMappingDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.form.FormMappingModelBuilder;\nimport org.bonitasoft.engine.bpm.parameter.ParameterCriterion;\nimport org.bonitasoft.engine.bpm.parameter.ParameterInstance;\nimport org.bonitasoft.engine.bpm.process.ConfigurationState;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.NotFoundException;\nimport org.bonitasoft.engine.exception.RetrieveException;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.form.FormMappingTarget;\nimport org.bonitasoft.engine.form.FormMappingType;\nimport org.bonitasoft.engine.identity.User;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class ProcessParameterIT extends CommonAPIIT {\n\n    @After\n    public void afterTest() throws BonitaException {\n        logout();\n    }\n\n    @Before\n    public void beforeTest() throws BonitaException {\n        loginWithTechnicalUser();\n    }\n\n    @Test\n    public void getNoParametersWhenAddingNoParameters() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(\"firstProcess\",\n                \"1.0\");\n        processBuilder.addUserTask(\"userTask1\", null);\n        final DesignProcessDefinition processDefinition = processBuilder.done();\n        final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive();\n        businessArchive.setProcessDefinition(processDefinition);\n\n        final ProcessDefinition definition = getProcessAPI().deploy(businessArchive.done());\n        final int numberOfParamters = getProcessAPI().getNumberOfParameterInstances(definition.getId());\n        assertEquals(0, numberOfParamters);\n\n        deleteProcess(definition);\n    }\n\n    @Test\n    public void getNumberOfParameters() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(\"firstProcess\",\n                \"1.0\");\n        processBuilder.addParameter(\"key1\", String.class.getCanonicalName())\n                .addParameter(\"key2\", String.class.getCanonicalName())\n                .addParameter(\"key3\", String.class.getCanonicalName())\n                .addParameter(\"key4\", String.class.getCanonicalName()).addUserTask(\"userTask1\", null);\n        final DesignProcessDefinition processDefinition = processBuilder.done();\n        final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive();\n        businessArchive.setProcessDefinition(processDefinition);\n\n        final ProcessDefinition definition = getProcessAPI().deploy(businessArchive.done());\n        final int numberOfParamters = getProcessAPI().getNumberOfParameterInstances(definition.getId());\n        assertEquals(4, numberOfParamters);\n\n        deleteProcess(definition);\n    }\n\n    @Test(expected = RetrieveException.class)\n    public void getNumberOfParametersThrowsAnExceptionBecauseTheProcessDoesNotExist() {\n        getProcessAPI().getNumberOfParameterInstances(45);\n    }\n\n    @Test\n    public void getNoParameters() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(\"firstProcess\",\n                \"1.0\");\n        processBuilder.addUserTask(\"userTask1\", null);\n        final DesignProcessDefinition processDefinition = processBuilder.done();\n        final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive();\n        businessArchive.setProcessDefinition(processDefinition);\n\n        final ProcessDefinition definition = getProcessAPI().deploy(businessArchive.done());\n        final List<ParameterInstance> parameters = getProcessAPI().getParameterInstances(definition.getId(), 0, 20,\n                ParameterCriterion.NAME_DESC);\n        assertEquals(0, parameters.size());\n\n        deleteProcess(definition);\n    }\n\n    @Test\n    public void getParameters() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(\"firstProcess\",\n                \"1.0\");\n        processBuilder.addParameter(\"key1\", String.class.getCanonicalName())\n                .addParameter(\"key2\", String.class.getCanonicalName())\n                .addUserTask(\"userTask1\", null);\n\n        final DesignProcessDefinition processDefinition = processBuilder.done();\n        final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive();\n        businessArchive.setProcessDefinition(processDefinition);\n        final Map<String, String> params = new HashMap<>();\n        params.put(\"key1\", \"engine\");\n        params.put(\"key2\", \"bos\");\n        businessArchive.setParameters(params);\n\n        final ProcessDefinition definition = getProcessAPI().deploy(businessArchive.done());\n        final List<ParameterInstance> parameters = getProcessAPI().getParameterInstances(definition.getId(), 0, 20,\n                ParameterCriterion.NAME_ASC);\n        assertEquals(2, parameters.size());\n        final ParameterInstance firstParameter = parameters.get(0);\n        assertEquals(\"key1\", firstParameter.getName());\n        assertEquals(\"engine\", firstParameter.getValue());\n        final ParameterInstance secondParameter = parameters.get(1);\n        assertEquals(\"key2\", secondParameter.getName());\n        assertEquals(\"bos\", secondParameter.getValue());\n\n        deleteProcess(definition);\n    }\n\n    @Test\n    public void getParameter() throws Exception {\n        final String parameterValue = \"a very important piece of information\";\n        final String parameterName = \"myParam1\";\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(\"getParameter\",\n                \"1.0\");\n        processBuilder.addParameter(\"myParam1\", String.class.getCanonicalName())\n                .addDescription(\"Parameter description\");\n        processBuilder.addParameter(\"myParam2\", String.class.getCanonicalName()).addUserTask(\"userTask1\", null);\n\n        final DesignProcessDefinition processDefinition = processBuilder.done();\n        final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive();\n        businessArchive.setProcessDefinition(processDefinition);\n        final Map<String, String> params = new HashMap<>();\n        params.put(parameterName, parameterValue);\n        byte[] theParam2Value = new byte[1150];\n        Arrays.fill(theParam2Value, (byte) 65);\n        params.put(\"myParam2\", new String(theParam2Value));\n        businessArchive.setParameters(params);\n\n        final ProcessDefinition definition = getProcessAPI().deploy(businessArchive.done());\n        final ParameterInstance parameter = getProcessAPI().getParameterInstance(definition.getId(), parameterName);\n        assertEquals(parameterName, parameter.getName());\n        assertEquals(parameterValue, parameter.getValue());\n        assertEquals(\"Parameter description\", parameter.getDescription());\n\n        deleteProcess(definition);\n    }\n\n    @Test\n    public void setProcessDataDefaultValueWithParameterValue() throws Exception {\n        final User user = createUser(\"jules\", \"his_password\");\n        final String parameterName = \"anotherParam\";\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"setDataDefaultValueWithParameter\", \"9.23\");\n        processBuilder.addActor(ACTOR_NAME);\n        final String aTask = \"userTask1\";\n        final String dataName = \"aData\";\n        processBuilder\n                .addParameter(parameterName, String.class.getCanonicalName())\n                .addData(dataName, String.class.getName(),\n                        new ExpressionBuilder().createParameterExpression(\"takes value of default parameter value\",\n                                parameterName, String.class.getName()))\n                .addUserTask(aTask, ACTOR_NAME);\n\n        final DesignProcessDefinition design = processBuilder.done();\n        final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive();\n        businessArchive.setProcessDefinition(design);\n        final Map<String, String> params = new HashMap<>(1);\n        final String paramValue = \"4 is the answer\";\n        params.put(parameterName, paramValue);\n        businessArchive.setParameters(params);\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchive.done(), ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance, aTask);\n        final DataInstance dataInstance = getProcessAPI().getProcessDataInstance(dataName, processInstance.getId());\n        assertEquals(paramValue, dataInstance.getValue());\n\n        disableAndDeleteProcess(processDefinition);\n        deleteUser(user);\n    }\n\n    @Test\n    public void deployWithNullParamAndFormMappings() throws Exception {\n        final String parameterName = \"myParam1\";\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(\"getParameter\",\n                \"1.0\");\n        processBuilder.addParameter(\"myParam1\", String.class.getCanonicalName())\n                .addParameter(\"myParam2\", String.class.getCanonicalName())\n                .addUserTask(\"userTask1\", null);\n\n        final DesignProcessDefinition processDefinition = processBuilder.done();\n        final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive();\n        businessArchive.setProcessDefinition(processDefinition);\n        final Map<String, String> params = new HashMap<>();\n        params.put(parameterName, null);\n        businessArchive.setParameters(params);\n        businessArchive.setFormMappings(FormMappingModelBuilder.buildFormMappingModel().withFormMapping(\n                FormMappingDefinitionBuilder\n                        .buildFormMapping(\"somePage\", FormMappingType.TASK, FormMappingTarget.INTERNAL)\n                        .withTaskname(\"someTask\").build())\n                .build());\n\n        final ProcessDefinition definition = getProcessAPI().deploy(businessArchive.done());\n\n        deleteProcess(definition);\n    }\n\n    @Test(expected = RetrieveException.class)\n    public void getParameterOfAnUnknownProcess() throws BonitaException {\n        getProcessAPI().getParameterInstance(123456789l, \"unknown\");\n    }\n\n    @Test(expected = NotFoundException.class)\n    public void getUnknownParameter() throws Exception {\n        final String parameterValue = \"a very important piece of information\";\n        final String parameterName = \"myParam1\";\n        final String wrongParameterName = \"wrongParameterName\";\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"getUnknownParameter\", \"1.0\");\n        processBuilder.addParameter(\"myParam1\", String.class.getCanonicalName())\n                .addParameter(\"myParam2\", String.class.getCanonicalName())\n                .addUserTask(\"userTask1\", null);\n\n        final DesignProcessDefinition processDefinition = processBuilder.done();\n        final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive();\n        businessArchive.setProcessDefinition(processDefinition);\n        final Map<String, String> params = new HashMap<>();\n        params.put(parameterName, parameterValue);\n        params.put(\"myParam2\", \"an unused parameter\");\n        businessArchive.setParameters(params);\n\n        final ProcessDefinition definition = getProcessAPI().deploy(businessArchive.done());\n        try {\n            getProcessAPI().getParameterInstance(definition.getId(), wrongParameterName);\n        } finally {\n            deleteProcess(definition);\n        }\n    }\n\n    @Test\n    public void sortParametersByNameAsc() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(\"firstProcess\",\n                \"1.0\");\n        processBuilder.addParameter(\"bee\", String.class.getCanonicalName())\n                .addParameter(\"bear\", String.class.getCanonicalName())\n                .addParameter(\"squirrel\", String.class.getCanonicalName())\n                .addParameter(\"donkey\", String.class.getCanonicalName())\n                .addUserTask(\"userTask1\", null);\n        final DesignProcessDefinition processDefinition = processBuilder.done();\n        final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive();\n        businessArchive.setProcessDefinition(processDefinition);\n        final Map<String, String> params = new HashMap<>();\n        params.put(\"donkey\", \"engine\");\n        params.put(\"bear\", \"bos\");\n        params.put(\"squirrel\", \"bee\");\n        params.put(\"bee\", \"busy\");\n        businessArchive.setParameters(params);\n\n        final ProcessDefinition definition = getProcessAPI().deploy(businessArchive.done());\n        final List<ParameterInstance> parameters = getProcessAPI().getParameterInstances(definition.getId(), 0, 20,\n                ParameterCriterion.NAME_ASC);\n        assertEquals(4, parameters.size());\n        assertEquals(\"bear\", parameters.get(0).getName());\n        assertEquals(\"bee\", parameters.get(1).getName());\n        assertEquals(\"donkey\", parameters.get(2).getName());\n        assertEquals(\"squirrel\", parameters.get(3).getName());\n\n        deleteProcess(definition);\n    }\n\n    @Test\n    public void sortParametersByNameDesc() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(\"firstProcess\",\n                \"1.0\");\n        processBuilder.addParameter(\"bee\", String.class.getCanonicalName())\n                .addParameter(\"bear\", String.class.getCanonicalName())\n                .addParameter(\"squirrel\", String.class.getCanonicalName())\n                .addParameter(\"donkey\", String.class.getCanonicalName())\n                .addUserTask(\"userTask1\", null);\n        final DesignProcessDefinition processDefinition = processBuilder.done();\n        final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive();\n        businessArchive.setProcessDefinition(processDefinition);\n        final Map<String, String> params = new HashMap<>();\n        params.put(\"donkey\", \"engine\");\n        params.put(\"bear\", \"bos\");\n        params.put(\"squirrel\", \"bee\");\n        params.put(\"bee\", \"busy\");\n        businessArchive.setParameters(params);\n\n        final ProcessDefinition definition = getProcessAPI().deploy(businessArchive.done());\n        final List<ParameterInstance> parameters = getProcessAPI().getParameterInstances(definition.getId(), 0, 20,\n                ParameterCriterion.NAME_DESC);\n        assertEquals(4, parameters.size());\n        assertEquals(\"squirrel\", parameters.get(0).getName());\n        assertEquals(\"donkey\", parameters.get(1).getName());\n        assertEquals(\"bee\", parameters.get(2).getName());\n        assertEquals(\"bear\", parameters.get(3).getName());\n\n        deleteProcess(definition);\n    }\n\n    @Test\n    public void getPageOne() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(\"firstProcess\",\n                \"1.0\");\n        processBuilder.addParameter(\"bee\", String.class.getCanonicalName())\n                .addParameter(\"bear\", String.class.getCanonicalName())\n                .addParameter(\"squirrel\", String.class.getCanonicalName())\n                .addParameter(\"donkey\", String.class.getCanonicalName())\n                .addUserTask(\"userTask1\", null);\n        final DesignProcessDefinition processDefinition = processBuilder.done();\n        final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive();\n        businessArchive.setProcessDefinition(processDefinition);\n        final Map<String, String> params = new HashMap<>();\n        params.put(\"donkey\", \"engine\");\n        params.put(\"bear\", \"bos\");\n        params.put(\"squirrel\", \"bee\");\n        params.put(\"bee\", \"busy\");\n        businessArchive.setParameters(params);\n\n        final ProcessDefinition definition = getProcessAPI().deploy(businessArchive.done());\n        final List<ParameterInstance> parameters = getProcessAPI().getParameterInstances(definition.getId(), 0, 2,\n                ParameterCriterion.NAME_DESC);\n        assertEquals(2, parameters.size());\n        assertEquals(\"squirrel\", parameters.get(0).getName());\n        assertEquals(\"donkey\", parameters.get(1).getName());\n\n        deleteProcess(definition);\n    }\n\n    @Test\n    public void getPageTwo() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(\"firstProcess\",\n                \"1.0\");\n        processBuilder.addParameter(\"bee\", String.class.getCanonicalName())\n                .addParameter(\"bear\", String.class.getCanonicalName())\n                .addParameter(\"squirrel\", String.class.getCanonicalName())\n                .addParameter(\"donkey\", String.class.getCanonicalName())\n                .addUserTask(\"userTask1\", null);\n        final DesignProcessDefinition processDefinition = processBuilder.done();\n        final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive();\n        businessArchive.setProcessDefinition(processDefinition);\n        final Map<String, String> params = new HashMap<>();\n        params.put(\"donkey\", \"engine\");\n        params.put(\"bear\", \"bos\");\n        params.put(\"squirrel\", \"bee\");\n        params.put(\"bee\", \"busy\");\n        businessArchive.setParameters(params);\n\n        final ProcessDefinition definition = getProcessAPI().deploy(businessArchive.done());\n        final List<ParameterInstance> parameters = getProcessAPI().getParameterInstances(definition.getId(), 2, 2,\n                ParameterCriterion.NAME_DESC);\n        assertEquals(2, parameters.size());\n        assertEquals(\"bee\", parameters.get(0).getName());\n        assertEquals(\"bear\", parameters.get(1).getName());\n\n        deleteProcess(definition);\n    }\n\n    @Test\n    public void getPageTwoOutOfBound() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(\"firstProcess\",\n                \"1.0\");\n        processBuilder.addParameter(\"bee\", String.class.getCanonicalName())\n                .addParameter(\"bear\", String.class.getCanonicalName())\n                .addParameter(\"squirrel\", String.class.getCanonicalName())\n                .addParameter(\"donkey\", String.class.getCanonicalName())\n                .addUserTask(\"userTask1\", null);\n        final DesignProcessDefinition processDefinition = processBuilder.done();\n        final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive();\n        businessArchive.setProcessDefinition(processDefinition);\n        final Map<String, String> params = new HashMap<>();\n        params.put(\"donkey\", \"engine\");\n        params.put(\"bear\", \"bos\");\n        params.put(\"squirrel\", \"bee\");\n        params.put(\"bee\", \"busy\");\n        businessArchive.setParameters(params);\n\n        final ProcessDefinition definition = getProcessAPI().deploy(businessArchive.done());\n        final List<ParameterInstance> parameterInstances = getProcessAPI().getParameterInstances(definition.getId(), 8,\n                8, ParameterCriterion.NAME_ASC);\n        assertEquals(0, parameterInstances.size());\n        deleteProcess(definition);\n    }\n\n    @Test\n    public void emptyParameterIsAValidValue() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"emptyParameterIsAValidValue\", \"1.7\");\n        processBuilder.addParameter(\"Astronaut\", String.class.getCanonicalName()).addUserTask(\"userTask1\", null);\n        final DesignProcessDefinition processDefinition = processBuilder.done();\n        final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive();\n        businessArchive.setProcessDefinition(processDefinition);\n        final Map<String, String> params = new HashMap<>(1);\n        params.put(\"Astronaut\", \"\");\n        businessArchive.setParameters(params);\n\n        final ProcessDefinition definition = getProcessAPI().deploy(businessArchive.done());\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(definition.getId());\n        assertEquals(ConfigurationState.RESOLVED, processDeploymentInfo.getConfigurationState());\n\n        deleteProcess(definition);\n    }\n\n    @Test\n    public void resolvedDependencies() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(\"firstProcess\",\n                \"1.0\");\n        processBuilder.addParameter(\"bee\", String.class.getCanonicalName())\n                .addParameter(\"bear\", String.class.getCanonicalName())\n                .addParameter(\"squirrel\", String.class.getCanonicalName())\n                .addParameter(\"donkey\", String.class.getCanonicalName())\n                .addUserTask(\"userTask1\", null);\n        final DesignProcessDefinition processDefinition = processBuilder.done();\n        final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive();\n        businessArchive.setProcessDefinition(processDefinition);\n        final Map<String, String> params = new HashMap<>();\n        params.put(\"donkey\", \"engine\");\n        params.put(\"bear\", \"bos\");\n        params.put(\"squirrel\", \"bee\");\n        params.put(\"bee\", \"busy\");\n        businessArchive.setParameters(params);\n\n        final ProcessDefinition definition = getProcessAPI().deploy(businessArchive.done());\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(definition.getId());\n        assertEquals(ConfigurationState.RESOLVED, processDeploymentInfo.getConfigurationState());\n\n        deleteProcess(definition);\n    }\n\n    @Test\n    public void showResolvedAndUnresolvedParameters() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(\"firstProcess\",\n                \"1.0\");\n        processBuilder.addParameter(\"bee\", String.class.getCanonicalName()).addDescription(\"description\")\n                .addParameter(\"bear\", String.class.getCanonicalName())\n                .addUserTask(\"userTask1\", null);\n        final DesignProcessDefinition processDefinition = processBuilder.done();\n        final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive();\n        businessArchive.setProcessDefinition(processDefinition);\n        final Map<String, String> params = new HashMap<>();\n        params.put(\"bee\", \"busy\");\n        businessArchive.setParameters(params);\n\n        final ProcessDefinition definition = getProcessAPI().deploy(businessArchive.done());\n\n        final List<ParameterInstance> parameters = getProcessAPI().getParameterInstances(definition.getId(), 0, 20,\n                ParameterCriterion.NAME_DESC);\n        assertEquals(2, parameters.size());\n        final ParameterInstance parameter1 = parameters.get(0);\n        assertEquals(\"bee\", parameter1.getName());\n        assertEquals(\"busy\", parameter1.getValue());\n        assertEquals(\"description\", parameter1.getDescription());\n        assertEquals(String.class.getCanonicalName(), parameter1.getType());\n        final ParameterInstance parameter2 = parameters.get(1);\n        assertEquals(\"bear\", parameter2.getName());\n        assertNull(parameter2.getValue());\n        assertNull(parameter2.getDescription());\n        assertEquals(String.class.getCanonicalName(), parameter2.getType());\n        deleteProcess(definition);\n    }\n\n    @Test\n    public void testParametersAreWellTyped() throws Exception {\n        final String actor = \"acting\";\n        final User jack = createUserAndLogin(\"jack\", \"leaking_caldron\");\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(\n                \"testCantResolveDataInExpressionInDataDefaultValue\", \"1\");\n        processBuilder.addActor(actor).addDescription(\"Process to test archiving mechanism\");\n        processBuilder.addDoubleData(\"aData\", null);\n        processBuilder.addParameter(\"integerValue\", String.class.getName());\n        processBuilder.addParameter(\"booleanValue\", String.class.getName());\n        processBuilder.addParameter(\"doubleValue\", String.class.getName());\n        processBuilder.addUserTask(\"humanTask\", actor);\n\n        final Map<String, String> parameters = new HashMap<>();\n        parameters.put(\"integerValue\", \"15\");\n        parameters.put(\"booleanValue\", \"true\");\n        parameters.put(\"doubleValue\", \"1.1\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActorAndParameters(processBuilder.done(),\n                actor, jack, parameters);\n\n        final Expression integerParameter = new ExpressionBuilder().createParameterExpression(\"integerExpression\",\n                \"integerValue\", Integer.class.getName());\n        final Expression isIntegerValueInteger = new ExpressionBuilder().createGroovyScriptExpression(\n                \"testIntegerValueToBeInteger\",\n                \"integerValue instanceof Integer\", Boolean.class.getName(), integerParameter);\n\n        final Expression booleanParameter = new ExpressionBuilder().createParameterExpression(\"booleanParameter\",\n                \"booleanValue\", Boolean.class.getName());\n        final Expression isBooleanValueBoolean = new ExpressionBuilder().createGroovyScriptExpression(\n                \"testBooleanValueToBeBoolean\",\n                \"booleanValue instanceof Boolean\", Boolean.class.getName(), booleanParameter);\n\n        final Expression doubleParameter = new ExpressionBuilder().createParameterExpression(\"doubleExpression\",\n                \"doubleValue\", Double.class.getName());\n        final Expression isDoubleValueDouble = new ExpressionBuilder().createGroovyScriptExpression(\n                \"testDoubleValueToBeDouble\",\n                \"doubleValue instanceof Double\", Boolean.class.getName(), doubleParameter);\n\n        final Map<String, Serializable> inputValues = new HashMap<>(0);\n        assertThat((Boolean) getProcessAPI().evaluateExpressionOnProcessDefinition(isIntegerValueInteger, inputValues,\n                processDefinition.getId()), is(true));\n        assertThat((Boolean) getProcessAPI().evaluateExpressionOnProcessDefinition(isBooleanValueBoolean, inputValues,\n                processDefinition.getId()), is(true));\n        assertThat((Boolean) getProcessAPI().evaluateExpressionOnProcessDefinition(isDoubleValueDouble, inputValues,\n                processDefinition.getId()), is(true));\n\n        disableAndDeleteProcess(processDefinition);\n        deleteUser(jack);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/ProcessResolutionIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.process;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.TestWithTechnicalUser;\nimport org.bonitasoft.engine.bpm.actor.ActorCriterion;\nimport org.bonitasoft.engine.bpm.actor.ActorInstance;\nimport org.bonitasoft.engine.bpm.actor.ActorMember;\nimport org.bonitasoft.engine.bpm.bar.BarResource;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.bpm.process.ConfigurationState;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.Problem;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.identity.Group;\nimport org.bonitasoft.engine.identity.Role;\nimport org.bonitasoft.engine.identity.User;\nimport org.junit.Assert;\nimport org.junit.Test;\n\npublic class ProcessResolutionIT extends TestWithTechnicalUser {\n\n    @Test(expected = ProcessDefinitionNotFoundException.class)\n    public void processNotFoundWhenGettingResolutionProblems() throws BonitaException {\n        getProcessAPI().getProcessResolutionProblems(-458);\n    }\n\n    @Test\n    public void noActorMapping() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder();\n        builder.createNewInstance(\"resolve\", \"1.0\").addActor(\"Leader\").addUserTask(\"step1\", \"Leader\");\n        final DesignProcessDefinition processDefinition = builder.done();\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(processDefinition).done();\n        final ProcessDefinition definition = deployProcess(businessArchive);\n        final ProcessDeploymentInfo deploymentInfo = getProcessAPI().getProcessDeploymentInfo(definition.getId());\n        Assert.assertEquals(ConfigurationState.UNRESOLVED, deploymentInfo.getConfigurationState());\n\n        final List<Problem> problems = getProcessAPI().getProcessResolutionProblems(definition.getId());\n        Assert.assertEquals(1, problems.size());\n        final Problem problem = problems.get(0);\n        Assert.assertEquals(Problem.Level.ERROR, problem.getLevel());\n        Assert.assertEquals(\"actor\", problem.getResource());\n        Assert.assertNotNull(problem.getDescription());\n\n        deleteProcess(definition.getId());\n    }\n\n    @Test\n    public void resolveActorMapping() throws Exception, InterruptedException {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder();\n        builder.createNewInstance(\"resolve\", \"1.0\").addActor(\"Leader\", true).addUserTask(\"step1\", \"Leader\");\n        final DesignProcessDefinition processDefinition = builder.done();\n        final BusinessArchiveBuilder archiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive();\n        archiveBuilder.addClasspathResource(new BarResource(\"aDependency\", new byte[] { 1, 5, 2, 3, 6, 4, 6, 8 }));\n        final BusinessArchive businessArchive = archiveBuilder.setProcessDefinition(processDefinition).done();\n        final ProcessDefinition definition = deployProcess(businessArchive);\n\n        ProcessDeploymentInfo deploymentInfo = getProcessAPI().getProcessDeploymentInfo(definition.getId());\n        long previousUpdate = deploymentInfo.getLastUpdateDate().getTime();\n        long lastUpdate = previousUpdate;\n        Assert.assertEquals(ConfigurationState.UNRESOLVED, deploymentInfo.getConfigurationState());\n\n        // add actor mapping on user\n        Thread.sleep(10);\n        final ActorInstance initiator = getProcessAPI().getActorInitiator(definition.getId());\n        getProcessAPI().addUserToActor(initiator.getId(), getSession().getUserId());\n\n        // check state is resolved\n        deploymentInfo = getProcessAPI().getProcessDeploymentInfo(definition.getId());\n        Assert.assertEquals(ConfigurationState.RESOLVED, deploymentInfo.getConfigurationState());\n        lastUpdate = deploymentInfo.getLastUpdateDate().getTime();\n        assertTrue(\"lastupdate date not changed\", lastUpdate > previousUpdate);\n        previousUpdate = lastUpdate;\n        List<Problem> problems = getProcessAPI().getProcessResolutionProblems(definition.getId());\n        Assert.assertEquals(0, problems.size());\n\n        Thread.sleep(10);\n        // remove actor member\n        final ActorInstance actorInstance = getProcessAPI()\n                .getActors(definition.getId(), 0, 10, ActorCriterion.NAME_ASC).get(0);\n        final ActorMember actorMember = getProcessAPI().getActorMembers(actorInstance.getId(), 0, 10).get(0);\n        getProcessAPI().removeActorMember(actorMember.getId());\n\n        // check unresolved\n        deploymentInfo = getProcessAPI().getProcessDeploymentInfo(definition.getId());\n        Assert.assertEquals(ConfigurationState.UNRESOLVED, deploymentInfo.getConfigurationState());\n        lastUpdate = deploymentInfo.getLastUpdateDate().getTime();\n        assertTrue(\"lastupdate date not changed\", lastUpdate > previousUpdate);\n        previousUpdate = lastUpdate;\n\n        Thread.sleep(10);\n        // add user again to actor and check resolved again\n        getProcessAPI().addUserToActor(actorInstance.getId(), getSession().getUserId());\n        deploymentInfo = getProcessAPI().getProcessDeploymentInfo(definition.getId());\n        Assert.assertEquals(ConfigurationState.RESOLVED, deploymentInfo.getConfigurationState());\n        lastUpdate = deploymentInfo.getLastUpdateDate().getTime();\n        assertTrue(\"lastupdate date not changed\", lastUpdate > previousUpdate);\n        previousUpdate = lastUpdate;\n        problems = getProcessAPI().getProcessResolutionProblems(definition.getId());\n        Assert.assertEquals(0, problems.size());\n\n        deleteProcess(definition.getId());\n    }\n\n    @Test\n    public void deploy2ProcessWithSameDependency() throws Exception {\n        ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder();\n        builder.createNewInstance(\"process\", \"1.0\").addAutomaticTask(\"step1\");\n        final DesignProcessDefinition processDefinition1 = builder.done();\n        final BusinessArchiveBuilder archiveBuilder1 = new BusinessArchiveBuilder().createNewBusinessArchive();\n        final BarResource resource = new BarResource(\"aDependency\", new byte[] { 1, 5, 2, 3, 6, 4, 6, 8 });\n        archiveBuilder1.addClasspathResource(resource);\n        final BusinessArchive businessArchive1 = archiveBuilder1.setProcessDefinition(processDefinition1).done();\n        final ProcessDefinition definition1 = getProcessAPI().deploy(businessArchive1);\n\n        builder = new ProcessDefinitionBuilder();\n        builder.createNewInstance(\"processbis\", \"1.0\").addAutomaticTask(\"step1\");\n        final DesignProcessDefinition processDefinition2 = builder.done();\n        final BusinessArchiveBuilder archiveBuilder2 = new BusinessArchiveBuilder().createNewBusinessArchive();\n        archiveBuilder2.addClasspathResource(resource);\n        final BusinessArchive businessArchive2 = archiveBuilder2.setProcessDefinition(processDefinition2).done();\n        final ProcessDefinition definition2 = getProcessAPI().deploy(businessArchive2);\n        deleteProcess(definition1.getId());\n        deleteProcess(definition2.getId());\n    }\n\n    @Test\n    public void removeLastUserFromActorUnresolvesProcess() throws Exception {\n        final User piouPiou = getIdentityAPI().createUser(\"Piou-piou\", \"s3cR3t\");\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder();\n        final String actor = \"Dev Leader\";\n        builder.createNewInstance(\"update proc resolution\", \"1.0\").addActor(actor, true).addUserTask(\"test_session\",\n                actor);\n        final DesignProcessDefinition processDefinition = builder.done();\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(processDefinition).done();\n        final ProcessDefinition definition = deployProcess(businessArchive);\n        final ActorInstance initiator = getProcessAPI().getActorInitiator(definition.getId());\n        getProcessAPI().addUserToActor(initiator.getId(), piouPiou.getId());\n        ProcessDeploymentInfo processDeploymentInfo = getProcessAPI().getProcessDeploymentInfo(definition.getId());\n        assertEquals(ConfigurationState.RESOLVED, processDeploymentInfo.getConfigurationState());\n\n        final List<ActorMember> actorMembers = getProcessAPI().getActorMembers(initiator.getId(), 0, 10);\n        getProcessAPI().removeActorMember(actorMembers.get(0).getId());\n        // reload the process deploy info:\n        processDeploymentInfo = getProcessAPI().getProcessDeploymentInfo(definition.getId());\n        assertEquals(ConfigurationState.UNRESOLVED, processDeploymentInfo.getConfigurationState());\n\n        deleteProcess(definition.getId());\n        deleteUser(piouPiou);\n    }\n\n    @Test\n    public void deleteUserFromActorUnresolvesProcess() throws Exception {\n        final User piouPiou = getIdentityAPI().createUser(\"Piou-piou\", \"s3cR3t\");\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder();\n        final String actor = \"Dev Leader\";\n        builder.createNewInstance(\"update proc resolution\", \"1.1\").addActor(actor, true).addUserTask(\"deleteUser\",\n                actor);\n        final DesignProcessDefinition processDefinition = builder.done();\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(processDefinition).done();\n        final ProcessDefinition definition = deployProcess(businessArchive);\n        final ActorInstance initiator = getProcessAPI().getActorInitiator(definition.getId());\n        getProcessAPI().addUserToActor(initiator.getId(), piouPiou.getId());\n        ProcessDeploymentInfo processDeploymentInfo = getProcessAPI().getProcessDeploymentInfo(definition.getId());\n        assertEquals(ConfigurationState.RESOLVED, processDeploymentInfo.getConfigurationState());\n\n        getIdentityAPI().deleteUser(piouPiou.getId());\n        // reload the process deploy info:\n        processDeploymentInfo = getProcessAPI().getProcessDeploymentInfo(definition.getId());\n        assertEquals(ConfigurationState.UNRESOLVED, processDeploymentInfo.getConfigurationState());\n\n        deleteProcess(definition.getId());\n    }\n\n    @Test\n    public void deleteRoleFromActorUnresolvesProcess() throws Exception {\n        final Role role = getIdentityAPI().createRole(\"Tester\");\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder();\n        final String actor = \"Dev Leader\";\n        builder.createNewInstance(\"update proc resolution\", \"1.2\").addActor(actor, true).addUserTask(\"deleteRole\",\n                actor);\n        final DesignProcessDefinition processDefinition = builder.done();\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(processDefinition).done();\n        final ProcessDefinition definition = deployProcess(businessArchive);\n        final ActorInstance initiator = getProcessAPI().getActorInitiator(definition.getId());\n        getProcessAPI().addRoleToActor(initiator.getId(), role.getId());\n        ProcessDeploymentInfo processDeploymentInfo = getProcessAPI().getProcessDeploymentInfo(definition.getId());\n        assertEquals(ConfigurationState.RESOLVED, processDeploymentInfo.getConfigurationState());\n\n        getIdentityAPI().deleteRole(role.getId());\n        // reload the process deploy info:\n        processDeploymentInfo = getProcessAPI().getProcessDeploymentInfo(definition.getId());\n        assertEquals(ConfigurationState.UNRESOLVED, processDeploymentInfo.getConfigurationState());\n\n        deleteProcess(definition.getId());\n    }\n\n    @Test\n    public void deleteGroupFromActorUnresolvesProcess() throws Exception {\n        final Group group = getIdentityAPI().createGroup(\"Tester\", null);\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder();\n        final String actor = \"Dev Leader\";\n        builder.createNewInstance(\"update proc resolution\", \"1.2\").addActor(actor, true).addUserTask(\"deleteGroup\",\n                actor);\n        final DesignProcessDefinition processDefinition = builder.done();\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(processDefinition).done();\n        final ProcessDefinition definition = deployProcess(businessArchive);\n        final ActorInstance initiator = getProcessAPI().getActorInitiator(definition.getId());\n        getProcessAPI().addGroupToActor(initiator.getId(), group.getId());\n        ProcessDeploymentInfo processDeploymentInfo = getProcessAPI().getProcessDeploymentInfo(definition.getId());\n        assertEquals(ConfigurationState.RESOLVED, processDeploymentInfo.getConfigurationState());\n\n        getIdentityAPI().deleteGroup(group.getId());\n        // reload the process deploy info:\n        processDeploymentInfo = getProcessAPI().getProcessDeploymentInfo(definition.getId());\n        assertEquals(ConfigurationState.UNRESOLVED, processDeploymentInfo.getConfigurationState());\n\n        deleteProcess(definition.getId());\n    }\n\n    @Test\n    public void noConnectorImplementation() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder();\n        builder.createNewInstance(\"resolve\", \"1.0\").addAutomaticTask(\"auto\").addConnector(\"exec\", \"exec-1.0\", \"1.0\",\n                ConnectorEvent.ON_ENTER);\n        final DesignProcessDefinition processDefinition = builder.done();\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(processDefinition).done();\n        final ProcessDefinition definition = deployProcess(businessArchive);\n        final ProcessDeploymentInfo deploymentInfo = getProcessAPI().getProcessDeploymentInfo(definition.getId());\n        Assert.assertEquals(ConfigurationState.UNRESOLVED, deploymentInfo.getConfigurationState());\n\n        final List<Problem> problems = getProcessAPI().getProcessResolutionProblems(definition.getId());\n        Assert.assertEquals(1, problems.size());\n        final Problem problem = problems.get(0);\n        Assert.assertEquals(Problem.Level.ERROR, problem.getLevel());\n        Assert.assertEquals(\"connector\", problem.getResource());\n        Assert.assertNotNull(problem.getDescription());\n\n        deleteProcess(definition.getId());\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/ProcessWithExpressionIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.process;\n\nimport static org.junit.Assert.*;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.Serializable;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.commons.io.IOUtils;\nimport org.bonitasoft.engine.CommonAPIIT;\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.bar.BarResource;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.data.DataInstance;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.expression.ComparisonOperator;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.expression.ExpressionConstants;\nimport org.bonitasoft.engine.expression.ExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.XPathReturnType;\nimport org.bonitasoft.engine.operation.LeftOperandBuilder;\nimport org.bonitasoft.engine.operation.OperatorType;\nimport org.junit.Test;\n\n/**\n * @author Baptiste Mesta\n */\npublic class ProcessWithExpressionIT extends TestWithUser {\n\n    @Test\n    public void executeProcessWithListExpression() throws Exception {\n        final Serializable result = executeProcessAndGetResultOfExpression(\n                new ExpressionBuilder().createListExpression(\"list1\",\n                        Collections.singletonList(new ExpressionBuilder().createConstantStringExpression(\"test\"))),\n                List.class.getName());\n        assertTrue(result instanceof List);\n        assertEquals(1, ((List<?>) result).size());\n        assertEquals(\"test\", ((List<?>) result).get(0));\n    }\n\n    @Test\n    public void executeProcessWithListOfListOfExpression() throws Exception {\n        final Serializable result = executeProcessAndGetResultOfExpression(\n                new ExpressionBuilder().createListOfListExpression(\"myMap\",\n                        Collections.singletonList(Collections\n                                .singletonList(new ExpressionBuilder().createConstantStringExpression(\"test\")))),\n                \"java.util.List\");\n        assertTrue(result instanceof List);\n        assertEquals(1, ((List<?>) result).size());\n        assertEquals(\"test\", ((List<?>) ((List<?>) result).get(0)).get(0));\n    }\n\n    @Test\n    public void executeProcessWithScriptUsingApi() throws Exception {\n        final Serializable result = executeProcessAndGetResultOfExpression(\n                new ExpressionBuilder().createGroovyScriptExpression(\"scriptAPI\",\n                        \"apiAccessor.getProcessAPI().getProcessDefinition(processDefinitionId) != null ?Boolean.TRUE:Boolean.false\",\n                        Boolean.class.getName(),\n                        new ExpressionBuilder().createEngineConstant(ExpressionConstants.API_ACCESSOR),\n                        new ExpressionBuilder().createEngineConstant(ExpressionConstants.PROCESS_DEFINITION_ID)),\n                Boolean.class.getName());\n\n        assertEquals(Boolean.TRUE, result);\n    }\n\n    private Serializable executeProcessAndGetResultOfExpression(final Expression expression, final String dataType)\n            throws Exception {\n        return executeProcessAndGetResultOfExpression(expression, dataType, null, null, null);\n    }\n\n    private Serializable executeProcessAndGetResultOfExpression(final Expression expression, final String dataType,\n            final String extraDataName,\n            final String extraDataType, final Expression extraDataValue) throws Exception {\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithExpression\", \"1.0\");\n        designProcessDefinition.addActor(ACTOR_NAME).addDescription(\"Delivery all day and night long\");\n        designProcessDefinition.addData(\"data1\", dataType, null);\n        if (extraDataName != null) {\n            designProcessDefinition.addData(extraDataName, extraDataType, extraDataValue);\n        }\n        designProcessDefinition.addAutomaticTask(\"step1\").addOperation(\n                new LeftOperandBuilder().createNewInstance(\"data1\").done(), OperatorType.ASSIGNMENT,\n                \"=\", null, expression);\n        designProcessDefinition.addUserTask(\"step2\", ACTOR_NAME);\n        designProcessDefinition.addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(),\n                ACTOR_NAME, user);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance, \"step2\");\n\n        final DataInstance processDataInstance = getProcessAPI().getProcessDataInstance(\"data1\",\n                processInstance.getId());\n        final Serializable value = processDataInstance.getValue();\n        disableAndDeleteProcess(processDefinition);\n        return value;\n    }\n\n    @Test\n    public void evaluateGroovyScriptWithConnectorHavingDependencies() throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder();\n        final ProcessDefinitionBuilder pBuilder = processDefinitionBuilder.createNewInstance(\"emptyProcess\",\n                String.valueOf(System.currentTimeMillis()));\n        pBuilder.addActor(ACTOR_NAME)\n                .addUserTask(\"step1\", ACTOR_NAME)\n                .addDisplayName(\n                        new ExpressionBuilder().createGroovyScriptExpression(\"myScript\",\n                                \"new org.bonitasoft.engine.test.TheClassOfMyLibrary().aPublicMethod()\",\n                                String.class.getName()));\n        final DesignProcessDefinition done = pBuilder.done();\n        final BusinessArchiveBuilder builder = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(done);\n        final InputStream stream = CommonAPIIT.class.getResourceAsStream(\"/mylibrary-jar.bak\");\n        assertNotNull(stream);\n        final byte[] byteArray = IOUtils.toByteArray(stream);\n        stream.close();\n        builder.addClasspathResource(new BarResource(\"mylibrary.jar\", byteArray));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user);\n\n        try {\n            final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n            final ActivityInstance task = waitForUserTaskAndGetIt(processInstance, \"step1\");\n            assertEquals(\"stringFromPublicMethod\", task.getDisplayName());\n        } finally {\n            disableAndDeleteProcess(processDefinition);\n        }\n    }\n\n    @Test\n    public void evaluateGroovyScriptWithDifferentClassloader() throws Exception {\n        final BarResource myLibrary1 = getMyLibrary();\n        final BarResource myLibrary2 = getMyLibrary2();\n        final String scriptLib1 = \"new org.bonitasoft.engine.test.TheClassOfMyLibrary().aPublicMethod()\";\n        final String scriptLib2 = \"new org.bonitasoft.engine.test2.TheClassOfMyLibrary2().aPublicMethod2()\";\n        final ProcessDefinition p11 = deployProcessWithScriptAndLibrary(myLibrary1, scriptLib1);\n        final ProcessDefinition p22 = deployProcessWithScriptAndLibrary(myLibrary2, scriptLib2);\n        final ProcessDefinition p12 = deployProcessWithScriptAndLibrary(myLibrary1, scriptLib2);\n        try {\n            final ProcessInstance pi11 = getProcessAPI().startProcess(p11.getId());\n            final ProcessInstance pi22 = getProcessAPI().startProcess(p22.getId());\n            final ProcessInstance pi12 = getProcessAPI().startProcess(p12.getId());\n            final ActivityInstance task1 = waitForUserTaskAndGetIt(pi11, \"step1\");\n            assertEquals(\"stringFromPublicMethod\", task1.getDisplayName());\n            final ActivityInstance task2 = waitForUserTaskAndGetIt(pi22, \"step1\");\n            assertEquals(\"stringFromPublicMethod2\", task2.getDisplayName());\n            final ActivityInstance task3 = waitForTaskToFail(pi12);\n            assertEquals(\"step1\", task3.getName());\n            final ProcessInstance pi11bis = getProcessAPI().startProcess(p11.getId());\n            final ActivityInstance task1bis = waitForUserTaskAndGetIt(pi11bis, \"step1\");\n            assertEquals(\"stringFromPublicMethod\", task1bis.getDisplayName());\n        } finally {\n            disableAndDeleteProcess(p11);\n            disableAndDeleteProcess(p22);\n            disableAndDeleteProcess(p12);\n        }\n    }\n\n    private ProcessDefinition deployProcessWithScriptAndLibrary(final BarResource myLibrary, final String script)\n            throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder();\n        final ProcessDefinitionBuilder pBuilder = processDefinitionBuilder.createNewInstance(\"emptyProcess\",\n                String.valueOf(System.currentTimeMillis()));\n        pBuilder.addActor(ACTOR_NAME).addUserTask(\"step1\", ACTOR_NAME)\n                .addDisplayName(new ExpressionBuilder().createGroovyScriptExpression(\"myScript\", script,\n                        String.class.getName()));\n        final DesignProcessDefinition done = pBuilder.done();\n        final BusinessArchiveBuilder builder = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(done);\n        builder.addClasspathResource(myLibrary);\n        return deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user);\n    }\n\n    private BarResource getMyLibrary() throws IOException {\n        final InputStream stream = CommonAPIIT.class.getResourceAsStream(\"/mylibrary-jar.bak\");\n        final byte[] byteArray = IOUtils.toByteArray(stream);\n        stream.close();\n        return new BarResource(\"mylibrary.jar\", byteArray);\n    }\n\n    private BarResource getMyLibrary2() throws IOException {\n        final InputStream stream = CommonAPIIT.class.getResourceAsStream(\"/mylibrary2-jar.bak\");\n        final byte[] byteArray = IOUtils.toByteArray(stream);\n        stream.close();\n        return new BarResource(\"mylibrary.jar\", byteArray);\n    }\n\n    @Test\n    public void evaluateConstantExpressionFromApi() throws Exception {\n        final ProcessDefinition processDefinition = deployEmptyProcess();\n        Expression expression = new ExpressionBuilder().createConstantBooleanExpression(true);\n        final Map<String, Serializable> inputValues = new HashMap<>(0);\n        assertEquals(true, getProcessAPI().evaluateExpressionOnProcessDefinition(expression, inputValues,\n                processDefinition.getId()));\n\n        expression = new ExpressionBuilder().createConstantStringExpression(\"test\");\n        assertEquals(\"test\", getProcessAPI().evaluateExpressionOnProcessDefinition(expression, inputValues,\n                processDefinition.getId()));\n\n        expression = new ExpressionBuilder().createConstantLongExpression(123456L);\n        assertEquals(123456L, getProcessAPI().evaluateExpressionOnProcessDefinition(expression, inputValues,\n                processDefinition.getId()));\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    private ProcessDefinition deployEmptyProcess() throws Exception {\n        final DesignProcessDefinition done = new ProcessDefinitionBuilder()\n                .createNewInstance(\"emptyProcess\", String.valueOf(System.currentTimeMillis()))\n                .done();\n        return deployAndEnableProcess(done);\n    }\n\n    @Test\n    public void evaluateInputExpressionFromApi() throws Exception {\n        final ProcessDefinition processDefinition = deployEmptyProcess();\n        final Expression expression = new ExpressionBuilder().createInputExpression(\"test\", Boolean.class.getName());\n        final Map<String, Serializable> inputValues = new HashMap<>();\n        inputValues.put(\"test\", true);\n        assertEquals(true, getProcessAPI().evaluateExpressionOnProcessDefinition(expression, inputValues,\n                processDefinition.getId()));\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void evaluateGroovyExpressionFromApi() throws Exception {\n        final ProcessDefinition processDefinition = deployEmptyProcess();\n        final Expression expression = new ExpressionBuilder().createGroovyScriptExpression(\n                \"evaluateGroovyExpressionFromApi\", \"input1 + 12\",\n                Integer.class.getName(),\n                new ExpressionBuilder().createInputExpression(\"input1\", Integer.class.getName()));\n        final Map<String, Serializable> inputValues = new HashMap<>(1);\n        inputValues.put(\"input1\", 8);\n        assertEquals(20, getProcessAPI().evaluateExpressionOnProcessDefinition(expression, inputValues,\n                processDefinition.getId()));\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void evaluateGroovyWithDataProvidedExpressionFromApi() throws Exception {\n        final ProcessDefinition processDefinition = deployEmptyProcess();\n        final Expression expression = new ExpressionBuilder().createGroovyScriptExpression(\n                \"evaluateGroovyWithDataProvidedExpressionFromApi\",\n                \"input1 + data1 + 12\", Integer.class.getName(),\n                new ExpressionBuilder().createInputExpression(\"input1\", Integer.class.getName()),\n                new ExpressionBuilder().createDataExpression(\"data1\", Integer.class.getName()));\n        final Map<String, Serializable> inputValues = new HashMap<>(2);\n        inputValues.put(\"input1\", 6);\n        inputValues.put(\"data1\", 2);\n        assertEquals(20, getProcessAPI().evaluateExpressionOnProcessDefinition(expression, inputValues,\n                processDefinition.getId()));\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Test\n    public void evaluateEmbeddedListExpressions() throws Exception {\n        final ProcessDefinition processDefinition = deployEmptyProcess();\n        final Expression constantExpr = new ExpressionBuilder().createConstantStringExpression(\"DUMMY\");\n        final String data1Content = \"data1_name\";\n        final Expression data1Expr = new ExpressionBuilder().createDataExpression(data1Content, String.class.getName());\n        final Expression listExpression1 = new ExpressionBuilder().createListExpression(\"manuList1\",\n                Arrays.asList(constantExpr, data1Expr));\n        final Expression felixConstExp = new ExpressionBuilder().createConstantStringExpression(\"FELIX\");\n        final Expression listExpression2 = new ExpressionBuilder().createListExpression(\"manuList2\",\n                Arrays.asList(listExpression1, felixConstExp));\n\n        final Map<String, Serializable> inputValues = new HashMap<>(1);\n        inputValues.put(data1Content, \"dataValue\");\n        final List<Serializable> result = (List<Serializable>) getProcessAPI().evaluateExpressionOnProcessDefinition(\n                listExpression2, inputValues,\n                processDefinition.getId());\n        assertEquals(2, result.size());\n        assertEquals(2, ((List<Serializable>) result.get(0)).size());\n        assertEquals(\"DUMMY\", ((List<Serializable>) result.get(0)).get(0));\n        assertEquals(\"dataValue\", ((List<Serializable>) result.get(0)).get(1));\n        assertEquals(\"FELIX\", result.get(1));\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void evaluateListExpression() throws Exception {\n        final ProcessDefinition processDefinition = deployEmptyProcess();\n        final Expression groovyExpr = new ExpressionBuilder()\n                .createGroovyScriptExpression(\"evaluateListExpression\", \"1+'_'+data1_name\", String.class.getName());\n        final Expression constantExpr = new ExpressionBuilder().createConstantStringExpression(\"Newbee\");\n        final String data1Content = \"data1_name\";\n        final Expression data1Expr = new ExpressionBuilder().createDataExpression(data1Content, String.class.getName());\n\n        final Expression listExpression = new ExpressionBuilder().createListExpression(\"ManuList1\",\n                Arrays.asList(groovyExpr, constantExpr, data1Expr));\n\n        final Map<String, Serializable> inputValues = new HashMap<>(1);\n        inputValues.put(data1Content, \"EXPRESSION\");\n        @SuppressWarnings(\"unchecked\")\n        final List<Serializable> result = (List<Serializable>) getProcessAPI().evaluateExpressionOnProcessDefinition(\n                listExpression, inputValues,\n                processDefinition.getId());\n        assertEquals(3, result.size());\n        assertEquals(\"1_EXPRESSION\", result.get(0));\n        assertEquals(\"Newbee\", result.get(1));\n        assertEquals(\"EXPRESSION\", result.get(2));\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test(expected = ExpressionEvaluationException.class)\n    public void evaluateDataExpressionFromApiOnUnknownData() throws Exception {\n        final ProcessDefinition processDefinition = deployEmptyProcess();\n        final Expression expression = new ExpressionBuilder().createDataExpression(\"data\", Boolean.class.getName());\n        final Map<String, Serializable> inputValues = new HashMap<>(0);\n        try {\n            getProcessAPI().evaluateExpressionOnProcessDefinition(expression, inputValues, processDefinition.getId());\n        } finally {\n            disableAndDeleteProcess(processDefinition);\n        }\n    }\n\n    @Test\n    public void evaluateGreaterThanComparationExpression() throws Exception {\n        final Expression dependExpr1 = new ExpressionBuilder().createConstantDoubleExpression(5.1);\n        final Expression dependExpr2 = new ExpressionBuilder().createConstantIntegerExpression(6);\n        final Expression expression1 = new ExpressionBuilder().createComparisonExpression(\"comp1\", dependExpr1,\n                ComparisonOperator.GREATER_THAN, dependExpr2);\n        Serializable result = executeProcessAndGetResultOfExpression(expression1, Boolean.class.getName());\n        assertTrue(result instanceof Boolean);\n        assertFalse((Boolean) result);\n\n        final Expression expression2 = new ExpressionBuilder().createComparisonExpression(\"comp1\", dependExpr2,\n                ComparisonOperator.GREATER_THAN, dependExpr1);\n        result = executeProcessAndGetResultOfExpression(expression2, Boolean.class.getName());\n        assertTrue(result instanceof Boolean);\n        assertTrue((Boolean) result);\n\n        final Expression expression3 = new ExpressionBuilder().createComparisonExpression(\"comp2\", dependExpr2,\n                ComparisonOperator.GREATER_THAN, dependExpr2);\n        result = executeProcessAndGetResultOfExpression(expression3, Boolean.class.getName());\n        assertTrue(result instanceof Boolean);\n        assertFalse((Boolean) result);\n    }\n\n    @Test\n    public void evaluateLogicalComplementExpression() throws Exception {\n        final Expression dependExpr1 = new ExpressionBuilder().createConstantBooleanExpression(true);\n        final Expression dependExpr2 = new ExpressionBuilder().createGroovyScriptExpression(\"toReturnFalse\", \"4 == 5\",\n                Boolean.class.getName());\n\n        final Expression expression1 = new ExpressionBuilder().createLogicalComplementExpression(\"complement1\",\n                dependExpr1);\n        Serializable result = executeProcessAndGetResultOfExpression(expression1, Boolean.class.getName());\n        assertTrue(result instanceof Boolean);\n        assertFalse((Boolean) result);\n\n        final Expression expression2 = new ExpressionBuilder().createLogicalComplementExpression(\"complement1\",\n                dependExpr2);\n        result = executeProcessAndGetResultOfExpression(expression2, Boolean.class.getName());\n        assertTrue(result instanceof Boolean);\n        assertTrue((Boolean) result);\n    }\n\n    @Test\n    public void evaluateGreaterThanOrEqualsComparisonExpression() throws Exception {\n        final Expression exprOperand1 = new ExpressionBuilder().createConstantIntegerExpression(7);\n        final Expression exprOperand2 = new ExpressionBuilder().createConstantIntegerExpression(9);\n        final Expression exprOperand3 = new ExpressionBuilder().createConstantIntegerExpression(7);\n        final Expression exprOperand4 = new ExpressionBuilder().createConstantIntegerExpression(-8);\n\n        final Expression exprResult1 = new ExpressionBuilder().createComparisonExpression(\"GreaterThanOrEquals1\",\n                exprOperand1,\n                ComparisonOperator.GREATER_THAN_OR_EQUALS, exprOperand2);\n        Serializable result = executeProcessAndGetResultOfExpression(exprResult1, Boolean.class.getName());\n        assertTrue(result instanceof Boolean);\n        assertFalse((Boolean) result);\n\n        final Expression exprResult2 = new ExpressionBuilder().createComparisonExpression(\"GreaterThanOrEquals2\",\n                exprOperand1,\n                ComparisonOperator.GREATER_THAN_OR_EQUALS, exprOperand3);\n        result = executeProcessAndGetResultOfExpression(exprResult2, Boolean.class.getName());\n        assertTrue(result instanceof Boolean);\n        assertTrue((Boolean) result);\n\n        final Expression exprResult3 = new ExpressionBuilder().createComparisonExpression(\"GreaterThanOrEquals3\",\n                exprOperand3,\n                ComparisonOperator.GREATER_THAN_OR_EQUALS, exprOperand4);\n        result = executeProcessAndGetResultOfExpression(exprResult3, Boolean.class.getName());\n        assertTrue(result instanceof Boolean);\n        assertTrue((Boolean) result);\n    }\n\n    @Test\n    public void evaluateLessThanComparisonExpression() throws Exception {\n        final Expression exprOperand1 = new ExpressionBuilder().createConstantIntegerExpression(7);\n        final Expression exprOperand2 = new ExpressionBuilder().createConstantIntegerExpression(9);\n        final Expression exprOperand3 = new ExpressionBuilder().createConstantIntegerExpression(7);\n        final Expression exprOperand4 = new ExpressionBuilder().createConstantIntegerExpression(-8);\n\n        final Expression exprResult1 = new ExpressionBuilder()\n                .createComparisonExpression(\"LessThan1\", exprOperand1, ComparisonOperator.LESS_THAN, exprOperand2);\n        Serializable result = executeProcessAndGetResultOfExpression(exprResult1, Boolean.class.getName());\n        assertTrue(result instanceof Boolean);\n        assertTrue((Boolean) result);\n\n        final Expression exprResult2 = new ExpressionBuilder()\n                .createComparisonExpression(\"LessThan2\", exprOperand1, ComparisonOperator.LESS_THAN, exprOperand3);\n        result = executeProcessAndGetResultOfExpression(exprResult2, Boolean.class.getName());\n        assertTrue(result instanceof Boolean);\n        assertFalse((Boolean) result);\n\n        final Expression exprResult3 = new ExpressionBuilder()\n                .createComparisonExpression(\"LessThan3\", exprOperand3, ComparisonOperator.LESS_THAN, exprOperand4);\n        result = executeProcessAndGetResultOfExpression(exprResult3, Boolean.class.getName());\n        assertTrue(result instanceof Boolean);\n        assertFalse((Boolean) result);\n    }\n\n    @Test\n    public void evaluateLessThanOrEqualsComparisonExpression() throws Exception {\n        final Expression exprOperand1 = new ExpressionBuilder().createConstantIntegerExpression(7);\n        final Expression exprOperand2 = new ExpressionBuilder().createConstantIntegerExpression(9);\n        final Expression exprOperand3 = new ExpressionBuilder().createConstantIntegerExpression(7);\n        final Expression exprOperand4 = new ExpressionBuilder().createConstantIntegerExpression(-8);\n\n        final Expression exprResult1 = new ExpressionBuilder().createComparisonExpression(\"LessThanOrEquals1\",\n                exprOperand1,\n                ComparisonOperator.LESS_THAN_OR_EQUALS, exprOperand2);\n        Serializable result = executeProcessAndGetResultOfExpression(exprResult1, Boolean.class.getName());\n        assertTrue(result instanceof Boolean);\n        assertTrue((Boolean) result);\n\n        final Expression exprResult2 = new ExpressionBuilder().createComparisonExpression(\"LessThanOrEquals2\",\n                exprOperand1,\n                ComparisonOperator.LESS_THAN_OR_EQUALS, exprOperand3);\n        result = executeProcessAndGetResultOfExpression(exprResult2, Boolean.class.getName());\n        assertTrue(result instanceof Boolean);\n        assertTrue((Boolean) result);\n\n        final Expression exprResult3 = new ExpressionBuilder().createComparisonExpression(\"LessThanOrEquals3\",\n                exprOperand3,\n                ComparisonOperator.LESS_THAN_OR_EQUALS, exprOperand4);\n        result = executeProcessAndGetResultOfExpression(exprResult3, Boolean.class.getName());\n        assertTrue(result instanceof Boolean);\n        assertFalse((Boolean) result);\n    }\n\n    @Test\n    public void evaluateComparisonExpressionWithBoolean() throws Exception {\n        final Expression exprOperand1 = new ExpressionBuilder().createConstantBooleanExpression(false);\n        final Expression exprOperand2 = new ExpressionBuilder().createConstantBooleanExpression(true);\n\n        final Expression exprResult1 = new ExpressionBuilder().createComparisonExpression(\"Equals\", exprOperand1,\n                ComparisonOperator.EQUALS, exprOperand2);\n        Serializable result = executeProcessAndGetResultOfExpression(exprResult1, Boolean.class.getName());\n        assertTrue(result instanceof Boolean);\n        assertFalse((Boolean) result);\n\n        final Expression exprResult2 = new ExpressionBuilder().createComparisonExpression(\"GreaterThan\", exprOperand2,\n                ComparisonOperator.GREATER_THAN,\n                exprOperand1);\n        result = executeProcessAndGetResultOfExpression(exprResult2, Boolean.class.getName());\n        assertTrue(result instanceof Boolean);\n        assertTrue((Boolean) result);\n    }\n\n    @Test\n    public void evaluateComparisonExpressionWithDouble() throws Exception {\n        final Expression exprOperand1 = new ExpressionBuilder().createConstantDoubleExpression(1.0);\n        final Expression exprOperand2 = new ExpressionBuilder().createConstantDoubleExpression(2.0);\n        final Expression exprOperand3 = new ExpressionBuilder().createConstantDoubleExpression(1.0);\n\n        final Expression exprResult1 = new ExpressionBuilder().createComparisonExpression(\"Equals\", exprOperand1,\n                ComparisonOperator.EQUALS, exprOperand3);\n        Serializable result = executeProcessAndGetResultOfExpression(exprResult1, Boolean.class.getName());\n        assertTrue(result instanceof Boolean);\n        assertTrue((Boolean) result);\n\n        final Expression exprResult2 = new ExpressionBuilder().createComparisonExpression(\"GreaterThan\", exprOperand1,\n                ComparisonOperator.GREATER_THAN,\n                exprOperand2);\n        result = executeProcessAndGetResultOfExpression(exprResult2, Boolean.class.getName());\n        assertTrue(result instanceof Boolean);\n        assertFalse((Boolean) result);\n    }\n\n    @Test\n    public void evaluateComparisonExpressionWithLong() throws Exception {\n        final Expression exprOperand1 = new ExpressionBuilder().createConstantLongExpression(145);\n        final Expression exprOperand2 = new ExpressionBuilder().createConstantLongExpression(353);\n        final Expression exprOperand3 = new ExpressionBuilder().createConstantLongExpression(145);\n\n        final Expression exprResult1 = new ExpressionBuilder().createComparisonExpression(\"Equals\", exprOperand1,\n                ComparisonOperator.EQUALS, exprOperand3);\n        Serializable result = executeProcessAndGetResultOfExpression(exprResult1, Boolean.class.getName());\n        assertTrue(result instanceof Boolean);\n        assertTrue((Boolean) result);\n\n        final Expression exprResult2 = new ExpressionBuilder().createComparisonExpression(\"GreaterThan\", exprOperand1,\n                ComparisonOperator.GREATER_THAN,\n                exprOperand2);\n        result = executeProcessAndGetResultOfExpression(exprResult2, Boolean.class.getName());\n        assertTrue(result instanceof Boolean);\n        assertFalse((Boolean) result);\n    }\n\n    @Test\n    public void evaluateComparisonExpressionWithLowerCaseString() throws Exception {\n        final Expression exprOperand1 = new ExpressionBuilder().createConstantStringExpression(\"john\");\n        final Expression exprOperand2 = new ExpressionBuilder().createConstantStringExpression(\"ashley\");\n        final Expression exprOperand3 = new ExpressionBuilder().createConstantStringExpression(\"john\");\n\n        final Expression exprResult1 = new ExpressionBuilder().createComparisonExpression(\"Equals\", exprOperand1,\n                ComparisonOperator.EQUALS, exprOperand3);\n        Serializable result = executeProcessAndGetResultOfExpression(exprResult1, Boolean.class.getName());\n        assertTrue(result instanceof Boolean);\n        assertTrue((Boolean) result);\n\n        final Expression exprResult2 = new ExpressionBuilder().createComparisonExpression(\"GreaterThan\", exprOperand2,\n                ComparisonOperator.GREATER_THAN,\n                exprOperand1);\n        result = executeProcessAndGetResultOfExpression(exprResult2, Boolean.class.getName());\n        assertTrue(result instanceof Boolean);\n        assertFalse((Boolean) result);\n    }\n\n    @Test\n    public void evaluateComparisonExpressionWithLowerCaseAndUpperCaseString() throws Exception {\n        final Expression exprOperand1 = new ExpressionBuilder().createConstantStringExpression(\"john\");\n        final Expression exprOperand2 = new ExpressionBuilder().createConstantStringExpression(\"ashley\");\n        final Expression exprOperand3 = new ExpressionBuilder().createConstantStringExpression(\"JOHN\");\n        final Expression exprOperand4 = new ExpressionBuilder().createConstantStringExpression(\"ASHLEY\");\n\n        final Expression exprResult1 = new ExpressionBuilder().createComparisonExpression(\"Equals\", exprOperand1,\n                ComparisonOperator.EQUALS, exprOperand3);\n        Serializable result = executeProcessAndGetResultOfExpression(exprResult1, Boolean.class.getName());\n        assertTrue(result instanceof Boolean);\n        assertFalse((Boolean) result);\n\n        final Expression exprResult2 = new ExpressionBuilder().createComparisonExpression(\"GreaterThan\", exprOperand2,\n                ComparisonOperator.GREATER_THAN,\n                exprOperand4);\n        result = executeProcessAndGetResultOfExpression(exprResult2, Boolean.class.getName());\n        assertTrue(result instanceof Boolean);\n        assertTrue((Boolean) result);\n\n        final Expression exprResult3 = new ExpressionBuilder().createComparisonExpression(\"Equals\", exprOperand3,\n                ComparisonOperator.EQUALS, exprOperand1);\n        result = executeProcessAndGetResultOfExpression(exprResult3, Boolean.class.getName());\n        assertTrue(result instanceof Boolean);\n        assertFalse((Boolean) result);\n\n        final Expression exprResult4 = new ExpressionBuilder().createComparisonExpression(\"GreaterThan\", exprOperand4,\n                ComparisonOperator.GREATER_THAN,\n                exprOperand1);\n        result = executeProcessAndGetResultOfExpression(exprResult4, Boolean.class.getName());\n        assertTrue(result instanceof Boolean);\n        assertFalse((Boolean) result);\n    }\n\n    @Test\n    public void runProcessWithScriptThatThrowExceptionFailTheTask() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithGroovy\", \"1.0\");\n        processBuilder.addAutomaticTask(\"activityThatFail\").addData(\"data1\", String.class.getName(),\n                new ExpressionBuilder().createGroovyScriptExpression(\"script\", \"throw new Exception()\",\n                        String.class.getName()));\n        processBuilder.addUserTask(\"aTask\", ACTOR_NAME);\n        processBuilder.addActor(ACTOR_NAME);\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance, \"aTask\");\n        waitForTaskToFail(processInstance);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void processWithXPathExpression() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"XPathExpression\", \"1.0\");\n        processBuilder.addData(\"data\", String.class.getName(),\n                new ExpressionBuilder().createXPathExpression(\"xpath\", \"/root/element/@name\",\n                        XPathReturnType.STRING, \"<root><element name='Alexander Corvinus' /></root>\"));\n        processBuilder.addUserTask(\"aDummyTask\", ACTOR_NAME);\n        processBuilder.addActor(ACTOR_NAME);\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance, \"aDummyTask\");\n\n        assertEquals(\"Alexander Corvinus\",\n                getProcessAPI().getProcessDataInstance(\"data\", processInstance.getId()).getValue());\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeProcessWithAutomaticTasksAndTransitionFailed() throws Exception {\n        // Build condition\n        final Expression condition = new ExpressionBuilder().createGroovyScriptExpression(\n                \"evaluateGroovyExpressionFromApi\", \"throw new Exception()\",\n                Boolean.class.getName());\n\n        // Build process\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder().createNewInstance(\n                \"executeConnectorOnStartOfAnAutomaticActivity\", \"1.0\");\n        designProcessDefinition.addActor(ACTOR_NAME).addDescription(\"Delivery all day and night long\");\n        designProcessDefinition.addAutomaticTask(\"step1\");\n        designProcessDefinition.addAutomaticTask(\"step2\");\n        designProcessDefinition.addAutomaticTask(\"default\");\n        designProcessDefinition.addTransition(\"step1\", \"step2\", condition);\n        designProcessDefinition.addDefaultTransition(\"step1\", \"default\");\n\n        // Start process\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(),\n                ACTOR_NAME, user);\n        getProcessAPI().startProcess(processDefinition.getId());\n\n        // Test if step1 state is FAILED\n        waitForFlowNodeInFailedState(\"step1\");\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void evaluateJavaMethodCallExpression() throws Exception {\n        final String extraDataName = \"myValues\";\n        final String extraDataType = List.class.getName();\n        final Expression extraDataValue = new ExpressionBuilder().createGroovyScriptExpression(\"defaultList\",\n                \"return Arrays.asList(1, 2 , 3);\", extraDataType);\n        final Expression dep = new ExpressionBuilder().createDataExpression(extraDataName, extraDataType);\n\n        final Expression expression = new ExpressionBuilder().createJavaMethodCallExpression(\"getter\", \"toString\",\n                String.class.getName(), dep);\n        final Serializable result = executeProcessAndGetResultOfExpression(expression, String.class.getName(),\n                extraDataName, extraDataType, extraDataValue);\n        assertEquals(\"[1, 2, 3]\", result);\n\n    }\n\n    @Test\n    public void evaluateJavaMethodCallExpressionTwice() throws Exception {\n        // the cache on ClassReflector made impossible to call the same expression on the same class if it was loaded by 2 different process\n        final BusinessArchiveBuilder builder = new BusinessArchiveBuilder().createNewBusinessArchive();\n        builder.addClasspathResource(getResource(\"/custom-0.1.jar.bak\", \"custom-0.1.jar\"));\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"ProcessWithCustomData\", \"1.0\");\n        designProcessDefinition.addData(\"adress\", \"org.bonitasoft.custom.Address\",\n                new ExpressionBuilder().createGroovyScriptExpression(\"create adress\",\n                        \"new org.bonitasoft.custom.Address(\\\"name1\\\",\\\"Rue ampère\\\",\\\"38000\\\",\\\"Grenoble\\\",\\\"France\\\")\",\n                        \"org.bonitasoft.custom.Address\"));\n        designProcessDefinition\n                .addActor(ACTOR_NAME)\n                .addUserTask(\"step1\", ACTOR_NAME)\n                .addDisplayName(\n                        new ExpressionBuilder().createJavaMethodCallExpression(\"getNameOfAdress\", \"getName\",\n                                String.class.getName(),\n                                new ExpressionBuilder().createDataExpression(\"adress\",\n                                        \"org.bonitasoft.custom.Address\")));\n        designProcessDefinition.addAutomaticTask(\"start\").addTransition(\"start\", \"step1\");\n        builder.setProcessDefinition(designProcessDefinition.done());\n        final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user);\n        final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition1.getId());\n        final ActivityInstance userTask = waitForUserTaskAndGetIt(processInstance1, \"step1\");\n        assertEquals(\"name1\", userTask.getDisplayName());\n\n        // do the same thing a second time\n        final BusinessArchiveBuilder builder2 = new BusinessArchiveBuilder().createNewBusinessArchive();\n        builder2.addClasspathResource(getResource(\"/custom-0.1.jar.bak\", \"custom-0.1.jar\"));\n        final ProcessDefinitionBuilder designProcessDefinition2 = new ProcessDefinitionBuilder()\n                .createNewInstance(\"ProcessWithCustomData\", \"1.1\");\n        designProcessDefinition2.addData(\"adress\", \"org.bonitasoft.custom.Address\",\n                new ExpressionBuilder().createGroovyScriptExpression(\"create adress\",\n                        \"new org.bonitasoft.custom.Address(\\\"name1\\\",\\\"Rue ampère\\\",\\\"38000\\\",\\\"Grenoble\\\",\\\"France\\\")\",\n                        \"org.bonitasoft.custom.Address\"));\n        designProcessDefinition2\n                .addActor(ACTOR_NAME)\n                .addUserTask(\"step1\", ACTOR_NAME)\n                .addDisplayName(\n                        new ExpressionBuilder().createJavaMethodCallExpression(\"getNameOfAdress\", \"getName\",\n                                String.class.getName(),\n                                new ExpressionBuilder().createDataExpression(\"adress\",\n                                        \"org.bonitasoft.custom.Address\")));\n        designProcessDefinition2.addAutomaticTask(\"start\").addTransition(\"start\", \"step1\");\n        builder2.setProcessDefinition(designProcessDefinition2.done());\n        final ProcessDefinition processDefinition2 = deployAndEnableProcessWithActor(builder2.done(), ACTOR_NAME, user);\n        final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinition2.getId());\n        final ActivityInstance userTask2 = waitForUserTaskAndGetIt(processInstance2, \"step1\");\n        assertEquals(\"name1\", userTask2.getDisplayName());\n\n        final ProcessInstance processInstance3 = getProcessAPI().startProcess(processDefinition1.getId());\n        final ActivityInstance userTask3 = waitForUserTaskAndGetIt(processInstance3, \"step1\");\n        assertEquals(\"name1\", userTask3.getDisplayName());\n\n        disableAndDeleteProcess(processDefinition1);\n        disableAndDeleteProcess(processDefinition2);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/StartProcessWithOperationsIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.process;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\n\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.data.DataInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.operation.OperationBuilder;\nimport org.junit.Test;\n\n/**\n * @author Baptiste Mesta\n */\npublic class StartProcessWithOperationsIT extends TestWithUser {\n\n    @Test\n    public void startProcessWithJavaOperations() throws Exception {\n        ProcessDefinitionBuilder processWithOps = new ProcessDefinitionBuilder().createNewInstance(\"processWithOps\",\n                \"1.0\");\n        User john = createUser(\"john\", \"bpm\");\n        processWithOps.addActor(\"actor\");\n        processWithOps.addData(\"data1\", ArrayList.class.getName(), new ExpressionBuilder().createGroovyScriptExpression(\n                \"createList\", \"new java.util.ArrayList<String>()\", ArrayList.class.getName()));\n        processWithOps.addUserTask(\"step1\", \"actor\");\n        ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processWithOps.done(), \"actor\", john);\n\n        ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId(),\n                Arrays.asList(\n                        new OperationBuilder().createJavaMethodOperation(\"data1\", \"add\", String.class.getName(),\n                                new ExpressionBuilder().createConstantStringExpression(\"listValue\"))),\n                new HashMap<String, Serializable>());\n\n        waitForUserTask(\"step1\");\n        DataInstance data1 = getProcessAPI().getProcessDataInstance(\"data1\", processInstance.getId());\n        assertThat(((List) data1.getValue()).get(0)).isEqualTo(\"listValue\");\n        disableAndDeleteProcess(processDefinition);\n        deleteUser(john);\n\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/actor/ExportActorMappingIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.process.actor;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\n\nimport java.io.InputStream;\nimport java.util.List;\n\nimport org.apache.commons.io.IOUtils;\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.actor.ActorCriterion;\nimport org.bonitasoft.engine.bpm.actor.ActorInstance;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.identity.Group;\nimport org.bonitasoft.engine.identity.Role;\nimport org.bonitasoft.engine.identity.User;\nimport org.junit.Test;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class ExportActorMappingIT extends TestWithUser {\n\n    @Test\n    public void exportSimpleActorMapping() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"exportProcess\", \"1.0\");\n        processBuilder.addActor(ACTOR_NAME).addDescription(\"Delivery all day and night long\").addUserTask(\"userTask1\",\n                ACTOR_NAME);\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user);\n\n        final String xmlContent = getProcessAPI().exportActorMapping(definition.getId());\n        final InputStream xmlStream = ImportActorMappingIT.class.getResourceAsStream(\"simpleActorMapping.xml\");\n        try {\n            final byte[] actormapping = IOUtils.toByteArray(xmlStream);\n            assertThatXmlHaveNoDifferences(new String(actormapping), xmlContent);\n        } finally {\n            xmlStream.close();\n        }\n        disableAndDeleteProcess(definition);\n    }\n\n    @Test\n    public void exportActorMappingWithDeletedGroup() throws Exception {\n        final Group sales = createGroup(\"sales\");\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"exportProcess\", \"1.0\");\n        processBuilder.addActor(ACTOR_NAME).addDescription(\"Delivery all day and night long\").addUserTask(\"userTask1\",\n                ACTOR_NAME);\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, sales);\n\n        deleteGroups(sales);\n        // should work\n        final String xmlContent = getProcessAPI().exportActorMapping(definition.getId());\n        disableAndDeleteProcess(definition);\n        assertFalse(xmlContent.contains(\"<group>\") || xmlContent.contains(\"<sale>\"));\n    }\n\n    @Test\n    public void exportActorMappingWithDeletedUser() throws Exception {\n        final User john = createUser(\"john\", \"bpm\");\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"exportProcess\", \"1.0\");\n        processBuilder.addActor(ACTOR_NAME).addDescription(\"Delivery all day and night long\").addUserTask(\"userTask1\",\n                ACTOR_NAME);\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, john);\n\n        deleteUsers(john);\n        // should work\n        final String xmlContent = getProcessAPI().exportActorMapping(definition.getId());\n        disableAndDeleteProcess(definition);\n        assertFalse(xmlContent.contains(\"<user>\") || xmlContent.contains(\"john\"));\n    }\n\n    @Test\n    public void exportActorMappingWithDeletedMembership() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"exportProcess\", \"1.0\");\n        processBuilder.addActor(ACTOR_NAME).addDescription(\"Delivery all day and night long\").addUserTask(\"userTask1\",\n                ACTOR_NAME);\n        final DesignProcessDefinition processDefinition = processBuilder.done();\n        final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive();\n        businessArchive.setProcessDefinition(processDefinition);\n\n        loginWithTechnicalUser();\n        final Group group = createGroup(\"group\");\n        final Role role = createRole(\"role\");\n        final ProcessDefinition definition = getProcessAPI().deploy(businessArchive.done());\n\n        final List<ActorInstance> actors = getProcessAPI().getActors(definition.getId(), 0, 1, ActorCriterion.NAME_ASC);\n        assertEquals(1, actors.size());\n        final ActorInstance actor = actors.get(0);\n\n        getProcessAPI().addRoleAndGroupToActor(actor.getId(), role.getId(), group.getId());\n        deleteRoles(role);\n        // should work\n        final String xmlContent = getProcessAPI().exportActorMapping(definition.getId());\n        deleteProcess(definition);\n        assertFalse(xmlContent.contains(\"<group>\") || xmlContent.contains(\"<role>\"));\n        deleteGroups(group);\n    }\n\n    @Test\n    public void exportActorMappingWithDeletedSubGroup() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"exportProcess\", \"1.0\");\n        processBuilder.addActor(ACTOR_NAME).addDescription(\"Delivery all day and night long\").addUserTask(\"userTask1\",\n                ACTOR_NAME);\n        final DesignProcessDefinition processDefinition = processBuilder.done();\n        final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive();\n        businessArchive.setProcessDefinition(processDefinition);\n\n        loginWithTechnicalUser();\n        final Group sales = createGroup(\"sales\");\n        final Group subSales = createGroup(\"sub\", \"/sales\");\n        final ProcessDefinition definition = getProcessAPI().deploy(businessArchive.done());\n\n        final List<ActorInstance> actors = getProcessAPI().getActors(definition.getId(), 0, 1, ActorCriterion.NAME_ASC);\n        assertEquals(1, actors.size());\n        final ActorInstance actor = actors.get(0);\n\n        getProcessAPI().addGroupToActor(actor.getId(), subSales.getId());\n        deleteGroups(sales);\n        // should work\n        final String xmlContent = getProcessAPI().exportActorMapping(definition.getId());\n        deleteProcess(definition);\n        assertFalse(xmlContent, xmlContent.contains(\"<group>\") || xmlContent.contains(\"<sale>\"));\n    }\n\n    @Test\n    public void exportActorMappingWithDeletedRole() throws Exception {\n        final Role sales = createRole(\"sales\");\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"exportProcess\", \"1.0\");\n        processBuilder.addActor(ACTOR_NAME).addDescription(\"Delivery all day and night long\").addUserTask(\"userTask1\",\n                ACTOR_NAME);\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user);\n\n        deleteRoles(sales);\n        // should work\n        final String xmlContent = getProcessAPI().exportActorMapping(definition.getId());\n        disableAndDeleteProcess(definition);\n        assertFalse(xmlContent.contains(\"<role>\") || xmlContent.contains(\"<sale>\"));\n    }\n\n    @Test\n    public void exportcomplexActorMapping() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"exportProcess\", \"1.0\");\n        processBuilder.addActor(ACTOR_NAME).addDescription(\"Delivery all day and night long\").addUserTask(\"userTask1\",\n                ACTOR_NAME);\n        final DesignProcessDefinition processDefinition = processBuilder.done();\n        final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive();\n        businessArchive.setProcessDefinition(processDefinition);\n\n        loginWithTechnicalUser();\n        final User john = createUser(\"john\", \"bpm\");\n        final Group rd = createGroup(\"RD\");\n        final Role role = createRole(\"dev\");\n        // final UserMembership createUserMembership = createUserMembership(johnName, \"RD\", \"dev\");\n        final ProcessDefinition definition = getProcessAPI().deploy(businessArchive.done());\n\n        final List<ActorInstance> actors = getProcessAPI().getActors(definition.getId(), 0, 1, ActorCriterion.NAME_ASC);\n        assertEquals(1, actors.size());\n        final ActorInstance actor = actors.get(0);\n        assertEquals(ACTOR_NAME, actor.getName());\n\n        getProcessAPI().addUserToActor(actor.getId(), john.getId());\n        getProcessAPI().addGroupToActor(actor.getId(), rd.getId());\n        getProcessAPI().addRoleToActor(actor.getId(), role.getId());\n        getProcessAPI().addRoleAndGroupToActor(actor.getId(), role.getId(), rd.getId());\n\n        final String xmlContent = getProcessAPI().exportActorMapping(definition.getId());\n        final InputStream xmlStream = ImportActorMappingIT.class.getResourceAsStream(\"complexActorMapping.xml\");\n        try {\n            final byte[] actormapping = IOUtils.toByteArray(xmlStream);\n            assertThatXmlHaveNoDifferences(new String(actormapping), xmlContent);\n        } finally {\n            xmlStream.close();\n        }\n        getIdentityAPI().deleteGroup(rd.getId());\n        getIdentityAPI().deleteRole(role.getId());\n        deleteUser(john);\n        deleteProcess(definition);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/actor/ImportActorMappingIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.process.actor;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.List;\n\nimport org.apache.commons.io.IOUtils;\nimport org.bonitasoft.engine.TestWithTechnicalUser;\nimport org.bonitasoft.engine.bpm.actor.ActorCriterion;\nimport org.bonitasoft.engine.bpm.actor.ActorInstance;\nimport org.bonitasoft.engine.bpm.actor.ActorMappingImportException;\nimport org.bonitasoft.engine.bpm.actor.ActorMember;\nimport org.bonitasoft.engine.bpm.bar.ActorMappingMarshaller;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.bar.actorMapping.ActorMapping;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.process.ActivationState;\nimport org.bonitasoft.engine.bpm.process.ConfigurationState;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.Problem;\nimport org.bonitasoft.engine.bpm.process.ProcessActivationException;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.identity.Group;\nimport org.bonitasoft.engine.identity.Role;\nimport org.bonitasoft.engine.identity.User;\nimport org.junit.After;\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class ImportActorMappingIT extends TestWithTechnicalUser {\n\n    private ProcessDefinition processDefinition;\n\n    @Override\n    @After\n    public void after() throws Exception {\n        try {\n            getProcessAPI().disableProcess(processDefinition.getId());\n        } catch (final ProcessActivationException e) {\n            // Do nothing. Process already disabled\n        }\n        deleteProcess(processDefinition);\n        getIdentityAPI().deleteOrganization();\n        super.after();\n    }\n\n    @Test\n    public void importSimpleActorMapping() throws Exception {\n        final User john = createUser(USERNAME, PASSWORD);\n        final BusinessArchiveBuilder businessArchive = createAndDeployProcessDefinitionWithImportedActorMapping(\n                \"simpleActorMapping.xml\");\n        processDefinition = deployProcess(businessArchive.done());\n        getProcessAPI().enableProcess(processDefinition.getId());\n\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance, \"userTask1\");\n        waitForPendingTasks(john.getId(), 1);\n\n        final List<HumanTaskInstance> tasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null);\n        assertEquals(1, tasks.size());\n\n        deleteUser(john);\n    }\n\n    @Test\n    public void importComplexActorMapping() throws Exception {\n        final BusinessArchiveBuilder businessArchive = createAndDeployProcessDefinitionWithImportedActorMapping(\n                \"complexActorMapping.xml\");\n        final User john = createUser(\"john\", \"bpm\");\n        final Group rd = createGroup(\"RD\");\n        final Role role = createRole(\"dev\");\n        processDefinition = deployProcess(businessArchive.done());\n        getProcessAPI().enableProcess(processDefinition.getId());\n\n        getAndCheckActors(john, rd, role, processDefinition);\n    }\n\n    @Test\n    public void importActorMappingWithWrongXMLFileBeforeDeploy() throws Exception {\n        final BusinessArchiveBuilder businessArchive = createAndDeployProcessDefinitionWithImportedActorMapping(\n                \"actorMappingWithException.xml\");\n        checkProcessNotActivated(businessArchive);\n    }\n\n    @Test(expected = ActorMappingImportException.class)\n    public void importActorMappingWithWrongXMLFileAfterDeploy() throws Exception {\n        final User user = createUser(\"john\", \"bpm\");\n\n        createProcessDefinitionAndCheckActorMappingImportException(user, \"actorMappingWithException.xml\");\n    }\n\n    @Test\n    public void importActorMappingWithUnknownUserBeforeDeploy() throws Exception {\n        final BusinessArchiveBuilder businessArchive = createAndDeployProcessDefinitionWithImportedActorMapping(\n                \"simpleActorMapping.xml\");\n        checkProcessNotActivated(businessArchive);\n    }\n\n    @Test(expected = ActorMappingImportException.class)\n    public void importActorMappingWithUnknownUserAfterDeploy() throws Exception {\n        final User user = createUser(\"paul\", \"bpm\");\n\n        createProcessDefinitionAndCheckActorMappingImportException(user, \"actorMappingWithException.xml\");\n    }\n\n    @Test\n    public void importActorMappingWithUnknownGroupBeforeDeploy() throws Exception {\n        final BusinessArchiveBuilder businessArchive = createAndDeployProcessDefinitionWithImportedActorMapping(\n                \"complexActorMapping.xml\");\n        createUser(\"john\", \"bpm\");\n        createGroup(\"RD1\");\n        createRole(\"dev\");\n\n        // even if missing group the process is resolved\n        checkProcessActivated(businessArchive);\n    }\n\n    @Test(expected = ActorMappingImportException.class)\n    public void importActorMappingWithUnknownGroupAfterDeploy() throws Exception {\n        final User user = createUser(\"john\", \"bpm\");\n        createGroup(\"RD1\");\n        createRole(\"dev\");\n\n        createProcessDefinitionAndCheckActorMappingImportException(user, \"complexActorMapping.xml\");\n    }\n\n    @Test\n    public void importActorMappingWithUnknownRoleBeforeDeploy() throws Exception {\n        final BusinessArchiveBuilder businessArchive = createAndDeployProcessDefinitionWithImportedActorMapping(\n                \"complexActorMapping.xml\");\n        createUser(\"john\", \"bpm\");\n        createGroup(\"RD\");\n        createRole(\"dev1\");\n\n        // even if missing role the process is resolved\n        checkProcessActivated(businessArchive);\n    }\n\n    @Test(expected = ActorMappingImportException.class)\n    public void importActorMappingWithUnknownRoleAfterDeploy() throws Exception {\n        final User user = createUser(\"john\", \"bpm\");\n        createGroup(\"RD\");\n        createRole(\"dev1\");\n\n        createProcessDefinitionAndCheckActorMappingImportException(user, \"complexActorMapping.xml\");\n    }\n\n    @Test\n    public void importActorMappingWithUnknownMemberShipBeforeDeploy() throws Exception {\n        createUser(\"john\", \"bpm\");\n        createGroup(\"RD2\");\n        createRole(\"dev\");\n        final BusinessArchiveBuilder businessArchive = createAndDeployProcessDefinitionWithImportedActorMapping(\n                \"complexActorMappingWithUnkownGroup.xml\");\n\n        // even if missing membership the process is resolved\n        checkProcessActivated(businessArchive);\n    }\n\n    @Test(expected = ActorMappingImportException.class)\n    public void importActorMappingWithUnknownMemberShipAfterDeploy() throws Exception {\n        final User user = createUser(\"john\", \"bpm\");\n        createGroup(\"RD2\");\n        createRole(\"dev\");\n\n        createProcessDefinitionAndCheckActorMappingImportException(user, \"complexActorMappingWithUnkownGroup.xml\");\n    }\n\n    @Test\n    public void importActorMapping() throws Exception {\n        final User user = createUser(\"john\", \"bpm\");\n        final Group rd = createGroup(\"RD\");\n        final Role role = createRole(\"dev\");\n\n        final ProcessDefinitionBuilder processBuilder = createProcessDefinitionBuilder();\n        processDefinition = deployAndEnableProcessWithActor(processBuilder.getProcess(), ACTOR_NAME, user);\n        getProcessAPI().importActorMapping(processDefinition.getId(), xmlToByteArray(\"complexActorMapping2.xml\"));\n\n        getAndCheckActors(user, rd, role, processDefinition);\n    }\n\n    @Test\n    public void importActorMappingComputesProcessResolution() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder();\n        builder.createNewInstance(\"resolve\", \"1.0\").addActor(\"Leader\", true).addUserTask(\"step1\", \"Leader\");\n        final DesignProcessDefinition processDesignDefinition = builder.done();\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(processDesignDefinition).done();\n        processDefinition = deployProcess(businessArchive);\n        ProcessDeploymentInfo deploymentInfo = getProcessAPI().getProcessDeploymentInfo(processDefinition.getId());\n        Assert.assertEquals(ConfigurationState.UNRESOLVED, deploymentInfo.getConfigurationState());\n\n        // Let's resolve actor mapping by importing a valid file:\n        final User user = createUser(\"john\", \"bpm\");\n        getProcessAPI()\n                .importActorMapping(\n                        processDefinition.getId(),\n                        \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?><actorMappings:actorMappings xmlns:actorMappings=\\\"http://www.bonitasoft.org/ns/actormapping/6.0\\\"><actorMapping name=\\\"Leader\\\"><users><user>john</user></users></actorMapping></actorMappings:actorMappings>\"\n                                .getBytes());\n\n        deploymentInfo = getProcessAPI().getProcessDeploymentInfo(processDefinition.getId());\n        Assert.assertEquals(ConfigurationState.RESOLVED, deploymentInfo.getConfigurationState());\n\n        deleteUser(user);\n    }\n\n    /**\n     * @param xmlFileName\n     * @return ProcessDefinition\n     * @throws Exception\n     * @since 6.0\n     */\n    private BusinessArchiveBuilder createAndDeployProcessDefinitionWithImportedActorMapping(final String xmlFileName)\n            throws Exception {\n        final DesignProcessDefinition processDefinition = createProcessDefinitionBuilder().done();\n        final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive();\n        businessArchive.setProcessDefinition(processDefinition);\n        ActorMapping actormapping = new ActorMappingMarshaller().deserializeFromXML(xmlToByteArray(xmlFileName));\n        if (actormapping != null) {\n            businessArchive.setActorMapping(actormapping);\n        }\n        return businessArchive;\n    }\n\n    /**\n     * @param xmlFileName\n     * @return\n     * @throws IOException\n     * @since 6.0\n     */\n    private byte[] xmlToByteArray(final String xmlFileName) throws IOException {\n        final InputStream xmlStream = ImportActorMappingIT.class.getResourceAsStream(xmlFileName);\n        byte[] actormapping = null;\n        try {\n            actormapping = IOUtils.toByteArray(xmlStream);\n        } finally {\n            xmlStream.close();\n        }\n        return actormapping;\n    }\n\n    /**\n     * @return\n     * @since 6.0\n     */\n    private ProcessDefinitionBuilder createProcessDefinitionBuilder() {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(\"firstProcess\",\n                \"1.0\");\n        processBuilder.addActor(ACTOR_NAME).addDescription(\"Delivery all day and night long\").addUserTask(\"userTask1\",\n                ACTOR_NAME);\n        return processBuilder;\n    }\n\n    private void checkProcessNotActivated(final BusinessArchiveBuilder businessArchive) throws Exception {\n        processDefinition = deployProcess(businessArchive.done());\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ConfigurationState.UNRESOLVED, processDeploymentInfo.getConfigurationState());\n        final List<Problem> processResolutionProblems = getProcessAPI()\n                .getProcessResolutionProblems(processDefinition.getId());\n        assertEquals(1, processResolutionProblems.size());\n        final Problem problem = processResolutionProblems.get(0);\n        assertEquals(\"actor\", problem.getResource());\n    }\n\n    private void checkProcessActivated(final BusinessArchiveBuilder businessArchive) throws Exception {\n        processDefinition = deployProcess(businessArchive.done());\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ConfigurationState.RESOLVED, processDeploymentInfo.getConfigurationState());\n    }\n\n    /**\n     * @param user\n     * @param xmlFileName\n     * @throws Exception\n     * @since 6.0\n     */\n    private void createProcessDefinitionAndCheckActorMappingImportException(final User user, final String xmlFileName)\n            throws Exception {\n        final ProcessDefinitionBuilder processBuilder = createProcessDefinitionBuilder();\n        processDefinition = deployAndEnableProcessWithActor(processBuilder.getProcess(), ACTOR_NAME, user);\n\n        getProcessAPI().importActorMapping(processDefinition.getId(), xmlToByteArray(xmlFileName));\n    }\n\n    /**\n     * @param user\n     * @param group\n     * @param role\n     * @param definition\n     * @since 6.0\n     */\n    private void getAndCheckActors(final User user, final Group group, final Role role,\n            final ProcessDefinition definition) {\n        final List<ActorInstance> actors = getProcessAPI().getActors(definition.getId(), 0, 15,\n                ActorCriterion.NAME_DESC);\n        final ActorInstance actorInstance = actors.get(0);\n        final List<ActorMember> actorMembers = getProcessAPI().getActorMembers(actorInstance.getId(), 0, 15);\n        assertEquals(4, actorMembers.size());\n        for (final ActorMember actorMember : actorMembers) {\n            assertTrue(checkRole(actorMember, role.getId()) || checkGroup(actorMember, group.getId())\n                    || checkUser(actorMember, user.getId())\n                    || checkMembership(actorMember, role.getId(), group.getId()));\n        }\n    }\n\n    private boolean checkMembership(final ActorMember actorMember, final long roleId, final long groupId) {\n        return actorMember.getRoleId() == roleId && actorMember.getGroupId() == groupId;\n    }\n\n    private boolean checkUser(final ActorMember actorMember, final long userId) {\n        return actorMember.getUserId() == userId;\n    }\n\n    private boolean checkGroup(final ActorMember actorMember, final long groupId) {\n        return actorMember.getGroupId() == groupId && actorMember.getRoleId() <= 0;\n    }\n\n    private boolean checkRole(final ActorMember actorMember, final long roleId) {\n        return actorMember.getRoleId() == roleId && actorMember.getGroupId() <= 0;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/actor/ProcessActorIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.process.actor;\n\nimport static org.junit.Assert.*;\n\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.actor.ActorCriterion;\nimport org.bonitasoft.engine.bpm.actor.ActorInstance;\nimport org.bonitasoft.engine.bpm.actor.ActorMember;\nimport org.bonitasoft.engine.bpm.actor.ActorUpdater;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveFactory;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.process.ActivationState;\nimport org.bonitasoft.engine.bpm.process.ConfigurationState;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.Problem;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.connectors.VariableStorage;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.identity.Group;\nimport org.bonitasoft.engine.identity.Role;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.test.BuildTestUtil;\nimport org.junit.After;\nimport org.junit.Test;\n\npublic class ProcessActorIT extends TestWithUser {\n\n    @Override\n    @After\n    public void after() throws Exception {\n        VariableStorage.clearAll();\n        super.after();\n    }\n\n    @Test(expected = AlreadyExistsException.class)\n    public void mapTwiceSameActorToAGroup() throws Exception {\n        final ProcessDefinition definition = deployAndEnableProcessWithHumanTask(PROCESS_NAME, ACTOR_NAME, \"deliver\");\n        final ActorInstance actor = checkActors(ACTOR_NAME, definition);\n        final Group ergo = createGroup(\"Ergonomists\");\n        try {\n            getProcessAPI().addGroupToActor(actor.getId(), ergo.getId());\n            getProcessAPI().addGroupToActor(actor.getId(), ergo.getId());\n            fail(\"This statement should never be reached\");\n        } finally {\n            deleteGroups(ergo);\n            disableAndDeleteProcess(definition);\n        }\n    }\n\n    @Test\n    public void mapActorToAlreadyMappedParentGroupShouldBeHandledSilently() throws Exception {\n        final ProcessDefinition definition = deployAndEnableProcessWithHumanTask(PROCESS_NAME, ACTOR_NAME, \"deliver\");\n        final ActorInstance actor = checkActors(ACTOR_NAME, definition);\n        final Group all = createGroup(\"Everyone\");\n        final Group ergo = createGroup(\"Ergonomists\", all.getPath());\n        try {\n            getProcessAPI().addGroupToActor(actor.getId(), ergo.getId());\n            getProcessAPI().addGroupToActor(actor.getId(), all.getId());\n            final List<ActorMember> actorMembers = getProcessAPI().getActorMembers(actor.getId(), 0, 50);\n            int nbGroupActorMembers = 0;\n            for (final ActorMember actorMember : actorMembers) {\n                if (actorMember.getGroupId() != -1 && actorMember.getRoleId() == -1) {\n                    nbGroupActorMembers++;\n                }\n            }\n            assertEquals(\"All group / actor mapping should have been retrieved\", 2, nbGroupActorMembers);\n        } finally {\n            deleteGroups(ergo);\n            deleteGroups(all);\n            disableAndDeleteProcess(definition);\n        }\n    }\n\n    @Test\n    public void mapActorToAGroupAndGroupPlusRoleIsValid() throws Exception {\n        final ProcessDefinition definition = deployAndEnableProcessWithHumanTask(PROCESS_NAME, ACTOR_NAME, \"deliver\");\n        final ActorInstance actor = checkActors(ACTOR_NAME, definition);\n        final Role role = createRole(\"Quality Manager\");\n        final Group ergo = createGroup(\"Ergonomists\");\n        try {\n            final ActorMember addGroupToActor = getProcessAPI().addGroupToActor(actor.getId(), ergo.getId());\n            final ActorMember addRoleAndGroupToActor = getProcessAPI().addRoleAndGroupToActor(actor.getId(),\n                    role.getId(), ergo.getId());\n            final List<ActorMember> actorMembers = getProcessAPI().getActorMembers(actor.getId(), 0, 50);\n            assertTrue(\"All group / actor mapping should have been retrieved\",\n                    actorMembers.containsAll(Arrays.asList(addGroupToActor, addRoleAndGroupToActor)));\n        } finally {\n            deleteGroups(ergo);\n            deleteRoles(role);\n            disableAndDeleteProcess(definition);\n        }\n    }\n\n    @Test\n    public void johnHasGotAPendingTask() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME).addUserTask(\"deliver\", ACTOR_NAME);\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME,\n                user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        final List<ActorInstance> actors = getProcessAPI().getActors(processDefinition.getId(), 0, 1,\n                ActorCriterion.NAME_ASC);\n        assertEquals(1, actors.size());\n        final ActorInstance actor = actors.get(0);\n        assertEquals(ACTOR_NAME, actor.getName());\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance, \"deliver\");\n\n        final List<HumanTaskInstance> tasks = getProcessAPI().getPendingHumanTaskInstances(user.getId(), 0, 10, null);\n        assertEquals(1, tasks.size());\n\n        // Clean up\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void deployProcessWithActorMappingNotMatchingOrganization() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME).addUserTask(\"deliver\", ACTOR_NAME);\n        final DesignProcessDefinition designProcessDefinition = processBuilder.done();\n        final BusinessArchiveBuilder barBuilder = new BusinessArchiveBuilder().createNewBusinessArchive();\n        barBuilder.setProcessDefinition(designProcessDefinition);\n        final StringBuilder builder = new StringBuilder();\n        builder.append(\"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>\");\n        builder.append(\n                \"<actormapping:actorMappings xmlns:actormapping=\\\"http://www.bonitasoft.org/ns/actormapping/6.0\\\">\");\n        builder.append(\"\\t<actorMapping name=\\\"ACTOR_NAME men\\\">\");\n        builder.append(\"\\t\\t<groups>\");\n        builder.append(\"\\t\\t\\t<group>/unknown</group>\");\n        builder.append(\"\\t\\t</groups>\");\n        builder.append(\"\\t\\t<users>\");\n        builder.append(\"\\t\\t\\t<user>toto</user>\");\n        builder.append(\"\\t\\t</users>\");\n        builder.append(\"\\t</actorMapping>\");\n        builder.append(\"</actormapping:actorMappings>\");\n        barBuilder.setActorMapping(builder.toString().getBytes(\"UTF-8\"));\n        final BusinessArchive businessArchive = barBuilder.done();\n        final ProcessDefinition processDefinition = deployProcess(businessArchive);\n\n        // process deployed but not activated\n        // trying to add an actor on the process now\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ConfigurationState.UNRESOLVED, processDeploymentInfo.getConfigurationState());\n        final List<Problem> processResolutionProblems = getProcessAPI()\n                .getProcessResolutionProblems(processDefinition.getId());\n        assertEquals(1, processResolutionProblems.size());\n        final Problem problem = processResolutionProblems.get(0);\n        assertEquals(\"actor\", problem.getResource());\n        final List<ActorInstance> actors = getProcessAPI().getActors(processDeploymentInfo.getProcessId(), 0, 50,\n                ActorCriterion.NAME_ASC);\n        getProcessAPI().addUserToActor(actors.get(0).getId(), user.getId());\n        getProcessAPI().enableProcess(processDeploymentInfo.getProcessId());\n        assertEquals(ActivationState.ENABLED,\n                getProcessAPI().getProcessDeploymentInfo(processDeploymentInfo.getProcessId()).getActivationState());\n\n        // Clean up\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void butJamesHasNoPendingTask() throws Exception {\n        final User plop = createUser(\"plop\", PASSWORD);\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME).addUserTask(\"userTask1\", ACTOR_NAME);\n        final DesignProcessDefinition designProcessDefinition = processBuilder.done();\n        final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive();\n        businessArchive.setProcessDefinition(designProcessDefinition);\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        getProcessAPI().startProcess(processDefinition.getId());\n        Thread.sleep(1000);\n\n        final List<HumanTaskInstance> tasks = getProcessAPI().getPendingHumanTaskInstances(plop.getId(), 0, 10, null);\n        assertEquals(0, tasks.size());\n\n        // Clean up\n        disableAndDeleteProcess(processDefinition);\n        deleteUser(plop);\n    }\n\n    @Test\n    public void eliasHasAssignedAndPendingUserTasks() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME).addUserTask(\"userTask1\", ACTOR_NAME).addUserTask(\"userTask2\", ACTOR_NAME)\n                .addUserTask(\"userTask3\", ACTOR_NAME);\n        final DesignProcessDefinition processDefinition = processBuilder.done();\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinition, ACTOR_NAME, user);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId());\n        waitForUserTask(processInstance, \"userTask1\");\n        waitForUserTask(processInstance, \"userTask2\");\n        waitForUserTask(processInstance, \"userTask3\");\n\n        final List<ActivityInstance> activities = getProcessAPI().getActivities(processInstance.getId(), 0, 10);\n        // assign first 2 user tasks to user:\n        getProcessAPI().assignUserTask(activities.get(0).getId(), user.getId());\n        getProcessAPI().assignUserTask(activities.get(1).getId(), user.getId());\n\n        final long myAssignedTasksNb = getProcessAPI().getNumberOfAssignedHumanTaskInstances(user.getId());\n        assertEquals(2L, myAssignedTasksNb);\n        // there should remain 1 usertask not assigned:\n        final long actorPendingTasksNb = getProcessAPI().getNumberOfPendingHumanTaskInstances(user.getId());\n        assertEquals(1L, actorPendingTasksNb);\n\n        disableAndDeleteProcess(definition);\n    }\n\n    private ProcessDefinition preparationBeforeTest(final String actorName) throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.setActorInitiator(actorName).addUserTask(\"userTask1\", actorName);\n        final DesignProcessDefinition processDefinition = processBuilder.done();\n        final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive();\n        businessArchive.setProcessDefinition(processDefinition);\n        final File file = File.createTempFile(\"Actor\", \".bar\");\n        try {\n            file.delete();\n            BusinessArchiveFactory.writeBusinessArchiveToFile(businessArchive.done(), file);\n            final BusinessArchive archive = BusinessArchiveFactory.readBusinessArchive(file);\n            return deployAndEnableProcessWithActor(archive.getProcessDefinition(), actorName, user);\n        } finally {\n            file.delete();\n        }\n    }\n\n    @Test\n    public void getActor() throws Exception {\n        final ProcessDefinition definition = preparationBeforeTest(ACTOR_NAME);\n\n        // Get the existing process def actor list:\n        final List<ActorInstance> actors = getProcessAPI().getActors(definition.getId(), 0, 10,\n                ActorCriterion.NAME_ASC);\n        final ActorInstance actor = actors.get(0);\n        assertEquals(ACTOR_NAME, actor.getName());\n        assertEquals(definition.getId(), actor.getProcessDefinitionId());\n\n        disableAndDeleteProcess(definition);\n    }\n\n    @Test\n    public void getActors() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        final List<String> actorNameList = BuildTestUtil.buildActorAndDescription(processBuilder, 5);\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.getProcess(),\n                actorNameList,\n                Arrays.asList(user, user, user, user, user));\n\n        // test ASC\n        List<ActorInstance> actors = getProcessAPI().getActors(processDefinition.getId(), 0, 2,\n                ActorCriterion.NAME_ASC);\n        assertNotNull(actors);\n        assertEquals(2, actors.size());\n        assertEquals(actorNameList.get(0), actors.get(0).getName());\n        assertEquals(actorNameList.get(1), actors.get(1).getName());\n        actors = getProcessAPI().getActors(processDefinition.getId(), 2, 2, ActorCriterion.NAME_ASC);\n        assertNotNull(actors);\n        assertEquals(2, actors.size());\n        assertEquals(actorNameList.get(2), actors.get(0).getName());\n        assertEquals(actorNameList.get(3), actors.get(1).getName());\n        actors = getProcessAPI().getActors(processDefinition.getId(), 4, 2, ActorCriterion.NAME_ASC);\n        assertNotNull(actors);\n        assertEquals(1, actors.size());\n        assertEquals(actorNameList.get(4), actors.get(0).getName());\n\n        // test PageOutOfRangeException\n        actors = getProcessAPI().getActors(processDefinition.getId(), 6, 2, ActorCriterion.NAME_ASC);\n        assertEquals(0, actors.size());\n\n        // test DESC\n        actors = getProcessAPI().getActors(processDefinition.getId(), 0, 2, ActorCriterion.NAME_DESC);\n        assertNotNull(actors);\n        assertEquals(2, actors.size());\n        assertEquals(actorNameList.get(4), actors.get(0).getName());\n        assertEquals(actorNameList.get(3), actors.get(1).getName());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getActorsByIds() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME).addDescription(\"actor description1\");\n        processBuilder.addActor(\"actor2\").addDescription(\"actor description2\");\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.getProcess(),\n                Arrays.asList(ACTOR_NAME, \"actor2\"),\n                Arrays.asList(user, user));\n\n        final List<ActorInstance> actors = getProcessAPI().getActors(processDefinition.getId(), 0, 5, null);\n        assertNotNull(actors);\n        assertEquals(2, actors.size());\n        final List<Long> actorIds = new ArrayList<>();\n        for (final ActorInstance actorInstance : actors) {\n            actorIds.add(actorInstance.getId());\n        }\n\n        final Map<Long, ActorInstance> actorInstanceRes = getProcessAPI().getActorsFromActorIds(actorIds);\n        assertNotNull(actorInstanceRes);\n        assertEquals(2, actorInstanceRes.size());\n\n        boolean isHas1 = false;\n        boolean isHas2 = false;\n        for (final Entry<Long, ActorInstance> et : actorInstanceRes.entrySet()) {\n            if (ACTOR_NAME.equals(et.getValue().getName())) {\n                isHas1 = true;\n            } else {\n                if (\"actor2\".equals(et.getValue().getName())) {\n                    isHas2 = true;\n                }\n            }\n        }\n        assertTrue(isHas1 && isHas2);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void updateActor() throws Exception {\n        final ProcessDefinition definition = preparationBeforeTest(ACTOR_NAME);\n\n        // Get the existing process def actor list:\n        final List<ActorInstance> actors = getProcessAPI().getActors(definition.getId(), 0, 1, ActorCriterion.NAME_ASC);\n        final ActorInstance actor = actors.get(0);\n        final long actorId = actor.getId();\n        final ActorInstance actorRes = getProcessAPI().getActor(actorId);\n        assertEquals(actorId, actorRes.getId());\n        assertEquals(ACTOR_NAME, actorRes.getName());\n        assertEquals(definition.getId(), actorRes.getProcessDefinitionId());\n\n        final String changedDescription = \"It's okay!\";\n        final String changedName = \"ACTOR_NAME women\";\n        final ActorUpdater descriptor = new ActorUpdater();\n        descriptor.setDescription(changedDescription);\n        descriptor.setDisplayName(changedName);\n        final ActorInstance actorUpdated = getProcessAPI().updateActor(actorId, descriptor);\n\n        assertEquals(actorId, actorUpdated.getId());\n        assertEquals(ACTOR_NAME, actorUpdated.getName());\n        assertEquals(changedDescription, actorUpdated.getDescription());\n        assertEquals(changedName, actorUpdated.getDisplayName());\n        assertEquals(definition.getId(), actorUpdated.getProcessDefinitionId());\n\n        disableAndDeleteProcess(definition);\n    }\n\n    @Test(expected = UpdateException.class)\n    public void updateActorWithEmptyDescriptor() throws Exception {\n        final ActorUpdater descriptor = new ActorUpdater();\n        getProcessAPI().updateActor(1L, descriptor);\n    }\n\n    @Test(expected = UpdateException.class)\n    public void updateActorWithNoDescriptor() throws Exception {\n        getProcessAPI().updateActor(1L, null);\n    }\n\n    @Test\n    public void addActorMembers() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME).addUserTask(\"userTask1\", ACTOR_NAME);\n        final DesignProcessDefinition processDefinition = processBuilder.done();\n\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinition, ACTOR_NAME, user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(definition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        final List<ActorInstance> actors = getProcessAPI().getActors(definition.getId(), 0, 1, ActorCriterion.NAME_ASC);\n        assertEquals(1, actors.size());\n        final ActorInstance actor = actors.get(0);\n        assertEquals(ACTOR_NAME, actor.getName());\n\n        assertEquals(1L, getProcessAPI().getNumberOfActorMembers(actor.getId()));\n\n        final List<ActorMember> actorMembers = getProcessAPI().getActorMembers(actor.getId(), 0, 10);\n        assertEquals(1, actorMembers.size());\n        final ActorMember actorMember = actorMembers.get(0);\n        assertEquals(user.getId(), actorMember.getUserId());\n        assertEquals(-1, actorMember.getGroupId());\n        assertEquals(-1, actorMember.getRoleId());\n\n        getProcessAPI().removeActorMember(actorMember.getId());\n        disableAndDeleteProcess(definition);\n    }\n\n    @Test(expected = AlreadyExistsException.class)\n    public void cantAddTwiceUserToActor() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME).addUserTask(\"userTask1\", ACTOR_NAME);\n\n        final ProcessDefinition processDefinition = deployProcess(\n                new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(processBuilder.done())\n                        .done());\n        getProcessAPI().addUserToActor(ACTOR_NAME, processDefinition, user.getId());\n        getProcessAPI().enableProcess(processDefinition.getId());\n        assertEquals(ActivationState.ENABLED,\n                getProcessAPI().getProcessDeploymentInfo(processDefinition.getId()).getActivationState());\n\n        try {\n            getProcessAPI().addUserToActor(ACTOR_NAME, processDefinition, user.getId());\n        } finally {\n            disableAndDeleteProcess(processDefinition);\n        }\n    }\n\n    @Test(expected = AlreadyExistsException.class)\n    public void cantAddTwiceRoleToActor() throws Exception {\n        final Role role = createRole(\"Quality Manager\");\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME).addUserTask(\"userTask1\", ACTOR_NAME);\n\n        final ProcessDefinition processDefinition = deployProcess(\n                new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(processBuilder.done())\n                        .done());\n        getProcessAPI().addRoleToActor(ACTOR_NAME, processDefinition, role.getId());\n        getProcessAPI().enableProcess(processDefinition.getId());\n        assertEquals(ActivationState.ENABLED,\n                getProcessAPI().getProcessDeploymentInfo(processDefinition.getId()).getActivationState());\n\n        try {\n            getProcessAPI().addRoleToActor(ACTOR_NAME, processDefinition, role.getId());\n        } finally {\n            deleteRoles(role);\n            disableAndDeleteProcess(processDefinition);\n        }\n    }\n\n    @Test(expected = AlreadyExistsException.class)\n    public void cantAddTwiceGroupToActor() throws Exception {\n        final Group group = createGroup(\"Ergonomists\");\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME).addUserTask(\"userTask1\", ACTOR_NAME);\n\n        final ProcessDefinition processDefinition = deployProcess(\n                new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(processBuilder.done())\n                        .done());\n        getProcessAPI().addGroupToActor(ACTOR_NAME, group.getId(), processDefinition);\n        getProcessAPI().enableProcess(processDefinition.getId());\n        assertEquals(ActivationState.ENABLED,\n                getProcessAPI().getProcessDeploymentInfo(processDefinition.getId()).getActivationState());\n\n        try {\n            getProcessAPI().addGroupToActor(ACTOR_NAME, group.getId(), processDefinition);\n        } finally {\n            deleteGroups(group);\n            disableAndDeleteProcess(processDefinition);\n        }\n    }\n\n    @Test(expected = AlreadyExistsException.class)\n    public void cantAddTwiceMembershipToActor() throws Exception {\n        final Role role = createRole(\"Quality Manager\");\n        final Group group = createGroup(\"Ergonomists\");\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME).addUserTask(\"userTask1\", ACTOR_NAME);\n\n        final ProcessDefinition processDefinition = deployProcess(\n                new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(processBuilder.done())\n                        .done());\n        getProcessAPI().addRoleAndGroupToActor(ACTOR_NAME, processDefinition, role.getId(), group.getId());\n        getProcessAPI().enableProcess(processDefinition.getId());\n        assertEquals(ActivationState.ENABLED,\n                getProcessAPI().getProcessDeploymentInfo(processDefinition.getId()).getActivationState());\n\n        try {\n            getProcessAPI().addRoleAndGroupToActor(ACTOR_NAME, processDefinition, role.getId(), group.getId());\n        } finally {\n            deleteGroups(group);\n            deleteRoles(role);\n            disableAndDeleteProcess(processDefinition);\n        }\n    }\n\n    @Test\n    public void getNumberOfUsersOfActor() throws Exception {\n        final User user1 = createUser(\"user1\", \"bpm\");\n        final User user2 = createUser(\"user2\", \"bpm\");\n        final User user3 = createUser(\"user3\", \"bpm\");\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME).addUserTask(\"userTask1\", ACTOR_NAME);\n        final ProcessDefinition definition = deployProcess(\n                new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(processBuilder.done())\n                        .done());\n        getProcessAPI().addUserToActor(ACTOR_NAME, definition, user1.getId());\n        getProcessAPI().addUserToActor(ACTOR_NAME, definition, user2.getId());\n        getProcessAPI().enableProcess(definition.getId());\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(definition.getId());\n\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        // Retrieve actor\n        final List<ActorInstance> actors = getProcessAPI().getActors(definition.getId(), 0, 1, ActorCriterion.NAME_ASC);\n        assertEquals(1, actors.size());\n        final ActorInstance actor = actors.get(0);\n        assertEquals(ACTOR_NAME, actor.getName());\n\n        // Check number of users mapped to actor\n        final long result = getProcessAPI().getNumberOfUsersOfActor(actor.getId());\n        assertEquals(2, result);\n\n        // Retrieve actor members to clean database\n        final List<ActorMember> actorMembers = getProcessAPI().getActorMembers(actor.getId(), 0, 10);\n        getProcessAPI().removeActorMember(actorMembers.get(0).getId());\n        getProcessAPI().removeActorMember(actorMembers.get(1).getId());\n        disableAndDeleteProcess(definition);\n        deleteUser(user1);\n        deleteUser(user2);\n        deleteUser(user3);\n    }\n\n    @Test\n    public void getNumberOfRolesOfActor() throws Exception {\n        final Role role1 = createRole(\"role1\");\n        final Role role2 = createRole(\"role2\");\n        final Role role3 = createRole(\"role3\");\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME).addUserTask(\"roleTask1\", ACTOR_NAME);\n        final ProcessDefinition definition = deployProcess(\n                new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(processBuilder.done())\n                        .done());\n        addMappingOfActorsForRole(ACTOR_NAME, role1.getId(), definition);\n        addMappingOfActorsForRole(ACTOR_NAME, role2.getId(), definition);\n        getProcessAPI().enableProcess(definition.getId());\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(definition.getId());\n\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        // Retrieve actor\n        final List<ActorInstance> actors = getProcessAPI().getActors(definition.getId(), 0, 1, ActorCriterion.NAME_ASC);\n        assertEquals(1, actors.size());\n        final ActorInstance actor = actors.get(0);\n        assertEquals(ACTOR_NAME, actor.getName());\n\n        // Check number of roles mapped to actor\n        final long result = getProcessAPI().getNumberOfRolesOfActor(actor.getId());\n        assertEquals(2, result);\n\n        // Retrieve actor members to clean database\n        final List<ActorMember> actorMembers = getProcessAPI().getActorMembers(actor.getId(), 0, 10);\n        getProcessAPI().removeActorMember(actorMembers.get(0).getId());\n        getProcessAPI().removeActorMember(actorMembers.get(1).getId());\n        disableAndDeleteProcess(definition);\n        deleteRoles(role1, role2, role3);\n    }\n\n    @Test\n    public void getNumberOfGroupsOfActor() throws Exception {\n        final Group group1 = createGroup(\"group1\");\n        final Group group2 = createGroup(\"group2\");\n        final Group group3 = createGroup(\"group3\");\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME).addUserTask(\"groupTask1\", ACTOR_NAME);\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME,\n                group1, group2);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        // Retrieve actor\n        final List<ActorInstance> actors = getProcessAPI().getActors(processDefinition.getId(), 0, 1,\n                ActorCriterion.NAME_ASC);\n        assertEquals(1, actors.size());\n        final ActorInstance actor = actors.get(0);\n        assertEquals(ACTOR_NAME, actor.getName());\n\n        // Check number of groups mapped to actor\n        final long result = getProcessAPI().getNumberOfGroupsOfActor(actor.getId());\n        assertEquals(2, result);\n\n        // Retrieve actor members to clean database\n        final List<ActorMember> actorMembers = getProcessAPI().getActorMembers(actor.getId(), 0, 10);\n        getProcessAPI().removeActorMember(actorMembers.get(0).getId());\n        getProcessAPI().removeActorMember(actorMembers.get(1).getId());\n        disableAndDeleteProcess(processDefinition);\n        deleteGroups(group1, group2, group3);\n    }\n\n    @Test\n    public void getNumberOfMembershipsOfActor() throws Exception {\n        final User user1 = createUser(\"user1\", \"bpm\");\n        final User user2 = createUser(\"user2\", \"bpm\");\n        final Group group1 = createGroup(\"group1\");\n        final Group group2 = createGroup(\"group2\");\n        final Role role1 = createRole(\"role1\");\n        final Role role2 = createRole(\"role2\");\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME).addUserTask(\"userMembershipTask1\", ACTOR_NAME);\n        final ProcessDefinition definition = deployProcess(\n                new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(processBuilder.done())\n                        .done());\n        getProcessAPI().addUserToActor(ACTOR_NAME, definition, user1.getId());\n        addMappingOfActorsForRoleAndGroup(ACTOR_NAME, user1.getId(), group1.getId(), definition);\n        addMappingOfActorsForRoleAndGroup(ACTOR_NAME, user1.getId(), group2.getId(), definition);\n        getProcessAPI().enableProcess(definition.getId());\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(definition.getId());\n\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        // Retrieve actor\n        final List<ActorInstance> actors = getProcessAPI().getActors(definition.getId(), 0, 1, ActorCriterion.NAME_ASC);\n        assertEquals(1, actors.size());\n        final ActorInstance actor = actors.get(0);\n        assertEquals(ACTOR_NAME, actor.getName());\n\n        // Check number of userMemberships mapped to actor\n        final long result = getProcessAPI().getNumberOfMembershipsOfActor(actor.getId());\n        assertEquals(2, result);\n\n        // Retrieve actor members to clean database\n        final List<ActorMember> actorMembers = getProcessAPI().getActorMembers(actor.getId(), 0, 10);\n        getProcessAPI().removeActorMember(actorMembers.get(0).getId());\n        getProcessAPI().removeActorMember(actorMembers.get(1).getId());\n        disableAndDeleteProcess(definition);\n        deleteUsers(user1, user2);\n        deleteRoles(role1, role2);\n        deleteGroups(group1, group2);\n    }\n\n    @Test\n    public void getStartableProcessesForActors() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        processBuilder.setActorInitiator(ACTOR_NAME).addUserTask(\"userTask1\", ACTOR_NAME);\n        final DesignProcessDefinition designProcessDefinition = processBuilder.done();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n\n        final ActorInstance actorInstance = getProcessAPI().getActorInitiator(processDefinition.getId());\n        final Set<Long> actorIds = new HashSet<>();\n        actorIds.add(actorInstance.getId());\n\n        final List<ProcessDeploymentInfo> processDeploymentInfos = getProcessAPI()\n                .getStartableProcessDeploymentInfosForActors(actorIds, 0, 10,\n                        ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertEquals(1, processDeploymentInfos.size());\n        assertEquals(processDefinition.getId(), processDeploymentInfos.get(0).getProcessId());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void testIsAllowedToStartProcess() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        processBuilder.setActorInitiator(ACTOR_NAME).addUserTask(\"userTask1\", ACTOR_NAME);\n        final DesignProcessDefinition designProcessDefinition = processBuilder.done();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n\n        final ActorInstance actorInstance = getProcessAPI().getActorInitiator(processDefinition.getId());\n        final Set<Long> actorIds = new HashSet<>();\n        actorIds.add(actorInstance.getId());\n\n        final boolean isAllowedToStartProcess = getProcessAPI().isAllowedToStartProcess(processDefinition.getId(),\n                actorIds);\n        assertEquals(true, isAllowedToStartProcess);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    private ProcessDefinition deployAndEnableProcessWithHumanTask(final String processName, final String actorName,\n            final String userTaskName)\n            throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder();\n        processBuilder.createNewInstance(processName, PROCESS_VERSION);\n        processBuilder.addActor(actorName);\n        processBuilder.addStartEvent(\"startEvent\");\n        processBuilder.addUserTask(userTaskName, actorName);\n        processBuilder.addEndEvent(\"endEvent\");\n        processBuilder.addTransition(\"startEvent\", userTaskName);\n        processBuilder.addTransition(userTaskName, \"endEvent\");\n        final DesignProcessDefinition designProcessDefinition = processBuilder.done();\n\n        final ProcessDefinition processDef = deployAndEnableProcessWithActor(designProcessDefinition, actorName, user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDef.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        return processDef;\n    }\n\n    @Test\n    public void userHasGotAPendingTaskFromGroup() throws Exception {\n        final ProcessDefinition definition = deployAndEnableProcessWithHumanTask(PROCESS_NAME, ACTOR_NAME, \"deliver\");\n        final ActorInstance actor = checkActors(ACTOR_NAME, definition);\n\n        final Group group = getIdentityAPI().createGroup(\"group1\", null);\n        final Role role = getIdentityAPI().createRole(\"role1\");\n        getIdentityAPI().addUserMembership(user.getId(), group.getId(), role.getId());\n\n        getProcessAPI().addGroupToActor(actor.getId(), group.getId());\n\n        getProcessAPI().startProcess(definition.getId());\n        waitForUserTask(\"deliver\");\n        final List<HumanTaskInstance> tasks = getProcessAPI().getPendingHumanTaskInstances(user.getId(), 0, 10, null);\n        assertEquals(1, tasks.size());\n\n        cleanUserGroupAndRole(group, role);\n        disableAndDeleteProcess(definition);\n    }\n\n    @Test\n    public void mapActorToASubGroup() throws Exception {\n        final ProcessDefinition definition = deployAndEnableProcessWithHumanTask(PROCESS_NAME, ACTOR_NAME, \"deliver\");\n        final ActorInstance actor = checkActors(ACTOR_NAME, definition);\n\n        final Group parent = createGroup(\"parent\");\n        final Group sub = createGroup(\"sub\", \"/parent\");\n        final Role role = getIdentityAPI().createRole(\"role1\");\n        getIdentityAPI().addUserMembership(user.getId(), sub.getId(), role.getId());\n\n        getProcessAPI().addGroupToActor(actor.getId(), parent.getId());\n\n        getProcessAPI().startProcess(definition.getId());\n        waitForPendingTasks(user.getId(), 1);// should have 1 task because john is in the parent\n\n        deleteGroups(parent);\n        deleteRoles(role);\n        disableAndDeleteProcess(definition);\n    }\n\n    @Test\n    public void userHasGotAPendingTaskFromRole() throws Exception {\n        final ProcessDefinition definition = deployAndEnableProcessWithHumanTask(PROCESS_NAME, ACTOR_NAME, \"deliver\");\n        final ActorInstance actor = checkActors(ACTOR_NAME, definition);\n\n        final Group group = getIdentityAPI().createGroup(\"group1\", null);\n        final Role role = getIdentityAPI().createRole(\"role1\");\n        getIdentityAPI().addUserMembership(user.getId(), group.getId(), role.getId());\n\n        getProcessAPI().addRoleToActor(actor.getId(), role.getId());\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId());\n        waitForUserTask(processInstance, \"deliver\");\n\n        final List<HumanTaskInstance> tasks = getProcessAPI().getPendingHumanTaskInstances(user.getId(), 0, 10, null);\n        assertEquals(1, tasks.size());\n\n        cleanUserGroupAndRole(group, role);\n        disableAndDeleteProcess(definition);\n    }\n\n    @Test\n    public void userHasGotAPendingTaskFromRoleAndGroup() throws Exception {\n        final ProcessDefinition definition = deployAndEnableProcessWithHumanTask(PROCESS_NAME, ACTOR_NAME, \"deliver\");\n\n        final ActorInstance actor = checkActors(ACTOR_NAME, definition);\n\n        final Group group = getIdentityAPI().createGroup(\"group1\", null);\n        final Role role = getIdentityAPI().createRole(\"role1\");\n        getIdentityAPI().addUserMembership(user.getId(), group.getId(), role.getId());\n\n        getProcessAPI().addRoleAndGroupToActor(actor.getId(), role.getId(), group.getId());\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId());\n        waitForUserTask(processInstance, \"deliver\");\n\n        final List<HumanTaskInstance> tasks = getProcessAPI().getPendingHumanTaskInstances(user.getId(), 0, 10, null);\n        assertEquals(1, tasks.size());\n\n        cleanUserGroupAndRole(group, role);\n        disableAndDeleteProcess(definition);\n    }\n\n    @Test\n    public void userDontGetAPendingTaskFromRoleOrGroupIfRoleAndGroupNeeded() throws Exception {\n        final String actorName = \"ACTOR_NAME men\";\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder();\n        processBuilder.createNewInstance(PROCESS_NAME, PROCESS_VERSION);\n        processBuilder.addActor(actorName);\n        processBuilder.addStartEvent(\"startEvent\");\n        processBuilder.addUserTask(\"deliver\", actorName);\n        processBuilder.addEndEvent(\"endEvent\");\n        processBuilder.addTransition(\"startEvent\", \"deliver\");\n        processBuilder.addTransition(\"deliver\", \"endEvent\");\n        final DesignProcessDefinition designProcessDefinition = processBuilder.done();\n\n        final ProcessDefinition definition = deployProcess(\n                new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(designProcessDefinition)\n                        .done());\n        final ActorInstance actor = checkActors(actorName, definition);\n\n        final Group group1 = getIdentityAPI().createGroup(\"group1\", null);\n        final Group group2 = getIdentityAPI().createGroup(\"group2\", null);\n        final Role role1 = getIdentityAPI().createRole(\"role1\");\n        final Role role2 = getIdentityAPI().createRole(\"role2\");\n        getIdentityAPI().addUserMembership(user.getId(), group1.getId(), role1.getId());\n\n        getProcessAPI().addRoleAndGroupToActor(actor.getId(), role1.getId(), group2.getId());\n        getProcessAPI().addRoleAndGroupToActor(actor.getId(), role2.getId(), group1.getId());\n\n        getProcessAPI().enableProcess(definition.getId());\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(definition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId());\n        waitForUserTask(processInstance, \"deliver\");\n\n        final List<HumanTaskInstance> tasks = getProcessAPI().getPendingHumanTaskInstances(user.getId(), 0, 10, null);\n        assertEquals(0, tasks.size());\n\n        cleanUserGroupAndRole(group1, role1);\n        getIdentityAPI().deleteGroup(group2.getId());\n        getIdentityAPI().deleteRole(role2.getId());\n        disableAndDeleteProcess(definition);\n    }\n\n    @Test\n    public void getNumberOfActors() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        final List<String> actorNames = BuildTestUtil.buildActorAndDescription(processBuilder, 2);\n        processBuilder.addActor(\"initiator\", true);\n\n        // Process def : one starting automatic activity that fires 3 human activities:\n        final DesignProcessDefinition designProcessDefinition = processBuilder.addAutomaticTask(\"step1\")\n                .addUserTask(\"step2\", actorNames.get(0))\n                .addUserTask(\"step3\", actorNames.get(0)).addUserTask(\"step4\", actorNames.get(0))\n                .addTransition(\"step1\", \"step2\")\n                .addTransition(\"step1\", \"step3\").addTransition(\"step1\", \"step4\").getProcess();\n\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(designProcessDefinition).done();\n        final ProcessDefinition processDefinition = deployProcess(businessArchive);\n        int nbOfActors = getProcessAPI().getNumberOfActors(processDefinition.getId());\n        assertEquals(3, nbOfActors);\n\n        final List<ActorInstance> actors = getProcessAPI().getActors(processDefinition.getId(), 0, 5,\n                ActorCriterion.NAME_ASC);\n        assertEquals(3, actors.size());\n        for (final ActorInstance actor : actors) {\n            getProcessAPI().addUserToActor(actor.getId(), user.getId());\n        }\n\n        getProcessAPI().enableProcess(processDefinition.getId());\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        nbOfActors = getProcessAPI().getNumberOfActors(processDefinition.getId());\n        assertEquals(3, nbOfActors);\n        waitForUserTask(processInstance, \"step2\");\n        waitForUserTask(processInstance, \"step3\");\n        waitForUserTask(processInstance, \"step4\");\n\n        nbOfActors = getProcessAPI().getNumberOfActors(processDefinition.getId());\n        assertEquals(3, nbOfActors);\n\n        // clean all data for test\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    private ActorInstance checkActors(final String ACTOR_NAME, final ProcessDefinition definition) {\n        final List<ActorInstance> actors = getProcessAPI().getActors(definition.getId(), 0, 1, ActorCriterion.NAME_ASC);\n        assertEquals(1, actors.size());\n        final ActorInstance actor = actors.get(0);\n        assertEquals(ACTOR_NAME, actor.getName());\n        return actor;\n    }\n\n    private void cleanUserGroupAndRole(final Group group, final Role role) throws DeletionException {\n        getIdentityAPI().deleteGroup(group.getId());\n        getIdentityAPI().deleteRole(role.getId());\n    }\n\n    @Test\n    public void getPaginatedStartableProcessesForActors() throws Exception {\n        final ProcessDefinition firstDefinition = getProcessDefinition(PROCESS_NAME);\n        final ProcessDefinition secondDefinition = getProcessDefinition(\"secondProcess\");\n\n        final ActorInstance firstActorInstance = getProcessAPI().getActorInitiator(firstDefinition.getId());\n        final ActorInstance secondActorInstance = getProcessAPI().getActorInitiator(secondDefinition.getId());\n        final Set<Long> actorIds = new HashSet<>();\n        actorIds.add(firstActorInstance.getId());\n        actorIds.add(secondActorInstance.getId());\n\n        final List<ProcessDeploymentInfo> processDeploymentInfos = getProcessAPI()\n                .getStartableProcessDeploymentInfosForActors(actorIds, 0, 1,\n                        ProcessDeploymentInfoCriterion.NAME_ASC);\n        assertEquals(1, processDeploymentInfos.size());\n        assertEquals(firstDefinition.getId(), processDeploymentInfos.get(0).getProcessId());\n\n        disableAndDeleteProcess(firstDefinition);\n        disableAndDeleteProcess(secondDefinition);\n    }\n\n    private ProcessDefinition getProcessDefinition(final String processName) throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(processName,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        processBuilder.setActorInitiator(ACTOR_NAME).addUserTask(\"userTask1\", ACTOR_NAME);\n        final DesignProcessDefinition designProcessDefinition = processBuilder.done();\n        return deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/comment/CommentIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.process.comment;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.comment.ArchivedComment;\nimport org.bonitasoft.engine.bpm.comment.ArchivedCommentsSearchDescriptor;\nimport org.bonitasoft.engine.bpm.comment.Comment;\nimport org.bonitasoft.engine.bpm.comment.SearchCommentsDescriptor;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.junit.Test;\n\npublic class CommentIT extends TestWithUser {\n\n    @Test\n    public void should_be_able_to_add_search_and_archive_comments() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        String taskName = \"userTask1\";\n        final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask(taskName, ACTOR_NAME)\n                .getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n\n        final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId());\n        // get comments before add new.\n        final List<Comment> originalComments = getProcessAPI().searchComments(\n                new SearchOptionsBuilder(0, 100).filter(SearchCommentsDescriptor.PROCESS_INSTANCE_ID, pi0.getId())\n                        .done())\n                .getResult();\n        // add comments\n        final String commentContent = \"added comment 0\";\n        final Comment comment = getProcessAPI().addProcessComment(pi0.getId(), commentContent);\n        assertNotNull(comment);\n        assertEquals(commentContent, comment.getContent());\n        getProcessAPI().addProcessComment(pi0.getId(), \"added comment 1\");\n        Comment comment2 = getProcessAPI().addProcessComment(pi0.getId(), \"added comment 2\");\n        // get comments again.\n        final List<Comment> commentsAfterAdd = getProcessAPI().searchComments(\n                new SearchOptionsBuilder(0, 100)\n                        .filter(SearchCommentsDescriptor.PROCESS_INSTANCE_ID, pi0.getId())\n                        .sort(SearchCommentsDescriptor.POSTDATE, Order.ASC)\n                        .done())\n                .getResult();\n        assertThat(commentsAfterAdd).as(\"should have 3 more comments\").hasSize(originalComments.size() + 3);\n        assertThat(commentsAfterAdd.get(1).getContent()).isEqualTo(\"added comment 1\");\n\n        // Archive comments\n        waitForUserTaskAndExecuteIt(pi0, taskName, user);\n        waitForProcessToFinish(pi0);\n\n        final SearchResult<ArchivedComment> searchArchivedComments = getProcessAPI().searchArchivedComments(\n                new SearchOptionsBuilder(0, 10)\n                        .filter(ArchivedCommentsSearchDescriptor.PROCESS_INSTANCE_ID, pi0.getId()).done());\n        assertThat(searchArchivedComments.getCount())\n                .as(\"At least 3 comments should have been retrieved (+ possible automatic comments)\")\n                .isGreaterThanOrEqualTo(3);\n\n        // ensure we can retrieve archived comments using the SOURCE_OBJECT_ID field\n        final SearchResult<ArchivedComment> searchArchivedCommentsBySourceObjectId = getProcessAPI()\n                .searchArchivedComments(\n                        new SearchOptionsBuilder(0, 30)\n                                .filter(ArchivedCommentsSearchDescriptor.SOURCE_OBJECT_ID, comment2.getId()).done());\n        assertThat(searchArchivedCommentsBySourceObjectId.getCount()).isEqualTo(1);\n        assertThat(searchArchivedCommentsBySourceObjectId.getResult().get(0).getContent()).isEqualTo(\"added comment 2\");\n\n        // ensure we can retrieve archived comments using the CONTENT field\n        final SearchResult<ArchivedComment> searchArchivedCommentsByContent = getProcessAPI().searchArchivedComments(\n                new SearchOptionsBuilder(0, 30).filter(ArchivedCommentsSearchDescriptor.CONTENT, \"added comment 2\")\n                        .done());\n        assertThat(searchArchivedCommentsByContent.getCount()).isEqualTo(1);\n        assertThat(searchArchivedCommentsByContent.getResult().get(0).getSourceObjectId()).isEqualTo(comment2.getId());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/data/ActivityDataDefinitionIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.process.data;\n\nimport static org.bonitasoft.engine.matchers.NameMatcher.nameIs;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertThat;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.data.DataDefinition;\nimport org.bonitasoft.engine.bpm.flownode.ActivityDefinitionNotFoundException;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.impl.AutomaticTaskDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.junit.Test;\n\npublic class ActivityDataDefinitionIT extends TestWithUser {\n\n    @Test\n    public void getDataDefinitionsHavingComplexeInitialValue() throws Exception {\n        final Expression scriptExpression = new ExpressionBuilder().createGroovyScriptExpression(\"a+b\", \"a+b\",\n                String.class.getName(),\n                new ExpressionBuilder().createDataExpression(\"a\", String.class.getName()),\n                new ExpressionBuilder().createDataExpression(\"b\", String.class.getName()));\n\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addShortTextData(\"a\",\n                new ExpressionBuilder().createConstantStringExpression(\"avalue\"));\n        processDefinitionBuilder.addShortTextData(\"b\",\n                new ExpressionBuilder().createConstantStringExpression(\"bvalue\"));\n        processDefinitionBuilder.addUserTask(\"step1\", ACTOR_NAME).addData(\"myData\", String.class.getName(),\n                scriptExpression);\n        final DesignProcessDefinition designProcessDefinition = processDefinitionBuilder.done();\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final List<DataDefinition> dataDefList = getProcessAPI().getActivityDataDefinitions(processDefinition.getId(),\n                \"step1\", 0, 10);\n        assertEquals(1, dataDefList.size());\n        assertEquals(2, dataDefList.get(0).getDefaultValueExpression().getDependencies().size());\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getNumberOfActivityDataDefinitions() throws Exception {\n        final String intDataName = \"luckyNum\";\n        final Expression intDefaultExp = new ExpressionBuilder().createConstantIntegerExpression(10);\n        final String strDataName = \"luckyColor\";\n        final Expression strDefaultExp = new ExpressionBuilder().createConstantStringExpression(\"blue\");\n        final String taskName = \"autoTask1\";\n\n        // process level\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        // activity level\n        final AutomaticTaskDefinitionBuilder activityDefinitionBuilder = processDefinitionBuilder\n                .addAutomaticTask(taskName);\n        activityDefinitionBuilder.addIntegerData(intDataName, intDefaultExp);\n        activityDefinitionBuilder.addShortTextData(strDataName, strDefaultExp);\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),\n                ACTOR_NAME, user);\n\n        final int number = getProcessAPI().getNumberOfActivityDataDefinitions(processDefinition.getId(), taskName);\n        assertEquals(2, number);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test(expected = ActivityDefinitionNotFoundException.class)\n    public void getActivityDataDefinitionsWithException() throws Exception {\n        final String intDataName = \"luckyNum\";\n        final Expression intDefaultExp = new ExpressionBuilder().createConstantIntegerExpression(10);\n        final String strDataName = \"luckyColor\";\n        final Expression strDefaultExp = new ExpressionBuilder().createConstantStringExpression(\"blue\");\n        final String taskName = \"autoTask1\";\n\n        // process level\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        // activity level\n        final AutomaticTaskDefinitionBuilder activityDefinitionBuilder = processDefinitionBuilder\n                .addAutomaticTask(taskName);\n        activityDefinitionBuilder.addIntegerData(intDataName, intDefaultExp);\n        activityDefinitionBuilder.addShortTextData(strDataName, strDefaultExp);\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),\n                ACTOR_NAME, user);\n        try {\n            getProcessAPI().getActivityDataDefinitions(processDefinition.getId(), taskName + \"qwer\", 0, 5);\n        } finally {\n            disableAndDeleteProcess(processDefinition);\n        }\n    }\n\n    @Test\n    public void getActivityDataDefinitions() throws Exception {\n        final String intDataName = \"luckyNum\";\n        final Expression intDefaultExp = new ExpressionBuilder().createConstantIntegerExpression(10);\n        final String strDataName = \"luckyColor\";\n        final Expression strDefaultExp = new ExpressionBuilder().createConstantStringExpression(\"blue\");\n        final String taskName = \"autoTask1\";\n\n        // process level\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        // activity level\n        final AutomaticTaskDefinitionBuilder activityDefinitionBuilder = processDefinitionBuilder\n                .addAutomaticTask(taskName);\n        activityDefinitionBuilder.addIntegerData(intDataName, intDefaultExp);\n        activityDefinitionBuilder.addShortTextData(strDataName, strDefaultExp);\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),\n                ACTOR_NAME, user);\n\n        final List<DataDefinition> dataDefList = getProcessAPI().getActivityDataDefinitions(processDefinition.getId(),\n                taskName, 0, 5);\n        assertEquals(2, dataDefList.size());\n\n        final DataDefinition dataDef1 = dataDefList.get(0);\n        assertEquals(intDataName, dataDef1.getName());\n        assertEquals(Integer.class.getName(), dataDef1.getClassName());\n        // assertEquals(intDefaultExp,dataDef1.getDefaultValueExpression());\n\n        final DataDefinition dataDef2 = dataDefList.get(1);\n        assertEquals(strDataName, dataDef2.getName());\n        assertEquals(String.class.getName(), dataDef2.getClassName());\n        // assertEquals(strDefaultExp,dataDef2.getDefaultValueExpression());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getActivityDataDefinitionsPaginated() throws Exception {\n        final String intDataName = \"luckyNum\";\n        final Expression intDefaultExp = new ExpressionBuilder().createConstantIntegerExpression(10);\n        final String strDataName = \"color\";\n        final Expression strDefaultExp = new ExpressionBuilder().createConstantStringExpression(\"blue\");\n        final String taskName = \"autoTask1\";\n\n        // process level\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        // activity level\n        final AutomaticTaskDefinitionBuilder activityDefinitionBuilder = processDefinitionBuilder\n                .addAutomaticTask(taskName);\n        activityDefinitionBuilder.addIntegerData(intDataName, intDefaultExp);\n        for (int i = 1; i <= 10; i++) {\n            activityDefinitionBuilder.addShortTextData(strDataName + i, strDefaultExp);\n        }\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),\n                ACTOR_NAME, user);\n        List<DataDefinition> processDataDefinitions = getProcessAPI()\n                .getActivityDataDefinitions(processDefinition.getId(), taskName, 0, 5);\n        assertEquals(5, processDataDefinitions.size());\n        assertThat(processDataDefinitions.get(0), nameIs(\"luckyNum\"));\n        assertThat(processDataDefinitions.get(1), nameIs(\"color1\"));\n        assertThat(processDataDefinitions.get(2), nameIs(\"color2\"));\n        assertThat(processDataDefinitions.get(3), nameIs(\"color3\"));\n        assertThat(processDataDefinitions.get(4), nameIs(\"color4\"));\n        processDataDefinitions = getProcessAPI().getActivityDataDefinitions(processDefinition.getId(), taskName, 5, 5);\n        assertEquals(5, processDataDefinitions.size());\n        assertThat(processDataDefinitions.get(0), nameIs(\"color5\"));\n        assertThat(processDataDefinitions.get(1), nameIs(\"color6\"));\n        assertThat(processDataDefinitions.get(2), nameIs(\"color7\"));\n        assertThat(processDataDefinitions.get(3), nameIs(\"color8\"));\n        assertThat(processDataDefinitions.get(4), nameIs(\"color9\"));\n        processDataDefinitions = getProcessAPI().getActivityDataDefinitions(processDefinition.getId(), taskName, 10, 5);\n        assertEquals(1, processDataDefinitions.size());\n        assertThat(processDataDefinitions.get(0), nameIs(\"color10\"));\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/data/ActivityDataInstanceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.process.data;\n\nimport static org.junit.Assert.*;\n\nimport java.io.Serializable;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.data.ArchivedDataInstance;\nimport org.bonitasoft.engine.bpm.data.ArchivedDataNotFoundException;\nimport org.bonitasoft.engine.bpm.data.DataInstance;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.process.ActivationState;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder;\nimport org.bonitasoft.engine.exception.ExceptionContext;\nimport org.bonitasoft.engine.exception.RetrieveException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.expression.ExpressionType;\nimport org.junit.Test;\n\npublic class ActivityDataInstanceIT extends TestWithUser {\n\n    @Test\n    public void getDataFromActivity() throws Exception {\n        final DesignProcessDefinition processDef = new ProcessDefinitionBuilder().createNewInstance(\"My_Process\", \"1.0\")\n                .addActor(ACTOR_NAME)\n                .addDescription(\"Delivery all day and night long\").addUserTask(\"step1\", ACTOR_NAME)\n                .addIntegerData(\"var1\", new ExpressionBuilder().createConstantIntegerExpression(1)).getProcess();\n\n        assertDataOnActivityInstances(processDef, 1);\n    }\n\n    @Test\n    public void getDataFromActivityThatIsAlsoInParent() throws Exception {\n        final DesignProcessDefinition processDef = new ProcessDefinitionBuilder().createNewInstance(\"My_Process\", \"1.1\")\n                .addActor(ACTOR_NAME)\n                .addDescription(\"Delivery all day and night long\")\n                .addIntegerData(\"var1\", new ExpressionBuilder().createConstantIntegerExpression(1))\n                .addUserTask(\"step1\", ACTOR_NAME)\n                .addIntegerData(\"var1\", new ExpressionBuilder().createConstantIntegerExpression(2)).getProcess();\n\n        assertDataOnActivityInstances(processDef, 2);\n    }\n\n    @Test\n    public void getDataFromActivityThatIsOnlyInParent() throws Exception {\n        final DesignProcessDefinition processDef = new ProcessDefinitionBuilder().createNewInstance(\"My_Process\", \"1.1\")\n                .addActor(ACTOR_NAME)\n                .addDescription(\"Delivery all day and night long\")\n                .addIntegerData(\"var1\", new ExpressionBuilder().createConstantIntegerExpression(3))\n                .addUserTask(\"step1\", ACTOR_NAME).getProcess();\n\n        assertDataOnActivityInstances(processDef, 3);\n    }\n\n    private void assertDataOnActivityInstances(final DesignProcessDefinition processDef, final int expectedNumber)\n            throws Exception {\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef,\n                org.bonitasoft.engine.test.APITestUtil.ACTOR_NAME, user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        // test execution\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n\n        // verify there are data\n        final List<DataInstance> processDataInstances = getProcessAPI().getActivityDataInstances(step1Id, 0, 10);\n        assertFalse(processDataInstances.isEmpty());\n        assertEquals(1, processDataInstances.size());\n        assertEquals(\"var1\", processDataInstances.get(0).getName());\n        assertEquals(expectedNumber, processDataInstances.get(0).getValue());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void updateIntegerActivityDataInstance() throws Exception {\n        updateActivityDataInstance(1, 2);\n    }\n\n    @Test\n    public void updateBooleanActivityDataInstance() throws Exception {\n        updateActivityDataInstance(false, true);\n    }\n\n    private void updateActivityDataInstance(final Serializable defaultDataValue, final Serializable updatedDataValue)\n            throws Exception {\n        final UserTaskDefinitionBuilder addUserTask = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process\", \"1.0\").addActor(ACTOR_NAME)\n                .addDescription(\"Delivery all day and night long\").addUserTask(\"step1\", ACTOR_NAME);\n        if (defaultDataValue instanceof Boolean) {\n            addUserTask.addBooleanData(\"var1\",\n                    new ExpressionBuilder().createConstantBooleanExpression((Boolean) defaultDataValue));\n        } else if (defaultDataValue instanceof Integer) {\n            addUserTask.addIntegerData(\"var1\",\n                    new ExpressionBuilder().createConstantIntegerExpression((Integer) defaultDataValue));\n        } else {\n            throw new Exception(\"This test does not support data type different from (boolean, integer)\");\n        }\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(addUserTask.getProcess(),\n                ACTOR_NAME, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long activityInstanceId = waitForUserTask(processInstance, \"step1\");\n\n        final DataInstance dataInstance = getActivityDataInstance(activityInstanceId);\n        assertEquals(\"var1\", dataInstance.getName());\n        assertEquals(defaultDataValue, dataInstance.getValue());\n\n        getProcessAPI().updateActivityDataInstance(\"var1\", activityInstanceId, updatedDataValue);\n        final DataInstance updatedData = getActivityDataInstance(activityInstanceId);\n        assertEquals(updatedDataValue, updatedData.getValue());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void updateActivityInstanceOperationsWithStringDataNotTransient() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = createProcessWithActorAndHumanTaskAndInitStringDataNotTransient();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n        final long activityInstanceId = waitForUserTask(processInstance, \"step1\");\n\n        // Update data instance\n        final String updatedValue = \"afterUpdate\";\n        updateActivityInstanceVariablesWithOperations(updatedValue, activityInstanceId, \"dataName\", false);\n        assertEquals(updatedValue, getProcessAPI().getActivityDataInstance(\"dataName\", activityInstanceId).getValue());\n\n        // Clean\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void updateActivityInstanceVariable() throws Exception {\n        final DesignProcessDefinition processDef = createProcessWithActorAndHumanTaskAndInitStringDataNotTransient();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, ACTOR_NAME, user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n        final long activityInstanceId = waitForUserTask(processInstance, \"step1\");\n\n        final String updatedValue = \"afterUpdate\";\n        final Map<String, Serializable> variables = new HashMap<>(2);\n        variables.put(\"dataName\", updatedValue);\n        getProcessAPI().updateActivityInstanceVariables(activityInstanceId, variables);\n\n        final DataInstance dataInstance = getProcessAPI().getActivityDataInstance(\"dataName\", activityInstanceId);\n        assertEquals(updatedValue, dataInstance.getValue());\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test(expected = UpdateException.class)\n    public void cannotUpdateAnActivityInstanceVariable() throws Exception {\n        final DesignProcessDefinition processDef = createProcessWithActorAndHumanTaskAndInitStringDataNotTransient();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, ACTOR_NAME, user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n        final long activityInstanceId = waitForUserTask(processInstance, \"step1\");\n\n        final Map<String, Serializable> variables = new HashMap<>(2);\n        variables.put(\"dataName1\", \"afterUpdate\");\n        try {\n            getProcessAPI().updateActivityInstanceVariables(activityInstanceId, variables);\n        } finally {\n            disableAndDeleteProcess(processDefinition);\n        }\n    }\n\n    private DataInstance getActivityDataInstance(final long activityInstanceId) {\n        final List<DataInstance> activityDataInstances = getProcessAPI().getActivityDataInstances(activityInstanceId, 0,\n                10);\n        assertEquals(1, activityDataInstances.size());\n        return activityDataInstances.get(0);\n    }\n\n    @Test(expected = RetrieveException.class)\n    public void dataNotAvailableAfterArchiveFromActivity() throws Exception {\n        final DesignProcessDefinition processDef = new ProcessDefinitionBuilder().createNewInstance(\"My_Process\", \"1.0\")\n                .addActor(ACTOR_NAME)\n                .addDescription(\"Delivery all day and night long\").addUserTask(\"step1\", ACTOR_NAME)\n                .addIntegerData(\"var1\", new ExpressionBuilder().createConstantIntegerExpression(1)).getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, ACTOR_NAME, user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        // test execution\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n\n        // verify the retrieved data\n        List<DataInstance> processDataInstances = getProcessAPI().getActivityDataInstances(step1Id, 0, 10);\n        assertFalse(processDataInstances.isEmpty());\n        assertEquals(1, processDataInstances.size());\n        assertEquals(\"var1\", processDataInstances.get(0).getName());\n        assertEquals(1, processDataInstances.get(0).getValue());\n\n        // Execute pending task\n        final List<ActivityInstance> activities = getProcessAPI().getActivities(processInstance.getId(), 0, 200);\n        final ActivityInstance activityInstance = activities.iterator().next();\n        assignAndExecuteStep(activityInstance, user.getId());\n        waitForProcessToFinish(processInstance);\n\n        // retrieve data after process has finished\n        try {\n            getProcessAPI().getActivityDataInstances(processInstance.getId(), 0, 10);\n        } finally {\n            disableAndDeleteProcess(processDefinition);\n        }\n    }\n\n    @Test\n    public void executeProcess3TimesWithNotInitializedData() throws Exception {\n        final Expression dataExpr = new ExpressionBuilder().createDataExpression(\"booleanData\",\n                Boolean.class.getName());\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addUserTask(\"step1\", ACTOR_NAME).addBooleanData(\"booleanData\", null);\n        processDefinitionBuilder.addUserTask(\"step2\", ACTOR_NAME).addUserTask(\"step3\", ACTOR_NAME);\n        processDefinitionBuilder.addTransition(\"step1\", \"step2\", dataExpr).addDefaultTransition(\"step1\", \"step3\");\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),\n                ACTOR_NAME, user);\n\n        // Start first process, and wait the first step\n        final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1_1Id = waitForUserTask(processInstance1, \"step1\");\n        // Set data to true, for the first instance\n        getProcessAPI().updateActivityDataInstance(\"booleanData\", step1_1Id, true);\n\n        // Start second process, and wait the first step\n        final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1_2Id = waitForUserTask(processInstance2, \"step1\");\n        // Set data to false, for the second instance\n        getProcessAPI().updateActivityDataInstance(\"booleanData\", step1_2Id, false);\n\n        // Start third process, and wait the first step\n        final ProcessInstance processInstance3 = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1_3Id = waitForUserTask(processInstance3, \"step1\");\n        // Set data to true, for the third instance\n        getProcessAPI().updateActivityDataInstance(\"booleanData\", step1_3Id, true);\n\n        // Execute all step1\n        assignAndExecuteStep(step1_1Id, user);\n        waitForUserTask(processInstance1, \"step2\");\n        assignAndExecuteStep(step1_2Id, user);\n        waitForUserTask(processInstance2, \"step3\");\n        assignAndExecuteStep(step1_3Id, user);\n        waitForUserTask(processInstance3, \"step2\");\n\n        // Check that only these 3 steps are pending\n        assertEquals(3, getProcessAPI().getNumberOfPendingHumanTaskInstances(user.getId()));\n\n        // Clean-up\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void runProcessWithInvalidInitialValue() throws Exception {\n        final ProcessDefinitionBuilder createNewInstance = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processwithIntegerData\", \"1.0\");\n        createNewInstance.addAutomaticTask(\"step1\").addIntegerData(\"intdata\",\n                new ExpressionBuilder().createExpression(\"d\", \"d\", Integer.class.getName(),\n                        ExpressionType.TYPE_CONSTANT));\n        final ProcessDefinition processDefinition = deployAndEnableProcess(createNewInstance.done());\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        final ActivityInstance failedTask = waitForTaskToFail(processInstance);\n        assertEquals(\"step1\", failedTask.getName());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void processWithDataHavingSameNameInTwoContainer() throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addUserTask(\"step1\", ACTOR_NAME).addShortTextData(\"a\",\n                new ExpressionBuilder().createConstantStringExpression(\"step1\"));\n        processDefinitionBuilder.addUserTask(\"step2\", ACTOR_NAME).addShortTextData(\"a\",\n                new ExpressionBuilder().createConstantStringExpression(\"step2\"));\n        processDefinitionBuilder.addShortTextData(\"a\",\n                new ExpressionBuilder().createConstantStringExpression(\"process\"));\n        final DesignProcessDefinition designProcessDefinition = processDefinitionBuilder.done();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        final HumanTaskInstance step1 = waitForUserTaskAndGetIt(processInstance, \"step1\");\n        assertEquals(step1.getName(), getProcessAPI().getActivityDataInstance(\"a\", step1.getId()).getValue());\n\n        final HumanTaskInstance step2 = waitForUserTaskAndGetIt(processInstance, \"step2\");\n        assertEquals(step2.getName(), getProcessAPI().getActivityDataInstance(\"a\", step2.getId()).getValue());\n\n        assertEquals(\"process\", getProcessAPI().getProcessDataInstance(\"a\", processInstance.getId()).getValue());\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    private DesignProcessDefinition createProcessWithActorAndHumanTaskAndInitStringDataNotTransient() throws Exception {\n        return createProcessWithActorAndHumanTaskAndStringData(\n                new ExpressionBuilder().createConstantStringExpression(\"beforeUpdate\")).done();\n    }\n\n    private ProcessDefinitionBuilder createProcessWithActorAndHumanTaskAndStringData(final Expression defaultValue) {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION);\n        processDefinitionBuilder.addActor(ACTOR_NAME).addDescription(\"Delivery all day and night long\");\n        final UserTaskDefinitionBuilder userTaskBuilder = processDefinitionBuilder.addUserTask(\"step1\", ACTOR_NAME);\n        userTaskBuilder.addShortTextData(\"dataName\", defaultValue);\n        return processDefinitionBuilder;\n    }\n\n    @Test\n    public void canGetDataInstanceWhenThereAreTransientData() throws Exception {\n        final String userTaskName = \"task1\";\n        final ProcessDefinition processDefinition = deployAndEnableProcWithPersistedAndTransientVariable();\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long userTaskId = waitForUserTask(processInstance, userTaskName);\n\n        final Map<Expression, Map<String, Serializable>> expressions = new HashMap<>(2);\n        final Expression persistedVariableExpression = new ExpressionBuilder().createDataExpression(\"persistedVariable\",\n                String.class.getName());\n        final Expression transientVariableExpression = new ExpressionBuilder()\n                .createTransientDataExpression(\"transientVariable\", String.class.getName());\n        expressions.put(persistedVariableExpression, null);\n        expressions.put(transientVariableExpression, null);\n\n        final Map<String, Serializable> expressionResult = getProcessAPI()\n                .evaluateExpressionsOnActivityInstance(userTaskId, expressions);\n        assertEquals(\"default\", expressionResult.get(persistedVariableExpression.getName()));\n        assertEquals(\"default\", expressionResult.get(transientVariableExpression.getName()));\n\n        disableAndDeleteProcess(processDefinition.getId());\n    }\n\n    @Test\n    public void getArchivedActivityDataInstance() throws Exception {\n        final String dataName = \"title\";\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"ProcessToArchive\",\n                \"1.0\");\n        builder.addActor(ACTOR_NAME);\n        builder.addUserTask(\"step\", ACTOR_NAME).addShortTextData(dataName,\n                new ExpressionBuilder().createConstantStringExpression(\"1\"));\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.getProcess(), ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long userTaskId = waitForUserTask(processInstance, \"step\");\n        Thread.sleep(10);\n        getProcessAPI().updateActivityDataInstance(dataName, userTaskId, \"2\");\n\n        final ArchivedDataInstance archivedData = getProcessAPI().getArchivedActivityDataInstance(dataName, userTaskId);\n        assertEquals(\"2\", archivedData.getValue());\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getArchivedActivityDataInstanceFromAnArchivedProcess() throws Exception {\n        final String dataName = \"title\";\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"ProcessToArchive\",\n                \"1.0\");\n        builder.addActor(ACTOR_NAME);\n        builder.addUserTask(\"step\", ACTOR_NAME).addShortTextData(dataName,\n                new ExpressionBuilder().createConstantStringExpression(\"1\"));\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.getProcess(), ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long userTaskId = waitForUserTask(processInstance, \"step\");\n        Thread.sleep(10); // to be sure that the archive date is different from the initial state date\n        getProcessAPI().updateActivityDataInstance(dataName, userTaskId, \"2\");\n        assignAndExecuteStep(userTaskId, user.getId());\n        waitForProcessToFinish(processInstance.getId());\n\n        final ArchivedDataInstance archivedData = getProcessAPI().getArchivedActivityDataInstance(dataName, userTaskId);\n        assertEquals(\"2\", archivedData.getValue());\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getArchivedActivityDataInstances() throws Exception {\n        final String dataName = \"title\";\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"ProcessToArchive\",\n                \"1.0\");\n        builder.addShortTextData(dataName, new ExpressionBuilder().createConstantStringExpression(\"0\"));\n        builder.addActor(ACTOR_NAME);\n        final UserTaskDefinitionBuilder taskDefinitionBuilder = builder.addUserTask(\"step\", ACTOR_NAME);\n        taskDefinitionBuilder.addShortTextData(dataName, new ExpressionBuilder().createConstantStringExpression(\"1\")); // overrides the process data with the same name\n        taskDefinitionBuilder.addShortTextData(\"job\", new ExpressionBuilder().createConstantStringExpression(\"job\"));\n        taskDefinitionBuilder.addShortTextData(\"desc\", new ExpressionBuilder().createConstantStringExpression(\"desc\"));\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.getProcess(), ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long userTaskId = waitForUserTask(processInstance, \"step\");\n        Thread.sleep(10);\n        getProcessAPI().updateActivityDataInstance(dataName, userTaskId, \"2\");\n        assignAndExecuteStep(userTaskId, user);\n        waitForProcessToFinish(processInstance.getId());\n\n        List<ArchivedDataInstance> archivedDataInstances = getProcessAPI().getArchivedActivityDataInstances(userTaskId,\n                0, 10);\n        assertEquals(3, archivedDataInstances.size());\n        ArchivedDataInstance archivedDataInstance = getArchivedDataInstance(archivedDataInstances, dataName);\n        assertEquals(\"2\", archivedDataInstance.getValue());\n        archivedDataInstance = getArchivedDataInstance(archivedDataInstances, \"job\");\n        assertEquals(\"job\", archivedDataInstance.getValue());\n        archivedDataInstance = getArchivedDataInstance(archivedDataInstances, \"desc\");\n        assertEquals(\"desc\", archivedDataInstance.getValue());\n\n        archivedDataInstances = getProcessAPI().getArchivedActivityDataInstances(userTaskId, 0, 1);\n        assertEquals(1, archivedDataInstances.size());\n        archivedDataInstance = getArchivedDataInstance(archivedDataInstances, dataName);\n        assertEquals(\"2\", archivedDataInstance.getValue());\n\n        archivedDataInstances = getProcessAPI().getArchivedActivityDataInstances(userTaskId, 1, 10);\n        assertEquals(2, archivedDataInstances.size());\n        archivedDataInstance = getArchivedDataInstance(archivedDataInstances, \"job\");\n        assertEquals(\"job\", archivedDataInstance.getValue());\n        archivedDataInstance = getArchivedDataInstance(archivedDataInstances, \"desc\");\n        assertEquals(\"desc\", archivedDataInstance.getValue());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getUnknownArchivedActivityDataInstance() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"ProcessToArchive\",\n                \"1.0\");\n        builder.addActor(ACTOR_NAME);\n        builder.addUserTask(\"step\", ACTOR_NAME);\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.getProcess(), ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long userTaskId = waitForUserTask(processInstance, \"step\");\n\n        try {\n            getProcessAPI().getArchivedProcessDataInstance(\"o\", userTaskId);\n            fail(\"The data named 'o' does not exists\");\n        } catch (final ArchivedDataNotFoundException ignored) {\n            // Do nothing\n        } finally {\n            disableAndDeleteProcess(processDefinition);\n        }\n    }\n\n    private ProcessDefinition deployAndEnableProcWithPersistedAndTransientVariable() throws Exception {\n        final String startName = \"start\";\n        final String endName = \"end\";\n        final Expression defaultValue = new ExpressionBuilder().createConstantStringExpression(\"default\");\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"proc\", \"1.0\");\n        builder.addActor(ACTOR_NAME);\n        builder.addStartEvent(startName);\n        final UserTaskDefinitionBuilder taskBuilder = builder.addUserTask(\"task1\", ACTOR_NAME);\n        taskBuilder.addShortTextData(\"persistedVariable\", defaultValue);\n        taskBuilder.addShortTextData(\"transientVariable\", defaultValue).isTransient();\n        builder.addEndEvent(endName);\n        builder.addTransition(startName, \"task1\");\n        builder.addTransition(\"task1\", endName);\n\n        return deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user);\n    }\n\n    @Test\n    public void cantUpdateActivityDataInstanceWithWrongValue() throws Exception {\n        final DesignProcessDefinition processDef = new ProcessDefinitionBuilder().createNewInstance(\"My_Process\", \"1.0\")\n                .addActor(ACTOR_NAME)\n                .addDescription(\"Delivery all day and night long\")\n                .addUserTask(\"step1\", ACTOR_NAME).addData(\"data\", List.class.getName(), null).getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, ACTOR_NAME, user);\n\n        // test execution\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n\n        // verify the retrieved data\n        try {\n            getProcessAPI().updateActivityDataInstance(\"data\", step1Id, \"wrong value\");\n            fail();\n        } catch (final UpdateException e) {\n            assertEquals(\"USERNAME=\" + USERNAME\n                    + \" | DATA_NAME=data | DATA_CLASS_NAME=java.util.List | The type of new value [\"\n                    + String.class.getName()\n                    + \"] is not compatible with the type of the data.\", e.getMessage());\n            final Map<ExceptionContext, Serializable> exceptionContext = e.getContext();\n            assertEquals(List.class.getName(), exceptionContext.get(ExceptionContext.DATA_CLASS_NAME));\n            assertEquals(\"data\", exceptionContext.get(ExceptionContext.DATA_NAME));\n        }\n\n        // retrieve data after the update\n        final List<DataInstance> activityDataInstances = getProcessAPI().getActivityDataInstances(step1Id, 0, 10);\n        assertEquals(1, activityDataInstances.size());\n        assertNull(activityDataInstances.get(0).getValue());\n\n        // Evaluate the data\n        final List<Expression> dependencies = Collections\n                .singletonList(new ExpressionBuilder().createDataExpression(\"data\", List.class.getName()));\n        final Expression longExpression = new ExpressionBuilder().createGroovyScriptExpression(\"Script\",\n                \"data = new ArrayList<String>(); data.add(\\\"plop\\\"); return data;\", List.class.getName(), dependencies);\n        final Map<Expression, Map<String, Serializable>> expressions = Collections.singletonMap(longExpression,\n                Collections.emptyMap());\n        getProcessAPI().evaluateExpressionsOnActivityInstance(step1Id, expressions);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/data/ProcessDataDefinitionIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.process.data;\n\nimport static org.bonitasoft.engine.matchers.NameMatcher.nameIs;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertThat;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.data.DataDefinition;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.junit.Test;\n\npublic class ProcessDataDefinitionIT extends TestWithUser {\n\n    @Test\n    public void getProcessDataDefinitions() throws Exception {\n        final String intDataName = \"luckyNum\";\n        final Expression intDefaultExp = new ExpressionBuilder().createConstantIntegerExpression(10);\n        final String strDataName = \"luckyColor\";\n        final Expression strDefaultExp = new ExpressionBuilder().createConstantStringExpression(\"blue\");\n\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addIntegerData(intDataName, intDefaultExp);\n        processDefinitionBuilder.addShortTextData(strDataName, strDefaultExp);\n        final DesignProcessDefinition designProcessDefinition = processDefinitionBuilder.done();\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final List<DataDefinition> dataDefList = getProcessAPI().getProcessDataDefinitions(processDefinition.getId(), 0,\n                5);\n        assertEquals(2, dataDefList.size());\n\n        final DataDefinition dataDef1 = dataDefList.get(0);\n        assertEquals(intDataName, dataDef1.getName());\n        assertEquals(Integer.class.getName(), dataDef1.getClassName());\n        // assertEquals(intDefaultExp,dataDef1.getDefaultValueExpression());\n\n        final DataDefinition dataDef2 = dataDefList.get(1);\n        assertEquals(strDataName, dataDef2.getName());\n        assertEquals(String.class.getName(), dataDef2.getClassName());\n        // assertEquals(strDefaultExp,dataDef2.getDefaultValueExpression());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getProcessDataDefinitionsPaginated() throws Exception {\n        final String intDataName = \"luckyNum\";\n        final Expression intDefaultExp = new ExpressionBuilder().createConstantIntegerExpression(10);\n        final String strDataName = \"color\";\n        final Expression strDefaultExp = new ExpressionBuilder().createConstantStringExpression(\"blue\");\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addIntegerData(intDataName, intDefaultExp);\n        for (int i = 1; i <= 10; i++) {\n            processDefinitionBuilder.addShortTextData(strDataName + i, strDefaultExp);\n        }\n        final DesignProcessDefinition designProcessDefinition = processDefinitionBuilder.done();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        List<DataDefinition> processDataDefinitions = getProcessAPI()\n                .getProcessDataDefinitions(processDefinition.getId(), 0, 5);\n        assertEquals(5, processDataDefinitions.size());\n        assertThat(processDataDefinitions.get(0), nameIs(\"luckyNum\"));\n        assertThat(processDataDefinitions.get(1), nameIs(\"color1\"));\n        assertThat(processDataDefinitions.get(2), nameIs(\"color2\"));\n        assertThat(processDataDefinitions.get(3), nameIs(\"color3\"));\n        assertThat(processDataDefinitions.get(4), nameIs(\"color4\"));\n        processDataDefinitions = getProcessAPI().getProcessDataDefinitions(processDefinition.getId(), 5, 5);\n        assertEquals(5, processDataDefinitions.size());\n        assertThat(processDataDefinitions.get(0), nameIs(\"color5\"));\n        assertThat(processDataDefinitions.get(1), nameIs(\"color6\"));\n        assertThat(processDataDefinitions.get(2), nameIs(\"color7\"));\n        assertThat(processDataDefinitions.get(3), nameIs(\"color8\"));\n        assertThat(processDataDefinitions.get(4), nameIs(\"color9\"));\n        processDataDefinitions = getProcessAPI().getProcessDataDefinitions(processDefinition.getId(), 10, 5);\n        assertEquals(1, processDataDefinitions.size());\n        assertThat(processDataDefinitions.get(0), nameIs(\"color10\"));\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getNumberOfProcessDataDefinitions() throws Exception {\n        final String intDataName = \"luckyNum\";\n        final Expression intDefaultExp = new ExpressionBuilder().createConstantIntegerExpression(10);\n        final String strDataName = \"luckyColor\";\n        final Expression strDefaultExp = new ExpressionBuilder().createConstantStringExpression(\"blue\");\n\n        final DesignProcessDefinition designProcessDefinition;\n        // process level\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addIntegerData(intDataName, intDefaultExp);\n        processDefinitionBuilder.addShortTextData(strDataName, strDefaultExp);\n\n        designProcessDefinition = processDefinitionBuilder.done();\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final int i = getProcessAPI().getNumberOfProcessDataDefinitions(processDefinition.getId());\n        assertEquals(2, i);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test(expected = ProcessDefinitionNotFoundException.class)\n    public void getProcessDataDefinitionsWithException() throws Exception {\n        final String intDataName = \"luckyNum\";\n        final Expression intDefaultExp = new ExpressionBuilder().createConstantIntegerExpression(10);\n        final String strDataName = \"luckyColor\";\n        final Expression strDefaultExp = new ExpressionBuilder().createConstantStringExpression(\"blue\");\n\n        final DesignProcessDefinition designProcessDefinition;\n        // process level\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION);\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addIntegerData(intDataName, intDefaultExp);\n        processDefinitionBuilder.addShortTextData(strDataName, strDefaultExp);\n\n        designProcessDefinition = processDefinitionBuilder.done();\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        try {\n            getProcessAPI().getProcessDataDefinitions(processDefinition.getId() + 1, 0, 5);\n        } finally {\n            disableAndDeleteProcess(processDefinition);\n        }\n    }\n\n    @Test\n    public void executeProcessWithNotInitializedData() throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION);\n        processDefinitionBuilder.addAutomaticTask(\"step1\");\n        processDefinitionBuilder.addIntegerData(\"intdata\", null);\n        processDefinitionBuilder.addShortTextData(\"stringData\", null);\n        processDefinitionBuilder.addDateData(\"dateData\", null);\n        processDefinitionBuilder.addBlobData(\"blobData\", null);\n        processDefinitionBuilder.addBooleanData(\"booleanData\", null);\n        processDefinitionBuilder.addLongData(\"longData\", null);\n        processDefinitionBuilder.addDoubleData(\"doubleData\", null);\n        processDefinitionBuilder.addFloatData(\"floatData\", null);\n        processDefinitionBuilder.addXMLData(\"xmlData\", null);\n        processDefinitionBuilder.addData(\"javaData\", \"java.util.List\", null);\n\n        final DesignProcessDefinition designProcessDefinition = processDefinitionBuilder.done();\n\n        final ProcessDefinition processDefinition = deployAndEnableProcess(designProcessDefinition);\n        final List<DataDefinition> dataDefList = getProcessAPI().getProcessDataDefinitions(processDefinition.getId(), 0,\n                15);\n        assertEquals(10, dataDefList.size());\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForProcessToFinish(processInstance);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/data/ProcessDataInstanceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.process.data;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.tuple;\nimport static org.junit.Assert.*;\n\nimport java.io.Serializable;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.api.ApiAccessType;\nimport org.bonitasoft.engine.bpm.bar.BarResource;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.data.ArchivedDataInstance;\nimport org.bonitasoft.engine.bpm.data.ArchivedDataNotFoundException;\nimport org.bonitasoft.engine.bpm.data.DataInstance;\nimport org.bonitasoft.engine.bpm.process.ActivationState;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.exception.ExceptionContext;\nimport org.bonitasoft.engine.exception.RetrieveException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.io.IOUtil;\nimport org.bonitasoft.engine.util.APITypeManager;\nimport org.junit.Test;\n\npublic class ProcessDataInstanceIT extends TestWithUser {\n\n    @Test\n    public void getIntegerDataInstanceFromProcess() throws Exception {\n        final String className = Integer.class.getName();\n        final ProcessDefinition processDefinition = operateProcess(user,\n                new ExpressionBuilder().createConstantIntegerExpression(1), className);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n\n        final List<DataInstance> processDataInstances = getProcessAPI().getProcessDataInstances(processInstance.getId(),\n                0, 10);\n        assertFalse(processDataInstances.isEmpty());\n        assertEquals(1, processDataInstances.size());\n        assertEquals(\"var1\", processDataInstances.get(0).getName());\n        assertEquals(1, processDataInstances.get(0).getValue());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getDataInstance_should_return_empty_list_when_start_index_is_greater_than_number_of_elements_in_Db()\n            throws Exception {\n        final String className = String.class.getName();\n        final ProcessDefinition processDefinition = operateProcess(user,\n                new ExpressionBuilder().createConstantStringExpression(\"aaa\"), className);\n\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n        final List<DataInstance> processDataInstances = getProcessAPI().getProcessDataInstances(processInstance.getId(),\n                2, 100);\n        assertThat(processDataInstances).isEmpty();\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getShortTextDataInstanceFromProcess() throws Exception {\n        final String className = String.class.getName();\n        final ProcessDefinition processDefinition = operateProcess(user,\n                new ExpressionBuilder().createConstantStringExpression(\"aaa\"), className);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n\n        final List<DataInstance> processDataInstances = getProcessAPI().getProcessDataInstances(processInstance.getId(),\n                0, 10);\n        assertFalse(processDataInstances.isEmpty());\n        assertEquals(1, processDataInstances.size());\n        assertEquals(\"var1\", processDataInstances.get(0).getName());\n        assertEquals(\"aaa\", processDataInstances.get(0).getValue());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void processWithShortAndLongTextData() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithLongAndShortText\", \"1.0\");\n        builder.addUserTask(\"step1\", ACTOR_NAME);\n        builder.addActor(ACTOR_NAME);\n        final String shortTextValue = \"shortTextValue\";\n        builder.addShortTextData(\"shortTextData\",\n                new ExpressionBuilder().createConstantStringExpression(shortTextValue));\n        final String longTextValue = \"longTextValue\";\n        final StringBuilder longBuilder = new StringBuilder(longTextValue);\n        longBuilder.append(longTextValue.repeat(10));\n        builder.addLongTextData(\"longTextData\",\n                new ExpressionBuilder().createConstantStringExpression(longBuilder.toString()));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        assertEquals(shortTextValue,\n                getProcessAPI().getProcessDataInstance(\"shortTextData\", processInstance.getId()).getValue());\n        assertEquals(longBuilder.toString(),\n                getProcessAPI().getProcessDataInstance(\"longTextData\", processInstance.getId()).getValue());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getDoubleDataInstanceFromProcess() throws Exception {\n        final String className = Double.class.getName();\n        final ProcessDefinition processDefinition = operateProcess(user,\n                new ExpressionBuilder().createConstantDoubleExpression(1.5), className);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n\n        final List<DataInstance> processDataInstances = getProcessAPI().getProcessDataInstances(processInstance.getId(),\n                0, 10);\n        assertFalse(processDataInstances.isEmpty());\n        assertEquals(1, processDataInstances.size());\n        assertEquals(\"var1\", processDataInstances.get(0).getName());\n        assertEquals(1.5, processDataInstances.get(0).getValue());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getFloatDataInstanceFromProcess() throws Exception {\n        final String className = Float.class.getName();\n        final ProcessDefinition processDefinition = operateProcess(user,\n                new ExpressionBuilder().createConstantFloatExpression(1.5f), className);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n\n        final List<DataInstance> processDataInstances = getProcessAPI().getProcessDataInstances(processInstance.getId(),\n                0, 10);\n        assertFalse(processDataInstances.isEmpty());\n        assertEquals(1, processDataInstances.size());\n        assertEquals(\"var1\", processDataInstances.get(0).getName());\n        assertEquals(1.5f, processDataInstances.get(0).getValue());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getBooleanDataInstanceFromProcess() throws Exception {\n        final String className = Boolean.class.getName();\n        final ProcessDefinition processDefinition = operateProcess(user,\n                new ExpressionBuilder().createConstantBooleanExpression(true), className);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n\n        final List<DataInstance> processDataInstances = getProcessAPI().getProcessDataInstances(processInstance.getId(),\n                0, 10);\n        assertFalse(processDataInstances.isEmpty());\n        assertEquals(1, processDataInstances.size());\n        assertEquals(\"var1\", processDataInstances.get(0).getName());\n        assertEquals(true, processDataInstances.get(0).getValue());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getDateDataInstanceFromProcess() throws Exception {\n        final ProcessDefinition processDefinition = operateProcess(user,\n                new ExpressionBuilder().createConstantDateExpression(\"2013-07-18T14:49:26.86+02:00\"),\n                Date.class.getName());\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n\n        final List<DataInstance> processDataInstances = getProcessAPI().getProcessDataInstances(processInstance.getId(),\n                0, 10);\n        assertFalse(processDataInstances.isEmpty());\n        assertEquals(1, processDataInstances.size());\n        assertEquals(\"var1\", processDataInstances.get(0).getName());\n        assertNotNull(processDataInstances.get(0).getValue());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getLongDataInstanceFromProcess() throws Exception {\n        final String className = Long.class.getName();\n        final ProcessDefinition processDefinition = operateProcess(user,\n                new ExpressionBuilder().createConstantLongExpression(1), className);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n\n        final List<DataInstance> processDataInstances = getProcessAPI().getProcessDataInstances(processInstance.getId(),\n                0, 10);\n        assertFalse(processDataInstances.isEmpty());\n        assertEquals(1, processDataInstances.size());\n        assertEquals(\"var1\", processDataInstances.get(0).getName());\n        assertEquals(1L, processDataInstances.get(0).getValue());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void updateProcessDataInstance() throws Exception {\n        final DesignProcessDefinition processDef = new ProcessDefinitionBuilder().createNewInstance(\"My_Process\", \"1.0\")\n                .addActor(ACTOR_NAME)\n                .addDescription(\"Delivery all day and night long\")\n                .addIntegerData(\"var1\", new ExpressionBuilder().createConstantIntegerExpression(1))\n                .addUserTask(\"step1\", ACTOR_NAME).getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, ACTOR_NAME, user);\n\n        // test execution\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        // verify the retrieved data\n        List<DataInstance> processDataInstances = getProcessAPI().getProcessDataInstances(processInstance.getId(), 0,\n                10);\n        assertFalse(processDataInstances.isEmpty());\n        assertEquals(1, processDataInstances.size());\n        assertEquals(\"var1\", processDataInstances.get(0).getName());\n        assertEquals(1, processDataInstances.get(0).getValue());\n\n        getProcessAPI().updateProcessDataInstance(\"var1\", processInstance.getId(), 2);\n\n        // retrieve data after the update\n        processDataInstances = getProcessAPI().getProcessDataInstances(processInstance.getId(), 0, 10);\n        assertFalse(processDataInstances.isEmpty());\n        assertEquals(1, processDataInstances.size());\n        assertEquals(2, processDataInstances.get(0).getValue());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void cantUpdateProcessDataInstanceWithWrongValue() throws Exception {\n        final DesignProcessDefinition processDef = new ProcessDefinitionBuilder().createNewInstance(\"My_Process\", \"1.0\")\n                .addActor(ACTOR_NAME)\n                .addDescription(\"Delivery all day and night long\")\n                .addData(\"data\", List.class.getName(), null)\n                .addUserTask(\"step1\", ACTOR_NAME).getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, ACTOR_NAME, user);\n\n        // test execution\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n\n        // verify the retrieved data\n        try {\n            getProcessAPI().updateProcessDataInstance(\"data\", processInstance.getId(), \"wrong value\");\n            fail();\n        } catch (final UpdateException e) {\n            assertEquals(\"USERNAME=\" + USERNAME\n                    + \" | DATA_NAME=data | DATA_CLASS_NAME=java.util.List | The type of new value [\"\n                    + String.class.getName()\n                    + \"] is not compatible with the type of the data.\", e.getMessage());\n            final Map<ExceptionContext, Serializable> exceptionContext = e.getContext();\n            assertEquals(List.class.getName(), exceptionContext.get(ExceptionContext.DATA_CLASS_NAME));\n            assertEquals(\"data\", exceptionContext.get(ExceptionContext.DATA_NAME));\n        }\n\n        // retrieve data after the update\n        final List<DataInstance> processDataInstances = getProcessAPI().getProcessDataInstances(processInstance.getId(),\n                0, 10);\n        assertEquals(1, processDataInstances.size());\n        assertNull(processDataInstances.get(0).getValue());\n\n        // Evaluate the data\n        final List<Expression> dependencies = Collections\n                .singletonList(new ExpressionBuilder().createDataExpression(\"data\", List.class.getName()));\n        final Expression longExpression = new ExpressionBuilder().createGroovyScriptExpression(\"Script\",\n                \"data = new ArrayList<String>(); data.add(\\\"plop\\\"); return data;\", List.class.getName(), dependencies);\n        final Map<Expression, Map<String, Serializable>> expressions = Collections.singletonMap(longExpression,\n                Collections.emptyMap());\n        getProcessAPI().evaluateExpressionsOnActivityInstance(step1Id, expressions);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void updateProcessDataInstanceTwice() throws Exception {\n        final DesignProcessDefinition processDef = new ProcessDefinitionBuilder().createNewInstance(\"My_Process\", \"1.0\")\n                .addActor(ACTOR_NAME)\n                .addDescription(\"Delivery all day and night long\")\n                .addIntegerData(\"var1\", new ExpressionBuilder().createConstantIntegerExpression(1))\n                .addUserTask(\"step1\", ACTOR_NAME).getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, ACTOR_NAME, user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        // test execution\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n\n        // verify the retrieved data\n        List<DataInstance> processDataInstances = getProcessAPI().getProcessDataInstances(processInstance.getId(), 0,\n                10);\n        assertFalse(processDataInstances.isEmpty());\n        assertEquals(1, processDataInstances.size());\n        assertEquals(\"var1\", processDataInstances.get(0).getName());\n        assertEquals(1, processDataInstances.get(0).getValue());\n\n        getProcessAPI().updateProcessDataInstance(\"var1\", processInstance.getId(), 2);\n\n        // retrieve data after the update\n        processDataInstances = getProcessAPI().getProcessDataInstances(processInstance.getId(), 0, 10);\n        assertFalse(processDataInstances.isEmpty());\n        assertEquals(1, processDataInstances.size());\n        assertEquals(2, processDataInstances.get(0).getValue());\n\n        getProcessAPI().updateProcessDataInstance(\"var1\", processInstance.getId(), 3);\n\n        // retrieve data after the update\n        processDataInstances = getProcessAPI().getProcessDataInstances(processInstance.getId(), 0, 10);\n        assertFalse(processDataInstances.isEmpty());\n        assertEquals(1, processDataInstances.size());\n        assertEquals(3, processDataInstances.get(0).getValue());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test(expected = RetrieveException.class)\n    public void dataNotAvailableAfterArchiveFromProcess() throws Exception {\n        final DesignProcessDefinition processDef = new ProcessDefinitionBuilder().createNewInstance(\"My_Process\", \"1.0\")\n                .addActor(ACTOR_NAME)\n                .addDescription(\"Delivery all day and night long\")\n                .addIntegerData(\"var1\", new ExpressionBuilder().createConstantIntegerExpression(1))\n                .addUserTask(\"step1\", ACTOR_NAME).getProcess();\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, ACTOR_NAME, user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        // test execution\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n\n        // verify the retrieved data\n        List<DataInstance> processDataInstances = getProcessAPI().getProcessDataInstances(processInstance.getId(), 0,\n                10);\n        assertFalse(processDataInstances.isEmpty());\n        assertEquals(1, processDataInstances.size());\n        assertEquals(\"var1\", processDataInstances.get(0).getName());\n        assertEquals(1, processDataInstances.get(0).getValue());\n\n        // Execute pending task\n        waitForUserTaskAndExecuteIt(processInstance, \"step1\", user);\n        waitForProcessToFinish(processInstance);\n\n        // retrieve data after process has finished\n        try {\n            getProcessAPI().getProcessDataInstances(processInstance.getId(), 0, 10);\n        } finally {\n            disableAndDeleteProcess(processDefinition);\n        }\n    }\n\n    private ProcessDefinition operateProcess(final User user, final Expression expression, final String className)\n            throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process\", \"1.0\");\n        if (className.equals(Integer.class.getName())) {\n            processDefinitionBuilder.addIntegerData(\"var1\", expression);\n        } else if (className.equals(Long.class.getName())) {\n            processDefinitionBuilder.addLongData(\"var1\", expression);\n        } else if (className.equals(Double.class.getName())) {\n            processDefinitionBuilder.addDoubleData(\"var1\", expression);\n        } else if (className.equals(Float.class.getName())) {\n            processDefinitionBuilder.addFloatData(\"var1\", expression);\n        } else if (className.equals(Boolean.class.getName())) {\n            processDefinitionBuilder.addBooleanData(\"var1\", expression);\n        } else if (className.equals(Date.class.getName())) {\n            processDefinitionBuilder.addDateData(\"var1\", expression);\n        } else if (className.equals(String.class.getName())) {\n            processDefinitionBuilder.addShortTextData(\"var1\", expression);\n        }\n        final DesignProcessDefinition processDef = processDefinitionBuilder.addActor(ACTOR_NAME)\n                .addDescription(\"Delivery all day and night long\")\n                .addUserTask(\"step1\", ACTOR_NAME).getProcess();\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, ACTOR_NAME, user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        return processDefinition;\n    }\n\n    @Test\n    public void getArchivedProcessDataInstance() throws Exception {\n        final String dataName = \"title\";\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"ProcessToArchive\",\n                \"1.0\");\n        builder.addShortTextData(dataName, new ExpressionBuilder().createConstantStringExpression(\"1\"));\n        builder.addActor(\"actor\");\n        builder.addUserTask(\"step\", \"actor\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.getProcess(), \"actor\",\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        Thread.sleep(10);\n        getProcessAPI().updateProcessDataInstance(dataName, processInstance.getId(), \"2\");\n\n        final ArchivedDataInstance archivedData = getProcessAPI().getArchivedProcessDataInstance(dataName,\n                processInstance.getId());\n        assertEquals(\"2\", archivedData.getValue());\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getArchivedProcessDataInstanceFromAnArchivedProcess() throws Exception {\n        final String dataName = \"title\";\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"ProcessToArchive\",\n                \"1.0\");\n        builder.addShortTextData(dataName, new ExpressionBuilder().createConstantStringExpression(\"1\"));\n        builder.addAutomaticTask(\"system\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcess(builder.getProcess());\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForProcessToFinish(processInstance);\n\n        final ArchivedDataInstance archivedData = getProcessAPI().getArchivedProcessDataInstance(dataName,\n                processInstance.getId());\n        assertEquals(\"1\", archivedData.getValue());\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void doNotArchiveTransientProcessDataInstance() throws Exception {\n        final String dataName = \"test\";\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"ProcessToArchive\",\n                \"1.0\");\n        builder.addShortTextData(dataName, new ExpressionBuilder().createConstantStringExpression(\"1\")).isTransient();\n        builder.addActor(\"actor\");\n        builder.addUserTask(\"step\", \"actor\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.getProcess(), \"actor\",\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        getProcessAPI().updateProcessDataInstance(dataName, processInstance.getId(), \"2\");\n        waitForUserTaskAndExecuteIt(processInstance, \"step\", user);\n        waitForProcessToFinish(processInstance);\n\n        try {\n            getProcessAPI().getArchivedProcessDataInstance(dataName, processInstance.getId());\n        } catch (final ArchivedDataNotFoundException e) {\n            disableAndDeleteProcess(processDefinition);\n        }\n    }\n\n    @Test\n    public void getUnknownArchivedProcessDataInstance() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"ProcessToArchive\",\n                \"1.0\");\n        builder.addAutomaticTask(\"system\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcess(builder.getProcess());\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForProcessToFinish(processInstance);\n\n        try {\n            getProcessAPI().getArchivedProcessDataInstance(\"o\", processInstance.getId());\n            fail(\"The data named 'o' does not exists\");\n        } catch (final ArchivedDataNotFoundException ignored) {\n            // Do nothing\n        } finally {\n            disableAndDeleteProcess(processDefinition);\n        }\n    }\n\n    @Test\n    public void getArchivedProcessDataInstances() throws Exception {\n        final String dataName = \"title\";\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"ProcessToArchive\",\n                \"1.0\");\n        builder.addShortTextData(dataName, new ExpressionBuilder().createConstantStringExpression(\"1\"));\n        builder.addShortTextData(\"job\", new ExpressionBuilder().createConstantStringExpression(\"job\"));\n        builder.addShortTextData(\"desc\", new ExpressionBuilder().createConstantStringExpression(\"desc\"));\n        builder.addActor(\"actor\");\n        builder.addUserTask(\"step\", \"actor\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.getProcess(), \"actor\",\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        Thread.sleep(10);\n        getProcessAPI().updateProcessDataInstance(dataName, processInstance.getId(), \"2\");\n\n        List<ArchivedDataInstance> archivedDataInstances = getProcessAPI()\n                .getArchivedProcessDataInstances(processInstance.getId(), 0, 10);\n        assertEquals(3, archivedDataInstances.size());\n        ArchivedDataInstance archivedDataInstance = getArchivedDataInstance(archivedDataInstances, dataName);\n        assertEquals(\"2\", archivedDataInstance.getValue());\n        archivedDataInstance = getArchivedDataInstance(archivedDataInstances, \"job\");\n        assertEquals(\"job\", archivedDataInstance.getValue());\n        archivedDataInstance = getArchivedDataInstance(archivedDataInstances, \"desc\");\n        assertEquals(\"desc\", archivedDataInstance.getValue());\n\n        archivedDataInstances = getProcessAPI().getArchivedProcessDataInstances(processInstance.getId(), 0, 1);\n        assertEquals(1, archivedDataInstances.size());\n        archivedDataInstance = getArchivedDataInstance(archivedDataInstances, dataName);\n        assertEquals(\"2\", archivedDataInstance.getValue());\n\n        archivedDataInstances = getProcessAPI().getArchivedProcessDataInstances(processInstance.getId(), 1, 10);\n        assertEquals(2, archivedDataInstances.size());\n        archivedDataInstance = getArchivedDataInstance(archivedDataInstances, \"job\");\n        assertEquals(\"job\", archivedDataInstance.getValue());\n        archivedDataInstance = getArchivedDataInstance(archivedDataInstances, \"desc\");\n        assertEquals(\"desc\", archivedDataInstance.getValue());\n\n        waitForUserTaskAndExecuteIt(processInstance, \"step\", user);\n        waitForProcessToFinish(processInstance);\n\n        archivedDataInstances = getProcessAPI().getArchivedProcessDataInstances(processInstance.getId(), 0, 10);\n        assertEquals(3, archivedDataInstances.size());\n        archivedDataInstance = getArchivedDataInstance(archivedDataInstances, dataName);\n        assertEquals(\"2\", archivedDataInstance.getValue());\n        archivedDataInstance = getArchivedDataInstance(archivedDataInstances, \"job\");\n        assertEquals(\"job\", archivedDataInstance.getValue());\n        archivedDataInstance = getArchivedDataInstance(archivedDataInstances, \"desc\");\n        assertEquals(\"desc\", archivedDataInstance.getValue());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getEmptyArchivedProcessDataInstances() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"ProcessToArchive\",\n                \"1.0\");\n        builder.addActor(\"actor\");\n        builder.addUserTask(\"step\", \"actor\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.getProcess(), \"actor\",\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        final List<ArchivedDataInstance> archivedDataInstances = getProcessAPI()\n                .getArchivedProcessDataInstances(processInstance.getId(), 0, 10);\n        assertEquals(0, archivedDataInstances.size());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getCustomTypeDataInstance() throws Exception {\n        if (APITypeManager.getAPIType() != ApiAccessType.LOCAL) {\n            //FIXME this test does not work in remote engine (class does not exists in client)\n            // Migrate to Junit5 and add an annotation to disable this test\n            return;\n        }\n        final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive();\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"ProcessWithPlopp\", \"1.0\");\n        processDefinitionBuilder.addActor(\"user\").addUserTask(\"step1\", \"user\").addData(\"myStepData\",\n                \"org.bonitasoft.plop.Plopp\",\n                new ExpressionBuilder().createGroovyScriptExpression(\"theScript\", \"new org.bonitasoft.plop.Plopp()\",\n                        \"org.bonitasoft.plop.Plopp\"));\n        processDefinitionBuilder.addData(\"myData\", \"org.bonitasoft.plop.Plopp\",\n                new ExpressionBuilder().createGroovyScriptExpression(\"theScript\", \"new org.bonitasoft.plop.Plopp()\",\n                        \"org.bonitasoft.plop.Plopp\"));\n        final User user = getIdentityAPI().createUser(\"custDataUser\", \"bpm\");\n        businessArchiveBuilder.setProcessDefinition(processDefinitionBuilder.done());\n        businessArchiveBuilder.addClasspathResource(\n                new BarResource(\"org.bonitasoft.plop.jar\",\n                        IOUtil.getAllContentFrom(this.getClass().getResourceAsStream(\"/org.bonitasoft.plop.bak\"))));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchiveBuilder.done(),\n                \"user\", user);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1 = waitForUserTask(\"step1\");\n\n        assertThat(getProcessAPI().getProcessDataInstances(processInstance.getId(), 0, 10))\n                .extracting(\"name\", \"className\")\n                .containsOnly(tuple(\"myData\", \"org.bonitasoft.plop.Plopp\"));\n        assertThat(getProcessAPI().getProcessDataInstance(\"myData\", processInstance.getId())).isNotNull();\n        assertThat(getProcessAPI().getActivityDataInstances(step1, 0, 10)).extracting(\"name\", \"className\")\n                .contains(tuple(\"myStepData\", \"org.bonitasoft.plop.Plopp\"));\n        assertThat(getProcessAPI().getActivityDataInstance(\"myStepData\", step1)).isNotNull();\n        assertThat(getProcessAPI().getArchivedProcessDataInstances(processInstance.getId(), 0, 10))\n                .extracting(\"name\", \"className\")\n                .containsOnly(tuple(\"myData\", \"org.bonitasoft.plop.Plopp\"));\n        assertThat(getProcessAPI().getArchivedProcessDataInstance(\"myData\", processInstance.getId())).isNotNull();\n        assertThat(getProcessAPI().getArchivedActivityDataInstances(step1, 0, 10)).extracting(\"name\", \"className\")\n                .contains(tuple(\"myStepData\", \"org.bonitasoft.plop.Plopp\"));\n        assertThat(getProcessAPI().getArchivedActivityDataInstance(\"myStepData\", step1)).isNotNull();\n\n        disableAndDeleteProcess(processDefinition);\n        deleteUser(user);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/document/DocumentIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.process.document;\n\nimport static java.util.Arrays.asList;\nimport static java.util.Collections.emptyMap;\nimport static java.util.Collections.singletonMap;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.tuple;\nimport static org.junit.Assert.*;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.assertj.core.api.Assertions;\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.bar.BarResource;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.bar.InvalidBusinessArchiveFormatException;\nimport org.bonitasoft.engine.bpm.contract.FileInputValue;\nimport org.bonitasoft.engine.bpm.data.DataInstance;\nimport org.bonitasoft.engine.bpm.document.ArchivedDocument;\nimport org.bonitasoft.engine.bpm.document.ArchivedDocumentNotFoundException;\nimport org.bonitasoft.engine.bpm.document.ArchivedDocumentsSearchDescriptor;\nimport org.bonitasoft.engine.bpm.document.Document;\nimport org.bonitasoft.engine.bpm.document.DocumentAttachmentException;\nimport org.bonitasoft.engine.bpm.document.DocumentCriterion;\nimport org.bonitasoft.engine.bpm.document.DocumentException;\nimport org.bonitasoft.engine.bpm.document.DocumentNotFoundException;\nimport org.bonitasoft.engine.bpm.document.DocumentValue;\nimport org.bonitasoft.engine.bpm.document.DocumentsSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.bpm.process.impl.AutomaticTaskDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.CallActivityBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.DocumentBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.DocumentDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.DocumentListDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.expression.InvalidExpressionException;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.operation.LeftOperandBuilder;\nimport org.bonitasoft.engine.operation.Operation;\nimport org.bonitasoft.engine.operation.OperationBuilder;\nimport org.bonitasoft.engine.operation.OperatorType;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.test.APITestUtil;\nimport org.bonitasoft.engine.test.BuildTestUtil;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.junit.Test;\n\n/**\n * @author Nicolas Chabanoles\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class DocumentIT extends TestWithUser {\n\n    private int processVersion = 0;\n\n    @Test\n    public void attachADocumentToProcessInstanceTest() throws Exception {\n        final ProcessInstance pi = deployAndEnableWithActorAndStartIt(user);\n        Document attachment;\n        try {\n            final Document doc = buildReferenceToExternalDocument();\n            attachment = getProcessAPI().attachDocument(pi.getId(), doc.getName(), doc.getContentFileName(),\n                    doc.getContentMimeType(), doc.getUrl());\n            final Document attachedDoc = getProcessAPI().getDocument(attachment.getId());\n            assertIsSameDocument(attachment, attachedDoc);\n            assertFalse(attachedDoc.hasContent());\n        } finally {\n            disableAndDeleteProcess(pi.getProcessDefinitionId());\n        }\n    }\n\n    @Test\n    public void removeADocument() throws Exception {\n        final ProcessInstance pi = deployAndEnableWithActorAndStartIt(user);\n        try {\n            //given\n            final Document attachment = getProcessAPI().attachDocument(pi.getId(), \"Name\", \"FileName\", \"MimeType\",\n                    new byte[] { 1, 2, 3 });\n\n            //when\n            final Document document = getProcessAPI().removeDocument(attachment.getId());\n\n            //then\n            assertEquals(\"removeDocument should return the removed document\", attachment, document);\n            try {\n                getProcessAPI().getDocument(attachment.getId());\n                fail(\"should not be able to find document after deletion\");\n            } catch (final DocumentNotFoundException e) {\n                //ok\n            }\n            final SearchResult<ArchivedDocument> archivedDocumentSearchResult = getProcessAPI().searchArchivedDocuments(\n                    new SearchOptionsBuilder(0, 100)\n                            .filter(ArchivedDocumentsSearchDescriptor.SOURCEOBJECT_ID, document.getId()).done());\n            assertThat(archivedDocumentSearchResult.getResult()).hasSize(1);\n            assertThat(archivedDocumentSearchResult.getResult().get(0).getContentStorageId())\n                    .isEqualTo(document.getContentStorageId());\n            assertThat(getProcessAPI().getDocumentContent(document.getContentStorageId()))\n                    .isEqualTo(new byte[] { 1, 2, 3 });\n        } finally {\n            disableAndDeleteProcess(pi.getProcessDefinitionId());\n        }\n    }\n\n    @Test\n    public void attachADocumentAndItsContentToProcessInstanceTest() throws Exception {\n        final ProcessInstance pi = deployAndEnableWithActorAndStartIt(user);\n        Document attachment;\n        try {\n            final String documentName = \"newDocument\";\n            final Document doc = BuildTestUtil.buildDocument(documentName);\n            final byte[] documentContent = BuildTestUtil.generateContent(doc);\n            attachment = getProcessAPI().attachDocument(pi.getId(), doc.getName(), doc.getContentFileName(),\n                    doc.getContentMimeType(), documentContent);\n            final Document attachedDoc = getProcessAPI().getDocument(attachment.getId());\n            assertIsSameDocument(attachment, attachedDoc);\n            final byte[] attachedContent = getProcessAPI().getDocumentContent(attachedDoc.getContentStorageId());\n            assertTrue(Arrays.equals(documentContent, attachedContent));\n            assertTrue(attachedDoc.hasContent());\n        } finally {\n            disableAndDeleteProcess(pi.getProcessDefinitionId());\n        }\n    }\n\n    private void assertIsSameDocument(final Document attachment, final Document attachedDoc) {\n        assertEquals(\"IDs are not the same!\", attachment.getId(), attachedDoc.getId());\n        assertEquals(\"Process instances IDs are not the same!\", attachment.getProcessInstanceId(),\n                attachedDoc.getProcessInstanceId());\n        assertEquals(\"Names are not the same!\", attachment.getName(), attachedDoc.getName());\n        assertEquals(\"Authors are not the same!\", attachment.getAuthor(), attachedDoc.getAuthor());\n        assertEquals(\"Creation dates are not the same!\", attachment.getCreationDate().getTime(),\n                attachedDoc.getCreationDate().getTime());\n        assertEquals(\"Has content flags are not the same!\", attachment.hasContent(), attachedDoc.hasContent());\n        assertEquals(\"File names are not the same!\", attachment.getContentFileName(), attachedDoc.getContentFileName());\n        assertEquals(\"Mime types are not the same!\", attachment.getContentMimeType(), attachedDoc.getContentMimeType());\n        assertEquals(\"Content storage IDs are not the same!\", attachment.getContentStorageId(),\n                attachedDoc.getContentStorageId());\n        assertEquals(\"URL are not the same!\", attachment.getUrl(), attachedDoc.getUrl());\n        assertEquals(\"Descriptions are not the same!\", attachment.getDescription(), attachedDoc.getDescription());\n    }\n\n    private void assertIsSameDocument(final Document attachedDoc, final long processInstanceId, final String name,\n            final long author, final boolean hasContent,\n            final String fileName, final String mimeType, final String url, final String description) {\n        assertEquals(\"Process instances IDs are not the same!\", processInstanceId, attachedDoc.getProcessInstanceId());\n        assertEquals(\"Names are not the same!\", name, attachedDoc.getName());\n        assertEquals(\"Authors are not the same!\", author, attachedDoc.getAuthor());\n        assertEquals(\"Has content flags are not the same!\", hasContent, attachedDoc.hasContent());\n        assertEquals(\"File names are not the same!\", fileName, attachedDoc.getContentFileName());\n        assertEquals(\"Mime types are not the same!\", mimeType, attachedDoc.getContentMimeType());\n        assertEquals(\"URL are not the same!\", url, attachedDoc.getUrl());\n        assertEquals(\"Descriptions are not the same!\", description, attachedDoc.getDescription());\n    }\n\n    @Test\n    public void createProcessWithUrlDocument() throws Exception {\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"createProcessWithUrlDocument\", \"1.0\");\n        designProcessDefinition.addActor(ACTOR_NAME);\n        designProcessDefinition.addUserTask(\"step1\", ACTOR_NAME);\n        final String docName = \"myRtfDocument\";\n        final String url = \"http://intranet.bonitasoft.com/private/docStorage/anyValue\";\n        final DocumentDefinitionBuilder documentDefinition = designProcessDefinition.addDocumentDefinition(docName);\n        documentDefinition.addUrl(url);\n        final String description = \"My document with an url and a description that is inside the process\";\n        documentDefinition.addDescription(description);\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(designProcessDefinition.getProcess()).done();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchive, ACTOR_NAME, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        try {\n            final SearchOptionsBuilder sob = new SearchOptionsBuilder(0, 10)\n                    .filter(DocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId());\n            final SearchResult<Document> docs = getProcessAPI().searchDocuments(sob.done());\n            final Document actualDoc = docs.getResult().get(0);\n            assertEquals(docName, actualDoc.getName());\n            assertEquals(url, actualDoc.getUrl());\n            assertEquals(description, actualDoc.getDescription());\n            assertTrue(\"Document Content filename should NOT be valuated\", null == actualDoc.getContentFileName());\n        } finally {\n            disableAndDeleteProcess(processDefinition);\n        }\n    }\n\n    @Test\n    public void attachAnExternalDocumentReferenceToProcessInstanceTest() throws Exception {\n        final ProcessInstance pi = deployAndEnableWithActorAndStartIt(user);\n        Document attachment;\n        try {\n            final Document doc = buildReferenceToExternalDocument();\n            attachment = getProcessAPI().attachDocument(pi.getId(), doc.getName(), doc.getContentFileName(),\n                    doc.getContentMimeType(), doc.getUrl());\n            final Document attachedDoc = getProcessAPI().getDocument(attachment.getId());\n            assertIsSameDocument(attachment, attachedDoc);\n\n        } finally {\n            disableAndDeleteProcess(pi.getProcessDefinitionId());\n        }\n    }\n\n    private Document buildReferenceToExternalDocument() {\n        final String now = String.valueOf(System.currentTimeMillis());\n        final String documentName = now + \"-series\";\n        final DocumentBuilder builder = new DocumentBuilder().createNewInstance(documentName, true);\n        builder.setDescription(\"a document that points to an url\");\n        builder.setURL(\"http://tinyurl.com/7n77prz\");\n        return builder.done();\n    }\n\n    private Document buildReferenceToExternalDocument(final String documentName) {\n        final DocumentBuilder builder = new DocumentBuilder().createNewInstance(documentName, true);\n        builder.setDescription(\"a document that points to an url\");\n        builder.setURL(\"http://tinyurl.com/7n77prz\");\n        return builder.done();\n    }\n\n    @Test\n    public void attachAnExternalDocumentReferenceToProcessInstanceAsNewVersionTest() throws Exception {\n        final ProcessInstance processInstance = deployAndEnableWithActorAndStartIt(user);\n        buildAndAttachDocument(processInstance);\n        Document attachment;\n        try {\n            final Document initialDocument = getAttachmentWithoutItsContent(processInstance);\n            assertThat(initialDocument.getVersion()).isEqualTo(\"1\");\n            final Document doc = buildReferenceToExternalDocument();\n            attachment = getProcessAPI().attachNewDocumentVersion(processInstance.getId(), initialDocument.getName(),\n                    doc.getContentFileName(),\n                    doc.getContentMimeType(),\n                    doc.getUrl());\n            final Document attachedDoc = getProcessAPI().getDocument(attachment.getId());\n            assertIsSameDocument(attachment, attachedDoc);\n            assertThat(attachedDoc.getVersion()).isEqualTo(\"2\");\n        } finally {\n            disableAndDeleteProcess(processInstance.getProcessDefinitionId());\n        }\n    }\n\n    @Test\n    public void attachADocumentAndItsContentToProcessInstanceAsNewVersionTest() throws Exception {\n        final ProcessInstance processInstance = deployAndEnableWithActorAndStartIt(user);\n        buildAndAttachDocument(processInstance);\n\n        try {\n            final Document initialDocument = getAttachmentWithoutItsContent(processInstance);\n            assertThat(initialDocument.getVersion()).isEqualTo(\"1\");\n            final Document doc = BuildTestUtil.buildDocument(initialDocument.getName());\n            final Document attachment = getProcessAPI().attachNewDocumentVersion(processInstance.getId(),\n                    initialDocument.getName(), doc.getContentFileName(),\n                    doc.getContentMimeType(),\n                    initialDocument.getName().getBytes());\n            final Document attachedDoc = getProcessAPI().getDocument(attachment.getId());\n            assertIsSameDocument(attachedDoc, attachment.getProcessInstanceId(), attachment.getName(),\n                    attachment.getAuthor(), attachment.hasContent(),\n                    attachment.getContentFileName(), attachment.getContentMimeType(), attachment.getUrl(),\n                    attachment.getDescription());\n            assertThat(attachedDoc.getVersion()).isEqualTo(\"2\");\n\n        } finally {\n            disableAndDeleteProcess(processInstance.getProcessDefinitionId());\n        }\n    }\n\n    @Test\n    public void getDocumentContentTest() throws Exception {\n        final ProcessInstance processInstance = deployAndEnableWithActorAndStartIt(user);\n        final String documentName = String.valueOf(System.currentTimeMillis());\n        final byte[] content = documentName.getBytes();\n        final Document document = getProcessAPI().attachDocument(processInstance.getId(), documentName, \"myPdf.pdf\",\n                \"text/plain\", content);\n        try {\n            final byte[] docContent = getProcessAPI().getDocumentContent(document.getContentStorageId());\n            assertThat(docContent).isEqualTo(content);\n            assertThat(document.getUrl()).isEqualTo(\n                    \"documentDownload?fileName=myPdf.pdf&contentStorageId=\" + document.getContentStorageId());\n\n        } finally {\n            disableAndDeleteProcess(processInstance.getProcessDefinitionId());\n        }\n    }\n\n    @Test\n    public void getLastDocumentTest() throws Exception {\n        final ProcessInstance processInstance = deployAndEnableWithActorAndStartIt(user);\n        buildAndAttachDocument(processInstance);\n\n        Document attachment;\n        Document lastVersion;\n        try {\n            final String documentName = getAttachmentDocumentName(processInstance);\n            attachment = getAttachmentWithoutItsContent(processInstance);\n            lastVersion = getProcessAPI().getLastDocument(processInstance.getId(), documentName);\n            assertIsSameDocument(attachment, lastVersion);\n\n            final Document doc = buildReferenceToExternalDocument(documentName);\n            final Document newVersion = getProcessAPI().attachNewDocumentVersion(processInstance.getId(), doc.getName(),\n                    doc.getContentFileName(),\n                    doc.getContentMimeType(),\n                    doc.getUrl());\n            lastVersion = getProcessAPI().getLastDocument(processInstance.getId(), documentName);\n            assertIsSameDocument(newVersion, lastVersion);\n\n        } finally {\n            disableAndDeleteProcess(processInstance.getProcessDefinitionId());\n        }\n    }\n\n    @Test\n    public void getDocumentOnProcessWithDocumentInDefinitionUsingBarResource() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcessWithDocumentsInBar\", \"1.0\");\n        builder.addUserTask(\"step1\", ACTOR_NAME);\n        builder.addActor(ACTOR_NAME);\n        builder.addDocumentDefinition(\"myDoc\").addContentFileName(\"myPdfModifiedName.pdf\")\n                .addDescription(\"a cool pdf document\").addMimeType(\"application/pdf\")\n                .addFile(\"myPdf.pdf\").addDescription(\"my description\");\n        final byte[] pdfContent = new byte[] { 5, 0, 1, 4, 6, 5, 2, 3, 1, 5, 6, 8, 4, 6, 6, 3, 2, 4, 5 };\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(builder.getProcess())\n                .addDocumentResource(new BarResource(\"myPdf.pdf\", pdfContent)).done();\n        final ProcessInstance processInstance = deployAndEnableProcessWithActorAndStartIt(businessArchive, user);\n        try {\n            final Document attachment = getAttachmentWithoutItsContent(processInstance);\n            assertIsSameDocument(attachment, processInstance.getId(), \"myDoc\", user.getId(), true,\n                    \"myPdfModifiedName.pdf\", \"application/pdf\",\n                    attachment.getUrl(), \"my description\");\n            final byte[] docContent = getProcessAPI().getDocumentContent(attachment.getContentStorageId());\n            assertTrue(Arrays.equals(pdfContent, docContent));\n        } finally {\n            waitForUserTask(processInstance, \"step1\");\n            disableAndDeleteProcess(processInstance.getProcessDefinitionId());\n        }\n    }\n\n    @Test\n    public void getDocumentUsingExpression() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcessWithDocumentsInBar\", \"1.0\");\n        final AutomaticTaskDefinitionBuilder automaticTask = builder.addAutomaticTask(\"setDataTask\");\n        automaticTask.addOperation(new LeftOperandBuilder().createNewInstance(\"myDocRef\").done(),\n                OperatorType.ASSIGNMENT, \"=\", null,\n                new ExpressionBuilder().createDocumentReferenceExpression(\"myDoc\"));\n        automaticTask.addOperation(\n                new LeftOperandBuilder().createNewInstance(\"docFileName\").done(),\n                OperatorType.ASSIGNMENT,\n                \"=\",\n                null,\n                new ExpressionBuilder().createGroovyScriptExpression(\"myScript\", \"myDoc.getFileName()\",\n                        String.class.getName(),\n                        new ExpressionBuilder().createDocumentReferenceExpression(\"myDoc\")));\n        builder.addUserTask(\"step1\", ACTOR_NAME);\n        builder.addActor(ACTOR_NAME);\n        builder.addData(\"myDocRef\", Document.class.getName(), null);\n        builder.addData(\"docFileName\", String.class.getName(), null);\n        builder.addDocumentDefinition(\"myDoc\").addContentFileName(\"myPdfModifiedName.pdf\")\n                .addDescription(\"a cool pdf document\").addMimeType(\"application/pdf\")\n                .addFile(\"myPdf.pdf\");\n        builder.addTransition(\"setDataTask\", \"step1\");\n        final byte[] pdfContent = new byte[] { 5, 0, 1, 4, 6, 5, 2, 3, 1, 5, 6, 8, 4, 6, 6, 3, 2, 4, 5 };\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(builder.getProcess())\n                .addDocumentResource(new BarResource(\"myPdf.pdf\", pdfContent)).done();\n        final ProcessInstance pi = deployAndEnableProcessWithActorAndStartIt(businessArchive, user);\n        try {\n            final long step1Id = waitForUserTask(pi, \"step1\");\n            final DataInstance activityDataInstance = getProcessAPI().getActivityDataInstance(\"myDocRef\", step1Id);\n            final Document docRef = (Document) activityDataInstance.getValue();\n\n            final Document attachment = getAttachmentWithoutItsContent(pi);\n            assertEquals(attachment, docRef);\n            assertEquals(\"myPdfModifiedName.pdf\",\n                    getProcessAPI().getActivityDataInstance(\"docFileName\", step1Id).getValue());\n        } finally {\n            disableAndDeleteProcess(pi.getProcessDefinitionId());\n        }\n    }\n\n    @Test\n    public void evaluateExpressionOnCompletedProcessInstance_should_be_able_to_retrieve_document_for_an_archived_process_instance()\n            throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcessWithDocumentsInBar\", \"1.0\");\n        builder.addStartEvent(\"start\");\n        builder.addAutomaticTask(\"auto\");\n        builder.addEndEvent(\"end\");\n\n        builder.addDocumentDefinition(\"document\").addContentFileName(\"document.content\").addFile(\"document.content\");\n        final BusinessArchiveBuilder archive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(builder.getProcess());\n        archive.addDocumentResource(new BarResource(\"document.content\", \"content\".getBytes()));\n        final ProcessInstance processInstance = getProcessAPI()\n                .startProcess(deployAndEnableProcess(archive.done()).getId());\n        waitForProcessToFinish(processInstance.getId());\n\n        final Map<Expression, Map<String, Serializable>> expressions = Collections.singletonMap(\n                new ExpressionBuilder().createDocumentReferenceExpression(\"document\"), emptyMap());\n\n        final Map<String, Serializable> result = getProcessAPI()\n                .evaluateExpressionOnCompletedProcessInstance(processInstance.getId(), expressions);\n\n        assertEquals(\"document.content\", ((Document) result.get(\"document\")).getContentFileName());\n        disableAndDeleteProcess(processInstance.getProcessDefinitionId());\n    }\n\n    @Test\n    public void getDocumentOnProcessWithDocumentInDefinitionUsingUrl() throws Exception {\n        final String url = \"http://plop.org/file.pdf\";\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcessWithExternalDocuments\", \"1.0\");\n        builder.addUserTask(\"step1\", ACTOR_NAME);\n        builder.addActor(ACTOR_NAME);\n        builder.addDocumentDefinition(\"myDoc\").addContentFileName(\"file.pdf\").addDescription(\"a cool pdf document\")\n                .addMimeType(\"application/pdf\").addUrl(url);\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(builder.getProcess()).done();\n        final ProcessInstance processInstance = deployAndEnableProcessWithActorAndStartIt(businessArchive, user);\n        try {\n            final Document attachment = getAttachmentWithoutItsContent(processInstance);\n            assertIsSameDocument(attachment, processInstance.getId(), \"myDoc\", user.getId(), false, \"file.pdf\",\n                    \"application/pdf\", url, \"a cool pdf document\");\n        } finally {\n            // Clean up\n            waitForUserTask(processInstance, \"step1\");\n            disableAndDeleteProcess(processInstance.getProcessDefinitionId());\n        }\n    }\n\n    @Test\n    public void getDocumentAtProcessInstantiation() throws Exception {\n        final ProcessInstance processInstance = deployAndEnableWithActorAndStartIt(user);\n        buildAndAttachDocument(processInstance);\n        try {\n            Thread.sleep(2000);\n            final Document beforeUpdate = getAttachmentWithoutItsContent(processInstance);\n            final Document doc = BuildTestUtil.buildDocument(beforeUpdate.getName());\n            getProcessAPI().attachNewDocumentVersion(processInstance.getId(), beforeUpdate.getName(),\n                    doc.getContentFileName(), doc.getContentMimeType(),\n                    \"contentOfTheDoc\".getBytes());\n            final Document afterUpdate = getAttachmentWithoutItsContent(processInstance);\n            assertNotSame(beforeUpdate, afterUpdate);\n            final Document documentAtProcessInstantiation = getProcessAPI()\n                    .getDocumentAtProcessInstantiation(processInstance.getId(), afterUpdate.getName());\n            assertEquals(beforeUpdate, documentAtProcessInstantiation);\n        } finally {\n            disableAndDeleteProcess(processInstance.getProcessDefinitionId());\n        }\n    }\n\n    @Test\n    public void getDocumentAtActivityInstanceCompletion() throws Exception {\n        final ProcessInstance processInstance = deployAndEnableWithActorAndStartIt(user);\n        buildAndAttachDocument(processInstance);\n        try {\n            final Document beforeUpdate = getAttachmentWithoutItsContent(processInstance);\n            final Document doc = BuildTestUtil.buildDocument(beforeUpdate.getName());\n            getProcessAPI().attachNewDocumentVersion(processInstance.getId(), beforeUpdate.getName(),\n                    doc.getContentFileName(), doc.getContentMimeType(),\n                    \"contentOfTheDoc\".getBytes());\n            final Document afterUpdate = getAttachmentWithoutItsContent(processInstance);\n            assertNotSame(beforeUpdate, afterUpdate);\n\n            final long step1Id = waitForUserTaskAndExecuteIt(processInstance, \"step1\", user);\n            waitForArchivedActivity(step1Id, TestStates.NORMAL_FINAL);\n\n            final Document doc2 = BuildTestUtil.buildDocument(beforeUpdate.getName());\n            getProcessAPI().attachNewDocumentVersion(processInstance.getId(), beforeUpdate.getName(),\n                    doc2.getContentFileName(), doc2.getContentMimeType(),\n                    \"contentOfTheDoc\".getBytes());\n            final Document documentAtActivityInstantiation = getProcessAPI()\n                    .getDocumentAtActivityInstanceCompletion(step1Id, beforeUpdate.getName());\n            assertEquals(afterUpdate, documentAtActivityInstantiation);\n        } finally {\n            disableAndDeleteProcess(processInstance.getProcessDefinitionId());\n        }\n\n    }\n\n    @Test\n    public void getNumberOfDocument() throws Exception {\n        final ProcessInstance processInstance = deployAndEnableWithActorAndStartIt(user);\n        buildAndAttachDocument(processInstance);\n\n        try {\n            final long initialNbOfDocument = getProcessAPI().getNumberOfDocuments(processInstance.getId());\n            final String documentName = \"anotherDocumentReference\";\n            final Document doc = buildReferenceToExternalDocument();\n\n            getProcessAPI().attachDocument(processInstance.getId(), documentName, doc.getContentFileName(),\n                    doc.getContentMimeType(), doc.getUrl());\n            final long currentNbOfDocument = getProcessAPI().getNumberOfDocuments(processInstance.getId());\n            assertEquals(\"Invalid number of attachments!\", initialNbOfDocument + 1, currentNbOfDocument);\n\n        } finally {\n            disableAndDeleteProcess(processInstance.getProcessDefinitionId());\n        }\n    }\n\n    @Test\n    public void getNumberOfDocumentAfterAddingDocumentValue() throws Exception {\n        final ProcessInstance processInstance = deployAndEnableWithActorAndStartIt(user);\n        buildAndAttachDocument(processInstance);\n\n        try {\n            final long initialNbOfDocument = getProcessAPI().getNumberOfDocuments(processInstance.getId());\n            final String documentName = \"anotherDocumentValue\";\n            final Document doc = BuildTestUtil.buildDocument(documentName);\n\n            getProcessAPI().attachDocument(processInstance.getId(), documentName, doc.getContentFileName(),\n                    doc.getContentMimeType(), documentName.getBytes());\n            final long currentNbOfDocument = getProcessAPI().getNumberOfDocuments(processInstance.getId());\n            assertEquals(\"Invalid number of attachments!\", initialNbOfDocument + 1, currentNbOfDocument);\n\n        } finally {\n            disableAndDeleteProcess(processInstance.getProcessDefinitionId());\n        }\n    }\n\n    @Test\n    public void searchDocuments() throws Exception {\n        // add a new document, search it.\n        SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"ProcessDoc\",\n                \"12000\");\n        builder.addActor(\"actor\").addUserTask(\"step1\", \"actor\");\n        builder.addDocumentDefinition(\"Doc1\").addDescription(\"This is a description\").addContentFileName(\"doc.jpg\")\n                .addMimeType(\"image\").addFile(\"doc.jpg\");\n        builder.addDocumentDefinition(\"Doc2\").addDescription(\"This is a description2\").addContentFileName(\"doc2.jpg\")\n                .addMimeType(\"image\").addFile(\"doc2.jpg\");\n\n        final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(builder.done())\n                .addDocumentResource(new BarResource(\"doc.jpg\", \"Hello World\".getBytes()))\n                .addDocumentResource(new BarResource(\"doc2.jpg\", \"Hello World2\".getBytes()));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchiveBuilder.done(),\n                \"actor\", user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        final long step1 = waitForUserTask(\"step1\");\n        searchOptionsBuilder.filter(DocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId());\n        searchOptionsBuilder.sort(DocumentsSearchDescriptor.DOCUMENT_NAME, Order.ASC);\n        SearchResult<Document> documentSearch = getProcessAPI().searchDocuments(searchOptionsBuilder.done());\n        assertThat(documentSearch.getCount()).isEqualTo(2);\n        assertThat(documentSearch.getResult()).extracting(\"contentFileName\", \"processInstanceId\", \"author\")\n                .containsExactly(\n                        tuple(\"doc.jpg\", processInstance.getId(), user.getId()),\n                        tuple(\"doc2.jpg\", processInstance.getId(), user.getId()));\n        final Document document = documentSearch.getResult().get(0);\n        assertThat(getProcessAPI()\n                .searchDocuments(new SearchOptionsBuilder(0, 45).searchTerm(\"Doc\")\n                        .sort(DocumentsSearchDescriptor.DOCUMENT_NAME, Order.ASC).done())\n                .getResult()\n                .get(0).getId()).isEqualTo(document.getId());\n        assertThat(getProcessAPI()\n                .searchDocuments(new SearchOptionsBuilder(0, 45).searchTerm(\"This is\")\n                        .sort(DocumentsSearchDescriptor.DOCUMENT_NAME, Order.ASC).done())\n                .getResult().get(0).getId()).isEqualTo(document.getId());\n\n        // search document by content storage id\n        String contentStorageId = document.getContentStorageId();\n        final SearchResult<Document> docSearchedByContentStorageId = getProcessAPI().searchDocuments(\n                new SearchOptionsBuilder(0, 100).filter(DocumentsSearchDescriptor.CONTENT_STORAGE_ID, contentStorageId)\n                        .done());\n        assertThat(docSearchedByContentStorageId.getCount()).isEqualTo(1);\n        assertThat(docSearchedByContentStorageId.getResult().get(0).getContentStorageId()).isEqualTo(contentStorageId);\n\n        // Finalize the process\n        getProcessAPI().assignUserTask(step1, user.getId());\n        getProcessAPI().executeUserTask(user.getId(), step1, emptyMap());\n        waitForProcessToFinish(processInstance);\n\n        //search archive document order by archive date\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        searchOptionsBuilder.filter(ArchivedDocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId());\n        searchOptionsBuilder.sort(ArchivedDocumentsSearchDescriptor.ARCHIVE_DATE, Order.DESC);\n        SearchResult<ArchivedDocument> archiveDocumentSearch = getProcessAPI()\n                .searchArchivedDocuments(searchOptionsBuilder.done());\n        assertThat(archiveDocumentSearch.getCount()).isEqualTo(2);\n        assertThat(archiveDocumentSearch.getResult()).extracting(\"contentFileName\", \"processInstanceId\", \"author\")\n                .containsOnly(\n                        tuple(\"doc.jpg\", processInstance.getId(), user.getId()),\n                        tuple(\"doc2.jpg\", processInstance.getId(), user.getId()));\n\n        // search archived document by content storage id\n        final SearchResult<ArchivedDocument> archivedDocSearchedByContentStorageId = getProcessAPI()\n                .searchArchivedDocuments(\n                        new SearchOptionsBuilder(0, 100)\n                                .filter(ArchivedDocumentsSearchDescriptor.CONTENT_STORAGE_ID, contentStorageId)\n                                .done());\n        assertThat(archivedDocSearchedByContentStorageId.getCount()).isEqualTo(1);\n        assertThat(archivedDocSearchedByContentStorageId.getResult().get(0).getContentStorageId())\n                .isEqualTo(contentStorageId);\n\n        disableAndDeleteProcess(processInstance.getProcessDefinitionId());\n    }\n\n    @Test\n    public void searchDocumentsWithApostrophe() throws Exception {\n        searchDocumentsWithApostrophe(\"'documentName\", \"fileName\");\n        searchDocumentsWithApostrophe(\"documentName\", \"'fileName\");\n    }\n\n    private void searchDocumentsWithApostrophe(final String documentName, final String fileName) throws Exception {\n        final ProcessInstance processInstance = deployAndEnableWithActorAndStartIt(user);\n        buildAndAttachDocument(processInstance, documentName, fileName);\n\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        searchOptionsBuilder.sort(DocumentsSearchDescriptor.DOCUMENT_NAME, Order.ASC);\n        searchOptionsBuilder.searchTerm(\"'\");\n        final SearchResult<Document> documentSearch = getProcessAPI().searchDocuments(searchOptionsBuilder.done());\n\n        assertEquals(1, documentSearch.getCount());\n        assertEquals(processInstance.getId(), documentSearch.getResult().get(0).getProcessInstanceId());\n        assertEquals(user.getId(), documentSearch.getResult().get(0).getAuthor());\n        disableAndDeleteProcess(processInstance.getProcessDefinitionId());\n    }\n\n    @Test\n    public void documentsAreDeletedWhenProcessIsDeleted() throws Exception {\n        final ProcessInstance processInstance = deployAndEnableWithActorAndStartIt(user);\n        buildAndAttachDocument(processInstance, \"test\", \"test.txt\");\n        disableAndDeleteProcess(processInstance.getProcessDefinitionId());\n\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        searchOptionsBuilder.sort(DocumentsSearchDescriptor.DOCUMENT_NAME, Order.ASC);\n        final SearchResult<Document> documentSearch = getProcessAPI().searchDocuments(searchOptionsBuilder.done());\n\n        assertEquals(0, documentSearch.getCount());\n    }\n\n    @Test\n    public void searchArchivedDocuments() throws Exception {\n        // first time search, no document in archive table.\n        final ProcessInstance processInstance = deployAndEnableWithActorAndStartIt(user);\n        getProcessAPI().attachDocument(processInstance.getId(), \"Doc 1\", \"doc1.jpg\", \"image\", \"Hello World\".getBytes());\n        SearchOptionsBuilder searchOptionsBuilder;\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        searchOptionsBuilder.filter(ArchivedDocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId());\n        SearchResult<ArchivedDocument> documentSearch = getProcessAPI()\n                .searchArchivedDocuments(searchOptionsBuilder.done());\n        assertEquals(0, documentSearch.getCount());\n\n        try {\n            final Document beforeUpdate = getAttachmentWithoutItsContent(processInstance);\n\n            getProcessAPI().attachNewDocumentVersion(processInstance.getId(), beforeUpdate.getName(), \"doc2.jpg\",\n                    \"image\",\n                    \"contentOfTheDoc\".getBytes());\n            final Document afterUpdate = getAttachmentWithoutItsContent(processInstance);\n            getProcessAPI().getDocumentAtProcessInstantiation(processInstance.getId(), afterUpdate.getName());\n\n            // search again. exist 1 document in archive table.\n            searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n            searchOptionsBuilder.filter(ArchivedDocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId());\n            searchOptionsBuilder.sort(ArchivedDocumentsSearchDescriptor.DOCUMENT_NAME, Order.ASC);\n            documentSearch = getProcessAPI().searchArchivedDocuments(searchOptionsBuilder.done());\n            assertEquals(1, documentSearch.getCount());\n            ArchivedDocument archivedDocument = documentSearch.getResult().get(0);\n            assertEquals(processInstance.getId(), archivedDocument.getProcessInstanceId());\n            assertEquals(user.getId(), archivedDocument.getAuthor());\n\n            // search with term:\n            searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n            searchOptionsBuilder.searchTerm(afterUpdate.getName());\n            documentSearch = getProcessAPI().searchArchivedDocuments(searchOptionsBuilder.done());\n            archivedDocument = documentSearch.getResult().get(0);\n            assertEquals(1, documentSearch.getCount());\n            assertEquals(afterUpdate.getName(), archivedDocument.getName());\n\n            assertThat(\n                    getProcessAPI().searchArchivedDocuments(new SearchOptionsBuilder(0, 45).searchTerm(\"doc1\").done())\n                            .getResult().get(0)\n                            .getContentFileName())\n                    .isEqualTo(\"doc1.jpg\");\n\n        } finally {\n            disableAndDeleteProcess(processInstance.getProcessDefinitionId());\n        }\n    }\n\n    @Test\n    public void searchArchivedDocumentsWithApostropheInTheDocumentName() throws Exception {\n        final ProcessInstance processInstance = deployAndEnableWithActorAndStartIt(user);\n        buildAndAttachDocument(processInstance, \"a'\", \"a\");\n        SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        SearchResult<ArchivedDocument> documentSearch = getProcessAPI()\n                .searchArchivedDocuments(searchOptionsBuilder.done());\n        assertEquals(0, documentSearch.getCount());\n\n        try {\n            final Document beforeUpdate = getAttachmentWithoutItsContent(processInstance);\n            final Document doc = BuildTestUtil.buildDocument(beforeUpdate.getName());\n            getProcessAPI().attachNewDocumentVersion(processInstance.getId(), beforeUpdate.getName(),\n                    doc.getContentFileName(), doc.getContentMimeType(),\n                    \"contentOfTheDoc\".getBytes());\n            final Document afterUpdate = getAttachmentWithoutItsContent(processInstance);\n            getProcessAPI().getDocumentAtProcessInstantiation(processInstance.getId(), afterUpdate.getName());\n\n            // search with term:\n            searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n            searchOptionsBuilder.searchTerm(\"a'\");\n            documentSearch = getProcessAPI().searchArchivedDocuments(searchOptionsBuilder.done());\n            assertEquals(1, documentSearch.getCount());\n            assertEquals(afterUpdate.getName(), documentSearch.getResult().get(0).getName());\n\n        } finally {\n            disableAndDeleteProcess(processInstance.getProcessDefinitionId());\n        }\n    }\n\n    @Test\n    public void searchArchivedDocumentsWithApostropheInTheFileName() throws Exception {\n        final ProcessInstance processInstance = deployAndEnableWithActorAndStartIt(user);\n        buildAndAttachDocument(processInstance, \"b\", \"b'\");\n        SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        SearchResult<ArchivedDocument> documentSearch = getProcessAPI()\n                .searchArchivedDocuments(searchOptionsBuilder.done());\n        assertEquals(0, documentSearch.getCount());\n\n        try {\n            final Document beforeUpdate = getAttachmentWithoutItsContent(processInstance);\n            final Document doc = BuildTestUtil.buildDocument(beforeUpdate.getName());\n            getProcessAPI().attachNewDocumentVersion(processInstance.getId(), beforeUpdate.getName(),\n                    doc.getContentFileName(), doc.getContentMimeType(),\n                    \"contentOfTheDoc\".getBytes());\n            final Document afterUpdate = getAttachmentWithoutItsContent(processInstance);\n            getProcessAPI().getDocumentAtProcessInstantiation(processInstance.getId(), afterUpdate.getName());\n\n            // search with term:\n            searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n            searchOptionsBuilder.searchTerm(\"b'\");\n            documentSearch = getProcessAPI().searchArchivedDocuments(searchOptionsBuilder.done());\n            assertEquals(1, documentSearch.getCount());\n            assertEquals(afterUpdate.getName(), documentSearch.getResult().get(0).getName());\n\n        } finally {\n            disableAndDeleteProcess(processInstance.getProcessDefinitionId());\n        }\n    }\n\n    @Test\n    public void getArchivedVersionOfDocuments() throws Exception {\n        // add new document\n        final ProcessInstance processInstance = deployAndEnableWithActorAndStartIt(user);\n        buildAndAttachDocument(processInstance);\n        // search archive document. result is 0.\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        searchOptionsBuilder.filter(ArchivedDocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId());\n        final SearchResult<ArchivedDocument> documentSearch = getProcessAPI()\n                .searchArchivedDocuments(searchOptionsBuilder.done());\n        assertEquals(0, documentSearch.getCount());\n\n        // archive document\n        try {\n            final Document beforeUpdate = getAttachmentWithoutItsContent(processInstance);\n            final Document doc = BuildTestUtil.buildDocument(beforeUpdate.getName());\n            getProcessAPI().attachNewDocumentVersion(processInstance.getId(), beforeUpdate.getName(),\n                    \"newContent1.file\", doc.getContentMimeType(),\n                    \"contentOfTheDoc1\".getBytes());\n            getProcessAPI().attachNewDocumentVersion(processInstance.getId(), beforeUpdate.getName(),\n                    \"newContent2.file\", doc.getContentMimeType(),\n                    \"contentOfTheDoc2\".getBytes());\n            getAttachmentWithoutItsContent(processInstance);\n\n            // get archived document\n            final ArchivedDocument archivedDocument = getProcessAPI()\n                    .getArchivedVersionOfProcessDocument(beforeUpdate.getId());\n            assertNotNull(archivedDocument.getArchiveDate());\n            assertEquals(beforeUpdate.getId(), archivedDocument.getSourceObjectId());\n            assertEquals(\"newContent1.file\", archivedDocument.getContentFileName());\n            assertEquals(processInstance.getId(), archivedDocument.getProcessInstanceId());\n        } finally {\n            disableAndDeleteProcess(processInstance.getProcessDefinitionId());\n        }\n    }\n\n    @Test(expected = ArchivedDocumentNotFoundException.class)\n    public void getArchivedDocumentNotFound() throws BonitaException {\n        getProcessAPI().getArchivedProcessDocument(123456789L);\n    }\n\n    @Test\n    public void getArchivedDocument() throws Exception {\n        // add new document\n        final ProcessInstance processInstance = deployAndEnableWithActorAndStartIt(user);\n        buildAndAttachDocument(processInstance);\n        // search archive document. result is 0.\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        searchOptionsBuilder.filter(ArchivedDocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId());\n        final SearchResult<ArchivedDocument> documentSearch = getProcessAPI()\n                .searchArchivedDocuments(searchOptionsBuilder.done());\n        assertEquals(0, documentSearch.getCount());\n\n        // archive document\n        try {\n            final Document beforeUpdate = getAttachmentWithoutItsContent(processInstance);\n            final Document doc = BuildTestUtil.buildDocument(beforeUpdate.getName());\n            getProcessAPI().attachNewDocumentVersion(processInstance.getId(), beforeUpdate.getName(),\n                    doc.getContentFileName(), doc.getContentMimeType(),\n                    \"contentOfTheDoc\".getBytes());\n            getAttachmentWithoutItsContent(processInstance);\n            final ArchivedDocument archivedDocument = getProcessAPI()\n                    .getArchivedVersionOfProcessDocument(beforeUpdate.getId());\n            assertEquals(archivedDocument, getProcessAPI().getArchivedProcessDocument(archivedDocument.getId()));\n        } finally {\n            disableAndDeleteProcess(processInstance.getProcessDefinitionId());\n        }\n    }\n\n    @Test\n    public void countAttachmentWithSomeAttachments() throws Exception {\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        final long initialNbOfDocument = getProcessAPI().countAttachments(searchOptionsBuilder.done());\n        final ProcessInstance processInstance = deployAndEnableWithActorAndStartIt(user);\n        buildAndAttachDocument(processInstance);\n        final long numberOfAttachments = getProcessAPI().countAttachments(searchOptionsBuilder.done());\n        assertEquals(1 + initialNbOfDocument, numberOfAttachments);\n        disableAndDeleteProcess(processInstance.getProcessDefinitionId());\n    }\n\n    @Test\n    public void updateExistingDocumentWithOperation() throws Exception {\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"procWithStringIndexes\", \"1.0\");\n        designProcessDefinition.addActor(ACTOR_NAME).addDescription(\"The doc'\");\n        designProcessDefinition.addUserTask(\"step1\", ACTOR_NAME);\n        final Expression groovyThatCreateDocumentContent = new ExpressionBuilder().createGroovyScriptExpression(\n                \"script\",\n                \"return new org.bonitasoft.engine.bpm.document.DocumentValue(\\\"updated Content\\\".getBytes(), \\\"plain/text\\\", \\\"updatedContent.txt\\\");\",\n                DocumentValue.class.getName());\n        designProcessDefinition.addAutomaticTask(\"step2\")\n                .addOperation(new OperationBuilder().createSetDocument(\"textFile\", groovyThatCreateDocumentContent));\n        designProcessDefinition.addUserTask(\"step3\", ACTOR_NAME);\n        designProcessDefinition.addTransition(\"step1\", \"step2\");\n        designProcessDefinition.addTransition(\"step2\", \"step3\");\n        designProcessDefinition.addDocumentDefinition(\"textFile\").addContentFileName(\"myUnmodifiedTextFile.pdf\")\n                .addDescription(\"a cool text document\")\n                .addMimeType(\"plain/text\").addFile(\"myUnmodifiedTextFile.txt\");\n        final byte[] textContent = \"Unmodified content\".getBytes();\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(designProcessDefinition.getProcess())\n                .addDocumentResource(new BarResource(\"myUnmodifiedTextFile.txt\", textContent))\n                .done();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchive, ACTOR_NAME, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        // before update\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        searchOptionsBuilder.filter(DocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId());\n        final SearchOptions searchOptions = searchOptionsBuilder.done();\n        SearchResult<Document> searchDocuments = getProcessAPI().searchDocuments(searchOptions);\n        assertEquals(1, searchDocuments.getCount());\n        final Document initialDocument = searchDocuments.getResult().iterator().next();\n        final byte[] documentContent = getProcessAPI().getDocumentContent(initialDocument.getContentStorageId());\n        assertEquals(\"Unmodified content\", new String(documentContent));\n\n        // update\n        assignAndExecuteStep(step1Id, user.getId());\n        waitForUserTask(processInstance, \"step3\");\n\n        // after update\n        searchDocuments = getProcessAPI().searchDocuments(searchOptions);\n        assertEquals(1, searchDocuments.getCount());\n        final Document newDocument = searchDocuments.getResult().iterator().next();\n        assertEquals(\"textFile\", newDocument.getName());\n        assertEquals(\"updatedContent.txt\", newDocument.getContentFileName());\n        assertEquals(\"plain/text\", newDocument.getContentMimeType());\n        final byte[] newDocumentContent = getProcessAPI().getDocumentContent(newDocument.getContentStorageId());\n        assertEquals(\"updated Content\", new String(newDocumentContent));\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void updateExistingDocumentWithNullOperation() throws Exception {\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"procWithStringIndexes\", \"1.0\");\n        designProcessDefinition.addActor(ACTOR_NAME).addDescription(\"The doc'\");\n        designProcessDefinition.addUserTask(\"step1\", ACTOR_NAME);\n        final Expression groovyThatReturnNull = new ExpressionBuilder().createGroovyScriptExpression(\"script\",\n                \"return null;\", DocumentValue.class.getName());\n        designProcessDefinition.addAutomaticTask(\"step2\")\n                .addOperation(new OperationBuilder().createSetDocument(\"textFile\", groovyThatReturnNull));\n        designProcessDefinition.addUserTask(\"step3\", ACTOR_NAME);\n        designProcessDefinition.addTransition(\"step1\", \"step2\");\n        designProcessDefinition.addTransition(\"step2\", \"step3\");\n        designProcessDefinition.addDocumentDefinition(\"textFile\").addContentFileName(\"myUnmodifiedTextFile.pdf\")\n                .addDescription(\"a cool text document\")\n                .addMimeType(\"plain/text\").addFile(\"myUnmodifiedTextFile.txt\");\n        final byte[] textContent = \"Unmodified content\".getBytes();\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(designProcessDefinition.getProcess())\n                .addDocumentResource(new BarResource(\"myUnmodifiedTextFile.txt\", textContent))\n                .done();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchive, ACTOR_NAME, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        // before update\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n        final Document initialDocument = getProcessAPI().getLastDocument(processInstance.getId(), \"textFile\");\n        final byte[] documentContent = getProcessAPI().getDocumentContent(initialDocument.getContentStorageId());\n        assertEquals(\"Unmodified content\", new String(documentContent));\n\n        // update\n        assignAndExecuteStep(step1Id, user.getId());\n        waitForUserTask(processInstance, \"step3\");\n\n        // after update\n        assertEquals(\"textFile\",\n                getProcessAPI().getArchivedVersionOfProcessDocument(initialDocument.getId()).getName());\n        try {\n            getProcessAPI().getLastDocument(processInstance.getId(), \"textFile\");\n            fail();\n        } catch (final DocumentNotFoundException e) {\n            // ok\n        } finally {\n            disableAndDeleteProcess(processDefinition);\n        }\n    }\n\n    @Test\n    public void updateExistingDocumentUrlWithOperation() throws Exception {\n\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"procWithStringIndexes\", \"1.0\");\n        designProcessDefinition.addActor(ACTOR_NAME).addDescription(\"The doc'\");\n        designProcessDefinition.addUserTask(\"step1\", ACTOR_NAME);\n        designProcessDefinition.addAutomaticTask(\"step2\").addOperation(\n                new OperationBuilder().createSetDocument(\"textFile\",\n                        getDocumentValueExpressionWithUrl(\"http://www.example.com/new_url.txt\")));\n        designProcessDefinition.addUserTask(\"step3\", ACTOR_NAME);\n        designProcessDefinition.addTransition(\"step1\", \"step2\");\n        designProcessDefinition.addTransition(\"step2\", \"step3\");\n        designProcessDefinition.addDocumentDefinition(\"textFile\").addContentFileName(\"myUnmodifiedTextFile.pdf\")\n                .addDescription(\"a cool text document\")\n                .addMimeType(\"plain/text\").addUrl(\"http://www.example.com/original_url.txt\");\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(designProcessDefinition.getProcess()).done();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchive, ACTOR_NAME, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        // before update\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        searchOptionsBuilder.filter(DocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId());\n        final SearchOptions searchOptions = searchOptionsBuilder.done();\n        SearchResult<Document> searchDocuments = getProcessAPI().searchDocuments(searchOptions);\n        assertEquals(1, searchDocuments.getCount());\n        final Document initialDocument = searchDocuments.getResult().iterator().next();\n        assertEquals(\"http://www.example.com/original_url.txt\", initialDocument.getUrl());\n\n        // update\n        assignAndExecuteStep(step1Id, user.getId());\n        waitForUserTask(processInstance, \"step3\");\n\n        // after update\n        searchDocuments = getProcessAPI().searchDocuments(searchOptions);\n        assertEquals(1, searchDocuments.getCount());\n        final Document newDocument = searchDocuments.getResult().iterator().next();\n        assertEquals(\"http://www.example.com/new_url.txt\", newDocument.getUrl());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void createDocumentWithOperationAndInitialValue() throws Exception {\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"procWithStringIndexes\", \"1.0\");\n        designProcessDefinition.addData(\"documentValue\", DocumentValue.class.getName(), null);\n        designProcessDefinition.addActor(ACTOR_NAME).addDescription(\"The doc'\");\n        designProcessDefinition.addAutomaticTask(\"step0\").addOperation(\n                new OperationBuilder().createSetDataOperation(\"documentValue\",\n                        new ExpressionBuilder().createDocumentReferenceExpression(\"textFile\")));\n        designProcessDefinition.addUserTask(\"step1\", ACTOR_NAME);\n        final Expression groovyThatCreateDocumentContent = new ExpressionBuilder().createGroovyScriptExpression(\n                \"script\",\n                \"return new org.bonitasoft.engine.bpm.document.DocumentValue(\\\"updated Content\\\".getBytes(), \\\"plain/text\\\", \\\"updatedContent.txt\\\");\",\n                DocumentValue.class.getName());\n        designProcessDefinition.addAutomaticTask(\"step2\")\n                .addOperation(new OperationBuilder().createSetDocument(\"textFile\", groovyThatCreateDocumentContent));\n        designProcessDefinition.addUserTask(\"step3\", ACTOR_NAME);\n        designProcessDefinition.addTransition(\"step0\", \"step1\");\n        designProcessDefinition.addTransition(\"step1\", \"step2\");\n        designProcessDefinition.addTransition(\"step2\", \"step3\");\n        designProcessDefinition.addDocumentDefinition(\"docInitWithDocValue\").addInitialValue(\n                new ExpressionBuilder().createGroovyScriptExpression(\"docValue\",\n                        \"new org.bonitasoft.engine.bpm.document.DocumentValue(\\\"hello3\\\".getBytes(),\\\"plain/text\\\",\\\"file1.txt\\\")\",\n                        DocumentValue.class.getName()));\n        designProcessDefinition.addDocumentDefinition(\"docInitWithFileInput\").addInitialValue(\n                new ExpressionBuilder().createGroovyScriptExpression(\"docValue2\",\n                        \"new org.bonitasoft.engine.bpm.contract.FileInputValue(\\\"file2.txt\\\", \\\"hello4\\\".getBytes())\",\n                        FileInputValue.class.getName()));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(),\n                ACTOR_NAME, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        // before update\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n        // document value expression should return null when document don't exists\n        assertNull(getProcessAPI().getProcessDataInstance(\"documentValue\", processInstance.getId()).getValue());\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        searchOptionsBuilder.filter(DocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId());\n        searchOptionsBuilder.sort(DocumentsSearchDescriptor.DOCUMENT_CREATIONDATE, Order.ASC);\n        final SearchOptions searchOptions = searchOptionsBuilder.done();\n        SearchResult<Document> searchDocuments = getProcessAPI().searchDocuments(searchOptions);\n        assertEquals(2, searchDocuments.getCount());\n        assertThat(searchDocuments.getResult()).extracting(\"name\", \"contentFileName\").containsExactly(\n                tuple(\"docInitWithDocValue\", \"file1.txt\"),\n                tuple(\"docInitWithFileInput\", \"file2.txt\"));\n        // update\n        assignAndExecuteStep(step1Id, user);\n        waitForUserTask(processInstance, \"step3\");\n\n        // after update\n        searchDocuments = getProcessAPI().searchDocuments(searchOptions);\n        assertEquals(3, searchDocuments.getCount());\n        final Document newDocument = searchDocuments.getResult().get(2);\n        assertEquals(\"textFile\", newDocument.getName());\n        assertEquals(\"updatedContent.txt\", newDocument.getContentFileName());\n        assertEquals(\"plain/text\", newDocument.getContentMimeType());\n        final byte[] newDocumentContent = getProcessAPI().getDocumentContent(newDocument.getContentStorageId());\n        assertEquals(\"updated Content\", new String(newDocumentContent));\n        disableAndDeleteProcess(processDefinition);\n\n    }\n\n    @Test\n    public void startProcessAndSetDocumentValueWithOperations() throws Exception {\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"LivingDay\", \"1.0\");\n        final String docRefName = \"invoiceReference\";\n        designProcessDefinition.addData(docRefName, DocumentValue.class.getName(), null);\n        final String docName = \"invoiceLetter\";\n        designProcessDefinition.addData(docName, DocumentValue.class.getName(), null);\n        designProcessDefinition.addActor(ACTOR_NAME);\n        designProcessDefinition.addUserTask(\"step1\", ACTOR_NAME);\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(),\n                ACTOR_NAME, user);\n\n        final String docUrl = \"http://internal.intranet.org/resources/myDoc.pdf\";\n        final Operation docRefOperation = new OperationBuilder().createSetDocument(docRefName,\n                new ExpressionBuilder().createInputExpression(\"documentReference\", DocumentValue.class.getName()));\n\n        final Operation docContentOperation = new OperationBuilder().createSetDocument(docName,\n                new ExpressionBuilder().createInputExpression(\"documentValue\", DocumentValue.class.getName()));\n\n        final Map<String, Serializable> expressionContext = new HashMap<>(2);\n        final String documentFileName = \"updatedContent.txt\";\n        expressionContext.put(\"documentValue\",\n                new DocumentValue(\"Binary content of the document\".getBytes(), \"plain/text\", documentFileName));\n        expressionContext.put(\"documentReference\", new DocumentValue(docUrl));\n        final ProcessInstance myCase = getProcessAPI()\n                .startProcess(processDefinition.getId(), asList(docContentOperation, docRefOperation),\n                        expressionContext);\n        waitForUserTask(myCase, \"step1\");\n\n        SearchResult<Document> searchDocuments = getProcessAPI().searchDocuments(\n                new SearchOptionsBuilder(0, 5).filter(DocumentsSearchDescriptor.DOCUMENT_NAME, docRefName).done());\n        assertEquals(docUrl, searchDocuments.getResult().get(0).getUrl());\n\n        searchDocuments = getProcessAPI().searchDocuments(\n                new SearchOptionsBuilder(0, 5).filter(DocumentsSearchDescriptor.DOCUMENT_NAME, docName).done());\n        assertEquals(documentFileName, searchDocuments.getResult().get(0).getContentFileName());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void createDocumentWithOperationUsingURL() throws Exception {\n        // deploy and instantiate process\n        final String url = \"http://www.example.com/new_url.txt\";\n        final ProcessDefinition processDefinition = deployProcessWithURLDocumentCreateOperation(\"textFile\", url);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        // before update\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        searchOptionsBuilder.filter(DocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId());\n        final SearchOptions searchOptions = searchOptionsBuilder.done();\n        SearchResult<Document> searchDocuments = getProcessAPI().searchDocuments(searchOptions);\n        assertEquals(0, searchDocuments.getCount());\n\n        // update\n        assignAndExecuteStep(step1Id, user);\n        waitForUserTask(processInstance, \"step3\");\n\n        // after update\n        searchDocuments = getProcessAPI().searchDocuments(searchOptions);\n        assertEquals(1, searchDocuments.getCount());\n        final Document newDocument = searchDocuments.getResult().iterator().next();\n        assertEquals(\"textFile\", newDocument.getName());\n        assertEquals(url, newDocument.getUrl());\n\n        // cleaup\n        disableAndDeleteProcess(processDefinition);\n\n    }\n\n    private ProcessDefinition deployProcessWithURLDocumentCreateOperation(final String documentName, final String url)\n            throws Exception {\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"simpleProcess\", \"1.0\");\n        designProcessDefinition.addActor(ACTOR_NAME).addDescription(\"The doctor\");\n        designProcessDefinition.addUserTask(\"step1\", ACTOR_NAME);\n        designProcessDefinition.addAutomaticTask(\"step2\").addOperation(\n                new OperationBuilder().createSetDocument(documentName, getDocumentValueExpressionWithUrl(url)));\n        designProcessDefinition.addUserTask(\"step3\", ACTOR_NAME);\n        designProcessDefinition.addTransition(\"step1\", \"step2\");\n        designProcessDefinition.addTransition(\"step2\", \"step3\");\n        return deployAndEnableProcessWithActor(designProcessDefinition.done(), ACTOR_NAME, user);\n    }\n\n    @Test\n    public void getLastVersionOfDocumentsOfAProcess() throws Exception {\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"procWithStringIndexes\", \"1.0\");\n        designProcessDefinition.addActor(ACTOR_NAME).addDescription(\"The doc'\");\n        UserTaskDefinitionBuilder userTaskBuilder = designProcessDefinition.addUserTask(\"step1\", ACTOR_NAME);\n        userTaskBuilder.addOperation(new OperationBuilder().createSetDocument(\"textFile2\",\n                getDocumentValueExpressionWithUrl(\"http://www.example.com/new_url.txt\")));\n        userTaskBuilder = designProcessDefinition.addUserTask(\"step2\", ACTOR_NAME);\n        userTaskBuilder.addOperation(new OperationBuilder().createSetDocument(\"textFile4\",\n                getDocumentValueExpressionWithUrl(\"http://www.example.com/new_url.txt\")));\n        designProcessDefinition.addDocumentDefinition(\"textFile2\").addContentFileName(\"myFile3.pdf\")\n                .addDescription(\"a cool text document\")\n                .addMimeType(\"application/atom+xml\").addUrl(\"http://www.example.com/original_url5.txt\");\n        designProcessDefinition.addDocumentDefinition(\"textFile1\").addContentFileName(\"myFile1.pdf\")\n                .addDescription(\"a cool text document\")\n                .addMimeType(\"plain/csv\").addUrl(\"http://www.example.com/original_url3.txt\");\n        designProcessDefinition.addDocumentDefinition(\"textFile3\").addContentFileName(\"myFile4.pdf\")\n                .addDescription(\"a cool text document\")\n                .addMimeType(\"plain/text\").addUrl(\"http://www.example.com/original_url2.txt\");\n        designProcessDefinition.addDocumentDefinition(\"textFile4\").addContentFileName(\"myFile2.pdf\")\n                .addDescription(\"a cool text document\")\n                .addMimeType(\"application/pdf\").addUrl(\"http://www.example.com/original_url4.txt\");\n        designProcessDefinition.addDocumentDefinition(\"textFile5\").addContentFileName(\"myFile5.pdf\")\n                .addDescription(\"a cool text document\")\n                .addMimeType(\"plain/xml\").addUrl(\"http://www.example.com/original_url1.txt\");\n        designProcessDefinition.addUserTask(\"step3\", ACTOR_NAME);\n        designProcessDefinition.addTransition(\"step1\", \"step2\");\n        designProcessDefinition.addTransition(\"step2\", \"step3\");\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(),\n                ACTOR_NAME, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n        check(processInstance, 1, 2, 3, 4, 5, DocumentCriterion.NAME_ASC);\n        check(processInstance, 5, 4, 3, 2, 1, DocumentCriterion.NAME_DESC);\n        check(processInstance, 1, 4, 2, 3, 5, DocumentCriterion.FILENAME_ASC);\n        check(processInstance, 5, 3, 2, 4, 1, DocumentCriterion.FILENAME_DESC);\n        check(processInstance, 2, 4, 1, 3, 5, DocumentCriterion.MIMETYPE_ASC);\n        check(processInstance, 5, 3, 1, 4, 2, DocumentCriterion.MIMETYPE_DESC);\n        check(processInstance, 5, 3, 1, 4, 2, DocumentCriterion.URL_ASC);\n        check(processInstance, 2, 4, 1, 3, 5, DocumentCriterion.URL_DESC);\n\n        final User john = createUser(\"john\", \"bpm\");\n        logout();\n        loginOnDefaultTenantWith(\"john\", \"bpm\");\n        assignAndExecuteStep(step1Id, john.getId());\n        final long step2Id = waitForUserTask(processInstance, \"step2\");\n\n        // user id of john > matti\n        check(processInstance, 1, 3, 4, 5, 2, DocumentCriterion.AUTHOR_ASC);\n        check(processInstance, 2, 1, 3, 4, 5, DocumentCriterion.AUTHOR_DESC);\n\n        // Assign and execute step2\n        assignAndExecuteStep(step2Id, john.getId());\n        waitForUserTask(processInstance, \"step3\");\n\n        // special check because date can be too close depending on systems\n        final List<Document> dateAsc = getProcessAPI().getLastVersionOfDocuments(processInstance.getId(), 0, 10,\n                DocumentCriterion.CREATION_DATE_ASC);\n        assertEquals(\"textFile2\", dateAsc.get(3).getName());\n        assertEquals(\"textFile4\", dateAsc.get(4).getName());\n        final List<Document> dateDesc = getProcessAPI().getLastVersionOfDocuments(processInstance.getId(), 0, 10,\n                DocumentCriterion.CREATION_DATE_DESC);\n        assertEquals(\"textFile2\", dateDesc.get(1).getName());\n        assertEquals(\"textFile4\", dateDesc.get(0).getName());\n\n        disableAndDeleteProcess(processDefinition);\n        deleteUser(john);\n    }\n\n    @Test(expected = InvalidProcessDefinitionException.class)\n    public void startProcessWithLongSizeDocumentName() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithDocumentWithLongName\", \"1.0\");\n        processBuilder.addActor(ACTOR_NAME).addUserTask(\"step1\", ACTOR_NAME);\n\n        // Build fileName with 256 characters\n        final StringBuilder builder = new StringBuilder();\n        for (int i = 0; i < 5; i++) {\n            builder.append(\"aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee\"); // + 50 characters\n        }\n        builder.append(\"12.pdf\");\n        final String fileName = builder.toString();\n        assertEquals(256, fileName.length());\n\n        // Build document\n        processBuilder.addDocumentDefinition(\"doc\").addFile(\"myPdf.pdf\").addContentFileName(fileName)\n                .addMimeType(\"application/octet-stream\");\n\n        processBuilder.getProcess();\n    }\n\n    @Test\n    public void startProcessWithMaxSizeDocumentName() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithDocumentWithLongName\", \"1.0\");\n        processBuilder.addActor(ACTOR_NAME).addUserTask(\"step1\", ACTOR_NAME);\n\n        // Build fileName with 255 characters\n        final StringBuilder builder = new StringBuilder();\n        for (int i = 0; i < 5; i++) {\n            builder.append(\"aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee\"); // + 50 characters\n        }\n        builder.append(\"1.pdf\");\n        final String fileName = builder.toString();\n        assertEquals(255, fileName.length());\n\n        // Build document\n        final byte[] pdfContent = \"Some document content\".getBytes();\n        processBuilder.addDocumentDefinition(\"doc\").addFile(\"myPdf.pdf\").addContentFileName(fileName)\n                .addMimeType(\"application/octet-stream\");\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(processBuilder.getProcess())\n                .addDocumentResource(new BarResource(\"myPdf.pdf\", pdfContent)).done();\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchive, ACTOR_NAME, user);\n        getProcessAPI().startProcess(processDefinition.getId());\n\n        disableAndDeleteProcess(processDefinition.getId());\n    }\n\n    @Test(expected = InvalidProcessDefinitionException.class)\n    public void startProcessWithLongSizeDocumentURL() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithDocumentWithLongName\", \"1.0\");\n        processBuilder.addActor(ACTOR_NAME).addUserTask(\"step1\", ACTOR_NAME);\n\n        // Build URL with 256 characters\n        final StringBuilder builder = new StringBuilder(\"http://intranet.bonitasoft.com/private/docStorage/\");\n        for (int i = 0; i < 975; i++) {\n            builder.append(\"a\"); // + 50 characters\n        }\n        final String url = builder.toString();\n        assertEquals(1025, url.length());\n\n        // Build document\n        final String docName = \"myRtfDocument\";\n        final DocumentDefinitionBuilder documentDefinition = processBuilder.addDocumentDefinition(docName);\n        documentDefinition.addUrl(url);\n\n        processBuilder.getProcess();\n    }\n\n    @Test\n    public void startProcessWithMaxSizeDocumentURL() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithDocumentWithLongName\", \"1.0\");\n        processBuilder.addActor(ACTOR_NAME).addUserTask(\"step1\", ACTOR_NAME);\n\n        // Build URL with 255 characters\n        final StringBuilder builder = new StringBuilder(\"http://intranet.bonitasoft.com/private/docStorage/\");\n        for (int i = 0; i < 974; i++) {\n            builder.append(\"a\"); // + 50 characters\n        }\n        final String url = builder.toString();\n        assertEquals(1024, url.length());\n\n        // Build document\n        final String docName = \"myRtfDocument\";\n        final DocumentDefinitionBuilder documentDefinition = processBuilder.addDocumentDefinition(docName);\n        documentDefinition.addUrl(url);\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(processBuilder.getProcess())\n                .done();\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchive, ACTOR_NAME, user);\n        getProcessAPI().startProcess(processDefinition.getId());\n\n        disableAndDeleteProcess(processDefinition.getId());\n\n    }\n\n    @Test\n    public void startProcessWithDocumentDefinitionWithNoInitialValue() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithDocumentWithLongName\", \"1.0\");\n        processBuilder.addActor(ACTOR_NAME).addUserTask(\"step1\", ACTOR_NAME);\n        processBuilder.addDocumentDefinition(\"plop\");\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME,\n                user);\n        getProcessAPI().startProcess(processDefinition.getId());\n\n        //process should be started even if no initial value on document\n        waitForUserTask(\"step1\");\n\n        disableAndDeleteProcess(processDefinition.getId());\n\n    }\n\n    private Expression getDocumentValueExpressionWithUrl(final String url) throws InvalidExpressionException {\n        return new ExpressionBuilder().createGroovyScriptExpression(\"script\",\n                \"return new org.bonitasoft.engine.bpm.document.DocumentValue(\\\"\" + url + \"\\\");\",\n                DocumentValue.class.getName());\n    }\n\n    private void check(final ProcessInstance processInstance, final int one, final int two, final int three,\n            final int four, final int five,\n            final DocumentCriterion documentCriterion) throws ProcessInstanceNotFoundException, DocumentException {\n        final List<Document> lastVersionOfDocuments = getProcessAPI().getLastVersionOfDocuments(processInstance.getId(),\n                0, 10, documentCriterion);\n        Assertions.assertThat(lastVersionOfDocuments).as(\"the order was not respected for \" + documentCriterion)\n                .extracting(\"name\").containsExactly(\"textFile\" + one, \"textFile\" + two, \"textFile\" + three,\n                        \"textFile\" + four, \"textFile\" + five);\n    }\n\n    @Test\n    public void getMIDocumentOnProcess() throws Exception {\n        final ExpressionBuilder expressionBuilder = new ExpressionBuilder();\n        final ProcessDefinitionBuilder miBuilder = new ProcessDefinitionBuilder().createNewInstance(\"MI\", \"0.8\");\n        miBuilder.addData(\"urls\", List.class.getName(),\n                expressionBuilder.createGroovyScriptExpression(\"urls\",\n                        \"[\\\"http://someurl\\\", \\\"http://someurl1\\\", \\\"http://someurl2\\\"]\", List.class.getName()));\n        final CallActivityBuilder callActivityBuilder = miBuilder.addCallActivity(\"mi\",\n                expressionBuilder.createConstantStringExpression(\"DocSubProcess\"),\n                expressionBuilder.createConstantStringExpression(\"0.4\"));\n        callActivityBuilder.addShortTextData(\"url\", null);\n        callActivityBuilder\n                .addDataInputOperation(\n                        new OperationBuilder().createSetDataOperation(\"url\",\n                                expressionBuilder.createDataExpression(\"url\", String.class.getName())))\n                .addMultiInstance(false, \"urls\")\n                .addDataInputItemRef(\"url\")\n                .addCompletionCondition(\n                        expressionBuilder.createGroovyScriptExpression(\"urls\",\n                                \"numberOfCompletedInstances == urls.size();\", Boolean.class.getName(),\n                                expressionBuilder.createDataExpression(\"urls\", List.class.getName())));\n\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"DocSubProcess\",\n                \"0.4\");\n        builder.addDocumentDefinition(\"caseDocument\").addUrl(\"toto\");\n        builder.addShortTextData(\"url\", null);\n        builder.addActor(ACTOR_NAME);\n        builder.addUserTask(\"step1\", ACTOR_NAME).addOperation(\n                new OperationBuilder().createSetDocument(\"caseDocument\", expressionBuilder.createGroovyScriptExpression(\n                        \"addDocVersion\",\n                        \"import org.bonitasoft.engine.bpm.document.DocumentValue;return new DocumentValue(url);\",\n                        DocumentValue.class.getName(),\n                        expressionBuilder.createDataExpression(\"url\", String.class.getName()))));\n        builder.addUserTask(\"step2\", ACTOR_NAME).addTransition(\"step1\", \"step2\");\n        final ProcessDefinition docDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user);\n        final ProcessDefinition miDefinition = deployAndEnableProcess(miBuilder.done());\n        getProcessAPI().startProcess(miDefinition.getId());\n        waitForUserTaskAndExecuteIt(\"step1\", user);\n        waitForUserTaskAndExecuteIt(\"step1\", user);\n        waitForUserTaskAndExecuteIt(\"step1\", user);\n        waitForUserTask(\"step2\");\n        waitForUserTask(\"step2\");\n        waitForUserTask(\"step2\");\n\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        searchOptionsBuilder.sort(DocumentsSearchDescriptor.DOCUMENT_URL, Order.ASC);\n        final SearchResult<Document> searchDocuments = getProcessAPI().searchDocuments(searchOptionsBuilder.done());\n        assertEquals(3, searchDocuments.getCount());\n        final List<Document> documents = searchDocuments.getResult();\n        final List<String> urls = new ArrayList<>();\n        urls.add(documents.get(0).getUrl());\n        urls.add(documents.get(1).getUrl());\n        urls.add(documents.get(2).getUrl());\n\n        assertEquals(asList(\"http://someurl\", \"http://someurl1\", \"http://someurl2\"), urls);\n\n        disableAndDeleteProcess(miDefinition);\n        disableAndDeleteProcess(docDefinition);\n    }\n\n    @Test\n    public void getDocumentOnACallActivityOfAProcess() throws Exception {\n        final ExpressionBuilder expressionBuilder = new ExpressionBuilder();\n\n        final ProcessDefinitionBuilder spBuilder = new ProcessDefinitionBuilder().createNewInstance(\"SubProcess\",\n                \"0.8\");\n        spBuilder.addActor(ACTOR_NAME);\n        spBuilder.addUserTask(\"step1\", ACTOR_NAME);\n        spBuilder.addDocumentDefinition(\"document1\").addMimeType(\"application/octet-stream\").addContentFileName(\"file\")\n                .addFile(\"file\");\n        final byte[] pdfContent = new byte[] { 5, 0, 1, 4, 6, 5, 2, 3, 1, 5, 6, 8, 4, 6, 6, 3, 2, 4, 5 };\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(spBuilder.getProcess())\n                .addDocumentResource(new BarResource(\"file\", pdfContent)).done();\n        final ProcessDefinition docDefinition = deployAndEnableProcessWithActor(businessArchive, ACTOR_NAME, user);\n\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"CAProcess\", \"0.4\");\n        builder.addActor(ACTOR_NAME);\n        builder.addCallActivity(\"ca\", expressionBuilder.createConstantStringExpression(\"SubProcess\"),\n                expressionBuilder.createConstantStringExpression(\"0.8\"));\n        final ProcessDefinition caDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user);\n\n        getProcessAPI().startProcess(caDefinition.getId());\n        final long step1Id = waitForUserTask(\"step1\");\n\n        final Expression expression = expressionBuilder.createDocumentReferenceExpression(\"document1\");\n        final Map<Expression, Map<String, Serializable>> expressions = new HashMap<>(5);\n        expressions.put(expression, new HashMap<String, Serializable>());\n\n        final Map<String, Serializable> docsMap = getProcessAPI().evaluateExpressionsOnActivityInstance(step1Id,\n                expressions);\n\n        assertNotNull(docsMap);\n        final String fileName = ((Document) docsMap.get(expression.getName())).getContentFileName();\n        assertEquals(\"file\", fileName);\n\n        disableAndDeleteProcess(caDefinition);\n        disableAndDeleteProcess(docDefinition);\n    }\n\n    public ProcessInstance deployAndEnableWithActorAndStartIt(final User user) throws Exception {\n        return deployAndEnableProcessWithActorAndStartIt(getNormalBar(), user);\n    }\n\n    public BusinessArchive getNormalBar()\n            throws InvalidProcessDefinitionException, InvalidBusinessArchiveFormatException {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process\",\n                        String.valueOf(processVersion++), asList(\"step1\", \"step2\"), asList(true, true), ACTOR_NAME,\n                        false);\n        return new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(designProcessDefinition)\n                .done();\n    }\n\n    @Test\n    public void processWithDocumentList() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithListOfDoc\", \"1.0\");\n        builder.addActor(\"john\");\n        builder.addLongData(\"doc1Id\", null);\n        builder.addLongData(\"doc2Id\", null);\n        builder.addUserTask(\"step1\", \"john\");\n        final Expression scriptExpression1 = new ExpressionBuilder()\n                .createGroovyScriptExpression(\n                        \"updateDocs\",\n                        \"[new org.bonitasoft.engine.bpm.document.DocumentValue(doc2Id), \" +\n                                \"new org.bonitasoft.engine.bpm.document.DocumentValue(\\\"newFile\\\".getBytes(),\\\"plain/text\\\",\\\"file.txt\\\"),\"\n                                +\n                                \"new org.bonitasoft.engine.bpm.document.DocumentValue(doc1Id,\\\"updatedDocFromUrl\\\".getBytes(),\\\"plain/text\\\",\\\"file.txt\\\")]\",\n                        List.class.getName(),\n                        new ExpressionBuilder().createDataExpression(\"doc1Id\", Long.class.getName()),\n                        new ExpressionBuilder().createDataExpression(\"doc2Id\", Long.class.getName()));\n        final Expression scriptExpression2 = new ExpressionBuilder()\n                .createGroovyScriptExpression(\n                        \"updateDocs\",\n                        \"[new org.bonitasoft.engine.bpm.document.DocumentValue(\\\"updatedDoc\\\".getBytes(),\\\"plain/text\\\",\\\"file.txt\\\")]\",\n                        List.class.getName(),\n                        new ExpressionBuilder().createDataExpression(\"doc2Id\", Long.class.getName()));\n        final UserTaskDefinitionBuilder userTaskDefinitionBuilder = builder.addUserTask(\"updateStep\", \"john\");\n        userTaskDefinitionBuilder\n                .addOperation(new OperationBuilder().createSetDocumentList(\"invoicesCopy\",\n                        new ExpressionBuilder().createDocumentListExpression(\"invoices\")));\n        userTaskDefinitionBuilder\n                .addOperation(new OperationBuilder().createSetDocumentList(\"invoices\", scriptExpression1));\n        userTaskDefinitionBuilder\n                .addOperation(new OperationBuilder().createSetDocumentList(\"emptyList\", scriptExpression2));\n        //        userTaskDefinitionBuilder.addOperation(new OperationBuilder().createSetDocumentList(\"unknown\", scriptExpression2));\n        final UserTaskDefinitionBuilder verifyStepBuilder = builder.addUserTask(\"verifyStep\", \"john\");\n        verifyStepBuilder\n                .addDisplayDescription(new ExpressionBuilder().createGroovyScriptExpression(\"getInvoicesListSize\",\n                        \"String.valueOf(invoices.size())\",\n                        String.class.getName(),\n                        new ExpressionBuilder().createDocumentListExpression(\"invoices\")));\n        builder.addTransition(\"step1\", \"updateStep\");\n        builder.addTransition(\"updateStep\", \"verifyStep\");\n        final DocumentListDefinitionBuilder invoices = builder.addDocumentListDefinition(\"invoices\");\n        invoices.addDescription(\"My invoices\");\n        final String script = \"[new org.bonitasoft.engine.bpm.document.DocumentValue(\\\"http://www.myurl.com/mydoc.txt\\\"), \"\n                +\n                \"new org.bonitasoft.engine.bpm.document.DocumentValue(\\\"hello1\\\".getBytes(),\\\"plain/text\\\",\\\"file.txt\\\"),\"\n                +\n                \"new org.bonitasoft.engine.bpm.document.DocumentValue(\\\"hello2\\\".getBytes(),\\\"plain/text\\\",\\\"file.txt\\\"),\"\n                +\n                \"null,\" +\n                \"new org.bonitasoft.engine.bpm.document.DocumentValue(\\\"hello3\\\".getBytes(),\\\"plain/text\\\",\\\"file.txt\\\"),\"\n                +\n                \"new org.bonitasoft.engine.bpm.contract.FileInputValue(\\\"file.txt\\\", \\\"hello4\\\".getBytes())\" +\n                \"]\";\n        invoices.addInitialValue(new ExpressionBuilder().createGroovyScriptExpression(\"initialDocs\",\n                script,\n                List.class.getName()));\n        final DocumentListDefinitionBuilder invoicesCopy = builder.addDocumentListDefinition(\"invoicesCopy\");\n        invoicesCopy.addDescription(\"My invoices copy\");\n        builder.addDocumentListDefinition(\"emptyList\");\n        final User john = createUser(\"john\", \"bpm\");\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), \"john\", john);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n\n        //we have a process with an initialized list and a non initialized list\n\n        //check with api methods\n        List<Document> invoices1 = getProcessAPI().getDocumentList(processInstance.getId(), \"invoices\", 0, 100);\n        assertThat(invoices1).hasSize(5);\n        final Document urlDocument = invoices1.get(0);\n        assertThat(urlDocument.getUrl()).isEqualTo(\"http://www.myurl.com/mydoc.txt\");\n        final Document fileDocument = invoices1.get(1);\n        assertThat(getProcessAPI().getDocumentContent(fileDocument.getContentStorageId()))\n                .isEqualTo(\"hello1\".getBytes());\n        final Document fileFromFileInput = invoices1.get(4);\n        assertThat(getProcessAPI().getDocumentContent(fileFromFileInput.getContentStorageId()))\n                .isEqualTo(\"hello4\".getBytes());\n        List<Document> invoicesCopyList = getProcessAPI().getDocumentList(processInstance.getId(), \"invoicesCopy\", 0,\n                100);\n        assertThat(invoicesCopyList).as(\"initial value of the list that will copy invoices\").isEmpty();\n\n        List<Document> emptyList = getProcessAPI().getDocumentList(processInstance.getId(), \"emptyList\", 0, 100);\n        assertThat(emptyList).isEmpty();\n        try {\n\n            getProcessAPI().getDocumentList(processInstance.getId(), \"unknown\", 0, 100);\n            fail(\"should not find document list unknown\");\n        } catch (final DocumentNotFoundException e) {\n            //ok\n        }\n        getProcessAPI().updateProcessDataInstance(\"doc1Id\", processInstance.getId(), urlDocument.getId());\n        getProcessAPI().updateProcessDataInstance(\"doc2Id\", processInstance.getId(), fileDocument.getId());\n\n        //execute operation to update\n        assignAndExecuteStep(step1Id, john.getId());\n        waitForUserTaskAndExecuteIt(processInstance, \"updateStep\", john);\n        final HumanTaskInstance verifyStep = waitForUserTaskAndGetIt(\"verifyStep\");\n\n        //check with api methods\n        invoices1 = getProcessAPI().getDocumentList(processInstance.getId(), \"invoices\", 0, 100);\n        assertThat(invoices1).hasSize(3);\n        final Document movedFileDocument = invoices1.get(0);\n        assertThat(movedFileDocument.getIndex()).isEqualTo(0);// was in index 2, now in index 1\n        assertThat(movedFileDocument.getId()).isEqualTo(fileDocument.getId());\n\n        Document emptyListDoc = invoices1.get(1);\n        assertThat(emptyListDoc.hasContent()).isTrue();\n        assertThat(emptyListDoc.getContentFileName()).isEqualTo(\"file.txt\");\n        assertThat(getProcessAPI().getDocumentContent(emptyListDoc.getContentStorageId()))\n                .isEqualTo(\"newFile\".getBytes());\n\n        Document updatedUrlFile = invoices1.get(2);\n        assertThat(updatedUrlFile.getId()).isEqualTo(urlDocument.getId());\n        assertThat(updatedUrlFile.hasContent()).isTrue();\n        assertThat(updatedUrlFile.getContentFileName()).isEqualTo(\"file.txt\");\n        assertThat(updatedUrlFile.getVersion()).isEqualTo(\"2\");\n        assertThat(new String(getProcessAPI().getDocumentContent(updatedUrlFile.getContentStorageId())))\n                .isEqualTo(\"updatedDocFromUrl\");\n\n        assertThat(getProcessAPI().getDocumentList(processInstance.getId(), \"invoices\", 1, 1).get(0))\n                .isEqualTo(emptyListDoc);\n\n        emptyList = getProcessAPI().getDocumentList(processInstance.getId(), \"emptyList\", 0, 100);\n        assertThat(emptyList).hasSize(1);\n\n        emptyListDoc = emptyList.get(0);\n        assertThat(emptyListDoc.hasContent()).isTrue();\n        assertThat(emptyListDoc.getContentFileName()).isEqualTo(\"file.txt\");\n        assertThat(getProcessAPI().getDocumentContent(emptyListDoc.getContentStorageId()))\n                .isEqualTo(\"updatedDoc\".getBytes());\n\n        invoicesCopyList = getProcessAPI().getDocumentList(processInstance.getId(), \"invoicesCopy\", 0, 100);\n        assertThat(invoicesCopyList).hasSize(5);\n        final Document urlDocumentCopy = invoicesCopyList.get(0);\n        assertThat(urlDocumentCopy.getUrl()).isEqualTo(\"http://www.myurl.com/mydoc.txt\");\n        final Document fileDocumentCopy = invoicesCopyList.get(1);\n        assertThat(getProcessAPI().getDocumentContent(fileDocumentCopy.getContentStorageId()))\n                .isEqualTo(\"hello1\".getBytes());\n        final Document fileFromFileInputCopy = invoicesCopyList.get(4);\n        assertThat(getProcessAPI().getDocumentContent(fileFromFileInputCopy.getContentStorageId()))\n                .isEqualTo(\"hello4\".getBytes());\n\n        //        List<Document> unknown = getProcessAPI().getDocumentList(processInstance.getId(), \"unknown\");\n        //        assertThat(unknown).hasSize(1);\n\n        //modify list with api method\n        getProcessAPI().setDocumentList(processInstance.getId(), \"invoices\",\n                Collections.singletonList(new DocumentValue(updatedUrlFile.getId())));\n\n        final List<Document> invoices2 = getProcessAPI().getDocumentList(processInstance.getId(), \"invoices\", 0, 100);\n        assertThat(invoices2).hasSize(1);\n        updatedUrlFile = invoices2.get(0);\n        assertThat(updatedUrlFile.getId()).isEqualTo(urlDocument.getId());\n        assertThat(updatedUrlFile.hasContent()).isTrue();\n        assertThat(updatedUrlFile.getContentFileName()).isEqualTo(\"file.txt\");\n        assertThat(updatedUrlFile.getVersion()).isEqualTo(\"2\");\n        assertThat(new String(getProcessAPI().getDocumentContent(updatedUrlFile.getContentStorageId())))\n                .isEqualTo(\"updatedDocFromUrl\");\n\n        //expression is executed on the display name of the verify step, the display name is the list size\n        assertThat(verifyStep.getDisplayDescription()).isEqualTo(\"3\");\n\n        //update empty list to have 3 version archived\n        getProcessAPI().setDocumentList(processInstance.getId(), \"emptyList\",\n                asList(new DocumentValue(emptyListDoc.getId(), \"anUrl1\"), new DocumentValue(\"anUrl2\")));\n        getProcessAPI().setDocumentList(processInstance.getId(), \"emptyList\",\n                asList(new DocumentValue(\"anUrl3\"), new DocumentValue(\"anUrl4\")));\n\n        final SearchResult<ArchivedDocument> searchAllVersions = getProcessAPI()\n                .searchArchivedDocuments(new SearchOptionsBuilder(0, 100)\n                        .filter(ArchivedDocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId())\n                        .sort(ArchivedDocumentsSearchDescriptor.DOCUMENT_NAME, Order.ASC)\n                        .sort(ArchivedDocumentsSearchDescriptor.DOCUMENT_VERSION, Order.ASC).done());\n\n        assertThat(searchAllVersions.getCount()).isEqualTo(9);\n        final List<ArchivedDocument> result = searchAllVersions.getResult();\n        assertThat(result.get(0).getName()).isEqualTo(\"emptyList\");\n        assertThat(result.get(0).getVersion()).isEqualTo(\"1\");\n        assertThat(result.get(1).getName()).isEqualTo(\"emptyList\");\n        assertThat(result.get(1).getVersion()).isEqualTo(\"1\");\n        assertThat(result.get(2).getName()).isEqualTo(\"emptyList\");\n        assertThat(result.get(2).getVersion()).isEqualTo(\"2\");\n        assertThat(result.get(3).getName()).isEqualTo(\"invoices\");\n        assertThat(result.get(3).getVersion()).isEqualTo(\"1\");\n\n        disableAndDeleteProcess(processDefinition);\n        deleteUser(john);\n    }\n\n    @Test\n    public void deleteContentOfArchivedDocumentTest() throws Exception {\n        //given\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithDocumentToDelete\", \"1.0\");\n        final User john = createUser(\"john\", \"bpm\");\n        builder.addActor(\"actor\");\n        builder.addUserTask(\"step1\", \"actor\");\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), \"actor\", john);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final Document doc1v1 = getProcessAPI().attachDocument(processInstance.getId(), \"doc1\", \"fileWithContent.txt\",\n                \"plain/text\", \"TheContent1\".getBytes());\n        getProcessAPI().attachNewDocumentVersion(processInstance.getId(), \"doc1\", \"fileWithContent.txt\", \"plain/text\",\n                \"theUrl\");\n        final Document doc1v3 = getProcessAPI().attachNewDocumentVersion(processInstance.getId(), \"doc1\",\n                \"fileWithContent.txt\", \"plain/text\",\n                \"TheContent2\".getBytes());\n        final Document doc2v1 = getProcessAPI().attachDocument(processInstance.getId(), \"doc2\", \"fileWithContent.txt\",\n                \"plain/text\", \"TheContent\".getBytes());\n        final Document doc2v2 = getProcessAPI().attachNewDocumentVersion(processInstance.getId(), \"doc2\",\n                \"fileWithContent.txt\", \"plain/text\",\n                \"TheContent2\".getBytes());\n        final Document doc2v3 = getProcessAPI().attachNewDocumentVersion(processInstance.getId(), \"doc2\",\n                \"fileWithContent.txt\", \"plain/text\",\n                \"TheContent3\".getBytes());\n\n        //when\n        final SearchResult<ArchivedDocument> archivedDocumentSearchResult = getProcessAPI().searchArchivedDocuments(\n                new SearchOptionsBuilder(0, 100)\n                        .filter(ArchivedDocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId())\n                        .filter(ArchivedDocumentsSearchDescriptor.DOCUMENT_NAME, \"doc1\")\n                        .sort(ArchivedDocumentsSearchDescriptor.DOCUMENT_VERSION, Order.ASC)\n                        .done());\n\n        final ArchivedDocument archDov1v1 = archivedDocumentSearchResult.getResult().get(0);\n        assertThat(archDov1v1.getContentStorageId()).isEqualTo(doc1v1.getContentStorageId());\n        getProcessAPI().deleteContentOfArchivedDocument(archDov1v1.getId());\n\n        //then\n        assertThat(getProcessAPI().getDocumentContent(doc1v1.getContentStorageId())).isNull();\n        assertThat(getProcessAPI().getDocumentContent(doc1v3.getContentStorageId())).isNotNull();\n        assertThat(getProcessAPI().getDocumentContent(doc2v1.getContentStorageId())).isNotNull();\n        assertThat(getProcessAPI().getDocumentContent(doc2v2.getContentStorageId())).isNotNull();\n        assertThat(getProcessAPI().getDocumentContent(doc2v3.getContentStorageId())).isNotNull();\n\n        disableAndDeleteProcess(processDefinition);\n        deleteUser(john);\n    }\n\n    @Test\n    public void add_and_update_a_single_document() throws Exception {\n        //process with doc1 init and doc2 non init\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"ProcessWithDocToUpdate\", \"1.0\");\n        builder.addDocumentDefinition(\"doc1\").addUrl(\"the url\");\n        builder.addDocumentDefinition(\"doc2\");\n        builder.addActor(\"actor\").addUserTask(\"step1\", \"actor\");\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), \"actor\", user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        //add doc2\n        getProcessAPI().addDocument(processInstance.getId(), \"doc2\", \"my doc\", new DocumentValue(\"the new url\"));\n        //add doc3\n        getProcessAPI().addDocument(processInstance.getId(), \"doc3\", \"my doc\", new DocumentValue(\"the new url\"));\n        //add doc1: fail\n        try {\n            getProcessAPI().addDocument(processInstance.getId(), \"doc1\", \"my doc\", new DocumentValue(\"the new url\"));\n            fail(\"should not be able to add a document on an existing document\");\n        } catch (final AlreadyExistsException e) {\n            //ok\n        }\n        //add doc4 with index: fail\n        try {\n            getProcessAPI().addDocument(processInstance.getId(), \"doc1\", \"my doc\",\n                    new DocumentValue(\"the new url\").setIndex(12));\n            fail(\"should not be able to add a document with index when there is no list\");\n        } catch (final DocumentAttachmentException e) {\n            //ok\n        }\n\n        List<Document> result = getProcessAPI().searchDocuments(\n                new SearchOptionsBuilder(0, 100)\n                        .filter(DocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId())\n                        .sort(DocumentsSearchDescriptor.DOCUMENT_NAME, Order.ASC).done())\n                .getResult();\n        assertThat(result).hasSize(3);\n        assertThat(result.get(0).getName()).isEqualTo(\"doc1\");\n        assertThat(result.get(1).getName()).isEqualTo(\"doc2\");\n        assertThat(result.get(2).getName()).isEqualTo(\"doc3\");\n\n        //update doc1\n        getProcessAPI().updateDocument(result.get(0).getId(), new DocumentValue(\"the new url updated\"));\n        //update doc2\n        getProcessAPI().updateDocument(result.get(1).getId(), new DocumentValue(\"the new url updated\"));\n\n        result = getProcessAPI().searchDocuments(\n                new SearchOptionsBuilder(0, 100)\n                        .filter(DocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId())\n                        .sort(DocumentsSearchDescriptor.DOCUMENT_NAME, Order.ASC).done())\n                .getResult();\n        assertThat(result).hasSize(3);\n        assertThat(result.get(0).getVersion()).isEqualTo(\"2\");\n        assertThat(result.get(1).getVersion()).isEqualTo(\"2\");\n        assertThat(result.get(2).getVersion()).isEqualTo(\"1\");\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Test\n    public void add_and_update_a_list_of_document() throws Exception {\n        final ProcessInstance processInstance = deployProcessWithList();\n\n        // To ensure doc1_1 is not added in the same millis as the process instance start date:\n        Thread.sleep(20);\n\n        //add doc1_1 to list1\n        getProcessAPI().addDocument(processInstance.getId(), \"list1\", \"doc list\", new DocumentValue(\"doc1_1\"));\n        //add doc1_2 to list1 with bad index: fail\n        try {\n            getProcessAPI().addDocument(processInstance.getId(), \"list1\", \"doc list\",\n                    new DocumentValue(\"doc1_2\").setIndex(12));\n            fail(\"should not be able to add a document on a list with bad index\");\n        } catch (final DocumentAttachmentException e) {\n            //ok\n        }\n        //add doc1_2 to list1 with good index\n        getProcessAPI().addDocument(processInstance.getId(), \"list1\", \"doc list\",\n                new DocumentValue(\"doc1_2\").setIndex(0));\n\n        waitForUserTaskAndExecuteIt(processInstance, \"step1\", user);\n        final long step2Id = waitForUserTask(processInstance, \"step2\");\n        //add doc1_3 to list1 at the end\n        getProcessAPI().addDocument(processInstance.getId(), \"list1\", \"doc list\", new DocumentValue(\"doc1_3\"));\n        //add doc2 to list2\n        final Document document = getProcessAPI().addDocument(processInstance.getId(), \"list2\", \"doc list\",\n                new DocumentValue(\"doc2\"));\n        //check added\n        final List<Document> list1 = getProcessAPI().getDocumentList(processInstance.getId(), \"list1\", 0, 100);\n        final List<Document> list2 = getProcessAPI().getDocumentList(processInstance.getId(), \"list2\", 0, 100);\n        final SearchResult<Document> list1_search = getProcessAPI().searchDocuments(\n                new SearchOptionsBuilder(0, 100)\n                        .filter(DocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId())\n                        .filter(DocumentsSearchDescriptor.DOCUMENT_NAME, \"list1\")\n                        .sort(DocumentsSearchDescriptor.LIST_INDEX, Order.DESC).done());\n        final ArrayList<Document> reversedList1 = new ArrayList<>(list1);\n        Collections.reverse(reversedList1);\n        assertThat(list1_search.getResult()).isEqualTo(reversedList1);\n        assertThat(list1).hasSize(7);\n        assertThat(list1.get(0).getUrl()).isEqualTo(\"doc1_2\");\n        assertThat(list1.get(5).getUrl()).isEqualTo(\"doc1_1\");\n        assertThat(list1.get(6).getUrl()).isEqualTo(\"doc1_3\");\n        assertThat(list2).hasSize(1);\n        assertThat(list2.get(0).getUrl()).isEqualTo(\"doc2\");\n\n        final Document updated = getProcessAPI().updateDocument(document.getId(), new DocumentValue(\"The new value\"));\n        assertThat(updated.getIndex()).isEqualTo(document.getIndex());\n        assertThat(updated.getUrl()).isEqualTo(\"The new value\");\n\n        final Map<Expression, Map<String, Serializable>> expressions = new HashMap<>();\n        expressions.put(new ExpressionBuilder().createDocumentListExpression(\"list1\"), emptyMap());\n        final List<Document> initialList1 = (List<Document>) getProcessAPI()\n                .evaluateExpressionsAtProcessInstanciation(processInstance.getId(), expressions)\n                .get(\"list1\");\n        try {\n            assertThat(initialList1).hasSize(4);\n        } catch (Exception e) {\n            for (Document doc : initialList1) {\n                APITestUtil.LOGGER.debug(\"{}: {}\", doc.getUrl(), doc.getCreationDate().getTime());\n            }\n            throw e;\n        }\n        assertThat(initialList1.get(0).getUrl()).isEqualTo(\"http://www.myurl.com/mydoc.txt\");\n        assertThat(new String(getProcessAPI().getDocumentContent(initialList1.get(1).getContentStorageId())))\n                .isEqualTo(\"hello1\");\n        assertThat(new String(getProcessAPI().getDocumentContent(initialList1.get(2).getContentStorageId())))\n                .isEqualTo(\"hello2\");\n        assertThat(new String(getProcessAPI().getDocumentContent(initialList1.get(3).getContentStorageId())))\n                .isEqualTo(\"hello3\");\n\n        assignAndExecuteStep(step2Id, user.getId());\n        waitForProcessToFinish(processInstance.getId());\n\n        final List<Document> finalList1 = (List<Document>) getProcessAPI()\n                .evaluateExpressionOnCompletedProcessInstance(processInstance.getId(), expressions)\n                .get(\"list1\");\n        assertThat(finalList1).hasSize(7);\n\n        disableAndDeleteProcess(processInstance.getProcessDefinitionId());\n    }\n\n    @Test\n    public void should_create_and_update_document_list_using_contract() throws Exception {\n\n        User user = getIdentityAPI().createUser(\"james\", \"bpm\");\n        ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"ProcessWithDocFromContract\", \"1.0\");\n        builder.addContract().addFileInput(\"fileInputValues\", \"create my list of document\", true);\n        builder.addDocumentListDefinition(\"myDocumentList\")\n                .addInitialValue(\n                        new ExpressionBuilder().createContractInputExpression(\"fileInputValues\", List.class.getName()));\n        builder.addActor(\"actor\");\n        UserTaskDefinitionBuilder userTaskBuilder = builder.addUserTask(\"updateList\", \"actor\");\n        userTaskBuilder.addOperation(new OperationBuilder().createSetDocumentList(\"myDocumentList\",\n                new ExpressionBuilder().createContractInputExpression(\"fileInputValues\", List.class.getName())));\n        userTaskBuilder.addContract().addFileInput(\"fileInputValues\", \"update my list of document\", true);\n        builder.addUserTask(\"checkListIsUpdated\", \"actor\");\n        builder.addTransition(\"updateList\", \"checkListIsUpdated\");\n        ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.getProcess(), \"actor\", user);\n\n        ArrayList<Serializable> createList = new ArrayList<>(asList(\n                new FileInputValue(\"file1\", \"text/plain\", \"the content\".getBytes()),\n                new FileInputValue(\"file2\", \"text/plain\", \"the content\".getBytes()),\n                new FileInputValue(\"file3\", \"text/plain\", \"the content\".getBytes())));\n        ProcessInstance processInstance = getProcessAPI().startProcessWithInputs(processDefinition.getId(),\n                singletonMap(\"fileInputValues\", createList));\n        long updateListTask = waitForUserTask(\"updateList\");\n\n        //verify list is created\n        List<Document> myDocumentList = getProcessAPI().getDocumentList(processInstance.getId(), \"myDocumentList\", 0,\n                100);\n\n        assertThat(myDocumentList).hasSize(3);\n        assertThat(myDocumentList).extracting(\"fileName\").containsExactly(\"file1\", \"file2\", \"file3\");\n\n        getProcessAPI().assignUserTask(updateListTask, user.getId());\n        ArrayList<Serializable> updateList = new ArrayList<>(asList(\n                new FileInputValue(\"file4\", \"text/plain\", \"the content\".getBytes()),\n                new FileInputValue(\"file3\", \"text/plain\", null, String.valueOf(myDocumentList.get(2).getId())),\n                new FileInputValue(\"file2\", \"text/plain\", \"updated content of file 2\".getBytes(),\n                        String.valueOf(myDocumentList.get(1).getId())),\n                new FileInputValue(\"updated file name\", \"updated content type\", \"updated content of file 1\".getBytes(),\n                        String.valueOf(myDocumentList.get(0).getId()))));\n        getProcessAPI().executeUserTask(user.getId(), updateListTask, singletonMap(\"fileInputValues\", updateList));\n        waitForUserTask(\"checkListIsUpdated\");\n\n        //verify list is update\n        List<Document> myUpdatedDocumentList = getProcessAPI().getDocumentList(processInstance.getId(),\n                \"myDocumentList\", 0, 100);\n        assertThat(myUpdatedDocumentList).hasSize(4);\n\n        //added document\n        assertThat(myUpdatedDocumentList.get(0)).matches(d -> d.getContentFileName().equals(\"file4\"));\n        //moved document\n        assertThat(myUpdatedDocumentList.get(1)).matches(d -> d.getContentFileName().equals(\"file3\") &&\n                d.getContentStorageId().equals(myDocumentList.get(2).getContentStorageId()) &&\n                d.getId() == myDocumentList.get(2).getId());\n        //document with content updated\n        assertThat(myUpdatedDocumentList.get(2)).matches(d -> d.getContentFileName().equals(\"file2\") &&\n                !d.getContentStorageId().equals(myDocumentList.get(1).getContentStorageId()) &&\n                d.getId() == myDocumentList.get(1).getId());\n        //document with content updated and file name\n        assertThat(myUpdatedDocumentList.get(3)).matches(d -> d.getContentFileName().equals(\"updated file name\") &&\n                d.getContentMimeType().equals(\"updated content type\") &&\n                !d.getContentStorageId().equals(myDocumentList.get(0).getContentStorageId()) &&\n                d.getId() == myDocumentList.get(0).getId());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void should_create_and_update_document_using_contract() throws Exception {\n\n        User user = getIdentityAPI().createUser(\"james\", \"bpm\");\n        ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"ProcessWithDocFromContract\", \"1.0\");\n        builder.addContract().addFileInput(\"fileInputValue\", \"create my document\");\n        builder.addDocumentDefinition(\"myDocument\")\n                .addInitialValue(new ExpressionBuilder().createContractInputExpression(\"fileInputValue\",\n                        FileInputValue.class.getName()));\n        builder.addActor(\"actor\");\n        UserTaskDefinitionBuilder userTaskBuilder = builder.addUserTask(\"update\", \"actor\");\n        userTaskBuilder.addOperation(new OperationBuilder().createSetDocument(\"myDocument\", new ExpressionBuilder()\n                .createContractInputExpression(\"fileInputValue\", FileInputValue.class.getName())));\n        userTaskBuilder.addContract().addFileInput(\"fileInputValue\", \"update my document\");\n        builder.addUserTask(\"checkIsUpdated\", \"actor\");\n        builder.addTransition(\"update\", \"checkIsUpdated\");\n        ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.getProcess(), \"actor\", user);\n\n        ProcessInstance processInstance = getProcessAPI().startProcessWithInputs(processDefinition.getId(),\n                singletonMap(\"fileInputValue\", new FileInputValue(\"file1\", \"text/plain\", \"the content\".getBytes())));\n        long checkListIsCreated = waitForUserTask(\"update\");\n\n        //verify list is created\n        Document myDocument = getProcessAPI().getLastDocument(processInstance.getId(), \"myDocument\");\n\n        assertThat(myDocument.getContentFileName()).isEqualTo(\"file1\");\n\n        getProcessAPI().assignUserTask(checkListIsCreated, user.getId());\n\n        getProcessAPI().executeUserTask(user.getId(), checkListIsCreated, singletonMap(\"fileInputValue\",\n                new FileInputValue(\"file1\", \"text/plain\", \"the content\".getBytes(),\n                        String.valueOf(myDocument.getId()))));\n        waitForUserTask(\"checkIsUpdated\");\n\n        //verify list is update\n\n        Document myUpdatedDocument = getProcessAPI().getLastDocument(processInstance.getId(), \"myDocument\");\n\n        //document with content updated\n        assertThat(myUpdatedDocument.getId()).isEqualTo(myDocument.getId());\n        assertThat(myUpdatedDocument.getContentStorageId()).isNotEqualTo(myDocument.getContentStorageId());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void removeDocumentFromList() throws Exception {\n        final ProcessInstance processInstance = deployProcessWithList();\n        final List<Document> list1 = getProcessAPI().getDocumentList(processInstance.getId(), \"list1\", 0, 100);\n        final Document document = getProcessAPI().removeDocument(list1.get(1).getId());\n        assertThat(document).isEqualTo(list1.get(1));\n        list1.remove(1);\n        final List<Document> updatedList = getProcessAPI().getDocumentList(processInstance.getId(), \"list1\", 0, 100);\n        assertThat(updatedList.get(0).getIndex()).isEqualTo(0);\n        assertThat(updatedList.get(1).getIndex()).isEqualTo(1);\n        assertThat(updatedList.get(2).getIndex()).isEqualTo(2);\n        assertThat(updatedList.get(0).getId()).isEqualTo(list1.get(0).getId());\n        assertThat(updatedList.get(1).getId()).isEqualTo(list1.get(1).getId());\n        assertThat(updatedList.get(2).getId()).isEqualTo(list1.get(2).getId());\n\n        disableAndDeleteProcess(processInstance.getProcessDefinitionId());\n    }\n\n    private ProcessInstance deployProcessWithList() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"ProcessWithDocToUpdate\", \"1.0\");\n        //process with list1 with init value\n        final String script = \"[new org.bonitasoft.engine.bpm.document.DocumentValue(\\\"http://www.myurl.com/mydoc.txt\\\"), \"\n                +\n                \"new org.bonitasoft.engine.bpm.document.DocumentValue(\\\"hello1\\\".getBytes(),\\\"plain/text\\\",\\\"file1.txt\\\"),\"\n                +\n                \"new org.bonitasoft.engine.bpm.document.DocumentValue(\\\"hello2\\\".getBytes(),\\\"plain/text\\\",\\\"file2.txt\\\"),\"\n                +\n                \"new org.bonitasoft.engine.bpm.document.DocumentValue(\\\"hello3\\\".getBytes(),\\\"plain/text\\\",\\\"file3.txt\\\")\"\n                +\n                \"]\";\n        builder.addDocumentListDefinition(\"list1\")\n                .addInitialValue(new ExpressionBuilder().createGroovyScriptExpression(\"initialDocs\",\n                        script,\n                        List.class.getName()));\n        //process with list2 without initial value\n        builder.addDocumentListDefinition(\"list2\");\n        builder.addActor(\"actor\").addUserTask(\"step1\", \"actor\").addUserTask(\"step2\", \"actor\");\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), \"actor\", user);\n        return getProcessAPI().startProcess(processDefinition.getId());\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/instance/AbortProcessInstanceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.process.instance;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.awaitility.Awaitility.await;\nimport static org.hamcrest.Matchers.hasSize;\nimport static org.junit.Assert.assertEquals;\n\nimport java.util.List;\nimport java.util.concurrent.Callable;\n\nimport org.bonitasoft.engine.bpm.NamedElement;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstance;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceCriterion;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.expression.ExpressionConstants;\nimport org.bonitasoft.engine.expression.InvalidExpressionException;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.junit.Ignore;\nimport org.junit.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class AbortProcessInstanceIT extends AbstractProcessInstanceIT {\n\n    private static final Logger logger = LoggerFactory.getLogger(AbortProcessInstanceIT.class);\n\n    private ProcessDefinition deployProcessWithMultiInstanceCallActivity(final int loopCardinality,\n            final String targetProcess, final String targetVersion)\n            throws Exception {\n        final Expression targetProcExpr = string(targetProcess);\n        final Expression targetVersionExpr = string(targetVersion);\n\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"RemainingInstancesAreAbortedAfterCompletionCondition\", \"1.0\");\n        builder.addStartEvent(\"start\");\n        builder.addCallActivity(\"callActivity\", targetProcExpr, targetVersionExpr)\n                .addMultiInstance(false, intExpr(loopCardinality))\n                .addCompletionCondition(\n                        new ExpressionBuilder()\n                                .createGroovyScriptExpression(\"deployProcessWithMultiInstanceCallActivity\",\n                                        ExpressionConstants.NUMBER_OF_COMPLETED_INSTANCES.getEngineConstantName()\n                                                + \" == 1 \",\n                                        Boolean.class.getName(),\n                                        new ExpressionBuilder().createEngineConstant(\n                                                ExpressionConstants.NUMBER_OF_COMPLETED_INSTANCES)));\n        builder.addEndEvent(\"end\");\n        builder.addTransition(\"start\", \"callActivity\");\n        builder.addTransition(\"callActivity\", \"end\");\n\n        return deployAndEnableProcess(builder.done());\n    }\n\n    @Test\n    public void abortProcessWithHumanTasks() throws Exception {\n        final String taskName1 = \"userTask1\";\n        final String taskName2 = \"userTask2\";\n        final String autoTaskName = \"auto1\";\n        final ProcessDefinition targetProcess = deployProcessWith2UserTasksAnd1AutoTask(taskName1, taskName2,\n                autoTaskName);\n        final int loopCardinality = 2;\n        final ProcessDefinition parentProcess = deployProcessWithMultiInstanceCallActivity(loopCardinality,\n                targetProcess.getName(), targetProcess.getVersion());\n        final ProcessInstance parentProcessInstance = getProcessAPI().startProcess(parentProcess.getId());\n        checkNbOfProcessInstances(loopCardinality + 1);\n\n        // execute task1 of a target process instance\n        final List<HumanTaskInstance> pendingHumanTaskInstances = checkNbPendingTaskOf(true, 2 * loopCardinality, user)\n                .getPendingHumanTaskInstances();\n        final HumanTaskInstance humanTaskInst1ToExecute = pendingHumanTaskInstances.get(0);\n        assertEquals(taskName1, humanTaskInst1ToExecute.getName());\n        assignAndExecuteStep(humanTaskInst1ToExecute, user);\n\n        final HumanTaskInstance humanTaskInst1ToAbort = pendingHumanTaskInstances.get(1);\n        assertEquals(taskName1, humanTaskInst1ToAbort.getName());\n        final long toBeAbortedProcInstId = humanTaskInst1ToAbort.getParentProcessInstanceId();\n        final ProcessInstance procInstToAbort = getProcessAPI().getProcessInstance(toBeAbortedProcInstId);\n\n        // execute task2 of same target process instance\n        HumanTaskInstance humanTaskInst2ToExecute = pendingHumanTaskInstances.get(2);\n        assertEquals(taskName2, humanTaskInst2ToExecute.getName());\n        HumanTaskInstance humanTaskInst2ToAbort = pendingHumanTaskInstances.get(3);\n        assertEquals(taskName2, humanTaskInst2ToAbort.getName());\n        if (humanTaskInst1ToExecute.getParentProcessInstanceId() != humanTaskInst2ToExecute\n                .getParentProcessInstanceId()) { // ensure tasks are in the same\n            humanTaskInst2ToAbort = humanTaskInst2ToExecute;\n            humanTaskInst2ToExecute = pendingHumanTaskInstances.get(3);\n        }\n        assignAndExecuteStep(humanTaskInst2ToExecute, user.getId());\n\n        // the target process instances that exceed the max loop must be in aborted state\n        waitForProcessToBeInState(procInstToAbort, ProcessInstanceState.ABORTED);\n\n        // task1 not executed must be in aborted state\n        waitForFlowNodeInState(parentProcessInstance, humanTaskInst1ToAbort.getName(), TestStates.ABORTED, true);\n        // task2 not executed must be in aborted state\n        waitForFlowNodeInState(parentProcessInstance, humanTaskInst2ToAbort.getName(), TestStates.ABORTED, true);\n\n        // check the automatic task in the aborted process instance was not created\n        checkWasntExecuted(procInstToAbort, autoTaskName);\n\n        // the parent process instance must finish in normal state\n        waitForProcessToFinish(parentProcessInstance);\n\n        disableAndDeleteProcess(parentProcess);\n        disableAndDeleteProcess(targetProcess);\n    }\n\n    @Test\n    public void should_abort_stable_and_non_stable_flow_nodes() throws Exception {\n        executeAndVerifyCompleted(() -> new ProcessDefinitionBuilder().createNewInstance(\"processWithTerminate\", \"3.0\")\n                .addAutomaticTask(\"step1\")\n                .addAutomaticTask(\"step2\")\n                .addAutomaticTask(\"step3\")\n                .addAutomaticTask(\"step4\")\n                .addEndEvent(\"terminateEnd\").addTerminateEventTrigger()\n                .addAutomaticTask(\"step5\")\n                .addAutomaticTask(\"step6\")\n                .addAutomaticTask(\"step7\")\n                .addAutomaticTask(\"step8\")\n                .addAutomaticTask(\"step9\")\n                .addAutomaticTask(\"step10\")\n                .getProcess());\n\n        shouldNotHaveFailedTasks();\n        shouldAllCompleteWithAbortedOrCompleted();\n        assertThat(getAllCompletedArchivedFlowNodeInstances().stream().map(NamedElement::getName))\n                .containsOnly(\"step1\", \"step2\", \"step3\", \"step4\", \"step5\", \"step6\", \"step7\", \"step8\", \"step9\",\n                        \"step10\");\n    }\n\n    @Test\n    public void should_abort_executing_loops() throws Exception {\n        executeAndVerifyCompleted(() -> new ProcessDefinitionBuilder().createNewInstance(\"processWithTerminate\", \"1.0\")\n                .addAutomaticTask(\"step1\").addLoop(false, bool(true))\n                .addAutomaticTask(\"step2\").addLoop(false, bool(true))\n                .addAutomaticTask(\"step3\").addLoop(false, bool(true))\n                .addAutomaticTask(\"step4\").addLoop(false, bool(true))\n                .addAutomaticTask(\"step5\").addLoop(false, bool(true))\n                .addAutomaticTask(\"step6\").addLoop(false, bool(true))\n                .addAutomaticTask(\"step7\").addLoop(false, bool(true))\n                .addAutomaticTask(\"step8\").addLoop(false, bool(true))\n                .addAutomaticTask(\"step9\").addLoop(false, bool(true))\n                .addAutomaticTask(\"step10\").addLoop(false, bool(true))\n                .addAutomaticTask(\"step_before_abort\").addEndEvent(\"terminateEnd\").addTerminateEventTrigger()\n                .addTransition(\"step_before_abort\", \"terminateEnd\")\n                .getProcess());\n\n        shouldNotHaveFailedTasks();\n        shouldAllCompleteWithAbortedOrCompleted();\n        assertThat(getAllCompletedArchivedFlowNodeInstances().stream().map(NamedElement::getName))\n                .contains(\"step1\", \"step2\", \"step3\", \"step4\", \"step5\", \"step6\", \"step7\", \"step8\", \"step9\", \"step10\",\n                        \"step_before_abort\");\n    }\n\n    private void shouldAllCompleteWithAbortedOrCompleted() throws SearchException {\n        assertThat(getAllCompletedArchivedFlowNodeInstances())\n                .allSatisfy(a -> assertThat(a.getState()).isIn(\"aborted\", \"completed\"));\n    }\n\n    @Test\n    public void should_abort_executing_multiInstances() throws Exception {\n        executeAndVerifyCompleted(() -> {\n            ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"processWithTerminate\",\n                    \"1.0\");\n            builder.addAutomaticTask(\"step1\").addMultiInstance(false, intExpr(5));\n            builder.addAutomaticTask(\"step2\").addMultiInstance(false, intExpr(5));\n            builder.addAutomaticTask(\"step3\").addMultiInstance(false, intExpr(5));\n            builder.addAutomaticTask(\"step4\").addMultiInstance(false, intExpr(5));\n            builder.addAutomaticTask(\"step5\").addMultiInstance(false, intExpr(5));\n            builder.addAutomaticTask(\"step6\").addMultiInstance(false, intExpr(5));\n            builder.addAutomaticTask(\"step7\").addMultiInstance(false, intExpr(5));\n            builder.addAutomaticTask(\"step8\").addMultiInstance(false, intExpr(5));\n            builder.addAutomaticTask(\"step9\").addMultiInstance(false, intExpr(5));\n            builder.addAutomaticTask(\"step10\").addMultiInstance(false, intExpr(5));\n            return builder\n                    .addAutomaticTask(\"step_before_abort\").addEndEvent(\"terminateEnd\").addTerminateEventTrigger()\n                    .addTransition(\"step_before_abort\", \"terminateEnd\")\n                    .getProcess();\n        });\n\n        shouldNotHaveFailedTasks();\n        shouldAllCompleteWithAbortedOrCompleted();\n        assertThat(getAllCompletedArchivedFlowNodeInstances().stream().map(NamedElement::getName))\n                .contains(\"step1\", \"step2\", \"step3\", \"step4\", \"step5\", \"step6\", \"step7\", \"step8\", \"step9\", \"step10\",\n                        \"step_before_abort\");\n    }\n\n    @Test\n    @Ignore(\"not working need the retry mechanism, the fix BR454 is needed  \")\n    public void should_abort_call_activities_from_calledProcess_with_event_subprocess() throws Exception {\n        ProcessDefinition calledProcess = getProcessAPI()\n                .deployAndEnableProcess(new ProcessDefinitionBuilder().createNewInstance(\"calledProcess\", \"4.0\")\n                        .addAutomaticTask(\"sub1\")\n                        .addAutomaticTask(\"sub2\")\n                        .addAutomaticTask(\"sub3\")\n                        .addAutomaticTask(\"sub4\").getProcess());\n        ProcessDefinition endErrorProcess = getProcessAPI()\n                .deployAndEnableProcess(new ProcessDefinitionBuilder().createNewInstance(\"endErrorProcess\", \"4.0\")\n                        .addAutomaticTask(\"sub1\")\n                        .addEndEvent(\"terminate\").addErrorEventTrigger(\"theError\")\n                        .addTransition(\"sub1\", \"terminate\")\n                        .getProcess());\n        ProcessDefinition processDefinition = executeAndVerifyAborted(() -> {\n\n            ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"processWithTerminate\",\n                    \"4.0\");\n            builder.addCallActivity(\"step1\", string(\"calledProcess\"), string(\"4.0\"));\n            builder.addCallActivity(\"step2\", string(\"calledProcess\"), string(\"4.0\")).addMultiInstance(false,\n                    intExpr(5));\n            builder.addCallActivity(\"step3\", string(\"calledProcess\"), string(\"4.0\"));//.addLoop(false, bool(true));\n            builder.addCallActivity(\"step4\", string(\"calledProcess\"), string(\"4.0\"));\n            builder.addCallActivity(\"step5\", string(\"calledProcess\"), string(\"4.0\"));\n            builder.addCallActivity(\"step6\", string(\"calledProcess\"), string(\"4.0\"));\n            builder.addCallActivity(\"step7\", string(\"calledProcess\"), string(\"4.0\"));\n            builder.addCallActivity(\"step10\", string(\"endErrorProcess\"), string(\"4.0\"));\n            builder.addCallActivity(\"step8\", string(\"calledProcess\"), string(\"4.0\"));\n            builder.addCallActivity(\"step9\", string(\"calledProcess\"), string(\"4.0\"));\n            builder.addSubProcess(\"errorEventSub\", true).getSubProcessBuilder()\n                    .addStartEvent(\"startError\").addErrorEventTrigger(\"theError\")\n                    .addAutomaticTask(\"stepToHandleError\")\n                    .addTransition(\"startError\", \"stepToHandleError\");\n            return builder\n                    .getProcess();\n        });\n\n        shouldNotHaveFailedTasks();\n        assertThat(getAllCompletedArchivedFlowNodeInstances().stream().map(NamedElement::getName))\n                .contains(\"stepToHandleError\");\n        shouldAllCompleteWithAbortedOrCompleted();\n        disableAndDeleteProcess(calledProcess);\n        disableAndDeleteProcess(endErrorProcess);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    @Ignore(\"not working need the retry mechanism, the fix BR454 is needed  \")\n    public void should_abort_elements_from_calledProcess_with_event_subprocess() throws Exception {\n\n        ProcessDefinition endErrorProcess = getProcessAPI()\n                .deployAndEnableProcess(new ProcessDefinitionBuilder().createNewInstance(\"endErrorProcess\", \"2.0\")\n                        .addAutomaticTask(\"sub1\")\n                        .addEndEvent(\"terminate\").addErrorEventTrigger(\"theError\")\n                        .addTransition(\"sub1\", \"terminate\")\n                        .getProcess());\n        ProcessDefinition processDefinition = executeAndVerifyAborted(() -> {\n\n            ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                    .createNewInstance(\"processWithCalledProcess\", \"2.0\");\n            builder.addAutomaticTask(\"step1\");\n            builder.addAutomaticTask(\"step2\").addMultiInstance(false, intExpr(5));\n            builder.addAutomaticTask(\"step3\").addLoop(false, bool(true));\n            builder.addAutomaticTask(\"step4\");\n            builder.addAutomaticTask(\"step5\");\n            builder.addCallActivity(\"step10\", string(\"endErrorProcess\"), string(\"2.0\"));\n            builder.addAutomaticTask(\"step6\");\n            builder.addAutomaticTask(\"step7\");\n            builder.addAutomaticTask(\"step8\");\n            builder.addAutomaticTask(\"step9\");\n            builder.addSubProcess(\"errorEventSub\", true).getSubProcessBuilder()\n                    .addStartEvent(\"startError\").addErrorEventTrigger(\"theError\")\n                    .addAutomaticTask(\"stepToHandleError\")\n                    .addTransition(\"startError\", \"stepToHandleError\");\n            return builder\n                    .getProcess();\n        });\n\n        shouldNotHaveFailedTasks();\n        assertThat(getAllCompletedArchivedFlowNodeInstances().stream().map(NamedElement::getName))\n                .contains(\"stepToHandleError\");\n        shouldAllCompleteWithAbortedOrCompleted();\n\n        disableAndDeleteProcess(endErrorProcess);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    @Ignore(\"not working need the retry mechanism, the fix BR454 is needed  \")\n    public void should_abort_call_activities_from_parent_processes() throws Exception {\n        ProcessDefinition calledProcess = getProcessAPI()\n                .deployAndEnableProcess(new ProcessDefinitionBuilder().createNewInstance(\"calledProcess\", \"1.0\")\n                        .addAutomaticTask(\"sub1\")\n                        .addAutomaticTask(\"sub2\")\n                        .addAutomaticTask(\"sub3\")\n                        .addAutomaticTask(\"sub4\").getProcess());\n        executeAndVerifyCompleted(() -> {\n\n            ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"processWithTerminate\",\n                    \"1.0\");\n            builder.addCallActivity(\"step1\", string(\"calledProcess\"), string(\"1.0\"));\n            builder.addCallActivity(\"step2\", string(\"calledProcess\"), string(\"1.0\")).addMultiInstance(false,\n                    intExpr(5));\n            builder.addCallActivity(\"step3\", string(\"calledProcess\"), string(\"1.0\")).addLoop(false, bool(true));\n            builder.addCallActivity(\"step4\", string(\"calledProcess\"), string(\"1.0\"));\n            builder.addCallActivity(\"step5\", string(\"calledProcess\"), string(\"1.0\"));\n            builder.addCallActivity(\"step6\", string(\"calledProcess\"), string(\"1.0\"));\n            builder.addCallActivity(\"step7\", string(\"calledProcess\"), string(\"1.0\"));\n            builder.addCallActivity(\"step8\", string(\"calledProcess\"), string(\"1.0\"));\n            builder.addCallActivity(\"step9\", string(\"calledProcess\"), string(\"1.0\"));\n            builder.addCallActivity(\"step10\", string(\"calledProcess\"), string(\"1.0\"));\n            return builder\n                    .addAutomaticTask(\"step_before_abort\").addEndEvent(\"terminateEnd\").addTerminateEventTrigger()\n                    .addTransition(\"step_before_abort\", \"terminateEnd\")\n                    .getProcess();\n        });\n\n        shouldNotHaveFailedTasks();\n        assertThat(getAllCompletedArchivedFlowNodeInstances().stream().map(NamedElement::getName))\n                .contains(\"step1\", \"step2\", \"step3\", \"step4\", \"step5\", \"step6\", \"step7\", \"step8\", \"step9\", \"step10\",\n                        \"step_before_abort\");\n        shouldAllCompleteWithAbortedOrCompleted();\n        disableAndDeleteProcess(calledProcess);\n    }\n\n    @Test\n    public void should_abort_or_cancel_all_flow_nodes_including_boundary_events_when_process_is_aborted_or_cancelled()\n            throws Exception {\n        // given:\n        ProcessDefinition processDefinition = deployAndEnableProcessWithActor(new ProcessDefinitionBuilder()\n                .createNewInstance(\"process to be aborted with boundary\", \"2.a\")\n                .addActor(\"actor\", true)\n                .addStartEvent(\"start\")\n                .addUserTask(\"toBeAborted\", \"actor\").addBoundaryEvent(\"boundary\").addSignalEventTrigger(\"theSignal\")\n                .addUserTask(\"terminateTask\", \"actor\")\n                .addEndEvent(\"end\").addTerminateEventTrigger()\n                .addTransition(\"start\", \"toBeAborted\")\n                .addTransition(\"start\", \"terminateTask\")\n                .addTransition(\"boundary\", \"end\")\n                .addTransition(\"terminateTask\", \"end\").getProcess(), \"actor\", user);\n\n        // when:\n        getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(\"toBeAborted\");\n        waitForUserTaskAndExecuteIt(\"terminateTask\", user);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance, \"toBeAborted\");\n        getProcessAPI().cancelProcessInstance(processInstance.getId());\n\n        // then:\n        await().until(\n                () -> getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.LAST_UPDATE_ASC),\n                hasSize(0));\n        assertThat(getProcessAPI().searchFlowNodeInstances(new SearchOptionsBuilder(0, 100).done()).getResult())\n                .hasSize(0);\n        await().until(\n                () -> getProcessAPI()\n                        .searchArchivedFlowNodeInstances(new SearchOptionsBuilder(0, 100)\n                                .filter(ArchivedFlowNodeInstanceSearchDescriptor.NAME, \"toBeAborted\")\n                                .filter(ArchivedFlowNodeInstanceSearchDescriptor.STATE_NAME, \"aborted\")\n                                .done())\n                        .getResult(),\n                hasSize(1));\n        await().until(\n                () -> getProcessAPI()\n                        .searchArchivedFlowNodeInstances(new SearchOptionsBuilder(0, 100)\n                                .filter(ArchivedFlowNodeInstanceSearchDescriptor.NAME, \"toBeAborted\")\n                                .filter(ArchivedFlowNodeInstanceSearchDescriptor.STATE_NAME, \"cancelled\")\n                                .done())\n                        .getResult(),\n                hasSize(1));\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    private void executeAndVerifyCompleted(Callable<DesignProcessDefinition> callable) throws Exception {\n        ProcessDefinition processDefinition = getProcessAPI().deployAndEnableProcess(callable.call());\n        ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        try {\n            waitForProcessToFinish(processInstance);\n        } catch (Exception e) {\n            logger.error(\"error while waiting for process to finish\");\n            ArchivedProcessInstance finalArchivedProcessInstance = getProcessAPI()\n                    .getFinalArchivedProcessInstance(processInstance.getId());\n            logger.error(\"final archive: state={}, endDate={}\", finalArchivedProcessInstance.getState(),\n                    finalArchivedProcessInstance.getEndDate());\n            getProcessAPI().searchProcessInstances(new SearchOptionsBuilder(0, 100).done()).getResult()\n                    .forEach(a -> logger.error(\"process found at the end: {}\", a));\n            getProcessAPI().searchFlowNodeInstances(new SearchOptionsBuilder(0, 100).done()).getResult()\n                    .forEach(a -> logger.error(\"flow node found at the end: {}\", a));\n            throw e;\n        }\n    }\n\n    private ProcessDefinition executeAndVerifyAborted(Callable<DesignProcessDefinition> callable) throws Exception {\n        ProcessDefinition processDefinition = getProcessAPI().deployAndEnableProcess(callable.call());\n        ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        try {\n            waitForProcessToBeInState(processInstance, ProcessInstanceState.ABORTED);\n        } catch (Exception e) {\n            logger.error(\"error while waiting for process to be aborted\");\n            ArchivedProcessInstance finalArchivedProcessInstance = getProcessAPI()\n                    .getFinalArchivedProcessInstance(processInstance.getId());\n            logger.error(\"final archive: state={}, endDate={}\", finalArchivedProcessInstance.getState(),\n                    finalArchivedProcessInstance.getEndDate());\n            getProcessAPI().searchProcessInstances(new SearchOptionsBuilder(0, 100).done()).getResult()\n                    .forEach(a -> logger.error(\"process found at the end: {}\", a));\n            getProcessAPI().searchFlowNodeInstances(new SearchOptionsBuilder(0, 100).done()).getResult()\n                    .forEach(a -> logger.error(\"flow node found at the end: {}\", a));\n            throw e;\n        }\n        return processDefinition;\n    }\n\n    private Expression bool(boolean b) throws InvalidExpressionException {\n        return new ExpressionBuilder().createConstantBooleanExpression(b);\n    }\n\n    private Expression intExpr(int i) throws InvalidExpressionException {\n        return new ExpressionBuilder().createConstantIntegerExpression(i);\n    }\n\n    private Expression string(String calledProcess) throws InvalidExpressionException {\n        return new ExpressionBuilder().createConstantStringExpression(calledProcess);\n    }\n\n    private void shouldNotHaveFailedTasks() throws SearchException {\n        assertThat(getProcessAPI().searchArchivedFlowNodeInstances(new SearchOptionsBuilder(0, 1000).done()).getResult()\n                .stream().map(ArchivedFlowNodeInstance::getState))\n                .doesNotContain(\"failed\");\n    }\n\n    private List<ArchivedFlowNodeInstance> getAllCompletedArchivedFlowNodeInstances() throws SearchException {\n        return getProcessAPI()\n                .searchArchivedFlowNodeInstances(new SearchOptionsBuilder(0, 100)\n                        .filter(ArchivedFlowNodeInstanceSearchDescriptor.TERMINAL, true).done())\n                .getResult();\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/instance/AbstractProcessInstanceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.process.instance;\n\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.flownode.GatewayType;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\n\npublic abstract class AbstractProcessInstanceIT extends TestWithUser {\n\n    protected ProcessDefinition deployProcessWith2UserTasksAnd1AutoTask(final String taskName1, final String taskName2,\n            final String autoTaskName)\n            throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process\", \"1.0\");\n        processDefinitionBuilder.addStartEvent(\"start\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addUserTask(taskName1, ACTOR_NAME);\n        processDefinitionBuilder.addUserTask(taskName2, ACTOR_NAME);\n        processDefinitionBuilder.addAutomaticTask(autoTaskName);\n        processDefinitionBuilder.addEndEvent(\"end1\");\n        processDefinitionBuilder.addEndEvent(\"end2\");\n        processDefinitionBuilder.addTransition(\"start\", taskName1);\n        processDefinitionBuilder.addTransition(taskName1, autoTaskName);\n        processDefinitionBuilder.addTransition(autoTaskName, \"end1\");\n        processDefinitionBuilder.addTransition(\"start\", taskName2);\n        processDefinitionBuilder.addTransition(taskName2, \"end2\");\n\n        return deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, user);\n    }\n\n    protected ProcessDefinition deployProcessWith2AutomaticTasks(final String taskName1, final String taskName2)\n            throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process\", \"1.0\");\n        processDefinitionBuilder.addStartEvent(\"start\");\n        processDefinitionBuilder.addAutomaticTask(taskName1);\n        processDefinitionBuilder.addAutomaticTask(taskName2);\n        processDefinitionBuilder.addEndEvent(\"end1\");\n        processDefinitionBuilder.addEndEvent(\"end2\");\n        processDefinitionBuilder.addTransition(\"start\", taskName1);\n        processDefinitionBuilder.addTransition(taskName1, \"end1\");\n        processDefinitionBuilder.addTransition(\"start\", taskName2);\n        processDefinitionBuilder.addTransition(taskName2, \"end2\");\n\n        return deployAndEnableProcess(processDefinitionBuilder.done());\n    }\n\n    protected ProcessDefinition deployProcessWithCallActivity(final String taskName1, final String callActivityName,\n            final String targetProcess,\n            final String targetVersion, final String taskName2) throws Exception {\n        final Expression targetProcessExpr = new ExpressionBuilder().createConstantStringExpression(targetProcess);\n        final Expression targetVersionExpr = new ExpressionBuilder().createConstantStringExpression(targetVersion);\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"Process with call activity\", \"1.0\");\n        processDefinitionBuilder.addStartEvent(\"start\");\n        processDefinitionBuilder.addAutomaticTask(taskName1);\n        processDefinitionBuilder.addCallActivity(callActivityName, targetProcessExpr, targetVersionExpr);\n        processDefinitionBuilder.addAutomaticTask(taskName2);\n        processDefinitionBuilder.addEndEvent(\"end\");\n        processDefinitionBuilder.addTransition(\"start\", taskName1);\n        processDefinitionBuilder.addTransition(taskName1, callActivityName);\n        processDefinitionBuilder.addTransition(callActivityName, taskName2);\n        processDefinitionBuilder.addTransition(taskName2, \"end\");\n\n        return deployAndEnableProcess(processDefinitionBuilder.done());\n    }\n\n    protected ProcessDefinition deployProcessWithIntermediateThrowMessageEvent(final String eventName,\n            final String messageName,\n            final String targetProcessName, final String targetFlowNodeName) throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process\", \"1.0\");\n        processDefinitionBuilder.addStartEvent(\"start\");\n        processDefinitionBuilder.addAutomaticTask(\"auto1\");\n        // create expression for target process/flowNode\n        final Expression targetProcessExpression = new ExpressionBuilder()\n                .createConstantStringExpression(targetProcessName);\n        final Expression targetFlowNodeExpression = new ExpressionBuilder()\n                .createConstantStringExpression(targetFlowNodeName);\n        processDefinitionBuilder.addIntermediateThrowEvent(eventName).addMessageEventTrigger(messageName,\n                targetProcessExpression, targetFlowNodeExpression);\n        processDefinitionBuilder.addAutomaticTask(\"auto2\");\n        processDefinitionBuilder.addEndEvent(\"end\");\n        processDefinitionBuilder.addTransition(\"start\", \"auto1\");\n        processDefinitionBuilder.addTransition(\"auto1\", eventName);\n        processDefinitionBuilder.addTransition(eventName, \"auto2\");\n        processDefinitionBuilder.addTransition(\"auto2\", \"end\");\n\n        return deployAndEnableProcess(processDefinitionBuilder.done());\n    }\n\n    protected ProcessDefinition deployProcessWithIntermediateThrowSignalEvent(final String eventName,\n            final String signalName) throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process\", \"1.0\");\n        processDefinitionBuilder.addStartEvent(\"start\");\n        processDefinitionBuilder.addAutomaticTask(\"auto1\");\n        processDefinitionBuilder.addIntermediateThrowEvent(eventName).addSignalEventTrigger(signalName);\n        processDefinitionBuilder.addAutomaticTask(\"auto2\");\n        processDefinitionBuilder.addEndEvent(\"end\");\n        processDefinitionBuilder.addTransition(\"start\", \"auto1\");\n        processDefinitionBuilder.addTransition(\"auto1\", eventName);\n        processDefinitionBuilder.addTransition(eventName, \"auto2\");\n        processDefinitionBuilder.addTransition(\"auto2\", \"end\");\n\n        return deployAndEnableProcess(processDefinitionBuilder.done());\n    }\n\n    protected ProcessDefinition deployProcessWithIntermediateCatchMessageEvent(final String eventName,\n            final String messageName, final String previousStep,\n            final String nextStep) throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process\", \"1.0\");\n        processDefinitionBuilder.addStartEvent(\"start\");\n        processDefinitionBuilder.addAutomaticTask(previousStep);\n        processDefinitionBuilder.addIntermediateCatchEvent(eventName).addMessageEventTrigger(messageName);\n        processDefinitionBuilder.addAutomaticTask(nextStep);\n        processDefinitionBuilder.addEndEvent(\"end\");\n        processDefinitionBuilder.addTransition(\"start\", previousStep);\n        processDefinitionBuilder.addTransition(previousStep, eventName);\n        processDefinitionBuilder.addTransition(eventName, nextStep);\n        processDefinitionBuilder.addTransition(nextStep, \"end\");\n\n        return deployAndEnableProcess(processDefinitionBuilder.done());\n    }\n\n    protected ProcessDefinition deployProcessWithIntermediateCatchSignalEvent(final String eventName,\n            final String signalName, final String previousStep,\n            final String nextStep) throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process\", \"1.0\");\n        processDefinitionBuilder.addStartEvent(\"start\");\n        processDefinitionBuilder.addAutomaticTask(\"auto1\");\n        processDefinitionBuilder.addIntermediateCatchEvent(eventName).addSignalEventTrigger(signalName);\n        processDefinitionBuilder.addAutomaticTask(nextStep);\n        processDefinitionBuilder.addEndEvent(\"end\");\n        processDefinitionBuilder.addTransition(\"start\", previousStep);\n        processDefinitionBuilder.addTransition(previousStep, eventName);\n        processDefinitionBuilder.addTransition(eventName, nextStep);\n        processDefinitionBuilder.addTransition(nextStep, \"end\");\n\n        return deployAndEnableProcess(processDefinitionBuilder.done());\n    }\n\n    protected ProcessDefinition deployProcessWithParallelGateways() throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process_with_parallel_gateway\", \"1.0\");\n        processDefinitionBuilder.addAutomaticTask(\"step1\");\n        processDefinitionBuilder.addAutomaticTask(\"step2\");\n        processDefinitionBuilder.addAutomaticTask(\"step3\");\n        processDefinitionBuilder.addAutomaticTask(\"step4\");\n        processDefinitionBuilder.addGateway(\"gateway1\", GatewayType.PARALLEL);\n        processDefinitionBuilder.addGateway(\"gateway2\", GatewayType.PARALLEL);\n        processDefinitionBuilder.addTransition(\"step1\", \"gateway1\");\n        processDefinitionBuilder.addTransition(\"gateway1\", \"step2\");\n        processDefinitionBuilder.addTransition(\"gateway1\", \"step3\");\n        processDefinitionBuilder.addTransition(\"step2\", \"gateway2\");\n        processDefinitionBuilder.addTransition(\"step3\", \"gateway2\");\n        processDefinitionBuilder.addTransition(\"gateway2\", \"step4\");\n\n        return deployAndEnableProcess(processDefinitionBuilder.done());\n    }\n\n    protected ProcessDefinition deployProcessWithExclusiveSplitGateway() throws Exception {\n        final Expression trueExpression = new ExpressionBuilder().createConstantBooleanExpression(true);\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process_with_parallel_gateway\", \"1.0\");\n        processDefinitionBuilder.addAutomaticTask(\"step1\");\n        processDefinitionBuilder.addAutomaticTask(\"step2\");\n        processDefinitionBuilder.addAutomaticTask(\"step3\");\n        processDefinitionBuilder.addAutomaticTask(\"step4\");\n        processDefinitionBuilder.addGateway(\"gateway1\", GatewayType.EXCLUSIVE);\n        processDefinitionBuilder.addTransition(\"step1\", \"gateway1\");\n        processDefinitionBuilder.addTransition(\"gateway1\", \"step2\", trueExpression);\n        processDefinitionBuilder.addTransition(\"gateway1\", \"step3\", trueExpression);\n        processDefinitionBuilder.addDefaultTransition(\"gateway1\", \"step4\");\n\n        return deployAndEnableProcess(processDefinitionBuilder.done());\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/instance/CancelProcessInstanceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.process.instance;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.fail;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertTrue;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstance;\nimport org.bonitasoft.engine.bpm.flownode.WaitingEvent;\nimport org.bonitasoft.engine.bpm.flownode.WaitingEventSearchDescriptor;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceCriterion;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.bonitasoft.engine.test.check.CheckNbOfActivities;\nimport org.junit.Test;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class CancelProcessInstanceIT extends AbstractProcessInstanceIT {\n\n    private static final String SEARCH_WAITING_EVENTS_COMMAND = \"searchWaitingEventsCommand\";\n\n    private static final String SEARCH_OPTIONS_KEY = \"searchOptions\";\n\n    @Test\n    public void cancelProcessInstanceWithHumanTasks() throws Exception {\n        final String taskName1 = \"userTask1\";\n        final String taskName2 = \"userTask2\";\n        final ProcessDefinition processDefinition = deployProcessWith2UserTasksAnd1AutoTask(taskName1, taskName2,\n                \"auto1\");\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(processInstance, taskName1);\n        final long step2Id = waitForUserTask(processInstance, taskName2);\n\n        getProcessAPI().cancelProcessInstance(processInstance.getId());\n        waitForProcessToBeInState(processInstance, ProcessInstanceState.CANCELLED);\n\n        final ArchivedActivityInstance archivedTask1 = getProcessAPI().getArchivedActivityInstance(step1Id);\n        assertEquals(TestStates.CANCELLED.getStateName(), archivedTask1.getState());\n\n        final ArchivedActivityInstance archivedTask2 = getProcessAPI().getArchivedActivityInstance(step2Id);\n        assertEquals(TestStates.CANCELLED.getStateName(), archivedTask2.getState());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void cancelCallActivity() throws Exception {\n        final String taskName1 = \"userTask1\";\n        final String taskName2 = \"userTask2\";\n        final String autoTaskName = \"auto1\";\n        final ProcessDefinition targetProcessDef = deployProcessWith2UserTasksAnd1AutoTask(taskName1, taskName2,\n                autoTaskName);\n        final ProcessDefinition callActivityProcDef = deployProcessWithCallActivity(taskName1, \"callActivity\",\n                targetProcessDef.getName(),\n                targetProcessDef.getVersion(), taskName2);\n        final ProcessInstance parentProcessInstance = getProcessAPI().startProcess(callActivityProcDef.getId());\n\n        final FlowNodeInstance waitForFlowNode = waitForFlowNodeInExecutingState(parentProcessInstance, \"callActivity\",\n                false);\n        assertNotNull(\"Expected call activity in executing state\", waitForFlowNode);\n\n        checkNbOfProcessInstances(2, ProcessInstanceCriterion.NAME_DESC);\n        final List<ProcessInstance> processInstances = getProcessAPI().getProcessInstances(0, 10,\n                ProcessInstanceCriterion.NAME_ASC);\n        assertEquals(2, processInstances.size());\n        final ProcessInstance targetProcessInstance = processInstances.get(0);\n        assertEquals(targetProcessDef.getId(), targetProcessInstance.getProcessDefinitionId());\n\n        final CheckNbOfActivities checkNbOfActivities = new CheckNbOfActivities(getProcessAPI(), 50, 5000, true,\n                parentProcessInstance, 2,\n                TestStates.READY);\n        assertTrue(checkNbOfActivities.waitUntil());\n\n        getProcessAPI().cancelProcessInstance(parentProcessInstance.getId());\n\n        waitForProcessToBeInState(parentProcessInstance, ProcessInstanceState.CANCELLED);\n        waitForProcessToBeInState(targetProcessInstance, ProcessInstanceState.CANCELLED);\n\n        checkWasntExecuted(targetProcessInstance, autoTaskName);\n        checkWasntExecuted(parentProcessInstance, taskName2);\n\n        disableAndDeleteProcess(callActivityProcDef);\n        disableAndDeleteProcess(targetProcessDef);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Test\n    public void cancelProcessInstanceWithIntermediateCatchMessageEvent() throws Exception {\n        final String catchMessageEvent = \"receiveMessage\";\n        final String previousStep = \"auto1\";\n        final String nextStep = \"auto2\";\n        final ProcessDefinition receiveProcess = deployProcessWithIntermediateCatchMessageEvent(catchMessageEvent, \"m1\",\n                previousStep, nextStep);\n\n        final ProcessInstance receiveProcessInstance = getProcessAPI().startProcess(receiveProcess.getId());\n        waitForFlowNodeInState(receiveProcessInstance, catchMessageEvent, TestStates.WAITING, true);\n\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.filter(WaitingEventSearchDescriptor.FLOW_NODE_NAME, catchMessageEvent);\n\n        final Map<String, Serializable> parameters = new HashMap<String, Serializable>(1);\n        parameters.put(SEARCH_OPTIONS_KEY, searchOptionsBuilder.done());\n\n        SearchResult<WaitingEvent> searchResult = (SearchResult<WaitingEvent>) getCommandAPI()\n                .execute(SEARCH_WAITING_EVENTS_COMMAND, parameters);\n        assertEquals(1, searchResult.getCount());\n\n        getProcessAPI().cancelProcessInstance(receiveProcessInstance.getId());\n        waitForProcessToBeInState(receiveProcessInstance, ProcessInstanceState.CANCELLED);\n\n        searchResult = (SearchResult<WaitingEvent>) getCommandAPI().execute(SEARCH_WAITING_EVENTS_COMMAND, parameters);\n        assertEquals(0, searchResult.getCount());\n\n        checkWasntExecuted(receiveProcessInstance, nextStep);\n\n        disableAndDeleteProcess(receiveProcess);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Test\n    public void cancelProcessInstanceWithIntermediateCatchSignalEvent() throws Exception {\n        final String catchMessageEvent = \"receiveSignal\";\n        final String previousStep = \"auto1\";\n        final String nextStep = \"auto2\";\n        final ProcessDefinition receiveProcess = deployProcessWithIntermediateCatchSignalEvent(catchMessageEvent, \"s1\",\n                previousStep, nextStep);\n\n        final ProcessInstance receiveProcessInstance = getProcessAPI().startProcess(receiveProcess.getId());\n        waitForFlowNodeInState(receiveProcessInstance, catchMessageEvent, TestStates.WAITING, true);\n\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.filter(WaitingEventSearchDescriptor.FLOW_NODE_NAME, catchMessageEvent);\n\n        final Map<String, Serializable> parameters = new HashMap<String, Serializable>(1);\n        parameters.put(SEARCH_OPTIONS_KEY, searchOptionsBuilder.done());\n\n        SearchResult<WaitingEvent> searchResult = (SearchResult<WaitingEvent>) getCommandAPI()\n                .execute(SEARCH_WAITING_EVENTS_COMMAND, parameters);\n        assertEquals(1, searchResult.getCount());\n\n        getProcessAPI().cancelProcessInstance(receiveProcessInstance.getId());\n        waitForProcessToBeInState(receiveProcessInstance, ProcessInstanceState.CANCELLED);\n\n        searchResult = (SearchResult<WaitingEvent>) getCommandAPI().execute(SEARCH_WAITING_EVENTS_COMMAND, parameters);\n        assertEquals(0, searchResult.getCount());\n\n        checkWasntExecuted(receiveProcessInstance, nextStep);\n\n        disableAndDeleteProcess(receiveProcess);\n    }\n\n    @Test(expected = ProcessInstanceNotFoundException.class)\n    public void cancelUnknownProcessInstance() throws Exception {\n        getProcessAPI().cancelProcessInstance(45);\n    }\n\n    @Test\n    public void should_cancel_process_instance_with_running_tasks() throws Exception {\n        //given\n        ProcessDefinition processDefinition = getProcessAPI()\n                .deployAndEnableProcess(\n                        new ProcessDefinitionBuilder().createNewInstance(\"processWithATaskThatCallCancel\", \"1.0\")\n                                .addStartEvent(\"start\")\n                                .addCallActivity(\"call\",\n                                        new ExpressionBuilder().createConstantStringExpression(\"unknownProcess\"),\n                                        new ExpressionBuilder().createConstantStringExpression(\"1.0\"))\n                                .addTransition(\"start\", \"call\")\n                                .getProcess());\n        //when\n        ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        ActivityInstance activityInstance = waitForTaskToFail(processInstance);\n        assertThat(activityInstance.getName()).isEqualTo(\"call\");\n        getProcessAPI().cancelProcessInstance(processInstance.getId());\n        waitForProcessToBeInState(processInstance, ProcessInstanceState.CANCELLED);\n\n        //then\n        try {\n            getProcessAPI().getProcessInstance(processInstance.getId());\n            fail(\"process should not exist anymore\");\n        } catch (ProcessInstanceNotFoundException ignored) {\n        }\n        //verify the call activity was archived in cancelled state:\n        SearchResult<ArchivedFlowNodeInstance> archivedFlowNodeInstances = getProcessAPI()\n                .searchArchivedFlowNodeInstances(new SearchOptionsBuilder(0, 10)\n                        .filter(ArchivedFlowNodeInstanceSearchDescriptor.NAME, \"call\")\n                        .filter(ArchivedFlowNodeInstanceSearchDescriptor.TERMINAL, true).done());\n        assertThat(archivedFlowNodeInstances.getResult()).hasSize(1);\n        assertThat(archivedFlowNodeInstances.getResult().get(0).getState()).isEqualTo(\"cancelled\");\n        disableAndDeleteProcess(processDefinition);\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/instance/InvolvedInProcessInstanceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.process.instance;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.awaitility.Awaitility.await;\n\nimport java.util.Collections;\n\nimport org.bonitasoft.engine.bpm.actor.ActorCriterion;\nimport org.bonitasoft.engine.bpm.actor.ActorInstance;\nimport org.bonitasoft.engine.bpm.actor.ActorMember;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.identity.Group;\nimport org.bonitasoft.engine.identity.Role;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.identity.UserCreator;\nimport org.bonitasoft.engine.test.BuildTestUtil;\nimport org.junit.Test;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class InvolvedInProcessInstanceIT extends AbstractProcessInstanceIT {\n\n    @Test\n    public void isInvolvedInProcessInstanceAndTheirManager() throws Exception {\n        //given\n        //user and manager\n        final User managerOfJim = createUser(new UserCreator(\"managerOfJim\", \"bpm\"));\n        // Jim starts the process instance:\n        final User jim = createUser(new UserCreator(\"jim\", \"bpm\").setManagerUserId(managerOfJim.getId()));\n\n        final User managerOfJohn = createUser(new UserCreator(\"managerOfJohn\", \"bpm\"));\n        // john is mapped as direct user:\n        final User john = createUser(new UserCreator(\"john\", \"bpm\").setManagerUserId(managerOfJohn.getId()));\n\n        final User managerOfJack = createUser(new UserCreator(\"managerOfJack\", \"bpm\"));\n        // Jack is mapped through his group:\n        final User jack = createUser(new UserCreator(\"jack\", \"bpm\").setManagerUserId(managerOfJack.getId()));\n        final Group jackGroup = createGroup(\"jackGroup\", null);\n        final Role jackRole = createRole(\"jackRole\");\n        getIdentityAPI().addUserMembership(jack.getId(), jackGroup.getId(), jackRole.getId());\n\n        final User managerOfJames = createUser(new UserCreator(\"managerOfJames\", \"bpm\"));\n        // James is mapped through his role:\n        final User james = createUser(new UserCreator(\"james\", \"bpm\").setManagerUserId(managerOfJames.getId()));\n        final Group jamesGroup = createGroup(\"jamesGroup\", null);\n        final Role jamesRole = createRole(\"jamesRole\");\n        getIdentityAPI().addUserMembership(james.getId(), jamesGroup.getId(), jamesRole.getId());\n\n        final User managerOfToto = createUser(new UserCreator(\"managerOfToto\", \"bpm\"));\n        // Toto is mapped through his group + role (=membership):\n        final User toto = createUser(new UserCreator(\"toto\", \"bpm\").setManagerUserId(managerOfToto.getId()));\n        final Group totoGroup = createGroup(\"totoGroup\", null);\n        final Role totoRole = createRole(\"totoRole\");\n        getIdentityAPI().addUserMembership(toto.getId(), totoGroup.getId(), totoRole.getId());\n\n        final ProcessDefinitionBuilder processBuilder1 = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder1.addActor(ACTOR_NAME);\n\n        // 1 instance of process def:\n        final DesignProcessDefinition designProcessDefinition = processBuilder1.addUserTask(\"step1\", ACTOR_NAME)\n                .addUserTask(\"step2\", ACTOR_NAME)\n                .addTransition(\"step1\", \"step2\").getProcess();\n        final BusinessArchive businessArchive1 = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(designProcessDefinition).done();\n        final ProcessDefinition processDefinition = deployProcess(businessArchive1);\n\n        // map user, group, role, and membership to that actor\n        final ActorInstance actor = getProcessAPI().getActors(processDefinition.getId(), 0, 1, ActorCriterion.NAME_ASC)\n                .get(0);\n        final ActorMember jimActorMember = getProcessAPI().addUserToActor(actor.getId(), jim.getId());\n        final ActorMember johnActorMember = getProcessAPI().addUserToActor(actor.getId(), john.getId());\n        final ActorMember jackActorMember = getProcessAPI().addGroupToActor(actor.getId(), jackGroup.getId());\n        final ActorMember jamesActorMember = getProcessAPI().addRoleToActor(actor.getId(), jamesRole.getId());\n        getProcessAPI().addRoleAndGroupToActor(actor.getId(), totoRole.getId(), totoGroup.getId());\n\n        logoutThenloginAs(jim.getUserName(), \"bpm\");\n        getProcessAPI().enableProcess(processDefinition.getId());\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long processInstanceId = processInstance.getId();\n        getProcessAPI().removeActorMember(jimActorMember.getId());\n\n        // then\n        await().alias(\"directly mapped user should be involved\")\n                .until(() -> getProcessAPI().isInvolvedInProcessInstance(john.getId(), processInstanceId));\n\n        assertThat(getProcessAPI().isInvolvedInProcessInstance(managerOfJohn.getId(), processInstanceId)).as(\n                \"manager of directly mapped user should not be involved\").isFalse();\n        assertThat(getProcessAPI().isManagerOfUserInvolvedInProcessInstance(john.getId(), processInstanceId))\n                .as(\"directly mapped user should be involved\")\n                .isFalse();\n        assertThat(getProcessAPI().isManagerOfUserInvolvedInProcessInstance(managerOfJohn.getId(), processInstanceId))\n                .as(\n                        \"manager of directly mapped user should be involved\")\n                .isTrue();\n\n        assertThat(getProcessAPI().isInvolvedInProcessInstance(jack.getId(), processInstanceId))\n                .as(\"user mapped using group should be involved\")\n                .isTrue();\n        assertThat(getProcessAPI().isInvolvedInProcessInstance(managerOfJack.getId(), processInstanceId)).as(\n                \"manager of user mapped using group should not be involved\").isFalse();\n        assertThat(getProcessAPI().isManagerOfUserInvolvedInProcessInstance(jack.getId(), processInstanceId))\n                .as(\"user mapped using group should be involved\")\n                .isFalse();\n        assertThat(getProcessAPI().isManagerOfUserInvolvedInProcessInstance(managerOfJack.getId(), processInstanceId))\n                .as(\n                        \"manager of user mapped using group should be involved\")\n                .isTrue();\n\n        assertThat(getProcessAPI().isInvolvedInProcessInstance(james.getId(), processInstanceId))\n                .as(\"user mapped using role should be involved\")\n                .isTrue();\n        assertThat(getProcessAPI().isInvolvedInProcessInstance(managerOfJames.getId(), processInstanceId)).as(\n                \"manager of user mapped using role should not be involved\").isFalse();\n        assertThat(getProcessAPI().isManagerOfUserInvolvedInProcessInstance(james.getId(), processInstanceId))\n                .as(\"user mapped using role should be involved\")\n                .isFalse();\n        assertThat(getProcessAPI().isManagerOfUserInvolvedInProcessInstance(managerOfJames.getId(), processInstanceId))\n                .as(\n                        \"manager of user mapped using role should be involved\")\n                .isTrue();\n\n        assertThat(getProcessAPI().isInvolvedInProcessInstance(toto.getId(), processInstanceId))\n                .as(\"user mapped using membership should be involved\")\n                .isTrue();\n        assertThat(getProcessAPI().isInvolvedInProcessInstance(managerOfToto.getId(), processInstanceId)).as(\n                \"manager of user mapped using membership should not be involved\").isFalse();\n        assertThat(getProcessAPI().isManagerOfUserInvolvedInProcessInstance(toto.getId(), processInstanceId)).as(\n                \"user mapped using membership should be involved\").isFalse();\n        assertThat(getProcessAPI().isManagerOfUserInvolvedInProcessInstance(managerOfToto.getId(), processInstanceId))\n                .as(\n                        \"manager of user mapped using membership should be involved\")\n                .isTrue();\n\n        assertThat(getProcessAPI().isInvolvedInProcessInstance(user.getId(), processInstanceId))\n                .as(\"not mapped user should not be involved\").isFalse();\n        assertThat(getProcessAPI().isManagerOfUserInvolvedInProcessInstance(user.getId(), processInstanceId))\n                .as(\"not mapped user should not be involved\").isFalse();\n\n        assertThat(getProcessAPI().isInvolvedInProcessInstance(jim.getId(), processInstanceId))\n                .as(\"the process instance initiator should be involved\")\n                .isTrue();\n        assertThat(getProcessAPI().isManagerOfUserInvolvedInProcessInstance(jim.getId(), processInstanceId)).as(\n                \"the process instance initiator should not be involved (as a manager)\").isFalse();\n        assertThat(getProcessAPI().isInvolvedInProcessInstance(managerOfJim.getId(), processInstanceId)).as(\n                \"the manager of the process instance initiator should not be involved\").isFalse();\n        assertThat(getProcessAPI().isManagerOfUserInvolvedInProcessInstance(managerOfJim.getId(), processInstanceId))\n                .as(\n                        \"the manager of process instance initiator should be involved (as a manager)\")\n                .isTrue();\n\n        getProcessAPI().removeActorMember(johnActorMember.getId());\n        logoutThenloginAs(john.getUserName(), \"bpm\");\n        final HumanTaskInstance step1Instance = waitForUserTaskAndAssignIt(processInstance, \"step1\", john);\n\n        assertThat(getProcessAPI().isInvolvedInProcessInstance(john.getId(), processInstanceId))\n                .as(\"assigned user should be involved\").isTrue();\n\n        assignAndExecuteStep(step1Instance, john);\n        waitForActivityInCompletedState(processInstance, \"step1\", false);\n        assertThat(getProcessAPI().isManagerOfUserInvolvedInProcessInstance(managerOfJohn.getId(), processInstanceId))\n                .as(\n                        \"the manager of the task executor should be involved\")\n                .isTrue();\n\n        getProcessAPI().removeActorMember(jamesActorMember.getId());\n        logoutThenloginAs(james.getUserName(), \"bpm\");\n        final HumanTaskInstance step2Instance = waitForUserTaskAndAssignIt(processInstance, \"step2\", james);\n        assertThat(getProcessAPI().isManagerOfUserInvolvedInProcessInstance(managerOfJames.getId(), processInstanceId))\n                .as(\"the manager of a user assigned to a task should be involved\").isTrue();\n\n        getProcessAPI().removeActorMember(jackActorMember.getId());\n        logoutThenloginAs(jack.getUserName(), \"bpm\");\n        assignAndExecuteStep(step2Instance, jack);\n        waitForProcessToFinish(processInstanceId);\n        assertThat(getProcessAPI().isInvolvedInProcessInstance(jack.getId(), processInstanceId)).as(\n                \"a user executor of a task in an archived process instance should be involved\").isTrue();\n        assertThat(getProcessAPI().isInvolvedInProcessInstance(managerOfJack.getId(), processInstanceId)).as(\n                \"a manager of a user executor of a task in an archived process instance should not be involved\")\n                .isFalse();\n        assertThat(getProcessAPI().isManagerOfUserInvolvedInProcessInstance(jack.getId(), processInstanceId)).as(\n                \"a user executor of a task in an archived process instance should not be involved (as a manager)\")\n                .isFalse();\n        assertThat(getProcessAPI().isManagerOfUserInvolvedInProcessInstance(managerOfJack.getId(), processInstanceId))\n                .as(\n                        \"a manager of a user executor of a task in an archived process instance should be involved (as a manager)\")\n                .isTrue();\n\n        assertThat(getProcessAPI().isInvolvedInProcessInstance(jim.getId(), processInstanceId)).as(\n                \"a user initiator of an archived process instance should be involved\").isTrue();\n        assertThat(getProcessAPI().isInvolvedInProcessInstance(managerOfJim.getId(), processInstanceId)).as(\n                \"a manager of a user initiator of an archived process instance should not be involved\").isFalse();\n        assertThat(getProcessAPI().isManagerOfUserInvolvedInProcessInstance(jim.getId(), processInstanceId)).as(\n                \"a user initiator of an archived process instance should not be involved (as a manager)\").isFalse();\n        assertThat(getProcessAPI().isManagerOfUserInvolvedInProcessInstance(managerOfJim.getId(), processInstanceId))\n                .as(\n                        \"a manager of a user initiator of an archived process instance should be involved (as a manager)\")\n                .isTrue();\n\n        //clean\n        deleteUsers(john, jack, james, toto, managerOfJohn, managerOfJames, managerOfJack, managerOfToto, jim,\n                managerOfJim);\n        deleteGroups(jackGroup, jamesGroup, totoGroup);\n        deleteRoles(jackRole, jamesRole, totoRole);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void isInvolvedInUserTask() throws Exception {\n        //given\n        // john is mapped as direct user:\n        final User john = createUser(new UserCreator(\"john\", \"bpm\"));\n\n        final ProcessDefinitionBuilder processBuilder1 = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder1.addActor(ACTOR_NAME);\n\n        // 1 instance of process def:\n        final DesignProcessDefinition designProcessDefinition = processBuilder1.addUserTask(\"step1\", ACTOR_NAME)\n                .addUserTask(\"step2\", ACTOR_NAME)\n                .addTransition(\"step1\", \"step2\").getProcess();\n        final BusinessArchive businessArchive1 = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(designProcessDefinition).done();\n        final ProcessDefinition processDefinition = deployProcess(businessArchive1);\n\n        // map user, group, role, and membership to that actor\n        final ActorInstance actor = getProcessAPI().getActors(processDefinition.getId(), 0, 1, ActorCriterion.NAME_ASC)\n                .get(0);\n        getProcessAPI().addUserToActor(actor.getId(), john.getId());\n        getProcessAPI().enableProcess(processDefinition.getId());\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long processInstanceId = processInstance.getId();\n        long step1 = waitForUserTask(processInstanceId, \"step1\");\n\n        // then\n        assertThat(getProcessAPI().isInvolvedInHumanTaskInstance(john.getId(), step1))\n                .as(\"directly mapped user should be involved\").isTrue();\n\n        //clean\n        deleteUsers(john);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test(expected = ProcessInstanceNotFoundException.class)\n    public void isInvolvedInProcessInstanceWithProcessInstanceNotFoundException() throws Exception {\n        getProcessAPI().isInvolvedInProcessInstance(user.getId(), 0);\n    }\n\n    @Test\n    public void isInvolvedInProcessInstanceWithInvalidUser() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithActorAndThreeHumanStepsAndThreeTransition();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance, \"step2\");\n        try {\n            assertThat(getProcessAPI().isInvolvedInProcessInstance(999999999999L, processInstance.getId())).isFalse();\n        } finally {\n            disableAndDeleteProcess(processDefinition);\n        }\n    }\n\n    @Test\n    public void should_assignee_of_call_activity_task_be_involved_in_root_process_instance() throws Exception {\n        //given\n        final User callActivityAssignee = createUser(new UserCreator(\"john\", PASSWORD));\n\n        final ProcessDefinitionBuilder subProcessDefinitionBuilder = getProcessCallActivityDefinitionBuilder();\n        final ProcessDefinition subProcessDefinition = deployAndEnableProcessWithActor(\n                subProcessDefinitionBuilder.done(), ACTOR_NAME, user);\n        final ProcessDefinition rootProcessDefinition = deployAndEnableProcessWithActor(\n                BuildTestUtil.buildProcessDefinitionWithCallActivity(\"processWithCallActivity\",\n                        new ExpressionBuilder().createConstantStringExpression(BuildTestUtil.PROCESS_NAME),\n                        new ExpressionBuilder().createConstantStringExpression(BuildTestUtil.PROCESS_VERSION)).done(),\n                ACTOR_NAME, user);\n\n        //when\n        final ProcessInstance rootProcessInstance = getProcessAPI().startProcess(rootProcessDefinition.getId());\n        final long rootProcessInstanceId = rootProcessInstance.getId();\n        final HumanTaskInstance callActivityHumanTask = waitForUserTaskAndAssignIt(\"callActivityHumanTask\",\n                callActivityAssignee);\n\n        //then\n        assertThat(getProcessAPI().isInvolvedInProcessInstance(callActivityAssignee.getId(), rootProcessInstanceId))\n                .as(\"should user assigned to call activity id:\" + callActivityHumanTask.getId()\n                        + \" be involved in process with Id \"\n                        + rootProcessInstanceId)\n                .isTrue();\n\n        //clean up\n        loginWithTechnicalUser();\n        disableAndDeleteProcess(rootProcessDefinition, subProcessDefinition);\n        deleteUsers(callActivityAssignee);\n\n    }\n\n    @Test\n    public void should_executor_of_call_activity_task_be_involved_in_root_process_instance() throws Exception {\n        //given\n        final User callActivityAssignee = createUser(new UserCreator(\"john\", PASSWORD));\n        final User callActivityExecutor = createUser(new UserCreator(\"jack\", PASSWORD));\n        final ProcessDefinitionBuilder subProcessDefinitionBuilder;\n\n        subProcessDefinitionBuilder = getProcessCallActivityDefinitionBuilder();\n\n        final ProcessDefinition subProcessDefinition = deployAndEnableProcessWithActor(\n                subProcessDefinitionBuilder.done(), ACTOR_NAME, user);\n        final ProcessDefinition rootProcessDefinition = deployAndEnableProcessWithActor(\n                BuildTestUtil.buildProcessDefinitionWithCallActivity(\"processWithCallActivity\",\n                        new ExpressionBuilder().createConstantStringExpression(BuildTestUtil.PROCESS_NAME),\n                        new ExpressionBuilder().createConstantStringExpression(BuildTestUtil.PROCESS_VERSION)).done(),\n                ACTOR_NAME, user);\n\n        //when\n        final ProcessInstance rootProcessInstance = getProcessAPI().startProcess(rootProcessDefinition.getId());\n        final long rootProcessInstanceId = rootProcessInstance.getId();\n        final HumanTaskInstance callActivityHumanTask = waitForUserTaskAndAssignIt(\"callActivityHumanTask\",\n                callActivityAssignee);\n\n        logoutThenloginAs(callActivityExecutor.getUserName(), PASSWORD);\n        getProcessAPI().executeUserTask(callActivityExecutor.getId(), callActivityHumanTask.getId(),\n                Collections.EMPTY_MAP);\n        waitForProcessToFinish(rootProcessInstanceId);\n\n        //then\n        assertThat(getProcessAPI().isInvolvedInProcessInstance(callActivityAssignee.getId(), rootProcessInstanceId))\n                .as(\"should assigned user that didn't perform call activity id:\" + callActivityHumanTask.getId()\n                        + \" not be involved in process with Id \"\n                        + rootProcessInstanceId)\n                .isFalse();\n\n        assertThat(getProcessAPI().isInvolvedInProcessInstance(callActivityExecutor.getId(), rootProcessInstanceId))\n                .as(\"should user that perform call activity id:\" + callActivityHumanTask.getId()\n                        + \" be involved in process with Id \"\n                        + rootProcessInstanceId)\n                .isTrue();\n        //clean up\n        loginWithTechnicalUser();\n        disableAndDeleteProcess(rootProcessDefinition, subProcessDefinition);\n        deleteUsers(callActivityAssignee, callActivityExecutor);\n\n    }\n\n    private ProcessDefinitionBuilder getProcessCallActivityDefinitionBuilder() {\n        ProcessDefinitionBuilder subProcessDefinitionBuilder;\n        subProcessDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance(BuildTestUtil.PROCESS_NAME,\n                BuildTestUtil.PROCESS_VERSION);\n        subProcessDefinitionBuilder.addStartEvent(\"start\");\n        subProcessDefinitionBuilder.addActor(ACTOR_NAME, true)\n                .addManualTask(\"callActivityHumanTask\", ACTOR_NAME);\n        subProcessDefinitionBuilder.addEndEvent(\"end\");\n        subProcessDefinitionBuilder.addTransition(\"start\", \"callActivityHumanTask\")\n                .addTransition(\"callActivityHumanTask\", \"end\");\n        return subProcessDefinitionBuilder;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/instance/ProcessInstanceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.process.instance;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertThrows;\nimport static org.junit.Assert.assertTrue;\n\nimport java.io.Serializable;\nimport java.math.BigDecimal;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.assertj.core.util.Lists;\nimport org.bonitasoft.engine.api.ApiAccessType;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.comment.Comment;\nimport org.bonitasoft.engine.bpm.comment.SearchCommentsDescriptor;\nimport org.bonitasoft.engine.bpm.data.DataInstance;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstance;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceCriterion;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.identity.UserSearchDescriptor;\nimport org.bonitasoft.engine.operation.Operation;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.test.APITestUtil;\nimport org.bonitasoft.engine.test.BuildTestUtil;\nimport org.bonitasoft.engine.util.APITypeManager;\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author Celine Souchet\n */\npublic class ProcessInstanceIT extends AbstractProcessInstanceIT {\n\n    @Test\n    public void checkProcessInstanceDescriptionNotNull() throws Exception {\n        checkProcessInstanceDescription(\"My description\");\n    }\n\n    @Test\n    public void checkProcessInstanceDescriptionNull() throws Exception {\n        checkProcessInstanceDescription(null);\n    }\n\n    private void checkProcessInstanceDescription(final String description) throws Exception {\n        // Create process definition with description;\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION)\n                .addDescription(description).getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcess(designProcessDefinition);\n\n        // Start ProcessInstance\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        assertEquals(description, processInstance.getDescription());\n\n        waitForProcessToFinish(processInstance);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void checkArchivedProcessInstanceDescriptionNotNull() throws Exception {\n        checkArchivedProcessInstanceDescription(\"My description\");\n    }\n\n    @Test\n    public void checkArchivedProcessInstanceDescriptionNull() throws Exception {\n        checkArchivedProcessInstanceDescription(null);\n    }\n\n    private void checkArchivedProcessInstanceDescription(final String description) throws Exception {\n        // Create process definition with description;\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION)\n                .addDescription(description).getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcess(designProcessDefinition);\n\n        // Start ProcessInstance\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        assertEquals(description, processInstance.getDescription());\n\n        checkProcessInstanceIsArchived(processInstance);\n        final List<ArchivedProcessInstance> processInstances = getProcessAPI().getArchivedProcessInstances(0, 1,\n                ProcessInstanceCriterion.CREATION_DATE_DESC);\n        assertEquals(1, processInstances.size());\n\n        // We check that the retrieved processes are the good ones:\n        final ArchivedProcessInstance archivedProcessInstance = processInstances.get(0);\n        assertEquals(processInstance.getId(), archivedProcessInstance.getSourceObjectId());\n        assertEquals(description, archivedProcessInstance.getDescription());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void deleteProcessInstance() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask(\"step1\", ACTOR_NAME)\n                .getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n\n        final ProcessInstance process1 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance process2 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance process3 = getProcessAPI().startProcess(processDefinition.getId());\n        getProcessAPI().startProcess(processDefinition.getId());\n        getProcessAPI().startProcess(processDefinition.getId());\n        assertEquals(5, getProcessAPI().getNumberOfProcessInstances());\n\n        getProcessAPI().deleteProcessInstance(process1.getId());\n        getProcessAPI().deleteProcessInstance(process3.getId());\n        assertEquals(3, getProcessAPI().getNumberOfProcessInstances());\n\n        getProcessAPI().getProcessInstance(process2.getId());\n        try {\n            getProcessAPI().getProcessInstance(process1.getId());\n            Assert.fail(\"this instance should be deleted\");\n        } catch (final ProcessInstanceNotFoundException e) {\n            // ok\n        }\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void deleteUnknownProcessInstance() throws Exception {\n        Throwable exception = assertThrows(DeletionException.class,\n                () -> getProcessAPI().deleteProcessInstance(123456789123L));\n        //Exception causes are not serialized if API type is HTTP\n        if (ApiAccessType.LOCAL.equals(APITypeManager.getAPIType())) {\n            assertTrue(exception.getCause() instanceof ProcessInstanceNotFoundException);\n        }\n    }\n\n    @Test\n    public void deleteProcessInstances() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask(\"step1\", ACTOR_NAME)\n                .getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n\n        final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance processInstance3 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance processInstance4 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance processInstance5 = getProcessAPI().startProcess(processDefinition.getId());\n        assertEquals(5, getProcessAPI().getNumberOfProcessInstances());\n\n        waitForUserTask(processInstance1, \"step1\");\n        waitForUserTask(processInstance2, \"step1\");\n        waitForUserTask(processInstance3, \"step1\");\n        waitForUserTask(processInstance4, \"step1\");\n        waitForUserTask(processInstance5, \"step1\");\n        getProcessAPI().deleteProcessInstances(processDefinition.getId(), 0, 4);\n        assertEquals(1, getProcessAPI().getNumberOfProcessInstances());\n\n        // Clean up\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void should_archivedProcessInstance_startDate_in_same_order() throws Exception {\n        // given\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(false, false));\n        final ProcessDefinition processDefinition = deployAndEnableProcess(designProcessDefinition);\n\n        // when\n        final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId());\n        Thread.sleep(20);\n        final ProcessInstance pi1 = getProcessAPI().startProcess(processDefinition.getId());\n        Thread.sleep(20);\n        final ProcessInstance pi2 = getProcessAPI().startProcess(processDefinition.getId());\n\n        waitForProcessToFinish(pi0.getId());\n        waitForProcessToFinish(pi1.getId());\n        waitForProcessToFinish(pi2.getId());\n\n        // then\n        final List<ArchivedProcessInstance> processInstances = getProcessAPI().getArchivedProcessInstances(0, 10,\n                ProcessInstanceCriterion.CREATION_DATE_ASC);\n        assertThat(processInstances).as(\"should find 3 archived processes\").hasSize(3);\n\n        final ArchivedProcessInstance returnedPI0 = processInstances.get(0);\n        final ArchivedProcessInstance returnedPI1 = processInstances.get(1);\n        final ArchivedProcessInstance returnedPI2 = processInstances.get(2);\n\n        assertThat(pi0.getId()).as(\"archived process should have the same id\")\n                .isEqualTo(returnedPI0.getSourceObjectId());\n        assertThat(pi1.getId()).as(\"archived process should have the same id\")\n                .isEqualTo(returnedPI1.getSourceObjectId());\n        assertThat(pi2.getId()).as(\"archived process should have the same id\")\n                .isEqualTo(returnedPI2.getSourceObjectId());\n\n        assertThat(returnedPI0.getStartDate()).as(\"process 0 should start before or at same time than process 1\")\n                .isBeforeOrEqualsTo(returnedPI1.getStartDate());\n        assertThat(returnedPI1.getStartDate()).as(\"process 1 should start before or at same time than process 2\")\n                .isBeforeOrEqualsTo(returnedPI2.getStartDate());\n\n        disableAndDeleteProcess(processDefinition);\n        assertThat(getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.DEFAULT))\n                .as(\"should delete all instances after process deletion\").hasSize(0);\n    }\n\n    @Test\n    public void getArchivedProcessInstanceOrderByLastUpdate() throws Exception {\n        getArchivedProcessInstances(ProcessInstanceCriterion.LAST_UPDATE_ASC, 0, 1, 2,\n                ProcessInstanceCriterion.LAST_UPDATE_DESC, 2, 1, 0);\n    }\n\n    private void getArchivedProcessInstances(final ProcessInstanceCriterion asc, final int asc1, final int asc2,\n            final int asc3,\n            final ProcessInstanceCriterion desc, final int desc1, final int desc2, final int desc3) throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(false, false));\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(designProcessDefinition).done();\n        final ProcessDefinition processDefinition = getProcessAPI().deploy(businessArchive);\n        getProcessAPI().enableProcess(processDefinition.getId());\n\n        final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId());\n        checkProcessInstanceIsArchived(pi0);\n\n        final ProcessInstance pi1 = getProcessAPI().startProcess(processDefinition.getId());\n        checkProcessInstanceIsArchived(pi1);\n\n        final ProcessInstance pi2 = getProcessAPI().startProcess(processDefinition.getId());\n        checkProcessInstanceIsArchived(pi2);\n\n        // We asked for creation date descending order:\n        List<ArchivedProcessInstance> processInstances = getProcessAPI().getArchivedProcessInstances(0, 10, asc);\n        assertEquals(3, processInstances.size());\n        assertEquals(\"completed\", processInstances.get(asc1).getState());\n        assertEquals(pi0.getId(), processInstances.get(asc1).getSourceObjectId());\n        assertEquals(\"completed\", processInstances.get(asc2).getState());\n        assertEquals(pi1.getId(), processInstances.get(asc2).getSourceObjectId());\n        assertEquals(\"completed\", processInstances.get(asc3).getState());\n        assertEquals(pi2.getId(), processInstances.get(asc3).getSourceObjectId());\n\n        processInstances = getProcessAPI().getArchivedProcessInstances(0, 10, desc);\n        assertEquals(3, processInstances.size());\n        assertEquals(pi0.getId(), processInstances.get(desc1).getSourceObjectId());\n        assertEquals(pi1.getId(), processInstances.get(desc2).getSourceObjectId());\n        assertEquals(pi2.getId(), processInstances.get(desc3).getSourceObjectId());\n\n        disableAndDeleteProcess(processDefinition);\n        assertEquals(0, getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.DEFAULT).size());\n    }\n\n    @Test\n    public void getNumberOfArchiveProcessInstance() throws Exception {\n        getNumberOfArchivedProcessInstances();\n    }\n\n    private void getNumberOfArchivedProcessInstances() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList(\"step1\"),\n                        Arrays.asList(false));\n        final ProcessDefinition processDefinition = deployAndEnableProcess(designProcessDefinition);\n        long numberOfProcessInstancesBefore;\n        numberOfProcessInstancesBefore = getProcessAPI().getNumberOfArchivedProcessInstances();\n        final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance processInstance3 = getProcessAPI().startProcess(processDefinition.getId());\n\n        checkProcessInstanceIsArchived(processInstance1);\n        checkProcessInstanceIsArchived(processInstance2);\n        checkProcessInstanceIsArchived(processInstance3);\n        assertEquals(numberOfProcessInstancesBefore + 3, getProcessAPI().getNumberOfArchivedProcessInstances());\n\n        waitForProcessToFinish(processInstance1);\n        waitForProcessToFinish(processInstance2);\n        waitForProcessToFinish(processInstance3);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getNoChildrenInstanceIdsFromProcessInstance() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n\n        final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId());\n        final List<Long> ids = getProcessAPI().getChildrenInstanceIdsOfProcessInstance(pi0.getId(), 0, 10,\n                ProcessInstanceCriterion.DEFAULT);\n        assertEquals(0, ids.size());\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getSingleChildInstanceOfProcessInstance() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"SubProcessInAInstance\", PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME).addAutomaticTask(\"step1\").addAutomaticTask(\"step2\")\n                .addUserTask(\"userSubTask\", ACTOR_NAME).addTransition(\"step1\", \"userSubTask\")\n                .addTransition(\"userSubTask\", \"step2\");\n\n        final ProcessDefinition subProcess = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user);\n\n        final Expression targetProcessNameExpr = new ExpressionBuilder()\n                .createConstantStringExpression(subProcess.getName());\n        final Expression targetProcessVersionExpr = new ExpressionBuilder()\n                .createConstantStringExpression(subProcess.getVersion());\n\n        final ProcessDefinitionBuilder builderProc = new ProcessDefinitionBuilder()\n                .createNewInstance(\"executeInstanceSequentialWithSubProcess\", \"1.1\");\n        builderProc.addActor(ACTOR_NAME).addStartEvent(\"start\")\n                .addCallActivity(\"callActivity\", targetProcessNameExpr, targetProcessVersionExpr);\n        builderProc.addAutomaticTask(\"step3\").addEndEvent(\"end\").addTransition(\"start\", \"callActivity\")\n                .addTransition(\"callActivity\", \"step3\")\n                .addUserTask(\"userTask\", ACTOR_NAME).addTransition(\"step3\", \"userTask\")\n                .addTransition(\"userTask\", \"end\");\n\n        final DesignProcessDefinition processDefinition = builderProc.done();\n        final ProcessDefinition mainProcess = deployAndEnableProcessWithActor(processDefinition, ACTOR_NAME, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(mainProcess.getId());\n        final HumanTaskInstance userTask = waitForUserTaskAndGetIt(\"userSubTask\");\n\n        final List<Long> ids = getProcessAPI().getChildrenInstanceIdsOfProcessInstance(processInstance.getId(), 0, 10,\n                ProcessInstanceCriterion.DEFAULT);\n        assertThat(ids).isNotEmpty().hasSize(1).containsExactly(userTask.getParentProcessInstanceId());\n        disableAndDeleteProcess(mainProcess, subProcess);\n    }\n\n    @Test\n    public void getChildrenInstanceIdsOfUnknownProcessInstance() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final List<Long> childrenInstanceIds = getProcessAPI().getChildrenInstanceIdsOfProcessInstance(\n                processInstance.getId() + 1, 0, 10,\n                ProcessInstanceCriterion.DEFAULT);\n        assertTrue(childrenInstanceIds.isEmpty());\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getProcessInstanceIdFromActivityInstanceId() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId());\n\n        final List<ActivityInstance> activityInstances = getProcessAPI().getActivities(pi0.getId(), 0, 10);\n        for (final ActivityInstance activityInstance : activityInstances) {\n            final long processInstanceId = getProcessAPI()\n                    .getProcessInstanceIdFromActivityInstanceId(activityInstance.getId());\n            assertEquals(pi0.getId(), processInstanceId);\n        }\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getNumberOfProcessInstances() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n\n        final long initialProcessInstanceNb = getProcessAPI().getNumberOfProcessInstances();\n\n        final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance processInstance3 = getProcessAPI().startProcess(processDefinition.getId());\n\n        final long finalProcessInstanceNb = getProcessAPI().getNumberOfProcessInstances();\n        assertEquals(initialProcessInstanceNb + 3, finalProcessInstanceNb);\n\n        // Clean up\n        waitForUserTask(processInstance1, \"step1\");\n        waitForUserTask(processInstance2, \"step1\");\n        waitForUserTask(processInstance3, \"step1\");\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getProcessInstances() throws Exception {\n        final List<ProcessDefinition> processDefinitions = createNbProcessDefinitionWithTwoHumanStepsAndDeployWithActor(\n                3, user);\n        final List<ProcessInstance> processInstances = startNbProcess(processDefinitions);\n        getProcessInstancesOrderByProcessInstanceCriterion(ProcessInstanceCriterion.CREATION_DATE_ASC,\n                processInstances);\n        getProcessInstancesOrderByProcessInstanceCriterion(ProcessInstanceCriterion.CREATION_DATE_DESC,\n                processInstances);\n        getProcessInstancesOrderByProcessInstanceCriterion(ProcessInstanceCriterion.LAST_UPDATE_DESC, processInstances);\n        getProcessInstancesOrderByProcessInstanceCriterion(ProcessInstanceCriterion.LAST_UPDATE_ASC, processInstances);\n        getProcessInstancesOrderByProcessInstanceCriterion(ProcessInstanceCriterion.NAME_ASC, processInstances);\n        getProcessInstancesOrderByProcessInstanceCriterion(ProcessInstanceCriterion.NAME_DESC, processInstances);\n        getProcessInstancesOrderByProcessInstanceCriterion(ProcessInstanceCriterion.DEFAULT, processInstances);\n        getProcessInstancesOrderByProcessInstanceCriterion(ProcessInstanceCriterion.STATE_DESC, processInstances);\n        getProcessInstancesOrderByProcessInstanceCriterion(ProcessInstanceCriterion.STATE_ASC, processInstances);\n        // Clean up\n        disableAndDeleteProcess(processDefinitions);\n\n        // We check that there are no resident process instances in DB:\n        assertEquals(0, getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.DEFAULT).size());\n    }\n\n    private void getProcessInstancesOrderByProcessInstanceCriterion(\n            final ProcessInstanceCriterion processInstanceCriterion,\n            final List<ProcessInstance> processInstances) {\n        // We asked for creation date descending order:\n        final List<ProcessInstance> resultProcessInstances = getProcessAPI().getProcessInstances(0, 10,\n                processInstanceCriterion);\n        assertEquals(3, resultProcessInstances.size());\n\n        final ProcessInstance returnedPI0 = resultProcessInstances.get(0);\n        final ProcessInstance returnedPI1 = resultProcessInstances.get(1);\n        final ProcessInstance returnedPI2 = resultProcessInstances.get(2);\n\n        final ProcessInstance pi0 = processInstances.get(0);\n        final ProcessInstance pi1 = processInstances.get(1);\n        final ProcessInstance pi2 = processInstances.get(2);\n\n        switch (processInstanceCriterion) {\n            case STATE_ASC:\n                // First state must be before second state :\n                assertTrue(returnedPI0.getState().compareToIgnoreCase(returnedPI1.getState()) <= 0);\n                // Second state must be before third state :\n                assertTrue(returnedPI1.getState().compareToIgnoreCase(returnedPI2.getState()) <= 0);\n                break;\n            case STATE_DESC:\n                // First state must be after second state :\n                assertTrue(returnedPI0.getState().compareToIgnoreCase(returnedPI1.getState()) >= 0);\n                // Second state must be after third state :\n                assertTrue(returnedPI1.getState().compareToIgnoreCase(returnedPI2.getState()) >= 0);\n                break;\n            case LAST_UPDATE_ASC:\n                // First last update must be before second last update :\n                assertTrue(returnedPI0.getLastUpdate().before(returnedPI1.getLastUpdate()));\n                // Second last update must be before third last update :\n                assertTrue(returnedPI1.getLastUpdate().before(returnedPI2.getLastUpdate()));\n                break;\n            case LAST_UPDATE_DESC:\n                // First last update must be after second last update :\n                assertTrue(returnedPI0.getLastUpdate().after(returnedPI1.getLastUpdate()));\n                // Second last update must be after third last update :\n                assertTrue(returnedPI1.getLastUpdate().after(returnedPI2.getLastUpdate()));\n                break;\n            case NAME_ASC:\n                // We check that the retrieved processes are the good ones:\n                assertEquals(pi0.getId(), returnedPI0.getId());\n                assertEquals(pi1.getId(), returnedPI1.getId());\n                assertEquals(pi2.getId(), returnedPI2.getId());\n\n                // First name must be before second name :\n                assertTrue(returnedPI0.getName().compareToIgnoreCase(returnedPI1.getName()) <= 0);\n                // Second name must be before third name :\n                assertTrue(returnedPI1.getName().compareToIgnoreCase(returnedPI2.getName()) <= 0);\n                break;\n            case NAME_DESC:\n                // We check that the retrieved processes are the good ones:\n                assertEquals(pi0.getId(), returnedPI2.getId());\n                assertEquals(pi1.getId(), returnedPI1.getId());\n                assertEquals(pi2.getId(), returnedPI0.getId());\n\n                // First name must be after second name :\n                assertTrue(returnedPI0.getName().compareToIgnoreCase(returnedPI1.getName()) >= 0);\n                // Second name must be after third name :\n                assertTrue(returnedPI1.getName().compareToIgnoreCase(returnedPI2.getName()) >= 0);\n                break;\n            case CREATION_DATE_ASC:\n                // We check that the retrieved processes are the good ones:\n                assertEquals(pi0.getId(), returnedPI0.getId());\n                assertEquals(pi1.getId(), returnedPI1.getId());\n                assertEquals(pi2.getId(), returnedPI2.getId());\n\n                // First creation date must be before second creation date:\n                assertTrue(returnedPI0.getStartDate().before(returnedPI1.getStartDate()));\n                // Second creation date must be before third creation date:\n                assertTrue(returnedPI1.getStartDate().before(returnedPI2.getStartDate()));\n                break;\n            case CREATION_DATE_DESC:\n            case DEFAULT:\n            default:\n                // We check that the retrieved processes are the good ones:\n                assertEquals(pi0.getId(), returnedPI2.getId());\n                assertEquals(pi1.getId(), returnedPI1.getId());\n                assertEquals(pi2.getId(), returnedPI0.getId());\n\n                // First creation date must be after second creation date:\n                assertTrue(returnedPI0.getStartDate().after(returnedPI1.getStartDate()));\n                // Second creation date must be after third creation date:\n                assertTrue(returnedPI1.getStartDate().after(returnedPI2.getStartDate()));\n                break;\n        }\n    }\n\n    @Test\n    public void setProcessInstanceState() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask(\"step1\", ACTOR_NAME)\n                .getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance, \"step1\");\n\n        final long processInstanceId = processInstance.getId();\n        processInstance = getProcessAPI().getProcessInstance(processInstanceId);\n        assertEquals(\"started\", processInstance.getState());\n\n        getProcessAPI().setProcessInstanceState(processInstance, \"initializing\");\n        processInstance = getProcessAPI().getProcessInstance(processInstanceId);\n        assertEquals(\"initializing\", processInstance.getState());\n\n        getProcessAPI().setProcessInstanceState(processInstance, \"started\");\n        processInstance = getProcessAPI().getProcessInstance(processInstanceId);\n        assertEquals(\"started\", processInstance.getState());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void startProcess2Times() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\n                        Arrays.asList(\"initTask1\"), Arrays.asList(true));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition,\n                APITestUtil.ACTOR_NAME, user);\n\n        // Start process instance first time, and complete it\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(processInstance, \"initTask1\", user);\n        waitForProcessToFinish(processInstance);\n\n        // Start process instance second time\n        final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(processInstance2, \"initTask1\", user);\n        waitForProcessToFinish(processInstance2);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void startProcessUsingInitialVariableValuesFor() throws Exception {\n        final String otherUserName = \"other\";\n        final User otherUser = createUser(otherUserName, \"user\");\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"cantResolveDataInExpressionInDataDefaultValue\", \"1\");\n        processBuilder.addActor(ACTOR_NAME).addDescription(\"Process to test archiving mechanism\");\n        processBuilder.addDoubleData(\"D\", new ExpressionBuilder().createConstantDoubleExpression(3.14));\n        processBuilder.addData(\"bigD\", BigDecimal.class.getName(), null);\n        processBuilder.addUserTask(\"step1\", ACTOR_NAME);\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME,\n                user);\n\n        try {\n            final Map<String, Serializable> variables = new HashMap<>();\n            variables.put(\"bigD\", new BigDecimal(\"3.141592653589793\"));\n            final ProcessInstance instance = getProcessAPI().startProcess(otherUser.getId(), processDefinition.getId(),\n                    variables);\n            final ProcessInstance processInstance2 = getProcessAPI().getProcessInstance(instance.getId());\n\n            DataInstance dataInstance = getProcessAPI().getProcessDataInstance(\"bigD\", instance.getId());\n            assertEquals(new BigDecimal(\"3.141592653589793\"), dataInstance.getValue());\n            dataInstance = getProcessAPI().getProcessDataInstance(\"D\", instance.getId());\n            assertEquals(Double.valueOf(3.14), dataInstance.getValue());\n\n            assertEquals(otherUser.getId(), processInstance2.getStartedBy());\n            assertEquals(user.getId(), processInstance2.getStartedBySubstitute());\n\n            // Check system comment\n            final SearchOptions searchOptions = new SearchOptionsBuilder(0, 100)\n                    .filter(SearchCommentsDescriptor.PROCESS_INSTANCE_ID, processInstance2.getId())\n                    .done();\n            final List<Comment> comments = getProcessAPI().searchComments(searchOptions).getResult();\n            boolean haveCommentForDelegate = false;\n            for (final Comment comment : comments) {\n                haveCommentForDelegate = haveCommentForDelegate\n                        || comment.getContent()\n                                .contains(\"The user \" + USERNAME + \" acting as delegate of the user \" + otherUserName\n                                        + \" has started the case.\");\n            }\n            assertTrue(haveCommentForDelegate);\n        } finally {\n            // Clean up\n            disableAndDeleteProcess(processDefinition);\n            deleteUsers(otherUser);\n        }\n    }\n\n    @Test\n    public void startProcessInstanceFor() throws Exception {\n        final String otherUserName = \"other\";\n        final User otherUser = createUser(otherUserName, \"user\");\n\n        // create process definition with integer data;\n        final String dataName = \"var1\";\n        final DesignProcessDefinition processDef = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addIntegerData(dataName, new ExpressionBuilder().createConstantIntegerExpression(1))\n                .addUserTask(\"step1\", ACTOR_NAME)\n                .addAutomaticTask(\"step2\").addTransition(\"step1\", \"step2\").getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, ACTOR_NAME, user);\n\n        try {\n            // create Operation keyed map\n            final Operation integerOperation = BuildTestUtil.buildIntegerOperation(dataName, 2);\n            final List<Operation> operations = new ArrayList<>();\n            final Map<String, Serializable> contexts = new HashMap<>();\n            contexts.put(\"page\", \"1\");\n            operations.add(integerOperation);\n            final long processDefinitionId = processDefinition.getId();\n            final ProcessInstance processInstance = getProcessAPI().startProcess(otherUser.getId(), processDefinitionId,\n                    operations, contexts);\n            final ProcessInstance processInstance2 = getProcessAPI().getProcessInstance(processInstance.getId());\n            assertEquals(otherUser.getId(), processInstance2.getStartedBy());\n            assertEquals(user.getId(), processInstance2.getStartedBySubstitute());\n\n            // Check system comment\n            final SearchOptions searchOptions = new SearchOptionsBuilder(0, 100)\n                    .filter(SearchCommentsDescriptor.PROCESS_INSTANCE_ID, processInstance.getId())\n                    .done();\n            final List<Comment> comments = getProcessAPI().searchComments(searchOptions).getResult();\n            boolean haveCommentForDelegate = false;\n            for (final Comment comment : comments) {\n                haveCommentForDelegate = haveCommentForDelegate\n                        || comment.getContent()\n                                .contains(\"The user \" + USERNAME + \" acting as delegate of the user \" + otherUserName\n                                        + \" has started the case.\");\n            }\n            assertTrue(haveCommentForDelegate);\n        } finally {\n            // Clean up\n            disableAndDeleteProcess(processDefinition);\n            deleteUser(otherUser);\n        }\n    }\n\n    private List<ProcessInstance> startNbProcess(final List<ProcessDefinition> processDefinitions) throws Exception {\n        final List<ProcessInstance> process = new ArrayList<>();\n        for (final ProcessDefinition processDefinition : processDefinitions) {\n            process.add(getProcessAPI().startProcess(processDefinition.getId()));\n            Thread.sleep(5);// avoid two instances with the same date\n        }\n        return process;\n    }\n\n    private List<ProcessDefinition> createNbProcessDefinitionWithTwoHumanStepsAndDeployWithActor(final int nbProcess,\n            final User user) throws BonitaException {\n        return createNbProcessDefinitionWithHumanAndAutomaticAndDeployWithActor(nbProcess, user,\n                Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true));\n    }\n\n    @Test\n    public void getNumberOfUsersCanExecutePendingHumanTaskDeploymentInfo() throws Exception {\n        final User otherUser = createUser(\"other\", \"user\");\n\n        // create process definition with integer data;\n        final String dataName = \"var1\";\n        final DesignProcessDefinition processDef = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addIntegerData(dataName, new ExpressionBuilder().createConstantIntegerExpression(1))\n                .addUserTask(\"step1\", ACTOR_NAME)\n                .addAutomaticTask(\"step2\").addTransition(\"step1\", \"step2\").getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef,\n                Lists.newArrayList(ACTOR_NAME, ACTOR_NAME),\n                Lists.newArrayList(user, otherUser));\n\n        // create Operation keyed map\n        final Operation integerOperation = BuildTestUtil.buildIntegerOperation(dataName, 2);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(otherUser.getId(),\n                processDefinition.getId(),\n                Collections.singletonList(integerOperation),\n                Collections.<String, Serializable> singletonMap(\"page\", \"1\"));\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.sort(UserSearchDescriptor.LAST_NAME, Order.DESC);\n        final SearchResult<User> results = getProcessAPI().searchUsersWhoCanExecutePendingHumanTask(step1Id,\n                builder.done());\n        assertThat(results.getCount()).isSameAs(2L);\n        assertThat(results.getResult()).isNotEmpty();\n        assertThat(results.getResult().get(0).getId()).isEqualTo(user.getId());\n        assertThat(results.getResult().get(1).getId()).isEqualTo(otherUser.getId());\n\n        // Clean up\n        disableAndDeleteProcess(processDefinition);\n        deleteUser(otherUser);\n    }\n\n    @Test\n    public void runProcessInstanceWithDefaultFlownode_should_pass_all_human_task() throws Exception {\n        final DesignProcessDefinition processDef = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addUserTask(\"step1\", ACTOR_NAME).addUserTask(\"step2\", ACTOR_NAME)\n                .addDefaultTransition(\"step1\", \"step2\").getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef,\n                Lists.newArrayList(ACTOR_NAME, ACTOR_NAME),\n                Lists.newArrayList(user));\n        final ProcessInstance pi = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(pi, \"step1\", user);\n        waitForUserTaskAndExecuteIt(pi, \"step2\", user);\n\n        waitForProcessToFinish(pi);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void runProcessInstanceWithDefaultFlownode_and_another_evaluated_to_false_transition_should_passto_step2()\n            throws Exception {\n        final DesignProcessDefinition processDef = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addUserTask(\"step1\", ACTOR_NAME).addUserTask(\"step2\", ACTOR_NAME).addUserTask(\"step3\", ACTOR_NAME)\n                .addTransition(\"step1\", \"step3\", new ExpressionBuilder().createConstantBooleanExpression(false))\n                .addDefaultTransition(\"step1\", \"step2\")\n                .getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef,\n                Lists.newArrayList(ACTOR_NAME, ACTOR_NAME),\n                Lists.newArrayList(user));\n        final ProcessInstance pi = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(pi, \"step1\", user);\n        waitForUserTaskAndExecuteIt(pi, \"step2\", user);\n\n        waitForProcessToFinish(pi);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void runProcessInstanceWithDefaultFlownode_and_another_evaluated_to_true_transition_should_passto_step3()\n            throws Exception {\n        final DesignProcessDefinition processDef = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addUserTask(\"step1\", ACTOR_NAME).addUserTask(\"step2\", ACTOR_NAME).addUserTask(\"step3\", ACTOR_NAME)\n                .addTransition(\"step1\", \"step3\", new ExpressionBuilder().createConstantBooleanExpression(true))\n                .addDefaultTransition(\"step1\", \"step2\")\n                .getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef,\n                Lists.newArrayList(ACTOR_NAME, ACTOR_NAME),\n                Lists.newArrayList(user));\n        final ProcessInstance pi = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(pi, \"step1\", user);\n        waitForUserTaskAndExecuteIt(pi, \"step3\", user);\n\n        waitForProcessToFinish(pi);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void runDeleteParentArchivedProcessInstanceAndElements_should_not_delete_process_instance_not_yet_archived()\n            throws Exception {\n        final DesignProcessDefinition processDef = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION).addActor(ACTOR_NAME)\n                .addUserTask(\"step1\", ACTOR_NAME).addUserTask(\"step2\", ACTOR_NAME).addTransition(\"step1\", \"step2\")\n                .getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef,\n                Lists.newArrayList(ACTOR_NAME, ACTOR_NAME),\n                Lists.newArrayList(user));\n        final ProcessInstance pi = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(pi, \"step1\", user);\n        final long nbDeleted = getProcessAPI().deleteArchivedProcessInstances(processDefinition.getId(), 0, 10);\n\n        // there is one archived process instance deleted because the former process_instance state has been archived\n        assertThat(nbDeleted).isEqualTo(1);\n        waitForUserTaskAndExecuteIt(pi, \"step2\", user);\n        waitForProcessToFinish(pi);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/profile/AbstractProfileIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile;\n\nimport org.bonitasoft.engine.TestWithTechnicalUser;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.identity.Group;\nimport org.bonitasoft.engine.identity.Role;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.junit.After;\nimport org.junit.Before;\n\n/**\n * @author Baptiste Mesta\n */\npublic abstract class AbstractProfileIT extends TestWithTechnicalUser {\n\n    protected Long adminProfileId;\n\n    protected Long userProfileId;\n\n    protected User user1;\n\n    protected User user2;\n\n    protected User user3;\n\n    protected User user4;\n\n    protected User user5;\n\n    protected Group group1;\n\n    protected Group group2;\n\n    protected Group group3;\n\n    protected Role role1;\n\n    protected Role role2;\n\n    protected Role role3;\n\n    @Override\n    @Before\n    public void before() throws Exception {\n        super.before();\n\n        user1 = createUser(\"userName1\", \"User1Pwd\", \"User1FirstName\", \"User1LastName\");\n        user2 = createUser(\"userName2\", \"User2Pwd\", \"User2FirstName\", \"User2LastName\");\n        user3 = createUser(\"userName3\", \"User3Pwd\", \"User3FirstName\", \"User3LastName\");\n        user4 = createUser(\"userName4\", \"User4Pwd\", \"User4FirstName\", \"User4LastName\");\n        user5 = createUser(\"userName5\", \"User5Pwd\", \"User5FirstName\", \"User5LastName\");\n\n        group1 = createGroup(\"group1\");\n        group2 = createGroup(\"group2\");\n        group3 = createGroup(\"group3\");\n        role1 = createRole(\"role1\");\n        role2 = createRole(\"role2\");\n        role3 = createRole(\"role3\");\n\n        // search for the newly created profile IDs:\n        adminProfileId = getProfileByName(\"Administrator\").getId();\n        userProfileId = getProfileByName(\"User\").getId();\n\n        getProfileAPI().createProfileMember(userProfileId, user1.getId(), null, null);\n        getProfileAPI().createProfileMember(userProfileId, null, group1.getId(), null);\n        getProfileAPI().createProfileMember(adminProfileId, user1.getId(), null, null);\n        getProfileAPI().createProfileMember(adminProfileId, null, group1.getId(), role2.getId());\n        getProfileAPI().createProfileMember(adminProfileId, null, group2.getId(), role2.getId());\n        getProfileAPI().createProfileMember(adminProfileId, null, null, role1.getId());\n        getProfileAPI().createProfileMember(adminProfileId, null, null, role2.getId());\n\n    }\n\n    private Profile getProfileByName(String name) throws SearchException {\n        return getProfileAPI().searchProfiles(new SearchOptionsBuilder(0, 1).filter(\"name\", name).done()).getResult()\n                .get(0);\n    }\n\n    @Override\n    @After\n    public void after() throws Exception {\n        deleteUsers(user1, user2, user3, user4, user5);\n        deleteGroups(group1, group2, group3);\n        deleteRoles(role1, role2, role3);\n        super.after();\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/profile/ProfileCommunityIT.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.junit.Test;\n\n/**\n * Specific tests for the community edition on the profile API\n */\npublic class ProfileCommunityIT extends AbstractProfileIT {\n\n    @Test\n    public void searchProfile() throws BonitaException {\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.sort(ProfileSearchDescriptor.NAME, Order.DESC);\n\n        final SearchResult<Profile> searchedProfiles = getProfileAPI().searchProfiles(builder.done());\n        assertThat(searchedProfiles.getCount()).isEqualTo(2);\n        assertThat(searchedProfiles.getResult().get(0).getName()).isEqualTo(\"User\");\n        assertThat(searchedProfiles.getResult().get(1).getName()).isEqualTo(\"Administrator\");\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/profile/ProfileIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.assertEquals;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.platform.LoginException;\nimport org.bonitasoft.engine.platform.LogoutException;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.junit.Test;\n\n/**\n * @author Julien Mege\n * @author Celine Souchet\n */\npublic class ProfileIT extends AbstractProfileIT {\n\n    @Test\n    public void should_have_profiles_in_session() throws Exception {\n        getApiClient().logout();\n        List<String> profilesOfUser1 = getProfilesFromSession(this.user1, \"User1Pwd\");\n        List<String> profilesOfUser2 = getProfilesFromSession(this.user2, \"User2Pwd\");\n        List<String> profilesOfUser3 = getProfilesFromSession(this.user3, \"User3Pwd\");\n        List<String> profilesOfUser4 = getProfilesFromSession(this.user4, \"User4Pwd\");\n        List<String> profilesOfUser5 = getProfilesFromSession(this.user5, \"User5Pwd\");\n        loginWithTechnicalUser();\n\n        //Theses profiles are the one mapped in the AbstractProfileIT's before\n        assertThat(profilesOfUser1).containsExactlyInAnyOrder(\"Administrator\", \"User\");\n        assertThat(profilesOfUser2).isEmpty();\n        assertThat(profilesOfUser3).isEmpty();\n        assertThat(profilesOfUser4).isEmpty();\n        assertThat(profilesOfUser5).isEmpty();\n    }\n\n    private List<String> getProfilesFromSession(User user, String password) throws LoginException, LogoutException {\n        getApiClient().login(user.getUserName(), password);\n        List<String> profiles = getApiClient().getSession().getProfiles();\n        getApiClient().logout();\n        return profiles;\n    }\n\n    @Test\n    public void searchProfileWithSearchTerm() throws BonitaException {\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.sort(ProfileSearchDescriptor.NAME, Order.ASC);\n        builder.searchTerm(\"Adm\");\n\n        final SearchResult<Profile> searchedProfiles = getProfileAPI().searchProfiles(builder.done());\n        assertEquals(1, searchedProfiles.getCount());\n        assertEquals(\"Administrator\", searchedProfiles.getResult().get(0).getName());\n    }\n\n    @Test(expected = SearchException.class)\n    public void searchProfileWithWrongParameter() throws Exception {\n        getProfileAPI().searchProfiles(null);\n    }\n\n    @Test(expected = ProfileNotFoundException.class)\n    public void getProfileWithWrongParameter() throws Exception {\n        getProfileAPI().getProfile(855);\n    }\n\n    @Test\n    public void addUser_to_profile_updates_profile_metadata() throws Exception {\n        final ProfileMemberCreator creator = new ProfileMemberCreator(\n                getProfileAPI().getProfile(adminProfileId).getId());\n        creator.setUserId(user3.getId());\n\n        shouldProfileMemberOperation_update_profile_metadata(creator);\n    }\n\n    @Test\n    public void addGroup_to_profile_updates_profile_metadata() throws Exception {\n        final ProfileMemberCreator creator = new ProfileMemberCreator(\n                getProfileAPI().getProfile(adminProfileId).getId());\n        creator.setGroupId(group3.getId());\n\n        shouldProfileMemberOperation_update_profile_metadata(creator);\n    }\n\n    @Test\n    public void addRole_to_profile_updates_profile_metadata() throws Exception {\n        final ProfileMemberCreator creator = new ProfileMemberCreator(\n                getProfileAPI().getProfile(adminProfileId).getId());\n        creator.setRoleId(role3.getId());\n\n        shouldProfileMemberOperation_update_profile_metadata(creator);\n    }\n\n    @Test\n    public void addRoleAndGroup_to_profile_updates_profile_metadata() throws Exception {\n        final ProfileMemberCreator creator = new ProfileMemberCreator(\n                getProfileAPI().getProfile(adminProfileId).getId());\n        creator.setGroupId(group3.getId());\n        creator.setRoleId(role3.getId());\n\n        shouldProfileMemberOperation_update_profile_metadata(creator);\n    }\n\n    private void shouldProfileMemberOperation_update_profile_metadata(final ProfileMemberCreator creator)\n            throws Exception {\n        // given\n        final Profile profileBefore = getProfileAPI().getProfile(adminProfileId);\n\n        // Make sure lastUpdateDate is changed\n        Thread.sleep(5);\n\n        // when\n        logout();\n        loginOnDefaultTenantWith(\"userName3\", \"User3Pwd\");\n        Thread.sleep(10);\n        final ProfileMember createProfileMember = getProfileAPI().createProfileMember(creator);\n\n        // then\n        final Profile profileAfter = getProfileAPI().getProfile(adminProfileId);\n        checkMetaData(profileBefore, profileAfter, user3);\n\n        // Make sure lastUpdateDate is changed\n        Thread.sleep(5);\n\n        // when\n        logout();\n        loginOnDefaultTenantWith(\"userName1\", \"User1Pwd\");\n        getProfileAPI().deleteProfileMember(createProfileMember.getId());\n\n        // then\n        final Profile profileAfterDelete = getProfileAPI().getProfile(adminProfileId);\n        checkMetaData(profileAfter, profileAfterDelete, user1);\n    }\n\n    private void checkMetaData(final Profile profileBefore, final Profile profileAfter, final User user) {\n        assertThat(profileAfter.getLastUpdateDate()).as(\"lastUpdateDate should be modified\")\n                .isAfter(profileBefore.getLastUpdateDate());\n        assertThat(profileAfter.getLastUpdatedBy()).as(\"lastUpdatedBy should be modified\")\n                .isNotEqualTo(profileBefore.getLastUpdatedBy());\n        assertThat(profileAfter.getLastUpdatedBy()).as(\"lastUpdatedBy should be modified\").isEqualTo(user.getId());\n\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/profile/ProfileMemberIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.*;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.junit.Test;\n\npublic class ProfileMemberIT extends AbstractProfileIT {\n\n    @Test\n    public void createAndDeleteGroupToProfile() throws BonitaException {\n        checkCreateAndDeleProfileMember(\"group\", null, group1.getId(), null);\n    }\n\n    @Test\n    public void createAndDeleteRoleAndGroupToProfile() throws BonitaException {\n        getIdentityAPI().addUserMembership(user1.getId(), group1.getId(), role1.getId());\n        getIdentityAPI().addUserMembership(user2.getId(), group1.getId(), role2.getId());\n        getIdentityAPI().addUserMembership(user3.getId(), group2.getId(), role1.getId());\n\n        // Create group Profile1\n        checkCreateAndDeleProfileMember(\"roleAndGroup\", null, group1.getId(), role1.getId());\n    }\n\n    @Test\n    public void createAndDeleteRoleToProfile() throws BonitaException {\n        checkCreateAndDeleProfileMember(\"role\", null, null, role3.getId());\n    }\n\n    @Test\n    public void createandDeleteUserProfile() throws BonitaException {\n        checkCreateAndDeleProfileMember(\"user\", user2.getId(), null, null);\n    }\n\n    @Test(expected = CreationException.class)\n    public void createProfileMemberWithWrongParameter() throws Exception {\n        getProfileAPI().createProfileMember(856L, null, null, null);\n    }\n\n    @Test\n    public void deleteOrganizationRemoveProfileMember() throws BonitaException {\n        // create user and add profile\n        final User user = getIdentityAPI().createUser(\"mixmaster.spike\", \"123456789\");\n        getProfileAPI().createProfileMember(adminProfileId, user.getId(), null, null);\n\n        // delete user\n        getIdentityAPI().deleteUser(user.getId());\n\n        // check there is no user mixmaster.spike anymore in this profile\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.sort(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART1, Order.ASC);\n        builder.filter(ProfileMemberSearchDescriptor.PROFILE_ID, adminProfileId);\n        final SearchResult<ProfileMember> searchedProfileMember = getProfileAPI().searchProfileMembers(\"user\",\n                builder.done());\n        assertEquals(1, searchedProfileMember.getResult().size());\n    }\n\n    private void checkCreateAndDeleProfileMember(final String memberType, final Long userId, final Long groupId,\n            final Long roleId) throws BonitaException {\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.sort(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART1, Order.ASC);\n        builder.filter(ProfileMemberSearchDescriptor.PROFILE_ID, adminProfileId);\n        SearchResult<ProfileMember> searchedProfileMember = getProfileAPI().searchProfileMembers(memberType,\n                builder.done());\n        final long numberOfProfileMembersBeforeCreation = searchedProfileMember.getCount();\n\n        final ProfileMember addProfileMemberResult = getProfileAPI().createProfileMember(adminProfileId, userId,\n                groupId, roleId);\n\n        searchedProfileMember = getProfileAPI().searchProfileMembers(memberType, builder.done());\n        assertEquals(numberOfProfileMembersBeforeCreation + 1, searchedProfileMember.getCount());\n\n        // delete UserProfile1\n        getProfileAPI().deleteProfileMember(addProfileMemberResult.getId());\n        searchedProfileMember = getProfileAPI().searchProfileMembers(memberType, builder.done());\n        assertEquals(numberOfProfileMembersBeforeCreation, searchedProfileMember.getCount());\n    }\n\n    @Test(expected = DeletionException.class)\n    public void deleteProfileMemberWithWrongParameter() throws Exception {\n        getProfileAPI().deleteProfileMember(856L);\n    }\n\n    @Test(expected = CreationException.class)\n    public void createTwiceSameProfileMemberWithUser() throws BonitaException {\n        getProfileAPI().createProfileMember(adminProfileId, user1.getId(), null, null);\n        getProfileAPI().createProfileMember(adminProfileId, user1.getId(), null, null);\n    }\n\n    @Test(expected = CreationException.class)\n    public void createTwiceSameProfileMemberWithRole() throws BonitaException {\n        getProfileAPI().createProfileMember(adminProfileId, null, null, role1.getId());\n        getProfileAPI().createProfileMember(adminProfileId, null, null, role1.getId());\n    }\n\n    @Test(expected = CreationException.class)\n    public void createTwiceSameProfileMemberWithGroup() throws BonitaException {\n        getProfileAPI().createProfileMember(adminProfileId, null, group1.getId(), null);\n        getProfileAPI().createProfileMember(adminProfileId, null, group1.getId(), null);\n    }\n\n    @Test(expected = CreationException.class)\n    public void createTwiceSameProfileMemberWithMembership() throws BonitaException {\n        getIdentityAPI().addUserMembership(user1.getId(), group1.getId(), role1.getId());\n\n        // Create UserProfile\n        getProfileAPI().createProfileMember(adminProfileId, null, group1.getId(), role1.getId());\n        getProfileAPI().createProfileMember(adminProfileId, null, group1.getId(), role1.getId());\n    }\n\n    @Test\n    public void getProfileForUser() {\n        // Get Profile For User\n        final List<Profile> profiles = getProfileAPI().getProfilesForUser(user1.getId(), 0, 10,\n                ProfileCriterion.NAME_ASC);\n        assertThat(profiles).hasSize(2).extracting(\"name\").contains(\"Administrator\", \"User\");\n    }\n\n    @Test\n    public void getProfileForUserReturnDisctinctProfiles() throws BonitaException {\n        // user 1 is mapped to profile \"Administrator\" through direct \"userName1\" mapping + through \"role1\" mapping:\n        getIdentityAPI().addUserMembership(user1.getId(), group2.getId(), role1.getId());\n\n        // Get Profile For User\n        final List<Profile> getUserProfiles = getProfileAPI().getProfilesForUser(user1.getId(), 0, 10,\n                ProfileCriterion.NAME_ASC);\n\n        assertEquals(2, getUserProfiles.size());\n        assertThat(getUserProfiles).extracting(\"name\").containsExactlyInAnyOrder(\"Administrator\", \"User\");\n    }\n\n    @Test\n    public void getProfileForUserFromGroup() throws BonitaException {\n        getIdentityAPI().addUserMembership(user1.getId(), group1.getId(), role1.getId());\n\n        // Get Profile For User\n        final List<Profile> getUserProfiles = getProfileAPI().getProfilesForUser(user1.getId(), 0, 10,\n                ProfileCriterion.NAME_ASC);\n        assertEquals(2, getUserProfiles.size());\n        assertEquals(\"Administrator\", getUserProfiles.get(0).getName());\n        assertEquals(\"User\", getUserProfiles.get(1).getName());\n    }\n\n    @Test\n    public void getProfileForUserFromRole() throws BonitaException {\n        getIdentityAPI().addUserMembership(user1.getId(), group1.getId(), role1.getId());\n\n        // Get Profile For User\n        final List<Profile> getUserProfiles = getProfileAPI().getProfilesForUser(user1.getId(), 0, 10,\n                ProfileCriterion.NAME_ASC);\n        assertEquals(2, getUserProfiles.size());\n        assertEquals(\"Administrator\", getUserProfiles.get(0).getName());\n        assertEquals(\"User\", getUserProfiles.get(1).getName());\n    }\n\n    @Test\n    public void getProfileForUserFromRoleAndGroup() throws BonitaException {\n        getIdentityAPI().addUserMembership(user5.getId(), group1.getId(), role2.getId());\n        getIdentityAPI().addUserMembership(user2.getId(), group1.getId(), role3.getId());\n\n        // User2 have membership group1 + role3 and does not match a profile member of administrator profile:\n        assertThat(getProfileAPI().getProfilesForUser(user2.getId(), 0, 10, ProfileCriterion.NAME_ASC))\n                .noneMatch(p -> p.getName().equals(\"Administrator\"));\n\n        // User5 have membership group1 + role2 and matches a profile member of administrator profile:\n        assertThat(getProfileAPI().getProfilesForUser(user5.getId(), 0, 10, ProfileCriterion.NAME_ASC))\n                .anyMatch(p -> p.getName().equals(\"Administrator\"));\n    }\n\n    @Test\n    public void getProfilesForUserWithWrongParameter() {\n        final List<Profile> profilesForUser = getProfileAPI().getProfilesForUser(564162L, 0, 10,\n                ProfileCriterion.NAME_ASC);\n        assertEquals(0, profilesForUser.size());\n    }\n\n    @Test\n    public void searchUserProfileMembersForProfile() throws BonitaException {\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.sort(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART1, Order.ASC);\n        builder.filter(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART1, \"User1FirstName\");\n        builder.filter(ProfileMemberSearchDescriptor.PROFILE_ID, adminProfileId);\n        builder.searchTerm(\"userName1\");\n        final SearchResult<ProfileMember> searchedProfileMember = getProfileAPI().searchProfileMembers(\"user\",\n                builder.done());\n        assertEquals(1, searchedProfileMember.getResult().size());\n        assertEquals(\"User1FirstName\", searchedProfileMember.getResult().get(0).getDisplayNamePart1());\n        assertEquals(\"User1LastName\", searchedProfileMember.getResult().get(0).getDisplayNamePart2());\n        assertEquals(\"userName1\", searchedProfileMember.getResult().get(0).getDisplayNamePart3());\n    }\n\n    @Test\n    public void searchGroupProfileMembersForProfile() throws BonitaException {\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.sort(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART1, Order.ASC);\n        builder.filter(ProfileMemberSearchDescriptor.PROFILE_ID, userProfileId);\n        final SearchResult<ProfileMember> searchedProfileMember = getProfileAPI().searchProfileMembers(\"group\",\n                builder.done());\n\n        assertEquals(1, searchedProfileMember.getResult().size());\n        final ProfileMember profileMember = searchedProfileMember.getResult().get(0);\n        assertEquals(\"group1\", profileMember.getDisplayNamePart1());\n        final String displayNamePart3 = profileMember.getDisplayNamePart3();\n        // Can be null with Oracle\n        assertTrue(displayNamePart3 == null || displayNamePart3.isEmpty());\n    }\n\n    @Test\n    public void searchRoleProfileMembersForProfile() throws BonitaException {\n        SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.sort(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART1, Order.ASC);\n        builder.filter(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART1, \"role2\");\n        builder.filter(ProfileMemberSearchDescriptor.PROFILE_ID, adminProfileId);\n        builder.searchTerm(\"role2\");\n        SearchResult<ProfileMember> searchedProfileMember = getProfileAPI().searchProfileMembers(\"role\",\n                builder.done());\n        assertEquals(1, searchedProfileMember.getCount());\n        ProfileMember profileMember = searchedProfileMember.getResult().get(0);\n        assertEquals(\"role2\", profileMember.getDisplayNamePart1());\n\n        builder = new SearchOptionsBuilder(0, 10);\n        builder.sort(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART1, Order.ASC);\n        builder.filter(ProfileMemberSearchDescriptor.PROFILE_ID, adminProfileId);\n        searchedProfileMember = getProfileAPI().searchProfileMembers(\"role\", builder.done());\n        profileMember = searchedProfileMember.getResult().get(0);\n        assertEquals(2, searchedProfileMember.getCount());\n        assertEquals(\"role1\", profileMember.getDisplayNamePart1());\n        final String displayNamePart2 = profileMember.getDisplayNamePart2();\n        // Can be null with Oracle\n        assertTrue(displayNamePart2 == null || displayNamePart2.isEmpty());\n        final String displayNamePart3 = profileMember.getDisplayNamePart3();\n        assertTrue(displayNamePart3 == null || displayNamePart3.isEmpty());\n        assertEquals(\"role2\", searchedProfileMember.getResult().get(1).getDisplayNamePart1());\n    }\n\n    @Test\n    public void searchRoleAndGroupProfileMembersForProfile() throws BonitaException {\n        SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.sort(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART1, Order.ASC);\n        builder.sort(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART2, Order.ASC);\n        builder.filter(ProfileMemberSearchDescriptor.PROFILE_ID, adminProfileId);\n        SearchResult<ProfileMember> searchedProfileMember = getProfileAPI().searchProfileMembers(\"roleAndGroup\",\n                builder.done());\n        assertEquals(2, searchedProfileMember.getCount());\n        assertEquals(\"role2\", searchedProfileMember.getResult().get(0).getDisplayNamePart1());\n        assertEquals(\"group1\", searchedProfileMember.getResult().get(0).getDisplayNamePart2());\n        assertEquals(\"role2\", searchedProfileMember.getResult().get(1).getDisplayNamePart1());\n        assertEquals(\"group2\", searchedProfileMember.getResult().get(1).getDisplayNamePart2());\n\n        builder = new SearchOptionsBuilder(0, 10);\n        builder.sort(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART1, Order.ASC);\n        builder.filter(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART2, \"group2\");\n        builder.filter(ProfileMemberSearchDescriptor.PROFILE_ID, adminProfileId);\n        searchedProfileMember = getProfileAPI().searchProfileMembers(\"roleAndGroup\", builder.done());\n        assertEquals(\"role2\", searchedProfileMember.getResult().get(0).getDisplayNamePart1());\n        assertEquals(\"group2\", searchedProfileMember.getResult().get(0).getDisplayNamePart2());\n    }\n\n    @Test\n    public void getNumberOfProfileMembers() {\n        final List<Long> profileIds = new ArrayList<>();\n        profileIds.add(adminProfileId);\n        profileIds.add(userProfileId);\n        final Map<Long, Long> numberOfProfileMembers = getProfileAPI().getNumberOfProfileMembers(profileIds);\n        assertNotNull(numberOfProfileMembers);\n        assertEquals(2, numberOfProfileMembers.size());\n        assertEquals(Long.valueOf(5L), numberOfProfileMembers.get(adminProfileId));\n        assertEquals(Long.valueOf(2L), numberOfProfileMembers.get(userProfileId));\n    }\n\n    @Test(expected = SearchException.class)\n    public void searchProfileMembersForProfileWithWrongParameter() throws Exception {\n        getProfileAPI().searchProfileMembers(\"plop\", null);\n    }\n\n    @Test\n    public void searchUserProfileMembersOfUser() throws BonitaException {\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.filter(ProfileMemberSearchDescriptor.USER_ID, user1.getId());\n        final SearchResult<ProfileMember> searchedProfileMember = getProfileAPI().searchProfileMembers(\"user\",\n                builder.done());\n        assertEquals(2, searchedProfileMember.getResult().size());\n        assertEquals(\"User1FirstName\", searchedProfileMember.getResult().get(0).getDisplayNamePart1());\n        assertEquals(\"User1LastName\", searchedProfileMember.getResult().get(0).getDisplayNamePart2());\n        assertEquals(\"userName1\", searchedProfileMember.getResult().get(0).getDisplayNamePart3());\n    }\n\n    @Test\n    public void searchUserProfileMembersOfGroup() throws BonitaException {\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.filter(ProfileMemberSearchDescriptor.GROUP_ID, group1.getId());\n        final SearchResult<ProfileMember> searchedProfileMember = getProfileAPI().searchProfileMembers(\"group\",\n                builder.done());\n        assertEquals(1, searchedProfileMember.getResult().size());\n        assertEquals(\"group1\", searchedProfileMember.getResult().get(0).getDisplayNamePart1());\n    }\n\n    @Test\n    public void searchUserProfileMembersOfRole() throws BonitaException {\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.filter(ProfileMemberSearchDescriptor.ROLE_ID, role1.getId());\n        final SearchResult<ProfileMember> searchedProfileMember = getProfileAPI().searchProfileMembers(\"role\",\n                builder.done());\n        assertEquals(1, searchedProfileMember.getResult().size());\n        assertEquals(\"role1\", searchedProfileMember.getResult().get(0).getDisplayNamePart1());\n    }\n\n    @Test\n    public void searchUserProfileMembersOfRoleAndGroup() throws BonitaException {\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.filter(ProfileMemberSearchDescriptor.ROLE_ID, role2.getId());\n        builder.filter(ProfileMemberSearchDescriptor.GROUP_ID, group2.getId());\n        final SearchResult<ProfileMember> searchedProfileMember = getProfileAPI().searchProfileMembers(\"roleAndGroup\",\n                builder.done());\n        assertEquals(1, searchedProfileMember.getResult().size());\n        assertEquals(\"role2\", searchedProfileMember.getResult().get(0).getDisplayNamePart1());\n        assertEquals(\"group2\", searchedProfileMember.getResult().get(0).getDisplayNamePart2());\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/search/SearchActivityInstanceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport static java.util.Arrays.asList;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.test.TestStates.SKIPPED;\nimport static org.junit.Assert.*;\n\nimport java.io.IOException;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\nimport java.util.stream.IntStream;\n\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.NamedElement;\nimport org.bonitasoft.engine.bpm.bar.BarResource;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.bpm.flownode.*;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.supervisor.ProcessSupervisor;\nimport org.bonitasoft.engine.bpm.supervisor.ProcessSupervisorSearchDescriptor;\nimport org.bonitasoft.engine.connectors.TestConnectorLongToExecute;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.expression.InvalidExpressionException;\nimport org.bonitasoft.engine.identity.Group;\nimport org.bonitasoft.engine.identity.Role;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.test.BuildTestUtil;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.bonitasoft.engine.test.check.CheckNbOfActivities;\nimport org.junit.Test;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class SearchActivityInstanceIT extends TestWithUser {\n\n    final long ONE_HOUR = 60L * 60L * 1000L;\n\n    /**\n     * Tests that the archiving mechanism works:\n     * <ul>\n     * <li>archived activities must be found in the archive</li>\n     * <li>archived activities must be deleted from the journal</li>\n     * </ul>\n     *\n     * @throws Exception\n     * @since 6.0\n     */\n    @Test\n    public void activityArchivingMechanism() throws Exception {\n        // create user and process\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME).addDescription(\"Process to test archiving mechanism\")\n                .addUserTask(\"userTask\", ACTOR_NAME)\n                .addUserTask(\"secondTask\", ACTOR_NAME);\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME,\n                user);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(user.getId(), processDefinition.getId());\n\n        // Wait for 2 activities in READY state:\n        waitForUserTask(processInstance, \"userTask\");\n        waitForUserTask(processInstance, \"secondTask\");\n\n        // Check that no tasks are archived yet:\n        SearchOptionsBuilder searchBuilder = new SearchOptionsBuilder(0, 12);\n        searchBuilder.filter(ArchivedActivityInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID,\n                processInstance.getId());\n        SearchResult<ArchivedActivityInstance> archActivitResult = getProcessAPI()\n                .searchArchivedActivities(searchBuilder.done());\n        assertEquals(0, archActivitResult.getCount());\n\n        // Skip the 2 tasks one by one:\n        final List<ActivityInstance> activities = getProcessAPI().getActivities(processInstance.getId(), 0, 2);\n        int nbTasksArchived = 0;\n        for (final ActivityInstance activityInstance : activities) {\n            skipTask(activityInstance.getId());\n            nbTasksArchived++;\n\n            final int nbActivities = 2 - nbTasksArchived;\n            // Check nb of remaining tasks in the journal:\n            final CheckNbOfActivities checkNbActivities = new CheckNbOfActivities(getProcessAPI(), 200, 3000, true,\n                    processInstance, nbActivities);\n            final boolean waitUntil = checkNbActivities.waitUntil();\n            assertTrue(\"Expected \" + nbActivities + \" open activities for process instance \" + processInstance.getId()\n                    + \" but got \"\n                    + checkNbActivities.getResult().size(), waitUntil);\n\n            // Check that both tasks are found in the archive:\n            searchBuilder = new SearchOptionsBuilder(0, 12);\n            searchBuilder.filter(ArchivedActivityInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID,\n                    processInstance.getId());\n            archActivitResult = getProcessAPI().searchArchivedActivities(searchBuilder.done());\n            assertEquals(nbTasksArchived, archActivitResult.getCount());\n        }\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void searchArchivedTasksManagedBy() throws Exception {\n        // Create tasks, assign them, some to some users managed by \"manager\", some to other users with different manager.\n        // execute / skip them so that the tasks (or only some) are archived.\n        // Retrieve the tasks. check the count, the retrieved list, the order.\n        final User jack = createUser(\"jack\", \"bpm\");\n        // Jules is not subordinates of jack:\n        final User jules = createUser(\"jules\", \"bpm\");\n        final User john = createUser(\"john\", \"bpm\", jack.getId());\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME).addDescription(\"Famous French actor\");\n        final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask(\"userTask1\", ACTOR_NAME)\n                .addUserTask(\"userTask2\", ACTOR_NAME)\n                .addUserTask(\"userTask3\", ACTOR_NAME).addUserTask(\"task4\", ACTOR_NAME)\n                .addUserTask(\"userTask5\", ACTOR_NAME)\n                .addUserTask(\"userTask6\", ACTOR_NAME).getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                jack);\n        final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId());\n        final long stepId1 = waitForUserTaskAndAssignIt(pi0, \"userTask1\", john).getId();\n        final long stepId2 = waitForUserTaskAndAssignIt(pi0, \"userTask2\", john).getId();\n        final long stepId3 = waitForUserTaskAndAssignIt(pi0, \"userTask3\", jack).getId();\n        final long stepId4 = waitForUserTaskAndAssignIt(pi0, \"task4\", john).getId();\n        final long stepId5 = waitForUserTaskAndAssignIt(pi0, \"userTask5\", jules).getId();\n        final long stepId6 = waitForUserTask(pi0, \"userTask6\");\n        // don't assign userTask6 to anyone.\n        skipTask(stepId1);\n        skipTask(stepId2);\n        skipTask(stepId3);\n        skipTask(stepId4);\n        skipTask(stepId5);\n        skipTask(stepId6);\n        waitForProcessToFinish(pi0);\n        SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        // filter only userTask1:\n        builder.filter(ArchivedHumanTaskInstanceSearchDescriptor.NAME, \"userTask1\");\n        builder.sort(ArchivedHumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n\n        SearchResult<ArchivedHumanTaskInstance> aHumanTasksRes = getProcessAPI()\n                .searchArchivedHumanTasksManagedBy(jack.getId(), builder.done());\n        assertEquals(1, aHumanTasksRes.getCount());\n        List<ArchivedHumanTaskInstance> tasks = aHumanTasksRes.getResult();\n        ArchivedHumanTaskInstance aArchivedHumanTaskInstance = tasks.get(0);\n        assertEquals(\"userTask1\", aArchivedHumanTaskInstance.getName());\n\n        builder = new SearchOptionsBuilder(0, 10);\n        // filter all userTask*:\n        builder.searchTerm(\"userTask\");\n        builder.sort(ArchivedHumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n        aHumanTasksRes = getProcessAPI().searchArchivedHumanTasksManagedBy(jack.getId(), builder.done());\n        assertEquals(2, aHumanTasksRes.getCount());\n        tasks = aHumanTasksRes.getResult();\n        aArchivedHumanTaskInstance = tasks.get(0);\n        assertEquals(\"userTask1\", aArchivedHumanTaskInstance.getName());\n        aArchivedHumanTaskInstance = tasks.get(1);\n        assertEquals(\"userTask2\", aArchivedHumanTaskInstance.getName());\n\n        builder = new SearchOptionsBuilder(0, 10);\n        // filter all ask*:\n        builder.searchTerm(\"user\");\n        builder.sort(ArchivedHumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n        aHumanTasksRes = getProcessAPI().searchArchivedHumanTasksManagedBy(jack.getId(), builder.done());\n        assertEquals(2, aHumanTasksRes.getCount());\n        tasks = aHumanTasksRes.getResult();\n        aArchivedHumanTaskInstance = tasks.get(0);\n        assertEquals(\"userTask1\", aArchivedHumanTaskInstance.getName());\n        aArchivedHumanTaskInstance = tasks.get(1);\n        assertEquals(\"userTask2\", aArchivedHumanTaskInstance.getName());\n\n        builder = new SearchOptionsBuilder(0, 10);\n        // filter all task of this process definition:\n        builder.filter(ArchivedHumanTaskInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDefinition.getId());\n        builder.sort(ArchivedHumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n        aHumanTasksRes = getProcessAPI().searchArchivedHumanTasksManagedBy(jack.getId(), builder.done());\n        assertEquals(3, aHumanTasksRes.getCount());\n        tasks = aHumanTasksRes.getResult();\n        aArchivedHumanTaskInstance = tasks.get(0);\n        assertEquals(\"task4\", aArchivedHumanTaskInstance.getName());\n        aArchivedHumanTaskInstance = tasks.get(1);\n        assertEquals(\"userTask1\", aArchivedHumanTaskInstance.getName());\n        aArchivedHumanTaskInstance = tasks.get(2);\n        assertEquals(\"userTask2\", aArchivedHumanTaskInstance.getName());\n\n        disableAndDeleteProcess(processDefinition);\n        deleteUsers(john, jack, jules);\n    }\n\n    @Test\n    public void searchAssignedTasksManagedBy() throws Exception {\n        final User jack = createUser(\"jack\", \"bpm\");\n        final User john = createUser(\"john\", \"bpm\", jack.getId());\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask(\"userTask1\", ACTOR_NAME)\n                .addUserTask(\"userTask2\", ACTOR_NAME)\n                .addUserTask(\"userTask3\", ACTOR_NAME).addUserTask(\"task4\", ACTOR_NAME).getProcess();\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                john);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndAssignIt(processInstance, \"userTask1\", john).getId();\n        waitForUserTaskAndAssignIt(processInstance, \"userTask2\", john).getId();\n        waitForUserTaskAndAssignIt(processInstance, \"userTask3\", jack).getId();\n        waitForUserTaskAndAssignIt(processInstance, \"task4\", john).getId();\n\n        SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.filter(HumanTaskInstanceSearchDescriptor.NAME, \"userTask1\");\n        builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n        SearchResult<HumanTaskInstance> searchAssignedTasksManagedBy = getProcessAPI()\n                .searchAssignedTasksManagedBy(jack.getId(), builder.done());\n        assertEquals(1, searchAssignedTasksManagedBy.getCount());\n        List<HumanTaskInstance> tasks = searchAssignedTasksManagedBy.getResult();\n        HumanTaskInstance humanTaskInstance = tasks.get(0);\n        assertEquals(\"userTask1\", humanTaskInstance.getName());\n\n        builder = new SearchOptionsBuilder(0, 10);\n        builder.searchTerm(\"userTask\");\n        builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n        searchAssignedTasksManagedBy = getProcessAPI().searchAssignedTasksManagedBy(jack.getId(), builder.done());\n        assertEquals(2, searchAssignedTasksManagedBy.getCount());\n        tasks = searchAssignedTasksManagedBy.getResult();\n        humanTaskInstance = tasks.get(0);\n        assertEquals(\"userTask1\", humanTaskInstance.getName());\n        humanTaskInstance = tasks.get(1);\n        assertEquals(\"userTask2\", humanTaskInstance.getName());\n\n        builder = new SearchOptionsBuilder(0, 10);\n        builder.searchTerm(\"user\");\n        builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n        searchAssignedTasksManagedBy = getProcessAPI().searchAssignedTasksManagedBy(jack.getId(), builder.done());\n        assertEquals(2, searchAssignedTasksManagedBy.getCount());\n        tasks = searchAssignedTasksManagedBy.getResult();\n        humanTaskInstance = tasks.get(0);\n        assertEquals(\"userTask1\", humanTaskInstance.getName());\n        humanTaskInstance = tasks.get(1);\n        assertEquals(\"userTask2\", humanTaskInstance.getName());\n\n        disableAndDeleteProcess(processDefinition);\n        deleteUsers(john, jack);\n    }\n\n    @Test\n    public void searchHumanTaskInstances() throws Exception {\n        final Group group = createGroup(\"groupName\");\n        final Role role = createRole(\"roleName\");\n\n        // First process def with 2 instances:\n        final DesignProcessDefinition designProcessDef1 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(asList(\"initTask1\"),\n                        asList(true));\n        final ProcessDefinition processDef1 = deployAndEnableProcessWithActor(designProcessDef1, ACTOR_NAME, user);\n        final ProcessInstance pi1 = getProcessAPI().startProcess(processDef1.getId());\n        final long step1Id = waitForUserTask(pi1, \"initTask1\");\n        final ProcessInstance pi2 = getProcessAPI().startProcess(processDef1.getId());\n        waitForUserTask(pi2, \"initTask1\");\n\n        final DesignProcessDefinition designProcessDef2 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(PROCESS_NAME + 2, PROCESS_VERSION,\n                        asList(\"initTask2\"), asList(true));\n        final ProcessDefinition processDef2 = deployAndEnableProcessWithActor(designProcessDef2, ACTOR_NAME, user);\n        final ProcessInstance pi3 = getProcessAPI().startProcess(processDef2.getId());\n        waitForUserTask(pi3, \"initTask2\");\n        final ProcessInstance pi4 = getProcessAPI().startProcess(processDef2.getId());\n        waitForUserTask(pi4, \"initTask2\");\n        final ProcessInstance pi5 = getProcessAPI().startProcess(processDef2.getId());\n        waitForUserTask(pi5, \"initTask2\");\n\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n        final SearchResult<HumanTaskInstance> humanTasksSearch = getProcessAPI()\n                .searchHumanTaskInstances(searchOptionsBuilder.done());\n        assertEquals(5, humanTasksSearch.getCount());\n\n        searchHumanTaskInstancesFilteredByPriority(pi2);\n        searchHumanTaskInstancesFilteredByAssigneeId();\n        searchHumanTaskInstancesFilteredByProcessInstance(pi2);\n        searchHumanTaskInstancesFilteredByProcessDefinition(processDef2);\n        searchHumanTaskInstancesFilteredByState(step1Id);\n        searchHumanTaskInstancesFilteredByUser(user.getId(), processDef1.getId());\n        searchHumanTaskInstancesFilteredByGroup(group.getId(), processDef2.getId());\n        searchHumanTaskInstancesFilteredByRole(role.getId(), processDef1.getId());\n        searchHumanTaskInstancesFilteredByMembership(group.getId(), role.getId(), processDef2.getId());\n        searchHumanTaskInstancesByTerm();\n\n        disableAndDeleteProcess(processDef1, processDef2);\n        deleteGroups(group);\n        deleteRoles(role);\n    }\n\n    @Test\n    public void searchAssignedAndPendingHumanTaskInstances() throws Exception {\n        BusinessArchive bar1 = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(new ProcessDefinitionBuilder().createNewInstance(\"p1\", \"1.0\")\n                        .addActor(\"a\")\n                        .addUserTask(\"p1task1\", \"a\")\n                        .addUserTask(\"p1task2\", \"a\")\n                        .addUserTask(\"p1task3\", \"a\")\n                        .addConnector(\"c1\", \"cid\", \"1.0\", ConnectorEvent.ON_FINISH).getProcess())\n                .addConnectorImplementation(\n                        BuildTestUtil.generateConnectorImplementation(\"cid\", \"1.0\", SlowConnector.class))\n                .done();\n        BusinessArchive bar2 = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(new ProcessDefinitionBuilder().createNewInstance(\"p2\", \"1.0\")\n                        .addActor(\"a\")\n                        .addUserTask(\"p2task1\", \"a\")\n                        .addUserTask(\"p2task2\", \"a\")\n                        .addUserTask(\"p2task3\", \"a\")\n                        .addConnector(\"c1\", \"cid\", \"1.0\", ConnectorEvent.ON_FINISH).getProcess())\n                .addConnectorImplementation(\n                        BuildTestUtil.generateConnectorImplementation(\"cid\", \"1.0\", SlowConnector.class))\n                .done();\n        ProcessDefinition p1 = deployAndEnableProcessWithActor(bar1, \"a\", user);\n        ProcessDefinition p2 = deployAndEnableProcessWithActor(bar2, \"a\", user);\n\n        var instance1 = getProcessAPI().startProcess(p1.getId());\n        var instance2 = getProcessAPI().startProcess(p2.getId());\n        waitForUserTask(\"p1task1\");\n        long p1task2 = waitForUserTask(\"p1task2\");\n        long p1task3 = waitForUserTask(\"p1task3\");\n        waitForUserTask(\"p2task1\");\n        long p2task2 = waitForUserTask(\"p2task2\");\n        long p2task3 = waitForUserTask(\"p2task3\");\n\n        getProcessAPI().assignUserTask(p1task2, user.getId());\n        getProcessAPI().assignUserTask(p1task3, user.getId());\n        getProcessAPI().assignUserTask(p2task2, user.getId());\n        getProcessAPI().assignUserTask(p2task3, user.getId());\n        getProcessAPI().executeUserTask(p1task3, Collections.emptyMap());\n        getProcessAPI().executeUserTask(p2task3, Collections.emptyMap());\n\n        SearchResult<HumanTaskInstance> searchResult = getProcessAPI().searchAssignedAndPendingHumanTasks(\n                new SearchOptionsBuilder(0, 100)\n                        .sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC).done());\n        assertThat(searchResult).matches(haveTasks(\"p1task1\", \"p1task2\", \"p2task1\", \"p2task2\"),\n                toDescription(searchResult));\n\n        searchResult = getProcessAPI().searchAssignedAndPendingHumanTasks(new SearchOptionsBuilder(0, 100)\n                .searchTerm(\"p1\")\n                .sort(HumanTaskInstanceSearchDescriptor.NAME, Order.DESC).done());\n        assertThat(searchResult).matches(haveTasks(\"p1task2\", \"p1task1\"), toDescription(searchResult));\n\n        searchResult = getProcessAPI().searchAssignedAndPendingHumanTasks(new SearchOptionsBuilder(0, 100)\n                .filter(\"name\", \"p1task3\").done());\n        assertThat(searchResult).matches(haveTasks(), toDescription(searchResult));\n\n        try {\n            waitForTaskInState(instance1, \"p1task3\", TestStates.NORMAL_FINAL);\n        } catch (ActivityInstanceNotFoundException e) {\n            // ignore it, instance is already completed\n        }\n        try {\n            waitForTaskInState(instance2, \"p2task3\", TestStates.NORMAL_FINAL);\n        } catch (ActivityInstanceNotFoundException e) {\n            // ignore it, instance is already completed\n        }\n        disableAndDeleteProcess(p1, p2);\n    }\n\n    private String toDescription(SearchResult<HumanTaskInstance> searchResult) {\n        return \"had tasks: \"\n                + searchResult.getResult().stream().map(NamedElement::getName).collect(Collectors.joining(\", \"));\n    }\n\n    private Predicate<? super SearchResult<HumanTaskInstance>> haveTasks(String... tasks) {\n        return result -> result.getCount() == tasks.length\n                && IntStream.range(0, tasks.length)\n                        .mapToObj(i -> tasks[i].equals(result.getResult().get(i).getName()))\n                        .allMatch(b -> b);\n    }\n\n    private void searchHumanTaskInstancesFilteredByUser(final long userId, final long processDefinitionId)\n            throws Exception {\n        final ProcessSupervisor supervisor = getProcessAPI().createProcessSupervisorForUser(processDefinitionId,\n                userId);\n\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n        searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.USER_ID, userId);\n        final SearchResult<HumanTaskInstance> humanTasksSearch = getProcessAPI()\n                .searchHumanTaskInstances(searchOptionsBuilder.done());\n        assertEquals(2, humanTasksSearch.getCount());\n        for (final HumanTaskInstance task : humanTasksSearch.getResult()) {\n            assertEquals(\"initTask1\", task.getName());\n        }\n\n        getProcessAPI().deleteSupervisor(supervisor.getSupervisorId());\n    }\n\n    private void searchHumanTaskInstancesFilteredByGroup(final long groupId, final long processDefinitionId)\n            throws Exception {\n        final ProcessSupervisor supervisor = getProcessAPI().createProcessSupervisorForGroup(processDefinitionId,\n                groupId);\n\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n        searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.GROUP_ID, groupId);\n        final SearchResult<HumanTaskInstance> humanTasksSearch = getProcessAPI()\n                .searchHumanTaskInstances(searchOptionsBuilder.done());\n        assertEquals(3, humanTasksSearch.getCount());\n        for (final HumanTaskInstance task : humanTasksSearch.getResult()) {\n            assertEquals(\"initTask2\", task.getName());\n        }\n\n        getProcessAPI().deleteSupervisor(supervisor.getSupervisorId());\n    }\n\n    private void searchHumanTaskInstancesFilteredByRole(final long roleId, final long processDefinitionId)\n            throws Exception {\n        final ProcessSupervisor supervisor = getProcessAPI().createProcessSupervisorForRole(processDefinitionId,\n                roleId);\n\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n        searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.ROLE_ID, roleId);\n        final SearchResult<HumanTaskInstance> humanTasksSearch = getProcessAPI()\n                .searchHumanTaskInstances(searchOptionsBuilder.done());\n        assertEquals(2, humanTasksSearch.getCount());\n        for (final HumanTaskInstance task : humanTasksSearch.getResult()) {\n            assertEquals(\"initTask1\", task.getName());\n        }\n\n        getProcessAPI().deleteSupervisor(supervisor.getSupervisorId());\n    }\n\n    private void searchHumanTaskInstancesFilteredByMembership(final long groupId, final long roleId,\n            final long processDefinitionId) throws Exception {\n        final ProcessSupervisor supervisor = getProcessAPI().createProcessSupervisorForMembership(processDefinitionId,\n                groupId, roleId);\n\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n        searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.ROLE_ID, roleId);\n        final SearchResult<HumanTaskInstance> humanTasksSearch = getProcessAPI()\n                .searchHumanTaskInstances(searchOptionsBuilder.done());\n        assertEquals(3, humanTasksSearch.getCount());\n        for (final HumanTaskInstance task : humanTasksSearch.getResult()) {\n            assertEquals(\"initTask2\", task.getName());\n        }\n\n        final SearchOptionsBuilder searchOptionsBuilder2 = new SearchOptionsBuilder(0, 45);\n        searchOptionsBuilder2.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n        searchOptionsBuilder2.filter(HumanTaskInstanceSearchDescriptor.GROUP_ID, groupId);\n        final SearchResult<HumanTaskInstance> humanTasksSearch2 = getProcessAPI()\n                .searchHumanTaskInstances(searchOptionsBuilder2.done());\n        assertEquals(humanTasksSearch, humanTasksSearch2);\n\n        getProcessAPI().deleteSupervisor(supervisor.getSupervisorId());\n    }\n\n    private void searchHumanTaskInstancesFilteredByState(final long step1Id) throws UpdateException, SearchException {\n        getProcessAPI().assignUserTask(step1Id, user.getId());\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n        searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.STATE_NAME, ActivityStates.READY_STATE);\n        final SearchResult<HumanTaskInstance> humanTasksSearch = getProcessAPI()\n                .searchHumanTaskInstances(searchOptionsBuilder.done());\n        assertEquals(5, humanTasksSearch.getCount());\n        for (final HumanTaskInstance task : humanTasksSearch.getResult()) {\n            assertEquals(ActivityStates.READY_STATE, task.getState());\n        }\n    }\n\n    private void searchHumanTaskInstancesByTerm() throws SearchException {\n        SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n        searchOptionsBuilder.searchTerm(\"initTask2\");\n        SearchResult<HumanTaskInstance> humanTasksSearch = getProcessAPI()\n                .searchHumanTaskInstances(searchOptionsBuilder.done());\n        assertEquals(3, humanTasksSearch.getCount());\n        for (final HumanTaskInstance task : humanTasksSearch.getResult()) {\n            assertEquals(\"initTask2\", task.getName());\n        }\n\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n        searchOptionsBuilder.searchTerm(\"initTask\");\n        humanTasksSearch = getProcessAPI().searchHumanTaskInstances(searchOptionsBuilder.done());\n        assertEquals(5, humanTasksSearch.getCount());\n        for (final HumanTaskInstance task : humanTasksSearch.getResult()) {\n            assertTrue(\"keyword search sould return only tasks with name containing 'initTask'\",\n                    task.getName().contains(\"initTask\"));\n        }\n    }\n\n    private void searchHumanTaskInstancesFilteredByProcessDefinition(final ProcessDefinition processDef2)\n            throws SearchException {\n        SearchOptionsBuilder searchOptionsBuilder;\n        SearchResult<HumanTaskInstance> humanTasksSearch;\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n        searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDef2.getId());\n        humanTasksSearch = getProcessAPI().searchHumanTaskInstances(searchOptionsBuilder.done());\n        assertEquals(3, humanTasksSearch.getCount());\n    }\n\n    private void searchHumanTaskInstancesFilteredByProcessInstance(final ProcessInstance pi2) throws SearchException {\n        SearchOptionsBuilder searchOptionsBuilder;\n        SearchResult<HumanTaskInstance> humanTasksSearch;\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n        searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.PROCESS_INSTANCE_ID, pi2.getId());\n        humanTasksSearch = getProcessAPI().searchHumanTaskInstances(searchOptionsBuilder.done());\n        assertEquals(1, humanTasksSearch.getCount());\n    }\n\n    private void searchHumanTaskInstancesFilteredByAssigneeId() throws SearchException {\n        // There should be no assigned tasks to 'user':\n        SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n        searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.ASSIGNEE_ID, user.getId());\n        SearchResult<HumanTaskInstance> humanTasksSearch = getProcessAPI()\n                .searchHumanTaskInstances(searchOptionsBuilder.done());\n        assertEquals(0, humanTasksSearch.getCount());\n\n        // There should be 5 non-assigned tasks:\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n        searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.ASSIGNEE_ID, 0L);\n        humanTasksSearch = getProcessAPI().searchHumanTaskInstances(searchOptionsBuilder.done());\n        assertEquals(5, humanTasksSearch.getCount());\n    }\n\n    private void searchHumanTaskInstancesFilteredByPriority(final ProcessInstance pi2)\n            throws UpdateException, SearchException {\n        // There should be 1 task which priority is above_normal:\n        final List<ActivityInstance> activityInstances = getProcessAPI().getActivities(pi2.getId(), 0, 10);\n        getProcessAPI().setTaskPriority(activityInstances.get(0).getId(), TaskPriority.ABOVE_NORMAL);\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.PRIORITY, TaskPriority.ABOVE_NORMAL);\n        final SearchResult<HumanTaskInstance> humanTasksSearch = getProcessAPI()\n                .searchHumanTaskInstances(searchOptionsBuilder.done());\n        assertEquals(1, humanTasksSearch.getCount());\n        assertEquals(TaskPriority.ABOVE_NORMAL, humanTasksSearch.getResult().get(0).getPriority());\n    }\n\n    @Test\n    public void searchHumanTaskInstancesOrderByPriorityAndDueDate() throws Exception {\n        final ProcessDefinitionBuilder definitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_NAME, PROCESS_VERSION);\n        definitionBuilder.addStartEvent(\"start\");\n        definitionBuilder.addActor(ACTOR_NAME);\n        definitionBuilder.addUserTask(\"initTask1\", ACTOR_NAME).addPriority(TaskPriority.HIGHEST.name())\n                .addExpectedDuration(dueDateInHours(10L));\n        definitionBuilder.addUserTask(\"initTask2\", ACTOR_NAME).addPriority(TaskPriority.HIGHEST.name())\n                .addExpectedDuration(dueDateInHours(20L));\n        definitionBuilder.addUserTask(\"initTask3\", ACTOR_NAME).addPriority(TaskPriority.LOWEST.name())\n                .addExpectedDuration(dueDateInHours(30L));\n        definitionBuilder.addUserTask(\"initTask4\", ACTOR_NAME).addPriority(TaskPriority.HIGHEST.name())\n                .addExpectedDuration(dueDateInHours(5L));\n        definitionBuilder.addUserTask(\"initTask5\", ACTOR_NAME).addPriority(TaskPriority.NORMAL.name())\n                .addExpectedDuration(dueDateInHours(15L));\n        definitionBuilder.addUserTask(\"initTask6\", ACTOR_NAME).addPriority(TaskPriority.NORMAL.name())\n                .addExpectedDuration(dueDateInHours(10L));\n        definitionBuilder.addEndEvent(\"end\");\n        definitionBuilder.addTransition(\"start\", \"initTask1\");\n        definitionBuilder.addTransition(\"start\", \"initTask2\");\n        definitionBuilder.addTransition(\"start\", \"initTask3\");\n        definitionBuilder.addTransition(\"start\", \"initTask4\");\n        definitionBuilder.addTransition(\"start\", \"initTask5\");\n        definitionBuilder.addTransition(\"start\", \"initTask6\");\n        final ProcessDefinition processDef = deployAndEnableProcessWithActor(definitionBuilder.done(), ACTOR_NAME,\n                user);\n        getProcessAPI().startProcess(processDef.getId());\n        waitForUserTask(\"initTask1\");\n        waitForUserTask(\"initTask2\");\n        waitForUserTask(\"initTask3\");\n        waitForUserTask(\"initTask4\");\n        waitForUserTask(\"initTask5\");\n        waitForUserTask(\"initTask6\");\n\n        // There should be 6 tasks\n        SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        SearchResult<HumanTaskInstance> humanTasksSearch = getProcessAPI()\n                .searchHumanTaskInstances(searchOptionsBuilder.done());\n        assertEquals(6, humanTasksSearch.getCount());\n\n        List<HumanTaskInstance> humanTaskInstances;\n        // There should be 6 tasks when order by ascending priority. The first task is \"initTask3\".\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDef.getId());\n        searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.PRIORITY, Order.ASC);\n        humanTasksSearch = getProcessAPI().searchHumanTaskInstances(searchOptionsBuilder.done());\n        assertEquals(6, humanTasksSearch.getCount());\n        humanTaskInstances = humanTasksSearch.getResult();\n        assertEquals(\"initTask3\", humanTaskInstances.get(0).getName());\n\n        // There should be 6 tasks when order by descending priority. The last task is \"initTask3\".\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDef.getId());\n        searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.PRIORITY, Order.DESC);\n        humanTasksSearch = getProcessAPI().searchHumanTaskInstances(searchOptionsBuilder.done());\n        assertEquals(6, humanTasksSearch.getCount());\n        humanTaskInstances = humanTasksSearch.getResult();\n        assertEquals(\"initTask3\", humanTaskInstances.get(5).getName());\n\n        // There should be 6 tasks when order by ascending due date in this order : \"initTask4\", [\"initTask1\" \"initTask6\"], \"initTask5\", \"initTask2\",\n        // \"initTask3\"\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDef.getId());\n        searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.DUE_DATE, Order.ASC);\n        humanTasksSearch = getProcessAPI().searchHumanTaskInstances(searchOptionsBuilder.done());\n        assertEquals(6, humanTasksSearch.getCount());\n        humanTaskInstances = humanTasksSearch.getResult();\n        assertEquals(\"initTask4\", humanTaskInstances.get(0).getName());\n        assertEquals(\"initTask5\", humanTaskInstances.get(3).getName());\n        assertEquals(\"initTask2\", humanTaskInstances.get(4).getName());\n        assertEquals(\"initTask3\", humanTaskInstances.get(5).getName());\n\n        // There should be 6 tasks when order by descending due date in this order : \"initTask3\", \"initTask2\", \"initTask5\", [\"initTask1\" \"initTask6\"],\n        // \"initTask4\"\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDef.getId());\n        searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.DUE_DATE, Order.DESC);\n        humanTasksSearch = getProcessAPI().searchHumanTaskInstances(searchOptionsBuilder.done());\n        assertEquals(6, humanTasksSearch.getCount());\n        humanTaskInstances = humanTasksSearch.getResult();\n        assertEquals(\"initTask3\", humanTaskInstances.get(0).getName());\n        assertEquals(\"initTask2\", humanTaskInstances.get(1).getName());\n        assertEquals(\"initTask5\", humanTaskInstances.get(2).getName());\n        assertEquals(\"initTask4\", humanTaskInstances.get(5).getName());\n\n        // There should be 6 tasks when order by due date and priority in this order : \"initTask4\", \"initTask1\", \"initTask2\", \"initTask5\", \"initTask6\",\n        // \"initTask3\"\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDef.getId());\n        searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.PRIORITY, Order.DESC);\n        searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.DUE_DATE, Order.ASC);\n        humanTasksSearch = getProcessAPI().searchHumanTaskInstances(searchOptionsBuilder.done());\n        assertEquals(6, humanTasksSearch.getCount());\n        humanTaskInstances = humanTasksSearch.getResult();\n        assertEquals(\"initTask4\", humanTaskInstances.get(0).getName());\n        assertEquals(\"initTask1\", humanTaskInstances.get(1).getName());\n        assertEquals(\"initTask2\", humanTaskInstances.get(2).getName());\n        assertEquals(\"initTask6\", humanTaskInstances.get(3).getName());\n        assertEquals(\"initTask5\", humanTaskInstances.get(4).getName());\n        assertEquals(\"initTask3\", humanTaskInstances.get(5).getName());\n\n        disableAndDeleteProcess(processDef);\n    }\n\n    private Expression dueDateInHours(long nbHours) throws InvalidExpressionException {\n        Expression expression = new ExpressionBuilder().createConstantLongExpression(nbHours * ONE_HOUR);\n        return expression;\n    }\n\n    /**\n     * @throws Exception\n     */\n    private void searchPendingTasks(final String taskName, final String actorName) throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(actorName);\n        final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask(taskName, actorName)\n                .getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, actorName,\n                user);\n        // -------- start process and wait for tasks\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance, taskName);\n\n        // -------- test pending task search methods\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.filter(HumanTaskInstanceSearchDescriptor.ASSIGNEE_ID, 0);\n        builder.filter(HumanTaskInstanceSearchDescriptor.STATE_NAME, ActivityStates.READY_STATE);\n        builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n        builder.searchTerm(\"'\");\n        final SearchResult<HumanTaskInstance> searchHumanTaskInstances = getProcessAPI()\n                .searchHumanTaskInstances(builder.done());\n        assertEquals(1, searchHumanTaskInstances.getCount());\n        final List<HumanTaskInstance> tasks = searchHumanTaskInstances.getResult();\n        assertEquals(taskName, tasks.get(0).getName());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void searchArchivedActivities() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask(\"userTask1\", ACTOR_NAME)\n                .addUserTask(\"userTask2\", ACTOR_NAME)\n                .addAutomaticTask(\"automaticTask\").addManualTask(\"manualTask\", ACTOR_NAME).getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessInstance pi1 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance pi2 = getProcessAPI().startProcess(processDefinition.getId());\n\n        // No need to verify anything, if no exception, query exists\n        getProcessAPI().searchActivities(\n                new SearchOptionsBuilder(0, 10)\n                        .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, pi1.getId())\n                        .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.AUTOMATIC_TASK).done());\n\n        getProcessAPI().searchActivities(\n                new SearchOptionsBuilder(0, 10)\n                        .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, pi1.getId())\n                        .filter(ProcessSupervisorSearchDescriptor.USER_ID, user.getId()).done());\n\n        getProcessAPI().searchActivities(\n                new SearchOptionsBuilder(0, 10)\n                        .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.AUTOMATIC_TASK)\n                        .filter(ProcessSupervisorSearchDescriptor.USER_ID, pi1.getId()).done());\n\n        waitForUserTask(pi1, \"userTask1\");\n        waitForUserTaskAndAssignIt(pi1, \"userTask2\", user);\n        waitForUserTask(pi1, \"manualTask\");\n\n        waitForUserTask(pi2, \"userTask1\");\n        waitForUserTask(pi2, \"userTask2\");\n        waitForUserTask(pi2, \"manualTask\");\n\n        // finish the tasks\n        final List<ActivityInstance> openedActivityInstances1 = getProcessAPI().getOpenActivityInstances(pi1.getId(), 0,\n                20, ActivityInstanceCriterion.DEFAULT);\n        assertEquals(3, openedActivityInstances1.size());\n        for (final ActivityInstance activityInstance : openedActivityInstances1) {\n            final long activityInstanceId = activityInstance.getId();\n            getProcessAPI().setActivityStateById(activityInstanceId, 12);\n        }\n        waitForProcessToFinish(pi1);\n\n        final List<ActivityInstance> openedActivityInstances2 = getProcessAPI().getOpenActivityInstances(pi2.getId(), 0,\n                20, ActivityInstanceCriterion.DEFAULT);\n        assertEquals(3, openedActivityInstances2.size());\n        for (final ActivityInstance activityInstance : openedActivityInstances2) {\n            final long activityInstanceId = activityInstance.getId();\n            getProcessAPI().setActivityStateById(activityInstanceId, 12);\n        }\n        waitForProcessToFinish(pi2);\n        // each automatic task will have four-state archived instance\n        SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 20);\n        searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.PROCESS_DEFINITION_ID,\n                processDefinition.getId());\n        searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.ACTIVITY_TYPE,\n                FlowNodeType.AUTOMATIC_TASK);\n        SearchResult<ArchivedActivityInstance> archivedActivityInstancesSearch = getProcessAPI()\n                .searchArchivedActivities(searchOptionsBuilder.done());\n        assertEquals(2, archivedActivityInstancesSearch.getCount());\n        for (final ArchivedActivityInstance activity : archivedActivityInstancesSearch.getResult()) {\n            assertTrue(activity instanceof ArchivedAutomaticTaskInstance);\n        }\n        // test activity type\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 20);\n        searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.PROCESS_DEFINITION_ID,\n                processDefinition.getId());\n        searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.USER_TASK);\n        archivedActivityInstancesSearch = getProcessAPI().searchArchivedActivities(searchOptionsBuilder.done());\n        assertEquals(4, archivedActivityInstancesSearch.getCount());\n        for (final ArchivedActivityInstance activity : archivedActivityInstancesSearch.getResult()) {\n            assertTrue(activity instanceof ArchivedUserTaskInstance);\n        }\n\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 20);\n        searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.PROCESS_DEFINITION_ID,\n                processDefinition.getId());\n        searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.MANUAL_TASK);\n        archivedActivityInstancesSearch = getProcessAPI().searchArchivedActivities(searchOptionsBuilder.done());\n        assertEquals(2, archivedActivityInstancesSearch.getCount());\n        for (final ArchivedActivityInstance activity : archivedActivityInstancesSearch.getResult()) {\n            assertTrue(activity instanceof ArchivedManualTaskInstance);\n        }\n\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 20);\n        searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.PROCESS_DEFINITION_ID,\n                processDefinition.getId());\n        searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.HUMAN_TASK);\n        archivedActivityInstancesSearch = getProcessAPI().searchArchivedActivities(searchOptionsBuilder.done());\n        assertEquals(6, archivedActivityInstancesSearch.getCount());\n        for (final ArchivedActivityInstance activity : archivedActivityInstancesSearch.getResult()) {\n            assertTrue(activity instanceof ArchivedHumanTaskInstance);\n            assertTrue(activity instanceof ArchivedManualTaskInstance || activity instanceof ArchivedUserTaskInstance);\n        }\n\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 20);\n        searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID, pi1.getId());\n        searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.ACTIVITY_TYPE,\n                FlowNodeType.AUTOMATIC_TASK);\n        archivedActivityInstancesSearch = getProcessAPI().searchArchivedActivities(searchOptionsBuilder.done());\n        assertEquals(1, archivedActivityInstancesSearch.getCount());\n        for (final ArchivedActivityInstance activity : archivedActivityInstancesSearch.getResult()) {\n            assertTrue(activity instanceof ArchivedAutomaticTaskInstance);\n        }\n\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 20);\n        searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID, pi1.getId());\n        searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.HUMAN_TASK);\n        archivedActivityInstancesSearch = getProcessAPI().searchArchivedActivities(searchOptionsBuilder.done());\n        assertEquals(3, archivedActivityInstancesSearch.getCount());\n        for (final ArchivedActivityInstance activity : archivedActivityInstancesSearch.getResult()) {\n            assertTrue(activity instanceof ArchivedHumanTaskInstance);\n            assertTrue(activity instanceof ArchivedManualTaskInstance || activity instanceof ArchivedUserTaskInstance);\n        }\n\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 20);\n        searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.PROCESS_DEFINITION_ID,\n                processDefinition.getId());\n        searchOptionsBuilder.sort(ArchivedActivityInstanceSearchDescriptor.ARCHIVE_DATE, Order.DESC);\n        archivedActivityInstancesSearch = getProcessAPI().searchArchivedActivities(searchOptionsBuilder.done());\n        assertEquals(8, archivedActivityInstancesSearch.getCount());\n        for (final ArchivedActivityInstance activity : archivedActivityInstancesSearch.getResult()) {\n            assertTrue(activity instanceof ArchivedManualTaskInstance || activity instanceof ArchivedUserTaskInstance\n                    || activity instanceof ArchivedAutomaticTaskInstance);\n        }\n\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 20);\n        searchOptionsBuilder.searchTerm(\"userTask\");\n        searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.PROCESS_DEFINITION_ID,\n                processDefinition.getId());\n        searchOptionsBuilder.sort(ArchivedActivityInstanceSearchDescriptor.NAME, Order.DESC);\n        final SearchResult<ArchivedActivityInstance> taskInstanceSearchResult = getProcessAPI()\n                .searchArchivedActivities(searchOptionsBuilder.done());\n        assertEquals(4, taskInstanceSearchResult.getCount());\n        final List<ArchivedActivityInstance> archivedActivities = taskInstanceSearchResult.getResult();\n        final ArchivedActivityInstance aut1 = archivedActivities.get(0);\n        final ArchivedActivityInstance aut2 = archivedActivities.get(1);\n        final ArchivedActivityInstance aut3 = archivedActivities.get(2);\n        final ArchivedActivityInstance aut4 = archivedActivities.get(3);\n        assertEquals(\"userTask2\", aut1.getName());\n        assertEquals(\"userTask2\", aut2.getName());\n        assertEquals(\"userTask1\", aut3.getName());\n        assertEquals(\"userTask1\", aut4.getName());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void searchArchivedTasks() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask(\"userTask1\", ACTOR_NAME)\n                .addUserTask(\"userTask2\", ACTOR_NAME)\n                .getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(pi0, \"userTask1\");\n        final long step2Id = waitForUserTaskAndAssignIt(pi0, \"userTask2\", user).getId();\n\n        getProcessAPI().setActivityStateByName(step1Id, SKIPPED.getStateName());\n        getProcessAPI().setActivityStateByName(step2Id, SKIPPED.getStateName());\n        waitForProcessToFinish(pi0);\n\n        SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.filter(ArchivedHumanTaskInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDefinition.getId());\n        builder.sort(ArchivedHumanTaskInstanceSearchDescriptor.NAME, Order.DESC);\n        SearchResult<ArchivedHumanTaskInstance> taskInstanceSearchResult = getProcessAPI()\n                .searchArchivedHumanTasks(builder.done());\n        assertEquals(2, taskInstanceSearchResult.getCount());\n        List<ArchivedHumanTaskInstance> archivedTasks = taskInstanceSearchResult.getResult();\n        final ArchivedHumanTaskInstance aut1 = archivedTasks.get(0);\n        final ArchivedHumanTaskInstance aut2 = archivedTasks.get(1);\n        assertEquals(\"userTask2\", aut1.getName());\n        assertEquals(\"userTask1\", aut2.getName());\n        final String archivedTaskActorName = getProcessAPI().getActor(aut1.getActorId()).getName();\n        assertEquals(ACTOR_NAME, archivedTaskActorName);\n\n        // filter task assigned by user\n        builder = new SearchOptionsBuilder(0, 10);\n        builder.filter(ArchivedHumanTaskInstanceSearchDescriptor.ASSIGNEE_ID, user.getId());\n        builder.filter(ArchivedHumanTaskInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDefinition.getId());\n        builder.sort(ArchivedHumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n        taskInstanceSearchResult = getProcessAPI().searchArchivedHumanTasks(builder.done());\n        assertEquals(1, taskInstanceSearchResult.getCount());\n        archivedTasks = taskInstanceSearchResult.getResult();\n        assertEquals(\"userTask2\", archivedTasks.get(0).getName());\n\n        // search with sort on reached state date\n        List<ArchivedHumanTaskInstance> descResult = getProcessAPI().searchArchivedHumanTasks(\n                new SearchOptionsBuilder(0, 10)\n                        .sort(ArchivedHumanTaskInstanceSearchDescriptor.REACHED_STATE_DATE, Order.DESC).done())\n                .getResult();\n        assertThat(descResult.get(0).getReachedStateDate()).isAfterOrEqualsTo(descResult.get(1).getReachedStateDate());\n\n        List<ArchivedHumanTaskInstance> ascResult = getProcessAPI().searchArchivedHumanTasks(\n                new SearchOptionsBuilder(0, 10)\n                        .sort(ArchivedHumanTaskInstanceSearchDescriptor.REACHED_STATE_DATE, Order.ASC).done())\n                .getResult();\n        assertThat(ascResult.get(0).getReachedStateDate()).isBeforeOrEqualsTo(ascResult.get(1).getReachedStateDate());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void searchArchivedActivitiesInTerminalState() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask(\"userTask1\", ACTOR_NAME)\n                .addUserTask(\"userTask2\", ACTOR_NAME)\n                .addUserTask(\"userTask3\", ACTOR_NAME).addTransition(\"userTask2\", \"userTask3\").getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndAssignIt(pi0, \"userTask1\", user);\n        waitForUserTaskAndExecuteIt(pi0, \"userTask2\", user);\n        waitForUserTask(pi0, \"userTask3\");\n\n        SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10)\n                .filter(ArchivedFlowNodeInstanceSearchDescriptor.TERMINAL, true);\n        SearchResult<ArchivedFlowNodeInstance> searchFlowNodeInstances = getProcessAPI()\n                .searchArchivedFlowNodeInstances(builder.done());\n        assertEquals(1, searchFlowNodeInstances.getResult().size());\n        assertEquals(\"userTask2\", searchFlowNodeInstances.getResult().get(0).getName());\n        builder = new SearchOptionsBuilder(0, 10).filter(ArchivedFlowNodeInstanceSearchDescriptor.TERMINAL, false);\n        searchFlowNodeInstances = getProcessAPI().searchArchivedFlowNodeInstances(builder.done());\n        assertTrue(searchFlowNodeInstances.getResult().size() > 1);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void searchArchivedTasksWithApostrophe() throws Exception {\n        final String taskName = \"'Task\";\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask(taskName, ACTOR_NAME)\n                .getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(pi0, taskName);\n\n        final List<ActivityInstance> activityInstances = getProcessAPI().getActivities(pi0.getId(), 0, 10);\n        assertEquals(1, activityInstances.size());\n        for (final ActivityInstance activityInstance : activityInstances) {\n            final long activityInstanceId = activityInstance.getId();\n            getProcessAPI().setActivityStateById(activityInstanceId, 12);\n        }\n        waitForProcessToFinish(pi0);\n        final SearchResult<ArchivedHumanTaskInstance> taskInstanceSearchResult = getProcessAPI()\n                .searchArchivedHumanTasks(\n                        new SearchOptionsBuilder(0, 10).searchTerm(\"'\").done());\n        assertEquals(1, taskInstanceSearchResult.getCount());\n        final List<ArchivedHumanTaskInstance> archivedTasks = taskInstanceSearchResult.getResult();\n        final ArchivedHumanTaskInstance archivedHumanTaskInstance = archivedTasks.get(0);\n        assertEquals(taskName, archivedHumanTaskInstance.getName());\n        final String archivedTaskActorName = getProcessAPI().getActor(archivedHumanTaskInstance.getActorId()).getName();\n        assertEquals(ACTOR_NAME, archivedTaskActorName);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    /**\n     * if you remove a process between the two calls, re-deploy it and re-instantiate it: it should get right results.\n     *\n     * @throws Exception\n     */\n    @Test\n    public void searchPendingUserTaskInstances() throws Exception {\n        searchPendingUserTaskInstancesByActivityInstanceCriterion(ActivityInstanceCriterion.NAME_ASC);\n        searchPendingUserTaskInstancesByActivityInstanceCriterion(ActivityInstanceCriterion.NAME_DESC);\n        searchPendingUserTaskInstancesByActivityInstanceCriterion(ActivityInstanceCriterion.REACHED_STATE_DATE_ASC);\n        searchPendingUserTaskInstancesByActivityInstanceCriterion(ActivityInstanceCriterion.REACHED_STATE_DATE_DESC);\n        searchPendingUserTaskInstancesByActivityInstanceCriterion(ActivityInstanceCriterion.LAST_UPDATE_ASC);\n        searchPendingUserTaskInstancesByActivityInstanceCriterion(ActivityInstanceCriterion.LAST_UPDATE_DESC);\n        searchPendingUserTaskInstancesByActivityInstanceCriterion(ActivityInstanceCriterion.PRIORITY_ASC);\n        searchPendingUserTaskInstancesByActivityInstanceCriterion(ActivityInstanceCriterion.PRIORITY_DESC);\n        searchPendingUserTaskInstancesByActivityInstanceCriterion(ActivityInstanceCriterion.DEFAULT);\n    }\n\n    private void searchPendingUserTaskInstancesByActivityInstanceCriterion(\n            final ActivityInstanceCriterion activityInstanceCriterion) throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        processBuilder.addUserTask(\"Request\", ACTOR_NAME).addPriority(TaskPriority.LOWEST.name());\n        processBuilder.addUserTask(\"Request2\", ACTOR_NAME);\n        processBuilder.addUserTask(\"Approval\", ACTOR_NAME);\n        processBuilder.addUserTask(\"Approval2\", ACTOR_NAME).addPriority(TaskPriority.HIGHEST.name());\n        processBuilder.addTransition(\"Request\", \"Approval\");\n        processBuilder.addTransition(\"Request2\", \"Approval2\");\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance, \"Request\");\n        waitForUserTask(processInstance, \"Request2\");\n\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 20);\n        searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.NAME, \"Request2\");\n        final SearchResult<HumanTaskInstance> humanTasksSearch = getProcessAPI().searchPendingTasksForUser(user.getId(),\n                searchOptionsBuilder.done());\n        assertEquals(1, humanTasksSearch.getCount());\n        final HumanTaskInstance userTaskId = humanTasksSearch.getResult().get(0);\n        assignAndExecuteStep(userTaskId, user.getId());\n        waitForUserTask(processInstance, \"Approval2\");\n\n        final List<HumanTaskInstance> userTaskInstances = getProcessAPI().getPendingHumanTaskInstances(user.getId(), 0,\n                10, activityInstanceCriterion);\n        assertNotNull(userTaskInstances);\n        assertEquals(2, userTaskInstances.size());\n        switch (activityInstanceCriterion) {\n            case NAME_ASC:\n                assertEquals(\"Approval2\", userTaskInstances.get(0).getName());\n                break;\n            case NAME_DESC:\n                assertEquals(\"Request\", userTaskInstances.get(0).getName());\n                break;\n            case REACHED_STATE_DATE_ASC:\n                assertEquals(\"Request\", userTaskInstances.get(0).getName());\n                break;\n            case REACHED_STATE_DATE_DESC:\n                assertEquals(\"Approval2\", userTaskInstances.get(0).getName());\n                break;\n            case LAST_UPDATE_ASC:\n                assertEquals(\"Request\", userTaskInstances.get(0).getName());\n                break;\n            case LAST_UPDATE_DESC:\n                assertEquals(\"Approval2\", userTaskInstances.get(0).getName());\n                break;\n            case PRIORITY_ASC:\n                assertEquals(\"Request\", userTaskInstances.get(0).getName());\n                break;\n            case PRIORITY_DESC:\n            case DEFAULT:\n            default:\n                assertEquals(\"Approval2\", userTaskInstances.get(0).getName());\n                break;\n        }\n\n        // Clean up\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void searchPendingTasks() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask(\"userTask1\", ACTOR_NAME)\n                .addUserTask(\"userTask2\", ACTOR_NAME)\n                .addUserTask(\"userTask3\", ACTOR_NAME).addUserTask(\"task4\", ACTOR_NAME)\n                .addUserTask(\"userTask5\", ACTOR_NAME).getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        // -------- start process and wait for tasks\n        final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(pi0, \"userTask1\");\n        waitForUserTaskAndAssignIt(pi0, \"userTask2\", user);\n        waitForUserTaskAndAssignIt(pi0, \"userTask3\", user);\n        waitForUserTask(pi0, \"task4\");\n        waitForUserTask(pi0, \"userTask5\");\n\n        // -------- test pending task search methods\n        SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.filter(HumanTaskInstanceSearchDescriptor.ASSIGNEE_ID, 0);\n        builder.filter(HumanTaskInstanceSearchDescriptor.STATE_NAME, ActivityStates.READY_STATE);\n        builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n        SearchResult<HumanTaskInstance> searchHumanTaskInstances = getProcessAPI()\n                .searchHumanTaskInstances(builder.done());\n        assertEquals(3, searchHumanTaskInstances.getCount());\n        List<HumanTaskInstance> tasks = searchHumanTaskInstances.getResult();\n        HumanTaskInstance humanTaskInstance = tasks.get(0);\n        assertEquals(\"task4\", humanTaskInstance.getName());\n        humanTaskInstance = tasks.get(1);\n        assertEquals(\"userTask1\", humanTaskInstance.getName());\n        humanTaskInstance = tasks.get(2);\n        assertEquals(\"userTask5\", humanTaskInstance.getName());\n        // -------- test assign task search methods\n        builder = new SearchOptionsBuilder(0, 10);\n        builder.filter(HumanTaskInstanceSearchDescriptor.ASSIGNEE_ID, user.getId());\n        builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n        searchHumanTaskInstances = getProcessAPI().searchHumanTaskInstances(builder.done());\n        assertEquals(2, searchHumanTaskInstances.getCount());\n        tasks = searchHumanTaskInstances.getResult();\n        humanTaskInstance = tasks.get(0);\n        assertEquals(\"userTask2\", humanTaskInstance.getName());\n        humanTaskInstance = tasks.get(1);\n        assertEquals(\"userTask3\", humanTaskInstance.getName());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void searchAssignedAndPendingHumanTasks() throws Exception {\n        final User john = createUser(\"John\", PASSWORD);\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask(\"userTask1\", ACTOR_NAME)\n                .addUserTask(\"userTask2\", ACTOR_NAME)\n                .addUserTask(\"userTask3\", ACTOR_NAME).addUserTask(\"task4\", ACTOR_NAME)\n                .addUserTask(\"userTask5\", ACTOR_NAME).getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                asList(user, john));\n        // -------- start process and wait for tasks\n        final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(pi0, \"userTask1\");\n        waitForUserTaskAndAssignIt(pi0, \"userTask2\", user);\n        waitForUserTaskAndAssignIt(pi0, \"userTask3\", user);\n        waitForUserTaskAndAssignIt(pi0, \"task4\", john);\n        waitForUserTask(pi0, \"userTask5\");\n\n        // -------- test assigned & pending task search methods\n        SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n        SearchResult<HumanTaskInstance> searchHumanTaskInstances = getProcessAPI()\n                .searchAssignedAndPendingHumanTasks(processDefinition.getId(), builder.done());\n        assertEquals(5, searchHumanTaskInstances.getCount());\n        List<HumanTaskInstance> tasks = searchHumanTaskInstances.getResult();\n        assertEquals(\"task4\", tasks.get(0).getName());\n        assertEquals(\"userTask1\", tasks.get(1).getName());\n        assertEquals(\"userTask2\", tasks.get(2).getName());\n\n        // -------- test assign task search methods\n        builder = new SearchOptionsBuilder(0, 10);\n        builder.filter(HumanTaskInstanceSearchDescriptor.ASSIGNEE_ID, user.getId());\n        builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n        searchHumanTaskInstances = getProcessAPI().searchAssignedAndPendingHumanTasks(processDefinition.getId(),\n                builder.done());\n        assertEquals(2, searchHumanTaskInstances.getCount());\n        tasks = searchHumanTaskInstances.getResult();\n        assertEquals(\"userTask2\", tasks.get(0).getName());\n        assertEquals(\"userTask3\", tasks.get(1).getName());\n\n        disableAndDeleteProcess(processDefinition);\n        deleteUser(john);\n    }\n\n    @Test\n    public void searchAssignedAndPendingHumanTasksFor() throws Exception {\n        final User john = createUser(\"John\", PASSWORD);\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask(\"userTask1\", ACTOR_NAME)\n                .addUserTask(\"userTask2\", ACTOR_NAME)\n                .addUserTask(\"userTask3\", ACTOR_NAME).addUserTask(\"task4\", ACTOR_NAME)\n                .addUserTask(\"userTask5\", ACTOR_NAME).getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                asList(user, john));\n        // -------- start process and wait for tasks\n        final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(pi0, \"userTask1\");\n        waitForUserTaskAndAssignIt(pi0, \"userTask2\", user);\n        waitForUserTaskAndAssignIt(pi0, \"userTask3\", user);\n        waitForUserTaskAndAssignIt(pi0, \"task4\", john);\n        waitForUserTask(pi0, \"userTask5\");\n\n        // -------- test pending task search methods\n        SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n        SearchResult<HumanTaskInstance> searchHumanTaskInstances = getProcessAPI()\n                .searchAssignedAndPendingHumanTasksFor(processDefinition.getId(), john.getId(), builder.done());\n        assertEquals(3, searchHumanTaskInstances.getCount());\n        List<HumanTaskInstance> tasks = searchHumanTaskInstances.getResult();\n        assertEquals(\"task4\", tasks.get(0).getName());\n        assertEquals(\"userTask1\", tasks.get(1).getName());\n        assertEquals(\"userTask5\", tasks.get(2).getName());\n\n        // -------- test assign task search methods\n        builder = new SearchOptionsBuilder(0, 10);\n        builder.filter(HumanTaskInstanceSearchDescriptor.NAME, \"userTask1\");\n        builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n        searchHumanTaskInstances = getProcessAPI().searchAssignedAndPendingHumanTasksFor(processDefinition.getId(),\n                john.getId(), builder.done());\n        assertEquals(1, searchHumanTaskInstances.getCount());\n        tasks = searchHumanTaskInstances.getResult();\n        assertEquals(\"userTask1\", tasks.get(0).getName());\n\n        disableAndDeleteProcess(processDefinition);\n        deleteUser(john);\n    }\n\n    @Test\n    public void searchPendingHumanTasksAssignedToUser() throws Exception {\n        final User john = createUser(\"John\", PASSWORD);\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        processBuilder\n                .addUserTask(\"userTask1\", ACTOR_NAME)\n                .addUserTask(\"userTask2\", ACTOR_NAME)\n                .addUserTask(\"userTask3\", ACTOR_NAME)\n                .addUserTask(\"userTask4_assigned_to_John\", ACTOR_NAME)\n                .addUserTask(\"userTask5_assigned_to_John\", ACTOR_NAME);\n        processBuilder\n                .addUserTask(\"userTask6_assigned_to_John_long_execution\", ACTOR_NAME)\n                .addConnector(\"Simulate long execution\", \"slow-connector\", \"1.0\", ConnectorEvent.ON_FINISH);\n        processBuilder.addUserTask(\"userTask7\", ACTOR_NAME);\n\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder()\n                .createNewBusinessArchive()\n                .setProcessDefinition(processBuilder.done())\n                .addConnectorImplementation(\n                        new BarResource(\n                                \"slow-connector.impl\",\n                                BuildTestUtil.buildConnectorImplementationFile(\"slow-connector\",\n                                        \"1.0\",\n                                        \"slow-connector-impl\",\n                                        \"1.0\", SlowConnector.class.getName())))\n                .done();\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchive, ACTOR_NAME,\n                asList(user, john));\n\n        // -------- start process and wait for tasks\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance, \"userTask1\");\n        waitForUserTaskAndAssignIt(processInstance, \"userTask2\", user);\n        waitForUserTaskAndAssignIt(processInstance, \"userTask3\", user);\n        waitForUserTaskAndAssignIt(processInstance, \"userTask4_assigned_to_John\", john);\n        waitForUserTaskAndAssignIt(processInstance, \"userTask5_assigned_to_John\", john);\n        HumanTaskInstance longExecutionHumanTask = waitForUserTaskAndAssignIt(processInstance,\n                \"userTask6_assigned_to_John_long_execution\", john);\n        waitForUserTask(processInstance, \"userTask7\");\n\n        getProcessAPI().executeUserTask(longExecutionHumanTask.getId(), null);\n\n        // -------- test pending task search methods\n        // Perform a search\n        SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n        SearchResult<HumanTaskInstance> searchHumanTaskInstances = getProcessAPI()\n                .searchPendingTasksAssignedToUser(john.getId(), builder.done());\n        assertThat(searchHumanTaskInstances.getResult())\n                .describedAs(\"Human tasks\").hasSize(2)\n                .extracting(\"name\").describedAs(\"task names\")\n                .containsExactly(\"userTask4_assigned_to_John\", \"userTask5_assigned_to_John\");\n        // Perform a count\n        SearchResult<HumanTaskInstance> searchHumanTaskInstancesCountOnly = getProcessAPI()\n                .searchPendingTasksAssignedToUser(\n                        john.getId(),\n                        new SearchOptionsBuilder(0, 0).done());\n        assertThat(searchHumanTaskInstancesCountOnly.getCount()).describedAs(\"Total Human tasks count\").isEqualTo(2);\n        assertThat(searchHumanTaskInstancesCountOnly.getResult()).describedAs(\"Human tasks\").isEmpty();\n\n        // -------- tear down\n        try {\n            waitForTaskInState(processInstance, \"userTask6_assigned_to_John_long_execution\", TestStates.NORMAL_FINAL);\n        } catch (ActivityInstanceNotFoundException e) {\n            // ignore it, activity is already finished\n        }\n        disableAndDeleteProcess(processDefinition);\n        deleteUser(john);\n    }\n\n    @Test\n    public void searchPendingTasksManagedBy() throws Exception {\n        // Create tasks, some to some users managed by \"manager\", some to other users with different manager.\n        final User jack = createUser(\"jack\", \"bpm\");\n        // Jules is not subordinates of jack:\n        final User jules = createUser(\"jules\", \"bpm\");\n        final User john = createUser(\"john\", \"bpm\", jack.getId());\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME).addDescription(\"Famous French actor\");\n        final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask(\"userTask1\", ACTOR_NAME)\n                .addUserTask(\"userTask2\", ACTOR_NAME)\n                .addUserTask(\"userTask3\", ACTOR_NAME).addUserTask(\"task4\", ACTOR_NAME)\n                .addUserTask(\"userTask5\", ACTOR_NAME)\n                .addUserTask(\"userTask6\", ACTOR_NAME).getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                john);\n        final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(pi0, \"userTask1\");\n        waitForUserTask(pi0, \"userTask2\");\n        waitForUserTask(pi0, \"userTask3\");\n        waitForUserTask(pi0, \"task4\");\n        waitForUserTask(pi0, \"userTask5\");\n        waitForUserTask(pi0, \"userTask6\");\n\n        // filter all *userTask*, managedBy jack:\n        SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.searchTerm(\"usertask\");\n        builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n        SearchResult<HumanTaskInstance> aHumanTasksRes = getProcessAPI().searchPendingTasksManagedBy(jack.getId(),\n                builder.done());\n        assertEquals(5, aHumanTasksRes.getCount());\n        List<HumanTaskInstance> tasks = aHumanTasksRes.getResult();\n        HumanTaskInstance humanTaskInstance = tasks.get(0);\n        assertEquals(\"userTask1\", humanTaskInstance.getName());\n        humanTaskInstance = tasks.get(1);\n        assertEquals(\"userTask2\", humanTaskInstance.getName());\n        humanTaskInstance = tasks.get(2);\n        assertEquals(\"userTask3\", humanTaskInstance.getName());\n        humanTaskInstance = tasks.get(3);\n        assertEquals(\"userTask5\", humanTaskInstance.getName());\n        humanTaskInstance = tasks.get(4);\n        assertEquals(\"userTask6\", humanTaskInstance.getName());\n\n        // filter all *userTask*, managedBy jules:\n        builder = new SearchOptionsBuilder(0, 10);\n        builder.searchTerm(\"usertask\");\n        builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n        aHumanTasksRes = getProcessAPI().searchPendingTasksManagedBy(jules.getId(), builder.done());\n        assertEquals(0, aHumanTasksRes.getCount());\n        assertTrue(aHumanTasksRes.getResult().isEmpty());\n\n        // filter task4, managedBy jack:\n        builder = new SearchOptionsBuilder(0, 10);\n        builder.searchTerm(\"task\");\n        builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.DESC);\n        aHumanTasksRes = getProcessAPI().searchPendingTasksManagedBy(jack.getId(), builder.done());\n        assertEquals(6, aHumanTasksRes.getCount());\n\n        disableAndDeleteProcess(processDefinition);\n        deleteUsers(john, jack, jules);\n    }\n\n    @Test\n    public void searchPendingTasksWithMultipleWords() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask(\"userTask\", ACTOR_NAME)\n                .addUserTask(\"step1\", ACTOR_NAME)\n                .addUserTask(\"etape1\", ACTOR_NAME).addUserTask(\"tache\", ACTOR_NAME).getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        // -------- start process and wait for tasks\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance, \"userTask\");\n        waitForUserTask(processInstance, \"step1\");\n        waitForUserTask(processInstance, \"etape1\");\n        waitForUserTask(processInstance, \"tache\");\n\n        // -------- test pending task search methods\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.filter(HumanTaskInstanceSearchDescriptor.ASSIGNEE_ID, 0);\n        builder.filter(HumanTaskInstanceSearchDescriptor.STATE_NAME, ActivityStates.READY_STATE);\n        builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n        builder.searchTerm(\"eta userTask step\");\n        final SearchResult<HumanTaskInstance> searchHumanTaskInstances = getProcessAPI()\n                .searchHumanTaskInstances(builder.done());\n        assertEquals(3, searchHumanTaskInstances.getCount());\n        final List<HumanTaskInstance> tasks = searchHumanTaskInstances.getResult();\n        assertThat(tasks).extracting(\"name\").containsExactly(\"etape1\", \"step1\", \"userTask\");\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void searchActivityTaskInstancesAdvancedFilters() throws Exception {\n        // define a process containing one userTask.\n        final String taskName = \"ActivityForUser\";\n        final DesignProcessDefinition designProcessDef = BuildTestUtil.buildProcessDefinitionWithHumanAndAutomaticSteps(\n                asList(taskName),\n                asList(true));\n        final ProcessDefinition processDef = deployAndEnableProcessWithActor(designProcessDef, ACTOR_NAME, user);\n        // start twice and get 2 processInstances for processDef\n        final ProcessInstance pi1 = getProcessAPI().startProcess(processDef.getId());\n        waitForUserTask(pi1, taskName);\n        Thread.sleep(5);\n        final long afterCreationTask1 = System.currentTimeMillis();\n        final ProcessInstance pi2 = getProcessAPI().startProcess(processDef.getId());\n        waitForUserTask(pi2, taskName);\n        final long afterCreationTask2 = System.currentTimeMillis();\n        Thread.sleep(5);\n        final ProcessInstance pi3 = getProcessAPI().startProcess(processDef.getId());\n        waitForUserTask(pi3, taskName);\n\n        SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.filter(ActivityInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDef.getId());\n        SearchResult<ActivityInstance> activityInstancesSearch = getProcessAPI()\n                .searchActivities(searchOptionsBuilder.done());\n        assertEquals(3, activityInstancesSearch.getCount());\n\n        // ********* LESS_THAN operator *********\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.filter(ActivityInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDef.getId());\n        searchOptionsBuilder.lessThan(ActivityInstanceSearchDescriptor.LAST_MODIFICATION_DATE, afterCreationTask1);\n        activityInstancesSearch = getProcessAPI().searchActivities(searchOptionsBuilder.done());\n        assertEquals(1, activityInstancesSearch.getCount());\n        assertEquals(pi1.getId(), activityInstancesSearch.getResult().get(0).getParentProcessInstanceId());\n\n        // ********* BETWEEN operator *********\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.filter(ActivityInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDef.getId());\n        searchOptionsBuilder.between(ActivityInstanceSearchDescriptor.LAST_MODIFICATION_DATE, afterCreationTask1,\n                afterCreationTask2);\n        activityInstancesSearch = getProcessAPI().searchActivities(searchOptionsBuilder.done());\n        assertEquals(1, activityInstancesSearch.getCount());\n        final ActivityInstance activityInstance2 = activityInstancesSearch.getResult().get(0);\n        assertEquals(pi2.getId(), activityInstance2.getParentProcessInstanceId());\n\n        // ********** all fields **********\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, activityInstance2.getType())\n                .filter(ActivityInstanceSearchDescriptor.DISPLAY_NAME, activityInstance2.getDisplayName())\n                .filter(ActivityInstanceSearchDescriptor.NAME, activityInstance2.getName())\n                .filter(ActivityInstanceSearchDescriptor.PROCESS_DEFINITION_ID,\n                        activityInstance2.getProcessDefinitionId())\n                .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, activityInstance2.getRootContainerId())\n                .filter(ActivityInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID,\n                        activityInstance2.getParentProcessInstanceId())\n                .filter(ActivityInstanceSearchDescriptor.LAST_MODIFICATION_DATE,\n                        activityInstance2.getLastUpdateDate().getTime());\n        activityInstancesSearch = getProcessAPI().searchActivities(searchOptionsBuilder.done());\n\n        assertEquals(1, activityInstancesSearch.getCount());\n        final ActivityInstance activityInstance2Result = activityInstancesSearch.getResult().get(0);\n        assertEquals(activityInstance2, activityInstance2Result);\n\n        // ********* DIFFERENT FROM operator *********\n        SearchOptionsBuilder sob = new SearchOptionsBuilder(0, 10);\n        sob.filter(HumanTaskInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDef.getId());\n        sob.differentFrom(HumanTaskInstanceSearchDescriptor.ASSIGNEE_ID, 0);\n        final SearchOptions searchOpts = sob.done();\n        SearchResult<HumanTaskInstance> humanTasksSR = getProcessAPI().searchHumanTaskInstances(searchOpts);\n        assertEquals(0, humanTasksSR.getCount());\n\n        // assign one task\n        getProcessAPI().assignUserTask(activityInstance2.getId(), user.getId());\n\n        // Should then be 1 task assigned (ASSIGNEE_ID different from 0):\n        humanTasksSR = getProcessAPI().searchHumanTaskInstances(searchOpts);\n        assertEquals(1, humanTasksSR.getCount());\n\n        // ********* OR operator *********\n        // PROCESS_INSTANCE_ID = pi1 OR ASSIGNEE_ID different from 0 (from pi2):\n        sob = new SearchOptionsBuilder(0, 10);\n        sob.filter(HumanTaskInstanceSearchDescriptor.PROCESS_INSTANCE_ID, pi1.getId());\n        sob.or();\n        sob.differentFrom(HumanTaskInstanceSearchDescriptor.ASSIGNEE_ID, 0);\n        humanTasksSR = getProcessAPI().searchHumanTaskInstances(sob.done());\n        assertEquals(2, humanTasksSR.getCount());\n\n        disableAndDeleteProcess(processDef);\n    }\n\n    @Test\n    public void searchActivityTaskInstancesWithApostrophe() throws Exception {\n        // define a process containing one userTask.\n        final String taskName = \"Activity'ForUser\";\n        final DesignProcessDefinition designProcessDef = BuildTestUtil.buildProcessDefinitionWithHumanAndAutomaticSteps(\n                asList(taskName),\n                asList(true));\n        final ProcessDefinition processDef = deployAndEnableProcessWithActor(designProcessDef, ACTOR_NAME, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDef.getId());\n        waitForUserTask(processInstance, taskName);\n\n        // Search apostrophe\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.searchTerm(\"Activity'\");\n        final SearchResult<ActivityInstance> activityInstancesSearch = getProcessAPI()\n                .searchActivities(searchOptionsBuilder.done());\n        assertEquals(1, activityInstancesSearch.getCount());\n        assertEquals(processInstance.getId(), activityInstancesSearch.getResult().get(0).getParentProcessInstanceId());\n\n        disableAndDeleteProcess(processDef);\n    }\n\n    @Test\n    public void searchPendingTasksWithLikeWildcardsCharacters() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask(\"step#1a\", ACTOR_NAME)\n                .addUserTask(\"step#1_b\", ACTOR_NAME)\n                .addUserTask(\"step#1_c\", ACTOR_NAME).addUserTask(\"%step#2\", ACTOR_NAME)\n                .addUserTask(\"mystep3\", ACTOR_NAME).addUserTask(\"%step#4_a\", ACTOR_NAME)\n                .addUserTask(\"step:5\", ACTOR_NAME).getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        // -------- start process and wait for tasks\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance, \"step#1a\");\n        waitForUserTask(processInstance, \"step#1_b\");\n        waitForUserTask(processInstance, \"step#1_c\");\n        waitForUserTask(processInstance, \"%step#2\");\n        waitForUserTask(processInstance, \"mystep3\");\n        waitForUserTask(processInstance, \"%step#4_a\");\n        waitForUserTask(processInstance, \"step:5\");\n\n        // -------- test pending task search methods\n        SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n        builder.searchTerm(\"step#\");\n        final SearchResult<HumanTaskInstance> searchHumanTaskInstancesWithEscapeCharacter = getProcessAPI()\n                .searchHumanTaskInstances(builder.done());\n        assertEquals(5, searchHumanTaskInstancesWithEscapeCharacter.getCount());\n        List<HumanTaskInstance> tasks = searchHumanTaskInstancesWithEscapeCharacter.getResult();\n        assertThat(tasks).extracting(\"name\").containsOnly(\"step#1_b\", \"step#1_c\", \"step#1a\", \"%step#2\", \"%step#4_a\");\n\n        builder = new SearchOptionsBuilder(0, 10);\n        builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n        builder.searchTerm(\"step#1_\");\n        final SearchResult<HumanTaskInstance> searchHumanTaskInstancesWithUnderscoreCharacter = getProcessAPI()\n                .searchHumanTaskInstances(builder.done());\n        assertEquals(2, searchHumanTaskInstancesWithUnderscoreCharacter.getCount());\n        tasks = searchHumanTaskInstancesWithUnderscoreCharacter.getResult();\n        assertThat(tasks).extracting(\"name\").containsOnly(\"step#1_b\", \"step#1_c\");\n\n        builder = new SearchOptionsBuilder(0, 10);\n        builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n        builder.searchTerm(\"%step#\");\n        final SearchResult<HumanTaskInstance> searchHumanTaskInstancesWithPercentageCharacter = getProcessAPI()\n                .searchHumanTaskInstances(builder.done());\n        assertEquals(2, searchHumanTaskInstancesWithPercentageCharacter.getCount());\n        tasks = searchHumanTaskInstancesWithPercentageCharacter.getResult();\n        assertThat(tasks).extracting(\"name\").containsOnly(\"%step#2\", \"%step#4_a\");\n\n        builder = new SearchOptionsBuilder(0, 10);\n        builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n        builder.searchTerm(\"step:5\");\n        final SearchResult<HumanTaskInstance> searchHumanTaskInstancesWithColonCharacter = getProcessAPI()\n                .searchHumanTaskInstances(builder.done());\n        assertEquals(1, searchHumanTaskInstancesWithColonCharacter.getCount());\n        tasks = searchHumanTaskInstancesWithColonCharacter.getResult();\n        assertThat(tasks).extracting(\"name\").containsExactly(\"step:5\");\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void searchSendTask() throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        processBuilder\n                .addSendTask(\"sendTask\", \"myMessage\", new ExpressionBuilder().createConstantStringExpression(\"p1\"))\n                .addConnector(\"wait2000ms\", \"testConnectorLongToExecute\", \"1.0.0\", ConnectorEvent.ON_FINISH)\n                .addInput(\"timeout\", new ExpressionBuilder().createConstantLongExpression(2000));\n        processBuilder.addAutomaticTask(\"autoTask\");\n        processBuilder.addUserTask(\"userTask\", ACTOR_NAME);\n        processBuilder.addTransition(\"autoTask\", \"sendTask\");\n        processBuilder.addTransition(\"sendTask\", \"userTask\");\n        final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorLongToExecute(processBuilder,\n                ACTOR_NAME, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        //        waitForFlowNodeInState(processInstance, \"sendTask\", TestStates.INITIALIZING, true);\n        waitForFlowNodeInExecutingState(processInstance, \"sendTask\", true);\n\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.sort(ArchivedActivityInstanceSearchDescriptor.NAME, Order.ASC);\n        final SearchResult<ActivityInstance> searchActivities = getProcessAPI().searchActivities(builder.done());\n        assertEquals(1, searchActivities.getCount());\n        final List<ActivityInstance> activities = searchActivities.getResult();\n        final ActivityInstance activity = activities.get(0);\n        assertEquals(\"sendTask\", activity.getName());\n        waitForUserTask(processInstance, \"userTask\");\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    public ProcessDefinition deployProcessWithActorAndTestConnectorLongToExecute(\n            final ProcessDefinitionBuilder processDefinitionBuilder,\n            final String actorName, final User user) throws BonitaException, IOException {\n        return deployAndEnableProcessWithActorAndConnectorAndParameter(processDefinitionBuilder, actorName, user, null,\n                \"TestConnectorLongToExecute.impl\",\n                TestConnectorLongToExecute.class, \"TestConnectorLongToExecute.jar\");\n    }\n\n    @Test\n    public void should_search_prevent_injection() throws Exception {\n\n        final SearchResult<FlowNodeInstance> result = getProcessAPI().searchFlowNodeInstances(\n                new SearchOptionsBuilder(0, 10)\n                        .filter(\"name\", \"d');DELETE * FROM TENANTS;select * from flow_node where name=toto\").done());\n\n        assertThat(result.getResult()).hasSize(0);\n\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/search/SearchCommentIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.*;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.comment.ArchivedComment;\nimport org.bonitasoft.engine.bpm.comment.ArchivedCommentsSearchDescriptor;\nimport org.bonitasoft.engine.bpm.comment.Comment;\nimport org.bonitasoft.engine.bpm.comment.SearchCommentsDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.test.BuildTestUtil;\nimport org.bonitasoft.engine.test.WaitUntil;\nimport org.junit.Test;\n\n/**\n * @author Celine Souchet\n */\npublic class SearchCommentIT extends TestWithUser {\n\n    @Test\n    public void searchArchiveComment() throws Exception {\n        // create a ProcessDefinition\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask(\"userTask1\", ACTOR_NAME)\n                .getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long userTask1Id = waitForUserTask(processInstance, \"userTask1\");\n        final String commentContent1 = \"commentContent1\";\n        getProcessAPI().addProcessComment(processInstance.getId(), commentContent1);\n        assignAndExecuteStep(userTask1Id, user);\n        waitForProcessToFinish(processInstance);\n        final SearchOptionsBuilder builder2 = new SearchOptionsBuilder(0, 10);\n        builder2.filter(ArchivedCommentsSearchDescriptor.PROCESS_INSTANCE_ID, processInstance.getId());\n        final SearchResult<ArchivedComment> archivedCommentsResult = getProcessAPI()\n                .searchArchivedComments(builder2.done());\n        final List<ArchivedComment> archivedComments = archivedCommentsResult.getResult();\n        final ArchivedComment archivedComment = archivedComments.get(0);\n        assertEquals(archivedComment, getProcessAPI().getArchivedComment(archivedComment.getId()));\n\n        // clean all data for test\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void searchArchivedComments() throws Exception {\n        searchArchivedComments(\"commentContent\", new SearchOptionsBuilder(0, 10));\n    }\n\n    @Test\n    public void searchArchivedCommentsWithApostrophe() throws Exception {\n        searchArchivedComments(\"comment'Content\", new SearchOptionsBuilder(0, 10).searchTerm(\"comment'\"));\n    }\n\n    private void searchArchivedComments(final String commentContent, final SearchOptionsBuilder builder)\n            throws Exception {\n        // create a ProcessDefinition\n        final String activityName = \"userTask1\";\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask(activityName, ACTOR_NAME)\n                .getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n\n        // create a ProcessInstance\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance, activityName);\n\n        // add an comment to ProcessInstance\n        getProcessAPI().addProcessComment(processInstance.getId(), commentContent);\n\n        // test the comment is added to ProcessInstance\n        final SearchOptionsBuilder builder0 = new SearchOptionsBuilder(0, 5);\n        builder0.filter(SearchCommentsDescriptor.PROCESS_INSTANCE_ID, processInstance.getId());\n        final SearchResult<Comment> searchResult0 = getProcessAPI().searchComments(builder0.done());\n        final List<Comment> commentList0 = searchResult0.getResult();\n        assertEquals(1, commentList0.size());\n\n        // get a ActivityInstance\n        final List<ActivityInstance> activityInstances = getProcessAPI().getActivities(processInstance.getId(), 0, 10);\n\n        // test before archive comment\n        final SearchOptionsBuilder builder3 = new SearchOptionsBuilder(0, 10);\n        builder3.filter(ArchivedCommentsSearchDescriptor.PROCESS_INSTANCE_ID, processInstance.getId());\n        SearchResult<ArchivedComment> archivedCommentsResult = getProcessAPI().searchArchivedComments(builder3.done());\n        assertEquals(0, archivedCommentsResult.getCount());\n\n        // Archive a ProcessInstance\n        for (final ActivityInstance activityInstance : activityInstances) {\n            final long activityInstanceId = activityInstance.getId();\n            getProcessAPI().setActivityStateById(activityInstanceId, 12);\n        }\n\n        // make sure archiving of the process instance is finished\n        final SearchOptionsBuilder searchOptions = BuildTestUtil.buildSearchOptions(processDefinition.getId(), 0, 1,\n                ArchivedProcessInstancesSearchDescriptor.ID, Order.ASC);\n        // search and check result ASC\n        assertTrue(\"Expected 1 ARCHIVED process instances not found\", new WaitUntil(500, 10000) {\n\n            @Override\n            protected boolean check() throws Exception {\n                return getProcessAPI().searchArchivedProcessInstances(searchOptions.done()).getCount() == 1;\n            }\n        }.waitUntil());\n\n        // test comment is archived\n        builder.filter(ArchivedCommentsSearchDescriptor.PROCESS_INSTANCE_ID, processInstance.getId());\n        archivedCommentsResult = getProcessAPI().searchArchivedComments(builder.done());\n        assertEquals(1, archivedCommentsResult.getCount());\n        final List<ArchivedComment> archivedComments = archivedCommentsResult.getResult();\n        final ArchivedComment archivedComment = archivedComments.get(0);\n        assertEquals(commentContent, archivedComment.getContent());\n\n        // clean all data for test\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void searchComments() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList(\"step1\", \"step2\"),\n                        Arrays.asList(true, true));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance pi1 = getProcessAPI().startProcess(processDefinition.getId());\n\n        final String commentContent1 = \"commentContent1\";\n        final String commentContent2 = \"commentContent2\";\n        final String commentContent3 = \"commentContent3\";\n        final String commentContent4 = \"content4\";\n        final String commentContent5 = \"content'5\";\n        getProcessAPI().addProcessCommentOnBehalfOfUser(pi1.getId(), commentContent1, pi1.getStartedBy());\n        getProcessAPI().addProcessComment(pi1.getId(), commentContent2);\n        getProcessAPI().addProcessComment(pi1.getId(), commentContent3);\n        getProcessAPI().addProcessComment(pi0.getId(), commentContent4);\n        getProcessAPI().addProcessComment(pi0.getId(), commentContent5);\n\n        final SearchOptionsBuilder builder0 = new SearchOptionsBuilder(0, 5);\n        builder0.filter(SearchCommentsDescriptor.PROCESS_INSTANCE_ID, pi0.getId());\n        builder0.sort(SearchCommentsDescriptor.POSTDATE, Order.ASC);\n\n        final SearchResult<Comment> searchResult0 = getProcessAPI().searchComments(builder0.done());\n        final List<Comment> commentList0 = searchResult0.getResult();\n        assertEquals(2, commentList0.size());\n\n        final SearchOptionsBuilder builder1 = new SearchOptionsBuilder(0, 5);\n        builder1.filter(SearchCommentsDescriptor.PROCESS_INSTANCE_ID, pi1.getId());\n        builder1.sort(SearchCommentsDescriptor.POSTDATE, Order.ASC);\n        final SearchResult<Comment> searchResult1 = getProcessAPI().searchComments(builder1.done());\n        final List<Comment> commentList1 = searchResult1.getResult();\n        assertEquals(3, commentList1.size());\n        assertEquals(commentContent1, commentList1.get(0).getContent());\n        assertThat(commentList1.get(0).getUserId()).isEqualTo(pi1.getStartedBy());\n        assertEquals(commentContent2, commentList1.get(1).getContent());\n        assertEquals(commentContent3, commentList1.get(2).getContent());\n\n        final SearchOptionsBuilder builder2 = new SearchOptionsBuilder(0, 5);\n        builder2.sort(SearchCommentsDescriptor.POSTDATE, Order.ASC);\n        builder2.searchTerm(\"comment\");\n        final SearchResult<Comment> searchResult2 = getProcessAPI().searchComments(builder2.done());\n        final List<Comment> commentList2 = searchResult2.getResult();\n        assertEquals(3, commentList2.size());\n\n        // Search with a apostrophe\n        final SearchOptionsBuilder builder4 = new SearchOptionsBuilder(0, 5);\n        builder4.sort(SearchCommentsDescriptor.POSTDATE, Order.ASC);\n        builder4.searchTerm(\"content'\");\n        final SearchResult<Comment> searchResult4 = getProcessAPI().searchComments(builder4.done());\n        final List<Comment> commentList4 = searchResult4.getResult();\n        assertEquals(1, commentList4.size());\n\n        final SearchOptionsBuilder builder3 = new SearchOptionsBuilder(0, 5);\n        builder3.filter(SearchCommentsDescriptor.USER_NAME, USERNAME);\n        builder3.sort(SearchCommentsDescriptor.POSTDATE, Order.ASC);\n        final SearchResult<Comment> searchResult3 = getProcessAPI().searchComments(builder3.done());\n        final List<Comment> commentList3 = searchResult3.getResult();\n        assertEquals(5, commentList3.size());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void searchCommentsForPartArchivedCases() throws Exception {\n        // create a ProcessDefinition\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask(\"userTask1\", ACTOR_NAME)\n                .getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n\n        // create a ProcessInstance\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance, \"userTask1\");\n        waitForUserTask(processInstance2, \"userTask1\");\n\n        // add an comment to ProcessInstance\n        final String commentContent1 = \"commentContent1\";\n        final String commentContent2 = \"commentContent2\";\n        getProcessAPI().addProcessComment(processInstance.getId(), commentContent1);\n        getProcessAPI().addProcessComment(processInstance2.getId(), commentContent2);\n\n        // get a ActivityInstance\n        final List<ActivityInstance> activityInstances = getProcessAPI().getActivities(processInstance.getId(), 0, 10);\n\n        // test before archive comment\n        final SearchOptionsBuilder builder3 = new SearchOptionsBuilder(0, 10);\n        SearchResult<Comment> commentsResult = getProcessAPI().searchComments(builder3.done());\n        assertEquals(2, commentsResult.getCount());\n\n        // Archive a ProcessInstance\n        for (final ActivityInstance activityInstance : activityInstances) {\n            final long activityInstanceId = activityInstance.getId();\n            getProcessAPI().setActivityStateById(activityInstanceId, 12);\n        }\n\n        // make sure archiving of the process instance is finished\n        final SearchOptionsBuilder searchOptions = BuildTestUtil.buildSearchOptions(processDefinition.getId(), 0, 10,\n                ArchivedProcessInstancesSearchDescriptor.ID, Order.ASC);\n        // search and check result ASC\n        assertTrue(\"Expected 1 ARCHIVED process instances not found\", new WaitUntil(500, 10000) {\n\n            @Override\n            protected boolean check() throws Exception {\n                return getProcessAPI().searchArchivedProcessInstances(searchOptions.done()).getCount() == 1;\n            }\n        }.waitUntil());\n\n        // test comment is archived\n        final SearchOptionsBuilder builder2 = new SearchOptionsBuilder(0, 10);\n        commentsResult = getProcessAPI().searchComments(builder2.done());\n        assertEquals(1, commentsResult.getCount());\n        final List<Comment> comments = commentsResult.getResult();\n        final Comment comment = comments.get(0);\n        assertEquals(commentContent2, comment.getContent());\n\n        // clean all data for test\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void searchCommentsInvolvingUser() throws Exception {\n        final String jackUserName = \"jack\";\n        final String johnUserName = \"john\";\n\n        final User jack = createUser(jackUserName, PASSWORD);\n        final User john = createUser(johnUserName, PASSWORD);\n\n        final SearchOptionsBuilder builderInitJack = new SearchOptionsBuilder(0, 10);\n        final SearchResult<Comment> resultinitjack = getProcessAPI().searchCommentsInvolvingUser(jack.getId(),\n                builderInitJack.done());\n        final long jackInitComments = resultinitjack.getCount();\n\n        final SearchOptionsBuilder builderInitJohn = new SearchOptionsBuilder(0, 10);\n        final SearchResult<Comment> resultInitJohn = getProcessAPI().searchCommentsInvolvingUser(john.getId(),\n                builderInitJohn.done());\n        final long johnInitComments = resultInitJohn.getCount();\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask(\"userTask1\", ACTOR_NAME)\n                .addUserTask(\"userTask2\", ACTOR_NAME)\n                .getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                jack);\n\n        final ProcessInstance instance1 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance instance2 = getProcessAPI().startProcess(john.getId(), processDefinition.getId());\n        waitForUserTaskAndAssignIt(instance1, \"userTask1\", jack);\n        waitForUserTaskAndAssignIt(instance1, \"userTask2\", jack);\n\n        logout();\n        loginOnDefaultTenantWith(jackUserName, PASSWORD);\n\n        final String commentContent1 = \"jack's comment Content1\";\n        final String commentContent2 = \"jack's comment Content3\";\n        final String commentContent3 = \"jack's comment Content3\";\n        getProcessAPI().addProcessComment(instance1.getId(), commentContent1);\n        getProcessAPI().addProcessComment(instance1.getId(), commentContent2);\n        getProcessAPI().addProcessComment(instance1.getId(), commentContent3);\n\n        logout();\n        loginOnDefaultTenantWith(johnUserName, PASSWORD);\n        final String commentContent4 = \"john's comment Content3\";\n        getProcessAPI().addProcessComment(instance2.getId(), commentContent4);\n\n        final SearchOptionsBuilder builder1 = new SearchOptionsBuilder(0, 10);\n        final SearchResult<Comment> result1 = getProcessAPI().searchCommentsInvolvingUser(jack.getId(),\n                builder1.done());\n        assertEquals(jackInitComments + 3, result1.getCount());\n        final List<Comment> commentList1 = result1.getResult();\n        assertNotNull(commentList1);\n        assertEquals(jackInitComments + 3, commentList1.size());\n\n        final SearchOptionsBuilder builder2 = new SearchOptionsBuilder(0, 10);\n        final SearchResult<Comment> result2 = getProcessAPI().searchCommentsInvolvingUser(john.getId(),\n                builder2.done());\n        assertEquals(johnInitComments + 1, result2.getCount());\n        final List<Comment> commentList2 = result2.getResult();\n        assertEquals(commentContent4, commentList2.get(0).getContent());\n\n        disableAndDeleteProcess(processDefinition);\n        deleteUsers(jack, john);\n    }\n\n    @Test\n    public void searchCommentsManagedBy() throws Exception {\n        // Create two new users John Jack and Steven, and set John managed by Steven, login with John.\n        final String johnUserName = \"john\";\n        final String jackUserName = \"jack\";\n        final String stevenUserName = \"steven\";\n\n        final User steven = createUser(stevenUserName, PASSWORD);\n        final User jack = createUser(jackUserName, PASSWORD);\n        final User john = createUser(johnUserName, PASSWORD, steven.getId());\n        final User jim = createUser(\"jim\", PASSWORD, steven.getId());\n\n        logout();\n        loginOnDefaultTenantWith(johnUserName, PASSWORD);\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask(\"userTask1\", ACTOR_NAME)\n                .addUserTask(\"userTask2\", ACTOR_NAME)\n                .getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                john);\n\n        final ProcessInstance pi1 = getProcessAPI().startProcess(steven.getId(), processDefinition.getId());\n        waitForUserTaskAndAssignIt(pi1, \"userTask1\", john);\n        waitForUserTaskAndAssignIt(pi1, \"userTask2\", john);\n\n        getProcessAPI().addProcessComment(pi1.getId(), \"John's comment Content1\");\n        getProcessAPI().addProcessComment(pi1.getId(), \"John's comment Content2\");\n        getProcessAPI().addProcessComment(pi1.getId(), \"John's comment Content3\");\n\n        logout();\n        loginOnDefaultTenantWith(jackUserName, PASSWORD);\n\n        final ProcessInstance pi2 = getProcessAPI().startProcess(jim.getId(), processDefinition.getId());\n        final String commentContent4 = \"Jack's comment Content4\";\n        getProcessAPI().addProcessComment(pi2.getId(), commentContent4);\n\n        logout();\n        loginOnDefaultTenantWith(stevenUserName, PASSWORD);\n\n        final ProcessInstance pi3 = getProcessAPI().startProcess(steven.getId(), processDefinition.getId());\n        waitForUserTaskAndAssignIt(pi3, \"userTask1\", john);\n        final String commentContent5 = \"Steven's comment Content5\";\n        getProcessAPI().addProcessComment(pi3.getId(), commentContent5);\n\n        final ProcessInstance pi4 = getProcessAPI().startProcess(steven.getId(), processDefinition.getId());\n        final String commentContent6 = \"Steven's comment Content6\";\n        getProcessAPI().addProcessComment(pi4.getId(), commentContent6);\n\n        logout();\n        loginWithTechnicalUser();\n\n        final SearchOptionsBuilder builder3 = new SearchOptionsBuilder(0, 10);\n        final SearchResult<Comment> searchResult3 = getProcessAPI().searchCommentsManagedBy(jack.getId(),\n                builder3.done());\n        final List<Comment> commentList3 = searchResult3.getResult();\n        assertEquals(0, commentList3.size());\n\n        final SearchOptionsBuilder builder2 = new SearchOptionsBuilder(0, 10);\n        builder2.sort(SearchCommentsDescriptor.POSTED_BY_ID, Order.ASC);\n        final SearchResult<Comment> searchResult2 = getProcessAPI().searchCommentsManagedBy(steven.getId(),\n                builder2.done());\n        final List<Comment> commentList2 = searchResult2.getResult();\n        assertEquals(5, searchResult2.getCount());\n        // Order by POSTED_BY_ID uses creation order:\n        assertEquals(Long.valueOf(steven.getId()), commentList2.get(0).getUserId());\n        assertEquals(Long.valueOf(jack.getId()), commentList2.get(1).getUserId());\n        assertEquals(Long.valueOf(john.getId()), commentList2.get(2).getUserId());\n        assertEquals(Long.valueOf(john.getId()), commentList2.get(3).getUserId());\n        assertEquals(Long.valueOf(john.getId()), commentList2.get(4).getUserId());\n        assertTrue(commentList2.get(0).getContent().contains(\"Content5\"));\n\n        disableAndDeleteProcess(processDefinition);\n        deleteUsers(john, jack, jim, steven);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/search/SearchProcessDefinitionIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.*;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.process.ActivationState;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoSearchDescriptor;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.test.BuildTestUtil;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Celine Souchet\n */\npublic class SearchProcessDefinitionIT extends TestWithUser {\n\n    private List<ProcessDefinition> enabledProcessDefinitions;\n\n    private List<ProcessDefinition> disabledProcessDefinitions;\n\n    @Override\n    @Before\n    public void before() throws Exception {\n        super.before();\n        enabledProcessDefinitions = new ArrayList<>(2);\n        disabledProcessDefinitions = new ArrayList<>(2);\n    }\n\n    @Override\n    @After\n    public void after() throws Exception {\n        disableAndDeleteProcess(enabledProcessDefinitions);\n        deleteProcess(disabledProcessDefinitions);\n        super.after();\n    }\n\n    @Test\n    public void searchRecentlyStartedByProcessDefinitions() throws Exception {\n        final long userId = user.getId();\n\n        // create process1\n        final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\n                        Arrays.asList(\"step1\", \"step2\"),\n                        Arrays.asList(true, true));\n        enabledProcessDefinitions.add(deployAndEnableProcessWithActor(designProcessDefinition1, ACTOR_NAME, user));\n        final ProcessInstance pi1 = getProcessAPI().startProcess(userId, enabledProcessDefinitions.get(0).getId());\n        waitForUserTask(pi1, \"step1\");\n\n        // create process2\n        final DesignProcessDefinition designProcessDefinition2 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process2\",\n                        PROCESS_VERSION,\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true));\n        enabledProcessDefinitions.add(deployAndEnableProcessWithActor(designProcessDefinition2, ACTOR_NAME, user));\n        final ProcessInstance pi2 = getProcessAPI().startProcess(userId, enabledProcessDefinitions.get(1).getId());\n        waitForUserTask(pi2, \"step1\");\n\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 5);\n        builder.sort(ProcessDeploymentInfoSearchDescriptor.ID, Order.ASC);\n        final SearchResult<ProcessDeploymentInfo> searchRes = getProcessAPI()\n                .searchProcessDeploymentInfosStartedBy(userId, builder.done());\n        assertEquals(2, searchRes.getCount());\n        final List<ProcessDeploymentInfo> processDeploymentInfos = searchRes.getResult();\n        assertEquals(\"The first process definition must be \" + designProcessDefinition1.getName(),\n                enabledProcessDefinitions.get(0).getId(),\n                processDeploymentInfos.get(0).getProcessId());\n        assertEquals(\"The second process definition must be \" + designProcessDefinition2.getName(),\n                enabledProcessDefinitions.get(1).getId(),\n                processDeploymentInfos.get(1).getProcessId());\n\n        // test search in order\n        final SearchOptionsBuilder builder1 = new SearchOptionsBuilder(0, 5);\n        builder1.sort(ProcessDeploymentInfoSearchDescriptor.ID, Order.DESC);\n        final SearchResult<ProcessDeploymentInfo> searchRes1 = getProcessAPI()\n                .searchProcessDeploymentInfosStartedBy(userId, builder1.done());\n        assertEquals(2, searchRes1.getCount());\n        final List<ProcessDeploymentInfo> processDeploymentInfos1 = searchRes1.getResult();\n        assertNotNull(processDeploymentInfos1);\n        assertEquals(2, processDeploymentInfos1.size());\n        assertEquals(enabledProcessDefinitions.get(1).getId(), processDeploymentInfos1.get(0).getProcessId());\n        assertEquals(enabledProcessDefinitions.get(0).getId(), processDeploymentInfos1.get(1).getProcessId());\n\n        // test term\n        final SearchOptionsBuilder builder2 = new SearchOptionsBuilder(0, 5);\n        builder2.searchTerm(\"My_Process2\"); // use name as term\n        final SearchResult<ProcessDeploymentInfo> searchRes2 = getProcessAPI()\n                .searchProcessDeploymentInfosStartedBy(userId, builder2.done());\n        assertEquals(1, searchRes2.getCount());\n        final List<ProcessDeploymentInfo> processDeploymentInfos2 = searchRes2.getResult();\n        assertNotNull(processDeploymentInfos2);\n        assertEquals(1, processDeploymentInfos2.size());\n        assertEquals(enabledProcessDefinitions.get(1).getId(), processDeploymentInfos2.get(0).getProcessId());\n\n        // test filter\n        final SearchOptionsBuilder builder3 = new SearchOptionsBuilder(0, 5);\n        builder3.filter(ProcessDeploymentInfoSearchDescriptor.NAME, \"My_Process2\");\n        final SearchResult<ProcessDeploymentInfo> searchRes3 = getProcessAPI()\n                .searchProcessDeploymentInfosStartedBy(userId, builder3.done());\n        assertEquals(1, searchRes3.getCount());\n        final List<ProcessDeploymentInfo> processDeploymentInfos3 = searchRes3.getResult();\n        assertNotNull(processDeploymentInfos3);\n        assertEquals(1, processDeploymentInfos3.size());\n        assertEquals(enabledProcessDefinitions.get(1).getId(), processDeploymentInfos3.get(0).getProcessId());\n    }\n\n    @Test\n    public void searchProcessDefinitions() throws Exception {\n        // create 2 process\n        createNbProcessDefinitionWithTwoHumanStepsAndDeployWithActor(2, user);\n\n        // Get all process definitions, reverse order:\n        SearchOptionsBuilder optsBuilder = new SearchOptionsBuilder(0, 5);\n        optsBuilder.sort(ProcessDeploymentInfoSearchDescriptor.DEPLOYMENT_DATE, Order.DESC);\n        final SearchResult<ProcessDeploymentInfo> searchRes0 = getProcessAPI()\n                .searchProcessDeploymentInfos(optsBuilder.done());\n        assertEquals(2, searchRes0.getCount());\n        // reverse order:\n        assertEquals(enabledProcessDefinitions.get(0).getId(), searchRes0.getResult().get(1).getProcessId());\n        assertEquals(enabledProcessDefinitions.get(1).getId(), searchRes0.getResult().get(0).getProcessId());\n\n        // partial term search\n        optsBuilder = new SearchOptionsBuilder(0, 5);\n        optsBuilder.searchTerm(PROCESS_NAME); // use process def as term\n        final SearchResult<ProcessDeploymentInfo> searchRes2 = getProcessAPI()\n                .searchProcessDeploymentInfos(optsBuilder.done());\n        assertEquals(2, searchRes2.getCount());\n\n        // partial term search\n        optsBuilder = new SearchOptionsBuilder(0, 5);\n        optsBuilder.searchTerm(\"1.01\"); // use process def as term\n        final SearchResult<ProcessDeploymentInfo> searchRes3 = getProcessAPI()\n                .searchProcessDeploymentInfos(optsBuilder.done());\n        assertEquals(1, searchRes3.getCount());\n    }\n\n    @Test\n    public void searchProcessDefinitionsSorted() throws Exception {\n        // create 5 process\n        createNbProcessDefinitionWithTwoHumanStepsAndDeployWithActor(5, user);\n\n        // Get all process definitions, reverse order:\n        SearchOptionsBuilder optsBuilder = new SearchOptionsBuilder(0, 10);\n        optsBuilder.sort(ProcessDeploymentInfoSearchDescriptor.VERSION, Order.ASC);\n        SearchResult<ProcessDeploymentInfo> searchRes0 = getProcessAPI()\n                .searchProcessDeploymentInfos(optsBuilder.done());\n        assertEquals(5, searchRes0.getCount());\n        assertThat(searchRes0.getResult()).extracting(\"version\").containsExactly(\"1.00\", \"1.01\", \"1.02\", \"1.03\",\n                \"1.04\");\n        optsBuilder = new SearchOptionsBuilder(0, 10);\n        optsBuilder.sort(ProcessDeploymentInfoSearchDescriptor.VERSION, Order.DESC);\n        searchRes0 = getProcessAPI().searchProcessDeploymentInfos(optsBuilder.done());\n        assertEquals(5, searchRes0.getCount());\n        assertThat(searchRes0.getResult()).extracting(\"version\").containsExactly(\"1.04\", \"1.03\", \"1.02\", \"1.01\",\n                \"1.00\");\n    }\n\n    @Test\n    public void searchProcessDefinitionsFilter() throws Exception {\n        // create 5 enabled process\n        createNbProcessDefinitionWithTwoHumanStepsAndDeployWithActor(5, user);\n\n        // create 1 disabled process\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"plop\",\n                        PROCESS_VERSION, Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true));\n        disabledProcessDefinitions.add(getProcessAPI().deploy(designProcessDefinition));\n\n        // Filter on version\n        SearchOptionsBuilder optsBuilder = new SearchOptionsBuilder(0, 10);\n        optsBuilder.sort(ProcessDeploymentInfoSearchDescriptor.VERSION, Order.ASC);\n        optsBuilder.filter(ProcessDeploymentInfoSearchDescriptor.VERSION, \"1.03\");\n        SearchResult<ProcessDeploymentInfo> searchResult = getProcessAPI()\n                .searchProcessDeploymentInfos(optsBuilder.done());\n        assertEquals(1, searchResult.getCount());\n        assertThat(searchResult.getResult()).extracting(\"version\").containsExactly(\"1.03\");\n\n        // Filter on activation state\n        optsBuilder = new SearchOptionsBuilder(0, 10);\n        optsBuilder.sort(ProcessDeploymentInfoSearchDescriptor.VERSION, Order.ASC);\n        optsBuilder.filter(ProcessDeploymentInfoSearchDescriptor.ACTIVATION_STATE, ActivationState.ENABLED.name());\n        searchResult = getProcessAPI().searchProcessDeploymentInfos(optsBuilder.done());\n        assertEquals(5, searchResult.getCount());\n        assertFalse(\"Don't have to contain the process definition \\\"plop\\\" !!\",\n                searchResult.getResult().contains(enabledProcessDefinitions.get(4)));\n    }\n\n    @Test\n    public void searchProcessDefinitionsWithApostropheOnProcessName() throws Exception {\n        searchProcessDefinitions(\"process'Name\", PROCESS_VERSION);\n    }\n\n    @Test\n    public void searchProcessDefinitionsWithApostropheOnProcessVersion() throws Exception {\n        searchProcessDefinitions(PROCESS_NAME, \"process'VERSION\");\n    }\n\n    private void searchProcessDefinitions(final String processName, final String processVersion) throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(processName,\n                processVersion);\n        processBuilder.addActor(ACTOR_NAME);\n        enabledProcessDefinitions.add(deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user));\n\n        // Get all process definitions, reverse order:\n        final SearchOptionsBuilder optsBuilder = new SearchOptionsBuilder(0, 10);\n        optsBuilder.searchTerm(\"process'\");\n        final SearchResult<ProcessDeploymentInfo> searchResult = getProcessAPI()\n                .searchProcessDeploymentInfos(optsBuilder.done());\n        assertEquals(1, searchResult.getCount());\n        assertEquals(processName, searchResult.getResult().get(0).getName());\n    }\n\n    @Test\n    public void searchProcessDefinitionsSearchTermOnVersion() throws Exception {\n        // create 5 process\n        createNbProcessDefinitionWithTwoHumanStepsAndDeployWithActor(5, user);\n\n        // Get all process definitions, reverse order:\n        final SearchOptionsBuilder optsBuilder = new SearchOptionsBuilder(0, 10);\n        optsBuilder.sort(ProcessDeploymentInfoSearchDescriptor.VERSION, Order.ASC);\n        optsBuilder.searchTerm(\"1.03\");\n        final SearchResult<ProcessDeploymentInfo> searchRes0 = getProcessAPI()\n                .searchProcessDeploymentInfos(optsBuilder.done());\n        assertEquals(1, searchRes0.getCount());\n        assertThat(searchRes0.getResult()).extracting(\"version\").containsExactly(\"1.03\");\n    }\n\n    private void createNbProcessDefinitionWithTwoHumanStepsAndDeployWithActor(final int nbProcess, final User user)\n            throws Exception {\n        for (int i = 0; i < nbProcess; i++) {\n            String processName = PROCESS_NAME;\n            if (i >= 0 && i < 10) {\n                processName += \"0\";\n            }\n            final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                    .buildProcessDefinitionWithHumanAndAutomaticSteps(processName + i,\n                            PROCESS_VERSION + i, Arrays.asList(\"step1_\" + i, \"step2_\" + i), Arrays.asList(true, true));\n            enabledProcessDefinitions.add(deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user));\n        }\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/search/SearchProcessDeploymentInfosCanBeStartedByIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport static org.junit.Assert.assertEquals;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.TestWithTechnicalUser;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.category.Category;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoSearchDescriptor;\nimport org.bonitasoft.engine.identity.Group;\nimport org.bonitasoft.engine.identity.Role;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.identity.UserMembership;\nimport org.bonitasoft.engine.test.BuildTestUtil;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Celine Souchet\n */\npublic class SearchProcessDeploymentInfosCanBeStartedByIT extends TestWithTechnicalUser {\n\n    private List<ProcessDefinition> enabledProcessDefinitions;\n\n    private List<ProcessDefinition> disabledProcessDefinitions;\n\n    private List<User> users = null;\n\n    private List<Category> categories = null;\n\n    private List<Group> groups = null;\n\n    private List<Role> roles = null;\n\n    private List<UserMembership> userMemberships = null;\n\n    @Override\n    @Before\n    public void before() throws Exception {\n        super.before();\n\n        // create users\n        users = new ArrayList<User>(2);\n        users.add(createUser(\"chicobento\", \"bpm\"));\n        users.add(createUser(\"cebolinha\", \"bpm\"));\n        users.add(createUser(\"cascao\", \"bpm\"));\n        users.add(createUser(\"magali\", \"bpm\"));\n        users.add(createUser(\"monica\", \"bpm\"));\n        users.add(createUser(\"dorinha\", \"bpm\"));\n\n        // create groups\n        groups = new ArrayList<Group>(2);\n        groups.add(createGroup(\"group1\"));\n        groups.add(createGroup(\"group2\"));\n\n        // create roles\n        roles = new ArrayList<Role>(2);\n        roles.add(createRole(\"role1\"));\n        roles.add(createRole(\"role2\"));\n\n        // create user memberships\n        userMemberships = new ArrayList<UserMembership>(3);\n        userMemberships.add(\n                getIdentityAPI().addUserMembership(users.get(3).getId(), groups.get(0).getId(), roles.get(0).getId()));\n        userMemberships.add(\n                getIdentityAPI().addUserMembership(users.get(4).getId(), groups.get(0).getId(), roles.get(1).getId()));\n        userMemberships.add(\n                getIdentityAPI().addUserMembership(users.get(5).getId(), groups.get(1).getId(), roles.get(0).getId()));\n\n        // create processes\n        enabledProcessDefinitions = new ArrayList<ProcessDefinition>(4);\n        disabledProcessDefinitions = new ArrayList<ProcessDefinition>(1);\n        createProcessesDefForSearchProcessUserCanStart();\n\n        categories = new ArrayList<Category>(3);\n        categories.add(getProcessAPI().createCategory(\"category1\", \"the first known category\"));\n        categories.add(getProcessAPI().createCategory(\"category2\", \"the second known category\"));\n        categories.add(getProcessAPI().createCategory(\"category3\", \"the third known category\"));\n        getProcessAPI().addProcessDefinitionToCategory(categories.get(0).getId(),\n                enabledProcessDefinitions.get(2).getId());\n        getProcessAPI().addProcessDefinitionToCategory(categories.get(1).getId(),\n                enabledProcessDefinitions.get(2).getId());\n        getProcessAPI().addProcessDefinitionToCategory(categories.get(1).getId(),\n                enabledProcessDefinitions.get(1).getId());\n        getProcessAPI().addProcessDefinitionToCategory(categories.get(2).getId(),\n                enabledProcessDefinitions.get(3).getId());\n    }\n\n    @Override\n    @After\n    public void after() throws Exception {\n        disableAndDeleteProcess(enabledProcessDefinitions);\n        deleteProcess(disabledProcessDefinitions);\n        deleteCategories(categories);\n        deleteUserMemberships(userMemberships);\n        deleteUsers(users);\n        deleteGroups(groups);\n        deleteRoles(roles);\n        super.after();\n    }\n\n    @Test\n    public void searchProcessDefinitionsUserCanStart() throws Exception {\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 5)\n                .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC);\n        SearchResult<ProcessDeploymentInfo> searchRes = getProcessAPI()\n                .searchProcessDeploymentInfosCanBeStartedBy(users.get(0).getId(), searchOptionsBuilder.done());\n        assertEquals(1, searchRes.getCount());\n        assertEquals(enabledProcessDefinitions.get(0).getName(), searchRes.getResult().get(0).getName());\n\n        searchRes = getProcessAPI().searchProcessDeploymentInfosCanBeStartedBy(users.get(1).getId(),\n                searchOptionsBuilder.done());\n        assertEquals(2, searchRes.getCount());\n        assertEquals(enabledProcessDefinitions.get(1).getName(), searchRes.getResult().get(0).getName());\n        assertEquals(enabledProcessDefinitions.get(2).getName(), searchRes.getResult().get(1).getName());\n\n        // user associated to a process without actor initiator\n        searchRes = getProcessAPI().searchProcessDeploymentInfosCanBeStartedBy(users.get(2).getId(),\n                searchOptionsBuilder.done());\n        assertEquals(0, searchRes.getCount());\n    }\n\n    @Test\n    public void searchProcessDefinitionsUserCanStartFromGroup() throws Exception {\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 5)\n                .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC);\n        final SearchResult<ProcessDeploymentInfo> searchRes = getProcessAPI()\n                .searchProcessDeploymentInfosCanBeStartedBy(users.get(4).getId(), searchOptionsBuilder.done());\n        assertEquals(1, searchRes.getCount());\n        assertEquals(enabledProcessDefinitions.get(4).getName(), searchRes.getResult().get(0).getName());\n    }\n\n    @Test\n    public void searchProcessDefinitionsUserCanStartFromRole() throws Exception {\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 5)\n                .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC);\n        final SearchResult<ProcessDeploymentInfo> searchRes = getProcessAPI()\n                .searchProcessDeploymentInfosCanBeStartedBy(users.get(5).getId(), searchOptionsBuilder.done());\n        assertEquals(1, searchRes.getCount());\n        assertEquals(enabledProcessDefinitions.get(5).getName(), searchRes.getResult().get(0).getName());\n    }\n\n    @Test\n    public void searchProcessDefinitionsUserCanStartFromRoleAndGroup() throws Exception {\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 5)\n                .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC);\n        final SearchResult<ProcessDeploymentInfo> searchRes = getProcessAPI()\n                .searchProcessDeploymentInfosCanBeStartedBy(users.get(3).getId(), searchOptionsBuilder.done());\n        assertEquals(3, searchRes.getCount());\n        assertEquals(enabledProcessDefinitions.get(4).getName(), searchRes.getResult().get(0).getName()); // from group\n        assertEquals(enabledProcessDefinitions.get(5).getName(), searchRes.getResult().get(1).getName()); // from role\n        assertEquals(enabledProcessDefinitions.get(6).getName(), searchRes.getResult().get(2).getName()); // from role and group\n    }\n\n    @Test\n    public void searchProcessDefinitionsUserCanStartWithSearchTerm() throws Exception {\n        // test term\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 5)\n                .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC);\n        searchOptionsBuilder.searchTerm(\"My_Process2\"); // use name as term\n        final SearchResult<ProcessDeploymentInfo> searchRes = getProcessAPI()\n                .searchProcessDeploymentInfosCanBeStartedBy(users.get(1).getId(), searchOptionsBuilder.done());\n        assertEquals(1, searchRes.getCount());\n        assertEquals(enabledProcessDefinitions.get(1).getId(), searchRes.getResult().get(0).getProcessId());\n    }\n\n    @Test\n    public void searchProcessDefinitionsUserCanStartWithFilter() throws Exception {\n        // test filter on process name\n        SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 5)\n                .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC);\n        searchOptionsBuilder.filter(ProcessDeploymentInfoSearchDescriptor.NAME, \"My_Process2\");\n        SearchResult<ProcessDeploymentInfo> searchRes = getProcessAPI()\n                .searchProcessDeploymentInfosCanBeStartedBy(users.get(1).getId(), searchOptionsBuilder.done());\n        assertEquals(1, searchRes.getCount());\n        assertEquals(enabledProcessDefinitions.get(1).getId(), searchRes.getResult().get(0).getProcessId());\n\n        // test filter category\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 5).sort(ProcessDeploymentInfoSearchDescriptor.NAME,\n                Order.ASC);\n        searchOptionsBuilder.filter(ProcessDeploymentInfoSearchDescriptor.CATEGORY_ID, categories.get(0).getId());\n        searchRes = getProcessAPI().searchProcessDeploymentInfosCanBeStartedBy(users.get(1).getId(),\n                searchOptionsBuilder.done());\n        assertEquals(1, searchRes.getCount());\n        assertEquals(enabledProcessDefinitions.get(2).getId(), searchRes.getResult().get(0).getProcessId());\n    }\n\n    private void createProcessesDefForSearchProcessUserCanStart() throws Exception {\n        final String actor1 = ACTOR_NAME;\n        final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process1\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor1, true);\n        final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition1, actor1,\n                users.get(0));\n        enabledProcessDefinitions.add(processDefinition1);\n\n        // create process2\n        final String actor2 = \"Actor2\";\n        final DesignProcessDefinition designProcessDefinition2 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process2\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor2, true);\n        final ProcessDefinition processDefinition2 = deployAndEnableProcessWithActor(designProcessDefinition2, actor2,\n                users.get(1));\n        enabledProcessDefinitions.add(processDefinition2);\n\n        final DesignProcessDefinition designProcessDefinition3 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process3\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor2, true);\n        final ProcessDefinition processDefinition3 = deployAndEnableProcessWithActor(designProcessDefinition3, actor2,\n                users.get(1));\n        enabledProcessDefinitions.add(processDefinition3);\n\n        // process not enabled\n        final DesignProcessDefinition designProcessDefinition4 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process4\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor2, true);\n        final ProcessDefinition processDefinition4 = getProcessAPI().deploy(\n                new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(designProcessDefinition4)\n                        .done());\n        getProcessAPI().addUserToActor(actor2, processDefinition4, users.get(1).getId());\n        disabledProcessDefinitions.add(processDefinition4);\n\n        // process without actor initiator\n        final DesignProcessDefinition designProcessDefinition5 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process5\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor2, false);\n        final ProcessDefinition processDefinition5 = deployAndEnableProcessWithActor(designProcessDefinition5, actor2,\n                users.get(2));\n        enabledProcessDefinitions.add(processDefinition5);\n\n        // actor initiator is a group\n        final DesignProcessDefinition designProcessDefinition6 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process6\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor2, true);\n        final ProcessDefinition processDefinition6 = deployAndEnableProcessWithActor(designProcessDefinition6, actor2,\n                groups.get(0));\n        enabledProcessDefinitions.add(processDefinition6);\n\n        // actor initiator is a role\n        final DesignProcessDefinition designProcessDefinition7 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process7\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor2, true);\n        final ProcessDefinition processDefinition7 = deployAndEnableProcessWithActor(designProcessDefinition7, actor2,\n                roles.get(0));\n        enabledProcessDefinitions.add(processDefinition7);\n\n        // actor initiator is a membership\n        final DesignProcessDefinition designProcessDefinition8 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process8\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor2, true);\n        final ProcessDefinition processDefinition8 = deployAndEnableProcessWithActor(designProcessDefinition8, actor2,\n                roles.get(0), groups.get(0));\n        enabledProcessDefinitions.add(processDefinition8);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/search/SearchProcessDeploymentInfosCanBeStartedByUsersManagedByIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport static org.junit.Assert.assertEquals;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.TestWithTechnicalUser;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoSearchDescriptor;\nimport org.bonitasoft.engine.identity.Group;\nimport org.bonitasoft.engine.identity.Role;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.identity.UserMembership;\nimport org.bonitasoft.engine.test.BuildTestUtil;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Celine Souchet\n */\npublic class SearchProcessDeploymentInfosCanBeStartedByUsersManagedByIT extends TestWithTechnicalUser {\n\n    private List<ProcessDefinition> enabledProcessDefinitions;\n\n    private List<ProcessDefinition> disabledProcessDefinitions;\n\n    private List<User> users = null;\n\n    private List<Group> groups = null;\n\n    private List<Role> roles = null;\n\n    private List<UserMembership> userMemberships = null;\n\n    @Override\n    @Before\n    public void before() throws Exception {\n        super.before();\n        // create users\n        users = new ArrayList<User>(10);\n        users.add(createUser(\"magali\", \"bpm\"));\n        users.add(createUser(\"monica\", \"bpm\"));\n        users.add(createUser(\"manager\", \"bpm\"));\n        users.add(createUser(\"chicobento\", users.get(0).getId()));\n        users.add(createUser(\"chicobento2\", users.get(2).getId()));\n        users.add(createUser(\"cebolinha\", users.get(0).getId()));\n        users.add(createUser(\"cascao\", users.get(1).getId()));\n        users.add(createUser(\"dorinha\", users.get(0).getId()));\n        users.add(createUser(\"dorinha2\", users.get(0).getId()));\n        users.add(createUser(\"dorinha3\", users.get(1).getId()));\n\n        // create groups\n        groups = new ArrayList<Group>(2);\n        groups.add(createGroup(\"groups.get(0)\"));\n        groups.add(createGroup(\"groups.get(1)\"));\n\n        // create roles\n        roles = new ArrayList<Role>(2);\n        roles.add(createRole(\"roles.get(0)\"));\n        roles.add(createRole(\"roles.get(1)\"));\n\n        // create user memberships\n        userMemberships = new ArrayList<UserMembership>(4);\n        userMemberships.add(\n                getIdentityAPI().addUserMembership(users.get(7).getId(), groups.get(0).getId(), roles.get(0).getId()));\n        userMemberships.add(\n                getIdentityAPI().addUserMembership(users.get(8).getId(), groups.get(0).getId(), roles.get(1).getId()));\n        userMemberships.add(\n                getIdentityAPI().addUserMembership(users.get(9).getId(), groups.get(1).getId(), roles.get(0).getId()));\n        userMemberships.add(\n                getIdentityAPI().addUserMembership(users.get(4).getId(), groups.get(0).getId(), roles.get(0).getId()));\n\n        // create processes\n        enabledProcessDefinitions = new ArrayList<ProcessDefinition>(4);\n        disabledProcessDefinitions = new ArrayList<ProcessDefinition>(1);\n        createProcessesDefForSearchProcessUserCanStart();\n    }\n\n    @Override\n    @After\n    public void after() throws Exception {\n        disableAndDeleteProcess(enabledProcessDefinitions);\n        deleteProcess(disabledProcessDefinitions);\n        deleteUserMemberships(userMemberships);\n        deleteUsers(users);\n        deleteGroups(groups);\n        deleteRoles(roles);\n        super.after();\n    }\n\n    @Test\n    public void searchProcessDefinitionsUsersManagedByCanStart() throws Exception {\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10)\n                .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC);\n        SearchResult<ProcessDeploymentInfo> searchRes = getProcessAPI()\n                .searchProcessDeploymentInfosCanBeStartedByUsersManagedBy(users.get(8).getId(),\n                        searchOptionsBuilder.done());\n        assertEquals(1, searchRes.getCount());\n        List<ProcessDeploymentInfo> result = searchRes.getResult();\n        assertEquals(enabledProcessDefinitions.get(4).getName(), result.get(0).getName());\n\n        searchRes = getProcessAPI().searchProcessDeploymentInfosCanBeStartedByUsersManagedBy(users.get(0).getId(),\n                searchOptionsBuilder.done());\n        assertEquals(4, searchRes.getCount());\n        result = searchRes.getResult();\n        assertEquals(enabledProcessDefinitions.get(0).getName(), result.get(0).getName());\n        assertEquals(enabledProcessDefinitions.get(4).getName(), result.get(1).getName());\n        assertEquals(enabledProcessDefinitions.get(5).getName(), result.get(2).getName());\n        assertEquals(enabledProcessDefinitions.get(6).getName(), result.get(3).getName());\n\n        // user associated to a process without actor initiator\n        searchRes = getProcessAPI().searchProcessDeploymentInfosCanBeStartedByUsersManagedBy(users.get(6).getId(),\n                searchOptionsBuilder.done());\n        assertEquals(0, searchRes.getCount());\n    }\n\n    @Test\n    public void searchProcessDefinitionsUsersManagedByCanStartFromRoleAndGroup() throws Exception {\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10)\n                .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC);\n        final SearchResult<ProcessDeploymentInfo> searchRes = getProcessAPI()\n                .searchProcessDeploymentInfosCanBeStartedByUsersManagedBy(users.get(2).getId(),\n                        searchOptionsBuilder.done());\n        assertEquals(3, searchRes.getCount());\n        assertEquals(enabledProcessDefinitions.get(4).getName(), searchRes.getResult().get(0).getName()); // from group\n        assertEquals(enabledProcessDefinitions.get(5).getName(), searchRes.getResult().get(1).getName()); // from role\n        assertEquals(enabledProcessDefinitions.get(6).getName(), searchRes.getResult().get(2).getName()); // from role and group\n    }\n\n    @Test\n    public void searchProcessDefinitionsUsersManagedByCanStartWithSearchTerm() throws Exception {\n        // test term\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10)\n                .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC);\n        searchOptionsBuilder.searchTerm(\"My_Process7\"); // use name as term\n        final SearchResult<ProcessDeploymentInfo> searchRes = getProcessAPI()\n                .searchProcessDeploymentInfosCanBeStartedByUsersManagedBy(users.get(4).getId(),\n                        searchOptionsBuilder.done());\n        assertEquals(1, searchRes.getCount());\n        assertEquals(enabledProcessDefinitions.get(5).getId(), searchRes.getResult().get(0).getProcessId());\n    }\n\n    @Test\n    public void searchProcessDefinitionsUsersManagedByCanStartWithFilter() throws Exception {\n        // test filter on process name\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10)\n                .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC);\n        searchOptionsBuilder.filter(ProcessDeploymentInfoSearchDescriptor.NAME, \"My_Process7\");\n        final SearchResult<ProcessDeploymentInfo> searchRes = getProcessAPI()\n                .searchProcessDeploymentInfosCanBeStartedByUsersManagedBy(users.get(4).getId(),\n                        searchOptionsBuilder.done());\n        assertEquals(1, searchRes.getCount());\n        assertEquals(enabledProcessDefinitions.get(5).getId(), searchRes.getResult().get(0).getProcessId());\n    }\n\n    private void createProcessesDefForSearchProcessUserCanStart() throws Exception {\n        final String actor1 = ACTOR_NAME;\n        final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process1\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor1, true);\n        final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition1, actor1,\n                users.get(3));\n        enabledProcessDefinitions.add(processDefinition1);\n\n        // create process2\n        final String actor2 = \"Actor2\";\n        final DesignProcessDefinition designProcessDefinition2 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process2\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor2, true);\n        final ProcessDefinition processDefinition2 = deployAndEnableProcessWithActor(designProcessDefinition2, actor2,\n                users.get(1));\n        enabledProcessDefinitions.add(processDefinition2);\n\n        final DesignProcessDefinition designProcessDefinition3 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process3\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor2, true);\n        final ProcessDefinition processDefinition3 = deployAndEnableProcessWithActor(designProcessDefinition3, actor2,\n                users.get(1));\n        enabledProcessDefinitions.add(processDefinition3);\n\n        // process not enabled\n        final DesignProcessDefinition designProcessDefinition4 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process4\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor2, true);\n        final ProcessDefinition processDefinition4 = getProcessAPI().deploy(\n                new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(designProcessDefinition4)\n                        .done());\n        getProcessAPI().addUserToActor(actor2, processDefinition4, users.get(1).getId());\n        disabledProcessDefinitions.add(processDefinition4);\n\n        // process without actor initiator\n        final DesignProcessDefinition designProcessDefinition5 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process5\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor2, false);\n        final ProcessDefinition processDefinition5 = deployAndEnableProcessWithActor(designProcessDefinition5, actor2,\n                users.get(2));\n        enabledProcessDefinitions.add(processDefinition5);\n\n        // actor initiator is a group\n        final DesignProcessDefinition designProcessDefinition6 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process6\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor2, true);\n        final ProcessDefinition processDefinition6 = deployAndEnableProcessWithActor(designProcessDefinition6, actor2,\n                groups.get(0));\n        enabledProcessDefinitions.add(processDefinition6);\n\n        // actor initiator is a role\n        final DesignProcessDefinition designProcessDefinition7 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process7\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor2, true);\n        final ProcessDefinition processDefinition7 = deployAndEnableProcessWithActor(designProcessDefinition7, actor2,\n                roles.get(0));\n        enabledProcessDefinitions.add(processDefinition7);\n\n        // actor initiator is a membership\n        final DesignProcessDefinition designProcessDefinition8 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process8\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor2, true);\n        final ProcessDefinition processDefinition8 = deployAndEnableProcessWithActor(designProcessDefinition8, actor2,\n                roles.get(0), groups.get(0));\n        enabledProcessDefinitions.add(processDefinition8);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/search/SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksForIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport static org.junit.Assert.assertEquals;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.TestWithTechnicalUser;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoSearchDescriptor;\nimport org.bonitasoft.engine.identity.Group;\nimport org.bonitasoft.engine.identity.Role;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.identity.UserMembership;\nimport org.bonitasoft.engine.test.BuildTestUtil;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Celine Souchet\n */\npublic class SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksForIT extends TestWithTechnicalUser {\n\n    private List<ProcessDefinition> enabledProcessDefinitions;\n\n    private List<User> users = null;\n\n    private List<Group> groups = null;\n\n    private List<Role> roles = null;\n\n    private List<UserMembership> userMemberships = null;\n\n    @Override\n    @After\n    public void after() throws Exception {\n        disableAndDeleteProcess(enabledProcessDefinitions);\n        deleteUserMemberships(userMemberships);\n        deleteUsers(users);\n        deleteGroups(groups);\n        deleteRoles(roles);\n        super.after();\n    }\n\n    @Override\n    @Before\n    public void before() throws Exception {\n        super.before();\n        // create users\n        users = new ArrayList<>(2);\n        users.add(createUser(\"chicobento\", \"bpm\"));\n        users.add(createUser(\"cebolinha\", \"bpm\"));\n        users.add(createUser(\"cascao\", \"bpm\"));\n\n        // create groups\n        groups = new ArrayList<>(2);\n        groups.add(createGroup(\"group1\"));\n        groups.add(createGroup(\"group2\"));\n\n        // create roles\n        roles = new ArrayList<>(2);\n        roles.add(createRole(\"role1\"));\n        roles.add(createRole(\"role2\"));\n\n        // create user memberships\n        userMemberships = new ArrayList<>(3);\n        userMemberships.add(\n                getIdentityAPI().addUserMembership(users.get(2).getId(), groups.get(0).getId(), roles.get(0).getId()));\n        userMemberships.add(\n                getIdentityAPI().addUserMembership(users.get(0).getId(), groups.get(0).getId(), roles.get(1).getId()));\n        userMemberships.add(\n                getIdentityAPI().addUserMembership(users.get(1).getId(), groups.get(1).getId(), roles.get(0).getId()));\n\n        // create processes\n        enabledProcessDefinitions = new ArrayList<>(4);\n        createProcessesDefinitions();\n    }\n\n    @Test\n    public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor() throws Exception {\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 5)\n                .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC);\n        SearchResult<ProcessDeploymentInfo> searchRes = getProcessAPI()\n                .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(\n                        users.get(0).getId(), searchOptionsBuilder.done());\n        assertEquals(2, searchRes.getCount());\n        assertEquals(enabledProcessDefinitions.get(0).getName(), searchRes.getResult().get(0).getName());\n\n        searchRes = getProcessAPI().searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(users.get(1).getId(),\n                searchOptionsBuilder.done());\n        assertEquals(3, searchRes.getCount());\n        assertEquals(enabledProcessDefinitions.get(1).getName(), searchRes.getResult().get(0).getName());\n        assertEquals(enabledProcessDefinitions.get(2).getName(), searchRes.getResult().get(1).getName());\n    }\n\n    @Test\n    public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksForWithFilter() throws Exception {\n        // test filter on process name\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 5)\n                .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC);\n        searchOptionsBuilder.filter(ProcessDeploymentInfoSearchDescriptor.NAME, \"My_Process2\");\n        final SearchResult<ProcessDeploymentInfo> searchRes = getProcessAPI()\n                .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(\n                        users.get(1).getId(), searchOptionsBuilder.done());\n        assertEquals(1, searchRes.getCount());\n        assertEquals(enabledProcessDefinitions.get(1).getId(), searchRes.getResult().get(0).getProcessId());\n    }\n\n    private void createProcessesDefinitions() throws Exception {\n        final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process1\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), ACTOR_NAME, true);\n        final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition1,\n                ACTOR_NAME, users.get(0));\n        enabledProcessDefinitions.add(processDefinition1);\n        startProcessAndWaitForTask(processDefinition1.getId(), \"step1\");\n\n        // create process2\n        final String actor2 = \"Actor2\";\n        final DesignProcessDefinition designProcessDefinition2 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process2\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor2, true);\n        final ProcessDefinition processDefinition2 = deployAndEnableProcessWithActor(designProcessDefinition2, actor2,\n                users.get(1));\n        enabledProcessDefinitions.add(processDefinition2);\n        startProcessAndWaitForTask(processDefinition2.getId(), \"step1\");\n\n        final DesignProcessDefinition designProcessDefinition3 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process3\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor2, true);\n        final ProcessDefinition processDefinition3 = deployAndEnableProcessWithActor(designProcessDefinition3, actor2,\n                users.get(1));\n        enabledProcessDefinitions.add(processDefinition3);\n        startProcessAndWaitForTask(processDefinition3.getId(), \"step1\");\n\n        // actor initiator is a group\n        final DesignProcessDefinition designProcessDefinition6 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process6\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor2, true);\n        final ProcessDefinition processDefinition6 = deployAndEnableProcessWithActor(designProcessDefinition6, actor2,\n                groups.get(0));\n        enabledProcessDefinitions.add(processDefinition6);\n        startProcessAndWaitForTask(processDefinition6.getId(), \"step1\");\n\n        // actor initiator is a role\n        final DesignProcessDefinition designProcessDefinition7 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process7\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor2, true);\n        final ProcessDefinition processDefinition7 = deployAndEnableProcessWithActor(designProcessDefinition7, actor2,\n                roles.get(0));\n        enabledProcessDefinitions.add(processDefinition7);\n        startProcessAndWaitForTask(processDefinition7.getId(), \"step1\");\n\n        // actor initiator is a membership\n        final DesignProcessDefinition designProcessDefinition8 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process8\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor2, true);\n        final ProcessDefinition processDefinition8 = deployAndEnableProcessWithActor(designProcessDefinition8, actor2,\n                roles.get(0), groups.get(0));\n        enabledProcessDefinitions.add(processDefinition8);\n        startProcessAndWaitForTask(processDefinition8.getId(), \"step1\");\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/search/SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport static org.junit.Assert.assertEquals;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.TestWithTechnicalUser;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoSearchDescriptor;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.test.BuildTestUtil;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Celine Souchet\n */\npublic class SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksIT extends TestWithTechnicalUser {\n\n    private List<ProcessDefinition> enabledProcessDefinitions;\n\n    private List<User> users = null;\n\n    @Override\n    @After\n    public void after() throws Exception {\n        disableAndDeleteProcess(enabledProcessDefinitions);\n        deleteUsers(users);\n        super.after();\n    }\n\n    @Override\n    @Before\n    public void before() throws Exception {\n        super.before();\n        // create users\n        users = new ArrayList<>(2);\n        users.add(createUser(\"chicobento\", \"bpm\"));\n        users.add(createUser(\"cebolinha\", \"bpm\"));\n\n        // create processes\n        enabledProcessDefinitions = new ArrayList<>(4);\n        createProcessesDefinitions();\n    }\n\n    @Test\n    public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks() throws Exception {\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 5)\n                .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC);\n        SearchResult<ProcessDeploymentInfo> searchRes = getProcessAPI()\n                .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks(searchOptionsBuilder.done());\n        assertEquals(2, searchRes.getCount());\n        assertEquals(enabledProcessDefinitions.get(0).getName(), searchRes.getResult().get(0).getName());\n\n        searchRes = getProcessAPI().searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(\n                users.get(1).getId(), searchOptionsBuilder.done());\n        assertEquals(0, searchRes.getCount());\n    }\n\n    @Test\n    public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksWithFilter() throws Exception {\n        // test filter on process name\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 5)\n                .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC);\n        searchOptionsBuilder.filter(ProcessDeploymentInfoSearchDescriptor.NAME, \"My_Process1\");\n        final SearchResult<ProcessDeploymentInfo> searchRes = getProcessAPI()\n                .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks(\n                        searchOptionsBuilder.done());\n        assertEquals(1, searchRes.getCount());\n        assertEquals(enabledProcessDefinitions.get(0).getId(), searchRes.getResult().get(0).getProcessId());\n    }\n\n    private void createProcessesDefinitions() throws Exception {\n        final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process1\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), ACTOR_NAME, true);\n        final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition1,\n                ACTOR_NAME, users.get(0));\n        enabledProcessDefinitions.add(processDefinition1);\n        startProcessAndWaitForTask(processDefinition1.getId(), \"step1\");\n\n        // create process2\n        final String actor2 = \"Actor2\";\n        final DesignProcessDefinition designProcessDefinition2 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process2\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor2, true);\n        final ProcessDefinition processDefinition2 = deployAndEnableProcessWithActor(designProcessDefinition2, actor2,\n                users.get(1));\n        enabledProcessDefinitions.add(processDefinition2);\n        startProcessAndWaitForTask(processDefinition2.getId(), \"step1\");\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/search/SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedByIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport static org.junit.Assert.assertEquals;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.TestWithTechnicalUser;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoSearchDescriptor;\nimport org.bonitasoft.engine.bpm.supervisor.ProcessSupervisor;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.test.BuildTestUtil;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Celine Souchet\n */\npublic class SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedByIT extends TestWithTechnicalUser {\n\n    private List<ProcessDefinition> enabledProcessDefinitions;\n\n    private List<User> users = null;\n\n    private ProcessSupervisor supervisor;\n\n    @Override\n    @After\n    public void after() throws Exception {\n        deleteSupervisor(supervisor);\n        disableAndDeleteProcess(enabledProcessDefinitions);\n        deleteUsers(users);\n        super.after();\n    }\n\n    @Override\n    @Before\n    public void before() throws Exception {\n        super.before();\n        // create users\n        users = new ArrayList<>(2);\n        users.add(createUser(\"chicobento\", \"bpm\"));\n        users.add(createUser(\"cebolinha\", \"bpm\"));\n\n        // create processes\n        enabledProcessDefinitions = new ArrayList<>(4);\n        createProcessesDefinitions();\n\n        supervisor = getProcessAPI().createProcessSupervisorForUser(enabledProcessDefinitions.get(0).getId(),\n                users.get(0).getId());\n    }\n\n    @Test\n    public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy() throws Exception {\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 5)\n                .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC);\n        SearchResult<ProcessDeploymentInfo> searchRes = getProcessAPI()\n                .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(\n                        users.get(0).getId(), searchOptionsBuilder.done());\n        assertEquals(1, searchRes.getCount());\n        assertEquals(enabledProcessDefinitions.get(0).getName(), searchRes.getResult().get(0).getName());\n\n        searchRes = getProcessAPI().searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(\n                users.get(1).getId(), searchOptionsBuilder.done());\n        assertEquals(0, searchRes.getCount());\n    }\n\n    @Test\n    public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedByWithFilter() throws Exception {\n        // test filter on process name\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 5)\n                .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC);\n        searchOptionsBuilder.filter(ProcessDeploymentInfoSearchDescriptor.NAME, \"My_Process1\");\n        final SearchResult<ProcessDeploymentInfo> searchRes = getProcessAPI()\n                .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(\n                        users.get(0).getId(), searchOptionsBuilder.done());\n        assertEquals(1, searchRes.getCount());\n        assertEquals(enabledProcessDefinitions.get(0).getId(), searchRes.getResult().get(0).getProcessId());\n    }\n\n    private void createProcessesDefinitions() throws Exception {\n        final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process1\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), ACTOR_NAME, true);\n        final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition1,\n                ACTOR_NAME, users.get(0));\n        enabledProcessDefinitions.add(processDefinition1);\n        startProcessAndWaitForTask(processDefinition1.getId(), \"step1\");\n\n        // create process2\n        final String actor2 = \"Actor2\";\n        final DesignProcessDefinition designProcessDefinition2 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process2\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor2, true);\n        final ProcessDefinition processDefinition2 = deployAndEnableProcessWithActor(designProcessDefinition2, actor2,\n                users.get(1));\n        enabledProcessDefinitions.add(processDefinition2);\n        startProcessAndWaitForTask(processDefinition2.getId(), \"step1\");\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/search/SearchProcessInstanceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport static java.util.Arrays.asList;\nimport static java.util.Collections.singletonList;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.test.BuildTestUtil.buildProcessDefinitionWithHumanAndAutomaticSteps;\nimport static org.hamcrest.core.Is.is;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.Index;\nimport org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessInstanceUpdater;\nimport org.bonitasoft.engine.bpm.process.impl.SubProcessDefinitionBuilder;\nimport org.bonitasoft.engine.connectors.TestConnectorThatThrowException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.expression.InvalidExpressionException;\nimport org.bonitasoft.engine.identity.Group;\nimport org.bonitasoft.engine.identity.Role;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.identity.UserMembership;\nimport org.bonitasoft.engine.identity.UserSearchDescriptor;\nimport org.bonitasoft.engine.identity.UserUpdater;\nimport org.bonitasoft.engine.test.BuildTestUtil;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class SearchProcessInstanceIT extends TestWithUser {\n\n    @Test\n    public void searchOpenProcessInstances() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps(\n                \"My_Process\",\n                \"1.0\", asList(\"step1\", \"step2\"), asList(true, true));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessInstance instance1 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance instance2 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance instance3 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance instance4 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance instance5 = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(instance1, \"step1\");\n        waitForUserTask(instance2, \"step1\");\n        waitForUserTask(instance3, \"step1\");\n        waitForUserTask(instance4, \"step1\");\n        waitForUserTask(instance5, \"step1\");\n\n        // search and check result ASC\n        final SearchOptionsBuilder searchOptions1 = BuildTestUtil.buildSearchOptions(processDefinition.getId(), 0, 2,\n                ProcessInstanceSearchDescriptor.ID,\n                Order.ASC);\n        SearchResult<ProcessInstance> result = getProcessAPI().searchOpenProcessInstances(searchOptions1.done());\n        assertNotNull(result);\n        assertEquals(5, result.getCount());\n        final List<ProcessInstance> processInstanceList1 = result.getResult();\n        assertNotNull(processInstanceList1);\n        assertEquals(2, processInstanceList1.size());\n        assertEquals(instance1.getId(), processInstanceList1.get(0).getId());\n        assertEquals(instance2.getId(), processInstanceList1.get(1).getId());\n\n        final SearchOptionsBuilder searchOptions2 = BuildTestUtil.buildSearchOptions(processDefinition.getId(), 4, 2,\n                ProcessInstanceSearchDescriptor.ID,\n                Order.ASC);\n        result = getProcessAPI().searchOpenProcessInstances(searchOptions2.done());\n        assertNotNull(result);\n        assertEquals(5, result.getCount());\n        final List<ProcessInstance> processInstanceList2 = result.getResult();\n        assertNotNull(processInstanceList2);\n        assertEquals(1, processInstanceList2.size());\n        assertEquals(instance5.getId(), processInstanceList2.get(0).getId());\n\n        final SearchOptionsBuilder searchOptions3 = BuildTestUtil.buildSearchOptions(processDefinition.getId(), 0, 3,\n                ProcessInstanceSearchDescriptor.ID,\n                Order.DESC);\n        // search and check result DESC\n        result = getProcessAPI().searchOpenProcessInstances(searchOptions3.done());\n        assertNotNull(result);\n        assertEquals(5, result.getCount());\n        final List<ProcessInstance> processInstanceList3 = result.getResult();\n        assertNotNull(processInstanceList3);\n        assertEquals(3, processInstanceList3.size());\n        assertEquals(instance5.getId(), processInstanceList3.get(0).getId());\n        assertEquals(instance4.getId(), processInstanceList3.get(1).getId());\n        assertEquals(instance3.getId(), processInstanceList3.get(2).getId());\n\n        final SearchOptionsBuilder searchOptions4 = BuildTestUtil.buildSearchOptions(processDefinition.getId() + 1, 0,\n                3, ProcessInstanceSearchDescriptor.ID,\n                Order.DESC);\n        result = getProcessAPI().searchOpenProcessInstances(searchOptions4.done());\n        assertNotNull(result);\n        assertEquals(0, result.getCount());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void searchFailedProcessInstances() throws Exception {\n        // Build a process with a failed connector on enter\n        final ProcessDefinition processDefinitionWithFailedConnector = deployAndEnableProcessWithConnector(BuildTestUtil\n                .buildProcessDefinitionWithFailedConnector(\"Process with failed connector in enter\"),\n                \"TestConnectorThatThrowException.impl\",\n                TestConnectorThatThrowException.class, \"TestConnectorThatThrowException.jar\");\n\n        // Build a process with a failed task\n        final ProcessDefinition processDefinitionWithFailedTask = deployAndEnableProcessWithConnector(BuildTestUtil\n                .buildProcessDefinitionWithAutomaticTaskAndFailedConnector(\"Process with failed task\"),\n                \"TestConnectorThatThrowException.impl\",\n                TestConnectorThatThrowException.class, \"TestConnectorThatThrowException.jar\");\n\n        final ProcessInstance instance1 = getProcessAPI().startProcess(processDefinitionWithFailedConnector.getId());\n        waitForProcessToBeInState(instance1.getId(), ProcessInstanceState.ERROR);\n\n        final ProcessInstance instance2 = getProcessAPI().startProcess(processDefinitionWithFailedTask.getId());\n        waitForFlowNodeInFailedState(instance2);\n\n        final ProcessInstance instance3 = getProcessAPI().startProcess(processDefinitionWithFailedConnector.getId());\n        waitForProcessToBeInState(instance3.getId(), ProcessInstanceState.ERROR);\n\n        // search and check result ASC\n        final SearchOptionsBuilder searchOptions1 = BuildTestUtil.buildSearchOptions(0, 2,\n                ProcessInstanceSearchDescriptor.START_DATE, Order.ASC);\n        SearchResult<ProcessInstance> result = getProcessAPI().searchFailedProcessInstances(searchOptions1.done());\n        assertNotNull(result);\n        assertEquals(3, result.getCount());\n        final List<ProcessInstance> processInstanceList1 = result.getResult();\n        assertNotNull(processInstanceList1);\n        assertEquals(2, processInstanceList1.size());\n        assertEquals(instance1.getId(), processInstanceList1.get(0).getId());\n        assertEquals(instance2.getId(), processInstanceList1.get(1).getId());\n\n        // search and check result DESC\n        final SearchOptionsBuilder searchOptions2 = BuildTestUtil.buildSearchOptions(0, 3,\n                ProcessInstanceSearchDescriptor.START_DATE, Order.DESC);\n        result = getProcessAPI().searchFailedProcessInstances(searchOptions2.done());\n        assertNotNull(result);\n        assertEquals(3, result.getCount());\n        final List<ProcessInstance> processInstanceList2 = result.getResult();\n        assertEquals(3, processInstanceList2.size());\n        assertEquals(instance3.getId(), processInstanceList2.get(0).getId());\n        assertEquals(instance2.getId(), processInstanceList2.get(1).getId());\n        assertEquals(instance1.getId(), processInstanceList2.get(2).getId());\n\n        // Search only process instance with state ERROR\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 3);\n        builder.filter(ProcessInstanceSearchDescriptor.STATE_NAME, ProcessInstanceState.ERROR);\n        builder.sort(ProcessInstanceSearchDescriptor.START_DATE, Order.ASC);\n        result = getProcessAPI().searchFailedProcessInstances(builder.done());\n        assertNotNull(result);\n        assertEquals(2, result.getCount());\n        final List<ProcessInstance> processInstances = result.getResult();\n        assertNotNull(processInstances);\n        assertEquals(2, processInstances.size());\n        assertEquals(instance1.getId(), processInstances.get(0).getId());\n        assertEquals(instance3.getId(), processInstances.get(1).getId());\n\n        disableAndDeleteProcess(processDefinitionWithFailedConnector, processDefinitionWithFailedTask);\n    }\n\n    /*\n     * Start process not with jack\n     * execute a task with jack\n     * check archived process instances worked on are 0\n     * finish process\n     * check there is one archived process instance worked on\n     */\n    @Test\n    public void searchArchivedProcessInstanceWorkedOnWithUserPerformedTask() throws Exception {\n        // create process\n        final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps(\n                asList(\"step1\", \"step2\"), asList(true, true));\n\n        // assign pending task to jack\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(processInstance, \"step1\", user);\n\n        final SearchOptionsBuilder searchOptions = BuildTestUtil.buildSearchOptions(processDefinition.getId(), 0, 5,\n                ProcessInstanceSearchDescriptor.ID,\n                Order.ASC);\n        // executed but not archived\n        SearchResult<ArchivedProcessInstance> result = getProcessAPI()\n                .searchArchivedProcessInstancesInvolvingUser(user.getId(), searchOptions.done());\n        assertNotNull(result);\n        assertEquals(0, result.getCount());\n\n        waitForUserTaskAndExecuteIt(processInstance, \"step2\", user);\n\n        // process finished: no more in worked on\n        waitForProcessToFinish(processInstance);\n        result = getProcessAPI().searchArchivedProcessInstancesInvolvingUser(user.getId(), searchOptions.done());\n        assertNotNull(result);\n        assertEquals(1, result.getCount());\n        final ArchivedProcessInstance processInstance2 = result.getResult().get(0);\n        assertEquals(processInstance.getId(), processInstance2.getSourceObjectId());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    /*\n     * Start process with jack\n     * execute a task with john\n     * check archived process instances worked on are 0\n     * finish process\n     * check there is one archived process instance worked on\n     */\n    @Test\n    public void searchArchivedProcessInstanceWorkedOnWithUserStartedProcess() throws Exception {\n        // create user\n        final User jack = createUser(\"jack\", PASSWORD);\n        final User john = createUser(\"john\", PASSWORD);\n        logout();\n        loginOnDefaultTenantWith(\"john\", PASSWORD);\n        // create process\n        final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps(\n                asList(\"step1\"), asList(true));\n        // assign pending task to jack\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                jack);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(\"step1\");\n\n        final SearchOptionsBuilder searchOptions = BuildTestUtil.buildSearchOptions(processDefinition.getId(), 0, 5,\n                ProcessInstanceSearchDescriptor.ID,\n                Order.ASC);\n        // the process is not started by jack but not finished: not in \"workedOn\"\n        SearchResult<ArchivedProcessInstance> result = getProcessAPI()\n                .searchArchivedProcessInstancesInvolvingUser(john.getId(), searchOptions.done());\n        assertNotNull(result);\n        assertEquals(0, result.getCount());\n\n        // assign\n        assignAndExecuteStep(step1Id, jack);\n\n        // process finished: in worked on\n        waitForProcessToFinish(processInstance);\n        result = getProcessAPI().searchArchivedProcessInstancesInvolvingUser(john.getId(), searchOptions.done());\n        assertNotNull(result);\n        assertEquals(1, result.getCount());\n\n        disableAndDeleteProcess(processDefinition);\n        deleteUsers(john, jack);\n    }\n\n    /*\n     * Start process not with jack\n     * assign task to jack\n     * check worked on = 0\n     * execute task with jack\n     * check worked on = 1\n     * finish process\n     * check worked on = 0\n     */\n    @Test\n    public void searchProcessInstanceWorkedOnWithUserPerformedTask() throws Exception {\n        // create user\n        final String username = \"jack\";\n        final User jack = createUser(username, PASSWORD);\n        final User john = createUser(\"john\", PASSWORD);\n        // create process\n        final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps(\n                asList(\"step1\", \"step2\"), asList(true, true));\n        // assign pending task to jack\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                jack);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        logout();\n        loginOnDefaultTenantWith(username, PASSWORD);\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n\n        final SearchOptionsBuilder searchOptions = BuildTestUtil.buildSearchOptions(processDefinition.getId(), 0, 5,\n                ProcessInstanceSearchDescriptor.ID,\n                Order.ASC);\n        // the process is not started by jack and jack has not performed tasks: not in \"workedOn\"\n        SearchResult<ProcessInstance> result = getProcessAPI().searchOpenProcessInstancesInvolvingUser(jack.getId(),\n                searchOptions.done());\n        assertNotNull(result);\n        assertEquals(0, result.getCount());\n\n        // assign\n        getProcessAPI().assignUserTask(step1Id, jack.getId());\n\n        // after assigned: still not worked on\n        result = getProcessAPI().searchOpenProcessInstancesInvolvingUser(jack.getId(), searchOptions.done());\n        assertNotNull(result);\n        assertEquals(0, result.getCount());\n\n        getProcessAPI().executeFlowNode(step1Id);\n        final long step2Id = waitForUserTask(processInstance, \"step2\");\n\n        // one task was performed: the process is in \"WorkedOn\"\n        result = getProcessAPI().searchOpenProcessInstancesInvolvingUser(jack.getId(), searchOptions.done());\n        assertNotNull(result);\n        assertEquals(1, result.getCount());\n        final ProcessInstance processInstance2 = result.getResult().get(0);\n        assertEquals(processInstance.getId(), processInstance2.getId());\n        result = getProcessAPI().searchOpenProcessInstancesInvolvingUser(john.getId(), searchOptions.done());\n        assertNotNull(result);\n        assertEquals(0, result.getCount());\n\n        assignAndExecuteStep(step2Id, jack);\n\n        // process finished: no more in worked on\n        waitForProcessToFinish(processInstance);\n        result = getProcessAPI().searchOpenProcessInstancesInvolvingUser(jack.getId(), searchOptions.done());\n        assertNotNull(result);\n        assertEquals(0, result.getCount());\n\n        disableAndDeleteProcess(processDefinition);\n        deleteUsers(john, jack);\n    }\n\n    @Test\n    public void searchProcessInstanceWorkedOnWithUserPerformedTaskOnMultipleInstances() throws Exception {\n        // create user\n        final String username = \"jack\";\n        final User jack = createUser(username, PASSWORD);\n        final User john = createUser(\"john\", PASSWORD);\n        // create process\n        final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps(\n                asList(\"step1\", \"step2\"), asList(true, true));\n        // assign pending task to jack\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                jack);\n        final ProcessInstance p1 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance p2 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance p3 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance p4 = getProcessAPI().startProcess(processDefinition.getId());\n\n        logout();\n        loginOnDefaultTenantWith(username, PASSWORD);\n        waitForUserTaskAndExecuteIt(p1, \"step1\", jack);\n        waitForUserTaskAndExecuteIt(p2, \"step1\", jack);\n        waitForUserTaskAndExecuteIt(p3, \"step1\", jack);\n        logout();\n        loginOnDefaultTenantWith(\"john\", PASSWORD);\n        waitForUserTaskAndExecuteIt(p4, \"step1\", jack);\n\n        waitForUserTask(p1, \"step2\");\n        waitForUserTask(p2, \"step2\");\n        waitForUserTask(p3, \"step2\");\n        waitForUserTask(p4, \"step2\");\n\n        final SearchOptionsBuilder searchOptions = BuildTestUtil.buildSearchOptions(processDefinition.getId(), 0, 5,\n                ProcessInstanceSearchDescriptor.ID,\n                Order.ASC);\n        SearchResult<ProcessInstance> result = getProcessAPI().searchOpenProcessInstancesInvolvingUser(jack.getId(),\n                searchOptions.done());\n        assertNotNull(result);\n        assertEquals(3, result.getCount());\n        assertEquals(3, result.getResult().size());\n\n        result = getProcessAPI().searchOpenProcessInstancesInvolvingUser(john.getId(), searchOptions.done());\n        assertNotNull(result);\n        assertEquals(1, result.getCount());\n        assertEquals(1, result.getResult().size());\n\n        disableAndDeleteProcess(processDefinition);\n        deleteUsers(john, jack);\n    }\n\n    @Test\n    public void searchProcessInstanceWorkedOnWithUserStartedItOnMultipleInstances() throws Exception {\n        // create user\n        final String username = \"jack\";\n        final User jack = createUser(username, PASSWORD);\n        final User john = createUser(\"john\", PASSWORD);\n        // create process\n        final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps(\n                asList(\"step1\", \"step2\"), asList(true, true));\n        // assign pending task to jack\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                jack);\n        logout();\n        loginOnDefaultTenantWith(username, PASSWORD);\n        final ProcessInstance p1 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance p2 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance p3 = getProcessAPI().startProcess(processDefinition.getId());\n        logout();\n        loginOnDefaultTenantWith(\"john\", PASSWORD);\n        final ProcessInstance p4 = getProcessAPI().startProcess(processDefinition.getId());\n\n        waitForUserTask(p1, \"step1\");\n        waitForUserTask(p2, \"step1\");\n        waitForUserTask(p3, \"step1\");\n        waitForUserTask(p4, \"step1\");\n\n        final SearchOptionsBuilder searchOptions = BuildTestUtil.buildSearchOptions(processDefinition.getId(), 0, 5,\n                ProcessInstanceSearchDescriptor.ID,\n                Order.ASC);\n        SearchResult<ProcessInstance> result = getProcessAPI().searchOpenProcessInstancesInvolvingUser(jack.getId(),\n                searchOptions.done());\n        assertNotNull(result);\n        assertEquals(3, result.getCount());\n        assertEquals(3, result.getResult().size());\n\n        result = getProcessAPI().searchOpenProcessInstancesInvolvingUser(john.getId(), searchOptions.done());\n        assertNotNull(result);\n        assertEquals(1, result.getCount());\n        assertEquals(1, result.getResult().size());\n\n        disableAndDeleteProcess(processDefinition);\n        deleteUsers(john, jack);\n    }\n\n    /*\n     * Start process with jack\n     * assign/execute task with john\n     * check worked on = 1 for jack\n     * finish process\n     * check worked on = 0\n     */\n    @Test\n    public void searchProcessInstanceWorkedOnWithUserStartedProcess() throws Exception {\n        // create user\n        final User jack = createUser(\"jack\", PASSWORD);\n        // create process\n        final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps(\n                asList(\"step1\"), asList(true));\n\n        // assign pending task to jack\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                jack);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(\"step1\");\n\n        final SearchOptionsBuilder searchOptions = BuildTestUtil.buildSearchOptions(processDefinition.getId(), 0, 5,\n                ProcessInstanceSearchDescriptor.ID,\n                Order.ASC);\n        // the process is started by jack and jack has not performed tasks: In \"workedOn\"\n        SearchResult<ProcessInstance> result = getProcessAPI().searchOpenProcessInstancesInvolvingUser(user.getId(),\n                searchOptions.done());\n        assertNotNull(result);\n        assertEquals(1, result.getCount());\n        assignAndExecuteStep(step1Id, jack);\n\n        // process finished: no more in worked on\n        waitForProcessToFinish(processInstance);\n        result = getProcessAPI().searchOpenProcessInstancesInvolvingUser(user.getId(), searchOptions.done());\n        assertNotNull(result);\n        assertEquals(0, result.getCount());\n\n        deleteUser(jack);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void searchOpenProcessInstancesInvolvingUsersManagedByWithUserPerformedTask() throws Exception {\n        // create user\n        final User paul = createUser(\"paul\", \"bpm\");\n        final User jack = createUser(\"jack\", paul.getId());\n        final User john = createUser(\"john\", \"bpm\");\n\n        // create process\n        final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps(\n                asList(\"step1\", \"step2\"), asList(true, true));\n\n        // assign pending task to jack\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                jack);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        logout();\n        loginOnDefaultTenantWith(\"jack\", \"bpm\");\n        final long step1Id = waitForUserTask(\"step1\");\n\n        final SearchOptionsBuilder searchOptions = BuildTestUtil.buildSearchOptions(processDefinition.getId(), 0, 5,\n                ProcessInstanceSearchDescriptor.ID,\n                Order.ASC);\n        // the process is not started by jack and jack has not performed tasks: not in \"workedOn\"\n        SearchResult<ProcessInstance> result = getProcessAPI()\n                .searchOpenProcessInstancesInvolvingUsersManagedBy(paul.getId(), searchOptions.done());\n        assertNotNull(result);\n        assertEquals(0, result.getCount());\n\n        // assign\n        getProcessAPI().assignUserTask(step1Id, jack.getId());\n\n        // after assigned: still not worked on\n        result = getProcessAPI().searchOpenProcessInstancesInvolvingUsersManagedBy(paul.getId(), searchOptions.done());\n        assertNotNull(result);\n        assertEquals(0, result.getCount());\n\n        getProcessAPI().executeFlowNode(step1Id);\n        final long step2Id = waitForUserTask(\"step2\");\n\n        // one task was performed: the process is in \"WorkedOn\"\n        result = getProcessAPI().searchOpenProcessInstancesInvolvingUsersManagedBy(paul.getId(), searchOptions.done());\n        assertNotNull(result);\n        assertEquals(1, result.getCount());\n        final ProcessInstance processInstance2 = result.getResult().get(0);\n        assertEquals(processInstance.getId(), processInstance2.getId());\n\n        assignAndExecuteStep(step2Id, jack);\n\n        // process finished: no more in worked on\n        waitForProcessToFinish(processInstance);\n        result = getProcessAPI().searchOpenProcessInstancesInvolvingUsersManagedBy(paul.getId(), searchOptions.done());\n        assertNotNull(result);\n        assertEquals(0, result.getCount());\n\n        disableAndDeleteProcess(processDefinition);\n        deleteUsers(john, jack, paul);\n    }\n\n    @Test\n    public void searchOpenProcessInstancesInvolvingUsersManagedByWithUserPerformedTaskOnMultipleInstances()\n            throws Exception {\n        // create user\n        final User paul = createUser(\"paul\", \"bpm\");\n        final User jack = createUser(\"jack\", paul.getId());\n        final User john = createUser(\"john\", paul.getId());\n\n        // create process\n        final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps(\n                asList(\"step1\", \"step2\"), asList(true, true));\n        // assign pending task to jack\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                jack);\n        final ProcessInstance p1 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance p2 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance p3 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance p4 = getProcessAPI().startProcess(processDefinition.getId());\n\n        logout();\n        loginOnDefaultTenantWith(\"jack\", \"bpm\");\n        waitForUserTaskAndExecuteIt(p1, \"step1\", jack);\n        waitForUserTaskAndExecuteIt(p2, \"step1\", jack);\n        waitForUserTaskAndExecuteIt(p3, \"step1\", jack);\n        logout();\n        loginOnDefaultTenantWith(\"john\", \"bpm\");\n        waitForUserTaskAndExecuteIt(p4, \"step1\", jack);\n\n        waitForUserTask(p1, \"step2\");\n        waitForUserTask(p2, \"step2\");\n        waitForUserTask(p3, \"step2\");\n        waitForUserTask(p4, \"step2\");\n\n        final SearchOptionsBuilder searchOptions = BuildTestUtil.buildSearchOptions(processDefinition.getId(), 0, 5,\n                ProcessInstanceSearchDescriptor.ID,\n                Order.ASC);\n        final SearchResult<ProcessInstance> result = getProcessAPI()\n                .searchOpenProcessInstancesInvolvingUsersManagedBy(paul.getId(), searchOptions.done());\n        assertNotNull(result);\n        assertEquals(4, result.getCount());\n        assertEquals(4, result.getResult().size());\n\n        disableAndDeleteProcess(processDefinition);\n        deleteUsers(john, jack, paul);\n    }\n\n    @Test\n    public void searchOpenProcessInstancesInvolvingUsersManagedByWithUserStartedItOnMultipleInstances()\n            throws Exception {\n        // create user\n        final User paul = createUser(\"paul\", \"bpm\");\n        final User jack = createUser(\"jack\", paul.getId());\n        final User john = createUser(\"john\", paul.getId());\n        final User pierre = createUser(\"pierre\", \"bpm\");\n        // create process\n        final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps(\n                asList(\"step1\", \"step2\"), asList(true, true));\n\n        // assign pending task to jack\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                jack);\n        logout();\n        loginOnDefaultTenantWith(\"jack\", \"bpm\");\n        final ProcessInstance p1 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance p2 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance p3 = getProcessAPI().startProcess(processDefinition.getId());\n        logout();\n        loginOnDefaultTenantWith(\"john\", \"bpm\");\n        final ProcessInstance p4 = getProcessAPI().startProcess(processDefinition.getId());\n        logout();\n        loginOnDefaultTenantWith(\"pierre\", \"bpm\");\n        final ProcessInstance p5 = getProcessAPI().startProcess(processDefinition.getId());\n\n        waitForUserTask(p1, \"step1\");\n        waitForUserTask(p2, \"step1\");\n        waitForUserTask(p3, \"step1\");\n        waitForUserTask(p4, \"step1\");\n        waitForUserTask(p5, \"step1\");\n\n        final SearchOptionsBuilder searchOptions = BuildTestUtil.buildSearchOptions(processDefinition.getId(), 0, 5,\n                ProcessInstanceSearchDescriptor.ID,\n                Order.ASC);\n        final SearchResult<ProcessInstance> result = getProcessAPI()\n                .searchOpenProcessInstancesInvolvingUsersManagedBy(paul.getId(), searchOptions.done());\n        assertNotNull(result);\n        assertEquals(4, result.getCount());\n        assertEquals(4, result.getResult().size());\n\n        disableAndDeleteProcess(processDefinition);\n        deleteUsers(john, jack, paul, pierre);\n    }\n\n    @Test\n    public void searchOpenProcessInstancesInvolvingUsersManagedByWithUserStartedProcess() throws Exception {\n        // create user\n        final User paul = createUser(\"paul\", \"bpm\");\n        final User jack = createUser(\"jack\", paul.getId());\n        final User john = createUser(\"john\", paul.getId());\n        logout();\n        loginOnDefaultTenantWith(\"john\", \"bpm\");\n\n        // create process\n        final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps(\n                asList(\"step1\"), asList(true));\n\n        // assign pending task to jack\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                jack);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(\"step1\");\n\n        final SearchOptionsBuilder searchOptions = BuildTestUtil.buildSearchOptions(processDefinition.getId(), 0, 5,\n                ProcessInstanceSearchDescriptor.ID,\n                Order.ASC);\n        // the process is started by jack and jack has not performed tasks: In \"workedOn\"\n        SearchResult<ProcessInstance> result = getProcessAPI()\n                .searchOpenProcessInstancesInvolvingUsersManagedBy(paul.getId(), searchOptions.done());\n        assertNotNull(result);\n        assertEquals(1, result.getCount());\n        assignAndExecuteStep(step1Id, jack);\n\n        // process finished: no more in worked on\n        waitForProcessToFinish(processInstance);\n        result = getProcessAPI().searchOpenProcessInstancesInvolvingUsersManagedBy(paul.getId(), searchOptions.done());\n        assertNotNull(result);\n        assertEquals(0, result.getCount());\n\n        disableAndDeleteProcess(processDefinition);\n        deleteUsers(john, jack, paul);\n    }\n\n    @Test\n    public void searchOpenProcessInstancesStartedBy() throws Exception {\n        // create process\n        final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps(\n                \"My_Process\",\n                \"1.0\", asList(\"step1\", \"step2\"), asList(true, true));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessInstance instance1 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance instance2 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance instance3 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance instance4 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance instance5 = getProcessAPI().startProcess(processDefinition.getId());\n\n        waitForUserTask(instance1, \"step1\");\n        waitForUserTask(instance2, \"step1\");\n        waitForUserTask(instance3, \"step1\");\n        waitForUserTask(instance4, \"step1\");\n        waitForUserTask(instance5, \"step1\");\n\n        // test started by correct user\n        final SearchOptionsBuilder searchOptions1 = BuildTestUtil.buildSearchOptions(processDefinition.getId(), 0, 10,\n                ProcessInstanceSearchDescriptor.ID,\n                Order.ASC);\n        searchOptions1.filter(ProcessInstanceSearchDescriptor.STARTED_BY, user.getId());\n        SearchResult<ProcessInstance> result = getProcessAPI().searchOpenProcessInstances(searchOptions1.done());\n        assertNotNull(result);\n        assertEquals(5, result.getCount());\n        final List<ProcessInstance> processInstanceList1 = result.getResult();\n        assertNotNull(processInstanceList1);\n        assertEquals(5, processInstanceList1.size());\n        assertEquals(instance1.getId(), processInstanceList1.get(0).getId());\n        assertEquals(instance2.getId(), processInstanceList1.get(1).getId());\n        assertEquals(instance3.getId(), processInstanceList1.get(2).getId());\n        assertEquals(instance4.getId(), processInstanceList1.get(3).getId());\n        assertEquals(instance5.getId(), processInstanceList1.get(4).getId());\n\n        // test started by not existed user\n        searchOptions1.filter(ProcessInstanceSearchDescriptor.STARTED_BY, user.getId() + 1500);\n        result = getProcessAPI().searchOpenProcessInstances(searchOptions1.done());\n        assertNotNull(result);\n        assertEquals(0, result.getCount());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void searchArchivedProcessInstancesInvolvingUser() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps(\n                asList(\"step1\"), asList(false));\n        final ProcessDefinition processDefinition = deployAndEnableProcess(designProcessDefinition);\n\n        final ProcessInstance instance1 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance instance2 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance instance3 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance instance4 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance instance5 = getProcessAPI().startProcess(processDefinition.getId());\n        waitForProcessToFinish(instance1);\n        waitForProcessToFinish(instance2);\n        waitForProcessToFinish(instance3);\n        waitForProcessToFinish(instance4);\n        waitForProcessToFinish(instance5);\n\n        // test started by correct user\n        final SearchOptionsBuilder opts = new SearchOptionsBuilder(0, 10);\n        opts.filter(ArchivedProcessInstancesSearchDescriptor.PROCESS_DEFINITION_ID, processDefinition.getId());\n        opts.sort(ArchivedProcessInstancesSearchDescriptor.SOURCE_OBJECT_ID, Order.ASC);\n        final SearchResult<ArchivedProcessInstance> result = getProcessAPI()\n                .searchArchivedProcessInstancesInvolvingUser(user.getId(), opts.done());\n        assertEquals(5, result.getCount());\n        final List<ArchivedProcessInstance> processInstances = result.getResult();\n        assertNotNull(processInstances);\n        assertEquals(5, processInstances.size());\n        assertEquals(instance1.getId(), processInstances.get(0).getSourceObjectId());\n        assertEquals(instance2.getId(), processInstances.get(1).getSourceObjectId());\n        assertEquals(instance3.getId(), processInstances.get(2).getSourceObjectId());\n        assertEquals(instance4.getId(), processInstances.get(3).getSourceObjectId());\n        assertEquals(instance5.getId(), processInstances.get(4).getSourceObjectId());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void twoPoolsWithOneWithACallActivityCaseTest() throws Exception {\n        final ProcessDefinition subProcessDefinition = buildAndDeploySubprocess();\n\n        final ProcessDefinition rootProcessDefinition = buildAndDeployProcessWithCallActivity();\n\n        final ProcessInstance rootProcessInstance = getProcessAPI().startProcess(rootProcessDefinition.getId());\n        waitForUserTask(rootProcessInstance, \"User task\");\n\n        final SearchOptions opts = new SearchOptionsBuilder(0, 10).done();\n        final SearchResult<ProcessInstance> processInstanceSearchResult = getProcessAPI()\n                .searchOpenProcessInstances(opts);\n        Assert.assertThat(processInstanceSearchResult.getCount(), is(1L));\n\n        disableAndDeleteProcess(rootProcessDefinition);\n        disableAndDeleteProcess(subProcessDefinition);\n    }\n\n    private ProcessDefinition buildAndDeploySubprocess() throws InvalidProcessDefinitionException, BonitaException {\n        final ProcessDefinitionBuilder subProcessDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"subprocess\", \"1.0\");\n        subProcessDefinitionBuilder.addActor(ACTOR_NAME);\n        subProcessDefinitionBuilder.addUserTask(\"User task\", ACTOR_NAME);\n        final DesignProcessDefinition designProcessDefinition = subProcessDefinitionBuilder.done();\n        return deployAndEnableProcessWithActor(designProcessDefinition,\n                ACTOR_NAME, user);\n    }\n\n    private ProcessDefinition buildAndDeployProcessWithCallActivity()\n            throws InvalidExpressionException, InvalidProcessDefinitionException, BonitaException {\n        final ProcessDefinitionBuilder rootProcessDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"process1\", \"1.0\");\n        rootProcessDefinitionBuilder.addActor(ACTOR_NAME);\n        final Expression subProcessName = new ExpressionBuilder().createConstantStringExpression(\"subprocess\");\n        final Expression subProcessVersion = new ExpressionBuilder().createConstantStringExpression(\"1.0\");\n        rootProcessDefinitionBuilder.addCallActivity(\"call subprocess\", subProcessName, subProcessVersion);\n        final DesignProcessDefinition designProcessDefinition = rootProcessDefinitionBuilder.done();\n        return deployAndEnableProcessWithActor(designProcessDefinition,\n                ACTOR_NAME, user);\n    }\n\n    @Test\n    public void searchProcessInstanceByRootProcessInstanceId() throws Exception {\n        final ProcessDefinition subProcessDefinition = buildAndDeploySubprocess();\n\n        final ProcessDefinition rootProcessDefinition = buildAndDeployProcessWithCallActivity();\n\n        final ProcessInstance rootProcessInstance = getProcessAPI().startProcess(rootProcessDefinition.getId());\n        waitForUserTask(rootProcessInstance, \"User task\");\n\n        SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.filter(ProcessInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID,\n                rootProcessInstance.getId());\n        searchOptionsBuilder.differentFrom(ProcessInstanceSearchDescriptor.ID, rootProcessInstance.getId());\n        final SearchOptions opts = searchOptionsBuilder.done();\n        final SearchResult<ProcessInstance> processInstanceSearchResult = getProcessAPI()\n                .searchProcessInstances(opts);\n        Assert.assertThat(processInstanceSearchResult.getCount(), is(1L));\n        Assert.assertThat(processInstanceSearchResult.getResult().get(0).getProcessDefinitionId(),\n                is(subProcessDefinition.getId()));\n\n        disableAndDeleteProcess(rootProcessDefinition);\n        disableAndDeleteProcess(subProcessDefinition);\n    }\n\n    @Test\n    public void searchArchivedProcessInstanceByRootProcessInstanceId() throws Exception {\n        final ProcessDefinition subProcessDefinition = buildAndDeploySubprocess();\n\n        final ProcessDefinition rootProcessDefinition = buildAndDeployProcessWithCallActivity();\n\n        final ProcessInstance rootProcessInstance = getProcessAPI().startProcess(rootProcessDefinition.getId());\n        waitForUserTaskAssignAndExecuteIt(rootProcessInstance, \"User task\", user, Collections.emptyMap());\n        waitForProcessToFinish(rootProcessInstance);\n\n        SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.filter(ArchivedProcessInstancesSearchDescriptor.ROOT_PROCESS_INSTANCE_ID,\n                rootProcessInstance.getId());\n        searchOptionsBuilder.differentFrom(ArchivedProcessInstancesSearchDescriptor.SOURCE_OBJECT_ID,\n                rootProcessInstance.getId());\n        searchOptionsBuilder.filter(ArchivedProcessInstancesSearchDescriptor.STATE_ID,\n                ProcessInstanceState.COMPLETED.getId());\n        final SearchOptions opts = searchOptionsBuilder.done();\n        final SearchResult<ArchivedProcessInstance> archivedProcessInstanceSearchResult = getProcessAPI()\n                .searchArchivedProcessInstancesInAllStates(opts);\n        Assert.assertThat(archivedProcessInstanceSearchResult.getCount(), is(1L));\n        Assert.assertThat(archivedProcessInstanceSearchResult.getResult().get(0).getProcessDefinitionId(),\n                is(subProcessDefinition.getId()));\n\n        disableAndDeleteProcess(rootProcessDefinition);\n        disableAndDeleteProcess(subProcessDefinition);\n    }\n\n    @Test\n    public void searchArchivedProcessInstancesWithApostrophe() throws Exception {\n        // Create process\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(\"Na'me\",\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask(\"userTask1\", ACTOR_NAME)\n                .getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(\"userTask1\");\n\n        final List<ActivityInstance> activityInstances = getProcessAPI().getActivities(processInstance.getId(), 0, 10);\n        assertEquals(1, activityInstances.size());\n        for (final ActivityInstance activityInstance : activityInstances) {\n            final long activityInstanceId = activityInstance.getId();\n            getProcessAPI().setActivityStateById(activityInstanceId, 12);\n        }\n        waitForProcessToFinish(processInstance);\n        // Search Archived process\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10).searchTerm(\"Na'\");\n        final SearchResult<ArchivedProcessInstance> searchProcessInstanceResult = getProcessAPI()\n                .searchArchivedProcessInstances(builder.done());\n        assertEquals(1, searchProcessInstanceResult.getCount());\n        final List<ArchivedProcessInstance> archivedProcessInstances = searchProcessInstanceResult.getResult();\n        final ArchivedProcessInstance archivedProcessInstance = archivedProcessInstances.get(0);\n        assertEquals(processInstance.getId(), archivedProcessInstance.getSourceObjectId());\n        assertEquals(\"Na'me\", archivedProcessInstance.getName());\n\n        // Clean\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void searchArchivedProcessInstancesRetrieveOnlyTerminalStates() throws Exception {\n        // create a process instance in state completed and a process instance in the state canceled\n        final ProcessDefinition simpleProcess = createArchivedProcessInstanceInStateCompletedAndCanceled();\n\n        // create process instance in state aborted\n        final ProcessDefinition procWithEventSubProcess = createArchivedProcInstInAbortedState();\n\n        // search\n        final SearchOptionsBuilder searchBuilder = new SearchOptionsBuilder(0, 10);\n        searchBuilder.sort(ArchivedProcessInstancesSearchDescriptor.STATE_ID, Order.ASC);\n\n        final SearchResult<ArchivedProcessInstance> searchResult = getProcessAPI()\n                .searchArchivedProcessInstances(searchBuilder.done());\n        // check the result: 3 instances are expected, one in the state completed, one in the state canceled, and one in the state aborted\n        assertEquals(3, searchResult.getCount());\n        final List<ArchivedProcessInstance> processes = searchResult.getResult();\n        // canceled\n        assertEquals(simpleProcess.getName(), processes.get(0).getName());\n        assertEquals(TestStates.CANCELLED.getStateName(), processes.get(0).getState());\n        // aborted\n        assertEquals(procWithEventSubProcess.getName(), processes.get(1).getName());\n        assertEquals(TestStates.ABORTED.getStateName(), processes.get(1).getState());\n        // completed\n        assertEquals(simpleProcess.getName(), processes.get(2).getName());\n        assertEquals(TestStates.NORMAL_FINAL.getStateName(), processes.get(2).getState());\n\n        // clean up\n        disableAndDeleteProcess(simpleProcess.getId());\n        disableAndDeleteProcess(procWithEventSubProcess.getId());\n    }\n\n    @Test\n    public void searchUsersWhoCanStartProcess() throws Exception {\n        // Create a disabled jack user\n        final User jackIsDisabled = createUser(\"jack\", PASSWORD);\n        final UserUpdater userUpdater = new UserUpdater();\n        userUpdater.setEnabled(false);\n        getIdentityAPI().updateUser(jackIsDisabled.getId(), userUpdater);\n\n        // create a group and add a new user to it\n        // this user will be add to the actor mapping directly and through this group\n        // only one instance of this user should be returned by the search\n        final User jackot = createUser(\"jackot\", PASSWORD);\n        Group employees = createGroup(\"Employees\");\n        createRole(\"Member\");\n        createUserMembership(\"jackot\", \"Member\", \"Employees\");\n\n        // Create the process, deploy and enable it\n        final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps(\n                PROCESS_NAME,\n                PROCESS_VERSION, singletonList(\"step1\"), singletonList(true), ACTOR_NAME, true);\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                asList(employees), asList(user, jackot, jackIsDisabled));\n\n        // Search\n        final SearchOptionsBuilder searchBuilder = new SearchOptionsBuilder(0, 10);\n        searchBuilder.sort(UserSearchDescriptor.USER_NAME, Order.ASC);\n        searchBuilder.searchTerm(\"jac\");\n        final SearchResult<User> searchResult = getProcessAPI()\n                .searchUsersWhoCanStartProcessDefinition(processDefinition.getId(), searchBuilder.done());\n        assertThat(searchResult.getCount()).isEqualTo(1);\n        final List<User> users = searchResult.getResult();\n        assertThat(users).extracting(\"id\").containsExactlyInAnyOrder(jackot.getId());\n\n        // clean up\n        deleteUsers(jackIsDisabled, jackot);\n        deleteGroups(employees);\n        disableAndDeleteProcess(processDefinition.getId());\n    }\n\n    @Test\n    public void searchUsersWhoCanStartProcessInAGroup() throws Exception {\n        final Group group = createGroup(GROUP_NAME);\n        final Role role = createRole(ROLE_NAME);\n        final UserMembership userMembership = getIdentityAPI().addUserMembership(user.getId(), group.getId(),\n                role.getId());\n\n        final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps(\n                PROCESS_NAME,\n                PROCESS_VERSION, asList(\"step1\"), asList(true), ACTOR_NAME, true);\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                group);\n\n        // Search\n        final SearchOptionsBuilder searchBuilder = new SearchOptionsBuilder(0, 10);\n        searchBuilder.sort(UserSearchDescriptor.USER_NAME, Order.ASC);\n        final SearchResult<User> searchResult = getProcessAPI()\n                .searchUsersWhoCanStartProcessDefinition(processDefinition.getId(), searchBuilder.done());\n        assertEquals(1, searchResult.getCount());\n        final List<User> users = searchResult.getResult();\n        assertEquals(user.getId(), users.get(0).getId());\n\n        // clean up\n        disableAndDeleteProcess(processDefinition.getId());\n        deleteUserMemberships(userMembership);\n        deleteGroups(group);\n        deleteRoles(role);\n    }\n\n    @Test\n    public void searchUsersWhoCanStartProcessInARole() throws Exception {\n        final Group group = createGroup(GROUP_NAME);\n        final Role role = createRole(ROLE_NAME);\n        final UserMembership userMembership = getIdentityAPI().addUserMembership(user.getId(), group.getId(),\n                role.getId());\n\n        final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps(\n                PROCESS_NAME,\n                PROCESS_VERSION, asList(\"step1\"), asList(true), ACTOR_NAME, true);\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                role);\n\n        // Search\n        final SearchOptionsBuilder searchBuilder = new SearchOptionsBuilder(0, 10);\n        searchBuilder.sort(UserSearchDescriptor.USER_NAME, Order.ASC);\n        final SearchResult<User> searchResult = getProcessAPI()\n                .searchUsersWhoCanStartProcessDefinition(processDefinition.getId(), searchBuilder.done());\n        assertEquals(1, searchResult.getCount());\n        final List<User> users = searchResult.getResult();\n        assertEquals(user.getId(), users.get(0).getId());\n\n        // clean up\n        disableAndDeleteProcess(processDefinition.getId());\n        deleteUserMemberships(userMembership);\n        deleteGroups(group);\n        deleteRoles(role);\n    }\n\n    @Test\n    public void searchUsersWhoCanStartProcessInARoleAndAGroup() throws Exception {\n        final Group group = createGroup(GROUP_NAME);\n        final Role role = createRole(ROLE_NAME);\n        final UserMembership userMembership = createUserMembership(USERNAME, ROLE_NAME, GROUP_NAME);\n\n        final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps(\n                PROCESS_NAME,\n                PROCESS_VERSION, asList(\"step1\"), asList(true), ACTOR_NAME, true);\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                role, group);\n\n        // Search\n        final SearchOptionsBuilder searchBuilder = new SearchOptionsBuilder(0, 10);\n        searchBuilder.sort(UserSearchDescriptor.USER_NAME, Order.ASC);\n        final SearchResult<User> searchResult = getProcessAPI()\n                .searchUsersWhoCanStartProcessDefinition(processDefinition.getId(), searchBuilder.done());\n        assertEquals(1, searchResult.getCount());\n        final List<User> users = searchResult.getResult();\n        assertEquals(user.getId(), users.get(0).getId());\n\n        // clean up\n        disableAndDeleteProcess(processDefinition.getId());\n        deleteUserMemberships(userMembership);\n        deleteGroups(group);\n        deleteRoles(role);\n    }\n\n    private ProcessDefinition createArchivedProcInstInAbortedState() throws Exception {\n        final String userTaskName = \"step1\";\n        final String subProcTaskName = \"subStep\";\n        final String signalName = \"go\";\n        // deploy and start a process with event subprocess\n        final ProcessDefinition procWithEventSubProcess = deployProcessWithEventSubProcess(userTaskName,\n                subProcTaskName, signalName);\n        final ProcessInstance procInstWithEventSubProc = getProcessAPI().startProcess(procWithEventSubProcess.getId());\n\n        // wait for first step of parent process and send a signal that will launch the event sub-process\n        waitForUserTask(procInstWithEventSubProc, userTaskName);\n        getProcessAPI().sendSignal(signalName);\n\n        // execute user task and wait the parent process to finish (state aborted)\n        waitForUserTaskAndExecuteIt(procInstWithEventSubProc, subProcTaskName, user);\n        waitForProcessToBeInState(procInstWithEventSubProc, ProcessInstanceState.ABORTED);\n        return procWithEventSubProcess;\n    }\n\n    private ProcessDefinition createArchivedProcessInstanceInStateCompletedAndCanceled() throws Exception {\n        final String userTaskName = \"step1\";\n        // deploy and start simple process\n        final ProcessDefinition simpleProcess = deployProcessWithHumanTask(userTaskName);\n        final ProcessInstance processInstanceToComplete = getProcessAPI().startProcess(simpleProcess.getId());\n\n        // execute user task and wait process to finish: the process will be in the state completed\n        waitForUserTaskAndExecuteIt(processInstanceToComplete, userTaskName, user);\n        waitForProcessToFinish(processInstanceToComplete);\n\n        // start another instance and cancel it: the process will be in the state canceled\n        final ProcessInstance processInstanceToCancel = getProcessAPI().startProcess(simpleProcess.getId());\n        waitForUserTask(processInstanceToCancel, userTaskName);\n        getProcessAPI().cancelProcessInstance(processInstanceToCancel.getId());\n        waitForProcessToBeInState(processInstanceToCancel, ProcessInstanceState.CANCELLED);\n        return simpleProcess;\n    }\n\n    private ProcessDefinition deployProcessWithHumanTask(final String userTaskName)\n            throws BonitaException, InvalidProcessDefinitionException {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"myProc\", \"1.0\");\n        builder.addActor(ACTOR_NAME);\n        builder.addUserTask(userTaskName, ACTOR_NAME);\n        return deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user);\n    }\n\n    private ProcessDefinition deployProcessWithEventSubProcess(final String parentUserTaskName,\n            final String suProcTaskName, final String signalName)\n            throws BonitaException, InvalidProcessDefinitionException {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"eventProc\", \"1.0\");\n        builder.addActor(ACTOR_NAME);\n        builder.addUserTask(parentUserTaskName, ACTOR_NAME);\n        final SubProcessDefinitionBuilder subProcessBuilder = builder.addSubProcess(\"sub\", true).getSubProcessBuilder();\n        subProcessBuilder.addStartEvent(\"start\").addSignalEventTrigger(signalName);\n        subProcessBuilder.addUserTask(suProcTaskName, ACTOR_NAME);\n        subProcessBuilder.addTransition(\"start\", suProcTaskName);\n        return deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user);\n    }\n\n    @Test\n    public void search_process_instances_in_all_states_retrieves_all_process_states() throws Exception {\n        // deploy and start a process\n        final ProcessDefinition simpleProcess = deployProcessWithHumanTask(\"step1\");\n        final ProcessInstance procInst = getProcessAPI().startProcess(simpleProcess.getId());\n\n        // execute it until the end\n        waitForUserTaskAndExecuteIt(procInst, \"step1\", user);\n        waitForProcessToFinish(procInst);\n\n        // search archived process instances: all states must be retrieved\n        final SearchResult<ArchivedProcessInstance> searchResult = searchArchivedProcessInstancesInAllStates(\n                procInst.getId());\n\n        assertEquals(3, searchResult.getCount());\n        final List<ArchivedProcessInstance> archivedProcesses = searchResult.getResult();\n        assertEquals(ProcessInstanceState.INITIALIZING.getId(), archivedProcesses.get(0).getStateId());\n        assertEquals(ProcessInstanceState.STARTED.getId(), archivedProcesses.get(1).getStateId());\n        assertEquals(ProcessInstanceState.COMPLETED.getId(), archivedProcesses.get(2).getStateId());\n\n        deleteProcessInstanceAndArchived(simpleProcess);\n        disableAndDeleteProcess(simpleProcess);\n    }\n\n    @Test\n    public void searchProcessInstancesWithAssigneeShouldReturnProcessWithAtLeastOneTaskAssignedToUser()\n            throws Exception {\n        final User otherUser = getIdentityAPI().createUser(\"newUser\", \"pepito\");\n\n        final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps(\n                asList(\"step1\"), asList(true));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n\n        final long processDefId = processDefinition.getId();\n        final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefId);\n        final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefId);\n        final ProcessInstance processInstance3 = getProcessAPI().startProcess(processDefId);\n        waitForUserTaskAndAssignIt(processInstance1, \"step1\", user);\n        waitForUserTaskAndAssignIt(processInstance2, \"step1\", user);\n        waitForUserTaskAndAssignIt(processInstance3, \"step1\", otherUser);\n\n        final SearchOptions searchOptions = new SearchOptionsBuilder(0, 10)\n                .filter(ProcessInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDefId)\n                .filter(ProcessInstanceSearchDescriptor.ASSIGNEE_ID, user.getId())\n                .sort(ProcessInstanceSearchDescriptor.ID, Order.ASC).done();\n        final SearchResult<ProcessInstance> processInstancesSearched = getProcessAPI()\n                .searchProcessInstances(searchOptions);\n        assertEquals(2, processInstancesSearched.getCount());\n\n        final List<ProcessInstance> processInstances = processInstancesSearched.getResult();\n        assertEquals(processInstance1, processInstances.get(0));\n        assertEquals(processInstance2, processInstances.get(1));\n\n        disableAndDeleteProcess(processDefinition);\n        deleteUser(otherUser);\n    }\n\n    private SearchResult<ArchivedProcessInstance> searchArchivedProcessInstancesInAllStates(\n            final long processInstanceId)\n            throws SearchException {\n        final SearchOptionsBuilder optionsBuilder = new SearchOptionsBuilder(0, 10);\n        optionsBuilder.filter(ArchivedProcessInstancesSearchDescriptor.SOURCE_OBJECT_ID, processInstanceId);\n        optionsBuilder.filter(ArchivedProcessInstancesSearchDescriptor.CALLER_ID, -1L);\n        optionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.ID, Order.ASC);\n        return getProcessAPI().searchArchivedProcessInstancesInAllStates(optionsBuilder.done());\n    }\n\n    @Test\n    public void searchOpenProcessInstancesFromStringIndex1AndUpdateIt() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithActorAndThreeHumanStepsAndThreeTransition();\n\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(designProcessDefinition).done();\n        final ProcessDefinition processDefinition = deployProcess(businessArchive);\n\n        addUserToFirstActorOfProcess(1, processDefinition);\n\n        getProcessAPI().enableProcess(processDefinition.getId());\n        final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance1, \"step2\");\n        getProcessAPI().startProcess(processDefinition.getId());\n\n        final ProcessInstanceUpdater updateDescriptor = new ProcessInstanceUpdater();\n        updateDescriptor.setStringIndex1(\"metsassa\");\n        ProcessInstance processInstance = getProcessAPI().updateProcessInstance(processInstance1.getId(),\n                updateDescriptor);\n        assertEquals(\"metsassa\", processInstance.getStringIndex1());\n\n        processInstance = getProcessAPI().updateProcessInstanceIndex(processInstance1.getId(), Index.FIRST,\n                \"metsassa1\");\n        assertEquals(\"metsassa1\", processInstance.getStringIndex1());\n\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.filter(ProcessInstanceSearchDescriptor.STRING_INDEX_1, \"metsassa1\");\n\n        final SearchResult<ProcessInstance> searchOpenProcessInstances = getProcessAPI()\n                .searchOpenProcessInstances(builder.done());\n        assertEquals(1, searchOpenProcessInstances.getCount());\n        final List<ProcessInstance> instances = searchOpenProcessInstances.getResult();\n        assertEquals(processInstance1, instances.get(0));\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void searchOpenProcessInstancesFromStringIndex2AndUpdateIt() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithActorAndThreeHumanStepsAndThreeTransition();\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(designProcessDefinition).done();\n        final ProcessDefinition processDefinition = deployProcess(businessArchive);\n        addUserToFirstActorOfProcess(1, processDefinition);\n        getProcessAPI().enableProcess(processDefinition.getId());\n        final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance1, \"step2\");\n        getProcessAPI().startProcess(processDefinition.getId());\n\n        final ProcessInstanceUpdater updateDescriptor = new ProcessInstanceUpdater();\n        updateDescriptor.setStringIndex2(\"metsassa\");\n        ProcessInstance processInstance = getProcessAPI().updateProcessInstance(processInstance1.getId(),\n                updateDescriptor);\n        assertEquals(\"metsassa\", processInstance.getStringIndex2());\n\n        processInstance = getProcessAPI().updateProcessInstanceIndex(processInstance1.getId(), Index.SECOND,\n                \"metsassa2\");\n        assertEquals(\"metsassa2\", processInstance.getStringIndex2());\n\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.filter(ProcessInstanceSearchDescriptor.STRING_INDEX_2, \"metsassa2\");\n\n        final SearchResult<ProcessInstance> searchOpenProcessInstances = getProcessAPI()\n                .searchOpenProcessInstances(builder.done());\n        assertEquals(1, searchOpenProcessInstances.getCount());\n        final List<ProcessInstance> instances = searchOpenProcessInstances.getResult();\n        assertEquals(processInstance1, instances.get(0));\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void searchOpenProcessInstancesFromStringIndex3AndUpdateIt() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithActorAndThreeHumanStepsAndThreeTransition();\n\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(designProcessDefinition).done();\n        final ProcessDefinition processDefinition = deployProcess(businessArchive);\n\n        addUserToFirstActorOfProcess(1, processDefinition);\n\n        getProcessAPI().enableProcess(processDefinition.getId());\n        final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance1, \"step2\");\n        getProcessAPI().startProcess(processDefinition.getId());\n\n        final ProcessInstanceUpdater updateDescriptor = new ProcessInstanceUpdater();\n        updateDescriptor.setStringIndex3(\"metsassa\");\n        ProcessInstance processInstance = getProcessAPI().updateProcessInstance(processInstance1.getId(),\n                updateDescriptor);\n        assertEquals(\"metsassa\", processInstance.getStringIndex3());\n\n        processInstance = getProcessAPI().updateProcessInstanceIndex(processInstance1.getId(), Index.THIRD,\n                \"metsassa3\");\n        assertEquals(\"metsassa3\", processInstance.getStringIndex3());\n\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.filter(ProcessInstanceSearchDescriptor.STRING_INDEX_3, \"metsassa3\");\n\n        final SearchResult<ProcessInstance> searchOpenProcessInstances = getProcessAPI()\n                .searchOpenProcessInstances(builder.done());\n        assertEquals(1, searchOpenProcessInstances.getCount());\n        final List<ProcessInstance> instances = searchOpenProcessInstances.getResult();\n        assertEquals(processInstance1, instances.get(0));\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void searchOpenProcessInstancesFromStringIndex4AndUpdateIt() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithActorAndThreeHumanStepsAndThreeTransition();\n\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(designProcessDefinition).done();\n        final ProcessDefinition processDefinition = deployProcess(businessArchive);\n\n        addUserToFirstActorOfProcess(1, processDefinition);\n\n        getProcessAPI().enableProcess(processDefinition.getId());\n        final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance1, \"step2\");\n        getProcessAPI().startProcess(processDefinition.getId());\n\n        final ProcessInstanceUpdater updateDescriptor = new ProcessInstanceUpdater();\n        updateDescriptor.setStringIndex4(\"metsassa\");\n        ProcessInstance processInstance = getProcessAPI().updateProcessInstance(processInstance1.getId(),\n                updateDescriptor);\n        assertEquals(\"metsassa\", processInstance.getStringIndex4());\n\n        processInstance = getProcessAPI().updateProcessInstanceIndex(processInstance1.getId(), Index.FOURTH,\n                \"metsassa4\");\n        assertEquals(\"metsassa4\", processInstance.getStringIndex4());\n\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.filter(ProcessInstanceSearchDescriptor.STRING_INDEX_4, \"metsassa4\");\n\n        final SearchResult<ProcessInstance> searchOpenProcessInstances = getProcessAPI()\n                .searchOpenProcessInstances(builder.done());\n        assertEquals(1, searchOpenProcessInstances.getCount());\n        final List<ProcessInstance> instances = searchOpenProcessInstances.getResult();\n        assertEquals(processInstance1, instances.get(0));\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void searchOpenProcessInstancesFromStringIndex5AndUpdateIt() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = BuildTestUtil\n                .buildProcessDefinitionWithActorAndThreeHumanStepsAndThreeTransition();\n\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(designProcessDefinition).done();\n        final ProcessDefinition processDefinition = deployProcess(businessArchive);\n\n        addUserToFirstActorOfProcess(1, processDefinition);\n\n        getProcessAPI().enableProcess(processDefinition.getId());\n        final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance1, \"step2\");\n        getProcessAPI().startProcess(processDefinition.getId());\n\n        final ProcessInstanceUpdater updateDescriptor = new ProcessInstanceUpdater();\n        updateDescriptor.setStringIndex5(\"metsassa\");\n        ProcessInstance processInstance = getProcessAPI().updateProcessInstance(processInstance1.getId(),\n                updateDescriptor);\n        assertEquals(\"metsassa\", processInstance.getStringIndex5());\n\n        processInstance = getProcessAPI().updateProcessInstanceIndex(processInstance1.getId(), Index.FIFTH,\n                \"metsassa5\");\n        assertEquals(\"metsassa5\", processInstance.getStringIndex5());\n\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.filter(ProcessInstanceSearchDescriptor.STRING_INDEX_5, \"metsassa5\");\n\n        final SearchResult<ProcessInstance> searchOpenProcessInstances = getProcessAPI()\n                .searchOpenProcessInstances(builder.done());\n        assertEquals(1, searchOpenProcessInstances.getCount());\n        final List<ProcessInstance> instances = searchOpenProcessInstances.getResult();\n        assertEquals(processInstance1, instances.get(0));\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void searchTermOnIndexOnProcessInstances() throws Exception {\n        final User user1 = createUser(\"john1\", \"bpm\");\n        final User user2 = createUser(\"john2\", \"bpm\");\n        final User user3 = createUser(\"john3\", \"bpm\");\n        final User user4 = createUser(\"john4\", \"bpm\");\n\n        final DesignProcessDefinition designProcessDefinition1 = createProcessDefinition(\"3\", true, \"value1\", \"value2\",\n                \"value3\", \"value4\", \"value5\").done();\n        final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition1,\n                ACTOR_NAME, user1);\n        final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition1.getId());\n        waitForUserTask(processInstance1, \"step1\");\n        logout();\n\n        loginOnDefaultTenantWith(\"john1\", \"bpm\");\n        final DesignProcessDefinition designProcessDefinition2 = createProcessDefinition(\"2\", true, \"value2\", \"value4\",\n                \"value1\", \"value5\", \"value3\").done();\n        final ProcessDefinition processDefinition2 = deployAndEnableProcessWithActor(designProcessDefinition2,\n                ACTOR_NAME, user2);\n        final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinition2.getId());\n        waitForUserTask(processInstance2, \"step1\");\n        logout();\n\n        loginOnDefaultTenantWith(\"john3\", \"bpm\");\n        final DesignProcessDefinition designProcessDefinition3 = createProcessDefinition(\"5\", true, \"value4\", \"value3\",\n                \"value5\", \"value2\", \"value1\").done();\n        final ProcessDefinition processDefinition3 = deployAndEnableProcessWithActor(designProcessDefinition3,\n                ACTOR_NAME, user3);\n        final ProcessInstance processInstance3 = getProcessAPI().startProcess(processDefinition3.getId());\n        waitForUserTask(processInstance3, \"step1\");\n        logout();\n\n        loginOnDefaultTenantWith(\"john2\", \"bpm\");\n        final DesignProcessDefinition designProcessDefinition4 = createProcessDefinition(\"4\", true, \"value5\", \"value1\",\n                \"value4\", \"value3\", \"value2\").done();\n        final ProcessDefinition processDefinition4 = deployAndEnableProcessWithActor(designProcessDefinition4,\n                ACTOR_NAME, user4);\n        final ProcessInstance processInstance4 = getProcessAPI().startProcess(processDefinition4.getId());\n        waitForUserTask(processInstance4, \"step1\");\n        logout();\n\n        loginOnDefaultTenantWith(\"john4\", \"bpm\");\n        final DesignProcessDefinition designProcessDefinition5 = createProcessDefinition(\"1\", true, \"value3\", \"value5\",\n                \"value2\", \"value1\", \"value4\").done();\n        final ProcessDefinition processDefinition5 = deployAndEnableProcessWithActor(designProcessDefinition5,\n                ACTOR_NAME, user1);\n        final ProcessInstance processInstance5 = getProcessAPI().startProcess(processDefinition5.getId());\n        waitForUserTask(processInstance5, \"step1\");\n\n        // Search term for STRING_INDEX\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.sort(org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor.ID, Order.ASC);\n        searchOptionsBuilder.searchTerm(\"value1\");\n        final List<ProcessInstance> archivedProcessInstances = getProcessAPI()\n                .searchOpenProcessInstances(searchOptionsBuilder.done()).getResult();\n        assertNotNull(archivedProcessInstances);\n        assertEquals(5, archivedProcessInstances.size());\n        assertEquals(processInstance1.getId(), archivedProcessInstances.get(0).getId());\n        assertEquals(processInstance2.getId(), archivedProcessInstances.get(1).getId());\n        assertEquals(processInstance3.getId(), archivedProcessInstances.get(2).getId());\n        assertEquals(processInstance4.getId(), archivedProcessInstances.get(3).getId());\n        assertEquals(processInstance5.getId(), archivedProcessInstances.get(4).getId());\n\n        disableAndDeleteProcess(processDefinition1, processDefinition2, processDefinition3, processDefinition4,\n                processDefinition5);\n        deleteUsers(user1, user2, user3, user4);\n    }\n\n    @Test\n    public void searchArchivedProcessInstances() throws Exception {\n        final User user1 = createUser(\"john1\", \"bpm\");\n        final User user2 = createUser(\"john2\", \"bpm\");\n        final User user3 = createUser(\"john3\", \"bpm\");\n        final User user4 = createUser(\"john4\", \"bpm\");\n\n        final DesignProcessDefinition designProcessDefinition1 = createProcessDefinition(\"3\", false, \"value1\", \"value2\",\n                \"value3\", \"value4\", \"value5\").done();\n        // final ProcessDefinition processDefinition1 = deployAndEnableWithActor(designProcessDefinition1, delivery, user1);\n        final ProcessDefinition processDefinition1 = deployAndEnableProcess(designProcessDefinition1);\n        final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition1.getId());\n        waitForProcessToFinish(processInstance1);\n        logout();\n\n        loginOnDefaultTenantWith(\"john1\", \"bpm\");\n        final DesignProcessDefinition designProcessDefinition2 = createProcessDefinition(\"2\", false, \"value2\", \"value4\",\n                \"value1\", \"value5\", \"value3\").done();\n        final ProcessDefinition processDefinition2 = deployAndEnableProcess(designProcessDefinition2);\n        final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinition2.getId());\n        waitForProcessToFinish(processInstance2);\n        logout();\n\n        loginOnDefaultTenantWith(\"john3\", \"bpm\");\n        final DesignProcessDefinition designProcessDefinition3 = createProcessDefinition(\"5\", false, \"value4\", \"value3\",\n                \"value5\", \"value2\", \"value1\").done();\n        final ProcessDefinition processDefinition3 = deployAndEnableProcess(designProcessDefinition3);\n        final ProcessInstance processInstance3 = getProcessAPI().startProcess(processDefinition3.getId());\n        waitForProcessToFinish(processInstance3);\n        logout();\n\n        loginOnDefaultTenantWith(\"john2\", \"bpm\");\n        final DesignProcessDefinition designProcessDefinition4 = createProcessDefinition(\"4\", false, \"value5\", \"value1\",\n                \"value4\", \"value3\", \"value2\").done();\n        final ProcessDefinition processDefinition4 = deployAndEnableProcess(designProcessDefinition4);\n        final ProcessInstance processInstance4 = getProcessAPI().startProcess(processDefinition4.getId());\n        waitForProcessToFinish(processInstance4);\n        logout();\n\n        loginOnDefaultTenantWith(\"john4\", \"bpm\");\n        final DesignProcessDefinition designProcessDefinition5 = createProcessDefinition(\"1\", false, \"value3\", \"value5\",\n                \"value2\", \"value1\", \"value4\").done();\n        final ProcessDefinition processDefinition5 = deployAndEnableProcess(designProcessDefinition5);\n        final ProcessInstance processInstance5 = getProcessAPI().startProcess(processDefinition5.getId());\n        waitForProcessToFinish(processInstance5);\n\n        // Order by ARCHIVE_DATE\n        SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.ARCHIVE_DATE, Order.ASC);\n        final SearchResult<ArchivedProcessInstance> result = getProcessAPI()\n                .searchArchivedProcessInstances(searchOptionsBuilder.done());\n        assertEquals(5, result.getCount());\n        List<ArchivedProcessInstance> archivedProcessInstances = result.getResult();\n        assertNotNull(archivedProcessInstances);\n        assertEquals(5, archivedProcessInstances.size());\n        assertEquals(processInstance1.getId(), archivedProcessInstances.get(0).getSourceObjectId());\n        assertEquals(processInstance2.getId(), archivedProcessInstances.get(1).getSourceObjectId());\n        assertEquals(processInstance3.getId(), archivedProcessInstances.get(2).getSourceObjectId());\n        assertEquals(processInstance4.getId(), archivedProcessInstances.get(3).getSourceObjectId());\n        assertEquals(processInstance5.getId(), archivedProcessInstances.get(4).getSourceObjectId());\n\n        // Order by END_DATE\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.END_DATE, Order.ASC);\n        archivedProcessInstances = getProcessAPI().searchArchivedProcessInstances(searchOptionsBuilder.done())\n                .getResult();\n        assertNotNull(archivedProcessInstances);\n        assertEquals(5, archivedProcessInstances.size());\n        assertEquals(processInstance1.getId(), archivedProcessInstances.get(0).getSourceObjectId());\n        assertEquals(processInstance2.getId(), archivedProcessInstances.get(1).getSourceObjectId());\n        assertEquals(processInstance3.getId(), archivedProcessInstances.get(2).getSourceObjectId());\n        assertEquals(processInstance4.getId(), archivedProcessInstances.get(3).getSourceObjectId());\n        assertEquals(processInstance5.getId(), archivedProcessInstances.get(4).getSourceObjectId());\n\n        // Order by ID\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.ID, Order.ASC);\n        archivedProcessInstances = getProcessAPI().searchArchivedProcessInstances(searchOptionsBuilder.done())\n                .getResult();\n        assertNotNull(archivedProcessInstances);\n        assertEquals(5, archivedProcessInstances.size());\n        assertEquals(processInstance1.getId(), archivedProcessInstances.get(0).getSourceObjectId());\n        assertEquals(processInstance2.getId(), archivedProcessInstances.get(1).getSourceObjectId());\n        assertEquals(processInstance3.getId(), archivedProcessInstances.get(2).getSourceObjectId());\n        assertEquals(processInstance4.getId(), archivedProcessInstances.get(3).getSourceObjectId());\n        assertEquals(processInstance5.getId(), archivedProcessInstances.get(4).getSourceObjectId());\n\n        // Order by LAST_UPDATE\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.LAST_UPDATE, Order.ASC);\n        archivedProcessInstances = getProcessAPI().searchArchivedProcessInstances(searchOptionsBuilder.done())\n                .getResult();\n        assertNotNull(archivedProcessInstances);\n        assertEquals(5, archivedProcessInstances.size());\n        assertEquals(processInstance1.getId(), archivedProcessInstances.get(0).getSourceObjectId());\n        assertEquals(processInstance2.getId(), archivedProcessInstances.get(1).getSourceObjectId());\n        assertEquals(processInstance3.getId(), archivedProcessInstances.get(2).getSourceObjectId());\n        assertEquals(processInstance4.getId(), archivedProcessInstances.get(3).getSourceObjectId());\n        assertEquals(processInstance5.getId(), archivedProcessInstances.get(4).getSourceObjectId());\n\n        // Order by NAME\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.NAME, Order.ASC);\n        archivedProcessInstances = getProcessAPI().searchArchivedProcessInstances(searchOptionsBuilder.done())\n                .getResult();\n        assertNotNull(archivedProcessInstances);\n        assertEquals(5, archivedProcessInstances.size());\n        assertEquals(processInstance5.getId(), archivedProcessInstances.get(0).getSourceObjectId());\n        assertEquals(processInstance2.getId(), archivedProcessInstances.get(1).getSourceObjectId());\n        assertEquals(processInstance1.getId(), archivedProcessInstances.get(2).getSourceObjectId());\n        assertEquals(processInstance4.getId(), archivedProcessInstances.get(3).getSourceObjectId());\n        assertEquals(processInstance3.getId(), archivedProcessInstances.get(4).getSourceObjectId());\n\n        // Order by SOURCE_OBJECT_ID\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.SOURCE_OBJECT_ID, Order.ASC);\n        archivedProcessInstances = getProcessAPI().searchArchivedProcessInstances(searchOptionsBuilder.done())\n                .getResult();\n        assertNotNull(archivedProcessInstances);\n        assertEquals(5, archivedProcessInstances.size());\n        assertEquals(processInstance1.getId(), archivedProcessInstances.get(0).getSourceObjectId());\n        assertEquals(processInstance2.getId(), archivedProcessInstances.get(1).getSourceObjectId());\n        assertEquals(processInstance3.getId(), archivedProcessInstances.get(2).getSourceObjectId());\n        assertEquals(processInstance4.getId(), archivedProcessInstances.get(3).getSourceObjectId());\n        assertEquals(processInstance5.getId(), archivedProcessInstances.get(4).getSourceObjectId());\n\n        // Order by START_DATE\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.START_DATE, Order.ASC);\n        archivedProcessInstances = getProcessAPI().searchArchivedProcessInstances(searchOptionsBuilder.done())\n                .getResult();\n        assertNotNull(archivedProcessInstances);\n        assertEquals(5, archivedProcessInstances.size());\n        assertEquals(processInstance1.getId(), archivedProcessInstances.get(0).getSourceObjectId());\n        assertEquals(processInstance2.getId(), archivedProcessInstances.get(1).getSourceObjectId());\n        assertEquals(processInstance3.getId(), archivedProcessInstances.get(2).getSourceObjectId());\n        assertEquals(processInstance4.getId(), archivedProcessInstances.get(3).getSourceObjectId());\n        assertEquals(processInstance5.getId(), archivedProcessInstances.get(4).getSourceObjectId());\n\n        // Order by STARTED_BY\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.STARTED_BY, Order.ASC);\n        archivedProcessInstances = getProcessAPI().searchArchivedProcessInstances(searchOptionsBuilder.done())\n                .getResult();\n        assertNotNull(archivedProcessInstances);\n        assertEquals(5, archivedProcessInstances.size());\n        assertEquals(processInstance1.getId(), archivedProcessInstances.get(0).getSourceObjectId());\n        assertEquals(processInstance2.getId(), archivedProcessInstances.get(1).getSourceObjectId());\n        assertEquals(processInstance4.getId(), archivedProcessInstances.get(2).getSourceObjectId());\n        assertEquals(processInstance3.getId(), archivedProcessInstances.get(3).getSourceObjectId());\n        assertEquals(processInstance5.getId(), archivedProcessInstances.get(4).getSourceObjectId());\n\n        // Order by STATE_ID\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.STATE_ID, Order.ASC);\n        archivedProcessInstances = getProcessAPI().searchArchivedProcessInstances(searchOptionsBuilder.done())\n                .getResult();\n        assertNotNull(archivedProcessInstances);\n        assertEquals(5, archivedProcessInstances.size());\n        assertEquals(6, archivedProcessInstances.get(0).getStateId());\n        assertEquals(6, archivedProcessInstances.get(1).getStateId());\n        assertEquals(6, archivedProcessInstances.get(2).getStateId());\n        assertEquals(6, archivedProcessInstances.get(3).getStateId());\n        assertEquals(6, archivedProcessInstances.get(4).getStateId());\n\n        // Order by STRING_INDEX_1\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.STRING_INDEX_1, Order.ASC);\n        archivedProcessInstances = getProcessAPI()\n                .searchArchivedProcessInstances(searchOptionsBuilder.done()).getResult();\n        assertNotNull(archivedProcessInstances);\n        assertEquals(5, archivedProcessInstances.size());\n        assertEquals(processInstance1.getId(), archivedProcessInstances.get(0).getSourceObjectId());\n        assertEquals(processInstance2.getId(), archivedProcessInstances.get(1).getSourceObjectId());\n        assertEquals(processInstance5.getId(), archivedProcessInstances.get(2).getSourceObjectId());\n        assertEquals(processInstance3.getId(), archivedProcessInstances.get(3).getSourceObjectId());\n        assertEquals(processInstance4.getId(), archivedProcessInstances.get(4).getSourceObjectId());\n\n        // Order by STRING_INDEX_2\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.STRING_INDEX_2, Order.ASC);\n        archivedProcessInstances = getProcessAPI().searchArchivedProcessInstances(searchOptionsBuilder.done())\n                .getResult();\n        assertNotNull(archivedProcessInstances);\n        assertEquals(5, archivedProcessInstances.size());\n        assertEquals(processInstance4.getId(), archivedProcessInstances.get(0).getSourceObjectId());\n        assertEquals(processInstance1.getId(), archivedProcessInstances.get(1).getSourceObjectId());\n        assertEquals(processInstance3.getId(), archivedProcessInstances.get(2).getSourceObjectId());\n        assertEquals(processInstance2.getId(), archivedProcessInstances.get(3).getSourceObjectId());\n        assertEquals(processInstance5.getId(), archivedProcessInstances.get(4).getSourceObjectId());\n\n        // Order by STRING_INDEX_3\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.STRING_INDEX_3, Order.ASC);\n        archivedProcessInstances = getProcessAPI().searchArchivedProcessInstances(searchOptionsBuilder.done())\n                .getResult();\n        assertNotNull(archivedProcessInstances);\n        assertEquals(5, archivedProcessInstances.size());\n        assertEquals(processInstance2.getId(), archivedProcessInstances.get(0).getSourceObjectId());\n        assertEquals(processInstance5.getId(), archivedProcessInstances.get(1).getSourceObjectId());\n        assertEquals(processInstance1.getId(), archivedProcessInstances.get(2).getSourceObjectId());\n        assertEquals(processInstance4.getId(), archivedProcessInstances.get(3).getSourceObjectId());\n        assertEquals(processInstance3.getId(), archivedProcessInstances.get(4).getSourceObjectId());\n\n        // Order by STRING_INDEX_4\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.STRING_INDEX_4, Order.ASC);\n        archivedProcessInstances = getProcessAPI().searchArchivedProcessInstances(searchOptionsBuilder.done())\n                .getResult();\n        assertNotNull(archivedProcessInstances);\n        assertEquals(5, archivedProcessInstances.size());\n        assertEquals(processInstance5.getId(), archivedProcessInstances.get(0).getSourceObjectId());\n        assertEquals(processInstance3.getId(), archivedProcessInstances.get(1).getSourceObjectId());\n        assertEquals(processInstance4.getId(), archivedProcessInstances.get(2).getSourceObjectId());\n        assertEquals(processInstance1.getId(), archivedProcessInstances.get(3).getSourceObjectId());\n        assertEquals(processInstance2.getId(), archivedProcessInstances.get(4).getSourceObjectId());\n\n        // Order by STRING_INDEX_5\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.STRING_INDEX_5, Order.ASC);\n        archivedProcessInstances = getProcessAPI().searchArchivedProcessInstances(searchOptionsBuilder.done())\n                .getResult();\n        assertNotNull(archivedProcessInstances);\n        assertEquals(5, archivedProcessInstances.size());\n        assertEquals(processInstance3.getId(), archivedProcessInstances.get(0).getSourceObjectId());\n        assertEquals(processInstance4.getId(), archivedProcessInstances.get(1).getSourceObjectId());\n        assertEquals(processInstance2.getId(), archivedProcessInstances.get(2).getSourceObjectId());\n        assertEquals(processInstance5.getId(), archivedProcessInstances.get(3).getSourceObjectId());\n        assertEquals(processInstance1.getId(), archivedProcessInstances.get(4).getSourceObjectId());\n\n        // Search term for STRING_INDEX\n        searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.sort(org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor.ID,\n                Order.ASC);\n        searchOptionsBuilder.searchTerm(\"value1\");\n        archivedProcessInstances = getProcessAPI().searchArchivedProcessInstances(searchOptionsBuilder.done())\n                .getResult();\n        assertNotNull(archivedProcessInstances);\n        assertEquals(5, archivedProcessInstances.size());\n        assertEquals(processInstance1.getId(), archivedProcessInstances.get(0).getSourceObjectId());\n        assertEquals(processInstance2.getId(), archivedProcessInstances.get(1).getSourceObjectId());\n        assertEquals(processInstance3.getId(), archivedProcessInstances.get(2).getSourceObjectId());\n        assertEquals(processInstance4.getId(), archivedProcessInstances.get(3).getSourceObjectId());\n        assertEquals(processInstance5.getId(), archivedProcessInstances.get(4).getSourceObjectId());\n\n        disableAndDeleteProcess(processDefinition1, processDefinition2, processDefinition3, processDefinition4,\n                processDefinition5);\n        deleteUsers(user1, user2, user3, user4);\n    }\n\n    private ProcessDefinitionBuilder createProcessDefinition(final String processName, final boolean withUserTask,\n            final String stringIndex1, final String stringIndex2, final String stringIndex3, final String stringIndex4,\n            final String stringIndex5) throws InvalidExpressionException {\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(processName, \"17.3\");\n        designProcessDefinition.addDescription(\"Delivery all day and night long\");\n        if (withUserTask) {\n            designProcessDefinition.addActor(ACTOR_NAME);\n            designProcessDefinition.addUserTask(\"step1\", ACTOR_NAME);\n        } else {\n            designProcessDefinition.addAutomaticTask(\"step1\");\n        }\n\n        if (!StringUtils.isBlank(stringIndex1)) {\n            designProcessDefinition.setStringIndex(1, \"label1\",\n                    new ExpressionBuilder().createConstantStringExpression(stringIndex1));\n        }\n        if (!StringUtils.isBlank(stringIndex2)) {\n            designProcessDefinition.setStringIndex(2, \"label2\",\n                    new ExpressionBuilder().createConstantStringExpression(stringIndex2));\n        }\n        if (!StringUtils.isBlank(stringIndex3)) {\n            designProcessDefinition.setStringIndex(3, \"label3\",\n                    new ExpressionBuilder().createConstantStringExpression(stringIndex3));\n        }\n        if (!StringUtils.isBlank(stringIndex4)) {\n            designProcessDefinition.setStringIndex(4, \"label4\",\n                    new ExpressionBuilder().createConstantStringExpression(stringIndex4));\n        }\n        if (!StringUtils.isBlank(stringIndex5)) {\n            designProcessDefinition.setStringIndex(5, \"label5\",\n                    new ExpressionBuilder().createConstantStringExpression(stringIndex5));\n        }\n        return designProcessDefinition;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/search/SearchUncategorizedProcessDeploymentInfosCanBeStartedByIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.TestWithTechnicalUser;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.category.Category;\nimport org.bonitasoft.engine.bpm.category.CategoryCriterion;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoSearchDescriptor;\nimport org.bonitasoft.engine.identity.Group;\nimport org.bonitasoft.engine.identity.Role;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.identity.UserMembership;\nimport org.bonitasoft.engine.test.BuildTestUtil;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Celine Souchet\n */\npublic class SearchUncategorizedProcessDeploymentInfosCanBeStartedByIT extends TestWithTechnicalUser {\n\n    private List<ProcessDefinition> enabledProcessDefinitions;\n\n    private List<ProcessDefinition> disabledProcessDefinitions;\n\n    private List<User> users = null;\n\n    private List<Category> categories;\n\n    private List<Group> groups = null;\n\n    private List<Role> roles = null;\n\n    private List<UserMembership> userMemberships = null;\n\n    @Override\n    @Before\n    public void before() throws Exception {\n        super.before();\n        categories = new ArrayList<Category>();\n\n        users = new ArrayList<User>(6);\n        users.add(createUser(\"chicobento\", \"bpm\"));\n        users.add(createUser(\"cebolinha\", \"bpm\"));\n        users.add(createUser(\"cascao\", \"bpm\"));\n        users.add(createUser(\"magali\", \"bpm\"));\n        users.add(createUser(\"monica\", \"bpm\"));\n        users.add(createUser(\"dorinha\", \"bpm\"));\n\n        groups = new ArrayList<Group>(2);\n        groups.add(createGroup(\"group1\"));\n        groups.add(createGroup(\"group2\"));\n\n        roles = new ArrayList<Role>(2);\n        roles.add(createRole(\"role1\"));\n        roles.add(createRole(\"role2\"));\n\n        userMemberships = new ArrayList<UserMembership>(3);\n        userMemberships.add(\n                getIdentityAPI().addUserMembership(users.get(3).getId(), groups.get(0).getId(), roles.get(0).getId()));\n        userMemberships.add(\n                getIdentityAPI().addUserMembership(users.get(4).getId(), groups.get(0).getId(), roles.get(1).getId()));\n        userMemberships.add(\n                getIdentityAPI().addUserMembership(users.get(5).getId(), groups.get(1).getId(), roles.get(0).getId()));\n\n        enabledProcessDefinitions = new ArrayList<ProcessDefinition>(4);\n        disabledProcessDefinitions = new ArrayList<ProcessDefinition>(1);\n        createProcessesDefForSearchProcessUserCanStart();\n    }\n\n    @Override\n    @After\n    public void after() throws Exception {\n        disableAndDeleteProcess(enabledProcessDefinitions);\n        deleteProcess(disabledProcessDefinitions);\n        deleteCategories(categories);\n        deleteUserMemberships(userMemberships);\n        deleteUsers(users);\n        deleteGroups(groups);\n        deleteRoles(roles);\n        super.after();\n    }\n\n    @Test\n    public void searchUncategorizedProcessDefinitions() throws Exception {\n        loginOnDefaultTenantWith(\"chicobento\", \"bpm\");\n\n        // add categories to processDefinition1\n        categories.add(getProcessAPI().createCategory(\"category1\", \"categoryDescription1\"));\n        categories.add(getProcessAPI().createCategory(\"category2\", \"categoryDescription2\"));\n        categories.add(getProcessAPI().createCategory(\"category3\", \"categoryDescription3\"));\n        final ArrayList<Long> categoryIds = new ArrayList<Long>();\n        categoryIds.add(categories.get(0).getId());\n        categoryIds.add(categories.get(1).getId());\n        categoryIds.add(categories.get(2).getId());\n        getProcessAPI().addCategoriesToProcess(enabledProcessDefinitions.get(0).getId(), categoryIds);\n        categories = getProcessAPI().getCategoriesOfProcessDefinition(enabledProcessDefinitions.get(0).getId(), 0, 10,\n                CategoryCriterion.NAME_ASC);\n        assertTrue(!categories.isEmpty());\n\n        // Get all process definitions:\n        SearchOptionsBuilder optsBuilder = new SearchOptionsBuilder(0, 5);\n        optsBuilder.sort(ProcessDeploymentInfoSearchDescriptor.DEPLOYMENT_DATE, Order.ASC);\n        SearchResult<ProcessDeploymentInfo> searchRes0 = getProcessAPI()\n                .searchProcessDeploymentInfos(optsBuilder.done());\n        assertEquals(8, searchRes0.getCount());\n        assertEquals(enabledProcessDefinitions.get(1).getId(), searchRes0.getResult().get(1).getProcessId());\n        assertEquals(enabledProcessDefinitions.get(0).getId(), searchRes0.getResult().get(0).getProcessId());\n\n        // Get all process definitions with no category associated:\n        optsBuilder = new SearchOptionsBuilder(0, 7);\n        optsBuilder.sort(ProcessDeploymentInfoSearchDescriptor.DEPLOYMENT_DATE, Order.ASC);\n        searchRes0 = getProcessAPI().searchUncategorizedProcessDeploymentInfos(optsBuilder.done());\n        assertEquals(7, searchRes0.getCount());\n        assertEquals(enabledProcessDefinitions.get(1).getId(), searchRes0.getResult().get(0).getProcessId());\n    }\n\n    @Test\n    public void searchUncategorizedProcessDefinitionsUserCanStartFromGroup() throws Exception {\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 5)\n                .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC);\n        final SearchResult<ProcessDeploymentInfo> searchRes = getProcessAPI()\n                .searchProcessDeploymentInfosCanBeStartedBy(users.get(4).getId(),\n                        searchOptionsBuilder.done());\n        assertEquals(1, searchRes.getCount());\n        assertEquals(enabledProcessDefinitions.get(4).getName(), searchRes.getResult().get(0).getName());\n    }\n\n    @Test\n    public void searchUncategorizedProcessDefinitionsUserCanStartFromRole() throws Exception {\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 5)\n                .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC);\n        final SearchResult<ProcessDeploymentInfo> searchRes = getProcessAPI()\n                .searchProcessDeploymentInfosCanBeStartedBy(users.get(5).getId(),\n                        searchOptionsBuilder.done());\n        assertEquals(1, searchRes.getCount());\n        assertEquals(enabledProcessDefinitions.get(5).getName(), searchRes.getResult().get(0).getName());\n    }\n\n    @Test\n    public void searchUncategorizedProcessDefinitionsUserCanStartFromRoleAndGroup() throws Exception {\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 5)\n                .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC);\n        final SearchResult<ProcessDeploymentInfo> searchRes = getProcessAPI()\n                .searchProcessDeploymentInfosCanBeStartedBy(users.get(3).getId(),\n                        searchOptionsBuilder.done());\n        assertEquals(3, searchRes.getCount());\n        assertEquals(enabledProcessDefinitions.get(4).getName(), searchRes.getResult().get(0).getName()); // from group\n        assertEquals(enabledProcessDefinitions.get(5).getName(), searchRes.getResult().get(1).getName()); // from role\n        assertEquals(enabledProcessDefinitions.get(6).getName(), searchRes.getResult().get(2).getName()); // from role and group\n    }\n\n    @Test\n    public void searchUncategorizedProcessDefinitionsUserCanStartWithSearchTearm() throws Exception {\n        // test term\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 5)\n                .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC);\n        searchOptionsBuilder.searchTerm(\"My_Process2\"); // use name as term\n        final SearchResult<ProcessDeploymentInfo> searchRes = getProcessAPI()\n                .searchProcessDeploymentInfosCanBeStartedBy(users.get(1).getId(),\n                        searchOptionsBuilder.done());\n        assertEquals(1, searchRes.getCount());\n        assertEquals(enabledProcessDefinitions.get(1).getId(), searchRes.getResult().get(0).getProcessId());\n    }\n\n    @Test\n    public void searchUncategorizedProcessDefinitionsUserCanStart() throws Exception {\n        // test uncategorized process definitions.\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 5)\n                .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC);\n        SearchResult<ProcessDeploymentInfo> searchRes = getProcessAPI()\n                .searchUncategorizedProcessDeploymentInfosCanBeStartedBy(users.get(0).getId(),\n                        searchOptionsBuilder.done());\n        assertEquals(1, searchRes.getCount());\n        assertEquals(enabledProcessDefinitions.get(0).getName(), searchRes.getResult().get(0).getName());\n\n        searchRes = getProcessAPI().searchUncategorizedProcessDeploymentInfosCanBeStartedBy(users.get(1).getId(),\n                searchOptionsBuilder.done());\n        assertEquals(2, searchRes.getCount());\n        assertEquals(enabledProcessDefinitions.get(1).getName(), searchRes.getResult().get(0).getName());\n        assertEquals(enabledProcessDefinitions.get(2).getName(), searchRes.getResult().get(1).getName());\n\n        // user associated to a process without actor initiator\n        searchRes = getProcessAPI().searchUncategorizedProcessDeploymentInfosCanBeStartedBy(users.get(2).getId(),\n                searchOptionsBuilder.done());\n        assertEquals(0, searchRes.getCount());\n    }\n\n    private void createProcessesDefForSearchProcessUserCanStart() throws Exception {\n        final String actor1 = ACTOR_NAME;\n        final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process1\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor1, true);\n        final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition1, actor1,\n                users.get(0));\n        enabledProcessDefinitions.add(processDefinition1);\n\n        // create process2\n        final String actor2 = \"Actor2\";\n        final DesignProcessDefinition designProcessDefinition2 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process2\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor2, true);\n        final ProcessDefinition processDefinition2 = deployAndEnableProcessWithActor(designProcessDefinition2, actor2,\n                users.get(1));\n        enabledProcessDefinitions.add(processDefinition2);\n\n        final DesignProcessDefinition designProcessDefinition3 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process3\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor2, true);\n        final ProcessDefinition processDefinition3 = deployAndEnableProcessWithActor(designProcessDefinition3, actor2,\n                users.get(1));\n        enabledProcessDefinitions.add(processDefinition3);\n\n        // process not enabled\n        final DesignProcessDefinition designProcessDefinition4 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process4\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor2, true);\n        final ProcessDefinition processDefinition4 = getProcessAPI().deploy(\n                new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(designProcessDefinition4)\n                        .done());\n        getProcessAPI().addUserToActor(actor2, processDefinition4, users.get(1).getId());\n        disabledProcessDefinitions.add(processDefinition4);\n\n        // process without actor initiator\n        final DesignProcessDefinition designProcessDefinition5 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process5\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor2, false);\n        final ProcessDefinition processDefinition5 = deployAndEnableProcessWithActor(designProcessDefinition5, actor2,\n                users.get(2));\n        enabledProcessDefinitions.add(processDefinition5);\n\n        // actor initiator is a group\n        final DesignProcessDefinition designProcessDefinition6 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process6\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor2, true);\n        final ProcessDefinition processDefinition6 = deployAndEnableProcessWithActor(designProcessDefinition6, actor2,\n                groups.get(0));\n        enabledProcessDefinitions.add(processDefinition6);\n\n        // actor initiator is a role\n        final DesignProcessDefinition designProcessDefinition7 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process7\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor2, true);\n        final ProcessDefinition processDefinition7 = deployAndEnableProcessWithActor(designProcessDefinition7, actor2,\n                roles.get(0));\n        enabledProcessDefinitions.add(processDefinition7);\n\n        // actor initiator is a membership\n        final DesignProcessDefinition designProcessDefinition8 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(\"My_Process8\", \"1.0\",\n                        Arrays.asList(\"step1\", \"step2\"), Arrays.asList(true, true), actor2, true);\n        final ProcessDefinition processDefinition8 = deployAndEnableProcessWithActor(designProcessDefinition8, actor2,\n                roles.get(0), groups.get(0));\n        enabledProcessDefinitions.add(processDefinition8);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/supervisor/ProcessSupervisedIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.supervisor;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.*;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.TestWithTechnicalUser;\nimport org.bonitasoft.engine.bpm.category.Category;\nimport org.bonitasoft.engine.bpm.category.CategoryCriterion;\nimport org.bonitasoft.engine.bpm.document.ArchivedDocument;\nimport org.bonitasoft.engine.bpm.document.ArchivedDocumentsSearchDescriptor;\nimport org.bonitasoft.engine.bpm.document.Document;\nimport org.bonitasoft.engine.bpm.document.DocumentsSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.TaskPriority;\nimport org.bonitasoft.engine.bpm.flownode.UserTaskInstance;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstance;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoSearchDescriptor;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.supervisor.ProcessSupervisor;\nimport org.bonitasoft.engine.identity.Group;\nimport org.bonitasoft.engine.identity.Role;\nimport org.bonitasoft.engine.identity.RoleCreator;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.identity.UserMembership;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.test.BuildTestUtil;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class ProcessSupervisedIT extends TestWithTechnicalUser {\n\n    private static final String SEARCH_S_COMMENT_SUPERVISED_BY = \"searchSCommentSupervisedBy\";\n\n    private static final String SUPERVISOR_ID_CMD_KEY = \"supervisorId\";\n\n    private User john;\n\n    private User matti;\n\n    private UserMembership membership;\n\n    private Role role;\n\n    private Group group;\n\n    private ProcessDefinition definition;\n\n    private List<ProcessDefinition> processDefinitions;\n\n    private List<ProcessInstance> processInstances;\n\n    private ProcessSupervisor supervisorForUser;\n\n    private ProcessSupervisor supervisorForRole;\n\n    private ProcessSupervisor supervisorForGroup;\n\n    private List<ProcessSupervisor> supervisors;\n\n    @Override\n    @Before\n    public void before() throws Exception {\n        super.before();\n        john = createUser(\"john\", \"bpm\");\n        matti = createUser(\"matti\", \"bpm\");\n\n        logout();\n        loginOnDefaultTenantWith(\"matti\", PASSWORD);\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(\"firstProcess\",\n                \"1.0\");\n        processBuilder.addActor(ACTOR_NAME).addUserTask(\"step1\", ACTOR_NAME);\n        processBuilder.addShortTextData(\"Application\", null);\n        definition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, john);\n        processDefinitions = new ArrayList<>();\n        processDefinitions.add(definition);\n\n        // Create supervisors\n        supervisorForUser = getProcessAPI().createProcessSupervisorForUser(definition.getId(), matti.getId());\n        assertEquals(definition.getId(), supervisorForUser.getProcessDefinitionId());\n\n        // add supervisor by role\n        role = getIdentityAPI().createRole(new RoleCreator(\"developer\"));\n        supervisorForRole = getProcessAPI().createProcessSupervisorForRole(definition.getId(), role.getId());\n\n        // add supervisor group\n        group = getIdentityAPI().createGroup(\"R&D\", null);\n        getIdentityAPI().addUserMembership(matti.getId(), group.getId(), role.getId());\n        supervisorForGroup = getProcessAPI().createProcessSupervisorForGroup(definition.getId(), group.getId());\n\n        // add supervisor membership\n        membership = getIdentityAPI().addUserMembership(john.getId(), group.getId(), role.getId());\n\n        supervisors = new ArrayList<>();\n        supervisors.add(supervisorForGroup);\n        supervisors.add(supervisorForRole);\n        supervisors.add(supervisorForUser);\n\n        // Three tasks\n        processInstances = new ArrayList<>();\n        processInstances.add(getProcessAPI().startProcess(definition.getId()));\n        processInstances.add(getProcessAPI().startProcess(definition.getId()));\n        processInstances.add(getProcessAPI().startProcess(definition.getId()));\n        waitForUserTaskAndAssignIt(processInstances.get(0), \"step1\", john);\n        waitForUserTaskAndAssignIt(processInstances.get(1), \"step1\", john);\n        waitForUserTask(processInstances.get(2), \"step1\");\n    }\n\n    @Override\n    @After\n    public void after() throws Exception {\n        deleteSupervisors(supervisors);\n        disableAndDeleteProcess(processDefinitions);\n        getIdentityAPI().deleteUserMembership(membership.getId());\n        getIdentityAPI().deleteUser(john.getId());\n        getIdentityAPI().deleteUser(matti.getId());\n        getIdentityAPI().deleteGroup(group.getId());\n        getIdentityAPI().deleteRole(role.getId());\n        super.after();\n    }\n\n    @Test\n    public void searchAssignedTasksSupervisedBy() throws Exception {\n        SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.DESC);\n        builder.filter(\"state\", \"ready\");\n        builder.filter(\"name\", \"step1\");\n        builder.filter(\"priority\", TaskPriority.NORMAL); // Test with Enum value filter on Task Priority\n\n        SearchResult<HumanTaskInstance> searchResult = getProcessAPI()\n                .searchAssignedTasksSupervisedBy(matti.getId(), builder.done());\n        assertEquals(2, searchResult.getResult().size());\n        UserTaskInstance taskInstance = (UserTaskInstance) searchResult.getResult().get(0);\n        assertEquals(\"step1\", taskInstance.getName());\n        assertEquals(john.getId(), taskInstance.getAssigneeId());\n\n        taskInstance = (UserTaskInstance) searchResult.getResult().get(1);\n        assertEquals(\"step1\", taskInstance.getName());\n        assertEquals(john.getId(), taskInstance.getAssigneeId());\n\n        // Test the same thing with numeric filter on Task Priority:\n        builder = new SearchOptionsBuilder(0, 10);\n        builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.DESC);\n        builder.filter(\"state\", \"ready\");\n        builder.filter(\"name\", \"step1\");\n        builder.filter(\"priority\", TaskPriority.NORMAL.ordinal());\n\n        searchResult = getProcessAPI().searchAssignedTasksSupervisedBy(matti.getId(), builder.done());\n        assertEquals(2, searchResult.getResult().size());\n        taskInstance = (UserTaskInstance) searchResult.getResult().get(0);\n        assertEquals(\"step1\", taskInstance.getName());\n        assertEquals(john.getId(), taskInstance.getAssigneeId());\n\n        taskInstance = (UserTaskInstance) searchResult.getResult().get(1);\n        assertEquals(\"step1\", taskInstance.getName());\n        assertEquals(john.getId(), taskInstance.getAssigneeId());\n\n        // Test the same thing with String filter on Task Priority:\n        builder = new SearchOptionsBuilder(0, 10);\n        builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.DESC);\n        builder.filter(\"state\", \"ready\");\n        builder.filter(\"name\", \"step1\");\n        builder.filter(\"priority\", TaskPriority.NORMAL.name());\n\n        searchResult = getProcessAPI().searchAssignedTasksSupervisedBy(matti.getId(), builder.done());\n        assertEquals(2, searchResult.getResult().size());\n        taskInstance = (UserTaskInstance) searchResult.getResult().get(0);\n        assertEquals(\"step1\", taskInstance.getName());\n        assertEquals(john.getId(), taskInstance.getAssigneeId());\n\n        taskInstance = (UserTaskInstance) searchResult.getResult().get(1);\n        assertEquals(\"step1\", taskInstance.getName());\n        assertEquals(john.getId(), taskInstance.getAssigneeId());\n    }\n\n    @Test\n    public void searchProcessDefinitionsSupervisedBy() throws Exception {\n        // create processDefintions\n        final List<Long> proDefIds = new ArrayList<>();\n        final int num = 5;\n        for (int i = 0; i < num; i++) {\n            final String actorName = \"actorManu\" + i;\n            final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                    .createNewInstance(\"My_Process\" + i, \"1.\" + i);\n            processBuilder.addActor(actorName).addDescription(\"actor description\" + i);\n            final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask(\"step1\", actorName)\n                    .getProcess();\n            final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition,\n                    actorName, john);\n            processDefinitions.add(processDefinition1);\n            proDefIds.add(processDefinition1.getId());\n        }\n\n        // add same supervisors\n        for (int i = num - 1; i > 0; i--) {\n            supervisors.add(getProcessAPI().createProcessSupervisorForUser(proDefIds.get(i), john.getId()));\n        }\n\n        // create category hr\n        final String categoryName = \"HR\";\n        final String categoryDescription = \"This category for HR.\";\n        final Category category1 = getProcessAPI().createCategory(categoryName, categoryDescription);\n\n        // create category sales\n        final String categoryName2 = \"sales\";\n        final String categoryDescription2 = \"This category for sales.\";\n        final Category category2 = getProcessAPI().createCategory(categoryName2, categoryDescription2);\n\n        // three processDefinitions for HR\n        getProcessAPI().addProcessDefinitionsToCategory(category1.getId(), new ArrayList<>(proDefIds.subList(0, 3)));\n        // two processDefinitions for sales\n        getProcessAPI().addProcessDefinitionsToCategory(category2.getId(),\n                new ArrayList<>(proDefIds.subList(3, proDefIds.size())));\n\n        // test get all process Definitions without filter\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.sort(ProcessDeploymentInfoSearchDescriptor.ID, Order.DESC);\n        final SearchResult<ProcessDeploymentInfo> searchRes = getProcessAPI()\n                .searchProcessDeploymentInfosSupervisedBy(john.getId(), builder.done());\n        assertEquals(5, searchRes.getCount());\n\n        // test search in order\n        final SearchOptionsBuilder builder1 = new SearchOptionsBuilder(0, 10);\n        builder1.filter(ProcessDeploymentInfoSearchDescriptor.CATEGORY_ID, category1.getId());\n        builder1.sort(ProcessDeploymentInfoSearchDescriptor.ID, Order.DESC);\n\n        final SearchResult<ProcessDeploymentInfo> searchRes1 = getProcessAPI()\n                .searchProcessDeploymentInfosSupervisedBy(john.getId(), builder1.done());\n        assertEquals(2, searchRes1.getCount());\n        final List<ProcessDeploymentInfo> processDeploymentInfos1 = searchRes1.getResult();\n        assertNotNull(processDeploymentInfos1);\n        assertEquals(2, processDeploymentInfos1.size());\n        assertEquals(proDefIds.get(2).longValue(), processDeploymentInfos1.get(0).getProcessId());\n        assertEquals(proDefIds.get(1).longValue(), processDeploymentInfos1.get(1).getProcessId());\n\n        // test term\n        final SearchOptionsBuilder builder2 = new SearchOptionsBuilder(0, 10);\n        builder2.filter(ProcessDeploymentInfoSearchDescriptor.CATEGORY_ID, category2.getId());\n        builder2.searchTerm(\"My_Process4\"); // use name as term\n\n        final SearchResult<ProcessDeploymentInfo> searchRes2 = getProcessAPI()\n                .searchProcessDeploymentInfosSupervisedBy(john.getId(), builder2.done());\n        assertEquals(1, searchRes2.getCount());\n        final List<ProcessDeploymentInfo> processDeploymentInfos2 = searchRes2.getResult();\n        assertNotNull(processDeploymentInfos2);\n        assertEquals(1, processDeploymentInfos2.size());\n        assertEquals(proDefIds.get(4).longValue(), processDeploymentInfos2.get(0).getProcessId());\n\n        getProcessAPI().deleteCategory(category1.getId());\n        getProcessAPI().deleteCategory(category2.getId());\n    }\n\n    @Test\n    @SuppressWarnings(\"unchecked\")\n    public void searchCommentsSupervisedBy() throws Exception {\n        // prepare commentContent\n        final ProcessInstance processInstance3 = processInstances.get(2);\n\n        // add comment to processInstance\n        getProcessAPI().addProcessComment(processInstance3.getId(), \"commentContent1\");\n        getProcessAPI().addProcessComment(processInstance3.getId(), \"commentContent2\");\n        getProcessAPI().addProcessComment(processInstance3.getId(), \"commentContent3\");\n\n        loginOnDefaultTenantWith(\"john\", \"bpm\");\n        final ProcessDefinitionBuilder processBuilder2 = new ProcessDefinitionBuilder()\n                .createNewInstance(\"secondProcess\", \"2.0\");\n        processBuilder2.addDescription(\"definition2 description\");\n        processBuilder2.addActor(ACTOR_NAME).addUserTask(\"temporize\", ACTOR_NAME);\n        final DesignProcessDefinition designprocessDefinition2 = processBuilder2.done();\n        final ProcessDefinition definition2 = deployAndEnableProcessWithActor(designprocessDefinition2, ACTOR_NAME,\n                matti);\n        processDefinitions.add(definition2);\n        final ProcessInstance pi1 = getProcessAPI().startProcess(definition2.getId());\n\n        getProcessAPI().addProcessComment(pi1.getId(), \"commentContent4\");\n        getProcessAPI().addProcessComment(pi1.getId(), \"commentContent5\");\n\n        // create supervisor for definition2\n        final ProcessSupervisor supervisor = getProcessAPI().createProcessSupervisorForUser(definition2.getId(),\n                john.getId());\n        supervisors.add(supervisor);\n        assertEquals(definition2.getId(), supervisor.getProcessDefinitionId());\n\n        final Map<String, Serializable> parameters1 = new HashMap<>();\n        parameters1.put(SUPERVISOR_ID_CMD_KEY, matti.getId());\n        parameters1.put(\"SEARCH_OPTIONS_KEY\", new SearchOptionsBuilder(0, 10).done());\n        final SearchResult<Serializable> searchResult1 = (SearchResult<Serializable>) getCommandAPI()\n                .execute(SEARCH_S_COMMENT_SUPERVISED_BY, parameters1);\n        assertEquals(3, searchResult1.getCount());\n\n        final Map<String, Serializable> parameters2 = new HashMap<>();\n        parameters2.put(SUPERVISOR_ID_CMD_KEY, john.getId());\n        parameters2.put(\"SEARCH_OPTIONS_KEY\", new SearchOptionsBuilder(0, 10).done());\n        final SearchResult<Serializable> searchResult2 = (SearchResult<Serializable>) getCommandAPI()\n                .execute(SEARCH_S_COMMENT_SUPERVISED_BY, parameters2);\n        assertEquals(5, searchResult2.getCount());\n    }\n\n    @Test\n    public void searchDocumentsSupervisedBy() throws Exception {\n        final ProcessInstance processInstance = processInstances.get(2);\n        buildAndAttachDocument(processInstance);\n\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        searchOptionsBuilder.filter(DocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId());\n        final SearchResult<Document> documentSearch = getProcessAPI().searchDocumentsSupervisedBy(john.getId(),\n                searchOptionsBuilder.done());\n        assertEquals(1, documentSearch.getCount());\n        assertEquals(processInstance.getId(), documentSearch.getResult().get(0).getProcessInstanceId());\n    }\n\n    @Test\n    public void searchArchivedDocumentsSupervisedBy() throws Exception {\n        final ProcessInstance processInstance = processInstances.get(2);\n        buildAndAttachDocument(processInstance);\n\n        skipTasks(processInstance);\n        waitForProcessToFinish(processInstance);\n\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45);\n        searchOptionsBuilder.filter(ArchivedDocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId());\n        searchOptionsBuilder.sort(ArchivedDocumentsSearchDescriptor.DOCUMENT_NAME, Order.ASC);\n        final SearchResult<ArchivedDocument> documentSearch = getProcessAPI()\n                .searchArchivedDocumentsSupervisedBy(matti.getId(), searchOptionsBuilder.done());\n        assertEquals(3, documentSearch.getCount());\n        assertEquals(processInstance.getId(), documentSearch.getResult().get(0).getProcessInstanceId());\n    }\n\n    @Test\n    public void searchPendingTasksSupervisedBy() throws Exception {\n        final SearchOptionsBuilder searchOptions = BuildTestUtil.buildSearchOptions(0, 10,\n                HumanTaskInstanceSearchDescriptor.NAME, Order.ASC);\n        final SearchResult<HumanTaskInstance> result = getProcessAPI().searchPendingTasksSupervisedBy(matti.getId(),\n                searchOptions.done());\n        assertNotNull(result);\n        assertEquals(3, result.getCount());\n        final List<HumanTaskInstance> humanTaskInstanceList = result.getResult();\n        assertNotNull(humanTaskInstanceList);\n        assertEquals(3, humanTaskInstanceList.size());\n        assertEquals(getProcessAPI().getActivities(processInstances.get(0).getId(), 0, 10).get(0).getId(),\n                humanTaskInstanceList.get(0).getId());\n        assertEquals(getProcessAPI().getActivities(processInstances.get(1).getId(), 0, 10).get(0).getId(),\n                humanTaskInstanceList.get(1).getId());\n        assertEquals(getProcessAPI().getActivities(processInstances.get(2).getId(), 0, 10).get(0).getId(),\n                humanTaskInstanceList.get(2).getId());\n    }\n\n    @Test\n    public void searchUncategorizedProcessDefinitionsSupervisedBy() throws Exception {\n        // create process1\n        final String processName1 = \"processDefinition1\";\n        final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(processName1, \"1.1\",\n                        Arrays.asList(\"step1_1\", \"step1_2\"), Arrays.asList(true, true));\n        processDefinitions.add(deployAndEnableProcessWithActor(designProcessDefinition1, ACTOR_NAME, matti));\n\n        // create process2\n        final String processName2 = \"processDefinition2\";\n        final DesignProcessDefinition designProcessDefinition2 = BuildTestUtil\n                .buildProcessDefinitionWithHumanAndAutomaticSteps(processName2, \"1.2\",\n                        Arrays.asList(\"step2_1\", \"step2_2\"), Arrays.asList(true, true));\n        processDefinitions.add(deployAndEnableProcessWithActor(designProcessDefinition2, ACTOR_NAME, matti));\n\n        // create supervisor\n        supervisors\n                .add(getProcessAPI().createProcessSupervisorForUser(processDefinitions.get(1).getId(), john.getId()));\n        supervisors\n                .add(getProcessAPI().createProcessSupervisorForUser(processDefinitions.get(2).getId(), matti.getId()));\n\n        // add categories to processDefinition1\n        final ArrayList<Long> categoryIds = new ArrayList<>();\n        final Category c1 = getProcessAPI().createCategory(\"category1\", \"categoryDescription1\");\n        final Category c2 = getProcessAPI().createCategory(\"category2\", \"categoryDescription2\");\n        final Category c3 = getProcessAPI().createCategory(\"category3\", \"categoryDescription3\");\n        categoryIds.add(c1.getId());\n        categoryIds.add(c2.getId());\n        categoryIds.add(c3.getId());\n        getProcessAPI().addCategoriesToProcess(processDefinitions.get(0).getId(), categoryIds);\n        final List<Category> categories = getProcessAPI()\n                .getCategoriesOfProcessDefinition(processDefinitions.get(0).getId(), 0, 10, CategoryCriterion.NAME_ASC);\n        assertTrue(!categories.isEmpty());\n\n        // Get all process definitions:\n        final SearchOptionsBuilder optsBuilder = new SearchOptionsBuilder(0, 5);\n        optsBuilder.sort(ProcessDeploymentInfoSearchDescriptor.DEPLOYMENT_DATE, Order.DESC);\n        SearchResult<ProcessDeploymentInfo> searchRes0 = getProcessAPI()\n                .searchProcessDeploymentInfos(optsBuilder.done());\n        assertEquals(3, searchRes0.getCount());\n\n        // Get all process definitions with no category associated, supervised by user:\n        searchRes0 = getProcessAPI().searchUncategorizedProcessDeploymentInfosSupervisedBy(matti.getId(),\n                optsBuilder.done());\n        assertEquals(1, searchRes0.getCount());\n        assertEquals(processDefinitions.get(2).getId(), searchRes0.getResult().get(0).getProcessId());\n        assertEquals(\"processDefinition2\", searchRes0.getResult().get(0).getName());\n\n        searchRes0 = getProcessAPI().searchUncategorizedProcessDeploymentInfosSupervisedBy(john.getId(),\n                optsBuilder.done());\n        assertEquals(1, searchRes0.getCount());\n        assertEquals(processDefinitions.get(1).getId(), searchRes0.getResult().get(0).getProcessId());\n        assertEquals(\"processDefinition1\", searchRes0.getResult().get(0).getName());\n\n        deleteCategories(categories);\n    }\n\n    @Test\n    public void searchOpenProcessInstancesSupervisedBy() throws Exception {\n        final ProcessInstance instance = processInstances.get(2);\n\n        // prepare search options\n        final SearchOptionsBuilder searchOptions = BuildTestUtil.buildSearchOptions(0, 10,\n                ProcessInstanceSearchDescriptor.ID, Order.ASC);\n        final SearchResult<ProcessInstance> result = getProcessAPI()\n                .searchOpenProcessInstancesSupervisedBy(matti.getId(), searchOptions.done());\n        assertNotNull(result);\n        assertEquals(3, result.getCount());\n        final List<ProcessInstance> processInstanceList = result.getResult();\n        assertNotNull(processInstanceList);\n        assertEquals(3, processInstanceList.size());\n        assertEquals(instance.getId(), processInstanceList.get(2).getId());\n    }\n\n    @Test\n    public void searchProcessInstancesInvolvingUserWithSupervisorStartedProcess() throws Exception {\n        final long processDefinitionId = processDefinitions.get(0).getId();\n        // assign pending task to jack\n        final ProcessInstance processInstance = getProcessAPI().startProcess(john.getId(), processDefinitionId);\n        processInstances.add(processInstance);\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n\n        logout();\n        loginOnDefaultTenantWith(\"john\", PASSWORD);\n\n        final SearchOptionsBuilder searchOptions = BuildTestUtil.buildSearchOptions(processDefinitionId, 0, 5,\n                ProcessInstanceSearchDescriptor.ID, Order.ASC);\n        SearchResult<ProcessInstance> result = getProcessAPI().searchOpenProcessInstancesInvolvingUser(john.getId(),\n                searchOptions.done());\n        assertNotNull(result);\n        assertEquals(1, result.getCount());\n        assertEquals(john.getId(), result.getResult().get(0).getStartedBy());\n        assertEquals(matti.getId(), result.getResult().get(0).getStartedBySubstitute());\n\n        getProcessAPI().assignUserTask(step1Id, john.getId());\n        getProcessAPI().executeFlowNode(matti.getId(), step1Id);\n\n        waitForProcessToFinish(processInstance);\n        result = getProcessAPI().searchOpenProcessInstancesInvolvingUser(john.getId(), searchOptions.done());\n        assertNotNull(result);\n        assertEquals(0, result.getCount());\n\n        final SearchResult<ArchivedProcessInstance> result2 = getProcessAPI()\n                .searchArchivedProcessInstancesInvolvingUser(john.getId(), searchOptions.done());\n        assertNotNull(result2);\n        assertEquals(1, result2.getCount());\n        assertEquals(john.getId(), result2.getResult().get(0).getStartedBy());\n        assertEquals(matti.getId(), result2.getResult().get(0).getStartedBySubstitute());\n\n        // No need to verify anything, if no exception, query exists\n        getProcessAPI().searchProcessInstances(\n                new SearchOptionsBuilder(0, 10)\n                        .filter(ProcessInstanceSearchDescriptor.ASSIGNEE_ID, john.getId())\n                        .done());\n        getProcessAPI().searchProcessInstances(\n                new SearchOptionsBuilder(0, 10)\n                        .filter(ProcessInstanceSearchDescriptor.PROCESS_SUPERVISOR_USER_ID, john.getId())\n                        .done());\n        getProcessAPI().searchProcessInstances(\n                new SearchOptionsBuilder(0, 10)\n                        .filter(ProcessInstanceSearchDescriptor.PROCESS_SUPERVISOR_USER_ID, john.getId())\n                        .filter(ProcessInstanceSearchDescriptor.ASSIGNEE_ID, john.getId())\n                        .done());\n    }\n\n    @Test\n    public void searchArchivedProcessInstancesSupervisedBy() throws Exception {\n        final ProcessInstance processInstance = processInstances.get(2);\n\n        final List<ActivityInstance> activityInstances = getProcessAPI().getActivities(processInstance.getId(), 0, 10);\n        for (final ActivityInstance activityInstance : activityInstances) {\n            final long activityInstanceId = activityInstance.getId();\n            skipTask(activityInstanceId);\n        }\n\n        waitForProcessToFinish(processInstance);\n\n        // test supervisor\n        final SearchResult<ArchivedProcessInstance> sapi = getProcessAPI()\n                .searchArchivedProcessInstancesSupervisedBy(matti.getId(), new SearchOptionsBuilder(0, 10).done());\n        assertThat(sapi.getCount()).as(\"Archived Process Instance count\").isEqualTo(1);\n        final List<ArchivedProcessInstance> archivedProcessInstances = sapi.getResult();\n        assertThat(archivedProcessInstances).extracting(ArchivedProcessInstance::getSourceObjectId)\n                .as(\"source object ids of archived process instances\")\n                .containsOnly(processInstance.getId());\n\n        // test supervisor with assignee\n        getProcessAPI().searchArchivedProcessInstancesSupervisedBy(matti.getId(),\n                new SearchOptionsBuilder(0, 10).filter(ProcessInstanceSearchDescriptor.ASSIGNEE_ID, john.getId())\n                        .done());\n\n        // No need to verify anything, if no exception, query exists\n        final long processDefinitionId = processInstance.getProcessDefinitionId();\n        getProcessAPI().searchArchivedProcessInstances(\n                new SearchOptionsBuilder(0, 10)\n                        .filter(ProcessInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDefinitionId)\n                        .filter(ProcessInstanceSearchDescriptor.ASSIGNEE_ID, john.getId())\n                        .done());\n        getProcessAPI().searchArchivedProcessInstances(\n                new SearchOptionsBuilder(0, 10)\n                        .filter(ProcessInstanceSearchDescriptor.PROCESS_SUPERVISOR_USER_ID, john.getId())\n                        .done());\n        getProcessAPI().searchArchivedProcessInstances(\n                new SearchOptionsBuilder(0, 10)\n                        .filter(ProcessInstanceSearchDescriptor.PROCESS_SUPERVISOR_USER_ID, john.getId())\n                        .filter(ProcessInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDefinitionId)\n                        .filter(ProcessInstanceSearchDescriptor.ASSIGNEE_ID, john.getId())\n                        .done());\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/supervisor/SupervisorIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.supervisor;\n\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\nimport static org.junit.Assert.*;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.bonitasoft.engine.TestWithTechnicalUser;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.supervisor.ProcessSupervisor;\nimport org.bonitasoft.engine.bpm.supervisor.ProcessSupervisorSearchDescriptor;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.identity.Group;\nimport org.bonitasoft.engine.identity.Role;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class SupervisorIT extends TestWithTechnicalUser {\n\n    private List<User> users;\n\n    private List<Group> groups;\n\n    private List<Role> roles;\n\n    private List<ProcessDefinition> processDefinitions;\n\n    private List<ProcessSupervisor> supervisors;\n\n    @Override\n    @Before\n    public void before() throws Exception {\n        super.before();\n        createUsers();\n        createGroups();\n        createRoles();\n        createProcessDefinitions();\n        supervisors = new ArrayList<>();\n        createUserSupervisors();\n        createGroupSupervisors();\n        createRoleSupervisors();\n        createMembershipSupervisors();\n    }\n\n    @Override\n    @After\n    public void after() throws Exception {\n        deleteSupervisors(supervisors);\n        disableAndDeleteProcess(processDefinitions);\n        deleteUsers(users);\n        deleteRoles(roles);\n        deleteGroups(groups);\n        super.after();\n    }\n\n    private void createProcessDefinitions() throws Exception {\n        processDefinitions = new ArrayList<>();\n        processDefinitions.add(createProcessDefinition(\"myProcess1\"));\n        processDefinitions.add(createProcessDefinition(\"myProcess2\"));\n    }\n\n    private ProcessDefinition createProcessDefinition(final String processName) throws Exception {\n        // test process definition with no supervisor\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(processName, \"1.0\").done();\n        return deployAndEnableProcess(designProcessDefinition);\n    }\n\n    private void createUsers() throws BonitaException {\n        users = new ArrayList<>();\n        users.add(getIdentityAPI().createUser(USERNAME, PASSWORD));\n        users.add(createUser(\"user2\", \"bpm\", \"FirstName2\", \"LastName2\"));\n        users.add(createUser(\"user3\", \"bpm\", \"FirstName3\", \"LastName3\"));\n        users.add(createUser(\"user4\", \"bpm\", \"FirstName4\", \"LastName4\"));\n        users.add(createUser(\"user5\", \"bpm\", \"FirstName5\", \"LastName5\"));\n    }\n\n    private void createGroups() throws BonitaException {\n        groups = new ArrayList<>();\n        groups.add(getIdentityAPI().createGroup(\"Engine\", null));\n        groups.add(createGroup(\"group2\", \"level2\"));\n    }\n\n    private void createRoles() throws BonitaException {\n        roles = new ArrayList<>();\n        roles.add(getIdentityAPI().createRole(\"Developer\"));\n        roles.add(createRole(\"role2\"));\n    }\n\n    private void createUserSupervisors() throws BonitaException {\n        final ProcessDefinition processDefinition1 = processDefinitions.get(0);\n        final ProcessDefinition processDefinition2 = processDefinitions.get(1);\n\n        supervisors\n                .add(getProcessAPI().createProcessSupervisorForUser(processDefinition1.getId(), users.get(0).getId()));\n        supervisors\n                .add(getProcessAPI().createProcessSupervisorForUser(processDefinition1.getId(), users.get(1).getId()));\n        supervisors\n                .add(getProcessAPI().createProcessSupervisorForUser(processDefinition1.getId(), users.get(2).getId()));\n        supervisors\n                .add(getProcessAPI().createProcessSupervisorForUser(processDefinition2.getId(), users.get(3).getId()));\n        supervisors\n                .add(getProcessAPI().createProcessSupervisorForUser(processDefinition2.getId(), users.get(4).getId()));\n    }\n\n    private void createGroupSupervisors() throws BonitaException {\n        supervisors.add(getProcessAPI().createProcessSupervisorForGroup(processDefinitions.get(0).getId(),\n                groups.get(0).getId()));\n        supervisors.add(getProcessAPI().createProcessSupervisorForGroup(processDefinitions.get(1).getId(),\n                groups.get(1).getId()));\n    }\n\n    private void createRoleSupervisors() throws BonitaException {\n        supervisors.add(getProcessAPI().createProcessSupervisorForRole(processDefinitions.get(0).getId(),\n                roles.get(0).getId()));\n        supervisors.add(getProcessAPI().createProcessSupervisorForRole(processDefinitions.get(1).getId(),\n                roles.get(1).getId()));\n    }\n\n    private void createMembershipSupervisors() throws BonitaException {\n        final ProcessDefinition processDefinition1 = processDefinitions.get(0);\n        final Role role1 = roles.get(0);\n        final Role role2 = roles.get(1);\n        final Group group1 = groups.get(0);\n        final Group group2 = groups.get(1);\n        supervisors.add(getProcessAPI().createProcessSupervisorForMembership(processDefinition1.getId(), group1.getId(),\n                role1.getId()));\n        supervisors.add(getProcessAPI().createProcessSupervisorForMembership(processDefinition1.getId(), group2.getId(),\n                role2.getId()));\n        supervisors.add(getProcessAPI().createProcessSupervisorForMembership(processDefinitions.get(1).getId(),\n                group2.getId(), role1.getId()));\n    }\n\n    @Test\n    public void isUserProcessSupervisor() throws Exception {\n        final long processDefinitionId = processDefinitions.get(0).getId();\n        final User user = createUser(\"user546\", \"bpm\", \"FirstName564\", \"LastName2\");\n        final long userId = user.getId();\n        ProcessSupervisor createdSupervisor = null;\n\n        try {\n            // before create supervisor\n            assertFalse(getProcessAPI().isUserProcessSupervisor(processDefinitionId, userId));\n            // create supervisor\n            createdSupervisor = getProcessAPI().createProcessSupervisorForUser(processDefinitionId, userId);\n            // after created supervisor\n            assertTrue(getProcessAPI().isUserProcessSupervisor(processDefinitionId, userId));\n        } finally {\n            // clean-up\n            if (createdSupervisor != null) {\n                deleteSupervisor(createdSupervisor.getSupervisorId());\n            }\n            deleteUser(user);\n        }\n    }\n\n    @Test\n    public void getAndDeleteSupervisor() throws BonitaException {\n        final long userId = users.get(0).getId();\n\n        // Count to assert\n        final SearchOptionsBuilder builder = buildSearchOptions(null, 7, 3, ProcessSupervisorSearchDescriptor.USER_ID,\n                Order.ASC);\n        final SearchResult<ProcessSupervisor> result = getProcessAPI().searchProcessSupervisors(builder.done());\n        assertEquals(12, result.getCount());\n\n        final ProcessSupervisor getSupervisorResult = result.getResult().get(0);\n        assertEquals(supervisors.get(0).getSupervisorId(), getSupervisorResult.getSupervisorId());\n        assertEquals(userId, getSupervisorResult.getUserId());\n        assertEquals(supervisors.get(0).getProcessDefinitionId(), getSupervisorResult.getProcessDefinitionId());\n    }\n\n    @Test(expected = AlreadyExistsException.class)\n    public void cantCreateTwiceSameUserSupervisor() throws BonitaException {\n        final long processDefinitionId = processDefinitions.get(0).getId();\n        final long userId = users.get(0).getId();\n\n        // Add Supervisor\n        final ProcessSupervisor createdSupervisor = getProcessAPI().createProcessSupervisorForUser(processDefinitionId,\n                userId);\n\n        try {\n            getProcessAPI().createProcessSupervisorForUser(processDefinitionId, userId);\n        } finally {\n            // clean-up\n            deleteSupervisor(createdSupervisor.getSupervisorId());\n        }\n    }\n\n    @Test(expected = AlreadyExistsException.class)\n    public void cantCreateTwiceSameGroupSupervisor() throws BonitaException {\n        final long processDefinitionId = processDefinitions.get(0).getId();\n        final long groupId = groups.get(0).getId();\n\n        // Add Supervisor\n        final ProcessSupervisor createdSupervisor = getProcessAPI().createProcessSupervisorForGroup(processDefinitionId,\n                groupId);\n\n        try {\n            getProcessAPI().createProcessSupervisorForGroup(processDefinitionId, groupId);\n        } finally {\n            // clean-up\n            deleteSupervisor(createdSupervisor.getSupervisorId());\n        }\n    }\n\n    @Test(expected = AlreadyExistsException.class)\n    public void cantCreateTwiceSameRoleSupervisor() throws BonitaException {\n        final long processDefinitionId = processDefinitions.get(0).getId();\n        final long roleId = roles.get(0).getId();\n\n        // Add Supervisor\n        final ProcessSupervisor createdSupervisor = getProcessAPI().createProcessSupervisorForRole(processDefinitionId,\n                roleId);\n\n        try {\n            getProcessAPI().createProcessSupervisorForRole(processDefinitionId, roleId);\n        } finally {\n            // clean-up\n            deleteSupervisor(createdSupervisor.getSupervisorId());\n        }\n    }\n\n    @Test(expected = AlreadyExistsException.class)\n    public void cantCreateTwiceSameMembershipSupervisor() throws BonitaException {\n        final long processDefinitionId = processDefinitions.get(0).getId();\n        final Group group1 = groups.get(0);\n        final Role role1 = roles.get(0);\n        // Add Supervisor\n        final ProcessSupervisor createdSupervisor = getProcessAPI().createProcessSupervisorForMembership(\n                processDefinitionId, group1.getId(),\n                role1.getId());\n\n        try {\n            getProcessAPI().createProcessSupervisorForMembership(processDefinitionId, group1.getId(), role1.getId());\n        } finally {\n            // clean-up\n            deleteSupervisor(createdSupervisor.getSupervisorId());\n        }\n    }\n\n    @Test\n    public void deleteSupervisors() throws BonitaException {\n        final ProcessDefinition processDefinition1 = processDefinitions.get(0);\n        final Role role1 = roles.get(0);\n        final Role role2 = roles.get(1);\n        final Group group2 = groups.get(1);\n        final long userId = users.get(0).getId();\n\n        // Delete supervisor using ids\n        // Unexisted Supervisor\n        try {\n            getProcessAPI().deleteSupervisor(processDefinition1.getId(), userId, role2.getId(), group2.getId());\n            fail(\"no exception was thrown when deleting an unknown supervisor\");\n        } catch (final DeletionException e) {\n\n        }\n        final SearchOptionsBuilder builder = buildSearchOptions(null, 0, 10, ProcessSupervisorSearchDescriptor.ROLE_ID,\n                Order.ASC);\n        SearchResult<ProcessSupervisor> result = getProcessAPI().searchProcessSupervisors(builder.done());\n        assertEquals(12, result.getCount());\n\n        try {\n            getProcessAPI().deleteSupervisor(null, null, role1.getId(), null);\n            fail(\"no exception was thrown when deleting an unknown supervisor\");\n        } catch (final DeletionException e) {\n\n        }\n        result = getProcessAPI().searchProcessSupervisors(builder.done());\n        assertEquals(12, result.getCount());\n\n        // Existed Supervisor\n        getProcessAPI().deleteSupervisor(processDefinition1.getId(), null, role2.getId(), group2.getId());\n        supervisors.remove(10);\n        result = getProcessAPI().searchProcessSupervisors(builder.done());\n        assertEquals(11, result.getCount());\n\n        getProcessAPI().deleteSupervisor(processDefinition1.getId(), userId, null, null);\n        supervisors.remove(0);\n        result = getProcessAPI().searchProcessSupervisors(builder.done());\n        assertEquals(10, result.getCount());\n\n        getProcessAPI().deleteSupervisor(processDefinition1.getId(), null, role1.getId(), null);\n        supervisors.remove(6);\n        result = getProcessAPI().searchProcessSupervisors(builder.done());\n        assertEquals(9, result.getCount());\n    }\n\n    @Test\n    public void deleteSupervisor_should_throw_SupervisorNotFoundException_when_supervisor_does_not_exist()\n            throws BonitaException {\n        final ProcessDefinition myProcess1 = processDefinitions.get(0);\n        final Role role2 = roles.get(1);\n        final Group group2 = groups.get(1);\n\n        // Removed Supervisor\n        getProcessAPI().deleteSupervisor(myProcess1.getId(), null, role2.getId(), group2.getId());\n        this.supervisors.remove(10);\n\n        // Already deleted Supervisor\n        assertThatThrownBy(\n                () -> getProcessAPI().deleteSupervisor(myProcess1.getId(), null, role2.getId(), group2.getId()))\n                .isInstanceOf(DeletionException.class)\n                // Only check the name and not the cause, because in HTTP mode, the stacks are merged and the cause removed:\n                .hasMessageContaining(\"SupervisorNotFoundException\");\n    }\n\n    @Test\n    public void addGroupToSupervisor() throws Exception {\n        final long processDefinitionId = processDefinitions.get(0).getId();\n        final Group group = getIdentityAPI().createGroup(\"Engine789489\", null);\n\n        // Add Supervisor\n        ProcessSupervisor createdSupervisor = null;\n        try {\n            createdSupervisor = getProcessAPI().createProcessSupervisorForGroup(processDefinitionId, group.getId());\n            assertEquals(processDefinitionId, createdSupervisor.getProcessDefinitionId());\n            // Search supervisor\n            final SearchOptionsBuilder builder = buildSearchOptions(null, 12, 1,\n                    ProcessSupervisorSearchDescriptor.GROUP_ID, Order.ASC);\n            final SearchResult<ProcessSupervisor> result = getProcessAPI().searchProcessSupervisors(builder.done());\n            assertEquals(13, result.getCount());\n            final ProcessSupervisor getSupervisorResult = result.getResult().get(0);\n            assertEquals(createdSupervisor.getSupervisorId(), getSupervisorResult.getSupervisorId());\n            assertEquals(group.getId(), getSupervisorResult.getGroupId());\n            assertEquals(createdSupervisor.getProcessDefinitionId(), getSupervisorResult.getProcessDefinitionId());\n        } finally {\n            // clean-up\n            if (createdSupervisor != null) {\n                deleteSupervisor(createdSupervisor.getSupervisorId());\n            }\n            deleteGroups(group);\n        }\n    }\n\n    @Test\n    public void addMembershipToSupervisor() throws Exception {\n        final long processDefinitionId = processDefinitions.get(0).getId();\n        final Role role = getIdentityAPI().createRole(\"Developer5646\");\n        final Group group1 = groups.get(0);\n\n        // Add Supervisor\n        ProcessSupervisor createdSupervisor = null;\n        try {\n            createdSupervisor = getProcessAPI()\n                    .createProcessSupervisorForMembership(processDefinitionId, group1.getId(), role.getId());\n            assertEquals(processDefinitionId, createdSupervisor.getProcessDefinitionId());\n            // Search supervisor\n            final SearchOptionsBuilder builder = buildSearchOptions(null, 12, 12,\n                    ProcessSupervisorSearchDescriptor.ROLE_ID, Order.ASC);\n            final SearchResult<ProcessSupervisor> result = getProcessAPI().searchProcessSupervisors(builder.done());\n            assertEquals(13, result.getCount());\n            final ProcessSupervisor getSupervisorResult = result.getResult().get(0);\n            assertEquals(createdSupervisor.getSupervisorId(), getSupervisorResult.getSupervisorId());\n            assertEquals(group1.getId(), getSupervisorResult.getGroupId());\n            assertEquals(role.getId(), getSupervisorResult.getRoleId());\n            assertEquals(createdSupervisor.getProcessDefinitionId(), getSupervisorResult.getProcessDefinitionId());\n            // Check is user supervisor\n            assertTrue(getProcessAPI().isUserProcessSupervisor(processDefinitionId, users.get(0).getId()));\n        } finally {\n            // clean-up\n            if (createdSupervisor != null) {\n                deleteSupervisor(createdSupervisor.getSupervisorId());\n            }\n            deleteRoles(role);\n        }\n    }\n\n    @Test\n    public void searchProcessSupervisorsForUser() throws Exception {\n        final ProcessSupervisor supervisor4 = supervisors.get(3);\n        final ProcessSupervisor supervisor5 = supervisors.get(4);\n\n        // test ASC\n        SearchOptionsBuilder builder = buildSearchOptions(null, 7, 3, ProcessSupervisorSearchDescriptor.USER_ID,\n                Order.ASC);\n        final SearchResult<ProcessSupervisor> result1 = getProcessAPI().searchProcessSupervisors(builder.done());\n        assertEquals(12, result1.getCount());\n        List<ProcessSupervisor> supervisorsResult = result1.getResult();\n        assertNotNull(supervisorsResult);\n        assertEquals(3, supervisorsResult.size());\n        assertEquals(supervisors.get(0).getUserId(), supervisorsResult.get(0).getUserId());\n        assertEquals(supervisors.get(1).getUserId(), supervisorsResult.get(1).getUserId());\n        assertEquals(supervisors.get(2).getUserId(), supervisorsResult.get(2).getUserId());\n\n        builder = buildSearchOptions(null, 10, 2, ProcessSupervisorSearchDescriptor.USER_ID, Order.ASC);\n        final SearchResult<ProcessSupervisor> result2 = getProcessAPI().searchProcessSupervisors(builder.done());\n        assertEquals(12, result2.getCount());\n        supervisorsResult = result2.getResult();\n        assertNotNull(supervisorsResult);\n        assertEquals(2, supervisorsResult.size());\n        assertEquals(supervisor4.getUserId(), supervisorsResult.get(0).getUserId());\n        assertEquals(supervisor5.getUserId(), supervisorsResult.get(1).getUserId());\n\n        // test DESC\n        builder = buildSearchOptions(null, 0, 2, ProcessSupervisorSearchDescriptor.USER_ID, Order.DESC);\n        final SearchResult<ProcessSupervisor> result4 = getProcessAPI().searchProcessSupervisors(builder.done());\n        assertEquals(12, result4.getCount());\n        supervisorsResult = result4.getResult();\n        assertNotNull(supervisorsResult);\n        assertEquals(2, supervisorsResult.size());\n        assertEquals(supervisor5.getUserId(), supervisorsResult.get(0).getUserId());\n        assertEquals(supervisor4.getUserId(), supervisorsResult.get(1).getUserId());\n    }\n\n    @Test\n    public void searchProcessSupervisorsForUserWithFilter() throws Exception {\n        // filter on process\n        Map<String, Serializable> filters = Collections.singletonMap(\n                ProcessSupervisorSearchDescriptor.PROCESS_DEFINITION_ID,\n                (Serializable) processDefinitions.get(1).getId());\n        SearchOptionsBuilder builder = buildSearchOptions(filters, 3, 5, ProcessSupervisorSearchDescriptor.USER_ID,\n                Order.ASC);\n        final SearchResult<ProcessSupervisor> result1 = getProcessAPI().searchProcessSupervisors(builder.done());\n        assertEquals(5, result1.getCount());\n        List<ProcessSupervisor> supervisorsResult = result1.getResult();\n        assertEquals(supervisors.get(3).getUserId(), supervisorsResult.get(0).getUserId());\n        assertEquals(supervisors.get(4).getUserId(), supervisorsResult.get(1).getUserId());\n\n        // filter on firstname\n        filters = Collections.singletonMap(ProcessSupervisorSearchDescriptor.USER_ID,\n                (Serializable) users.get(0).getId());\n        builder = buildSearchOptions(filters, 0, 3, ProcessSupervisorSearchDescriptor.USER_ID, Order.ASC);\n        final SearchResult<ProcessSupervisor> result2 = getProcessAPI().searchProcessSupervisors(builder.done());\n        assertEquals(1, result2.getCount());\n        supervisorsResult = result2.getResult();\n        assertEquals(supervisors.get(0).getUserId(), supervisorsResult.get(0).getUserId());\n    }\n\n    @Test\n    public void searchProcessSupervisorsForGroup() throws Exception {\n        final SearchOptionsBuilder builder = buildSearchOptions(null, 8, 2, ProcessSupervisorSearchDescriptor.GROUP_ID,\n                Order.ASC);\n\n        final SearchResult<ProcessSupervisor> result1 = getProcessAPI().searchProcessSupervisors(builder.done());\n        assertEquals(12, result1.getCount());\n        final List<ProcessSupervisor> supervisors = result1.getResult();\n        assertEquals(groups.get(0).getId(), supervisors.get(0).getGroupId());\n        assertEquals(groups.get(1).getId(), supervisors.get(1).getGroupId());\n    }\n\n    @Test\n    public void searchProcessSupervisorsForGroupWithFilter() throws Exception {\n        final Group group1 = groups.get(0);\n\n        final Map<String, Serializable> filters = Collections.singletonMap(ProcessSupervisorSearchDescriptor.GROUP_ID,\n                (Serializable) group1.getId());\n        final SearchOptionsBuilder builder = buildSearchOptions(filters, 0, 5,\n                ProcessSupervisorSearchDescriptor.GROUP_ID, Order.ASC);\n\n        final SearchResult<ProcessSupervisor> result1 = getProcessAPI().searchProcessSupervisors(builder.done());\n        assertEquals(2, result1.getCount());\n        final List<ProcessSupervisor> supervisors = result1.getResult();\n        assertEquals(group1.getId(), supervisors.get(0).getGroupId());\n    }\n\n    @Test\n    public void searchProcessSupervisorsForRole() throws Exception {\n        final SearchOptionsBuilder builder = buildSearchOptions(null, 9, 11, ProcessSupervisorSearchDescriptor.ROLE_ID,\n                Order.ASC);\n        final SearchResult<ProcessSupervisor> result1 = getProcessAPI().searchProcessSupervisors(builder.done());\n        assertEquals(12, result1.getCount());\n        final List<ProcessSupervisor> supervisors = result1.getResult();\n        assertEquals(roles.get(0).getId(), supervisors.get(0).getRoleId());\n        assertEquals(roles.get(1).getId(), supervisors.get(1).getRoleId());\n    }\n\n    @Test\n    public void searchProcessSupervisorsForRoleWithFilter() throws Exception {\n        final Role role1 = roles.get(0);\n\n        final Map<String, Serializable> filters = Collections.singletonMap(ProcessSupervisorSearchDescriptor.ROLE_ID,\n                (Serializable) role1.getId());\n        final SearchOptionsBuilder builder = buildSearchOptions(filters, 0, 5,\n                ProcessSupervisorSearchDescriptor.ROLE_ID, Order.ASC);\n        final SearchResult<ProcessSupervisor> result1 = getProcessAPI().searchProcessSupervisors(builder.done());\n        assertEquals(3, result1.getCount());\n        final List<ProcessSupervisor> supervisors = result1.getResult();\n        assertEquals(role1.getId(), supervisors.get(0).getRoleId());\n    }\n\n    @Test\n    public void searchProcessSupervisorsForRoleAndGroup() throws Exception {\n        final Role role1 = roles.get(0);\n        final Role role2 = roles.get(1);\n        final Group group1 = groups.get(0);\n        final Group group2 = groups.get(1);\n\n        final SearchOptionsBuilder builder = buildSearchOptions(null, 8, 4, ProcessSupervisorSearchDescriptor.ROLE_ID,\n                Order.ASC);\n        builder.sort(ProcessSupervisorSearchDescriptor.GROUP_ID, Order.ASC);\n        final SearchResult<ProcessSupervisor> result1 = getProcessAPI().searchProcessSupervisors(builder.done());\n        assertEquals(12, result1.getCount());\n        final List<ProcessSupervisor> supervisorsResult = result1.getResult();\n        assertEquals(role1.getId(), supervisorsResult.get(0).getRoleId());\n        assertEquals(group1.getId(), supervisorsResult.get(0).getGroupId());\n        assertEquals(role1.getId(), supervisorsResult.get(1).getRoleId());\n        assertEquals(group2.getId(), supervisorsResult.get(1).getGroupId());\n        assertEquals(role2.getId(), supervisorsResult.get(3).getRoleId());\n        assertEquals(group2.getId(), supervisorsResult.get(3).getGroupId());\n    }\n\n    @Test\n    public void searchProcessSupervisorsForRoleAndGroupWithFilter() throws Exception {\n        final Map<String, Serializable> filters = Collections.singletonMap(ProcessSupervisorSearchDescriptor.ROLE_ID,\n                (Serializable) roles.get(0).getId());\n        final SearchOptionsBuilder builder = buildSearchOptions(filters, 1, 5,\n                ProcessSupervisorSearchDescriptor.ROLE_ID, Order.ASC);\n        builder.sort(ProcessSupervisorSearchDescriptor.GROUP_ID, Order.ASC);\n\n        final SearchResult<ProcessSupervisor> result1 = getProcessAPI().searchProcessSupervisors(builder.done());\n        assertEquals(3, result1.getCount());\n        final List<ProcessSupervisor> supervisorsResult = result1.getResult();\n        assertEquals(supervisors.get(9).getSupervisorId(), supervisorsResult.get(0).getSupervisorId());\n        assertEquals(supervisors.get(11).getSupervisorId(), supervisorsResult.get(1).getSupervisorId());\n    }\n\n    @Test\n    public void searchProcessSupervisorsForUserAndRole() throws Exception {\n        final Role role1 = roles.get(0);\n\n        final SearchOptionsBuilder builder = buildSearchOptions(null, 3, 6, ProcessSupervisorSearchDescriptor.USER_ID,\n                Order.ASC);\n        builder.sort(ProcessSupervisorSearchDescriptor.ROLE_ID, Order.ASC);\n\n        final SearchResult<ProcessSupervisor> result1 = getProcessAPI().searchProcessSupervisors(builder.done());\n        assertEquals(12, result1.getCount());\n        final List<ProcessSupervisor> supervisors = result1.getResult();\n        assertEquals(role1.getId(), supervisors.get(0).getRoleId());\n        assertEquals(role1.getId(), supervisors.get(1).getRoleId());\n        assertEquals(roles.get(1).getId(), supervisors.get(2).getRoleId());\n        assertEquals(users.get(0).getId(), supervisors.get(4).getUserId());\n        assertEquals(users.get(1).getId(), supervisors.get(5).getUserId());\n    }\n\n    @Test\n    public void searchProcessSupervisorsForUserAndRoleWithFilter() throws Exception {\n        final Role role1 = roles.get(0);\n\n        final Map<String, Serializable> filters = Collections.singletonMap(ProcessSupervisorSearchDescriptor.ROLE_ID,\n                (Serializable) role1.getId());\n        final SearchOptionsBuilder builder = buildSearchOptions(filters, 0, 5,\n                ProcessSupervisorSearchDescriptor.ROLE_ID, Order.ASC);\n        builder.sort(ProcessSupervisorSearchDescriptor.USER_ID, Order.ASC);\n\n        final SearchResult<ProcessSupervisor> result1 = getProcessAPI().searchProcessSupervisors(builder.done());\n        assertEquals(3, result1.getCount());\n        final List<ProcessSupervisor> supervisors = result1.getResult();\n        assertEquals(role1.getId(), supervisors.get(0).getRoleId());\n        assertEquals(role1.getId(), supervisors.get(1).getRoleId());\n    }\n\n    @Test\n    public void searchProcessSupervisorsForUserAndGroup() throws Exception {\n        final Group group2 = groups.get(1);\n\n        final SearchOptionsBuilder builder = buildSearchOptions(null, 3, 8, ProcessSupervisorSearchDescriptor.USER_ID,\n                Order.ASC);\n        builder.sort(ProcessSupervisorSearchDescriptor.GROUP_ID, Order.ASC);\n\n        final SearchResult<ProcessSupervisor> result1 = getProcessAPI().searchProcessSupervisors(builder.done());\n        assertEquals(12, result1.getCount());\n        final List<ProcessSupervisor> supervisors = result1.getResult();\n        assertEquals(groups.get(0).getId(), supervisors.get(0).getGroupId());\n        assertEquals(group2.getId(), supervisors.get(1).getGroupId());\n        assertEquals(group2.getId(), supervisors.get(2).getGroupId());\n        assertEquals(group2.getId(), supervisors.get(3).getGroupId());\n        assertEquals(users.get(0).getId(), supervisors.get(4).getUserId());\n        assertEquals(users.get(1).getId(), supervisors.get(5).getUserId());\n    }\n\n    @Test\n    public void searchProcessSupervisorsForUserAndGroupWithFilter() throws Exception {\n        final Map<String, Serializable> filters = Collections.singletonMap(ProcessSupervisorSearchDescriptor.USER_ID,\n                (Serializable) users.get(0).getId());\n        final SearchOptionsBuilder builder = buildSearchOptions(filters, 0, 5,\n                ProcessSupervisorSearchDescriptor.GROUP_ID, Order.ASC);\n        builder.sort(ProcessSupervisorSearchDescriptor.USER_ID, Order.ASC);\n\n        final SearchResult<ProcessSupervisor> result1 = getProcessAPI().searchProcessSupervisors(builder.done());\n        assertEquals(1, result1.getCount());\n        final List<ProcessSupervisor> supervisors = result1.getResult();\n        assertEquals(supervisors.get(0).getSupervisorId(), supervisors.get(0).getSupervisorId());\n    }\n\n    @Test\n    public void searchProcessSupervisorsForUserAndGroupAndRole() throws Exception {\n        final Role role1 = roles.get(0);\n        final Role role2 = roles.get(1);\n        final Group group1 = groups.get(0);\n        final Group group2 = groups.get(1);\n\n        final SearchOptionsBuilder builder = buildSearchOptions(null, 3, 6, ProcessSupervisorSearchDescriptor.USER_ID,\n                Order.ASC);\n        builder.sort(ProcessSupervisorSearchDescriptor.GROUP_ID, Order.ASC);\n        builder.sort(ProcessSupervisorSearchDescriptor.ROLE_ID, Order.ASC);\n\n        final SearchResult<ProcessSupervisor> result1 = getProcessAPI().searchProcessSupervisors(builder.done());\n        assertEquals(12, result1.getCount());\n        final List<ProcessSupervisor> supervisors = result1.getResult();\n        assertEquals(group1.getId(), supervisors.get(0).getGroupId());\n        assertEquals(role1.getId(), supervisors.get(0).getRoleId());\n        assertEquals(group2.getId(), supervisors.get(2).getGroupId());\n        assertEquals(role1.getId(), supervisors.get(2).getRoleId());\n        assertEquals(group2.getId(), supervisors.get(3).getGroupId());\n        assertEquals(role2.getId(), supervisors.get(3).getRoleId());\n        assertEquals(users.get(0).getId(), supervisors.get(4).getUserId());\n        assertEquals(users.get(1).getId(), supervisors.get(5).getUserId());\n    }\n\n    @Test\n    public void searchProcessSupervisorsForUserAndGroupAndRoleWithFilter() throws Exception {\n        final Map<String, Serializable> filters = Collections.singletonMap(ProcessSupervisorSearchDescriptor.USER_ID,\n                (Serializable) users.get(0).getId());\n        final SearchOptionsBuilder builder = buildSearchOptions(filters, 0, 5,\n                ProcessSupervisorSearchDescriptor.USER_ID, Order.ASC);\n        builder.sort(ProcessSupervisorSearchDescriptor.GROUP_ID, Order.ASC);\n        builder.sort(ProcessSupervisorSearchDescriptor.ROLE_ID, Order.ASC);\n\n        final SearchResult<ProcessSupervisor> result1 = getProcessAPI().searchProcessSupervisors(builder.done());\n        assertEquals(1, result1.getCount());\n        final List<ProcessSupervisor> supervisors = result1.getResult();\n        assertEquals(supervisors.get(0).getSupervisorId(), supervisors.get(0).getSupervisorId());\n    }\n\n    private SearchOptionsBuilder buildSearchOptions(final Map<String, Serializable> filters, final int pageIndex,\n            final int numberOfResults,\n            final String orderByField, final Order order) {\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(pageIndex, numberOfResults);\n        if (filters != null) {\n            for (final Entry<String, Serializable> filter : filters.entrySet()) {\n                builder.filter(filter.getKey(), filter.getValue());\n            }\n        }\n        builder.sort(orderByField, order);\n        return builder;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/tenant/TenantMaintenanceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tenant;\n\nimport static org.assertj.core.api.Assertions.fail;\n\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.platform.LoginException;\nimport org.junit.Test;\n\n/**\n * @author Laurent Leseigneur\n * @author Celine Souchet\n */\npublic class TenantMaintenanceIT extends TestWithUser {\n\n    @Test\n    public void should_be_able_to_start_process_after_pause_resume() throws Exception {\n        //given\n        final ProcessDefinition processDefinition = createProcessOnTenant();\n\n        //when\n        logoutThenlogin();\n        pauseTenant();\n\n        //then\n        assertCanLoginOnTenant();\n\n        //when\n        loginWithTechnicalUser();\n        resumeTenant();\n\n        // then\n        assertCanLoginOnTenantAndStartProcess(processDefinition);\n\n        // cleanup\n        loginWithTechnicalUser();\n        disableAndDeleteProcess(processDefinition.getId());\n    }\n\n    private void assertCanLoginOnTenant() throws Exception {\n        try {\n            loginOnDefaultTenantWith(USERNAME, PASSWORD);\n            logout();\n        } catch (LoginException e) {\n            fail(\"Expected that user is able to log in, but is not\");\n        }\n    }\n\n    private void assertCanLoginOnTenantAndStartProcess(final ProcessDefinition processDefinition) throws Exception {\n        loginOnDefaultTenantWith(USERNAME, PASSWORD);\n        ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance, \"step1\");\n        logout();\n    }\n\n    private ProcessDefinition createProcessOnTenant() throws Exception {\n        final String processName = new StringBuilder().append(PROCESS_NAME).toString();\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(processName,\n                        PROCESS_VERSION)\n                .addActor(ACTOR_NAME)\n                .addStartEvent(\"start event\")\n                .addUserTask(\"step1\", ACTOR_NAME)\n                .addEndEvent(\"end event\").getProcess();\n\n        return deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, getSession().getUserId());\n    }\n\n    private void pauseTenant() throws BonitaException {\n        getTenantAdministrationAPI().pause();\n    }\n\n    private void resumeTenant() throws BonitaException {\n        getTenantAdministrationAPI().resume();\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/resources/application-to-test-permissions.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<applications xmlns=\"http://documentation.bonitasoft.com/application-xml-schema/1.1\">\n    <application token=\"application-to-test-permissions\" version=\"2.1\" profile=\"User\" state=\"ACTIVATED\" layout=\"custompage_layoutBonita\" theme=\"custompage_themeBonita\">\n        <displayName>Bonita Editable Application</displayName>\n        <iconPath>bonita-application-directory.png</iconPath>\n        <applicationPages>\n            <applicationPage customPage=\"custompage_pageToTestPermissions\" token=\"pageToTestPermissions\"/>\n        </applicationPages>\n        <applicationMenus>\n            <applicationMenu applicationPage=\"pageToTestPermissions\">\n                <displayName>Some page</displayName>\n            </applicationMenu>\n        </applicationMenus>\n    </application>\n</applications>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/resources/logback-test.xml",
    "content": "<configuration>\n\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->\n        <encoder>\n            <pattern>|%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg| %X%n</pattern>\n        </encoder>\n    </appender>\n\n    <logger name=\"org.bonitasoft\" level=\"INFO\" />\n    <logger name=\"org.hibernate.orm.cache\" level=\"WARN\" />\n    <logger name=\"org.hibernate.SQL_SLOW\" level=\"INFO\"/>\n    <logger name=\"org.bonitasoft.engine.execution.ProcessStarterVerifierImpl\" level=\"DEBUG\"/>\n    <logger name=\"org.bonitasoft.engine.execution.ProcessCounterChecker\" level=\"DEBUG\"/>\n\n    <root level=\"INFO\">\n        <appender-ref ref=\"STDOUT\" />\n    </root>\n    <!--   Show events of synchro repository -->\n    <!--   <logger name=\"org.bonitasoft.engine.test.synchro.SynchroRepository\" level=\"DEBUG\" /> -->\n\n</configuration>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/resources/org/bonitasoft/engine/application/superAdminApp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<applications xmlns=\"http://documentation.bonitasoft.com/application-xml-schema/1.1\">\n    <application token=\"superAdminAppBonita\" version=\"1.0\" profile=\"_BONITA_INTERNAL_PROFILE_SUPER_ADMIN\" state=\"ACTIVATED\">\n        <displayName>Bonita Super Administrator Application</displayName>\n        <iconPath>bonita-super-admin-application.png</iconPath>\n        <applicationPages />\n        <applicationMenus />\n    </application>\n</applications>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/resources/org/bonitasoft/engine/application/testApp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<applications xmlns=\"http://documentation.bonitasoft.com/application-xml-schema/1.1\">\n    <application token=\"testAppBonita\" version=\"1.0\" profile=\"User\" state=\"ACTIVATED\"\n                 layout=\"custompage_layoutBonita\">\n        <displayName>Test App</displayName>\n        <description />\n        <iconPath>bonita-admin-application.png</iconPath>\n        <applicationPages>\n            <applicationPage customPage=\"custompage_layoutBonita\" token=\"not-really-a-page\"/>\n        </applicationPages>\n        <applicationMenus />\n    </application>\n</applications>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/resources/org/bonitasoft/engine/connectors/TestConnector.xml",
    "content": "<connectorDefinition>\n\t<version>1.0</version>\n\t<connectorId>org.bonitasoft.connector.testConnector</connectorId>\n\t<icon>xxx.png</icon>\n\t<categories>\n\t\t<category>\n\t\t\t<name>other</name>\n\t\t\t<icon>org/bonitasoft/engine/connectors/TestConnector.png</icon>\n\t\t</category>\n\t</categories>\n\t<inputs>\n\t\t<input type=\"String\">input1</input>\n\t\t<required />\n\t\t<parameters>\n\t\t\t<string />\n\t\t</parameters>\n\t</inputs>\n\t<outputs>\n\t</outputs>\n</connectorDefinition>"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-client/src/test/resources/org/bonitasoft/engine/connectors/TestConnector_Implementation.xml",
    "content": "<ConnectorImplementation id=\"org.bonitasoft.connector.testConnector\" version=\"1.0\"\n\tdefinitionId=\"org.bonitasoft.connector.testConnector\" definitionVersion=\"1.0\"\n\timplementationClassname=\"org.bonitasoft.engine.connectors.TestConnector\">\n\t<jarDependencies>\n\t\t<jarDependency>TestConnector.jar</jarDependency>\n\t</jarDependencies>\n</ConnectorImplementation>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/build.gradle",
    "content": "plugins {\n    id(\"bonita-docker-database\")\n    id(\"bonita-tests\")\n}\ndependencies {\n    api libs.assertj\n    api libs.mockitoCore\n    api project(':bonita-integration-tests:bonita-integration-tests-client')\n    implementation(project(\":bpm:bonita-core:bonita-process-engine\"))\n    api project(':bpm:bonita-server')\n    api libs.commonsIO\n    testRuntimeOnly libs.logback\n    testAnnotationProcessor libs.lombok\n    testImplementation libs.lombok\n    testImplementation libs.awaitility\n    testImplementation libs.springTest\n    testImplementation libs.systemLambda\n}\n\ntasks.register(\"testsJar\", Jar) {\n    archiveClassifier = 'tests'\n    from(sourceSets.test.output)\n}\n\ngroup = 'org.bonitasoft.engine.test'\n\npublishing {\n    publications {\n        mavenJava(MavenPublication) {\n            from project.components.java\n            artifact testsJar\n        }\n    }\n}\n\ndatabaseIntegrationTest { include \"**/*IT.class\" }\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/main/java/org/bonitasoft/engine/bpm/CommonBPMServicesTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.PrintTestsStatusRule;\nimport org.bonitasoft.engine.actor.mapping.SActorCreationException;\nimport org.bonitasoft.engine.actor.mapping.model.SActor;\nimport org.bonitasoft.engine.api.impl.IdentityAPIImpl;\nimport org.bonitasoft.engine.api.impl.LoginAPIImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.FlowElementContainerDefinitionImpl;\nimport org.bonitasoft.engine.bpm.process.impl.internal.DesignProcessDefinitionImpl;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.exception.SDeletingEnabledProcessException;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionException;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDeletionException;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceHierarchicalDeletionException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceModificationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.SEventInstanceCreationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageInstanceCreationException;\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SGatewayInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SAutomaticTaskInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SUserTaskInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SEndEventInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SIntermediateCatchEventInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SIntermediateThrowEventInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SStartEventInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.event.SEventInstance;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.identity.Group;\nimport org.bonitasoft.engine.identity.SIdentityException;\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.identity.model.SRole;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.identity.model.SUserMembership;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.impl.ServiceAccessorFactory;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.bonitasoft.engine.test.junit.BonitaEngineRule;\nimport org.bonitasoft.engine.test.util.TestUtil;\nimport org.bonitasoft.engine.transaction.STransactionCommitException;\nimport org.bonitasoft.engine.transaction.STransactionCreationException;\nimport org.bonitasoft.engine.transaction.STransactionException;\nimport org.bonitasoft.engine.transaction.STransactionRollbackException;\nimport org.bonitasoft.engine.transaction.TransactionService;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.rules.TestRule;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Baptiste Mesta\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class CommonBPMServicesTest {\n\n    @Rule\n    public BonitaEngineRule bonitaEngineRule = BonitaEngineRule.create();\n\n    private final static Logger LOGGER = LoggerFactory.getLogger(CommonBPMServicesTest.class);\n    protected static SessionAccessor sessionAccessor;\n    protected static ServiceAccessor serviceAccessor;\n    @Rule\n    public TestRule testWatcher = new PrintTestsStatusRule(LOGGER) {\n\n        @Override\n        public void clean() throws Exception {\n            CommonBPMServicesTest.this.clean();\n        }\n\n    };\n\n    private APISession apiSession = null;\n\n    protected ServiceAccessorFactory getServiceAccessorFactory() {\n        return ServiceAccessorFactory.getInstance();\n    }\n\n    APISession getAPISession() {\n        return this.apiSession;\n    }\n\n    protected SessionAccessor getSessionAccessor() {\n        return sessionAccessor;\n    }\n\n    protected ServiceAccessor getServiceAccessor() {\n        if (serviceAccessor == null) {\n            try {\n                serviceAccessor = getServiceAccessorFactory().createServiceAccessor();\n            } catch (Exception e) {\n                throw new RuntimeException(e);\n            }\n        }\n        return serviceAccessor;\n    }\n\n    protected TransactionService getTransactionService() {\n        return getServiceAccessor().getTransactionService();\n    }\n\n    @Before\n    public void commonSetup() throws Exception {\n        apiSession = new LoginAPIImpl().login(TestUtil.getDefaultUserName(), TestUtil.getDefaultPassword());\n        if (sessionAccessor == null) {\n            sessionAccessor = getServiceAccessorFactory().createSessionAccessor();\n        }\n        if (serviceAccessor == null) {\n            serviceAccessor = getServiceAccessorFactory().createServiceAccessor();\n        }\n\n        apiSession = new LoginAPIImpl().login(TestUtil.getDefaultUserName(), TestUtil.getDefaultPassword());\n        sessionAccessor.setSessionId(apiSession.getId());\n    }\n\n    protected Group createGroup(final String groupName) throws CreationException {\n        return createGroup(groupName, null);\n    }\n\n    protected Group createGroup(final String groupName, final String groupPath) throws CreationException {\n        try {\n            openTx();\n            final Group group = new IdentityAPIImpl().createGroup(groupName, groupPath);\n            closeTx();\n            return group;\n        } catch (final STransactionException e) {\n            throw new CreationException(e);\n        }\n    }\n\n    @After\n    public void after() throws Exception {\n        TestUtil.closeTransactionIfOpen(serviceAccessor.getTransactionService());\n        if (apiSession != null) {\n            new LoginAPIImpl().logout(apiSession);\n        }\n    }\n\n    private void clean() throws Exception {\n        try {\n            final APISession apiSession = new LoginAPIImpl().login(TestUtil.getDefaultUserName(),\n                    TestUtil.getDefaultPassword());\n            openTx();\n            cleanProcessesDefinitions();\n            cleanProcessInstance();\n            cleanMemberships();\n            cleaRoles();\n            cleanUsers();\n            cleanGroups();\n            getServiceAccessor().getLoginService().logout(apiSession.getId());\n        } finally {\n            if (serviceAccessor.getTransactionService().isTransactionActive()) {\n                closeTx();\n            }\n        }\n    }\n\n    private void cleanProcessesDefinitions() throws SBonitaReadException, SProcessDefinitionNotFoundException,\n            SProcessDeletionException, SDeletingEnabledProcessException {\n        final QueryOptions queryOptions = new QueryOptions(0, 200, SProcessDefinitionDeployInfo.class, \"name\",\n                OrderByType.ASC);\n        final List<SProcessDefinitionDeployInfo> processes = getServiceAccessor().getProcessDefinitionService()\n                .getProcessDeploymentInfos(queryOptions);\n        if (processes.size() > 0) {\n            for (final SProcessDefinitionDeployInfo process : processes) {\n                try {\n                    getServiceAccessor().getProcessDefinitionService()\n                            .disableProcessDeploymentInfo(process.getProcessId());\n                } catch (final Throwable ignored) {\n                }\n                getServiceAccessor().getProcessDefinitionService().delete(process.getProcessId());\n            }\n        }\n    }\n\n    private void cleanProcessInstance() throws SBonitaReadException, SProcessInstanceNotFoundException,\n            SProcessInstanceReadException, SFlowNodeReadException,\n            SProcessInstanceModificationException, SProcessInstanceHierarchicalDeletionException {\n        // let's clean up All Process Instances:\n        List<SProcessInstance> processInstances = getFirstProcessInstances(5000);\n        while (processInstances.size() > 0) {\n            for (final SProcessInstance sProcessInstance : processInstances) {\n                getServiceAccessor().getProcessInstanceService().deleteProcessInstance(sProcessInstance.getId());\n            }\n            // get the next 100:\n            processInstances = getFirstProcessInstances(5000);\n        }\n    }\n\n    private void cleanMemberships() throws SIdentityException {\n        for (final SUserMembership sMembership : getServiceAccessor().getIdentityService().getUserMemberships(0,\n                5000)) {\n            getServiceAccessor().getIdentityService().deleteUserMembership(sMembership);\n        }\n    }\n\n    private void cleaRoles() throws SIdentityException {\n        for (final SRole sRole : getServiceAccessor().getIdentityService().getRoles(0, 5000)) {\n            getServiceAccessor().getIdentityService().deleteRole(sRole);\n        }\n    }\n\n    private void cleanUsers() throws SIdentityException {\n        for (final SUser sUser : getServiceAccessor().getIdentityService().getUsers(0, 5000)) {\n            getServiceAccessor().getIdentityService().deleteUser(sUser);\n        }\n    }\n\n    private void cleanGroups() throws SIdentityException {\n        for (final SGroup sGroup : getServiceAccessor().getIdentityService().getGroups(0, 5000)) {\n            getServiceAccessor().getIdentityService().deleteGroup(sGroup);\n        }\n    }\n\n    public List<SProcessInstance> getFirstProcessInstances(final int nb) throws SBonitaReadException {\n        // we are already in a transaction context here:\n        final OrderByOption orderByOption = new OrderByOption(SProcessInstance.class, SProcessInstance.LAST_UPDATE_KEY,\n                OrderByType.DESC);\n        final QueryOptions queryOptions = new QueryOptions(0, nb, Collections.singletonList(orderByOption),\n                Collections.emptyList(), null);\n        return getServiceAccessor().getProcessInstanceService().searchProcessInstances(queryOptions);\n    }\n\n    public List<SAProcessInstance> getFirstArchivedProcessInstances(final int nb) throws SBonitaReadException {\n        // we are already in a transaction context here:\n        final OrderByOption orderByOption = new OrderByOption(SAProcessInstance.class, SProcessInstance.LAST_UPDATE_KEY,\n                OrderByType.DESC);\n        final QueryOptions queryOptions = new QueryOptions(0, nb, Collections.singletonList(orderByOption),\n                Collections.emptyList(), null);\n        return getServiceAccessor().getProcessInstanceService().searchArchivedProcessInstances(queryOptions);\n    }\n\n    protected SProcessInstance createSProcessInstance() throws SBonitaException {\n        final SProcessInstance processInstance = SProcessInstance.builder().name(\"process\").processDefinitionId(1)\n                .build();\n\n        openTx();\n        getServiceAccessor().getProcessInstanceService().createProcessInstance(processInstance);\n        closeTx();\n\n        return processInstance;\n    }\n\n    protected void deleteSProcessInstance(final SProcessInstance processInstance) throws SBonitaException {\n        openTx();\n        getServiceAccessor().getProcessInstanceService().deleteProcessInstance(processInstance.getId());\n        closeTx();\n    }\n\n    protected SFlowNodeInstance getFlowNodeInstance(final long flowNodeInstanceId) throws SBonitaException {\n        SFlowNodeInstance flowNodeInstance = null;\n        openTx();\n        try {\n            flowNodeInstance = getServiceAccessor().getActivityInstanceService()\n                    .getFlowNodeInstance(flowNodeInstanceId);\n        } finally {\n            closeTx();\n        }\n        return flowNodeInstance;\n    }\n\n    protected SEventInstance createSStartEventInstance(final String eventName, final long flowNodeDefinitionId,\n            final long rootProcessInstanceId,\n            final long processDefinitionId, final long parentProcessInstanceId)\n            throws STransactionCreationException, SEventInstanceCreationException,\n            STransactionCommitException, STransactionRollbackException, SMessageInstanceCreationException {\n        final SEventInstance eventInstance = BuilderFactory\n                .get(SStartEventInstanceBuilderFactory.class)\n                .createNewStartEventInstance(eventName, flowNodeDefinitionId, rootProcessInstanceId,\n                        rootProcessInstanceId, processDefinitionId,\n                        rootProcessInstanceId, parentProcessInstanceId)\n                .done();\n        createSEventInstance(eventInstance);\n        return eventInstance;\n    }\n\n    protected SEventInstance createSEndEventInstance(final String eventName, final long flowNodeDefinitionId,\n            final long rootProcessInstanceId,\n            final long processDefinitionId, final long parentProcessInstanceId)\n            throws STransactionCreationException, SEventInstanceCreationException,\n            STransactionCommitException, STransactionRollbackException, SMessageInstanceCreationException {\n        final SEventInstance eventInstance = BuilderFactory\n                .get(SEndEventInstanceBuilderFactory.class)\n                .createNewEndEventInstance(eventName, flowNodeDefinitionId, rootProcessInstanceId,\n                        rootProcessInstanceId, processDefinitionId,\n                        rootProcessInstanceId, parentProcessInstanceId)\n                .done();\n        createSEventInstance(eventInstance);\n        return eventInstance;\n    }\n\n    protected SEventInstance createSIntermediateCatchEventInstance(final String eventName,\n            final long flowNodeDefinitionId, final long rootProcessInstanceId,\n            final long processDefinitionId, final long parentProcessInstanceId)\n            throws STransactionCreationException, SEventInstanceCreationException,\n            STransactionCommitException, STransactionRollbackException, SMessageInstanceCreationException {\n        final SEventInstance eventInstance = BuilderFactory\n                .get(SIntermediateCatchEventInstanceBuilderFactory.class)\n                .createNewIntermediateCatchEventInstance(eventName, flowNodeDefinitionId, rootProcessInstanceId,\n                        parentProcessInstanceId, processDefinitionId,\n                        rootProcessInstanceId, parentProcessInstanceId)\n                .done();\n        createSEventInstance(eventInstance);\n        return eventInstance;\n    }\n\n    protected SEventInstance createSIntermediateThrowEventInstance(final String eventName,\n            final long flowNodeDefinitionId, final long processInstanceId,\n            final long processDefinitionId, final long parentProcessInstanceId)\n            throws STransactionCreationException, SEventInstanceCreationException,\n            STransactionCommitException, STransactionRollbackException, SMessageInstanceCreationException {\n        final SEventInstance eventInstance = BuilderFactory\n                .get(SIntermediateThrowEventInstanceBuilderFactory.class)\n                .createNewIntermediateThrowEventInstance(eventName, flowNodeDefinitionId, processInstanceId,\n                        processInstanceId, processDefinitionId,\n                        processInstanceId, parentProcessInstanceId)\n                .done();\n        createSEventInstance(eventInstance);\n        return eventInstance;\n    }\n\n    protected SEventInstance createIntermediateThrowEventInstance(final String eventName,\n            final long flowNodeDefinitionId, final long processInstanceId,\n            final long processDefinitionId, final long parentProcessInstanceId)\n            throws STransactionCreationException, SEventInstanceCreationException,\n            STransactionCommitException, STransactionRollbackException, SMessageInstanceCreationException {\n        final SEventInstance eventInstance = BuilderFactory\n                .get(SIntermediateThrowEventInstanceBuilderFactory.class)\n                .createNewIntermediateThrowEventInstance(eventName, flowNodeDefinitionId, processInstanceId,\n                        processInstanceId, processDefinitionId,\n                        processInstanceId, parentProcessInstanceId)\n                .done();\n        createSEventInstance(eventInstance);\n        return eventInstance;\n    }\n\n    protected void createSEventInstance(final SEventInstance eventInstance)\n            throws STransactionCreationException, SEventInstanceCreationException,\n            STransactionCommitException, STransactionRollbackException, SMessageInstanceCreationException {\n        openTx();\n        getServiceAccessor().getEventInstanceService().createEventInstance(eventInstance);\n        closeTx();\n    }\n\n    protected void insertGatewayInstance(final SGatewayInstance gatewayInstance) throws SBonitaException {\n        openTx();\n        getServiceAccessor().getGatewayInstanceService().createGatewayInstance(gatewayInstance);\n        closeTx();\n    }\n\n    protected SUserTaskInstance createSUserTaskInstance(final String name, final long flowNodeDefinitionId,\n            final long parentId,\n            final long processDefinitionId,\n            final long rootProcessInst, final long actorId) throws SBonitaException {\n        final SUserTaskInstance taskInstance = BuilderFactory.get(SUserTaskInstanceBuilderFactory.class)\n                .createNewUserTaskInstance(name, flowNodeDefinitionId, rootProcessInst, parentId, actorId,\n                        processDefinitionId, rootProcessInst, parentId)\n                .done();\n        openTx();\n        getServiceAccessor().getActivityInstanceService().createActivityInstance(taskInstance);\n        closeTx();\n        return taskInstance;\n    }\n\n    protected SActivityInstance createSAutomaticTaskInstance(final String name, final long flowNodeDefinitionId,\n            final long parentId,\n            final long processDefinitionId, final long rootProcessInst) throws SBonitaException {\n        final SActivityInstance taskInstance = BuilderFactory.get(SAutomaticTaskInstanceBuilderFactory.class)\n                .createNewAutomaticTaskInstance(name, flowNodeDefinitionId, rootProcessInst, parentId,\n                        processDefinitionId, rootProcessInst, parentId)\n                .done();\n        openTx();\n        getServiceAccessor().getActivityInstanceService().createActivityInstance(taskInstance);\n        closeTx();\n        return taskInstance;\n    }\n\n    protected SProcessDefinition buildSProcessDefinition(final String name, final String version)\n            throws SProcessDefinitionException {\n        final DesignProcessDefinitionImpl designProcessDefinition = new DesignProcessDefinitionImpl(name, version);\n        designProcessDefinition.setProcessContainer(new FlowElementContainerDefinitionImpl());\n        return getServiceAccessor().getProcessDefinitionService().store(designProcessDefinition);\n    }\n\n    private SActor buildSActor(final String name, final long scopeId, final boolean initiator)\n            throws SActorCreationException {\n        final SActor sActor = SActor.builder().name(name).scopeId(scopeId).initiator(initiator).build();\n        return getServiceAccessor().getActorMappingService().addActor(sActor);\n    }\n\n    public SProcessDefinition createSProcessDefinitionWithSActor(final String name, final String version,\n            final String actorName,\n            final boolean actorIsInitiator, final List<SUser> sUsersToAddToActor) throws SBonitaException {\n        openTx();\n\n        final SProcessDefinition sProcessDefinition = buildSProcessDefinition(name, version);\n        getServiceAccessor().getProcessDefinitionService().resolveProcess(sProcessDefinition.getId());\n        getServiceAccessor().getProcessDefinitionService().enableProcessDeploymentInfo(sProcessDefinition.getId());\n\n        final SActor sActor = buildSActor(actorName, sProcessDefinition.getId(), actorIsInitiator);\n        for (final SUser sUser : sUsersToAddToActor) {\n            getServiceAccessor().getActorMappingService().addUserToActor(sActor.getId(), sUser.getId());\n        }\n\n        closeTx();\n        return sProcessDefinition;\n    }\n\n    public SProcessDefinition createSProcessDefinition(final String name, final String version)\n            throws SBonitaException {\n        openTx();\n\n        final SProcessDefinition sProcessDefinition = buildSProcessDefinition(name, version);\n        getServiceAccessor().getProcessDefinitionService().resolveProcess(sProcessDefinition.getId());\n        getServiceAccessor().getProcessDefinitionService().enableProcessDeploymentInfo(sProcessDefinition.getId());\n\n        closeTx();\n        return sProcessDefinition;\n    }\n\n    public List<SProcessDefinition> createSProcessDefinitions(final int count, final String name, final String version)\n            throws SBonitaException {\n        final List<SProcessDefinition> processDefinitons = new ArrayList<>();\n\n        openTx();\n        for (int i = 1; i <= count; i++) {\n            final SProcessDefinition sProcessDefinition = buildSProcessDefinition(name + i, version + i);\n            getServiceAccessor().getProcessDefinitionService().resolveProcess(sProcessDefinition.getId());\n            getServiceAccessor().getProcessDefinitionService().enableProcessDeploymentInfo(sProcessDefinition.getId());\n            processDefinitons.add(sProcessDefinition);\n        }\n        closeTx();\n        return processDefinitons;\n    }\n\n    public void deleteSProcessDefinition(final SProcessDefinition sProcessDefinition) throws SBonitaException {\n        deleteSProcessDefinition(sProcessDefinition.getId());\n    }\n\n    public void deleteSProcessDefinition(final long sProcessDefinitionId) throws SBonitaException {\n        openTx();\n        getServiceAccessor().getActorMappingService().deleteActors(sProcessDefinitionId);\n        getServiceAccessor().getProcessDefinitionService().disableProcessDeploymentInfo(sProcessDefinitionId);\n        getServiceAccessor().getProcessDefinitionService().delete(sProcessDefinitionId);\n        closeTx();\n    }\n\n    void openTx() throws STransactionCreationException {\n        serviceAccessor.getTransactionService().begin();\n    }\n\n    public void deleteSProcessDefinitions(final SProcessDefinition... sProcessDefinitions) throws SBonitaException {\n        if (sProcessDefinitions != null) {\n            deleteSProcessDefinitions(Arrays.asList(sProcessDefinitions));\n        }\n    }\n\n    public void deleteSProcessDefinitions(final List<SProcessDefinition> sProcessDefinitions) throws SBonitaException {\n        openTx();\n        for (final SProcessDefinition sProcessDefinition : sProcessDefinitions) {\n            getServiceAccessor().getActorMappingService().deleteActors(sProcessDefinition.getId());\n            getServiceAccessor().getProcessDefinitionService().disableProcessDeploymentInfo(sProcessDefinition.getId());\n            getServiceAccessor().getProcessDefinitionService().delete(sProcessDefinition.getId());\n        }\n        closeTx();\n    }\n\n    public List<SUser> createEnabledSUsers(final int count, final String firstName, final String lastName,\n            final String password) throws SBonitaException {\n        openTx();\n        final List<SUser> users = new ArrayList<>();\n        for (int i = 1; i <= count; i++) {\n            users.add(getServiceAccessor().getIdentityService()\n                    .createUser(buildEnabledSUser(firstName + i, lastName + i, password + i, 0)));\n        }\n        closeTx();\n\n        return users;\n    }\n\n    public SUser createEnabledSUser(final String firstName, final String lastName, final String password)\n            throws SBonitaException {\n        return createEnabledSUser(firstName, lastName, password, 0);\n    }\n\n    public SUser createEnabledSUser(final String firstName, final String lastName, final String password,\n            final long managerUserId) throws SBonitaException {\n        openTx();\n        final SUser user = getServiceAccessor().getIdentityService()\n                .createUser(buildEnabledSUser(firstName, lastName, password, managerUserId));\n        closeTx();\n\n        return user;\n    }\n\n    public SUser buildEnabledSUser(final String firstName, final String lastName, final String password,\n            final long managerUserId) {\n        final SUser.SUserBuilder userBuilder = SUser.builder();\n        userBuilder.createdBy(2);\n        userBuilder.creationDate(6);\n        userBuilder.enabled(true);\n        userBuilder.firstName(firstName);\n        userBuilder.jobTitle(\"jobTitle\");\n        userBuilder.lastName(lastName);\n        userBuilder.lastUpdate(4L);\n        userBuilder.managerUserId(managerUserId);\n        userBuilder.password(password);\n        userBuilder.title(\"title\");\n        userBuilder.userName(firstName);\n        return userBuilder.build();\n    }\n\n    public SUser createSUser(final String username, final String password) throws SBonitaException {\n        openTx();\n        final SUser.SUserBuilder userBuilder = SUser.builder().userName(username).password(password);\n        final SUser user = getServiceAccessor().getIdentityService().createUser(userBuilder.build());\n        closeTx();\n        return user;\n    }\n\n    public void deleteSUser(final SUser sUser) throws SBonitaException {\n        openTx();\n        getServiceAccessor().getIdentityService().deleteUser(sUser);\n        closeTx();\n    }\n\n    public void deleteSUsers(final SUser... users) throws SBonitaException {\n        if (users != null) {\n            deleteSUsers(Arrays.asList(users));\n        }\n    }\n\n    public void deleteSUsers(final List<SUser> users) throws SBonitaException {\n        openTx();\n        for (final SUser sUser : users) {\n            getServiceAccessor().getIdentityService().deleteUser(sUser);\n        }\n        closeTx();\n    }\n\n    public void deleteSGroup(final SGroup sGroup) throws SBonitaException {\n        openTx();\n        getServiceAccessor().getIdentityService().deleteGroup(sGroup);\n        closeTx();\n    }\n\n    public void deleteSGroups(final SGroup... groups) throws SBonitaException {\n        if (groups != null) {\n            openTx();\n            for (final SGroup sGroup : groups) {\n                getServiceAccessor().getIdentityService().deleteGroup(sGroup);\n            }\n            closeTx();\n        }\n    }\n\n    public void deleteSRole(final SRole sRole) throws SBonitaException {\n        openTx();\n        getServiceAccessor().getIdentityService().deleteRole(sRole);\n        closeTx();\n    }\n\n    public void deleteSRoles(final SRole... roles) throws SBonitaException {\n        if (roles != null) {\n            openTx();\n            for (final SRole sRole : roles) {\n                getServiceAccessor().getIdentityService().deleteRole(sRole);\n            }\n            closeTx();\n        }\n    }\n\n    public SRole createSRole(final String roleName) throws SBonitaException {\n        openTx();\n        final SRole role = SRole.builder().name(roleName).build();\n        getServiceAccessor().getIdentityService().createRole(role, null, null);\n        closeTx();\n        return role;\n    }\n\n    public SUserMembership createSUserMembership(final SUser user, final SGroup group, final SRole role)\n            throws SBonitaException {\n        openTx();\n        final SUserMembership userMembership = SUserMembership.builder().userId(user.getId()).groupId(group.getId())\n                .roleId(role.getId()).build();\n        getServiceAccessor().getIdentityService().createUserMembership(userMembership);\n        closeTx();\n        return userMembership;\n    }\n\n    protected List<SFlowNodeInstance> searchFlowNodeInstances(final QueryOptions searchOptions)\n            throws SBonitaException {\n        openTx();\n        final List<SFlowNodeInstance> flowNodes = getServiceAccessor().getActivityInstanceService()\n                .searchFlowNodeInstances(SFlowNodeInstance.class,\n                        searchOptions);\n        closeTx();\n\n        return flowNodes;\n    }\n\n    private void closeTx() throws STransactionCommitException, STransactionRollbackException {\n        serviceAccessor.getTransactionService().complete();\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/main/java/org/bonitasoft/engine/test/util/TestUtil.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.util;\n\nimport java.util.concurrent.Callable;\n\nimport org.bonitasoft.engine.scheduler.SchedulerService;\nimport org.bonitasoft.engine.scheduler.exception.SSchedulerException;\nimport org.bonitasoft.engine.transaction.STransactionCommitException;\nimport org.bonitasoft.engine.transaction.STransactionException;\nimport org.bonitasoft.engine.transaction.STransactionRollbackException;\nimport org.bonitasoft.engine.transaction.TransactionService;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Baptiste Mesta, Yanyan Liu\n */\npublic class TestUtil {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(TestUtil.class);\n\n    private static final String DEFAULT_USER_NAME = \"install\";\n\n    private static final String DEFAULT_PASSWORD = \"install\";\n\n    public static String getDefaultUserName() {\n        return DEFAULT_USER_NAME;\n    }\n\n    public static String getDefaultPassword() {\n        return DEFAULT_PASSWORD;\n    }\n\n    public static void startScheduler(final SchedulerService scheduler) throws Exception {\n        if (!scheduler.isStarted()) {\n            scheduler.start();\n        }\n    }\n\n    // Always call the startScheduler method after calling this one\n    public static void stopScheduler(final SchedulerService scheduler, final TransactionService txService)\n            throws Exception {\n        if (scheduler.isStarted() && !scheduler.isStopped()) {\n            try {\n                txService.executeInTransaction((Callable<Void>) () -> {\n                    try {\n                        scheduler.deleteJobs();\n                    } catch (SSchedulerException e) {\n                        if (!scheduler.getJobs().isEmpty()) {\n                            LOGGER.error(\"There are still some jobs not deleted!\");\n                            throw e;\n                        }\n                    }\n                    return null;\n                });\n            } catch (final STransactionException txException) {\n                throw new SSchedulerException(txException);\n            }\n            scheduler.stop();\n        }\n    }\n\n    public static void closeTransactionIfOpen(final TransactionService txService) {\n        try {\n            txService.complete();\n        } catch (STransactionCommitException | STransactionRollbackException e) {\n            LOGGER.debug(\"Cannot complete the transaction. Probably already completed. Ignoring.\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/main/resources/logback-test.xml",
    "content": "<configuration debug=\"false\" scan=\"false\">\n\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->\n        <encoder>\n            <pattern>|%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <logger name=\"org.bonitasoft\" level=\"INFO\" />\n    <logger name=\"org.bonitasoft.engine.api.impl.application.installer\" level=\"INFO\" />\n    <logger name=\"org.bonitasoft.engine.api.impl.application.installer.detector.ArtifactTypeDetector\" level=\"DEBUG\" />\n\n    <root level=\"WARN\">\n        <appender-ref ref=\"STDOUT\" />\n    </root>\n    <!-- Show events of synchro repository -->\n    <!-- <logger name=\"org.bonitasoft.engine.test.synchro.SynchroRepository\" level=\"DEBUG\" /> -->\n\n    <!-- Show stack traces when there is error on jobs -->\n    <!-- <logger name=\"org.bonitasoft.engine.scheduler.impl.JobWrapper\" level=\"DEBUG\" /> -->\n     <logger name=\"org.bonitasoft.engine.scheduler.impl.QuartzSchedulerExecutor\" level=\"DEBUG\" />\n\n    <!-- Show lock acquire/release -->\n    <!--<logger name=\"org.bonitasoft.engine.execution.work.FailureHandlingBonitaWork\" level=\"TRACE\" />-->\n    <!--<logger name=\"org.bonitasoft.engine.core.process.instance.impl.GatewayInstanceServiceImpl\" level=\"TRACE\" />-->\n    <!--<logger name=\"org.bonitasoft.engine.execution.ProcessExecutorImpl\" level=\"DEBUG\" />-->\n    <!---<logger name=\"org.bonitasoft.engine.execution.FlowNodeExecutorImpl\" level=\"DEBUG\" />-->\n    <!--<logger name=\"org.bonitasoft.engine.core.process.instance.impl.ActivityInstanceServiceImpl\" level=\"DEBUG\" />-->\n    <!--<logger name=\"org.bonitasoft.engine.recorder.impl.RecorderImpl\" level=\"DEBUG\" />-->\n    <!--<logger name=\"org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceServiceImpl\" level=\"DEBUG\" />-->\n    <!--<logger name=\"org.bonitasoft.engine.execution.work.TxBonitaWork\" level=\"DEBUG\" />-->\n    <!--<logger name=\"org.bonitasoft.engine.execution.work.LockProcessInstanceWork\" level=\"DEBUG\" />-->\n    <!--<logger name=\"org.bonitasoft.engine.work.ExecutorWorkService\" level=\"DEBUG\" />-->\n    <logger name=\"org.bonitasoft.engine.tracking.TimeTracker\" level=\"WARN\" />\n    <!--<logger name=\"org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl\" level=\"ERROR\" />-->\n    <!--<logger name=\"org.hibernate.hql.internal.ast.QueryTranslatorImpl\" level=\"DEBUG\" />-->\n    <logger name=\"org.hibernate.SQL_SLOW\" level=\"INFO\" />\n\n    <!-- Quartz log for job lifecycle -->\n    <!-- <property name=\"baseLogFileName\" value=\"target/log/quartzJobs\" />\n\n    <appender name=\"FILE\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <file>${baseLogFileName}.log</file>\n\n        Support multiple-JVM writing to the same log file\n        <prudent>true</prudent>\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\">\n            <fileNamePattern>${baseLogFileName}-%d{yyyy-MM-dd}.log.gz</fileNamePattern>\n            <maxHistory>30</maxHistory>\n            <cleanHistoryOnStart>true</cleanHistoryOnStart>\n        </rollingPolicy>\n        <encoder>\n            <pattern>|%d{HH:mm:ss.SSS}| %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <logger name=\"org.bonitasoft.engine.scheduler.impl.FileJobListener\" level=\"INFO\">\n        <appender-ref ref=\"FILE\" />\n    </logger> -->\n\n</configuration>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/CallableWithException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface CallableWithException<T> {\n\n    T call() throws Exception;\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/DeleteEventTriggerInstanceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.awaitility.Awaitility.await;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.bar.actorMapping.Actor;\nimport org.bonitasoft.engine.bpm.bar.actorMapping.ActorMapping;\nimport org.bonitasoft.engine.bpm.flownode.TimerType;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.expression.InvalidExpressionException;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class DeleteEventTriggerInstanceIT extends TestWithUser {\n\n    @Before\n    public void cleanTriggers() throws Exception {\n        SQLUtils.execute(\"DELETE FROM event_trigger_instance\");\n    }\n\n    @Test\n    public void should_delete_timer_event_trigger_on_interrupted_process() throws Exception {\n        ProcessDefinition procWithTimers = getProcessAPI()\n                .deployAndEnableProcess(new ProcessDefinitionBuilder().createNewInstance(\"ProcWithTimers\", \"1.0\")\n                        .addStartEvent(\"start\")\n                        .addIntermediateCatchEvent(\"intermediate\")\n                        .addTimerEventTriggerDefinition(TimerType.DURATION, constant(100000))\n                        .addTransition(\"start\", \"intermediate\")\n                        .getProcess());\n        ProcessInstance processInstance = getProcessAPI().startProcess(procWithTimers.getId());\n        await().until(() -> !getProcessAPI()\n                .searchFlowNodeInstances(new SearchOptionsBuilder(0, 1).filter(\"name\", \"intermediate\").done())\n                .getResult().isEmpty());\n\n        getProcessAPI().deleteProcessInstance(processInstance.getId());\n        disableAndDeleteProcess(procWithTimers);\n\n        assertThat(((Number) query(\"SELECT count(*) FROM event_trigger_instance\").get(0)).intValue()).isEqualTo(0);\n    }\n\n    @Test\n    public void should_delete_timer_event_of_aborted_boundary_event() throws Exception {\n        ActorMapping actorMapping = new ActorMapping();\n        Actor actor = new Actor(\"actor\");\n        actorMapping.addActor(actor);\n        actor.addUser(user.getUserName());\n        ProcessDefinition procWithTimers = getProcessAPI().deployAndEnableProcess(\n                new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(\n                        new ProcessDefinitionBuilder().createNewInstance(\"ProcWithBoundaryTimers\", \"1.0\")\n                                .addActor(\"actor\")\n                                .addUserTask(\"myTask\", \"actor\").addBoundaryEvent(\"timerBoundary\", true)\n                                .addTimerEventTriggerDefinition(TimerType.DURATION, constant(100000))\n                                .addUserTask(\"afterBoundary\", \"actor\")\n                                .addUserTask(\"boundaryOut\", \"actor\")\n                                .addTransition(\"timerBoundary\", \"boundaryOut\")\n                                .getProcess())\n                        .setActorMapping(actorMapping)\n                        .done());\n        ProcessInstance processInstance = getProcessAPI().startProcess(procWithTimers.getId());\n\n        long myTask = waitForUserTask(\"myTask\");\n        getProcessAPI().assignAndExecuteUserTask(user.getId(), myTask, Collections.emptyMap());\n        long afterBoundary = waitForUserTask(\"afterBoundary\");\n        getProcessAPI().assignAndExecuteUserTask(user.getId(), afterBoundary, Collections.emptyMap());\n        waitForProcessToFinish(processInstance.getId());\n        assertThat(((Number) query(\"SELECT count(*) FROM event_trigger_instance\").get(0)).intValue()).isEqualTo(0);\n\n        disableAndDeleteProcess(procWithTimers);\n\n        assertThat(((Number) query(\"SELECT count(*) FROM event_trigger_instance\").get(0)).intValue()).isEqualTo(0);\n    }\n\n    private List query(String query) throws Exception {\n        int t = 0;\n        //retry because database locking issues might happen\n        while (true) {\n            t++;\n            try {\n                return SQLUtils.query(query);\n            } catch (Exception e) {\n                if (t > 5) {\n                    throw e;\n                }\n            }\n        }\n\n    }\n\n    private void deleteAllArchivedProcessInstances(ProcessDefinition processDefinition) throws Exception {\n        getProcessAPI().deleteArchivedProcessInstances(processDefinition.getId(), 0, 1000);\n    }\n\n    private Expression constant(long longValue) throws InvalidExpressionException {\n        return new ExpressionBuilder().createConstantLongExpression(longValue);\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/MockQueriableLogSessionProviderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\nimport org.bonitasoft.engine.services.QueriableLogSessionProvider;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class MockQueriableLogSessionProviderImpl implements QueriableLogSessionProvider {\n\n    @Override\n    public String getUserId() {\n        return \"admin\";\n    }\n\n    @Override\n    public String getClusterNode() {\n        return \"node1\";\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/MockQueriableLoggerStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity;\nimport org.bonitasoft.engine.services.QueriableLoggerStrategy;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class MockQueriableLoggerStrategy implements QueriableLoggerStrategy {\n\n    private final List<String> loggable;\n\n    public MockQueriableLoggerStrategy() {\n        this.loggable = new ArrayList<String>();\n        this.loggable.add(\"execute_connector_:\" + SQueriableLogSeverity.BUSINESS);\n        this.loggable.add(\"variable_update_:\" + SQueriableLogSeverity.BUSINESS);\n        this.loggable.add(\"execute_connector_:\" + SQueriableLogSeverity.INTERNAL);\n        this.loggable.add(\"variable_update_:\" + SQueriableLogSeverity.INTERNAL);\n    }\n\n    @Override\n    public boolean isLoggable(final String actionType, final SQueriableLogSeverity severity) {\n        return !this.loggable.contains(actionType + \":\" + severity.toString());\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/PageAPILocalIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class PageAPILocalIT extends CommonAPIIT {\n\n    @Before\n    public void before() throws BonitaException {\n        loginWithTechnicalUser();\n    }\n\n    @After\n    public void after() throws BonitaException {\n        final SearchResult<Page> searchPages = getPageAPI().searchPages(new SearchOptionsBuilder(0, 1000).done());\n        for (final Page page : searchPages.getResult()) {\n            if (!page.isProvided()) {\n                getPageAPI().deletePage(page.getId());\n            }\n        }\n        logout();\n    }\n\n    /*\n     * when the tenant is created the provided page \"bonita-home-page.zip\" should be imported from classpath\n     */\n    @Test\n    public void should_provided_page_be_imported() throws BonitaException {\n        // given\n        // engine started\n\n        // when\n        final Page homePage = getPageAPI().getPageByName(\"custompage_home\");\n\n        // then\n        assertThat(homePage).isNotNull();\n        assertThat(homePage.isProvided()).isTrue();\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/RunnableWithException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface RunnableWithException {\n\n    void run() throws Exception;\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/SQLUtils.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\nimport java.lang.reflect.Field;\nimport java.util.List;\n\nimport org.bonitasoft.engine.persistence.HibernatePersistenceService;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.service.ServiceAccessorSingleton;\nimport org.hibernate.SessionFactory;\n\npublic class SQLUtils {\n\n    private static SessionFactory sessionFactory;\n\n    public static List query(String query) throws Exception {\n        return ServiceAccessorSingleton.getInstance().getUserTransactionService()\n                .executeInTransaction(() -> getSessionFactory().getCurrentSession().createSQLQuery(query).list());\n    }\n\n    public static int execute(String query) throws Exception {\n        return ServiceAccessorSingleton.getInstance().getUserTransactionService()\n                .executeInTransaction(\n                        () -> getSessionFactory().getCurrentSession().createSQLQuery(query).executeUpdate());\n    }\n\n    private static SessionFactory getSessionFactory() throws NoSuchFieldException, IllegalAccessException {\n        if (sessionFactory == null) {\n            sessionFactory = createSessionFactory();\n        }\n        return sessionFactory;\n    }\n\n    private static SessionFactory createSessionFactory() throws NoSuchFieldException, IllegalAccessException {\n        ReadPersistenceService persistenceService = ServiceAccessorSingleton.getInstance().getReadPersistenceService();\n        Field sessionFactoryField = HibernatePersistenceService.class.getDeclaredField(\"sessionFactory\");\n        sessionFactoryField.setAccessible(true);\n        return (SessionFactory) sessionFactoryField.get(persistenceService);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/application/installer/ApplicationInstallerIT.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.application.installer;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\nimport java.io.File;\n\nimport org.bonitasoft.engine.CommonAPIIT;\nimport org.bonitasoft.engine.api.impl.application.installer.ApplicationArchive;\nimport org.bonitasoft.engine.api.impl.application.installer.ApplicationArchiveReader;\nimport org.bonitasoft.engine.api.impl.application.installer.ApplicationInstaller;\nimport org.bonitasoft.engine.api.impl.application.installer.detector.ArtifactTypeDetector;\nimport org.bonitasoft.engine.api.impl.application.installer.detector.BdmDetector;\nimport org.bonitasoft.engine.api.impl.application.installer.detector.CustomPageDetector;\nimport org.bonitasoft.engine.api.impl.application.installer.detector.IconDetector;\nimport org.bonitasoft.engine.api.impl.application.installer.detector.LayoutDetector;\nimport org.bonitasoft.engine.api.impl.application.installer.detector.LivingApplicationDetector;\nimport org.bonitasoft.engine.api.impl.application.installer.detector.OrganizationDetector;\nimport org.bonitasoft.engine.api.impl.application.installer.detector.PageAndFormDetector;\nimport org.bonitasoft.engine.api.impl.application.installer.detector.ProcessDetector;\nimport org.bonitasoft.engine.api.impl.application.installer.detector.ThemeDetector;\nimport org.bonitasoft.engine.bpm.process.ActivationState;\nimport org.bonitasoft.engine.bpm.process.ConfigurationState;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.business.application.ApplicationNotFoundException;\nimport org.bonitasoft.engine.exception.ApplicationInstallationException;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.service.ServiceAccessorSingleton;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class ApplicationInstallerIT extends CommonAPIIT {\n\n    @Before\n    public void before() throws Exception {\n        loginWithTechnicalUser();\n    }\n\n    @After\n    public void after() throws Exception {\n        if (!getTenantAdministrationAPI().isPaused()) {\n            getTenantAdministrationAPI().pause();\n            getTenantAdministrationAPI().cleanAndUninstallBusinessDataModel();\n            getTenantAdministrationAPI().resume();\n        }\n        logout();\n    }\n\n    @Test\n    public void custom_application_should_be_deployed_entirely() throws Exception {\n        // ensure application did not exist initially:\n        assertThatExceptionOfType(ApplicationNotFoundException.class)\n                .isThrownBy(() -> getApplicationAPI().getIApplicationByToken(\"appsManagerBonita\"));\n\n        // given:\n        ApplicationInstaller applicationInstaller = ServiceAccessorSingleton.getInstance()\n                .lookup(ApplicationInstaller.class);\n        final ApplicationArchiveReader applicationArchiveReader = new ApplicationArchiveReader(\n                new ArtifactTypeDetector(new BdmDetector(),\n                        new LivingApplicationDetector(), new OrganizationDetector(), new CustomPageDetector(),\n                        new ProcessDetector(), new ThemeDetector(), new PageAndFormDetector(), new LayoutDetector(),\n                        new IconDetector()));\n\n        // when:\n        try (var applicationAsStream = ApplicationInstallerIT.class.getResourceAsStream(\"/customer-application.zip\")) {\n            var applicationArchive = applicationArchiveReader.read(applicationAsStream);\n            applicationInstaller.install(applicationArchive);\n        }\n\n        // then:\n\n        // Organization has been installed:\n        final User captainBonita = getIdentityAPI().getUserByUserName(\"captainBonita\");\n        assertThat(captainBonita).isNotNull();\n        assertThat(getIdentityAPI().getRoleByName(\"appsManager\")).isNotNull();\n        assertThat(getIdentityAPI().getGroupByPath(\"/appsManagement\")).isNotNull();\n\n        assertThat(getApplicationAPI().getIApplicationByToken(\"appsManagerBonita\").getDisplayName())\n                .isEqualTo(\"Application manager\");\n        final long processDefinitionId = getProcessAPI().getProcessDefinitionId(\"CallHealthCheck\", \"1.0\");\n        final ProcessDeploymentInfo deploymentInfo = getProcessAPI().getProcessDeploymentInfo(processDefinitionId);\n        assertThat(deploymentInfo.getConfigurationState()).isEqualTo(ConfigurationState.RESOLVED);\n        assertThat(deploymentInfo.getActivationState()).isEqualTo(ActivationState.ENABLED);\n\n        // Rest API Extension is there:\n        assertThat(getPageAPI().getPageByName(\"custompage_processStarter\")).isNotNull();\n\n        // Pages are there:\n        assertThat(getPageAPI().getPageByName(\"custompage_HealthPage\")).isNotNull();\n\n        // Layouts are there:\n        assertThat(getPageAPI().getPageByName(\"custompage_pmLayout\")).isNotNull();\n    }\n\n    @Test\n    public void custom_application_should_be_installed_with_configuration() throws Exception {\n        // given:\n        ApplicationInstaller applicationInstaller = ServiceAccessorSingleton.getInstance()\n                .lookup(ApplicationInstaller.class);\n        final ApplicationArchiveReader applicationArchiveReader = new ApplicationArchiveReader(\n                new ArtifactTypeDetector(new BdmDetector(),\n                        new LivingApplicationDetector(), new OrganizationDetector(), new CustomPageDetector(),\n                        new ProcessDetector(), new ThemeDetector(), new PageAndFormDetector(), new LayoutDetector(),\n                        new IconDetector()));\n\n        // when:\n        try (var applicationAsStream = ApplicationInstallerIT.class\n                .getResourceAsStream(\"/simple-app-1.0.0-SNAPSHOT-local.zip\")) {\n            var applicationArchive = applicationArchiveReader.read(applicationAsStream);\n            applicationArchive.setConfigurationFile(new File(ApplicationInstallerIT.class\n                    .getResource(\"/simple-app-1.0.0-SNAPSHOT-local.bconf\").getFile()));\n            applicationInstaller.install(applicationArchive);\n        }\n\n        final long processDefinitionId = getProcessAPI().getProcessDefinitionId(\"Pool\", \"1.0\");\n        final ProcessDeploymentInfo deploymentInfo = getProcessAPI().getProcessDeploymentInfo(processDefinitionId);\n        assertThat(deploymentInfo.getConfigurationState()).isEqualTo(ConfigurationState.RESOLVED);\n        assertThat(deploymentInfo.getActivationState()).isEqualTo(ActivationState.ENABLED);\n        var paramInstance = getProcessAPI().getParameterInstance(processDefinitionId, \"hello\");\n        assertThat(paramInstance.getValue()).isEqualTo(\"world_post_install\");\n    }\n\n    @Test\n    public void empty_custom_application_should_throw_an_exception() throws Exception {\n        // given:\n        ApplicationInstaller applicationInstaller = ServiceAccessorSingleton.getInstance()\n                .lookup(ApplicationInstaller.class);\n        final ApplicationArchiveReader applicationArchiveReader = new ApplicationArchiveReader(\n                new ArtifactTypeDetector(new BdmDetector(),\n                        new LivingApplicationDetector(), new OrganizationDetector(), new CustomPageDetector(),\n                        new ProcessDetector(), new ThemeDetector(), new PageAndFormDetector(),\n                        new LayoutDetector(), new IconDetector()));\n\n        try (var applicationAsStream = ApplicationInstallerIT.class\n                .getResourceAsStream(\"/empty-customer-application.zip\")) {\n            final ApplicationArchive applicationArchive = applicationArchiveReader.read(applicationAsStream);\n\n            // then:\n            assertThatExceptionOfType(ApplicationInstallationException.class)\n                    .isThrownBy(() -> applicationInstaller.install(applicationArchive))\n                    .withMessage(\"The Application Archive contains no valid artifact to install\");\n        }\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/application/installer/ApplicationInstallerUpdateIT.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.application.installer;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.time.Duration;\nimport java.time.temporal.ChronoUnit;\n\nimport org.awaitility.Awaitility;\nimport org.bonitasoft.engine.CommonAPIIT;\nimport org.bonitasoft.engine.api.impl.application.installer.ApplicationArchive;\nimport org.bonitasoft.engine.api.impl.application.installer.ApplicationArchiveReader;\nimport org.bonitasoft.engine.api.impl.application.installer.ApplicationInstaller;\nimport org.bonitasoft.engine.api.impl.application.installer.detector.ArtifactTypeDetector;\nimport org.bonitasoft.engine.api.impl.application.installer.detector.BdmDetector;\nimport org.bonitasoft.engine.api.impl.application.installer.detector.CustomPageDetector;\nimport org.bonitasoft.engine.api.impl.application.installer.detector.IconDetector;\nimport org.bonitasoft.engine.api.impl.application.installer.detector.LayoutDetector;\nimport org.bonitasoft.engine.api.impl.application.installer.detector.LivingApplicationDetector;\nimport org.bonitasoft.engine.api.impl.application.installer.detector.OrganizationDetector;\nimport org.bonitasoft.engine.api.impl.application.installer.detector.PageAndFormDetector;\nimport org.bonitasoft.engine.api.impl.application.installer.detector.ProcessDetector;\nimport org.bonitasoft.engine.api.impl.application.installer.detector.ThemeDetector;\nimport org.bonitasoft.engine.bpm.process.ActivationState;\nimport org.bonitasoft.engine.bpm.process.ConfigurationState;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.business.application.ApplicationNotFoundException;\nimport org.bonitasoft.engine.business.application.ApplicationPage;\nimport org.bonitasoft.engine.business.application.IApplication;\nimport org.bonitasoft.engine.exception.ApplicationInstallationException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.engine.page.PageNotFoundException;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.service.ServiceAccessorSingleton;\nimport org.bonitasoft.engine.tenant.TenantResource;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Haroun El Alami\n * @author Danila Mazour\n */\npublic class ApplicationInstallerUpdateIT extends CommonAPIIT {\n\n    private ApplicationInstaller applicationInstaller;\n    private ApplicationArchiveReader applicationArchiveReader;\n\n    @Before\n    public void before() throws Exception {\n        loginWithTechnicalUser();\n\n        applicationInstaller = ServiceAccessorSingleton.getInstance()\n                .lookup(ApplicationInstaller.class);\n        applicationArchiveReader = new ApplicationArchiveReader(\n                new ArtifactTypeDetector(new BdmDetector(),\n                        new LivingApplicationDetector(), new OrganizationDetector(), new CustomPageDetector(),\n                        new ProcessDetector(), new ThemeDetector(), new PageAndFormDetector(), new LayoutDetector(),\n                        new IconDetector()));\n        initFirstInstall();\n    }\n\n    private void initFirstInstall() throws Exception {\n        // ensure application did not exist initially:\n        assertThatExceptionOfType(ApplicationNotFoundException.class)\n                .isThrownBy(() -> getApplicationAPI().getIApplicationByToken(\"appsManagerBonita\"));\n\n        // given:\n        try (final InputStream applicationAsStream = this.getClass().getResourceAsStream(\"/customer-application.zip\")) {\n            // when:\n            applicationInstaller.install(applicationArchiveReader.read(applicationAsStream));\n        }\n\n        // then:\n\n        // Organization has been installed:\n        final User captainBonita = getIdentityAPI().getUserByUserName(\"captainBonita\");\n        assertThat(captainBonita).isNotNull();\n        assertThat(getIdentityAPI().getRoleByName(\"appsManager\")).isNotNull();\n        assertThat(getIdentityAPI().getGroupByPath(\"/appsManagement\")).isNotNull();\n\n        assertThat(getApplicationAPI().getIApplicationByToken(\"appsManagerBonita\").getDisplayName())\n                .isEqualTo(\"Application manager\");\n        final long processDefinitionId = getProcessAPI().getProcessDefinitionId(\"CallHealthCheck\", \"1.0\");\n        final ProcessDeploymentInfo deploymentInfo = getProcessAPI().getProcessDeploymentInfo(processDefinitionId);\n        assertThat(deploymentInfo.getConfigurationState()).isEqualTo(ConfigurationState.RESOLVED);\n        assertThat(deploymentInfo.getActivationState()).isEqualTo(ActivationState.ENABLED);\n\n        // Rest API Extension is there:\n        assertThat(getPageAPI().getPageByName(\"custompage_processStarter\")).isNotNull();\n\n        // Pages are there:\n        assertThat(getPageAPI().getPageByName(\"custompage_HealthPage\")).isNotNull();\n\n        // Layouts are there:\n        assertThat(getPageAPI().getPageByName(\"custompage_pmLayout\")).isNotNull();\n    }\n\n    @Test\n    public void empty_custom_application_should_throw_an_exception() throws Exception {\n        // given:\n        final InputStream applicationAsStream = this.getClass().getResourceAsStream(\"/empty-customer-application.zip\");\n\n        final ApplicationArchive applicationArchive = applicationArchiveReader.read(applicationAsStream);\n\n        // then:\n        assertThatExceptionOfType(ApplicationInstallationException.class)\n                .isThrownBy(() -> applicationInstaller.update(applicationArchive))\n                .withMessage(\"The Application Archive contains no valid artifact to install\");\n    }\n\n    @Test\n    public void process_update_custom_application_with_same_installed_version() throws ApplicationNotFoundException,\n            PageNotFoundException, ProcessDefinitionNotFoundException, IOException, ApplicationInstallationException {\n        // given:\n        //installed resources\n        TenantResource bdm = getTenantAdministrationAPI().getBusinessDataModelResource();\n        IApplication application = getApplicationAPI().getIApplicationByToken(\"appsManagerBonita\");\n        Page processStarterAPI = getPageAPI().getPageByName(\"custompage_processStarter\");\n        Page healthPage = getPageAPI().getPageByName(\"custompage_HealthPage\");\n        Page pmLayout = getPageAPI().getPageByName(\"custompage_pmLayout\");\n        final long processDefinitionId = getProcessAPI().getProcessDefinitionId(\"CallHealthCheck\", \"1.0\");\n        final ProcessDeploymentInfo deploymentInfo = getProcessAPI().getProcessDeploymentInfo(processDefinitionId);\n\n        // when:\n        try (var applicationAsStream = this.getClass().getResourceAsStream(\"/customer-application.zip\")) {\n            // Avoid test failure due to instant update.\n            Awaitility.await().timeout(Duration.of(1, ChronoUnit.SECONDS));\n            applicationInstaller.update(applicationArchiveReader.read(applicationAsStream));\n        }\n        // then:\n        TenantResource updatedBdm = getTenantAdministrationAPI().getBusinessDataModelResource();\n        IApplication updatedApplication = getApplicationAPI().getIApplicationByToken(\"appsManagerBonita\");\n        Page updatedProcessStarterAPI = getPageAPI().getPageByName(\"custompage_processStarter\");\n        Page updatedHealthPage = getPageAPI().getPageByName(\"custompage_HealthPage\");\n        Page updatedPmLayout = getPageAPI().getPageByName(\"custompage_pmLayout\");\n        final ProcessDeploymentInfo deploymentInfoAfterUpdate = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinitionId);\n\n        // check that bdm resource has NOT been updated (same content)\n        assertThat(updatedBdm.getLastUpdateDate()).isEqualTo(bdm.getLastUpdateDate());\n        // check that resources has been updated\n        assertThat(updatedApplication.getLastUpdateDate().after(application.getLastUpdateDate())).isTrue();\n        assertThat(\n                updatedProcessStarterAPI.getLastModificationDate().after(processStarterAPI.getLastModificationDate()))\n                .isTrue();\n        assertThat(updatedHealthPage.getLastModificationDate().after(healthPage.getLastModificationDate())).isTrue();\n        assertThat(updatedPmLayout.getLastModificationDate().after(pmLayout.getLastModificationDate())).isTrue();\n\n        // CallHealthCheck Process must not be updated\n        assertThat(deploymentInfoAfterUpdate.getLastUpdateDate()).isEqualTo(deploymentInfo.getLastUpdateDate());\n    }\n\n    @Test\n    public void process_update_custom_application_with_new_version()\n            throws ApplicationNotFoundException, PageNotFoundException, ProcessDefinitionNotFoundException, IOException,\n            ApplicationInstallationException, SearchException {\n        // given:\n        //installed resources\n        TenantResource bdm = getTenantAdministrationAPI().getBusinessDataModelResource();\n        IApplication application = getApplicationAPI().getIApplicationByToken(\"appsManagerBonita\");\n        Page processStarterAPI = getPageAPI().getPageByName(\"custompage_processStarter\");\n        Page healthPage = getPageAPI().getPageByName(\"custompage_HealthPage\");\n        Page pmLayout = getPageAPI().getPageByName(\"custompage_pmLayout\");\n        final long processDefinitionId = getProcessAPI().getProcessDefinitionId(\"CallHealthCheck\", \"1.0\");\n        final ProcessDeploymentInfo deploymentInfo = getProcessAPI().getProcessDeploymentInfo(processDefinitionId);\n\n        // when:\n        final InputStream applicationAsStream = this.getClass().getResourceAsStream(\"/customer-application-v2.zip\");\n        applicationInstaller.update(applicationArchiveReader.read(applicationAsStream));\n\n        // then:\n        TenantResource updatedBdm = getTenantAdministrationAPI().getBusinessDataModelResource();\n        IApplication updatedApplication = getApplicationAPI().getIApplicationByToken(\"appsManagerBonita\");\n        Page updatedProcessStarterAPI = getPageAPI().getPageByName(\"custompage_processStarter\");\n        Page updatedHealthPage = getPageAPI().getPageByName(\"custompage_HealthPage\");\n        Page updatedPmLayout = getPageAPI().getPageByName(\"custompage_pmLayout\");\n        final ProcessDeploymentInfo deploymentInfoAfterUpdate = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinitionId);\n\n        // check that resources has been updated\n        assertThat(updatedBdm.getLastUpdateDate()).isAfter(bdm.getLastUpdateDate());\n        // check installed apps\n        assertThat(updatedApplication.getLastUpdateDate().after(application.getLastUpdateDate())).isTrue();\n        // fetch application menus\n        ApplicationPage healthzPage = getApplicationAPI().searchApplicationPages(\n                new SearchOptionsBuilder(0, Integer.MAX_VALUE)\n                        .filter(\"applicationId\", (Long) updatedApplication.getId())\n                        .done())\n                .getResult().get(0);\n        assertThat(healthzPage.getToken()).isEqualTo(\"healthz\");\n\n        // new app installed\n        assertThat(getPageAPI().getPageByName(\"custompage_reportsPage\")).isNotNull();\n\n        // check updated custom pages\n        assertThat(\n                updatedProcessStarterAPI.getLastModificationDate().after(processStarterAPI.getLastModificationDate()))\n                .isTrue();\n        assertThat(updatedProcessStarterAPI.getContentName()).isEqualTo(\"processStarter-1.1.zip\");\n        assertThat(updatedHealthPage.getLastModificationDate().after(healthPage.getLastModificationDate())).isTrue();\n        assertThat(updatedHealthPage.getContentName()).isEqualTo(\"page_HealthPage.zip\");\n        assertThat(updatedPmLayout.getLastModificationDate().after(pmLayout.getLastModificationDate())).isTrue();\n        assertThat(updatedPmLayout.getContentName()).isEqualTo(\"layout_pmLayout.zip\");\n\n        // CallHealthCheck Process must not be updated\n        assertThat(deploymentInfoAfterUpdate.getLastUpdateDate().after(deploymentInfo.getLastUpdateDate())).isTrue();\n        assertThat(deploymentInfoAfterUpdate.getActivationState()).isEqualTo(ActivationState.DISABLED);\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/archive/ArchiveServiceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.archive;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\n\nimport org.bonitasoft.engine.bpm.CommonBPMServicesTest;\nimport org.bonitasoft.engine.data.instance.model.archive.SAShortTextDataInstance;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class ArchiveServiceIT extends CommonBPMServicesTest {\n\n    private static final long START_OF_2009 = 1230739500052L;\n\n    private static final long BEFORE_2009 = 1130739200052L;\n\n    private static final int ONE_DAY = 86400000;\n\n    private ArchiveService archiveService;\n\n    @Before\n    public void before() {\n        this.archiveService = getServiceAccessor().getArchiveService();\n    }\n\n    @Test\n    public void testRecordInsert() throws Exception {\n        getTransactionService().begin();\n\n        final SAShortTextDataInstance dataInstance = insertDataWithYesterdayDate();\n        assertNotNull(dataInstance);\n\n        getTransactionService().complete();\n    }\n\n    @Test\n    public void archiveInSlidingArchiveNotDone() throws Exception {\n\n        getTransactionService().begin();\n        final SAShortTextDataInstance dataInstance = insertDataWithFirstJanuary2009Date();\n        getTransactionService().complete();\n\n        getTransactionService().begin();\n\n        final SAShortTextDataInstance dataInstanceFromArchive = selectDataByIdFromDefinitiveArchive(dataInstance);\n        assertNotNull(\"should be in definitive archive\", dataInstanceFromArchive);\n        assertEquals(dataInstance.getName(), dataInstanceFromArchive.getName());\n        assertEquals(dataInstance.getValue(), dataInstanceFromArchive.getValue());\n\n        getTransactionService().complete();\n    }\n\n    @Test\n    public void insertWithNoDefinitiveArchiveForThatDate() throws Exception {\n        getTransactionService().begin();\n        try {\n            insertDataWithBefore2009Date();\n        } finally {\n            getTransactionService().complete();\n        }\n    }\n\n    private SAShortTextDataInstance insertDataWithYesterdayDate() throws SRecorderException {\n        return insertData(System.currentTimeMillis() - ONE_DAY);\n    }\n\n    private SAShortTextDataInstance insertDataWithFirstJanuary2009Date() throws SRecorderException {\n        return insertData(START_OF_2009);\n    }\n\n    private SAShortTextDataInstance insertDataWithBefore2009Date() throws SRecorderException {\n        return insertData(BEFORE_2009);\n    }\n\n    private SAShortTextDataInstance insertData(long before2009) throws SRecorderException {\n        final SAShortTextDataInstance data = new SAShortTextDataInstance();\n        data.setName(\"archiveTestEmployee\");\n        data.setValue(\"password\");\n        archiveService.recordInsert(before2009, new ArchiveInsertRecord(data));\n        return data;\n    }\n\n    private SAShortTextDataInstance selectDataByIdFromDefinitiveArchive(final SAShortTextDataInstance dataInstance)\n            throws SBonitaReadException {\n        final SelectByIdDescriptor<SAShortTextDataInstance> selectByIdDescriptor1 = new SelectByIdDescriptor<>(\n                SAShortTextDataInstance.class,\n                dataInstance.getId());\n        return archiveService.getDefinitiveArchiveReadPersistenceService().selectById(selectByIdDescriptor1);\n    }\n\n    @Test\n    public void testRecordDelete() throws Exception {\n        getTransactionService().begin();\n\n        final SAShortTextDataInstance dataInstance = insertDataWithYesterdayDate();\n\n        getTransactionService().complete();\n\n        getTransactionService().begin();\n\n        archiveService.recordDelete(new DeleteRecord(dataInstance));\n\n        getTransactionService().complete();\n    }\n\n    @Test\n    public void testGetDefinitiveArchiveReadPersistenceService() {\n        final ReadPersistenceService persistenceService = archiveService.getDefinitiveArchiveReadPersistenceService();\n        assertNotNull(persistenceService);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/archive/model/TestLogBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.archive.model;\n\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class TestLogBuilder extends CRUDELogBuilder implements SPersistenceLogBuilder {\n\n    @Override\n    public SPersistenceLogBuilder objectId(final long objectId) {\n        queriableLogBuilder.numericIndex(0, objectId);\n        return this;\n    }\n\n    @Override\n    protected String getActionTypePrefix() {\n        return \"TEST\";\n    }\n\n    @Override\n    protected void checkExtraRules(final SQueriableLog log) {\n        // nothing here\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/archive/model/TestLogBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.archive.model;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class TestLogBuilderFactory extends CRUDELogBuilderFactory implements SPersistenceLogBuilderFactory {\n\n    @Override\n    public TestLogBuilder createNewInstance() {\n        return new TestLogBuilder();\n    }\n\n    @Override\n    public String getObjectIdKey() {\n        return \"numericIndex1\";\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/authentication/AuthenticationServiceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.authentication;\n\nimport static org.junit.Assert.assertNull;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.CommonBPMServicesTest;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class AuthenticationServiceIT extends CommonBPMServicesTest {\n\n    private static GenericAuthenticationService authService;\n\n    private static IdentityService identityService;\n\n    @Before\n    public void setup() {\n        identityService = getServiceAccessor().getIdentityService();\n        authService = getServiceAccessor().getAuthenticationService();\n    }\n\n    @Test\n    public void testCheckValidUser() throws Exception {\n        final String username = \"john\";\n        final String password = \"bpm\";\n        final SUser user = createUser(username, password);\n\n        getTransactionService().begin();\n        Map<String, Serializable> credentials = new HashMap<String, Serializable>();\n        credentials.put(AuthenticationConstants.BASIC_PASSWORD, password);\n        credentials.put(AuthenticationConstants.BASIC_USERNAME, username);\n        authService.checkUserCredentials(credentials);\n        getTransactionService().complete();\n\n        deleteUser(user);\n    }\n\n    private SUser createUser(final String username, final String password) throws Exception {\n        getTransactionService().begin();\n        final SUser.SUserBuilder userBuilder = SUser.builder().userName(username).password(password);\n        final SUser user = identityService.createUser(userBuilder.build());\n        getTransactionService().complete();\n\n        return user;\n    }\n\n    private void deleteUser(final SUser user) throws Exception {\n        getTransactionService().begin();\n        identityService.deleteUser(user);\n        getTransactionService().complete();\n    }\n\n    @Test\n    public void testCheckUserWithWrongPassword() throws Exception {\n        final String username = \"james\";\n        final String password = \"bpm\";\n        final SUser user = createUser(username, password);\n\n        getTransactionService().begin();\n        Map<String, Serializable> credentials = new HashMap<String, Serializable>();\n        credentials.put(AuthenticationConstants.BASIC_PASSWORD, \"wrong\");\n        credentials.put(AuthenticationConstants.BASIC_USERNAME, username);\n        final String userNameResult = authService.checkUserCredentials(credentials);\n        getTransactionService().complete();\n        assertNull(userNameResult);\n\n        deleteUser(user);\n    }\n\n    @Test\n    public void testCheckNonExistentUser() throws Exception {\n        final String username = \"anonyme\";\n        final String password = \"bpm\";\n        getTransactionService().begin();\n        Map<String, Serializable> credentials = new HashMap<String, Serializable>();\n        credentials.put(AuthenticationConstants.BASIC_PASSWORD, password);\n        credentials.put(AuthenticationConstants.BASIC_USERNAME, username);\n        final String userNameResult = authService.checkUserCredentials(credentials);\n        getTransactionService().complete();\n        assertNull(userNameResult);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/ActorMappingServiceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm;\n\nimport static org.junit.Assert.*;\n\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.actor.mapping.SActorNotFoundException;\nimport org.bonitasoft.engine.actor.mapping.model.SActor;\nimport org.bonitasoft.engine.actor.mapping.model.SActorMember;\nimport org.bonitasoft.engine.api.impl.IdentityAPIImpl;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.identity.Group;\nimport org.bonitasoft.engine.identity.Role;\nimport org.bonitasoft.engine.transaction.STransactionException;\nimport org.junit.After;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class ActorMappingServiceIT extends CommonBPMServicesTest {\n\n    private ActorMappingService actorMappingService;\n\n    private Group mainGroup;\n\n    private Group parentGroup;\n\n    private Group childGroup;\n\n    @Before\n    public void setup() throws Exception {\n        actorMappingService = getServiceAccessor().getActorMappingService();\n        mainGroup = createGroup(\"main\");\n        parentGroup = createGroup(\"parent\");\n        childGroup = createGroup(\"child\", \"/parent\");\n    }\n\n    @After\n    public void tearDown() throws DeletionException {\n        try {\n            getTransactionService().begin();\n            new IdentityAPIImpl()\n                    .deleteGroups(Arrays.asList(childGroup.getId(), parentGroup.getId(), mainGroup.getId()));\n            getTransactionService().complete();\n        } catch (STransactionException e) {\n            throw new DeletionException(e);\n        }\n    }\n\n    @Test(expected = SActorNotFoundException.class)\n    public void cannotGetAnUnknownActor() throws Exception {\n        getTransactionService().executeInTransaction(() -> actorMappingService.getActor(0));\n    }\n\n    @Test\n    public void getAnActor() throws SBonitaException {\n        final Set<SActor> actors = new HashSet<>();\n        final String manager = \"Manager\";\n        final long scopeId = 12;\n        final SActor actor = SActor.builder().name(manager).scopeId(scopeId).initiator(false).build();\n        actors.add(actor);\n        getTransactionService().begin();\n        try {\n            actorMappingService.addActors(actors);\n        } finally {\n            getTransactionService().complete();\n        }\n        getTransactionService().begin();\n        try {\n            final SActor sActor = actorMappingService.getActor(manager, scopeId);\n            assertNotNull(sActor);\n            assertEquals(manager, sActor.getName());\n            assertEquals(scopeId, sActor.getScopeId());\n            actorMappingService.deleteActors(scopeId);\n        } finally {\n            getTransactionService().complete();\n        }\n    }\n\n    @Test\n    public void getActorsFromActorIds() throws SBonitaException {\n        final Set<SActor> actors = new HashSet<>();\n\n        final String manager = \"Manager\";\n        final long scopeId = 12;\n        final SActor actor = SActor.builder().name(manager).scopeId(scopeId).initiator(false).build();\n        actors.add(actor);\n\n        final String manager2 = \"Leader\";\n        final long scopeId2 = 12;\n        final SActor actor2 = SActor.builder().name(manager2).scopeId(scopeId2).initiator(false).build();\n        actors.add(actor2);\n\n        getTransactionService().begin();\n        try {\n            actorMappingService.addActors(actors);\n        } finally {\n            getTransactionService().complete();\n        }\n        getTransactionService().begin();\n        long actor1id, actor2id;\n        try {\n            final SActor sActor1 = actorMappingService.getActor(manager, scopeId);\n            assertNotNull(sActor1);\n            assertEquals(manager, sActor1.getName());\n            assertEquals(scopeId, sActor1.getScopeId());\n            actor1id = sActor1.getId();\n\n            final SActor sActor2 = actorMappingService.getActor(manager2, scopeId2);\n            assertNotNull(sActor2);\n            assertEquals(manager2, sActor2.getName());\n            assertEquals(scopeId2, sActor2.getScopeId());\n            actor2id = sActor2.getId();\n        } finally {\n            getTransactionService().complete();\n        }\n\n        getTransactionService().begin();\n        try {\n            final List<Long> actorIds = Arrays.asList(actor1id, actor2id);\n            final List<SActor> actorsRes = actorMappingService.getActors(actorIds);\n\n            boolean isHasManager = false;\n            boolean isHasManager2 = false;\n            assertEquals(2, actorsRes.size());\n            for (final SActor sActor : actorsRes) {\n                if (manager.equals(sActor.getName())) {\n                    isHasManager = true;\n                } else {\n                    if (manager2.equals(sActor.getName())) {\n                        isHasManager2 = true;\n                    }\n                }\n            }\n            assertTrue(isHasManager && isHasManager2);\n\n            actorMappingService.deleteActors(scopeId2);\n        } finally {\n            getTransactionService().complete();\n        }\n    }\n\n    @Test\n    public void addAndRemoveAUserOfAnActor() throws Exception {\n        Set<SActor> actors = new HashSet<>();\n        final SActor actor = SActor.builder().name(\"Manager\").scopeId(12).initiator(false).build();\n        actors.add(actor);\n\n        getTransactionService().begin();\n        actors = actorMappingService.addActors(actors);\n        final long actorId = actors.iterator().next().getId();\n\n        List<SActorMember> actorMembers = actorMappingService.getActorMembers(actorId, 0, 10);\n        Assert.assertEquals(0, actorMembers.size());\n\n        actorMappingService.addUserToActor(actorId, 1);\n        getTransactionService().complete();\n\n        getTransactionService().begin();\n        try {\n            actorMembers = actorMappingService.getActorMembers(actorId, 0, 10);\n            Assert.assertEquals(1, actorMembers.size());\n            checkActorMember(actorMembers.get(0), 1, -1, -1);\n\n            actorMappingService.deleteActorMember(actorMembers.get(0).getId());\n            actorMembers = actorMappingService.getActorMembers(actorId, 0, 10);\n            Assert.assertEquals(0, actorMembers.size());\n            actorMappingService.deleteActors(12);\n        } finally {\n            getTransactionService().complete();\n        }\n    }\n\n    private void checkActorMember(final SActorMember sActorMember, final long userId, final long groupId,\n            final long roleId) {\n        assertEquals(userId, sActorMember.getUserId());\n        assertEquals(groupId, sActorMember.getGroupId());\n        assertEquals(roleId, sActorMember.getRoleId());\n    }\n\n    @Test\n    public void addAndRemoveARoleOfAnActor() throws Exception {\n        Set<SActor> actors = new HashSet<>();\n        final SActor actor = SActor.builder().name(\"Manager\").scopeId(12).initiator(false).build();\n        actors.add(actor);\n\n        getTransactionService().begin();\n        actors = actorMappingService.addActors(actors);\n        final long actorId = actors.iterator().next().getId();\n\n        List<SActorMember> actorMembers = actorMappingService.getActorMembers(actorId, 0, 10);\n        Assert.assertEquals(0, actorMembers.size());\n\n        actorMappingService.addRoleToActor(actorId, 1);\n        getTransactionService().complete();\n\n        getTransactionService().begin();\n        try {\n            actorMembers = actorMappingService.getActorMembers(actorId, 0, 10);\n            Assert.assertEquals(1, actorMembers.size());\n            checkActorMember(actorMembers.get(0), -1, -1, 1);\n\n            actorMappingService.deleteActorMember(actorMembers.get(0).getId());\n            actorMembers = actorMappingService.getActorMembers(actorId, 0, 10);\n            Assert.assertEquals(0, actorMembers.size());\n            actorMappingService.deleteActors(12);\n        } finally {\n            getTransactionService().complete();\n        }\n    }\n\n    @Test\n    public void addAndRemoveAGroupOfAnActor() throws Exception {\n        Set<SActor> actors = new HashSet<>();\n        final SActor actor = SActor.builder().name(\"Manager\").scopeId(12).initiator(false).build();\n        actors.add(actor);\n\n        getTransactionService().begin();\n        actors = actorMappingService.addActors(actors);\n        final long actorId = actors.iterator().next().getId();\n\n        List<SActorMember> actorMembers = actorMappingService.getActorMembers(actorId, 0, 10);\n        Assert.assertEquals(0, actorMembers.size());\n\n        actorMappingService.addGroupToActor(actorId, mainGroup.getId());\n        getTransactionService().complete();\n\n        getTransactionService().begin();\n        try {\n            actorMembers = actorMappingService.getActorMembers(actorId, 0, 10);\n            Assert.assertEquals(1, actorMembers.size());\n            checkActorMember(actorMembers.get(0), -1, mainGroup.getId(), -1);\n\n            actorMappingService.deleteActorMember(actorMembers.get(0).getId());\n            actorMembers = actorMappingService.getActorMembers(actorId, 0, 10);\n            Assert.assertEquals(0, actorMembers.size());\n            actorMappingService.deleteActors(12);\n        } finally {\n            getTransactionService().complete();\n        }\n    }\n\n    @Test\n    public void addAndRemoveAGroupWithSubGroupsOfAnActor() throws Exception {\n        Set<SActor> actors = new HashSet<>();\n        final SActor actor = SActor.builder().name(\"Manager\").scopeId(12).initiator(false).build();\n        actors.add(actor);\n\n        getTransactionService().begin();\n        actors = actorMappingService.addActors(actors);\n        final long actorId = actors.iterator().next().getId();\n\n        List<SActorMember> actorMembers = actorMappingService.getActorMembers(actorId, 0, 10);\n        Assert.assertEquals(0, actorMembers.size());\n\n        actorMappingService.addGroupToActor(actorId, parentGroup.getId());\n        getTransactionService().complete();\n\n        getTransactionService().begin();\n        try {\n            actorMembers = actorMappingService.getActorMembers(actorId, 0, 10);\n            Assert.assertEquals(2, actorMembers.size());\n            checkActorMember(actorMembers.get(0), -1, parentGroup.getId(), -1);\n            checkActorMember(actorMembers.get(1), -1, childGroup.getId(), -1);\n\n            actorMappingService.deleteActorMember(actorMembers.get(0).getId());\n            actorMappingService.deleteActorMember(actorMembers.get(1).getId());\n            actorMembers = actorMappingService.getActorMembers(actorId, 0, 10);\n            Assert.assertEquals(0, actorMembers.size());\n            actorMappingService.deleteActors(12);\n        } finally {\n            getTransactionService().complete();\n        }\n    }\n\n    @Test\n    public void addAndRemoveAMembershipOfAnActor() throws Exception {\n        Set<SActor> actors = new HashSet<>();\n        final SActor actor = SActor.builder().name(\"Manager\").scopeId(12).initiator(false).build();\n        actors.add(actor);\n\n        getTransactionService().begin();\n        actors = actorMappingService.addActors(actors);\n        final long actorId = actors.iterator().next().getId();\n\n        List<SActorMember> actorMembers = actorMappingService.getActorMembers(actorId, 0, 10);\n        Assert.assertEquals(0, actorMembers.size());\n\n        actorMappingService.addRoleAndGroupToActor(actorId, 1, mainGroup.getId());\n        getTransactionService().complete();\n\n        getTransactionService().begin();\n        try {\n            actorMembers = actorMappingService.getActorMembers(actorId, 0, 10);\n            Assert.assertEquals(1, actorMembers.size());\n            checkActorMember(actorMembers.get(0), -1, mainGroup.getId(), 1);\n\n            actorMappingService.deleteActorMember(actorMembers.get(0).getId());\n            actorMembers = actorMappingService.getActorMembers(actorId, 0, 10);\n            Assert.assertEquals(0, actorMembers.size());\n            actorMappingService.deleteActors(12);\n        } finally {\n            getTransactionService().complete();\n        }\n    }\n\n    @Test\n    public void addAndRemoveAMembershipOfAnActorWithSubGroups() throws Exception {\n        Set<SActor> actors = new HashSet<>();\n        final SActor actor = SActor.builder().name(\"Manager\").scopeId(12).initiator(false).build();\n        actors.add(actor);\n\n        getTransactionService().begin();\n        actors = actorMappingService.addActors(actors);\n        final long actorId = actors.iterator().next().getId();\n\n        List<SActorMember> actorMembers = actorMappingService.getActorMembers(actorId, 0, 10);\n        Assert.assertEquals(0, actorMembers.size());\n\n        actorMappingService.addRoleAndGroupToActor(actorId, 1, parentGroup.getId());\n        getTransactionService().complete();\n\n        getTransactionService().begin();\n        try {\n            actorMembers = actorMappingService.getActorMembers(actorId, 0, 10);\n            Assert.assertEquals(2, actorMembers.size());\n            checkActorMember(actorMembers.get(0), -1, parentGroup.getId(), 1);\n            checkActorMember(actorMembers.get(1), -1, childGroup.getId(), 1);\n\n            actorMappingService.deleteActorMember(actorMembers.get(0).getId());\n            actorMappingService.deleteActorMember(actorMembers.get(1).getId());\n            actorMembers = actorMappingService.getActorMembers(actorId, 0, 10);\n            Assert.assertEquals(0, actorMembers.size());\n            actorMappingService.deleteActors(12);\n        } finally {\n            getTransactionService().complete();\n        }\n    }\n\n    @Test\n    public void countActorMembers() throws Exception {\n        final long scopeId = 12;\n        SActor actor = SActor.builder().name(\"Manager\").scopeId(scopeId).initiator(false).build();\n\n        getTransactionService().begin();\n        actor = actorMappingService.addActor(actor);\n\n        final List<SActorMember> actorMembers = actorMappingService.getActorMembers(actor.getId(), 0, 10);\n        Assert.assertEquals(0, actorMembers.size());\n\n        actorMappingService.addRoleToActor(actor.getId(), 41);\n        actorMappingService.addUserToActor(actor.getId(), 7);\n        actorMappingService.addGroupToActor(actor.getId(), mainGroup.getId());\n        actorMappingService.addGroupToActor(actor.getId(), childGroup.getId());\n        actorMappingService.addUserToActor(actor.getId(), 83);\n\n        final long numberOfActorMembers = actorMappingService.getNumberOfActorMembers(actor.getId());\n        assertEquals(5L, numberOfActorMembers);\n        actorMappingService.deleteActors(12);\n\n        getTransactionService().complete();\n    }\n\n    @Test\n    public void getNumberOfRolesOfActorShouldNotCountMemberships() throws Exception {\n        final long scopeId = 12;\n        getTransactionService().begin();\n        final IdentityAPIImpl identityAPI = new IdentityAPIImpl();\n        final Role role1 = identityAPI.createRole(\"roletest\");\n        final Role role2 = identityAPI.createRole(\"role2test\");\n        getTransactionService().complete();\n\n        SActor actor = SActor.builder().name(\"ActorRoleTest\").scopeId(scopeId).initiator(false).build();\n        getTransactionService().begin();\n        actor = actorMappingService.addActor(actor);\n\n        final List<SActorMember> actorMembers = actorMappingService.getActorMembers(actor.getId(), 0, 10);\n        Assert.assertEquals(0, actorMembers.size());\n\n        actorMappingService.addRoleAndGroupToActor(actor.getId(), role1.getId(), mainGroup.getId());\n        actorMappingService.addRoleToActor(actor.getId(), role2.getId());\n\n        final long numberOfActorMembers = actorMappingService.getNumberOfRolesOfActor(actor.getId());\n        getTransactionService().complete();\n\n        assertEquals(1L, numberOfActorMembers);\n\n        // clean-up:\n        getTransactionService().begin();\n        actorMappingService.deleteActors(12);\n        identityAPI.deleteRole(role1.getId());\n        identityAPI.deleteRole(role2.getId());\n        getTransactionService().complete();\n    }\n\n    @Test\n    public void getNumberOfGroupsOfActorShouldNotCountMemberships() throws Exception {\n        final long scopeId = 12;\n        SActor actor = SActor.builder().name(\"ActorGroupTest\").scopeId(scopeId).initiator(false).build();\n\n        getTransactionService().begin();\n        actor = actorMappingService.addActor(actor);\n\n        final List<SActorMember> actorMembers = actorMappingService.getActorMembers(actor.getId(), 0, 10);\n        Assert.assertEquals(0, actorMembers.size());\n\n        actorMappingService.addRoleAndGroupToActor(actor.getId(), 21, mainGroup.getId());\n        actorMappingService.addGroupToActor(actor.getId(), mainGroup.getId());\n\n        final long numberOfActorMembers = actorMappingService.getNumberOfGroupsOfActor(actor.getId());\n        assertEquals(1L, numberOfActorMembers);\n\n        actorMappingService.deleteActors(12);\n        getTransactionService().complete();\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/CategoryServiceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm;\n\nimport static org.junit.Assert.*;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.core.category.CategoryService;\nimport org.bonitasoft.engine.core.category.exception.SCategoryAlreadyExistsException;\nimport org.bonitasoft.engine.core.category.exception.SCategoryCreationException;\nimport org.bonitasoft.engine.core.category.exception.SCategoryNotFoundException;\nimport org.bonitasoft.engine.core.category.model.SCategory;\nimport org.bonitasoft.engine.core.category.model.builder.SCategoryUpdateBuilder;\nimport org.bonitasoft.engine.core.category.model.builder.SCategoryUpdateBuilderFactory;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.transaction.TransactionService;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Yanyan Liu\n */\npublic class CategoryServiceIT extends CommonBPMServicesTest {\n\n    private static CategoryService categoryService;\n\n    private static TransactionService transactionService;\n\n    @Before\n    public void setup() {\n        categoryService = getServiceAccessor().getCategoryService();\n        transactionService = getTransactionService();\n    }\n\n    @Test\n    public void createCategory() throws Exception {\n        final String name = \"categoryName\";\n        final String description = \"test create category\";\n        transactionService.begin();\n        final SCategory category = categoryService.createCategory(name, description);\n        assertNotNull(category);\n        assertEquals(name, category.getName());\n        assertEquals(description, category.getDescription());\n        categoryService.deleteCategory(category.getId());\n        transactionService.complete();\n    }\n\n    @Test(expected = SCategoryAlreadyExistsException.class)\n    public void createCategoryWithSCategoryAlreadyExistsException() throws Exception {\n        final String name = \"categoryTestExceptionName\";\n        final String description = \"test create category with SCategoryAlreadyExistsException\";\n        transactionService.begin();\n        final SCategory category = categoryService.createCategory(name, description);\n        assertNotNull(category);\n        assertEquals(name, category.getName());\n        try {\n            categoryService.createCategory(name, description);\n        } finally {\n            categoryService.deleteCategory(category.getId());\n            transactionService.complete();\n        }\n    }\n\n    @Test(expected = SCategoryCreationException.class)\n    public void createCategoryWithSCategoryCreationException() throws Exception {\n        final String name = null;\n        final String description = \"test create category with SCategoryCreationException\";\n        transactionService.begin();\n        try {\n            categoryService.createCategory(name, description);\n        } finally {\n            transactionService.complete();\n        }\n    }\n\n    @Test\n    public void getCategory() throws Exception {\n        final String name = \"categoryName\";\n        final String description = \"test retrieve category\";\n        transactionService.begin();\n        // create\n        final SCategory category = categoryService.createCategory(name, description);\n        assertNotNull(category);\n        // retrieve test\n        final SCategory retrievedCategory = categoryService.getCategory(category.getId());\n        assertNotNull(retrievedCategory);\n        assertEquals(name, retrievedCategory.getName());\n        assertEquals(description, retrievedCategory.getDescription());\n        // delete\n        categoryService.deleteCategory(category.getId());\n        transactionService.complete();\n    }\n\n    @Test\n    public void getCategoryByName() throws Exception {\n        final String name = \"categoryName\";\n        final String description = \"test get category by name\";\n        transactionService.begin();\n        // create\n        final SCategory category1 = categoryService.createCategory(name, description);\n        assertNotNull(category1);\n        // retrieve test\n        final SCategory category2 = categoryService.getCategoryByName(category1.getName());\n        assertNotNull(category2);\n        assertEquals(category1.getId(), category2.getId());\n        assertEquals(name, category2.getName());\n        assertEquals(description, category2.getDescription());\n        // delete\n        categoryService.deleteCategory(category2.getId());\n        transactionService.complete();\n    }\n\n    @Test\n    public void updateCategory() throws Exception {\n        final String name = \"categoryName\";\n        final String description = \"test update category\";\n        transactionService.begin();\n        // create\n        final SCategory category1 = categoryService.createCategory(name, description);\n        assertNotNull(category1);\n        final long categoryId = category1.getId();\n        // update\n        final String newName = \"updatedName\";\n        final String newDescription = \"updatedDescription\";\n\n        final SCategoryUpdateBuilder updateBuilder = BuilderFactory.get(SCategoryUpdateBuilderFactory.class)\n                .createNewInstance();\n        updateBuilder.updateName(newName).updateDescription(newDescription);\n        categoryService.updateCategory(categoryId, updateBuilder.done());\n        transactionService.complete();\n\n        transactionService.begin();\n        // test\n        final SCategory updatedCategory = categoryService.getCategory(categoryId);\n        assertNotNull(updatedCategory);\n        assertEquals(newName, updatedCategory.getName());\n        assertEquals(newDescription, updatedCategory.getDescription());\n        // delete\n        categoryService.deleteCategory(categoryId);\n        transactionService.complete();\n    }\n\n    @Test(expected = SCategoryNotFoundException.class)\n    public void updateCategoryWithSCategoryNotFoundException() throws Exception {\n        final long categoryId = 1;\n        final String newName = \"updatedName\";\n        final String newDescription = \"updatedDescription\";\n        final SCategoryUpdateBuilder updateBuilder = BuilderFactory.get(SCategoryUpdateBuilderFactory.class)\n                .createNewInstance();\n        updateBuilder.updateName(newName).updateDescription(newDescription);\n\n        transactionService.begin();\n        categoryService.updateCategory(categoryId, updateBuilder.done());\n        transactionService.complete();\n    }\n\n    @Test\n    public void deleteCategory() throws Exception {\n        final String name = \"categoryName_delete\";\n        final String description = \"test delete category\";\n        transactionService.begin();\n        // create\n        final SCategory category = categoryService.createCategory(name, description);\n        assertNotNull(category);\n        assertEquals(name, category.getName());\n        // delete test\n        categoryService.deleteCategory(category.getId());\n        transactionService.complete();\n\n        transactionService.begin();\n        try {\n            categoryService.getCategory(category.getId());\n            fail();\n        } catch (final SCategoryNotFoundException ignored) {\n        }\n        transactionService.complete();\n    }\n\n    @Test\n    public void getNumberOfCategories() throws Exception {\n        transactionService.begin();\n        long count = categoryService.getNumberOfCategories();\n        assertEquals(0, count);\n        // create\n        final String name = \"categoryName\";\n        final String description = \"category description\";\n        final List<SCategory> categoryList = createCategories(3, name, description);\n        assertNotNull(categoryList);\n        assertEquals(3, categoryList.size());\n        count = categoryService.getNumberOfCategories();\n        assertEquals(3, count);\n        // delete test\n        for (final SCategory category : categoryList) {\n            categoryService.deleteCategory(category.getId());\n        }\n        transactionService.complete();\n    }\n\n    @Test\n    // this should be client test\n    public void getCategoriesInOrder() throws Exception {\n        final String name = \"categoryName\";\n        final String description = \"test get categories in order\";\n        transactionService.begin();\n        // create\n        final List<SCategory> categoryList = createCategories(5, name, description);\n        assertNotNull(categoryList);\n        assertEquals(5, categoryList.size());\n        // test name ASC\n        final List<SCategory> categoryList1 = categoryService.getCategories(0, 2, \"name\", OrderByType.ASC);\n        assertNotNull(categoryList1);\n        assertEquals(2, categoryList1.size());\n        assertEquals(\"categoryName1\", categoryList1.get(0).getName());\n        assertEquals(\"categoryName2\", categoryList1.get(1).getName());\n\n        final List<SCategory> categoryList2 = categoryService.getCategories(1, 2, \"name\", OrderByType.ASC);\n        assertNotNull(categoryList2);\n        assertEquals(2, categoryList2.size());\n        assertEquals(\"categoryName2\", categoryList2.get(0).getName());\n        assertEquals(\"categoryName3\", categoryList2.get(1).getName());\n\n        final List<SCategory> categoryList3 = categoryService.getCategories(3, 2, \"name\", OrderByType.ASC);\n        assertNotNull(categoryList3);\n        assertEquals(2, categoryList3.size());\n        assertEquals(\"categoryName4\", categoryList3.get(0).getName());\n        assertEquals(\"categoryName5\", categoryList3.get(1).getName());\n\n        final List<SCategory> categoryList4 = categoryService.getCategories(5, 2, \"name\", OrderByType.ASC);\n        assertEquals(0, categoryList4.size());\n\n        // test name DESC\n        final List<SCategory> categoryList5 = categoryService.getCategories(0, 2, \"name\", OrderByType.DESC);\n        assertNotNull(categoryList5);\n        assertEquals(2, categoryList5.size());\n        assertEquals(\"categoryName5\", categoryList5.get(0).getName());\n        assertEquals(\"categoryName4\", categoryList5.get(1).getName());\n\n        // delete test\n        for (final SCategory category : categoryList) {\n            categoryService.deleteCategory(category.getId());\n        }\n\n        transactionService.complete();\n    }\n\n    @Test(expected = SCategoryNotFoundException.class)\n    public void addProcessDefinitionToCategoryWithSCategoryNotFoundException() throws Exception {\n        // generate the meaningful of ProcessDefinition id\n        final SProcessDefinition processDefinition = createSProcessDefinition(\"processName\",\n                \"test category not found exceptioin\");\n\n        transactionService.begin();\n        try {\n            categoryService.addProcessDefinitionToCategory(1, processDefinition.getId());\n        } finally {\n            transactionService.complete();\n\n            // Clean-up\n            deleteSProcessDefinition(processDefinition);\n        }\n    }\n\n    @Test\n    public void getNumberOfCategories4Process() throws Exception {\n        // generate a meaningful processDefinitionId\n        final long processDefinitionId = createSProcessDefinition(\"processName\",\n                \"test get number of categories of process\").getId();\n\n        transactionService.begin();\n        long count = categoryService.getNumberOfCategoriesOfProcess(processDefinitionId);\n        assertEquals(0, count);\n\n        final String name = \"categoryName\";\n        final String description = \"category description\";\n        // create category\n        final List<SCategory> categoryList = createCategories(2, name, description);\n        assertNotNull(categoryList);\n        assertEquals(2, categoryList.size());\n        // add process definition info to category\n        for (final SCategory category : categoryList) {\n            categoryService.addProcessDefinitionToCategory(category.getId(), processDefinitionId);\n        }\n        // check\n        count = categoryService.getNumberOfCategoriesOfProcess(processDefinitionId);\n        assertEquals(2, count);\n        // delete category and process definition\n        for (final SCategory category : categoryList) {\n            categoryService.deleteCategory(category.getId());\n        }\n        transactionService.complete();\n\n        // Clean-up\n        deleteSProcessDefinition(processDefinitionId);\n    }\n\n    @Test\n    public void getNumberOfCategoriesUnrelatedToProcess() throws Exception {\n        final List<SProcessDefinition> processDefinitions = createSProcessDefinitions(2, \"processName\",\n                \"test get number of categories of process\");\n\n        transactionService.begin();\n        // generate a meaningful processDefinitionId\n        final long processDefinitionId = processDefinitions.get(0).getId();\n        long count = categoryService.getNumberOfCategoriesUnrelatedToProcess(processDefinitionId);\n        assertEquals(0, count);\n\n        final String name = \"categoryName\";\n        final String description = \"category description\";\n        // create category\n        final List<SCategory> categoryList = createCategories(4, name, description);\n        assertNotNull(categoryList);\n        assertEquals(4, categoryList.size());\n        // add process definition info to category\n        categoryService.addProcessDefinitionToCategory(categoryList.get(0).getId(), processDefinitionId);\n        categoryService.addProcessDefinitionToCategory(categoryList.get(1).getId(), processDefinitionId);\n\n        // check\n        count = categoryService.getNumberOfCategoriesUnrelatedToProcess(processDefinitionId);\n        assertEquals(2, count);\n\n        count = categoryService.getNumberOfCategoriesUnrelatedToProcess(processDefinitions.get(1).getId());\n        assertEquals(4, count);\n\n        // delete category and process definition\n        for (final SCategory category : categoryList) {\n            categoryService.deleteCategory(category.getId());\n        }\n        transactionService.complete();\n\n        // Clean-up\n        deleteSProcessDefinitions(processDefinitions);\n    }\n\n    @Test\n    public void getCategoriesUnrelatedToProcess() throws Exception {\n        final List<SProcessDefinition> processDefinitions = createSProcessDefinitions(2, \"processName\",\n                \"test get number of categories of process\");\n\n        transactionService.begin();\n        // generate a meaningful processDefinitionId\n        final long processDefinitionId = processDefinitions.get(0).getId();\n        List<SCategory> categories = categoryService.getCategoriesUnrelatedToProcessDefinition(processDefinitionId, 0,\n                4, OrderByType.ASC);\n        assertEquals(0, categories.size());\n\n        final String name = \"categoryName\";\n        final String description = \"category description\";\n        // create category\n        final List<SCategory> categoryList = createCategories(4, name, description);\n        assertNotNull(categoryList);\n        assertEquals(4, categoryList.size());\n        // add process definition info to category\n        categoryService.addProcessDefinitionToCategory(categoryList.get(0).getId(), processDefinitionId);\n        categoryService.addProcessDefinitionToCategory(categoryList.get(1).getId(), processDefinitionId);\n\n        // check\n        categories = categoryService.getCategoriesUnrelatedToProcessDefinition(processDefinitionId, 0, 4,\n                OrderByType.ASC);\n        assertEquals(2, categories.size());\n        assertEquals(categoryList.get(2).getId(), categories.get(0).getId());\n        assertEquals(categoryList.get(3).getId(), categories.get(1).getId());\n\n        categories = categoryService.getCategoriesUnrelatedToProcessDefinition(processDefinitions.get(1).getId(), 0, 4,\n                OrderByType.ASC);\n        assertEquals(4, categories.size());\n        assertEquals(categoryList.get(0).getId(), categories.get(0).getId());\n        assertEquals(categoryList.get(1).getId(), categories.get(1).getId());\n        assertEquals(categoryList.get(2).getId(), categories.get(2).getId());\n        assertEquals(categoryList.get(3).getId(), categories.get(3).getId());\n\n        // delete category and process definition\n        for (final SCategory category : categoryList) {\n            categoryService.deleteCategory(category.getId());\n        }\n        transactionService.complete();\n\n        // Clean-up\n        deleteSProcessDefinitions(processDefinitions);\n    }\n\n    @Test\n    public void getCategoriesOfProcessDefinition() throws Exception {\n        final SProcessDefinition processDefinition = createSProcessDefinitions(1, \"processName\",\n                \"test get categores of process definition\").get(0);\n\n        transactionService.begin();\n        // generate the meaningful processDefinition id and delete it in the end\n        final long processDefinitionId = processDefinition.getId();\n        List<SCategory> categoryList = categoryService.getCategoriesOfProcessDefinition(processDefinitionId, 0, 2,\n                OrderByType.ASC);\n        assertEquals(0, categoryList.size());\n        final String name = \"categoryName\";\n        final String description = \"test get categories of process definition\";\n        // create categories, add process definition to categories\n        categoryList = createCategories(5, name, description);\n        transactionService.complete();\n\n        transactionService.begin();\n        for (final SCategory category : categoryList) {\n            categoryService.addProcessDefinitionToCategory(category.getId(), processDefinitionId);\n        }\n        transactionService.complete();\n\n        transactionService.begin();\n        // test\n        final List<SCategory> categoryList1 = categoryService.getCategoriesOfProcessDefinition(processDefinitionId, 0,\n                2, OrderByType.ASC);\n        assertNotNull(categoryList1);\n        assertEquals(2, categoryList1.size());\n        assertEquals(\"categoryName1\", categoryList1.get(0).getName());\n        assertEquals(\"categoryName2\", categoryList1.get(1).getName());\n\n        final List<SCategory> categoryList2 = categoryService.getCategoriesOfProcessDefinition(processDefinitionId, 1,\n                2, OrderByType.ASC);\n        assertNotNull(categoryList2);\n        assertEquals(2, categoryList2.size());\n        assertEquals(\"categoryName2\", categoryList2.get(0).getName());\n        assertEquals(\"categoryName3\", categoryList2.get(1).getName());\n\n        final List<SCategory> categoryList3 = categoryService.getCategoriesOfProcessDefinition(processDefinitionId, 2,\n                2, OrderByType.ASC);\n        assertNotNull(categoryList3);\n        assertEquals(2, categoryList3.size());\n        assertEquals(\"categoryName3\", categoryList3.get(0).getName());\n        assertEquals(\"categoryName4\", categoryList3.get(1).getName());\n\n        final List<SCategory> categoryList4 = categoryService.getCategoriesOfProcessDefinition(processDefinitionId, 4,\n                2, OrderByType.ASC);\n        assertNotNull(categoryList4);\n        assertEquals(1, categoryList4.size());\n        assertEquals(\"categoryName5\", categoryList4.get(0).getName());\n\n        final List<SCategory> categories = categoryService.getCategoriesOfProcessDefinition(processDefinitionId, 5, 2,\n                OrderByType.ASC);\n        assertTrue(categories.isEmpty());\n        final List<SCategory> categoryList5 = categoryService.getCategoriesOfProcessDefinition(processDefinitionId, 0,\n                3, OrderByType.DESC);\n        assertNotNull(categoryList5);\n        assertEquals(3, categoryList5.size());\n        assertEquals(\"categoryName5\", categoryList5.get(0).getName());\n        assertEquals(\"categoryName4\", categoryList5.get(1).getName());\n        assertEquals(\"categoryName3\", categoryList5.get(2).getName());\n\n        // delete categories and process\n        for (final SCategory category : categoryList) {\n            categoryService.deleteCategory(category.getId());\n        }\n        transactionService.complete();\n\n        // Clean-up\n        deleteSProcessDefinition(processDefinitionId);\n    }\n\n    private List<SCategory> createCategories(final int count, final String name, final String description)\n            throws Exception {\n        final List<SCategory> categoryList = new ArrayList<SCategory>();\n        for (int i = 1; i <= count; i++) {\n            final SCategory category = categoryService.createCategory(name + i, description + i);\n            categoryList.add(category);\n        }\n        return categoryList;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/DocumentServiceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.junit.Assert.assertEquals;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.core.document.api.DocumentService;\nimport org.bonitasoft.engine.core.document.model.SDocument;\nimport org.bonitasoft.engine.core.document.model.SMappedDocument;\nimport org.bonitasoft.engine.core.document.model.builder.SDocumentBuilder;\nimport org.bonitasoft.engine.core.document.model.builder.SDocumentBuilderFactory;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.transaction.TransactionService;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Nicolas Chabanoles\n * @author Celine Souchet\n */\npublic class DocumentServiceIT extends CommonBPMServicesTest {\n\n    private static final int NUMBER_OF_DOCUMENT = 10;\n\n    private static DocumentService documentService;\n\n    private static TransactionService transactionService;\n\n    private static final long processInstanceId = 123L;\n\n    private static String documentNameKey;\n\n    @Before\n    public void before() {\n        documentService = getServiceAccessor().getDocumentService();\n        transactionService = getTransactionService();\n        documentNameKey = SDocument.NAME;\n    }\n\n    @Test\n    public void attachDocumentToProcessInstanceWithContentTest() throws SBonitaException {\n        transactionService.begin();\n        final SDocument document = buildProcessDocumentWithContent(1, \"theContent\".getBytes());\n        final SMappedDocument result = documentService.attachDocumentToProcessInstance(document, processInstanceId,\n                \"documentName\", \"the description\");\n        transactionService.complete();\n\n        assertEquals(document.getAuthor(), result.getAuthor());\n        assertEquals(document.getFileName(), result.getFileName());\n        assertEquals(document.getMimeType(), result.getMimeType());\n        assertEquals(document.getCreationDate(), result.getCreationDate());\n        assertEquals(document.getUrl(), result.getUrl());\n        assertEquals(document.getId(), result.getDocumentId());\n        assertEquals(\"documentName\", result.getName());\n        assertEquals(\"the description\", result.getDescription());\n        assertEquals(processInstanceId, result.getProcessInstanceId());\n\n        // Clean up\n        delete(result);\n    }\n\n    @Test\n    public void attachDocumentToProcessInstanceWithUrlTest() throws SBonitaException {\n        transactionService.begin();\n        final SDocument document = buildProcessDocumentWithUrl(1);\n        final SMappedDocument result = documentService.attachDocumentToProcessInstance(document, processInstanceId,\n                \"documentName\", \"the description\");\n        transactionService.complete();\n\n        assertEquals(document.getAuthor(), result.getAuthor());\n        assertEquals(document.getFileName(), result.getFileName());\n        assertEquals(document.getMimeType(), result.getMimeType());\n        assertEquals(document.getCreationDate(), result.getCreationDate());\n        assertEquals(\"documentName\", result.getName());\n        assertEquals(processInstanceId, result.getProcessInstanceId());\n        assertEquals(document.getUrl(), result.getUrl());\n        assertEquals(-1, result.getIndex());\n\n        // Clean up\n        delete(result);\n    }\n\n    @Test\n    public void attachDocumentToProcessInList() throws SBonitaException {\n        transactionService.begin();\n        final SDocument document0 = buildProcessDocumentWithUrl(1);\n        final SDocument document1 = buildProcessDocumentWithUrl(1);\n        final SMappedDocument result0 = documentService.attachDocumentToProcessInstance(document0, processInstanceId,\n                \"documentName\", \"the description\", 0);\n        final SMappedDocument result1 = documentService.attachDocumentToProcessInstance(document1, processInstanceId,\n                \"documentName\", \"the description\", 1);\n        transactionService.complete();\n\n        assertEquals(document0.getAuthor(), result0.getAuthor());\n        assertEquals(document0.getFileName(), result0.getFileName());\n        assertEquals(document0.getMimeType(), result0.getMimeType());\n        assertEquals(document0.getCreationDate(), result0.getCreationDate());\n        assertEquals(\"documentName\", result0.getName());\n        assertEquals(processInstanceId, result0.getProcessInstanceId());\n        assertEquals(document0.getUrl(), result0.getUrl());\n        assertEquals(0, result0.getIndex());\n        assertEquals(1, result1.getIndex());\n\n        // Clean up\n        delete(result0);\n        delete(result1);\n    }\n\n    @Test\n    public void getDocumentByIdTest() throws SBonitaException {\n        final SMappedDocument sDocument = createDocumentMapping();\n        final SMappedDocument result = getDocumentMapping(sDocument);\n        assertEquals(sDocument.getId(), result.getId());\n\n        // Clean up\n        delete(result);\n    }\n\n    @Test\n    public void getDocumentByNameAndProcessIdTest() throws SBonitaException {\n        final SMappedDocument sDocument = createDocumentMapping();\n\n        transactionService.begin();\n        final SMappedDocument result = documentService.getMappedDocument(sDocument.getProcessInstanceId(),\n                sDocument.getName());\n        transactionService.complete();\n\n        assertEquals(sDocument.getId(), result.getId());\n        assertEquals(sDocument.getName(), result.getName());\n        assertEquals(sDocument.getProcessInstanceId(), result.getProcessInstanceId());\n\n        // Clean up\n        delete(result);\n    }\n\n    @Test\n    public void getNumberOfDocumentMappingsForProcessInstanceTest() throws SBonitaException {\n        final List<SMappedDocument> list = createDocumentMappings();\n\n        transactionService.begin();\n        final long result = documentService.getNumberOfDocumentsOfProcessInstance(processInstanceId);\n        transactionService.complete();\n\n        assertEquals(NUMBER_OF_DOCUMENT, result);\n\n        // Clean up\n        delete(list);\n    }\n\n    @Test\n    public void getDocumentMappingsForProcessInstanceTest() throws SBonitaException {\n        final List<SMappedDocument> list = createDocumentMappings();\n\n        transactionService.begin();\n        final List<SMappedDocument> result = documentService.getDocumentsOfProcessInstance(\n                list.get(0).getProcessInstanceId(), 0,\n                NUMBER_OF_DOCUMENT + 1, \"name\", OrderByType.ASC);\n        transactionService.complete();\n\n        assertEquals(NUMBER_OF_DOCUMENT, result.size());\n        assertEquals(list.get(0).getId(), result.get(0).getId());\n        assertEquals(list.get(0).getName(), result.get(0).getName());\n        assertEquals(list.get(1).getId(), result.get(1).getId());\n        assertEquals(list.get(1).getName(), result.get(1).getName());\n        assertEquals(list.get(2).getId(), result.get(2).getId());\n        assertEquals(list.get(2).getName(), result.get(2).getName());\n        assertEquals(list.get(3).getId(), result.get(3).getId());\n        assertEquals(list.get(3).getName(), result.get(3).getName());\n        assertEquals(list.get(4).getId(), result.get(4).getId());\n        assertEquals(list.get(4).getName(), result.get(4).getName());\n        assertEquals(list.get(5).getId(), result.get(5).getId());\n        assertEquals(list.get(5).getName(), result.get(5).getName());\n        assertEquals(list.get(6).getId(), result.get(6).getId());\n        assertEquals(list.get(6).getName(), result.get(6).getName());\n        assertEquals(list.get(7).getId(), result.get(7).getId());\n        assertEquals(list.get(7).getName(), result.get(7).getName());\n        assertEquals(list.get(8).getId(), result.get(8).getId());\n        assertEquals(list.get(8).getName(), result.get(8).getName());\n        assertEquals(list.get(9).getId(), result.get(9).getId());\n        assertEquals(list.get(9).getName(), result.get(9).getName());\n\n        // Clean up\n        delete(list);\n    }\n\n    private SDocument buildProcessDocumentWithContent(final int i, final byte[] documentContent) {\n        final SDocumentBuilder builder = new SDocumentBuilderFactory().createNewInstance(\"getContentTest.txt\",\n                \"text/plain\", i);\n        builder.setHasContent(true);\n        builder.setContent(documentContent);\n        return builder.done();\n    }\n\n    private SDocument buildProcessDocumentWithUrl(final int i) {\n        final SDocumentBuilder builder = new SDocumentBuilderFactory().createNewInstance(\"getContentTest.txt\",\n                \"text/plain\", i);\n        builder.setHasContent(false);\n        builder.setURL(\"theUrl\");\n        return builder.done();\n    }\n\n    private SMappedDocument createDocumentMapping() throws SBonitaException {\n        transactionService.begin();\n        final SDocument document = buildProcessDocumentWithContent(1, \"content\".getBytes());\n        final SMappedDocument doc = documentService.attachDocumentToProcessInstance(document, processInstanceId,\n                \"documentName\", \"the description\");\n        transactionService.complete();\n        return doc;\n    }\n\n    private List<SMappedDocument> createDocumentMappings() throws SBonitaException {\n        transactionService.begin();\n        final List<SMappedDocument> list = new ArrayList<>(10);\n        for (int i = 0; i < NUMBER_OF_DOCUMENT; i++) {\n            final SDocument document = buildProcessDocumentWithContent(i, \"content\".getBytes());\n            list.add(documentService.attachDocumentToProcessInstance(document, processInstanceId, \"documentName\",\n                    \"the description\"));\n        }\n        transactionService.complete();\n        return list;\n    }\n\n    private SMappedDocument getDocumentMapping(final SMappedDocument sDocument) throws SBonitaException {\n        transactionService.begin();\n        final SMappedDocument result = documentService.getMappedDocument(sDocument.getId());\n        transactionService.complete();\n        return result;\n    }\n\n    private void delete(final List<SMappedDocument> sDocuments) throws SBonitaException {\n        transactionService.begin();\n        for (final SMappedDocument sDocument : sDocuments) {\n            documentService.deleteMappedDocument(sDocument);\n            documentService.deleteDocument(sDocument.getDocumentId());\n        }\n        transactionService.complete();\n    }\n\n    private void delete(final SMappedDocument mappedDocument) throws SBonitaException {\n        transactionService.begin();\n        documentService.deleteMappedDocument(mappedDocument);\n        documentService.deleteDocument(mappedDocument.getDocumentId());\n        transactionService.complete();\n    }\n\n    @Test\n    public void getDocumentContentTest() throws SBonitaException {\n        final byte[] documentContent = \"this is the content of the document\".getBytes();\n\n        final SMappedDocument sProcessDocument = createAndAttachDocumentToProcessInstanceWithContent(documentContent);\n        final byte[] storedContents = getDocumentContent(sProcessDocument);\n        assertEquals(documentContent.length, storedContents.length);\n\n        // Clean up\n        delete(sProcessDocument);\n    }\n\n    @Test\n    public void getNumberOfDocumentsOfProcessInstanceTest() throws SBonitaException {\n        final List<SMappedDocument> list = createAndAttachDocumentToProcessInstances();\n\n        transactionService.begin();\n        final long result = documentService.getNumberOfDocumentsOfProcessInstance(list.get(0).getProcessInstanceId());\n        transactionService.complete();\n\n        assertEquals(NUMBER_OF_DOCUMENT, result);\n\n        // Clean up\n        delete(list);\n    }\n\n    @Test\n    public void getDocumentsOfProcessInstanceTest() throws SBonitaException {\n        final List<SMappedDocument> list = createAndAttachDocumentToProcessInstances();\n\n        transactionService.begin();\n        final List<SMappedDocument> result = documentService.getDocumentsOfProcessInstance(\n                list.get(0).getProcessInstanceId(), 0,\n                NUMBER_OF_DOCUMENT + 1, documentNameKey, OrderByType.ASC);\n        transactionService.complete();\n\n        assertEquals(NUMBER_OF_DOCUMENT, result.size());\n        assertEquals(list.get(0).getId(), result.get(0).getId());\n        assertEquals(list.get(0).getName(), result.get(0).getName());\n        assertEquals(list.get(1).getId(), result.get(1).getId());\n        assertEquals(list.get(1).getName(), result.get(1).getName());\n        assertEquals(list.get(2).getId(), result.get(2).getId());\n        assertEquals(list.get(2).getName(), result.get(2).getName());\n        assertEquals(list.get(3).getId(), result.get(3).getId());\n        assertEquals(list.get(3).getName(), result.get(3).getName());\n        assertEquals(list.get(4).getId(), result.get(4).getId());\n        assertEquals(list.get(4).getName(), result.get(4).getName());\n        assertEquals(list.get(5).getId(), result.get(5).getId());\n        assertEquals(list.get(5).getName(), result.get(5).getName());\n        assertEquals(list.get(6).getId(), result.get(6).getId());\n        assertEquals(list.get(6).getName(), result.get(6).getName());\n        assertEquals(list.get(7).getId(), result.get(7).getId());\n        assertEquals(list.get(7).getName(), result.get(7).getName());\n        assertEquals(list.get(8).getId(), result.get(8).getId());\n        assertEquals(list.get(8).getName(), result.get(8).getName());\n        assertEquals(list.get(9).getId(), result.get(9).getId());\n        assertEquals(list.get(9).getName(), result.get(9).getName());\n\n        // Clean up\n        delete(list);\n    }\n\n    @Test\n    public void removeDocumentsTest() throws Exception {\n        //given\n        final byte[] documentContent = \"this is the content of the document\".getBytes();\n        final List<SMappedDocument> list = createAndAttachDocumentToProcessInstancesWithContent(documentContent);\n\n        //when\n        delete(list);\n\n        //then\n        for (final SMappedDocument sProcessDocument : list) {\n            transactionService.begin();\n            assertThatExceptionOfType(SObjectNotFoundException.class)\n                    .isThrownBy(() -> documentService.getMappedDocument(sProcessDocument.getId()));\n            assertThatExceptionOfType(SObjectNotFoundException.class)\n                    .isThrownBy(() -> documentService.getDocument(sProcessDocument.getDocumentId()));\n            transactionService.complete();\n        }\n    }\n\n    private SMappedDocument createAndAttachDocumentToProcessInstanceWithContent(final byte[] documentContent)\n            throws SBonitaException {\n        transactionService.begin();\n        final SDocument document = buildProcessDocumentWithContent(1, documentContent);\n        final SMappedDocument doc = documentService.attachDocumentToProcessInstance(document, processInstanceId,\n                \"documentName\", \"the description\");\n        transactionService.complete();\n        return doc;\n    }\n\n    private List<SMappedDocument> createAndAttachDocumentToProcessInstancesWithContent(final byte[] documentContent)\n            throws SBonitaException {\n        transactionService.begin();\n        final List<SMappedDocument> list = new ArrayList<>(10);\n        for (int i = 0; i < NUMBER_OF_DOCUMENT; i++) {\n            final SDocument document = buildProcessDocumentWithContent(i, documentContent);\n            final SMappedDocument doc = documentService.attachDocumentToProcessInstance(document, processInstanceId,\n                    \"documentName\", \"the description\");\n            list.add(doc);\n        }\n        transactionService.complete();\n        return list;\n    }\n\n    private List<SMappedDocument> createAndAttachDocumentToProcessInstances() throws SBonitaException {\n        transactionService.begin();\n        final List<SMappedDocument> list = new ArrayList<>(10);\n        for (int i = 0; i < NUMBER_OF_DOCUMENT; i++) {\n            final SDocument document = buildProcessDocumentWithUrl(i);\n            final SMappedDocument doc = documentService.attachDocumentToProcessInstance(document, processInstanceId,\n                    \"documentName\", \"the description\");\n            list.add(doc);\n        }\n        transactionService.complete();\n        return list;\n    }\n\n    private byte[] getDocumentContent(final SMappedDocument sProcessDocument) throws SBonitaException {\n        transactionService.begin();\n        final byte[] storedContents = documentService\n                .getDocumentContent(String.valueOf(sProcessDocument.getDocumentId()));\n        transactionService.complete();\n        return storedContents;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/EventInstanceRepositoryIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm;\n\nimport static java.util.Collections.emptyList;\nimport static java.util.Collections.singletonList;\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.time.Instant;\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceRepository;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.transaction.TransactionService;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class EventInstanceRepositoryIT extends CommonBPMServicesTest {\n\n    private EventInstanceRepository eventInstanceRepository;\n    private TransactionService transactionService;\n    private final long oneMinuteAgo = Instant.now().minusSeconds(60).toEpochMilli();\n\n    @Before\n    public void before() throws Exception {\n        eventInstanceRepository = getServiceAccessor().getEventInstanceRepository();\n        transactionService = getTransactionService();\n\n        transactionService.executeInTransaction(() -> {\n            List<Long> messageInstanceIdOlderThanCreationDate = eventInstanceRepository\n                    .getMessageInstanceIdOlderThanCreationDate(System.currentTimeMillis(), new QueryOptions(0, 1000));\n            System.out.println(\"ids\" + messageInstanceIdOlderThanCreationDate);\n            eventInstanceRepository.deleteMessageInstanceByIds(messageInstanceIdOlderThanCreationDate);\n            return null;\n        });\n    }\n\n    @Test\n    public void should_create_message_with_a_creation_date() throws Exception {\n        SMessageInstance sMessageInstance = createMessageInstance(\"myMessage\");\n\n        SMessageInstance createdMessage = transactionService.executeInTransaction(() -> {\n            eventInstanceRepository.createMessageInstance(sMessageInstance);\n            return eventInstanceRepository.getMessageInstance(sMessageInstance.getId());\n        });\n\n        assertThat(createdMessage.getId()).isGreaterThan(0);\n        assertThat(createdMessage.getCreationDate()).isGreaterThan(0);\n\n    }\n\n    private SMessageInstance createMessageInstance(String myMessage) {\n        return new SMessageInstance(myMessage, \"targetProcess\", \"targetFlowNode\", 12345L, \"fnName\");\n    }\n\n    @Test\n    public void should_get_older_message_with_creationDate_and_no_filters() throws Exception {\n        SMessageInstance myMessageNow = createMessageInstance(\"myMessage\");\n        SMessageInstance myMessageOld2 = createMessageInstance(\"myMessage2\");\n        SMessageInstance myMessageOld = createMessageInstance(\"myMessage\");\n        myMessageOld2.setCreationDate(oneMinuteAgo);\n        myMessageOld.setCreationDate(oneMinuteAgo);\n\n        List<Long> ids = transactionService.executeInTransaction(() -> {\n            eventInstanceRepository.createMessageInstance(myMessageNow);\n            eventInstanceRepository.createMessageInstance(myMessageOld);\n            eventInstanceRepository.createMessageInstance(myMessageOld2);\n            return eventInstanceRepository.getMessageInstanceIdOlderThanCreationDate(oneMinuteAgo, null);\n\n        });\n        assertThat(ids).containsExactly(myMessageOld.getId(), myMessageOld2.getId());\n\n    }\n\n    @Test\n    public void should_get_older_message_with_creationDate_messageName_filter() throws Exception {\n        SMessageInstance myMessageNow = createMessageInstance(\"myMessage\");\n        SMessageInstance myMessageOld2 = createMessageInstance(\"myMessage2\");\n        SMessageInstance myMessageOld = createMessageInstance(\"myMessage\");\n        myMessageOld2.setCreationDate(oneMinuteAgo);\n        myMessageOld.setCreationDate(oneMinuteAgo);\n\n        List<Long> ids = transactionService.executeInTransaction(() -> {\n            QueryOptions queryOptions = new QueryOptions(0, 100, emptyList(),\n                    singletonList(new FilterOption(SMessageInstance.class, \"messageName\", \"myMessage\")), null);\n\n            eventInstanceRepository.createMessageInstance(myMessageNow);\n            eventInstanceRepository.createMessageInstance(myMessageOld);\n            eventInstanceRepository.createMessageInstance(myMessageOld2);\n            return eventInstanceRepository.getMessageInstanceIdOlderThanCreationDate(oneMinuteAgo, queryOptions);\n\n        });\n        assertThat(ids).containsExactly(myMessageOld.getId());\n\n    }\n\n    @Test\n    public void should_delete_a_message_with_given_id() throws Exception {\n\n        SMessageInstance sMessageInstance = createMessageInstance(\"myMessage\");\n        SMessageInstance sMessageInstance2 = createMessageInstance(\"myMessage2\");\n        transactionService.executeInTransaction(() -> {\n            eventInstanceRepository.createMessageInstance(sMessageInstance);\n            eventInstanceRepository.createMessageInstance(sMessageInstance2);\n            return null;\n\n        });\n\n        transactionService.executeInTransaction(() -> {\n            eventInstanceRepository.deleteMessageInstanceByIds(singletonList(sMessageInstance.getId()));\n            return null;\n        });\n\n        assertThat(getMessageInstance(sMessageInstance.getId())).isNull();\n        assertThat(getMessageInstance(sMessageInstance2.getId())).isNotNull();\n\n    }\n\n    private SMessageInstance getMessageInstance(long id) throws Exception {\n        return transactionService.executeInTransaction(() -> eventInstanceRepository.getMessageInstance(id));\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/GatewayExecutionLocalIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm;\n\nimport static org.junit.Assert.assertEquals;\n\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstance;\nimport org.bonitasoft.engine.bpm.flownode.GatewayType;\nimport org.bonitasoft.engine.bpm.process.ActivationState;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.junit.Test;\n\npublic class GatewayExecutionLocalIT extends TestWithUser {\n\n    @Test\n    public void exclusiveGatewayFailed() throws Exception {\n        final Expression scriptExpression = new ExpressionBuilder()\n                .createGroovyScriptExpression(\"mycondition\", \"fzdfsdfsdfsdfsdf\", Boolean.class.getName());\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process_with_exclusive_gateway\", PROCESS_VERSION)\n                .addActor(ACTOR_NAME).addAutomaticTask(\"step1\").addUserTask(\"step2\", ACTOR_NAME)\n                .addUserTask(\"step3\", ACTOR_NAME).addGateway(\"gateway1\", GatewayType.EXCLUSIVE)\n                .addTransition(\"step1\", \"gateway1\")\n                .addTransition(\"gateway1\", \"step2\", scriptExpression).addDefaultTransition(\"gateway1\", \"step3\")\n                .getProcess();\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME,\n                user);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n        final FlowNodeInstance failFlowNodeInstance = waitForFlowNodeInFailedState(processInstance);\n        assertEquals(\"gateway1\", failFlowNodeInstance.getName());\n        disableAndDeleteProcess(processDefinition);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/GatewayInstanceServiceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\n\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.model.SGatewayType;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeDeletionException;\nimport org.bonitasoft.engine.core.process.instance.model.SGatewayInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SGatewayInstanceBuilderFactory;\nimport org.junit.Test;\n\n/**\n * @author Feng Hui\n * @author Zhao Na\n */\npublic class GatewayInstanceServiceIT extends CommonBPMServicesTest {\n\n    protected void deleteGatewayInstance(final SGatewayInstance gatewayInstance) throws SBonitaException {\n        getTransactionService().begin();\n        try {\n            getServiceAccessor().getActivityInstanceService().deleteFlowNodeInstance(gatewayInstance);\n        } catch (final SBonitaException e) {\n            throw new SFlowNodeDeletionException(e);\n        } finally {\n            getTransactionService().complete();\n        }\n    }\n\n    @Test\n    public void testCreateAndGetGatewayInstance() throws SBonitaException {\n        final SGatewayInstance gatewayInstance = BuilderFactory.get(SGatewayInstanceBuilderFactory.class)\n                .createNewInstance(\"Gateway1\", 1, 1, 1, SGatewayType.EXCLUSIVE, 2, 3, 3).setStateId(1)\n                .setHitBys(\"a,b,c\").done();\n\n        insertGatewayInstance(gatewayInstance);\n\n        final SGatewayInstance gatewayInstanceRes = getGatewayInstanceFromDB(gatewayInstance.getId());\n\n        checkGateway(gatewayInstance, gatewayInstanceRes, 2, 3);\n\n        deleteGatewayInstance(gatewayInstanceRes);\n    }\n\n    private SGatewayInstance getGatewayInstanceFromDB(final Long gatewayId) throws SBonitaException {\n        getTransactionService().begin();\n        final SGatewayInstance gatewayInstanceRes = getServiceAccessor().getGatewayInstanceService()\n                .getGatewayInstance(gatewayId);\n        getTransactionService().complete();\n        return gatewayInstanceRes;\n    }\n\n    private void checkGateway(final SGatewayInstance gatewayInstance, final SGatewayInstance gatewayInstanceRes,\n            final long expectedProcessDefinitionId,\n            final long expectedProcessInstanceId) {\n        assertNotNull(gatewayInstance);\n        final SGatewayInstanceBuilderFactory gatewayInstanceBuilderFact = BuilderFactory\n                .get(SGatewayInstanceBuilderFactory.class);\n        final long actualProcessDefinitionId = gatewayInstanceRes\n                .getLogicalGroup(gatewayInstanceBuilderFact.getProcessDefinitionIndex());\n        final long actualProcessInstanceId = gatewayInstanceRes\n                .getLogicalGroup(gatewayInstanceBuilderFact.getRootProcessInstanceIndex());\n        assertEquals(expectedProcessDefinitionId, actualProcessDefinitionId);\n        assertEquals(expectedProcessInstanceId, actualProcessInstanceId);\n        assertEquals(gatewayInstance, gatewayInstanceRes);\n    }\n\n    private void updateGatewayState(final SGatewayInstance gatewayInstance, final int stateId) throws SBonitaException {\n        getTransactionService().begin();\n        final SGatewayInstance gatewayInstance2 = getServiceAccessor().getGatewayInstanceService()\n                .getGatewayInstance(gatewayInstance.getId());\n        getServiceAccessor().getGatewayInstanceService().setState(gatewayInstance2, stateId);\n        getTransactionService().complete();\n    }\n\n    private void updateGatewayHitbys(final SGatewayInstance gatewayInstance, final long transitionIndex)\n            throws SBonitaException {\n        getTransactionService().begin();\n        final SGatewayInstance gatewayInstance2 = getServiceAccessor().getGatewayInstanceService()\n                .getGatewayInstance(gatewayInstance.getId());\n        getServiceAccessor().getGatewayInstanceService().hitTransition(gatewayInstance2, transitionIndex);\n        getTransactionService().complete();\n    }\n\n    // @Test\n    public void testCheckMergingCondition() {\n        // it's implement need to be improved\n    }\n\n    @Test\n    public void testSetState() throws SBonitaException {\n        final SGatewayInstance gatewayInstance = BuilderFactory.get(SGatewayInstanceBuilderFactory.class)\n                .createNewInstance(\"Gateway1\", 1, 1, 1, SGatewayType.EXCLUSIVE, 2, 3, 3).setStateId(1)\n                .setHitBys(\"a,b,c\").done();\n\n        insertGatewayInstance(gatewayInstance);\n\n        final SGatewayInstance gatewayInstanceRes = getGatewayInstanceFromDB(gatewayInstance.getId());\n\n        checkGateway(gatewayInstance, gatewayInstanceRes, 2, 3);\n\n        updateGatewayState(gatewayInstanceRes, 2);\n\n        final SGatewayInstance gatewayInstanceRes2 = getGatewayInstanceFromDB(gatewayInstance.getId());\n        assertNotNull(gatewayInstanceRes2);\n        assertEquals(2, gatewayInstanceRes2.getStateId());\n\n        deleteGatewayInstance(gatewayInstanceRes);\n    }\n\n    @Test\n    public void testHitTransition() throws SBonitaException {\n        final SGatewayInstance gatewayInstance = BuilderFactory.get(SGatewayInstanceBuilderFactory.class)\n                .createNewInstance(\"Gateway1\", 1, 1, 1, SGatewayType.EXCLUSIVE, 2, 3, 3).setStateId(1)\n                .setHitBys(\"1,2,3\").done();\n\n        insertGatewayInstance(gatewayInstance);\n\n        final SGatewayInstance gatewayInstanceRes = getGatewayInstanceFromDB(gatewayInstance.getId());\n\n        checkGateway(gatewayInstance, gatewayInstanceRes, 2, 3);\n\n        updateGatewayHitbys(gatewayInstanceRes, 4);\n\n        final SGatewayInstance gatewayInstanceRes2 = getGatewayInstanceFromDB(gatewayInstance.getId());\n        assertNotNull(gatewayInstanceRes2);\n        assertEquals(\"1,2,3,4\", gatewayInstanceRes2.getHitBys());\n\n        deleteGatewayInstance(gatewayInstanceRes);\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/NodeConfigurationIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.PlatformRestartHandler;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.handler.SchedulerServiceRestartHandler;\nimport org.bonitasoft.engine.platform.configuration.NodeConfiguration;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class NodeConfigurationIT extends CommonBPMServicesTest {\n\n    public NodeConfiguration nodeConfiguration;\n\n    @Before\n    public void setup() throws BonitaException {\n        nodeConfiguration = getServiceAccessor().getPlatformConfiguration();\n    }\n\n    @Test\n    public void should_have_at_least_one_restart_handler() {\n        List<PlatformRestartHandler> platformRestartHandlers = nodeConfiguration.getPlatformRestartHandlers();\n        assertThat(platformRestartHandlers).hasSize(1);\n        assertThat(platformRestartHandlers.get(0)).isInstanceOf(SchedulerServiceRestartHandler.class);\n    }\n\n    @Test\n    public void should_clear_sessions() {\n        assertThat(nodeConfiguration.shouldClearSessions()).isTrue();\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/OperationServiceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.Callable;\n\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.OperationService;\nimport org.bonitasoft.engine.core.operation.model.SLeftOperand;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.core.operation.model.SOperatorType;\nimport org.bonitasoft.engine.core.operation.model.builder.SLeftOperandBuilderFactory;\nimport org.bonitasoft.engine.core.operation.model.builder.SOperationBuilderFactory;\nimport org.bonitasoft.engine.data.ParentContainerResolverImpl;\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\nimport org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilder;\nimport org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilderFactory;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceService;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SDataInstanceBuilder;\nimport org.bonitasoft.engine.expression.ExpressionType;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.expression.model.builder.SExpressionBuilder;\nimport org.bonitasoft.engine.expression.model.builder.SExpressionBuilderFactory;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Zhang Bole\n */\npublic class OperationServiceIT extends CommonBPMServicesTest {\n\n    private UserTransactionService transactionService;\n\n    private OperationService operationService;\n\n    private DataInstanceService dataInstanceService;\n    private ParentContainerResolverImpl parentContainerResolver;\n\n    @Before\n    public void setup() {\n        transactionService = getTransactionService();\n        dataInstanceService = getServiceAccessor().getDataInstanceService();\n        operationService = getServiceAccessor().getOperationService();\n        parentContainerResolver = (ParentContainerResolverImpl) getServiceAccessor().getParentContainerResolver();\n        parentContainerResolver.setAllowUnknownContainer(true);\n    }\n\n    @After\n    public void after() {\n        parentContainerResolver.setAllowUnknownContainer(false);\n    }\n\n    /**\n     * Assign a new value to a String Variable. Using an expression which is a constant.\n     * variableName = \"afterUpdate\"\n     *\n     * @throws Exception\n     */\n    @Test\n    public void executeOperationUsingStringConstantExpression() throws Exception {\n        final String dataInstanceName = \"variableName\";\n        final long containerId = 11L;\n        final String containerType = \"miniTask\";\n        final String defaultValue = \"beforeUpdate\";\n        final String newConstantValue = \"afterUpdate\";\n        createStringDataInstance(dataInstanceName, containerId, containerType, defaultValue, defaultValue);\n        SDataInstance dataInstance = getDataInstance(dataInstanceName, containerId, containerType);\n        assertEquals(defaultValue, dataInstance.getValue());\n\n        final SOperation operation;\n        final Map<String, Serializable> expressionContexts = new HashMap<>();\n        expressionContexts.put(\"containerId\", containerId);\n        expressionContexts.put(\"containerType\", containerType);\n        operation = buildAssignmentOperation(dataInstanceName, newConstantValue);\n        executeOperation(operation, containerId, containerType, expressionContexts);\n\n        dataInstance = getDataInstance(dataInstanceName, containerId, containerType);\n        assertEquals(newConstantValue, dataInstance.getValue());\n        deleteDataInstance(dataInstance);\n    }\n\n    /**\n     * Assign a new value to a List Variable. Using an expression which is a constant.\n     * variableName.add(\"afterUpdate\");\n     *\n     * @throws Exception\n     */\n    @SuppressWarnings(\"unchecked\")\n    @Test\n    public void executeOperationUsingJavaMethodConstantExpression() throws Exception {\n        final String dataInstanceName = \"variableName\";\n        final long containerId = 12L;\n        final String containerType = \"miniTask\";\n        final String defaultValueExpressionContent = \"new ArrayList<String>();\";\n        final String newConstantValue = \"stringAddedIntoTheList\";\n        createListDataInstance(dataInstanceName, containerId, containerType, defaultValueExpressionContent,\n                new ArrayList<String>());\n        SDataInstance dataInstance = getDataInstance(dataInstanceName, containerId, containerType);\n        assertTrue(dataInstance.getValue() instanceof ArrayList<?>);\n\n        final SOperation operation;\n        final Map<String, Serializable> expressionContexts = new HashMap<>();\n        expressionContexts.put(\"containerId\", containerId);\n        expressionContexts.put(\"containerType\", containerType);\n        operation = buildJavaMethodOperation(dataInstanceName, newConstantValue);\n        executeOperation(operation, containerId, containerType, expressionContexts);\n\n        dataInstance = getDataInstance(dataInstanceName, containerId, containerType);\n        assertTrue(dataInstance.getValue() instanceof ArrayList<?>);\n        final List<String> list = (ArrayList<String>) dataInstance.getValue();\n        assertTrue(list.contains(newConstantValue));\n\n        // clean\n        deleteDataInstance(dataInstance);\n    }\n\n    private void createListDataInstance(final String dataInstanceName, final long containerId,\n            final String containerType,\n            final String defaultValueExpressionConstant, final Serializable defaultValue) throws Exception {\n        final String description = null;\n        final SDataInstance dataInstance = buildDataInstance(dataInstanceName, ArrayList.class.getName(), description,\n                defaultValueExpressionConstant,\n                containerId, containerType, false, SExpression.TYPE_READ_ONLY_SCRIPT, SExpression.GROOVY, defaultValue);\n        insertDataInstance(dataInstance);\n    }\n\n    private SOperation buildJavaMethodOperation(final String dataInstanceName, final String newConstantValue)\n            throws SInvalidExpressionException {\n        final SLeftOperand leftOperand = BuilderFactory.get(SLeftOperandBuilderFactory.class).createNewInstance()\n                .setName(dataInstanceName).done();\n        final SExpression expression = BuilderFactory.get(SExpressionBuilderFactory.class).createNewInstance()\n                .setContent(newConstantValue)\n                .setExpressionType(ExpressionType.TYPE_CONSTANT.name()).setReturnType(String.class.getName()).done();\n        return BuilderFactory.get(SOperationBuilderFactory.class).createNewInstance()\n                .setOperator(\"add:\" + Object.class.getName()).setLeftOperand(leftOperand)\n                .setType(SOperatorType.JAVA_METHOD).setRightOperand(expression).done();\n    }\n\n    private SOperation buildAssignmentOperation(final String dataInstanceName, final String newConstantValue)\n            throws SInvalidExpressionException {\n        final SLeftOperand leftOperand = BuilderFactory.get(SLeftOperandBuilderFactory.class).createNewInstance()\n                .setName(dataInstanceName).done();\n        final SExpression expression = BuilderFactory.get(SExpressionBuilderFactory.class).createNewInstance()\n                .setContent(newConstantValue)\n                .setReturnType(String.class.getName()).setExpressionType(ExpressionType.TYPE_CONSTANT.name())\n                .setReturnType(String.class.getName()).done();\n        return BuilderFactory.get(SOperationBuilderFactory.class).createNewInstance().setOperator(\"=\")\n                .setLeftOperand(leftOperand)\n                .setType(SOperatorType.ASSIGNMENT)\n                .setRightOperand(expression).done();\n    }\n\n    private void executeOperation(final SOperation operation, final long containerId, final String containerType,\n            final Map<String, Serializable> expressionContexts) throws Exception {\n        transactionService.executeInTransaction((Callable<Void>) () -> {\n            final SExpressionContext sExpressionContext = new SExpressionContext();\n            sExpressionContext.setSerializableInputValues(expressionContexts);\n            sExpressionContext.setContainerId(containerId);\n            sExpressionContext.setContainerType(containerType);\n            operationService.execute(operation, containerId, containerType, sExpressionContext);\n            return null;\n        });\n    }\n\n    private SDataInstance getDataInstance(final String dataInstanceName, final long containerId,\n            final String containerType) throws Exception {\n        return transactionService.executeInTransaction(\n                () -> dataInstanceService.getDataInstance(dataInstanceName, containerId, containerType,\n                        parentContainerResolver));\n    }\n\n    private void createStringDataInstance(final String instanceName, final long containerId, final String containerType,\n            final String defaultValueExpressionContent, final Serializable currentDataInstanceValue) throws Exception {\n        final SDataInstance dataInstance = buildDataInstance(instanceName, String.class.getName(), \"testUpdate\",\n                defaultValueExpressionContent, containerId,\n                containerType, false, SExpression.TYPE_CONSTANT, null, currentDataInstanceValue);\n        insertDataInstance(dataInstance);\n    }\n\n    private SDataInstance buildDataInstance(final String instanceName, final String className, final String description,\n            final String defaultValueExpressionContent, final long containerId, final String containerType,\n            final boolean isTransient,\n            final String expressionType, final String expressionInterpreter,\n            final Serializable currentDataInstanceValue) throws SBonitaException {\n        // create definition\n        final SDataDefinitionBuilder dataDefinitionBuilder = BuilderFactory.get(SDataDefinitionBuilderFactory.class)\n                .createNewInstance(instanceName, className);\n        initializeBuilder(dataDefinitionBuilder, description, defaultValueExpressionContent, className, isTransient,\n                expressionType, expressionInterpreter);\n        final SDataDefinition dataDefinition = dataDefinitionBuilder.done();\n        // create data instance\n        final SDataInstanceBuilder dataInstanceBuilder = SDataInstanceBuilder.createNewInstance(dataDefinition);\n        return dataInstanceBuilder.setContainerId(containerId).setContainerType(containerType)\n                .setValue(currentDataInstanceValue).done();\n    }\n\n    private void insertDataInstance(final SDataInstance dataInstance) throws Exception {\n        transactionService.executeInTransaction((Callable<Void>) () -> {\n            dataInstanceService.createDataInstance(dataInstance);\n            return null;\n        });\n    }\n\n    private void initializeBuilder(final SDataDefinitionBuilder dataDefinitionBuilder, final String description,\n            final String defaultValueExpressionContent,\n            final String defaultValueExprReturnType, final boolean isTransient, final String expressionType,\n            final String interpreter)\n            throws SInvalidExpressionException {\n        SExpression expression = null;\n        if (defaultValueExpressionContent != null) {\n            // create expression\n            final SExpressionBuilder expreBuilder = BuilderFactory.get(SExpressionBuilderFactory.class)\n                    .createNewInstance();\n            // this discrimination'll be changed.\n            expreBuilder.setContent(defaultValueExpressionContent).setExpressionType(expressionType)\n                    .setReturnType(defaultValueExprReturnType);\n            if (interpreter != null) {\n                expreBuilder.setInterpreter(interpreter);\n            }\n            expression = expreBuilder.done();\n        }\n        dataDefinitionBuilder.setDescription(description);\n        dataDefinitionBuilder.setTransient(isTransient);\n        dataDefinitionBuilder.setDefaultValue(expression);\n    }\n\n    private void deleteDataInstance(final SDataInstance dataInstance) throws Exception {\n        transactionService.executeInTransaction((Callable<Void>) () -> {\n            dataInstanceService.deleteDataInstance(dataInstance);\n            return null;\n        });\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/ProcessDefinitionServiceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm;\n\nimport static org.junit.Assert.*;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.bpm.process.ActivationState;\nimport org.bonitasoft.engine.bpm.process.ConfigurationState;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionDeployInfoUpdateBuilderFactory;\nimport org.bonitasoft.engine.identity.UserSearchDescriptor;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Celine Souchet\n */\npublic class ProcessDefinitionServiceIT extends CommonBPMServicesTest {\n\n    private ProcessDefinitionService processDefinitionService;\n\n    private ActorMappingService actorMappingService;\n\n    @Before\n    public void before() {\n        processDefinitionService = getServiceAccessor().getProcessDefinitionService();\n        actorMappingService = getServiceAccessor().getActorMappingService();\n    }\n\n    private SessionService getSessionService() {\n        return getServiceAccessor().getSessionService();\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void storeNullProcessDefinition() throws Exception {\n        getTransactionService().begin();\n        processDefinitionService.store(null);\n        getTransactionService().complete();\n    }\n\n    @Test\n    public void addedDisplayName() throws Exception {\n        final SProcessDefinition sProcessDefinition = createSProcessDefinition(\"myProcessName\", \"1.0\");\n\n        getTransactionService().begin();\n        final SProcessDefinitionDeployInfo processDefinitionDeployInfo = processDefinitionService\n                .getProcessDeploymentInfo(sProcessDefinition.getId());\n        assertEquals(\"myProcessName\", processDefinitionDeployInfo.getName());\n        assertEquals(\"myProcessName\", processDefinitionDeployInfo.getDisplayName()); // display name should be the same as name\n        assertEquals(\"1.0\", processDefinitionDeployInfo.getVersion());\n        assertEquals(ConfigurationState.RESOLVED.name(), processDefinitionDeployInfo.getConfigurationState());\n        assertEquals(ActivationState.ENABLED.name(), processDefinitionDeployInfo.getActivationState());\n        getTransactionService().complete();\n\n        // clean-up\n        deleteSProcessDefinition(sProcessDefinition);\n    }\n\n    @Test\n    public void updateProcessDefinitionDeployInfo() throws Exception {\n        // create process definition\n        final SProcessDefinition sProcessDefinition = createSProcessDefinition(\"myProcessName\", \"1.0\");\n        final Long processId = sProcessDefinition.getId();\n        final String updatedDisplayName = \"updateDisplayName\";\n\n        getTransactionService().begin();\n        // update processDefinitionDeployInfo\n        final EntityUpdateDescriptor updateDescriptor = BuilderFactory\n                .get(SProcessDefinitionDeployInfoUpdateBuilderFactory.class).createNewInstance()\n                .updateDisplayName(updatedDisplayName)\n                .updateActivationState(ActivationState.ENABLED).done();\n        processDefinitionService.updateProcessDefinitionDeployInfo(processId, updateDescriptor);\n\n        // check and do assert\n        final SProcessDefinitionDeployInfo processDefinitionDeployInfo = processDefinitionService\n                .getProcessDeploymentInfo(processId);\n        assertEquals(\"myProcessName\", processDefinitionDeployInfo.getName());\n        assertEquals(updatedDisplayName, processDefinitionDeployInfo.getDisplayName());\n        assertNotNull(processDefinitionDeployInfo.getLastUpdateDate());\n        assertEquals(ConfigurationState.RESOLVED.name(), processDefinitionDeployInfo.getConfigurationState());\n        assertEquals(ActivationState.ENABLED.name(), processDefinitionDeployInfo.getActivationState());\n        getTransactionService().complete();\n\n        // clean-up\n        deleteSProcessDefinition(sProcessDefinition);\n    }\n\n    @Test(expected = SProcessDefinitionNotFoundException.class)\n    public void updateProcessDefinitionDeployInfoThrowException() throws Exception {\n        final String updatedDisplayName = \"updateDisplayName\";\n\n        // create process definition\n        final SProcessDefinition sProcessDefinition = createSProcessDefinition(\"myProcessName\", \"1.0\");\n\n        getTransactionService().begin();\n        // update processDefinitionDeployInfo with wrong processId\n\n        final EntityUpdateDescriptor updateDescriptor = BuilderFactory\n                .get(SProcessDefinitionDeployInfoUpdateBuilderFactory.class).createNewInstance()\n                .updateDisplayName(updatedDisplayName).done();\n        try {\n            processDefinitionService.updateProcessDefinitionDeployInfo(sProcessDefinition.getId() + 1,\n                    updateDescriptor);\n        } finally {\n            getTransactionService().complete();\n            // clean-up\n            deleteSProcessDefinition(sProcessDefinition);\n        }\n    }\n\n    @Test\n    public void ifDeployedByIsTheUserId() throws Exception {\n        final SProcessDefinition sProcessDefinition = createSProcessDefinition(\"testIfDeployedByIsTheUserId\", \"1.0\");\n\n        getTransactionService().begin();\n        final Long processId = sProcessDefinition.getId();\n        final SProcessDefinitionDeployInfo processDefinitionDeployInfo = processDefinitionService\n                .getProcessDeploymentInfo(processId);\n        assertEquals(processDefinitionDeployInfo.getDeployedBy(),\n                getSessionService().getSession(getAPISession().getId()).getUserId());\n        getTransactionService().complete();\n\n        assertEquals(processDefinitionDeployInfo.getDeployedBy(),\n                getSessionService().getSession(getAPISession().getId()).getUserId());\n\n        getTransactionService().begin();\n        actorMappingService.deleteActors(processId);\n        processDefinitionService.disableProcessDeploymentInfo(processId);\n        processDefinitionService.delete(processId);\n        getTransactionService().complete();\n    }\n\n    @Test\n    public void getProcessDefIds() throws Exception {\n        final List<SProcessDefinition> sProcessDefinitions = createSProcessDefinitions(25, \"testGetProcessDefIds\",\n                \"0.0\");\n\n        getTransactionService().begin();\n        List<Long> processDefIds = processDefinitionService.getProcessDefinitionIds(0, 10);\n        assertEquals(10, processDefIds.size());\n        processDefIds = processDefinitionService.getProcessDefinitionIds(0, 20);\n        assertEquals(20, processDefIds.size());\n        processDefIds = processDefinitionService.getProcessDefinitionIds(0, 25);\n        assertEquals(25, processDefIds.size());\n        getTransactionService().complete();\n\n        // clean-up\n        deleteSProcessDefinitions(sProcessDefinitions);\n    }\n\n    @Test\n    public void getNumberOfUsersWhoCanStartProcessWithActorInitiator() throws Exception {\n        final SUser sUser1 = createEnabledSUser(\"firstname1\", \"lastname1\", \"pwd1\");\n        final SUser sUser2 = createEnabledSUser(\"firstname2\", \"lastname2\", \"pwd2\");\n        final SProcessDefinition sProcessDefinition = createSProcessDefinitionWithSActor(\"process1\", \"1.0\", \"actor1\",\n                true, Arrays.asList(sUser1, sUser2));\n\n        getTransactionService().begin();\n        final QueryOptions searchOptions = new QueryOptions(0, 5);\n        final long result = processDefinitionService\n                .getNumberOfUsersWhoCanStartProcessDeploymentInfo(sProcessDefinition.getId(), searchOptions);\n        assertEquals(2, result);\n        getTransactionService().complete();\n\n        // clean-up\n        deleteSProcessDefinition(sProcessDefinition);\n        deleteSUsers(sUser1, sUser2);\n    }\n\n    @Test\n    public void getNumberOfUsersWhoCanStartProcessWithActorNotInitiator() throws Exception {\n        final SUser sUser1 = createEnabledSUser(\"firstname1\", \"lastname1\", \"pwd1\");\n        final SUser sUser2 = createEnabledSUser(\"firstname2\", \"lastname2\", \"pwd2\");\n        final SProcessDefinition sProcessDefinition = createSProcessDefinitionWithSActor(\"process1\", \"1.0\", \"actor1\",\n                false, Arrays.asList(sUser1, sUser2));\n\n        getTransactionService().begin();\n        final QueryOptions searchOptions = new QueryOptions(0, 5);\n        final long result = processDefinitionService\n                .getNumberOfUsersWhoCanStartProcessDeploymentInfo(sProcessDefinition.getId(), searchOptions);\n        assertEquals(0, result);\n        getTransactionService().complete();\n\n        // clean-up\n        deleteSProcessDefinition(sProcessDefinition);\n        deleteSUsers(sUser1, sUser2);\n    }\n\n    @Test\n    public void getNumberOfUsersWhoCanStartProcessWithActorInitiatorAndFilterManagedBy() throws Exception {\n        final SUser sUser1 = createEnabledSUser(\"firstname1\", \"lastname1\", \"pwd1\");\n        final SUser sUser2 = createEnabledSUser(\"firstname2\", \"lastname2\", \"pwd2\", sUser1.getId());\n        final SUser sUser3 = createEnabledSUser(\"firstname3\", \"lastname3\", \"pwd3\", sUser2.getId());\n        final SUser sUser4 = createEnabledSUser(\"firstname4\", \"lastname4\", \"pwd4\");\n        final SProcessDefinition sProcessDefinition = createSProcessDefinitionWithSActor(\"process1\", \"1.0\", \"actor1\",\n                true,\n                Arrays.asList(sUser2, sUser3, sUser4));\n\n        getTransactionService().begin();\n        final FilterOption filterManagedBy = new FilterOption(SUser.class, UserSearchDescriptor.MANAGER_USER_ID,\n                sUser1.getId());\n        final QueryOptions searchOptions = new QueryOptions(0, 5, null, Arrays.asList(filterManagedBy), null);\n        final long result = processDefinitionService\n                .getNumberOfUsersWhoCanStartProcessDeploymentInfo(sProcessDefinition.getId(), searchOptions);\n        assertEquals(1, result);\n        getTransactionService().complete();\n\n        // clean-up\n        deleteSProcessDefinition(sProcessDefinition);\n        deleteSUsers(sUser1, sUser2);\n    }\n\n    @Test\n    public void searchUsersWhoCanStartProcessWithActorInitiator() throws Exception {\n        final SUser sUser1 = createEnabledSUser(\"firstname1\", \"lastname1\", \"pwd1\");\n        final SUser sUser2 = createEnabledSUser(\"firstname2\", \"lastname2\", \"pwd2\");\n        final SProcessDefinition sProcessDefinition = createSProcessDefinitionWithSActor(\"process1\", \"1.0\", \"actor1\",\n                true, Arrays.asList(sUser1, sUser2));\n\n        getTransactionService().begin();\n        final QueryOptions searchOptions = new QueryOptions(0, 5, SUser.class, UserSearchDescriptor.FIRST_NAME,\n                OrderByType.ASC);\n        final List<SUser> result = processDefinitionService\n                .searchUsersWhoCanStartProcessDeploymentInfo(sProcessDefinition.getId(), searchOptions);\n        assertEquals(sUser1, result.get(0));\n        assertEquals(sUser2, result.get(1));\n        getTransactionService().complete();\n\n        // clean-up\n        deleteSProcessDefinition(sProcessDefinition);\n        deleteSUsers(sUser1, sUser2);\n    }\n\n    @Test\n    public void searchUsersWhoCanStartProcessWithActorNotInitiator() throws Exception {\n        final SUser sUser1 = createEnabledSUser(\"firstname1\", \"lastname1\", \"pwd1\");\n        final SUser sUser2 = createEnabledSUser(\"firstname2\", \"lastname2\", \"pwd2\");\n        final SProcessDefinition sProcessDefinition = createSProcessDefinitionWithSActor(\"process1\", \"1.0\", \"actor1\",\n                false, Arrays.asList(sUser1, sUser2));\n\n        getTransactionService().begin();\n        final QueryOptions searchOptions = new QueryOptions(0, 5, SUser.class, UserSearchDescriptor.FIRST_NAME,\n                OrderByType.ASC);\n        final List<SUser> result = processDefinitionService\n                .searchUsersWhoCanStartProcessDeploymentInfo(sProcessDefinition.getId(), searchOptions);\n        assertTrue(\"Users are added to a actor that isn't initiator\", result.isEmpty());\n        getTransactionService().complete();\n\n        // clean-up\n        deleteSProcessDefinition(sProcessDefinition);\n        deleteSUsers(sUser1, sUser2);\n    }\n\n    @Test\n    public void searchUsersWhoCanStartProcessWithActorInitiatorAndFilterManagedBy() throws Exception {\n        final SUser sUser1 = createEnabledSUser(\"firstname1\", \"lastname1\", \"pwd1\");\n        final SUser sUser2 = createEnabledSUser(\"firstname2\", \"lastname2\", \"pwd2\", sUser1.getId());\n        final SUser sUser3 = createEnabledSUser(\"firstname3\", \"lastname3\", \"pwd3\", sUser2.getId());\n        final SUser sUser4 = createEnabledSUser(\"firstname4\", \"lastname4\", \"pwd4\");\n        final SProcessDefinition sProcessDefinition = createSProcessDefinitionWithSActor(\"process1\", \"1.0\", \"actor1\",\n                true,\n                Arrays.asList(sUser2, sUser3, sUser4));\n\n        getTransactionService().begin();\n        final FilterOption filterManagedBy = new FilterOption(SUser.class, UserSearchDescriptor.MANAGER_USER_ID,\n                sUser1.getId());\n        final OrderByOption orderByFirstName = new OrderByOption(SUser.class, UserSearchDescriptor.FIRST_NAME,\n                OrderByType.ASC);\n        final QueryOptions searchOptions = new QueryOptions(0, 5, Arrays.asList(orderByFirstName),\n                Arrays.asList(filterManagedBy), null);\n        final List<SUser> result = processDefinitionService\n                .searchUsersWhoCanStartProcessDeploymentInfo(sProcessDefinition.getId(), searchOptions);\n        getTransactionService().complete();\n        assertEquals(sUser2, result.get(0));\n\n        // clean-up\n        deleteSProcessDefinition(sProcessDefinition);\n        deleteSUsers(sUser1, sUser2);\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/ProcessInstanceServiceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.junit.Assert.assertEquals;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.CallableWithException;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.core.document.model.SDocument;\nimport org.bonitasoft.engine.core.document.model.SMappedDocument;\nimport org.bonitasoft.engine.core.document.model.builder.SDocumentBuilderFactory;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceCreationException;\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SCallActivityInstanceBuilderFactory;\nimport org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilder;\nimport org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilderFactory;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.data.instance.exception.SDataInstanceNotFoundException;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SDataInstanceBuilder;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.transaction.STransactionCommitException;\nimport org.bonitasoft.engine.transaction.STransactionCreationException;\nimport org.bonitasoft.engine.transaction.STransactionRollbackException;\nimport org.bonitasoft.engine.transaction.TransactionService;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Emmanuel Duchastenier\n * @author Yanyan Liu\n */\npublic class ProcessInstanceServiceIT extends CommonBPMServicesTest {\n\n    private final static Logger LOGGER = LoggerFactory.getLogger(CommonBPMServicesTest.class);\n\n    private TransactionService transactionService;\n    private ProcessInstanceService processInstanceService;\n    private ActivityInstanceService activityInstanceService;\n\n    @Before\n    public void setup() {\n        transactionService = getTransactionService();\n        processInstanceService = getServiceAccessor().getProcessInstanceService();\n        activityInstanceService = getServiceAccessor().getActivityInstanceService();\n    }\n\n    /**\n     * Clean up of all existing process instances\n     */\n    private void cleanUpAllProcessInstances() {\n        try {\n            List<SProcessInstance> processInstances = inTx(() -> getFirstProcessInstances(1000));\n\n            inTx(() -> {\n                for (final SProcessInstance sProcessInstance : processInstances) {\n                    processInstanceService.deleteProcessInstance(sProcessInstance);\n                }\n                return null;\n            });\n\n            List<SAProcessInstance> archives = inTx(() -> getFirstArchivedProcessInstances(1000));\n\n            inTx(() -> {\n                for (final SAProcessInstance saProcessInstance : archives) {\n                    processInstanceService.deleteArchivedProcessInstance(saProcessInstance);\n                }\n                return null;\n            });\n\n        } catch (final Exception e) {\n            LOGGER.error(\"Error during clean-up. Ignoring...\");\n        }\n    }\n\n    private <T> T inTx(CallableWithException<T> callable) throws Exception {\n        transactionService.begin();\n        final T result = callable.call();\n        transactionService.complete();\n        return result;\n    }\n\n    @Test\n    public void testGetNumberOfProcessInstances()\n            throws STransactionCreationException, STransactionCommitException, SProcessInstanceCreationException,\n            STransactionRollbackException, SBonitaReadException {\n        transactionService.begin();\n        final long processDefinitionId = 123L;\n        SProcessInstance sProcessInstance = SProcessInstance.builder()\n                .name(\"an instance name\")\n                .processDefinitionId(processDefinitionId).build();\n        processInstanceService.createProcessInstance(sProcessInstance);\n        final QueryOptions queryOptions = new QueryOptions(0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS);\n        final long processInstanceNumber = processInstanceService.getNumberOfProcessInstances(queryOptions);\n        transactionService.complete();\n\n        // first test with one process:\n        assertEquals(1, processInstanceNumber);\n\n        transactionService.begin();\n\n        // second test with 100 processes:\n        for (int i = 0; i < 100; i++) {\n            sProcessInstance = SProcessInstance.builder()\n                    .name(\"process instance \" + i)\n                    .processDefinitionId(processDefinitionId).build();\n            processInstanceService.createProcessInstance(sProcessInstance);\n        }\n        final long numberOfProcessInstances = processInstanceService.getNumberOfProcessInstances(queryOptions);\n        transactionService.complete();\n        assertEquals(101, numberOfProcessInstances);\n\n        // clean up:\n        cleanUpAllProcessInstances();\n    }\n\n    @Test\n    public void getCorrectProcessInstancesOrder() throws Exception {\n        // Creation of the process instances we want to retrieve:\n        transactionService.begin();\n        final long processDefinitionId = 123L;\n        final SProcessInstance sProcessInstance0 = SProcessInstance.builder()\n                .name(\"instance name 0\")\n                .processDefinitionId(processDefinitionId).build();\n        final SProcessInstance sProcessInstance1 = SProcessInstance.builder()\n                .name(\"instance name 1\")\n                .processDefinitionId(processDefinitionId).build();\n        final SProcessInstance sProcessInstance2 = SProcessInstance.builder()\n                .name(\"instance name 2\")\n                .processDefinitionId(processDefinitionId).build();\n        processInstanceService.createProcessInstance(sProcessInstance0);\n        processInstanceService.setState(sProcessInstance0, ProcessInstanceState.STARTED);\n        // to ensure the date is not exactly the same as the previous one:\n        Thread.sleep(5);\n        processInstanceService.createProcessInstance(sProcessInstance1);\n        processInstanceService.setState(sProcessInstance1, ProcessInstanceState.STARTED);\n        // to ensure the date is not exactly the same as the previous one:\n        Thread.sleep(5);\n        processInstanceService.createProcessInstance(sProcessInstance2);\n        processInstanceService.setState(sProcessInstance2, ProcessInstanceState.STARTED);\n        transactionService.complete();\n\n        // Retrieval of the previously created process instances:\n        transactionService.begin();\n        final List<SProcessInstance> processInstances = getFirstProcessInstances(20);\n        transactionService.complete();\n\n        // Verification of the number of process instances retrieved:\n        assertEquals(3, processInstances.size());\n        // Verification of the order:\n        assertEquals(sProcessInstance0.getId(), processInstances.get(2).getId());\n        assertEquals(sProcessInstance1.getId(), processInstances.get(1).getId());\n        assertEquals(sProcessInstance2.getId(), processInstances.get(0).getId());\n\n        // clean up:\n        cleanUpAllProcessInstances();\n    }\n\n    @Test\n    public void testSetState() {\n        // TODO: not yet implemented\n    }\n\n    @Test\n    public void testGetChildInstanceIdsOfProcessInstance() throws Exception {\n        // first create parent process instance\n        transactionService.begin();\n        final SCallActivityInstanceBuilderFactory sCallActivityInstanceBuilder = BuilderFactory\n                .get(SCallActivityInstanceBuilderFactory.class);\n        final long processDefinitionId = 123L;\n        final SProcessInstance parentProcessInstance = SProcessInstance.builder()\n                .name(\"an instance name\")\n                .processDefinitionId(processDefinitionId).build();\n        processInstanceService.createProcessInstance(parentProcessInstance);\n        transactionService.complete();\n\n        transactionService.begin();\n        // second create 10 child processes:\n        SActivityInstance activityInstance = sCallActivityInstanceBuilder\n                .createNewCallActivityInstance(\"callActivity\", 1,\n                        parentProcessInstance.getContainerId(),\n                        parentProcessInstance.getContainerId(), processDefinitionId, parentProcessInstance.getId(),\n                        parentProcessInstance.getId())\n                .done();\n        activityInstanceService.createActivityInstance(activityInstance);\n        final List<Long> childInstanceIds = new ArrayList<>();\n        SProcessInstance childProcessInstance;\n        for (int i = 0; i < 10; i++) {\n            childProcessInstance = SProcessInstance.builder()\n                    .name(\"child process instance \" + i)\n                    .processDefinitionId(processDefinitionId)\n                    .containerId(parentProcessInstance.getId()).callerId(activityInstance.getId())\n                    .callerType(SFlowNodeType.CALL_ACTIVITY).build();\n            processInstanceService.createProcessInstance(childProcessInstance);\n            childInstanceIds.add(childProcessInstance.getId());\n        }\n        transactionService.complete();\n        // test get child by paging, order by name ASC\n        final String nameField = SProcessInstance.NAME_KEY;\n        transactionService.begin();\n        final List<Long> childInstanceIdList1 = processInstanceService.getChildInstanceIdsOfProcessInstance(\n                parentProcessInstance.getId(), 0, 4, nameField,\n                OrderByType.ASC);\n        assertEquals(4, childInstanceIdList1.size());\n        assertEquals(childInstanceIds.get(0), childInstanceIdList1.get(0));\n        assertEquals(childInstanceIds.get(1), childInstanceIdList1.get(1));\n        assertEquals(childInstanceIds.get(2), childInstanceIdList1.get(2));\n        assertEquals(childInstanceIds.get(3), childInstanceIdList1.get(3));\n\n        final List<Long> childInstanceIdList2 = processInstanceService.getChildInstanceIdsOfProcessInstance(\n                parentProcessInstance.getId(), 4, 4, nameField,\n                OrderByType.ASC);\n        assertEquals(4, childInstanceIdList2.size());\n        assertEquals(childInstanceIds.get(4), childInstanceIdList2.get(0));\n        assertEquals(childInstanceIds.get(5), childInstanceIdList2.get(1));\n        assertEquals(childInstanceIds.get(6), childInstanceIdList2.get(2));\n        assertEquals(childInstanceIds.get(7), childInstanceIdList2.get(3));\n\n        final List<Long> childInstanceIdList3 = processInstanceService.getChildInstanceIdsOfProcessInstance(\n                parentProcessInstance.getId(), 8, 4, nameField,\n                OrderByType.ASC);\n        assertEquals(2, childInstanceIdList3.size());\n        assertEquals(childInstanceIds.get(8), childInstanceIdList3.get(0));\n        assertEquals(childInstanceIds.get(9), childInstanceIdList3.get(1));\n\n        // test DESC\n        final List<Long> childInstanceIdList4 = processInstanceService.getChildInstanceIdsOfProcessInstance(\n                parentProcessInstance.getId(), 0, 4, nameField,\n                OrderByType.DESC);\n        assertEquals(4, childInstanceIdList4.size());\n        assertEquals(childInstanceIds.get(9), childInstanceIdList4.get(0));\n        assertEquals(childInstanceIds.get(8), childInstanceIdList4.get(1));\n        assertEquals(childInstanceIds.get(7), childInstanceIdList4.get(2));\n        assertEquals(childInstanceIds.get(6), childInstanceIdList4.get(3));\n\n        activityInstance = activityInstanceService.getActivityInstance(activityInstance.getId());\n        activityInstanceService.deleteFlowNodeInstance(activityInstance);\n\n        transactionService.complete();\n\n        // clean up:\n        cleanUpAllProcessInstances();\n\n    }\n\n    @Test\n    public void testGetNumberOfChildInstancesOfProcessInstance() throws Exception {\n        // first create parent process instance and test\n        transactionService.begin();\n        final SCallActivityInstanceBuilderFactory sCallActivityInstanceBuilder = BuilderFactory\n                .get(SCallActivityInstanceBuilderFactory.class);\n        final long processDefinitionId = 123L;\n        final SProcessInstance parentProcessInstance = SProcessInstance.builder()\n                .name(\"an instance name\")\n                .processDefinitionId(processDefinitionId).build();\n        processInstanceService.createProcessInstance(parentProcessInstance);\n        long numberOfChild = processInstanceService\n                .getNumberOfChildInstancesOfProcessInstance(parentProcessInstance.getId());\n        transactionService.complete();\n        assertEquals(0, numberOfChild);\n\n        transactionService.begin();\n        // second create 10 child processes:\n        final List<Long> childInstanceIds = new ArrayList<>();\n        SProcessInstance childProcessInstance;\n        SActivityInstance activityInstance = sCallActivityInstanceBuilder\n                .createNewCallActivityInstance(\"callActivity\", 1,\n                        parentProcessInstance.getContainerId(),\n                        parentProcessInstance.getContainerId(), processDefinitionId, parentProcessInstance.getId(),\n                        parentProcessInstance.getId())\n                .done();\n        activityInstanceService.createActivityInstance(activityInstance);\n        for (int i = 0; i < 10; i++) {\n            childProcessInstance = SProcessInstance.builder()\n                    .name(\"child process instance \" + i)\n                    .processDefinitionId(processDefinitionId)\n                    .containerId(parentProcessInstance.getId()).callerId(activityInstance.getId())\n                    .callerType(SFlowNodeType.CALL_ACTIVITY).build();\n            processInstanceService.createProcessInstance(childProcessInstance);\n            childInstanceIds.add(childProcessInstance.getId());\n        }\n        numberOfChild = processInstanceService\n                .getNumberOfChildInstancesOfProcessInstance(parentProcessInstance.getId());\n        activityInstanceService.deleteFlowNodeInstance(activityInstance);\n        transactionService.complete();\n        assertEquals(childInstanceIds.size(), numberOfChild);\n\n        // clean up:\n        cleanUpAllProcessInstances();\n    }\n\n    @Test\n    public void testDeleteProcessInstance() throws Exception {\n        //given\n        createProcessInstanceInTransaction(123L, \"an instance name\");\n\n        //when\n        cleanUpAllProcessInstances();\n\n        //then\n        assertEquals(0, getNumberOfProcessInstances());\n    }\n\n    @Test\n    public void testDeleteProcessInstanceAlsoDeleteDataInstances() throws Exception {\n        final long processDefinitionId = 123123123L;\n        final String processName = \"myProcInst\";\n\n        // create a process instance\n        final SProcessInstance processInstance = createProcessInstanceInTransaction(processDefinitionId, processName);\n\n        // create a data instance having the process instance as container\n        final SDataInstance globalDataInstance = createDataInTransaction(\"myData\", String.class.getName(),\n                processInstance.getId(),\n                DataInstanceContainer.PROCESS_INSTANCE);\n\n        // create a automatic task\n        final SActivityInstance taskInstance = createSAutomaticTaskInstance(\n                \"auto\", 1234L, processInstance.getId(), processDefinitionId, processInstance.getId());\n        final SDataInstance localDataInstance = createDataInTransaction(\"myLocalData\", String.class.getName(),\n                taskInstance.getId(),\n                DataInstanceContainer.ACTIVITY_INSTANCE);\n\n        // delete the process instance: the data instance is supposed to be deleted at same time\n        deleteSProcessInstance(processInstance);\n\n        // check that no more data is available for the deleted process instance and flow node instance\n        assertEquals(0, getNumberOfProcessInstances());\n        checkDataDoesNotExist(globalDataInstance);\n        checkDataDoesNotExist(localDataInstance);\n        checkFlowNodeDoesNotExist(taskInstance);\n\n    }\n\n    @Test\n    public void testDeleteProcessInstanceAlsoDeleteDocumentMappings() throws Exception {\n        //given\n        final long processDefinitionId = 123123123L;\n        final String processName = \"myProcInst\";\n\n        // create a process instance\n        final SProcessInstance processInstance = createProcessInstanceInTransaction(processDefinitionId, processName);\n\n        // attached a document to the process instance\n        SDocument document = new SDocumentBuilderFactory()\n                .createNewProcessDocument(\"myDocument\", \"mimeType\", 1234L, \"content\".getBytes()).done();\n        transactionService.begin();\n        SMappedDocument mappedDocument = getServiceAccessor().getDocumentService()\n                .attachDocumentToProcessInstance(document, processInstance.getId(), document.getFileName(),\n                        \"a description\");\n        transactionService.complete();\n\n        //when\n        deleteSProcessInstance(processInstance);\n\n        //then\n        assertEquals(0, getNumberOfProcessInstances());\n        transactionService.begin();\n        // assert document mapping does not exist\n        assertThatExceptionOfType(SObjectNotFoundException.class)\n                .isThrownBy(() -> getServiceAccessor().getDocumentService().getMappedDocument(mappedDocument.getId()));\n        transactionService.complete();\n    }\n\n    private void checkDataDoesNotExist(final SDataInstance dataInstance) {\n        assertThatExceptionOfType(SDataInstanceNotFoundException.class)\n                .as(\"the data instance was not deleted\")\n                .isThrownBy(() -> getDataInstanceInTransaction(dataInstance.getId()));\n    }\n\n    private void checkFlowNodeDoesNotExist(final SFlowNodeInstance flowNodeInstance) {\n        assertThatExceptionOfType(SFlowNodeNotFoundException.class)\n                .as(\"the flowNode instance was not deleted\")\n                .isThrownBy(() -> getFlowNodeInstance(flowNodeInstance.getId()));\n    }\n\n    private SProcessInstance createProcessInstanceInTransaction(final long process_definition_id,\n            final String processName)\n            throws STransactionCreationException, SProcessInstanceCreationException, STransactionCommitException,\n            STransactionRollbackException {\n        getTransactionService().begin();\n        // Creation of a process instance:\n        final SProcessInstance processInstance = SProcessInstance.builder()\n                .name(processName)\n                .processDefinitionId(process_definition_id).build();\n        processInstanceService.createProcessInstance(processInstance);\n        getTransactionService().complete();\n        return processInstance;\n    }\n\n    private SDataInstance createDataInTransaction(final String dataName, final String dataType, final long containerId,\n            final DataInstanceContainer containerType) throws SBonitaException {\n        getTransactionService().begin();\n        final SDataDefinitionBuilder dataDefBuilder = BuilderFactory.get(SDataDefinitionBuilderFactory.class)\n                .createNewInstance(dataName, dataType);\n\n        final SDataInstanceBuilder dataInstanceBuilder = SDataInstanceBuilder.createNewInstance(dataDefBuilder.done());\n        dataInstanceBuilder.setContainerId(containerId);\n        dataInstanceBuilder.setContainerType(containerType.name());\n\n        final SDataInstance dataInstance = dataInstanceBuilder.done();\n        getServiceAccessor().getDataInstanceService().createDataInstance(dataInstance);\n\n        getTransactionService().complete();\n        return dataInstance;\n    }\n\n    private SDataInstance getDataInstanceInTransaction(final long dataInstanceId) throws SBonitaException {\n        SDataInstance dataInstance;\n        getTransactionService().begin();\n        try {\n            dataInstance = getServiceAccessor().getDataInstanceService().getDataInstance(dataInstanceId);\n        } finally {\n            getTransactionService().complete();\n        }\n        return dataInstance;\n    }\n\n    private long getNumberOfProcessInstances() throws SBonitaException {\n        transactionService.begin();\n        final QueryOptions queryOptions = new QueryOptions(0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS);\n        final long processInstanceNumber = processInstanceService.getNumberOfProcessInstances(queryOptions);\n        transactionService.complete();\n        return processInstanceNumber;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/RecoveryMechanismIT.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.concurrent.TimeUnit;\n\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.tenant.restart.RecoveryService;\nimport org.bonitasoft.engine.test.CommonAPILocalIT;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class RecoveryMechanismIT extends CommonAPILocalIT {\n\n    private RecoveryService recoveryService;\n\n    @Before\n    public void before() throws BonitaException {\n        loginWithTechnicalUser();\n        recoveryService = getServiceAccessor().lookup(\"recoveryService\");\n        recoveryService.setConsiderElementsOlderThan(\"PT0S\");\n    }\n\n    @After\n    public void after() {\n        recoveryService.setConsiderElementsOlderThan(\"PT1H\");\n    }\n\n    @Test\n    public void should_recover_elements_after_incident() throws Exception {\n        ProcessDefinitionBuilder definitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processRecovered\", \"1.0\");\n        definitionBuilder.addAutomaticTask(\"auto1\").addMultiInstance(false,\n                new ExpressionBuilder().createConstantIntegerExpression(100));\n        ProcessDefinition processDefinition = deployAndEnableProcess(definitionBuilder.done());\n\n        ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        TimeUnit.MILLISECONDS.sleep(100);\n\n        getServiceAccessor().getWorkExecutorService().pause();\n        getServiceAccessor().getWorkExecutorService().resume();\n\n        TimeUnit.MILLISECONDS.sleep(500);\n        assertThat(getProcessAPI().getProcessInstance(processInstance.getId()).getState())\n                .isEqualToIgnoringCase(ProcessInstanceState.STARTED.name());\n\n        recoveryService.recoverAllElements();\n\n        waitForProcessToFinish(processInstance);\n\n        recoveryService.recoverAllElements();\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/SupervisorServiceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm;\n\nimport static org.junit.Assert.*;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.identity.model.SRole;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.supervisor.mapping.SSupervisorNotFoundException;\nimport org.bonitasoft.engine.supervisor.mapping.SupervisorMappingService;\nimport org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor;\nimport org.bonitasoft.engine.transaction.TransactionService;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Yanyan Liu\n * @author Elias Ricken de Medeiros\n */\npublic class SupervisorServiceIT extends CommonBPMServicesTest {\n\n    private TransactionService transactionService;\n\n    private static SupervisorMappingService supervisorService;\n\n    private IdentityService identityService;\n\n    final long processDefId = 123L;\n\n    @Before\n    public void setup() {\n        this.transactionService = getTransactionService();\n        supervisorService = getServiceAccessor().getSupervisorService();\n        this.identityService = getServiceAccessor().getIdentityService();\n    }\n\n    @Test\n    public void testCreateAndGetSupervisor() throws Exception {\n        final SUser user = createSUser(\"zé\", \"bpm\");\n        final SProcessSupervisor createdSupervisor = createUserSupervisors(Collections.singletonList(user)).get(0);\n        final SProcessSupervisor gotSupervisor = getSSupervisor(createdSupervisor.getId());\n        Assert.assertEquals(createdSupervisor, gotSupervisor);\n\n        deleteSupervisor(createdSupervisor);\n        deleteSUser(user);\n    }\n\n    @Test\n    public void testCreateRoleSupervisor() throws Exception {\n        final SRole role = createSRole(\"role1\");\n        final SProcessSupervisor createdSupervisor = createRoleSupervisors(Collections.singletonList(role)).get(0);\n        final SProcessSupervisor gotSupervisor = getSSupervisor(createdSupervisor.getId());\n        Assert.assertEquals(createdSupervisor, gotSupervisor);\n\n        deleteSupervisor(createdSupervisor);\n        deleteSRole(role);\n    }\n\n    private SGroup createSGroup(final String groupName) throws SBonitaException {\n        this.transactionService.begin();\n        final SGroup group = SGroup.builder().name(groupName).build();\n        this.identityService.createGroup(group, null, null);\n        this.transactionService.complete();\n        return group;\n    }\n\n    @Test\n    public void testCreateGroupSupervisor() throws Exception {\n        final SGroup group = createSGroup(\"group1\");\n        final SProcessSupervisor createdSupervisor = createGroupSupervisors(Collections.singletonList(group)).get(0);\n        final SProcessSupervisor gotSupervisor = getSSupervisor(createdSupervisor.getId());\n        Assert.assertEquals(createdSupervisor, gotSupervisor);\n\n        deleteSupervisor(createdSupervisor);\n        deleteSGroup(group);\n    }\n\n    @Test\n    public void testCreateMembershipSupervisor() throws Exception {\n        final SRole role = createSRole(\"role1\");\n        final SGroup group = createSGroup(\"group1\");\n        final SProcessSupervisor createdSupervisor = createRoleAndGroupSupervisors(\n                Collections.singletonMap(role.getId(), group.getId())).get(0);\n        final SProcessSupervisor gotSupervisor = getSSupervisor(createdSupervisor.getId());\n        Assert.assertEquals(createdSupervisor, gotSupervisor);\n        // clean-up\n        deleteSupervisor(gotSupervisor);\n        deleteSGroup(group);\n        deleteSRole(role);\n    }\n\n    // FIXME with hibernate the exception arrives only when transaction.complete is called,\n    // with mybatis the exception arrives before calling transaction.complete\n    // we don't get the same exception. todo: investigate this and not use generic exception\n    @Test(expected = Exception.class)\n    public void testCreateSupervisorWithSSupervisorCreationException() throws Exception {\n        final SUser user = createSUser(\"zeca\", \"bpm\");\n        final SProcessSupervisor createdSupervisor = createUserSupervisors(Collections.singletonList(user)).get(0);\n        try {\n            createUserSupervisors(Collections.singletonList(user));\n        } finally {\n            // clean-up\n            deleteSupervisor(createdSupervisor);\n            deleteSUser(user);\n        }\n    }\n\n    @Test(expected = SSupervisorNotFoundException.class)\n    public void testGetSupervisorThrowException() throws Exception {\n        this.transactionService.begin();\n        supervisorService.getProcessSupervisor(-1);\n        this.transactionService.complete();\n    }\n\n    @Test(expected = SSupervisorNotFoundException.class)\n    public void testDeleteSupervisor() throws Exception {\n        final SUser user = createSUser(\"zeca\", \"bpm\");\n        final SProcessSupervisor createdSupervisor = createUserSupervisors(Collections.singletonList(user)).get(0);\n        this.transactionService.begin();\n        final SProcessSupervisor gotSupervisor = supervisorService.getProcessSupervisor(createdSupervisor.getId());\n        Assert.assertEquals(createdSupervisor, gotSupervisor);\n        supervisorService.deleteProcessSupervisor(gotSupervisor.getId());\n        try {\n            supervisorService.getProcessSupervisor(createdSupervisor.getId());\n            Assert.fail(\"supervisor not deleted successfully!\");\n        } finally {\n            this.transactionService.complete();\n            deleteSUser(user);\n        }\n    }\n\n    @Test(expected = SSupervisorNotFoundException.class)\n    public void testDeleteSupervisorThrowException() throws Exception {\n        this.transactionService.begin();\n        supervisorService.deleteProcessSupervisor(-1);\n        this.transactionService.complete();\n    }\n\n    private List<SProcessSupervisor> createUserSupervisors(final List<SUser> users) throws Exception {\n        final List<SProcessSupervisor> supervisorList = new ArrayList<>();\n        for (final SUser sUser : users) {\n            final SProcessSupervisor supervisor = SProcessSupervisor.builder().processDefId(this.processDefId)\n                    .userId(sUser.getId()).build();\n            final SProcessSupervisor createdSupervisor = createSupervisor(supervisor);\n            supervisorList.add(createdSupervisor);\n        }\n        return supervisorList;\n    }\n\n    private SProcessSupervisor createSupervisor(final SProcessSupervisor supervisor) throws SBonitaException {\n        this.transactionService.begin();\n        SProcessSupervisor createdSupervisor;\n        try {\n            createdSupervisor = supervisorService.createProcessSupervisor(supervisor);\n        } finally {\n            this.transactionService.complete();\n        }\n        return createdSupervisor;\n    }\n\n    private List<SProcessSupervisor> createRoleSupervisors(final List<SRole> roles) throws Exception {\n        final List<SProcessSupervisor> supervisorList = new ArrayList<>();\n        this.transactionService.begin();\n        for (final SRole sRole : roles) {\n            final SProcessSupervisor supervisor = SProcessSupervisor.builder().processDefId(this.processDefId)\n                    .roleId(sRole.getId()).build();\n            final SProcessSupervisor createdSupervisor = supervisorService.createProcessSupervisor(supervisor);\n            supervisorList.add(createdSupervisor);\n        }\n        this.transactionService.complete();\n        return supervisorList;\n    }\n\n    private List<SProcessSupervisor> createGroupSupervisors(final List<SGroup> groups) throws Exception {\n        final List<SProcessSupervisor> supervisorList = new ArrayList<>();\n        this.transactionService.begin();\n        for (final SGroup sGroup : groups) {\n            final SProcessSupervisor supervisor = SProcessSupervisor.builder().processDefId(this.processDefId)\n                    .groupId(sGroup.getId()).build();\n            final SProcessSupervisor createdSupervisor = supervisorService.createProcessSupervisor(supervisor);\n            supervisorList.add(createdSupervisor);\n        }\n        this.transactionService.complete();\n        return supervisorList;\n    }\n\n    private List<SProcessSupervisor> createRoleAndGroupSupervisors(final Map<Long, Long> roleGroupMap)\n            throws Exception {\n        final List<SProcessSupervisor> supervisorList = new ArrayList<>();\n        this.transactionService.begin();\n        for (final Entry<Long, Long> roleGroup : roleGroupMap.entrySet()) {\n            final SProcessSupervisor supervisor = SProcessSupervisor.builder().processDefId(this.processDefId)\n                    .roleId(roleGroup.getKey())\n                    .groupId(roleGroup.getValue()).build();\n            final SProcessSupervisor createdSupervisor = supervisorService.createProcessSupervisor(supervisor);\n            supervisorList.add(createdSupervisor);\n        }\n        this.transactionService.complete();\n        return supervisorList;\n    }\n\n    @Test\n    public void testSearchProcessDefSupervisorsInOrder() throws Exception {\n        final List<SUser> users = new ArrayList<>(5);\n        users.add(createSUser(\"roberto\", \"bpm\"));\n        users.add(createSUser(\"joao\", \"bpm\"));\n        users.add(createSUser(\"maria\", \"bpm\"));\n        users.add(createSUser(\"paula\", \"bpm\"));\n        users.add(createSUser(\"julia\", \"bpm\"));\n        final List<SProcessSupervisor> createdSupervisorList = createUserSupervisors(users);\n        assertEquals(5, createdSupervisorList.size());\n        this.transactionService.begin();\n        final List<OrderByOption> orderByOptions = Collections\n                .singletonList(new OrderByOption(SProcessSupervisor.class, SProcessSupervisor.USER_ID_KEY,\n                        OrderByType.DESC));\n        final List<FilterOption> filterOptions = Collections\n                .singletonList(new FilterOption(SProcessSupervisor.class, SProcessSupervisor.PROCESS_DEF_ID_KEY,\n                        this.processDefId));\n\n        // test ASC\n        final QueryOptions searchOptions = new QueryOptions(0, 6, orderByOptions, filterOptions, null);\n        final List<SProcessSupervisor> gotSupervisorList1 = supervisorService.searchProcessSupervisors(searchOptions);\n        assertEquals(5, gotSupervisorList1.size());\n        assertEquals(createdSupervisorList.get(4).getId(), gotSupervisorList1.get(0).getId());\n        assertEquals(createdSupervisorList.get(3).getId(), gotSupervisorList1.get(1).getId());\n        assertEquals(createdSupervisorList.get(2).getId(), gotSupervisorList1.get(2).getId());\n        assertEquals(createdSupervisorList.get(1).getId(), gotSupervisorList1.get(3).getId());\n        assertEquals(createdSupervisorList.get(0).getId(), gotSupervisorList1.get(4).getId());\n\n        for (final SProcessSupervisor supervisor : createdSupervisorList) {\n            supervisorService.deleteProcessSupervisor(supervisor.getId());\n        }\n        this.transactionService.complete();\n\n        deleteSUsers(users);\n    }\n\n    private boolean isUserProcessSupervisor(final SUser user) throws SBonitaException {\n        this.transactionService.begin();\n        final boolean isUserSupervisor = supervisorService.isProcessSupervisor(this.processDefId, user.getId());\n        this.transactionService.complete();\n        return isUserSupervisor;\n    }\n\n    @Test\n    public void testIsUserProcessSupervisor() throws Exception {\n        final SUser user = createSUser(\"paula\", \"bpm\");\n        assertFalse(isUserProcessSupervisor(user));\n        final SProcessSupervisor userSupervisor = createUserSupervisors(Collections.singletonList(user)).get(0);\n        assertTrue(isUserProcessSupervisor(user));\n\n        deleteSupervisor(userSupervisor);\n        deleteSUser(user);\n    }\n\n    @Test\n    public void testIsUserProcessSupervisorFromGroup() throws Exception {\n        final SUser user = createSUser(\"paula\", \"bpm\");\n        final SGroup group = createSGroup(\"group1\");\n        final SRole role = createSRole(\"role1\");\n\n        createSUserMembership(user, group, role);\n\n        assertFalse(isUserProcessSupervisor(user));\n        final SProcessSupervisor supervisor = createGroupSupervisors(Collections.singletonList(group)).get(0);\n        assertTrue(isUserProcessSupervisor(user));\n\n        deleteSupervisor(supervisor);\n        deleteSUser(user);\n        deleteSGroup(group);\n        deleteSRole(role);\n    }\n\n    @Test\n    public void testIsUserProcessSupervisorFromRole() throws Exception {\n        final SUser user = createSUser(\"paula\", \"bpm\");\n        final SGroup group = createSGroup(\"group1\");\n        final SRole role = createSRole(\"role1\");\n\n        createSUserMembership(user, group, role);\n\n        assertFalse(isUserProcessSupervisor(user));\n        final SProcessSupervisor supervisor = createRoleSupervisors(Collections.singletonList(role)).get(0);\n        assertTrue(isUserProcessSupervisor(user));\n\n        deleteSupervisor(supervisor);\n        deleteSUser(user);\n        deleteSGroup(group);\n        deleteSRole(role);\n    }\n\n    @Test\n    public void testIsUserProcessSupervisorFromRoleAndGroup() throws Exception {\n        final SUser user1 = createSUser(\"paula\", \"bpm\");\n        final SUser user2 = createSUser(\"julia\", \"bpm\");\n        final SGroup group1 = createSGroup(\"group1\");\n        final SGroup group2 = createSGroup(\"group2\");\n        final SRole role1 = createSRole(\"role1\");\n        final SRole role2 = createSRole(\"role2\");\n\n        createSUserMembership(user1, group1, role1);\n        createSUserMembership(user2, group1, role2);\n        createSUserMembership(user2, group2, role1);\n\n        assertFalse(isUserProcessSupervisor(user1));\n        assertFalse(isUserProcessSupervisor(user2));\n        final SProcessSupervisor supervisor = createRoleAndGroupSupervisors(\n                Collections.singletonMap(role1.getId(), group1.getId())).get(0);\n        assertTrue(isUserProcessSupervisor(user1));\n        assertFalse(isUserProcessSupervisor(user2));\n\n        deleteSupervisor(supervisor);\n        deleteSUsers(user1, user2);\n        deleteSGroups(group1, group2);\n        deleteSRoles(role1, role2);\n    }\n\n    private SProcessSupervisor getSSupervisor(final long supervisorId) throws SBonitaException {\n        this.transactionService.begin();\n        final SProcessSupervisor supervisor = supervisorService.getProcessSupervisor(supervisorId);\n        this.transactionService.complete();\n        return supervisor;\n    }\n\n    private void deleteSupervisor(final SProcessSupervisor supervisor) throws SBonitaException {\n        this.transactionService.begin();\n        supervisorService.deleteProcessSupervisor(supervisor);\n        this.transactionService.complete();\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/connector/ConnectorExecutionsLocalIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.connector;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.*;\n\nimport java.io.IOException;\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.commons.io.IOUtils;\nimport org.bonitasoft.engine.archive.model.TestLogBuilder;\nimport org.bonitasoft.engine.bpm.bar.BarResource;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.TimerType;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.FlowElementContainerDefinitionImpl;\nimport org.bonitasoft.engine.bpm.process.ConfigurationState;\nimport org.bonitasoft.engine.bpm.process.Problem;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.AutomaticTaskDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder;\nimport org.bonitasoft.engine.cache.CacheService;\nimport org.bonitasoft.engine.connectors.ConnectorExecutionIT;\nimport org.bonitasoft.engine.connectors.TestConnector;\nimport org.bonitasoft.engine.connectors.TestConnector3;\nimport org.bonitasoft.engine.connectors.VariableStorage;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.expression.ExpressionConstants;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.operation.LeftOperand;\nimport org.bonitasoft.engine.operation.OperationBuilder;\nimport org.bonitasoft.engine.operation.OperatorType;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.ServiceAccessorSingleton;\nimport org.bonitasoft.engine.service.impl.ServiceAccessorFactory;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.bonitasoft.engine.test.BlockingConnector;\nimport org.bonitasoft.engine.test.BuildTestUtil;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.bonitasoft.engine.test.WaitUntil;\nimport org.junit.Test;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n * @author Elias Ricken de Medeiros\n */\n@SuppressWarnings(\"javadoc\")\npublic class ConnectorExecutionsLocalIT extends ConnectorExecutionIT {\n\n    protected ServiceAccessor getServiceAccessor() {\n        try {\n            return ServiceAccessorSingleton.getInstance();\n        } catch (final Exception e) {\n            throw new BonitaRuntimeException(e);\n        }\n    }\n\n    @Test\n    public void executeConnectorOnFinishOfAnAutomaticActivityWithDataAsInput() throws Exception {\n        final String valueOfInput1 = \"valueOfInput1\";\n        final String dataName = \"myData1\";\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder().createNewInstance(\n                \"executeConnectorOnFinishOfAnAutomaticActivityWithDataAsInput\", \"1.0\");\n        designProcessDefinition.addShortTextData(dataName,\n                new ExpressionBuilder().createConstantStringExpression(valueOfInput1));\n        designProcessDefinition.addActor(ACTOR_NAME);\n        designProcessDefinition.addAutomaticTask(\"step1\")\n                .addConnector(\"myConnector\", \"org.bonitasoft.connector.testConnector\", \"1.0\", ConnectorEvent.ON_FINISH)\n                .addInput(TestConnector.INPUT1,\n                        new ExpressionBuilder().createDataExpression(dataName, String.class.getName()));\n        designProcessDefinition.addUserTask(\"step2\", ACTOR_NAME);\n        designProcessDefinition.addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector(designProcessDefinition,\n                ACTOR_NAME, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(processInstance, \"step2\");\n        waitForVariableStorage(TestConnector.INPUT1, valueOfInput1);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeSeveralConnectorsOnUserTaskOnStart() throws Exception {\n        final String valueOfInput1 = \"valueOfInput1\";\n        final String valueOfInput2 = \"valueOfInput2\";\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"executeSeveralConnectorsOnUserTaskOnStart\", \"1.0\");\n        processBuilder.addActor(ACTOR_NAME);\n\n        final UserTaskDefinitionBuilder taskBuilder = new UserTaskDefinitionBuilder(processBuilder,\n                (FlowElementContainerDefinitionImpl) processBuilder\n                        .getProcess().getProcessContainer(),\n                \"step1\", ACTOR_NAME);\n        taskBuilder\n                .addConnector(\"myConnector1\", \"org.bonitasoft.connector.testConnector3\", \"1.0\", ConnectorEvent.ON_ENTER)\n                .addInput(TestConnector3.INPUT1,\n                        new ExpressionBuilder().createConstantStringExpression(valueOfInput1));\n        taskBuilder\n                .addConnector(\"myConnector2\", \"org.bonitasoft.connector.testConnector3\", \"1.0\", ConnectorEvent.ON_ENTER)\n                .addInput(TestConnector3.INPUT2,\n                        new ExpressionBuilder().createConstantStringExpression(valueOfInput2));\n\n        processBuilder.addUserTask(\"step2\", ACTOR_NAME).addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector3(processBuilder, ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n        waitForVariableStorage(TestConnector3.INPUT1, valueOfInput1);\n        waitForVariableStorage(TestConnector3.INPUT2, valueOfInput2);\n        assignAndExecuteStep(step1Id, user);\n\n        waitForUserTaskAndExecuteIt(processInstance, \"step2\", user);\n\n        waitForProcessToFinish(processInstance);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeSeveralConnectorsOnAutomaticTaskOnStart() throws Exception {\n        final String valueOfInput1 = \"valueOfInput1\";\n        final String valueOfInput2 = \"valueOfInput2\";\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(\n                \"executeSeveralConnectorsOnAutomaticTaskOnStart\",\n                \"1.0\");\n        processBuilder.addActor(ACTOR_NAME);\n        final AutomaticTaskDefinitionBuilder taskBuilder = new AutomaticTaskDefinitionBuilder(processBuilder,\n                (FlowElementContainerDefinitionImpl) processBuilder.getProcess().getProcessContainer(), \"step1\");\n        taskBuilder\n                .addConnector(\"myConnector1\", \"org.bonitasoft.connector.testConnector3\", \"1.0\", ConnectorEvent.ON_ENTER)\n                .addInput(TestConnector3.INPUT1,\n                        new ExpressionBuilder().createConstantStringExpression(valueOfInput1));\n        taskBuilder\n                .addConnector(\"myConnector2\", \"org.bonitasoft.connector.testConnector3\", \"1.0\", ConnectorEvent.ON_ENTER)\n                .addInput(TestConnector3.INPUT2,\n                        new ExpressionBuilder().createConstantStringExpression(valueOfInput2));\n        processBuilder.addAutomaticTask(\"step2\").addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector3(processBuilder, ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        waitForVariableStorage(TestConnector3.INPUT1, valueOfInput1);\n        waitForVariableStorage(TestConnector3.INPUT2, valueOfInput2);\n        waitForProcessToFinish(processInstance);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeSeveralConnectorsOnUserTaskOnFinish() throws Exception {\n        final String valueOfInput1 = \"valueOfInput1\";\n        final String valueOfInput2 = \"valueOfInput2\";\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"executeSeveralConnectorsOnUserTaskOnFinish\", \"1.0\");\n        processBuilder.addActor(ACTOR_NAME);\n        final UserTaskDefinitionBuilder taskBuilder = processBuilder.addUserTask(\"step1\", ACTOR_NAME);\n        taskBuilder.addConnector(\"myConnector1\", \"org.bonitasoft.connector.testConnector3\", \"1.0\",\n                ConnectorEvent.ON_FINISH).addInput(TestConnector3.INPUT1,\n                        new ExpressionBuilder().createConstantStringExpression(valueOfInput1));\n        taskBuilder.addConnector(\"myConnector2\", \"org.bonitasoft.connector.testConnector3\", \"1.0\",\n                ConnectorEvent.ON_FINISH).addInput(TestConnector3.INPUT2,\n                        new ExpressionBuilder().createConstantStringExpression(valueOfInput2));\n        processBuilder.addUserTask(\"step2\", ACTOR_NAME).addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector3(processBuilder, ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(processInstance, \"step1\", user);\n\n        final long step2Id = waitForUserTask(processInstance, \"step2\");\n        waitForVariableStorage(TestConnector3.INPUT1, valueOfInput1);\n        waitForVariableStorage(TestConnector3.INPUT2, valueOfInput2);\n        assignAndExecuteStep(step2Id, userId);\n\n        waitForProcessToFinish(processInstance);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeSeveralConnectorsOnAutomaticTaskOnFinish() throws Exception {\n        final String valueOfInput1 = \"valueOfInput1\";\n        final String valueOfInput2 = \"valueOfInput2\";\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(\n                \"executeSeveralConnectorsOnAutomaticTaskOnFinish\",\n                \"1.0\");\n        processBuilder.addActor(ACTOR_NAME);\n\n        final AutomaticTaskDefinitionBuilder taskBuilder = processBuilder.addAutomaticTask(\"step1\");\n        taskBuilder.addConnector(\"myConnector1\", \"org.bonitasoft.connector.testConnector3\", \"1.0\",\n                ConnectorEvent.ON_FINISH).addInput(\"input1\",\n                        new ExpressionBuilder().createConstantStringExpression(valueOfInput1));\n        taskBuilder.addConnector(\"myConnector2\", \"org.bonitasoft.connector.testConnector3\", \"1.0\",\n                ConnectorEvent.ON_FINISH).addInput(\"input2\",\n                        new ExpressionBuilder().createConstantStringExpression(valueOfInput2));\n\n        final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector3(processBuilder, ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        waitForVariableStorage(TestConnector3.INPUT1, valueOfInput1);\n        waitForVariableStorage(TestConnector3.INPUT2, valueOfInput2);\n        waitForProcessToFinish(processInstance);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeSeveralConnectorsOnStartAndOnFinishWithDataInput() throws Exception {\n        final String valueOfInput1 = \"valueOfInput1\";\n        final String valueOfInput2 = \"valueOfInput2\";\n        final String valueOfInput3 = \"valueOfInput3\";\n        final String valueOfInput4 = \"valueOfInput4\";\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"executeSeveralConnectorsOnStartAndOnFinish\", \"1.0\");\n        processBuilder.addActor(ACTOR_NAME);\n\n        final UserTaskDefinitionBuilder taskBuilder = processBuilder.addUserTask(\"step1\", ACTOR_NAME);\n        taskBuilder\n                .addConnector(\"myConnector1\", \"org.bonitasoft.connector.testConnector3\", \"1.0\", ConnectorEvent.ON_ENTER)\n                .addInput(TestConnector3.INPUT1,\n                        new ExpressionBuilder().createConstantStringExpression(valueOfInput1));\n        taskBuilder\n                .addConnector(\"myConnector2\", \"org.bonitasoft.connector.testConnector3\", \"1.0\", ConnectorEvent.ON_ENTER)\n                .addInput(TestConnector3.INPUT2,\n                        new ExpressionBuilder().createConstantStringExpression(valueOfInput2));\n        taskBuilder.addConnector(\"myConnector3\", \"org.bonitasoft.connector.testConnector3\", \"1.0\",\n                ConnectorEvent.ON_FINISH).addInput(TestConnector3.INPUT3,\n                        new ExpressionBuilder().createConstantStringExpression(valueOfInput3));\n        taskBuilder.addConnector(\"myConnector4\", \"org.bonitasoft.connector.testConnector3\", \"1.0\",\n                ConnectorEvent.ON_FINISH).addInput(TestConnector3.INPUT4,\n                        new ExpressionBuilder().createConstantStringExpression(valueOfInput4));\n\n        processBuilder.addUserTask(\"step2\", ACTOR_NAME).addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector3(processBuilder, ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n        waitForVariableStorage(TestConnector3.INPUT1, valueOfInput1);\n        waitForVariableStorage(TestConnector3.INPUT2, valueOfInput2);\n        assignAndExecuteStep(step1Id, userId);\n\n        final long step2Id = waitForUserTask(processInstance, \"step2\");\n        waitForVariableStorage(TestConnector3.INPUT1, valueOfInput1);\n        waitForVariableStorage(TestConnector3.INPUT2, valueOfInput2);\n        assignAndExecuteStep(step2Id, userId);\n\n        waitForProcessToFinish(processInstance);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeConnectorOnFinishOfAnAutomaticActivity() throws Exception {\n        final String valueOfInput1 = \"valueOfInput1\";\n\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance(\n                \"executeConnectorOnStartOfAnAutomaticActivity\", \"1.0\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addAutomaticTask(\"step1\")\n                .addConnector(\"myConnector\", \"org.bonitasoft.connector.testConnector\", \"1.0\", ConnectorEvent.ON_FINISH)\n                .addInput(TestConnector.INPUT1, new ExpressionBuilder().createConstantStringExpression(valueOfInput1));\n\n        final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector(processDefinitionBuilder,\n                ACTOR_NAME, user);\n        final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId());\n\n        waitForVariableStorage(TestConnector.INPUT1, valueOfInput1);\n        waitForProcessToFinish(startProcess);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeConnectorOnEnterOfAnUserTask() throws Exception {\n        final String valueOfInput1 = \"valueOfInput1\";\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(\n                \"executeConnectorOnStartOfAnUserActivity\", \"1.0\");\n        processBuilder.addActor(ACTOR_NAME);\n        processBuilder.addUserTask(\"step1\", ACTOR_NAME)\n                .addConnector(\"myConnector\", \"org.bonitasoft.connector.testConnector\", \"1.0\", ConnectorEvent.ON_ENTER)\n                .addInput(TestConnector.INPUT1, new ExpressionBuilder().createConstantStringExpression(valueOfInput1));\n\n        final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector(processBuilder, ACTOR_NAME,\n                user);\n        getProcessAPI().startProcess(processDefinition.getId());\n\n        waitForVariableStorage(TestConnector.INPUT1, valueOfInput1);\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void connectorsAreDeletedAfterTaskCompletion() throws Exception {\n        // deploy process\n        final String taskName = \"step1\";\n        final ProcessDefinition processDefinition = deployProcessWithConnectorOnUserTask(user, taskName);\n\n        // start the process\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        // wait for step containing the connector and execute it\n        final long step1Id = waitForUserTaskAndExecuteIt(processInstance, taskName, user);\n        waitForUserTask(processInstance, \"step2\");\n\n        // check that there are no more connector instances\n        final SearchResult<ConnectorInstance> searchResult = searchConnectors(step1Id, ConnectorInstance.FLOWNODE_TYPE,\n                10);\n        assertEquals(0, searchResult.getCount());\n\n        // clean up\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    private SearchResult<ConnectorInstance> searchConnectors(final long containerId, final String containerType,\n            final int maxResults) throws SearchException {\n        final SearchOptionsBuilder optionsBuilder = new SearchOptionsBuilder(0, maxResults);\n        optionsBuilder.filter(ConnectorInstancesSearchDescriptor.CONTAINER_ID, containerId);\n        optionsBuilder.filter(ConnectorInstancesSearchDescriptor.CONTAINER_TYPE, containerType);\n        return getProcessAPI().searchConnectorInstances(optionsBuilder.done());\n    }\n\n    private ProcessDefinition deployProcessWithConnectorOnUserTask(final User user, final String taskName)\n            throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(\n                \"executeConnectorOnStartOfAnAutomaticActivity\", \"1.0\");\n        processBuilder.addActor(ACTOR_NAME);\n        processBuilder.addUserTask(taskName, ACTOR_NAME)\n                .addConnector(\"myConnector\", \"org.bonitasoft.connector.testConnector\", \"1.0\", ConnectorEvent.ON_ENTER)\n                .addInput(\"input1\", new ExpressionBuilder().createConstantStringExpression(\"valueOfInput1\"));\n        processBuilder.addUserTask(\"step2\", ACTOR_NAME).addTransition(taskName, \"step2\");\n        return deployProcessWithActorAndTestConnector(processBuilder, ACTOR_NAME, user);\n    }\n\n    @Test\n    public void executeConnectorOnFinishOfAnUserTask() throws Exception {\n        final String valueOfInput1 = \"valueOfInput1\";\n\n        // Configure process and human tasks\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"executeConnectorOnFinishOfAnUserTask\", \"1.0\");\n        processBuilder.addActor(ACTOR_NAME);\n        processBuilder.addUserTask(\"step1\", ACTOR_NAME)\n                .addConnector(\"myConnector\", \"org.bonitasoft.connector.testConnector\", \"1.0\", ConnectorEvent.ON_FINISH)\n                .addInput(TestConnector.INPUT1, new ExpressionBuilder().createConstantStringExpression(valueOfInput1));\n        processBuilder.addUserTask(\"step2\", ACTOR_NAME);\n        processBuilder.addTransition(\"step1\", \"step2\");\n\n        // Deploy and start process\n        final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector(processBuilder, ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        // Assign human task with connector\n        final long step1Id = waitForUserTaskAndAssignIt(processInstance, \"step1\", user).getId();\n\n        // Check that the \"input1\" variable has no value for \"valueOfInput1\"\n        final WaitUntil waitUntil = waitForVariableStorage(50, 800, TestConnector.INPUT1, valueOfInput1);\n        assertFalse(waitUntil.waitUntil());\n\n        // Run Started state of the human task\n        getProcessAPI().executeFlowNode(step1Id);\n        waitForArchivedActivity(step1Id, TestStates.NORMAL_FINAL);\n\n        // Check that the \"input1\" variable has value for \"valueOfInput1\", in Started state of human task\n        waitForVariableStorage(TestConnector.INPUT1, valueOfInput1);\n\n        // Remove all for the end\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeConnectorOnFinishStateOfAnUserTaskWithTimerEvent() throws Exception {\n        final String valueOfInput1 = \"valueOfInput1\";\n\n        // Configure process and human tasks\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(\n                \"executeConnectorOnFinishStateOfAnUserTaskWithTimerEvent\", \"1.0\");\n        processBuilder.addActor(ACTOR_NAME);\n        final UserTaskDefinitionBuilder userTaskDefinitionBuilder = processBuilder.addUserTask(\"step1\", ACTOR_NAME);\n        userTaskDefinitionBuilder\n                .addConnector(\"myConnector\", \"org.bonitasoft.connector.testConnector\", \"1.0\", ConnectorEvent.ON_FINISH)\n                .addInput(\n                        TestConnector.INPUT1, new ExpressionBuilder().createConstantStringExpression(valueOfInput1));\n        processBuilder.addStartEvent(\"start\");\n        userTaskDefinitionBuilder.addBoundaryEvent(\"timer\", true).addTimerEventTriggerDefinition(TimerType.DURATION,\n                new ExpressionBuilder().createConstantLongExpression(30000));\n        userTaskDefinitionBuilder.addUserTask(\"exceptionStep\", ACTOR_NAME);\n        processBuilder.addEndEvent(\"end\");\n        processBuilder.addUserTask(\"step2\", ACTOR_NAME);\n        processBuilder.addTransition(\"start\", \"step1\");\n        processBuilder.addTransition(\"step1\", \"step2\");\n        processBuilder.addTransition(\"step2\", \"end\");\n        processBuilder.addTransition(\"timer\", \"exceptionStep\");\n\n        // Deploy and start process\n        final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector(processBuilder, ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        // Assign human task with connector\n        final ActivityInstance step1 = waitForUserTaskAndAssignIt(processInstance, \"step1\", user);\n\n        // Check that the \"input1\" variable has no value for \"valueOfInput1\", in Ready state of human task\n        final WaitUntil waitUntil = waitForVariableStorage(50, 800, TestConnector.INPUT1, valueOfInput1);\n        assertFalse(waitUntil.waitUntil());\n\n        // Run Started state of the human task\n        getProcessAPI().executeFlowNode(step1.getId());\n        waitForUserTask(processInstance, \"step2\");\n\n        // Check that the \"input1\" variable has value for \"valueOfInput1\", in Started state of human task\n        waitForVariableStorage(TestConnector.INPUT1, valueOfInput1);\n\n        // Remove all for the end\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    private WaitUntil waitForVariableStorage(final int repeatEach, final int timeout, final String inputName,\n            final String valueOfInput) {\n        final WaitUntil waitUntil = new WaitUntil(repeatEach, timeout, false) {\n\n            @Override\n            protected boolean check() {\n                return VariableStorage.getInstance().getVariableValue(inputName).equals(valueOfInput);\n            }\n        };\n        return waitUntil;\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    private void waitForVariableStorage(final String inputName, final String valueOfInput) throws Exception {\n        final WaitUntil waitUntil = waitForVariableStorage(50, 5000, inputName, valueOfInput);\n        assertTrue(waitUntil.waitUntil());\n    }\n\n    @Test\n    public void executeConnectorOnEnterOfProcess() throws Exception {\n        final String valueOfInput1 = \"valueOfInput1\";\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(\n                \"executeConnectorOnStartOfAnAutomaticActivity\", \"1.0\");\n        processBuilder.addActor(ACTOR_NAME);\n        processBuilder\n                .addConnector(\"myConnector\", \"org.bonitasoft.connector.testConnector\", \"1.0\", ConnectorEvent.ON_ENTER)\n                .addInput(TestConnector.INPUT1,\n                        new ExpressionBuilder().createConstantStringExpression(valueOfInput1));\n        processBuilder.addAutomaticTask(\"step1\");\n        processBuilder.addUserTask(\"step2\", ACTOR_NAME);\n        processBuilder.addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector(processBuilder, ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForVariableStorage(TestConnector.INPUT1, valueOfInput1);\n        waitForUserTask(processInstance, \"step2\");\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeConnectorOnFinishOfAProcess() throws Exception {\n        final String valueOfInput = \"valueOfInput1\";\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(\n                \"executeConnectorOnStartOfAnAutomaticActivity\",\n                PROCESS_VERSION);\n        processBuilder.addActor(ACTOR_NAME);\n        processBuilder\n                .addConnector(\"myConnector\", \"org.bonitasoft.connector.testConnector\", \"1.0\", ConnectorEvent.ON_FINISH)\n                .addInput(TestConnector.INPUT1,\n                        new ExpressionBuilder().createConstantStringExpression(valueOfInput));\n        processBuilder.addUserTask(\"step1\", ACTOR_NAME);\n        final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector(processBuilder, ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n        final WaitUntil waitUntil = waitForVariableStorage(50, 800, TestConnector.INPUT1, valueOfInput);\n        assertFalse(waitUntil.waitUntil());\n\n        assignAndExecuteStep(step1Id, userId);\n        waitForVariableStorage(TestConnector.INPUT1, valueOfInput);\n\n        // Clean up\n        waitForProcessToFinish(processInstance);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void connectorsAreDeletedAfterProcessCompletion() throws Exception {\n        // deploy the a process with a connector\n        final String taskName = \"step1\";\n        final ProcessDefinition processDefinition = deployProcessWithConnectorOnFinish(user, taskName);\n\n        // execute the process\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTaskAndExecuteIt(processInstance, taskName, user);\n        waitForProcessToFinish(processInstance);\n\n        // check there are no connector instances\n        final SearchResult<ConnectorInstance> searchResult = searchConnectors(processInstance.getId(),\n                ConnectorInstance.PROCESS_TYPE, 10);\n        assertEquals(0, searchResult.getCount());\n\n        // clean up\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    private ProcessDefinition deployProcessWithConnectorOnFinish(final User user, final String taskName)\n            throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(\n                \"executeConnectorOnStartOfAnAutomaticActivity\", \"1.0\");\n        processBuilder.addActor(ACTOR_NAME);\n        processBuilder\n                .addConnector(\"myConnector\", \"org.bonitasoft.connector.testConnector\", \"1.0\", ConnectorEvent.ON_FINISH)\n                .addInput(\"input1\",\n                        new ExpressionBuilder().createConstantStringExpression(\"valueOfInput1\"));\n        processBuilder.addUserTask(taskName, ACTOR_NAME);\n        return deployProcessWithActorAndTestConnector(processBuilder, ACTOR_NAME, user);\n    }\n\n    @Test\n    public void executeConnectorOnEnterOfAnAutomaticActivity() throws Exception {\n        final String valueOfInput1 = \"valueOfInput1\";\n\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"executeConnectorOnStartOfAnAutomaticActivity\", \"1.0\");\n        processBuilder.addActor(ACTOR_NAME);\n        processBuilder.addAutomaticTask(\"step1\")\n                .addConnector(\"myConnector\", \"org.bonitasoft.connector.testConnector\", \"1.0\", ConnectorEvent.ON_ENTER)\n                .addInput(TestConnector.INPUT1, new ExpressionBuilder().createConstantStringExpression(valueOfInput1));\n        processBuilder.addUserTask(\"step2\", ACTOR_NAME);\n        processBuilder.addTransition(\"step1\", \"step2\");\n\n        final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector(processBuilder, ACTOR_NAME,\n                user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        waitForVariableStorage(TestConnector.INPUT1, valueOfInput1);\n        waitForUserTask(processInstance, \"step2\");\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeMissingImplConnectorOnProcessInstance() throws Exception {\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"executeConnectorOnActivityInstance\", \"1.0\");\n        designProcessDefinition.addActor(ACTOR_NAME).addUserTask(\"step1\", ACTOR_NAME)\n                .addConnector(\"UnkownConnector\", \"unkownConnectorId\", \"1.0.0\", ConnectorEvent.ON_ENTER);\n        final ProcessDefinition processDefinition = getProcessAPI().deploy(\n                new BusinessArchiveBuilder().createNewBusinessArchive()\n                        .setProcessDefinition(designProcessDefinition.done()).done());\n        getProcessAPI().addUserToActor(ACTOR_NAME, processDefinition, userId);\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ConfigurationState.UNRESOLVED, processDeploymentInfo.getConfigurationState());\n        final List<Problem> processResolutionProblems = getProcessAPI()\n                .getProcessResolutionProblems(processDefinition.getId());\n        assertThat(processResolutionProblems).extracting(\"resource\").contains(\"connector\");\n\n        deleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeMissingClassConnectorOnProcessInstance() throws Exception {\n        final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"executeConnectorOnActivityInstance\", \"1.0\");\n        processDefBuilder.addActor(ACTOR_NAME).addUserTask(\"step1\", ACTOR_NAME)\n                .addConnector(\"UnkownClassConnector\", \"unkownClassConnectorDef\", \"1.0.0\", ConnectorEvent.ON_ENTER);\n        final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(\n                        processDefBuilder.done());\n        businessArchiveBuilder.addConnectorImplementation(\n                new BarResource(\"UnknownClassConnector.impl\", IOUtils.toByteArray(TestLogBuilder.class\n                        .getResourceAsStream(\"/org/bonitasoft/engine/connectors/UnknownClassConnector.impl\"))));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchiveBuilder.done(),\n                ACTOR_NAME, user);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        waitForFlowNodeInFailedState(processInstance, \"step1\");\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeConnectorWithCustomOutputTypeOnActivity() throws Exception {\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"testConnectorWithExecutionTooLong\", \"1.0\");\n        designProcessDefinition.addActor(ACTOR_NAME);\n        designProcessDefinition.addShortTextData(\"value\", null);\n        designProcessDefinition\n                .addAutomaticTask(\"step1\")\n                .addConnector(\"myConnector1\", \"connectorWithCustomType\", \"1.0.0\", ConnectorEvent.ON_ENTER)\n                .addOutput(\n                        new OperationBuilder()\n                                .createNewInstance()\n                                .setLeftOperand(\"value\", LeftOperand.TYPE_DATA)\n                                .setType(OperatorType.ASSIGNMENT)\n                                .setRightOperand(\n                                        new ExpressionBuilder().createGroovyScriptExpression(\"script\",\n                                                \"output.getValue()\", String.class.getName(),\n                                                new ExpressionBuilder().createInputExpression(\"output\",\n                                                        \"org.connector.custom.CustomType\")))\n                                .done());\n        designProcessDefinition.addUserTask(\"step2\", ACTOR_NAME);\n        designProcessDefinition.addTransition(\"step1\", \"step2\");\n\n        final List<BarResource> resources = new ArrayList<>();\n        addResource(resources, \"/org/bonitasoft/engine/connectors/TestConnectorWithCustomType.impl\",\n                \"TestConnectorWithCustomType.impl\");\n        addResource(resources, \"/org/bonitasoft/engine/connectors/connector-with-custom-type.bak\",\n                \"connector-with-custom-type.jar\");\n        final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive();\n        businessArchiveBuilder.addConnectorImplementation(resources.get(0));\n        businessArchiveBuilder.addClasspathResource(resources.get(1));\n        businessArchiveBuilder.setProcessDefinition(designProcessDefinition.done());\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchiveBuilder.done(),\n                ACTOR_NAME, user);\n\n        final ProcessInstance process = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(process, \"step2\");\n        assertEquals(\"value\", getProcessAPI().getProcessDataInstance(\"value\", process.getId()).getValue());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeConnectorWithCustomOutputTypeOnProcess() throws Exception {\n        final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"testConnectorWithExecutionTooLong\", \"1.0\");\n        designProcessDefinition.addActor(ACTOR_NAME);\n        designProcessDefinition.addShortTextData(\"value\", null);\n        designProcessDefinition\n                .addConnector(\"myConnector1\", \"connectorWithCustomType\", \"1.0.0\", ConnectorEvent.ON_ENTER).addOutput(\n                        new OperationBuilder()\n                                .createNewInstance()\n                                .setLeftOperand(\"value\", LeftOperand.TYPE_DATA)\n                                .setType(OperatorType.ASSIGNMENT)\n                                .setRightOperand(\n                                        new ExpressionBuilder().createGroovyScriptExpression(\"script\",\n                                                \"output.getValue()\", String.class.getName(),\n                                                new ExpressionBuilder().createInputExpression(\"output\",\n                                                        \"org.connector.custom.CustomType\")))\n                                .done());\n        designProcessDefinition.addAutomaticTask(\"step1\");\n        designProcessDefinition.addUserTask(\"step2\", ACTOR_NAME);\n        designProcessDefinition.addTransition(\"step1\", \"step2\");\n\n        final List<BarResource> resources = new ArrayList<>();\n        addResource(resources, \"/org/bonitasoft/engine/connectors/TestConnectorWithCustomType.impl\",\n                \"TestConnectorWithCustomType.impl\");\n        addResource(resources, \"/org/bonitasoft/engine/connectors/connector-with-custom-type.bak\",\n                \"connector-with-custom-type.jar\");\n        final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive();\n        businessArchiveBuilder.addConnectorImplementation(resources.get(0));\n        businessArchiveBuilder.addClasspathResource(resources.get(1));\n        businessArchiveBuilder.setProcessDefinition(designProcessDefinition.done());\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchiveBuilder.done(),\n                ACTOR_NAME, user);\n        final ProcessInstance process = getProcessAPI().startProcess(processDefinition.getId());\n        waitForUserTask(process, \"step2\");\n        assertEquals(\"value\", getProcessAPI().getProcessDataInstance(\"value\", process.getId()).getValue());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void getConnectorImplementationWorksAfterCacheCleared() throws Exception {\n        final String valueOfInput1 = \"valueOfInput1\";\n\n        // Configure process and human tasks\n        final Expression input1Expression = new ExpressionBuilder().createConstantStringExpression(valueOfInput1);\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(\n                \"executeConnectorOnStartOfAnAutomaticActivity\", \"1.0\");\n        processBuilder.addActor(ACTOR_NAME);\n        processBuilder.addUserTask(\"step1\", ACTOR_NAME)\n                .addConnector(\"myConnector\", \"org.bonitasoft.connector.testConnector\", \"1.0\", ConnectorEvent.ON_FINISH)\n                .addInput(TestConnector.INPUT1, input1Expression);\n        processBuilder.addUserTask(\"step2\", ACTOR_NAME);\n        processBuilder.addTransition(\"step1\", \"step2\");\n\n        // Deploy and start process\n        final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector(processBuilder, ACTOR_NAME,\n                user);\n        assertTrue(getProcessAPI().getProcessResolutionProblems(processDefinition.getId()).isEmpty());\n\n        final SessionAccessor sessionAccessor = ServiceAccessorFactory.getInstance().createSessionAccessor();\n        sessionAccessor.setSessionId(getSession().getId());\n\n        final CacheService cacheservice = getServiceAccessor().getCacheService();\n        cacheservice.clearAll();\n        sessionAccessor.deleteSessionId();\n\n        assertTrue(getProcessAPI().getProcessResolutionProblems(processDefinition.getId()).isEmpty());\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeFailedNonSerializableOutputConnectorOnEnterOfAnAutomaticActivity() throws Exception {\n        executeFailedNonSerializableOutputConnectorOfAnAutomaticActivity(ConnectorEvent.ON_ENTER);\n    }\n\n    @Test\n    public void executeFailedNonSerializableOutputConnectorOnFinishOfAnAutomaticActivity() throws Exception {\n        executeFailedNonSerializableOutputConnectorOfAnAutomaticActivity(ConnectorEvent.ON_FINISH);\n    }\n\n    protected void executeFailedNonSerializableOutputConnectorOfAnAutomaticActivity(final ConnectorEvent connectorEvent)\n            throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"ProcessWithNonSerializableOutputConnector\", \"1.0\");\n        processBuilder.addStartEvent(\"start\").addEndEvent(\"end\");\n        processBuilder.addActor(ACTOR_NAME);\n        processBuilder.addData(\"resultProcess\", Object.class.getName(),\n                new ExpressionBuilder().createConstantLongExpression(0L));\n        final AutomaticTaskDefinitionBuilder autoTask = processBuilder.addAutomaticTask(\"Step1\");\n        processBuilder.addTransition(\"start\", \"end\");\n        autoTask.addConnector(\"nonSerializableFailedConnector\",\n                \"org.bonitasoft.connector.testConnectorWithNotSerializableOutput\", \"1.0\", connectorEvent)\n                .addOutput(\n                        new OperationBuilder().createNewInstance()\n                                .setLeftOperand(\"resultProcess\", LeftOperand.TYPE_DATA)\n                                .setType(OperatorType.ASSIGNMENT)\n                                .setRightOperand(new ExpressionBuilder().createInputExpression(\"output1\",\n                                        Object.class.getName()))\n                                .done());\n\n        final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorWithNotSerializableOutput(\n                processBuilder, ACTOR_NAME, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        waitForFlowNodeInFailedState(processInstance, \"Step1\");\n\n        // Clean-up\n        Thread.sleep(1000);\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void executeExpressionAtProcessInstantiationOnProcessInInitializing() throws Exception {\n        // will block the connector\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithBlockingConnector\", \"1.0\");\n        processBuilder.addConnector(\"myConnector\", \"blocking-connector\", \"1.0\", ConnectorEvent.ON_ENTER);\n        processBuilder.addData(\"a\", String.class.getName(),\n                new ExpressionBuilder().createConstantStringExpression(\"avalue\"));\n        processBuilder.addData(\"b\", String.class.getName(),\n                new ExpressionBuilder().createConstantStringExpression(\"bvalue\"));\n        processBuilder.addData(\"c\", String.class.getName(),\n                new ExpressionBuilder().createConstantStringExpression(\"cvalue\"));\n        final AutomaticTaskDefinitionBuilder addAutomaticTask = processBuilder.addAutomaticTask(\"step1\");\n        addAutomaticTask.addOperation(new OperationBuilder().createSetDataOperation(\"a\",\n                new ExpressionBuilder().createConstantStringExpression(\"changed\")));\n        addAutomaticTask.addOperation(new OperationBuilder().createSetDataOperation(\"b\",\n                new ExpressionBuilder().createConstantStringExpression(\"changed\")));\n        addAutomaticTask.addOperation(new OperationBuilder().createSetDataOperation(\"c\",\n                new ExpressionBuilder().createConstantStringExpression(\"changed\")));\n\n        // Add user task for confirmation form evaluation:\n        processBuilder.addActor(ACTOR_NAME);\n        final String userTaskName = \"userTaskWithOnFinishConnector\";\n        final UserTaskDefinitionBuilder userTaskDef = processBuilder.addUserTask(userTaskName, ACTOR_NAME);\n        userTaskDef.addConnector(\"myConnector2\", \"blocking-connector\", \"1.0\", ConnectorEvent.ON_FINISH);\n\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder()\n                .createNewBusinessArchive()\n                .setProcessDefinition(processBuilder.done())\n                .addConnectorImplementation(\n                        new BarResource(\"blocking-connector.impl\",\n                                BuildTestUtil.buildConnectorImplementationFile(\"blocking-connector\", \"1.0\",\n                                        \"blocking-connector-impl\",\n                                        \"1.0\", BlockingConnector.class.getName())))\n                .done();\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchive, ACTOR_NAME, user);\n\n        BlockingConnector.semaphore.acquire();\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final Map<Expression, Map<String, Serializable>> expressions = new HashMap<>(2);\n        expressions.put(\n                new ExpressionBuilder().createGroovyScriptExpression(\"ascripte\", \"a+b+c\", String.class.getName(),\n                        new ExpressionBuilder().createDataExpression(\"a\", String.class.getName()),\n                        new ExpressionBuilder().createDataExpression(\"b\", String.class.getName()),\n                        new ExpressionBuilder().createDataExpression(\"c\", String.class.getName())),\n                Collections.emptyMap());\n        expressions.put(new ExpressionBuilder().createDataExpression(\"a\", String.class.getName()),\n                Collections.emptyMap());\n        final Map<String, Serializable> evaluateExpressionsAtProcessInstantiation = getProcessAPI()\n                .evaluateExpressionsAtProcessInstanciation(\n                        processInstance.getId(), expressions);\n        assertEquals(\"avaluebvaluecvalue\", evaluateExpressionsAtProcessInstantiation.get(\"ascripte\"));\n        assertEquals(\"avalue\", evaluateExpressionsAtProcessInstantiation.get(\"a\"));\n\n        BlockingConnector.semaphore.release();\n\n        final long userTaskId = waitForUserTask(processInstance.getId(), userTaskName);\n        BlockingConnector.semaphore.acquire();\n        assignAndExecuteStep(userTaskId, userId);\n        // Try to evaluate expression on non-completed activity:\n        final Expression engineConstantExpr = new ExpressionBuilder()\n                .createEngineConstant(ExpressionConstants.PROCESS_INSTANCE_ID);\n        final Map<Expression, Map<String, Serializable>> exprToEvaluate = new HashMap<>(1);\n        exprToEvaluate.put(engineConstantExpr, Collections.emptyMap());\n        final Map<String, Serializable> evaluatedExpressions = getProcessAPI()\n                .evaluateExpressionsOnCompletedActivityInstance(userTaskId, exprToEvaluate);\n        assertEquals(processInstance.getId(), ((Long) evaluatedExpressions.get(\"processInstanceId\")).longValue());\n        // Release the connector for the user task to complete:\n        BlockingConnector.semaphore.release();\n\n        waitForProcessToFinish(processInstance.getId());\n        getProcessAPI().evaluateExpressionsAtProcessInstanciation(processInstance.getId(), expressions);\n        assertEquals(\"avaluebvaluecvalue\", evaluateExpressionsAtProcessInstantiation.get(\"ascripte\"));\n        assertEquals(\"avalue\", evaluateExpressionsAtProcessInstantiation.get(\"a\"));\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Override\n    public ProcessDefinition deployProcessWithActorAndTestConnector(\n            final ProcessDefinitionBuilder processDefinitionBuilder,\n            final String actorName, final User user) throws BonitaException, IOException {\n        return deployAndEnableProcessWithActorAndConnectorAndParameter(processDefinitionBuilder, actorName, user, null,\n                \"TestConnector.impl\", TestConnector.class, \"TestConnector.jar\");\n    }\n\n    @Override\n    public ProcessDefinition deployProcessWithActorAndTestConnector3(\n            final ProcessDefinitionBuilder processDefinitionBuilder,\n            final String actorName, final User user) throws BonitaException, IOException {\n        return deployAndEnableProcessWithActorAndConnectorAndParameter(processDefinitionBuilder, actorName, user, null,\n                \"TestConnector3.impl\", TestConnector3.class, \"TestConnector3.jar\");\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/connector/ConnectorInstanceServiceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.connector;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNull;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.CommonBPMServicesTest;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.connector.ConnectorInstanceService;\nimport org.bonitasoft.engine.core.connector.ConnectorService;\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstance;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Baptiste Mesta\n */\npublic class ConnectorInstanceServiceIT extends CommonBPMServicesTest {\n\n    private ConnectorInstanceService connectorInstanceService;\n\n    @Before\n    public void setUp() {\n        connectorInstanceService = getServiceAccessor().getConnectorInstanceService();\n    }\n\n    @After\n    public void tearDown() throws Exception {\n        cleanConnectorInstances();\n    }\n\n    private void cleanConnectorInstances() throws Exception {\n        getTransactionService().begin();\n        final QueryOptions queryOptions = new QueryOptions(0, 100, SConnectorInstance.class, \"id\", OrderByType.ASC);\n        List<SConnectorInstance> connetorInstances = null;\n        do {\n            connetorInstances = connectorInstanceService.searchConnectorInstances(queryOptions);\n            for (final SConnectorInstance connectorInstance : connetorInstances) {\n                connectorInstanceService.deleteConnectorInstance(connectorInstance);\n            }\n        } while (!connetorInstances.isEmpty());\n        getTransactionService().complete();\n    }\n\n    private SConnectorInstance createConnectorInTransaction(final String name, final long containerId,\n            final String containerType, final String connectorId,\n            final String version, final ConnectorEvent activationEvent, final int executionOrder)\n            throws SBonitaException {\n        final SConnectorInstance connectorInstance = SConnectorInstance.builder().name(name)\n                .containerId(containerId)\n                .containerType(containerType)\n                .connectorId(connectorId)\n                .version(version)\n                .activationEvent(activationEvent)\n                .state(ConnectorState.TO_BE_EXECUTED.name())\n                .executionOrder(executionOrder).build();\n        getTransactionService().begin();\n        connectorInstanceService.createConnectorInstance(connectorInstance);\n        getTransactionService().complete();\n        return connectorInstance;\n    }\n\n    private SConnectorInstance getNextInTransaction(final long containerId, final String containerType,\n            final ConnectorEvent activationEvent)\n            throws SBonitaException {\n        getTransactionService().begin();\n        final SConnectorInstance connectorInstance = connectorInstanceService.getNextExecutableConnectorInstance(\n                containerId, containerType,\n                activationEvent);\n        getTransactionService().complete();\n        return connectorInstance;\n    }\n\n    private void setStateIntransaction(final long connectorId, final String state) throws SBonitaException {\n        getTransactionService().begin();\n        final SConnectorInstance connectorInstance = connectorInstanceService.getConnectorInstance(connectorId);\n        connectorInstanceService.setState(connectorInstance, state);\n        getTransactionService().complete();\n    }\n\n    @Test\n    public void testGetNextExecutableConnectorInstance() throws Exception {\n        final long containerId = 1L;\n        final String containerType = SConnectorInstance.FLOWNODE_TYPE;\n        final ConnectorEvent activationEvent = ConnectorEvent.ON_ENTER;\n\n        // insert two connector instances\n        final SConnectorInstance connectorA = createConnectorInTransaction(\"a\", containerId, containerType,\n                \"myConnector-1\", \"1.0\", activationEvent, 1);\n        final SConnectorInstance connectorB = createConnectorInTransaction(\"b\", containerId, containerType,\n                \"myConnector-2\", \"1.0\", activationEvent, 2);\n\n        // check that the next connector to be executed is the first inserted\n        checkNextExecutableConnector(containerId, containerType, activationEvent, \"a\");\n\n        // mark the first connector as done: the next connector must be the second one\n        setStateIntransaction(connectorA.getId(), ConnectorService.DONE);\n        checkNextExecutableConnector(containerId, containerType, activationEvent, \"b\");\n\n        // mark the second connector as done: no more connectors are expected\n        setStateIntransaction(connectorB.getId(), ConnectorService.DONE);\n        assertNull(getNextInTransaction(containerId, containerType, activationEvent));\n    }\n\n    @Test\n    public void testGetNextExecutableConnectorInstanceWithFaillingConnectors() throws Exception {\n        final long containerId = 1L;\n        final String containerType = SConnectorInstance.FLOWNODE_TYPE;\n        final ConnectorEvent activationEvent = ConnectorEvent.ON_ENTER;\n\n        // insert two connector instances\n        final SConnectorInstance connectorA = createConnectorInTransaction(\"a\", containerId, containerType,\n                \"myConnector-1\", \"1.0\", activationEvent, 1);\n        createConnectorInTransaction(\"b\", containerId, containerType, \"myConnector-2\", \"1.0\", activationEvent, 2);\n\n        // check that the next connector to be executed is the first inserted\n        checkNextExecutableConnector(containerId, containerType, activationEvent, \"a\");\n\n        // put the first connector in the state to re execute: the next connector must remain the first connector\n        setStateIntransaction(connectorA.getId(), ConnectorService.TO_RE_EXECUTE);\n        checkNextExecutableConnector(containerId, containerType, activationEvent, \"a\");\n    }\n\n    private void checkNextExecutableConnector(final long containerId, final String containerType,\n            final ConnectorEvent activationEvent,\n            final String connectorName) throws SBonitaException {\n        final SConnectorInstance nextConnectorInstance = getNextInTransaction(containerId, containerType,\n                activationEvent);\n        assertEquals(connectorName, nextConnectorInstance.getName());\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/event/EventInstanceServiceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.event;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.concurrent.Callable;\n\nimport org.bonitasoft.engine.bpm.CommonBPMServicesTest;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SBoundaryEventInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SEndEventInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SStartEventInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingErrorEventBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingMessageEventBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingSignalEventBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SEndEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SIntermediateCatchEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SIntermediateThrowEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SStartEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class EventInstanceServiceIT extends CommonBPMServicesTest {\n\n    private UserTransactionService userTransactionService;\n\n    private EventInstanceService eventInstanceService;\n\n    @Before\n    public void setup() {\n        userTransactionService = getServiceAccessor().getUserTransactionService();\n        eventInstanceService = getServiceAccessor().getEventInstanceService();\n    }\n\n    private void checkStartEventInstance(final SEventInstance expectedEventInstance,\n            final SEventInstance actualEventInstance) {\n        assertTrue(actualEventInstance instanceof SStartEventInstance);\n        checkEventInstance(expectedEventInstance, actualEventInstance);\n    }\n\n    private void checkEndEventInstance(final SEventInstance expectedEventInstance,\n            final SEventInstance actualEventInstance) {\n        assertTrue(actualEventInstance instanceof SEndEventInstance);\n        checkEventInstance(expectedEventInstance, actualEventInstance);\n    }\n\n    private void checkIntermediateCatchEventInstance(final SEventInstance expectedEventInstance,\n            final SEventInstance actualEventInstance) {\n        assertTrue(actualEventInstance instanceof SIntermediateCatchEventInstance);\n        checkEventInstance(expectedEventInstance, actualEventInstance);\n    }\n\n    private void checkBoundaryEventInstance(final SEventInstance expectedEventInstance,\n            final SEventInstance actualEventInstance) {\n        assertTrue(actualEventInstance instanceof SBoundaryEventInstance);\n        final SBoundaryEventInstance expectedBoundary = (SBoundaryEventInstance) expectedEventInstance;\n        final SBoundaryEventInstance actualBoundary = (SBoundaryEventInstance) actualEventInstance;\n        assertEquals(expectedBoundary.getActivityInstanceId(), actualBoundary.getActivityInstanceId());\n        checkEventInstance(expectedEventInstance, actualEventInstance);\n    }\n\n    private void checkIntermediateThrowEventInstance(final SEventInstance expectedEventInstance,\n            final SEventInstance actualEventInstance) {\n        assertTrue(actualEventInstance instanceof SIntermediateThrowEventInstance);\n        checkEventInstance(expectedEventInstance, actualEventInstance);\n    }\n\n    private void checkEventInstance(final SEventInstance expectedEventInstance,\n            final SEventInstance actualEventInstance) {\n        final SEndEventInstanceBuilderFactory eventInstanceBuilderFact = BuilderFactory\n                .get(SEndEventInstanceBuilderFactory.class);\n        final int processDefinitionIndex = eventInstanceBuilderFact.getProcessDefinitionIndex();\n        final int processInstanceIndex = eventInstanceBuilderFact.getRootProcessInstanceIndex();\n\n        assertEquals(expectedEventInstance.getId(), actualEventInstance.getId());\n        assertEquals(expectedEventInstance.getName(), actualEventInstance.getName());\n        assertEquals(expectedEventInstance.getParentContainerId(), actualEventInstance.getParentContainerId());\n        assertEquals(expectedEventInstance.getStateId(), actualEventInstance.getStateId());\n        assertEquals(expectedEventInstance.getLogicalGroup(processDefinitionIndex),\n                actualEventInstance.getLogicalGroup(processDefinitionIndex));\n        assertEquals(expectedEventInstance.getLogicalGroup(processInstanceIndex),\n                actualEventInstance.getLogicalGroup(processInstanceIndex));\n    }\n\n    private List<SEventInstance> getEventInstances(final long processInstanceId, final int fromIndex,\n            final int maxResult) throws Exception {\n        return getEventInstances(processInstanceId, fromIndex, maxResult,\n                BuilderFactory.get(SStartEventInstanceBuilderFactory.class).getNameKey(),\n                OrderByType.ASC);\n    }\n\n    private List<SEventInstance> getEventInstances(final long processInstanceId, final int fromIndex,\n            final int maxResult, final String fieldName,\n            final OrderByType orderByType) throws Exception {\n        return userTransactionService.executeInTransaction(\n                () -> eventInstanceService.getEventInstances(processInstanceId, fromIndex, maxResult, fieldName,\n                        orderByType));\n    }\n\n    private List<SBoundaryEventInstance> getActivityBoundaryEventInstances(final long activityId, final int fromIndex,\n            final int maxResults) throws Exception {\n        return userTransactionService.executeInTransaction(\n                () -> eventInstanceService.getActivityBoundaryEventInstances(activityId, fromIndex, maxResults));\n    }\n\n    private void createWaitingEvent(final SWaitingEvent waitingEvent) throws Exception {\n        userTransactionService.executeInTransaction((Callable<Void>) () -> {\n            eventInstanceService.createWaitingEvent(waitingEvent);\n            return null;\n        });\n    }\n\n    protected void deleteWaitingEvents(final SProcessInstance processInstance) throws Exception {\n        userTransactionService.executeInTransaction((Callable<Void>) () -> {\n            eventInstanceService.deleteWaitingEvents(processInstance);\n            return null;\n        });\n    }\n\n    private SEventInstance createBoundaryEventInstance(final String eventName,\n            final long flowNodeDefinitionId, final long rootProcessInstanceId, final long processDefinitionId,\n            final long parentProcessInstanceId,\n            final long activityInstanceId, final boolean isInterrupting) throws SBonitaException {\n        final SEventInstance eventInstance = BuilderFactory\n                .get(SBoundaryEventInstanceBuilderFactory.class)\n                .createNewBoundaryEventInstance(eventName, isInterrupting, flowNodeDefinitionId,\n                        rootProcessInstanceId, parentProcessInstanceId, processDefinitionId, rootProcessInstanceId,\n                        parentProcessInstanceId, activityInstanceId)\n                .done();\n        createSEventInstance(eventInstance);\n        return eventInstance;\n    }\n\n    @Test\n    public void testCreateAndRetrieveStartEventInstanceFromRootContainer() throws Exception {\n        final SProcessInstance processInstance = createSProcessInstance();\n\n        List<SEventInstance> eventInstances = getEventInstances(processInstance.getId(), 0, 5);\n        assertTrue(eventInstances.isEmpty());\n\n        final SEventInstance startEventInstance = createSStartEventInstance(\"startEvent\", 1, processInstance.getId(), 5,\n                processInstance.getId());\n        eventInstances = getEventInstances(processInstance.getId(), 0, 5);\n\n        assertEquals(1, eventInstances.size());\n        checkStartEventInstance(startEventInstance, eventInstances.get(0));\n\n        deleteSProcessInstance(processInstance);\n\n        eventInstances = getEventInstances(processInstance.getId(), 0, 5);\n        assertTrue(eventInstances.isEmpty());\n    }\n\n    @Test\n    public void testCreateAndRetrieveEndEventInstance() throws Exception {\n        final SProcessInstance processInstance = createSProcessInstance();\n\n        List<SEventInstance> eventInstances = getEventInstances(processInstance.getId(), 0, 5);\n        assertTrue(eventInstances.isEmpty());\n\n        final SEventInstance eventInstance = createSEndEventInstance(\"EndEvent\", 1, processInstance.getId(), 5,\n                processInstance.getId());\n        eventInstances = getEventInstances(processInstance.getId(), 0, 5);\n\n        assertEquals(1, eventInstances.size());\n        checkEndEventInstance(eventInstance, eventInstances.get(0));\n\n        deleteSProcessInstance(processInstance);\n\n        eventInstances = getEventInstances(processInstance.getId(), 0, 5);\n        assertTrue(eventInstances.isEmpty());\n    }\n\n    @Test\n    public void testCreateAndRetrieveIntermediateCatchEventInstance() throws Exception {\n        final SProcessInstance processInstance = createSProcessInstance();\n\n        List<SEventInstance> eventInstances = getEventInstances(processInstance.getId(), 0, 5);\n        assertTrue(eventInstances.isEmpty());\n\n        final SEventInstance eventInstance = createSIntermediateCatchEventInstance(\"IntermediateCatchEvent\", 1,\n                processInstance.getId(),\n                5,\n                processInstance.getId());\n        eventInstances = getEventInstances(processInstance.getId(), 0, 5);\n\n        assertEquals(1, eventInstances.size());\n        checkIntermediateCatchEventInstance(eventInstance, eventInstances.get(0));\n\n        deleteSProcessInstance(processInstance);\n\n        eventInstances = getEventInstances(processInstance.getId(), 0, 5);\n        assertTrue(eventInstances.isEmpty());\n    }\n\n    @Test\n    public void testCreateAndRetrieveBoundaryEventInstance() throws Exception {\n        final SProcessInstance processInstance = createSProcessInstance();\n\n        List<SEventInstance> eventInstances = getEventInstances(processInstance.getId(), 0, 5);\n        assertTrue(eventInstances.isEmpty());\n\n        final int activityInstanceId = 10;\n        final SEventInstance eventInstance = createBoundaryEventInstance(\"BoundaryEvent\", 1, processInstance.getId(), 5,\n                processInstance.getId(), activityInstanceId, true);\n        eventInstances = getEventInstances(processInstance.getId(), 0, 5);\n\n        assertEquals(1, eventInstances.size());\n        checkBoundaryEventInstance(eventInstance, eventInstances.get(0));\n\n        deleteSProcessInstance(processInstance);\n\n        eventInstances = getEventInstances(processInstance.getId(), 0, 5);\n        assertTrue(eventInstances.isEmpty());\n    }\n\n    @Test\n    public void testGetActivityBoundaryEventInstances() throws Exception {\n        final SProcessInstance processInstance = createSProcessInstance();\n        final long processDefinitionId = 5;\n        final SActivityInstance automaticTaskInstance = createSAutomaticTaskInstance(\"auto1\", 1,\n                processInstance.getId(), processDefinitionId,\n                processInstance.getId());\n        final long activityInstanceId = automaticTaskInstance.getId();\n\n        List<SBoundaryEventInstance> boundaryEventInstances = getActivityBoundaryEventInstances(activityInstanceId, 0,\n                1);\n        assertTrue(boundaryEventInstances.isEmpty());\n\n        final SEventInstance eventInstance1 = createBoundaryEventInstance(\"BoundaryEvent1\", 2, processInstance.getId(),\n                processDefinitionId, processInstance.getId(), activityInstanceId, true);\n        final SEventInstance eventInstance2 = createBoundaryEventInstance(\"BoundaryEvent2\", 3, processInstance.getId(),\n                processDefinitionId, processInstance.getId(), activityInstanceId, true);\n\n        boundaryEventInstances = getActivityBoundaryEventInstances(activityInstanceId, 0, 3);\n        assertEquals(2, boundaryEventInstances.size());\n        checkBoundaryEventInstance(eventInstance1, boundaryEventInstances.get(0));\n        checkBoundaryEventInstance(eventInstance2, boundaryEventInstances.get(1));\n\n        deleteSProcessInstance(processInstance);\n\n        boundaryEventInstances = getActivityBoundaryEventInstances(activityInstanceId, 0, 1);\n        assertTrue(boundaryEventInstances.isEmpty());\n    }\n\n    @Test\n    public void testCreateAndRetrieveIntermediateThrowEventInstance() throws Exception {\n        final SProcessInstance processInstance = createSProcessInstance();\n\n        List<SEventInstance> eventInstances = getEventInstances(processInstance.getId(), 0, 5);\n        assertTrue(eventInstances.isEmpty());\n\n        final SEventInstance eventInstance = createSIntermediateThrowEventInstance(\"IntermediateThrowEvent\", 1,\n                processInstance.getId(),\n                5,\n                processInstance.getId());\n        eventInstances = getEventInstances(processInstance.getId(), 0, 5);\n\n        assertEquals(1, eventInstances.size());\n        checkIntermediateThrowEventInstance(eventInstance, eventInstances.get(0));\n\n        deleteSProcessInstance(processInstance);\n\n        eventInstances = getEventInstances(processInstance.getId(), 0, 5);\n        assertTrue(eventInstances.isEmpty());\n    }\n\n    @Test\n    public void testGetEventInstancesOrderByNameAsc() throws Exception {\n        final SProcessInstance processInstance = createSProcessInstance();\n\n        final SEventInstance eventInstance1 = createSEndEventInstance(\"EndEvent1\", 1, processInstance.getId(), 5,\n                processInstance.getId());\n        final SEventInstance eventInstance2 = createSEndEventInstance(\"EndEvent2\", 1, processInstance.getId(), 5,\n                processInstance.getId());\n        final List<SEventInstance> eventInstances = getEventInstances(processInstance.getId(), 0, 5,\n                BuilderFactory.get(SEndEventInstanceBuilderFactory.class)\n                        .getNameKey(),\n                OrderByType.ASC);\n\n        assertEquals(2, eventInstances.size());\n        checkEndEventInstance(eventInstance1, eventInstances.get(0));\n        checkEndEventInstance(eventInstance2, eventInstances.get(1));\n\n        deleteSProcessInstance(processInstance);\n    }\n\n    @Test\n    public void testGetEventInstancesOrderByNameDesc() throws Exception {\n        final SProcessInstance processInstance = createSProcessInstance();\n\n        final SEventInstance eventInstance1 = createSEndEventInstance(\"EndEvent1\", 1, processInstance.getId(), 5,\n                processInstance.getId());\n        final SEventInstance eventInstance2 = createSEndEventInstance(\"EndEvent2\", 1, processInstance.getId(), 5,\n                processInstance.getId());\n        final List<SEventInstance> eventInstances = getEventInstances(processInstance.getId(), 0, 5,\n                BuilderFactory.get(SEndEventInstanceBuilderFactory.class)\n                        .getNameKey(),\n                OrderByType.DESC);\n\n        assertEquals(2, eventInstances.size());\n        checkEndEventInstance(eventInstance2, eventInstances.get(0));\n        checkEndEventInstance(eventInstance1, eventInstances.get(1));\n\n        deleteSProcessInstance(processInstance);\n    }\n\n    private <T extends SWaitingEvent> List<T> searchWaitingEvents(final Class<T> clazz,\n            final QueryOptions searchOptions) throws Exception {\n        return getTransactionService()\n                .executeInTransaction(() -> eventInstanceService.searchWaitingEvents(clazz, searchOptions));\n    }\n\n    private long getNumberOfWaitingEvents(final Class<? extends SWaitingEvent> clazz, final QueryOptions countOptions)\n            throws Exception {\n        return getTransactionService()\n                .executeInTransaction(() -> eventInstanceService.getNumberOfWaitingEvents(clazz, countOptions));\n    }\n\n    @Test\n    public void testSearchWaitingEvents() throws Exception {\n        final SProcessInstance processInstance = createSProcessInstance();\n        final SWaitingErrorEventBuilderFactory waitingErrorEventBuilder = BuilderFactory\n                .get(SWaitingErrorEventBuilderFactory.class);\n\n        final SEventInstance eventInstance = createSIntermediateCatchEventInstance(\"intermediate\", 1,\n                processInstance.getId(), 5, processInstance.getId());\n\n        final Class<SWaitingEvent> waitingEventClass = SWaitingEvent.class;\n        final String processDefinitionIdKey = waitingErrorEventBuilder.getProcessDefinitionIdKey();\n        final String flowNodeInstanceIdKey = waitingErrorEventBuilder.getFlowNodeInstanceIdKey();\n        final long eventInstanceId = eventInstance.getId();\n        checkWaitingEvents(0, waitingEventClass, processDefinitionIdKey, flowNodeInstanceIdKey, eventInstanceId);\n\n        final SWaitingMessageEvent messageWaitingEvent = BuilderFactory.get(SWaitingMessageEventBuilderFactory.class)\n                .createNewWaitingMessageIntermediateEventInstance(5, processInstance.getId(), processInstance.getId(),\n                        eventInstanceId, \"m1\", processInstance.getName(), eventInstance.getFlowNodeDefinitionId(),\n                        eventInstance.getName())\n                .done();\n        createWaitingEvent(messageWaitingEvent);\n\n        final SWaitingSignalEvent waitingSignalEvent = BuilderFactory.get(SWaitingSignalEventBuilderFactory.class)\n                .createNewWaitingSignalIntermediateEventInstance(5, processInstance.getId(), processInstance.getId(),\n                        eventInstanceId, \"go\", processInstance.getName(), eventInstance.getFlowNodeDefinitionId(),\n                        eventInstance.getName())\n                .done();\n        createWaitingEvent(waitingSignalEvent);\n\n        // search with SWaitingEvent\n        checkWaitingEvents(2, waitingEventClass, processDefinitionIdKey, flowNodeInstanceIdKey, eventInstanceId);\n\n        // search with SWaitingMessageEvent, SWaitingSignalEvent\n        checkWaitingEvents(1, SWaitingMessageEvent.class, processDefinitionIdKey, flowNodeInstanceIdKey,\n                eventInstanceId);\n        checkWaitingEvents(1, SWaitingSignalEvent.class, processDefinitionIdKey, flowNodeInstanceIdKey,\n                eventInstanceId);\n\n        deleteWaitingEvents(processInstance);\n        deleteSProcessInstance(processInstance);\n\n        checkWaitingEvents(0, waitingEventClass, processDefinitionIdKey, flowNodeInstanceIdKey, eventInstanceId);\n    }\n\n    private void checkWaitingEvents(final int expectedNbOfWaitingEvents, final Class<? extends SWaitingEvent> clazz,\n            final String processDefinitionIdKey,\n            final String flowNodeInstanceIdKey, final long eventInstanceId) throws Exception {\n        final int maxResults = Math.max(expectedNbOfWaitingEvents + 1, 10);\n        final QueryOptions queryOptions = getQueryOptions(clazz, 0, maxResults, processDefinitionIdKey, OrderByType.ASC,\n                flowNodeInstanceIdKey, eventInstanceId);\n        final QueryOptions countOptions = getCountOptions(clazz, flowNodeInstanceIdKey, eventInstanceId);\n        final List<? extends SWaitingEvent> waitingErrorEvents = searchWaitingEvents(clazz, queryOptions);\n        final long numberOfWaitingErrorEvents = getNumberOfWaitingEvents(clazz, countOptions);\n        assertEquals(expectedNbOfWaitingEvents, numberOfWaitingErrorEvents);\n        assertEquals(expectedNbOfWaitingEvents, waitingErrorEvents.size());\n    }\n\n    private QueryOptions getQueryOptions(final Class<? extends PersistentObject> clazz, final int fromIndex,\n            final int maxResult, final String orderByField,\n            final OrderByType orderByType, final String filterKey, final Object filterValue) {\n        final OrderByOption orderByOption = new OrderByOption(clazz, orderByField, orderByType);\n        final FilterOption filterOption = new FilterOption(clazz, filterKey, filterValue);\n        final QueryOptions boundaryQueryOptions = new QueryOptions(fromIndex, maxResult,\n                Collections.singletonList(orderByOption),\n                Collections.singletonList(filterOption), null);\n        return boundaryQueryOptions;\n    }\n\n    private QueryOptions getCountOptions(final Class<? extends PersistentObject> clazz, final String filterKey,\n            final Object filterValue) {\n        final FilterOption filterOption = new FilterOption(clazz, filterKey, filterValue);\n        final List<OrderByOption> emptyOrderByOptions = Collections.emptyList();\n        final QueryOptions countOptions = new QueryOptions(0, 1, emptyOrderByOptions,\n                Collections.singletonList(filterOption), null);\n        return countOptions;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/event/LocalInterruptingTimerBoundaryEventIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.event;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstance;\nimport org.bonitasoft.engine.bpm.flownode.TimerType;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceCriterion;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder;\nimport org.bonitasoft.engine.event.AbstractEventIT;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.impl.ServiceAccessorFactory;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class LocalInterruptingTimerBoundaryEventIT extends AbstractEventIT {\n\n    private static final String TIMER_EVENT_PREFIX = \"Timer_Ev_\";\n\n    private ServiceAccessor serviceAccessor;\n\n    protected static void setSessionInfo(final APISession session) throws Exception {\n        final SessionAccessor sessionAccessor = ServiceAccessorFactory.getInstance().createSessionAccessor();\n        sessionAccessor.setSessionId(session.getId());\n    }\n\n    @Before\n    public void setUp() throws Exception {\n        serviceAccessor = ServiceAccessorFactory.getInstance().createServiceAccessor();\n    }\n\n    private boolean containsTimerJob(final String jobName) throws Exception {\n        setSessionInfo(getSession());\n        return serviceAccessor.getTransactionService().executeInTransaction(() -> serviceAccessor.getSchedulerService()\n                .getJobs().stream().anyMatch(serverJobName -> serverJobName.contains(jobName)));\n    }\n\n    private List<String> getJobs() throws Exception {\n        return serviceAccessor.getTransactionService()\n                .executeInTransaction(() -> serviceAccessor.getSchedulerService().getJobs());\n    }\n\n    private String getJobName(final long eventInstanceId) {\n        return TIMER_EVENT_PREFIX + eventInstanceId;\n    }\n\n    @Test\n    // when the boundary event is not triggered we will have the same behavior for interrupting and non-interrupting events; only interrupting will be tested\n    public void timerBoundaryEventNotTriggered() throws Exception {\n        // given\n        final long timerDuration = 20000;\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryTimerEvent(timerDuration, true,\n                \"step1\", \"exceptionStep\", \"step2\");\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        final FlowNodeInstance timer = waitForFlowNodeInWaitingState(processInstance, \"timer\", false);\n        final long boundaryId = timer.getId();\n        assertThat(containsTimerJob(getJobName(boundaryId))).isTrue();\n\n        // when\n        waitForUserTaskAndExecuteIt(processInstance, \"step1\", user);\n\n        // then\n        waitForFlowNodeInState(processInstance, \"timer\", TestStates.ABORTED, false);\n        assertThat(containsTimerJob(getJobName(boundaryId))).isFalse();\n\n        waitForUserTaskAndExecuteIt(processInstance, \"step2\", user);\n        waitForProcessToFinish(processInstance);\n\n        checkFlowNodeWasntExecuted(processInstance.getId(), \"exceptionStep\");\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    // when the boundary event is not triggered we will have the same behavior for interrupting and non-interrupting events; only interrupting will be tested\n    public void timerBoundaryEventNotTriggeredOnCallActivity() throws Exception {\n        // given\n        final long timerDuration = 20000;\n        final String simpleProcessName = \"targetProcess\";\n        final String simpleTaskName = \"stepCA\";\n\n        // deploy a simple process p1\n        final ProcessDefinition targetProcessDefinition = deployAndEnableSimpleProcess(simpleProcessName,\n                simpleTaskName);\n\n        // deploy a process, p2, with a call activity calling p1. The call activity has an interrupting timer boundary event\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryTimerEventOnCallActivity(\n                timerDuration, true, simpleProcessName);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        final FlowNodeInstance timer = waitForFlowNodeInWaitingState(processInstance, \"timer\", false);\n        final long boundaryId = timer.getId();\n        assertThat(containsTimerJob(getJobName(boundaryId))).isTrue();\n\n        // when\n        waitForUserTaskAndExecuteIt(processInstance, \"stepCA\", user);\n\n        // then\n        waitForFlowNodeInState(processInstance, \"timer\", TestStates.ABORTED, false);\n        assertThat(containsTimerJob(getJobName(boundaryId))).isFalse();\n\n        waitForUserTaskAndExecuteIt(processInstance, PARENT_PROCESS_USER_TASK_NAME, user);\n        waitForProcessToFinish(processInstance);\n        checkFlowNodeWasntExecuted(processInstance.getId(), EXCEPTION_STEP);\n\n        disableAndDeleteProcess(processDefinition);\n        disableAndDeleteProcess(targetProcessDefinition);\n    }\n\n    @Test\n    // when the boundary event is not triggered we will have the same behavior for interrupting and non-interrupting events; only interrupting will be tested\n    public void timerBoundaryEventNotTriggeredOnSequentialMultiInstance() throws Exception {\n        // given\n        final long timerDuration = 20000;\n        final int loopCardinality = 1;\n        final boolean isSequential = true;\n        final ProcessDefinition processDefinition = deployAndEnableProcessMultiInstanceWithBoundaryEvent(timerDuration,\n                true, \"step1\", loopCardinality,\n                isSequential, \"step2\", \"exceptionStep\");\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        final FlowNodeInstance timer = waitForFlowNodeInWaitingState(processInstance, \"timer\", false);\n        final long boundaryId = timer.getId();\n        assertThat(containsTimerJob(getJobName(boundaryId))).isTrue();\n\n        // when\n        waitForUserTasksAndExecuteIt(\"step1\", processInstance, loopCardinality);\n\n        // then\n        waitForFlowNodeInState(processInstance, \"timer\", TestStates.ABORTED, false);\n        assertThat(containsTimerJob(getJobName(boundaryId))).isFalse();\n\n        waitForUserTaskAndExecuteIt(processInstance, \"step2\", user);\n        waitForProcessToFinish(processInstance);\n\n        checkFlowNodeWasntExecuted(processInstance.getId(), \"exceptionStep\");\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    // when the boundary event is not triggered we will have the same behavior for interrupting and non-interrupting events; only interrupting will be tested\n    public void timerBoundaryEventNotTriggeredOnParallelMultiInstance() throws Exception {\n        // given\n        final long timerDuration = 20000;\n        final int loopCardinality = 2;\n        final boolean isSequential = false;\n        final ProcessDefinition processDefinition = deployAndEnableProcessMultiInstanceWithBoundaryEvent(timerDuration,\n                true, \"step1\", loopCardinality,\n                isSequential, \"step2\", \"exceptionStep\");\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        final FlowNodeInstance timer = waitForFlowNodeInWaitingState(processInstance, \"timer\", false);\n        final long boundaryId = timer.getId();\n        assertThat(containsTimerJob(getJobName(boundaryId))).isTrue();\n\n        // when\n        waitForUserTasksAndExecuteIt(\"step1\", processInstance, loopCardinality);\n\n        // then\n        waitForFlowNodeInState(processInstance, \"timer\", TestStates.ABORTED, false);\n        assertThat(containsTimerJob(getJobName(boundaryId))).isFalse();\n\n        waitForUserTaskAndExecuteIt(processInstance, \"step2\", user);\n        waitForProcessToFinish(processInstance);\n        checkFlowNodeWasntExecuted(processInstance.getId(), \"exceptionStep\");\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    // when the boundary event is not triggered we will have the same behavior for interrupting and non-interrupting events; only interrupting will be tested\n    public void timerBoundaryEventNotTriggeredOnLoopActivity() throws Exception {\n        // given\n        final long timerDuration = 20000;\n        final int loopMax = 1;\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryTimerEventOnLoopActivity(\n                timerDuration, true, loopMax, \"step1\", \"step2\",\n                \"exceptionStep\");\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final FlowNodeInstance timer = waitForFlowNodeInWaitingState(processInstance, \"timer\", false);\n        final long boundaryId = timer.getId();\n        assertThat(containsTimerJob(getJobName(boundaryId))).isTrue();\n\n        // when\n        waitForUserTasksAndExecuteIt(\"step1\", processInstance, loopMax);\n\n        // then\n        waitForFlowNodeInState(processInstance, \"timer\", TestStates.ABORTED, false);\n        assertThat(containsTimerJob(getJobName(boundaryId))).isFalse();\n\n        waitForUserTaskAndExecuteIt(processInstance, \"step2\", user);\n        waitForProcessToFinish(processInstance);\n        checkFlowNodeWasntExecuted(processInstance.getId(), \"exceptionStep\");\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void deleteProcessInstanceAlsoDeleteChildrenProcessesEvents() throws Exception {\n        // deploy a simple process with BoundaryEvent P1\n        List<ProcessDefinition> processDefinitions = new ArrayList<>();\n        final String simpleStepName = \"simpleStep\";\n        final ProcessDefinition simpleProcess = deployAndEnableSimpleProcessWithBoundaryEvent(\"simpleProcess\",\n                simpleStepName);\n        processDefinitions.add(simpleProcess); // To clean in the end\n\n        // deploy a process P2 containing a call activity calling P1\n        final String intermediateStepName = \"intermediateStep1\";\n        final String intermediateCallActivityName = \"intermediateCall\";\n        final ProcessDefinition intermediateProcess = deployAndEnableProcessWithCallActivity(\"intermediateProcess\",\n                simpleProcess.getName(),\n                intermediateStepName, intermediateCallActivityName);\n        processDefinitions.add(intermediateProcess); // To clean in the end\n\n        // deploy a process P3 containing a call activity calling P2\n        final String rootStepName = \"rootStep1\";\n        final String rootCallActivityName = \"rootCall\";\n        final ProcessDefinition rootProcess = deployAndEnableProcessWithCallActivity(\"rootProcess\",\n                intermediateProcess.getName(), rootStepName,\n                rootCallActivityName);\n        processDefinitions.add(rootProcess); // To clean in the end\n\n        // start P3, the call activities will start instances of P2 a and P1\n        final ProcessInstance rootProcessInstance = getProcessAPI().startProcess(rootProcess.getId());\n        waitForUserTask(rootProcessInstance, simpleStepName);\n        List<String> allJobs = getJobs();\n\n        //make sure timer events are created\n        assertThat(allJobs.stream().anyMatch(job -> job.contains(TIMER_EVENT_PREFIX))).isTrue();\n\n        // delete the root process instance\n        getProcessAPI().deleteProcessInstance(rootProcessInstance.getId());\n\n        // check that the instances of p1 and p2 were deleted\n        List<ProcessInstance> processInstances = getProcessAPI().getProcessInstances(0, 10,\n                ProcessInstanceCriterion.NAME_ASC);\n        assertThat(processInstances).isEmpty();\n\n        // check that archived flow nodes were deleted.\n        List<ArchivedActivityInstance> taskInstances = getProcessAPI().getArchivedActivityInstances(\n                rootProcessInstance.getId(), 0, 100,\n                ActivityInstanceCriterion.DEFAULT);\n        assertThat(taskInstances).isEmpty();\n\n        //check the quartz events got deleted correctly\n        allJobs = getJobs();\n\n        for (String job : allJobs) {\n            // There might be a few of those left in the DB, it should be the only ones\n            assertThat(job).isEqualToIgnoringCase(\"CleanInvalidSessions\");\n        }\n        //cleanup\n        disableAndDeleteProcess(processDefinitions);\n    }\n\n    private ProcessDefinition deployAndEnableSimpleProcessWithBoundaryEvent(final String processName,\n            final String userTaskName) throws Exception {\n        final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder().createNewInstance(processName,\n                \"1.0\");\n        processDefBuilder.addActor(ACTOR_NAME);\n        processDefBuilder.addStartEvent(\"tStart\");\n        processDefBuilder.addUserTask(userTaskName, ACTOR_NAME).addBoundaryEvent(\"TheBoundaryEvent\")\n                .addTimerEventTriggerDefinition(TimerType.DURATION,\n                        new ExpressionBuilder().createConstantLongExpression(6000L));\n        processDefBuilder.addEndEvent(\"tEnd\");\n        processDefBuilder.addEndEvent(\"tBoundaryEnd\");\n        processDefBuilder.addTransition(\"TheBoundaryEvent\", \"tBoundaryEnd\");\n        processDefBuilder.addTransition(\"tStart\", userTaskName);\n        processDefBuilder.addTransition(userTaskName, \"tEnd\");\n        return deployAndEnableProcessWithActor(processDefBuilder.done(), ACTOR_NAME, user);\n    }\n\n    private ProcessDefinition deployAndEnableProcessWithCallActivity(final String processName,\n            final String targetProcessName, final String userTaskName,\n            final String callActivityName) throws Exception {\n        final Expression targetProcessNameExpr = new ExpressionBuilder()\n                .createConstantStringExpression(targetProcessName);\n\n        final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder().createNewInstance(processName,\n                \"1.0\");\n        processDefBuilder.addActor(ACTOR_NAME);\n        processDefBuilder.addStartEvent(\"start\");\n        processDefBuilder.addCallActivity(callActivityName, targetProcessNameExpr, null);\n        processDefBuilder.addUserTask(userTaskName, ACTOR_NAME);\n        processDefBuilder.addEndEvent(\"end\");\n        processDefBuilder.addTransition(\"start\", callActivityName);\n        processDefBuilder.addTransition(callActivityName, userTaskName);\n        processDefBuilder.addTransition(userTaskName, \"end\");\n        return deployAndEnableProcessWithActor(processDefBuilder.done(), ACTOR_NAME, user);\n    }\n\n    @Test\n    public void timerBoundaryEvent_should_not_trigger_and_be_deleted_at_flownode_abortion() throws Exception {\n        final int timerDuration = 20_000;//long enough not to trigger\n\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"pTimerBoundary\", \"2.0\");\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n        processDefinitionBuilder.addStartEvent(\"start\");\n        final UserTaskDefinitionBuilder userTaskDefinitionBuilder = processDefinitionBuilder.addUserTask(\"step1\",\n                ACTOR_NAME);\n        userTaskDefinitionBuilder.addBoundaryEvent(\"Boundary timer\").addTimerEventTriggerDefinition(TimerType.DURATION,\n                new ExpressionBuilder().createConstantLongExpression(timerDuration));\n        userTaskDefinitionBuilder.addUserTask(\"exceptionStep\", ACTOR_NAME);\n        processDefinitionBuilder.addUserTask(\"step2\", ACTOR_NAME);\n        processDefinitionBuilder.addEndEvent(\"end\").addTerminateEventTrigger();\n        processDefinitionBuilder.addEndEvent(\"end2\").addTerminateEventTrigger();\n        processDefinitionBuilder.addTransition(\"start\", \"step1\");\n        processDefinitionBuilder.addTransition(\"start\", \"step2\");\n        processDefinitionBuilder.addTransition(\"step1\", \"end\");\n        processDefinitionBuilder.addTransition(\"step2\", \"end2\");\n        processDefinitionBuilder.addTransition(\"Boundary timer\", \"exceptionStep\");\n        processDefinitionBuilder.addTransition(\"exceptionStep\", \"end\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),\n                ACTOR_NAME, user);\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        waitForUserTask(processInstance.getId(), \"step1\");\n        waitForUserTaskAssignAndExecuteIt(processInstance, \"step2\", user, Map.of());\n        waitForProcessToFinish(processInstance);\n        assertThat(getJobs()).isEmpty();\n        disableAndDeleteProcess(processDefinition);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/event/LocalTimerEventIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.event;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.*;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstance;\nimport org.bonitasoft.engine.bpm.flownode.TimerType;\nimport org.bonitasoft.engine.bpm.process.ActivationState;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.scheduler.SchedulerService;\nimport org.bonitasoft.engine.test.CommonAPILocalIT;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.bonitasoft.engine.transaction.TransactionService;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class LocalTimerEventIT extends CommonAPILocalIT {\n\n    private static final String TIMER_EVENT_PREFIX = \"Timer_Ev_\";\n\n    @Before\n    public void setUp() throws BonitaException {\n        loginWithTechnicalUser();\n    }\n\n    @After\n    public void tearDown() throws BonitaException {\n        logout();\n    }\n\n    @Test\n    public void timerJobsAreDeleteOnDisable() throws Exception {\n        final ProcessDefinitionBuilder definitionBuilder = new ProcessDefinitionBuilder().createNewInstance(\"proc\",\n                \"1.0\");\n        final String timerEventName = \"startTimer\";\n        definitionBuilder.addStartEvent(timerEventName).addTimerEventTriggerDefinition(TimerType.DURATION,\n                new ExpressionBuilder().createConstantLongExpression(2000));\n        definitionBuilder.addAutomaticTask(\"auto\");\n        definitionBuilder.addTransition(timerEventName, \"auto\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcess(definitionBuilder.done());\n        final String jobName = getJobName(processDefinition, timerEventName);\n        assertTrue(containsTimerJob(jobName));\n\n        getProcessAPI().disableProcess(processDefinition.getId());\n        assertFalse(containsTimerJob(jobName));\n\n        getProcessAPI().enableProcess(processDefinition.getId());\n        assertTrue(containsTimerJob(jobName));\n\n        disableAndDeleteProcess(processDefinition.getId());\n        assertFalse(containsTimerJob(jobName));\n\n    }\n\n    private boolean containsTimerJob(final String jobName) throws Exception {\n        setSessionInfo(getSession());\n        final SchedulerService schedulerService = getServiceAccessor().getSchedulerService();\n        final TransactionService transactionService = getServiceAccessor().getTransactionService();\n        return transactionService.executeInTransaction(() -> {\n            final List<String> jobs = schedulerService.getJobs();\n            for (final String serverJobName : jobs) {\n                if (serverJobName.contains(jobName)) {\n                    return true;\n                }\n            }\n            return false;\n        });\n    }\n\n    private String getJobName(final ProcessDefinition processDefinition, final String timerEventName) {\n        return TIMER_EVENT_PREFIX + processDefinition.getId() + timerEventName;\n    }\n\n    private String getJobName(final long eventInstanceId) {\n        return TIMER_EVENT_PREFIX + eventInstanceId;\n    }\n\n    private ProcessDefinition deployProcessWithTimerIntermediateCatchEvent(final TimerType timerType,\n            final Expression timerValue, final String stepName)\n            throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My Process with start event\", \"1.0\");\n        processDefinitionBuilder.addIntermediateCatchEvent(\"intermediateCatchEvent\")\n                .addTimerEventTriggerDefinition(timerType, timerValue);\n        processDefinitionBuilder.addAutomaticTask(stepName);\n        processDefinitionBuilder.addEndEvent(\"end\");\n        processDefinitionBuilder.addTransition(\"intermediateCatchEvent\", stepName);\n        processDefinitionBuilder.addTransition(stepName, \"end\");\n\n        final ProcessDefinition definition = deployAndEnableProcess(processDefinitionBuilder.getProcess());\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(definition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n        return definition;\n    }\n\n    @Test\n    public void cancelProcessInstanceWithTimerIntermediateCatchEvent() throws Exception {\n        // given\n        final int timerTrigger = 20000; // the timer intermediate catch event will wait 20 seconds\n        final Expression timerExpression = new ExpressionBuilder().createConstantLongExpression(timerTrigger);\n        final ProcessDefinition definition = deployProcessWithTimerIntermediateCatchEvent(TimerType.DURATION,\n                timerExpression, \"step\");\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId());\n        final FlowNodeInstance intermediateCatchEvent = waitForFlowNodeInWaitingState(processInstance,\n                \"intermediateCatchEvent\", false);\n        final Long floNodeInstanceId = intermediateCatchEvent.getId();\n        final String jobName = getJobName(floNodeInstanceId);\n        assertThat(containsTimerJob(jobName)).isTrue();\n\n        // when\n        getProcessAPI().cancelProcessInstance(processInstance.getId());\n\n        // then\n        waitForFlowNodeInState(processInstance, \"intermediateCatchEvent\", TestStates.CANCELLED, false);\n        assertThat(containsTimerJob(jobName)).isFalse();\n\n        waitForProcessToBeInState(processInstance, ProcessInstanceState.CANCELLED);\n        checkWasntExecuted(processInstance, \"step\");\n\n        disableAndDeleteProcess(definition);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/failure/FlowNodeFailureIT.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.failure;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.assertEquals;\n\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstance;\nimport org.bonitasoft.engine.bpm.process.*;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.commons.exceptions.ScopedException;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.operation.LeftOperandBuilder;\nimport org.bonitasoft.engine.operation.OperatorType;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.impl.ServiceAccessorFactory;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class FlowNodeFailureIT extends TestWithUser {\n\n    private ServiceAccessor serviceAccessor;\n    private ProcessDefinition processDefinition;\n\n    @Override\n    @Before\n    public void before() throws Exception {\n        super.before();\n        serviceAccessor = ServiceAccessorFactory.getInstance().createServiceAccessor();\n        DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process_with_failed_flownode\", PROCESS_VERSION)\n                .addActor(ACTOR_NAME)\n                .addAutomaticTask(\"step1\")\n                .addOperation(new LeftOperandBuilder().createDataLeftOperand(\"myData\"),\n                        OperatorType.ASSIGNMENT,\n                        \"\",\n                        new ExpressionBuilder()\n                                .createGroovyScriptExpression(\"my-failing-script\",\n                                        \"throw new RuntimeException('Failed !')\", String.class.getName()))\n                .getProcess();\n        processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user);\n    }\n\n    @After\n    public void after() throws Exception {\n        disableAndDeleteProcess(processDefinition);\n        super.after();\n    }\n\n    @Test\n    public void create_a_failure_on_flownode_operation_exception() throws Exception {\n        // Given a process failing on a flownode operation\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(processDefinition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        // When the flownode is executed\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n        final FlowNodeInstance failFlowNodeInstance = waitForFlowNodeInFailedState(processInstance);\n\n        // Then a failure is created\n        assertEquals(\"step1\", failFlowNodeInstance.getName());\n        var failureService = ServiceAccessorFactory.getInstance().createServiceAccessor().getBpmFailureService();\n        var failures = serviceAccessor.getTransactionService()\n                .executeInTransaction(() -> failureService.getFlowNodeFailures(failFlowNodeInstance.getId(), 5));\n        assertThat(failures).hasSize(1);\n        var failure = failures.get(0);\n        assertThat(failure.getScope())\n                .isEqualTo(ScopedException.OPERATION);\n        assertThat(failure.getContext())\n                .isEqualTo(\"expression::my-failing-script\");\n        assertThat(failure.getErrorMessage())\n                .isEqualTo(\"RuntimeException: Failed !\");\n\n        var processInstanceFailures = serviceAccessor.getTransactionService()\n                .executeInTransaction(() -> failureService.getProcessInstanceFailures(processInstance.getId(), 5));\n        assertThat(processInstanceFailures).hasSize(1);\n        assertThat(processInstanceFailures.get(0))\n                .isEqualTo(failures.get(0));\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/failure/ProcessInstanceFailureIT.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.failure;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.awaitility.Awaitility.await;\nimport static org.junit.Assert.assertEquals;\n\nimport java.io.IOException;\nimport java.util.concurrent.TimeUnit;\n\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.bar.InvalidBusinessArchiveFormatException;\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.bpm.process.*;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.commons.exceptions.ScopedException;\nimport org.bonitasoft.engine.connectors.TestConnectorThatThrowException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.impl.ServiceAccessorFactory;\nimport org.bonitasoft.engine.test.BuildTestUtil;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class ProcessInstanceFailureIT extends TestWithUser {\n\n    private ServiceAccessor serviceAccessor;\n    private ProcessDefinition failingProcessDefinition;\n\n    @Override\n    @Before\n    public void before() throws Exception {\n        super.before();\n        serviceAccessor = ServiceAccessorFactory.getInstance().createServiceAccessor();\n        failingProcessDefinition = getFailingProcessDefinition(\"My_Process_with_failing_connector\");\n    }\n\n    @After\n    public void after() throws Exception {\n        disableAndDeleteProcess(failingProcessDefinition);\n        super.after();\n    }\n\n    private ProcessDefinition getFailingProcessDefinition(String processName) throws InvalidProcessDefinitionException,\n            IOException, BonitaException, InvalidBusinessArchiveFormatException {\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(processName, PROCESS_VERSION)\n                .addActor(ACTOR_NAME)\n                .addConnector(\"testConnectorThatThrowException\", \"testConnectorThatThrowException\", \"1.0\",\n                        ConnectorEvent.ON_FINISH)\n                .addAutomaticTask(\"step1\")\n                .getProcess();\n        final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(\n                        designProcessDefinition);\n        businessArchiveBuilder.addConnectorImplementation(\n                getResource(\"/org/bonitasoft/engine/connectors/TestConnectorThatThrowException.impl\",\n                        \"TestConnectorThatThrowException.impl\"));\n        businessArchiveBuilder\n                .addClasspathResource(BuildTestUtil\n                        .generateJarAndBuildBarResource(TestConnectorThatThrowException.class,\n                                \"TestConnectorThatThrowException.jar\"));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchiveBuilder.done(),\n                ACTOR_NAME,\n                user);\n        return processDefinition;\n    }\n\n    @Test\n    public void create_a_failure_on_process_connector_exception() throws Exception {\n        // Given a process failing on a flownode operation\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI()\n                .getProcessDeploymentInfo(failingProcessDefinition.getId());\n        assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState());\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId());\n        waitForProcessToBeInState(processInstance, ProcessInstanceState.ERROR);\n\n        // Then a failure is created\n        var failureService = ServiceAccessorFactory.getInstance().createServiceAccessor().getBpmFailureService();\n        var failures = serviceAccessor.getTransactionService()\n                .executeInTransaction(() -> failureService.getProcessInstanceFailures(processInstance.getId(), 5));\n        assertThat(failures).hasSize(1);\n        var failure = failures.get(0);\n        assertThat(failure.getScope())\n                .isEqualTo(ScopedException.CONNECTOR);\n        assertThat(failure.getContext())\n                .isEqualTo(\n                        \"testConnectorThatThrowException::testConnectorThatThrowException::on_finish//input-validation\");\n        assertThat(failure.getErrorMessage())\n                .isEqualTo(\"ConnectorValidationException: bad kind of exception\");\n\n        //There are no subprocess so no subprocess failure should be retrieved\n        var subProcessInstanceFailures = serviceAccessor.getTransactionService()\n                .executeInTransaction(\n                        () -> failureService.getChildProcessInstancesFailures(processInstance.getId(), 5));\n        assertThat(subProcessInstanceFailures).hasSize(0);\n    }\n\n    @Test\n    public void create_a_failure_on_sub_process_connector_exception() throws Exception {\n        // Given a process failing on a flownode operation\n        final DesignProcessDefinition mainProcessDesign = new ProcessDefinitionBuilder()\n                .createNewInstance(\"My_Process_with_call_activity\", PROCESS_VERSION)\n                .addActor(ACTOR_NAME)\n                .addCallActivity(\"call activity\",\n                        new ExpressionBuilder().createConstantStringExpression(failingProcessDefinition.getName()),\n                        new ExpressionBuilder().createConstantStringExpression(PROCESS_VERSION))\n                .getProcess();\n        final ProcessDefinition mainProcessDefinition = deployAndEnableProcessWithActor(mainProcessDesign, ACTOR_NAME,\n                user);\n        try {\n            final ProcessDeploymentInfo mainProcessDeploymentInfo = getProcessAPI()\n                    .getProcessDeploymentInfo(mainProcessDefinition.getId());\n            assertEquals(ActivationState.ENABLED, mainProcessDeploymentInfo.getActivationState());\n\n            final ProcessInstance processInstance = getProcessAPI()\n                    .startProcess(mainProcessDeploymentInfo.getProcessId());\n            waitForProcessToBeInState(processInstance, ProcessInstanceState.STARTED);\n\n            SearchOptions SearchOption = new SearchOptionsBuilder(0, 1)\n                    .filter(ProcessInstanceSearchDescriptor.PROCESS_DEFINITION_ID, failingProcessDefinition.getId())\n                    .done();\n            await().atMost(3, TimeUnit.SECONDS)\n                    .until(() -> !getProcessAPI().searchProcessInstances(SearchOption).getResult().isEmpty());\n            ProcessInstance subProcessInstance = getProcessAPI().searchProcessInstances(SearchOption).getResult()\n                    .get(0);\n            waitForProcessToBeInState(subProcessInstance, ProcessInstanceState.ERROR);\n\n            // Then a failure is created in the subprocess\n            var failureService = ServiceAccessorFactory.getInstance().createServiceAccessor().getBpmFailureService();\n            var subProcessInstanceFailures = serviceAccessor.getTransactionService()\n                    .executeInTransaction(\n                            () -> failureService.getChildProcessInstancesFailures(processInstance.getId(), 5));\n            assertThat(subProcessInstanceFailures).hasSize(1);\n            var failure = subProcessInstanceFailures.get(0);\n            assertThat(failure.getScope())\n                    .isEqualTo(ScopedException.CONNECTOR);\n            assertThat(failure.getContext())\n                    .isEqualTo(\n                            \"testConnectorThatThrowException::testConnectorThatThrowException::on_finish//input-validation\");\n            assertThat(failure.getErrorMessage())\n                    .isEqualTo(\"ConnectorValidationException: bad kind of exception\");\n\n            //No failure as root process instance level\n            var mainProcessInstanceFailures = serviceAccessor.getTransactionService()\n                    .executeInTransaction(() -> failureService.getProcessInstanceFailures(processInstance.getId(), 5));\n            assertThat(mainProcessInstanceFailures).hasSize(0);\n\n            //cancel process instance to archive it\n            getProcessAPI().cancelProcessInstance(processInstance.getId());\n            waitForProcessToBeInState(processInstance, ProcessInstanceState.CANCELLED);\n\n            // Now let's check the archived version, for sub-process...\n            var archSubProcessInstanceFailures = serviceAccessor.getTransactionService()\n                    .executeInTransaction(\n                            () -> failureService.getArchivedChildProcessInstancesFailures(processInstance.getId(), 5));\n            assertThat(archSubProcessInstanceFailures).hasSize(1);\n\n            // ... and for the main process:\n            var archMainProcessInstanceFailures = serviceAccessor.getTransactionService()\n                    .executeInTransaction(\n                            () -> failureService.getArchivedProcessInstanceFailures(processInstance.getId(), 5));\n            assertThat(archMainProcessInstanceFailures).hasSize(0);\n\n        } finally {\n            disableAndDeleteProcess(mainProcessDefinition);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/flownode/FlowNodeInstanceServiceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.concurrent.CopyOnWriteArrayList;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.CyclicBarrier;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.TimeUnit;\nimport java.util.function.ToIntFunction;\n\nimport org.bonitasoft.engine.bpm.CommonBPMServicesTest;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.model.SGatewayType;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowElementsContainerType;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SGatewayInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SGatewayInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SStartEventInstanceBuilderFactory;\nimport org.bonitasoft.engine.lock.BonitaLock;\nimport org.bonitasoft.engine.lock.LockService;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class FlowNodeInstanceServiceIT extends CommonBPMServicesTest {\n\n    private UserTransactionService userTransactionService;\n\n    private ActivityInstanceService activityInstanceService;\n\n    @Before\n    public void setup() {\n        userTransactionService = getServiceAccessor().getUserTransactionService();\n        activityInstanceService = getServiceAccessor().getActivityInstanceService();\n    }\n\n    private long getNbFlowNodeInstances(final QueryOptions countOptions) throws Exception {\n        return userTransactionService.executeInTransaction(\n                () -> activityInstanceService.getNumberOfFlowNodeInstances(SFlowNodeInstance.class, countOptions));\n    }\n\n    @Test\n    public void searchFlowNodeInstances() throws Exception {\n        final SStartEventInstanceBuilderFactory startEventInstanceBuilder = BuilderFactory\n                .get(SStartEventInstanceBuilderFactory.class);\n        final SProcessInstance procInst1 = createSProcessInstance();\n        final SProcessInstance procInst2 = createSProcessInstance();\n\n        final OrderByOption oderByOption = new OrderByOption(SFlowNodeInstance.class,\n                startEventInstanceBuilder.getNameKey(), OrderByType.ASC);\n        final List<FilterOption> filterOptions = Collections.emptyList();\n        final QueryOptions queryOptions = new QueryOptions(0, 10, Collections.singletonList(oderByOption),\n                filterOptions, null);\n\n        // search: no result expected\n        List<SFlowNodeInstance> flowNodeInstances = searchFlowNodeInstances(queryOptions);\n        assertTrue(\"There should not be any flownode instance instead of \" + flowNodeInstances.size(),\n                flowNodeInstances.isEmpty());\n\n        // create flow nodes\n        createFlowNodeInstances(procInst1, procInst2);\n\n        // search: created flow nodes must be retrieved\n        flowNodeInstances = searchFlowNodeInstances(queryOptions);\n        assertEquals(10, flowNodeInstances.size());\n\n        // delete process instances\n        deleteSProcessInstance(procInst1);\n        deleteSProcessInstance(procInst2);\n\n        flowNodeInstances = searchFlowNodeInstances(queryOptions);\n        assertEquals(0, flowNodeInstances.size());\n    }\n\n    @Test\n    public void searchFlowNodeInstancesWithFilter() throws Exception {\n        final SStartEventInstanceBuilderFactory startEventInstanceBuilder = BuilderFactory\n                .get(SStartEventInstanceBuilderFactory.class);\n        final SProcessInstance procInst1 = createSProcessInstance();\n        final SProcessInstance procInst2 = createSProcessInstance();\n\n        final OrderByOption oderByOption = new OrderByOption(SFlowNodeInstance.class,\n                startEventInstanceBuilder.getNameKey(), OrderByType.ASC);\n        final FilterOption filterOption = new FilterOption(SFlowNodeInstance.class,\n                startEventInstanceBuilder.getParentProcessInstanceKey(), procInst1.getId());\n        final QueryOptions queryOptions = new QueryOptions(0, 10, Collections.singletonList(oderByOption),\n                Collections.singletonList(filterOption), null);\n        final QueryOptions countOptions = new QueryOptions(0, 10, null, Collections.singletonList(filterOption), null);\n\n        // search: no result expected\n        List<SFlowNodeInstance> flowNodeInstances = searchFlowNodeInstances(queryOptions);\n        long nbFlowNodeInstances = getNbFlowNodeInstances(countOptions);\n        assertTrue(flowNodeInstances.isEmpty());\n        assertEquals(0, nbFlowNodeInstances);\n\n        // create flow nodes\n        createFlowNodeInstances(procInst1, procInst2);\n\n        // search: created flow nodes must be retrieved\n        flowNodeInstances = searchFlowNodeInstances(queryOptions);\n        nbFlowNodeInstances = getNbFlowNodeInstances(countOptions);\n        assertEquals(7, flowNodeInstances.size());\n        assertEquals(7, nbFlowNodeInstances);\n\n        deleteSProcessInstance(procInst1);\n        deleteSProcessInstance(procInst2);\n\n    }\n\n    private void createFlowNodeInstances(final SProcessInstance procInst1, final SProcessInstance procInst2)\n            throws SBonitaException {\n        // add flow nodes to procInst 1\n        createSStartEventInstance(\"startEvent\", 1, procInst1.getId(), 5, procInst1.getId());\n        createSIntermediateCatchEventInstance(\"intermediateCatchEvent\", 2, procInst1.getId(), 5, procInst1.getId());\n        createSIntermediateThrowEventInstance(\"intermediateThrowEvent\", 3, procInst1.getId(), 5, procInst1.getId());\n        createSEndEventInstance(\"endEvent\", 4, procInst1.getId(), 5, procInst1.getId());\n\n        final SGatewayInstance gatewayInstance = BuilderFactory.get(SGatewayInstanceBuilderFactory.class)\n                .createNewInstance(\"Gateway1\", 5, procInst1.getId(), procInst1.getId(), SGatewayType.EXCLUSIVE, 2,\n                        procInst1.getId(), procInst1.getId())\n                .setStateId(1).setHitBys(\"a,b,c\").done();\n        insertGatewayInstance(gatewayInstance);\n\n        createSUserTaskInstance(\"userTask\", 6, procInst1.getId(), 5, procInst1.getId(), 10);\n        createSAutomaticTaskInstance(\"autoTask\", 7, procInst1.getId(), 5, procInst1.getId());\n\n        // add flow nodes to procInst 2\n        createSStartEventInstance(\"startEvent\", 8, procInst2.getId(), 5, procInst2.getId());\n        createSAutomaticTaskInstance(\"autoTask\", 9, procInst2.getId(), 5, procInst2.getId());\n        createSEndEventInstance(\"endEvent\", 10, procInst2.getId(), 5, procInst2.getId());\n    }\n\n    @Test\n    public void isTaskPendingForUser() throws Exception {\n        long flowNodeDefinitionId = 12355467L;\n        long processDefinitionId = 123445566L;\n        long rootProcessInstanceID = 7754L;\n        long actorId = 5589L;\n        SUserTaskInstance step1 = createSUserTaskInstance(\"step1\", flowNodeDefinitionId, -1, processDefinitionId,\n                rootProcessInstanceID, actorId);\n        long userId = 4411L;\n        //given\n        getTransactionService().begin();\n        final SPendingActivityMapping mapping = SPendingActivityMapping.builder().activityId(step1.getId())\n                .userId(userId).build();\n        activityInstanceService.addPendingActivityMappings(mapping);\n        getTransactionService().complete();\n        //\n        //when\n        getTransactionService().begin();\n        boolean taskPendingForUser = activityInstanceService.isTaskPendingForUser(step1.getId(), userId);\n        getTransactionService().complete();\n        //then\n        assertTrue(\"task should be pending\", taskPendingForUser);\n\n        // clean-up:\n        getTransactionService().begin();\n        activityInstanceService.deleteAllPendingMappings();\n        activityInstanceService.deleteFlowNodeInstance(step1);\n        getTransactionService().complete();\n    }\n\n    @Test\n    public void lastUpdateDate_should_be_persisted_when_modifying_flowNode() throws Exception {\n        // given: create a user task\n        long flowNodeDefinitionId = 12355467L;\n        long processDefinitionId = 123445566L;\n        long rootProcessInstanceID = 7754L;\n        long actorId = 5589L;\n        SUserTaskInstance task = createSUserTaskInstance(\"taskWithLastUpdateDate\", flowNodeDefinitionId, -1,\n                processDefinitionId, rootProcessInstanceID, actorId);\n\n        // Verify initial lastUpdateDate is set during creation\n        getTransactionService().begin();\n        SFlowNodeInstance initialTask = activityInstanceService.getFlowNodeInstance(task.getId());\n        long initialLastUpdateDate = initialTask.getLastUpdateDate();\n        getTransactionService().complete();\n\n        assertThat(initialLastUpdateDate).as(\"Initial lastUpdateDate should be > 0\").isGreaterThan(0);\n\n        // Wait a bit to ensure timestamp difference\n        Thread.sleep(10);\n\n        // when: modify the flow node using setExecuting\n        getTransactionService().begin();\n        SFlowNodeInstance taskToModify = activityInstanceService.getFlowNodeInstance(task.getId());\n        activityInstanceService.setExecuting(taskToModify);\n        getTransactionService().complete();\n\n        // then: re-fetch from database and verify lastUpdateDate was updated\n        getTransactionService().begin();\n        SFlowNodeInstance updatedTask = activityInstanceService.getFlowNodeInstance(task.getId());\n        long updatedLastUpdateDate = updatedTask.getLastUpdateDate();\n        getTransactionService().complete();\n\n        assertThat(updatedLastUpdateDate).as(\"Updated lastUpdateDate should be > 0\").isGreaterThan(0);\n        assertThat(updatedLastUpdateDate).as(\"lastUpdateDate should be updated after modification\")\n                .isGreaterThanOrEqualTo(initialLastUpdateDate);\n\n        // clean-up:\n        getTransactionService().begin();\n        activityInstanceService.deleteFlowNodeInstance(updatedTask);\n        getTransactionService().complete();\n    }\n\n    @Test\n    public void lastUpdateDate_should_be_persisted_when_assigning_humanTask_with_strict_query() throws Exception {\n        // given: create a user task\n        long flowNodeDefinitionId = 12355467L;\n        long processDefinitionId = 123445566L;\n        long rootProcessInstanceID = 7754L;\n        long actorId = 5589L;\n        SUserTaskInstance task = createSUserTaskInstance(\"taskForAssignment\", flowNodeDefinitionId, -1,\n                processDefinitionId, rootProcessInstanceID, actorId);\n\n        // Verify initial lastUpdateDate is set during creation\n        getTransactionService().begin();\n        SFlowNodeInstance initialTask = activityInstanceService.getFlowNodeInstance(task.getId());\n        long initialLastUpdateDate = initialTask.getLastUpdateDate();\n        getTransactionService().complete();\n\n        assertThat(initialLastUpdateDate).as(\"Initial lastUpdateDate should be > 0\").isGreaterThan(0);\n\n        // Wait a bit to ensure timestamp difference\n        Thread.sleep(10);\n\n        // when: assign the task using assignHumanTaskIfNotAssigned (uses updateStrictHuman query)\n        long userId = 12345L;\n        getTransactionService().begin();\n        activityInstanceService.assignHumanTaskIfNotAssigned(task.getId(), userId);\n        getTransactionService().complete();\n\n        // then: re-fetch from database and verify lastUpdateDate was updated\n        getTransactionService().begin();\n        SFlowNodeInstance assignedTask = activityInstanceService.getFlowNodeInstance(task.getId());\n        long assignedLastUpdateDate = assignedTask.getLastUpdateDate();\n        long assigneeId = ((SUserTaskInstance) assignedTask).getAssigneeId();\n        getTransactionService().complete();\n\n        assertThat(assigneeId).as(\"Assignee should be set\").isEqualTo(userId);\n        assertThat(assignedLastUpdateDate).as(\"Assigned lastUpdateDate should be > 0\").isGreaterThan(0);\n        assertThat(assignedLastUpdateDate).as(\"lastUpdateDate should be updated after assignment\")\n                .isGreaterThanOrEqualTo(initialLastUpdateDate);\n\n        // clean-up:\n        getTransactionService().begin();\n        activityInstanceService.deleteFlowNodeInstance(assignedTask);\n        getTransactionService().complete();\n    }\n\n    // Thread-safety tests for the multi-instance counter updates. Verify that atomic SQL\n    // (SET counter = counter + :n) prevents lost updates both without the process instance\n    // lock and with it (the production Lock → Tx → Work pattern).\n\n    @Test\n    public void concurrent_addMultiInstanceNumberOfCompletedActivities_without_lock_should_not_lose_updates()\n            throws Exception {\n        runConcurrentMiCounterTest(\n                \"miActivity\", /* useLock */ false,\n                (svc, mi) -> svc.addMultiInstanceNumberOfCompletedActivities(mi, 1),\n                SMultiInstanceActivityInstance::getNumberOfCompletedInstances,\n                \"completions\");\n    }\n\n    @Test\n    public void concurrent_addMultiInstanceNumberOfTerminatedActivities_without_lock_should_not_lose_updates()\n            throws Exception {\n        runConcurrentMiCounterTest(\n                \"miActivityTerminated\", /* useLock */ false,\n                (svc, mi) -> svc.addMultiInstanceNumberOfTerminatedActivities(mi, 1),\n                SMultiInstanceActivityInstance::getNumberOfTerminatedInstances,\n                \"terminations\");\n    }\n\n    @Test\n    public void concurrent_addMultiInstanceNumberOfCompletedActivities_with_lock_should_not_lose_updates()\n            throws Exception {\n        runConcurrentMiCounterTest(\n                \"miActivityLockedCompleted\", /* useLock */ true,\n                (svc, mi) -> svc.addMultiInstanceNumberOfCompletedActivities(mi, 1),\n                SMultiInstanceActivityInstance::getNumberOfCompletedInstances,\n                \"completions\");\n    }\n\n    @Test\n    public void concurrent_addMultiInstanceNumberOfTerminatedActivities_with_lock_should_not_lose_updates()\n            throws Exception {\n        runConcurrentMiCounterTest(\n                \"miActivityLockedTerminated\", /* useLock */ true,\n                (svc, mi) -> svc.addMultiInstanceNumberOfTerminatedActivities(mi, 1),\n                SMultiInstanceActivityInstance::getNumberOfTerminatedInstances,\n                \"terminations\");\n    }\n\n    private void runConcurrentMiCounterTest(\n            final String activityName,\n            final boolean useLock,\n            final MiCounterOp op,\n            final ToIntFunction<SMultiInstanceActivityInstance> counterGetter,\n            final String counterLabel) throws Exception {\n        final int threadCount = 50;\n\n        final SProcessInstance processInstance = createSProcessInstance();\n        try {\n            final SMultiInstanceActivityInstance miActivity = createMultiInstanceActivity(\n                    activityName, processInstance, threadCount);\n\n            final LockService lockService = useLock ? getServiceAccessor().getLockService() : null;\n            final String objectType = SFlowElementsContainerType.PROCESS.name();\n            final List<Throwable> errors = new CopyOnWriteArrayList<>();\n            final CyclicBarrier barrier = new CyclicBarrier(threadCount);\n            final CountDownLatch done = new CountDownLatch(threadCount);\n            final ExecutorService executor = Executors.newFixedThreadPool(threadCount);\n            final long sessionId = getSessionAccessor().getSessionId();\n\n            for (int i = 0; i < threadCount; i++) {\n                executor.submit(() -> {\n                    try {\n                        getSessionAccessor().setSessionId(sessionId);\n                        barrier.await(30, TimeUnit.SECONDS);\n\n                        final BonitaLock lock = useLock\n                                ? lockService.lock(processInstance.getId(), objectType)\n                                : null;\n                        try {\n                            getTransactionService().begin();\n                            try {\n                                final SMultiInstanceActivityInstance freshMi = (SMultiInstanceActivityInstance) activityInstanceService\n                                        .getFlowNodeInstance(miActivity.getId());\n                                op.apply(activityInstanceService, freshMi);\n                                getTransactionService().complete();\n                            } catch (final Exception e) {\n                                getTransactionService().setRollbackOnly();\n                                getTransactionService().complete();\n                                throw e;\n                            }\n                        } finally {\n                            if (useLock) {\n                                lockService.unlock(lock);\n                            }\n                        }\n                    } catch (final Throwable t) {\n                        errors.add(t);\n                    } finally {\n                        done.countDown();\n                    }\n                });\n            }\n\n            done.await(60, TimeUnit.SECONDS);\n            executor.shutdown();\n            executor.awaitTermination(10, TimeUnit.SECONDS);\n\n            assertThat(errors).as(\"Unexpected errors in worker threads\").isEmpty();\n\n            getTransactionService().begin();\n            final SMultiInstanceActivityInstance result = (SMultiInstanceActivityInstance) activityInstanceService\n                    .getFlowNodeInstance(miActivity.getId());\n            getTransactionService().complete();\n\n            assertThat(counterGetter.applyAsInt(result))\n                    .as(\"All %d %s should be counted\", threadCount, counterLabel)\n                    .isEqualTo(threadCount);\n            assertThat(result.getNumberOfActiveInstances())\n                    .as(\"Active instances should reach 0\")\n                    .isEqualTo(0);\n        } finally {\n            deleteSProcessInstance(processInstance);\n        }\n    }\n\n    @FunctionalInterface\n    private interface MiCounterOp {\n\n        void apply(ActivityInstanceService svc, SMultiInstanceActivityInstance mi) throws SBonitaException;\n    }\n\n    private SMultiInstanceActivityInstance createMultiInstanceActivity(\n            final String name, final SProcessInstance processInstance, final int numberOfActiveInstances)\n            throws SBonitaException {\n        final SMultiInstanceActivityInstance miActivity = new SMultiInstanceActivityInstance(\n                name, 1L, processInstance.getId(), processInstance.getId(),\n                1L, processInstance.getId(), false);\n        miActivity.setNumberOfActiveInstances(numberOfActiveInstances);\n        miActivity.setLoopCardinality(numberOfActiveInstances);\n        miActivity.setStateId(28); // EXECUTING state\n        miActivity.setLogicalGroup(3, processInstance.getId()); // parent process instance\n\n        getTransactionService().begin();\n        activityInstanceService.createActivityInstance(miActivity);\n        getTransactionService().complete();\n\n        return miActivity;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/process/AddCommentConnector.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.process;\n\nimport org.bonitasoft.engine.connector.AbstractConnector;\nimport org.bonitasoft.engine.connector.ConnectorException;\nimport org.bonitasoft.engine.exception.CreationException;\n\npublic class AddCommentConnector extends AbstractConnector {\n\n    @Override\n    protected void executeBusinessLogic() throws ConnectorException {\n        try {\n            getAPIAccessor().getProcessAPI().addProcessComment(getExecutionContext().getRootProcessInstanceId(),\n                    \"comment added by connector\");\n        } catch (CreationException e) {\n            throw new ConnectorException(e);\n        }\n    }\n\n    @Override\n    public void validateInputParameters() {\n\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/process/DeleteProcessInstancesIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.process;\n\nimport static java.util.Arrays.asList;\nimport static java.util.Map.entry;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.SoftAssertions.assertSoftly;\nimport static org.awaitility.Awaitility.await;\nimport static org.bonitasoft.engine.data.instance.api.DataInstanceContainer.ACTIVITY_INSTANCE;\nimport static org.bonitasoft.engine.data.instance.api.DataInstanceContainer.PROCESS_INSTANCE;\nimport static org.junit.Assert.assertThrows;\nimport static org.junit.Assert.fail;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport org.bonitasoft.engine.bpm.bar.BarResource;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.bar.actorMapping.Actor;\nimport org.bonitasoft.engine.bpm.bar.actorMapping.ActorMapping;\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.bpm.contract.FileInputValue;\nimport org.bonitasoft.engine.bpm.contract.Type;\nimport org.bonitasoft.engine.bpm.document.DocumentValue;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.core.contract.data.SContractDataNotFoundException;\nimport org.bonitasoft.engine.core.document.api.DocumentService;\nimport org.bonitasoft.engine.core.document.model.SLightDocument;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAConnectorInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.expression.InvalidExpressionException;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.test.BuildTestUtil;\nimport org.bonitasoft.engine.test.CommonAPILocalIT;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.junit.Test;\n\n/**\n * Verify that api methods delete process instances and all its elements\n */\npublic class DeleteProcessInstancesIT extends CommonAPILocalIT {\n\n    @Test\n    public void should_delete_complete_archived_process_instances() throws Exception {\n        loginWithTechnicalUser();\n        User user = createUser(\"deleteProcessInstanceIT\", \"bpm\");\n        ProcessDefinition mainProcess = createMainProcessDefinition();\n        ProcessDefinition sub1 = createSubProcessDefinition1();\n        ProcessDefinition sub2 = createSubProcessDefinition2();\n\n        List<Long> processInstances = new ArrayList<>();\n        List<Long> userTaskInstances = new ArrayList<>();\n        for (int i = 0; i < 2; i++) {\n            long id = getProcessAPI().startProcessWithInputs(mainProcess.getId(),\n                    Map.ofEntries(entry(\"simpleInput1\", \"singleInputValue\"),\n                            entry(\"myFile\", new FileInputValue(\"testFile\", \"testFile\".getBytes()))))\n                    .getId();\n            var archivedFileInput = await().until(() -> inTx(\n                    () -> getServiceAccessor().getContractDataService().getArchivedProcessDataValue(id, \"myFile\")),\n                    Objects::nonNull);\n            assertThat(archivedFileInput)\n                    .as(\"File input content of archived contract data should be null\")\n                    .isInstanceOf(FileInputValue.class)\n                    .extracting(\"content\")\n                    .isNull();\n            long userTask1 = waitForUserTask(id, \"userTask1\");\n            userTaskInstances.add(userTask1);\n            getProcessAPI().assignUserTask(userTask1, user.getId());\n            getProcessAPI().executeUserTask(userTask1,\n                    Collections.singletonMap(\"simpleInputTask\", \"simpleInputTaskValue\"));\n            waitForProcessToFinish(id);\n            processInstances.add(id);\n        }\n\n        List<SFlowNodeInstance> allFlowNodesBeforeDelete = getAllFlowNodes();\n        List<SAFlowNodeInstance> allArchFlowNodesBeforeDelete = getAllArchFlowNodes();\n        List<SAProcessInstance> allArchProcessInstancesBeforeDelete = getAllProcessInstances();\n\n        getProcessAPI().deleteArchivedProcessInstancesInAllStates(processInstances);\n\n        inTx(() -> {\n            for (Long userTaskInstance : userTaskInstances) {\n                try {\n                    getServiceAccessor().getContractDataService().getArchivedUserTaskDataValue(userTaskInstance,\n                            \"simpleInputTask\");\n                    fail(\"should have deleted archived contract data on activity instance\");\n                } catch (SContractDataNotFoundException e) {\n                    //ok\n                }\n            }\n            for (Long processInstance : processInstances) {\n                try {\n                    getServiceAccessor().getContractDataService().getArchivedProcessDataValue(processInstance,\n                            \"simpleInput1\");\n                    fail(\"should have deleted archived contract data on process instance\");\n                } catch (SContractDataNotFoundException e) {\n                    //ok\n                }\n            }\n            assertSoftly((soft) -> {\n                try {\n                    soft.assertThat(allFlowNodesBeforeDelete).isEmpty();\n                    soft.assertThat(searchAllArchProcessInstances()).isEmpty();\n                    soft.assertThat(searchAllArchFlowNodes()).isEmpty();\n                    soft.assertThat(\n                            getServiceAccessor().getCommentService().searchArchivedComments(new QueryOptions(0, 1000)))\n                            .isEmpty();\n                    soft.assertThat(getServiceAccessor().getConnectorInstanceService().searchArchivedConnectorInstance(\n                            new QueryOptions(0, 100, SAConnectorInstance.class, null, null),\n                            getServiceAccessor().getReadPersistenceService())).isEmpty();\n                    soft.assertThat(getServiceAccessor().getDocumentService()\n                            .getNumberOfArchivedDocuments(new QueryOptions(0, 100))).isEqualTo(0);\n                    for (SAFlowNodeInstance flowNodeInstance : allArchFlowNodesBeforeDelete) {\n                        soft.assertThat(getServiceAccessor().getDataInstanceService().getLocalSADataInstances(\n                                flowNodeInstance.getSourceObjectId(), ACTIVITY_INSTANCE.toString(), 0, 1)).isEmpty();\n                    }\n                    for (SAProcessInstance processInstance : allArchProcessInstancesBeforeDelete) {\n                        soft.assertThat(getServiceAccessor().getDataInstanceService().getLocalSADataInstances(\n                                processInstance.getSourceObjectId(), PROCESS_INSTANCE.toString(), 0, 1)).isEmpty();\n                    }\n                } catch (Exception e) {\n                    throw new RuntimeException(e);\n                }\n            });\n\n            return null;\n        });\n        disableAndDeleteProcess(asList(mainProcess, sub1, sub2));\n    }\n\n    @Test\n    public void should_delete_process_instance_currently_executing() throws Exception {\n        loginWithTechnicalUser();\n        User user = createUser(\"deleteProcessInstanceIT\", \"bpm\");\n        ProcessDefinition mainProcess = createMainProcessDefinition();\n        ProcessDefinition sub1 = createSubProcessDefinition1();\n        ProcessDefinition sub2 = createSubProcessDefinitionWithUserTask(user);\n\n        long processInstanceId = getProcessAPI().startProcessWithInputs(mainProcess.getId(),\n                Map.ofEntries(entry(\"simpleInput1\", \"singleInputValue\"),\n                        entry(\"myFile\", new FileInputValue(\"testFile\", \"testFile\".getBytes()))))\n                .getId();\n        waitForUserTask(processInstanceId, \"userTask1\");\n        waitForUserTask(\"taskOfSubProcess\");\n        waitForUserTask(\"taskOfSubProcess\");\n\n        final SLightDocument document = getDocument(processInstanceId);\n        assertThat(document).isNotNull();\n\n        Thread.sleep(200);\n\n        getProcessAPI().deleteProcessInstance(processInstanceId);\n\n        assertSoftly((soft) -> {\n            try {\n                soft.assertThat(getAllFlowNodes()).isEmpty();\n                soft.assertThat(getAllArchFlowNodes()).isEmpty();\n                soft.assertThat(getAllProcessInstances()).isEmpty();\n            } catch (Exception e) {\n                throw new RuntimeException(e);\n            }\n        });\n        assertThrows(SObjectNotFoundException.class, () -> getDocumentContent(document.getId()));\n\n        disableAndDeleteProcess(asList(mainProcess, sub1, sub2));\n    }\n\n    private SLightDocument getDocument(long processInstanceId) throws Exception {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final DocumentService documentService = serviceAccessor.getDocumentService();\n        final UserTransactionService userTransactionService = serviceAccessor.getUserTransactionService();\n        final Long documentId = userTransactionService.executeInTransaction(\n                () -> documentService.getMappedDocument(processInstanceId, \"myDoc\").getDocumentId());\n        return getDocumentContent(documentId);\n    }\n\n    private SLightDocument getDocumentContent(long documentId) throws Exception {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        return serviceAccessor.getUserTransactionService().executeInTransaction(\n                () -> serviceAccessor.getDocumentService().getDocument(documentId));\n    }\n\n    protected List<SAProcessInstance> getAllProcessInstances() throws Exception {\n        return getServiceAccessor()\n                .getUserTransactionService().executeInTransaction(this::searchAllArchProcessInstances);\n    }\n\n    protected List<SAFlowNodeInstance> getAllArchFlowNodes() throws Exception {\n        return getServiceAccessor().getUserTransactionService()\n                .executeInTransaction(this::searchAllArchFlowNodes);\n    }\n\n    protected List<SFlowNodeInstance> getAllFlowNodes() throws Exception {\n        return getServiceAccessor().getUserTransactionService()\n                .executeInTransaction(this::searchAllFlowNodes);\n    }\n\n    protected ProcessDefinition createMainProcessDefinition() throws Exception {\n        ProcessDefinitionBuilder mainProcessBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"mainProcess\", \"1.0\");\n        mainProcessBuilder.addContract()\n                .addInput(\"simpleInput1\", Type.TEXT, \"a simple input\")\n                .addFileInput(\"myFile\", \"A file input\");\n        mainProcessBuilder.addActor(\"actor\");\n        mainProcessBuilder.addUserTask(\"userTask1\", \"actor\").addContract().addInput(\"simpleInputTask\", Type.TEXT,\n                \"a simple task input\");\n        ActorMapping actorMapping = new ActorMapping();\n        Actor actor = new Actor(\"actor\");\n        actorMapping.addActor(actor);\n        actor.addUser(\"deleteProcessInstanceIT\");\n        mainProcessBuilder\n                .addDocumentDefinition(\"myDoc\").addInitialValue(docValueExpr())\n                .addStartEvent(\"start1\")\n                .addConnector(\"connector1\", \"myConnector\", \"1.0\", ConnectorEvent.ON_ENTER)\n                .addAutomaticTask(\"autoWithConnector\")\n                .addConnector(\"connector1\", \"myConnector\", \"1.0\", ConnectorEvent.ON_ENTER)\n                .addAutomaticTask(\"autoWithData\").addShortTextData(\"activityData\", s(\"activityDataValue\"))\n                .addCallActivity(\"call1\", s(\"subProcess\"), s(\"1.0\"));\n        mainProcessBuilder.addCallActivity(\"call2\", s(\"subProcess\"), s(\"2.0\"))\n                .addMultiInstance(false, new ExpressionBuilder().createConstantIntegerExpression(2));\n        return deployAndEnableProcess(barWithConnector(mainProcessBuilder\n                .getProcess()).setActorMapping(actorMapping).done());\n    }\n\n    protected ProcessDefinition createSubProcessDefinition1() throws Exception {\n        return getProcessAPI().deployAndEnableProcess(barWithConnector(new ProcessDefinitionBuilder()\n                .createNewInstance(\"subProcess\", \"1.0\")\n                .addDocumentDefinition(\"myDoc\").addInitialValue(docValueExpr())\n                .addStartEvent(\"start1\")\n                .addConnector(\"connector1\", \"myConnector\", \"1.0\", ConnectorEvent.ON_ENTER)\n                .addAutomaticTask(\"autoWithConnector\")\n                .addConnector(\"connector1\", \"myConnector\", \"1.0\", ConnectorEvent.ON_ENTER)\n                .addAutomaticTask(\"autoWithData\").addShortTextData(\"activityData\", s(\"activityDataValue\"))\n                .addCallActivity(\"sub2\", s(\"subProcess\"), s(\"2.0\")).getProcess()).done());\n    }\n\n    protected ProcessDefinition createSubProcessDefinition2() throws Exception {\n        return getProcessAPI().deployAndEnableProcess(barWithConnector(new ProcessDefinitionBuilder()\n                .createNewInstance(\"subProcess\", \"2.0\")\n                .addDocumentDefinition(\"myDoc\").addInitialValue(docValueExpr())\n                .addStartEvent(\"start1\")\n                .addConnector(\"connector1\", \"myConnector\", \"1.0\", ConnectorEvent.ON_ENTER)\n                .addAutomaticTask(\"autoWithConnector\")\n                .addConnector(\"connector1\", \"myConnector\", \"1.0\", ConnectorEvent.ON_ENTER)\n                .addAutomaticTask(\"autoWithData\").addShortTextData(\"activityData\", s(\"activityDataValue\")).getProcess())\n                .done());\n    }\n\n    protected ProcessDefinition createSubProcessDefinitionWithUserTask(User user) throws Exception {\n        return deployAndEnableProcessWithActor(new ProcessDefinitionBuilder()\n                .createNewInstance(\"subProcess\", \"2.0\")\n                .addActor(\"actor\")\n                .addStartEvent(\"start1\")\n                .addUserTask(\"taskOfSubProcess\", \"actor\")\n                .addTransition(\"start1\", \"taskOfSubProcess\").getProcess(),\n                \"actor\",\n                user);\n    }\n\n    private List<SAProcessInstance> searchAllArchProcessInstances() throws SBonitaReadException {\n        return getServiceAccessor().getProcessInstanceService()\n                .searchArchivedProcessInstances(new QueryOptions(0, 1000));\n    }\n\n    private List<SAFlowNodeInstance> searchAllArchFlowNodes()\n            throws org.bonitasoft.engine.persistence.SBonitaReadException {\n        return getServiceAccessor().getActivityInstanceService()\n                .searchArchivedFlowNodeInstances(SAFlowNodeInstance.class, new QueryOptions(0, 1000));\n    }\n\n    private List<SFlowNodeInstance> searchAllFlowNodes()\n            throws org.bonitasoft.engine.persistence.SBonitaReadException {\n        return getServiceAccessor().getActivityInstanceService()\n                .searchFlowNodeInstances(SFlowNodeInstance.class, new QueryOptions(0, 1000));\n    }\n\n    private Expression docValueExpr() throws InvalidExpressionException {\n        return new ExpressionBuilder().createGroovyScriptExpression(\"docValue\",\n                \"new org.bonitasoft.engine.bpm.document.DocumentValue(\\\"hello3\\\".getBytes(),\\\"plain/text\\\",\\\"file1.txt\\\")\",\n                DocumentValue.class.getName());\n    }\n\n    private BusinessArchiveBuilder barWithConnector(DesignProcessDefinition process) throws Exception {\n        byte[] connectorImplementationFile = BuildTestUtil.buildConnectorImplementationFile(\"myConnector\", \"1.0\",\n                \"impl1\", \"1.0\", AddCommentConnector.class.getName());\n        return new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(process)\n                .addConnectorImplementation(new BarResource(\"connector.impl\", connectorImplementationFile));\n    }\n\n    private Expression s(String s) throws InvalidExpressionException {\n        return new ExpressionBuilder().createConstantStringExpression(s);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/business/application/importer/ApplicationImporterIT.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.importer;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.ImportStatus;\nimport org.bonitasoft.engine.api.impl.SessionInfos;\nimport org.bonitasoft.engine.bpm.CommonBPMServicesTest;\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.model.SApplication;\nimport org.bonitasoft.engine.business.application.model.SApplicationState;\nimport org.bonitasoft.engine.page.PageService;\nimport org.bonitasoft.engine.page.SPage;\nimport org.bonitasoft.engine.profile.ProfileService;\nimport org.bonitasoft.engine.profile.model.SProfile;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class ApplicationImporterIT extends CommonBPMServicesTest {\n\n    private ApplicationImporter applicationImporter;\n    private ApplicationService applicationService;\n    private PageService pageService;\n    private ProfileService profileService;\n\n    private static final String APP_1_TOKEN = \"app1\";\n    private static final String APP_2_TOKEN = \"app2\";\n    private static final String APP_3_TOKEN = \"app3\";\n    private static final String APP_4_TOKEN = \"app4\";\n\n    @Before\n    public void setUp() {\n        applicationImporter = getServiceAccessor().getApplicationImporter();\n        applicationService = getServiceAccessor().getApplicationService();\n        pageService = getServiceAccessor().getPageService();\n        profileService = getServiceAccessor().getProfileService();\n    }\n\n    @After\n    public void tearDown() throws Exception {\n        deleteApplication(APP_1_TOKEN);\n        deleteApplication(APP_2_TOKEN);\n        deleteApplication(APP_3_TOKEN);\n        deleteApplication(APP_4_TOKEN);\n        getTransactionService().executeInTransaction(() -> {\n            SPage page = pageService.getPageByName(\"custompage_mynewcustompage\");\n            if (page != null) {\n                pageService.deletePage(page.getId());\n            }\n            return page;\n        });\n    }\n\n    private void deleteApplication(String appToken) throws Exception {\n        getTransactionService().executeInTransaction(() -> {\n            SApplication app = applicationService.getApplicationByToken(appToken);\n            if (app != null) {\n                applicationService.forceDeleteApplication(app);\n            }\n            return app;\n        });\n    }\n\n    @Test\n    public void one_application_link_should_be_imported_successfully() throws Exception {\n        //given\n        String xmlToImport = \"/applications-importer/oneApplicationLink.xml\";\n\n        // ensure applications did not exist initially:\n        assertAppNotExists(APP_1_TOKEN);\n\n        //when\n        List<ImportStatus> importStatuses = importApplicationsFromXml(xmlToImport);\n\n        //then\n        assertThat(importStatuses).hasSize(1);\n        assertThat(importStatuses.get(0).getName()).isEqualTo(APP_1_TOKEN);\n        assertThat(importStatuses.get(0).getStatus()).isEqualTo(ImportStatus.Status.ADDED);\n        assertThat(importStatuses.get(0).getErrors()).isEmpty();\n\n        assertAppLink1();\n    }\n\n    @Test\n    public void multiple_application_links_should_be_imported_successfully() throws Exception {\n        //given\n        String xmlToImport = \"/applications-importer/multipleApplicationLinks.xml\";\n\n        // ensure applications did not exist initially:\n        assertAppNotExists(APP_1_TOKEN);\n        assertAppNotExists(APP_2_TOKEN);\n\n        //when\n        List<ImportStatus> importStatuses = importApplicationsFromXml(xmlToImport);\n\n        //then\n        assertThat(importStatuses).hasSize(2);\n        assertThat(importStatuses.get(0).getName()).isEqualTo(APP_1_TOKEN);\n        assertThat(importStatuses.get(0).getStatus()).isEqualTo(ImportStatus.Status.ADDED);\n        assertThat(importStatuses.get(0).getErrors()).isEmpty();\n        assertThat(importStatuses.get(1).getName()).isEqualTo(APP_2_TOKEN);\n        assertThat(importStatuses.get(1).getStatus()).isEqualTo(ImportStatus.Status.ADDED);\n        assertThat(importStatuses.get(1).getErrors()).isEmpty();\n\n        assertAppLink1();\n        assertAppLink2();\n    }\n\n    @Test\n    public void mixed_legacy_and_application_links_should_be_imported_successfully() throws Exception {\n        //given\n        String xmlToImport = \"/applications-importer/mixedLegacyAndApplicationLinks.xml\";\n\n        // ensure applications did not exist initially:\n        assertAppNotExists(APP_1_TOKEN);\n        assertAppNotExists(APP_2_TOKEN);\n        assertAppNotExists(APP_3_TOKEN);\n        assertAppNotExists(APP_4_TOKEN);\n\n        // create page mandatory for app3\n        createDummyPage();\n\n        //when\n        List<ImportStatus> importStatuses = importApplicationsFromXml(xmlToImport);\n\n        //then\n        assertThat(importStatuses).hasSize(4);\n        assertThat(importStatuses.get(0).getName()).isEqualTo(APP_1_TOKEN);\n        assertThat(importStatuses.get(0).getStatus()).isEqualTo(ImportStatus.Status.ADDED);\n        assertThat(importStatuses.get(0).getErrors()).isEmpty();\n        assertThat(importStatuses.get(1).getName()).isEqualTo(APP_2_TOKEN);\n        assertThat(importStatuses.get(1).getStatus()).isEqualTo(ImportStatus.Status.ADDED);\n        assertThat(importStatuses.get(1).getErrors()).isEmpty();\n        assertThat(importStatuses.get(2).getName()).isEqualTo(APP_3_TOKEN);\n        assertThat(importStatuses.get(2).getStatus()).isEqualTo(ImportStatus.Status.ADDED);\n        assertThat(importStatuses.get(2).getErrors()).isEmpty();\n        assertThat(importStatuses.get(3).getName()).isEqualTo(APP_4_TOKEN);\n        assertThat(importStatuses.get(3).getStatus()).isEqualTo(ImportStatus.Status.ADDED);\n        assertThat(importStatuses.get(3).getErrors()).isEmpty();\n\n        assertAppLink1();\n        assertAppLink2();\n        assertAppLink3();\n        assertAppLink4();\n    }\n\n    @Test\n    public void mixed_legacy_and_application_links_should_be_imported_successfully_twice() throws Exception {\n        //given\n        String xmlToImport = \"/applications-importer/mixedLegacyAndApplicationLinks.xml\";\n        ApplicationImportStrategy updateStrategy = (a1, a2) -> ApplicationImportStrategy.ImportStrategy.REPLACE;\n\n        // create page mandatory for app3\n        createDummyPage();\n\n        //when\n        List<ImportStatus> importStatuses = importApplicationsFromXml(xmlToImport);\n        assertThat(importStatuses).hasSize(4);\n        // and re-import\n        try (var xmlAsStream = this.getClass().getResourceAsStream(xmlToImport)) {\n            assertThat(xmlAsStream).isNotNull();\n            importStatuses = getTransactionService()\n                    .executeInTransaction(() -> applicationImporter.importApplications(xmlAsStream.readAllBytes(), null,\n                            null, SessionInfos.getUserIdFromSession(), updateStrategy));\n        }\n\n        //then\n        assertThat(importStatuses).hasSize(4);\n        assertThat(importStatuses.get(0).getName()).isEqualTo(APP_1_TOKEN);\n        assertThat(importStatuses.get(0).getStatus()).isEqualTo(ImportStatus.Status.REPLACED);\n        assertThat(importStatuses.get(0).getErrors()).isEmpty();\n        assertThat(importStatuses.get(1).getName()).isEqualTo(APP_2_TOKEN);\n        assertThat(importStatuses.get(1).getStatus()).isEqualTo(ImportStatus.Status.REPLACED);\n        assertThat(importStatuses.get(1).getErrors()).isEmpty();\n        assertThat(importStatuses.get(2).getName()).isEqualTo(APP_3_TOKEN);\n        assertThat(importStatuses.get(2).getStatus()).isEqualTo(ImportStatus.Status.REPLACED);\n        assertThat(importStatuses.get(2).getErrors()).isEmpty();\n        assertThat(importStatuses.get(3).getName()).isEqualTo(APP_4_TOKEN);\n        assertThat(importStatuses.get(3).getStatus()).isEqualTo(ImportStatus.Status.REPLACED);\n        assertThat(importStatuses.get(3).getErrors()).isEmpty();\n\n        assertAppLink1();\n        assertAppLink2();\n        assertAppLink3();\n        assertAppLink4();\n\n    }\n\n    private List<ImportStatus> importApplicationsFromXml(String xmlToImport) throws Exception {\n        try (var xmlAsStream = this.getClass().getResourceAsStream(xmlToImport)) {\n            assertThat(xmlAsStream).isNotNull();\n            return getTransactionService()\n                    .executeInTransaction(() -> applicationImporter.importApplications(xmlAsStream.readAllBytes(), null,\n                            null, SessionInfos.getUserIdFromSession(), null));\n        }\n    }\n\n    private void assertAppNotExists(String appToken) throws Exception {\n        SApplication existingApp1 = getTransactionService()\n                .executeInTransaction(() -> applicationService.getApplicationByToken(appToken));\n        assertThat(existingApp1).isNull();\n    }\n\n    private void assertAppLink1() throws Exception {\n        SApplication app = getTransactionService()\n                .executeInTransaction(() -> applicationService.getApplicationByToken(APP_1_TOKEN));\n        assertThat(app).isNotNull();\n        assertThat(app.getDisplayName()).isEqualTo(\"Application 1\");\n        assertThat(app.getDescription()).isEqualTo(\"Description of Application 1\");\n        assertThat(app.getVersion()).isEqualTo(\"1.0\");\n        assertThat(app.getIconPath()).isEqualTo(\"/app1.jpg\");\n        assertThat(app.getCreatedBy()).isEqualTo(-1L);\n        assertThat(app.getState()).isEqualTo(SApplicationState.ACTIVATED.name());\n        assertThat(getProfile(app.getProfileId()).getName()).isEqualTo(\"User\");\n        assertThat(app.getInternalProfile()).isNull();\n        assertThat(app.getHomePageId()).isNull();\n        assertThat(app.getLayoutId()).isNull();\n        assertThat(app.getThemeId()).isNull();\n        assertThat(app.isEditable()).isTrue();\n        assertThat(app.isLink()).isTrue();\n    }\n\n    private SProfile getProfile(Long profileId) throws Exception {\n        return getTransactionService().executeInTransaction(() -> profileService.getProfile(profileId));\n    }\n\n    private void assertAppLink2() throws Exception {\n        SApplication app = getTransactionService()\n                .executeInTransaction(() -> applicationService.getApplicationByToken(APP_2_TOKEN));\n        assertThat(app).isNotNull();\n        assertThat(app.getDisplayName()).isEqualTo(\"Application 2\");\n        assertThat(app.getDescription()).isNull();\n        assertThat(app.getVersion()).isEqualTo(\"1.1\");\n        assertThat(app.getIconPath()).isNull();\n        assertThat(app.getCreatedBy()).isEqualTo(-1L);\n        assertThat(app.getState()).isEqualTo(SApplicationState.DEACTIVATED.name());\n        assertThat(app.getProfileId()).isNull();\n        assertThat(app.getInternalProfile()).isNull();\n        assertThat(app.getHomePageId()).isNull();\n        assertThat(app.getLayoutId()).isNull();\n        assertThat(app.getThemeId()).isNull();\n        assertThat(app.isEditable()).isTrue();\n        assertThat(app.isLink()).isTrue();\n    }\n\n    private void assertAppLink3() throws Exception {\n        SApplication app = getTransactionService()\n                .executeInTransaction(() -> applicationService.getApplicationByToken(APP_3_TOKEN));\n        assertThat(app).isNotNull();\n        assertThat(app.getDisplayName()).isEqualTo(\"Application 3\");\n        assertThat(app.getDescription()).isEqualTo(\"Description of Application 3\");\n        assertThat(app.getVersion()).isEqualTo(\"2.0\");\n        assertThat(app.getIconPath()).isEqualTo(\"/app3.jpg\");\n        assertThat(app.getCreatedBy()).isEqualTo(-1L);\n        assertThat(app.getState()).isEqualTo(SApplicationState.ACTIVATED.name());\n        assertThat(app.getHomePageId()).isNotNull();\n        assertThat(getProfile(app.getProfileId()).getName()).isEqualTo(\"User\");\n        assertThat(app.getInternalProfile()).isNull();\n        assertThat(app.getLayoutId()).isNotNull();\n        assertThat(app.getThemeId()).isNotNull();\n        assertThat(app.isEditable()).isTrue();\n        assertThat(app.isLink()).isFalse();\n    }\n\n    private void assertAppLink4() throws Exception {\n        SApplication app = getTransactionService()\n                .executeInTransaction(() -> applicationService.getApplicationByToken(APP_4_TOKEN));\n        assertThat(app).isNotNull();\n        assertThat(app.getDisplayName()).isEqualTo(\"Application 4\");\n        assertThat(app.getDescription()).isNull();\n        assertThat(app.getVersion()).isEqualTo(\"2.0\");\n        assertThat(app.getIconPath()).isNull();\n        assertThat(app.getCreatedBy()).isEqualTo(-1L);\n        assertThat(app.getState()).isEqualTo(SApplicationState.DEACTIVATED.name());\n        assertThat(app.getProfileId()).isNull();\n        assertThat(app.getInternalProfile()).isNull();\n        assertThat(app.getHomePageId()).isNull();\n        assertThat(app.getLayoutId()).isNotNull();\n        assertThat(app.getThemeId()).isNotNull();\n        assertThat(app.isEditable()).isTrue();\n        assertThat(app.isLink()).isFalse();\n    }\n\n    private void createDummyPage() throws Exception {\n        try (var contentStream = this.getClass().getResourceAsStream(\"/applications-importer/dummy-bizapp-page.zip\")) {\n            assertThat(contentStream).isNotNull();\n            getTransactionService().executeInTransaction(() -> pageService.addPage(contentStream.readAllBytes(),\n                    \"custompage_mynewcustompage\", SessionInfos.getUserIdFromSession()));\n        }\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/business/data/BDRepositoryLocalIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.io.File;\nimport java.io.Serializable;\nimport java.math.BigDecimal;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.commons.io.FileUtils;\nimport org.bonitasoft.engine.CommonAPIIT;\nimport org.bonitasoft.engine.bdm.BusinessObjectModelConverter;\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.model.Query;\nimport org.bonitasoft.engine.bdm.model.field.FieldType;\nimport org.bonitasoft.engine.bdm.model.field.RelationField;\nimport org.bonitasoft.engine.bdm.model.field.SimpleField;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.business.data.impl.EntityManagerFactoryAware;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.expression.InvalidExpressionException;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.operation.LeftOperandBuilder;\nimport org.bonitasoft.engine.operation.OperationBuilder;\nimport org.bonitasoft.engine.operation.OperatorType;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.ServiceAccessorSingleton;\nimport org.bonitasoft.platform.setup.PlatformSetup;\nimport org.junit.After;\nimport org.junit.Assume;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.TemporaryFolder;\n\npublic class BDRepositoryLocalIT extends CommonAPIIT {\n\n    private static final String FIND_BY_FIRST_NAME_AND_LAST_NAME_NEW_ORDER = \"findByFirstNameAndLastNameNewOrder\";\n\n    private static final String BIZ_GRENOBLE_ADDRESS = \"bizGrenobleAddress\";\n\n    private static final String BIZ_SF_ADDRESS = \"bizSfAddress\";\n\n    private static final String BIZ_ROME_ADDRESS = \"bizRomeAddress\";\n\n    private static final String BIZ_EMPLOYEE = \"bizEmployee\";\n\n    private static final String PROCESS_NAME = \"lazy\";\n\n    private static final String VERSION = \"1.0\";\n\n    private static final String TASK_AUTOMATIC_TASK_TO_INIT_BIZ_DATA = \"automaticTaskToInitBizData\";\n\n    private static final String TASK_TO_CALL_JAVA_METHOD_OPERATION = \"automaticTaskToCallJavaMethodOperation\";\n\n    private static final String TASK_HUMAN_TASK = \"humanTask\";\n\n    private static final String CITY_SF = \"S.F.\";\n\n    private static final String CITY_GRENOBLE = \"Grenoble\";\n\n    private static final String CITY_ROME = \"Rome\";\n\n    private static final String COUNTRY_ITALY = \"Italy\";\n\n    private static final String COUNTRY_FRANCE = \"France\";\n\n    private static final String COUNTRY_USA = \"USA\";\n\n    private static final String BDM_PACKAGE_PREFIX = \"com.company.model\";\n\n    public static final String PRODUCT_QUALIFIED_NAME = BDM_PACKAGE_PREFIX + \".pojo.Product\";\n\n    public static final String PRODUCT_CATALOG_QUALIFIED_NAME = BDM_PACKAGE_PREFIX + \".pojo.ProductCatalog\";\n\n    private static final String ADDRESS_QUALIFIED_NAME = BDM_PACKAGE_PREFIX + \".pojo.Address\";\n\n    private static final String EMPLOYEE_QUALIFIED_NAME = BDM_PACKAGE_PREFIX + \".pojo.Employee\";\n\n    private static final String COUNTRY_QUALIFIED_NAME = BDM_PACKAGE_PREFIX + \".pojo.Country\";\n\n    public static final String PERSON_QUALIFIED_NAME = BDM_PACKAGE_PREFIX + \".pojo.Person\";\n\n    private User matti;\n\n    private File clientFolder;\n\n    private ClassLoader contextClassLoaderBeforeAddingBPMClientZip;\n\n    @Rule\n    public TemporaryFolder temporaryFolder = new TemporaryFolder();\n\n    @Before\n    public void setUp() throws Exception {\n        clientFolder = temporaryFolder.newFolder();\n        loginWithTechnicalUser();\n        matti = createUser(\"matti\", \"bpm\");\n\n        final BusinessObjectModelConverter converter = new BusinessObjectModelConverter();\n        final byte[] zip = converter.zip(buildCustomBOM());\n        getTenantAdministrationAPI().pause();\n        getTenantAdministrationAPI().cleanAndUninstallBusinessDataModel();\n        getTenantAdministrationAPI().updateBusinessDataModel(zip);\n        getTenantAdministrationAPI().resume();\n\n        // needed for remote testing\n        addClientBDMZipToClassLoader();\n    }\n\n    @After\n    public void tearDown() throws Exception {\n        resumeClassloader();\n        try {\n            FileUtils.deleteDirectory(clientFolder);\n        } catch (final Exception e) {\n            clientFolder.deleteOnExit();\n        }\n        if (!getTenantAdministrationAPI().isPaused()) {\n            getTenantAdministrationAPI().pause();\n            getTenantAdministrationAPI().cleanAndUninstallBusinessDataModel();\n            getTenantAdministrationAPI().resume();\n        }\n        resumeClassloader();\n\n        deleteUser(matti);\n        logout();\n    }\n\n    private void resumeClassloader() {\n        Thread.currentThread().setContextClassLoader(contextClassLoaderBeforeAddingBPMClientZip);\n    }\n\n    @Test\n    public void should_get_lazy_object_outside_a_transaction() throws Exception {\n        // given\n        final Expression addressGrenobleExpression = createGrovyExpressionThatCreateAddressWithCityName(CITY_GRENOBLE,\n                COUNTRY_FRANCE);\n        final Expression addressSfExpression = createGrovyExpressionThatCreateAddressWithCityName(CITY_SF, COUNTRY_USA);\n        final Expression addressRomeExpression = createGrovyExpressionThatCreateAddressWithCityName(CITY_ROME,\n                COUNTRY_ITALY);\n\n        final Expression employeeExpression = createGroovyExpressionThatCreateEmployeeWithOneAddress(\n                BIZ_GRENOBLE_ADDRESS);\n\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance(\n                PROCESS_NAME, VERSION);\n\n        processDefinitionBuilder.addBusinessData(BIZ_EMPLOYEE, EMPLOYEE_QUALIFIED_NAME, null);\n        processDefinitionBuilder.addBusinessData(BIZ_GRENOBLE_ADDRESS, ADDRESS_QUALIFIED_NAME,\n                addressGrenobleExpression);\n        processDefinitionBuilder.addBusinessData(BIZ_SF_ADDRESS, ADDRESS_QUALIFIED_NAME, addressSfExpression);\n        processDefinitionBuilder.addBusinessData(BIZ_ROME_ADDRESS, ADDRESS_QUALIFIED_NAME, addressRomeExpression);\n\n        processDefinitionBuilder.addActor(ACTOR_NAME);\n\n        // create employee and addresses\n        processDefinitionBuilder.addAutomaticTask(TASK_AUTOMATIC_TASK_TO_INIT_BIZ_DATA)\n                .addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand(BIZ_SF_ADDRESS),\n                        OperatorType.ASSIGNMENT, null, null,\n                        addressSfExpression)\n                .addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand(BIZ_GRENOBLE_ADDRESS),\n                        OperatorType.ASSIGNMENT, null, null,\n                        addressGrenobleExpression)\n                .addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand(BIZ_ROME_ADDRESS),\n                        OperatorType.ASSIGNMENT, null, null,\n                        addressRomeExpression)\n                .addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand(BIZ_EMPLOYEE),\n                        OperatorType.ASSIGNMENT, null, null,\n                        employeeExpression);\n\n        // call java operation to add address to employee\n        processDefinitionBuilder.addAutomaticTask(TASK_TO_CALL_JAVA_METHOD_OPERATION)\n                .addOperation(\n                        new OperationBuilder().createBusinessDataSetAttributeOperation(BIZ_EMPLOYEE, \"addToAddresses\",\n                                ADDRESS_QUALIFIED_NAME,\n                                new ExpressionBuilder().createBusinessDataExpression(BIZ_ROME_ADDRESS,\n                                        ADDRESS_QUALIFIED_NAME)));\n\n        // waiting task\n        processDefinitionBuilder.addUserTask(TASK_HUMAN_TASK, ACTOR_NAME);\n\n        // transitions\n        processDefinitionBuilder.addTransition(TASK_AUTOMATIC_TASK_TO_INIT_BIZ_DATA,\n                TASK_TO_CALL_JAVA_METHOD_OPERATION);\n        processDefinitionBuilder.addTransition(TASK_TO_CALL_JAVA_METHOD_OPERATION, TASK_HUMAN_TASK);\n\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(),\n                ACTOR_NAME, matti);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId());\n        final HumanTaskInstance humanTaskInstance = waitForUserTaskAndGetIt(processInstance, TASK_HUMAN_TASK);\n\n        // then\n        verifyLazyAddressesCount(humanTaskInstance, 2);\n        verifySimpleFieldInAddresses(humanTaskInstance, CITY_GRENOBLE);\n        verifyEagerCountryFieldInAddresses(humanTaskInstance, COUNTRY_FRANCE);\n\n        // cleanup\n        disableAndDeleteProcess(definition.getId());\n    }\n\n    private void addClientBDMZipToClassLoader() throws Exception {\n        contextClassLoaderBeforeAddingBPMClientZip = Thread.currentThread().getContextClassLoader();\n        final byte[] clientBDMZip = getTenantAdministrationAPI().getClientBDMZip();\n        final ClassLoader classLoaderWithBDM = new ClassloaderRefresher().loadClientModelInClassloader(clientBDMZip,\n                contextClassLoaderBeforeAddingBPMClientZip,\n                EMPLOYEE_QUALIFIED_NAME, clientFolder);\n        Thread.currentThread().setContextClassLoader(classLoaderWithBDM);\n    }\n\n    private void verifyLazyAddressesCount(final HumanTaskInstance humanTaskInstance, final int expectedCount)\n            throws Exception {\n\n        final Map<String, Serializable> map = new HashMap<>();\n        final Map<Expression, Map<String, Serializable>> expressions = new HashMap<>();\n\n        final Expression createQueryBusinessDataExpression = new ExpressionBuilder().createQueryBusinessDataExpression(\n                \"expression Name\",\n                \"Employee.\" + FIND_BY_FIRST_NAME_AND_LAST_NAME_NEW_ORDER, EMPLOYEE_QUALIFIED_NAME,\n                new ExpressionBuilder().createConstantStringExpression(\"firstName\", \"Alphonse\"),\n                new ExpressionBuilder().createConstantStringExpression(\"lastName\", \"Dupond\"));\n\n        final Expression countExpression = new ExpressionBuilder().createGroovyScriptExpression(\"countExpression\",\n                \"myEmployee.getAddresses().size()\", Integer.class.getName(),\n                new ExpressionBuilder().createInputExpression(\"myEmployee\", EMPLOYEE_QUALIFIED_NAME));\n        expressions.put(createQueryBusinessDataExpression, map);\n\n        final Map<String, Serializable> evaluateExpressionsAtProcessInstanciation = getProcessAPI()\n                .evaluateExpressionsOnActivityInstance(\n                        humanTaskInstance.getId(), expressions);\n\n        final Serializable businessData = evaluateExpressionsAtProcessInstanciation\n                .get(createQueryBusinessDataExpression.getName());\n\n        final Map<Expression, Map<String, Serializable>> expressions2 = new HashMap<>();\n        expressions2.put(countExpression, Collections.singletonMap(\"myEmployee\", businessData));\n\n        final Map<String, Serializable> evaluateExpressionsOnActivityInstance = getProcessAPI()\n                .evaluateExpressionsOnActivityInstance(\n                        humanTaskInstance.getId(), expressions2);\n\n        final Serializable serializable = evaluateExpressionsOnActivityInstance.get(\"countExpression\");\n        assertThat(serializable).as(\"should get \" + expectedCount + \" address count\").isEqualTo(expectedCount);\n\n    }\n\n    private void verifyEagerCountryFieldInAddresses(final HumanTaskInstance humanTaskInstance,\n            final String expectedCountry) throws Exception {\n\n        final Map<String, Serializable> map = new HashMap<>();\n        final Map<Expression, Map<String, Serializable>> mapGetEmployee = new HashMap<>();\n\n        final Expression getEmployeeExpression = new ExpressionBuilder().createQueryBusinessDataExpression(\n                \"expression Name\",\n                \"Employee.\" + FIND_BY_FIRST_NAME_AND_LAST_NAME_NEW_ORDER, EMPLOYEE_QUALIFIED_NAME,\n                new ExpressionBuilder().createConstantStringExpression(\"firstName\", \"Alphonse\"),\n                new ExpressionBuilder().createConstantStringExpression(\"lastName\", \"Dupond\"));\n        final String queryName = getEmployeeExpression.getName();\n\n        final String getCountryExpressionName = \"country\";\n        final String script = \"myEmployee.getAddresses().get(0).getCountry().getName()\";\n        final Expression getCountryExpression = new ExpressionBuilder().createGroovyScriptExpression(\n                getCountryExpressionName,\n                script, String.class.getName(),\n                new ExpressionBuilder().createInputExpression(\"myEmployee\", EMPLOYEE_QUALIFIED_NAME));\n        mapGetEmployee.put(getEmployeeExpression, map);\n\n        final Map<String, Serializable> getEmployeeResultMap = getProcessAPI().evaluateExpressionsOnActivityInstance(\n                humanTaskInstance.getId(), mapGetEmployee);\n\n        final Serializable returnedEmployee = getEmployeeResultMap.get(queryName);\n\n        final Map<Expression, Map<String, Serializable>> mapGetCountry = new HashMap<>();\n        mapGetCountry.put(getCountryExpression, Collections.singletonMap(\"myEmployee\", returnedEmployee));\n\n        final Map<String, Serializable> getCountryResultMap = getProcessAPI().evaluateExpressionsOnActivityInstance(\n                humanTaskInstance.getId(), mapGetCountry);\n\n        final Serializable serializable = getCountryResultMap.get(getCountryExpressionName);\n        assertThat(serializable).as(\"should get \" + expectedCountry + \" address count\").isEqualTo(expectedCountry);\n\n    }\n\n    private void verifySimpleFieldInAddresses(final HumanTaskInstance humanTaskInstance, final String expectedCity)\n            throws Exception {\n\n        final Map<String, Serializable> map = new HashMap<>();\n        final Map<Expression, Map<String, Serializable>> mapGetEmployee = new HashMap<>();\n\n        final Expression getEmployeeExpression = new ExpressionBuilder().createQueryBusinessDataExpression(\n                \"expression Name\",\n                \"Employee.\" + FIND_BY_FIRST_NAME_AND_LAST_NAME_NEW_ORDER, EMPLOYEE_QUALIFIED_NAME,\n                new ExpressionBuilder().createConstantStringExpression(\"firstName\", \"Alphonse\"),\n                new ExpressionBuilder().createConstantStringExpression(\"lastName\", \"Dupond\"));\n        final String queryName = getEmployeeExpression.getName();\n\n        final String cityExpression = \"city\";\n        final Expression getCountryExpression = new ExpressionBuilder().createGroovyScriptExpression(cityExpression,\n                \"myEmployee.getAddresses().get(0).getCity()\", String.class.getName(),\n                new ExpressionBuilder().createInputExpression(\"myEmployee\", EMPLOYEE_QUALIFIED_NAME));\n        mapGetEmployee.put(getEmployeeExpression, map);\n\n        final Map<String, Serializable> getEmployeeResultMap = getProcessAPI().evaluateExpressionsOnActivityInstance(\n                humanTaskInstance.getId(), mapGetEmployee);\n\n        final Serializable returnedEmployee = getEmployeeResultMap.get(queryName);\n\n        final Map<Expression, Map<String, Serializable>> mapGetCountry = new HashMap<>();\n        mapGetCountry.put(getCountryExpression, Collections.singletonMap(\"myEmployee\", returnedEmployee));\n\n        final Map<String, Serializable> cityResultMap = getProcessAPI().evaluateExpressionsOnActivityInstance(\n                humanTaskInstance.getId(), mapGetCountry);\n\n        final Serializable cityResult = cityResultMap.get(cityExpression);\n\n        assertThat(cityResult).as(\"should get city name\" + expectedCity).isEqualTo(expectedCity);\n\n    }\n\n    private Expression createGroovyExpressionThatCreateEmployeeWithOneAddress(final String businessDataAddressName)\n            throws InvalidExpressionException {\n        String script = \"import \" + EMPLOYEE_QUALIFIED_NAME + \"; import \" + ADDRESS_QUALIFIED_NAME +\n                \"; Employee e = new Employee(); e.firstName = 'Alphonse'; e.lastName = 'Dupond'; e.addToAddresses(\" +\n                businessDataAddressName +\n                \"); return e;\";\n        return new ExpressionBuilder().createGroovyScriptExpression(\"createNewEmployee\", script,\n                EMPLOYEE_QUALIFIED_NAME,\n                createBusinessDataExpressionWithName(businessDataAddressName));\n    }\n\n    private Expression createBusinessDataExpressionWithName(final String businessDataName)\n            throws InvalidExpressionException {\n        Expression createBusinessDataExpression;\n        createBusinessDataExpression = new ExpressionBuilder().createBusinessDataExpression(businessDataName,\n                ADDRESS_QUALIFIED_NAME);\n        return createBusinessDataExpression;\n    }\n\n    private Expression createGrovyExpressionThatCreateAddressWithCityName(final String city, final String country)\n            throws InvalidExpressionException {\n        final Expression addressExpression;\n        final StringBuilder builder = new StringBuilder();\n        builder.append(\"import \")\n                .append(ADDRESS_QUALIFIED_NAME)\n                .append(\"; \")\n                .append(\"import \")\n                .append(COUNTRY_QUALIFIED_NAME)\n                .append(\"; \")\n                .append(\"Country country = new Country(); \")\n                .append(\"country.name='\")\n                .append(country)\n                .append(\"'; \")\n                .append(\"Address address = new Address();\")\n                .append(\"address.street='32, rue Gustave Eiffel'; \")\n                .append(\"address.city='\")\n                .append(city)\n                .append(\"'; \")\n                .append(\"address.country = country; \")\n                .append(\"address;\");\n\n        addressExpression = new ExpressionBuilder().createGroovyScriptExpression(\"createNewAddress\",\n                builder.toString(),\n                ADDRESS_QUALIFIED_NAME);\n        return addressExpression;\n    }\n\n    private BusinessObjectModel buildCustomBOM() {\n        final SimpleField name = new SimpleField();\n        name.setName(\"name\");\n        name.setType(FieldType.STRING);\n\n        final BusinessObject countryBO = new BusinessObject();\n        countryBO.setQualifiedName(COUNTRY_QUALIFIED_NAME);\n        countryBO.addField(name);\n\n        final SimpleField street = new SimpleField();\n        street.setName(\"street\");\n        street.setType(FieldType.STRING);\n\n        final SimpleField city = new SimpleField();\n        city.setName(\"city\");\n        city.setType(FieldType.STRING);\n\n        final RelationField country = new RelationField();\n        country.setType(RelationField.Type.AGGREGATION);\n        country.setFetchType(RelationField.FetchType.EAGER);\n        country.setName(\"country\");\n        country.setCollection(Boolean.FALSE);\n        country.setNullable(Boolean.TRUE);\n        country.setReference(countryBO);\n\n        final BusinessObject addressBO = new BusinessObject();\n        addressBO.setQualifiedName(ADDRESS_QUALIFIED_NAME);\n        addressBO.addField(street);\n        addressBO.addField(city);\n        addressBO.addField(country);\n\n        final RelationField addresses = new RelationField();\n        addresses.setType(RelationField.Type.AGGREGATION);\n        addresses.setFetchType(RelationField.FetchType.LAZY);\n        addresses.setName(\"addresses\");\n        addresses.setCollection(Boolean.TRUE);\n        addresses.setNullable(Boolean.TRUE);\n        addresses.setReference(addressBO);\n\n        final SimpleField firstName = new SimpleField();\n        firstName.setName(\"firstName\");\n        firstName.setType(FieldType.STRING);\n        firstName.setLength(Integer.valueOf(10));\n\n        final SimpleField lastName = new SimpleField();\n        lastName.setName(\"lastName\");\n        lastName.setType(FieldType.STRING);\n        lastName.setNullable(Boolean.FALSE);\n\n        final SimpleField phoneNumbers = new SimpleField();\n        phoneNumbers.setName(\"phoneNumbers\");\n        phoneNumbers.setType(FieldType.STRING);\n        phoneNumbers.setLength(Integer.valueOf(10));\n        phoneNumbers.setCollection(Boolean.TRUE);\n\n        final BusinessObject employee = new BusinessObject();\n        employee.setQualifiedName(EMPLOYEE_QUALIFIED_NAME);\n        employee.addField(firstName);\n        employee.addField(lastName);\n        employee.addField(phoneNumbers);\n        employee.addField(addresses);\n        employee.setDescription(\"Describe a simple employee\");\n        employee.addUniqueConstraint(\"uk_fl\", \"firstName\", \"lastName\");\n\n        final Query findByFirstNAmeAndLastNameNewOrder = employee.addQuery(FIND_BY_FIRST_NAME_AND_LAST_NAME_NEW_ORDER,\n                \"SELECT e FROM Employee e WHERE e.firstName =:firstName AND e.lastName = :lastName ORDER BY e.lastName\",\n                List.class.getName());\n        findByFirstNAmeAndLastNameNewOrder.addQueryParameter(\"firstName\", String.class.getName());\n        findByFirstNAmeAndLastNameNewOrder.addQueryParameter(\"lastName\", String.class.getName());\n\n        employee.addIndex(\"IDX_LSTNM\", \"lastName\");\n\n        final BusinessObject person = new BusinessObject();\n        person.setQualifiedName(PERSON_QUALIFIED_NAME);\n        person.addField(firstName);\n        person.addField(lastName);\n        person.addField(phoneNumbers);\n        person.setDescription(\"Describe a simple person\");\n        person.addUniqueConstraint(\"uk_fl\", \"firstName\", \"lastName\");\n\n        final BusinessObject productBO = new BusinessObject();\n        productBO.setQualifiedName(PRODUCT_QUALIFIED_NAME);\n        productBO.addField(name);\n\n        final RelationField products = new RelationField();\n        products.setType(RelationField.Type.AGGREGATION);\n        products.setFetchType(RelationField.FetchType.LAZY);\n        products.setName(\"products\");\n        products.setCollection(Boolean.TRUE);\n        products.setNullable(Boolean.TRUE);\n        products.setReference(productBO);\n\n        final BusinessObject catalogBO = new BusinessObject();\n        catalogBO.setQualifiedName(PRODUCT_CATALOG_QUALIFIED_NAME);\n        catalogBO.addField(name);\n        catalogBO.addField(products);\n\n        final BusinessObjectModel model = new BusinessObjectModel();\n        model.addBusinessObject(employee);\n        model.addBusinessObject(person);\n        model.addBusinessObject(addressBO);\n        model.addBusinessObject(countryBO);\n        model.addBusinessObject(productBO);\n        model.addBusinessObject(catalogBO);\n        return model;\n    }\n\n    @Test\n    public void deploy_a_BDR_and_verify_sequence_behaviour_by_DBVendor() throws Exception {\n        String dbVendor = PlatformSetup.getPropertyBonitaBdmDbVendor();\n        Assume.assumeTrue(\"We don't test sequence behaviour on h2\", !dbVendor.equals(\"h2\"));\n        switch (dbVendor) {\n            case \"postgres\":\n                assertThat(execute_native_sql(\"SELECT 0 FROM pg_class where relname = 'hibernate_sequence'\"))\n                        .containsOnly(0);\n                break;\n            case \"oracle\":\n                assertThat(((List<BigDecimal>) execute_native_sql(\n                        \"SELECT COUNT(*) FROM user_sequences WHERE sequence_name = 'HIBERNATE_SEQUENCE'\")).get(0)\n                        .intValue()).isEqualTo(1);\n                break;\n            case \"mysql\":\n                assertThat(Arrays.toString((Object[]) execute_native_sql(\"describe EMPLOYEE\").get(0)))\n                        .contains(\"auto_increment\", \"persistenceId\");\n                break;\n            case \"sqlserver\":\n                assertThat(Arrays.toString((Object[]) execute_native_sql(\"exec sp_columns EMPLOYEE\").get(0)))\n                        .contains(\"bigint identity\", \"persistenceId\");\n                break;\n        }\n    }\n\n    private List execute_native_sql(String query) throws Exception {\n        ServiceAccessor serviceAccessor = ServiceAccessorSingleton.getInstance();\n        return serviceAccessor.getUserTransactionService().executeInTransaction(\n                () -> ((EntityManagerFactoryAware) serviceAccessor.getBusinessDataRepository())\n                        .getEntityManagerFactory().createEntityManager().createNativeQuery(query).getResultList());\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/business/data/DataRetentionBdmTrackingIT.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.CommonAPIIT;\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.model.field.FieldType;\nimport org.bonitasoft.engine.bdm.model.field.SimpleField;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.business.data.model.SDataRetentionBdmTracking;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.operation.LeftOperandBuilder;\nimport org.bonitasoft.engine.operation.OperatorType;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.ServiceAccessorSingleton;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * Integration test verifying that a tracking record is inserted into\n * {@code data_retention_bdm_tracking} when a BDM object is created via process execution.\n */\npublic class DataRetentionBdmTrackingIT extends CommonAPIIT {\n\n    private static final String INVOICE_QUALIFIED_NAME = \"com.company.model.Invoice\";\n    private static final String BIZ_INVOICE = \"bizInvoice\";\n\n    private UserTransactionService userTransactionService;\n    private DataRetentionBdmTrackingRepository trackingRepository;\n\n    @Before\n    public void setUp() throws Exception {\n        ServiceAccessor serviceAccessor = ServiceAccessorSingleton.getInstance();\n        userTransactionService = serviceAccessor.getUserTransactionService();\n        trackingRepository = serviceAccessor.lookup(DataRetentionBdmTrackingRepository.class);\n\n        loginWithTechnicalUser();\n        createUser(\"testUser\", \"bpm\");\n\n        installBusinessDataModel(buildBom());\n    }\n\n    // No @After needed: CommonAPIIT.clean() handles process definitions, users, BDM, trackings, and logout\n\n    @Test\n    public void should_insert_tracking_record_when_bdm_object_is_created_via_process() throws Exception {\n        // given — a process that creates an Invoice BDM object\n        var groovyScript = \"import \" + INVOICE_QUALIFIED_NAME\n                + \"; Invoice invoice = new Invoice(); invoice.reference = 'INV-001'; return invoice;\";\n        var initExpression = new ExpressionBuilder().createGroovyScriptExpression(\n                \"createInvoice\", groovyScript, INVOICE_QUALIFIED_NAME);\n\n        var builder = new ProcessDefinitionBuilder().createNewInstance(\"TrackingTestProcess\", \"1.0\");\n        builder.addActor(\"actor\");\n        builder.addBusinessData(BIZ_INVOICE, INVOICE_QUALIFIED_NAME, null);\n        builder.addAutomaticTask(\"createInvoice\")\n                .addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand(BIZ_INVOICE),\n                        OperatorType.ASSIGNMENT, null, null, initExpression);\n        builder.addUserTask(\"wait\", \"actor\");\n        builder.addTransition(\"createInvoice\", \"wait\");\n\n        var definition = deployAndEnableProcessWithActor(builder.done(), \"actor\",\n                getIdentityAPI().getUserByUserName(\"testUser\"));\n\n        // when — start the process (BDM object is created in the automatic task)\n        ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId());\n        waitForUserTaskAndGetIt(processInstance, \"wait\");\n\n        // then — verify a tracking record was inserted via the repository\n        List<SDataRetentionBdmTracking> trackingRecords = userTransactionService.executeInTransaction(\n                () -> trackingRepository.getByClassname(INVOICE_QUALIFIED_NAME));\n\n        assertThat(trackingRecords).hasSize(1);\n        SDataRetentionBdmTracking tracking = trackingRecords.get(0);\n        assertThat(tracking.getDataId()).isGreaterThan(0);\n        assertThat(tracking.getDataClassname()).isEqualTo(INVOICE_QUALIFIED_NAME);\n        assertThat(tracking.getCreatedAt()).isPositive();\n        assertThat(tracking.getLastModifiedAt()).isEqualTo(tracking.getCreatedAt());\n    }\n\n    @Test\n    public void should_not_insert_duplicate_tracking_on_update() throws Exception {\n        // given — a process that creates then updates an Invoice BDM object\n        var createScript = \"import \" + INVOICE_QUALIFIED_NAME\n                + \"; Invoice invoice = new Invoice(); invoice.reference = 'INV-002'; return invoice;\";\n        var createExpression = new ExpressionBuilder().createGroovyScriptExpression(\n                \"createInvoice\", createScript, INVOICE_QUALIFIED_NAME);\n\n        var builder = new ProcessDefinitionBuilder().createNewInstance(\"TrackingUpdateProcess\", \"1.0\");\n        builder.addActor(\"actor\");\n        builder.addBusinessData(BIZ_INVOICE, INVOICE_QUALIFIED_NAME, null);\n\n        // step 1: create the BDM object\n        builder.addAutomaticTask(\"createInvoice\")\n                .addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand(BIZ_INVOICE),\n                        OperatorType.ASSIGNMENT, null, null, createExpression);\n\n        // step 2: update the same BDM object (set a new reference)\n        var updateScript = BIZ_INVOICE + \".reference = 'INV-002-UPDATED'; return \" + BIZ_INVOICE + \";\";\n        var updateExpression = new ExpressionBuilder().createGroovyScriptExpression(\n                \"updateInvoice\", updateScript, INVOICE_QUALIFIED_NAME,\n                new ExpressionBuilder().createBusinessDataExpression(BIZ_INVOICE, INVOICE_QUALIFIED_NAME));\n        builder.addAutomaticTask(\"updateInvoice\")\n                .addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand(BIZ_INVOICE),\n                        OperatorType.ASSIGNMENT, null, null, updateExpression);\n\n        builder.addUserTask(\"wait\", \"actor\");\n        builder.addTransition(\"createInvoice\", \"updateInvoice\");\n        builder.addTransition(\"updateInvoice\", \"wait\");\n\n        var definition = deployAndEnableProcessWithActor(builder.done(), \"actor\",\n                getIdentityAPI().getUserByUserName(\"testUser\"));\n\n        // when\n        ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId());\n        waitForUserTaskAndGetIt(processInstance, \"wait\");\n\n        // then — still only one tracking record (update does not create a second one)\n        List<SDataRetentionBdmTracking> trackingRecords = userTransactionService.executeInTransaction(\n                () -> trackingRepository.getByClassname(INVOICE_QUALIFIED_NAME));\n\n        assertThat(trackingRecords).hasSize(1);\n    }\n\n    private static BusinessObjectModel buildBom() {\n        var reference = new SimpleField();\n        reference.setName(\"reference\");\n        reference.setType(FieldType.STRING);\n\n        var invoiceBO = new BusinessObject();\n        invoiceBO.setQualifiedName(INVOICE_QUALIFIED_NAME);\n        invoiceBO.addField(reference);\n\n        var bom = new BusinessObjectModel();\n        bom.addBusinessObject(invoiceBO);\n        return bom;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/business/data/TransactionTimeoutEntityManagerIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.lang.reflect.Field;\n\nimport javax.persistence.EntityManager;\nimport javax.transaction.Status;\nimport javax.transaction.TransactionManager;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.CommonAPIIT;\nimport org.bonitasoft.engine.bdm.BusinessObjectModelConverter;\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.model.field.FieldType;\nimport org.bonitasoft.engine.bdm.model.field.SimpleField;\nimport org.bonitasoft.engine.business.data.impl.JPABusinessDataRepositoryImpl;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.ServiceAccessorSingleton;\nimport org.bonitasoft.engine.transaction.JTATransactionServiceImpl;\nimport org.bonitasoft.engine.transaction.STransactionException;\nimport org.bonitasoft.engine.transaction.TransactionService;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.springframework.test.util.AopTestUtils;\n\n/**\n * Integration tests for EntityManager lifecycle under various JTA transaction outcomes.\n * Uses the full Bonita engine with real Narayana JTA transactions to reproduce actual behavior,\n * particularly the transaction timeout scenario (BPA-321).\n */\n@Slf4j\npublic class TransactionTimeoutEntityManagerIT extends CommonAPIIT {\n\n    private JPABusinessDataRepositoryImpl bdmRepository;\n\n    private TransactionService transactionService;\n\n    @Before\n    public void setUp() throws Exception {\n        loginWithTechnicalUser();\n\n        // Deploy a minimal BDM so the EntityManagerFactory is created\n        final BusinessObjectModelConverter converter = new BusinessObjectModelConverter();\n        final byte[] zip = converter.zip(buildMinimalBOM());\n        getTenantAdministrationAPI().pause();\n        getTenantAdministrationAPI().cleanAndUninstallBusinessDataModel();\n        getTenantAdministrationAPI().updateBusinessDataModel(zip);\n        getTenantAdministrationAPI().resume();\n\n        // Get the real JPABusinessDataRepositoryImpl from the engine\n        // Unwrap Spring AOP proxy (created by BusinessDataRepositoryEventAspect) to access internal fields via reflection\n        ServiceAccessor serviceAccessor = ServiceAccessorSingleton.getInstance();\n        bdmRepository = AopTestUtils.getTargetObject(serviceAccessor.getBusinessDataRepository());\n        transactionService = serviceAccessor.getTransactionService();\n    }\n\n    @After\n    public void tearDown() throws Exception {\n        // Clean up ThreadLocal to avoid leaking to the next test\n        clearManagersThreadLocal();\n\n        if (!getTenantAdministrationAPI().isPaused()) {\n            getTenantAdministrationAPI().pause();\n            getTenantAdministrationAPI().cleanAndUninstallBusinessDataModel();\n            getTenantAdministrationAPI().resume();\n        }\n        logout();\n    }\n\n    /**\n     * Scenario (a): Nominal — transaction commits successfully.\n     * The EntityManager is created, used, and properly cleaned up by afterCompletion(STATUS_COMMITTED).\n     */\n    @Test\n    public void afterCompletion_should_cleanup_entityManager_on_successful_commit() throws Exception {\n        //given\n        transactionService.begin();\n\n        //when — trigger getEntityManager() which registers the RemoveEntityManagerSynchronization\n        bdmRepository.getEntityClassNames();\n        EntityManager emDuringTx = getManagersThreadLocalValue();\n        assertThat(emDuringTx).as(\"EM should exist during transaction\").isNotNull();\n        assertThat(emDuringTx.isOpen()).isTrue();\n\n        transactionService.complete();\n\n        //then — afterCompletion(STATUS_COMMITTED) should have closed and removed the EM\n        EntityManager emAfterCommit = getManagersThreadLocalValue();\n        assertThat(emAfterCommit).as(\"EM should be removed from ThreadLocal after commit\").isNull();\n    }\n\n    /**\n     * Scenario (b): Application rollback — transaction is rolled back explicitly.\n     * The EntityManager should be cleaned up by afterCompletion(STATUS_ROLLEDBACK).\n     */\n    @Test\n    public void afterCompletion_should_cleanup_entityManager_on_explicit_rollback() throws Exception {\n        //given\n        transactionService.begin();\n\n        bdmRepository.getEntityClassNames();\n        EntityManager emDuringTx = getManagersThreadLocalValue();\n        assertThat(emDuringTx).as(\"EM should exist during transaction\").isNotNull();\n\n        //when\n        rollbackTransaction();\n\n        //then — afterCompletion(STATUS_ROLLEDBACK) should have closed and removed the EM\n        EntityManager emAfterRollback = getManagersThreadLocalValue();\n        assertThat(emAfterRollback).as(\"EM should be removed from ThreadLocal after rollback\").isNull();\n    }\n\n    /**\n     * Scenario (c): Transaction marked rollback-only (setRollbackOnly), then commit attempt.\n     * commit() triggers a rollback. The EntityManager should still be cleaned up.\n     */\n    @Test\n    public void afterCompletion_should_cleanup_entityManager_on_rollback_only_commit_attempt() throws Exception {\n        //given\n        transactionService.begin();\n\n        bdmRepository.getEntityClassNames();\n        EntityManager emDuringTx = getManagersThreadLocalValue();\n        assertThat(emDuringTx).isNotNull();\n\n        //when\n        rollbackTransaction();\n\n        //then — afterCompletion(STATUS_ROLLEDBACK) should have closed and removed the EM\n        EntityManager emAfter = getManagersThreadLocalValue();\n        assertThat(emAfter).as(\"EM should be removed from ThreadLocal after rollback-only commit\").isNull();\n    }\n\n    /**\n     * Scenario (d): Transaction timeout — Narayana's TransactionReaper aborts the transaction.\n     * This is the BPA-321 scenario. The reaper calls afterCompletion on its own thread,\n     * so ThreadLocal.get() returns null and the EM is NOT cleaned up.\n     * <p>\n     * After the timeout, the defensive getEntityManager() should detect the stale EM\n     * (not joined to any transaction) and discard it, creating a fresh one for the next tx.\n     */\n    @Test\n    public void getEntityManager_should_detect_stale_entityManager_after_transaction_timeout_and_create_fresh_one()\n            throws Exception {\n        //given — start a transaction with a 1-second timeout\n        TransactionManager txManager = getTransactionManager();\n        txManager.setTransactionTimeout(1);\n        transactionService.begin();\n\n        // Trigger EntityManager creation within the transaction\n        bdmRepository.getEntityClassNames();\n        EntityManager emDuringTx = getManagersThreadLocalValue();\n        assertThat(emDuringTx).as(\"EM should exist during transaction\").isNotNull();\n\n        //when — wait for the timeout to fire\n        log.info(\"Waiting for transaction timeout (1s)...\");\n        Thread.sleep(2000);\n\n        // The transaction should have been aborted by the reaper\n        int status = txManager.getStatus();\n        log.info(\"Transaction status after timeout: {}\", status);\n        assertThat(status).as(\"Transaction should be rolled back or no longer active\")\n                .isIn(Status.STATUS_ROLLEDBACK, Status.STATUS_NO_TRANSACTION, Status.STATUS_MARKED_ROLLBACK);\n\n        // The stale EM may still be in the ThreadLocal (the reaper thread couldn't clean it)\n        EntityManager staleEM = getManagersThreadLocalValue();\n        log.info(\"Stale EM still in ThreadLocal after timeout: {}\", staleEM != null);\n\n        // Clean up the timed-out transaction if needed\n        if (txManager.getStatus() != Status.STATUS_NO_TRANSACTION) {\n            try {\n                rollbackTransaction();\n            } catch (Exception e) {\n                log.info(\"Rollback of timed-out tx: {}\", e.getMessage());\n            }\n        }\n\n        //then — start a new transaction and verify the defensive getEntityManager() works\n        txManager.setTransactionTimeout(0); // restore default timeout\n        transactionService.begin();\n\n        // The defensive getEntityManager() should detect the stale EM and create a fresh one\n        bdmRepository.getEntityClassNames();\n        EntityManager freshEM = getManagersThreadLocalValue();\n        assertThat(freshEM).as(\"A fresh EntityManager should be provided for the new transaction\").isNotNull();\n        assertThat(freshEM.isOpen()).isTrue();\n        assertThat(freshEM.isJoinedToTransaction()).isTrue();\n\n        // If the stale EM was still around, the fresh EM should be a different instance\n        if (staleEM != null) {\n            assertThat(freshEM).as(\"Fresh EM should be a different instance than the stale one\")\n                    .isNotSameAs(staleEM);\n        }\n\n        rollbackTransaction();\n    }\n\n    // --- Helpers ---\n\n    private BusinessObjectModel buildMinimalBOM() {\n        SimpleField nameField = new SimpleField();\n        nameField.setName(\"name\");\n        nameField.setType(FieldType.STRING);\n\n        BusinessObject bo = new BusinessObject();\n        bo.setQualifiedName(\"com.company.model.pojo.SimpleEntity\");\n        bo.addField(nameField);\n\n        BusinessObjectModel bom = new BusinessObjectModel();\n        bom.addBusinessObject(bo);\n        return bom;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private ThreadLocal<EntityManager> getManagersThreadLocal() {\n        try {\n            Field managersField = JPABusinessDataRepositoryImpl.class.getDeclaredField(\"managers\");\n            managersField.setAccessible(true);\n            return (ThreadLocal<EntityManager>) managersField.get(bdmRepository);\n        } catch (Exception e) {\n            throw new RuntimeException(\"Failed to access managers ThreadLocal\", e);\n        }\n    }\n\n    private EntityManager getManagersThreadLocalValue() {\n        return getManagersThreadLocal().get();\n    }\n\n    private void clearManagersThreadLocal() {\n        getManagersThreadLocal().remove();\n    }\n\n    private TransactionManager getTransactionManager() {\n        try {\n            Field txMgrField = JTATransactionServiceImpl.class.getDeclaredField(\"txManager\");\n            txMgrField.setAccessible(true);\n            return (TransactionManager) txMgrField.get(transactionService);\n        } catch (Exception e) {\n            throw new RuntimeException(\"Failed to access txManager field\", e);\n        }\n    }\n\n    private void rollbackTransaction() throws STransactionException {\n        transactionService.setRollbackOnly();\n        transactionService.complete(); // will trigger rollback since it's marked as rollback-only\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/cache/ehcache/CacheConfigurationIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.cache.ehcache;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.bpm.CommonBPMServicesTest;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class CacheConfigurationIT extends CommonBPMServicesTest {\n\n    EhCacheCacheService cacheService;\n\n    @Before\n    public void setUp() {\n        cacheService = (EhCacheCacheService) getServiceAccessor().getPlatformCacheService();\n    }\n\n    @Test\n    public void all_required_cache_configurations_should_exist() {\n        assertThat(cacheService.getCacheConfigurationNames()).containsExactlyInAnyOrder(\n                \"CONFIGURATION_FILES_CACHE\",\n                \"USER_FILTER\",\n                \"transient_data\",\n                \"GROOVY_SCRIPT_CACHE_NAME\",\n                \"_PROCESSDEF\",\n                \"SYNCHRO_SERVICE_CACHE\",\n                \"parameters\",\n                \"DEFAULT_PLATFORM\",\n                \"CONNECTOR\",\n                \"application-token\");\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/classloader/ClassLoaderIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.classloader;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.io.IOUtil.generateJar;\nimport static org.junit.Assert.assertEquals;\n\nimport java.io.IOException;\n\nimport org.apache.commons.io.IOUtils;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.bonitasoft.engine.CommonAPIIT;\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.api.PlatformAPIAccessor;\nimport org.bonitasoft.engine.bdm.BusinessObjectModelConverter;\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.model.field.FieldType;\nimport org.bonitasoft.engine.bdm.model.field.SimpleField;\nimport org.bonitasoft.engine.bpm.bar.BarResource;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.bar.InvalidBusinessArchiveFormatException;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.connectors.TestConnector3;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.expression.InvalidExpressionException;\nimport org.bonitasoft.engine.filter.user.TestFilterWithAutoAssign;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.session.PlatformSession;\nimport org.bonitasoft.engine.test.BuildTestUtil;\nimport org.junit.After;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.SystemOutRule;\n\npublic class ClassLoaderIT extends TestWithUser {\n\n    @Rule\n    public SystemOutRule systemOutRule = new SystemOutRule().enableLog();\n\n    @After\n    public void cleanUp() throws Exception {\n        String businessDataModelVersion = getTenantAdministrationAPI().getBusinessDataModelVersion();\n        if (businessDataModelVersion != null) {\n            getTenantAdministrationAPI().pause();\n            getTenantAdministrationAPI().cleanAndUninstallBusinessDataModel();\n            getTenantAdministrationAPI().resume();\n        }\n    }\n\n    @Test\n    public void should_refresh_classloader_only_once_on_deploy_process() throws Exception {\n        BusinessArchive businessArchive = createProcessWithDependencies();\n        User user = getIdentityAPI().createUser(\"john\", \"bpm\");\n\n        systemOutRule.clearLog();\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchive, ACTOR_NAME, user);\n\n        String processDeployLog = systemOutRule.getLog();\n\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        final ActivityInstance task = waitForUserTaskAndGetIt(processInstance, \"step1\");\n\n        assertEquals(\"stringFromPublicMethod\", task.getDisplayName());\n        assertThat(processDeployLog)\n                .containsOnlyOnce(\"Refreshing classloader PROCESS:\" + processDefinition.getId());\n    }\n\n    public String toString() {\n        return \"MyObject\";\n    }\n\n    @Test\n    public void should_be_able_to_execute_scripts_on_processes_having_same_classes() throws Exception {\n        byte[] myObjectJar1 = generateJar(\n                Pair.of(\"MyObject\",\n                        \"public interface MyObject extends java.io.Serializable {}\"),\n                Pair.of(\"MyObjectImpl1\",\n                        \"public class MyObjectImpl1 implements MyObject {\\n\" +\n                                \"   public String toString() {\\n\" +\n                                \"        return \\\"MyObjectImpl\\\";\\n\" +\n                                \"    }\\n\" +\n                                \"}\"));\n        byte[] myObjectJar2 = generateJar(\n                Pair.of(\"MyObject\",\n                        \"public interface MyObject extends java.io.Serializable {}\"),\n                Pair.of(\"MyObjectImpl2\",\n                        \"public class MyObjectImpl2 implements MyObject {\\n\" +\n                                \"   public String toString() {\\n\" +\n                                \"        return \\\"MyObjectImpl\\\";\\n\" +\n                                \"    }\\n\" +\n                                \"}\"));\n        BusinessArchive bar1 = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(new ProcessDefinitionBuilder().createNewInstance(\"process1 with MyObject\", \"1.0\")\n                        .addData(\"myObject\", \"MyObject\", new ExpressionBuilder().createGroovyScriptExpression(\"s1\",\n                                \"new MyObjectImpl1()\", \"MyObject\"))\n                        .getProcess())\n                .addClasspathResource(new BarResource(\"myObjectJar.jar\", myObjectJar1)).done();\n        BusinessArchive bar2 = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(new ProcessDefinitionBuilder().createNewInstance(\"process2 with MyObject\", \"1.0\")\n                        .addData(\"myObject\", \"MyObject\", new ExpressionBuilder().createGroovyScriptExpression(\"s1\",\n                                \"new MyObjectImpl2()\", \"MyObject\"))\n                        .getProcess())\n                .addClasspathResource(new BarResource(\"myObjectJar.jar\", myObjectJar2)).done();\n\n        ProcessDefinition processDefinition1 = deployAndEnableProcess(bar1);\n        ProcessDefinition processDefinition2 = deployAndEnableProcess(bar2);\n\n        ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition1.getId());\n        waitForProcessToFinish(processInstance1);\n        ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinition2.getId());\n        waitForProcessToFinish(processInstance2);\n    }\n\n    @Test\n    public void should_refresh_classloader_only_once_on_deploy_bdm() throws Exception {\n        loginWithTechnicalUser();\n        final BusinessObjectModelConverter converter = new BusinessObjectModelConverter();\n        final byte[] zip = converter.zip(buildCustomBOM());\n        getTenantAdministrationAPI().pause();\n        getTenantAdministrationAPI().cleanAndUninstallBusinessDataModel();\n        systemOutRule.clearLog();\n        getTenantAdministrationAPI().updateBusinessDataModel(zip);\n        String deployBDMLog = systemOutRule.getLog();\n        getTenantAdministrationAPI().resume();\n\n        assertThat(deployBDMLog).containsOnlyOnce(\"Refreshing classloader TENANT\");\n    }\n\n    @Test\n    public void should_not_refresh_classloader_on_delete_process_definition() throws Exception {\n        BusinessArchive businessArchive = createProcessWithDependencies();\n        User user = getIdentityAPI().createUser(\"john\", \"bpm\");\n\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchive, ACTOR_NAME, user);\n\n        systemOutRule.clearLog();\n        getProcessAPI().disableAndDeleteProcessDefinition(processDefinition.getId());\n        String processDeployLog = systemOutRule.getLog();\n\n        assertThat(processDeployLog)\n                .doesNotContain(\"Refreshing classloader PROCESS:\" + processDefinition.getId());\n    }\n\n    @Test\n    public void should_be_able_to_fix_groovy_script_by_updating_dependency_in_platform_classloader() throws Exception {\n        PlatformSession session = loginOnPlatform();\n        PlatformAPIAccessor.getPlatformCommandAPI(session).addDependency(\"hello-there-1.0.0.jar\", generateJar(\"Hello\",\n                \"public class Hello{\",\n                \"public String hello(){\",\n                \"return \\\"Hello there!\\\";\",\n                \"}\",\n                \"}\"));\n        logoutOnPlatform(session);\n        loginWithTechnicalUser();\n\n        ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder().createNewInstance(\n                \"processWithDisplayName\",\n                \"1.0\");\n        designProcessDefinition.addActor(ACTOR_NAME);\n        designProcessDefinition.addUserTask(\"step0\", ACTOR_NAME).addDisplayName(\n                new ExpressionBuilder().createGroovyScriptExpression(\"groovyExpr\",\n                        \"new Hello().hello()\", String.class.getName()));\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(),\n                ACTOR_NAME, user);\n        ProcessInstance p1 = getProcessAPI().startProcess(processDefinition.getId());\n\n        assertThat(waitForUserTaskAndGetIt(p1, \"step0\").getDisplayName()).isEqualTo(\"Hello there!\");\n\n        session = loginOnPlatform();\n        PlatformAPIAccessor.getPlatformCommandAPI(session).removeDependency(\"hello-there-1.0.0.jar\");\n        PlatformAPIAccessor.getPlatformCommandAPI(session).addDependency(\"hello-there-1.0.1.jar\", generateJar(\"Hello\",\n                \"public class Hello{\",\n                \"public String hello(){\",\n                \"return \\\"Hello there! General Kenobi.\\\";\",\n                \"}\",\n                \"}\"));\n        logoutOnPlatform(session);\n        loginWithTechnicalUser();\n\n        ProcessInstance p2 = getProcessAPI().startProcess(processDefinition.getId());\n\n        assertThat(waitForUserTaskAndGetIt(p2, \"step0\").getDisplayName()).isEqualTo(\"Hello there! General Kenobi.\");\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    private BusinessObjectModel buildCustomBOM() {\n        final SimpleField name = new SimpleField();\n        name.setName(\"name\");\n        name.setType(FieldType.STRING);\n        final BusinessObject somethings = new BusinessObject();\n        somethings.setQualifiedName(\"org.acme.pojo.Something\");\n        somethings.addField(name);\n        final BusinessObjectModel model = new BusinessObjectModel();\n        model.addBusinessObject(somethings);\n        return model;\n    }\n\n    private BusinessArchive createProcessWithDependencies() throws InvalidExpressionException,\n            InvalidProcessDefinitionException, IOException, InvalidBusinessArchiveFormatException {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder();\n        final ProcessDefinitionBuilder pBuilder = processDefinitionBuilder.createNewInstance(\"emptyProcess\",\n                String.valueOf(System.currentTimeMillis()));\n        pBuilder.addShortTextData(\"aData\", new ExpressionBuilder().createGroovyScriptExpression(\"myScript\",\n                \"new org.bonitasoft.engine.test.TheClassOfMyLibrary().aPublicMethod()\", String.class.getName()));\n        pBuilder.addActor(ACTOR_NAME)\n                .addUserTask(\"step1\", ACTOR_NAME)\n                .addDisplayName(\n                        new ExpressionBuilder().createGroovyScriptExpression(\"myScript\",\n                                \"new org.bonitasoft.engine.test.TheClassOfMyLibrary().aPublicMethod()\",\n                                String.class.getName()));\n        final DesignProcessDefinition designProcessDefinition = pBuilder.done();\n\n        final BusinessArchiveBuilder builder = new BusinessArchiveBuilder()\n                .createNewBusinessArchive()\n                .setProcessDefinition(designProcessDefinition);\n        builder.addClasspathResource(new BarResource(\"mylibrary.jar\",\n                IOUtils.toByteArray(CommonAPIIT.class.getResourceAsStream(\"/mylibrary-jar.bak\"))));\n        builder.addClasspathResource(BuildTestUtil.generateJarAndBuildBarResource(TestFilterWithAutoAssign.class,\n                \"TestFilterWithAutoAssign.jar\"));\n        builder.addClasspathResource(\n                BuildTestUtil.generateJarAndBuildBarResource(TestConnector3.class, \"TestConnector3.jar\"));\n        return builder.done();\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/classloader/ClassLoaderServiceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.classloader;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier;\nimport static org.bonitasoft.engine.commons.io.IOUtil.generateJar;\nimport static org.bonitasoft.engine.dependency.model.ScopeType.PROCESS;\nimport static org.bonitasoft.engine.dependency.model.ScopeType.TENANT;\nimport static org.junit.Assert.*;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.net.URL;\nimport java.net.URLConnection;\nimport java.util.Collections;\nimport java.util.Map;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.CallableWithException;\nimport org.bonitasoft.engine.RunnableWithException;\nimport org.bonitasoft.engine.bpm.CommonBPMServicesTest;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.io.IOUtil;\nimport org.bonitasoft.engine.dependency.DependencyService;\nimport org.bonitasoft.engine.dependency.SDependencyException;\nimport org.bonitasoft.engine.dependency.model.SPlatformDependency;\nimport org.bonitasoft.engine.dependency.model.ScopeType;\nimport org.bonitasoft.engine.test.util.TestUtil;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Elias Ricken de Medeiros, Charles Souillard, Baptiste Mesta\n */\n@Slf4j\npublic class ClassLoaderServiceIT extends CommonBPMServicesTest {\n\n    private DependencyService dependencyService;\n\n    private DependencyService platformDependencyService;\n\n    private ClassLoaderService classLoaderService;\n\n    private static final long ID1 = 1;\n\n    private static final long ID2 = 2;\n\n    @After\n    public void tearDown() throws Exception {\n        TestUtil.closeTransactionIfOpen(getTransactionService());\n\n        getTransactionService().begin();\n\n        dependencyService.deleteDependencies(ID1, PROCESS);\n        dependencyService.deleteDependencies(ID1, TENANT);\n        dependencyService.deleteDependencies(ID2, PROCESS);\n        dependencyService.deleteDependencies(ID2, TENANT);\n        platformDependencyService.deleteDependencies(ClassLoaderIdentifier.GLOBAL_ID,\n                ClassLoaderIdentifier.GLOBAL_TYPE);\n        getTransactionService().complete();\n        classLoaderService.stop();\n        classLoaderService = null;\n        dependencyService = null;\n        platformDependencyService = null;\n    }\n\n    @Before\n    public void setUp() throws SBonitaException {\n        classLoaderService = getServiceAccessor().getClassLoaderService();\n        dependencyService = getServiceAccessor().getDependencyService();\n        platformDependencyService = getServiceAccessor().getPlatformDependencyService();\n        classLoaderService.start();\n    }\n\n    private <T> T inTx(CallableWithException<T> runnable) throws Exception {\n        getTransactionService().begin();\n        try {\n            return runnable.call();\n        } finally {\n            getTransactionService().complete();\n        }\n    }\n\n    private void inTx(RunnableWithException runnable) throws Exception {\n        getTransactionService().begin();\n        try {\n            runnable.run();\n        } finally {\n            getTransactionService().complete();\n        }\n    }\n\n    private void initializeClassLoaderService() throws Exception {\n        inTx(() -> {\n            createPlatformDependency(\"globalResource\", \"globalResource.jar\",\n                    generateJar(GlobalClass1.class, GlobalClass2.class, SharedClass1.class));\n            createDependency(ID1, PROCESS, \"LocalResource1\", \"LocalResource1.jar\",\n                    generateJar(LocalClass1.class, LocalClass2.class));\n            createDependency(ID2, PROCESS, \"LocalResource1\", \"LocalResource1.jar\",\n                    generateJar(LocalClass1.class, LocalClass2.class));\n            createDependency(ID1, PROCESS, \"LocalResource2\", \"LocalResource2.jar\",\n                    generateJar(LocalClass3.class, LocalClass4.class, SharedClass1.class));\n        });\n    }\n\n    private void addNotInPathDependencies() throws Exception {\n        inTx(() -> {\n            createPlatformDependency(\"NotInPathGlobal\", \"NotInPathGlobal.jar\",\n                    IOUtil.getAllContentFrom(ClassLoaderServiceIT.class.getResource(\"NotInPathGlobal.jar\")));\n            createPlatformDependency(\"NotInPathShared\", \"NotInPathShared.jar\",\n                    IOUtil.getAllContentFrom(ClassLoaderServiceIT.class.getResource(\"NotInPathShared.jar\")));\n            createDependency(ID1, PROCESS, \"NotInPathLocal\", \"NotInPathLocal.jar\",\n                    IOUtil.getAllContentFrom(ClassLoaderServiceIT.class.getResource(\"NotInPathLocal.jar\")));\n        });\n    }\n\n    private long createDependency(final long artifactId, final ScopeType artifactType, final String name,\n            final String fileName,\n            final byte[] value) throws SDependencyException, SClassLoaderException {\n        long id = dependencyService.createMappedDependency(name, value, fileName, artifactId, artifactType).getId();\n        classLoaderService.refreshClassLoaderImmediately(identifier(artifactType, artifactId));\n        log.info(\"Created {}:{} dependency with {}\", artifactType, artifactId, fileName);\n        return id;\n    }\n\n    private long createPlatformDependency(final String name, final String fileName, final byte[] value)\n            throws SDependencyException, SClassLoaderException {\n        final SPlatformDependency dependency = new SPlatformDependency(name, fileName, value);\n        platformDependencyService.createMappedDependency(name, value, fileName,\n                ClassLoaderIdentifier.GLOBAL_ID,\n                ClassLoaderIdentifier.GLOBAL_TYPE);\n        classLoaderService.refreshClassLoaderImmediately(ClassLoaderIdentifier.GLOBAL);\n        log.info(\"Created platform dependency with {}\", fileName);\n        return dependency.getId();\n    }\n\n    private void initializeClassLoaderServiceWithTwoApplications() throws Exception {\n        getTransactionService().begin();\n        createPlatformDependency(\"globalResource\", \"globalResource.jar\",\n                generateJar(GlobalClass1.class, SharedClass1.class));\n        createDependency(ID1, PROCESS, \"LocalResource111\", \"LocalResource1.jar\", generateJar(LocalClass1.class));\n        createDependency(ID2, PROCESS, \"LocalResource211\", \"LocalResource1.jar\", generateJar(LocalClass1.class));\n        createDependency(ID1, PROCESS, \"LocalResource123\", \"LocalResource3.jar\", generateJar(LocalClass3.class));\n        createDependency(ID1, TENANT, \"LocalResource122\", \"LocalResource2.jar\", generateJar(LocalClass2.class));\n        getTransactionService().complete();\n    }\n\n    @Test\n    public void testLoadGlobalClassUsingGlobalClassLoader() throws Exception {\n        initializeClassLoaderService();\n        // getTransactionService().begin();\n        final ClassLoader globalClassLoader = classLoaderService.getClassLoader(ClassLoaderIdentifier.GLOBAL);\n        final Class<?> clazz = globalClassLoader.loadClass(\"org.bonitasoft.engine.classloader.GlobalClass1\");\n        final ClassLoader classLoader = clazz.getClassLoader();\n\n        // getTransactionService().complete();\n        checkGlobalClassLoader(classLoader);\n        assertSameClassloader(globalClassLoader, classLoader);\n    }\n\n    @Test\n    public void testLoadLocalClassUsingLocalClassLoader() throws Exception {\n        initializeClassLoaderService();\n        // getTransactionService().begin();\n        final ClassLoader localClassLoader = classLoaderService.getClassLoader(identifier(PROCESS, ID1));\n        final Class<?> clazz = localClassLoader.loadClass(\"org.bonitasoft.engine.classloader.LocalClass1\");\n        final ClassLoader classLoader = clazz.getClassLoader();\n        checkLocalClassLoader(classLoader);\n\n        assertSameClassloader(localClassLoader, classLoader);\n        // getTransactionService().complete();\n    }\n\n    @Test\n    public void testLoadGlobalClassUsingLocalClassLoader() throws Exception {\n        initializeClassLoaderService();\n        // getTransactionService().begin();\n        final ClassLoader localClassLoader = classLoaderService.getClassLoader(identifier(PROCESS, ID1));\n        final Class<?> clazz = localClassLoader.loadClass(\"org.bonitasoft.engine.classloader.GlobalClass1\");\n        final ClassLoader classLoader = clazz.getClassLoader();\n        checkGlobalClassLoader(classLoader);\n\n        assertNotSameClassloader(localClassLoader, classLoader);\n        // getTransactionService().complete();\n    }\n\n    @Test\n    public void testLoadSharedClassUsingLocalClassLoader() throws Exception {\n        initializeClassLoaderService();\n        // getTransactionService().begin();\n        final ClassLoader localClassLoader = classLoaderService.getClassLoader(identifier(PROCESS, ID1));\n        final Class<?> clazz = localClassLoader.loadClass(\"org.bonitasoft.engine.classloader.SharedClass1\");\n        final ClassLoader classLoader = clazz.getClassLoader();\n        checkLocalClassLoader(classLoader);\n\n        assertSameClassloader(localClassLoader, classLoader);\n        // getTransactionService().complete();\n    }\n\n    @Test\n    public void testLoadSharedClassUsingGlobalClassLoader() throws Exception {\n        initializeClassLoaderService();\n        // getTransactionService().begin();\n        final ClassLoader globalClassLoader = classLoaderService.getClassLoader(ClassLoaderIdentifier.GLOBAL);\n        final Class<?> clazz = globalClassLoader.loadClass(\"org.bonitasoft.engine.classloader.SharedClass1\");\n        final ClassLoader classLoader = clazz.getClassLoader();\n        checkGlobalClassLoader(classLoader);\n\n        assertSameClassloader(globalClassLoader, classLoader);\n        // getTransactionService().complete();\n    }\n\n    @Test\n    public void testLoadOnlyInPathClassUsingGlobalClassLoader() throws Exception {\n        initializeClassLoaderService();\n        // getTransactionService().begin();\n        final ClassLoader classLoader = classLoaderService.getClassLoader(ClassLoaderIdentifier.GLOBAL);\n        final Class<?> clazz = classLoader.loadClass(\"org.bonitasoft.engine.classloader.OnlyInPathClass1\");\n        assertFalse(isBonitaClassLoader(clazz.getClassLoader()));\n        // getTransactionService().complete();\n    }\n\n    @Test\n    public void testLoadOnlyInPathClassUsingLocalClassLoader() throws Exception {\n        initializeClassLoaderService();\n        // getTransactionService().begin();\n        final ClassLoader classLoader = classLoaderService.getClassLoader(identifier(PROCESS, ID1));\n        final Class<?> clazz = classLoader.loadClass(\"org.bonitasoft.engine.classloader.OnlyInPathClass1\");\n        assertFalse(isBonitaClassLoader(clazz.getClassLoader()));\n        // getTransactionService().complete();\n    }\n\n    @Test\n    public void testLoadLocalClassUsingGlobalClassLoader() throws Exception {\n        initializeClassLoaderService();\n        // getTransactionService().begin();\n        final ClassLoader virtualGlobalClassLoader = classLoaderService.getClassLoader(ClassLoaderIdentifier.GLOBAL);\n\n        final Class<?> clazz = virtualGlobalClassLoader.loadClass(\"org.bonitasoft.engine.classloader.LocalClass1\");\n        assertFalse(isBonitaClassLoader(clazz.getClassLoader()));\n        // getTransactionService().complete();\n    }\n\n    @Test\n    public void testGlobalClassLoaderIsSingleForTwoLocalClassLoaders() throws Exception {\n        initializeClassLoaderService();\n        // getTransactionService().begin();\n        final ClassLoader localClassLoaderP1 = classLoaderService\n                .getClassLoader(identifier(PROCESS, ID1));\n        final Class<?> clazzP1 = localClassLoaderP1.loadClass(\"org.bonitasoft.engine.classloader.GlobalClass1\");\n        final ClassLoader classLoader = clazzP1.getClassLoader();\n        checkGlobalClassLoader(classLoader);\n\n        assertFalse(localClassLoaderP1 == classLoader);\n\n        final ClassLoader localClassLoaderP2 = classLoaderService\n                .getClassLoader(identifier(PROCESS, ID2));\n        final Class<?> clazzP2 = localClassLoaderP2.loadClass(\"org.bonitasoft.engine.classloader.GlobalClass1\");\n        final ClassLoader classLoader2 = clazzP2.getClassLoader();\n        checkGlobalClassLoader(classLoader2);\n\n        assertFalse(localClassLoaderP2 == classLoader2);\n\n        // verify if they are the same object (same reference)\n        assertSame(classLoader, classLoader2);\n        // getTransactionService().complete();\n    }\n\n    @Test\n    public void testLoadLocalClassUsingUsingBadLocalClassLoader() throws Exception {\n        initializeClassLoaderService();\n        // getTransactionService().begin();\n        final ClassLoader classLoader = classLoaderService.getClassLoader(identifier(PROCESS, ID2));\n        final Class<?> clazz = classLoader.loadClass(\"org.bonitasoft.engine.classloader.LocalClass3\");\n        assertFalse(isBonitaClassLoader(clazz.getClassLoader()));\n        // getTransactionService().complete();\n    }\n\n    @Test\n    public void testLoadSharedClassUsingBadLocalClassLoader() throws Exception {\n        initializeClassLoaderService();\n        // Should not be found with ID2 (bad scope):\n        final ClassLoader classLoader = classLoaderService.getClassLoader(identifier(PROCESS, ID2));\n        final Class<?> clazz = classLoader.loadClass(\"org.bonitasoft.engine.classloader.SharedClass1\");\n        final ClassLoader classLoader2 = clazz.getClassLoader();\n        checkGlobalClassLoader(classLoader2);\n\n        assertNotSame(classLoader, classLoader2);\n    }\n\n    @Test\n    public void testRemoveLocalClassLoader() throws Exception {\n        initializeClassLoaderService();\n        final ClassLoader localClassLoader1 = classLoaderService.getClassLoader(identifier(PROCESS, ID1));\n        final ClassLoader localClassLoader2 = classLoaderService.getClassLoader(identifier(PROCESS, ID2));\n\n        classLoaderService.removeLocalClassloader(identifier(PROCESS, ID1));\n\n        assertNotSameClassloader(localClassLoader1, classLoaderService.getClassLoader(identifier(PROCESS, ID1)));\n\n        classLoaderService.removeLocalClassloader(identifier(PROCESS, ID2));\n\n        assertNotSameClassloader(localClassLoader2, classLoaderService.getClassLoader(identifier(PROCESS, ID2)));\n    }\n\n    @Test\n    public void testAddResourcesToGlobalClassLoader() throws Exception {\n        initializeClassLoaderService();\n        final ClassLoader globalClassLoaderBefore = classLoaderService.getClassLoader(ClassLoaderIdentifier.GLOBAL);\n\n        //GlobalClass 3 is in the app classloader\n        assertThat(globalClassLoaderBefore.loadClass(\"org.bonitasoft.engine.classloader.GlobalClass3\").getClassLoader())\n                .isNotInstanceOf(BonitaClassLoader.class);\n\n        inTx(() -> {\n            createPlatformDependency(\"newlib\", \"newlib.jar\", generateJar(GlobalClass3.class));\n        });\n        ClassLoader globalClassLoaderAfter = classLoaderService.getClassLoader(ClassLoaderIdentifier.GLOBAL);\n        final ClassLoader classLoader2 = globalClassLoaderAfter\n                .loadClass(\"org.bonitasoft.engine.classloader.GlobalClass3\").getClassLoader();\n        checkGlobalClassLoader(classLoader2);\n\n        assertThat(((BonitaClassLoader) globalClassLoaderBefore).isDestroyed()).isTrue();\n        assertThat(globalClassLoaderBefore).isNotEqualTo(globalClassLoaderAfter);\n        assertSameClassloader(globalClassLoaderAfter, classLoader2);\n    }\n\n    @Test\n    public void testAddResourcesToLocalClassLoader() throws Exception {\n        initializeClassLoaderService();\n\n        //at first, the local classloader does contains GlobalClass2, only the root classloader contains the GlobalClass2\n        ClassLoader classLoaderBefore = inTx(\n                () -> classLoaderService.getClassLoader(identifier(PROCESS, ID1)));\n        checkGlobalClassLoader(\n                classLoaderBefore.loadClass(\"org.bonitasoft.engine.classloader.GlobalClass2\").getClassLoader());\n\n        //add a dependency with GlobalClass2 and re-ask for the classloader\n        ClassLoader classLoaderAfter = inTx(() -> {\n            createDependency(ID1, PROCESS, \"newlib\", \"newlib.jar\", generateJar(GlobalClass2.class));\n            return classLoaderService.getClassLoader(identifier(PROCESS, ID1));\n        });\n\n        checkGlobalClassLoader(\n                classLoaderBefore.loadClass(\"org.bonitasoft.engine.classloader.GlobalClass2\").getClassLoader());\n        checkLocalClassLoader(\n                classLoaderAfter.loadClass(\"org.bonitasoft.engine.classloader.GlobalClass2\").getClassLoader());\n\n        assertThat(classLoaderBefore)\n                .isNotEqualTo(classLoaderService.getClassLoader(identifier(PROCESS, ID1)));\n        assertThat(((BonitaClassLoader) classLoaderBefore).isDestroyed()).isTrue();\n        assertThat(classLoaderAfter)\n                .isEqualTo(classLoaderService.getClassLoader(identifier(PROCESS, ID1)));\n    }\n\n    @Test\n    public void testResetGlobalClassLoader() throws Exception {\n        initializeClassLoaderService();\n\n        inTx(() -> {\n            createPlatformDependency(\"newlib\", \"newlib.jar\", generateJar(GlobalClass3.class));\n        });\n\n        inTx(() -> {\n            ClassLoader globalClassLoader = classLoaderService.getClassLoader(ClassLoaderIdentifier.GLOBAL);\n            Class<?> clazz = globalClassLoader.loadClass(\"org.bonitasoft.engine.classloader.GlobalClass3\");\n            final ClassLoader classLoader = clazz.getClassLoader();\n            checkGlobalClassLoader(classLoader);\n            assertSameClassloader(globalClassLoader, classLoader);\n            platformDependencyService.deleteDependencies(ClassLoaderIdentifier.GLOBAL_ID,\n                    ClassLoaderIdentifier.GLOBAL_TYPE);\n            classLoaderService.refreshClassLoaderImmediately(ClassLoaderIdentifier.GLOBAL);\n\n        });\n        inTx(() -> {\n            ClassLoader globalClassLoader = classLoaderService.getClassLoader(ClassLoaderIdentifier.GLOBAL);\n            Class<?> clazz = globalClassLoader.loadClass(\"org.bonitasoft.engine.classloader.GlobalClass3\");\n            // dependency is not in the bonita classloader anymore but only in the Test classloader\n            assertFalse(isBonitaClassLoader(clazz.getClassLoader()));\n        });\n\n    }\n\n    @Test\n    public void testLoadNotInPathGlobalClassUsingGlobalClassLoader() throws Exception {\n        initializeClassLoaderService();\n        addNotInPathDependencies();\n        // getTransactionService().begin();\n\n        final ClassLoader globalClassLoader = classLoaderService.getClassLoader(ClassLoaderIdentifier.GLOBAL);\n        final Class<?> clazz = globalClassLoader.loadClass(\"org.bonitasoft.classloader.test.NotInPathGlobalClass1\");\n        final ClassLoader classLoader = clazz.getClassLoader();\n        checkGlobalClassLoader(classLoader);\n\n        assertSameClassloader(globalClassLoader, classLoader);\n        // getTransactionService().complete();\n    }\n\n    @Test\n    public void testLoadNotInPathGlobalClassUsingLocalClassLoader() throws Exception {\n        initializeClassLoaderService();\n        addNotInPathDependencies();\n        // getTransactionService().begin();\n\n        final ClassLoader localClassLoader = classLoaderService.getClassLoader(identifier(PROCESS, ID1));\n        final Class<?> clazz = localClassLoader.loadClass(\"org.bonitasoft.classloader.test.NotInPathGlobalClass1\");\n        checkGlobalClassLoader(clazz.getClassLoader());\n        assertNotSameClassloader(localClassLoader, clazz.getClassLoader());\n        assertSameClassloader(classLoaderService.getClassLoader(ClassLoaderIdentifier.GLOBAL), clazz.getClassLoader());\n        // getTransactionService().complete();\n    }\n\n    @Test\n    public void testLoadNotInPathLocalClassUsingLocalClassLoader() throws Exception {\n        initializeClassLoaderService();\n        addNotInPathDependencies();\n        // getTransactionService().begin();\n\n        final ClassLoader localClassLoader = classLoaderService.getClassLoader(identifier(PROCESS, ID1));\n        final Class<?> clazz = localClassLoader.loadClass(\"org.bonitasoft.classloader.test.NotInPathLocalClass1\");\n        final ClassLoader classLoader = clazz.getClassLoader();\n        checkLocalClassLoader(classLoader);\n\n        assertSameClassloader(localClassLoader, classLoader);\n        // getTransactionService().complete();\n    }\n\n    @Test(expected = ClassNotFoundException.class)\n    public void testLoadNotInPathLocalClassUsingWrongLocalClassLoader() throws Exception {\n        initializeClassLoaderService();\n        addNotInPathDependencies();\n        // getTransactionService().begin();\n\n        final ClassLoader classLoader = classLoaderService.getClassLoader(identifier(PROCESS, ID2));\n        // getTransactionService().complete();\n        classLoader.loadClass(\"org.bonitasoft.classloader.test.NotInPathLocalClass1\");\n        fail(\"load class with wrong classloader\");\n    }\n\n    @Test\n    public void testLoadNotInPathSharedClassUsingGlobalClassLoader() throws Exception {\n        initializeClassLoaderService();\n        addNotInPathDependencies();\n        // getTransactionService().begin();\n\n        final ClassLoader globalClassLoader = classLoaderService.getClassLoader(ClassLoaderIdentifier.GLOBAL);\n        final Class<?> clazz = globalClassLoader.loadClass(\"org.bonitasoft.classloader.test.NotInPathSharedClass1\");\n        final ClassLoader classLoader = clazz.getClassLoader();\n        checkGlobalClassLoader(classLoader);\n\n        assertSameClassloader(globalClassLoader, classLoader);\n        // getTransactionService().complete();\n    }\n\n    @Test\n    public void testLoadNotInPathSharedClassUsingLocalClassLoader() throws Exception {\n        initializeClassLoaderService();\n        addNotInPathDependencies();\n        // getTransactionService().begin();\n\n        final ClassLoader localClassLoader = classLoaderService.getClassLoader(identifier(PROCESS, ID1));\n        final Class<?> clazz = localClassLoader.loadClass(\"org.bonitasoft.classloader.test.NotInPathSharedClass1\");\n        final ClassLoader classLoader = clazz.getClassLoader();\n        checkGlobalClassLoader(classLoader);\n\n        // getTransactionService().complete();\n    }\n\n    @Test\n    public void loadResource() throws Exception {\n        initializeClassLoaderService();\n        getTransactionService().begin();\n        final URL resourceFile = ClassLoaderServiceIT.class.getResource(\"resource.txt\");\n        final byte[] resourceFileContent = IOUtil.getAllContentFrom(resourceFile);\n\n        createPlatformDependency(\"resource\", \"resource.txt\", resourceFileContent);\n        getTransactionService().complete();\n\n        final ClassLoader virtualGlobalClassLoader = classLoaderService.getClassLoader(ClassLoaderIdentifier.GLOBAL);\n        final InputStream resourceStream = virtualGlobalClassLoader.getResourceAsStream(\"resource.txt\");\n        assertEquals(resourceFileContent.length, IOUtil.getAllContentFrom(resourceStream).length);\n        resourceStream.close();\n    }\n\n    @Test\n    public void different_applications_should_have_same_global_classLoader() throws Exception {\n        initializeClassLoaderServiceWithTwoApplications();\n        final ClassLoader process1Classloader = classLoaderService.getClassLoader(identifier(PROCESS, ID1));\n        final ClassLoader tenant1Classloader = classLoaderService.getClassLoader(ClassLoaderIdentifier.TENANT);\n\n        final Class<?> sharedClassLoadedFromProcess1 = process1Classloader\n                .loadClass(\"org.bonitasoft.engine.classloader.SharedClass1\");\n        final Class<?> sharedClassLoadedFromTenant1 = tenant1Classloader\n                .loadClass(\"org.bonitasoft.engine.classloader.SharedClass1\");\n        checkGlobalClassLoader(sharedClassLoadedFromProcess1.getClassLoader());\n        assertSameClassloader(classLoaderService.getClassLoader(ClassLoaderIdentifier.GLOBAL),\n                sharedClassLoadedFromProcess1.getClassLoader());\n\n        checkGlobalClassLoader(sharedClassLoadedFromTenant1.getClassLoader());\n        assertSameClassloader(classLoaderService.getClassLoader(ClassLoaderIdentifier.GLOBAL),\n                sharedClassLoadedFromTenant1.getClassLoader());\n    }\n\n    private void checkGlobalClassLoader(final ClassLoader classLoader) {\n        assertThat(((BonitaClassLoader) classLoader).getIdentifier()).isEqualTo(ClassLoaderIdentifier.GLOBAL);\n    }\n\n    private void checkLocalClassLoader(final ClassLoader classLoader) {\n        assertThat(((BonitaClassLoader) classLoader).getIdentifier()).isNotEqualTo(ClassLoaderIdentifier.GLOBAL);\n    }\n\n    private boolean isBonitaClassLoader(final ClassLoader classLoader) {\n        return classLoader instanceof BonitaClassLoader;\n    }\n\n    private void assertSameClassloader(final ClassLoader classLoader1, final ClassLoader classLoader2) {\n        assertThat(classLoader1).isNotNull().isEqualTo(classLoader2);\n    }\n\n    private void assertNotSameClassloader(final ClassLoader classLoader1, final ClassLoader classLoader2) {\n        assertThat(classLoader1).isNotNull().isNotEqualTo(classLoader2);\n    }\n\n    @Test\n    public void should_getResource_point_to_existing_file_after_classloader_refresh() throws Exception {\n        //given a classloader that is refreshed\n        getTransactionService().begin();\n        Map<String, byte[]> jarResources = Collections.singletonMap(\"test.xml\", \"<node>content</mode>\".getBytes());\n        byte[] jarContent = generateJar(jarResources);\n        dependencyService.createMappedDependency(\"myResource.jar\", jarContent, \"myResource.jar\", ID1,\n                PROCESS);\n        getTransactionService().complete();\n        getTransactionService().begin();\n        classLoaderService.refreshClassLoaderImmediately(identifier(PROCESS, ID1));\n        getTransactionService().complete();\n        getTransactionService().begin();\n        classLoaderService.refreshClassLoaderImmediately(identifier(PROCESS, ID1));\n        getTransactionService().complete();\n\n        //when\n        ClassLoader localClassLoader = classLoaderService.getClassLoader(identifier(PROCESS, ID1));\n        URL resource = localClassLoader.getResource(\"test.xml\");\n        assertThat(resource).isNotNull();\n        String stringUrl = resource.toExternalForm();\n        URL url = new URL(stringUrl);\n\n        //then\n        assertThat(url).isNotNull();\n        String contentFromUrlOfTheClassLoader = readUrl(resource);\n        String contentFromUrlRecreated = readUrl(url);\n\n        assertThat(contentFromUrlOfTheClassLoader).isEqualTo(\"<node>content</mode>\");\n        assertThat(contentFromUrlRecreated).isEqualTo(\"<node>content</mode>\");\n\n    }\n\n    @Test\n    public void in_case_of_rollback_classloader_should_end_in_same_state_after_calling_refreshClassloaderImmediatelyWithRollback()\n            throws Exception {\n        getTransactionService().begin();\n        dependencyService.createMappedDependency(\"myResource.jar\",\n                IOUtil.generateJar(Collections.singletonMap(\"test.xml\", \"<node>content</mode>\".getBytes())),\n                \"myResource.jar\", ID1, PROCESS);\n        classLoaderService.refreshClassLoaderAfterUpdate(identifier(PROCESS, ID1));\n        getTransactionService().complete();\n        assertThat(classLoaderService.getClassLoader(identifier(PROCESS, ID1)).getResource(\"test.xml\")).isNotNull();\n\n        getTransactionService().begin();\n        dependencyService.createMappedDependency(\"myResource2.jar\",\n                IOUtil.generateJar(Collections.singletonMap(\"test2.xml\", \"<node>content</mode>\".getBytes())),\n                \"myResource2.jar\", ID1, PROCESS);\n        classLoaderService.refreshClassLoaderImmediatelyWithRollback(identifier(PROCESS, ID1));\n        assertThat(classLoaderService.getClassLoader(identifier(PROCESS, ID1)).getResource(\"test2.xml\")).isNotNull();\n        getTransactionService().setRollbackOnly();\n        getTransactionService().complete();\n        assertThat(classLoaderService.getClassLoader(identifier(PROCESS, ID1)).getResource(\"test.xml\")).isNotNull();\n        assertThat(classLoaderService.getClassLoader(identifier(PROCESS, ID1)).getResource(\"test2.xml\")).isNull();\n\n    }\n\n    @Test\n    public void refreshClassloaderImmediatelyWithRollback_should_update_classloader_if_transaction_is_committed()\n            throws Exception {\n        getTransactionService().begin();\n        dependencyService.createMappedDependency(\"myResource.jar\",\n                IOUtil.generateJar(Collections.singletonMap(\"test.xml\", \"<node>content</mode>\".getBytes())),\n                \"myResource.jar\", ID1, PROCESS);\n        classLoaderService.refreshClassLoaderAfterUpdate(identifier(PROCESS, ID1));\n        getTransactionService().complete();\n        assertThat(classLoaderService.getClassLoader(identifier(PROCESS, ID1)).getResource(\"test.xml\")).isNotNull();\n\n        getTransactionService().begin();\n        dependencyService.createMappedDependency(\"myResource2.jar\",\n                IOUtil.generateJar(Collections.singletonMap(\"test2.xml\", \"<node>content</mode>\".getBytes())),\n                \"myResource2.jar\", ID1, PROCESS);\n        classLoaderService.refreshClassLoaderImmediatelyWithRollback(identifier(PROCESS, ID1));\n        assertThat(classLoaderService.getClassLoader(identifier(PROCESS, ID1)).getResource(\"test2.xml\")).isNotNull();\n        getTransactionService().complete();\n        assertThat(classLoaderService.getClassLoader(identifier(PROCESS, ID1)).getResource(\"test.xml\")).isNotNull();\n        assertThat(classLoaderService.getClassLoader(identifier(PROCESS, ID1)).getResource(\"test2.xml\")).isNotNull();\n\n    }\n\n    private String readUrl(URL resource) throws IOException {\n        URLConnection urlConnection = resource.openConnection();\n        BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));\n        String inputLine;\n        StringBuilder content = new StringBuilder();\n        while ((inputLine = in.readLine()) != null)\n            content.append(inputLine);\n        in.close();\n        return content.toString();\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/classloader/GlobalClass1.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.classloader;\n\n/**\n * Only for test\n */\npublic class GlobalClass1 {\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/classloader/GlobalClass2.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.classloader;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class GlobalClass2 {\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/classloader/GlobalClass3.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.classloader;\n\npublic class GlobalClass3 {\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/classloader/LocalClass1.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.classloader;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class LocalClass1 {\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/classloader/LocalClass2.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.classloader;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class LocalClass2 {\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/classloader/LocalClass3.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.classloader;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class LocalClass3 {\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/classloader/LocalClass4.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.classloader;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class LocalClass4 {\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/classloader/OnlyInPathClass1.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.classloader;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class OnlyInPathClass1 {\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/classloader/SharedClass1.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.classloader;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SharedClass1 {\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/command/CommandServiceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport static org.junit.Assert.*;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.CommonBPMServicesTest;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.command.model.SCommand;\nimport org.bonitasoft.engine.command.model.SCommandCriterion;\nimport org.bonitasoft.engine.command.model.SCommandUpdateBuilderFactory;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Zhang Bole\n */\npublic class CommandServiceIT extends CommonBPMServicesTest {\n\n    private CommandService commandService;\n\n    @Before\n    public void setup() {\n        commandService = getServiceAccessor().getCommandService();\n    }\n\n    @After\n    public void restoreDefaultCommands() throws SBonitaException {\n        getTransactionService().begin();\n        getServiceAccessor().getCommandService().start();\n        getTransactionService().complete();\n    }\n\n    @Test(expected = SCommandAlreadyExistsException.class)\n    public void testSCommandAlreadyExistsException() throws Exception {\n        getTransactionService().begin();\n        final SCommand command = SCommand.builder()\n                .name(\"createCommand\")\n                .description(\"this is a command\")\n                .implementation(\"command implementation\")\n                .build();\n        commandService.create(command);\n        try {\n            commandService.create(command);\n        } finally {\n            commandService.delete(\"createCommand\");\n            getTransactionService().complete();\n        }\n    }\n\n    @Test(expected = SCommandNotFoundException.class)\n    public void testSCommandNotFoundException() throws Exception {\n        getTransactionService().begin();\n        try {\n            commandService.get(\"a\");\n        } finally {\n            getTransactionService().complete();\n        }\n\n    }\n\n    @Test\n    public void testCreateCommand() throws Exception {\n        getTransactionService().begin();\n        final SCommand command1 = SCommand.builder()\n                .name(\"createCommand\")\n                .description(\"this is a command\")\n                .implementation(\"command implementation\")\n                .build();\n        commandService.create(command1);\n        final SCommand command2 = commandService.get(\"createCommand\");\n        assertNotNull(\"can't find the category after adding it\", command2);\n        assertEquals(\"can't retrieve the same category\", command1.getName(), command2.getName());\n        assertEquals(\"can't retrieve the same category\", command1.getId(), command2.getId());\n        commandService.delete(\"createCommand\");\n        getTransactionService().complete();\n    }\n\n    @Test(expected = SCommandNotFoundException.class)\n    public void testDeleteCommand() throws Exception {\n        getTransactionService().begin();\n        final SCommand command = SCommand.builder()\n                .name(\"testCommandDelete\")\n                .description(\"this is a command\")\n                .implementation(\"command implementation\").build();\n        commandService.create(command);\n        commandService.delete(\"testCommandDelete\");\n        try {\n            commandService.get(\"testCommandDelete\");\n        } finally {\n            getTransactionService().complete();\n        }\n    }\n\n    @Test\n    public void testDeleteAll() throws Exception {\n        getTransactionService().begin();\n        final int initialNbOfCommands = commandService.getAllCommands(0, 500, SCommandCriterion.NAME_ASC).size();\n\n        final SCommand command1 = SCommand.builder()\n                .name(\"createCommand1\")\n                .description(\"this is a command\")\n                .implementation(\"command implementation\").build();\n        final SCommand command2 = SCommand.builder()\n                .name(\"createCommand2\")\n                .description(\"this is a command\")\n                .implementation(\"command implementation\").build();\n        final SCommand command3 = SCommand.builder()\n                .name(\"createCommand3\")\n                .description(\"this is a command\")\n                .implementation(\"command implementation\").build();\n        commandService.create(command1);\n        commandService.create(command2);\n        commandService.create(command3);\n        final List<SCommand> commands1 = commandService.getAllCommands(0, 500, SCommandCriterion.NAME_ASC);\n        assertEquals(3 + initialNbOfCommands, commands1.size());\n\n        commandService.deleteAll();\n        final List<SCommand> commands2 = commandService.getAllCommands(0, 500, SCommandCriterion.NAME_ASC);\n        assertTrue(commands2.isEmpty());\n        getTransactionService().setRollbackOnly(); // so that existing commands are restored\n        getTransactionService().complete();\n    }\n\n    @Test\n    public void testUpdateCommand() throws Exception {\n        getTransactionService().begin();\n        final SCommand oldCommand = SCommand.builder()\n                .name(\"old\")\n                .description(\"this is an old command\")\n                .implementation(\"command implementation\")\n                .build();\n        commandService.create(oldCommand);\n        assertEquals(\"old\", oldCommand.getName());\n        assertEquals(\"this is an old command\", oldCommand.getDescription());\n\n        final String commandName = \"new\";\n        final EntityUpdateDescriptor updateDescriptor = BuilderFactory.get(SCommandUpdateBuilderFactory.class)\n                .createNewInstance().updateName(commandName)\n                .updateDescription(\"this is a new command\").done();\n        commandService.update(oldCommand, updateDescriptor);\n        final SCommand newCommand = commandService.get(commandName);\n        assertEquals(\"new\", newCommand.getName());\n        assertEquals(\"this is a new command\", newCommand.getDescription());\n        commandService.delete(commandName);\n        getTransactionService().complete();\n    }\n\n    @Test\n    public void testget() throws Exception {\n        getTransactionService().begin();\n        final SCommand sCommand = SCommand.builder()\n                .name(\"commandOne\")\n                .description(\"this is a command\")\n                .implementation(\"command implementation\")\n                .build();\n        commandService.create(sCommand);\n        final SCommand command = commandService.get(\"commandOne\");\n        assertEquals(\"commandOne\", command.getName());\n        assertEquals(\"this is a command\", command.getDescription());\n        commandService.delete(\"commandOne\");\n        getTransactionService().complete();\n    }\n\n    @Test\n    public void testGetCommandsWithCriterion() throws Exception {\n        getTransactionService().begin();\n        final int initialNbOfCommands = commandService.getAllCommands(0, 500, SCommandCriterion.NAME_ASC).size();\n\n        final SCommand sCommand1 = SCommand.builder()\n                .name(\"aaaaa\")\n                .description(\"this is command1\")\n                .implementation(\"command implementation\")\n                .build();\n        final SCommand sCommand2 = SCommand.builder()\n                .name(\"aaaab\")\n                .description(\"this is command2\")\n                .implementation(\"command implementation\")\n                .build();\n        final SCommand sCommand3 = SCommand.builder()\n                .name(\"aaaac\")\n                .description(\"this is command3\")\n                .implementation(\"command implementation\")\n                .build();\n        commandService.create(sCommand1);\n        commandService.create(sCommand2);\n        commandService.create(sCommand3);\n\n        final List<SCommand> commands = commandService.getAllCommands(0, 500, SCommandCriterion.NAME_ASC);\n        assertEquals(3 + initialNbOfCommands, commands.size());\n        assertEquals(\"aaaaa\", commands.get(0).getName());\n        assertEquals(\"aaaab\", commands.get(1).getName());\n        assertEquals(\"aaaac\", commands.get(2).getName());\n\n        commandService.delete(sCommand1.getId());\n        commandService.delete(sCommand2.getId());\n        commandService.delete(sCommand3.getId());\n        getTransactionService().complete();\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/core/form/impl/FormMappingServiceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.form.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.fail;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.CommonBPMServicesTest;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.core.form.FormMappingService;\nimport org.bonitasoft.engine.core.form.SFormMapping;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.form.FormMappingTarget;\nimport org.bonitasoft.engine.form.FormMappingType;\nimport org.bonitasoft.engine.page.PageService;\nimport org.bonitasoft.engine.page.SPage;\nimport org.bonitasoft.engine.page.URLAdapterConstants;\nimport org.bonitasoft.engine.test.CommonTestUtil;\nimport org.bonitasoft.engine.transaction.TransactionService;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Baptiste Mesta\n */\npublic class FormMappingServiceIT extends CommonBPMServicesTest {\n\n    public static final String PAGE_NAME = \"custompage_coucou\";\n    public FormMappingService formMappingService;\n    private PageService pageService;\n    private TransactionService transactionService;\n    private SPage page;\n    private ProcessDefinitionService processDefinitionService;\n    private SProcessDefinition p1;\n    private SProcessDefinition p2;\n\n    @Before\n    public void setup() throws Exception {\n        processDefinitionService = getServiceAccessor().getProcessDefinitionService();\n        transactionService = getTransactionService();\n        formMappingService = getServiceAccessor().getFormMappingService();\n        pageService = getServiceAccessor().getPageService();\n        transactionService.begin();\n\n        p1 = buildSProcessDefinition(\"P1\", \"1.0\");\n        p2 = buildSProcessDefinition(\"P2\", \"1.0\");\n        page = pageService.addPage(\n                CommonTestUtil.createTestPageContent(PAGE_NAME, \"coucou depuis la page\",\n                        \"C'était juste pour dire coucou\"),\n                \"mySuperPage.zip\",\n                54L);\n        transactionService.complete();\n    }\n\n    @After\n    public void tearDown() throws Exception {\n        clearFormMapping();\n    }\n\n    protected void clearFormMapping() throws Exception {\n        transactionService.begin();\n        for (SFormMapping sFormMapping : formMappingService.list(0, 1000)) {\n            formMappingService.delete(sFormMapping);\n        }\n        pageService.deletePage(page.getId());\n        processDefinitionService.delete(p1.getId());\n        processDefinitionService.delete(p2.getId());\n        transactionService.complete();\n    }\n\n    @Test\n    public void createAndListFormMapping() throws Exception {\n        //given\n        transactionService.begin();\n\n        formMappingService.create(p1.getId(), \"step1\", FormMappingType.TASK.getId(), \"INTERNAL\", PAGE_NAME);\n        formMappingService.create(p1.getId(), null, FormMappingType.PROCESS_START.getId(), \"URL\", \"http://bit.coin\");\n        formMappingService.create(p1.getId(), null, FormMappingType.PROCESS_OVERVIEW.getId(), \"LEGACY\", null);\n        formMappingService.create(p2.getId(), null, FormMappingType.PROCESS_OVERVIEW.getId(), \"UNDEFINED\", null);\n        transactionService.complete();\n\n        transactionService.begin();\n        List<SFormMapping> list = formMappingService.list(p1.getId(), 0, 10);\n        List<SFormMapping> listAll = formMappingService.list(0, 10);\n\n        transactionService.complete();\n        assertThat(list).extracting(\"type\").containsExactlyInAnyOrder(FormMappingType.TASK.getId(),\n                FormMappingType.PROCESS_START.getId(),\n                FormMappingType.PROCESS_OVERVIEW.getId());\n        assertThat(list).extracting(\"task\").containsExactlyInAnyOrder(\"step1\", null, null);\n        assertThat(list).extracting(\"pageMapping.url\").containsExactlyInAnyOrder(null, \"http://bit.coin\", null);\n        assertThat(list).extracting(\"pageMapping.urlAdapter\").containsExactlyInAnyOrder(null,\n                URLAdapterConstants.EXTERNAL_URL_ADAPTER,\n                URLAdapterConstants.LEGACY_URL_ADAPTER);\n        assertThat(list).extracting(\"pageMapping.pageId\").containsExactlyInAnyOrder(page.getId(), null, null);\n        //        assertThat(list).extracting(\"pageMapping.key\").containsExactlyInAnyOrder();\n        assertThat(listAll).extracting(\"type\").containsExactlyInAnyOrder(FormMappingType.TASK.getId(),\n                FormMappingType.PROCESS_START.getId(),\n                FormMappingType.PROCESS_OVERVIEW.getId(), FormMappingType.PROCESS_OVERVIEW.getId());\n        assertThat(listAll).extracting(\"processDefinitionId\").containsExactlyInAnyOrder(p1.getId(), p1.getId(),\n                p1.getId(), p2.getId());\n    }\n\n    @Test\n    public void create_and_get_FormMapping() throws Exception {\n        transactionService.begin();\n        SFormMapping taskForm = formMappingService.create(p1.getId(), \"step1\", FormMappingType.TASK.getId(), \"URL\",\n                \"http://bit.coin\");\n        transactionService.complete();\n\n        transactionService.begin();\n        SFormMapping sFormMapping = formMappingService.get(taskForm.getId());\n        SFormMapping sFormMappingByProperties = formMappingService.get(p1.getId(), FormMappingType.TASK.getId(),\n                \"step1\");\n        transactionService.complete();\n        assertThat(sFormMapping).isEqualTo(taskForm).isEqualTo(sFormMappingByProperties);\n    }\n\n    @Test\n    public void create_and_get_by_key_FormMapping() throws Exception {\n        transactionService.begin();\n        SFormMapping taskForm = formMappingService.create(p1.getId(), \"step1\", FormMappingType.TASK.getId(), \"URL\",\n                \"http://bit.coin\");\n        transactionService.complete();\n\n        transactionService.begin();\n        SFormMapping sFormMapping = formMappingService.get(taskForm.getPageMapping().getKey());\n        SFormMapping sFormMappingByProperties = formMappingService.get(p1.getId(), FormMappingType.TASK.getId(),\n                \"step1\");\n        transactionService.complete();\n        assertThat(sFormMapping).isEqualTo(taskForm).isEqualTo(sFormMappingByProperties);\n    }\n\n    @Test\n    public void create_and_get_FormMapping_with_no_task() throws Exception {\n        transactionService.begin();\n        SFormMapping taskForm = formMappingService.create(p1.getId(), \"task\", FormMappingType.TASK.getId(), \"URL\",\n                \"http://bit.coin\");\n        transactionService.complete();\n\n        transactionService.begin();\n        SFormMapping sFormMapping = formMappingService.get(taskForm.getId());\n        SFormMapping sFormMappingByProperties = formMappingService.get(p1.getId(), FormMappingType.TASK.getId());\n        transactionService.complete();\n        assertThat(sFormMapping).isEqualTo(taskForm);\n        assertThat(sFormMappingByProperties).isEqualTo(sFormMapping);\n    }\n\n    @Test\n    public void delete_FormMapping() throws Exception {\n        transactionService.begin();\n        SFormMapping taskForm = formMappingService.create(p1.getId(), \"step1\", FormMappingType.TASK.getId(), \"URL\",\n                \"http://bit.coin\");\n        transactionService.complete();\n\n        transactionService.begin();\n        formMappingService.delete(formMappingService.get(taskForm.getId()));\n        transactionService.complete();\n\n        transactionService.begin();\n        try {\n            formMappingService.get(taskForm.getId());\n            fail(\"should have thrown a not found Exception\");\n        } catch (SObjectNotFoundException e) {\n            //ok\n        }\n        transactionService.complete();\n    }\n\n    @Test\n    public void update_FormMapping() throws Exception {\n        transactionService.begin();\n        SFormMapping taskForm = formMappingService.create(p1.getId(), \"step1\", FormMappingType.TASK.getId(), \"URL\",\n                \"http://bit.coin\");\n        transactionService.complete();\n\n        transactionService.begin();\n        SFormMapping sFormMapping = formMappingService.get(taskForm.getId());\n        formMappingService.update(sFormMapping, \"newFormName\", null);\n        transactionService.complete();\n\n        transactionService.begin();\n        SFormMapping updatedInDatabase = formMappingService.get(taskForm.getId());\n        transactionService.complete();\n\n        assertThat(sFormMapping).isEqualTo(updatedInDatabase);\n        assertThat(updatedInDatabase.getPageMapping().getUrl()).isEqualTo(\"newFormName\");\n        assertThat(updatedInDatabase.getTarget()).isEqualTo(FormMappingTarget.URL.name());\n        assertThat(updatedInDatabase.getLastUpdateDate()).isGreaterThan(taskForm.getLastUpdateDate());\n\n        Thread.sleep(10);\n\n        transactionService.begin();\n        SFormMapping reupdated = formMappingService.get(taskForm.getId());\n        formMappingService.update(reupdated, null, page.getId());\n        transactionService.complete();\n\n        assertThat(reupdated.getPageMapping().getUrl()).isNull();\n        assertThat(reupdated.getPageMapping().getPageId()).isEqualTo(page.getId());\n        assertThat(reupdated.getTarget()).isEqualTo(FormMappingTarget.INTERNAL.name());\n        assertThat(reupdated.getLastUpdateDate()).isGreaterThan(updatedInDatabase.getLastUpdateDate());\n\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/core/form/impl/custom/CustomAuthorizationRuleMappingImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.form.impl.custom;\n\nimport static org.bonitasoft.engine.page.AuthorizationRuleConstants.*;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.form.AuthorizationRuleMapping;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class CustomAuthorizationRuleMappingImpl implements AuthorizationRuleMapping {\n\n    @Override\n    public List<String> getProcessStartRuleKeys() {\n        return Arrays.asList(IS_ADMIN, IS_PROCESS_OWNER, IS_ACTOR_INITIATOR);\n    }\n\n    @Override\n    public List<String> getProcessOverviewRuleKeys() {\n        return Arrays.asList(IS_ADMIN, IS_PROCESS_OWNER, IS_PROCESS_INITIATOR, IS_TASK_PERFORMER,\n                IS_INVOLVED_IN_PROCESS_INSTANCE,\n                IS_PROCESS_INITIATOR + \"_CUSTOM\");\n    }\n\n    @Override\n    public List<String> getTaskRuleKeys() {\n        return Arrays.asList(IS_ADMIN, IS_PROCESS_OWNER, IS_TASK_AVAILABLE_FOR_USER);\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/core/form/impl/custom/CustomIsProcessInitiatorRule.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.form.impl.custom;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.commons.exceptions.SExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.page.AuthorizationRule;\nimport org.bonitasoft.engine.page.AuthorizationRuleConstants;\nimport org.bonitasoft.engine.page.AuthorizationRuleWithParameters;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class CustomIsProcessInitiatorRule extends AuthorizationRuleWithParameters implements AuthorizationRule {\n\n    private final ProcessInstanceService processInstanceService;\n\n    private final SessionService sessionService;\n\n    private final SessionAccessor sessionAccessor;\n\n    // autowired by spring\n    public CustomIsProcessInitiatorRule(ProcessInstanceService processInstanceService, SessionService sessionService,\n            SessionAccessor sessionAccessor) {\n        this.processInstanceService = processInstanceService;\n        this.sessionAccessor = sessionAccessor;\n        this.sessionService = sessionService;\n    }\n\n    @Override\n    public boolean isAllowed(String key, Map<String, Serializable> context) throws SExecutionException {\n        //add business logic here\n        return true;\n    }\n\n    @Override\n    public String getId() {\n        return AuthorizationRuleConstants.IS_PROCESS_INITIATOR + \"_CUSTOM\";\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/data/instance/DataInstanceIntegrationLocalIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance;\n\nimport static org.junit.Assert.assertNotNull;\n\nimport java.io.IOException;\n\nimport org.bonitasoft.engine.bpm.bar.BarResource;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.CallActivityBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.io.IOUtil;\nimport org.bonitasoft.engine.operation.OperatorType;\nimport org.bonitasoft.engine.process.Employee;\nimport org.bonitasoft.engine.test.BuildTestUtil;\nimport org.bonitasoft.engine.test.CommonAPILocalIT;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class DataInstanceIntegrationLocalIT extends CommonAPILocalIT {\n\n    private User cebolinha;\n\n    private User cascao;\n\n    @Before\n    public void before() throws Exception {\n        loginWithTechnicalUser();\n        cebolinha = createUser(\"cebolinha\", \"bpm\");\n        cascao = createUser(\"cascao\", \"bpm\");\n    }\n\n    @After\n    public void after() throws Exception {\n        deleteUsers(cebolinha, cascao);\n        logout();\n    }\n\n    @Test\n    public void processWithCustomData() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithCustomData\", \"1.0\");\n        builder.addUserTask(\"step1\", \"actor\");\n        builder.addActor(\"actor\");\n        final Expression expression = new ExpressionBuilder().createGroovyScriptExpression(\"initEmployee\",\n                \"import org.bonitasoft.engine.process.Employee; \\n\"\n                        + \"return new Employee(\\\"TÃ¤htikuja\\\", \\\"firstName48\\\")\",\n                \"org.bonitasoft.engine.process.Employee\");\n        builder.addData(\"address\", \"org.bonitasoft.engine.process.Employee\", expression);\n\n        final BusinessArchive businessArchive = addClasspathRessource(builder).done();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchive, \"actor\", cascao);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n\n        assertNotNull(getProcessAPI().getProcessDataInstance(\"address\", processInstance.getId()).getValue());\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void callActivityWith2CustomDatasOnActivityUsingSameExternalLibrairy() throws Exception {\n        final String targetProcessName = \"targetProcess\";\n        final Expression targetProcessNameExpr = new ExpressionBuilder()\n                .createConstantStringExpression(targetProcessName);\n        final Expression targetProcessVersionExpr = new ExpressionBuilder()\n                .createConstantStringExpression(PROCESS_VERSION);\n\n        // Build target process\n        final Expression defaultValueForEmployeeOnTStep1 = new ExpressionBuilder().createGroovyScriptExpression(\n                \"initEmployee\",\n                \"import org.bonitasoft.engine.process.Employee; \\n\"\n                        + \"return new Employee(\\\"TÃ¤htikuja\\\", \\\"firstName48\\\")\",\n                \"org.bonitasoft.engine.process.Employee\");\n        final ProcessDefinitionBuilder targetProcessDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(targetProcessName, PROCESS_VERSION);\n        targetProcessDefinitionBuilder.addActor(ACTOR_NAME).addStartEvent(\"tStart\");\n        targetProcessDefinitionBuilder.addUserTask(\"tStep1\", ACTOR_NAME).addData(\"data\", Employee.class.getName(),\n                defaultValueForEmployeeOnTStep1);\n        targetProcessDefinitionBuilder.addEndEvent(\"tEnd\");\n        targetProcessDefinitionBuilder.addTransition(\"tStart\", \"tStep1\").addTransition(\"tStep1\", \"tEnd\");\n        final ProcessDefinition targetProcessDefinition = deployAndEnableProcessWithActor(\n                addClasspathRessource(targetProcessDefinitionBuilder).done(),\n                ACTOR_NAME, cebolinha);\n\n        // Build main process\n        final Expression defaultValueForEmployeeOnStep1 = new ExpressionBuilder().createGroovyScriptExpression(\n                \"initEmployee\",\n                \"import org.bonitasoft.engine.process.Employee; \\n\" + \"return new Employee(\\\"name\\\", \\\"firstName\\\")\",\n                \"org.bonitasoft.engine.process.Employee\");\n        final ProcessDefinitionBuilder callingProcessDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"callingProcess\", PROCESS_VERSION);\n        callingProcessDefinitionBuilder.addActor(ACTOR_NAME).addStartEvent(\"start\");\n        callingProcessDefinitionBuilder.addUserTask(\"step1\", ACTOR_NAME).addData(\"data\", Employee.class.getName(),\n                defaultValueForEmployeeOnStep1);\n        callingProcessDefinitionBuilder.addCallActivity(\"callActivity\", targetProcessNameExpr, targetProcessVersionExpr)\n                .addDisplayName(new ExpressionBuilder().createConstantStringExpression(\"callActivityDisplayName\"))\n                .addDescription(\"callActivityDescription\")\n                .addDisplayDescription(\n                        new ExpressionBuilder().createConstantStringExpression(\"callActivityDisplayDescription\"));\n        callingProcessDefinitionBuilder.addEndEvent(\"end\");\n        callingProcessDefinitionBuilder.addTransition(\"start\", \"step1\").addTransition(\"step1\", \"callActivity\")\n                .addTransition(\"callActivity\", \"end\");\n        final ProcessDefinition callingProcessDefinition = deployAndEnableProcessWithActor(\n                addClasspathRessource(callingProcessDefinitionBuilder).done(),\n                ACTOR_NAME, cascao);\n        final ProcessInstance callingProcessInstance = getProcessAPI().startProcess(callingProcessDefinition.getId());\n\n        final long step1Id = waitForUserTask(callingProcessInstance, \"step1\");\n        assertNotNull(getProcessAPI().getActivityDataInstance(\"data\", step1Id).getValue());\n        assignAndExecuteStep(step1Id, cascao.getId());\n        final long tStep1Id = waitForUserTask(callingProcessInstance, \"tStep1\");\n        assertNotNull(getProcessAPI().getActivityDataInstance(\"data\", tStep1Id).getValue());\n        assignAndExecuteStep(tStep1Id, cebolinha.getId());\n        waitForProcessToFinish(callingProcessInstance);\n\n        // Clean up\n        disableAndDeleteProcess(callingProcessDefinition, targetProcessDefinition);\n    }\n\n    @Test\n    public void callActivityWithMappingAnd2CustomDatasOnProcessUsingSameExternalLibrairy() throws Exception {\n        // getCommandAPI().addDependency(\"Employee.jar\", IOUtil.generateJar(Employee.class));\n\n        final String dataName = \"data\";\n        final String dataType = Employee.class.getName();\n        final String targetProcessName = \"targetProcess\";\n        final Expression targetProcessNameExpr = new ExpressionBuilder()\n                .createConstantStringExpression(targetProcessName);\n        final Expression targetProcessVersionExpr = new ExpressionBuilder()\n                .createConstantStringExpression(PROCESS_VERSION);\n\n        // Build target process\n        final ProcessDefinitionBuilder targetProcessDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(targetProcessName, PROCESS_VERSION);\n        targetProcessDefinitionBuilder.addActor(ACTOR_NAME + 2).addStartEvent(\"tStart\");\n        targetProcessDefinitionBuilder.addUserTask(\"tStep1\", ACTOR_NAME + 2);\n        targetProcessDefinitionBuilder.addData(dataName, dataType, null);\n        targetProcessDefinitionBuilder.addEndEvent(\"tEnd\").addTerminateEventTrigger();\n        targetProcessDefinitionBuilder.addTransition(\"tStart\", \"tStep1\").addTransition(\"tStep1\", \"tEnd\");\n        final ProcessDefinition targetProcessDefinition = deployAndEnableProcessWithActor(\n                addClasspathRessource(targetProcessDefinitionBuilder).done(),\n                ACTOR_NAME + 2, cebolinha);\n\n        // Build main process\n        final Expression defaultValueForEmployeeOnStep1 = new ExpressionBuilder().createGroovyScriptExpression(\n                \"initEmployee\",\n                \"import org.bonitasoft.engine.process.Employee; \\n\" + \"return new Employee(\\\"name\\\", \\\"firstName\\\")\",\n                \"org.bonitasoft.engine.process.Employee\");\n        final ProcessDefinitionBuilder callingProcessDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"callingProcess\", PROCESS_VERSION);\n        callingProcessDefinitionBuilder.addActor(ACTOR_NAME).addStartEvent(\"start\");\n        callingProcessDefinitionBuilder.addData(dataName, dataType, defaultValueForEmployeeOnStep1);\n        callingProcessDefinitionBuilder.addUserTask(\"Step1\", ACTOR_NAME);\n        final CallActivityBuilder callActivity = callingProcessDefinitionBuilder.addCallActivity(\"callActivity\",\n                targetProcessNameExpr,\n                targetProcessVersionExpr);\n        final Expression rightOperand = new ExpressionBuilder().createDataExpression(dataName, dataType);\n        callActivity.addDataInputOperation(\n                BuildTestUtil.buildOperation(dataName, false, OperatorType.ASSIGNMENT, \"=\", rightOperand));\n        callActivity.addDataOutputOperation(\n                BuildTestUtil.buildOperation(dataName, false, OperatorType.ASSIGNMENT, \"=\", rightOperand));\n        callingProcessDefinitionBuilder.addEndEvent(\"end\");\n        callingProcessDefinitionBuilder.addTransition(\"start\", \"callActivity\").addTransition(\"callActivity\", \"Step1\")\n                .addTransition(\"Step1\", \"end\");\n        final ProcessDefinition callingProcessDefinition = deployAndEnableProcessWithActor(\n                addClasspathRessource(callingProcessDefinitionBuilder).done(),\n                ACTOR_NAME, cebolinha);\n        final ProcessInstance callingProcessInstance = getProcessAPI().startProcess(callingProcessDefinition.getId());\n\n        final HumanTaskInstance tStep1 = waitForUserTaskAndGetIt(callingProcessInstance, \"tStep1\");\n        assertNotNull(getProcessAPI().getProcessDataInstance(dataName, tStep1.getParentProcessInstanceId()).getValue());\n        assignAndExecuteStep(tStep1, cebolinha.getId());\n        final HumanTaskInstance step1 = waitForUserTaskAndGetIt(callingProcessInstance, \"Step1\");\n        assertNotNull(getProcessAPI().getProcessDataInstance(dataName, step1.getParentProcessInstanceId()).getValue());\n        assignAndExecuteStep(step1, cebolinha.getId());\n        waitForProcessToFinish(callingProcessInstance);\n\n        // Clean up\n        disableAndDeleteProcess(callingProcessDefinition, targetProcessDefinition);\n    }\n\n    private BusinessArchiveBuilder addClasspathRessource(final ProcessDefinitionBuilder targetProcessDefinitionBuilder)\n            throws InvalidProcessDefinitionException, IOException {\n        final BusinessArchiveBuilder archiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(\n                        targetProcessDefinitionBuilder.done());\n        archiveBuilder.addClasspathResource(new BarResource(\"Employee.jar\", IOUtil.generateJar(Employee.class)));\n        return archiveBuilder;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/data/instance/DataInstanceServiceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance;\n\nimport static org.junit.Assert.*;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.assertj.core.api.Assertions;\nimport org.bonitasoft.engine.archive.ArchiveService;\nimport org.bonitasoft.engine.bpm.CommonBPMServicesTest;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.cache.ehcache.EhCacheCacheService;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.data.ParentContainerResolverImpl;\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\nimport org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilder;\nimport org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilderFactory;\nimport org.bonitasoft.engine.data.definition.model.builder.SXMLDataDefinitionBuilder;\nimport org.bonitasoft.engine.data.definition.model.builder.SXMLDataDefinitionBuilderFactory;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceService;\nimport org.bonitasoft.engine.data.instance.api.impl.DataInstanceServiceImpl;\nimport org.bonitasoft.engine.data.instance.exception.SDataInstanceNotFoundException;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SDataInstanceBuilder;\nimport org.bonitasoft.engine.data.instance.model.SXMLDataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.SADataInstance;\nimport org.bonitasoft.engine.expression.ContainerState;\nimport org.bonitasoft.engine.expression.ExpressionService;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.expression.model.builder.SExpressionBuilder;\nimport org.bonitasoft.engine.expression.model.builder.SExpressionBuilderFactory;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Zhao Na\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @author Emmanuel Duchastenier\n */\n@SuppressWarnings(\"javadoc\")\npublic class DataInstanceServiceIT extends CommonBPMServicesTest {\n\n    private static final Map<Integer, Object> EMPTY_RESOLVED_EXPRESSIONS = Collections.emptyMap();\n\n    protected ExpressionService expressionService;\n\n    protected DataInstanceService dataInstanceService;\n\n    protected ParentContainerResolverImpl parentContainerResolver;\n\n    @Before\n    public void setupDataInstanceService() {\n        final Recorder recorder = getServiceAccessor().getRecorder();\n        final ReadPersistenceService persistenceService = getServiceAccessor().getReadPersistenceService();\n        final ArchiveService archiveService = getServiceAccessor().getArchiveService();\n        expressionService = getServiceAccessor().getExpressionService();\n        parentContainerResolver = (ParentContainerResolverImpl) getServiceAccessor().getParentContainerResolver();\n        dataInstanceService = new DataInstanceServiceImpl(recorder, persistenceService, archiveService);\n        parentContainerResolver.setAllowUnknownContainer(true);\n        final EhCacheCacheService cacheService = (EhCacheCacheService) getServiceAccessor().getCacheService();\n        if (cacheService.isStopped()) {\n            try {\n                cacheService.start();\n            } catch (final SBonitaException e) {\n                throw new RuntimeException(e);\n            }\n        }\n    }\n\n    @After\n    public void tearDown() {\n        final EhCacheCacheService cacheService = (EhCacheCacheService) getServiceAccessor().getCacheService();\n        try {\n            cacheService.stop();\n            cacheService.start();\n        } catch (final SBonitaException e) {\n            throw new RuntimeException(e);\n        }\n        parentContainerResolver.setAllowUnknownContainer(false);\n    }\n\n    public SDataInstance buildDataInstance(final String instanceName, final String className, final String description,\n            final String content,\n            final long containerId, final String containerType, final boolean isTransient) throws SBonitaException {\n        // create definition\n        final SDataDefinitionBuilder dataDefinitionBuilder = BuilderFactory.get(SDataDefinitionBuilderFactory.class)\n                .createNewInstance(instanceName, className);\n        initializeBuilder(dataDefinitionBuilder, description, content, className, isTransient);\n        final SDataDefinition dataDefinition = dataDefinitionBuilder.done();\n\n        final SDataInstanceBuilder dataInstanceBuilder = SDataInstanceBuilder.createNewInstance(dataDefinition);\n        evaluateDefaultValueOf(dataDefinition, dataInstanceBuilder);\n        return dataInstanceBuilder.setContainerId(containerId).setContainerType(containerType).done();\n    }\n\n    private SDataInstance buildDataInstanceConstant(final String instanceName, final String className,\n            final String description, final String content,\n            final long containerId, final String containerType, final boolean isTransient) throws SBonitaException {\n        // create definition\n        final SDataDefinitionBuilder dataDefinitionBuilder = BuilderFactory.get(SDataDefinitionBuilderFactory.class)\n                .createNewInstance(instanceName, className);\n        initializeBuilderConstant(dataDefinitionBuilder, description, content, className, isTransient);\n        final SDataDefinition dataDefinition = dataDefinitionBuilder.done();\n        // create datainstance\n        final SDataInstanceBuilder dataInstanceBuilder = SDataInstanceBuilder.createNewInstance(dataDefinition);\n        evaluateDefaultValueOf(dataDefinition, dataInstanceBuilder);\n        return dataInstanceBuilder.setContainerId(containerId).setContainerType(containerType).done();\n    }\n\n    private void evaluateDefaultValueOf(final SDataDefinition dataDefinition,\n            final SDataInstanceBuilder dataInstanceBuilder) throws SBonitaException {\n        final SExpression expression = dataDefinition.getDefaultValueExpression();\n        if (expression != null) {\n            dataInstanceBuilder.setValue((Serializable) expressionService.evaluate(expression,\n                    Collections.singletonMap(\"processDefinitionId\", 546L), EMPTY_RESOLVED_EXPRESSIONS,\n                    ContainerState.ACTIVE));\n        }\n    }\n\n    private SDataInstance buildLongTextDataInstance(final String instanceName, final String description,\n            final String content, final long containerId,\n            final String containerType, final Boolean isTransient) throws SBonitaException {\n        // create definition\n        final SDataDefinitionBuilder dataDefinitionBuilder = BuilderFactory.get(SDataDefinitionBuilderFactory.class)\n                .createNewTextData(instanceName)\n                .setAsLongText(true);\n\n        initializeBuilder(dataDefinitionBuilder, description, content, String.class.getName(), isTransient);\n        final SDataDefinition dataDefinition = dataDefinitionBuilder.done();\n        final SDataInstanceBuilder dataInstanceBuilder = SDataInstanceBuilder.createNewInstance(dataDefinition)\n                .setContainerId(containerId).setContainerType(containerType);\n        // create data instance\n        evaluateDefaultValueOf(dataDefinition, dataInstanceBuilder);\n        return dataInstanceBuilder.done();\n    }\n\n    @Test\n    public void testCreateAndRetrieveDateDataInstance() throws Exception {\n        final long time = System.currentTimeMillis();\n        verifyCreateAndRetrieveDataInstance(\"createDate\", Date.class.getName(), \"creates new Date\",\n                \"new java.util.Date(\" + time + \")\", 9L, \"process\", false,\n                new Date(time));\n    }\n\n    @Test\n    public void createAndRetrieveNullDateDataInstanceShouldBeSupported() throws Exception {\n        verifyCreateAndRetrieveDataInstance(\"createNullDate\", Date.class.getName(), \"creates a null Date\", \"null\", 9L,\n                \"process\", false, null);\n    }\n\n    //    private SDataInstance buildDateDataInstance(final String instanceName, final String description, final String content, final long containerId,\n    //            final String containerType, final Boolean isTransient) throws SBonitaException {\n    //        // create definition\n    //        final SDataDefinitionBuilder dataDefinitionBuilder = BuilderFactory.get(SDataDefinitionBuilderFactory.class).createNewInstance(instanceName, Date.class.getName());\n    //\n    //        initializeBuilder(dataDefinitionBuilder, description, content, Date.class.getName(), isTransient);\n    //        final SDataDefinition dataDefinition = dataDefinitionBuilder.done();\n    //        final SDataInstanceBuilder dataInstanceBuilder = BuilderFactory.get(SDataInstanceBuilderFactory.class).createNewInstance(dataDefinition)\n    //                .setContainerId(containerId).setContainerType(containerType);\n    //        // create data instance\n    //        evaluateDefaultValueOf(dataDefinition, dataInstanceBuilder);\n    //        return dataInstanceBuilder.done();\n    //    }\n\n    private SDataInstance buildXMLDataInstance(final String instanceName, final String description,\n            final String namespace, final String xmlElement,\n            final String content, final long containerId, final String containerType) throws SBonitaException {\n        // create definition\n        final SDataDefinition dataDefinition = buildDataDefinition(instanceName, description, namespace, xmlElement,\n                content, String.class.getName());\n\n        // create data instance\n        final SDataInstanceBuilder dataInstanceBuilder = SDataInstanceBuilder.createNewInstance(dataDefinition)\n                .setContainerId(containerId).setContainerType(containerType);\n        evaluateDefaultValueOf(dataDefinition, dataInstanceBuilder);\n        return dataInstanceBuilder.done();\n    }\n\n    private SDataDefinition buildDataDefinition(final String instanceName, final String description,\n            final String namespace, final String xmlElement,\n            final String content, final String defaultValueReturnType) throws SInvalidExpressionException {\n        final SXMLDataDefinitionBuilder dataDefinitionBuilder = BuilderFactory\n                .get(SXMLDataDefinitionBuilderFactory.class).createNewXMLData(instanceName)\n                .setNamespace(namespace).setElement(xmlElement);\n        dataDefinitionBuilder.setDescription(description);\n        dataDefinitionBuilder.setTransient(false);\n        SExpression expression = null;\n        if (content != null) {\n            // create expression\n            final SExpressionBuilder expreBuilder = BuilderFactory.get(SExpressionBuilderFactory.class)\n                    .createNewInstance();\n            // this discrimination'll be changed.\n            expreBuilder.setContent(content).setReturnType(defaultValueReturnType)\n                    .setExpressionType(SExpression.TYPE_READ_ONLY_SCRIPT)\n                    .setInterpreter(SExpression.GROOVY);\n            expression = expreBuilder.done();\n        }\n        dataDefinitionBuilder.setDefaultValue(expression);\n        return dataDefinitionBuilder.done();\n    }\n\n    private void initializeBuilder(final SDataDefinitionBuilder dataDefinitionBuilder, final String description,\n            final String content,\n            final String defaultValueReturnType, final boolean isTransient) throws SInvalidExpressionException {\n        SExpression expression = null;\n        if (content != null) {\n            // create expression\n            final SExpressionBuilder expreBuilder = BuilderFactory.get(SExpressionBuilderFactory.class)\n                    .createNewInstance();\n            // this discrimination'll be changed.\n            expreBuilder.setContent(content).setReturnType(defaultValueReturnType)\n                    .setExpressionType(SExpression.TYPE_READ_ONLY_SCRIPT)\n                    .setInterpreter(SExpression.GROOVY);\n            expression = expreBuilder.done();\n        }\n\n        dataDefinitionBuilder.setDescription(description);\n        dataDefinitionBuilder.setTransient(isTransient);\n        dataDefinitionBuilder.setDefaultValue(expression);\n    }\n\n    private void initializeBuilderConstant(final SDataDefinitionBuilder dataDefinitionBuilder, final String description,\n            final String content,\n            final String defaultValueReturnType, final boolean isTransient) throws SInvalidExpressionException {\n        SExpression expression = null;\n        if (content != null) {\n            // create expression\n            final SExpressionBuilder expreBuilder = BuilderFactory.get(SExpressionBuilderFactory.class)\n                    .createNewInstance();\n            // this discrimination'll be changed.\n            expreBuilder.setContent(content).setReturnType(defaultValueReturnType)\n                    .setExpressionType(SExpression.TYPE_CONSTANT);\n            expression = expreBuilder.done();\n        }\n\n        dataDefinitionBuilder.setDescription(description);\n        dataDefinitionBuilder.setTransient(isTransient);\n        dataDefinitionBuilder.setDefaultValue(expression);\n    }\n\n    private void checkDataInstance(final SDataInstance dataInstance, final String name, final String description,\n            final boolean isTransient,\n            final String className, final Serializable value, final long containerId, final String containerType) {\n        assertEquals(name, dataInstance.getName());\n        assertEquals(description, dataInstance.getDescription());\n        assertEquals(isTransient, dataInstance.isTransientData());\n        assertEquals(className, dataInstance.getClassName());\n        assertEquals(value, dataInstance.getValue());\n        assertEquals(containerId, dataInstance.getContainerId());\n        assertEquals(containerType, dataInstance.getContainerType());\n    }\n\n    private void checkXMLDataInstance(final SXMLDataInstance dataInstance, final String name, final String description,\n            final boolean isTransient,\n            final String className, final Serializable value, final long containerId, final String containerType,\n            final String namespace,\n            final String element) {\n        assertEquals(namespace, dataInstance.getNamespace());\n        assertEquals(element, dataInstance.getElement());\n        checkDataInstance(dataInstance, name, description, isTransient, className, value, containerId, containerType);\n    }\n\n    private EntityUpdateDescriptor getUpdateDescriptor(final String description, final Serializable newValue) {\n        // update data instance\n        final EntityUpdateDescriptor updateDescriptor = new EntityUpdateDescriptor();\n        updateDescriptor.addField(SDataInstance.VALUE, newValue);\n        updateDescriptor.addField(SDataInstance.DESCRIPTION, description);\n\n        return updateDescriptor;\n    }\n\n    private void insertDataInstance(final SDataInstance dataInstance) throws SBonitaException {\n        getTransactionService().begin();\n        // create data instance\n        dataInstanceService.createDataInstance(dataInstance);\n        getTransactionService().complete();\n    }\n\n    private SDataInstance getDataInstance(final long dataInstanceId) throws SBonitaException {\n        getTransactionService().begin();\n\n        final SDataInstance dataInstanceRes = dataInstanceService.getDataInstance(dataInstanceId);\n        getTransactionService().complete();\n\n        return dataInstanceRes;\n    }\n\n    private SDataInstance getDataInstanceByNameAndContainer(final String dataName, final long containerId,\n            final String containerType) throws SBonitaException {\n        getTransactionService().begin();\n        // get the data instance by several conditions\n        final SDataInstance dataInstanceRes = dataInstanceService.getLocalDataInstance(dataName, containerId,\n                containerType);\n        getTransactionService().complete();\n\n        return dataInstanceRes;\n    }\n\n    private String getLongText() {\n        final StringBuilder stb = new StringBuilder();\n        for (int i = 0; i < 255; i++) {\n            stb.append(i);\n        }\n        return stb.toString();\n    }\n\n    private void deleteDataInstance(final SDataInstance dataInstance) throws SBonitaException {\n        getTransactionService().begin();\n        dataInstanceService.deleteDataInstance(dataInstance);\n        getTransactionService().complete();\n    }\n\n    private void updateDataInstance(final String dataName, final Long containerId, final String containerType,\n            final String newDescription,\n            final Serializable value) throws SBonitaException {\n        getTransactionService().begin();\n        // retrieve the data instance\n        final SDataInstance dataInstanceRes = dataInstanceService.getLocalDataInstance(dataName, containerId,\n                containerType);\n        // update the data instance and this step must be with an activity data Instance in same transaction.\n        final EntityUpdateDescriptor updateDescriptor = getUpdateDescriptor(newDescription, value);\n        dataInstanceService.updateDataInstance(dataInstanceRes, updateDescriptor);\n        getTransactionService().complete();\n    }\n\n    private void verifyCreateAndRetrieveDataInstance(final String name, final String classType,\n            final String description, final String content,\n            final Long containerId, final String containerType, final Boolean isTransient,\n            final Serializable checkValue) throws Exception {\n        final SDataInstance dataInstance = buildDataInstance(name, classType, description, content, containerId,\n                containerType, isTransient);\n        insertDataInstance(dataInstance);\n\n        final SDataInstance dataInstanceRes = getDataInstanceByNameAndContainer(dataInstance.getName(),\n                dataInstance.getContainerId(),\n                dataInstance.getContainerType());\n        checkDataInstance(dataInstanceRes, name, description, isTransient, classType, checkValue, containerId,\n                containerType);\n\n        deleteDataInstance(dataInstanceRes);\n    }\n\n    private void verifyCreateAndRetrieveDataInstanceConstant(final String name, final String classType,\n            final String description, final String content,\n            final Long containerId, final String containerType, final Boolean isTransient,\n            final Serializable checkValue) throws Exception {\n        final SDataInstance dataInstance = buildDataInstanceConstant(name, classType, description, content, containerId,\n                containerType, isTransient);\n        insertDataInstance(dataInstance);\n\n        final SDataInstance dataInstanceRes = getDataInstanceByNameAndContainer(dataInstance.getName(),\n                dataInstance.getContainerId(),\n                dataInstance.getContainerType());\n        checkDataInstance(dataInstanceRes, name, description, isTransient, classType, checkValue, containerId,\n                containerType);\n\n        deleteDataInstance(dataInstanceRes);\n    }\n\n    private void verifyCreateAndRetrieveDoubleDataInstance(final boolean isTransient) throws Exception {\n        verifyCreateAndRetrieveDataInstance(\"createDouble1\", Double.class.getName(), \"testCreateDescriptionForDouble1\",\n                \"new Double(5.0)\", 6L, \"process\",\n                isTransient, 5.0);\n    }\n\n    private void verifyCreateAndRetrieveFloatDataInstance(final boolean isTransient) throws Exception {\n        verifyCreateAndRetrieveDataInstance(\"createFloat1\", Float.class.getName(), \"testCreateDescriptionForFloat1\",\n                \"new Float(5.0)\", 6L, \"process\",\n                isTransient, 5.0F);\n    }\n\n    @Test\n    public void getDataInstancesWithTransientAndNonTransientData() throws Exception {\n        final long containerId = 4444L;\n        final String containerType = \"ActivityScope\";\n        final String instance1Name = \"non-transient\";\n        final SDataInstance data1Instance = buildDataInstance(instance1Name, Integer.class.getName(),\n                \"some non transient data\", null, containerId,\n                containerType, false);\n        insertDataInstance(data1Instance);\n        final String instance2Name = \"transient\";\n        final SDataInstance data2Instance = buildDataInstance(instance2Name, Integer.class.getName(),\n                \"some transient data\", null, containerId, containerType,\n                true);\n        insertDataInstance(data2Instance);\n        getTransactionService().begin();\n        getTransactionService().complete();\n\n        final List<String> dataNames = new ArrayList<>(2);\n        dataNames.add(instance1Name);\n        dataNames.add(instance2Name);\n        getTransactionService().begin();\n        final List<SDataInstance> dataInstances = dataInstanceService.getDataInstances(dataNames, containerId,\n                containerType, parentContainerResolver);\n        getTransactionService().complete();\n        assertEquals(2, dataInstances.size());\n        Assertions.assertThat(dataInstances).extracting(\"name\").containsOnly(instance1Name, instance2Name);\n    }\n\n    @Test\n    public void getSADataInstancesWithEmptyList() throws Exception {\n        getTransactionService().begin();\n        final List<SADataInstance> dataInstances = dataInstanceService.getSADataInstances(13544L, \"dummyContainerType\",\n                parentContainerResolver,\n                Collections.emptyList(), 1111111L);\n        getTransactionService().complete();\n        assertEquals(0, dataInstances.size());\n    }\n\n    @Test\n    public void createDataWithComplexName() throws Exception {\n        final SDataInstance dataInstance = buildDataInstance(\"FirstLetterUpperCase\", Integer.class.getName(), null,\n                \"987456321\", 4444L, \"ActivityScope\", false);\n        insertDataInstance(dataInstance);\n        assertTrue(0 != dataInstance.getId());\n\n        final SDataInstance dataInstance2 = buildDataInstance(\"var with spaces\", Double.class.getName(), null, \"221d\",\n                4654L, \"DummyScopeType\", false);\n        insertDataInstance(dataInstance2);\n        assertTrue(0 != dataInstance2.getId());\n    }\n\n    @Test\n    public void testCreateAndRetrieveDoubleInstance() throws Exception {\n        verifyCreateAndRetrieveDoubleDataInstance(false);\n    }\n\n    @Test\n    public void testCreateAndRetrieveDoubleInstanceTransient() throws Exception {\n        verifyCreateAndRetrieveDoubleDataInstance(true);\n    }\n\n    @Test\n    public void testCreateAndRetrieveFloatInstance() throws Exception {\n        verifyCreateAndRetrieveFloatDataInstance(false);\n    }\n\n    @Test\n    public void testCreateAndRetrieveFloatInstanceTransient() throws Exception {\n        verifyCreateAndRetrieveFloatDataInstance(true);\n    }\n\n    private void verifyCreateAndRetrieveBooleanDataInstance(final boolean isTransient) throws Exception {\n        verifyCreateAndRetrieveDataInstance(\"createBoolean1\", Boolean.class.getName(),\n                \"testCreateDescriptionForBoolean1\", \"true\", 6L, \"process\", isTransient,\n                true);\n    }\n\n    @Test\n    public void testCreateAndRetrieveBooleanInstance() throws Exception {\n        verifyCreateAndRetrieveBooleanDataInstance(false);\n    }\n\n    @Test\n    public void testCreateAndRetrieveBooleanInstanceTransient() throws Exception {\n        verifyCreateAndRetrieveBooleanDataInstance(true);\n    }\n\n    @Test\n    public void testCreateAndRetrieveIntegerInstance() throws Exception {\n        verifyCreateAndRetrieveDataInstance(\"createInteger\", Integer.class.getName(), \"testCreateDescription\", \"1+2\",\n                7L, \"process\", false, 3);\n    }\n\n    @Test\n    public void testCreateAndRetrieveIntegerInstanceTransient() throws Exception {\n        verifyCreateAndRetrieveDataInstance(\"createInteger\", Integer.class.getName(), \"testCreateDescription\", \"1+2\",\n                7L, \"process\", true, 3);\n    }\n\n    @Test\n    public void testCreateAndRetrieveLongInstance() throws Exception {\n        verifyCreateAndRetrieveDataInstance(\"createLong\", Long.class.getName(), \"testCreateDescription\", \"new Long(2)\",\n                8L, \"process\", false, 2L);\n    }\n\n    @Test\n    public void testCreateAndRetrieveLongInstanceTransient() throws Exception {\n        verifyCreateAndRetrieveDataInstance(\"createLong\", Long.class.getName(), \"testCreateDescription\", \"new Long(2)\",\n                8L, \"process\", true, 2L);\n    }\n\n    @Test\n    public void testCreateAndRetrieveShortTextInstance() throws Exception {\n        verifyCreateAndRetrieveDataInstance(\"createString\", String.class.getName(), \"testCreateDescription\",\n                \"'test123'\", 9L, \"process\", false, \"test123\");\n    }\n\n    @Test\n    public void testCreateAndRetrieveShortTextInstanceTransient() throws Exception {\n        verifyCreateAndRetrieveDataInstance(\"createString\", String.class.getName(), \"testCreateDescription\",\n                \"'test123'\", 9L, \"process\", true, \"test123\");\n    }\n\n    private void verifyCreateAndRetrieveLongTextData(final boolean isTransient) throws Exception {\n\n        final String longText = getLongText();\n\n        final SDataInstance longTextDataInstance = buildLongTextDataInstance(\"longTextData\", \"A very long text\",\n                getStringForExpression(longText), 11L,\n                \"process\", isTransient);\n        insertDataInstance(longTextDataInstance);\n\n        final SDataInstance dataInstance = getDataInstanceByNameAndContainer(longTextDataInstance.getName(),\n                longTextDataInstance.getContainerId(),\n                longTextDataInstance.getContainerType());\n        checkDataInstance(dataInstance, \"longTextData\", \"A very long text\", isTransient, String.class.getName(),\n                longText, 11L, \"process\");\n\n        deleteDataInstance(dataInstance);\n    }\n\n    @Test\n    public void testCreateAndRetrieveLongTextData() throws Exception {\n        verifyCreateAndRetrieveLongTextData(false);\n    }\n\n    @Test\n    public void testCreateAndRetrieveLongTextDataTransient() throws Exception {\n        verifyCreateAndRetrieveLongTextData(true);\n    }\n\n    @Test\n    public void testCreateAndRetrieveBlobDataInstanceWithoutDefaultValue() throws Exception {\n        verifyCreateAndRetrieveDataInstance(\"blobTextData\", LightEmployee.class.getName(),\n                \"A custom java object as blob\",\n                \"return new org.bonitasoft.engine.data.instance.LightEmployee(\\\"manu\\\", \\\"duch\\\", 35)\", 15L, \"process\",\n                false, new LightEmployee(\"manu\",\n                        \"duch\", 35));\n    }\n\n    @Test\n    public void testCreateAndRetrieveBlobDataInstanceWithoutDefaultValueTransient() throws Exception {\n        verifyCreateAndRetrieveDataInstance(\"blobTextData\", LightEmployee.class.getName(),\n                \"A custom java object as blob\",\n                \"return new org.bonitasoft.engine.data.instance.LightEmployee(\\\"manu\\\", \\\"duch\\\", 53)\", 15L, \"process\",\n                true, new LightEmployee(\"manu\", \"duch\",\n                        53));\n    }\n\n    @Test\n    public void testCreateBooleanInstanceWithoutDefaultValue() throws Exception {\n        verifyCreateAndRetrieveDataInstanceConstant(\"createBoolean2\", Boolean.class.getName(),\n                \"testCreateDescriptionForBoolean2\", \"true\", 11L, \"process\",\n                false, true);\n    }\n\n    @Test\n    public void testCreateBooleanInstanceWithoutDefaultValueTransient() throws Exception {\n        verifyCreateAndRetrieveDataInstanceConstant(\"createBoolean2\", Boolean.class.getName(),\n                \"testCreateDescriptionForBoolean2\", \"false\", 11L, \"process\",\n                true, false);\n    }\n\n    @Test\n    public void testCreateDoubleInstanceWithoutDefaultValue() throws Exception {\n        verifyCreateAndRetrieveDataInstanceConstant(\"createDouble2\", Double.class.getName(),\n                \"testCreateDescriptionForDouble2\", \"37.0\", 12L, \"task\", false,\n                37.0);\n    }\n\n    @Test\n    public void testCreateDoubleInstanceWithoutDefaultValueTransient() throws Exception {\n        verifyCreateAndRetrieveDataInstanceConstant(\"createDouble2\", Double.class.getName(),\n                \"testCreateDescriptionForDouble2\", \"73.0\", 12L, \"task\", true,\n                73.0);\n    }\n\n    @Test\n    public void testCreateFloatInstanceWithoutDefaultValue() throws Exception {\n        verifyCreateAndRetrieveDataInstanceConstant(\"createFloat2\", Float.class.getName(),\n                \"testCreateDescriptionForFloat2\", \"34.F\", 12L, \"task\", false, 34.F);\n    }\n\n    @Test\n    public void testCreateFloatInstanceWithoutDefaultValueTransient() throws Exception {\n        verifyCreateAndRetrieveDataInstanceConstant(\"createFloat2\", Float.class.getName(),\n                \"testCreateDescriptionForFloat2\", \"43.F\", 12L, \"task\", true, 43.F);\n    }\n\n    @Test\n    public void testCreateIntegerInstanceWithoutDefaultValue() throws Exception {\n        verifyCreateAndRetrieveDataInstanceConstant(\"createInteger2\", Integer.class.getName(),\n                \"testCreateDescriptionForInteger2\", \"32\", 13L, \"task1\", false,\n                32);\n    }\n\n    @Test\n    public void testCreateIntegerInstanceWithoutDefaultValueTransient() throws Exception {\n        verifyCreateAndRetrieveDataInstanceConstant(\"createInteger2\", Integer.class.getName(),\n                \"testCreateDescriptionForInteger2\", \"23\", 13L, \"task1\", true,\n                23);\n    }\n\n    @Test\n    public void testCreateLongInstanceWithoutDefaultValue() throws Exception {\n        verifyCreateAndRetrieveDataInstanceConstant(\"createLong2\", Long.class.getName(),\n                \"testCreateDescriptionForLong2\", \"47\", 14L, \"task2\", false, 47L);\n    }\n\n    @Test\n    public void testCreateLongInstanceWithoutDefaultValueTransient() throws Exception {\n        verifyCreateAndRetrieveDataInstanceConstant(\"createLong2\", Long.class.getName(),\n                \"testCreateDescriptionForLong2\", \"74\", 14L, \"task2\", true, 74L);\n    }\n\n    @Test\n    public void testCreateShortTextInstanceWithoutDefaultValue() throws Exception {\n        verifyCreateAndRetrieveDataInstanceConstant(\"createString2\", String.class.getName(),\n                \"testCreateDescriptionForString2\", \"strTest\", 15L, \"task3\", false,\n                \"strTest\");\n    }\n\n    @Test\n    public void testCreateShortTextInstanceWithoutDefaultValueTransient() throws Exception {\n        verifyCreateAndRetrieveDataInstanceConstant(\"createString2\", String.class.getName(),\n                \"testCreateDescriptionForString2\", \"strTest2\", 15L, \"task3\", true,\n                \"strTest2\");\n    }\n\n    private void verifyDeleteDataInstance(final String name, final String classType, final String description,\n            final String content, final Long containerId,\n            final String containerType, final Boolean isTransient, final Serializable checkValue,\n            final String assertContent) throws Exception {\n        final SDataInstance dataInstance = buildDataInstance(name, classType, description, content, containerId,\n                containerType, isTransient);\n        insertDataInstance(dataInstance);\n\n        final SDataInstance dataInstanceRes = getDataInstance(dataInstance.getId());\n        assertNotNull(dataInstanceRes);\n        checkDataInstance(dataInstanceRes, name, description, isTransient, classType, checkValue, containerId,\n                containerType);\n\n        deleteDataInstance(dataInstance);\n\n        final SDataInstance dataDeletedRes = getDataInstance(dataInstance.getId());\n        assertNull(assertContent, dataDeletedRes);\n    }\n\n    @Test(expected = SDataInstanceNotFoundException.class)\n    public void testDeleteBooleanInstance() throws Exception {\n        verifyDeleteDataInstance(\"deleteBoolean1\", Boolean.class.getName(), \"testDeleteDescriptionForBoolean\", \"true\",\n                1L, \"task\", false, true,\n                \"the Boolean instance was not deleted\");\n    }\n\n    @Test(expected = SDataInstanceNotFoundException.class)\n    public void testDeleteTransientBooleanInstance() throws Exception {\n        verifyDeleteDataInstance(\"deleteBoolean1\", Boolean.class.getName(), \"testDeleteDescriptionForBoolean\", \"true\",\n                1L, \"task\", true, true,\n                \"the Boolean instance was not deleted\");\n    }\n\n    @Test(expected = SDataInstanceNotFoundException.class)\n    public void testDeleteDoubleInstance() throws Exception {\n        verifyDeleteDataInstance(\"deleteDouble1\", Double.class.getName(), \"testDeleteDescriptionForDouble\", \"1.23D\", 1L,\n                \"task\", false, 1.23D,\n                \"the Double instance was not deleted\");\n    }\n\n    @Test(expected = SDataInstanceNotFoundException.class)\n    public void testDeleteFloatInstance() throws Exception {\n        verifyDeleteDataInstance(\"deleteFloat1\", Float.class.getName(), \"testDeleteDescriptionForFloat\", \"1.23F\", 1L,\n                \"task\", false, 1.23F,\n                \"the Float instance was not deleted\");\n    }\n\n    @Test(expected = SDataInstanceNotFoundException.class)\n    public void testTransientDeleteDoubleInstance() throws Exception {\n        verifyDeleteDataInstance(\"deleteDouble1\", Double.class.getName(), \"testDeleteDescriptionForDouble\", \"1.23D\", 1L,\n                \"task\", true, 1.23D,\n                \"the Double instance was not deleted\");\n    }\n\n    @Test(expected = SDataInstanceNotFoundException.class)\n    public void testTransientDeleteFloatInstance() throws Exception {\n        verifyDeleteDataInstance(\"deleteFloat1\", Float.class.getName(), \"testDeleteDescriptionForFloat\", \"1.23F\", 1L,\n                \"task\", true, 1.23F,\n                \"the Float instance was not deleted\");\n    }\n\n    @Test(expected = SDataInstanceNotFoundException.class)\n    public void testDeleteIntegerInstance() throws Exception {\n        verifyDeleteDataInstance(\"deleteInteger1\", Integer.class.getName(), \"testDeleteDescriptionForInteger\", \"8+4\",\n                1L, \"task\", false, 12,\n                \"the Integer instance was not deleted\");\n    }\n\n    @Test(expected = SDataInstanceNotFoundException.class)\n    public void testTransientDeleteIntegerInstance() throws Exception {\n        verifyDeleteDataInstance(\"deleteInteger1\", Integer.class.getName(), \"testDeleteDescriptionForInteger\", \"8+4\",\n                1L, \"task\", true, 12,\n                \"the Integer instance was not deleted\");\n    }\n\n    @Test(expected = SDataInstanceNotFoundException.class)\n    public void testDeleteLongInstance() throws Exception {\n        verifyDeleteDataInstance(\"deleteLong1\", Long.class.getName(), \"testDeleteDescriptionForLong\", \"12L\", 1L, \"task\",\n                false, 12L,\n                \"the Long instance was not deleted\");\n    }\n\n    @Test(expected = SDataInstanceNotFoundException.class)\n    public void testTransientDeleteLongInstance() throws Exception {\n        verifyDeleteDataInstance(\"deleteLong1\", Long.class.getName(), \"testDeleteDescriptionForLong\", \"12L\", 1L, \"task\",\n                true, 12L,\n                \"the Long instance was not deleted\");\n    }\n\n    @Test(expected = SDataInstanceNotFoundException.class)\n    public void testDeleteShortTextInstance() throws Exception {\n        verifyDeleteDataInstance(\"deleteString1\", String.class.getName(), \"testDeleteDescriptionForString\", \"'test123'\",\n                1L, \"task\", false, \"test123\",\n                \"the Short Text instance was not deleted\");\n    }\n\n    @Test(expected = SDataInstanceNotFoundException.class)\n    public void testTransientDeleteShortTextInstance() throws Exception {\n        verifyDeleteDataInstance(\"deleteString1\", String.class.getName(), \"testDeleteDescriptionForString\", \"'test123'\",\n                1L, \"task\", true, \"test123\",\n                \"the Short Text instance was not deleted\");\n    }\n\n    @Test(expected = SDataInstanceNotFoundException.class)\n    public void testDeleteBlobInstance() throws Exception {\n        final LightEmployee employee = new LightEmployee(\"firstName\", \"lastName\", 30);\n        verifyDeleteDataInstance(\"blobData\", LightEmployee.class.getName(), \"A custom java object\",\n                \"new org.bonitasoft.engine.data.instance.LightEmployee(\\\"firstName\\\", \\\"lastName\\\", 30)\", 11L,\n                \"processTask\", false, employee,\n                \"the blob data instance was not deleted\");\n    }\n\n    @Test(expected = SDataInstanceNotFoundException.class)\n    public void testTransientDeleteBlobInstance() throws Exception {\n        final LightEmployee employee = new LightEmployee(\"firstName\", \"lastName\", 30);\n        verifyDeleteDataInstance(\"blobData\", LightEmployee.class.getName(), \"A custom java object\",\n                \"new org.bonitasoft.engine.data.instance.LightEmployee(\\\"firstName\\\", \\\"lastName\\\", 30)\", 11L,\n                \"processTask\", true, employee,\n                \"the blob data instance was not deleted\");\n    }\n\n    private void verifyDeleteLongTextData(final Boolean isTransient) throws Exception {\n        final String longText = getLongText();\n\n        final SDataInstance longTextDataInstance = buildLongTextDataInstance(\"longTextData\", \"A very long text\",\n                getStringForExpression(longText), 0, null,\n                isTransient);\n        insertDataInstance(longTextDataInstance);\n\n        final SDataInstance dataInstance = getDataInstance(longTextDataInstance.getId());\n        assertNotNull(dataInstance);\n\n        deleteDataInstance(dataInstance);\n\n        final SDataInstance dataInstanceRes = getDataInstance(longTextDataInstance.getId());\n        assertNull(\"the Long Text instance was not deleted\", dataInstanceRes);\n    }\n\n    @Test(expected = SDataInstanceNotFoundException.class)\n    public void testDeleteLongTextData() throws Exception {\n        verifyDeleteLongTextData(false);\n    }\n\n    @Test(expected = SDataInstanceNotFoundException.class)\n    public void testTransientDeleteLongTextData() throws Exception {\n        verifyDeleteLongTextData(true);\n    }\n\n    private long verifyUpdateDataInstance(final String name, final String classType, final String description,\n            final String content, final Long containerId,\n            final String containerType, final Boolean isTransient, final String updateDescription,\n            final Serializable updateValue) throws Exception {\n        final SDataInstance dataInstance = buildDataInstance(name, classType, description, content, containerId,\n                containerType, isTransient);\n        insertDataInstance(dataInstance);\n        updateDataInstance(dataInstance.getName(), dataInstance.getContainerId(), dataInstance.getContainerType(),\n                updateDescription, updateValue);\n\n        final long dataInstanceId = dataInstance.getId();\n        final SDataInstance dataInstanceRes = getDataInstance(dataInstanceId);\n        checkDataInstance(dataInstanceRes, name, updateDescription, isTransient, classType, updateValue, containerId,\n                containerType);\n        deleteDataInstance(dataInstanceRes);\n        return dataInstanceId;\n\n    }\n\n    @Test\n    public void testUpdateBooleanInstance() throws Exception {\n        verifyUpdateDataInstance(\"updateBoolean\", Boolean.class.getName(), \"testUpdateDescription\", \"true\", 11L,\n                \"miniTask\", false,\n                \"testUpdateDescription123456\", false);\n    }\n\n    @Test\n    public void testTransientUpdateBooleanInstance() throws Exception {\n        verifyUpdateDataInstance(\"updateBoolean\", Boolean.class.getName(), \"testUpdateDescription\", \"true\", 11L,\n                \"miniTask\", true,\n                \"testUpdateDescription123456\", false);\n    }\n\n    @Test\n    public void testUpdateDoubleInstance() throws Exception {\n        verifyUpdateDataInstance(\"updateDouble\", Double.class.getName(), \"testUpdateDescription\", \"new Double(5.0)\",\n                11L, \"miniTask\", false,\n                \"testUpdateDescription4\", 7.0);\n    }\n\n    @Test\n    public void testTransientUpdateDoubleInstance() throws Exception {\n        verifyUpdateDataInstance(\"updateDouble\", Double.class.getName(), \"testUpdateDescription\", \"new Double(5.0)\",\n                11L, \"miniTask\", true,\n                \"testUpdateDescription4\", 7.0);\n    }\n\n    @Test\n    public void testUpdateFloatInstance() throws Exception {\n        verifyUpdateDataInstance(\"updateFloat\", Float.class.getName(), \"testUpdateDescription\", \"new Float(5.0)\", 11L,\n                \"miniTask\", false,\n                \"testUpdateDescription4\", 7.0F);\n    }\n\n    @Test\n    public void testTransientUpdateFloatInstance() throws Exception {\n        verifyUpdateDataInstance(\"updateFloat\", Float.class.getName(), \"testUpdateDescription\", \"new Float(5.0)\", 11L,\n                \"miniTask\", true,\n                \"testUpdateDescription4\", 7.0F);\n    }\n\n    @Test\n    public void testUpdateIntegerInstance() throws Exception {\n        verifyUpdateDataInstance(\"updateInteger\", Integer.class.getName(), \"testUpdateDescription\", \"new Integer(5)\",\n                11L, \"miniTask\", false,\n                \"testUpdateDescription2\", 8);\n    }\n\n    @Test\n    public void testTransientUpdateIntegerInstance() throws Exception {\n        verifyUpdateDataInstance(\"updateInteger\", Integer.class.getName(), \"testUpdateDescription\", \"new Integer(5)\",\n                11L, \"miniTask\", true,\n                \"testUpdateDescription2\", 8);\n    }\n\n    @Test\n    public void testUpdateLongInstance() throws Exception {\n        verifyUpdateDataInstance(\"updateLong\", Long.class.getName(), \"testUpdateDescription\", \"new Long(5)\", 11L,\n                \"miniTask\", false, \"testUpdateDescription2\",\n                6L);\n    }\n\n    @Test\n    public void testTransientUpdateLongInstance() throws Exception {\n        verifyUpdateDataInstance(\"updateLong\", Long.class.getName(), \"testUpdateDescription\", \"new Long(5)\", 11L,\n                \"miniTask\", true, \"testUpdateDescription2\",\n                6L);\n    }\n\n    @Test\n    public void testUpdateShortTextInstance() throws Exception {\n        verifyUpdateDataInstance(\"updateShortText\", String.class.getName(), \"testUpdateDescription\", \"'123qwe'\", 11L,\n                \"miniTask\", false,\n                \"testUpdateDescription2\", \"123qwe2\");\n    }\n\n    @Test\n    public void testTransientUpdateShortTextInstance() throws Exception {\n        verifyUpdateDataInstance(\"updateShortText\", String.class.getName(), \"testUpdateDescription\", \"'123qwe'\", 11L,\n                \"miniTask\", true,\n                \"testUpdateDescription2\", \"123qwe2\");\n    }\n\n    private void verifyUpdateLongTextDataInstance(final String name, final String classType, final String description,\n            final String content,\n            final Long containerId, final String containerType, final Boolean isTransient,\n            final String updateDescription, final Serializable updateValue)\n            throws Exception {\n        final SDataInstance dataInstance = buildLongTextDataInstance(name, description, content, containerId,\n                containerType, isTransient);\n        insertDataInstance(dataInstance);\n\n        updateDataInstance(dataInstance.getName(), dataInstance.getContainerId(), dataInstance.getContainerType(),\n                updateDescription, updateValue);\n\n        final SDataInstance dataInstanceRes = getDataInstance(dataInstance.getId());\n        checkDataInstance(dataInstanceRes, name, updateDescription, isTransient, classType, updateValue, containerId,\n                containerType);\n        deleteDataInstance(dataInstanceRes);// dataInstance has no id here.\n    }\n\n    @Test\n    public void testUpdateLongTextData() throws Exception {\n        final String longText = getLongText();\n        verifyUpdateLongTextDataInstance(\"longTextData\", String.class.getName(), \"A very long text\",\n                getStringForExpression(longText), 11L, \"processTask\",\n                false, \"Updated description for long text\", longText + \"Updated\");\n    }\n\n    @Test\n    public void testTransientUpdateLongTextData() throws Exception {\n        final String longText = getLongText();\n        verifyUpdateLongTextDataInstance(\"longTextData\", String.class.getName(), \"A very long text\",\n                getStringForExpression(longText), 11L, \"processTask\",\n                true, \"Updated description for long text\", longText + \"Updated\");\n    }\n\n    @Test\n    public void testUpdateBlobDataInstance() throws Exception {\n        final LightEmployee employee = new LightEmployee(\"firstName\", \"lastName\", 30);\n        verifyUpdateDataInstance(\"blobTextData\", LightEmployee.class.getName(), \"A custom java object\", null, 11L,\n                \"processTask\", false,\n                \"A custom java updated\", employee);\n    }\n\n    @Test\n    public void testTransientUpdateBlobDataInstance() throws Exception {\n        final LightEmployee employee = new LightEmployee(\"firstName\", \"lastName\", 30);\n        verifyUpdateDataInstance(\"blobTextData\", LightEmployee.class.getName(), \"A custom java object\", null, 11L,\n                \"processTask\", true,\n                \"A custom java updated\", employee);\n    }\n\n    private void verifyCreateAndGetDataInstance(final String name, final String classType, final String description,\n            final String content,\n            final Long containerId, final String containerType, final Boolean isTransient,\n            final Serializable checkValue) throws Exception {\n        final SDataInstance dataInstance = buildDataInstance(name, classType, description, content, containerId,\n                containerType, isTransient);\n        insertDataInstance(dataInstance);\n\n        final SDataInstance dataInstanceRes = getDataInstanceByNameAndContainer(dataInstance.getName(),\n                dataInstance.getContainerId(),\n                dataInstance.getContainerType());\n        checkDataInstance(dataInstanceRes, name, description, isTransient, classType, checkValue, containerId,\n                containerType);\n\n        deleteDataInstance(dataInstanceRes);\n    }\n\n    @Test\n    public void testGetDataIntegerInstance() throws Exception {\n        verifyCreateAndGetDataInstance(\"getInteger\", Integer.class.getName(), \"testgetDescription\", \"new Integer(2)\",\n                20L, \"processInstance\", false, 2);\n    }\n\n    @Test\n    public void testGetTransientIntegerDataInstance() throws Exception {\n        verifyCreateAndGetDataInstance(\"getInteger\", Integer.class.getName(), \"testgetDescription\", \"new Integer(2)\",\n                20L, \"processInstance\", true, 2);\n    }\n\n    @Test\n    public void testGetDataBooleanInstance() throws Exception {\n        verifyCreateAndGetDataInstance(\"getBoolean\", Boolean.class.getName(), \"testgetDescription\", \"true\", 20L,\n                \"processInstance\", false, true);\n    }\n\n    @Test\n    public void testGetTransientBooleanDataInstance() throws Exception {\n        verifyCreateAndGetDataInstance(\"getBoolean\", Boolean.class.getName(), \"testgetDescription\", \"true\", 20L,\n                \"processInstance\", true, true);\n    }\n\n    @Test\n    public void testGetDataShortTextInstance() throws Exception {\n        verifyCreateAndGetDataInstance(\"getShortText\", String.class.getName(), \"testgetDescription\", \"'1122aabb'\", 20L,\n                \"processInstance\", false, \"1122aabb\");\n    }\n\n    @Test\n    public void testGetTransientShortTextDataInstance() throws Exception {\n        verifyCreateAndGetDataInstance(\"getShortText\", String.class.getName(), \"testgetDescription\", \"'1122aabb'\", 20L,\n                \"processInstance\", true, \"1122aabb\");\n    }\n\n    @Test\n    public void testGetLongDataInstance() throws Exception {\n        verifyCreateAndGetDataInstance(\"getLong\", Long.class.getName(), \"testgetDescription\", \"new Long(2)\", 20L,\n                \"processInstance\", false, 2L);\n    }\n\n    @Test\n    public void testGetTransientLongDataInstance() throws Exception {\n        verifyCreateAndGetDataInstance(\"getLong\", Long.class.getName(), \"testgetDescription\", \"new Long(2)\", 20L,\n                \"processInstance\", true, 2L);\n    }\n\n    @Test\n    public void testGetDataDoubleInstance() throws Exception {\n        verifyCreateAndGetDataInstance(\"getDouble\", Double.class.getName(), \"testgetDescription\", \"new Double(2.0)\",\n                20L, \"processInstance\", false, 2.0);\n    }\n\n    @Test\n    public void testGetTransientDoubleDataInstance() throws Exception {\n        verifyCreateAndGetDataInstance(\"getDouble\", Double.class.getName(), \"testgetDescription\", \"new Double(2.0)\",\n                20L, \"processInstance\", true, 2.0);\n    }\n\n    @Test\n    public void testGetDataFloatInstance() throws Exception {\n        verifyCreateAndGetDataInstance(\"getFloat\", Float.class.getName(), \"testgetDescription\", \"new Float(2.0)\", 20L,\n                \"processInstance\", false, 2.0F);\n    }\n\n    @Test\n    public void testGetTransientFloatDataInstance() throws Exception {\n        verifyCreateAndGetDataInstance(\"getFloat\", Float.class.getName(), \"testgetDescription\", \"new Float(2.0)\", 20L,\n                \"processInstance\", true, 2.0F);\n    }\n\n    @Test\n    public void testGetLongTextData() throws Exception {\n        verifyCreateAndRetrieveLongTextData(false);\n    }\n\n    @Test\n    public void testGetLongTextDataTransient() throws Exception {\n        verifyCreateAndRetrieveLongTextData(true);\n    }\n\n    @Test\n    public void testGetBlobData() throws Exception {\n        final LightEmployee employee = new LightEmployee(\"firstName\", \"lastName\", 30);\n        verifyCreateAndGetDataInstance(\"blobTextData\", LightEmployee.class.getName(), \"A custom java object2\",\n                \"new org.bonitasoft.engine.data.instance.LightEmployee(\\\"firstName\\\", \\\"lastName\\\", 30)\", 3L,\n                \"processTaskDefinition\", false, employee);\n    }\n\n    @Test\n    public void testGetBlobDataTransient() throws Exception {\n        final LightEmployee employee = new LightEmployee(\"firstName\", \"lastName\", 30);\n        verifyCreateAndGetDataInstance(\"blobTextData\", LightEmployee.class.getName(), \"A custom java object3\",\n                \"new org.bonitasoft.engine.data.instance.LightEmployee(\\\"firstName\\\", \\\"lastName\\\", 30)\", 11L,\n                \"processTask\", true, employee);\n    }\n\n    @Test\n    public void testCreateAndRetrieveXMLDataById() throws Exception {\n        final String xmlContent = buildSimpleXML1();\n\n        final long containerId = 15;\n        final String containerType = \"ActivityInstance\";\n\n        final SDataInstance dataInstance = buildXMLDataInstance(\"xmlVar\", \"This is a xml variable\",\n                \"org.bonitasoft.engine.data.instance.model.SDataInstance\", \"xmlElement\",\n                getStringForExpression(xmlContent), containerId,\n                containerType);\n        insertDataInstance(dataInstance);\n\n        // get the data instance by several conditions\n        final SDataInstance dataInstanceRes = getDataInstance(dataInstance.getId());\n        assertTrue(dataInstanceRes instanceof SXMLDataInstance);\n        checkXMLDataInstance((SXMLDataInstance) dataInstanceRes, \"xmlVar\", \"This is a xml variable\", false,\n                String.class.getName(), xmlContent, containerId,\n                containerType, \"org.bonitasoft.engine.data.instance.model.SDataInstance\", \"xmlElement\");\n\n        deleteDataInstance(dataInstanceRes);\n    }\n\n    private String getStringForExpression(final String str) {\n        return \"'\" + str + \"'\";\n    }\n\n    private String buildSimpleXML1() {\n        return \"<root>\" +\n                \"<child>\" +\n                \"value\" +\n                \"</child>\" +\n                \"</root>\";\n    }\n\n    private String buildSimpleXML2() {\n        return \"<root>\" +\n                \"<child/>\" +\n                \"</root>\";\n    }\n\n    @Test\n    public void testUpdateXMLData() throws Exception {\n        final String xmlContent = buildSimpleXML1();\n        final long containerId = 15;\n        final String containerType = \"ActivityInstance\";\n        final SDataInstance dataInstance = buildXMLDataInstance(\"xmlVar\", \"This is a xml variable\",\n                \"org.bonitasoft.engine.data.instance.model.SDataInstance\", \"xmlElement\",\n                getStringForExpression(xmlContent), containerId,\n                containerType);\n        insertDataInstance(dataInstance);\n        // get the data instance by several conditions\n        SDataInstance dataInstanceRes = getDataInstance(dataInstance.getId());\n        assertTrue(dataInstanceRes instanceof SXMLDataInstance);\n        checkXMLDataInstance((SXMLDataInstance) dataInstanceRes, \"xmlVar\", \"This is a xml variable\", false,\n                String.class.getName(), xmlContent, containerId,\n                containerType, \"org.bonitasoft.engine.data.instance.model.SDataInstance\", \"xmlElement\");\n        final String updatedContent = buildSimpleXML2();\n        updateDataInstance(dataInstanceRes.getName(), dataInstanceRes.getContainerId(),\n                dataInstanceRes.getContainerType(),\n                \"This is a xml variable after update\", updatedContent);\n        dataInstanceRes = getDataInstance(dataInstance.getId());\n        checkXMLDataInstance((SXMLDataInstance) dataInstanceRes, \"xmlVar\", \"This is a xml variable after update\", false,\n                String.class.getName(),\n                updatedContent, containerId, containerType, \"org.bonitasoft.engine.data.instance.model.SDataInstance\",\n                \"xmlElement\");\n        deleteDataInstance(dataInstanceRes);\n    }\n\n    @Test(expected = SDataInstanceNotFoundException.class)\n    public void testDeleteXMLData() throws Exception {\n        final String xmlContent = buildSimpleXML1();\n\n        final long containerId = 15;\n        final String containerType = \"ActivityInstance\";\n\n        final SDataInstance dataInstance = buildXMLDataInstance(\"xmlVar\", \"This is a xml variable\",\n                \"org.bonitasoft.engine.data.instance.model.SDataInstance\", \"xmlElement\",\n                getStringForExpression(xmlContent), containerId,\n                containerType);\n        insertDataInstance(dataInstance);\n\n        // get the data instance by several conditions\n        final SDataInstance dataInstanceRes = getDataInstance(dataInstance.getId());\n        assertTrue(dataInstanceRes instanceof SXMLDataInstance);\n        checkXMLDataInstance((SXMLDataInstance) dataInstanceRes, \"xmlVar\", \"This is a xml variable\", false,\n                String.class.getName(), xmlContent, containerId,\n                containerType, \"org.bonitasoft.engine.data.instance.model.SDataInstance\", \"xmlElement\");\n\n        deleteDataInstance(dataInstanceRes);\n\n        getDataInstance(dataInstance.getId());\n    }\n\n    @Test\n    public void testRetrieveXMLDataByNameAndContainer() throws Exception {\n        final String xmlContent = buildSimpleXML1();\n\n        final long containerId = 16;\n        final String containerType = \"ActivityInstance\";\n\n        final SDataInstance dataInstance = buildXMLDataInstance(\"xmlVar\", \"This is a xml variable\",\n                \"org.bonitasoft.engine.data.instance.model.SDataInstance\", \"xmlElement\",\n                getStringForExpression(xmlContent), containerId,\n                containerType);\n        insertDataInstance(dataInstance);\n\n        // get the data instance by several conditions\n        final SDataInstance dataInstanceRes = getDataInstanceByNameAndContainer(\"xmlVar\", containerId, containerType);\n        assertTrue(dataInstanceRes instanceof SXMLDataInstance);\n        checkXMLDataInstance((SXMLDataInstance) dataInstanceRes, \"xmlVar\", \"This is a xml variable\", false,\n                String.class.getName(), xmlContent, containerId,\n                containerType, \"org.bonitasoft.engine.data.instance.model.SDataInstance\", \"xmlElement\");\n\n        deleteDataInstance(dataInstanceRes);\n    }\n\n    @Test\n    public void testGetSADataInstance() throws Exception {\n        final String classType = Integer.class.getName();\n        final SDataInstance dataInstance = buildDataInstance(\"updateInteger\", classType, \"testUpdateDescription\",\n                \"new Integer(5)\", 111L, \"miniTask\", false);\n        insertDataInstance(dataInstance);\n        Thread.sleep(10);\n\n        final long beforeUpdateDate = System.currentTimeMillis();\n        Thread.sleep(10);\n        updateDataInstance(dataInstance.getName(), dataInstance.getContainerId(), dataInstance.getContainerType(),\n                \"testUpdateDescription2\", 8);\n\n        final SDataInstance dataInstanceRes = getDataInstance(dataInstance.getId());\n        checkDataInstance(dataInstanceRes, \"updateInteger\", \"testUpdateDescription2\", false, classType, 8, 111L,\n                \"miniTask\");\n        deleteDataInstance(dataInstanceRes);\n        final long dataInstanceId = dataInstance.getId();\n\n        getTransactionService().begin();\n        try {\n            final SADataInstance beforeUpdate = dataInstanceService.getSADataInstance(dataInstanceId, beforeUpdateDate);\n            assertEquals(5, beforeUpdate.getValue());\n            assertTrue(beforeUpdate.getArchiveDate() <= beforeUpdateDate);\n\n            final SADataInstance afterUpdate = dataInstanceService.getSADataInstance(dataInstanceId,\n                    System.currentTimeMillis());\n            assertEquals(8, afterUpdate.getValue());\n            assertTrue(afterUpdate.getArchiveDate() <= System.currentTimeMillis()\n                    && afterUpdate.getArchiveDate() >= beforeUpdateDate);\n        } finally {\n            getTransactionService().complete();\n        }\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/data/instance/LightEmployee.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance;\n\nimport java.io.Serializable;\n\npublic class LightEmployee implements Serializable {\n\n    private static final long serialVersionUID = 3544356825663515645L;\n\n    private final String firstName;\n\n    private final String lastName;\n\n    private final int age;\n\n    public LightEmployee(final String firstName, final String lastName, final int age) {\n        super();\n        this.firstName = firstName;\n        this.lastName = lastName;\n        this.age = age;\n    }\n\n    public String getFirstName() {\n        return firstName;\n    }\n\n    public String getLastName() {\n        return lastName;\n    }\n\n    public int getAge() {\n        return age;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + age;\n        result = prime * result + ((firstName == null) ? 0 : firstName.hashCode());\n        result = prime * result + ((lastName == null) ? 0 : lastName.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        final LightEmployee other = (LightEmployee) obj;\n        if (age != other.age) {\n            return false;\n        }\n        if (firstName == null) {\n            if (other.firstName != null) {\n                return false;\n            }\n        } else if (!firstName.equals(other.firstName)) {\n            return false;\n        }\n        if (lastName == null) {\n            if (other.lastName != null) {\n                return false;\n            }\n        } else if (!lastName.equals(other.lastName)) {\n            return false;\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/data/instance/TransientDataInstanceServiceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertNull;\nimport static org.junit.Assert.assertTrue;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.assertj.core.api.Assertions;\nimport org.bonitasoft.engine.bpm.CommonBPMServicesTest;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.cache.ehcache.EhCacheCacheService;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.data.instance.TransientDataService;\nimport org.bonitasoft.engine.core.data.instance.impl.TransientDataServiceImpl;\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\nimport org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilder;\nimport org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilderFactory;\nimport org.bonitasoft.engine.data.definition.model.builder.SXMLDataDefinitionBuilder;\nimport org.bonitasoft.engine.data.definition.model.builder.SXMLDataDefinitionBuilderFactory;\nimport org.bonitasoft.engine.data.instance.exception.SDataInstanceNotFoundException;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SDataInstanceBuilder;\nimport org.bonitasoft.engine.data.instance.model.SXMLDataInstance;\nimport org.bonitasoft.engine.expression.ContainerState;\nimport org.bonitasoft.engine.expression.ExpressionService;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.expression.model.builder.SExpressionBuilder;\nimport org.bonitasoft.engine.expression.model.builder.SExpressionBuilderFactory;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Zhao Na\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @author Emmanuel Duchastenier\n */\n@SuppressWarnings(\"javadoc\")\npublic class TransientDataInstanceServiceIT extends CommonBPMServicesTest {\n\n    private static final Map<Integer, Object> EMPTY_RESOLVED_EXPRESSIONS = Collections.emptyMap();\n\n    protected ExpressionService expressionService;\n    protected EhCacheCacheService cacheService;\n\n    protected TransientDataService dataInstanceService;\n\n    @Before\n    public void setup() {\n        expressionService = getServiceAccessor().getExpressionService();\n        cacheService = (EhCacheCacheService) getServiceAccessor().getCacheService();\n        if (cacheService.isStopped()) {\n            try {\n                cacheService.start();\n            } catch (SBonitaException e) {\n                throw new RuntimeException(e);\n            }\n        }\n        dataInstanceService = new TransientDataServiceImpl(cacheService,\n                getServiceAccessor().getExpressionResolverService(),\n                getServiceAccessor().getActivityInstanceService(),\n                getServiceAccessor().getProcessDefinitionService());\n    }\n\n    @After\n    public void after() {\n        try {\n            cacheService.stop();\n            cacheService.start();\n        } catch (SBonitaException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    private SDataInstance buildDataInstance(final String instanceName, final String className, final String description,\n            final String content,\n            final long containerId, final String containerType, final boolean isTransient) throws SBonitaException {\n        // create definition\n        final SDataDefinitionBuilder dataDefinitionBuilder = BuilderFactory.get(SDataDefinitionBuilderFactory.class)\n                .createNewInstance(instanceName, className);\n        initializeBuilder(dataDefinitionBuilder, description, content, className, isTransient);\n        final SDataDefinition dataDefinition = dataDefinitionBuilder.done();\n\n        // create data instance\n        final SDataInstanceBuilder dataInstanceBuilder = SDataInstanceBuilder.createNewInstance(dataDefinition);\n        evaluateDefaultValueOf(dataDefinition, dataInstanceBuilder);\n        return dataInstanceBuilder.setContainerId(containerId).setContainerType(containerType).done();\n    }\n\n    private SDataInstance buildDataInstanceConstant(final String instanceName, final String className,\n            final String description, final String content,\n            final long containerId, final String containerType, final boolean isTransient) throws SBonitaException {\n        // create definition\n        final SDataDefinitionBuilder dataDefinitionBuilder = BuilderFactory.get(SDataDefinitionBuilderFactory.class)\n                .createNewInstance(instanceName, className);\n        initializeBuilderConstant(dataDefinitionBuilder, description, content, className, isTransient);\n        final SDataDefinition dataDefinition = dataDefinitionBuilder.done();\n        // create datainstance\n        final SDataInstanceBuilder dataInstanceBuilder = SDataInstanceBuilder.createNewInstance(dataDefinition);\n        evaluateDefaultValueOf(dataDefinition, dataInstanceBuilder);\n        return dataInstanceBuilder.setContainerId(containerId).setContainerType(containerType).done();\n    }\n\n    private void evaluateDefaultValueOf(final SDataDefinition dataDefinition,\n            final SDataInstanceBuilder dataInstanceBuilder) throws SBonitaException {\n        final SExpression expression = dataDefinition.getDefaultValueExpression();\n        if (expression != null) {\n            dataInstanceBuilder.setValue((Serializable) expressionService.evaluate(expression,\n                    Collections.singletonMap(\"processDefinitionId\", 546l), EMPTY_RESOLVED_EXPRESSIONS,\n                    ContainerState.ACTIVE));\n        }\n    }\n\n    private SDataInstance buildLongTextDataInstance(final String instanceName, final String description,\n            final String content, final long containerId,\n            final String containerType, final Boolean isTransient) throws SBonitaException {\n        // create definition\n        final SDataDefinitionBuilder dataDefinitionBuilder = BuilderFactory.get(SDataDefinitionBuilderFactory.class)\n                .createNewTextData(instanceName)\n                .setAsLongText(true);\n\n        initializeBuilder(dataDefinitionBuilder, description, content, String.class.getName(), isTransient);\n        final SDataDefinition dataDefinition = dataDefinitionBuilder.done();\n        final SDataInstanceBuilder dataInstanceBuilder = SDataInstanceBuilder.createNewInstance(dataDefinition)\n                .setContainerId(containerId).setContainerType(containerType);\n        // create data instance\n        evaluateDefaultValueOf(dataDefinition, dataInstanceBuilder);\n        return dataInstanceBuilder.done();\n    }\n\n    private SDataInstance buildXMLDataInstance(final String instanceName, final String description,\n            final String namespace, final String xmlElement,\n            final String content, final long containerId, final String containerType) throws SBonitaException {\n        // create definition\n        final SDataDefinition dataDefinition = buildDataDefinition(instanceName, description, namespace, xmlElement,\n                content, String.class.getName());\n\n        // create data instance\n        final SDataInstanceBuilder dataInstanceBuilder = SDataInstanceBuilder.createNewInstance(dataDefinition)\n                .setContainerId(containerId).setContainerType(containerType);\n        evaluateDefaultValueOf(dataDefinition, dataInstanceBuilder);\n        return dataInstanceBuilder.done();\n    }\n\n    private SDataDefinition buildDataDefinition(final String instanceName, final String description,\n            final String namespace, final String xmlElement,\n            final String content, final String defaultValueReturnType) throws SInvalidExpressionException {\n        final SXMLDataDefinitionBuilder dataDefinitionBuilder = BuilderFactory\n                .get(SXMLDataDefinitionBuilderFactory.class).createNewXMLData(instanceName)\n                .setNamespace(namespace).setElement(xmlElement);\n        dataDefinitionBuilder.setDescription(description);\n        dataDefinitionBuilder.setTransient(false);\n        SExpression expression = null;\n        if (content != null) {\n            // create expression\n            final SExpressionBuilder expreBuilder = BuilderFactory.get(SExpressionBuilderFactory.class)\n                    .createNewInstance();\n            // this discrimination'll be changed.\n            expreBuilder.setContent(content).setReturnType(defaultValueReturnType)\n                    .setExpressionType(SExpression.TYPE_READ_ONLY_SCRIPT)\n                    .setInterpreter(SExpression.GROOVY);\n            expression = expreBuilder.done();\n        }\n        dataDefinitionBuilder.setDefaultValue(expression);\n        return dataDefinitionBuilder.done();\n    }\n\n    private void initializeBuilder(final SDataDefinitionBuilder dataDefinitionBuilder, final String description,\n            final String content,\n            final String defaultValueReturnType, final boolean isTransient) throws SInvalidExpressionException {\n        SExpression expression = null;\n        if (content != null) {\n            // create expression\n            final SExpressionBuilder expreBuilder = BuilderFactory.get(SExpressionBuilderFactory.class)\n                    .createNewInstance();\n            // this discrimination'll be changed.\n            expreBuilder.setContent(content).setReturnType(defaultValueReturnType)\n                    .setExpressionType(SExpression.TYPE_READ_ONLY_SCRIPT)\n                    .setInterpreter(SExpression.GROOVY);\n            expression = expreBuilder.done();\n        }\n\n        dataDefinitionBuilder.setDescription(description);\n        dataDefinitionBuilder.setTransient(isTransient);\n        dataDefinitionBuilder.setDefaultValue(expression);\n    }\n\n    private void initializeBuilderConstant(final SDataDefinitionBuilder dataDefinitionBuilder, final String description,\n            final String content,\n            final String defaultValueReturnType, final boolean isTransient) throws SInvalidExpressionException {\n        SExpression expression = null;\n        if (content != null) {\n            // create expression\n            final SExpressionBuilder expreBuilder = BuilderFactory.get(SExpressionBuilderFactory.class)\n                    .createNewInstance();\n            // this discrimination'll be changed.\n            expreBuilder.setContent(content).setReturnType(defaultValueReturnType)\n                    .setExpressionType(SExpression.TYPE_CONSTANT);\n            expression = expreBuilder.done();\n        }\n\n        dataDefinitionBuilder.setDescription(description);\n        dataDefinitionBuilder.setTransient(isTransient);\n        dataDefinitionBuilder.setDefaultValue(expression);\n    }\n\n    private void checkDataInstance(final SDataInstance dataInstance, final String name, final String description,\n            final boolean isTransient,\n            final String className, final Serializable value, final long containerId, final String containerType) {\n        assertEquals(name, dataInstance.getName());\n        assertEquals(description, dataInstance.getDescription());\n        assertEquals(isTransient, dataInstance.isTransientData());\n        assertEquals(className, dataInstance.getClassName());\n        assertEquals(value, dataInstance.getValue());\n        assertEquals(containerId, dataInstance.getContainerId());\n        assertEquals(containerType, dataInstance.getContainerType());\n    }\n\n    private void checkXMLDataInstance(final SXMLDataInstance dataInstance, final String name, final String description,\n            final boolean isTransient,\n            final String className, final Serializable value, final long containerId, final String containerType,\n            final String namespace,\n            final String element) {\n        assertEquals(namespace, dataInstance.getNamespace());\n        assertEquals(element, dataInstance.getElement());\n        checkDataInstance(dataInstance, name, description, isTransient, className, value, containerId, containerType);\n    }\n\n    private EntityUpdateDescriptor getUpdateDescriptor(final String description, final Serializable newValue) {\n        // update data instance\n        final EntityUpdateDescriptor updateDescriptor = new EntityUpdateDescriptor();\n        updateDescriptor.addField(SDataInstance.VALUE, newValue);\n        updateDescriptor.addField(SDataInstance.DESCRIPTION, description);\n\n        return updateDescriptor;\n    }\n\n    private void insertDataInstance(final SDataInstance dataInstance) throws SBonitaException {\n        getTransactionService().begin();\n        try {\n            // create data instance\n            dataInstanceService.createDataInstance(dataInstance);\n        } finally {\n            getTransactionService().complete();\n        }\n    }\n\n    private SDataInstance getDataInstance(final long dataInstanceId) throws SBonitaException {\n        getTransactionService().begin();\n        try {\n            return dataInstanceService.getDataInstance(dataInstanceId);\n        } finally {\n            getTransactionService().complete();\n        }\n    }\n\n    private SDataInstance getDataInstanceByNameAndContainer(final String dataName, final long containerId,\n            final String containerType) throws SBonitaException {\n        getTransactionService().begin();\n        try {\n            // get the data instance by several conditions\n            return dataInstanceService.getDataInstance(dataName, containerId, containerType);\n        } finally {\n            getTransactionService().complete();\n        }\n    }\n\n    private String getLongText() {\n        final StringBuilder stb = new StringBuilder();\n        for (int i = 0; i < 255; i++) {\n            stb.append(i);\n        }\n        return stb.toString();\n    }\n\n    private void deleteDataInstance(final SDataInstance dataInstance) throws SBonitaException {\n        getTransactionService().begin();\n        try {\n            dataInstanceService.deleteDataInstance(dataInstance);\n        } finally {\n            getTransactionService().complete();\n        }\n    }\n\n    private void updateDataInstance(final String dataName, final Long containerId, final String containerType,\n            final String newDescription,\n            final Serializable value) throws SBonitaException {\n        getTransactionService().begin();\n        try {\n            // retrieve the data instance\n            final SDataInstance dataInstanceRes = dataInstanceService.getDataInstance(dataName, containerId,\n                    containerType);\n            // update the data instance and this step must be with an activity data Instance in same transaction.\n            final EntityUpdateDescriptor updateDescriptor = getUpdateDescriptor(newDescription, value);\n            dataInstanceService.updateDataInstance(dataInstanceRes, updateDescriptor);\n        } finally {\n            getTransactionService().complete();\n        }\n    }\n\n    private void verifyCreateAndRetrieveDataInstance(final String name, final String classType,\n            final String description, final String content,\n            final Long containerId, final String containerType, final Boolean isTransient,\n            final Serializable checkValue) throws Exception {\n        final SDataInstance dataInstance = buildDataInstance(name, classType, description, content, containerId,\n                containerType, isTransient);\n        insertDataInstance(dataInstance);\n\n        final SDataInstance dataInstanceRes = getDataInstanceByNameAndContainer(dataInstance.getName(),\n                dataInstance.getContainerId(),\n                dataInstance.getContainerType());\n        checkDataInstance(dataInstanceRes, name, description, isTransient, classType, checkValue, containerId,\n                containerType);\n\n        deleteDataInstance(dataInstanceRes);\n    }\n\n    private void verifyCreateAndRetrieveDataInstanceConstant(final String name, final String classType,\n            final String description, final String content,\n            final Long containerId, final String containerType, final Boolean isTransient,\n            final Serializable checkValue) throws Exception {\n        final SDataInstance dataInstance = buildDataInstanceConstant(name, classType, description, content, containerId,\n                containerType, isTransient);\n        insertDataInstance(dataInstance);\n\n        final SDataInstance dataInstanceRes = getDataInstanceByNameAndContainer(dataInstance.getName(),\n                dataInstance.getContainerId(),\n                dataInstance.getContainerType());\n        checkDataInstance(dataInstanceRes, name, description, isTransient, classType, checkValue, containerId,\n                containerType);\n\n        deleteDataInstance(dataInstanceRes);\n    }\n\n    private void verifyCreateAndRetrieveDoubleDataInstance(final boolean isTransient) throws Exception {\n        verifyCreateAndRetrieveDataInstance(\"createDouble1\", Double.class.getName(), \"testCreateDescriptionForDouble1\",\n                \"new Double(5.0)\", 6L, \"process\",\n                isTransient, 5.0);\n    }\n\n    private void verifyCreateAndRetrieveFloatDataInstance(final boolean isTransient) throws Exception {\n        verifyCreateAndRetrieveDataInstance(\"createFloat1\", Float.class.getName(), \"testCreateDescriptionForFloat1\",\n                \"new Float(5.0)\", 6L, \"process\",\n                isTransient, 5.0F);\n    }\n\n    @Test\n    public void getDataInstancesWithTransientAndNonTransientData() throws Exception {\n        final long containerId = 4444L;\n        final String containerType = \"ActivityScope\";\n        final String instance1Name = \"non-transient\";\n        final SDataInstance data1Instance = buildDataInstance(instance1Name, Integer.class.getName(),\n                \"some non transient data\", null, containerId,\n                containerType, false);\n        insertDataInstance(data1Instance);\n        final String instance2Name = \"transient\";\n        final SDataInstance data2Instance = buildDataInstance(instance2Name, Integer.class.getName(),\n                \"some transient data\", null, containerId, containerType,\n                true);\n        insertDataInstance(data2Instance);\n\n        final List<String> dataNames = new ArrayList<>(2);\n        dataNames.add(instance1Name);\n        dataNames.add(instance2Name);\n        getTransactionService().begin();\n        List<SDataInstance> dataInstances = null;\n        try {\n            dataInstances = dataInstanceService.getDataInstances(dataNames, containerId, containerType);\n        } finally {\n            getTransactionService().complete();\n        }\n        assertEquals(2, dataInstances.size());\n        Assertions.assertThat(dataInstances).extracting(\"name\").containsOnly(instance1Name, instance2Name);\n    }\n\n    @Test\n    public void createDataWithComplexName() throws Exception {\n        final SDataInstance dataInstance = buildDataInstance(\"FirstLetterUpperCase\", Integer.class.getName(), null,\n                \"987456321\", 4444L, \"ActivityScope\", false);\n        insertDataInstance(dataInstance);\n        assertTrue(0 != dataInstance.getId());\n\n        final SDataInstance dataInstance2 = buildDataInstance(\"var with spaces\", Double.class.getName(), null, \"221d\",\n                4654L, \"DummyScopeType\", false);\n        insertDataInstance(dataInstance2);\n        assertTrue(0 != dataInstance2.getId());\n    }\n\n    @Test\n    public void testCreateAndRetrieveDoubleInstance() throws Exception {\n        verifyCreateAndRetrieveDoubleDataInstance(false);\n    }\n\n    @Test\n    public void testCreateAndRetrieveDoubleInstanceTransient() throws Exception {\n        verifyCreateAndRetrieveDoubleDataInstance(true);\n    }\n\n    @Test\n    public void testCreateAndRetrieveFloatInstance() throws Exception {\n        verifyCreateAndRetrieveFloatDataInstance(false);\n    }\n\n    @Test\n    public void testCreateAndRetrieveFloatInstanceTransient() throws Exception {\n        verifyCreateAndRetrieveFloatDataInstance(true);\n    }\n\n    private void verifyCreateAndRetrieveBooleanDataInstance(final boolean isTransient) throws Exception {\n        verifyCreateAndRetrieveDataInstance(\"createBoolean1\", Boolean.class.getName(),\n                \"testCreateDescriptionForBoolean1\", \"true\", 6L, \"process\", isTransient,\n                true);\n    }\n\n    @Test\n    public void testCreateAndRetrieveBooleanInstance() throws Exception {\n        verifyCreateAndRetrieveBooleanDataInstance(false);\n    }\n\n    @Test\n    public void testCreateAndRetrieveBooleanInstanceTransient() throws Exception {\n        verifyCreateAndRetrieveBooleanDataInstance(true);\n    }\n\n    @Test\n    public void testCreateAndRetrieveIntegerInstance() throws Exception {\n        verifyCreateAndRetrieveDataInstance(\"createInteger\", Integer.class.getName(), \"testCreateDescription\", \"1+2\",\n                7L, \"process\", false, 3);\n    }\n\n    @Test\n    public void testCreateAndRetrieveIntegerInstanceTransient() throws Exception {\n        verifyCreateAndRetrieveDataInstance(\"createInteger\", Integer.class.getName(), \"testCreateDescription\", \"1+2\",\n                7L, \"process\", true, 3);\n    }\n\n    @Test\n    public void testCreateAndRetrieveLongInstance() throws Exception {\n        verifyCreateAndRetrieveDataInstance(\"createLong\", Long.class.getName(), \"testCreateDescription\", \"new Long(2)\",\n                8L, \"process\", false, 2L);\n    }\n\n    @Test\n    public void testCreateAndRetrieveLongInstanceTransient() throws Exception {\n        verifyCreateAndRetrieveDataInstance(\"createLong\", Long.class.getName(), \"testCreateDescription\", \"new Long(2)\",\n                8L, \"process\", true, 2L);\n    }\n\n    @Test\n    public void testCreateAndRetrieveShortTextInstance() throws Exception {\n        verifyCreateAndRetrieveDataInstance(\"createString\", String.class.getName(), \"testCreateDescription\",\n                \"'test123'\", 9L, \"process\", false, \"test123\");\n    }\n\n    @Test\n    public void testCreateAndRetrieveShortTextInstanceTransient() throws Exception {\n        verifyCreateAndRetrieveDataInstance(\"createString\", String.class.getName(), \"testCreateDescription\",\n                \"'test123'\", 9L, \"process\", true, \"test123\");\n    }\n\n    private void verifyCreateAndRetrieveLongTextData(final boolean isTransient) throws Exception {\n\n        final String longText = getLongText();\n\n        final SDataInstance longTextDataInstance = buildLongTextDataInstance(\"longTextData\", \"A very long text\",\n                getStringForExpression(longText), 11L,\n                \"process\", isTransient);\n        insertDataInstance(longTextDataInstance);\n\n        final SDataInstance dataInstance = getDataInstanceByNameAndContainer(longTextDataInstance.getName(),\n                longTextDataInstance.getContainerId(),\n                longTextDataInstance.getContainerType());\n        checkDataInstance(dataInstance, \"longTextData\", \"A very long text\", isTransient, String.class.getName(),\n                longText, 11L, \"process\");\n\n        deleteDataInstance(dataInstance);\n    }\n\n    @Test\n    public void testCreateAndRetrieveLongTextData() throws Exception {\n        verifyCreateAndRetrieveLongTextData(false);\n    }\n\n    @Test\n    public void testCreateAndRetrieveLongTextDataTransient() throws Exception {\n        verifyCreateAndRetrieveLongTextData(true);\n    }\n\n    @Test\n    public void testCreateAndRetrieveBlobDataInstanceWithoutDefaultValue() throws Exception {\n        verifyCreateAndRetrieveDataInstance(\"blobTextData\", LightEmployee.class.getName(),\n                \"A custom java object as blob\",\n                \"return new org.bonitasoft.engine.data.instance.LightEmployee(\\\"manu\\\", \\\"duch\\\", 35)\", 15L, \"process\",\n                false, new LightEmployee(\"manu\",\n                        \"duch\", 35));\n    }\n\n    @Test\n    public void testCreateAndRetrieveBlobDataInstanceWithoutDefaultValueTransient() throws Exception {\n        verifyCreateAndRetrieveDataInstance(\"blobTextData\", LightEmployee.class.getName(),\n                \"A custom java object as blob\",\n                \"return new org.bonitasoft.engine.data.instance.LightEmployee(\\\"manu\\\", \\\"duch\\\", 53)\", 15L, \"process\",\n                true, new LightEmployee(\"manu\", \"duch\",\n                        53));\n    }\n\n    @Test\n    public void testCreateBooleanInstanceWithoutDefaultValue() throws Exception {\n        verifyCreateAndRetrieveDataInstanceConstant(\"createBoolean2\", Boolean.class.getName(),\n                \"testCreateDescriptionForBoolean2\", \"true\", 11L, \"process\",\n                false, true);\n    }\n\n    @Test\n    public void testCreateBooleanInstanceWithoutDefaultValueTransient() throws Exception {\n        verifyCreateAndRetrieveDataInstanceConstant(\"createBoolean2\", Boolean.class.getName(),\n                \"testCreateDescriptionForBoolean2\", \"false\", 11L, \"process\",\n                true, false);\n    }\n\n    @Test\n    public void testCreateDoubleInstanceWithoutDefaultValue() throws Exception {\n        verifyCreateAndRetrieveDataInstanceConstant(\"createDouble2\", Double.class.getName(),\n                \"testCreateDescriptionForDouble2\", \"37.0\", 12L, \"task\", false,\n                37.0);\n    }\n\n    @Test\n    public void testCreateDoubleInstanceWithoutDefaultValueTransient() throws Exception {\n        verifyCreateAndRetrieveDataInstanceConstant(\"createDouble2\", Double.class.getName(),\n                \"testCreateDescriptionForDouble2\", \"73.0\", 12L, \"task\", true,\n                73.0);\n    }\n\n    @Test\n    public void testCreateFloatInstanceWithoutDefaultValue() throws Exception {\n        verifyCreateAndRetrieveDataInstanceConstant(\"createFloat2\", Float.class.getName(),\n                \"testCreateDescriptionForFloat2\", \"34.F\", 12L, \"task\", false, 34.F);\n    }\n\n    @Test\n    public void testCreateFloatInstanceWithoutDefaultValueTransient() throws Exception {\n        verifyCreateAndRetrieveDataInstanceConstant(\"createFloat2\", Float.class.getName(),\n                \"testCreateDescriptionForFloat2\", \"43.F\", 12L, \"task\", true, 43.F);\n    }\n\n    @Test\n    public void testCreateIntegerInstanceWithoutDefaultValue() throws Exception {\n        verifyCreateAndRetrieveDataInstanceConstant(\"createInteger2\", Integer.class.getName(),\n                \"testCreateDescriptionForInteger2\", \"32\", 13L, \"task1\", false,\n                32);\n    }\n\n    @Test\n    public void testCreateIntegerInstanceWithoutDefaultValueTransient() throws Exception {\n        verifyCreateAndRetrieveDataInstanceConstant(\"createInteger2\", Integer.class.getName(),\n                \"testCreateDescriptionForInteger2\", \"23\", 13L, \"task1\", true,\n                23);\n    }\n\n    @Test\n    public void testCreateLongInstanceWithoutDefaultValue() throws Exception {\n        verifyCreateAndRetrieveDataInstanceConstant(\"createLong2\", Long.class.getName(),\n                \"testCreateDescriptionForLong2\", \"47\", 14L, \"task2\", false, 47L);\n    }\n\n    @Test\n    public void testCreateLongInstanceWithoutDefaultValueTransient() throws Exception {\n        verifyCreateAndRetrieveDataInstanceConstant(\"createLong2\", Long.class.getName(),\n                \"testCreateDescriptionForLong2\", \"74\", 14L, \"task2\", true, 74L);\n    }\n\n    @Test\n    public void testCreateShortTextInstanceWithoutDefaultValue() throws Exception {\n        verifyCreateAndRetrieveDataInstanceConstant(\"createString2\", String.class.getName(),\n                \"testCreateDescriptionForString2\", \"strTest\", 15L, \"task3\", false,\n                \"strTest\");\n    }\n\n    @Test\n    public void testCreateShortTextInstanceWithoutDefaultValueTransient() throws Exception {\n        verifyCreateAndRetrieveDataInstanceConstant(\"createString2\", String.class.getName(),\n                \"testCreateDescriptionForString2\", \"strTest2\", 15L, \"task3\", true,\n                \"strTest2\");\n    }\n\n    private void verifyDeleteDataInstance(final String name, final String classType, final String description,\n            final String content, final Long containerId,\n            final String containerType, final Boolean isTransient, final Serializable checkValue,\n            final String assertContent) throws Exception {\n        final SDataInstance dataInstance = buildDataInstance(name, classType, description, content, containerId,\n                containerType, isTransient);\n        insertDataInstance(dataInstance);\n\n        final SDataInstance dataInstanceRes = getDataInstance(dataInstance.getId());\n        assertNotNull(dataInstanceRes);\n        checkDataInstance(dataInstanceRes, name, description, isTransient, classType, checkValue, containerId,\n                containerType);\n\n        deleteDataInstance(dataInstance);\n\n        final SDataInstance dataDeletedRes = getDataInstance(dataInstance.getId());\n        assertNull(assertContent, dataDeletedRes);\n    }\n\n    @Test(expected = SDataInstanceNotFoundException.class)\n    public void testDeleteBooleanInstance() throws Exception {\n        verifyDeleteDataInstance(\"deleteBoolean1\", Boolean.class.getName(), \"testDeleteDescriptionForBoolean\", \"true\",\n                1L, \"task\", false, true,\n                \"the Boolean instance was not deleted\");\n    }\n\n    @Test(expected = SDataInstanceNotFoundException.class)\n    public void testDeleteTransientBooleanInstance() throws Exception {\n        verifyDeleteDataInstance(\"deleteBoolean1\", Boolean.class.getName(), \"testDeleteDescriptionForBoolean\", \"true\",\n                1L, \"task\", true, true,\n                \"the Boolean instance was not deleted\");\n    }\n\n    @Test(expected = SDataInstanceNotFoundException.class)\n    public void testDeleteDoubleInstance() throws Exception {\n        verifyDeleteDataInstance(\"deleteDouble1\", Double.class.getName(), \"testDeleteDescriptionForDouble\", \"1.23D\", 1L,\n                \"task\", false, 1.23D,\n                \"the Double instance was not deleted\");\n    }\n\n    @Test(expected = SDataInstanceNotFoundException.class)\n    public void testDeleteFloatInstance() throws Exception {\n        verifyDeleteDataInstance(\"deleteFloat1\", Float.class.getName(), \"testDeleteDescriptionForFloat\", \"1.23F\", 1L,\n                \"task\", false, 1.23F,\n                \"the Float instance was not deleted\");\n    }\n\n    @Test(expected = SDataInstanceNotFoundException.class)\n    public void testTransientDeleteDoubleInstance() throws Exception {\n        verifyDeleteDataInstance(\"deleteDouble1\", Double.class.getName(), \"testDeleteDescriptionForDouble\", \"1.23D\", 1L,\n                \"task\", true, 1.23D,\n                \"the Double instance was not deleted\");\n    }\n\n    @Test(expected = SDataInstanceNotFoundException.class)\n    public void testTransientDeleteFloatInstance() throws Exception {\n        verifyDeleteDataInstance(\"deleteFloat1\", Float.class.getName(), \"testDeleteDescriptionForFloat\", \"1.23F\", 1L,\n                \"task\", true, 1.23F,\n                \"the Float instance was not deleted\");\n    }\n\n    @Test(expected = SDataInstanceNotFoundException.class)\n    public void testDeleteIntegerInstance() throws Exception {\n        verifyDeleteDataInstance(\"deleteInteger1\", Integer.class.getName(), \"testDeleteDescriptionForInteger\", \"8+4\",\n                1L, \"task\", false, 12,\n                \"the Integer instance was not deleted\");\n    }\n\n    @Test(expected = SDataInstanceNotFoundException.class)\n    public void testTransientDeleteIntegerInstance() throws Exception {\n        verifyDeleteDataInstance(\"deleteInteger1\", Integer.class.getName(), \"testDeleteDescriptionForInteger\", \"8+4\",\n                1L, \"task\", true, 12,\n                \"the Integer instance was not deleted\");\n    }\n\n    @Test(expected = SDataInstanceNotFoundException.class)\n    public void testDeleteLongInstance() throws Exception {\n        verifyDeleteDataInstance(\"deleteLong1\", Long.class.getName(), \"testDeleteDescriptionForLong\", \"12L\", 1L, \"task\",\n                false, 12L,\n                \"the Long instance was not deleted\");\n    }\n\n    @Test(expected = SDataInstanceNotFoundException.class)\n    public void testTransientDeleteLongInstance() throws Exception {\n        verifyDeleteDataInstance(\"deleteLong1\", Long.class.getName(), \"testDeleteDescriptionForLong\", \"12L\", 1L, \"task\",\n                true, 12L,\n                \"the Long instance was not deleted\");\n    }\n\n    @Test(expected = SDataInstanceNotFoundException.class)\n    public void testDeleteShortTextInstance() throws Exception {\n        verifyDeleteDataInstance(\"deleteString1\", String.class.getName(), \"testDeleteDescriptionForString\", \"'test123'\",\n                1L, \"task\", false, \"test123\",\n                \"the Short Text instance was not deleted\");\n    }\n\n    @Test(expected = SDataInstanceNotFoundException.class)\n    public void testTransientDeleteShortTextInstance() throws Exception {\n        verifyDeleteDataInstance(\"deleteString1\", String.class.getName(), \"testDeleteDescriptionForString\", \"'test123'\",\n                1L, \"task\", true, \"test123\",\n                \"the Short Text instance was not deleted\");\n    }\n\n    @Test(expected = SDataInstanceNotFoundException.class)\n    public void testDeleteBlobInstance() throws Exception {\n        final LightEmployee employee = new LightEmployee(\"firstName\", \"lastName\", 30);\n        verifyDeleteDataInstance(\"blobData\", LightEmployee.class.getName(), \"A custom java object\",\n                \"new org.bonitasoft.engine.data.instance.LightEmployee(\\\"firstName\\\", \\\"lastName\\\", 30)\", 11L,\n                \"processTask\", false, employee,\n                \"the blob data instance was not deleted\");\n    }\n\n    @Test(expected = SDataInstanceNotFoundException.class)\n    public void testTransientDeleteBlobInstance() throws Exception {\n        final LightEmployee employee = new LightEmployee(\"firstName\", \"lastName\", 30);\n        verifyDeleteDataInstance(\"blobData\", LightEmployee.class.getName(), \"A custom java object\",\n                \"new org.bonitasoft.engine.data.instance.LightEmployee(\\\"firstName\\\", \\\"lastName\\\", 30)\", 11L,\n                \"processTask\", true, employee,\n                \"the blob data instance was not deleted\");\n    }\n\n    private void verifyDeleteLongTextData(final Boolean isTransient) throws Exception {\n        final String longText = getLongText();\n\n        final SDataInstance longTextDataInstance = buildLongTextDataInstance(\"longTextData\", \"A very long text\",\n                getStringForExpression(longText), 0, null,\n                isTransient);\n        insertDataInstance(longTextDataInstance);\n\n        final SDataInstance dataInstance = getDataInstance(longTextDataInstance.getId());\n        assertNotNull(dataInstance);\n\n        deleteDataInstance(dataInstance);\n\n        final SDataInstance dataInstanceRes = getDataInstance(longTextDataInstance.getId());\n        assertNull(\"the Long Text instance was not deleted\", dataInstanceRes);\n    }\n\n    @Test(expected = SDataInstanceNotFoundException.class)\n    public void testDeleteLongTextData() throws Exception {\n        verifyDeleteLongTextData(false);\n    }\n\n    @Test(expected = SDataInstanceNotFoundException.class)\n    public void testTransientDeleteLongTextData() throws Exception {\n        verifyDeleteLongTextData(true);\n    }\n\n    private long verifyUpdateDataInstance(final String name, final String classType, final String description,\n            final String content, final Long containerId,\n            final String containerType, final Boolean isTransient, final String updateDescription,\n            final Serializable updateValue) throws Exception {\n        final SDataInstance dataInstance = buildDataInstance(name, classType, description, content, containerId,\n                containerType, isTransient);\n        insertDataInstance(dataInstance);\n        updateDataInstance(dataInstance.getName(), dataInstance.getContainerId(), dataInstance.getContainerType(),\n                updateDescription, updateValue);\n\n        final long dataInstanceId = dataInstance.getId();\n        final SDataInstance dataInstanceRes = getDataInstance(dataInstanceId);\n        checkDataInstance(dataInstanceRes, name, updateDescription, isTransient, classType, updateValue, containerId,\n                containerType);\n        deleteDataInstance(dataInstanceRes);\n        return dataInstanceId;\n\n    }\n\n    @Test\n    public void testUpdateBooleanInstance() throws Exception {\n        verifyUpdateDataInstance(\"updateBoolean\", Boolean.class.getName(), \"testUpdateDescription\", \"true\", 11L,\n                \"miniTask\", false,\n                \"testUpdateDescription123456\", false);\n    }\n\n    @Test\n    public void testTransientUpdateBooleanInstance() throws Exception {\n        verifyUpdateDataInstance(\"updateBoolean\", Boolean.class.getName(), \"testUpdateDescription\", \"true\", 11L,\n                \"miniTask\", true,\n                \"testUpdateDescription123456\", false);\n    }\n\n    @Test\n    public void testUpdateDoubleInstance() throws Exception {\n        verifyUpdateDataInstance(\"updateDouble\", Double.class.getName(), \"testUpdateDescription\", \"new Double(5.0)\",\n                11L, \"miniTask\", false,\n                \"testUpdateDescription4\", 7.0);\n    }\n\n    @Test\n    public void testTransientUpdateDoubleInstance() throws Exception {\n        verifyUpdateDataInstance(\"updateDouble\", Double.class.getName(), \"testUpdateDescription\", \"new Double(5.0)\",\n                11L, \"miniTask\", true,\n                \"testUpdateDescription4\", 7.0);\n    }\n\n    @Test\n    public void testUpdateFloatInstance() throws Exception {\n        verifyUpdateDataInstance(\"updateFloat\", Float.class.getName(), \"testUpdateDescription\", \"new Float(5.0)\", 11L,\n                \"miniTask\", false,\n                \"testUpdateDescription4\", 7.0F);\n    }\n\n    @Test\n    public void testTransientUpdateFloatInstance() throws Exception {\n        verifyUpdateDataInstance(\"updateFloat\", Float.class.getName(), \"testUpdateDescription\", \"new Float(5.0)\", 11L,\n                \"miniTask\", true,\n                \"testUpdateDescription4\", 7.0F);\n    }\n\n    @Test\n    public void testUpdateIntegerInstance() throws Exception {\n        verifyUpdateDataInstance(\"updateInteger\", Integer.class.getName(), \"testUpdateDescription\", \"new Integer(5)\",\n                11L, \"miniTask\", false,\n                \"testUpdateDescription2\", 8);\n    }\n\n    @Test\n    public void testTransientUpdateIntegerInstance() throws Exception {\n        verifyUpdateDataInstance(\"updateInteger\", Integer.class.getName(), \"testUpdateDescription\", \"new Integer(5)\",\n                11L, \"miniTask\", true,\n                \"testUpdateDescription2\", 8);\n    }\n\n    @Test\n    public void testUpdateLongInstance() throws Exception {\n        verifyUpdateDataInstance(\"updateLong\", Long.class.getName(), \"testUpdateDescription\", \"new Long(5)\", 11L,\n                \"miniTask\", false, \"testUpdateDescription2\",\n                6L);\n    }\n\n    @Test\n    public void testTransientUpdateLongInstance() throws Exception {\n        verifyUpdateDataInstance(\"updateLong\", Long.class.getName(), \"testUpdateDescription\", \"new Long(5)\", 11L,\n                \"miniTask\", true, \"testUpdateDescription2\",\n                6L);\n    }\n\n    @Test\n    public void testUpdateShortTextInstance() throws Exception {\n        verifyUpdateDataInstance(\"updateShortText\", String.class.getName(), \"testUpdateDescription\", \"'123qwe'\", 11L,\n                \"miniTask\", false,\n                \"testUpdateDescription2\", \"123qwe2\");\n    }\n\n    @Test\n    public void testTransientUpdateShortTextInstance() throws Exception {\n        verifyUpdateDataInstance(\"updateShortText\", String.class.getName(), \"testUpdateDescription\", \"'123qwe'\", 11L,\n                \"miniTask\", true,\n                \"testUpdateDescription2\", \"123qwe2\");\n    }\n\n    private void verifyUpdateLongTextDataInstance(final String name, final String classType, final String description,\n            final String content,\n            final Long containerId, final String containerType, final Boolean isTransient,\n            final String updateDescription, final Serializable updateValue)\n            throws Exception {\n        final SDataInstance dataInstance = buildLongTextDataInstance(name, description, content, containerId,\n                containerType, isTransient);\n        insertDataInstance(dataInstance);\n\n        updateDataInstance(dataInstance.getName(), dataInstance.getContainerId(), dataInstance.getContainerType(),\n                updateDescription, updateValue);\n\n        final SDataInstance dataInstanceRes = getDataInstance(dataInstance.getId());\n        checkDataInstance(dataInstanceRes, name, updateDescription, isTransient, classType, updateValue, containerId,\n                containerType);\n        deleteDataInstance(dataInstanceRes);// dataInstance has no id here.\n    }\n\n    @Test\n    public void testUpdateLongTextData() throws Exception {\n        final String longText = getLongText();\n        verifyUpdateLongTextDataInstance(\"longTextData\", String.class.getName(), \"A very long text\",\n                getStringForExpression(longText), 11L, \"processTask\",\n                false, \"Updated description for long text\", longText + \"Updated\");\n    }\n\n    @Test\n    public void testTransientUpdateLongTextData() throws Exception {\n        final String longText = getLongText();\n        verifyUpdateLongTextDataInstance(\"longTextData\", String.class.getName(), \"A very long text\",\n                getStringForExpression(longText), 11L, \"processTask\",\n                true, \"Updated description for long text\", longText + \"Updated\");\n    }\n\n    @Test\n    public void testUpdateBlobDataInstance() throws Exception {\n        final LightEmployee employee = new LightEmployee(\"firstName\", \"lastName\", 30);\n        verifyUpdateDataInstance(\"blobTextData\", LightEmployee.class.getName(), \"A custom java object\", null, 11L,\n                \"processTask\", false,\n                \"A custom java updated\", employee);\n    }\n\n    @Test\n    public void testTransientUpdateBlobDataInstance() throws Exception {\n        final LightEmployee employee = new LightEmployee(\"firstName\", \"lastName\", 30);\n        verifyUpdateDataInstance(\"blobTextData\", LightEmployee.class.getName(), \"A custom java object\", null, 11L,\n                \"processTask\", true,\n                \"A custom java updated\", employee);\n    }\n\n    private void verifyCreateAndGetDataInstance(final String name, final String classType, final String description,\n            final String content,\n            final Long containerId, final String containerType, final Boolean isTransient,\n            final Serializable checkValue) throws Exception {\n        final SDataInstance dataInstance = buildDataInstance(name, classType, description, content, containerId,\n                containerType, isTransient);\n        insertDataInstance(dataInstance);\n\n        final SDataInstance dataInstanceRes = getDataInstanceByNameAndContainer(dataInstance.getName(),\n                dataInstance.getContainerId(),\n                dataInstance.getContainerType());\n        checkDataInstance(dataInstanceRes, name, description, isTransient, classType, checkValue, containerId,\n                containerType);\n\n        deleteDataInstance(dataInstanceRes);\n    }\n\n    @Test\n    public void testGetDataIntegerInstance() throws Exception {\n        verifyCreateAndGetDataInstance(\"getInteger\", Integer.class.getName(), \"testgetDescription\", \"new Integer(2)\",\n                20L, \"processInstance\", false, 2);\n    }\n\n    @Test\n    public void testGetTransientIntegerDataInstance() throws Exception {\n        verifyCreateAndGetDataInstance(\"getInteger\", Integer.class.getName(), \"testgetDescription\", \"new Integer(2)\",\n                20L, \"processInstance\", true, 2);\n    }\n\n    @Test\n    public void testGetDataBooleanInstance() throws Exception {\n        verifyCreateAndGetDataInstance(\"getBoolean\", Boolean.class.getName(), \"testgetDescription\", \"true\", 20L,\n                \"processInstance\", false, true);\n    }\n\n    @Test\n    public void testGetTransientBooleanDataInstance() throws Exception {\n        verifyCreateAndGetDataInstance(\"getBoolean\", Boolean.class.getName(), \"testgetDescription\", \"true\", 20L,\n                \"processInstance\", true, true);\n    }\n\n    @Test\n    public void testGetDataShortTextInstance() throws Exception {\n        verifyCreateAndGetDataInstance(\"getShortText\", String.class.getName(), \"testgetDescription\", \"'1122aabb'\", 20L,\n                \"processInstance\", false, \"1122aabb\");\n    }\n\n    @Test\n    public void testGetTransientShortTextDataInstance() throws Exception {\n        verifyCreateAndGetDataInstance(\"getShortText\", String.class.getName(), \"testgetDescription\", \"'1122aabb'\", 20L,\n                \"processInstance\", true, \"1122aabb\");\n    }\n\n    @Test\n    public void testGetLongDataInstance() throws Exception {\n        verifyCreateAndGetDataInstance(\"getLong\", Long.class.getName(), \"testgetDescription\", \"new Long(2)\", 20L,\n                \"processInstance\", false, 2L);\n    }\n\n    @Test\n    public void testGetTransientLongDataInstance() throws Exception {\n        verifyCreateAndGetDataInstance(\"getLong\", Long.class.getName(), \"testgetDescription\", \"new Long(2)\", 20L,\n                \"processInstance\", true, 2L);\n    }\n\n    @Test\n    public void testGetDataDoubleInstance() throws Exception {\n        verifyCreateAndGetDataInstance(\"getDouble\", Double.class.getName(), \"testgetDescription\", \"new Double(2.0)\",\n                20L, \"processInstance\", false, 2.0);\n    }\n\n    @Test\n    public void testGetTransientDoubleDataInstance() throws Exception {\n        verifyCreateAndGetDataInstance(\"getDouble\", Double.class.getName(), \"testgetDescription\", \"new Double(2.0)\",\n                20L, \"processInstance\", true, 2.0);\n    }\n\n    @Test\n    public void testGetDataFloatInstance() throws Exception {\n        verifyCreateAndGetDataInstance(\"getFloat\", Float.class.getName(), \"testgetDescription\", \"new Float(2.0)\", 20L,\n                \"processInstance\", false, 2.0F);\n    }\n\n    @Test\n    public void testGetTransientFloatDataInstance() throws Exception {\n        verifyCreateAndGetDataInstance(\"getFloat\", Float.class.getName(), \"testgetDescription\", \"new Float(2.0)\", 20L,\n                \"processInstance\", true, 2.0F);\n    }\n\n    @Test\n    public void testGetLongTextData() throws Exception {\n        verifyCreateAndRetrieveLongTextData(false);\n    }\n\n    @Test\n    public void testGetLongTextDataTransient() throws Exception {\n        verifyCreateAndRetrieveLongTextData(true);\n    }\n\n    @Test\n    public void testGetBlobData() throws Exception {\n        final LightEmployee employee = new LightEmployee(\"firstName\", \"lastName\", 30);\n        verifyCreateAndGetDataInstance(\"blobTextData\", LightEmployee.class.getName(), \"A custom java object2\",\n                \"new org.bonitasoft.engine.data.instance.LightEmployee(\\\"firstName\\\", \\\"lastName\\\", 30)\", 3L,\n                \"processTaskDefinition\", false, employee);\n    }\n\n    @Test\n    public void testGetBlobDataTransient() throws Exception {\n        final LightEmployee employee = new LightEmployee(\"firstName\", \"lastName\", 30);\n        verifyCreateAndGetDataInstance(\"blobTextData\", LightEmployee.class.getName(), \"A custom java object3\",\n                \"new org.bonitasoft.engine.data.instance.LightEmployee(\\\"firstName\\\", \\\"lastName\\\", 30)\", 11L,\n                \"processTask\", true, employee);\n    }\n\n    @Test\n    public void testCreateAndRetrieveXMLDataById() throws Exception {\n        final String xmlContent = buildSimpleXML1();\n\n        final long containerId = 15;\n        final String containerType = \"ActivityInstance\";\n\n        final SDataInstance dataInstance = buildXMLDataInstance(\"xmlVar\", \"This is a xml variable\",\n                \"org.bonitasoft.engine.data.instance.model.SDataInstance\", \"xmlElement\",\n                getStringForExpression(xmlContent), containerId,\n                containerType);\n        insertDataInstance(dataInstance);\n\n        // get the data instance by several conditions\n        final SDataInstance dataInstanceRes = getDataInstance(dataInstance.getId());\n        assertTrue(dataInstanceRes instanceof SXMLDataInstance);\n        checkXMLDataInstance((SXMLDataInstance) dataInstanceRes, \"xmlVar\", \"This is a xml variable\", false,\n                String.class.getName(), xmlContent, containerId,\n                containerType, \"org.bonitasoft.engine.data.instance.model.SDataInstance\", \"xmlElement\");\n\n        deleteDataInstance(dataInstanceRes);\n    }\n\n    private String getStringForExpression(final String str) {\n        return \"'\" + str + \"'\";\n    }\n\n    private String buildSimpleXML1() {\n        final StringBuilder stb = new StringBuilder();\n        stb.append(\"<root>\");\n        stb.append(\"<child>\");\n        stb.append(\"value\");\n        stb.append(\"</child>\");\n        stb.append(\"</root>\");\n        final String xmlContent = stb.toString();\n        return xmlContent;\n    }\n\n    private String buildSimpleXML2() {\n        final StringBuilder stb = new StringBuilder();\n        stb.append(\"<root>\");\n        stb.append(\"<child/>\");\n        stb.append(\"</root>\");\n        final String xmlContent = stb.toString();\n        return xmlContent;\n    }\n\n    @Test\n    public void testUpdateXMLData() throws Exception {\n        final String xmlContent = buildSimpleXML1();\n        final long containerId = 15;\n        final String containerType = \"ActivityInstance\";\n        final SDataInstance dataInstance = buildXMLDataInstance(\"xmlVar\", \"This is a xml variable\",\n                \"org.bonitasoft.engine.data.instance.model.SDataInstance\", \"xmlElement\",\n                getStringForExpression(xmlContent), containerId,\n                containerType);\n        insertDataInstance(dataInstance);\n        // get the data instance by several conditions\n        SDataInstance dataInstanceRes = getDataInstance(dataInstance.getId());\n        assertTrue(dataInstanceRes instanceof SXMLDataInstance);\n        checkXMLDataInstance((SXMLDataInstance) dataInstanceRes, \"xmlVar\", \"This is a xml variable\", false,\n                String.class.getName(), xmlContent, containerId,\n                containerType, \"org.bonitasoft.engine.data.instance.model.SDataInstance\", \"xmlElement\");\n        final String updatedContent = buildSimpleXML2();\n        updateDataInstance(dataInstanceRes.getName(), dataInstanceRes.getContainerId(),\n                dataInstanceRes.getContainerType(),\n                \"This is a xml variable after update\", updatedContent);\n        dataInstanceRes = getDataInstance(dataInstance.getId());\n        checkXMLDataInstance((SXMLDataInstance) dataInstanceRes, \"xmlVar\", \"This is a xml variable after update\", false,\n                String.class.getName(),\n                updatedContent, containerId, containerType, \"org.bonitasoft.engine.data.instance.model.SDataInstance\",\n                \"xmlElement\");\n        deleteDataInstance(dataInstanceRes);\n    }\n\n    @Test(expected = SDataInstanceNotFoundException.class)\n    public void testDeleteXMLData() throws Exception {\n        final String xmlContent = buildSimpleXML1();\n\n        final long containerId = 15;\n        final String containerType = \"ActivityInstance\";\n\n        final SDataInstance dataInstance = buildXMLDataInstance(\"xmlVar\", \"This is a xml variable\",\n                \"org.bonitasoft.engine.data.instance.model.SDataInstance\", \"xmlElement\",\n                getStringForExpression(xmlContent), containerId,\n                containerType);\n        insertDataInstance(dataInstance);\n\n        // get the data instance by several conditions\n        final SDataInstance dataInstanceRes = getDataInstance(dataInstance.getId());\n        assertTrue(dataInstanceRes instanceof SXMLDataInstance);\n        checkXMLDataInstance((SXMLDataInstance) dataInstanceRes, \"xmlVar\", \"This is a xml variable\", false,\n                String.class.getName(), xmlContent, containerId,\n                containerType, \"org.bonitasoft.engine.data.instance.model.SDataInstance\", \"xmlElement\");\n\n        deleteDataInstance(dataInstanceRes);\n\n        getDataInstance(dataInstance.getId());\n    }\n\n    @Test\n    public void testRetrieveXMLDataByNameAndContainer() throws Exception {\n        final String xmlContent = buildSimpleXML1();\n\n        final long containerId = 16;\n        final String containerType = \"ActivityInstance\";\n\n        final SDataInstance dataInstance = buildXMLDataInstance(\"xmlVar\", \"This is a xml variable\",\n                \"org.bonitasoft.engine.data.instance.model.SDataInstance\", \"xmlElement\",\n                getStringForExpression(xmlContent), containerId,\n                containerType);\n        insertDataInstance(dataInstance);\n\n        // get the data instance by several conditions\n        final SDataInstance dataInstanceRes = getDataInstanceByNameAndContainer(\"xmlVar\", containerId, containerType);\n        assertTrue(dataInstanceRes instanceof SXMLDataInstance);\n        checkXMLDataInstance((SXMLDataInstance) dataInstanceRes, \"xmlVar\", \"This is a xml variable\", false,\n                String.class.getName(), xmlContent, containerId,\n                containerType, \"org.bonitasoft.engine.data.instance.model.SDataInstance\", \"xmlElement\");\n\n        deleteDataInstance(dataInstanceRes);\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/data/model/Address.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.model;\n\nimport java.io.Serializable;\n\npublic class Address implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    private long id;\n    private String street;\n    private String city;\n    private int zipCode;\n\n    public Address(final long id, final String street, final String city, final int zipCode) {\n        super();\n        this.id = id;\n        this.street = street;\n        this.city = city;\n        this.zipCode = zipCode;\n    }\n\n    public long getId() {\n        return id;\n    }\n\n    public String getStreet() {\n        return street;\n    }\n\n    public String getCity() {\n        return city;\n    }\n\n    public int getZipCode() {\n        return zipCode;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/data/model/Employee.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.model;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class Employee extends LightEmployee implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    private List<Address> addresses;\n\n    public Employee(final long id, final String firstName, final String lastName, final int age) {\n        super(id, firstName, lastName, age);\n    }\n\n    public List<Address> getAddresses() {\n        return addresses;\n    }\n\n    public void addAddress(final Address address) {\n        if (this.addresses == null) {\n            this.addresses = new ArrayList<Address>();\n        }\n        this.addresses.add(address);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/data/model/LightEmployee.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.model;\n\npublic class LightEmployee {\n\n    private long id;\n    private String firstName;\n    private String lastName;\n    private int age;\n\n    public LightEmployee(final long id, final String firstName, final String lastName, final int age) {\n        super();\n        this.id = id;\n        this.firstName = firstName;\n        this.lastName = lastName;\n        this.age = age;\n    }\n\n    public long getId() {\n        return id;\n    }\n\n    public String getFirstName() {\n        return firstName;\n    }\n\n    public String getLastName() {\n        return lastName;\n    }\n\n    public int getAge() {\n        return age;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/dependency/DependencyServiceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.fail;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.CommonBPMServicesTest;\nimport org.bonitasoft.engine.dependency.model.AbstractSDependency;\nimport org.bonitasoft.engine.dependency.model.ScopeType;\nimport org.junit.Test;\n\n/**\n * @author Charles Souillard\n */\npublic class DependencyServiceIT extends CommonBPMServicesTest {\n\n    private static DependencyService dependencyService;\n\n    public DependencyServiceIT() {\n        dependencyService = getServiceAccessor().getDependencyService();\n    }\n\n    private final String defaultName = \"abc\";\n\n    private final String defaultFileName = \"dfv.cu\";\n\n    private final byte[] defaultValue = new byte[] { 12, 33 };\n\n    @Test\n    public void testLifeCycle() throws Exception {\n        getTransactionService().begin();\n\n        AbstractSDependency mappedDependency = dependencyService.createMappedDependency(defaultName, defaultValue,\n                defaultFileName, 2L, ScopeType.PROCESS);\n\n        List<Long> dependencyIds = dependencyService.getDependencyIds(2L, ScopeType.PROCESS, 0, 1);\n\n        assertThat(mappedDependency.getFileName()).isEqualTo(defaultFileName);\n        assertThat(mappedDependency.getName()).isEqualTo(\"2_\" + defaultName);\n        assertThat(mappedDependency.getValue()).isEqualTo(defaultValue);\n        assertThat(dependencyIds.get(0)).isEqualTo(mappedDependency.getId());\n\n        dependencyService.deleteDependencies(2L, ScopeType.PROCESS);\n\n        try {\n            dependencyService.getDependency(mappedDependency.getId());\n            fail(\"dependency with id: \" + mappedDependency.getId() + \" must not be found!\");\n        } catch (final SDependencyNotFoundException e) {\n            // OK\n        }\n\n        getTransactionService().complete();\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/expression/ExpressionServiceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.fail;\n\nimport java.io.Serializable;\nimport java.math.BigDecimal;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.Callable;\n\nimport org.bonitasoft.engine.bpm.CommonBPMServicesTest;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.cache.ehcache.EhCacheCacheService;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.data.ParentContainerResolverImpl;\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\nimport org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilder;\nimport org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilderFactory;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceService;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SDataInstanceBuilder;\nimport org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.impl.GroovyScriptExpressionExecutorCacheStrategy;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.expression.model.builder.SExpressionBuilder;\nimport org.bonitasoft.engine.expression.model.builder.SExpressionBuilderFactory;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Zhao na\n */\npublic class ExpressionServiceIT extends CommonBPMServicesTest {\n\n    private static final long DEFINITION_ID_VALUE = 123L;\n    private static final Map<Integer, Object> EMPTY_RESOLVED_EXPRESSIONS = Collections.emptyMap();\n\n    private static DataInstanceService dataInstanceService;\n\n    private static ExpressionService expressionService;\n\n    private static EhCacheCacheService cacheService;\n    private ParentContainerResolverImpl containerResolver;\n\n    @Before\n    public void setup() {\n        expressionService = getServiceAccessor().getExpressionService();\n        dataInstanceService = getServiceAccessor().getDataInstanceService();\n        containerResolver = (ParentContainerResolverImpl) getServiceAccessor().getParentContainerResolver();\n        containerResolver.setAllowUnknownContainer(true);\n        cacheService = (EhCacheCacheService) getServiceAccessor().getCacheService();\n        if (cacheService.isStopped()) {\n            try {\n                cacheService.start();\n            } catch (final SBonitaException e) {\n                throw new RuntimeException(e);\n            }\n        }\n    }\n\n    @After\n    public void tearDown() {\n        try {\n            cacheService.stop();\n            cacheService.start();\n        } catch (final SBonitaException e) {\n            throw new RuntimeException(e);\n        }\n        containerResolver.setAllowUnknownContainer(false);\n    }\n\n    private synchronized ExpressionService getExpressionService() {\n        return expressionService;\n    }\n\n    private Object evaluate(final SExpression expression, final Map<String, Object> dependencyValues,\n            final Map<Integer, Object> resolvedExpressions)\n            throws Exception {\n        return getTransactionService().executeInTransaction(\n                () -> expressionService.evaluate(expression, dependencyValues, resolvedExpressions,\n                        ContainerState.ACTIVE));\n    }\n\n    @Test\n    public void evaluateStrConstant() throws Exception {\n        // string\n        final String strContent = \"aabb*&^%$#@()!~><?|}{+_0123456789\";// \"''\" or\"/\"/\"\"\n        final SExpression strExpr = buildExpression(strContent, SExpression.TYPE_CONSTANT, String.class.getName(), null,\n                null);\n        evaluateAndCheckResult(strExpr, strContent, EMPTY_RESOLVED_EXPRESSIONS);\n    }\n\n    @Test(expected = SInvalidExpressionException.class)\n    public void evaluateStrEmptyConstant() throws Exception {\n        // string\n        final String strContent = \" \";\n        final SExpression strExpr = buildExpression(strContent, SExpression.TYPE_CONSTANT, String.class.getName(), null,\n                null);\n        evaluateAndCheckResult(strExpr, strContent, EMPTY_RESOLVED_EXPRESSIONS);\n    }\n\n    @Test(expected = SExpressionEvaluationException.class)\n    public void evaluateWrongTypeIntegerExpression() throws Exception {\n        SExpression dataExpr = null;\n        final long containerId = 987456L;\n        final String containerType = \"process\";\n        final Map<String, Object> dependencyValues = new HashMap<>();\n        dependencyValues.put(\"containerId\", containerId);\n        dependencyValues.put(\"containerType\", containerType);\n        try {\n            final String dataName = \"abcd\";\n            final SDataInstance dataInstance = buildDataInstance(dataName, String.class.getName(), \"data_description\",\n                    \"'some data initial value'\",\n                    containerId, containerType, false);\n            insertDataInstance(dataInstance);\n            dataExpr = buildExpression(dataName, SExpression.TYPE_VARIABLE, Integer.class.getName(), null, null);\n        } catch (final Exception e) {\n            fail(\"Unexpected exception \" + e.getMessage());\n        }\n        evaluate(dataExpr, dependencyValues, EMPTY_RESOLVED_EXPRESSIONS);\n    }\n\n    @Test(expected = SExpressionEvaluationException.class)\n    public void evaluateWrongTypeGroovyExpression() throws Exception {\n        final String strContent = \"return new ArrayList();\";\n        final SExpression strExpr = buildExpression(strContent, SExpression.TYPE_READ_ONLY_SCRIPT,\n                Long.class.getName(), SExpression.GROOVY, null);\n        evaluate(strExpr, EMPTY_RESOLVED_EXPRESSIONS);\n    }\n\n    @Test\n    public void evaluateCorrectSuperTypeGroovyExpression() throws Exception {\n        final String strContent = \"return new ArrayList();\";\n        final SExpression strExpr = buildExpression(strContent, SExpression.TYPE_READ_ONLY_SCRIPT, List.class.getName(),\n                SExpression.GROOVY, null);\n        evaluate(strExpr, EMPTY_RESOLVED_EXPRESSIONS);\n    }\n\n    @Test\n    public void checkGroovyScriptStrategyUsesCache() throws Exception {\n        //given\n        final String strContent = \"return \\\"junit test checkGroovyScriptStrategyUsesCache\\\"\";\n        final SExpression strExpr = buildExpression(strContent, SExpression.TYPE_READ_ONLY_SCRIPT,\n                String.class.getName(), SExpression.GROOVY, null);\n        final String cacheKey = GroovyScriptExpressionExecutorCacheStrategy.SCRIPT_KEY + strContent.hashCode();\n        assertThat(cacheService.get(GroovyScriptExpressionExecutorCacheStrategy.GROOVY_SCRIPT_CACHE_NAME, cacheKey))\n                .as(\"should not contains key\").isNull();\n\n        //when\n        evaluate(strExpr, EMPTY_RESOLVED_EXPRESSIONS);\n\n        //then\n        assertThat(cacheService.get(GroovyScriptExpressionExecutorCacheStrategy.GROOVY_SCRIPT_CACHE_NAME, cacheKey))\n                .as(\"should contains key\").isNotNull();\n    }\n\n    @Test\n    @SuppressWarnings(\"unchecked\")\n    public void evaluateListExpression() throws Exception {\n        final String strConstant = \"abcd\";\n        final String dataName = \"myLocalVariable\";\n        final String dataValue = \"ManuDataValue\";\n        final long containerId = 1498674654675968768L;\n        final String containerType = \"localEvaluationContext\";\n        insertDataInstance(buildDataInstance(dataName, String.class.getName(), \"Some dummy data description\",\n                \"'\" + dataValue + \"'\", containerId,\n                containerType, false));\n        final SExpression exprConstant = buildExpression(strConstant, SExpression.TYPE_CONSTANT, String.class.getName(),\n                null, null);\n        final SExpression exprData = buildExpression(dataName, SExpression.TYPE_VARIABLE, String.class.getName(), null,\n                null);\n        final SExpression expr = buildExpression(null, SExpression.TYPE_LIST, List.class.getName(), null,\n                Arrays.asList(exprConstant, exprData));\n        final Map<String, Object> ctx = new HashMap<>(2);\n        ctx.put(\"containerId\", containerId);\n        ctx.put(\"containerType\", containerType);\n        final Map<Integer, Object> evaluatedExpressions = new HashMap<>(2);\n        evaluatedExpressions.put(exprConstant.getDiscriminant(), evaluate(exprConstant, EMPTY_RESOLVED_EXPRESSIONS));\n        evaluatedExpressions.put(exprData.getDiscriminant(), evaluate(exprData, ctx, EMPTY_RESOLVED_EXPRESSIONS));\n        final Object res = evaluate(expr, null, evaluatedExpressions);\n        assertEquals(strConstant, ((List<Serializable>) res).get(0));\n        assertEquals(dataValue, ((List<Serializable>) res).get(1));\n    }\n\n    @Test\n    public void evaluateDoubleConstant() throws Exception {\n        // Double\n        final String doubleContent = \"10.3\";\n        final SExpression doubExpr = buildExpression(doubleContent, SExpression.TYPE_CONSTANT, Double.class.getName(),\n                null, null);\n        final Object expressionResult = evaluate(doubExpr, EMPTY_RESOLVED_EXPRESSIONS);\n        assertEquals(10.3, expressionResult);\n    }\n\n    @Test(expected = SInvalidExpressionException.class)\n    public void evaluateDoubleEmptyConstant() throws Exception {\n        final String strContent = \"\";\n        final SExpression strExpr = buildExpression(strContent, SExpression.TYPE_CONSTANT, Double.class.getName(), null,\n                null);\n        evaluateAndCheckResult(strExpr, strContent, EMPTY_RESOLVED_EXPRESSIONS);\n    }\n\n    @Test\n    public void evaluateDoubleTypeNoDecimal() throws Exception {\n        // Double\n        final String doubleContent = \"10.\";\n        final SExpression doubExpr = buildExpression(doubleContent, SExpression.TYPE_CONSTANT, Double.class.getName(),\n                null, null);\n        final Object expressionResult = evaluate(doubExpr, EMPTY_RESOLVED_EXPRESSIONS);\n        assertEquals(10.0, expressionResult);\n    }\n\n    @Test\n    public void evaluateDoubleTypeNoRadical() throws Exception {\n        // Double\n        final String doubleContent = \".3\";\n        final SExpression doubExpr = buildExpression(doubleContent, SExpression.TYPE_CONSTANT, Double.class.getName(),\n                null, null);\n        final Object expressionResult = evaluate(doubExpr, EMPTY_RESOLVED_EXPRESSIONS);\n        assertEquals(0.3, expressionResult);\n    }\n\n    @Test\n    public void evaluateLongConstant() throws Exception {\n        // Long\n        final String longContent = \"123000000\";\n        final SExpression longExpr = buildExpression(longContent, SExpression.TYPE_CONSTANT, Long.class.getName(), null,\n                null);\n        final Object expressionResult = evaluate(longExpr, EMPTY_RESOLVED_EXPRESSIONS);\n        assertEquals(123000000L, expressionResult);\n    }\n\n    @Test(expected = SInvalidExpressionException.class)\n    public void evaluateLongEmptyConstant() throws Exception {\n        final String longContent = \"\";\n        final SExpression longExpr = buildExpression(longContent, SExpression.TYPE_CONSTANT, Long.class.getName(), null,\n                null);\n        evaluate(longExpr, EMPTY_RESOLVED_EXPRESSIONS);\n    }\n\n    @Test(expected = SExpressionDependencyMissingException.class)\n    public void evaluatePatternExpressionWithException() throws Exception {\n        final String expressionContent = \"Expression with missing dependencies\";\n        final List<SExpression> dependencies = new ArrayList<>(1);\n        dependencies.add(buildExpression(\"value_will_be_missing\", SExpression.TYPE_CONSTANT, Double.class.getName(),\n                null, null));\n        final SExpression simplePatternExpression = newPatternExpression(expressionContent, dependencies);\n        evaluate(simplePatternExpression, EMPTY_RESOLVED_EXPRESSIONS);\n    }\n\n    @Test\n    public void evaluatePatternExpressionWithoutDependencies() throws Exception {\n        final String expressionContent = \"Default expression with no dependencies\";\n        final SExpression simplePatternExpression = newPatternExpression(expressionContent, null);\n        final Object expressionResult = evaluate(simplePatternExpression, EMPTY_RESOLVED_EXPRESSIONS);\n        assertEquals(expressionContent, expressionResult);\n    }\n\n    @Test(expected = SInvalidExpressionException.class)\n    public void evaluateEmptyPatternExpression() throws Exception {\n        final long processInstanceId = 123456L;\n        final String data1Content = \"emptyPatternData\";\n        insertDataInstance(buildDataInstance(data1Content, String.class.getName(), \"emptyPatternData_description\",\n                \"'EXPRESSION'\", processInstanceId,\n                \"process\", false));\n        final SExpression data1Expr = buildExpression(data1Content, SExpression.TYPE_VARIABLE, String.class.getName(),\n                null, null);\n\n        final String constant2Content = \"123456789\";\n        final SExpression intExpr = buildExpression(constant2Content, SExpression.TYPE_CONSTANT,\n                Integer.class.getName(), null, null);\n\n        final List<SExpression> dependencyExpresssions = new ArrayList<>(2);\n        dependencyExpresssions.add(data1Expr);\n        dependencyExpresssions.add(intExpr);\n\n        final String expressionContent = \" \";\n        final SExpression patternExpression = newPatternExpression(expressionContent, dependencyExpresssions);\n        final Map<String, Object> expressionEvalContext = new HashMap<>(2);\n        expressionEvalContext.put(data1Content, \"EXPRESSION\");\n        expressionEvalContext.put(constant2Content, 2);\n        evaluate(patternExpression, expressionEvalContext, EMPTY_RESOLVED_EXPRESSIONS);\n    }\n\n    @Test\n    public void evaluateStrGroovy() throws Exception {\n        // String\n        final String groovyContentStr = \"'a'+1+'b'\";\n        final SExpression groovyContentExpr = buildExpression(groovyContentStr, SExpression.TYPE_READ_ONLY_SCRIPT,\n                String.class.getName(), SExpression.GROOVY,\n                null);\n        final Object expressionResult = evaluate(groovyContentExpr, EMPTY_RESOLVED_EXPRESSIONS);\n        assertEquals(\"a1b\", expressionResult);\n    }\n\n    @Test\n    public void evaluateNumericGroovy() throws Exception {\n        // numeric\n        final String groovyContentNum = \"((1+2)*2)/3\";\n        final SExpression groovyNumExpr = buildExpression(groovyContentNum, SExpression.TYPE_READ_ONLY_SCRIPT,\n                BigDecimal.class.getName(), SExpression.GROOVY,\n                null);\n        final Object expressionResult = evaluate(groovyNumExpr, EMPTY_RESOLVED_EXPRESSIONS);\n        assertEquals(new BigDecimal(2), expressionResult);\n    }\n\n    @Test(expected = SInvalidExpressionException.class)\n    public void evaluateStrEmptyGroovy() throws Exception {\n        // String\n        final String groovyContentStr = \" \";\n        final SExpression groovyContentExpr = buildExpression(groovyContentStr, SExpression.TYPE_READ_ONLY_SCRIPT,\n                String.class.getName(), SExpression.GROOVY,\n                null);\n        final Object expressionResult = evaluate(groovyContentExpr, EMPTY_RESOLVED_EXPRESSIONS);\n        assertEquals(\"a1b\", expressionResult);\n    }\n\n    @Test(expected = SExpressionTypeUnknownException.class)\n    public void evaluateUnkonwnExpressionType() throws Exception {\n        final String expressionContent = \"str\";\n        final SExpression expression = buildExpression(expressionContent, \"TYPE_UNKNOWN\", Boolean.class.getName(), null,\n                null);\n        final Map<String, Object> map = new HashMap<>();\n        map.put(expressionContent, \"abc\");\n        evaluate(expression, map, EMPTY_RESOLVED_EXPRESSIONS);\n    }\n\n    @Test(expected = SExpressionEvaluationException.class)\n    public void evaluateUnknownProperty() throws Exception {\n        // numeric\n        final String groovyContentNum = \"variable1\";\n        final SExpression groovyNumExpr = buildExpression(groovyContentNum, SExpression.TYPE_READ_ONLY_SCRIPT,\n                BigDecimal.class.getName(), SExpression.GROOVY,\n                null);\n        evaluate(groovyNumExpr, EMPTY_RESOLVED_EXPRESSIONS);\n    }\n\n    private SExpression newPatternExpression(final String content, final List<SExpression> dependencies) {\n        return buildExpression(content, SExpression.TYPE_PATTERN, String.class.getName(), null, dependencies);\n    }\n\n    private void initializeBuilder(final SDataDefinitionBuilder dataDefinitionBuilder, final String description,\n            final String content,\n            final boolean isTransient, final String defaultValueReturnType) {\n        SExpression expression = null;\n        if (content != null) {\n            // create expression\n            final SExpressionBuilder expreBuilder = BuilderFactory.get(SExpressionBuilderFactory.class)\n                    .createNewInstance();\n            // this discrimination'll be changed.\n            expreBuilder.setContent(content).setExpressionType(SExpression.TYPE_READ_ONLY_SCRIPT)\n                    .setInterpreter(SExpression.GROOVY)\n                    .setReturnType(defaultValueReturnType);\n            try {\n                expression = expreBuilder.done();\n            } catch (final SInvalidExpressionException e) {\n                throw new IllegalArgumentException(e);\n            }\n        }\n\n        dataDefinitionBuilder.setDescription(description);\n        dataDefinitionBuilder.setTransient(isTransient);\n        dataDefinitionBuilder.setDefaultValue(expression);\n    }\n\n    private SDataInstance buildDataInstance(final String instanceName, final String className, final String description,\n            final String content,\n            final long containerId, final String containerType, final boolean isTransient) throws Exception {\n        // create definition\n        final SDataDefinitionBuilder dataDefinitionBuilder = BuilderFactory.get(SDataDefinitionBuilderFactory.class)\n                .createNewInstance(instanceName, className);\n        initializeBuilder(dataDefinitionBuilder, description, content, isTransient, className);\n        final SDataDefinition dataDefinition = dataDefinitionBuilder.done();\n        // create datainstance\n        final SDataInstanceBuilder dataInstanceBuilder = SDataInstanceBuilder.createNewInstance(dataDefinition);\n        evaluateDefaultValueOf(dataDefinition, dataInstanceBuilder);\n        return dataInstanceBuilder.setContainerId(containerId).setContainerType(containerType).done();\n    }\n\n    private void evaluateDefaultValueOf(final SDataDefinition dataDefinition,\n            final SDataInstanceBuilder dataInstanceBuilder) throws Exception {\n        final SExpression expression = dataDefinition.getDefaultValueExpression();\n        if (expression != null) {\n            dataInstanceBuilder.setValue((Serializable) evaluate(expression, EMPTY_RESOLVED_EXPRESSIONS));\n        }\n    }\n\n    private void insertDataInstance(final SDataInstance dataInstance) throws Exception {\n        getTransactionService().executeInTransaction((Callable<Void>) () -> {\n            dataInstanceService.createDataInstance(dataInstance);\n            return null;\n        });\n    }\n\n    private void verifyEvaluateVariable(final String expressionContent, final Class<?> classType,\n            final String description, final Long containerId,\n            final String containerType, final String value, final Boolean isTransient, final Serializable hopeValue)\n            throws Exception {\n        // create data\n        final SDataInstance dataInstance = buildDataInstance(expressionContent, classType.getName(), description, value,\n                containerId, containerType,\n                isTransient);\n        // insert data\n        insertDataInstance(dataInstance);\n        // definite expression\n        final SExpression variableExpr = buildExpression(expressionContent, SExpression.TYPE_VARIABLE,\n                classType.getName(), null, null);\n        final Map<String, Object> dependencyValues = new HashMap<>();\n        dependencyValues.put(\"containerId\", containerId);\n        dependencyValues.put(\"containerType\", containerType);\n\n        final Object resultValue = evaluate(variableExpr, dependencyValues, EMPTY_RESOLVED_EXPRESSIONS);\n        if (resultValue != null) {\n            assertEquals(classType, resultValue.getClass());\n        }\n        assertEquals(hopeValue, resultValue);\n    }\n\n    @Test\n    public void evaluateShortTextVariableDB() throws Exception {\n        verifyEvaluateVariable(\"userName\", String.class, \"123456qwewr\", 11L, \"process\", \"'Edward'\", false, \"Edward\");\n    }\n\n    @Test\n    public void evaluateShortTextVariableDBWithNoDefaultValue() throws Exception {\n        verifyEvaluateVariable(\"nullableData\", String.class, \"data that is possibly null\", 21L, \"process\", null, false,\n                null);\n    }\n\n    @Test\n    public void evaluateShortTextVariableCache() throws Exception {\n        verifyEvaluateVariable(\"userName\", String.class, \"123456qwewr\", 12L, \"process\", \"'Edward'\", true, \"Edward\");\n    }\n\n    @Test\n    public void evaluateNumericVariableDB() throws Exception {\n        verifyEvaluateVariable(\"pageSum\", Integer.class, \"evaluate Numeric Variable DB\", 12L, \"task\", \"100\", false,\n                100);\n    }\n\n    @Test\n    public void evaluateNumericVariableCache() throws Exception {\n        verifyEvaluateVariable(\"pageSum\", Integer.class, \"evaluate Numeric Variabl eCache\", 13L, \"task\", \"100\", true,\n                100);\n    }\n\n    @Test(expected = SInvalidExpressionException.class)\n    public void evaluateShortTextEmptyVariableDB() throws Exception {\n        verifyEvaluateVariable(\"\", String.class, \"evaluate ShortText Empty Variable DB\", 23456L, \"container_16\",\n                \"'Edward'\", false, \"Edward\");\n    }\n\n    @Test(expected = SInvalidExpressionException.class)\n    public void evaluateShortTextEmptyVariableCache() throws Exception {\n        verifyEvaluateVariable(\"  \", String.class, \"evaluate ShortText Empty Variable Cache\", 34567L, \"container_17\",\n                \"'Edward'\", true, \"Edward\");\n    }\n\n    private SExpression buildExpression(final String content, final String expressionType, final String returnType,\n            final String interpreter,\n            final List<SExpression> dependencies) {\n        final SExpressionBuilder eb = BuilderFactory.get(SExpressionBuilderFactory.class).createNewInstance();\n        eb.setName(content);\n        eb.setContent(content);\n        eb.setExpressionType(expressionType);\n        eb.setInterpreter(interpreter);\n        eb.setReturnType(returnType);\n        eb.setDependencies(dependencies);\n        try {\n            return eb.done();\n        } catch (final SInvalidExpressionException e) {\n            throw new IllegalArgumentException(e);\n        }\n    }\n\n    private Object evaluate(final SExpression expression, final Map<Integer, Object> resolvedExpressions)\n            throws Exception {\n        return getTransactionService().executeInTransaction(() -> {\n            final Map<String, Object> dependencyValues = new HashMap<>();\n            dependencyValues.put(ExpressionExecutorStrategy.DEFINITION_ID, DEFINITION_ID_VALUE);\n            return getExpressionService().evaluate(expression, dependencyValues, resolvedExpressions,\n                    ContainerState.ACTIVE);\n        });\n    }\n\n    private void evaluateAndCheckResult(final SExpression expression, final Object expectedValue,\n            final Map<Integer, Object> resolvedExpression)\n            throws Exception {\n        final Object expressionResult = evaluate(expression, resolvedExpression);\n        assertEquals(expectedValue, expressionResult);\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/identity/IdentityServiceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.*;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.bpm.CommonBPMServicesTest;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.identity.model.SContactInfo;\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.identity.model.SRole;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.identity.model.SUserMembership;\nimport org.bonitasoft.engine.identity.model.builder.SContactInfoUpdateBuilderFactory;\nimport org.bonitasoft.engine.identity.model.builder.SCustomUserInfoDefinitionUpdateBuilderFactory;\nimport org.bonitasoft.engine.identity.model.builder.SGroupUpdateBuilderFactory;\nimport org.bonitasoft.engine.identity.model.builder.SRoleUpdateBuilderFactory;\nimport org.bonitasoft.engine.identity.model.builder.SUserUpdateBuilderFactory;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SearchFields;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.test.util.TestUtil;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Baptiste Mesta\n * @author Elias Ricken de Medeiros\n */\npublic class IdentityServiceIT extends CommonBPMServicesTest {\n\n    private static IdentityService identityService;\n\n    @Before\n    public void before() {\n        identityService = getServiceAccessor().getIdentityService();\n    }\n\n    @After\n    public void tearDown() throws Exception {\n        TestUtil.closeTransactionIfOpen(getTransactionService());\n\n        getTransactionService().begin();\n        deleteUserMemberships();\n        deleteRoles();\n        deleteGroups();\n        deleteUsers();\n        getTransactionService().complete();\n    }\n\n    private void deleteUserMemberships() throws SIdentityException {\n        final List<SUserMembership> memberships = identityService.getUserMemberships(0, 5000);\n        for (final SUserMembership sMembership : memberships) {\n            identityService.deleteUserMembership(sMembership);\n        }\n    }\n\n    @Test\n    public void testAddUser() throws Exception {\n        getTransactionService().begin();\n        final SUser user = SUser.builder().userName(\"john\").password(\"bpm\").build();\n        identityService.createUser(user);\n        getTransactionService().complete();\n\n        getTransactionService().begin();\n        final SUser user2 = identityService.getUserByUserName(\"john\");\n        assertNotNull(\"can't find the user after adding it\", user2);\n        assertEquals(user.getUserName(), user2.getUserName());\n        assertNotSame(user.getPassword(), user2.getPassword());\n        assertEquals(user.getCreationDate(), user2.getCreationDate());\n        // all fields\n        getTransactionService().complete();\n    }\n\n    @Test\n    public void testAddUserWithId() throws Exception {\n        getTransactionService().begin();\n        final SUser user = SUser.builder().userName(\"testAddUserWithId\").password(\"bpm\").build();\n        assertEquals(0, user.getId());\n        final SUser updatedUser = identityService.createUser(user);\n        assertNotSame(\"The identifier must be set after adding a user\", 0, updatedUser.getId());\n        getTransactionService().complete();\n    }\n\n    @Test\n    public void testAddUsersWithoutIds() throws Exception {\n        getTransactionService().begin();\n        final SUser user1 = SUser.builder().userName(\"testAddUsersWithoutIds7\").password(\"bpm\").build();\n        identityService.createUser(user1);\n        getTransactionService().complete();\n\n        getTransactionService().begin();\n        final SUser user2 = SUser.builder().userName(\"testAddUsersWithoutIds2\").password(\"bpm\").build();\n        final SUser user3 = SUser.builder().userName(\"testAddUsersWithoutIds3\").password(\"bpm\").build();\n        final SUser user4 = SUser.builder().userName(\"testAddUsersWithoutIds4\").password(\"bpm\").build();\n        final SUser user5 = SUser.builder().userName(\"testAddUsersWithoutIds5\").password(\"bpm\").build();\n        final SUser user6 = SUser.builder().userName(\"testAddUsersWithoutIds6\").password(\"bpm\").build();\n        identityService.createUser(user2);\n        identityService.createUser(user3);\n        identityService.createUser(user4);\n        identityService.createUser(user5);\n        identityService.createUser(user6);\n        getTransactionService().complete();\n    }\n\n    @Test\n    public void testGetUserByUserName() throws Exception {\n        getTransactionService().begin();\n        final String username = \"myUser\";\n        final SUser user = SUser.builder().userName(username).password(username).build();\n        identityService.createUser(user);\n        final SUser user2 = identityService.getUserByUserName(username);\n        getTransactionService().complete();\n        assertNotNull(\"can't find the user after adding it\", user2);\n        assertEquals(\"Does not retrieved the good user\", username, user2.getUserName());\n    }\n\n    @Test(expected = SIdentityException.class)\n    public void testGetUserByUsernameNotExists() throws Exception {\n        getTransactionService().begin();\n        final String username = \"unexistingError\";\n        identityService.getUserByUserName(username);\n        getTransactionService().complete();\n    }\n\n    @Test\n    public void testGetRoleByName() throws Exception {\n        getTransactionService().begin();\n        final String roleName = \"myRole\";\n        final SRole role = SRole.builder().name(roleName).build();\n        identityService.createRole(role, null, null);\n        final SRole role2 = identityService.getRoleByName(roleName);\n        getTransactionService().complete();\n        assertNotNull(\"can't find the role after adding it\", role2);\n        assertEquals(\"Does not retrieved the good role\", roleName, role2.getName());\n    }\n\n    @Test\n    public void testGetProfileMetadataByName() throws Exception {\n        getTransactionService().begin();\n        final String name = \"MyProfileMetadata\";\n        final SCustomUserInfoDefinition metadata = SCustomUserInfoDefinition.builder().name(name).build();\n        identityService.createCustomUserInfoDefinition(metadata);\n        final SCustomUserInfoDefinition metadata2 = identityService.getCustomUserInfoDefinitionByName(name);\n        assertNotNull(\"can't find the profile metadata after adding it\", metadata2);\n        assertEquals(\"Does not retrieved the good profile metadata\", name, metadata2.getName());\n        getTransactionService().complete();\n    }\n\n    /*\n     * Getters that use objects Ids\n     */\n\n    @Test\n    public void testGetUser() throws Exception {\n        getTransactionService().begin();\n        final SUser.SUserBuilder userBuilder = SUser.builder().userName(\"Seppo\").password(\"kikoo\");\n        final SUser seppo = identityService.createUser(userBuilder.build());\n        final SUser user2 = identityService.getUser(seppo.getId());\n        getTransactionService().complete();\n\n        assertNotNull(\"can't find the user after adding it\", user2);\n        assertEquals(\"Does not retrieved the good user\", seppo.getId(), user2.getId());\n    }\n\n    @Test(expected = SIdentityException.class)\n    public void testGetUnexistingUser() throws Exception {\n        getTransactionService().begin();\n        identityService.getUser(1254863);\n        getTransactionService().complete();\n    }\n\n    @Test\n    public void testGetRole() throws Exception {\n        getTransactionService().begin();\n        final long id = new Date().getTime();\n        final SRole testGetRole = SRole.builder().name(\"testGetRole\").id(id).build();\n        identityService.createRole(testGetRole, null, null);\n        final SRole role2 = identityService.getRole(id);\n        getTransactionService().complete();\n\n        assertNotNull(\"can't find the role after adding it\", role2);\n        assertEquals(\"Does not retrieved the good role\", id, role2.getId());\n    }\n\n    @Test\n    public void testGetGroup() throws Exception {\n        getTransactionService().begin();\n        final long id = new Date().getTime();\n        final SGroup testGetGroup = SGroup.builder().name(\"testGetGroup\").id(id).build();\n        identityService.createGroup(testGetGroup, null, null);\n        getTransactionService().complete();\n\n        getTransactionService().begin();\n        final SGroup group2 = identityService.getGroup(id);\n        getTransactionService().complete();\n\n        assertNotNull(\"can't find the group after adding it\", group2);\n        assertEquals(\"Does not retrieved the good group\", id, group2.getId());\n    }\n\n    @Test\n    public void testGetGroupByPath() throws Exception {\n        getTransactionService().begin();\n        final SGroup group = SGroup.builder().name(\"R&D\").build();\n        identityService.createGroup(group, null, null);\n        final SGroup subGroup = SGroup.builder().parentPath(group.getPath()).name(\"R&D\").build();\n        identityService.createGroup(subGroup, null, null);\n        getTransactionService().complete();\n\n        getTransactionService().begin();\n        SGroup actual = identityService.getGroupByPath(\"R&D\");\n        assertEquals(group, actual);\n        actual = identityService.getGroupByPath(\"/R&D\");\n        assertEquals(group, actual);\n        actual = identityService.getGroupByPath(\"/R&D/R&D\");\n        assertEquals(subGroup, actual);\n        getTransactionService().complete();\n    }\n\n    @Test\n    public void testGetProfileMetadata() throws Exception {\n        getTransactionService().begin();\n        final long id = new Date().getTime();\n        final SCustomUserInfoDefinition testGetProfileMetadata = SCustomUserInfoDefinition.builder()\n                .name(\"testGetProfileMetadata\").id(id).build();\n        identityService.createCustomUserInfoDefinition(testGetProfileMetadata);\n        final SCustomUserInfoDefinition metadata2 = identityService.getCustomUserInfoDefinition(id);\n        getTransactionService().complete();\n\n        assertNotNull(\"can't find the metadata after adding it\", metadata2);\n        assertEquals(\"Does not retrieved the good metadata\", id, metadata2.getId());\n    }\n\n    @Test\n    public void testGetUsers() throws Exception {\n        getTransactionService().begin();\n        final SUser user1 = SUser.builder().userName(\"Akseli\").password(\"kikoo\").build();\n        final long id1 = identityService.createUser(user1).getId();\n        final SUser user2 = SUser.builder().userName(\"Anja\").password(\"kikoo\").build();\n        final long id2 = identityService.createUser(user2).getId();\n\n        final List<SUser> retrievedUsers = identityService.getUsers(Arrays.asList(id1, id2));\n        getTransactionService().complete();\n\n        assertNotNull(\"can't find the users after adding them\", retrievedUsers);\n        assertEquals(\"bad number of retrieved users\", 2, retrievedUsers.size());\n        assertTrue(\"does not contains user 1\",\n                retrievedUsers.get(0).getId() == id1 || retrievedUsers.get(1).getId() == id1);\n        assertTrue(\"does not contains user 2\",\n                retrievedUsers.get(1).getId() == id2 || retrievedUsers.get(0).getId() == id2);\n    }\n\n    @Test\n    public void testGetUsersFromNullListIds() throws Exception {\n        final SUser eetu = SUser.builder().userName(\"Eetu\").password(\"kikoo\").build();\n        final SUser inkeri = SUser.builder().userName(\"Inkeri\").password(\"kikoo\").build();\n        getTransactionService().begin();\n        identityService.createUser(eetu);\n        identityService.createUser(inkeri);\n        final List<SUser> retrievedUsers = identityService.getUsers(null);\n        getTransactionService().complete();\n\n        assertEquals(0, retrievedUsers.size());\n    }\n\n    @Test\n    public void testGetUsersFromEmptyListIds() throws Exception {\n        final SUser lauri = SUser.builder().userName(\"lauri\").password(\"kikoo\").build();\n        final SUser mika = SUser.builder().userName(\"mika\").password(\"kikoo\").build();\n        getTransactionService().begin();\n        identityService.createUser(lauri);\n        identityService.createUser(mika);\n        final List<SUser> retrievedUsers = identityService.getUsers(Collections.emptyList());\n        getTransactionService().complete();\n\n        assertEquals(0, retrievedUsers.size());\n    }\n\n    @Test\n    public void testGetRoles() throws Exception {\n        getTransactionService().begin();\n        final long id1 = new Date().getTime();\n        final SRole role1 = SRole.builder().name(\"testGetRoles1\").id(id1).build();\n        identityService.createRole(role1, null, null);\n        final long id2 = id1 + 1L;\n        final SRole role2 = SRole.builder().name(\"testGetRoles2\").id(id2).build();\n        identityService.createRole(role2, null, null);\n\n        final List<SRole> retrievedUsers = identityService.getRoles(Arrays.asList(new Long[] { id1, id2 }));\n        getTransactionService().complete();\n\n        assertNotNull(\"can't find the roles after adding them\", retrievedUsers);\n        assertEquals(\"bad number of retrieved roles\", 2, retrievedUsers.size());\n        assertTrue(\"does not contains role 1\",\n                retrievedUsers.get(0).getId() == id1 || retrievedUsers.get(1).getId() == id1);\n        assertTrue(\"does not contains role 2\",\n                retrievedUsers.get(1).getId() == id2 || retrievedUsers.get(0).getId() == id2);\n    }\n\n    @Test\n    public void testGetRolesFromNullListids() throws Exception {\n        getTransactionService().begin();\n        final long id1 = new Date().getTime();\n        final SRole role1 = SRole.builder().name(\"testGetRoles1\").id(id1).build();\n        identityService.createRole(role1, null, null);\n        final long id2 = id1 + 1L;\n        final SRole role2 = SRole.builder().name(\"testGetRoles2\").id(id2).build();\n        identityService.createRole(role2, null, null);\n\n        final List<SRole> retrievedUsers = identityService.getRoles(null);\n        getTransactionService().complete();\n\n        assertEquals(0, retrievedUsers.size());\n    }\n\n    @Test\n    public void testGetRolesFromEmptyListIds() throws Exception {\n        getTransactionService().begin();\n        final long id1 = new Date().getTime();\n        final SRole role1 = SRole.builder().name(\"testGetRoles1\").id(id1).build();\n        identityService.createRole(role1, null, null);\n        final long id2 = id1 + 1L;\n        final SRole role2 = SRole.builder().name(\"testGetRoles2\").id(id2).build();\n        identityService.createRole(role2, null, null);\n\n        final List<SRole> retrievedUsers = identityService.getRoles(Collections.emptyList());\n        getTransactionService().complete();\n\n        assertEquals(0, retrievedUsers.size());\n    }\n\n    @Test\n    public void testGetGroups() throws Exception {\n        getTransactionService().begin();\n        final long id1 = new Date().getTime();\n        final SGroup group1 = SGroup.builder().name(\"testGetGroups1\").id(id1).build();\n        identityService.createGroup(group1, null, null);\n        final long id2 = id1 + 1L;\n        final SGroup group2 = SGroup.builder().name(\"testGetGroups2\").id(id2).build();\n        identityService.createGroup(group2, null, null);\n\n        final List<SGroup> retrievedGroups = identityService.getGroups(Arrays.asList(new Long[] { id1, id2 }));\n        getTransactionService().complete();\n\n        assertNotNull(\"can't find the groups after adding them\", retrievedGroups);\n        assertEquals(\"bad number of retrieved groups\", 2, retrievedGroups.size());\n        assertTrue(\"does not contains group 1\",\n                retrievedGroups.get(0).getId() == id1 || retrievedGroups.get(1).getId() == id1);\n        assertTrue(\"does not contains group 2\",\n                retrievedGroups.get(1).getId() == id2 || retrievedGroups.get(0).getId() == id2);\n    }\n\n    @Test\n    public void testGetGroupsFromNullListIds() throws Exception {\n        getTransactionService().begin();\n        final long id1 = new Date().getTime();\n        final SGroup group1 = SGroup.builder().name(\"testGetGroups1\").id(id1).build();\n        identityService.createGroup(group1, null, null);\n        final long id2 = id1 + 1L;\n        final SGroup group2 = SGroup.builder().name(\"testGetGroups2\").id(id2).build();\n        identityService.createGroup(group2, null, null);\n\n        final List<SGroup> retrievedGroups = identityService.getGroups(null);\n        getTransactionService().complete();\n\n        assertEquals(0, retrievedGroups.size());\n    }\n\n    @Test\n    public void testGetGroupsFromEmptyListIds() throws Exception {\n        getTransactionService().begin();\n        final long id1 = new Date().getTime();\n        final SGroup group1 = SGroup.builder().name(\"testGetGroups1\").id(id1).build();\n        identityService.createGroup(group1, null, null);\n        final long id2 = id1 + 1L;\n        final SGroup group2 = SGroup.builder().name(\"testGetGroups2\").id(id2).build();\n        identityService.createGroup(group2, null, null);\n\n        final List<SGroup> retrievedGroups = identityService.getGroups(Collections.emptyList());\n        getTransactionService().complete();\n\n        assertEquals(0, retrievedGroups.size());\n    }\n\n    @Test\n    public void testGetRolesPaginated() throws Exception {\n        getTransactionService().begin();\n        long id;\n        SRole role;\n        final long time = new Date().getTime();\n        for (int i = 0; i < 30; i++) {\n            id = time + i;\n            role = SRole.builder().name(\"testGetRolesPaginated\" + i).id(id).build();\n            identityService.createRole(role, null, null);\n        }\n\n        List<SRole> retrievedRoles = identityService.getRoles(5, 5);\n        assertNotNull(\"can't find the roles after adding them\", retrievedRoles);\n        assertEquals(\"bad number of retrieved roles\", 5, retrievedRoles.size());\n\n        retrievedRoles = identityService.getRoles(0, 20);\n        getTransactionService().complete();\n        assertNotNull(\"can't find the roles after adding them\", retrievedRoles);\n        assertEquals(\"bad number of retrieved roles\", 20, retrievedRoles.size());\n    }\n\n    @Test\n    public void testGetRolesOrderByName() throws Exception {\n        getTransactionService().begin();\n        createRoles(10, \"testGetRolesOrderByName_name\", \"testGetRolesOrderByName_label\");\n        List<SRole> retrievedRoles = identityService.getRoles(5, 5, SRole.NAME, OrderByType.DESC);\n        assertNotNull(\"can't find the roles after adding them\", retrievedRoles);\n        assertEquals(\"bad number of retrieved roles\", 5, retrievedRoles.size());\n        assertTrue(\"not in descending order\",\n                retrievedRoles.get(1).getName().compareTo(retrievedRoles.get(2).getName()) > 0);\n\n        retrievedRoles = identityService.getRoles(5, 5, SRole.NAME, OrderByType.ASC);\n        getTransactionService().complete();\n        assertNotNull(\"can't find the roles after adding them\", retrievedRoles);\n        assertEquals(\"bad number of retrieved roles\", 5, retrievedRoles.size());\n        assertTrue(\n                \"not in asc order: first= \" + retrievedRoles.get(0).getName() + \"  second = \"\n                        + retrievedRoles.get(3).getName(),\n                retrievedRoles.get(0)\n                        .getName().compareTo(retrievedRoles.get(3).getName()) < 0);\n    }\n\n    @Test\n    public void testGetRolesOrderByLabel() throws Exception {\n        getTransactionService().begin();\n        deleteUsers();\n        deleteRoles();\n        createRoles(10, \"testGetRolesOrderByLabel_name\", \"testGetRolesOrderByLabel_label\");\n        List<SRole> retrievedRoles = identityService.getRoles(5, 5, SRole.DISPLAY_NAME, OrderByType.DESC);\n        assertNotNull(\"can't find the roles after adding them\", retrievedRoles);\n        assertEquals(\"bad number of retrieved roles\", 5, retrievedRoles.size());\n        assertTrue(\"not in descending order\",\n                retrievedRoles.get(1).getDisplayName().compareTo(retrievedRoles.get(2).getDisplayName()) > 0);\n\n        retrievedRoles = identityService.getRoles(5, 5, SRole.DISPLAY_NAME, OrderByType.ASC);\n        getTransactionService().complete();\n        assertNotNull(\"can't find the roles after adding them\", retrievedRoles);\n        assertEquals(\"bad number of retrieved roles\", 5, retrievedRoles.size());\n        assertTrue(\"not in asc order: first= \",\n                retrievedRoles.get(0).getDisplayName().compareTo(retrievedRoles.get(3).getDisplayName()) < 0);\n    }\n\n    private List<SRole> createRoles(final int i, final String baseName, final String baseLabel)\n            throws SIdentityException {\n        final ArrayList<SRole> results = new ArrayList<>();\n        for (int j = 0; j < i; j++) {\n            final SRole role = SRole.builder().name(baseName + j).displayName(baseLabel + j).build();\n            identityService.createRole(role, null, null);\n            results.add(role);\n        }\n        return results;\n    }\n\n    private void deleteRoles() throws SIdentityException {\n        final List<SRole> roles = identityService.getRoles(0, 5000);\n        for (final SRole sRole : roles) {\n            identityService.deleteRole(sRole);\n        }\n    }\n\n    @Test\n    public void testGetNumberOfRoles() throws Exception {\n        getTransactionService().begin();\n        final long numberOfRoles = identityService.getNumberOfRoles();\n        long id;\n        SRole role;\n        final long time = new Date().getTime();\n        for (int i = 0; i < 5; i++) {\n            id = time + i;\n            role = SRole.builder().name(\"testGetNumberOfRoles\" + i).id(id).build();\n            identityService.createRole(role, null, null);\n        }\n        assertEquals(\"bad count of roles\", numberOfRoles + 5, identityService.getNumberOfRoles());\n        getTransactionService().complete();\n    }\n\n    /*\n     * Method that helps to retrieve groups\n     */\n    @Test\n    public void testGetGroupsPaginated() throws Exception {\n        getTransactionService().begin();\n        long id;\n        SGroup group;\n        final long time = new Date().getTime();\n        for (int i = 0; i < 30; i++) {\n            id = time + i;\n            group = SGroup.builder().name(\"testGetGroupsPaginated\" + i).id(id).build();\n            identityService.createGroup(group, null, null);\n        }\n\n        List<SGroup> retrievedGroups = identityService.getGroups(5, 5);\n        assertNotNull(\"can't find the groups after adding them\", retrievedGroups);\n        assertEquals(\"bad number of retrieved groups\", 5, retrievedGroups.size());\n\n        retrievedGroups = identityService.getGroups(0, 20);\n        getTransactionService().complete();\n        assertNotNull(\"can't find the groups after adding them\", retrievedGroups);\n        assertEquals(\"bad number of retrieved groups\", 20, retrievedGroups.size());\n    }\n\n    @Test\n    public void testGetGroupsOrderByName() throws Exception {\n        getTransactionService().begin();\n        deleteGroups();\n        createGroups(10, \"testGetGroupsOrderByName_name\", \"testGetGroupsOrderByName_label\", null);\n        getTransactionService().complete();\n\n        getTransactionService().begin();\n        List<SGroup> retrievedGroups = identityService.getGroups(5, 5, SGroup.NAME, OrderByType.DESC);\n        assertNotNull(\"can't find the groups after adding them\", retrievedGroups);\n        assertEquals(\"bad number of retrieved roles\", 5, retrievedGroups.size());\n        assertTrue(\"not in descending order\",\n                retrievedGroups.get(1).getName().compareTo(retrievedGroups.get(2).getName()) > 0);\n\n        retrievedGroups = identityService.getGroups(5, 5, SGroup.NAME, OrderByType.ASC);\n        getTransactionService().complete();\n        assertNotNull(\"can't find the groups after adding them\", retrievedGroups);\n        assertEquals(\"bad number of retrieved groups\", 5, retrievedGroups.size());\n        assertTrue(\"not in descending order\",\n                retrievedGroups.get(0).getName().compareTo(retrievedGroups.get(3).getName()) < 0);\n    }\n\n    @Test\n    public void testGetGroupsOrderByLabel() throws Exception {\n        getTransactionService().begin();\n        deleteGroups();\n        createGroups(10, \"testGetGroupsOrderByLabel_name\", \"testGetGroupsOrderByLabel_label\", null);\n        List<SGroup> retrievedGroups = identityService.getGroups(5, 5, SGroup.DISPLAY_NAME, OrderByType.DESC);\n        assertNotNull(\"can't find the groups after adding them\", retrievedGroups);\n        assertEquals(\"bad number of retrieved roles\", 5, retrievedGroups.size());\n        assertTrue(\"not in descending order\",\n                retrievedGroups.get(1).getName().compareTo(retrievedGroups.get(2).getName()) > 0);\n\n        retrievedGroups = identityService.getGroups(5, 5, SGroup.DISPLAY_NAME, OrderByType.ASC);\n        getTransactionService().complete();\n        assertNotNull(\"can't find the groups after adding them\", retrievedGroups);\n        assertEquals(\"bad number of retrieved groups\", 5, retrievedGroups.size());\n        assertTrue(\"not in descending order\",\n                retrievedGroups.get(0).getDisplayName().compareTo(retrievedGroups.get(3).getDisplayName()) < 0);\n    }\n\n    private List<SGroup> createGroups(final int i, final String basename, final String baseLabel, final SGroup g)\n            throws SIdentityException {\n        final List<SGroup> groups = new ArrayList<>();\n        for (int j = 0; j < i; j++) {\n            final SGroup.SGroupBuilder inst = SGroup.builder().name(basename + j).displayName(baseLabel + j);\n            if (g != null) {\n                inst.parentPath(g.getPath());\n            }\n            final SGroup group = inst.build();\n            identityService.createGroup(group, null, null);\n            groups.add(group);\n        }\n        return groups;\n    }\n\n    private void deleteGroups() throws SIdentityException {\n        final List<SGroup> groups = identityService.getGroups(0, 5000);\n        for (final SGroup sGroup : groups) {\n            identityService.deleteGroup(sGroup);\n        }\n    }\n\n    @Test\n    public void testGetNumberOfGroups() throws Exception {\n        getTransactionService().begin();\n        final long numberOfGroups = identityService.getNumberOfGroups();\n        long id;\n        SGroup group;\n        final long time = new Date().getTime();\n        for (int i = 0; i < 5; i++) {\n            id = time + i;\n            group = SGroup.builder().name(\"testGetNumberOfGroups\" + i).id(id).build();\n            identityService.createGroup(group, null, null);\n        }\n        assertEquals(\"bad count of groups\", numberOfGroups + 5, identityService.getNumberOfGroups());\n        getTransactionService().complete();\n    }\n\n    @Test\n    public void testGetGroupChildrenPaginated() throws Exception {\n        getTransactionService().begin();\n        deleteGroups();\n        final List<SGroup> groups = createGroups(1, \"testGetGroupChildrenPaginated_name\",\n                \"testGetGroupChildrenPaginated_label\", null);\n        identityService.getGroup(groups.get(0).getId());\n\n        final SGroup parentGroup = groups.iterator().next();\n        createGroups(5, \"testGetGroupChildrenPaginatedChildren_name\", \"testGetGroupChildrenPaginatedChildren_label\",\n                parentGroup);\n        getTransactionService().complete();\n\n        getTransactionService().begin();\n        final List<SGroup> retrievedGroups = identityService.getGroupChildren(parentGroup.getId(), 0, 5);\n        getTransactionService().complete();\n        assertNotNull(\"can't find the groups after adding them\", retrievedGroups);\n        assertEquals(\"bad number of retrieved groups\", 5, retrievedGroups.size());\n    }\n\n    @Test\n    public void testGetGroupChildrenWithCriterion() throws Exception {\n        getTransactionService().begin();\n        deleteGroups();\n        final List<SGroup> groups = createGroups(1, \"testGetGroupChildrenWithCriterion_name\",\n                \"testGetGroupChildrenWithCriterion_label\", null);\n        final SGroup parentGroup = groups.iterator().next();\n        createGroups(5, \"testGetGroupChildrenWithCriterionChildren_name\",\n                \"testGetGroupChildrenWithCriterionChildren_label\", parentGroup);\n        getTransactionService().complete();\n\n        getTransactionService().begin();\n        List<SGroup> retrievedGroups = identityService.getGroupChildren(parentGroup.getId(), 0, 3, SGroup.NAME,\n                OrderByType.DESC);\n        assertNotNull(\"can't find the groups after adding them\", retrievedGroups);\n        assertEquals(\"bad number of retrieved groups\", 3, retrievedGroups.size());\n        assertTrue(\"not in descending order\",\n                retrievedGroups.get(1).getName().compareTo(retrievedGroups.get(2).getName()) > 0);\n\n        retrievedGroups = identityService.getGroupChildren(parentGroup.getId(), 0, 3, SGroup.NAME,\n                OrderByType.ASC);\n        getTransactionService().complete();\n        assertNotNull(\"can't find the groups after adding them\", retrievedGroups);\n        assertEquals(\"bad number of retrieved groups\", 3, retrievedGroups.size());\n        assertTrue(\"not in descending order\",\n                retrievedGroups.get(0).getName().compareTo(retrievedGroups.get(1).getName()) < 0);\n\n    }\n\n    @Test\n    public void testGetNumberOfGroupChildren() throws Exception {\n        getTransactionService().begin();\n        deleteGroups();\n        final List<SGroup> groups = createGroups(1, \"testGetNumberOfGroupChildren_name\",\n                \"testGetNumberOfGroupChildren_label\", null);\n        final SGroup parentGroup = groups.iterator().next();\n        createGroups(5, \"testGetNumberOfGroupChildrenChildren_name\", \"testGetNumberOfGroupChildrenChildren_label\",\n                parentGroup);\n        getTransactionService().complete();\n\n        getTransactionService().begin();\n        assertEquals(\"bad count of groups\", 5, identityService.getNumberOfGroupChildren(parentGroup.getId()));\n        getTransactionService().complete();\n    }\n\n    /*\n     * Method that helps to retrieve profile metadata\n     */\n\n    @Test\n    public void testGetProfileMetadataPaginated() throws Exception {\n        getTransactionService().begin();\n        long id;\n        SCustomUserInfoDefinition metadata;\n        final long time = new Date().getTime();\n        for (int i = 0; i < 30; i++) {\n            id = time + i;\n            metadata = SCustomUserInfoDefinition.builder().name(\"testGetProfileMetadataPaginated\" + i)\n                    .id(id).build();\n            identityService.createCustomUserInfoDefinition(metadata);\n        }\n\n        List<SCustomUserInfoDefinition> retrievedMetadata = identityService.getCustomUserInfoDefinitions(5, 5);\n        assertNotNull(\"can't find the groups after adding them\", retrievedMetadata);\n        assertEquals(\"bad number of retrieved groups\", 5, retrievedMetadata.size());\n\n        retrievedMetadata = identityService.getCustomUserInfoDefinitions(0, 20);\n        getTransactionService().complete();\n        assertNotNull(\"can't find the groups after adding them\", retrievedMetadata);\n        assertEquals(\"bad number of retrieved groups\", 20, retrievedMetadata.size());\n    }\n\n    @Test\n    public void testGetNumberOfCustomUserInfoDefinition() throws Exception {\n        getTransactionService().begin();\n        final long numberOfMetadata = identityService.getNumberOfCustomUserInfoDefinition();\n        long id;\n        SCustomUserInfoDefinition info;\n        final long time = new Date().getTime();\n        for (int i = 0; i < 30; i++) {\n            id = time + 50L + i;\n            info = SCustomUserInfoDefinition.builder().name(\"testGetNumberOfCustomUserInfoDefinition\" + i)\n                    .id(id).build();\n            identityService.createCustomUserInfoDefinition(info);\n        }\n        assertEquals(\"bad count of custom user info definition\", numberOfMetadata + 30,\n                identityService.getNumberOfCustomUserInfoDefinition());\n        getTransactionService().complete();\n    }\n\n    /*\n     * Method that helps to retrieve users\n     */\n    @Test\n    public void testGetUsersWithRolePaginated() throws Exception {\n        getTransactionService().begin();\n        final SRole role = SRole.builder().name(\"testGetUsersWithRole\").build();\n        identityService.createRole(role, null, null);\n        final List<SGroup> groups = createGroups(2, \"testGetUsersWithRoleGroup\", \"testGetUsersWithRoleGroup\", null);\n        for (int i = 0; i < 10; i++) {\n            final SUser.SUserBuilder userBuilder = SUser.builder().userName(\"user\" + i).password(\"kikoo\");\n            final SUser user = identityService.createUser(userBuilder.build());\n            final SUserMembership userMembership1 = SUserMembership.builder().userId(user.getId())\n                    .groupId(groups.get(0).getId()).roleId(role.getId())\n                    .build();\n            identityService.createUserMembership(userMembership1);\n        }\n        for (int i = 10; i < 20; i++) {\n            final SUser.SUserBuilder userBuilder = SUser.builder().userName(\"user\" + i).password(\"kikoo\");\n            final SUser user = identityService.createUser(userBuilder.build());\n            final SUserMembership userMembership2 = SUserMembership.builder().userId(user.getId())\n                    .groupId(groups.get(1).getId()).roleId(role.getId())\n                    .build();\n            identityService.createUserMembership(userMembership2);\n        }\n\n        final List<SUser> usersWithRole = identityService.getUsersWithRole(role.getId(), 10, 10);\n        getTransactionService().complete();\n        assertEquals(\"not all user were retrieved\", 10, usersWithRole.size());\n    }\n\n    @Test\n    public void testGetUsersWithRoleWithCriterion() throws Exception {\n        getTransactionService().begin();\n\n        final SRole role = SRole.builder().name(\"testGetUsersWithRole\").build();\n        identityService.createRole(role, null, null);\n        final List<SGroup> groups = createGroups(2, \"testGetUsersWithRoleGroup\", \"testGetUsersWithRoleGroup\", null);\n        SUser user;\n        for (int i = 0; i < 10; i++) {\n            user = SUser.builder().userName(\"user\" + i).password(\"kikoo\").build();\n            final SUser user2 = identityService.createUser(user);\n            final SUserMembership userMembership1 = SUserMembership.builder().userId(user2.getId())\n                    .groupId(groups.get(0).getId()).roleId(role.getId())\n                    .build();\n            identityService.createUserMembership(userMembership1);\n        }\n        for (int i = 10; i < 20; i++) {\n            user = SUser.builder().userName(\"user\" + i).password(\"kikoo\").build();\n            final SUser user2 = identityService.createUser(user);\n            final SUserMembership userMembership2 = SUserMembership.builder().userId(user2.getId())\n                    .groupId(groups.get(1).getId()).roleId(role.getId())\n                    .build();\n            identityService.createUserMembership(userMembership2);\n        }\n\n        List<SUser> usersWithRole = identityService.getUsersWithRole(role.getId(), 10, 10, SUser.USER_NAME,\n                OrderByType.DESC);\n        assertEquals(\"not all user were retrieved\", 10, usersWithRole.size());\n        assertTrue(\"not in descending order\",\n                usersWithRole.get(1).getUserName().compareTo(usersWithRole.get(2).getUserName()) > 0);\n        usersWithRole = identityService.getUsersWithRole(role.getId(), 10, 10, SUser.USER_NAME, OrderByType.ASC);\n        getTransactionService().complete();\n        assertEquals(\"not all user were retrieved\", 10, usersWithRole.size());\n        assertTrue(\"not in asc order\",\n                usersWithRole.get(1).getUserName().compareTo(usersWithRole.get(2).getUserName()) < 0);\n    }\n\n    @Test\n    public void testGetActiveAndInactiveUsersWithRole() throws Exception {\n        getTransactionService().begin();\n        final SRole role = SRole.builder().name(\"testGetActiveAndInactiveUsersWithRole\").build();\n        identityService.createRole(role, null, null);\n        final List<SGroup> groups = createGroups(2, \"testGetUsersWithRoleGroup\", \"testGetUsersWithRoleGroup\", null);\n        SUser user;\n        for (int i = 0; i < 10; i++) {\n            user = SUser.builder().userName(\"inactive user\" + i).password(\"kikoo\").enabled(false).build();\n            final SUser user2 = identityService.createUser(user);\n            final SUserMembership userMembership1 = SUserMembership.builder().userId(user2.getId())\n                    .groupId(groups.get(0).getId()).roleId(role.getId())\n                    .build();\n            identityService.createUserMembership(userMembership1);\n        }\n        for (int i = 10; i < 20; i++) {\n            user = SUser.builder().userName(\"active user\" + i).enabled(true).password(\"kikoo\").build();\n            final SUser user2 = identityService.createUser(user);\n            final SUserMembership userMembership2 = SUserMembership.builder().userId(user2.getId())\n                    .groupId(groups.get(1).getId()).roleId(role.getId())\n                    .build();\n            identityService.createUserMembership(userMembership2);\n        }\n\n        List<SUser> activeUsersWithRole = identityService\n                .getActiveUsersWithRole(role.getId(), 0, 20, SUser.USER_NAME,\n                        OrderByType.DESC);\n        List<SUser> inactiveUsersByRole = identityService\n                .getInactiveUsersWithRole(role.getId(), 0, 20, SUser.USER_NAME,\n                        OrderByType.DESC);\n        assertThat(inactiveUsersByRole.size()).isEqualTo(10);\n        assertThat(activeUsersWithRole.size()).isEqualTo(10);\n        for (int i = 0; i < 10; i++) {\n            assertThat(inactiveUsersByRole.get(i).getUserName()).contains(\"inactive user\");\n            assertThat(activeUsersWithRole.get(i).getUserName()).contains(\"active user\");\n        }\n\n        getTransactionService().complete();\n    }\n\n    @Test\n    public void testGetNumberOfUsersByRole() throws Exception {\n        getTransactionService().begin();\n        final SRole role = SRole.builder().name(\"testGetNumberOfUsersByRole\").build();\n        identityService.createRole(role, null, null);\n        final long numberOfUsersByRole = identityService.getNumberOfUsersByRole(role.getId());\n        final SGroup group = SGroup.builder().name(\"testGetUsersByGroup\").build();\n        identityService.createGroup(group, null, null);\n        SUser user;\n        for (int i = 0; i < 5; i++) {\n            user = SUser.builder().userName(\"testGetNumberOfUsersByRole\" + i).password(\"kikoo\").build();\n            final SUser user2 = identityService.createUser(user);\n            final SUserMembership userMembership = SUserMembership.builder().userId(user2.getId())\n                    .groupId(group.getId()).roleId(role.getId()).build();\n            identityService.createUserMembership(userMembership);\n        }\n        assertEquals(\"not the good number of users by role\", numberOfUsersByRole + 5,\n                identityService.getNumberOfUsersByRole(role.getId()));\n        getTransactionService().complete();\n    }\n\n    @Test\n    public void testGetUsersInGroupPaginated() throws Exception {\n        getTransactionService().begin();\n        final SRole role = SRole.builder().name(\"testGetUsersByRole\").build();\n        identityService.createRole(role, null, null);\n        final List<SGroup> groups = createGroups(2, \"testGetUsersByRoleGroup\", \"testGetUsersByRoleGroup\", null);\n        SUser user;\n        for (int i = 0; i < 5; i++) {\n            final SUser.SUserBuilder userBuilder = SUser.builder().userName(\"user\" + i).password(\"kikoo\");\n            user = identityService.createUser(userBuilder.build());\n            final SUserMembership userMembership1 = SUserMembership.builder().userId(user.getId())\n                    .groupId(groups.get(0).getId()).roleId(role.getId())\n                    .build();\n            identityService.createUserMembership(userMembership1);\n        }\n        for (int i = 5; i < 10; i++) {\n            final SUser.SUserBuilder userBuilder = SUser.builder().userName(\"user\" + i).password(\"kikoo\");\n            user = identityService.createUser(userBuilder.build());\n            final SUserMembership userMembership1 = SUserMembership.builder().userId(user.getId())\n                    .groupId(groups.get(1).getId()).roleId(role.getId())\n                    .build();\n            identityService.createUserMembership(userMembership1);\n        }\n        final List<SUser> usersByGroup = identityService.getUsersInGroup(groups.get(0).getId(), 1, 2, \"id\", null);\n        getTransactionService().complete();\n        assertEquals(\"not the good number of user with the role\", 2, usersByGroup.size());\n    }\n\n    @Test\n    public void testGetUsersInGroupwithCriterion() throws Exception {\n        getTransactionService().begin();\n\n        final SRole role = SRole.builder().name(\"testGetUsersByRole\").build();\n        identityService.createRole(role, null, null);\n        final List<SGroup> groups = createGroups(2, \"testGetUsersByRoleGroup\", \"testGetUsersByRoleGroup\", null);\n        SUser user;\n        for (int i = 0; i < 5; i++) {\n            final SUser.SUserBuilder userBuilder = SUser.builder().userName(\"user\" + i).password(\"kikoo\");\n            user = identityService.createUser(userBuilder.build());\n            final SUserMembership userMembership1 = SUserMembership.builder().userId(user.getId())\n                    .groupId(groups.get(0).getId()).roleId(role.getId())\n                    .build();\n            identityService.createUserMembership(userMembership1);\n        }\n        for (int i = 5; i < 10; i++) {\n            final SUser.SUserBuilder userBuilder = SUser.builder().userName(\"user\" + i).password(\"kikoo\");\n            user = identityService.createUser(userBuilder.build());\n            final SUserMembership userMembership1 = SUserMembership.builder().userId(user.getId())\n                    .groupId(groups.get(1).getId()).roleId(role.getId())\n                    .build();\n            identityService.createUserMembership(userMembership1);\n        }\n\n        List<SUser> usersByGroup = identityService.getUsersInGroup(groups.get(0).getId(), 1, 2, SUser.USER_NAME,\n                OrderByType.DESC);\n        assertEquals(\"not the good number of user with the role\", 2, usersByGroup.size());\n        assertTrue(\"not in descending order\",\n                usersByGroup.get(0).getUserName().compareTo(usersByGroup.get(1).getUserName()) > 0);\n        usersByGroup = identityService.getUsersInGroup(groups.get(0).getId(), 1, 2, SUser.USER_NAME,\n                OrderByType.ASC);\n        getTransactionService().complete();\n        assertEquals(\"not all user were retrieved\", 2, usersByGroup.size());\n        assertTrue(\"not in asc order\",\n                usersByGroup.get(0).getUserName().compareTo(usersByGroup.get(1).getUserName()) < 0);\n    }\n\n    @Test\n    public void testGetActiveAndInactiveUsersInGroup() throws Exception {\n        getTransactionService().begin();\n        final SRole role = SRole.builder().name(\"testGetActiveAndInactiveUsersByGroup\").build();\n        identityService.createRole(role, null, null);\n        final List<SGroup> groups = createGroups(2, \"testGetActiveAndInactiveUsersByGroup\",\n                \"testGetActiveAndInactiveUsersByGroup\", null);\n        SUser user;\n        for (int i = 0; i < 5; i++) {\n            final SUser.SUserBuilder userBuilder = SUser.builder().userName(\"inactive user\" + i).password(\"kikoo\")\n                    .enabled(false);\n            user = identityService.createUser(userBuilder.build());\n            final SUserMembership userMembership1 = SUserMembership.builder().userId(user.getId())\n                    .groupId(groups.get(0).getId()).roleId(role.getId())\n                    .build();\n            identityService.createUserMembership(userMembership1);\n        }\n        for (int i = 5; i < 10; i++) {\n            final SUser.SUserBuilder userBuilder = SUser.builder().userName(\"active user \" + i).password(\"kikoo\")\n                    .enabled(true);\n            user = identityService.createUser(userBuilder.build());\n            final SUserMembership userMembership1 = SUserMembership.builder().userId(user.getId())\n                    .groupId(groups.get(1).getId()).roleId(role.getId())\n                    .build();\n            identityService.createUserMembership(userMembership1);\n        }\n        for (int i = 10; i < 15; i++) {\n            final SUser.SUserBuilder userBuilder = SUser.builder().userName(\"active user \" + i).password(\"kikoo\")\n                    .enabled(true);\n            user = identityService.createUser(userBuilder.build());\n            final SUserMembership userMembership1 = SUserMembership.builder().userId(user.getId())\n                    .groupId(groups.get(0).getId()).roleId(role.getId())\n                    .build();\n            identityService.createUserMembership(userMembership1);\n        }\n        final List<SUser> activeUsersByGroup1 = identityService.getActiveUsersInGroup(groups.get(0).getId(), 0, 2, null,\n                null);\n        final List<SUser> inactiveUsersByGroup1 = identityService.getInactiveUsersInGroup(groups.get(0).getId(), 0, 8,\n                null, null);\n        assertThat(activeUsersByGroup1.size()).isEqualTo(2);\n        assertThat(inactiveUsersByGroup1.size()).isEqualTo(5);\n        final List<SUser> activeUsersByGroup2 = identityService.getActiveUsersInGroup(groups.get(1).getId(), 0, 3, null,\n                null);\n        final List<SUser> inactiveUsersByGroup2 = identityService.getInactiveUsersInGroup(groups.get(1).getId(), 0, 2,\n                null, null);\n        assertThat(activeUsersByGroup2.size()).isEqualTo(3);\n        assertThat(inactiveUsersByGroup2.size()).isEqualTo(0);\n        final List<SUser> activeUsersByGroup3 = identityService.getActiveUsersInGroup(groups.get(0).getId(), 0, 500,\n                null, null);\n        final List<SUser> inactiveUsersByGroup3 = identityService.getInactiveUsersInGroup(groups.get(0).getId(), 0,\n                1000, null, null);\n        assertThat(activeUsersByGroup3.size()).isEqualTo(5);\n        assertThat(inactiveUsersByGroup3.size()).isEqualTo(5);\n        for (int i = 0; i < 5; i++) {\n            assertThat(activeUsersByGroup3.get(i).getUserName()).contains(\"active user\");\n            assertThat(inactiveUsersByGroup3.get(i).getUserName()).contains(\"inactive user\");\n        }\n        getTransactionService().complete();\n    }\n\n    @Test\n    public void testGetNumberOfUsersByGroup() throws Exception {\n        getTransactionService().begin();\n\n        final SRole role = SRole.builder().name(\"testGetUsersByRole\").build();\n        identityService.createRole(role, null, null);\n        final List<SGroup> groups = createGroups(2, \"testGetUsersByRoleGroup\", \"testGetUsersByRoleGroup\", null);\n        SUser user;\n        for (int i = 0; i < 5; i++) {\n            final SUser.SUserBuilder userBuilder = SUser.builder().userName(\"user\" + i).password(\"kikoo\");\n            user = identityService.createUser(userBuilder.build());\n            final SUserMembership userMembership1 = SUserMembership.builder().userId(user.getId())\n                    .groupId(groups.get(0).getId()).roleId(role.getId())\n                    .build();\n            identityService.createUserMembership(userMembership1);\n        }\n        for (int i = 5; i < 10; i++) {\n            final SUser.SUserBuilder userBuilder = SUser.builder().userName(\"user\" + i).password(\"kikoo\");\n            user = identityService.createUser(userBuilder.build());\n            final SUserMembership userMembership1 = SUserMembership.builder().userId(user.getId())\n                    .groupId(groups.get(1).getId()).roleId(role.getId())\n                    .build();\n            identityService.createUserMembership(userMembership1);\n        }\n        final long id = groups.get(0).getId();\n\n        assertEquals(\"not the good number of users by group\", 5, identityService.getNumberOfUsersByGroup(id));\n        getTransactionService().complete();\n    }\n\n    @Test\n    public void testGetNumberOfUsersByMembership() throws Exception {\n        getTransactionService().begin();\n        final SGroup group = createGroups(1, \"testGetUserByMembership\", \"testGetUserByMembership\", null).iterator()\n                .next();\n        final SRole role = createRoles(1, \"testGetUserByMembership\", \"testGetUserByMembership\").iterator().next();\n        final List<SUser> users = createUsers(5, \"getMembershipUsers\");\n        for (final SUser sUser : users) {\n            final SUserMembership userMembership = SUserMembership.builder().userId(sUser.getId())\n                    .groupId(group.getId()).roleId(role.getId()).build();\n            identityService.createUserMembership(userMembership);\n        }\n        assertEquals(\"not the good number of user by membership\", 5,\n                identityService.getNumberOfUsersByMembership(group.getId(), role.getId()));\n        getTransactionService().complete();\n    }\n\n    @Test\n    public void testGetUsersPaginated() throws Exception {\n        getTransactionService().begin();\n        createUsers(20, \"testGetUsersPaginated\");\n        List<SUser> users = identityService.getUsers(5, 10);\n        final SUser user1 = users.get(0);\n        assertEquals(\"returned list have not the correct size\", 10, users.size());\n\n        users = identityService.getUsers(6, 10);\n        final SUser user2 = users.get(0);\n        getTransactionService().complete();\n        assertEquals(\"returned list have not the correct size\", 10, users.size());\n        assertNotSame(\"from index not working\", user1.getId(), user2.getId());\n    }\n\n    @Test\n    public void testGetUsersOrderByUserName() throws Exception {\n        getTransactionService().begin();\n        createUsers(10, \"testGetUsersOrderByUserName\");\n        List<SUser> users = identityService.getUsers(5, 10, SUser.USER_NAME, OrderByType.DESC);\n        assertTrue(\"not in desc order\",\n                users.get(0).getUserName().compareTo(users.get(users.size() - 1).getUserName()) > 0);\n        users = identityService.getUsers(5, 10, SUser.USER_NAME, OrderByType.ASC);\n        getTransactionService().complete();\n        assertTrue(\"not in asc order\",\n                users.get(0).getUserName().compareTo(users.get(users.size() - 1).getUserName()) < 0);\n    }\n\n    @Test\n    public void testGetUsersOrderByFirstName() throws Exception {\n        getTransactionService().begin();\n        deleteUsers();\n        createUsers(10, \"testGetUsersOrderByFirstName\");\n        List<SUser> users = identityService.getUsers(5, 10, SUser.FIRST_NAME, OrderByType.DESC);\n        assertTrue(\"not in desc order\",\n                users.get(0).getFirstName().compareTo(users.get(users.size() - 1).getFirstName()) > 0);\n        users = identityService.getUsers(5, 10, SUser.FIRST_NAME, OrderByType.ASC);\n        getTransactionService().complete();\n        assertTrue(\"not in asc order\",\n                users.get(0).getFirstName().compareTo(users.get(users.size() - 1).getFirstName()) < 0);\n    }\n\n    @Test\n    public void testGetUsersOrderByLastName() throws Exception {\n        getTransactionService().begin();\n        deleteUsers();\n        createUsers(10, \"testGetUsersOrderByLastName\");\n        List<SUser> users = identityService.getUsers(5, 10, SUser.LAST_NAME, OrderByType.DESC);\n        assertTrue(\"not in desc order\",\n                users.get(0).getLastName().compareTo(users.get(users.size() - 1).getLastName()) > 0);\n        users = identityService.getUsers(5, 10, SUser.LAST_NAME, OrderByType.ASC);\n        getTransactionService().complete();\n        assertTrue(\"not in asc order\",\n                users.get(0).getLastName().compareTo(users.get(users.size() - 1).getLastName()) < 0);\n    }\n\n    private List<SUser> createUsers(final int i, final String baseUsername) throws SIdentityException {\n        final List<SUser> ids = new ArrayList<>();\n        for (int j = 0; j < i; j++) {\n            final SUser user = SUser.builder().userName(baseUsername + j).firstName(\"firstName\" + j)\n                    .lastName(\"lastName\" + j).password(\"password\" + j).build();\n            ids.add(identityService.createUser(user));\n        }\n        return ids;\n    }\n\n    private void deleteUsers() throws SIdentityException {\n        final List<SUser> users = identityService.getUsers(0, 5000);\n        for (final SUser sUser : users) {\n            identityService.deleteUser(sUser);\n        }\n    }\n\n    @Test\n    public void testGetNumberOfUsers() throws Exception {\n        getTransactionService().begin();\n        final long numberOfUsers = identityService.getNumberOfUsers();\n        final SUser user = SUser.builder().userName(\"testGetNumberOfUsers\").password(\"kikoo\").build();\n        identityService.createUser(user);\n        assertEquals(numberOfUsers + 1, identityService.getNumberOfUsers());\n        getTransactionService().complete();\n    }\n\n    @Test\n    public void testGetUsersWithManager() throws Exception {\n        getTransactionService().begin();\n        createUsers(11, \"testGetUsersWithManager\");\n        final SUser manager = identityService.getUsers(0, 1).get(0);\n        final List<SUser> users = identityService.getUsers(1, 10);\n        for (final SUser user : users) {\n            final EntityUpdateDescriptor changeDescriptor = BuilderFactory.get(SUserUpdateBuilderFactory.class)\n                    .createNewInstance()\n                    .updateManagerUserId(manager.getId()).done();\n            identityService.updateUser(user, changeDescriptor);\n        }\n        final long id = manager.getId();\n        final List<SUser> usersWithManager = identityService.getUsersWithManager(id, 0, 20, null, null);\n        getTransactionService().complete();\n        assertEquals(\"did not retrieved all user having the manager\", 10, usersWithManager.size());\n        for (final SUser sUser : usersWithManager) {\n            assertEquals(\"One of the user have not the good manager\", manager.getId(), sUser.getManagerUserId());\n        }\n    }\n\n    @Test\n    public void testGetActiveAndInactiveUsersWithManager() throws Exception {\n        getTransactionService().begin();\n        createUsers(31, \"testGetActiveUsersWithManager\");\n        final SUser manager = identityService.getUsers(0, 1).get(0);\n        final List<SUser> activeUsers = identityService.getUsers(0, 15);\n        final List<SUser> inactiveUsers = identityService.getUsers(15, 5);\n        for (final SUser user : activeUsers) {\n            final EntityUpdateDescriptor changeDescriptor = BuilderFactory.get(SUserUpdateBuilderFactory.class)\n                    .createNewInstance()\n                    .updateManagerUserId(manager.getId()).updateEnabled(true).done();\n            identityService.updateUser(user, changeDescriptor);\n        }\n        for (final SUser user : inactiveUsers) {\n            final EntityUpdateDescriptor changeDescriptor = BuilderFactory.get(SUserUpdateBuilderFactory.class)\n                    .createNewInstance()\n                    .updateManagerUserId(manager.getId()).updateEnabled(false).done();\n            identityService.updateUser(user, changeDescriptor);\n        }\n        final long id = manager.getId();\n        final List<SUser> activeUsersWithManager = identityService.getActiveUsersWithManager(id, 0, 20, null, null);\n        final List<SUser> inactiveUsersWithManager = identityService.getInactiveUsersWithManager(id, 0, 20, null, null);\n        getTransactionService().complete();\n        assertThat(activeUsersWithManager.size()).isEqualTo(15);\n        assertThat(inactiveUsersWithManager.size()).isEqualTo(5);\n        for (final SUser sUser : activeUsersWithManager) {\n            assertThat(sUser.getManagerUserId()).isEqualTo(manager.getId());\n        }\n        for (final SUser sUser : inactiveUsersWithManager) {\n            assertThat(sUser.getManagerUserId()).isEqualTo(manager.getId());\n        }\n    }\n\n    @Test\n    // FIXME change name\n    public void testUpdateUserDoesNotChangeManagerId() throws Exception {\n        getTransactionService().begin();\n        createUsers(3, \"testGetUpdateUserDoesNotChangeanagerId\");\n        final List<SUser> users = identityService.getUsers(0, 3);\n        assertEquals(3, users.size());\n        final SUser manager = users.get(0);\n        SUser user = users.get(1);\n        final SUser newManager = users.get(2);\n        EntityUpdateDescriptor changeDescriptor = BuilderFactory.get(SUserUpdateBuilderFactory.class)\n                .createNewInstance().updateManagerUserId(manager.getId())\n                .done();\n        identityService.updateUser(user, changeDescriptor);\n        final long id = manager.getId();\n        final List<SUser> usersWithManager = identityService.getUsersWithManager(id, 0, 20, null, null);\n        assertEquals(\"did not retrieved all user having the manager\", 1, usersWithManager.size());\n        for (final SUser sUser : usersWithManager) {\n            assertEquals(\"One of the user have not the good manager\", manager.getId(), sUser.getManagerUserId());\n        }\n        changeDescriptor = BuilderFactory.get(SUserUpdateBuilderFactory.class).createNewInstance()\n                .updateFirstName(\"kevin\").done();\n        identityService.updateUser(user, changeDescriptor);\n        user = identityService.getUser(user.getId());\n        assertEquals(\"kevin\", user.getFirstName());\n        assertEquals(manager.getId(), user.getManagerUserId());\n\n        changeDescriptor = BuilderFactory.get(SUserUpdateBuilderFactory.class).createNewInstance()\n                .updateManagerUserId(newManager.getId()).done();\n        identityService.updateUser(user, changeDescriptor);\n        user = identityService.getUser(user.getId());\n        assertEquals(newManager.getId(), user.getManagerUserId());\n        getTransactionService().complete();\n    }\n\n    @Test\n    public void testUpdateUser() throws Exception {\n        getTransactionService().begin();\n        SUser.SUserBuilder userBuilder = null;\n        userBuilder = SUser.builder().userName(\"testUpdateUser\").password(\"kikoo\")\n                .firstName(\"Update\").lastName(\"User\");\n        final SUser user = identityService.createUser(userBuilder.build());\n        final String password = user.getPassword();\n        final SContactInfo contactInfo = SContactInfo.builder().userId(user.getId()).personal(true).address(\"Somewhere\")\n                .building(\"AA11\")\n                .city(\"Taiwan\").build();\n        identityService.createUserContactInfo(contactInfo);\n        final EntityUpdateDescriptor changeDescriptor = BuilderFactory.get(SUserUpdateBuilderFactory.class)\n                .createNewInstance()\n                .updateUserName(\"testUpdateUser2\").updatePassword(\"lol\")\n                .updateFirstName(\"updated\").updateLastName(\"user2\").updateEnabled(true).done();\n        final String newAddress = \"SomeWhereElse\";\n        final String newCity = \"Ouarzazate\";\n        final String newBuilding = \"BB22\";\n        final String country = \"Marrocco\";\n        final String email = \"other@fifi.org\";\n        final String faxNumber = \"99999999\";\n        final String mobileNumber = \"77777777\";\n        final String phoneNumber = \"555555\";\n        final String room = \"Room2\";\n        final String state = \"State2\";\n        final String website = \"website2\";\n        final String zipCode = \"zipCode2\";\n        final EntityUpdateDescriptor updateContactInfo = BuilderFactory.get(SContactInfoUpdateBuilderFactory.class)\n                .createNewInstance()\n                .updateAddress(newAddress).updateCity(newCity)\n                .updateBuilding(newBuilding).updateCountry(country).updateEmail(email).updateFaxNumber(faxNumber)\n                .updateMobileNumber(mobileNumber)\n                .updatePhoneNumber(phoneNumber).updateRoom(room).updateState(state).updateWebsite(website)\n                .updateZipCode(zipCode).done();\n        identityService.updateUser(user, changeDescriptor);\n        identityService.updateUserContactInfo(contactInfo, updateContactInfo);\n        final SUser user2 = identityService.getUser(user.getId());\n        final SContactInfo contactInfo2 = identityService.getUserContactInfo(user2.getId(), true);\n        getTransactionService().complete();\n        assertEquals(\"user was not updated\", user, user2);\n        assertEquals(\"testUpdateUser2\", user2.getUserName());\n        assertNotSame(password, user2.getPassword()); // FIXME replace password by user.getPassword()\n        assertEquals(\"updated\", user2.getFirstName());\n        assertEquals(\"user2\", user2.getLastName());\n        assertEquals(user2.getId(), (long) contactInfo2.getUserId());\n        assertEquals(newAddress, contactInfo2.getAddress());\n        assertEquals(newBuilding, contactInfo2.getBuilding());\n        assertEquals(newCity, contactInfo2.getCity());\n        assertEquals(country, contactInfo2.getCountry());\n        assertEquals(email, contactInfo2.getEmail());\n        assertEquals(faxNumber, contactInfo2.getFaxNumber());\n        assertEquals(mobileNumber, contactInfo2.getMobileNumber());\n        assertEquals(phoneNumber, contactInfo2.getPhoneNumber());\n        assertEquals(room, contactInfo2.getRoom());\n        assertEquals(state, contactInfo2.getState());\n        assertEquals(website, contactInfo2.getWebsite());\n        assertEquals(zipCode, contactInfo2.getZipCode());\n    }\n\n    @Test\n    public void testAddProfileMetadata() throws Exception {\n        getTransactionService().begin();\n        final long metadataId = new Date().getTime();\n        final SCustomUserInfoDefinition metadata = SCustomUserInfoDefinition.builder().id(metadataId)\n                .name(\"testAddProfileMetadata\").build();\n        identityService.createCustomUserInfoDefinition(metadata);\n        final SCustomUserInfoDefinition metadata2 = identityService.getCustomUserInfoDefinition(metadataId);\n        getTransactionService().complete();\n        assertNotNull(\"can't retrieve the metadata\", metadata2);\n        assertEquals(\"retrieved not the good metadata\", metadata.getId(), metadata2.getId());\n    }\n\n    @Test\n    public void testUpdateProfileMetadata() throws Exception {\n        getTransactionService().begin();\n        final SCustomUserInfoDefinition metadata = identityService.getCustomUserInfoDefinitions(0, 1).get(0);\n        final long metadataId = metadata.getId();\n        final String newName = \"theNewName\";\n        final EntityUpdateDescriptor changeDescriptor = BuilderFactory\n                .get(SCustomUserInfoDefinitionUpdateBuilderFactory.class).createNewInstance()\n                .updateName(newName).done();\n        identityService.updateCustomUserInfoDefinition(metadata, changeDescriptor);\n        final SCustomUserInfoDefinition metadata2 = identityService.getCustomUserInfoDefinition(metadataId);\n        getTransactionService().complete();\n        assertNotNull(\"can't retrieve the metadata\", metadata2);\n        assertEquals(\"retrieved not the good metadata\", metadata.getId(), metadata2.getId());\n        assertEquals(\"metadata not updated\", newName, metadata2.getName());\n    }\n\n    @Test\n    public void testAddRole() throws Exception {\n        getTransactionService().begin();\n        final long id = new Date().getTime();\n        final SRole role = SRole.builder().id(id).name(\"testAddRole\").build();\n        identityService.createRole(role, null, null);\n        final SRole role2 = identityService.getRole(id);\n        getTransactionService().complete();\n        assertNotNull(\"can't find the added role\", role2);\n        assertEquals(\"not the good role was added\", role.getName(), role2.getName());\n    }\n\n    @Test\n    public void testUpdateRole() throws Exception {\n        getTransactionService().begin();\n\n        final SRole role = createRoles(1, \"firstName\", \"firstLabel\").get(0);\n        final long id = role.getId();\n        final String newName = \"newRoleName\";\n        final EntityUpdateDescriptor changeDescriptor = BuilderFactory.get(SRoleUpdateBuilderFactory.class)\n                .createNewInstance().updateName(newName).done();\n        identityService.updateRole(role, changeDescriptor, null);\n        final SRole role2 = identityService.getRole(id);\n        getTransactionService().complete();\n        assertNotNull(\"can't find the updated role\", role2);\n        assertEquals(\"not udpated\", newName, role2.getName());\n    }\n\n    @Test\n    public void testAddGroup() throws Exception {\n        getTransactionService().begin();\n        final long id = new Date().getTime();\n        final SGroup group = SGroup.builder().id(id).name(\"testAddGroup\").build();\n        identityService.createGroup(group, null, null);\n        final SGroup group2 = identityService.getGroup(id);\n        getTransactionService().complete();\n        assertNotNull(\"can't find the added group\", group2);\n        assertEquals(\"not the good group was added\", group.getName(), group2.getName());\n    }\n\n    @Test\n    public void testUpdateGroup() throws Exception {\n        getTransactionService().begin();\n        final SGroup group = createGroups(1, \"firstName\", \"firstLabel\", null).get(0);\n        final long id = group.getId();\n        final String newName = \"newGroupName\";\n        final EntityUpdateDescriptor changeDescriptor = BuilderFactory.get(SGroupUpdateBuilderFactory.class)\n                .createNewInstance().updateName(newName).done();\n        identityService.updateGroup(group, changeDescriptor, null);\n        final SGroup group2 = identityService.getGroup(id);\n        getTransactionService().complete();\n        assertNotNull(\"can't find the updated group\", group2);\n        assertEquals(\"not udpated\", newName, group2.getName());\n    }\n\n    /*\n     * Methods that delete objects from the identityService module\n     */\n\n    @Test(expected = SIdentityException.class)\n    public void testDeleteUser() throws Exception {\n        getTransactionService().begin();\n        final SUser user = createUsers(1, \"testDeleteUser\").get(0);\n        final long id = user.getId();\n        identityService.deleteUser(user);\n\n        assertNull(\"the user was not deleted\", identityService.getUser(id));\n        getTransactionService().complete();\n    }\n\n    @Test(expected = SIdentityException.class)\n    public void testDeleteProfileMetadata() throws Exception {\n        getTransactionService().begin();\n        final SCustomUserInfoDefinition metadataDefinition = SCustomUserInfoDefinition.builder()\n                .name(\"kikooMetadata\").build();\n        identityService.createCustomUserInfoDefinition(metadataDefinition);\n        final long id = metadataDefinition.getId();\n        identityService.deleteCustomUserInfoDefinition(metadataDefinition);\n        assertNull(\"the profile metadata was not deleted\", identityService.getCustomUserInfoDefinition(id));\n        getTransactionService().complete();\n    }\n\n    @Test(expected = SIdentityException.class)\n    public void testDeleteRole() throws Exception {\n        getTransactionService().begin();\n        final SRole role = createRoles(1, \"testDeleteRole\", \"testDeleteRole\").get(0);\n        final long id = role.getId();\n        identityService.deleteRole(role);\n        assertNull(\"the role was not deleted\", identityService.getRole(id));\n        getTransactionService().complete();\n    }\n\n    @Test(expected = SIdentityException.class)\n    public void testDeleteGroup() throws Exception {\n        getTransactionService().begin();\n        final List<SGroup> groups = createGroups(1, \"testDeleteGroup_name\", \"testDeleteGroup_label\", null);\n        getTransactionService().complete();\n\n        getTransactionService().begin();\n        final SGroup group = groups.iterator().next();\n        assertNotNull(group);\n        final long id = group.getId();\n        identityService.deleteGroup(group);\n        assertNull(\"the group was not deleted\", identityService.getGroup(id));\n        getTransactionService().complete();\n    }\n\n    /*\n     * Methods to add/remove/set memberships to user\n     */\n\n    @Test\n    public void testAddMembershipToUser() throws Exception {\n        getTransactionService().begin();\n        final List<SGroup> groups = createGroups(1, \"testAddMembershipToUser_name\", \"testAddMembershipToUser_label\",\n                null);\n        createRoles(1, \"testAddMembershipToUser_name\", \"testAddMembershipToUser_label\");\n        final SGroup group = groups.iterator().next();\n\n        final SRole role = identityService.getRoleByName(\"testAddMembershipToUser_name0\");\n        createUsers(1, \"testAddMembershipToUser\");\n        SUser user = identityService.getUserByUserName(\"testAddMembershipToUser0\");\n        assertNotNull(user);\n        getTransactionService().complete();\n\n        getTransactionService().begin();\n        user = identityService.getUserByUserName(\"testAddMembershipToUser0\");\n        final int size = identityService\n                .getUserMembershipsOfUser(user.getId(), 0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS).size();\n        final SUserMembership userMembership = SUserMembership.builder().userId(user.getId()).groupId(group.getId())\n                .roleId(role.getId()).build();\n        identityService.createUserMembership(userMembership);\n        getTransactionService().complete();\n\n        getTransactionService().begin();\n        final SUser user2 = identityService.getUser(user.getId());\n        assertEquals(\"membership not added\", size + 1,\n                identityService.getUserMembershipsOfUser(user2.getId(), 0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS)\n                        .size());\n        getTransactionService().complete();\n    }\n\n    @Test\n    public void testRemoveMembershipFromUser() throws Exception {\n        getTransactionService().begin();\n        deleteGroups();\n        final List<SGroup> groups = createGroups(1, \"testRemoveMembershipFromUser_name\",\n                \"testRemoveMembershipFromUser_label\", null);\n        createRoles(1, \"testRemoveMembershipFromUser_name\", \"testRemoveMembershipFromUser_label\");\n        final SGroup group = groups.iterator().next();\n        final SRole role = identityService.getRoleByName(\"testRemoveMembershipFromUser_name0\");\n        createUsers(1, \"testRemoveMembershipFromUser\");\n        SUser user = identityService.getUserByUserName(\"testRemoveMembershipFromUser0\");\n        assertNotNull(user);\n        getTransactionService().complete();\n\n        getTransactionService().begin();\n\n        user = identityService.getUserByUserName(\"testRemoveMembershipFromUser0\");\n        final SUserMembership userMembership = SUserMembership.builder().userId(user.getId()).groupId(group.getId())\n                .roleId(role.getId()).build();\n        identityService.createUserMembership(userMembership);\n        getTransactionService().complete();\n\n        getTransactionService().begin();\n        user = identityService.getUserByUserName(\"testRemoveMembershipFromUser0\");\n        final int size = identityService\n                .getUserMembershipsOfUser(user.getId(), 0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS).size();\n        assertTrue(\"no membership on user\", size >= 1);\n\n        final SUserMembership userMembership2 = identityService.getUserMembership(user.getId(), group.getId(),\n                role.getId());\n        identityService.deleteUserMembership(userMembership2);\n\n        final SUser user2 = identityService.getUser(user.getId());\n\n        assertEquals(\"no membership on user\", size - 1,\n                identityService.getUserMembershipsOfUser(user2.getId(), 0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS)\n                        .size());\n        getTransactionService().complete();\n    }\n\n    @Test\n    public void searchUsersWithWildcards() throws Exception {\n        getTransactionService().begin();\n        final SUser user1 = identityService.createUser(SUser.builder().userName(\"user1\")\n                .firstName(\"firstname1\")\n                .lastName(\"lastname1\").password(\"lkh\").build());\n        final SUser user2 = identityService.createUser(SUser.builder().userName(\"user2\")\n                .firstName(\"firstname2\")\n                .lastName(\"lastname2\").password(\"mlbxcvjmsdkljf\").build());\n        getTransactionService().complete();\n\n        final Map<Class<? extends PersistentObject>, Set<String>> userAllFields = new HashMap<>();\n        final Set<String> fields = new HashSet<>(4);\n        fields.add(\"userName\");\n        fields.add(\"firstName\");\n        fields.add(\"lastName\");\n        fields.add(\"jobTitle\");\n        userAllFields.put(SUser.class, fields);\n        final QueryOptions queryOptions = new QueryOptions(0, 10,\n                Arrays.asList(new OrderByOption(SUser.class, \"userName\", OrderByType.ASC)),\n                new ArrayList<>(0), new SearchFields(Arrays.asList(\"#\"), userAllFields));\n        getTransactionService().begin();\n        final List<SUser> result = identityService.searchUsers(queryOptions);\n        assertEquals(0, result.size());\n        getTransactionService().complete();\n\n        // clean-up\n        getTransactionService().begin();\n        identityService.deleteUser(user1);\n        identityService.deleteUser(user2);\n        getTransactionService().complete();\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/job/AddJobCommand.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.job;\n\nimport java.io.Serializable;\nimport java.util.Date;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.command.RuntimeCommand;\nimport org.bonitasoft.engine.command.SCommandExecutionException;\nimport org.bonitasoft.engine.scheduler.SchedulerService;\nimport org.bonitasoft.engine.scheduler.exception.SSchedulerException;\nimport org.bonitasoft.engine.scheduler.model.SJobDescriptor;\nimport org.bonitasoft.engine.scheduler.trigger.OneShotTrigger;\nimport org.bonitasoft.engine.scheduler.trigger.Trigger;\nimport org.bonitasoft.engine.service.ServiceAccessor;\n\npublic class AddJobCommand extends RuntimeCommand {\n\n    @Override\n    public Serializable execute(final Map<String, Serializable> parameters, final ServiceAccessor serviceAccessor)\n            throws SCommandExecutionException {\n        final SchedulerService schedulerService = serviceAccessor.getSchedulerService();\n        final Trigger trigger = new OneShotTrigger(\"OneShot\", new Date());\n        final SJobDescriptor jobDescriptor = SJobDescriptor.builder()\n                .jobClassName(ThrowsExceptionJob.class.getName())\n                .jobName(\"ThrowsExceptionJob\")\n                .description(\"Throw an exception when 'throwException'=true\")\n                .build();\n        try {\n            schedulerService.schedule(jobDescriptor, trigger);\n            return null;\n        } catch (final SSchedulerException sse) {\n            throw new SCommandExecutionException(sse);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/job/JobExecutionIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.job;\n\nimport static java.util.Collections.*;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.awaitility.Awaitility.await;\nimport static org.awaitility.Durations.ONE_MINUTE;\nimport static org.awaitility.Durations.ONE_SECOND;\nimport static org.hamcrest.collection.IsCollectionWithSize.hasSize;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.Callable;\n\nimport org.bonitasoft.engine.connectors.VariableStorage;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.scheduler.JobService;\nimport org.bonitasoft.engine.scheduler.model.SJobDescriptor;\nimport org.bonitasoft.engine.scheduler.model.SJobLog;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.test.CommonAPILocalIT;\nimport org.bonitasoft.engine.test.WaitUntil;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n// because of waituntil but its the only class where we use failed jobs... so i don't want to add a handler and so on\n// only for jobs\npublic class JobExecutionIT extends CommonAPILocalIT {\n\n    private static final String THROWS_EXCEPTION_JOB = \"ThrowsExceptionJob\";\n\n    @Before\n    public void before() throws Exception {\n        loginWithTechnicalUser();\n\n        createUser(USERNAME, PASSWORD);\n        logout();\n        loginOnDefaultTenantWith(USERNAME, PASSWORD);\n        setSessionInfo(getSession());\n    }\n\n    @After\n    public void after() throws Exception {\n        VariableStorage.clearAll();\n        deleteUser(USERNAME);\n        logout();\n        cleanSession();\n    }\n\n    @Test\n    public void getFailedJobs_should_return_zero_when_there_are_no_failed_jobs() {\n        final List<FailedJob> failedJobs = getProcessAPI().getFailedJobs(0, 100);\n        assertThat(failedJobs).isEmpty();\n    }\n\n    @Test\n    public void retryAJob_should_execute_again_a_failed_job_and_clean_related_job_logs_and_jobDescriptor_if_not_recurrent()\n            throws Exception {\n        //given\n        getCommandAPI().register(\"except\", \"Throws Exception when scheduling a job\", AddJobCommand.class.getName());\n        try {\n            getCommandAPI().execute(\"except\", emptyMap());\n            final FailedJob failedJob = waitForFailedJob();\n            assertThat(failedJob.getJobName()).isEqualTo(THROWS_EXCEPTION_JOB);\n            assertThat(failedJob.getNumberOfFailures()).isEqualTo(1);\n            assertThat(failedJob.getDescription()).isEqualTo(\"Throw an exception when 'throwException'=true\");\n\n            final List<SJobDescriptor> jobDescriptors = waitForJobDescriptorsToHaveSize(1);\n            waitForJobLogsToHaveSize(jobDescriptors.get(0).getId(), 1);\n\n            //when\n            getProcessAPI().replayFailedJob(failedJob.getJobDescriptorId(),\n                    singletonMap(\"throwException\", Boolean.FALSE));\n\n            //then\n            assertJobWasExecutedWithSuccess();\n\n            waitForJobDescriptorsToHaveSize(0);\n            waitForJobLogsToHaveSize(jobDescriptors.get(0).getId(), 0);\n\n            // clean up:\n            deleteJobLogsAndDescriptors(failedJob.getJobDescriptorId());\n        } finally {\n            getCommandAPI().unregister(\"except\");\n        }\n    }\n\n    private void waitForJobLogsToHaveSize(final long jobDescriptorId, final int nbOfExpectedJobLogs) {\n        final QueryOptions options = new QueryOptions(0, 1, null,\n                singletonList(new FilterOption(SJobLog.class, \"jobDescriptorId\", jobDescriptorId)), null);\n\n        await().until(() -> {\n            setSessionInfo(getSession()); // the session was cleaned by api call. This must be improved\n            final ServiceAccessor serviceAccessor = getServiceAccessor();\n            final UserTransactionService transactionService = serviceAccessor.getUserTransactionService();\n            final JobService jobService = serviceAccessor.getJobService();\n            return transactionService.executeInTransaction(() -> jobService.searchJobLogs(options));\n        }, hasSize(nbOfExpectedJobLogs));\n    }\n\n    private List<SJobDescriptor> waitForJobDescriptorsToHaveSize(final int nbOfExpectedJobDescriptors) {\n        final List<FilterOption> filters = singletonList(\n                new FilterOption(SJobDescriptor.class, \"jobClassName\", ThrowsExceptionJob.class.getName()));\n        final QueryOptions queryOptions = new QueryOptions(0, 1, null, filters, null);\n\n        return await().until(() -> {\n            setSessionInfo(getSession()); // the session was cleaned by api call. This must be improved\n            final ServiceAccessor serviceAccessor = getServiceAccessor();\n            final UserTransactionService transactionService = serviceAccessor.getUserTransactionService();\n            final JobService jobService = serviceAccessor.getJobService();\n            return transactionService.executeInTransaction(() -> jobService.searchJobDescriptors(queryOptions));\n        }, hasSize(nbOfExpectedJobDescriptors));\n    }\n\n    @Test\n    public void retryAJob_should_update_job_log_when_execution_fails_again() throws Exception {\n        //given\n        getCommandAPI().register(\"except\", \"Throws Exception when scheduling a job\", AddJobCommand.class.getName());\n        try {\n            getCommandAPI().execute(\"except\", Map.of());\n            // Job can trigger in up to 'org.quartz.scheduler.idleWaitTime' milliseconds, so better wait long enough:\n            FailedJob failedJob = await().atMost(ONE_MINUTE).pollInterval(ONE_SECOND).until(\n                    () -> getProcessAPI().getFailedJobs(0, 100), hasSize(1)).get(0);\n\n            //when\n            getProcessAPI().replayFailedJob(failedJob.getJobDescriptorId());\n\n            //then\n            // Job can trigger in up to 'org.quartz.scheduler.idleWaitTime' milliseconds, so better wait long enough:\n            failedJob = await().atMost(ONE_MINUTE).pollInterval(ONE_SECOND)\n                    .until(() -> getProcessAPI().getFailedJobs(0, 100), hasSize(1)).get(0);\n            assertThat(failedJob.getJobName()).isEqualTo(THROWS_EXCEPTION_JOB);\n            assertThat(failedJob.getDescription()).isEqualTo(\"Throw an exception when 'throwException'=true\");\n            assertThat(failedJob.getLastMessage()).contains(\n                    \"org.bonitasoft.engine.scheduler.exception.SJobExecutionException: This job throws an arbitrary exception\");\n            assertThat(failedJob.getNumberOfFailures()).isEqualTo(1);\n\n            deleteJobLogsAndDescriptors(failedJob.getJobDescriptorId());\n        } finally {\n            getCommandAPI().unregister(\"except\");\n        }\n    }\n\n    private void deleteJobLogsAndDescriptors(final long jobDescriptorId) throws Exception {\n        setSessionInfo(getSession()); // the session was cleaned by api call. This must be improved\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final UserTransactionService transactionService = serviceAccessor.getUserTransactionService();\n        final JobService jobService = serviceAccessor.getJobService();\n        transactionService.executeInTransaction((Callable) () -> {\n            jobService.deleteJobLogs(jobDescriptorId);\n            jobService.deleteJobDescriptor(jobDescriptorId);\n            return null;\n        });\n    }\n\n    private FailedJob waitForFailedJob() throws Exception {\n        new WaitUntil(DEFAULT_REPEAT_EACH, DEFAULT_TIMEOUT) {\n\n            @Override\n            protected boolean check() {\n                return getFailedJob() != null;\n            }\n        }.waitUntil();\n        final FailedJob failedJob = getFailingJob();\n        assertThat(failedJob).isNotNull();\n        assertThat(failedJob.getNumberOfFailures()).isPositive();\n        return failedJob;\n    }\n\n    private void assertJobWasExecutedWithSuccess() throws Exception {\n        new WaitUntil(DEFAULT_REPEAT_EACH, DEFAULT_TIMEOUT) {\n\n            @Override\n            protected boolean check() {\n                final FailedJob failedJob = getFailingJob();\n                return failedJob == null;\n            }\n        }.waitUntil();\n        final FailedJob failedJob = getFailingJob();\n        assertThat(failedJob).isNull();\n    }\n\n    private FailedJob getFailedJob() {\n        final FailedJob failedJob = getFailingJob();\n        if (failedJob != null && failedJob.getNumberOfFailures() > 0) {\n            return failedJob;\n        }\n        return null;\n    }\n\n    private FailedJob getFailingJob() {\n        final List<FailedJob> failedJobs = getProcessAPI().getFailedJobs(0, 100);\n        FailedJob searchJob = null;\n        for (final FailedJob failedJob : failedJobs) {\n            if (failedJob.getJobName().equals(JobExecutionIT.THROWS_EXCEPTION_JOB)) {\n                searchJob = failedJob;\n            }\n        }\n        return searchJob;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/job/ThrowsExceptionJob.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.job;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.scheduler.StatelessJob;\nimport org.bonitasoft.engine.scheduler.exception.SJobExecutionException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ThrowsExceptionJob implements StatelessJob {\n\n    private static final long serialVersionUID = 3528070481384646426L;\n\n    private boolean throwException = true;\n\n    @Override\n    public String getDescription() {\n        return \"Job that throws a exception\";\n    }\n\n    @Override\n    public void execute() throws SJobExecutionException {\n        if (throwException) {\n            throw new SJobExecutionException(\n                    \"This job throws an arbitrary exception if parameter 'throwException' is provided.\");\n        }\n    }\n\n    @Override\n    public String getName() {\n        return \"exception\";\n    }\n\n    @Override\n    public void setAttributes(final Map<String, Serializable> attributes) {\n        final Boolean result = (Boolean) attributes.get(\"throwException\");\n        if (result != null) {\n            throwException = result;\n        }\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/login/LoginAPIIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.login;\n\nimport static org.junit.Assert.*;\n\nimport java.io.IOException;\nimport java.util.Date;\n\nimport lombok.SneakyThrows;\nimport org.bonitasoft.engine.CommonAPIIT;\nimport org.bonitasoft.engine.api.LoginAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.identity.UserUpdater;\nimport org.bonitasoft.engine.platform.LoginException;\nimport org.bonitasoft.engine.service.impl.ServiceAccessorFactory;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.PlatformSession;\nimport org.bonitasoft.engine.session.SessionNotFoundException;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class LoginAPIIT extends CommonAPIIT {\n\n    private static PlatformSession platformSession;\n\n    @Before\n    public void before() throws BonitaException, IOException {\n        platformSession = loginOnPlatform();\n    }\n\n    @After\n    public void after() throws BonitaException {\n        logoutOnPlatform(platformSession);\n    }\n\n    @Test(expected = SessionNotFoundException.class)\n    public void testSessionNotFoundExceptionIsThrownAfterSessionDeletion() throws Exception {\n        loginWithTechnicalUser();\n        // login to create a session\n        final long sessionId = getSession().getId();\n\n        // delete the session created by the login\n        deleteSession(sessionId);\n\n        // will throw SessionNotFoundException\n        getLoginAPI().logout(getSession());\n    }\n\n    @SneakyThrows\n    private void deleteSession(final long sessionId) {\n        ServiceAccessorFactory.getInstance().createServiceAccessor()\n                .getSessionService()\n                .deleteSession(sessionId);\n    }\n\n    @Test(expected = LoginException.class)\n    public void loginFailsWithNullUsername() throws BonitaException {\n        final LoginAPI loginTenant = TenantAPIAccessor.getLoginAPI();\n        loginTenant.login(null, null);\n    }\n\n    @Test(expected = LoginException.class)\n    public void loginFailsWithEmptyUsername() throws BonitaException {\n        final LoginAPI loginTenant = TenantAPIAccessor.getLoginAPI();\n        loginTenant.login(\"\", null);\n    }\n\n    @Test(expected = LoginException.class)\n    public void loginFailsWithNullPassword() throws BonitaException {\n        final LoginAPI loginTenant = TenantAPIAccessor.getLoginAPI();\n        loginTenant.login(\"matti\", null);\n    }\n\n    @Test(expected = LoginException.class)\n    public void loginFailsWithWrongPassword() throws BonitaException {\n        loginWithTechnicalUser();\n        final String userName = \"Truc\";\n        createUser(userName, \"goodPassword\");\n        try {\n            final LoginAPI loginTenant = TenantAPIAccessor.getLoginAPI();\n            loginTenant.login(userName, \"WrongPassword\");\n            fail(\"Should not be reached\");\n        } finally {\n            loginWithTechnicalUser();\n            getIdentityAPI().deleteUser(userName);\n        }\n    }\n\n    @Test(expected = LoginException.class)\n    public void loginFailsWithEmptyPassword() throws BonitaException {\n        final LoginAPI loginTenant = TenantAPIAccessor.getLoginAPI();\n        loginTenant.login(\"matti\", \"\");\n    }\n\n    @Test\n    public void userLoginDefaultTenant() throws BonitaException, InterruptedException {\n        loginWithTechnicalUser();\n        final String userName = \"matti\";\n        final String password = \"tervetuloa\";\n        createUser(userName, password);\n\n        final Date now = new Date();\n        Thread.sleep(300);\n        loginOnDefaultTenantWith(userName, password);\n        final User user = getIdentityAPI().getUserByUserName(userName);\n        getIdentityAPI().deleteUser(userName);\n\n        assertEquals(userName, user.getUserName());\n        assertTrue(now.before(user.getLastConnection()));\n        logout();\n    }\n\n    @Test\n    public void loginOnDefaultTenantWithExistingUserAndCheckId() throws BonitaException {\n        loginWithTechnicalUser();\n        final String userName = \"corvinus\";\n        final String password = \"underworld\";\n        final User user = createUser(userName, password);\n        final LoginAPI loginTenant = TenantAPIAccessor.getLoginAPI();\n        final APISession login = loginTenant.login(userName, password);\n        assertTrue(\"userId should be valuated\", user.getId() != -1);\n        assertEquals(user.getId(), login.getUserId());\n\n        getIdentityAPI().deleteUser(user.getId());\n        logout();\n    }\n\n    @Test\n    public void loginOnDefaultTenantWithNonTechnicalUser() throws BonitaException {\n        loginWithTechnicalUser();\n        final User user = createUser(\"matti\", \"kieli\");\n        logout();\n\n        loginOnDefaultTenantWith(\"matti\", \"kieli\");\n        assertTrue(\"Should be logged in as a NON-Technical user\", !getSession().isTechnicalUser());\n        logout();\n\n        loginWithTechnicalUser();\n        getIdentityAPI().deleteUser(user.getId());\n        logout();\n    }\n\n    @Test\n    public void loginOnDefaultTenantWithTechnicalUser() throws BonitaException {\n        loginWithTechnicalUser();\n        assertTrue(\"Should be logged in as Technical user\", getSession().isTechnicalUser());\n        logout();\n    }\n\n    @Test(expected = LoginException.class)\n    public void unableToLoginWhenTheUserIsDisable() throws BonitaException {\n        loginWithTechnicalUser();\n        final String userName = \"matti\";\n        final String password = \"bpm\";\n        final User user = getIdentityAPI().createUser(userName, password);\n        final UserUpdater updater = new UserUpdater();\n        updater.setEnabled(false);\n        getIdentityAPI().updateUser(user.getId(), updater);\n        final LoginAPI loginTenant = TenantAPIAccessor.getLoginAPI();\n        try {\n            loginTenant.login(userName, password);\n            fail(\"It is not possible to login when the user is disable.\");\n        } finally {\n            getIdentityAPI().deleteUser(user.getId());\n            logout();\n        }\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/page/PageMappingServiceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.Collections;\n\nimport org.bonitasoft.engine.bpm.CommonBPMServicesTest;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.transaction.TransactionService;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class PageMappingServiceIT extends CommonBPMServicesTest {\n\n    private static final long PAGE_ID = 12345L;\n    private static final long NEW_PAGE_ID = 88854L;\n\n    private TransactionService transactionService;\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n    private PageMappingService pageMappingService;\n\n    @Before\n    public void setUp() {\n        transactionService = getTransactionService();\n        pageMappingService = getServiceAccessor().getPageMappingService();\n    }\n\n    @After\n    public void tearDown() {\n    }\n\n    @Test\n    public void getByKey() throws Exception {\n        transactionService.begin();\n        final SPageMapping internal = pageMappingService.create(\"getByKey/process1/12.0\", PAGE_ID,\n                Collections.emptyList());\n        final SPageMapping external = pageMappingService.create(\"getByKey/process2/12.0\", \"http://www.google.com\", null,\n                Collections.emptyList());\n        final SPageMapping externalWithAdapter = pageMappingService.create(\"getByKey/process3/12.0\",\n                \"http://www.google.com\", \"theAdapter\",\n                Collections.emptyList());\n\n        transactionService.complete();\n\n        transactionService.begin();\n        final SPageMapping internalPersisted = pageMappingService.get(\"getByKey/process1/12.0\");\n        final SPageMapping externalPersisted = pageMappingService.get(\"getByKey/process2/12.0\");\n        final SPageMapping exterAdaPersisted = pageMappingService.get(\"getByKey/process3/12.0\");\n\n        transactionService.complete();\n\n        assertThat(internalPersisted).isEqualTo(internal);\n        assertThat(internalPersisted.getPageId()).isEqualTo(PAGE_ID);\n        assertThat(internalPersisted.getUrl()).isNull();\n        assertThat(externalPersisted).isEqualTo(external);\n        assertThat(externalPersisted.getUrl()).isEqualTo(\"http://www.google.com\");\n        assertThat(exterAdaPersisted).isEqualTo(externalWithAdapter);\n        assertThat(exterAdaPersisted.getUrl()).isEqualTo(\"http://www.google.com\");\n        assertThat(exterAdaPersisted.getUrlAdapter()).isEqualTo(\"theAdapter\");\n    }\n\n    @Test\n    public void delete() throws Exception {\n        transactionService.begin();\n        final SPageMapping internal = pageMappingService.create(\"delete/process1/12.0\", PAGE_ID,\n                Collections.emptyList());\n        transactionService.complete();\n\n        transactionService.begin();\n        pageMappingService.delete(internal);\n        transactionService.complete();\n\n        try {\n            expectedException.expect(SObjectNotFoundException.class);\n            transactionService.begin();\n            pageMappingService.get(internal.getKey());\n        } finally {\n            transactionService.complete();\n        }\n    }\n\n    @Test\n    public void update() throws Exception {\n\n        final String key = \"theKey/process1/12.0\";\n        transactionService.begin();\n        pageMappingService.create(key, PAGE_ID, Collections.emptyList());\n        transactionService.complete();\n\n        transactionService.begin();\n        final SPageMapping pageMapping1 = pageMappingService.get(key);\n        pageMappingService.update(pageMapping1, NEW_PAGE_ID);\n        transactionService.complete();\n\n        transactionService.begin();\n        final SPageMapping updated = pageMappingService.get(key);\n        transactionService.complete();\n\n        assertThat(updated).isEqualTo(pageMapping1);\n        assertThat(updated.getPageId()).isEqualTo(NEW_PAGE_ID);\n        assertThat(updated.getUrl()).isNull();\n        assertThat(updated.getUrlAdapter()).isNull();\n\n        Thread.sleep(10);\n\n        transactionService.begin();\n        final SPageMapping reUpdated = pageMappingService.get(key);\n        pageMappingService.update(reUpdated, \"http://www.yahoo.com\", \"adapterURL\");\n        transactionService.complete();\n\n        assertThat(reUpdated.getKey()).isEqualTo(key);\n        assertThat(reUpdated.getPageId()).isNull();\n        assertThat(reUpdated.getUrl()).isEqualTo(\"http://www.yahoo.com\");\n        assertThat(reUpdated.getUrlAdapter()).isEqualTo(\"adapterURL\");\n        assertThat(reUpdated.getLastUpdateDate()).isGreaterThan(updated.getLastUpdateDate());\n        assertThat(reUpdated.getLastUpdatedBy()).isEqualTo(-1);\n\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/persistence/PersistenceIT.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport static org.junit.Assert.fail;\n\nimport org.assertj.core.api.Assertions;\nimport org.bonitasoft.engine.bpm.CommonBPMServicesTest;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.transaction.STransactionCommitException;\nimport org.bonitasoft.engine.transaction.STransactionCreationException;\nimport org.bonitasoft.engine.transaction.STransactionRollbackException;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class PersistenceIT extends CommonBPMServicesTest {\n\n    private ReadPersistenceService persistenceService;\n    private Recorder recorder;\n\n    @Before\n    public void before() {\n        persistenceService = getServiceAccessor().getReadPersistenceService();\n        recorder = getServiceAccessor().getRecorder();\n    }\n\n    @Test\n    public void testIfOneFailAllFail() throws Exception {\n        Long numberOfUsersBefore = getNumberOfUsers();\n        getTransactionService().begin();\n\n        final SUser user = new SUser();\n        user.setUserName(\"SUserImpl1FN\");\n        user.setPassword(\"SUserImpl1LN\");\n        recorder.recordInsert(new InsertRecord(user), \"USER\");\n        try {\n            persistenceService.selectOne(new SelectOneDescriptor<SUser>(\"wrong query\", null, SUser.class));\n            fail(\"Exception expected\");\n        } catch (final Exception e) {\n            getTransactionService().setRollbackOnly();\n        } finally {\n            getTransactionService().complete();\n        }\n\n        Assertions.assertThat(getNumberOfUsers()).isEqualTo(numberOfUsersBefore);\n    }\n\n    private Long getNumberOfUsers() throws STransactionCreationException, SBonitaReadException,\n            STransactionCommitException, STransactionRollbackException {\n        getTransactionService().begin();\n        final Long nbOfSUserImpl = persistenceService\n                .selectOne(new SelectOneDescriptor<>(\"getNumberOfSUser\", null, SUser.class, Long.class));\n        getTransactionService().complete();\n        return nbOfSUserImpl;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/platform/auth/PlatformAuthenticationServiceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.auth;\n\nimport org.bonitasoft.engine.bpm.CommonBPMServicesTest;\nimport org.bonitasoft.engine.platform.authentication.PlatformAuthenticationService;\nimport org.bonitasoft.engine.platform.authentication.SInvalidPasswordException;\nimport org.bonitasoft.engine.platform.authentication.SInvalidUserException;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class PlatformAuthenticationServiceIT extends CommonBPMServicesTest {\n\n    private PlatformAuthenticationService platformAuthService;\n\n    @Before\n    public void setup() {\n        platformAuthService = getServiceAccessor().getPlatformAuthenticationService();\n    }\n\n    @Test\n    public void testCheckValidUser() throws Exception {\n        final String username = \"platformAdmin\";\n        final String password = \"platform\";\n\n        //getTransactionService().begin();\n        platformAuthService.checkUserCredentials(username, password);\n        //getTransactionService().complete();\n    }\n\n    @Test(expected = SInvalidPasswordException.class)\n    public void testCheckUserWithWrongPassword() throws Exception {\n        final String username = \"platformAdmin\";\n        //getTransactionService().begin();\n        platformAuthService.checkUserCredentials(username, \"wrong\");\n        //getTransactionService().complete();\n    }\n\n    @Test(expected = SInvalidUserException.class)\n    public void testCheckNonExistentUser() throws Exception {\n        final String username = \"anonyme\";\n        final String password = \"bpm\";\n        //getTransactionService().begin();\n        platformAuthService.checkUserCredentials(username, password);\n        //getTransactionService().complete();\n\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/platform/command/PlatformCommandServiceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.command;\n\nimport static org.junit.Assert.*;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.CommonBPMServicesTest;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.platform.command.model.SPlatformCommand;\nimport org.bonitasoft.engine.platform.command.model.SPlatformCommandUpdateBuilderFactory;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Zhang Bole\n */\npublic class PlatformCommandServiceIT extends CommonBPMServicesTest {\n\n    private PlatformCommandService platformCommandService;\n\n    @Before\n    public void setup() {\n        platformCommandService = getServiceAccessor().getPlatformCommandService();\n    }\n\n    @Test(expected = SPlatformCommandAlreadyExistsException.class)\n    public void testSPlatformCommandAlreadyExistsException() throws Exception {\n        getTransactionService().begin();\n        final SPlatformCommand sPlatformCommand = new SPlatformCommand(\"createCommand\", \"this is a command\",\n                \"command implementation\");\n        platformCommandService.create(sPlatformCommand);\n        try {\n            platformCommandService.create(sPlatformCommand);\n        } finally {\n            platformCommandService.delete(\"createCommand\");\n            getTransactionService().complete();\n        }\n    }\n\n    @Test(expected = SPlatformCommandNotFoundException.class)\n    public void testSPlatformCommandNotFoundException() throws Exception {\n        getTransactionService().begin();\n        try {\n            platformCommandService.getPlatformCommand(\"a\");\n        } finally {\n            getTransactionService().complete();\n        }\n    }\n\n    @Test\n    public void testCreatePlatformCommand() throws Exception {\n        getTransactionService().begin();\n        final SPlatformCommand command1 = new SPlatformCommand(\"createCommand\", \"this is a command\",\n                \"command implementation\");\n        platformCommandService.create(command1);\n        final SPlatformCommand command2 = platformCommandService.getPlatformCommand(\"createCommand\");\n        assertNotNull(\"can't find the category after adding it\", command2);\n        assertEquals(\"can't retrieve the same category\", command1.getName(), command2.getName());\n        assertEquals(\"can't retrieve the same category\", command1.getId(), command2.getId());\n        platformCommandService.delete(\"createCommand\");\n        getTransactionService().complete();\n    }\n\n    @Test(expected = SPlatformCommandNotFoundException.class)\n    public void testDeletePlatformCommand() throws Exception {\n        getTransactionService().begin();\n        final SPlatformCommand sPlatformCommand = new SPlatformCommand(\"testCommandDelete\", \"this is a command\",\n                \"command implementation\");\n        platformCommandService.create(sPlatformCommand);\n        platformCommandService.delete(\"testCommandDelete\");\n        try {\n            platformCommandService.getPlatformCommand(\"testCommandDelete\");\n        } finally {\n            getTransactionService().complete();\n        }\n    }\n\n    @Test\n    public void testDeleteAll() throws Exception {\n        getTransactionService().begin();\n        final SPlatformCommand command1 = new SPlatformCommand(\"createCommand1\", \"this is a command\",\n                \"command implementation\");\n        final SPlatformCommand command2 = new SPlatformCommand(\"createCommand2\", \"this is a command\",\n                \"command implementation\");\n        final SPlatformCommand command3 = new SPlatformCommand(\"createCommand3\", \"this is a command\",\n                \"command implementation\");\n        platformCommandService.create(command1);\n        platformCommandService.create(command2);\n        platformCommandService.create(command3);\n        final QueryOptions queryOptions = new QueryOptions(0, 500, SPlatformCommand.class, \"name\", OrderByType.ASC);\n        final List<SPlatformCommand> commands1 = platformCommandService.getPlatformCommands(queryOptions);\n        assertEquals(3, commands1.size());\n\n        platformCommandService.deleteAll();\n        final List<SPlatformCommand> commands2 = platformCommandService.getPlatformCommands(queryOptions);\n        assertTrue(commands2.isEmpty());\n        getTransactionService().complete();\n    }\n\n    @Test\n    public void testUpdatePlatformCommand() throws Exception {\n        getTransactionService().begin();\n        final SPlatformCommand oldCommand = new SPlatformCommand(\"old\", \"this is an old command\",\n                \"command implementation\");\n        platformCommandService.create(oldCommand);\n        assertEquals(\"old\", oldCommand.getName());\n        assertEquals(\"this is an old command\", oldCommand.getDescription());\n\n        final String commandName = \"new\";\n        final EntityUpdateDescriptor updateDescriptor = BuilderFactory.get(SPlatformCommandUpdateBuilderFactory.class)\n                .createNewInstance().updateName(commandName)\n                .updateDescription(\"this is a new command\").done();\n        platformCommandService.update(oldCommand, updateDescriptor);\n        final SPlatformCommand newCommand = platformCommandService.getPlatformCommand(commandName);\n        assertEquals(\"new\", newCommand.getName());\n        assertEquals(\"this is a new command\", newCommand.getDescription());\n        platformCommandService.delete(commandName);\n        getTransactionService().complete();\n    }\n\n    @Test\n    public void testGetPlatformCommandByName() throws Exception {\n        getTransactionService().begin();\n        final SPlatformCommand sPlatformCommand = new SPlatformCommand(\"commandOne\", \"this is a command\",\n                \"command implementation\");\n        platformCommandService.create(sPlatformCommand);\n        final SPlatformCommand command = platformCommandService.getPlatformCommand(\"commandOne\");\n        assertEquals(\"commandOne\", command.getName());\n        assertEquals(\"this is a command\", command.getDescription());\n        platformCommandService.delete(\"commandOne\");\n        getTransactionService().complete();\n    }\n\n    @Test\n    public void testGetPlatformCommandsWithCriterion() throws Exception {\n        getTransactionService().begin();\n        final SPlatformCommand sPlatformCommand1 = new SPlatformCommand(\"commandB\", \"this is command1\",\n                \"command implementation\");\n        final SPlatformCommand sPlatformCommand2 = new SPlatformCommand(\"commandC\", \"this is command2\",\n                \"command implementation\");\n        final SPlatformCommand sPlatformCommand3 = new SPlatformCommand(\"commandA\", \"this is command3\",\n                \"command implementation\");\n        platformCommandService.create(sPlatformCommand1);\n        platformCommandService.create(sPlatformCommand2);\n        platformCommandService.create(sPlatformCommand3);\n\n        final QueryOptions queryOptions = new QueryOptions(0, 5, SPlatformCommand.class, \"name\", OrderByType.ASC);\n        final List<SPlatformCommand> commands = platformCommandService.getPlatformCommands(queryOptions);\n        assertEquals(3, commands.size());\n        assertEquals(\"commandA\", commands.get(0).getName());\n        assertEquals(\"commandB\", commands.get(1).getName());\n        assertEquals(\"commandC\", commands.get(2).getName());\n\n        platformCommandService.deleteAll();\n        getTransactionService().complete();\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/platform/login/PlatformLoginServiceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.login;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\n\nimport org.bonitasoft.engine.bpm.CommonBPMServicesTest;\nimport org.bonitasoft.engine.core.platform.login.PlatformLoginService;\nimport org.bonitasoft.engine.core.platform.login.SInvalidPlatformCredentialsException;\nimport org.bonitasoft.engine.platform.session.SSessionNotFoundException;\nimport org.bonitasoft.engine.platform.session.model.SPlatformSession;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Emmanuel Duchastenier\n */\npublic class PlatformLoginServiceIT extends CommonBPMServicesTest {\n\n    private PlatformLoginService platformLoginService;\n\n    @Before\n    public void setup() {\n        platformLoginService = getServiceAccessor().getPlatformLoginService();\n    }\n\n    @Test\n    public void testLoginLogout() throws Exception {\n        final String username = \"platformAdmin\";\n        final String password = \"platform\";\n        final SPlatformSession session = platformLoginService.login(username, password);\n        assertNotNull(session);\n        assertEquals(username, session.getUserName());\n\n        platformLoginService.logout(session.getId());\n    }\n\n    @Test(expected = SInvalidPlatformCredentialsException.class)\n    public void testLoginBadUser() throws Exception {\n        final String username = \"noAdmin\";\n        final String password = \"platform\";\n        platformLoginService.login(username, password);\n    }\n\n    @Test(expected = SInvalidPlatformCredentialsException.class)\n    public void testLoginBadPassword() throws Exception {\n        final String username = \"platformAdmin\";\n        final String password = \"wrong\";\n        platformLoginService.login(username, password);\n    }\n\n    @Test(expected = SSessionNotFoundException.class)\n    public void testLogoutWrongSession() throws Exception {\n        platformLoginService.logout(System.currentTimeMillis());\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/profile/ProfileServiceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.CommonBPMServicesTest;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.SUserCreationException;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.profile.exception.profile.SProfileNotFoundException;\nimport org.bonitasoft.engine.profile.model.SProfile;\nimport org.bonitasoft.engine.profile.model.SProfileMember;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n */\npublic class ProfileServiceIT extends CommonBPMServicesTest {\n\n    private static ProfileService profileService;\n\n    private static IdentityService identityService;\n\n    @Before\n    public void setup() {\n        profileService = getServiceAccessor().getProfileService();\n        identityService = getServiceAccessor().getIdentityService();\n    }\n\n    @Test(expected = SProfileNotFoundException.class)\n    public void cannotGetAnUnknownProfile() throws SBonitaException {\n        try {\n            getTransactionService().begin();\n            profileService.getProfile(10);\n            Assert.fail();\n        } finally {\n            getTransactionService().complete();\n        }\n    }\n\n    @Test\n    public void getProfile() throws SBonitaException {\n        getTransactionService().begin();\n        final SProfile profile = SProfile.builder()\n                .name(\"profile1\")\n                .isDefault(true).build();\n        final SProfile createdProfile = profileService.createProfile(profile);\n        final SProfile gotProfile = profileService.getProfile(createdProfile.getId());\n        Assert.assertEquals(createdProfile, gotProfile);\n        profileService.deleteProfile(gotProfile);\n        try {\n            profileService.getProfile(createdProfile.getId());\n            Assert.fail();\n        } catch (final SProfileNotFoundException spnfe) {\n            getTransactionService().complete();\n        }\n    }\n\n    @Test\n    public void getUserProfile() throws SBonitaException {\n        getTransactionService().begin();\n        final SProfile profile = profileService.createProfile(SProfile.builder()\n                .name(\"profile1\")\n                .isDefault(true)\n                .build());\n\n        final List<OrderByOption> orderByOptions = getOrderByOptions();\n        final QueryOptions queryOptions = new QueryOptions(0, 10, orderByOptions,\n                Collections.singletonList(new FilterOption(SProfileMember.class, \"profileId\",\n                        profile.getId())),\n                null);\n\n        List<SProfileMember> profileMembers = profileService.searchProfileMembers(\"ForUser\", queryOptions);\n        Assert.assertEquals(0, profileMembers.size());\n\n        SUser.SUserBuilder userBuilder = SUser.builder().userName(\"john\").password(\"bpm\")\n                .firstName(\"John\").lastName(\"Doe\");\n        final SUser john = identityService.createUser(userBuilder.build());\n\n        userBuilder = SUser.builder().userName(\"jane\").password(\"bpm\").firstName(\"Jane\")\n                .lastName(\"Doe\");\n        final SUser jane = identityService.createUser(userBuilder.build());\n\n        final SProfileMember johnProfileMember = profileService.addUserToProfile(profile.getId(), john.getId(), \"John\",\n                \"Doe\", \"john\");\n        final SProfileMember janeProfileMember = profileService.addUserToProfile(profile.getId(), jane.getId(), \"Jane\",\n                \"Doe\", \"jane\");\n        profileMembers = profileService.searchProfileMembers(\"ForUser\", queryOptions);\n        Assert.assertEquals(2, profileMembers.size());\n\n        profileService.deleteProfileMember(johnProfileMember);\n        profileService.deleteProfileMember(janeProfileMember);\n        profileMembers = profileService.searchProfileMembers(\"ForUser\", queryOptions);\n        Assert.assertEquals(0, profileMembers.size());\n\n        identityService.deleteUser(john);\n        identityService.deleteUser(jane);\n        profileService.deleteProfile(profile);\n        getTransactionService().complete();\n    }\n\n    private List<OrderByOption> getOrderByOptions() {\n        final List<OrderByOption> orderByOptions = new ArrayList<OrderByOption>(1);\n        orderByOptions.add(new OrderByOption(SUser.class, SUser.FIRST_NAME, OrderByType.ASC));\n        return orderByOptions;\n    }\n\n    private SUser createUser(final String username, final String password) throws SUserCreationException {\n        final SUser.SUserBuilder userBuilder = SUser.builder().userName(username).password(password);\n        return identityService.createUser(userBuilder.build());\n    }\n\n    @Test\n    public void getProfileOfUserFrom() throws SBonitaException {\n        getTransactionService().begin();\n        final SProfile profile = profileService.createProfile(SProfile.builder()\n                .name(\"profile1\")\n                .build());\n\n        final SUser john = createUser(\"john\", \"bpm\");\n        final SUser jane = createUser(\"jane\", \"bpm\");\n\n        List<SProfile> profilesOfUser = profileService.searchProfilesOfUser(john.getId(), 0, 10, \"name\",\n                OrderByType.ASC);\n        Assert.assertEquals(0, profilesOfUser.size());\n\n        final SProfileMember johnProfileMember = profileService.addUserToProfile(profile.getId(), john.getId(), \"John\",\n                \"Doe\", \"john\");\n        final SProfileMember janeProfileMember = profileService.addUserToProfile(profile.getId(), jane.getId(), \"Jane\",\n                \"Doe\", \"jane\");\n\n        profilesOfUser = profileService.searchProfilesOfUser(john.getId(), 0, 10, \"name\", OrderByType.ASC);\n        Assert.assertEquals(1, profilesOfUser.size());\n\n        final QueryOptions countOptions = new QueryOptions(0, 10, null,\n                Collections.singletonList(new FilterOption(SProfileMember.class, \"profileId\", profile\n                        .getId())),\n                null);\n\n        Assert.assertEquals(2, profileService.getNumberOfProfileMembers(\"ForUser\", countOptions));\n\n        profileService.deleteProfileMember(johnProfileMember);\n        profileService.deleteProfileMember(janeProfileMember);\n\n        profilesOfUser = profileService.searchProfilesOfUser(john.getId(), 0, 10, \"name\", OrderByType.ASC);\n        Assert.assertEquals(0, profilesOfUser.size());\n\n        identityService.deleteUser(john);\n        identityService.deleteUser(jane);\n        profileService.deleteProfile(profile);\n        getTransactionService().complete();\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/recorder/RecorderIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.recorder;\n\nimport static org.junit.Assert.*;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.archive.model.TestLogBuilderFactory;\nimport org.bonitasoft.engine.bpm.CommonBPMServicesTest;\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.bonitasoft.engine.scheduler.SchedulerService;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\nimport org.bonitasoft.engine.test.util.TestUtil;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Elias Ricken de Medeiros, Yanyan Liu\n */\npublic class RecorderIT extends CommonBPMServicesTest {\n\n    private static final String TEST_DELETE = \"TEST_DELETED\";\n\n    private static final String TEST_UPDATE = \"TEST_UPDATED\";\n\n    private static final String TEST_CREATED = \"TEST_CREATED\";\n\n    private final static int SLEEP_TIME = 200;\n\n    private final static String SUSER_IMPL = \"SUSER_IMPL\";\n\n    protected ReadPersistenceService persistenceService;\n\n    protected Recorder recorder;\n\n    protected SchedulerService scheduler;\n\n    protected EventService eventService;\n\n    protected QueriableLoggerService loggerService;\n\n    private TestLogBuilderFactory logModelBuilderFactory;\n\n    @Before\n    public void setUp() throws Exception {\n        persistenceService = getServiceAccessor().getReadPersistenceService();\n        recorder = getServiceAccessor().getRecorder();\n        eventService = getServiceAccessor().getEventService();\n        loggerService = getServiceAccessor().getQueriableLoggerService();\n        scheduler = getServiceAccessor().getSchedulerService();\n        TestUtil.stopScheduler(scheduler, getTransactionService());\n        TestUtil.startScheduler(scheduler);\n    }\n\n    @After\n    public void tearDown() throws Exception {\n        TestUtil.closeTransactionIfOpen(getTransactionService());\n    }\n\n    private SUser buildSUserImpl(final String firstName, final String lastName) {\n        final SUser sUser = new SUser();\n        sUser.setUserName(firstName);\n        sUser.setPassword(lastName);\n        return sUser;\n    }\n\n    private List<SQueriableLog> getLogs(final long indexValue, final String actionType) throws SBonitaReadException {\n        final List<FilterOption> filters = new ArrayList<>(2);\n        filters.add(getActionTypeFilterOption(actionType));\n        filters.add(new FilterOption(SQueriableLog.class, getLogModelBuilderFactory().getObjectIdKey(), indexValue));\n        final List<OrderByOption> orders = Collections\n                .singletonList(new OrderByOption(SQueriableLog.class, \"id\", OrderByType.ASC));\n        final QueryOptions opts = new QueryOptions(0, 10, orders, filters, null);\n        return loggerService.searchLogs(opts);\n    }\n\n    private FilterOption getActionTypeFilterOption(final String actionType) {\n        return new FilterOption(SQueriableLog.class, SQueriableLog.ACTION_TYPE, actionType);\n    }\n\n    private List<SQueriableLog> getLogs(final String actionType) throws SBonitaReadException {\n        final List<FilterOption> filters = Collections.singletonList(getActionTypeFilterOption(actionType));\n        final List<OrderByOption> orders = Collections\n                .singletonList(new OrderByOption(SQueriableLog.class, \"id\", OrderByType.ASC));\n        return loggerService.searchLogs(new QueryOptions(0, 10, orders, filters, null));\n    }\n\n    private void checkSUserImpl(final SUser expectedSUser, final SUser retrievedSUser) {\n        assertNotNull(retrievedSUser);\n        assertEquals(expectedSUser, retrievedSUser);\n    }\n\n    private SUser getUserByUsername(final String firstName) throws SBonitaReadException {\n        return getPersistenceService()\n                .selectOne(new SelectOneDescriptor<SUser>(\"getUserByUserName\",\n                        Collections.singletonMap(\"userName\", (Object) firstName), SUser.class));\n    }\n\n    @Test\n    public void testNotLogOnInsertRecordWhenBTXRolledBack() throws Exception {\n        getTransactionService().begin();\n        final SelectOneDescriptor<SUser> selectDescriptor = new SelectOneDescriptor<>(\"getUserByUserName\",\n                Collections.singletonMap(\"userName\", (Object) \"firstName\"), SUser.class);\n        SUser retrievedSUser = getPersistenceService().selectOne(selectDescriptor);\n        assertNull(\"Should not have any SUSER_IMPL in DB before test\", retrievedSUser);\n\n        final String firstName = \"Laurent\";\n        final SUser sUser = buildSUserImpl(firstName, \"Vaills\");\n        recorder.recordInsert(new InsertRecord(sUser), SUSER_IMPL);\n\n        // set rollback\n        getTransactionService().setRollbackOnly();\n        getTransactionService().complete();\n\n        Thread.sleep(SLEEP_TIME);\n\n        // The transaction has been rolled back no SUserImpl nor log should have been inserted.\n        getTransactionService().begin();\n\n        retrievedSUser = getPersistenceService().selectOne(selectDescriptor);\n        assertNull(retrievedSUser);\n\n        final List<SQueriableLog> retrievedLogs = getLogs(TEST_CREATED);\n        assertEquals(0, retrievedLogs.size());\n\n        getTransactionService().complete();\n    }\n\n    @Test\n    public void testNotLogOnUpdateRecordWhenBTXRolledBack() throws Exception {\n        // add sUserImpl using persistence service\n        getTransactionService().begin();\n\n        final SUser sUser = buildSUserImpl(\"firstName\", \"lastName\");\n        recorder.recordInsert(new InsertRecord(sUser), SUSER_IMPL);\n        getTransactionService().complete();\n\n        // update sUserImpl (fail)\n        getTransactionService().begin();\n\n        final SUser sUserToUpdate = getUserByUsername(\"firstName\");\n        assertNotNull(sUserToUpdate);\n        final Map<String, Object> stringObjectMap = Collections.singletonMap(\"userName\", \"firstName\");\n        recorder.recordUpdate(UpdateRecord.buildSetFields(sUserToUpdate, stringObjectMap), SUSER_IMPL);\n        getTransactionService().setRollbackOnly();\n        getTransactionService().complete();\n\n        // wait\n        Thread.sleep(SLEEP_TIME);\n\n        // query\n        getTransactionService().begin();\n        final SUser retrivedSUser = getPersistenceService().selectOne(\n                new SelectOneDescriptor<SUser>(\"getUserByUserName\",\n                        Collections.singletonMap(\"userName\", (Object) \"firstNameUpdate\"), SUser.class));\n        assertNull(retrivedSUser);\n\n        final List<SQueriableLog> retrievedLogs = getLogs(TEST_UPDATE);\n        assertEquals(0, retrievedLogs.size());\n\n        final SUser addedSUser = getUserByUsername(\"firstName\");\n        recorder.recordDelete(new DeleteRecord(addedSUser), SUSER_IMPL);\n\n        getTransactionService().complete();\n    }\n\n    @Test\n    public void testNotLogOnDeleteRecordIfBTXRolledBack() throws Exception {\n        // add sUserImpl using persistence service\n        getTransactionService().begin();\n        final SUser sUser = buildSUserImpl(\"firstName\", \"lastName\");\n        recorder.recordInsert(new InsertRecord(sUser), SUSER_IMPL);\n        getTransactionService().complete();\n\n        // delete sUserImpl using recorder\n        getTransactionService().begin();\n\n        final SUser sUserToDelete = getUserByUsername(\"firstName\");\n        assertNotNull(sUserToDelete);\n        recorder.recordDelete(new DeleteRecord(sUserToDelete), SUSER_IMPL);\n        getTransactionService().setRollbackOnly();\n        getTransactionService().complete();\n\n        // wait\n        Thread.sleep(SLEEP_TIME);\n\n        // query\n        getTransactionService().begin();\n        final SUser retrievedSUser = getUserByUsername(\"firstName\");\n        checkSUserImpl(sUser, retrievedSUser);\n\n        final List<SQueriableLog> retrievedLogs = getLogs(retrievedSUser.getId(), TEST_DELETE);\n        assertEquals(0, retrievedLogs.size());\n\n        getTransactionService().complete();\n\n        // clean up:\n        getTransactionService().begin();\n        recorder.recordDelete(new DeleteRecord(retrievedSUser), SUSER_IMPL);\n        getTransactionService().complete();\n    }\n\n    protected ReadPersistenceService getPersistenceService() {\n        return persistenceService;\n    }\n\n    protected TestLogBuilderFactory getLogModelBuilderFactory() {\n        if (logModelBuilderFactory == null) {\n            logModelBuilderFactory = new TestLogBuilderFactory();\n        }\n        return logModelBuilderFactory;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/resources/TenantResourcesServiceIT.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.resources;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.bpm.CommonBPMServicesTest;\nimport org.bonitasoft.engine.transaction.TransactionService;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Baptiste Mesta\n */\npublic class TenantResourcesServiceIT extends CommonBPMServicesTest {\n\n    private static TenantResourcesService tenantResourcesService;\n\n    private static TransactionService transactionService;\n\n    @Before\n    public void before() {\n        tenantResourcesService = getServiceAccessor().getTenantResourcesService();\n        transactionService = getTransactionService();\n    }\n\n    @After\n    public void after() throws Exception {\n        transactionService.executeInTransaction(() -> {\n            tenantResourcesService.removeAll(TenantResourceType.BDM);\n            return null;\n        });\n    }\n\n    @Test\n    public void should_create_and_get_resource_work() throws Exception {\n        transactionService.begin();\n        //given\n        tenantResourcesService.add(\"myResource\", TenantResourceType.BDM, \"theResourceContent\".getBytes(), -1);\n        //when\n        transactionService.complete();\n        transactionService.begin();\n        STenantResource myResource = tenantResourcesService.get(TenantResourceType.BDM, \"myResource\");\n        //then\n        assertThat(myResource.getName()).isEqualTo(\"myResource\");\n        assertThat(myResource.getType()).isEqualTo(TenantResourceType.BDM);\n        assertThat(new String(myResource.getContent())).isEqualTo(\"theResourceContent\");\n        transactionService.complete();\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/scheduler/impl/JobThatMayThrowErrorOrJobException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.impl;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.commons.exceptions.SRetryableException;\nimport org.bonitasoft.engine.scheduler.exception.SJobExecutionException;\nimport org.bonitasoft.engine.scheduler.job.GroupJob;\nimport org.bonitasoft.engine.scheduler.job.VariableStorage;\n\npublic class JobThatMayThrowErrorOrJobException extends GroupJob {\n\n    static final String TYPE = \"type\";\n    static final String ERROR = \"ERROR\";\n    static final String JOBEXCEPTION = \"JOBEXCEPTION\";\n    static final String NO_EXCEPTION = \"NO_EXCEPTION\";\n    static final String FAIL_ONCE = \"FAIL_ONCE\";\n    static final String FAIL_ONCE_WITH_RETRYABLE = \"FAIL_ONCE_WITH_RETRYABLE\";\n    private boolean throwsJobExecutionException;\n    private boolean throwsError;\n    private boolean failOnce;\n    private boolean failOnceWithRetryable;\n    private final VariableStorage variableStorage = VariableStorage.getInstance();\n\n    @Override\n    public String getDescription() {\n        return \"throw error\";\n    }\n\n    @Override\n    public void setAttributes(Map<String, Serializable> attributes) {\n        super.setAttributes(attributes);\n        Serializable type = attributes.get(TYPE);\n        throwsJobExecutionException = JOBEXCEPTION.equals(type);\n        throwsError = ERROR.equals(type);\n        failOnce = FAIL_ONCE.equals(type);\n        failOnceWithRetryable = FAIL_ONCE_WITH_RETRYABLE.equals(type);\n    }\n\n    @Override\n    public void execute() throws SJobExecutionException {\n        if (failOnce) {\n            if (variableStorage.getVariableValue(\"nbJobException\", 0) == 0) {\n                variableStorage.setVariable(\"nbJobException\", 1);\n                throw new SJobExecutionException(\"Failing only once\");\n            }\n        }\n        if (failOnceWithRetryable) {\n            if (variableStorage.getVariableValue(\"nbJobException\", 0) == 0) {\n                variableStorage.setVariable(\"nbJobException\", 1);\n                throw new SRetryableException(\"Failing only once\");\n            }\n        }\n        if (throwsError) {\n            variableStorage.setVariable(\"nbError\", variableStorage.getVariableValue(\"nbError\", 0) + 1);\n            throw new Error(\"an Error\");\n        }\n        if (throwsJobExecutionException) {\n            variableStorage.setVariable(\"nbJobException\", variableStorage.getVariableValue(\"nbJobException\", 0) + 1);\n            throw new SJobExecutionException(\"a Job exception\");\n        }\n        variableStorage.setVariable(\"nbSuccess\", variableStorage.getVariableValue(\"nbSuccess\", 0) + 1);\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/scheduler/impl/SchedulerServiceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.impl;\n\nimport static java.util.Collections.singletonMap;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.awaitility.Awaitility.await;\nimport static org.awaitility.Durations.*;\nimport static org.bonitasoft.engine.scheduler.impl.JobThatMayThrowErrorOrJobException.*;\nimport static org.hamcrest.collection.IsCollectionWithSize.hasSize;\nimport static org.junit.Assert.*;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.Callable;\nimport java.util.stream.Collectors;\n\nimport org.bonitasoft.engine.bpm.CommonBPMServicesTest;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.scheduler.JobService;\nimport org.bonitasoft.engine.scheduler.SchedulerService;\nimport org.bonitasoft.engine.scheduler.job.ReleaseWaitersJob;\nimport org.bonitasoft.engine.scheduler.job.VariableStorage;\nimport org.bonitasoft.engine.scheduler.model.SFailedJob;\nimport org.bonitasoft.engine.scheduler.model.SJobDescriptor;\nimport org.bonitasoft.engine.scheduler.model.SJobParameter;\nimport org.bonitasoft.engine.scheduler.trigger.OneShotTrigger;\nimport org.bonitasoft.engine.scheduler.trigger.Trigger;\nimport org.bonitasoft.engine.scheduler.trigger.UnixCronTrigger;\nimport org.bonitasoft.engine.scheduler.trigger.UnixCronTriggerForTest;\nimport org.bonitasoft.engine.test.util.TestUtil;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.bonitasoft.engine.util.FunctionalMatcher;\nimport org.hamcrest.BaseMatcher;\nimport org.hamcrest.Description;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class SchedulerServiceIT extends CommonBPMServicesTest {\n\n    private SchedulerService schedulerService;\n    private JobService jobService;\n    private UserTransactionService userTransactionService;\n    private final VariableStorage storage = VariableStorage.getInstance();\n\n    @Before\n    public void before() throws Exception {\n        schedulerService = getServiceAccessor().getSchedulerService();\n        userTransactionService = getServiceAccessor().getUserTransactionService();\n        jobService = getServiceAccessor().getJobService();\n        TestUtil.stopScheduler(schedulerService, getTransactionService());\n        TestUtil.startScheduler(schedulerService);\n    }\n\n    @After\n    public void after() throws Exception {\n        storage.clear();\n        userTransactionService.executeInTransaction(() -> {\n            schedulerService.deleteJobs();\n            return null;\n        });\n    }\n\n    @Test\n    public void canRestartTheSchedulerAfterShutdown() throws Exception {\n        schedulerService.stop();\n        assertTrue(schedulerService.isStopped());\n        schedulerService.start();\n        assertTrue(schedulerService.isStarted());\n    }\n\n    @Test\n    public void doNotExecuteAFutureJob() throws Exception {\n        final Date future = new Date(System.currentTimeMillis() + 10000000);\n        final String variableName = \"myVar\";\n        final SJobDescriptor jobDescriptor = SJobDescriptor.builder()\n                .jobClassName(\"org.bonitasoft.engine.scheduler.job.IncrementVariableJob\")\n                .jobName(\"IncrementVariableJob\").build();\n        final List<SJobParameter> parameters = new ArrayList<>();\n        parameters.add(SJobParameter.builder().key(\"jobName\").value(\"testDoNotExecuteAFutureJob\").build());\n        parameters.add(SJobParameter.builder().key(\"variableName\").value(variableName).build());\n        parameters.add(SJobParameter.builder().key(\"throwExceptionAfterNIncrements\").value(-1).build());\n        final Trigger trigger = new OneShotTrigger(\"events\", future, 10);\n        getTransactionService().begin();\n        schedulerService.schedule(jobDescriptor, parameters, trigger);\n        getTransactionService().complete();\n        Thread.sleep(200);\n        assertNull(storage.getVariableValue(variableName));\n    }\n\n    @Test\n    public void doNotThrowAnExceptionWhenDeletingAnUnknownJob() throws Exception {\n        getTransactionService().begin();\n        final boolean deleted = schedulerService.delete(\"MyJob\");\n        getTransactionService().complete();\n        assertFalse(deleted);\n    }\n\n    /*\n     * We must ensure that:\n     * * pause jobs\n     * * trigger new job are not executed\n     * * resume the jobs resume it really\n     */\n    @Test\n    public void pause_and_resume_jobs_of_a_tenant() throws Exception {\n        final String jobName = \"ReleaseWaitersJob\";\n        Date now = new Date();\n        SJobDescriptor jobDescriptor = SJobDescriptor.builder()\n                .jobClassName(ReleaseWaitersJob.class.getName()).jobName(jobName + \"1\").build();\n        List<SJobParameter> parameters = new ArrayList<>();\n        parameters.add(SJobParameter.builder().key(\"jobName\").value(jobName).build());\n        parameters.add(SJobParameter.builder().key(\"jobKey\").value(\"1\").build());\n        Trigger trigger = new UnixCronTriggerForTest(\"events\", now, 10, \"0/1 * * * * ?\");\n\n        // trigger it\n        getTransactionService().begin();\n        schedulerService.schedule(jobDescriptor, parameters, trigger);\n        getTransactionService().complete();\n        ReleaseWaitersJob.waitForJobToExecuteOnce();\n\n        // pause\n        getTransactionService().begin();\n        schedulerService.pauseJobs();\n        getTransactionService().complete();\n        Thread.sleep(100);\n        ReleaseWaitersJob.checkNotExecutedDuring(1500);\n    }\n\n    @Test\n    public void should_be_able_to_list_job_that_failed_because_of_an_Error() throws Exception {\n        // schedule a job that throws an Error\n        schedule(jobDescriptor(JobThatMayThrowErrorOrJobException.class, \"MyJob\"),\n                new OneShotTrigger(\"triggerJob\", new Date(System.currentTimeMillis() + 100)),\n                singletonMap(TYPE, ERROR));\n\n        //we have failed job\n        List<SFailedJob> failedJobs = await().atMost(ONE_MINUTE).pollInterval(ONE_SECOND)\n                .until(() -> inTx(() -> jobService.getFailedJobs(0, 100)), hasSize(1));\n        assertThat(failedJobs).hasOnlyOneElementSatisfying(f -> assertThat(f.getLastMessage()).contains(\"an Error\"));\n    }\n\n    @Test\n    public void should_be_able_to_restart_a_job_that_failed_because_of_a_SJobExecutionException() throws Exception {\n        // schedule a job that throws a SJobExecutionException\n        schedule(jobDescriptor(JobThatMayThrowErrorOrJobException.class, \"MyJob\"),\n                new OneShotTrigger(\"triggerJob\", new Date(System.currentTimeMillis() + 10)),\n                singletonMap(TYPE, JOBEXCEPTION));\n        SJobDescriptor persistedJobDescriptor = getFirstPersistedJob();\n\n        // we should have a failed job:\n        // Job can trigger in up to 'org.quartz.scheduler.idleWaitTime' milliseconds, so better wait long enough:\n        List<SFailedJob> failedJobs = await().atMost(ONE_MINUTE).pollInterval(ONE_SECOND).until(() -> {\n            System.out.println(\"condition not here yet. Waiting again...\");\n            return inTx(() -> jobService.getFailedJobs(0, 3));\n        }, hasSize(1));\n        assertThat(failedJobs.get(0).getLastMessage()).contains(\"a Job exception\");\n\n        // small sleep because quartz does not always immediately delete the associated trigger (done in the quartz Thread)\n        // because of that, it can cause issues when rescheduling (Foreign key violation)\n        Thread.sleep(100);\n        // reschedule the job: should be no more exception\n        inTx(() -> {\n            schedulerService.retryJobThatFailed(persistedJobDescriptor.getId(),\n                    toJobParameterList(singletonMap(TYPE, NO_EXCEPTION)));\n            return null;\n        });\n        await().pollDelay(FIVE_HUNDRED_MILLISECONDS).atMost(ONE_MINUTE).pollInterval(ONE_SECOND)\n                .until(() -> storage.getVariableValue(\"nbSuccess\", 0).equals(1));\n    }\n\n    @Test\n    public void should_be_able_to_restart_a_cron_job_that_failed_because_of_a_SJobExecutionException()\n            throws Exception {\n        // schedule a job that throws a SJobExecutionException\n        schedule(jobDescriptor(JobThatMayThrowErrorOrJobException.class, \"MyJob\"),\n                new UnixCronTrigger(\"triggerJob\", new Date(System.currentTimeMillis() + 100), \"* * * * * ?\"),\n                singletonMap(TYPE, JOBEXCEPTION));\n        SJobDescriptor persistedJobDescriptor = getFirstPersistedJob();\n\n        //ensure there is more than one failure: i.e. cron is still triggering new jobs\n        await().atMost(ONE_MINUTE).pollInterval(ONE_SECOND).until(() -> storage.getVariableValue(\"nbJobException\", 0),\n                isGreaterThan(1));\n        //wait a little because the failure is registered later...\n        Callable<List<SFailedJob>> getFailedJobs = () -> {\n            try {\n                return inTx(() -> jobService.getFailedJobs(0, 100));\n            } catch (Exception e) {\n                throw new RuntimeException(e);\n            }\n        };\n        List<SFailedJob> sFailedJobs = await().until(getFailedJobs, new BaseMatcher<>() {\n\n            @Override\n            public boolean matches(Object item) {\n                List<SFailedJob> list = (List<SFailedJob>) item;\n                return list.size() == 1 && list.get(0).getNumberOfFailures() > 1;\n            }\n\n            @Override\n            public void describeTo(Description description) {\n\n            }\n        });\n\n        assertThat(sFailedJobs).hasSize(1);\n        //ensure we trace the number of failure\n        assertThat(sFailedJobs.get(0).getNumberOfFailures()).isGreaterThan(1);\n\n        //reschedule the job: no more exception\n        inTx(() -> {\n            schedulerService.retryJobThatFailed(persistedJobDescriptor.getId(),\n                    toJobParameterList(singletonMap(TYPE, NO_EXCEPTION)));\n            return null;\n        });\n\n        //ensure there is more than one success: i.e. cron is still triggering new jobs\n        await().atMost(ONE_MINUTE).pollInterval(ONE_SECOND).until(() -> storage.getVariableValue(\"nbSuccess\", 0),\n                isGreaterThan(1));\n        //ensure no more failed job is present\n        assertThat(inTx(() -> jobService.getFailedJobs(0, 100))).isEmpty();\n    }\n\n    @Test\n    public void should_keep_a_failed_job_when_failing_once() throws Exception {\n        // schedule a job that throws a SJobExecutionException\n        schedule(jobDescriptor(JobThatMayThrowErrorOrJobException.class, \"MyJob\"),\n                new UnixCronTrigger(\"triggerJob\", new Date(System.currentTimeMillis() + 100), \"* * * * * ?\"),\n                singletonMap(TYPE, FAIL_ONCE));\n        SJobDescriptor persistedJobDescriptor = getFirstPersistedJob();\n\n        //this job fail only the first time\n        await().atMost(ONE_MINUTE).pollInterval(ONE_SECOND).until(() -> storage.getVariableValue(\"nbJobException\", 0),\n                isGreaterThan(0));\n        await().atMost(ONE_MINUTE).pollInterval(ONE_SECOND).until(() -> storage.getVariableValue(\"nbSuccess\", 0),\n                isGreaterThan(0));\n\n        List<SFailedJob> sFailedJobs = inTx(() -> jobService.getFailedJobs(0, 100));\n\n        assertThat(sFailedJobs).hasSize(1);\n        //ensure we trace the number of failure\n        assertThat(sFailedJobs.get(0).getNumberOfFailures()).isEqualTo(1);\n\n        //reschedule the job: no more exception\n        inTx(() -> {\n            schedulerService.retryJobThatFailed(persistedJobDescriptor.getId(),\n                    toJobParameterList(singletonMap(TYPE, NO_EXCEPTION)));\n            return null;\n        });\n\n        //ensure there is more than one success: i.e. cron is still triggering new jobs\n        await().atMost(ONE_MINUTE).pollInterval(ONE_SECOND).until(() -> storage.getVariableValue(\"nbSuccess\", 0),\n                isGreaterThan(1));\n        //ensure no more failed job is present\n        assertThat(inTx(() -> jobService.getFailedJobs(0, 100))).isEmpty();\n    }\n\n    @Test\n    public void should_let_quartz_retry_a_job_that_failed_because_of_a_SRetryableException() throws Exception {\n        // schedule one shot job that throws a SJobExecutionException\n        schedule(jobDescriptor(JobThatMayThrowErrorOrJobException.class, \"MyJob\"),\n                new OneShotTrigger(\"triggerJob\", new Date(System.currentTimeMillis() + 100)),\n                singletonMap(TYPE, FAIL_ONCE_WITH_RETRYABLE));\n        SJobDescriptor persistedJobDescriptor = getFirstPersistedJob();\n\n        //this job fail once and is immediately retried, even if its a one shot job\n        await().atMost(ONE_MINUTE).pollInterval(ONE_SECOND).until(() -> storage.getVariableValue(\"nbJobException\", 0),\n                isGreaterThan(0));\n        await().atMost(ONE_MINUTE).pollInterval(ONE_SECOND).until(() -> storage.getVariableValue(\"nbSuccess\", 0),\n                isGreaterThan(0));\n\n        List<SFailedJob> sFailedJobs = inTx(() -> jobService.getFailedJobs(0, 100));\n\n        //no error traced, it was retried\n        assertThat(sFailedJobs).hasSize(0);\n    }\n\n    private FunctionalMatcher<Integer> isGreaterThan(int i) {\n        return t -> t > i;\n    }\n\n    private SJobDescriptor getFirstPersistedJob() throws Exception {\n        return inTx(() -> jobService.searchJobDescriptors(new QueryOptions(0, 1))).get(0);\n    }\n\n    private <T> T inTx(Callable<T> callable) throws Exception {\n\n        return userTransactionService.executeInTransaction(() -> callable.call());\n    }\n\n    private SJobDescriptor jobDescriptor(Class<?> jobClass, String jobName) {\n        return SJobDescriptor.builder()\n                .jobClassName(jobClass.getName()).jobName(jobName).build();\n    }\n\n    private void schedule(SJobDescriptor jobDescriptor, Trigger trigger, Map<String, Serializable> parameters)\n            throws Exception {\n        List<SJobParameter> parametersList = toJobParameterList(parameters);\n        inTx(() -> {\n            schedulerService.schedule(jobDescriptor, parametersList, trigger);\n            return null;\n        });\n    }\n\n    private List<SJobParameter> toJobParameterList(Map<String, Serializable> parameters) {\n        return parameters.entrySet().stream()\n                .map(e -> SJobParameter.builder().key(e.getKey()).value(e.getValue()).build())\n                .collect(Collectors.toList());\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/scheduler/impl/WaitForIncrementJobToHaveValue.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.impl;\n\nimport static org.junit.Assert.assertTrue;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.scheduler.job.IncrementItselfJob;\n\n/**\n * @author Baptiste Mesta\n */\npublic class WaitForIncrementJobToHaveValue {\n\n    private final int value;\n    private final int timeout;\n    private final int repeatEach;\n\n    public WaitForIncrementJobToHaveValue(final int repeatEach, final int timeout, int value) {\n        assertTrue(\"timeout is not big enough\", repeatEach < timeout);\n        this.repeatEach = repeatEach;\n        this.timeout = timeout;\n        this.value = value;\n    }\n\n    /**\n     * @param timeout\n     * @param value\n     */\n    public WaitForIncrementJobToHaveValue(final int timeout, final int value) {\n        this(10, timeout, value);\n    }\n\n    boolean check() {\n        return IncrementItselfJob.getValue() == value;\n    }\n\n    public boolean waitFor() throws InterruptedException {\n        final long limit = new Date().getTime() + timeout;\n        while (new Date().getTime() < limit) {\n            Thread.sleep(repeatEach);\n            if (check()) {\n                return true;\n            }\n        }\n        return check();\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/scheduler/job/DoNothingJob.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.job;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.scheduler.StatelessJob;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class DoNothingJob implements StatelessJob {\n\n    private static final long serialVersionUID = 5253574298401130601L;\n\n    @Override\n    public String getName() {\n        return \"doNothing\";\n    }\n\n    @Override\n    public String getDescription() {\n        return \"DoNothing\";\n    }\n\n    @Override\n    public void execute() {\n    }\n\n    @Override\n    public void setAttributes(final Map<String, Serializable> attributes) {\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/scheduler/job/GroupJob.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.job;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.scheduler.StatelessJob;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic abstract class GroupJob implements StatelessJob {\n\n    private static final long serialVersionUID = 1L;\n\n    private String jobName;\n\n    @Override\n    public String getName() {\n        return jobName;\n    }\n\n    @Override\n    public void setAttributes(final Map<String, Serializable> attributes) {\n        jobName = (String) attributes.get(\"jobName\");\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/scheduler/job/IncrementItselfJob.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.job;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @author Baptiste Mesta\n */\npublic class IncrementItselfJob extends GroupJob {\n\n    private static final long serialVersionUID = 3707724945060118636L;\n\n    private static int value = 0;\n\n    private static List<Date> executionDates = new ArrayList<Date>();\n\n    @Override\n    public void execute() {\n        value++;\n        addToExecutionDates(new Date(System.currentTimeMillis()));\n    }\n\n    public static int getValue() {\n        return value;\n    }\n\n    public static void reset() {\n        value = 0;\n        executionDates = new ArrayList<Date>();\n    }\n\n    public static synchronized List<Date> getExecutionDates() {\n        return new ArrayList<Date>(executionDates);\n    }\n\n    synchronized void addToExecutionDates(Date date) {\n        executionDates.add(date);\n    }\n\n    @Override\n    public String getDescription() {\n        return \"Increment itself \";\n    }\n\n    @Override\n    public void setAttributes(final Map<String, Serializable> attributes) {\n        super.setAttributes(attributes);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/scheduler/job/IncrementVariableJob.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.job;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.scheduler.exception.SJobExecutionException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class IncrementVariableJob extends GroupJob {\n\n    private static final long serialVersionUID = 3707724945060118636L;\n\n    private String variableName;\n\n    private int throwExceptionAfterNIncrements;\n\n    @Override\n    public void execute() throws SJobExecutionException {\n        synchronized (IncrementVariableJob.class) {\n\n            final VariableStorage storage = VariableStorage.getInstance();\n            final Integer value = (Integer) storage.getVariableValue(variableName);\n            if (value == null) {\n                storage.setVariable(variableName, 1);\n            } else if (value + 1 == throwExceptionAfterNIncrements) {\n                throw new SJobExecutionException(\"Increment reached\");\n            } else {\n                storage.setVariable(variableName, value + 1);\n            }\n        }\n    }\n\n    @Override\n    public String getDescription() {\n        return \"Increment the variable \" + variableName;\n    }\n\n    @Override\n    public void setAttributes(final Map<String, Serializable> attributes) {\n        super.setAttributes(attributes);\n        variableName = (String) attributes.get(\"variableName\");\n        throwExceptionAfterNIncrements = (Integer) attributes.get(\"throwExceptionAfterNIncrements\");\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/scheduler/job/JobSemaphore.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.job;\n\nimport java.util.concurrent.Semaphore;\n\nclass JobSemaphore extends Semaphore {\n\n    /**\n     *\n     */\n    private static final long serialVersionUID = 1L;\n\n    String key;\n\n    public JobSemaphore(final int permits) {\n        super(permits);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/scheduler/job/ReleaseWaitersJob.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.job;\n\nimport java.io.Serializable;\nimport java.util.Date;\nimport java.util.Map;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * @author Baptiste Mesta\n */\npublic class ReleaseWaitersJob extends GroupJob {\n\n    private static final long serialVersionUID = 3707724945060118636L;\n\n    private static JobSemaphore semaphore;\n\n    private static String jobKey;\n\n    @Override\n    public void execute() {\n        System.out.println(\"ReleaseWaitersJob EXECUTES at time \" + new Date().getSeconds());\n        if (semaphore != null) {\n            semaphore.key = jobKey;\n            semaphore.release();\n        }\n    }\n\n    @Override\n    public String getDescription() {\n        return \"release a semaphore\";\n    }\n\n    @Override\n    public void setAttributes(final Map<String, Serializable> attributes) {\n        super.setAttributes(attributes);\n        jobKey = (String) attributes.get(\"jobKey\");\n    }\n\n    /*\n     * create a new semaphore and wait for the release to be called\n     */\n    public static void waitForJobToExecuteOnce() throws Exception {\n        semaphore = new JobSemaphore(1);\n        semaphore.acquire();\n        boolean acquired = semaphore.tryAcquire(30, TimeUnit.SECONDS);\n        if (!acquired) {\n            throw new Exception(\"job was not triggered\");\n        }\n    }\n\n    public static void checkNotExecutedDuring(final int milliseconds) throws Exception {\n        semaphore = new JobSemaphore(1);\n        semaphore.acquire();\n        boolean acquired = semaphore.tryAcquire(milliseconds, TimeUnit.MILLISECONDS);\n        if (acquired) {\n            throw new Exception(\"job \" + jobKey + \" was triggered\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/scheduler/job/ThrowsExceptionJob.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.job;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.scheduler.StatelessJob;\nimport org.bonitasoft.engine.scheduler.exception.SJobExecutionException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ThrowsExceptionJob implements StatelessJob {\n\n    private static final long serialVersionUID = 1L;\n\n    public static final String THROW_EXCEPTION = \"throwException\";\n\n    private Boolean throwException = null;\n\n    @Override\n    public String getDescription() {\n        return \"Job that throws an exception\";\n    }\n\n    @Override\n    public void execute() throws SJobExecutionException {\n        if (throwException != null && throwException) {\n            throw new SJobExecutionException(\"This job throws an arbitrary exception\");\n        }\n    }\n\n    @Override\n    public String getName() {\n        return \"ThrowsExceptionJob\";\n    }\n\n    @Override\n    public void setAttributes(final Map<String, Serializable> attributes) {\n        final Boolean result = (Boolean) attributes.get(THROW_EXCEPTION);\n        if (result != null) {\n            throwException = result;\n        }\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/scheduler/job/UpdateVariable.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.job;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class UpdateVariable extends GroupJob {\n\n    private static final long serialVersionUID = 8379781766551862114L;\n\n    private String variableName;\n\n    private Object variableValue;\n\n    public UpdateVariable(final String variableName, final Object variableValue) {\n        super();\n        this.variableName = variableName;\n        this.variableValue = variableValue;\n    }\n\n    @Override\n    public void execute() {\n        final VariableStorage storage = VariableStorage.getInstance();\n        storage.setVariable(variableName, variableValue);\n    }\n\n    @Override\n    public String getDescription() {\n        return \"Change the value of \" + variableName + \" with \" + variableValue;\n    }\n\n    @Override\n    public void setAttributes(final Map<String, Serializable> attributes) {\n        super.setAttributes(attributes);\n        variableName = (String) attributes.get(\"variableName\");\n        variableValue = attributes.get(\"variableValue\");\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/scheduler/job/VariableStorage.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.job;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class VariableStorage implements Serializable {\n\n    private static final long serialVersionUID = -4195221111626812999L;\n\n    private final Object lock = new Object();\n    private final Map<String, Object> variables;\n    public static final VariableStorage INSTANCE = new VariableStorage();\n\n    private VariableStorage() {\n        variables = new HashMap<>();\n    }\n\n    public static VariableStorage getInstance() {\n        return INSTANCE;\n    }\n\n    public void setVariable(final String name, final Object value) {\n        synchronized (lock) {\n            variables.put(name, value);\n        }\n    }\n\n    public Object getVariableValue(final String name) {\n        return getVariableValue(name, null);\n    }\n\n    public <T> T getVariableValue(final String name, T defaultValue) {\n        synchronized (lock) {\n            return ((T) variables.getOrDefault(name, defaultValue));\n        }\n    }\n\n    public static void clearAll() {\n        INSTANCE.clear();\n    }\n\n    public void clear() {\n        synchronized (lock) {\n            variables.clear();\n        }\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/scheduler/trigger/UnixCronTriggerForTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.trigger;\n\nimport java.util.Date;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class UnixCronTriggerForTest extends OneShotTrigger implements CronTrigger {\n\n    private final String expression;\n\n    private final Date endDate;\n\n    public UnixCronTriggerForTest(final String name, final Date startDate, final int priority,\n            final String expression) {\n        super(name, startDate, priority);\n        this.expression = expression;\n        this.endDate = null;\n    }\n\n    public UnixCronTriggerForTest(final String name, final Date startDate, final int priority, final String expression,\n            final MisfireRestartPolicy misfireRestartPolicy) {\n        super(name, startDate, priority, misfireRestartPolicy);\n        this.expression = expression;\n        this.endDate = null;\n    }\n\n    public UnixCronTriggerForTest(final String name, final Date startDate, final int priority, final String expression,\n            final Date endDate) {\n        super(name, startDate, priority);\n        this.expression = expression;\n        this.endDate = endDate;\n    }\n\n    @Override\n    public String getExpression() {\n        return this.expression;\n    }\n\n    @Override\n    public Date getEndDate() {\n        return this.endDate;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/session/PlatformSessionServiceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.session;\n\nimport static org.junit.Assert.*;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.bpm.CommonBPMServicesTest;\nimport org.bonitasoft.engine.platform.session.PlatformSessionService;\nimport org.bonitasoft.engine.platform.session.SSessionNotFoundException;\nimport org.bonitasoft.engine.platform.session.model.SPlatformSession;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class PlatformSessionServiceIT extends CommonBPMServicesTest {\n\n    private static PlatformSessionService sessionService;\n\n    @Before\n    public void before() {\n        sessionService = getServiceAccessor().getPlatformSessionService();\n    }\n\n    @Test\n    public void testCreateSession() throws Exception {\n        final String username = \"platformAdmin\";\n        final Date before = new Date();\n        final SPlatformSession session = sessionService.createSession(username);\n        assertNotNull(session);\n        assertNotNull(session.getCreationDate());\n        assertTrue(before.getTime() <= session.getCreationDate().getTime());\n        assertTrue(session.getDuration() > 0);\n        assertEquals(sessionService.getSessionsDuration(), session.getDuration());\n        assertEquals(session.getLastRenewDate().getTime() + session.getDuration(),\n                session.getExpirationDate().getTime());\n        assertEquals(username, session.getUserName());\n    }\n\n    @Test\n    public void testIsValid() throws Exception {\n        final String username = \"platformAdmin\";\n        long sessionsDuration = sessionService.getSessionsDuration();\n        try {\n            sessionService.setSessionDuration(1000);\n            final SPlatformSession session = sessionService.createSession(username);\n            assertNotNull(session);\n            assertEquals(1000, session.getDuration());\n            assertTrue(sessionService.isValid(session.getId()));\n            Thread.sleep(session.getDuration() + 1);\n            assertFalse(sessionService.isValid(session.getId()));\n        } finally {\n            sessionService.setSessionDuration(sessionsDuration);\n        }\n    }\n\n    @Test\n    public void testGetSession() throws Exception {\n        final String username = \"platformAdmin\";\n        final SPlatformSession session = sessionService.createSession(username);\n        assertNotNull(session);\n        final SPlatformSession retrievedSession = sessionService.getSession(session.getId());\n        assertEquals(session, retrievedSession);\n    }\n\n    @Test(expected = SSessionNotFoundException.class)\n    public void testDeleteInvalidSession() throws Exception {\n        sessionService.deleteSession(System.currentTimeMillis());\n    }\n\n    @Test(expected = SSessionNotFoundException.class)\n    public void testDeleteSession() throws Exception {\n        final String username = \"platformAdmin\";\n        final SPlatformSession session = sessionService.createSession(username);\n        assertNotNull(session);\n        SPlatformSession retrievedSession = sessionService.getSession(session.getId());\n        assertEquals(session, retrievedSession);\n\n        sessionService.deleteSession(session.getId());\n        sessionService.getSession(session.getId());\n    }\n\n    @Test\n    public void testrenewSession() throws Exception {\n        final String username = \"matti\";\n        final SPlatformSession session = sessionService.createSession(username);\n        Thread.sleep(100);\n\n        sessionService.renewSession(session.getId());\n\n        final SPlatformSession session2 = sessionService.getSession(session.getId());\n        assertTrue(session2.getExpirationDate().after(session.getExpirationDate()));\n        assertTrue(session2.getLastRenewDate().after(session.getLastRenewDate()));\n        assertEquals(session2.getLastRenewDate().getTime() + session2.getDuration(),\n                session2.getExpirationDate().getTime());\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/session/SessionServiceIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.session;\n\nimport static org.junit.Assert.*;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.bpm.CommonBPMServicesTest;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.session.model.SSession;\nimport org.bonitasoft.engine.test.util.TestUtil;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Elias Ricken de Medeiros, Yanyan Liu\n */\npublic class SessionServiceIT extends CommonBPMServicesTest {\n\n    private SessionService sessionService;\n\n    @Before\n    public void setup() {\n        sessionService = getServiceAccessor().getSessionService();\n        sessionService.setSessionDuration(sessionService.getDefaultSessionDuration());\n    }\n\n    @After\n    public void tearDown() throws Exception {\n        TestUtil.closeTransactionIfOpen(getTransactionService());\n        getSessionAccessor().deleteSessionId();\n    }\n\n    @Test\n    public void testSessionUserIdForUnknownUser() throws Exception {\n        final String username = \"DonaldDuck\";\n        final SSession session = createSession(username);\n        assertNotNull(session);\n        assertEquals(-1, session.getUserId());\n    }\n\n    @Test\n    public void testCreateSession() throws Exception {\n        final String username = \"john\";\n        final Date before = new Date();\n        final SSession session = createSession(username);\n        assertNotNull(session);\n        assertNotNull(session.getCreationDate());\n        assertTrue(before.getTime() <= session.getCreationDate().getTime());\n        assertTrue(session.getDuration() > 0);\n        assertEquals(sessionService.getSessionDuration(), session.getDuration());\n        assertEquals(session.getLastRenewDate().getTime() + session.getDuration(),\n                session.getExpirationDate().getTime());\n        assertEquals(username, session.getUserName());\n    }\n\n    private SSession createSession(final String username) throws SBonitaException {\n        getTransactionService().begin();\n        final SSession session = sessionService.createSession(username);\n        getTransactionService().complete();\n        return session;\n    }\n\n    @Test\n    public void should_session_be_valid() throws Exception {\n        final String username = \"john\";\n        sessionService.setSessionDuration(1000);\n        final SSession session = createSession(username);\n        assertNotNull(session);\n        assertTrue(sessionService.isValid(session.getId()));\n        Thread.sleep(session.getDuration() + 1);\n        assertFalse(sessionService.isValid(session.getId()));\n    }\n\n    @Test\n    public void testRenewSession() throws Exception {\n        final String username = \"matti\";\n        final SSession session = createSession(username);\n        Thread.sleep(10);\n        // getTransactionService().begin();\n        sessionService.renewSession(session.getId());\n        // getTransactionService().complete();\n\n        // getTransactionService().begin();\n        final SSession session2 = sessionService.getSession(session.getId());\n        // getTransactionService().complete();\n        assertTrue(session2.getExpirationDate().after(session.getExpirationDate()));\n        assertTrue(session2.getLastRenewDate().after(session.getLastRenewDate()));\n        assertEquals(session2.getLastRenewDate().getTime() + session2.getDuration(),\n                session2.getExpirationDate().getTime());\n    }\n\n    @Test\n    public void testGetSession() throws Exception {\n        final String username = \"john\";\n        final SSession session = createSession(username);\n        assertNotNull(session);\n        final SSession retrievedSession = sessionService.getSession(session.getId());\n        assertEquals(session, retrievedSession);\n    }\n\n    @Test(expected = SSessionNotFoundException.class)\n    public void testCleanInvalidSessions() throws Exception {\n        final String username = \"john\";\n        sessionService.setSessionDuration(1);\n        final SSession invalidSession = createSession(username);\n        assertNotNull(invalidSession);\n        // the session will expires\n        Thread.sleep(10);\n        sessionService.cleanInvalidSessions();\n        // throw exception\n        sessionService.getSession(invalidSession.getId());\n    }\n\n    @Test(expected = SSessionNotFoundException.class)\n    public void testDeleteSession() throws Exception {\n        final String username = \"john\";\n        final SSession session = createSession(username);\n        assertNotNull(session);\n        SSession retrievedSession = sessionService.getSession(session.getId());\n        assertEquals(session, retrievedSession);\n        sessionService.deleteSession(session.getId());\n        try {\n            sessionService.getSession(session.getId());\n        } finally {\n            // restore deleted session:\n            createSession(username);\n        }\n    }\n\n    @Test(expected = SSessionNotFoundException.class)\n    public void testDeleteWrongSession() throws Exception {\n        sessionService.deleteSession(System.currentTimeMillis());\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/tenant/TenantMaintenanceLocalIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tenant;\n\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertTrue;\n\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.ServiceAccessorSingleton;\nimport org.bonitasoft.engine.work.WorkService;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.SystemOutRule;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class TenantMaintenanceLocalIT extends TestWithUser {\n\n    @Rule\n    public final SystemOutRule systemOutRule = new SystemOutRule().enableLog().muteForSuccessfulTests();\n\n    @Test\n    public void should_pause_tenant_then_stop_start_node_start_pageService_again_but_dont_restart_elements_but_resume_restart_them()\n            throws Exception {\n        // given: tenant is paused\n        WorkService workService = getServiceAccessor().getWorkService();\n        assertFalse(workService.isStopped());\n\n        logoutThenloginAs(USERNAME, PASSWORD);\n\n        final ProcessDefinitionBuilder pdb = new ProcessDefinitionBuilder().createNewInstance(\"loop process def\",\n                \"1.0\");\n        pdb.addAutomaticTask(\"step1\").addMultiInstance(false,\n                new ExpressionBuilder().createConstantIntegerExpression(100));\n        final DesignProcessDefinition dpd = pdb.done();\n        final ProcessDefinition pd = deployAndEnableProcess(dpd);\n        ProcessInstance processInstance = getProcessAPI().startProcess(pd.getId());\n        logoutThenlogin();\n\n        getTenantAdministrationAPI().pause();\n        assertTrue(workService.isStopped());\n        logout();\n\n        // clear logs before restarting the node\n        systemOutRule.clearLog();\n\n        // when: we stop and start the node\n        stopAndStartPlatform();\n\n        // assert that provided mandatory pages have re-imported again even though the tenant is paused\n        assertTrue(systemOutRule.getLog().contains(\"Import of Bonita mandatory pages completed\"));\n        // then: work service is not running\n        workService = getServiceAccessor().getWorkService();\n        assertTrue(workService.isStopped());\n\n        // cleanup\n        loginWithTechnicalUser();\n        getTenantAdministrationAPI().resume();\n\n        waitForProcessToFinish(processInstance);\n        disableAndDeleteProcess(pd);\n    }\n\n    protected ServiceAccessor getServiceAccessor() {\n        return ServiceAccessorSingleton.getInstance();\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/test/APIMethodLocalIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test;\n\nimport org.bonitasoft.engine.api.impl.CommandAPIImpl;\nimport org.bonitasoft.engine.api.impl.IdentityAPIImpl;\nimport org.bonitasoft.engine.api.impl.PlatformAPIImpl;\nimport org.bonitasoft.engine.api.impl.PlatformCommandAPIImpl;\nimport org.bonitasoft.engine.api.impl.ProcessAPIImpl;\nimport org.junit.Test;\n\npublic class APIMethodLocalIT extends APITestUtil {\n\n    @Test\n    public void checkAllMethodsOfCommandAPIContainsSerializableParameters() {\n        checkAllParametersAreSerializable(CommandAPIImpl.class);\n    }\n\n    @Test\n    public void checkAllMethodsOfPlatformCommandAPIContainsSerializableParameters() {\n        checkAllParametersAreSerializable(PlatformCommandAPIImpl.class);\n    }\n\n    @Test\n    public void checkAllMethodsOfPlatformAPIContainsSerializableParameters() {\n        checkAllParametersAreSerializable(PlatformAPIImpl.class);\n    }\n\n    @Test\n    public void checkAllMethodsOfIdentityAPIContainsSerializableParameters() {\n        checkAllParametersAreSerializable(IdentityAPIImpl.class);\n    }\n\n    @Test\n    public void checkAllMethodsOfProcessAPIContainsSerializableParameters() {\n        checkAllParametersAreSerializable(ProcessAPIImpl.class);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/test/BPMLocalIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test;\n\nimport static org.junit.Assert.*;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.Semaphore;\nimport java.util.concurrent.TimeUnit;\nimport java.util.logging.Logger;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport org.assertj.core.api.Assertions;\nimport org.bonitasoft.engine.actor.mapping.model.SActor;\nimport org.bonitasoft.engine.actor.mapping.model.SActorMember;\nimport org.bonitasoft.engine.api.ApiAccessType;\nimport org.bonitasoft.engine.api.IdentityAPI;\nimport org.bonitasoft.engine.api.LoginAPI;\nimport org.bonitasoft.engine.api.PlatformAPI;\nimport org.bonitasoft.engine.api.PlatformAPIAccessor;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.bar.BarResource;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.bpm.flownode.ActivityExecutionException;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceNotFoundException;\nimport org.bonitasoft.engine.bpm.flownode.ActivityStates;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.connectors.VariableStorage;\nimport org.bonitasoft.engine.core.process.comment.api.SCommentService;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping;\nimport org.bonitasoft.engine.dependency.DependencyService;\nimport org.bonitasoft.engine.dependency.model.AbstractSDependency;\nimport org.bonitasoft.engine.dependency.model.ScopeType;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.operation.OperationBuilder;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.platform.Platform;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.bonitasoft.engine.session.PlatformSession;\nimport org.bonitasoft.engine.session.impl.APISessionImpl;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.bonitasoft.engine.util.APITypeManager;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class BPMLocalIT extends CommonAPILocalIT {\n\n    public static Semaphore semaphore1 = new Semaphore(1);\n\n    public static Semaphore semaphore2 = new Semaphore(1);\n\n    private User john;\n\n    @Before\n    public void before() throws Exception {\n        loginWithTechnicalUser();\n\n        john = createUser(USERNAME, PASSWORD);\n        logout();\n        loginOnDefaultTenantWith(USERNAME, PASSWORD);\n        setSessionInfo(getSession());\n    }\n\n    @After\n    public void after() throws Exception {\n        VariableStorage.clearAll();\n        deleteUser(USERNAME);\n        logout();\n        cleanSession();\n    }\n\n    @Test(expected = InvalidSessionException.class)\n    public void useAFakeSessionId() throws BonitaException {\n        final LoginAPI loginAPI = getLoginAPI();\n        final APISession session = loginAPI.login(DEFAULT_TECHNICAL_LOGGER_USERNAME, DEFAULT_TECHNICAL_LOGGER_PASSWORD);\n        final APISession fakeSession = new APISessionImpl(session.getId() + 1, session.getCreationDate(),\n                session.getDuration(), session.getUserName(), session.getUserId());\n\n        final IdentityAPI identityAPI = TenantAPIAccessor.getIdentityAPI(fakeSession);\n        identityAPI.getGroup(12);\n    }\n\n    @Test\n    public void checkProcessCommentAreArchived() throws Exception {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final SCommentService commentService = serviceAccessor.getCommentService();\n        final UserTransactionService transactionService = serviceAccessor.getUserTransactionService();\n        final ProcessDefinitionBuilder processDef = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processToTestComment\", \"1.0\");\n        processDef.addStartEvent(\"start\");\n        processDef.addUserTask(\"step1\", ACTOR_NAME);\n        processDef.addEndEvent(\"end\");\n        processDef.addTransition(\"start\", \"step1\");\n        processDef.addTransition(\"step1\", \"end\");\n        processDef.addActor(ACTOR_NAME);\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(processDef.done(), ACTOR_NAME, john);\n        setSessionInfo(getSession()); // the session was cleaned by api call. This must be improved\n        final Callable<Long> getNumberOfComments = () -> commentService\n                .getNumberOfComments(QueryOptions.countQueryOptions());\n        final Callable<Long> getNumberOfArchivedComments = () -> commentService\n                .getNumberOfArchivedComments(QueryOptions.countQueryOptions());\n        assertEquals(0, (long) transactionService.executeInTransaction(getNumberOfComments));\n        final long numberOfInitialArchivedComments = transactionService\n                .executeInTransaction(getNumberOfArchivedComments);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId());\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n        getProcessAPI().addProcessComment(processInstance.getId(), \"kikoo lol\");\n        setSessionInfo(getSession()); // the session was cleaned by api call. This must be improved\n        assertEquals(1, (long) transactionService.executeInTransaction(getNumberOfComments));\n        assertEquals(numberOfInitialArchivedComments,\n                (long) transactionService.executeInTransaction(getNumberOfArchivedComments));\n        assignAndExecuteStep(step1Id, john);\n        waitForProcessToFinish(processInstance);\n\n        setSessionInfo(getSession()); // the session was cleaned by api call. This must be improved\n        assertEquals(0, (long) transactionService.executeInTransaction(getNumberOfComments));\n        assertEquals(numberOfInitialArchivedComments + 2,\n                (long) transactionService.executeInTransaction(getNumberOfArchivedComments));\n        disableAndDeleteProcess(definition);\n    }\n\n    @Test\n    public void checkPendingMappingAreDeleted() throws Exception {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final UserTransactionService transactionService = serviceAccessor.getUserTransactionService();\n        final ProcessDefinitionBuilder processDef = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processToTestComment\", \"1.0\");\n        processDef.addShortTextData(\"kikoo\", new ExpressionBuilder().createConstantStringExpression(\"lol\"));\n        processDef.addStartEvent(\"start\");\n        processDef.addUserTask(\"step1\", ACTOR_NAME).addShortTextData(\"kikoo2\",\n                new ExpressionBuilder().createConstantStringExpression(\"lol\"));\n        processDef.addUserTask(\"step2\", ACTOR_NAME);\n        processDef.addEndEvent(\"end\");\n        processDef.addTransition(\"start\", \"step1\");\n        processDef.addTransition(\"step1\", \"step2\");\n        processDef.addTransition(\"step2\", \"end\");\n        processDef.addActor(ACTOR_NAME);\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(processDef.done(), ACTOR_NAME, john);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId());\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n        setSessionInfo(getSession()); // the session was cleaned by api call. This must be improved\n        final Callable<List<SPendingActivityMapping>> getPendingMappings = () -> {\n            final QueryOptions queryOptions = new QueryOptions(0, 100, SPendingActivityMapping.class, \"id\",\n                    OrderByType.ASC);\n            return activityInstanceService.getPendingMappings(step1Id, queryOptions);\n        };\n        List<SPendingActivityMapping> mappings = transactionService.executeInTransaction(getPendingMappings);\n        assertEquals(1, mappings.size());\n        assignAndExecuteStep(step1Id, john.getId());\n        waitForUserTask(processInstance, \"step2\");\n        setSessionInfo(getSession()); // the session was cleaned by api call. This must be improved\n        mappings = transactionService.executeInTransaction(getPendingMappings);\n        assertEquals(0, mappings.size());\n        disableAndDeleteProcess(definition);\n    }\n\n    @Test\n    public void checkDependenciesAreDeletedWhenProcessIsDeleted() throws Exception {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final DependencyService dependencyService = serviceAccessor.getDependencyService();\n        final UserTransactionService transactionService = serviceAccessor.getUserTransactionService();\n        final ProcessDefinitionBuilder processDef = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processToTestTransitions\", \"1.0\");\n        processDef.addStartEvent(\"start\");\n        processDef.addUserTask(\"step1\", ACTOR_NAME);\n        processDef.addEndEvent(\"end\");\n        processDef.addTransition(\"start\", \"step1\");\n        processDef.addTransition(\"step1\", \"end\");\n        processDef.addActor(ACTOR_NAME);\n        final byte[] content = new byte[] { 1, 2, 3, 4, 5, 6, 7 };\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(processDef.done())\n                .addClasspathResource(new BarResource(\"myDep\", content)).done();\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(businessArchive, ACTOR_NAME, john);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId());\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n        List<Long> dependencyIds = transactionService\n                .executeInTransaction(new GetDependenciesIds(getSession(), definition.getId(), dependencyService, 0,\n                        100));\n        assertEquals(1, dependencyIds.size());\n        final AbstractSDependency dependency = transactionService\n                .executeInTransaction(new GetSDependency(dependencyIds.get(0), dependencyService));\n        assertTrue(dependency.getName().endsWith(\"myDep\"));\n        assertTrue(Arrays.equals(content, dependency.getValue()));\n\n        assignAndExecuteStep(step1Id, john);\n        waitForProcessToFinish(processInstance);\n        disableAndDeleteProcess(definition);\n\n        dependencyIds = transactionService.executeInTransaction(\n                new GetDependenciesIds(getSession(), definition.getId(), dependencyService, 0, 100));\n        assertEquals(0, dependencyIds.size());\n    }\n\n    @Test\n    public void checkMoreThan20DependenciesAreDeletedWhenProcessIsDeleted() throws Exception {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final DependencyService dependencyService = serviceAccessor.getDependencyService();\n        final UserTransactionService transactionService = serviceAccessor.getUserTransactionService();\n        final ProcessDefinitionBuilder processDef = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processToTestTransitions\", \"1.0\");\n        processDef.addStartEvent(\"start\").addUserTask(\"step1\", ACTOR_NAME).addEndEvent(\"end\");\n        processDef.addTransition(\"start\", \"step1\").addTransition(\"step1\", \"end\");\n        processDef.addActor(ACTOR_NAME);\n        final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(processDef.done());\n        for (int i = 0; i < 25; i++) {\n            final byte[] content = new byte[] { 1, 2, 3, 4, 5, 6, 7, (byte) (i >>> 24), (byte) (i >> 16 & 0xff),\n                    (byte) (i >> 8 & 0xff), (byte) (i & 0xff) };\n            businessArchiveBuilder.addClasspathResource(new BarResource(\"myDep\" + i, content));\n        }\n        final ProcessDefinition definition = deployAndEnableProcessWithActor(businessArchiveBuilder.done(), ACTOR_NAME,\n                john);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId());\n        final long step1Id = waitForUserTask(processInstance, \"step1\");\n        setSessionInfo(getSession()); // the session was cleaned by api call. This must be improved\n        List<Long> dependencyIds = transactionService\n                .executeInTransaction(new GetDependenciesIds(getSession(), definition.getId(), dependencyService,\n                        0, 100));\n        assertEquals(25, dependencyIds.size());\n        final AbstractSDependency dependency = transactionService\n                .executeInTransaction(new GetSDependency(dependencyIds.get(0), dependencyService));\n        assertNotNull(dependency);\n\n        assignAndExecuteStep(step1Id, john.getId());\n        waitForProcessToFinish(processInstance);\n        disableAndDeleteProcess(definition);\n        setSessionInfo(getSession()); // the session was cleaned by api call. This must be improved\n        dependencyIds = transactionService.executeInTransaction(\n                new GetDependenciesIds(getSession(), definition.getId(), dependencyService, 0, 100));\n        assertEquals(0, dependencyIds.size());\n    }\n\n    @Test\n    public void deletingProcessDeletesActors() throws Exception {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final UserTransactionService transactionService = serviceAccessor.getUserTransactionService();\n        final String userTaskName = \"actNaturally\";\n        final ProcessDefinition definition = deployAndEnableProcessWithOneHumanTask(\"deletingProcessDeletesActors\",\n                \"CandidateForOscarReward\", userTaskName);\n\n        final ProcessInstance processInstanceId = getProcessAPI().startProcess(definition.getId());\n        waitForUserTask(processInstanceId, userTaskName);\n\n        disableAndDeleteProcess(definition);\n\n        setSessionInfo(getSession()); // the session was cleaned by api call. This must be improved\n        final List<SActor> actors = transactionService.executeInTransaction(() -> {\n            final QueryOptions queryOptions = new QueryOptions(0, 1, SActor.class, \"id\", OrderByType.ASC);\n            return getServiceAccessor().getActorMappingService().getActors(definition.getId(), queryOptions);\n        });\n\n        // Check there is no actor left:\n        assertEquals(0, actors.size());\n    }\n\n    @Test\n    public void deletingProcessDeletesActorMappings() throws Exception {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final UserTransactionService transactionService = serviceAccessor.getUserTransactionService();\n        final String userTaskName = \"actNaturally\";\n        final ProcessDefinition definition = deployAndEnableProcessWithOneHumanTask(\n                \"deletingProcessDeletesActorMappings\", \"CandidateForOscarReward\",\n                userTaskName);\n\n        final ProcessInstance processInstanceId = getProcessAPI().startProcess(definition.getId());\n        waitForUserTask(processInstanceId, userTaskName);\n\n        disableAndDeleteProcess(definition);\n\n        setSessionInfo(getSession()); // the session was cleaned by api call. This must be improved\n        final List<SActorMember> actorMembers = transactionService\n                .executeInTransaction(\n                        () -> getServiceAccessor().getActorMappingService().getActorMembersOfUser(john.getId(), 0, 1));\n\n        // Check there is no actor left:\n        assertEquals(0, actorMembers.size());\n    }\n\n    private ProcessDefinition deployAndEnableProcessWithOneHumanTask(final String processName, final String actorName,\n            final String userTaskName)\n            throws Exception {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder();\n        processBuilder.createNewInstance(processName, \"1.0\");\n        processBuilder.addActor(actorName).addDescription(actorName + \" description\");\n        processBuilder.addStartEvent(\"startEvent\");\n        processBuilder.addUserTask(userTaskName, actorName);\n        processBuilder.addEndEvent(\"endEvent\");\n        processBuilder.addTransition(\"startEvent\", userTaskName);\n        processBuilder.addTransition(userTaskName, \"endEvent\");\n        return deployAndEnableProcessWithActor(processBuilder.done(), actorName, john);\n    }\n\n    @Test\n    public void restartHandlerTests() throws Exception {\n        /*\n         * process with blocking connector\n         */\n        final ProcessDefinitionBuilder builder1 = new ProcessDefinitionBuilder().createNewInstance(\"p1\", \"1.0\");\n        builder1.addActor(ACTOR_NAME);\n        builder1.addUserTask(\"step1\", ACTOR_NAME);\n        builder1.addAutomaticTask(\"step2\").addConnector(\"myConnector\", \"blocking-connector\", \"1.0\",\n                ConnectorEvent.ON_ENTER);\n        builder1.addTransition(\"step1\", \"step2\");\n        builder1.addUserTask(\"ustep2\", ACTOR_NAME);\n        builder1.addTransition(\"step2\", \"ustep2\");\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder()\n                .createNewBusinessArchive()\n                .setProcessDefinition(builder1.done())\n                .addConnectorImplementation(\n                        new BarResource(\"blocking-connector.impl\",\n                                BuildTestUtil.buildConnectorImplementationFile(\"blocking-connector\", \"1.0\",\n                                        \"blocking-connector-impl\",\n                                        \"1.0\", BlockingConnector.class.getName())))\n                .done();\n        final ProcessDefinition p1 = deployAndEnableProcessWithActor(businessArchive, ACTOR_NAME, john);\n\n        /*\n         * process with blocking operation (executing work)\n         */\n        final ProcessDefinitionBuilder builder2 = new ProcessDefinitionBuilder().createNewInstance(\"p2\", \"1.0\");\n        final String blockingGroovyScript1 = \"org.bonitasoft.engine.test.BPMLocalIT.tryAcquireSemaphore1();\\nreturn \\\"done\\\";\";\n        builder2.addActor(ACTOR_NAME);\n        builder2.addShortTextData(\"data\", null);\n        builder2.addUserTask(\"step1\", ACTOR_NAME);\n        builder2.addAutomaticTask(\"step2\").addOperation(\n                new OperationBuilder().createSetDataOperation(\"data\",\n                        new ExpressionBuilder().createGroovyScriptExpression(\"blockingGroovyScript1\",\n                                blockingGroovyScript1, String.class.getName())));\n        builder2.addTransition(\"step1\", \"step2\");\n        builder2.addUserTask(\"ustep2\", ACTOR_NAME);\n        builder2.addTransition(\"step2\", \"ustep2\");\n\n        final ProcessDefinition p2 = deployAndEnableProcessWithActor(builder2.done(), ACTOR_NAME, john);\n\n        /*\n         * process with blocking transition (notify work)\n         */\n        final ProcessDefinitionBuilder builder3 = new ProcessDefinitionBuilder().createNewInstance(\"p3\", \"1.0\");\n        final String blockingGroovyScript2 = \"org.bonitasoft.engine.test.BPMLocalIT.tryAcquireSemaphore2();\\nreturn true;\";\n        builder3.addActor(ACTOR_NAME);\n        builder3.addUserTask(\"step1\", ACTOR_NAME);\n        builder3.addAutomaticTask(\"step2\");\n        builder3.addTransition(\"step1\", \"step2\",\n                new ExpressionBuilder().createGroovyScriptExpression(\"blockingGroovyScript2\", blockingGroovyScript2,\n                        Boolean.class.getName()));\n        builder3.addUserTask(\"ustep2\", ACTOR_NAME);\n        builder3.addTransition(\"step2\", \"ustep2\");\n\n        final ProcessDefinition p3 = deployAndEnableProcessWithActor(builder3.done(), ACTOR_NAME, john);\n\n        // Block all 3 tasks\n        BlockingConnector.semaphore.acquire();\n        semaphore1.acquire();\n        semaphore2.acquire();\n\n        final ProcessInstance pi1 = getProcessAPI().startProcess(p1.getId());\n        final ProcessInstance pi2 = getProcessAPI().startProcess(p2.getId());\n        final ProcessInstance pi3 = getProcessAPI().startProcess(p3.getId());\n        waitForUserTaskAndExecuteIt(pi1, \"step1\", john);\n        waitForUserTaskAndExecuteIt(pi2, \"step1\", john);\n        waitForUserTaskAndExecuteIt(pi3, \"step1\", john);\n        logout();\n        final PlatformSession loginPlatform = loginOnPlatform();\n        final PlatformAPI platformAPI = PlatformAPIAccessor.getPlatformAPI(loginPlatform);\n        // stop node and in the same time release the semaphores to unlock works\n        final Thread thread = new Thread(() -> {\n            try {\n                Thread.sleep(200);\n            } catch (final InterruptedException e) {\n                e.printStackTrace();\n            }\n            BlockingConnector.semaphore.release();\n            semaphore1.release();\n            semaphore2.release();\n        });\n        thread.start();\n        platformAPI.stopNode();\n        // release them (work will fail, node is stopped)\n        thread.join(1000);\n        Thread.sleep(50);\n        platformAPI.startNode();\n        logoutOnPlatform(loginPlatform);\n        loginWithTechnicalUser();\n\n        //during stop node some flow node can be put in failed state\n        retryFailedFlowNodes();\n\n        // check we have all task ready\n        waitForPendingTasks(john.getId(), 3);\n\n        disableAndDeleteProcess(p1.getId());\n        disableAndDeleteProcess(p2.getId());\n        disableAndDeleteProcess(p3.getId());\n    }\n\n    private void retryFailedFlowNodes()\n            throws SearchException, ActivityInstanceNotFoundException, ActivityExecutionException {\n        List<FlowNodeInstance> failedFlowNodes = getFailedFlowNodes();\n        for (FlowNodeInstance failedFlowNode : failedFlowNodes) {\n            getProcessAPI().retryTask(failedFlowNode.getId());\n        }\n    }\n\n    private List<FlowNodeInstance> getFailedFlowNodes() throws SearchException {\n        SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 3);\n        builder.filter(FlowNodeInstanceSearchDescriptor.STATE_NAME, ActivityStates.FAILED_STATE);\n        SearchResult<FlowNodeInstance> searchResult = getProcessAPI().searchFlowNodeInstances(builder.done());\n        return searchResult.getResult();\n    }\n\n    public static void tryAcquireSemaphore1() throws InterruptedException {\n        System.out.println(\"tryAcquire semaphore1\");\n        semaphore1.tryAcquire(15, TimeUnit.SECONDS);\n        semaphore1.release();\n        System.out.println(\"release semaphore1\");\n    }\n\n    public static void tryAcquireSemaphore2() throws InterruptedException {\n        System.out.println(\"tryAcquire semaphore2\");\n        semaphore2.tryAcquire(15, TimeUnit.SECONDS);\n        semaphore2.release();\n        System.out.println(\"release semaphore2\");\n    }\n\n    @Test\n    public void getPlatformVersion() throws BonitaException, IOException {\n        logout();\n        final PlatformSession platformSession = loginOnPlatform();\n        final PlatformAPI platformAPI = PlatformAPIAccessor.getPlatformAPI(platformSession);\n        final Platform platform = platformAPI.getPlatform();\n        logoutOnPlatform(platformSession);\n        loginOnDefaultTenantWith(USERNAME, PASSWORD);\n        final String platformVersionToTest = getBonitaVersion();\n\n        assertNotNull(\"can't find the platform\", platform);\n        assertEquals(\"platformAdmin\", platform.getCreatedBy());\n        assertEquals(platformVersionToTest, platform.getVersion());\n        assertEquals(platformVersionToTest, platform.getInitialVersion());\n    }\n\n    public static String getBonitaVersion() throws IOException {\n        String version = System.getProperty(\"bonita.version\");// works in maven\n        if (version == null) {\n            // when running tests in eclipse get it from the pom.xml\n            final File file = new File(\"pom.xml\");\n            final String pomContent = Files.readString(file.toPath());\n            final Pattern pattern = Pattern.compile(\"<version>(.*)</version>\");\n            final Matcher matcher = pattern.matcher(pomContent);\n            matcher.find();\n            version = matcher.group(1);\n        }\n        return version;\n    }\n\n    private static class GetDependenciesIds implements Callable<List<Long>> {\n\n        private final APISession session;\n\n        private final long processDefinitionId;\n\n        private final DependencyService dependencyService;\n\n        private final int startIndex;\n\n        private final int maxResult;\n\n        public GetDependenciesIds(final APISession session, final long processDefinitionId,\n                final DependencyService dependencyService,\n                final int startIndex, final int maxResult) {\n            this.session = session;\n            this.processDefinitionId = processDefinitionId;\n            this.dependencyService = dependencyService;\n            this.startIndex = startIndex;\n            this.maxResult = maxResult;\n        }\n\n        @Override\n        public List<Long> call() throws Exception {\n            setSessionInfo(session); // the session was cleaned by api call. This must be improved\n            return dependencyService.getDependencyIds(processDefinitionId, ScopeType.PROCESS, startIndex, maxResult);\n        }\n    }\n\n    private static class GetSDependency implements Callable<AbstractSDependency> {\n\n        private final long dependencyId;\n\n        private final DependencyService dependencyService;\n\n        public GetSDependency(final long dependencyId, final DependencyService dependencyService) {\n            this.dependencyId = dependencyId;\n            this.dependencyService = dependencyService;\n        }\n\n        @Override\n        public AbstractSDependency call() throws Exception {\n            return dependencyService.getDependency(dependencyId);\n        }\n    }\n\n    @Test\n    public void should_warn_when_setting_remote_connection_with_local_engine() throws Exception {\n        //when\n        Logger logger = Logger.getLogger(APITypeManager.class.getName());\n        TestHandler testHandler = new TestHandler();\n        logger.addHandler(testHandler);\n\n        APITypeManager.setAPITypeAndParams(ApiAccessType.HTTP, Collections.emptyMap());\n\n        //then\n        String log = testHandler.getLogs();\n        APITypeManager.setAPITypeAndParams(ApiAccessType.LOCAL, Collections.emptyMap());\n\n        logger.removeHandler(testHandler);\n\n        Assertions.assertThat(log).contains(\n                \"You are declaring an API access to Bonita Engine as a remote connection, whereas it looks like you are running in the same JVM.\");\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/test/BlockingConnector.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test;\n\nimport java.util.concurrent.Semaphore;\nimport java.util.concurrent.TimeUnit;\n\nimport org.bonitasoft.engine.connector.AbstractConnector;\n\npublic class BlockingConnector extends AbstractConnector {\n\n    public static Semaphore semaphore = new Semaphore(1);\n\n    @Override\n    public void validateInputParameters() {\n    }\n\n    @Override\n    protected void executeBusinessLogic() {\n        try {\n            System.out.println(\"Try aqcuire in connector\");\n            semaphore.tryAcquire(15, TimeUnit.SECONDS);\n        } catch (InterruptedException e) {\n            e.printStackTrace();\n        }\n        semaphore.release();\n        System.out.println(\"semaphore in connector released\");\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/test/CommonAPILocalIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test;\n\nimport java.util.concurrent.Callable;\n\nimport org.bonitasoft.engine.CommonAPIIT;\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.ServiceAccessorSingleton;\nimport org.bonitasoft.engine.service.impl.ServiceAccessorFactory;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\n\n/**\n * @author Baptiste Mesta\n */\npublic class CommonAPILocalIT extends CommonAPIIT {\n\n    protected ServiceAccessor getServiceAccessor() {\n        try {\n            return ServiceAccessorSingleton.getInstance();\n        } catch (final Exception e) {\n            throw new BonitaRuntimeException(e);\n        }\n    }\n\n    protected static void setSessionInfo(final APISession session) throws Exception {\n        final SessionAccessor sessionAccessor = ServiceAccessorFactory.getInstance().createSessionAccessor();\n        sessionAccessor.setSessionId(session.getId());\n    }\n\n    protected static void cleanSession() throws Exception {\n        final SessionAccessor sessionAccessor = ServiceAccessorFactory.getInstance().createSessionAccessor();\n        sessionAccessor.deleteSessionId();\n    }\n\n    protected <T> T inTx(Callable<T> callable) throws Exception {\n        return getServiceAccessor().getUserTransactionService().executeInTransaction(callable);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/test/PermissionAPIIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.api.permission.APICallContext;\nimport org.junit.Test;\n\n/**\n * @author Baptiste Mesta\n */\npublic class PermissionAPIIT extends TestWithUser {\n\n    @Test\n    public void should_allow_with_provided_dynamic_rule() throws Exception {\n\n        APICallContext apiCallContext = new APICallContext(\"GET\", \"bpm\", \"process\", null, \"\", \"body\");\n        //when\n        boolean isAllowedWithoutFilter = getPermissionAPI()\n                .isAuthorized(apiCallContext);\n\n        //then\n        assertThat(isAllowedWithoutFilter).isFalse();\n\n        //given\n        apiCallContext = getApiCallContextWithUserFilter(getSession().getUserId());\n\n        //when\n        boolean isAllowedWithCurrentUserFilter = getPermissionAPI()\n                .isAuthorized(apiCallContext);\n\n        //then\n        assertThat(isAllowedWithCurrentUserFilter).isTrue();\n\n        //given\n        apiCallContext = getApiCallContextWithUserFilter(99999L);\n\n        //when\n        boolean isAllowedWithOtherUserFilter = getPermissionAPI()\n                .isAuthorized(apiCallContext);\n\n        //then\n        assertThat(isAllowedWithOtherUserFilter).isFalse();\n    }\n\n    private static APICallContext getApiCallContextWithUserFilter(long userId) {\n        return new APICallContext(\"GET\", \"bpm\", \"process\", null, \"\", \"body\") {\n\n            @Override\n            public Map<String, String> getFilters() {\n                return Collections.singletonMap(\"user_id\", String.valueOf(userId));\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/test/ProcessArchiveIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test;\n\nimport static org.junit.Assert.*;\n\nimport java.util.List;\nimport java.util.concurrent.Callable;\n\nimport org.bonitasoft.engine.bpm.comment.Comment;\nimport org.bonitasoft.engine.bpm.comment.SearchCommentsDescriptor;\nimport org.bonitasoft.engine.bpm.data.DataInstance;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstanceNotFoundException;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder;\nimport org.bonitasoft.engine.core.process.comment.api.SCommentService;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceService;\nimport org.bonitasoft.engine.data.instance.model.archive.SADataInstance;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.operation.LeftOperandBuilder;\nimport org.bonitasoft.engine.operation.OperatorType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class ProcessArchiveIT extends CommonAPILocalIT {\n\n    private User john;\n\n    @Before\n    public void beforeTest() throws BonitaException {\n        loginWithTechnicalUser();\n        john = createUser(USERNAME, \"bpm\");\n    }\n\n    @After\n    public void afterTest() throws BonitaException {\n        deleteUser(john);\n        logout();\n    }\n\n    @Test()\n    public void deleteProcessDefinitionDeleteArchivedInstancesWithDataAndComments() throws Exception {\n        setSessionInfo(getSession()); // the session was cleaned by api call. This must be improved\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final SCommentService commentService = serviceAccessor.getCommentService();\n        final UserTransactionService userTransactionService = serviceAccessor.getUserTransactionService();\n        final DataInstanceService dataInstanceService = serviceAccessor.getDataInstanceService();\n        final long initialNumberOfArchivedProcessInstance = getProcessAPI().getNumberOfArchivedProcessInstances();\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"ProcessToDelete\", \"1.0\");\n        processDefinitionBuilder.addActor(\"actor\");\n        processDefinitionBuilder.addShortTextData(\"procData\",\n                new ExpressionBuilder().createConstantStringExpression(\"procDataValue\"));\n        final UserTaskDefinitionBuilder userTaskDefinitionBuilder = processDefinitionBuilder.addUserTask(\"step1\",\n                \"actor\");\n        userTaskDefinitionBuilder.addOperation(new LeftOperandBuilder().createNewInstance(\"procData\").done(),\n                OperatorType.ASSIGNMENT, \"=\", null,\n                new ExpressionBuilder().createConstantStringExpression(\"updated proc value\"));\n        userTaskDefinitionBuilder.addOperation(new LeftOperandBuilder().createNewInstance(\"activityData\").done(),\n                OperatorType.ASSIGNMENT, \"=\", null,\n                new ExpressionBuilder().createConstantStringExpression(\"updated a value\"));\n        processDefinitionBuilder.addShortTextData(\"activityData\",\n                new ExpressionBuilder().createConstantStringExpression(\"activityDataBalue\")).getProcess();\n        final DesignProcessDefinition designProcessDefinition = processDefinitionBuilder.getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, \"actor\",\n                john);\n        final ProcessInstance p1 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance p2 = getProcessAPI().startProcess(processDefinition.getId());\n        final ProcessInstance p3 = getProcessAPI().startProcess(processDefinition.getId());\n        final long step1Id = waitForUserTask(p1, \"step1\");\n        getProcessAPI().addProcessComment(p1.getId(), \"A cool comment on p1\");\n        getProcessAPI().addProcessComment(p2.getId(), \"A cool comment on p2\");\n        getProcessAPI().addProcessComment(p3.getId(), \"A cool comment on p3\");\n        final List<Comment> comments = getProcessAPI().searchComments(\n                new SearchOptionsBuilder(0, 10).filter(SearchCommentsDescriptor.PROCESS_INSTANCE_ID, p1.getId()).done())\n                .getResult();\n        assertEquals(1, comments.size());\n        assertEquals(\"A cool comment on p1\", comments.get(0).getContent());\n        setSessionInfo(getSession()); // the session was cleaned by api call. This must be improved\n        userTransactionService.executeInTransaction((Callable<Void>) () -> {\n            assertEquals(3, commentService.getNumberOfComments(QueryOptions.countQueryOptions()));\n            assertEquals(0, commentService.getNumberOfArchivedComments(QueryOptions.countQueryOptions()));\n            return null;\n        });\n        final DataInstance activityDataInstance = getProcessAPI().getActivityDataInstance(\"activityData\", step1Id);\n        final DataInstance processDataInstance = getProcessAPI().getProcessDataInstance(\"procData\", p1.getId());\n        assertNotNull(activityDataInstance);\n        assignAndExecuteStep(step1Id, john.getId());\n        waitForUserTaskAndExecuteIt(p2, \"step1\", john);\n        waitForUserTaskAndExecuteIt(p3, \"step1\", john);\n        userTransactionService.executeInTransaction((Callable<Void>) () -> {\n            final SADataInstance saActDataInstances = dataInstanceService\n                    .getSADataInstance(activityDataInstance.getId(), System.currentTimeMillis());\n            assertNotNull(saActDataInstances);\n            final SADataInstance saProcDataInstances = dataInstanceService\n                    .getSADataInstance(processDataInstance.getId(), System.currentTimeMillis());\n            assertNotNull(saProcDataInstances);\n\n            return null;\n        });\n        waitForProcessToFinish(p1);\n        waitForProcessToFinish(p2);\n        waitForProcessToFinish(p3);\n        assertEquals(initialNumberOfArchivedProcessInstance + 3, getProcessAPI().getNumberOfArchivedProcessInstances());\n\n        setSessionInfo(getSession()); // the session was cleaned by api call. This must be improved\n        userTransactionService.executeInTransaction((Callable<Void>) () -> {\n            assertEquals(0, commentService.getNumberOfComments(QueryOptions.countQueryOptions()));\n            // 3 comments + 3 system comments\n            assertEquals(6, commentService.getNumberOfArchivedComments(QueryOptions.countQueryOptions()));\n            return null;\n        });\n\n        disableAndDeleteProcess(processDefinition);\n        assertEquals(initialNumberOfArchivedProcessInstance, getProcessAPI().getNumberOfArchivedProcessInstances());\n\n        setSessionInfo(getSession()); // the session was cleaned by api call. This must be improved\n        userTransactionService.executeInTransaction((Callable<Void>) () -> {\n            final SADataInstance saActDataInstances = dataInstanceService\n                    .getSADataInstance(activityDataInstance.getId(), System.currentTimeMillis());\n            final SADataInstance saProcDataInstances = dataInstanceService\n                    .getSADataInstance(processDataInstance.getId(), System.currentTimeMillis());\n            assertNull(saActDataInstances);\n            assertNull(saProcDataInstances);\n\n            assertEquals(0, commentService.getNumberOfComments(QueryOptions.countQueryOptions()));\n            assertEquals(0, commentService.getNumberOfArchivedComments(QueryOptions.countQueryOptions()));\n\n            return null;\n        });\n        cleanSession();\n    }\n\n    @Test\n    public void archivedFlowNodeInstance() throws Exception {\n        logout();\n        loginOnDefaultTenantWith(USERNAME, \"bpm\");\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"ProcessToDelete\", \"1.0\");\n        processDefinitionBuilder.addActor(\"actor\");\n        processDefinitionBuilder.addUserTask(\"step1\", \"actor\").addDescription(\"My Description\")\n                .addDisplayName(new ExpressionBuilder().createConstantStringExpression(\"My Display Name\"))\n                .addDisplayDescriptionAfterCompletion(\n                        new ExpressionBuilder().createConstantStringExpression(\"My Display Description\"));\n        final DesignProcessDefinition designProcessDefinition = processDefinitionBuilder.getProcess();\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, \"actor\",\n                john);\n\n        final ProcessDefinitionBuilder callingProcess = new ProcessDefinitionBuilder().createNewInstance(\"Caller\",\n                \"1.0\");\n        callingProcess.addCallActivity(\"call\",\n                new ExpressionBuilder().createConstantStringExpression(\"ProcessToDelete\"),\n                new ExpressionBuilder().createConstantStringExpression(\"1.0\"));\n\n        final ProcessDefinition callingProcessDef = deployAndEnableProcess(callingProcess.getProcess());\n\n        final ProcessInstance p1 = getProcessAPI().startProcess(callingProcessDef.getId());\n        final ActivityInstance userTask = waitForUserTaskAndExecuteAndGetIt(p1, \"step1\", john);\n        waitForProcessToFinish(p1);\n        waitForArchivedActivity(userTask.getId(), TestStates.NORMAL_FINAL);\n        final ArchivedActivityInstance archivedUserTask = getProcessAPI().getArchivedActivityInstance(userTask.getId());\n        assertEquals(\"My Description\", archivedUserTask.getDescription());\n        assertEquals(\"My Display Description\", archivedUserTask.getDisplayDescription());\n        assertEquals(\"My Display Name\", archivedUserTask.getDisplayName());\n        assertEquals(\"step1\", archivedUserTask.getName());\n        assertEquals(archivedUserTask.getParentContainerId(), userTask.getParentContainerId());\n        assertEquals(archivedUserTask.getRootContainerId(), userTask.getRootContainerId());\n        assertEquals(archivedUserTask.getFlownodeDefinitionId(), userTask.getFlownodeDefinitionId());\n        assertEquals(archivedUserTask.getType(), userTask.getType());\n        disableAndDeleteProcess(processDefinition);\n        disableAndDeleteProcess(callingProcessDef);\n    }\n\n    @Test(expected = ArchivedFlowNodeInstanceNotFoundException.class)\n    public void getArchivedFlowNodeInstanceNotFound() throws ArchivedFlowNodeInstanceNotFoundException {\n        getProcessAPI().getArchivedFlowNodeInstance(123456789L);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/test/ProcessWithExpressionLocalIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test;\n\nimport static java.util.Collections.singletonList;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.assertEquals;\n\nimport java.io.Serializable;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.TestWithUser;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.expression.*;\nimport org.bonitasoft.engine.operation.LeftOperand;\nimport org.bonitasoft.engine.operation.Operation;\nimport org.bonitasoft.engine.operation.OperationBuilder;\nimport org.bonitasoft.engine.operation.OperatorType;\nimport org.bonitasoft.engine.process.Employee;\nimport org.bonitasoft.engine.process.Secretary;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.ServiceAccessorSingleton;\nimport org.junit.Test;\n\n/**\n * @author Baptiste Mesta\n */\npublic class ProcessWithExpressionLocalIT extends TestWithUser {\n\n    private ProcessDefinition processDefinition;\n\n    private ProcessDefinition deployEmptyProcess() throws Exception {\n        final DesignProcessDefinition done = new ProcessDefinitionBuilder()\n                .createNewInstance(\"emptyProcess\", String.valueOf(System.currentTimeMillis()))\n                .done();\n        return deployAndEnableProcess(done);\n    }\n\n    @Test\n    public void evaluateObjectComparisonWithGreaterThan() throws Exception {\n        final Employee emp1 = new Employee(\"Doe\", \"John\", 4);\n        final Employee emp2 = new Employee(\"Doe\", \"Jane\", 1);\n        final Expression exprOperand1 = new ExpressionBuilder().createDataExpression(\"emp1\", Employee.class.getName());\n        final Expression exprOperand2 = new ExpressionBuilder().createDataExpression(\"emp2\", Employee.class.getName());\n\n        final Map<String, Serializable> inputValues = new HashMap<>(2);\n        inputValues.put(\"emp1\", emp1);\n        inputValues.put(\"emp2\", emp2);\n\n        final ProcessDefinition processDefinition = deployEmptyProcess();\n        final Expression expression1 = new ExpressionBuilder().createComparisonExpression(\"GreaterThan\", exprOperand1,\n                ComparisonOperator.GREATER_THAN,\n                exprOperand2);\n        final Expression expression2 = new ExpressionBuilder().createComparisonExpression(\"GreaterThan\", exprOperand2,\n                ComparisonOperator.GREATER_THAN,\n                exprOperand1);\n\n        assertEquals(true, getProcessAPI().evaluateExpressionOnProcessDefinition(expression1, inputValues,\n                processDefinition.getId()));\n        assertEquals(false, getProcessAPI().evaluateExpressionOnProcessDefinition(expression2, inputValues,\n                processDefinition.getId()));\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void evaluateObjectComparisonWithEquals() throws Exception {\n        final Employee emp1 = new Employee(\"Doe\", \"John\", 3);\n        final Employee emp2 = new Employee(\"Doe\", \"John\", 3);\n        final Employee emp3 = new Employee(\"Doe\", \"John\", 4);\n        final Expression exprOperand1 = new ExpressionBuilder().createDataExpression(\"emp1\", Employee.class.getName());\n        final Expression exprOperand2 = new ExpressionBuilder().createDataExpression(\"emp2\", Employee.class.getName());\n        final Expression exprOperand3 = new ExpressionBuilder().createDataExpression(\"emp3\", Employee.class.getName());\n\n        final Map<String, Serializable> inputValues = new HashMap<>(3);\n        inputValues.put(\"emp1\", emp1);\n        inputValues.put(\"emp2\", emp2);\n        inputValues.put(\"emp3\", emp3);\n\n        final ProcessDefinition processDefinition = deployEmptyProcess();\n        final Expression expression1 = new ExpressionBuilder().createComparisonExpression(\"Equals\", exprOperand1,\n                ComparisonOperator.EQUALS, exprOperand2);\n        final Expression expression2 = new ExpressionBuilder().createComparisonExpression(\"Equals2\", exprOperand1,\n                ComparisonOperator.EQUALS, exprOperand3);\n\n        assertEquals(true, getProcessAPI().evaluateExpressionOnProcessDefinition(expression1, inputValues,\n                processDefinition.getId()));\n        assertEquals(false, getProcessAPI().evaluateExpressionOnProcessDefinition(expression2, inputValues,\n                processDefinition.getId()));\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void evaluateComparisonExpressionWithObjectAndParentObject() throws Exception {\n        final Employee employee = new Employee(\"Smith\", \"Ashley\", 2);\n        final Secretary secretary = new Secretary(\"Smith\", \"Ashley\", 2);\n        final Expression exprOperand1 = new ExpressionBuilder().createDataExpression(\"emp1\", Employee.class.getName());\n        final Expression exprOperand2 = new ExpressionBuilder().createDataExpression(\"emp2\", Secretary.class.getName());\n\n        final Map<String, Serializable> inputValues = new HashMap<>(2);\n        inputValues.put(\"emp1\", employee);\n        inputValues.put(\"emp2\", secretary);\n\n        final ProcessDefinition processDefinition = deployEmptyProcess();\n        final Expression expression = new ExpressionBuilder().createComparisonExpression(\"Equals\", exprOperand1,\n                ComparisonOperator.EQUALS, exprOperand2);\n\n        assertEquals(true, getProcessAPI().evaluateExpressionOnProcessDefinition(expression, inputValues,\n                processDefinition.getId()));\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    @Test\n    public void should_operation_with_transient_data_reevaluate_the_definition_if_lost() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\n                \"processWithTransientData\",\n                String.valueOf(System.currentTimeMillis()));\n        builder.addActor(\"actor\");\n        builder.addUserTask(\"step1\", \"actor\")\n                .addData(\"tData\", String.class.getName(),\n                        new ExpressionBuilder().createConstantStringExpression(\"The default value\"))\n                .isTransient();\n        processDefinition = deployAndEnableProcessWithActor(builder.done(), \"actor\", user);\n        getProcessAPI().startProcess(processDefinition.getId());\n        final HumanTaskInstance step1 = waitForUserTaskAndGetIt(\"step1\");\n\n        // evaluate the expression of the transient data, it should return the default value\n        assertThat(evaluateTransientDataWithExpression(step1).get(\"tData\")).isEqualTo(\"The default value\");\n        // update it using operation\n        updateDataWithOperation(step1, \"The updated value\", \"tData\");\n        // evaluate it: it should return the updated value\n        assertThat(evaluateTransientDataWithExpression(step1).get(\"tData\")).isEqualTo(\"The updated value\");\n        // clear the cache\n        getServiceAccessor().getCacheService().clear(\"transient_data\");\n        // evaluate it: it should return the default value\n        assertThat(evaluateTransientDataWithExpression(step1).get(\"tData\")).isEqualTo(\"The default value\");\n\n        disableAndDeleteProcess(processDefinition);\n    }\n\n    protected ServiceAccessor getServiceAccessor() {\n        return ServiceAccessorSingleton.getInstance();\n    }\n\n    private void updateDataWithOperation(final HumanTaskInstance step1, final String value, final String name)\n            throws InvalidExpressionException,\n            UpdateException {\n        final Operation operation = new OperationBuilder().createNewInstance()\n                .setLeftOperand(name, LeftOperand.TYPE_TRANSIENT_DATA)\n                .setRightOperand(new ExpressionBuilder().createConstantStringExpression(value))\n                .setType(OperatorType.ASSIGNMENT).done();\n        getProcessAPI().updateActivityInstanceVariables(singletonList(operation), step1.getId(), null);\n    }\n\n    private Map<String, Serializable> evaluateTransientDataWithExpression(final HumanTaskInstance step1)\n            throws ExpressionEvaluationException,\n            InvalidExpressionException {\n        final Map<Expression, Map<String, Serializable>> expressionMap = new HashMap<>();\n        expressionMap.put(new ExpressionBuilder().createTransientDataExpression(\"tData\", String.class.getName()),\n                Collections.emptyMap());\n        return getProcessAPI().evaluateExpressionsOnActivityInstance(step1.getId(), expressionMap);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/test/TestHandler.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test;\n\nimport java.util.logging.Handler;\nimport java.util.logging.LogRecord;\n\n/**\n * @author Baptiste Mesta\n */\npublic class TestHandler extends Handler {\n\n    private StringBuilder stb = new StringBuilder();\n\n    @Override\n    public void publish(LogRecord record) {\n        stb.append(record.getMessage()).append('\\n');\n    }\n\n    @Override\n    public void flush() {\n\n    }\n\n    @Override\n    public void close() throws SecurityException {\n        stb = null;\n    }\n\n    public String getLogs() {\n        return stb.toString();\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/work/WorkServiceIT.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.awaitility.Awaitility.await;\n\nimport java.util.Map;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport org.bonitasoft.engine.bpm.CommonBPMServicesTest;\nimport org.bonitasoft.engine.commons.exceptions.SRetryableException;\nimport org.bonitasoft.engine.execution.work.BPMWorkFactory;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Baptiste Mesta.\n */\npublic class WorkServiceIT extends CommonBPMServicesTest {\n\n    private RetryingWorkExecutorService workExecutorService;\n    private BPMWorkFactory workFactory;\n    private int originalDelay;\n\n    @Before\n    public void before() {\n        this.workExecutorService = (RetryingWorkExecutorService) getServiceAccessor().getWorkExecutorService();\n        this.workFactory = getServiceAccessor().getBPMWorkFactory();\n        originalDelay = workExecutorService.getDelay();\n        workExecutorService.setDelay(1);\n    }\n\n    @Override\n    public void after() throws Exception {\n        workExecutorService.setDelay(originalDelay);\n        super.after();\n    }\n\n    @Test\n    public void should_retry_work_that_fails_2_times() throws Exception {\n        AtomicInteger failureCounter = new AtomicInteger(0);\n        AtomicBoolean workDone = new AtomicBoolean(false);\n        BonitaWork myFailingWork = new BonitaWork() {\n\n            @Override\n            public String getDescription() {\n                return null;\n            }\n\n            @Override\n            public CompletableFuture<Void> work(Map<String, Object> context) throws Exception {\n                if (failureCounter.get() < 2) {\n                    failureCounter.incrementAndGet();\n                    throw new SRetryableException(new Exception(\"\"));\n                }\n                workDone.set(true);\n                return CompletableFuture.completedFuture(null);\n            }\n\n            @Override\n            public void handleFailure(Throwable e, Map<String, Object> context) throws Exception {\n\n            }\n        };\n        workFactory.addExtension(\"MyFailingWork\", workDescriptor -> myFailingWork);\n\n        workExecutorService.execute(WorkDescriptor.create(\"MyFailingWork\"));\n\n        //work should complete\n        await().until(workDone::get);\n        //work should have failed 2 times\n        assertThat(failureCounter.get()).isEqualTo(2);\n    }\n\n    @Test\n    public void should_retry_work_that_always_fails() throws Exception {\n        AtomicInteger failureCounter = new AtomicInteger(0);\n        AtomicBoolean handleFailureCalled = new AtomicBoolean(false);\n        BonitaWork myFailingWork = new BonitaWork() {\n\n            @Override\n            public String getDescription() {\n                return null;\n            }\n\n            @Override\n            public CompletableFuture<Void> work(Map<String, Object> context) throws Exception {\n                failureCounter.incrementAndGet();\n                throw new SRetryableException(new Exception(\"\"));\n            }\n\n            @Override\n            public void handleFailure(Throwable e, Map<String, Object> context) throws Exception {\n                handleFailureCalled.set(true);\n            }\n        };\n        workFactory.addExtension(\"MyFailingWork\", workDescriptor -> myFailingWork);\n\n        workExecutorService.execute(WorkDescriptor.create(\"MyFailingWork\"));\n\n        //work should complete\n        await().until(handleFailureCalled::get);\n        //work should be retried 11 times before failing completely\n        assertThat(failureCounter.get()).isEqualTo(11);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/resources/applications-importer/mixedLegacyAndApplicationLinks.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<applications xmlns=\"http://documentation.bonitasoft.com/application-xml-schema/1.1\">\n  <applicationLink token=\"app1\" version=\"1.0\" profile=\"User\" state=\"ACTIVATED\">\n    <displayName>Application 1</displayName>\n    <description>Description of Application 1</description>\n    <iconPath>/app1.jpg</iconPath>\n  </applicationLink>\n  <applicationLink token=\"app2\" version=\"1.1\" state=\"DEACTIVATED\">\n    <displayName>Application 2</displayName>\n  </applicationLink>\n  <application token=\"app3\" version=\"2.0\" profile=\"User\" state=\"ACTIVATED\" homePage=\"my-new-custom-page\"\n               layout=\"custompage_layoutBonita\" theme=\"custompage_themeBonita\">\n    <displayName>Application 3</displayName>\n    <description>Description of Application 3</description>\n    <iconPath>/app3.jpg</iconPath>\n    <applicationPages>\n      <applicationPage customPage=\"custompage_mynewcustompage\" token=\"my-new-custom-page\"/>\n    </applicationPages>\n    <applicationMenus>\n      <applicationMenu>\n        <displayName>Menu level 1</displayName>\n        <applicationMenus>\n          <applicationMenu applicationPage=\"my-new-custom-page\">\n            <displayName>Menu level 1.1</displayName>\n          </applicationMenu>\n        </applicationMenus>\n      </applicationMenu>\n      <applicationMenu>\n        <displayName>Empty menu</displayName>\n      </applicationMenu>\n    </applicationMenus>\n  </application>\n  <application token=\"app4\" version=\"2.0\" state=\"DEACTIVATED\" layout=\"custompage_layoutBonita\"\n               theme=\"custompage_themeBonita\">\n    <displayName>Application 4</displayName>\n    <applicationPages/>\n    <applicationMenus/>\n  </application>\n</applications>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/resources/applications-importer/multipleApplicationLinks.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<applications xmlns=\"http://documentation.bonitasoft.com/application-xml-schema/1.1\">\n  <applicationLink token=\"app1\" version=\"1.0\" profile=\"User\" state=\"ACTIVATED\">\n    <displayName>Application 1</displayName>\n    <description>Description of Application 1</description>\n    <iconPath>/app1.jpg</iconPath>\n  </applicationLink>\n  <applicationLink token=\"app2\" version=\"1.1\" state=\"DEACTIVATED\">\n    <displayName>Application 2</displayName>\n  </applicationLink>\n</applications>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/resources/applications-importer/oneApplicationLink.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<applications xmlns=\"http://documentation.bonitasoft.com/application-xml-schema/1.1\">\n  <applicationLink token=\"app1\" version=\"1.0\" profile=\"User\" state=\"ACTIVATED\">\n    <displayName>Application 1</displayName>\n    <description>Description of Application 1</description>\n    <iconPath>/app1.jpg</iconPath>\n  </applicationLink>\n</applications>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-local/src/test/resources/org/bonitasoft/engine/classloader/resource.txt",
    "content": "Text resource"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/build.gradle",
    "content": "plugins {\n    id('bonita-tests')\n}\n\ndependencies {\n    implementation project(':bpm:bonita-web-server')\n    implementation project(':bonita-test-api')\n    implementation libs.slf4jApi\n    implementation libs.commonsIO\n    implementation libs.jakartaServletApi\n    implementation libs.junit4\n    implementation libs.springTest\n    testImplementation libs.hamcrest\n    testImplementation libs.mockitoCore\n    testImplementation libs.assertj\n    testImplementation libs.awaitility\n    testImplementation libs.tomcatEmbedCore\n}\n\njava {\n    withSourcesJar()\n}\n\ngroup = 'org.bonitasoft.console'\ndescription = 'Bonita Integration Tests Web'\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/AbstractJUnitTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.test.toolkit;\n\nimport org.bonitasoft.engine.test.junit.BonitaEngineRule;\nimport org.bonitasoft.test.toolkit.organization.AdminUser;\nimport org.bonitasoft.test.toolkit.organization.TestToolkitCtx;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\nimport org.bonitasoft.test.toolkit.organization.TestUserFactory;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Rule;\n\n/**\n * Template of JUnit Test. Do initialization as well as necessary clean up.\n *\n * @author Vincent Elcrin, Anthony Birembaut\n */\npublic abstract class AbstractJUnitTest {\n\n    @Rule\n    public BonitaEngineRule bonitaEngineRule = createBonitaEngineRule();\n\n    protected BonitaEngineRule createBonitaEngineRule() {\n        return BonitaEngineRule.create().withCleanAfterTest();\n    }\n\n    @Before\n    public final void aaSetUp() throws Exception {\n        getContext().check();\n        getContext().setInitiator(getInitiator());\n        testSetUp();\n    }\n\n    @After\n    public final void zzTearDown() throws Exception {\n        testTearDown();\n        final AdminUser adminUser = TestToolkitCtx.getInstance().getAdminUser();\n        for (TestUser testUser : TestUserFactory.getInstance().getUserList().values()) {\n            adminUser.delete(testUser);\n        }\n        getContext().clearSession();\n    }\n\n    /**\n     * Initiator is a convenient notion to set a test user as source of all transactions done with the framework via its\n     * API Session.\n     * It still can be overridden for all methods of the framework and also can be set for the session with\n     * {@link TestToolkitCtx#setInitiator(TestUser)}\n     *\n     * @return\n     * @throws Exception\n     */\n    protected abstract TestUser getInitiator();\n\n    /**\n     * Define context to use during the test. Two implementations exist. TestToolkitCtx and TestToolkitCtx (SP version)\n     *\n     * @return\n     */\n    protected abstract TestToolkitCtx getContext();\n\n    /**\n     * JUnit's {@link Before} implementation.\n     *\n     * @throws Exception\n     */\n    protected abstract void testSetUp() throws Exception;\n\n    /**\n     * JUnit's {@link After} implementation.\n     *\n     * @throws Exception\n     */\n    protected abstract void testTearDown() throws Exception;\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/bpm/AbstractManualTask.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.test.toolkit.bpm;\n\n/**\n * @author Vincent Elcrin\n */\npublic abstract class AbstractManualTask {\n\n    public abstract long getId();\n\n    public abstract String getName();\n\n    public abstract String getDescription();\n\n    // /////////////////////////////////////////////////////////////////////////////\n    // / Test state\n    // /////////////////////////////////////////////////////////////////////////////\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/bpm/ProcessVariable.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.test.toolkit.bpm;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.expression.InvalidExpressionException;\n\n/**\n * @author Colin PUY\n */\npublic class ProcessVariable {\n\n    private final String name;\n\n    private final Class<?> clazz;\n\n    private final Expression defaultValue;\n\n    public static ProcessVariable aStringVariable(String name, String defaultValue) throws InvalidExpressionException {\n        return new ProcessVariable(name, String.class,\n                new ExpressionBuilder().createConstantStringExpression(defaultValue));\n    }\n\n    public static ProcessVariable aLongVariable(String name, long defaultValue) throws InvalidExpressionException {\n        return new ProcessVariable(name, Long.class,\n                new ExpressionBuilder().createConstantLongExpression(defaultValue));\n    }\n\n    public static ProcessVariable aDateVariable(String name, String defaultValue) throws InvalidExpressionException {\n        return new ProcessVariable(name, Date.class,\n                new ExpressionBuilder().createConstantDateExpression(defaultValue));\n    }\n\n    public ProcessVariable(String name, Class<?> clazz, Expression defaultValue) {\n        this.name = name;\n        this.clazz = clazz;\n        this.defaultValue = defaultValue;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public String getClassName() {\n        return clazz.getName();\n    }\n\n    public Expression getDefaultValue() {\n        return defaultValue;\n    }\n\n    public static ProcessVariable createLongVariable(long value) throws InvalidExpressionException {\n        return new ProcessVariable(\"aLongVariable\", Long.class,\n                new ExpressionBuilder().createConstantLongExpression(value));\n    }\n\n    public static ProcessVariable createIntVariable(int value) throws InvalidExpressionException {\n        return new ProcessVariable(\"aIntVariable\", Integer.class,\n                new ExpressionBuilder().createConstantIntegerExpression(value));\n    }\n\n    public static ProcessVariable createStringVariable(String value) throws InvalidExpressionException {\n        return new ProcessVariable(\"aStringVariable\", String.class,\n                new ExpressionBuilder().createConstantStringExpression(value));\n    }\n\n    public static ProcessVariable createBooleanVariable(Boolean value) throws InvalidExpressionException {\n        return new ProcessVariable(\"aBooleanVariable\", Boolean.class,\n                new ExpressionBuilder().createConstantBooleanExpression(value));\n    }\n\n    public static ProcessVariable createDoubleVariable(Double value) throws InvalidExpressionException {\n        return new ProcessVariable(\"aDoubleVariable\", Double.class,\n                new ExpressionBuilder().createConstantDoubleExpression(value));\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/bpm/TestActor.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.test.toolkit.bpm;\n\n/**\n * @author Vincent Elcrin\n */\npublic interface TestActor {\n\n    /**\n     * Id used by the process to define the actor\n     */\n    long getId();\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/bpm/TestCase.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.test.toolkit.bpm;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.flownode.ActivityStates;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.bonitasoft.test.toolkit.exception.NextActivityIsNotAllowedStateException;\nimport org.bonitasoft.test.toolkit.exception.NoActivityLeftException;\nimport org.bonitasoft.test.toolkit.exception.TestToolkitException;\nimport org.bonitasoft.test.toolkit.organization.TestToolkitCtx;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\n\n/**\n * @author Vincent Elcrin\n */\npublic class TestCase {\n\n    private final ProcessInstance processInstance;\n\n    public final static int GET_NEXT_NB_ATTEMPT = 30;\n\n    public final static int SLEEP_TIME_MS = 100;\n\n    public final static String READY_STATE = \"started\";\n\n    public TestCase(final ProcessInstance instance) {\n        processInstance = instance;\n    }\n\n    /**\n     * Wait until the process return the state in parameter\n     */\n    public void waitProcessState(final APISession apiSession, final String state) {\n        final ProcessAPI processAPI = TestProcess.getProcessAPI(apiSession);\n        ProcessInstance instance = null;\n        for (int i = 0; i < GET_NEXT_NB_ATTEMPT; i++) {\n            try {\n                instance = processAPI.getProcessInstance(processInstance.getId());\n                if (instance != null && state.equals(instance.getState())) {\n                    break;\n                }\n                Thread.sleep(SLEEP_TIME_MS);\n            } catch (final Exception e) {\n                throw new TestToolkitException(\"Can't get process instance <\" + processInstance.getId() + \">.\", e);\n            }\n        }\n        if (instance == null || !state.equals(instance.getState())) {\n            throw new TestToolkitException(\n                    \"Instance <\" + processInstance.getId() + \"> has not reached the expected state <\" + state + \">.\");\n        }\n    }\n\n    /**\n     * Search and get next human task\n     */\n    public TestHumanTask getNextHumanTask(final APISession apiSession) {\n        final ProcessAPI processAPI = TestProcess.getProcessAPI(apiSession);\n        final SearchOptionsBuilder searchOptBuilder = new SearchOptionsBuilder(0, 1);\n        searchOptBuilder.filter(HumanTaskInstanceSearchDescriptor.STATE_NAME, ActivityStates.READY_STATE);\n\n        /*\n         * Get next workable human task. (e.g. not in initialization state)\n         */\n        HumanTaskInstance humanTask = null;\n        SearchResult<HumanTaskInstance> result = null;\n        for (int i = 0; i < GET_NEXT_NB_ATTEMPT; i++) {\n            try {\n                result = processAPI.searchHumanTaskInstances(searchOptBuilder.done());\n                if (!result.getResult().isEmpty()) {\n                    humanTask = result.getResult().get(0);\n                    break;\n                }\n                Thread.sleep(SLEEP_TIME_MS);\n            } catch (final InvalidSessionException e) {\n                throw new TestToolkitException(\"Can't search human task instances. Invalid session\", e);\n            } catch (final SearchException e) {\n                throw new TestToolkitException(\"Can't search human task instances\", e);\n            } catch (final InterruptedException e) {\n                throw new TestToolkitException(\"Interrupted during searching process\", e);\n            }\n        }\n\n        if (humanTask != null) {\n            return new TestHumanTask(humanTask);\n        } else {\n            if (result.getResult().size() > 0) {\n                throw new NextActivityIsNotAllowedStateException(result.getResult().get(0));\n            } else {\n                throw new NoActivityLeftException();\n            }\n        }\n    }\n\n    public TestHumanTask getNextHumanTask() {\n        return getNextHumanTask(TestToolkitCtx.getInstance().getInitiator().getSession());\n    }\n\n    public ProcessInstance getProcessInstance() {\n        return processInstance;\n    }\n\n    private ArchivedProcessInstance getArchive(final APISession apiSession) {\n        final ProcessAPI processAPI = TestProcess.getProcessAPI(apiSession);\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 1);\n        searchOptionsBuilder.filter(ArchivedProcessInstancesSearchDescriptor.SOURCE_OBJECT_ID, getId());\n        searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.ARCHIVE_DATE, Order.DESC);\n        SearchResult<ArchivedProcessInstance> searchArchivedProcessInstances;\n        try {\n            searchArchivedProcessInstances = processAPI\n                    .searchArchivedProcessInstancesInAllStates(searchOptionsBuilder.done());\n        } catch (final SearchException se) {\n            throw new TestToolkitException(\"Can't get process instance archived for <\" + getId() + \">\", se);\n        }\n        if (searchArchivedProcessInstances != null && searchArchivedProcessInstances.getCount() > 0) {\n            return searchArchivedProcessInstances.getResult().get(0);\n        } else {\n            throw new TestToolkitException(\"Can't get process instance archived for <\" + getId() + \">\");\n        }\n    }\n\n    public ArchivedProcessInstance getArchive(final TestUser initiator) {\n        return getArchive(initiator.getSession());\n    }\n\n    public ArchivedProcessInstance getArchive() {\n        return getArchive(TestToolkitCtx.getInstance().getInitiator());\n    }\n\n    public long getId() {\n        return processInstance.getId();\n    }\n\n    // ///////////////////////////////////////////////////////////////////\n    // / Execution\n    // ///////////////////////////////////////////////////////////////////\n\n    public void execute(final APISession apiSession) {\n        try {\n            final TestHumanTask nextActivityInstance = getNextHumanTask(apiSession);\n            if (nextActivityInstance != null) {\n                nextActivityInstance.execute(apiSession);\n            }\n        } catch (final NoActivityLeftException e) {\n            // there were no activity in the process\n        }\n\n    }\n\n    public void execute(final TestUser user) {\n        execute(user.getSession());\n    }\n\n    public void execute() {\n        execute(TestToolkitCtx.getInstance().getInitiator());\n    }\n\n    // ///////////////////////////////////////////////////////////////////\n    // / Comments\n    // ///////////////////////////////////////////////////////////////////\n\n    private void addComment(final APISession apiSession, final String content) {\n        final ProcessAPI processAPI = TestProcess.getProcessAPI(apiSession);\n        try {\n            processAPI.addProcessComment(processInstance.getId(), content);\n        } catch (final Exception e) {\n            throw new TestToolkitException(\"Can't add comment to <\" + processInstance.getId() + \">\", e);\n        }\n    }\n\n    private void addComment(final TestUser initiator, final String content) {\n        addComment(initiator.getSession(), content);\n    }\n\n    public void addComments(final TestUser initiator, final int nbOfComments, final String content) {\n        for (int i = 0; i < nbOfComments; i++) {\n            addComment(initiator, content + i);\n        }\n    }\n\n    public void addComment(final String content) {\n        addComment(TestToolkitCtx.getInstance().getInitiator(), content);\n    }\n\n    public void cancel() {\n        TestUser testUser = TestToolkitCtx.getInstance().getInitiator();\n        final ProcessAPI processAPI = TestProcess.getProcessAPI(testUser.getSession());\n        try {\n            processAPI.cancelProcessInstance(processInstance.getId());\n        } catch (ProcessInstanceNotFoundException e) {\n            throw new TestToolkitException(\"Can't get process instance <\" + getId() + \">\", e);\n        } catch (UpdateException e) {\n            throw new TestToolkitException(\"Can't update process instance <\" + getId() + \">\", e);\n        }\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/bpm/TestCaseFactory.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.test.toolkit.bpm;\n\nimport org.bonitasoft.test.toolkit.organization.TestUser;\n\n/**\n * @author Colin PUY\n */\npublic class TestCaseFactory {\n\n    public static TestCase createRandomCase(TestUser initiator) {\n        return TestProcessFactory.createRandomResolvedProcess(initiator).startCase();\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/bpm/TestCategory.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.test.toolkit.bpm;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.category.Category;\nimport org.bonitasoft.engine.bpm.category.CategoryCriterion;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.test.toolkit.exception.TestToolkitException;\nimport org.bonitasoft.test.toolkit.organization.TestToolkitCtx;\n\n/**\n * @author Séverin Moussel\n */\npublic class TestCategory {\n\n    private final Category category;\n\n    /**\n     * Default Constructor.\n     */\n    public TestCategory(final Category category) {\n        this.category = category;\n    }\n\n    /**\n     * Default Constructor.\n     */\n    public TestCategory(final APISession apiSession, final String name, final String description) {\n        this(createCategory(apiSession, name, description));\n    }\n\n    public TestCategory(final String name, final String description) {\n        this(TestToolkitCtx.getInstance().getInitiator().getSession(), name, description);\n    }\n\n    private static Category createCategory(final APISession apiSession, final String name, final String description) {\n        try {\n            return TenantAPIAccessor.getProcessAPI(apiSession).createCategory(name, description);\n        } catch (final Exception e) {\n            throw new TestToolkitException(\"Can't create category.\", e);\n        }\n    }\n\n    public void delete(final APISession apiSession) {\n        try {\n            TenantAPIAccessor.getProcessAPI(apiSession).deleteCategory(this.category.getId());\n        } catch (final Exception e) {\n            throw new TestToolkitException(\"Can't delete category\", e);\n        }\n    }\n\n    public void delete() {\n        try {\n            delete(TestToolkitCtx.getInstance().getInitiator().getSession());\n        } catch (final Exception e) {\n            throw new TestToolkitException(\"Can't delete category\", e);\n        }\n    }\n\n    public Category getCategory() {\n        return this.category;\n    }\n\n    public static List<TestCategory> getAll(final APISession apiSession) {\n        try {\n            final ProcessAPI processAPI = TenantAPIAccessor.getProcessAPI(apiSession);\n            final int nbCat = (int) processAPI.getNumberOfCategories();\n            final List<Category> catList = processAPI.getCategories(0, nbCat, CategoryCriterion.NAME_ASC);\n            final List<TestCategory> testCatList = new ArrayList<>();\n            for (final Category cat : catList) {\n                testCatList.add(new TestCategory(cat));\n            }\n            return testCatList;\n        } catch (final Exception e) {\n            throw new TestToolkitException(\"Can't get categories\", e);\n        }\n\n    }\n\n    public long getId() {\n        return this.category.getId();\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/bpm/TestCategoryFactory.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.test.toolkit.bpm;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Random;\n\nimport org.bonitasoft.engine.session.APISession;\n\n/**\n * @author Séverin Moussel\n */\npublic class TestCategoryFactory {\n\n    private final Map<String, TestCategory> categories = new HashMap<>();\n\n    private TestCategory _getCategory(final String name, final String description) {\n        if (!this.categories.containsKey(name)) {\n            this.categories.put(name, new TestCategory(name, description));\n        }\n        return this.categories.get(name);\n    }\n\n    private static String getRandomString() {\n        return String.valueOf(new Random().nextLong());\n    }\n\n    private static final TestCategoryFactory instance = new TestCategoryFactory();\n\n    public static TestCategoryFactory getInstance() {\n        return instance;\n    }\n\n    public static TestCategory getCategory(final String name, final String description) {\n        return getInstance()._getCategory(name, description);\n    }\n\n    public static List<TestCategory> getCategories(final int count) {\n        final List<TestCategory> results = new ArrayList<>(count);\n        for (int i = 0; i < count; i++) {\n            results.add(getRandomCategory());\n        }\n\n        return results;\n    }\n\n    public static List<TestCategory> getAllCategories(final APISession apiSession) {\n        final List<TestCategory> all = TestCategory.getAll(apiSession);\n        for (TestCategory testCategory : all) {\n            getInstance().getCategoriesList().put(testCategory.getCategory().getName(), testCategory);\n        }\n        return all;\n    }\n\n    public static TestCategory getRandomCategory() {\n        return getCategory(getRandomString(), getRandomString());\n    }\n\n    public void clear() {\n        for (TestCategory testCategory : getCategoriesList().values()) {\n            testCategory.delete();\n        }\n        getCategoriesList().clear();\n    }\n\n    /**\n     * @return the userList\n     */\n    private Map<String, TestCategory> getCategoriesList() {\n        return this.categories;\n    }\n\n    public void check() {\n        if (!getCategoriesList().isEmpty()) {\n            throw new RuntimeException(this.getClass().getName() + \" cannot be reset because the list is not empty: \"\n                    + getCategoriesList());\n        }\n    }\n\n    public static void removeTestCategoryFromList(TestCategory category) {\n        getInstance().getCategoriesList().remove(category.getCategory().getName());\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/bpm/TestHumanTask.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.test.toolkit.bpm;\n\nimport java.util.Collections;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.data.DataInstance;\nimport org.bonitasoft.engine.bpm.data.DataNotFoundException;\nimport org.bonitasoft.engine.bpm.flownode.ActivityExecutionException;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.test.toolkit.exception.TestToolkitException;\nimport org.bonitasoft.test.toolkit.organization.TestToolkitCtx;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\n\n/**\n * @author Vincent Elcrin\n */\npublic class TestHumanTask extends AbstractManualTask {\n\n    private HumanTaskInstance humanTaskInstance;\n\n    private final static int GET_NEXT_NB_ATTEMPT = 30;\n\n    private final static int SLEEP_TIME_MS = 100;\n\n    /**\n     * Default Constructor.\n     */\n    public TestHumanTask(final ActivityInstance activityInstance) {\n        assert activityInstance instanceof HumanTaskInstance;\n        humanTaskInstance = (HumanTaskInstance) activityInstance;\n    }\n\n    public HumanTaskInstance getHumanTaskInstance() {\n        return humanTaskInstance;\n    }\n\n    /**\n     * @return the processInstance\n     */\n    private HumanTaskInstance fetchHumanTaskInstance(final APISession apiSession) {\n        try {\n            return TestProcess.getProcessAPI(apiSession).getHumanTaskInstance(getId());\n        } catch (final Exception e) {\n            throw new TestToolkitException(\"Can't get humanTask instance for <\" + getId() + \">\", e);\n        }\n    }\n\n    public void refreshHumanTaskInstanceInstance() {\n        humanTaskInstance = fetchHumanTaskInstance(TestToolkitCtx.getInstance().getInitiator().getSession());\n    }\n\n    public DataInstance getDataInstance(final String dataName) {\n        try {\n            return TestProcess.getProcessAPI(TestToolkitCtx.getInstance().getInitiator().getSession())\n                    .getActivityDataInstance(dataName, humanTaskInstance.getId());\n        } catch (final DataNotFoundException e) {\n            throw new TestToolkitException(\"Unable to find dataInstance \" + dataName, e);\n        }\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see org.bonitasoft.test.AbstractManualTask#getId()\n     */\n    @Override\n    public long getId() {\n        return humanTaskInstance.getId();\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see org.bonitasoft.test.toolkit.bpm.AbstractManualTask#getDescription()\n     */\n    @Override\n    public String getDescription() {\n        return humanTaskInstance.getDescription();\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see org.bonitasoft.test.toolkit.bpm.AbstractManualTask#getName()\n     */\n    @Override\n    public String getName() {\n        return humanTaskInstance.getName();\n    }\n\n    // /////////////////////////////////////////////////////////////////////////////\n    // / Assign\n    // /////////////////////////////////////////////////////////////////////////////\n\n    private TestHumanTask assignTo(final APISession apiSession, final TestUser user) {\n        final ProcessAPI processAPI = TestProcess.getProcessAPI(apiSession);\n        try {\n            processAPI.assignUserTask(humanTaskInstance.getId(), user.getId());\n        } catch (final Exception e) {\n            throw new TestToolkitException(\"Can't assign user\", e);\n        }\n        return this;\n    }\n\n    public TestHumanTask assignTo(final TestUser initiator, final TestUser user) {\n        return assignTo(initiator.getSession(), user);\n    }\n\n    public TestHumanTask assignTo(final TestUser user) {\n        return assignTo(TestToolkitCtx.getInstance().getInitiator(), user);\n    }\n\n    // ////////////////////////////////////////////////////////////////////////////\n    // / Execute\n    // ////////////////////////////////////////////////////////////////////////////\n    public void executeUserTask(final TestUser executor) {\n        final ProcessAPI processAPI = TestProcess.getProcessAPI(executor.getSession());\n        try {\n            processAPI.executeUserTask(humanTaskInstance.getId(), Collections.emptyMap());\n        } catch (final Exception e) {\n            throw new TestToolkitException(\"Can't execute user task <\" + humanTaskInstance.getId() + \">.\", e);\n        }\n    }\n\n    public void execute(final APISession apiSession) {\n        final ProcessAPI processAPI = TestProcess.getProcessAPI(apiSession);\n        try {\n            processAPI.executeFlowNode(humanTaskInstance.getId());\n        } catch (final Exception e) {\n            throw new TestToolkitException(\"Can't execute activity <\" + humanTaskInstance.getId() + \">.\", e);\n        }\n    }\n\n    public void archive(final APISession apiSession) {\n        try {\n            execute(apiSession);\n        } catch (final TestToolkitException e) {\n            if (!(e.getCause() instanceof ActivityExecutionException)) {\n                throw e;\n            }\n        }\n    }\n\n    public void archive(final TestUser initiator) {\n        archive(initiator.getSession());\n    }\n\n    public void archive() {\n        archive(TestToolkitCtx.getInstance().getInitiator());\n    }\n\n    // /////////////////////////////////////////////////////////////////////////////////\n    // Convenient method\n    // /////////////////////////////////////////////////////////////////////////////////\n\n    public void waitState(final String state) {\n        for (int i = 0; i < GET_NEXT_NB_ATTEMPT; i++) {\n            try {\n                Thread.sleep(SLEEP_TIME_MS);\n            } catch (final InterruptedException e) {\n                throw new TestToolkitException(\n                        \"Problem while waiting for state <\" + state + \"> for human task <\" + getId() + \">. Interrupted\",\n                        e);\n            }\n            refreshHumanTaskInstanceInstance();\n            if (getHumanTaskInstance() != null && state.equals(getHumanTaskInstance().getState())) {\n                break;\n            }\n        }\n        if (getHumanTaskInstance() == null || !state.equals(getHumanTaskInstance().getState())) {\n            throw new TestToolkitException(\n                    \"Expected state <\" + state + \"> has not been reached for human task<\" + getId() + \">.\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/bpm/TestProcess.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.test.toolkit.bpm;\n\nimport static java.util.stream.IntStream.range;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.actor.ActorCriterion;\nimport org.bonitasoft.engine.bpm.actor.ActorInstance;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.bar.form.model.FormMappingModel;\nimport org.bonitasoft.engine.bpm.category.Category;\nimport org.bonitasoft.engine.bpm.category.CategoryCriterion;\nimport org.bonitasoft.engine.bpm.flownode.ActivityDefinition;\nimport org.bonitasoft.engine.bpm.flownode.UserTaskDefinition;\nimport org.bonitasoft.engine.bpm.form.FormMappingDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.form.FormMappingTarget;\nimport org.bonitasoft.engine.form.FormMappingType;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.bonitasoft.test.toolkit.exception.TestToolkitException;\nimport org.bonitasoft.test.toolkit.organization.TestGroup;\nimport org.bonitasoft.test.toolkit.organization.TestRole;\nimport org.bonitasoft.test.toolkit.organization.TestToolkitCtx;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\n\n/**\n * @author Vincent Elcrin\n */\npublic class TestProcess {\n\n    private final ProcessDefinition processDefinition;\n\n    private boolean enabled = false;\n\n    private final List<ActorInstance> actors = new ArrayList<>();\n\n    public TestProcess(final APISession apiSession, final ProcessDefinitionBuilder processDefinitionBuilder) {\n        this.processDefinition = createProcessDefinition(apiSession, processDefinitionBuilder);\n    }\n\n    public TestProcess(final ProcessDefinitionBuilder processDefinitionBuilder) {\n        this(getSession(), processDefinitionBuilder);\n    }\n\n    public TestProcess(final APISession apiSession, final BusinessArchiveBuilder businessArchiveBuilder) {\n        this.processDefinition = deployProcessDefinition(apiSession, businessArchiveBuilder);\n    }\n\n    public TestProcess(final BusinessArchiveBuilder businessArchiveBuilder) {\n        this(getSession(), businessArchiveBuilder);\n    }\n\n    private static APISession getSession() {\n        return TestToolkitCtx.getInstance().getInitiator().getSession();\n    }\n\n    /**\n     * Create an archive and deploy process\n     */\n    private ProcessDefinition createProcessDefinition(final APISession apiSession,\n            final ProcessDefinitionBuilder processDefinitionBuilder) {\n        try {\n            return deployProcessDefinition(apiSession, new BusinessArchiveBuilder().createNewBusinessArchive()\n                    .setFormMappings(createDefaultProcessFormMapping(processDefinitionBuilder.getProcess()))\n                    .setProcessDefinition(processDefinitionBuilder.done()));\n        } catch (final InvalidProcessDefinitionException e) {\n            throw new TestToolkitException(\"Invalid process definition\", e);\n        }\n    }\n\n    public static FormMappingModel createDefaultProcessFormMapping(DesignProcessDefinition designProcessDefinition) {\n        FormMappingModel formMappingModel = new FormMappingModel();\n        formMappingModel.addFormMapping(FormMappingDefinitionBuilder\n                .buildFormMapping(\"http://url.com\", FormMappingType.PROCESS_START, FormMappingTarget.URL).build());\n        formMappingModel.addFormMapping(FormMappingDefinitionBuilder\n                .buildFormMapping(\"http://url.com\", FormMappingType.PROCESS_OVERVIEW, FormMappingTarget.URL).build());\n        for (ActivityDefinition activityDefinition : designProcessDefinition.getFlowElementContainer()\n                .getActivities()) {\n            if (activityDefinition instanceof UserTaskDefinition) {\n                formMappingModel.addFormMapping(FormMappingDefinitionBuilder\n                        .buildFormMapping(\"http://url.com\", FormMappingType.TASK, FormMappingTarget.URL)\n                        .withTaskname(activityDefinition.getName()).build());\n            }\n        }\n        return formMappingModel;\n    }\n\n    /**\n     * Deploy process from the archive\n     */\n    private ProcessDefinition deployProcessDefinition(final APISession apiSession,\n            final BusinessArchiveBuilder businessArchiveBuilder) {\n        try {\n            return getProcessAPI(apiSession).deploy(businessArchiveBuilder.done());\n        } catch (final Exception e) {\n            throw new TestToolkitException(\"Can't deploy business archive.\", e);\n        }\n    }\n\n    protected static ProcessAPI getProcessAPI(final APISession apiSession) {\n        ProcessAPI processAPI;\n        try {\n            processAPI = TenantAPIAccessor.getProcessAPI(apiSession);\n        } catch (final InvalidSessionException e) {\n            throw new TestToolkitException(\"Can't get process API. Invalid session\", e);\n        } catch (final BonitaHomeNotSetException e) {\n            throw new TestToolkitException(\"Can't get process API. Bonita home not set\", e);\n        } catch (final ServerAPIException e) {\n            throw new TestToolkitException(\"Can't get process API. Server API exception\", e);\n        } catch (final UnknownAPITypeException e) {\n            throw new TestToolkitException(\"Can't get process API. Unknown API type\", e);\n        }\n        return processAPI;\n    }\n\n    public ProcessDefinition getProcessDefinition() {\n        return this.processDefinition;\n    }\n\n    public long getId() {\n        return this.processDefinition.getId();\n    }\n\n    /**\n     * Set process enablement\n     */\n    protected TestProcess setEnable(final APISession apiSession, final boolean enabled) {\n        if (enabled && !this.enabled) {\n            enableProcess(apiSession);\n        } else if (!enabled && this.enabled) {\n            disableProcess(apiSession);\n        }\n        this.enabled = enabled;\n        return this;\n    }\n\n    protected void delete(final APISession apiSession) {\n        try {\n            // Delete all process instances\n            long nbDeletedProcessInstances;\n            do {\n                nbDeletedProcessInstances = getProcessAPI(apiSession).deleteProcessInstances(processDefinition.getId(),\n                        0, 100);\n            } while (nbDeletedProcessInstances > 0);\n\n            // Delete all archived process instances\n            long nbDeletedArchivedProcessInstances;\n            do {\n                nbDeletedArchivedProcessInstances = getProcessAPI(apiSession)\n                        .deleteArchivedProcessInstances(processDefinition.getId(), 0, 100);\n            } while (nbDeletedArchivedProcessInstances > 0);\n\n            getProcessAPI(apiSession).deleteProcessDefinition(processDefinition.getId());\n        } catch (DeletionException e) {\n            throw new TestToolkitException(\"Can't delete process <\" + this.processDefinition.getId() + \"> with name \"\n                    + this.processDefinition.getName(), e);\n        }\n    }\n\n    private void enableProcess(APISession apiSession) {\n        try {\n            getProcessAPI(apiSession).enableProcess(this.processDefinition.getId());\n        } catch (Exception e) {\n            throw new TestToolkitException(\"Can't enable process <\" + this.processDefinition.getId() + \">\", e);\n        }\n    }\n\n    private void disableProcess(APISession apiSession) {\n        try {\n            getProcessAPI(apiSession).disableProcess(this.processDefinition.getId());\n        } catch (Exception e) {\n            throw new TestToolkitException(\"Can't disable process <\" + this.processDefinition.getId() + \">\", e);\n        }\n    }\n\n    public TestProcess setEnable(final TestUser initiator, final boolean enabled) {\n        return setEnable(initiator.getSession(), enabled);\n    }\n\n    public void delete(final TestUser initiator) {\n        delete(initiator.getSession());\n    }\n\n    public TestProcess enable() {\n        return setEnable(TestToolkitCtx.getInstance().getInitiator(), true);\n    }\n\n    public TestProcess disable() {\n        return setEnable(TestToolkitCtx.getInstance().getInitiator(), false);\n    }\n\n    public void delete() {\n        delete(TestToolkitCtx.getInstance().getInitiator());\n    }\n\n    /**\n     * Add actors to enable process\n     * <p/>\n     * TODO: Need to evolve to choose on which Actors category the actor will be added\n     */\n    private void addActor(final APISession apiSession, final TestUser actor) {\n        final ProcessAPI processAPI = getProcessAPI(apiSession);\n        ActorInstance processActor;\n        try {\n            processActor = processAPI\n                    .getActors(this.processDefinition.getId(), 0, Integer.MAX_VALUE, ActorCriterion.NAME_ASC)\n                    .get(this.actors.size());\n            processAPI.addUserToActor(processActor.getId(), actor.getUser().getId());\n            this.actors.add(processActor);\n        } catch (final Exception e) {\n            throw new TestToolkitException(\"Can't get actors for <\" + this.processDefinition.getId() + \">.\", e);\n        }\n\n    }\n\n    public TestProcess addActor(final TestGroup actor) {\n        return addActor(TestToolkitCtx.getInstance().getInitiator().getSession(), actor);\n    }\n\n    private TestProcess addActor(final APISession apiSession, final TestGroup actor) {\n        try {\n            final ProcessAPI processAPI = getProcessAPI(apiSession);\n            final ActorInstance processActor = processAPI\n                    .getActors(this.processDefinition.getId(), 0, Integer.MAX_VALUE, ActorCriterion.NAME_ASC).get(\n                            this.actors.size());\n            processAPI.addGroupToActor(processActor.getId(), actor.getId());\n            this.actors.add(processActor);\n        } catch (final IndexOutOfBoundsException e) {\n            final String message = \"can't add actor to process \" + this.processDefinition.getId()\n                    + \" process definition has only \" + this.actors.size() + \" actors\";\n            throw new TestToolkitException(message, e);\n        } catch (final Exception e) {\n            throw new TestToolkitException(\"can't add actor to process \" + this.processDefinition.getId(), e);\n        }\n\n        return this;\n    }\n\n    public TestProcess addActor(final TestRole actor) {\n        return addActor(TestToolkitCtx.getInstance().getInitiator().getSession(), actor);\n    }\n\n    private TestProcess addActor(final APISession apiSession, final TestRole actor) {\n        try {\n            final ProcessAPI processAPI = getProcessAPI(apiSession);\n            final ActorInstance processActor = processAPI\n                    .getActors(this.processDefinition.getId(), 0, Integer.MAX_VALUE, ActorCriterion.NAME_ASC).get(\n                            this.actors.size());\n            processAPI.addRoleToActor(processActor.getId(), actor.getId());\n            this.actors.add(processActor);\n        } catch (final IndexOutOfBoundsException e) {\n            final String message = \"can't add actor to process \" + this.processDefinition.getId()\n                    + \" process definition has only \" + this.actors.size() + \" actors\";\n            throw new TestToolkitException(message, e);\n        } catch (final Exception e) {\n            throw new TestToolkitException(\"can't add actor to process \" + this.processDefinition.getId(), e);\n        }\n\n        return this;\n    }\n\n    public TestProcess addActor(final TestUser initiator, final TestUser actor) {\n        addActor(initiator.getSession(), actor);\n        return this;\n    }\n\n    public TestProcess addActor(final TestUser actor) {\n        return addActor(TestToolkitCtx.getInstance().getInitiator(), actor);\n    }\n\n    private TestCase startCase(final APISession apiSession) {\n        setEnable(apiSession, true);\n        TestCase testCase = new TestCase(createProcessInstance(apiSession));\n        testCase.waitProcessState(apiSession, TestCase.READY_STATE);\n        return testCase;\n    }\n\n    protected ProcessInstance createProcessInstance(final APISession apiSession) {\n        try {\n            return getProcessAPI(apiSession).startProcess(apiSession.getUserId(), processDefinition.getId());\n        } catch (final Exception e) {\n            throw new TestToolkitException(\"Can't start process <\" + processDefinition.getId() + \">\", e);\n        }\n    }\n\n    public TestCase startCase(final TestUser initiator) {\n        return startCase(initiator.getSession());\n    }\n\n    public TestCase startCase() {\n        return startCase(TestToolkitCtx.getInstance().getInitiator());\n    }\n\n    /**\n     * Start several cases\n     */\n    public void startCases(final int number) {\n        range(0, number).forEach(i -> startCase());\n    }\n\n    // /////////////////////////////////////////////////////////////////////////////////////////\n    // / SUPERVISOR\n    // /////////////////////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Add a user as process supervisor\n     */\n    private void addSupervisor(final APISession apiSession, final TestUser user) {\n        try {\n            getProcessAPI(apiSession).createProcessSupervisorForUser(this.processDefinition.getId(), user.getId());\n        } catch (final Exception e) {\n            throw new TestToolkitException(\"Unable to add supervisor\", e);\n        }\n    }\n\n    public void addSupervisor(final TestUser initiator, final TestUser user) {\n        addSupervisor(initiator.getSession(), user);\n    }\n\n    public void addSupervisor(final TestUser user) {\n        addSupervisor(TestToolkitCtx.getInstance().getInitiator(), user);\n    }\n\n    /**\n     * Add a role as process supervisor\n     */\n    private void addSupervisor(final APISession apiSession, final TestRole role) {\n        try {\n            getProcessAPI(apiSession).createProcessSupervisorForRole(this.processDefinition.getId(), role.getId());\n        } catch (final Exception e) {\n            throw new TestToolkitException(\"Unable to add supervisor\", e);\n        }\n    }\n\n    public void addSupervisor(final TestUser initiator, final TestRole role) {\n        addSupervisor(initiator.getSession(), role);\n    }\n\n    public void addSupervisor(final TestRole role) {\n        addSupervisor(TestToolkitCtx.getInstance().getInitiator(), role);\n    }\n\n    /**\n     * Add a group as process supervisor\n     */\n    private TestProcess addSupervisor(final APISession apiSession, final TestGroup group) {\n        try {\n            getProcessAPI(apiSession).createProcessSupervisorForGroup(this.processDefinition.getId(), group.getId());\n        } catch (final Exception e) {\n            throw new TestToolkitException(\"Unable to add supervisor\", e);\n        }\n        return this;\n    }\n\n    public TestProcess addSupervisor(final TestUser initiator, final TestGroup group) {\n        return addSupervisor(initiator.getSession(), group);\n    }\n\n    public TestProcess addSupervisor(final TestGroup group) {\n        return addSupervisor(TestToolkitCtx.getInstance().getInitiator(), group);\n    }\n\n    /**\n     * Add a memebership as process supervisor\n     */\n    private TestProcess addSupervisor(final APISession apiSession, final TestGroup group, final TestRole role) {\n        try {\n            getProcessAPI(apiSession).createProcessSupervisorForMembership(this.processDefinition.getId(),\n                    group.getId(), role.getId());\n        } catch (final Exception e) {\n            throw new TestToolkitException(\"Unable to add supervisor\", e);\n        }\n        return this;\n    }\n\n    public void addSupervisor(final TestUser initiator, final TestGroup group, final TestRole role) {\n        addSupervisor(initiator.getSession(), group, role);\n    }\n\n    public void addSupervisor(final TestGroup group, final TestRole role) {\n        addSupervisor(TestToolkitCtx.getInstance().getInitiator(), group, role);\n    }\n\n    public void addCategory(final long categoryId) {\n        try {\n            TenantAPIAccessor.getProcessAPI(getSession()).addCategoriesToProcess(getId(), List.of(categoryId));\n        } catch (final Exception e) {\n            throw new TestToolkitException(\"Can't add this process to this category. \" + e.getMessage(), e);\n        }\n\n    }\n\n    public List<TestCategory> getCategories() {\n\n        try {\n            final List<Category> categories = TenantAPIAccessor.getProcessAPI(getSession())\n                    .getCategoriesOfProcessDefinition(getId(), 0, 100, CategoryCriterion.NAME_ASC);\n            final List<TestCategory> results = new ArrayList<>(categories.size());\n\n            for (final Category category : categories) {\n                results.add(new TestCategory(category));\n            }\n\n            return results;\n        } catch (final Exception e) {\n            throw new TestToolkitException(\"Can't get categories\", e);\n        }\n\n    }\n\n    public List<ActorInstance> getActors() {\n        return this.actors;\n    }\n\n    public List<TestCase> listOpenCases() throws SearchException {\n        List<ProcessInstance> processInstances = searchOpenedProcessInstances();\n        return convertToCasesList(processInstances);\n    }\n\n    public List<TestCase> listAllOpenCases() throws SearchException {\n        List<ProcessInstance> processInstances = searchProcessInstances();\n        return convertToCasesList(processInstances);\n    }\n\n    private List<TestCase> convertToCasesList(List<ProcessInstance> processInstances) {\n        List<TestCase> cases = new ArrayList<>();\n        for (ProcessInstance instance : processInstances) {\n            cases.add(new TestCase(instance));\n        }\n        return cases;\n    }\n\n    private List<ProcessInstance> searchProcessInstances() throws SearchException {\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 100);\n        builder.filter(ProcessInstanceSearchDescriptor.PROCESS_DEFINITION_ID, getProcessDefinition().getId());\n        builder.differentFrom(ProcessInstanceSearchDescriptor.STATE_ID,\n                ProcessInstanceState.COMPLETED.getId());\n        return getProcessAPI(getSession()).searchProcessInstances(builder.done()).getResult();\n    }\n\n    private List<ProcessInstance> searchOpenedProcessInstances() throws SearchException {\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 100);\n        builder.filter(ProcessInstanceSearchDescriptor.PROCESS_DEFINITION_ID, getProcessDefinition().getId());\n        return getProcessAPI(getSession()).searchOpenProcessInstances(builder.done()).getResult();\n    }\n\n    public void deleteCases() throws Exception {\n        final ProcessAPI processAPI = TenantAPIAccessor.getProcessAPI(getSession());\n        int repeatNb = 10;\n        boolean repeat;\n        long sleep = 500;\n        Exception latestException = null;\n        do {\n            repeat = false;\n            repeatNb--;\n            final SearchOptions searchOptions = new SearchOptionsBuilder(0, 100000)\n                    .filter(ProcessInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDefinition.getId())\n                    .sort(ProcessInstanceSearchDescriptor.ID, Order.ASC).done();\n            for (ProcessInstance pi : processAPI.searchProcessInstances(searchOptions).getResult()) {\n                try {\n                    processAPI.deleteProcessInstance(pi.getId());\n                } catch (DeletionException e) {\n                    //ignore as it may be due a process instance finishing its execution\n                    repeat = true;\n                    latestException = e;\n                }\n            }\n            try {\n                processAPI.deleteArchivedProcessInstances(processDefinition.getId(), 0, 10000);\n            } catch (DeletionException e) {\n                //ignore as it may be due a process instance finishing its execution\n                repeat = true;\n                latestException = e;\n            }\n            Thread.sleep(sleep);\n        } while (repeat && repeatNb > 0);\n        if (repeat) {\n            throw latestException;\n        }\n\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/bpm/TestProcessFactory.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.test.toolkit.bpm;\n\nimport java.io.InputStream;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Random;\nimport java.util.UUID;\n\nimport org.apache.commons.io.IOUtils;\nimport org.bonitasoft.engine.bpm.bar.BarResource;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException;\nimport org.bonitasoft.engine.bpm.process.ProcessActivationException;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.expression.ExpressionType;\nimport org.bonitasoft.engine.expression.InvalidExpressionException;\nimport org.bonitasoft.test.toolkit.bpm.process.TestProcessConnector;\nimport org.bonitasoft.test.toolkit.exception.TestToolkitException;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\n\n/**\n * @author Vincent Elcrin\n */\npublic class TestProcessFactory {\n\n    protected static final String DEFAULT_HUMAN_TASK_PROCESS_NAME = \"Default human task process\";\n\n    protected static final String PROCESS_WITH_DOCUMENT_ATTACHED = \"Process with document attached\";\n\n    protected static final String PROCESS_CALL_ACTIVTY = \"Process call activity\";\n\n    private final Map<String, TestProcess> processList;\n\n    private static TestProcessFactory instance;\n\n    /**\n     * Default Constructor.\n     */\n    public TestProcessFactory() {\n        processList = new HashMap<>();\n    }\n\n    public static TestProcessFactory getInstance() {\n        if (instance == null) {\n            instance = new TestProcessFactory();\n        }\n        return instance;\n    }\n\n    private static String getRandomString() {\n        return String.valueOf(new Random().nextLong());\n    }\n\n    public void clear() throws Exception {\n        for (TestProcess testProcess : processList.values()) {\n            clear(testProcess);\n        }\n        processList.clear();\n    }\n\n    /**\n     * @return the processList\n     */\n    protected Map<String, TestProcess> getProcessList() {\n        return processList;\n    }\n\n    // ///////////////////////////////////////////////////////////////////////////////////\n    // / Process definitions\n    // ///////////////////////////////////////////////////////////////////////////////////\n\n    protected static ProcessDefinitionBuilder getDefaultProcessDefinitionBuilder(final String processName) {\n        return getDefaultProcessDefinitionBuilder(processName, \"1.0\");\n    }\n\n    public static ProcessDefinitionBuilder getDefaultProcessDefinitionBuilder(final String processName,\n            final String version) {\n        final ProcessDefinitionBuilder processDefinitionBuidler = new ProcessDefinitionBuilder()\n                .createNewInstance(processName, version);\n        processDefinitionBuidler.addActor(\"Employees\", true)\n                .addDescription(\"This a default process\")\n                .addStartEvent(\"Start\")\n                .addUserTask(\"Activity 1\", \"Employees\")\n                .addEndEvent(\"Finish\")\n                .addTransition(\"Start\", \"Activity 1\")\n                .addTransition(\"Activity 1\", \"Finish\");\n        return processDefinitionBuidler;\n    }\n\n    protected static BusinessArchiveBuilder getBusinessArchiveWithDocumentBuilder(final String processName) {\n\n        final ProcessDefinitionBuilder processDefinitionBuidler = new ProcessDefinitionBuilder()\n                .createNewInstance(processName, \"1.0\");\n        processDefinitionBuidler.addDocumentDefinition(\"Document 1667\").addContentFileName(\"filename.txt\")\n                .addFile(\"attachedfile.txt\");\n        processDefinitionBuidler.addActor(\"Employees\", true)\n                .addStartEvent(\"Start\")\n                .addUserTask(\"Activity 1\", \"Employees\")\n                .addEndEvent(\"Finish\")\n                .addTransition(\"Start\", \"Activity 1\")\n                .addTransition(\"Activity 1\", \"Finish\");\n        try {\n            return new BusinessArchiveBuilder().createNewBusinessArchive()\n                    .setFormMappings(TestProcess.createDefaultProcessFormMapping(processDefinitionBuidler.getProcess()))\n                    .addDocumentResource(\n                            new BarResource(\"attachedfile.txt\", \"thisisthecontentofthedocumentattached\".getBytes()))\n                    .setProcessDefinition(processDefinitionBuidler.done());\n        } catch (final InvalidProcessDefinitionException e) {\n            throw new TestToolkitException(\"Invalid process definition\", e);\n        }\n    }\n\n    private static ProcessDefinitionBuilder getCallActivityProcessDefinitionBuilder(\n            final ProcessDefinition processToStartViaCallActivity) {\n        Expression expressionName;\n        Expression expressionVersion;\n        try {\n            expressionName = new ExpressionBuilder().createNewInstance(\"process name\")\n                    .setExpressionType(ExpressionType.TYPE_CONSTANT)\n                    .setReturnType(String.class.getName())\n                    .setContent(processToStartViaCallActivity.getName()).done();\n\n            expressionVersion = new ExpressionBuilder().createNewInstance(\"process version\")\n                    .setExpressionType(ExpressionType.TYPE_CONSTANT)\n                    .setReturnType(String.class.getName())\n                    .setContent(processToStartViaCallActivity.getVersion()).done();\n        } catch (final InvalidExpressionException e) {\n            throw new TestToolkitException(\"Invalid expression definition\", e);\n        }\n\n        final ProcessDefinitionBuilder processDefinitionBuidler = new ProcessDefinitionBuilder()\n                .createNewInstance(PROCESS_CALL_ACTIVTY, \"1.0\");\n        processDefinitionBuidler.addActor(\"Employees\", true)\n                .addStartEvent(\"Start\")\n                .addCallActivity(\"Call Activity\", expressionName, expressionVersion)\n                .addEndEvent(\"Finish\")\n                .addTransition(\"Start\", \"Call Activity\")\n                .addTransition(\"Call Activity\", \"Finish\");\n        return processDefinitionBuidler;\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////\n    // / Factory accessors\n    // //////////////////////////////////////////////////////////////////////////////////\n\n    public static TestProcess createRandomResolvedProcess(final TestUser actor) {\n        return getRandomHumanTaskProcess().addActor(actor);\n    }\n\n    /**\n     * Variables :\n     * - variable1 : String\n     * - variable2 : Long\n     * - variable3 : Date\n     */\n    public static TestHumanTask createActivityWithVariables(TestUser initiator) throws InvalidExpressionException {\n        final String processName = \"processName\";\n        final ProcessDefinitionBuilder processDefinitionBuidler = new ProcessDefinitionBuilder().createNewInstance(\n                processName + (String.valueOf(UUID.randomUUID().getLeastSignificantBits()).substring(0, 5)), \"1.0\");\n        processDefinitionBuidler.addActor(\"Employees\", true)\n                .addDescription(\"This a default process\")\n                .addStartEvent(\"Start\")\n                .addUserTask(\"Activity 1\", \"Employees\")\n\n                .addData(\"variable1\", String.class.getName(),\n                        new ExpressionBuilder().createConstantStringExpression(\"defaultValue\"))\n                .addData(\"variable2\", Long.class.getName(), new ExpressionBuilder().createConstantLongExpression(1))\n                .addData(\"variable3\", Date.class.getName(),\n                        new ExpressionBuilder().createConstantDateExpression(\"428558400000\"))\n\n                .addEndEvent(\"Finish\")\n                .addTransition(\"Start\", \"Activity 1\")\n                .addTransition(\"Activity 1\", \"Finish\");\n        final TestProcess testProcess = new TestProcess(processDefinitionBuidler);\n        getInstance().getProcessList().put(processName, testProcess);\n        return testProcess.addActor(initiator).enable().startCase().getNextHumanTask().assignTo(initiator);\n    }\n\n    /**\n     * This process contains only a human task\n     */\n    public static TestProcess getDefaultHumanTaskProcess() {\n        return getHumanTaskProcess(DEFAULT_HUMAN_TASK_PROCESS_NAME);\n    }\n\n    public static TestProcess createProcessWithConnector(final TestProcessConnector testConnector) {\n        final String aProcessWithConnector = \"aProcessWithConnector\";\n        final ProcessDefinitionBuilder processBuilder = getDefaultProcessDefinitionBuilder(aProcessWithConnector);\n        processBuilder.addConnector(testConnector.getName(), testConnector.getId(), testConnector.getVersion(),\n                testConnector.getConnectorEvent());\n\n        try {\n            final InputStream stream = TestProcessFactory.class\n                    .getResourceAsStream(testConnector.getResourceFilePath());\n            final BarResource connectorResource = new BarResource(testConnector.getResourceFileName(),\n                    IOUtils.toByteArray(stream));\n            stream.close();\n            final BusinessArchiveBuilder barBuilder = new BusinessArchiveBuilder().createNewBusinessArchive()\n                    .setProcessDefinition(processBuilder.done());\n            barBuilder.addConnectorImplementation(connectorResource);\n            final TestProcess testProcess = new TestProcess(barBuilder);\n            getInstance().getProcessList().put(aProcessWithConnector, testProcess);\n            return testProcess;\n        } catch (final Exception e) {\n            throw new TestToolkitException(\"Unable to create a process with connector\", e);\n        }\n    }\n\n    public static TestProcess getProcessWithDocumentAttached() {\n        if (getInstance().getProcessList().get(PROCESS_WITH_DOCUMENT_ATTACHED) == null) {\n            final TestProcess testProcess = new TestProcess(\n                    getBusinessArchiveWithDocumentBuilder(PROCESS_WITH_DOCUMENT_ATTACHED));\n            getInstance().getProcessList().put(PROCESS_WITH_DOCUMENT_ATTACHED, testProcess);\n        }\n\n        return getInstance().getProcessList().get(PROCESS_WITH_DOCUMENT_ATTACHED);\n    }\n\n    public static TestProcess createProcessWith3Actors() {\n        final String processWith3Actors = \"processWith3Actors\";\n        final ProcessDefinitionBuilder builder = getDefaultProcessDefinitionBuilder(processWith3Actors);\n        builder.addActor(\"actor2\").addDescription(\"description actor2\").addActor(\"actor3\")\n                .addDescription(\"description actor3\");\n        final TestProcess testProcess = new TestProcess(builder);\n        getInstance().getProcessList().put(processWith3Actors, testProcess);\n        return testProcess;\n    }\n\n    public static TestProcess createProcessWithVariables(final String processName, final ProcessVariable... variables) {\n        final ProcessDefinitionBuilder builder = getDefaultProcessDefinitionBuilder(processName);\n        for (final ProcessVariable variable : variables) {\n            builder.addData(variable.getName(), variable.getClassName(), variable.getDefaultValue());\n        }\n        final TestProcess testProcess = new TestProcess(builder);\n        getInstance().getProcessList().put(processName, testProcess);\n        return testProcess;\n    }\n\n    public static TestProcess getRandomHumanTaskProcess() {\n        return getHumanTaskProcess(getRandomString());\n    }\n\n    /**\n     * This process contains only a human task\n     */\n    public static TestProcess getHumanTaskProcess(final String processName) {\n        if (getInstance().getProcessList().get(processName) == null) {\n            final TestProcess testProcess = new TestProcess(getDefaultProcessDefinitionBuilder(processName));\n            getInstance().getProcessList().put(processName, testProcess);\n        }\n\n        return getInstance().getProcessList().get(processName);\n    }\n\n    /**\n     * This process contains a call activity\n     */\n    public static TestProcess getCallActivityProcess(final ProcessDefinition processToStartViaCallActivity) {\n        if (getInstance().getProcessList().get(PROCESS_CALL_ACTIVTY) == null) {\n            final TestProcess testProcess = new TestProcess(\n                    getCallActivityProcessDefinitionBuilder(processToStartViaCallActivity));\n            getInstance().getProcessList().put(PROCESS_CALL_ACTIVTY, testProcess);\n        }\n        return getInstance().getProcessList().get(PROCESS_CALL_ACTIVTY);\n    }\n\n    public void check() {\n        if (!getProcessList().isEmpty()) {\n            throw new RuntimeException(\n                    this.getClass().getName() + \" cannot be reset because the list is not empty: \" + getProcessList());\n        }\n    }\n\n    private void clear(TestProcess testProcess) throws Exception {\n        testProcess.deleteCases();\n        try {\n            testProcess.disable();\n        } catch (TestToolkitException e) {\n            if (!(e.getCause() instanceof ProcessActivationException)) {\n                throw e;\n            }\n            //ignore as the process can be disabled\n        }\n        testProcess.delete();\n    }\n\n    public void delete(TestProcess testProcess) throws Exception {\n        clear(testProcess);\n        getProcessList().remove(testProcess.getProcessDefinition().getName());\n    }\n\n    public void remove(TestProcess testProcess) {\n        getProcessList().remove(testProcess.getProcessDefinition().getName());\n    }\n\n    public void add(TestProcess testProcess) {\n        getProcessList().put(testProcess.getProcessDefinition().getName(), testProcess);\n    }\n\n    /**\n     * @param parameters\n     *        <name, type>\n     */\n    public static TestProcess createProcessWithParameters(Map<String, String> parameters) {\n        ProcessDefinitionBuilder processBuilder = getDefaultProcessDefinitionBuilder(\"aProcessWithParameters\");\n        for (Map.Entry<String, String> parameter : parameters.entrySet()) {\n            processBuilder.addParameter(parameter.getKey(), parameter.getValue());\n        }\n        try {\n            BusinessArchiveBuilder barBuilder = new BusinessArchiveBuilder().createNewBusinessArchive()\n                    .setParameters(parameters).setProcessDefinition(processBuilder.done());\n            final TestProcess testProcess = new TestProcess(barBuilder);\n            getInstance().getProcessList().put(testProcess.getProcessDefinition().getName(), testProcess);\n            return testProcess;\n        } catch (InvalidProcessDefinitionException e) {\n            throw new TestToolkitException(\"Unable to create a process with parameters\", e);\n        }\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/bpm/process/TestActorMemberFactory.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.test.toolkit.bpm.process;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.actor.ActorMember;\nimport org.bonitasoft.test.toolkit.organization.TestGroup;\nimport org.bonitasoft.test.toolkit.organization.TestRole;\nimport org.bonitasoft.test.toolkit.organization.TestToolkitCtx;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\n\n/**\n * @author Colin PUY\n */\npublic class TestActorMemberFactory {\n\n    public static ActorMember createMembershipActorMember(long actorId, TestGroup testGroup, TestRole testRole)\n            throws Exception {\n        return getProcessAPI().addRoleAndGroupToActor(actorId, testRole.getId(), testGroup.getId());\n    }\n\n    public static ActorMember createUserActorMember(long actorId, TestUser testUser) throws Exception {\n        return getProcessAPI().addUserToActor(actorId, testUser.getId());\n    }\n\n    public static ActorMember createGroupActorMember(long actorId, TestGroup testGroup) throws Exception {\n        return getProcessAPI().addGroupToActor(actorId, testGroup.getId());\n    }\n\n    private static ProcessAPI getProcessAPI() throws Exception {\n        return TenantAPIAccessor.getProcessAPI(TestToolkitCtx.getInstance().getInitiator().getSession());\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/bpm/process/TestProcessConnector.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.test.toolkit.bpm.process;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\n\n/**\n * @author Colin PUY\n */\npublic class TestProcessConnector {\n\n    private final String name;\n    private final String id;\n    private final String version;\n    private final String implementationClassname;\n    private final String implementationId;\n    private final ConnectorEvent connectorEvent;\n\n    private final String resourceFileName;\n    private final String resourceFilePath;\n\n    private final List<String> dependencies = new ArrayList<>();\n\n    public TestProcessConnector(String name, String id, String version, String implementationClassname,\n            String implementationId,\n            ConnectorEvent connectorEvent, String resourceFileName, String resourceFilePath) {\n        super();\n        this.name = name;\n        this.id = id;\n        this.version = version;\n        this.implementationClassname = implementationClassname;\n        this.implementationId = implementationId;\n        this.connectorEvent = connectorEvent;\n        this.resourceFileName = resourceFileName;\n        this.resourceFilePath = resourceFilePath;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public String getId() {\n        return id;\n    }\n\n    public String getVersion() {\n        return version;\n    }\n\n    public ConnectorEvent getConnectorEvent() {\n        return connectorEvent;\n    }\n\n    public String getResourceFileName() {\n        return resourceFileName;\n    }\n\n    public String getResourceFilePath() {\n        return resourceFilePath;\n    }\n\n    public String getImplementationClassname() {\n        return implementationClassname;\n    }\n\n    public String getImplementationId() {\n        return implementationId;\n    }\n\n    public TestProcessConnector addDependency(String dependency) {\n        dependencies.add(dependency);\n        return this;\n    }\n\n    public List<String> getDependencies() {\n        return dependencies;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/bpm/process/TestProcessConnectorFactory.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.test.toolkit.bpm.process;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\n\n/**\n * @author Colin PUY\n */\npublic final class TestProcessConnectorFactory {\n\n    public static TestProcessConnector getDefaultConnector() {\n        TestProcessConnector testProcessConnector = new TestProcessConnector(\"aConnector\",\n                \"org.bonitasoft.test.toolkit.connector.TestConnector\", \"1.0\",\n                \"org.bonitasoft.test.toolkit.connector.TestConnector\",\n                \"org.bonitasoft.test.toolkit.connector.testConnector\", ConnectorEvent.ON_ENTER,\n                \"TestConnector.impl\", \"/org/bonitasoft/test/toolkit/connector/TestConnector.impl\");\n        testProcessConnector.addDependency(\"aDependency.jar\").addDependency(\"anOtherDependency.jar\");\n        return testProcessConnector;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/exception/NextActivityIsNotAllowedStateException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.test.toolkit.exception;\n\nimport java.text.MessageFormat;\n\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\n\n/**\n * @author Vincent Elcrin\n */\npublic class NextActivityIsNotAllowedStateException extends TestToolkitException {\n\n    /**\n     *\n     */\n    private static final long serialVersionUID = 8542205212242675994L;\n\n    /**\n     * Default Constructor.\n     */\n    public NextActivityIsNotAllowedStateException(final HumanTaskInstance humanTask) {\n        super(MessageFormat.format(\"Activity with id {0} is the state {1} which is not allowed.\", humanTask.getId(),\n                humanTask.getState()));\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/exception/NoActivityLeftException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.test.toolkit.exception;\n\n/**\n * @author Vincent Elcrin\n */\npublic class NoActivityLeftException extends TestToolkitException {\n\n    /**\n     *\n     */\n    private static final long serialVersionUID = 5201304240169814990L;\n\n    /**\n     * Default Constructor.\n     */\n    public NoActivityLeftException() {\n        super(\"The case is archived\");\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/exception/TestToolkitException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.test.toolkit.exception;\n\n/**\n * @author Vincent Elcrin\n */\npublic class TestToolkitException extends RuntimeException {\n\n    /**\n     * ID used by serialization.\n     */\n    private static final long serialVersionUID = -2678731898708770494L;\n\n    /**\n     * Default constructor.\n     */\n    public TestToolkitException() {\n        super();\n    }\n\n    /**\n     * Default constructor.\n     *\n     * @param message\n     */\n    public TestToolkitException(final String message) {\n        super(message);\n    }\n\n    /**\n     * Default constructor.\n     *\n     * @param message\n     */\n    public TestToolkitException(final String message, final Exception e) {\n        super(message, e);\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/organization/AdminUser.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.test.toolkit.organization;\n\n/**\n * @author Vincent Elcrin\n */\npublic final class AdminUser extends TestUser {\n\n    private static final String USERNAME = \"install\";\n\n    private static final String PASSWORD = \"install\";\n\n    /**\n     * Default Constructor.\n     *\n     * @throws Exception\n     */\n    protected AdminUser() {\n        super(USERNAME, PASSWORD);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/organization/IdentityAccessor.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.test.toolkit.organization;\n\nimport org.bonitasoft.engine.api.IdentityAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.test.toolkit.exception.TestToolkitException;\n\n/**\n * @author Colin PUY\n */\npublic class IdentityAccessor {\n\n    public IdentityAPI getIdentityAPI(final APISession apiSession) {\n        try {\n            return TenantAPIAccessor.getIdentityAPI(apiSession);\n        } catch (final Exception e) {\n            throw new TestToolkitException(\"Can't get identy api\", e);\n        }\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/organization/TestGroup.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.test.toolkit.organization;\n\nimport org.bonitasoft.engine.api.IdentityAPI;\nimport org.bonitasoft.engine.identity.Group;\nimport org.bonitasoft.engine.identity.GroupCreator;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.test.toolkit.bpm.TestActor;\nimport org.bonitasoft.test.toolkit.exception.TestToolkitException;\n\n/**\n * @author Vincent Elcrin\n */\npublic class TestGroup implements TestActor {\n\n    private final Group group;\n\n    public TestGroup(final GroupCreator creator) {\n        this.group = createGroup(TestToolkitCtx.getInstance().getInitiator().getSession(), creator);\n    }\n\n    private Group createGroup(APISession apiSession, GroupCreator creator) {\n        final IdentityAPI identityAPI = new IdentityAccessor().getIdentityAPI(apiSession);\n        try {\n            return identityAPI.createGroup(creator);\n        } catch (final Exception e) {\n            throw new TestToolkitException(\"Can't create group\", e);\n        }\n    }\n\n    public void delete() throws Exception {\n        final IdentityAPI identityAPI = new IdentityAccessor()\n                .getIdentityAPI(TestToolkitCtx.getInstance().getInitiator().getSession());\n        try {\n            identityAPI.deleteGroup(this.group.getId());\n        } catch (final Exception e) {\n            throw new TestToolkitException(\"Can't delete group\", e);\n        }\n    }\n\n    public long getId() {\n        return this.group.getId();\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/organization/TestGroupFactory.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.test.toolkit.organization;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Random;\n\nimport org.bonitasoft.engine.identity.GroupCreator;\n\n/**\n * @author Séverin Moussel\n */\npublic class TestGroupFactory {\n\n    private static final String NAME_R_AND_D = \"RetD\";\n\n    private static final String DESCRIPTION_R_AND_D = \"The team that drinks all the coffee\";\n\n    private static final String NAME_WEB = \"Web\";\n\n    private static final String DESCRIPTION_WEB = \"The team that also drinks all the beer\";\n\n    private final Map<String, TestGroup> groupList;\n\n    private static TestGroupFactory instance;\n\n    /**\n     * Default Constructor.\n     */\n    public TestGroupFactory() {\n        this.groupList = new HashMap<>();\n    }\n\n    public static TestGroupFactory getInstance() {\n        if (instance == null) {\n            instance = new TestGroupFactory();\n        }\n        return instance;\n    }\n\n    public void clear() throws Exception {\n        for (TestGroup testGroup : this.groupList.values()) {\n            testGroup.delete();\n        }\n        this.groupList.clear();\n    }\n\n    /**\n     * @return the userList\n     */\n    private Map<String, TestGroup> getGroupList() {\n        return this.groupList;\n    }\n\n    public static List<TestGroup> createRandomGroups(final int nbOfGroups) {\n        final List<TestGroup> results = new ArrayList<>();\n\n        for (int i = 0; i < nbOfGroups; i++) {\n            results.add(createGroup(getRandomString(), getRandomString()));\n        }\n\n        return results;\n    }\n\n    private static String getRandomString() {\n        return String.valueOf(new Random().nextLong());\n    }\n\n    // ////////////////////////////////////////////////////////////////////////////////\n    // / Group's creation\n    // ////////////////////////////////////////////////////////////////////////////////\n\n    /**\n     * @param name\n     * @param description\n     * @return\n     */\n    public static TestGroup createGroup(final String name, final String description) {\n        if (getInstance().getGroupList().get(name) == null) {\n            final GroupCreator groupBuilder = new GroupCreator(name).setDescription(description);\n\n            getInstance().getGroupList().put(name, new TestGroup(groupBuilder));\n        }\n\n        return getInstance().getGroupList().get(name);\n    }\n\n    public static TestGroup getRAndD() {\n        return createGroup(NAME_R_AND_D, DESCRIPTION_R_AND_D);\n    }\n\n    public static TestGroup getWeb() {\n        return createGroup(NAME_WEB, DESCRIPTION_WEB);\n    }\n\n    public void check() {\n        if (!getGroupList().isEmpty()) {\n            throw new RuntimeException(\n                    this.getClass().getName() + \" cannot be reset because the list is not empty: \" + getGroupList());\n        }\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/organization/TestMembership.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.test.toolkit.organization;\n\nimport org.bonitasoft.engine.api.IdentityAPI;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.test.toolkit.bpm.TestActor;\nimport org.bonitasoft.test.toolkit.exception.TestToolkitException;\n\n/**\n * @author Séverin Moussel\n */\npublic class TestMembership implements TestActor {\n\n    public TestMembership(final APISession apiSession, final long userId, final long groupId, final long roleId) {\n        createMembership(apiSession, userId, groupId, roleId);\n    }\n\n    public TestMembership(final long userId, final long groupId, final long roleId) {\n        this(TestToolkitCtx.getInstance().getInitiator().getSession(), userId, groupId, roleId);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////\n    // / Membership creation\n    // //////////////////////////////////////////////////////////////////////////////////\n\n    private void createMembership(final APISession apiSession, final long userId, final long groupId,\n            final long roleId) {\n        final IdentityAPI identityAPI = TestUser.getIdentityAPI(apiSession);\n        try {\n            identityAPI.addUserMembership(userId, groupId, roleId);\n        } catch (final Exception e) {\n            throw new TestToolkitException(\n                    \"Can't create membership <\" + userId + \"/\" + groupId + \"/\" + roleId + \"/\" + \">\", e);\n        }\n    }\n\n    public long getId() {\n        throw new IllegalStateException(\"Never called!\");\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/organization/TestMembershipFactory.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.test.toolkit.organization;\n\n/**\n * @author Séverin Moussel\n */\npublic class TestMembershipFactory {\n\n    public static void assignMembership(final TestUser user, final TestGroup group, final TestRole role) {\n        new TestMembership(user.getId(), group.getId(), role.getId());\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/organization/TestRole.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.test.toolkit.organization;\n\nimport org.bonitasoft.engine.identity.Role;\nimport org.bonitasoft.engine.identity.RoleCreator;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.test.toolkit.bpm.TestActor;\nimport org.bonitasoft.test.toolkit.exception.TestToolkitException;\n\n/**\n * @author Séverin Moussel\n */\npublic class TestRole extends IdentityAccessor implements TestActor {\n\n    private final Role role;\n    private final APISession apiSession;\n\n    public TestRole(RoleCreator creator) {\n        this(TestToolkitCtx.getInstance().getInitiator().getSession(), creator);\n    }\n\n    public TestRole(APISession apiSession, RoleCreator creator) {\n        this.apiSession = apiSession;\n        this.role = createRole(creator);\n        /*\n         * System.err.println(\"\\n\\n\");\n         * System.err.println(\"Building role: \" + role.getName());\n         * Thread.dumpStack();\n         * System.err.println(\"\\n\\n\");\n         */\n    }\n\n    private Role createRole(RoleCreator creator) {\n        try {\n            return getIdentityAPI(apiSession).createRole(creator);\n        } catch (final Exception e) {\n            throw new TestToolkitException(\"Can't create role <\" + creator + \">\", e);\n        }\n    }\n\n    public Role getRole() {\n        return this.role;\n    }\n\n    public long getId() {\n        return this.role.getId();\n    }\n\n    public void delete() {\n        try {\n            getIdentityAPI(apiSession).deleteRole(this.role.getId());\n        } catch (final Exception e) {\n            throw new TestToolkitException(\"Can't delete role <\" + this.role.getId() + \">\", e);\n        }\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/organization/TestRoleFactory.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.test.toolkit.organization;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Random;\n\nimport org.bonitasoft.engine.identity.RoleCreator;\n\n/**\n * @author Séverin Moussel\n */\npublic class TestRoleFactory {\n\n    private static final String NAME_MANAGER = \"Manager\";\n    private static final String DESCRIPTION_MANAGER = \"Team manager\";\n    private static final String NAME_DEVELOPER = \"Developer\";\n    private static final String DESCRIPTION_DEVELOPER = \"The man who write code and drink a lot of coffee\";\n\n    private final Map<String, TestRole> roleList;\n    private static TestRoleFactory instance;\n\n    private TestRoleFactory() {\n        this.roleList = new HashMap<>();\n    }\n\n    public static TestRoleFactory getInstance() {\n        if (instance == null) {\n            instance = new TestRoleFactory();\n        }\n        return instance;\n    }\n\n    public void clear() {\n        for (TestRole testRole : this.roleList.values()) {\n            testRole.delete();\n        }\n        this.roleList.clear();\n    }\n\n    /**\n     * @return the userList\n     */\n    private Map<String, TestRole> getRoleList() {\n        return this.roleList;\n    }\n\n    public List<TestRole> createRandomRoles(final int nbOfRoles) {\n        final List<TestRole> results = new ArrayList<>();\n\n        for (int i = 0; i < nbOfRoles; i++) {\n            final String name = getRandomString();\n            RoleCreator creator = new RoleCreator(name).setDescription(getRandomString());\n            final TestRole testRole = new TestRole(creator);\n            results.add(testRole);\n            getInstance().getRoleList().put(name, testRole);\n        }\n\n        return results;\n    }\n\n    private static String getRandomString() {\n        return String.valueOf(new Random().nextLong());\n    }\n\n    // ////////////////////////////////////////////////////////////////////////////////\n    // / Role's creation\n    // ////////////////////////////////////////////////////////////////////////////////\n\n    public static TestRole createRole(final String name, final String description) {\n        if (getInstance().getRoleList().get(name) == null) {\n            RoleCreator creator = new RoleCreator(name).setDescription(description);\n            getInstance().getRoleList().put(name, new TestRole(creator));\n        }\n\n        return getInstance().getRoleList().get(name);\n    }\n\n    public static TestRole getManager() {\n        return createRole(NAME_MANAGER, DESCRIPTION_MANAGER);\n    }\n\n    public static TestRole getDeveloper() {\n        return createRole(NAME_DEVELOPER, DESCRIPTION_DEVELOPER);\n    }\n\n    public void check() {\n        if (!getRoleList().isEmpty()) {\n            throw new RuntimeException(\n                    this.getClass().getName() + \" cannot be reset because the list is not empty: \" + getRoleList());\n        }\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/organization/TestToolkitCtx.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.test.toolkit.organization;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.test.toolkit.bpm.TestCategoryFactory;\nimport org.bonitasoft.test.toolkit.bpm.TestProcessFactory;\n\n/**\n * @author Vincent Elcrin\n */\npublic class TestToolkitCtx {\n\n    protected static final String ADMIN_USER = \"adminuser\";\n\n    protected static final String INITIATOR = \"initiator\";\n\n    protected static TestToolkitCtx instance;\n\n    protected final Map<String, Object> sessionsVariables;\n\n    protected TestToolkitCtx() {\n        this.sessionsVariables = new HashMap<>();\n    }\n\n    /**\n     * Singleton\n     */\n    public static TestToolkitCtx getInstance() {\n        if (instance == null) {\n            instance = new TestToolkitCtx();\n        }\n\n        return instance;\n    }\n\n    public void clearSession() throws Exception {\n        /*\n         * Clear Factories\n         */\n        clearFactories();\n\n        /*\n         * Clear session's variables\n         */\n        this.sessionsVariables.clear();\n    }\n\n    protected void clearFactories() throws Exception {\n        TestUserFactory.getInstance().clear();\n        TestProcessFactory.getInstance().clear();\n        TestGroupFactory.getInstance().clear();\n        TestRoleFactory.getInstance().clear();\n        TestCategoryFactory.getInstance().clear();\n    }\n\n    protected void checkFactories() {\n        TestUserFactory.getInstance().check();\n        TestProcessFactory.getInstance().check();\n        TestGroupFactory.getInstance().check();\n        TestRoleFactory.getInstance().check();\n        TestCategoryFactory.getInstance().check();\n    }\n\n    /**\n     * Admin user use login and password used during default tenant creation\n     *\n     * @return The Admin user\n     */\n    public AdminUser getAdminUser() {\n        if (!this.sessionsVariables.containsKey(ADMIN_USER)) {\n            this.sessionsVariables.put(ADMIN_USER, new AdminUser());\n        }\n        return (AdminUser) this.sessionsVariables.get(ADMIN_USER);\n    }\n\n    /**\n     * Initiator is a convenient notion to set a test user as source of all transactions done with the framework via its\n     * API Session.\n     *\n     * @param initiator\n     */\n    public void setInitiator(final TestUser initiator) {\n        this.sessionsVariables.put(INITIATOR, initiator);\n    }\n\n    public TestUser getInitiator() {\n        if (!this.sessionsVariables.containsKey(INITIATOR)) {\n            setInitiator(getAdminUser());\n        }\n        return (TestUser) this.sessionsVariables.get(INITIATOR);\n    }\n\n    public void check() throws Exception {\n        checkFactories();\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/organization/TestUser.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.test.toolkit.organization;\n\nimport org.bonitasoft.engine.api.IdentityAPI;\nimport org.bonitasoft.engine.api.LoginAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.NotFoundException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.identity.ContactDataCreator;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.identity.UserCreator;\nimport org.bonitasoft.engine.platform.LogoutException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.bonitasoft.engine.session.SessionNotFoundException;\nimport org.bonitasoft.test.toolkit.bpm.TestActor;\nimport org.bonitasoft.test.toolkit.exception.TestToolkitException;\n\n/**\n * @author Vincent Elcrin\n */\npublic class TestUser implements TestActor {\n\n    private final String userName;\n\n    private final String password;\n\n    private User user;\n\n    private APISession apiSession;\n\n    private boolean loggedIn = false;\n\n    protected TestUser(final String userName, final String password) {\n        this.userName = userName;\n        this.password = password;\n    }\n\n    public TestUser(final APISession apiSession, final String userName, final String password) {\n        this.user = createUser(apiSession, userName, password);\n        this.userName = userName;\n        this.password = password;\n        /*\n         * System.err.println(\"\\n\\n\");\n         * System.err.println(\"Building user: \" + user.getUserName());\n         * Thread.dumpStack();\n         * System.err.println(\"\\n\\n\");\n         */\n    }\n\n    public TestUser(final APISession apiSession, UserCreator userCreator, final ContactDataCreator personalInfoCreator,\n            final ContactDataCreator professionalInfoCreator) {\n        this.user = createUser(apiSession, userCreator, personalInfoCreator, professionalInfoCreator);\n        this.userName = (String) userCreator.getFields().get(UserCreator.UserField.NAME);\n        this.password = (String) userCreator.getFields().get(UserCreator.UserField.PASSWORD);\n        /*\n         * System.err.println(\"\\n\\n\");\n         * System.err.println(\"Building user: \" + user.getUserName());\n         * Thread.dumpStack();\n         * System.err.println(\"\\n\\n\");\n         */\n    }\n\n    public TestUser(final APISession apiSession, final UserCreator userBuilder) {\n        this(apiSession, userBuilder, new ContactDataCreator(), new ContactDataCreator());\n    }\n\n    private APISession logIn(final String userName, final String password) {\n        LoginAPI loginAPI;\n        APISession apiSession;\n        try {\n            loginAPI = TenantAPIAccessor.getLoginAPI();\n            apiSession = loginAPI.login(userName, password);\n        } catch (final Exception e) {\n            throw new TestToolkitException(\"Can't log user <\" + userName + \"> in\", e);\n        }\n\n        this.loggedIn = true;\n        return apiSession;\n    }\n\n    private void logOut(final APISession apiSession) {\n        LoginAPI loginAPI;\n        try {\n            loginAPI = TenantAPIAccessor.getLoginAPI();\n            loginAPI.logout(apiSession);\n        } catch (final BonitaHomeNotSetException e) {\n            throw new TestToolkitException(\"Can't get api to log out. Bonita home not set\", e);\n        } catch (final ServerAPIException e) {\n            throw new TestToolkitException(\"Can't get api to log out. Server api exception\", e);\n        } catch (final UnknownAPITypeException e) {\n            throw new TestToolkitException(\"Can't get api to log out. Unknown api type\", e);\n        } catch (final LogoutException e) {\n            throw new TestToolkitException(\"Can't get log out user <\" + apiSession.getUserName() + \">\", e);\n        } catch (SessionNotFoundException e) {\n            throw new TestToolkitException(\"Can't find the session to log out\", e);\n        }\n        this.loggedIn = false;\n    }\n\n    public APISession logIn() {\n        this.apiSession = logIn(this.userName, this.password);\n        return this.apiSession;\n    }\n\n    public void logOut() {\n        if (this.apiSession != null) {\n            logOut(this.apiSession);\n        }\n    }\n\n    public APISession getSession() {\n        if (!this.loggedIn) {\n            logIn();\n        }\n        return this.apiSession;\n    }\n\n    protected static IdentityAPI getIdentityAPI(final APISession apiSession) {\n        IdentityAPI identityAPI;\n        try {\n            identityAPI = TenantAPIAccessor.getIdentityAPI(apiSession);\n        } catch (final InvalidSessionException e) {\n            throw new TestToolkitException(\"Can't get identity api. Invalid session\", e);\n        } catch (final BonitaHomeNotSetException e) {\n            throw new TestToolkitException(\"Can't get identity api. Bonita home not set\", e);\n        } catch (final ServerAPIException e) {\n            throw new TestToolkitException(\"Can't get identity api. Server api exception\", e);\n        } catch (final UnknownAPITypeException e) {\n            throw new TestToolkitException(\"Can't get identity api. Unknown api type\", e);\n        }\n        return identityAPI;\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////\n    // / Users creation\n    // //////////////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Create user with only username & password\n     */\n    private User createUser(final APISession apiSession, final String userName, final String password) {\n        final IdentityAPI identityAPI = getIdentityAPI(apiSession);\n        User newUser;\n        try {\n            newUser = identityAPI.createUser(userName, password);\n        } catch (final AlreadyExistsException e) {\n            try {\n                newUser = identityAPI.getUserByUserName(userName);\n            } catch (final Exception e1) {\n                throw new TestToolkitException(\"Can't get user <\" + userName + \">\", e);\n            }\n        } catch (final Exception e) {\n            throw new TestToolkitException(\"Can't create user <\" + userName + \">\", e);\n        }\n        return newUser;\n    }\n\n    /**\n     * Create user with user engine's builder\n     */\n    private User createUser(final APISession apiSession, final UserCreator creator,\n            final ContactDataCreator personalInfoCreator,\n            final ContactDataCreator professionalInfoBuilder) {\n        final IdentityAPI identityAPI = getIdentityAPI(apiSession);\n        User newUser;\n        try {\n            try {\n                creator.setPersonalContactData(personalInfoCreator);\n                creator.setPersonalContactData(professionalInfoBuilder);\n                newUser = identityAPI.createUser(creator);\n            } catch (final AlreadyExistsException e) {\n                try {\n                    String userName = (String) creator.getFields().get(UserCreator.UserField.NAME);\n                    newUser = identityAPI.getUserByUserName(userName);\n                } catch (final NotFoundException getEx) {\n                    throw new TestToolkitException(\"User <\" + userName + \"> not found\", e);\n                }\n            } catch (final CreationException e) {\n                throw new TestToolkitException(\"Can't create user\", e);\n            }\n        } catch (final InvalidSessionException e) {\n            throw new TestToolkitException(\"Can't get identity api to create user. Invalid session\", e);\n        }\n\n        return newUser;\n    }\n\n    public TestUser createUser(final UserCreator userBuilder) {\n        return new TestUser(getSession(), userBuilder);\n    }\n\n    public TestUser createUser(final String userName, final String password) {\n        return new TestUser(getSession(), userName, password);\n    }\n\n    public void delete(final TestUser testUser) {\n        final IdentityAPI identityAPI = getIdentityAPI(getSession());\n        try {\n            identityAPI.deleteUser(testUser.getUser().getId());\n        } catch (final Exception e) {\n            throw new TestToolkitException(\"Can't delete user\", e);\n        }\n    }\n\n    public User getUser() {\n        return this.user;\n    }\n\n    public long getId() {\n        return this.user.getId();\n    }\n\n    public String getUserName() {\n        return userName;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/organization/TestUserFactory.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.test.toolkit.organization;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.identity.ContactDataCreator;\nimport org.bonitasoft.engine.identity.UserCreator;\n\n/**\n * @author Vincent Elcrin\n */\npublic class TestUserFactory {\n\n    private static final String JOHN_CARPENTER_LOGIN = \"john.carpenter\";\n\n    private static final String TEAM_MANAGER_LOGIN = \"team.manager\";\n\n    private static final String RIDLEY_SCOTT_LOGIN = \"ridley.scott\";\n\n    private static final String MR_SPECHAR_LOGIN = \"#*Ã©Ã Ã¢Ã¤Ã«ÃªÃ©~Ã§ÃžÅ¡Å’Ã˜Ã�Ã†\";\n\n    private final Map<String, TestUser> userList;\n\n    private static TestUserFactory instance;\n\n    /**\n     * Default Constructor.\n     */\n    public TestUserFactory() {\n        this.userList = new HashMap<>();\n    }\n\n    public static TestUserFactory getInstance() {\n        if (instance == null) {\n            instance = new TestUserFactory();\n        }\n        return instance;\n    }\n\n    public void clear() {\n        this.userList.clear();\n    }\n\n    /**\n     * @return the userList\n     */\n    public Map<String, TestUser> getUserList() {\n        return userList;\n    }\n\n    // ////////////////////////////////////////////////////////////////////////////////\n    // / User's creation\n    // ////////////////////////////////////////////////////////////////////////////////\n\n    public static TestUser getJohnCarpenter() {\n        if (getInstance().getUserList().get(JOHN_CARPENTER_LOGIN) == null) {\n            UserCreator creator = new UserCreator(JOHN_CARPENTER_LOGIN, \"bpm\")\n                    .setFirstName(\"John\")\n                    .setLastName(\"Carpenter\")\n                    .setJobTitle(\"Director\");\n            getInstance().getUserList().put(JOHN_CARPENTER_LOGIN,\n                    new TestUser(TestToolkitCtx.getInstance().getAdminUser().logIn(), creator));\n            TestToolkitCtx.getInstance().getAdminUser().logOut();\n        }\n\n        return getInstance().getUserList().get(JOHN_CARPENTER_LOGIN);\n    }\n\n    public static TestUser getTeamManagerUser() {\n        if (getInstance().getUserList().get(TEAM_MANAGER_LOGIN) == null) {\n            UserCreator creator = new UserCreator(TEAM_MANAGER_LOGIN, \"bpm\")\n                    .setFirstName(\"Team\")\n                    .setLastName(\"Manager\")\n                    .setJobTitle(\"Team Manager\");\n            getInstance().getUserList().put(TEAM_MANAGER_LOGIN,\n                    new TestUser(TestToolkitCtx.getInstance().getAdminUser().logIn(), creator));\n            TestToolkitCtx.getInstance().getAdminUser().logOut();\n        }\n\n        return getInstance().getUserList().get(TEAM_MANAGER_LOGIN);\n    }\n\n    public static Map<String, TestUser> getManagedUsers(int nbManagedUsers) {\n        for (int i = 0; i < nbManagedUsers; i++) {\n            String firstName = \"managed\";\n            String lastName = \"user\" + i;\n            UserCreator creator = new UserCreator(firstName + \".\" + lastName, \"bpm\")\n                    .setFirstName(firstName)\n                    .setLastName(lastName)\n                    .setManagerUserId(getTeamManagerUser().getId());\n            getInstance().getUserList().put(firstName + \".\" + lastName,\n                    new TestUser(TestToolkitCtx.getInstance().getAdminUser().logIn(), creator));\n            TestToolkitCtx.getInstance().getAdminUser().logOut();\n        }\n\n        return getInstance().getUserList();\n    }\n\n    public static TestUser getRidleyScott() {\n        if (getInstance().getUserList().get(RIDLEY_SCOTT_LOGIN) == null) {\n            final UserCreator userCreator = new UserCreator(RIDLEY_SCOTT_LOGIN, \"bpm\")\n                    .setFirstName(\"Ridley\")\n                    .setLastName(\"Scott\")\n                    .setJobTitle(\"Director\");\n\n            final ContactDataCreator personalContactData = new ContactDataCreator()\n                    .setAddress(\"address\")\n                    .setBuilding(\"building\")\n                    .setCity(\"city\")\n                    .setCountry(\"country\")\n                    .setEmail(\"ridley.scott@gmail.com\")\n                    .setFaxNumber(\"123456789\")\n                    .setMobileNumber(\"9876543214\")\n                    .setRoom(\"1408\")\n                    .setState(\"51\")\n                    .setWebsite(\"http://www.prometheus-movie.com\")\n                    .setZipCode(\"BT7 1GU\");\n\n            final ContactDataCreator professionalContactData = new ContactDataCreator()\n                    .setAddress(\"address pro\")\n                    .setBuilding(\"building pro\")\n                    .setCity(\"city pro\")\n                    .setCountry(\"country pro\")\n                    .setEmail(\"ridley.scott.pro@gmail.com\")\n                    .setFaxNumber(\"0123456789\")\n                    .setMobileNumber(\"98765432140\")\n                    .setRoom(\"1408-b\")\n                    .setState(\"42\")\n                    .setWebsite(\"http://www.imdb.com/title/tt0078748\")\n                    .setZipCode(\"38000\");\n\n            getInstance().getUserList().put(RIDLEY_SCOTT_LOGIN,\n                    new TestUser(TestToolkitCtx.getInstance().getAdminUser().logIn(), userCreator, personalContactData,\n                            professionalContactData));\n            TestToolkitCtx.getInstance().getAdminUser().logOut();\n        }\n\n        return getInstance().getUserList().get(RIDLEY_SCOTT_LOGIN);\n    }\n\n    public static TestUser getMrSpechar() {\n        if (getInstance().getUserList().get(MR_SPECHAR_LOGIN) == null) {\n            final UserCreator userBuilder = new UserCreator(MR_SPECHAR_LOGIN, \"Ã«ÃªÃ©~Ã\")\n                    .setFirstName(\"#*Ã©Ã Ã¢Ã¤Ã«ÃªÃ©~Ã§ÃžÅ¡Å’Ã˜Ã�Ã†\")\n                    .setLastName(\"Ã Ã¢Ã¤Ã«\")\n                    .setJobTitle(\"©Ã Ã¢Ã¤Ã«Ãª\");\n            getInstance().getUserList().put(MR_SPECHAR_LOGIN,\n                    new TestUser(TestToolkitCtx.getInstance().getAdminUser().logIn(), userBuilder));\n            TestToolkitCtx.getInstance().getAdminUser().logOut();\n        }\n\n        return getInstance().getUserList().get(MR_SPECHAR_LOGIN);\n    }\n\n    public void check() {\n        if (!getUserList().isEmpty()) {\n            throw new RuntimeException(\n                    this.getClass().getName() + \" cannot be reset because the list is not empty: \" + getUserList());\n        }\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/server/MockHttpServletRequest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.test.toolkit.server;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.UnsupportedEncodingException;\nimport java.security.Principal;\nimport java.util.Collection;\nimport java.util.Enumeration;\nimport java.util.HashMap;\nimport java.util.Locale;\nimport java.util.Map;\n\nimport javax.servlet.AsyncContext;\nimport javax.servlet.DispatcherType;\nimport javax.servlet.RequestDispatcher;\nimport javax.servlet.ServletContext;\nimport javax.servlet.ServletException;\nimport javax.servlet.ServletInputStream;\nimport javax.servlet.ServletRequest;\nimport javax.servlet.ServletResponse;\nimport javax.servlet.http.Cookie;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpSession;\nimport javax.servlet.http.HttpUpgradeHandler;\nimport javax.servlet.http.Part;\n\n/**\n * @author Nicolas Chabanoles\n */\npublic class MockHttpServletRequest implements HttpServletRequest {\n\n    HttpSession session = null;\n\n    Map<String, String[]> parametersMap;\n\n    Map<String, Object> attributesMap;\n\n    HttpServletRequest req = null;\n\n    String pathInfo = null;\n\n    public MockHttpServletRequest() {\n        parametersMap = new HashMap<>();\n        attributesMap = new HashMap<>();\n    }\n\n    @Override\n    public String getAuthType() {\n        if (this.req != null) {\n            return this.req.getAuthType();\n        }\n        return null;\n    }\n\n    @Override\n    public String getContextPath() {\n        if (this.req != null) {\n            return this.req.getContextPath();\n        }\n        return null;\n    }\n\n    @Override\n    public Cookie[] getCookies() {\n        if (this.req != null) {\n            return this.req.getCookies();\n        }\n        return null;\n    }\n\n    @Override\n    public long getDateHeader(final String anName) {\n        if (this.req != null) {\n            return this.req.getDateHeader(anName);\n        }\n        return 0;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletRequest#getHeader(java.lang.String)\n     */\n    @Override\n    public String getHeader(final String anName) {\n        if (this.req != null) {\n            return this.req.getHeader(anName);\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletRequest#getHeaderNames()\n     */\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public Enumeration getHeaderNames() {\n        if (this.req != null) {\n            return this.req.getHeaderNames();\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletRequest#getHeaders(java.lang.String)\n     */\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public Enumeration getHeaders(final String anName) {\n        if (this.req != null) {\n            return this.req.getHeaders(anName);\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletRequest#getIntHeader(java.lang.String)\n     */\n    @Override\n    public int getIntHeader(final String anName) {\n        if (this.req != null) {\n            return this.req.getIntHeader(anName);\n        }\n        return 0;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletRequest#getMethod()\n     */\n    @Override\n    public String getMethod() {\n        if (this.req != null) {\n            return this.req.getMethod();\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletRequest#getPathInfo()\n     */\n    @Override\n    public String getPathInfo() {\n        if (pathInfo != null) {\n            return this.pathInfo;\n        } else if (this.req != null) {\n            return this.req.getPathInfo();\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletRequest#getPathTranslated()\n     */\n    @Override\n    public String getPathTranslated() {\n        if (this.req != null) {\n            return this.req.getPathTranslated();\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletRequest#getQueryString()\n     */\n    @Override\n    public String getQueryString() {\n        if (this.req != null) {\n            return this.req.getQueryString();\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletRequest#getRemoteUser()\n     */\n    @Override\n    public String getRemoteUser() {\n        if (this.req != null) {\n            return this.req.getRemoteUser();\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletRequest#getRequestURI()\n     */\n    @Override\n    public String getRequestURI() {\n        if (this.req != null) {\n            return this.req.getRequestURI();\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletRequest#getRequestURL()\n     */\n    @Override\n    public StringBuffer getRequestURL() {\n        if (this.req != null) {\n            return this.req.getRequestURL();\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletRequest#getRequestedSessionId()\n     */\n    @Override\n    public String getRequestedSessionId() {\n        if (this.req != null) {\n            return this.req.getRequestedSessionId();\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletRequest#getServletPath()\n     */\n    @Override\n    public String getServletPath() {\n        if (this.req != null) {\n            return this.req.getRequestedSessionId();\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletRequest#getSession()\n     */\n    @Override\n    public HttpSession getSession() {\n        if (this.req != null) {\n            this.session = this.req.getSession();\n        }\n        if (this.session == null) {\n            this.session = new MockHttpSession();\n        }\n        return this.session;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletRequest#getSession(boolean)\n     */\n    @Override\n    public HttpSession getSession(final boolean anCreate) {\n\n        if (anCreate) {\n            if (this.req != null) {\n                this.session = this.req.getSession(anCreate);\n            } else {\n                this.session = new MockHttpSession();\n            }\n        }\n        return this.session;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletRequest#getUserPrincipal()\n     */\n    @Override\n    public Principal getUserPrincipal() {\n        if (this.req != null) {\n            return this.req.getUserPrincipal();\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletRequest#isRequestedSessionIdFromCookie()\n     */\n    @Override\n    public boolean isRequestedSessionIdFromCookie() {\n        if (this.req != null) {\n            return this.req.isRequestedSessionIdFromCookie();\n        }\n        return false;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletRequest#isRequestedSessionIdFromURL()\n     */\n    @Override\n    public boolean isRequestedSessionIdFromURL() {\n        if (this.req != null) {\n            return this.req.isRequestedSessionIdFromURL();\n        }\n        return false;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletRequest#isRequestedSessionIdFromUrl()\n     */\n    @Override\n    @Deprecated\n    public boolean isRequestedSessionIdFromUrl() {\n        if (this.req != null) {\n            return this.req.isRequestedSessionIdFromUrl();\n        }\n        return false;\n    }\n\n    @Override\n    public boolean authenticate(HttpServletResponse response) throws IOException, ServletException {\n        return false;\n    }\n\n    @Override\n    public void login(String username, String password) throws ServletException {\n\n    }\n\n    @Override\n    public void logout() throws ServletException {\n\n    }\n\n    @Override\n    public Collection<Part> getParts() throws IOException, ServletException {\n        return null;\n    }\n\n    @Override\n    public Part getPart(String name) throws IOException, ServletException {\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletRequest#isRequestedSessionIdValid()\n     */\n    @Override\n    public boolean isRequestedSessionIdValid() {\n        if (this.req != null) {\n            return this.req.isRequestedSessionIdValid();\n        }\n        return false;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletRequest#isUserInRole(java.lang.String)\n     */\n    @Override\n    public boolean isUserInRole(final String anRole) {\n        if (this.req != null) {\n            return this.req.isUserInRole(anRole);\n        }\n        return false;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletRequest#getAttribute(java.lang.String)\n     */\n    @Override\n    public Object getAttribute(final String aName) {\n        if (this.req != null) {\n            return this.req.getAttribute(aName);\n        } else {\n            return attributesMap.get(aName);\n        }\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletRequest#getAttributeNames()\n     */\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public Enumeration getAttributeNames() {\n        if (this.req != null) {\n            return this.req.getAttributeNames();\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletRequest#getCharacterEncoding()\n     */\n    @Override\n    public String getCharacterEncoding() {\n        if (this.req != null) {\n            return this.req.getCharacterEncoding();\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletRequest#getContentLength()\n     */\n    @Override\n    public int getContentLength() {\n        if (this.req != null) {\n            return this.req.getContentLength();\n        }\n        return 0;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletRequest#getContentType()\n     */\n    @Override\n    public String getContentType() {\n        if (this.req != null) {\n            return this.req.getContentType();\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletRequest#getInputStream()\n     */\n    @Override\n    public ServletInputStream getInputStream() throws IOException {\n        if (this.req != null) {\n            return this.req.getInputStream();\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletRequest#getLocalAddr()\n     */\n    @Override\n    public String getLocalAddr() {\n        if (this.req != null) {\n            return this.req.getLocalAddr();\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletRequest#getLocalName()\n     */\n    @Override\n    public String getLocalName() {\n        if (this.req != null) {\n            return this.req.getLocalName();\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletRequest#getLocalPort()\n     */\n    @Override\n    public int getLocalPort() {\n        if (this.req != null) {\n            return this.req.getLocalPort();\n        }\n        return 0;\n    }\n\n    @Override\n    public ServletContext getServletContext() {\n        return null;\n    }\n\n    @Override\n    public AsyncContext startAsync() throws IllegalStateException {\n        return null;\n    }\n\n    @Override\n    public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse)\n            throws IllegalStateException {\n        return null;\n    }\n\n    @Override\n    public boolean isAsyncStarted() {\n        return false;\n    }\n\n    @Override\n    public boolean isAsyncSupported() {\n        return false;\n    }\n\n    @Override\n    public AsyncContext getAsyncContext() {\n        return null;\n    }\n\n    @Override\n    public DispatcherType getDispatcherType() {\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletRequest#getLocale()\n     */\n    @Override\n    public Locale getLocale() {\n        if (this.req != null) {\n            return this.req.getLocale();\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletRequest#getLocales()\n     */\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public Enumeration getLocales() {\n        if (this.req != null) {\n            return this.req.getLocales();\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletRequest#getParameter(java.lang.String)\n     */\n    @Override\n    public String getParameter(final String aName) {\n        final String[] strings = this.parametersMap.get(aName);\n        if (strings != null && strings.length > 0) {\n            return strings[0];\n        } else {\n            return null;\n        }\n    }\n\n    public void setParameter(final String anName, final String value) {\n        this.parametersMap.put(anName, new String[] { value });\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletRequest#getParameterMap()\n     */\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public Map<String, String[]> getParameterMap() {\n        if (req == null) {\n            return this.parametersMap;\n        } else {\n            return this.req.getParameterMap();\n        }\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletRequest#getParameterNames()\n     */\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public Enumeration getParameterNames() {\n        if (this.req != null) {\n            return this.req.getParameterNames();\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletRequest#getParameterValues(java.lang.String)\n     */\n    @Override\n    public String[] getParameterValues(final String anName) {\n        if (this.req != null) {\n            return this.req.getParameterValues(anName);\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletRequest#getProtocol()\n     */\n    @Override\n    public String getProtocol() {\n        if (this.req != null) {\n            return this.req.getProtocol();\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletRequest#getReader()\n     */\n    @Override\n    public BufferedReader getReader() throws IOException {\n        if (this.req != null) {\n            return this.req.getReader();\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletRequest#getRealPath(java.lang.String)\n     */\n    @Override\n    @Deprecated\n    public String getRealPath(final String anPath) {\n        if (this.req != null) {\n            return this.req.getRealPath(anPath);\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletRequest#getRemoteAddr()\n     */\n    @Override\n    public String getRemoteAddr() {\n        if (this.req != null) {\n            return this.req.getRemoteAddr();\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletRequest#getRemoteHost()\n     */\n    @Override\n    public String getRemoteHost() {\n        if (this.req != null) {\n            return this.req.getRemoteHost();\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletRequest#getRemotePort()\n     */\n    @Override\n    public int getRemotePort() {\n        if (this.req != null) {\n            return this.req.getRemotePort();\n        }\n        return 0;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletRequest#getRequestDispatcher(java.lang.String)\n     */\n    @Override\n    public RequestDispatcher getRequestDispatcher(final String anPath) {\n        if (this.req != null) {\n            return this.req.getRequestDispatcher(anPath);\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletRequest#getScheme()\n     */\n    @Override\n    public String getScheme() {\n        if (this.req != null) {\n            return this.req.getScheme();\n        }\n        return \"http\";\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletRequest#getServerName()\n     */\n    @Override\n    public String getServerName() {\n        if (this.req != null) {\n            return this.req.getServerName();\n        }\n        return \"localhost\";\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletRequest#getServerPort()\n     */\n    @Override\n    public int getServerPort() {\n        if (this.req != null) {\n            return this.req.getServerPort();\n        }\n        return 8080;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletRequest#isSecure()\n     */\n    @Override\n    public boolean isSecure() {\n        if (this.req != null) {\n            return this.req.isSecure();\n        }\n        return false;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletRequest#removeAttribute(java.lang.String)\n     */\n    @Override\n    public void removeAttribute(final String anName) {\n        if (this.req != null) {\n            this.req.removeAttribute(anName);\n        }\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletRequest#setAttribute(java.lang.String, java.lang.Object)\n     */\n    @Override\n    public void setAttribute(final String aName, final Object aValue) {\n        if (this.req != null) {\n            this.req.setAttribute(aName, aValue);\n        } else {\n            this.attributesMap.put(aName, aValue);\n        }\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletRequest#setCharacterEncoding(java.lang.String)\n     */\n    @Override\n    public void setCharacterEncoding(final String anEnv) throws UnsupportedEncodingException {\n        if (this.req != null) {\n            this.req.setCharacterEncoding(anEnv);\n        }\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletRequest#setCharacterEncoding(java.lang.String)\n     */\n    public void setPathInfo(final String pathInfo) {\n        this.pathInfo = pathInfo;\n    }\n\n    @Override\n    public long getContentLengthLong() {\n        if (this.req != null) {\n            return this.req.getContentLengthLong();\n        }\n        return 0;\n    }\n\n    @Override\n    public String changeSessionId() {\n        if (this.req != null) {\n            return this.req.changeSessionId();\n        }\n        return null;\n    }\n\n    @Override\n    public <T extends HttpUpgradeHandler> T upgrade(Class<T> handlerClass) throws IOException, ServletException {\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/server/MockHttpServletResponse.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.test.toolkit.server;\n\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.util.Collection;\nimport java.util.Locale;\n\nimport javax.servlet.ServletOutputStream;\nimport javax.servlet.http.Cookie;\nimport javax.servlet.http.HttpServletResponse;\n\n/**\n * @author Rohart Bastien\n */\npublic class MockHttpServletResponse implements HttpServletResponse {\n\n    HttpServletResponse res = null;\n\n    public MockHttpServletResponse() {\n    }\n\n    @Override\n    public String getCharacterEncoding() {\n        if (res != null) {\n            return res.getCharacterEncoding();\n        }\n        return null;\n    }\n\n    @Override\n    public String getContentType() {\n        if (res != null) {\n            return res.getContentType();\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletResponse#getOutputStream()\n     */\n    @Override\n    public ServletOutputStream getOutputStream() throws IOException {\n        if (res != null) {\n            return res.getOutputStream();\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletResponse#getWriter()\n     */\n    @Override\n    public PrintWriter getWriter() throws IOException {\n        if (res != null) {\n            return res.getWriter();\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletResponse#setCharacterEncoding(java.lang.String)\n     */\n    @Override\n    public void setCharacterEncoding(String charset) {\n        res.setCharacterEncoding(charset);\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletResponse#setContentLength(int)\n     */\n    @Override\n    public void setContentLength(int len) {\n        res.setContentLength(len);\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletResponse#setContentType(java.lang.String)\n     */\n    @Override\n    public void setContentType(String type) {\n        res.setContentType(type);\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletResponse#setBufferSize(int)\n     */\n    @Override\n    public void setBufferSize(int size) {\n        res.setBufferSize(size);\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletResponse#getBufferSize()\n     */\n    @Override\n    public int getBufferSize() {\n        if (res != null) {\n            return res.getBufferSize();\n        }\n        return 0;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletResponse#flushBuffer()\n     */\n    @Override\n    public void flushBuffer() throws IOException {\n        res.flushBuffer();\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletResponse#resetBuffer()\n     */\n    @Override\n    public void resetBuffer() {\n        res.resetBuffer();\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletResponse#isCommitted()\n     */\n    @Override\n    public boolean isCommitted() {\n        if (res.isCommitted()) {\n            return true;\n        }\n        return false;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletResponse#reset()\n     */\n    @Override\n    public void reset() {\n        res.reset();\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletResponse#setLocale(java.util.Locale)\n     */\n    @Override\n    public void setLocale(Locale loc) {\n        res.setLocale(loc);\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.ServletResponse#getLocale()\n     */\n    @Override\n    public Locale getLocale() {\n        if (res != null) {\n            return res.getLocale();\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletResponse#addCookie(javax.servlet.http.Cookie)\n     */\n    @Override\n    public void addCookie(Cookie cookie) {\n        if (cookie != null) {\n            res.addCookie(cookie);\n        }\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletResponse#containsHeader(java.lang.String)\n     */\n    @Override\n    public boolean containsHeader(String name) {\n        if (res.containsHeader(name)) {\n            return true;\n        }\n        return false;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletResponse#encodeURL(java.lang.String)\n     */\n    @Override\n    public String encodeURL(String url) {\n        if (res != null) {\n            return res.encodeURL(url);\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletResponse#encodeRedirectURL(java.lang.String)\n     */\n    @Override\n    public String encodeRedirectURL(String url) {\n        if (res != null) {\n            return res.encodeRedirectURL(url);\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletResponse#encodeUrl(java.lang.String)\n     */\n    @Override\n    public String encodeUrl(String url) {\n        if (res != null) {\n            return res.encodeUrl(url);\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletResponse#encodeRedirectUrl(java.lang.String)\n     */\n    @Override\n    public String encodeRedirectUrl(String url) {\n        if (res != null) {\n            return res.encodeRedirectUrl(url);\n        }\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletResponse#sendError(int, java.lang.String)\n     */\n    @Override\n    public void sendError(int sc, String msg) throws IOException {\n        res.sendError(sc, msg);\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletResponse#sendError(int)\n     */\n    @Override\n    public void sendError(int sc) throws IOException {\n        res.sendError(sc);\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletResponse#sendRedirect(java.lang.String)\n     */\n    @Override\n    public void sendRedirect(String location) throws IOException {\n        res.sendRedirect(location);\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletResponse#setDateHeader(java.lang.String, long)\n     */\n    @Override\n    public void setDateHeader(String name, long date) {\n        res.setDateHeader(name, date);\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletResponse#addDateHeader(java.lang.String, long)\n     */\n    @Override\n    public void addDateHeader(String name, long date) {\n        res.addDateHeader(name, date);\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletResponse#setHeader(java.lang.String, java.lang.String)\n     */\n    @Override\n    public void setHeader(String name, String value) {\n        res.setHeader(name, value);\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletResponse#addHeader(java.lang.String, java.lang.String)\n     */\n    @Override\n    public void addHeader(String name, String value) {\n        if (res != null) {\n            res.addHeader(name, value);\n        }\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletResponse#setIntHeader(java.lang.String, int)\n     */\n    @Override\n    public void setIntHeader(String name, int value) {\n        res.setIntHeader(name, value);\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletResponse#addIntHeader(java.lang.String, int)\n     */\n    @Override\n    public void addIntHeader(String name, int value) {\n        res.addIntHeader(name, value);\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletResponse#setStatus(int)\n     */\n    @Override\n    public void setStatus(int sc) {\n        res.setStatus(sc);\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpServletResponse#setStatus(int, java.lang.String)\n     */\n    @Override\n    public void setStatus(int sc, String sm) {\n        res.setStatus(sc, sm);\n    }\n\n    @Override\n    public int getStatus() {\n        if (res != null) {\n            return res.getStatus();\n        }\n        return 0;\n    }\n\n    @Override\n    public String getHeader(String name) {\n        if (res != null) {\n            return res.getHeader(name);\n        }\n        return null;\n    }\n\n    @Override\n    public Collection<String> getHeaders(String name) {\n        if (res != null) {\n            return res.getHeaders(name);\n        }\n        return null;\n    }\n\n    @Override\n    public Collection<String> getHeaderNames() {\n        if (res != null) {\n            return res.getHeaderNames();\n        }\n        return null;\n    }\n\n    @Override\n    public void setContentLengthLong(long len) {\n        if (res != null) {\n            res.setContentLengthLong(len);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/server/MockHttpSession.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.test.toolkit.server;\n\nimport java.util.Date;\nimport java.util.Enumeration;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport javax.servlet.ServletContext;\nimport javax.servlet.http.HttpSession;\nimport javax.servlet.http.HttpSessionContext;\n\n/**\n * @author Ruiheng Fan\n */\npublic class MockHttpSession implements HttpSession {\n\n    Map<String, Object> attributesMap = new HashMap<>();\n\n    String id = new Date().getTime() + \"\";\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpSession#getCreationTime()\n     */\n    @Override\n    public long getCreationTime() {\n        // TODO Auto-generated method stub\n        return 0;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpSession#getId()\n     */\n    @Override\n    public String getId() {\n        return this.id;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpSession#getLastAccessedTime()\n     */\n    @Override\n    public long getLastAccessedTime() {\n        // TODO Auto-generated method stub\n        return 0;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpSession#getServletContext()\n     */\n    @Override\n    public ServletContext getServletContext() {\n        // TODO Auto-generated method stub\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpSession#setMaxInactiveInterval(int)\n     */\n    @Override\n    public void setMaxInactiveInterval(final int interval) {\n        // TODO Auto-generated method stub\n\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpSession#getMaxInactiveInterval()\n     */\n    @Override\n    public int getMaxInactiveInterval() {\n        // TODO Auto-generated method stub\n        return 0;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpSession#getSessionContext()\n     */\n    @Override\n    public HttpSessionContext getSessionContext() {\n        // TODO Auto-generated method stub\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpSession#getAttribute(java.lang.String)\n     */\n    @Override\n    public Object getAttribute(final String name) {\n        return this.attributesMap.get(name);\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpSession#getValue(java.lang.String)\n     */\n    @Override\n    public Object getValue(final String name) {\n        // TODO Auto-generated method stub\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpSession#getAttributeNames()\n     */\n    @Override\n    public Enumeration getAttributeNames() {\n        // TODO Auto-generated method stub\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpSession#getValueNames()\n     */\n    @Override\n    public String[] getValueNames() {\n        // TODO Auto-generated method stub\n        return null;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpSession#setAttribute(java.lang.String, java.lang.Object)\n     */\n    @Override\n    public void setAttribute(final String name, final Object value) {\n        this.attributesMap.put(name, value);\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpSession#putValue(java.lang.String, java.lang.Object)\n     */\n    @Override\n    public void putValue(final String name, final Object value) {\n        // TODO Auto-generated method stub\n\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpSession#removeAttribute(java.lang.String)\n     */\n    @Override\n    public void removeAttribute(final String name) {\n        // TODO Auto-generated method stub\n\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpSession#removeValue(java.lang.String)\n     */\n    @Override\n    public void removeValue(final String name) {\n        // TODO Auto-generated method stub\n\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpSession#invalidate()\n     */\n    @Override\n    public void invalidate() {\n        // TODO Auto-generated method stub\n\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.servlet.http.HttpSession#isNew()\n     */\n    @Override\n    public boolean isNew() {\n        // TODO Auto-generated method stub\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/web/rest/server/api/page/builder/PageItemBuilder.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.page.builder;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.net.URISyntaxException;\nimport java.net.URL;\nimport java.util.Date;\n\nimport org.bonitasoft.engine.api.PlatformAPIAccessor;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.io.FileContent;\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.web.rest.model.portal.page.PageItem;\nimport org.bonitasoft.web.rest.server.datastore.page.PageDatastore;\n\n/**\n * @author Fabio Lombardi\n */\npublic class PageItemBuilder {\n\n    protected long id = 1L;\n\n    protected String urlToken = \"custompage_aName\";\n\n    protected String displayName = \"aDisplayName\";\n\n    protected String description = \"aDescription\";\n\n    protected boolean isProvided = false;\n\n    protected Date creation_date = new Date(1);\n\n    protected long createdBy = 1L;\n\n    protected Date last_update_date = new Date(1);\n\n    protected String contentName = \"page.zip\";\n\n    protected long updatedBy = 1L;\n\n    public static PageItemBuilder aPageItem() {\n        return new PageItemBuilder();\n    }\n\n    public PageItem build() throws IOException, URISyntaxException, ServerAPIException, BonitaHomeNotSetException,\n            UnknownAPITypeException {\n        final PageItem item = new PageItem();\n        item.setId(id);\n        item.setUrlToken(urlToken);\n        item.setDisplayName(displayName);\n        item.setDescription(description);\n        item.setIsProvided(isProvided);\n        item.setCreationDate(creation_date);\n        item.setCreatedByUserId(createdBy);\n        item.setLastUpdateDate(last_update_date);\n        item.setUpdatedByUserId(updatedBy);\n        item.setContentName(contentName);\n\n        String zipFileName = \"/page.zip\";\n        final URL zipFileUrl = getClass().getResource(zipFileName);\n        final File zipFile = new File(zipFileUrl.toURI());\n        //store page zip into database\n        String pageZipKey = PlatformAPIAccessor.getTemporaryContentAPI()\n                .storeTempFile(new FileContent(zipFile.getName(), new FileInputStream(zipFile), \"application/zip\"));\n\n        item.setAttribute(PageDatastore.UNMAPPED_ATTRIBUTE_ZIP_FILE, pageZipKey);\n        return item;\n    }\n\n    public PageItemBuilder fromEngineItem(final Page page) {\n        id = page.getId();\n        urlToken = page.getName();\n        displayName = page.getDisplayName();\n        description = page.getDescription();\n        isProvided = page.isProvided();\n        creation_date = page.getInstallationDate();\n        createdBy = page.getInstalledBy();\n        last_update_date = page.getLastModificationDate();\n        updatedBy = page.getLastUpdatedBy();\n        contentName = page.getContentName();\n\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/web/test/AbstractConsoleTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.test;\n\nimport static junit.framework.Assert.assertTrue;\n\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\nimport org.bonitasoft.test.toolkit.organization.TestUserFactory;\nimport org.bonitasoft.test.toolkit.server.MockHttpServletRequest;\nimport org.bonitasoft.test.toolkit.server.MockHttpServletResponse;\nimport org.bonitasoft.web.rest.server.BonitaRestAPIServlet;\nimport org.bonitasoft.web.rest.server.datastore.bpm.flownode.FlowNodeConverter;\nimport org.bonitasoft.web.rest.server.framework.APIServletCall;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\n\n/**\n * @author Vincent Elcrin\n */\npublic abstract class AbstractConsoleTest extends AbstractJUnitWebTest {\n\n    /*\n     * (non-Javadoc)\n     * @see org.bonitasoft.console.common.server.AbstractJUnitWebTest#webTestSetUp()\n     */\n    @Override\n    public void webTestSetUp() throws Exception {\n        FlowNodeConverter.setFlowNodeConverter(new FlowNodeConverter());\n\n        new BonitaRestAPIServlet();\n        consoleTestSetUp();\n    }\n\n    @Override\n    protected TestUser getInitiator() {\n        return TestUserFactory.getJohnCarpenter();\n    }\n\n    public APIServletCall getAPICaller(final APISession apiSession, final String apiPath) {\n\n        // Get the httpSession and set attributes\n        final MockHttpServletRequest mockHttpServletRequest = new MockHttpServletRequest();\n        mockHttpServletRequest.setPathInfo(apiPath);\n        final MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse();\n        final HttpSession httpSession = mockHttpServletRequest.getSession();\n        httpSession.setAttribute(USERNAME_SESSION_PARAM, apiSession.getUserName());\n        httpSession.setAttribute(API_SESSION_PARAM_KEY, apiSession);\n\n        // Initialize APIUser for HTTP requests of the API\n        return new APIServletCall(mockHttpServletRequest, mockHttpServletResponse);\n    }\n\n    public abstract void consoleTestSetUp() throws Exception;\n\n    protected void assertItemEquals(Item expectedItem, Item actual) {\n        assertTrue(\"expected { \" + expectedItem + \"} \\n actual {\" + actual + \"}\", areEquals(expectedItem, actual));\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/web/test/AbstractJUnitWebTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.test;\n\nimport org.bonitasoft.console.common.server.i18n.I18n;\nimport org.bonitasoft.console.common.server.utils.PlatformManagementUtils;\nimport org.bonitasoft.test.toolkit.AbstractJUnitTest;\nimport org.bonitasoft.test.toolkit.organization.TestToolkitCtx;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\n\n/**\n * @author Vincent Elcrin\n */\npublic abstract class AbstractJUnitWebTest extends AbstractJUnitTest {\n\n    /**\n     * the request param for the username\n     */\n    public static final String USERNAME_SESSION_PARAM = \"username\";\n\n    public static final String API_SESSION_PARAM_KEY = \"apiSession\";\n\n    @Override\n    protected void testSetUp() throws Exception {\n        // toolkit initialization\n        I18n.getInstance();\n        new PlatformManagementUtils().initializePlatformConfiguration();\n        webTestSetUp();\n    }\n\n    @Override\n    protected TestToolkitCtx getContext() {\n        return TestToolkitCtx.getInstance();\n    }\n\n    @Override\n    protected void testTearDown() {\n        webTestTearDown();\n    }\n\n    public abstract void webTestSetUp() throws Exception;\n\n    protected void webTestTearDown() {\n    }\n\n    protected boolean areEquals(Item item1, Item item2) {\n        return item1.getAttributes().equals(item2.getAttributes());\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/resources/logback.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<configuration>\n\n  <timestamp key=\"bySecond\" datePattern=\"yyyyMMdd'T'HHmmss\"/>\n\n  <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n    <encoder>\n      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level \\(%file:%line\\) %method - %msg%n</pattern>\n    </encoder>\n  </appender>\n\n  <root level=\"INFO\">\n    <appender-ref ref=\"STDOUT\" />\n  </root>\n\n</configuration>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/resources/org/bonitasoft/test/toolkit/connector/TestConnector.impl",
    "content": "<connectorImplementation>\n\t\n\t<definitionId>org.bonitasoft.test.toolkit.connector.TestConnector</definitionId>\n\t<definitionVersion>1.0</definitionVersion>\n\t<implementationClassname>org.bonitasoft.test.toolkit.connector.TestConnector</implementationClassname>\n\t<implementationId>org.bonitasoft.test.toolkit.connector.testConnector</implementationId>\n\t<implementationVersion>1.0</implementationVersion>\n\t\n\t<jarDependencies>\n\t\t<jarDependency>aDependency.jar</jarDependency>\n\t\t<jarDependency>anOtherDependency.jar</jarDependency>\n\t</jarDependencies>\n\t\n</connectorImplementation>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/main/resources/org/bonitasoft/test/toolkit/connector/TestConnector.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.test.toolkit.connector;\n\nimport org.bonitasoft.engine.connector.AbstractConnector;\nimport org.bonitasoft.engine.connector.ConnectorException;\nimport org.bonitasoft.engine.connector.ConnectorValidationException;\n\n/**\n * @author Colin PUY\n */\npublic class TestConnector extends AbstractConnector {\n\n    public void validateInputParameters() throws ConnectorValidationException {\n        // Do Nothing\n    }\n\n    @Override\n    protected void executeBusinessLogic() throws ConnectorException {\n        // Do Nothing\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/console/common/server/auth/impl/standard/StandardAuthenticationManagerImplIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.auth.impl.standard;\n\nimport static org.junit.Assert.fail;\n\nimport org.bonitasoft.console.common.server.auth.AuthenticationFailedException;\nimport org.bonitasoft.console.common.server.login.HttpServletRequestAccessor;\nimport org.bonitasoft.console.common.server.login.credentials.StandardCredentials;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\nimport org.bonitasoft.test.toolkit.organization.TestUserFactory;\nimport org.bonitasoft.test.toolkit.server.MockHttpServletRequest;\nimport org.bonitasoft.web.test.AbstractJUnitWebTest;\nimport org.junit.Test;\n\n/**\n * @author Rohart Bastien\n */\npublic class StandardAuthenticationManagerImplIT extends AbstractJUnitWebTest {\n\n    private static final String TECHNICAL_USER_USERNAME = \"install\";\n\n    private static final String TECHNICAL_USER_PASSWORD = \"install\";\n\n    @Override\n    public void webTestSetUp() {\n    }\n\n    @Override\n    protected TestUser getInitiator() {\n        return TestUserFactory.getJohnCarpenter();\n    }\n\n    @Test\n    public void testLogin() {\n        final HttpServletRequestAccessor request = new HttpServletRequestAccessor(new MockHttpServletRequest());\n        try {\n            new StandardAuthenticationManagerImpl()\n                    .authenticate(request,\n                            new StandardCredentials(TECHNICAL_USER_USERNAME, TECHNICAL_USER_PASSWORD));\n        } catch (final AuthenticationFailedException e) {\n            fail(\"Cannot login \" + e);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/console/common/server/filter/PathTraversalProtectionIT.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.filter;\n\nimport static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.HttpURLConnection;\nimport java.net.URL;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.Comparator;\nimport java.util.stream.Stream;\n\nimport javax.servlet.Filter;\nimport javax.servlet.FilterChain;\nimport javax.servlet.ServletException;\nimport javax.servlet.ServletRequest;\nimport javax.servlet.ServletResponse;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.apache.catalina.Context;\nimport org.apache.catalina.startup.Tomcat;\nimport org.apache.tomcat.util.descriptor.web.FilterDef;\nimport org.apache.tomcat.util.descriptor.web.FilterMap;\nimport org.bonitasoft.console.common.server.login.filter.AuthenticationFilter;\nimport org.bonitasoft.console.common.server.login.filter.RestAPIAuthorizationFilter;\nimport org.bonitasoft.console.common.server.login.filter.TokenValidatorFilter;\nimport org.junit.AfterClass;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\n\n/**\n * Integration test verifying that path traversal attacks using semicolons (..;)\n * are blocked when running in an embedded Tomcat container (the production container).\n * <p>\n * Each filter stub extends the real filter class, inheriting the actual\n * {@code doFilter()} &rarr; {@code matchExcludePatterns()} &rarr;\n * {@code URLExcludePattern} + {@code PathSanitizer} code path.\n * Only {@code proceedWithFiltering()} is overridden to short-circuit engine dependencies.\n * <p>\n * This tests the CVE-233 fix end-to-end:\n * <ul>\n * <li>Tomcat's URL parsing and semicolon handling</li>\n * <li>Filter chain execution with REQUEST and FORWARD dispatchers</li>\n * <li>PathSanitizer-based defense in URLExcludePattern</li>\n * </ul>\n */\npublic class PathTraversalProtectionIT {\n\n    private static final int HTTP_TIMEOUT_MS = 5000;\n\n    private static Tomcat tomcat;\n    private static File baseDir;\n    private static int port;\n\n    @BeforeClass\n    public static void startTomcat() throws Exception {\n        tomcat = new Tomcat();\n        tomcat.setPort(0);\n\n        baseDir = new File(System.getProperty(\"java.io.tmpdir\"), \"tomcat-it-\" + System.nanoTime());\n        tomcat.setBaseDir(baseDir.getAbsolutePath());\n\n        Context context = tomcat.addContext(\"/bonita\", null);\n\n        // Register filters in the same order as web.xml\n        addFilter(context, \"TokenValidatorFilter\", StubTokenValidatorFilter.class,\n                \"/apps/*\", \"REQUEST\", \"FORWARD\");\n        addFilter(context, \"AuthenticationFilter\", StubAuthenticationFilter.class,\n                \"/apps/*\", \"REQUEST\", \"FORWARD\");\n        addFilter(context, \"RestAPIAuthorizationFilter\", StubRestAPIAuthorizationFilter.class,\n                \"/API/*\", \"REQUEST\", \"FORWARD\");\n\n        // Register servlets\n        Tomcat.addServlet(context, \"apps\", new StubOkServlet());\n        context.addServletMappingDecoded(\"/apps/*\", \"apps\");\n\n        Tomcat.addServlet(context, \"serverAPI\", new StubForbiddenServlet());\n        context.addServletMappingDecoded(\"/serverAPI/*\", \"serverAPI\");\n\n        tomcat.start();\n        port = tomcat.getConnector().getLocalPort();\n    }\n\n    @AfterClass\n    public static void stopTomcat() throws Exception {\n        if (tomcat != null) {\n            tomcat.stop();\n            tomcat.destroy();\n        }\n        if (baseDir != null && baseDir.exists()) {\n            try (Stream<Path> paths = Files.walk(baseDir.toPath())) {\n                paths.sorted(Comparator.reverseOrder())\n                        .map(Path::toFile)\n                        .forEach(File::delete);\n            }\n        }\n    }\n\n    /**\n     * Semicolon traversal that stays within the /apps/* context after Tomcat normalization.\n     * Tomcat strips path parameters and normalizes '..' segments, routing the request to\n     * /apps/serverAPI/test. The filter chain runs and blocks the request (401).\n     */\n    @Test\n    public void semicolonTraversalStayingInContextShouldBeBlocked() throws Exception {\n        // /apps/A/B/../../serverAPI/test normalizes to /apps/serverAPI/test (within /apps/*)\n        int status = get(\"/bonita/apps/A/B/..;/..;/serverAPI/test\");\n\n        assertThat(status).as(\"Semicolon traversal staying within /apps/* context must be blocked\")\n                .isNotEqualTo(200);\n    }\n\n    /**\n     * Percent-encoded semicolons (%3b) are NOT decoded by Tomcat during routing, so\n     * the filter receives the raw URL. URLExcludePattern URL-decodes then strips path\n     * parameters via PathSanitizer, correctly detecting the traversal.\n     */\n    @Test\n    public void percentEncodedSemicolonTraversalShouldBeBlocked() throws Exception {\n        int status = get(\"/bonita/apps/..%3b/..%3b/serverAPI/test\");\n\n        assertThat(status).as(\"Percent-encoded semicolon traversal must not succeed\")\n                .isNotEqualTo(200);\n    }\n\n    /**\n     * Deep traversal that exploits the filter exclude pattern. The URL is crafted so\n     * that the raw path matches an exclude pattern (e.g. apps/.+/API/system/session),\n     * but after PathSanitizer strips semicolons and normalizes '..', the resolved path\n     * no longer matches the exclude pattern.\n     */\n    @Test\n    public void deepTraversalViaExcludePatternShouldBeBlocked() throws Exception {\n        int status = get(\"/bonita/apps/FAKE/API/system/session/..;/..;/..;/..;/serverAPI/x\");\n\n        assertThat(status).as(\"Deep traversal exploiting exclude pattern must not succeed\")\n                .isNotEqualTo(200);\n    }\n\n    @Test\n    public void normalAppRequestShouldRequireAuthentication() throws Exception {\n        int status = get(\"/bonita/apps/myapp\");\n\n        assertThat(status).as(\"Normal app request should be blocked by auth filter\")\n                .isEqualTo(SC_UNAUTHORIZED);\n    }\n\n    @Test\n    public void urlWithEncodedSpacesShouldPassThrough() throws Exception {\n        int status = get(\"/bonita/apps/My%20App/API/system/i18ntranslation\");\n\n        assertThat(status).as(\"URL with encoded spaces in an excluded path should reach the servlet\")\n                .isEqualTo(200);\n    }\n\n    @Test\n    public void legitimateExcludedUrlShouldPassThrough() throws Exception {\n        int status = get(\"/bonita/apps/myapp/API/system/i18ntranslation\");\n\n        assertThat(status).as(\"Legitimate excluded URL should reach the servlet\")\n                .isEqualTo(200);\n    }\n\n    // --- Helper methods ---\n\n    private static int get(String path) throws IOException {\n        URL url = new URL(\"http://localhost:\" + port + path);\n        HttpURLConnection conn = (HttpURLConnection) url.openConnection();\n        conn.setRequestMethod(\"GET\");\n        conn.setInstanceFollowRedirects(false);\n        conn.setConnectTimeout(HTTP_TIMEOUT_MS);\n        conn.setReadTimeout(HTTP_TIMEOUT_MS);\n        try {\n            return conn.getResponseCode();\n        } finally {\n            conn.disconnect();\n        }\n    }\n\n    private static void addFilter(Context ctx, String name,\n            Class<? extends Filter> clazz, String urlPattern,\n            String... dispatchers) {\n        FilterDef filterDef = new FilterDef();\n        filterDef.setFilterName(name);\n        filterDef.setFilterClass(clazz.getName());\n        ctx.addFilterDef(filterDef);\n\n        FilterMap filterMap = new FilterMap();\n        filterMap.setFilterName(name);\n        filterMap.addURLPattern(urlPattern);\n        for (String d : dispatchers) {\n            filterMap.setDispatcher(d);\n        }\n        ctx.addFilterMap(filterMap);\n    }\n\n    // --- Stub filters (public static for Tomcat reflection) ---\n\n    public static class StubTokenValidatorFilter extends TokenValidatorFilter {\n\n        @Override\n        public void proceedWithFiltering(ServletRequest request, ServletResponse response,\n                FilterChain chain) throws ServletException, IOException {\n            ((HttpServletResponse) response).setStatus(SC_UNAUTHORIZED);\n        }\n    }\n\n    public static class StubAuthenticationFilter extends AuthenticationFilter {\n\n        @Override\n        public void proceedWithFiltering(ServletRequest request, ServletResponse response,\n                FilterChain chain) throws ServletException, IOException {\n            ((HttpServletResponse) response).setStatus(SC_UNAUTHORIZED);\n        }\n    }\n\n    public static class StubRestAPIAuthorizationFilter extends RestAPIAuthorizationFilter {\n\n        @Override\n        public void proceedWithFiltering(ServletRequest request, ServletResponse response,\n                FilterChain chain) throws ServletException {\n            ((HttpServletResponse) response).setStatus(SC_UNAUTHORIZED);\n        }\n    }\n\n    // --- Stub servlets ---\n\n    public static class StubOkServlet extends HttpServlet {\n\n        @Override\n        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {\n            resp.setStatus(200);\n            resp.getWriter().write(\"OK\");\n        }\n    }\n\n    public static class StubForbiddenServlet extends HttpServlet {\n\n        @Override\n        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {\n            resp.setStatus(HttpServletResponse.SC_FORBIDDEN);\n            resp.getWriter().write(\"FORBIDDEN\");\n        }\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/WaitUntil.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server;\n\nimport static org.junit.Assert.assertTrue;\n\nimport java.util.Date;\n\n/**\n * @author Baptiste Mesta\n */\npublic abstract class WaitUntil {\n\n    private final int timeout;\n\n    private final int repeatEach;\n\n    private final boolean throwExceptions;\n\n    /**\n     * @param repeatEach\n     *        time to wait for before retrying, if condition not fullfilled, in milliseconds\n     * @param timeout\n     *        max time to wait for, in milliseconds\n     */\n    public WaitUntil(final int repeatEach, final int timeout) {\n        this(repeatEach, timeout, true);\n    }\n\n    /**\n     * @param repeatEach\n     *        time to wait for before retrying, if condition not fullfilled, in milliseconds\n     * @param timeout\n     *        max time to wait for, in milliseconds\n     * @param throwExceptions\n     *        can the check condition throw exceptions?\n     */\n    public WaitUntil(final int repeatEach, final int timeout, final boolean throwExceptions) {\n        this.throwExceptions = throwExceptions;\n        assertTrue(\"timeout is not big enough\", repeatEach < timeout);\n        this.repeatEach = repeatEach;\n        this.timeout = timeout;\n    }\n\n    public boolean waitUntil() throws Exception {\n        final long limit = new Date().getTime() + timeout;\n        while (new Date().getTime() < limit) {\n            Thread.sleep(repeatEach);\n            if (checkCondition()) {\n                return true;\n            }\n        }\n        return checkCondition();\n    }\n\n    protected boolean checkCondition() throws Exception {\n        if (throwExceptions) {\n            return check();\n        } else {\n            try {\n                return check();\n            } catch (final Exception e) {\n                // do nothing\n            }\n            return false;\n        }\n    };\n\n    /**\n     * Condition to check for.\n     *\n     * @return true if condition is true, false otherwise.\n     * @throws Exception\n     */\n    protected abstract boolean check() throws Exception;\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/application/APIApplicationIT.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.application;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.web.rest.server.api.page.builder.PageItemBuilder.aPageItem;\nimport static org.junit.Assert.*;\nimport static org.mockito.Mockito.spy;\n\nimport java.io.InputStream;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.apache.commons.io.IOUtils;\nimport org.bonitasoft.engine.api.ApplicationAPI;\nimport org.bonitasoft.engine.api.PageAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.business.application.Application;\nimport org.bonitasoft.engine.business.application.ApplicationImportPolicy;\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.application.*;\nimport org.bonitasoft.web.rest.model.portal.page.PageItem;\nimport org.bonitasoft.web.rest.server.api.applicationpage.APIApplicationDataStoreFactory;\nimport org.bonitasoft.web.rest.server.datastore.application.ApplicationDataStoreCreator;\nimport org.bonitasoft.web.test.AbstractConsoleTest;\nimport org.junit.After;\nimport org.junit.Assert;\nimport org.junit.Test;\n\npublic class APIApplicationIT extends AbstractConsoleTest {\n\n    public static final String HOME_PAGE_ZIP = \"/homePage.zip\";\n    public static final String LAYOUT_ZIP = \"/layout.zip\";\n    public static final String THEME_ZIP = \"/theme.zip\";\n    public static final String APP_LINK_DESCRIPTOR = \"/appLinkDescriptor.xml\";\n    private APIApplication apiApplication;\n    private APISession session;\n\n    @Override\n    public void consoleTestSetUp() throws Exception {\n        session = getInitiator().getSession();\n        apiApplication = spy(\n                new APIApplication(new ApplicationDataStoreCreator(), new APIApplicationDataStoreFactory()));\n        apiApplication.setCaller(getAPICaller(session, \"API/living/application\"));\n    }\n\n    private PageAPI getPageAPI() throws Exception {\n        return TenantAPIAccessor.getCustomPageAPI(session);\n    }\n\n    private ApplicationAPI getApplicationAPI() throws Exception {\n        return TenantAPIAccessor.getApplicationAPI(session);\n    }\n\n    @After\n    public void cleanPagesAndApplications() throws Exception {\n        final SearchOptions searchOptions = new SearchOptionsBuilder(0, 100000).done();\n\n        List<Application> apps = getApplicationAPI().searchApplications(searchOptions).getResult();\n        apps.stream().map(Application::getId).forEach(t -> {\n            try {\n                getApplicationAPI().deleteApplication(t);\n            } catch (Exception e) {\n                e.printStackTrace();\n            }\n        });\n\n        List<Page> pages = getPageAPI().searchPages(searchOptions).getResult();\n        var ids = pages.stream().map(Page::getId).toList();\n        getPageAPI().deletePages(ids);\n    }\n\n    private PageItem addPage(String pageFileName) throws Exception {\n        final PageItem pageItem = aPageItem().build();\n        final InputStream pageInputStream = getClass().getResourceAsStream(pageFileName);\n        final byte[] pageContent = IOUtils.toByteArray(pageInputStream);\n        return addPageItemToRepository(pageItem.getContentName(), pageContent);\n    }\n\n    private PageItem addPageItemToRepository(final String pageContentName, final byte[] pageContent) throws Exception {\n        return aPageItem().fromEngineItem(getPageAPI().createPage(pageContentName, pageContent)).build();\n    }\n\n    @Test\n    public void should_add_ApplicationLink() {\n        // Given\n        final ApplicationLinkItem linkItem = ApplicationLinkDefinition.get().createItem();\n        linkItem.setToken(\"tokenLink\");\n        linkItem.setDisplayName(\"Link\");\n        linkItem.setVersion(\"1.0\");\n        linkItem.setProfileId(2L);\n        linkItem.setState(\"ACTIVATED\");\n\n        // When\n        var createdLink = apiApplication.add(linkItem);\n\n        // Then\n        Map<String, String> attributes = new HashMap<>(linkItem.getAttributes().size());\n        linkItem.getAttributes().keySet().forEach(k -> attributes.put(k, createdLink.getAttributes().get(k)));\n        Assert.assertEquals(new HashMap<>(linkItem.getAttributes()), attributes);\n        assertThat(createdLink.isLink()).isTrue();\n    }\n\n    @Test\n    public void should_add_LegacyApplication() throws Exception {\n        // Given\n        var pair = createLegacyApplicationWithOriginal();\n        var original = pair.getKey();\n        var legacyApp = pair.getValue();\n\n        // Then\n        Map<String, String> attributes = new HashMap<>(original.getAttributes().size());\n        original.getAttributes().keySet().forEach(k -> attributes.put(k, legacyApp.getAttributes().get(k)));\n        Assert.assertEquals(new HashMap<>(original.getAttributes()), attributes);\n        assertThat(legacyApp.isLink()).isFalse();\n    }\n\n    @Test\n    public void should_update_ApplicationLink() throws Exception {\n        // Given\n        getApplicationAPI().importApplications(\n                IOUtils.toByteArray(getClass().getResourceAsStream(APP_LINK_DESCRIPTOR)),\n                ApplicationImportPolicy.REPLACE_DUPLICATES);\n        ApplicationLinkItem linkItem = (ApplicationLinkItem) apiApplication\n                .search(0, 1, null, null, Collections.singletonMap(\"token\", \"app1\")).getResults().get(0);\n        Map<String, String> attributes = Map.of(AbstractApplicationItem.ATTRIBUTE_DISPLAY_NAME, \"Link Updated\");\n\n        // When\n        var updatedLink = apiApplication.update(linkItem.getId(), attributes);\n\n        // Then updated\n        assertEquals(\"Link Updated\", updatedLink.getDisplayName());\n        assertEquals(\"Link Updated\", apiApplication.get(linkItem.getId()).getDisplayName());\n        assertThat(linkItem.isLink()).isTrue();\n    }\n\n    @Test\n    public void should_update_LegacyApplication() throws Exception {\n        // Given\n        var legacyApp = createLegacyApplication();\n\n        // When\n        Map<String, String> attributes = Map.of(AbstractApplicationItem.ATTRIBUTE_DISPLAY_NAME, \"Legacy Updated\");\n        var updatedItem = apiApplication.update(legacyApp.getId(), attributes);\n\n        // Then\n        assertEquals(\"Legacy Updated\", updatedItem.getDisplayName());\n        assertThat(updatedItem.isLink()).isFalse();\n    }\n\n    @Test\n    public void should_search_not_support_filtering_on_ApplicationLinks() throws Exception {\n        // Given\n        var legacyApp = createLegacyApplication();\n\n        // When\n        final String search = legacyApp.getDisplayName();\n        final String orders = ApplicationItem.ATTRIBUTE_TOKEN + \" DESC\";\n        final HashMap<String, String> filters = new HashMap<>();\n        filters.put(ApplicationItem.ATTRIBUTE_LINK, \"true\");\n\n        // Then\n        assertThrows(\n                \"Expected exception: The search does not support filtering on application links in this edition.\",\n                BonitaRuntimeException.class, () -> apiApplication.search(0, 1, search, orders, filters));\n    }\n\n    private ApplicationItem createLegacyApplication() throws Exception {\n        return createLegacyApplicationWithOriginal().getValue();\n    }\n\n    /**\n     * Create a legacy application.\n     *\n     * @return a pair with the constructed item as key and the api call result as value.\n     * @throws Exception exception during creation\n     */\n    private Entry<ApplicationItem, ApplicationItem> createLegacyApplicationWithOriginal() throws Exception {\n        addPage(HOME_PAGE_ZIP);\n        final PageItem layout = addPage(LAYOUT_ZIP);\n        final PageItem theme = addPage(THEME_ZIP);\n        final ApplicationItem legacyItem = ApplicationDefinition.get().createItem();\n        legacyItem.setToken(\"tokenLegacy\");\n        legacyItem.setDisplayName(\"Legacy\");\n        legacyItem.setVersion(\"1.0\");\n        legacyItem.setProfileId(2L);\n        legacyItem.setState(\"ACTIVATED\");\n        legacyItem.setLayoutId(layout.getId().toLong());\n        legacyItem.setThemeId(theme.getId().toLong());\n\n        return Collections.singletonMap(legacyItem, (ApplicationItem) apiApplication.add(legacyItem)).entrySet()\n                .iterator().next();\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/cases/APIArchivedCaseIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.cases;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\n\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstance;\nimport org.bonitasoft.test.toolkit.bpm.TestCase;\nimport org.bonitasoft.test.toolkit.bpm.TestProcessFactory;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\nimport org.bonitasoft.test.toolkit.organization.TestUserFactory;\nimport org.bonitasoft.web.rest.model.bpm.cases.ArchivedCaseItem;\nimport org.bonitasoft.web.test.AbstractConsoleTest;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author Séverin Moussel\n */\npublic class APIArchivedCaseIT extends AbstractConsoleTest {\n\n    /**\n     * @return\n     */\n    private APIArchivedCase getAPIArchivedCase() {\n        final APIArchivedCase api = new APIArchivedCase();\n        api.setCaller(getAPICaller(getInitiator().getSession(), \"API/bpm/archivedCase\"));\n        return api;\n    }\n\n    @Override\n    public void consoleTestSetUp() throws Exception {\n    }\n\n    @Override\n    protected TestUser getInitiator() {\n        return TestUserFactory.getJohnCarpenter();\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // GET\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    /**\n     * @return\n     */\n    private TestCase initArchivedCaseForGet() {\n        final TestCase testArchivedCase = TestProcessFactory.getDefaultHumanTaskProcess()\n                .addActor(getInitiator())\n                .startCase();\n\n        testArchivedCase.getNextHumanTask().assignTo(getInitiator()).archive();\n\n        return testArchivedCase;\n    }\n\n    private void assertEquals(final String message, final ArchivedProcessInstance engineItem,\n            final ArchivedCaseItem consoleItem) {\n        Assert.assertEquals(message, engineItem.getId(), consoleItem.getId().toLong().longValue());\n        Assert.assertEquals(message, engineItem.getLastUpdate().toString(), consoleItem.getLastUpdateDate().toString());\n        Assert.assertEquals(message, engineItem.getState(), consoleItem.getState());\n        Assert.assertEquals(message, engineItem.getStartDate(), consoleItem.getStartDate());\n        Assert.assertEquals(message, engineItem.getStartedBy(), (long) consoleItem.getStartedByUserId().toLong());\n        Assert.assertEquals(message, engineItem.getEndDate(), consoleItem.getEndDate());\n        Assert.assertEquals(message, engineItem.getProcessDefinitionId(), (long) consoleItem.getProcessId().toLong());\n        Assert.assertNotNull(message, consoleItem.getArchivedDate());\n\n    }\n\n    @Test\n    public void testGetArchivedCase() {\n        final TestCase testCase = initArchivedCaseForGet();\n        final ArchivedProcessInstance archivedProcessInstance = testCase.getArchive();\n\n        final ArchivedCaseItem caseItem = getAPIArchivedCase().runGet(APIID.makeAPIID(archivedProcessInstance.getId()),\n                new ArrayList<>(),\n                new ArrayList<>());\n\n        Assert.assertNotNull(\"ArchivedCase not found\", caseItem);\n\n        assertEquals(\"Wrong case found\" + \". Expected=\" + archivedProcessInstance + \". Found=\" + caseItem.toString(),\n                archivedProcessInstance, caseItem);\n    }\n\n    @Test\n    public void testGetArchivedCaseWithDeploys() {\n        final TestCase testCase = initArchivedCaseForGet();\n        final ArchivedProcessInstance archivedProcessInstance = testCase.getArchive();\n\n        final ArchivedCaseItem caseItem = getAPIArchivedCase().runGet(APIID.makeAPIID(archivedProcessInstance.getId()),\n                Arrays.asList(ArchivedCaseItem.ATTRIBUTE_PROCESS_ID, ArchivedCaseItem.ATTRIBUTE_STARTED_BY_USER_ID),\n                new ArrayList<>());\n\n        Assert.assertNotNull(\"Failed to deploy process\", caseItem.getProcess());\n        Assert.assertEquals(\"Wrong process deployed\", testCase.getProcessInstance().getName(),\n                caseItem.getProcess().getName());\n\n        Assert.assertNotNull(\"Failed to deploy intiator user\", caseItem.getStartedByUserId());\n        Assert.assertEquals(\"Wrong process deployed\", getInitiator().getUserName(),\n                caseItem.getStartedByUser().getUserName());\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/cases/APIArchivedCommentIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.cases;\n\nimport static org.junit.Assert.assertEquals;\n\nimport java.util.HashMap;\n\nimport org.bonitasoft.test.toolkit.bpm.TestCase;\nimport org.bonitasoft.test.toolkit.bpm.TestProcessFactory;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\nimport org.bonitasoft.test.toolkit.organization.TestUserFactory;\nimport org.bonitasoft.web.rest.model.bpm.cases.ArchivedCommentItem;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.test.AbstractConsoleTest;\nimport org.junit.Test;\n\n/**\n * @author Paul AMAR\n */\npublic class APIArchivedCommentIT extends AbstractConsoleTest {\n\n    private APIArchivedComment apiArchivedComment;\n\n    @Override\n    public void consoleTestSetUp() throws Exception {\n        this.apiArchivedComment = new APIArchivedComment();\n        this.apiArchivedComment\n                .setCaller(getAPICaller(TestUserFactory.getJohnCarpenter().getSession(), \"API/bpm/archivedComment\"));\n    }\n\n    @Override\n    protected TestUser getInitiator() {\n        return TestUserFactory.getJohnCarpenter();\n    }\n\n    @Test\n    public void testSearch() throws Exception {\n        TestCase aCase = TestProcessFactory.getDefaultHumanTaskProcess().addActor(getInitiator()).startCase();\n        aCase.getNextHumanTask().assignTo(getInitiator());\n        aCase.addComments(getInitiator(), 12, \"mon Commentaire\");\n        aCase.execute();\n\n        final ItemSearchResult<ArchivedCommentItem> mesResultats = this.apiArchivedComment.search(0, 12, \"\", \"\",\n                new HashMap<>());\n\n        assertEquals(mesResultats.getLength(), 12);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/cases/APICaseDocumentAnotherIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.cases;\n\nimport static junit.framework.Assert.assertEquals;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.test.toolkit.bpm.TestCase;\nimport org.bonitasoft.test.toolkit.bpm.TestProcessFactory;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\nimport org.bonitasoft.test.toolkit.organization.TestUserFactory;\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseDocumentItem;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.test.AbstractConsoleTest;\nimport org.junit.Test;\n\n/**\n * @author Colin PUY\n */\npublic class APICaseDocumentAnotherIT extends AbstractConsoleTest {\n\n    private APICaseDocument apiCaseDocument;\n\n    @Override\n    public void consoleTestSetUp() throws Exception {\n        apiCaseDocument = new APICaseDocument();\n        apiCaseDocument.setCaller(getAPICaller(getInitiator().getSession(), \"API/bpm/caseDocument\"));\n    }\n\n    @Override\n    protected TestUser getInitiator() {\n        return TestUserFactory.getJohnCarpenter();\n    }\n\n    @Test\n    public void caseDocumentsCanBeCountedByCaseId() throws Exception {\n        TestCase startCase = TestProcessFactory.getProcessWithDocumentAttached().addActor(getInitiator()).startCase();\n        Map<String, String> caseIdfilter = buildCaseIdFilter(startCase.getId());\n\n        ItemSearchResult<CaseDocumentItem> searchResult = apiCaseDocument.runSearch(0, 0, null, null, caseIdfilter,\n                null, null);\n\n        assertEquals(1L, searchResult.getTotal());\n    }\n\n    private Map<String, String> buildCaseIdFilter(long caseId) {\n        Map<String, String> filters = new HashMap<>();\n        filters.put(CaseDocumentItem.ATTRIBUTE_CASE_ID, String.valueOf(caseId));\n        return filters;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/cases/APICaseDocumentIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.cases;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.util.HashMap;\n\nimport org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils;\nimport org.bonitasoft.engine.api.PlatformAPIAccessor;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.document.Document;\nimport org.bonitasoft.engine.io.FileContent;\nimport org.bonitasoft.test.toolkit.bpm.TestCase;\nimport org.bonitasoft.test.toolkit.bpm.TestProcessFactory;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\nimport org.bonitasoft.test.toolkit.organization.TestUserFactory;\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseDocumentItem;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.test.AbstractConsoleTest;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author Vincent Elcrin\n */\npublic class APICaseDocumentIT extends AbstractConsoleTest {\n\n    private APICaseDocument apiCaseDocument;\n\n    private Document expectedDocument;\n\n    /*\n     * (non-Javadoc)\n     * @see org.bonitasoft.console.server.AbstractConsoleTest#consoleTestSetUp()\n     */\n    @Override\n    public void consoleTestSetUp() throws Exception {\n        apiCaseDocument = new APICaseDocument();\n        apiCaseDocument.setCaller(getAPICaller(TestUserFactory.getJohnCarpenter().getSession(),\n                \"API/bpm/caseDocument\"));\n\n        // create process with attached document\n        final TestCase testCase = TestProcessFactory.getProcessWithDocumentAttached()\n                .addActor(getInitiator())\n                .startCase();\n\n        expectedDocument = TenantAPIAccessor.getProcessAPI(getInitiator().getSession())\n                .getLastDocument(testCase.getId(), \"Document 1667\");\n    }\n\n    private void assertDocumentsMatch(final Document document, final CaseDocumentItem caseDocumentItem) {\n        Assert.assertEquals(\"Name is different\", document.getName(), caseDocumentItem.getName());\n        Assert.assertEquals(\"File name is different\", document.getContentFileName(), caseDocumentItem.getFileName());\n        Assert.assertEquals(\"Author is different\", document.getAuthor(),\n                (long) caseDocumentItem.getSubmittedBy().toLong());\n        Assert.assertEquals(\"Mime type is different\", document.getContentMimeType(), caseDocumentItem.getMIMEType());\n        Assert.assertEquals(\"Content storage id is different\", document.getContentStorageId(),\n                caseDocumentItem.getStorageId());\n        Assert.assertEquals(\"Creation date is different\", document.getCreationDate(),\n                caseDocumentItem.getCreationDate());\n        Assert.assertEquals(\"Id is different\", document.getId(), (long) caseDocumentItem.getId().toLong());\n        Assert.assertEquals(\"Process instance id is different\", document.getProcessInstanceId(),\n                (long) caseDocumentItem.getCaseId().toLong());\n        Assert.assertEquals(\"Url is different\", document.getUrl(), caseDocumentItem.getURL());\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see org.bonitasoft.test.toolkit.AbstractJUnitTest#getInitiator()\n     */\n    @Override\n    protected TestUser getInitiator() {\n        return TestUserFactory.getJohnCarpenter();\n    }\n\n    @Test\n    public void testAPISearch() {\n        final ItemSearchResult<CaseDocumentItem> res = apiCaseDocument.search(0, 10, null, null, new HashMap<>());\n        Assert.assertNotNull(res);\n        Assert.assertNotNull(res.getResults());\n        Assert.assertTrue(res.getResults().size() > 0);\n        assertDocumentsMatch(expectedDocument, res.getResults().get(0));\n    }\n\n    @Test\n    public void testAPISearchSupervisedResultEmpty() {\n        final HashMap<String, String> filters = new HashMap<>();\n        filters.put(CaseDocumentItem.FILTER_SUPERVISOR_ID, String.valueOf(getInitiator().getId()));\n\n        final ItemSearchResult<CaseDocumentItem> res = apiCaseDocument.search(0, 10, null, null, filters);\n        Assert.assertNotNull(res);\n        Assert.assertNotNull(res.getResults());\n        Assert.assertTrue(res.getResults().size() == 0);\n    }\n\n    @Test\n    public void testAPISearchSupervised() throws Exception {\n        // set initiator as process supervisor\n        TestProcessFactory.getProcessWithDocumentAttached()\n                .addSupervisor(getInitiator());\n\n        final HashMap<String, String> filters = new HashMap<>();\n        filters.put(CaseDocumentItem.FILTER_SUPERVISOR_ID, String.valueOf(getInitiator().getId()));\n\n        final ItemSearchResult<CaseDocumentItem> res = apiCaseDocument.search(0, 10, null, null, filters);\n        Assert.assertNotNull(res);\n        Assert.assertNotNull(res.getResults());\n        Assert.assertTrue(res.getResults().size() > 0);\n        assertDocumentsMatch(expectedDocument, res.getResults().get(0));\n    }\n\n    @Test\n    public void testAPIGet() {\n        assertDocumentsMatch(expectedDocument, apiCaseDocument.get(APIID.makeAPIID(expectedDocument.getId())));\n    }\n\n    @Test\n    public void testAPICaseDocumentUpdateUrl() {\n        final HashMap<String, String> attributes = new HashMap<>();\n        attributes.put(CaseDocumentItem.ATTRIBUTE_CASE_ID, String.valueOf(expectedDocument.getProcessInstanceId()));\n        attributes.put(CaseDocumentItem.ATTRIBUTE_NAME, expectedDocument.getName());\n        attributes.put(CaseDocumentItem.ATTRIBUTE_CONTENT_FILENAME, expectedDocument.getContentFileName());\n        attributes.put(CaseDocumentItem.ATTRIBUTE_CONTENT_MIMETYPE, expectedDocument.getContentMimeType());\n\n        attributes.put(CaseDocumentItem.ATTRIBUTE_URL, \"newurl\");\n\n        Assert.assertEquals(\"newurl\",\n                apiCaseDocument.update(APIID.makeAPIID(expectedDocument.getId()), attributes).getURL());\n    }\n\n    @Test\n    public void testAPICaseDocumentUpdateFile() throws Exception {\n        final HashMap<String, String> attributes = new HashMap<>();\n        attributes.put(CaseDocumentItem.ATTRIBUTE_CASE_ID, String.valueOf(expectedDocument.getProcessInstanceId()));\n        attributes.put(CaseDocumentItem.ATTRIBUTE_NAME, expectedDocument.getName());\n        attributes.put(CaseDocumentItem.ATTRIBUTE_CONTENT_FILENAME, expectedDocument.getContentFileName());\n        attributes.put(CaseDocumentItem.ATTRIBUTE_CONTENT_MIMETYPE, expectedDocument.getContentMimeType());\n\n        final File tmpDir = WebBonitaConstantsUtils.getTenantInstance().getTempFolder();\n        tmpDir.mkdirs();\n        final File file = new File(tmpDir, \"thisismynewfile.doc\");\n        file.createNewFile();\n\n        String fileKey = PlatformAPIAccessor.getTemporaryContentAPI()\n                .storeTempFile(new FileContent(\"thisismynewfile.doc\", new FileInputStream(file), \"text/plain\"));\n\n        attributes.put(CaseDocumentItem.ATTRIBUTE_UPLOAD_PATH, fileKey);\n\n        CaseDocumentItem doc = apiCaseDocument.update(APIID.makeAPIID(expectedDocument.getId()), attributes);\n        Assert.assertNotNull(\"Failed while updating the case document\", doc);\n    }\n\n    @Test(expected = APIException.class)\n    public void testMalformedUpdate() {\n        apiCaseDocument.update(APIID.makeAPIID(expectedDocument.getId()), new HashMap<>());\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/cases/APICaseIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.cases;\n\nimport static org.bonitasoft.test.toolkit.bpm.ProcessVariable.*;\nimport static org.bonitasoft.web.rest.model.builder.bpm.cases.CaseItemBuilder.aCaseItem;\nimport static org.hamcrest.Matchers.equalTo;\nimport static org.hamcrest.Matchers.is;\nimport static org.junit.Assert.assertThat;\nimport static org.junit.Assert.assertTrue;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceNotFoundException;\nimport org.bonitasoft.test.toolkit.bpm.*;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\nimport org.bonitasoft.test.toolkit.organization.TestUserFactory;\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseItem;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.test.AbstractConsoleTest;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author Séverin Moussel\n */\npublic class APICaseIT extends AbstractConsoleTest {\n\n    private APICase apiCase;\n\n    @Override\n    public void consoleTestSetUp() throws Exception {\n        apiCase = new APICase();\n        apiCase.setCaller(getAPICaller(getInitiator().getSession(), \"API/bpm/case\"));\n    }\n\n    @Override\n    protected TestUser getInitiator() {\n        return TestUserFactory.getJohnCarpenter();\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // GET\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    private ProcessInstance getProcessInstance(final APIID caseId) throws Exception {\n        try {\n            return TenantAPIAccessor.getProcessAPI(getInitiator().getSession()).getProcessInstance(caseId.toLong());\n        } catch (final ProcessInstanceNotFoundException e) {\n            return null;\n        }\n    }\n\n    private void assertEquals(final String message, final ProcessInstance engineItem, final CaseItem consoleItem) {\n        Assert.assertEquals(message, engineItem.getId(), consoleItem.getId().toLong().longValue());\n        Assert.assertEquals(message, engineItem.getLastUpdate(), consoleItem.getLastUpdateDate());\n        Assert.assertEquals(message, engineItem.getState(), consoleItem.getState());\n        Assert.assertEquals(message, engineItem.getStartDate(), consoleItem.getStartDate());\n        Assert.assertEquals(message, engineItem.getStartedBy(),\n                (long) consoleItem.getStartedByUserId().getPartAsLong(0).longValue());\n        Assert.assertEquals(message, engineItem.getEndDate(), consoleItem.getEndDate());\n        Assert.assertEquals(message, engineItem.getProcessDefinitionId(), (long) consoleItem.getProcessId().toLong());\n        Assert.assertEquals(message, engineItem.getCallerId(),\n                (long) consoleItem.getCallerId().getPartAsLong(0).longValue());\n    }\n\n    @Test\n    public void testGetCase() {\n        final TestCase testCase = TestProcessFactory.getDefaultHumanTaskProcess().addActor(getInitiator()).startCase();\n\n        final CaseItem caseItem = apiCase.runGet(APIID.makeAPIID(testCase.getId()), new ArrayList<>(),\n                new ArrayList<>());\n\n        Assert.assertNotNull(\"Case not found\", caseItem);\n        assertEquals(\"Wrong case found\", testCase.getProcessInstance(), caseItem);\n    }\n\n    @Test\n    public void should_get_child_process_with_callerId() throws Exception {\n        final TestProcess childProcess = TestProcessFactory.getDefaultHumanTaskProcess().addActor(getInitiator())\n                .enable();\n        TestProcess parentProcess = TestProcessFactory.getCallActivityProcess(childProcess.getProcessDefinition())\n                .addActor(getInitiator()).enable();\n        try {\n            final TestCase parentCase = parentProcess.startCase();\n\n            TestHumanTask humanTask = parentCase.getNextHumanTask();\n\n            final CaseItem childCaseItem = apiCase.runGet(\n                    APIID.makeAPIID(humanTask.getHumanTaskInstance().getParentProcessInstanceId()), new ArrayList<>(),\n                    new ArrayList<>());\n\n            Assert.assertNotNull(\"Case not found\", childCaseItem);\n            assertEquals(\"Wrong case found\", getProcessInstance(childCaseItem.getId()), childCaseItem);\n        } finally {\n            TestProcessFactory.getInstance().delete(parentProcess);\n            TestProcessFactory.getInstance().delete(childProcess);\n        }\n    }\n\n    @Test\n    public void testGetCaseWithDeploys() {\n        final TestProcess testProcess = TestProcessFactory.getDefaultHumanTaskProcess().addActor(getInitiator());\n        final TestCase testCase = testProcess.startCase();\n\n        final CaseItem caseItem = apiCase.runGet(APIID.makeAPIID(testCase.getId()),\n                Arrays.asList(CaseItem.ATTRIBUTE_PROCESS_ID, CaseItem.ATTRIBUTE_STARTED_BY_USER_ID), new ArrayList<>());\n\n        Assert.assertNotNull(\"Failed to deploy process\", caseItem.getProcess());\n        Assert.assertEquals(\"Wrong process deployed\", testCase.getProcessInstance().getName(),\n                caseItem.getProcess().getName());\n\n        Assert.assertNotNull(\"Failed to deploy intiator user\", caseItem.getStartedByUserId());\n        Assert.assertEquals(\"Wrong process deployed\", getInitiator().getUserName(),\n                caseItem.getStartedByUser().getUserName());\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // SEARCH\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Init cases for search\n     */\n    private void initCasesForSearch() {\n        final TestProcess testProcess = TestProcessFactory.getDefaultHumanTaskProcess();\n        testProcess.addActor(TestUserFactory.getMrSpechar());\n\n        // 13 cases for current User and 1 for another user\n        testProcess.startCases(13);\n        testProcess.startCase(TestUserFactory.getMrSpechar());\n\n        // Another process\n        final TestProcess testProcess2 = TestProcessFactory.getHumanTaskProcess(\"SecondProcess\");\n        testProcess2.addActor(getInitiator());\n\n        // 1 case for current User\n        testProcess2.startCase();\n    }\n\n    /**\n     * Test search for user (Initiator)\n     */\n    @Test\n    public void testSearchByInitiator() {\n\n        initCasesForSearch();\n\n        // Search for current User\n        final Map<String, String> filters = new HashMap<>();\n        filters.put(CaseItem.ATTRIBUTE_STARTED_BY_USER_ID, String.valueOf(getInitiator().getId()));\n\n        final ItemSearchResult<CaseItem> caseItems = apiCase.runSearch(0, 10, null, null, filters, null, null);\n\n        checkSearchResults(caseItems, 10, 14);\n    }\n\n    /**\n     * Test search for user, with only worked on cases\n     */\n    @Test\n    public void testSearchUserWorkedOn() {\n        initCasesForSearch();\n\n        // Search for current User\n        final Map<String, String> filters = new HashMap<>();\n        filters.put(CaseItem.FILTER_USER_ID, String.valueOf(getInitiator().getId()));\n\n        final ItemSearchResult<CaseItem> caseItems = apiCase.runSearch(0, 10, null, null, filters, null, null);\n\n        checkSearchResults(caseItems, 10, 14);\n    }\n\n    /**\n     * Test search for user by process\n     */\n    @Test\n    public void testSearchByProcess() {\n\n        initCasesForSearch();\n\n        final TestProcess testProcess3 = TestProcessFactory.getHumanTaskProcess(\"ThirdProcess\");\n        testProcess3.addActor(getInitiator());\n        testProcess3.startCases(5);\n\n        // Search for current User\n        final Map<String, String> filters = new HashMap<>();\n        filters.put(CaseItem.ATTRIBUTE_PROCESS_ID, String.valueOf(testProcess3.getId()));\n\n        final ItemSearchResult<CaseItem> caseItems = apiCase.runSearch(0, 3, null, null, filters, null, null);\n\n        checkSearchResults(caseItems, 3, 5);\n\n    }\n\n    /**\n     * Admin test cases\n     */\n    @Test\n    public void testSearchAdministrator() {\n\n        initCasesForSearch();\n\n        final ItemSearchResult<CaseItem> caseItems = apiCase.runSearch(0, 10, null, null, null, null, null);\n\n        // On more process (testProcess3)\n        checkSearchResults(caseItems, 10, 15);\n    }\n\n    /**\n     * Process Owner test cases\n     */\n    @Test\n    public void testSearchProcessOwner() {\n\n        initCasesForSearch();\n\n        final TestProcess testProcess3 = TestProcessFactory.getHumanTaskProcess(\"ThirdProcess\");\n        testProcess3.addActor(getInitiator());\n        testProcess3.addSupervisor(TestUserFactory.getRidleyScott());\n        testProcess3.startCases(5);\n\n        // Search for current User\n        final Map<String, String> filters = new HashMap<>();\n        filters.put(CaseItem.FILTER_SUPERVISOR_ID, String.valueOf(TestUserFactory.getRidleyScott().getId()));\n\n        final ItemSearchResult<CaseItem> caseItems = apiCase.runSearch(0, 10, null, null, filters, null, null);\n\n        checkSearchResults(caseItems, 10, 5);\n    }\n\n    // @Test This test needs to be fixed. Has it ever passed?\n    public void testSearchTeamManager() {\n        final Map<String, TestUser> managedList = TestUserFactory.getManagedUsers(1);\n\n        final TestProcess testProcess4 = TestProcessFactory.getHumanTaskProcess(\"ProcessTeamManager\");\n        testProcess4.addActor(managedList.get(\"managed.user0\"));\n        testProcess4.startCases(25);\n\n        final TestProcess testProcess5 = TestProcessFactory.getHumanTaskProcess(\"Process five\");\n        testProcess5.addActor(TestUserFactory.getRidleyScott());\n        testProcess5.startCases(10);\n\n        // Search for current User\n        final Map<String, String> filters = new HashMap<>();\n        filters.put(CaseItem.FILTER_TEAM_MANAGER_ID, String.valueOf(TestUserFactory.getTeamManagerUser().getId()));\n\n        final ItemSearchResult<CaseItem> caseItems = apiCase.runSearch(0, 100, null, null, filters, null, null);\n        checkSearchResults(caseItems, 100, 25);\n    }\n\n    private void checkSearchResults(final ItemSearchResult<CaseItem> caseItems, final int nbResultsByPageExpected,\n            final int nbTotalResultsExpected) {\n        assertTrue(\"Empty search results\", caseItems.getLength() > 0);\n        Assert.assertEquals(\"Wrong page size\", caseItems.getLength(), nbResultsByPageExpected);\n        Assert.assertEquals(\"Wrong Total size\", caseItems.getTotal(), nbTotalResultsExpected);\n    }\n\n    @Test\n    public void we_can_start_a_case() throws Exception {\n        final TestProcess process = TestProcessFactory.getDefaultHumanTaskProcess().addActor(getInitiator()).enable();\n\n        final CaseItem item = apiCase.runAdd(aCaseItem().withProcessId(process.getId()).build());\n\n        final ProcessInstance instance = getProcessInstance(item.getId());\n        assertThat(instance.getProcessDefinitionId(), is(item.getProcessId().toLong()));\n    }\n\n    @Test\n    public void we_can_start_a_case_with_user() throws Exception {\n        final TestProcess process = TestProcessFactory.getDefaultHumanTaskProcess()\n                .addActor(TestUserFactory.getRidleyScott()).enable();\n\n        final CaseItem item = apiCase.runAdd(aCaseItem().withProcessId(process.getId())\n                .withUserId(TestUserFactory.getRidleyScott().getId()).build());\n\n        final ProcessInstance instance = getProcessInstance(item.getId());\n        assertThat(instance.getProcessDefinitionId(), is(item.getProcessId().toLong()));\n        assertThat(instance.getStartedBy(), is(TestUserFactory.getRidleyScott().getId()));\n    }\n\n    @Test\n    public void we_can_start_a_case_with_variables() throws Exception {\n        final String jsonVariables = \"[\" + \"{\\\"name\\\": \\\"stringVariable\\\", \\\"value\\\": \\\"newValue\\\"},\"\n                + \"{\\\"name\\\": \\\"longVariable\\\", \\\"value\\\": 9},\"\n                + \"{\\\"name\\\": \\\"dateVariable\\\", \\\"value\\\": 349246800000}\" + \"]\";\n        final TestProcess process = createProcessWithVariables(aStringVariable(\"stringVariable\", \"firstValue\"),\n                aLongVariable(\"longVariable\", 1L),\n                aDateVariable(\"dateVariable\", \"428558400000\"));\n\n        final CaseItem item = apiCase\n                .runAdd(aCaseItem().withProcessId(process.getId()).withVariables(jsonVariables).build());\n\n        final ProcessInstance instance = getProcessInstance(item.getId());\n        assertThat(instance.getProcessDefinitionId(), is(item.getProcessId().toLong()));\n        assertThat((String) getProcessDataInstanceValue(\"stringVariable\", instance.getId()), equalTo(\"newValue\"));\n        assertThat((Long) getProcessDataInstanceValue(\"longVariable\", instance.getId()), equalTo(9L));\n        assertThat((Date) getProcessDataInstanceValue(\"dateVariable\", instance.getId()),\n                equalTo(new Date(349246800000L)));\n    }\n\n    private TestProcess createProcessWithVariables(final ProcessVariable... processVariables) {\n        return TestProcessFactory.createProcessWithVariables(\"processWithVariables\", processVariables)\n                .addActor(getInitiator()).enable();\n    }\n\n    private Serializable getProcessDataInstanceValue(final String dataName, final long processInstanceId)\n            throws Exception {\n        return TenantAPIAccessor.getProcessAPI(getInitiator().getSession())\n                .getProcessDataInstance(dataName, processInstanceId).getValue();\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/cases/APICaseVariableIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.cases;\n\nimport static org.bonitasoft.test.toolkit.bpm.ProcessVariable.*;\nimport static org.bonitasoft.web.toolkit.client.data.APIID.makeAPIID;\nimport static org.junit.Assert.assertEquals;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.test.toolkit.bpm.ProcessVariable;\nimport org.bonitasoft.test.toolkit.bpm.TestCase;\nimport org.bonitasoft.test.toolkit.bpm.TestProcessFactory;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\nimport org.bonitasoft.test.toolkit.organization.TestUserFactory;\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseVariableDefinition;\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseVariableItem;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.test.AbstractConsoleTest;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.Test;\n\n/**\n * @author Colin PUY\n */\npublic class APICaseVariableIT extends AbstractConsoleTest {\n\n    private APICaseVariable apiCaseVariable;\n\n    @Override\n    public void consoleTestSetUp() throws Exception {\n        apiCaseVariable = new APICaseVariable();\n        apiCaseVariable.setCaller(getAPICaller(getInitiator().getSession(), \"API/bpm/caseVariable\"));\n    }\n\n    @Override\n    protected TestUser getInitiator() {\n        return TestUserFactory.getJohnCarpenter();\n    }\n\n    private APIID buildAPIID(TestCase aCase, ProcessVariable expectedVariable) {\n        APIID apiid = makeAPIID(Arrays.asList(String.valueOf(aCase.getId()), expectedVariable.getName()));\n        apiid.setItemDefinition(CaseVariableDefinition.get());\n        return apiid;\n    }\n\n    private TestCase createCaseWithVariable(ProcessVariable... expectedVariable) {\n        return TestProcessFactory.createProcessWithVariables(\"aProcessWithVariablesVariable\", expectedVariable)\n                .addActor(getInitiator()).startCase();\n    }\n\n    private Map<String, String> buildUpdateAttributes(String newValue, String type) {\n        Map<String, String> attributes = new HashMap<>();\n        attributes.put(CaseVariableItem.ATTRIBUTE_VALUE, newValue);\n        attributes.put(CaseVariableItem.ATTRIBUTE_TYPE, type);\n        return attributes;\n    }\n\n    private Map<String, String> buildCaseIdFilter(TestCase aCase) {\n        Map<String, String> filters = new HashMap<>();\n        filters.put(CaseVariableItem.ATTRIBUTE_CASE_ID, String.valueOf(aCase.getId()));\n        return filters;\n    }\n\n    @Test\n    public void weCanUpdateALongValue() throws Exception {\n        ProcessVariable expectedVariable = createLongVariable(1L);\n        TestCase aCase = createCaseWithVariable(expectedVariable);\n        String newLongValue = \"2\";\n        Map<String, String> attributes = buildUpdateAttributes(newLongValue, Long.class.getName());\n\n        CaseVariableItem variable = apiCaseVariable.runUpdate(buildAPIID(aCase, expectedVariable), attributes);\n\n        assertEquals(newLongValue, variable.getValue());\n    }\n\n    @Test\n    public void weCanUpdateAnIntegerValue() throws Exception {\n        ProcessVariable expectedVariable = createIntVariable(1);\n        TestCase aCase = createCaseWithVariable(expectedVariable);\n        String newIntValue = \"2\";\n        Map<String, String> attributes = buildUpdateAttributes(newIntValue, Integer.class.getName());\n\n        CaseVariableItem variable = apiCaseVariable.runUpdate(buildAPIID(aCase, expectedVariable), attributes);\n\n        assertEquals(newIntValue, variable.getValue());\n    }\n\n    @Test\n    public void weCanUpdateABooleanValue() throws Exception {\n        ProcessVariable expectedVariable = createBooleanVariable(true);\n        TestCase aCase = createCaseWithVariable(expectedVariable);\n        String newBooleanValue = \"false\";\n        Map<String, String> attributes = buildUpdateAttributes(newBooleanValue, Boolean.class.getName());\n\n        CaseVariableItem variable = apiCaseVariable.runUpdate(buildAPIID(aCase, expectedVariable), attributes);\n\n        assertEquals(newBooleanValue, variable.getValue());\n    }\n\n    @Test\n    public void weCanUpdateAStringValue() throws Exception {\n        ProcessVariable expectedVariable = createStringVariable(\"aString\");\n        TestCase aCase = createCaseWithVariable(expectedVariable);\n        String newStringValue = \"aNewString\";\n        Map<String, String> attributes = buildUpdateAttributes(newStringValue, String.class.getName());\n\n        CaseVariableItem variable = apiCaseVariable.runUpdate(buildAPIID(aCase, expectedVariable), attributes);\n\n        assertEquals(newStringValue, variable.getValue());\n    }\n\n    @Test\n    public void weCanUpdateADoubleValue() throws Exception {\n        ProcessVariable expectedVariable = createDoubleVariable(12.3d);\n        TestCase aCase = createCaseWithVariable(expectedVariable);\n        String newDoubleValue = \"46.35\";\n        Map<String, String> attributes = buildUpdateAttributes(newDoubleValue, Double.class.getName());\n\n        CaseVariableItem variable = apiCaseVariable.runUpdate(buildAPIID(aCase, expectedVariable), attributes);\n\n        assertEquals(newDoubleValue, variable.getValue());\n    }\n\n    @Test\n    public void search() throws Exception {\n        TestCase aCase = createCaseWithVariable(createLongVariable(1L), createIntVariable(1),\n                createStringVariable(\"hello\"));\n        Map<String, String> filters = buildCaseIdFilter(aCase);\n\n        ItemSearchResult<CaseVariableItem> searchResults = apiCaseVariable.runSearch(0, 2, null, null, filters, null,\n                null);\n\n        assertEquals(3L, searchResults.getTotal());\n        assertEquals(2, searchResults.getLength());\n        assertEquals(2, searchResults.getResults().size());\n    }\n\n    @Test\n    public void getReturnACaseVariable() throws Exception {\n        ProcessVariable expectedVariable = createLongVariable(1L);\n        TestCase aCase = createCaseWithVariable(expectedVariable);\n        APIID apiid = buildAPIID(aCase, expectedVariable);\n\n        CaseVariableItem variable = apiCaseVariable.runGet(apiid, null, null);\n\n        assertEquals(expectedVariable.getName(), variable.getName());\n        assertEquals(expectedVariable.getClassName(), variable.getType());\n        assertEquals(expectedVariable.getDefaultValue().getContent(), variable.getValue());\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/cases/APICommentIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.cases;\n\nimport static org.junit.Assert.assertEquals;\n\nimport java.util.HashMap;\n\nimport org.assertj.core.api.Assertions;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.comment.SearchCommentsDescriptor;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.test.toolkit.bpm.TestCase;\nimport org.bonitasoft.test.toolkit.bpm.TestProcessFactory;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\nimport org.bonitasoft.test.toolkit.organization.TestUserFactory;\nimport org.bonitasoft.web.rest.model.bpm.cases.CommentItem;\nimport org.bonitasoft.web.test.AbstractConsoleTest;\nimport org.junit.Test;\n\npublic class APICommentIT extends AbstractConsoleTest {\n\n    private APIComment apiComment;\n\n    private final String content = \"Commentaire du processus par défault\";\n\n    private TestCase testCase;\n\n    /*\n     * (non-Javadoc)\n     * @see org.bonitasoft.console.server.AbstractJUnitWebTest#webTestSetUp()\n     */\n    @Override\n    public void consoleTestSetUp() throws Exception {\n        this.apiComment = new APIComment();\n        this.apiComment.setCaller(getAPICaller(getInitiator().getSession(), \"API/bpm/comment\"));\n\n        testCase = TestProcessFactory.getDefaultHumanTaskProcess().addActor(getInitiator()).startCase();\n    }\n\n    @Override\n    protected TestUser getInitiator() {\n        return TestUserFactory.getJohnCarpenter();\n    }\n\n    @Test\n    public void testAddCommentItem() throws Exception {\n        final CommentItem commentItem = createCommentItem(this.content);\n        this.apiComment.add(commentItem);\n\n        Assertions.assertThat(\n                TenantAPIAccessor.getProcessAPI(getInitiator().getSession())\n                        .searchComments(\n                                new SearchOptionsBuilder(0, 10)\n                                        .filter(SearchCommentsDescriptor.PROCESS_INSTANCE_ID, testCase.getId()).done())\n                        .getCount())\n                .isEqualTo(1);\n    }\n\n    @Test\n    public void testSearchCommentItem() {\n        testCase.addComments(getInitiator(), 3, this.content);\n\n        // Set the filters\n        final HashMap<String, String> filters = new HashMap<>();\n        filters.put(CommentItem.ATTRIBUTE_USER_ID, String.valueOf(getInitiator().getId()));\n        filters.put(CommentItem.ATTRIBUTE_PROCESS_INSTANCE_ID, String.valueOf(testCase.getId()));\n\n        // Search the CommentItem\n        final CommentItem item = this.apiComment.search(0, 10, null, null, filters).getResults().get(0);\n        assertEquals(\"Find the wrong CommentItem with APIComment\", this.content + \"0\",\n                item.getAttributeValue(CommentItem.ATTRIBUTE_CONTENT));\n\n    }\n\n    /**\n     * @param comment\n     */\n    private CommentItem createCommentItem(final String comment) {\n        final CommentItem item = new CommentItem();\n        item.setProcessInstanceId(testCase.getId());\n        item.setContent(comment);\n        return item;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/flownode/APIActivityIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.web.toolkit.client.data.APIID.makeAPIID;\nimport static org.hamcrest.Matchers.is;\nimport static org.junit.Assert.assertThat;\n\nimport java.io.Serializable;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceNotFoundException;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance;\nimport org.bonitasoft.test.toolkit.bpm.TestHumanTask;\nimport org.bonitasoft.test.toolkit.bpm.TestProcessFactory;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\nimport org.bonitasoft.test.toolkit.organization.TestUserFactory;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ActivityItem;\nimport org.bonitasoft.web.rest.server.WaitUntil;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.test.AbstractConsoleTest;\nimport org.junit.Test;\n\npublic class APIActivityIT extends AbstractConsoleTest {\n\n    private static final String JSON_UPDATE_VARIABLES = \"[\" +\n            \"{\\\"name\\\": \\\"variable1\\\", \\\"value\\\": \\\"newValue\\\"},\" +\n            \"{\\\"name\\\": \\\"variable2\\\", \\\"value\\\": 9},\" +\n            \"{\\\"name\\\": \\\"variable3\\\", \\\"value\\\": 349246800000}\" +\n            \"]\";\n\n    private APIActivity apiActivity;\n\n    @Override\n    public void consoleTestSetUp() throws Exception {\n        apiActivity = new APIActivity();\n        apiActivity.setCaller(getAPICaller(TestUserFactory.getJohnCarpenter().getSession(), \"API/bpm/activity\"));\n    }\n\n    @Override\n    protected TestUser getInitiator() {\n        return TestUserFactory.getJohnCarpenter();\n    }\n\n    @Test\n    public void api_can_update_activity_variables() throws Exception {\n        final TestHumanTask activity = TestProcessFactory.createActivityWithVariables(getInitiator());\n        final Map<String, String> attributes = new HashMap<>();\n        attributes.put(ActivityItem.ATTRIBUTE_VARIABLES, JSON_UPDATE_VARIABLES);\n\n        apiActivity.runUpdate(makeAPIID(activity.getId()), attributes);\n\n        assertThat(activity.getDataInstance(\"variable1\").getValue(), is((Serializable) \"newValue\"));\n        assertThat(activity.getDataInstance(\"variable2\").getValue(), is((Serializable) 9L));\n        assertThat(activity.getDataInstance(\"variable3\").getValue(), is((Serializable) new Date(349246800000L)));\n    }\n\n    @Test\n    public void api_can_update_variables_and_terminate_activity() throws Exception {\n        final TestHumanTask activity = TestProcessFactory.createActivityWithVariables(getInitiator());\n        final Map<String, String> attributes = new HashMap<>();\n        attributes.put(ActivityItem.ATTRIBUTE_VARIABLES, JSON_UPDATE_VARIABLES);\n        attributes.put(ActivityItem.ATTRIBUTE_STATE, ActivityItem.VALUE_STATE_COMPLETED);\n\n        apiActivity.runUpdate(makeAPIID(activity.getId()), attributes);\n\n        final ArchivedActivityInstance archivedActivityInstance = getArchivedDataInstance(activity);\n        assertThat(archivedActivityInstance.getState(), is(ActivityItem.VALUE_STATE_COMPLETED));\n\n        // Can't manage to do variable verification because of asynchronous engine update ...\n        //        assertThat(getArchivedDataInstanceValue(\"variable1\", archivedActivityInstance), is((Serializable) \"newValue\"));\n        //        assertThat(getArchivedDataInstanceValue(\"variable2\", archivedActivityInstance), is((Serializable) 9L));\n        //        assertThat(getArchivedDataInstanceValue(\"variable3\", archivedActivityInstance), is((Serializable) new Date(349246800000L)));\n    }\n\n    //    private Serializable getArchivedDataInstanceValue(String dataName, ArchivedActivityInstance archivedActivityInstance) throws Exception {\n    //        return getProcessAPI().getArchivedActivityDataInstance(dataName, archivedActivityInstance.getSourceObjectId()).getValue();\n    //    }\n\n    /**\n     * Activity state is updated asynchronously - need to wait... :-(\n     */\n    private ArchivedActivityInstance getArchivedDataInstance(final TestHumanTask activity) throws Exception {\n        if (new WaitUntil(50, 3000) {\n\n            @Override\n            protected boolean check() throws Exception {\n                try {\n                    final ArchivedActivityInstance instance = getProcessAPI()\n                            .getArchivedActivityInstance(activity.getId());\n                    return ActivityItem.VALUE_STATE_COMPLETED.equals(instance.getState());\n                } catch (final ActivityInstanceNotFoundException e) {\n                    return false;\n                }\n            }\n        }.waitUntil()) {\n            return getProcessAPI().getArchivedActivityInstance(activity.getId());\n        } else {\n            throw new Exception(\"can't get archived task\");\n        }\n    }\n\n    private ProcessAPI getProcessAPI() throws Exception {\n        return TenantAPIAccessor.getProcessAPI(getInitiator().getSession());\n    }\n\n    @Test\n    public void api_can_search_with_default_search_order() throws Exception {\n        //given\n        TestProcessFactory.createActivityWithVariables(getInitiator());\n\n        //when\n        final ItemSearchResult<ActivityItem> searchResult = apiActivity.runSearch(0, 1, null,\n                apiActivity.defineDefaultSearchOrder(), null, null, null);\n\n        //then\n        assertThat(searchResult.getResults()).as(\"should be able to search with default search order\").isNotEmpty();\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/flownode/APIHumanTaskIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.awaitility.Awaitility.await;\nimport static org.junit.Assert.*;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\n\nimport org.bonitasoft.test.toolkit.bpm.TestCase;\nimport org.bonitasoft.test.toolkit.bpm.TestHumanTask;\nimport org.bonitasoft.test.toolkit.bpm.TestProcessFactory;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\nimport org.bonitasoft.test.toolkit.organization.TestUserFactory;\nimport org.bonitasoft.web.rest.model.bpm.flownode.HumanTaskItem;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.test.AbstractConsoleTest;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.Test;\n\npublic class APIHumanTaskIT extends AbstractConsoleTest {\n\n    public APIHumanTask apiHumanTask;\n\n    private TestHumanTask testHumanTask;\n\n    /*\n     * (non-Javadoc)\n     * @see org.bonitasoft.console.server.AbstractJUnitWebTest#webTestSetUp()\n     */\n    @Override\n    public void consoleTestSetUp() throws Exception {\n        testHumanTask = TestProcessFactory.getDefaultHumanTaskProcess()\n                .addActor(TestUserFactory.getJohnCarpenter())\n                .startCase()\n                .getNextHumanTask();\n        createAPIHumanTask();\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see org.bonitasoft.test.toolkit.AbstractJUnitTest#getInitiator()\n     */\n    @Override\n    protected TestUser getInitiator() {\n        return TestUserFactory.getJohnCarpenter();\n    }\n\n    @Test\n    public void testGetDatastore() {\n        assertNotNull(\"Is not possible to retrieve the dataStore\", apiHumanTask.getDefaultDatastore());\n    }\n\n    @Test\n    public void testGetHumanTaskItem() {\n        final ArrayList<String> deploys = new ArrayList<>();\n        deploys.add(HumanTaskItem.ATTRIBUTE_PROCESS_ID);\n        final ArrayList<String> counters = new ArrayList<>();\n        final APIID apiId = APIID.makeAPIID(testHumanTask.getId());\n        final HumanTaskItem humanTaskItem = apiHumanTask.runGet(apiId, deploys, counters);\n        assertEquals(\"Not possible to get the APIHUmanTaskItem \", humanTaskItem.getName(), testHumanTask.getName());\n        assertEquals(\"Not possible to get the APIHUmanTaskItem \", humanTaskItem.getDescription(),\n                testHumanTask.getDescription());\n    }\n\n    @Test\n    public void testUpdateHumanTaskItem() {\n\n        final APIID apiId = APIID.makeAPIID(testHumanTask.getId());\n\n        // Update the humanTaskItem attributes\n        final HashMap<String, String> attributes = new HashMap<>();\n        attributes.put(HumanTaskItem.ATTRIBUTE_ASSIGNED_USER_ID,\n                String.valueOf(TestUserFactory.getJohnCarpenter().getId()));\n        apiHumanTask.update(apiId, attributes);\n        final HumanTaskItem updateHumanTaskItem = apiHumanTask.get(apiId);\n        assertNotSame(\"Attributes are not updated\", updateHumanTaskItem.getAssignedId(),\n                TestUserFactory.getJohnCarpenter().getId());\n\n    }\n\n    @Test\n    public void testSearch() {\n        // Set the filters\n        final HashMap<String, String> filters = new HashMap<>();\n        filters.put(HumanTaskItem.ATTRIBUTE_ID, String.valueOf(testHumanTask.getId()));\n\n        // Search the humanTaskItem\n        final ArrayList<String> deploys = new ArrayList<>();\n        deploys.add(HumanTaskItem.ATTRIBUTE_PROCESS_ID);\n        final ArrayList<String> counters = new ArrayList<>();\n        final HumanTaskItem foundHumanTaskItem = apiHumanTask.runSearch(0, 1, null, null, filters, deploys, counters)\n                .getResults().get(0);\n        assertEquals(\"Can't search the humanTaskItem\", testHumanTask.getName(), foundHumanTaskItem.getName());\n    }\n\n    @Test\n    /**\n     * Check that the paging system works fine\n     */\n    public void testHumanTaskItemSearchPaging() throws InterruptedException {\n\n        final long before = apiHumanTask.runSearch(0, 10, null,\n                apiHumanTask.defineDefaultSearchOrder(),\n                new HashMap<>(),\n                new ArrayList<>(), new ArrayList<>()).getTotal();\n\n        // Setup : insert enough tasks to have 2 pages\n        var instances = new ArrayList<TestCase>();\n        for (int i = 0; i < 15; i++) {\n            try {\n                instances.add(TestProcessFactory.getDefaultHumanTaskProcess().startCase());\n            } catch (final Exception e) {\n                fail(\"Can't start process [\" + e.getLocalizedMessage() + \"]\");\n            }\n        }\n        await(\"Wait for the tasks to be created\")\n                .until(() -> instances.stream().allMatch(testCase -> testCase.getNextHumanTask() != null));\n\n        // Setup: retrieve the needed APIs\n        // this.apiHumanTask = new APIHumanTask();\n        // final APIServletCall caller = new APIServletCall(mockHttpServletRequest, mockHttpServletResponse);\n        // this.apiHumanTask.setCaller(caller);\n\n        // Search for page 2 (1 in zero based)\n        final ItemSearchResult<HumanTaskItem> search = apiHumanTask.runSearch(1, 10, null,\n                apiHumanTask.defineDefaultSearchOrder(),\n                new HashMap<>(),\n                new ArrayList<>(), new ArrayList<>());\n\n        assertThat(search.getResults()).hasSizeGreaterThan(2);\n        assertThat(search.getTotal()).isGreaterThan(before);\n    }\n\n    @Test\n    /**\n     * Check when assigned a task to me this task is in available list\n     *\n     * @throws Exception\n     */\n    public void testAssignedTaskInAvailable() {\n        testHumanTask.assignTo(TestUserFactory.getJohnCarpenter());\n\n        final ArrayList<String> deploys = new ArrayList<>();\n        deploys.add(HumanTaskItem.ATTRIBUTE_PROCESS_ID);\n        final ArrayList<String> counters = new ArrayList<>();\n        final HashMap<String, String> filters = new HashMap<>();\n        filters.put(HumanTaskItem.FILTER_USER_ID,\n                String.valueOf(TestUserFactory.getJohnCarpenter().getId()));\n\n        final List<HumanTaskItem> listHumanTaskItem = apiHumanTask\n                .runSearch(0, 1, null, null, filters, deploys, counters).getResults();\n        assertEquals(\"HumanTask assigned to me not in available list\", 1, listHumanTaskItem.size());\n    }\n\n    /**\n     * Initialize APIHumanTask\n     */\n    private void createAPIHumanTask() {\n        apiHumanTask = new APIHumanTask();\n        apiHumanTask.setCaller(getAPICaller(TestUserFactory.getJohnCarpenter().getSession(),\n                \"API/bpm/humanTask\"));\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/flownode/APITaskIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.expression.InvalidExpressionException;\nimport org.bonitasoft.test.toolkit.bpm.TestHumanTask;\nimport org.bonitasoft.test.toolkit.bpm.TestProcess;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\nimport org.bonitasoft.test.toolkit.organization.TestUserFactory;\nimport org.bonitasoft.web.rest.model.bpm.flownode.TaskItem;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.test.AbstractConsoleTest;\nimport org.junit.Test;\n\npublic class APITaskIT extends AbstractConsoleTest {\n\n    private APITask apiTask;\n\n    @Override\n    public void consoleTestSetUp() throws Exception {\n        apiTask = new APITask();\n        apiTask.setCaller(getAPICaller(TestUserFactory.getJohnCarpenter().getSession(), \"API/bpm/task\"));\n    }\n\n    @Override\n    protected TestUser getInitiator() {\n        return TestUserFactory.getJohnCarpenter();\n    }\n\n    private TestHumanTask createActivityWithVariables() throws InvalidExpressionException {\n        final ProcessDefinitionBuilder processDefinitionBuidler = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processName\", \"1.0\");\n        processDefinitionBuidler.addActor(\"Employees\", true)\n                .addDescription(\"This a default process\")\n                .addStartEvent(\"Start\")\n                .addUserTask(\"Activity 1\", \"Employees\")\n\n                .addData(\"variable1\", String.class.getName(),\n                        new ExpressionBuilder().createConstantStringExpression(\"defaultValue\"))\n                .addData(\"variable2\", Long.class.getName(), new ExpressionBuilder().createConstantLongExpression(1))\n                .addData(\"variable3\", Date.class.getName(),\n                        new ExpressionBuilder().createConstantDateExpression(\"428558400000\"))\n\n                .addEndEvent(\"Finish\");\n        return new TestProcess(processDefinitionBuidler).addActor(getInitiator()).setEnable(getInitiator(), true)\n                .startCase().getNextHumanTask()\n                .assignTo(getInitiator());\n    }\n\n    @Test\n    public void api_can_search_with_default_search_order() throws Exception {\n        //given\n        createActivityWithVariables();\n\n        //when\n        final ItemSearchResult<TaskItem> searchResultWithNoOrder = apiTask.runSearch(0, 1, null, null, null, null,\n                null);\n\n        //then\n        assertThat(searchResultWithNoOrder).as(\"should be able to search with default search order\").isNotNull();\n\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/flownode/archive/APIArchivedActivityIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode.archive;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.test.toolkit.bpm.TestProcess;\nimport org.bonitasoft.test.toolkit.bpm.TestProcessFactory;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\nimport org.bonitasoft.test.toolkit.organization.TestUserFactory;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedActivityItem;\nimport org.bonitasoft.web.rest.server.WaitUntil;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.test.AbstractConsoleTest;\nimport org.junit.Assert;\nimport org.junit.Test;\n\npublic class APIArchivedActivityIT extends AbstractConsoleTest {\n\n    private APIArchivedActivity apiArchivedActivity;\n\n    @Override\n    public void consoleTestSetUp() throws Exception {\n        apiArchivedActivity = new APIArchivedActivity();\n        apiArchivedActivity.setCaller(getAPICaller(getInitiator().getSession(), \"API/bpm/archivedActivity\"));\n    }\n\n    @Override\n    protected TestUser getInitiator() {\n        return TestUserFactory.getJohnCarpenter();\n    }\n\n    private HumanTaskInstance initArchivedHumanTaskInstance() throws Exception {\n        final TestProcess defaultHumanTaskProcess = TestProcessFactory.getDefaultHumanTaskProcess();\n        defaultHumanTaskProcess.addActor(getInitiator());\n        final ProcessInstance processInstance = defaultHumanTaskProcess.startCase(getInitiator()).getProcessInstance();\n\n        waitPendingHumanTask();\n\n        // Retrieve a humanTaskInstance\n        final HumanTaskInstance humanTaskInstance = getProcessAPI()\n                .getPendingHumanTaskInstances(getInitiator().getId(), 0, 10, null).get(0);\n        getProcessAPI().assignUserTask(humanTaskInstance.getId(), getInitiator().getId());\n\n        waitAssignedHumanTask();\n\n        getProcessAPI().executeFlowNode(humanTaskInstance.getId());\n\n        waitArchivedActivityInstance(processInstance.getId());\n\n        return humanTaskInstance;\n    }\n\n    private ProcessAPI getProcessAPI() throws Exception {\n        return TenantAPIAccessor.getProcessAPI(getInitiator().getSession());\n    }\n\n    /**\n     * Wait the process contain PendingHumanTaskInstance\n     */\n    private void waitPendingHumanTask() throws Exception {\n        Assert.assertTrue(\"no pending task instances are found\", new WaitUntil(50, 3000) {\n\n            @Override\n            protected boolean check() throws Exception {\n                return getProcessAPI().getPendingHumanTaskInstances(getInitiator().getId(), 0, 10, null).size() >= 1;\n            }\n        }.waitUntil());\n    }\n\n    private void waitAssignedHumanTask() throws Exception {\n        Assert.assertTrue(\"Human task hasnt been assign\", new WaitUntil(50, 3000) {\n\n            @Override\n            protected boolean check() throws Exception {\n                return getProcessAPI().getAssignedHumanTaskInstances(getInitiator().getId(), 0, 10,\n                        ActivityInstanceCriterion.DEFAULT).size() >= 1;\n            }\n        }.waitUntil());\n    }\n\n    /**\n     * Wait the process contain ArchivedHumanTaskInstance\n     */\n    private void waitArchivedActivityInstance(final long processInstanceId) throws Exception {\n        Assert.assertTrue(\"no archived task instances are found\", new WaitUntil(50, 3000) {\n\n            @Override\n            protected boolean check() throws Exception {\n                final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n                searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID,\n                        processInstanceId);\n                return getProcessAPI().searchArchivedActivities(searchOptionsBuilder.done()).getCount() >= 1L;\n            }\n        }.waitUntil());\n    }\n\n    @Test\n    public void testSearchWithDefaultOrder() throws Exception {\n        verifySearhWithOrder(apiArchivedActivity.defineDefaultSearchOrder());\n    }\n\n    @Test\n    public void testSearchWithNoOrder() throws Exception {\n        verifySearhWithOrder(null);\n\n    }\n\n    private void verifySearhWithOrder(final String order) throws Exception {\n        //given\n        initArchivedHumanTaskInstance();\n\n        //when\n        final ItemSearchResult<ArchivedActivityItem> search = apiArchivedActivity.runSearch(0, 1, null, order, null,\n                null, null);\n\n        //then\n        assertThat(search.getResults()).as(\"should get results\").isNotEmpty();\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/flownode/archive/APIArchivedHumanTaskIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode.archive;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.web.toolkit.client.data.APIID.makeAPIID;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.test.toolkit.bpm.TestProcess;\nimport org.bonitasoft.test.toolkit.bpm.TestProcessFactory;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\nimport org.bonitasoft.test.toolkit.organization.TestUserFactory;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedHumanTaskItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.HumanTaskItem;\nimport org.bonitasoft.web.rest.server.WaitUntil;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.test.AbstractConsoleTest;\nimport org.junit.Assert;\nimport org.junit.Test;\n\npublic class APIArchivedHumanTaskIT extends AbstractConsoleTest {\n\n    private APIArchivedHumanTask apiArchivedHumanTask;\n\n    @Override\n    public void consoleTestSetUp() throws Exception {\n        apiArchivedHumanTask = new APIArchivedHumanTask();\n        apiArchivedHumanTask.setCaller(getAPICaller(getInitiator().getSession(), \"API/bpm/archivedHumanTask\"));\n    }\n\n    @Override\n    protected TestUser getInitiator() {\n        return TestUserFactory.getJohnCarpenter();\n    }\n\n    private HumanTaskInstance initArchivedHumanTaskInstance() throws Exception {\n        final TestProcess defaultHumanTaskProcess = TestProcessFactory.getDefaultHumanTaskProcess();\n        defaultHumanTaskProcess.addActor(getInitiator());\n        final ProcessInstance processInstance = defaultHumanTaskProcess.startCase(getInitiator()).getProcessInstance();\n\n        waitPendingHumanTask(processInstance.getId());\n\n        // Retrieve a humanTaskInstance\n        final HumanTaskInstance humanTaskInstance = getProcessAPI()\n                .getPendingHumanTaskInstances(getInitiator().getId(), 0, 10, null).get(0);\n        getProcessAPI().assignUserTask(humanTaskInstance.getId(), getInitiator().getId());\n\n        waitAssignedHumanTask();\n\n        getProcessAPI().executeFlowNode(humanTaskInstance.getId());\n\n        waitArchivedActivityInstance(processInstance.getId());\n\n        return humanTaskInstance;\n    }\n\n    private ProcessAPI getProcessAPI() throws Exception {\n        return TenantAPIAccessor.getProcessAPI(getInitiator().getSession());\n    }\n\n    private ArrayList<String> getProcessIdDeploy() {\n        final ArrayList<String> deploys = new ArrayList<>();\n        deploys.add(HumanTaskItem.ATTRIBUTE_PROCESS_ID);\n        return deploys;\n    }\n\n    private HashMap<String, String> getNameFilter(final HumanTaskInstance humanTaskInstance) {\n        final HashMap<String, String> filters = new HashMap<>();\n        filters.put(ArchivedHumanTaskItem.ATTRIBUTE_NAME, humanTaskInstance.getName());\n        return filters;\n    }\n\n    /**\n     * Wait the process contain PendingHumanTaskInstance\n     */\n    private void waitPendingHumanTask(final long processInstanceId) throws Exception {\n\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.PROCESS_INSTANCE_ID, processInstanceId);\n\n        Assert.assertTrue(\"no pending task instances are found\", new WaitUntil(50, 3000) {\n\n            @Override\n            protected boolean check() throws Exception {\n                return getProcessAPI().searchPendingTasksForUser(getInitiator().getId(), searchOptionsBuilder.done())\n                        .getCount() >= 1;\n            }\n        }.waitUntil());\n    }\n\n    private void waitAssignedHumanTask() throws Exception {\n        Assert.assertTrue(\"Human task hasnt been assign\", new WaitUntil(50, 3000) {\n\n            @Override\n            protected boolean check() throws Exception {\n                return getProcessAPI().getAssignedHumanTaskInstances(getInitiator().getId(), 0, 10,\n                        ActivityInstanceCriterion.DEFAULT).size() >= 1;\n            }\n        }.waitUntil());\n    }\n\n    /**\n     * Wait the process contain ArchivedHumanTaskInstance\n     */\n    private void waitArchivedActivityInstance(final long processInstanceId) throws Exception {\n        Assert.assertTrue(\"no archived task instances are found\", new WaitUntil(50, 3000) {\n\n            @Override\n            protected boolean check() throws Exception {\n                final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n                searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID,\n                        processInstanceId);\n                return getProcessAPI().searchArchivedActivities(searchOptionsBuilder.done()).getCount() >= 1L;\n            }\n        }.waitUntil());\n    }\n\n    @Test\n    public void testGetArchivedHumanTask() throws Exception {\n        final HumanTaskInstance humanTaskInstance = initArchivedHumanTaskInstance();\n        final ArrayList<String> deploys = getProcessIdDeploy();\n\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID,\n                humanTaskInstance.getRootContainerId());\n        final ArchivedActivityInstance archivedActivityInstance = getProcessAPI()\n                .searchArchivedActivities(searchOptionsBuilder.done()).getResult().get(0);\n\n        final ArchivedHumanTaskItem archivedHumanTaskItem = apiArchivedHumanTask\n                .runGet(makeAPIID(archivedActivityInstance.getId()), deploys, new ArrayList<>());\n\n        assertEquals(\"Can't get the good archivedTaskItem\", archivedHumanTaskItem.getName(),\n                humanTaskInstance.getName());\n    }\n\n    @Test\n    public void testSearchArchivedHumanTask() throws Exception {\n        final HumanTaskInstance humanTaskInstance = initArchivedHumanTaskInstance();\n        final ArrayList<String> deploys = getProcessIdDeploy();\n        final HashMap<String, String> filters = getNameFilter(humanTaskInstance);\n\n        final ArchivedHumanTaskItem archivedHumanTaskItem = apiArchivedHumanTask.runSearch(0, 1, null, null,\n                filters, deploys, new ArrayList<>()).getResults().get(0);\n\n        assertNotNull(\"Can't find the good archivedTaskItem\", archivedHumanTaskItem);\n    }\n\n    @Test\n    public void testGetDatastore() {\n        assertNotNull(\"Can't get the Datastore\", apiArchivedHumanTask.getDefaultDatastore());\n    }\n\n    @Test\n    public void archivedHumanTasksCanBeSortedByReachedStateDate() throws Exception {\n        shouldSearchArchivedHumaTaskWithOrder(ArchivedHumanTaskItem.ATTRIBUTE_REACHED_STATE_DATE + \" DESC\");\n    }\n\n    @Test\n    public void testSearchWithDefaultOrder() throws Exception {\n        shouldSearchArchivedHumaTaskWithOrder(apiArchivedHumanTask.defineDefaultSearchOrder());\n\n    }\n\n    private void shouldSearchArchivedHumaTaskWithOrder(final String orders) throws Exception {\n        //given\n        final HumanTaskInstance humanTaskInstance = initArchivedHumanTaskInstance();\n        final HashMap<String, String> filters = getNameFilter(humanTaskInstance);\n\n        //when\n        final ItemSearchResult<ArchivedHumanTaskItem> search = apiArchivedHumanTask.runSearch(0, 1, null, orders,\n                filters, null, null);\n\n        //then\n        assertThat(search.getResults()).as(\"should get results\").isNotEmpty();\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/process/APIActorIntegrationIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.process;\n\nimport static java.util.Arrays.asList;\nimport static org.bonitasoft.web.rest.model.bpm.process.ActorItem.ATTRIBUTE_DESCRIPTION;\nimport static org.bonitasoft.web.rest.model.bpm.process.ActorItem.ATTRIBUTE_PROCESS_ID;\nimport static org.bonitasoft.web.rest.model.bpm.process.ActorItem.COUNTER_GROUPS;\nimport static org.bonitasoft.web.rest.model.bpm.process.ActorItem.COUNTER_MEMBERSHIPS;\nimport static org.bonitasoft.web.rest.model.bpm.process.ActorItem.COUNTER_ROLES;\nimport static org.bonitasoft.web.rest.model.bpm.process.ActorItem.COUNTER_USERS;\nimport static org.bonitasoft.web.rest.model.builder.bpm.process.ActorItemBuilder.anActorItem;\nimport static org.bonitasoft.web.toolkit.client.data.APIID.makeAPIID;\nimport static org.bonitasoft.web.toolkit.client.data.item.template.ItemHasDualName.ATTRIBUTE_DISPLAY_NAME;\nimport static org.bonitasoft.web.toolkit.client.data.item.template.ItemHasDualName.ATTRIBUTE_NAME;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertTrue;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.actor.ActorInstance;\nimport org.bonitasoft.test.toolkit.bpm.TestProcess;\nimport org.bonitasoft.test.toolkit.bpm.TestProcessFactory;\nimport org.bonitasoft.test.toolkit.bpm.process.TestActorMemberFactory;\nimport org.bonitasoft.test.toolkit.organization.TestGroupFactory;\nimport org.bonitasoft.test.toolkit.organization.TestRoleFactory;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\nimport org.bonitasoft.test.toolkit.organization.TestUserFactory;\nimport org.bonitasoft.web.rest.model.bpm.process.ActorItem;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.test.AbstractConsoleTest;\nimport org.junit.Test;\n\n/**\n * @author Colin PUY\n */\npublic class APIActorIntegrationIT extends AbstractConsoleTest {\n\n    private APIActor apiActor;\n\n    @Override\n    public void consoleTestSetUp() throws Exception {\n        apiActor = new APIActor();\n        apiActor.setCaller(getAPICaller(getInitiator().getSession(), \"API/bpm/actor\"));\n    }\n\n    @Override\n    protected TestUser getInitiator() {\n        return TestUserFactory.getJohnCarpenter();\n    }\n\n    private ActorItem getFromEngine(final long actorId) throws Exception {\n        return anActorItem().fromActorInstance(getProcessAPI().getActor(actorId)).build();\n    }\n\n    private ProcessAPI getProcessAPI() throws Exception {\n        return TenantAPIAccessor.getProcessAPI(getInitiator().getSession());\n    }\n\n    @Test\n    public void testGet() throws Exception {\n        final ActorInstance actor = TestProcessFactory.getDefaultHumanTaskProcess()\n                .addActor(TestUserFactory.getJohnCarpenter()).getActors().get(0);\n\n        final ActorItem fetchedActorItem = apiActor.runGet(makeAPIID(actor.getId()), null, null);\n\n        assertItemEquals(getFromEngine(actor.getId()), fetchedActorItem);\n    }\n\n    @Test\n    public void testGetWithDeploys() {\n        final TestProcess process = TestProcessFactory.getDefaultHumanTaskProcess()\n                .addActor(TestUserFactory.getJohnCarpenter());\n        final long actorId = process.getActors().get(0).getId();\n\n        final ActorItem fetchedActorItem = apiActor.runGet(makeAPIID(actorId), List.of(ATTRIBUTE_PROCESS_ID), null);\n\n        assertNotNull(fetchedActorItem.getProcess());\n        assertEquals(fetchedActorItem.getProcess().getId(), process.getId());\n    }\n\n    @Test\n    public void testGetWithCounters() {\n        //given\n        final TestUser johnCarpenter = TestUserFactory.getJohnCarpenter();\n        final TestProcess process = TestProcessFactory.getDefaultHumanTaskProcess().addActor(johnCarpenter);\n        final long actorId = process.getActors().get(0).getId();\n\n        //when\n        final List<String> counters = asList(COUNTER_USERS, COUNTER_GROUPS, COUNTER_ROLES, COUNTER_MEMBERSHIPS);\n        final ActorItem fetchedActorItem = apiActor.runGet(makeAPIID(actorId), null, counters);\n\n        //then\n        assertEquals(1L, (long) fetchedActorItem.getNbSelectedUsers());\n        assertEquals(0L, (long) fetchedActorItem.getNbSelectedGroups());\n        assertEquals(0L, (long) fetchedActorItem.getNbSelectedRoles());\n        assertEquals(0L, (long) fetchedActorItem.getNbSelectedMembershipss());\n    }\n\n    @Test\n    public void getCanCountNumberOfMembershipForActor() throws Exception {\n        final ActorInstance actor = TestProcessFactory.getDefaultHumanTaskProcess()\n                .addActor(TestUserFactory.getJohnCarpenter()).getActors().get(0);\n        TestActorMemberFactory.createMembershipActorMember(actor.getId(), TestGroupFactory.getRAndD(),\n                TestRoleFactory.getDeveloper());\n        final List<String> counters = List.of(COUNTER_MEMBERSHIPS);\n\n        final ActorItem fetchedActorItem = apiActor.runGet(makeAPIID(actor.getId()), null, counters);\n\n        assertEquals(1L, (long) fetchedActorItem.getNbSelectedMembershipss());\n    }\n\n    @Test\n    public void testUpdate() throws Exception {\n        final TestProcess process = TestProcessFactory.getDefaultHumanTaskProcess()\n                .addActor(TestUserFactory.getJohnCarpenter());\n        final Map<String, String> attributes = buildUpdateAttributes(\"newDescription\", \"newDisplayName\");\n        final long actorId = process.getActors().get(0).getId();\n\n        final ActorItem updatedItem = apiActor.runUpdate(makeAPIID(actorId), attributes);\n\n        assertItemEquals(getFromEngine(actorId), updatedItem);\n        assertEquals(\"newDisplayName\", updatedItem.getDisplayName());\n        assertEquals(\"newDescription\", updatedItem.getDescription());\n    }\n\n    private Map<String, String> buildUpdateAttributes(final String description, final String displayName) {\n        return Map.of(ATTRIBUTE_DESCRIPTION, description, ATTRIBUTE_DISPLAY_NAME, displayName);\n    }\n\n    @Test\n    public void testSearchCanBePaginatedAndOrdered() {\n        final TestProcess process = TestProcessFactory.createProcessWith3Actors()\n                .addActor(TestUserFactory.getJohnCarpenter())\n                .addActor(TestGroupFactory.getRAndD())\n                .addActor(TestRoleFactory.getDeveloper());\n        final HashMap<String, String> filters = new HashMap<>();\n        filters.put(ATTRIBUTE_PROCESS_ID, String.valueOf(process.getId()));\n        final String order = ATTRIBUTE_NAME + \" ASC\";\n\n        final ItemSearchResult<ActorItem> searchResult = apiActor.search(0, 2, null, order, filters);\n\n        assertEquals(3L, searchResult.getTotal());\n        assertEquals(2, searchResult.getResults().size());\n        final String result1Name = searchResult.getResults().get(0).getName();\n        final String result2Name = searchResult.getResults().get(1).getName();\n        assertTrue(isAscendantOrder(result1Name, result2Name));\n    }\n\n    private boolean isAscendantOrder(final String name1, final String name2) {\n        return name1.compareTo(name2) < 0;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/process/APIActorMemberIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.process;\n\nimport static java.util.Arrays.asList;\nimport static java.util.Collections.singletonList;\nimport static org.bonitasoft.web.rest.model.bpm.process.ActorMemberItem.ATTRIBUTE_ACTOR_ID;\nimport static org.bonitasoft.web.rest.model.builder.bpm.process.ActorMemberItemBuilder.anActorMemberItem;\nimport static org.bonitasoft.web.rest.model.identity.MemberType.GROUP;\nimport static org.bonitasoft.web.rest.model.portal.profile.AbstractMemberItem.FILTER_MEMBER_TYPE;\nimport static org.bonitasoft.web.toolkit.client.data.APIID.makeAPIID;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertNull;\n\nimport java.util.HashMap;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.actor.ActorInstance;\nimport org.bonitasoft.engine.bpm.actor.ActorMember;\nimport org.bonitasoft.engine.bpm.actor.ActorNotFoundException;\nimport org.bonitasoft.test.toolkit.bpm.TestProcess;\nimport org.bonitasoft.test.toolkit.bpm.TestProcessFactory;\nimport org.bonitasoft.test.toolkit.bpm.process.TestActorMemberFactory;\nimport org.bonitasoft.test.toolkit.organization.TestGroupFactory;\nimport org.bonitasoft.test.toolkit.organization.TestRoleFactory;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\nimport org.bonitasoft.test.toolkit.organization.TestUserFactory;\nimport org.bonitasoft.web.rest.model.bpm.process.ActorMemberItem;\nimport org.bonitasoft.web.rest.model.identity.MemberType;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.test.AbstractConsoleTest;\nimport org.junit.Test;\n\n/**\n * @author Colin PUY\n */\npublic class APIActorMemberIT extends AbstractConsoleTest {\n\n    private APIActorMember apiActorMember;\n\n    @Override\n    public void consoleTestSetUp() throws Exception {\n        apiActorMember = new APIActorMember();\n        apiActorMember.setCaller(getAPICaller(getInitiator().getSession(), \"API/bpm/actorMember\"));\n    }\n\n    @Override\n    protected TestUser getInitiator() {\n        return TestUserFactory.getJohnCarpenter();\n    }\n\n    private ActorMemberItem fetchUserActorMember(final long actorId, final long userId) throws Exception {\n        try {\n            final List<ActorMember> actorMembers = getProcessAPI().getActorMembers(actorId, 0, Integer.MAX_VALUE);\n            for (final ActorMember actorMember : actorMembers) {\n                if (actorMember.getUserId() == userId) {\n                    return anActorMemberItem().fromActorMember(actorMember, actorId).build();\n                }\n            }\n        } catch (final ActorNotFoundException e) {\n            return null;\n        }\n        return null;\n    }\n\n    private ProcessAPI getProcessAPI() throws Exception {\n        return TenantAPIAccessor.getProcessAPI(getInitiator().getSession());\n    }\n\n    private HashMap<String, String> buildActorIdFilter(final long actorId) {\n        final HashMap<String, String> filters = new HashMap<>();\n        filters.put(ATTRIBUTE_ACTOR_ID, String.valueOf(actorId));\n        return filters;\n    }\n\n    private HashMap<String, String> buildMemberTypeFilter(final long actorId, final MemberType memberType) {\n        final HashMap<String, String> filters = buildActorIdFilter(actorId);\n        filters.put(FILTER_MEMBER_TYPE, memberType.name());\n        return filters;\n    }\n\n    @Test\n    public void testAdd() throws Exception {\n        final TestProcess process = TestProcessFactory.getRandomHumanTaskProcess()\n                .addActor(TestUserFactory.getMrSpechar());\n        final long actorId = process.getActors().get(0).getId();\n        final long userId = getInitiator().getId();\n\n        final ActorMemberItem addedItem = apiActorMember\n                .runAdd(anActorMemberItem().withActorId(actorId).withuserId(userId).build());\n\n        final ActorMemberItem expectedItem = fetchUserActorMember(actorId, userId);\n        assertItemEquals(expectedItem, addedItem);\n    }\n\n    @Test\n    public void testDelete() throws Exception {\n        final TestUser aUser = getInitiator();\n        final ActorInstance actor = TestProcessFactory.getRandomHumanTaskProcess()\n                .addActor(TestUserFactory.getMrSpechar()).getActors().get(0);\n        final ActorMember addedMember = TestActorMemberFactory.createUserActorMember(actor.getId(), aUser);\n\n        apiActorMember.runDelete(asList(makeAPIID(addedMember.getId())));\n\n        assertNull(fetchUserActorMember(actor.getId(), aUser.getId()));\n    }\n\n    @Test\n    public void searchCanBeFilteredByActorIdAndMemberType() throws Exception {\n        final ActorInstance actor = TestProcessFactory.getRandomHumanTaskProcess()\n                .addActor(TestUserFactory.getMrSpechar()).getActors().get(0);\n        TestActorMemberFactory.createUserActorMember(actor.getId(), getInitiator());\n        final ActorMember addedMember = TestActorMemberFactory.createGroupActorMember(actor.getId(),\n                TestGroupFactory.getRAndD());\n        final HashMap<String, String> filter = buildMemberTypeFilter(actor.getId(), GROUP);\n\n        final ItemSearchResult<ActorMemberItem> searchResult = apiActorMember.runSearch(0, 10, null, null, filter, null,\n                null);\n\n        assertEquals(1L, searchResult.getTotal());\n        assertItemEquals(anActorMemberItem().fromActorMember(addedMember, actor.getId()).build(),\n                searchResult.getResults().get(0));\n    }\n\n    @Test\n    public void searchCanBeFilteredByActorIdAndMemberTypeMemberShip() throws Exception {\n        final ActorInstance actor = TestProcessFactory.getRandomHumanTaskProcess()\n                .addActor(TestUserFactory.getMrSpechar()).getActors().get(0);\n        final ActorMember addedMember = TestActorMemberFactory.createMembershipActorMember(actor.getId(),\n                TestGroupFactory.getWeb(),\n                TestRoleFactory.getManager());\n        final HashMap<String, String> filters = buildMemberTypeFilter(actor.getId(), MemberType.MEMBERSHIP);\n\n        final ItemSearchResult<ActorMemberItem> searchResult = apiActorMember.runSearch(0, 10, null, null, filters,\n                null, null);\n\n        assertEquals(1L, searchResult.getTotal());\n        assertItemEquals(anActorMemberItem().fromActorMember(addedMember, actor.getId()).build(),\n                searchResult.getResults().get(0));\n    }\n\n    @Test\n    public void testSearchCanBePaginated() throws Exception {\n        final TestProcess process = TestProcessFactory.getRandomHumanTaskProcess()\n                .addActor(TestUserFactory.getMrSpechar());\n        final long actorId = process.getActors().get(0).getId();\n        TestActorMemberFactory.createUserActorMember(actorId, getInitiator());\n        TestActorMemberFactory.createGroupActorMember(actorId, TestGroupFactory.getWeb());\n\n        final ItemSearchResult<ActorMemberItem> searchResult = apiActorMember.runSearch(1, 1, null, null,\n                buildActorIdFilter(actorId), null, null);\n\n        // when adding actor to process, it seems to create automaticaly an actorMember for this user\n        // so total is 3 even we add only 2 ActorMember\n        assertEquals(3L, searchResult.getTotal());\n        assertEquals(1, searchResult.getResults().size());\n    }\n\n    @Test\n    public void testSearchWithDeploy() throws Exception {\n        final ActorInstance actor = TestProcessFactory.getRandomHumanTaskProcess()\n                .addActor(TestUserFactory.getMrSpechar()).getActors().get(0);\n        TestActorMemberFactory.createUserActorMember(actor.getId(), getInitiator());\n        final HashMap<String, String> filters = buildActorIdFilter(actor.getId());\n        final List<String> deploy = singletonList(ATTRIBUTE_ACTOR_ID);\n\n        final ItemSearchResult<ActorMemberItem> searchResult = apiActorMember.runSearch(0, 1, null, null, filters,\n                deploy, null);\n\n        assertNotNull(searchResult.getResults().get(0).getActor());\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/process/APICategoryIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.process;\n\nimport static junit.framework.Assert.assertNull;\nimport static org.bonitasoft.web.rest.model.builder.bpm.process.CategoryItemBuilder.aCategoryItem;\nimport static org.bonitasoft.web.toolkit.client.data.APIID.makeAPIID;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport junit.framework.Assert;\nimport org.bonitasoft.engine.bpm.category.Category;\nimport org.bonitasoft.test.toolkit.bpm.TestCategory;\nimport org.bonitasoft.test.toolkit.bpm.TestCategoryFactory;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\nimport org.bonitasoft.test.toolkit.organization.TestUserFactory;\nimport org.bonitasoft.web.rest.model.bpm.process.CategoryItem;\nimport org.bonitasoft.web.rest.server.datastore.bpm.process.CategoryDatastore;\nimport org.bonitasoft.web.test.AbstractConsoleTest;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIForbiddenException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.After;\nimport org.junit.Test;\n\n/**\n * @author Nicolas Tith\n */\npublic class APICategoryIT extends AbstractConsoleTest {\n\n    private APICategory api;\n\n    @Override\n    public void consoleTestSetUp() throws Exception {\n        api = new APICategory();\n        api.setCaller(getAPICaller(getInitiator().getSession(), \"API/bpm/category\"));\n    }\n\n    @Override\n    protected TestUser getInitiator() {\n        return TestUserFactory.getRidleyScott();\n    }\n\n    @After\n    public void cleanCategoriesInDB() {\n        final List<TestCategory> allCategories = TestCategoryFactory.getAllCategories(getInitiator().getSession());\n        for (final TestCategory category : allCategories) {\n            TestCategoryFactory.removeTestCategoryFromList(category);\n            category.delete();\n        }\n    }\n\n    /**\n     * Fetch a Category by id from engine\n     *\n     * @return the category or null if not found\n     */\n    private CategoryItem getFromEngine(final long categoryId) {\n        try {\n            return new CategoryDatastore(getInitiator().getSession()).get(makeAPIID(categoryId));\n        } catch (final APIException e) {\n            if (e instanceof APIItemNotFoundException) {\n                return null;\n            }\n            throw e;\n        }\n    }\n\n    @Test\n    public void testSearchCategoryItem() {\n        final List<TestCategory> catList = TestCategoryFactory.getCategories(3);\n\n        // Search the CommentItem\n        final List<CategoryItem> actualCatList = api.runSearch(0, 10, null, null, new HashMap<>(), new ArrayList<>(),\n                new ArrayList<>()).getResults();\n        Assert.assertNotNull(\"Categories not found\", actualCatList);\n        Assert.assertTrue(catList.size() == 3);\n    }\n\n    @Test\n    public void addCategoryTest() {\n        //before\n\n        // API call\n        final CategoryItem categoryItem = new CategoryItem();\n        categoryItem.setName(\"categoryTest\");\n        categoryItem.setDescription(\"categoryDescription\");\n        api.runAdd(categoryItem);\n\n        // Check\n        final List<TestCategory> catList = TestCategoryFactory.getAllCategories(getInitiator().getSession());\n        final int nbOfCategories = catList.size();\n        String message = \"No categories added. \" + nbOfCategories + \" categories found. Categories are: \\n\";\n        for (final TestCategory testCategory : catList) {\n            message += \" catgeory with id \" + testCategory.getId() + \": \" + testCategory.getCategory().getName() + \"\\n\";\n        }\n        Assert.assertEquals(message, 1, nbOfCategories);\n        final Category resultCategory = catList.get(0).getCategory();\n        Assert.assertEquals(\"Wrong category found (not same name)\", categoryItem.getName(), resultCategory.getName());\n        Assert.assertEquals(\"Wrong category found (not same description)\", categoryItem.getDescription(),\n                resultCategory.getDescription());\n\n    }\n\n    @Test\n    public void updateCategoryTest() {\n        final String newDescription = \"Lorem ipsum dolor sit amet\";\n\n        // Init\n        final TestCategory category = TestCategoryFactory.getRandomCategory();\n\n        // Update\n        final Map<String, String> updates = new HashMap<>();\n        updates.put(CategoryItem.ATTRIBUTE_DESCRIPTION, newDescription);\n        api.runUpdate(APIID.makeAPIID(category.getCategory().getId()), updates);\n\n        // Get\n        final CategoryItem output = api.runGet(APIID.makeAPIID(category.getCategory().getId()), new ArrayList<>(),\n                new ArrayList<>());\n\n        Assert.assertNotNull(\"Category not found\", output);\n        Assert.assertEquals(\"Update of category failed\", newDescription, output.getDescription());\n\n    }\n\n    @Test\n    public void getCategoryTest() {\n        // Init\n        final TestCategory category = TestCategoryFactory.getRandomCategory();\n\n        // API Call\n        final CategoryItem catItem = api.runGet(APIID.makeAPIID(category.getId()), new ArrayList<>(),\n                new ArrayList<>());\n\n        Assert.assertNotNull(\"Category not found\", category);\n        Assert.assertEquals(\"Wrong category description found\", category.getCategory().getDescription(),\n                catItem.getDescription());\n        Assert.assertEquals(\"Wrong category found\", category.getCategory().getName(), catItem.getName());\n    }\n\n    @Test\n    public void deleteCategoryTest() {\n        final TestCategory category = TestCategoryFactory.getRandomCategory();\n\n        api.runDelete(List.of(makeAPIID(category.getId())));\n\n        assertNull(getFromEngine(category.getId()));\n\n        TestCategoryFactory.removeTestCategoryFromList(category);\n    }\n\n    @Test(expected = APIForbiddenException.class)\n    public void addingTwiceSameCategoryIsForbidden() {\n        //given\n        final CategoryItem categoryItem = aCategoryItem().build();\n\n        //when then exception\n        api.runAdd(categoryItem);\n        api.runAdd(categoryItem);\n\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/process/APIProcessCategoryIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.process;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport junit.framework.Assert;\nimport org.bonitasoft.engine.bpm.category.Category;\nimport org.bonitasoft.test.toolkit.bpm.TestCategory;\nimport org.bonitasoft.test.toolkit.bpm.TestCategoryFactory;\nimport org.bonitasoft.test.toolkit.bpm.TestProcess;\nimport org.bonitasoft.test.toolkit.bpm.TestProcessFactory;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\nimport org.bonitasoft.test.toolkit.organization.TestUserFactory;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessCategoryItem;\nimport org.bonitasoft.web.test.AbstractConsoleTest;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.Test;\n\n/**\n * @author Séverin Moussel\n */\npublic class APIProcessCategoryIT extends AbstractConsoleTest {\n\n    private APIProcessCategory api;\n\n    @Override\n    public void consoleTestSetUp() throws Exception {\n        this.api = new APIProcessCategory();\n        this.api.setCaller(getAPICaller(getInitiator().getSession(),\n                \"API/bpm/processCategory\"));\n    }\n\n    @Override\n    protected TestUser getInitiator() {\n        return TestUserFactory.getJohnCarpenter();\n    }\n\n    @Test\n    public void addProcessCategoryTest() {\n        // Init\n        final TestProcess process = TestProcessFactory.getRandomHumanTaskProcess();\n        final TestCategory category = TestCategoryFactory.getRandomCategory();\n\n        // API call\n        final ProcessCategoryItem processCategory = new ProcessCategoryItem();\n        processCategory.setProcessId(process.getId());\n        processCategory.setCategoryId(category.getId());\n\n        this.api.runAdd(processCategory);\n\n        // Check\n        final List<TestCategory> categories = process.getCategories();\n        Assert.assertEquals(\"No categories added\", 1, categories.size());\n\n        final Category resultCategory = categories.get(0).getCategory();\n        Assert.assertEquals(\"Wrong category found\", category.getCategory().getName(), resultCategory.getName());\n        Assert.assertEquals(\"Wrong category found\", category.getCategory().getDescription(),\n                resultCategory.getDescription());\n    }\n\n    @Test\n    public void deleteProcessCategoryTest() {\n        // Init\n        final TestProcess process = TestProcessFactory.getRandomHumanTaskProcess();\n        final TestCategory category = TestCategoryFactory.getRandomCategory();\n\n        process.addCategory(category.getId());\n\n        // API call\n        this.api.runDelete(Arrays.asList(APIID.makeAPIID(process.getId(), category.getId())));\n\n        // Check\n        final List<TestCategory> categories = process.getCategories();\n        Assert.assertEquals(\"No categories deleted\", 0, categories.size());\n\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/process/APIProcessConnectorDependencyIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.process;\n\nimport static java.util.Arrays.asList;\nimport static junit.framework.Assert.assertEquals;\nimport static org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDependencyItem.ATTRIBUTE_CONNECTOR_NAME;\nimport static org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDependencyItem.ATTRIBUTE_CONNECTOR_VERSION;\nimport static org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDependencyItem.ATTRIBUTE_PROCESS_ID;\nimport static org.junit.Assert.assertNotNull;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.test.toolkit.bpm.TestProcess;\nimport org.bonitasoft.test.toolkit.bpm.TestProcessFactory;\nimport org.bonitasoft.test.toolkit.bpm.process.TestProcessConnector;\nimport org.bonitasoft.test.toolkit.bpm.process.TestProcessConnectorFactory;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\nimport org.bonitasoft.test.toolkit.organization.TestUserFactory;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDependencyItem;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.test.AbstractConsoleTest;\nimport org.junit.Test;\n\n/**\n * @author Colin PUY\n */\npublic class APIProcessConnectorDependencyIT extends AbstractConsoleTest {\n\n    private APIProcessConnectorDependency apiProcessConnectorDependency;\n\n    @Override\n    public void consoleTestSetUp() throws Exception {\n        apiProcessConnectorDependency = new APIProcessConnectorDependency();\n        apiProcessConnectorDependency\n                .setCaller(getAPICaller(getInitiator().getSession(), \"API/bpm/processConnectorDependency\"));\n    }\n\n    @Override\n    protected TestUser getInitiator() {\n        return TestUserFactory.getJohnCarpenter();\n    }\n\n    private Map<String, String> buildFilters(long processId, String connectorId, String connectorVersion) {\n        Map<String, String> filters = new HashMap<>();\n        filters.put(ATTRIBUTE_PROCESS_ID, String.valueOf(processId));\n        filters.put(ATTRIBUTE_CONNECTOR_NAME, connectorId);\n        filters.put(ATTRIBUTE_CONNECTOR_VERSION, connectorVersion);\n        return filters;\n    }\n\n    @Test\n    public void testSearch() throws Exception {\n        TestProcessConnector defaultConnector = TestProcessConnectorFactory.getDefaultConnector();\n        TestProcess processWithConnector = TestProcessFactory.createProcessWithConnector(defaultConnector);\n        Map<String, String> filters = buildFilters(processWithConnector.getId(), defaultConnector.getId(),\n                defaultConnector.getVersion());\n\n        ItemSearchResult<ProcessConnectorDependencyItem> search = apiProcessConnectorDependency.runSearch(0, 10, \"\",\n                null, filters, null, null);\n\n        assertEquals(defaultConnector.getDependencies().get(0), search.getResults().get(0).getFilename());\n        assertEquals(defaultConnector.getDependencies().get(1), search.getResults().get(1).getFilename());\n        assertEquals(defaultConnector.getDependencies().size(), search.getTotal());\n    }\n\n    @Test\n    public void testSearchWithDeploys() throws Exception {\n        TestProcessConnector defaultConnector = TestProcessConnectorFactory.getDefaultConnector();\n        TestProcess processWithConnector = TestProcessFactory.createProcessWithConnector(defaultConnector);\n        Map<String, String> filters = buildFilters(processWithConnector.getId(), defaultConnector.getId(),\n                defaultConnector.getVersion());\n        List<String> deploys = asList(ATTRIBUTE_PROCESS_ID);\n\n        ItemSearchResult<ProcessConnectorDependencyItem> search = apiProcessConnectorDependency.runSearch(0, 10, \"\",\n                null, filters, deploys, null);\n\n        for (ProcessConnectorDependencyItem item : search.getResults()) {\n            assertNotNull(item.getProcess());\n            assertEquals((long) processWithConnector.getId(), (long) item.getProcess().getId().toLong());\n        }\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/process/APIProcessConnectorIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.process;\n\nimport static junit.framework.Assert.assertEquals;\nimport static junit.framework.Assert.assertTrue;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.test.toolkit.bpm.TestProcess;\nimport org.bonitasoft.test.toolkit.bpm.TestProcessFactory;\nimport org.bonitasoft.test.toolkit.bpm.process.TestProcessConnector;\nimport org.bonitasoft.test.toolkit.bpm.process.TestProcessConnectorFactory;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\nimport org.bonitasoft.test.toolkit.organization.TestUserFactory;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDefinition;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorItem;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.test.AbstractConsoleTest;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.Test;\n\n/**\n * @author Colin PUY\n */\npublic class APIProcessConnectorIT extends AbstractConsoleTest {\n\n    private APIProcessConnector apiProcessConnector;\n\n    @Override\n    public void consoleTestSetUp() throws Exception {\n        apiProcessConnector = new APIProcessConnector();\n        apiProcessConnector.setCaller(getAPICaller(getInitiator().getSession(), \"API/bpm/processConnector\"));\n    }\n\n    @Override\n    protected TestUser getInitiator() {\n        return TestUserFactory.getJohnCarpenter();\n    }\n\n    @Test\n    public void testSearch() {\n        TestProcessConnector defaultConnector = TestProcessConnectorFactory.getDefaultConnector();\n        TestProcess processWithConnector = TestProcessFactory.createProcessWithConnector(defaultConnector);\n        Map<String, String> filters = new HashMap<>();\n        filters.put(ProcessConnectorItem.ATTRIBUTE_PROCESS_ID, String.valueOf(processWithConnector.getId()));\n\n        ItemSearchResult<ProcessConnectorItem> search = apiProcessConnector.runSearch(0, 10, \"\", null, filters, null,\n                null);\n\n        ProcessConnectorItem expectedItem = toProcessConnectorItem(defaultConnector, processWithConnector.getId());\n        assertTrue(areEquals(expectedItem, search.getResults().get(0)));\n        assertEquals(1L, search.getTotal());\n    }\n\n    @Test\n    public void testGet() {\n        TestProcessConnector defaultConnector = TestProcessConnectorFactory.getDefaultConnector();\n        TestProcess processWithConnector = TestProcessFactory.createProcessWithConnector(defaultConnector);\n\n        APIID apiid = anApiId(processWithConnector.getId(), defaultConnector.getId(), defaultConnector.getVersion());\n        ProcessConnectorItem processConnectorItem = apiProcessConnector.runGet(apiid, null, null);\n\n        ProcessConnectorItem expectedItem = toProcessConnectorItem(defaultConnector, processWithConnector.getId());\n        assertTrue(areEquals(expectedItem, processConnectorItem));\n    }\n\n    private APIID anApiId(long processId, String connectorId, String connectorVersion) {\n        APIID apiid = APIID.makeAPIID(String.valueOf(processId), connectorId, connectorVersion);\n        apiid.setItemDefinition(ProcessConnectorDefinition.get());\n        return apiid;\n    }\n\n    private ProcessConnectorItem toProcessConnectorItem(TestProcessConnector testProcessConnector, long processId) {\n        ProcessConnectorItem item = new ProcessConnectorItem();\n        item.setName(testProcessConnector.getId());\n        item.setVersion(testProcessConnector.getVersion());\n        item.setProcessId(processId);\n        item.setImplementationName(testProcessConnector.getImplementationId());\n        item.setImplementationVersion(testProcessConnector.getVersion());\n        item.setClassname(testProcessConnector.getImplementationClassname());\n        return item;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/process/APIProcessIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.process;\n\nimport static org.junit.Assert.assertEquals;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils;\nimport org.bonitasoft.engine.api.PlatformAPIAccessor;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveFactory;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.io.FileContent;\nimport org.bonitasoft.test.toolkit.bpm.TestProcess;\nimport org.bonitasoft.test.toolkit.bpm.TestProcessFactory;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\nimport org.bonitasoft.test.toolkit.organization.TestUserFactory;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessItem;\nimport org.bonitasoft.web.test.AbstractConsoleTest;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.Test;\n\npublic class APIProcessIT extends AbstractConsoleTest {\n\n    private APIProcess apiProcess;\n\n    @Override\n    protected TestUser getInitiator() {\n        return TestUserFactory.getJohnCarpenter();\n    }\n\n    @Override\n    public void consoleTestSetUp() throws Exception {\n        apiProcess = new APIProcess();\n        apiProcess.setCaller(getAPICaller(TestUserFactory.getJohnCarpenter().getSession(), \"API/bpm/process\"));\n\n    }\n\n    /**\n     * Add a process uploaded\n     *\n     * @throws Exception\n     */\n    @Test\n    public void testAddProcessItem() throws Exception {\n        final ProcessAPI processAPI = TenantAPIAccessor.getProcessAPI(TestUserFactory.getJohnCarpenter().getSession());\n        final List<ProcessDeploymentInfo> before = processAPI.getProcessDeploymentInfos(0, 10,\n                ProcessDeploymentInfoCriterion.DEFAULT);\n\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(new ProcessDefinitionBuilder().createNewInstance(\"Test process\", \"1.0\").done())\n                .done();\n        final String fileKey = writeBarToUploads(\"addProcessTest\", businessArchive);\n\n        // use api to deploy process uploaded\n        final ProcessItem item = new ProcessItem();\n        item.setAttribute(\"fileupload\", fileKey);\n        apiProcess.add(item);\n\n        // check the process has been correctly uploaded\n        String assertMessage = \"Can't add a ProcessItem to APIProcess. \";\n        int actualSize = -1;\n        final List<ProcessDeploymentInfo> processDeploymentInfos = processAPI.getProcessDeploymentInfos(0, 10,\n                ProcessDeploymentInfoCriterion.DEFAULT);\n        if (processDeploymentInfos != null) {\n            actualSize = processDeploymentInfos.size();\n            for (ProcessDeploymentInfo processDeploymentInfo : processDeploymentInfos) {\n                assertMessage += \"\\nprocessDeploymentInfo=\" + processDeploymentInfo;\n            }\n        } else {\n            assertMessage += \"processDeploymentInfos is null.\";\n        }\n        assertEquals(assertMessage, 1, actualSize - before.size());\n    }\n\n    /**\n     * Update state of an enabled process to disabled\n     *\n     * @throws Exception\n     */\n    @Test\n    public void testUpdateProcessItem() throws Exception {\n        final APIID processDefinitionId = APIID.makeAPIID(TestProcessFactory.getDefaultHumanTaskProcess()\n                .addActor(getInitiator())\n                .enable()\n                .getId());\n\n        // assert process is well enabled\n        final ProcessAPI processAPI = TenantAPIAccessor.getProcessAPI(TestUserFactory.getJohnCarpenter().getSession());\n        final String expectedState = processAPI.getProcessDeploymentInfos(0, 1, ProcessDeploymentInfoCriterion.DEFAULT)\n                .get(0).getActivationState().name();\n        assertEquals(\"Process should start enabled\", ProcessItem.VALUE_ACTIVATION_STATE_ENABLED, expectedState);\n\n        // use process api to update the state\n        final Map<String, String> attributes = new HashMap<>();\n        attributes.put(ProcessItem.ATTRIBUTE_ACTIVATION_STATE, ProcessItem.VALUE_ACTIVATION_STATE_DISABLED);\n        final ProcessItem processItem = apiProcess.update(processDefinitionId, attributes);\n\n        // check the process is disabled (resolved)\n        assertEquals(\n                \"Can't update a processItem with APIProcess <\" + processItem.getActivationState() + \" - \"\n                        + ProcessItem.VALUE_ACTIVATION_STATE_DISABLED\n                        + \">\",\n                processItem.getActivationState(), ProcessItem.VALUE_ACTIVATION_STATE_DISABLED);\n    }\n\n    /**\n     * Get a process\n     *\n     * @throws Exception\n     */\n    @Test\n    public void testGetProcessItem() throws Exception {\n        final APIID processDefinitionId = APIID.makeAPIID(TestProcessFactory.getDefaultHumanTaskProcess()\n                .addActor(getInitiator())\n                .getId());\n\n        final ArrayList<String> deploys = new ArrayList<>();\n        final ArrayList<String> counters = new ArrayList<>();\n\n        assertEquals(\"Can't get a processItem with APIProcess\",\n                apiProcess.runGet(processDefinitionId, deploys, counters).getName(),\n                TestProcessFactory.getDefaultHumanTaskProcess().getProcessDefinition().getName());\n        assertEquals(\"Can't get a processItem with APIProcess\",\n                apiProcess.runGet(processDefinitionId, deploys, counters).getDescription(),\n                TestProcessFactory.getDefaultHumanTaskProcess().getProcessDefinition().getDescription());\n    }\n\n    /**\n     * Search process by its id\n     *\n     * @throws Exception\n     */\n    @Test\n    public void testSearchProcessItemForUser() throws Exception {\n        final APIID processDefinitionId = APIID.makeAPIID(TestProcessFactory.getDefaultHumanTaskProcess()\n                .addActor(getInitiator())\n                .enable()\n                .getId());\n\n        // Set the filters\n        final HashMap<String, String> filters = new HashMap<>();\n        filters.put(ProcessItem.FILTER_USER_ID, String.valueOf(TestUserFactory.getJohnCarpenter().getId()));\n\n        // Search the ProcessItem\n        final ArrayList<String> deploys = new ArrayList<>();\n        final ArrayList<String> counters = new ArrayList<>();\n        final ProcessItem processItem = apiProcess\n                .runSearch(0, 10, null, ProcessItem.ATTRIBUTE_DISPLAY_NAME + \" ASC\", filters, deploys, counters)\n                .getResults().get(0);\n        assertEquals(\n                \"Can't search a processItem with APIProcess <\" + processDefinitionId + \" - \"\n                        + processItem.getId().toLong() + \">\",\n                processDefinitionId,\n                processItem.getId().toLong());\n    }\n\n    /*\n     * Create a temporary file, contain a businessArchive\n     * @return File key from temporary content\n     * @param String\n     * prefix path for the temporary file\n     * @param BusinessArchive\n     * businessArchive write in the temporary file\n     */\n    private static String writeBarToUploads(final String barName, final BusinessArchive businessArchive) {\n        String fileKey = null;\n        try {\n            File tempFile = File.createTempFile(barName, \".bar\",\n                    WebBonitaConstantsUtils.getTenantInstance().getTempFolder());\n            tempFile.delete();\n            BusinessArchiveFactory.writeBusinessArchiveToFile(businessArchive, tempFile);\n\n            // write into database\n            fileKey = PlatformAPIAccessor.getTemporaryContentAPI()\n                    .storeTempFile(new FileContent(\"thisismynewfile.doc\", new FileInputStream(tempFile),\n                            \"application/octet-stream\"));\n\n        } catch (final IOException | BonitaHomeNotSetException | ServerAPIException | UnknownAPITypeException e) {\n            e.printStackTrace();\n        }\n        return fileKey;\n    }\n\n    /**\n     * Get the latest process version\n     *\n     * @throws Exception\n     */\n    @Test\n    public void testGetLastProcessVersion() throws Exception {\n        // create 3 version of a process\n        final TestProcess p1 = new TestProcess(\n                TestProcessFactory.getDefaultProcessDefinitionBuilder(\"multipleVersionsProcess\", \"aVersion\"));\n        TestProcessFactory.getInstance().add(p1);\n        final TestProcess p2 = new TestProcess(\n                TestProcessFactory.getDefaultProcessDefinitionBuilder(\"multipleVersionsProcess\", \"aVersion2\"));\n        TestProcessFactory.getInstance().add(p2);\n        final TestProcess p3 = new TestProcess(\n                TestProcessFactory.getDefaultProcessDefinitionBuilder(\"multipleVersionsProcess\", \"anOtherVersion\"));\n        TestProcessFactory.getInstance().add(p3);\n\n        // map actor John Carpenter on the created processes, then set enable\n        p1.addActor(TestUserFactory.getJohnCarpenter()).enable();\n        p2.addActor(TestUserFactory.getJohnCarpenter()).enable();\n        p3.addActor(TestUserFactory.getJohnCarpenter()).enable();\n\n        // Set the filters\n        final HashMap<String, String> filters = new HashMap<>();\n        filters.put(ProcessItem.FILTER_USER_ID, String.valueOf(TestUserFactory.getJohnCarpenter().getId()));\n        filters.put(ProcessItem.ATTRIBUTE_DISPLAY_NAME, \"multipleVersionsProcess\");\n\n        // search the last version of a process\n        final List<ProcessItem> resultList = apiProcess\n                .runSearch(0, 1, null, ProcessItem.ATTRIBUTE_DEPLOYMENT_DATE + \" DESC\", filters, null, null)\n                .getResults();\n\n        // get the first element\n        final ProcessItem searchedProcessItem = resultList.get(0);\n        assertEquals(\"multipleVersionsProcess\", searchedProcessItem.getDisplayName());\n        assertEquals(\"anOtherVersion\", searchedProcessItem.getVersion());\n\n        //Because TestProcessFactory is based on names, at least 2 out of the three above processes should be cleaned manually.\n        // This could be improved later in TestProcessFactory\n        TestProcessFactory.getInstance().delete(p1);\n        TestProcessFactory.getInstance().delete(p2);\n        TestProcessFactory.getInstance().delete(p3);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/process/APIProcessResolutionProblemIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.process;\n\nimport junit.framework.Assert;\nimport org.bonitasoft.test.toolkit.bpm.TestProcess;\nimport org.bonitasoft.test.toolkit.bpm.TestProcessFactory;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\nimport org.bonitasoft.test.toolkit.organization.TestUserFactory;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessResolutionProblemItem;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.test.AbstractConsoleTest;\nimport org.bonitasoft.web.toolkit.client.common.texttemplate.Arg;\nimport org.bonitasoft.web.toolkit.client.common.util.MapUtil;\nimport org.junit.Test;\n\n/**\n * @author Séverin Moussel\n */\npublic class APIProcessResolutionProblemIT extends AbstractConsoleTest {\n\n    @Override\n    public void consoleTestSetUp() throws Exception {\n    }\n\n    @Override\n    protected TestUser getInitiator() {\n        return TestUserFactory.getJohnCarpenter();\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // UTILS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    private APIProcessResolutionProblem getAPI() {\n        final APIProcessResolutionProblem api = new APIProcessResolutionProblem();\n        api.setCaller(getAPICaller(getInitiator().getSession(), \"API/bpm/processResolutionProblem\"));\n        return api;\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // TESTS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Test\n    public void testSearchWithResults() {\n        final TestProcess process = TestProcessFactory.getDefaultHumanTaskProcess();\n        final ItemSearchResult<ProcessResolutionProblemItem> results = getAPI().runSearch(\n                0, 100,\n                null,\n                null,\n                MapUtil.asMap(new Arg(ProcessResolutionProblemItem.FILTER_PROCESS_ID, process.getId())),\n                null,\n                null);\n\n        Assert.assertFalse(\"No resolution issues found\", results.getResults().size() == 0);\n        Assert.assertTrue(\"Wrong number of resolution issues found\", results.getResults().size() == 1);\n    }\n\n    @Test\n    public void testSearchWithoutResults() {\n        final TestProcess process = TestProcessFactory.getDefaultHumanTaskProcess();\n        process.addActor(getInitiator());\n\n        final ItemSearchResult<ProcessResolutionProblemItem> results = getAPI().runSearch(\n                0, 100,\n                null,\n                null,\n                MapUtil.asMap(new Arg(ProcessResolutionProblemItem.FILTER_PROCESS_ID, process.getId())),\n                null,\n                null);\n\n        Assert.assertTrue(\"Resolution issues found\", results.getResults().size() == 0);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/organization/APIGroupIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.organization;\n\nimport static org.bonitasoft.web.rest.model.builder.organisation.GroupItemBuilder.aGroup;\nimport static org.mockito.Mockito.spy;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.nio.file.Files;\n\nimport org.bonitasoft.engine.api.PlatformAPIAccessor;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.io.FileContent;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\nimport org.bonitasoft.test.toolkit.organization.TestUserFactory;\nimport org.bonitasoft.web.rest.model.identity.GroupItem;\nimport org.bonitasoft.web.test.AbstractConsoleTest;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.Assert;\nimport org.junit.Test;\n\npublic class APIGroupIT extends AbstractConsoleTest {\n\n    private APIGroup apiGroup;\n\n    @Override\n    public void consoleTestSetUp() throws Exception {\n        apiGroup = spy(new APIGroup());\n        apiGroup.setCaller(getAPICaller(getInitiator().getSession(), \"API/identity/group\"));\n    }\n\n    @Override\n    protected TestUser getInitiator() {\n        return TestUserFactory.getJohnCarpenter();\n    }\n\n    @Test(expected = APIException.class)\n    public void addingTwiceSameGroupIsForbidden() {\n        final GroupItem groupItem = aGroup().build();\n\n        apiGroup.add(groupItem);\n        apiGroup.add(groupItem);\n    }\n\n    @Test\n    public void should_update_group_icon()\n            throws ServerAPIException, BonitaHomeNotSetException, UnknownAPITypeException, IOException {\n        GroupItem input = new GroupItem();\n        input.setName(\"Developper\");\n        input.setDescription(\"The guys who drink a lot of coffee\");\n        input = apiGroup.runAdd(input);\n        final APIID id = input.getId();\n        Assert.assertNotNull(\"Failed to add a new role\", input);\n        input = new GroupItem();\n\n        //store icon into database\n        File file = File.createTempFile(\"tmp\", \".png\");\n        Files.writeString(file.toPath(), \"content\");\n        String iconFileKey = PlatformAPIAccessor.getTemporaryContentAPI()\n                .storeTempFile(new FileContent(\"icon.png\", new FileInputStream(file), \"img/png\"));\n\n        input.setIcon(iconFileKey);\n        input = apiGroup.runUpdate(id, input.getAttributes());\n        Assert.assertNotNull(\"Failed while updating the group\", input);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/organization/APIMembershipIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.organization;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.test.toolkit.organization.TestGroup;\nimport org.bonitasoft.test.toolkit.organization.TestGroupFactory;\nimport org.bonitasoft.test.toolkit.organization.TestMembershipFactory;\nimport org.bonitasoft.test.toolkit.organization.TestRole;\nimport org.bonitasoft.test.toolkit.organization.TestRoleFactory;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\nimport org.bonitasoft.test.toolkit.organization.TestUserFactory;\nimport org.bonitasoft.web.rest.model.identity.MembershipItem;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.test.AbstractConsoleTest;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIForbiddenException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author Séverin Moussel\n * @author Colin PUY\n */\npublic class APIMembershipIT extends AbstractConsoleTest {\n\n    private APIMembership apiMembership;\n\n    @Override\n    public void consoleTestSetUp() throws Exception {\n        apiMembership = new APIMembership();\n        apiMembership.setCaller(getAPICaller(getInitiator().getSession(), \"API/identity/membership\"));\n    }\n\n    @Override\n    protected TestUser getInitiator() {\n        return TestUserFactory.getJohnCarpenter();\n    }\n\n    private void checkSearchResults(final ItemSearchResult<MembershipItem> membershipItems,\n            final int nbResultsByPageExpected, final int nbTotalResultsExpected) {\n        assertTrue(\"Empty search results\", membershipItems.getLength() > 0);\n        assertEquals(\"Wrong page size\", membershipItems.getLength(), nbResultsByPageExpected);\n        assertEquals(\"Wrong Total size\", membershipItems.getTotal(), nbTotalResultsExpected);\n    }\n\n    @Test\n    public void testAdd() {\n        // Add\n        final MembershipItem input = new MembershipItem();\n        input.setUserId(getInitiator().getId());\n        input.setGroupId(TestGroupFactory.getWeb().getId());\n        input.setRoleId(TestRoleFactory.getManager().getId());\n\n        final MembershipItem output = apiMembership.runAdd(input);\n\n        Assert.assertNotNull(\"Failed to add a new membership\", input);\n        assertEquals(\"Wrong membership inserted\", input.getUserId(), output.getUserId());\n        assertEquals(\"Wrong membership inserted\", input.getGroupId(), output.getGroupId());\n        assertEquals(\"Wrong membership inserted\", input.getRoleId(), output.getRoleId());\n    }\n\n    private void beforeSearch() {\n        final List<TestRole> roles = TestRoleFactory.getInstance().createRandomRoles(5);\n        final List<TestGroup> groups = TestGroupFactory.createRandomGroups(5);\n\n        final TestUser user1 = getInitiator();\n        final TestUser user2 = TestUserFactory.getRidleyScott();\n\n        for (final TestRole role : roles) {\n            for (final TestGroup group : groups) {\n                TestMembershipFactory.assignMembership(user1, group, role);\n                TestMembershipFactory.assignMembership(user2, group, role);\n            }\n        }\n    }\n\n    @Test\n    public void testSearch() {\n        beforeSearch();\n\n        final Map<String, String> filters = new HashMap<>();\n        filters.put(MembershipItem.ATTRIBUTE_USER_ID, String.valueOf(getInitiator().getId()));\n\n        final ItemSearchResult<MembershipItem> searchResults = apiMembership.runSearch(0, 12, null, null, filters, null,\n                null);\n\n        checkSearchResults(searchResults, 12, 25);\n    }\n\n    @Test\n    public void testDeploys() {\n        beforeSearch();\n\n        final Map<String, String> filters = new HashMap<>();\n        filters.put(MembershipItem.ATTRIBUTE_USER_ID, String.valueOf(getInitiator().getId()));\n\n        final ItemSearchResult<MembershipItem> searchResults = apiMembership.runSearch(\n                0,\n                11,\n                null,\n                null,\n                filters,\n                Arrays.asList(\n                        MembershipItem.ATTRIBUTE_USER_ID,\n                        MembershipItem.ATTRIBUTE_ROLE_ID,\n                        MembershipItem.ATTRIBUTE_GROUP_ID,\n                        MembershipItem.ATTRIBUTE_ASSIGNED_BY_USER_ID),\n                null);\n\n        checkSearchResults(searchResults, 11, 25);\n\n        final MembershipItem firstMembership = searchResults.getResults().get(0);\n\n        Assert.assertNotNull(\"Failed to deploy user_id\", firstMembership.getUser());\n        assertEquals(\"Wrong user deployed\", getInitiator().getUser().getUserName(),\n                firstMembership.getUser().getUserName());\n        Assert.assertNotNull(\"Failed to deploy role_id\", firstMembership.getRole());\n        Assert.assertNotNull(\"Failed to deploy group_id\", firstMembership.getGroup());\n        Assert.assertNotNull(\"Failed to deploy assigned_by_user_id\", firstMembership.getAssignedByUser());\n    }\n\n    @Test\n    public void testDelete() {\n\n        // INIT\n        final TestRole roleManager = TestRoleFactory.getManager();\n        final TestRole roleDeveloper = TestRoleFactory.getDeveloper();\n        final TestGroup groupWeb = TestGroupFactory.createRandomGroups(1).get(0);\n\n        final TestUser user = getInitiator();\n\n        TestMembershipFactory.assignMembership(user, groupWeb, roleManager);\n        TestMembershipFactory.assignMembership(user, groupWeb, roleDeveloper);\n\n        // ACTION\n        apiMembership.runDelete(\n                List.of(APIID.makeAPIID(\n                        getInitiator().getId(),\n                        groupWeb.getId(),\n                        roleManager.getId())));\n\n        // CHECK RESULT\n\n        final Map<String, String> filters = new HashMap<>();\n        filters.put(MembershipItem.ATTRIBUTE_USER_ID, String.valueOf(getInitiator().getId()));\n\n        final ItemSearchResult<MembershipItem> searchResults = apiMembership.runSearch(0, 12, null, null, filters, null,\n                null);\n\n        checkSearchResults(searchResults, 12, 1);\n    }\n\n    @Test(expected = APIForbiddenException.class)\n    public void addingTwiceSameMembershipIsForbidden() {\n        MembershipItem input = new MembershipItem();\n        input.setUserId(getInitiator().getId());\n        input.setGroupId(TestGroupFactory.getWeb().getId());\n        input.setRoleId(TestRoleFactory.getManager().getId());\n\n        apiMembership.runAdd(input);\n        apiMembership.runAdd(input);\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/organization/APIPersonalContactDataIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.organization;\n\nimport static org.junit.Assert.assertEquals;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.test.toolkit.organization.TestUser;\nimport org.bonitasoft.test.toolkit.organization.TestUserFactory;\nimport org.bonitasoft.web.rest.model.identity.PersonalContactDataItem;\nimport org.bonitasoft.web.test.AbstractConsoleTest;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.Test;\n\n/**\n * @author Paul AMAR\n */\npublic class APIPersonalContactDataIT extends AbstractConsoleTest {\n\n    private APIPersonalContactData apiPersonalContactData;\n\n    @Override\n    public void consoleTestSetUp() throws Exception {\n        this.apiPersonalContactData = new APIPersonalContactData();\n        this.apiPersonalContactData.setCaller(getAPICaller(TestUserFactory.getRidleyScott().getSession(),\n                \"API/identity/personalcontactdata\"));\n    }\n\n    @Test\n    public void getPersonalContactData() {\n        final PersonalContactDataItem result = this.apiPersonalContactData\n                .get(APIID.makeAPIID(TestUserFactory.getRidleyScott().getId()));\n        assertEquals(result.getAttributes().size(), 13);\n    }\n\n    @Test\n    public void updatePersonalContactData() {\n\n        final Map<String, String> attributes = new HashMap<>();\n\n        // Set all the fields.\n        attributes.put(PersonalContactDataItem.ATTRIBUTE_EMAIL, \"email\");\n        attributes.put(PersonalContactDataItem.ATTRIBUTE_PHONE, \"phone\");\n        attributes.put(PersonalContactDataItem.ATTRIBUTE_MOBILE, \"mobile\");\n        attributes.put(PersonalContactDataItem.ATTRIBUTE_FAX, \"fax\");\n        attributes.put(PersonalContactDataItem.ATTRIBUTE_BUILDING, \"building\");\n        attributes.put(PersonalContactDataItem.ATTRIBUTE_ROOM, \"room\");\n        attributes.put(PersonalContactDataItem.ATTRIBUTE_ADDRESS, \"address\");\n        attributes.put(PersonalContactDataItem.ATTRIBUTE_ZIPCODE, \"zipcode\");\n        attributes.put(PersonalContactDataItem.ATTRIBUTE_CITY, \"city\");\n        attributes.put(PersonalContactDataItem.ATTRIBUTE_STATE, \"state\");\n        attributes.put(PersonalContactDataItem.ATTRIBUTE_COUNTRY, \"country\");\n        attributes.put(PersonalContactDataItem.ATTRIBUTE_WEBSITE, \"website\");\n\n        this.apiPersonalContactData.update(APIID.makeAPIID(TestUserFactory.getRidleyScott().getId()), attributes);\n\n        final PersonalContactDataItem result = this.apiPersonalContactData\n                .get(APIID.makeAPIID(TestUserFactory.getRidleyScott().getId()));\n\n        assertEquals(result.getBuilding(), \"building\");\n        assertEquals(result.getPhoneNumber(), \"phone\");\n        assertEquals(result.getMobileNumber(), \"mobile\");\n        assertEquals(result.getFaxNumber(), \"fax\");\n        assertEquals(result.getRoom(), \"room\");\n        assertEquals(result.getAddress(), \"address\");\n        assertEquals(result.getZipCode(), \"zipcode\");\n        assertEquals(result.getCountry(), \"country\");\n        assertEquals(result.getState(), \"state\");\n        assertEquals(result.getEmail(), \"email\");\n        assertEquals(result.getWebsite(), \"website\");\n\n    }\n\n    @Test\n    public void addPersonalContactData() {\n        final TestUser user = getInitiator().createUser(\"user\", \"pwd\");\n        final PersonalContactDataItem res = new PersonalContactDataItem();\n        res.setAddress(\"New address\");\n        res.setId(user.getId());\n        this.apiPersonalContactData.add(res);\n\n        final PersonalContactDataItem result = this.apiPersonalContactData.get(APIID.makeAPIID(user.getId()));\n\n        assertEquals(result.getCity(), null);\n        assertEquals(result.getAddress(), \"New address\");\n        assertEquals(result.getBuilding(), null);\n    }\n\n    @Override\n    protected TestUser getInitiator() {\n        return TestUserFactory.getRidleyScott();\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/organization/APIProfessionalContactDataIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.organization;\n\nimport static org.bonitasoft.web.rest.model.builder.identity.ContactDataBuilder.aContactData;\nimport static org.bonitasoft.web.toolkit.client.data.APIID.makeAPIID;\nimport static org.junit.Assert.assertEquals;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.IdentityAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.identity.UserCreator;\nimport org.bonitasoft.engine.identity.UserCriterion;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\nimport org.bonitasoft.test.toolkit.organization.TestUserFactory;\nimport org.bonitasoft.web.rest.model.builder.identity.ContactDataBuilder;\nimport org.bonitasoft.web.rest.model.identity.ProfessionalContactDataItem;\nimport org.bonitasoft.web.test.AbstractConsoleTest;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.After;\nimport org.junit.Test;\n\n/**\n * @author Paul AMAR\n */\npublic class APIProfessionalContactDataIT extends AbstractConsoleTest {\n\n    private APIProfessionalContactData apiProfessionalContactData;\n\n    @After\n    public void cleanUsersInDB()\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException, DeletionException {\n        final IdentityAPI identityAPI = TenantAPIAccessor.getIdentityAPI(TestUserFactory.getRidleyScott().getSession());\n\n        List<User> users = identityAPI.getUsers(0, 200, UserCriterion.FIRST_NAME_ASC);\n        while (!users.isEmpty()) {\n            for (final User user : users) {\n                identityAPI.deleteUser(user.getId());\n            }\n            users = identityAPI.getUsers(0, 200, UserCriterion.FIRST_NAME_ASC);\n        }\n    }\n\n    @Override\n    public void consoleTestSetUp() throws Exception {\n        apiProfessionalContactData = new APIProfessionalContactData();\n        apiProfessionalContactData.setCaller(\n                getAPICaller(TestUserFactory.getRidleyScott().getSession(), \"API/identity/professionalcontactdata\"));\n    }\n\n    @Override\n    protected TestUser getInitiator() {\n        return TestUserFactory.getRidleyScott();\n    }\n\n    protected TestUser createUserWithProfessionnalContactData(final ContactDataBuilder aContactData) {\n        final UserCreator userCreator = new UserCreator(\"aUser\", \"aPassword\");\n        userCreator.setProfessionalContactData(aContactData.toContactDataCreator());\n        return getInitiator().createUser(userCreator);\n    }\n\n    @Test\n    public void getProfessionalContactData_return_professional_contact_data_of_user_with_given_id() {\n        final ContactDataBuilder aContactData = aContactData();\n        final TestUser user = createUserWithProfessionnalContactData(aContactData);\n\n        final ProfessionalContactDataItem result = apiProfessionalContactData.get(makeAPIID(user.getId()));\n\n        final ProfessionalContactDataItem expectedItem = aContactData.toProfessionalContactDataItem();\n        assertEquals(expectedItem.getEmail(), result.getEmail());\n        assertEquals(expectedItem.getPhoneNumber(), result.getPhoneNumber());\n        assertEquals(expectedItem.getMobileNumber(), result.getMobileNumber());\n        assertEquals(expectedItem.getFaxNumber(), result.getFaxNumber());\n        assertEquals(expectedItem.getBuilding(), result.getBuilding());\n        assertEquals(expectedItem.getRoom(), result.getRoom());\n        assertEquals(expectedItem.getAddress(), result.getAddress());\n        assertEquals(expectedItem.getZipCode(), result.getZipCode());\n        assertEquals(expectedItem.getCity(), result.getCity());\n        assertEquals(expectedItem.getState(), result.getState());\n        assertEquals(expectedItem.getCountry(), result.getCountry());\n        assertEquals(expectedItem.getWebsite(), result.getWebsite());\n    }\n\n    @Test\n    public void updateProfessionalContactData_update_professional_contact_data_of_given_user() {\n        final TestUser user = createUserWithProfessionnalContactData(aContactData());\n        final ProfessionalContactDataItem contactDataItem = aContactData().withAddress(\"anOtherAddress\")\n                .toProfessionalContactDataItem();\n\n        final ProfessionalContactDataItem updatedItem = apiProfessionalContactData.update(makeAPIID(user.getId()),\n                contactDataItem.getAttributes());\n\n        final ProfessionalContactDataItem expectedItem = apiProfessionalContactData.get(makeAPIID(user.getId()));\n        assertItemEquals(expectedItem, updatedItem);\n    }\n\n    @Test\n    public void addProfessionalContactData_add_professional_contact_data_to_a_user() {\n        final TestUser user = getInitiator().createUser(\"user\", \"pwd\");\n        final ProfessionalContactDataItem res = new ProfessionalContactDataItem();\n        res.setAddress(\"New address\");\n        res.setId(user.getId());\n\n        apiProfessionalContactData.add(res);\n\n        final ProfessionalContactDataItem result = apiProfessionalContactData.get(APIID.makeAPIID(user.getId()));\n        assertEquals(result.getCity(), null);\n        assertEquals(result.getAddress(), \"New address\");\n        assertEquals(result.getBuilding(), null);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/organization/APIRoleIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.organization;\n\nimport static java.util.Arrays.asList;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.test.toolkit.organization.TestUserFactory.getJohnCarpenter;\nimport static org.bonitasoft.test.toolkit.organization.TestUserFactory.getMrSpechar;\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.Mockito.spy;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.util.*;\n\nimport org.bonitasoft.engine.api.PlatformAPIAccessor;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.identity.Role;\nimport org.bonitasoft.engine.io.FileContent;\nimport org.bonitasoft.test.toolkit.organization.*;\nimport org.bonitasoft.web.rest.model.identity.RoleItem;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.test.AbstractConsoleTest;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author Séverin Moussel\n */\npublic class APIRoleIT extends AbstractConsoleTest {\n\n    @Override\n    public void consoleTestSetUp() throws Exception {\n    }\n\n    @Override\n    protected TestUser getInitiator() {\n        return TestUserFactory.getJohnCarpenter();\n    }\n\n    /**\n     * @return\n     */\n    private APIRole getAPIRole() {\n        final APIRole apiRole = new APIRole();\n        apiRole.setCaller(getAPICaller(getInitiator().getSession(), \"API/identity/role\"));\n        return apiRole;\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // GET / ADD\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    private void assertItemEquals(final String message, final RoleItem expected, final RoleItem actual) {\n        Assert.assertEquals(message, expected.getAttributes(), actual.getAttributes());\n    }\n\n    @Test\n    public void testAddAndGet() {\n        // Add\n        RoleItem input = new RoleItem();\n        input.setName(\"Developper\");\n        input.setDescription(\"The guys who drink a lot of coffee\");\n        input = getAPIRole().runAdd(input);\n\n        Assert.assertNotNull(\"Failed to add a new role\", input);\n\n        // Get\n        final RoleItem output = getAPIRole().runGet(input.getId(), new ArrayList<>(), new ArrayList<>());\n\n        Assert.assertNotNull(\"Role not found\", output);\n        assertItemEquals(\"Wrong role found\", input, output);\n        getAPIRole().runDelete(Arrays.asList(input.getId()));\n    }\n\n    @Test\n    public void testDeploys() {\n        // Add\n        RoleItem input = new RoleItem();\n        input.setName(\"Developper\");\n        input.setDescription(\"The guys who drink a lot of coffee\");\n        input = getAPIRole().runAdd(input);\n\n        Assert.assertNotNull(\"Failed to add a new role\", input);\n\n        // Get\n        final RoleItem output = getAPIRole().runGet(\n                input.getId(),\n                Arrays.asList(RoleItem.ATTRIBUTE_CREATED_BY_USER_ID),\n                new ArrayList<>());\n\n        Assert.assertNotNull(\"Role not found\", output);\n        assertItemEquals(\"Wrong role found\", input, output);\n\n        Assert.assertNotNull(\"Failed to deploy initiator user\", output.getCreatedByUserId());\n        Assert.assertEquals(\"Wrong process deployed\", getInitiator().getUserName(),\n                output.getCreatedByUser().getUserName());\n\n        getAPIRole().runDelete(Arrays.asList(input.getId()));\n\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // SEARCH\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Test\n    public void testSearch() throws Exception {\n        TestRoleFactory.getInstance().createRandomRoles(13);\n\n        final ItemSearchResult<RoleItem> roleItems = getAPIRole().runSearch(0, 10, null, null, null, null, null);\n\n        checkSearchResults(roleItems, 10, 13);\n    }\n\n    /**\n     * @param roleItems\n     */\n    private void checkSearchResults(final ItemSearchResult<RoleItem> roleItems, final int nbResultsByPageExpected,\n            final int nbTotalResultsExpected) {\n        Assert.assertTrue(\"Empty search results\", roleItems.getLength() > 0);\n        Assert.assertTrue(\"Wrong page size\", roleItems.getLength() == nbResultsByPageExpected);\n        Assert.assertTrue(\"Wrong Total size\", roleItems.getTotal() == nbTotalResultsExpected);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // DELETE\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Test\n    public void testDeleteOne() throws Exception {\n        TestRoleFactory.getInstance().createRandomRoles(13);\n\n        final ItemSearchResult<RoleItem> roleItems = getAPIRole().runSearch(0, 10, null, null, null, null, null);\n        getAPIRole().runDelete(Arrays.asList(roleItems.getResults().get(0).getId()));\n\n        final ItemSearchResult<RoleItem> roleItemsAfter = getAPIRole().runSearch(0, 10, null, null, null, null, null);\n        Assert.assertEquals(\"Failed to delete one role\", 12, roleItemsAfter.getTotal());\n    }\n\n    @Test\n    public void testDeleteMultiple() throws Exception {\n        TestRoleFactory.getInstance().createRandomRoles(13);\n\n        final ItemSearchResult<RoleItem> roleItems = getAPIRole().runSearch(0, 10, null, null, null, null, null);\n\n        getAPIRole().runDelete(Arrays.asList(\n                roleItems.getResults().get(1).getId(),\n                roleItems.getResults().get(0).getId()));\n\n        final ItemSearchResult<RoleItem> roleItemsAfter = getAPIRole().runSearch(0, 10, null, null, null, null, null);\n        Assert.assertEquals(\"Failed to delete multiple roles\", 11, roleItemsAfter.getTotal());\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // UPDATE\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Test\n    public void testUpdate() throws Exception {\n        final String newDescription = \"Lorem ipsum dolor sit amet\";\n\n        // Add\n        RoleItem input = new RoleItem();\n        input.setName(\"Developper\");\n        input.setDescription(\"The guys who drink a lot of coffee\");\n        input = getAPIRole().runAdd(input);\n\n        Assert.assertNotNull(\"Failed to add a new role\", input);\n\n        // Update\n        final Map<String, String> updates = new HashMap<>();\n        updates.put(RoleItem.ATTRIBUTE_DESCRIPTION, newDescription);\n        getAPIRole().runUpdate(input.getId(), updates);\n\n        // Get\n        final RoleItem output = getAPIRole().runGet(input.getId(), new ArrayList<>(), new ArrayList<>());\n\n        Assert.assertNotNull(\"Role not found\", output);\n        Assert.assertEquals(\"Update of role failed\", newDescription, output.getDescription());\n\n        getAPIRole().runDelete(Arrays.asList(input.getId()));\n    }\n\n    @Test\n    public void should_update_role_icon()\n            throws IOException, ServerAPIException, BonitaHomeNotSetException, UnknownAPITypeException {\n        // Add\n        RoleItem input = new RoleItem();\n        final APIRole spyApiRole = spy(getAPIRole());\n        input.setName(\"Developper\");\n        input.setDescription(\"The guys who drink a lot of coffee\");\n        input = spyApiRole.runAdd(input);\n        final APIID id = input.getId();\n        assertThat(input).isNotNull();\n        input = new RoleItem();\n\n        //store icon into database\n        File file = File.createTempFile(\"tmp\", \".png\");\n        Files.writeString(file.toPath(), \"content\");\n        String iconFileKey = PlatformAPIAccessor.getTemporaryContentAPI()\n                .storeTempFile(new FileContent(\"icon.png\", new FileInputStream(file), \"img/png\"));\n\n        input.setIcon(iconFileKey);\n\n        try {\n            input = spyApiRole.runUpdate(id, input.getAttributes());\n            assertThat(input).isNotNull();\n        } finally {\n            spyApiRole.runDelete(Arrays.asList(id));\n        }\n    }\n\n    @Test\n    public void weCanCountAllUsersInAGroup() throws Exception {\n        final Role roleWith2Users = createRoleWithAssignedUsers(getJohnCarpenter(), getMrSpechar());\n        final List<String> counters = asList(RoleItem.COUNTER_NUMBER_OF_USERS);\n\n        final RoleItem roleItem = getAPIRole().runGet(APIID.makeAPIID(roleWith2Users.getId()), null, counters);\n\n        assertEquals(2L, (long) roleItem.getNumberOfUsers());\n    }\n\n    private Role createRoleWithAssignedUsers(final TestUser... users) {\n        final TestGroup aGroup = TestGroupFactory.getRAndD();\n        final TestRole aRole = TestRoleFactory.getDeveloper();\n        for (final TestUser user : users) {\n            TestMembershipFactory.assignMembership(user, aGroup, aRole);\n        }\n        return aRole.getRole();\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/organization/APIUserAnotherIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.organization;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\n\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.engine.api.IdentityAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.identity.UserCreator;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\nimport org.bonitasoft.test.toolkit.organization.TestUserFactory;\nimport org.bonitasoft.test.toolkit.server.MockHttpServletRequest;\nimport org.bonitasoft.test.toolkit.server.MockHttpServletResponse;\nimport org.bonitasoft.web.rest.model.identity.UserItem;\nimport org.bonitasoft.web.rest.server.framework.APIServletCall;\nimport org.bonitasoft.web.test.AbstractConsoleTest;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasCreator;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasLastUpdateDate;\nimport org.junit.After;\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author Vincent Elcrin\n */\npublic class APIUserAnotherIT extends AbstractConsoleTest {\n\n    private final static String PATH_INFO = \"API/identity/user\";\n\n    /**\n     * the request param for the username\n     */\n    public static final String USERNAME_SESSION_PARAM = \"username\";\n\n    public static final String API_SESSION_PARAM_KEY = \"apiSession\";\n\n    /**\n     * Tested API\n     */\n    private APIUser apiUser;\n\n    // magic number. Look at testSearchUser for details.\n    private static final int NUM_EXPECTED_USERS = 12;\n\n    private HashMap<Integer, UserItem> expectedUsers;\n\n    private static final List<String> userAttributesList = Arrays.asList(\n            UserItem.ATTRIBUTE_FIRSTNAME,\n            UserItem.ATTRIBUTE_LASTNAME,\n            UserItem.ATTRIBUTE_PASSWORD,\n            UserItem.ATTRIBUTE_USERNAME,\n            UserItem.ATTRIBUTE_TITLE,\n            UserItem.ATTRIBUTE_JOB_TITLE);\n\n    private static final List<String> searchableAttributesList = Arrays.asList(\n            UserItem.ATTRIBUTE_FIRSTNAME,\n            UserItem.ATTRIBUTE_LASTNAME,\n            UserItem.ATTRIBUTE_USERNAME,\n            UserItem.ATTRIBUTE_JOB_TITLE);\n\n    @Override\n    public void consoleTestSetUp() throws Exception {\n        this.apiUser = createAPIUser(getInitiator().getSession());\n        createUsersViaEngineAPI(getInitiator().getSession(), NUM_EXPECTED_USERS);\n    }\n\n    @Override\n    protected TestUser getInitiator() {\n        return TestUserFactory.getJohnCarpenter();\n    }\n\n    @After\n    public void deleteUsers() throws Exception {\n        final IdentityAPI identityAPI = TenantAPIAccessor.getIdentityAPI(getInitiator().getSession());\n        final Iterator<UserItem> it = this.expectedUsers.values().iterator();\n        while (it.hasNext()) {\n            identityAPI.deleteUser(it.next().getId().toLong());\n        }\n    }\n\n    /**\n     * Create api user.\n     * Used to migrate other test to the new user API.\n     *\n     * @param apiSession\n     * @return\n     */\n    public static APIUser createAPIUser(final APISession apiSession) {\n\n        // Get the httpSession and set attributes\n        final MockHttpServletRequest mockHttpServletRequest = new MockHttpServletRequest();\n        mockHttpServletRequest.setPathInfo(PATH_INFO);\n        final MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse();\n        final HttpSession httpSession = mockHttpServletRequest.getSession();\n        httpSession.setAttribute(USERNAME_SESSION_PARAM, \"admin\");\n        httpSession.setAttribute(API_SESSION_PARAM_KEY, apiSession);\n\n        // Initialize APIUser for HTTP requests of the API\n        final APIUser apiUser = new APIUser();\n        final APIServletCall caller = new APIServletCall(mockHttpServletRequest,\n                mockHttpServletResponse);\n        apiUser.setCaller(caller);\n        return apiUser;\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // CONVENIENCE METHODS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Create user containing all attributes listed in userAttributeList with attributeKey_userNumber for value\n     *\n     * @param userNumber\n     * @return\n     */\n    private UserItem createCompleteUser(final List<String> userAttributesList, final int userNumber) {\n        final HashMap<String, String> attributes = new HashMap<>();\n        for (final String attribute : userAttributesList) {\n            if (UserItem.ATTRIBUTE_LAST_CONNECTION_DATE.equals(attribute)\n                    || ItemHasCreator.ATTRIBUTE_CREATION_DATE.equals(attribute)\n                    || ItemHasLastUpdateDate.ATTRIBUTE_LAST_UPDATE_DATE.equals(attribute)) {\n                final Date date = new Date();\n                attributes.put(attribute, date.toString());\n            } else {\n                attributes.put(attribute, attribute + \"_\" + userNumber);\n            }\n        }\n        final UserItem user = new UserItem();\n        user.setAttributes(attributes);\n        return user;\n    }\n\n    /**\n     * Create numberExpectedUser of users via the engine api\n     *\n     * @param numberExpectedUser\n     * @throws Exception\n     */\n    private void createUsersViaEngineAPI(final APISession apiSession, final int numberExpectedUser) throws Exception {\n        final IdentityAPI identityAPI = TenantAPIAccessor.getIdentityAPI(apiSession);\n        this.expectedUsers = new HashMap<>();\n        for (int i = 0; i < numberExpectedUser; i++) {\n            final UserItem newUser = createCompleteUser(userAttributesList, i);\n            final APIID newUserId = APIID.makeAPIID(identityAPI.createUser(buildEngineUser(newUser))\n                    .getId());\n            newUser.setId(newUserId);\n            this.expectedUsers.put(i, newUser);\n        }\n    }\n\n    /**\n     * Assert that listed attributes of expected user are equals to the actual ones.\n     *\n     * @param userAttributesList\n     *        : list of attribute to test\n     * @param expected\n     *        : Expected user\n     * @param actual\n     *        : User tested against\n     */\n    private void assertUserEquals(final List<String> userAttributesList, final UserItem expected,\n            final UserItem actual) {\n        final ArrayList<String> localAttributeList = new ArrayList<>();\n        localAttributeList.addAll(userAttributesList);\n        // password do not come back from the api.\n        localAttributeList.remove(UserItem.ATTRIBUTE_PASSWORD);\n        // test\n        for (final String attribute : localAttributeList) {\n            Assert.assertEquals(attribute + \" isnt equals to the attribute setted previously\",\n                    expected.getAttributeValue(attribute),\n                    actual.getAttributeValue(attribute));\n        }\n    }\n\n    @Test\n    public void testGetUser() {\n        final Iterator<UserItem> it = this.expectedUsers.values().iterator();\n        while (it.hasNext()) {\n            final UserItem expectedUser = it.next();\n            final UserItem anUser = this.apiUser.runGet(expectedUser.getId(), new ArrayList<>(), new ArrayList<>());\n\n            // tests\n            Assert.assertNotNull(anUser);\n            assertUserEquals(userAttributesList, expectedUser, anUser);\n        }\n    }\n\n    @Test\n    public void testSearchUser() {\n        final Iterator<UserItem> it = this.expectedUsers.values().iterator();\n        while (it.hasNext()) {\n            final UserItem expectedUser = it.next();\n            for (final String attribute : searchableAttributesList) {\n                final List<UserItem> results = this.apiUser\n                        .runSearch(0, 10, expectedUser.getAttributeValue(attribute), null, new HashMap<>(),\n                                new ArrayList<>(), new ArrayList<>())\n                        .getResults();\n\n                // tests\n                Assert.assertNotNull(results);\n                if (expectedUser.getAttributeValue(attribute).endsWith(\"_1\")) {\n\n                    // we created 12 users so the search of attribute_1 should return _1, _10 and _11\n                    Assert.assertEquals(\n                            \"Not the right number of result for <\" + attribute + \"> - value: \"\n                                    + expectedUser.getAttributeValue(attribute),\n                            3,\n                            results.size());\n                } else {\n                    Assert.assertEquals(\n                            \"Not the right number of result for <\" + attribute + \"> - value: \"\n                                    + expectedUser.getAttributeValue(attribute),\n                            1,\n                            results.size());\n                    assertUserEquals(userAttributesList, expectedUser, results.get(0));\n                }\n            }\n        }\n    }\n\n    public UserCreator buildEngineUser(final UserItem user) throws NumberFormatException {\n        if (user == null) {\n            throw new IllegalArgumentException(\"The user must be not null!\");\n        }\n\n        final UserCreator userCreator = new UserCreator(user.getAttributeValue(UserItem.ATTRIBUTE_USERNAME),\n                user.getAttributeValue(UserItem.ATTRIBUTE_PASSWORD))\n                .setFirstName(user.getAttributeValue(UserItem.ATTRIBUTE_FIRSTNAME))\n                .setLastName(user.getAttributeValue(UserItem.ATTRIBUTE_LASTNAME))\n                .setTitle(user.getAttributeValue(UserItem.ATTRIBUTE_TITLE))\n                .setIconPath(user.getAttributeValue(UserItem.ATTRIBUTE_ICON))\n                .setJobTitle(user.getAttributeValue(UserItem.ATTRIBUTE_JOB_TITLE));\n        // .setPersonalData(personalInfo.done())\n        // .setProfessionalData(professionalInfo.done());\n\n        final String managerId = user.getAttributeValue(UserItem.ATTRIBUTE_MANAGER_ID);\n        if (managerId != null && !managerId.isEmpty()) {\n            userCreator.setManagerUserId(Long.valueOf(managerId));\n        }\n        return userCreator;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/organization/APIUserIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.organization;\n\nimport static java.util.Collections.EMPTY_LIST;\nimport static java.util.Collections.EMPTY_MAP;\nimport static org.junit.Assert.assertTrue;\n\nimport org.bonitasoft.test.toolkit.organization.TestUser;\nimport org.bonitasoft.test.toolkit.organization.TestUserFactory;\nimport org.bonitasoft.web.rest.model.identity.UserItem;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.test.AbstractConsoleTest;\nimport org.junit.Test;\n\n/**\n * @author Colin PUY\n */\n@SuppressWarnings(\"unchecked\")\npublic class APIUserIT extends AbstractConsoleTest {\n\n    private static final String ASCENDING = \" asc\";\n    private static final String DESCENDING = \" desc\";\n\n    private APIUser apiUser;\n\n    @Override\n    public void consoleTestSetUp() throws Exception {\n        apiUser = new APIUser();\n        apiUser.setCaller(getAPICaller(getInitiator().getSession(), \"API/identity/user\"));\n\n    }\n\n    @Override\n    protected TestUser getInitiator() {\n        return TestUserFactory.getJohnCarpenter();\n    }\n\n    private boolean lowerThan(final String string1, final String string2) {\n        return string1.compareTo(string2) < 0;\n    }\n\n    private boolean upperThan(final String string1, final String string2) {\n        return string1.compareTo(string2) > 0;\n    }\n\n    @Test\n    public void searchCanBeOrderdByFirstNameAscending() throws Exception {\n        TestUserFactory.getRidleyScott();\n        TestUserFactory.getJohnCarpenter();\n\n        final ItemSearchResult<UserItem> searchResult = apiUser.runSearch(0, 10, null,\n                UserItem.ATTRIBUTE_FIRSTNAME + ASCENDING,\n                EMPTY_MAP, EMPTY_LIST, EMPTY_LIST);\n\n        final String firstUserFirstName = searchResult.getResults().get(0).getFirstName();\n        final String secondUserFirstName = searchResult.getResults().get(1).getFirstName();\n        assertTrue(lowerThan(firstUserFirstName, secondUserFirstName));\n    }\n\n    @Test\n    public void searchCanBeOrderdByFirstNameDescending() throws Exception {\n        TestUserFactory.getRidleyScott();\n        TestUserFactory.getJohnCarpenter();\n\n        final ItemSearchResult<UserItem> searchResult = apiUser.runSearch(0, 10, null,\n                UserItem.ATTRIBUTE_FIRSTNAME + DESCENDING,\n                EMPTY_MAP, EMPTY_LIST, EMPTY_LIST);\n\n        final String firstUserFirstName = searchResult.getResults().get(0).getFirstName();\n        final String secondUserFirstName = searchResult.getResults().get(1).getFirstName();\n        assertTrue(upperThan(firstUserFirstName, secondUserFirstName));\n    }\n\n    @Test\n    public void searchCanBeOrderdByLastNameAscending() throws Exception {\n        TestUserFactory.getRidleyScott();\n        TestUserFactory.getJohnCarpenter();\n\n        final ItemSearchResult<UserItem> searchResult = apiUser.runSearch(0, 10, null,\n                UserItem.ATTRIBUTE_LASTNAME + ASCENDING,\n                EMPTY_MAP, EMPTY_LIST, EMPTY_LIST);\n\n        final String firstUserLastName = searchResult.getResults().get(0).getLastName();\n        final String secondUserLastName = searchResult.getResults().get(1).getLastName();\n        assertTrue(lowerThan(firstUserLastName, secondUserLastName));\n    }\n\n    @Test\n    public void searchCanBeOrderdByLastNameDescending() throws Exception {\n        TestUserFactory.getRidleyScott();\n        TestUserFactory.getJohnCarpenter();\n\n        final ItemSearchResult<UserItem> searchResult = apiUser.runSearch(0, 10, null,\n                UserItem.ATTRIBUTE_LASTNAME + DESCENDING,\n                EMPTY_MAP, EMPTY_LIST, EMPTY_LIST);\n\n        final String firstUserLastName = searchResult.getResults().get(0).getLastName();\n        final String secondUserLastName = searchResult.getResults().get(1).getLastName();\n        assertTrue(upperThan(firstUserLastName, secondUserLastName));\n    }\n\n    @Test\n    public void searchCanBeOrderdByUserNameAscending() throws Exception {\n        TestUserFactory.getRidleyScott();\n        TestUserFactory.getJohnCarpenter();\n\n        final ItemSearchResult<UserItem> searchResult = apiUser.runSearch(0, 10, null,\n                UserItem.ATTRIBUTE_USERNAME + ASCENDING,\n                EMPTY_MAP, EMPTY_LIST, EMPTY_LIST);\n\n        final String firstUserUserName = searchResult.getResults().get(0).getUserName();\n        final String secondUserUserName = searchResult.getResults().get(1).getUserName();\n        assertTrue(lowerThan(firstUserUserName, secondUserUserName));\n    }\n\n    @Test\n    public void searchCanBeOrderdByUserNameDescending() throws Exception {\n        TestUserFactory.getRidleyScott();\n        TestUserFactory.getJohnCarpenter();\n\n        final ItemSearchResult<UserItem> searchResult = apiUser.runSearch(0, 10, null,\n                UserItem.ATTRIBUTE_USERNAME + DESCENDING,\n                EMPTY_MAP, EMPTY_LIST, EMPTY_LIST);\n\n        final String firstUserUserName = searchResult.getResults().get(0).getUserName();\n        final String secondUserUserName = searchResult.getResults().get(1).getUserName();\n        assertTrue(upperThan(firstUserUserName, secondUserUserName));\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/page/APIPageIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.page;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.web.rest.server.api.page.builder.PageItemBuilder.aPageItem;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.commons.io.FileUtils;\nimport org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils;\nimport org.bonitasoft.engine.api.PageAPI;\nimport org.bonitasoft.engine.api.PlatformAPIAccessor;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.io.FileContent;\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\nimport org.bonitasoft.test.toolkit.organization.TestUserFactory;\nimport org.bonitasoft.web.rest.model.portal.page.PageItem;\nimport org.bonitasoft.web.rest.server.datastore.page.PageDatastore;\nimport org.bonitasoft.web.test.AbstractConsoleTest;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.After;\nimport org.junit.Test;\n\npublic class APIPageIT extends AbstractConsoleTest {\n\n    public static final String NEW_PAGE_ZIP = \"/newPage.zip\";\n    private APIPage apiPage;\n\n    @After\n    public void cleanPages() throws Exception {\n        final SearchOptions searchOptions = new SearchOptionsBuilder(0, 100000).done();\n        for (Page page : getPageAPI().searchPages(searchOptions).getResult()) {\n            final List<APIID> ids = new ArrayList<>();\n            ids.add(APIID.makeAPIID(page.getId()));\n            apiPage.delete(ids);\n        }\n    }\n\n    @Override\n    public void consoleTestSetUp() throws Exception {\n        apiPage = new APIPage();\n        final APISession session = getInitiator().getSession();\n        apiPage.setCaller(getAPICaller(session, \"API/portal/page\"));\n    }\n\n    @Override\n    protected TestUser getInitiator() {\n        return TestUserFactory.getJohnCarpenter();\n    }\n\n    private PageAPI getPageAPI() throws Exception {\n        return TenantAPIAccessor.getCustomPageAPI(getInitiator().getSession());\n    }\n\n    @Test\n    public void runAdd_a_page_to_the_repository() throws Exception {\n        // Given\n        final PageItem expectedPage = aPageItem().build();\n        final PageItem addedPage = apiPage.add(expectedPage);\n\n        // Validate\n        assertNotNull(addedPage);\n        assertThat(addedPage.getUrlToken()).isEqualTo(expectedPage.getUrlToken());\n        assertThat(addedPage.getDescription()).isEqualTo(expectedPage.getDescription());\n        assertThat(addedPage.getDisplayName()).isEqualTo(expectedPage.getDisplayName());\n        assertThat(addedPage.getContentName()).isEqualTo(expectedPage.getContentName());\n\n    }\n\n    @Test\n    public void runGet_a_page_from_the_repository() throws Exception {\n\n        // Given\n        final PageItem expectedItem = addNewPage(NEW_PAGE_ZIP);\n\n        // When\n        final PageItem getItem = apiPage.get(expectedItem.getId());\n\n        // Validate\n        assertEquals(expectedItem.getUrlToken(), getItem.getUrlToken());\n    }\n\n    @Test(expected = APIException.class)\n    public void runGet_not_existing_page_rise_exception() throws Exception {\n        // When\n        final APIID notExistingPageId = aPageItem().build().getId();\n        apiPage.get(notExistingPageId);\n    }\n\n    @Test(expected = APIException.class)\n    public void runDelete_then_get_page_rise_exception() throws Exception {\n\n        // Given\n        final PageItem pageToBeRemoved = addNewPage(NEW_PAGE_ZIP);\n        final List<APIID> ids = new ArrayList<>();\n        ids.add(pageToBeRemoved.getId());\n\n        // When\n        apiPage.delete(ids);\n        apiPage.get(pageToBeRemoved.getId());\n    }\n\n    @Test\n    public void runUpdate_with_new_page_content_change_it() throws Exception {\n        // Given\n        final PageItem pageToBeUpdated = addNewPage(NEW_PAGE_ZIP);\n        String oldPageKey = pageToBeUpdated.getAttributeValue(PageDatastore.UNMAPPED_ATTRIBUTE_ZIP_FILE);\n        assertNotNull(oldPageKey);\n\n        //store new page zip into database\n        File file = new File(getClass().getResource(NEW_PAGE_ZIP).toURI());\n        String pageZipKey = PlatformAPIAccessor.getTemporaryContentAPI()\n                .storeTempFile(new FileContent(file.getName(), new FileInputStream(file), \"application/zip\"));\n\n        // When\n        final Map<String, String> attributes = new HashMap<>();\n        attributes.put(PageDatastore.UNMAPPED_ATTRIBUTE_ZIP_FILE, pageZipKey);\n\n        final PageItem updatedPage = apiPage.update(pageToBeUpdated.getId(), attributes);\n        // Validate\n        assertNotNull(updatedPage);\n        assertThat(updatedPage.getUrlToken()).as(\"url token\").isEqualTo(\"custompage_groovyexampletest\");\n        assertThat(updatedPage.getDisplayName()).as(\"display name\").isEqualTo(\"Groovy example page_test\");\n        assertThat(updatedPage.getDescription()).as(\"description\")\n                .isEqualTo(\"Groovy class example of custom page source structure (in English)._test\");\n\n    }\n\n    private PageItem addNewPage(String pageFileName) throws Exception {\n        final PageItem pageItem = aPageItem().build();\n        final URL zipFileUrl = getClass().getResource(pageFileName);\n\n        final File zipFile = new File(zipFileUrl.toURI());\n        FileUtils.copyFileToDirectory(zipFile, WebBonitaConstantsUtils.getTenantInstance().getTempFolder());\n\n        final byte[] pageContent = FileUtils.readFileToByteArray(new File(zipFileUrl.toURI()));\n        return addPageItemToRepository(pageItem.getContentName(), pageContent);\n    }\n\n    private PageItem addPageItemToRepository(final String pageContentName, final byte[] pageContent) throws Exception {\n        return aPageItem().fromEngineItem(getPageAPI().createPage(pageContentName, pageContent)).build();\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/CommentDatastoreIntegrationIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm;\n\nimport static org.hamcrest.MatcherAssert.assertThat;\nimport static org.hamcrest.Matchers.is;\n\nimport java.util.HashMap;\n\nimport org.bonitasoft.test.toolkit.bpm.TestCase;\nimport org.bonitasoft.test.toolkit.bpm.TestProcessFactory;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\nimport org.bonitasoft.test.toolkit.organization.TestUserFactory;\nimport org.bonitasoft.web.rest.model.bpm.cases.CommentItem;\nimport org.bonitasoft.web.rest.server.datastore.bpm.cases.CommentDatastore;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.test.AbstractConsoleTest;\nimport org.junit.Test;\n\npublic class CommentDatastoreIntegrationIT extends AbstractConsoleTest {\n\n    private CommentDatastore commentDatastore;\n\n    @Override\n    protected TestUser getInitiator() {\n        return TestUserFactory.getJohnCarpenter();\n    }\n\n    @Override\n    public void consoleTestSetUp() throws Exception {\n        commentDatastore = new CommentDatastore(TestUserFactory.getJohnCarpenter().getSession());\n    }\n\n    private TestCase aCase() {\n        return TestProcessFactory.getDefaultHumanTaskProcess().addActor(TestUserFactory.getJohnCarpenter()).startCase();\n    }\n\n    private HashMap<String, String> filterByCaseId(long caseId) {\n        HashMap<String, String> filters = new HashMap<>();\n        filters.put(CommentItem.ATTRIBUTE_PROCESS_INSTANCE_ID, String.valueOf(caseId));\n        return filters;\n    }\n\n    @Test\n    public void comment_datastore_can_do_a_paginated_search() throws Exception {\n        TestCase aCase = aCase();\n        aCase.addComment(\"Comment 1\");\n        aCase.addComment(\"Comment 2\");\n        aCase.addComment(\"Comment 3\");\n\n        final ItemSearchResult<CommentItem> results = commentDatastore.search(0, 2, null, null,\n                filterByCaseId(aCase.getId()));\n\n        assertThat(results.getTotal(), is(3L));\n        assertThat(results.getResults().size(), is(2));\n        assertThat(results.getResults().get(0).getContent(), is(\"Comment 1\"));\n        assertThat(results.getResults().get(1).getContent(), is(\"Comment 2\"));\n    }\n\n    @Test\n    public void comment_datastore_can_search_comments_with_special_characters() throws Exception {\n        String specialCharComment = \"#*Ã©Ã Ã¢Ã¤Ã«ÃªÃ©~Ã§ÃžÅ¡Å’Ã˜Ã�Ã†\";\n        TestCase aCase = aCase();\n        aCase.addComment(specialCharComment);\n\n        final ItemSearchResult<CommentItem> results = commentDatastore.search(0, 10, null, null,\n                filterByCaseId(aCase.getId()));\n\n        assertThat(results.getResults().get(0).getContent(), is(specialCharComment));\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/ArchivedCaseDatastoreIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.cases;\n\nimport static org.awaitility.Awaitility.await;\nimport static org.junit.Assert.assertEquals;\n\nimport java.util.HashMap;\nimport java.util.concurrent.TimeUnit;\n\nimport org.bonitasoft.test.toolkit.bpm.TestCase;\nimport org.bonitasoft.test.toolkit.bpm.TestProcess;\nimport org.bonitasoft.test.toolkit.bpm.TestProcessFactory;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\nimport org.bonitasoft.test.toolkit.organization.TestUserFactory;\nimport org.bonitasoft.web.rest.model.bpm.cases.ArchivedCaseItem;\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseItem;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.test.AbstractConsoleTest;\nimport org.junit.Test;\n\n/**\n * @author ROHART Bastien\n */\npublic class ArchivedCaseDatastoreIT extends AbstractConsoleTest {\n\n    private ArchivedCaseDatastore archivedCaseDatastore;\n\n    /*\n     * (non-Javadoc)\n     * @see org.bonitasoft.console.server.AbstractConsoleTest#consoleTestSetUp()\n     */\n    @Override\n    public void consoleTestSetUp() throws Exception {\n        archivedCaseDatastore = new ArchivedCaseDatastore(getInitiator().getSession());\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see org.bonitasoft.test.toolkit.AbstractJUnitTest#getInitiator()\n     */\n    @Override\n    protected TestUser getInitiator() {\n        return TestUserFactory.getJohnCarpenter();\n    }\n\n    @Test\n    public void twoPoolsWithOneWithACallActivityArchivedCaseTest() throws Exception {\n        TestProcess subprocess = TestProcessFactory.getDefaultHumanTaskProcess();\n        subprocess.addActor(getInitiator()).enable();\n\n        // start subprocess case via call activity\n        TestProcess rootProcess = TestProcessFactory.getCallActivityProcess(subprocess.getProcessDefinition());\n        var rootInstance = rootProcess.addActor(getInitiator()).enable().startCase();\n        await().atMost(5, TimeUnit.SECONDS).until(() -> !subprocess.listAllOpenCases().isEmpty());\n\n        // archive process 1 case\n        TestCase testCaseRootProcess = rootProcess.listOpenCases().get(0);\n        testCaseRootProcess.getNextHumanTask().assignTo(getInitiator()).executeUserTask(getInitiator());\n        // asynchronous, wait subprocess to be archived\n        await().atMost(5, TimeUnit.SECONDS).until(() -> rootInstance.getArchive() != null);\n\n        // Search for archived Cases\n        ItemSearchResult<ArchivedCaseItem> itemSearchResult = archivedCaseDatastore.search(0, 100, null, null,\n                new HashMap<>());\n\n        assertEquals(\"2 cases started but one via call activity so only 1 should be retrieved\", 1,\n                itemSearchResult.getResults().size());\n\n        // Search for archived Cases with caller any filter\n        HashMap<String, String> filters = new HashMap<>();\n        filters.put(CaseItem.FILTER_CALLER, \"any\");\n        ItemSearchResult<ArchivedCaseItem> anyCallerSearchResult = archivedCaseDatastore.search(0, 100, null, null,\n                filters);\n\n        assertEquals(\"Subprocesses should be retrieved as well\", 2,\n                anyCallerSearchResult.getResults().size());\n\n        TestProcessFactory.getInstance().delete(rootProcess);\n        TestProcessFactory.getInstance().delete(subprocess);\n    }\n\n    @Test\n    public void searchArchivedSubCases() throws Exception {\n        TestProcess subprocess = TestProcessFactory.getDefaultHumanTaskProcess();\n        subprocess.addActor(getInitiator()).enable();\n\n        // start subprocess case via call activity\n        TestProcess rootProcess = TestProcessFactory.getCallActivityProcess(subprocess.getProcessDefinition());\n        var rootInstance = rootProcess.addActor(getInitiator()).enable().startCase();\n        await().atMost(5, TimeUnit.SECONDS).until(() -> !subprocess.listAllOpenCases().isEmpty());\n\n        // archive process 1 case\n        TestCase testCaseRootProcess = rootProcess.listOpenCases().get(0);\n        testCaseRootProcess.getNextHumanTask().assignTo(getInitiator()).executeUserTask(getInitiator());\n        await().atMost(5, TimeUnit.SECONDS).until(() -> rootInstance.getArchive() != null);\n\n        // Filters for archived Cases\n        HashMap<String, String> filters = new HashMap<>();\n        filters.put(ArchivedCaseItem.ATTRIBUTE_ROOT_CASE_ID, String.valueOf(testCaseRootProcess.getId()));\n        filters.put(CaseItem.FILTER_CALLER, \"any\");\n        ItemSearchResult<ArchivedCaseItem> itemSearchResult = archivedCaseDatastore.search(0, 100, null, null,\n                filters);\n\n        assertEquals(\"Filtering on root case ID, only the subcase should be retrieved\", 1,\n                itemSearchResult.getResults().size());\n        assertEquals(subprocess.getProcessDefinition().getId(),\n                itemSearchResult.getResults().get(0).getProcessId().toLong().longValue());\n\n        TestProcessFactory.getInstance().delete(rootProcess);\n        TestProcessFactory.getInstance().delete(subprocess);\n    }\n\n    @Test\n    public void search_cancelled_archived_case() throws Exception {\n        TestProcess process = TestProcessFactory.getDefaultHumanTaskProcess();\n\n        // start process\n        var processInstance = process.addActor(getInitiator()).enable().startCase();\n        await().atMost(5, TimeUnit.SECONDS).until(() -> !process.listOpenCases().isEmpty());\n\n        // archive process 1 case\n        TestCase testCaseProcess = process.listOpenCases().get(0);\n\n        // cancel the case\n        processInstance.cancel();\n        // asynchronous, wait process to be archived\n        await().atMost(5, TimeUnit.SECONDS).until(() -> processInstance.getArchive() != null);\n\n        // Search for cancelled archived Cases\n        ItemSearchResult<ArchivedCaseItem> searchResult = archivedCaseDatastore.search(0, 100, null, null,\n                new HashMap<>());\n\n        assertEquals(1,\n                searchResult.getResults().size());\n\n        // Search for cancelled archived Cases with caller any filter\n        HashMap<String, String> filters = new HashMap<>();\n        filters.put(CaseItem.FILTER_CALLER, \"any\");\n        ItemSearchResult<ArchivedCaseItem> anyCallerSearchResult = archivedCaseDatastore.search(0, 100, null, null,\n                filters);\n\n        assertEquals(1,\n                anyCallerSearchResult.getResults().size());\n\n        TestProcessFactory.getInstance().delete(process);\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/CaseDatastoreIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.cases;\n\nimport static org.junit.Assert.assertEquals;\n\nimport java.util.HashMap;\n\nimport org.bonitasoft.test.toolkit.bpm.TestCase;\nimport org.bonitasoft.test.toolkit.bpm.TestProcess;\nimport org.bonitasoft.test.toolkit.bpm.TestProcessFactory;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\nimport org.bonitasoft.test.toolkit.organization.TestUserFactory;\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseItem;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.test.AbstractConsoleTest;\nimport org.junit.Test;\n\n/**\n * @author ROHART Bastien\n */\npublic class CaseDatastoreIT extends AbstractConsoleTest {\n\n    private CaseDatastore caseDatastore;\n\n    @Override\n    public void consoleTestSetUp() throws Exception {\n        caseDatastore = new CaseDatastore(getInitiator().getSession());\n    }\n\n    @Override\n    protected TestUser getInitiator() {\n        return TestUserFactory.getJohnCarpenter();\n    }\n\n    @Test\n    public void twoPoolsWithOneWithACallActivityCaseTest() throws Exception {\n        final long before = caseDatastore.search(0, 100, null, null, new HashMap<>()).getTotal();\n        final TestProcess process2 = TestProcessFactory.getDefaultHumanTaskProcess();\n        process2.addActor(getInitiator());\n        process2.enable();\n\n        // start process1 case via call activity\n        final TestProcess process1 = TestProcessFactory.getCallActivityProcess(process2.getProcessDefinition());\n        final TestCase parentCase = process1.addActor(getInitiator()).startCase();\n\n        //wait for process instance to be in a \"stable\" state\n        parentCase.getNextHumanTask();\n        // Filters for Opened Cases\n        final ItemSearchResult<CaseItem> itemSearchResult = caseDatastore.search(0, 100, null, null, new HashMap<>());\n\n        assertEquals(\"2 cases started but one via call activity so only 1 should be opened\", 1,\n                itemSearchResult.getResults().size() - before);\n\n        TestProcessFactory.getInstance().delete(process1);\n        TestProcessFactory.getInstance().delete(process2);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/HumanTaskDatastoreIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.flownode;\n\nimport static org.bonitasoft.web.toolkit.client.data.APIID.makeAPIID;\nimport static org.junit.Assert.assertEquals;\n\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.test.toolkit.bpm.TestHumanTask;\nimport org.bonitasoft.test.toolkit.bpm.TestProcessFactory;\nimport org.bonitasoft.web.rest.model.bpm.flownode.HumanTaskItem;\nimport org.bonitasoft.web.test.AbstractConsoleTest;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ValidatorEngine;\nimport org.junit.Test;\n\npublic class HumanTaskDatastoreIT extends AbstractConsoleTest {\n\n    private HumanTaskDatastore humanTaskDatastore;\n\n    @Override\n    public void consoleTestSetUp() throws Exception {\n        humanTaskDatastore = new HumanTaskDatastore(getInitiator().getSession());\n    }\n\n    private HumanTaskItem fetchHumanTask(final long taskId) throws Exception {\n        final HumanTaskInstance humanTaskInstance = (HumanTaskInstance) TenantAPIAccessor\n                .getProcessAPI(getInitiator().getSession()).getActivityInstance(taskId);\n        return HumanTaskDatastore.fillConsoleItem(new HumanTaskItem(), humanTaskInstance);\n    }\n\n    @Test\n    public void task_priority_can_be_changed() throws Exception {\n        final TestHumanTask humanTask = TestProcessFactory.getDefaultHumanTaskProcess().addActor(getInitiator())\n                .startCase().getNextHumanTask();\n        final HumanTaskItem humanTaskItem = new HumanTaskItem();\n        humanTaskItem.setId(makeAPIID(humanTask.getId()));\n        humanTaskItem.setPriority(HumanTaskItem.VALUE_PRIORITY_ABOVE_NORMAL);\n        ValidatorEngine.validate(humanTaskItem);\n\n        humanTaskDatastore.update(humanTaskItem.getId(), humanTaskItem.getAttributes());\n\n        final HumanTaskItem fetchedTask = fetchHumanTask(humanTask.getId());\n        assertEquals(HumanTaskItem.VALUE_PRIORITY_ABOVE_NORMAL, fetchedTask.getPriority());\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/engineclient/CaseEngineClientIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.engineclient;\n\nimport static junit.framework.Assert.assertEquals;\n\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.test.toolkit.bpm.TestCaseFactory;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\nimport org.bonitasoft.test.toolkit.organization.TestUserFactory;\nimport org.bonitasoft.web.test.AbstractConsoleTest;\nimport org.junit.Test;\n\n/**\n * @author Colin PUY\n */\npublic class CaseEngineClientIT extends AbstractConsoleTest {\n\n    private CaseEngineClient caseEngineClient;\n\n    @Override\n    public void consoleTestSetUp() throws Exception {\n        caseEngineClient = new CaseEngineClient(TenantAPIAccessor.getProcessAPI(getInitiator().getSession()));\n    }\n\n    @Override\n    protected TestUser getInitiator() {\n        return TestUserFactory.getJohnCarpenter();\n    }\n\n    @Test\n    public void testCountNumberOfOpenCases() throws Exception {\n        final long before = caseEngineClient.countOpenedCases();\n        start2cases();\n\n        long numberOfOpenCases = caseEngineClient.countOpenedCases();\n\n        assertEquals(2L, numberOfOpenCases - before);\n    }\n\n    private void start2cases() {\n        TestCaseFactory.createRandomCase(getInitiator());\n        TestCaseFactory.createRandomCase(getInitiator());\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/engineclient/HumanTaskEngineClientIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.engineclient;\n\nimport static junit.framework.Assert.assertEquals;\n\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.test.toolkit.bpm.TestCaseFactory;\nimport org.bonitasoft.test.toolkit.bpm.TestHumanTask;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\nimport org.bonitasoft.test.toolkit.organization.TestUserFactory;\nimport org.bonitasoft.web.rest.model.bpm.flownode.HumanTaskItem;\nimport org.bonitasoft.web.test.AbstractConsoleTest;\nimport org.junit.Test;\n\n/**\n * @author Colin PUY\n */\npublic class HumanTaskEngineClientIT extends AbstractConsoleTest {\n\n    private HumanTaskEngineClient humanTaskEngineClient;\n\n    @Override\n    public void consoleTestSetUp() throws Exception {\n        humanTaskEngineClient = new HumanTaskEngineClient(TenantAPIAccessor.getProcessAPI(getInitiator().getSession()));\n    }\n\n    @Override\n    protected TestUser getInitiator() {\n        return TestUserFactory.getJohnCarpenter();\n    }\n\n    @Test\n    public void testCountOpenedTasks() throws Exception {\n        final long before = humanTaskEngineClient.countOpenedHumanTasks();\n        create2openedTasks();\n\n        long openedTasks = humanTaskEngineClient.countOpenedHumanTasks();\n\n        assertEquals(2L, openedTasks - before);\n    }\n\n    private void create2openedTasks() throws InterruptedException {\n        TestHumanTask task1 = TestCaseFactory.createRandomCase(getInitiator()).getNextHumanTask()\n                .assignTo(getInitiator());\n        TestHumanTask task2 = TestCaseFactory.createRandomCase(getInitiator()).getNextHumanTask()\n                .assignTo(getInitiator());\n        task1.waitState(HumanTaskItem.VALUE_STATE_READY);\n        task2.waitState(HumanTaskItem.VALUE_STATE_READY);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/engineclient/ProcessEngineClientIT.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.engineclient;\n\nimport static java.util.Arrays.asList;\nimport static junit.framework.Assert.assertEquals;\nimport static junit.framework.Assert.assertNotNull;\nimport static org.junit.Assert.assertNull;\n\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.test.toolkit.bpm.TestProcess;\nimport org.bonitasoft.test.toolkit.bpm.TestProcessFactory;\nimport org.bonitasoft.test.toolkit.organization.TestUser;\nimport org.bonitasoft.test.toolkit.organization.TestUserFactory;\nimport org.bonitasoft.web.test.AbstractConsoleTest;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException;\nimport org.junit.Test;\n\n/**\n * @author Colin PUY\n */\npublic class ProcessEngineClientIT extends AbstractConsoleTest {\n\n    private ProcessEngineClient processEngineClient;\n\n    @Override\n    public void consoleTestSetUp() throws Exception {\n        processEngineClient = new ProcessEngineClient(TenantAPIAccessor.getProcessAPI(getInitiator().getSession()));\n    }\n\n    @Override\n    protected TestUser getInitiator() {\n        return TestUserFactory.getJohnCarpenter();\n    }\n\n    private ProcessDefinition getProcessDefinition(final long processId) throws Exception {\n        try {\n            return TenantAPIAccessor.getProcessAPI(getInitiator().getSession()).getProcessDefinition(processId);\n        } catch (final ProcessDefinitionNotFoundException e) {\n            return null;\n        }\n    }\n\n    @Test\n    public void testCountResolvedProcesses() throws Exception {\n        final long before = processEngineClient.countResolvedProcesses();\n        create2resolvedProcesses();\n\n        final long resolvedProcesses = processEngineClient.countResolvedProcesses();\n\n        assertEquals(2L, resolvedProcesses - before);\n    }\n\n    private void create2resolvedProcesses() {\n        TestProcessFactory.createRandomResolvedProcess(getInitiator());\n        TestProcessFactory.createRandomResolvedProcess(getInitiator());\n    }\n\n    @Test\n    public void testDeleteProcesses() throws Exception {\n        final TestProcess deployedProcess = TestProcessFactory.getDefaultHumanTaskProcess().addActor(getInitiator());\n\n        processEngineClient.deleteDisabledProcesses(asList(deployedProcess.getId()));\n\n        assertNull(getProcessDefinition(deployedProcess.getId()));\n        TestProcessFactory.getInstance().remove(deployedProcess);\n    }\n\n    @Test(expected = APIItemNotFoundException.class)\n    public void getProcessDeploymentInfo_return_api_not_found_exception_if_process_is_not_found() throws Exception {\n        final long unknownProcessId = 1L;\n\n        processEngineClient.getProcessDeploymentInfo(unknownProcessId);\n    }\n\n    @Test\n    public void getProcessDeploymentInfo_return_info_if_process_is_found() throws Exception {\n        final TestProcess deployedProcess = TestProcessFactory.getDefaultHumanTaskProcess().addActor(getInitiator());\n\n        final ProcessDeploymentInfo processDeploymentInfo = processEngineClient\n                .getProcessDeploymentInfo(deployedProcess.getId());\n\n        assertNotNull(processDeploymentInfo);\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-integration-tests-web/src/test/resources/appLinkDescriptor.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<applications xmlns=\"http://documentation.bonitasoft.com/application-xml-schema/1.1\">\n  <applicationLink token=\"app1\" version=\"1.0\" profile=\"User\" state=\"ACTIVATED\">\n    <displayName>Application 1</displayName>\n    <description>Description of Application 1</description>\n    <iconPath>/app1.jpg</iconPath>\n  </applicationLink>\n</applications>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/build.gradle",
    "content": "plugins { id 'bonita-docker-database' }\n\ndependencies {\n    testAnnotationProcessor libs.lombok\n    testImplementation libs.lombok\n    testImplementation libs.assertj\n    testImplementation libs.h2\n    testImplementation libs.springBeans\n    testImplementation libs.springTx\n    testImplementation libs.springTest\n    testImplementation libs.springJdbc\n    testImplementation libs.springOrm\n    testImplementation project(\":bpm:bonita-server\")\n    testImplementation project(\":bpm:bonita-common\")\n\n    testRuntimeOnly libs.tomcatDbcp\n}\n\ndatabaseIntegrationTest { include \"**/*Test.class\" }\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/application/ApplicationQueriesTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.application;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.test.persistence.builder.ApplicationBuilder.anApplication;\nimport static org.bonitasoft.engine.test.persistence.builder.ApplicationMenuBuilder.anApplicationMenu;\nimport static org.bonitasoft.engine.test.persistence.builder.ApplicationPageBuilder.anApplicationPage;\nimport static org.bonitasoft.engine.test.persistence.builder.GroupBuilder.aGroup;\nimport static org.bonitasoft.engine.test.persistence.builder.PageBuilder.aPage;\nimport static org.bonitasoft.engine.test.persistence.builder.ProfileBuilder.aProfile;\nimport static org.bonitasoft.engine.test.persistence.builder.ProfileMemberBuilder.aProfileMember;\nimport static org.bonitasoft.engine.test.persistence.builder.RoleBuilder.aRole;\nimport static org.bonitasoft.engine.test.persistence.builder.UserBuilder.aUser;\nimport static org.bonitasoft.engine.test.persistence.builder.UserMembershipBuilder.aUserMembership;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.business.application.model.AbstractSApplication;\nimport org.bonitasoft.engine.business.application.model.SApplication;\nimport org.bonitasoft.engine.business.application.model.SApplicationMenu;\nimport org.bonitasoft.engine.business.application.model.SApplicationPage;\nimport org.bonitasoft.engine.business.application.model.SApplicationWithIcon;\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.identity.model.SRole;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.page.AbstractSPage;\nimport org.bonitasoft.engine.profile.model.SProfile;\nimport org.bonitasoft.engine.test.persistence.repository.ApplicationRepository;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.transaction.annotation.Transactional;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@RunWith(SpringRunner.class)\n@ContextConfiguration(locations = { \"/testContext.xml\" })\n@Transactional\npublic class ApplicationQueriesTest {\n\n    @Autowired\n    private ApplicationRepository repository;\n\n    @Test\n    public void getApplicationByToken_returns_the_application_with_the_given_token() {\n        //given\n        repository.add(anApplication().withToken(\"app1\").withDisplayName(\"my app1\").withDisplayName(\"my app1\")\n                .withVersion(\"1.0\").withPath(\"app1\").build());\n        final AbstractSApplication application2 = repository\n                .add(anApplication().withToken(\"app2\").withDisplayName(\"my app2\").withDisplayName(\"my app2\")\n                        .withDisplayName(\"my app2\").withVersion(\"1.0\").withPath(\"/app2\")\n                        .build());\n        repository.add(anApplication().withToken(\"app3\").withDisplayName(\"my app3\").withDisplayName(\"my app3\")\n                .withVersion(\"1.0\").withPath(\"app3\").build());\n\n        //when\n        final SApplication retrievedApp = repository.getApplicationByToken(\"app2\");\n\n        //then\n        assertThat(retrievedApp).isEqualTo(repository.getById(SApplication.class, application2.getId()));\n    }\n\n    @Test\n    public void getApplication_returns_the_application_with_the_given_id() {\n        //given\n        repository.add(anApplication().withToken(\"app1\").withDisplayName(\"my app1\").withDisplayName(\"my app1\")\n                .withVersion(\"1.0\").withPath(\"app1\").build());\n        final AbstractSApplication application2 = repository\n                .add(anApplication().withToken(\"app2\").withDisplayName(\"my app2\").withDisplayName(\"my app2\")\n                        .withVersion(\"1.0\").withPath(\"app1\").build());\n        repository.add(anApplication().withToken(\"app3\").withDisplayName(\"my app3\").withDisplayName(\"my app3\")\n                .withVersion(\"1.0\").withPath(\"app1\").build());\n\n        //when\n        final SApplicationWithIcon retrievedApp = repository.getById(SApplicationWithIcon.class, application2.getId());\n\n        //then\n        assertThat(retrievedApp).isEqualTo(application2);\n    }\n\n    @Test\n    public void getApplicationPageById_should_return_the_applicationPage_identified_by_the_given_id() {\n        //given\n        final AbstractSApplication application1 = repository\n                .add(anApplication().withToken(\"app1\").withDisplayName(\"my app1\").withDisplayName(\"my app\")\n                        .withVersion(\"1.0\").withPath(\"/app1\").build());\n        final AbstractSPage page = repository\n                .add(aPage().withName(\"MyPage\").withContent(\"The content\".getBytes()).build());\n        repository.add(anApplicationPage().withToken(\"FirstPage\").withApplicationId(application1.getId())\n                .withPageId(page.getId()).build());\n        final SApplicationPage secondPageApp = repository\n                .add(anApplicationPage().withToken(\"SecondPage\").withApplicationId(application1.getId())\n                        .withPageId(page.getId()).build());\n        repository.add(anApplicationPage().withToken(\"ThirdPage\").withApplicationId(application1.getId())\n                .withPageId(page.getId()).build());\n\n        //when\n        final SApplicationPage retrievedAppPage = repository.getApplicationPage(secondPageApp.getId());\n\n        //then\n        assertThat(retrievedAppPage).isEqualTo(secondPageApp);\n    }\n\n    @Test\n    public void getApplicationPageByNameAnApplicationName_should_return_the_applicationPage_with_the_given_name_in_the_given_application() {\n        //given\n        final AbstractSApplication application1 = repository\n                .add(anApplication().withToken(\"app1\").withDisplayName(\"my app1\").withDisplayName(\"my app\")\n                        .withVersion(\"1.0\").withPath(\"/app1\")\n                        .build());\n        final AbstractSApplication application2 = repository\n                .add(anApplication().withToken(\"app2\").withDisplayName(\"my app2\").withDisplayName(\"my app\")\n                        .withVersion(\"1.0\").withPath(\"/app2\")\n                        .build());\n        final AbstractSPage page = repository\n                .add(aPage().withName(\"MyPage\").withContent(\"The content\".getBytes()).build());\n        repository.add(anApplicationPage().withToken(\"FirstPage\").withApplicationId(application1.getId())\n                .withPageId(page.getId()).build());\n        final SApplicationPage secondPageApp1 = repository\n                .add(anApplicationPage().withToken(\"SecondPage\").withApplicationId(application1.getId())\n                        .withPageId(page.getId()).build());\n        repository.add(anApplicationPage().withToken(\"FirstPage\").withApplicationId(application2.getId())\n                .withPageId(page.getId()).build());\n        repository.add(anApplicationPage().withToken(\"SecondPage\").withApplicationId(application2.getId())\n                .withPageId(page.getId()).build());\n\n        //when\n        final SApplicationPage retrievedAppPage = repository.getApplicationPageByTokenAndApplicationToken(\"app1\",\n                \"SecondPage\");\n\n        //then\n        assertThat(retrievedAppPage).isEqualTo(secondPageApp1);\n    }\n\n    @Test\n    public void getApplicationPageByTokenAndApplicationId_should_return_the_applicationPage_with_the_given_name_in_the_given_application() {\n        //given\n        final AbstractSApplication application1 = repository\n                .add(anApplication().withToken(\"app1\").withDisplayName(\"my app1\").withDisplayName(\"my app\")\n                        .withVersion(\"1.0\").withPath(\"/app1\")\n                        .build());\n        final AbstractSApplication application2 = repository\n                .add(anApplication().withToken(\"app2\").withDisplayName(\"my app2\").withDisplayName(\"my app\")\n                        .withVersion(\"1.0\").withPath(\"/app2\")\n                        .build());\n        final AbstractSPage page = repository\n                .add(aPage().withName(\"MyPage\").withContent(\"The content\".getBytes()).build());\n        repository.add(anApplicationPage().withToken(\"FirstPage\").withApplicationId(application1.getId())\n                .withPageId(page.getId()).build());\n        final SApplicationPage secondPageApp1 = repository\n                .add(anApplicationPage().withToken(\"SecondPage\").withApplicationId(application1.getId())\n                        .withPageId(page.getId()).build());\n        repository.add(anApplicationPage().withToken(\"FirstPage\").withApplicationId(application2.getId())\n                .withPageId(page.getId()).build());\n        repository.add(anApplicationPage().withToken(\"SecondPage\").withApplicationId(application2.getId())\n                .withPageId(page.getId()).build());\n\n        //when\n        final SApplicationPage retrievedAppPage = repository\n                .getApplicationPageByTokenAndApplicationId(application1.getId(), \"SecondPage\");\n\n        //then\n        assertThat(retrievedAppPage).isEqualTo(secondPageApp1);\n    }\n\n    @Test\n    public void getAllPagesForProfile_should_return_layouts_and_all_pages_related_to_application() {\n        //given\n        //used by app1 and app2\n        SProfile firstProfile = repository.add(aProfile().withName(\"firstProfile\").build());\n\n        //used by app3\n        SProfile secondProfile = repository.add(aProfile().withName(\"secondProfile\").build());\n\n        //not used\n        SProfile thirdProfile = repository.add(aProfile().withName(\"thirdProfile\").build());\n\n        AbstractSPage layoutApp1 = repository\n                .add(aPage().withName(\"layoutApp1\").withContent(\"The content\".getBytes()).build());\n        AbstractSPage themeApp2 = repository\n                .add(aPage().withName(\"themeApp2\").withContent(\"The content\".getBytes()).build());\n        AbstractSPage layoutApp4 = repository\n                .add(aPage().withName(\"layoutApp4\").withContent(\"The content\".getBytes()).build());\n        AbstractSPage themeApp4 = repository\n                .add(aPage().withName(\"themeApp4\").withContent(\"The content\".getBytes()).build());\n        final AbstractSApplication application1 = repository.add(anApplication().withToken(\"app1\")\n                .withDisplayName(\"my app1\")\n                .withDisplayName(\"my app1\")\n                .withVersion(\"1.0\").withPath(\"/app1\").withProfile(firstProfile.getId()).withLayout(layoutApp1.getId())\n                .build());\n        final AbstractSApplication application2 = repository.add(anApplication().withToken(\"app2\")\n                .withDisplayName(\"my app2\")\n                .withDisplayName(\"my app2\")\n                .withVersion(\"1.0\").withPath(\"/app2\").withProfile(firstProfile.getId()).withTheme(themeApp2.getId())\n                .build());\n        final AbstractSApplication application3 = repository\n                .add(anApplication().withToken(\"app3\").withDisplayName(\"my app3\").withDisplayName(\"my app3\")\n                        .withVersion(\"1.0\").withPath(\"/app3\").withProfile(secondProfile.getId())\n                        .build());\n        final AbstractSApplication application4 = repository.add(anApplication().withToken(\"app4\")\n                .withDisplayName(\"my app4\")\n                .withDisplayName(\"my app4\")\n                .withVersion(\"1.0\").withPath(\"/app4\").withLayout(layoutApp4.getId()).withTheme(themeApp4.getId())\n                .build());\n\n        final AbstractSPage page1 = repository\n                .add(aPage().withName(\"page1\").withContent(\"The content\".getBytes()).build());\n        final AbstractSPage page2 = repository\n                .add(aPage().withName(\"page2\").withContent(\"The content\".getBytes()).build());\n        final AbstractSPage page3 = repository\n                .add(aPage().withName(\"page3\").withContent(\"The content\".getBytes()).build());\n        final AbstractSPage page4 = repository\n                .add(aPage().withName(\"page4\").withContent(\"The content\".getBytes()).build());\n        final AbstractSPage page5 = repository\n                .add(aPage().withName(\"page5\").withContent(\"The content\".getBytes()).build());\n        final AbstractSPage page6 = repository\n                .add(aPage().withName(\"page6\").withContent(\"The content\".getBytes()).build());\n\n        //app1 has layout layoutApp1 and references page1 and page2\n        repository.add(anApplicationPage().withToken(\"FirstPageApp1\").withApplicationId(application1.getId())\n                .withPageId(page1.getId()).build());\n        repository.add(anApplicationPage().withToken(\"SecondPageApp1\").withApplicationId(application1.getId())\n                .withPageId(page1.getId()).build());\n        repository.add(anApplicationPage().withToken(\"ThirdPageApp1\").withApplicationId(application1.getId())\n                .withPageId(page2.getId()).build());\n\n        //app2 has layout themeApp2 and references page3 and page4\n        repository.add(anApplicationPage().withToken(\"FirstPageApp2\").withApplicationId(application2.getId())\n                .withPageId(page3.getId()).build());\n        repository.add(anApplicationPage().withToken(\"SecondPageApp2\").withApplicationId(application2.getId())\n                .withPageId(page4.getId()).build());\n\n        //app3 has no layout and references page4 and page5\n        repository.add(anApplicationPage().withToken(\"FirstPageApp3\").withApplicationId(application3.getId())\n                .withPageId(page4.getId()).build());\n        repository.add(anApplicationPage().withToken(\"SecondPageApp3\").withApplicationId(application3.getId())\n                .withPageId(page5.getId()).build());\n\n        //app3 has layout layoutApp4, themeApp4 and references page6\n        repository.add(anApplicationPage().withToken(\"FirstPageApp4\").withApplicationId(application4.getId())\n                .withPageId(page6.getId()).build());\n\n        //when\n        List<String> pagesForProfile = repository.getAllPagesForProfile(firstProfile.getId());\n\n        //then\n        assertThat(pagesForProfile).containsExactlyInAnyOrder(\"layoutApp1\", \"page1\", \"page2\", \"page3\", \"page4\",\n                \"themeApp2\");\n\n        //when\n        pagesForProfile = repository.getAllPagesForProfile(secondProfile.getId());\n\n        //then\n        assertThat(pagesForProfile).containsExactlyInAnyOrder(\"page4\", \"page5\");\n\n        //when\n        pagesForProfile = repository.getAllPagesForProfile(thirdProfile.getId());\n\n        //then\n        assertThat(pagesForProfile).isEmpty();\n    }\n\n    @Test\n    public void getApplicationHomePage_should_return_the_applicationPage_set_as_home_page_for_the_given_application() {\n        //given\n        final AbstractSApplication application = repository\n                .add(anApplication().withToken(\"app1\").withDisplayName(\"my app1\").withDisplayName(\"my app\")\n                        .withVersion(\"1.0\").withPath(\"/app1\")\n                        .build());\n        final AbstractSPage page = repository\n                .add(aPage().withName(\"MyPage\").withContent(\"The content\".getBytes()).build());\n        final SApplicationPage firstPage = repository\n                .add(anApplicationPage().withToken(\"FirstPage\").withApplicationId(application.getId())\n                        .withPageId(page.getId()).build());\n        repository.add(anApplicationPage().withToken(\"SecondPage\").withApplicationId(application.getId())\n                .withPageId(page.getId()).build());\n\n        application.setHomePageId(firstPage.getId());\n        repository.update(application);\n\n        //when\n        final SApplicationPage retrievedAppPage = repository.getApplicationHomePage(application.getId());\n\n        //then\n        assertThat(retrievedAppPage).isEqualTo(firstPage);\n    }\n\n    @Test\n    public void getApplicationMenu_by_id_should_return_the_application_menu_identified_by_the_given_id() {\n        //given\n        final AbstractSApplication application = repository\n                .add(anApplication().withToken(\"app1\").withDisplayName(\"my app1\").withDisplayName(\"my app\")\n                        .withVersion(\"1.0\").withPath(\"/app1\")\n                        .build());\n        final AbstractSPage page = repository\n                .add(aPage().withName(\"MyPage\").withContent(\"The content\".getBytes()).build());\n        final SApplicationPage appPage = repository\n                .add(anApplicationPage().withToken(\"FirstPage\").withApplicationId(application.getId())\n                        .withPageId(page.getId()).build());\n        final SApplicationMenu menu = repository\n                .add(anApplicationMenu().withApplicationId(application.getId()).withApplicationPageId(appPage.getId())\n                        .withDisplayName(\"menu app1\").withIndex(1)\n                        .build());\n\n        //when\n        final SApplicationMenu retrievedMenu = repository.getApplicationMenu(menu.getId());\n\n        //then\n        assertThat(retrievedMenu).isEqualTo(menu);\n    }\n\n    @Test\n    public void getLastIndexForRootMenu_should_return_last_used_index() {\n        //given\n        final AbstractSApplication application = repository\n                .add(anApplication().withToken(\"app1\").withDisplayName(\"my app1\").withDisplayName(\"my app\")\n                        .withVersion(\"1.0\").withPath(\"/app1\")\n                        .build());\n        final AbstractSPage page = repository\n                .add(aPage().withName(\"MyPage\").withContent(\"The content\".getBytes()).build());\n        final SApplicationPage appPage = repository\n                .add(anApplicationPage().withToken(\"FirstPage\").withApplicationId(application.getId())\n                        .withPageId(page.getId()).build());\n        repository.add(anApplicationMenu().withApplicationId(application.getId()).withApplicationPageId(appPage.getId())\n                .withDisplayName(\"menu app1\")\n                .withIndex(1)\n                .build());\n\n        //when\n        final int lastIndex = repository.getLastIndexForRootMenu();\n\n        //then\n        assertThat(lastIndex).isEqualTo(1);\n    }\n\n    @Test\n    public void getLastIndexForChildMenu_should_return_last_used_index_by_children_of_a_given_parent() {\n        //given\n        final AbstractSApplication application = repository\n                .add(anApplication().withToken(\"app1\").withDisplayName(\"my app1\").withDisplayName(\"my app\")\n                        .withVersion(\"1.0\").withPath(\"/app1\")\n                        .build());\n        final AbstractSPage page = repository\n                .add(aPage().withName(\"MyPage\").withContent(\"The content\".getBytes()).build());\n        final SApplicationPage appPage = repository\n                .add(anApplicationPage().withToken(\"FirstPage\").withApplicationId(application.getId())\n                        .withPageId(page.getId()).build());\n        final SApplicationMenu parentMenu = repository\n                .add(anApplicationMenu().withApplicationId(application.getId()).withApplicationPageId(appPage.getId())\n                        .withDisplayName(\"menu app1\").withIndex(1)\n                        .build());\n        repository.add(anApplicationMenu().withApplicationId(application.getId()).withParentId(parentMenu.getId())\n                .withApplicationPageId(appPage.getId())\n                .withDisplayName(\"menu app1\").withIndex(1)\n                .build());\n\n        //when\n        final int lastIndex = repository.getLastIndexForChildOf(parentMenu.getId());\n\n        //then\n        assertThat(lastIndex).isEqualTo(1);\n    }\n\n    @Test\n    public void getApplicationOfUser_returns_the_application_for_the_given_user() {\n        //given\n        SUser user1 = repository.add(aUser().withId(1L).withUserName(\"walter.bates\").build());\n        SUser user2 = repository.add(aUser().withId(2L).withUserName(\"helen.kelly\").build());\n        SUser user3 = repository.add(aUser().withId(3L).withUserName(\"daniela.angelo\").build());\n        SUser user4 = repository.add(aUser().withId(4L).withUserName(\"jan.fisher\").build());\n\n        final SProfile profile1 = repository.add(aProfile().withName(\"firstProfile\").build());\n        repository.add(aProfileMember().withUserId(user1.getId()).withProfileId(profile1.getId()).build());\n        repository.add(aProfileMember().withUserId(user3.getId()).withProfileId(profile1.getId()).build());\n\n        final SProfile profile2 = repository.add(aProfile().withName(\"secondProfile\").build());\n        repository.add(aProfileMember().withUserId(user2.getId()).withProfileId(profile2.getId()).build());\n        repository.add(aProfileMember().withUserId(user3.getId()).withProfileId(profile2.getId()).build());\n\n        long application1 = repository\n                .add(anApplication().withToken(\"app1\").withDisplayName(\"my app1\").withDisplayName(\"my app1\")\n                        .withVersion(\"1.0\").withPath(\"app1\").withProfile(profile1.getId()).build())\n                .getId();\n        long application2 = repository\n                .add(anApplication().withToken(\"app2\").withDisplayName(\"my app2\").withDisplayName(\"my app2\")\n                        .withDisplayName(\"my app2\").withVersion(\"1.0\").withPath(\"/app2\").withProfile(profile1.getId())\n                        .build())\n                .getId();\n        long application3 = repository\n                .add(anApplication().withToken(\"app3\").withDisplayName(\"my app3\").withDisplayName(\"my app3\")\n                        .withVersion(\"1.0\").withPath(\"app3\").withProfile(profile2.getId()).build())\n                .getId();\n\n        repository.flush();\n\n        //then\n        assertThat(repository.getNumberOfApplicationOfUser(user1.getId())).isEqualTo(2);\n        assertThat(repository.getNumberOfApplicationOfUser(user2.getId())).isEqualTo(1);\n        assertThat(repository.getNumberOfApplicationOfUser(user3.getId())).isEqualTo(3);\n        assertThat(repository.getNumberOfApplicationOfUser(user4.getId())).isEqualTo(0);\n        assertThat(repository.getNumberOfApplicationOfUser(5L)).isEqualTo(0);\n        assertThat(repository.searchApplicationOfUser(user1.getId())).extracting(\"id\").containsExactlyInAnyOrder(\n                application1,\n                application2);\n        assertThat(repository.searchApplicationOfUser(user2.getId())).extracting(\"id\").containsExactly(application3);\n        assertThat(repository.searchApplicationOfUser(user3.getId())).extracting(\"id\").containsExactlyInAnyOrder(\n                application1,\n                application2,\n                application3);\n        assertThat(repository.searchApplicationOfUser(user4.getId())).isEmpty();\n        assertThat(repository.searchApplicationOfUser(5L)).isEmpty();\n\n    }\n\n    @Test\n    public void getApplicationOfUser_returns_the_application_only_once_even_if_mapped_twice() {\n        //given\n        SUser user = repository.add(aUser().withId(1L).withUserName(\"walter.bates\").build());\n        SGroup group = repository.add(aGroup().forGroupId(20L).forGroupName(\"Group1\").build());\n        repository.add(aUserMembership().forUser(user.getId()).memberOf(group.getId(), -1L).build());\n        final SProfile profile1 = repository.add(aProfile().withName(\"firstProfile\").build());\n        repository.add(aProfileMember().withUserId(user.getId()).withProfileId(profile1.getId()).build());\n\n        repository.add(aProfileMember().withGroupId(group.getId()).withProfileId(profile1.getId()).build());\n\n        long applicationId = repository\n                .add(anApplication().withToken(\"app1\").withDisplayName(\"my app1\").withDisplayName(\"my app1\")\n                        .withVersion(\"1.0\").withPath(\"app1\").withProfile(profile1.getId()).build())\n                .getId();\n\n        repository.flush();\n\n        //then\n        assertThat(repository.getNumberOfApplicationOfUser(user.getId())).isEqualTo(1);\n        final List<SApplication> applicationList = repository.searchApplicationOfUser(user.getId());\n        assertThat(applicationList).hasSize(1);\n        assertThat(applicationList.get(0).getId()).isEqualTo(applicationId);\n    }\n\n    @Test\n    public void searchApplicationOfUser_returns_the_application_for_the_given_user_mapped_through_group() {\n        //given\n        SUser user1 = repository.add(aUser().withId(1L).withUserName(\"walter.bates\").build());\n        SUser user2 = repository.add(aUser().withId(2L).withUserName(\"helen.kelly\").build());\n        SUser user3 = repository.add(aUser().withId(3L).withUserName(\"daniela.angelo\").build());\n        SUser user4 = repository.add(aUser().withId(4L).withUserName(\"jan.fisher\").build());\n        SUser user5 = repository.add(aUser().withId(5L).withUserName(\"antonio.banderas\").build());\n        SGroup group1 = repository.add(aGroup().forGroupId(20L).forGroupName(\"Group1\").build());\n        SGroup group2 = repository\n                .add(aGroup().forGroupId(21L).forGroupName(\"Group2\").forParentPath(\"/Group1\").build());\n        SGroup group3 = repository.add(aGroup().forGroupId(22L).forGroupName(\"Group3\").forParentPath(\"\").build());\n\n        final SProfile profile1 = repository.add(aProfile().withName(\"firstProfile\").build());\n        repository.add(aProfileMember().withGroupId(group1.getId()).withProfileId(profile1.getId()).build());\n        repository.add(aUserMembership().forUser(user1.getId()).memberOf(group1.getId(), -1L).build());\n        repository.add(aUserMembership().forUser(user3.getId()).memberOf(group1.getId(), -1L).build());\n\n        final SProfile profile2 = repository.add(aProfile().withName(\"secondProfile\").build());\n        repository.add(aProfileMember().withGroupId(group2.getId()).withProfileId(profile2.getId()).build());\n        repository.add(aUserMembership().forUser(user2.getId()).memberOf(group2.getId(), -1L).build());\n        repository.add(aUserMembership().forUser(user3.getId()).memberOf(group2.getId(), -1L).build());\n\n        repository.add(aUserMembership().forUser(user4.getId()).memberOf(group3.getId(), -1L).build());\n\n        final AbstractSApplication application1 = repository\n                .add(anApplication().withToken(\"app1\").withDisplayName(\"my app1\").withDisplayName(\"my app1\")\n                        .withVersion(\"1.0\").withPath(\"app1\").withProfile(profile1.getId()).build());\n        final AbstractSApplication application2 = repository\n                .add(anApplication().withToken(\"app2\").withDisplayName(\"my app2\").withDisplayName(\"my app2\")\n                        .withDisplayName(\"my app2\").withVersion(\"1.0\").withPath(\"/app2\").withProfile(profile1.getId())\n                        .build());\n        final AbstractSApplication application3 = repository\n                .add(anApplication().withToken(\"app3\").withDisplayName(\"my app3\").withDisplayName(\"my app3\")\n                        .withVersion(\"1.0\").withPath(\"app3\").withProfile(profile2.getId()).build());\n\n        repository.flush();\n\n        //then\n        assertThat(repository.searchApplicationOfUser(user1.getId())).extracting(\"id\")\n                .containsExactlyInAnyOrder(application1.getId(), application2.getId());\n        assertThat(repository.searchApplicationOfUser(user2.getId())).extracting(\"id\")\n                .containsExactly(application3.getId());\n        assertThat(repository.searchApplicationOfUser(user3.getId())).extracting(\"id\")\n                .containsExactlyInAnyOrder(application1.getId(), application2.getId(), application3.getId());\n        assertThat(repository.searchApplicationOfUser(user4.getId())).isEmpty();\n        assertThat(repository.searchApplicationOfUser(user5.getId())).isEmpty();\n        assertThat(repository.searchApplicationOfUser(6L)).isEmpty();\n        assertThat(repository.getNumberOfApplicationOfUser(user1.getId())).isEqualTo(2);\n        assertThat(repository.getNumberOfApplicationOfUser(user2.getId())).isEqualTo(1);\n        assertThat(repository.getNumberOfApplicationOfUser(user3.getId())).isEqualTo(3);\n        assertThat(repository.getNumberOfApplicationOfUser(user4.getId())).isEqualTo(0);\n        assertThat(repository.getNumberOfApplicationOfUser(user5.getId())).isEqualTo(0);\n        assertThat(repository.getNumberOfApplicationOfUser(6L)).isEqualTo(0);\n    }\n\n    @Test\n    public void getApplicationOfUser_returns_the_application_for_the_given_user_mapped_through_role() {\n        //given\n        SUser user1 = repository.add(aUser().withId(1L).withUserName(\"walter.bates\").build());\n        SUser user2 = repository.add(aUser().withId(2L).withUserName(\"helen.kelly\").build());\n        SUser user3 = repository.add(aUser().withId(3L).withUserName(\"daniela.angelo\").build());\n        SUser user4 = repository.add(aUser().withId(4L).withUserName(\"jan.fisher\").build());\n        SUser user5 = repository.add(aUser().withId(5L).withUserName(\"antonio.banderas\").build());\n        SRole role1 = repository.add(aRole().forRoleId(40L).forRoleName(\"Role1\").build());\n        SRole role2 = repository.add(aRole().forRoleId(41L).forRoleName(\"Role2\").build());\n        SRole role3 = repository.add(aRole().forRoleId(42L).forRoleName(\"Role3\").build());\n\n        final SProfile profile1 = repository.add(aProfile().withName(\"firstProfile\").build());\n        repository.add(aProfileMember().withRoleId(role1.getId()).withProfileId(profile1.getId()).build());\n        repository.add(aUserMembership().forUser(user1.getId()).memberOf(-1L, role1.getId()).build());\n        repository.add(aUserMembership().forUser(user3.getId()).memberOf(-1L, role1.getId()).build());\n\n        final SProfile profile2 = repository.add(aProfile().withName(\"secondProfile\").build());\n        repository.add(aProfileMember().withRoleId(role2.getId()).withProfileId(profile2.getId()).build());\n        repository.add(aUserMembership().forUser(user2.getId()).memberOf(-1L, role2.getId()).build());\n        repository.add(aUserMembership().forUser(user3.getId()).memberOf(-1L, role2.getId()).build());\n\n        repository.add(aUserMembership().forUser(user4.getId()).memberOf(-1L, role3.getId()).build());\n\n        long application1 = repository\n                .add(anApplication().withToken(\"app1\").withDisplayName(\"my app1\").withDisplayName(\"my app1\")\n                        .withVersion(\"1.0\").withPath(\"app1\").withProfile(profile1.getId()).build())\n                .getId();\n        long application2 = repository\n                .add(anApplication().withToken(\"app2\").withDisplayName(\"my app2\").withDisplayName(\"my app2\")\n                        .withDisplayName(\"my app2\").withVersion(\"1.0\").withPath(\"/app2\").withProfile(profile1.getId())\n                        .build())\n                .getId();\n        long application3 = repository\n                .add(anApplication().withToken(\"app3\").withDisplayName(\"my app3\").withDisplayName(\"my app3\")\n                        .withVersion(\"1.0\").withPath(\"app3\").withProfile(profile2.getId()).build())\n                .getId();\n\n        repository.flush();\n\n        //then\n        assertThat(repository.searchApplicationOfUser(user1.getId())).extracting(\"id\").containsExactlyInAnyOrder(\n                application1,\n                application2);\n        assertThat(repository.searchApplicationOfUser(user2.getId())).extracting(\"id\").containsExactly(application3);\n        assertThat(repository.searchApplicationOfUser(user3.getId())).extracting(\"id\").containsExactlyInAnyOrder(\n                application1,\n                application2,\n                application3);\n        assertThat(repository.searchApplicationOfUser(user4.getId())).isEmpty();\n        assertThat(repository.searchApplicationOfUser(user5.getId())).isEmpty();\n        assertThat(repository.searchApplicationOfUser(6L)).isEmpty();\n\n        //then\n        assertThat(repository.getNumberOfApplicationOfUser(user1.getId())).isEqualTo(2);\n        assertThat(repository.getNumberOfApplicationOfUser(user2.getId())).isEqualTo(1);\n        assertThat(repository.getNumberOfApplicationOfUser(user3.getId())).isEqualTo(3);\n        assertThat(repository.getNumberOfApplicationOfUser(user4.getId())).isEqualTo(0);\n        assertThat(repository.getNumberOfApplicationOfUser(user5.getId())).isEqualTo(0);\n        assertThat(repository.getNumberOfApplicationOfUser(6L)).isEqualTo(0);\n    }\n\n    @Test\n    public void getApplicationOfUser_returns_the_application_for_the_given_user_mapped_through_group_and_role() {\n        //given\n        SUser user1 = repository.add(aUser().withId(1L).withUserName(\"walter.bates\").build());\n        SUser user2 = repository.add(aUser().withId(2L).withUserName(\"helen.kelly\").build());\n        SUser user3 = repository.add(aUser().withId(3L).withUserName(\"daniela.angelo\").build());\n        SUser user4 = repository.add(aUser().withId(4L).withUserName(\"jan.fisher\").build());\n        SUser user5 = repository.add(aUser().withId(5L).withUserName(\"antonio.banderas\").build());\n        SGroup group1 = repository.add(aGroup().forGroupId(20L).forGroupName(\"Group1\").build());\n        SGroup group2 = repository\n                .add(aGroup().forGroupId(21L).forGroupName(\"Group2\").forParentPath(\"/Group1\").build());\n        SGroup group3 = repository.add(aGroup().forGroupId(22L).forGroupName(\"Group3\").forParentPath(\"\").build());\n        SRole role1 = repository.add(aRole().forRoleId(40L).forRoleName(\"Role1\").build());\n        SRole role2 = repository.add(aRole().forRoleId(41L).forRoleName(\"Role2\").build());\n        SRole role3 = repository.add(aRole().forRoleId(42L).forRoleName(\"Role3\").build());\n\n        final SProfile profile1 = repository.add(aProfile().withName(\"firstProfile\").build());\n        repository.add(aProfileMember().withGroupId(group1.getId()).withRoleId(role1.getId())\n                .withProfileId(profile1.getId()).build());\n        repository.add(aUserMembership().forUser(user1.getId()).memberOf(group1.getId(), role1.getId()).build());\n        repository.add(aUserMembership().forUser(user3.getId()).memberOf(group1.getId(), role1.getId()).build());\n\n        final SProfile profile2 = repository.add(aProfile().withName(\"secondProfile\").build());\n        repository.add(aProfileMember().withGroupId(group2.getId()).withRoleId(role2.getId())\n                .withProfileId(profile2.getId()).build());\n        repository.add(aUserMembership().forUser(user2.getId()).memberOf(group2.getId(), role2.getId()).build());\n        repository.add(aUserMembership().forUser(user3.getId()).memberOf(group2.getId(), role2.getId()).build());\n\n        repository.add(aUserMembership().forUser(user4.getId()).memberOf(group3.getId(), role3.getId()).build());\n\n        long application1 = repository\n                .add(anApplication().withToken(\"app1\").withDisplayName(\"my app1\").withDisplayName(\"my app1\")\n                        .withVersion(\"1.0\").withPath(\"app1\").withProfile(profile1.getId()).build())\n                .getId();\n        long application2 = repository\n                .add(anApplication().withToken(\"app2\").withDisplayName(\"my app2\").withDisplayName(\"my app2\")\n                        .withDisplayName(\"my app2\").withVersion(\"1.0\").withPath(\"/app2\").withProfile(profile1.getId())\n                        .build())\n                .getId();\n        long application3 = repository\n                .add(anApplication().withToken(\"app3\").withDisplayName(\"my app3\").withDisplayName(\"my app3\")\n                        .withVersion(\"1.0\").withPath(\"app3\").withProfile(profile2.getId()).build())\n                .getId();\n\n        repository.flush();\n\n        //then\n        assertThat(repository.searchApplicationOfUser(user1.getId())).extracting(\"id\").containsExactlyInAnyOrder(\n                application1,\n                application2);\n        assertThat(repository.searchApplicationOfUser(user2.getId())).extracting(\"id\").containsExactly(application3);\n        assertThat(repository.searchApplicationOfUser(user3.getId())).extracting(\"id\").containsExactlyInAnyOrder(\n                application1,\n                application2,\n                application3);\n        assertThat(repository.searchApplicationOfUser(user4.getId())).isEmpty();\n        assertThat(repository.searchApplicationOfUser(user5.getId())).isEmpty();\n        assertThat(repository.searchApplicationOfUser(6L)).isEmpty();\n\n        //then\n        assertThat(repository.getNumberOfApplicationOfUser(user1.getId())).isEqualTo(2);\n        assertThat(repository.getNumberOfApplicationOfUser(user2.getId())).isEqualTo(1);\n        assertThat(repository.getNumberOfApplicationOfUser(user3.getId())).isEqualTo(3);\n        assertThat(repository.getNumberOfApplicationOfUser(user4.getId())).isEqualTo(0);\n        assertThat(repository.getNumberOfApplicationOfUser(user5.getId())).isEqualTo(0);\n        assertThat(repository.getNumberOfApplicationOfUser(6L)).isEqualTo(0);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/contract/data/ContractDataTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.contract.data;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.commons.Pair.pair;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.test.persistence.jdbc.JdbcRowMapper;\nimport org.bonitasoft.engine.test.persistence.repository.ContractDataRepository;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.transaction.annotation.Transactional;\n\n/**\n * author Emmanuel Duchastenier\n */\n@RunWith(SpringRunner.class)\n@ContextConfiguration(locations = { \"/testContext.xml\" })\n@Transactional\npublic class ContractDataTest {\n\n    @Autowired\n    private ContractDataRepository repository;\n    @Autowired\n    private JdbcTemplate jdbcTemplate;\n\n    @Test\n    public void should_be_able_to_add_contract_data() {\n        repository.add(SProcessContractData.builder().name(\"myProcessContractData\").scopeId(123L)\n                .value(\"SerializedValue\").build());\n        repository.add(\n                STaskContractData.builder().name(\"myTaskContractData\").scopeId(124L).value(\"SerializedValue\").build());\n\n        repository.flush();\n\n        List<Map<String, Object>> contractData = jdbcTemplate\n                .query(\"SELECT KIND, NAME, SCOPEID, VAL from contract_data\", new JdbcRowMapper(\"SCOPEID\"));\n\n        assertThat(contractData).hasSize(2);\n        assertThat(contractData).anySatisfy(c -> {\n            assertThat(c.get(\"KIND\")).isEqualTo(\"PROCESS\");\n            assertThat(c.get(\"NAME\")).isEqualTo(\"myProcessContractData\");\n            assertThat(c.get(\"SCOPEID\")).isEqualTo(123L);\n            assertThat(c.get(\"VAL\")).isEqualTo(\"<string>SerializedValue</string>\");\n        });\n        assertThat(contractData).anySatisfy(c -> {\n            assertThat(c.get(\"KIND\")).isEqualTo(\"TASK\");\n            assertThat(c.get(\"NAME\")).isEqualTo(\"myTaskContractData\");\n            assertThat(c.get(\"SCOPEID\")).isEqualTo(124L);\n            assertThat(c.get(\"VAL\")).isEqualTo(\"<string>SerializedValue</string>\");\n        });\n    }\n\n    @Test\n    public void should_be_able_to_retrieve_process_and_task_contract_data() {\n        SProcessContractData processContractData = repository.add(SProcessContractData.builder()\n                .name(\"myProcessContractData\").scopeId(123L).value(\"SerializedValue\").build());\n        STaskContractData taskContractData = repository.add(STaskContractData.builder()\n                .name(\"myTaskContractData\").scopeId(124L).value(\"SerializedValue\").build());\n\n        assertThat(repository.selectOne(\"getContractDataByProcessInstanceId\", pair(\"scopeId\", 123L)))\n                .isEqualTo(processContractData);\n        assertThat(repository.selectOne(\"getContractDataByUserTaskId\", pair(\"scopeId\", 124L)))\n                .isEqualTo(taskContractData);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/form/FormMappingTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.form;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.commons.Pair.pair;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.page.SPageMapping;\nimport org.bonitasoft.engine.test.persistence.jdbc.JdbcRowMapper;\nimport org.bonitasoft.engine.test.persistence.repository.TestRepository;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.transaction.annotation.Transactional;\n\n@RunWith(SpringRunner.class)\n@ContextConfiguration(locations = { \"/testContext.xml\" })\n@Transactional\npublic class FormMappingTest {\n\n    @Autowired\n    TestRepository testRepository;\n\n    @Autowired\n    private JdbcTemplate jdbcTemplate;\n\n    @Test\n    public void should_be_able_to_add_form_mapping() {\n        SPageMapping pageMapping = SPageMapping.builder().id(1).build();\n        testRepository.add(pageMapping);\n        testRepository.add(SFormMapping.builder().pageMapping(pageMapping).type(3).task(\"task1\").target(\"target1\")\n                .lastUpdateDate(200L).lastUpdatedBy(100L).processDefinitionId(2L).build());\n        testRepository.add(SFormMapping.builder().pageMapping(pageMapping).type(4).task(\"task2\").target(\"target2\")\n                .processDefinitionId(3L).build());\n\n        testRepository.flush();\n\n        List<Map<String, Object>> formMapping = jdbcTemplate.query(\"SELECT * from form_mapping\",\n                new JdbcRowMapper(\"PAGE_MAPPING_ID\", \"LASTUPDATEDATE\", \"LASTUPDATEDBY\", \"PROCESS\"));\n\n        assertThat(formMapping).hasSize(2);\n\n        assertThat(formMapping).anySatisfy(c -> {\n            assertThat(c.get(\"TASK\")).isEqualTo(\"task1\");\n            assertThat(c.get(\"TYPE\")).isEqualTo(3);\n            assertThat(c.get(\"PAGE_MAPPING_ID\")).isEqualTo(1L);\n            assertThat(c.get(\"LASTUPDATEDATE\")).isEqualTo(200L);\n            assertThat(c.get(\"LASTUPDATEDBY\")).isEqualTo(100L);\n            assertThat(c.get(\"PROCESS\")).isEqualTo(2L);\n            assertThat(c.get(\"TARGET\")).isEqualTo(\"target1\");\n        });\n        assertThat(formMapping).anySatisfy(c -> {\n            assertThat(c.get(\"TASK\")).isEqualTo(\"task2\");\n            assertThat(c.get(\"PAGE_MAPPING_ID\")).isEqualTo(1L);\n            assertThat(c.get(\"TYPE\")).isEqualTo(4);\n            assertThat(c.get(\"PROCESS\")).isEqualTo(3L);\n            assertThat(c.get(\"TARGET\")).isEqualTo(\"target2\");\n        });\n    }\n\n    @Test\n    public void should_be_able_to_retrieve_formMapping_with_process_definition_id() {\n        SFormMapping formMapping = testRepository.add(SFormMapping.builder().processDefinitionId(123L).build());\n        SFormMapping formMapping1 = testRepository.add(SFormMapping.builder().processDefinitionId(124L).build());\n\n        assertThat(testRepository.selectOne(\"getFormMappingsOfProcessDefinition\", pair(\"processDefinitionId\", 123L)))\n                .isEqualTo(formMapping);\n        assertThat(testRepository.selectOne(\"getFormMappingsOfProcessDefinition\", pair(\"processDefinitionId\", 124L)))\n                .isEqualTo(formMapping1);\n\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/process/instance/model/ActorMappingTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.test.persistence.builder.ActorBuilder.anActor;\nimport static org.bonitasoft.engine.test.persistence.builder.ActorMemberBuilder.anActorMember;\nimport static org.bonitasoft.engine.test.persistence.builder.UserBuilder.aUser;\nimport static org.bonitasoft.engine.test.persistence.builder.UserMembershipBuilder.aUserMembership;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.actor.mapping.model.SActor;\nimport org.bonitasoft.engine.actor.mapping.model.SActorMember;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.test.persistence.repository.UserMembershipRepository;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.transaction.annotation.Transactional;\n\n/**\n * author Emmanuel Duchastenier\n */\n@RunWith(SpringRunner.class)\n@ContextConfiguration(locations = { \"/testContext.xml\" })\n@Transactional\npublic class ActorMappingTest {\n\n    @Autowired\n    private UserMembershipRepository repository;\n\n    @Test\n    public void userWithUserMappingShouldBeReturned() {\n        final SActor actor = repository.add(anActor().build());\n        long unusedRoleId = 111L;\n        long unusedGroupId = 222L;\n        final SUser user = repository.add(aUser().withId(1L).build());\n        SActorMember actorMember = repository.add(anActorMember().forActor(actor).withUserId(user.getId()).build());\n        repository.add(aUserMembership().forUser(user).memberOf(unusedGroupId, unusedRoleId).build());\n\n        List<Long> actorMemberIds = new ArrayList<Long>(1);\n        actorMemberIds.add(actorMember.getId());\n        Long members = repository.getNumberOfUserMembersForUserOrManagerForActorMembers(user.getId(), actorMemberIds);\n        assertThat(members).isEqualTo(1L);\n    }\n\n    @Test\n    public void userWithRoleMappingShouldBeReturned() {\n        final SActor actor = repository.add(anActor().build());\n        long aRoleId = 111L;\n        long unusedGroupId = 222L;\n        SActorMember actorMember = repository.add(anActorMember().forActor(actor).withRoleId(aRoleId).build());\n        final SUser user = repository.add(aUser().withId(22L).build());\n        repository.add(aUserMembership().forUser(user).memberOf(unusedGroupId, aRoleId).build());\n\n        List<Long> actorMemberIds = new ArrayList<Long>(1);\n        actorMemberIds.add(actorMember.getId());\n        Long members = repository.getNumberOfUserMembersForUserOrManagerForActorMembers(user.getId(), actorMemberIds);\n        assertThat(members).isEqualTo(1L);\n    }\n\n    @Test\n    public void userWithGroupMappingShouldBeReturned() {\n        final SActor actor = repository.add(anActor().build());\n        long unusedRoleId = 111L;\n        long aGroupId = 222L;\n        SActorMember actorMember = repository.add(anActorMember().forActor(actor).withGroupId(aGroupId).build());\n        final SUser user = repository.add(aUser().withId(333L).build());\n        repository.add(aUserMembership().forUser(user).memberOf(aGroupId, unusedRoleId).build());\n\n        List<Long> actorMemberIds = new ArrayList<Long>(1);\n        actorMemberIds.add(actorMember.getId());\n        Long members = repository.getNumberOfUserMembersForUserOrManagerForActorMembers(user.getId(), actorMemberIds);\n        assertThat(members).isEqualTo(1L);\n    }\n\n    @Test\n    public void userWithGroupAndRoleMappingShouldBeReturned() {\n        final SActor actor = repository.add(anActor().build());\n        long aRoleId = 111L;\n        long aGroupId = 222L;\n        SActorMember actorMember = repository\n                .add(anActorMember().forActor(actor).withRoleId(aRoleId).withGroupId(aGroupId).build());\n        final SUser user = repository.add(aUser().withId(4444L).build());\n        repository.add(aUserMembership().forUser(user).memberOf(aGroupId, aRoleId).build());\n\n        Long members = repository.getNumberOfUserMembersForUserOrManagerForActorMembers(user.getId(),\n                Arrays.asList(actorMember.getId()));\n        assertThat(members).isEqualTo(1L);\n    }\n\n    @Test\n    public void managerOfUserWithUserMappingShouldBeReturned() {\n        final SActor actor = repository.add(anActor().build());\n        long unusedRoleId = 111L;\n        long unusedGroupId = 222L;\n        final SUser manager = repository.add(aUser().withId(2L).build());\n        final SUser user = repository.add(aUser().withId(5555L).withManager(manager.getId()).build());\n        SActorMember actorMember = repository.add(anActorMember().forActor(actor).withUserId(user.getId()).build());\n        repository.add(aUserMembership().forUser(user).memberOf(unusedGroupId, unusedRoleId).build());\n\n        Long members = repository.getNumberOfUserMembersForUserOrManagerForActorMembers(manager.getId(),\n                Arrays.asList(actorMember.getId()));\n        assertThat(members).isEqualTo(1L);\n    }\n\n    @Test\n    public void managerOfUserWithRoleMappingShouldBeReturned() {\n        final SActor actor = repository.add(anActor().build());\n        long aRoleId = 111L;\n        long unusedGroupId = 222L;\n        SActorMember actorMember = repository.add(anActorMember().forActor(actor).withRoleId(aRoleId).build());\n        final SUser manager = repository.add(aUser().withId(2L).build());\n        final SUser user = repository.add(aUser().withId(66666L).withManager(manager.getId()).build());\n        repository.add(aUserMembership().forUser(user).memberOf(unusedGroupId, aRoleId).build());\n\n        Long members = repository.getNumberOfUserMembersForUserOrManagerForActorMembers(manager.getId(),\n                Arrays.asList(actorMember.getId()));\n        assertThat(members).isEqualTo(1L);\n    }\n\n    @Test\n    public void managerOfUserWithGroupMappingShouldBeReturned() {\n        final SActor actor = repository.add(anActor().build());\n        long unusedRoleId = 111L;\n        long aGroupId = 222L;\n        SActorMember actorMember = repository.add(anActorMember().forActor(actor).withGroupId(aGroupId).build());\n        final SUser manager = repository.add(aUser().withId(2L).build());\n        final SUser user = repository.add(aUser().withId(77777L).withManager(manager.getId()).build());\n        repository.add(aUserMembership().forUser(user).memberOf(aGroupId, unusedRoleId).build());\n\n        Long members = repository.getNumberOfUserMembersForUserOrManagerForActorMembers(manager.getId(),\n                Arrays.asList(actorMember.getId()));\n        assertThat(members).isEqualTo(1L);\n    }\n\n    @Test\n    public void managerOfUserWithGroupAndRoleMappingShouldBeReturned() {\n        final SActor actor = repository.add(anActor().build());\n        long aRoleId = 111L;\n        long aGroupId = 222L;\n        SActorMember actorMember = repository\n                .add(anActorMember().forActor(actor).withGroupId(aGroupId).withRoleId(aRoleId).build());\n        final SUser manager = repository.add(aUser().withId(2L).build());\n        final SUser user = repository.add(aUser().withId(888888L).withManager(manager.getId()).build());\n        repository.add(aUserMembership().forUser(user).memberOf(aGroupId, aRoleId).build());\n\n        Long members = repository.getNumberOfUserMembersForUserOrManagerForActorMembers(manager.getId(),\n                Arrays.asList(actorMember.getId()));\n        assertThat(members).isEqualTo(1L);\n    }\n\n    @Test\n    public void userWithNoMatchingGroupAndRoleShouldNotBeReturned() {\n        final SActor actor = repository.add(anActor().build());\n        long aRoleId = 111L;\n        long aGroupId = 222L;\n        SActorMember actorMember = repository\n                .add(anActorMember().forActor(actor).withGroupId(aGroupId).withRoleId(aRoleId).build());\n        final SUser user = repository.add(aUser().withId(99999999L).build());\n        repository.add(aUserMembership().forUser(user).memberOf(aGroupId, 16545L).build());\n\n        Long members = repository.getNumberOfUserMembersForUserOrManagerForActorMembers(user.getId(),\n                Arrays.asList(actorMember.getId()));\n        assertThat(members).isEqualTo(0L);\n    }\n\n    @Test\n    public void userWithNoMembershipButWithDirectActorMappingShouldBeReturned() {\n        final SActor actor = repository.add(anActor().build());\n        long aRoleId = 111L;\n        long aGroupId = 222L;\n        final SUser user = repository.add(aUser().withId(132457L).build());\n        SActorMember actorMember = repository.add(anActorMember().forActor(actor).withUserId(user.getId()).build());\n\n        Long members = repository.getNumberOfUserMembersForUserOrManagerForActorMembers(user.getId(),\n                Arrays.asList(actorMember.getId()));\n        assertThat(members).isEqualTo(1L);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/process/instance/model/ArchiveFlowNodeInstanceTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.commons.Pair.pair;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.core.process.definition.model.SGatewayType;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAAutomaticTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SACallActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAGatewayInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SALoopActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAManualTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAMultiInstanceActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAReceiveTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SASendTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SASubProcessActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.test.persistence.repository.FlowNodeInstanceRepository;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.transaction.annotation.Transactional;\n\n@RunWith(SpringRunner.class)\n@ContextConfiguration(locations = { \"/testContext.xml\" })\n@Transactional\npublic class ArchiveFlowNodeInstanceTest {\n\n    @Autowired\n    private FlowNodeInstanceRepository repository;\n    @Autowired\n    private JdbcTemplate jdbcTemplate;\n\n    @Test\n    public void should_save_and_get_SAAutomaticTaskInstance() {\n        SAFlowNodeInstance entity = new SAAutomaticTaskInstance();\n        SAFlowNodeInstance flowNode = repository.add(entity);\n        repository.flush();\n\n        PersistentObject flowNodeFromQuery = repository.selectOne(\"getArchivedFlowNodeInstanceById\",\n                pair(\"id\", flowNode.getId()));\n        Map<String, Object> flowNodeAsMap = jdbcTemplate\n                .queryForMap(\"SELECT * FROM arch_flownode_instance where id = \" + flowNode.getId());\n\n        assertThat(flowNodeFromQuery).isEqualTo(flowNode);\n        assertThat(flowNodeAsMap.get(\"KIND\")).isEqualTo(\"auto\");\n    }\n\n    @Test\n    public void should_save_and_get_SAUserTaskInstance_with_task_priority() {\n        SAUserTaskInstance entity = new SAUserTaskInstance();\n        entity.setPriority(STaskPriority.ABOVE_NORMAL);\n        SAUserTaskInstance flowNode = repository.add(entity);\n        repository.flush();\n\n        PersistentObject flowNodeFromQuery = repository.selectOne(\"getArchivedFlowNodeInstanceById\",\n                pair(\"id\", flowNode.getId()));\n        Map<String, Object> flowNodeAsMap = jdbcTemplate\n                .queryForMap(\"SELECT * FROM arch_flownode_instance where id = \" + flowNode.getId());\n\n        assertThat(flowNodeFromQuery).isEqualTo(flowNode);\n        assertThat(((Number) flowNodeAsMap.get(\"PRIORITY\")).intValue()).isEqualTo(3);\n    }\n\n    @Test\n    public void should_save_and_get_SAGatewayInstance_with_gateway_type() {\n        SAGatewayInstance entity = new SAGatewayInstance();\n        entity.setGatewayType(SGatewayType.INCLUSIVE);\n        SAFlowNodeInstance flowNode = repository.add(entity);\n        repository.flush();\n\n        PersistentObject flowNodeFromQuery = repository.selectOne(\"getArchivedFlowNodeInstanceById\",\n                pair(\"id\", flowNode.getId()));\n        Map<String, Object> flowNodeAsMap = jdbcTemplate\n                .queryForMap(\"SELECT * FROM arch_flownode_instance where id = \" + flowNode.getId());\n\n        assertThat(flowNodeFromQuery).isEqualTo(flowNode);\n        assertThat(flowNodeAsMap.get(\"GATEWAYTYPE\")).isEqualTo(\"INCLUSIVE\");\n    }\n\n    @Test\n    public void should_save_and_get_SACallActivityInstance() {\n        SAFlowNodeInstance entity = new SACallActivityInstance();\n        SAFlowNodeInstance flowNode = repository.add(entity);\n        repository.flush();\n\n        PersistentObject flowNodeFromQuery = repository.selectOne(\"getArchivedFlowNodeInstanceById\",\n                pair(\"id\", flowNode.getId()));\n        Map<String, Object> flowNodeAsMap = jdbcTemplate\n                .queryForMap(\"SELECT * FROM arch_flownode_instance where id = \" + flowNode.getId());\n\n        assertThat(flowNodeFromQuery).isEqualTo(flowNode);\n        assertThat(flowNodeAsMap.get(\"KIND\")).isEqualTo(\"call\");\n    }\n\n    @Test\n    public void should_save_and_get_SAGatewayInstance() {\n        SAFlowNodeInstance entity = new SAGatewayInstance();\n        SAFlowNodeInstance flowNode = repository.add(entity);\n        repository.flush();\n\n        PersistentObject flowNodeFromQuery = repository.selectOne(\"getArchivedFlowNodeInstanceById\",\n                pair(\"id\", flowNode.getId()));\n        Map<String, Object> flowNodeAsMap = jdbcTemplate\n                .queryForMap(\"SELECT * FROM arch_flownode_instance where id = \" + flowNode.getId());\n\n        assertThat(flowNodeFromQuery).isEqualTo(flowNode);\n        assertThat(flowNodeAsMap.get(\"KIND\")).isEqualTo(\"gate\");\n    }\n\n    @Test\n    public void should_save_and_get_SALoopActivityInstance() {\n        SAFlowNodeInstance entity = new SALoopActivityInstance();\n        SAFlowNodeInstance flowNode = repository.add(entity);\n        repository.flush();\n\n        PersistentObject flowNodeFromQuery = repository.selectOne(\"getArchivedFlowNodeInstanceById\",\n                pair(\"id\", flowNode.getId()));\n        Map<String, Object> flowNodeAsMap = jdbcTemplate\n                .queryForMap(\"SELECT * FROM arch_flownode_instance where id = \" + flowNode.getId());\n\n        assertThat(flowNodeFromQuery).isEqualTo(flowNode);\n        assertThat(flowNodeAsMap.get(\"KIND\")).isEqualTo(\"loop\");\n    }\n\n    @Test\n    public void should_save_and_get_SAManualTaskInstance() {\n        SAFlowNodeInstance entity = new SAManualTaskInstance();\n        SAFlowNodeInstance flowNode = repository.add(entity);\n        repository.flush();\n\n        PersistentObject flowNodeFromQuery = repository.selectOne(\"getArchivedFlowNodeInstanceById\",\n                pair(\"id\", flowNode.getId()));\n        Map<String, Object> flowNodeAsMap = jdbcTemplate\n                .queryForMap(\"SELECT * FROM arch_flownode_instance where id = \" + flowNode.getId());\n\n        assertThat(flowNodeFromQuery).isEqualTo(flowNode);\n        assertThat(flowNodeAsMap.get(\"KIND\")).isEqualTo(\"manual\");\n    }\n\n    @Test\n    public void should_save_and_get_SAMultiInstanceActivityInstance() {\n        SAFlowNodeInstance entity = new SAMultiInstanceActivityInstance();\n        SAFlowNodeInstance flowNode = repository.add(entity);\n        repository.flush();\n\n        PersistentObject flowNodeFromQuery = repository.selectOne(\"getArchivedFlowNodeInstanceById\",\n                pair(\"id\", flowNode.getId()));\n        Map<String, Object> flowNodeAsMap = jdbcTemplate\n                .queryForMap(\"SELECT * FROM arch_flownode_instance where id = \" + flowNode.getId());\n\n        assertThat(flowNodeFromQuery).isEqualTo(flowNode);\n        assertThat(flowNodeAsMap.get(\"KIND\")).isEqualTo(\"multi\");\n    }\n\n    @Test\n    public void should_save_and_get_SAReceiveTaskInstance() {\n        SAFlowNodeInstance entity = new SAReceiveTaskInstance();\n        SAFlowNodeInstance flowNode = repository.add(entity);\n        repository.flush();\n\n        PersistentObject flowNodeFromQuery = repository.selectOne(\"getArchivedFlowNodeInstanceById\",\n                pair(\"id\", flowNode.getId()));\n        Map<String, Object> flowNodeAsMap = jdbcTemplate\n                .queryForMap(\"SELECT * FROM arch_flownode_instance where id = \" + flowNode.getId());\n\n        assertThat(flowNodeFromQuery).isEqualTo(flowNode);\n        assertThat(flowNodeAsMap.get(\"KIND\")).isEqualTo(\"receive\");\n    }\n\n    @Test\n    public void should_save_and_get_SASendTaskInstance() {\n        SAFlowNodeInstance entity = new SASendTaskInstance();\n        SAFlowNodeInstance flowNode = repository.add(entity);\n        repository.flush();\n\n        PersistentObject flowNodeFromQuery = repository.selectOne(\"getArchivedFlowNodeInstanceById\",\n                pair(\"id\", flowNode.getId()));\n        Map<String, Object> flowNodeAsMap = jdbcTemplate\n                .queryForMap(\"SELECT * FROM arch_flownode_instance where id = \" + flowNode.getId());\n\n        assertThat(flowNodeFromQuery).isEqualTo(flowNode);\n        assertThat(flowNodeAsMap.get(\"KIND\")).isEqualTo(\"send\");\n    }\n\n    @Test\n    public void should_save_and_get_SASubProcessActivityInstance() {\n        SAFlowNodeInstance entity = new SASubProcessActivityInstance();\n        SAFlowNodeInstance flowNode = repository.add(entity);\n        repository.flush();\n\n        PersistentObject flowNodeFromQuery = repository.selectOne(\"getArchivedFlowNodeInstanceById\",\n                pair(\"id\", flowNode.getId()));\n        Map<String, Object> flowNodeAsMap = jdbcTemplate\n                .queryForMap(\"SELECT * FROM arch_flownode_instance where id = \" + flowNode.getId());\n\n        assertThat(flowNodeFromQuery).isEqualTo(flowNode);\n        assertThat(flowNodeAsMap.get(\"KIND\")).isEqualTo(\"subProc\");\n    }\n\n    @Test\n    public void should_save_and_get_SAUserTaskInstance() {\n        SAFlowNodeInstance entity = new SAUserTaskInstance();\n        SAFlowNodeInstance flowNode = repository.add(entity);\n        repository.flush();\n\n        PersistentObject flowNodeFromQuery = repository.selectOne(\"getArchivedFlowNodeInstanceById\",\n                pair(\"id\", flowNode.getId()));\n        Map<String, Object> flowNodeAsMap = jdbcTemplate\n                .queryForMap(\"SELECT * FROM arch_flownode_instance where id = \" + flowNode.getId());\n\n        assertThat(flowNodeFromQuery).isEqualTo(flowNode);\n        assertThat(flowNodeAsMap.get(\"KIND\")).isEqualTo(\"user\");\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/process/instance/model/ArchiveProcessInstanceQueriesTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.bonitasoft.engine.commons.Pair.mapOf;\nimport static org.bonitasoft.engine.commons.Pair.pair;\nimport static org.junit.Assert.*;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.business.data.SAFlowNodeSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.business.data.SAProcessMultiRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.business.data.SAProcessSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.test.persistence.jdbc.JdbcRowMapper;\nimport org.bonitasoft.engine.test.persistence.repository.ProcessInstanceRepository;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.transaction.annotation.Transactional;\n\n@RunWith(SpringRunner.class)\n@ContextConfiguration(locations = { \"/testContext.xml\" })\n@Transactional\npublic class ArchiveProcessInstanceQueriesTest {\n\n    private static final long PROCESS_INSTANCE_ID = 43578923425L;\n    private static final long FLOW_NODE_INSTANCE_ID = 342678L;\n\n    @Autowired\n    private ProcessInstanceRepository repository;\n\n    @Autowired\n    private JdbcTemplate jdbcTemplate;\n\n    @Test\n    public void getArchivedProcessInstancesInAllStates_should_return_archived_process_instances_when_exist() {\n        // Given\n        final SAProcessInstance saProcessInstance1 = repository.add(buildSAProcessInstance(1L));\n        final SAProcessInstance saProcessInstance2 = repository.add(buildSAProcessInstance(2L));\n\n        // When\n        final List<SAProcessInstance> archivedProcessInstances = repository\n                .getArchivedProcessInstancesInAllStates(Arrays.asList(1L, 2L));\n\n        // Then\n        assertFalse(\"The list of archived process instance must not be empty !!\", archivedProcessInstances.isEmpty());\n        assertEquals(\"The first element of the list must to have as id 1\", saProcessInstance1,\n                archivedProcessInstances.get(0));\n        assertEquals(\"The second element of the list must to have as id 2\", saProcessInstance2,\n                archivedProcessInstances.get(1));\n    }\n\n    @Test\n    public void getArchivedProcessInstancesInAllStates_should_return_empty_list_when_no_archived_process_instances_with_ids() {\n        // Given\n\n        // When\n        final List<SAProcessInstance> archivedProcessInstances = repository\n                .getArchivedProcessInstancesInAllStates(Arrays.asList(1L, 2L));\n\n        // Then\n        assertTrue(\"The list of archived process instance must be empty !!\", archivedProcessInstances.isEmpty());\n    }\n\n    @Test\n    public void should_save_and_get_multi_business_data_reference_for_archived_process() {\n        SAProcessMultiRefBusinessDataInstance multiRefBusinessDataInstance = new SAProcessMultiRefBusinessDataInstance();\n        multiRefBusinessDataInstance.setDataIds(Arrays.asList(23L, 25L, 27L));\n        multiRefBusinessDataInstance.setProcessInstanceId(PROCESS_INSTANCE_ID);\n        multiRefBusinessDataInstance.setName(\"myMultiProcData\");\n        multiRefBusinessDataInstance.setDataClassName(\"someDataClassName\");\n        multiRefBusinessDataInstance = repository.add(multiRefBusinessDataInstance);\n        repository.flush();\n\n        PersistentObject multiRefBusinessData = repository.selectOne(\"getSARefBusinessDataInstance\",\n                pair(\"processInstanceId\", PROCESS_INSTANCE_ID), pair(\"name\", \"myMultiProcData\"));\n        Map<String, Object> multiRefBusinessDataAsMap = jdbcTemplate\n                .queryForObject(\n                        \"SELECT ID, KIND, NAME, DATA_CLASSNAME, DATA_ID, ORIG_PROC_INST_ID, ORIG_FN_INST_ID FROM arch_ref_biz_data_inst WHERE orig_proc_inst_id=\"\n                                + PROCESS_INSTANCE_ID + \" AND name='myMultiProcData'\",\n                        new JdbcRowMapper(\"ID\", \"DATA_ID\", \"ORIG_PROC_INST_ID\", \"ORIG_FN_INST_ID\"));\n        List<Map<String, Object>> dataIds = jdbcTemplate\n                .query(\"SELECT ID, IDX, DATA_ID FROM arch_multi_biz_data WHERE id=\"\n                        + multiRefBusinessDataInstance.getId(),\n                        new JdbcRowMapper(\"ID\", \"IDX\", \"DATA_ID\"));\n\n        assertThat(((SAProcessMultiRefBusinessDataInstance) multiRefBusinessData).getDataIds())\n                .isEqualTo(Arrays.asList(23L, 25L, 27L));\n        assertThat(multiRefBusinessData).isEqualTo(multiRefBusinessDataInstance);\n        assertThat(multiRefBusinessDataAsMap).containsOnly(\n                entry(\"ID\", multiRefBusinessDataInstance.getId()),\n                entry(\"KIND\", \"proc_multi_ref\"),\n                entry(\"NAME\", \"myMultiProcData\"),\n                entry(\"DATA_CLASSNAME\", \"someDataClassName\"),\n                entry(\"DATA_ID\", null),\n                entry(\"ORIG_PROC_INST_ID\", PROCESS_INSTANCE_ID),\n                entry(\"ORIG_FN_INST_ID\", null));\n        assertThat(dataIds).containsExactly(\n                mapOf(pair(\"ID\", multiRefBusinessDataInstance.getId()), pair(\"IDX\", 0L), pair(\"DATA_ID\", 23L)),\n                mapOf(pair(\"ID\", multiRefBusinessDataInstance.getId()), pair(\"IDX\", 1L), pair(\"DATA_ID\", 25L)),\n                mapOf(pair(\"ID\", multiRefBusinessDataInstance.getId()), pair(\"IDX\", 2L), pair(\"DATA_ID\", 27L)));\n    }\n\n    @Test\n    public void should_save_and_get_single_business_data_reference_for_archived_process() {\n        SAProcessSimpleRefBusinessDataInstance singleRef = new SAProcessSimpleRefBusinessDataInstance();\n        singleRef.setDataId(43L);\n        singleRef.setProcessInstanceId(PROCESS_INSTANCE_ID);\n        singleRef.setName(\"mySingleData\");\n        singleRef.setDataClassName(\"someDataClassName\");\n        singleRef = repository.add(singleRef);\n        repository.flush();\n\n        PersistentObject singleRefFromQuery = repository.selectOne(\"getSARefBusinessDataInstance\",\n                pair(\"processInstanceId\", PROCESS_INSTANCE_ID), pair(\"name\", \"mySingleData\"));\n        Map<String, Object> multiRefBusinessDataAsMap = jdbcTemplate\n                .queryForObject(\n                        \"SELECT ID, KIND, NAME, DATA_CLASSNAME, DATA_ID, ORIG_PROC_INST_ID, ORIG_FN_INST_ID FROM arch_ref_biz_data_inst WHERE orig_proc_inst_id=\"\n                                + PROCESS_INSTANCE_ID\n                                + \" AND name='mySingleData'\",\n                        new JdbcRowMapper(\"ID\", \"DATA_ID\", \"ORIG_PROC_INST_ID\", \"ORIG_FN_INST_ID\"));\n        assertThat(singleRefFromQuery).isEqualTo(singleRef);\n        assertThat(multiRefBusinessDataAsMap).containsOnly(\n                entry(\"ID\", singleRef.getId()),\n                entry(\"KIND\", \"proc_simple_ref\"),\n                entry(\"NAME\", \"mySingleData\"),\n                entry(\"DATA_CLASSNAME\", \"someDataClassName\"),\n                entry(\"DATA_ID\", 43L),\n                entry(\"ORIG_PROC_INST_ID\", PROCESS_INSTANCE_ID),\n                entry(\"ORIG_FN_INST_ID\", null));\n    }\n\n    @Test\n    public void should_save_and_get_single_business_data_reference_for_flow_node_of_archived_process() {\n        SAFlowNodeSimpleRefBusinessDataInstance singleRef = new SAFlowNodeSimpleRefBusinessDataInstance();\n        singleRef.setDataId(43L);\n        singleRef.setFlowNodeInstanceId(FLOW_NODE_INSTANCE_ID);\n        singleRef.setName(\"mySingleData\");\n        singleRef.setDataClassName(\"someDataClassName\");\n        singleRef = repository.add(singleRef);\n        repository.flush();\n\n        PersistentObject singleRefFromQuery = repository.selectOne(\"getSAFlowNodeRefBusinessDataInstance\",\n                pair(\"flowNodeInstanceId\", FLOW_NODE_INSTANCE_ID), pair(\"name\", \"mySingleData\"));\n        Map<String, Object> multiRefBusinessDataAsMap = jdbcTemplate\n                .queryForObject(\n                        \"SELECT ID, KIND, NAME, DATA_CLASSNAME, DATA_ID, ORIG_PROC_INST_ID, ORIG_FN_INST_ID FROM arch_ref_biz_data_inst WHERE orig_fn_inst_id=\"\n                                + FLOW_NODE_INSTANCE_ID\n                                + \" AND name='mySingleData'\",\n                        new JdbcRowMapper(\"ID\", \"DATA_ID\", \"ORIG_PROC_INST_ID\", \"ORIG_FN_INST_ID\"));\n        assertThat(singleRefFromQuery).isEqualTo(singleRef);\n        assertThat(multiRefBusinessDataAsMap).containsOnly(\n                entry(\"ID\", singleRef.getId()),\n                entry(\"KIND\", \"fn_simple_ref\"),\n                entry(\"NAME\", \"mySingleData\"),\n                entry(\"DATA_CLASSNAME\", \"someDataClassName\"),\n                entry(\"DATA_ID\", 43L),\n                entry(\"ORIG_PROC_INST_ID\", null),\n                entry(\"ORIG_FN_INST_ID\", FLOW_NODE_INSTANCE_ID));\n    }\n\n    private SAProcessInstance buildSAProcessInstance(final long id) {\n        final SAProcessInstance saProcessInstance = new SAProcessInstance();\n        saProcessInstance.setId(id);\n        saProcessInstance.setSourceObjectId(id);\n        saProcessInstance.setName(\"process\" + id);\n        return saProcessInstance;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/process/instance/model/BPMEventQueriesTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.core.process.instance.model.event.handling.SBPMEventType.INTERMEDIATE_THROW_EVENT;\nimport static org.bonitasoft.engine.test.persistence.builder.MessageInstanceBuilder.aMessageInstance;\nimport static org.bonitasoft.engine.test.persistence.builder.WaitingMessageEventBuilder.aWaitingEvent;\n\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent;\nimport org.bonitasoft.engine.test.persistence.repository.BPMEventRepository;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.transaction.annotation.Transactional;\n\n@RunWith(SpringRunner.class)\n@ContextConfiguration(locations = { \"/testContext.xml\" })\n@Transactional\npublic class BPMEventQueriesTest {\n\n    private static final int SOME_NUMBER_OF_MESSAGE_INSTANCES = 5;\n\n    private static final int MORE_THAN_DEFAULT_PAGE_SIZE = 42;\n\n    @Autowired\n    private BPMEventRepository bPMEventRepository;\n\n    @Autowired\n    private JdbcTemplate jdbcTemplate;\n\n    @Test\n    public void getInProgressMessageInstancesShouldOnlyConsiderHandledMessages() {\n        // given:\n        for (int i = 0; i < SOME_NUMBER_OF_MESSAGE_INSTANCES; i++) {\n            bPMEventRepository.add(aMessageInstance().handled(true).build());\n        }\n        // Add instances that should be ignored by getInProgressMessageInstances():\n        bPMEventRepository.add(aMessageInstance().handled(false).build());\n        bPMEventRepository.add(aMessageInstance().handled(false).build());\n\n        // when:\n        List<Long> inProgressMessageInstances = bPMEventRepository.getInProgressMessageInstances();\n\n        // then:\n        assertThat(inProgressMessageInstances).hasSize(SOME_NUMBER_OF_MESSAGE_INSTANCES);\n    }\n\n    @Test\n    public void resetMessageInstancesShouldResetAllHandledFlagToFalse() {\n        // given:\n        for (int i = 0; i < MORE_THAN_DEFAULT_PAGE_SIZE; i++) {\n            bPMEventRepository.add(aMessageInstance().handled(true).build());\n        }\n\n        // when:\n        bPMEventRepository.resetProgressMessageInstances();\n\n        // then:\n        assertThat(bPMEventRepository.getInProgressMessageInstances()).hasSize(0);\n    }\n\n    @Test\n    public void getInProgressWaitingEventsShouldOnlyConsiderInProgressElements() {\n        // given:\n        for (int i = 0; i < SOME_NUMBER_OF_MESSAGE_INSTANCES; i++) {\n            bPMEventRepository.add(aWaitingEvent().inProgress(true).build());\n        }\n        // Add instances that should be ignored by getInProgressWaitingEvents():\n        bPMEventRepository.add(aWaitingEvent().inProgress(false).build());\n        bPMEventRepository.add(aWaitingEvent().inProgress(false).build());\n\n        // when:\n        List<Long> inProgressWaitingEvents = bPMEventRepository.getInProgressWaitingEvents();\n\n        // then:\n        assertThat(inProgressWaitingEvents).hasSize(SOME_NUMBER_OF_MESSAGE_INSTANCES);\n    }\n\n    @Test\n    public void resetWaitingEventsShouldResetAllInProgressFlagToFalse() {\n        // given:\n        for (int i = 0; i < MORE_THAN_DEFAULT_PAGE_SIZE; i++) {\n            bPMEventRepository.add(aWaitingEvent().inProgress(true).build());\n        }\n\n        // when:\n        bPMEventRepository.resetInProgressWaitingEvents();\n\n        // then:\n        assertThat(bPMEventRepository.getInProgressWaitingEvents()).hasSize(0);\n    }\n\n    @Test\n    public void resetWaitingEventsShouldReturnNumberOfRowsUpdated() {\n        // given:\n        for (int i = 0; i < MORE_THAN_DEFAULT_PAGE_SIZE; i++) {\n            bPMEventRepository.add(aWaitingEvent().inProgress(true).build());\n        }\n        bPMEventRepository.add(aWaitingEvent().inProgress(false).build());\n        bPMEventRepository.add(aWaitingEvent().inProgress(false).build());\n\n        // when:\n        int resetInProgressWaitingEvents = bPMEventRepository.resetInProgressWaitingEvents();\n\n        // then:\n        assertThat(resetInProgressWaitingEvents).as(\"wrong result\").isEqualTo(MORE_THAN_DEFAULT_PAGE_SIZE);\n    }\n\n    @Test\n    public void resetMessageInstancesShouldReturnNumberOfRowsUpdated() {\n        // given:\n        for (int i = 0; i < MORE_THAN_DEFAULT_PAGE_SIZE; i++) {\n            bPMEventRepository.add(aMessageInstance().handled(true).build());\n        }\n        bPMEventRepository.add(aMessageInstance().handled(false).build());\n        bPMEventRepository.add(aMessageInstance().handled(false).build());\n\n        // when:\n        int resetMessageInstances = bPMEventRepository.resetProgressMessageInstances();\n\n        // then:\n        assertThat(resetMessageInstances).as(\"wrong result\").isEqualTo(MORE_THAN_DEFAULT_PAGE_SIZE);\n    }\n\n    @Test\n    public void should_return_message_older_than_creationDate() {\n        SMessageInstance messageInstance = bPMEventRepository.add(aMessageInstance().creationDate(100).build());\n        SMessageInstance messageInstance1 = bPMEventRepository.add(aMessageInstance().creationDate(200).build());\n        bPMEventRepository.add(aMessageInstance().creationDate(300).build());\n\n        List<Long> messageInstanceIds = bPMEventRepository.getMessageInstanceIdOlderThanCreationDate(200);\n\n        // then:\n        assertThat(messageInstanceIds).containsOnly(messageInstance.getId(), messageInstance1.getId());\n    }\n\n    @Test\n    public void should_delete_message_with_given_ids() {\n        SMessageInstance messageInstance = bPMEventRepository.add(aMessageInstance().creationDate(100).build());\n        SMessageInstance messageInstance1 = bPMEventRepository.add(aMessageInstance().creationDate(200).build());\n        SMessageInstance messageInstance2 = bPMEventRepository.add(aMessageInstance().creationDate(300).build());\n\n        bPMEventRepository.deleteMessageInstanceByIds(Arrays.asList(messageInstance.getId(), messageInstance1.getId()));\n\n        List<Long> messageInstanceIds = bPMEventRepository\n                .getMessageInstanceIdOlderThanCreationDate(Instant.now().toEpochMilli());\n\n        // then:\n        assertThat(messageInstanceIds).containsExactly(messageInstance2.getId());\n    }\n\n    @Test\n    public void should_get_waitingEvents_by_eventType() {\n        // Given\n        final SWaitingEvent sWaitingEvent = bPMEventRepository\n                .add(aWaitingEvent().withEventType(INTERMEDIATE_THROW_EVENT).build());\n        bPMEventRepository.flush();\n        final String eventType = jdbcTemplate.queryForObject(\"select eventType from waiting_event\", String.class);\n        assertThat(eventType).isEqualTo(\"INTERMEDIATE_THROW_EVENT\");\n        assertThat(sWaitingEvent.getEventType()).isEqualTo(INTERMEDIATE_THROW_EVENT);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/process/instance/model/CommentsTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.assertj.core.groups.Tuple.tuple;\nimport static org.bonitasoft.engine.commons.Pair.pair;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.core.process.comment.model.SHumanComment;\nimport org.bonitasoft.engine.core.process.comment.model.SSystemComment;\nimport org.bonitasoft.engine.core.process.comment.model.archive.SAComment;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.test.persistence.jdbc.JdbcRowMapper;\nimport org.bonitasoft.engine.test.persistence.repository.CommentRepository;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.transaction.annotation.Transactional;\n\n/**\n * @author Danila Mazour\n */\n@RunWith(SpringRunner.class)\n@ContextConfiguration(locations = { \"/testContext.xml\" })\n@Transactional\npublic class CommentsTest {\n\n    private static final long JACK_ID = 783L;\n    private static final long JOHN_ID = 784L;\n    private static final long PROCESS1_ID = 123L;\n    private static final long PROCESS2_ID = 124L;\n    @Autowired\n    private CommentRepository repository;\n    @Autowired\n    private JdbcTemplate jdbcTemplate;\n\n    //Those tests currently verify that the queries returning UserMemberships correctly retrieve the groupParentPath when building the Usermembership objects\n\n    @Test\n    public void should_getComments_of_process_instance() {\n        repository.add(new SHumanComment(PROCESS1_ID, \"comment1\", JACK_ID));\n        repository.add(new SHumanComment(PROCESS1_ID, \"comment2\", JOHN_ID));\n        repository.add(new SHumanComment(PROCESS1_ID, \"comment3\", JACK_ID));\n        repository.add(new SSystemComment(PROCESS1_ID, \"comment4\"));\n        repository.add(new SHumanComment(PROCESS2_ID, \"comment5\", JACK_ID));\n\n        assertThat(repository.getCommentsOfProcessInstance(PROCESS1_ID)).extracting(\"content\", \"userId\", \"class\")\n                .containsExactlyInAnyOrder(\n                        tuple(\"comment1\", JACK_ID, SHumanComment.class),\n                        tuple(\"comment2\", JOHN_ID, SHumanComment.class),\n                        tuple(\"comment3\", JACK_ID, SHumanComment.class),\n                        tuple(\"comment4\", null, SSystemComment.class));\n\n    }\n\n    @Test\n    public void should_save_and_get_SComment() {\n        SHumanComment comment1 = repository.add(new SHumanComment(PROCESS1_ID, \"comment1\", JACK_ID));\n        SSystemComment comment2 = repository.add(new SSystemComment(PROCESS2_ID, \"comment2\"));\n        repository.flush();\n\n        Map<String, Object> comment1AsMap = jdbcTemplate\n                .queryForObject(\n                        \"SELECT ID, KIND, CONTENT, POSTDATE, PROCESSINSTANCEID, USERID FROM process_comment WHERE processInstanceId = \"\n                                + PROCESS1_ID,\n                        new JdbcRowMapper(\"ID\", \"POSTDATE\", \"PROCESSINSTANCEID\", \"USERID\"));\n        Map<String, Object> comment2AsMap = jdbcTemplate\n                .queryForObject(\n                        \"SELECT ID, KIND, CONTENT, POSTDATE, PROCESSINSTANCEID, USERID FROM process_comment WHERE processInstanceId = \"\n                                + PROCESS2_ID,\n                        new JdbcRowMapper(\"ID\", \"POSTDATE\", \"PROCESSINSTANCEID\", \"USERID\"));\n\n        assertThat(comment1AsMap).containsOnly(\n                entry(\"ID\", comment1.getId()),\n                entry(\"KIND\", \"human\"),\n                entry(\"CONTENT\", \"comment1\"),\n                entry(\"POSTDATE\", comment1.getPostDate()),\n                entry(\"PROCESSINSTANCEID\", PROCESS1_ID),\n                entry(\"USERID\", JACK_ID));\n        assertThat(comment2AsMap).containsOnly(\n                entry(\"ID\", comment2.getId()),\n                entry(\"KIND\", \"system\"),\n                entry(\"CONTENT\", \"comment2\"),\n                entry(\"POSTDATE\", comment2.getPostDate()),\n                entry(\"PROCESSINSTANCEID\", PROCESS2_ID),\n                entry(\"USERID\", null));\n    }\n\n    @Test\n    public void should_save_and_get_SAComment() {\n        SAComment comment1 = repository.add(new SAComment(new SHumanComment(PROCESS1_ID, \"comment1\", JACK_ID)));\n        SAComment comment2 = repository.add(new SAComment(new SSystemComment(PROCESS2_ID, \"comment2\")));\n        repository.flush();\n\n        PersistentObject comment1FromQuery = repository.selectOne(\"getArchivedCommentById\",\n                pair(\"id\", comment1.getId()));\n        PersistentObject comment2FromQuery = repository.selectOne(\"getArchivedCommentById\",\n                pair(\"id\", comment2.getId()));\n        Map<String, Object> comment1AsMap = jdbcTemplate\n                .queryForObject(\n                        \"SELECT ID, SOURCEOBJECTID, ARCHIVEDATE, CONTENT, POSTDATE, PROCESSINSTANCEID, USERID FROM arch_process_comment WHERE processInstanceId = \"\n                                + PROCESS1_ID,\n                        new JdbcRowMapper(\"ID\", \"SOURCEOBJECTID\", \"ARCHIVEDATE\", \"POSTDATE\", \"PROCESSINSTANCEID\",\n                                \"USERID\"));\n        Map<String, Object> comment2AsMap = jdbcTemplate\n                .queryForObject(\n                        \"SELECT ID, SOURCEOBJECTID, ARCHIVEDATE, CONTENT, POSTDATE, PROCESSINSTANCEID, USERID FROM arch_process_comment WHERE processInstanceId = \"\n                                + PROCESS2_ID,\n                        new JdbcRowMapper(\"ID\", \"SOURCEOBJECTID\", \"ARCHIVEDATE\", \"POSTDATE\", \"PROCESSINSTANCEID\",\n                                \"USERID\"));\n\n        assertThat(comment1FromQuery).isEqualTo(comment1);\n        assertThat(comment2FromQuery).isEqualTo(comment2);\n        assertThat(comment1AsMap).containsOnly(\n                entry(\"ID\", comment1.getId()),\n                entry(\"SOURCEOBJECTID\", 0L),\n                entry(\"ARCHIVEDATE\", 0L),\n                entry(\"CONTENT\", \"comment1\"),\n                entry(\"POSTDATE\", comment1.getPostDate()),\n                entry(\"PROCESSINSTANCEID\", PROCESS1_ID),\n                entry(\"USERID\", JACK_ID));\n        assertThat(comment2AsMap).containsOnly(\n                entry(\"ID\", comment2.getId()),\n                entry(\"SOURCEOBJECTID\", 0L),\n                entry(\"ARCHIVEDATE\", 0L),\n                entry(\"CONTENT\", \"comment2\"),\n                entry(\"POSTDATE\", comment2.getPostDate()),\n                entry(\"PROCESSINSTANCEID\", PROCESS2_ID),\n                entry(\"USERID\", null));\n\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/process/instance/model/ConnectorInstanceQueriesTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.bpm.connector.ConnectorEvent.ON_ENTER;\nimport static org.bonitasoft.engine.bpm.connector.ConnectorEvent.ON_FINISH;\nimport static org.bonitasoft.engine.test.persistence.builder.ConnectorInstanceBuilder.aConnectorInstance;\n\nimport java.util.List;\n\nimport org.assertj.core.api.Condition;\nimport org.assertj.core.util.Lists;\nimport org.bonitasoft.engine.bpm.connector.ConnectorState;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAConnectorInstance;\nimport org.bonitasoft.engine.test.persistence.repository.ConnectorInstanceRepository;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.transaction.annotation.Transactional;\n\n/**\n * @author Julien Reboul\n */\n@RunWith(SpringRunner.class)\n@ContextConfiguration(locations = { \"/testContext.xml\" })\n@Transactional\npublic class ConnectorInstanceQueriesTest {\n\n    @Autowired\n    private ConnectorInstanceRepository repository;\n\n    @Autowired\n    private JdbcTemplate jdbcTemplate;\n\n    private SAbstractConnectorInstance expectedConnector1;\n    private SAbstractConnectorInstance expectedConnector2;\n    private String containerType;\n    private long containerId;\n\n    private SAbstractConnectorInstance expectedConnector3;\n\n    private SAbstractConnectorInstance expectedConnector4;\n\n    private SAbstractConnectorInstance expectedConnector5;\n\n    private SAbstractConnectorInstance connectorInstanceOfDifferentContainer;\n\n    private SAbstractConnectorInstance expectedConnector6;\n\n    /**\n     *\n     */\n    @Before\n    public void setUp() {\n        containerType = \"Pouet\";\n        containerId = 1L;\n        long differentContainerId = 5L;\n        expectedConnector1 = aConnectorInstance().setContainerId(containerId).setContainerType(containerType)\n                .setActivationEvent(ON_FINISH)\n                .withFailureInfo(false).setState(ConnectorState.EXECUTING.toString()).build();\n        expectedConnector2 = aConnectorInstance().setContainerId(containerId).setContainerType(containerType)\n                .setActivationEvent(ON_ENTER)\n                .setState(ConnectorState.EXECUTING.toString()).withFailureInfo(false).setExecutionOrder(1)\n                .build();\n        expectedConnector3 = aConnectorInstance().setContainerId(containerId).setContainerType(containerType)\n                .setActivationEvent(ON_ENTER)\n                .setState(ConnectorState.DONE.toString()).withFailureInfo(false)\n                .build();\n        expectedConnector4 = aConnectorInstance().setContainerId(containerId).setContainerType(containerType)\n                .setActivationEvent(ON_ENTER)\n                .setState(ConnectorState.TO_BE_EXECUTED.toString()).withFailureInfo(false).setExecutionOrder(2)\n                .build();\n        expectedConnector5 = aConnectorInstance().setContainerId(containerId).setContainerType(containerType)\n                .setActivationEvent(ON_ENTER)\n                .setState(ConnectorState.FAILED.toString()).withFailureInfo(false)\n                .build();\n        expectedConnector6 = aConnectorInstance().setContainerId(containerId).setContainerType(containerType)\n                .setActivationEvent(ON_FINISH)\n                .setState(ConnectorState.FAILED.toString()).withFailureInfo(false)\n                .build();\n        expectedConnector1.setId(10L);\n        expectedConnector2.setId(2L);\n        expectedConnector3.setId(7L);\n        expectedConnector4.setId(160L);\n        expectedConnector5.setId(1L);\n        expectedConnector6.setId(6L);\n        repository.add(expectedConnector1);\n        repository.add(expectedConnector2);\n        repository.add(expectedConnector3);\n        repository.add(expectedConnector4);\n        repository.add(expectedConnector5);\n        repository.add(expectedConnector6);\n        connectorInstanceOfDifferentContainer = repository\n                .add(aConnectorInstance().setContainerId(differentContainerId).setContainerType(containerType)\n                        .setActivationEvent(ON_ENTER)\n                        .withFailureInfo(false).build());// unexpected connector on different container\n    }\n\n    @Test\n    public void getConnectorInstances() {\n        List<SAbstractConnectorInstance> connectors = repository.getConnectorInstances(containerId, containerType);\n\n        assertThat(connectors).containsOnly(expectedConnector1, expectedConnector2, expectedConnector6,\n                expectedConnector3, expectedConnector4,\n                expectedConnector5);\n    }\n\n    @Test\n    public void getConnectorInstancesOrderedById() {\n\n        List<SAbstractConnectorInstance> connectors = repository\n                .getConnectorInstancesOrderedById(containerId, containerType);\n\n        assertThat(connectors).containsExactly(expectedConnector5, expectedConnector2, expectedConnector6,\n                expectedConnector3, expectedConnector1,\n                expectedConnector4);\n    }\n\n    @Test\n    public void getNumberOfConnectorInstances() {\n        long nbOfConnectors = repository\n                .getNumberOfConnectorInstances(containerId, containerType);\n        assertThat(nbOfConnectors).isEqualTo(6);\n    }\n\n    @Test\n    public void getNextExecutableConnectorInstance() {\n        SConnectorInstance connector = repository\n                .getNextExecutableConnectorInstance(containerId, containerType, ON_ENTER);\n\n        assertThat(connector).isSameAs(expectedConnector2);\n    }\n\n    @Test\n    public void searchSConnectorInstance() {\n        List<SAbstractConnectorInstance> connectors = repository\n                .searchSConnectorInstance();\n\n        assertThat(connectors).containsOnly(expectedConnector1, expectedConnector2, expectedConnector3,\n                expectedConnector4, expectedConnector5,\n                expectedConnector6, connectorInstanceOfDifferentContainer);\n    }\n\n    @Test\n    public void getNumberOfSConnectorInstance() {\n        long nbOfConnectors = repository\n                .getNumberOfSConnectorInstance();\n\n        assertThat(nbOfConnectors).isEqualTo(7);\n    }\n\n    @Test\n    public void getConnectorInstancesWithState() {\n        List<SAbstractConnectorInstance> connectors = repository\n                .getConnectorInstances(containerId, containerType, ON_ENTER,\n                        ConnectorState.TO_BE_EXECUTED.toString());\n\n        assertThat(connectors).containsOnly(expectedConnector4);\n    }\n\n    @Test\n    public void getConnectorInstanceWithFailureInfo() {\n        List<SConnectorInstanceWithFailureInfo> connectors = repository\n                .getConnectorInstanceWithFailureInfo(containerId);\n        final List<Long> idsToFind = Lists.newArrayList(expectedConnector1.getId(), expectedConnector2.getId(),\n                expectedConnector3.getId(),\n                expectedConnector4.getId(), expectedConnector5.getId(), expectedConnector6.getId());\n        assertThat(connectors).areExactly(6, new Condition<SConnectorInstanceWithFailureInfo>() {\n\n            @Override\n            public boolean matches(SConnectorInstanceWithFailureInfo connector) {\n                return idsToFind.remove(connector.getId());\n            }\n        });\n        assertThat(idsToFind).isEmpty();\n    }\n\n    @Test\n    public void getConnectorInstancesWithFailureInState() {\n        List<SConnectorInstanceWithFailureInfo> connectors = repository\n                .getConnectorInstancesWithFailureInfo(containerId, containerType, ConnectorState.FAILED.toString());\n\n        final List<Long> idsToFind = Lists.newArrayList(expectedConnector5.getId(), expectedConnector6.getId());\n        assertThat(connectors).hasSize(2);\n        assertThat(idsToFind).contains(connectors.get(0).getId());\n        assertThat(idsToFind).contains(connectors.get(1).getId());\n    }\n\n    @Test\n    public void should_store_ACTIVATION_EVENT_as_string() {\n        final SAbstractConnectorInstance connectorInstance = repository\n                .add(aConnectorInstance().setActivationEvent(ON_ENTER).build());\n        repository.flush();\n        final String activationEvent = jdbcTemplate.queryForObject(\n                \"select activationEvent from connector_instance where id = \" + connectorInstance.getId(), String.class);\n        assertThat(activationEvent).isEqualTo(\"ON_ENTER\");\n        assertThat(connectorInstance.getActivationEvent()).isEqualTo(ON_ENTER);\n    }\n\n    @Test\n    public void should_store_ACTIVATION_EVENT_as_string_of_archived_connetor() {\n        SAConnectorInstance entity = new SAConnectorInstance();\n        entity.setActivationEvent(ON_FINISH);\n        final SAConnectorInstance connectorInstance = repository.add(entity);\n        repository.flush();\n        final String activationEvent = jdbcTemplate.queryForObject(\n                \"select activationEvent from arch_connector_instance where id = \" + connectorInstance.getId(),\n                String.class);\n        assertThat(activationEvent).isEqualTo(\"ON_FINISH\");\n        assertThat(connectorInstance.getActivationEvent()).isEqualTo(ON_FINISH);\n    }\n\n    @Test\n    public void should_store_stack_trace_of_connector() {\n        SConnectorInstanceWithFailureInfo sConnectorInstanceWithFailureInfo = new SConnectorInstanceWithFailureInfo();\n        sConnectorInstanceWithFailureInfo.setStackTrace(\"a stack trace\");\n        final SConnectorInstanceWithFailureInfo connectorInstance = repository.add(sConnectorInstanceWithFailureInfo);\n        repository.flush();\n        final String stackTrace = jdbcTemplate.queryForObject(\n                \"select stackTrace from connector_instance where id = \" + connectorInstance.getId(), String.class);\n        assertThat(stackTrace).isEqualTo(\"a stack trace\");\n        assertThat(connectorInstance.getStackTrace()).isEqualTo(\"a stack trace\");\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/process/instance/model/CustomUserInfoQueriesTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.test.persistence.builder.CustomUserInfoDefinitionBuilder.aCustomUserInfoDefinition;\nimport static org.bonitasoft.engine.test.persistence.builder.CustomUserInfoValueBuilder.aCustomUserInfoValue;\nimport static org.bonitasoft.engine.test.persistence.builder.UserBuilder.aUser;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.test.persistence.repository.CustomUserInfoRepository;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.transaction.annotation.Transactional;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@RunWith(SpringRunner.class)\n@ContextConfiguration(locations = { \"/testContext.xml\" })\n@Transactional\npublic class CustomUserInfoQueriesTest {\n\n    private static final String DEVELOPER_NAME = \"developer\";\n\n    private static final String SKILLS_NAME = \"skills\";\n\n    @Autowired\n    private CustomUserInfoRepository repository;\n\n    private SUser user1;\n\n    private SUser user2;\n\n    private SUser user3;\n\n    private SUser user4;\n\n    private SCustomUserInfoDefinition skills;\n\n    private SCustomUserInfoDefinition developer;\n\n    @Before\n    public void setUp() {\n        user1 = repository.add(aUser().withUserName(\"john\").build());\n        user2 = repository.add(aUser().withUserName(\"peter\").build());\n        user3 = repository.add(aUser().withUserName(\"paul\").build());\n        user4 = repository.add(aUser().withUserName(\"mike\").build());\n\n        skills = repository.add(aCustomUserInfoDefinition().withName(SKILLS_NAME).build());\n        developer = repository.add(aCustomUserInfoDefinition().withName(DEVELOPER_NAME).build());\n    }\n\n    @Test\n    public void query_getUserIdsWithCustomUserInfo_should_return_empty_list_if_no_users_has_the_chosen_custom_user_info() {\n        // when\n        final List<Long> userIds = repository.getUserIdsWithCustomUserInfo(SKILLS_NAME, \"Java\", false);\n\n        // then\n        assertThat(userIds).isEmpty();\n    }\n\n    @Test\n    public void query_getUserIdsWithCustomUserInfo_should_return_only_ids_of_users_with_chosen_custom_user_info() {\n        // given\n        repository.add(aCustomUserInfoValue().withUserId(user1.getId()).withCustomUserInfoDefinitionId(skills.getId())\n                .withValue(\"Java\").build());\n        repository.add(aCustomUserInfoValue().withUserId(user2.getId())\n                .withCustomUserInfoDefinitionId(developer.getId()).withValue(\"Java\").build());\n        repository.add(aCustomUserInfoValue().withUserId(user3.getId()).withCustomUserInfoDefinitionId(skills.getId())\n                .withValue(\"C\").build());\n        repository.add(aCustomUserInfoValue().withUserId(user4.getId()).withCustomUserInfoDefinitionId(skills.getId())\n                .withValue(\"Java\").build());\n\n        // when\n        final List<Long> userIds = repository.getUserIdsWithCustomUserInfo(SKILLS_NAME, \"Java\", false);\n\n        // then\n        assertThat(userIds).hasSize(2);\n        assertThat(userIds).contains(user1.getId());\n        assertThat(userIds).contains(user4.getId());\n    }\n\n    @Test\n    public void query_getUserIdsWithCustomUserInfoContains_should_return_ids_of_users_with_chosen_custom_user_info_partial_match() {\n        // given\n        repository.add(aCustomUserInfoValue().withUserId(user1.getId()).withCustomUserInfoDefinitionId(skills.getId())\n                .withValue(\"Java\").build());\n        repository.add(aCustomUserInfoValue().withUserId(user2.getId())\n                .withCustomUserInfoDefinitionId(developer.getId()).withValue(\"Java\").build());\n        repository.add(aCustomUserInfoValue().withUserId(user3.getId()).withCustomUserInfoDefinitionId(skills.getId())\n                .withValue(\"C\").build());\n        repository.add(aCustomUserInfoValue().withUserId(user4.getId()).withCustomUserInfoDefinitionId(skills.getId())\n                .withValue(\"Java\").build());\n\n        // when\n        final List<Long> userIdsWholeValue = repository.getUserIdsWithCustomUserInfo(SKILLS_NAME, \"Java\", true);\n        final List<Long> userIdsPartialValue = repository.getUserIdsWithCustomUserInfo(SKILLS_NAME, \"av\", true);\n\n        // then\n        assertThat(userIdsWholeValue).hasSize(2);\n        assertThat(userIdsWholeValue).contains(user1.getId());\n        assertThat(userIdsWholeValue).contains(user4.getId());\n\n        assertThat(userIdsPartialValue).hasSize(2);\n        assertThat(userIdsPartialValue).contains(user1.getId());\n        assertThat(userIdsPartialValue).contains(user4.getId());\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/process/instance/model/EventTriggerInstanceQueriesTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport static org.junit.Assert.assertEquals;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.instance.model.event.SIntermediateCatchEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance;\nimport org.bonitasoft.engine.test.persistence.repository.ProcessInstanceRepository;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.transaction.annotation.Transactional;\n\n@RunWith(SpringRunner.class)\n@ContextConfiguration(locations = { \"/testContext.xml\" })\n@Transactional\npublic class EventTriggerInstanceQueriesTest {\n\n    @Autowired\n    private ProcessInstanceRepository repository;\n\n    @Test\n    public void getNumberOfTimerEventTriggerInstances_should_return_number_of_event_trigger_instance_attached_to_the_process_instance() {\n        // Given\n        final long processInstanceId = 9634L;\n\n        final SIntermediateCatchEventInstance eventInstanceImpl = (SIntermediateCatchEventInstance) repository\n                .add(buildSIntermediateCatchEventInstanceImpl(101L, \"name\", processInstanceId));\n\n        repository.add(buildSTimerEventTriggerInstance(87L, eventInstanceImpl.getId(), \"name\", \"jobTriggerName\"));\n\n        // Then\n        final long numberOfEventTriggerInstances = repository.getNumberOfTimerEventTriggerInstances(processInstanceId,\n                null);\n\n        // When\n        assertEquals(1, numberOfEventTriggerInstances);\n    }\n\n    @Test\n    public void getNumberOfTimerEventTriggerInstances_should_return_number_of_event_trigger_instance_attached_to_the_process_instance_filter_by_event_instance_name() {\n        // Given\n        final long processInstanceId = 9634L;\n\n        final SIntermediateCatchEventInstance eventInstanceImpl = (SIntermediateCatchEventInstance) repository\n                .add(buildSIntermediateCatchEventInstanceImpl(101L, \"name\", processInstanceId));\n        repository.add(buildSTimerEventTriggerInstance(96L, eventInstanceImpl.getId(), \"name\", \"jobTriggerName\"));\n\n        final SIntermediateCatchEventInstance eventInstanceImpl2 = (SIntermediateCatchEventInstance) repository\n                .add(buildSIntermediateCatchEventInstanceImpl(104L, \"toto\", processInstanceId));\n        repository.add(buildSTimerEventTriggerInstance(98L, eventInstanceImpl2.getId(), \"toto\", \"plop\"));\n\n        // Then\n        final long numberOfEventTriggerInstances = repository.getNumberOfTimerEventTriggerInstances(processInstanceId,\n                \"name\");\n\n        // When\n        assertEquals(1, numberOfEventTriggerInstances);\n    }\n\n    @Test\n    public void searchTimerEventTriggerInstances_should_return_event_trigger_instance_attached_to_the_process_instance() {\n        // Given\n        final long processInstanceId = 9634L;\n\n        final SIntermediateCatchEventInstance eventInstanceImpl = (SIntermediateCatchEventInstance) repository\n                .add(buildSIntermediateCatchEventInstanceImpl(101L, \"name\", processInstanceId));\n\n        repository.add(buildSTimerEventTriggerInstance(87L, eventInstanceImpl.getId(), \"name\", \"jobTriggerName\"));\n\n        // Then\n        final List<STimerEventTriggerInstance> sTimerEventTriggerInstances = repository\n                .searchTimerEventTriggerInstances(processInstanceId, null);\n\n        // When\n        assertEquals(1, sTimerEventTriggerInstances.size());\n    }\n\n    @Test\n    public void searchTimerEventTriggerInstances_should_should_return_event_trigger_instance_attached_to_the_process_instance_filter_by_event_instance_name() {\n        // Given\n        final long processInstanceId = 9634L;\n\n        final SIntermediateCatchEventInstance eventInstanceImpl = (SIntermediateCatchEventInstance) repository\n                .add(buildSIntermediateCatchEventInstanceImpl(101L, \"name\", processInstanceId));\n        repository.add(buildSTimerEventTriggerInstance(96L, eventInstanceImpl.getId(), \"name\", \"jobTriggerName\"));\n\n        final SIntermediateCatchEventInstance eventInstanceImpl2 = (SIntermediateCatchEventInstance) repository\n                .add(buildSIntermediateCatchEventInstanceImpl(103L, \"toto\", processInstanceId));\n        repository.add(buildSTimerEventTriggerInstance(98L, eventInstanceImpl2.getId(), \"toto\", \"plop\"));\n\n        // Then\n        final List<STimerEventTriggerInstance> sTimerEventTriggerInstances = repository\n                .searchTimerEventTriggerInstances(processInstanceId, \"toto\");\n\n        // When\n        assertEquals(1, sTimerEventTriggerInstances.size());\n        assertEquals(98L, sTimerEventTriggerInstances.get(0).getId());\n    }\n\n    private STimerEventTriggerInstance buildSTimerEventTriggerInstance(final long id, final long eventInstanceId,\n            final String eventInstanceName,\n            final String jobTriggerName) {\n        final STimerEventTriggerInstance sTimerEventTriggerInstance = new STimerEventTriggerInstance(eventInstanceId,\n                eventInstanceName, 96L,\n                jobTriggerName);\n        sTimerEventTriggerInstance.setId(id);\n        return sTimerEventTriggerInstance;\n    }\n\n    private SIntermediateCatchEventInstance buildSIntermediateCatchEventInstanceImpl(final long id, final String name,\n            final long processInstanceId) {\n        final SIntermediateCatchEventInstance sIntermediateCatchEventInstance = new SIntermediateCatchEventInstance(\n                name, 9, 6, 8, 4, 2);\n        sIntermediateCatchEventInstance.setLogicalGroup(3, processInstanceId);\n        sIntermediateCatchEventInstance.setId(id);\n        return sIntermediateCatchEventInstance;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/process/instance/model/FlowNodeInstanceTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport static java.time.Instant.now;\nimport static java.time.temporal.ChronoUnit.DAYS;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.commons.Pair.pair;\nimport static org.bonitasoft.engine.core.process.definition.model.SGatewayType.PARALLEL;\nimport static org.bonitasoft.engine.test.persistence.builder.ActorBuilder.anActor;\nimport static org.bonitasoft.engine.test.persistence.builder.ActorMemberBuilder.anActorMember;\nimport static org.bonitasoft.engine.test.persistence.builder.BoundaryInstanceBuilder.aBoundary;\nimport static org.bonitasoft.engine.test.persistence.builder.GatewayInstanceBuilder.aGatewayInstanceBuilder;\nimport static org.bonitasoft.engine.test.persistence.builder.LoopActivityInstanceBuilder.aLoopActivity;\nimport static org.bonitasoft.engine.test.persistence.builder.PendingActivityMappingBuilder.aPendingActivityMapping;\nimport static org.bonitasoft.engine.test.persistence.builder.ProcessInstanceBuilder.aProcessInstance;\nimport static org.bonitasoft.engine.test.persistence.builder.UserBuilder.aUser;\nimport static org.bonitasoft.engine.test.persistence.builder.UserMembershipBuilder.aUserMembership;\nimport static org.bonitasoft.engine.test.persistence.builder.UserTaskInstanceBuilder.aUserTask;\nimport static org.bonitasoft.engine.test.persistence.builder.archive.ArchivedUserTaskInstanceBuilder.anArchivedUserTask;\n\nimport java.time.Duration;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.actor.mapping.model.SActor;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SEndEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SIntermediateCatchEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SIntermediateThrowEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SStartEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.test.persistence.repository.FlowNodeInstanceRepository;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.transaction.annotation.Transactional;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\n@RunWith(SpringRunner.class)\n@ContextConfiguration(locations = { \"/testContext.xml\" })\n@Transactional\npublic class FlowNodeInstanceTest {\n\n    private static final long GROUP_FOR_BOB_ID = 74L;\n\n    private static final long GROUP_FOR_JOHN_ID = 9875L;\n\n    private static final long JOHN_ID = 654L;\n\n    private static final long BOB_ID = 697L;\n\n    private static final long PROCESS_DEFINITION_ID = 111L;\n\n    private static final long ROOT_PROCESS_INSTANCE_ID = 963L;\n\n    private static final long NORMAL_HUMAN_INSTANCE_ID = 743L;\n\n    @Autowired\n    private FlowNodeInstanceRepository repository;\n\n    @Autowired\n    private JdbcTemplate jdbcTemplate;\n\n    @Before\n    public void before() {\n        repository.add(aUser().withId(JOHN_ID).build());\n        repository.add(aUser().withId(BOB_ID).build());\n\n        // Create process\n        final String processNameSupervisedByJohn = \"Process definition\";\n        buildAndCreateProcessDefinition(6L, PROCESS_DEFINITION_ID, processNameSupervisedByJohn);\n        repository.add(\n                aProcessInstance().withProcessDefinitionId(PROCESS_DEFINITION_ID).withName(processNameSupervisedByJohn)\n                        .withId(ROOT_PROCESS_INSTANCE_ID).build());\n    }\n\n    protected void buildAndCreateProcessDefinition(final long id, final long processDefinitionId,\n            final String processName) {\n        final SProcessDefinitionDeployInfo sProcessDefinitionDeployInfo = new SProcessDefinitionDeployInfo();\n        sProcessDefinitionDeployInfo.setId(id);\n        sProcessDefinitionDeployInfo.setName(processName);\n        sProcessDefinitionDeployInfo.setVersion(\"version\");\n        sProcessDefinitionDeployInfo.setProcessId(processDefinitionId);\n        repository.add(sProcessDefinitionDeployInfo);\n    }\n\n    @Test\n    public void getFlowNodeInstanceIdsToRecover_should_return_ids_of_flow_nodes_that_needs_to_be_recovered() {\n        // given\n        repository\n                .add(aUserTask().withName(\"normalTask1\").withStateExecuting(false).withStable(true).withTerminal(false)\n                        .build());\n        repository\n                .add(aUserTask().withName(\"executingTask\").withStateExecuting(true).withStable(true).withTerminal(false)\n                        .build());\n        repository.add(\n                aUserTask().withName(\"notStableTask\").withStateExecuting(false).withStable(false).withTerminal(true)\n                        .build());\n        final SFlowNodeInstance terminal = repository\n                .add(aUserTask().withName(\"terminalTask\").withStateExecuting(false).withStable(true).withTerminal(true)\n                        .build());\n        repository.add(aUserTask().withName(\"failedTask\").withStateExecuting(true).withStable(true).withTerminal(true)\n                .withStateId(3).build());\n        repository.add(aUserTask().withName(\"normalTask2\").withStateExecuting(false).withStable(true)\n                .withTerminal(false).build());\n        repository.add(aGatewayInstanceBuilder().withName(\"gateway_completed\").withStateId(2).withStable(true)\n                .withTerminal(true).withHitBys(\"FINISH:12\").build());\n        repository.add(aGatewayInstanceBuilder().withName(\"gateway_initializing_but_not_finished1\").withStateId(61)\n                .withStable(false).withHitBys(\"1\").build());\n\n        repository.add(aBoundary().withName(\"abortingBoundary\").withActivity(terminal.getId()).withStateId(10)\n                .withStateExecuting(false).withStable(true)\n                .withTerminal(false).withStateName(\"WAITING\")\n                .withStateCategory(SStateCategory.ABORTING).build());\n        repository.add(aBoundary().withName(\"cancellingBoundary\").withActivity(terminal.getId()).withStateId(10)\n                .withStateExecuting(false).withStable(true)\n                .withTerminal(false).withStateName(\"WAITING\")\n                .withStateCategory(SStateCategory.CANCELLING).build());\n        repository.add(aBoundary().withName(\"errorBoundary\").withActivity(terminal.getId()).withStateId(10)\n                .withStateExecuting(false).withStable(true)\n                .withTerminal(false).withStateName(\"WAITING\")\n                .withStateCategory(SStateCategory.NORMAL).build());\n\n        // when\n        final QueryOptions options = new QueryOptions(0, 10);\n        final List<Long> nodeToRestart = repository.getFlowNodeInstanceIdsToRecover(Duration.ZERO, options);\n\n        // then\n        assertThat(nodeToRestart.stream()\n                .map(id -> repository.getSession()\n                        .get(SFlowNodeInstance.class, id).getName()))\n                .containsOnly(\"executingTask\", \"notStableTask\", \"terminalTask\",\n                        \"abortingBoundary\", \"cancellingBoundary\");\n    }\n\n    @Test\n    public void getGatewayInstanceIdsToRecover_should_return_ids_of_gateways_flagged_as_FINISH() {\n        // given\n        repository.add(aGatewayInstanceBuilder().withName(\"gateway_initializing_but_not_finished1\").withStateId(61)\n                .withStable(false).withHitBys(\"1\").build());\n        repository.add(aGatewayInstanceBuilder().withName(\"gateway_initializing_but_not_finished2\").withStateId(61)\n                .withStable(false).withHitBys(\"1,2\").build());\n        repository.add(aGatewayInstanceBuilder().withName(\"gateway_initializing_but_finished\").withStateId(61)\n                .withStable(false).withHitBys(\"FINISH:12\").build());\n        repository.add(aGatewayInstanceBuilder().withName(\"gateway_failed\").withStateExecuting(true).withStable(true)\n                .withTerminal(true).withStateId(3).build());\n        repository.add(aGatewayInstanceBuilder().withName(\"gateway_completed\").withStateId(2).withStable(true)\n                .withTerminal(true).withHitBys(\"FINISH:12\").build());\n        repository.add(aGatewayInstanceBuilder().withName(\"gateway_aborting\").withStateId(61).withStable(true)\n                .withHitBys(\"1,2\").withStateCategory(SStateCategory.ABORTING).build());\n        repository.add(aGatewayInstanceBuilder().withName(\"gateway_cancelling\").withStateId(61).withStable(true)\n                .withHitBys(\"1,2\").withStateCategory(SStateCategory.ABORTING).build());\n\n        repository.flush();\n        // when\n        final QueryOptions options = new QueryOptions(0, 10);\n        final List<Long> nodeToRestart = repository.getGatewayInstanceIdsToRecover(Duration.ZERO, options);\n\n        // then\n        assertThat(nodeToRestart.stream()\n                .map(id -> (repository.getSession().get(SFlowNodeInstance.class, id)).getName()))\n                .containsOnly(\n                        \"gateway_initializing_but_finished\",\n                        \"gateway_completed\",\n                        \"gateway_aborting\",\n                        \"gateway_cancelling\");\n    }\n\n    @Test\n    public void getGatewayInstanceIdsToRecover_should_return_ids_of_gateways_flagged_as_FINISH_and_older_than_given_duration() {\n        // given\n        repository.add(aGatewayInstanceBuilder().withName(\"gateway_initializing_but_finished\")\n                .withLastUpdateDate(now().minusSeconds(200).toEpochMilli()).withStateId(61).withStable(false)\n                .withHitBys(\"FINISH:12\").build());\n        repository.add(aGatewayInstanceBuilder().withName(\"gateway_completed\").withStateId(2).withStable(true)\n                .withLastUpdateDate(now().minusSeconds(600).toEpochMilli()).withTerminal(true).withHitBys(\"FINISH:12\")\n                .build());\n        repository.add(aGatewayInstanceBuilder().withName(\"gateway_aborting\")\n                .withLastUpdateDate(now().minus(2, DAYS).toEpochMilli()).withStateId(61).withStable(true)\n                .withHitBys(\"1,2\").withStateCategory(SStateCategory.ABORTING).build());\n\n        repository.flush();\n        // when\n        final QueryOptions options = new QueryOptions(0, 10);\n        final List<Long> nodeToRestart = repository.getGatewayInstanceIdsToRecover(Duration.ofSeconds(500), options);\n\n        // then\n        assertThat(nodeToRestart.stream()\n                .map(id -> (repository.getSession().get(SFlowNodeInstance.class, id)).getName()))\n                .containsOnly(\n                        \"gateway_completed\",\n                        \"gateway_aborting\");\n    }\n\n    @Test\n    public void getFlowNodeInstanceIdsToRecover_should_return_only_element_older_than_given_duration() {\n        // given\n        long now = System.currentTimeMillis();\n\n        SFlowNodeInstance oldTask1 = repository\n                .add(aUserTask().withName(\"oldTask1\").withTerminal(true)\n                        .withLastUpdateDate(now().minus(2, DAYS).toEpochMilli()).build());\n        SFlowNodeInstance oldTask2 = repository\n                .add(aUserTask().withName(\"oldTask2\").withTerminal(true)\n                        .withLastUpdateDate(now().minusSeconds(600).toEpochMilli()).build());\n\n        repository\n                .add(aUserTask().withName(\"recentTask1\").withTerminal(true)\n                        .withLastUpdateDate(now().minusSeconds(400).toEpochMilli()).build());\n        repository.add(aUserTask().withName(\"recentTask2\").withTerminal(true).withLastUpdateDate(now).build());\n        // when\n        List<Long> nodeToRestart = repository.getFlowNodeInstanceIdsToRecover(Duration.ofSeconds(500),\n                new QueryOptions(0, 10));\n\n        // then\n        assertThat(nodeToRestart).containsOnly(oldTask1.getId(), oldTask2.getId());\n    }\n\n    // For\n    @Test\n    public void getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcessFor_should_return_number_of_process_definition_if_one_instance_has_assigned_tasks_to_the_user() {\n        // Given\n        buildAndAddAssignedTasks();\n\n        // When\n        final long numberOfHumanTaskInstances = repository\n                .getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcessFor(PROCESS_DEFINITION_ID,\n                        JOHN_ID);\n        // Then\n        assertThat(numberOfHumanTaskInstances).isEqualTo(1);\n    }\n\n    @Test\n    public void getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcessFor_should_return_number_of_process_definition_if_one_instance_has_pending_tasks_for_the_user() {\n        // Given\n        buildAndAddTasksWithPendingMappingForUser();\n\n        // When\n        final long numberOfHumanTaskInstances = repository\n                .getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcessFor(PROCESS_DEFINITION_ID,\n                        JOHN_ID);\n\n        // Then\n        assertThat(numberOfHumanTaskInstances).isEqualTo(1);\n    }\n\n    @Test\n    public void getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcessFor_should_return_number_of_process_definition_if_one_instance_has_pending_tasks_for_a_actor_with_the_user_as_member() {\n        // Given\n        buildAndAddTasksWithPendingMappingForActorMappedToUser();\n\n        // When\n        final long numberOfHumanTaskInstances = repository\n                .getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcessFor(PROCESS_DEFINITION_ID,\n                        JOHN_ID);\n\n        // Then\n        assertThat(numberOfHumanTaskInstances).isEqualTo(1);\n    }\n\n    @Test\n    public void getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcessFor_should_return_number_of_process_definition_if_one_instance_has_pending_tasks_for_a_actor_with_a_membership_mapped_to_the_user_as_member() {\n        // Given\n        buildAndAddTasksWithPendingMappingForActorMappedToUserMembershipMappedToUser();\n\n        // When\n        final long numberOfHumanTaskInstances = repository\n                .getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcessFor(PROCESS_DEFINITION_ID,\n                        JOHN_ID);\n\n        // Then\n        assertThat(numberOfHumanTaskInstances).isEqualTo(1);\n    }\n\n    @Test\n    public void searchSHumanTaskInstanceAssignedAndPendingByRootProcessFor_should_return_number_of_process_definition_if_one_instance_has_assigned_tasks_to_the_user() {\n        // Given\n        buildAndAddAssignedTasks();\n\n        // When\n        final List<SHumanTaskInstance> sHumanTaskInstances = repository\n                .searchSHumanTaskInstanceAssignedAndPendingByRootProcessFor(PROCESS_DEFINITION_ID,\n                        JOHN_ID);\n\n        // Then\n        assertThat(sHumanTaskInstances).hasSize(1);\n        assertThat(sHumanTaskInstances.get(0).getId()).isEqualTo(NORMAL_HUMAN_INSTANCE_ID);\n    }\n\n    @Test\n    public void searchSHumanTaskInstanceAssignedAndPendingByRootProcessFor_should_return_number_of_process_definition_if_one_instance_has_pending_tasks_for_the_user() {\n        // Given\n        buildAndAddTasksWithPendingMappingForUser();\n\n        // When\n        final List<SHumanTaskInstance> sHumanTaskInstances = repository\n                .searchSHumanTaskInstanceAssignedAndPendingByRootProcessFor(PROCESS_DEFINITION_ID,\n                        JOHN_ID);\n\n        // Then\n        assertThat(sHumanTaskInstances).hasSize(1);\n        assertThat(sHumanTaskInstances.get(0).getId()).isEqualTo(NORMAL_HUMAN_INSTANCE_ID);\n    }\n\n    @Test\n    public void searchSHumanTaskInstanceAssignedAndPendingByRootProcessFor_should_return_number_of_process_definition_if_one_instance_has_pending_tasks_for_a_actor_with_the_user_as_member() {\n        // Given\n        buildAndAddTasksWithPendingMappingForActorMappedToUser();\n\n        // When\n        final List<SHumanTaskInstance> sHumanTaskInstances = repository\n                .searchSHumanTaskInstanceAssignedAndPendingByRootProcessFor(PROCESS_DEFINITION_ID,\n                        JOHN_ID);\n\n        // Then\n        assertThat(sHumanTaskInstances).hasSize(1);\n        assertThat(sHumanTaskInstances.get(0).getId()).isEqualTo(NORMAL_HUMAN_INSTANCE_ID);\n    }\n\n    @Test\n    public void searchSHumanTaskInstanceAssignedAndPendingByRootProcessFor_should_return_number_of_process_definition_if_one_instance_has_pending_tasks_for_a_actor_with_a_membership_mapped_to_the_user_as_member() {\n        // Given\n        buildAndAddTasksWithPendingMappingForActorMappedToUserMembershipMappedToUser();\n\n        // When\n        final List<SHumanTaskInstance> sHumanTaskInstances = repository\n                .searchSHumanTaskInstanceAssignedAndPendingByRootProcessFor(PROCESS_DEFINITION_ID,\n                        JOHN_ID);\n\n        // Then\n        assertThat(sHumanTaskInstances).hasSize(1);\n        assertThat(sHumanTaskInstances.get(0).getId()).isEqualTo(NORMAL_HUMAN_INSTANCE_ID);\n    }\n\n    // All\n    @Test\n    public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasks_should_return_number_of_process_definition_supervised_if_one_instance_has_assigned_tasks() {\n        // Given\n        buildAndAddAssignedTasks();\n\n        // When\n        final long numberOfHumanTaskInstances = repository\n                .getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcess(PROCESS_DEFINITION_ID);\n\n        // Then\n        assertThat(numberOfHumanTaskInstances).isEqualTo(2);\n    }\n\n    @Test\n    public void getNumberOfFlowNodesOfProcessDefinitionInAllStates_should_return_results_aggregated_by_name_and_state() {\n        buildAndAddUserTaskWithProcessDefinitionId(\"step1\", 15L, 28, \"executing\");\n        buildAndAddUserTaskWithProcessDefinitionId(\"step1\", 15L, 28, \"executing\");\n        buildAndAddUserTaskWithProcessDefinitionId(\"step1\", 15L, 3, \"failed\");\n        buildAndAddUserTaskWithProcessDefinitionId(\"step1\", 1L, 29, \"someflownodeStateName\"); // should not be selected because it is a flownode of another process\n        buildAndAddUserTaskWithProcessDefinitionId(\"step2\", 15L, 3, \"failed\");\n        buildAndAddUserTaskWithProcessDefinitionId(\"step1\", 15L, 4, \"ready\");\n\n        List<SFlowNodeInstanceStateCounter> counters = repository\n                .getNumberOfFlowNodesOfProcessDefinitionInAllStates(15L);\n        assertThat(counters).hasSize(4);\n\n        assertThat(counters.get(0).getFlownodeName()).isEqualTo(\"step1\");\n        assertThat(counters.get(0).getStateName()).isEqualTo(\"executing\");\n        assertThat(counters.get(0).getNumberOf()).isEqualTo(2L);\n\n        assertThat(counters.get(1).getFlownodeName()).isEqualTo(\"step1\");\n        assertThat(counters.get(1).getStateName()).isEqualTo(\"failed\");\n        assertThat(counters.get(1).getNumberOf()).isEqualTo(1L);\n\n        assertThat(counters.get(2).getFlownodeName()).isEqualTo(\"step1\");\n        assertThat(counters.get(2).getStateName()).isEqualTo(\"ready\");\n        assertThat(counters.get(2).getNumberOf()).isEqualTo(1L);\n\n        assertThat(counters.get(3).getFlownodeName()).isEqualTo(\"step2\");\n        assertThat(counters.get(3).getStateName()).isEqualTo(\"failed\");\n        assertThat(counters.get(3).getNumberOf()).isEqualTo(1L);\n\n    }\n\n    private SFlowNodeInstance buildAndAddUserTaskWithProcessDefinitionId(final String taskName,\n            final long processDefinitionId,\n            int stateId, String stateName) {\n        return repository\n                .add(aUserTask().withName(taskName).withStateExecuting(false).withStable(true).withTerminal(false)\n                        .withLogicalGroup1(processDefinitionId)\n                        .withStateId(stateId).withStateName(stateName).build());\n    }\n\n    @Test\n    public void getNumberOfFlowNodesInAllStates_should_return_results_aggregated_by_name_and_state() {\n        buildAndAddUserTaskWithParentAndRootProcessInstanceId(\"step1\", 147L, 0L, 28, \"executing\");\n        buildAndAddUserTaskWithParentAndRootProcessInstanceId(\"step1\", 147L, 0L, 28, \"executing\");\n        buildAndAddUserTaskWithParentAndRootProcessInstanceId(\"step1\", 147L, 0L, 3, \"failed\");\n        buildAndAddUserTaskWithParentAndRootProcessInstanceId(\"step1\", 0L, 147L, 29, \"someflownodeStateName\"); // should not be selected because it is a flownode in a sub-process\n        buildAndAddUserTaskWithParentAndRootProcessInstanceId(\"step2\", 147L, 0L, 3, \"failed\");\n        buildAndAddUserTaskWithParentAndRootProcessInstanceId(\"step1\", 147L, 0L, 4, \"ready\");\n\n        List<SFlowNodeInstanceStateCounter> counters = repository.getNumberOfFlowNodesInAllStates(147L);\n        assertThat(counters).hasSize(4);\n\n        assertThat(counters.get(0).getFlownodeName()).isEqualTo(\"step1\");\n        assertThat(counters.get(0).getStateName()).isEqualTo(\"executing\");\n        assertThat(counters.get(0).getNumberOf()).isEqualTo(2L);\n\n        assertThat(counters.get(1).getFlownodeName()).isEqualTo(\"step1\");\n        assertThat(counters.get(1).getStateName()).isEqualTo(\"failed\");\n        assertThat(counters.get(1).getNumberOf()).isEqualTo(1L);\n\n        assertThat(counters.get(2).getFlownodeName()).isEqualTo(\"step1\");\n        assertThat(counters.get(2).getStateName()).isEqualTo(\"ready\");\n        assertThat(counters.get(2).getNumberOf()).isEqualTo(1L);\n\n        assertThat(counters.get(3).getFlownodeName()).isEqualTo(\"step2\");\n        assertThat(counters.get(3).getStateName()).isEqualTo(\"failed\");\n        assertThat(counters.get(3).getNumberOf()).isEqualTo(1L);\n\n    }\n\n    private SFlowNodeInstance buildAndAddUserTaskWithParentAndRootProcessInstanceId(final String taskName,\n            final long containingProcessInstanceId,\n            final long rootProcessInstanceId, int stateId, String stateName) {\n        return repository\n                .add(aUserTask().withName(taskName).withStateExecuting(false).withStable(true).withTerminal(false)\n                        .withLogicalGroup4(containingProcessInstanceId).withLogicalGroup2(rootProcessInstanceId)\n                        .withStateId(stateId).withStateName(stateName).build());\n    }\n\n    @Test\n    public void getNumberOfArchivedFlowNodesInAllStates_should_return_results_aggregated_by_name_and_state() {\n        buildAndAddArchivedUserTaskWithParentAndRootProcessInstanceId(\"step1\", 147L, 0L, 2, \"completed\", true);\n        buildAndAddArchivedUserTaskWithParentAndRootProcessInstanceId(\"step1\", 147L, 0L, 2, \"completed\", true);\n        buildAndAddArchivedUserTaskWithParentAndRootProcessInstanceId(\"step1\", 147L, 0L, 2, \"completed\", false); // should not be selected because non-terminal state\n        buildAndAddArchivedUserTaskWithParentAndRootProcessInstanceId(\"step1\", 0L, 147L, 221, \"not_used\", true); // should not be selected because it is a flownode in a sub-process\n        buildAndAddArchivedUserTaskWithParentAndRootProcessInstanceId(\"step2\", 147L, 0L, 2, \"completed\", true);\n        buildAndAddArchivedUserTaskWithParentAndRootProcessInstanceId(\"step1\", 147L, 0L, 4, \"aborted\", true);\n\n        List<SFlowNodeInstanceStateCounter> counters = repository.getNumberOfArchivedFlowNodesInAllStates(147L);\n        assertThat(counters).hasSize(3);\n\n        assertThat(counters.get(0).getFlownodeName()).isEqualTo(\"step1\");\n        assertThat(counters.get(0).getStateName()).isEqualTo(\"aborted\");\n        assertThat(counters.get(0).getNumberOf()).isEqualTo(1L);\n\n        assertThat(counters.get(1).getFlownodeName()).isEqualTo(\"step1\");\n        assertThat(counters.get(1).getStateName()).isEqualTo(\"completed\");\n        assertThat(counters.get(1).getNumberOf()).isEqualTo(2L);\n\n        assertThat(counters.get(2).getFlownodeName()).isEqualTo(\"step2\");\n        assertThat(counters.get(2).getStateName()).isEqualTo(\"completed\");\n        assertThat(counters.get(2).getNumberOf()).isEqualTo(1L);\n\n    }\n\n    private SAFlowNodeInstance buildAndAddArchivedUserTaskWithParentAndRootProcessInstanceId(String taskName,\n            long containingProcessInstanceId,\n            long rootProcessInstanceId, int stateId, String stateName, boolean terminal) {\n        return repository.add(anArchivedUserTask().withName(taskName).withLogicalGroup4(containingProcessInstanceId)\n                .withLogicalGroup2(rootProcessInstanceId)\n                .withStateId(stateId).withStateName(stateName).withTerminal(terminal).build());\n    }\n\n    @Test\n    public void getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcess_should_return_number_of_process_definition_supervised_if_one_instance_has_pending_tasks_for_a_user() {\n        // Given\n        buildAndAddTasksWithPendingMappingForUser();\n\n        // When\n        final long numberOfHumanTaskInstances = repository\n                .getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcess(PROCESS_DEFINITION_ID);\n\n        // Then\n        assertThat(numberOfHumanTaskInstances).isEqualTo(2);\n    }\n\n    @Test\n    public void getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcess_should_return_number_of_process_definition_supervised_if_one_instance_has_pending_tasks_for_a_actor_with_a_user_as_member() {\n        // Given\n        buildAndAddTasksWithPendingMappingForActorMappedToUser();\n\n        // When\n        final long numberOfHumanTaskInstances = repository\n                .getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcess(PROCESS_DEFINITION_ID);\n\n        // Then\n        assertThat(numberOfHumanTaskInstances).isEqualTo(2);\n    }\n\n    @Test\n    public void getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcess_should_return_number_of_process_definition_supervised_if_one_instance_has_pending_tasks_for_a_actor_with_a_membership_mapped_to_a_user_as_member() {\n        // Given\n        buildAndAddTasksWithPendingMappingForActorMappedToUserMembershipMappedToUser();\n\n        // When\n        final long numberOfHumanTaskInstances = repository\n                .getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcess(PROCESS_DEFINITION_ID);\n\n        // Then\n        assertThat(numberOfHumanTaskInstances).isEqualTo(2);\n    }\n\n    @Test\n    public void searchSHumanTaskInstanceAssignedAndPendingByRootProcess_should_return_number_of_process_definition_supervised_if_one_instance_has_assigned_tasks() {\n        // Given\n        buildAndAddAssignedTasks();\n\n        // When\n        final List<SHumanTaskInstance> sHumanTaskInstances = repository\n                .searchSHumanTaskInstanceAssignedAndPendingByRootProcess(PROCESS_DEFINITION_ID);\n\n        // Then\n        assertThat(sHumanTaskInstances).hasSize(2);\n        assertThat(sHumanTaskInstances.get(0).getId()).isEqualTo(NORMAL_HUMAN_INSTANCE_ID);\n    }\n\n    @Test\n    public void searchSHumanTaskInstanceAssignedAndPendingByRootProcess_should_return_number_of_process_definition_supervised_if_one_instance_has_pending_tasks_for_a_user() {\n        // Given\n        buildAndAddTasksWithPendingMappingForUser();\n\n        // When\n        final List<SHumanTaskInstance> sHumanTaskInstances = repository\n                .searchSHumanTaskInstanceAssignedAndPendingByRootProcess(PROCESS_DEFINITION_ID);\n\n        // Then\n        assertThat(sHumanTaskInstances).hasSize(2);\n        assertThat(sHumanTaskInstances.get(0).getId()).isEqualTo(NORMAL_HUMAN_INSTANCE_ID);\n    }\n\n    @Test\n    public void searchSHumanTaskInstanceAssignedAndPendingByRootProcess_should_return_number_of_process_definition_supervised_if_one_instance_has_pending_tasks_for_a_actor_with_a_user_as_member() {\n        // Given\n        buildAndAddTasksWithPendingMappingForActorMappedToUser();\n\n        // When\n        final List<SHumanTaskInstance> sHumanTaskInstances = repository\n                .searchSHumanTaskInstanceAssignedAndPendingByRootProcess(PROCESS_DEFINITION_ID);\n\n        // Then\n        assertThat(sHumanTaskInstances).hasSize(2);\n        assertThat(sHumanTaskInstances.get(0).getId()).isEqualTo(NORMAL_HUMAN_INSTANCE_ID);\n    }\n\n    @Test\n    public void searchSHumanTaskInstanceAssignedAndPendingByRootProcess_should_return_number_of_process_definition_supervised_if_one_instance_has_pending_tasks_for_a_actor_with_a_membership_mapped_to_a_user_as_member() {\n        // Given\n        buildAndAddTasksWithPendingMappingForActorMappedToUserMembershipMappedToUser();\n\n        // When\n        final List<SHumanTaskInstance> sHumanTaskInstances = repository\n                .searchSHumanTaskInstanceAssignedAndPendingByRootProcess(PROCESS_DEFINITION_ID);\n\n        // Then\n        assertThat(sHumanTaskInstances).hasSize(2);\n        assertThat(sHumanTaskInstances.get(0).getId()).isEqualTo(NORMAL_HUMAN_INSTANCE_ID);\n    }\n\n    @Test\n    public void getActiveGatewayInstance_should_return_gateway_if_not_finished() {\n        // Given\n        final SGatewayInstance gatewayInstance = aGatewayInstanceBuilder().withHitBys(\"1,2\").withName(\"gate1\")\n                .withTerminal(false)\n                .withLogicalGroup4(ROOT_PROCESS_INSTANCE_ID).build();\n        repository.add(gatewayInstance);\n\n        // When\n        final SGatewayInstance gate1 = repository.getActiveGatewayInstanceOfProcess(ROOT_PROCESS_INSTANCE_ID, \"gate1\");\n\n        // Then\n        assertThat(gate1).isEqualTo(gatewayInstance);\n    }\n\n    @Test\n    public void getActiveGatewayInstance_should_not_return_gateway_if_finished() {\n        // Given\n        repository\n                .add(aGatewayInstanceBuilder().withHitBys(\"FINISH:1\").withTerminal(true).withName(\"gate1\")\n                        .withLogicalGroup4(ROOT_PROCESS_INSTANCE_ID).build());\n\n        // When\n        final SGatewayInstance gate1 = repository.getActiveGatewayInstanceOfProcess(ROOT_PROCESS_INSTANCE_ID, \"gate1\");\n\n        // Then\n        assertThat(gate1).isNull();\n    }\n\n    @Test\n    public void getActiveGatewayInstance_should_not_return_gateway_if_wrong_name() {\n        // Given\n        final SGatewayInstance gatewayInstance = aGatewayInstanceBuilder().withHitBys(\"1,2\")\n                .withName(\"notTheGoodGateway\").withTerminal(false)\n                .withLogicalGroup4(ROOT_PROCESS_INSTANCE_ID).build();\n        repository.add(gatewayInstance);\n\n        // When\n        final SGatewayInstance gate1 = repository.getActiveGatewayInstanceOfProcess(ROOT_PROCESS_INSTANCE_ID, \"gate1\");\n\n        // Then\n        assertThat(gate1).isNull();\n    }\n\n    @Test\n    public void should_get_flownodeInstances_by_stateCategory() {\n        // Given\n        final SFlowNodeInstance sFlowNodeInstance = repository\n                .add(aUserTask().withName(\"executingTask\").withStateExecuting(true).build());\n        repository.flush();\n        final String stateCategory = jdbcTemplate.queryForObject(\"select stateCategory from flownode_instance\",\n                String.class);\n        final String priority = jdbcTemplate.queryForObject(\"select priority from flownode_instance\", String.class);\n        assertThat(stateCategory).isEqualTo(\"NORMAL\");\n        assertThat(priority).isEqualTo(\"2\");\n    }\n\n    @Test\n    public void should_getDirectChildrenOfProcessInstance() {\n        // Given\n        //is directly contained in the process \"5\"\n        repository.add(aUserTask().withLogicalGroup3(0).withLogicalGroup4(5).withName(\"multiInstance\")\n                .withStateExecuting(true).build());\n\n        //is directly contained in the activity \"6\" that is in the process \"5\"\n        repository.add(aUserTask().withLogicalGroup3(6).withLogicalGroup4(5).withName(\"callActivity\")\n                .withStateExecuting(true).build());\n        repository.flush();\n\n        List<SFlowNodeInstance> flowNodes = repository.selectList(\"getDirectChildrenOfProcessInstance\",\n                pair(\"parentProcessInstanceId\", 5L));\n        assertThat(flowNodes).hasSize(1).anyMatch(f -> f.getName().equals(\"multiInstance\"));\n    }\n\n    @Test\n    public void should_getAllChildrenOfProcessInstance() {\n        // Given\n        //is directly contained in the process \"5\"\n        repository.add(aUserTask().withLogicalGroup3(0).withLogicalGroup4(5).withName(\"multiInstance\")\n                .withStateExecuting(true).build());\n\n        //is directly contained in the activity \"6\" that is in the process \"5\"\n        repository.add(aUserTask().withLogicalGroup3(6).withLogicalGroup4(5).withName(\"callActivity\")\n                .withStateExecuting(true).build());\n        repository.flush();\n\n        List<SFlowNodeInstance> flowNodes = repository.selectList(\"getAllChildrenOfProcessInstance\",\n                pair(\"parentProcessInstanceId\", 5L));\n        assertThat(flowNodes).hasSize(2)\n                .anyMatch(f -> f.getName().equals(\"multiInstance\"))\n                .anyMatch(f -> f.getName().equals(\"callActivity\"));\n    }\n\n    @Test\n    public void should_have_loopCounter_on_loop_Activity() {\n        // Given\n        final SLoopActivityInstance sLoopActivityInstance = (SLoopActivityInstance) repository\n                .add((PersistentObject) aLoopActivity().withLoopCounter(6).build());\n        repository.flush();\n        final int loopCounter = jdbcTemplate.queryForObject(\"select loop_counter from flownode_instance\",\n                Integer.class);\n        repository.getById(sLoopActivityInstance.getId());\n        assertThat(loopCounter).isEqualTo(6);\n        assertThat(sLoopActivityInstance.getLoopCounter()).isEqualTo(6);\n    }\n\n    @Test\n    public void should_get_gateway_instances_by_gateway_type() {\n        // Given\n        final SGatewayInstance gatewayInstance = repository\n                .add(aGatewayInstanceBuilder().withGatewayType(PARALLEL).build());\n        repository.flush();\n        final String gatewayType = jdbcTemplate.queryForObject(\"select gatewayType from flownode_instance\",\n                String.class);\n        assertThat(gatewayType).isEqualTo(\"PARALLEL\");\n        assertThat(gatewayInstance.getGatewayType()).isEqualTo(PARALLEL);\n    }\n\n    private void buildAndAddAssignedTasks() {\n        // Tasks OK assigned to John\n        repository\n                .add(aUserTask().withName(\"normalTask1\").withStateExecuting(false).withStable(true).withTerminal(false)\n                        .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID).withAssigneeId(JOHN_ID)\n                        .withId(NORMAL_HUMAN_INSTANCE_ID).build());\n\n        // Tasks KO assigned to john & OK not assigned\n        repository\n                .add(aUserTask().withName(\"executingTask\").withStateExecuting(true).withStable(true).withTerminal(false)\n                        .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID).withAssigneeId(JOHN_ID).build());\n        repository.add(\n                aUserTask().withName(\"notStableTask\").withStateExecuting(false).withStable(false).withTerminal(true)\n                        .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID).withAssigneeId(JOHN_ID).build());\n        repository\n                .add(aUserTask().withName(\"terminalTask\").withStateExecuting(false).withStable(true).withTerminal(true)\n                        .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID).withAssigneeId(JOHN_ID).build());\n        buildAndAddNormalTask(\"normalTask4\", ROOT_PROCESS_INSTANCE_ID);\n\n        // Tasks OK assigned to Bob\n        repository\n                .add(aUserTask().withName(\"normalTask2\").withStateExecuting(false).withStable(true).withTerminal(false)\n                        .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID).withAssigneeId(BOB_ID).build());\n    }\n\n    private void buildAndAddTasksWithPendingMappingForUser() {\n        // Tasks OK not assigned & pending for John\n        final SFlowNodeInstance normalTask1 = repository\n                .add(aUserTask().withName(\"normalTask1\").withStateExecuting(false).withStable(true).withTerminal(false)\n                        .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID).withId(NORMAL_HUMAN_INSTANCE_ID).build());\n        repository.add(aPendingActivityMapping().withUserId(JOHN_ID).withActivityId(normalTask1.getId()).build());\n\n        // Tasks KO not assigned & pending for john, and OK not assigned & not pending\n        final SFlowNodeInstance executingTask = buildAndAddExecutingTask();\n        repository.add(aPendingActivityMapping().withUserId(JOHN_ID).withActivityId(executingTask.getId()).build());\n        final SFlowNodeInstance notStableTask = buildAndAddNotStableTask();\n        repository.add(aPendingActivityMapping().withUserId(JOHN_ID).withActivityId(notStableTask.getId()).build());\n        final SFlowNodeInstance terminalTask = buildAndAddTerminalTask();\n        repository.add(aPendingActivityMapping().withUserId(JOHN_ID).withActivityId(terminalTask.getId()).build());\n        buildAndAddNormalTask(\"normalTask4\", ROOT_PROCESS_INSTANCE_ID);\n\n        // Tasks OK not assigned & pending for Bob\n        final SFlowNodeInstance normalTask4 = buildAndAddNormalTask(\"normalTask2\", ROOT_PROCESS_INSTANCE_ID);\n        repository.add(aPendingActivityMapping().withUserId(BOB_ID).withActivityId(normalTask4.getId()).build());\n    }\n\n    private void buildAndAddTasksWithPendingMappingForActorMappedToUser() {\n        final SActor actorForJohn = repository.add(anActor().build());\n        repository.add(anActorMember().forActor(actorForJohn).withUserId(JOHN_ID).build());\n        final SActor actorForBob = repository.add(anActor().build());\n        repository.add(anActorMember().forActor(actorForBob).withUserId(BOB_ID).build());\n\n        buildAndAddTasksWithPendingMappingForActor(actorForJohn, actorForBob);\n    }\n\n    private void buildAndAddTasksWithPendingMappingForActorMappedToUserMembershipMappedToUser() {\n        final long roleId = 1235L;\n        repository.add(aUserMembership().forUser(JOHN_ID).memberOf(GROUP_FOR_JOHN_ID, roleId).build());\n        repository.add(aUserMembership().forUser(BOB_ID).memberOf(GROUP_FOR_BOB_ID, roleId).build());\n\n        final SActor actorForJohn = repository.add(anActor().build());\n        repository.add(anActorMember().forActor(actorForJohn).withGroupId(GROUP_FOR_JOHN_ID).build());\n\n        final SActor actorForBob = repository.add(anActor().build());\n        repository.add(anActorMember().forActor(actorForBob).withGroupId(GROUP_FOR_BOB_ID).build());\n\n        buildAndAddTasksWithPendingMappingForActor(actorForJohn, actorForBob);\n    }\n\n    private void buildAndAddTasksWithPendingMappingForActor(final SActor actorForJohn, final SActor actorForBob) {\n        // Tasks OK not assigned & pending for John\n        final SFlowNodeInstance normalTask1 = repository\n                .add(aUserTask().withName(\"normalTask1\").withStateExecuting(false).withStable(true).withTerminal(false)\n                        .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID).withId(NORMAL_HUMAN_INSTANCE_ID).build());\n        repository.add(aPendingActivityMapping().withActorId(actorForJohn.getId()).withActivityId(normalTask1.getId())\n                .build());\n\n        // Tasks KO not assigned & pending for john, and OK not assigned & not pending\n        final SFlowNodeInstance executingTask = buildAndAddExecutingTask();\n        repository.add(aPendingActivityMapping().withActorId(actorForJohn.getId()).withActivityId(executingTask.getId())\n                .build());\n        final SFlowNodeInstance notStableTask = buildAndAddNotStableTask();\n        repository.add(aPendingActivityMapping().withActorId(actorForJohn.getId()).withActivityId(notStableTask.getId())\n                .build());\n        final SFlowNodeInstance terminalTask = buildAndAddTerminalTask();\n        repository.add(aPendingActivityMapping().withActorId(actorForJohn.getId()).withActivityId(terminalTask.getId())\n                .build());\n        buildAndAddNormalTask(\"normalTask4\", ROOT_PROCESS_INSTANCE_ID);\n\n        // Tasks OK not assigned & pending for Bob\n        final SFlowNodeInstance normalTask4 = buildAndAddNormalTask(\"normalTask2\", ROOT_PROCESS_INSTANCE_ID);\n        repository.add(\n                aPendingActivityMapping().withActorId(actorForBob.getId()).withActivityId(normalTask4.getId()).build());\n    }\n\n    private SFlowNodeInstance buildAndAddNormalTask(final String taskName, final long rootProcessInstanceId) {\n        return repository\n                .add(aUserTask().withName(taskName).withStateExecuting(false).withStable(true).withTerminal(false)\n                        .withRootProcessInstanceId(rootProcessInstanceId).build());\n    }\n\n    private SFlowNodeInstance buildAndAddExecutingTask() {\n        return repository.add(aUserTask().withName(\"executingTask\").withStateExecuting(true).withStable(true)\n                .withTerminal(false).withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID).build());\n    }\n\n    private SFlowNodeInstance buildAndAddNotStableTask() {\n        return repository.add(aUserTask().withName(\"notStableTask\").withStateExecuting(false).withStable(false)\n                .withTerminal(true).withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID).build());\n    }\n\n    private SFlowNodeInstance buildAndAddTerminalTask() {\n        return repository.add(aUserTask().withName(\"terminalTask\").withStateExecuting(false).withStable(true)\n                .withTerminal(true).withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID).build());\n    }\n\n    @Test\n    public void should_save_and_get_SAutomaticTaskInstance() {\n        SFlowNodeInstance entity = new SAutomaticTaskInstance();\n        SFlowNodeInstance flowNode = repository.add(entity);\n        repository.flush();\n\n        PersistentObject flowNodeFromQuery = repository.selectOne(\"getSFlowNodeInstanceById\",\n                pair(\"id\", flowNode.getId()));\n        Map<String, Object> flowNodeAsMap = jdbcTemplate\n                .queryForMap(\"SELECT * FROM flownode_instance where id = \" + flowNode.getId());\n\n        assertThat(flowNodeFromQuery).isEqualTo(flowNode);\n        assertThat(flowNodeAsMap.get(\"KIND\")).isEqualTo(\"auto\");\n    }\n\n    @Test\n    public void should_save_and_get_SCallActivityInstance() {\n        SFlowNodeInstance entity = new SCallActivityInstance();\n        SFlowNodeInstance flowNode = repository.add(entity);\n        repository.flush();\n\n        PersistentObject flowNodeFromQuery = repository.selectOne(\"getSFlowNodeInstanceById\",\n                pair(\"id\", flowNode.getId()));\n        Map<String, Object> flowNodeAsMap = jdbcTemplate\n                .queryForMap(\"SELECT * FROM flownode_instance where id = \" + flowNode.getId());\n\n        assertThat(flowNodeFromQuery).isEqualTo(flowNode);\n        assertThat(flowNodeAsMap.get(\"KIND\")).isEqualTo(\"call\");\n    }\n\n    @Test\n    public void should_save_and_get_SGatewayInstance() {\n        SFlowNodeInstance entity = new SGatewayInstance();\n        SFlowNodeInstance flowNode = repository.add(entity);\n        repository.flush();\n\n        PersistentObject flowNodeFromQuery = repository.selectOne(\"getSFlowNodeInstanceById\",\n                pair(\"id\", flowNode.getId()));\n        Map<String, Object> flowNodeAsMap = jdbcTemplate\n                .queryForMap(\"SELECT * FROM flownode_instance where id = \" + flowNode.getId());\n\n        assertThat(flowNodeFromQuery).isEqualTo(flowNode);\n        assertThat(flowNodeAsMap.get(\"KIND\")).isEqualTo(\"gate\");\n    }\n\n    @Test\n    public void should_save_and_get_SLoopActivityInstance() {\n        SFlowNodeInstance entity = new SLoopActivityInstance();\n        SFlowNodeInstance flowNode = repository.add(entity);\n        repository.flush();\n\n        PersistentObject flowNodeFromQuery = repository.selectOne(\"getSFlowNodeInstanceById\",\n                pair(\"id\", flowNode.getId()));\n        Map<String, Object> flowNodeAsMap = jdbcTemplate\n                .queryForMap(\"SELECT * FROM flownode_instance where id = \" + flowNode.getId());\n\n        assertThat(flowNodeFromQuery).isEqualTo(flowNode);\n        assertThat(flowNodeAsMap.get(\"KIND\")).isEqualTo(\"loop\");\n    }\n\n    @Test\n    public void should_save_and_get_SManualTaskInstance() {\n        SFlowNodeInstance entity = new SManualTaskInstance();\n        SFlowNodeInstance flowNode = repository.add(entity);\n        repository.flush();\n\n        PersistentObject flowNodeFromQuery = repository.selectOne(\"getSFlowNodeInstanceById\",\n                pair(\"id\", flowNode.getId()));\n        Map<String, Object> flowNodeAsMap = jdbcTemplate\n                .queryForMap(\"SELECT * FROM flownode_instance where id = \" + flowNode.getId());\n\n        assertThat(flowNodeFromQuery).isEqualTo(flowNode);\n        assertThat(flowNodeAsMap.get(\"KIND\")).isEqualTo(\"manual\");\n    }\n\n    @Test\n    public void should_save_and_get_SMultiInstanceActivityInstance() {\n        SFlowNodeInstance entity = new SMultiInstanceActivityInstance();\n        SFlowNodeInstance flowNode = repository.add(entity);\n        repository.flush();\n\n        PersistentObject flowNodeFromQuery = repository.selectOne(\"getSFlowNodeInstanceById\",\n                pair(\"id\", flowNode.getId()));\n        Map<String, Object> flowNodeAsMap = jdbcTemplate\n                .queryForMap(\"SELECT * FROM flownode_instance where id = \" + flowNode.getId());\n\n        assertThat(flowNodeFromQuery).isEqualTo(flowNode);\n        assertThat(flowNodeAsMap.get(\"KIND\")).isEqualTo(\"multi\");\n    }\n\n    @Test\n    public void should_save_and_get_SReceiveTaskInstance() {\n        SFlowNodeInstance entity = new SReceiveTaskInstance();\n        SFlowNodeInstance flowNode = repository.add(entity);\n        repository.flush();\n\n        PersistentObject flowNodeFromQuery = repository.selectOne(\"getSFlowNodeInstanceById\",\n                pair(\"id\", flowNode.getId()));\n        Map<String, Object> flowNodeAsMap = jdbcTemplate\n                .queryForMap(\"SELECT * FROM flownode_instance where id = \" + flowNode.getId());\n\n        assertThat(flowNodeFromQuery).isEqualTo(flowNode);\n        assertThat(flowNodeAsMap.get(\"KIND\")).isEqualTo(\"receive\");\n    }\n\n    @Test\n    public void should_save_and_get_SSendTaskInstance() {\n        SFlowNodeInstance entity = new SSendTaskInstance();\n        SFlowNodeInstance flowNode = repository.add(entity);\n        repository.flush();\n\n        PersistentObject flowNodeFromQuery = repository.selectOne(\"getSFlowNodeInstanceById\",\n                pair(\"id\", flowNode.getId()));\n        Map<String, Object> flowNodeAsMap = jdbcTemplate\n                .queryForMap(\"SELECT * FROM flownode_instance where id = \" + flowNode.getId());\n\n        assertThat(flowNodeFromQuery).isEqualTo(flowNode);\n        assertThat(flowNodeAsMap.get(\"KIND\")).isEqualTo(\"send\");\n    }\n\n    @Test\n    public void should_save_and_get_SSubProcessActivityInstance() {\n        SFlowNodeInstance entity = new SSubProcessActivityInstance();\n        SFlowNodeInstance flowNode = repository.add(entity);\n        repository.flush();\n\n        PersistentObject flowNodeFromQuery = repository.selectOne(\"getSFlowNodeInstanceById\",\n                pair(\"id\", flowNode.getId()));\n        Map<String, Object> flowNodeAsMap = jdbcTemplate\n                .queryForMap(\"SELECT * FROM flownode_instance where id = \" + flowNode.getId());\n\n        assertThat(flowNodeFromQuery).isEqualTo(flowNode);\n        assertThat(flowNodeAsMap.get(\"KIND\")).isEqualTo(\"subProc\");\n    }\n\n    @Test\n    public void should_save_and_get_SUserTaskInstance() {\n        SFlowNodeInstance entity = new SUserTaskInstance();\n        SFlowNodeInstance flowNode = repository.add(entity);\n        repository.flush();\n\n        PersistentObject flowNodeFromQuery = repository.selectOne(\"getSFlowNodeInstanceById\",\n                pair(\"id\", flowNode.getId()));\n        Map<String, Object> flowNodeAsMap = jdbcTemplate\n                .queryForMap(\"SELECT * FROM flownode_instance where id = \" + flowNode.getId());\n\n        assertThat(flowNodeFromQuery).isEqualTo(flowNode);\n        assertThat(flowNodeAsMap.get(\"KIND\")).isEqualTo(\"user\");\n    }\n\n    @Test\n    public void should_save_and_get_SBoundaryEventInstance() {\n        SFlowNodeInstance entity = new SBoundaryEventInstance();\n        SFlowNodeInstance flowNode = repository.add(entity);\n        repository.flush();\n\n        PersistentObject flowNodeFromQuery = repository.selectOne(\"getSFlowNodeInstanceById\",\n                pair(\"id\", flowNode.getId()));\n        Map<String, Object> flowNodeAsMap = jdbcTemplate\n                .queryForMap(\"SELECT * FROM flownode_instance where id = \" + flowNode.getId());\n\n        assertThat(flowNodeFromQuery).isEqualTo(flowNode);\n        assertThat(flowNodeAsMap.get(\"KIND\")).isEqualTo(\"boundaryEvent\");\n    }\n\n    @Test\n    public void should_save_and_get_SEndEventInstance() {\n        SFlowNodeInstance entity = new SEndEventInstance();\n        SFlowNodeInstance flowNode = repository.add(entity);\n        repository.flush();\n\n        PersistentObject flowNodeFromQuery = repository.selectOne(\"getSFlowNodeInstanceById\",\n                pair(\"id\", flowNode.getId()));\n        Map<String, Object> flowNodeAsMap = jdbcTemplate\n                .queryForMap(\"SELECT * FROM flownode_instance where id = \" + flowNode.getId());\n\n        assertThat(flowNodeFromQuery).isEqualTo(flowNode);\n        assertThat(flowNodeAsMap.get(\"KIND\")).isEqualTo(\"endEvent\");\n    }\n\n    @Test\n    public void should_save_and_get_SIntermediateCatchEventInstance() {\n        SFlowNodeInstance entity = new SIntermediateCatchEventInstance();\n        SFlowNodeInstance flowNode = repository.add(entity);\n        repository.flush();\n\n        PersistentObject flowNodeFromQuery = repository.selectOne(\"getSFlowNodeInstanceById\",\n                pair(\"id\", flowNode.getId()));\n        Map<String, Object> flowNodeAsMap = jdbcTemplate\n                .queryForMap(\"SELECT * FROM flownode_instance where id = \" + flowNode.getId());\n\n        assertThat(flowNodeFromQuery).isEqualTo(flowNode);\n        assertThat(flowNodeAsMap.get(\"KIND\")).isEqualTo(\"intermediateCatchEvent\");\n    }\n\n    @Test\n    public void should_save_and_get_SIntermediateThrowEventInstance() {\n        SFlowNodeInstance entity = new SIntermediateThrowEventInstance();\n        SFlowNodeInstance flowNode = repository.add(entity);\n        repository.flush();\n\n        PersistentObject flowNodeFromQuery = repository.selectOne(\"getSFlowNodeInstanceById\",\n                pair(\"id\", flowNode.getId()));\n        Map<String, Object> flowNodeAsMap = jdbcTemplate\n                .queryForMap(\"SELECT * FROM flownode_instance where id = \" + flowNode.getId());\n\n        assertThat(flowNodeFromQuery).isEqualTo(flowNode);\n        assertThat(flowNodeAsMap.get(\"KIND\")).isEqualTo(\"intermediateThrowEvent\");\n    }\n\n    @Test\n    public void should_save_and_get_SStartEventInstance() {\n        SFlowNodeInstance entity = new SStartEventInstance();\n        SFlowNodeInstance flowNode = repository.add(entity);\n        repository.flush();\n\n        PersistentObject flowNodeFromQuery = repository.selectOne(\"getSFlowNodeInstanceById\",\n                pair(\"id\", flowNode.getId()));\n        Map<String, Object> flowNodeAsMap = jdbcTemplate\n                .queryForMap(\"SELECT * FROM flownode_instance where id = \" + flowNode.getId());\n\n        assertThat(flowNodeFromQuery).isEqualTo(flowNode);\n        assertThat(flowNodeAsMap.get(\"KIND\")).isEqualTo(\"startEvent\");\n    }\n\n    @Test\n    public void should_save_and_get_STimerEventTriggerInstance() {\n        STimerEventTriggerInstance entity = new STimerEventTriggerInstance();\n        entity.setEventInstanceName(\"eventInstanceName\");\n        STimerEventTriggerInstance trigger = repository.add(entity);\n        repository.flush();\n\n        PersistentObject triggerFromQuery = repository.selectOne(\"getEventTriggerInstanceById\",\n                pair(\"id\", trigger.getId()));\n        Map<String, Object> triggerAsMap = jdbcTemplate\n                .queryForMap(\"SELECT * FROM event_trigger_instance where id = \" + trigger.getId());\n\n        assertThat(triggerFromQuery).isEqualTo(trigger);\n        assertThat(triggerAsMap.get(\"EVENTINSTANCENAME\")).isEqualTo(\"eventInstanceName\");\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/process/instance/model/ProcessDefinitionQueriesTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.test.persistence.builder.ActorBuilder.anActor;\nimport static org.bonitasoft.engine.test.persistence.builder.ActorMemberBuilder.anActorMember;\nimport static org.bonitasoft.engine.test.persistence.builder.UserBuilder.aUser;\nimport static org.bonitasoft.engine.test.persistence.builder.UserMembershipBuilder.aUserMembership;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.actor.mapping.model.SActor;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.test.persistence.repository.ProcessInstanceRepository;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.transaction.annotation.Transactional;\n\n@RunWith(SpringRunner.class)\n@ContextConfiguration(locations = { \"/testContext.xml\" })\n@Transactional\npublic class ProcessDefinitionQueriesTest {\n\n    private static final long PROCESS_ID = 45354312L;\n    private static final long ROLE_ID = 222222L;\n    @Autowired\n    private ProcessInstanceRepository repository;\n    /*\n     * Tests for queries:\n     * getNumberOfSUserWhoCanStartProcess\n     * searchSUserWhoCanStartProcess\n     */\n\n    @Test\n    public void searchSUserWhoCanStartProcess_should_return_users_having_the_right_user_membership() {\n        long G1 = 333331L;\n        long G2 = 333332L;\n        long G3 = 333333L;\n        long G4 = 333334L;\n\n        long R2 = 222225L;\n        long R3 = 222226L;\n        long R4 = 222227L;\n\n        final SUser john = repository.add(aUser().withUserName(\"john\").withId(1L).build());\n        final SUser paul = repository.add(aUser().withUserName(\"paul\").withId(2L).build());\n        final SUser walter = repository.add(aUser().withUserName(\"walter\").withId(3L).build());\n        final SUser marie = repository.add(aUser().withUserName(\"marie\").withId(4L).build());\n        final SUser helen = repository.add(aUser().withUserName(\"helen\").withId(5L).build());\n        final SUser jobs = repository.add(aUser().withUserName(\"jobs\").withId(6L).build());\n\n        final SActor actor = repository.add(anActor().withScopeId(PROCESS_ID).whoIsInitiator().build());\n\n        repository.add(anActorMember().forActor(actor).withUserId(helen.getId()).build());\n        repository.add(anActorMember().forActor(actor).withGroupId(G1).withRoleId(ROLE_ID).build());\n        repository.add(anActorMember().forActor(actor).withRoleId(R3).build());\n        repository.add(anActorMember().forActor(actor).withGroupId(G3).build());\n\n        repository.add(aUserMembership().forUser(john).memberOf(G1, ROLE_ID).build());\n        repository.add(aUserMembership().forUser(paul).memberOf(G1, ROLE_ID).build());\n        repository.add(aUserMembership().forUser(walter).memberOf(G2, R3).build());\n        repository.add(aUserMembership().forUser(marie).memberOf(G3, R2).build());\n        repository.add(aUserMembership().forUser(jobs).memberOf(G4, R4).build());\n\n        // John is mapped through membership, but also through direct mapping.\n        // He should only be returned once:\n        repository.add(anActorMember().forActor(actor).withUserId(john.getId()).build());\n\n        final List<SUser> users = repository.searchSUserWhoCanStartProcess(PROCESS_ID);\n\n        assertThat(users).hasSize(5).containsOnly(john, paul, walter, marie, helen);\n\n        long numberOfSUserWhoCanStartProcess = repository.getNumberOfSUserWhoCanStartProcess(PROCESS_ID);\n\n        assertThat(numberOfSUserWhoCanStartProcess).isEqualTo(5);\n    }\n\n    @Test\n    public void searchSUserWhoCanStartProcess_should_return_users_mapped_on_initiator_actor_of_the_process() {\n        SUser john = repository.add(aUser().withUserName(\"john\").withId(1L).build());\n        repository.add(aUser().withUserName(\"paul\").withId(2L).build());\n        SActor actor = repository.add(anActor().withScopeId(PROCESS_ID).whoIsInitiator().build());\n        repository.add(anActorMember().forActor(actor).withUserId(john.getId()).build());\n\n        List<SUser> users = repository.searchSUserWhoCanStartProcess(PROCESS_ID);\n\n        assertThat(users).hasSize(1).contains(john);\n\n        long numberOfSUserWhoCanStartProcess = repository.getNumberOfSUserWhoCanStartProcess(PROCESS_ID);\n\n        assertThat(numberOfSUserWhoCanStartProcess).isEqualTo(1);\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/process/instance/model/ProcessDeploymentInfoQueriesTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.commons.Pair.pair;\nimport static org.bonitasoft.engine.test.persistence.builder.ActorBuilder.anActor;\nimport static org.bonitasoft.engine.test.persistence.builder.ActorMemberBuilder.anActorMember;\nimport static org.bonitasoft.engine.test.persistence.builder.PendingActivityMappingBuilder.aPendingActivityMapping;\nimport static org.bonitasoft.engine.test.persistence.builder.ProcessInstanceBuilder.aProcessInstance;\nimport static org.bonitasoft.engine.test.persistence.builder.UserBuilder.aUser;\nimport static org.bonitasoft.engine.test.persistence.builder.UserMembershipBuilder.aUserMembership;\nimport static org.bonitasoft.engine.test.persistence.builder.UserTaskInstanceBuilder.aUserTask;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.actor.mapping.model.SActor;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDesignContent;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor;\nimport org.bonitasoft.engine.test.persistence.jdbc.JdbcRowMapper;\nimport org.bonitasoft.engine.test.persistence.repository.ProcessDeploymentInfoRepository;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.transaction.annotation.Transactional;\n\n@RunWith(SpringRunner.class)\n@ContextConfiguration(locations = { \"/testContext.xml\" })\n@Transactional\npublic class ProcessDeploymentInfoQueriesTest {\n\n    private static final long GROUP_FOR_BOB_ID = 74L;\n\n    private static final long GROUP_FOR_JOHN_ID = 9875L;\n\n    private static final long GROUP_FOR_SUPERVISOR_FOR_BOB_ID = 369L;\n\n    private static final long GROUP_FOR_SUPERVISOR_FOR_JOHN_ID = 7453L;\n\n    private static final long JOHN_ID = 654L;\n\n    private static final long BOB_ID = 697L;\n\n    private static final long PAUL_ID = 94L;\n\n    private static final long JACK_ID = 63L;\n\n    private static final long PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN = 111L;\n\n    private static final long PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS = 112L;\n\n    private static final long PROCESS_DEFINITION_ID_SUPERVISED_BY_BOB = 113L;\n\n    private static final long PROCESS_DEFINITION_ID_NOT_SUPERVISED = 114L;\n\n    private static final long ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN = 963L;\n\n    private static final long ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS = 1234L;\n\n    private static final long ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_BOB = 1258L;\n\n    private static final long ROOT_PROCESS_INSTANCE_ID_NOT_SUPERVISED = 1269L;\n\n    @Autowired\n    private ProcessDeploymentInfoRepository repository;\n    @Autowired\n    private JdbcTemplate jdbcTemplate;\n\n    @Before\n    public void before() {\n        repository.add(aUser().withId(JOHN_ID).build());\n        repository.add(aUser().withId(BOB_ID).build());\n        repository.add(aUser().withId(PAUL_ID).build());\n        repository.add(aUser().withId(JACK_ID).build());\n\n        // Create process supervised by john\n        final String processNameSupervisedByJohn = \"Process supervised by John\";\n        buildAndCreateProcessDefinition(6L, PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN, processNameSupervisedByJohn);\n        repository.add(aProcessInstance().withProcessDefinitionId(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN)\n                .withName(processNameSupervisedByJohn)\n                .withId(ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN).build());\n\n        // Create process supervised by john with only KO taks\n        final String processNameSupervisedByJohnWithOnlyKOTasks = \"Process supervised by John with only KO taks\";\n        buildAndCreateProcessDefinition(8L, PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS,\n                processNameSupervisedByJohnWithOnlyKOTasks);\n        repository.add(\n                aProcessInstance().withProcessDefinitionId(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS)\n                        .withName(processNameSupervisedByJohnWithOnlyKOTasks)\n                        .withId(ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS).build());\n\n        // Create process supervised by bob\n        final String processNameSupervisedByBob = \"Process supervised by Bob\";\n        buildAndCreateProcessDefinition(10L, PROCESS_DEFINITION_ID_SUPERVISED_BY_BOB, processNameSupervisedByBob);\n        repository.add(aProcessInstance().withProcessDefinitionId(PROCESS_DEFINITION_ID_SUPERVISED_BY_BOB)\n                .withName(processNameSupervisedByBob)\n                .withId(ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_BOB).build());\n\n        // Create not supervised process\n        final String processNameNotSupervised = \"Process not supervised\";\n        buildAndCreateProcessDefinition(12L, PROCESS_DEFINITION_ID_NOT_SUPERVISED, processNameNotSupervised);\n        repository.add(aProcessInstance().withProcessDefinitionId(PROCESS_DEFINITION_ID_NOT_SUPERVISED)\n                .withName(processNameNotSupervised)\n                .withId(ROOT_PROCESS_INSTANCE_ID_NOT_SUPERVISED).build());\n    }\n\n    protected void buildAndCreateProcessDefinition(final long id, long processDefinitionId, final String processName) {\n        final SProcessDefinitionDeployInfo sProcessDefinitionDeployInfo = new SProcessDefinitionDeployInfo();\n        sProcessDefinitionDeployInfo.setId(id);\n        sProcessDefinitionDeployInfo.setName(processName);\n        sProcessDefinitionDeployInfo.setVersion(\"version\");\n        sProcessDefinitionDeployInfo.setProcessId(processDefinitionId);\n        repository.add(sProcessDefinitionDeployInfo);\n    }\n\n    // For\n    @Test\n    public void getNumberOfProcessDefinitionDeployInfoWithAssignedOrPendingHumanTasksFor_should_return_number_of_process_definition_if_one_instance_has_assigned_tasks_to_the_user() {\n        // Given\n        buildAndAddAssignedTasks();\n\n        // When\n        final long numberOfProcessDefinitionDeployInfos = repository\n                .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(JACK_ID);\n\n        // Then\n        assertThat(numberOfProcessDefinitionDeployInfos).isEqualTo(1);\n    }\n\n    @Test\n    public void getNumberOfProcessDefinitionDeployInfoWithAssignedOrPendingHumanTasksFor_should_return_number_of_process_definition_if_one_instance_has_pending_tasks_for_the_user() {\n        // Given\n        buildAndAddTasksWithPendingMappingForUser();\n\n        // When\n        long numberOfProcessDefinitionDeployInfos = repository\n                .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(JACK_ID);\n\n        // Then\n        assertThat(numberOfProcessDefinitionDeployInfos).isEqualTo(1);\n    }\n\n    @Test\n    public void getNumberOfProcessDefinitionDeployInfoWithAssignedOrPendingHumanTasksFor_should_return_number_of_process_definition_if_one_instance_has_pending_tasks_for_a_actor_with_the_user_as_member() {\n        // Given\n        buildAndAddTasksWithPendingMappingForActorMappedToUser();\n\n        // When\n        long numberOfProcessDefinitionDeployInfos = repository\n                .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(JACK_ID);\n\n        // Then\n        assertThat(numberOfProcessDefinitionDeployInfos).isEqualTo(1);\n    }\n\n    @Test\n    public void getNumberOfProcessDefinitionDeployInfoWithAssignedOrPendingHumanTasksFor_should_return_number_of_process_definition_if_one_instance_has_pending_tasks_for_a_actor_with_a_membership_mapped_to_the_user_as_member() {\n        // Given\n        buildAndAddTasksWithPendingMappingForActorMappedToUserMembershipMappedToUser();\n\n        // When\n        long numberOfProcessDefinitionDeployInfos = repository\n                .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(JACK_ID);\n\n        // Then\n        assertThat(numberOfProcessDefinitionDeployInfos).isEqualTo(1);\n    }\n\n    @Test\n    public void searchProcessDefinitionDeployInfoWithAssignedOrPendingHumanTasksFor_should_return_number_of_process_definition_if_one_instance_has_assigned_tasks_to_the_user() {\n        // Given\n        buildAndAddAssignedTasks();\n\n        // When\n        final List<SProcessDefinitionDeployInfo> sProcessDefinitionDeployInfos = repository\n                .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(JACK_ID);\n\n        // Then\n        assertThat(sProcessDefinitionDeployInfos.size()).isEqualTo(1);\n        assertThat(sProcessDefinitionDeployInfos.get(0).getProcessId())\n                .isEqualTo(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN);\n    }\n\n    @Test\n    public void searchProcessDefinitionDeployInfoWithAssignedOrPendingHumanTasksFor_should_return_number_of_process_definition_if_one_instance_has_pending_tasks_for_the_user() {\n        // Given\n        buildAndAddTasksWithPendingMappingForUser();\n\n        // When\n        final List<SProcessDefinitionDeployInfo> sProcessDefinitionDeployInfos = repository\n                .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(JACK_ID);\n\n        // Then\n        assertThat(sProcessDefinitionDeployInfos.size()).isEqualTo(1);\n        assertThat(sProcessDefinitionDeployInfos.get(0).getProcessId())\n                .isEqualTo(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN);\n    }\n\n    @Test\n    public void searchProcessDefinitionDeployInfoWithAssignedOrPendingHumanTasksFor_should_return_number_of_process_definition_if_one_instance_has_pending_tasks_for_a_actor_with_the_user_as_member() {\n        // Given\n        buildAndAddTasksWithPendingMappingForActorMappedToUser();\n\n        // When\n        final List<SProcessDefinitionDeployInfo> sProcessDefinitionDeployInfos = repository\n                .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(JACK_ID);\n\n        // Then\n        assertThat(sProcessDefinitionDeployInfos.size()).isEqualTo(1);\n        assertThat(sProcessDefinitionDeployInfos.get(0).getProcessId())\n                .isEqualTo(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN);\n    }\n\n    @Test\n    public void searchProcessDefinitionDeployInfoWithAssignedOrPendingHumanTasksFor_should_return_number_of_process_definition_if_one_instance_has_pending_tasks_for_a_actor_with_a_membership_mapped_to_the_user_as_member() {\n        // Given\n        buildAndAddTasksWithPendingMappingForActorMappedToUserMembershipMappedToUser();\n\n        // When\n        final List<SProcessDefinitionDeployInfo> sProcessDefinitionDeployInfos = repository\n                .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(JACK_ID);\n\n        // Then\n        assertThat(sProcessDefinitionDeployInfos.size()).isEqualTo(1);\n        assertThat(sProcessDefinitionDeployInfos.get(0).getProcessId())\n                .isEqualTo(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN);\n    }\n\n    // Supervised By\n    @Test\n    public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy_should_return_number_of_process_definition_supervised_by_user_if_one_instance_has_assigned_tasks() {\n        // Given\n        buildAndAddAssignedTasks();\n        buildAndAddSupervisorMappedToUser();\n\n        // When\n        final long numberOfProcessDefinitionDeployInfos = repository\n                .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(JOHN_ID);\n\n        // Then\n        assertThat(numberOfProcessDefinitionDeployInfos).isEqualTo(1);\n    }\n\n    @Test\n    public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy_should_return_number_of_process_definition_supervised_by_userMembership_if_one_instance_has_assigned_tasks() {\n        // Given\n        buildAndAddAssignedTasks();\n        buildAndAddSupervisorMappedToUserMembershipMappedToUser();\n\n        // When\n        final long numberOfProcessDefinitionDeployInfos = repository\n                .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(JOHN_ID);\n\n        // Then\n        assertThat(numberOfProcessDefinitionDeployInfos).isEqualTo(1);\n    }\n\n    @Test\n    public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy_should_return_number_of_process_definition_supervised_by_user_if_one_instance_has_pending_tasks_for_a_user() {\n        // Given\n        buildAndAddTasksWithPendingMappingForUser();\n        buildAndAddSupervisorMappedToUser();\n\n        // When\n        long numberOfProcessDefinitionDeployInfos = repository\n                .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(JOHN_ID);\n\n        // Then\n        assertThat(numberOfProcessDefinitionDeployInfos).isEqualTo(1);\n    }\n\n    @Test\n    public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy_should_return_number_of_process_definition_supervised_by_userMembership_if_one_instance_has_pending_tasks_for_a_user() {\n        // Given\n        buildAndAddTasksWithPendingMappingForUser();\n        buildAndAddSupervisorMappedToUserMembershipMappedToUser();\n\n        // When\n        long numberOfProcessDefinitionDeployInfos = repository\n                .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(JOHN_ID);\n\n        // Then\n        assertThat(numberOfProcessDefinitionDeployInfos).isEqualTo(1);\n    }\n\n    @Test\n    public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy_should_return_number_of_process_definition_supervised_by_user_if_one_instance_has_pending_tasks_for_a_actor_with_a_user_as_member() {\n        // Given\n        buildAndAddTasksWithPendingMappingForActorMappedToUser();\n        buildAndAddSupervisorMappedToUser();\n\n        // When\n        long numberOfProcessDefinitionDeployInfos = repository\n                .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(JOHN_ID);\n\n        // Then\n        assertThat(numberOfProcessDefinitionDeployInfos).isEqualTo(1);\n    }\n\n    @Test\n    public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy_should_return_number_of_process_definition_supervised_by_userMembership_if_one_instance_has_pending_tasks_for_a_actor_with_a_user_as_member() {\n        // Given\n        buildAndAddTasksWithPendingMappingForActorMappedToUser();\n        buildAndAddSupervisorMappedToUserMembershipMappedToUser();\n\n        // When\n        long numberOfProcessDefinitionDeployInfos = repository\n                .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(JOHN_ID);\n\n        // Then\n        assertThat(numberOfProcessDefinitionDeployInfos).isEqualTo(1);\n    }\n\n    @Test\n    public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy_should_return_number_of_process_definition_supervised_by_user_if_one_instance_has_pending_tasks_for_a_actor_with_a_membership_mapped_to_a_user_as_member() {\n        // Given\n        buildAndAddTasksWithPendingMappingForActorMappedToUserMembershipMappedToUser();\n        buildAndAddSupervisorMappedToUser();\n\n        // When\n        long numberOfProcessDefinitionDeployInfos = repository\n                .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(JOHN_ID);\n\n        // Then\n        assertThat(numberOfProcessDefinitionDeployInfos).isEqualTo(1);\n    }\n\n    @Test\n    public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy_should_return_number_of_process_definition_supervised_by_userMembership_if_one_instance_has_pending_tasks_for_a_actor_with_a_membership_mapped_to_a_user_as_member() {\n        // Given\n        buildAndAddTasksWithPendingMappingForActorMappedToUserMembershipMappedToUser();\n        buildAndAddSupervisorMappedToUserMembershipMappedToUser();\n\n        // When\n        long numberOfProcessDefinitionDeployInfos = repository\n                .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(JOHN_ID);\n\n        // Then\n        assertThat(numberOfProcessDefinitionDeployInfos).isEqualTo(1);\n    }\n\n    @Test\n    public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy_should_return_number_of_process_definition_supervised_by_user_if_one_instance_has_assigned_tasks() {\n        // Given\n        buildAndAddAssignedTasks();\n        buildAndAddSupervisorMappedToUser();\n\n        // When\n        final List<SProcessDefinitionDeployInfo> sProcessDefinitionDeployInfos = repository\n                .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(JOHN_ID);\n\n        // Then\n        assertThat(sProcessDefinitionDeployInfos.size()).isEqualTo(1);\n        assertThat(sProcessDefinitionDeployInfos.get(0).getProcessId())\n                .isEqualTo(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN);\n    }\n\n    @Test\n    public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy_should_return_number_of_process_definition_supervised_by_userMembership_if_one_instance_has_assigned_tasks() {\n        // Given\n        buildAndAddAssignedTasks();\n        buildAndAddSupervisorMappedToUserMembershipMappedToUser();\n\n        // When\n        final List<SProcessDefinitionDeployInfo> sProcessDefinitionDeployInfos = repository\n                .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(JOHN_ID);\n\n        // Then\n        assertThat(sProcessDefinitionDeployInfos.size()).isEqualTo(1);\n        assertThat(sProcessDefinitionDeployInfos.get(0).getProcessId())\n                .isEqualTo(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN);\n    }\n\n    @Test\n    public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy_should_return_number_of_process_definition_supervised_by_user_if_one_instance_has_pending_tasks_for_a_user() {\n        // Given\n        buildAndAddTasksWithPendingMappingForUser();\n        buildAndAddSupervisorMappedToUser();\n\n        // When\n        final List<SProcessDefinitionDeployInfo> sProcessDefinitionDeployInfos = repository\n                .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(JOHN_ID);\n\n        // Then\n        assertThat(sProcessDefinitionDeployInfos.size()).isEqualTo(1);\n        assertThat(sProcessDefinitionDeployInfos.get(0).getProcessId())\n                .isEqualTo(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN);\n    }\n\n    @Test\n    public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy_should_return_number_of_process_definition_supervised_by_userMembership_if_one_instance_has_pending_tasks_for_a_user() {\n        // Given\n        buildAndAddTasksWithPendingMappingForUser();\n        buildAndAddSupervisorMappedToUserMembershipMappedToUser();\n\n        // When\n        final List<SProcessDefinitionDeployInfo> sProcessDefinitionDeployInfos = repository\n                .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(JOHN_ID);\n\n        // Then\n        assertThat(sProcessDefinitionDeployInfos.size()).isEqualTo(1);\n        assertThat(sProcessDefinitionDeployInfos.get(0).getProcessId())\n                .isEqualTo(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN);\n    }\n\n    @Test\n    public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy_should_return_number_of_process_definition_supervised_by_user_if_one_instance_has_pending_tasks_for_a_actor_with_a_user_as_member() {\n        // Given\n        buildAndAddTasksWithPendingMappingForActorMappedToUser();\n        buildAndAddSupervisorMappedToUser();\n\n        // When\n        final List<SProcessDefinitionDeployInfo> sProcessDefinitionDeployInfos = repository\n                .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(JOHN_ID);\n\n        // Then\n        assertThat(sProcessDefinitionDeployInfos.size()).isEqualTo(1);\n        assertThat(sProcessDefinitionDeployInfos.get(0).getProcessId())\n                .isEqualTo(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN);\n    }\n\n    @Test\n    public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy_should_return_number_of_process_definition_supervised_by_userMembership_if_one_instance_has_pending_tasks_for_a_actor_with_a_user_as_member() {\n        // Given\n        buildAndAddTasksWithPendingMappingForActorMappedToUser();\n        buildAndAddSupervisorMappedToUserMembershipMappedToUser();\n\n        // When\n        final List<SProcessDefinitionDeployInfo> sProcessDefinitionDeployInfos = repository\n                .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(JOHN_ID);\n\n        // Then\n        assertThat(sProcessDefinitionDeployInfos.size()).isEqualTo(1);\n        assertThat(sProcessDefinitionDeployInfos.get(0).getProcessId())\n                .isEqualTo(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN);\n    }\n\n    @Test\n    public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy_should_return_number_of_process_definition_supervised_by_user_if_one_instance_has_pending_tasks_for_a_actor_with_a_membership_mapped_to_a_user_as_member() {\n        // Given\n        buildAndAddTasksWithPendingMappingForActorMappedToUserMembershipMappedToUser();\n        buildAndAddSupervisorMappedToUser();\n\n        // When\n        final List<SProcessDefinitionDeployInfo> sProcessDefinitionDeployInfos = repository\n                .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(JOHN_ID);\n\n        // Then\n        assertThat(sProcessDefinitionDeployInfos.size()).isEqualTo(1);\n        assertThat(sProcessDefinitionDeployInfos.get(0).getProcessId())\n                .isEqualTo(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN);\n    }\n\n    @Test\n    public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy_should_return_number_of_process_definition_supervised_by_userMembership_if_one_instance_has_pending_tasks_for_a_actor_with_a_membership_mapped_to_a_user_as_member() {\n        // Given\n        buildAndAddTasksWithPendingMappingForActorMappedToUserMembershipMappedToUser();\n        buildAndAddSupervisorMappedToUserMembershipMappedToUser();\n\n        // When\n        final List<SProcessDefinitionDeployInfo> sProcessDefinitionDeployInfos = repository\n                .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(JOHN_ID);\n\n        // Then\n        assertThat(sProcessDefinitionDeployInfos.size()).isEqualTo(1);\n        assertThat(sProcessDefinitionDeployInfos.get(0).getProcessId())\n                .isEqualTo(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN);\n    }\n\n    // All (for admin)\n    @Test\n    public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasks_should_return_number_of_process_definition_supervised_if_one_instance_has_assigned_tasks() {\n        // Given\n        buildAndAddAssignedTasks();\n        buildAndAddSupervisorMappedToUser();\n\n        // When\n        final long numberOfProcessDefinitionDeployInfos = repository\n                .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasks();\n\n        // Then\n        assertThat(numberOfProcessDefinitionDeployInfos).isEqualTo(4);\n    }\n\n    @Test\n    public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasks_should_return_number_of_process_definition_supervised_if_one_instance_has_pending_tasks_for_a_user() {\n        // Given\n        buildAndAddTasksWithPendingMappingForUser();\n        buildAndAddSupervisorMappedToUser();\n\n        // When\n        long numberOfProcessDefinitionDeployInfos = repository\n                .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasks();\n\n        // Then\n        assertThat(numberOfProcessDefinitionDeployInfos).isEqualTo(4);\n    }\n\n    @Test\n    public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasks_should_return_number_of_process_definition_supervised_if_one_instance_has_pending_tasks_for_a_actor_with_a_user_as_member() {\n        // Given\n        buildAndAddTasksWithPendingMappingForActorMappedToUser();\n        buildAndAddSupervisorMappedToUser();\n\n        // When\n        long numberOfProcessDefinitionDeployInfos = repository\n                .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasks();\n\n        // Then\n        assertThat(numberOfProcessDefinitionDeployInfos).isEqualTo(1);\n    }\n\n    @Test\n    public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasks_should_return_number_of_process_definition_supervised_if_one_instance_has_pending_tasks_for_a_actor_with_a_membership_mapped_to_a_user_as_member() {\n        // Given\n        buildAndAddTasksWithPendingMappingForActorMappedToUserMembershipMappedToUser();\n        buildAndAddSupervisorMappedToUser();\n\n        // When\n        long numberOfProcessDefinitionDeployInfos = repository\n                .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasks();\n\n        // Then\n        assertThat(numberOfProcessDefinitionDeployInfos).isEqualTo(1);\n    }\n\n    @Test\n    public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks_should_return_number_of_process_definition_supervised_if_one_instance_has_assigned_tasks() {\n        // Given\n        buildAndAddAssignedTasks();\n        buildAndAddSupervisorMappedToUser();\n\n        // When\n        final List<SProcessDefinitionDeployInfo> sProcessDefinitionDeployInfos = repository\n                .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks();\n\n        // Then\n        assertThat(sProcessDefinitionDeployInfos.size()).isEqualTo(4);\n        assertThat(sProcessDefinitionDeployInfos.get(0).getProcessId())\n                .isEqualTo(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN);\n    }\n\n    @Test\n    public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks_should_return_number_of_process_definition_supervised_if_one_instance_has_pending_tasks_for_a_user() {\n        // Given\n        buildAndAddTasksWithPendingMappingForUser();\n        buildAndAddSupervisorMappedToUser();\n\n        // When\n        final List<SProcessDefinitionDeployInfo> sProcessDefinitionDeployInfos = repository\n                .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks();\n\n        // Then\n        assertThat(sProcessDefinitionDeployInfos.size()).isEqualTo(4);\n        assertThat(sProcessDefinitionDeployInfos.get(0).getProcessId())\n                .isEqualTo(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN);\n    }\n\n    @Test\n    public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks_should_return_number_of_process_definition_supervised_if_one_instance_has_pending_tasks_for_a_actor_with_a_user_as_member() {\n        // Given\n        buildAndAddTasksWithPendingMappingForActorMappedToUser();\n        buildAndAddSupervisorMappedToUser();\n\n        // When\n        final List<SProcessDefinitionDeployInfo> sProcessDefinitionDeployInfos = repository\n                .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks();\n\n        // Then\n        assertThat(sProcessDefinitionDeployInfos.size()).isEqualTo(1);\n        assertThat(sProcessDefinitionDeployInfos.get(0).getProcessId())\n                .isEqualTo(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN);\n    }\n\n    @Test\n    public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks_should_return_number_of_process_definition_supervised_if_one_instance_has_pending_tasks_for_a_actor_with_a_membership_mapped_to_a_user_as_member() {\n        // Given\n        buildAndAddTasksWithPendingMappingForActorMappedToUserMembershipMappedToUser();\n        buildAndAddSupervisorMappedToUser();\n\n        // When\n        final List<SProcessDefinitionDeployInfo> sProcessDefinitionDeployInfos = repository\n                .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks();\n\n        // Then\n        assertThat(sProcessDefinitionDeployInfos.size()).isEqualTo(1);\n        assertThat(sProcessDefinitionDeployInfos.get(0).getProcessId())\n                .isEqualTo(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN);\n    }\n\n    private void buildAndAddSupervisorMappedToUser() {\n        repository.add(new SProcessSupervisor(3, PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN, JOHN_ID, -1, -1));\n        repository.add(new SProcessSupervisor(4, PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS,\n                JOHN_ID, -1, -1));\n        repository.add(new SProcessSupervisor(5, PROCESS_DEFINITION_ID_SUPERVISED_BY_BOB, BOB_ID, -1, -1));\n    }\n\n    private void buildAndAddSupervisorMappedToUserMembershipMappedToUser() {\n        final long roleId = 1295L;\n        repository.add(aUserMembership().forUser(JOHN_ID).memberOf(GROUP_FOR_SUPERVISOR_FOR_JOHN_ID, roleId).build());\n        repository.add(aUserMembership().forUser(BOB_ID).memberOf(GROUP_FOR_SUPERVISOR_FOR_BOB_ID, roleId).build());\n\n        repository.add(new SProcessSupervisor(3, PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN, -1,\n                GROUP_FOR_SUPERVISOR_FOR_JOHN_ID, -1));\n        repository.add(new SProcessSupervisor(4, PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS, -1,\n                GROUP_FOR_SUPERVISOR_FOR_JOHN_ID, -1));\n        repository.add(new SProcessSupervisor(5, PROCESS_DEFINITION_ID_SUPERVISED_BY_BOB, -1,\n                GROUP_FOR_SUPERVISOR_FOR_BOB_ID, -1));\n    }\n\n    private void buildAndAddAssignedTasks() {\n        // Tasks OK assigned to John\n        repository\n                .add(aUserTask().withName(\"normalTask1\").withStateExecuting(false).withStable(true).withTerminal(false)\n                        .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN).withAssigneeId(JACK_ID)\n                        .withStateId(4).withProcessDefinition(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN).build());\n        repository\n                .add(aUserTask().withName(\"normalTask2\").withStateExecuting(false).withStable(true).withTerminal(false)\n                        .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN).withAssigneeId(JACK_ID)\n                        .withStateId(4).withProcessDefinition(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN).build());\n        repository\n                .add(aUserTask().withName(\"normalTask3\").withStateExecuting(false).withStable(true).withTerminal(false)\n                        .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN).withAssigneeId(JACK_ID)\n                        .withStateId(4).withProcessDefinition(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN).build());\n\n        // Tasks KO assigned to john & OK not assigned\n        repository\n                .add(aUserTask().withName(\"executingTask\").withStateExecuting(true).withStable(true).withTerminal(false)\n                        .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS)\n                        .withAssigneeId(JACK_ID)\n                        .withProcessDefinition(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS).build());\n        repository.add(\n                aUserTask().withName(\"notStableTask\").withStateExecuting(false).withStable(false).withTerminal(true)\n                        .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS)\n                        .withAssigneeId(JACK_ID)\n                        .withProcessDefinition(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS).build());\n        repository.add(\n                aUserTask().withName(\"notStableTask\").withStateExecuting(false).withStable(false).withTerminal(true)\n                        .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS)\n                        .withAssigneeId(JACK_ID).withStateId(4)\n                        .withProcessDefinition(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS).build());\n        repository\n                .add(aUserTask().withName(\"terminalTask\").withStateExecuting(false).withStable(true).withTerminal(true)\n                        .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS)\n                        .withAssigneeId(JACK_ID).build());\n        buildAndAddNormalTask(\"normalTask1\", ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS,\n                PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS);\n\n        // Tasks OK assigned to Bob\n        repository.add(aUserTask().withName(\"normalTask1\").withStateExecuting(false).withStable(true)\n                .withTerminal(false)\n                .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_BOB).withStateId(4)\n                .withProcessDefinition(PROCESS_DEFINITION_ID_SUPERVISED_BY_BOB).withAssigneeId(PAUL_ID).build());\n\n        // Tasks OK assigned to Bob, process not supervised\n        repository\n                .add(aUserTask().withName(\"normalTask1\").withStateExecuting(false).withStable(true).withTerminal(false)\n                        .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID_NOT_SUPERVISED).withStateId(4)\n                        .withProcessDefinition(PROCESS_DEFINITION_ID_NOT_SUPERVISED).withAssigneeId(PAUL_ID).build());\n    }\n\n    private void buildAndAddTasksWithPendingMappingForUser() {\n        // Tasks OK not assigned & pending for John\n        final SFlowNodeInstance normalTask1 = buildAndAddNormalTask(\"normalTask1\",\n                ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN, PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN);\n        repository.add(aPendingActivityMapping().withUserId(JACK_ID).withActivityId(normalTask1.getId()).build());\n        final SFlowNodeInstance normalTask2 = buildAndAddNormalTask(\"normalTask2\",\n                ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN, PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN);\n        repository.add(aPendingActivityMapping().withUserId(JACK_ID).withActivityId(normalTask2.getId()).build());\n        final SFlowNodeInstance normalTask3 = buildAndAddNormalTask(\"normalTask3\",\n                ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN, PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN);\n        repository.add(aPendingActivityMapping().withUserId(JACK_ID).withActivityId(normalTask3.getId()).build());\n\n        // Tasks KO not assigned & pending for john, and OK not assigned & not pending\n        final SFlowNodeInstance executingTask = buildAndAddExecutingTask();\n        repository.add(aPendingActivityMapping().withUserId(JACK_ID).withActivityId(executingTask.getId()).build());\n        final SFlowNodeInstance notStableTask = buildAndAddNotStableTask();\n        repository.add(aPendingActivityMapping().withUserId(JACK_ID).withActivityId(notStableTask.getId()).build());\n        final SFlowNodeInstance terminalTask = buildAndAddTerminalTask();\n        repository.add(aPendingActivityMapping().withUserId(JACK_ID).withActivityId(terminalTask.getId()).build());\n        buildAndAddNormalTask(\"normalTask1\", ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS,\n                PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS);\n\n        // Tasks OK not assigned & pending for Bob\n        final SFlowNodeInstance normalTask4 = buildAndAddNormalTask(\"normalTask1\",\n                ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_BOB, PROCESS_DEFINITION_ID_SUPERVISED_BY_BOB);\n        repository.add(aPendingActivityMapping().withUserId(PAUL_ID).withActivityId(normalTask4.getId()).build());\n\n        // Tasks OK not assigned & pending for Bob, process not supervised\n        final SFlowNodeInstance normalTask5 = buildAndAddNormalTask(\"normalTask1\",\n                ROOT_PROCESS_INSTANCE_ID_NOT_SUPERVISED, PROCESS_DEFINITION_ID_NOT_SUPERVISED);\n        repository.add(aPendingActivityMapping().withUserId(PAUL_ID).withActivityId(normalTask5.getId()).build());\n    }\n\n    private void buildAndAddTasksWithPendingMappingForActorMappedToUser() {\n        final SActor actorForJohn = repository.add(anActor().build());\n        repository.add(anActorMember().forActor(actorForJohn).withUserId(JACK_ID).build());\n        final SActor actorForBob = repository.add(anActor().build());\n        repository.add(anActorMember().forActor(actorForBob).withUserId(PAUL_ID).build());\n\n        buildAndAddTasksWithPendingMappingForActor(actorForJohn, actorForBob);\n    }\n\n    private void buildAndAddTasksWithPendingMappingForActorMappedToUserMembershipMappedToUser() {\n        final long roleId = 1235L;\n        repository.add(aUserMembership().forUser(JACK_ID).memberOf(GROUP_FOR_JOHN_ID, roleId).build());\n        repository.add(aUserMembership().forUser(PAUL_ID).memberOf(GROUP_FOR_BOB_ID, roleId).build());\n\n        final SActor actorForJohn = repository.add(anActor().build());\n        repository.add(anActorMember().forActor(actorForJohn).withGroupId(GROUP_FOR_JOHN_ID).build());\n\n        final SActor actorForBob = repository.add(anActor().build());\n        repository.add(anActorMember().forActor(actorForBob).withGroupId(GROUP_FOR_BOB_ID).build());\n\n        buildAndAddTasksWithPendingMappingForActor(actorForJohn, actorForBob);\n    }\n\n    private void buildAndAddTasksWithPendingMappingForActor(final SActor actorForJohn, final SActor actorForBob) {\n        // Tasks OK not assigned & pending for John\n        final SFlowNodeInstance normalTask1 = buildAndAddNormalTask(\"normalTask1\",\n                ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN, PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN);\n        repository.add(aPendingActivityMapping().withActorId(actorForJohn.getId()).withActivityId(normalTask1.getId())\n                .build());\n        final SFlowNodeInstance normalTask2 = buildAndAddNormalTask(\"normalTask2\",\n                ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN, PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN);\n        repository.add(aPendingActivityMapping().withActorId(actorForJohn.getId()).withActivityId(normalTask2.getId())\n                .build());\n        final SFlowNodeInstance normalTask3 = buildAndAddNormalTask(\"normalTask3\",\n                ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN, PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN);\n        repository.add(aPendingActivityMapping().withActorId(actorForJohn.getId()).withActivityId(normalTask3.getId())\n                .build());\n\n        // Tasks KO not assigned & pending for john, and OK not assigned & not pending\n        final SFlowNodeInstance executingTask = buildAndAddExecutingTask();\n        repository.add(aPendingActivityMapping().withActorId(actorForJohn.getId()).withActivityId(executingTask.getId())\n                .build());\n        final SFlowNodeInstance notStableTask = buildAndAddNotStableTask();\n        repository.add(aPendingActivityMapping().withActorId(actorForJohn.getId()).withActivityId(notStableTask.getId())\n                .build());\n        final SFlowNodeInstance terminalTask = buildAndAddTerminalTask();\n        repository.add(aPendingActivityMapping().withActorId(actorForJohn.getId()).withActivityId(terminalTask.getId())\n                .build());\n        buildAndAddNormalTask(\"normalTask1\", ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS,\n                PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN);\n\n        // Tasks OK not assigned & pending for Bob\n        final SFlowNodeInstance normalTask4 = buildAndAddNormalTask(\"normalTask1\",\n                ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_BOB, PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN);\n        repository.add(\n                aPendingActivityMapping().withActorId(actorForBob.getId()).withActivityId(normalTask4.getId()).build());\n\n        // Tasks OK not assigned & pending for Bob, process not supervised\n        final SFlowNodeInstance normalTask5 = buildAndAddNormalTask(\"normalTask1\",\n                ROOT_PROCESS_INSTANCE_ID_NOT_SUPERVISED, PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN);\n        repository.add(\n                aPendingActivityMapping().withActorId(actorForBob.getId()).withActivityId(normalTask5.getId()).build());\n    }\n\n    private SFlowNodeInstance buildAndAddNormalTask(final String taskName, final long rootProcessInstanceId,\n            long processDefinitionId) {\n        return repository\n                .add(aUserTask().withName(taskName).withStateExecuting(false).withStable(true).withTerminal(false)\n                        .withStateId(4).withRootProcessInstanceId(rootProcessInstanceId)\n                        .withProcessDefinition(processDefinitionId).build());\n    }\n\n    private SFlowNodeInstance buildAndAddExecutingTask() {\n        return repository.add(aUserTask().withName(\"executingTask\").withStateExecuting(true).withStable(true)\n                .withTerminal(false)\n                .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS).build());\n    }\n\n    private SFlowNodeInstance buildAndAddNotStableTask() {\n        return repository.add(aUserTask().withName(\"notStableTask\").withStateExecuting(false).withStable(false)\n                .withTerminal(true)\n                .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS).build());\n    }\n\n    private SFlowNodeInstance buildAndAddTerminalTask() {\n        return repository.add(aUserTask().withName(\"terminalTask\").withStateExecuting(false).withStable(true)\n                .withTerminal(true)\n                .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS).build());\n    }\n\n    protected void buildAndCreateProcessDefinition(final long id, long processDefinitionId, final String processName,\n            String processVersion,\n            String activationState) {\n        final SProcessDefinitionDeployInfo sProcessDefinitionDeployInfo = new SProcessDefinitionDeployInfo();\n        sProcessDefinitionDeployInfo.setId(id);\n        sProcessDefinitionDeployInfo.setName(processName);\n        sProcessDefinitionDeployInfo.setVersion(processVersion);\n        sProcessDefinitionDeployInfo.setProcessId(processDefinitionId);\n        sProcessDefinitionDeployInfo.setActivationState(activationState);\n        repository.add(sProcessDefinitionDeployInfo);\n    }\n\n    @Test\n    public void getProcessDefinitionDeployInfosByName_should_not_retrieve_disabled_processes() {\n        // given:\n        buildAndCreateProcessDefinition(1L, 44657531564675L, \"ChildProcess\", \"1.0\", \"DISABLED\");\n\n        // when:\n        final List<SProcessDefinitionDeployInfo> processes = repository\n                .getProcessDefinitionDeployInfosByName(\"ChildProcess\");\n\n        // then:\n        assertThat(processes).hasSize(0);\n    }\n\n    @Test\n    public void getProcessDefinitionDeployInfosByName_should_retrieve_an_enabled_process_even_if_not_most_recent()\n            throws Exception {\n        // given:\n        buildAndCreateProcessDefinition(2L, 4465753156777L, \"ChildProcess\", \"1.0\", \"ENABLED\");\n        Thread.sleep(20L);\n        buildAndCreateProcessDefinition(3L, 4465753158885L, \"ChildProcess\", \"2.0\", \"DISABLED\");\n\n        // when:\n        final List<SProcessDefinitionDeployInfo> processes = repository\n                .getProcessDefinitionDeployInfosByName(\"ChildProcess\");\n\n        // then:\n        assertThat(processes).hasSize(1);\n        assertThat(processes.get(0).getVersion()).isEqualTo(\"1.0\");\n    }\n\n    @Test\n    public void should_save_and_retrieve_process_definition_content() {\n        SProcessDefinitionDesignContent content = repository\n                .add(SProcessDefinitionDesignContent.builder().content(\"<xml>content</xml>\").build());\n        SProcessDefinitionDeployInfo processDefinition = repository.add(SProcessDefinitionDeployInfo.builder()\n                .name(\"MyProcessWithContent\")\n                .processId(123456L)\n                .version(\"1.12\")\n                .description(\"a normal process\")\n                .displayName(\"My process with content\")\n                .displayDescription(\"A normal process display description\")\n                .configurationState(\"OK\")\n                .activationState(\"ACTIVATED\")\n                .designContent(content).build());\n\n        PersistentObject persistentObject = repository.selectOne(\"getDeployInfoByProcessDefId\",\n                pair(\"processId\", 123456L));\n\n        assertThat(persistentObject).isEqualTo(processDefinition);\n        assertThat(((SProcessDefinitionDeployInfo) persistentObject).getDesignContent().getContent())\n                .isEqualTo(\"<xml>content</xml>\");\n\n    }\n\n    @Test\n    public void should_reference_design_content_using_id() {\n        SProcessDefinitionDesignContent content = repository\n                .add(SProcessDefinitionDesignContent.builder().content(\"<xml>content</xml>\").build());\n        repository.add(SProcessDefinitionDeployInfo.builder()\n                .name(\"MyProcessWithContent\")\n                .processId(123456L)\n                .version(\"1.12\")\n                .description(\"a normal process\")\n                .displayName(\"My process with content\")\n                .displayDescription(\"A normal process display description\")\n                .configurationState(\"OK\")\n                .activationState(\"ACTIVATED\")\n                .designContent(content).build());\n        repository.flush();\n\n        Map<String, Object> processDefinitionMap = jdbcTemplate.queryForObject(\n                \"SELECT name, CONTENT_ID FROM process_definition WHERE processId=123456\",\n                new JdbcRowMapper(\"CONTENT_ID\"));\n        Map<String, Object> processContentMap = jdbcTemplate.queryForObject(\"SELECT ID FROM process_content\",\n                new JdbcRowMapper(\"ID\"));\n\n        assertThat(processDefinitionMap.get(\"NAME\")).isEqualTo(\"MyProcessWithContent\");\n        assertThat(processDefinitionMap.get(\"CONTENT_ID\")).isEqualTo(content.getId());\n        assertThat(processContentMap.get(\"ID\")).isEqualTo(content.getId());\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/process/instance/model/ProcessInstanceQueriesTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport static java.time.Instant.now;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.bonitasoft.engine.bpm.process.ProcessInstanceState.*;\nimport static org.bonitasoft.engine.commons.Pair.mapOf;\nimport static org.bonitasoft.engine.commons.Pair.pair;\nimport static org.bonitasoft.engine.core.process.definition.model.SFlowNodeType.RECEIVE_TASK;\nimport static org.bonitasoft.engine.test.persistence.builder.ActorBuilder.anActor;\nimport static org.bonitasoft.engine.test.persistence.builder.ActorMemberBuilder.anActorMember;\nimport static org.bonitasoft.engine.test.persistence.builder.CallActivityInstanceBuilder.aCallActivityInstanceBuilder;\nimport static org.bonitasoft.engine.test.persistence.builder.GatewayInstanceBuilder.aGatewayInstanceBuilder;\nimport static org.bonitasoft.engine.test.persistence.builder.PendingActivityMappingBuilder.aPendingActivityMapping;\nimport static org.bonitasoft.engine.test.persistence.builder.ProcessInstanceBuilder.aProcessInstance;\nimport static org.bonitasoft.engine.test.persistence.builder.SupervisorBuilder.aSupervisor;\nimport static org.bonitasoft.engine.test.persistence.builder.UserBuilder.aUser;\nimport static org.bonitasoft.engine.test.persistence.builder.UserMembershipBuilder.aUserMembership;\nimport static org.junit.Assert.assertEquals;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.actor.mapping.model.SActor;\nimport org.bonitasoft.engine.actor.mapping.model.SActorMember;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SFlowNodeSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.test.persistence.jdbc.JdbcRowMapper;\nimport org.bonitasoft.engine.test.persistence.repository.ProcessInstanceRepository;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.transaction.annotation.Transactional;\n\n@RunWith(SpringRunner.class)\n@ContextConfiguration(locations = { \"/testContext.xml\" })\n@Transactional\npublic class ProcessInstanceQueriesTest {\n\n    private static final long aGroupId = 654L;\n    private static final long anotherGroupId = 9875L;\n    private static final long aRoleId = 1235L;\n    private static final long anotherRoleId = 956L;\n    private static final long PROCESS_INSTANCE_ID = 43578923425L;\n    private static final long FLOW_NODE_INSTANCE_ID = 342678L;\n\n    @Autowired\n    private ProcessInstanceRepository repository;\n\n    @Autowired\n    private JdbcTemplate jdbcTemplate;\n\n    @Test\n    public void getPossibleUserIdsOfPendingTasks_should_return_users_mapped_through_user_filters() {\n        final SUser expectedUser = repository.add(aUser().withId(1L).build());\n        repository.add(aUser().withId(2L).build()); // not expected user\n        final SPendingActivityMapping pendingActivity = repository\n                .add(aPendingActivityMapping().withUserId(expectedUser.getId()).build());\n\n        final List<Long> userIds = repository.getPossibleUserIdsOfPendingTasks(pendingActivity.getActivityId());\n\n        assertThat(userIds).containsOnly(expectedUser.getId());\n    }\n\n    @Test\n    public void isTaskPendingForUser_should_be_true_when_mapped_using_pending_mapping() {\n        final SUser expectedUser = repository.add(aUser().withId(1L).build());\n        repository.add(aUser().withId(2L).build()); // not expected user\n        final SPendingActivityMapping pendingActivity = repository\n                .add(aPendingActivityMapping().withUserId(expectedUser.getId()).build());\n\n        boolean taskPendingForUser = repository.isTaskPendingForUser(pendingActivity.getActivityId(),\n                expectedUser.getId());\n\n        assertThat(taskPendingForUser).isTrue();\n    }\n\n    @Test\n    public void isTaskPendingForUser_should_be_true_when_mapped_using_actor() {\n        final SUser expectedUser = repository.add(aUser().build());\n        SActor actor = repository.add(anActor().build());\n        SActorMember actorMember = repository\n                .add(anActorMember().withUserId(expectedUser.getId()).forActor(actor).build());\n        final SPendingActivityMapping pendingActivity = repository\n                .add(aPendingActivityMapping().withActorId(actor.getId()).build());\n\n        boolean taskPendingForUser = repository.isTaskPendingForUser(pendingActivity.getActivityId(),\n                expectedUser.getId());\n\n        assertThat(taskPendingForUser).isTrue();\n    }\n\n    @Test\n    public void isTaskPendingForUser_should_be_true_when_mapped_using_actor_having_role() {\n        final SUser expectedUser = repository.add(aUser().build());\n        repository.add(aUserMembership().forUser(expectedUser).memberOf(aGroupId, aRoleId).build());\n        SActor actor = repository.add(anActor().build());\n        repository.add(anActorMember().withRoleId(aRoleId).forActor(actor).build());\n        final SPendingActivityMapping pendingActivity = repository\n                .add(aPendingActivityMapping().withActorId(actor.getId()).build());\n\n        boolean taskPendingForUser = repository.isTaskPendingForUser(pendingActivity.getActivityId(),\n                expectedUser.getId());\n\n        assertThat(taskPendingForUser).isTrue();\n    }\n\n    @Test\n    public void isTaskPendingForUser_should_be_true_when_mapped_using_actor_having_group() {\n        final SUser expectedUser = repository.add(aUser().build());\n        repository.add(aUserMembership().forUser(expectedUser).memberOf(aGroupId, aRoleId).build());\n        SActor actor = repository.add(anActor().build());\n        repository.add(anActorMember().withGroupId(aGroupId).forActor(actor).build());\n        final SPendingActivityMapping pendingActivity = repository\n                .add(aPendingActivityMapping().withActorId(actor.getId()).build());\n\n        boolean taskPendingForUser = repository.isTaskPendingForUser(pendingActivity.getActivityId(),\n                expectedUser.getId());\n\n        assertThat(taskPendingForUser).isTrue();\n    }\n\n    @Test\n    public void isTaskPendingForUser_should_be_true_when_mapped_using_actor_having_membership() {\n        final SUser expectedUser = repository.add(aUser().build());\n        repository.add(aUserMembership().forUser(expectedUser).memberOf(anotherGroupId, anotherRoleId).build());\n        SActor actor = repository.add(anActor().build());\n        repository.add(anActorMember().withGroupId(anotherGroupId).withRoleId(anotherRoleId).forActor(actor).build());\n        final SPendingActivityMapping pendingActivity = repository\n                .add(aPendingActivityMapping().withActorId(actor.getId()).build());\n\n        boolean taskPendingForUser = repository.isTaskPendingForUser(pendingActivity.getActivityId(),\n                expectedUser.getId());\n\n        assertThat(taskPendingForUser).isTrue();\n    }\n\n    @Test\n    public void isTaskPendingForUser_should_be_false_when_not_pending() {\n        final SUser expectedUser = repository.add(aUser().withId(1L).build());\n        SUser notPendingUser = repository.add(aUser().withId(2L).build());// not expected user\n        final SPendingActivityMapping pendingActivity = repository\n                .add(aPendingActivityMapping().withUserId(expectedUser.getId()).build());\n\n        boolean taskPendingForUser = repository.isTaskPendingForUser(pendingActivity.getActivityId(),\n                notPendingUser.getId());\n\n        assertThat(taskPendingForUser).isFalse();\n    }\n\n    @Test\n    public void isTaskPendingForUser() {\n        final SUser expectedUser = repository.add(aUser().withId(1L).build());\n        repository.add(aUser().withId(2L).build()); // not expected user\n        final SPendingActivityMapping pendingActivity = repository\n                .add(aPendingActivityMapping().withUserId(expectedUser.getId()).build());\n\n        boolean taskPendingForUser = repository.isTaskPendingForUser(pendingActivity.getActivityId(),\n                expectedUser.getId());\n\n        assertThat(taskPendingForUser).isTrue();\n    }\n\n    @Test\n    public void getPossibleUserIdsOfPendingTasks_should_return_users_mapped_through_his_userid_in_actormember() {\n        final SActor actor = repository.add(anActor().build());\n        final SPendingActivityMapping addedPendingMapping = repository\n                .add(aPendingActivityMapping().withActorId(actor.getId()).build());\n        final SUser expectedUser = repository.add(aUser().withId(1L).build());\n        repository.add(anActorMember().forActor(actor).withUserId(expectedUser.getId()).build());\n        repository.add(aUser().withId(2L).build()); // not expected user\n\n        final List<Long> userIds = repository.getPossibleUserIdsOfPendingTasks(addedPendingMapping.getActivityId());\n\n        assertThat(userIds).containsOnly(expectedUser.getId());\n    }\n\n    @Test\n    public void getPossibleUserIdsOfPendingTasks_should_return_users_mapped_through_his_groupid_in_actormember() {\n        final SActor actor = repository.add(anActor().build());\n        final SPendingActivityMapping addedPendingMapping = repository\n                .add(aPendingActivityMapping().withActorId(actor.getId()).build());\n        repository.add(anActorMember().forActor(actor).withGroupId(aGroupId).build());\n        final SUser expectedUser = repository.add(aUser().withId(1L).build());\n        repository.add(aUserMembership().forUser(expectedUser).memberOf(aGroupId, aRoleId).build());\n        final SUser notExpectedUser = repository.add(aUser().withId(2L).build());\n        repository.add(aUserMembership().forUser(notExpectedUser).memberOf(anotherGroupId, aRoleId).build());\n\n        final List<Long> userIds = repository.getPossibleUserIdsOfPendingTasks(addedPendingMapping.getActivityId());\n\n        assertThat(userIds).containsOnly(expectedUser.getId());\n    }\n\n    @Test\n    public void getPossibleUserIdsOfPendingTasks_should_return_users_mapped_through_his_roleid_in_actormember() {\n        final SActor actor = repository.add(anActor().build());\n        final SPendingActivityMapping addedPendingMapping = repository\n                .add(aPendingActivityMapping().withActorId(actor.getId()).build());\n        repository.add(anActorMember().forActor(actor).withRoleId(aRoleId).build());\n        final SUser expectedUser = repository.add(aUser().withId(1L).build());\n        repository.add(aUserMembership().forUser(expectedUser).memberOf(aGroupId, aRoleId).build());\n        final SUser notexpectedUser = repository.add(aUser().withId(2L).build());\n        repository.add(aUserMembership().forUser(notexpectedUser).memberOf(aGroupId, anotherRoleId).build());\n\n        final List<Long> userIds = repository.getPossibleUserIdsOfPendingTasks(addedPendingMapping.getActivityId());\n\n        assertThat(userIds).containsOnly(expectedUser.getId());\n    }\n\n    @Test\n    public void getPossibleUserIdsOfPendingTasks_should_return_users_mapped_through_his_membership_in_actormember() {\n        final SUser expectedUser = repository.add(aUser().withId(1L).build());\n        final SUser expectedUser2 = repository.add(aUser().withId(4L).build());\n        final SUser notExpectedUser = repository.add(aUser().withId(2L).build());\n        final SUser notExpectedUser2 = repository.add(aUser().withId(3L).build());\n        final SActor actor = repository.add(anActor().build());\n        final SPendingActivityMapping addedPendingMapping = repository\n                .add(aPendingActivityMapping().withActorId(actor.getId()).build());\n        repository.add(anActorMember().forActor(actor).withGroupId(aGroupId).withRoleId(aRoleId).build());\n        repository.add(aUserMembership().forUser(expectedUser).memberOf(aGroupId, aRoleId).build());\n        repository.add(aUserMembership().forUser(expectedUser2).memberOf(aGroupId, aRoleId).build());\n        repository.add(aUserMembership().forUser(notExpectedUser).memberOf(anotherGroupId, aRoleId).build());\n        repository.add(aUserMembership().forUser(notExpectedUser2).memberOf(aGroupId, anotherRoleId).build());\n\n        final List<Long> userIds = repository.getPossibleUserIdsOfPendingTasks(addedPendingMapping.getActivityId());\n\n        assertThat(userIds).containsOnly(expectedUser.getId(), expectedUser2.getId());\n    }\n\n    @Test\n    public void getPossibleUserIdsOfPendingTasks_return_userIds_ordered_by_userName() {\n        final SUser john = repository.add(aUser().withUserName(\"john\").withId(1L).build());\n        final SUser paul = repository.add(aUser().withUserName(\"paul\").withId(2L).build());\n        final SUser walter = repository.add(aUser().withUserName(\"walter\").withId(3L).build());\n        final SUser marie = repository.add(aUser().withUserName(\"marie\").withId(4L).build());\n        final SActor actor = repository.add(anActor().build());\n        final SPendingActivityMapping addedPendingMapping = repository\n                .add(aPendingActivityMapping().withActorId(actor.getId()).build());\n        repository.add(anActorMember().forActor(actor).withGroupId(aGroupId).withRoleId(aRoleId).build());\n        repository.add(aUserMembership().forUser(john).memberOf(aGroupId, aRoleId).build());\n        repository.add(aUserMembership().forUser(paul).memberOf(aGroupId, aRoleId).build());\n        repository.add(aUserMembership().forUser(walter).memberOf(aGroupId, aRoleId).build());\n        repository.add(aUserMembership().forUser(marie).memberOf(aGroupId, aRoleId).build());\n\n        final List<Long> userIds = repository.getPossibleUserIdsOfPendingTasks(addedPendingMapping.getActivityId());\n\n        assertThat(userIds).containsExactly(john.getId(), marie.getId(), paul.getId(), walter.getId());\n    }\n\n    @Test\n    public void getNumberOfSUserWhoCanStartPendingTask_should_return_users_mapped_through_user_filters() {\n        final SUser expectedUser = repository.add(aUser().withId(1L).build());\n        repository.add(aUser().withId(2L).build()); // not expected user\n        final SPendingActivityMapping pendingActivity = repository\n                .add(aPendingActivityMapping().withUserId(expectedUser.getId()).build());\n\n        final long numberOfUsers = repository.getNumberOfSUserWhoCanStartPendingTask(pendingActivity.getActivityId());\n\n        assertThat(numberOfUsers).isEqualTo(1);\n    }\n\n    @Test\n    public void getNumberOfSUserWhoCanStartPendingTask_should_return_users_mapped_through_his_userid_in_actormember() {\n        final SActor actor = repository.add(anActor().build());\n        final SPendingActivityMapping addedPendingMapping = repository\n                .add(aPendingActivityMapping().withActorId(actor.getId()).build());\n        final SUser expectedUser = repository.add(aUser().withId(1L).build());\n        repository.add(anActorMember().forActor(actor).withUserId(expectedUser.getId()).build());\n        repository.add(aUser().withId(2L).build()); // not expected user\n\n        final long numberOfUsers = repository\n                .getNumberOfSUserWhoCanStartPendingTask(addedPendingMapping.getActivityId());\n\n        assertThat(numberOfUsers).isEqualTo(1);\n    }\n\n    @Test\n    public void getNumberOfSUserWhoCanStartPendingTask_should_return_users_mapped_through_his_groupid_in_actormember() {\n        final SActor actor = repository.add(anActor().build());\n        final SPendingActivityMapping addedPendingMapping = repository\n                .add(aPendingActivityMapping().withActorId(actor.getId()).build());\n        repository.add(anActorMember().forActor(actor).withGroupId(aGroupId).build());\n        final SUser expectedUser = repository.add(aUser().withId(1L).build());\n        repository.add(aUserMembership().forUser(expectedUser).memberOf(aGroupId, aRoleId).build());\n        final SUser notExpectedUser = repository.add(aUser().withId(2L).build());\n        repository.add(aUserMembership().forUser(notExpectedUser).memberOf(anotherGroupId, aRoleId).build());\n\n        final long numberOfUsers = repository\n                .getNumberOfSUserWhoCanStartPendingTask(addedPendingMapping.getActivityId());\n\n        assertThat(numberOfUsers).isEqualTo(1);\n    }\n\n    @Test\n    public void getNumberOfSUserWhoCanStartPendingTask_should_return_users_mapped_through_his_roleid_in_actormember() {\n        final SActor actor = repository.add(anActor().build());\n        final SPendingActivityMapping addedPendingMapping = repository\n                .add(aPendingActivityMapping().withActorId(actor.getId()).build());\n        repository.add(anActorMember().forActor(actor).withRoleId(aRoleId).build());\n        final SUser expectedUser = repository.add(aUser().withId(1L).build());\n        repository.add(aUserMembership().forUser(expectedUser).memberOf(aGroupId, aRoleId).build());\n        final SUser notexpectedUser = repository.add(aUser().withId(2L).build());\n        repository.add(aUserMembership().forUser(notexpectedUser).memberOf(aGroupId, anotherRoleId).build());\n\n        final long numberOfUsers = repository\n                .getNumberOfSUserWhoCanStartPendingTask(addedPendingMapping.getActivityId());\n\n        assertThat(numberOfUsers).isEqualTo(1);\n    }\n\n    @Test\n    public void getNumberOfSUserWhoCanStartPendingTask_should_return_users_mapped_through_his_membership_in_actormember() {\n        final SUser expectedUser = repository.add(aUser().withId(1L).build());\n        final SUser expectedUser2 = repository.add(aUser().withId(4L).build());\n        final SUser notExpectedUser = repository.add(aUser().withId(2L).build());\n        final SUser notExpectedUser2 = repository.add(aUser().withId(3L).build());\n        final SActor actor = repository.add(anActor().build());\n        final SPendingActivityMapping addedPendingMapping = repository\n                .add(aPendingActivityMapping().withActorId(actor.getId()).build());\n        repository.add(anActorMember().forActor(actor).withGroupId(aGroupId).withRoleId(aRoleId).build());\n        repository.add(aUserMembership().forUser(expectedUser).memberOf(aGroupId, aRoleId).build());\n        repository.add(aUserMembership().forUser(expectedUser2).memberOf(aGroupId, aRoleId).build());\n        repository.add(aUserMembership().forUser(notExpectedUser).memberOf(anotherGroupId, aRoleId).build());\n        repository.add(aUserMembership().forUser(notExpectedUser2).memberOf(aGroupId, anotherRoleId).build());\n\n        final long numberOfUsers = repository\n                .getNumberOfSUserWhoCanStartPendingTask(addedPendingMapping.getActivityId());\n\n        assertThat(numberOfUsers).isEqualTo(2);\n    }\n\n    @Test\n    public void getNumberOfSUserWhoCanStartPendingTask_return_userIds_ordered_by_userName() {\n        final SUser john = repository.add(aUser().withUserName(\"john\").withId(1L).build());\n        final SUser paul = repository.add(aUser().withUserName(\"paul\").withId(2L).build());\n        final SUser walter = repository.add(aUser().withUserName(\"walter\").withId(3L).build());\n        final SUser marie = repository.add(aUser().withUserName(\"marie\").withId(4L).build());\n        final SActor actor = repository.add(anActor().build());\n        final SPendingActivityMapping addedPendingMapping = repository\n                .add(aPendingActivityMapping().withActorId(actor.getId()).build());\n        repository.add(anActorMember().forActor(actor).withGroupId(aGroupId).withRoleId(aRoleId).build());\n        repository.add(aUserMembership().forUser(john).memberOf(aGroupId, aRoleId).build());\n        repository.add(aUserMembership().forUser(paul).memberOf(aGroupId, aRoleId).build());\n        repository.add(aUserMembership().forUser(walter).memberOf(aGroupId, aRoleId).build());\n        repository.add(aUserMembership().forUser(marie).memberOf(aGroupId, aRoleId).build());\n\n        final long numberOfUsers = repository\n                .getNumberOfSUserWhoCanStartPendingTask(addedPendingMapping.getActivityId());\n\n        assertThat(numberOfUsers).isEqualTo(4);\n    }\n\n    @Test\n    public void searchSUserWhoCanStartPendingTask_should_return_users_mapped_through_user_filters() {\n        final SUser expectedUser = repository.add(aUser().withId(1L).build());\n        repository.add(aUser().withId(2L).build()); // not expected user\n        final SPendingActivityMapping pendingActivity = repository\n                .add(aPendingActivityMapping().withUserId(expectedUser.getId()).build());\n\n        final List<SUser> userIds = repository.searchPossibleUserIdsOfPendingTasks(pendingActivity.getActivityId());\n\n        assertThat(userIds).containsOnly(expectedUser);\n    }\n\n    @Test\n    public void searchSUserWhoCanStartPendingTask_should_return_users_mapped_through_his_userid_in_actormember() {\n        final SActor actor = repository.add(anActor().build());\n        final SPendingActivityMapping addedPendingMapping = repository\n                .add(aPendingActivityMapping().withActorId(actor.getId()).build());\n        final SUser expectedUser = repository.add(aUser().withId(1L).build());\n        repository.add(anActorMember().forActor(actor).withUserId(expectedUser.getId()).build());\n        repository.add(aUser().withId(2L).build()); // not expected user\n\n        final List<SUser> userIds = repository.searchPossibleUserIdsOfPendingTasks(addedPendingMapping.getActivityId());\n\n        assertThat(userIds).containsOnly(expectedUser);\n    }\n\n    @Test\n    public void searchSUserWhoCanStartPendingTask_should_return_users_mapped_through_his_groupid_in_actormember() {\n        final SActor actor = repository.add(anActor().build());\n        final SPendingActivityMapping addedPendingMapping = repository\n                .add(aPendingActivityMapping().withActorId(actor.getId()).build());\n        repository.add(anActorMember().forActor(actor).withGroupId(aGroupId).build());\n        final SUser expectedUser = repository.add(aUser().withId(1L).build());\n        repository.add(aUserMembership().forUser(expectedUser).memberOf(aGroupId, aRoleId).build());\n        final SUser notExpectedUser = repository.add(aUser().withId(2L).build());\n        repository.add(aUserMembership().forUser(notExpectedUser).memberOf(anotherGroupId, aRoleId).build());\n\n        final List<SUser> userIds = repository.searchPossibleUserIdsOfPendingTasks(addedPendingMapping.getActivityId());\n\n        assertThat(userIds).containsOnly(expectedUser);\n    }\n\n    @Test\n    public void searchSUserWhoCanStartPendingTask_should_return_users_mapped_through_his_roleid_in_actormember() {\n        final SActor actor = repository.add(anActor().build());\n        final SPendingActivityMapping addedPendingMapping = repository\n                .add(aPendingActivityMapping().withActorId(actor.getId()).build());\n        repository.add(anActorMember().forActor(actor).withRoleId(aRoleId).build());\n        final SUser expectedUser = repository.add(aUser().withId(1L).build());\n        repository.add(aUserMembership().forUser(expectedUser).memberOf(aGroupId, aRoleId).build());\n        final SUser notexpectedUser = repository.add(aUser().withId(2L).build());\n        repository.add(aUserMembership().forUser(notexpectedUser).memberOf(aGroupId, anotherRoleId).build());\n\n        final List<SUser> userIds = repository.searchPossibleUserIdsOfPendingTasks(addedPendingMapping.getActivityId());\n\n        assertThat(userIds).containsOnly(expectedUser);\n    }\n\n    @Test\n    public void searchSUserWhoCanStartPendingTask_should_return_users_mapped_through_his_membership_in_actormember() {\n        final SUser expectedUser = repository.add(aUser().withId(1L).build());\n        final SUser expectedUser2 = repository.add(aUser().withId(4L).build());\n        final SUser notExpectedUser = repository.add(aUser().withId(2L).build());\n        final SUser notExpectedUser2 = repository.add(aUser().withId(3L).build());\n        final SActor actor = repository.add(anActor().build());\n        final SPendingActivityMapping addedPendingMapping = repository\n                .add(aPendingActivityMapping().withActorId(actor.getId()).build());\n        repository.add(anActorMember().forActor(actor).withGroupId(aGroupId).withRoleId(aRoleId).build());\n        repository.add(aUserMembership().forUser(expectedUser).memberOf(aGroupId, aRoleId).build());\n        repository.add(aUserMembership().forUser(expectedUser2).memberOf(aGroupId, aRoleId).build());\n        repository.add(aUserMembership().forUser(notExpectedUser).memberOf(anotherGroupId, aRoleId).build());\n        repository.add(aUserMembership().forUser(notExpectedUser2).memberOf(aGroupId, anotherRoleId).build());\n\n        final List<SUser> userIds = repository.searchPossibleUserIdsOfPendingTasks(addedPendingMapping.getActivityId());\n\n        assertThat(userIds).containsOnly(expectedUser, expectedUser2);\n    }\n\n    @Test\n    public void searchSUserWhoCanStartPendingTask_return_userIds_ordered_by_userName() {\n        final SUser john = repository.add(aUser().withUserName(\"john\").withId(1L).build());\n        final SUser paul = repository.add(aUser().withUserName(\"paul\").withId(2L).build());\n        final SUser walter = repository.add(aUser().withUserName(\"walter\").withId(3L).build());\n        final SUser marie = repository.add(aUser().withUserName(\"marie\").withId(4L).build());\n        final SActor actor = repository.add(anActor().build());\n        final SPendingActivityMapping addedPendingMapping = repository\n                .add(aPendingActivityMapping().withActorId(actor.getId()).build());\n        repository.add(anActorMember().forActor(actor).withGroupId(aGroupId).withRoleId(aRoleId).build());\n        repository.add(aUserMembership().forUser(john).memberOf(aGroupId, aRoleId).build());\n        repository.add(aUserMembership().forUser(paul).memberOf(aGroupId, aRoleId).build());\n        repository.add(aUserMembership().forUser(walter).memberOf(aGroupId, aRoleId).build());\n        repository.add(aUserMembership().forUser(marie).memberOf(aGroupId, aRoleId).build());\n\n        final List<SUser> users = repository.searchPossibleUserIdsOfPendingTasks(addedPendingMapping.getActivityId());\n\n        assertThat(users).hasSize(4).contains(john, marie, paul, walter);\n    }\n\n    @Test\n    public void searchSingleChildrenSProcessInstanceOfProcessInstance_return_processInstancesIds() {\n        final SProcessInstance parentPI = repository\n                .add(aProcessInstance().withContainerId(1).withName(\"test Parent Process Instance\")\n                        .withStateId(STARTED.getId()).build());\n        final SCallActivityInstance callActivity = (SCallActivityInstance) repository.add(aCallActivityInstanceBuilder()\n                .withLogicalGroup4(parentPI.getId())\n                .withName(\"call Activity\")\n                .build());\n        final SProcessInstance childPI = repository\n                .add(aProcessInstance().withContainerId(1).withName(\"test Child Process Instance\")\n                        .withCallerId(callActivity.getId()).withCallerType(SFlowNodeType.CALL_ACTIVITY)\n                        .withStateId(STARTED.getId()).build());\n\n        assertThat(repository.countChildrenInstanceIdsOfProcessInstance(parentPI.getId())).isEqualTo(1);\n        final List<Long> piIds = repository.getChildrenInstanceIdsOfProcessInstance(parentPI.getId());\n        assertThat(piIds).isNotEmpty().containsExactly(childPI.getId());\n    }\n\n    @Test\n    public void searchOnlyChildrenSProcessInstanceOfProcessInstance_return_processInstancesIdsFromCallActivity() {\n        final SProcessInstance parentPI = repository\n                .add(aProcessInstance().withContainerId(1).withName(\"test Parent Process Instance\")\n                        .withStateId(STARTED.getId()).build());\n        final SCallActivityInstance callActivity = (SCallActivityInstance) repository.add(aCallActivityInstanceBuilder()\n                .withLogicalGroup4(parentPI.getId())\n                .withName(\"call Activity\")\n                .build());\n        final SGatewayInstance gatewayActivity = (SGatewayInstance) repository\n                .add(aGatewayInstanceBuilder().withLogicalGroup4(parentPI.getId())\n                        .withName(\"call Activity\")\n                        .build());\n        final SProcessInstance childPI = repository\n                .add(aProcessInstance().withContainerId(1).withName(\"test Child Process Instance\")\n                        .withCallerId(callActivity.getId()).withCallerType(SFlowNodeType.CALL_ACTIVITY)\n                        .withStateId(STARTED.getId()).build());\n        repository.add(aProcessInstance().withContainerId(1).withName(\"test Parent Process Instance\")\n                .withCallerId(gatewayActivity.getId()).withCallerType(SFlowNodeType.GATEWAY)\n                .withStateId(STARTED.getId()).build());\n\n        assertThat(repository.countChildrenInstanceIdsOfProcessInstance(parentPI.getId())).isEqualTo(1);\n        final List<Long> piIds = repository.getChildrenInstanceIdsOfProcessInstance(parentPI.getId());\n        assertThat(piIds).isNotEmpty().containsExactly(childPI.getId());\n    }\n\n    @Test\n    public void searchChildProcessInstanceOfProcessInstance_return_processInstancesIdsNotGrandChildren() {\n        final SProcessInstance parentPI = repository\n                .add(aProcessInstance().withContainerId(1).withName(\"test Parent Process Instance\")\n                        .withStateId(STARTED.getId()).build());\n        final SCallActivityInstance callActivity = (SCallActivityInstance) repository.add(aCallActivityInstanceBuilder()\n                .withLogicalGroup4(parentPI.getId())\n                .withName(\"call Activity\")\n                .build());\n        final SProcessInstance childPI = repository\n                .add(aProcessInstance().withContainerId(1).withName(\"test Child Process Instance Started\")\n                        .withCallerId(callActivity.getId()).withCallerType(SFlowNodeType.CALL_ACTIVITY)\n                        .withStateId(STARTED.getId()).build());\n        final SCallActivityInstance callSubActivity = (SCallActivityInstance) repository\n                .add(aCallActivityInstanceBuilder()\n                        .withLogicalGroup4(childPI.getId())\n                        .withName(\"call Activity\")\n                        .build());\n        repository.add(aProcessInstance().withContainerId(1).withName(\"test Grand Child Process Instance Started\")\n                .withCallerId(callSubActivity.getId()).withCallerType(SFlowNodeType.CALL_ACTIVITY)\n                .withStateId(ProcessInstanceState.CANCELLED.getId()).build());\n\n        assertThat(repository.countChildrenInstanceIdsOfProcessInstance(parentPI.getId())).isEqualTo(1);\n        final List<Long> piIds = repository.getChildrenInstanceIdsOfProcessInstance(parentPI.getId());\n        assertThat(piIds).isNotEmpty().containsOnly(childPI.getId());\n    }\n\n    @Test\n    public void searchGrandChildProcessInstanceOfChildProcessInstance_return_processInstancesIdsNotChild() {\n        final SProcessInstance parentPI = repository\n                .add(aProcessInstance().withContainerId(1).withName(\"test Parent Process Instance\")\n                        .withStateId(STARTED.getId()).build());\n        final SCallActivityInstance callActivity = (SCallActivityInstance) repository.add(aCallActivityInstanceBuilder()\n                .withLogicalGroup4(parentPI.getId())\n                .withName(\"call Activity\")\n                .build());\n        final SProcessInstance childPI = repository\n                .add(aProcessInstance().withContainerId(1).withName(\"test Child Process Instance Started\")\n                        .withCallerId(callActivity.getId()).withCallerType(SFlowNodeType.CALL_ACTIVITY)\n                        .withStateId(STARTED.getId()).build());\n        final SCallActivityInstance callSubActivity = (SCallActivityInstance) repository\n                .add(aCallActivityInstanceBuilder()\n                        .withLogicalGroup4(childPI.getId())\n                        .withName(\"call Activity\")\n                        .build());\n        final SProcessInstance grandChildPI = repository\n                .add(aProcessInstance().withContainerId(1).withName(\"test Grand Child Process Instance Started\")\n                        .withCallerId(callSubActivity.getId()).withCallerType(SFlowNodeType.CALL_ACTIVITY)\n                        .withStateId(ProcessInstanceState.CANCELLED.getId()).build());\n\n        assertThat(repository.countChildrenInstanceIdsOfProcessInstance(childPI.getId())).isEqualTo(1);\n        final List<Long> piIds = repository.getChildrenInstanceIdsOfProcessInstance(childPI.getId());\n        assertThat(piIds).isNotEmpty().containsOnly(grandChildPI.getId());\n    }\n\n    @Test\n    public void searchChildrenSProcessInstanceOfProcessInstance_return_processInstancesIds() {\n        final SProcessInstance parentPI = repository\n                .add(aProcessInstance().withContainerId(1).withName(\"test Parent Process Instance\")\n                        .withStateId(STARTED.getId()).build());\n        final SCallActivityInstance callActivity = (SCallActivityInstance) repository.add(aCallActivityInstanceBuilder()\n                .withLogicalGroup4(parentPI.getId())\n                .withName(\"call Activity\")\n                .build());\n        final SProcessInstance childPI1 = repository\n                .add(aProcessInstance().withContainerId(1).withName(\"test Child Process Instance Started\")\n                        .withCallerId(callActivity.getId()).withCallerType(SFlowNodeType.CALL_ACTIVITY)\n                        .withStateId(STARTED.getId()).build());\n        final SProcessInstance childPI2 = repository\n                .add(aProcessInstance().withContainerId(1).withName(\"test Child Process Instance Started\")\n                        .withCallerId(callActivity.getId()).withCallerType(SFlowNodeType.CALL_ACTIVITY)\n                        .withStateId(ProcessInstanceState.COMPLETED.getId()).build());\n        final SProcessInstance childPI3 = repository\n                .add(aProcessInstance().withContainerId(1).withName(\"test Child Process Instance Started\")\n                        .withCallerId(callActivity.getId()).withCallerType(SFlowNodeType.CALL_ACTIVITY)\n                        .withStateId(ProcessInstanceState.CANCELLED.getId()).build());\n\n        assertThat(repository.countChildrenInstanceIdsOfProcessInstance(parentPI.getId())).isEqualTo(3);\n        final List<Long> piIds = repository.getChildrenInstanceIdsOfProcessInstance(parentPI.getId());\n        assertThat(piIds).isNotEmpty().containsOnly(childPI1.getId(), childPI2.getId(), childPI3.getId());\n    }\n\n    @Test\n    public void searchChildSProcessInstanceOfProcessInstance_return_processInstancesIdsFromParentPIOnly() {\n        final SProcessInstance parentPI = repository\n                .add(aProcessInstance().withContainerId(1).withName(\"test Parent Process Instance\")\n                        .withStateId(STARTED.getId()).build());\n        repository.add(aProcessInstance().withContainerId(1).withName(\"test Process Instance Independent Started\")\n                .withStateId(STARTED.getId()).build());\n        repository.add(aProcessInstance().withContainerId(1).withName(\"test Process Instance Independent Completed\")\n                .withStateId(ProcessInstanceState.COMPLETED.getId()).build());\n        repository.add(aProcessInstance().withContainerId(1).withName(\"test Process Instance Independent Cancelled\")\n                .withStateId(ProcessInstanceState.CANCELLED.getId()).build());\n        final SCallActivityInstance callActivity = (SCallActivityInstance) repository.add(aCallActivityInstanceBuilder()\n                .withLogicalGroup4(parentPI.getId())\n                .withName(\"call Activity\")\n                .build());\n        final SProcessInstance childPI = repository\n                .add(aProcessInstance().withContainerId(1).withName(\"test Child Process Instance\")\n                        .withCallerId(callActivity.getId()).withCallerType(SFlowNodeType.CALL_ACTIVITY)\n                        .withStateId(STARTED.getId()).build());\n\n        assertThat(repository.countChildrenInstanceIdsOfProcessInstance(parentPI.getId())).isEqualTo(1);\n        final List<Long> piIds = repository.getChildrenInstanceIdsOfProcessInstance(parentPI.getId());\n        assertThat(piIds).isNotEmpty().containsExactly(childPI.getId());\n    }\n\n    @Test\n    public void getNumberOfProcessInstances_should_return_the_number_of_running_instances_of_a_process_definition() {\n        repository.add(aProcessInstance().withProcessDefinitionId(45l).build());\n        repository.add(aProcessInstance().withProcessDefinitionId(45l).build());\n        repository.add(aProcessInstance().withProcessDefinitionId(45l).build());\n        repository.add(aProcessInstance().withProcessDefinitionId(12l).build());\n\n        assertThat(repository.getNumberOfProcessInstances(45l)).isEqualTo(3);\n    }\n\n    @Test\n    public void getNumberOfSProcessInstanceFailedShouldAcceptExtraFilters() {\n        // Given\n        repository.add(buildFailedProcessInstance(1, 777777L));\n        repository.add(buildFailedProcessInstance(2, 777777L));\n        repository.add(buildFailedProcessInstance(3, 888888L));\n\n        // When\n        final long numberOfSProcessInstanceFailed = repository\n                .getNumberOfSProcessInstanceFailedForProcessDefinition(777777L);\n\n        // Then\n        assertEquals(2, numberOfSProcessInstanceFailed);\n    }\n\n    @Test\n    public void searchSProcessInstanceFailedShouldAcceptExtraFilters() {\n        // Given\n        repository.add(buildFailedProcessInstance(1, 777777L));\n        repository.add(buildFailedProcessInstance(2, 777777L));\n        repository.add(buildFailedProcessInstance(3, 888888L));\n\n        // When\n        final List<SProcessInstance> sProcessInstanceFailed = repository\n                .searchSProcessInstanceFailedForProcessDefinition(777777L);\n\n        // Then\n        assertEquals(2, sProcessInstanceFailed.size());\n    }\n\n    @Test\n    public void getNumberOfSProcessInstanceFailed_should_return_number_of_distinct_process_instances() {\n        // Given\n        repository.add(buildFailedProcessInstance(1));\n\n        final SProcessInstance processInstanceWithFailedFlowNode = new SProcessInstance(\"process2\", 10L);\n        processInstanceWithFailedFlowNode.setId(2);\n        repository.add(processInstanceWithFailedFlowNode);\n        repository.add(buildFailedGateway(852, processInstanceWithFailedFlowNode.getId()));\n\n        final SProcessInstance failedProcessInstanceWithFailedFlowNode = repository.add(buildFailedProcessInstance(3));\n        repository.add(buildFailedGateway(56, failedProcessInstanceWithFailedFlowNode.getId()));\n\n        // When\n        final long numberOfSProcessInstanceFailed = repository.getNumberOfSProcessInstanceFailed();\n\n        // Then\n        assertEquals(3, numberOfSProcessInstanceFailed);\n    }\n\n    @Test\n    public void getNumberOfSProcessInstanceFailed_should_return_number_of_failed_process_instances() {\n        // Given\n        repository.add(buildFailedProcessInstance(1));\n\n        // When\n        final long numberOfSProcessInstanceFailed = repository.getNumberOfSProcessInstanceFailed();\n\n        // Then\n        assertEquals(1, numberOfSProcessInstanceFailed);\n    }\n\n    @Test\n    public void getNumberOfSProcessInstanceFailed_should_return_number_of_process_instances_with_failed_flow_nodes() {\n        // Given\n        final SProcessInstance processInstanceWithFailedFlowNode = new SProcessInstance(\"process2\", 10L);\n        processInstanceWithFailedFlowNode.setId(2);\n        repository.add(processInstanceWithFailedFlowNode);\n        repository.add(buildFailedGateway(1, processInstanceWithFailedFlowNode.getId()));\n\n        // When\n        final long numberOfSProcessInstanceFailed = repository.getNumberOfSProcessInstanceFailed();\n\n        // Then\n        assertEquals(1, numberOfSProcessInstanceFailed);\n    }\n\n    @Test\n    public void searchSProcessInstanceFailed_return_distinct_process_instances() {\n        // Given\n        final SProcessInstance failedProcessInstance = repository.add(buildFailedProcessInstance(1));\n\n        final SProcessInstance processInstanceWithFailedFlowNode = SProcessInstance.builder().name(\"process2\")\n                .processDefinitionId(10L)\n                .id(2)\n                .build();\n        repository.add(processInstanceWithFailedFlowNode);\n        repository.add(buildFailedGateway(1, processInstanceWithFailedFlowNode.getId()));\n\n        final SProcessInstance failedProcessInstanceWithFailedFlowNode = repository.add(buildFailedProcessInstance(3));\n        repository.add(buildFailedGateway(2, failedProcessInstanceWithFailedFlowNode.getId()));\n\n        // When\n        final List<SProcessInstance> failedSProcessInstance = repository.searchSProcessInstanceFailed();\n\n        // Then\n        assertThat(failedSProcessInstance).containsOnly(failedProcessInstance, failedProcessInstanceWithFailedFlowNode,\n                processInstanceWithFailedFlowNode);\n    }\n\n    @Test\n    public void searchSProcessInstanceFailed_return_failed_process_instances() {\n        // Given\n        final SProcessInstance failedProcessInstance = repository.add(buildFailedProcessInstance(1));\n\n        // When\n        final List<SProcessInstance> failedSProcessInstance = repository.searchSProcessInstanceFailed();\n\n        // Then\n        assertEquals(1, failedSProcessInstance.size());\n        assertEquals(failedProcessInstance, failedSProcessInstance.get(0));\n    }\n\n    @Test\n    public void searchSProcessInstanceFailed_return_process_instances_with_failed_flow_nodes() {\n        // Given\n        final SProcessInstance processInstanceWithFailedFlowNode = new SProcessInstance(\"process2\", 10L);\n        processInstanceWithFailedFlowNode.setId(2);\n        repository.add(processInstanceWithFailedFlowNode);\n        repository.add(buildFailedGateway(1, processInstanceWithFailedFlowNode.getId()));\n\n        // When\n        final List<SProcessInstance> failedSProcessInstance = repository.searchSProcessInstanceFailed();\n\n        // Then\n        assertEquals(1, failedSProcessInstance.size());\n        assertEquals(processInstanceWithFailedFlowNode, failedSProcessInstance.get(0));\n    }\n\n    @Test\n    public void getNumberOfSProcessInstanceFailedAndSupervisedBy_should_return_number_of_distinct_process_instances() {\n        // Given\n        final long userId = 2L;\n        repository.add(buildFailedProcessInstance(1));\n        repository.add(aSupervisor().withProcessDefinitionId(9L).withUserId(userId).build());\n\n        repository.add(aProcessInstance().withProcessDefinitionId(8L).withContainerId(1)\n                .withName(\"test Parent Process Instance\")\n                .withStateId(STARTED.getId()).build());\n        repository.add(aSupervisor().withProcessDefinitionId(8L).withUserId(userId).build());\n\n        SProcessInstance processInstanceWithFailedFlowNode = new SProcessInstance(\"process2\", 10L);\n        processInstanceWithFailedFlowNode.setId(2);\n        repository.add(processInstanceWithFailedFlowNode);\n        repository.add(buildFailedGateway(852, processInstanceWithFailedFlowNode.getId()));\n        repository.add(buildFailedProcessInstance(3, 11L));\n\n        processInstanceWithFailedFlowNode = new SProcessInstance(\"process2\", 15L);\n        processInstanceWithFailedFlowNode.setId(4);\n        repository.add(processInstanceWithFailedFlowNode);\n        repository.add(buildFailedGateway(853, processInstanceWithFailedFlowNode.getId()));\n        repository.add(buildStartedProcessInstance(5, 15L));\n        repository.add(aSupervisor().withProcessDefinitionId(15L).withUserId(userId).build());\n\n        // When\n        final long numberOfSProcessInstanceFailed = repository.getNumberOfFailedSProcessInstanceSupervisedBy(userId);\n\n        // Then\n        assertEquals(2, numberOfSProcessInstanceFailed);\n    }\n\n    @Test\n    public void getSProcessInstanceFailedAndSupervisedBy_should_return_number_of_distinct_process_instances() {\n        // Given\n        final long userId = 2L;\n        repository.add(buildFailedProcessInstance(1));\n        repository.add(aSupervisor().withProcessDefinitionId(9L).withUserId(userId).build());\n\n        repository.add(aProcessInstance().withProcessDefinitionId(8L).withContainerId(1)\n                .withName(\"test Parent Process Instance\")\n                .withStateId(STARTED.getId()).build());\n        repository.add(aSupervisor().withProcessDefinitionId(8L).withUserId(userId).build());\n\n        SProcessInstance processInstanceWithFailedFlowNode = new SProcessInstance(\"process2\", 10L);\n        processInstanceWithFailedFlowNode.setId(2);\n        repository.add(processInstanceWithFailedFlowNode);\n        repository.add(buildFailedGateway(852, processInstanceWithFailedFlowNode.getId()));\n        repository.add(buildFailedProcessInstance(3, 11L));\n\n        processInstanceWithFailedFlowNode = new SProcessInstance(\"process2\", 15L);\n        processInstanceWithFailedFlowNode.setId(4);\n        repository.add(processInstanceWithFailedFlowNode);\n        repository.add(buildFailedGateway(853, processInstanceWithFailedFlowNode.getId()));\n        repository.add(buildStartedProcessInstance(5, 15L));\n        repository.add(aSupervisor().withProcessDefinitionId(15L).withUserId(userId).build());\n\n        // When\n        final List<SProcessInstance> sProcessInstanceFailedList = repository\n                .searchFailedSProcessInstanceSupervisedBy(userId);\n\n        // Then\n        assertThat(sProcessInstanceFailedList).hasSize(2).extracting(\"id\").contains(1L, 4L);\n\n    }\n\n    private SGatewayInstance buildFailedGateway(final long gatewayId, final long parentProcessInstanceId) {\n        final SGatewayInstance sGatewayInstance = new SGatewayInstance();\n        sGatewayInstance.setId(gatewayId);\n        sGatewayInstance.setStateId(3);\n        sGatewayInstance.setLogicalGroup(3, parentProcessInstanceId);\n        return sGatewayInstance;\n    }\n\n    private SProcessInstance buildFailedProcessInstance(final long processInstanceId) {\n        return buildFailedProcessInstance(processInstanceId, 9L);\n    }\n\n    private SProcessInstance buildStartedProcessInstance(final long processInstanceId, final long processDefinitionId) {\n        return SProcessInstance.builder().name(\"process\" + processInstanceId).processDefinitionId(processDefinitionId)\n                .id(processInstanceId).stateId(STARTED.getId()).build();\n    }\n\n    private SProcessInstance buildFailedProcessInstance(final long processInstanceId, final long processDefinitionId) {\n        return SProcessInstance.builder().name(\"process\" + processInstanceId).processDefinitionId(processDefinitionId)\n                .id(processInstanceId)\n                .stateId(7).build();\n    }\n\n    @Test\n    public void should_get_processInstances_by_callertype_and_stateCategory() {\n        SProcessInstance sProcessInstance = buildStartedProcessInstance(12L, 102L);\n        sProcessInstance.setCallerType(RECEIVE_TASK);\n        repository.add(sProcessInstance);\n        repository.flush();\n        final String stateCategory = jdbcTemplate.queryForObject(\"select stateCategory from process_instance\",\n                String.class);\n        final String callerType = jdbcTemplate.queryForObject(\"select callerType from process_instance\", String.class);\n        assertThat(stateCategory).isEqualTo(\"NORMAL\");\n        assertThat(callerType).isEqualTo(\"RECEIVE_TASK\");\n    }\n\n    @Test\n    public void should_save_and_get_multi_business_data_reference_for_process() {\n        SProcessMultiRefBusinessDataInstance multiRefBusinessDataInstance = new SProcessMultiRefBusinessDataInstance();\n        multiRefBusinessDataInstance.setDataIds(Arrays.asList(23L, 25L, 27L));\n        multiRefBusinessDataInstance.setProcessInstanceId(PROCESS_INSTANCE_ID);\n        multiRefBusinessDataInstance.setName(\"myMultiProcData\");\n        multiRefBusinessDataInstance.setDataClassName(\"someDataClassName\");\n        multiRefBusinessDataInstance = repository.add(multiRefBusinessDataInstance);\n        repository.flush();\n\n        PersistentObject multiRefBusinessData = repository.selectOne(\"getSRefBusinessDataInstance\",\n                pair(\"processInstanceId\", PROCESS_INSTANCE_ID), pair(\"name\", \"myMultiProcData\"));\n        Map<String, Object> multiRefBusinessDataAsMap = jdbcTemplate\n                .queryForObject(\n                        \"SELECT ID, KIND, NAME, DATA_CLASSNAME, DATA_ID, PROC_INST_ID, FN_INST_ID FROM ref_biz_data_inst WHERE proc_inst_id=\"\n                                + PROCESS_INSTANCE_ID\n                                + \" AND name='myMultiProcData'\",\n                        new JdbcRowMapper(\"ID\", \"DATA_ID\", \"PROC_INST_ID\", \"FN_INST_ID\"));\n        List<Map<String, Object>> dataIds = jdbcTemplate.query(\n                \"SELECT ID, IDX, DATA_ID FROM multi_biz_data WHERE id=\" + multiRefBusinessDataInstance.getId(),\n                new JdbcRowMapper(\"ID\", \"IDX\", \"DATA_ID\"));\n\n        assertThat(((SProcessMultiRefBusinessDataInstance) multiRefBusinessData).getDataIds())\n                .isEqualTo(Arrays.asList(23L, 25L, 27L));\n        assertThat(multiRefBusinessData).isEqualTo(multiRefBusinessDataInstance);\n        assertThat(multiRefBusinessDataAsMap).containsOnly(\n                entry(\"ID\", multiRefBusinessDataInstance.getId()),\n                entry(\"KIND\", \"proc_multi_ref\"),\n                entry(\"NAME\", \"myMultiProcData\"),\n                entry(\"DATA_CLASSNAME\", \"someDataClassName\"),\n                entry(\"DATA_ID\", null),\n                entry(\"PROC_INST_ID\", PROCESS_INSTANCE_ID),\n                entry(\"FN_INST_ID\", null));\n        assertThat(dataIds).containsExactly(\n                mapOf(pair(\"ID\", multiRefBusinessDataInstance.getId()), pair(\"IDX\", 0L), pair(\"DATA_ID\", 23L)),\n                mapOf(pair(\"ID\", multiRefBusinessDataInstance.getId()), pair(\"IDX\", 1L), pair(\"DATA_ID\", 25L)),\n                mapOf(pair(\"ID\", multiRefBusinessDataInstance.getId()), pair(\"IDX\", 2L), pair(\"DATA_ID\", 27L)));\n    }\n\n    @Test\n    public void should_save_and_get_single_business_data_reference_for_process() {\n        SProcessSimpleRefBusinessDataInstance singleRef = new SProcessSimpleRefBusinessDataInstance();\n        singleRef.setDataId(43L);\n        singleRef.setProcessInstanceId(PROCESS_INSTANCE_ID);\n        singleRef.setName(\"mySingleData\");\n        singleRef.setDataClassName(\"someDataClassName\");\n        singleRef = repository.add(singleRef);\n        repository.flush();\n\n        PersistentObject singleRefFromQuery = repository.selectOne(\"getSRefBusinessDataInstance\",\n                pair(\"processInstanceId\", PROCESS_INSTANCE_ID), pair(\"name\", \"mySingleData\"));\n        Map<String, Object> multiRefBusinessDataAsMap = jdbcTemplate\n                .queryForObject(\n                        \"SELECT ID, KIND, NAME, DATA_CLASSNAME, DATA_ID, PROC_INST_ID, FN_INST_ID FROM ref_biz_data_inst WHERE proc_inst_id=\"\n                                + PROCESS_INSTANCE_ID + \" AND name='mySingleData'\",\n                        new JdbcRowMapper(\"ID\", \"DATA_ID\", \"PROC_INST_ID\", \"FN_INST_ID\"));\n        assertThat(singleRefFromQuery).isEqualTo(singleRef);\n        assertThat(multiRefBusinessDataAsMap).containsOnly(\n                entry(\"ID\", singleRef.getId()),\n                entry(\"KIND\", \"proc_simple_ref\"),\n                entry(\"NAME\", \"mySingleData\"),\n                entry(\"DATA_CLASSNAME\", \"someDataClassName\"),\n                entry(\"DATA_ID\", 43L),\n                entry(\"PROC_INST_ID\", PROCESS_INSTANCE_ID),\n                entry(\"FN_INST_ID\", null));\n    }\n\n    @Test\n    public void should_save_and_get_single_business_data_reference_for_flow_node() {\n        SFlowNodeSimpleRefBusinessDataInstance singleRef = new SFlowNodeSimpleRefBusinessDataInstance();\n        singleRef.setDataId(43L);\n        singleRef.setFlowNodeInstanceId(FLOW_NODE_INSTANCE_ID);\n        singleRef.setName(\"mySingleData\");\n        singleRef.setDataClassName(\"someDataClassName\");\n        singleRef = repository.add(singleRef);\n        repository\n                .flush();\n\n        PersistentObject singleRefFromQuery = repository.selectOne(\"getSFlowNodeRefBusinessDataInstance\",\n                pair(\"flowNodeInstanceId\", FLOW_NODE_INSTANCE_ID), pair(\"name\", \"mySingleData\"));\n        Map<String, Object> multiRefBusinessDataAsMap = jdbcTemplate\n                .queryForObject(\n                        \"SELECT ID, KIND, NAME, DATA_CLASSNAME, DATA_ID, PROC_INST_ID, FN_INST_ID FROM ref_biz_data_inst WHERE fn_inst_id=\"\n                                + FLOW_NODE_INSTANCE_ID\n                                + \" AND name='mySingleData'\",\n                        new JdbcRowMapper(\"ID\", \"DATA_ID\", \"PROC_INST_ID\", \"FN_INST_ID\"));\n        assertThat(singleRefFromQuery).isEqualTo(singleRef);\n        assertThat(multiRefBusinessDataAsMap).containsOnly(\n                entry(\"ID\", singleRef.getId()),\n                entry(\"KIND\", \"fn_simple_ref\"),\n                entry(\"NAME\", \"mySingleData\"),\n                entry(\"DATA_CLASSNAME\", \"someDataClassName\"),\n                entry(\"DATA_ID\", 43L),\n                entry(\"PROC_INST_ID\", null),\n                entry(\"FN_INST_ID\", FLOW_NODE_INSTANCE_ID));\n    }\n\n    @Test\n    public void should_return_process_instance_ids_to_restart_that_older_than_max_last_update_date() {\n        long now = System.currentTimeMillis();\n\n        SProcessInstance oldProcess1 = SProcessInstance.builder().name(\"oldProcess1\").stateId(INITIALIZING.getId())\n                .lastUpdate(now().minusSeconds(60).toEpochMilli()).build();\n        SProcessInstance oldProcess2 = SProcessInstance.builder().name(\"oldProcess2\").stateId(INITIALIZING.getId())\n                .lastUpdate(now().minusSeconds(70).toEpochMilli()).build();\n        SProcessInstance recentProcess1 = SProcessInstance.builder().name(\"recentProcess1\")\n                .stateId(INITIALIZING.getId()).lastUpdate(now().minusSeconds(10).toEpochMilli()).build();\n        SProcessInstance recentProcess2 = SProcessInstance.builder().name(\"recentProcess2\")\n                .stateId(INITIALIZING.getId()).lastUpdate(now).build();\n\n        repository.add(oldProcess1, oldProcess2, recentProcess1, recentProcess2);\n        List<Long> processInstanceIdsToRestart = repository\n                .getProcessInstanceIdsToRecover(now().minusSeconds(30).toEpochMilli());\n\n        assertThat(processInstanceIdsToRestart).containsOnly(oldProcess1.getId(), oldProcess2.getId());\n    }\n\n    @Test\n    public void should_return_process_instance_ids_to_restart_are_in_the_expected_states() {\n\n        SProcessInstance process1 = SProcessInstance.builder().id(1).name(\"process1\").stateId(INITIALIZING.getId())\n                .build();\n        SProcessInstance process2 = SProcessInstance.builder().id(2).name(\"process2\").stateId(COMPLETING.getId())\n                .build();\n        SProcessInstance process3 = SProcessInstance.builder().id(3).name(\"process3\").stateId(COMPLETED.getId())\n                .build();\n        SProcessInstance process4 = SProcessInstance.builder().id(4).name(\"process4\").stateId(CANCELLED.getId())\n                .build();\n        SProcessInstance process5 = SProcessInstance.builder().id(5).name(\"process5\").stateId(ABORTED.getId()).build();\n        SProcessInstance process6 = SProcessInstance.builder().id(6).name(\"process6\").stateId(STARTED.getId()).build();\n        SProcessInstance process7 = SProcessInstance.builder().id(7).name(\"process7\").stateId(ERROR.getId()).build();\n        SProcessInstance process8 = SProcessInstance.builder().id(8).name(\"process8\").stateId(ABORTING.getId()).build();\n\n        repository.add(process1, process2, process3, process4, process5, process6, process7, process8);\n\n        List<Long> processInstanceIdsToRestart = repository.getProcessInstanceIdsToRecover(System.currentTimeMillis());\n\n        assertThat(processInstanceIdsToRestart).containsOnly(1L, 2L, 3L, 4L, 5L);\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/process/instance/model/RoleTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.test.persistence.builder.RoleBuilder.aRole;\n\nimport org.bonitasoft.engine.test.persistence.repository.RoleRepository;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.transaction.annotation.Transactional;\n\n/**\n * @author Danila Mazour\n */\n@RunWith(SpringRunner.class)\n@ContextConfiguration(locations = { \"/testContext.xml\" })\n@Transactional\npublic class RoleTest {\n\n    @Autowired\n    private RoleRepository repository;\n\n    @Test\n    public void should_get_role_by_name() {\n        repository.add(aRole().forRoleId(10L).forRoleName(\"MyRole10\").build());\n        repository.add(aRole().forRoleId(11L).forRoleName(\"MyRole11\").build());\n\n        assertThat(repository.getRoleByName(\"MyRole10\").getId()).isEqualTo(10L);\n        assertThat(repository.getRoleByName(\"MyRole11\").getId()).isEqualTo(11L);\n\n        assertThat(repository.getRoleByName(\"NonExistingRole\")).isNull();\n\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/process/instance/model/SADataInstanceQueriesTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.commons.Pair.pair;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.data.instance.model.SBlobDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SBooleanDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SDateDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SDoubleDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SFloatDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SIntegerDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SLongDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SLongTextDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SShortTextDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SXMLDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SXMLObjectDataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.SABlobDataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.SABooleanDataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.SADataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.SADateDataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.SADoubleDataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.SAFloatDataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.SAIntegerDataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.SALongDataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.SALongTextDataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.SAShortTextDataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.SAXMLObjectDataInstance;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.test.persistence.jdbc.JdbcRowMapper;\nimport org.bonitasoft.engine.test.persistence.repository.SADataInstanceRepository;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.transaction.annotation.Transactional;\n\n@RunWith(SpringRunner.class)\n@ContextConfiguration(locations = { \"/testContext.xml\" })\n@Transactional\npublic class SADataInstanceQueriesTest {\n\n    private static final long CONTAINER_ID = 12345L;\n    @Autowired\n    private SADataInstanceRepository repository;\n    @Autowired\n    private JdbcTemplate jdbcTemplate;\n\n    private void addSADataInstance(final int id, final String name, final int containerId, final String value,\n            final long archiveDate, final long sourceObjectId) {\n        repository.add(SAShortTextDataInstance.builder()\n                .id(id)\n                .name(name)\n                .className(String.class.getName())\n                .containerId(containerId)\n                .containerType(\"PROCESS_INSTANCE\")\n                .value(value)\n                .archiveDate(archiveDate)\n                .sourceObjectId(sourceObjectId).build());\n    }\n\n    private SDataInstance.SDataInstanceBuilder<?, ?> fillCommonValues(\n            SDataInstance.SDataInstanceBuilder<?, ?> builder) {\n        return builder.className(\"theClassName\")\n                .containerId(CONTAINER_ID)\n                .containerType(\"ContainerType\")\n                .description(\"My data\");\n    }\n\n    private SADataInstance.SADataInstanceBuilder<?, ?> fillCommonValues(\n            SADataInstance.SADataInstanceBuilder<?, ?> builder) {\n        return builder.className(\"theClassName\")\n                .containerId(CONTAINER_ID)\n                .containerType(\"ContainerType\")\n                .description(\"My data\");\n    }\n\n    protected Map<String, Object> getDataUsingJDBC(SDataInstance dataInstance) {\n        return jdbcTemplate.queryForObject(\"SELECT * FROM data_instance where id = \" + dataInstance.getId(),\n                new JdbcRowMapper(List.of(\"LONGVALUE\"),\n                        List.of(\"BOOLEANVALUE\"),\n                        List.of(\"DOUBLEVALUE\")));\n    }\n\n    protected Map<String, Object> getDataUsingJDBC(SADataInstance dataInstance) {\n        return jdbcTemplate.queryForObject(\"SELECT * FROM arch_data_instance where id = \" + dataInstance.getId(),\n                new JdbcRowMapper(List.of(\"LONGVALUE\"),\n                        List.of(\"BOOLEANVALUE\"),\n                        List.of(\"DOUBLEVALUE\")));\n    }\n\n    protected PersistentObject getDataInstance(String dataName) {\n        return repository.selectOne(\"getDataInstancesByNameAndContainer\", pair(\"name\", dataName),\n                pair(\"containerId\", CONTAINER_ID), pair(\"containerType\", \"ContainerType\"));\n    }\n\n    protected PersistentObject getSADataInstance(String dataName) {\n        return repository.selectOne(\"getLastSADataInstanceByContainer\", pair(\"dataName\", dataName),\n                pair(\"containerId\", CONTAINER_ID), pair(\"containerType\", \"ContainerType\"));\n    }\n\n    @Test\n    public void should_save_and_get_SXMLDataInstance() {\n        SDataInstance dataInstance = repository.add(fillCommonValues(SXMLDataInstance.builder()\n                .name(\"myXmlData\")\n                .element(\"theElement\")\n                .namespace(\"theNameSpace\")\n                .value(\"<xml></xml>\")).build());\n\n        PersistentObject persistentObject = getDataInstance(\"myXmlData\");\n        Map<String, Object> dataAsMap = getDataUsingJDBC(dataInstance);\n\n        assertThat(persistentObject).isEqualTo(dataInstance);\n        assertThat(persistentObject).isInstanceOf(SXMLDataInstance.class);\n        assertThat(dataAsMap).containsEntry(\"CLOBVALUE\", \"<xml></xml>\");\n        assertThat(dataAsMap).containsEntry(\"DISCRIMINANT\", \"SXMLDataInstanceImpl\");\n    }\n\n    @Test\n    public void should_save_and_get_SShortTextDataInstance() {\n        SDataInstance dataInstance = repository.add(fillCommonValues(SShortTextDataInstance.builder()\n                .name(\"myTextData\")\n                .value(\"shortText\")).build());\n\n        PersistentObject persistentObject = getDataInstance(\"myTextData\");\n        Map<String, Object> dataAsMap = getDataUsingJDBC(dataInstance);\n\n        assertThat(persistentObject).isEqualTo(dataInstance);\n        assertThat(persistentObject).isInstanceOf(SShortTextDataInstance.class);\n        assertThat(dataAsMap).containsEntry(\"SHORTTEXTVALUE\", \"shortText\");\n        assertThat(dataAsMap).containsEntry(\"DISCRIMINANT\", \"SShortTextDataInstanceImpl\");\n    }\n\n    @Test\n    public void should_save_and_get_SSShortTextDataInstance() {\n        SADataInstance dataInstance = repository.add(fillCommonValues(SAShortTextDataInstance.builder()\n                .name(\"myTextData\")\n                .value(\"shortText\")).build());\n\n        PersistentObject persistentObject = getSADataInstance(\"myTextData\");\n        Map<String, Object> dataAsMap = getDataUsingJDBC(dataInstance);\n\n        assertThat(persistentObject).isEqualTo(dataInstance);\n        assertThat(persistentObject).isInstanceOf(SAShortTextDataInstance.class);\n        assertThat(dataAsMap).containsEntry(\"SHORTTEXTVALUE\", \"shortText\");\n        assertThat(dataAsMap).containsEntry(\"DISCRIMINANT\", \"SAShortTextDataInstanceImpl\");\n    }\n\n    @Test\n    public void should_save_and_get_SLongDataInstance() {\n        SDataInstance dataInstance = repository.add(fillCommonValues(SLongDataInstance.builder()\n                .name(\"myLongData\")\n                .value(1234567890L)).build());\n\n        PersistentObject persistentObject = getDataInstance(\"myLongData\");\n        Map<String, Object> dataAsMap = getDataUsingJDBC(dataInstance);\n\n        assertThat(persistentObject).isEqualTo(dataInstance);\n        assertThat(persistentObject).isInstanceOf(SLongDataInstance.class);\n        assertThat(dataAsMap).containsEntry(\"LONGVALUE\", 1234567890L);\n        assertThat(dataAsMap).containsEntry(\"DISCRIMINANT\", \"SLongDataInstanceImpl\");\n    }\n\n    @Test\n    public void should_save_and_get_SDoubleDataInstance() {\n        SDataInstance dataInstance = repository.add(fillCommonValues(SDoubleDataInstance.builder()\n                .name(\"myDoubleData\")\n                .value(1234567890.0)).build());\n\n        PersistentObject persistentObject = getDataInstance(\"myDoubleData\");\n        Map<String, Object> dataAsMap = getDataUsingJDBC(dataInstance);\n\n        assertThat(persistentObject).isEqualTo(dataInstance);\n        assertThat(persistentObject).isInstanceOf(SDoubleDataInstance.class);\n        assertThat(dataAsMap).containsEntry(\"DOUBLEVALUE\", 1234567890.0d);\n        assertThat(dataAsMap).containsEntry(\"DISCRIMINANT\", \"SDoubleDataInstanceImpl\");\n    }\n\n    @Test\n    public void should_save_and_get_SXMLObjectDataInstance() {\n        SDataInstance dataInstance = repository.add(fillCommonValues(SXMLObjectDataInstance.builder()\n                .name(\"myXmlData\")\n                .value(\"<string>MyString<string>\")).build());\n\n        PersistentObject persistentObject = getDataInstance(\"myXmlData\");\n        Map<String, Object> dataAsMap = getDataUsingJDBC(dataInstance);\n\n        assertThat(persistentObject).isEqualTo(dataInstance);\n        assertThat(persistentObject).isInstanceOf(SXMLObjectDataInstance.class);\n        assertThat(dataAsMap).containsEntry(\"CLOBVALUE\", \"<string>MyString<string>\");\n        assertThat(((SXMLObjectDataInstance) persistentObject).getValue()).isEqualTo(\"MyString\");\n        assertThat(dataAsMap).containsEntry(\"DISCRIMINANT\", \"SXMLObjectDataInstanceImpl\");\n    }\n\n    @Test\n    public void should_save_and_get_SIntegerDataInstance() {\n        SDataInstance dataInstance = repository.add(fillCommonValues(SIntegerDataInstance.builder()\n                .name(\"myIntData\")\n                .value(1234567890)).build());\n\n        PersistentObject persistentObject = getDataInstance(\"myIntData\");\n        Map<String, Object> dataAsMap = getDataUsingJDBC(dataInstance);\n\n        assertThat(persistentObject).isEqualTo(dataInstance);\n        assertThat(persistentObject).isInstanceOf(SIntegerDataInstance.class);\n        assertThat(dataAsMap).containsEntry(\"INTVALUE\", 1234567890);\n        assertThat(dataAsMap).containsEntry(\"DISCRIMINANT\", \"SIntegerDataInstanceImpl\");\n    }\n\n    @Test\n    public void should_save_and_get_SDateDataInstance() {\n        Date date = new Date();\n        SDataInstance dataInstance = repository.add(fillCommonValues(SDateDataInstance.builder()\n                .name(\"myDateData\")\n                .value(date)).build());\n\n        PersistentObject persistentObject = getDataInstance(\"myDateData\");\n        Map<String, Object> dataAsMap = getDataUsingJDBC(dataInstance);\n\n        assertThat(persistentObject).isEqualTo(dataInstance);\n        assertThat(persistentObject).isInstanceOf(SDateDataInstance.class);\n        assertThat(dataAsMap).containsEntry(\"LONGVALUE\", date.getTime());\n        assertThat(dataAsMap).containsEntry(\"DISCRIMINANT\", \"SDateDataInstanceImpl\");\n    }\n\n    @Test\n    public void should_save_and_get_SLongTextDataInstance() {\n        Date date = new Date();\n        SDataInstance dataInstance = repository.add(fillCommonValues(SLongTextDataInstance.builder()\n                .name(\"myTextData\")\n                .value(\"lonnnnnnnnng text\")).build());\n\n        PersistentObject persistentObject = getDataInstance(\"myTextData\");\n        Map<String, Object> dataAsMap = getDataUsingJDBC(dataInstance);\n\n        assertThat(persistentObject).isEqualTo(dataInstance);\n        assertThat(persistentObject).isInstanceOf(SLongTextDataInstance.class);\n        assertThat(dataAsMap).containsEntry(\"CLOBVALUE\", \"lonnnnnnnnng text\");\n        assertThat(dataAsMap).containsEntry(\"DISCRIMINANT\", \"SLongTextDataInstanceImpl\");\n    }\n\n    @Test\n    public void should_save_and_get_SFloatDataInstance() {\n        SDataInstance dataInstance = repository.add(fillCommonValues(SFloatDataInstance.builder()\n                .name(\"myFloatData\")\n                .value(1f)).build());\n\n        PersistentObject persistentObject = getDataInstance(\"myFloatData\");\n        Map<String, Object> dataAsMap = getDataUsingJDBC(dataInstance);\n\n        assertThat(persistentObject).isEqualTo(dataInstance);\n        assertThat(persistentObject).isInstanceOf(SFloatDataInstance.class);\n        assertThat(((Number) dataAsMap.get(\"FLOATVALUE\")).floatValue()).isEqualTo(1.0f);\n        assertThat(dataAsMap).containsEntry(\"DISCRIMINANT\", \"SFloatDataInstanceImpl\");\n    }\n\n    @Test\n    public void should_save_and_get_SBlobDataInstance() {\n        SDataInstance dataInstance = repository.add(fillCommonValues(SBlobDataInstance.builder()\n                .name(\"myBlob\")\n                .value(new byte[] { 1, 2, 3 })).build());\n\n        PersistentObject persistentObject = getDataInstance(\"myBlob\");\n        Map<String, Object> dataAsMap = getDataUsingJDBC(dataInstance);\n\n        assertThat(persistentObject).isEqualTo(dataInstance);\n        assertThat(persistentObject).isInstanceOf(SBlobDataInstance.class);\n        assertThat(dataAsMap.get(\"BLOBVALUE\")).isEqualTo(new byte[] { 1, 2, 3 });\n        assertThat(dataAsMap).containsEntry(\"DISCRIMINANT\", \"SBlobDataInstanceImpl\");\n    }\n\n    @Test\n    public void should_save_and_get_SBooleanDataInstance() {\n        SDataInstance dataInstance = repository.add(fillCommonValues(SBooleanDataInstance.builder()\n                .name(\"myBoolean\")\n                .value(true)).build());\n\n        PersistentObject persistentObject = getDataInstance(\"myBoolean\");\n        Map<String, Object> dataAsMap = getDataUsingJDBC(dataInstance);\n\n        assertThat(persistentObject).isEqualTo(dataInstance);\n        assertThat(persistentObject).isInstanceOf(SBooleanDataInstance.class);\n        assertThat(dataAsMap.get(\"BOOLEANVALUE\")).isEqualTo(true);\n        assertThat(dataAsMap).containsEntry(\"DISCRIMINANT\", \"SBooleanDataInstanceImpl\");\n    }\n\n    @Test\n    public void should_save_and_get_SALongDataInstance() {\n        SADataInstance dataInstance = repository.add(fillCommonValues(SALongDataInstance.builder()\n                .name(\"myLongData\")\n                .value(1234567890L)).build());\n\n        PersistentObject persistentObject = getSADataInstance(\"myLongData\");\n        Map<String, Object> dataAsMap = getDataUsingJDBC(dataInstance);\n\n        assertThat(persistentObject).isEqualTo(dataInstance);\n        assertThat(persistentObject).isInstanceOf(SALongDataInstance.class);\n        assertThat(dataAsMap).containsEntry(\"LONGVALUE\", 1234567890L);\n        assertThat(dataAsMap).containsEntry(\"DISCRIMINANT\", \"SALongDataInstanceImpl\");\n    }\n\n    @Test\n    public void should_save_and_get_SADoubleDataInstance() {\n        SADataInstance dataInstance = repository.add(fillCommonValues(SADoubleDataInstance.builder()\n                .name(\"myDoubleData\")\n                .value(1234567890.0)).build());\n\n        PersistentObject persistentObject = getSADataInstance(\"myDoubleData\");\n        Map<String, Object> dataAsMap = getDataUsingJDBC(dataInstance);\n\n        assertThat(persistentObject).isEqualTo(dataInstance);\n        assertThat(persistentObject).isInstanceOf(SADoubleDataInstance.class);\n        assertThat(dataAsMap).containsEntry(\"DOUBLEVALUE\", 1234567890.0d);\n        assertThat(dataAsMap).containsEntry(\"DISCRIMINANT\", \"SADoubleDataInstanceImpl\");\n    }\n\n    @Test\n    public void should_save_and_get_SAXMLObjectDataInstance() {\n        SADataInstance dataInstance = repository.add(fillCommonValues(SAXMLObjectDataInstance.builder()\n                .name(\"myXmlData\")\n                .value(\"<string>MyString<string>\")).build());\n\n        PersistentObject persistentObject = getSADataInstance(\"myXmlData\");\n        Map<String, Object> dataAsMap = getDataUsingJDBC(dataInstance);\n\n        assertThat(persistentObject).isEqualTo(dataInstance);\n        assertThat(persistentObject).isInstanceOf(SAXMLObjectDataInstance.class);\n        assertThat(dataAsMap).containsEntry(\"CLOBVALUE\", \"<string>MyString<string>\");\n        assertThat(((SAXMLObjectDataInstance) persistentObject).getValue()).isEqualTo(\"MyString\");\n        assertThat(dataAsMap).containsEntry(\"DISCRIMINANT\", \"SAXMLObjectDataInstanceImpl\");\n    }\n\n    @Test\n    public void should_save_and_get_SAIntegerDataInstance() {\n        SADataInstance dataInstance = repository.add(fillCommonValues(SAIntegerDataInstance.builder()\n                .name(\"myIntData\")\n                .value(1234567890)).build());\n\n        PersistentObject persistentObject = getSADataInstance(\"myIntData\");\n        Map<String, Object> dataAsMap = getDataUsingJDBC(dataInstance);\n\n        assertThat(persistentObject).isEqualTo(dataInstance);\n        assertThat(persistentObject).isInstanceOf(SAIntegerDataInstance.class);\n        assertThat(dataAsMap).containsEntry(\"INTVALUE\", 1234567890);\n        assertThat(dataAsMap).containsEntry(\"DISCRIMINANT\", \"SAIntegerDataInstanceImpl\");\n    }\n\n    @Test\n    public void should_save_and_get_SADateDataInstance() {\n        Date date = new Date();\n        SADataInstance dataInstance = repository.add(fillCommonValues(SADateDataInstance.builder()\n                .name(\"myDateData\")\n                .value(date)).build());\n\n        PersistentObject persistentObject = getSADataInstance(\"myDateData\");\n        Map<String, Object> dataAsMap = getDataUsingJDBC(dataInstance);\n\n        assertThat(persistentObject).isEqualTo(dataInstance);\n        assertThat(persistentObject).isInstanceOf(SADateDataInstance.class);\n        assertThat(dataAsMap).containsEntry(\"LONGVALUE\", date.getTime());\n        assertThat(dataAsMap).containsEntry(\"DISCRIMINANT\", \"SADateDataInstanceImpl\");\n    }\n\n    @Test\n    public void should_save_and_get_SALongTextDataInstance() {\n        SADataInstance dataInstance = repository.add(fillCommonValues(SALongTextDataInstance.builder()\n                .name(\"myTextData\")\n                .value(\"lonnnnnnnnng text\")).build());\n\n        PersistentObject persistentObject = getSADataInstance(\"myTextData\");\n        Map<String, Object> dataAsMap = getDataUsingJDBC(dataInstance);\n\n        assertThat(persistentObject).isEqualTo(dataInstance);\n        assertThat(persistentObject).isInstanceOf(SALongTextDataInstance.class);\n        assertThat(dataAsMap).containsEntry(\"CLOBVALUE\", \"lonnnnnnnnng text\");\n        assertThat(dataAsMap).containsEntry(\"DISCRIMINANT\", \"SALongTextDataInstanceImpl\");\n    }\n\n    @Test\n    public void should_save_and_get_SAFloatDataInstance() {\n        SADataInstance dataInstance = repository.add(fillCommonValues(SAFloatDataInstance.builder()\n                .name(\"myFloatData\")\n                .value(1f)).build());\n\n        PersistentObject persistentObject = getSADataInstance(\"myFloatData\");\n        Map<String, Object> dataAsMap = getDataUsingJDBC(dataInstance);\n\n        assertThat(persistentObject).isEqualTo(dataInstance);\n        assertThat(persistentObject).isInstanceOf(SAFloatDataInstance.class);\n        assertThat(((Number) dataAsMap.get(\"FLOATVALUE\")).floatValue()).isEqualTo(1.0f);\n        assertThat(dataAsMap).containsEntry(\"DISCRIMINANT\", \"SAFloatDataInstanceImpl\");\n    }\n\n    @Test\n    public void should_save_and_get_SABlobDataInstance() {\n        SADataInstance dataInstance = repository.add(fillCommonValues(SABlobDataInstance.builder()\n                .name(\"myBlob\")\n                .value(new byte[] { 1, 2, 3 })).build());\n\n        PersistentObject persistentObject = getSADataInstance(\"myBlob\");\n        Map<String, Object> dataAsMap = getDataUsingJDBC(dataInstance);\n\n        assertThat(persistentObject).isEqualTo(dataInstance);\n        assertThat(persistentObject).isInstanceOf(SABlobDataInstance.class);\n        assertThat(dataAsMap.get(\"BLOBVALUE\")).isEqualTo(new byte[] { 1, 2, 3 });\n        assertThat(dataAsMap).containsEntry(\"DISCRIMINANT\", \"SABlobDataInstanceImpl\");\n    }\n\n    @Test\n    public void should_save_and_get_SABooleanDataInstance() {\n        SADataInstance dataInstance = repository.add(fillCommonValues(SABooleanDataInstance.builder()\n                .name(\"myBoolean\")\n                .value(true)).build());\n\n        PersistentObject persistentObject = getSADataInstance(\"myBoolean\");\n        Map<String, Object> dataAsMap = getDataUsingJDBC(dataInstance);\n\n        assertThat(persistentObject).isEqualTo(dataInstance);\n        assertThat(persistentObject).isInstanceOf(SABooleanDataInstance.class);\n        assertThat(dataAsMap.get(\"BOOLEANVALUE\")).isEqualTo(true);\n        assertThat(dataAsMap).containsEntry(\"DISCRIMINANT\", \"SABooleanDataInstanceImpl\");\n    }\n\n    @Test\n    public void getSADataInstancesByDataInstanceIdAndArchiveDate_should_return_an_empty_list_if_not_data_defined() {\n        final List<Long> dataInstanceIds = new ArrayList<>();\n        dataInstanceIds.add(4L);\n        final long time = System.currentTimeMillis();\n\n        final List<SADataInstance> dataInstances = repository\n                .getSADataInstancesByDataInstanceIdAndArchiveDate(dataInstanceIds, time);\n\n        assertThat(dataInstances).isEmpty();\n    }\n\n    @Test\n    public void getSADataInstancesByDataInstanceIdAndArchiveDate_should_return_an_empty_list_if_not_matching_identifiers() {\n        addSADataInstance(1025244, \"identifiant\", 41008, null, 1411051738348L, 205093L);\n        final List<Long> dataInstanceIds = new ArrayList<>();\n        dataInstanceIds.add(4L);\n\n        final List<SADataInstance> dataInstances = repository\n                .getSADataInstancesByDataInstanceIdAndArchiveDate(dataInstanceIds, 1411051738348L);\n\n        assertThat(dataInstances).isEmpty();\n    }\n\n    @Test\n    public void getSADataInstancesByDataInstanceIdAndArchiveDate_should_return_an_empty_list_if_not_matching_time() {\n        final long archiveDate = 1411051738348L;\n        addSADataInstance(1025244, \"identifiant\", 41008, null, archiveDate, 205093L);\n        addSADataInstance(1025259, \"identifiant\", 41008, \"matti\", archiveDate + 1500, 205093L);\n\n        final List<Long> dataInstanceIds = new ArrayList<>();\n        dataInstanceIds.add(205093L);\n\n        final long time = 0L;\n        final List<SADataInstance> dataInstances = repository\n                .getSADataInstancesByDataInstanceIdAndArchiveDate(dataInstanceIds, time);\n        assertThat(dataInstances).isEmpty();\n    }\n\n    @Test\n    public void getSADataInstancesByDataInstanceIdAndArchiveDate_should_get_the_latest_upate_of_all_data_updates() {\n        final long archiveDate = 1411051738348L;\n        addSADataInstance(1025244, \"identifiant\", 41008, null, archiveDate, 205093L);\n        addSADataInstance(1025259, \"identifiant\", 41008, \"matti\", archiveDate + 1500, 205093L);\n        addSADataInstance(1025356, \"identifiant\", 41008, \"hannu\", archiveDate + 2500, 205093L);\n\n        final List<Long> dataInstanceIds = new ArrayList<>();\n        dataInstanceIds.add(205093L);\n\n        final List<SADataInstance> dataInstances = repository\n                .getSADataInstancesByDataInstanceIdAndArchiveDate(dataInstanceIds, archiveDate + 5000);\n        assertThat(dataInstances).hasSize(1);\n        assertThat(dataInstances.get(0).getId()).isEqualTo(1025356);\n    }\n\n    @Test\n    public void getSADataInstancesByDataInstanceIdAndArchiveDate_should_get_the_latest_upate_of_a_frame_of_data_updates() {\n        final long archiveDate = 1411051738348L;\n        addSADataInstance(1025244, \"identifiant\", 41008, null, archiveDate, 205093L);\n        addSADataInstance(1025259, \"identifiant\", 41008, \"matti\", archiveDate + 1500, 205093L);\n        addSADataInstance(1025356, \"identifiant\", 41008, \"hannu\", archiveDate + 2500, 205093L);\n\n        final List<Long> dataInstanceIds = new ArrayList<>();\n        dataInstanceIds.add(205093L);\n\n        final List<SADataInstance> dataInstances = repository\n                .getSADataInstancesByDataInstanceIdAndArchiveDate(dataInstanceIds, archiveDate + 2000);\n        assertThat(dataInstances).hasSize(1);\n        assertThat(dataInstances.get(0).getId()).isEqualTo(1025259);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/process/instance/model/SupervisorQueriesTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.test.persistence.builder.UserBuilder.aUser;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.identity.model.SRole;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor;\nimport org.bonitasoft.engine.test.persistence.repository.SupervisorRepository;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.transaction.annotation.Transactional;\n\n@RunWith(SpringRunner.class)\n@ContextConfiguration(locations = { \"/testContext.xml\" })\n@Transactional\npublic class SupervisorQueriesTest {\n\n    private static final long supervisorId = 1;\n\n    private static final long processDefId = 3;\n\n    private static final long userId = 4;\n\n    private static final long groupId = 5;\n\n    private static final long roleId = 6;\n\n    @Autowired\n    private SupervisorRepository repository;\n\n    @Test\n    public void searchSProcessSupervisorWithSUserSGroupSRole_should_return_supervisors_mapped_through_user() {\n        SUser user = aUser().withId(userId).build();\n        repository.add(user);\n        final SRole sRole = new SRole();\n        sRole.setId(roleId);\n        repository.add(sRole);\n        final SGroup sGroup = new SGroup();\n        sGroup.setId(groupId);\n        repository.add(sGroup);\n        final SProcessSupervisor expectedSProcessSupervisor = repository\n                .add(new SProcessSupervisor(supervisorId, processDefId, userId, -1, -1));\n        repository.add(new SProcessSupervisor(2, processDefId, userId, groupId, roleId));\n\n        final List<Long> sProcessSupervisors = repository.searchSProcessSupervisorWithSUserSGroupSRole();\n\n        assertThat(sProcessSupervisors).containsOnly(expectedSProcessSupervisor.getId());\n    }\n\n    @Test\n    public void searchSProcessSupervisorWithSUserSGroupSRole_should_return_supervisors_mapped_through_group() {\n        SUser user = aUser().withId(userId).build();\n        repository.add(user);\n        final SRole sRole = new SRole();\n        sRole.setId(roleId);\n        repository.add(sRole);\n        final SGroup sGroup = new SGroup();\n        sGroup.setId(groupId);\n        repository.add(sGroup);\n        final SProcessSupervisor expectedSProcessSupervisor = repository\n                .add(new SProcessSupervisor(supervisorId, processDefId, 0, groupId, 0));\n        repository.add(new SProcessSupervisor(2, processDefId, userId, groupId, roleId));\n\n        final List<Long> sProcessSupervisors = repository.searchSProcessSupervisorWithSUserSGroupSRole();\n\n        assertThat(sProcessSupervisors).containsOnly(expectedSProcessSupervisor.getId());\n    }\n\n    @Test\n    public void searchSProcessSupervisorWithSUserSGroupSRole_should_return_supervisors_mapped_through_role() {\n        SUser user = aUser().withId(userId).build();\n        repository.add(user);\n        final SRole sRole = new SRole();\n        sRole.setId(roleId);\n        repository.add(sRole);\n        final SGroup sGroup = new SGroup();\n        sGroup.setId(groupId);\n        repository.add(sGroup);\n        final SProcessSupervisor expectedSProcessSupervisor = repository\n                .add(new SProcessSupervisor(supervisorId, processDefId, 0, 0, roleId));\n        repository.add(new SProcessSupervisor(2, processDefId, userId, groupId, roleId));\n\n        final List<Long> sProcessSupervisors = repository.searchSProcessSupervisorWithSUserSGroupSRole();\n\n        assertThat(sProcessSupervisors).containsOnly(expectedSProcessSupervisor.getId());\n    }\n\n    @Test\n    public void searchSProcessSupervisorWithSUserSGroupSRole_should_return_supervisors_mapped_through_group_and_role() {\n        SUser user = aUser().withId(userId).build();\n        repository.add(user);\n        final SRole sRole = new SRole();\n        sRole.setId(roleId);\n        repository.add(sRole);\n        final SGroup sGroup = new SGroup();\n        sGroup.setId(groupId);\n        repository.add(sGroup);\n        final SProcessSupervisor expectedSProcessSupervisor = repository\n                .add(new SProcessSupervisor(supervisorId, processDefId, 0, groupId,\n                        roleId));\n        repository.add(new SProcessSupervisor(2, processDefId, userId, groupId, roleId));\n\n        final List<Long> sProcessSupervisors = repository.searchSProcessSupervisorWithSUserSGroupSRole();\n\n        assertThat(sProcessSupervisors).containsOnly(expectedSProcessSupervisor.getId());\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/process/instance/model/UserMembershipTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.test.persistence.builder.GroupBuilder.aGroup;\nimport static org.bonitasoft.engine.test.persistence.builder.RoleBuilder.aRole;\nimport static org.bonitasoft.engine.test.persistence.builder.UserBuilder.aUser;\nimport static org.bonitasoft.engine.test.persistence.builder.UserMembershipBuilder.aUserMembership;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.identity.model.SRole;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.identity.model.SUserMembership;\nimport org.bonitasoft.engine.test.persistence.repository.UserMembershipRepository;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.transaction.annotation.Transactional;\n\n/**\n * @author Danila Mazour\n */\n@RunWith(SpringRunner.class)\n@ContextConfiguration(locations = { \"/testContext.xml\" })\n@Transactional\npublic class UserMembershipTest {\n\n    @Autowired\n    private UserMembershipRepository repository;\n\n    //Those tests currently verify that the queries returning UserMemberships correctly retrieve the groupParentPath when building the Usermembership objects\n\n    @Test\n    public void getUserMembershipsByGroup_should_fill_in_groupParentPath() {\n\n        SUser user = repository.add(aUser().withId(1L).withUserName(\"dummy username\").build());\n        SGroup group = repository\n                .add(aGroup().forGroupId(258L).forGroupName(\"dummy groupName\").forParentPath(\"bonita/devList\").build());\n        SRole role = repository.add(aRole().forRoleId(259L).forRoleName(\"dummy roleName\").build());\n        SUserMembership sUserMembership = aUserMembership().forUser(user).memberOf(group.getId(), role.getId()).build();\n        repository.add(sUserMembership);\n        List userMemberships = repository.getUserMembershipsByGroup(group);\n        assertThat(userMemberships).hasSize(1);\n        SUserMembership userMembership = (SUserMembership) userMemberships.get(0);\n        assertThat(userMembership.getGroupParentPath()).isEqualTo(\"bonita/devList\");\n\n    }\n\n    @Test\n    public void getUserMembershipsByRole_should_fill_in_groupParentPath() {\n\n        SUser user = repository.add(aUser().withId(1L).withUserName(\"dummy username\").build());\n        SGroup group = repository\n                .add(aGroup().forGroupId(258L).forGroupName(\"dummy groupName\").forParentPath(\"bonita/devList\").build());\n        SRole role = repository.add(aRole().forRoleId(259L).forRoleName(\"dummy roleName\").build());\n        SUserMembership sUserMembership = aUserMembership().forUser(user).memberOf(group.getId(), role.getId()).build();\n        repository.add(sUserMembership);\n        List userMemberships = repository.getUserMembershipsByRole(role);\n        assertThat(userMemberships).hasSize(1);\n        SUserMembership userMembership = (SUserMembership) userMemberships.get(0);\n        assertThat(userMembership.getGroupParentPath()).isEqualTo(\"bonita/devList\");\n\n    }\n\n    @Test\n    public void getUserMembershipsOfUser_should_fill_in_groupParentPath() {\n\n        SUser user = repository.add(aUser().withId(1L).withUserName(\"dummy username\").build());\n        SGroup group = repository\n                .add(aGroup().forGroupId(258L).forGroupName(\"dummy groupName\").forParentPath(\"bonita/devList\").build());\n        SRole role = repository.add(aRole().forRoleId(259L).forRoleName(\"dummy roleName\").build());\n        SUserMembership sUserMembership = aUserMembership().forUser(user).memberOf(group.getId(), role.getId()).build();\n        repository.add(sUserMembership);\n        List userMemberships = repository.getUserMembershipsOfUser(user.getId());\n        assertThat(userMemberships).hasSize(1);\n        SUserMembership userMembership = (SUserMembership) userMemberships.get(0);\n        assertThat(userMembership.getGroupParentPath()).isEqualTo(\"bonita/devList\");\n\n    }\n\n    @Test\n    public void getUserMembershipWithIds_should_fill_in_groupParentPath() {\n\n        SUser user = repository.add(aUser().withId(1L).withUserName(\"dummy username\").build());\n        SGroup group = repository\n                .add(aGroup().forGroupId(258L).forGroupName(\"dummy groupName\").forParentPath(\"bonita/devList\").build());\n        SRole role = repository.add(aRole().forRoleId(259L).forRoleName(\"dummy roleName\").build());\n        SUserMembership sUserMembership = aUserMembership().forUser(user).memberOf(group.getId(), role.getId()).build();\n        repository.add(sUserMembership);\n        List userMemberships = repository.getUserMembershipWithIds(role.getId(), group.getId(), user.getId());\n        assertThat(userMemberships).hasSize(1);\n        SUserMembership userMembership = (SUserMembership) userMemberships.get(0);\n        assertThat(userMembership.getGroupParentPath()).isEqualTo(\"bonita/devList\");\n\n    }\n\n    @Test\n    public void getUserMemberships_should_fill_transient_fields() {\n        SUser user = repository.add(aUser().withId(1L).withUserName(\"dummy username\").build());\n        SGroup group = repository\n                .add(aGroup().forGroupId(258L).forGroupName(\"dummy groupName\").forParentPath(\"bonita/devList\").build());\n        SRole role = repository.add(aRole().forRoleId(259L).forRoleName(\"dummy roleName\").build());\n        SUserMembership sUserMembership = aUserMembership().forUser(user).memberOf(group.getId(), role.getId()).build();\n        repository.add(sUserMembership);\n        List userMemberships = repository.getUserMemberships();\n        assertThat(userMemberships).hasSize(1);\n        SUserMembership userMembership = (SUserMembership) userMemberships.get(0);\n        assertThat(userMembership.getGroupParentPath()).isEqualTo(\"bonita/devList\");\n        assertThat(userMembership.getRoleName()).isEqualTo(\"dummy roleName\");\n        assertThat(userMembership.getGroupName()).isEqualTo(\"dummy groupName\");\n        assertThat(userMembership.getUsername()).isEqualTo(\"dummy username\");\n    }\n\n    @Test\n    public void getSUserMembershipById_should_fill_in_groupParentPath() {\n\n        SUser user = repository.add(aUser().withId(1L).withUserName(\"dummy username\").build());\n        SGroup group = repository\n                .add(aGroup().forGroupId(258L).forGroupName(\"dummy groupName\").forParentPath(\"bonita/devList\").build());\n        SRole role = repository.add(aRole().forRoleId(259L).forRoleName(\"dummy roleName\").build());\n        SUserMembership sUserMembership = aUserMembership().forUser(user).memberOf(group.getId(), role.getId()).build();\n        repository.add(sUserMembership);\n        List userMemberships = repository.getSUserMembershipById(sUserMembership.getId());\n        assertThat(userMemberships).hasSize(1);\n        SUserMembership userMembership = (SUserMembership) userMemberships.get(0);\n        assertThat(userMembership.getGroupParentPath()).isEqualTo(\"bonita/devList\");\n\n    }\n\n    @Test\n    public void searchUserMembership_should_fill_in_groupParentPath() {\n\n        SUser user = repository.add(aUser().withId(1L).withUserName(\"dummy username\").build());\n        SGroup group = repository\n                .add(aGroup().forGroupId(258L).forGroupName(\"dummy groupName\").forParentPath(\"bonita/devList\").build());\n        SRole role = repository.add(aRole().forRoleId(259L).forRoleName(\"dummy roleName\").build());\n        SUserMembership sUserMembership = aUserMembership().forUser(user).memberOf(group.getId(), role.getId()).build();\n        repository.add(sUserMembership);\n        List userMemberships = repository.searchUserMembership();\n        assertThat(userMemberships).hasSize(1);\n        SUserMembership userMembership = (SUserMembership) userMemberships.get(0);\n        assertThat(userMembership.getGroupParentPath()).isEqualTo(\"bonita/devList\");\n\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/process/instance/model/UserTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.test.persistence.builder.UserBuilder.aUser;\n\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.identity.model.SUserLogin;\nimport org.bonitasoft.engine.test.persistence.repository.UserRepository;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.transaction.annotation.Transactional;\n\n/**\n * @author Danila Mazour\n */\n@RunWith(SpringRunner.class)\n@ContextConfiguration(locations = { \"/testContext.xml\" })\n@Transactional\npublic class UserTest {\n\n    @Autowired\n    private UserRepository repository;\n\n    //Those tests currently verify that the queries returning UserMemberships correctly retrieve the groupParentPath when building the Usermembership objects\n\n    @Test\n    public void should_retrieve_user_login() {\n        SUser user = aUser().withId(124L).withUserName(\"walter.bates\").build();\n        repository.add(user);\n        repository.flush();\n        SUserLogin userLogin = SUserLogin.builder().id(124L).lastConnection(1234567L).sUser(user).build();\n        user.setSUserLogin(userLogin);\n        repository.add(userLogin);\n        repository.flush();\n\n        assertThat(repository.getUserByName(\"walter.bates\").getSUserLogin().getLastConnection()).isEqualTo(1234567L);\n\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/document/DocumentQueryTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.document;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.core.document.model.SDocument;\nimport org.bonitasoft.engine.core.document.model.SDocumentMapping;\nimport org.bonitasoft.engine.core.document.model.SLightDocument;\nimport org.bonitasoft.engine.core.document.model.SMappedDocument;\nimport org.bonitasoft.engine.core.document.model.archive.SAMappedDocument;\nimport org.bonitasoft.engine.test.persistence.repository.DocumentRepository;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.transaction.annotation.Transactional;\n\n@RunWith(SpringRunner.class)\n@ContextConfiguration(locations = { \"/testContext.xml\" })\n@Transactional\npublic class DocumentQueryTest {\n\n    private static final long PROCESS_INSTANCE_ID = 6666666666666666L;\n\n    @Autowired\n    private DocumentRepository repository;\n\n    @Test\n    public void getLightDocument_should_read_previously_saved_document() {\n        // given\n        repository.add(\n                SLightDocument.builder().id(99L).author(11L).hasContent(true).fileName(\"myFile.pdf\")\n                        .mimeType(\"application/pdf\").author(22L)\n                        .build());\n\n        //when\n        final SLightDocument document = repository.getById(SLightDocument.class, 99L);\n\n        // //then\n        assertThat(document.getFileName()).isEqualTo(\"myFile.pdf\");\n        assertThat(document.getMimeType()).isEqualTo(\"application/pdf\");\n        assertThat(document.getAuthor()).isEqualTo(22L);\n    }\n\n    @Test\n    public void getDocumentWithContent_should_retrieve_previously_saved_document() {\n        // given\n        final byte[] binaryDocContent = \"someBinaryContent\".getBytes();\n        repository.add(\n                SDocument.builder().id(666L).hasContent(true).fileName(\"myFile.pdf\")\n                        .mimeType(\"application/pdf\").content(binaryDocContent)\n                        .build());\n\n        //when\n        final SDocument document = repository.getById(SDocument.class, 666L);\n\n        // //then\n        assertThat(document.getFileName()).isEqualTo(\"myFile.pdf\");\n        assertThat(document.getMimeType()).isEqualTo(\"application/pdf\");\n        assertThat(document.getContent()).isEqualTo(binaryDocContent);\n    }\n\n    @Test\n    public void getDocumentMapping_should_retrieve_previously_saved_document_mapping() {\n        // given:\n        repository.add(SDocumentMapping.builder().id(14L)\n                .name(\"myDocMapping\").description(\"doc mapping description\").documentId(111L)\n                .index(9).processInstanceId(987987987987L).version(\"2.0\")\n                .build());\n\n        // when:\n        SDocumentMapping documentMapping = repository.getById(SDocumentMapping.class, 14L);\n\n        // then:\n        assertThat(documentMapping.getName()).isEqualTo(\"myDocMapping\");\n        assertThat(documentMapping.getDescription()).isEqualTo(\"doc mapping description\");\n        assertThat(documentMapping.getIndex()).isEqualTo(9);\n        assertThat(documentMapping.getProcessInstanceId()).isEqualTo(987987987987L);\n        assertThat(documentMapping.getVersion()).isEqualTo(\"2.0\");\n        assertThat(documentMapping.getDocumentId()).isEqualTo(111L);\n    }\n\n    @Test\n    public void getSMappedDocumentOfProcessWithName_should_retrieve_previously_saved_mapped_document() {\n        // given:\n        final SLightDocument docContent = SLightDocument.builder()\n                .id(666L).hasContent(true).fileName(\"myFile.pdf\").mimeType(\"application/pdf\")\n                .build();\n\n        repository.add(docContent);\n        repository.add(SMappedDocument.builder().id(14L)\n                .name(\"myMappedDocument\").description(\"doc desc\")\n                .index(-1).processInstanceId(444888444488844L).version(\"1.7\")\n                .document(docContent)\n                .documentId(docContent.getId())\n                .build());\n\n        // when:\n        SMappedDocument mappedDocument = repository.getSMappedDocumentOfProcessWithName(\"myMappedDocument\",\n                444888444488844L);\n\n        // then:\n        assertThat(mappedDocument.getName()).isEqualTo(\"myMappedDocument\");\n        assertThat(mappedDocument.getDescription()).isEqualTo(\"doc desc\");\n        assertThat(mappedDocument.getIndex()).isEqualTo(-1);\n        assertThat(mappedDocument.getProcessInstanceId()).isEqualTo(444888444488844L);\n        assertThat(mappedDocument.getVersion()).isEqualTo(\"1.7\");\n        assertThat(mappedDocument.getDocument()).isEqualTo(docContent);\n    }\n\n    @Test\n    public void getSAMappedDocumentOfProcessWithName_should_retrieve_previously_saved_archived_mapped_document() {\n        // given:\n        final SLightDocument docContent = SLightDocument.builder()\n                .id(915L).hasContent(true).fileName(\"myFile.txt\").mimeType(\"test/plain\")\n                .build();\n\n        repository.add(docContent);\n        repository.add(SAMappedDocument.builder().id(32L)\n                .name(\"archivedMappedDoc\").description(\"doc desc\")\n                .index(-1).processInstanceId(PROCESS_INSTANCE_ID).version(\"1.7\")\n                .document(docContent)\n                .documentId(docContent.getId())\n                .archiveDate(System.currentTimeMillis())\n                .build());\n\n        // when:\n        SAMappedDocument archivedMappedDocument = repository.getSAMappedDocumentOfProcessWithName(\"archivedMappedDoc\",\n                PROCESS_INSTANCE_ID);\n\n        // then:\n        assertThat(archivedMappedDocument.getName()).isEqualTo(\"archivedMappedDoc\");\n        assertThat(archivedMappedDocument.getDescription()).isEqualTo(\"doc desc\");\n        assertThat(archivedMappedDocument.getIndex()).isEqualTo(-1);\n        assertThat(archivedMappedDocument.getProcessInstanceId()).isEqualTo(PROCESS_INSTANCE_ID);\n        assertThat(archivedMappedDocument.getVersion()).isEqualTo(\"1.7\");\n        assertThat(archivedMappedDocument.getDocument()).isEqualTo(docContent);\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/page/PageQueriesTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.test.persistence.builder.PageBuilder.aPage;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.test.persistence.repository.PageRepository;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.transaction.annotation.Transactional;\n\n@RunWith(SpringRunner.class)\n@ContextConfiguration(locations = { \"/testContext.xml\" })\n@Transactional\npublic class PageQueriesTest {\n\n    @Autowired\n    private PageRepository repository;\n\n    @Test\n    public void getPageContent_should_return_the_content_of_the_page() {\n        // given\n        final AbstractSPage page = repository\n                .add(aPage().withName(\"MyPage\").withContent(\"The content\".getBytes()).build());\n\n        //when\n        final SPageWithContent pageContent = repository.getPageContent(page.getId());\n        // //then\n        assertThat(pageContent.getContent()).isEqualTo(\"The content\".getBytes());\n        assertThat(pageContent.getId()).isEqualTo(page.getId());\n    }\n\n    @Test\n    public void getPageByName_should_return_the_page_having_the_name() {\n        // given\n        repository.add(aPage().withName(\"MyPage1\").withContent(\"The content\".getBytes()).build());\n        final AbstractSPage page2 = repository\n                .add(aPage().withName(\"MyPage2\").withContent(\"The content\".getBytes()).build());\n\n        //when\n        final AbstractSPage pageByName = repository.getPageByName(\"MyPage2\");\n\n        // //then\n        assertThat(pageByName.getId()).isEqualTo(page2.getId());\n    }\n\n    @Test\n    public void should_getPageByName_return_the_page_without_processDefinitionId() {\n        //given\n        repository.add(aPage().withName(\"aPage\").withContent(\"content of the global page\".getBytes()).build());\n        repository.add(aPage().withName(\"aPage\").withContent(\"content of the process page 1\".getBytes())\n                .withProcessDefinitionId(123L).build());\n        repository.add(aPage().withName(\"anOtherPage\").withContent(\"content of the process page 2\".getBytes())\n                .withProcessDefinitionId(123L).build());\n        //when\n        SPage globalPage = repository.getPageByName(\"aPage\");\n        SPage processPage1 = repository.getPageByNameAndProcessDefinitionId(\"aPage\", 123L);\n        SPage processPage2 = repository.getPageByNameAndProcessDefinitionId(\"anOtherPage\", 123L);\n        //then\n        assertThat(new String(repository.getPageContent(globalPage.getId()).getContent()))\n                .isEqualTo(\"content of the global page\");\n        assertThat(new String(repository.getPageContent(processPage1.getId()).getContent()))\n                .isEqualTo(\"content of the process page 1\");\n        assertThat(new String(repository.getPageContent(processPage2.getId()).getContent()))\n                .isEqualTo(\"content of the process page 2\");\n    }\n\n    @Test\n    public void getPageByNameAndProcessDefinition_should_return_the_page_having_the_name() {\n        // given\n        final AbstractSPage myPage1 = repository.add(aPage()\n                .withName(\"MyPage1\")\n                .withProcessDefinitionId(1L)\n                .build());\n        assertThat(myPage1).as(\"should add the page\").isNotNull();\n\n        //when\n        final SPage pageByName = repository.getPageByNameAndProcessDefinitionId(\"MyPage1\", 1L);\n\n        // //then\n        assertThat(pageByName).as(\"should retrieve the page\").isNotNull();\n    }\n\n    @Test\n    public void getPageByProcessDefinition_should_filter_results() {\n        // given\n        final AbstractSPage myPage1 = repository.add(aPage()\n                .withName(\"MyPage1\")\n                .withProcessDefinitionId(1L)\n                .withContentType(ContentType.FORM)\n                .build());\n        repository.add(aPage()\n                .withName(\"AnotherPage\")\n                .withProcessDefinitionId(2L)\n                .withContentType(ContentType.FORM)\n                .build());\n\n        assertThat(myPage1).as(\"should add the page\").isNotNull();\n\n        //when\n        final List<SPage> results = repository.getPageByProcessDefinitionId(1L);\n\n        // //then\n        assertThat(results).as(\"should retrieve the page\").hasSize(1);\n        assertThat(results.get(0).getName()).as(\"should retrieve the right page\").isEqualTo(myPage1.getName());\n        assertThat(results.get(0).getProcessDefinitionId()).as(\"should retrieve the right page\")\n                .isEqualTo(myPage1.getProcessDefinitionId());\n        assertThat(results.get(0).getContentType()).as(\"should retrieve the right page\")\n                .isEqualTo(myPage1.getContentType());\n    }\n\n    @Test\n    public void should_retrieve_SPageMapping_previously_saved() {\n        // given:\n        final List<String> authorizationRules = new ArrayList<>(Arrays.asList(\"rule1\", \"rule2\", \"rule3\"));\n        SPageMapping pageMapping = SPageMapping.builder().pageId(2L).key(\"myKey\").url(\"http://www/example.com\")\n                .urlAdapter(\"legacy\").build();\n        pageMapping.setPageAuthorizationRules(authorizationRules);\n        repository.add(pageMapping);\n\n        // when:\n        final SPageMapping mapping = repository.getPageMappingByKey(\"myKey\");\n\n        // then:\n        assertThat(mapping).extracting(\"pageId\", \"key\", \"url\", \"urlAdapter\")\n                .containsOnly(2L, \"myKey\", \"http://www/example.com\", \"legacy\");\n        assertThat(mapping.getPageAuthorizationRules()).isEqualTo(authorizationRules);\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/parameter/ParameterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.parameter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.bonitasoft.engine.commons.Pair.pair;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.test.persistence.jdbc.JdbcRowMapper;\nimport org.bonitasoft.engine.test.persistence.repository.TestRepository;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.transaction.annotation.Transactional;\n\n@RunWith(SpringRunner.class)\n@ContextConfiguration(locations = { \"/testContext.xml\" })\n@Transactional\npublic class ParameterTest {\n\n    @Autowired\n    private TestRepository testRepository;\n    @Autowired\n    private JdbcTemplate jdbcTemplate;\n\n    @Test\n    public void should_save_and_get_parameter() {\n        SParameter sParameter = testRepository.add(new SParameter(\"parameterName\", \"StringValue\", 12345L));\n\n        PersistentObject parameterFromQuery = testRepository.selectOne(\"getParameterByName\",\n                pair(\"name\", \"parameterName\"), pair(\"processDefinitionId\", 12345L));\n        testRepository.flush();\n        Map<String, Object> parameterAsMap = jdbcTemplate\n                .queryForObject(\"SELECT * FROM proc_parameter WHERE name = 'parameterName'\",\n                        new JdbcRowMapper(\"ID\", \"PROCESS_ID\"));\n\n        assertThat(parameterFromQuery).isEqualTo(sParameter);\n        assertThat(parameterAsMap).containsOnly(\n                entry(\"ID\", sParameter.getId()),\n                entry(\"PROCESS_ID\", 12345L),\n                entry(\"NAME\", \"parameterName\"),\n                entry(\"VALUE\", \"StringValue\"));\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/platform/command/model/PlatformCommandTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.command.model;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.bonitasoft.engine.commons.Pair.pair;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.test.persistence.jdbc.JdbcRowMapper;\nimport org.bonitasoft.engine.test.persistence.repository.PlatformRepository;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.transaction.annotation.Transactional;\n\n@RunWith(SpringRunner.class)\n@ContextConfiguration(locations = { \"/testContext.xml\" })\n@Transactional\npublic class PlatformCommandTest {\n\n    @Autowired\n    private PlatformRepository repository;\n    @Autowired\n    private JdbcTemplate jdbcTemplate;\n\n    @Test\n    public void should_save_and_get_SPlatformCommand() {\n        SPlatformCommand platformCommand = repository.add(SPlatformCommand.builder()\n                .name(\"myPlatformCommand\")\n                .implementation(\"com.acme.PlatformCommandClass\")\n                .description(\"a custom command on platform\")\n                .build());\n        repository.flush();\n\n        PersistentObject platformCommandFromQuery = repository.selectOneOnPlatform(\"getPlatformCommandByName\",\n                pair(\"name\", \"myPlatformCommand\"));\n        Map<String, Object> platformCommandAsMap = jdbcTemplate.queryForObject(\"SELECT * FROM platformCommand\",\n                new JdbcRowMapper(\"ID\"));\n\n        assertThat(platformCommandFromQuery).isEqualTo(platformCommand);\n        assertThat(platformCommandAsMap).containsOnly(\n                entry(\"ID\", platformCommand.getId()),\n                entry(\"NAME\", \"myPlatformCommand\"),\n                entry(\"DESCRIPTION\", \"a custom command on platform\"),\n                entry(\"IMPLEMENTATION\", \"com.acme.PlatformCommandClass\"));\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/platform/model/impl/PlatformTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.model.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.platform.model.SPlatform;\nimport org.bonitasoft.engine.test.persistence.jdbc.JdbcRowMapper;\nimport org.bonitasoft.engine.test.persistence.repository.PlatformRepository;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.transaction.annotation.Transactional;\n\n@RunWith(SpringRunner.class)\n@ContextConfiguration(locations = { \"/testContext.xml\" })\n@Transactional\npublic class PlatformTest {\n\n    @Autowired\n    private PlatformRepository repository;\n    @Autowired\n    private JdbcTemplate jdbcTemplate;\n\n    @Test\n    public void should_save_and_get_SPlatform() {\n        SPlatform platform = repository.add(SPlatform.builder()\n                .initialBonitaVersion(\"5.9.0\")\n                .dbSchemaVersion(\"1.2\")\n                .applicationVersion(\"0.0.0\")\n                .created(345L)\n                .information(\"some infos XYZ\")\n                .createdBy(\"The almighty\")\n                .maintenanceEnabled(false)\n                .build());\n        repository.flush();\n\n        PersistentObject platformFromQuery = repository.selectOneOnPlatform(\"getPlatform\");\n        Map<String, Object> platformAsMap = jdbcTemplate.queryForObject(\"SELECT * FROM platform\",\n                new JdbcRowMapper(List.of(\"ID\", \"CREATED\"),\n                        List.of(\"MAINTENANCE_MESSAGE_ACTIVE\", \"MAINTENANCE_ENABLED\")));\n\n        assertThat(platformFromQuery).isEqualTo(platform);\n        assertThat(platformAsMap).containsOnly(\n                entry(\"ID\", platform.getId()),\n                entry(\"CREATED\", 345L),\n                entry(\"CREATED_BY\", \"The almighty\"),\n                entry(\"INITIAL_BONITA_VERSION\", \"5.9.0\"),\n                entry(\"APPLICATION_VERSION\", \"0.0.0\"),\n                entry(\"MAINTENANCE_MESSAGE\", null),\n                entry(\"MAINTENANCE_MESSAGE_ACTIVE\", false),\n                entry(\"VERSION\", \"1.2\"),\n                entry(\"INFORMATION\", \"some infos XYZ\"),\n                entry(\"MAINTENANCE_ENABLED\", false));\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/profile/ProfilesTest.java",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.tuple;\nimport static org.bonitasoft.engine.test.persistence.builder.GroupBuilder.aGroup;\nimport static org.bonitasoft.engine.test.persistence.builder.ProfileBuilder.aProfile;\nimport static org.bonitasoft.engine.test.persistence.builder.ProfileMemberBuilder.aProfileMember;\nimport static org.bonitasoft.engine.test.persistence.builder.RoleBuilder.aRole;\nimport static org.bonitasoft.engine.test.persistence.builder.UserBuilder.aUser;\nimport static org.bonitasoft.engine.test.persistence.builder.UserMembershipBuilder.aUserMembership;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.identity.model.SRole;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.profile.model.SProfile;\nimport org.bonitasoft.engine.test.persistence.repository.ProfileRepository;\nimport org.bonitasoft.engine.test.persistence.repository.UserMembershipRepository;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.transaction.annotation.Transactional;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@RunWith(SpringRunner.class)\n@ContextConfiguration(locations = { \"/testContext.xml\" })\n@Transactional\npublic class ProfilesTest {\n\n    @SuppressWarnings(\"unused\")\n    @Autowired\n    private ProfileRepository repository;\n\n    @SuppressWarnings(\"unused\")\n    @Autowired\n    private UserMembershipRepository userMembershipRepository;\n\n    @Test\n    public void profile_with_user_mapped_should_be_retrieved() {\n        // given:\n        final SUser user = aUser().build();\n        repository.add(user);\n        final String profileName = \"retrieved\";\n        final SProfile profile = aProfile().withName(profileName).build();\n        repository.add(profile);\n        repository.add(aProfileMember().withProfileId(profile.getId()).withUserId(user.getId()).build());\n\n        // when:\n        final List<SProfile> profiles = repository.getProfilesOfUser(user.getId());\n\n        // then:\n        assertThat(profiles).hasSize(1).extracting(\"id\", \"name\").containsExactly(tuple(profile.getId(), profileName));\n    }\n\n    @Test\n    public void profile_with_role_mapped_should_be_retrieved() {\n        // given:\n        final SRole role = aRole().build();\n        final SUser user = aUser().build();\n        repository.add(role);\n        repository.add(user);\n        userMembershipRepository.add(aUserMembership().forUser(user).memberOf(-1, role.getId()).build());\n        final String profileName = \"retrieved\";\n        final SProfile profile = aProfile().withName(profileName).build();\n        repository.add(profile);\n        repository.add(aProfileMember().withProfileId(profile.getId()).withRoleId(role.getId()).build());\n\n        // when:\n        final List<SProfile> profiles = repository.getProfilesOfUser(user.getId());\n\n        // then:\n        assertThat(profiles).hasSize(1).extracting(\"id\", \"name\").containsExactly(tuple(profile.getId(), profileName));\n    }\n\n    @Test\n    public void no_profileWithNavigation_should_be_retrieved_for_system_user() {\n        // given:\n        final SRole role = aRole().build();\n        repository.add(role);\n        final SProfile profile = aProfile().withName(\"not retrieved\").build();\n        repository.add(profile);\n        repository.add(aProfileMember().withProfileId(profile.getId()).withRoleId(role.getId()).build());\n\n        // when:\n        final List<SProfile> profiles = repository.getProfilesOfUser(-1);\n\n        // then:\n        assertThat(profiles).hasSize(0);\n    }\n\n    @Test\n    public void profile_with_group_mapped_should_be_retrieved() {\n        // given:\n        final SGroup group = aGroup().build();\n        final SUser user = aUser().build();\n        repository.add(group);\n        repository.add(user);\n        userMembershipRepository.add(aUserMembership().forUser(user).memberOf(group.getId(), -1).build());\n        final String profileName = \"retrieved\";\n        final SProfile profile = aProfile().withName(profileName).build();\n        repository.add(profile);\n        repository.add(aProfileMember().withProfileId(profile.getId()).withGroupId(group.getId()).build());\n\n        // when:\n        final List<SProfile> profiles = repository.getProfilesOfUser(user.getId());\n\n        // then:\n        assertThat(profiles).hasSize(1).extracting(\"id\", \"name\").containsExactly(tuple(profile.getId(), profileName));\n    }\n\n    @Test\n    public void profile_with_membership_mapped_should_be_retrieved() {\n        // given:\n        final SGroup group = aGroup().build();\n        final SRole role = aRole().build();\n        final SUser user = aUser().build();\n        repository.add(group);\n        repository.add(role);\n        repository.add(user);\n        userMembershipRepository.add(aUserMembership().forUser(user).memberOf(group.getId(), role.getId()).build());\n        final String profileName = \"retrieved\";\n        final SProfile profile = aProfile().withName(profileName).build();\n        repository.add(profile);\n        repository.add(aProfileMember().withProfileId(profile.getId()).withGroupId(group.getId())\n                .withRoleId(role.getId()).build());\n\n        // when:\n        final List<SProfile> profiles = repository.getProfilesOfUser(user.getId());\n\n        // then:\n        assertThat(profiles).hasSize(1).extracting(\"id\", \"name\").containsExactly(tuple(profile.getId(), profileName));\n    }\n\n    @Test\n    public void profile_without_user_mapped_should_not_be_retrieved() {\n        // given:\n        final SUser user = aUser().build();\n        repository.add(user);\n        final String profileName = \"should not be retrieved\";\n        final SProfile profile = aProfile().withName(profileName).build();\n        repository.add(profile);\n\n        // when:\n        final List<SProfile> profiles = repository.getProfilesOfUser(user.getId());\n\n        // then:\n        assertThat(profiles).hasSize(0);\n    }\n\n    @Test\n    public void profile_without_given_user_mapped_should_not_be_retrieved() {\n        // given:\n        final SUser userMapped = aUser().build();\n        final SUser userAsked = aUser().build();\n        repository.add(userMapped);\n        final String profileName = \"should not be retrieved\";\n        final SProfile profile = aProfile().withName(profileName).build();\n        repository.add(profile);\n        repository.add(aProfileMember().withProfileId(profile.getId()).withUserId(userMapped.getId()).build());\n\n        // when:\n        final List<SProfile> profiles = repository.getProfilesOfUser(userAsked.getId());\n\n        // then:\n        assertThat(profiles).hasSize(0);\n    }\n\n    @Test\n    public void no_profile_should_be_retrieved_for_system_user() {\n        // given:\n        final SRole role = aRole().build();\n        repository.add(role);\n        final SProfile profile = aProfile().withName(\"not retrieved\").build();\n        repository.add(profile);\n        repository.add(aProfileMember().withProfileId(profile.getId()).withRoleId(role.getId()).build());\n\n        // when:\n        final List<SProfile> profiles = repository.getProfilesOfUser(-1);\n\n        // then:\n        assertThat(profiles).hasSize(0);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/queriablelogger/model/QueriableLogTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.queriablelogger.model;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.data.MapEntry.entry;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.test.persistence.jdbc.JdbcRowMapper;\nimport org.bonitasoft.engine.test.persistence.repository.TestRepository;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.transaction.annotation.Transactional;\n\n@RunWith(SpringRunner.class)\n@ContextConfiguration(locations = { \"/testContext.xml\" })\n@Transactional\npublic class QueriableLogTest {\n\n    @Autowired\n    TestRepository testRepository;\n    @Autowired\n    private JdbcTemplate jdbcTemplate;\n\n    @Test\n    public void should_be_able_to_add_queriable_log() {\n\n        SQueriableLog queriableLog = SQueriableLog.builder()\n                .initializeNow()//\n                .id(1)\n                .userId(\"userId1\")\n                .actionType(\"actionType1\") //\n                .actionScope(\"actionScope1\") //\n                .productVersion(\"productVersion1\") //\n                .clusterNode(\"clusterNode1\") //\n                .severity(SQueriableLogSeverity.BUSINESS) //\n                .actionScope(\"actionScope1\") //\n                .callerClassName(\"callerClassName1\") //\n                .callerMethodName(\"callerMethodName1\") //\n                .rawMessage(\"message1\").build();\n\n        SQueriableLog queriableLog1 = SQueriableLog.builder() //\n                .initializeNow()\n                .id(2)\n                .userId(\"userId2\")\n                .productVersion(\"productVersion2\") //\n                .actionType(\"actionType2\") //\n                .actionScope(\"actionScope2\") //\n                .clusterNode(\"clusterNode2\") //\n                .severity(SQueriableLogSeverity.BUSINESS) //\n                .actionScope(\"actionScope2\") //\n                .callerClassName(\"callerClassName2\") //\n                .callerMethodName(\"callerMethodName2\") //\n                .rawMessage(\"message2\").build();\n\n        testRepository.add(queriableLog);\n        testRepository.add(queriableLog1);\n\n        testRepository.flush();\n\n        List<Map<String, Object>> queriableLogs = jdbcTemplate.query(\"SELECT * from queriable_log\",\n                new JdbcRowMapper(\"ID\", \"NUMERICINDEX1\", \"NUMERICINDEX2\", \"NUMERICINDEX3\",\n                        \"NUMERICINDEX4\", \"NUMERICINDEX5\", \"THREADNUMBER\", \"LOG_TIMESTAMP\"));\n\n        assertThat(queriableLogs).hasSize(2);\n        assertThat(queriableLogs.stream().filter(m -> m.get(\"ID\").equals(1L)).findFirst().get()).containsOnly(\n                entry(\"ACTIONSCOPE\", \"actionScope1\"), entry(\"ACTIONSTATUS\", -1), entry(\"ACTIONTYPE\", \"actionType1\"),\n                entry(\"CALLERCLASSNAME\", \"callerClassName1\"),\n                entry(\"CALLERMETHODNAME\", \"callerMethodName1\"),\n                entry(\"CLUSTERNODE\", \"clusterNode1\"),\n                entry(\"NUMERICINDEX1\", -1L),\n                entry(\"NUMERICINDEX2\", -1L),\n                entry(\"NUMERICINDEX3\", -1L),\n                entry(\"NUMERICINDEX4\", -1L),\n                entry(\"NUMERICINDEX5\", -1L),\n                entry(\"PRODUCTVERSION\", \"productVersion1\"),\n                entry(\"SEVERITY\", \"BUSINESS\"),\n                entry(\"RAWMESSAGE\", \"message1\"),\n                entry(\"THREADNUMBER\", queriableLog.getThreadNumber()),\n                entry(\"USERID\", \"userId1\"),\n                entry(\"WEEKOFYEAR\", queriableLog.getWeekOfYear()),\n                entry(\"WHATMONTH\", queriableLog.getMonth()),\n                entry(\"DAYOFYEAR\", queriableLog.getDayOfYear()),\n                entry(\"LOG_TIMESTAMP\", queriableLog.getTimeStamp()),\n                entry(\"ID\", 1L),\n                entry(\"WHATYEAR\", queriableLog.getYear()));\n        assertThat(queriableLogs.stream().filter(m -> m.get(\"ID\").equals(2L)).findFirst().get()).containsOnly(\n                entry(\"ACTIONSCOPE\", \"actionScope2\"),\n                entry(\"ACTIONSTATUS\", -1),\n                entry(\"ACTIONTYPE\", \"actionType2\"),\n                entry(\"CALLERCLASSNAME\", \"callerClassName2\"),\n                entry(\"CALLERMETHODNAME\", \"callerMethodName2\"),\n                entry(\"CLUSTERNODE\", \"clusterNode2\"),\n                entry(\"NUMERICINDEX1\", -1L),\n                entry(\"NUMERICINDEX2\", -1L),\n                entry(\"NUMERICINDEX3\", -1L),\n                entry(\"NUMERICINDEX4\", -1L),\n                entry(\"NUMERICINDEX5\", -1L),\n                entry(\"PRODUCTVERSION\", \"productVersion2\"),\n                entry(\"SEVERITY\", \"BUSINESS\"),\n                entry(\"RAWMESSAGE\", \"message2\"),\n                entry(\"THREADNUMBER\", queriableLog.getThreadNumber()),\n                entry(\"USERID\", \"userId2\"),\n                entry(\"WEEKOFYEAR\", queriableLog1.getWeekOfYear()),\n                entry(\"WHATMONTH\", queriableLog1.getMonth()),\n                entry(\"DAYOFYEAR\", queriableLog1.getDayOfYear()),\n                entry(\"LOG_TIMESTAMP\", queriableLog1.getTimeStamp()),\n                entry(\"ID\", 2L),\n                entry(\"WHATYEAR\", queriableLog1.getYear())\n\n        );\n\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/resources/DependencyServiceQueriesTest.java",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.resources;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.bonitasoft.engine.commons.Pair.pair;\nimport static org.bonitasoft.engine.dependency.model.ScopeType.PROCESS;\nimport static org.bonitasoft.engine.dependency.model.ScopeType.TENANT;\nimport static org.junit.Assert.fail;\n\nimport java.util.Map;\nimport java.util.Random;\n\nimport org.bonitasoft.engine.dependency.model.DependencyContent;\nimport org.bonitasoft.engine.dependency.model.SDependency;\nimport org.bonitasoft.engine.dependency.model.SDependencyMapping;\nimport org.bonitasoft.engine.dependency.model.SPlatformDependency;\nimport org.bonitasoft.engine.dependency.model.SPlatformDependencyMapping;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.test.persistence.jdbc.JdbcRowMapper;\nimport org.bonitasoft.engine.test.persistence.repository.DependencyRepository;\nimport org.bonitasoft.engine.test.persistence.repository.PlatformRepository;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.transaction.annotation.Transactional;\n\n/**\n * @author Danila Mazour\n */\n@RunWith(SpringRunner.class)\n@ContextConfiguration(locations = { \"/testContext.xml\" })\n@Transactional\npublic class DependencyServiceQueriesTest {\n\n    @Autowired\n    private DependencyRepository repository;\n    @Autowired\n    private PlatformRepository platformRepository;\n    @Autowired\n    private JdbcTemplate jdbcTemplate;\n\n    private static String getSaltString() {\n        String SALTCHARS = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890\";\n        StringBuilder salt = new StringBuilder();\n        Random rnd = new Random();\n        while (salt.length() < 10) { // length of the random string.\n            int index = (int) (rnd.nextFloat() * SALTCHARS.length());\n            salt.append(SALTCHARS.charAt(index));\n        }\n        return salt.toString();\n    }\n\n    @Test\n    public void should_retrieve_the_correct_dependencyId_from_tenant_and_filename() {\n        SDependency resource_to_retrieve = repository.add(SDependency.builder()\n                .name(getSaltString())\n                .fileName(\"aFileName.zip\")\n                .value_(\"toutou\".getBytes()).build());\n        repository.add(SDependency.builder()\n                .name(getSaltString())\n                .fileName(\"file.lst\")\n                .value_(\"toutou\".getBytes()).build());\n        repository.add(SDependencyMapping.builder()\n                .artifactId(666L)\n                .artifactType(TENANT)\n                .dependencyId(resource_to_retrieve.getId()).build());\n\n        Long dependencyId = repository.getDependencyIdFromArtifact(666L, TENANT, \"aFileName.zip\");\n\n        assertThat(dependencyId).as(\"dependency id\").isEqualTo(resource_to_retrieve.getId());\n    }\n\n    @Test\n    public void should_retrieve_nothing_when_BDM_not_deployed() {\n        SDependency resource_to_not_retrieve = repository.add(SDependency.builder()\n                .name(getSaltString())\n                .fileName(\"aFileName.zip\")\n                .value_(\"toutou\".getBytes())\n                .build());\n        repository.add(SDependencyMapping.builder()\n                .artifactId(666L)\n                .artifactType(TENANT)\n                .dependencyId(resource_to_not_retrieve.getId()).build());\n\n        Long dependencyId = repository.getDependencyIdFromArtifact(666L, TENANT, \"anotherFileName.zip\");\n\n        assertThat(dependencyId).as(\"dependency id\").isNull();\n    }\n\n    @Test\n    public void should_retrieve_nothing_when_the_DB_isEmpty() {\n        Long dependencyId = repository.getDependencyIdFromArtifact(666L, TENANT, \"aFileName.zip\");\n\n        assertThat(dependencyId).as(\"dependency id\").isNull();\n    }\n\n    @Test\n    public void should_retrieve_dependency_content_only_using_disconnected_objects() throws Exception {\n        SDependency aDependency = repository.add(SDependency.builder()\n                .name(getSaltString())\n                .fileName(\"aFileName.zip\")\n                .value_(\"toutou\".getBytes()).build());\n\n        DependencyContent dependencyContentOnly = repository.getDependencyContentOnly(aDependency.getId());\n\n        assertThat(dependencyContentOnly.getContent()).isEqualTo(\"toutou\".getBytes());\n        assertThat(dependencyContentOnly.getFileName()).isEqualTo(\"aFileName.zip\");\n        assertThat(repository.getSession().getIdentifier(aDependency)).isNotNull();\n        try {\n            repository.getSession().getIdentifier(dependencyContentOnly);\n            fail(\"should fail because the object should not be in the hibernate session\");\n        } catch (Exception ignored) {\n        }\n    }\n\n    @Test\n    public void should_save_and_get_dependency() {\n        SDependency aDependency = repository.add(SDependency.builder()\n                .name(\"dependencyName\")\n                .fileName(\"aFileName.jar\")\n                .description(\"description of the jar\")\n                .value_(\"jarContent\".getBytes()).build());\n\n        PersistentObject dependencyFromQuery = repository.selectOne(\"getDependencyByName\",\n                pair(\"name\", \"dependencyName\"));\n        Map<String, Object> dependencyAsMap = jdbcTemplate.queryForObject(\"SELECT * FROM dependency\",\n                new JdbcRowMapper(\"ID\"));\n\n        assertThat(dependencyFromQuery).isEqualTo(aDependency);\n        assertThat(dependencyAsMap).containsOnly(\n                entry(\"ID\", aDependency.getId()),\n                entry(\"NAME\", \"dependencyName\"),\n                entry(\"DESCRIPTION\", \"description of the jar\"),\n                entry(\"FILENAME\", \"aFileName.jar\"),\n                entry(\"VALUE_\", \"jarContent\".getBytes()));\n    }\n\n    @Test\n    public void should_save_and_get_dependency_mapping() {\n        SDependency aDependency = repository.add(SDependency.builder()\n                .name(\"dependencyName\")\n                .fileName(\"aFileName.jar\")\n                .description(\"description of the jar\")\n                .value_(\"jarContent\".getBytes()).build());\n        SDependencyMapping dependencyMapping = repository.add(SDependencyMapping.builder()\n                .artifactId(567L)\n                .artifactType(PROCESS)\n                .dependencyId(aDependency.getId()).build());\n\n        PersistentObject dependencyMappingFromQuery = repository.selectOne(\"getDependencyMappingsByDependency\",\n                pair(\"dependencyId\", aDependency.getId()));\n        Map<String, Object> dependencyMappingAsMap = jdbcTemplate\n                .queryForObject(\"SELECT * FROM dependencymapping WHERE dependencyId=\" + aDependency.getId(),\n                        new JdbcRowMapper(\"ID\", \"ARTIFACTID\", \"DEPENDENCYID\"));\n\n        assertThat(dependencyMappingFromQuery).isEqualTo(dependencyMapping);\n        assertThat(dependencyMappingAsMap).containsOnly(\n                entry(\"ID\", dependencyMapping.getId()),\n                entry(\"ARTIFACTID\", 567L),\n                entry(\"ARTIFACTTYPE\", \"PROCESS\"),\n                entry(\"DEPENDENCYID\", aDependency.getId()));\n    }\n\n    @Test\n    public void should_save_and_get_platform_dependency() {\n        SPlatformDependency aDependency = platformRepository.add(SPlatformDependency.builder()\n                .name(\"dependencyName\")\n                .fileName(\"aFileName.jar\")\n                .description(\"description of the jar\")\n                .value_(\"jarContent\".getBytes()).build());\n\n        PersistentObject dependencyFromQuery = platformRepository.selectOneOnPlatform(\n                \"getPlatformDependencyByName\",\n                pair(\"name\", \"dependencyName\"));\n        Map<String, Object> dependencyAsMap = jdbcTemplate.queryForObject(\"SELECT * FROM pdependency\",\n                new JdbcRowMapper(\"ID\"));\n\n        assertThat(dependencyFromQuery).isEqualTo(aDependency);\n\n        assertThat(dependencyAsMap).containsOnly(\n                entry(\"ID\", aDependency.getId()),\n                entry(\"NAME\", \"dependencyName\"),\n                entry(\"DESCRIPTION\", \"description of the jar\"),\n                entry(\"FILENAME\", \"aFileName.jar\"),\n                entry(\"VALUE_\", \"jarContent\".getBytes()));\n    }\n\n    @Test\n    public void should_save_and_get_platform_dependency_mapping() {\n        SPlatformDependency aDependency = platformRepository.add(SPlatformDependency.builder()\n                .name(\"dependencyName\")\n                .fileName(\"aFileName.jar\")\n                .description(\"description of the jar\")\n                .value_(\"jarContent\".getBytes()).build());\n        SPlatformDependencyMapping dependencyMapping = platformRepository.add(SPlatformDependencyMapping.builder()\n                .artifactId(567L)\n                .artifactType(PROCESS)\n                .dependencyId(aDependency.getId()).build());\n\n        PersistentObject dependencyMappingFromQuery = platformRepository.selectOneOnPlatform(\n                \"getPlatformDependencyMappingsByDependency\", pair(\"dependencyId\", aDependency.getId()));\n        Map<String, Object> dependencyMappingAsMap = jdbcTemplate\n                .queryForObject(\"SELECT * FROM pdependencymapping WHERE dependencyId=\" + aDependency.getId(),\n                        new JdbcRowMapper(\"ID\", \"ARTIFACTID\", \"DEPENDENCYID\"));\n\n        assertThat(dependencyMappingFromQuery).isEqualTo(dependencyMapping);\n        assertThat(dependencyMappingAsMap).containsOnly(\n                entry(\"ID\", dependencyMapping.getId()),\n                entry(\"ARTIFACTID\", 567L),\n                entry(\"ARTIFACTTYPE\", \"PROCESS\"),\n                entry(\"DEPENDENCYID\", aDependency.getId()));\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/resources/ProcessResourceServiceQueriesTest.java",
    "content": "/**\n * Copyright (C) 2015 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.resources;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.tuple;\nimport static org.bonitasoft.engine.test.persistence.builder.BARResourceBuilder.aBARResource;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.test.persistence.repository.ProcessResourceRepository;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.transaction.annotation.Transactional;\n\n@RunWith(SpringRunner.class)\n@ContextConfiguration(locations = { \"/testContext.xml\" })\n@Transactional\npublic class ProcessResourceServiceQueriesTest {\n\n    @Autowired\n    private ProcessResourceRepository repository;\n\n    @Test\n    public void getBarResource_should_get_one_resource() {\n        // given\n        SBARResource resource = repository\n                .add(aBARResource().withName(\"MyResource\").withContent(\"The content\".getBytes())\n                        .withProcessDefinitionId(345L).withType(BARResourceType.EXTERNAL).build());\n        repository.add(aBARResource().withName(\"MyResource2\").withContent(\"The content@\".getBytes())\n                .withProcessDefinitionId(346L).withType(BARResourceType.EXTERNAL).build());\n\n        //when\n        SBARResource myResource = repository.getBARResource(345L, BARResourceType.EXTERNAL, \"MyResource\");\n        // //then\n        assertThat(myResource.getContent()).isEqualTo(\"The content\".getBytes());\n        assertThat(myResource.getId()).isEqualTo(resource.getId());\n    }\n\n    @Test\n    public void getBarResourceOfType_should_get_all_resource_of_type() {\n        // given\n        SBARResource resource1 = repository\n                .add(aBARResource().withName(\"MyResource\").withContent(\"The content\".getBytes())\n                        .withProcessDefinitionId(345L).withType(BARResourceType.EXTERNAL).build());\n        repository.add(aBARResource().withName(\"MyResource2\").withContent(\"The content@\".getBytes())\n                .withProcessDefinitionId(346L).withType(BARResourceType.EXTERNAL).build());\n        SBARResource resource2 = repository\n                .add(aBARResource().withName(\"MyResource3\").withContent(\"The content3\".getBytes())\n                        .withProcessDefinitionId(345L).withType(BARResourceType.EXTERNAL).build());\n        repository.add(aBARResource().withName(\"MyConnector\").withContent(\"The content\".getBytes())\n                .withProcessDefinitionId(345L).withType(BARResourceType.CONNECTOR).build());\n\n        //when\n        List<SBARResource> myResources = repository.getBARResourcesOfType(345L, BARResourceType.EXTERNAL);\n        // //then\n        assertThat(myResources).extracting(\"id\", \"content\").containsOnly(\n                tuple(resource1.getId(), resource1.getContent()), tuple(resource2.getId(), resource2.getContent()));\n    }\n\n    @Test\n    public void getNumberBarResourceOfType_should_get_all_resource_of_type() {\n        // given\n        repository.add(aBARResource().withName(\"MyResource\").withContent(\"The content\".getBytes())\n                .withProcessDefinitionId(345L).withType(BARResourceType.USER_FILTER).build());\n        repository.add(aBARResource().withName(\"MyResource2\").withContent(\"The content@\".getBytes())\n                .withProcessDefinitionId(346L).withType(BARResourceType.USER_FILTER).build());\n        repository.add(aBARResource().withName(\"MyResource3\").withContent(\"The content3\".getBytes())\n                .withProcessDefinitionId(345L).withType(BARResourceType.USER_FILTER).build());\n        repository.add(aBARResource().withName(\"MyConnector\").withContent(\"The content\".getBytes())\n                .withProcessDefinitionId(345L).withType(BARResourceType.CONNECTOR).build());\n\n        //when\n        long myResources = repository.getNumberOfBARResourcesOfType(345L, BARResourceType.USER_FILTER);\n        // //then\n        assertThat(myResources).isEqualTo(2);\n    }\n\n    @Test\n    public void getBarResourceLightOfType_should_get_all_resource_of_type_without_content() {\n        // given\n        SBARResource resource1 = repository\n                .add(aBARResource().withName(\"MyResource\").withContent(\"The content\".getBytes())\n                        .withProcessDefinitionId(345L).withType(BARResourceType.EXTERNAL).build());\n        repository.add(aBARResource().withName(\"MyResource2\").withContent(\"The content@\".getBytes())\n                .withProcessDefinitionId(346L).withType(BARResourceType.EXTERNAL).build());\n        SBARResource resource2 = repository\n                .add(aBARResource().withName(\"MyResource3\").withContent(\"The content3\".getBytes())\n                        .withProcessDefinitionId(345L).withType(BARResourceType.EXTERNAL).build());\n        repository.add(aBARResource().withName(\"MyConnector\").withContent(\"The content\".getBytes())\n                .withProcessDefinitionId(345L).withType(BARResourceType.CONNECTOR).build());\n\n        //when\n        List<SBARResourceLight> myResources = repository.getBARResourcesLightOfType(345L, BARResourceType.EXTERNAL);\n        // //then\n        assertThat(myResources).extracting(\"id\").containsOnly(resource1.getId(), resource2.getId());\n        for (SBARResourceLight myResource : myResources) {\n            assertThat(myResource).isInstanceOf(SBARResourceLight.class);\n            assertThat(myResource).isNotInstanceOf(SBARResource.class);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/resources/TenantResourceServiceQueriesTest.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.resources;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.tuple;\nimport static org.bonitasoft.engine.test.persistence.builder.TenantResourceBuilder.aTenantResource;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.test.persistence.repository.TenantResourceRepository;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.transaction.annotation.Transactional;\n\n@RunWith(SpringRunner.class)\n@ContextConfiguration(locations = { \"/testContext.xml\" })\n@Transactional\npublic class TenantResourceServiceQueriesTest {\n\n    @Autowired\n    private TenantResourceRepository repository;\n\n    @Autowired\n    private JdbcTemplate jdbcTemplate;\n\n    @Test\n    public void getTenantResource_should_get_one_resource() {\n        // given\n        STenantResource resource = repository.add(aTenantResource().withName(\"MyResource\")\n                .withContent(\"The content\".getBytes()).withType(TenantResourceType.BDM)\n                .withState(STenantResourceState.INSTALLED).lastUpdatedBy(135L).withLastUpdateDate(1973L).build());\n        repository.add(aTenantResource().withName(\"MyResource2\").withContent(\"The content@\".getBytes())\n                .withType(TenantResourceType.BDM).build());\n\n        // This check is to ensure we added the annotation @Enumerated(EnumType.STRING) on field 'state',\n        // because by default Hibernate converts ENUMs with their ordinal value when storing to Database:\n        repository.flush();\n        assertThat(jdbcTemplate.queryForObject(\"select state from tenant_resource where name = 'MyResource'\",\n                String.class)).isEqualTo(\"INSTALLED\");\n        assertThat(\n                jdbcTemplate.queryForObject(\"select type from tenant_resource where name = 'MyResource'\", String.class))\n                .isEqualTo(\"BDM\");\n\n        //when\n        STenantResource myResource = repository.getTenantResource(TenantResourceType.BDM, \"MyResource\");\n\n        // //then\n        assertThat(myResource.getContent()).isEqualTo(\"The content\".getBytes());\n        assertThat(myResource.lastUpdateDate).isEqualTo(1973L);\n        assertThat(myResource.lastUpdatedBy).isEqualTo(135L);\n        assertThat(myResource.state).isEqualTo(STenantResourceState.INSTALLED);\n        assertThat(myResource.getId()).isEqualTo(resource.getId());\n    }\n\n    @Test\n    public void getTenantResourceOfType_should_get_all_resource_of_type() {\n        // given\n        STenantResource resource1 = repository.add(aTenantResource().withName(\"MyResource\")\n                .withContent(\"The content\".getBytes()).withType(TenantResourceType.BDM).build());\n        STenantResource resource2 = repository.add(aTenantResource().withName(\"MyResource2\")\n                .withContent(\"The content@\".getBytes()).withType(TenantResourceType.BDM).build());\n        STenantResource resource3 = repository.add(aTenantResource().withName(\"MyResource3\")\n                .withContent(\"The content3\".getBytes()).withType(TenantResourceType.BDM).build());\n        STenantResource resource4 = repository.add(aTenantResource().withName(\"MyConnector\")\n                .withContent(\"The content\".getBytes()).withType(TenantResourceType.BDM).build());\n\n        //when\n        List<STenantResource> myResources = repository.getTenantResourcesOfType(TenantResourceType.BDM);\n        // //then\n        assertThat(myResources).extracting(\"id\", \"content\").containsOnly(\n                tuple(resource1.getId(), resource1.getContent()),\n                tuple(resource2.getId(), resource2.getContent()),\n                tuple(resource3.getId(), resource3.getContent()),\n                tuple(resource4.getId(), resource4.getContent()));\n    }\n\n    @Test\n    public void getNumberTenantResourceOfType_should_get_all_resource_of_type() {\n        // given\n        repository.add(aTenantResource().withName(\"MyResource\").withContent(\"The content\".getBytes())\n                .withType(TenantResourceType.BDM).build());\n        repository.add(aTenantResource().withName(\"MyResource2\").withContent(\"The content@\".getBytes())\n                .withType(TenantResourceType.BDM).build());\n        repository.add(aTenantResource().withName(\"MyResource3\").withContent(\"The content3\".getBytes())\n                .withType(TenantResourceType.BDM).build());\n        repository.add(aTenantResource().withName(\"MyConnector\").withContent(\"The content\".getBytes())\n                .withType(TenantResourceType.BDM).build());\n\n        // This resources should be excluded by the type filter:\n        repository.add(aTenantResource().withName(\"excluded\").withContent(\"binary\".getBytes())\n                .withType(TenantResourceType.BDM_ACCESS_CTRL).build());\n\n        //when\n        long myResources = repository.getNumberOfTenantResourcesOfType(TenantResourceType.BDM);\n        // //then\n        assertThat(myResources).isEqualTo(4);\n    }\n\n    @Test\n    public void getTenantResourceLightOfType_should_get_all_resource_of_type_without_content() {\n        // given\n        STenantResource resource1 = repository.add(aTenantResource().withName(\"MyResource\")\n                .withContent(\"The content\".getBytes()).withType(TenantResourceType.BDM).build());\n\n        // This resources should be excluded by the type filter:\n        repository.add(aTenantResource().withName(\"excluded\").withContent(\"binary\".getBytes())\n                .withType(TenantResourceType.BDM_ACCESS_CTRL).build());\n\n        STenantResource resource2 = repository.add(aTenantResource().withName(\"MyResource2\")\n                .withContent(\"The content@\".getBytes()).withType(TenantResourceType.BDM).build());\n        STenantResource resource3 = repository.add(aTenantResource().withName(\"MyResource3\")\n                .withContent(\"The content3\".getBytes()).withType(TenantResourceType.BDM).build());\n        STenantResource resource4 = repository.add(aTenantResource().withName(\"MyConnector\")\n                .withContent(\"The content\".getBytes()).withType(TenantResourceType.BDM).build());\n\n        //when\n        List<STenantResourceLight> myResources = repository.getTenantResourcesLightOfType(TenantResourceType.BDM);\n        // //then\n        assertThat(myResources).extracting(\"id\").containsOnly(resource1.getId(),\n                resource2.getId(),\n                resource3.getId(),\n                resource4.getId());\n        for (STenantResourceLight myResource : myResources) {\n            assertThat(myResource).isInstanceOf(STenantResourceLight.class);\n            assertThat(myResource).isNotInstanceOf(STenantResource.class);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/scheduler/SchedulerQueryTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.bonitasoft.engine.commons.Pair.pair;\nimport static org.bonitasoft.engine.test.persistence.builder.JobDescriptorBuilder.aJobDescriptor;\nimport static org.bonitasoft.engine.test.persistence.builder.JobLogBuilder.aJobLog;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.scheduler.model.SFailedJob;\nimport org.bonitasoft.engine.scheduler.model.SJobDescriptor;\nimport org.bonitasoft.engine.scheduler.model.SJobLog;\nimport org.bonitasoft.engine.scheduler.model.SJobParameter;\nimport org.bonitasoft.engine.test.persistence.jdbc.JdbcRowMapper;\nimport org.bonitasoft.engine.test.persistence.repository.JobRepository;\nimport org.hibernate.internal.util.SerializationHelper;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.transaction.annotation.Transactional;\n\n@RunWith(SpringRunner.class)\n@ContextConfiguration(locations = { \"/testContext.xml\" })\n@Transactional\npublic class SchedulerQueryTest {\n\n    @Autowired\n    private JobRepository jobRepository;\n    @Autowired\n    private JdbcTemplate jdbcTemplate;\n\n    @Test\n    public void getFailedJobsShouldRetrieveAFailedJobIfThereIsAJobLogAndAJobDescriptor() {\n        // given:\n        SJobDescriptor addJobDescriptor = jobRepository.add(aJobDescriptor().build());\n        jobRepository.add(aJobLog().withJobDescriptorId(addJobDescriptor.getId()).build());\n\n        // when:\n        List<SFailedJob> failedJobs = jobRepository.getFailedJobs(new QueryOptions(0, 10));\n\n        // then:\n        assertThat(failedJobs).hasSize(1);\n    }\n\n    @Test\n    public void getFailedJobsShouldRetrieveZeroFailedJobIfThereIsNoJobLog() {\n        // given:\n        jobRepository.add(aJobDescriptor().build());\n\n        // when:\n        List<SFailedJob> failedJobs = jobRepository.getFailedJobs(new QueryOptions(0, 10));\n\n        // then:\n        assertThat(failedJobs).hasSize(0);\n    }\n\n    @Test\n    public void getFailedJobsShouldRetrieveZeroFailedJobIfThereIsNoJobDescriptor() {\n        // given:\n        jobRepository.add(aJobLog().withJobDescriptorId(1234566789L).build());\n\n        // when:\n        List<SFailedJob> failedJobs = jobRepository.getFailedJobs(new QueryOptions(0, 10));\n\n        // then:\n        assertThat(failedJobs).hasSize(0);\n    }\n\n    @Test\n    public void should_save_and_get_SJobDescriptor() {\n        SJobDescriptor jobDescriptor = jobRepository.add(SJobDescriptor.builder()\n                .jobClassName(\"com.bonitasoft.JobClass\")\n                .description(\"a job\")\n                .jobName(\"job name\")\n                .build());\n        jobRepository.flush();\n\n        Long numberOfSJobDescriptor = jobRepository.selectCount(\"getNumberOfSJobDescriptor\");\n        PersistentObject searchSJobDescriptor = jobRepository.selectOne(\"searchSJobDescriptor\");\n        Map<String, Object> jobDescriptorAsMap = jdbcTemplate.queryForObject(\"SELECT * FROM job_desc\",\n                new JdbcRowMapper(\"ID\"));\n\n        assertThat(numberOfSJobDescriptor).isEqualTo(1);\n        assertThat(searchSJobDescriptor).isEqualTo(jobDescriptor);\n        assertThat(jobDescriptorAsMap).containsOnly(\n                entry(\"ID\", jobDescriptor.getId()),\n                entry(\"JOBCLASSNAME\", \"com.bonitasoft.JobClass\"),\n                entry(\"JOBNAME\", \"job name\"),\n                entry(\"DESCRIPTION\", \"a job\"));\n    }\n\n    @Test\n    public void should_save_and_get_SJobParameter() {\n        SJobParameter jobParameter = jobRepository.add(SJobParameter.builder()\n                .jobDescriptorId(1234L)\n                .key(\"paramKeyName\")\n                .value(\"Some string value\")\n                .build());\n\n        Long numberOfSJobParameters = jobRepository.selectCount(\"getNumberOfSJobParameter\");\n        PersistentObject jobParameterFromQuery = jobRepository.selectOne(\"getJobParameters\",\n                pair(\"jobDescriptorId\", 1234L));\n        Map<String, Object> jobParameterAsMap = jdbcTemplate.queryForObject(\"SELECT * FROM job_param\",\n                new JdbcRowMapper(\"ID\", \"JOBDESCRIPTORID\"));\n\n        assertThat(numberOfSJobParameters).isEqualTo(1);\n        assertThat(jobParameterFromQuery).isEqualTo(jobParameter);\n\n        assertThat(jobParameterAsMap).containsOnly(\n                entry(\"ID\", jobParameter.getId()),\n                entry(\"JOBDESCRIPTORID\", 1234L),\n                entry(\"KEY_\", \"paramKeyName\"),\n                entry(\"VALUE_\", SerializationHelper.serialize(\"Some string value\")));\n    }\n\n    @Test\n    public void should_save_and_get_SJobLog() {\n        SJobLog jobLog = jobRepository.add(SJobLog.builder()\n                .jobDescriptorId(1234L)\n                .lastMessage(\"the last message\")\n                .retryNumber(13L)\n                .lastUpdateDate(555555L)\n                .build());\n\n        Long numberOfSJobLog = jobRepository.selectCount(\"getNumberOfSJobLog\");\n        PersistentObject jobLogFromQuery = jobRepository.selectOne(\"searchSJobLog\");\n        Map<String, Object> jobLogAsMap = jdbcTemplate.queryForObject(\"SELECT * FROM job_log\",\n                new JdbcRowMapper(\"ID\", \"JOBDESCRIPTORID\", \"RETRYNUMBER\", \"LASTUPDATEDATE\"));\n\n        assertThat(numberOfSJobLog).isEqualTo(1);\n        assertThat(jobLogFromQuery).isEqualTo(jobLog);\n        assertThat(jobLogAsMap).containsOnly(\n                entry(\"ID\", jobLog.getId()),\n                entry(\"JOBDESCRIPTORID\", 1234L),\n                entry(\"RETRYNUMBER\", 13L),\n                entry(\"LASTUPDATEDATE\", 555555L),\n                entry(\"LASTMESSAGE\", \"the last message\"));\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/TestLocalSessionFactoryBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence;\n\nimport org.bonitasoft.engine.persistence.PostgresMaterializedBlobType;\nimport org.bonitasoft.engine.persistence.PostgresMaterializedClobType;\nimport org.bonitasoft.engine.persistence.PostgresXMLType;\nimport org.bonitasoft.engine.persistence.XMLType;\nimport org.bonitasoft.engine.services.Vendor;\nimport org.hibernate.Interceptor;\nimport org.hibernate.SessionFactory;\nimport org.springframework.orm.hibernate5.LocalSessionFactoryBean;\nimport org.springframework.orm.hibernate5.LocalSessionFactoryBuilder;\n\npublic class TestLocalSessionFactoryBuilder extends LocalSessionFactoryBean {\n\n    private Interceptor interceptor;\n\n    @Override\n    protected SessionFactory buildSessionFactory(LocalSessionFactoryBuilder sfb) {\n        Vendor vendor = Vendor.fromHibernateConfiguration(sfb);\n        //register type before loading mappings/entities, type should be present before loading JPA entities\n        switch (vendor) {\n            case ORACLE, OTHER, SQLSERVER:\n                sfb.registerTypeOverride(XMLType.INSTANCE);\n                break;\n            case MYSQL:\n                System.setProperty(\"hibernate.dialect.storage_engine\", \"innodb\");\n                sfb.registerTypeOverride(XMLType.INSTANCE);\n                break;\n            case POSTGRES:\n                sfb.registerTypeOverride(new PostgresMaterializedBlobType());\n                sfb.registerTypeOverride(new PostgresMaterializedClobType());\n                sfb.registerTypeOverride(PostgresXMLType.INSTANCE);\n                break;\n        }\n        if (interceptor != null) {\n            sfb.setInterceptor(interceptor);\n        }\n        return super.buildSessionFactory(sfb);\n    }\n\n    public void setInterceptor(Interceptor interceptor) {\n        this.interceptor = interceptor;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/ActivityInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\n\n/**\n * @author Julien Reboul\n */\npublic abstract class ActivityInstanceBuilder<T extends SActivityInstance, B extends ActivityInstanceBuilder<T, B>>\n        extends FlowNodeInstanceBuilder<T, B> {\n\n    protected long abortedByBoundaryEventId = 0;\n\n    public B withAbortedByBoundary(final long abortedByBoundaryEventId) {\n        this.abortedByBoundaryEventId = abortedByBoundaryEventId;\n        return thisBuilder;\n    }\n\n    @Override\n    protected T fill(T persistent) {\n        super.fill(persistent);\n        persistent.setAbortedByBoundary(abortedByBoundaryEventId);\n        return persistent;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/ActorBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.builder;\n\nimport org.bonitasoft.engine.actor.mapping.model.SActor;\n\npublic class ActorBuilder extends PersistentObjectBuilder<SActor, ActorBuilder> {\n\n    private long scopeId;\n    private boolean initiator;\n\n    @Override\n    ActorBuilder getThisBuilder() {\n\n        return this;\n    }\n\n    public ActorBuilder withScopeId(long scopeId) {\n        this.scopeId = scopeId;\n        return this;\n    }\n\n    public ActorBuilder whoIsInitiator() {\n        this.initiator = true;\n        return this;\n    }\n\n    public static ActorBuilder anActor() {\n        return new ActorBuilder();\n    }\n\n    @Override\n    SActor _build() {\n        SActor sActor = new SActor();\n        sActor.setInitiator(initiator);\n        sActor.setScopeId(scopeId);\n        return sActor;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/ActorMemberBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.builder;\n\nimport org.bonitasoft.engine.actor.mapping.model.SActor;\nimport org.bonitasoft.engine.actor.mapping.model.SActorMember;\n\npublic class ActorMemberBuilder extends PersistentObjectBuilder<SActorMember, ActorMemberBuilder> {\n\n    private long actorId;\n    private Long userId;\n    private Long groupId;\n    private Long roleId;\n\n    @Override\n    ActorMemberBuilder getThisBuilder() {\n        return this;\n    }\n\n    public static ActorMemberBuilder anActorMember() {\n        return new ActorMemberBuilder();\n    }\n\n    @Override\n    SActorMember _build() {\n        SActorMember actorMember = new SActorMember();\n        actorMember.setActorId(actorId);\n        if (userId != null)\n            actorMember.setUserId(userId);\n        if (groupId != null)\n            actorMember.setGroupId(groupId);\n        if (roleId != null)\n            actorMember.setRoleId(roleId);\n        return actorMember;\n    }\n\n    public ActorMemberBuilder forActor(SActor actor) {\n        this.actorId = actor.getId();\n        return this;\n    }\n\n    public ActorMemberBuilder withUserId(long userId) {\n        this.userId = userId;\n        return this;\n    }\n\n    public ActorMemberBuilder withGroupId(long groupId) {\n        this.groupId = groupId;\n        return this;\n    }\n\n    public ActorMemberBuilder withRoleId(long roleId) {\n        this.roleId = roleId;\n        return this;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/ApplicationBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.builder;\n\nimport org.bonitasoft.engine.business.application.model.AbstractSApplication;\nimport org.bonitasoft.engine.business.application.model.SApplicationState;\nimport org.bonitasoft.engine.business.application.model.SApplicationWithIcon;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ApplicationBuilder extends PersistentObjectBuilder<AbstractSApplication, ApplicationBuilder> {\n\n    private String name;\n    private String version;\n    private String path;\n    private String displayName;\n    private Long layoutId;\n    private Long themeId;\n    private Long profileId;\n\n    public static ApplicationBuilder anApplication() {\n        return new ApplicationBuilder();\n    }\n\n    @Override\n    SApplicationWithIcon _build() {\n        SApplicationWithIcon application = new SApplicationWithIcon(name, displayName, version,\n                System.currentTimeMillis(), 21,\n                SApplicationState.DEACTIVATED.name());\n        application.setIconPath(path);\n        application.setProfileId(profileId);\n        application.setThemeId(themeId);\n        application.setLayoutId(layoutId);\n        return application;\n    }\n\n    public ApplicationBuilder withToken(final String name) {\n        this.name = name;\n        return this;\n    }\n\n    public ApplicationBuilder withDisplayName(final String displayName) {\n        this.displayName = displayName;\n        return this;\n    }\n\n    public ApplicationBuilder withVersion(final String version) {\n        this.version = version;\n        return this;\n    }\n\n    public ApplicationBuilder withPath(final String path) {\n        this.path = path;\n        return this;\n    }\n\n    public ApplicationBuilder withLayout(final Long layoutId) {\n        this.layoutId = layoutId;\n        return this;\n    }\n\n    public ApplicationBuilder withTheme(final Long themeId) {\n        this.themeId = themeId;\n        return this;\n    }\n\n    public ApplicationBuilder withProfile(final long profileId) {\n        this.profileId = profileId;\n        return this;\n    }\n\n    @Override\n    ApplicationBuilder getThisBuilder() {\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/ApplicationMenuBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.builder;\n\nimport org.bonitasoft.engine.business.application.model.SApplicationMenu;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ApplicationMenuBuilder extends PersistentObjectBuilder<SApplicationMenu, ApplicationMenuBuilder> {\n\n    public static ApplicationMenuBuilder anApplicationMenu() {\n        return new ApplicationMenuBuilder();\n    }\n\n    private String displayName;\n    private long applicationId;\n    private Long applicationPageId;\n    private int index;\n    private Long parentId;\n\n    @Override\n    SApplicationMenu _build() {\n        final SApplicationMenu menu = new SApplicationMenu(displayName, applicationId, applicationPageId, index);\n        menu.setParentId(parentId);\n        return menu;\n    }\n\n    public ApplicationMenuBuilder withDisplayName(final String displayName) {\n        this.displayName = displayName;\n        return this;\n    }\n\n    public ApplicationMenuBuilder withApplicationId(final Long applicationId) {\n        this.applicationId = applicationId;\n        return this;\n    }\n\n    public ApplicationMenuBuilder withApplicationPageId(final Long applicationPageId) {\n        this.applicationPageId = applicationPageId;\n        return this;\n    }\n\n    public ApplicationMenuBuilder withIndex(final int index) {\n        this.index = index;\n        return this;\n    }\n\n    public ApplicationMenuBuilder withParentId(final long parentId) {\n        this.parentId = parentId;\n        return this;\n    }\n\n    @Override\n    ApplicationMenuBuilder getThisBuilder() {\n        return this;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/ApplicationPageBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.builder;\n\nimport org.bonitasoft.engine.business.application.model.SApplicationPage;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ApplicationPageBuilder extends PersistentObjectBuilder<SApplicationPage, ApplicationPageBuilder> {\n\n    private long applicationId;\n    private long pageId;\n    private String token;\n\n    public static ApplicationPageBuilder anApplicationPage() {\n        return new ApplicationPageBuilder();\n    }\n\n    @Override\n    SApplicationPage _build() {\n        return new SApplicationPage(applicationId, pageId, token);\n    }\n\n    public ApplicationPageBuilder withApplicationId(final long applicationId) {\n        this.applicationId = applicationId;\n        return this;\n    }\n\n    public ApplicationPageBuilder withPageId(final long pageId) {\n        this.pageId = pageId;\n        return this;\n    }\n\n    public ApplicationPageBuilder withToken(final String token) {\n        this.token = token;\n        return this;\n    }\n\n    @Override\n    ApplicationPageBuilder getThisBuilder() {\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/BARResourceBuilder.java",
    "content": "/**\n * Copyright (C) 2015 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.builder;\n\nimport org.bonitasoft.engine.resources.BARResourceType;\nimport org.bonitasoft.engine.resources.SBARResource;\n\npublic class BARResourceBuilder extends PersistentObjectBuilder<SBARResource, BARResourceBuilder> {\n\n    private String name;\n\n    private Long processDefinitionId;\n\n    private byte[] content;\n    private BARResourceType type;\n\n    public static BARResourceBuilder aBARResource() {\n        return new BARResourceBuilder();\n    }\n\n    @Override\n    public SBARResource _build() {\n        return new SBARResource(name, type, processDefinitionId, content);\n    }\n\n    public BARResourceBuilder withName(final String name) {\n        this.name = name;\n        return this;\n    }\n\n    public BARResourceBuilder withType(final BARResourceType type) {\n        this.type = type;\n        return this;\n    }\n\n    public BARResourceBuilder withContent(final byte[] content) {\n        this.content = content;\n        return this;\n    }\n\n    public BARResourceBuilder withProcessDefinitionId(final Long processDefinitionId) {\n        this.processDefinitionId = processDefinitionId;\n        return this;\n    }\n\n    @Override\n    BARResourceBuilder getThisBuilder() {\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/BoundaryInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance;\n\n/**\n * @author Baptiste Mesta\n */\npublic class BoundaryInstanceBuilder extends FlowNodeInstanceBuilder<SBoundaryEventInstance, BoundaryInstanceBuilder> {\n\n    private long activityInstanceId;\n\n    public static BoundaryInstanceBuilder aBoundary() {\n        return new BoundaryInstanceBuilder();\n    }\n\n    @Override\n    BoundaryInstanceBuilder getThisBuilder() {\n        return this;\n    }\n\n    @Override\n    SBoundaryEventInstance _build() {\n        final SBoundaryEventInstance boundaryEventInstance = new SBoundaryEventInstance(name, flowNodeDefinitionId,\n                rootContainerId, parentContainerId,\n                logicalGroup1, logicalGroup2);\n        boundaryEventInstance.setActivityInstanceId(activityInstanceId);\n        return boundaryEventInstance;\n    }\n\n    public BoundaryInstanceBuilder withActivity(final long activityId) {\n        this.activityInstanceId = activityId;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/CallActivityInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance;\n\n/**\n * @author Julien Reboul\n */\npublic class CallActivityInstanceBuilder\n        extends ActivityInstanceBuilder<SCallActivityInstance, CallActivityInstanceBuilder> {\n\n    @Override\n    CallActivityInstanceBuilder getThisBuilder() {\n        return this;\n    }\n\n    public static CallActivityInstanceBuilder aCallActivityInstanceBuilder() {\n        return new CallActivityInstanceBuilder();\n    }\n\n    @Override\n    SCallActivityInstance _build() {\n        return new SCallActivityInstance();\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/ConnectorInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.builder;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.core.process.instance.model.SAbstractConnectorInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstanceWithFailureInfo;\n\n/**\n * @author Julien Reboul\n */\npublic class ConnectorInstanceBuilder\n        extends PersistentObjectBuilder<SAbstractConnectorInstance, ConnectorInstanceBuilder> {\n\n    private boolean withFailureInfo = false;\n\n    private String state;\n\n    private String connectorId;\n\n    private ConnectorEvent activationEvent;\n\n    private long containerId;\n\n    private String containerType;\n\n    private String version;\n\n    private int executionOrder;\n\n    private String exceptionMessage;\n\n    private String stackTrace;\n\n    private String name;\n\n    public static ConnectorInstanceBuilder aConnectorInstance() {\n        return new ConnectorInstanceBuilder();\n    }\n\n    @Override\n    ConnectorInstanceBuilder getThisBuilder() {\n        return this;\n    }\n\n    @Override\n    SAbstractConnectorInstance _build() {\n        SAbstractConnectorInstance connectorInstance;\n        if (withFailureInfo) {\n            connectorInstance = new SConnectorInstanceWithFailureInfo();\n            ((SConnectorInstanceWithFailureInfo) connectorInstance).setExceptionMessage(exceptionMessage);\n            ((SConnectorInstanceWithFailureInfo) connectorInstance).setStackTrace(stackTrace);\n        } else {\n            connectorInstance = new SConnectorInstance();\n        }\n        connectorInstance.setState(state);\n        connectorInstance.setConnectorId(connectorId);\n        connectorInstance.setActivationEvent(activationEvent);\n        connectorInstance.setContainerId(containerId);\n        connectorInstance.setContainerType(containerType);\n        connectorInstance.setVersion(version);\n        connectorInstance.setExecutionOrder(executionOrder);\n        connectorInstance.setName(name);\n        return connectorInstance;\n    }\n\n    public ConnectorInstanceBuilder setName(final String name) {\n        this.name = name;\n        return this;\n    }\n\n    public ConnectorInstanceBuilder setState(final String state) {\n        this.state = state;\n        return this;\n    }\n\n    public ConnectorInstanceBuilder withFailureInfo(final boolean withFailureInfo) {\n        this.withFailureInfo = withFailureInfo;\n        return this;\n    }\n\n    public ConnectorInstanceBuilder setConnectorId(final String connectorId) {\n        this.connectorId = connectorId;\n        return this;\n    }\n\n    public ConnectorInstanceBuilder setActivationEvent(final ConnectorEvent activationEvent) {\n        this.activationEvent = activationEvent;\n        return this;\n    }\n\n    public ConnectorInstanceBuilder setContainerId(final long containerId) {\n        this.containerId = containerId;\n        return this;\n    }\n\n    public ConnectorInstanceBuilder setContainerType(final String containerType) {\n        this.containerType = containerType;\n        return this;\n    }\n\n    public ConnectorInstanceBuilder setVersion(final String version) {\n        this.version = version;\n        return this;\n    }\n\n    public ConnectorInstanceBuilder setExecutionOrder(final int executionOrder) {\n        this.executionOrder = executionOrder;\n        return this;\n    }\n\n    public ConnectorInstanceBuilder setExceptionMessage(String message) {\n        this.exceptionMessage = message;\n        return this;\n    }\n\n    public ConnectorInstanceBuilder setStackTrace(String stackTrace) {\n        this.stackTrace = stackTrace;\n        return this;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/CustomUserInfoDefinitionBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.builder;\n\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class CustomUserInfoDefinitionBuilder\n        extends PersistentObjectBuilder<SCustomUserInfoDefinition, CustomUserInfoDefinitionBuilder> {\n\n    private String name;\n\n    public static CustomUserInfoDefinitionBuilder aCustomUserInfoDefinition() {\n        return new CustomUserInfoDefinitionBuilder();\n    }\n\n    @Override\n    CustomUserInfoDefinitionBuilder getThisBuilder() {\n        return this;\n    }\n\n    @Override\n    SCustomUserInfoDefinition _build() {\n        SCustomUserInfoDefinition infoDef = new SCustomUserInfoDefinition();\n        infoDef.setName(name);\n        return infoDef;\n    }\n\n    public CustomUserInfoDefinitionBuilder withName(String name) {\n        this.name = name;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/CustomUserInfoValueBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.builder;\n\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoValue;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class CustomUserInfoValueBuilder\n        extends PersistentObjectBuilder<SCustomUserInfoValue, CustomUserInfoValueBuilder> {\n\n    private long infoDefId;\n    private long userId;\n    private String value;\n\n    public static CustomUserInfoValueBuilder aCustomUserInfoValue() {\n        return new CustomUserInfoValueBuilder();\n    }\n\n    @Override\n    CustomUserInfoValueBuilder getThisBuilder() {\n        return this;\n    }\n\n    @Override\n    SCustomUserInfoValue _build() {\n        SCustomUserInfoValue infoValueImpl = new SCustomUserInfoValue();\n        infoValueImpl.setUserId(userId);\n        infoValueImpl.setDefinitionId(infoDefId);\n        infoValueImpl.setValue(value);\n        return infoValueImpl;\n    }\n\n    public CustomUserInfoValueBuilder withCustomUserInfoDefinitionId(long infoDefId) {\n        this.infoDefId = infoDefId;\n        return this;\n    }\n\n    public CustomUserInfoValueBuilder withUserId(long userId) {\n        this.userId = userId;\n        return this;\n    }\n\n    public CustomUserInfoValueBuilder withValue(String value) {\n        this.value = value;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/FlowNodeInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\n\npublic abstract class FlowNodeInstanceBuilder<T extends SFlowNodeInstance, B extends FlowNodeInstanceBuilder<T, B>>\n        extends PersistentObjectBuilder<T, B> {\n\n    protected int stateId;\n\n    protected String stateName;\n\n    protected long lastUpdateDate;\n\n    protected String name;\n\n    protected int loopCounter;\n\n    protected long executedBy;\n\n    protected long executedBySubstitute;\n\n    protected boolean stateExecuting;\n\n    protected long flowNodeDefinitionId;\n\n    protected long rootContainerId;\n\n    protected long parentContainerId;\n\n    protected SStateCategory stateCategory = SStateCategory.NORMAL;\n\n    protected String description;\n\n    protected long logicalGroup1;\n\n    protected long logicalGroup2;\n\n    protected long logicalGroup3;\n\n    protected long logicalGroup4;\n\n    protected boolean terminal;\n\n    protected boolean stable;\n\n    @Override\n    protected T fill(final T persistent) {\n        super.fill(persistent);\n        persistent.setDescription(description);\n        persistent.setExecutedBy(executedBy);\n        persistent.setExecutedBySubstitute(executedBySubstitute);\n        persistent.setFlowNodeDefinitionId(flowNodeDefinitionId);\n        persistent.setLastUpdateDate(lastUpdateDate);\n        persistent.setLoopCounter(loopCounter);\n        persistent.setName(name);\n        persistent.setParentContainerId(parentContainerId);\n        persistent.setRootContainerId(rootContainerId);\n        persistent.setStable(stable);\n        persistent.setStateCategory(stateCategory);\n        persistent.setStateExecuting(stateExecuting);\n        persistent.setStateId(stateId);\n        persistent.setStateName(stateName);\n        persistent.setTerminal(terminal);\n        persistent.setLogicalGroup(0, logicalGroup1);\n        persistent.setLogicalGroup(1, logicalGroup2);\n        persistent.setLogicalGroup(2, logicalGroup3);\n        persistent.setLogicalGroup(3, logicalGroup4);\n        return persistent;\n    }\n\n    public B withFlowNodeDefinitionId(final long flowNodeDefinitionId) {\n        this.flowNodeDefinitionId = flowNodeDefinitionId;\n        return thisBuilder;\n    }\n\n    public B withTerminal(final boolean terminal) {\n        this.terminal = terminal;\n        return thisBuilder;\n    }\n\n    public B withStable(final boolean stable) {\n        this.stable = stable;\n        return thisBuilder;\n    }\n\n    public B withStateExecuting(final boolean stateExecuting) {\n        this.stateExecuting = stateExecuting;\n        return thisBuilder;\n    }\n\n    public B withExecutedBy(final long executedBy) {\n        this.executedBy = executedBy;\n        return thisBuilder;\n    }\n\n    public B withExecutedBySubstitute(final long executedBySubstitute) {\n        this.executedBySubstitute = executedBySubstitute;\n        return thisBuilder;\n    }\n\n    public B withId(final long id) {\n        this.id = id;\n        return thisBuilder;\n    }\n\n    public B withName(final String name) {\n        this.name = name;\n        return thisBuilder;\n    }\n\n    public B withStateName(final String stateName) {\n        this.stateName = stateName;\n        return thisBuilder;\n    }\n\n    public B withDescription(final String description) {\n        this.description = description;\n        return thisBuilder;\n    }\n\n    public B withStateId(final int stateId) {\n        this.stateId = stateId;\n        return thisBuilder;\n    }\n\n    public B withLastUpdateDate(final long lastUpdateDate) {\n        this.lastUpdateDate = lastUpdateDate;\n        return thisBuilder;\n    }\n\n    public B withRootContainerId(final long containerId) {\n        this.rootContainerId = containerId;\n        return thisBuilder;\n    }\n\n    public B withParentContainerId(final long processInstanceId) {\n        this.parentContainerId = processInstanceId;\n        return thisBuilder;\n    }\n\n    public B withLoopCounter(final int loopCounter) {\n        this.loopCounter = loopCounter;\n        return thisBuilder;\n    }\n\n    public B withStateCategory(final SStateCategory stateCategory) {\n        this.stateCategory = stateCategory;\n        return thisBuilder;\n    }\n\n    public B withLogicalGroup1(final long logicalGroup) {\n        this.logicalGroup1 = logicalGroup;\n        return thisBuilder;\n    }\n\n    public B withLogicalGroup2(final long logicalGroup) {\n        this.logicalGroup2 = logicalGroup;\n        return thisBuilder;\n    }\n\n    public B withLogicalGroup3(final long logicalGroup) {\n        this.logicalGroup3 = logicalGroup;\n        return thisBuilder;\n    }\n\n    public B withLogicalGroup4(final long logicalGroup) {\n        this.logicalGroup4 = logicalGroup;\n        return thisBuilder;\n    }\n\n    public B withProcessDefinition(long processDefinitionId) {\n        return withLogicalGroup1(processDefinitionId);\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/GatewayInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.builder;\n\nimport org.bonitasoft.engine.core.process.definition.model.SGatewayType;\nimport org.bonitasoft.engine.core.process.instance.model.SGatewayInstance;\n\n/**\n * @author Julien Reboul\n */\npublic class GatewayInstanceBuilder extends FlowNodeInstanceBuilder<SGatewayInstance, GatewayInstanceBuilder> {\n\n    private SGatewayType gatewayType;\n\n    private String hitBys = \"\";\n\n    public static GatewayInstanceBuilder aGatewayInstanceBuilder() {\n        return new GatewayInstanceBuilder();\n    }\n\n    private GatewayInstanceBuilder() {\n    }\n\n    @Override\n    GatewayInstanceBuilder getThisBuilder() {\n        return this;\n    }\n\n    @Override\n    SGatewayInstance _build() {\n        return new SGatewayInstance();\n    }\n\n    @Override\n    protected SGatewayInstance fill(SGatewayInstance persistent) {\n        super.fill(persistent);\n        persistent.setHitBys(hitBys);\n        persistent.setGatewayType(gatewayType);\n        return persistent;\n    }\n\n    public GatewayInstanceBuilder withGatewayType(final SGatewayType gatewayType) {\n        this.gatewayType = gatewayType;\n        return thisBuilder;\n    }\n\n    public GatewayInstanceBuilder withHitBys(final String hitBys) {\n        this.hitBys = hitBys;\n        return thisBuilder;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/GroupBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.builder;\n\nimport org.bonitasoft.engine.identity.model.SGroup;\n\n/**\n * @author Danila Mazour\n */\npublic class GroupBuilder extends PersistentObjectBuilder<SGroup, GroupBuilder> {\n\n    private String name;\n\n    private String parentPath;\n\n    public static GroupBuilder aGroup() {\n        return new GroupBuilder();\n    }\n\n    @Override\n    GroupBuilder getThisBuilder() {\n        return this;\n    }\n\n    @Override\n    SGroup _build() {\n        SGroup group = new SGroup();\n        group.setId(this.id);\n        group.setName(this.name);\n        group.setParentPath(parentPath);\n        return group;\n    }\n\n    public GroupBuilder forParentPath(String path) {\n        this.parentPath = path;\n        return this;\n    }\n\n    public GroupBuilder forGroupName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public GroupBuilder forGroupId(Long id) {\n        this.id = id;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/JobDescriptorBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.builder;\n\nimport org.bonitasoft.engine.scheduler.model.SJobDescriptor;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class JobDescriptorBuilder extends PersistentObjectBuilder<SJobDescriptor, JobDescriptorBuilder> {\n\n    public static JobDescriptorBuilder aJobDescriptor() {\n        return new JobDescriptorBuilder();\n    }\n\n    @Override\n    JobDescriptorBuilder getThisBuilder() {\n        return this;\n    }\n\n    @Override\n    SJobDescriptor _build() {\n        return new SJobDescriptor();\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/JobLogBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.builder;\n\nimport org.bonitasoft.engine.scheduler.model.SJobLog;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class JobLogBuilder extends PersistentObjectBuilder<SJobLog, JobLogBuilder> {\n\n    long jobDescriptorId;\n\n    public static JobLogBuilder aJobLog() {\n        return new JobLogBuilder();\n    }\n\n    @Override\n    JobLogBuilder getThisBuilder() {\n        return this;\n    }\n\n    @Override\n    SJobLog _build() {\n        // lastUpdateDate must not be null for the SFailedJobImpl constructor to be called normally:\n        SJobLog sJobLog = new SJobLog(jobDescriptorId);\n        sJobLog.setLastUpdateDate(System.currentTimeMillis());\n        return sJobLog;\n    }\n\n    public JobLogBuilder withJobDescriptorId(final long jobDescriptorId) {\n        this.jobDescriptorId = jobDescriptorId;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/LoopActivityInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance;\n\npublic class LoopActivityInstanceBuilder extends FlowNodeInstanceBuilder {\n\n    public static LoopActivityInstanceBuilder aLoopActivity() {\n        return new LoopActivityInstanceBuilder();\n    }\n\n    @Override\n    LoopActivityInstanceBuilder getThisBuilder() {\n        return this;\n    }\n\n    @Override\n    SLoopActivityInstance _build() {\n        final SLoopActivityInstance loopActivityInstance = new SLoopActivityInstance(name, flowNodeDefinitionId,\n                rootContainerId, parentContainerId, logicalGroup1, logicalGroup2);\n        loopActivityInstance.setLoopCounter(loopCounter);\n        return loopActivityInstance;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/MessageInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance;\n\n/**\n * @author Emmanuel Duchastenier\n * @author Laurent Leseigneur\n */\npublic class MessageInstanceBuilder extends PersistentObjectBuilder<SMessageInstance, MessageInstanceBuilder> {\n\n    public static MessageInstanceBuilder aMessageInstance() {\n        return new MessageInstanceBuilder();\n    }\n\n    @Override\n    MessageInstanceBuilder getThisBuilder() {\n        return this;\n    }\n\n    private boolean handled;\n    private long creationDate;\n\n    @Override\n    SMessageInstance _build() {\n        SMessageInstance messageInstance = new SMessageInstance();\n        messageInstance.setHandled(handled);\n        messageInstance.setCreationDate(creationDate);\n        return messageInstance;\n    }\n\n    public MessageInstanceBuilder handled(final boolean handled) {\n        this.handled = handled;\n        return this;\n    }\n\n    public MessageInstanceBuilder creationDate(final long creationDate) {\n        this.creationDate = creationDate;\n        return this;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/PageBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.builder;\n\nimport org.bonitasoft.engine.page.AbstractSPage;\nimport org.bonitasoft.engine.page.SPage;\nimport org.bonitasoft.engine.page.SPageWithContent;\n\npublic class PageBuilder extends PersistentObjectBuilder<AbstractSPage, PageBuilder> {\n\n    private String name;\n\n    private String description;\n\n    private String displayName;\n\n    private long installationDate;\n\n    private long installedBy;\n\n    private boolean provided;\n\n    private long lastModificationDate;\n\n    private long lastUpdatedBy;\n\n    private String contentName;\n\n    private String contentType;\n\n    private long processDefinitionId;\n\n    private byte[] content;\n    private boolean editable;\n\n    public static PageBuilder aPage() {\n        return new PageBuilder();\n    }\n\n    @Override\n    public SPageWithContent _build() {\n        final SPage sPage = new SPage(name, description, displayName, installationDate, installedBy, provided, editable,\n                lastModificationDate, lastUpdatedBy,\n                contentName);\n        sPage.setProcessDefinitionId(processDefinitionId);\n\n        return new SPageWithContent(sPage, content);\n    }\n\n    public PageBuilder withName(final String name) {\n        this.name = name;\n        return this;\n    }\n\n    public PageBuilder withDescription(final String description) {\n        this.description = description;\n        return this;\n    }\n\n    public PageBuilder withDisplayName(final String displayName) {\n        this.displayName = displayName;\n        return this;\n    }\n\n    public PageBuilder withInstallationDate(final long installationDate) {\n        this.installationDate = installationDate;\n        return this;\n    }\n\n    public PageBuilder withInstalledBy(final long installedBy) {\n        this.installedBy = installedBy;\n        return this;\n    }\n\n    public PageBuilder withProvided(final boolean provided) {\n        this.provided = provided;\n        return this;\n    }\n\n    public PageBuilder withLastModificationDate(final long lastModificationDate) {\n        this.lastModificationDate = lastModificationDate;\n        return this;\n    }\n\n    public PageBuilder withLastUpdatedBy(final long lastUpdatedBy) {\n        this.lastUpdatedBy = lastUpdatedBy;\n        return this;\n    }\n\n    public PageBuilder withContentName(final String contentName) {\n        this.contentName = contentName;\n        return this;\n    }\n\n    public PageBuilder withContent(final byte[] content) {\n        this.content = content;\n        return this;\n    }\n\n    public PageBuilder withContentType(final String contentType) {\n        this.contentType = contentType;\n        return this;\n    }\n\n    public PageBuilder withProcessDefinitionId(final Long processDefinitionId) {\n        this.processDefinitionId = processDefinitionId;\n        return this;\n    }\n\n    public PageBuilder makeEditable(Boolean editable) {\n        this.editable = editable;\n        return this;\n    }\n\n    @Override\n    PageBuilder getThisBuilder() {\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/PendingActivityMappingBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.builder;\n\nimport java.util.Random;\n\nimport org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping;\n\npublic class PendingActivityMappingBuilder\n        extends PersistentObjectBuilder<SPendingActivityMapping, PendingActivityMappingBuilder> {\n\n    private long activityId = new Random().nextLong();\n\n    private long userId;\n\n    private long actorId;\n\n    public static PendingActivityMappingBuilder aPendingActivityMapping() {\n        return new PendingActivityMappingBuilder();\n    }\n\n    @Override\n    PendingActivityMappingBuilder getThisBuilder() {\n        return this;\n    }\n\n    @Override\n    SPendingActivityMapping _build() {\n        return SPendingActivityMapping.builder().activityId(activityId)\n                .userId(userId)\n                .actorId(actorId).build();\n    }\n\n    public PendingActivityMappingBuilder withUserId(final long userId) {\n        this.userId = userId;\n        return this;\n    }\n\n    public PendingActivityMappingBuilder withActorId(final long actorId) {\n        this.actorId = actorId;\n        return this;\n    }\n\n    public PendingActivityMappingBuilder withActivityId(final long activityId) {\n        this.activityId = activityId;\n        return this;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/PersistentObjectBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.builder;\n\nimport java.util.Random;\n\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\npublic abstract class PersistentObjectBuilder<T extends PersistentObject, B extends PersistentObjectBuilder<T, B>> {\n\n    protected long id = new Random().nextLong();\n\n    protected T persistentObject;\n\n    protected B thisBuilder;\n\n    public PersistentObjectBuilder() {\n        thisBuilder = getThisBuilder();\n    }\n\n    public T build() {\n        persistentObject = _build();\n        fill(persistentObject);\n        return persistentObject;\n    }\n\n    protected T fill(T persistent) {\n        persistent.setId(id);\n        return persistent;\n    }\n\n    abstract B getThisBuilder();\n\n    abstract T _build();\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/ProcessInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.builder;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\n\npublic class ProcessInstanceBuilder extends PersistentObjectBuilder<SProcessInstance, ProcessInstanceBuilder> {\n\n    private long processDefinitionId;\n\n    private String name;\n\n    private String description;\n\n    private int stateId;\n\n    private long startDate;\n\n    private long startedBy;\n\n    private long startedBySubstitute;\n\n    private long endDate;\n\n    private long lastUpdate;\n\n    private long containerId;\n\n    private long rootProcessInstanceId = -1;\n\n    private long callerId = -1;\n\n    private SFlowNodeType callerType;\n\n    private long interruptingEventId = -1;\n\n    private SStateCategory stateCategory;\n\n    @Override\n    ProcessInstanceBuilder getThisBuilder() {\n        return this;\n    }\n\n    public static ProcessInstanceBuilder aProcessInstance() {\n        return new ProcessInstanceBuilder();\n    }\n\n    @Override\n    SProcessInstance _build() {\n        SProcessInstance processInstance = new SProcessInstance(name, processDefinitionId);\n        processInstance.setCallerId(this.callerId);\n        processInstance.setCallerType(this.callerType);\n        processInstance.setContainerId(this.containerId);\n        processInstance.setDescription(description);\n        processInstance.setEndDate(endDate);\n        processInstance.setInterruptingEventId(interruptingEventId);\n        processInstance.setLastUpdate(lastUpdate);\n        processInstance.setRootProcessInstanceId(rootProcessInstanceId);\n        processInstance.setStartDate(startDate);\n        processInstance.setStartedBy(startedBy);\n        processInstance.setStartedBySubstitute(startedBySubstitute);\n        processInstance.setStateCategory(stateCategory);\n        processInstance.setStateId(stateId);\n        return processInstance;\n    }\n\n    public ProcessInstanceBuilder withId(long id) {\n        this.id = id;\n        return this;\n    }\n\n    public ProcessInstanceBuilder withName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public ProcessInstanceBuilder withProcessDefinitionId(long processDefinitionId) {\n        this.processDefinitionId = processDefinitionId;\n        return this;\n    }\n\n    public ProcessInstanceBuilder withDescription(final String description) {\n        this.description = description;\n        return this;\n    }\n\n    public ProcessInstanceBuilder withStateId(final int stateId) {\n        this.stateId = stateId;\n        return this;\n    }\n\n    public ProcessInstanceBuilder withStartDate(final long startDate) {\n        this.startDate = startDate;\n        return this;\n    }\n\n    public ProcessInstanceBuilder withStartedBy(final long startedBy) {\n        this.startedBy = startedBy;\n        return this;\n    }\n\n    public ProcessInstanceBuilder withStartedBySubstitute(final long startedBySubstitute) {\n        this.startedBySubstitute = startedBySubstitute;\n        return this;\n    }\n\n    public ProcessInstanceBuilder withEndDate(final long endDate) {\n        this.endDate = endDate;\n        return this;\n    }\n\n    public ProcessInstanceBuilder withLastUpdate(final long lastUpdate) {\n        this.lastUpdate = lastUpdate;\n        return this;\n    }\n\n    public ProcessInstanceBuilder withContainerId(final long containerId) {\n        this.containerId = containerId;\n        return this;\n    }\n\n    public ProcessInstanceBuilder withRootProcessInstanceId(final long rootProcessInstanceId) {\n        this.rootProcessInstanceId = rootProcessInstanceId;\n        return this;\n    }\n\n    public ProcessInstanceBuilder withCallerId(final long callerId) {\n        this.callerId = callerId;\n        return this;\n    }\n\n    public ProcessInstanceBuilder withCallerType(final SFlowNodeType callerType) {\n        this.callerType = callerType;\n        return this;\n    }\n\n    public ProcessInstanceBuilder withInterruptingEventId(final long interruptingEventId) {\n        this.interruptingEventId = interruptingEventId;\n        return this;\n    }\n\n    public ProcessInstanceBuilder withStateCategory(final SStateCategory processStateCategory) {\n        stateCategory = processStateCategory;\n        return this;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/ProfileBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.builder;\n\nimport org.bonitasoft.engine.profile.model.SProfile;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ProfileBuilder extends PersistentObjectBuilder<SProfile, ProfileBuilder> {\n\n    private String name;\n\n    public static ProfileBuilder aProfile() {\n        return new ProfileBuilder();\n    }\n\n    @Override\n    ProfileBuilder getThisBuilder() {\n        return this;\n    }\n\n    @Override\n    SProfile _build() {\n        return SProfile.builder().name(name).build();\n    }\n\n    public ProfileBuilder withName(String name) {\n        this.name = name;\n        return this;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/ProfileMemberBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.builder;\n\nimport org.bonitasoft.engine.profile.model.SProfileMember;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class ProfileMemberBuilder extends PersistentObjectBuilder<SProfileMember, ProfileMemberBuilder> {\n\n    private long userId = -1;\n\n    private long groupId = -1;\n\n    private long roleId = -1;\n\n    private long profileId;\n\n    public static ProfileMemberBuilder aProfileMember() {\n        return new ProfileMemberBuilder();\n    }\n\n    @Override\n    ProfileMemberBuilder getThisBuilder() {\n        return this;\n    }\n\n    @Override\n    SProfileMember _build() {\n        return SProfileMember.builder()\n                .profileId(profileId)\n                .groupId(groupId)\n                .roleId(roleId)\n                .userId(userId).build();\n    }\n\n    public ProfileMemberBuilder withProfileId(long profileId) {\n        this.profileId = profileId;\n        return this;\n    }\n\n    public ProfileMemberBuilder withUserId(long userId) {\n        this.userId = userId;\n        return this;\n    }\n\n    public ProfileMemberBuilder withGroupId(long groupId) {\n        this.groupId = groupId;\n        return this;\n    }\n\n    public ProfileMemberBuilder withRoleId(long roleId) {\n        this.roleId = roleId;\n        return this;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/RoleBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.builder;\n\nimport org.bonitasoft.engine.identity.model.SRole;\n\n/**\n * @author Danila Mazour\n */\npublic class RoleBuilder extends PersistentObjectBuilder<SRole, RoleBuilder> {\n\n    private String name;\n\n    public static RoleBuilder aRole() {\n        return new RoleBuilder();\n    }\n\n    @Override\n    RoleBuilder getThisBuilder() {\n        return this;\n    }\n\n    @Override\n    SRole _build() {\n        SRole role = new SRole();\n        role.setName(this.name);\n        role.setId(this.id);\n        return role;\n    }\n\n    public RoleBuilder forRoleName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public RoleBuilder forRoleId(Long id) {\n        this.id = id;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/SupervisorBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.builder;\n\nimport org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor;\n\npublic class SupervisorBuilder extends PersistentObjectBuilder<SProcessSupervisor, SupervisorBuilder> {\n\n    private long processDefId;\n\n    private long groupId = 0;\n\n    private long roleId = 0;\n\n    private long userId = 0;\n\n    public static SupervisorBuilder aSupervisor() {\n        return new SupervisorBuilder();\n    }\n\n    @Override\n    SupervisorBuilder getThisBuilder() {\n        return this;\n    }\n\n    @Override\n    public SProcessSupervisor _build() {\n        final SProcessSupervisor user = new SProcessSupervisor();\n        user.setId(id);\n        user.setGroupId(groupId);\n        user.setRoleId(roleId);\n        user.setUserId(userId);\n        user.setProcessDefId(processDefId);\n        return user;\n    }\n\n    public SupervisorBuilder withProcessDefinitionId(final long processDefId) {\n        this.processDefId = processDefId;\n        return this;\n    }\n\n    public SupervisorBuilder withId(final long id) {\n        this.id = id;\n        return this;\n    }\n\n    public SupervisorBuilder withGroupId(final long groupId) {\n        this.groupId = groupId;\n        return this;\n    }\n\n    public SupervisorBuilder withUserId(final long userId) {\n        this.userId = userId;\n        return this;\n    }\n\n    public SupervisorBuilder withroleId(final long roleId) {\n        this.roleId = roleId;\n        return this;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/TenantResourceBuilder.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.builder;\n\nimport org.bonitasoft.engine.resources.STenantResource;\nimport org.bonitasoft.engine.resources.STenantResourceState;\nimport org.bonitasoft.engine.resources.TenantResourceType;\n\npublic class TenantResourceBuilder extends PersistentObjectBuilder<STenantResource, TenantResourceBuilder> {\n\n    private String name;\n\n    private byte[] content;\n    private TenantResourceType type;\n\n    private long lastUpdatedBy;\n    private long lastUpdateDate;\n    private STenantResourceState state;\n\n    public static TenantResourceBuilder aTenantResource() {\n        return new TenantResourceBuilder();\n    }\n\n    @Override\n    public STenantResource _build() {\n        return new STenantResource(name, type, content, lastUpdatedBy, lastUpdateDate, state);\n    }\n\n    public TenantResourceBuilder withName(final String name) {\n        this.name = name;\n        return this;\n    }\n\n    public TenantResourceBuilder withType(final TenantResourceType type) {\n        this.type = type;\n        return this;\n    }\n\n    public TenantResourceBuilder withContent(final byte[] content) {\n        this.content = content;\n        return this;\n    }\n\n    public TenantResourceBuilder lastUpdatedBy(final long userId) {\n        this.lastUpdatedBy = userId;\n        return this;\n    }\n\n    public TenantResourceBuilder withState(STenantResourceState state) {\n        this.state = state;\n        return this;\n    }\n\n    public TenantResourceBuilder withLastUpdateDate(long date) {\n        this.lastUpdateDate = date;\n        return this;\n    }\n\n    @Override\n    TenantResourceBuilder getThisBuilder() {\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/UserBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.builder;\n\nimport org.bonitasoft.engine.identity.model.SUser;\n\npublic class UserBuilder extends PersistentObjectBuilder<SUser, UserBuilder> {\n\n    private String userName = \"userName\" + id;\n\n    private long managerUserId = 0;\n\n    public static UserBuilder aUser() {\n        return new UserBuilder();\n    }\n\n    @Override\n    UserBuilder getThisBuilder() {\n        return this;\n    }\n\n    @Override\n    public SUser _build() {\n        SUser user = new SUser();\n        user.setFirstName(\"aFirstName\" + id);\n        user.setLastName(\"aLastName\" + id);\n        user.setUserName(userName);\n        user.setManagerUserId(managerUserId);\n        user.setEnabled(true);\n        return user;\n    }\n\n    public UserBuilder withId(long id) {\n        this.id = id;\n        return this;\n    }\n\n    public UserBuilder withUserName(String userName) {\n        this.userName = userName;\n        return this;\n    }\n\n    public UserBuilder withManager(long managerUserId) {\n        this.managerUserId = managerUserId;\n        return this;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/UserMembershipBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.builder;\n\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.identity.model.SUserMembership;\n\npublic class UserMembershipBuilder extends PersistentObjectBuilder<SUserMembership, UserMembershipBuilder> {\n\n    private long groupId;\n\n    private long userId;\n\n    private long roleId;\n\n    public static UserMembershipBuilder aUserMembership() {\n        return new UserMembershipBuilder();\n    }\n\n    @Override\n    UserMembershipBuilder getThisBuilder() {\n        return this;\n    }\n\n    @Override\n    SUserMembership _build() {\n        SUserMembership membership = new SUserMembership();\n        membership.setGroupId(groupId);\n        membership.setUserId(userId);\n        membership.setRoleId(roleId);\n        return membership;\n    }\n\n    public UserMembershipBuilder forUser(final SUser user) {\n        this.userId = user.getId();\n        return this;\n    }\n\n    public UserMembershipBuilder forUser(final long userId) {\n        this.userId = userId;\n        return this;\n    }\n\n    public UserMembershipBuilder memberOf(final long groupId, final long roleId) {\n        this.groupId = groupId;\n        this.roleId = roleId;\n        return this;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/UserTaskInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.STaskPriority;\nimport org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance;\n\n/**\n * @author Julien Reboul\n */\npublic class UserTaskInstanceBuilder extends ActivityInstanceBuilder<SUserTaskInstance, UserTaskInstanceBuilder> {\n\n    public static UserTaskInstanceBuilder aUserTask() {\n        return new UserTaskInstanceBuilder();\n    }\n\n    private long assigneeId;\n\n    private long actorId = 10;\n\n    @Override\n    UserTaskInstanceBuilder getThisBuilder() {\n        return this;\n    }\n\n    @Override\n    SUserTaskInstance _build() {\n        final SUserTaskInstance userTaskInstanceImpl = new SUserTaskInstance(name, flowNodeDefinitionId,\n                rootContainerId, parentContainerId, actorId,\n                STaskPriority.NORMAL, logicalGroup1, logicalGroup2);\n        userTaskInstanceImpl.setAssigneeId(assigneeId);\n        return userTaskInstanceImpl;\n    }\n\n    public UserTaskInstanceBuilder withRootProcessInstanceId(final long rootProcessInstanceId) {\n        this.logicalGroup2 = rootProcessInstanceId;\n        return this;\n    }\n\n    public UserTaskInstanceBuilder withAssigneeId(final long assigneeId) {\n        this.assigneeId = assigneeId;\n        return this;\n    }\n\n    public UserTaskInstanceBuilder withActorId(final long actorId) {\n        this.actorId = actorId;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/WaitingMessageEventBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.builder;\n\nimport static org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingMessageEventBuilderFactory.PROGRESS_FREE_KEY;\nimport static org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingMessageEventBuilderFactory.PROGRESS_IN_TREATMENT_KEY;\n\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SBPMEventType;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent;\n\n/**\n * @author Emmanuel Duchastenier\n * @author Laurent Leseigneur\n */\npublic class WaitingMessageEventBuilder\n        extends PersistentObjectBuilder<SWaitingMessageEvent, WaitingMessageEventBuilder> {\n\n    private SWaitingMessageEvent event = new SWaitingMessageEvent();\n\n    private boolean inProgress;\n\n    public static WaitingMessageEventBuilder aWaitingEvent() {\n        return new WaitingMessageEventBuilder();\n    }\n\n    @Override\n    WaitingMessageEventBuilder getThisBuilder() {\n        return this;\n    }\n\n    @Override\n    SWaitingMessageEvent _build() {\n        event.setProgress(inProgress ? PROGRESS_IN_TREATMENT_KEY : PROGRESS_FREE_KEY);\n        return event;\n    }\n\n    public WaitingMessageEventBuilder withEventType(SBPMEventType eventType) {\n        event.setEventType(eventType);\n        return this;\n    }\n\n    public WaitingMessageEventBuilder inProgress(final boolean inProgress) {\n        this.inProgress = inProgress;\n        return this;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/archive/ArchivedFlowNodeInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.builder.archive;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance;\n\npublic abstract class ArchivedFlowNodeInstanceBuilder<T extends SAFlowNodeInstance, B extends ArchivedFlowNodeInstanceBuilder<T, B>>\n        extends\n        ArchivedPersistentObjectBuilder<T, B> {\n\n    protected int stateId;\n\n    protected String stateName;\n\n    protected long lastUpdateDate;\n\n    protected String name;\n\n    protected int loopCounter;\n\n    protected long executedBy;\n\n    protected long executedBySubstitute;\n\n    protected long flowNodeDefinitionId;\n\n    protected long rootContainerId;\n\n    protected long parentContainerId;\n\n    protected String description;\n\n    protected long logicalGroup1;\n\n    protected long logicalGroup2;\n\n    protected long logicalGroup3;\n\n    protected long logicalGroup4;\n\n    protected boolean terminal;\n\n    protected boolean stable;\n\n    @Override\n    protected T fill(final T persistent) {\n        super.fill(persistent);\n        persistent.setDescription(description);\n        persistent.setExecutedBy(executedBy);\n        persistent.setExecutedBySubstitute(executedBySubstitute);\n        persistent.setFlowNodeDefinitionId(flowNodeDefinitionId);\n        persistent.setLastUpdateDate(lastUpdateDate);\n        persistent.setName(name);\n        persistent.setParentContainerId(parentContainerId);\n        persistent.setRootContainerId(rootContainerId);\n        persistent.setStable(stable);\n        persistent.setStateId(stateId);\n        persistent.setStateName(stateName);\n        persistent.setTerminal(terminal);\n        persistent.setLogicalGroup(0, logicalGroup1);\n        persistent.setLogicalGroup(1, logicalGroup2);\n        persistent.setLogicalGroup(2, logicalGroup3);\n        persistent.setLogicalGroup(3, logicalGroup4);\n        return persistent;\n    }\n\n    public B withFlowNodeDefinitionId(final long flowNodeDefinitionId) {\n        this.flowNodeDefinitionId = flowNodeDefinitionId;\n        return thisBuilder;\n    }\n\n    public B withTerminal(final boolean terminal) {\n        this.terminal = terminal;\n        return thisBuilder;\n    }\n\n    public B withStable(final boolean stable) {\n        this.stable = stable;\n        return thisBuilder;\n    }\n\n    public B withExecutedBy(final long executedBy) {\n        this.executedBy = executedBy;\n        return thisBuilder;\n    }\n\n    public B withExecutedBySubstitute(final long executedBySubstitute) {\n        this.executedBySubstitute = executedBySubstitute;\n        return thisBuilder;\n    }\n\n    public B withId(final long id) {\n        this.id = id;\n        return thisBuilder;\n    }\n\n    public B withName(final String name) {\n        this.name = name;\n        return thisBuilder;\n    }\n\n    public B withStateName(final String stateName) {\n        this.stateName = stateName;\n        return thisBuilder;\n    }\n\n    public B withDescription(final String description) {\n        this.description = description;\n        return thisBuilder;\n    }\n\n    public B withStateId(final int stateId) {\n        this.stateId = stateId;\n        return thisBuilder;\n    }\n\n    public B withLastUpdateDate(final long lastUpdateDate) {\n        this.lastUpdateDate = lastUpdateDate;\n        return thisBuilder;\n    }\n\n    public B withRootContainerId(final long containerId) {\n        this.rootContainerId = containerId;\n        return thisBuilder;\n    }\n\n    public B withParentContainerId(final long processInstanceId) {\n        this.parentContainerId = processInstanceId;\n        return thisBuilder;\n    }\n\n    public B withLoopCounter(final int loopCounter) {\n        this.loopCounter = loopCounter;\n        return thisBuilder;\n    }\n\n    public B withLogicalGroup1(final long logicalGroup) {\n        this.logicalGroup1 = logicalGroup;\n        return thisBuilder;\n    }\n\n    public B withLogicalGroup2(final long logicalGroup) {\n        this.logicalGroup2 = logicalGroup;\n        return thisBuilder;\n    }\n\n    public B withLogicalGroup3(final long logicalGroup) {\n        this.logicalGroup3 = logicalGroup;\n        return thisBuilder;\n    }\n\n    public B withLogicalGroup4(final long logicalGroup) {\n        this.logicalGroup4 = logicalGroup;\n        return thisBuilder;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/archive/ArchivedPersistentObjectBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.builder.archive;\n\nimport java.util.Random;\n\nimport org.bonitasoft.engine.persistence.ArchivedPersistentObject;\n\npublic abstract class ArchivedPersistentObjectBuilder<T extends ArchivedPersistentObject, B extends ArchivedPersistentObjectBuilder<T, B>> {\n\n    protected long id = new Random().nextLong();\n\n    protected T persistentObject;\n\n    protected B thisBuilder;\n\n    public ArchivedPersistentObjectBuilder() {\n        thisBuilder = getThisBuilder();\n    }\n\n    public T build() {\n        persistentObject = _build();\n        fill(persistentObject);\n        return persistentObject;\n    }\n\n    protected T fill(T persistent) {\n        persistent.setId(id);\n        return persistent;\n    }\n\n    abstract B getThisBuilder();\n\n    abstract T _build();\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/archive/ArchivedUserTaskInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.builder.archive;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class ArchivedUserTaskInstanceBuilder\n        extends ArchivedFlowNodeInstanceBuilder<SAUserTaskInstance, ArchivedUserTaskInstanceBuilder> {\n\n    public static ArchivedUserTaskInstanceBuilder anArchivedUserTask() {\n        return new ArchivedUserTaskInstanceBuilder();\n    }\n\n    private long assigneeId;\n\n    @Override\n    ArchivedUserTaskInstanceBuilder getThisBuilder() {\n        return this;\n    }\n\n    @Override\n    SAUserTaskInstance _build() {\n        final SAUserTaskInstance userTaskInstanceImpl = new SAUserTaskInstance();\n        userTaskInstanceImpl.setAssigneeId(assigneeId);\n        return userTaskInstanceImpl;\n    }\n\n    public ArchivedUserTaskInstanceBuilder withAssigneeId(final long assigneeId) {\n        this.assigneeId = assigneeId;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/jdbc/JdbcRowMapper.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.jdbc;\n\nimport java.sql.Blob;\nimport java.sql.Clob;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.jdbc.core.RowMapper;\n\n/**\n * Spring JDBC RowMapper that converts column names and values as Hibernate woulds:\n * <ul>\n * <li>converts column names to upper case so that tests can assert on column name whatever the DB vendor</li>\n * <li>converts some column values to long, double, int or boolean if needed, notably on Oracle where types seem to be\n * harder to detect</li>\n * <li>converts Clob and Blob to String and byte[] respectively</li>\n * </ul>\n *\n * @author Emmanuel Duchastenier\n */\n@Slf4j\npublic class JdbcRowMapper implements RowMapper<Map<String, Object>> {\n\n    private final String dbVendor = System.getProperty(\"sysprop.bonita.db.vendor\", \"h2\");\n\n    private List<String> columnsToConvertToLong = new ArrayList<>(); // column names must be upper case\n    private List<String> columnsToConvertToBoolean = new ArrayList<>(); // column names must be upper case\n    private List<String> columnsToConvertToDouble = new ArrayList<>(); // column names must be upper case\n\n    private final Map<String, JdbcValueConverter> converters = Map.of(\n            \"h2\", new DefaultValueConverter(),\n            \"postgres\", new DefaultValueConverter(),\n            \"mysql\", new DefaultValueConverter(),\n            \"sqlserver\", new DefaultValueConverter(),\n            \"oracle\", new OracleValueConverter());\n\n    public JdbcRowMapper() {\n        log.debug(\"Detected DB vendor: {}\", dbVendor);\n    }\n\n    /**\n     * @param columnsToConvertToLong column names must be upper case\n     */\n    public JdbcRowMapper(String... columnsToConvertToLong) {\n        this();\n        this.columnsToConvertToLong.addAll(List.of(columnsToConvertToLong));\n    }\n\n    /**\n     * @param columnsToConvertToLong column names must be upper case\n     * @param columnsToConvertToBoolean column names must be upper case\n     */\n    public JdbcRowMapper(List<String> columnsToConvertToLong, List<String> columnsToConvertToBoolean) {\n        this();\n        this.columnsToConvertToLong = columnsToConvertToLong;\n        this.columnsToConvertToBoolean = columnsToConvertToBoolean;\n    }\n\n    public JdbcRowMapper(List<String> columnsToConvertToLong, List<String> columnsToConvertToBoolean,\n            List<String> columnsToConvertToDouble) {\n        this(columnsToConvertToLong, columnsToConvertToBoolean);\n        this.columnsToConvertToDouble = columnsToConvertToDouble;\n    }\n\n    @Override\n    public Map<String, Object> mapRow(ResultSet rs, int rowNum) throws SQLException {\n        Map<String, Object> result = new HashMap<>();\n        int columnCount = rs.getMetaData().getColumnCount();\n        for (int i = 1; i <= columnCount; i++) {\n            final Object object = rs.getObject(i);\n            Object value = converters.get(dbVendor).getValue(rs, object, i);\n            result.put(rs.getMetaData().getColumnName(i).toUpperCase(), value);\n        }\n        return result;\n    }\n\n    @FunctionalInterface\n    public interface JdbcValueConverter {\n\n        Object getValue(ResultSet resultSet, Object object, int rowNum) throws SQLException;\n    }\n\n    class DefaultValueConverter implements JdbcValueConverter {\n\n        @Override\n        public Object getValue(ResultSet rs, Object object, int columnNumber) throws SQLException {\n            if (object == null) {\n                return null;\n            }\n\n            if (object instanceof Clob) {\n                final Clob clob = rs.getClob(columnNumber);\n                return clob.getSubString(1, (int) clob.length());\n            }\n            if (object instanceof Blob) {\n                final Blob blob = rs.getBlob(columnNumber);\n                return blob.getBytes(1, (int) blob.length());\n            }\n\n            // if value must be converted to double, let's do it:\n            final String upperCaseColumnName = rs.getMetaData().getColumnName(columnNumber).toUpperCase();\n            if (columnsToConvertToDouble.contains(upperCaseColumnName)) {\n                return ((Number) object).doubleValue();\n            }\n            // if value must be converted to long, let's do it:\n            if (columnsToConvertToLong.contains(upperCaseColumnName)) {\n                return ((Number) object).longValue();\n            } else if (object instanceof Number) {\n                // if value can be converted to int, let's do it:\n                try {\n                    return ((Number) object).intValue();\n                } catch (Exception ignored) {\n                }\n            }\n            return object;\n        }\n    }\n\n    class OracleValueConverter extends DefaultValueConverter {\n\n        @Override\n        public Object getValue(ResultSet rs, Object object, int columnNumber) throws SQLException {\n            if (object != null\n                    && columnsToConvertToBoolean.contains(rs.getMetaData().getColumnName(columnNumber).toUpperCase())) {\n                return \"1\".equals(rs.getString(columnNumber));\n            } else {\n                return super.getValue(rs, object, columnNumber);\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/ApplicationRepository.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.repository;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.business.application.model.AbstractSApplication;\nimport org.bonitasoft.engine.business.application.model.SApplication;\nimport org.bonitasoft.engine.business.application.model.SApplicationMenu;\nimport org.bonitasoft.engine.business.application.model.SApplicationPage;\nimport org.hibernate.SessionFactory;\nimport org.hibernate.query.Query;\nimport org.springframework.stereotype.Repository;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Repository\npublic class ApplicationRepository extends TestRepository {\n\n    public ApplicationRepository(final SessionFactory sessionFactory) {\n        super(sessionFactory);\n    }\n\n    public SApplication getApplicationByToken(final String token) {\n        final Query namedQuery = getNamedQuery(\"getApplicationByToken\");\n        namedQuery.setParameter(\"token\", token);\n        return (SApplication) namedQuery.uniqueResult();\n    }\n\n    public SApplicationPage getApplicationPage(final long applicationPageId) {\n        final Query namedQuery = getNamedQuery(\"getApplicationPageById\");\n        namedQuery.setParameter(\"id\", applicationPageId);\n        return (SApplicationPage) namedQuery.uniqueResult();\n    }\n\n    public SApplicationPage getApplicationPageByTokenAndApplicationToken(final String applicationToken,\n            final String applicationPageToken) {\n        final Query namedQuery = getNamedQuery(\"getApplicationPageByTokenAndApplicationToken\");\n        namedQuery.setParameter(\"applicationToken\", applicationToken);\n        namedQuery.setParameter(\"applicationPageToken\", applicationPageToken);\n        return (SApplicationPage) namedQuery.uniqueResult();\n    }\n\n    public SApplicationPage getApplicationPageByTokenAndApplicationId(final long applicationId,\n            final String applicationPageToken) {\n        final Query namedQuery = getNamedQuery(\"getApplicationPageByTokenAndApplicationId\");\n        namedQuery.setParameter(\"applicationId\", applicationId);\n        namedQuery.setParameter(\"applicationPageToken\", applicationPageToken);\n        return (SApplicationPage) namedQuery.uniqueResult();\n    }\n\n    public SApplicationPage getApplicationHomePage(final long applicationId) {\n        final Query namedQuery = getNamedQuery(\"getApplicationHomePage\");\n        namedQuery.setParameter(\"applicationId\", applicationId);\n        return (SApplicationPage) namedQuery.uniqueResult();\n    }\n\n    public SApplicationMenu getApplicationMenu(final long applicationMenuId) {\n        final Query namedQuery = getNamedQuery(\"getApplicationMenuById\");\n        namedQuery.setParameter(\"id\", applicationMenuId);\n        return (SApplicationMenu) namedQuery.uniqueResult();\n    }\n\n    public int getLastIndexForRootMenu() {\n        final Query namedQuery = getNamedQuery(\"getLastIndexForRootMenu\");\n        return (Integer) namedQuery.uniqueResult();\n    }\n\n    public int getLastIndexForChildOf(final long parentMenuId) {\n        final Query namedQuery = getNamedQuery(\"getLastIndexForChildOf\");\n        namedQuery.setParameter(\"parentId\", parentMenuId);\n        return (Integer) namedQuery.uniqueResult();\n    }\n\n    public List<String> getAllPagesForProfile(final long profileId) {\n        final Query namedQuery = getNamedQuery(\"getAllPagesForProfile\");\n        namedQuery.setParameter(\"profileId\", profileId);\n        return namedQuery.list();\n    }\n\n    public Long getNumberOfApplicationOfUser(final long userId) {\n        final Query namedQuery = getNamedQuery(\"getNumberOfSApplicationOfUser\");\n        namedQuery.setParameter(\"userId\", userId);\n        return (Long) namedQuery.uniqueResult();\n    }\n\n    public List<SApplication> searchApplicationOfUser(final long userId) {\n        final Query namedQuery = getNamedQuery(\"searchSApplicationOfUser\");\n        namedQuery.setParameter(\"userId\", userId);\n        return namedQuery.list();\n    }\n\n    public void update(final AbstractSApplication application) {\n        getSession().update(application);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/BPMEventRepository.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.repository;\n\nimport static org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl.QUERY_RESET_IN_PROGRESS_WAITING_EVENTS;\n\nimport java.util.List;\n\nimport org.hibernate.SessionFactory;\nimport org.hibernate.query.Query;\nimport org.springframework.stereotype.Repository;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@Repository\npublic class BPMEventRepository extends TestRepository {\n\n    public BPMEventRepository(final SessionFactory sessionFactory) {\n        super(sessionFactory);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public List<Long> getInProgressMessageInstances() {\n        Query namedQuery = getNamedQuery(\"getInProgressMessageInstances\");\n        return namedQuery.list();\n    }\n\n    public int resetProgressMessageInstances() {\n        Query namedQuery = getNamedQuery(\"resetProgressMessageInstances\");\n        return namedQuery.executeUpdate();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public List<Long> getInProgressWaitingEvents() {\n        Query namedQuery = getNamedQuery(\"getInProgressWaitingEvents\");\n        return namedQuery.list();\n    }\n\n    public int resetInProgressWaitingEvents() {\n        Query namedQuery = getNamedQuery(QUERY_RESET_IN_PROGRESS_WAITING_EVENTS);\n        return namedQuery.executeUpdate();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public int deleteMessageInstanceByIds(List<Long> ids) {\n        Query namedQuery = getNamedQuery(\"deleteMessageInstanceByIds\");\n\n        namedQuery.setParameterList(\"ids\", ids);\n        return namedQuery.executeUpdate();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public List<Long> getMessageInstanceIdOlderThanCreationDate(long creationDate) {\n        Query namedQuery = getNamedQuery(\"getMessageInstanceIdOlderThanCreationDate\");\n        namedQuery.setParameter(\"creationDate\", creationDate);\n        return namedQuery.list();\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/CommentRepository.java",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.repository;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.comment.model.SComment;\nimport org.hibernate.SessionFactory;\nimport org.hibernate.query.Query;\nimport org.springframework.stereotype.Repository;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@Repository\npublic class CommentRepository extends TestRepository {\n\n    public CommentRepository(SessionFactory sessionFactory) {\n        super(sessionFactory);\n    }\n\n    public List<SComment> getCommentsOfProcessInstance(long processInstanceId) {\n        final Query namedQuery = getNamedQuery(\"getSComments\");\n        namedQuery.setParameter(\"processInstanceId\", processInstanceId);\n        return namedQuery.list();\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/ConnectorInstanceRepository.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.repository;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.core.process.instance.model.SAbstractConnectorInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstanceWithFailureInfo;\nimport org.hibernate.SessionFactory;\nimport org.hibernate.query.Query;\nimport org.springframework.stereotype.Repository;\n\n@Repository\npublic class ConnectorInstanceRepository extends TestRepository {\n\n    public ConnectorInstanceRepository(SessionFactory sessionFactory) {\n        super(sessionFactory);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public List<SAbstractConnectorInstance> getConnectorInstances(final long containerId, final String containerType) {\n        Query namedQuery = getNamedQuery(\"getConnectorInstances\");\n        namedQuery.setParameter(\"containerId\", containerId);\n        namedQuery.setParameter(\"containerType\", containerType);\n        return namedQuery.list();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public List<SAbstractConnectorInstance> getConnectorInstancesOrderedById(final long containerId,\n            final String containerType) {\n        Query namedQuery = getNamedQuery(\"getConnectorInstancesOrderedById\");\n        namedQuery.setParameter(\"containerId\", containerId);\n        namedQuery.setParameter(\"containerType\", containerType);\n        return namedQuery.list();\n    }\n\n    public List<SConnectorInstanceWithFailureInfo> getConnectorInstancesWithFailureInfo(final long containerId,\n            final String containerType, String state) {\n        Query namedQuery = getNamedQuery(\"getConnectorInstancesWithFailureInfoInState\");\n        namedQuery.setParameter(\"containerId\", containerId);\n        namedQuery.setParameter(\"containerType\", containerType);\n        namedQuery.setParameter(\"state\", state);\n        return namedQuery.list();\n    }\n\n    public long getNumberOfConnectorInstances(final long containerId, final String containerType) {\n        Query namedQuery = getNamedQuery(\"getNumberOfConnectorInstances\");\n        namedQuery.setParameter(\"containerId\", containerId);\n        namedQuery.setParameter(\"containerType\", containerType);\n        return ((Number) namedQuery.uniqueResult()).longValue();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public SConnectorInstance getNextExecutableConnectorInstance(final long containerId, final String containerType,\n            final ConnectorEvent activationEvent) {\n        Query namedQuery = getNamedQuery(\"getNextExecutableConnectorInstance\");\n        namedQuery.setParameter(\"containerId\", containerId);\n        namedQuery.setParameter(\"containerType\", containerType);\n        namedQuery.setParameter(\"activationEvent\", activationEvent);\n        List<SConnectorInstance> results = namedQuery.list();\n        return (results != null && results.size() > 0) ? results.get(0) : null;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public List<SAbstractConnectorInstance> searchSConnectorInstance() {\n        Query namedQuery = getNamedQuery(\"searchSConnectorInstance\");\n        return namedQuery.list();\n    }\n\n    public long getNumberOfSConnectorInstance() {\n        Query namedQuery = getNamedQuery(\"getNumberOfSConnectorInstance\");\n        return ((Number) namedQuery.uniqueResult()).longValue();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public List<SAbstractConnectorInstance> getConnectorInstances(final long containerId, final String containerType,\n            final ConnectorEvent activationEvent, String state) {\n        Query namedQuery = getNamedQuery(\"getConnectorInstancesWithState\");\n        namedQuery.setParameter(\"containerId\", containerId);\n        namedQuery.setParameter(\"containerType\", containerType);\n        namedQuery.setParameter(\"activationEvent\", activationEvent);\n        namedQuery.setParameter(\"state\", state);\n        return namedQuery.list();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public List<SConnectorInstanceWithFailureInfo> getConnectorInstanceWithFailureInfo(final long containerId) {\n        Query namedQuery = getNamedQuery(\"getConnectorInstanceWithFailureInfo\");\n        namedQuery.setParameter(\"id\", containerId);\n        return namedQuery.list();\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/ContractDataRepository.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.repository;\n\nimport org.hibernate.SessionFactory;\nimport org.springframework.stereotype.Repository;\n\n@Repository\npublic class ContractDataRepository extends TestRepository {\n\n    public ContractDataRepository(SessionFactory sessionFactory) {\n        super(sessionFactory);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/CustomUserInfoRepository.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.repository;\n\nimport java.util.List;\n\nimport org.hibernate.SessionFactory;\nimport org.hibernate.query.Query;\nimport org.springframework.stereotype.Repository;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Repository\npublic class CustomUserInfoRepository extends TestRepository {\n\n    public CustomUserInfoRepository(SessionFactory sessionFactory) {\n        super(sessionFactory);\n    }\n\n    public List<Long> getUserIdsWithCustomUserInfo(String userInfoName, String userInfoValue, boolean partialMatch) {\n        Query namedQuery;\n        if (partialMatch) {\n            namedQuery = getNamedQuery(\"getUserIdsWithCustomUserInfoContains\");\n            userInfoValue = \"%\" + userInfoValue + \"%\";\n        } else {\n            namedQuery = getNamedQuery(\"getUserIdsWithCustomUserInfo\");\n        }\n        namedQuery.setParameter(\"userInfoName\", userInfoName);\n        namedQuery.setParameter(\"userInfoValue\", userInfoValue);\n        return namedQuery.list();\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/DependencyRepository.java",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.repository;\n\nimport org.bonitasoft.engine.dependency.model.DependencyContent;\nimport org.bonitasoft.engine.dependency.model.ScopeType;\nimport org.hibernate.SessionFactory;\nimport org.hibernate.query.Query;\nimport org.springframework.stereotype.Repository;\n\n/**\n * @author Danila Mazour\n */\n@Repository\npublic class DependencyRepository extends TestRepository {\n\n    public DependencyRepository(final SessionFactory sessionFactory) {\n        super(sessionFactory);\n    }\n\n    public Long getDependencyIdFromArtifact(Long artifactId, ScopeType artifactType, String fileName) {\n        final Query namedQuery = getNamedQuery(\"getIdOfDependencyOfArtifact\");\n        namedQuery.setParameter(\"artifactId\", artifactId);\n        namedQuery.setParameter(\"artifactType\", artifactType);\n        namedQuery.setParameter(\"fileName\", fileName);\n        return (Long) namedQuery.uniqueResult();\n    }\n\n    public DependencyContent getDependencyContentOnly(long id) {\n        return (DependencyContent) getNamedQuery(\"getDependencyContentOnly\")\n                .setParameter(\"id\", id).uniqueResult();\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/DocumentRepository.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.repository;\n\nimport static java.lang.System.currentTimeMillis;\n\nimport org.bonitasoft.engine.core.document.model.SMappedDocument;\nimport org.bonitasoft.engine.core.document.model.archive.SAMappedDocument;\nimport org.hibernate.SessionFactory;\nimport org.hibernate.query.Query;\nimport org.springframework.stereotype.Repository;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@Repository\npublic class DocumentRepository extends TestRepository {\n\n    public DocumentRepository(final SessionFactory sessionFactory) {\n        super(sessionFactory);\n    }\n\n    public SMappedDocument getSMappedDocumentOfProcessWithName(String name, long processInstanceId) {\n        final Query namedQuery = getNamedQuery(\"getSMappedDocumentOfProcessWithName\");\n        namedQuery.setParameter(\"name\", name);\n        namedQuery.setParameter(\"processInstanceId\", processInstanceId);\n        return (SMappedDocument) namedQuery.uniqueResult();\n    }\n\n    public SAMappedDocument getSAMappedDocumentOfProcessWithName(String name, long processInstanceId) {\n        final Query namedQuery = getNamedQuery(\"getArchivedDocumentList\");\n        namedQuery.setParameter(\"name\", name);\n        namedQuery.setParameter(\"processInstanceId\", processInstanceId);\n        namedQuery.setParameter(\"time\", currentTimeMillis() - 100000);\n        return (SAMappedDocument) namedQuery.uniqueResult();\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/FlowNodeInstanceRepository.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.repository;\n\nimport java.time.Duration;\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstanceStateCounter;\nimport org.bonitasoft.engine.core.process.instance.model.SGatewayInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.hibernate.SessionFactory;\nimport org.hibernate.query.Query;\nimport org.springframework.stereotype.Repository;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Repository\npublic class FlowNodeInstanceRepository extends TestRepository {\n\n    public FlowNodeInstanceRepository(final SessionFactory sessionFactory) {\n        super(sessionFactory);\n    }\n\n    public SFlowNodeInstance getById(long id) {\n        final Query namedQuery = getNamedQuery(\"getSFlowNodeInstanceById\");\n        namedQuery.setParameter(\"id\", id);\n        return (SFlowNodeInstance) namedQuery.uniqueResult();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public List<Long> getFlowNodeInstanceIdsToRecover(Duration considerElementsOlderThan,\n            final QueryOptions queryOptions) {\n        final Query namedQuery = getNamedQuery(\"getFlowNodeInstanceIdsToRecover\");\n        namedQuery.setMaxResults(queryOptions.getNumberOfResults());\n        namedQuery.setFirstResult(queryOptions.getFromIndex());\n        namedQuery.setParameter(\"maxLastUpdate\", System.currentTimeMillis() - considerElementsOlderThan.toMillis());\n        return (List<Long>) namedQuery.list();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public List<Long> getGatewayInstanceIdsToRecover(Duration considerElementsOlderThan,\n            final QueryOptions queryOptions) {\n        final Query namedQuery = getNamedQuery(\"getGatewayInstanceIdsToRecover\");\n        namedQuery.setMaxResults(queryOptions.getNumberOfResults());\n        namedQuery.setFirstResult(queryOptions.getFromIndex());\n        namedQuery.setParameter(\"maxLastUpdate\", System.currentTimeMillis() - considerElementsOlderThan.toMillis());\n        return (List<Long>) namedQuery.list();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public SGatewayInstance getActiveGatewayInstanceOfProcess(long parentProcessInstanceId, String name) {\n        final Query namedQuery = getNamedQuery(\"getActiveGatewayInstanceOfProcess\");\n        namedQuery.setParameter(\"parentProcessInstanceId\", parentProcessInstanceId);\n        namedQuery.setParameter(\"name\", name);\n        return (SGatewayInstance) namedQuery.uniqueResult();\n    }\n\n    public long getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcessFor(final long rootProcessDefinitionId,\n            final long userId) {\n        final Query namedQuery = getNamedQuery(\"getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcessFor\");\n        namedQuery.setParameter(\"userId\", userId);\n        namedQuery.setParameter(\"rootProcessDefinitionId\", rootProcessDefinitionId);\n        return ((Number) namedQuery.uniqueResult()).longValue();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public List<SHumanTaskInstance> searchSHumanTaskInstanceAssignedAndPendingByRootProcessFor(\n            final long rootProcessDefinitionId, final long userId) {\n        Query namedQuery = getNamedQuery(\"searchSHumanTaskInstanceAssignedAndPendingByRootProcessFor\");\n        namedQuery = getSession().createQuery(namedQuery.getQueryString() + \" ORDER BY a.name\");\n        namedQuery.setParameter(\"userId\", userId);\n        namedQuery.setParameter(\"rootProcessDefinitionId\", rootProcessDefinitionId);\n        return namedQuery.list();\n    }\n\n    public long getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcess(final long rootProcessDefinitionId) {\n        final Query namedQuery = getNamedQuery(\"getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcess\");\n        namedQuery.setParameter(\"rootProcessDefinitionId\", rootProcessDefinitionId);\n        return ((Number) namedQuery.uniqueResult()).longValue();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public List<SHumanTaskInstance> searchSHumanTaskInstanceAssignedAndPendingByRootProcess(\n            final long rootProcessDefinitionId) {\n        Query namedQuery = getNamedQuery(\"searchSHumanTaskInstanceAssignedAndPendingByRootProcess\");\n        namedQuery = getSession().createQuery(namedQuery.getQueryString() + \" ORDER BY a.name\");\n        namedQuery.setParameter(\"rootProcessDefinitionId\", rootProcessDefinitionId);\n        return namedQuery.list();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public List<SFlowNodeInstanceStateCounter> getNumberOfArchivedFlowNodesInAllStates(long processInstanceId) {\n        Query namedQuery = getNamedQuery(\"getNumberOfArchivedFlowNodesInAllStates\");\n        namedQuery.setParameter(\"parentProcessInstanceId\", processInstanceId);\n        return (List<SFlowNodeInstanceStateCounter>) namedQuery.list();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public List<SFlowNodeInstanceStateCounter> getNumberOfFlowNodesInAllStates(long processInstanceId) {\n        Query namedQuery = getNamedQuery(\"getNumberOfFlowNodesInAllStates\");\n        namedQuery.setParameter(\"parentProcessInstanceId\", processInstanceId);\n        return (List<SFlowNodeInstanceStateCounter>) namedQuery.list();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public List<SFlowNodeInstanceStateCounter> getNumberOfFlowNodesOfProcessDefinitionInAllStates(\n            long processDefinitionId) {\n        Query namedQuery = getNamedQuery(\"getNumberOfFlowNodesOfProcessDefinitionInAllStates\");\n        namedQuery.setParameter(\"processDefinitionId\", processDefinitionId);\n        return (List<SFlowNodeInstanceStateCounter>) namedQuery.list();\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/JobRepository.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.repository;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.scheduler.model.SFailedJob;\nimport org.hibernate.SessionFactory;\nimport org.hibernate.query.Query;\nimport org.springframework.stereotype.Repository;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@Repository\npublic class JobRepository extends TestRepository {\n\n    public JobRepository(final SessionFactory sessionFactory) {\n        super(sessionFactory);\n    }\n\n    public List<SFailedJob> getFailedJobs(final QueryOptions queryOptions) {\n        final Query namedQuery = getNamedQuery(\"getFailedJobs\");\n        namedQuery.setMaxResults(queryOptions.getNumberOfResults());\n        namedQuery.setFirstResult(queryOptions.getFromIndex());\n        return namedQuery.list();\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/PageRepository.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.repository;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.page.SPage;\nimport org.bonitasoft.engine.page.SPageMapping;\nimport org.bonitasoft.engine.page.SPageWithContent;\nimport org.hibernate.SessionFactory;\nimport org.hibernate.query.Query;\nimport org.springframework.stereotype.Repository;\n\n@Repository\npublic class PageRepository extends TestRepository {\n\n    public PageRepository(final SessionFactory sessionFactory) {\n        super(sessionFactory);\n    }\n\n    public SPageWithContent getPageContent(final long id) {\n        final Query namedQuery = getNamedQuery(\"getPageContent\");\n        namedQuery.setParameter(\"id\", id);\n        return (SPageWithContent) namedQuery.uniqueResult();\n    }\n\n    public SPage getPageByName(final String name) {\n        final Query namedQuery = getNamedQuery(\"getPageByName\");\n        namedQuery.setParameter(\"pageName\", name);\n        return (SPage) namedQuery.uniqueResult();\n    }\n\n    public SPage getPageByNameAndProcessDefinitionId(final String name, long processDefinitionId) {\n        final Query namedQuery = getNamedQuery(\"getPageByNameAndProcessDefinitionId\");\n        namedQuery.setParameter(\"pageName\", name);\n        namedQuery.setParameter(\"processDefinitionId\", processDefinitionId);\n        return (SPage) namedQuery.uniqueResult();\n    }\n\n    public List<SPage> getPageByProcessDefinitionId(long processDefinitionId) {\n        final Query namedQuery = getNamedQuery(\"getPageByProcessDefinitionId\");\n        namedQuery.setParameter(\"processDefinitionId\", processDefinitionId);\n        return namedQuery.list();\n    }\n\n    public SPageMapping getPageMappingByKey(final String key) {\n        final Query namedQuery = getNamedQuery(\"getPageMappingByKey\");\n        namedQuery.setParameter(\"key\", key);\n        return (SPageMapping) namedQuery.uniqueResult();\n    }\n\n    public SPageMapping getPageMappingByPageId(final long pageId) {\n        final Query namedQuery = getNamedQuery(\"getPageMappingByPageId\");\n        namedQuery.setParameter(\"pageId\", pageId);\n        return (SPageMapping) namedQuery.uniqueResult();\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/PlatformRepository.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.repository;\n\nimport java.util.Random;\n\nimport org.bonitasoft.engine.commons.Pair;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.hibernate.SessionFactory;\nimport org.hibernate.query.Query;\nimport org.springframework.stereotype.Repository;\n\n@Repository\npublic class PlatformRepository extends TestRepository {\n\n    public PlatformRepository(SessionFactory sessionFactory) {\n        super(sessionFactory);\n    }\n\n    public PersistentObject selectOneOnPlatform(String queryName, Pair... parameters) {\n        Query namedQuery = getSession().getNamedQuery(queryName);\n        for (Pair parameter : parameters) {\n            namedQuery.setParameter(((String) parameter.getKey()), parameter.getValue());\n        }\n        return ((PersistentObject) namedQuery.uniqueResult());\n    }\n\n    public <T extends PersistentObject> T add(T entity) {\n        if (entity.getId() <= 0) {\n            entity.setId(new Random().nextLong());\n        }\n        getSession().save(entity);\n        return (T) getSession().get(entity.getClass(), entity.getId());\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/ProcessDeploymentInfoRepository.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.repository;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.hibernate.SessionFactory;\nimport org.hibernate.query.Query;\nimport org.springframework.stereotype.Repository;\n\n@Repository\npublic class ProcessDeploymentInfoRepository extends TestRepository {\n\n    public ProcessDeploymentInfoRepository(SessionFactory sessionFactory) {\n        super(sessionFactory);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public List<SProcessDefinitionDeployInfo> searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(\n            final long userId) {\n        final Query namedQuery = getNamedQuery(\"searchSProcessDefinitionDeployInfoWithAssignedOrPendingHumanTasksFor\");\n        namedQuery.setParameter(\"userId\", userId);\n        return namedQuery.list();\n    }\n\n    public long getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(final long userId) {\n        final Query namedQuery = getNamedQuery(\n                \"getNumberOfSProcessDefinitionDeployInfoWithAssignedOrPendingHumanTasksFor\");\n        namedQuery.setParameter(\"userId\", userId);\n        return ((Number) namedQuery.uniqueResult()).longValue();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public List<SProcessDefinitionDeployInfo> searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(\n            final long userId) {\n        final Query namedQuery = getNamedQuery(\n                \"searchSProcessDefinitionDeployInfoWithAssignedOrPendingHumanTasksSupervisedBy\");\n        namedQuery.setParameter(\"userId\", userId);\n        return namedQuery.list();\n    }\n\n    public long getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(final long userId) {\n        final Query namedQuery = getNamedQuery(\n                \"getNumberOfSProcessDefinitionDeployInfoWithAssignedOrPendingHumanTasksSupervisedBy\");\n        namedQuery.setParameter(\"userId\", userId);\n        return ((Number) namedQuery.uniqueResult()).longValue();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public List<SProcessDefinitionDeployInfo> searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks() {\n        Query namedQuery = getNamedQuery(\"searchSProcessDefinitionDeployInfoWithAssignedOrPendingHumanTasks\");\n        namedQuery = getSession().createQuery(namedQuery.getQueryString() + \" ORDER BY process_definition.id\");\n        return namedQuery.list();\n    }\n\n    public long getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasks() {\n        final Query namedQuery = getNamedQuery(\n                \"getNumberOfSProcessDefinitionDeployInfoWithAssignedOrPendingHumanTasks\");\n        return ((Number) namedQuery.uniqueResult()).longValue();\n    }\n\n    public List<SProcessDefinitionDeployInfo> getProcessDefinitionDeployInfosByName(String processName) {\n        final Query namedQuery = getNamedQuery(\"getProcessDefinitionDeployInfosByName\");\n        namedQuery.setParameter(\"name\", processName);\n        return namedQuery.list();\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/ProcessInstanceRepository.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.repository;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.hibernate.SessionFactory;\nimport org.hibernate.query.Query;\nimport org.springframework.stereotype.Repository;\n\n@Repository\npublic class ProcessInstanceRepository extends TestRepository {\n\n    public ProcessInstanceRepository(final SessionFactory sessionFactory) {\n        super(sessionFactory);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public List<Long> getPossibleUserIdsOfPendingTasks(final long activityInstanceId) {\n        final Query namedQuery = getNamedQuery(\"getPossibleUserIdsOfPendingTasks\");\n        namedQuery.setParameter(\"humanTaskInstanceId\", activityInstanceId);\n        return namedQuery.list();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public List<SUser> searchPossibleUserIdsOfPendingTasks(final long activityInstanceId) {\n        final Query namedQuery = getNamedQuery(\"searchSUserWhoCanStartPendingTask\");\n        namedQuery.setParameter(\"humanTaskInstanceId\", activityInstanceId);\n        return namedQuery.list();\n    }\n\n    public long getNumberOfSUserWhoCanStartPendingTask(final long activityInstanceId) {\n        final Query namedQuery = getNamedQuery(\"getNumberOfSUserWhoCanStartPendingTask\");\n        namedQuery.setParameter(\"humanTaskInstanceId\", activityInstanceId);\n        return ((Number) namedQuery.uniqueResult()).longValue();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public List<SUser> searchSUserWhoCanStartProcess(final long processId) {\n        final Query namedQuery = getNamedQuery(\"searchSUserWhoCanStartProcess\");\n        namedQuery.setParameter(\"processId\", processId);\n        namedQuery.setParameter(\"trueValue\", true);\n        return namedQuery.list();\n    }\n\n    public long getNumberOfSUserWhoCanStartProcess(final long processId) {\n        final Query namedQuery = getNamedQuery(\"getNumberOfSUserWhoCanStartProcess\");\n        namedQuery.setParameter(\"processId\", processId);\n        namedQuery.setParameter(\"trueValue\", true);\n        return ((Number) namedQuery.uniqueResult()).longValue();\n    }\n\n    public boolean isTaskPendingForUser(final long activityInstanceId, final long userId) {\n        final Query namedQuery = getNamedQuery(\"isTaskPendingForUser\");\n        namedQuery.setParameter(\"humanTaskInstanceId\", activityInstanceId);\n        namedQuery.setParameter(\"userId\", userId);\n        return ((Number) namedQuery.uniqueResult()).longValue() == 1;\n    }\n\n    public long countChildrenInstanceIdsOfProcessInstance(final long processInstanceId) {\n        final Query namedQuery = getNamedQuery(\"getNumberOfChildInstancesOfProcessInstance\");\n        namedQuery.setParameter(\"processInstanceId\", processInstanceId);\n        return ((Number) namedQuery.uniqueResult()).longValue();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public List<Long> getChildrenInstanceIdsOfProcessInstance(final long processInstanceId) {\n        final Query namedQuery = getNamedQuery(\"getChildInstanceIdsOfProcessInstance\");\n        namedQuery.setParameter(\"processInstanceId\", processInstanceId);\n        return namedQuery.list();\n    }\n\n    public long getNumberOfFailedSProcessInstanceSupervisedBy(final long userId) {\n        final Query namedQuery = getNamedQuery(\"getNumberOfSProcessInstanceFailedAndSupervisedBy\");\n        namedQuery.setParameter(\"userId\", userId);\n        return ((Number) namedQuery.uniqueResult()).longValue();\n    }\n\n    public List<SProcessInstance> searchFailedSProcessInstanceSupervisedBy(final long userId) {\n        final Query namedQuery = getNamedQuery(\"searchSProcessInstanceFailedAndSupervisedBy\");\n        namedQuery.setParameter(\"userId\", userId);\n        return namedQuery.list();\n    }\n\n    public long getNumberOfSProcessInstanceFailed() {\n        final Query namedQuery = getNamedQuery(\"getNumberOfSProcessInstanceFailed\");\n        return ((Number) namedQuery.uniqueResult()).longValue();\n    }\n\n    public long getNumberOfSProcessInstanceFailedForProcessDefinition(final long processDefinitionId) {\n        Query namedQuery = getNamedQuery(\"getNumberOfSProcessInstanceFailed\");\n        namedQuery = getSession()\n                .createQuery(namedQuery.getQueryString() + \" AND p.processDefinitionId = \" + processDefinitionId);\n        return ((Number) namedQuery.uniqueResult()).longValue();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public List<SProcessInstance> searchSProcessInstanceFailedForProcessDefinition(final long processDefinitionId) {\n        Query namedQuery = getNamedQuery(\"searchSProcessInstanceFailed\");\n        namedQuery = getSession()\n                .createQuery(namedQuery.getQueryString() + \" AND p.processDefinitionId = \" + processDefinitionId);\n        return namedQuery.list();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public List<SProcessInstance> searchSProcessInstanceFailed() {\n        final Query namedQuery = getNamedQuery(\"searchSProcessInstanceFailed\");\n        return namedQuery.list();\n    }\n\n    public long getNumberOfProcessInstances(final long processDefinitionId) {\n        final Query namedQuery = getNamedQuery(\"countProcessInstancesOfProcessDefinition\");\n        namedQuery.setParameter(\"processDefinitionId\", processDefinitionId);\n        return (Long) namedQuery.uniqueResult();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public List<SAProcessInstance> getArchivedProcessInstancesInAllStates(final List<Long> sourceProcessInstanceIds) {\n        final Query namedQuery = getNamedQuery(\"getArchivedProcessInstancesInAllStates\");\n        namedQuery.setParameterList(\"sourceObjectIds\", sourceProcessInstanceIds);\n        return namedQuery.list();\n    }\n\n    public long getNumberOfTimerEventTriggerInstances(final long processInstanceId, final String jobTriggerName) {\n        Query namedQuery = getNamedQuery(\"getNumberOfSTimerEventTriggerInstanceByProcessInstance\");\n        if (jobTriggerName != null) {\n            namedQuery = getSession()\n                    .createQuery(namedQuery.getQueryString() + \" AND e.name = '\" + jobTriggerName + \"'\");\n        }\n        namedQuery.setParameter(\"processInstanceId\", processInstanceId);\n        return ((Number) namedQuery.uniqueResult()).longValue();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public List<STimerEventTriggerInstance> searchTimerEventTriggerInstances(final long processInstanceId,\n            final String jobTriggerName) {\n        Query namedQuery = getNamedQuery(\"searchSTimerEventTriggerInstanceByProcessInstance\");\n        if (jobTriggerName != null) {\n            namedQuery = getSession()\n                    .createQuery(namedQuery.getQueryString() + \" AND e.name = '\" + jobTriggerName + \"'\");\n        }\n        namedQuery.setParameter(\"processInstanceId\", processInstanceId);\n        return namedQuery.list();\n    }\n\n    public List<Long> getProcessInstanceIdsToRecover(final long maxLastUpdate) {\n        final Query<Long> namedQuery = getNamedQuery(\"getProcessInstanceIdsToRecover\");\n        namedQuery.setParameter(\"maxLastUpdate\", maxLastUpdate);\n        return namedQuery.list();\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/ProcessResourceRepository.java",
    "content": "/**\n * Copyright (C) 2015 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.repository;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.resources.BARResourceType;\nimport org.bonitasoft.engine.resources.SBARResource;\nimport org.bonitasoft.engine.resources.SBARResourceLight;\nimport org.hibernate.SessionFactory;\nimport org.hibernate.query.Query;\nimport org.springframework.stereotype.Repository;\n\n@Repository\n@SuppressWarnings(\"unchecked\")\npublic class ProcessResourceRepository extends TestRepository {\n\n    public ProcessResourceRepository(final SessionFactory sessionFactory) {\n        super(sessionFactory);\n    }\n\n    public SBARResource getBARResource(long processDefinitionId, BARResourceType type, String name) {\n        final Query namedQuery = getNamedQuery(\"getBARResource\");\n        namedQuery.setParameter(\"processDefinitionId\", processDefinitionId);\n        namedQuery.setParameter(\"type\", type);\n        namedQuery.setParameter(\"name\", name);\n        return (SBARResource) namedQuery.uniqueResult();\n    }\n\n    public List<SBARResource> getBARResourcesOfType(long processDefinitionId, BARResourceType type) {\n        final Query namedQuery = getNamedQuery(\"getBARResourcesOfType\");\n        namedQuery.setParameter(\"processDefinitionId\", processDefinitionId);\n        namedQuery.setParameter(\"type\", type);\n        return namedQuery.list();\n    }\n\n    public List<SBARResourceLight> getBARResourcesLightOfType(long processDefinitionId, BARResourceType type) {\n        final Query namedQuery = getNamedQuery(\"getBARResourcesLightOfType\");\n        namedQuery.setParameter(\"processDefinitionId\", processDefinitionId);\n        namedQuery.setParameter(\"type\", type);\n        return namedQuery.list();\n    }\n\n    public long getNumberOfBARResourcesOfType(long processDefinitionId, BARResourceType type) {\n        final Query namedQuery = getNamedQuery(\"getNumberOfBARResourcesOfType\");\n        namedQuery.setParameter(\"processDefinitionId\", processDefinitionId);\n        namedQuery.setParameter(\"type\", type);\n        return (long) namedQuery.uniqueResult();\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/ProfileRepository.java",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.repository;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.profile.model.SProfile;\nimport org.hibernate.SessionFactory;\nimport org.hibernate.query.Query;\nimport org.springframework.stereotype.Repository;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@Repository\npublic class ProfileRepository extends TestRepository {\n\n    public ProfileRepository(SessionFactory sessionFactory) {\n        super(sessionFactory);\n    }\n\n    public List<SProfile> getProfilesOfUser(long userId) {\n        final Query namedQuery = getNamedQuery(\"getProfilesOfUser\");\n        namedQuery.setParameter(\"userId\", userId);\n        return namedQuery.list();\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/RoleRepository.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.repository;\n\nimport org.bonitasoft.engine.identity.model.SRole;\nimport org.hibernate.SessionFactory;\nimport org.hibernate.query.Query;\nimport org.springframework.stereotype.Repository;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@Repository\npublic class RoleRepository extends TestRepository {\n\n    public RoleRepository(SessionFactory sessionFactory) {\n        super(sessionFactory);\n    }\n\n    public SRole getRoleByName(String name) {\n        Query namedQuery = getNamedQuery(\"getRoleByName\");\n        namedQuery.setParameter(\"name\", name);\n        return ((SRole) namedQuery.uniqueResult());\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/SADataInstanceRepository.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.repository;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.data.instance.model.archive.SADataInstance;\nimport org.hibernate.SessionFactory;\nimport org.hibernate.query.Query;\nimport org.springframework.stereotype.Repository;\n\n@Repository\npublic class SADataInstanceRepository extends TestRepository {\n\n    public SADataInstanceRepository(final SessionFactory sessionFactory) {\n        super(sessionFactory);\n    }\n\n    public List<SADataInstance> getSADataInstancesByDataInstanceIdAndArchiveDate(final List<Long> dataInstanceIds,\n            final long time) {\n        final Query namedQuery = getNamedQuery(\"getSADataInstancesByDataInstanceIdAndArchiveDate\");\n        namedQuery.setParameterList(\"dataInstanceIds\", dataInstanceIds);\n        namedQuery.setParameter(\"time\", time);\n        return namedQuery.list();\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/SupervisorRepository.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.repository;\n\nimport java.util.List;\n\nimport org.hibernate.SessionFactory;\nimport org.springframework.stereotype.Repository;\n\n@Repository\npublic class SupervisorRepository extends TestRepository {\n\n    public SupervisorRepository(SessionFactory sessionFactory) {\n        super(sessionFactory);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public List<Long> searchSProcessSupervisorWithSUserSGroupSRole() {\n        return getNamedQuery(\"searchSProcessSupervisorwithSUserSGroupSRole\").list();\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/TenantResourceRepository.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.repository;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.resources.STenantResource;\nimport org.bonitasoft.engine.resources.STenantResourceLight;\nimport org.bonitasoft.engine.resources.TenantResourceType;\nimport org.hibernate.SessionFactory;\nimport org.hibernate.query.Query;\nimport org.springframework.stereotype.Repository;\n\n@Repository\npublic class TenantResourceRepository extends TestRepository {\n\n    public TenantResourceRepository(final SessionFactory sessionFactory) {\n        super(sessionFactory);\n    }\n\n    public STenantResource getTenantResource(TenantResourceType type, String name) {\n        final Query namedQuery = getNamedQuery(\"getTenantResource\");\n        namedQuery.setParameter(\"type\", type);\n        namedQuery.setParameter(\"name\", name);\n        return (STenantResource) namedQuery.uniqueResult();\n    }\n\n    public List<STenantResource> getTenantResourcesOfType(TenantResourceType type) {\n        final Query namedQuery = getNamedQuery(\"getTenantResourcesOfType\");\n        namedQuery.setParameter(\"type\", type);\n        return namedQuery.list();\n    }\n\n    public List<STenantResourceLight> getTenantResourcesLightOfType(TenantResourceType type) {\n        final Query namedQuery = getNamedQuery(\"getTenantResourcesLightOfType\");\n        namedQuery.setParameter(\"type\", type);\n        return namedQuery.list();\n    }\n\n    public long getNumberOfTenantResourcesOfType(TenantResourceType type) {\n        final Query namedQuery = getNamedQuery(\"getNumberOfTenantResourcesOfType\");\n        namedQuery.setParameter(\"type\", type);\n        return (long) namedQuery.uniqueResult();\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/TestRepository.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.repository;\n\nimport java.util.List;\nimport java.util.Random;\n\nimport org.bonitasoft.engine.commons.Pair;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.hibernate.Session;\nimport org.hibernate.SessionFactory;\nimport org.hibernate.query.Query;\nimport org.springframework.stereotype.Repository;\n\n/**\n * Test Repository\n * Need to be used in a transactional context\n */\n@Repository\npublic class TestRepository {\n\n    private final SessionFactory sessionFactory;\n\n    public TestRepository(final SessionFactory sessionFactory) {\n        this.sessionFactory = sessionFactory;\n    }\n\n    public Session getSession() {\n        return sessionFactory.getCurrentSession();\n    }\n\n    protected Query getNamedQuery(final String queryName) {\n        return getSession().getNamedQuery(queryName);\n    }\n\n    public void flush() {\n        getSession().flush();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public <T extends PersistentObject> T getById(final Class<? extends PersistentObject> clazz, long id) {\n        return (T) getSession().get(clazz, id);\n    }\n\n    public Long selectCount(String queryName, Pair... parameters) {\n        Query namedQuery = getNamedQuery(queryName);\n        setParameters(namedQuery, parameters);\n        return ((Long) namedQuery.uniqueResult());\n    }\n\n    public PersistentObject selectOne(String queryName, Pair... parameters) {\n        Query namedQuery = getNamedQuery(queryName);\n        setParameters(namedQuery, parameters);\n        return ((PersistentObject) namedQuery.uniqueResult());\n    }\n\n    public <T> List<T> selectList(String name, Pair... parameters) {\n        Query<T> namedQuery = getNamedQuery(name);\n        setParameters(namedQuery, parameters);\n        return namedQuery.list();\n    }\n\n    protected <T> void setParameters(Query<T> namedQuery, Pair[] parameters) {\n        for (Pair parameter : parameters) {\n            namedQuery.setParameter(((String) parameter.getKey()), parameter.getValue());\n        }\n    }\n\n    /**\n     * Utility method to run native queries on tables while developping tests:\n     * e.g.\n     * `repository.list(\"SELECT user_.* from user_ where user_.username = 'walter.bates'\", SUser.class)`\n     * will return the list of object SUser from database\n     */\n    public List<Object[]> list(String sqlQuery, Class type) {\n        return getSession().createSQLQuery(sqlQuery).addEntity(type).list();\n    }\n\n    public <T extends PersistentObject> void add(T... entities) {\n        for (T entity : entities) {\n            add(entity);\n        }\n    }\n\n    public <T extends PersistentObject> T add(T entity) {\n        if (entity.getId() <= 0) {\n            entity.setId(new Random().nextLong());\n        }\n        getSession().save(entity);\n        return (T) getSession().get(entity.getClass(), entity.getId());\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/UserMembershipRepository.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.repository;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.identity.model.SRole;\nimport org.hibernate.SessionFactory;\nimport org.hibernate.query.Query;\nimport org.springframework.stereotype.Repository;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@Repository\npublic class UserMembershipRepository extends TestRepository {\n\n    public UserMembershipRepository(SessionFactory sessionFactory) {\n        super(sessionFactory);\n    }\n\n    public Long getNumberOfUserMembersForUserOrManagerForActorMembers(long userId, final List<Long> actorMemberIds) {\n        Query namedQuery = getNamedQuery(\"getNumberOfUserMembersForUserOrManagerForActorMembers\");\n        namedQuery.setParameter(\"userId\", userId);\n        namedQuery.setParameterList(\"actorMemberIds\", actorMemberIds);\n        return ((Number) namedQuery.uniqueResult()).longValue();\n    }\n\n    public List getUserMembershipsByGroup(SGroup group) {\n        Query namedQuery = getNamedQuery(\"getUserMembershipsByGroup\");\n        namedQuery.setParameter(\"groupId\", group.getId());\n        return namedQuery.list();\n\n    }\n\n    public List getUserMembershipsByRole(SRole role) {\n        Query namedQuery = getNamedQuery(\"getUserMembershipsByRole\");\n        namedQuery.setParameter(\"roleId\", role.getId());\n        return namedQuery.list();\n    }\n\n    public List getUserMembershipsOfUser(long userId) {\n        Query namedQuery = getNamedQuery(\"getUserMembershipsOfUser\");\n        namedQuery.setParameter(\"userId\", userId);\n        return namedQuery.list();\n    }\n\n    public List getUserMembershipWithIds(long roleId, long groupId, long userId) {\n        Query namedQuery = getNamedQuery(\"getUserMembershipWithIds\");\n        namedQuery.setParameter(\"userId\", userId);\n        namedQuery.setParameter(\"roleId\", roleId);\n        namedQuery.setParameter(\"groupId\", groupId);\n        return namedQuery.list();\n    }\n\n    public List getUserMemberships() {\n        Query namedQuery = getNamedQuery(\"getUserMemberships\");\n        return namedQuery.list();\n    }\n\n    public List getSUserMembershipById(long id) {\n        Query namedQuery = getNamedQuery(\"getSUserMembershipById\");\n        namedQuery.setParameter(\"id\", id);\n        return namedQuery.list();\n    }\n\n    public List searchUserMembership() {\n        Query namedQuery = getNamedQuery(\"searchUserMembership\");\n        return namedQuery.list();\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/UserRepository.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.persistence.repository;\n\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.hibernate.SessionFactory;\nimport org.hibernate.query.Query;\nimport org.springframework.stereotype.Repository;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@Repository\npublic class UserRepository extends TestRepository {\n\n    public UserRepository(SessionFactory sessionFactory) {\n        super(sessionFactory);\n    }\n\n    public SUser getUserByName(String userName) {\n        Query namedQuery = getNamedQuery(\"getUserByUserName\");\n        namedQuery.setParameter(\"userName\", userName);\n        return ((SUser) namedQuery.uniqueResult());\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/resources/datasource/datasource-dependency-h2.xml",
    "content": "<beans xmlns=\"http://www.springframework.org/schema/beans\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://www.springframework.org/schema/beans\n\t   http://www.springframework.org/schema/beans/spring-beans-4.3.xsd\">\n\n\t<bean class=\"org.springframework.beans.factory.config.PropertyPlaceholderConfigurer\">\n\t\t<property name=\"properties\">\n\t\t\t<props>\n\t\t\t\t<prop key=\"db.hibernate.dialect\">org.hibernate.dialect.H2Dialect</prop>\n\t\t\t\t<prop key=\"db.datasource.classname\">org.h2.jdbcx.JdbcDataSource</prop>\n\t\t\t\t<prop key=\"db.url\">jdbc:h2:mem:bonita;DB_CLOSE_ON_EXIT=FALSE;AUTOCOMMIT=OFF;IGNORECASE=TRUE;DATABASE_TO_LOWER=TRUE;</prop>\n\t\t\t\t<prop key=\"db.user\">sa</prop>\n\t\t\t\t<prop key=\"db.password\" />\n\t\t\t</props>\n\t\t</property>\n\t</bean>\n\n\t<bean id=\"interceptor\" class=\"org.hibernate.EmptyInterceptor\" />\n\n</beans>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/resources/datasource/datasource-dependency-postgres.xml",
    "content": "<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans\n       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd\">\n\n    <bean class=\"org.springframework.beans.factory.config.PropertyPlaceholderConfigurer\">\n        <property name=\"systemPropertiesModeName\" value=\"SYSTEM_PROPERTIES_MODE_OVERRIDE\" />\n        <property name=\"properties\">\n            <props>\n                <prop key=\"db.hibernate.dialect\">org.hibernate.dialect.PostgreSQL10Dialect</prop>\n                <prop key=\"db.datasource.classname\">org.postgresql.xa.PGXADataSource</prop>\n                <prop key=\"db.server.name\">localhost</prop>\n                <prop key=\"db.server.port\">5432</prop>\n                <prop key=\"db.database.name\">bonita</prop>\n                <prop key=\"db.user\">bonita</prop>\n                <prop key=\"db.password\">bonita</prop>\n                <prop key=\"db.url\">jdbc:postgresql://${db.server.name}:${db.server.port}/${db.database.name}</prop>\n            </props>\n        </property>\n    </bean>\n\n    <bean id=\"interceptor\" class=\"org.bonitasoft.engine.persistence.PostgresInterceptor\" />\n\n</beans>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/resources/logback-test.xml",
    "content": "<configuration>\n\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->\n        <encoder>\n            <pattern>|%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <logger name=\"org.bonitasoft\" level=\"INFO\" />\n    <logger name=\"org.bonitasoft.platform\" level=\"DEBUG\" />\n\n    <logger name=\"org.springframework\" level=\"WARN\" />\n\n    <root level=\"WARN\">\n        <appender-ref ref=\"STDOUT\" />\n    </root>\n\n</configuration>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-query-tests/src/test/resources/testContext.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:context=\"http://www.springframework.org/schema/context\"\n       xsi:schemaLocation=\"\n            http://www.springframework.org/schema/beans\n            http://www.springframework.org/schema/beans/spring-beans-4.3.xsd\n            http://www.springframework.org/schema/context\n            http://www.springframework.org/schema/context/spring-context-4.3.xsd\">\n\n    <import resource=\"classpath:/datasource/datasource-dependency-${sysprop.bonita.db.vendor:h2}.xml\" />\n\n    <context:component-scan base-package=\"org.bonitasoft.engine.test.persistence\" />\n\n    <bean id=\"dataSource\" class=\"org.apache.tomcat.dbcp.dbcp2.BasicDataSource\" destroy-method=\"close\">\n        <property name=\"initialSize\" value=\"1\"/>\n        <property name=\"maxTotal\" value=\"10\"/>\n        <property name=\"url\" value=\"${db.url}\"/>\n        <property name=\"username\" value=\"${db.user}\"/>\n        <property name=\"password\" value=\"${db.password}\"/>\n    </bean>\n\n    <bean class=\"org.springframework.jdbc.core.JdbcTemplate\">\n        <constructor-arg name=\"dataSource\" ref=\"dataSource\" />\n    </bean>\n\n    <bean id=\"sessionFactory\" class=\"org.bonitasoft.engine.test.persistence.TestLocalSessionFactoryBuilder\">\n        <property name=\"interceptor\" ref=\"interceptor\" />\n        <property name=\"dataSource\" ref=\"dataSource\" />\n        <property name=\"annotatedPackages\">\n            <list>\n                <value>org.bonitasoft.engine.persistence</value>\n            </list>\n        </property>\n        <property name=\"annotatedClasses\">\n            <list>\n                <value>org.bonitasoft.engine.identity.model.SContactInfo</value>\n                <value>org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition</value>\n                <value>org.bonitasoft.engine.identity.model.SCustomUserInfoValue</value>\n                <value>org.bonitasoft.engine.identity.model.SGroup</value>\n                <value>org.bonitasoft.engine.identity.model.SHavingIcon</value>\n                <value>org.bonitasoft.engine.identity.SIcon</value>\n                <value>org.bonitasoft.engine.identity.model.SRole</value>\n                <value>org.bonitasoft.engine.identity.model.SUser</value>\n                <value>org.bonitasoft.engine.identity.model.SUserLogin</value>\n                <value>org.bonitasoft.engine.identity.model.SUserMembership</value>\n                <value>org.bonitasoft.engine.parameter.SParameter</value>\n                <value>org.bonitasoft.engine.actor.mapping.model.SActor</value>\n                <value>org.bonitasoft.engine.actor.mapping.model.SActorMember</value>\n                <value>org.bonitasoft.engine.business.application.model.SApplication</value>\n                <value>org.bonitasoft.engine.business.application.model.SApplicationWithIcon</value>\n                <value>org.bonitasoft.engine.business.application.model.SApplicationPage</value>\n                <value>org.bonitasoft.engine.business.application.model.SApplicationMenu</value>\n                <value>org.bonitasoft.engine.platform.model.SPlatform</value>\n                <value>org.bonitasoft.engine.platform.command.model.SPlatformCommand</value>\n                <value>org.bonitasoft.engine.core.category.model.SCategory</value>\n                <value>org.bonitasoft.engine.core.category.model.SProcessCategoryMapping</value>\n                <value>org.bonitasoft.engine.core.contract.data.SContractData</value>\n                <value>org.bonitasoft.engine.core.contract.data.SProcessContractData</value>\n                <value>org.bonitasoft.engine.core.contract.data.STaskContractData</value>\n                <value>org.bonitasoft.engine.core.contract.data.SAContractData</value>\n                <value>org.bonitasoft.engine.core.contract.data.SAProcessContractData</value>\n                <value>org.bonitasoft.engine.core.contract.data.SATaskContractData</value>\n                <value>org.bonitasoft.engine.core.process.comment.model.SComment</value>\n                <value>org.bonitasoft.engine.core.process.comment.model.SHumanComment</value>\n                <value>org.bonitasoft.engine.core.process.comment.model.SSystemComment</value>\n                <value>org.bonitasoft.engine.core.process.comment.model.archive.SAComment</value>\n                <value>org.bonitasoft.engine.dependency.model.SDependency</value>\n                <value>org.bonitasoft.engine.dependency.model.SDependencyMapping</value>\n                <value>org.bonitasoft.engine.dependency.model.SPlatformDependency</value>\n                <value>org.bonitasoft.engine.dependency.model.SPlatformDependencyMapping</value>\n                <value>org.bonitasoft.engine.profile.model.SProfile</value>\n                <value>org.bonitasoft.engine.profile.model.SProfileMember</value>\n                <value>org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor</value>\n                <value>org.bonitasoft.engine.data.instance.model.archive.SABlobDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.archive.SABooleanDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.archive.SADataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.archive.SADateDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.archive.SADoubleDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.archive.SAFloatDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.archive.SAIntegerDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.archive.SALongDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.archive.SALongTextDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.archive.SAShortTextDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.archive.SAXMLDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.archive.SAXMLObjectDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.SXMLDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.SLongDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.SDoubleDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.SXMLObjectDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.SIntegerDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.SDateDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.SLongTextDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.SFloatDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.SDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.SBooleanDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.SBlobDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.SShortTextDataInstance</value>\n                <value>org.bonitasoft.engine.resources.SBARResourceLight</value>\n                <value>org.bonitasoft.engine.resources.SBARResource</value>\n                <value>org.bonitasoft.engine.resources.STenantResourceLight</value>\n                <value>org.bonitasoft.engine.resources.STenantResource</value>\n                <value>org.bonitasoft.engine.scheduler.model.SJobDescriptor</value>\n                <value>org.bonitasoft.engine.scheduler.model.SJobParameter</value>\n                <value>org.bonitasoft.engine.scheduler.model.SJobLog</value>\n                <value>org.bonitasoft.engine.core.contract.data.SContractData</value>\n                <value>org.bonitasoft.engine.core.contract.data.SProcessContractData</value>\n                <value>org.bonitasoft.engine.core.contract.data.STaskContractData</value>\n                <value>org.bonitasoft.engine.core.contract.data.SAContractData</value>\n                <value>org.bonitasoft.engine.core.form.SFormMapping</value>\n                <value>org.bonitasoft.engine.core.contract.data.SAProcessContractData</value>\n                <value>org.bonitasoft.engine.core.contract.data.SATaskContractData</value>\n                <value>org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo</value>\n                <value>org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDesignContent</value>\n                <value>org.bonitasoft.engine.core.document.model.SLightDocument</value>\n                <value>org.bonitasoft.engine.temporary.content.STemporaryContent</value>\n                <value>org.bonitasoft.engine.core.document.model.SDocument</value>\n                <value>org.bonitasoft.engine.core.document.model.SDocumentMapping</value>\n                <value>org.bonitasoft.engine.core.document.model.SMappedDocument</value>\n                <value>org.bonitasoft.engine.core.document.model.archive.SAMappedDocument</value>\n                <value>org.bonitasoft.engine.core.document.model.archive.SADocumentMapping</value>\n                <value>org.bonitasoft.engine.page.SPage</value>\n                <value>org.bonitasoft.engine.page.SPageWithContent</value>\n                <value>org.bonitasoft.engine.page.SPageMapping</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SProcessInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SActivityInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SGatewayInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SConnectorInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SConnectorInstanceWithFailureInfo</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.SEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.SCatchEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.SStartEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.SIntermediateCatchEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.SThrowEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.SIntermediateThrowEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.SEndEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingErrorEvent</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.business.data.SSimpleRefBusinessDataInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.business.data.SProcessSimpleRefBusinessDataInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.business.data.SFlowNodeSimpleRefBusinessDataInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SAManualTaskInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SAAutomaticTaskInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SAReceiveTaskInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SASendTaskInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SACallActivityInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SASubProcessActivityInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SALoopActivityInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SAMultiInstanceActivityInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SAGatewayInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SAConnectorInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.event.SAEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.event.SACatchEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.event.SAStartEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.event.SAIntermediateCatchEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.event.SABoundaryEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.event.SAThrowEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.event.SAIntermediateThrowEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.event.SAEndEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.business.data.SARefBusinessDataInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.business.data.SASimpleRefBusinessDataInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.business.data.SAProcessSimpleRefBusinessDataInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.business.data.SAFlowNodeSimpleRefBusinessDataInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.business.data.SAProcessMultiRefBusinessDataInstance</value>\n                <value>org.bonitasoft.engine.queriablelogger.model.SQueriableLog</value>\n            </list>\n        </property>\n        <property name=\"mappingResources\">\n            <list>\n                <value>/org/bonitasoft/engine/core/process/definition/model/impl/hibernate/process.definition.queries.hbm.xml</value>\n\n                <value>/org/bonitasoft/engine/core/process/instance/model/impl/hibernate/process.instance.queries.hbm.xml</value>\n\n                <value>/org/bonitasoft/engine/supervisor/mapping/model/impl/hibernate/supervisor.queries.hbm.xml</value>\n\n                <value>/org/bonitasoft/engine/actor/mapping/model/impl/hibernate/actor.queries.hbm.xml</value>\n\n                <value>/org/bonitasoft/engine/identity/model/impl/hibernate/identity.queries.hbm.xml</value>\n\n                <value>/org/bonitasoft/engine/scheduler/impl/hibernate/schedulerimpl.queries.hbm.xml</value>\n\n                <value>/org/bonitasoft/engine/data/instance/model/impl/hibernate/data.instance.queries.hbm.xml</value>\n\n                <value>/org/bonitasoft/engine/data/instance/model/impl/hibernate/archived.data.instance.queries.hbm.xml</value>\n\n                <value>/org/bonitasoft/engine/profile/model/impl/hibernate/profile.queries.hbm.xml</value>\n\n                <value>/org/bonitasoft/engine/dependency/model/impl/hibernate/dependency.queries.hbm.xml</value>\n                <value>/org/bonitasoft/engine/dependency/model/impl/hibernate/platform-dependency.queries.hbm.xml</value>\n\n                <value>/org/bonitasoft/engine/platform/model/impl/hibernate/platform.queries.hbm.xml</value>\n                <value>org/bonitasoft/engine/platform/command/model/impl/hibernate/platformCommand.queries.hbm.xml</value>\n\n                <value>/org/bonitasoft/engine/core/process/instance/model/impl/hibernate/archived.process.instance.queries.hbm.xml</value>\n\n                <value>/org/bonitasoft/engine/page/impl/hibernate/page.queries.hbm.xml</value>\n                <value>/org/bonitasoft/engine/parameter/parameter.queries.hbm.xml</value>\n\n                <value>/org/bonitasoft/engine/business/application/impl/hibernate/application.queries.hbm.xml</value>\n\n                <value>/org/bonitasoft/engine/resources/hibernate/resources.queries.hbm.xml</value>\n                <value>/org/bonitasoft/engine/temporary/content/hibernate/temporary.content.queries.hbm.xml</value>\n\n                <value>/org/bonitasoft/engine/core/process/comment/model/impl/hibernate/comment.queries.hbm.xml</value>\n                <value>/org/bonitasoft/engine/core/process/comment/model/impl/hibernate/archive.comment.queries.hbm.xml</value>\n\n                <value>/org/bonitasoft/engine/core/contract/data/model/impl/hibernate/contract.queries.hbm.xml</value>\n\n                <value>org/bonitasoft/engine/core/document/model/impl/hibernate/document.queries.hbm.xml</value>\n                <value>org/bonitasoft/engine/core/document/model/impl/hibernate/archive.document.queries.hbm.xml</value>\n\n                <value>/org/bonitasoft/engine/core/form/impl/form-mapping.queries.hbm.xml</value>\n            </list>\n        </property>\n        <property name=\"hibernateProperties\">\n            <props>\n                <prop key=\"hibernate.hbm2ddl.auto\">create-drop</prop>\n                <prop key=\"hibernate.dialect\">${db.hibernate.dialect}</prop>\n                <prop key=\"hibernate.show_sql\">false</prop>\n                <prop key=\"hibernate.format_sql\">false</prop>\n                <prop key=\"hibernate.use_sql_comments\">false</prop>\n            </props>\n        </property>\n    </bean>\n\n    <bean id=\"transactionManager\" class=\"org.springframework.orm.hibernate5.HibernateTransactionManager\">\n        <property name=\"sessionFactory\" ref=\"sessionFactory\" />\n    </bean>\n\n</beans>\n"
  },
  {
    "path": "bonita-integration-tests/bonita-test-utils/build.gradle",
    "content": "dependencies {\n    api project(':bpm:bonita-client')\n    api project(':bpm:bonita-common')\n    api project(':bonita-test-api')\n    api libs.commonsIO\n    api libs.xmlunit\n    api libs.assertj\n\n    implementation project(':bpm:bonita-core:bonita-process-engine')\n    implementation libs.junit4\n    implementation libs.springTest\n}\n\npublishing {\n    publications {\n        mavenJava(MavenPublication) { from project.components.java }\n    }\n}"
  },
  {
    "path": "bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/connectors/TestConnectorEngineExecutionContext.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connectors;\n\nimport org.bonitasoft.engine.connector.AbstractConnector;\nimport org.bonitasoft.engine.expression.ExpressionConstants;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class TestConnectorEngineExecutionContext extends AbstractConnector {\n\n    @Override\n    public void validateInputParameters() {\n\n    }\n\n    @Override\n    protected void executeBusinessLogic() {\n        setOutputParameter(ExpressionConstants.TASK_ASSIGNEE_ID.getEngineConstantName(),\n                getExecutionContext().getTaskAssigneeId());\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/matchers/NameMatcher.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.matchers;\n\nimport org.bonitasoft.engine.bpm.NamedElement;\nimport org.hamcrest.BaseMatcher;\nimport org.hamcrest.Description;\n\n/**\n * @author Baptiste Mesta\n */\npublic class NameMatcher extends BaseMatcher<NamedElement> {\n\n    private final String name;\n\n    /**\n     * @param name\n     */\n    public NameMatcher(final String name) {\n        this.name = name;\n    }\n\n    public static NameMatcher nameIs(final String name) {\n        return new NameMatcher(name);\n    }\n\n    @Override\n    public boolean matches(final Object item) {\n        return name.equals(((NamedElement) item).getName());\n    }\n\n    @Override\n    public void describeTo(final Description description) {\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/APITestUtil.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder.aBusinessArchive;\nimport static org.junit.Assert.*;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.Serializable;\nimport java.lang.reflect.Method;\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.concurrent.TimeoutException;\nimport java.util.stream.Collectors;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipOutputStream;\n\nimport org.apache.commons.io.IOUtils;\nimport org.bonitasoft.engine.api.APIClient;\nimport org.bonitasoft.engine.api.ApplicationAPI;\nimport org.bonitasoft.engine.api.BusinessDataAPI;\nimport org.bonitasoft.engine.api.CommandAPI;\nimport org.bonitasoft.engine.api.IdentityAPI;\nimport org.bonitasoft.engine.api.MaintenanceAPI;\nimport org.bonitasoft.engine.api.PageAPI;\nimport org.bonitasoft.engine.api.PermissionAPI;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.ProfileAPI;\nimport org.bonitasoft.engine.api.TenantAdministrationAPI;\nimport org.bonitasoft.engine.api.platform.PlatformInformationAPI;\nimport org.bonitasoft.engine.bdm.BusinessObjectModelConverter;\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.model.Query;\nimport org.bonitasoft.engine.bdm.model.field.FieldType;\nimport org.bonitasoft.engine.bdm.model.field.RelationField;\nimport org.bonitasoft.engine.bdm.model.field.SimpleField;\nimport org.bonitasoft.engine.bpm.actor.ActorCriterion;\nimport org.bonitasoft.engine.bpm.actor.ActorInstance;\nimport org.bonitasoft.engine.bpm.actor.ActorNotFoundException;\nimport org.bonitasoft.engine.bpm.bar.BarResource;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.bar.InvalidBusinessArchiveFormatException;\nimport org.bonitasoft.engine.bpm.category.Category;\nimport org.bonitasoft.engine.bpm.category.CategoryCriterion;\nimport org.bonitasoft.engine.bpm.data.ArchivedDataInstance;\nimport org.bonitasoft.engine.bpm.document.Document;\nimport org.bonitasoft.engine.bpm.document.DocumentCriterion;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceNotFoundException;\nimport org.bonitasoft.engine.bpm.flownode.ActivityStates;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstanceNotFoundException;\nimport org.bonitasoft.engine.bpm.flownode.GatewayInstance;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.process.ActivationState;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException;\nimport org.bonitasoft.engine.bpm.process.Problem;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion;\nimport org.bonitasoft.engine.bpm.process.ProcessEnablementException;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceCriterion;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.supervisor.ProcessSupervisor;\nimport org.bonitasoft.engine.bpm.supervisor.ProcessSupervisorSearchDescriptor;\nimport org.bonitasoft.engine.business.application.IApplication;\nimport org.bonitasoft.engine.command.CommandDescriptor;\nimport org.bonitasoft.engine.command.CommandExecutionException;\nimport org.bonitasoft.engine.command.CommandNotFoundException;\nimport org.bonitasoft.engine.command.CommandParameterizationException;\nimport org.bonitasoft.engine.command.CommandSearchDescriptor;\nimport org.bonitasoft.engine.connector.AbstractConnector;\nimport org.bonitasoft.engine.connectors.TestConnectorEngineExecutionContext;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.RetrieveException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.expression.InvalidExpressionException;\nimport org.bonitasoft.engine.identity.Group;\nimport org.bonitasoft.engine.identity.GroupCreator;\nimport org.bonitasoft.engine.identity.GroupCriterion;\nimport org.bonitasoft.engine.identity.Role;\nimport org.bonitasoft.engine.identity.RoleCriterion;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.identity.UserCreator;\nimport org.bonitasoft.engine.identity.UserCriterion;\nimport org.bonitasoft.engine.identity.UserMembership;\nimport org.bonitasoft.engine.maintenance.MaintenanceDetails;\nimport org.bonitasoft.engine.operation.Operation;\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.engine.page.PageSearchDescriptor;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.bonitasoft.engine.tenant.TenantResource;\nimport org.bonitasoft.engine.test.check.CheckNbOfArchivedActivities;\nimport org.bonitasoft.engine.test.check.CheckNbOfArchivedActivityInstances;\nimport org.bonitasoft.engine.test.check.CheckNbOfOpenActivities;\nimport org.bonitasoft.engine.test.check.CheckNbOfProcessInstances;\nimport org.bonitasoft.engine.test.check.CheckNbPendingTaskOf;\nimport org.bonitasoft.engine.test.check.CheckProcessInstanceIsArchived;\nimport org.bonitasoft.engine.test.wait.WaitForArchivedActivity;\nimport org.bonitasoft.engine.test.wait.WaitForCompletedArchivedStep;\nimport org.bonitasoft.engine.test.wait.WaitForEvent;\nimport org.bonitasoft.engine.test.wait.WaitForFinalArchivedActivity;\nimport org.bonitasoft.engine.test.wait.WaitForPendingTasks;\nimport org.custommonkey.xmlunit.DetailedDiff;\nimport org.custommonkey.xmlunit.XMLUnit;\nimport org.junit.After;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.xml.sax.SAXException;\n\n/**\n * @author Emmanuel Duchastenier\n * @author Frederic Bouquet\n * @author Celine Souchet\n */\npublic class APITestUtil extends PlatformTestUtil {\n\n    public static final Logger LOGGER = LoggerFactory.getLogger(APITestUtil.class);\n\n    public static final int DEFAULT_REPEAT_EACH = 500;\n\n    // Use 'sysprop.bonita.test.timeout.millis' to overwrite:\n    public static final int DEFAULT_TIMEOUT;\n\n    public static final String ACTOR_NAME = BuildTestUtil.ACTOR_NAME;\n\n    public static final String PROCESS_VERSION = BuildTestUtil.PROCESS_VERSION;\n\n    public static final String PROCESS_NAME = BuildTestUtil.PROCESS_NAME;\n\n    public static final String PASSWORD = BuildTestUtil.PASSWORD;\n\n    public static final String USERNAME = BuildTestUtil.USERNAME;\n\n    public static final String GROUP_NAME = BuildTestUtil.GROUP_NAME;\n\n    public static final String ROLE_NAME = BuildTestUtil.ROLE_NAME;\n    public static final String FIND_BY_HIRE_DATE_RANGE = \"findByHireDateRange\";\n    public static final String COUNT_FOR_FIND_BY_HIRE_DATE_RANGE = \"countForFindByHireDateRange\";\n    private static final String BDM_PACKAGE_PREFIX = \"com.company.model\";\n    protected static final String COUNTRY_QUALIFIED_NAME = BDM_PACKAGE_PREFIX + \".Country\";\n    protected static final String ADDRESS_QUALIFIED_NAME = BDM_PACKAGE_PREFIX + \".Address\";\n    protected static final String DOG_QUALIFIED_NAME = BDM_PACKAGE_PREFIX + \".Dog\";\n    protected static final String CAT_QUALIFIED_NAME = BDM_PACKAGE_PREFIX + \".Cat\";\n    protected static final String EMPLOYEE_QUALIFIED_NAME = BDM_PACKAGE_PREFIX + \".Employee\";\n    protected static final String PRODUCT_QUALIFIED_NAME = BDM_PACKAGE_PREFIX + \".Product\";\n    protected static final String PRODUCT_CATALOG_QUALIFIED_NAME = BDM_PACKAGE_PREFIX + \".ProductCatalog\";\n    protected static final String GET_EMPLOYEE_BY_LAST_NAME_QUERY_NAME = \"findByLastName\";\n    protected static final String GET_EMPLOYEE_BY_PHONE_NUMBER_QUERY_NAME = \"findByPhoneNumber\";\n    protected static final String FIND_BY_FIRST_NAME_FETCH_ADDRESSES = \"findByFirstNameFetchAddresses\";\n    protected static final String FIND_BY_FIRST_NAME_AND_LAST_NAME_NEW_ORDER = \"findByFirstNameAndLastNameNewOrder\";\n    protected static final String COUNT_EMPLOYEE = \"countEmployee\";\n    protected static final String COUNT_ADDRESS = \"countAddress\";\n    private static final String PERSON_QUALIFIED_NAME = BDM_PACKAGE_PREFIX + \".Person\";\n    protected static final String FIND_EMPLOYEE_WITH_FIRSTNAMES = \"findEmployeeWithFirstNames\";\n\n    private static final List<String> DEFAULT_METHODS = Arrays.asList(\"wait\", \"equals\", \"toString\", \"hashCode\",\n            \"getClass\",\n            \"notify\", \"notifyAll\");\n\n    private final APIClient apiClient = new APIClient();\n\n    static {\n        final String strTimeout = System.getProperty(\"sysprop.bonita.test.timeout.millis\");\n        if (strTimeout != null) {\n            LOGGER.info(\"Using overridden test timeout value {} ms\", strTimeout);\n            DEFAULT_TIMEOUT = Integer.parseInt(strTimeout);\n        } else {\n            DEFAULT_TIMEOUT = (int) Duration.ofSeconds(30).toMillis();\n        }\n    }\n\n    @After\n    public void clearSynchroRepository() {\n        try {\n            loginWithTechnicalUser();\n            if (getTenantAdministrationAPI().isPaused()) {\n                getTenantAdministrationAPI().resume();\n            }\n            ClientEventUtil.clearRepo(getCommandAPI());\n            logout();\n        } catch (final Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    public void loginOnDefaultTenantWith(final String userName, final String password) throws BonitaException {\n        getApiClient().login(userName, password);\n    }\n\n    public void loginWithTechnicalUser() throws BonitaException {\n        getApiClient().login(DEFAULT_TECHNICAL_LOGGER_USERNAME, DEFAULT_TECHNICAL_LOGGER_PASSWORD);\n    }\n\n    public BusinessDataAPI getBusinessDataAPI() {\n        return getApiClient().getBusinessDataAPI();\n    }\n\n    public void logout() throws BonitaException {\n        getApiClient().logout();\n    }\n\n    public void logoutThenlogin() throws BonitaException {\n        logout();\n        loginWithTechnicalUser();\n    }\n\n    public void logoutThenloginAs(final String userName, final String password) throws BonitaException {\n        logout();\n        loginOnDefaultTenantWith(userName, password);\n    }\n\n    public void addMappingOfActorsForRole(final String actorName, final long roleId, final ProcessDefinition definition)\n            throws BonitaException {\n        getProcessAPI().addRoleToActor(actorName, definition, roleId);\n    }\n\n    public void addMappingOfActorsForRoleAndGroup(final String actorName, final long roleId, final long groupId,\n            final ProcessDefinition definition)\n            throws BonitaException {\n        getProcessAPI().addRoleAndGroupToActor(actorName, definition, roleId, groupId);\n    }\n\n    /**\n     * First actor means \"first one in Alphanumerical order !\"\n     */\n    public void addUserToFirstActorOfProcess(final long userId, final ProcessDefinition processDefinition)\n            throws BonitaException {\n        final List<ActorInstance> actors = getProcessAPI().getActors(processDefinition.getId(), 0, 1,\n                ActorCriterion.NAME_ASC);\n        final ActorInstance actor = actors.get(0);\n        getProcessAPI().addUserToActor(actor.getId(), userId);\n    }\n\n    public ActorInstance getActor(final String actorName, final ProcessDefinition processDefinition)\n            throws ActorNotFoundException {\n        final List<ActorInstance> actors = getProcessAPI().getActors(processDefinition.getId(), 0, 50,\n                ActorCriterion.NAME_ASC);\n        final ActorInstance actorInstance = getActorInstance(actors, actorName);\n        if (actorInstance == null) {\n            throw new ActorNotFoundException(actorName, processDefinition);\n        }\n        return actorInstance;\n    }\n\n    private ActorInstance getActorInstance(final List<ActorInstance> actors, final String actorName) {\n        for (final ActorInstance actor : actors) {\n            if (actor.getName().equals(actorName)) {\n                return actor;\n            }\n        }\n        return null;\n    }\n\n    public User createUser(final String userName, final String password) throws BonitaException {\n        return getIdentityAPI().createUser(userName, password);\n    }\n\n    public User createUser(final String userName, final String password, final String firstName, final String lastName)\n            throws BonitaException {\n        return getIdentityAPI().createUser(userName, password, firstName, lastName);\n    }\n\n    public User createUser(final UserCreator creator) throws BonitaException {\n        return getIdentityAPI().createUser(creator);\n    }\n\n    public User createUser(final String userName, final long managerId) throws BonitaException {\n        return createUser(userName, \"bpm\", managerId);\n    }\n\n    public User createUser(final String userName, final String password, final long managerId) throws BonitaException {\n        final UserCreator creator = new UserCreator(userName, password);\n        creator.setManagerUserId(managerId);\n        return getIdentityAPI().createUser(creator);\n    }\n\n    public User createUserAndLogin(final String userName, final String password) throws BonitaException {\n        final User user = getIdentityAPI().createUser(userName, password);\n        logout();\n        loginOnDefaultTenantWith(userName, password);\n        return user;\n    }\n\n    public void deleteUsers(final User... users) throws BonitaException {\n        deleteUsers(Arrays.asList(users));\n    }\n\n    public void deleteUsers(final List<User> users) throws BonitaException {\n        if (users != null) {\n            for (final User user : users) {\n                getIdentityAPI().deleteUser(user.getId());\n            }\n        }\n    }\n\n    public void deleteGroups(final Group... groups) throws BonitaException {\n        deleteGroups(Arrays.asList(groups));\n    }\n\n    public void deleteGroups(final List<Group> groups) throws BonitaException {\n        if (groups != null) {\n            for (final Group group : groups) {\n                getIdentityAPI().deleteGroup(group.getId());\n            }\n        }\n    }\n\n    public void deleteRoles(final Role... roles) throws BonitaException {\n        deleteRoles(Arrays.asList(roles));\n    }\n\n    public void deleteRoles(final List<Role> roles) throws BonitaException {\n        if (roles != null) {\n            for (final Role role : roles) {\n                getIdentityAPI().deleteRole(role.getId());\n            }\n        }\n    }\n\n    public void deleteUserMembership(final long id) throws BonitaException {\n        getIdentityAPI().deleteUserMembership(id);\n    }\n\n    public void deleteUserMemberships(final UserMembership... userMemberships) throws BonitaException {\n        deleteUserMemberships(Arrays.asList(userMemberships));\n    }\n\n    public void deleteUserMemberships(final List<UserMembership> userMemberships) throws BonitaException {\n        if (userMemberships != null) {\n            for (final UserMembership userMembership : userMemberships) {\n                getIdentityAPI().deleteUserMembership(userMembership.getId());\n            }\n        }\n    }\n\n    public UserMembership createUserMembership(final String userName, final String roleName, final String groupName)\n            throws BonitaException {\n        return getIdentityAPI().addUserMembership(getIdentityAPI().getUserByUserName(userName).getId(),\n                getIdentityAPI().getGroupByPath(groupName).getId(),\n                getIdentityAPI().getRoleByName(roleName).getId());\n    }\n\n    public void deleteProcess(final ProcessDefinition... processDefinitions) throws BonitaException {\n        deleteProcess(Arrays.asList(processDefinitions));\n    }\n\n    public void deleteProcess(final ProcessDefinition processDefinition) throws BonitaException {\n        deleteProcess(processDefinition.getId());\n    }\n\n    public void deleteProcess(final List<ProcessDefinition> processDefinitions) throws BonitaException {\n        if (processDefinitions != null) {\n            for (final ProcessDefinition processDefinition : processDefinitions) {\n                deleteProcess(processDefinition);\n            }\n        }\n    }\n\n    public void deleteProcess(final long processDefinitionId) throws BonitaException {\n        deleteProcessInstanceAndArchived(processDefinitionId);\n        getProcessAPI().deleteProcessDefinition(processDefinitionId);\n    }\n\n    public void deleteUser(final String userName) throws BonitaException {\n        getIdentityAPI().deleteUser(userName);\n    }\n\n    public void deleteUser(final User user) throws BonitaException {\n        getIdentityAPI().deleteUser(user.getId());\n    }\n\n    public void deleteCategories(final List<Category> categories) throws BonitaException {\n        for (final Category category : categories) {\n            getProcessAPI().deleteCategory(category.getId());\n        }\n    }\n\n    public ProcessDefinition deployAndEnableProcess(final DesignProcessDefinition designProcessDefinition)\n            throws BonitaException {\n        return deployAndEnableProcess(createNewBusinessArchive(designProcessDefinition));\n    }\n\n    public ProcessDefinition deployAndEnableProcess(final BusinessArchive businessArchive) throws BonitaException {\n        final ProcessDefinition processDefinition = deployProcess(businessArchive);\n        enableProcess(processDefinition);\n        return processDefinition;\n\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithActor(final DesignProcessDefinition designProcessDefinition,\n            final String actorName, final User user)\n            throws BonitaException {\n        return deployAndEnableProcessWithActor(designProcessDefinition, Collections.singletonList(actorName),\n                Collections.singletonList(user));\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithActor(final DesignProcessDefinition designProcessDefinition,\n            final String actorName,\n            final List<User> users)\n            throws BonitaException {\n        return deployAndEnableProcessWithActor(createNewBusinessArchive(designProcessDefinition), actorName, users);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithActor(final BusinessArchive businessArchive,\n            final String actorName, final List<User> users)\n            throws BonitaException {\n        final ProcessDefinition processDefinition = deployProcess(businessArchive);\n        for (final User user : users) {\n            getProcessAPI().addUserToActor(actorName, processDefinition, user.getId());\n        }\n        enableProcess(processDefinition);\n        return processDefinition;\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithActorAndParameters(\n            final DesignProcessDefinition designProcessDefinition, final String actorName,\n            final User user, final Map<String, String> parameters) throws BonitaException {\n        return deployAndEnableProcessWithActorAndParameters(designProcessDefinition,\n                Collections.singletonList(actorName), Collections.singletonList(user),\n                parameters);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithActor(final DesignProcessDefinition designProcessDefinition,\n            final List<String> actorsName,\n            final List<User> users) throws BonitaException {\n        return deployAndEnableProcessWithActor(\n                createNewBusinessArchive(designProcessDefinition),\n                actorsName, users);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithActor(final BusinessArchive businessArchive,\n            final List<String> actorsName, final List<User> users)\n            throws BonitaException {\n        final ProcessDefinition processDefinition = deployProcess(businessArchive);\n        for (int i = 0; i < users.size(); i++) {\n            getProcessAPI().addUserToActor(actorsName.get(i), processDefinition, users.get(i).getId());\n        }\n        enableProcess(processDefinition);\n        return processDefinition;\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithActor(final DesignProcessDefinition designProcessDefinition,\n            final Map<String, List<User>> actorUsers)\n            throws BonitaException {\n        return deployAndEnableProcessWithActor(\n                createNewBusinessArchive(designProcessDefinition),\n                actorUsers);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithActor(final BusinessArchive businessArchive,\n            final Map<String, List<User>> actorUsers)\n            throws BonitaException {\n        final ProcessDefinition processDefinition = deployProcess(businessArchive);\n        for (final Entry<String, List<User>> actorUser : actorUsers.entrySet()) {\n            final String actorName = actorUser.getKey();\n            final List<User> users = actorUser.getValue();\n            for (final User user : users) {\n                getProcessAPI().addUserToActor(actorName, processDefinition, user.getId());\n            }\n        }\n\n        enableProcess(processDefinition);\n        return processDefinition;\n    }\n\n    public ProcessDefinition deployProcess(final BusinessArchive businessArchive) throws BonitaException {\n        return getProcessAPI().deploy(businessArchive);\n    }\n\n    private void enableProcess(final ProcessDefinition processDefinition)\n            throws ProcessDefinitionNotFoundException, ProcessEnablementException {\n        try {\n            getProcessAPI().enableProcess(processDefinition.getId());\n        } catch (final ProcessEnablementException e) {\n            final List<Problem> problems = getProcessAPI().getProcessResolutionProblems(processDefinition.getId());\n            throw new ProcessEnablementException(\"not resolved: \" + problems);\n        }\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithActor(final BusinessArchive businessArchive,\n            final String actorName, final User user)\n            throws BonitaException {\n        return deployAndEnableProcessWithActor(businessArchive, Collections.singletonList(actorName),\n                Collections.singletonList(user));\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithActorAndParameters(\n            final DesignProcessDefinition designProcessDefinition, final List<String> actorsName,\n            final List<User> users, final Map<String, String> parameters) throws BonitaException {\n        return deployAndEnableProcessWithActor(\n                createNewBusinessArchive(designProcessDefinition, parameters),\n                actorsName, users);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithActor(final DesignProcessDefinition designProcessDefinition,\n            final String actorName, final Group group)\n            throws BonitaException {\n        final ProcessDefinition processDefinition = deployProcess(createNewBusinessArchive(designProcessDefinition));\n        getProcessAPI().addGroupToActor(actorName, group.getId(), processDefinition);\n        getProcessAPI().enableProcess(processDefinition.getId());\n        return processDefinition;\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithActor(final DesignProcessDefinition designProcessDefinition,\n            final String actorName,\n            final Group... groups)\n            throws BonitaException {\n        final ProcessDefinition processDefinition = deployProcess(createNewBusinessArchive(designProcessDefinition));\n        for (final Group group : groups) {\n            getProcessAPI().addGroupToActor(actorName, group.getId(), processDefinition);\n        }\n        getProcessAPI().enableProcess(processDefinition.getId());\n        return processDefinition;\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithActor(final DesignProcessDefinition designProcessDefinition,\n            final String actorName,\n            final List<Group> groups, final List<User> users)\n            throws BonitaException {\n        final ProcessDefinition processDefinition = deployProcess(createNewBusinessArchive(designProcessDefinition));\n        for (final Group group : groups) {\n            getProcessAPI().addGroupToActor(actorName, group.getId(), processDefinition);\n        }\n        for (User user : users) {\n            getProcessAPI().addUserToActor(actorName, processDefinition, user.getId());\n        }\n        getProcessAPI().enableProcess(processDefinition.getId());\n        return processDefinition;\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithActor(final DesignProcessDefinition designProcessDefinition,\n            final String actorName, final Role role)\n            throws BonitaException {\n        final ProcessDefinition processDefinition = deployProcess(createNewBusinessArchive(designProcessDefinition));\n        addMappingOfActorsForRole(actorName, role.getId(), processDefinition);\n        getProcessAPI().enableProcess(processDefinition.getId());\n        return processDefinition;\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithActor(final DesignProcessDefinition designProcessDefinition,\n            final String actorName, final Role... roles)\n            throws BonitaException {\n        final ProcessDefinition processDefinition = deployProcess(createNewBusinessArchive(designProcessDefinition));\n        for (final Role role : roles) {\n            addMappingOfActorsForRole(actorName, role.getId(), processDefinition);\n        }\n        getProcessAPI().enableProcess(processDefinition.getId());\n        return processDefinition;\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithActor(final DesignProcessDefinition designProcessDefinition,\n            final String actorName, final Role role,\n            final Group group) throws BonitaException {\n        final ProcessDefinition processDefinition = deployProcess(createNewBusinessArchive(designProcessDefinition));\n        addMappingOfActorsForRoleAndGroup(actorName, role.getId(), group.getId(), processDefinition);\n        getProcessAPI().enableProcess(processDefinition.getId());\n        return processDefinition;\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithActor(final DesignProcessDefinition designProcessDefinition,\n            final String actorName, final long userId)\n            throws BonitaException {\n        final ProcessDefinition processDefinition = deployProcess(createNewBusinessArchive(designProcessDefinition));\n        getProcessAPI().addUserToActor(actorName, processDefinition, userId);\n        getProcessAPI().enableProcess(processDefinition.getId());\n        return processDefinition;\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithConnector(\n            final ProcessDefinitionBuilder processDefinitionBuilder,\n            final List<BarResource> connectorImplementations, final List<BarResource> generateConnectorDependencies)\n            throws BonitaException {\n        try {\n            final BusinessArchiveBuilder businessArchiveBuilder = BuildTestUtil\n                    .buildBusinessArchiveWithConnectorAndUserFilter(processDefinitionBuilder,\n                            connectorImplementations, generateConnectorDependencies, Collections.emptyList());\n            return deployAndEnableProcess(businessArchiveBuilder.done());\n        } catch (InvalidProcessDefinitionException | InvalidBusinessArchiveFormatException e) {\n            throw new BonitaException(e);\n        }\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithConnector(\n            final ProcessDefinitionBuilder processDefinitionBuilder, final String connectorImplName,\n            final Class<? extends AbstractConnector> clazz, final String jarName) throws BonitaException, IOException {\n        return deployAndEnableProcessWithConnector(processDefinitionBuilder,\n                Collections.singletonList(BuildTestUtil.getContentAndBuildBarResource(connectorImplName, clazz)),\n                Collections.singletonList(BuildTestUtil.generateJarAndBuildBarResource(clazz, jarName)));\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithActorAndConnector(\n            final ProcessDefinitionBuilder processDefinitionBuilder, final String actorName,\n            final User user, final String name, final Class<? extends AbstractConnector> clazz, final String jarName)\n            throws BonitaException, IOException {\n        return deployAndEnableProcessWithActorAndConnectorAndParameter(processDefinitionBuilder, actorName, user,\n                Collections.singletonList(BuildTestUtil.getContentAndBuildBarResource(name, clazz)),\n                Collections.singletonList(BuildTestUtil.generateJarAndBuildBarResource(clazz, jarName)), null);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithActorAndTestConnectorEngineExecutionContext(\n            final ProcessDefinitionBuilder processDefinitionBuilder,\n            final String actorName, final User user) throws BonitaException, IOException {\n        return deployAndEnableProcessWithActorAndConnector(processDefinitionBuilder, actorName, user,\n                \"TestConnectorEngineExecutionContext.impl\",\n                TestConnectorEngineExecutionContext.class, \"TestConnectorEngineExecutionContext.jar\");\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithActorAndConnectorAndParameter(\n            final ProcessDefinitionBuilder processDefinitionBuilder,\n            final String actorName, final User user, final List<BarResource> connectorImplementations,\n            final List<BarResource> generateConnectorDependencies,\n            final Map<String, String> parameters) throws BonitaException {\n        try {\n            final BusinessArchiveBuilder businessArchiveBuilder = BuildTestUtil\n                    .buildBusinessArchiveWithConnectorAndUserFilter(processDefinitionBuilder,\n                            connectorImplementations, generateConnectorDependencies, Collections.emptyList());\n            if (parameters != null) {\n                businessArchiveBuilder.setParameters(parameters);\n            }\n            return deployAndEnableProcessWithActor(businessArchiveBuilder.done(), actorName, user);\n        } catch (InvalidProcessDefinitionException | InvalidBusinessArchiveFormatException e) {\n            throw new BonitaException(e);\n        }\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithActorAndConnectorAndUserFilter(\n            final ProcessDefinitionBuilder processDefinitionBuilder,\n            final String actorName, final User user, final List<BarResource> connectorImplementations,\n            final List<BarResource> generateConnectorDependencies,\n            final List<BarResource> userFilters) throws BonitaException {\n        try {\n            final BusinessArchiveBuilder businessArchiveBuilder = BuildTestUtil\n                    .buildBusinessArchiveWithConnectorAndUserFilter(processDefinitionBuilder,\n                            connectorImplementations, generateConnectorDependencies, userFilters);\n            return deployAndEnableProcessWithActor(businessArchiveBuilder.done(), actorName, user);\n        } catch (InvalidProcessDefinitionException | InvalidBusinessArchiveFormatException e) {\n            throw new BonitaException(e);\n        }\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithActorAndConnectorAndParameter(\n            final ProcessDefinitionBuilder processDefinitionBuilder,\n            final String actorName, final User user, final Map<String, String> parameters, final String name,\n            final Class<? extends AbstractConnector> clazz,\n            final String jarName) throws BonitaException, IOException {\n        return deployAndEnableProcessWithActorAndConnectorAndParameter(processDefinitionBuilder, actorName, user,\n                Collections.singletonList(BuildTestUtil.getContentAndBuildBarResource(name, clazz)),\n                Collections.singletonList(BuildTestUtil.generateJarAndBuildBarResource(clazz, jarName)), parameters);\n    }\n\n    public ProcessDefinition deployAndEnableProcessWithActorAndUserFilter(\n            final ProcessDefinitionBuilder processDefinitionBuilder, final String actorName,\n            final User user, final List<BarResource> generateFilterDependencies, final List<BarResource> userFilters)\n            throws BonitaException {\n        return deployAndEnableProcessWithActorAndConnectorAndUserFilter(processDefinitionBuilder, actorName, user,\n                Collections.<BarResource> emptyList(),\n                generateFilterDependencies, userFilters);\n    }\n\n    private static BusinessArchive createNewBusinessArchive(final DesignProcessDefinition designProcessDefinition)\n            throws BonitaException {\n        try {\n            return aBusinessArchive().setProcessDefinition(designProcessDefinition).done();\n        } catch (InvalidBusinessArchiveFormatException e) {\n            throw new BonitaException(e);\n        }\n    }\n\n    private static BusinessArchive createNewBusinessArchive(final DesignProcessDefinition designProcessDefinition,\n            final Map<String, String> parameters)\n            throws BonitaException {\n        try {\n            return aBusinessArchive().setProcessDefinition(designProcessDefinition).setParameters(parameters).done();\n        } catch (InvalidBusinessArchiveFormatException e) {\n            throw new BonitaException(e);\n        }\n    }\n\n    public void disableAndDeleteProcess(final ProcessDefinition processDefinition) throws BonitaException {\n        if (processDefinition != null) {\n            disableAndDeleteProcess(processDefinition.getId());\n        }\n    }\n\n    public void disableAndDeleteProcess(final long processDefinitionId) throws BonitaException {\n        var processApi = getProcessAPI();\n        if (processApi.getProcessDeploymentInfo(processDefinitionId).getActivationState() == ActivationState.ENABLED) {\n            processApi.disableProcess(processDefinitionId);\n        }\n\n        // Delete all process instances\n        long nbDeletedProcessInstances;\n        do {\n            nbDeletedProcessInstances = processApi.deleteProcessInstances(processDefinitionId, 0, 100);\n        } while (nbDeletedProcessInstances > 0);\n\n        // Search all root archived process instances ids for the given processDefinition\n        var rootArchivedProcessInstancesIds = processApi\n                .searchArchivedProcessInstancesInAllStates(new SearchOptionsBuilder(0, Integer.MAX_VALUE)\n                        .filter(ArchivedProcessInstancesSearchDescriptor.PROCESS_DEFINITION_ID, processDefinitionId)\n                        .differentFrom(ArchivedProcessInstancesSearchDescriptor.CALLER_ID, \"-1\")\n                        .done())\n                .getResult()\n                .stream()\n                .map(ArchivedProcessInstance::getRootProcessInstanceId)\n                .collect(Collectors.toList());\n        if (!rootArchivedProcessInstancesIds.isEmpty()) {\n            processApi.deleteArchivedProcessInstancesInAllStates(rootArchivedProcessInstancesIds);\n        }\n\n        // Delete all archived process instances\n        long nbDeletedArchivedProcessInstances;\n        do {\n            nbDeletedArchivedProcessInstances = processApi.deleteArchivedProcessInstances(processDefinitionId, 0,\n                    100);\n        } while (nbDeletedArchivedProcessInstances > 0);\n\n        getProcessAPI().deleteProcessDefinition(processDefinitionId);\n    }\n\n    public void disableAndDeleteProcess(final ProcessDefinition... processDefinitions) throws BonitaException {\n        disableAndDeleteProcess(Arrays.asList(processDefinitions));\n    }\n\n    public void disableAndDeleteProcess(final List<ProcessDefinition> processDefinitions) throws BonitaException {\n        if (processDefinitions != null) {\n            for (final ProcessDefinition processDefinition : processDefinitions) {\n                disableAndDeleteProcess(processDefinition);\n            }\n        }\n    }\n\n    public void disableAndDeleteProcessById(final List<Long> processDefinitionIds) throws BonitaException {\n        if (processDefinitionIds != null) {\n            for (final Long id : processDefinitionIds) {\n                disableAndDeleteProcess(id);\n            }\n        }\n    }\n\n    public void deleteProcessInstanceAndArchived(final long processDefinitionId) throws BonitaException {\n        while (getProcessAPI().deleteArchivedProcessInstances(processDefinitionId, 0, 500) != 0);\n        while (getProcessAPI().deleteProcessInstances(processDefinitionId, 0, 500) != 0);\n    }\n\n    public void deleteProcessInstanceAndArchived(final ProcessDefinition... processDefinitions) throws BonitaException {\n        deleteProcessInstanceAndArchived(Arrays.asList(processDefinitions));\n    }\n\n    public void deleteProcessInstanceAndArchived(final List<ProcessDefinition> processDefinitions)\n            throws BonitaException {\n        if (processDefinitions != null) {\n            for (final ProcessDefinition processDefinition : processDefinitions) {\n                deleteProcessInstanceAndArchived(processDefinition.getId());\n            }\n        }\n    }\n\n    public APISession getSession() {\n        return getApiClient().getSession();\n    }\n\n    protected APIClient getApiClient() {\n        return apiClient;\n    }\n\n    public static boolean containsState(final List<ArchivedProcessInstance> instances, final TestStates state) {\n        for (final ArchivedProcessInstance pi : instances) {\n            if (state.getStateName().equals(pi.getState())) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    public Group createGroup(final String groupName) throws CreationException {\n        return createGroup(groupName, null);\n    }\n\n    public Group createGroup(final String groupName, final String parentGroupPath) throws CreationException {\n        return getIdentityAPI().createGroup(groupName, parentGroupPath);\n    }\n\n    public Group createGroup(final String name, final String displayName, final String description)\n            throws CreationException {\n        final GroupCreator groupCreator = new GroupCreator(name);\n        groupCreator.setDisplayName(displayName).setDescription(description);\n        return getIdentityAPI().createGroup(groupCreator);\n    }\n\n    public Role createRole(final String roleName) throws BonitaException {\n        return getIdentityAPI().createRole(roleName);\n    }\n\n    public void assignAndExecuteStep(final ActivityInstance activityInstance, final long userId)\n            throws BonitaException {\n        assignAndExecuteStep(activityInstance.getId(), userId);\n    }\n\n    public void assignAndExecuteStep(final ActivityInstance activityInstance, final User user) throws BonitaException {\n        assignAndExecuteStep(activityInstance.getId(), user.getId());\n    }\n\n    public void assignAndExecuteStep(final long activityInstanceId, final User user) throws BonitaException {\n        assignAndExecuteStep(activityInstanceId, user.getId());\n    }\n\n    public void assignAndExecuteStep(final long activityInstanceId, final long userId) throws BonitaException {\n        getProcessAPI().assignUserTask(activityInstanceId, userId);\n        getProcessAPI().executeUserTask(activityInstanceId, null);\n    }\n\n    public HumanTaskInstance waitForUserTaskAndExecuteAndGetIt(final String taskName, final User user)\n            throws Exception {\n        final HumanTaskInstance humanTaskInstance = waitForUserTaskAndGetIt(taskName);\n        assignAndExecuteStep(humanTaskInstance, user);\n        return humanTaskInstance;\n    }\n\n    public long waitForUserTaskAndExecuteIt(final String taskName, final User user) throws Exception {\n        final long humanTaskInstanceId = waitForUserTask(taskName);\n        assignAndExecuteStep(humanTaskInstanceId, user);\n        return humanTaskInstanceId;\n    }\n\n    public HumanTaskInstance waitForUserTaskAndExecuteAndGetIt(final ProcessInstance processInstance,\n            final String taskName, final User user) throws Exception {\n        final HumanTaskInstance humanTaskInstance = waitForUserTaskAndGetIt(processInstance.getId(), taskName,\n                DEFAULT_TIMEOUT);\n        assignAndExecuteStep(humanTaskInstance, user);\n        return humanTaskInstance;\n    }\n\n    public long waitForUserTaskAndExecuteIt(final ProcessInstance processInstance, final String taskName,\n            final User user) throws Exception {\n        final long humanTaskInstanceId = waitForUserTask(processInstance.getId(), taskName, DEFAULT_TIMEOUT);\n        assignAndExecuteStep(humanTaskInstanceId, user);\n        return humanTaskInstanceId;\n    }\n\n    public HumanTaskInstance waitForUserTaskAndAssignIt(final String taskName, final User user) throws Exception {\n        final HumanTaskInstance humanTaskInstance = waitForUserTaskAndGetIt(taskName);\n        getProcessAPI().assignUserTask(humanTaskInstance.getId(), user.getId());\n        return humanTaskInstance;\n    }\n\n    public HumanTaskInstance waitForUserTaskAndAssignIt(final ProcessInstance processInstance, final String taskName,\n            final User user) throws Exception {\n        final HumanTaskInstance humanTaskInstance = waitForUserTaskAndGetIt(processInstance.getId(), taskName);\n        getProcessAPI().assignUserTask(humanTaskInstance.getId(), user.getId());\n        return humanTaskInstance;\n    }\n\n    public HumanTaskInstance waitForUserTaskAssignAndExecuteIt(final ProcessInstance processInstance,\n            final String taskName, final User user, Map<String, Serializable> inputs) throws Exception {\n        final HumanTaskInstance humanTaskInstance = waitForUserTaskAndGetIt(processInstance.getId(), taskName);\n        getProcessAPI().assignAndExecuteUserTask(user.getId(), humanTaskInstance.getId(), inputs);\n        return humanTaskInstance;\n    }\n\n    public HumanTaskInstance waitForUserTaskAndGetIt(final ProcessInstance processInstance, final String taskName)\n            throws Exception {\n        return waitForUserTaskAndGetIt(processInstance.getId(), taskName, DEFAULT_TIMEOUT);\n    }\n\n    public HumanTaskInstance waitForUserTaskAndGetIt(final long processInstanceId, final String taskName)\n            throws Exception {\n        return waitForUserTaskAndGetIt(processInstanceId, taskName, DEFAULT_TIMEOUT);\n    }\n\n    public HumanTaskInstance waitForUserTaskAndGetIt(final String taskName) throws Exception {\n        return waitForUserTaskAndGetIt(-1, taskName, DEFAULT_TIMEOUT);\n    }\n\n    public long waitForUserTask(final long processInstanceId, final String taskName) throws Exception {\n        return waitForUserTask(processInstanceId, taskName, DEFAULT_TIMEOUT);\n    }\n\n    public long waitForUserTask(final String taskName) throws Exception {\n        return waitForUserTask(-1, taskName, DEFAULT_TIMEOUT);\n    }\n\n    private HumanTaskInstance waitForUserTaskAndGetIt(final long processInstanceId, final String taskName,\n            final int timeout) throws Exception {\n        final long activityInstanceId = waitForUserTask(processInstanceId, taskName, timeout);\n        final HumanTaskInstance getHumanTaskInstance = getHumanTaskInstance(activityInstanceId);\n        assertNotNull(getHumanTaskInstance);\n        return getHumanTaskInstance;\n    }\n\n    private long waitForUserTask(final long processInstanceId, final String taskName, final int timeout)\n            throws CommandNotFoundException,\n            CommandParameterizationException, CommandExecutionException, TimeoutException {\n        final Map<String, Serializable> readyTaskEvent;\n        if (processInstanceId > 0) {\n            readyTaskEvent = ClientEventUtil.getReadyFlowNodeEvent(processInstanceId, taskName);\n        } else {\n            readyTaskEvent = ClientEventUtil.getReadyFlowNodeEvent(taskName);\n        }\n        return ClientEventUtil.executeWaitServerCommand(getCommandAPI(), readyTaskEvent, timeout);\n    }\n\n    private HumanTaskInstance getHumanTaskInstance(final Long id)\n            throws ActivityInstanceNotFoundException, RetrieveException {\n        if (id != null) {\n            return getProcessAPI().getHumanTaskInstance(id);\n        }\n        throw new RuntimeException(\"no id returned for human task \");\n    }\n\n    private ActivityInstance getActivityInstance(final Long id)\n            throws ActivityInstanceNotFoundException, RetrieveException {\n        if (id != null) {\n            return getProcessAPI().getActivityInstance(id);\n        }\n        throw new RuntimeException(\"no id returned for activity instance \");\n    }\n\n    private FlowNodeInstance getFlowNodeInstance(final Long id) throws RuntimeException {\n        try {\n            return getProcessAPI().getFlowNodeInstance(id);\n        } catch (final FlowNodeInstanceNotFoundException e) {\n            throw new RuntimeException(\"no id returned for flow node instance \");\n        }\n    }\n\n    public long waitForUserTask(final ProcessInstance processInstance, final String taskName) throws Exception {\n        return waitForUserTask(processInstance.getId(), taskName, DEFAULT_TIMEOUT);\n    }\n\n    public void deleteSupervisor(final long id) throws BonitaException {\n        getProcessAPI().deleteSupervisor(id);\n    }\n\n    public void deleteSupervisor(final ProcessSupervisor supervisor) throws BonitaException {\n        getProcessAPI().deleteSupervisor(supervisor.getSupervisorId());\n    }\n\n    @Deprecated\n    public List<HumanTaskInstance> waitForPendingTasks(final long userId, final int nbPendingTasks) throws Exception {\n        final WaitForPendingTasks waitUntil = new WaitForPendingTasks(DEFAULT_REPEAT_EACH, DEFAULT_TIMEOUT,\n                nbPendingTasks, userId, getProcessAPI());\n        assertTrue(\"no pending user task instances are found\", waitUntil.waitUntil());\n        return waitUntil.getResults();\n    }\n\n    @Deprecated\n    public List<HumanTaskInstance> waitForPendingTasks(final User user, final int nbPendingTasks) throws Exception {\n        return waitForPendingTasks(user.getId(), nbPendingTasks);\n    }\n\n    public StartProcessUntilStep startProcessAndWaitForTask(final long processDefinitionId, final String taskName)\n            throws Exception {\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinitionId);\n        final ActivityInstance task = waitForUserTaskAndGetIt(processInstance.getId(), taskName);\n        return new StartProcessUntilStep(processInstance, task);\n    }\n\n    @Deprecated\n    public ArchivedActivityInstance waitForArchivedActivity(final long activityId, final TestStates state)\n            throws Exception {\n        final WaitForArchivedActivity waitForArchivedActivity = new WaitForArchivedActivity(DEFAULT_REPEAT_EACH,\n                DEFAULT_TIMEOUT, activityId, state,\n                getProcessAPI());\n        assertTrue(waitForArchivedActivity.waitUntil());\n        final ArchivedActivityInstance archivedActivityInstance = waitForArchivedActivity.getArchivedActivityInstance();\n        assertNotNull(archivedActivityInstance);\n        return archivedActivityInstance;\n    }\n\n    @Deprecated\n    public void waitForCompletedArchivedStep(final String value, final long id, final String displayName,\n            final String displayDescription) throws Exception {\n        final WaitForCompletedArchivedStep waitUntil = new WaitForCompletedArchivedStep(DEFAULT_REPEAT_EACH,\n                DEFAULT_TIMEOUT, value, id, getProcessAPI());\n        assertTrue(waitUntil.waitUntil());\n        final ArchivedHumanTaskInstance saUserTaskInstance = waitUntil.getArchivedTask();\n        assertEquals(displayName, saUserTaskInstance.getDisplayName());\n        assertEquals(displayDescription, saUserTaskInstance.getDisplayDescription());\n    }\n\n    public void waitForProcessToFinish(final ProcessInstance processInstance) throws Exception {\n        waitForProcessToFinish(processInstance.getId());\n    }\n\n    public void waitForProcessToFinish(final long processInstanceId) throws Exception {\n        waitForProcessToFinish(processInstanceId, DEFAULT_TIMEOUT);\n    }\n\n    private void waitForProcessToFinish(final long processInstanceId, final int timeout) throws Exception {\n        ClientEventUtil.executeWaitServerCommand(getCommandAPI(),\n                ClientEventUtil.getProcessInstanceFinishedEvent(processInstanceId), timeout);\n    }\n\n    public void waitForProcessToBeInState(final ProcessInstance processInstance, final ProcessInstanceState state)\n            throws Exception {\n        waitForProcessToBeInState(processInstance.getId(), state);\n    }\n\n    public void waitForProcessToBeInState(final long processInstanceId, final ProcessInstanceState state)\n            throws Exception {\n        waitForProcessToBeInState(processInstanceId, state, DEFAULT_TIMEOUT);\n    }\n\n    public void waitForProcessToBeInState(final long processInstanceId, final ProcessInstanceState state,\n            final int timeoutInMillis)\n            throws Exception {\n        ClientEventUtil.executeWaitServerCommand(getCommandAPI(),\n                ClientEventUtil.getProcessInstanceInState(processInstanceId, state.getId()),\n                timeoutInMillis);\n    }\n\n    public void waitForInitializingProcess() throws Exception {\n        ClientEventUtil.executeWaitServerCommand(getCommandAPI(),\n                ClientEventUtil.getProcessInstanceInState(ProcessInstanceState.INITIALIZING.getId()),\n                DEFAULT_TIMEOUT);\n    }\n\n    private Long waitForFlowNode(final long processInstanceId, final TestStates state, final String flowNodeName,\n            final boolean useRootProcessInstance,\n            final int timeoutInMillis) throws Exception {\n        Map<String, Serializable> params;\n        if (useRootProcessInstance) {\n            params = ClientEventUtil.getFlowNodeInState(processInstanceId, state.getStateName(), flowNodeName);\n        } else {\n            params = ClientEventUtil.getFlowNodeInStateWithParentId(processInstanceId, state.getStateName(),\n                    flowNodeName);\n        }\n        return ClientEventUtil.executeWaitServerCommand(getCommandAPI(), params, timeoutInMillis);\n    }\n\n    public FlowNodeInstance waitForFlowNodeInReadyState(final ProcessInstance processInstance,\n            final String flowNodeName, final boolean useRootProcessInstance)\n            throws Exception {\n        final Long flowNodeInstanceId = waitForFlowNode(processInstance.getId(), TestStates.READY, flowNodeName,\n                useRootProcessInstance,\n                DEFAULT_TIMEOUT);\n        return getFlowNode(flowNodeInstanceId);\n    }\n\n    public FlowNodeInstance waitForFlowNodeInExecutingState(final ProcessInstance processInstance,\n            final String flowNodeName,\n            final boolean useRootProcessInstance) throws Exception {\n        final Long activityId = waitForFlowNodeInState(processInstance, flowNodeName, TestStates.EXECUTING,\n                useRootProcessInstance);\n        return getFlowNodeInstance(activityId);\n    }\n\n    public ArchivedActivityInstance waitForActivityInCompletedState(final ProcessInstance processInstance,\n            final String flowNodeName,\n            final boolean useRootProcessInstance) throws Exception {\n        final Long activityId = waitForFlowNodeInState(processInstance, flowNodeName, TestStates.NORMAL_FINAL,\n                useRootProcessInstance);\n        // we must wait for the activity to be completely archived:\n        final WaitForArchivedActivity waitForArchivedActivity = new WaitForArchivedActivity(30, 8000, activityId,\n                TestStates.NORMAL_FINAL, getProcessAPI());\n        assertTrue(waitForArchivedActivity.waitUntil());\n        return waitForArchivedActivity.getArchivedActivityInstance();\n    }\n\n    public FlowNodeInstance waitForFlowNodeInWaitingState(final ProcessInstance processInstance,\n            final String flowNodeName,\n            final boolean useRootProcessInstance) throws Exception {\n        final Long activityId = waitForFlowNodeInState(processInstance, flowNodeName, TestStates.WAITING,\n                useRootProcessInstance);\n        return getFlowNodeInstance(activityId);\n    }\n\n    public Long waitForFlowNodeInState(final ProcessInstance processInstance, final String flowNodeName,\n            final TestStates state,\n            final boolean useRootProcessInstance) throws Exception {\n        return waitForFlowNode(processInstance.getId(), state, flowNodeName, useRootProcessInstance, DEFAULT_TIMEOUT);\n    }\n\n    public ActivityInstance waitForTaskInState(final ProcessInstance processInstance, final String flowNodeName,\n            final TestStates state) throws Exception {\n        final Long activityId = waitForFlowNode(processInstance.getId(), state, flowNodeName, true, DEFAULT_TIMEOUT);\n        return getActivityInstance(activityId);\n    }\n\n    private FlowNodeInstance getFlowNode(final Long flowNodeInstanceId) throws FlowNodeInstanceNotFoundException {\n        final FlowNodeInstance flowNodeInstance = getProcessAPI().getFlowNodeInstance(flowNodeInstanceId);\n        assertNotNull(flowNodeInstance);\n        return flowNodeInstance;\n    }\n\n    public ActivityInstance waitForTaskToFail(final ProcessInstance processInstance) throws Exception {\n        final Long activityId = ClientEventUtil.executeWaitServerCommand(getCommandAPI(),\n                ClientEventUtil.getFlowNodeInState(processInstance.getId(), TestStates.FAILED.getStateName()),\n                DEFAULT_TIMEOUT);\n        return getActivityInstance(activityId);\n    }\n\n    public FlowNodeInstance waitForFlowNodeInFailedState(final ProcessInstance processInstance) throws Exception {\n        final Long activityId = ClientEventUtil.executeWaitServerCommand(getCommandAPI(),\n                ClientEventUtil.getFlowNodeInState(processInstance.getId(), TestStates.FAILED.getStateName()),\n                DEFAULT_TIMEOUT);\n        return getFlowNodeInstance(activityId);\n    }\n\n    public void waitForFlowNodeInFailedState(final String flowNodeInstanceName) throws Exception {\n        final long failedTaskId = ClientEventUtil.executeWaitServerCommand(getCommandAPI(),\n                ClientEventUtil.getFlowNodeInState(TestStates.FAILED.getStateName(), flowNodeInstanceName),\n                DEFAULT_TIMEOUT);\n        assertNotNull(failedTaskId);\n    }\n\n    public FlowNodeInstance waitForFlowNodeInFailedState(final ProcessInstance processInstance,\n            final String flowNodeName) throws Exception {\n        final Long flowNodeInstanceId = waitForFlowNodeInState(processInstance, flowNodeName, TestStates.FAILED, true);\n        return getFlowNodeInstance(flowNodeInstanceId);\n    }\n\n    public GatewayInstance waitForGateway(final ProcessInstance processInstance, final String name) throws Exception {\n        final Map<String, Serializable> readyTaskEvent = ClientEventUtil.getFlowNode(processInstance.getId(), name);\n        final Long activityInstanceId = ClientEventUtil.executeWaitServerCommand(getCommandAPI(), readyTaskEvent,\n                DEFAULT_REPEAT_EACH * DEFAULT_TIMEOUT);\n        final FlowNodeInstance flowNodeInstance = getProcessAPI().getFlowNodeInstance(activityInstanceId);\n        assertNotNull(flowNodeInstance);\n        if (flowNodeInstance instanceof GatewayInstance) {\n            return (GatewayInstance) flowNodeInstance;\n        }\n        return null;\n    }\n\n    @Deprecated\n    public WaitForFinalArchivedActivity waitForFinalArchivedActivity(final String activityName,\n            final ProcessInstance processInstance) throws Exception {\n        final WaitForFinalArchivedActivity waitForFinalArchivedActivity = new WaitForFinalArchivedActivity(\n                DEFAULT_REPEAT_EACH, DEFAULT_TIMEOUT, activityName,\n                processInstance.getId(), getProcessAPI());\n        assertTrue(activityName + \" should be finished and archived\", waitForFinalArchivedActivity.waitUntil());\n        return waitForFinalArchivedActivity;\n    }\n\n    @Deprecated\n    public WaitForEvent waitForEventInWaitingState(final ProcessInstance processInstance, final String eventName)\n            throws Exception {\n        return waitForEvent(processInstance, eventName, TestStates.WAITING);\n    }\n\n    @Deprecated\n    public WaitForEvent waitForEvent(final ProcessInstance processInstance, final String eventName,\n            final TestStates state) throws Exception {\n        return waitForEvent(DEFAULT_REPEAT_EACH, DEFAULT_TIMEOUT, processInstance.getId(), eventName, state);\n    }\n\n    @Deprecated\n    private WaitForEvent waitForEvent(final int repeatEach, final int timeout, final long processInstanceId,\n            final String eventName, final TestStates state)\n            throws Exception {\n        final WaitForEvent waitForEvent = new WaitForEvent(repeatEach, timeout, eventName, processInstanceId, state,\n                getProcessAPI());\n        assertTrue(\"Expected 1 activities in \" + state + \" state\", waitForEvent.waitUntil());\n        return waitForEvent;\n    }\n\n    @Deprecated\n    public void checkNbOfOpenTasks(final ProcessInstance processInstance, final String message,\n            final int expectedNbOfOpenActivities) throws Exception {\n        final CheckNbOfOpenActivities checkNbOfOpenActivities = new CheckNbOfOpenActivities(DEFAULT_REPEAT_EACH,\n                DEFAULT_TIMEOUT, false, processInstance,\n                expectedNbOfOpenActivities, getProcessAPI());\n        checkNbOfOpenActivities.waitUntil();\n        assertEquals(message, expectedNbOfOpenActivities, checkNbOfOpenActivities.getNumberOfOpenActivities());\n    }\n\n    @Deprecated\n    public CheckNbPendingTaskOf checkNbPendingTaskOf(final int nbOfPendingTasks, final User user) throws Exception {\n        return checkNbPendingTaskOf(false, nbOfPendingTasks, user);\n    }\n\n    @Deprecated\n    public CheckNbPendingTaskOf checkNbPendingTaskOf(final boolean throwExceptions, final int nbOfPendingTasks,\n            final User user) throws Exception {\n        return checkNbPendingTaskOf(DEFAULT_REPEAT_EACH, DEFAULT_TIMEOUT, throwExceptions, nbOfPendingTasks, user);\n    }\n\n    @Deprecated\n    private CheckNbPendingTaskOf checkNbPendingTaskOf(final int repeatEach, final int timeout,\n            final boolean throwExceptions, final int nbOfPendingTasks,\n            final User user) throws Exception {\n        final CheckNbPendingTaskOf checkNbPendingTaskOf = new CheckNbPendingTaskOf(getProcessAPI(), repeatEach, timeout,\n                throwExceptions, nbOfPendingTasks,\n                user);\n        assertTrue(\"There isn't \" + nbOfPendingTasks + \" pending task\", checkNbPendingTaskOf.waitUntil());\n        return checkNbPendingTaskOf;\n    }\n\n    @Deprecated\n    public void checkNbOfOpenActivities(final ProcessInstance processInstance, final int nbActivities)\n            throws Exception {\n        checkNbOfOpenActivities(DEFAULT_REPEAT_EACH, DEFAULT_TIMEOUT, processInstance, nbActivities);\n    }\n\n    @Deprecated\n    private void checkNbOfOpenActivities(final int repeatEach, final int timeout, final ProcessInstance processInstance,\n            final int nbActivities)\n            throws Exception {\n        assertTrue(\"Expected \" + nbActivities + \" OPEN activities for process instance\",\n                new CheckNbOfOpenActivities(repeatEach, timeout, true,\n                        processInstance, nbActivities, getProcessAPI()).waitUntil());\n    }\n\n    @Deprecated\n    public void checkProcessInstanceIsArchived(final ProcessInstance processInstance) throws Exception {\n        checkProcessInstanceIsArchived(DEFAULT_REPEAT_EACH, DEFAULT_TIMEOUT, processInstance);\n    }\n\n    @Deprecated\n    private void checkProcessInstanceIsArchived(final int repeatEach, final int timeout,\n            final ProcessInstance processInstance) throws Exception {\n        assertTrue(new CheckProcessInstanceIsArchived(repeatEach, timeout, processInstance.getId(), getProcessAPI())\n                .waitUntil());\n    }\n\n    @Deprecated\n    public void checkNbOfArchivedActivityInstances(final ProcessInstance processInstance, final int expected)\n            throws Exception {\n        checkNbOfArchivedActivityInstances(DEFAULT_REPEAT_EACH, DEFAULT_TIMEOUT, processInstance, expected);\n    }\n\n    @Deprecated\n    private void checkNbOfArchivedActivityInstances(final int repeatEach, final int timeout,\n            final ProcessInstance processInstance, final int expected)\n            throws Exception {\n        assertTrue(\n                new CheckNbOfArchivedActivityInstances(repeatEach, timeout, processInstance, expected, getProcessAPI())\n                        .waitUntil());\n    }\n\n    @Deprecated\n    public void checkNbOfArchivedActivities(final ProcessInstance processInstance, final int nbAbortedActivities)\n            throws Exception {\n        checkNbOfArchivedActivities(DEFAULT_REPEAT_EACH, DEFAULT_TIMEOUT, processInstance, nbAbortedActivities);\n    }\n\n    @Deprecated\n    private void checkNbOfArchivedActivities(final int repeatEach, final int timeout,\n            final ProcessInstance processInstance, final int nbAbortedActivities)\n            throws Exception {\n        final CheckNbOfArchivedActivities checkNbOfActivities = new CheckNbOfArchivedActivities(getProcessAPI(),\n                repeatEach, timeout, true, processInstance,\n                nbAbortedActivities, TestStates.ABORTED);\n        final boolean waitUntil = checkNbOfActivities.waitUntil();\n        assertTrue(\"Expected \" + nbAbortedActivities + \" in the aboted state. But was \"\n                + checkNbOfActivities.getResult().size(), waitUntil);\n    }\n\n    @Deprecated\n    public CheckNbOfProcessInstances checkNbOfProcessInstances(final int nbOfProcInst) throws Exception {\n        final CheckNbOfProcessInstances checkNbOfProcessInstances = new CheckNbOfProcessInstances(DEFAULT_REPEAT_EACH,\n                DEFAULT_TIMEOUT, nbOfProcInst,\n                getProcessAPI());\n        assertTrue(checkNbOfProcessInstances.waitUntil());\n        return checkNbOfProcessInstances;\n    }\n\n    @Deprecated\n    public CheckNbOfProcessInstances checkNbOfProcessInstances(final int nbOfProcInst,\n            final ProcessInstanceCriterion orderBy) throws Exception {\n        return checkNbOfProcessInstances(DEFAULT_REPEAT_EACH, DEFAULT_TIMEOUT, nbOfProcInst, orderBy);\n    }\n\n    @Deprecated\n    private CheckNbOfProcessInstances checkNbOfProcessInstances(final int repeatEach, final int timeout,\n            final int nbOfProcInst,\n            final ProcessInstanceCriterion orderBy) throws Exception {\n        final CheckNbOfProcessInstances checkNbOfProcessInstances = new CheckNbOfProcessInstances(repeatEach, timeout,\n                nbOfProcInst, orderBy, getProcessAPI());\n        assertTrue(checkNbOfProcessInstances.waitUntil());\n        return checkNbOfProcessInstances;\n    }\n\n    public void checkFlowNodeWasntExecuted(final long processInstancedId, final String flowNodeName) {\n        final List<ArchivedActivityInstance> archivedActivityInstances = getProcessAPI().getArchivedActivityInstances(\n                processInstancedId, 0, 200,\n                ActivityInstanceCriterion.DEFAULT);\n        for (final ArchivedActivityInstance archivedActivityInstance : archivedActivityInstances) {\n            assertNotEquals(flowNodeName, archivedActivityInstance.getName());\n        }\n    }\n\n    public void checkWasntExecuted(final ProcessInstance parentProcessInstance, final String flowNodeName)\n            throws InvalidSessionException, SearchException {\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 20);\n        searchOptionsBuilder.filter(ArchivedFlowNodeInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID,\n                parentProcessInstance.getId());\n        searchOptionsBuilder.filter(ArchivedFlowNodeInstanceSearchDescriptor.NAME, flowNodeName);\n        final SearchResult<ArchivedFlowNodeInstance> searchArchivedActivities = getProcessAPI()\n                .searchArchivedFlowNodeInstances(searchOptionsBuilder.done());\n        assertEquals(0, searchArchivedActivities.getCount());\n    }\n\n    public void skipTask(final long activityId) throws UpdateException {\n        getProcessAPI().setActivityStateByName(activityId, ActivityStates.SKIPPED_STATE);\n    }\n\n    public void skipTasks(final ProcessInstance processInstance) throws UpdateException {\n        final List<ActivityInstance> activityInstances = getProcessAPI().getActivities(processInstance.getId(), 0, 10);\n        for (final ActivityInstance activityInstance : activityInstances) {\n            final long activityInstanceId = activityInstance.getId();\n            skipTask(activityInstanceId);\n        }\n    }\n\n    public void updateActivityInstanceVariablesWithOperations(final String updatedValue, final long activityInstanceId,\n            final String dataName,\n            final boolean isTransient)\n            throws InvalidExpressionException, UpdateException {\n        final Operation stringOperation = BuildTestUtil.buildStringOperation(dataName, updatedValue, isTransient);\n        final List<Operation> operations = new ArrayList<>();\n        operations.add(stringOperation);\n        getProcessAPI().updateActivityInstanceVariables(operations, activityInstanceId, null);\n    }\n\n    protected void cleanCategories() throws DeletionException {\n        final long numberOfCategories = getProcessAPI().getNumberOfCategories();\n        if (numberOfCategories > 0) {\n            final List<Category> categories = getProcessAPI().getCategories(0, 5000, CategoryCriterion.NAME_ASC);\n            for (final Category category : categories) {\n                getProcessAPI().deleteCategory(category.getId());\n            }\n        }\n    }\n\n    protected void cleanProcessDefinitions() throws BonitaException {\n        final List<ProcessDeploymentInfo> processes = getProcessAPI().getProcessDeploymentInfos(0, 200,\n                ProcessDeploymentInfoCriterion.DEFAULT);\n        for (final ProcessDeploymentInfo processDeploymentInfo : processes) {\n            if (ActivationState.ENABLED.equals(processDeploymentInfo.getActivationState())) {\n                getProcessAPI().disableProcess(processDeploymentInfo.getProcessId());\n            }\n            getProcessAPI().deleteProcessDefinition(processDeploymentInfo.getProcessId());\n        }\n    }\n\n    protected void cleanSupervisors() throws SearchException, DeletionException {\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 200);\n        builder.sort(ProcessSupervisorSearchDescriptor.ID, Order.ASC);\n        final List<ProcessSupervisor> supervisors = getProcessAPI().searchProcessSupervisors(builder.done())\n                .getResult();\n        for (final ProcessSupervisor supervisor : supervisors) {\n            getProcessAPI().deleteSupervisor(supervisor.getSupervisorId());\n        }\n    }\n\n    protected void cleanApplications() throws SearchException, DeletionException {\n        final SearchResult<IApplication> applications = getApplicationAPI()\n                .searchIApplications(new SearchOptionsBuilder(0, 100).done());\n        for (IApplication application : applications.getResult()) {\n            if (application.isEditable()) {\n                getApplicationAPI().deleteApplication(application.getId());\n            }\n        }\n    }\n\n    protected void cleanPages() throws SearchException, DeletionException {\n        getPageAPI().deletePages(getPageAPI()\n                .searchPages(new SearchOptionsBuilder(0, 100)\n                        .filter(PageSearchDescriptor.PROVIDED, false).done())\n                .getResult()\n                .stream().map(Page::getId).collect(Collectors.toList()));\n    }\n\n    protected void cleanProcessInstances() throws DeletionException {\n        final List<ProcessInstance> processInstances = getProcessAPI().getProcessInstances(0, 1000,\n                ProcessInstanceCriterion.DEFAULT);\n        if (!processInstances.isEmpty()) {\n            for (final ProcessInstance processInstance : processInstances) {\n                getProcessAPI().deleteProcessInstance(processInstance.getId());\n            }\n        }\n    }\n\n    protected void cleanArchiveProcessInstances() throws DeletionException {\n        final List<ArchivedProcessInstance> archivedProcessInstances = getProcessAPI().getArchivedProcessInstances(0,\n                1000, ProcessInstanceCriterion.DEFAULT);\n        if (!archivedProcessInstances.isEmpty()) {\n            for (final ArchivedProcessInstance archivedProcessInstance : archivedProcessInstances) {\n                getProcessAPI().deleteArchivedProcessInstancesInAllStates(archivedProcessInstance.getSourceObjectId());\n            }\n        }\n    }\n\n    protected void cleanGroups() throws DeletionException {\n        final long numberOfGroups = getIdentityAPI().getNumberOfGroups();\n        if (numberOfGroups > 0) {\n            final List<Group> groups = getIdentityAPI().getGroups(0, Long.valueOf(numberOfGroups).intValue(),\n                    GroupCriterion.NAME_ASC);\n            for (final Group group : groups) {\n                getIdentityAPI().deleteGroup(group.getId());\n            }\n        }\n    }\n\n    protected void cleanRoles() throws DeletionException {\n        final long numberOfRoles = getIdentityAPI().getNumberOfRoles();\n        if (numberOfRoles > 0) {\n            final List<Role> roles = getIdentityAPI().getRoles(0, Long.valueOf(numberOfRoles).intValue(),\n                    RoleCriterion.NAME_ASC);\n            for (final Role role : roles) {\n                getIdentityAPI().deleteRole(role.getId());\n            }\n        }\n    }\n\n    protected void cleanUsers() throws DeletionException {\n        final long numberOfUsers = getIdentityAPI().getNumberOfUsers();\n        if (numberOfUsers > 0) {\n            final List<User> users = getIdentityAPI().getUsers(0, Long.valueOf(numberOfUsers).intValue(),\n                    UserCriterion.USER_NAME_ASC);\n            for (final User user : users) {\n                getIdentityAPI().deleteUser(user.getId());\n            }\n        }\n    }\n\n    protected void cleanCommands() throws SearchException, CommandNotFoundException, DeletionException {\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 1000);\n        searchOptionsBuilder.filter(CommandSearchDescriptor.SYSTEM, false);\n        searchOptionsBuilder.differentFrom(CommandSearchDescriptor.NAME, ClientEventUtil.ADD_HANDLER_COMMAND);\n        searchOptionsBuilder.differentFrom(CommandSearchDescriptor.NAME, ClientEventUtil.WAIT_SERVER_COMMAND);\n        final SearchResult<CommandDescriptor> searchCommands = getCommandAPI()\n                .searchCommands(searchOptionsBuilder.done());\n        final List<CommandDescriptor> commands = searchCommands.getResult();\n        if (searchCommands.getCount() > 0) {\n            for (final CommandDescriptor command : commands) {\n                getCommandAPI().unregister(command.getName());\n            }\n        }\n    }\n\n    protected void cleanBdm() throws BonitaException {\n        TenantAdministrationAPI tenantAdministrationAPI = getTenantAdministrationAPI();\n        MaintenanceAPI maintenanceAPI = getMaintenanceAPI();\n        if (tenantAdministrationAPI.getBusinessDataModelResource() != TenantResource.NONE) {\n            if (maintenanceAPI.getMaintenanceDetails().getMaintenanceState() == MaintenanceDetails.State.DISABLED) {\n                maintenanceAPI.enableMaintenanceMode();\n            }\n            try {\n                tenantAdministrationAPI.cleanAndUninstallBusinessDataModel();\n            } finally {\n                maintenanceAPI.disableMaintenanceMode();\n            }\n        }\n    }\n\n    /**\n     * Deploys a Business Data Model, replacing any previously installed one.\n     * Handles pausing/resuming the tenant around the installation.\n     *\n     * @param bom the business object model to deploy\n     */\n    protected String installBusinessDataModel(BusinessObjectModel bom) throws Exception {\n        final byte[] zip = new BusinessObjectModelConverter().zip(bom);\n        getTenantAdministrationAPI().pause();\n        getTenantAdministrationAPI().cleanAndUninstallBusinessDataModel();\n        var bdmVersion = getTenantAdministrationAPI().updateBusinessDataModel(zip);\n        getTenantAdministrationAPI().resume();\n        return bdmVersion;\n    }\n\n    public PlatformInformationAPI getPlatformInformationAPI() {\n        return getApiClient().getPlatformInformationAPI();\n    }\n\n    public ProcessAPI getProcessAPI() {\n        return getApiClient().getProcessAPI();\n    }\n\n    public IdentityAPI getIdentityAPI() {\n        return getApiClient().getIdentityAPI();\n    }\n\n    public CommandAPI getCommandAPI() {\n        return getApiClient().getCommandAPI();\n    }\n\n    public ProfileAPI getProfileAPI() {\n        return getApiClient().getProfileAPI();\n    }\n\n    public PermissionAPI getPermissionAPI() {\n        return getApiClient().getPermissionAPI();\n    }\n\n    public PageAPI getPageAPI() {\n        return getApiClient().getCustomPageAPI();\n    }\n\n    public ApplicationAPI getLivingApplicationAPI() {\n        return getApiClient().getLivingApplicationAPI();\n    }\n\n    public ApplicationAPI getApplicationAPI() {\n        return getApiClient().getApplicationAPI();\n    }\n\n    public TenantAdministrationAPI getTenantAdministrationAPI() {\n        return getApiClient().getTenantAdministrationAPI();\n    }\n\n    public MaintenanceAPI getMaintenanceAPI() {\n        return getApiClient().getMaintenanceAPI();\n    }\n\n    public void deleteSupervisors(final List<ProcessSupervisor> processSupervisors) throws BonitaException {\n        if (processSupervisors != null) {\n            for (final ProcessSupervisor processSupervisor : processSupervisors) {\n                deleteSupervisor(processSupervisor.getSupervisorId());\n            }\n        }\n    }\n\n    public void buildAndAttachDocument(final ProcessInstance processInstance) throws BonitaException {\n        final String documentName = String.valueOf(System.currentTimeMillis());\n        final Document doc = BuildTestUtil.buildDocument(documentName);\n        buildAndAttachDocument(processInstance, documentName, doc.getContentFileName());\n    }\n\n    public void buildAndAttachDocument(final ProcessInstance processInstance, final String documentName,\n            final String fileName) throws BonitaException {\n        final Document doc = BuildTestUtil.buildDocument(documentName);\n        getProcessAPI().attachDocument(processInstance.getId(), documentName, fileName, doc.getContentMimeType(),\n                documentName.getBytes());\n    }\n\n    public String getAttachmentDocumentName(final ProcessInstance processInstance) throws BonitaException {\n        final Document attachment = getAttachmentWithoutItsContent(processInstance);\n        return attachment.getName();\n    }\n\n    public Document getAttachmentWithoutItsContent(final ProcessInstance processInstance) throws BonitaException {\n        final List<Document> attachments = getProcessAPI().getLastVersionOfDocuments(processInstance.getId(), 0, 1,\n                DocumentCriterion.DEFAULT);\n        assertTrue(\"No attachments found!\", attachments != null && attachments.size() == 1);\n        return attachments.get(0);\n    }\n\n    public ProcessInstance deployAndEnableProcessWithActorAndStartIt(final BusinessArchive businessArchive,\n            final User user) throws BonitaException {\n        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchive,\n                BuildTestUtil.ACTOR_NAME, user);\n        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());\n        assertNotNull(processInstance);\n        return processInstance;\n    }\n\n    public ArchivedDataInstance getArchivedDataInstance(final List<ArchivedDataInstance> archivedDataInstances,\n            final String dataName) {\n        return archivedDataInstances.stream()\n                .filter(archDataInstance -> archDataInstance.getName().equals(dataName))\n                .findFirst()\n                .orElseThrow(() -> new AssertionError(\"No archived data instance found for name: \" + dataName));\n    }\n\n    public List<ProcessDefinition> createNbProcessDefinitionWithHumanAndAutomaticAndDeployWithActor(final int nbProcess,\n            final User user,\n            final List<String> stepNames, final List<Boolean> isHuman) throws BonitaException {\n        try {\n            final List<ProcessDefinition> processDefinitions = new ArrayList<>();\n            final List<DesignProcessDefinition> designProcessDefinitions = BuildTestUtil\n                    .buildNbProcessDefinitionWithHumanAndAutomatic(nbProcess, stepNames, isHuman);\n\n            for (final DesignProcessDefinition designProcessDefinition : designProcessDefinitions) {\n                processDefinitions\n                        .add(deployAndEnableProcessWithActor(designProcessDefinition, BuildTestUtil.ACTOR_NAME, user));\n            }\n            return processDefinitions;\n        } catch (InvalidProcessDefinitionException e) {\n            throw new BonitaException(e);\n        }\n    }\n\n    protected void assertThatXmlHaveNoDifferences(final String xmlPrettyFormatExpected,\n            final String xmlPrettyFormatExported) throws SAXException, IOException {\n        XMLUnit.setIgnoreWhitespace(true);\n        XMLUnit.setIgnoreAttributeOrder(true);\n        final DetailedDiff diff = new DetailedDiff(\n                XMLUnit.compareXML(xmlPrettyFormatExported, xmlPrettyFormatExpected));\n        final List<?> allDifferences = diff.getAllDifferences();\n        assertThat(allDifferences).as(\"should have no differences between:\\n%s\\n and:\\n%s\\n\", xmlPrettyFormatExpected,\n                xmlPrettyFormatExported).isEmpty();\n    }\n\n    public BarResource getBarResource(final String path, final String name, Class<?> clazz) throws IOException {\n        try (final InputStream stream = clazz.getResourceAsStream(path)) {\n            assertThat(stream).isNotNull();\n            final byte[] byteArray = IOUtils.toByteArray(stream);\n            return new BarResource(name, byteArray);\n        }\n    }\n\n    protected byte[] createTestPageContent(final String pageName, final String displayName, final String description)\n            throws Exception {\n        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {\n            final ZipOutputStream zos = new ZipOutputStream(baos);\n            zos.putNextEntry(new ZipEntry(\"Index.groovy\"));\n            zos.write(\"return \\\"\\\";\".getBytes());\n\n            zos.putNextEntry(new ZipEntry(\"page.properties\"));\n            zos.write((\"name=\" + pageName + \"\\n\" + \"displayName=\" + displayName + \"\\n\" + \"description=\" + description\n                    + \"\\n\").getBytes());\n\n            zos.closeEntry();\n            return baos.toByteArray();\n        }\n    }\n\n    protected BusinessObjectModel buildBOM() {\n        final SimpleField name = new SimpleField();\n        name.setName(\"name\");\n        name.setType(FieldType.STRING);\n        final SimpleField age = new SimpleField();\n        age.setName(\"age\");\n        age.setType(FieldType.INTEGER);\n\n        final BusinessObject countryBO = new BusinessObject();\n        countryBO.setQualifiedName(COUNTRY_QUALIFIED_NAME);\n        countryBO.addField(name);\n        countryBO.addUniqueConstraint(\"uk_name\", \"name\");\n\n        final SimpleField street = new SimpleField();\n        street.setName(\"street\");\n        street.setType(FieldType.STRING);\n\n        final SimpleField city = new SimpleField();\n        city.setName(\"city\");\n        city.setType(FieldType.STRING);\n\n        final RelationField country = new RelationField();\n        country.setType(RelationField.Type.AGGREGATION);\n        country.setFetchType(RelationField.FetchType.LAZY);\n        country.setName(\"country\");\n        country.setCollection(Boolean.FALSE);\n        country.setNullable(Boolean.TRUE);\n        country.setReference(countryBO);\n\n        final BusinessObject addressBO = new BusinessObject();\n        addressBO.setQualifiedName(ADDRESS_QUALIFIED_NAME);\n        addressBO.addField(street);\n        addressBO.addField(city);\n        addressBO.addField(country);\n        addressBO.addQuery(COUNT_ADDRESS, \"SELECT count(a) FROM Address a\", Long.class.getName());\n        addressBO.addUniqueConstraint(\"addressUK_with_relation\", \"city\", \"country\");\n\n        final BusinessObject dogBO = new BusinessObject();\n        dogBO.setQualifiedName(DOG_QUALIFIED_NAME);\n        dogBO.addField(name);\n        dogBO.addField(age);\n        final BusinessObject catBO = new BusinessObject();\n        catBO.setQualifiedName(CAT_QUALIFIED_NAME);\n        catBO.addField(name);\n        catBO.addField(age);\n\n        final RelationField addresses = new RelationField();\n        addresses.setType(RelationField.Type.AGGREGATION);\n        addresses.setFetchType(RelationField.FetchType.EAGER);\n        addresses.setName(\"addresses\");\n        addresses.setCollection(Boolean.TRUE);\n        addresses.setNullable(Boolean.TRUE);\n        addresses.setReference(addressBO);\n\n        final RelationField address = new RelationField();\n        address.setType(RelationField.Type.AGGREGATION);\n        address.setFetchType(RelationField.FetchType.LAZY);\n        address.setName(\"address\");\n        address.setCollection(Boolean.FALSE);\n        address.setNullable(Boolean.TRUE);\n        address.setReference(addressBO);\n\n        final RelationField dog = new RelationField();\n        dog.setType(RelationField.Type.COMPOSITION);\n        dog.setFetchType(RelationField.FetchType.EAGER);\n        dog.setName(\"dog\");\n        dog.setCollection(Boolean.FALSE);\n        dog.setNullable(Boolean.TRUE);\n        dog.setReference(dogBO);\n\n        final RelationField cat = new RelationField();\n        cat.setType(RelationField.Type.COMPOSITION);\n        cat.setFetchType(RelationField.FetchType.LAZY);\n        //bug on lazy attribute in composition starting with a capital letter, see:  https://bonitasoft.atlassian.net/browse/BS-16031\n        cat.setName(\"Cat\");\n        cat.setCollection(Boolean.FALSE);\n        cat.setNullable(Boolean.TRUE);\n        cat.setReference(catBO);\n\n        final SimpleField firstName = new SimpleField();\n        firstName.setName(\"firstName\");\n        firstName.setType(FieldType.STRING);\n        firstName.setLength(10);\n\n        final SimpleField birthDate = new SimpleField();\n        birthDate.setName(\"birthDate\");\n        birthDate.setType(FieldType.LOCALDATE);\n        birthDate.setNullable(Boolean.TRUE);\n\n        final SimpleField lastName = new SimpleField();\n        lastName.setName(\"lastName\");\n        lastName.setType(FieldType.STRING);\n        lastName.setNullable(Boolean.FALSE);\n\n        final SimpleField phoneNumbers = new SimpleField();\n        phoneNumbers.setName(\"phoneNumbers\");\n        phoneNumbers.setType(FieldType.STRING);\n        phoneNumbers.setLength(10);\n        phoneNumbers.setCollection(Boolean.TRUE);\n\n        final SimpleField hireDate = new SimpleField();\n        hireDate.setName(\"hireDate\");\n        hireDate.setType(FieldType.DATE);\n\n        final SimpleField booleanField = new SimpleField();\n        booleanField.setName(\"booleanField\");\n        booleanField.setType(FieldType.BOOLEAN);\n\n        final BusinessObject employee = new BusinessObject();\n        employee.setQualifiedName(EMPLOYEE_QUALIFIED_NAME);\n        employee.addField(hireDate);\n        employee.addField(booleanField);\n        employee.addField(firstName);\n        employee.addField(lastName);\n        employee.addField(phoneNumbers);\n        employee.addField(addresses);\n        employee.addField(address);\n        employee.addField(dog);\n        employee.addField(cat);\n        employee.addField(birthDate);\n        employee.setDescription(\"Describe a simple employee\");\n        employee.addUniqueConstraint(\"uk_fl\", \"firstName\", \"lastName\");\n\n        final Query getEmployeeByPersistId = employee\n                .addQuery(\"findByPersistId\", \"SELECT e FROM Employee e WHERE e.persistenceId=:id\",\n                        EMPLOYEE_QUALIFIED_NAME);\n        getEmployeeByPersistId.addQueryParameter(\"id\", Long.class.getName());\n\n        final Query getEmployeeByPhoneNumber = employee.addQuery(GET_EMPLOYEE_BY_PHONE_NUMBER_QUERY_NAME,\n                \"SELECT e FROM Employee e WHERE :phoneNumber IN ELEMENTS(e.phoneNumbers)\", List.class.getName());\n        getEmployeeByPhoneNumber.addQueryParameter(\"phoneNumber\", String.class.getName());\n\n        final Query findByFirstNAmeAndLastNameNewOrder = employee.addQuery(FIND_BY_FIRST_NAME_AND_LAST_NAME_NEW_ORDER,\n                \"SELECT e FROM Employee e WHERE e.firstName =:firstName AND e.lastName = :lastName ORDER BY e.lastName\",\n                List.class.getName());\n        findByFirstNAmeAndLastNameNewOrder.addQueryParameter(\"firstName\", String.class.getName());\n        findByFirstNAmeAndLastNameNewOrder.addQueryParameter(\"lastName\", String.class.getName());\n\n        final Query findByFirstNameFetchAddresses = employee.addQuery(FIND_BY_FIRST_NAME_FETCH_ADDRESSES,\n                \"SELECT e FROM Employee e INNER JOIN FETCH e.addresses WHERE e.firstName =:firstName ORDER BY e.lastName\",\n                List.class.getName());\n        findByFirstNameFetchAddresses.addQueryParameter(\"firstName\", String.class.getName());\n\n        final Query findByHireDate = employee.addQuery(FIND_BY_HIRE_DATE_RANGE,\n                \"SELECT e FROM Employee e WHERE e.hireDate >=:date1 and e.hireDate <=:date2\", List.class.getName());\n        findByHireDate.addQueryParameter(\"date1\", Date.class.getName());\n        findByHireDate.addQueryParameter(\"date2\", Date.class.getName());\n\n        final Query countForFindByHireDate = employee.addQuery(COUNT_FOR_FIND_BY_HIRE_DATE_RANGE,\n                \"SELECT count(e) FROM Employee e WHERE e.hireDate >=:date1 and e.hireDate <=:date2\",\n                Long.class.getName());\n        countForFindByHireDate.addQueryParameter(\"date1\", Date.class.getName());\n        countForFindByHireDate.addQueryParameter(\"date2\", Date.class.getName());\n\n        employee.addQuery(COUNT_EMPLOYEE, \"SELECT COUNT(e) FROM Employee e\", Long.class.getName());\n\n        final Query findEmployeesWithFirstNames = employee.addQuery(FIND_EMPLOYEE_WITH_FIRSTNAMES,\n                \"SELECT e FROM Employee e WHERE e.firstName IN (:firstNames) ORDER BY e.firstName\",\n                List.class.getName());\n        findEmployeesWithFirstNames.addQueryParameter(\"firstNames\", String[].class.getName());\n\n        employee.addIndex(\"IDX_LSTNM\", \"lastName\");\n        employee.addIndex(\"IDX_LSTNM\", \"address\");\n\n        final BusinessObject person = new BusinessObject();\n        person.setQualifiedName(PERSON_QUALIFIED_NAME);\n        person.addField(hireDate);\n        person.addField(firstName);\n        person.addField(lastName);\n        person.addField(phoneNumbers);\n        person.setDescription(\"Describe a simple person\");\n        person.addUniqueConstraint(\"uk_fl\", \"firstName\", \"lastName\");\n\n        final BusinessObject productBO = new BusinessObject();\n        productBO.setQualifiedName(PRODUCT_QUALIFIED_NAME);\n        productBO.addField(name);\n\n        final RelationField products = new RelationField();\n        products.setType(RelationField.Type.AGGREGATION);\n        products.setFetchType(RelationField.FetchType.LAZY);\n        products.setName(\"products\");\n        products.setCollection(Boolean.TRUE);\n        products.setNullable(Boolean.TRUE);\n        products.setReference(productBO);\n\n        final SimpleField releaseYear = new SimpleField();\n        releaseYear.setName(\"releaseYear\");\n        releaseYear.setType(FieldType.STRING);\n\n        final BusinessObject editionBO = new BusinessObject();\n        editionBO.setQualifiedName(\"com.company.model.Edition\");\n        editionBO.addField(releaseYear);\n\n        final RelationField editionField = new RelationField();\n        editionField.setType(RelationField.Type.COMPOSITION);\n        editionField.setFetchType(RelationField.FetchType.EAGER);\n        editionField.setName(\"editions\");\n        editionField.setCollection(Boolean.TRUE);\n        editionField.setNullable(Boolean.TRUE);\n        editionField.setReference(editionBO);\n\n        final BusinessObject catalogBO = new BusinessObject();\n        catalogBO.setQualifiedName(PRODUCT_CATALOG_QUALIFIED_NAME);\n        catalogBO.addField(name);\n        catalogBO.addField(products);\n        catalogBO.addField(editionField);\n\n        final BusinessObjectModel model = new BusinessObjectModel();\n        model.addBusinessObject(employee);\n        model.addBusinessObject(person);\n        model.addBusinessObject(addressBO);\n        model.addBusinessObject(dogBO);\n        model.addBusinessObject(catBO);\n        model.addBusinessObject(countryBO);\n        model.addBusinessObject(productBO);\n        model.addBusinessObject(editionBO);\n        model.addBusinessObject(catalogBO);\n        return model;\n    }\n\n    protected void checkAllParametersAreSerializable(final Class<?> api) {\n        final Method[] methods = api.getMethods();\n        for (final Method method : methods) {\n            if (!isADefaultMethod(method)) {\n                final Class<?>[] parameterTypes = method.getParameterTypes();\n                for (final Class<?> parameterType : parameterTypes) {\n                    if (!parameterType.isPrimitive() && !Collection.class.isAssignableFrom(parameterType)\n                            && !Map.class.isAssignableFrom(parameterType)) {\n                        final boolean assignableFrom = Serializable.class.isAssignableFrom(parameterType);\n                        assertTrue(\n                                \"Method: \" + method.getName() + \" of API: \" + api.getName()\n                                        + \" contains an unserializable parameter \" + parameterType,\n                                assignableFrom);\n                    }\n                }\n                final Class<?> returnType = method.getReturnType();\n                if (!returnType.isPrimitive() && !Collection.class.isAssignableFrom(returnType)\n                        && !Map.class.isAssignableFrom(returnType)) {\n                    final boolean assignableFrom = Serializable.class.isAssignableFrom(returnType);\n                    assertTrue(\n                            \"Method: \" + method.getName() + \" of API: \" + api.getName()\n                                    + \" contains an unserializable return type \" + returnType,\n                            assignableFrom);\n                }\n            }\n        }\n    }\n\n    private boolean isADefaultMethod(final Method method) {\n        return DEFAULT_METHODS.contains(method.getName());\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/BDMTestUtil.java",
    "content": "/**\n * Copyright (C) 2021 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test;\n\nimport java.io.IOException;\nimport java.util.function.Consumer;\n\nimport javax.xml.bind.JAXBException;\n\nimport org.bonitasoft.engine.bdm.BusinessObjectModelConverter;\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.model.field.FieldType;\nimport org.bonitasoft.engine.bdm.model.field.SimpleField;\nimport org.xml.sax.SAXException;\n\npublic class BDMTestUtil {\n\n    public static byte[] getZip(BusinessObjectModel model) throws IOException, JAXBException, SAXException {\n        BusinessObjectModelConverter converter = new BusinessObjectModelConverter();\n        return converter.zip(model);\n    }\n\n    public static BusinessObjectModel buildSimpleBom(final String boQualifiedName) {\n        return businessObjectModel(bom -> {\n            bom.addBusinessObject(businessObject(boQualifiedName, (businessObject) -> {\n                businessObject.addField(stringField(\"aField\"));\n            }));\n        });\n    }\n\n    public static BusinessObjectModel businessObjectModel(Consumer<BusinessObjectModel> apply) {\n        BusinessObjectModel businessObjectModel = new BusinessObjectModel();\n        apply.accept(businessObjectModel);\n        return businessObjectModel;\n    }\n\n    public static BusinessObject businessObject(String boQualifiedName, Consumer<BusinessObject> apply) {\n        final BusinessObject bo = new BusinessObject();\n        bo.setQualifiedName(boQualifiedName);\n        apply.accept(bo);\n        return bo;\n    }\n\n    public static SimpleField stringField(String name) {\n        final SimpleField field = new SimpleField();\n        field.setName(name);\n        field.setType(FieldType.STRING);\n        return field;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/BuildTestUtil.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test;\n\nimport static org.junit.Assert.assertNotNull;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.UnsupportedEncodingException;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.commons.io.IOUtils;\nimport org.bonitasoft.engine.bpm.bar.BarResource;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.bpm.document.Document;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.process.impl.BoundaryEventDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.ConnectorDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.DocumentBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.SubProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder;\nimport org.bonitasoft.engine.connector.Connector;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.expression.ExpressionType;\nimport org.bonitasoft.engine.expression.InvalidExpressionException;\nimport org.bonitasoft.engine.io.IOUtil;\nimport org.bonitasoft.engine.operation.LeftOperand;\nimport org.bonitasoft.engine.operation.LeftOperandBuilder;\nimport org.bonitasoft.engine.operation.Operation;\nimport org.bonitasoft.engine.operation.OperationBuilder;\nimport org.bonitasoft.engine.operation.OperatorType;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\n\npublic class BuildTestUtil {\n\n    public static final String ACTOR_NAME = \"Employee actor\";\n\n    public static final String PROCESS_VERSION = \"1.0\";\n\n    public static final String PROCESS_NAME = \"ProcessName\";\n\n    public static final String EVENT_SUB_PROCESS_NAME = \"eventSubProcess\";\n\n    public static final String DESCRIPTION = \"Coding all-night-long\";\n\n    public static final String PASSWORD = \"bpm\";\n\n    public static final String USERNAME = \"william.jobs\";\n\n    public static final String GROUP_NAME = \"R&D\";\n\n    public static final String ROLE_NAME = \"Developper\";\n\n    public static byte[] buildConnectorImplementationFile(final String definitionId, final String definitionVersion,\n            final String implementationId,\n            final String implementationVersion, final String implementationClassname, String... dependencies)\n            throws UnsupportedEncodingException {\n        final StringBuilder stb = new StringBuilder();\n        stb.append(\"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>\\n\");\n        stb.append(\n                \"<implementation:connectorImplementation xmlns:implementation=\\\"http://www.bonitasoft.org/ns/connector/implementation/6.0\\\">\\n\");\n        stb.append(\"\\t<definitionId>\").append(definitionId).append(\"</definitionId>\\n\");\n        stb.append(\"\\t<definitionVersion>\").append(definitionVersion).append(\"</definitionVersion>\\n\");\n        stb.append(\"\\t<implementationClassname>\").append(implementationClassname)\n                .append(\"</implementationClassname>\\n\");\n        stb.append(\"\\t<implementationId>\").append(implementationId).append(\"</implementationId>\\n\");\n        stb.append(\"\\t<implementationVersion>\").append(implementationVersion).append(\"</implementationVersion>\\n\");\n        if (dependencies.length > 0) {\n            stb.append(\"\\t<jarDependencies>\\n\");\n            for (String dependency : dependencies) {\n                stb.append(\"\\t\\t<jarDependency>\").append(dependency).append(\"</jarDependency>\\n\");\n            }\n            stb.append(\"\\t</jarDependencies>\\n\");\n        }\n        stb.append(\"</implementation:connectorImplementation>\");\n        return stb.toString().getBytes(\"UTF-8\");\n    }\n\n    public static BarResource generateConnectorImplementation(String definitionId, String definitionVersion,\n            Class<? extends Connector> connectorClass) throws UnsupportedEncodingException {\n        return new BarResource(\n                connectorClass.getSimpleName() + \".impl\",\n                BuildTestUtil.buildConnectorImplementationFile(definitionId,\n                        definitionVersion,\n                        definitionId + \"-impl\",\n                        \"generated\", connectorClass.getName()));\n    }\n\n    public static BarResource generateConnectorImplementation(String definitionId, String definitionVersion,\n            String className, String... dependencies) throws UnsupportedEncodingException {\n        return new BarResource(\n                className + \".impl\",\n                BuildTestUtil.buildConnectorImplementationFile(definitionId,\n                        definitionVersion,\n                        definitionId + \"-impl\",\n                        \"generated\", className,\n                        dependencies));\n    }\n\n    public static BusinessArchiveBuilder buildBusinessArchiveWithConnectorAndUserFilter(\n            final ProcessDefinitionBuilder processDefinitionBuilder,\n            final List<BarResource> connectorImplementations, final List<BarResource> generateConnectorDependencies,\n            final List<BarResource> userFilters)\n            throws InvalidProcessDefinitionException {\n        final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(\n                        processDefinitionBuilder.done());\n        for (final BarResource barResource : connectorImplementations) {\n            businessArchiveBuilder.addConnectorImplementation(barResource);\n        }\n\n        for (final BarResource barResource : generateConnectorDependencies) {\n            businessArchiveBuilder.addClasspathResource(barResource);\n        }\n\n        for (final BarResource barResource : userFilters) {\n            businessArchiveBuilder.addUserFilters(barResource);\n        }\n        return businessArchiveBuilder;\n    }\n\n    public static ProcessDefinitionBuilder buildProcessDefinitionWithCallActivity(final String processName,\n            final Expression targetProcessExpr,\n            final Expression targetVersionExpr) {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(processName,\n                PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME);\n        builder.addStartEvent(\"start\");\n        builder.addCallActivity(\"callActivity\", targetProcessExpr, targetVersionExpr);\n        builder.addEndEvent(\"end\").addTerminateEventTrigger();\n        builder.addTransition(\"start\", \"callActivity\").addTransition(\"callActivity\", \"end\");\n        return builder;\n    }\n\n    public static ProcessDefinitionBuilder buildProcessDefinitionWithHumanTaskAndErrorEndEvent(final String processName,\n            final String taskName) {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(processName,\n                PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME);\n        builder.addStartEvent(\"start\");\n        builder.addUserTask(taskName, ACTOR_NAME);\n        builder.addEndEvent(\"end\").addErrorEventTrigger(\"errorCode\");\n        builder.addTransition(\"start\", taskName).addTransition(taskName, \"end\");\n        return builder;\n    }\n\n    public static ProcessDefinitionBuilder buildProcessDefinitionWithAutomaticTaskAndErrorEndEvent(\n            final String processName) {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(processName,\n                PROCESS_VERSION);\n        builder.addStartEvent(\"start\");\n        builder.addAutomaticTask(\"step\");\n        builder.addEndEvent(\"end\").addErrorEventTrigger(\"errorCode\");\n        builder.addTransition(\"start\", \"step\").addTransition(\"step\", \"end\");\n        return builder;\n    }\n\n    public static ProcessDefinitionBuilder buildProcessDefinitionWithAutomaticTaskAndFailedGroovyScript(\n            final String processName)\n            throws InvalidExpressionException {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(processName,\n                PROCESS_VERSION);\n        builder.addStartEvent(\"start\");\n        builder.addAutomaticTask(\"activityThatFail\").addData(\"data\", String.class.getName(),\n                new ExpressionBuilder().createGroovyScriptExpression(\"script\", \"throw new java.lang.RuntimeException()\",\n                        String.class.getName()));\n        builder.addEndEvent(\"end\");\n        builder.addTransition(\"start\", \"activityThatFail\").addTransition(\"activityThatFail\", \"end\");\n        return builder;\n    }\n\n    public static ProcessDefinitionBuilder buildProcessDefinitionWithFailedConnector(final String processName)\n            throws InvalidExpressionException {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(processName,\n                PROCESS_VERSION);\n        final ConnectorDefinitionBuilder connectorDefinitionBuilder = builder\n                .addConnector(\"testConnectorThatThrowException\",\n                        \"testConnectorThatThrowException\", \"1.0\", ConnectorEvent.ON_ENTER)\n                .throwErrorEventWhenFailed(\"errorCode\");\n        connectorDefinitionBuilder.addInput(\"kind\", new ExpressionBuilder().createConstantStringExpression(\"plop\"));\n        builder.addStartEvent(\"start\").addAutomaticTask(\"AutomaticStep\").addEndEvent(\"end\");\n        builder.addTransition(\"start\", \"AutomaticStep\").addTransition(\"AutomaticStep\", \"end\");\n        return builder;\n    }\n\n    public static ProcessDefinitionBuilder buildProcessDefinitionWithAutomaticTaskAndFailedConnector(\n            final String processName)\n            throws InvalidExpressionException {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(processName,\n                PROCESS_VERSION);\n        builder.addStartEvent(\"start\");\n        final ConnectorDefinitionBuilder connectorDefinitionBuilder = builder.addAutomaticTask(\"AutomaticStep\")\n                .addConnector(\"testConnectorThatThrowException\",\n                        \"testConnectorThatThrowException\", \"1.0\", ConnectorEvent.ON_ENTER)\n                .throwErrorEventWhenFailed(\"errorCode\");\n        connectorDefinitionBuilder.addInput(\"kind\", new ExpressionBuilder().createConstantStringExpression(\"plop\"));\n        builder.addEndEvent(\"end\");\n        builder.addTransition(\"start\", \"AutomaticStep\").addTransition(\"AutomaticStep\", \"end\");\n        return builder;\n    }\n\n    public static ProcessDefinitionBuilder buildProcessDefinitionWithUserTaskAndFailedConnector(\n            final String processName)\n            throws InvalidExpressionException {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(processName,\n                PROCESS_VERSION);\n        builder.addStartEvent(\"start\");\n        builder.addActor(ACTOR_NAME).addUserTask(\"StepBeforeFailedConnector\", ACTOR_NAME);\n        final ConnectorDefinitionBuilder connectorDefinitionBuilder = builder.addAutomaticTask(\"AutomaticStep\")\n                .addConnector(\"testConnectorThatThrowException\",\n                        \"testConnectorThatThrowException\", \"1.0\", ConnectorEvent.ON_ENTER)\n                .throwErrorEventWhenFailed(\"errorCode\");\n        connectorDefinitionBuilder.addInput(\"kind\", new ExpressionBuilder().createConstantStringExpression(\"plop\"));\n        builder.addEndEvent(\"end\");\n        builder.addTransition(\"start\", \"StepBeforeFailedConnector\")\n                .addTransition(\"StepBeforeFailedConnector\", \"AutomaticStep\")\n                .addTransition(\"AutomaticStep\", \"end\");\n        return builder;\n    }\n\n    public static ProcessDefinitionBuilder buildProcessDefinitionWithMultiInstanceUserTaskAndFailedConnector(\n            final String processName,\n            final String userTaskName)\n            throws InvalidExpressionException {\n        final String errorCode = \"mistake\";\n\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(processName,\n                PROCESS_VERSION);\n        builder.addActor(ACTOR_NAME);\n        final UserTaskDefinitionBuilder userTaskBuilder = builder.addUserTask(userTaskName, ACTOR_NAME);\n        userTaskBuilder.addMultiInstance(false, new ExpressionBuilder().createConstantIntegerExpression(3));\n        final ConnectorDefinitionBuilder connectorDefinitionBuilder = userTaskBuilder\n                .addConnector(\"testConnectorThatThrowException\",\n                        \"testConnectorThatThrowException\", \"1.0\", ConnectorEvent.ON_FINISH)\n                .throwErrorEventWhenFailed(errorCode);\n        connectorDefinitionBuilder.addInput(\"kind\", new ExpressionBuilder().createConstantStringExpression(\"plop\"));\n        final BoundaryEventDefinitionBuilder boundaryEvent = userTaskBuilder.addBoundaryEvent(\"error\", true);\n        boundaryEvent.addErrorEventTrigger(errorCode);\n        builder.addUserTask(\"normalFlow\", ACTOR_NAME);\n        builder.addUserTask(\"errorFlow\", ACTOR_NAME);\n        builder.addTransition(userTaskName, \"normalFlow\").addTransition(\"error\", \"errorFlow\");\n        return builder;\n    }\n\n    public static void buildErrorEventSubProcessWithUserTask(final String taskName,\n            final ProcessDefinitionBuilder builder) {\n        final SubProcessDefinitionBuilder subProcessBuilder = builder.addSubProcess(EVENT_SUB_PROCESS_NAME, true)\n                .getSubProcessBuilder();\n        subProcessBuilder.addStartEvent(\"SubStart\").addErrorEventTrigger();\n        subProcessBuilder.addUserTask(taskName, ACTOR_NAME);\n        subProcessBuilder.addEndEvent(\"SubEnd\");\n        subProcessBuilder.addTransition(\"SubStart\", taskName).addTransition(taskName, \"SubEnd\");\n    }\n\n    public static Document buildDocument(final String documentName) {\n        final String now = String.valueOf(System.currentTimeMillis());\n        final String fileName = now + \".txt\";\n        final DocumentBuilder builder = new DocumentBuilder().createNewInstance(documentName, false);\n        builder.setFileName(fileName);\n        builder.setContentMimeType(\"plain/text\");\n        builder.setDescription(\"a generated document for tests\");\n        return builder.done();\n    }\n\n    public static SearchOptionsBuilder buildSearchOptions(final long processDefId, final int pageIndex,\n            final int numberOfResults, final String orderByField,\n            final Order order) {\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(pageIndex, numberOfResults);\n        builder.filter(ProcessInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDefId);\n        builder.sort(orderByField, order);\n        return builder;\n    }\n\n    public static SearchOptionsBuilder buildSearchOptions(final int pageIndex, final int numberOfResults,\n            final String orderByField, final Order order) {\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(pageIndex, numberOfResults);\n        builder.sort(orderByField, order);\n        return builder;\n    }\n\n    public static Operation buildStringOperation(final String dataInstanceName, final String newConstantValue,\n            final boolean isTransient)\n            throws InvalidExpressionException {\n        final LeftOperand leftOperand = new LeftOperandBuilder().createNewInstance().setName(dataInstanceName)\n                .setType(isTransient ? LeftOperand.TYPE_TRANSIENT_DATA : LeftOperand.TYPE_DATA).done();\n        final Expression expression = new ExpressionBuilder().createConstantStringExpression(newConstantValue);\n        final Operation operation;\n        operation = new OperationBuilder().createNewInstance().setOperator(\"=\").setLeftOperand(leftOperand)\n                .setType(OperatorType.ASSIGNMENT)\n                .setRightOperand(expression).done();\n        return operation;\n    }\n\n    public static Operation buildIntegerOperation(final String dataInstanceName, final int newConstantValue)\n            throws InvalidExpressionException {\n        final LeftOperand leftOperand = new LeftOperandBuilder().createNewInstance().setName(dataInstanceName).done();\n        final Expression expression = new ExpressionBuilder().createConstantIntegerExpression(newConstantValue);\n        final Operation operation;\n        operation = new OperationBuilder().createNewInstance().setOperator(\"=\").setLeftOperand(leftOperand)\n                .setType(OperatorType.ASSIGNMENT)\n                .setRightOperand(expression).done();\n        return operation;\n    }\n\n    public static Operation buildOperation(final String dataName, final boolean isTransient,\n            final OperatorType operatorType, final String operator,\n            final Expression rightOperand) {\n        final OperationBuilder operationBuilder = new OperationBuilder().createNewInstance();\n        operationBuilder.setOperator(operator);\n        operationBuilder.setRightOperand(rightOperand);\n        operationBuilder.setType(operatorType);\n        operationBuilder.setLeftOperand(new LeftOperandBuilder().createNewInstance(dataName)\n                .setType(isTransient ? LeftOperand.TYPE_TRANSIENT_DATA : LeftOperand.TYPE_DATA).done());\n        return operationBuilder.done();\n    }\n\n    public static Operation buildAssignOperation(final String dataInstanceName, final String expressionContent,\n            final ExpressionType expressionType,\n            final String returnType) throws InvalidExpressionException {\n        final LeftOperand leftOperand = new LeftOperandBuilder().createNewInstance().setName(dataInstanceName).done();\n        final Expression expression = new ExpressionBuilder().createNewInstance(dataInstanceName)\n                .setContent(expressionContent)\n                .setExpressionType(expressionType).setReturnType(returnType).done();\n        return new OperationBuilder().createNewInstance().setOperator(\"=\").setLeftOperand(leftOperand)\n                .setType(OperatorType.ASSIGNMENT)\n                .setRightOperand(expression).done();\n    }\n\n    public static List<String> buildActorAndDescription(final ProcessDefinitionBuilder processBuilder,\n            final int nbActor) {\n        final List<String> actorList = new ArrayList<>();\n        for (int i = 1; i <= nbActor; i++) {\n            final String actorName = ACTOR_NAME + i;\n            processBuilder.addActor(actorName).addDescription(DESCRIPTION + i);\n            actorList.add(actorName);\n        }\n        return actorList;\n    }\n\n    public static DesignProcessDefinition buildProcessDefinitionWithActorAndThreeHumanStepsAndThreeTransition()\n            throws InvalidProcessDefinitionException {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,\n                PROCESS_VERSION);\n        processBuilder.addActor(BuildTestUtil.ACTOR_NAME).addDescription(DESCRIPTION);\n\n        // 1 instance of process def:\n        return processBuilder.addAutomaticTask(\"step1\").addUserTask(\"step2\", BuildTestUtil.ACTOR_NAME)\n                .addUserTask(\"step3\", BuildTestUtil.ACTOR_NAME)\n                .addUserTask(\"step4\", BuildTestUtil.ACTOR_NAME).addTransition(\"step1\", \"step2\")\n                .addTransition(\"step1\", \"step3\").addTransition(\"step1\", \"step4\")\n                .getProcess();\n    }\n\n    public static List<DesignProcessDefinition> buildNbProcessDefinitionWithHumanAndAutomatic(final int nbProcess,\n            final List<String> stepNames, final List<Boolean> isHuman) throws InvalidProcessDefinitionException {\n        final List<DesignProcessDefinition> processDefinitions = new ArrayList<>();\n        for (int i = 0; i < nbProcess; i++) {\n            String processName = PROCESS_NAME;\n            if (i >= 0 && i < 10) {\n                processName += \"0\";\n            }\n            final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps(\n                    processName + i, PROCESS_VERSION + i,\n                    stepNames, isHuman);\n            processDefinitions.add(designProcessDefinition);\n        }\n        return processDefinitions;\n    }\n\n    public static DesignProcessDefinition buildProcessDefinitionWithHumanAndAutomaticSteps(final String processName,\n            final String processVersion,\n            final List<String> stepNames, final List<Boolean> isHuman, final String actorName,\n            final boolean addActorInitiator)\n            throws InvalidProcessDefinitionException {\n        return buildProcessDefinitionWithHumanAndAutomaticSteps(processName, processVersion, stepNames, isHuman,\n                actorName, addActorInitiator, false);\n    }\n\n    public static DesignProcessDefinition buildProcessDefinitionWithHumanAndAutomaticSteps(final String processName,\n            final String processVersion,\n            final List<String> stepNames, final List<Boolean> isHuman) throws InvalidProcessDefinitionException {\n        return buildProcessDefinitionWithHumanAndAutomaticSteps(processName, processVersion, stepNames, isHuman,\n                ACTOR_NAME, false);\n    }\n\n    public static DesignProcessDefinition buildProcessDefinitionWithHumanAndAutomaticSteps(final List<String> stepNames,\n            final List<Boolean> isHuman)\n            throws InvalidProcessDefinitionException {\n        return buildProcessDefinitionWithHumanAndAutomaticSteps(PROCESS_NAME, PROCESS_VERSION, stepNames, isHuman,\n                ACTOR_NAME, false);\n    }\n\n    public static DesignProcessDefinition buildProcessDefinitionWithHumanAndAutomaticSteps(final String processName,\n            final String processVersion,\n            final List<String> stepNames, final List<Boolean> isHuman, final String actorName,\n            final boolean addActorInitiator,\n            final boolean parallelActivities)\n            throws InvalidProcessDefinitionException {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(processName,\n                processVersion);\n        if (!isHuman.isEmpty() && isHuman.contains(true)) {\n            processBuilder.addActor(actorName);\n            if (addActorInitiator) {\n                processBuilder.setActorInitiator(actorName);\n            }\n        }\n        for (int i = 0; i < stepNames.size(); i++) {\n            final String stepName = stepNames.get(i);\n            if (isHuman.get(i)) {\n                processBuilder.addUserTask(stepName, actorName);\n            } else {\n                processBuilder.addAutomaticTask(stepName);\n            }\n        }\n        if (!parallelActivities) {\n            for (int i = 0; i < stepNames.size() - 1; i++) {\n                processBuilder.addTransition(stepNames.get(i), stepNames.get(i + 1));\n            }\n        }\n        return processBuilder.done();\n    }\n\n    public static byte[] generateContent(final Document doc) {\n        return doc.getName().getBytes();\n    }\n\n    public static BarResource generateJarAndBuildBarResource(final Class<?> clazz, final String name)\n            throws IOException {\n        final byte[] data = IOUtil.generateJar(clazz);\n        return new BarResource(name, data);\n    }\n\n    public static BarResource getContentAndBuildBarResource(final String name, final Class<? extends Connector> clazz)\n            throws IOException {\n        final InputStream stream = clazz.getResourceAsStream(name);\n        assertNotNull(stream);\n        final byte[] byteArray = IOUtils.toByteArray(stream);\n        stream.close();\n        return new BarResource(name, byteArray);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/CommonTestUtil.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipOutputStream;\n\n/**\n * author Emmanuel Duchastenier\n */\npublic class CommonTestUtil {\n\n    /**\n     * @param pageName\n     * @param displayName\n     * @param description\n     * @param extraProperties some property to add in page.properties file. eg: value=data\n     * @return\n     * @throws IOException\n     */\n    public static byte[] createTestPageContent(final String pageName, final String displayName,\n            final String description, final String... extraProperties) throws IOException {\n        try (final ByteArrayOutputStream baos = new ByteArrayOutputStream();\n                ZipOutputStream zos = new ZipOutputStream(baos)) {\n            zos.putNextEntry(new ZipEntry(\"Index.groovy\"));\n            zos.write(\"return \\\"\\\";\".getBytes());\n\n            zos.putNextEntry(new ZipEntry(\"page.properties\"));\n            final StringBuilder stringBuilder = new StringBuilder();\n            stringBuilder.append(\"name=\");\n            stringBuilder.append(pageName);\n            stringBuilder.append(\"\\n\");\n            stringBuilder.append(\"displayName=\");\n            stringBuilder.append(displayName);\n            stringBuilder.append(\"\\n\");\n            stringBuilder.append(\"description=\");\n            stringBuilder.append(description);\n            stringBuilder.append(\"\\n\");\n            for (final String extraProperty : extraProperties) {\n                stringBuilder.append(extraProperty);\n                stringBuilder.append(\"\\n\");\n            }\n\n            zos.write(stringBuilder.toString().getBytes(StandardCharsets.UTF_8));\n\n            zos.closeEntry();\n            return baos.toByteArray();\n        }\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/PlatformTestUtil.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test;\n\nimport org.bonitasoft.engine.api.APIClient;\nimport org.bonitasoft.engine.api.LoginAPI;\nimport org.bonitasoft.engine.api.PlatformAPI;\nimport org.bonitasoft.engine.api.PlatformAPIAccessor;\nimport org.bonitasoft.engine.api.PlatformLoginAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.execution.ProcessStarterVerifier;\nimport org.bonitasoft.engine.session.PlatformSession;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.test.context.ContextConfiguration;\n\n/**\n * @author Celine Souchet\n */\n@ContextConfiguration(classes = PlatformTestUtil.TestConfiguration.class)\npublic class PlatformTestUtil {\n\n    public static final String DEFAULT_TECHNICAL_LOGGER_USERNAME = \"install\";\n\n    public static final String DEFAULT_TECHNICAL_LOGGER_PASSWORD = \"install\";\n\n    public PlatformSession loginOnPlatform() throws BonitaException {\n        final PlatformLoginAPI platformLoginAPI = getPlatformLoginAPI();\n        return platformLoginAPI.login(\"platformAdmin\", \"platform\");\n    }\n\n    public void logoutOnPlatform(final PlatformSession session) throws BonitaException {\n        final PlatformLoginAPI platformLoginAPI = getPlatformLoginAPI();\n        platformLoginAPI.logout(session);\n    }\n\n    public PlatformLoginAPI getPlatformLoginAPI() throws BonitaException {\n        return PlatformAPIAccessor.getPlatformLoginAPI();\n    }\n\n    public LoginAPI getLoginAPI() throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return TenantAPIAccessor.getLoginAPI();\n    }\n\n    public PlatformAPI getPlatformAPI(final PlatformSession session)\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return PlatformAPIAccessor.getPlatformAPI(session);\n    }\n\n    protected void stopAndStartPlatform() throws BonitaException {\n        final PlatformSession loginPlatform = loginOnPlatform();\n        final PlatformAPI platformAPI = PlatformAPIAccessor.getPlatformAPI(loginPlatform);\n        platformAPI.stopNode();\n        platformAPI.startNode();\n        logoutOnPlatform(loginPlatform);\n    }\n\n    public void deployCommandsOnDefaultTenant() throws BonitaException {\n        APIClient apiClient = new APIClient();\n        apiClient.login(DEFAULT_TECHNICAL_LOGGER_USERNAME, DEFAULT_TECHNICAL_LOGGER_PASSWORD);\n        ClientEventUtil.deployCommand(apiClient.getSession());\n        apiClient.logout();\n    }\n\n    /**\n     * Configuration class used to override bean definitions for test purposes.\n     */\n    @Configuration\n    static class TestConfiguration {\n\n        @Bean\n        ProcessStarterVerifier processStarterVerifierImpl() {\n            return processInstance -> {\n                // Override this bean to disable the process starter verifier\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/Repeat.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * @author Laurent Leseigneur\n *         <p>use this rule to play x times the same junit test and add rule {@link RepeatRule}.</p>\n *         Example:<br>\n *\n *         <pre>\n * {@code\n * &#64;Rule public RepeatRule repeatRule = new RepeatRule();\n * &#64;Repeat(times = 100)\n *               public void testName() throws Exception {\n *               ...\n *               }\n *               }\n *         </pre>\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target({\n        java.lang.annotation.ElementType.METHOD\n})\npublic @interface Repeat {\n\n    public abstract int times();\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/RepeatRule.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test;\n\nimport org.junit.rules.TestRule;\nimport org.junit.runner.Description;\nimport org.junit.runners.model.Statement;\n\n/**\n * @author Laurent Leseigneur\n *         <p>use this rule to play x times the same junit test and add rule {@link Repeat}.</p>\n *         Example:<br>\n *\n *         <pre>\n * {@code\n * &#64;Rule public RepeatRule repeatRule = new RepeatRule();\n * &#64;Repeat(times = 100)\n * public void testName() throws Exception {\n *   ...\n * }\n * }\n *         </pre>\n */\npublic class RepeatRule implements TestRule {\n\n    private static class RepeatStatement extends Statement {\n\n        private final int times;\n        private final Statement statement;\n\n        private RepeatStatement(final int times, final Statement statement) {\n            this.times = times;\n            this.statement = statement;\n        }\n\n        @Override\n        public void evaluate() throws Throwable {\n            for (int i = 0; i < times; i++) {\n                statement.evaluate();\n            }\n        }\n    }\n\n    @Override\n    public Statement apply(final Statement statement, final Description description) {\n        Statement result = statement;\n        final Repeat repeat = description.getAnnotation(Repeat.class);\n        if (repeat != null) {\n            final int times = repeat.times();\n            result = new RepeatStatement(times, statement);\n        }\n        return result;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/StartProcessUntilStep.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test;\n\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\n\npublic class StartProcessUntilStep {\n\n    private final ProcessInstance processInstance;\n\n    private final ActivityInstance activityInstance;\n\n    public StartProcessUntilStep(final ProcessInstance processInstance, final ActivityInstance activityInstance) {\n        super();\n        this.processInstance = processInstance;\n        this.activityInstance = activityInstance;\n    }\n\n    public ProcessInstance getProcessInstance() {\n        return processInstance;\n    }\n\n    public ActivityInstance getActivityInstance() {\n        return activityInstance;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/TestStates.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic enum TestStates {\n\n    INITIALIZING(\"initializing\"),\n\n    NORMAL_FINAL(\"completed\"),\n\n    READY(\"ready\"),\n\n    SKIPPED(\"skipped\"),\n\n    WAITING(\"waiting\"),\n\n    FAILED(\"failed\"),\n\n    ABORTED(\"aborted\"),\n\n    EXECUTING(\"executing\"),\n\n    CANCELLED(\"cancelled\"),\n\n    INTERRUPTED(\"interrupted\");\n\n    private final String stateName;\n\n    private TestStates(final String stateName) {\n        this.stateName = stateName;\n    }\n\n    public String getStateName() {\n        return stateName;\n    }\n\n    @Override\n    public String toString() {\n        return stateName;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/WaitUntil.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test;\n\nimport java.util.Date;\n\n/**\n * @author Baptiste Mesta\n */\n@Deprecated\npublic abstract class WaitUntil {\n\n    private final int timeout;\n\n    private final int repeatEach;\n\n    private final boolean throwExceptions;\n\n    /**\n     * @param repeatEach\n     *        time to wait for before retrying, if condition not fullfilled, in milliseconds\n     * @param timeout\n     *        max time to wait for, in milliseconds\n     */\n    public WaitUntil(final int repeatEach, final int timeout) {\n        this(repeatEach, timeout, true);\n    }\n\n    /**\n     * @param repeatEach\n     *        time to wait for before retrying, if condition not fullfilled, in milliseconds\n     * @param timeout\n     *        max time to wait for, in milliseconds\n     * @param throwExceptions\n     *        can the check condition throw exceptions?\n     */\n    public WaitUntil(final int repeatEach, final int timeout, final boolean throwExceptions) {\n        this.throwExceptions = throwExceptions;\n        if (repeatEach > timeout) {\n            throw new AssertionError(\"timeout \" + timeout + \" cannot be smaller than repeatEach \" + repeatEach);\n        }\n        this.repeatEach = repeatEach;\n        this.timeout = timeout;\n    }\n\n    public boolean waitUntil() throws Exception {\n        final long limit = new Date().getTime() + timeout;\n        while (new Date().getTime() < limit) {\n            Thread.sleep(repeatEach);\n            if (checkCondition()) {\n                return true;\n            }\n        }\n        return checkCondition();\n    }\n\n    protected boolean checkCondition() throws Exception {\n        if (throwExceptions) {\n            return check();\n        }\n        try {\n            return check();\n        } catch (final Exception e) {\n            // do nothing\n        }\n        return false;\n    }\n\n    /**\n     * Condition to check for.\n     *\n     * @return true if condition is true, false otherwise.\n     * @throws Exception\n     */\n    protected abstract boolean check() throws Exception;\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/check/CheckNbAssignedTaskOf.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.check;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.test.WaitUntil;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic final class CheckNbAssignedTaskOf extends WaitUntil {\n\n    private final int nbActivities;\n\n    private final User user;\n\n    private final ProcessAPI processAPI;\n\n    private List<HumanTaskInstance> assignedHumanTaskInstances;\n\n    public CheckNbAssignedTaskOf(final ProcessAPI processAPI, final int repeatEach, final int timeout,\n            final boolean throwExceptions, final int nbActivities,\n            final User user) {\n        super(repeatEach, timeout, throwExceptions);\n        this.nbActivities = nbActivities;\n        this.user = user;\n        this.processAPI = processAPI;\n    }\n\n    @Override\n    protected boolean check() {\n        assignedHumanTaskInstances = processAPI.getAssignedHumanTaskInstances(user.getId(), 0,\n                Math.max(nbActivities, 20), ActivityInstanceCriterion.NAME_ASC);\n        return assignedHumanTaskInstances.size() == nbActivities;\n    }\n\n    /**\n     * @return the pendingHumanTaskInstances\n     */\n    public List<HumanTaskInstance> getAssingnedHumanTaskInstances() {\n        return assignedHumanTaskInstances;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/check/CheckNbOfActivities.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.check;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.bonitasoft.engine.test.WaitUntil;\n\n/**\n * @author Baptiste Mesta\n */\n@Deprecated\npublic final class CheckNbOfActivities extends WaitUntil {\n\n    private final ProcessAPI processAPI;\n\n    private final ProcessInstance processInstance;\n\n    private final int nbActivities;\n\n    private Set<ActivityInstance> result;\n\n    private String activityState = null;\n\n    @Deprecated\n    public CheckNbOfActivities(final ProcessAPI processAPI, final int repeatEach, final int timeout,\n            final boolean throwExceptions,\n            final ProcessInstance processInstance, final int nbActivities) {\n        super(repeatEach, timeout, throwExceptions);\n        this.processInstance = processInstance;\n        this.nbActivities = nbActivities;\n        this.processAPI = processAPI;\n    }\n\n    @Deprecated\n    public CheckNbOfActivities(final ProcessAPI processAPI, final int repeatEach, final int timeout,\n            final boolean throwExceptions,\n            final ProcessInstance processInstance, final int nbActivities, final TestStates state) {\n        this(processAPI, repeatEach, timeout, throwExceptions, processInstance, nbActivities);\n        activityState = state.getStateName();\n    }\n\n    @Override\n    protected boolean check() {\n        final List<ActivityInstance> activities = processAPI.getActivities(processInstance.getId(), 0, 200);\n        result = new HashSet<ActivityInstance>(activities.size());\n        // The number of activities is the one expected...\n\n        if (activityState != null) {\n            for (final ActivityInstance activityInstance : activities) {\n                // ... and all states are equal to the expected one:\n                if (activityInstance.getState().equals(activityState)) {\n                    result.add(activityInstance);\n                }\n            }\n        } else {\n            result.addAll(activities);\n        }\n        final boolean check = result.size() == nbActivities;\n\n        return check;// get activities with a state\n    }\n\n    public Set<ActivityInstance> getResult() {\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/check/CheckNbOfArchivedActivities.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.check;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.bonitasoft.engine.test.WaitUntil;\n\n@Deprecated\npublic final class CheckNbOfArchivedActivities extends WaitUntil {\n\n    private final ProcessAPI processAPI;\n\n    private final ProcessInstance processInstance;\n\n    private final int nbActivities;\n\n    private Set<ArchivedActivityInstance> result;\n\n    private String activityState = null;\n\n    @Deprecated\n    public CheckNbOfArchivedActivities(final ProcessAPI processAPI, final int repeatEach, final int timeout,\n            final boolean throwExceptions,\n            final ProcessInstance processInstance, final int nbActivities) {\n        super(repeatEach, timeout, throwExceptions);\n        this.processInstance = processInstance;\n        this.nbActivities = nbActivities;\n        this.processAPI = processAPI;\n    }\n\n    @Deprecated\n    public CheckNbOfArchivedActivities(final ProcessAPI processAPI, final int repeatEach, final int timeout,\n            final boolean throwExceptions,\n            final ProcessInstance processInstance, final int nbActivities, final TestStates state) {\n        this(processAPI, repeatEach, timeout, throwExceptions, processInstance, nbActivities);\n        activityState = state.getStateName();\n    }\n\n    @Override\n    protected boolean check() {\n        final List<ArchivedActivityInstance> activities = processAPI.getArchivedActivityInstances(\n                processInstance.getId(), 0, 200,\n                ActivityInstanceCriterion.NAME_ASC);\n        result = new HashSet<ArchivedActivityInstance>(activities.size());\n        // The number of activities is the one expected...\n\n        if (activityState != null) {\n            for (final ArchivedActivityInstance activityInstance : activities) {\n                // ... and all states are equal to the expected one:\n                if (activityInstance.getState().equals(activityState)) {\n                    result.add(activityInstance);\n                }\n            }\n        } else {\n            result.addAll(activities);\n        }\n        final boolean check = result.size() == nbActivities;\n\n        return check;// get activities with a state\n    }\n\n    public Set<ArchivedActivityInstance> getResult() {\n        return result;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/check/CheckNbOfArchivedActivityInstances.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.check;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.test.WaitUntil;\n\n/**\n * @author Baptiste Mesta\n */\n@Deprecated\npublic final class CheckNbOfArchivedActivityInstances extends WaitUntil {\n\n    private final ProcessAPI processAPI;\n\n    private final ProcessInstance processInstance1;\n\n    private final int expected;\n\n    @Deprecated\n    public CheckNbOfArchivedActivityInstances(final int repeatEach, final int timeout,\n            final ProcessInstance processInstance, final int expected,\n            final ProcessAPI processAPI) {\n        super(repeatEach, timeout, false);\n        this.processInstance1 = processInstance;\n        this.expected = expected;\n        this.processAPI = processAPI;\n    }\n\n    @Override\n    protected boolean check() {\n        return processAPI\n                .getArchivedActivityInstances(processInstance1.getId(), 0, 100, ActivityInstanceCriterion.DEFAULT)\n                .size() == expected;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/check/CheckNbOfHumanTasks.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.check;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.test.WaitUntil;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@Deprecated\npublic final class CheckNbOfHumanTasks extends WaitUntil {\n\n    private final ProcessAPI processAPI;\n\n    private final long nbOfHumanTasks;\n\n    private final SearchOptions searchOptions;\n\n    private SearchResult<HumanTaskInstance> humanTaskInstances;\n\n    @Deprecated\n    public CheckNbOfHumanTasks(final int repeatEach, final int timeout, final boolean throwExceptions,\n            final long nbOfHumanTasks,\n            final SearchOptions searchOptions, final ProcessAPI processAPI) {\n        super(repeatEach, timeout, throwExceptions);\n        this.nbOfHumanTasks = nbOfHumanTasks;\n        this.searchOptions = searchOptions;\n        this.processAPI = processAPI;\n    }\n\n    @Override\n    protected boolean check() throws Exception {\n        humanTaskInstances = processAPI.searchHumanTaskInstances(searchOptions);\n        return nbOfHumanTasks == humanTaskInstances.getCount();\n    }\n\n    public SearchResult<HumanTaskInstance> getHumanTaskInstances() {\n        return humanTaskInstances;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/check/CheckNbOfOpenActivities.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.check;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.test.WaitUntil;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@Deprecated\npublic final class CheckNbOfOpenActivities extends WaitUntil {\n\n    private final ProcessAPI processAPI;\n\n    private final ProcessInstance processInstance;\n\n    private final int nbActivities;\n\n    @Deprecated\n    public CheckNbOfOpenActivities(final int repeatEach, final int timeout, final boolean throwExceptions,\n            final ProcessInstance processInstance,\n            final int nbActivities, final ProcessAPI processAPI) {\n        super(repeatEach, timeout, throwExceptions);\n        this.processInstance = processInstance;\n        this.nbActivities = nbActivities;\n        this.processAPI = processAPI;\n    }\n\n    @Override\n    protected boolean check() throws Exception {\n        final int activityNumber = processAPI.getNumberOfOpenedActivityInstances(processInstance.getId());\n        // The number of activities must be the one expected:\n        return activityNumber == nbActivities;\n    }\n\n    public int getNumberOfOpenActivities() {\n        return nbActivities;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/check/CheckNbOfProcessInstances.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.check;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceCriterion;\nimport org.bonitasoft.engine.test.WaitUntil;\n\n@Deprecated\npublic final class CheckNbOfProcessInstances extends WaitUntil {\n\n    private final ProcessAPI processAPI;\n\n    private final int nbOfProcInst;\n\n    private List<ProcessInstance> result;\n\n    private final ProcessInstanceCriterion orderBy;\n\n    @Deprecated\n    public CheckNbOfProcessInstances(final int repeatEach, final int timeout, final int nbOfProcInst,\n            final ProcessInstanceCriterion orderBy,\n            final ProcessAPI processAPI) {\n        super(repeatEach, timeout);\n        this.nbOfProcInst = nbOfProcInst;\n        this.orderBy = orderBy;\n        this.processAPI = processAPI;\n    }\n\n    @Deprecated\n    public CheckNbOfProcessInstances(final int repeatEach, final int timeout, final int nbOfProcInst,\n            final ProcessAPI processAPI) {\n        this(repeatEach, timeout, nbOfProcInst, ProcessInstanceCriterion.NAME_ASC, processAPI);\n    }\n\n    @Override\n    protected boolean check() {\n        result = processAPI.getProcessInstances(0, nbOfProcInst + 1, orderBy);\n        return nbOfProcInst == result.size();\n    }\n\n    public List<ProcessInstance> getResult() {\n        return result;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/check/CheckNbPendingTaskOf.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.check;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.test.WaitUntil;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @deprecated use {@link org.bonitasoft.engine.test.APITestUtil} .waitFor...\n */\n@Deprecated\npublic final class CheckNbPendingTaskOf extends WaitUntil {\n\n    private final int nbActivities;\n\n    private final User user;\n\n    private final ProcessAPI processAPI;\n\n    private List<HumanTaskInstance> pendingHumanTaskInstances;\n\n    private final ActivityInstanceCriterion orderBy;\n\n    public CheckNbPendingTaskOf(final ProcessAPI processAPI, final int repeatEach, final int timeout,\n            final boolean throwExceptions, final int nbActivities,\n            final User user) {\n        this(processAPI, repeatEach, timeout, throwExceptions, nbActivities, user, ActivityInstanceCriterion.NAME_ASC);\n    }\n\n    public CheckNbPendingTaskOf(final ProcessAPI processAPI, final int repeatEach, final int timeout,\n            final boolean throwExceptions, final int nbActivities,\n            final User user, final ActivityInstanceCriterion orderBy) {\n        super(repeatEach, timeout, throwExceptions);\n        this.nbActivities = nbActivities;\n        this.user = user;\n        this.processAPI = processAPI;\n        this.orderBy = orderBy;\n    }\n\n    @Override\n    protected boolean check() {\n        pendingHumanTaskInstances = processAPI.getPendingHumanTaskInstances(user.getId(), 0, Math.max(nbActivities, 20),\n                orderBy);\n        return pendingHumanTaskInstances.size() == nbActivities;\n    }\n\n    public List<HumanTaskInstance> getPendingHumanTaskInstances() {\n        return pendingHumanTaskInstances;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/check/CheckNbPendingTasksForUserUsingSearch.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.check;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.test.WaitUntil;\n\n/**\n * @author Emmanuel Duchastenier\n * @author Matthieu Chaffotte\n */\npublic final class CheckNbPendingTasksForUserUsingSearch extends WaitUntil {\n\n    private final int nbTasks;\n\n    private final long userId;\n\n    private final ProcessAPI processAPI;\n\n    private List<HumanTaskInstance> pendingHumanTasks;\n\n    private final SearchOptions searchOptions;\n\n    public CheckNbPendingTasksForUserUsingSearch(final ProcessAPI processAPI, final int repeatEach, final int timeout,\n            final boolean throwExceptions,\n            final int nbTasks, final long userId, final SearchOptions searchOptions) {\n        super(repeatEach, timeout, throwExceptions);\n        this.nbTasks = nbTasks;\n        this.userId = userId;\n        this.processAPI = processAPI;\n        this.searchOptions = searchOptions;\n    }\n\n    @Override\n    protected boolean check() throws Exception {\n        final SearchResult<HumanTaskInstance> searchResult = processAPI.searchPendingTasksForUser(userId,\n                searchOptions);\n        pendingHumanTasks = searchResult.getResult();\n        return searchResult.getCount() == nbTasks;\n    }\n\n    public List<HumanTaskInstance> getPendingHumanTasks() {\n        return pendingHumanTasks;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/check/CheckProcessInstanceIsArchived.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.check;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstance;\nimport org.bonitasoft.engine.test.WaitUntil;\n\n/**\n * @author Baptiste Mesta\n */\n@Deprecated\npublic final class CheckProcessInstanceIsArchived extends WaitUntil {\n\n    private final ProcessAPI processAPI;\n\n    private final long processInstance;\n\n    @Deprecated\n    public CheckProcessInstanceIsArchived(final int repeatEach, final int timeout, final long processInstance,\n            final ProcessAPI processAPI) {\n        super(repeatEach, timeout, false);\n        this.processInstance = processInstance;\n        this.processAPI = processAPI;\n    }\n\n    @Override\n    protected boolean check() throws Exception {\n        ArchivedProcessInstance archivedProcessInstance;\n        archivedProcessInstance = processAPI.getFinalArchivedProcessInstance(processInstance);\n        return archivedProcessInstance != null && archivedProcessInstance.getEndDate() != null;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/wait/WaitForActivity.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.wait;\n\nimport java.util.Iterator;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.test.WaitUntil;\n\n@Deprecated\npublic class WaitForActivity extends WaitUntil {\n\n    private final String activityName;\n\n    private final long processInstanceId;\n\n    private String state = null;\n\n    private ActivityInstance result;\n\n    private final ProcessAPI processAPI;\n\n    @Deprecated\n    public WaitForActivity(final int repeatEach, final int timeout, final String activityName,\n            final long processInstanceId, final ProcessAPI processAPI) {\n        super(repeatEach, timeout);\n        this.activityName = activityName;\n        this.processInstanceId = processInstanceId;\n        this.processAPI = processAPI;\n    }\n\n    @Deprecated\n    public WaitForActivity(final int repeatEach, final int timeout, final String activityName,\n            final long processInstanceId, final String state,\n            final ProcessAPI processAPI) {\n        super(repeatEach, timeout);\n        this.activityName = activityName;\n        this.processInstanceId = processInstanceId;\n        this.state = state;\n        this.processAPI = processAPI;\n    }\n\n    @Override\n    protected boolean check() {\n        final List<ActivityInstance> activityInstances = processAPI.getActivities(processInstanceId, 0, 10);\n        final Iterator<ActivityInstance> iterator = activityInstances.iterator();\n        boolean found = false;\n        while (iterator.hasNext() && !found) {\n            final ActivityInstance activityInstance = iterator.next();\n            if (activityInstance.getName().equals(activityName)) {\n                if (state == null || state.equals(activityInstance.getState())) {\n                    result = activityInstance;\n                    found = true;\n                }\n            }\n        }\n        return found;\n    }\n\n    public ActivityInstance getResult() {\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/wait/WaitForArchivedActivity.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.wait;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.bonitasoft.engine.test.WaitUntil;\n\n@Deprecated\npublic class WaitForArchivedActivity extends WaitUntil {\n\n    private final long activityId;\n\n    private ArchivedActivityInstance archivedActivityInstance;\n\n    private final String stateName;\n\n    private final ProcessAPI processAPI;\n\n    public WaitForArchivedActivity(final int repeatEach, final int timeout, final long activityId,\n            final TestStates state, final ProcessAPI processAPI) {\n        super(repeatEach, timeout, false);\n        this.activityId = activityId;\n        this.stateName = state.getStateName();\n        this.processAPI = processAPI;\n    }\n\n    @Override\n    protected boolean check() throws BonitaException {\n        archivedActivityInstance = processAPI.getArchivedActivityInstance(activityId);\n        return stateName.equals(archivedActivityInstance.getState());\n    }\n\n    public ArchivedActivityInstance getArchivedActivityInstance() {\n        return archivedActivityInstance;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/wait/WaitForAssignedStep.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.wait;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.test.WaitUntil;\n\npublic class WaitForAssignedStep extends WaitUntil {\n\n    private final ProcessAPI processAPI;\n\n    private final String userTaskName;\n\n    private final long processInstanceId;\n\n    private final long userId;\n\n    private HumanTaskInstance result;\n\n    public WaitForAssignedStep(final ProcessAPI processAPI, final String userTaskName, final long processInstanceId,\n            final long userId) {\n        super(100, 1500);\n        this.processAPI = processAPI;\n        this.userTaskName = userTaskName;\n        this.processInstanceId = processInstanceId;\n        this.userId = userId;\n    }\n\n    @Override\n    protected boolean check() {\n        final List<HumanTaskInstance> taskInstances = processAPI.getAssignedHumanTaskInstances(userId, 0, 20,\n                ActivityInstanceCriterion.DEFAULT);\n        for (final HumanTaskInstance taskInstance : taskInstances) {\n            if (taskInstance.getName().equals(userTaskName)\n                    && taskInstance.getParentProcessInstanceId() == processInstanceId) {\n                result = taskInstance;\n                return true;\n            }\n        }\n        return false;\n    }\n\n    public HumanTaskInstance getResult() {\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/wait/WaitForCompletedArchivedStep.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.wait;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.flownode.ActivityStates;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstanceSearchDescriptor;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.test.WaitUntil;\n\n/**\n * @author Baptiste Mesta\n */\n@Deprecated\npublic final class WaitForCompletedArchivedStep extends WaitUntil {\n\n    private final String value;\n\n    private final long id;\n\n    private final ProcessAPI processApi;\n\n    private ArchivedHumanTaskInstance archivedHumanTaskInstance;\n\n    public WaitForCompletedArchivedStep(final int repeatEach, final int timeout, final String stepName,\n            final long processDefinitionId,\n            final ProcessAPI processApi) {\n        super(repeatEach, timeout);\n        this.processApi = processApi;\n        value = stepName;\n        id = processDefinitionId;\n    }\n\n    @Override\n    protected boolean check() throws Exception {\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n        builder.filter(ArchivedHumanTaskInstanceSearchDescriptor.PROCESS_DEFINITION_ID, id);\n        builder.filter(ArchivedHumanTaskInstanceSearchDescriptor.NAME, value);\n        builder.filter(ArchivedHumanTaskInstanceSearchDescriptor.STATE_NAME, ActivityStates.COMPLETED_STATE);\n        final SearchResult<ArchivedHumanTaskInstance> searchArchivedTasks = processApi\n                .searchArchivedHumanTasks(builder.done());\n        final boolean ok = searchArchivedTasks.getCount() >= 1;\n        if (ok) {\n            archivedHumanTaskInstance = searchArchivedTasks.getResult().get(0);\n        }\n        return ok;\n    }\n\n    public ArchivedHumanTaskInstance getArchivedTask() {\n        return archivedHumanTaskInstance;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/wait/WaitForDataValue.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.wait;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.test.WaitUntil;\n\n/**\n * @author Sébastien Chevassu\n * @author Celine Souchet\n */\npublic class WaitForDataValue extends WaitUntil {\n\n    private final ProcessAPI processAPI;\n\n    private final long processInstanceId;\n\n    private final String dataName;\n\n    private final String valueExpected;\n\n    public WaitForDataValue(final int repeatEach, final int timeout, final long processInstanceId,\n            final String dataName,\n            final String valueExpected, final ProcessAPI processAPI) {\n        super(repeatEach, timeout, false);\n        this.processAPI = processAPI;\n        this.processInstanceId = processInstanceId;\n        this.dataName = dataName;\n        this.valueExpected = valueExpected;\n    }\n\n    @Override\n    protected boolean check() throws Exception {\n        final String value = (String) processAPI.getProcessDataInstance(dataName, processInstanceId).getValue();\n        return value.equals(valueExpected);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/wait/WaitForEvent.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.wait;\n\nimport java.util.Iterator;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.flownode.EventCriterion;\nimport org.bonitasoft.engine.bpm.flownode.EventInstance;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.bonitasoft.engine.test.WaitUntil;\n\n@Deprecated\npublic class WaitForEvent extends WaitUntil {\n\n    private final String eventName;\n\n    private final long processInstanceId;\n\n    private String state = null;\n\n    private EventInstance result;\n\n    private final ProcessAPI processAPI;\n\n    @Deprecated\n    public WaitForEvent(final int repeatEach, final int timeout, final String eventName, final long processInstanceId,\n            final ProcessAPI processAPI) {\n        super(repeatEach, timeout);\n        this.eventName = eventName;\n        this.processInstanceId = processInstanceId;\n        this.processAPI = processAPI;\n    }\n\n    @Deprecated\n    public WaitForEvent(final int repeatEach, final int timeout, final String eventName, final long processInstanceId,\n            final TestStates state,\n            final ProcessAPI processAPI) {\n        this(repeatEach, timeout, eventName, processInstanceId, processAPI);\n        this.state = state.getStateName();\n    }\n\n    @Override\n    protected boolean check() {\n        final List<EventInstance> eventInstances = processAPI.getEventInstances(processInstanceId, 0, 10,\n                EventCriterion.NAME_ASC);\n        boolean found = false;\n        final Iterator<EventInstance> iterator = eventInstances.iterator();\n        while (iterator.hasNext() && !found) {\n            final EventInstance eventInstance = iterator.next();\n            if (eventInstance.getName().equals(eventName)) {\n                if (state == null) {\n                    found = true;\n                    result = eventInstance;\n                } else {\n                    found = state.equals(eventInstance.getState());\n                    result = eventInstance;\n                }\n            }\n        }\n        return found;\n    }\n\n    public EventInstance getResult() {\n        return result;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/wait/WaitForFinalArchivedActivity.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.wait;\n\nimport java.util.Iterator;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.bonitasoft.engine.test.WaitUntil;\n\n@Deprecated\npublic class WaitForFinalArchivedActivity extends WaitUntil {\n\n    private final String activityName;\n\n    private final long processInstanceId;\n\n    private ArchivedActivityInstance result;\n\n    private final ProcessAPI processAPI;\n\n    @Deprecated\n    public WaitForFinalArchivedActivity(final int repeatEach, final int timeout, final String activityName,\n            final long processInstanceId,\n            final ProcessAPI processAPI) {\n        super(repeatEach, timeout);\n        this.activityName = activityName;\n        this.processInstanceId = processInstanceId;\n        this.processAPI = processAPI;\n    }\n\n    @Override\n    protected boolean check() {\n        final List<ArchivedActivityInstance> activityInstances = processAPI.getArchivedActivityInstances(\n                processInstanceId, 0, 100,\n                ActivityInstanceCriterion.NAME_ASC);\n        final Iterator<ArchivedActivityInstance> iterator = activityInstances.iterator();\n        boolean found = false;\n        while (iterator.hasNext() && !found) {\n            final ArchivedActivityInstance activityInstance = iterator.next();\n            if (activityInstance.getName().equals(activityName)\n                    && activityInstance.getState().equals(TestStates.NORMAL_FINAL.getStateName())) {\n                result = activityInstance;\n                found = true;\n            }\n        }\n        return found;\n    }\n\n    public ArchivedActivityInstance getResult() {\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/wait/WaitForFlowNode.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.wait;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.StateCategory;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.test.WaitUntil;\n\n@Deprecated\npublic class WaitForFlowNode extends WaitUntil {\n\n    private final String name;\n\n    private final long processInstanceId;\n\n    private String state = null;\n\n    private StateCategory stateCategory = null;\n\n    private FlowNodeInstance result;\n\n    private final boolean useRootProcessInstance;\n\n    private final ProcessAPI processAPI;\n\n    @Deprecated\n    public WaitForFlowNode(final int repeatEach, final int timeout, final String name, final long processInstanceId,\n            final boolean useRootProcessInstance,\n            final ProcessAPI processAPI) {\n        super(repeatEach, timeout);\n        this.name = name;\n        this.processInstanceId = processInstanceId;\n        this.useRootProcessInstance = useRootProcessInstance;\n        this.processAPI = processAPI;\n    }\n\n    @Deprecated\n    public WaitForFlowNode(final int repeatEach, final int timeout, final String name, final long processInstanceId,\n            final String state,\n            final boolean rootProcessInstance, final ProcessAPI processAPI) {\n        this(repeatEach, timeout, name, processInstanceId, rootProcessInstance, processAPI);\n        this.state = state;\n    }\n\n    @Deprecated\n    public WaitForFlowNode(final int repeatEach, final int timeout, final String name, final long processInstanceId,\n            final StateCategory stateCategory,\n            final boolean rootProcessInstance, final ProcessAPI processAPI) {\n        this(repeatEach, timeout, name, processInstanceId, rootProcessInstance, processAPI);\n        this.stateCategory = stateCategory;\n    }\n\n    @Override\n    protected boolean check() throws Exception {\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        if (useRootProcessInstance) {\n            searchOptionsBuilder.filter(FlowNodeInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID, processInstanceId);\n        } else {\n            searchOptionsBuilder.filter(FlowNodeInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID, processInstanceId);\n        }\n        searchOptionsBuilder.filter(FlowNodeInstanceSearchDescriptor.NAME, name);\n        final SearchResult<FlowNodeInstance> searchResult = processAPI\n                .searchFlowNodeInstances(searchOptionsBuilder.done());\n        final boolean found = searchResult.getCount() > 0;\n        boolean check = found;\n        if (found) {\n            result = searchResult.getResult().get(0);\n            if (state != null) {\n                check = state.equals(result.getState());\n            }\n            if (stateCategory != null) {\n                check = stateCategory.equals(result.getStateCategory());\n            }\n        }\n        return check;\n    }\n\n    public FlowNodeInstance getResult() {\n        return result;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/wait/WaitForPendingTasks.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.wait;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.test.WaitUntil;\n\n/**\n * @author Baptiste Mesta\n */\n@Deprecated\npublic final class WaitForPendingTasks extends WaitUntil {\n\n    private final int nbPendingTasks;\n\n    private final long userId;\n\n    private List<HumanTaskInstance> results;\n\n    private final ProcessAPI processAPI;\n\n    public WaitForPendingTasks(final int repeatEach, final int timeout, final int nbPendingTasks, final long userId,\n            final ProcessAPI processAPI) {\n        super(repeatEach, timeout);\n        this.nbPendingTasks = nbPendingTasks;\n        this.userId = userId;\n        this.processAPI = processAPI;\n    }\n\n    @Override\n    protected boolean check() {\n        results = processAPI.getPendingHumanTaskInstances(userId, 0, nbPendingTasks, null);\n        return results.size() == nbPendingTasks;\n    }\n\n    public List<HumanTaskInstance> getResults() {\n        return results;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/wait/WaitForStep.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.wait;\n\nimport java.util.Iterator;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.bonitasoft.engine.test.WaitUntil;\n\n@Deprecated\npublic class WaitForStep extends WaitUntil {\n\n    private final String stepName;\n\n    private final long processInstanceId;\n\n    private long activityInstanceId;\n\n    private ActivityInstance result;\n\n    private String state = null;\n\n    private final ProcessAPI processAPI;\n\n    @Deprecated\n    public WaitForStep(final int repeatEach, final int timeout, final String stepName, final long processInstanceId,\n            final ProcessAPI processAPI) {\n        super(repeatEach, timeout);\n        this.stepName = stepName;\n        this.processInstanceId = processInstanceId;\n        this.processAPI = processAPI;\n    }\n\n    @Deprecated\n    public WaitForStep(final int repeatEach, final int timeout, final String stepName, final long processInstanceId,\n            final TestStates state,\n            final ProcessAPI processAPI) {\n        super(repeatEach, timeout);\n        this.stepName = stepName;\n        this.processInstanceId = processInstanceId;\n        this.state = state.getStateName();\n        this.processAPI = processAPI;\n    }\n\n    @Override\n    protected boolean check() {\n        final List<ActivityInstance> openedActivityInstances = processAPI.getOpenActivityInstances(processInstanceId, 0,\n                10,\n                ActivityInstanceCriterion.DEFAULT);\n        final Iterator<ActivityInstance> iterator = openedActivityInstances.iterator();\n        boolean found = false;\n        while (iterator.hasNext() && !found) {\n            final ActivityInstance activityInstance = iterator.next();\n            if (activityInstance.getName().equals(stepName)) {\n                if (state == null || state.equals(activityInstance.getState())) {\n                    activityInstanceId = activityInstance.getId();\n                    result = activityInstance;\n                    found = true;\n                }\n            }\n        }\n        return found;\n    }\n\n    public long getStepId() {\n        return activityInstanceId;\n    }\n\n    public ActivityInstance getResult() {\n        return result;\n    }\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/wait/WaitProcessToFinishAndBeArchived.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.wait;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.test.APITestUtil;\nimport org.bonitasoft.engine.test.TestStates;\nimport org.bonitasoft.engine.test.WaitUntil;\n\n@Deprecated\npublic final class WaitProcessToFinishAndBeArchived extends WaitUntil {\n\n    private final ProcessInstance processInstance;\n\n    private final ProcessAPI processAPI;\n\n    private final TestStates state;\n\n    @Deprecated\n    public WaitProcessToFinishAndBeArchived(final int repeatEach, final int timeout, final boolean throwExceptions,\n            final ProcessInstance processInstance,\n            final ProcessAPI processAPI, final TestStates state) {\n        super(repeatEach, timeout, throwExceptions);\n        this.processInstance = processInstance;\n        this.processAPI = processAPI;\n        this.state = state;\n    }\n\n    @Deprecated\n    public WaitProcessToFinishAndBeArchived(final int repeatEach, final int timeout, final boolean throwExceptions,\n            final ProcessInstance processInstance,\n            final ProcessAPI processAPI) {\n        this(repeatEach, timeout, throwExceptions, processInstance, processAPI, TestStates.NORMAL_FINAL);\n    }\n\n    @Deprecated\n    public WaitProcessToFinishAndBeArchived(final int repeatEach, final int timeout,\n            final ProcessInstance processInstance, final ProcessAPI processAPI) {\n        this(repeatEach, timeout, false, processInstance, processAPI);\n    }\n\n    @Override\n    protected boolean check() {\n        final List<ArchivedProcessInstance> archivedProcessInstances = processAPI\n                .getArchivedProcessInstances(processInstance.getId(), 0, 200);\n        return APITestUtil.containsState(archivedProcessInstances, state);\n    }\n\n}\n"
  },
  {
    "path": "bonita-integration-tests/bonita-test-utils/src/main/resources/org/bonitasoft/engine/connectors/TestConnectorEngineExecutionContext.def",
    "content": "<connectorDefinition>\n\t<version>1.0</version>\n\t<connectorId>org.bonitasoft.connector.testConnectorEngineExecutionContext</connectorId>\n\t<icon>xxx.png</icon>\n\t<categories>\n\t\t<category>\n\t\t\t<name>other</name>\n\t\t\t<icon>org/bonitasoft/engine/connectors/TestConnectorEngineExecutionContext.png</icon>\n\t\t</category>\n\t</categories>\n\t<inputs>\n\t</inputs>\n\t<outputs>\n\t\t<output type=\"long\">taskAssigneeId</output>\n\t</outputs>\n</connectorDefinition>"
  },
  {
    "path": "bonita-integration-tests/bonita-test-utils/src/main/resources/org/bonitasoft/engine/connectors/TestConnectorEngineExecutionContext.impl",
    "content": "<connectorImplementation>\n\n\t<definitionId>org.bonitasoft.connector.testConnectorEngineExecutionContext</definitionId>\n\t<definitionVersion>1.0</definitionVersion>\n\t<implementationClassname>org.bonitasoft.engine.connectors.TestConnectorEngineExecutionContext</implementationClassname>\n\t<implementationId>org.bonitasoft.connector.testConnectorEngineExecutionContext</implementationId>\n\t<implementationVersion>1.0</implementationVersion>\n\n\t<jarDependencies>\n\t\t<jarDependency>TestConnectorEngineExecutionContext.jar</jarDependency>\n\t</jarDependencies>\n</connectorImplementation>\n"
  },
  {
    "path": "bonita-test-api/build.gradle",
    "content": "dependencies {\n    api libs.commonsIO\n    api project(':bonita-engine-standalone')\n    api libs.junit4\n    api(project(':platform:platform-resources'))\n\n    implementation libs.springTest\n\n    // for http tests:\n    compileOnly(libs.jettyServer)\n    compileOnly(libs.jettyServlet)\n    compileOnly(project(':bpm:bonita-api:bonita-server-api-http'))\n\n    annotationProcessor libs.lombok\n    compileOnly libs.lombok\n\n    testImplementation project(':bpm:bonita-server')\n    testImplementation libs.mockitoCore\n    testImplementation libs.assertj\n}\n\npublishing {\n    publications {\n        mavenJava(MavenPublication) { from project.components.java }\n    }\n}\n"
  },
  {
    "path": "bonita-test-api/src/main/java/org/bonitasoft/engine/test/ClientEventUtil.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test;\n\nimport java.io.Serializable;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.TimeoutException;\n\nimport org.bonitasoft.engine.api.CommandAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.command.CommandDescriptor;\nimport org.bonitasoft.engine.command.CommandExecutionException;\nimport org.bonitasoft.engine.command.CommandNotFoundException;\nimport org.bonitasoft.engine.command.CommandParameterizationException;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Utility methods to get maps that match process as wanted\n *\n * @author Baptiste Mesta\n */\npublic class ClientEventUtil {\n\n    public static final String ADD_HANDLER_COMMAND = \"addHandlerCommand\";\n\n    public static final String WAIT_SERVER_COMMAND = \"waitServerCommand\";\n\n    private static final String FLOW_NODE = \"flowNode\";\n\n    private static final String PROCESS = \"process\";\n\n    private static final String TYPE = \"type\";\n\n    private static final String NAME = \"name\";\n\n    private static final String ROOT_CONTAINER_ID = \"rootContainerId\";\n\n    private static final String PARENT_CONTAINER_ID = \"parentContainerId\";\n\n    private static final String STATE_ID = \"stateId\";\n\n    private static final String ID = \"id\";\n\n    private static final String STATE = \"state\";\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(ClientEventUtil.class.getName());\n\n    public static void undeployCommand(final APISession apiSession) throws BonitaException {\n        final CommandAPI commandAPI = TenantAPIAccessor.getCommandAPI(apiSession);\n        try {\n            commandAPI.unregister(WAIT_SERVER_COMMAND);\n            commandAPI.unregister(ADD_HANDLER_COMMAND);\n            LOGGER.debug(\"commands undeployed\");\n        } catch (final BonitaException e) {\n            // ok\n            LOGGER.debug(\"error undeploying command (maybe already undeployed?) \" + e.getMessage());\n        }\n    }\n\n    public static void deployCommand(final APISession apiSession) throws BonitaException {\n        final CommandAPI commandAPI = TenantAPIAccessor.getCommandAPI(apiSession);\n\n        final Map<String, Serializable> parameters = new HashMap<>(1);\n\n        final CommandDescriptor waitServerCommand;\n        try {\n            waitServerCommand = commandAPI.register(WAIT_SERVER_COMMAND, WAIT_SERVER_COMMAND,\n                    \"org.bonitasoft.engine.synchro.WaitServerCommand\");\n        } catch (AlreadyExistsException e) {\n            return;\n        }\n        final CommandDescriptor addHandlerCommand = commandAPI.register(ADD_HANDLER_COMMAND, ADD_HANDLER_COMMAND,\n                \"org.bonitasoft.engine.synchro.AddHandlerCommand\");\n\n        parameters.put(\"commands\", (Serializable) Arrays.asList(waitServerCommand.getId(), addHandlerCommand.getId()));\n        commandAPI.execute(ADD_HANDLER_COMMAND, parameters);\n        LOGGER.debug(\"commands deployed\");\n    }\n\n    public static Long executeWaitServerCommand(final CommandAPI commandAPI, final Map<String, Serializable> event,\n            final int defaultTimeout)\n            throws CommandNotFoundException, CommandParameterizationException, CommandExecutionException,\n            TimeoutException {\n        final Map<String, Serializable> parameters = new HashMap<>(2);\n        parameters.put(\"event\", (Serializable) event);\n        parameters.put(\"timeout\", defaultTimeout);\n        try {\n            return (Long) commandAPI.execute(WAIT_SERVER_COMMAND, parameters);\n        } catch (final CommandExecutionException e) {\n            LOGGER.error(\"error while executing wait server command for element:\" + event, e);\n            if (e.getMessage().toLowerCase().contains(\"timeout\")) {\n                throw new TimeoutException(\"Timeout (\" + defaultTimeout + \" ms) when looking for element: \" + event);\n            }\n            throw e;\n        }\n    }\n\n    public static void clearRepo(final CommandAPI commandAPI) {\n        final Map<String, Serializable> parameters = new HashMap<>(1);\n        parameters.put(\"clear\", true);\n        try {\n            commandAPI.execute(WAIT_SERVER_COMMAND, parameters);\n        } catch (final BonitaException e) {\n            // ok\n            LOGGER.debug(\"error undeploying command (maybe already undeployed?) \" + e.getMessage());\n        }\n    }\n\n    public static Map<String, Serializable> getReadyFlowNodeEvent(final long processInstanceId, final String taskName) {\n        final Map<String, Serializable> map = new HashMap<>(5);\n        map.put(TYPE, FLOW_NODE);\n        map.put(ROOT_CONTAINER_ID, processInstanceId);\n        map.put(NAME, taskName);\n        map.put(STATE_ID, 4);\n        return map;\n    }\n\n    public static Map<String, Serializable> getReadyFlowNodeEvent(final String taskName) {\n        final Map<String, Serializable> map = new HashMap<>(5);\n        map.put(TYPE, FLOW_NODE);\n        map.put(NAME, taskName);\n        map.put(STATE_ID, 4);\n        return map;\n    }\n\n    public static Map<String, Serializable> getFlowNodeInState(final String state, final String taskName) {\n        final Map<String, Serializable> map = new HashMap<>(5);\n        map.put(TYPE, FLOW_NODE);\n        map.put(NAME, taskName);\n        map.put(STATE, state);\n        return map;\n    }\n\n    public static Map<String, Serializable> getFlowNodeInState(final long rootContainerId, final String state,\n            final String flowNodeName) {\n        final Map<String, Serializable> map = new HashMap<>(4);\n        map.put(TYPE, FLOW_NODE);\n        map.put(ROOT_CONTAINER_ID, rootContainerId);\n        map.put(STATE, state);\n        map.put(NAME, flowNodeName);\n        return map;\n    }\n\n    public static Map<String, Serializable> getFlowNode(final long rootContainerId, final String flowNodeName) {\n        final Map<String, Serializable> map = new HashMap<>(4);\n        map.put(TYPE, FLOW_NODE);\n        map.put(ROOT_CONTAINER_ID, rootContainerId);\n        map.put(NAME, flowNodeName);\n        return map;\n    }\n\n    public static Map<String, Serializable> getFlowNodeInState(final long rootContainerId, final String stateName) {\n        final Map<String, Serializable> map = new HashMap<>(3);\n        map.put(TYPE, FLOW_NODE);\n        map.put(ROOT_CONTAINER_ID, rootContainerId);\n        map.put(STATE, stateName);\n        return map;\n    }\n\n    public static Map<String, Serializable> getFlowNodeInStateWithParentId(final long processInstanceId,\n            final String state, final String flowNodeName) {\n        final Map<String, Serializable> map = new HashMap<>(4);\n        map.put(TYPE, FLOW_NODE);\n        map.put(PARENT_CONTAINER_ID, processInstanceId);\n        map.put(STATE, state);\n        map.put(NAME, flowNodeName);\n        return map;\n    }\n\n    public static Map<String, Serializable> getProcessInstanceInState(final long processInstanceId, final int stateId) {\n        final HashMap<String, Serializable> map = new HashMap<>(3);\n        map.put(TYPE, PROCESS);\n        map.put(ID, processInstanceId);\n        map.put(STATE_ID, stateId);\n        return map;\n    }\n\n    public static Map<String, Serializable> getProcessInstanceInState(final int stateId) {\n        final HashMap<String, Serializable> map = new HashMap<>(3);\n        map.put(TYPE, PROCESS);\n        map.put(STATE_ID, stateId);\n        return map;\n    }\n\n    public static Map<String, Serializable> getProcessInstanceFinishedEvent(final long processInstanceId) {\n        final Map<String, Serializable> map = new HashMap<>(4);\n        map.put(TYPE, PROCESS);\n        map.put(ID, processInstanceId);\n        map.put(STATE_ID, 6);\n        return map;\n    }\n\n}\n"
  },
  {
    "path": "bonita-test-api/src/main/java/org/bonitasoft/engine/test/TestDatabaseConfigurator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test;\n\nimport static org.bonitasoft.engine.BonitaEngine.BONITA_BDM_DB_VENDOR;\nimport static org.bonitasoft.engine.BonitaEngine.BONITA_DB_VENDOR;\nimport static org.bonitasoft.engine.DefaultBonitaDatabaseConfigurations.defaultConfiguration;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.BonitaDatabaseConfiguration;\n\n@Slf4j\nclass TestDatabaseConfigurator {\n\n    /**\n     * construct a database configuration bases on system properties\n     * sysprop.bonita.db.vendor\n     * db.vendor\n     * db.url\n     * db.driverClass\n     * db.user\n     * db.password\n     * If some properties are not set use the default one from the db vendor\n     */\n    BonitaDatabaseConfiguration getDatabaseConfiguration() {\n        String dbVendor = System.getProperty(BONITA_DB_VENDOR, System.getProperty(\"db.vendor\"));\n        if (nullOrEmpty(dbVendor)) {\n            log.info(\"System property '{}' or 'db.vendor' is not set, will use h2\", BONITA_DB_VENDOR);\n            dbVendor = \"h2\";\n        }\n        String url = System.getProperty(\"db.url\");\n        String driverClass = System.getProperty(\"db.driverClass\");\n        String user = System.getProperty(\"db.user\");\n        String password = System.getProperty(\"db.password\");\n\n        BonitaDatabaseConfiguration.BonitaDatabaseConfigurationBuilder builder = BonitaDatabaseConfiguration.builder();\n        builder.dbVendor(dbVendor);\n        BonitaDatabaseConfiguration defaultConfig = defaultConfiguration(dbVendor, \"bonita\");\n        builder.url(nullOrEmpty(url) ? defaultConfig.getUrl() : url);\n        if (!nullOrEmpty(driverClass)) {\n            builder.driverClassName(driverClass);\n        }\n        builder.user(nullOrEmpty(user) ? defaultConfig.getUser() : user);\n        builder.password(password == null ? defaultConfig.getPassword() : password);\n        return builder.build();\n    }\n\n    BonitaDatabaseConfiguration getBDMDatabaseConfiguration() {\n        String dbVendor = System.getProperty(BONITA_BDM_DB_VENDOR, System.getProperty(\"bdm.db.vendor\",\n                System.getProperty(BONITA_DB_VENDOR, System.getProperty(\"db.vendor\"))));\n        if (nullOrEmpty(dbVendor)) {\n            log.info(\"System property '{}' or 'db.vendor' is not set, will use h2\", BONITA_DB_VENDOR);\n            dbVendor = \"h2\";\n        }\n        String url = System.getProperty(\"bdm.db.url\", System.getProperty(\"db.url\"));\n        String driverClass = System.getProperty(\"bdm.db.driverClass\", System.getProperty(\"db.driverClass\"));\n        String user = System.getProperty(\"bdm.db.user\", System.getProperty(\"db.user\"));\n        String password = System.getProperty(\"bdm.db.password\", System.getProperty(\"db.password\"));\n\n        BonitaDatabaseConfiguration.BonitaDatabaseConfigurationBuilder builder = BonitaDatabaseConfiguration.builder();\n        builder.dbVendor(dbVendor);\n        BonitaDatabaseConfiguration defaultConfig = defaultConfiguration(dbVendor, \"business_data\");\n        builder.url(nullOrEmpty(url) ? defaultConfig.getUrl() : url);\n        if (!nullOrEmpty(driverClass)) {\n            builder.driverClassName(driverClass);\n        }\n        builder.user(nullOrEmpty(user) ? defaultConfig.getUser() : user);\n        builder.password(password == null ? defaultConfig.getPassword() : password);\n        return builder.build();\n    }\n\n    private static boolean nullOrEmpty(String s) {\n        return s == null || s.isEmpty();\n    }\n\n}\n"
  },
  {
    "path": "bonita-test-api/src/main/java/org/bonitasoft/engine/test/TestEngine.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test;\n\nimport org.bonitasoft.engine.BonitaDatabaseConfiguration;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface TestEngine {\n\n    String TECHNICAL_USER_NAME = \"install\";\n    String TECHNICAL_USER_PASSWORD = \"install\";\n\n    boolean start() throws Exception;\n\n    void stop() throws Exception;\n\n    void clearData() throws Exception;\n\n    void setDropOnStart(boolean dropOnStart);\n\n    void setBonitaDatabaseProperties(BonitaDatabaseConfiguration database);\n\n    void setBusinessDataDatabaseProperties(BonitaDatabaseConfiguration database);\n}\n"
  },
  {
    "path": "bonita-test-api/src/main/java/org/bonitasoft/engine/test/TestEngineImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test;\n\nimport lombok.Getter;\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.BonitaDatabaseConfiguration;\nimport org.bonitasoft.engine.execution.ProcessStarterVerifier;\nimport org.bonitasoft.engine.test.http.BonitaHttpServer;\nimport org.bonitasoft.engine.test.internal.EngineCommander;\nimport org.bonitasoft.engine.test.internal.EngineStarter;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Profile;\nimport org.springframework.test.context.ContextConfiguration;\n\n/**\n * @author Baptiste Mesta\n */\n@Getter\n@Slf4j\n@ContextConfiguration(classes = TestEngineImpl.TestConfiguration.class)\npublic class TestEngineImpl implements TestEngine {\n\n    private static TestEngineImpl INSTANCE = createTestEngine();\n\n    private BonitaDatabaseConfiguration bonitaDatabaseConfiguration;\n\n    private BonitaDatabaseConfiguration businessDataDatabaseConfiguration;\n\n    private final TestDatabaseConfigurator testDatabaseConfigurator = new TestDatabaseConfigurator();\n    BonitaHttpServer httpServer = new BonitaHttpServer();\n\n    private static TestEngineImpl createTestEngine() {\n        return new TestEngineImpl(EngineStarter.create(), new EngineCommander());\n    }\n\n    private final EngineStarter engineStarter;\n    private final EngineCommander engineCommander;\n    private boolean started = false;\n\n    public static TestEngine getInstance() {\n        return INSTANCE;\n    }\n\n    protected TestEngineImpl(EngineStarter engineStarter, EngineCommander engineCommander) {\n        this.engineStarter = engineStarter;\n        this.engineCommander = engineCommander;\n    }\n\n    protected static void replaceInstance(TestEngineImpl newTestEngine) {\n        if (INSTANCE.started) {\n            throw new IllegalStateException(\"trying to replace an already started instance\");\n        }\n        INSTANCE = newTestEngine;\n    }\n\n    /**\n     * start the engine and return if it was effectively started\n     *\n     * @throws Exception if Engine cannot be started\n     */\n    @Override\n    public boolean start() throws Exception {\n        initializeDatabaseConfigurations();\n        if (!started) {\n            doStart();\n            started = true;\n            // Starting HTTP server must be done AFTER engine start, as the API Type will be overridden to HTTP\n            // (See EngineStarter logic):\n            httpServer.startIfNeeded();\n            return true;\n        }\n        return false;\n    }\n\n    private void initializeDatabaseConfigurations() {\n        if (bonitaDatabaseConfiguration == null || businessDataDatabaseConfiguration == null) {\n            if (bonitaDatabaseConfiguration == null) {\n                bonitaDatabaseConfiguration = testDatabaseConfigurator.getDatabaseConfiguration();\n                engineStarter.setBonitaDatabaseConfiguration(bonitaDatabaseConfiguration);\n            }\n            if (businessDataDatabaseConfiguration == null) {\n                businessDataDatabaseConfiguration = testDatabaseConfigurator.getBDMDatabaseConfiguration();\n                engineStarter.setBusinessDataDatabaseConfiguration(businessDataDatabaseConfiguration);\n            }\n        }\n    }\n\n    protected synchronized void doStart() throws Exception {\n        engineStarter.start();\n    }\n\n    @Override\n    public void stop() throws Exception {\n        if (started) {\n            doStop();\n            started = false;\n            httpServer.stopIfNeeded();\n        }\n    }\n\n    private void doStop() throws Exception {\n        engineStarter.stop();\n    }\n\n    @Override\n    public void clearData() throws Exception {\n        engineCommander.clearData();\n    }\n\n    @Override\n    public void setDropOnStart(boolean dropOnStart) {\n        engineStarter.setDropOnStart(dropOnStart);\n    }\n\n    @Override\n    public void setBonitaDatabaseProperties(BonitaDatabaseConfiguration configuration) {\n        bonitaDatabaseConfiguration = configuration;\n    }\n\n    @Override\n    public void setBusinessDataDatabaseProperties(BonitaDatabaseConfiguration database) {\n        this.businessDataDatabaseConfiguration = database;\n    }\n\n    /**\n     * Configuration class used to override bean definitions for test purposes.\n     */\n    @Configuration\n    @Profile(\"!update-tool\") // to allow to remove this default bypass in Update Tool tests (and so test it in the real condition)\n    static class TestConfiguration {\n\n        @Bean\n        ProcessStarterVerifier processStarterVerifierImpl() {\n            return processInstance -> {\n                // Override this bean to disable the process starter verifier\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "bonita-test-api/src/main/java/org/bonitasoft/engine/test/http/BonitaHttpServer.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.http;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.api.ApiAccessType;\nimport org.bonitasoft.engine.util.APITypeManager;\nimport org.eclipse.jetty.server.ServerConnector;\n\n/**\n * Handles a jetty HTTP server and registers Bonita APIs in HTTP mode,\n * if tests are requested to be configured that way.\n * To request that, start Engine with system property '-Dorg.bonitasoft.engine.access.mode=http'.\n *\n * @author Emmanuel Duchastenier\n */\n@Slf4j\npublic class BonitaHttpServer {\n\n    private JettyServer jettyServer;\n\n    public void startIfNeeded() throws Exception {\n        if (\"http\".equals(System.getProperty(\"org.bonitasoft.engine.access.mode\", null))) {\n            log.info(\"Starting Http Server for Bonita Engine...\");\n            jettyServer = new JettyServer();\n            jettyServer.start();\n\n            setClientApiToHTTP(((ServerConnector) jettyServer.getServer().getConnectors()[0]).getLocalPort());\n        }\n    }\n\n    public static void setClientApiToHTTP(int localPort) {\n        Map<String, String> params = new HashMap<>();\n        params.put(\"server.url\", \"http://localhost:\" + localPort);\n        params.put(\"application.name\", \"bonita\");\n        APITypeManager.setAPITypeAndParams(ApiAccessType.HTTP, params);\n    }\n\n    public void stopIfNeeded() throws Exception {\n        if (jettyServer != null) {\n            jettyServer.stop();\n        }\n    }\n\n}\n"
  },
  {
    "path": "bonita-test-api/src/main/java/org/bonitasoft/engine/test/http/JettyServer.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.http;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.api.internal.servlet.HttpAPIServlet;\nimport org.eclipse.jetty.server.Server;\nimport org.eclipse.jetty.servlet.ServletHandler;\n\n/**\n * Handles a jetty HTTP server and registers Bonita APIs in HTTP mode,\n * if tests are requested to be configured that way.\n * To request that, start Engine with system property '-Dorg.bonitasoft.engine.access.mode=http'.\n *\n * @author Emmanuel Duchastenier\n */\n@Slf4j\npublic class JettyServer {\n\n    private Server server;\n\n    public void start() throws Exception {\n        server = startJettyServer();\n    }\n\n    public Server startJettyServer() throws Exception {\n        // Running our HttpAPIServlet in a Jetty server:\n        Server jettyServer = new Server(0);// 0=random port\n        ServletHandler handler = new ServletHandler();\n        jettyServer.setHandler(handler);\n        handler.addServletWithMapping(HttpAPIServlet.class, \"/bonita/serverAPI/*\");\n        jettyServer.start();\n        return jettyServer;\n    }\n\n    public void stop() throws Exception {\n        if (server != null) {\n            server.stop();\n            server.join();\n        }\n    }\n\n    Server getServer() {\n        return server;\n    }\n}\n"
  },
  {
    "path": "bonita-test-api/src/main/java/org/bonitasoft/engine/test/internal/EngineCommander.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.internal;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.ApplicationAPI;\nimport org.bonitasoft.engine.api.BusinessDataAPI;\nimport org.bonitasoft.engine.api.CommandAPI;\nimport org.bonitasoft.engine.api.IdentityAPI;\nimport org.bonitasoft.engine.api.LoginAPI;\nimport org.bonitasoft.engine.api.PageAPI;\nimport org.bonitasoft.engine.api.PermissionAPI;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.ProfileAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.api.TenantAdministrationAPI;\nimport org.bonitasoft.engine.bpm.category.Category;\nimport org.bonitasoft.engine.bpm.category.CategoryCriterion;\nimport org.bonitasoft.engine.bpm.comment.ArchivedComment;\nimport org.bonitasoft.engine.bpm.comment.Comment;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstance;\nimport org.bonitasoft.engine.bpm.flownode.WaitingEvent;\nimport org.bonitasoft.engine.bpm.process.ActivationState;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceCriterion;\nimport org.bonitasoft.engine.bpm.supervisor.ProcessSupervisor;\nimport org.bonitasoft.engine.bpm.supervisor.ProcessSupervisorSearchDescriptor;\nimport org.bonitasoft.engine.command.CommandDescriptor;\nimport org.bonitasoft.engine.command.CommandNotFoundException;\nimport org.bonitasoft.engine.command.CommandSearchDescriptor;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.identity.Group;\nimport org.bonitasoft.engine.identity.GroupCriterion;\nimport org.bonitasoft.engine.identity.Role;\nimport org.bonitasoft.engine.identity.RoleCriterion;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.identity.UserCriterion;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.test.ClientEventUtil;\nimport org.bonitasoft.engine.test.TestEngineImpl;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Baptiste Mesta\n */\npublic class EngineCommander {\n\n    private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(EngineCommander.class.getName());\n    private APISession session;\n\n    private ProcessAPI processAPI;\n\n    private IdentityAPI identityAPI;\n\n    private CommandAPI commandAPI;\n\n    private ProfileAPI profileAPI;\n\n    private PermissionAPI permissionAPI;\n\n    private PageAPI pageAPI;\n\n    private ApplicationAPI applicationAPI;\n\n    private TenantAdministrationAPI tenantManagementCommunityAPI;\n\n    private BusinessDataAPI businessDataAPI;\n\n    public APISession getSession() {\n        return session;\n    }\n\n    public void loginOnDefaultTenantWithDefaultTechnicalUser() throws BonitaException {\n        final LoginAPI loginAPI = getLoginAPI();\n        setSession(loginAPI.login(TestEngineImpl.TECHNICAL_USER_NAME, TestEngineImpl.TECHNICAL_USER_NAME));\n        setAPIs();\n    }\n\n    protected void setAPIs() throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        setIdentityAPI(TenantAPIAccessor.getIdentityAPI(getSession()));\n        setProcessAPI(TenantAPIAccessor.getProcessAPI(getSession()));\n        setCommandAPI(TenantAPIAccessor.getCommandAPI(getSession()));\n        setProfileAPI(TenantAPIAccessor.getProfileAPI(getSession()));\n        setPermissionAPI(TenantAPIAccessor.getPermissionAPI(getSession()));\n        setPageAPI(TenantAPIAccessor.getCustomPageAPI(getSession()));\n        setApplicationAPI(TenantAPIAccessor.getLivingApplicationAPI(getSession()));\n        setTenantManagementCommunityAPI(TenantAPIAccessor.getTenantAdministrationAPI(getSession()));\n        setBusinessDataAPI(TenantAPIAccessor.getBusinessDataAPI(getSession()));\n    }\n\n    public void setBusinessDataAPI(final BusinessDataAPI businessDataAPI) {\n        this.businessDataAPI = businessDataAPI;\n    }\n\n    public void logoutOnTenant() throws BonitaException {\n        final LoginAPI loginAPI = getLoginAPI();\n        loginAPI.logout(session);\n        setSession(null);\n        setIdentityAPI(null);\n        setProcessAPI(null);\n        setCommandAPI(null);\n        setProfileAPI(null);\n        setPermissionAPI(null);\n        setApplicationAPI(null);\n        setTenantManagementCommunityAPI(null);\n        setPageAPI(null);\n        setBusinessDataAPI(null);\n    }\n\n    public LoginAPI getLoginAPI() throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return TenantAPIAccessor.getLoginAPI();\n    }\n\n    public BusinessDataAPI getBusinessDataAPI() {\n        return businessDataAPI;\n    }\n\n    public ProcessAPI getProcessAPI() {\n        return processAPI;\n    }\n\n    public void setProcessAPI(final ProcessAPI processAPI) {\n        this.processAPI = processAPI;\n    }\n\n    public IdentityAPI getIdentityAPI() {\n        return identityAPI;\n    }\n\n    public void setIdentityAPI(final IdentityAPI identityAPI) {\n        this.identityAPI = identityAPI;\n    }\n\n    public CommandAPI getCommandAPI() {\n        return commandAPI;\n    }\n\n    public void setCommandAPI(final CommandAPI commandAPI) {\n        this.commandAPI = commandAPI;\n    }\n\n    public ProfileAPI getProfileAPI() {\n        return profileAPI;\n    }\n\n    public void setProfileAPI(final ProfileAPI profileAPI) {\n        this.profileAPI = profileAPI;\n    }\n\n    public PermissionAPI getPermissionAPI() {\n        return permissionAPI;\n    }\n\n    public void setPermissionAPI(final PermissionAPI permissionAPI) {\n        this.permissionAPI = permissionAPI;\n    }\n\n    public void setSession(final APISession session) {\n        this.session = session;\n    }\n\n    protected void setPageAPI(final PageAPI pageAPI) {\n        this.pageAPI = pageAPI;\n    }\n\n    public PageAPI getPageAPI() {\n        return pageAPI;\n    }\n\n    public ApplicationAPI getApplicationAPI() {\n        return applicationAPI;\n    }\n\n    public void setApplicationAPI(final ApplicationAPI applicationAPI) {\n        this.applicationAPI = applicationAPI;\n    }\n\n    public TenantAdministrationAPI getTenantAdministrationAPI() {\n        return tenantManagementCommunityAPI;\n    }\n\n    public void setTenantManagementCommunityAPI(final TenantAdministrationAPI tenantManagementCommunityAPI) {\n        this.tenantManagementCommunityAPI = tenantManagementCommunityAPI;\n    }\n\n    public void clearData() throws Exception {\n        loginOnDefaultTenantWithDefaultTechnicalUser();\n        final List<String> messages = new ArrayList<>();\n        messages.addAll(checkNoCommands());\n        messages.addAll(checkNoFlowNodes());\n        messages.addAll(checkNoArchivedFlowNodes());\n        messages.addAll(checkNoComments());\n        messages.addAll(checkNoArchivedComments());\n        messages.addAll(checkNoWaitingEvent());\n        messages.addAll(checkNoProcessInstances());\n        messages.addAll(checkNoArchivedProcessInstances());\n        messages.addAll(checkNoProcessDefinitions());\n        messages.addAll(checkNoCategories());\n        messages.addAll(checkNoUsers());\n        messages.addAll(checkNoGroups());\n        messages.addAll(checkNoRoles());\n        messages.addAll(checkNoSupervisors());\n        logoutOnTenant();\n        if (!messages.isEmpty()) {\n            LOGGER.warn(\"Engine was not clean after test. We cleaned for you the following elements:\");\n            for (String message : messages) {\n                LOGGER.warn(message);\n            }\n        }\n    }\n\n    public List<String> checkNoCategories() throws DeletionException {\n        final List<String> messages = new ArrayList<>();\n        final long numberOfCategories = getProcessAPI().getNumberOfCategories();\n        if (numberOfCategories > 0) {\n            final List<Category> categories = getProcessAPI().getCategories(0, 5000, CategoryCriterion.NAME_ASC);\n            final StringBuilder categoryBuilder = new StringBuilder(\"Categories were still present: \");\n            for (final Category category : categories) {\n                categoryBuilder.append(category.getName()).append(\", \");\n                getProcessAPI().deleteCategory(category.getId());\n            }\n            messages.add(categoryBuilder.toString());\n        }\n        return messages;\n    }\n\n    public List<String> checkNoFlowNodes() throws SearchException {\n        final List<String> messages = new ArrayList<>();\n        final SearchOptionsBuilder build = new SearchOptionsBuilder(0, 1000);\n        final SearchResult<FlowNodeInstance> searchResult = getProcessAPI().searchFlowNodeInstances(build.done());\n        final List<FlowNodeInstance> flowNodeInstances = searchResult.getResult();\n        if (searchResult.getCount() > 0) {\n            final StringBuilder messageBuilder = new StringBuilder(\"FlowNodes were still present: \");\n            for (final FlowNodeInstance flowNodeInstance : flowNodeInstances) {\n                messageBuilder.append(\"{\").append(flowNodeInstance.getName()).append(\" - \")\n                        .append(flowNodeInstance.getType()).append(\"}\").append(\", \");\n            }\n            messages.add(messageBuilder.toString());\n        }\n        return messages;\n    }\n\n    public List<String> checkNoArchivedFlowNodes() throws SearchException {\n        final List<String> messages = new ArrayList<>();\n        final SearchOptionsBuilder build = new SearchOptionsBuilder(0, 1000);\n        final SearchResult<ArchivedFlowNodeInstance> searchResult = getProcessAPI()\n                .searchArchivedFlowNodeInstances(build.done());\n        final List<ArchivedFlowNodeInstance> archivedFlowNodeInstances = searchResult.getResult();\n        if (searchResult.getCount() > 0) {\n            final StringBuilder messageBuilder = new StringBuilder(\"Archived flowNodes were still present: \");\n            for (final ArchivedFlowNodeInstance archivedFlowNodeInstance : archivedFlowNodeInstances) {\n                messageBuilder.append(archivedFlowNodeInstance.getName()).append(\", \");\n            }\n            messages.add(messageBuilder.toString());\n        }\n        return messages;\n    }\n\n    public List<String> checkNoComments() throws SearchException {\n        final List<String> messages = new ArrayList<>();\n        final SearchOptionsBuilder build = new SearchOptionsBuilder(0, 1000);\n        final SearchResult<Comment> searchResult = getProcessAPI().searchComments(build.done());\n        final List<Comment> comments = searchResult.getResult();\n        if (searchResult.getCount() > 0) {\n            final StringBuilder messageBuilder = new StringBuilder(\"Comments were still present: \");\n            for (final Comment comment : comments) {\n                messageBuilder.append(comment.getContent()).append(\", \");\n            }\n            messages.add(messageBuilder.toString());\n        }\n        return messages;\n    }\n\n    public List<String> checkNoArchivedComments() throws SearchException {\n        final List<String> messages = new ArrayList<>();\n        final SearchOptionsBuilder build = new SearchOptionsBuilder(0, 1000);\n        final SearchResult<ArchivedComment> searchResult = getProcessAPI().searchArchivedComments(build.done());\n        final List<ArchivedComment> archivedComments = searchResult.getResult();\n        if (searchResult.getCount() > 0) {\n            final StringBuilder messageBuilder = new StringBuilder(\"Archived comments were still present: \");\n            for (final ArchivedComment archivedComment : archivedComments) {\n                messageBuilder.append(archivedComment.getName()).append(\", \");\n            }\n            messages.add(messageBuilder.toString());\n        }\n        return messages;\n    }\n\n    public List<String> checkNoProcessDefinitions() throws BonitaException {\n        final List<String> messages = new ArrayList<>();\n        final List<ProcessDeploymentInfo> processes = getProcessAPI().getProcessDeploymentInfos(0, 200,\n                ProcessDeploymentInfoCriterion.DEFAULT);\n        if (processes.size() > 0) {\n            final StringBuilder processBuilder = new StringBuilder(\"Process Definitions were still active: \");\n            for (final ProcessDeploymentInfo processDeploymentInfo : processes) {\n                processBuilder.append(processDeploymentInfo.getId()).append(\", \");\n                if (ActivationState.ENABLED.equals(processDeploymentInfo.getActivationState())) {\n                    getProcessAPI().disableProcess(processDeploymentInfo.getProcessId());\n                }\n                getProcessAPI().deleteProcessDefinition(processDeploymentInfo.getProcessId());\n            }\n            messages.add(processBuilder.toString());\n        }\n        return messages;\n    }\n\n    public List<String> checkNoWaitingEvent() throws BonitaException {\n        final List<String> messages = new ArrayList<>();\n        final Map<String, Serializable> parameters = new HashMap<>(1);\n        parameters.put(\"searchOptions\", new SearchOptionsBuilder(0, 200).done());\n\n        @SuppressWarnings(\"unchecked\")\n        final List<WaitingEvent> waitingEvents = ((SearchResult<WaitingEvent>) getCommandAPI()\n                .execute(\"searchWaitingEventsCommand\", parameters)).getResult();\n        if (waitingEvents.size() > 0) {\n            final StringBuilder messageBuilder = new StringBuilder(\n                    \"Waiting Events are still present (not deleted automatically): \");\n            for (final WaitingEvent waitingEvent : waitingEvents) {\n                messageBuilder.append(\"[process instance:\").append(waitingEvent.getProcessName())\n                        .append(\", flow node instance:\")\n                        .append(waitingEvent.getFlowNodeInstanceId()).append(\"]\").append(\n                                \", \");\n            }\n            messages.add(messageBuilder.toString());\n        }\n        return messages;\n    }\n\n    public List<String> checkNoSupervisors() throws SearchException, DeletionException {\n        final List<String> messages = new ArrayList<>();\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 200);\n        builder.sort(ProcessSupervisorSearchDescriptor.ID, Order.ASC);\n        final List<ProcessSupervisor> supervisors = getProcessAPI().searchProcessSupervisors(builder.done())\n                .getResult();\n\n        if (supervisors.size() > 0) {\n            final StringBuilder processBuilder = new StringBuilder(\"Process Supervisors were still active: \");\n            for (final ProcessSupervisor supervisor : supervisors) {\n                processBuilder.append(supervisor.getSupervisorId()).append(\", \");\n                getProcessAPI().deleteSupervisor(supervisor.getSupervisorId());\n            }\n            messages.add(processBuilder.toString());\n        }\n        return messages;\n    }\n\n    public List<String> checkNoProcessInstances() throws DeletionException {\n        final List<String> messages = new ArrayList<>();\n        final List<ProcessInstance> processInstances = getProcessAPI().getProcessInstances(0, 1000,\n                ProcessInstanceCriterion.DEFAULT);\n        if (!processInstances.isEmpty()) {\n            final StringBuilder stb = new StringBuilder(\"Process instances were still present: \");\n            for (final ProcessInstance processInstance : processInstances) {\n                stb.append(processInstance).append(\", \");\n                getProcessAPI().deleteProcessInstance(processInstance.getId());\n            }\n            messages.add(stb.toString());\n        }\n        return messages;\n    }\n\n    public List<String> checkNoArchivedProcessInstances() throws DeletionException {\n        final List<String> messages = new ArrayList<>();\n        final List<ArchivedProcessInstance> archivedProcessInstances = getProcessAPI().getArchivedProcessInstances(0,\n                1000, ProcessInstanceCriterion.DEFAULT);\n        if (!archivedProcessInstances.isEmpty()) {\n            final StringBuilder stb = new StringBuilder(\"Archived process instances were still present: \");\n            for (final ArchivedProcessInstance archivedProcessInstance : archivedProcessInstances) {\n                stb.append(archivedProcessInstance).append(\", \");\n                getProcessAPI().deleteArchivedProcessInstancesInAllStates(archivedProcessInstance.getSourceObjectId());\n            }\n            messages.add(stb.toString());\n        }\n        return messages;\n    }\n\n    public List<String> checkNoGroups() throws DeletionException {\n        final List<String> messages = new ArrayList<>();\n        final long numberOfGroups = getIdentityAPI().getNumberOfGroups();\n        if (numberOfGroups > 0) {\n            final List<Group> groups = getIdentityAPI().getGroups(0, Long.valueOf(numberOfGroups).intValue(),\n                    GroupCriterion.NAME_ASC);\n            final StringBuilder groupBuilder = new StringBuilder(\"Groups were still present: \");\n            for (final Group group : groups) {\n                groupBuilder.append(group.getId()).append(\", \");\n                getIdentityAPI().deleteGroup(group.getId());\n            }\n            messages.add(groupBuilder.toString());\n        }\n        return messages;\n    }\n\n    public List<String> checkNoRoles() throws DeletionException {\n        final List<String> messages = new ArrayList<>();\n        final long numberOfRoles = getIdentityAPI().getNumberOfRoles();\n        if (numberOfRoles > 0) {\n            final List<Role> roles = getIdentityAPI().getRoles(0, Long.valueOf(numberOfRoles).intValue(),\n                    RoleCriterion.NAME_ASC);\n            final StringBuilder roleBuilder = new StringBuilder(\"Roles were still present: \");\n            for (final Role role : roles) {\n                roleBuilder.append(role.getId()).append(\", \");\n                getIdentityAPI().deleteRole(role.getId());\n            }\n            messages.add(roleBuilder.toString());\n        }\n        return messages;\n    }\n\n    public List<String> checkNoUsers() throws DeletionException {\n        final List<String> messages = new ArrayList<>();\n        final long numberOfUsers = getIdentityAPI().getNumberOfUsers();\n        if (numberOfUsers > 0) {\n            final List<User> users = getIdentityAPI().getUsers(0, Long.valueOf(numberOfUsers).intValue(),\n                    UserCriterion.USER_NAME_ASC);\n            final StringBuilder userBuilder = new StringBuilder(\"Users were still present: \");\n            for (final User user : users) {\n                userBuilder.append(user.getId()).append(\", \");\n                getIdentityAPI().deleteUser(user.getId());\n            }\n            messages.add(userBuilder.toString());\n        }\n        return messages;\n    }\n\n    public List<String> checkNoCommands() throws SearchException, CommandNotFoundException, DeletionException {\n        final List<String> messages = new ArrayList<>();\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 1000);\n        searchOptionsBuilder.filter(CommandSearchDescriptor.SYSTEM, false);\n        searchOptionsBuilder.differentFrom(CommandSearchDescriptor.NAME, ClientEventUtil.ADD_HANDLER_COMMAND);\n        searchOptionsBuilder.differentFrom(CommandSearchDescriptor.NAME, ClientEventUtil.WAIT_SERVER_COMMAND);\n        final SearchResult<CommandDescriptor> searchCommands = getCommandAPI()\n                .searchCommands(searchOptionsBuilder.done());\n        final List<CommandDescriptor> commands = searchCommands.getResult();\n        if (searchCommands.getCount() > 0) {\n            final StringBuilder commandBuilder = new StringBuilder(\"Commands were still present: \");\n            for (final CommandDescriptor command : commands) {\n                commandBuilder.append(command.getName()).append(\", \");\n                getCommandAPI().unregister(command.getName());\n            }\n            messages.add(commandBuilder.toString());\n        }\n        return messages;\n    }\n\n}\n"
  },
  {
    "path": "bonita-test-api/src/main/java/org/bonitasoft/engine/test/internal/EngineStarter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.internal;\n\nimport java.io.File;\nimport java.io.FilenameFilter;\nimport java.io.IOException;\nimport java.lang.management.ManagementFactory;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.*;\n\nimport javax.naming.NamingException;\n\nimport org.apache.commons.io.FileUtils;\nimport org.bonitasoft.engine.BonitaDatabaseConfiguration;\nimport org.bonitasoft.engine.BonitaEngine;\nimport org.bonitasoft.engine.api.*;\nimport org.bonitasoft.engine.event.PlatformStartedEvent;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.io.IOUtil;\nimport org.bonitasoft.engine.platform.LoginException;\nimport org.bonitasoft.engine.platform.LogoutException;\nimport org.bonitasoft.engine.service.impl.ServiceAccessorFactory;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.PlatformSession;\nimport org.bonitasoft.engine.session.SessionNotFoundException;\nimport org.bonitasoft.engine.test.ClientEventUtil;\nimport org.bonitasoft.engine.test.TestEngineImpl;\nimport org.bonitasoft.engine.util.APITypeManager;\nimport org.bonitasoft.platform.database.DatabaseVendor;\nimport org.bonitasoft.platform.setup.PlatformSetup;\nimport org.bonitasoft.platform.setup.PlatformSetupAccessor;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Baptiste Mesta\n */\npublic class EngineStarter {\n\n    private static final String DATABASE_DIR = \"org.bonitasoft.h2.database.dir\";\n\n    protected static final Logger LOGGER = LoggerFactory.getLogger(EngineStarter.class.getName());\n    private static boolean hasFailed = false;\n\n    private boolean dropOnStart = true;\n    private final BonitaEngine engine;\n\n    protected EngineStarter(BonitaEngine engine) {\n        this.engine = engine;\n    }\n\n    public static EngineStarter create() {\n        return new EngineStarter(new BonitaEngine());\n    }\n\n    public static EngineStarter create(BonitaEngine engine) {\n        return new EngineStarter(engine);\n    }\n\n    public void start() throws Exception {\n        if (hasFailed) {\n            throw new IllegalStateException(\"Engine has previously failed to start\");\n        }\n        try {\n            LOGGER.info(\"=====================================================\");\n            LOGGER.info(\"==============  Starting Bonita Engine  =============\");\n            LOGGER.info(\"=====================================================\");\n            final long startTime = System.currentTimeMillis();\n            System.setProperty(\"com.arjuna.ats.arjuna.common.propertiesFile\", \"jbossts-properties.xml\");\n            if (System.getProperty(\"org.bonitasoft.engine.api-type\") == null) {\n                //force it to local if not specified\n                APITypeManager.setAPITypeAndParams(ApiAccessType.LOCAL, Collections.emptyMap());\n            }\n            if (APITypeManager.getAPIType().equals(ApiAccessType.LOCAL)) {\n                prepareEnvironment();\n                setupPlatform();\n                engine.start();\n            }\n            deployCommandsOnDefaultTenant();\n            LOGGER.info(\"==== Finished initialization (took \" + (System.currentTimeMillis() - startTime) / 1000\n                    + \"s)  ===\");\n\n            LOGGER.debug(\"Publishing platform started event\");\n            ServiceAccessorFactory.getInstance().createServiceAccessor().publishEvent(new PlatformStartedEvent());\n        } catch (Exception e) {\n            hasFailed = true;\n            throw e;\n        }\n    }\n\n    protected void setupPlatform() throws Exception {\n        PlatformSetup platformSetup = getPlatformSetup();\n        if (isDropOnStart()) {\n            platformSetup.destroy();\n        }\n    }\n\n    protected PlatformSetup getPlatformSetup() throws NamingException {\n        return PlatformSetupAccessor.getInstance().getPlatformSetup();\n    }\n\n    //--------------  engine life cycle methods\n\n    protected void prepareEnvironment() throws Exception {\n        LOGGER.info(\"=========  PREPARE ENVIRONMENT =======\");\n        String dbVendor = setSystemPropertyIfNotSet(PlatformSetup.BONITA_DB_VENDOR_PROPERTY,\n                DatabaseVendor.H2.getValue());\n        //is h2 and not started outside\n        if (DatabaseVendor.H2.equalsValue(dbVendor)) {\n            setSystemPropertyIfNotSet(DATABASE_DIR, \"build/database\");\n        }\n        engine.initializeEnvironment();\n    }\n\n    protected void shutdown() throws Exception {\n        undeployCommands();\n        deleteTenantAndPlatform();\n    }\n\n    protected void deleteTenantAndPlatform() throws Exception {\n        LOGGER.info(\"=========  CLEAN PLATFORM =======\");\n        final PlatformSession session = loginOnPlatform();\n        final PlatformAPI platformAPI = getPlatformAPI(session);\n        if (platformAPI.isNodeStarted()) {\n            platformAPI.stopNode();\n        }\n        logoutOnPlatform(session);\n        engine.stop();\n    }\n\n    protected PlatformAPI getPlatformAPI(PlatformSession session) throws BonitaHomeNotSetException, ServerAPIException,\n            UnknownAPITypeException {\n        return PlatformAPIAccessor.getPlatformAPI(session);\n    }\n\n    protected void checkThreadsAreStopped() throws InterruptedException {\n        LOGGER.info(\"=========  CHECK ENGINE IS SHUTDOWN =======\");\n        final Set<Thread> keySet = Thread.getAllStackTraces().keySet();\n        List<Thread> expectedThreads = new ArrayList<>();\n        List<Thread> cacheManagerThreads = new ArrayList<>();\n        List<Thread> unexpectedThreads = new ArrayList<>();\n        for (Thread thread : keySet) {\n            if (isExpectedThread(thread)) {\n                expectedThreads.add(thread);\n            } else {\n                if (isCacheManager(thread)) {\n                    cacheManagerThreads.add(thread);\n                } else {\n                    unexpectedThreads.add(thread);\n                }\n            }\n        }\n        // Only 1 cache manager thread is allowed\n        // there is no clean way to kill it, a shutdown hook is doing this\n        // killing it using hibernate implementation classes is causing weird behaviours\n        int nbOfThreads = keySet.size();\n        int nbOfExpectedThreads = expectedThreads.size() + 2;\n        boolean fail = nbOfThreads > nbOfExpectedThreads;\n        LOGGER.info(nbOfThreads + \" threads are alive. \" + nbOfExpectedThreads + \" are expected.\");\n        if (cacheManagerThreads.size() > 2) {\n            LOGGER.info(\n                    \"Only 1 CacheManager thread is expected (HibernatePersistenceService) but \"\n                            + cacheManagerThreads.size() + \" are found:\");\n            for (Thread thread : cacheManagerThreads) {\n                printThread(thread);\n            }\n        }\n        if (!unexpectedThreads.isEmpty()) {\n            LOGGER.info(\"The following list of threads is not expected:\");\n            for (Thread thread : unexpectedThreads) {\n                printThread(thread);\n            }\n        }\n        if (fail) {\n            throw new IllegalStateException(\n                    \"Some threads are still active : \\nCacheManager potential issues:\" + cacheManagerThreads\n                            + \"\\nOther threads:\" + unexpectedThreads);\n        }\n        LOGGER.info(\"All engine threads are stopped properly\");\n    }\n\n    private boolean isCacheManager(Thread thread) {\n        return thread.getName().contains(\"ehcache\") || thread.getName().contains(\"Ehcache\");\n    }\n\n    private void printThread(final Thread thread) {\n        LOGGER.info(\"\\n\");\n        LOGGER.info(\"Thread is still alive:\" + thread.getName());\n        for (StackTraceElement stackTraceElement : thread.getStackTrace()) {\n            LOGGER.info(\"        at \" + stackTraceElement);\n        }\n    }\n\n    private boolean isExpectedThread(final Thread thread) {\n        final String name = thread.getName();\n        final ThreadGroup threadGroup = thread.getThreadGroup();\n        if (threadGroup != null && threadGroup.getName().equals(\"system\")) {\n            return true;\n        }\n        final List<String> startWithFilter = Arrays.asList(\"H2 \", \"Timer-0\" /* postgres driver related */,\n                \"Transaction Reaper Worker 0\", \"Transaction Reaper\", \"main\", \"Reference Handler\", \"Signal Dispatcher\",\n                \"Finalizer\", \"com.google.common.base.internal.Finalizer\", \"process reaper\", \"ReaderThread\",\n                \"Abandoned connection cleanup thread\", \"Monitor Ctrl-Break\"/* Intellij */, \"daemon-shutdown\",\n                \"surefire-forkedjvm\", \"Restlet\", \"hz.bonita\");\n        for (final String prefix : startWithFilter) {\n            if (name.startsWith(prefix)) {\n                return true;\n            }\n        }\n        //shutdown hook not executed in main thread\n        return thread.getId() == Thread.currentThread().getId();\n    }\n\n    protected PlatformLoginAPI getPlatformLoginAPI() throws BonitaException {\n        return PlatformAPIAccessor.getPlatformLoginAPI();\n    }\n\n    protected PlatformSession loginOnPlatform() throws BonitaException {\n        final PlatformLoginAPI platformLoginAPI = getPlatformLoginAPI();\n        return platformLoginAPI.login(\"platformAdmin\", \"platform\");\n    }\n\n    protected void deployCommandsOnDefaultTenant() throws BonitaException {\n        final LoginAPI loginAPI = getLoginAPI();\n        final APISession session = login(loginAPI);\n        ClientEventUtil.deployCommand(session);\n        logout(loginAPI, session);\n    }\n\n    private void logout(LoginAPI loginAPI, APISession session) throws SessionNotFoundException, LogoutException {\n        loginAPI.logout(session);\n    }\n\n    private APISession login(LoginAPI loginAPI) throws LoginException {\n        return loginAPI.login(TestEngineImpl.TECHNICAL_USER_NAME, TestEngineImpl.TECHNICAL_USER_PASSWORD);\n    }\n\n    protected LoginAPI getLoginAPI() throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return TenantAPIAccessor.getLoginAPI();\n    }\n\n    protected void logoutOnPlatform(final PlatformSession session) throws BonitaException {\n        final PlatformLoginAPI platformLoginAPI = getPlatformLoginAPI();\n        platformLoginAPI.logout(session);\n    }\n\n    protected static String setSystemPropertyIfNotSet(final String property, final String value) {\n        final String finalValue = System.getProperty(property, value);\n        System.setProperty(property, finalValue);\n        return finalValue;\n    }\n\n    protected void undeployCommands() throws BonitaException {\n        final LoginAPI loginAPI = getLoginAPI();\n        final APISession session = login(loginAPI);\n        ClientEventUtil.undeployCommand(session);\n        logout(loginAPI, session);\n    }\n\n    public void stop() throws Exception {\n        LOGGER.info(\"=====================================================\");\n        LOGGER.info(\"============ CLEANING OF TEST ENVIRONMENT ===========\");\n        LOGGER.info(\"=====================================================\");\n\n        shutdown();\n\n        checkTempFoldersAreCleaned();\n        checkThreadsAreStopped();\n    }\n\n    protected void checkTempFoldersAreCleaned() throws IOException {\n        final List<File> folders = getTemporaryFolders();\n        removeLicensesFolderAndDeleteIt(folders);\n        StringBuilder builder = new StringBuilder();\n        for (File folder : folders) {\n            builder.append(\"[\");\n            builder.append(folder.getName());\n            builder.append(\"] \");\n        }\n        if (!folders.isEmpty()) {\n            throw new IllegalStateException(\"Temporary configuration folders are not cleaned:\" + builder.toString());\n        }\n        LOGGER.info(\"Temporary configuration folder is cleaned\");\n    }\n\n    private List<File> getTemporaryFolders() {\n        File tempFolder = new File(IOUtil.TMP_DIRECTORY);\n        FilenameFilter filter = (file, s) -> s.startsWith(\"bonita_\");\n        return new ArrayList<>(Arrays.asList(tempFolder.listFiles(filter)));\n    }\n\n    private void removeLicensesFolderAndDeleteIt(List<File> list) throws IOException {\n        Iterator<File> iterator = list.iterator();\n        while (iterator.hasNext()) {\n            File tempFolder = iterator.next();\n            //folder of licenses not deleted because the shutdown hook that delete temp files\n            //is executed as the same time as the shutdown hook that stops the engine\n            if (tempFolder.getName().contains(\"bonita_engine\")\n                    && tempFolder.getName().contains(ManagementFactory.getRuntimeMXBean().getName())) {\n                Path licenses = tempFolder.toPath().resolve(\"licenses\");\n                if (Files.exists(licenses)) {\n                    FileUtils.deleteDirectory(licenses.toFile());\n                }\n                if (tempFolder.list().length == 0) {\n                    FileUtils.deleteDirectory(tempFolder);\n                    //remove this directory because there was only the licenses there\n                    iterator.remove();\n                }\n            }\n        }\n    }\n\n    public void setDropOnStart(boolean dropOnStart) {\n        this.dropOnStart = dropOnStart;\n    }\n\n    public boolean isDropOnStart() {\n        return dropOnStart;\n    }\n\n    public void setBonitaDatabaseConfiguration(BonitaDatabaseConfiguration database) {\n        engine.setBonitaDatabaseConfiguration(database);\n    }\n\n    public void setBusinessDataDatabaseConfiguration(BonitaDatabaseConfiguration bonitaDatabaseConfiguration) {\n        engine.setBusinessDataDatabaseConfiguration(bonitaDatabaseConfiguration);\n    }\n}\n"
  },
  {
    "path": "bonita-test-api/src/main/java/org/bonitasoft/engine/test/junit/BonitaEngineRule.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.junit;\n\nimport java.lang.reflect.InvocationTargetException;\n\nimport org.bonitasoft.engine.test.TestEngine;\nimport org.bonitasoft.engine.test.TestEngineImpl;\nimport org.junit.rules.MethodRule;\nimport org.junit.runners.model.FrameworkMethod;\nimport org.junit.runners.model.Statement;\n\n/**\n * @author Baptiste Mesta\n */\npublic class BonitaEngineRule implements MethodRule {\n\n    private final TestEngine testEngine;\n    private boolean cleanAfterTest;\n\n    protected BonitaEngineRule(TestEngine testEngine) {\n        this.testEngine = testEngine;\n    }\n\n    public static BonitaEngineRule create() {\n        try {\n            var testEngineSpClazz = BonitaEngineRule.class.getClassLoader()\n                    .loadClass(\"com.bonitasoft.engine.test.TestEngineSP\");\n            TestEngine testEngineSpInstance = (TestEngine) testEngineSpClazz.getMethod(\"getInstance\").invoke(null);\n            return new BonitaEngineRule(testEngineSpInstance);\n        } catch (ClassNotFoundException e) {\n            // Use Community TestEngine\n        } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {\n            throw new RuntimeException(e);\n        }\n        return new BonitaEngineRule(TestEngineImpl.getInstance());\n    }\n\n    public static BonitaEngineRule createWith(TestEngine testEngine) {\n        return new BonitaEngineRule(testEngine);\n    }\n\n    public BonitaEngineRule withCleanAfterTest() {\n        cleanAfterTest = true;\n        return this;\n    }\n\n    // Used by Migration:\n    public BonitaEngineRule reuseExistingPlatform() {\n        testEngine.setDropOnStart(false);\n        return this;\n    }\n\n    @Override\n    public Statement apply(Statement statement, FrameworkMethod method, Object target) {\n        Statement newStatement = new WithTestEngine(statement, getTestEngine());\n        if (cleanAfterTest) {\n            newStatement = new WithCleanAfterTest(newStatement, getTestEngine());\n        }\n        return newStatement;\n    }\n\n    private static class WithTestEngine extends Statement {\n\n        private final Statement statement;\n        private final TestEngine testEngine;\n\n        public WithTestEngine(Statement statement, TestEngine testEngine) {\n            this.statement = statement;\n            this.testEngine = testEngine;\n        }\n\n        @Override\n        public void evaluate() throws Throwable {\n            startEngine();\n            statement.evaluate();\n        }\n\n        private void startEngine() throws Exception {\n            final boolean start = testEngine.start();\n            if (start) {\n                Runtime.getRuntime().addShutdownHook(new Thread(() -> {\n                    try {\n                        testEngine.stop();\n                    } catch (Exception e) {\n                        e.printStackTrace();\n                    }\n                }));\n            }\n        }\n    }\n\n    protected TestEngine getTestEngine() {\n        return testEngine;\n    }\n\n    private static class WithCleanAfterTest extends Statement {\n\n        private final Statement statement;\n        private final TestEngine testEngine;\n\n        public WithCleanAfterTest(Statement statement, TestEngine testEngine) {\n            this.statement = statement;\n            this.testEngine = testEngine;\n        }\n\n        @Override\n        public void evaluate() throws Throwable {\n            statement.evaluate();\n            testEngine.clearData();\n        }\n    }\n}\n"
  },
  {
    "path": "bonita-test-api/src/main/resources/logback-test.xml",
    "content": "<configuration>\n\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->\n        <encoder>\n            <pattern>|%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <logger name=\"org.bonitasoft\" level=\"INFO\" />\n    <logger name=\"com.arjuna\" level=\"INFO\" />\n    <logger name=\"org.hibernate.orm.cache\" level=\"WARN\" />\n    <logger name=\"org.hibernate.SQL_SLOW\" level=\"INFO\" />\n\n    <root level=\"WARN\">\n        <appender-ref ref=\"STDOUT\" />\n    </root>\n    <!-- Show events of synchro repository -->\n    <!-- <logger name=\"org.bonitasoft.engine.test.synchro.SynchroRepository\" level=\"DEBUG\" /> -->\n\n    <!-- Show stack traces when there is error on jobs -->\n    <!-- <logger name=\"org.bonitasoft.engine.scheduler.impl.JobWrapper\" level=\"DEBUG\" /> -->\n\n    <!-- Show lock acquire/release -->\n    <!--<logger name=\"org.bonitasoft.engine.lock.impl.MemoryLockService\" level=\"TRACE\" />-->\n    <!--<logger name=\"org.bonitasoft.engine.execution.work.FailureHandlingBonitaWork\" level=\"TRACE\" />-->\n    <!--<logger name=\"org.bonitasoft.engine.core.process.instance.impl.GatewayInstanceServiceImpl\" level=\"TRACE\" />-->\n    <!--<logger name=\"org.bonitasoft.engine.execution.ProcessExecutorImpl\" level=\"DEBUG\" />-->\n    <!---<logger name=\"org.bonitasoft.engine.execution.FlowNodeExecutorImpl\" level=\"DEBUG\" />-->\n    <!--<logger name=\"org.bonitasoft.engine.core.process.instance.impl.ActivityInstanceServiceImpl\" level=\"DEBUG\" />-->\n    <!--<logger name=\"org.bonitasoft.engine.recorder.impl.RecorderImpl\" level=\"DEBUG\" />-->\n    <!--<logger name=\"org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceServiceImpl\" level=\"DEBUG\" />-->\n    <!--<logger name=\"org.bonitasoft.engine.execution.work.TxBonitaWork\" level=\"DEBUG\" />-->\n    <!--<logger name=\"org.bonitasoft.engine.execution.work.LockProcessInstanceWork\" level=\"DEBUG\" />-->\n    <!--<logger name=\"org.bonitasoft.engine.work.ExecutorWorkService\" level=\"DEBUG\" />-->\n    <!--<logger name=\"org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl\" level=\"ERROR\"/>-->\n\n\n    <!-- Quartz log for job lifecycle -->\n    <!-- <property name=\"baseLogFileName\" value=\"target/log/quartzJobs\" />\n\n    <appender name=\"FILE\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <file>${baseLogFileName}.log</file>\n\n        Support multiple-JVM writing to the same log file\n        <prudent>true</prudent>\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\">\n            <fileNamePattern>${baseLogFileName}-%d{yyyy-MM-dd}.log.gz</fileNamePattern>\n            <maxHistory>30</maxHistory>\n            <cleanHistoryOnStart>true</cleanHistoryOnStart>\n        </rollingPolicy>\n        <encoder>\n            <pattern>|%d{HH:mm:ss.SSS}| %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <logger name=\"org.bonitasoft.engine.scheduler.impl.FileJobListener\" level=\"INFO\">\n        <appender-ref ref=\"FILE\" />\n    </logger> -->\n\n</configuration>\n"
  },
  {
    "path": "bonita-test-api/src/test/java/org/bonitasoft/engine/test/TestEngineImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test;\n\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\nimport org.bonitasoft.engine.test.internal.EngineCommander;\nimport org.bonitasoft.engine.test.internal.EngineStarter;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Baptiste Mesta\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class TestEngineImplTest {\n\n    @Mock\n    private EngineStarter engineStarter;\n    @Mock\n    private EngineCommander engineCommander;\n    @InjectMocks\n    private TestEngineImpl testEngine;\n\n    @Test\n    public void should_start_do_nothing_second_time() throws Exception {\n        //when\n        testEngine.start();\n        testEngine.start();\n\n        //then\n        verify(engineStarter, times(1)).start();\n    }\n\n}\n"
  },
  {
    "path": "bonita-test-api/src/test/java/org/bonitasoft/engine/test/internal/EngineStarterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.internal;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.Mockito.*;\n\nimport org.bonitasoft.engine.BonitaEngine;\nimport org.bonitasoft.engine.api.PlatformAPI;\nimport org.bonitasoft.engine.api.PlatformLoginAPI;\nimport org.bonitasoft.engine.session.PlatformSession;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Baptiste Mesta\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class EngineStarterTest {\n\n    @Mock\n    private PlatformSession platformSession;\n    @Mock\n    private PlatformAPI platformAPI;\n    @Mock\n    private PlatformLoginAPI platformLoginAPI;\n    @Mock\n    private BonitaEngine bonitaEngine;\n    private EngineStarter engineStarter;\n\n    @Before\n    public void before() throws Exception {\n        engineStarter = spy(EngineStarter.create(bonitaEngine));\n        doReturn(platformSession).when(platformLoginAPI).login(anyString(), anyString());\n        doReturn(platformLoginAPI).when(engineStarter).getPlatformLoginAPI();\n        doReturn(platformAPI).when(engineStarter).getPlatformAPI(any(PlatformSession.class));\n        doNothing().when(engineStarter).undeployCommands();\n        doNothing().when(engineStarter).checkTempFoldersAreCleaned();\n        doNothing().when(engineStarter).checkThreadsAreStopped();\n    }\n\n    @Test\n    public void should_stop_node() throws Exception {\n        //given\n        doReturn(true).when(platformAPI).isNodeStarted();\n        //when\n        engineStarter.stop();\n        //then\n        verify(platformAPI).stopNode();\n        verify(bonitaEngine).stop();\n    }\n\n}\n"
  },
  {
    "path": "bonita-test-api/src/test/java/org/bonitasoft/engine/test/junit/BonitaEngineRuleTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.test.junit;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.BonitaDatabaseConfiguration;\nimport org.bonitasoft.engine.test.TestEngine;\nimport org.junit.Rule;\nimport org.junit.Test;\n\n/**\n * @author Baptiste Mesta\n */\npublic class BonitaEngineRuleTest {\n\n    private final MyTestEngine testEngineToInject = new MyTestEngine();\n    @Rule\n    public BonitaEngineRule bonitaEngineRule = BonitaEngineRule.createWith(testEngineToInject);\n\n    @Test\n    public void should_TestEngine_be_started() {\n        assertThat(testEngineToInject.isStarted).isTrue();\n    }\n\n    private static class MyTestEngine implements TestEngine {\n\n        public boolean isStarted;\n\n        @Override\n        public boolean start() {\n            isStarted = true;\n            return false;\n        }\n\n        @Override\n        public void stop() {\n        }\n\n        @Override\n        public void clearData() {\n        }\n\n        @Override\n        public void setDropOnStart(boolean dropOnStart) {\n        }\n\n        @Override\n        public void setBonitaDatabaseProperties(BonitaDatabaseConfiguration database) {\n        }\n\n        @Override\n        public void setBusinessDataDatabaseProperties(BonitaDatabaseConfiguration database) {\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-api/bonita-server-api-http/build.gradle",
    "content": "dependencies {\n    api project(':platform:platform-resources')\n    api project(':bpm:bonita-core:bonita-process-engine')\n    api project(':services:bonita-platform-session')\n    api project(':services:bonita-session')\n    api project(':bpm:bonita-common')\n    api libs.xstream\n    api libs.commonsFileUpload\n    api libs.commonsIO\n    testImplementation libs.springTest\n    testImplementation libs.assertj\n    testImplementation libs.mockitoCore\n    testImplementation libs.systemRules\n    testRuntimeOnly libs.springWebMvc\n    compileOnly libs.jakartaServletApi\n    testImplementation libs.jakartaServletApi\n}\n"
  },
  {
    "path": "bpm/bonita-api/bonita-server-api-http/src/main/java/org/bonitasoft/engine/api/internal/servlet/EngineInitializerListener.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.internal.servlet;\n\nimport javax.naming.NamingException;\nimport javax.servlet.ServletContextEvent;\nimport javax.servlet.ServletContextListener;\n\nimport org.bonitasoft.engine.EngineInitializer;\nimport org.bonitasoft.engine.service.impl.ServiceAccessorFactory;\nimport org.bonitasoft.platform.setup.PlatformSetup;\nimport org.bonitasoft.platform.setup.PlatformSetupAccessor;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.context.support.AnnotationConfigWebApplicationContext;\n\n/**\n * Servlet container entry point that orchestrates the complete Bonita Engine initialization sequence.\n * <p>\n * This listener is triggered by the servlet container (Tomcat) during application startup\n * and shutdown. It coordinates the following initialization phases:\n * <ol>\n * <li><b>Platform Setup Phase</b>: Initializes the database schema and configuration via {@link PlatformSetup}</li>\n * <li><b>Engine Initialization Phase</b>: Starts the engine and loads all services via {@link EngineInitializer}</li>\n * <li><b>Web Context Phase</b>: Creates the web application context with the engine context as parent</li>\n * </ol>\n * <p>\n * <b>Responsibilities:</b>\n * <ul>\n * <li>Invokes {@link PlatformSetup#init()} to create/update database tables and configuration</li>\n * <li>Calls {@link EngineInitializer#initializeEngine()} to start the platform node</li>\n * <li>Creates an {@link AnnotationConfigWebApplicationContext} with the engine Spring context as parent</li>\n * <li>Registers the web context in servlet context for access by REST controllers and filters</li>\n * <li>Handles graceful shutdown via {@link EngineInitializer#unloadEngine()}</li>\n * <li>Supports update-only mode via {@code bonita.runtime.startup.update-only} property</li>\n * </ul>\n * <p>\n * The resulting Spring context hierarchy is:\n *\n * <pre>\n * Engine Context (parent) → contains all engine services (100+ beans)\n *     └── Web Context (child) → contains REST API controllers, filters, web config\n * </pre>\n *\n * @see EngineInitializer\n * @see PlatformSetup\n * @see ServiceAccessorFactory\n */\npublic class EngineInitializerListener implements ServletContextListener {\n\n    static final String UPDATE_ONLY_STARTUP_PROPERTY = \"bonita.runtime.startup.update-only\";\n\n    private static final Logger log = LoggerFactory.getLogger(EngineInitializerListener.class);\n\n    @Override\n    public void contextInitialized(final ServletContextEvent event) {\n        var engineInitializer = getEngineInitializer();\n        try {\n            var webApplicationContext = initializeWebApplicationContext(event, engineInitializer);\n            boolean updateOnly = webApplicationContext.getEnvironment().getProperty(UPDATE_ONLY_STARTUP_PROPERTY,\n                    Boolean.class,\n                    Boolean.FALSE);\n            if (updateOnly) {\n                log.info(\"'{}' enabled. Shutting down JVM.\", UPDATE_ONLY_STARTUP_PROPERTY);\n                engineInitializer.unloadEngine();\n                exit(0);\n            }\n        } catch (final Throwable e) {\n            try {\n                engineInitializer.unloadEngine();\n            } catch (Exception ex) {\n                log.warn(\"Error while unloading the Engine\", ex);\n            }\n            log.error(\"Error occurred while initializing the Engine. Shutting down JVM...\", e);\n            exit(1);\n        }\n    }\n\n    AnnotationConfigWebApplicationContext initializeWebApplicationContext(ServletContextEvent event,\n            EngineInitializer engineInitializer) throws Exception {\n        getPlatformSetup().init(); // init tables and default configuration\n        engineInitializer.initializeEngine();\n        ApplicationContext engineContext = ServiceAccessorFactory.getInstance()\n                .createServiceAccessor()\n                .getContext();\n        AnnotationConfigWebApplicationContext webApplicationContext = initializeWebContext(event, engineContext);\n        webApplicationContext.refresh();\n        return webApplicationContext;\n    }\n\n    protected PlatformSetup getPlatformSetup() throws NamingException {\n        return PlatformSetupAccessor.getInstance().getPlatformSetup();\n    }\n\n    void exit(int code) {\n        System.exit(code);\n    }\n\n    protected AnnotationConfigWebApplicationContext initializeWebContext(ServletContextEvent event,\n            ApplicationContext engineContext) {\n        AnnotationConfigWebApplicationContext webApplicationContext = new AnnotationConfigWebApplicationContext();\n        webApplicationContext.setParent(engineContext);\n        webApplicationContext.setServletContext(event.getServletContext());\n        //required for login configuration beans (Brute force protection, etc.)\n        webApplicationContext.scan(\"org.bonitasoft.web.server\");\n        //A web application context needs to be referenced in the Servlet context so that servlet and filters beans handled by Spring web can use it\n        event.getServletContext().setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,\n                webApplicationContext);\n        return webApplicationContext;\n    }\n\n    protected EngineInitializer getEngineInitializer() {\n        return new EngineInitializer();\n    }\n\n    @Override\n    public void contextDestroyed(final ServletContextEvent event) {\n        try {\n            getEngineInitializer().unloadEngine();\n        } catch (final Throwable e) {\n            log.error(\"Error while unloading the Engine\", e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-api/bonita-server-api-http/src/main/java/org/bonitasoft/engine/api/internal/servlet/HttpAPIServlet.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.internal.servlet;\n\nimport static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;\n\nimport java.io.IOException;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.apache.commons.fileupload.FileUploadException;\nimport org.bonitasoft.engine.properties.BooleanProperty;\n\n/**\n * @author Julien Mege\n * @author Baptiste Mesta\n * @author Emmanuel Duchastenier\n */\npublic class HttpAPIServlet extends HttpServlet {\n\n    public static final String PROPERTY_TO_ENABLE_HTTP_API = \"http.api\";\n\n    private static final long serialVersionUID = 4936475894513095747L;\n    private BooleanProperty httpApi;\n\n    @Override\n    public void init() throws ServletException {\n        httpApi = new BooleanProperty(\"Http API\", PROPERTY_TO_ENABLE_HTTP_API, true);\n    }\n\n    @Override\n    protected void doPost(final HttpServletRequest req, final HttpServletResponse resp)\n            throws ServletException, IOException {\n        // Defense-in-depth: reject FORWARD dispatches to /serverAPI/*.\n        // The /serverAPI endpoint is an internal engine HTTP API protected by a web.xml\n        // security-constraint, but security-constraints only apply to REQUEST dispatches\n        // (Servlet 3.0 spec). A path traversal attack could forward requests here from\n        // the /apps/* or /API/* namespaces, bypassing the security-constraint entirely.\n        if (req.getDispatcherType() == javax.servlet.DispatcherType.FORWARD) {\n            resp.sendError(SC_FORBIDDEN);\n            return;\n        }\n        if (!httpApi.isEnabled()) {\n            resp.sendError(SC_FORBIDDEN);\n            return;\n        }\n        callHttpApi(req, resp);\n    }\n\n    void callHttpApi(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {\n        try {\n            new HttpAPIServletCall(req, resp).doPost();\n        } catch (final FileUploadException e) {\n            throw new ServletException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-api/bonita-server-api-http/src/main/java/org/bonitasoft/engine/api/internal/servlet/HttpAPIServletCall.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.internal.servlet;\n\nimport static org.apache.commons.lang3.StringUtils.isNotBlank;\n\nimport java.io.IOException;\nimport java.io.InvalidClassException;\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.stream.Stream;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport com.thoughtworks.xstream.security.ForbiddenClassException;\nimport org.apache.commons.fileupload.FileUploadException;\nimport org.bonitasoft.engine.api.impl.ServerAPIFactory;\nimport org.bonitasoft.engine.api.internal.ServerAPI;\nimport org.bonitasoft.engine.api.internal.ServerWrappedException;\nimport org.bonitasoft.engine.api.internal.servlet.impl.XmlConverter;\nimport org.bonitasoft.engine.exception.StackTraceTransformer;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Julien Mege\n * @author Matthieu Chaffotte\n */\npublic class HttpAPIServletCall extends ServletCall {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(HttpAPIServletCall.class);\n\n    // JEP 290 filter rejection message — part of JDK internal format since JDK 9\n    // (see java.io.ObjectInputStream); may need updating if a future JDK changes this wording.\n    private static final String JEP290_FILTER_REJECTED_MSG = \"filter status: REJECTED\";\n\n    private static final String SLASH = \"/\";\n\n    private static final String ARRAY = \"[]\";\n\n    private static final String NULL = \"null\";\n\n    private static final String BYTE_ARRAY = \"==ByteArray==\";\n\n    private static final String CLASS_NAME_PARAMETERS = \"classNameParameters\";\n\n    private static final String PARAMETERS_VALUES = \"parametersValues\";\n\n    private static final String OPTIONS = \"options\";\n\n    private XmlConverter xmlConverter;\n\n    public HttpAPIServletCall(final HttpServletRequest request, final HttpServletResponse response)\n            throws FileUploadException, IOException {\n        super(request, response);\n        xmlConverter = new XmlConverter();\n    }\n\n    @Override\n    protected String getResponseContentType() {\n        return \"application/xml;charset=UTF-8\";\n    }\n\n    @Override\n    public void doGet() {\n        error(\"GET method forbidden\", HttpServletResponse.SC_FORBIDDEN);\n    }\n\n    @Override\n    public void doPost() {\n        try {\n            String apiInterfaceName = null;\n            String methodName = null;\n            final String[] pathParams = getRequestURL().split(SLASH);\n            if (pathParams != null && pathParams.length >= 2) {\n                apiInterfaceName = pathParams[pathParams.length - 2];\n                methodName = pathParams[pathParams.length - 1];\n            }\n            final String options = this.getParameter(OPTIONS);\n            final String parametersValues = this.getParameter(PARAMETERS_VALUES);\n            final String parametersClasses = this.getParameter(CLASS_NAME_PARAMETERS);\n\n            Map<String, Serializable> myOptions = new HashMap<>();\n            if (isNotBlank(options)) {\n                myOptions = xmlConverter.fromXML(options);\n            }\n            List<String> myClassNameParameters = new ArrayList<>();\n            if (parametersClasses != null && !parametersClasses.isEmpty() && !parametersClasses.equals(ARRAY)) {\n                myClassNameParameters = xmlConverter.fromXML(parametersClasses);\n            }\n            Object[] myParametersValues = new Object[0];\n            if (parametersValues != null && !parametersValues.isEmpty() && !parametersValues.equals(NULL)) {\n                myParametersValues = xmlConverter.fromXML(parametersValues);\n                if (myParametersValues != null && !(myParametersValues.length == 0)) {\n                    final Iterator<byte[]> binaryParameters = getBinaryParameters().iterator();\n                    for (int i = 0; i < myParametersValues.length; i++) {\n                        final Object parameter = myParametersValues[i];\n                        if (BYTE_ARRAY.equals(parameter)) {\n                            myParametersValues[i] = deserialize(binaryParameters.next());\n                        }\n                    }\n                }\n            }\n\n            final Object invokeMethod;\n            try {\n                invokeMethod = getServerAPI().invokeMethod(myOptions, apiInterfaceName, methodName,\n                        myClassNameParameters, myParametersValues);\n            } catch (ServerWrappedException e) {\n                // merge stack trace of the server exception\n                throw StackTraceTransformer.mergeStackTraces(e);\n            }\n\n            String invokeMethodSerialized = null;\n            if (invokeMethod != null) {\n                invokeMethodSerialized = xmlConverter.toXML(invokeMethod);\n            }\n\n            // add charset avoid encoding problems\n            output(invokeMethodSerialized);\n        } catch (final Exception e) {\n            error(toResponse(e), HttpServletResponse.SC_INTERNAL_SERVER_ERROR);\n        }\n    }\n\n    // Visible for testing\n    ServerAPI getServerAPI() {\n        return ServerAPIFactory.getServerAPI();\n    }\n\n    @Override\n    public void doPut() {\n        error(\"PUT method forbidden\", HttpServletResponse.SC_FORBIDDEN);\n    }\n\n    @Override\n    public void doDelete() {\n        error(\"DELETE method forbidden\", HttpServletResponse.SC_FORBIDDEN);\n    }\n\n    private String toResponse(final Exception exception) {\n        Throwable result = exception;\n        if (exception instanceof ServerWrappedException) {\n            result = exception.getCause();\n        }\n        if (isDeserializationSecurityException(result)) {\n            LOGGER.warn(\"Blocked suspected deserialization attack on {}\", getRequestURL(), result);\n            // Serialize a plain String rather than a BonitaRuntimeException to avoid\n            // XStream's ThrowableConverter hitting Java module access restrictions\n            // when walking exception class hierarchies (e.g. IOException.serialVersionUID).\n            return xmlConverter.toXML(\"Invalid request\");\n        }\n        return xmlConverter.toXML(result);\n    }\n\n    private boolean isDeserializationSecurityException(Throwable t) {\n        // Check message to distinguish JEP 290 filter rejections from normal\n        // InvalidClassException causes (e.g. serialVersionUID mismatch)\n        return Stream.iterate(t, Objects::nonNull, Throwable::getCause)\n                .anyMatch(cause -> cause instanceof ForbiddenClassException\n                        || (cause instanceof InvalidClassException\n                                && cause.getMessage() != null\n                                && cause.getMessage().contains(JEP290_FILTER_REJECTED_MSG)));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-api/bonita-server-api-http/src/main/java/org/bonitasoft/engine/api/internal/servlet/ServletCall.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.internal.servlet;\n\nimport java.io.BufferedReader;\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.ObjectInputFilter;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectOutputStream;\nimport java.io.PrintWriter;\nimport java.nio.charset.StandardCharsets;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Set;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpSession;\n\nimport org.apache.commons.fileupload.FileItemIterator;\nimport org.apache.commons.fileupload.FileItemStream;\nimport org.apache.commons.fileupload.FileUploadException;\nimport org.apache.commons.fileupload.servlet.ServletFileUpload;\nimport org.apache.commons.io.IOUtils;\n\n/**\n * @author Severin Moussel\n */\npublic abstract class ServletCall {\n\n    private static final String BINARY_PARAMETER = \"binaryParameter\";\n\n    /**\n     * JEP 290 deserialization filter.\n     * The binary parameter path (==ByteArray== multipart attachments) deserializes objects with\n     * ObjectInputStream. Without a filter, an attacker can send crafted payloads containing\n     * gadget-chain classes (URLDNS, commons-collections4, JNDI, etc.) to achieve SSRF or RCE.\n     * This allowlist restricts deserialization to the only types legitimately sent through this path:\n     * - org.bonitasoft.** / com.bonitasoft.** : BusinessArchive and its full object graph\n     * - java.lang.* : String, Long, Integer, etc. (not subpackages like java.lang.reflect)\n     * - java.util.** : HashMap, ArrayList, Collections inner classes\n     * - java.math.* : BigDecimal, BigInteger (process variables)\n     * - !* : reject everything else\n     */\n    private static final ObjectInputFilter DESERIALIZATION_FILTER = ObjectInputFilter.Config\n            .createFilter(\n                    \"org.bonitasoft.**;com.bonitasoft.**;java.lang.*;java.util.**;java.math.*;!*\");\n\n    private String inputStream = null;\n\n    /**\n     * The parameters of the URL.<br />\n     * Result of the parsing of the query string : \"?a=b&c=d&...\"\n     */\n    protected Map<String, String> parameters = null;\n\n    /**\n     * The request made to access this servletCall.\n     */\n    private final HttpServletRequest request;\n\n    /**\n     * The response to return.\n     */\n    private final HttpServletResponse response;\n\n    private final List<byte[]> binaryParameters;\n\n    /**\n     * Default constructor.\n     *\n     * @param request\n     *        The request made to access this servletCall.\n     * @param response\n     *        The response to return.\n     * @throws IOException\n     * @throws FileUploadException\n     */\n    protected ServletCall(final HttpServletRequest request, final HttpServletResponse response)\n            throws FileUploadException, IOException {\n        super();\n        this.request = request;\n        this.response = response;\n        parameters = new HashMap<>();\n        binaryParameters = new ArrayList<>();\n        if (ServletFileUpload.isMultipartContent(request)) {\n            final ServletFileUpload upload = new ServletFileUpload();\n            // Parse the request\n            final FileItemIterator iter = upload.getItemIterator(request);\n            while (iter.hasNext()) {\n                try {\n                    final FileItemStream item = iter.next();\n                    try (var stream = item.openStream()) {\n                        String fieldName = item.getFieldName();\n                        if (fieldName.startsWith(BINARY_PARAMETER)) {\n                            binaryParameters.add(stream.readAllBytes());\n                        } else {\n                            parameters.put(fieldName, new String(stream.readAllBytes(), StandardCharsets.UTF_8));\n                        }\n                    }\n                } catch (final Exception t) {\n                    throw new IOException(t);\n                }\n            }\n        } else {\n            final Map<String, String[]> parameterMap = this.request.getParameterMap();\n            final Set<Entry<String, String[]>> entrySet = parameterMap.entrySet();\n            for (final Entry<String, String[]> entry : entrySet) {\n                parameters.put(entry.getKey(), entry.getValue()[0]);\n            }\n        }\n    }\n\n    /**\n     * @return the binaryParameters\n     */\n    public List<byte[]> getBinaryParameters() {\n        return binaryParameters;\n    }\n\n    public byte[] serialize(final Object obj) throws IOException {\n        final ByteArrayOutputStream b = new ByteArrayOutputStream();\n        try (final ObjectOutputStream o = new ObjectOutputStream(b)) {\n            o.writeObject(obj);\n        }\n        return b.toByteArray();\n    }\n\n    public Object deserialize(final byte[] bytes) throws IOException, ClassNotFoundException {\n        final ByteArrayInputStream b = new ByteArrayInputStream(bytes);\n        try (final ObjectInputStream o = new ObjectInputStream(b)) {\n            o.setObjectInputFilter(DESERIALIZATION_FILTER);\n            return o.readObject();\n        }\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // PARAMETERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Get the current call's HttpSession\n     *\n     * @return This method returns the session from the current call.\n     */\n    public HttpSession getHttpSession() {\n        return request.getSession();\n    }\n\n    /**\n     * @see javax.servlet.http.HttpServletRequest#getQueryString()\n     */\n    public String getQueryString() {\n        return request.getQueryString();\n    }\n\n    /**\n     * Reconstruct the URL the client used to make the request.\n     * The returned URL contains a protocol, server name, port\n     * number, and server path, but it does not include query\n     * string parameters.\n     *\n     * @return This method returns the reconstructed URL\n     */\n    public final String getRequestURL() {\n        return request.getRequestURL().toString();\n    }\n\n    /**\n     * Read the input stream and set it in a String\n     */\n    public final String getInputStream() {\n        if (inputStream == null) {\n            try {\n                final BufferedReader reader = request.getReader();\n                final StringBuilder sb = new StringBuilder();\n                String line = reader.readLine();\n                while (line != null) {\n                    sb.append(line + \"\\n\");\n                    line = reader.readLine();\n                }\n                reader.close();\n\n                inputStream = sb.toString();\n\n            } catch (final IOException e) {\n                throw new RuntimeException(\"Can't read input Stream.\", e);\n            }\n        }\n\n        return inputStream;\n    }\n\n    /**\n     * Count the number of parameters passed in the URL\n     *\n     * @return This method returns the number of parameters in the URL\n     */\n    public final int countParameters() {\n        return parameters.size();\n    }\n\n    /**\n     * Get a parameter values by its name\n     *\n     * @param name\n     *        The name of the parameter (case sensitive)\n     * @return This method returns the values of a parameter as a list of String or null if the parameter isn't defined\n     */\n    public final List<String> getParameterAsList(final String name) {\n        return getParameterAsList(name, (String) null);\n    }\n\n    /**\n     * Get a parameter values by its name\n     *\n     * @param name\n     *        The name of the parameter (case sensitive)\n     * @param defaultValue\n     *        The value to return if the parameter isn't define\n     * @return This method returns the values of a parameter as a list of String\n     */\n    public final List<String> getParameterAsList(final String name, final String defaultValue) {\n        if (parameters.containsKey(name)) {\n            return Arrays.asList(parameters.get(name));\n        }\n        if (defaultValue != null) {\n            final List<String> results = new ArrayList<String>();\n            results.add(defaultValue);\n            return results;\n        }\n        return null;\n    }\n\n    /**\n     * Get a parameter first value by its name\n     *\n     * @param name\n     *        The name of the parameter (case sensitive)\n     * @return This method returns the first value of a parameter as a String or null if the parameter isn't define\n     */\n    public final String getParameter(final String name) {\n        return getParameter(name, (String) null);\n    }\n\n    /**\n     * Get a parameter first value by its name\n     *\n     * @param name\n     *        The name of the parameter (case sensitive)\n     * @param defaultValue\n     *        The value to return if the parameter isn't define\n     * @return This method returns the first value of a parameter as a String\n     */\n    public final String getParameter(final String name, final String defaultValue) {\n        if (parameters.containsKey(name)) {\n            return parameters.get(name);\n        }\n        return defaultValue;\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // GENERATE RESPONSES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Write into the output header.\n     *\n     * @param name\n     *        The name of the header to write.\n     * @param value\n     *        The value of the header to write.\n     */\n    protected final void head(final String name, final String value) {\n        response.addHeader(name, value);\n    }\n\n    /**\n     * Output a file\n     *\n     * @param file\n     *        The file to output\n     */\n    protected final void output(final File file) {\n        try {\n            output(new FileInputStream(file));\n        } catch (final FileNotFoundException e) {\n            error(\"Can not download file.\", HttpServletResponse.SC_INTERNAL_SERVER_ERROR);\n        }\n    }\n\n    /**\n     * Output a stream as a file\n     *\n     * @param stream\n     *        The stream to output\n     * @param filename\n     *        The name of the file to retrieve with the stream.\n     */\n    protected void output(final InputStream stream, final String filename) {\n        response.addHeader(\"Content-Disposition\", \"attachment; filename=\" + filename + \";\");\n        output(stream);\n    }\n\n    /**\n     * Output a stream as a file\n     *\n     * @param stream\n     *        The stream to output\n     */\n    protected void output(final InputStream stream) {\n        response.setContentType(\"application/octet-stream\");\n        try {\n            IOUtils.copy(stream, response.getOutputStream());\n        } catch (final IOException e) {\n            error(\"Can not download stream.\", HttpServletResponse.SC_INTERNAL_SERVER_ERROR);\n        }\n    }\n\n    protected void error(final String message, final int errorCode) {\n        output(message);\n        response.setStatus(errorCode);\n    }\n\n    /**\n     * Write into the output\n     *\n     * @param string\n     *        The string to output\n     */\n    protected final void output(final String string) {\n        final PrintWriter outputWriter = getOutputWriter();\n        // Skip the write on null rather than letting PrintWriter.print(null) emit the literal \"null\":\n        // wrapping response writers that track content length (e.g. Spring Session's\n        // SaveContextPrintWriter via OnCommittedResponseWrapper.trackContentLength) NPE on that path (BPA-443)\n        if (string != null) {\n            outputWriter.print(string);\n        }\n        outputWriter.flush();\n        outputWriter.close();\n    }\n\n    /**\n     * Write into the output\n     *\n     * @param object\n     *        An object that will be transform into JSon\n     */\n    protected final void output(final Object object) {\n        final PrintWriter outputWriter = getOutputWriter();\n        // FIXME use xstream\n\n        // Skip on null: object.toString() would NPE. Symmetric with output(String)\n        if (object != null) {\n            outputWriter.print(object.toString());\n        }\n        outputWriter.flush();\n        outputWriter.close();\n    }\n\n    /**\n     * The outputWriter in which to write the response String.\n     */\n    private PrintWriter outputWriter = null;\n\n    private PrintWriter getOutputWriter() {\n        if (outputWriter == null) {\n            response.setContentType(getResponseContentType());\n            try {\n                outputWriter = response.getWriter();\n            } catch (final IOException e) {\n                throw new RuntimeException(e);\n            }\n        }\n        return outputWriter;\n    }\n\n    protected String getResponseContentType() {\n        return \"application/json;charset=UTF-8\";\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // REQUEST ENTRY POINTS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Entry point for GET and SEARCH\n     */\n    public abstract void doGet();\n\n    /**\n     * Entry point for CREATE\n     */\n    public abstract void doPost();\n\n    /**\n     * Entry point for UPDATE\n     */\n    public abstract void doPut();\n\n    /**\n     * Entry point for DELETE\n     */\n    public abstract void doDelete();\n\n}\n"
  },
  {
    "path": "bpm/bonita-api/bonita-server-api-http/src/main/java/org/bonitasoft/engine/api/internal/servlet/impl/XmlConverter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.internal.servlet.impl;\n\nimport java.io.IOException;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectOutputStream;\nimport java.io.StringReader;\nimport java.io.StringWriter;\n\nimport com.thoughtworks.xstream.XStream;\nimport com.thoughtworks.xstream.security.AnyTypePermission;\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\nimport org.bonitasoft.engine.xml.XStreamDenyList;\n\n// TODO duplicated with the implementation in org.bonitasoft.engine.api.impl\n// The only difference is the settings of Xstream\n// TODO xstream instance should also ignored unknowns elements as the client to avoid issue in the future (fill a task)\npublic class XmlConverter {\n\n    private static volatile XStream xstream;\n\n    private static XStream getXStream() {\n        if (xstream == null) {\n            synchronized (XmlConverter.class) {\n                if (xstream == null) {\n                    xstream = createXStream();\n                }\n            }\n        }\n        return xstream;\n    }\n\n    private static XStream createXStream() {\n        var xs = new XStream();\n        xs.addPermission(AnyTypePermission.ANY);\n        // Block known deserialization gadget chain libraries to prevent RCE.\n        // A strict allowlist is not possible here because API parameters can contain BDM types with arbitrary packages.\n        xs.denyTypesByWildcard(XStreamDenyList.getDenyPatterns());\n        // ignore fields suppressedExceptions causing exceptions in some cases\n        xs.omitField(Throwable.class, \"suppressedExceptions\");\n        return xs;\n    }\n\n    // Package-private for testing\n    static void reset() {\n        xstream = null;\n    }\n\n    public String toXML(final Object object) {\n        final StringWriter stringWriter = new StringWriter();\n        try (final ObjectOutputStream out = getXStream().createObjectOutputStream(stringWriter)) {\n            out.writeObject(object);\n        } catch (IOException e) {\n            throw new BonitaRuntimeException(\"Unable to serialize object \" + object, e);\n        }\n        return stringWriter.toString();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public <T> T fromXML(final String object) {\n        try (final StringReader xmlReader = new StringReader(object);\n                final ObjectInputStream in = getXStream().createObjectInputStream(xmlReader)) {\n            return (T) in.readObject();\n        } catch (final ClassNotFoundException | IOException | RuntimeException e) {\n            // Do not include the XML payload in the error message to prevent\n            // information disclosure of attacker-controlled input\n            throw new BonitaRuntimeException(\"Unable to deserialize object\", e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-api/bonita-server-api-http/src/test/java/org/bonitasoft/engine/api/internal/servlet/EngineInitializerListenerTest.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.internal.servlet;\n\nimport static org.mockito.Mockito.*;\n\nimport javax.servlet.ServletContext;\nimport javax.servlet.ServletContextEvent;\n\nimport org.bonitasoft.engine.EngineInitializer;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport org.mockito.junit.jupiter.MockitoSettings;\nimport org.mockito.quality.Strictness;\nimport org.springframework.mock.env.MockEnvironment;\nimport org.springframework.web.context.support.AnnotationConfigWebApplicationContext;\n\n@ExtendWith(MockitoExtension.class)\n@MockitoSettings(strictness = Strictness.LENIENT)\nclass EngineInitializerListenerTest {\n\n    @Spy\n    private EngineInitializerListener listener;\n\n    @Mock\n    private AnnotationConfigWebApplicationContext context;\n    @Mock\n    private EngineInitializer engineInitializer;\n\n    private MockEnvironment env;\n\n    @BeforeEach\n    void setUp() throws Exception {\n        doReturn(engineInitializer).when(listener).getEngineInitializer();\n        env = new MockEnvironment();\n        when(context.getEnvironment()).thenReturn(env);\n        doReturn(context).when(listener).initializeWebApplicationContext(any(), any());\n        doNothing().when(listener).exit(anyInt());\n    }\n\n    @Test\n    void doNotExit() {\n        listener.contextInitialized(new ServletContextEvent(mock(ServletContext.class)));\n\n        verify(listener, never()).exit(anyInt());\n    }\n\n    @Test\n    void exitNormally() {\n        env.setProperty(EngineInitializerListener.UPDATE_ONLY_STARTUP_PROPERTY, Boolean.TRUE.toString());\n        listener.contextInitialized(new ServletContextEvent(mock(ServletContext.class)));\n\n        verify(listener).exit(0);\n    }\n\n    @Test\n    void exitOnError() throws Exception {\n        doThrow(IllegalStateException.class).when(listener).initializeWebApplicationContext(any(), any());\n        listener.contextInitialized(new ServletContextEvent(mock(ServletContext.class)));\n\n        verify(listener).exit(1);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-api/bonita-server-api-http/src/test/java/org/bonitasoft/engine/api/internal/servlet/HttpAPIServletCallTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.internal.servlet;\n\nimport static java.util.Arrays.asList;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.*;\n\nimport java.io.InvalidClassException;\nimport java.lang.reflect.InaccessibleObjectException;\nimport java.time.Instant;\nimport java.util.Date;\nimport java.util.HashMap;\n\nimport com.thoughtworks.xstream.security.ForbiddenClassException;\nimport org.bonitasoft.engine.api.internal.ServerAPI;\nimport org.bonitasoft.engine.api.internal.ServerWrappedException;\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\nimport org.bonitasoft.engine.session.impl.APISessionImpl;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.mockito.junit.MockitoJUnit;\nimport org.mockito.junit.MockitoRule;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockServletContext;\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders;\n\npublic class HttpAPIServletCallTest {\n\n    @Rule\n    public MockitoRule mockitoRule = MockitoJUnit.rule();\n\n    @Test\n    public void should_manage_login_request() throws Exception {\n        //given:\n        MockHttpServletRequest request = MockMvcRequestBuilders\n                .post(\"http://localhost/serverAPI/com.bonitasoft.engine.api.LoginAPI/login\")\n                .param(\"options\", \"<object-stream>\" +\n                        \"  <map/>\" +\n                        \"</object-stream>\")\n                .param(\"classNameParameters\", \"<object-stream>\" +\n                        \"  <java.util.Arrays_-ArrayList>\" +\n                        \"    <a class=\\\"string-array\\\">\" +\n                        \"      <string>java.lang.String</string>\" +\n                        \"      <string>java.lang.String</string>\" +\n                        \"    </a>\" +\n                        \"  </java.util.Arrays_-ArrayList>\" +\n                        \"</object-stream>\")\n                .param(\"parametersValues\", \"<object-stream>\" +\n                        \"  <object-array>\" +\n                        \"    <string>install</string>\" +\n                        \"    <string>install</string>\" +\n                        \"  </object-array>\" +\n                        \"</object-stream>\")\n                .buildRequest(new MockServletContext());\n\n        MockHttpServletResponse response = new MockHttpServletResponse();\n\n        HttpAPIServletCall httpAPIServletCall = spy(new HttpAPIServletCall(request, response));\n        ServerAPI serverAPI = mock(ServerAPI.class);\n        doReturn(serverAPI).when(httpAPIServletCall).getServerAPI();\n        APISessionImpl apiSession = new APISessionImpl(-2241174137745053814L, date(\"2018-06-07T15:10:09.132Z\"),\n                3600000, \"install\", -1);\n        apiSession.setTechnicalUser(true);\n        when(serverAPI.invokeMethod(new HashMap<>(), \"com.bonitasoft.engine.api.LoginAPI\", \"login\",\n                asList(String.class.getName(), String.class.getName()) //\n                , new Object[] { \"install\", \"install\" })) //\n                .thenReturn(apiSession);\n\n        //when:\n        httpAPIServletCall.doPost();\n\n        //then:\n        assertThat(response.getStatus()).as(\"Response status\").isEqualTo(200);\n        assertThat(response.getContentType()).as(\"Response content type\").isEqualTo(\"application/xml;charset=UTF-8\");\n        assertThat(response.getContentAsString()).as(\"Response content\").isEqualToIgnoringWhitespace(\"<object-stream>\" +\n                \"  <org.bonitasoft.engine.session.impl.APISessionImpl>\" +\n                \"    <id>-2241174137745053814</id>\" +\n                \"    <creationDate>2018-06-07 15:10:09.132 UTC</creationDate>\" +\n                \"    <duration>3600000</duration>\" +\n                \"    <userName>install</userName>\" +\n                \"    <userId>-1</userId>\" +\n                \"    <technicalUser>true</technicalUser>\" +\n                \"  </org.bonitasoft.engine.session.impl.APISessionImpl>\" +\n                \"</object-stream>\");\n    }\n\n    @Test\n    public void should_sanitize_error_response_when_xstream_deny_list_blocks_type() throws Exception {\n        // given: a ForbiddenClassException from XStream deny list (simulated via mock to avoid\n        // Java module access issues with XStream's FieldDictionary on Java 17+)\n        MockHttpServletRequest request = MockMvcRequestBuilders\n                .post(\"http://localhost/serverAPI/org.bonitasoft.engine.api.LoginAPI/login\")\n                .param(\"options\", \"<object-stream><map/></object-stream>\")\n                .buildRequest(new MockServletContext());\n\n        MockHttpServletResponse response = new MockHttpServletResponse();\n        HttpAPIServletCall httpAPIServletCall = spy(new HttpAPIServletCall(request, response));\n        ServerAPI serverAPI = mock(ServerAPI.class);\n        doReturn(serverAPI).when(httpAPIServletCall).getServerAPI();\n        ForbiddenClassException forbidden = new ForbiddenClassException(java.net.URL.class);\n        when(serverAPI.invokeMethod(any(), any(), any(), any(), any()))\n                .thenThrow(new BonitaRuntimeException(\"Unable to deserialize object\", forbidden));\n\n        // when:\n        httpAPIServletCall.doPost();\n\n        // then: response should contain a generic error, not security mechanism details\n        // Note: status code assertion omitted — ServletCall.error() flushes the output before\n        // calling setStatus(), so MockHttpServletResponse commits with 200 before the 500 is set.\n        // In production, the real servlet container buffers the response so 500 is effective.\n        String content = response.getContentAsString();\n        assertThat(content).contains(\"Invalid request\");\n        assertThat(content).doesNotContain(\"ForbiddenClassException\");\n        assertThat(content).doesNotContain(\"java.net.URL\");\n        // NoPermission is the XStream security error type name — must not leak to clients\n        assertThat(content).doesNotContain(\"NoPermission\");\n    }\n\n    @Test\n    public void should_return_full_error_response_for_non_security_exceptions() throws Exception {\n        // given: a request that triggers a non-security exception from the server API\n        MockHttpServletRequest request = MockMvcRequestBuilders\n                .post(\"http://localhost/serverAPI/org.bonitasoft.engine.api.LoginAPI/login\")\n                .param(\"options\", \"<object-stream><map/></object-stream>\")\n                .buildRequest(new MockServletContext());\n\n        MockHttpServletResponse response = new MockHttpServletResponse();\n        HttpAPIServletCall httpAPIServletCall = spy(new HttpAPIServletCall(request, response));\n        ServerAPI serverAPI = mock(ServerAPI.class);\n        doReturn(serverAPI).when(httpAPIServletCall).getServerAPI();\n        when(serverAPI.invokeMethod(any(), any(), any(), any(), any()))\n                .thenThrow(new BonitaRuntimeException(\"Something went wrong\"));\n\n        // when:\n        httpAPIServletCall.doPost();\n\n        // then: response should contain the real exception, not the sanitized \"Invalid request\"\n        String content = response.getContentAsString();\n        assertThat(content).contains(\"BonitaRuntimeException\");\n        assertThat(content).contains(\"Something went wrong\");\n        assertThat(content).doesNotContain(\"Invalid request\");\n    }\n\n    @Test\n    public void should_sanitize_error_response_when_jep290_filter_rejects_class() throws Exception {\n        // given: a request where the JEP 290 ObjectInputFilter rejects a class during deserialization\n        MockHttpServletRequest request = MockMvcRequestBuilders\n                .post(\"http://localhost/serverAPI/org.bonitasoft.engine.api.LoginAPI/login\")\n                .param(\"options\", \"<object-stream><map/></object-stream>\")\n                .buildRequest(new MockServletContext());\n\n        MockHttpServletResponse response = new MockHttpServletResponse();\n        HttpAPIServletCall httpAPIServletCall = spy(new HttpAPIServletCall(request, response));\n        ServerAPI serverAPI = mock(ServerAPI.class);\n        doReturn(serverAPI).when(httpAPIServletCall).getServerAPI();\n        InvalidClassException ice = new InvalidClassException(\"com.evil.Payload\", \"filter status: REJECTED\");\n        when(serverAPI.invokeMethod(any(), any(), any(), any(), any()))\n                .thenThrow(new RuntimeException(\"deserialization failed\", ice));\n\n        // when:\n        httpAPIServletCall.doPost();\n\n        // then: response should be sanitized\n        String content = response.getContentAsString();\n        assertThat(content).contains(\"Invalid request\");\n        assertThat(content).doesNotContain(\"com.evil.Payload\");\n        assertThat(content).doesNotContain(\"filter status: REJECTED\");\n    }\n\n    @Test\n    public void should_sanitize_error_response_when_security_exception_is_deeply_nested() throws Exception {\n        // given: ForbiddenClassException wrapped two levels deep in the cause chain\n        MockHttpServletRequest request = MockMvcRequestBuilders\n                .post(\"http://localhost/serverAPI/org.bonitasoft.engine.api.LoginAPI/login\")\n                .param(\"options\", \"<object-stream><map/></object-stream>\")\n                .buildRequest(new MockServletContext());\n\n        MockHttpServletResponse response = new MockHttpServletResponse();\n        HttpAPIServletCall httpAPIServletCall = spy(new HttpAPIServletCall(request, response));\n        ServerAPI serverAPI = mock(ServerAPI.class);\n        doReturn(serverAPI).when(httpAPIServletCall).getServerAPI();\n        ForbiddenClassException forbidden = new ForbiddenClassException(java.net.URL.class);\n        RuntimeException nested = new RuntimeException(\"conversion failed\",\n                new RuntimeException(\"inner wrapper\", forbidden));\n        when(serverAPI.invokeMethod(any(), any(), any(), any(), any())).thenThrow(nested);\n\n        // when:\n        httpAPIServletCall.doPost();\n\n        // then: response should be sanitized despite the deep nesting\n        String content = response.getContentAsString();\n        assertThat(content).contains(\"Invalid request\");\n        assertThat(content).doesNotContain(\"ForbiddenClassException\");\n        assertThat(content).doesNotContain(\"java.net.URL\");\n    }\n\n    @Test\n    public void should_sanitize_error_response_when_security_exception_wrapped_in_ServerWrappedException()\n            throws Exception {\n        // given: a ForbiddenClassException wrapped in ServerWrappedException (production wire-transport path)\n        MockHttpServletRequest request = MockMvcRequestBuilders\n                .post(\"http://localhost/serverAPI/org.bonitasoft.engine.api.LoginAPI/login\")\n                .param(\"options\", \"<object-stream><map/></object-stream>\")\n                .buildRequest(new MockServletContext());\n\n        MockHttpServletResponse response = new MockHttpServletResponse();\n        HttpAPIServletCall httpAPIServletCall = spy(new HttpAPIServletCall(request, response));\n        ServerAPI serverAPI = mock(ServerAPI.class);\n        doReturn(serverAPI).when(httpAPIServletCall).getServerAPI();\n        ForbiddenClassException forbidden = new ForbiddenClassException(java.net.URL.class);\n        when(serverAPI.invokeMethod(any(), any(), any(), any(), any()))\n                .thenThrow(new ServerWrappedException(forbidden));\n\n        // when:\n        httpAPIServletCall.doPost();\n\n        // then: response should be sanitized despite ServerWrappedException wrapping\n        String content = response.getContentAsString();\n        assertThat(content).contains(\"Invalid request\");\n        assertThat(content).doesNotContain(\"ForbiddenClassException\");\n        assertThat(content).doesNotContain(\"java.net.URL\");\n    }\n\n    @Test\n    public void should_not_sanitize_error_response_for_non_jep290_InvalidClassException() throws Exception {\n        // given: an InvalidClassException caused by serialVersionUID mismatch (not a security rejection)\n        MockHttpServletRequest request = MockMvcRequestBuilders\n                .post(\"http://localhost/serverAPI/org.bonitasoft.engine.api.LoginAPI/login\")\n                .param(\"options\", \"<object-stream><map/></object-stream>\")\n                .buildRequest(new MockServletContext());\n\n        MockHttpServletResponse response = new MockHttpServletResponse();\n        HttpAPIServletCall httpAPIServletCall = spy(new HttpAPIServletCall(request, response));\n        ServerAPI serverAPI = mock(ServerAPI.class);\n        doReturn(serverAPI).when(httpAPIServletCall).getServerAPI();\n        InvalidClassException ice = new InvalidClassException(\"com.example.MyClass\",\n                \"local class incompatible: stream classdesc serialVersionUID = 123, local class serialVersionUID = 456\");\n        when(serverAPI.invokeMethod(any(), any(), any(), any(), any()))\n                .thenThrow(new RuntimeException(\"deserialization failed\", ice));\n\n        // when:\n        try {\n            httpAPIServletCall.doPost();\n        } catch (InaccessibleObjectException e) {\n            // On Java 17+, XStream fails to serialize IOException-derived exceptions\n            // due to module access restrictions (same issue documented in toResponse()).\n            // This is expected and not what we're testing.\n        }\n\n        // then: response should NOT contain the sanitized \"Invalid request\" message,\n        // proving isDeserializationSecurityException correctly returned false\n        String content = response.getContentAsString();\n        assertThat(content).doesNotContain(\"Invalid request\");\n    }\n\n    @Test\n    public void should_write_empty_body_when_output_object_is_null() throws Exception {\n        // BPA-443 defence-in-depth: output(Object) is the sibling of output(String) and\n        // carries the same null hazard — object.toString() NPEs if object is null. Same guard.\n        // given:\n        MockHttpServletRequest request = MockMvcRequestBuilders\n                .post(\"http://localhost/serverAPI/org.bonitasoft.engine.api.LoginAPI/logout\")\n                .buildRequest(new MockServletContext());\n        MockHttpServletResponse response = new MockHttpServletResponse();\n        HttpAPIServletCall httpAPIServletCall = new HttpAPIServletCall(request, response);\n\n        // when:\n        httpAPIServletCall.output((Object) null);\n\n        // then:\n        assertThat(response.getContentAsString())\n                .as(\"Null Object must produce empty body, not NPE or literal \\\"null\\\"\")\n                .isEmpty();\n    }\n\n    @Test\n    public void should_write_empty_body_when_server_api_returns_null_for_void_method() throws Exception {\n        // BPA-443: when a void API method is invoked (e.g. logout, retryTask), the server-side\n        // invokeMethod returns null. Writing \"null\" via PrintWriter.print(null) yields an NPE\n        // inside response wrappers that track content length (e.g. Spring Session's\n        // SaveContextPrintWriter). Guard against this at the source: null → empty body.\n        // given:\n        MockHttpServletRequest request = MockMvcRequestBuilders\n                .post(\"http://localhost/serverAPI/com.bonitasoft.engine.api.LoginAPI/logout\")\n                .param(\"options\", \"<object-stream><map/></object-stream>\")\n                .buildRequest(new MockServletContext());\n\n        MockHttpServletResponse response = new MockHttpServletResponse();\n\n        HttpAPIServletCall httpAPIServletCall = spy(new HttpAPIServletCall(request, response));\n        ServerAPI serverAPI = mock(ServerAPI.class);\n        doReturn(serverAPI).when(httpAPIServletCall).getServerAPI();\n        when(serverAPI.invokeMethod(any(), any(), any(), any(), any())).thenReturn(null);\n\n        // when:\n        httpAPIServletCall.doPost();\n\n        // then:\n        assertThat(response.getStatus()).as(\"Response status\").isEqualTo(200);\n        assertThat(response.getContentAsString())\n                .as(\"Response body for void method must be empty, not the literal string \\\"null\\\"\")\n                .isEmpty();\n    }\n\n    // =================================================================================================================\n    // UTILS\n    // =================================================================================================================\n\n    private static Date date(String date) {\n        return Date.from(Instant.parse(date));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-api/bonita-server-api-http/src/test/java/org/bonitasoft/engine/api/internal/servlet/HttpAPIServletTest.java",
    "content": "/**\n * Copyright (C) 2021 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.internal.servlet;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.EnvironmentVariables;\nimport org.junit.contrib.java.lang.system.RestoreSystemProperties;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnit;\nimport org.mockito.junit.MockitoRule;\n\npublic class HttpAPIServletTest {\n\n    @Rule\n    public MockitoRule mockitoRule = MockitoJUnit.rule();\n\n    @Mock\n    HttpServletRequest request;\n    @Mock\n    HttpServletResponse response;\n\n    @Rule\n    public EnvironmentVariables envVar = new EnvironmentVariables();\n    @Rule\n    public RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties();\n\n    @Test\n    public void should_send_403_when_disabled_using_env() throws Exception {\n        HttpAPIServlet httpAPIServlet = spy(new HttpAPIServlet());\n        envVar.set(\"HTTP_API\", \"false\");\n\n        httpAPIServlet.init();\n        httpAPIServlet.doPost(request, response);\n\n        verify(response).sendError(403);\n    }\n\n    @Test\n    public void should_send_403_when_disabled_using_props() throws Exception {\n        System.setProperty(\"http.api\", \"false\");\n        HttpAPIServlet httpAPIServlet = spy(new HttpAPIServlet());\n\n        httpAPIServlet.init();\n        httpAPIServlet.doPost(request, response);\n\n        verify(response).sendError(403);\n    }\n\n    @Test\n    public void should_not_send_403_when_enabled_using_props() throws Exception {\n        System.setProperty(\"http.api\", \"true\");\n        HttpAPIServlet httpAPIServlet = spy(new HttpAPIServlet());\n        doNothing().when(httpAPIServlet).callHttpApi(any(), any());\n\n        httpAPIServlet.init();\n        httpAPIServlet.doPost(request, response);\n\n        verify(response, never()).sendError(anyInt());\n    }\n\n    @Test\n    public void should_be_enabled_by_default() throws Exception {\n        HttpAPIServlet httpAPIServlet = spy(new HttpAPIServlet());\n        doNothing().when(httpAPIServlet).callHttpApi(any(), any());\n\n        httpAPIServlet.init();\n        httpAPIServlet.doPost(request, response);\n\n        verify(response, never()).sendError(anyInt());\n    }\n\n    @Test\n    public void should_send_403_when_dispatch_type_is_forward() throws Exception {\n        HttpAPIServlet httpAPIServlet = spy(new HttpAPIServlet());\n        doReturn(javax.servlet.DispatcherType.FORWARD).when(request).getDispatcherType();\n\n        httpAPIServlet.init();\n        httpAPIServlet.doPost(request, response);\n\n        verify(response).sendError(403);\n        verify(httpAPIServlet, never()).callHttpApi(any(), any());\n    }\n\n    @Test\n    public void should_allow_request_dispatch_type() throws Exception {\n        HttpAPIServlet httpAPIServlet = spy(new HttpAPIServlet());\n        doReturn(javax.servlet.DispatcherType.REQUEST).when(request).getDispatcherType();\n        doNothing().when(httpAPIServlet).callHttpApi(any(), any());\n\n        httpAPIServlet.init();\n        httpAPIServlet.doPost(request, response);\n\n        verify(response, never()).sendError(anyInt());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-api/bonita-server-api-http/src/test/java/org/bonitasoft/engine/api/internal/servlet/ServletCallDeserializationFilterTest.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.internal.servlet;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.InvalidClassException;\nimport java.io.ObjectOutputStream;\nimport java.net.URL;\nimport java.util.HashMap;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\n\npublic class ServletCallDeserializationFilterTest {\n\n    private ServletCall servletCall;\n\n    @Before\n    public void setUp() throws Exception {\n        MockHttpServletRequest request = new MockHttpServletRequest();\n        MockHttpServletResponse response = new MockHttpServletResponse();\n        servletCall = new HttpAPIServletCall(request, response);\n    }\n\n    @Test\n    public void should_deserialize_byte_array() throws Exception {\n        byte[] original = new byte[] { 1, 2, 3, 4, 5 };\n        byte[] serialized = serializeObject(original);\n\n        Object result = servletCall.deserialize(serialized);\n\n        assertThat(result).isInstanceOf(byte[].class);\n        assertThat((byte[]) result).isEqualTo(original);\n    }\n\n    @Test\n    public void should_deserialize_java_util_types() throws Exception {\n        HashMap<String, String> original = new HashMap<>();\n        original.put(\"key\", \"value\");\n        byte[] serialized = serializeObject(original);\n\n        Object result = servletCall.deserialize(serialized);\n\n        assertThat(result).isInstanceOf(HashMap.class);\n        assertThat(((HashMap<?, ?>) result).get(\"key\")).isEqualTo(\"value\");\n    }\n\n    @Test\n    public void should_deserialize_bonita_engine_types() throws Exception {\n        org.bonitasoft.engine.exception.CreationException original = new org.bonitasoft.engine.exception.CreationException(\n                \"test error\");\n        byte[] serialized = serializeObject(original);\n\n        Object result = servletCall.deserialize(serialized);\n\n        assertThat(result).isInstanceOf(org.bonitasoft.engine.exception.CreationException.class);\n        assertThat(((Exception) result).getMessage()).isEqualTo(\"test error\");\n    }\n\n    @Test\n    public void should_reject_java_net_URL() throws Exception {\n        HashMap<String, Object> malicious = new HashMap<>();\n        malicious.put(\"url\", new URL(\"http://example.com\"));\n        byte[] serialized = serializeObject(malicious);\n\n        assertThatThrownBy(() -> servletCall.deserialize(serialized))\n                .isInstanceOf(InvalidClassException.class)\n                .hasMessageContaining(\"REJECTED\");\n    }\n\n    @Test\n    public void should_reject_java_net_InetAddress() throws Exception {\n        java.net.InetAddress malicious = java.net.InetAddress.getByName(\"127.0.0.1\");\n        byte[] serialized = serializeObject(malicious);\n\n        assertThatThrownBy(() -> servletCall.deserialize(serialized))\n                .isInstanceOf(InvalidClassException.class)\n                .hasMessageContaining(\"REJECTED\");\n    }\n\n    private static byte[] serializeObject(Object obj) throws Exception {\n        ByteArrayOutputStream bos = new ByteArrayOutputStream();\n        try (ObjectOutputStream oos = new ObjectOutputStream(bos)) {\n            oos.writeObject(obj);\n        }\n        return bos.toByteArray();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-api/bonita-server-api-http/src/test/java/org/bonitasoft/engine/api/internal/servlet/impl/XmlConverterTest.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.internal.servlet.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.catchThrowable;\n\nimport com.thoughtworks.xstream.security.ForbiddenClassException;\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\nimport org.junit.After;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.RestoreSystemProperties;\n\npublic class XmlConverterTest {\n\n    @Rule\n    public RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties();\n\n    private final XmlConverter xmlConverter = new XmlConverter();\n\n    @After\n    public void resetXStream() {\n        XmlConverter.reset();\n    }\n\n    @Test\n    public void should_reject_deserialization_of_gadget_chain_types() {\n        // given: XML payload referencing a known gadget chain type\n        String maliciousXml = \"<root>\"\n                + \"<org.apache.commons.collections4.comparators.TransformingComparator>\"\n                + \"<decorated/><transformer/>\"\n                + \"</org.apache.commons.collections4.comparators.TransformingComparator>\"\n                + \"</root>\";\n\n        // when:\n        Throwable thrown = catchThrowable(() -> xmlConverter.fromXML(maliciousXml));\n\n        // then:\n        assertThat(thrown)\n                .isInstanceOf(BonitaRuntimeException.class)\n                .hasCauseInstanceOf(ForbiddenClassException.class);\n    }\n\n    @Test\n    public void should_reject_deserialization_of_javax_script_gadget_chain_types() {\n        // given: XML payload referencing a denied gadget chain type\n        String maliciousXml = \"<root>\"\n                + \"<javax.script.ScriptEngineManager/>\"\n                + \"</root>\";\n\n        // when:\n        Throwable thrown = catchThrowable(() -> xmlConverter.fromXML(maliciousXml));\n\n        // then:\n        assertThat(thrown)\n                .isInstanceOf(BonitaRuntimeException.class)\n                .hasCauseInstanceOf(ForbiddenClassException.class);\n    }\n\n    @Test\n    public void should_allow_deserialization_of_bonita_types() {\n        // given: XML payload with a legitimate Bonita type\n        String xml = \"<root>\"\n                + \"<org.bonitasoft.engine.bpm.actor.impl.ActorDefinitionImpl>\"\n                + \"<name>test</name>\"\n                + \"</org.bonitasoft.engine.bpm.actor.impl.ActorDefinitionImpl>\"\n                + \"</root>\";\n\n        // when:\n        Object result = xmlConverter.fromXML(xml);\n\n        // then:\n        assertThat(result).isNotNull();\n    }\n\n    @Test\n    public void should_reject_deserialization_of_java_net_URL() {\n        // given: XML payload containing a java.net.URL element (URLDNS gadget chain)\n        String maliciousXml = \"<root>\"\n                + \"<java.net.URL>\"\n                + \"<protocol>http</protocol>\"\n                + \"<host>attacker.example.com</host>\"\n                + \"<port>80</port>\"\n                + \"<file>/</file>\"\n                + \"</java.net.URL>\"\n                + \"</root>\";\n\n        // when:\n        Throwable thrown = catchThrowable(() -> xmlConverter.fromXML(maliciousXml));\n\n        // then:\n        assertThat(thrown)\n                .isInstanceOf(BonitaRuntimeException.class)\n                .hasCauseInstanceOf(ForbiddenClassException.class);\n    }\n\n    @Test\n    public void should_use_custom_deny_list_from_system_property() {\n        // given: custom deny list via system property\n        XmlConverter.reset();\n        System.setProperty(\"bonita.xstream.deny.packages\", \"java.awt.**\");\n\n        // when: attempting to deserialize a type matching the custom deny pattern\n        Throwable thrown = catchThrowable(() -> new XmlConverter().fromXML(\"<root><java.awt.Color/></root>\"));\n\n        // then:\n        assertThat(thrown)\n                .isInstanceOf(BonitaRuntimeException.class)\n                .hasCauseInstanceOf(ForbiddenClassException.class);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-client/build.gradle",
    "content": "import org.bonitasoft.engine.gradle.PomUtils\n\nplugins {\n    id \"java-library\"\n    id \"bonita-tests\"\n}\n\ndependencies {\n    api project(':bpm:bonita-common')\n    api libs.httpComponentsClient\n    api libs.xstream\n    api libs.httpComponentsMime\n    testImplementation libs.assertj\n    testImplementation libs.logback\n    testImplementation libs.mockitoCore\n    testImplementation libs.jettyServer\n    testImplementation libs.jettySecurity\n}\n\ntasks.register(\"testsJar\", Jar) {\n    archiveClassifier = 'tests'\n    from(sourceSets.test.output)\n}\n\ntasks.register(\"sourcesJar\", Jar) {\n    from sourceSets.main.allJava\n    archiveClassifier = 'sources'\n}\n\ntasks.register(\"javadocJar\", Jar) {\n    from javadoc\n    archiveClassifier = 'javadoc'\n}\n\npublishing {\n    publications {\n        mavenJava(MavenPublication) {\n            from project.components.java\n            artifact project.sourcesJar\n            artifact project.javadocJar\n            artifact project.testsJar\n            pom { pom ->\n                name = \"Bonita Client\"\n                description = \"Bonita Client is the Jar used to interact with a running Bonita Engine\"\n                PomUtils.pomCommunityPublication(pom)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-client/src/main/java/org/bonitasoft/engine/api/APIClient.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\nimport java.io.IOException;\nimport java.io.Serializable;\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Proxy;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.impl.ClientInterceptor;\nimport org.bonitasoft.engine.api.impl.LocalServerAPIFactory;\nimport org.bonitasoft.engine.api.internal.ServerAPI;\nimport org.bonitasoft.engine.api.platform.PlatformInformationAPI;\nimport org.bonitasoft.engine.bdm.BusinessObjectDaoCreationException;\nimport org.bonitasoft.engine.bdm.dao.BusinessObjectDAO;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.platform.LoginException;\nimport org.bonitasoft.engine.platform.LogoutException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.SessionNotFoundException;\nimport org.bonitasoft.engine.util.APITypeManager;\n\n/**\n * Bonita <b>Community</b> Edition APIs client.<br>\n * <p/>\n * <ul>\n * <li>{@link IdentityAPI},</li>\n * <li>{@link ProcessAPI},</li>\n * <li>{@link CommandAPI},</li>\n * <li>{@link ProfileAPI},</li>\n * <li>{@link TenantAdministrationAPI},</li>\n * <li>{@link PageAPI},</li>\n * <li>{@link ApplicationAPI},</li>\n * <li>{@link PermissionAPI},</li>\n * <li>{@link BusinessDataAPI} (deprecated as of 7.3),</li>\n * <li>{@link MaintenanceAPI},</li>\n * </ul>\n *\n * @author Nicolas Chabanoles\n */\npublic class APIClient {\n\n    private static final String IMPL_SUFFIX = \"Impl\";\n\n    protected APISession session;\n\n    public APIClient() {\n        session = null;\n    }\n\n    public APIClient(APISession session) {\n        this.session = session;\n    }\n\n    public APISession getSession() {\n        return session;\n    }\n\n    ServerAPI getServerAPI() throws ServerAPIException, UnknownAPITypeException {\n        try {\n            final ApiAccessType apiType = APITypeManager.getAPIType();\n            Map<String, String> parameters;\n            return switch (apiType) {\n                case LOCAL -> LocalServerAPIFactory.getServerAPI();\n                case HTTP -> {\n                    parameters = APITypeManager.getAPITypeParameters();\n                    yield new HTTPServerAPI(parameters);\n                }\n            };\n        } catch (IOException e) {\n            throw new ServerAPIException(e);\n        }\n    }\n\n    protected <T> T getAPI(final Class<T> apiClass) {\n        ensureSessionExists();\n        try {\n            final ClientInterceptor clientInterceptor = new ClientInterceptor(apiClass.getName(), getServerAPI(),\n                    session);\n            @SuppressWarnings(\"unchecked\")\n            final T api = (T) Proxy.newProxyInstance(APIAccessor.class.getClassLoader(), new Class[] { apiClass },\n                    clientInterceptor);\n            return api;\n        } catch (ServerAPIException | UnknownAPITypeException e) {\n            throw new IllegalStateException(e.getMessage(), e);\n        }\n    }\n\n    private void ensureSessionExists() {\n        if (session == null) {\n            throw new IllegalStateException(\"You must call login() prior to accessing any API.\");\n        }\n    }\n\n    protected LoginAPI getLoginAPI() {\n        return getLoginAPI(LoginAPI.class);\n    }\n\n    /**\n     * This method serves the purpose to remove confusion between getAPI() when a session is mandatory, and this one,\n     * where no session is needed to access the\n     * API class.\n     *\n     * @param apiClass the API to retrieve\n     * @param <T> The type of the API, extending {@link org.bonitasoft.engine.api.LoginAPI}\n     * @return the retrieved API\n     * @throws IllegalStateException if the API cannot be retrieved.\n     */\n    protected <T extends LoginAPI> T getLoginAPI(Class<T> apiClass) {\n        try {\n            final ClientInterceptor interceptor = new ClientInterceptor(apiClass.getName(), getServerAPI());\n            @SuppressWarnings(\"unchecked\")\n            final T api = (T) Proxy.newProxyInstance(APIAccessor.class.getClassLoader(), new Class[] { apiClass },\n                    interceptor);\n            return api;\n        } catch (ServerAPIException | UnknownAPITypeException e) {\n            throw new IllegalStateException(e.getMessage(), e);\n        }\n    }\n\n    /**\n     * Connects a user, identified by his (her) username and password, in order to use API methods of a tenant.\n     *\n     * @param username the user name\n     * @param password the password\n     * @throws LoginException occurs when an exception is thrown during the login (userName does not exist, or couple\n     *         (userName,\n     *         password) is incorrect)\n     * @since 7.2\n     */\n    public void login(String username, String password) throws LoginException {\n        session = getLoginAPI(LoginAPI.class).login(username, password);\n    }\n\n    /**\n     * Connects a user, identified by credentials, in order to use API methods of a tenant.\n     *\n     * @param credentials the credentials to login with. Can be username / password couple, SSO ticket, ... depending on\n     *        the implementation.<br>\n     *        By default possible map keys are:\n     *        <ul>\n     *        <li>Basic Authentication: authentication.username and authentication.password</li>\n     *        <li>CAS Authentication: ticket and service</li>\n     *        <li>Please refer to specific documentation regarding the Authentication Service in use, to know what the\n     *        credentials must contain.</li>\n     *        </ul>\n     * @throws LoginException occurs when an exception is thrown during the login (userName does not exist, or couple\n     *         (userName,\n     *         password) is incorrect)\n     * @since 10.3\n     */\n    public void login(final Map<String, Serializable> credentials) throws LoginException {\n        session = getLoginAPI(LoginAPI.class).login(credentials);\n    }\n\n    /**\n     * Disconnect user from tenant APIs.\n     *\n     * @since 7.2\n     */\n    public void logout() throws LogoutException {\n        try {\n            if (session != null) {\n                getLoginAPI().logout(session);\n                session = null;\n            }\n        } catch (SessionNotFoundException ignored) {\n            // If the session is not found on server, then the client is already logged out.\n            // Do nothing\n        }\n    }\n\n    /**\n     * Get an implementation instance of the DAO Interface.\n     *\n     * @param daoInterface the interface of the DAO\n     * @return the implementation of the DAO\n     * @throws BusinessObjectDaoCreationException if the factory is not able to instantiate the DAO\n     */\n    public <T extends BusinessObjectDAO> T getDAO(final Class<T> daoInterface)\n            throws BusinessObjectDaoCreationException {\n        ensureSessionExists();\n        if (daoInterface == null) {\n            throw new IllegalArgumentException(\"daoInterface is null\");\n        }\n        if (!daoInterface.isInterface()) {\n            throw new IllegalArgumentException(daoInterface.getName() + \" is not an interface\");\n        }\n\n        Class<T> daoImplClass;\n        try {\n            daoImplClass = loadClass(daoInterface);\n\n            if (daoImplClass != null) {\n                final Constructor<T> constructor = daoImplClass.getConstructor(APISession.class);\n                return constructor.newInstance(session);\n            }\n        } catch (final ClassNotFoundException | SecurityException | NoSuchMethodException | IllegalArgumentException\n                | InstantiationException\n                | IllegalAccessException | InvocationTargetException e) {\n            throw new BusinessObjectDaoCreationException(e);\n        }\n        throw new BusinessObjectDaoCreationException(\"No Implementation of the DAO available.\");\n    }\n\n    /**\n     * Loads the class of the {@link BusinessObjectDAO} according to its class name.\n     * <p>\n     * The loading is done in the current Thread ClassLoader.\n     *\n     * @param daoInterface the DAO's interface\n     * @return the Implementation class of the BusinessObjectDAO\n     * @throws ClassNotFoundException if the implementation class name is unknown by the current Thread ClassLoader\n     */\n    @SuppressWarnings(\"unchecked\")\n    protected <T extends BusinessObjectDAO> Class<T> loadClass(final Class<T> daoInterface)\n            throws ClassNotFoundException {\n        final String implementationClassName = daoInterface.getName() + IMPL_SUFFIX;\n        return (Class<T>) Class.forName(implementationClassName, true, Thread.currentThread().getContextClassLoader());\n    }\n\n    /**\n     * Get API to manage the organization, i.e., users, groups and roles.\n     *\n     * @since 7.2\n     */\n    public IdentityAPI getIdentityAPI() {\n        return getAPI(IdentityAPI.class);\n    }\n\n    /**\n     * Get API to manage the business processes.\n     *\n     * @since 7.2\n     */\n    public ProcessAPI getProcessAPI() {\n        return getAPI(ProcessAPI.class);\n    }\n\n    /**\n     * Get API to manage commands and Tenant level dependencies.\n     *\n     * @since 7.2\n     */\n    public CommandAPI getCommandAPI() {\n        return getAPI(CommandAPI.class);\n    }\n\n    /**\n     * Get API to manage portal user profiles.\n     *\n     * @since 7.2\n     */\n    public ProfileAPI getProfileAPI() {\n        return getAPI(ProfileAPI.class);\n    }\n\n    /**\n     * Get API to manage the tenant your are logged on.\n     *\n     * @since 7.2\n     */\n    public TenantAdministrationAPI getTenantAdministrationAPI() {\n        return getAPI(TenantAdministrationAPI.class);\n    }\n\n    /**\n     * Get API to manage portal pages.\n     *\n     * @since 7.2\n     */\n    public PageAPI getCustomPageAPI() {\n        return getAPI(PageAPI.class);\n    }\n\n    /**\n     * Get API to manage Living Applications.\n     *\n     * @since 7.2\n     */\n    public ApplicationAPI getLivingApplicationAPI() {\n        return getAPI(ApplicationAPI.class);\n    }\n\n    /**\n     * Get API to dynamically check REST API call access right.\n     *\n     * @since 7.2\n     */\n    public PermissionAPI getPermissionAPI() {\n        return getAPI(PermissionAPI.class);\n    }\n\n    /**\n     * Get API to access Business Data related to processes.\n     *\n     * @since 7.2\n     * @deprecated as of 7.3, see {@link BusinessDataAPI} for replacements\n     */\n    @Deprecated(since = \"7.3\")\n    public BusinessDataAPI getBusinessDataAPI() {\n        return getAPI(BusinessDataAPI.class);\n    }\n\n    /**\n     * Get API to manage Bonita Applications.\n     *\n     * @since 7.10\n     */\n    public ApplicationAPI getApplicationAPI() {\n        return getAPI(ApplicationAPI.class);\n    }\n\n    /**\n     * Get API to store and retrieve temporary content like uploaded files.\n     * For internal usage only.\n     *\n     * @since 9.0\n     */\n    @Internal\n    public TemporaryContentAPI getTemporaryContentAPI() {\n        return getAPI(TemporaryContentAPI.class);\n    }\n\n    /**\n     * Get API to manage Platform maintenance.\n     *\n     * @since 9.0\n     */\n    public MaintenanceAPI getMaintenanceAPI() {\n        return getAPI(MaintenanceAPI.class);\n    }\n\n    /**\n     * Get API that retrieves information on the platform: basic information in a Community edition,\n     * More detailed information on license in a Subscription edition.\n     *\n     * @since 10.2.0\n     */\n    public PlatformInformationAPI getPlatformInformationAPI() {\n        return getAPI(PlatformInformationAPI.class);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-client/src/main/java/org/bonitasoft/engine/api/ApiAccessType.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\n/**\n * Enum that defines how APIs should be accessed\n */\npublic enum ApiAccessType {\n    /**\n     * Local access to the api, it means that the local JVM is running the engine.\n     */\n    LOCAL,\n\n    /**\n     * Access a remote engine using through a servlet. The remote engine should have the HTTP_API env property set to\n     * true.\n     */\n    HTTP\n}\n"
  },
  {
    "path": "bpm/bonita-client/src/main/java/org/bonitasoft/engine/api/BonitaStackTraceElementConverter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\nimport com.thoughtworks.xstream.converters.ConversionException;\nimport com.thoughtworks.xstream.converters.extended.StackTraceElementConverter;\nimport com.thoughtworks.xstream.converters.extended.StackTraceElementFactory;\n\n/**\n * Override default stack trace element parser to avoid having big exception when there is parsing issues\n *\n * @author Baptiste Mesta\n */\npublic class BonitaStackTraceElementConverter extends StackTraceElementConverter {\n\n    private static final StackTraceElementFactory FACTORY = new StackTraceElementFactory();\n\n    @Override\n    public Object fromString(final String str) {\n        try {\n            return super.fromString(str);\n        } catch (ConversionException e) {\n            return FACTORY.element(str, \" \", \" \", -3);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-client/src/main/java/org/bonitasoft/engine/api/HTTPServerAPI.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\nimport static java.lang.String.format;\nimport static java.nio.charset.StandardCharsets.UTF_8;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.ObjectOutputStream;\nimport java.io.Serializable;\nimport java.lang.reflect.UndeclaredThrowableException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.http.HttpEntity;\nimport org.apache.http.NameValuePair;\nimport org.apache.http.client.ClientProtocolException;\nimport org.apache.http.client.HttpClient;\nimport org.apache.http.client.HttpResponseException;\nimport org.apache.http.client.ResponseHandler;\nimport org.apache.http.client.entity.UrlEncodedFormEntity;\nimport org.apache.http.client.methods.HttpPost;\nimport org.apache.http.entity.ContentType;\nimport org.apache.http.entity.mime.MultipartEntityBuilder;\nimport org.apache.http.entity.mime.content.ByteArrayBody;\nimport org.apache.http.entity.mime.content.StringBody;\nimport org.apache.http.impl.client.BasicResponseHandler;\nimport org.apache.http.impl.client.HttpClientBuilder;\nimport org.apache.http.message.BasicNameValuePair;\nimport org.bonitasoft.engine.api.impl.XmlConverter;\nimport org.bonitasoft.engine.api.internal.ServerAPI;\nimport org.bonitasoft.engine.api.internal.ServerWrappedException;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.digest.DigestUtils;\nimport org.bonitasoft.engine.exception.StackTraceTransformer;\n\n/**\n * Call the remote engine using HTTP post\n * That class serialize api call parameters in a XML body and post it to the serverAPI servlet like this\n * /serverAPI/[api interface name]/[method name]\n */\npublic class HTTPServerAPI implements ServerAPI {\n\n    private static final long serialVersionUID = -3375874140999200702L;\n\n    private static final ContentType XML_UTF_8 = ContentType.create(\"application/xml\", UTF_8);\n\n    private static final String CLASS_NAME_PARAMETERS = \"classNameParameters\";\n\n    private static final String OPTIONS = \"options\";\n\n    private static final String PARAMETERS_VALUES = \"parametersValues\";\n\n    private static final String BINARY_PARAMETER = \"binaryParameter\";\n\n    private static final String BYTE_ARRAY = \"==ByteArray==\";\n\n    private static final char SLASH = '/';\n\n    private static final String SERVER_API = \"/serverAPI/\";\n\n    // package-private for testing purpose\n    static final String SERVER_URL = \"server.url\";\n\n    private static final String BASIC_AUTHENTICATION_ACTIVE = \"basicAuthentication.active\";\n\n    private static final String BASIC_AUTHENTICATION_USERNAME = \"basicAuthentication.username\";\n\n    private static final String BASIC_AUTHENTICATION_PASSWORD = \"basicAuthentication.password\";\n\n    // package-private for testing purpose\n    static final String APPLICATION_NAME = \"application.name\";\n    static final String CONNECTIONS_MAX = \"connections.max\";\n\n    private final String serverUrl;\n\n    private final String applicationName;\n\n    private final boolean basicAuthenticationActive;\n\n    private final String basicAuthenticationUserName;\n\n    private final String basicAuthenticationPassword;\n\n    private static HttpClient httpclient;\n\n    private static final ResponseHandler<String> RESPONSE_HANDLER = new BasicResponseHandler();\n\n    private final XmlConverter xmlConverter;\n\n    public HTTPServerAPI(final Map<String, String> parameters) {\n        xmlConverter = new XmlConverter();\n        // initialize httpclient in the constructor to avoid incompatibility when running tests:\n        // java.security.NoSuchAlgorithmException: class configured for SSLContext: sun.security.ssl.SSLContextImpl$TLS10Context not a SSLContext\n        if (httpclient == null) {\n            httpclient = createHttpClient(parameters);\n        }\n        serverUrl = parameters.get(SERVER_URL);\n        applicationName = parameters.get(APPLICATION_NAME);\n        basicAuthenticationActive = \"true\".equalsIgnoreCase(parameters.get(BASIC_AUTHENTICATION_ACTIVE));\n        basicAuthenticationUserName = parameters.get(BASIC_AUTHENTICATION_USERNAME);\n        basicAuthenticationPassword = parameters.get(BASIC_AUTHENTICATION_PASSWORD);\n    }\n\n    private HttpClient createHttpClient(final Map<String, String> parameters) {\n        HttpClientBuilder builder = HttpClientBuilder.create();\n        try {\n            int connectionsMax = Integer.parseInt(parameters.getOrDefault(CONNECTIONS_MAX, \"20\"));\n            // we have only one route, use same number for connection max and max per route\n            builder.setMaxConnPerRoute(connectionsMax);\n            builder.setMaxConnTotal(connectionsMax);\n        } catch (NumberFormatException e) {\n            throw new IllegalArgumentException(\n                    \"Client connection pool size '\" + CONNECTIONS_MAX + \"' must be set to a number\");\n        }\n        return builder.build();\n    }\n\n    @Override\n    public Object invokeMethod(final Map<String, Serializable> options, final String apiInterfaceName,\n            final String methodName,\n            final List<String> classNameParameters, final Object[] parametersValues) throws ServerWrappedException {\n        String response = null;\n        try {\n            response = executeHttpPost(options, apiInterfaceName, methodName, classNameParameters, parametersValues);\n            return checkInvokeMethodReturn(response);\n        } catch (final UndeclaredThrowableException e) {\n            throw new ServerWrappedException(e);\n        } catch (final Throwable e) {\n            final StackTraceElement[] stackTrace = new Exception().getStackTrace();\n            StackTraceTransformer.addStackTo(e, stackTrace);\n            throw new ServerWrappedException(e.getMessage() + \" / response: \" + response, e);\n        }\n    }\n\n    // package-private for testing purpose\n    Object checkInvokeMethodReturn(final String response) throws Throwable {\n        Object invokeMethodReturn = null;\n        if (response != null && !response.isEmpty() && !\"null\".equals(response)) {\n            invokeMethodReturn = xmlConverter.fromXML(response);\n            if (invokeMethodReturn instanceof Throwable) {\n                throw (Throwable) invokeMethodReturn;\n            }\n        }\n        return invokeMethodReturn;\n    }\n\n    // package-private for testing purpose\n    String executeHttpPost(final Map<String, Serializable> options, final String apiInterfaceName,\n            final String methodName,\n            final List<String> classNameParameters, final Object[] parametersValues) throws IOException {\n        final HttpPost httpost = createHttpPost(options, apiInterfaceName, methodName, classNameParameters,\n                parametersValues);\n        try {\n            return httpclient.execute(httpost, RESPONSE_HANDLER);\n        } catch (final ClientProtocolException e) {\n            String httpCodeMessage = \"\";\n            // required as the http code is not included in the exception message\n            if (e instanceof HttpResponseException) {\n                final int statusCode = ((HttpResponseException) e).getStatusCode();\n                httpCodeMessage = format(\" (http code: %s)\", statusCode);\n            }\n            throw new IOException(\"Error while executing POST request\" + httpCodeMessage + \" <\" + httpost + \">\", e);\n        }\n    }\n\n    private final HttpPost createHttpPost(final Map<String, Serializable> options, final String apiInterfaceName,\n            final String methodName,\n            final List<String> classNameParameters, final Object[] parametersValues) throws IOException {\n        final HttpEntity httpEntity = buildEntity(options, classNameParameters, parametersValues);\n        final StringBuilder sBuilder = new StringBuilder(serverUrl);\n        sBuilder.append(SLASH).append(applicationName).append(SERVER_API).append(apiInterfaceName).append(SLASH)\n                .append(methodName);\n        final HttpPost httpPost = new HttpPost(sBuilder.toString());\n        httpPost.setEntity(httpEntity);\n\n        // Basic authentication\n        if (basicAuthenticationActive) {\n            final StringBuilder credentials = new StringBuilder();\n            credentials.append(basicAuthenticationUserName).append(\":\").append(basicAuthenticationPassword);\n            final String encodedCredentials = DigestUtils\n                    .encodeBase64AsUtf8String(credentials.toString().getBytes(UTF_8));\n            httpPost.setHeader(\"Authorization\", \"Basic \" + encodedCredentials);\n        }\n\n        return httpPost;\n    }\n\n    // package-private for testing purpose\n    final HttpEntity buildEntity(final Map<String, Serializable> options, final List<String> classNameParameters,\n            final Object[] parametersValues) throws IOException {\n        final HttpEntity httpEntity;\n        // if we have a business archive we use multipart to have the business archive attached as a binary content (it can be big)\n        if (classNameParameters.contains(BusinessArchive.class.getName())\n                || classNameParameters.contains(byte[].class.getName())) {\n            final List<Object> bytearrayParameters = new ArrayList<>();\n\n            MultipartEntityBuilder entityBuilder = MultipartEntityBuilder.create().setBoundary(null).setCharset(UTF_8);\n            entityBuilder.addPart(OPTIONS, new StringBody(xmlConverter.toXML(options), XML_UTF_8));\n            entityBuilder.addPart(CLASS_NAME_PARAMETERS,\n                    new StringBody(xmlConverter.toXML(classNameParameters), XML_UTF_8));\n            for (int i = 0; i < parametersValues.length; i++) {\n                final Object parameterValue = parametersValues[i];\n                if (parameterValue instanceof BusinessArchive || parameterValue instanceof byte[]) {\n                    parametersValues[i] = BYTE_ARRAY;\n                    bytearrayParameters.add(parameterValue);\n                }\n            }\n            entityBuilder.addPart(PARAMETERS_VALUES, new StringBody(xmlConverter.toXML(parametersValues), XML_UTF_8));\n            int i = 0;\n            for (final Object object : bytearrayParameters) {\n                entityBuilder.addPart(BINARY_PARAMETER + i, new ByteArrayBody(serialize(object), BINARY_PARAMETER + i));\n                i++;\n            }\n            httpEntity = entityBuilder.build();\n        } else {\n            final List<NameValuePair> nvps = new ArrayList<>();\n            nvps.add(new BasicNameValuePair(OPTIONS, xmlConverter.toXML(options)));\n            nvps.add(new BasicNameValuePair(CLASS_NAME_PARAMETERS, xmlConverter.toXML(classNameParameters)));\n            nvps.add(new BasicNameValuePair(PARAMETERS_VALUES, xmlConverter.toXML(parametersValues)));\n            httpEntity = new UrlEncodedFormEntity(nvps, UTF_8.name());\n        }\n        return httpEntity;\n    }\n\n    private static byte[] serialize(final Object obj) throws IOException {\n        final ByteArrayOutputStream b = new ByteArrayOutputStream();\n        final ObjectOutputStream o = new ObjectOutputStream(b);\n        o.writeObject(obj);\n        return b.toByteArray();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-client/src/main/java/org/bonitasoft/engine/api/PlatformAPIAccessor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\nimport java.io.IOException;\nimport java.lang.reflect.Proxy;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.impl.ClientInterceptor;\nimport org.bonitasoft.engine.api.impl.LocalServerAPIFactory;\nimport org.bonitasoft.engine.api.internal.ServerAPI;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.bonitasoft.engine.session.PlatformSession;\nimport org.bonitasoft.engine.util.APITypeManager;\n\n/**\n * <b>Accessor class that retrieve Platform APIs</b>\n * <p>\n * All APIs given by this class are relevant to the platform only.\n * <ul>\n * <li>{@link PlatformAPI}</li>\n * <li>{@link PlatformCommandAPI}</li>\n * <li>{@link PlatformLoginAPI}</li>\n * </ul>\n *\n * @author Matthieu Chaffotte\n */\npublic class PlatformAPIAccessor {\n\n    static ServerAPI getServerAPI() throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        try {\n            final ApiAccessType apiType = APITypeManager.getAPIType();\n            Map<String, String> parameters;\n            switch (apiType) {\n                case LOCAL:\n                    return LocalServerAPIFactory.getServerAPI();\n                case HTTP:\n                    parameters = APITypeManager.getAPITypeParameters();\n                    return new HTTPServerAPI(parameters);\n                default:\n                    throw new UnknownAPITypeException(\"Unsupported API Type: \" + apiType);\n            }\n        } catch (IOException e) {\n            throw new ServerAPIException(e);\n        }\n    }\n\n    /**\n     * Reload the configuration of the Bonita home from the file system\n     * It allows to change in runtime the Bonita engine your client application uses\n     */\n    public static void refresh() {\n        APITypeManager.refresh();\n    }\n\n    /**\n     * @return the {@link PlatformLoginAPI}\n     * @throws BonitaHomeNotSetException\n     * @throws ServerAPIException\n     * @throws UnknownAPITypeException\n     */\n    public static PlatformLoginAPI getPlatformLoginAPI()\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return getAPI(PlatformLoginAPI.class);\n    }\n\n    static <T> T getAPI(final Class<T> clazz, final PlatformSession session)\n            throws BonitaHomeNotSetException, ServerAPIException,\n            UnknownAPITypeException {\n        final ServerAPI serverAPI = getServerAPI();\n        final ClientInterceptor sessionInterceptor = new ClientInterceptor(clazz.getName(), serverAPI, session);\n        return (T) Proxy.newProxyInstance(APIAccessor.class.getClassLoader(), new Class[] { clazz },\n                sessionInterceptor);\n    }\n\n    static <T> T getAPI(final Class<T> clazz) throws BonitaHomeNotSetException, ServerAPIException,\n            UnknownAPITypeException {\n        final ServerAPI serverAPI = getServerAPI();\n        final ClientInterceptor sessionInterceptor = new ClientInterceptor(clazz.getName(), serverAPI);\n        return (T) Proxy.newProxyInstance(APIAccessor.class.getClassLoader(), new Class[] { clazz },\n                sessionInterceptor);\n    }\n\n    /**\n     * @param session\n     *        a {@link PlatformSession} created using the {@link PlatformLoginAPI}\n     * @return\n     *         the {@link PlatformAPI}\n     * @throws InvalidSessionException\n     * @throws BonitaHomeNotSetException\n     * @throws ServerAPIException\n     * @throws UnknownAPITypeException\n     */\n    public static PlatformAPI getPlatformAPI(final PlatformSession session)\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return getAPI(PlatformAPI.class, session);\n    }\n\n    /**\n     * @param session\n     *        a {@link PlatformSession} created using the {@link PlatformLoginAPI}\n     * @return\n     *         the {@link PlatformCommandAPI}\n     * @throws BonitaHomeNotSetException\n     * @throws ServerAPIException\n     * @throws UnknownAPITypeException\n     * @throws InvalidSessionException\n     */\n    public static PlatformCommandAPI getPlatformCommandAPI(final PlatformSession session)\n            throws BonitaHomeNotSetException, ServerAPIException,\n            UnknownAPITypeException {\n        return getAPI(PlatformCommandAPI.class, session);\n    }\n\n    public static TemporaryContentAPI getTemporaryContentAPI()\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return getAPI(TemporaryContentAPI.class);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-client/src/main/java/org/bonitasoft/engine/api/TcpDestination.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\npublic class TcpDestination {\n\n    private final String host;\n    private final int port;\n\n    public TcpDestination(final String host, final int port) {\n        this.host = host;\n        this.port = port;\n    }\n\n    public String getHost() {\n        return host;\n    }\n\n    public int getPort() {\n        return port;\n    }\n\n    @Override\n    public String toString() {\n        return \"TcpDestination [host=\" + host + \", port=\" + port + \"]\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-client/src/main/java/org/bonitasoft/engine/api/TenantAPIAccessor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\nimport java.lang.reflect.Proxy;\n\nimport org.bonitasoft.engine.api.impl.ClientInterceptor;\nimport org.bonitasoft.engine.api.internal.ServerAPI;\nimport org.bonitasoft.engine.api.platform.PlatformInformationAPI;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.util.APITypeManager;\n\n/**\n * Accessor class that retrieve APIs in Bonita <b>Community</b> Edition.\n * <ul>\n * <li>{@link ProcessAPI},</li>\n * <li>{@link CommandAPI},</li>\n * <li>{@link IdentityAPI},</li>\n * <li>{@link LoginAPI}</li>\n * </ul>\n *\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n */\npublic final class TenantAPIAccessor {\n\n    private static ServerAPI getServerAPI()\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return PlatformAPIAccessor.getServerAPI();\n    }\n\n    /**\n     * Refreshes the way the engine client communicates to the engine server.\n     *\n     * @see APITypeManager\n     * @see ApiAccessType\n     */\n    public static void refresh() {\n        APITypeManager.refresh();\n    }\n\n    private static <T> T getAPI(final Class<T> clazz, final APISession session)\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        final ServerAPI serverAPI = getServerAPI();\n        final ClientInterceptor sessionInterceptor = new ClientInterceptor(clazz.getName(), serverAPI, session);\n        return (T) Proxy.newProxyInstance(APIAccessor.class.getClassLoader(), new Class[] { clazz },\n                sessionInterceptor);\n    }\n\n    private static <T> T getAPI(final Class<T> clazz)\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        final ServerAPI serverAPI = getServerAPI();\n        final ClientInterceptor sessionInterceptor = new ClientInterceptor(clazz.getName(), serverAPI);\n        return (T) Proxy.newProxyInstance(APIAccessor.class.getClassLoader(), new Class[] { clazz },\n                sessionInterceptor);\n    }\n\n    public static LoginAPI getLoginAPI() throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return getAPI(LoginAPI.class);\n    }\n\n    public static IdentityAPI getIdentityAPI(final APISession session)\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return getAPI(IdentityAPI.class, session);\n    }\n\n    public static ProcessAPI getProcessAPI(final APISession session)\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return getAPI(ProcessAPI.class, session);\n    }\n\n    public static CommandAPI getCommandAPI(final APISession session)\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return getAPI(CommandAPI.class, session);\n    }\n\n    public static ProfileAPI getProfileAPI(final APISession session)\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return getAPI(ProfileAPI.class, session);\n    }\n\n    public static PermissionAPI getPermissionAPI(final APISession session)\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return getAPI(PermissionAPI.class, session);\n    }\n\n    public static PageAPI getCustomPageAPI(final APISession session)\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return getAPI(PageAPI.class, session);\n    }\n\n    public static ApplicationAPI getLivingApplicationAPI(final APISession session)\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return getAPI(ApplicationAPI.class, session);\n    }\n\n    public static TenantAdministrationAPI getTenantAdministrationAPI(final APISession session)\n            throws BonitaHomeNotSetException, ServerAPIException,\n            UnknownAPITypeException {\n        return getAPI(TenantAdministrationAPI.class, session);\n    }\n\n    /**\n     * @deprecated as of 7.3, see {@link BusinessDataAPI} for replacements\n     */\n    @Deprecated(since = \"7.3\")\n    public static BusinessDataAPI getBusinessDataAPI(APISession session)\n            throws BonitaHomeNotSetException, ServerAPIException,\n            UnknownAPITypeException {\n        return getAPI(BusinessDataAPI.class, session);\n    }\n\n    public static ApplicationAPI getApplicationAPI(final APISession session)\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return getAPI(ApplicationAPI.class, session);\n    }\n\n    public static MaintenanceAPI getMaintenanceAPI(final APISession session)\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return getAPI(MaintenanceAPI.class, session);\n    }\n\n    public static PlatformInformationAPI getPlatformInformationAPI(final APISession session)\n            throws ServerAPIException, BonitaHomeNotSetException, UnknownAPITypeException {\n        return getAPI(PlatformInformationAPI.class, session);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-client/src/main/java/org/bonitasoft/engine/api/impl/LocalServerAPIFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport org.bonitasoft.engine.api.internal.ServerAPI;\nimport org.bonitasoft.engine.exception.ServerAPIException;\n\n/**\n * @author Matthieu Chaffotte\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n */\npublic class LocalServerAPIFactory {\n\n    private static Class<?> forName = null;\n\n    private LocalServerAPIFactory() {\n        // For Sonar\n    }\n\n    static {\n        try {\n            forName = Class.forName(\"org.bonitasoft.engine.api.impl.ServerAPIFactory\");\n        } catch (ClassNotFoundException e) {\n            e.printStackTrace(System.err);\n            throw new ExceptionInInitializerError(e);\n        }\n    }\n\n    public static ServerAPI getServerAPI() throws ServerAPIException {\n        try {\n            return (ServerAPI) forName.getMethod(\"getServerAPI\").invoke(null);\n        } catch (final Exception e) {\n            throw new ServerAPIException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-client/src/main/java/org/bonitasoft/engine/api/impl/XmlConverter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport java.io.IOException;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectOutputStream;\nimport java.io.StringReader;\nimport java.io.StringWriter;\n\nimport com.thoughtworks.xstream.XStream;\nimport com.thoughtworks.xstream.security.AnyTypePermission;\nimport org.bonitasoft.engine.api.BonitaStackTraceElementConverter;\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\nimport org.bonitasoft.engine.xml.XStreamDenyList;\n\npublic class XmlConverter {\n\n    private static volatile XStream xstream;\n\n    private static XStream getXStream() {\n        if (xstream == null) {\n            synchronized (XmlConverter.class) {\n                if (xstream == null) {\n                    xstream = createXStream();\n                }\n            }\n        }\n        return xstream;\n    }\n\n    private static XStream createXStream() {\n        var xs = new XStream();\n        xs.ignoreUnknownElements();\n        xs.addPermission(AnyTypePermission.ANY);\n        // Block known deserialization gadget chain libraries to prevent RCE.\n        // A strict allowlist is not possible here because API responses can contain BDM types with arbitrary packages.\n        xs.denyTypesByWildcard(XStreamDenyList.getDenyPatterns());\n        xs.registerConverter(new BonitaStackTraceElementConverter(), XStream.PRIORITY_VERY_HIGH);\n        return xs;\n    }\n\n    // Package-private for testing\n    static void reset() {\n        xstream = null;\n    }\n\n    public String toXML(final Object object) {\n        final StringWriter stringWriter = new StringWriter();\n        try (final ObjectOutputStream out = getXStream().createObjectOutputStream(stringWriter)) {\n            out.writeObject(object);\n        } catch (IOException e) {\n            throw new BonitaRuntimeException(\"Unable to serialize object \" + object, e);\n        }\n        return stringWriter.toString();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public <T> T fromXML(final String object) {\n        try (final StringReader xmlReader = new StringReader(object);\n                final ObjectInputStream in = getXStream().createObjectInputStream(xmlReader)) {\n            return (T) in.readObject();\n        } catch (final ClassNotFoundException | IOException | RuntimeException e) {\n            // Do not include the XML payload in the error message to prevent\n            // information disclosure of attacker-controlled input\n            throw new BonitaRuntimeException(\"Unable to deserialize object\", e);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-client/src/main/java/org/bonitasoft/engine/bdm/BusinessObjectDAOFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.InvocationTargetException;\n\nimport org.bonitasoft.engine.bdm.dao.BusinessObjectDAO;\nimport org.bonitasoft.engine.session.APISession;\n\n/**\n * A factory to create Data Access Objects (DAO). These DAOs interact with\n * {@link org.bonitasoft.engine.bdm.model.BusinessObject}s.\n *\n * @author Romain Bioteau\n * @author Matthieu Chaffotte\n */\npublic class BusinessObjectDAOFactory {\n\n    private static final String IMPL_SUFFIX = \"Impl\";\n\n    /**\n     * Creates the implementation of the DAO for the given session.\n     *\n     * @param session the current opened session\n     * @param daoInterface the interface of the DAO\n     * @return the implementation of the DAO\n     * @throws BusinessObjectDaoCreationException if the factory is not able to instantiate the DAO\n     */\n    public <T extends BusinessObjectDAO> T createDAO(final APISession session, final Class<T> daoInterface)\n            throws BusinessObjectDaoCreationException {\n        if (session == null) {\n            throw new IllegalArgumentException(\"session is null\");\n        }\n        if (daoInterface == null) {\n            throw new IllegalArgumentException(\"daoInterface is null\");\n        }\n        if (!daoInterface.isInterface()) {\n            throw new IllegalArgumentException(daoInterface.getName() + \" is not an interface\");\n        }\n        final String daoClassName = daoInterface.getName();\n        Class<T> daoImplClass = null;\n        try {\n            daoImplClass = loadClass(daoClassName);\n        } catch (final ClassNotFoundException e) {\n            throw new BusinessObjectDaoCreationException(e);\n        }\n        if (daoImplClass != null) {\n            try {\n                final Constructor<T> constructor = daoImplClass.getConstructor(APISession.class);\n                return constructor.newInstance(session);\n            } catch (final SecurityException e) {\n                throw new BusinessObjectDaoCreationException(e);\n            } catch (final NoSuchMethodException e) {\n                throw new BusinessObjectDaoCreationException(e);\n            } catch (final IllegalArgumentException e) {\n                throw new BusinessObjectDaoCreationException(e);\n            } catch (final InstantiationException e) {\n                throw new BusinessObjectDaoCreationException(e);\n            } catch (final IllegalAccessException e) {\n                throw new BusinessObjectDaoCreationException(e);\n            } catch (final InvocationTargetException e) {\n                throw new BusinessObjectDaoCreationException(e);\n            }\n        }\n        return null;\n    }\n\n    /**\n     * Loads the class of the {@link BusinessObjectDAO} according to its class name.\n     * <p>\n     * The loading is done in the current Thread.\n     *\n     * @param daoClassName the name of the class of the DAO\n     * @return the class of the BusinessObjectDAO\n     * @throws ClassNotFoundException if the daoClassName is unknown by the current Thread\n     */\n    @SuppressWarnings(\"unchecked\")\n    protected <T extends BusinessObjectDAO> Class<T> loadClass(final String daoClassName)\n            throws ClassNotFoundException {\n        return (Class<T>) Class.forName(toDaoImplClassName(daoClassName), true,\n                Thread.currentThread().getContextClassLoader());\n    }\n\n    private String toDaoImplClassName(final String daoClassName) {\n        return daoClassName + IMPL_SUFFIX;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-client/src/main/java/org/bonitasoft/engine/bdm/BusinessObjectDaoCreationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm;\n\nimport org.bonitasoft.engine.exception.CreationException;\n\n/**\n * Thrown to indicate that the DAO was not created.\n *\n * @author Romain Bioteau\n * @author Matthieu Chaffotte\n */\npublic class BusinessObjectDaoCreationException extends CreationException {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * Constructs a BusinessObjectDaoCreationException with the specified cause.\n     *\n     * @param cause the cause\n     */\n    public BusinessObjectDaoCreationException(final Throwable cause) {\n        super(cause);\n    }\n\n    /**\n     * Constructs a BusinessObjectDaoCreationException with the specified message.\n     *\n     * @param message the explanations of the exception\n     */\n    public BusinessObjectDaoCreationException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-client/src/main/java/org/bonitasoft/engine/bdm/package-info.java",
    "content": "/**\n * Copyright (C) 2015 BonitaSoft S.A.\n * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n/**\n * <p>\n * Provides classes and interfaces for business data models (BDM).\n * </p>\n */\npackage org.bonitasoft.engine.bdm;\n"
  },
  {
    "path": "bpm/bonita-client/src/main/java/org/bonitasoft/engine/exception/UnableToReadBonitaClientConfiguration.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class UnableToReadBonitaClientConfiguration extends BonitaException {\n\n    private static final long serialVersionUID = -7494844524785100125L;\n\n    public UnableToReadBonitaClientConfiguration(final String message) {\n        super(message);\n    }\n\n    public UnableToReadBonitaClientConfiguration(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public UnableToReadBonitaClientConfiguration(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-client/src/main/java/org/bonitasoft/engine/exception/UnknownAPITypeException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class UnknownAPITypeException extends BonitaException {\n\n    private static final long serialVersionUID = 7203710840971376884L;\n\n    public UnknownAPITypeException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-client/src/main/java/org/bonitasoft/engine/util/APITypeManager.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.util;\n\nimport static org.bonitasoft.engine.api.ApiAccessType.HTTP;\nimport static org.bonitasoft.engine.api.ApiAccessType.LOCAL;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Paths;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.logging.Logger;\n\nimport org.bonitasoft.engine.api.ApiAccessType;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.io.PropertiesManager;\n\n/**\n * Specify how the client communicate with the engine. There are three ways of doing it:\n * <ul>\n * <li>Using Java System Properties</li>\n * <li>Programmatically</li>\n * <li>DEPRECATED: using a file inside bonita-home. See <a\n * href=\"https://documentation.ofelia.com/bonita/latest/configure-client-of-bonita-bpm-engine#_configure_client_using_bonita_home_client\">online\n * documentation</a>.</li>\n * </ul>\n * <h1>Using Java System Properties</h1>\n * <p>\n * Use System property <code>-Dorg.bonitasoft.engine.api-type=</code>\n * <ul>\n * <li>LOCAL:\n * <p>connect to the server in the local JVM (default). No other configuration is necessary.</p>\n * </li>\n * <li>HTTP\n * <p>\n * connect to the server using HTTP. You must also specify:\n * <ul>\n * <li><code>-Dorg.bonitasoft.engine.api-type.server.url=HTTP_SERVER_URL</code>, e.g.\n * <code>http://localhost:8080</code></li>\n * <li><code>-Dorg.bonitasoft.engine.api-type.application.name=WEBAPP_NAME</code>, this is the name of the web\n * application, e.g. <code>bonita</code></li>\n * </ul>\n * Optionally you can specify the maximum number of connections (JVM-wide) using\n * <code>-Dorg.bonitasoft.engine.api-type.connections.max=CONNECTIONS_MAX</code>\n * </p>\n * </li>\n * <li>TCP\n * <p>\n * not recommended, only for testing purpose.\n * </p></li>\n * </ul>\n * </p>\n * <h1>Programmatically</h1>\n * <ul>\n * <li>LOCAL access:\n * <p>APITypeManager.setAPITypeAndParams(ApiAccessType.LOCAL, null);</p>\n * </li>\n * <li>HTTP access:\n *\n * <pre>\n * <code>HashMap<String, String> parameters = new HashMap<>();\n * parameters.put(\"server.url\", \"http://myserver.com:8080\");\n * parameters.put(\"application.name\", \"bonita-application\");\n * parameters.put(\"connections.max\", \"5\");\n * APITypeManager.setAPITypeAndParams(ApiAccessType.HTTP, parameters);</code>\n * </pre>\n *\n * </li>\n * </ul>\n *\n * @author Baptiste Mesta\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @see <a\n *      href=\"https://documentation.ofelia.com/bonita/latest/configure-client-of-bonita-bpm-engine#client_config\">Online\n *      documentation on Client configuration</a>\n */\npublic class APITypeManager {\n\n    private static final Logger LOGGER = Logger.getLogger(APITypeManager.class.getName());\n\n    private static final String API_TYPE = \"org.bonitasoft.engine.api-type\";\n\n    private static ApiAccessType apiAccessType = null;\n\n    private static Map<String, String> apiTypeParameters = null;\n\n    public static ApiAccessType getAPIType() throws ServerAPIException, UnknownAPITypeException, IOException {\n        if (apiAccessType == null) {\n            final String apiType = getAPITypeFromProperties();\n            if (LOCAL.name().equalsIgnoreCase(apiType)) {\n                apiAccessType = LOCAL;\n            } else if (HTTP.name().equalsIgnoreCase(apiType)) {\n                apiAccessType = HTTP;\n            } else {\n                throw new UnknownAPITypeException(\"Invalid API type: \" + apiType);\n            }\n        }\n        return apiAccessType;\n    }\n\n    private static String getAPITypeFromProperties() throws IOException {\n        String property = getProperties().get(API_TYPE);\n        if (property != null) {\n            return property;\n        }\n        return LOCAL.name();\n    }\n\n    public static Map<String, String> getAPITypeParameters() throws ServerAPIException, IOException {\n        if (apiTypeParameters == null) {\n            final Map<String, String> properties = getProperties();\n            apiTypeParameters = new HashMap<>(properties.size());\n            for (Map.Entry<String, String> property : properties.entrySet()) {\n                if (API_TYPE.equals(property.getKey())) {\n                    continue;\n                }\n                apiTypeParameters.put(property.getKey(), property.getValue());\n            }\n        }\n        return apiTypeParameters;\n    }\n\n    public static void setAPITypeAndParams(final ApiAccessType type, final Map<String, String> parameters) {\n        warnIfUsingRemoteConnectionWithLocalEngine(type);\n        apiAccessType = type;\n        apiTypeParameters = new HashMap<>();\n        if (parameters != null) {\n            apiTypeParameters.putAll(parameters);\n        }\n    }\n\n    private static void warnIfUsingRemoteConnectionWithLocalEngine(ApiAccessType type) {\n        if (type != null && type != LOCAL) {\n            try {\n                Thread.currentThread().getContextClassLoader()\n                        .loadClass(\"org.bonitasoft.engine.api.impl.ProcessAPIImpl\");\n                LOGGER.warning(\n                        \"You are declaring an API access to Bonita Engine as a remote connection, whereas it looks like you are running in the same JVM. You should use LOCAL connection, using constant 'ApiAccessType.LOCAL'\");\n            } catch (ClassNotFoundException ignored) {\n                //no warning\n            }\n        }\n    }\n\n    private static Map<String, String> getProperties() throws IOException {\n        Map<String, String> properties = getPropertiesFromSystemProperties();\n        Properties propertiesFromBonitaHome = getPropertiesFromBonitaHome();\n        for (String property : propertiesFromBonitaHome.stringPropertyNames()) {\n            if (!properties.containsKey(property)) {\n                properties.put(property, propertiesFromBonitaHome.getProperty(property));\n            }\n        }\n        return properties;\n    }\n\n    private static Map<String, String> getPropertiesFromSystemProperties() {\n        Map<String, String> properties = new HashMap<>();\n        String apiType = System.getProperty(\"org.bonitasoft.engine.api-type\");\n        if (apiType != null) {\n            properties.put(\"org.bonitasoft.engine.api-type\", apiType);\n        }\n        addParameter(properties, \"org.bonitasoft.engine.api-type.\", \"server.url\");\n        addParameter(properties, \"org.bonitasoft.engine.api-type.\", \"application.name\");\n        addParameter(properties, \"org.bonitasoft.engine.api-type.\", \"connections.max\");\n        addParameter(properties, \"org.bonitasoft.engine.api-type.\", \"basicAuthentication.active\");\n        addParameter(properties, \"org.bonitasoft.engine.api-type.\", \"basicAuthentication.username\");\n        addParameter(properties, \"org.bonitasoft.engine.api-type.\", \"basicAuthentication.password\");\n        return properties;\n    }\n\n    private static void addParameter(Map<String, String> properties, String parameterPrefix, String parameterName) {\n        String parameter = System.getProperty(parameterPrefix + parameterName);\n        if (parameter != null) {\n            properties.put(parameterName, parameter);\n        }\n    }\n\n    /*\n     * LEGACY MODE\n     */\n    private static Properties getPropertiesFromBonitaHome() throws IOException {\n        String bonitaHomePath = System.getProperty(\"bonita.home\");\n        if (bonitaHomePath == null || bonitaHomePath.isEmpty()) {\n            return new Properties();\n        }\n        File clientFolder = new File(bonitaHomePath.trim(), \"engine-client\");\n        final Properties result = new Properties();\n        addPropertiesFrom(clientFolder, result, \"work\", \"bonita-client-community.properties\");\n        addPropertiesFrom(clientFolder, result, \"conf\", \"bonita-client-custom.properties\");\n        return result;\n    }\n\n    private static void addPropertiesFrom(File clientFolder, Properties result, String... paths) throws IOException {\n        File folder = Paths.get(clientFolder.getPath(), paths).toFile();\n        if (folder.exists()) {\n            final Properties defaultProperties = PropertiesManager.getProperties(folder);\n            result.putAll(defaultProperties);\n        }\n    }\n\n    public static void refresh() {\n        apiAccessType = null;\n        apiTypeParameters = null;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-client/src/test/java/org/bonitasoft/engine/api/APIClientTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.doReturn;\n\nimport java.io.IOException;\n\nimport org.bonitasoft.engine.api.internal.ServerAPI;\nimport org.bonitasoft.engine.api.internal.ServerWrappedException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.platform.LoginException;\nimport org.bonitasoft.engine.platform.LogoutException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Nicolas Chabanoles on 18/11/15.\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class APIClientTest {\n\n    private static final String VALID_USERNAME = \"username\";\n    private static final String VALID_PASSWORD = \"password\";\n\n    @Spy\n    APIClient client;\n\n    @Mock\n    ServerAPI server;\n\n    @Mock\n    APISession session;\n\n    @Rule\n    public ExpectedException expectedEx = ExpectedException.none();\n\n    @Before\n    public void before() throws IOException, ServerAPIException, UnknownAPITypeException, ServerWrappedException {\n        doReturn(server).when(client).getServerAPI();\n        doReturn(session).when(server).invokeMethod(anyMap(), eq(\"org.bonitasoft.engine.api.LoginAPI\"), eq(\"login\"),\n                anyList(),\n                eq(new Object[] { VALID_USERNAME, VALID_PASSWORD }));\n    }\n\n    @Test\n    public void should_throw_exception_when_accessing_api_without_being_loggedIn() {\n        expectedEx.expect(IllegalStateException.class);\n        expectedEx.expectMessage(\"You must call login() prior to accessing any API.\");\n\n        client.getProcessAPI();\n    }\n\n    @Test\n    public void should_login_create_a_session() throws LoginException {\n        client.login(VALID_USERNAME, VALID_PASSWORD);\n        assertThat(client.getSession()).isNotNull();\n    }\n\n    @Test\n    public void should_logout_destroy_session() throws LoginException, LogoutException {\n        client.login(VALID_USERNAME, VALID_PASSWORD);\n        assertThat(client.getSession()).isNotNull();\n        client.logout();\n        assertThat(client.getSession()).isNull();\n    }\n\n    @Test\n    public void should_get_ProcessAPI_from_server() throws LoginException {\n        client.login(VALID_USERNAME, VALID_PASSWORD);\n        ProcessAPI processAPI = client.getProcessAPI();\n        assertThat(processAPI).isNotNull();\n    }\n\n    @Test\n    public void should_get_IdentityAPI_from_server() throws LoginException {\n        client.login(VALID_USERNAME, VALID_PASSWORD);\n        IdentityAPI api = client.getIdentityAPI();\n        assertThat(api).isNotNull();\n    }\n\n    @Test\n    public void should_get_CommandAPI_from_server() throws LoginException {\n        client.login(VALID_USERNAME, VALID_PASSWORD);\n        CommandAPI api = client.getCommandAPI();\n        assertThat(api).isNotNull();\n    }\n\n    @Test\n    public void should_get_ProfileAPI_from_server() throws LoginException {\n        client.login(VALID_USERNAME, VALID_PASSWORD);\n        ProfileAPI api = client.getProfileAPI();\n        assertThat(api).isNotNull();\n    }\n\n    @Test\n    public void should_get_TenantAdministrationAPI_from_server() throws LoginException {\n        client.login(VALID_USERNAME, VALID_PASSWORD);\n        TenantAdministrationAPI api = client.getTenantAdministrationAPI();\n        assertThat(api).isNotNull();\n    }\n\n    @Test\n    public void should_get_PageAPI_from_server() throws LoginException {\n        client.login(VALID_USERNAME, VALID_PASSWORD);\n        PageAPI api = client.getCustomPageAPI();\n        assertThat(api).isNotNull();\n    }\n\n    @Test\n    public void should_get_LivingApplicationAPI_from_server() throws LoginException {\n        client.login(VALID_USERNAME, VALID_PASSWORD);\n        ApplicationAPI api = client.getLivingApplicationAPI();\n        assertThat(api).isNotNull();\n    }\n\n    @Test\n    public void should_get_PermissionAPI_from_server() throws LoginException {\n        client.login(VALID_USERNAME, VALID_PASSWORD);\n        PermissionAPI api = client.getPermissionAPI();\n        assertThat(api).isNotNull();\n    }\n\n    @Test\n    public void should_get_BusinessDataAPI_from_server() throws LoginException {\n        client.login(VALID_USERNAME, VALID_PASSWORD);\n        BusinessDataAPI api = client.getBusinessDataAPI();\n        assertThat(api).isNotNull();\n    }\n\n    @Test\n    public void should_get_ApplicationAPI_from_server() throws LoginException {\n        client.login(VALID_USERNAME, VALID_PASSWORD);\n        ApplicationAPI api = client.getApplicationAPI();\n        assertThat(api).isNotNull();\n    }\n\n    @Test\n    public void should_throw_exception_when_accessing_api_after_logout() throws LoginException, LogoutException {\n        client.login(VALID_USERNAME, VALID_PASSWORD);\n        client.logout();\n\n        expectedEx.expect(IllegalStateException.class);\n        expectedEx.expectMessage(\"You must call login() prior to accessing any API.\");\n\n        client.getProcessAPI();\n    }\n\n    @Test\n    public void should_return_session_used_at_creation() {\n        APIClient clientToTest = new APIClient(session);\n        assertThat(clientToTest.getSession()).isEqualTo(session);\n    }\n\n    @Test\n    public void should_return_session_created_at_login() throws LoginException {\n        client.login(VALID_USERNAME, VALID_PASSWORD);\n        assertThat(client.getSession()).isEqualTo(session);\n    }\n\n    @Test\n    public void should_newly_created_client_has_no_session() {\n        assertThat(client.getSession()).isNull();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-client/src/test/java/org/bonitasoft/engine/api/BusinessObjectDAOUsingAPIClientTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\nimport org.assertj.core.api.Assertions;\nimport org.bonitasoft.engine.bdm.BusinessObjectDaoCreationException;\nimport org.bonitasoft.engine.bdm.DummyDAO;\nimport org.bonitasoft.engine.bdm.DummyDAOImpl;\nimport org.bonitasoft.engine.bdm.dao.BusinessObjectDAO;\nimport org.bonitasoft.engine.session.APISession;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentMatchers;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Mockito;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Nicolas Chabanoles on 30/11/15.\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class BusinessObjectDAOUsingAPIClientTest {\n\n    @Spy\n    @InjectMocks\n    APIClient client;\n\n    @Mock\n    APISession session;\n\n    @Rule\n    public ExpectedException expectedEx = ExpectedException.none();\n\n    @Test\n    public void should_create_dao_throw_IllegalArgmumentException_for_null_interface() throws Exception {\n        expectedEx.expect(IllegalArgumentException.class);\n        expectedEx.expectMessage(\"daoInterface is null\");\n        client.getDAO(null);\n    }\n\n    @Test\n    public void should_create_dao_throw_IllegalArgmumentException_if_daoInterface_is_not_an_interface()\n            throws Exception {\n        expectedEx.expect(IllegalArgumentException.class);\n        expectedEx.expectMessage(\"DummyDAOImpl is not an interface\");\n        client.getDAO(DummyDAOImpl.class);\n    }\n\n    @Test\n    public void should_create_dao_throw_BusinessObjectDaoCreationException_if_daoImpl_not_in_classpath()\n            throws Exception {\n        Mockito.doThrow(ClassNotFoundException.class).when(client).loadClass(BusinessObjectDAO.class);\n\n        expectedEx.expect(BusinessObjectDaoCreationException.class);\n        expectedEx.expectMessage(\"\");\n\n        client.getDAO(BusinessObjectDAO.class);\n    }\n\n    @Test\n    public void should_create_dao_throw_BusinessObjectDaoCreationException_if_daoImpl_has_no_constructor_with_session()\n            throws Exception {\n        Mockito.doReturn(DummyDAOWithoutConstructorImpl.class).when(client)\n                .loadClass(ArgumentMatchers.any(Class.class));\n\n        expectedEx.expect(BusinessObjectDaoCreationException.class);\n        expectedEx.expectMessage(\"\");\n\n        client.getDAO(DummyDAO.class);\n    }\n\n    @Test\n    public void should_create_dao_return_implementation() throws Exception {\n        DummyDAO daoInstance = client.getDAO(DummyDAO.class);\n        Assertions.assertThat(daoInstance).isNotNull();\n    }\n\n    class DummyDAOWithoutConstructorImpl implements DummyDAO {\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-client/src/test/java/org/bonitasoft/engine/api/HTTPServerAPIIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.catchThrowable;\n\nimport java.io.Serializable;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.bonitasoft.engine.api.internal.ServerWrappedException;\nimport org.eclipse.jetty.security.ConstraintMapping;\nimport org.eclipse.jetty.security.ConstraintSecurityHandler;\nimport org.eclipse.jetty.security.HashLoginService;\nimport org.eclipse.jetty.security.LoginService;\nimport org.eclipse.jetty.security.authentication.BasicAuthenticator;\nimport org.eclipse.jetty.server.Request;\nimport org.eclipse.jetty.server.Server;\nimport org.eclipse.jetty.server.ServerConnector;\nimport org.eclipse.jetty.server.handler.AbstractHandler;\nimport org.eclipse.jetty.util.security.Constraint;\nimport org.junit.AfterClass;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\n\n/**\n * @author Emmanuel Duchastenier\n * @author Ludovic Bertin\n */\npublic class HTTPServerAPIIT {\n\n    private static final String APPLICATION_NAME = \"HTTPServerAPITest\";\n\n    private static String baseResourceUrl;\n\n    private static Server server;\n\n    private final Map<String, Serializable> options = new HashMap<>();\n\n    private final String apiInterfaceName = \"someInterface\";\n\n    private final String methodName = \"someMethod\";\n\n    final List<String> classNameParameters = new ArrayList<>();\n\n    @BeforeClass\n    public static void startJetty() throws Exception {\n        // Launch server on random port\n        server = new Server(0);\n\n        // Simple login service\n        URL realmPropertyURL = HTTPServerAPIIT.class.getClassLoader().getResource(\"myRealm.properties\");\n        LoginService loginService = new HashLoginService(\"MyRealm\", realmPropertyURL.toString());\n        server.addBean(loginService);\n\n        // Security handler.\n        ConstraintSecurityHandler security = new ConstraintSecurityHandler();\n        server.setHandler(security);\n\n        // Add security constraint\n        Constraint constraint = new Constraint();\n        constraint.setName(\"auth\");\n        constraint.setAuthenticate(true);\n        constraint.setRoles(new String[] { \"user\", \"admin\" });\n\n        // Bind constraint\n        ConstraintMapping mapping = new ConstraintMapping();\n        mapping.setPathSpec(\"/*\");\n        mapping.setConstraint(constraint);\n\n        // Configure the security handler\n        security.setConstraintMappings(Collections.singletonList(mapping));\n        security.setAuthenticator(new BasicAuthenticator());\n        security.setLoginService(loginService);\n\n        // Simulate Bonita engine part\n        BonitaHandler bonitaHandler = new BonitaHandler();\n        security.setHandler(bonitaHandler);\n\n        // start server\n        server.start();\n\n        // retrieve port\n        int actualPort = ((ServerConnector) server.getConnectors()[0]).getLocalPort();\n        baseResourceUrl = \"http://localhost:\" + actualPort;\n    }\n\n    @AfterClass\n    public static void stopJetty() throws Exception {\n        server.stop();\n    }\n\n    @Test\n    public void invokeMethodWithBasicAuthentication() throws Exception {\n        Map<String, String> configuration = new HashMap<>();\n        configuration.put(\"server.url\", baseResourceUrl);\n        configuration.put(\"application.name\", APPLICATION_NAME);\n        configuration.put(\"connections.max\", \"2\");\n        configuration.put(\"basicAuthentication.active\", \"true\");\n        configuration.put(\"basicAuthentication.username\", \"john\");\n        configuration.put(\"basicAuthentication.password\", \"doe\");\n\n        final HTTPServerAPI httpServerAPI = new HTTPServerAPI(configuration);\n        httpServerAPI.invokeMethod(options, apiInterfaceName, methodName, classNameParameters, null);\n    }\n\n    @Test\n    public void invokeBasicAuthenticationWithWrongCredentialsShouldFail() {\n        Map<String, String> configuration = new HashMap<>();\n        configuration.put(\"server.url\", baseResourceUrl);\n        configuration.put(\"application.name\", APPLICATION_NAME);\n        configuration.put(\"basicAuthentication.active\", \"true\");\n        configuration.put(\"basicAuthentication.username\", \"john\");\n        configuration.put(\"basicAuthentication.password\", \"__LENNON__\");\n\n        final HTTPServerAPI httpServerAPI = new HTTPServerAPI(configuration);\n        Throwable thrown = catchThrowable(() -> httpServerAPI.invokeMethod(options, apiInterfaceName, methodName,\n                classNameParameters, null));\n\n        assertThat(thrown).isInstanceOf(ServerWrappedException.class)\n                .hasMessageStartingWith(\"Error while executing POST request (http code: 401)\");\n    }\n\n    private static final class BonitaHandler extends AbstractHandler {\n\n        @Override\n        public void handle(final String s, final Request baseRequest, final HttpServletRequest request,\n                final HttpServletResponse response) {\n            assertThat(request.getUserPrincipal().getName()).isEqualTo(\"john\");\n            response.setContentType(\"text/html;charset=utf-8\");\n            response.setStatus(HttpServletResponse.SC_OK);\n            baseRequest.setHandled(true);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-client/src/test/java/org/bonitasoft/engine/api/HTTPServerAPITest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\nimport static java.util.Arrays.asList;\nimport static java.util.Collections.emptyMap;\nimport static java.util.Collections.singletonMap;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.catchThrowable;\nimport static org.mockito.Mockito.*;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.Serializable;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.UndeclaredThrowableException;\nimport java.net.URLDecoder;\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.http.HttpEntity;\nimport org.apache.http.client.HttpClient;\nimport org.apache.http.impl.conn.PoolingHttpClientConnectionManager;\nimport org.bonitasoft.engine.api.internal.ServerWrappedException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.mockito.junit.MockitoJUnit;\nimport org.mockito.junit.MockitoRule;\n\n/**\n * @author Celine Souchet\n */\npublic class HTTPServerAPITest {\n\n    @Rule\n    public MockitoRule mockitoRule = MockitoJUnit.rule();\n\n    private HTTPServerAPI httpServerAPI;\n\n    @Before\n    public void initialize() {\n        HashMap<String, String> map = new HashMap<>();\n        map.put(HTTPServerAPI.SERVER_URL, \"localhost:8080\");\n        map.put(HTTPServerAPI.APPLICATION_NAME, \"bonita\");\n        map.put(HTTPServerAPI.CONNECTIONS_MAX, \"3\");\n        httpServerAPI = new HTTPServerAPI(map);\n    }\n\n    /**\n     * BPA-443: locks down the wire-format tolerance for void engine HTTP API methods.\n     * Both wire shapes produced by server versions past and present must deserialize to\n     * null on the client — otherwise a server change between \"null\" and \"\" would break\n     * the client silently.\n     */\n    @Test\n    public void should_return_null_for_empty_and_null_literal_response_body() throws Throwable {\n        assertThat(httpServerAPI.checkInvokeMethodReturn(\"\"))\n                .as(\"Empty body (current server wire shape for void methods)\").isNull();\n        assertThat(httpServerAPI.checkInvokeMethodReturn(\"null\"))\n                .as(\"Literal \\\"null\\\" body (legacy server wire shape for void methods)\").isNull();\n        assertThat(httpServerAPI.checkInvokeMethodReturn(null))\n                .as(\"Null response (defensive)\").isNull();\n    }\n\n    @Test\n    public void should_have_max_connections_configured() throws Exception {\n        Field httpclient = httpServerAPI.getClass().getDeclaredField(\"httpclient\");\n        httpclient.setAccessible(true);\n        HttpClient httpClient = (HttpClient) httpclient.get(null);\n        Field connManager = httpClient.getClass().getDeclaredField(\"connManager\");\n        connManager.setAccessible(true);\n\n        PoolingHttpClientConnectionManager connectionManager = (PoolingHttpClientConnectionManager) connManager\n                .get(httpClient);\n\n        assertThat(connectionManager.getDefaultMaxPerRoute()).isEqualTo(3);\n        assertThat(connectionManager.getMaxTotal()).isEqualTo(3);\n    }\n\n    @Test\n    public void should_invoke_method_catch_and_wrap_UndeclaredThrowableException() throws Throwable {\n        //given:\n        final Map<String, Serializable> options = new HashMap<>();\n        final String apiInterfaceName = \"apiInterfaceName\";\n        final String methodName = \"methodName\";\n        final List<String> classNameParameters = new ArrayList<>();\n        final Object[] parametersValues = null;\n\n        final HTTPServerAPI httpServerAPI = mock(HTTPServerAPI.class);\n        final String response = \"response\";\n        doReturn(response).when(httpServerAPI).executeHttpPost(eq(options), eq(apiInterfaceName), eq(methodName),\n                eq(classNameParameters),\n                eq(parametersValues));\n        doThrow(new UndeclaredThrowableException(new BonitaException(\"Bonita exception\"), \"Exception plop\"))\n                .when(httpServerAPI).checkInvokeMethodReturn(eq(response));\n\n        // Let's call it for real:\n        doCallRealMethod().when(httpServerAPI).invokeMethod(options, apiInterfaceName, methodName, classNameParameters,\n                parametersValues);\n\n        //when:\n        Throwable thrown = catchThrowable(\n                () -> httpServerAPI.invokeMethod(options, apiInterfaceName, methodName, classNameParameters,\n                        parametersValues));\n\n        //then:\n        assertThat(thrown).as(\"Thrown exception\").isInstanceOf(ServerWrappedException.class)\n                .hasMessageContaining(\"java.lang.reflect.UndeclaredThrowableException: Exception plop\")\n                .hasRootCauseInstanceOf(BonitaException.class);\n    }\n\n    @Test\n    public void should_build_entity_serialize_simple_parameters() throws Exception {\n        HttpEntity entity = httpServerAPI.buildEntity(emptyMap(),\n                asList(\"param1\", \"param2\"),\n                new Object[] { \"Välue1\", singletonMap(\"key\", \"välue\") });\n        String content = new String(entity.getContent().readAllBytes(), StandardCharsets.UTF_8);\n        String decodedContent = URLDecoder.decode(content, \"UTF-8\");\n        assertThat(decodedContent).as(\"Decoded content\").contains(\"välue\", \"Välue1\");\n    }\n\n    @Test\n    public void should_build_entity_serialize_byte_array_parameters() throws Exception {\n        HttpEntity entity = httpServerAPI.buildEntity(emptyMap(),\n                asList(String.class.getName(), Map.class.getName(), byte[].class.getName()),\n                new Object[] { \"Välue36\", singletonMap(\"key\", \"välue\"), new byte[] {} });\n        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();\n        entity.writeTo(outputStream);\n        byte[] content = outputStream.toByteArray();\n        String contentAsString = new String(content, Charset.forName(\"UTF-8\"));\n        assertThat(contentAsString).as(\"Content\").contains(\"välue\", \"Välue36\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-client/src/test/java/org/bonitasoft/engine/api/impl/XmlConverterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.catchThrowable;\n\nimport java.io.IOException;\n\nimport com.thoughtworks.xstream.converters.ConversionException;\nimport com.thoughtworks.xstream.security.ForbiddenClassException;\nimport org.bonitasoft.engine.bpm.actor.impl.ActorDefinitionImpl;\nimport org.bonitasoft.engine.bpm.businessdata.impl.BusinessDataDefinitionImpl;\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class XmlConverterTest {\n\n    private XmlConverter xmlConverter;\n\n    @Before\n    public void setUp() {\n        xmlConverter = new XmlConverter();\n    }\n\n    @Test\n    public void should_fromXML_serialize_entity() {\n        // given:\n        XmlConverter xmlConverter = new XmlConverter();\n        String xml = new StringBuilder(\"<root>\")\n                .append(\"<org.bonitasoft.engine.bpm.actor.impl.ActorDefinitionImpl>\")\n                .append(\"<name>test</name>\")\n                .append(\"</org.bonitasoft.engine.bpm.actor.impl.ActorDefinitionImpl></root>\").toString();\n        // when:\n        ActorDefinitionImpl actor = xmlConverter.fromXML(xml);\n\n        // then:\n        assertThat(actor.getName()).isEqualTo(\"test\");\n    }\n\n    @Test\n    public void should_fromXML_serialize_entity_when_xml_contains_unknown_elements() {\n        // given:\n\n        String xml = new StringBuilder(\"<root>\")\n                .append(\"<org.bonitasoft.engine.bpm.actor.impl.ActorDefinitionImpl>\")\n                .append(\"<name>test with unknown xml node</name>\")\n                .append(\"<unknown_property_in_xml>test</unknown_property_in_xml>\")\n                .append(\"</org.bonitasoft.engine.bpm.actor.impl.ActorDefinitionImpl></root>\").toString();\n        // when:\n        ActorDefinitionImpl actor = xmlConverter.fromXML(xml);\n\n        // then:\n        assertThat(actor.getName()).isEqualTo(\"test with unknown xml node\");\n    }\n\n    @Test\n    public void should_fromXML_always_throws_a_bonita_exception_when_the_xml_is_malformed() {\n        // given:\n        XmlConverter xmlConverter = new XmlConverter();\n        String xml = new StringBuilder(\"<malformed>\")\n                .append(\"<org.bonitasoft.engine.bpm.actor.impl.ActorDefinitionImpl>\")\n                .append(\"<name>test\")\n                .append(\"</org.bonitasoft.engine.bpm.actor.impl.ActorDefinitionImpl></root>\").toString();\n        // when:\n        Throwable thrown = catchThrowable(() -> xmlConverter.fromXML(xml));\n\n        // then:\n        assertThat(thrown)\n                .isInstanceOf(BonitaRuntimeException.class)\n                .hasMessage(\"Unable to deserialize object\")\n                .hasCauseInstanceOf(ConversionException.class);\n    }\n\n    @Test\n    public void should_reject_deserialization_of_gadget_chain_types() {\n        // given: XML payload referencing a denied gadget chain type\n        String maliciousXml = \"<root>\"\n                + \"<javax.script.ScriptEngineManager/>\"\n                + \"</root>\";\n\n        // when:\n        Throwable thrown = catchThrowable(() -> xmlConverter.fromXML(maliciousXml));\n\n        // then:\n        assertThat(thrown)\n                .isInstanceOf(BonitaRuntimeException.class)\n                .hasCauseInstanceOf(ForbiddenClassException.class);\n    }\n\n    @Test\n    public void should_reject_deserialization_of_java_net_URL() {\n        // given: XML payload containing a java.net.URL element (URLDNS gadget chain)\n        String maliciousXml = \"<root>\"\n                + \"<java.net.URL>\"\n                + \"<protocol>http</protocol>\"\n                + \"<host>attacker.example.com</host>\"\n                + \"<port>80</port>\"\n                + \"<file>/</file>\"\n                + \"</java.net.URL>\"\n                + \"</root>\";\n\n        // when:\n        Throwable thrown = catchThrowable(() -> xmlConverter.fromXML(maliciousXml));\n\n        // then:\n        assertThat(thrown)\n                .isInstanceOf(BonitaRuntimeException.class)\n                .hasCauseInstanceOf(ForbiddenClassException.class);\n    }\n\n    @Test\n    public void should_use_custom_deny_list_from_system_property() {\n        try {\n            // given: custom deny list via system property\n            XmlConverter.reset();\n            System.setProperty(\"bonita.xstream.deny.packages\", \"java.awt.**\");\n\n            // when: attempting to deserialize a type matching the custom deny pattern\n            Throwable thrown = catchThrowable(() -> new XmlConverter().fromXML(\"<root><java.awt.Color/></root>\"));\n\n            // then:\n            assertThat(thrown)\n                    .isInstanceOf(BonitaRuntimeException.class)\n                    .hasCauseInstanceOf(ForbiddenClassException.class);\n        } finally {\n            System.clearProperty(\"bonita.xstream.deny.packages\");\n            XmlConverter.reset();\n        }\n    }\n\n    @Test\n    public void should_toXml_deserialize_entity() throws IOException {\n        //given:\n        BusinessDataDefinitionImpl businessDataDefinition = new BusinessDataDefinitionImpl(\"my name\", null);\n        businessDataDefinition.setId(12L);\n        businessDataDefinition.setDescription(\"my description\");\n\n        //when:\n        String xml = xmlConverter.toXML(businessDataDefinition);\n\n        //then:\n        assertThat(xml).isEqualTo(\"<object-stream>\\n\" +\n                \"  <org.bonitasoft.engine.bpm.businessdata.impl.BusinessDataDefinitionImpl>\\n\" +\n                \"    <id>12</id>\\n\" +\n                \"    <name>my name</name>\\n\" +\n                \"    <description>my description</description>\\n\" +\n                \"    <multiple>false</multiple>\\n\" +\n                \"  </org.bonitasoft.engine.bpm.businessdata.impl.BusinessDataDefinitionImpl>\\n\" +\n                \"</object-stream>\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-client/src/test/java/org/bonitasoft/engine/bar/BusinessArchiveTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bar;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertTrue;\nimport static org.junit.Assert.fail;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.nio.file.Files;\nimport java.util.Arrays;\nimport java.util.Iterator;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.actor.ActorDefinition;\nimport org.bonitasoft.engine.bpm.bar.BarResource;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveFactory;\nimport org.bonitasoft.engine.bpm.bar.InvalidBusinessArchiveFormatException;\nimport org.bonitasoft.engine.bpm.bar.ProcessDefinitionBARContribution;\nimport org.bonitasoft.engine.bpm.bar.actorMapping.ActorMapping;\nimport org.bonitasoft.engine.bpm.bar.form.model.FormMappingDefinition;\nimport org.bonitasoft.engine.bpm.bar.form.model.FormMappingModel;\nimport org.bonitasoft.engine.bpm.connector.ConnectorDefinition;\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.bpm.contract.Type;\nimport org.bonitasoft.engine.bpm.data.DataDefinition;\nimport org.bonitasoft.engine.bpm.data.TextDataDefinition;\nimport org.bonitasoft.engine.bpm.document.DocumentDefinition;\nimport org.bonitasoft.engine.bpm.flownode.ActivityDefinition;\nimport org.bonitasoft.engine.bpm.flownode.AutomaticTaskDefinition;\nimport org.bonitasoft.engine.bpm.flownode.BoundaryEventDefinition;\nimport org.bonitasoft.engine.bpm.flownode.CallActivityDefinition;\nimport org.bonitasoft.engine.bpm.flownode.CatchMessageEventTriggerDefinition;\nimport org.bonitasoft.engine.bpm.flownode.EndEventDefinition;\nimport org.bonitasoft.engine.bpm.flownode.GatewayType;\nimport org.bonitasoft.engine.bpm.flownode.MultiInstanceLoopCharacteristics;\nimport org.bonitasoft.engine.bpm.flownode.SendTaskDefinition;\nimport org.bonitasoft.engine.bpm.flownode.StandardLoopCharacteristics;\nimport org.bonitasoft.engine.bpm.flownode.ThrowMessageEventTriggerDefinition;\nimport org.bonitasoft.engine.bpm.flownode.TimerType;\nimport org.bonitasoft.engine.bpm.flownode.UserTaskDefinition;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.impl.AutomaticTaskDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.CallActivityBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.CatchMessageEventTriggerDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.ContractDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.MultiInstanceLoopCharacteristicsBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.SendTaskDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.ThrowMessageEventTriggerBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.form.FormMappingTarget;\nimport org.bonitasoft.engine.form.FormMappingType;\nimport org.bonitasoft.engine.io.IOUtil;\nimport org.bonitasoft.engine.operation.LeftOperand;\nimport org.bonitasoft.engine.operation.LeftOperandBuilder;\nimport org.bonitasoft.engine.operation.Operation;\nimport org.bonitasoft.engine.operation.OperationBuilder;\nimport org.bonitasoft.engine.operation.OperatorType;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.rules.TemporaryFolder;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n * @author Emmanuel Duchastenier\n */\npublic class BusinessArchiveTest {\n\n    private static final String ASSIGN_OPERATOR = \"=\";\n\n    private File tempFolder;\n\n    private File barFile;\n\n    @Rule\n    public TemporaryFolder temporaryFolder = new TemporaryFolder();\n    @Rule\n    public ExpectedException expectedEx = ExpectedException.none();\n\n    @Before\n    public void before() throws IOException {\n        tempFolder = temporaryFolder.newFolder();\n        this.barFile = File.createTempFile(\"barFile\", \".bar\", tempFolder);\n        barFile.delete();\n    }\n\n    @Test\n    public void should_be_able_to_read_BusinessArchive_with_any_hash() throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyBOSProcess\", \"1.0\");\n        final DesignProcessDefinition process = processDefinitionBuilder.done();\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(process).done();\n        BusinessArchiveFactory.writeBusinessArchiveToFolder(businessArchive, tempFolder);\n        IOUtil.writeContentToFile(\"random process infos\", getFile(\"process-infos.txt\"));\n        BusinessArchiveFactory.readBusinessArchive(tempFolder);\n    }\n\n    private File getFile(final String fileName) {\n        return new File(tempFolder, fileName);\n    }\n\n    @Test\n    public void createBusinessArchiveFolder() throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcess\", \"1.0\");\n        final DesignProcessDefinition process = processDefinitionBuilder.done();\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(process).done();\n        BusinessArchiveFactory.writeBusinessArchiveToFolder(businessArchive, tempFolder);\n        assertTrue(tempFolder.exists());\n        assertTrue(tempFolder.isDirectory());\n        final File file = getFile(ProcessDefinitionBARContribution.PROCESS_DEFINITION_XML);\n        assertTrue(file.exists());\n        assertFalse(file.isDirectory());\n    }\n\n    @Test\n    public void createBusinessArchiveFileFromFolder() throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcess\", \"1.0\");\n        final DesignProcessDefinition process = processDefinitionBuilder.done();\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(process).done();\n        BusinessArchiveFactory.writeBusinessArchiveToFolder(businessArchive, tempFolder);\n\n        BusinessArchiveFactory.businessArchiveFolderToFile(barFile, tempFolder.getAbsolutePath());\n        assertTrue(barFile.exists());\n\n        final InputStream inputStream = new FileInputStream(barFile);\n        final BusinessArchive businessArchive2;\n        try {\n            businessArchive2 = BusinessArchiveFactory.readBusinessArchive(inputStream);\n        } finally {\n            inputStream.close();\n        }\n        final ProcessDefinition result = businessArchive2.getProcessDefinition();\n\n        assertEquals(process, result);\n    }\n\n    @Test\n    public void createBusinessArchiveFromFile() throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcess\", \"1.0\");\n        final DesignProcessDefinition process = processDefinitionBuilder.done();\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(process).done();\n        BusinessArchiveFactory.writeBusinessArchiveToFolder(businessArchive, tempFolder);\n\n        BusinessArchiveFactory.businessArchiveFolderToFile(barFile, tempFolder.getAbsolutePath());\n        assertTrue(barFile.exists());\n        final BusinessArchive businessArchive2 = BusinessArchiveFactory.readBusinessArchive(barFile);\n        final ProcessDefinition result = businessArchive2.getProcessDefinition();\n\n        assertEquals(process, result);\n    }\n\n    @Test(expected = InvalidBusinessArchiveFormatException.class)\n    public void readInvalidBusinessArchive() throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcess\", \"1.0\");\n        final DesignProcessDefinition process = processDefinitionBuilder.done();\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(process).done();\n        BusinessArchiveFactory.writeBusinessArchiveToFolder(businessArchive, tempFolder);\n        final File file = getFile(ProcessDefinitionBARContribution.PROCESS_DEFINITION_XML);\n        file.delete();\n        file.createNewFile();\n        final FileWriter fileWriter = new FileWriter(file);\n        fileWriter.write(\"test\");\n        fileWriter.flush();\n        fileWriter.close();\n\n        BusinessArchiveFactory.readBusinessArchive(tempFolder);\n    }\n\n    @Test(expected = IOException.class)\n    public void createBusinessArchiveFolderWithInvalidPath() throws Exception {\n        BusinessArchiveFactory.readBusinessArchive(new File(\"$$$an invalidPath@//\\\\ùù%%%\"));\n    }\n\n    @Test\n    public void createBusinessArchiveWithProcessDefinition() throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcess\", \"1.0\");\n        final DesignProcessDefinition process = processDefinitionBuilder.done();\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(process).done();\n        final ProcessDefinition result = businessArchive.getProcessDefinition();\n\n        assertEquals(process, result);\n    }\n\n    @Test(expected = InvalidBusinessArchiveFormatException.class)\n    public void createEmptyBusinessArchive() throws Exception {\n        new BusinessArchiveBuilder().createNewBusinessArchive().done();\n    }\n\n    @Test\n    public void exportBusinessArchiveAsFile() throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcess\", \"1.0\");\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(processDefinitionBuilder.done())\n                .done();\n        BusinessArchiveFactory.writeBusinessArchiveToFile(businessArchive, barFile);\n        assertTrue(barFile.exists());\n        assertFalse(barFile.isDirectory());\n    }\n\n    @Test(expected = IOException.class)\n    public void exportBusinessArchiveAsFileOnExistingFile() throws Exception {\n        barFile = temporaryFolder.newFile();\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcess\", \"1.0\");\n        final DesignProcessDefinition process = processDefinitionBuilder.done();\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(process).done();\n        BusinessArchiveFactory.writeBusinessArchiveToFile(businessArchive, barFile);\n    }\n\n    @Test\n    public void readBusinessArchive() throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcess\", \"1.0\");\n        final DesignProcessDefinition process = processDefinitionBuilder.done();\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(process).done();\n        BusinessArchiveFactory.writeBusinessArchiveToFile(businessArchive, barFile);\n\n        final InputStream inputStream = new FileInputStream(barFile);\n        final BusinessArchive businessArchive2;\n        try {\n            businessArchive2 = BusinessArchiveFactory.readBusinessArchive(inputStream);\n        } finally {\n            inputStream.close();\n        }\n\n        final ProcessDefinition result = businessArchive2.getProcessDefinition();\n        assertEquals(process, result);\n    }\n\n    @Test(expected = InvalidBusinessArchiveFormatException.class)\n    public void importOldBusinessArchiveFail() throws Exception {\n        final InputStream resourceAsStream = this.getClass().getResourceAsStream(\"MyProcess--1.0.bar\");\n        try {\n            BusinessArchiveFactory.readBusinessArchive(resourceAsStream);\n        } finally {\n            resourceAsStream.close();\n        }\n    }\n\n    @Test(expected = InvalidBusinessArchiveFormatException.class)\n    public void importOldBusinessArchiveFileFail() throws Exception {\n        final InputStream inputStream = this.getClass().getResourceAsStream(\"MyProcess--1.0.bar\");\n        final OutputStream out = new FileOutputStream(barFile);\n        try {\n            final byte buf[] = new byte[1024];\n            int len;\n            while ((len = inputStream.read(buf)) > 0) {\n                out.write(buf, 0, len);\n            }\n        } finally {\n            out.close();\n            inputStream.close();\n        }\n\n        BusinessArchiveFactory.readBusinessArchive(barFile);\n    }\n\n    @Test\n    public void addingEmptyDocument_to_BusinessArchive_should_throw_exception() throws Exception {\n        final BusinessArchiveBuilder builder = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(new ProcessDefinitionBuilder().createNewInstance(\"EmptyDoc\", \"7.3\").done());\n\n        expectedEx.expect(IllegalArgumentException.class);\n        expectedEx.expectMessage(\n                \"You are trying to add file documents/resume.pdf with empty content into the BusinessArchive (bar file).\"\n                        + \" Either add content to this file, or remove it from the resources.\");\n\n        builder.addDocumentResource(new BarResource(\"resume.pdf\", new byte[] {}));\n    }\n\n    @Test\n    public void addingEmptyResource_to_BusinessArchive_should_throw_exception() throws Exception {\n        final BusinessArchiveBuilder builder = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(new ProcessDefinitionBuilder().createNewInstance(\"Dummy\", \"11.01\").done());\n\n        expectedEx.expect(IllegalArgumentException.class);\n        expectedEx.expectMessage(\n                \"You are trying to add file resources/dummy.txt with empty content into the BusinessArchive (bar file).\"\n                        + \" Either add content to this file, or remove it from the resources.\");\n\n        builder.addExternalResource(new BarResource(\"dummy.txt\", new byte[] {}));\n    }\n\n    @Test\n    public void addingNullResource_to_BusinessArchive_should_throw_exception() throws Exception {\n        final BusinessArchiveBuilder builder = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(new ProcessDefinitionBuilder().createNewInstance(\"Dummy\", \"11.02\").done());\n\n        expectedEx.expect(IllegalArgumentException.class);\n        expectedEx.expectMessage(\n                \"You are trying to add file resources/dummy.txt with empty content into the BusinessArchive (bar file).\"\n                        + \" Either add content to this file, or remove it from the resources.\");\n\n        builder.addExternalResource(new BarResource(\"dummy.txt\", null));\n    }\n\n    @Test\n    public void manageBusinessArchiveResources() throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcess\", \"1.0\");\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(processDefinitionBuilder.done())\n                .addExternalResource(new BarResource(\"dummy.txt\", new byte[] { 'a', 'b', 'c', 'd' })).done();\n\n        // Add a resource to the biz archive:\n        BusinessArchiveFactory.writeBusinessArchiveToFile(businessArchive, barFile);\n\n        // read from the file\n        final BusinessArchive readBusinessArchive = BusinessArchiveFactory.readBusinessArchive(barFile);\n        assertTrue(\"Added resource not found in BusinessArchive\",\n                readBusinessArchive.getResources().containsKey(\"resources/dummy.txt\"));\n    }\n\n    @Test\n    public void formMappingInBarShouldBeWrittenAndReadProperly() throws Exception {\n        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MethCookingPlanning\", \"Season 5\").done();\n\n        final FormMappingModel formMappingModel = new FormMappingModel();\n        formMappingModel.addFormMapping(new FormMappingDefinition(\"/?myPageTokenID\", FormMappingType.PROCESS_START,\n                FormMappingTarget.INTERNAL));\n        formMappingModel.addFormMapping(new FormMappingDefinition(\"someExternalPage\", FormMappingType.TASK,\n                FormMappingTarget.URL, \"requestTask\"));\n\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(designProcessDefinition)\n                .setFormMappings(formMappingModel).done();\n\n        BusinessArchiveFactory.writeBusinessArchiveToFile(businessArchive, barFile);\n\n        final BusinessArchive readBusinessArchive = BusinessArchiveFactory.readBusinessArchive(barFile);\n        assertThat(readBusinessArchive.getFormMappingModel().getFormMappings())\n                .as(\"Form Mapping should be found in BusinessArchive\").hasSize(2);\n    }\n\n    /*\n     * Changed to work with the new system of actorMapping storage. Note that the test may have lost his purpose\n     * since you cannot give the BusinessArchive garbage as an actorMapping, you need an actual actorMapping class\n     * object\n     */\n    @Test\n    public void putActorMappingInBar() throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"ProductionPlanning\", \"3.1\");\n        final DesignProcessDefinition designProcessDefinition = processDefinitionBuilder.done();\n        ActorMapping actorMapping = new ActorMapping();\n        // Add a resource to the biz archive:\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(designProcessDefinition)\n                .setActorMapping(actorMapping).done();\n\n        BusinessArchiveFactory.writeBusinessArchiveToFile(businessArchive, barFile);\n\n        // read from the file\n        final BusinessArchive readBusinessArchive = BusinessArchiveFactory.readBusinessArchive(barFile);\n        // final ProcessDefinition processDefinition = processAPI.deploy(readBusinessArchive);\n        assertEquals(actorMapping, readBusinessArchive.getActorMapping());\n    }\n\n    @Test\n    public void readProcessFromBusinessArchive() throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcess\", \"1.0\");\n        processDefinitionBuilder.addDocumentDefinition(\"testDoc\").addContentFileName(\"testFile.txt\")\n                .addFile(\"testFile.txt\").addDescription(\"desc\")\n                .addMimeType(\"text/plain\")\n                .addInitialValue(new ExpressionBuilder().createConstantStringExpression(\"plop\"));\n        processDefinitionBuilder.addDocumentDefinition(\"testDocUrl\").addContentFileName(\"testFile.txt\")\n                .addUrl(\"http://test.com/testFile.txt\")\n                .addDescription(\"desc\");\n        processDefinitionBuilder.addDescription(\"a 2-lines\\ndescription\");\n        processDefinitionBuilder.addDisplayDescription(\n                \"A very good and clean description that will be displayed in user xp\\nwith multilines\");\n        processDefinitionBuilder.addDisplayName(\"Truck Handling Process\");\n        processDefinitionBuilder.addActor(\"Truck Driver\").addDescription(\"A man that is driving big trucks\");\n        processDefinitionBuilder.setActorInitiator(\"Truck Driver\");\n        processDefinitionBuilder.addStartEvent(\"start1\").addTimerEventTriggerDefinition(TimerType.CYCLE,\n                new ExpressionBuilder().createConstantStringExpression(\"*/3 * * * * ?\"));\n        // No Java operation, so empty string passed:\n        processDefinitionBuilder\n                .addAutomaticTask(\"auto1\")\n                .addOperation(new LeftOperandBuilder().createNewInstance().setName(\"testData\").done(),\n                        OperatorType.ASSIGNMENT, ASSIGN_OPERATOR, null,\n                        new ExpressionBuilder().createConstantBooleanExpression(true))\n                .addConnector(\"conn1\", \"connId1\", \"1.0.0\", ConnectorEvent.ON_FINISH).ignoreError();\n        processDefinitionBuilder\n                .addManualTask(\"manual1\", \"Truck Driver\")\n                .addPriority(\"urgent\")\n                .addExpectedDuration(5000000)\n                .addDescription(\"description of manual task1\")\n                .addDisplayDescription(\n                        new ExpressionBuilder().createConstantStringExpression(\n                                \"this is an urgent task that will take more than one hour to be done\"))\n                .addDisplayName(new ExpressionBuilder().createConstantStringExpression(\"Urgent task\"))\n                .addDisplayDescriptionAfterCompletion(\n                        new ExpressionBuilder().createConstantStringExpression(\"this is a done task that was urgent\"));\n        processDefinitionBuilder.addIntermediateCatchEvent(\"intermediateTimerEvent\").addTimerEventTriggerDefinition(\n                TimerType.DURATION,\n                new ExpressionBuilder().createConstantLongExpression(1000));\n        final UserTaskDefinitionBuilder addUserTask = processDefinitionBuilder.addUserTask(\"user1\", \"Truck Driver\");\n        final String ERROR_CODE = \"errorToBeCaught\";\n        addUserTask.addConnector(\"conn2\", \"connId2\", \"1.0.0\", ConnectorEvent.ON_ENTER)\n                .throwErrorEventWhenFailed(ERROR_CODE);\n        addUserTask.addUserFilter(\"myUserFilter\", \"org.bonitasoft.test.user.filter\", \"1.0.0\");\n        addUserTask.addData(\"testData\", String.class.getName(), null);\n        addUserTask.addShortTextData(\"shortText\", new ExpressionBuilder().createConstantStringExpression(\"shortText\"));\n        addUserTask.addLongTextData(\"longText\", new ExpressionBuilder().createConstantStringExpression(\"longText\"));\n        processDefinitionBuilder.addGateway(\"gate1\", GatewayType.INCLUSIVE).addDefaultTransition(\"user1\");\n        processDefinitionBuilder.addEndEvent(\"end1\");\n        processDefinitionBuilder.addTransition(\"start1\", \"auto1\",\n                new ExpressionBuilder().createConstantBooleanExpression(true));\n        processDefinitionBuilder.addTransition(\"auto1\", \"intermediateTimerEvent\");\n        processDefinitionBuilder.addTransition(\"intermediateTimerEvent\", \"user1\");\n        processDefinitionBuilder.addTransition(\"user1\", \"gate1\");\n        processDefinitionBuilder.addTransition(\"user1\", \"end1\");\n        processDefinitionBuilder\n                .addConnector(\"conn3\", \"connId3\", \"1.0.0\", ConnectorEvent.ON_FINISH)\n                .ignoreError()\n                .addInput(\"input1\", new ExpressionBuilder().createConstantBooleanExpression(true))\n                .addOutput(new LeftOperandBuilder().createNewInstance().setName(\"testData\").done(),\n                        OperatorType.ASSIGNMENT, ASSIGN_OPERATOR, null,\n                        new ExpressionBuilder().createConstantBooleanExpression(true));\n        processDefinitionBuilder\n                .addData(\"myData\", \"java.lang.Boolean\", new ExpressionBuilder().createConstantBooleanExpression(true))\n                .addDescription(\"My boolean data\");\n        final DesignProcessDefinition process = processDefinitionBuilder.done();\n\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(process)\n                .addDocumentResource(new BarResource(\"testFile.txt\", new byte[] { 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }))\n                .done();\n        BusinessArchiveFactory.writeBusinessArchiveToFile(businessArchive, barFile);\n\n        final BusinessArchive businessArchive2;\n        try (InputStream inputStream = new FileInputStream(barFile)) {\n            businessArchive2 = BusinessArchiveFactory.readBusinessArchive(inputStream);\n        }\n\n        final DesignProcessDefinition result = businessArchive2.getProcessDefinition();\n        final List<DocumentDefinition> documentDefinitions = result.getFlowElementContainer().getDocumentDefinitions();\n        final DocumentDefinition testDoc1 = documentDefinitions.get(0);\n        assertEquals(\"testDoc\", testDoc1.getName());\n        assertEquals(\"desc\", testDoc1.getDescription());\n        assertEquals(\"testFile.txt\", testDoc1.getFile());\n        assertEquals(\"text/plain\", testDoc1.getContentMimeType());\n        assertEquals(\"plop\", testDoc1.getInitialValue().getContent());\n        final DocumentDefinition testDoc2 = documentDefinitions.get(1);\n        assertEquals(\"testDocUrl\", testDoc2.getName());\n        assertEquals(\"desc\", testDoc2.getDescription());\n        assertEquals(\"http://test.com/testFile.txt\", testDoc2.getUrl());\n        assertEquals(\"application/octet-stream\", testDoc2.getContentMimeType());\n        assertEquals(process.getName(), result.getName());\n        assertEquals(process.getVersion(), result.getVersion());\n        assertEquals(process.getDescription(), result.getDescription());\n        assertEquals(process.getDisplayName(), result.getDisplayName());\n        assertEquals(process.getDisplayDescription(), result.getDisplayDescription());\n        assertEquals(process.getFlowElementContainer().getStartEvents().size(),\n                result.getFlowElementContainer().getStartEvents().size());\n        assertEquals(process.getFlowElementContainer().getStartEvents().get(0),\n                result.getFlowElementContainer().getStartEvents().get(0));\n        assertEquals(process.getFlowElementContainer().getIntermediateCatchEvents().size(),\n                result.getFlowElementContainer().getIntermediateCatchEvents()\n                        .size());\n        assertEquals(process.getFlowElementContainer().getIntermediateCatchEvents().get(0),\n                result.getFlowElementContainer().getIntermediateCatchEvents()\n                        .get(0));\n        assertEquals(process.getFlowElementContainer().getEndEvents().size(),\n                result.getFlowElementContainer().getEndEvents().size());\n        assertEquals(process.getFlowElementContainer().getEndEvents().get(0),\n                result.getFlowElementContainer().getEndEvents().get(0));\n        assertEquals(process.getActorsList().size(), result.getActorsList().size());\n        assertEquals(process.getActorsList().iterator().next(), result.getActorsList().iterator().next());\n        assertEquals(process.getFlowElementContainer().getActivities().size(),\n                result.getFlowElementContainer().getActivities().size());\n        final Iterator<ActivityDefinition> iterator = result.getFlowElementContainer().getActivities().iterator();\n        final Iterator<ActivityDefinition> iterator2 = process.getFlowElementContainer().getActivities().iterator();\n        final ActivityDefinition procAct1 = iterator2.next();\n        final ActivityDefinition procAct2 = iterator2.next();\n        final ActivityDefinition procAct3 = iterator2.next();\n        ActivityDefinition orgAuto1 = null;\n        ActivityDefinition orgUser1 = null;\n        ActivityDefinition orgManual1 = null;\n        List<ActivityDefinition> asList = Arrays.asList(procAct1, procAct2, procAct3);\n        for (final ActivityDefinition activityDefinition : asList) {\n            if (activityDefinition.getName().equals(\"auto1\")) {\n                orgAuto1 = activityDefinition;\n            } else if (activityDefinition.getName().equals(\"user1\")) {\n                orgUser1 = activityDefinition;\n            } else if (activityDefinition.getName().equals(\"manual1\")) {\n                orgManual1 = activityDefinition;\n            }\n        }\n        final ActivityDefinition resAct1 = iterator.next();\n        final ActivityDefinition resAct2 = iterator.next();\n        final ActivityDefinition resAct3 = iterator.next();\n        ActivityDefinition auto1 = null;\n        ActivityDefinition user1 = null;\n        ActivityDefinition manual1 = null;\n        asList = Arrays.asList(resAct1, resAct2, resAct3);\n        for (final ActivityDefinition activityDefinition : asList) {\n            if (activityDefinition.getName().equals(\"auto1\")) {\n                auto1 = activityDefinition;\n            } else if (activityDefinition.getName().equals(\"user1\")) {\n                user1 = activityDefinition;\n            } else if (activityDefinition.getName().equals(\"manual1\")) {\n                manual1 = activityDefinition;\n            }\n        }\n        assertNotNull(\"user task not found\", user1);\n        assertNotNull(\"auto task not found\", auto1);\n        assertNotNull(\"manual task not found\", manual1);\n        assertEquals(\"user task not same\", orgUser1, user1);\n        assertEquals(\"auto task not same\", orgAuto1, auto1);\n        assertEquals(\"manual task not same\", orgManual1, manual1);\n        assertEquals(3, user1.getDataDefinitions().size());\n        final DataDefinition dataDefinition1 = user1.getDataDefinitions().get(0);\n        final DataDefinition dataDefinition2 = user1.getDataDefinitions().get(1);\n        final DataDefinition dataDefinition3 = user1.getDataDefinitions().get(2);\n        assertEquals(\"testData\", dataDefinition1.getName());\n        assertEquals(\"shortText\", dataDefinition2.getName());\n        assertEquals(\"longText\", dataDefinition3.getName());\n        assertTrue(dataDefinition2 instanceof TextDataDefinition);\n        assertTrue(dataDefinition3 instanceof TextDataDefinition);\n        assertFalse(((TextDataDefinition) dataDefinition2).isLongText());\n        assertTrue(((TextDataDefinition) dataDefinition3).isLongText());\n        assertEquals(1, auto1.getOperations().size());\n        assertEquals(auto1.getOperations().get(0), auto1.getOperations().get(0));\n        assertTrue(procAct2.equals(resAct1) || procAct2.equals(resAct2) || procAct2.equals(resAct3));\n        assertEquals(process.getFlowElementContainer().getGatewaysList().size(),\n                result.getFlowElementContainer().getGatewaysList().size());\n        assertEquals(process.getFlowElementContainer().getGatewaysList().iterator().next(),\n                result.getFlowElementContainer().getGatewaysList().iterator()\n                        .next());\n        assertEquals(process.getFlowElementContainer().getTransitions().size(),\n                result.getFlowElementContainer().getTransitions().size());\n\n        assertThat(result.getProcessContainer().getFlowNode(\"start1\").getOutgoingTransitions().get(0).getCondition()\n                .getContent())\n                .as(\"the condition on the transition was not kept\").isEqualTo(\"true\");\n\n        assertEquals(process.getFlowElementContainer().getConnectors().size(),\n                result.getFlowElementContainer().getConnectors().size());\n        boolean connectorWithInputOutputOk = false;\n        for (final ConnectorDefinition connector : result.getFlowElementContainer().getConnectors()) {\n            final Operation operation = connector.getOutputs().get(0);\n            if (\"conn3\".equals(connector.getName()) && \"true\".equals(connector.getInputs().get(\"input1\").getContent())\n                    && \"testData\".equals(operation.getLeftOperand().getName())\n                    && OperatorType.ASSIGNMENT.equals(operation.getType())\n                    && ASSIGN_OPERATOR.equals(operation.getOperator())\n                    && \"true\".equals(operation.getRightOperand().getContent())) {\n                connectorWithInputOutputOk = true;\n                break;\n            }\n        }\n        assertTrue(\"the input/output on the connector was not kept\", connectorWithInputOutputOk);\n        final Iterator<ConnectorDefinition> resultConnectors = result.getFlowElementContainer().getConnectors()\n                .iterator();\n        for (final ConnectorDefinition connectorDef : process.getFlowElementContainer().getConnectors()) {\n            final ConnectorDefinition resultConn = resultConnectors.next();\n            assertEquals(connectorDef, resultConn);\n        }\n        assertEquals(process.getFlowElementContainer().getDataDefinitions().size(),\n                result.getFlowElementContainer().getDataDefinitions().size());\n        assertEquals(process.getFlowElementContainer().getDataDefinitions().iterator().next(),\n                result.getFlowElementContainer().getDataDefinitions().iterator()\n                        .next());\n        final ActivityDefinition sourceActivity = process.getFlowElementContainer().getActivity(\"user1\");\n        final ActivityDefinition resultActivity = result.getFlowElementContainer().getActivity(\"user1\");\n        assertTrue(resultActivity instanceof UserTaskDefinition);\n        assertEquals(((UserTaskDefinition) sourceActivity).getUserFilter(),\n                ((UserTaskDefinition) resultActivity).getUserFilter());\n\n        final List<ActivityDefinition> processActivities = process.getFlowElementContainer().getActivities();\n        final Iterator<ActivityDefinition> itResultActs = result.getFlowElementContainer().getActivities().iterator();\n        for (final ActivityDefinition processActivity : processActivities) {\n            final ActivityDefinition resultAct = itResultActs.next();\n            final List<ConnectorDefinition> processActivityConnectors = processActivity.getConnectors();\n            final Iterator<ConnectorDefinition> itResultCon = resultAct.getConnectors().iterator();\n            for (final ConnectorDefinition connectorDefinition : processActivityConnectors) {\n                final ConnectorDefinition nextResultConnector = itResultCon.next();\n                assertEquals(connectorDefinition.getFailAction(), nextResultConnector.getFailAction());\n            }\n        }\n    }\n\n    @Test\n    public void readProcessWithContract() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"contract\", \"1.0\");\n        builder.addActor(\"myActor\");\n        final ContractDefinitionBuilder contractDefinitionBuilder = builder.addUserTask(\"step1\", \"myActor\")\n                .addContract();\n        createContract(contractDefinitionBuilder);\n        createContract(builder.addContract());\n        final DesignProcessDefinition process = builder.getProcess();\n        final DesignProcessDefinition result = getDesignProcessDefinition(builder);\n\n        assertThat(process).isEqualTo(result);\n    }\n\n    void createContract(final ContractDefinitionBuilder contractDefinitionBuilder) {\n        contractDefinitionBuilder.addInput(\"numberOfDays\", Type.INTEGER, null).addConstraint(\"Mystical constraint\",\n                \"true\", null, \"numberOfDays\");\n        contractDefinitionBuilder.addComplexInput(\"complex\", \"a complex input\")\n                .addInput(\"childText\", Type.TEXT, \"a text simple input\")\n                .addInput(\"childDecimal\", Type.DECIMAL, \"a decimal simple input\");\n    }\n\n    @Test\n    public void readProcessWithMessageEventsFromBusinessArchive() throws Exception {\n        final Expression conditionKey = new ExpressionBuilder().createConstantStringExpression(\"coditionKey\");\n        final Expression trueExpression = new ExpressionBuilder().createConstantBooleanExpression(true);\n        final Expression displayNameExpression = new ExpressionBuilder().createConstantStringExpression(\"dataToSend\");\n        final LeftOperandBuilder leftOperandBuilder = new LeftOperandBuilder();\n        leftOperandBuilder.createNewInstance().setName(\"var1\");\n        final OperationBuilder opb = new OperationBuilder();\n        opb.createNewInstance().setOperator(ASSIGN_OPERATOR).setRightOperand(trueExpression)\n                .setType(OperatorType.ASSIGNMENT)\n                .setLeftOperand(leftOperandBuilder.done());\n\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"MyProcess\", \"1.0\");\n        builder.addBooleanData(\"var1\", null);\n        builder.addStartEvent(\"start1\").addMessageEventTrigger(\"m1\").addOperation(opb.done());\n        builder.addAutomaticTask(\"auto1\");\n        final CatchMessageEventTriggerDefinitionBuilder catchMessageEventTriggerDefinitionBuilder = builder\n                .addIntermediateCatchEvent(\"waitForMessage\")\n                .addMessageEventTrigger(\"m2\");\n        catchMessageEventTriggerDefinitionBuilder.addCorrelation(conditionKey, trueExpression);\n        catchMessageEventTriggerDefinitionBuilder.addOperation(opb.done());\n        // create expression for target process/flowNode\n        Expression targetProcess = new ExpressionBuilder().createDataExpression(\"p3\", String.class.getName());\n        final Expression receiveMessage = new ExpressionBuilder().createDataExpression(\"receiveMessage\",\n                String.class.getName());\n        builder.addIntermediateThrowEvent(\"sendMessage\").addMessageEventTrigger(\"m4\", targetProcess, receiveMessage);\n        targetProcess = new ExpressionBuilder().createConstantStringExpression(\"p2\");\n        final Expression waitMessage = new ExpressionBuilder().createConstantStringExpression(\"waitMessage\");\n        final ThrowMessageEventTriggerBuilder throwMessageEventTriggerBuilder = builder.addEndEvent(\"end1\")\n                .addMessageEventTrigger(\"m2\", targetProcess,\n                        waitMessage);\n        throwMessageEventTriggerBuilder.addCorrelation(conditionKey, trueExpression);\n        throwMessageEventTriggerBuilder.addMessageContentExpression(displayNameExpression, trueExpression);\n        builder.addTransition(\"start1\", \"auto1\", trueExpression);\n        builder.addTransition(\"auto1\", \"waitForMessage\");\n        builder.addTransition(\"waitForMessage\", \"sendMessage\");\n        builder.addTransition(\"sendMessage\", \"end1\");\n\n        final DesignProcessDefinition process = builder.getProcess();\n        final DesignProcessDefinition result = getDesignProcessDefinition(builder);\n\n        checkProcessForMessagesEvents(process, result);\n    }\n\n    @Test(expected = InvalidProcessDefinitionException.class)\n    public void tooMuchCorrelationOnCatchMessage() throws Exception {\n        final Expression conditionKey = new ExpressionBuilder().createConstantStringExpression(\"coditionKey\");\n        final Expression trueExpression = new ExpressionBuilder().createConstantBooleanExpression(true);\n        final Expression displayNameExpression = new ExpressionBuilder().createConstantStringExpression(\"dataToSend\");\n\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcess\", \"1.0\");\n        processDefinitionBuilder.addStartEvent(\"start1\").addMessageEventTrigger(\"m1\");\n        processDefinitionBuilder.addAutomaticTask(\"auto1\");\n        final CatchMessageEventTriggerDefinitionBuilder catchMessageEventTriggerDefinitionBuilder = processDefinitionBuilder\n                .addIntermediateCatchEvent(\n                        \"waitForMessage\")\n                .addMessageEventTrigger(\"m2\");\n        catchMessageEventTriggerDefinitionBuilder.addCorrelation(conditionKey, trueExpression);\n        catchMessageEventTriggerDefinitionBuilder.addCorrelation(conditionKey, trueExpression);\n        catchMessageEventTriggerDefinitionBuilder.addCorrelation(conditionKey, trueExpression);\n        catchMessageEventTriggerDefinitionBuilder.addCorrelation(conditionKey, trueExpression);\n        catchMessageEventTriggerDefinitionBuilder.addCorrelation(conditionKey, trueExpression);\n        catchMessageEventTriggerDefinitionBuilder.addCorrelation(conditionKey, trueExpression);\n        // create expression for target process/flowNode\n        Expression targetProcess = new ExpressionBuilder().createDataExpression(\"p3\", String.class.getName());\n        final Expression receiveMessage = new ExpressionBuilder().createDataExpression(\"receiveMessage\",\n                String.class.getName());\n        processDefinitionBuilder.addIntermediateThrowEvent(\"sendMessage\").addMessageEventTrigger(\"m4\", targetProcess,\n                receiveMessage);\n        targetProcess = new ExpressionBuilder().createConstantStringExpression(\"p2\");\n        final Expression waitMessage = new ExpressionBuilder().createConstantStringExpression(\"waitMessage\");\n        final ThrowMessageEventTriggerBuilder throwMessageEventTriggerBuilder = processDefinitionBuilder\n                .addEndEvent(\"end1\").addMessageEventTrigger(\"m2\",\n                        targetProcess, waitMessage);\n        throwMessageEventTriggerBuilder.addCorrelation(conditionKey, trueExpression);\n        throwMessageEventTriggerBuilder.addMessageContentExpression(displayNameExpression, trueExpression);\n        processDefinitionBuilder.addTransition(\"start1\", \"auto1\", trueExpression);\n        processDefinitionBuilder.addTransition(\"auto1\", \"waitForMessage\");\n        processDefinitionBuilder.addTransition(\"waitForMessage\", \"sendMessage\");\n        processDefinitionBuilder.addTransition(\"sendMessage\", \"end1\");\n        processDefinitionBuilder.done();\n    }\n\n    @Test(expected = InvalidProcessDefinitionException.class)\n    public void tooMuchCorrelationOnThrowMessage() throws Exception {\n        final Expression conditionKey = new ExpressionBuilder().createConstantStringExpression(\"coditionKey\");\n        final Expression trueExpression = new ExpressionBuilder().createConstantBooleanExpression(true);\n        final Expression displayNameExpression = new ExpressionBuilder().createConstantStringExpression(\"dataToSend\");\n\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcess\", \"1.0\");\n        processDefinitionBuilder.addStartEvent(\"start1\").addMessageEventTrigger(\"m1\");\n        processDefinitionBuilder.addAutomaticTask(\"auto1\");\n        final CatchMessageEventTriggerDefinitionBuilder catchMessageEventTriggerDefinitionBuilder = processDefinitionBuilder\n                .addIntermediateCatchEvent(\n                        \"waitForMessage\")\n                .addMessageEventTrigger(\"m2\");\n        catchMessageEventTriggerDefinitionBuilder.addCorrelation(conditionKey, trueExpression);\n        // create expression for target process/flowNode\n        Expression targetProcess = new ExpressionBuilder().createDataExpression(\"p3\", String.class.getName());\n        final Expression receiveMessage = new ExpressionBuilder().createDataExpression(\"receiveMessage\",\n                String.class.getName());\n        processDefinitionBuilder.addIntermediateThrowEvent(\"sendMessage\").addMessageEventTrigger(\"m4\", targetProcess,\n                receiveMessage);\n        targetProcess = new ExpressionBuilder().createConstantStringExpression(\"p2\");\n        final Expression waitMessage = new ExpressionBuilder().createConstantStringExpression(\"waitMessage\");\n        final ThrowMessageEventTriggerBuilder throwMessageEventTriggerBuilder = processDefinitionBuilder\n                .addEndEvent(\"end1\").addMessageEventTrigger(\"m2\",\n                        targetProcess, waitMessage);\n        throwMessageEventTriggerBuilder.addCorrelation(conditionKey, trueExpression);\n        throwMessageEventTriggerBuilder.addCorrelation(conditionKey, trueExpression);\n        throwMessageEventTriggerBuilder.addCorrelation(conditionKey, trueExpression);\n        throwMessageEventTriggerBuilder.addCorrelation(conditionKey, trueExpression);\n        throwMessageEventTriggerBuilder.addCorrelation(conditionKey, trueExpression);\n        throwMessageEventTriggerBuilder.addCorrelation(conditionKey, trueExpression);\n        throwMessageEventTriggerBuilder.addMessageContentExpression(displayNameExpression, trueExpression);\n        processDefinitionBuilder.addTransition(\"start1\", \"auto1\", trueExpression);\n        processDefinitionBuilder.addTransition(\"auto1\", \"waitForMessage\");\n        processDefinitionBuilder.addTransition(\"waitForMessage\", \"sendMessage\");\n        processDefinitionBuilder.addTransition(\"sendMessage\", \"end1\");\n        processDefinitionBuilder.done();\n    }\n\n    @Test\n    public void readProcessWithCallActivityFromBusinessArchive() throws Exception {\n        final Expression fromCallerData = new ExpressionBuilder().createDataExpression(\"var1\", Boolean.class.getName());\n        final LeftOperand dataInputLeftOp = new LeftOperandBuilder().createNewInstance(\"data1\").done();\n        final Operation dataInputOperation = getOperation(fromCallerData, dataInputLeftOp);\n\n        final Expression fromCallableElementData = new ExpressionBuilder().createDataExpression(\"data2\",\n                Integer.class.getName());\n        final LeftOperand dataOutputOp = new LeftOperandBuilder().createNewInstance(\"var2\").done();\n        final Operation dataOutputOperation = getOperation(fromCallableElementData, dataOutputOp);\n\n        final Expression targetProcess = new ExpressionBuilder().createConstantStringExpression(\"MyProcess2\");\n        final Expression processVersion = new ExpressionBuilder().createConstantStringExpression(\"1.0\");\n\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"MyProcess\", \"1.0\");\n        builder.addBooleanData(\"var1\", null);\n        builder.addIntegerData(\"var2\", null);\n        builder.addStartEvent(\"start1\");\n        final CallActivityBuilder callActivityBuilder = builder.addCallActivity(\"callActivity\", targetProcess,\n                processVersion);\n        final Expression supportCaseIdExpression = new ExpressionBuilder().createConstantLongExpression(206L);\n        final String supportCaseIdInputName = \"supportCaseId\";\n        callActivityBuilder.addProcessStartContractInput(supportCaseIdInputName, supportCaseIdExpression);\n        callActivityBuilder.addDataInputOperation(dataInputOperation);\n        callActivityBuilder.addDataOutputOperation(dataOutputOperation);\n        builder.addEndEvent(\"end1\");\n        builder.addTransition(\"start1\", \"callActivity\");\n        builder.addTransition(\"callActivity\", \"end1\");\n\n        final DesignProcessDefinition process = builder.getProcess();\n        final DesignProcessDefinition result = getDesignProcessDefinition(builder);\n\n        checkProcessForCallActivity(process, result);\n\n        final CallActivityDefinition callActivity = (CallActivityDefinition) result.getFlowElementContainer()\n                .getActivity(\"callActivity\");\n        assertThat(callActivity.getProcessStartContractInputs().get(supportCaseIdInputName)\n                .isEquivalent(supportCaseIdExpression)).isTrue();\n    }\n\n    @Test\n    public void readProcessWithMultiInstanceFromBusinessArchive() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"ProcessWithMultiInstances\", \"1.0\");\n        builder.addData(\"inputList\", List.class.getName(), null).addData(\"outputList\", List.class.getName(), null);\n        final AutomaticTaskDefinitionBuilder automaticTaskBuilder = builder.addAutomaticTask(\"auto1\");\n        automaticTaskBuilder.addShortTextData(\"input\", null).addShortTextData(\"output\", null);\n        final MultiInstanceLoopCharacteristicsBuilder multiInstance1 = automaticTaskBuilder.addMultiInstance(false,\n                \"inputList\");\n        multiInstance1.addDataInputItemRef(\"input\");\n        multiInstance1.addDataOutputItemRef(\"output\");\n        multiInstance1.addLoopDataOutputRef(\"outputList\");\n        final MultiInstanceLoopCharacteristicsBuilder multiInstance2 = builder.addAutomaticTask(\"auto2\")\n                .addMultiInstance(true,\n                        new ExpressionBuilder().createConstantIntegerExpression(5));\n        multiInstance2.addCompletionCondition(new ExpressionBuilder().createConstantBooleanExpression(false));\n        builder.addAutomaticTask(\"auto3\").addLoop(true, new ExpressionBuilder().createConstantBooleanExpression(true),\n                new ExpressionBuilder().createConstantIntegerExpression(5));\n        final DesignProcessDefinition result = getDesignProcessDefinition(builder);\n\n        final AutomaticTaskDefinition auto1 = (AutomaticTaskDefinition) result.getFlowElementContainer()\n                .getFlowNode(\"auto1\");\n        final MultiInstanceLoopCharacteristics multi1 = (MultiInstanceLoopCharacteristics) auto1\n                .getLoopCharacteristics();\n        assertEquals(false, multi1.isSequential());\n        assertEquals(\"inputList\", multi1.getLoopDataInputRef());\n        assertEquals(\"outputList\", multi1.getLoopDataOutputRef());\n        assertEquals(\"input\", multi1.getDataInputItemRef());\n        assertEquals(\"output\", multi1.getDataOutputItemRef());\n\n        final AutomaticTaskDefinition auto2 = (AutomaticTaskDefinition) result.getFlowElementContainer()\n                .getFlowNode(\"auto2\");\n        final MultiInstanceLoopCharacteristics multi2 = (MultiInstanceLoopCharacteristics) auto2\n                .getLoopCharacteristics();\n        assertEquals(true, multi2.isSequential());\n        assertEquals(\"5\", multi2.getLoopCardinality().getContent());\n        assertEquals(\"false\", multi2.getCompletionCondition().getContent());\n\n        final AutomaticTaskDefinition auto3 = (AutomaticTaskDefinition) result.getFlowElementContainer()\n                .getFlowNode(\"auto3\");\n        final StandardLoopCharacteristics loop2 = (StandardLoopCharacteristics) auto3.getLoopCharacteristics();\n        assertEquals(true, loop2.isTestBefore());\n        assertEquals(\"true\", loop2.getLoopCondition().getContent());\n        assertEquals(\"5\", loop2.getLoopMax().getContent());\n    }\n\n    @Test\n    public void readProcessWithBoundaryEventsFromBusinessArchive() throws Exception {\n        final Expression timerExpression = new ExpressionBuilder().createConstantLongExpression(1000);\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"MyProcess\", \"1.0\");\n        builder.addStartEvent(\"start1\");\n        final UserTaskDefinitionBuilder userTaskDefinitionBuilder = builder.addUserTask(\"userTask\", \"delivery\");\n        userTaskDefinitionBuilder.addBoundaryEvent(\"b1\", true).addTimerEventTriggerDefinition(TimerType.DURATION,\n                timerExpression);\n        userTaskDefinitionBuilder.addBoundaryEvent(\"b2\", true).addMessageEventTrigger(\"m1\");\n        userTaskDefinitionBuilder.addBoundaryEvent(\"b3\", true).addErrorEventTrigger(\"e1\");\n        builder.addAutomaticTask(\"exceptionFlowB1\");\n        builder.addAutomaticTask(\"exceptionFlowB2\");\n        builder.addAutomaticTask(\"exceptionFlowB3\");\n        builder.addEndEvent(\"end1\");\n        builder.addTransition(\"start1\", \"userTask\");\n        builder.addTransition(\"userTask\", \"end1\");\n        builder.addTransition(\"b1\", \"exceptionFlowB1\");\n        builder.addTransition(\"b2\", \"exceptionFlowB2\");\n        builder.addTransition(\"b3\", \"exceptionFlowB3\");\n\n        final DesignProcessDefinition process = builder.getProcess();\n        final DesignProcessDefinition result = getDesignProcessDefinition(builder);\n\n        checkProcessForBoundaryEvents(process, result, true);\n    }\n\n    @Test\n    public void readProcessWithNonInterruptingBoundaryEventsFromBusinessArchive() throws Exception {\n        final Expression timerExpression = new ExpressionBuilder().createConstantLongExpression(1000);\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"MyProcess\", \"1.0\");\n        builder.addStartEvent(\"start1\");\n        final UserTaskDefinitionBuilder userTaskDefinitionBuilder = builder.addUserTask(\"userTask\", \"delivery\");\n        userTaskDefinitionBuilder.addBoundaryEvent(\"b1\", false).addTimerEventTriggerDefinition(TimerType.DURATION,\n                timerExpression);\n        builder.addAutomaticTask(\"exceptionFlowB1\");\n        builder.addEndEvent(\"end1\");\n        builder.addTransition(\"start1\", \"userTask\");\n        builder.addTransition(\"userTask\", \"end1\");\n        builder.addTransition(\"b1\", \"exceptionFlowB1\");\n\n        final DesignProcessDefinition process = builder.getProcess();\n        final DesignProcessDefinition result = getDesignProcessDefinition(builder);\n\n        checkProcessForBoundaryEvents(process, result, false);\n    }\n\n    @Test\n    public void readProcessWithSendTaskFromBusinessArchive() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"MyProcess\", \"1.0\");\n        final SendTaskDefinitionBuilder sendTaskDefinitionBuilder = builder.addSendTask(\"sendTask\", \"messageName\",\n                new ExpressionBuilder().createConstantStringExpression(\"processName\"));\n        sendTaskDefinitionBuilder\n                .setTargetFlowNode(new ExpressionBuilder().createConstantStringExpression(\"flowNodeName\"));\n        sendTaskDefinitionBuilder.addCorrelation(new ExpressionBuilder().createConstantStringExpression(\"un\"),\n                new ExpressionBuilder().createConstantStringExpression(\"value\"));\n        sendTaskDefinitionBuilder.addMessageContentExpression(\n                new ExpressionBuilder().createConstantStringExpression(\"myData\"),\n                new ExpressionBuilder().createConstantStringExpression(\"dataValue\"));\n        final DesignProcessDefinition result = getDesignProcessDefinition(builder);\n\n        final SendTaskDefinition flowNode = (SendTaskDefinition) result.getFlowElementContainer()\n                .getFlowNode(\"sendTask\");\n        assertEquals(\"sendTask\", flowNode.getName());\n        final ThrowMessageEventTriggerDefinition messageTrigger = flowNode.getMessageTrigger();\n        assertEquals(\"processName\", messageTrigger.getTargetProcess().getContent());\n        assertEquals(\"messageName\", messageTrigger.getMessageName());\n        assertEquals(\"flowNodeName\", messageTrigger.getTargetFlowNode().getContent());\n        assertEquals(\"myData\", messageTrigger.getDataDefinitions().get(0).getName());\n        assertEquals(\"java.lang.String\", messageTrigger.getDataDefinitions().get(0).getClassName());\n        assertEquals(\"dataValue\", messageTrigger.getDataDefinitions().get(0).getDefaultValueExpression().getContent());\n        assertEquals(\"un\", messageTrigger.getCorrelations().get(0).getKey().getContent());\n        assertEquals(\"value\", messageTrigger.getCorrelations().get(0).getValue().getContent());\n    }\n\n    @Test(expected = InvalidProcessDefinitionException.class)\n    public void unableToExportSendTaskWithNoMessageName() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"MyProcess\", \"1.0\");\n        final SendTaskDefinitionBuilder sendTaskDefinitionBuilder = builder.addSendTask(\"sendTask\", null,\n                new ExpressionBuilder().createConstantStringExpression(\"processName\"));\n        sendTaskDefinitionBuilder\n                .setTargetFlowNode(new ExpressionBuilder().createConstantStringExpression(\"flowNodeName\"));\n        sendTaskDefinitionBuilder.addCorrelation(new ExpressionBuilder().createConstantStringExpression(\"un\"),\n                new ExpressionBuilder().createConstantStringExpression(\"value\"));\n        sendTaskDefinitionBuilder.addMessageContentExpression(\n                new ExpressionBuilder().createConstantStringExpression(\"myData\"),\n                new ExpressionBuilder().createConstantStringExpression(\"dataValue\"));\n        builder.done();\n    }\n\n    @Test\n    public void readProcessWithThowErrorEventFromBusinessArchive() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"MyProcess\", \"1.0\");\n        builder.addStartEvent(\"start1\");\n        builder.addAutomaticTask(\"a1\");\n        builder.addEndEvent(\"end1\").addErrorEventTrigger(\"e1\");\n        builder.addTransition(\"start1\", \"a1\");\n        builder.addTransition(\"a1\", \"end1\");\n        final DesignProcessDefinition process = builder.getProcess();\n        final DesignProcessDefinition result = getDesignProcessDefinition(builder);\n\n        assertEquals(process.getName(), result.getName());\n        assertEquals(process.getVersion(), result.getVersion());\n\n        assertEquals(process.getFlowElementContainer().getStartEvents(),\n                result.getFlowElementContainer().getStartEvents());\n        assertEquals(1, result.getFlowElementContainer().getStartEvents().size());\n\n        assertEquals(1, process.getFlowElementContainer().getActivities().size());\n        assertEquals(1, result.getFlowElementContainer().getActivities().size());\n\n        final List<EndEventDefinition> resultEndEvents = result.getFlowElementContainer().getEndEvents();\n        assertEquals(process.getFlowElementContainer().getEndEvents(), resultEndEvents);\n        assertEquals(1, result.getFlowElementContainer().getEndEvents().size());\n        final EndEventDefinition endEventDefinition = resultEndEvents.get(0);\n        assertEquals(1, endEventDefinition.getEventTriggers().size());\n        assertEquals(1, endEventDefinition.getErrorEventTriggerDefinitions().size());\n    }\n\n    private Operation getOperation(final Expression rightOperand, final LeftOperand leftOperand) {\n        final OperationBuilder opb = new OperationBuilder().createNewInstance();\n        opb.setLeftOperand(leftOperand);\n        opb.setOperator(ASSIGN_OPERATOR);\n        opb.setRightOperand(rightOperand);\n        opb.setType(OperatorType.ASSIGNMENT);\n        return opb.done();\n    }\n\n    private void checkProcessForCallActivity(final DesignProcessDefinition process,\n            final DesignProcessDefinition result) {\n        assertEquals(process.getName(), result.getName());\n        assertEquals(process.getVersion(), result.getVersion());\n\n        assertEquals(process.getFlowElementContainer().getStartEvents(),\n                result.getFlowElementContainer().getStartEvents());\n        assertEquals(1, result.getFlowElementContainer().getStartEvents().size());\n\n        assertEquals(1, result.getFlowElementContainer().getActivities().size());\n        assertEquals(1, process.getFlowElementContainer().getActivities().size());\n        assertEquals(process.getFlowElementContainer().getActivities().iterator().next(),\n                result.getFlowElementContainer().getActivities().iterator().next());\n\n        assertEquals(process.getFlowElementContainer().getEndEvents(), result.getFlowElementContainer().getEndEvents());\n        assertEquals(1, result.getFlowElementContainer().getEndEvents().size());\n    }\n\n    private void checkProcessForBoundaryEvents(final DesignProcessDefinition process,\n            final DesignProcessDefinition result, final boolean isInterrupting) {\n        assertEquals(process.getName(), result.getName());\n        assertEquals(process.getVersion(), result.getVersion());\n\n        assertEquals(process.getFlowElementContainer().getStartEvents(),\n                result.getFlowElementContainer().getStartEvents());\n        assertEquals(1, result.getFlowElementContainer().getStartEvents().size());\n\n        assertEquals(process.getFlowElementContainer().getActivities().size(),\n                result.getFlowElementContainer().getActivities().size());\n        final ActivityDefinition resultActivity = result.getFlowElementContainer().getActivity(\"userTask\");\n        final ActivityDefinition origActivity = process.getFlowElementContainer().getActivity(\"userTask\");\n        assertEquals(origActivity, resultActivity);\n        assertEquals(origActivity.getBoundaryEventDefinitions().size(),\n                resultActivity.getBoundaryEventDefinitions().size());\n        for (final BoundaryEventDefinition boundary : resultActivity.getBoundaryEventDefinitions()) {\n            assertEquals(1, boundary.getEventTriggers().size());\n            assertEquals(isInterrupting, boundary.isInterrupting());\n        }\n\n        assertEquals(process.getFlowElementContainer().getEndEvents(), result.getFlowElementContainer().getEndEvents());\n        assertEquals(1, result.getFlowElementContainer().getEndEvents().size());\n    }\n\n    private void checkProcessForMessagesEvents(final DesignProcessDefinition process,\n            final DesignProcessDefinition result) {\n        assertEquals(process.getName(), result.getName());\n        assertEquals(process.getVersion(), result.getVersion());\n\n        assertEquals(process.getFlowElementContainer().getStartEvents().size(),\n                result.getFlowElementContainer().getStartEvents().size());\n        assertEquals(1, result.getFlowElementContainer().getStartEvents().size());\n        assertEquals(process.getFlowElementContainer().getStartEvents().get(0),\n                result.getFlowElementContainer().getStartEvents().get(0));\n        assertEquals(1,\n                result.getFlowElementContainer().getStartEvents().get(0).getMessageEventTriggerDefinitions().size());\n        assertEquals(\n                process.getFlowElementContainer().getStartEvents().get(0).getMessageEventTriggerDefinitions().get(0)\n                        .getOperations(),\n                result\n                        .getFlowElementContainer().getStartEvents().get(0).getMessageEventTriggerDefinitions().get(0)\n                        .getOperations());\n        assertEquals(1, result.getFlowElementContainer().getStartEvents().get(0).getMessageEventTriggerDefinitions()\n                .get(0).getOperations().size());\n\n        assertEquals(process.getFlowElementContainer().getIntermediateCatchEvents().size(),\n                result.getFlowElementContainer().getIntermediateCatchEvents()\n                        .size());\n        assertEquals(1, result.getFlowElementContainer().getIntermediateCatchEvents().size());\n        assertEquals(process.getFlowElementContainer().getIntermediateCatchEvents().get(0),\n                result.getFlowElementContainer().getIntermediateCatchEvents()\n                        .get(0));\n        assertEquals(1, result.getFlowElementContainer().getIntermediateCatchEvents().get(0)\n                .getMessageEventTriggerDefinitions().size());\n\n        final CatchMessageEventTriggerDefinition expectedCatchMessageEventTrigger = process.getFlowElementContainer()\n                .getIntermediateCatchEvents().get(0)\n                .getMessageEventTriggerDefinitions().get(0);\n        final CatchMessageEventTriggerDefinition actualCatchMessageEventTrigger = result.getFlowElementContainer()\n                .getIntermediateCatchEvents().get(0)\n                .getMessageEventTriggerDefinitions().get(0);\n        assertEquals(expectedCatchMessageEventTrigger.getCorrelations(),\n                actualCatchMessageEventTrigger.getCorrelations());\n        assertEquals(1, actualCatchMessageEventTrigger.getCorrelations().size());\n        assertEquals(expectedCatchMessageEventTrigger.getOperations(), actualCatchMessageEventTrigger.getOperations());\n        assertEquals(1, actualCatchMessageEventTrigger.getOperations().size());\n\n        assertEquals(process.getFlowElementContainer().getIntermediateThrowEvents().size(),\n                result.getFlowElementContainer().getIntermediateThrowEvents()\n                        .size());\n        assertEquals(1, result.getFlowElementContainer().getIntermediateThrowEvents().size());\n        assertEquals(process.getFlowElementContainer().getIntermediateThrowEvents().get(0),\n                result.getFlowElementContainer().getIntermediateThrowEvents()\n                        .get(0));\n        assertEquals(1, result.getFlowElementContainer().getIntermediateThrowEvents().get(0)\n                .getMessageEventTriggerDefinitions().size());\n\n        assertEquals(process.getFlowElementContainer().getEndEvents().size(),\n                result.getFlowElementContainer().getEndEvents().size());\n        assertEquals(1, result.getFlowElementContainer().getEndEvents().size());\n        assertEquals(process.getFlowElementContainer().getEndEvents().get(0),\n                result.getFlowElementContainer().getEndEvents().get(0));\n        assertEquals(1,\n                result.getFlowElementContainer().getEndEvents().get(0).getMessageEventTriggerDefinitions().size());\n\n        final ThrowMessageEventTriggerDefinition actualThrowMessage = result.getFlowElementContainer().getEndEvents()\n                .get(0)\n                .getMessageEventTriggerDefinitions()\n                .get(0);\n        final ThrowMessageEventTriggerDefinition expectedThrowMessage = process.getFlowElementContainer().getEndEvents()\n                .get(0)\n                .getMessageEventTriggerDefinitions()\n                .get(0);\n        assertEquals(expectedThrowMessage.getCorrelations(), actualThrowMessage.getCorrelations());\n        assertEquals(1, actualThrowMessage.getCorrelations().size());\n        assertEquals(expectedThrowMessage.getDataDefinitions(), actualThrowMessage.getDataDefinitions());\n        assertEquals(1, actualThrowMessage.getDataDefinitions().size());\n\n        assertEquals(process.getActorsList().size(), result.getActorsList().size());\n        assertEquals(process.getFlowElementContainer().getActivities().size(),\n                result.getFlowElementContainer().getActivities().size());\n        assertEquals(process.getFlowElementContainer().getGatewaysList().size(),\n                result.getFlowElementContainer().getGatewaysList().size());\n        assertEquals(process.getFlowElementContainer().getTransitions().size(),\n                result.getFlowElementContainer().getTransitions().size());\n        assertEquals(process.getFlowElementContainer().getDataDefinitions().size(),\n                result.getFlowElementContainer().getDataDefinitions().size());\n    }\n\n    @Test\n    public void checkErrorMessageOnInvalidTransition() {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcess\", \"1.0\");\n        processDefinitionBuilder.addTransition(\"unknown1\", \"unknown2\");\n        try {\n            processDefinitionBuilder.done();\n            fail(\"should have thrown an \" + InvalidProcessDefinitionException.class.getSimpleName());\n        } catch (final InvalidProcessDefinitionException e) {\n            assertTrue(e.getMessage().contains(\"unknown1\"));\n            assertTrue(e.getMessage().contains(\"unknown2\"));\n        }\n    }\n\n    @Test\n    public void readProcessWithAnActorWithADescription() throws Exception {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(\"MyProcessTT\", \"1.0\");\n        builder.addActor(\"Truck Driver\").addDescription(\"desc\");\n        builder.addUserTask(\"step\", \"Truck Driver\");\n\n        final DesignProcessDefinition result = getDesignProcessDefinition(builder);\n        final List<ActorDefinition> actors = result.getActorsList();\n        assertEquals(1, actors.size());\n        final ActorDefinition actor = actors.iterator().next();\n        assertEquals(\"Truck Driver\", actor.getName());\n        assertEquals(\"desc\", actor.getDescription());\n    }\n\n    private DesignProcessDefinition getDesignProcessDefinition(final ProcessDefinitionBuilder builder)\n            throws Exception {\n        final DesignProcessDefinition process = builder.done();\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(process).done();\n        BusinessArchiveFactory.writeBusinessArchiveToFile(businessArchive, barFile);\n\n        final InputStream inputStream = new FileInputStream(barFile);\n        try {\n            final BusinessArchive businessArchive2 = BusinessArchiveFactory.readBusinessArchive(inputStream);\n            return businessArchive2.getProcessDefinition();\n        } finally {\n            inputStream.close();\n        }\n    }\n\n    @Test\n    public void readProcessWithActorWithoutDescription() throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcessTT\", \"1.0\");\n        processDefinitionBuilder.addActor(\"Truck Driver\");\n        processDefinitionBuilder.addStartEvent(\"start1\");\n        processDefinitionBuilder.addAutomaticTask(\"auto1\").addConnector(\"conn1\", \"connId1\", \"1.0.0\",\n                ConnectorEvent.ON_FINISH);\n        processDefinitionBuilder.addUserTask(\"user1\", \"Truck Driver\").addConnector(\"conn2\", \"1.0.0\", \"connId2\",\n                ConnectorEvent.ON_ENTER);\n        processDefinitionBuilder.addGateway(\"gate1\", GatewayType.INCLUSIVE).addDefaultTransition(\"user1\");\n        processDefinitionBuilder.addEndEvent(\"end1\");\n        processDefinitionBuilder.addTransition(\"start1\", \"auto1\");\n        processDefinitionBuilder.addTransition(\"auto1\", \"user1\");\n        processDefinitionBuilder.addTransition(\"user1\", \"gate1\");\n        processDefinitionBuilder.addTransition(\"user1\", \"end1\");\n        processDefinitionBuilder.addConnector(\"conn3\", \"connId3\", \"1.0.0\", ConnectorEvent.ON_FINISH);\n\n        final DesignProcessDefinition process = processDefinitionBuilder.done();\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(process).done();\n        BusinessArchiveFactory.writeBusinessArchiveToFile(businessArchive, barFile);\n\n        final InputStream inputStream = new FileInputStream(barFile);\n        final BusinessArchive businessArchive2;\n        try {\n            businessArchive2 = BusinessArchiveFactory.readBusinessArchive(inputStream);\n        } finally {\n            inputStream.close();\n        }\n\n        final DesignProcessDefinition result = businessArchive2.getProcessDefinition();\n        assertEquals(process.getName(), result.getName());\n        assertEquals(process.getVersion(), result.getVersion());\n        assertEquals(process.getFlowElementContainer().getStartEvents().size(),\n                result.getFlowElementContainer().getStartEvents().size());\n        assertEquals(process.getFlowElementContainer().getStartEvents().get(0),\n                result.getFlowElementContainer().getStartEvents().get(0));\n        assertEquals(process.getFlowElementContainer().getEndEvents().size(),\n                result.getFlowElementContainer().getEndEvents().size());\n        assertEquals(process.getFlowElementContainer().getEndEvents().get(0),\n                result.getFlowElementContainer().getEndEvents().get(0));\n        assertEquals(process.getActorsList().size(), result.getActorsList().size());\n        assertEquals(process.getActorsList().iterator().next(), result.getActorsList().iterator().next());\n        assertEquals(process.getFlowElementContainer().getActivities().size(),\n                result.getFlowElementContainer().getActivities().size());\n        final Iterator<ActivityDefinition> iterator = result.getFlowElementContainer().getActivities().iterator();\n        final Iterator<ActivityDefinition> iterator2 = process.getFlowElementContainer().getActivities().iterator();\n        final ActivityDefinition procAct1 = iterator2.next();\n        final ActivityDefinition procAct2 = iterator2.next();\n        final ActivityDefinition resAct1 = iterator.next();\n        final ActivityDefinition resAct2 = iterator.next();\n        assertTrue(procAct1.equals(resAct1) || procAct1.equals(resAct2));\n        assertTrue(procAct2.equals(resAct1) || procAct2.equals(resAct2));\n        assertEquals(process.getFlowElementContainer().getGatewaysList().size(),\n                result.getFlowElementContainer().getGatewaysList().size());\n        assertEquals(process.getFlowElementContainer().getGatewaysList().iterator().next(),\n                result.getFlowElementContainer().getGatewaysList().iterator()\n                        .next());\n        assertEquals(process.getFlowElementContainer().getTransitions().size(),\n                result.getFlowElementContainer().getTransitions().size());\n        assertEquals(process.getFlowElementContainer().getConnectors().size(),\n                result.getFlowElementContainer().getConnectors().size());\n        assertEquals(process.getFlowElementContainer().getConnectors().iterator().next(),\n                result.getFlowElementContainer().getConnectors().iterator().next());\n    }\n\n    @Test(expected = InvalidBusinessArchiveFormatException.class)\n    public void readInvalidProcessFromBusinessArchive() throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcess\", \"1.0\");\n        processDefinitionBuilder.addActor(\"Truck Driver\").addDescription(\"A man that is driving bigs trucks\");\n        processDefinitionBuilder.addStartEvent(\"start1\");\n        processDefinitionBuilder.addAutomaticTask(\"auto1\").addConnector(\"conn1\", \"connId1\", \"1.0.0\",\n                ConnectorEvent.ON_FINISH);\n        processDefinitionBuilder.addUserTask(\"user1\", \"Truck Driver\").addConnector(\"conn2\", \"connId2\", \"1.0.0\",\n                ConnectorEvent.ON_ENTER);\n        processDefinitionBuilder.addGateway(\"gate1\", GatewayType.INCLUSIVE).addDefaultTransition(\"user1\");\n        processDefinitionBuilder.addEndEvent(\"end1\");\n        processDefinitionBuilder.addTransition(\"start1\", \"auto1\");\n        processDefinitionBuilder.addTransition(\"auto1\", \"user1\");\n        processDefinitionBuilder.addTransition(\"user1\", \"gate1\");\n        processDefinitionBuilder.addTransition(\"user1\", \"end1\");\n        processDefinitionBuilder.addConnector(\"conn3\", \"connId3\", \"1.0.0\", ConnectorEvent.ON_FINISH);\n        processDefinitionBuilder.addParameter(\"myParam\", String.class.getName())\n                .addDescription(\"an important parameter\");\n\n        final DesignProcessDefinition process = processDefinitionBuilder.done();\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(process).done();\n        BusinessArchiveFactory.writeBusinessArchiveToFolder(businessArchive, tempFolder);\n\n        final File file = getFile(ProcessDefinitionBARContribution.PROCESS_DEFINITION_XML);\n        String fileContent = Files.readString(file.toPath());\n        fileContent = fileContent.replace(\"<tns:processDefinition\", \"<tns:pro_cessDefinition\");\n        fileContent = fileContent.replace(\"</tns:processDefinition\", \"</tns:pro_cessDefinition\");\n        file.delete();\n        file.createNewFile();\n        IOUtil.writeContentToFile(fileContent, file);\n        BusinessArchiveFactory.readBusinessArchive(tempFolder);\n    }\n\n    @Test(expected = InvalidBusinessArchiveFormatException.class)\n    public void readInvalidXMLProcessFromBusinessArchive() throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcess\", \"1.0\");\n        processDefinitionBuilder.addActor(\"Truck Driver\").addDescription(\"A man that is driving bigs trucks\");\n        processDefinitionBuilder.addStartEvent(\"start1\");\n        processDefinitionBuilder.addAutomaticTask(\"auto1\").addConnector(\"conn1\", \"connId1\", \"1.0.0\",\n                ConnectorEvent.ON_FINISH);\n        processDefinitionBuilder.addUserTask(\"user1\", \"Truck Driver\").addConnector(\"conn2\", \"connId2\", \"1.0.0\",\n                ConnectorEvent.ON_ENTER);\n        processDefinitionBuilder.addGateway(\"gate1\", GatewayType.INCLUSIVE).addDefaultTransition(\"user1\");\n        processDefinitionBuilder.addEndEvent(\"end1\");\n        processDefinitionBuilder.addTransition(\"start1\", \"auto1\");\n        processDefinitionBuilder.addTransition(\"auto1\", \"user1\");\n        processDefinitionBuilder.addTransition(\"user1\", \"gate1\");\n        processDefinitionBuilder.addTransition(\"user1\", \"end1\");\n        processDefinitionBuilder.addConnector(\"conn3\", \"connId3\", \"1.0.0\", ConnectorEvent.ON_FINISH);\n        processDefinitionBuilder.addParameter(\"myParam\", String.class.getName())\n                .addDescription(\"an important parameter\");\n\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(processDefinitionBuilder.done())\n                .done();\n        BusinessArchiveFactory.writeBusinessArchiveToFolder(businessArchive, tempFolder);\n\n        final File file = getFile(ProcessDefinitionBARContribution.PROCESS_DEFINITION_XML);\n        String fileContent = Files.readString(file.toPath());\n        fileContent = fileContent.replace(\"<tns:processDefinition\", \"<tns:pro_typo_cessDefinition\");\n        file.delete();\n        file.createNewFile();\n        IOUtil.writeContentToFile(fileContent, file);\n        BusinessArchiveFactory.readBusinessArchive(tempFolder);\n    }\n\n    @Test(expected = InvalidProcessDefinitionException.class)\n    public void createProcessWithADocumentHavingBothUrlAndFile() throws InvalidProcessDefinitionException {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcessWithExternalDocuments\", \"1.0\");\n        builder.addDocumentDefinition(\"myDoc\").addContentFileName(\"testFile.txt\").addDescription(\"a cool pdf document\")\n                .addMimeType(\"application/pdf\")\n                .addFile(\"myPdf.pdf\").addUrl(\"http://plop\");\n        builder.done();\n    }\n\n    @Test(expected = InvalidBusinessArchiveFormatException.class)\n    public void createProcessWithAInBarDocumentMissingFile()\n            throws InvalidProcessDefinitionException, InvalidBusinessArchiveFormatException {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcessWithExternalDocuments\", \"1.0\");\n        builder.addDocumentDefinition(\"myDoc\").addContentFileName(\"testFile.txt\").addDescription(\"a cool pdf document\")\n                .addMimeType(\"application/pdf\")\n                .addFile(\"myPdf.pdf\");\n        final DesignProcessDefinition processDefinition = builder.done();\n        new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(processDefinition)\n                .addDocumentResource(new BarResource(\"testFile.txt\", new byte[] { 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }))\n                .done();\n    }\n\n    @Test(expected = InvalidProcessDefinitionException.class)\n    public void createProcessWithConnectorHavingNullInput() throws InvalidProcessDefinitionException {\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithConnectorNullInput\", \"1.0\");\n        builder.addAutomaticTask(\"step1\").addConnector(\"eee\", \"zzz\", \"eee\", ConnectorEvent.ON_ENTER).addInput(\"name\",\n                null);\n        builder.addConnector(\"eee\", \"zzz\", \"eee\", ConnectorEvent.ON_ENTER).addInput(\"name\", null);\n        builder.done();\n    }\n\n    @Test\n    public void subProcess() throws Exception {\n        final Expression createdExpression = new ExpressionBuilder().createConstantBooleanExpression(false);\n\n        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder();\n        builder.createNewInstance(\"process\", \"10.2\").addSubProcess(\"subProcessActivity\", true).getSubProcessBuilder()\n                .addStartEvent(\"start\")\n                .addTimerEventTriggerDefinition(TimerType.DURATION,\n                        new ExpressionBuilder().createConstantLongExpression(5000))\n                .addBooleanData(\"created\", createdExpression).addAutomaticTask(\"auto\");\n\n        final DesignProcessDefinition process = builder.getProcess();\n        assertEquals(1, process.getFlowElementContainer().getActivities().size());\n        final DesignProcessDefinition result = getDesignProcessDefinition(builder);\n        assertEquals(1, result.getFlowElementContainer().getActivities().size());\n    }\n\n    @Test\n    public void generatingOutgoingDefaultTransitionShouldBeConformToProcessDefinitionXsd() throws Exception {\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"MyProcess\", \"1.0\");\n        processDefinitionBuilder.addActor(\"Truck Driver\").addDescription(\"A man that is driving bigs trucks\");\n        processDefinitionBuilder.addStartEvent(\"start1\");\n        processDefinitionBuilder.addGateway(\"Gateway1\", GatewayType.EXCLUSIVE);\n        processDefinitionBuilder.addEndEvent(\"end1\");\n        processDefinitionBuilder.addEndEvent(\"end2\");\n        processDefinitionBuilder.addEndEvent(\"end3\");\n        processDefinitionBuilder.addTransition(\"start1\", \"Gateway1\");\n        processDefinitionBuilder.addTransition(\"Gateway1\", \"end1\");\n        processDefinitionBuilder.addTransition(\"Gateway1\", \"end2\");\n        processDefinitionBuilder.addDefaultTransition(\"Gateway1\", \"end3\");\n\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(processDefinitionBuilder.done())\n                .done();\n        BusinessArchiveFactory.writeBusinessArchiveToFile(businessArchive, barFile);\n\n        BusinessArchiveFactory.readBusinessArchive(barFile);\n    }\n\n    @Test(expected = InvalidBusinessArchiveFormatException.class)\n    public void readBarWithConnectorFailActionsFails() throws Exception {\n        try (InputStream resourceAsStream = BusinessArchiveTest.class\n                .getResourceAsStream(\"testBuy_a_mini_extended--6.1.bar\")) {\n            BusinessArchiveFactory.readBusinessArchive(resourceAsStream);\n        }\n    }\n\n    @Test\n    public void parameters() {\n        final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(\"firstProcess\",\n                \"1.0\");\n        processBuilder\n                .addParameter(\"key1\", String.class.getCanonicalName())\n                .addParameter(\"key.2\", String.class.getCanonicalName()).addUserTask(\"userTask1\", null);\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-client/src/test/java/org/bonitasoft/engine/bdm/BusinessObjectDAOFactoryTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm;\n\nimport org.assertj.core.api.Assertions;\nimport org.bonitasoft.engine.bdm.dao.BusinessObjectDAO;\nimport org.bonitasoft.engine.session.APISession;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentMatchers;\nimport org.mockito.Mock;\nimport org.mockito.Mockito;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Romain Bioteau\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class BusinessObjectDAOFactoryTest {\n\n    @Mock\n    private APISession session;\n\n    @Spy\n    private BusinessObjectDAOFactory factory;\n\n    @Test(expected = IllegalArgumentException.class)\n    public void should_create_dao_throw_IllegalArgmumentException_for_null_session() throws Exception {\n        factory.createDAO(null, null);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void should_create_dao_throw_IllegalArgmumentException_for_null_interface() throws Exception {\n        factory.createDAO(session, null);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void should_create_dao_throw_IllegalArgmumentException_if_daoInterface_is_not_an_interface()\n            throws Exception {\n        factory.createDAO(session, DummyDAOImpl.class);\n    }\n\n    @Test(expected = BusinessObjectDaoCreationException.class)\n    public void should_create_dao_throw_BusinessObjectDaoCreationException_if_daoImpl_not_in_classpath()\n            throws Exception {\n        Mockito.doThrow(ClassNotFoundException.class).when(factory).loadClass(BusinessObjectDAO.class.getName());\n        factory.createDAO(session, BusinessObjectDAO.class);\n    }\n\n    @Test(expected = BusinessObjectDaoCreationException.class)\n    public void should_create_dao_throw_BusinessObjectDaoCreationException_if_daoImpl_has_no_constructor_with_session()\n            throws Exception {\n        Mockito.doReturn(DummyDAOWithoutConstructorImpl.class).when(factory).loadClass(ArgumentMatchers.anyString());\n        factory.createDAO(session, DummyDAO.class);\n    }\n\n    @Test\n    public void should_create_dao_return_implementation() throws Exception {\n        DummyDAO daoInstance = factory.createDAO(session, DummyDAO.class);\n        Assertions.assertThat(daoInstance).isNotNull();\n    }\n\n    class DummyDAOWithoutConstructorImpl implements DummyDAO {\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-client/src/test/java/org/bonitasoft/engine/bdm/DummyDAO.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm;\n\nimport org.bonitasoft.engine.bdm.dao.BusinessObjectDAO;\n\n/**\n * @author Romain Bioteau\n */\npublic interface DummyDAO extends BusinessObjectDAO {\n\n}\n"
  },
  {
    "path": "bpm/bonita-client/src/test/java/org/bonitasoft/engine/bdm/DummyDAOImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm;\n\nimport org.bonitasoft.engine.session.APISession;\n\n/**\n * @author Romain Bioteau\n */\npublic class DummyDAOImpl implements DummyDAO {\n\n    public DummyDAOImpl(APISession session) {\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-client/src/test/java/org/bonitasoft/engine/util/APITypeManagerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.util;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.ApiAccessType;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.TemporaryFolder;\n\n/**\n * @author Baptiste Mesta\n */\npublic class APITypeManagerTest {\n\n    @Rule\n    public TemporaryFolder temporaryFolder = new TemporaryFolder();\n\n    @Before\n    @After\n    public void before() throws Exception {\n        APITypeManager.refresh();\n        System.clearProperty(\"bonita.home\");\n        System.clearProperty(\"org.bonitasoft.engine.api-type\");\n        System.clearProperty(\"org.bonitasoft.engine.api-type.server.url\");\n        System.clearProperty(\"org.bonitasoft.engine.api-type.application.name\");\n        System.clearProperty(\"org.bonitasoft.engine.api-type.connections.max\");\n        System.clearProperty(\"org.bonitasoft.engine.api-type.basicAuthentication.active\");\n        System.clearProperty(\"org.bonitasoft.engine.api-type.basicAuthentication.username\");\n        System.clearProperty(\"org.bonitasoft.engine.api-type.basicAuthentication.password\");\n    }\n\n    @Test\n    public void should_getAPIType_when_not_set_return_local() throws Exception {\n        ApiAccessType apiType = APITypeManager.getAPIType();\n\n        assertThat(apiType).isEqualTo(ApiAccessType.LOCAL);\n    }\n\n    @Test\n    public void should_getAPIType_when_not_set_but_have_other_properties_return_local() throws Exception {\n        System.setProperty(\"org.bonitasoft.engine.api-type.server.url\", \"localhost\");\n\n        ApiAccessType apiType = APITypeManager.getAPIType();\n\n        assertThat(apiType).isEqualTo(ApiAccessType.LOCAL);\n    }\n\n    @Test\n    public void should_getAPIType_when_set_with_system_properties_works() throws Exception {\n        //given\n        System.setProperty(\"org.bonitasoft.engine.api-type\", \"HTTP\");\n        //when\n        ApiAccessType apiType = APITypeManager.getAPIType();\n        //then\n        assertThat(apiType).isEqualTo(ApiAccessType.HTTP);\n    }\n\n    @Test\n    public void should_getAPIType_when_set_with_bonita_home_community_properties_works() throws Exception {\n        //given\n        File bonitaHome = temporaryFolder.newFolder();\n        System.setProperty(\"bonita.home\", bonitaHome.getAbsolutePath());\n        writePropertiesFile(bonitaHome, \"work\" + File.separator\n                + \"bonita-client-community.properties\", \"org.bonitasoft.engine.api-type = HTTP\");\n        //when\n        ApiAccessType apiType = APITypeManager.getAPIType();\n        //then\n        assertThat(apiType).isEqualTo(ApiAccessType.HTTP);\n    }\n\n    @Test\n    public void should_getAPIType_when_set_with_bonita_home_having_spaces_and_slash_works() throws Exception {\n        //given\n        File bonitaHome = temporaryFolder.newFolder();\n        System.setProperty(\"bonita.home\", \"      \" + bonitaHome.getAbsolutePath() + File.separator + \"       \");\n        writePropertiesFile(bonitaHome, \"work\" + File.separator\n                + \"bonita-client-community.properties\", \"org.bonitasoft.engine.api-type = HTTP\");\n        //when\n        ApiAccessType apiType = APITypeManager.getAPIType();\n        //then\n        assertThat(apiType).isEqualTo(ApiAccessType.HTTP);\n    }\n\n    @Test\n    public void should_getAPIType_when_set_with_bonita_home_custom_properties_works() throws Exception {\n        //given\n        File bonitaHome = temporaryFolder.newFolder();\n        System.setProperty(\"bonita.home\", bonitaHome.getAbsolutePath());\n        writePropertiesFile(bonitaHome, \"conf\" + File.separator\n                + \"bonita-client-custom.properties\", \"org.bonitasoft.engine.api-type = HTTP\");\n        //when\n        ApiAccessType apiType = APITypeManager.getAPIType();\n        //then\n        assertThat(apiType).isEqualTo(ApiAccessType.HTTP);\n    }\n\n    @Test\n    public void should_getAPIType_when_set_with_bonita_home_community_and_custom_properties_works() throws Exception {\n        //given\n        File bonitaHome = temporaryFolder.newFolder();\n        System.setProperty(\"bonita.home\", bonitaHome.getAbsolutePath());\n        writePropertiesFile(bonitaHome, \"work\" + File.separator\n                + \"bonita-client-community.properties\", \"org.bonitasoft.engine.api-type = LOCAL\");\n        writePropertiesFile(bonitaHome, \"conf\" + File.separator\n                + \"bonita-client-custom.properties\", \"org.bonitasoft.engine.api-type = HTTP\");\n        //when\n        ApiAccessType apiType = APITypeManager.getAPIType();\n        //then\n        assertThat(apiType).isEqualTo(ApiAccessType.HTTP);\n    }\n\n    @Test\n    public void should_getAPIType_when_set_with_bonita_home_custom_properties_and_system_properties_works()\n            throws Exception {\n        //given\n        System.setProperty(\"org.bonitasoft.engine.api-type.server.url\", \"localhost\");\n        File bonitaHome = temporaryFolder.newFolder();\n        System.setProperty(\"bonita.home\", bonitaHome.getAbsolutePath());\n        writePropertiesFile(bonitaHome, \"conf\" + File.separator\n                + \"bonita-client-custom.properties\", \"org.bonitasoft.engine.api-type = HTTP\");\n        //when\n        ApiAccessType apiType = APITypeManager.getAPIType();\n        //then\n        assertThat(apiType).isEqualTo(ApiAccessType.HTTP);\n    }\n\n    @Test\n    public void should_getAPIType_when_set_programmatically_should_work() throws Exception {\n        //given\n        APITypeManager.setAPITypeAndParams(ApiAccessType.HTTP, null);\n        //when\n        ApiAccessType apiType = APITypeManager.getAPIType();\n        //then\n        assertThat(apiType).isEqualTo(ApiAccessType.HTTP);\n    }\n\n    @Test\n    public void should_getAPITypeParameters_when_not_set_return_empty() throws Exception {\n        Map<String, String> apiTypeParameters = APITypeManager.getAPITypeParameters();\n        assertThat(apiTypeParameters).isEmpty();\n    }\n\n    @Test\n    public void should_getAPITypeParameters_when_set_with_system_properties_works() throws Exception {\n        //given\n        System.setProperty(\"org.bonitasoft.engine.api-type.server.url\", \"localhost\");\n        System.setProperty(\"org.bonitasoft.engine.api-type.application.name\", \"bonita\");\n        System.setProperty(\"org.bonitasoft.engine.api-type.connections.max\", \"12\");\n        System.setProperty(\"org.bonitasoft.engine.api-type.basicAuthentication.active\", \"true\");\n        System.setProperty(\"org.bonitasoft.engine.api-type.basicAuthentication.username\", \"someUser\");\n        System.setProperty(\"org.bonitasoft.engine.api-type.basicAuthentication.password\", \"secret\");\n        //when\n        Map<String, String> apiTypeParameters = APITypeManager.getAPITypeParameters();\n        //then\n        assertThat(apiTypeParameters).containsOnly(entry(\"server.url\", \"localhost\"),\n                entry(\"application.name\", \"bonita\"), entry(\"connections.max\", \"12\"),\n                entry(\"basicAuthentication.active\", \"true\"), entry(\"basicAuthentication.username\", \"someUser\"),\n                entry(\"basicAuthentication.password\", \"secret\"));\n    }\n\n    @Test\n    public void should_getAPITypeParameters_when_set_with_bonita_home_community_properties_works() throws Exception {\n        //given\n        File bonitaHome = temporaryFolder.newFolder();\n        System.setProperty(\"bonita.home\", bonitaHome.getAbsolutePath());\n        writePropertiesFile(bonitaHome, \"work\" + File.separator\n                + \"bonita-client-community.properties\", \"server.url = localhost\");\n        //when\n        Map<String, String> apiTypeParameters = APITypeManager.getAPITypeParameters();\n        //then\n        assertThat(apiTypeParameters).containsOnly(entry(\"server.url\", \"localhost\"));\n    }\n\n    @Test\n    public void should_getAPITypeParameters_when_set_with_bonita_home_custom_properties_works() throws Exception {\n        //given\n        File bonitaHome = temporaryFolder.newFolder();\n        System.setProperty(\"bonita.home\", bonitaHome.getAbsolutePath());\n        writePropertiesFile(bonitaHome, \"conf\" + File.separator\n                + \"bonita-client-custom.properties\", \"server.url = localhost\");\n        //when\n        Map<String, String> apiTypeParameters = APITypeManager.getAPITypeParameters();\n        //then\n        assertThat(apiTypeParameters).containsOnly(entry(\"server.url\", \"localhost\"));\n    }\n\n    @Test\n    public void should_getAPITypeParameters_when_set_with_bonita_home_community_and_custom_properties_works()\n            throws Exception {\n        //given\n        File bonitaHome = temporaryFolder.newFolder();\n        System.setProperty(\"bonita.home\", bonitaHome.getAbsolutePath());\n        writePropertiesFile(bonitaHome, \"work\" + File.separator\n                + \"bonita-client-community.properties\", \"server.url = other\");\n        writePropertiesFile(bonitaHome, \"conf\" + File.separator\n                + \"bonita-client-custom.properties\", \"server.url = localhost\");\n        //when\n        Map<String, String> apiTypeParameters = APITypeManager.getAPITypeParameters();\n        //then\n        assertThat(apiTypeParameters).containsOnly(entry(\"server.url\", \"localhost\"));\n    }\n\n    @Test\n    public void should_getAPITypeParameters_when_set_programmatically_should_work() throws Exception {\n        //given\n        APITypeManager.setAPITypeAndParams(ApiAccessType.HTTP, Collections.singletonMap(\"server.url\", \"localhost\"));\n        //when\n        Map<String, String> apiTypeParameters = APITypeManager.getAPITypeParameters();\n        //then\n        assertThat(apiTypeParameters).containsOnly(entry(\"server.url\", \"localhost\"));\n    }\n\n    private void writePropertiesFile(File bonitaHome, String fileName, String content) throws IOException {\n        File file = new File(\n                bonitaHome.getAbsolutePath() + File.separator + \"engine-client\" + File.separator + fileName);\n        file.getParentFile().mkdirs();\n        Files.write(file.toPath(), content.getBytes(StandardCharsets.UTF_8));\n    }\n\n    @Test(expected = UnknownAPITypeException.class)\n    public void should_getAPIType_when_set_with_bad_system_properties_throw_exception() throws Exception {\n        //given\n        System.setProperty(\"org.bonitasoft.engine.api-type\", \"BAD\");\n        //when\n        APITypeManager.getAPIType();\n    }\n\n    @Test(expected = UnknownAPITypeException.class)\n    public void should_getAPIType_when_set_with_bad_bonita_home_community_properties_throw_exception()\n            throws Exception {\n        //given\n        File bonitaHome = temporaryFolder.newFolder();\n        System.setProperty(\"bonita.home\", bonitaHome.getAbsolutePath());\n        writePropertiesFile(bonitaHome, \"work\" + File.separator\n                + \"bonita-client-community.properties\", \"org.bonitasoft.engine.api-type = BAD\");\n        //when\n        APITypeManager.getAPIType();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-client/src/test/resources/myRealm.properties",
    "content": "john: doe ,admin"
  },
  {
    "path": "bpm/bonita-common/build.gradle",
    "content": "import org.bonitasoft.engine.gradle.PomUtils\n\napply plugin: 'java-test-fixtures'\n\ndependencies {\n    api platform(libs.jacksonBom)\n    api platform(libs.bonitaArtifactsModelBom)\n\n    api \"org.bonitasoft.engine:bonita-business-archive\"\n    api \"org.bonitasoft.engine:bonita-business-object-model\"\n    api \"org.bonitasoft.engine:bonita-profile-model\"\n    api \"org.bonitasoft.engine:bonita-organization-model\"\n    api \"org.bonitasoft.engine:bonita-application-model\"\n    api \"com.fasterxml.jackson.core:jackson-databind\"\n    api libs.slf4jApi\n    api \"com.fasterxml.jackson.core:jackson-annotations\"\n\n    runtimeOnly \"com.sun.activation:jakarta.activation\"\n\n    compileOnly project(':bpm:bonita-sap-jco-connector-api')\n    compileOnly libs.lombok\n    annotationProcessor libs.lombok\n\n    testImplementation project(':bpm:bonita-sap-jco-connector-api')\n    testImplementation libs.assertj\n    testImplementation libs.mockitoCore\n    testImplementation libs.logback\n    testImplementation libs.jmockit\n    testImplementation(libs.jsonUnit) {\n        exclude(group: \"org.slf4j\", module: \"slf4j-api\")\n    }\n\n    testFixturesApi(\"org.bonitasoft.engine:bonita-business-object-model\") {\n        artifact { classifier = 'tests' }\n    }\n    testFixturesImplementation libs.assertj\n}\n\ntasks.register(\"testsJar\", Jar) {\n    archiveClassifier = 'tests'\n    from(sourceSets.test.output)\n}\n\ntasks.register(\"sourcesJar\", Jar) {\n    from sourceSets.main.allJava\n    archiveClassifier = 'sources'\n}\n\ntasks.register(\"javadocJar\", Jar) {\n    from javadoc\n    archiveClassifier = 'javadoc'\n}\n\n// Disable publishing of test fixtures variants because their dependencies are published as optional dependencies\n// It prevents consumers (e.g. bonita-distrib) to retrieve some transitive dependencies (e.g. bonita-business-object-model)\ncomponents.java.withVariantsFromConfiguration(configurations.testFixturesApiElements) { skip() }\ncomponents.java.withVariantsFromConfiguration(configurations.testFixturesRuntimeElements) { skip() }\n\npublishing {\n    publications {\n        mavenJava(MavenPublication) {\n            from project.components.java\n            artifact project.sourcesJar\n            artifact project.javadocJar\n            artifact project.testsJar\n            pom { pom ->\n                name = \"Bonita Common\"\n                description = \"Bonita Common is the useful layer common to bonita-client and bonita-server\"\n                PomUtils.pomCommunityPublication(pom)\n            }\n        }\n    }\n}\n\ntest { jvmArgs \"-javaagent:${classpath.find { it.name.contains(\"jmockit\") }.absolutePath}\" }\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/InvalidFileFormatException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\n/**\n * @author Danila Mazour\n */\npublic interface InvalidFileFormatException {\n\n    Throwable getCause();\n\n    String getMessage();\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/APIAccessor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\nimport java.io.Serializable;\n\n/**\n * Gives access to some common APIs.\n *\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @author Emmanuel Duchastenier\n */\npublic interface APIAccessor extends Serializable {\n\n    /**\n     * Gives access to the <code>IdentityAPI</code>\n     *\n     * @return an <code>IdentityAPI</code>\n     */\n    IdentityAPI getIdentityAPI();\n\n    /**\n     * Gives access to the <code>ProcessAPI</code>\n     *\n     * @return an <code>ProcessAPI</code>\n     */\n    ProcessAPI getProcessAPI();\n\n    /**\n     * Gives access to the <code>CommandAPI</code>\n     *\n     * @return an <code>CommandAPI</code>\n     */\n    CommandAPI getCommandAPI();\n\n    /**\n     * Gives access to the <code>ProfileAPI</code>\n     *\n     * @return an <code>ProfileAPI</code>\n     */\n    ProfileAPI getProfileAPI();\n\n    /**\n     * Gives access to the <code>PermissionAPI</code>\n     *\n     * @return an <code>PermissionAPI</code>\n     */\n    PermissionAPI getPermissionAPI();\n\n    /**\n     * Gives access to the <code>PageAPI</code>\n     *\n     * @return an <code>PageAPI</code>\n     */\n    PageAPI getCustomPageAPI();\n\n    /**\n     * Gives access to the <code>ApplicationAPI</code>\n     *\n     * @return an <code>ApplicationAPI</code>\n     */\n    ApplicationAPI getLivingApplicationAPI();\n\n    /**\n     * Gives access to the <code>BusinessDataAPI</code>\n     *\n     * @return an <code>BusinessDataAPI</code>\n     * @deprecated as of 7.3, see {@link BusinessDataAPI} for replacements\n     */\n    @Deprecated(since = \"7.3\")\n    BusinessDataAPI getBusinessDataAPI();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/ApplicationAPI.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.business.application.Application;\nimport org.bonitasoft.engine.business.application.ApplicationCreator;\nimport org.bonitasoft.engine.business.application.ApplicationImportPolicy;\nimport org.bonitasoft.engine.business.application.ApplicationLink;\nimport org.bonitasoft.engine.business.application.ApplicationLinkCreator;\nimport org.bonitasoft.engine.business.application.ApplicationLinkUpdater;\nimport org.bonitasoft.engine.business.application.ApplicationMenu;\nimport org.bonitasoft.engine.business.application.ApplicationMenuCreator;\nimport org.bonitasoft.engine.business.application.ApplicationMenuNotFoundException;\nimport org.bonitasoft.engine.business.application.ApplicationMenuUpdater;\nimport org.bonitasoft.engine.business.application.ApplicationNotFoundException;\nimport org.bonitasoft.engine.business.application.ApplicationPage;\nimport org.bonitasoft.engine.business.application.ApplicationPageNotFoundException;\nimport org.bonitasoft.engine.business.application.ApplicationUpdater;\nimport org.bonitasoft.engine.business.application.IApplication;\nimport org.bonitasoft.engine.business.application.Icon;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.ExportException;\nimport org.bonitasoft.engine.exception.ImportException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\n\n/**\n * This API allows to list and manage Bonita Living Applications ({@link IApplication}).\n *\n * @author Elias Ricken de Medeiros\n * @see org.bonitasoft.engine.business.application.IApplication\n * @see org.bonitasoft.engine.business.application.ApplicationLink\n * @see org.bonitasoft.engine.business.application.Application\n * @since 6.4\n */\npublic interface ApplicationAPI {\n\n    /**\n     * Creates a new {@link Application} based on the supplied {@link ApplicationCreator}\n     *\n     * @param applicationCreator creator describing characteristics of application to be created\n     * @return the created <code>Application</code>\n     * @throws AlreadyExistsException if an application already exists with the same name\n     * @throws CreationException if an error occurs during the creation\n     * @see Application\n     * @see ApplicationCreator\n     * @deprecated as of 9.0.0, Applications should be created at startup.\n     */\n    @Deprecated(since = \"9.0.0\")\n    Application createApplication(ApplicationCreator applicationCreator)\n            throws AlreadyExistsException, CreationException;\n\n    /**\n     * Creates a new {@link ApplicationLink} based on the supplied {@link ApplicationLinkCreator}\n     *\n     * @param applicationLinkCreator creator describing characteristics of application link to be created\n     * @return the created <code>ApplicationLink</code>\n     * @throws AlreadyExistsException if an application already exists with the same name\n     * @throws CreationException if an error occurs during the creation\n     * @see ApplicationLink\n     * @see ApplicationLinkCreator\n     * @deprecated as of 9.0.0, Applications should be created at startup. This also concerns application links\n     *             introduced in 10.2.0.\n     */\n    @Deprecated(since = \"10.2.0\")\n    ApplicationLink createApplicationLink(ApplicationLinkCreator applicationLinkCreator)\n            throws AlreadyExistsException, CreationException;\n\n    /**\n     * Retrieves an {@link Application} from its identifier.\n     *\n     * @param applicationId the application identifier\n     * @return an <code>Application</code> from its identifier.\n     * @throws ApplicationNotFoundException if no application is found for the given identifier\n     * @see Application\n     * @deprecated as of 10.2.0, use {@link #getIApplication(long)} instead to include application links.\n     */\n    @Deprecated(since = \"10.2.0\")\n    default Application getApplication(final long applicationId) throws ApplicationNotFoundException {\n        var res = getIApplication(applicationId);\n        if (res instanceof Application legacy) {\n            return legacy;\n        } else {\n            throw new ApplicationNotFoundException(applicationId);\n        }\n    }\n\n    /**\n     * Retrieves an {@link IApplication} from its identifier.\n     *\n     * @param applicationId the application identifier\n     * @return an <code>Application</code> from its identifier.\n     * @throws ApplicationNotFoundException if no application is found for the given identifier\n     * @see IApplication\n     */\n    IApplication getIApplication(final long applicationId) throws ApplicationNotFoundException;\n\n    /**\n     * Retrieves an {@link Application} from its token.\n     *\n     * @param applicationToken the application token used in the URL\n     * @return an <code>Application</code> from its token.\n     * @throws ApplicationNotFoundException if no application is found for the given token\n     * @see Application\n     * @deprecated as of 10.2.0, use {@link #getIApplicationByToken(String)} instead to include application links.\n     */\n    @Deprecated(since = \"10.2.0\")\n    default Application getApplicationByToken(final String applicationToken) throws ApplicationNotFoundException {\n        var app = getIApplicationByToken(applicationToken);\n        if (app instanceof Application legacy) {\n            return legacy;\n        } else {\n            throw new ApplicationNotFoundException(applicationToken);\n        }\n    }\n\n    /**\n     * Retrieves an {@link IApplication} from its token.\n     *\n     * @param applicationToken the application token used in the URL\n     * @return an <code>Application</code> from its token.\n     * @throws ApplicationNotFoundException if no application is found for the given token\n     * @see IApplication\n     */\n    IApplication getIApplicationByToken(final String applicationToken) throws ApplicationNotFoundException;\n\n    /**\n     * Deletes an {@link IApplication} by its identifier. All related\n     * {@link org.bonitasoft.engine.business.application.ApplicationPage}s and\n     * {@link org.bonitasoft.engine.business.application.ApplicationMenu}s will be automatically deleted.\n     *\n     * @param applicationId the <code>Application</code> identifier\n     * @throws DeletionException if an error occurs during the deletion\n     * @see IApplication\n     * @see org.bonitasoft.engine.business.application.ApplicationPage\n     * @see org.bonitasoft.engine.business.application.ApplicationMenu\n     */\n    void deleteApplication(long applicationId) throws DeletionException;\n\n    /**\n     * Updates an {@link Application} based on the information supplied by the {@link ApplicationUpdater}\n     *\n     * @param applicationId a long representing the application identifier\n     * @param updater an <code>ApplicationUpdater</code> describing the fields to be updated.\n     * @return the <code>Application</code> as it is after the update.\n     * @throws ApplicationNotFoundException if no <code>Application</code> is found for the given id\n     * @throws AlreadyExistsException if another <code>IApplication</code> already exists with the new name value\n     * @throws UpdateException if an error occurs during the update\n     * @see Application\n     * @see ApplicationUpdater\n     * @deprecated as of 9.0.0, Applications should be updated at startup.\n     */\n    @Deprecated(since = \"9.0.0\")\n    Application updateApplication(long applicationId, ApplicationUpdater updater)\n            throws ApplicationNotFoundException, UpdateException, AlreadyExistsException;\n\n    /**\n     * Updates an {@link ApplicationLink} based on the information supplied by the {@link ApplicationLinkUpdater}\n     *\n     * @param applicationId a long representing the application identifier\n     * @param updater an <code>ApplicationLinkUpdater</code> describing the fields to be updated.\n     * @return the <code>ApplicationLink</code> as it is after the update.\n     * @throws ApplicationNotFoundException if no <code>ApplicationLink</code> is found for the given id\n     * @throws AlreadyExistsException if another <code>IApplication</code> already exists with the new name value\n     * @throws UpdateException if an error occurs during the update\n     * @see ApplicationLink\n     * @see ApplicationLinkUpdater\n     * @deprecated as of 9.0.0, Applications should be updated at startup. This also concerns application links\n     *             introduced in 10.2.0.\n     */\n    @Deprecated(since = \"10.2.0\")\n    ApplicationLink updateApplicationLink(long applicationId, ApplicationLinkUpdater updater)\n            throws ApplicationNotFoundException, UpdateException, AlreadyExistsException;\n\n    /**\n     * Searches for {@link Application}s with specific search criteria. Use\n     * {@link org.bonitasoft.engine.business.application.ApplicationSearchDescriptor} to\n     * know the available filters.\n     *\n     * @param searchOptions the search criteria. See {@link SearchOptions} for details.\n     * @return a {@link SearchResult} containing the number and the list of applications matching the search criteria.\n     * @throws SearchException if an error occurs during search\n     * @see Application\n     * @see org.bonitasoft.engine.business.application.ApplicationSearchDescriptor\n     * @see SearchOptions\n     * @see SearchResult\n     * @deprecated as of 10.2.0, use {@link #searchIApplications(SearchOptions)} instead to include application links.\n     */\n    @Deprecated(since = \"10.2.0\")\n    SearchResult<Application> searchApplications(final SearchOptions searchOptions) throws SearchException;\n\n    /**\n     * Searches for {@link IApplication}s with specific search criteria. Use\n     * {@link org.bonitasoft.engine.business.application.ApplicationSearchDescriptor} to\n     * know the available filters.\n     *\n     * @param searchOptions the search criteria. See {@link SearchOptions} for details.\n     * @return a {@link SearchResult} containing the number and the list of applications matching the search criteria.\n     * @throws SearchException if an error occurs during search\n     * @see IApplication\n     * @see org.bonitasoft.engine.business.application.ApplicationSearchDescriptor\n     * @see SearchOptions\n     * @see SearchResult\n     */\n    SearchResult<IApplication> searchIApplications(final SearchOptions searchOptions) throws SearchException;\n\n    /**\n     * Creates an {@link ApplicationPage}\n     *\n     * @param applicationId the identifier of the {@link org.bonitasoft.engine.business.application.Application} to\n     *        which the\n     *        {@link org.bonitasoft.engine.page.Page} will be associated\n     * @param pageId the identifier of <code>Page</code> to be associated to the <code>Application</code>\n     * @param token the token that this <code>Page</code> will take in this <code>ApplicationPage</code>. The token must\n     *        be unique for a given application and\n     *        should contain only alpha numeric characters and the following special characters '-', '.', '_' or '~'. In\n     *        addition, the following words are reserved\n     *        key words and cannot be used\n     *        as token: 'api', 'content', 'theme'.\n     * @return the created {@link ApplicationPage}\n     * @throws AlreadyExistsException if the token is already used by another <code>ApplicationPage</code> on this\n     *         <code>Application</code>\n     * @throws CreationException if an error occurs during the creation\n     * @throws ApplicationNotFoundException if the referenced application does not exist.\n     * @see ApplicationPage\n     * @see Application\n     * @see org.bonitasoft.engine.page.Page\n     * @deprecated as of 9.0.0, Application page should be created at startup.\n     */\n    @Deprecated(since = \"9.0.0\")\n    ApplicationPage createApplicationPage(long applicationId, long pageId, String token)\n            throws AlreadyExistsException, CreationException,\n            ApplicationNotFoundException;\n\n    /**\n     * Retrieves the {@link ApplicationPage} for the given {@code Application} token and {@code ApplicationPage} token\n     *\n     * @param applicationToken the <code>Application</code> name\n     * @param applicationPageToken the <code>ApplicationPage</code> token\n     * @return the {@link ApplicationPage} for the given {@code Application} token and {@code ApplicationPage} token\n     * @throws ApplicationPageNotFoundException if no {@link ApplicationPage} is found for the given\n     *         <code>Application</code> token and\n     *         <code>ApplicationPage</code> token\n     * @see ApplicationPage\n     */\n    ApplicationPage getApplicationPage(String applicationToken, String applicationPageToken)\n            throws ApplicationPageNotFoundException;\n\n    /**\n     * Retrieves the {@link ApplicationPage} from its identifier\n     *\n     * @param applicationPageId the {@code ApplicationPage} identifier\n     * @return the {@link ApplicationPage} from its identifier\n     * @throws ApplicationPageNotFoundException if no {@link ApplicationPage} is found for the given identifier\n     * @see ApplicationPage\n     */\n    ApplicationPage getApplicationPage(long applicationPageId) throws ApplicationPageNotFoundException;\n\n    /**\n     * Deletes an {@link ApplicationPage} by its identifier. All related\n     * {@link org.bonitasoft.engine.business.application.ApplicationMenu} will be\n     * automatically deleted.\n     *\n     * @param applicationPageId the {@code ApplicationPage} identifier\n     * @throws DeletionException if an error occurs during the deletion\n     * @see ApplicationPage\n     * @see org.bonitasoft.engine.business.application.ApplicationMenu\n     */\n    void deleteApplicationPage(long applicationPageId) throws DeletionException;\n\n    /**\n     * Searches for {@link ApplicationPage}s with specific search criteria.\n     *\n     * @param searchOptions the search criteria. See {@link SearchOptions} for details. Use\n     *        {@link org.bonitasoft.engine.business.application.ApplicationPageSearchDescriptor} to know the available\n     *        filters.\n     * @return a {@link SearchResult} containing the number and the list of\n     *         {@code org.bonitasoft.engine.business.application.ApplicationPageSearchDescriptor}s\n     *         matching the search criteria.\n     * @throws SearchException if an error occurs during the search execution\n     * @see ApplicationPage\n     * @see org.bonitasoft.engine.business.application.ApplicationPageSearchDescriptor\n     * @see SearchOptions\n     * @see SearchResult\n     */\n    SearchResult<ApplicationPage> searchApplicationPages(final SearchOptions searchOptions) throws SearchException;\n\n    /**\n     * Defines which {@link ApplicationPage} will represent the {@link Application} home page\n     *\n     * @param applicationId the {@code Application} identifier\n     * @param applicationPageId the identifier of the {@code ApplicationPage} to be used as home page\n     * @throws UpdateException if an error occurs during the update\n     * @throws ApplicationNotFoundException if no {@code Application} is found with the given id\n     * @see Application\n     * @see ApplicationPage\n     * @deprecated as of 9.0.0, Application home page should be defined at startup.\n     */\n    @Deprecated(since = \"9.0.0\")\n    void setApplicationHomePage(long applicationId, long applicationPageId)\n            throws UpdateException, ApplicationNotFoundException;\n\n    /**\n     * Retrieves the {@link ApplicationPage} defined as the {@link Application} home page\n     *\n     * @param applicationId the {@code Application} identifier\n     * @return the t{@code ApplicationPage} defined as {@code Application} home page\n     * @throws ApplicationPageNotFoundException if no home page is found for the given application\n     * @see Application\n     * @see ApplicationPage\n     */\n    ApplicationPage getApplicationHomePage(long applicationId) throws ApplicationPageNotFoundException;\n\n    /**\n     * Creates a {@link ApplicationMenu} based on the supplied {@link ApplicationMenuCreator}. The new created\n     * {@code ApplicationMenu} will be ordered at the\n     * last position of its level with an auto generated index.\n     *\n     * @param applicationMenuCreator creator describing the characteristics of the {@code ApplicationMenu} to be created\n     * @return the created {@code ApplicationMenu}\n     * @throws CreationException if an error occurs during the creation\n     * @see ApplicationMenu\n     * @see ApplicationMenuCreator\n     * @deprecated as of 9.0.0, Application menu should be created at startup.\n     */\n    @Deprecated(since = \"9.0.0\")\n    ApplicationMenu createApplicationMenu(ApplicationMenuCreator applicationMenuCreator) throws CreationException;\n\n    /**\n     * Updates an {@link org.bonitasoft.engine.business.application.ApplicationMenu} based on the information supplied\n     * by the\n     * {@link org.bonitasoft.engine.business.application.ApplicationMenuUpdater}.\n     * <p>\n     * When the {@code ApplicationMenu} index is updated all other {@code ApplicationMenu}s in the same level will have\n     * indexes automatically updated in order\n     * to keep indexes coherency. For instance, when an {@code ApplicationMenu} is moved from index 4 to index 2, the\n     * {@code ApplicationMenu} previously at\n     * index 2 will be moved to index 3 and the {@code ApplicationMenu} previously at index 3 will be moved to index 4.\n     * </p>\n     *\n     * @param applicationMenuId the {@code ApplicationMenu} identifier\n     * @param updater the {@code ApplicationMenuUpdater} describing the fields to be updated.\n     * @return the {@code ApplicationMenu} up to date\n     * @throws ApplicationMenuNotFoundException if no {@code ApplicationMenu} is found for the given identifier\n     * @throws UpdateException if an exception occurs during the update\n     * @see org.bonitasoft.engine.business.application.ApplicationMenu\n     * @see org.bonitasoft.engine.business.application.ApplicationMenuUpdater\n     * @deprecated as of 9.0.0, Application menu should be updated at startup.\n     */\n    @Deprecated(since = \"9.0.0\")\n    ApplicationMenu updateApplicationMenu(long applicationMenuId, ApplicationMenuUpdater updater)\n            throws ApplicationMenuNotFoundException, UpdateException;\n\n    /**\n     * Retrieves the {@link ApplicationMenu} from its identifier\n     *\n     * @param applicationMenuId the {@code ApplicationMenu} menu identifier\n     * @return the {@code ApplicationMenu} from its identifier\n     * @throws ApplicationMenuNotFoundException if no {@code ApplicationMenu} is found for the given identifier\n     * @see ApplicationMenu\n     */\n    ApplicationMenu getApplicationMenu(long applicationMenuId) throws ApplicationMenuNotFoundException;\n\n    /**\n     * Deletes an {@link ApplicationMenu} by its identifier. All children {@code ApplicationMenu} will be automatically\n     * deleted.\n     * <p>\n     * When an {@code ApplicationMenu} is deleted all others {@code ApplicationMenu}s having index greater than the\n     * index of deleted {@code ApplicationMenu} in\n     * the same level will be automatically updated in order to keep indexes coherency.\n     * </p>\n     *\n     * @param applicationMenuId the {@code ApplicationMenu} identifier\n     * @throws DeletionException if an error occurs during the deletion\n     * @see ApplicationMenu\n     */\n    void deleteApplicationMenu(long applicationMenuId) throws DeletionException;\n\n    /**\n     * Searches for {@link ApplicationMenu}s with specific search criteria.\n     *\n     * @param searchOptions the search criteria. See {@link SearchOptions} for details. Use\n     *        {@link org.bonitasoft.engine.business.application.ApplicationMenuSearchDescriptor} to know the available\n     *        filters\n     * @return a {@link SearchResult} containing the number and the list of {@code ApplicationMenu}s matching the search\n     *         criteria.\n     * @throws SearchException if an error occurs during search\n     * @see ApplicationMenu\n     * @see SearchOptions\n     * @see org.bonitasoft.engine.business.application.ApplicationMenuSearchDescriptor\n     * @see SearchResult\n     */\n    SearchResult<ApplicationMenu> searchApplicationMenus(final SearchOptions searchOptions) throws SearchException;\n\n    /**\n     * Return all pages names that can be accessed by the profile through applications.\n     * The portal use this method to calculate all permissions for a user.\n     *\n     * @param profileId\n     *        the id of the profile\n     * @return\n     *         list of page name accessible by the profile through applications\n     */\n    List<String> getAllPagesForProfile(long profileId);\n\n    /**\n     * Returns all pages names that can be accessed by the profile through applications.\n     * The portal use this method to calculate all permissions for a user.\n     * <br/>\n     * <b>WARNING:</b> this method is <b>Experimental</b>, it might change in later versions\n     *\n     * @param profile\n     *        the name of the profile\n     * @return\n     *         list of page name accessible by the profile through applications\n     * @since 7.8\n     */\n    @Experimental\n    List<String> getAllPagesForProfile(String profile);\n\n    /**\n     * Exports the {@link IApplication}s which identifier is in\n     * {@code applicationIds}\n     *\n     * @param applicationIds the identifiers of {@code Application}s to be exported\n     * @return a byte array representing the content of XML file containing the exported {@code Application}s\n     * @throws ExportException if an exception occurs during the export.\n     * @see org.bonitasoft.engine.business.application.IApplication\n     */\n    byte[] exportApplications(long... applicationIds) throws ExportException;\n\n    /**\n     * Imports {@link org.bonitasoft.engine.business.application.Application}s based on a XML file content.\n     * <p>\n     * Before importing {@code Application}s ensure that all {@link org.bonitasoft.engine.profile.Profile}s referenced\n     * by {@code Application}s and all\n     * {@link org.bonitasoft.engine.page.Page}s referenced by\n     * {@link org.bonitasoft.engine.business.application.ApplicationPage}s are available.\n     * <ul>\n     * <li>When the {@code Profile} does not exist the {@code Application} will be imported, but no {@code Profile} will\n     * be associated to it. An\n     * {@link org.bonitasoft.engine.api.ImportError} will be added to the {@link org.bonitasoft.engine.api.ImportStatus}\n     * related to this {@code Application}.\n     * </li>\n     * <li>When a {@code Page} does not exist the related {@code ApplicationPage} and\n     * {@link org.bonitasoft.engine.business.application.ApplicationMenu}s\n     * pointing to this {@code ApplicationPage} will not be created. An {@code ImportError} will be added to the\n     * {@code ImportStatus} related to the\n     * {@code Application} containing this {@code ApplicationPage}.</li>\n     * </ul>\n     * </p>\n     *\n     * @param xmlContent a byte array representing the content of XML file containing the applications to be imported.\n     * @param policy the {@link org.bonitasoft.engine.business.application.ApplicationImportPolicy} used to execute the\n     *        import\n     * @return a {@link java.util.List} of {@link org.bonitasoft.engine.api.ImportStatus} representing the\n     *         {@code ImportStatus} for each imported\n     *         {@code Application}\n     * @throws ImportException if an error occurs during the import\n     * @throws org.bonitasoft.engine.exception.AlreadyExistsException if one of applications being imported already\n     *         exists and the policy\n     *         {@code ApplicationImportPolicy.FAIL_ON_DUPLICATES} is used\n     * @see org.bonitasoft.engine.business.application.Application\n     * @see org.bonitasoft.engine.business.application.ApplicationImportPolicy\n     * @see org.bonitasoft.engine.api.ImportStatus\n     * @see org.bonitasoft.engine.api.ImportError\n     * @see org.bonitasoft.engine.business.application.ApplicationPage\n     * @see org.bonitasoft.engine.business.application.ApplicationMenu\n     * @see org.bonitasoft.engine.profile.Profile\n     * @see org.bonitasoft.engine.page.Page\n     * @deprecated as of 9.0.0, Applications should be imported at startup.\n     */\n    @Deprecated(since = \"9.0.0\")\n    List<ImportStatus> importApplications(final byte[] xmlContent, final ApplicationImportPolicy policy)\n            throws ImportException, AlreadyExistsException;\n\n    /**\n     * Return the icon associated to the application\n     *\n     * @param applicationId id of the application\n     * @return Icon of the application or null or there is no icon for the application\n     * @throws ApplicationNotFoundException when there is no application having that id\n     */\n    Icon getIconOfApplication(long applicationId) throws ApplicationNotFoundException;\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/BusinessDataAPI.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.data.DataNotFoundException;\nimport org.bonitasoft.engine.business.data.BusinessDataReference;\n\n/**\n * This API allows to list the {@link org.bonitasoft.engine.business.data.BusinessDataReference} related to specific\n * {@link org.bonitasoft.engine.bpm.process.ProcessInstance}\n *\n * @author Elias Ricken de Medeiros\n * @author Laurent Leseigneur\n * @since 7.0.0\n * @see org.bonitasoft.engine.business.data.BusinessDataReference\n * @see org.bonitasoft.engine.bpm.process.ProcessInstance\n */\npublic interface BusinessDataAPI {\n\n    /**\n     * Returns the {@link org.bonitasoft.engine.business.data.BusinessDataReference} of the named business data of the\n     * process instance. The value is returned in a DataInstance object.\n     *\n     * @param businessDataName\n     *        The name of the business data\n     * @param processInstanceId\n     *        The identifier of the process instance\n     * @return the reference of the business data\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws org.bonitasoft.engine.bpm.data.DataNotFoundException\n     *         If the specified business data value cannot be found.\n     * @deprecated As of 7.3, replaced by {@link ProcessAPI#getProcessInstanceExecutionContext(long)}\n     *             ({@link ProcessAPI#getArchivedProcessInstanceExecutionContext(long)} for\n     *             archived process instances). In\n     *             the map returned by {@link ProcessAPI#getProcessInstanceExecutionContext(long)} you can get\n     *             {@link BusinessDataReference} by using\n     *             \"yourBusinessDataName_ref\" key (business data name as declared in\n     *             the process definition followed by \"_ref\" suffix).\n     */\n    @Deprecated(since = \"7.3\")\n    BusinessDataReference getProcessBusinessDataReference(String businessDataName, long processInstanceId)\n            throws DataNotFoundException;\n\n    /**\n     * Lists the paginated {@link BusinessDataReference}s of the process instance order by identifier.\n     *\n     * @param processInstanceId\n     *        The identifier of the process instance\n     * @param startIndex\n     *        the index of the first result (starting from 0).\n     * @param maxResults\n     *        the maximum number of result per page\n     * @return the paginated references of the business data\n     * @deprecated As of 7.3, replaced by {@link ProcessAPI#getProcessInstanceExecutionContext(long)}\n     *             ({@link ProcessAPI#getArchivedProcessInstanceExecutionContext(long)} for\n     *             archived process instances). In\n     *             the map returned by {@link ProcessAPI#getProcessInstanceExecutionContext(long)} you can get\n     *             {@link BusinessDataReference} by using\n     *             \"yourBusinessDataName_ref\" key (business data name as declared in\n     *             the process definition followed by \"_ref\" suffix).\n     */\n    @Deprecated(since = \"7.3\")\n    List<BusinessDataReference> getProcessBusinessDataReferences(long processInstanceId, int startIndex,\n            int maxResults);\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/CommandAPI.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.command.CommandCriterion;\nimport org.bonitasoft.engine.command.CommandDescriptor;\nimport org.bonitasoft.engine.command.CommandExecutionException;\nimport org.bonitasoft.engine.command.CommandNotFoundException;\nimport org.bonitasoft.engine.command.CommandParameterizationException;\nimport org.bonitasoft.engine.command.CommandUpdater;\nimport org.bonitasoft.engine.command.DependencyNotFoundException;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\n\n/**\n * Manipulate tenant commands. A command can be registered, unregistered, and executed with parameters.\n * <p>\n * Commands are used to extend engine behavior, and are classes that are called from this API and executed on the server\n * side. <br>\n * in the execute method of this class.\n * </p>\n * <p>\n * A command is composed of a jar containing at least one class that implements\n * <code>org.bonitasoft.engine.command.RuntimeCommand</code>.\n * The behavior of the command must be defined in the execute method of this class.<br>\n * RuntimeCommand is a class available only in bonita-server.jar. In order to create the jar you will need to have a\n * dependency on that jar.\n * <p>\n * The jar containing the command class must be added to the engine using the {@link CommandAPI#addDependency} method\n * with a name to identify the dependency so\n * that it can be removed later.<br>\n * Then the command must be registered using {@link CommandAPI#register(String, String, String)} with a name to identify\n * it and an implementation that is the\n * fully qualified name of the command class.<br>\n * After registration, the command can be executed using {@link CommandAPI#execute(long, Map)} with the id returned by\n * the register method or\n * {@link CommandAPI#execute(String, Map)} with the name of the command and with a map of parameters required by the\n * command.<br>\n * Finally the command can be removed using both {@link CommandAPI#unregister(long)} or\n * {@link CommandAPI#unregister(String)} and\n * {@link CommandAPI#removeDependency(String)} </p>\n *\n * <pre>\n * Code example:<br>\n *\n * In this example we deploy a command named \"myCommandName\". The class that implements <code>RuntimeCommand</code> is <code>org.bonitasoft.engine.command.IntegerCommand</code> and\n * is contained in the jar we deploy using CommandAPI.addDependency.\n * <br>\n * <br>\n * {@code\n * byte[] byteArray = /* read the jar containing the command as a byte array * /\n *\n *  //deploy\n * getCommandAPI().addDependency(\"myCommandDependency\", byteArray);\n * getCommandAPI().register(\"myCommandName\", \"Retrieving the integer value\", \"org.bonitasoft.engine.command.IntegerCommand\");\n *\n *  //execute\n * final Map<String, Serializable> parameters = new HashMap<String, Serializable>();\n * parameters.put(\"aParamterName\", \"aParameterValue\");\n * parameters.put(\"anIntParameter\", 42);\n * Integer theResultOfTheCommandExecution = (Integer) getCommandAPI().execute(\"myCommandName\", parameters);\n *\n *  //undeploy\n * getCommandAPI().unregister(\"myCommandName\");\n * getCommandAPI().removeDependency(\"myCommandDependency\");\n * }\n * </pre>\n *\n * @author Matthieu Chaffotte\n * @author Yanyan Liu\n * @author Celine Souchet\n * @author Emmanuel Duchastenier\n * @author Baptiste Mesta\n * @author Emmanuel Duchastenier\n * @see CommandDescriptor\n * @see #register(String, String, String)\n * @see #unregister(long)\n * @see #addDependency(String, byte[])\n * @see #removeDependency(String)\n * @since 6.0.0\n * @version 6.4.1\n */\npublic interface CommandAPI {\n\n    /**\n     * Add a dependency to the tenant scope.\n     *\n     * @param name\n     *        The name of the dependency.\n     * @param jar\n     *        The JAR content of the dependency.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @throws AlreadyExistsException\n     *         If a dependency with the same name already exists\n     * @throws CreationException\n     *         If an other problem occurs\n     * @since 6.0\n     */\n    void addDependency(String name, byte[] jar) throws AlreadyExistsException, CreationException;\n\n    /**\n     * Remove a dependency to the tenant scope.\n     *\n     * @param name\n     *        The name of the dependency.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @throws DependencyNotFoundException\n     *         If the name does not refer to any existing dependency\n     * @throws DeletionException\n     *         If an other problem occurs\n     * @since 6.0\n     */\n    void removeDependency(String name) throws DependencyNotFoundException, DeletionException;\n\n    /**\n     * Create a new command.\n     *\n     * @param name\n     *        The name of the command\n     * @param description\n     *        The description of the command\n     * @param implementation\n     *        The name of the implementation class of the command. It will be used when executing the command. This\n     *        class is inside the jar of a dependency.\n     * @return The descriptor of the newly created command\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @throws AlreadyExistsException\n     *         If a command with the same name already exists\n     * @throws CreationException\n     *         If an other problem occurs\n     * @since 6.0\n     */\n    CommandDescriptor register(String name, String description, String implementation)\n            throws AlreadyExistsException, CreationException;\n\n    /**\n     * Execute a command according to its name and a map of parameters.\n     *\n     * @param name\n     *        The name of the command\n     * @param parameters\n     *        The parameters of the command\n     * @return The result of the command execution.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @throws CommandNotFoundException\n     *         If the name does not refer to any existing command\n     * @throws CommandParameterizationException\n     *         If a parameter of the command is not correct\n     * @throws CommandExecutionException\n     *         If an other problem occurs\n     * @since 6.0\n     */\n    Serializable execute(String name, Map<String, Serializable> parameters)\n            throws CommandNotFoundException, CommandParameterizationException,\n            CommandExecutionException;\n\n    /**\n     * Execute a command according to its name and a map of parameters. During the execution of this method, the\n     * command's implementation will have to manage\n     * itself its transactions.\n     *\n     * @param name\n     *        The name of the command\n     * @param parameters\n     *        The parameters of the command\n     * @return The result of the command execution.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @throws CommandNotFoundException\n     *         If the name does not refer to any existing command\n     * @throws CommandParameterizationException\n     *         If a parameter of the command is not correct\n     * @throws CommandExecutionException\n     *         If an other problem occurs\n     * @since 6.2\n     */\n    Serializable executeWithUserTransactions(String name, Map<String, Serializable> parameters)\n            throws CommandNotFoundException,\n            CommandParameterizationException, CommandExecutionException;\n\n    /**\n     * Execute a command according to its id and a map of parameters.\n     *\n     * @param commandId\n     *        The identifier of the command\n     * @param parameters\n     *        The parameters of the command\n     * @return The result of the command execution.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @throws CommandNotFoundException\n     *         If the name does not refer to any existing command\n     * @throws CommandParameterizationException\n     *         If a parameter of the command is not correct\n     * @throws CommandExecutionException\n     *         If an other problem occurs\n     * @since 6.0\n     */\n    Serializable execute(long commandId, Map<String, Serializable> parameters)\n            throws CommandNotFoundException, CommandParameterizationException,\n            CommandExecutionException;\n\n    /**\n     * Execute a command according to its id and a map of parameters. During the execution of this method, the command's\n     * implementation\n     * will have to manage itself its transactions.\n     *\n     * @param commandId\n     *        The identifier of the command\n     * @param parameters\n     *        The parameters of the command\n     * @return The result of the command execution.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @throws CommandNotFoundException\n     *         If the name does not refer to any existing command\n     * @throws CommandParameterizationException\n     *         If a parameter of the command is not correct\n     * @throws CommandExecutionException\n     *         If an other problem occurs\n     * @since 6.2\n     */\n    Serializable executeWithUserTransactions(long commandId, Map<String, Serializable> parameters)\n            throws CommandNotFoundException,\n            CommandParameterizationException, CommandExecutionException;\n\n    /**\n     * Delete a command through its name.\n     *\n     * @param name\n     *        The name of the command\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @throws CommandNotFoundException\n     *         If the name does not refer to any existing command.\n     * @throws DeletionException\n     *         If an other problem occurs\n     * @since 6.0\n     */\n    void unregister(String name) throws CommandNotFoundException, DeletionException;\n\n    /**\n     * Get the descriptor of the command.\n     *\n     * @param name\n     *        The name of the command\n     * @return The descriptor of the command\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @throws CommandNotFoundException\n     *         If an other problem occurs\n     * @since 6.0\n     */\n    CommandDescriptor getCommand(String name) throws CommandNotFoundException;\n\n    /**\n     * Get the paginated list of the descriptors of the command according to the sort criterion.\n     *\n     * @param startIndex\n     *        The index of the first element to be retrieved (it starts from zero)\n     * @param maxResults\n     *        The number of {@link CommandDescriptor} to get.\n     * @param sort\n     *        The sorting criterion of the list.\n     * @return The paginated list of descriptors of the command\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @since 6.0\n     */\n    List<CommandDescriptor> getAllCommands(int startIndex, int maxResults, CommandCriterion sort);\n\n    /**\n     * Update a command according to the update descriptor.\n     *\n     * @param name\n     *        The name of the command\n     * @param updateDescriptor\n     *        The update descriptor (containing the fields to update &amp; their new value).\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @throws CommandNotFoundException\n     *         If the name does not refer to any existing command\n     * @throws UpdateException\n     *         If an other problem occurs.\n     * @since 6.0\n     */\n    void update(String name, CommandUpdater updateDescriptor) throws CommandNotFoundException, UpdateException;\n\n    /**\n     * Delete all commands.\n     *\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @throws DeletionException\n     *         If an other problem occurs.\n     * @since 6.0\n     */\n    void unregisterAll() throws DeletionException;\n\n    /**\n     * Get the list of the descriptor of the user commands (no system command).\n     *\n     * @param startIndex\n     *        The index of the first element to be retrieved (it starts from zero)\n     * @param maxResults\n     *        The number of {@link CommandDescriptor} to get.\n     * @param sort\n     *        The sorting criterion of the list.\n     * @return The paginated list of descriptors of the command\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @since 6.0\n     */\n    List<CommandDescriptor> getUserCommands(final int startIndex, final int maxResults, final CommandCriterion sort);\n\n    /**\n     * Get the descriptor of the command by its identifier.\n     *\n     * @param commandId\n     *        The identifier of command\n     * @return The descriptor of the command\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @throws CommandNotFoundException\n     *         If the identifier does not refer to any existing command.\n     * @since 6.0\n     */\n    CommandDescriptor get(long commandId) throws CommandNotFoundException;\n\n    /**\n     * Update a command according to the update descriptor.\n     *\n     * @param commandId\n     *        The identifier of command to update.\n     * @param updateDescriptor\n     *        The update descriptor\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @throws CommandNotFoundException\n     *         If the identifier does not refer to any existing command.\n     * @throws UpdateException\n     *         If an other problem occurs.\n     * @since 6.0\n     */\n    void update(long commandId, CommandUpdater updateDescriptor) throws CommandNotFoundException, UpdateException;\n\n    /**\n     * Delete a command through its id.\n     *\n     * @param commandId\n     *        The identifier of command\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @throws CommandNotFoundException\n     *         If the name does not refer to any existing command.\n     * @throws DeletionException\n     *         If an other problem occurs.\n     * @since 6.0\n     */\n    void unregister(long commandId) throws CommandNotFoundException, DeletionException;\n\n    /**\n     * Search commands corresponding to the criteria.\n     *\n     * @param searchOptions\n     *        The criterion used during the search\n     * @return A {@link SearchResult} containing the descriptor of the commands.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @throws SearchException\n     *         If an other problem occurs.\n     * @since 6.0\n     */\n    SearchResult<CommandDescriptor> searchCommands(SearchOptions searchOptions) throws SearchException;\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/CustomUserInfoAPI.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.identity.CustomUserInfo;\nimport org.bonitasoft.engine.identity.CustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.CustomUserInfoDefinitionCreator;\nimport org.bonitasoft.engine.identity.CustomUserInfoValue;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\n\n/**\n * CustomUserInfoAPI forms part of the {@link OrganizationAPI} and gives access to all the Administration operations\n * available on\n * {@link org.bonitasoft.engine.identity.CustomUserInfoDefinition} and\n * {@link org.bonitasoft.engine.identity.CustomUserInfoValue}: creation,\n * deletion and retrieve methods\n *\n * @author Vincent Elcrin\n * @see org.bonitasoft.engine.api.OrganizationAPI\n * @see org.bonitasoft.engine.identity.CustomUserInfoDefinition\n * @see org.bonitasoft.engine.identity.CustomUserInfoValue\n * @since 6.3\n */\npublic interface CustomUserInfoAPI {\n\n    /**\n     * Creates a new {@link org.bonitasoft.engine.identity.CustomUserInfoDefinition} that will be available for all\n     * {@link org.bonitasoft.engine.identity.User}\n     * s in the organization. In order to set a value for the new created {@code CustomUserInfoDefinition} for a specif\n     * {@link org.bonitasoft.engine.identity.User} use the method {@link #setCustomUserInfoValue(long, long, String)}.\n     * <p></p>\n     * <p>Example:</p>\n     *\n     * <pre>\n     *\n     * CustomUserInfoDefinitionCreator creator = new CustomUserInfoDefinitionCreator(\"Skills\", \"The user skills\");\n     * CustomUserInfoDefinition userInfoDef = identityAPI.createCustomUserInfoDefinition(creator);\n     * </pre>\n     *\n     * <p></p>\n     *\n     * @param creator\n     *        A {@link org.bonitasoft.engine.identity.CustomUserInfoDefinitionCreator} describing all information about\n     *        the {@code CustomUserInfoDefinition} to\n     *        be created\n     * @return The created {@code CustomUserInfoDefinition}\n     * @throws AlreadyExistsException\n     *         If a {@code CustomUserInfoDefinition} already exists with the same name.\n     * @throws CreationException\n     *         If an error occurs during the creation\n     * @see org.bonitasoft.engine.identity.CustomUserInfoDefinition\n     * @see org.bonitasoft.engine.identity.User\n     * @see org.bonitasoft.engine.identity.CustomUserInfoDefinitionCreator\n     * @since 6.3\n     */\n    CustomUserInfoDefinition createCustomUserInfoDefinition(CustomUserInfoDefinitionCreator creator)\n            throws AlreadyExistsException, CreationException;\n\n    /**\n     * Retrieves the list of {@link CustomUserInfoDefinition} according to the given pagination criteria, ordered by\n     * name.\n     *\n     * @param startIndex\n     *        The index for the first element to be retrieved (starts from zero)\n     * @param maxResult\n     *        The maximum number of elements to be retrieved.\n     * @return The list of {@code CustomUserInfoDefinition} according to the given pagination criteria, ordered by name.\n     * @since 6.3\n     * @see org.bonitasoft.engine.identity.CustomUserInfoDefinition\n     */\n\n    List<CustomUserInfoDefinition> getCustomUserInfoDefinitions(int startIndex, int maxResult);\n\n    /**\n     * Retrieves the number of existing {@link org.bonitasoft.engine.identity.CustomUserInfoDefinition}s.\n     *\n     * @return The number of existing {@code CustomUserInfoDefinition}s.\n     * @since 6.3\n     * @see org.bonitasoft.engine.identity.CustomUserInfoDefinition\n     */\n    long getNumberOfCustomInfoDefinitions();\n\n    /**\n     * Deletes the {@link CustomUserInfoDefinition} identified by the given id. All {@link CustomUserInfoValue} related\n     * to this {@code CustomUserInfoDefinition}\n     * will be deleted as well.\n     *\n     * @param id\n     *        The identifier of the {@code CustomUserInfoDefinition}\n     * @throws DeletionException\n     *         If an error occurs during deletion\n     * @since 6.3\n     * @see org.bonitasoft.engine.identity.CustomUserInfoDefinition\n     * @see org.bonitasoft.engine.identity.CustomUserInfoValue\n     */\n    void deleteCustomUserInfoDefinition(long id) throws DeletionException;\n\n    /**\n     * Retrieves the list of {@link CustomUserInfo} for the given user, ordered by\n     * {@link org.bonitasoft.engine.identity.CustomUserInfoDefinition} name. For\n     * {@code CustomUserInfo}s which have {@code CustomUserInfoDefinition} without a related\n     * {@link CustomUserInfoValue}, the field value will be null.\n     *\n     * @param userId\n     *        The identifier of the {@link org.bonitasoft.engine.identity.User}\n     * @param startIndex\n     *        The index of the first element to be retrieved (it starts from zero)\n     * @param maxResult\n     *        The maximum elements to be retrieved.\n     * @return The list of {@link CustomUserInfo} for the given {@code User}, ordered by\n     *         {@code CustomUserInfoDefinition} name.\n     * @see CustomUserInfoDefinition\n     * @see CustomUserInfoValue\n     * @see org.bonitasoft.engine.identity.CustomUserInfo\n     * @see org.bonitasoft.engine.identity.User\n     * @since 6.3\n     */\n    List<CustomUserInfo> getCustomUserInfo(long userId, int startIndex, int maxResult);\n\n    /**\n     * Searches {@link org.bonitasoft.engine.identity.CustomUserInfoValue}s according to the criteria contained in the\n     * given\n     * {@link org.bonitasoft.engine.search.SearchOptions}. In order to know which fields can be used in filters and\n     * sorting, please refer to\n     * {@link org.bonitasoft.engine.identity.CustomUserInfoValueSearchDescriptor}.\n     * <p></p>\n     * Example: searches the first 10 {@code CustomUserInfoValue}s having the given {@code CustomUserInfoDefinition}\n     * (referenced by its identifier) with the\n     * given value. The result is ordered by the related {@code User} identifier:\n     *\n     * <pre>\n     * SearchOptionsBuilder optionsBuilder = new SearchOptionsBuilder(0, 10);\n     * optionsBuilder.filter(CustomUserInfoValueSearchDescriptor.DEFINITION_ID, userInfoDefinition.getId());\n     * optionsBuilder.filter(CustomUserInfoValueSearchDescriptor.VALUE, value);\n     * optionsBuilder.sort(CustomUserInfoValueSearchDescriptor.USER_ID, Order.ASC);\n     * SearchResult&lt;CustomUserInfoValue&gt; searchResult = identityAPI.searchCustomUserInfoValues(optionsBuilder.done());\n     * </pre>\n     *\n     * @param options\n     *        The {@link org.bonitasoft.engine.search.SearchOptions} containing the search criteria\n     * @return The {@link org.bonitasoft.engine.search.SearchResult} containing the number and the list of\n     *         {@code CustomUserInfoValue}s matching the criteria\n     * @since 6.3\n     * @see org.bonitasoft.engine.search.SearchOptions\n     * @see org.bonitasoft.engine.identity.CustomUserInfoValue\n     * @see org.bonitasoft.engine.identity.CustomUserInfoValueSearchDescriptor\n     * @see org.bonitasoft.engine.search.SearchResult\n     */\n    SearchResult<CustomUserInfoValue> searchCustomUserInfoValues(SearchOptions options);\n\n    /**\n     * Defines the value of a {@link org.bonitasoft.engine.identity.CustomUserInfoDefinition} for a given\n     * {@link org.bonitasoft.engine.identity.User}.\n     *\n     * @param definitionId\n     *        The identifier of the {@code CustomUserInfoDefinition}\n     * @param userId\n     *        The identifier of the {@code User}\n     * @param value\n     *        The {@code Custom User Information} value\n     * @return a {@link org.bonitasoft.engine.identity.CustomUserInfoValue} representing the value of the given\n     *         {@code CustomUserInfoDefinition} for the given\n     *         {@code User}\n     * @throws org.bonitasoft.engine.exception.UpdateException\n     *         When an error occurs during the update.\n     * @since 6.3\n     * @see org.bonitasoft.engine.identity.CustomUserInfoDefinition#getId()\n     * @see org.bonitasoft.engine.identity.User#getId()\n     * @see org.bonitasoft.engine.identity.CustomUserInfoValue\n     */\n    CustomUserInfoValue setCustomUserInfoValue(long definitionId, long userId, String value) throws UpdateException;\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/DocumentAPI.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.document.ArchivedDocument;\nimport org.bonitasoft.engine.bpm.document.ArchivedDocumentNotFoundException;\nimport org.bonitasoft.engine.bpm.document.Document;\nimport org.bonitasoft.engine.bpm.document.DocumentAttachmentException;\nimport org.bonitasoft.engine.bpm.document.DocumentCriterion;\nimport org.bonitasoft.engine.bpm.document.DocumentException;\nimport org.bonitasoft.engine.bpm.document.DocumentNotFoundException;\nimport org.bonitasoft.engine.bpm.document.DocumentValue;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.identity.UserNotFoundException;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\n\n/**\n * Manipulate documents that are attached to a process instance.\n * <p>\n * A document can be stored directly with the process instance, or by reference. If you store a document by reference,\n * the process instance contains a document\n * object that has metadata describing the document: its name, content MimeType, the name of the file of the document,\n * and a URL giving the location of the\n * document. The choice of direct local storage or storage by reference depends on the performance profile of document\n * access within an instance of the process.\n * If you require frequent and rapid access to update the document and the document is not large, use direct storage. If\n * the document is large or is not\n * accessed frequently within a process instance, or is not updated by the process, store it by reference.\n * <p>\n * Multiple versions of a document can be stored. You can retrieve the latest version or the version that was current at\n * a given milestone (for example process\n * instantiation, or activity completion).\n *\n * @author Emmanuel Duchastenier\n * @author Baptiste Mesta\n */\npublic interface DocumentAPI {\n\n    /**\n     * Attach a document by reference to the specified process instance.\n     * <p>\n     * The document itself does not contain content but is a reference to external content specified by its URL.\n     * The author of the document will be the user currently calling the API method. Note that the operations of a task\n     * are not executed by the assigned user,\n     * but directly by the System. It means that calling this method directly from an operation inside a task (through a\n     * groovy script for example) will attach a\n     * document whose author is the user System.\n     * </p>\n     *\n     * @param processInstanceId\n     *        The identifier of the process instance\n     * @param documentName\n     *        The name of the document\n     * @param fileName\n     *        The filename of the document content\n     * @param mimeType\n     *        The MimeType of the document content (optional)\n     * @param url\n     *        The URL of the document content\n     * @return a document object\n     * @throws ProcessInstanceNotFoundException\n     *         If the identifier does not refer to an existing process instance.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @throws DocumentAttachmentException\n     *         when an error occurs while attaching the document\n     * @since 6.0\n     */\n    Document attachDocument(long processInstanceId, String documentName, String fileName, String mimeType, String url)\n            throws ProcessInstanceNotFoundException,\n            DocumentAttachmentException;\n\n    /**\n     * Attach a new document version to a process instance.\n     * <p>\n     * Depending on the DocumentValue given the document will be internal (with content) or external (with url).\n     * The document state is archived and is then updated to the new version\n     * </p>\n     *\n     * @param documentId\n     *        The identifier of the document to update\n     * @param documentValue\n     *        The value of the document\n     * @return a document object\n     * @throws ProcessInstanceNotFoundException\n     *         If the identifier does not refer to an existing process instance.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @throws DocumentAttachmentException\n     *         when an error occurs while attaching the document\n     * @throws org.bonitasoft.engine.exception.AlreadyExistsException\n     *         If the document already exists.\n     * @since 6.4.0\n     */\n    Document updateDocument(long documentId, DocumentValue documentValue) throws ProcessInstanceNotFoundException,\n            DocumentAttachmentException, AlreadyExistsException;\n\n    /**\n     * Attach a new document to a process instance.\n     * <br>\n     * Depending on the DocumentValue given the document will be internal (with content) or external (with url).\n     * <ol>\n     * <li>If the target document is a list of document then we append it to the list</li>\n     * <li>If the target document is a list of document and the index is set on the document value then we insert the\n     * element in the list at the specified\n     * index</li>\n     * <li>If the target single document or is non existent in the definition we create it</li>\n     * <li>If the target single document and is already existent an exception is thrown</li>\n     * </ol>\n     * The author of the document will be the user currently calling the API method. Note that the operations of a task\n     * are not executed by the assigned user,\n     * but directly by the System. It means that calling this method directly from an operation inside a task (through a\n     * groovy script for example) will attach add\n     * a document whose author is the user System.\n     *\n     * @param processInstanceId\n     *        The identifier of the process instance\n     * @param documentName\n     *        The name of the document\n     * @param description\n     *        The description of the document\n     * @param documentValue\n     *        The value of the document\n     * @return a document object\n     * @throws ProcessInstanceNotFoundException\n     *         If the identifier does not refer to an existing process instance.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @throws DocumentAttachmentException\n     *         when an error occurs while attaching the document\n     * @throws org.bonitasoft.engine.exception.AlreadyExistsException\n     *         If the document already exists.\n     * @since 6.4.0\n     */\n    Document addDocument(long processInstanceId, String documentName, String description, DocumentValue documentValue)\n            throws ProcessInstanceNotFoundException, DocumentAttachmentException, AlreadyExistsException;\n\n    /**\n     * Attach the given document to the specified process instance.\n     * <p>\n     * The content is stored to enable later retrieval.\n     * The author of the document will be the user currently calling the API method. Note that the operations of a task\n     * are not executed by the assigned user,\n     * but directly by the System. It means that calling this method directly from an operation inside a task (through a\n     * groovy script for example) will attach a\n     * document whose author is the user System.\n     * </p>\n     *\n     * @param processInstanceId\n     *        The identifier of the process instance\n     * @param documentName\n     *        The name of the document\n     * @param fileName\n     *        The name of the file containing the document\n     * @param mimeType\n     *        The MimeType of the document content (optional)\n     * @param documentContent\n     *        The content of the document\n     * @return a document object\n     * @throws ProcessInstanceNotFoundException\n     **         If the identifier does not refer to an existing process instance.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @throws DocumentAttachmentException\n     *         when an error occurs while attaching the document\n     * @since 6.0\n     */\n    Document attachDocument(long processInstanceId, String documentName, String fileName, String mimeType,\n            byte[] documentContent)\n            throws ProcessInstanceNotFoundException, DocumentAttachmentException;\n\n    /**\n     * Attach a new version of a document by reference to the specified process instance. The referenced document is\n     * a new version of the named document.\n     * The author of the document will be the user currently calling the API method. Note that the operations of a task\n     * are not executed by the assigned user,\n     * but directly by the System. It means that calling this method directly from an operation inside a task (through a\n     * groovy script for example) will attach a\n     * document whose author is the user System.\n     *\n     * @param processInstanceId\n     *        The identifier of the process instance\n     * @param documentName\n     *        The name of the document\n     * @param fileName\n     *        The name of the file containing the document\n     * @param mimeType\n     *        The MimeType of the document content (optional)\n     * @param url\n     *        The URL of the document content\n     * @return a document object\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @throws DocumentAttachmentException\n     *         when an error occurs while attaching the new version of the document\n     * @since 6.0\n     */\n    Document attachNewDocumentVersion(long processInstanceId, String documentName, String fileName, String mimeType,\n            String url)\n            throws DocumentAttachmentException;\n\n    /**\n     * Attach a new document version to the specified process instance. The document is a new version of the named\n     * document.\n     * <p>\n     * The content is stored to enable later retrieval.\n     * The author of the document will be the user currently calling the API method. Note that the operations of a task\n     * are not executed by the assigned user,\n     * but directly by the System. It means that calling this method directly from an operation inside a task (through a\n     * groovy script for example) will attach a\n     * document whose author is the user System.\n     * </p>\n     *\n     * @param processInstanceId\n     *        The identifier of the process instance\n     * @param documentName\n     *        The name of the document\n     * @param contentFileName\n     *        The name of the file containing the content of the document\n     * @param contentMimeType\n     *        The MimeType of the document content (optional)\n     * @param documentContent\n     *        The content of the document\n     * @return a document object\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @throws DocumentAttachmentException\n     *         when an error occurs while attaching the new version of the document\n     * @since 6.0\n     */\n    Document attachNewDocumentVersion(long processInstanceId, String documentName, String contentFileName,\n            String contentMimeType, byte[] documentContent)\n            throws DocumentAttachmentException;\n\n    /**\n     * Get the document with the specified identifier.\n     *\n     * @param documentId\n     *        The identifier of the document to retrieve\n     * @return a document object\n     * @throws DocumentNotFoundException\n     *         If the specified identifier does not refer to an existing document.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @since 6.0\n     */\n    Document getDocument(long documentId) throws DocumentNotFoundException;\n\n    /**\n     * Remove the document with the specified identifier and returns it.\n     * <p>\n     * this archive and delete mapping on the process, i.e. the content of the document itself will be kept in database,\n     * use\n     * {@link #deleteContentOfArchivedDocument} to delete the content\n     * </p>\n     *\n     * @param documentId\n     *        The identifier of the document to retrieve\n     * @return the removed document object\n     * @throws DocumentNotFoundException\n     *         If the specified identifier does not refer to an existing document.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @since 6.4.0\n     */\n    Document removeDocument(long documentId) throws DocumentNotFoundException, DeletionException;\n\n    /**\n     * Get the latest version of all documents attached to the specified process instance.\n     *\n     * @param processInstanceId\n     *        The identifier of the process instance\n     * @param pageIndex\n     *        The index of the page\n     * @param numberPerPage\n     *        The number of documents to list per page\n     * @param pagingCriterion\n     *        The sort criterion for the returned list\n     * @return the matching list of documents\n     *         a paginated list of the latest version of each document attached to the process instance\n     * @throws ProcessInstanceNotFoundException\n     *         If the identifier does not refer to an existing process instance.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @throws DocumentException\n     *         when any other error occurs during document handling\n     * @since 6.0\n     */\n    List<Document> getLastVersionOfDocuments(long processInstanceId, int pageIndex, int numberPerPage,\n            DocumentCriterion pagingCriterion)\n            throws ProcessInstanceNotFoundException, DocumentException;\n\n    /**\n     * Get content of the document with the specified identifier.\n     *\n     * @param storageId\n     *        The identifier of the document to retrieve the content from\n     * @return document content as a byte array\n     * @throws DocumentNotFoundException\n     *         If the specified identifier does not refer to an existing document.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         when the session is note valid\n     * @since 6.0\n     */\n    byte[] getDocumentContent(String storageId) throws DocumentNotFoundException;\n\n    /**\n     * Get the last version of the named document for the specified process instance.\n     * This method does not work on archived process instances.\n     *\n     * @param processInstanceId\n     *        The identifier of the process instance that the document is attached to\n     * @param documentName\n     *        The name of the document\n     * @return a document object\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @throws DocumentNotFoundException\n     *         If the specified documentName does not refer to an existing document attached to this process instance\n     * @since 6.0\n     */\n    Document getLastDocument(long processInstanceId, String documentName) throws DocumentNotFoundException;\n\n    /**\n     * Get the version of the named document that was current when the specified process instance is instantiated.\n     * This method search in the archives for the document at the date of the process instantiation.\n     *\n     * @param processInstanceId\n     *        The identifier of the process instance\n     * @param documentName\n     *        The name of the document\n     * @return a document object\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @throws DocumentNotFoundException\n     *         If the specified documentName does not refer to a document attached to the specified process instance.\n     * @since 6.0\n     */\n    Document getDocumentAtProcessInstantiation(long processInstanceId, String documentName)\n            throws DocumentNotFoundException;\n\n    /**\n     * Get the version of the named document when the specified activity completed.\n     * This method search in archives for the document at the date of the activity completion.\n     *\n     * @param activityInstanceId\n     *        The identifier of the activity instance\n     * @param documentName\n     *        The name of the document\n     * @return a document object\n     * @throws DocumentNotFoundException\n     *         If the specified documentName does not refer to an existing document attached to the process instance\n     *         that contains the activity.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @since 6.0\n     */\n    Document getDocumentAtActivityInstanceCompletion(long activityInstanceId, String documentName)\n            throws DocumentNotFoundException;\n\n    /**\n     * Get the number of documents attached to the specified process instance. A document with multiple versions is\n     * counted once.\n     *\n     * @param processInstanceId\n     *        The process instance identifier\n     * @return the number of documents in the specified process instance\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @throws DocumentException\n     *         when an error occurs during document handling\n     * @since 6.0\n     */\n    long getNumberOfDocuments(long processInstanceId) throws DocumentException;\n\n    /**\n     * Search for documents that match the search options.\n     *\n     * @param searchOptions\n     *        A {@link SearchOptions} object defining the search options\n     * @return the matching document list and its total number\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @throws SearchException\n     *         when an error occurs during the search\n     * @since 6.0\n     */\n    SearchResult<Document> searchDocuments(SearchOptions searchOptions) throws SearchException;\n\n    /**\n     * Search for documents that match the search options and are supervised by the specified user.\n     *\n     * @param userId\n     *        The identifier of the supervising user\n     * @param searchOptions\n     *        A {@link SearchOptions} object defining the search options\n     * @return the list of matching documents and the number of such documents\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @throws UserNotFoundException\n     *         when the specified userId does not refer to an existing user\n     * @throws SearchException\n     *         when an error occurs during the search\n     * @since 6.0\n     */\n    SearchResult<Document> searchDocumentsSupervisedBy(long userId, SearchOptions searchOptions)\n            throws UserNotFoundException, SearchException;\n\n    /**\n     * Search for archived documents that meet the search options. An archived document is a document that is not the\n     * latest version.\n     * -- ex: Retrieve documents of a given archived case --\n     *\n     * <pre>\n     * {@code\n     *\n     * public List<ArchivedDocument> retrieveDocuments(DocumentAPI documentAPI,\n     *         ArchivedProcessInstance archivedProcessInstance) {\n     *     SearchOptions searchOptions = new SearchOptionsBuilder(0, Integer.MAX_VALUE)\n     *             .filter(ArchivedDocumentsSearchDescriptor.PROCESSINSTANCE_ID,\n     *                     archivedProcessInstance.getSourceObjectId())\n     *             .done();\n     *     return documentAPI.searchArchivedDocuments(searchOptions).getResult();\n     * }\n     * }\n     * </pre>\n     *\n     * @param searchOptions\n     *        A {@link SearchOptions} object defining the search options\n     * @return the matching archived document list and its total number\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @throws SearchException\n     *         when an error occurs during the search\n     * @since 6.0\n     */\n    SearchResult<ArchivedDocument> searchArchivedDocuments(SearchOptions searchOptions) throws SearchException;\n\n    /**\n     * Search for archived documents that match the search options and are supervised by the specified user. An archived\n     * document is a document that is not the\n     * latest version.\n     *\n     * @param userId\n     *        The identifier of the supervising user\n     * @param searchOptions\n     *        A {@link SearchOptions} object defining the search options\n     * @return the matching archived document list and its total number\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @throws UserNotFoundException\n     *         when the specified userId does not refer to an existing user\n     * @throws SearchException\n     *         when an error occurs during the search\n     * @since 6.0\n     */\n    SearchResult<ArchivedDocument> searchArchivedDocumentsSupervisedBy(long userId, SearchOptions searchOptions)\n            throws UserNotFoundException, SearchException;\n\n    /**\n     * Get an ArchivedDocument based on it's id.\n     *\n     * @param archivedProcessDocumentId\n     *        The identifier of the document\n     * @return an archived document\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @throws ArchivedDocumentNotFoundException\n     *         when the specified identifier does not refer to an archived document\n     * @since 6.0\n     */\n    ArchivedDocument getArchivedProcessDocument(final long archivedProcessDocumentId)\n            throws ArchivedDocumentNotFoundException;\n\n    /**\n     * Get the latest version of the document with the specified identifier.\n     *\n     * @param sourceObjectId\n     *        The identifier of the document\n     * @return an archived document\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @throws ArchivedDocumentNotFoundException\n     *         If the identifier does not refer to any existing archived document.\n     * @since 6.0\n     */\n    ArchivedDocument getArchivedVersionOfProcessDocument(long sourceObjectId) throws ArchivedDocumentNotFoundException;\n\n    /**\n     * Get a document list that have the specified name on the process\n     *\n     * @param processInstanceId\n     *        The identifier of the process instance that contains the list\n     * @param name\n     *        The name of the document\n     * @param fromIndex\n     *        The index of the first element to be retrieved (it starts from zero)\n     * @param numberOfResult\n     *        The max number of result to get\n     * @return\n     *         The document list\n     * @throws DocumentNotFoundException\n     * @since 6.4.0\n     */\n    List<Document> getDocumentList(long processInstanceId, String name, int fromIndex, int numberOfResult)\n            throws DocumentNotFoundException;\n\n    /**\n     * Set the document list on a specified process\n     *\n     * @param processInstanceId\n     *        The identifier of the process instance that contains the list\n     * @param name\n     *        The name of the document list\n     * @param documentsValues the values to set the list with\n     * @throws DocumentException\n     *         If an error occurs\n     * @see org.bonitasoft.engine.bpm.process.ProcessInstance#getId()\n     * @since 6.4.0\n     */\n    void setDocumentList(long processInstanceId, String name, List<DocumentValue> documentsValues)\n            throws DocumentNotFoundException, DocumentException;\n\n    /**\n     * Remove the content of an archived document while keeping it's metadata.\n     * <p>\n     * After calling this method you will not be able to retrieve the content of the document since it will be erased\n     * from the database.\n     * This method can be useful for keeping history of a document without overloading the database.\n     * </p>\n     *\n     * @param archivedDocumentId\n     *        The identifier of the archived document to remove content on\n     * @throws DocumentNotFoundException\n     *         If the identifier does not refer to any existing archived document.\n     * @throws DocumentException\n     *         If an error occurs\n     * @see ArchivedDocument#getId()\n     * @since 6.4.0\n     */\n    void deleteContentOfArchivedDocument(long archivedDocumentId) throws DocumentException, DocumentNotFoundException;\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/Experimental.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\nimport static java.lang.annotation.ElementType.*;\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.Target;\n\n/**\n * An experimental API that can be used but might change in any version of Bonita.\n * Also no guarantee is provided on the correct behavior of this API.\n *\n * @author Baptiste Mesta.\n */\n@Retention(value = CLASS)\n@Target(value = { TYPE, METHOD, PACKAGE })\npublic @interface Experimental {\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/GroupAPI.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.identity.Group;\nimport org.bonitasoft.engine.identity.GroupCreator;\nimport org.bonitasoft.engine.identity.GroupCriterion;\nimport org.bonitasoft.engine.identity.GroupNotFoundException;\nimport org.bonitasoft.engine.identity.GroupUpdater;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\n\n/**\n * GroupAPI forms part of the {@link OrganizationAPI} and gives access to all the Administration operations available on\n * Groups: creation, deletion, updating,\n * search, etc...\n *\n * @author Zhao Na\n * @author Bole Zhang\n * @author Matthieu Chaffotte\n * @author Hongwen Zang\n * @see Group\n */\npublic interface GroupAPI {\n\n    /**\n     * Creates a group from its name and parent path.\n     * <p>\n     * If the group is a top level one, the parent path must be null.\n     * </p>\n     *\n     * @param name\n     *        the name of the group\n     * @param parentPath\n     *        the parent path of the group (null means no parent)\n     * @return the created group\n     * @throws AlreadyExistsException\n     *         If the couple name/parentPath is already taken by an existing group\n     * @throws CreationException\n     *         If an exception occurs during the group creation\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    Group createGroup(String name, String parentPath) throws AlreadyExistsException, CreationException;\n\n    /**\n     * Creates a group.\n     * <p>\n     * It takes the values of the creator in order to create a group.\n     * </p>\n     *\n     * @param creator\n     *        the group creator\n     * @return the created group\n     * @throws AlreadyExistsException\n     *         If the couple name/parentPath is already taken by an existing group\n     * @throws CreationException\n     *         If an exception occurs during group creation\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    Group createGroup(GroupCreator creator) throws AlreadyExistsException, CreationException;\n\n    /**\n     * Updates the group according to the updater values.\n     * <p>\n     * This method also allow to update the icon of the group.\n     * When you update it, the iconId will be set on the group and you can later get it using\n     * {@link IdentityAPI#getIcon(long)}.\n     * Changing the content of the icon will create a new icon and change the iconId of the group.\n     * </p>\n     *\n     * @param groupId\n     *        the identifier of the group\n     * @param updater\n     *        the group updater\n     * @return the updated group\n     * @throws GroupNotFoundException\n     *         If the group identifier does not refer to an existing group\n     * @throws UpdateException\n     *         If an exception occurs during the group update\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    Group updateGroup(long groupId, GroupUpdater updater)\n            throws GroupNotFoundException, UpdateException, AlreadyExistsException;\n\n    /**\n     * Deletes the group.\n     *\n     * @param groupId\n     *        the identifier of the group\n     * @throws DeletionException\n     *         If an exception occurs during the group deletion\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    void deleteGroup(long groupId) throws DeletionException;\n\n    /**\n     * Deletes the groups.\n     *\n     * @param groupIds\n     *        the list of group identifiers\n     * @throws DeletionException\n     *         If an exception occurs during the group deletion\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    void deleteGroups(List<Long> groupIds) throws DeletionException;\n\n    /**\n     * Retrieves the group.\n     *\n     * @param groupId\n     *        the group identifier\n     * @return the group\n     * @throws GroupNotFoundException\n     *         If the group identifier does not refer to an existing group\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs during the group retrieving\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    Group getGroup(long groupId) throws GroupNotFoundException;\n\n    /**\n     * Retrieves the group according to its full path.\n     *\n     * @param groupPath\n     *        the full path of the group (parentPath/name)\n     * @return the group\n     * @throws GroupNotFoundException\n     *         If the group path does not refer to an existing group\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs during the group retrieving\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    Group getGroupByPath(final String groupPath) throws GroupNotFoundException;\n\n    /**\n     * Returns the total number of groups.\n     *\n     * @return the total number of groups\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs during the count retrieving\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    long getNumberOfGroups();\n\n    /**\n     * Retrieves the paginated list of groups.\n     * <p>\n     * It retrieves from the startIndex to the startIndex + maxResults.\n     * </p>\n     *\n     * @param startIndex\n     *        the start index\n     * @param maxResults\n     *        the max number of groups\n     * @param criterion\n     *        the sorting criterion\n     * @return the list of groups\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs during the group retrieving\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    List<Group> getGroups(int startIndex, int maxResults, GroupCriterion criterion);\n\n    /**\n     * Retrieves the groups.\n     * <p>\n     * The map contains the couples groupId/Group.\n     * If a group does not exists, no exception is thrown and no value is added in the map.\n     * </p>\n     *\n     * @param groupIds\n     *        the identifiers of the groups\n     * @return the groups\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs during the group retrieving\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    Map<Long, Group> getGroups(List<Long> groupIds);\n\n    /**\n     * Searches groups according to the criteria containing in the options.\n     *\n     * @param options\n     *        the search criteria\n     * @return the search result\n     * @throws SearchException\n     *         If an exception occurs during the group searching\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    SearchResult<Group> searchGroups(SearchOptions options) throws SearchException;\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/IdentityAPI.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\n/**\n * The Interface IdentityAPI.\n *\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n * @author Feng Hui\n * @author Bole Zhang\n * @author Yanyan Liu\n */\npublic interface IdentityAPI extends UserAPI, RoleAPI, GroupAPI, MembershipAPI, OrganizationAPI, CustomUserInfoAPI {\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/ImportError.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\nimport java.io.Serializable;\n\n/**\n * @author Baptiste Mesta\n * @since 6.3.1\n */\npublic class ImportError implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    public enum Type {\n        USER, GROUP, ROLE, PAGE, PROFILE, APPLICATION_PAGE, LAYOUT, THEME\n    }\n\n    private final String name;\n\n    private final Type type;\n\n    public ImportError(final String name, final Type type) {\n        super();\n        this.name = name;\n        this.type = type;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public Type getType() {\n        return type;\n    }\n\n    @Override\n    public String toString() {\n        final StringBuilder builder = new StringBuilder();\n        builder.append(\"ImportError [name=\");\n        builder.append(name);\n        builder.append(\", type=\");\n        builder.append(type);\n        builder.append(\"]\");\n        return builder.toString();\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + (name == null ? 0 : name.hashCode());\n        result = prime * result + (type == null ? 0 : type.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        final ImportError other = (ImportError) obj;\n        if (name == null) {\n            if (other.name != null) {\n                return false;\n            }\n        } else if (!name.equals(other.name)) {\n            return false;\n        }\n        if (type != other.type) {\n            return false;\n        }\n        return true;\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/ImportStatus.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport lombok.Data;\n\n/**\n * This object represents the status of the import of an entity\n * e.g. for an import of profile this object can be ImportStatus [name=MyProfile, status=ADDED, errors=[]]\n *\n * @author Baptiste Mesta\n * @since 6.3.1\n */\n@Data\npublic class ImportStatus implements Serializable {\n\n    public enum Status {\n        ADDED, REPLACED, SKIPPED\n    }\n\n    private final String name;\n\n    private Status status = Status.ADDED;\n\n    private List<ImportError> errors = new ArrayList<>();\n\n    public void addError(final ImportError error) {\n        errors.add(error);\n    }\n\n    public void addErrors(final List<ImportError> errors) {\n        this.errors.addAll(errors);\n    }\n\n    public void addErrorsIfNotExists(final List<ImportError> errors) {\n        for (ImportError importError : errors) {\n            if (importError != null && !getErrors().contains(importError)) {\n                addError(importError);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/Internal.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\n/**\n * Element annotated with @Internal are for internal usage only and may change without any warning\n *\n * @author Baptiste Mesta\n */\npublic @interface Internal {\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/Logger.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\n/**\n * A logger given to {@link org.bonitasoft.engine.api.permission.PermissionRule}\n *\n * @author Baptiste Mesta\n */\npublic interface Logger {\n\n    void trace(String message, Throwable t);\n\n    void trace(String message);\n\n    void debug(String message, Throwable t);\n\n    void debug(String message);\n\n    void info(String message, Throwable t);\n\n    void info(String message);\n\n    void warning(String message, Throwable t);\n\n    void warning(String message);\n\n    void error(String message, Throwable t);\n\n    void error(String message);\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/LoginAPI.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.platform.LoginException;\nimport org.bonitasoft.engine.platform.LogoutException;\nimport org.bonitasoft.engine.platform.UnknownUserException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.SessionNotFoundException;\n\n/**\n * The LoginAPI allows to log in (and out) onto the Engine. This is a mandatory step to go further using the Engine\n * APIs.\n * Other Engine APIs are only accessible through the returned APISession.\n *\n * @author Matthieu Chaffotte\n * @author Zhang Bole\n * @see APISession\n */\n@NoSessionRequired\npublic interface LoginAPI {\n\n    /**\n     * Connects the user in order to use API methods of the default tenant.\n     * <p>\n     * Brute-force protection for web-based login requests is handled at the servlet layer,\n     * which enforces per-user rate limiting with HTTP 429 responses after repeated failures.\n     * Callers using the Java API directly (embedded engine, without the web layer) should\n     * implement their own rate-limiting or account lockout mechanism if the login endpoint\n     * is exposed to untrusted input.\n     *\n     * @param userName\n     *        the user name\n     * @param password\n     *        the password\n     * @return the session to use with other tenant API methods\n     * @throws LoginException\n     *         occurs when an exception is thrown during login\n     * @throws UnknownUserException\n     *         occurs when the user trying to login is unknown to the engine\n     */\n    APISession login(String userName, String password) throws LoginException, UnknownUserException;\n\n    /**\n     * Connects the user in order to use API methods of the default tenant.\n     * <p>\n     * Brute-force protection for web-based login requests is handled at the servlet layer,\n     * which enforces per-user rate limiting with HTTP 429 responses after repeated failures.\n     * Callers using the Java API directly (embedded engine, without the web layer) should\n     * implement their own rate-limiting or account lockout mechanism if the login endpoint\n     * is exposed to untrusted input.\n     *\n     * @param credentials\n     *        the properties to use to login\n     * @return the session to use with other tenant API methods\n     * @throws LoginException\n     *         occurs when an exception is thrown during login\n     */\n    APISession login(Map<String, Serializable> credentials) throws LoginException;\n\n    /**\n     * Disconnects the logged user on a tenant according to the given session.\n     *\n     * @param session\n     *        the tenant session\n     * @throws SessionNotFoundException\n     *         if the given session is not found on the server side. This may occur when the session has expired.\n     * @throws LogoutException\n     *         occurs when an exception is thrown during the logout\n     */\n    void logout(APISession session) throws SessionNotFoundException, LogoutException;\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/MaintenanceAPI.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.maintenance.MaintenanceDetails;\nimport org.bonitasoft.engine.maintenance.MaintenanceDetailsNotFoundException;\nimport org.bonitasoft.engine.platform.PlatformNotFoundException;\n\n/**\n * This API gives access to maintenance administration tasks such as enabling maintenance mode and enabling/disabling\n * maintenance message.\n */\npublic interface MaintenanceAPI {\n\n    /**\n     * Retrieve platform maintenance details\n     *\n     * @return MaintenanceInfo\n     * @throws MaintenanceDetailsNotFoundException\n     * @throws PlatformNotFoundException\n     */\n    MaintenanceDetails getMaintenanceDetails() throws MaintenanceDetailsNotFoundException, PlatformNotFoundException;\n\n    /**\n     * Enable maintenance mode\n     * This method replaces {@link TenantAdministrationAPI#pause()}\n     * When maintenance mode is enabled, All BPM and BDM APIs are not accessible.\n     *\n     * @throws UpdateException\n     *         if maintenance state cannot be updated.\n     */\n    void enableMaintenanceMode() throws UpdateException;\n\n    /**\n     * Disable maintenance mode\n     * This method replaces {@link TenantAdministrationAPI#resume()}\n     *\n     * @throws UpdateException\n     *         if maintenance state cannot be updated.\n     */\n    void disableMaintenanceMode() throws UpdateException;\n\n    /**\n     * Update maintenance message\n     * This message will be displayed in bonita apps if enabled\n     *\n     * @throws UpdateException\n     *         if maintenance message cannot be updated.\n     */\n    void updateMaintenanceMessage(String message) throws UpdateException;\n\n    /**\n     * Enable maintenance message\n     *\n     * @throws UpdateException\n     *         if maintenance message cannot be enabled.\n     */\n    void enableMaintenanceMessage() throws UpdateException;\n\n    /**\n     * Disable maintenance message\n     *\n     * @throws UpdateException\n     *         if maintenance message cannot be disabled.\n     */\n    void disableMaintenanceMessage() throws UpdateException;\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/MembershipAPI.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.identity.MembershipNotFoundException;\nimport org.bonitasoft.engine.identity.UserMembership;\nimport org.bonitasoft.engine.identity.UserMembershipCriterion;\n\n/**\n * MembershipAPI forms part of the {@link OrganizationAPI} and gives access to all the Administration operations\n * available on <code>UserMembership</code>s:\n * creation, deletion, updating, retrieval, etc...\n *\n * @author Matthieu Chaffotte\n * @author Emmanuel Duchastenier\n * @see UserMembership\n */\npublic interface MembershipAPI {\n\n    /**\n     * Associates the user with the group and the role.\n     * The association is called a user membership.\n     *\n     * @param userId\n     *        the identifier of the user\n     * @param groupId\n     *        the identifier of the group\n     * @param roleId\n     *        the identifier of the role\n     * @return the user membership\n     * @throws AlreadyExistsException\n     *         If the triplet userId/groupId/roleId is already taken by an existing user membership\n     * @throws CreationException\n     *         If an exception occurs during the user membership creation\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    UserMembership addUserMembership(long userId, long groupId, long roleId)\n            throws AlreadyExistsException, CreationException;\n\n    /**\n     * Associates the users with the group and the role.\n     * The association is called a user membership.\n     *\n     * @param userIds\n     *        the identifiers of the users\n     * @param groupId\n     *        the identifier of the group\n     * @param roleId\n     *        the identifier of the role\n     * @throws AlreadyExistsException\n     *         If the triplet userId/groupId/roleId is already taken by an existing user membership\n     * @throws CreationException\n     *         If an exception occurs during the user membership creation\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    void addUserMemberships(List<Long> userIds, long groupId, long roleId)\n            throws AlreadyExistsException, CreationException;\n\n    /**\n     * Changes the association of the user membership.\n     * It associates the user membership to the new role and group identifiers.\n     *\n     * @param userMembershipId\n     *        the identifier of the user membership\n     * @param newGroupId\n     *        the identifier of the new group\n     * @param newRoleId\n     *        the identifier of the new role\n     * @return the updated user membership\n     * @throws MembershipNotFoundException\n     *         If the identifier of the user membership does not refer to an existing user membership\n     * @throws UpdateException\n     *         If an exception occurs during the user membership update\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    UserMembership updateUserMembership(long userMembershipId, long newGroupId, long newRoleId)\n            throws MembershipNotFoundException, UpdateException;\n\n    /**\n     * Deletes the user membership.\n     *\n     * @param userMembershipId\n     *        the identifier of the user membership\n     * @throws DeletionException\n     *         If an exception occurs during the user membership deletion\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    void deleteUserMembership(long userMembershipId) throws DeletionException;\n\n    /**\n     * Deletes the user membership.\n     *\n     * @param userId\n     *        the identifier of the user\n     * @param groupId\n     *        the identifier of the group\n     * @param roleId\n     *        the identifier of the role\n     * @throws DeletionException\n     *         If an exception occurs during the user membership deletion\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    void deleteUserMembership(long userId, long groupId, long roleId) throws DeletionException;\n\n    /**\n     * Deletes the user memberships.\n     *\n     * @param userIds\n     *        the identifiers of the users\n     * @param groupId\n     *        the identifier of the group\n     * @param roleId\n     *        the identifier of the role\n     * @throws DeletionException\n     *         If an exception occurs during the user membership deletion\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    void deleteUserMemberships(List<Long> userIds, long groupId, long roleId) throws DeletionException;\n\n    /**\n     * Retrieves the user membership.\n     *\n     * @param membershipId\n     *        the identifier of the user membership to retrieve.\n     * @return the found <code>UserMembership</code> with the provided id\n     * @throws MembershipNotFoundException\n     *         If the identifier of the user membership does not refer to an existing user membership\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs during the user membership retrieving\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     */\n    UserMembership getUserMembership(long membershipId) throws MembershipNotFoundException;\n\n    /**\n     * Returns the total number of memberships of the user.\n     *\n     * @param userId\n     *        the identifier of the user\n     * @return the total number of memberships of the user\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs during the count retrieving\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     */\n    long getNumberOfUserMemberships(long userId);\n\n    /**\n     * Retrieves the paginated list of user memberships of the user.\n     * It retrieves from the startIndex to the startIndex + maxResults.\n     *\n     * @param userId\n     *        the identifier of the user\n     * @param startIndex\n     *        the start index\n     * @param maxResults\n     *        the max number of user memberships\n     * @param criterion\n     *        the sorting criterion\n     * @return the paginated list of user memberships\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs during the count retrieving\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    List<UserMembership> getUserMemberships(long userId, int startIndex, int maxResults,\n            UserMembershipCriterion criterion);\n\n    /**\n     * Retrieves the paginated list of user memberships of the group.\n     * It retrieves from the startIndex to the startIndex + maxResults.\n     *\n     * @param groupId\n     *        the identifier of the group\n     * @param startIndex\n     *        the start index\n     * @param maxResults\n     *        the max number of user memberships\n     * @return the paginated list of user memberships\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs during the count retrieving\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    List<UserMembership> getUserMembershipsByGroup(long groupId, final int startIndex, final int maxResults);\n\n    /**\n     * Retrieves the paginated list of user memberships of the role.\n     * It retrieves from the startIndex to the startIndex + maxResults.\n     *\n     * @param roleId\n     *        the identifier of the role\n     * @param startIndex\n     *        the start index\n     * @param maxResults\n     *        the max number of user memberships\n     * @return the paginated list of user memberships\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs during the count retrieving\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    List<UserMembership> getUserMembershipsByRole(long roleId, int startIndex, int maxResults);\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/NoSessionRequired.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * Indicates that a Bonita API method does not need to have a valid session to be called.\n * No session required means that there is no transaction.\n *\n * @author Emmanuel Duchastenier\n */\n@Target(ElementType.TYPE)\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface NoSessionRequired {\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/OrganizationAPI.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.business.application.Application;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.identity.ImportPolicy;\nimport org.bonitasoft.engine.identity.OrganizationExportException;\nimport org.bonitasoft.engine.identity.OrganizationImportException;\nimport org.bonitasoft.engine.identity.UserUpdater;\n\n/**\n * Manages the Organization, that is the users, groups, roles, memberships, through import / export methods.\n *\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n * @author Emmanuel Duchastenier\n */\npublic interface OrganizationAPI {\n\n    /**\n     * Deletes the organization.\n     * <p>\n     * It deletes all user memberships, roles, groups, users and custom user info.\n     * </p>\n     * <p>Use this method with caution: some artifacts like {@link Application}s and {@link DesignProcessDefinition}s\n     * may present display problems in the Bonita\n     * BPM Portal if the referenced user was deleted. Note that you can disable a user instead of deleting it. To do so,\n     * use the method\n     * {@link IdentityAPI#updateUser(long, UserUpdater)} to set the attribute 'enabled' to false</p>.\n     *\n     * @throws DeletionException\n     *         If an exception occurs during the organization deletion\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0.0\n     * @see IdentityAPI#updateUser(long, UserUpdater)\n     * @see Application\n     * @see DesignProcessDefinition\n     */\n    void deleteOrganization() throws DeletionException;\n\n    /**\n     * Imports the organization using the {@link ImportPolicy#MERGE_DUPLICATES} policy.\n     * <p>\n     * An organization is composed by users, roles, groups and user memberships.\n     * </p>\n     *\n     * @param organizationContent\n     *        the XML content of the organization\n     * @throws OrganizationImportException\n     *         If an exception occurs during the organization import\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0.0\n     */\n    void importOrganization(String organizationContent) throws OrganizationImportException;\n\n    /**\n     * Imports the organization. Returns the error/info messages about what occurred during the process.\n     * Functionally this method informs the end-user of particular non-critical events that occurred during the import.\n     * <p>\n     * Ex: If a group in the organization contains an illegal character in their name, the method will import all the\n     * other\n     * groups in the organization, and return a List with one String : \"The group name (...) contains the illegal\n     * character (...). The group has not been imported\"\n     * </p>\n     * <p>\n     * An organization is composed by users, roles, groups and user memberships.\n     * </p>\n     *\n     * @return List of warning messages\n     * @param organizationContent\n     *        the XML content of the organization\n     * @param policy\n     *        the import policy\n     * @throws OrganizationImportException\n     *         If an exception occurs during the organization import\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     */\n    List<String> importOrganizationWithWarnings(String organizationContent, ImportPolicy policy)\n            throws OrganizationImportException;\n\n    /**\n     * Exports the organization.\n     * <p>\n     * An organization is composed by users, roles, groups and user memberships.\n     * </p>\n     *\n     * @return the organization contented in an XML format\n     * @throws OrganizationExportException\n     *         If an exception occurs during the organization export\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0.0\n     */\n    String exportOrganization() throws OrganizationExportException;\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/PageAPI.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Properties;\n\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.ExecutionException;\nimport org.bonitasoft.engine.exception.InvalidPageTokenException;\nimport org.bonitasoft.engine.exception.InvalidPageZipContentException;\nimport org.bonitasoft.engine.exception.InvalidPageZipInconsistentException;\nimport org.bonitasoft.engine.exception.InvalidPageZipMissingAPropertyException;\nimport org.bonitasoft.engine.exception.InvalidPageZipMissingIndexException;\nimport org.bonitasoft.engine.exception.InvalidPageZipMissingPropertiesException;\nimport org.bonitasoft.engine.exception.NotFoundException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.exception.UnauthorizedAccessException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.exception.UpdatingWithInvalidPageTokenException;\nimport org.bonitasoft.engine.exception.UpdatingWithInvalidPageZipContentException;\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.engine.page.PageCreator;\nimport org.bonitasoft.engine.page.PageNotFoundException;\nimport org.bonitasoft.engine.page.PageURL;\nimport org.bonitasoft.engine.page.PageUpdater;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\n\n/**\n * This API gives access to all page features. Page is a way to add pages on portal.\n * <p>\n * Also allows to manipulate <code>Page</code>s, through creation, deletion, search.\n * </p>\n *\n * @author Laurent Leseigneur\n * @see org.bonitasoft.engine.page.Page\n */\npublic interface PageAPI {\n\n    /**\n     * Retrieves a page from its ID.\n     *\n     * @param pageId\n     *        the Identifier of the page to retrieve\n     * @return the found page\n     * @throws org.bonitasoft.engine.page.PageNotFoundException\n     *         if no page can be found with the provided ID.\n     */\n    Page getPage(final long pageId) throws PageNotFoundException;\n\n    /**\n     * Retrieves a page from its name.\n     *\n     * @param name\n     *        the name of the page to retrieve\n     * @return the found page\n     * @throws PageNotFoundException\n     *         if no page can be found with the provided page or is assigned with a process definition.\n     */\n    Page getPageByName(final String name) throws PageNotFoundException;\n\n    /**\n     * Retrieves a page from its name and processDefinitionId.\n     *\n     * @param name\n     *        the name of the page to retrieve\n     * @param processDefinitionId\n     *        the process definition ID associated to the page\n     * @return the found page\n     * @throws PageNotFoundException\n     *         if no page can be found with the provided name and process definition ID.\n     */\n    Page getPageByNameAndProcessDefinitionId(final String name, long processDefinitionId) throws PageNotFoundException;\n\n    /**\n     * Retrieves the binary content of a page.\n     *\n     * @param pageId\n     *        the ID of the page to extract the content for.\n     * @return\n     *         the binary content of the page.\n     * @throws PageNotFoundException\n     *         if no page can be found with the provided ID.\n     */\n    byte[] getPageContent(final long pageId) throws PageNotFoundException;\n\n    /**\n     * Searches for pages with specific search criteria.\n     *\n     * @param searchOptions\n     *        the search options for the search. See {@link org.bonitasoft.engine.search.SearchOptions} for search\n     *        option details.\n     * @return the <code>SearchResult</code> containing\n     * @throws org.bonitasoft.engine.exception.SearchException\n     *         if a problem occurs during the search.\n     */\n    SearchResult<Page> searchPages(final SearchOptions searchOptions) throws SearchException;\n\n    /**\n     * Creates a custom page.\n     * Note that if called from an operation in a task, the author of the page will always be the user System.\n     *\n     * @param pageCreator\n     *        the creator object to instantiate the new page.\n     * @param content\n     *        the binary content of the page.\n     * @return the newly created page.\n     * @throws org.bonitasoft.engine.exception.AlreadyExistsException\n     *         if a page with this name already exists.\n     * @throws org.bonitasoft.engine.exception.CreationException\n     *         if an error occurs during the creation.\n     * @deprecated as of 9.0.0. Page content should be created at startup.\n     */\n    @Deprecated(since = \"9.0.0\")\n    Page createPage(final PageCreator pageCreator, final byte[] content)\n            throws AlreadyExistsException, CreationException, InvalidPageTokenException,\n            InvalidPageZipContentException;\n\n    /**\n     * Updates a custom page.\n     *\n     * @param pageId\n     *        the Identifier of the page to update\n     * @param pageUpdater\n     *        the creator object to instantiate the new page.\n     * @return the newly created page.\n     * @throws org.bonitasoft.engine.exception.UpdateException\n     *         if an error occurs during the update.\n     * @throws org.bonitasoft.engine.exception.AlreadyExistsException\n     *         if a page with this name already exists.\n     * @deprecated as of 9.0.0. Page content should be updated at startup.\n     */\n    @Deprecated(since = \"9.0.0\")\n    Page updatePage(final long pageId, final PageUpdater pageUpdater)\n            throws UpdateException, AlreadyExistsException, UpdatingWithInvalidPageTokenException,\n            UpdatingWithInvalidPageZipContentException;\n\n    /**\n     * Updates a custom page content.\n     * it read the page.properties inside to update the page properties\n     *\n     * @param pageId\n     *        the Identifier of the page to update\n     * @param content\n     *        the binary content of the page.\n     * @throws org.bonitasoft.engine.exception.UpdateException\n     *         if an error occurs during the update.\n     * @deprecated as of 9.0.0. Page content should be updated at startup.\n     */\n    @Deprecated(since = \"9.0.0\")\n    void updatePageContent(final long pageId, final byte[] content)\n            throws UpdateException, UpdatingWithInvalidPageTokenException,\n            UpdatingWithInvalidPageZipContentException;\n\n    /**\n     * Deletes a page identified by its ID.\n     *\n     * @param pageId\n     *        the page identifier to delete.\n     * @throws org.bonitasoft.engine.exception.DeletionException\n     *         if a problem occurs during deletion.\n     */\n    void deletePage(final long pageId) throws DeletionException;\n\n    /**\n     * Deletes a list of pages, given by their IDs.\n     *\n     * @param pageIds\n     *        a list of page identifiers to delete.\n     * @throws org.bonitasoft.engine.exception.DeletionException\n     *         if a problem occurs during deletion.\n     */\n    void deletePages(final List<Long> pageIds) throws DeletionException;\n\n    /**\n     * create a page using the given content\n     * the content must contain a page.properties file that contains information on the page:\n     * name, displayName and description. Be aware that this method does not update your web permission-mappings.\n     * It means that rest api extensions created with this method will not be accessible from the portal.\n     * To avoid the problem, either use the <a href=\"https://documentation.ofelia.com/?page=portal-api#toc0\">create\n     * page rest api</a> or upload the extensions directly from Bonita Portal.\n     * Note that if called from an operation in a task, the author of the page will always be the user System.\n     *\n     * @param contentName\n     *        name of the zip file containing the page\n     * @param content\n     *        content of the zip file containing the page\n     * @return\n     *         the created page\n     * @throws org.bonitasoft.engine.exception.AlreadyExistsException\n     *         if a page with the same name already exists\n     * @throws org.bonitasoft.engine.exception.CreationException\n     * @since 6.3.1\n     * @deprecated as of 9.0.0. Page content should be created at startup.\n     */\n    @Deprecated(since = \"9.0.0\")\n    Page createPage(String contentName, byte[] content)\n            throws AlreadyExistsException, CreationException, InvalidPageTokenException,\n            InvalidPageZipContentException;\n\n    /**\n     * Read the content of the page zip file check it is consistent and return it's properties\n     *\n     * @param content\n     *        content of the zip file containing the page\n     * @return\n     *         the properties of the page\n     * @since 6.4.0\n     */\n    Properties getPageProperties(byte[] content, boolean checkIfItAlreadyExists) throws InvalidPageTokenException,\n            AlreadyExistsException, InvalidPageZipMissingPropertiesException, InvalidPageZipMissingIndexException,\n            InvalidPageZipInconsistentException,\n            InvalidPageZipMissingAPropertyException;\n\n    /**\n     * Resolves a Page URL from a specific key.\n     *\n     * @param key the key of the page to resolve.\n     * @return the <code>PageURL</code> containing the pageId or the complete\n     * @throws NotFoundException if the key does not match anything.\n     * @see PageURL the structured PageURL that points to the Page or URL\n     */\n    PageURL resolvePageOrURL(String key, Map<String, Serializable> context, boolean executeAuthorizationRules)\n            throws NotFoundException, UnauthorizedAccessException, ExecutionException;\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/PermissionAPI.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\nimport java.util.Set;\n\nimport org.bonitasoft.engine.api.permission.APICallContext;\nimport org.bonitasoft.engine.exception.ExecutionException;\n\n/**\n * Handle permissions of users\n *\n * @author Baptiste Mesta\n */\npublic interface PermissionAPI {\n\n    /**\n     * Checks if the REST API request defined in the {@link APICallContext} is authorized for the logged in user\n     *\n     * @param apiCallContext\n     *        contains all the attributes of the request\n     * @return true or false depending if the user it authorized to make the call or not\n     * @throws ExecutionException if there was an error while executing the authorization checks\n     */\n    boolean isAuthorized(APICallContext apiCallContext) throws ExecutionException;\n\n    /**\n     * Returns the REST permissions required to access a REST resource (the expected format for the resource key is\n     * <HTTP method>|<API name>/<resource name> e.g. GET|identity/user)\n     *\n     * @param resourceKey\n     *        the resource identifier. The expected format is <HTTP method>|<API name>/<resource name> (e.g.\n     *        GET|identity/user)\n     * @return a Set of permissions, as Strings. e.g. [\"organization_visualization\"]\n     */\n    Set<String> getResourcePermissions(String resourceKey);\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/PlatformAPI.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.platform.*;\n\n/**\n * <b>Manage the platform.</b>\n * <p>\n * The platform is the base on which runs the engine.<br>\n * It mainly handles the creation of tables in database and also allow to start/stop a Node which is the current Virtual\n * machine on which runs the engine. There\n * is only one platform for a running Bonita Engine.\n * </p>\n *\n * @author Elias Ricken de Medeiros\n * @author Lu Kai\n * @author Zhang Bole\n * @author Matthieu Chaffotte\n * @author Emmanuel Duchastenier\n */\npublic interface PlatformAPI {\n\n    /**\n     * <b>Initialize the platform.</b>\n     * The running environment of Bonita Engine is initialized and marked as activated.<br>\n     * Business elements linked to the execution are initialized, after this step the technical user will be able to\n     * connect to the engine and to import the\n     *\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         occurs if the API session is invalid, e.g session has expired.\n     * @throws CreationException\n     *         occurs when an exception is thrown during platform creation\n     * @deprecated Not useful anymore, initialization is done by the setup tool\n     */\n    @Deprecated(forRemoval = true, since = \"8.0.0\")\n    void initializePlatform() throws CreationException;\n\n    /**\n     * <b>Starts the node.</b>\n     * <p>\n     * The node is the currently Java Virtual Machine on which Bonita Engine is running\n     * <p>\n     * Starting the node make the Scheduler service to start and restart elements that were not finished by the Work\n     * service on the previous shutdown.\n     *\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         occurs if API Session is invalid, e.g session has expired.\n     * @throws StartNodeException\n     *         occurs when an exception is thrown during the activation of the node\n     */\n    void startNode() throws StartNodeException;\n\n    /**\n     * <b>Stops the node.</b>\n     * <p>\n     * The node is the currently Java Virtual Machine on which Bonita Engine is running\n     * <p>\n     * Stopping the node make the Scheduler service to stop.\n     *\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         occurs if API Session is invalid, e.g session has expired.\n     * @throws StopNodeException\n     *         occurs when an exception is thrown during the stop of the node\n     */\n    void stopNode() throws StopNodeException;\n\n    /**\n     * Get the platform.\n     *\n     * @return the Platform object\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @throws PlatformNotFoundException\n     *         occurs when the identifier does not refer to an existing platform\n     */\n    Platform getPlatform() throws PlatformNotFoundException;\n\n    /**\n     * Check if the platform created or not.\n     *\n     * @return true if the platform exists\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @throws PlatformNotFoundException\n     *         occurs when the identifier does not refer to an existing platform\n     */\n    boolean isPlatformCreated() throws PlatformNotFoundException;\n\n    /**\n     * Get the state of the platform of the current node\n     *\n     * @return {@link PlatformState#STARTED} or {@link PlatformState#STOPPED} depending on the scheduler state\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @throws PlatformNotFoundException\n     *         occurs when the identifier does not refer to an existing platform\n     */\n    PlatformState getPlatformState() throws PlatformNotFoundException;\n\n    /**\n     * Is the current node started?\n     *\n     * @return true if the node is started, false if not started or if its state cannot be determined.\n     * @since 6.1\n     */\n    boolean isNodeStarted();\n\n    /**\n     * Reschedules triggers which are in error state.\n     *\n     * @throws UpdateException\n     *         If an exception occurs during the scheduling\n     * @since 6.2\n     */\n    void rescheduleErroneousTriggers() throws UpdateException;\n\n    /**\n     * INTERNAL USE ONLY\n     * get client configuration files of the platform\n     *\n     * @return the client platform configuration files as a map containing file name and file content\n     * @since 7.3\n     */\n    @Internal\n    Map<String, byte[]> getClientPlatformConfigurations();\n\n    /**\n     * INTERNAL USE ONLY\n     * Get client configuration files\n     *\n     * @return the client tenants configuration files as a map of \"file name\" and \"file content\"\n     * @since 7.3\n     */\n    @Internal\n    Map<String, byte[]> getClientTenantConfigurations();\n\n    /**\n     * INTERNAL USE ONLY\n     * get a specific client configuration file\n     *\n     * @return file content\n     * @since 7.3\n     */\n    @Internal\n    byte[] getClientTenantConfiguration(String file);\n\n    /**\n     * INTERNAL USE ONLY\n     * update a single client configuration file\n     *\n     * @param file file name to update\n     * @param content the new content of the file\n     * @since 7.3\n     */\n    @Internal\n    void updateClientTenantConfigurationFile(String file, byte[] content) throws UpdateException;\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/PlatformCommandAPI.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.command.CommandCriterion;\nimport org.bonitasoft.engine.command.CommandDescriptor;\nimport org.bonitasoft.engine.command.CommandExecutionException;\nimport org.bonitasoft.engine.command.CommandNotFoundException;\nimport org.bonitasoft.engine.command.CommandParameterizationException;\nimport org.bonitasoft.engine.command.CommandUpdater;\nimport org.bonitasoft.engine.command.DependencyNotFoundException;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.UpdateException;\n\n/**\n * Manipulates a platform command. A command can be registered, unregistered and executed with parameters.<br>\n * These commands are executed in a platform scope, see {@link CommandAPI} for an explanation of how to deploy, execute,\n * ... a command. The only\n * difference between the {@link CommandAPI} and the {@link PlatformCommandAPI} is that a platform command must extend\n * {@code org.bonitasoft.engine.command.RuntimeCommand}.\n *\n * @author Matthieu Chaffotte\n * @author Emmanuel Duchastenier\n * @see CommandDescriptor\n */\npublic interface PlatformCommandAPI {\n\n    /**\n     * Adds a dependency.\n     *\n     * @param name\n     *        the dependency name.\n     * @param jar\n     *        the JAR content\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         occurs when the session is not valid\n     * @throws AlreadyExistsException\n     *         occurs when the dependency name is already used for another dependency\n     * @throws CreationException\n     *         if a problem occurs when creating the dependency\n     */\n    void addDependency(String name, byte[] jar) throws AlreadyExistsException, CreationException;\n\n    /**\n     * Removes the dependency.\n     *\n     * @param name\n     *        the dependency name.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         if the current platform session is not valid\n     * @throws DependencyNotFoundException\n     *         if no dependency can be found with the specified name\n     * @throws DeletionException\n     *         if a problem occurs when deleting the dependency\n     */\n    void removeDependency(String name) throws DependencyNotFoundException, DeletionException;\n\n    /**\n     * Adds a command and its descriptor.\n     *\n     * @param name\n     *        the command name\n     * @param description\n     *        the command description\n     * @param implementation\n     *        the implementation class which will be used when executing the command\n     * @return the descriptor of the command\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         occurs when the session is not valid\n     * @throws AlreadyExistsException\n     *         occurs when the command name is already used for another command\n     * @throws CreationException\n     *         if a problem occurs when registering the command\n     */\n    CommandDescriptor register(String name, String description, String implementation)\n            throws AlreadyExistsException, CreationException;\n\n    /**\n     * Executes a command according to its name and the list of parameters.\n     *\n     * @param name\n     *        the command name\n     * @param parameters\n     *        the parameters (specific to the command to execute)\n     * @return the result of the command execution\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         occurs when the session is not valid\n     * @throws CommandNotFoundException\n     *         occurs when the name does not refer to any existing command\n     * @throws CommandParameterizationException\n     *         when command parameters are not correct\n     * @throws CommandExecutionException\n     *         occurs when an exception is thrown during command execution\n     */\n    Serializable execute(String name, Map<String, Serializable> parameters)\n            throws CommandNotFoundException, CommandParameterizationException,\n            CommandExecutionException;\n\n    /**\n     * Deletes a command and its descriptor.\n     *\n     * @param name\n     *        the command name\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         occurs when the session is not valid\n     * @throws CommandNotFoundException\n     *         occurs when the name does not refer to an existing command\n     * @throws DeletionException\n     *         occurs when an exception is thrown during command deletion\n     */\n    void unregister(String name) throws CommandNotFoundException, DeletionException;\n\n    /**\n     * Returns the command descriptor corresponding to the given command name.\n     *\n     * @param commandName\n     *        the name of the command\n     * @return the descriptor of the command\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         occurs when the session is not valid\n     * @throws CommandNotFoundException\n     *         occurs when the command name does not refer to an existing command.\n     * @since 6.2.1\n     */\n    CommandDescriptor getCommand(String commandName) throws CommandNotFoundException;\n\n    /**\n     * Returns the paginated list of command descriptors according to the sort criterion.\n     *\n     * @param startIndex\n     *        the start index\n     * @param maxResults\n     *        the number of {@link CommandDescriptor} to retrieve\n     * @param sort\n     *        the sorting criterion\n     * @return the paginated list of command descriptors\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         occurs when the session is not valid\n     */\n    List<CommandDescriptor> getCommands(int startIndex, int maxResults, CommandCriterion sort);\n\n    /**\n     * Updates a command according to the update descriptor.\n     *\n     * @param name\n     *        the command name\n     * @param updater\n     *        the update descriptor\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         if the session is not valid\n     * @throws CommandNotFoundException\n     *         occurs when the name does not refer to any existing command\n     * @throws UpdateException\n     *         occurs when an exception is thrown during command update\n     */\n    void update(String name, CommandUpdater updater) throws CommandNotFoundException, UpdateException;\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/PlatformLoginAPI.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\nimport org.bonitasoft.engine.platform.InvalidPlatformCredentialsException;\nimport org.bonitasoft.engine.platform.PlatformLoginException;\nimport org.bonitasoft.engine.platform.PlatformLogoutException;\nimport org.bonitasoft.engine.session.PlatformSession;\nimport org.bonitasoft.engine.session.SessionNotFoundException;\n\n/**\n * <b>Manage the login on the platform.</b>\n * <p>\n * Using this API you can obtain a {@link PlatformSession} that can be used to retreive a PlatformAPIAccessor.\n * <p>\n * <code>PlatformSession</code> gives access to platform APIs only:\n * <ul>\n * <li>{@link PlatformAPI}</li>\n * <li>{@link PlatformCommandAPI}</li>\n * </ul>\n *\n * @author Matthieu Chaffotte\n * @author Baptiste Mesta\n * @author Emmanuel Duchastenier\n */\n@NoSessionRequired\npublic interface PlatformLoginAPI {\n\n    /**\n     * Login with username and password of the platform administrator defined in {@code bonita-platform.properties}\n     *\n     * @param userName\n     *        the platform administrator name\n     * @param password\n     *        the platform administrator password\n     * @return\n     *         the session created for you, can be used to retrieve platform APIs\n     * @throws PlatformLoginException\n     *         occurs when an exception is thrown during login the platform\n     * @throws InvalidPlatformCredentialsException\n     *         occurs when thr username or password is not valid\n     */\n    PlatformSession login(String userName, String password)\n            throws PlatformLoginException, InvalidPlatformCredentialsException;\n\n    /**\n     * Logout from a platform.\n     *\n     * @param session\n     *        the platform session to logout from.\n     * @throws PlatformLogoutException\n     *         occurs when an exception is thrown during logout the platform\n     * @throws SessionNotFoundException\n     *         if the session is not found on the server side. This may occurs when the session has expired.\n     */\n    void logout(PlatformSession session) throws PlatformLogoutException, SessionNotFoundException;\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/ProcessAPI.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\n/**\n * Contains all methods that handle processes.\n * Using this API you can handle:\n * <ul>\n * <li>{@link ProcessRuntimeAPI Execution of processes}: start process, retrieve tasks, execute tasks, retrieve\n * data...</li>\n * <li> {@link ProcessManagementAPI Management of processes}: Deploy/Undeploy processes, enable/disable process...</li>\n * <li> {@link DocumentAPI Documents}: create, list, retrieve documents</li>\n * </ul>\n *\n * @see ProcessRuntimeAPI\n * @see ProcessManagementAPI\n * @see DocumentAPI\n * @author Baptiste Mesta\n */\npublic interface ProcessAPI extends ProcessManagementAPI, ProcessRuntimeAPI, DocumentAPI {\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/ProcessManagementAPI.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\nimport java.io.FileNotFoundException;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.bpm.actor.ActorCriterion;\nimport org.bonitasoft.engine.bpm.actor.ActorInstance;\nimport org.bonitasoft.engine.bpm.actor.ActorMappingExportException;\nimport org.bonitasoft.engine.bpm.actor.ActorMappingImportException;\nimport org.bonitasoft.engine.bpm.actor.ActorMember;\nimport org.bonitasoft.engine.bpm.actor.ActorNotFoundException;\nimport org.bonitasoft.engine.bpm.actor.ActorUpdater;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.category.Category;\nimport org.bonitasoft.engine.bpm.category.CategoryCriterion;\nimport org.bonitasoft.engine.bpm.category.CategoryNotFoundException;\nimport org.bonitasoft.engine.bpm.category.CategoryUpdater;\nimport org.bonitasoft.engine.bpm.connector.ConnectorCriterion;\nimport org.bonitasoft.engine.bpm.connector.ConnectorImplementationDescriptor;\nimport org.bonitasoft.engine.bpm.connector.ConnectorNotFoundException;\nimport org.bonitasoft.engine.bpm.data.DataDefinition;\nimport org.bonitasoft.engine.bpm.flownode.ActivityDefinitionNotFoundException;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\nimport org.bonitasoft.engine.bpm.parameter.ParameterCriterion;\nimport org.bonitasoft.engine.bpm.parameter.ParameterInstance;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException;\nimport org.bonitasoft.engine.bpm.process.Problem;\nimport org.bonitasoft.engine.bpm.process.ProcessActivationException;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ProcessDeployException;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoSearchDescriptor;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoUpdater;\nimport org.bonitasoft.engine.bpm.process.ProcessEnablementException;\nimport org.bonitasoft.engine.bpm.process.ProcessExportException;\nimport org.bonitasoft.engine.bpm.process.ProcessResourceNotFoundException;\nimport org.bonitasoft.engine.bpm.process.V6FormDeployException;\nimport org.bonitasoft.engine.bpm.supervisor.ProcessSupervisor;\nimport org.bonitasoft.engine.bpm.supervisor.ProcessSupervisorSearchDescriptor;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.FormMappingNotFoundException;\nimport org.bonitasoft.engine.exception.NotFoundException;\nimport org.bonitasoft.engine.exception.RetrieveException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.form.FormMapping;\nimport org.bonitasoft.engine.form.FormMappingSearchDescriptor;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\n\n/**\n * This API deals with definition objects such as {@link ProcessDefinition}, {@link ProcessDeploymentInfo},\n * {@link Category}, ...\n * It enables interaction with the lifecycle of the process definition.\n *\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Yanyan Liu\n * @author Hongwen Zang\n * @author Zhang Bole\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n * @author Arthur Freycon\n * @author Emmanuel Duchastenier\n * @version 6.3.5\n * @since 6.0.0\n */\npublic interface ProcessManagementAPI {\n\n    /**\n     * Deploys a {@link BusinessArchive} which contains a {@link DesignProcessDefinition} and its dependencies.\n     *\n     * @param businessArchive\n     *        The archive to deploy.\n     * @return The process definition.\n     * @throws AlreadyExistsException\n     *         If a process with same name and version is already deployed.\n     * @throws ProcessDeployException\n     *         If an exception occurs when deploying the archive.\n     * @throws V6FormDeployException\n     *         If the deployed process contains v6 forms, not supported anymore.\n     * @see BusinessArchive\n     * @since 6.0\n     */\n    ProcessDefinition deploy(BusinessArchive businessArchive)\n            throws AlreadyExistsException, ProcessDeployException, V6FormDeployException;\n\n    /**\n     * Deploys a simple {@link DesignProcessDefinition} (without any dependencies).\n     *\n     * @param designProcessDefinition\n     *        The description of a process definition.\n     * @return The process definition corresponding of the description.\n     * @throws AlreadyExistsException\n     *         If a process with same name and version is already deployed.\n     * @throws ProcessDeployException\n     *         If an exception occurs when deploying the process.\n     * @see #deploy(BusinessArchive)\n     * @since 6.0\n     */\n    ProcessDefinition deploy(DesignProcessDefinition designProcessDefinition)\n            throws AlreadyExistsException, ProcessDeployException;\n\n    /**\n     * Enables the process definition.\n     *\n     * @param processDefinitionId\n     *        The process definition identifier.\n     * @throws ProcessDefinitionNotFoundException\n     *         If the identifier does not refer to an existing process definition.\n     * @throws ProcessEnablementException\n     *         If an exception occurs during the process enablement.\n     * @since 6.0\n     */\n    void enableProcess(long processDefinitionId) throws ProcessDefinitionNotFoundException, ProcessEnablementException;\n\n    /**\n     * Disables the process definition by giving its identifier. A process can only be disabled if it is enabled.\n     *\n     * @param processDefinitionId\n     *        The process definition identifier.\n     * @throws ProcessDefinitionNotFoundException\n     *         If the identifier does not refer to an existing process definition.\n     * @throws ProcessActivationException\n     *         If an exception occurs during the process disablement.\n     * @since 6.0\n     */\n    void disableProcess(long processDefinitionId) throws ProcessDefinitionNotFoundException, ProcessActivationException;\n\n    /**\n     * Returns the process definition by giving its identifier.\n     * If the identifier is null, a ProcessDefinitionNotFoundException is thrown.\n     *\n     * @param processDefinitionId\n     *        The identifier of the process definition.\n     * @return The process definition referenced by the identifier.\n     * @throws ProcessDefinitionNotFoundException\n     *         If the identifier does not refer to an existing process definition.\n     * @throws RetrieveException\n     *         If an exception occurs when getting the process definition.\n     * @since 6.0\n     */\n    ProcessDefinition getProcessDefinition(long processDefinitionId) throws ProcessDefinitionNotFoundException;\n\n    /**\n     * Deletes a process definition by giving its identifier. A process can only be deleted if it is disabled and it has\n     * no more existing process instances.\n     *\n     * @param processDefinitionId\n     *        The identifier of the process definition.\n     * @throws DeletionException\n     *         If an exception occurs during process deletion.\n     * @since 6.1\n     */\n    void deleteProcessDefinition(long processDefinitionId) throws DeletionException;\n\n    /**\n     * Deletes process definitions by giving their identifiers. If any specified identifier does not refer to a real\n     * process definition, or if an exception\n     * occurs, no process definition is deleted. All instances of given processes must be deleted prior to calling this\n     * operation.\n     *\n     * @param processDefinitionIds\n     *        The list of identifiers of process definitions.\n     * @throws DeletionException\n     *         If an exception occurs during process deletion.\n     * @see #deleteProcessDefinition(long)\n     * @since 6.1\n     */\n    void deleteProcessDefinitions(List<Long> processDefinitionIds) throws DeletionException;\n\n    /**\n     * Deploys, enables and returns a process.\n     *\n     * @param designProcessDefinition\n     *        The description of a process definition.\n     * @return ProcessDefinition the process definition corresponding of the description.\n     * @throws AlreadyExistsException\n     *         If a process with same name and version was already deployed.\n     * @throws ProcessEnablementException\n     *         If a process cannot be enabled.\n     * @throws InvalidProcessDefinitionException\n     *         If the designProcessDefinition is invalid.\n     * @throws ProcessDeployException\n     *         If an exception occurs when deploying the process.\n     * @see #deploy(DesignProcessDefinition)\n     * @see #enableProcess(long)\n     * @since 6.0\n     */\n    ProcessDefinition deployAndEnableProcess(DesignProcessDefinition designProcessDefinition)\n            throws ProcessDeployException, ProcessEnablementException,\n            AlreadyExistsException, InvalidProcessDefinitionException;\n\n    /**\n     * Deploys and enables a process by giving a {@link BusinessArchive}.\n     *\n     * @param businessArchive\n     *        The archive ready to deploy.\n     * @return ProcessDefinition Process definition by given a business archive.\n     * @throws ProcessDeployException\n     *         If an exception occurs when deploying the archive.\n     * @throws AlreadyExistsException\n     *         If a process with same name and same version already exists.\n     * @throws ProcessEnablementException\n     *         If a process cannot be enabled.\n     */\n    ProcessDefinition deployAndEnableProcess(BusinessArchive businessArchive)\n            throws ProcessDeployException, ProcessEnablementException, AlreadyExistsException;\n\n    /**\n     * Returns a list of problems if the process is configured incorrectly or the configuration is incomplete.\n     *\n     * @param processDefinitionId\n     *        The process definition identifier.\n     * @return a list of problems or an empty list.\n     * @throws ProcessDefinitionNotFoundException\n     *         If the identifier does not refer to an existing process definition.\n     * @throws RetrieveException\n     *         If an exception occurs when getting the problems of the process definition.\n     * @since 6.0\n     */\n    List<Problem> getProcessResolutionProblems(long processDefinitionId) throws ProcessDefinitionNotFoundException;\n\n    /**\n     * Gets the current number of process definitions in all states.\n     *\n     * @return The number of process definitions.\n     * @throws RetrieveException\n     *         If an exception occurs when getting the number of the process definitions.\n     * @since 6.0\n     */\n    long getNumberOfProcessDeploymentInfos();\n\n    /**\n     * Gets the deployment information of a process definition by giving the process definition identifier.\n     *\n     * @param processDefinitionId\n     *        The process definition identifier.\n     * @return The deployment information of the process definition.\n     * @throws ProcessDefinitionNotFoundException\n     *         If the identifier does not refer to an existing process definition.\n     * @throws RetrieveException\n     *         If an exception occurs when getting the process deployment information.\n     * @since 6.0\n     */\n    ProcessDeploymentInfo getProcessDeploymentInfo(long processDefinitionId) throws ProcessDefinitionNotFoundException;\n\n    /**\n     * Updates the process deployment information for a specified process.\n     *\n     * @param processDefinitionId\n     *        The process definition identifier.\n     * @param processDeploymentInfoUpdater\n     *        The description which describe how to update the process deployment information.\n     * @throws ProcessDefinitionNotFoundException\n     *         If the identifier does not refer to an existing process definition.\n     * @throws UpdateException\n     *         If an exception occurs when updating the process deployment information.\n     * @since 6.0\n     * @deprecated as of 9.0.0, Process should be updated at startup.\n     */\n    @Deprecated(since = \"9.0.0\")\n    void updateProcessDeploymentInfo(long processDefinitionId,\n            ProcessDeploymentInfoUpdater processDeploymentInfoUpdater)\n            throws ProcessDefinitionNotFoundException, UpdateException;\n\n    /**\n     * Returns a paged list of process deployment information for a number of processes.\n     *\n     * @param startIndex\n     *        The index of the first result (starting from 0).\n     * @param maxResults\n     *        The maximum number of process deployment information results per page.\n     * @param sortCriterion\n     *        The sorting criterion.\n     * @return The ordered list of process deployment informations.\n     * @throws RetrieveException\n     *         If an exception occurs when getting the process deployment informations.\n     * @since 6.0\n     */\n    List<ProcessDeploymentInfo> getProcessDeploymentInfos(int startIndex, int maxResults,\n            ProcessDeploymentInfoCriterion sortCriterion);\n\n    /**\n     * Returns the number of actors in a process definition.\n     *\n     * @param processDefinitionId\n     *        The process definition identifier.\n     * @return The number of actors in the process.\n     * @throws ProcessDefinitionNotFoundException\n     *         If the identifier does not refer to an existing process definition.\n     * @since 6.0\n     */\n    int getNumberOfActors(long processDefinitionId) throws ProcessDefinitionNotFoundException;\n\n    /**\n     * Returns the actor.\n     *\n     * @param actorId\n     *        The identifier of the actor.\n     * @return The actor.\n     * @throws ActorNotFoundException\n     *         If an identifier does not refer to an existing actor.\n     * @since 6.0\n     */\n    ActorInstance getActor(long actorId) throws ActorNotFoundException;\n\n    /**\n     * Returns a paged list of actors in a process.\n     *\n     * @param processDefinitionId\n     *        The process definition identifier.\n     * @param startIndex\n     *        The index of the first result (starting from 0).\n     * @param maxResults\n     *        The maximum number of actors per page.\n     * @param sort\n     *        The sorting criterion.\n     * @return The ordered list of actors.\n     * @since 6.0\n     */\n    List<ActorInstance> getActors(long processDefinitionId, int startIndex, int maxResults, ActorCriterion sort);\n\n    /**\n     * Returns a paged list of members of an actor.\n     * An actor member can be a user, a role, a group, or a membership. An actor member is created when a user, role,\n     * group, or membership is mapped to the\n     * actor.\n     * No ordering must be assumed on the list of results.\n     *\n     * @param actorId\n     *        The identifier of the actor.\n     * @param startIndex\n     *        The index of the first result (starting from 0).\n     * @param maxResults\n     *        The maximum number of actor members per page.\n     * @return The list of actor members.\n     * @since 6.0\n     */\n    List<ActorMember> getActorMembers(long actorId, int startIndex, int maxResults);\n\n    /**\n     * Counts the number of members mapped to the actor.\n     * An actor member can be a user, a role, a group, or a membership. An actor member is created when a user, role,\n     * group, or membership is mapped to the\n     * actor.\n     *\n     * @param actorId\n     *        The identifier of the actor.\n     * @return The number of actors members of the actor\n     * @since 6.0\n     */\n    long getNumberOfActorMembers(long actorId);\n\n    /**\n     * Counts the number of users mapped to the actor.\n     *\n     * @param actorId\n     *        The identifier of the actor.\n     * @return The number of users mapped to the actor.\n     * @since 6.0\n     */\n    long getNumberOfUsersOfActor(long actorId);\n\n    /**\n     * Counts the number of roles mapped to the actor.\n     *\n     * @param actorId\n     *        The identifier of the actor.\n     * @return The number of roles mapped to the actor.\n     * @since 6.0\n     */\n    long getNumberOfRolesOfActor(long actorId);\n\n    /**\n     * Counts the number of groups mapped to the actor.\n     *\n     * @param actorId\n     *        The identifier of the actor.\n     * @return The number of groups mapped to the actor.\n     * @since 6.0\n     */\n    long getNumberOfGroupsOfActor(long actorId);\n\n    /**\n     * Counts the number of memberships mapped to the actor.\n     *\n     * @param actorId\n     *        The identifier of the actor.\n     * @return The total number of user memberships mapped to an actor\n     * @since 6.0\n     */\n    long getNumberOfMembershipsOfActor(long actorId);\n\n    /**\n     * Updates the actor.\n     *\n     * @param actorId\n     *        The identifier of the actor.\n     * @param actorUpdater\n     *        The descriptor which contains the fields to update.\n     * @return The actor.\n     * @throws ActorNotFoundException\n     *         If an identifier does not refer to an existing actor.\n     * @throws UpdateException\n     *         If an exception occurs when updating the actor.\n     * @since 6.0\n     * @deprecated as of 9.0.0, Actor should be updated at startup.\n     */\n    @Deprecated(since = \"9.0.0\")\n    ActorInstance updateActor(long actorId, ActorUpdater actorUpdater) throws ActorNotFoundException, UpdateException;\n\n    /**\n     * Maps the user to the actor. The user will be mapped to the actor as an {@link ActorMember}.\n     *\n     * @param actorId\n     *        The identifier of the actor.\n     * @param userId\n     *        The identifier of the user.\n     * @return The couple actor/user as an actor member.\n     * @throws CreationException\n     *         If an exception occurs when creating the actor mapping.\n     * @throws AlreadyExistsException\n     *         If the association already exists.\n     * @see IdentityAPI#getUser(long)\n     * @since 6.0\n     */\n    ActorMember addUserToActor(long actorId, long userId) throws CreationException, AlreadyExistsException;\n\n    /**\n     * Maps a user to the actor of the process definition. The user will be mapped to the actor as an\n     * {@link ActorMember}.\n     *\n     * @param actorName\n     *        The name of the actor.\n     * @param processDefinition\n     *        The process definition.\n     * @param userId\n     *        The identifier of the user.\n     * @return The couple actor/user as an actor member.\n     * @throws ActorNotFoundException\n     *         If the name does not refer to an existing actor of the process definition.\n     * @throws CreationException\n     *         If an exception occurs when creating the actor mapping.\n     * @throws AlreadyExistsException\n     *         If the association already exists.\n     * @see IdentityAPI#getUser(long)\n     * @since 6.0\n     */\n    ActorMember addUserToActor(String actorName, ProcessDefinition processDefinition, long userId)\n            throws ActorNotFoundException, CreationException,\n            AlreadyExistsException;\n\n    /**\n     * Maps the group to the actor.\n     *\n     * @param actorId\n     *        The identifier of the actor.\n     * @param groupId\n     *        The identifier of the group.\n     * @return The couple actor/group as an actor member.\n     * @throws CreationException\n     *         If the exception occurs when creating the actor mapping.\n     * @throws AlreadyExistsException\n     *         If the association already exists.\n     * @see IdentityAPI#getGroup(long)\n     * @since 6.0\n     */\n    ActorMember addGroupToActor(long actorId, long groupId) throws CreationException, AlreadyExistsException;\n\n    /**\n     * Maps the group to the actor of the process definition.\n     *\n     * @param actorName\n     *        The name of the actor.\n     * @param groupId\n     *        The identifier of the group.\n     * @param processDefinition\n     *        The process definition.\n     * @return The couple actor/group as an actor member.\n     * @throws ActorNotFoundException\n     *         If the name does not refer to an existing actor of the process definition.\n     * @throws CreationException\n     *         If an exception occurs when creating the actor mapping.\n     * @throws AlreadyExistsException\n     *         If the association already exists.\n     * @since 6.0\n     */\n    ActorMember addGroupToActor(String actorName, long groupId, ProcessDefinition processDefinition)\n            throws ActorNotFoundException, CreationException,\n            AlreadyExistsException;\n\n    /**\n     * Maps the role to the actor.\n     *\n     * @param actorId\n     *        The identifier of the actor.\n     * @param roleId\n     *        The identifier of the role.\n     * @return The couple actor/role as an actor member.\n     * @throws CreationException\n     *         If an exception occurs when creating the actor mapping.\n     * @since 6.0\n     */\n    ActorMember addRoleToActor(long actorId, long roleId) throws CreationException;\n\n    /**\n     * Maps the role to the actor of the process definition.\n     *\n     * @param actorName\n     *        The name of the actor.\n     * @param processDefinition\n     *        The process definition.\n     * @param roleId\n     *        The identifier of the role.\n     * @return The couple actor/role as an actor member.\n     * @throws ActorNotFoundException\n     *         If the name does not refer to an existing actor of the process definition.\n     * @throws CreationException\n     *         If an exception occurs when creating the actor mapping.\n     * @since 6.0\n     */\n    ActorMember addRoleToActor(String actorName, ProcessDefinition processDefinition, long roleId)\n            throws ActorNotFoundException, CreationException;\n\n    /**\n     * Maps the role and the group to the actor.\n     *\n     * @param actorId\n     *        The identifier of the actor.\n     * @param roleId\n     *        The identifier of the role.\n     * @param groupId\n     *        The identifier of the group.\n     * @return The tuple actor/role/group as an actor member.\n     * @throws CreationException\n     *         If an exception occurs when creating the actor mapping.\n     * @since 6.0\n     */\n    ActorMember addRoleAndGroupToActor(long actorId, long roleId, long groupId) throws CreationException;\n\n    /**\n     * Maps the role and the group to the actor of the process definition.\n     *\n     * @param actorName\n     *        The name of the actor.\n     * @param processDefinition\n     *        The process definition.\n     * @param roleId\n     *        The identifier of the role.\n     * @param groupId\n     *        The identifier of the role.\n     * @return The tuple actor/role/group as an actor member.\n     * @throws ActorNotFoundException\n     *         If the actor name does not refer to an existing actor in the process definition.\n     * @throws CreationException\n     *         If an exception occurs when creating the actor mapping.\n     * @since 6.0\n     */\n    ActorMember addRoleAndGroupToActor(String actorName, ProcessDefinition processDefinition, long roleId, long groupId)\n            throws ActorNotFoundException,\n            CreationException;\n\n    /**\n     * Deletes the actor member. This removes the mapping between the user, group, role, or membership and the actor.\n     * The user, group, role, or membership is not removed from the organization.\n     *\n     * @param actorMemberId\n     *        The identifier of the actor member\n     * @throws DeletionException\n     *         If an exception occurs when deleting the actor mapping.\n     * @since 6.0\n     */\n    void removeActorMember(long actorMemberId) throws DeletionException;\n\n    /**\n     * Imports into the process definition an actor mapping in XML format.\n     *\n     * @param processDefinitionId\n     *        The identifier of the process.\n     * @param xmlContent\n     *        The XML content of the mapping. If null, nothing is performed.\n     * @throws ActorMappingImportException\n     *         If an exception occurs when importing the actor mapping.\n     * @since 6.0\n     */\n    void importActorMapping(long processDefinitionId, String xmlContent) throws ActorMappingImportException;\n\n    /**\n     * Imports to the process definition, the actor mapping in XML format as a byte array.\n     *\n     * @param processDefinitionId\n     *        The identifier of the process.\n     * @param actorMappingXML\n     *        The XML content of the mapping as a byte array. If null, nothing is performed.\n     * @throws ActorMappingImportException\n     *         If an exception occurs when importing the actor mapping.\n     * @since 6.0\n     */\n    void importActorMapping(long processDefinitionId, byte[] actorMappingXML) throws ActorMappingImportException;\n\n    /**\n     * Exports the actor mapping of the process definition. The result contains the mapping in XML format.\n     *\n     * @param processDefinitionId\n     *        The identifier of the process.\n     * @return The XML content of the mapping.\n     * @throws ActorMappingExportException\n     *         If an exception occurs when exporting the actor mapping.\n     * @since 6.0\n     */\n    String exportActorMapping(long processDefinitionId) throws ActorMappingExportException;\n\n    /**\n     * Adds a category.\n     * A category is a string that can be assigned to processes, to make it easier\n     * to identify sets of related processes. For example, you could have\n     * a category called hr to identify all HR processes, or a category called\n     * finance to identify all processes used in the purchasing and accounts departments.\n     *\n     * @param name\n     *        The name of the category.\n     * @param description\n     *        The description of the category.\n     * @return The category.\n     * @throws AlreadyExistsException\n     *         If a category already exists with the given name.\n     * @throws CreationException\n     *         If an exception occurs when creating the category.\n     * @since 6.0\n     */\n    Category createCategory(String name, String description) throws AlreadyExistsException, CreationException;\n\n    /**\n     * Counts the number of categories.\n     *\n     * @return The number of categories.\n     * @since 6.0\n     */\n    long getNumberOfCategories();\n\n    /**\n     * Returns a paged list of categories.\n     *\n     * @param startIndex\n     *        The index of the first result (starting from 0).\n     * @param maxResults\n     *        The maximum number of categories.\n     * @param sortCriterion\n     *        The sorting criterion.\n     * @return The ordered list of categories.\n     * @since 6.0\n     */\n    List<Category> getCategories(int startIndex, int maxResults, CategoryCriterion sortCriterion);\n\n    /**\n     * Returns the category.\n     *\n     * @param categoryId\n     *        The identifier of the category.\n     * @return The category.\n     * @throws CategoryNotFoundException\n     *         If the identifier does not refer to an existing category.\n     * @since 6.0\n     */\n    Category getCategory(long categoryId) throws CategoryNotFoundException;\n\n    /**\n     * Associates the process definition with the category.\n     *\n     * @param categoryId\n     *        The identifier of the category.\n     * @param processDefinitionId\n     *        The identifier of the process definition.\n     * @throws AlreadyExistsException\n     *         If the association category/process already exists.\n     * @throws CreationException\n     *         If an exception occurs while adding the process to the category.\n     * @since 6.0\n     */\n    void addProcessDefinitionToCategory(long categoryId, long processDefinitionId)\n            throws AlreadyExistsException, CreationException;\n\n    /**\n     * Associates a list of process definitions with the category.\n     *\n     * @param categoryId\n     *        The identifier of the category.\n     * @param processDefinitionIds\n     *        The identifiers of the process definitions.\n     * @throws AlreadyExistsException\n     *         If an association category/process already exists.\n     * @throws CreationException\n     *         If an exception occurs while adding the process to the category.\n     * @since 6.0\n     */\n    void addProcessDefinitionsToCategory(long categoryId, List<Long> processDefinitionIds)\n            throws AlreadyExistsException, CreationException;\n\n    /**\n     * Counts the number of categories of the process definition, that is, the number of categories to which the process\n     * belongs.\n     *\n     * @param processDefinitionId\n     *        The identifier of the process definition.\n     * @return The number of categories of the process.\n     * @since 6.0\n     */\n    long getNumberOfCategories(long processDefinitionId);\n\n    /**\n     * Counts the number of process deployment information entries of the category.\n     * This is the number of deployed processes in the specified category.\n     *\n     * @param categoryId\n     *        The identifier of the category.\n     * @return The number of process deployment informations of the category.\n     * @since 6.0\n     */\n    long getNumberOfProcessDefinitionsOfCategory(long categoryId);\n\n    /**\n     * Returns the paged list of process deployment information items for the category.\n     *\n     * @param categoryId\n     *        The identifier of the category.\n     * @param startIndex\n     *        The index of the first result (starting from 0).\n     * @param maxResults\n     *        The maximum number of process deployment information.\n     * @param sort\n     *        The sorting criterion.\n     * @return The ordered list of process deployment informations of the category.\n     * @since 6.0\n     */\n    List<ProcessDeploymentInfo> getProcessDeploymentInfosOfCategory(long categoryId, int startIndex, int maxResults,\n            ProcessDeploymentInfoCriterion sort);\n\n    /**\n     * Get categories from process definition\n     *\n     * @param processDefinitionId\n     *        The identifier of the process definition.\n     * @param startIndex\n     *        The index of the first result (starting from 0).\n     * @param maxResults\n     *        The maximum number of categories.\n     * @param sort\n     *        The sorting criterion.\n     * @return The ordered list of categories of the process definition.\n     * @since 6.0\n     */\n    List<Category> getCategoriesOfProcessDefinition(long processDefinitionId, int startIndex, int maxResults,\n            CategoryCriterion sort);\n\n    /**\n     * Updates the category according to the updater values.\n     *\n     * @param categoryId\n     *        The identifier of the category.\n     * @param updater\n     *        The role updater.\n     * @throws CategoryNotFoundException\n     *         If the category identifier does not refer to an existing category.\n     * @throws UpdateException\n     *         If an exception occurs during the category update.\n     * @since 6.0\n     */\n    void updateCategory(long categoryId, CategoryUpdater updater) throws CategoryNotFoundException, UpdateException;\n\n    /**\n     * Deletes a category and its associations. It does not delete the associated process definitions.\n     *\n     * @param categoryId\n     *        The identifier of the category.\n     * @throws DeletionException\n     *         If an exception occurs when deleting the category.\n     * @since 6.0\n     */\n    void deleteCategory(long categoryId) throws DeletionException;\n\n    /**\n     * Deletes the associations of all the process definitions related to the category given as input parameter\n     * respecting the pagination parameters.\n     * It does not delete the associated process definitions.\n     *\n     * @param categoryId\n     *        The identifier of the category.\n     * @param startIndex\n     *        The index\n     * @param maxResults\n     *        The max number of elements to retrieve per page\n     * @return The number of elements that have been removed\n     * @throws DeletionException\n     *         If an error occurs while removing the process definitions of category.\n     * @since 6.1\n     */\n    long removeProcessDefinitionsFromCategory(long categoryId, int startIndex, int maxResults) throws DeletionException;\n\n    /**\n     * Deletes the associations of categories related the process definition given as input parameter respecting the\n     * pagination parameters.\n     * The process definition and categories are not deleted, but there is no longer an association between them.\n     *\n     * @param processDefinitionId\n     *        The identifier of the process definition.\n     * @param startIndex\n     *        The index\n     * @param maxResults\n     *        The max number of elements to retrieve per page\n     * @return The number of elements that have been removed\n     * @throws DeletionException\n     *         If an error occurs while removing the process definition from category.\n     * @since 6.1\n     */\n    long removeCategoriesFromProcessDefinition(long processDefinitionId, int startIndex, int maxResults)\n            throws DeletionException;\n\n    /**\n     * Counts the number of process definitions which have no category.\n     *\n     * @return The number of process definitions which have no category.\n     * @since 6.0\n     */\n    long getNumberOfUncategorizedProcessDefinitions();\n\n    /**\n     * Returns the paged list of process deployment information items which have no category.\n     *\n     * @param startIndex\n     *        The number of the page (the first page number is 0).\n     * @param maxResults\n     *        The number of categories.\n     * @param sortCriterion\n     *        The sorting criterion.\n     * @return The ordered list of process deployment informations.\n     * @since 6.0\n     */\n    List<ProcessDeploymentInfo> getUncategorizedProcessDeploymentInfos(int startIndex, int maxResults,\n            ProcessDeploymentInfoCriterion sortCriterion);\n\n    /**\n     * Returns the paged list of data definitions of the activity of the process definition.\n     *\n     * @param processDefinitionId\n     *        The identifier of the process definition.\n     * @param activityName\n     *        The name of the activity.\n     * @param startIndex\n     *        The index of the first result (starting from 0).\n     * @param maxResults\n     *        The maximum number of data definitions.\n     * @return The ordered list of data definitions.\n     * @throws ProcessDefinitionNotFoundException\n     *         If the identifier does not refer to an existing process definition.\n     * @throws ActivityDefinitionNotFoundException\n     *         If the name does not refer to an existing activity.\n     * @since 6.0\n     */\n    List<DataDefinition> getActivityDataDefinitions(long processDefinitionId, String activityName, int startIndex,\n            int maxResults)\n            throws ProcessDefinitionNotFoundException, ActivityDefinitionNotFoundException;\n\n    /**\n     * Counts the number of data definitions of the activity of the process definition.\n     *\n     * @param processDefinitionId\n     *        The identifier of the process definition.\n     * @param activityName\n     *        The name of the activity.\n     * @return The number of data definitions of the activity of the process definition.\n     * @throws ProcessDefinitionNotFoundException\n     *         If the identifier does not refer to an existing process definition.\n     * @throws ActivityDefinitionNotFoundException\n     *         If the name does not refer to an existing activity.\n     * @since 6.0\n     */\n    int getNumberOfActivityDataDefinitions(long processDefinitionId, String activityName)\n            throws ProcessDefinitionNotFoundException,\n            ActivityDefinitionNotFoundException;\n\n    /**\n     * Returns the paged list of data definitions of the process definition.\n     *\n     * @param processDefinitionId\n     *        The identifier of the process definition.\n     * @param startIndex\n     *        The index of the first result (starting from 0).\n     * @param maxResults\n     *        The maximum number of data definitions.\n     * @return The ordered list of data definitions.\n     * @throws ProcessDefinitionNotFoundException\n     *         If the identifier does not refer to an existing process definition.\n     * @since 6.0\n     */\n    List<DataDefinition> getProcessDataDefinitions(long processDefinitionId, int startIndex, int maxResults)\n            throws ProcessDefinitionNotFoundException;\n\n    /**\n     * Counts the number of data definitions of the process definition.\n     *\n     * @param processDefinitionId\n     *        The identifier of the process definition.\n     * @return The number of data definitions of the process definition.\n     * @throws ProcessDefinitionNotFoundException\n     *         If the identifier does not refer to an existing process definition.\n     * @since 6.0\n     */\n    int getNumberOfProcessDataDefinitions(long processDefinitionId) throws ProcessDefinitionNotFoundException;\n\n    /**\n     * Retrieves resources inside .bar (or <code>BusinessArchive</code>) file representing the deployed process.\n     * Resources to retrieved are specified with a\n     * file names pattern. The pattern format must be relative to the root of the business archive, without starting\n     * with a '/' character. The pattern can\n     * contain forward slashes after the first character.\n     * eg. if you have an image in resources/folder/image.jpg in your bar file, you can call:\n     *\n     * <pre>\n     * processAPI.getProcessResources(processDefinitionId, \"resources/folder/image.jpg\");\n     * </pre>\n     *\n     * to retrieve a map with one entry: the key is the path of the resource, the value is the binary content of the\n     * resource.\n     *\n     * <pre>\n     * eg.processAPI().getProcessResources(processDefinitionId, {@literal \"resources/folder/.*}\\\\.jpg\")\n     * </pre>\n     *\n     * would retrieve all .jpg files in the folder `resources/folder`. The key in the map is the path for each matching\n     * resource.\n     *\n     * @param processDefinitionId\n     *        The identifier of the process definition.\n     * @param filenamesPattern\n     *        The pattern to retrieve the resources.\n     * @return The map containing the pairs (name, content) of the matching files.\n     * @throws RetrieveException\n     *         If an exception occurs when getting the resources of the process definition.\n     * @since 6.0\n     * @see #exportBarProcessContentUnderHome(long)\n     * @see BusinessArchive#getResources(String)\n     */\n    Map<String, byte[]> getProcessResources(long processDefinitionId, String filenamesPattern) throws RetrieveException;\n\n    /**\n     * Get a resource from the process.\n     * Can only retrieve resources stored in the 'resources' folder of the business archive\n     *\n     * @param processDefinitionId if of the process definition\n     * @param fileName name of the file to retrieve inside the resources folder of the business archive\n     * @return the content of the file\n     * @throws RetrieveException\n     * @throws FileNotFoundException\n     */\n    byte[] getExternalProcessResource(long processDefinitionId, String fileName)\n            throws RetrieveException, FileNotFoundException;\n\n    /**\n     * Get a document resource from the process.\n     * Can only retrieve resources stored in the 'documents' folder of the business archive\n     *\n     * @param processDefinitionId id of the process definition\n     * @param fileName name of the file to retrieve inside the documents folder of the business archive\n     * @return the content of the file\n     * @throws RetrieveException if the resource cannot be read from the database\n     * @throws ProcessResourceNotFoundException if no resource with the given name exists in the process definition\n     */\n    byte[] getDocumentProcessResource(long processDefinitionId, String fileName)\n            throws RetrieveException, ProcessResourceNotFoundException;\n\n    /**\n     * Returns the identifier of the most recently deployed process definition with the given name. This method does not\n     * take into consideration the process\n     * version, but only its deployment date.\n     *\n     * @param processName\n     *        The process definition name.\n     * @return The identifier of the most recently deployed process definition with the given name.\n     * @throws ProcessDefinitionNotFoundException\n     *         If the identifier does not refer to an existing process definition.\n     * @since 6.0\n     */\n    long getLatestProcessDefinitionId(String processName) throws ProcessDefinitionNotFoundException;\n\n    /**\n     * Returns the states of the flow node type. Flow nodes are activities, gateways, or events.\n     *\n     * @param nodeType\n     *        The flow node type.\n     * @return The set of the states of the flow node type.\n     * @since 6.0\n     */\n    Set<String> getSupportedStates(FlowNodeType nodeType);\n\n    /**\n     * Returns the identifier of the process definition with the specified name and version.\n     *\n     * @param name\n     *        The name of the process definition.\n     * @param version\n     *        The version of the process definition.\n     * @return The identifier of the process definition.\n     * @throws ProcessDefinitionNotFoundException\n     *         If the name and version do not refer to an existing process definition.\n     * @since 6.0\n     */\n    long getProcessDefinitionId(String name, String version) throws ProcessDefinitionNotFoundException;\n\n    /**\n     * Returns the paged list of process deployment information items that the actors can start.\n     *\n     * @param actorIds\n     *        The identifiers of the actors.\n     * @param startIndex\n     *        The index of the first result (starting from 0).\n     * @param maxResults\n     *        The maximum number of process deployment informations.\n     * @param sortingCriterion\n     *        The sort criterion\n     * @return The ordered list of process deployment informations.\n     * @since 6.0\n     */\n    List<ProcessDeploymentInfo> getStartableProcessDeploymentInfosForActors(Set<Long> actorIds, int startIndex,\n            int maxResults,\n            ProcessDeploymentInfoCriterion sortingCriterion);\n\n    /**\n     * Checks whether the actors are allowed to start the process definition.\n     *\n     * @param processDefinitionId\n     *        The identifier of the process definition.\n     * @param actorIds\n     *        The identifiers of the actors.\n     * @return true if the actors are allowed to start the process definition; false otherwise.\n     * @since 6.0\n     */\n    boolean isAllowedToStartProcess(long processDefinitionId, Set<Long> actorIds);\n\n    /**\n     * Returns the actor initiator of the process definition.\n     *\n     * @param processDefinitionId\n     *        The identifier of the process definition.\n     * @return The actor initiator of the process definition.\n     * @throws ActorNotFoundException\n     *         If the process definition does not have an actor initiator.\n     * @throws ProcessDefinitionNotFoundException\n     *         If the process definition corresponding to the given identifier is not found\n     * @since 6.0\n     */\n    ActorInstance getActorInitiator(long processDefinitionId)\n            throws ActorNotFoundException, ProcessDefinitionNotFoundException;\n\n    /**\n     * Searches for the number and the list of processes which have been recently started by the user.\n     *\n     * @param userId\n     *        The identifier of the user.\n     * @param searchOptions\n     *        The search criteria. Use ProcessDeploymentInfoSearchDescriptor constants\n     * @see ProcessDeploymentInfoSearchDescriptor\n     * @return The number and the list of processes which have been recently started by the user.\n     * @throws SearchException\n     *         If an exception occurs when getting the processes.\n     * @since 6.0\n     */\n    SearchResult<ProcessDeploymentInfo> searchProcessDeploymentInfosStartedBy(long userId, SearchOptions searchOptions)\n            throws SearchException;\n\n    /**\n     * Searches for the number and the list of processes that the user can start.\n     *\n     * @param userId\n     *        The identifier of the user.\n     * @param searchOptions\n     *        The search criteria. Use ProcessDeploymentInfoSearchDescriptor constants\n     * @see ProcessDeploymentInfoSearchDescriptor\n     * @return The number and the list of processes that the user can start.\n     * @throws SearchException\n     *         If an exception occurs when getting the processes.\n     * @since 6.3.3\n     */\n    SearchResult<ProcessDeploymentInfo> searchProcessDeploymentInfosCanBeStartedBy(long userId,\n            SearchOptions searchOptions) throws SearchException;\n\n    /**\n     * Searches for the number and the list of process definitions.\n     *\n     * @param searchOptions\n     *        The search criteria. Use ProcessDeploymentInfoSearchDescriptor constants\n     * @see ProcessDeploymentInfoSearchDescriptor\n     * @return matching process deployment information results.\n     * @throws SearchException\n     *         If an exception occurs when getting the processes.\n     * @since 6.0\n     */\n    SearchResult<ProcessDeploymentInfo> searchProcessDeploymentInfos(SearchOptions searchOptions)\n            throws SearchException;\n\n    /**\n     * Associates the categories to the process definition.\n     *\n     * @param processDefinitionId\n     *        The identifier of the process definition.\n     * @param categoryIds\n     *        The identifiers of the categories.\n     * @throws AlreadyExistsException\n     *         If the association category/process has already added.\n     * @throws CreationException\n     *         If an exception occurs when associating the process with the categories.\n     * @since 6.0\n     */\n    void addCategoriesToProcess(long processDefinitionId, List<Long> categoryIds)\n            throws AlreadyExistsException, CreationException;\n\n    /**\n     * Dissociates the categories from the process definition. The process definition itself is unchanged.\n     *\n     * @param processDefinitionId\n     *        The identifier of the process definition.\n     * @param categoryIds\n     *        The identifiers of the categories.\n     * @throws DeletionException\n     *         If an exception occurs when dissociating the categories from the process definition.\n     * @since 6.0\n     */\n    void removeCategoriesFromProcess(long processDefinitionId, List<Long> categoryIds) throws DeletionException;\n\n    /**\n     * Searches for the number and the list of uncategorized processes.\n     *\n     * @param searchOptions\n     *        The search criteria. Use ProcessDeploymentInfoSearchDescriptor constants\n     * @see ProcessDeploymentInfoSearchDescriptor\n     * @return The number and the list of uncategorized processes.\n     * @throws SearchException\n     *         If an exception occurs when searching the process deployment information.\n     * @since 6.0\n     */\n    SearchResult<ProcessDeploymentInfo> searchUncategorizedProcessDeploymentInfos(SearchOptions searchOptions)\n            throws SearchException;\n\n    /**\n     * Searches for the number and the list of uncategorized processes supervised by the user.\n     *\n     * @param userId\n     *        The identifier of the user.\n     * @param searchOptions\n     *        The search criteria. Use ProcessDeploymentInfoSearchDescriptor constants\n     * @see ProcessDeploymentInfoSearchDescriptor\n     * @return The number and the list of uncategorized processes.\n     * @throws SearchException\n     *         If an exception occurs when searching the process deployment information.\n     * @since 6.0\n     */\n    SearchResult<ProcessDeploymentInfo> searchUncategorizedProcessDeploymentInfosSupervisedBy(long userId,\n            SearchOptions searchOptions) throws SearchException;\n\n    /**\n     * Searches the number and the list of processes that the user can start which have no category.\n     *\n     * @param userId\n     *        The identifier of the user.\n     * @param searchOptions\n     *        The search criteria. Use ProcessDeploymentInfoSearchDescriptor constants\n     * @see ProcessDeploymentInfoSearchDescriptor\n     * @return The number and the list of uncategorized processes that the user can start.\n     * @throws SearchException\n     *         If an exception occurs when searching the process deployment information.\n     * @since 6.3.3\n     */\n    SearchResult<ProcessDeploymentInfo> searchUncategorizedProcessDeploymentInfosCanBeStartedBy(long userId,\n            SearchOptions searchOptions)\n            throws SearchException;\n\n    /**\n     * Returns the process deployment information of the process definitions.\n     *\n     * @param processDefinitionIds\n     *        The identifiers of the process definitions.\n     * @return The process deployment information of the process definitions, order by name ascending.\n     * @since 6.0\n     */\n    Map<Long, ProcessDeploymentInfo> getProcessDeploymentInfosFromIds(List<Long> processDefinitionIds);\n\n    /**\n     * Returns the implementation of a connector of the process definition.\n     *\n     * @param processDefinitionId\n     *        The identifier of the process definition.\n     * @param connectorName\n     *        The name of the connector.\n     * @param connectorVersion\n     *        The version of the connector.\n     * @return The description of the connector implementation.\n     * @throws ConnectorNotFoundException\n     *         If an exception occurs when getting the connector implementation.\n     * @since 6.0\n     */\n    ConnectorImplementationDescriptor getConnectorImplementation(long processDefinitionId, String connectorName,\n            String connectorVersion)\n            throws ConnectorNotFoundException;\n\n    /**\n     * Returns a paged list of connector implementation descriptors for the process definition.\n     *\n     * @param processDefinitionId\n     *        The identifier of the process definition.\n     * @param startIndex\n     *        The index of the first result (starting from 0).\n     * @param maxsResults\n     *        The maximum number of connector implementations.\n     * @param sortingCriterion\n     *        The sort criterion.\n     * @return The ordered list of connector implementation descriptors of the process definition.\n     * @since 6.0\n     */\n    List<ConnectorImplementationDescriptor> getConnectorImplementations(long processDefinitionId, int startIndex,\n            int maxsResults,\n            ConnectorCriterion sortingCriterion);\n\n    /**\n     * Returns the number of connector implementations of the process definition.\n     *\n     * @param processDefinitionId\n     *        The identifier of the process definition.\n     * @return The number of connector implementation of the process definition.\n     * @since 6.0\n     */\n    long getNumberOfConnectorImplementations(final long processDefinitionId);\n\n    /**\n     * Returns the actor instances.\n     *\n     * @param actorIds\n     *        The identifiers of the actors.\n     * @return The actor instances. (key=actorID, value=actor instance)\n     * @since 6.0\n     */\n    Map<Long, ActorInstance> getActorsFromActorIds(List<Long> actorIds);\n\n    /**\n     * Returns the processes for which a specified group is the only mapped actor.\n     * This is be called before deleting a group from the organization, to make sure that\n     * there are no processes that would become unresolved as a result of removing the group.\n     * A process that has no actor mapping is unresolved and cannot be started.\n     *\n     * @param groupId\n     *        The identifier of the group.\n     * @param startIndex\n     *        The index of the first result (starting from 0).\n     * @param maxResults\n     *        The maximum number of processes.\n     * @param sortingCriterion\n     *        The sort criterion.\n     * @return The processes that the group is the last actor.\n     * @since 6.0\n     */\n    List<ProcessDeploymentInfo> getProcessDeploymentInfosWithActorOnlyForGroup(long groupId, int startIndex,\n            int maxResults,\n            ProcessDeploymentInfoCriterion sortingCriterion);\n\n    /**\n     * Returns the processes for which one of the listed groups is the only mapped actors.\n     * This is be called before deleting a group from the organization, to make sure that\n     * there are no processes that would become unresolved as a result of removing one of the listed groups.\n     * A process that has no actor mapping is unresolved and cannot be started.\n     *\n     * @param groupIds\n     *        The identifiers of the groups.\n     * @param startIndex\n     *        The index of the first result (starting from 0).\n     * @param maxResults\n     *        The maximum number of processes.\n     * @param sortingCriterion\n     *        The sort criterion.\n     * @return The processes that the groups are the last actor(s).\n     * @since 6.0\n     */\n    List<ProcessDeploymentInfo> getProcessDeploymentInfosWithActorOnlyForGroups(List<Long> groupIds, int startIndex,\n            int maxResults,\n            ProcessDeploymentInfoCriterion sortingCriterion);\n\n    /**\n     * Returns the processes for which the role is the only mapped actor.\n     * This is be called before deleting a role from the organization, to make sure that\n     * there are no processes that would become unresolved as a result of removing the role.\n     * A process that has no actor mapping is unresolved and cannot be started.\n     *\n     * @param roleId\n     *        The identifier of the role.\n     * @param startIndex\n     *        The index of the first result (starting from 0).\n     * @param maxResults\n     *        The maximum number of processes.\n     * @param sortingCriterion\n     *        The sort criterion.\n     * @return The processes that the role is the last actor.\n     * @since 6.0\n     */\n    List<ProcessDeploymentInfo> getProcessDeploymentInfosWithActorOnlyForRole(long roleId, int startIndex,\n            int maxResults,\n            ProcessDeploymentInfoCriterion sortingCriterion);\n\n    /**\n     * Returns the processes for which one of the listed roles is the only mapped actors.\n     * This is be called before deleting a role from the organization, to make sure that\n     * there are no processes that would become unresolved as a result of removing one of the listed roles.\n     * A process that has no actor mapping is unresolved and cannot be started.\n     *\n     * @param roleIds\n     *        The identifiers of the roles.\n     * @param startIndex\n     *        The index of the first result (starting from 0).\n     * @param maxResults\n     *        The maximum number of processes.\n     * @param sortingCriterion\n     *        The sort criterion.\n     * @return The processes that the roles are actor(s).\n     * @since 6.0\n     */\n    List<ProcessDeploymentInfo> getProcessDeploymentInfosWithActorOnlyForRoles(List<Long> roleIds, int startIndex,\n            int maxResults,\n            ProcessDeploymentInfoCriterion sortingCriterion);\n\n    /**\n     * Returns the processes for which the user is the only mapped actor.\n     * This is be called before deleting a user from the organization, to make sure that\n     * there are no processes that would become unresolved as a result of removing the user.\n     * A process that has no actor mapping is unresolved and cannot be started.\n     *\n     * @param userId\n     *        The identifier of the user.\n     * @param startIndex\n     *        The index of the first result (starting from 0).\n     * @param maxResults\n     *        The maximum number of processes.\n     * @param sortingCriterion\n     *        The sort criterion.\n     * @return The processes that the user is the last actor.\n     * @see #getProcessDeploymentInfosWithActorOnlyForUsers(List, int, int, ProcessDeploymentInfoCriterion)\n     * @since 6.0\n     */\n    List<ProcessDeploymentInfo> getProcessDeploymentInfosWithActorOnlyForUser(long userId, int startIndex,\n            int maxResults,\n            ProcessDeploymentInfoCriterion sortingCriterion);\n\n    /**\n     * Returns the processes for which one of the listed users is the only mapped actor.\n     *\n     * @param userIds\n     *        The identifiers of the users.\n     * @param startIndex\n     *        The index of the first result (starting from 0).\n     * @param maxResults\n     *        The maximum number of processes.\n     * @param sortingCriterion\n     *        The sort criterion.\n     * @return The processes that the users are the last actor(s).\n     * @see #getProcessDeploymentInfosWithActorOnlyForUser(long, int, int, ProcessDeploymentInfoCriterion)\n     * @since 6.0\n     */\n    List<ProcessDeploymentInfo> getProcessDeploymentInfosWithActorOnlyForUsers(List<Long> userIds, int startIndex,\n            int maxResults,\n            ProcessDeploymentInfoCriterion sortingCriterion);\n\n    /**\n     * Returns a specific process definition that include informations such as tasks definition, actors...\n     *\n     * @param processDefinitionId\n     *        Identifier of process definition\n     * @return The corresponding process definition with informations.\n     * @throws ProcessDefinitionNotFoundException\n     *         If the process definition doesn't exist.\n     * @since 6.1\n     */\n    DesignProcessDefinition getDesignProcessDefinition(long processDefinitionId)\n            throws ProcessDefinitionNotFoundException;\n\n    /**\n     * Searches the number and the list of processes supervised by the user.\n     *\n     * @param userId\n     *        The identifier of the user.\n     * @param searchOptions\n     *        The search criteria. Use ProcessDeploymentInfoSearchDescriptor constants\n     * @see ProcessDeploymentInfoSearchDescriptor\n     * @return The number and the list of processes supervised by the user.\n     * @throws SearchException\n     *         If an exception occurs when getting the process deployment information.\n     */\n    SearchResult<ProcessDeploymentInfo> searchProcessDeploymentInfosSupervisedBy(long userId,\n            SearchOptions searchOptions) throws SearchException;\n\n    /**\n     * Search for process definitions that can be started by users managed by a specific user.\n     *\n     * @param managerUserId\n     *        The identifier of the manager.\n     * @param searchOptions\n     *        The search criteria. Use ProcessDeploymentInfoSearchDescriptor constants\n     * @see ProcessDeploymentInfoSearchDescriptor\n     * @return\n     *         The list of process definitions that have at least one initiator who is mapped to a user to who reports\n     *         to the specified manager.\n     * @throws SearchException\n     *         If an exception occurs when getting the process deployment information.\n     * @since 6.3.3\n     */\n    SearchResult<ProcessDeploymentInfo> searchProcessDeploymentInfosCanBeStartedByUsersManagedBy(long managerUserId,\n            SearchOptions searchOptions)\n            throws SearchException;\n\n    /**\n     * Adds the user as a supervisor of the process.\n     * A supervisor of a process is responsible for what happens to the process. A supervisor can see\n     * the tasks in the process, and can carry out process administration. A supervisor is defined in a\n     * ProcessSupervisor\n     * object as a mapping of users, groups, or roles to the process supervisor (similar to actor mapping).\n     * A process has one ProcessSupervisor; however, as this can be mapped to several users, either explicitly or by\n     * mapping groups or roles, the process can be supervised by several people.\n     *\n     * @param processDefinitionId\n     *        The identifier of the process definition.\n     * @param userId\n     *        The identifier of the user.\n     * @return The user as a process supervisor.\n     * @throws CreationException\n     *         If an exception occurs when creating the process supervisor.\n     * @throws AlreadyExistsException\n     *         If the user is already the process supervisor.\n     * @since 6.0\n     */\n    ProcessSupervisor createProcessSupervisorForUser(long processDefinitionId, long userId)\n            throws CreationException, AlreadyExistsException;\n\n    /**\n     * Adds the role as a supervisor of the process.\n     * A supervisor of a process is responsible for what happens to the process. A supervisor can see\n     * the tasks in the process, and can carry out process administration. A supervisor is defined in a\n     * ProcessSupervisor\n     * object as a mapping of users, groups, or roles to the process supervisor (similar to actor mapping).\n     * A process has one ProcessSupervisor; however, as this can be mapped to several users, either explicitly or by\n     * mapping groups or roles, the process can be supervised by several people.\n     *\n     * @param processDefinitionId\n     *        The identifier of the process definition.\n     * @param roleId\n     *        The identifier of the role.\n     * @return The role as a supervisor of the process.\n     * @throws CreationException\n     *         If an exception occurs when creating the process supervisor.\n     * @throws AlreadyExistsException\n     *         If the provided role is already a supervisor for the provided process.\n     * @since 6.0\n     */\n    ProcessSupervisor createProcessSupervisorForRole(long processDefinitionId, long roleId)\n            throws CreationException, AlreadyExistsException;\n\n    /**\n     * Adds the group as a supervisor of the process.\n     * A supervisor of a process is responsible for what happens to the process. A supervisor can see\n     * the tasks in the process, and can carry out process administration. A supervisor is defined in a\n     * ProcessSupervisor\n     * object as a mapping of users, groups, or roles to the process supervisor (similar to actor mapping).\n     * A process has one ProcessSupervisor; however, as this can be mapped to several users, either explicitly or by\n     * mapping groups or roles, the process can be supervised by several people.\n     *\n     * @param processDefinitionId\n     *        The identifier of the process definition.\n     * @param groupId\n     *        The identifier of the group.\n     * @return The group as a supervisor of the process.\n     * @throws CreationException\n     *         If an exception occurs when creating the process supervisor.\n     * @throws AlreadyExistsException\n     *         If the provided group is already a supervisor for the provided process.\n     * @since 6.0\n     */\n    ProcessSupervisor createProcessSupervisorForGroup(long processDefinitionId, long groupId)\n            throws CreationException, AlreadyExistsException;\n\n    /**\n     * Adds the membership as a supervisor of the process.\n     * A supervisor of a process is responsible for what happens to the process. A supervisor can see\n     * the tasks in the process, and can carry out process administration. A supervisor is defined in a\n     * ProcessSupervisor\n     * object as a mapping of users, groups, or roles to the process supervisor (similar to actor mapping).\n     * A process has one ProcessSupervisor; however, as this can be mapped to several users, either explicitly or by\n     * mapping groups or roles, the process can be supervised by several people.\n     *\n     * @param processDefinitionId\n     *        The identifier of the process definition.\n     * @param groupId\n     *        The identifier of the group.\n     * @param roleId\n     *        The identifier of the role.\n     * @return The membership as a supervisor of the process.\n     * @throws CreationException\n     *         If an exception occurs when creating the process supervisor.\n     * @throws AlreadyExistsException\n     *         If the provided membership (group + role) is already a supervisor for the provided process.\n     * @since 6.0\n     */\n    ProcessSupervisor createProcessSupervisorForMembership(long processDefinitionId, long groupId, long roleId)\n            throws CreationException,\n            AlreadyExistsException;\n\n    /**\n     * Checks whether the user is the process supervisor.\n     *\n     * @param processDefinitionId\n     *        The identifier of the process definition.\n     * @param userId\n     *        The identifier of the user.\n     * @return true if the user is currently a supervisor of the process; false otherwise.\n     * @since 6.0\n     */\n    boolean isUserProcessSupervisor(long processDefinitionId, long userId);\n\n    /**\n     * Deletes a process supervisor.\n     *\n     * @param supervisorId\n     *        The identifier of the {@link ProcessSupervisor}.\n     * @throws DeletionException\n     *         If an exception occurs when deleting the process supervisor.\n     * @since 6.0\n     */\n    void deleteSupervisor(long supervisorId) throws DeletionException;\n\n    /**\n     * Delete the {@link ProcessSupervisor} object that is identified by this processDefinitionId, userId, roleId and\n     * groupId.\n     * <p>\n     * e.g. to delete the process supervisor that is set for userId 12 and process id 255 call deleteSupervisor(255, 12,\n     * null, null)\n     * <p>\n     * be careful if the user is supervisor because he is in e.g. a group of super visor calling this method with the\n     * userId will do nothing, you must find the\n     * {@link ProcessSupervisor} that link the user to the process\n     *\n     * @param processDefinitionId\n     *        The Identifier of the process definition to delete the supervisor for.\n     * @param userId\n     *        The Id of the user used as a supervisor for the given process. Can be null.\n     * @param roleId\n     *        The Id of the role used as a supervisor for the given process. Can be null.\n     * @param groupId\n     *        The Id of the group used as a supervisor for the given process. Can be null.\n     * @throws DeletionException\n     *         If a problem occurs while deleting the supervisor for the given process.\n     * @since 6.0\n     */\n    void deleteSupervisor(Long processDefinitionId, Long userId, Long roleId, Long groupId) throws DeletionException;\n\n    /**\n     * Searches for the number and the list of processes supervisors.\n     *\n     * @param searchOptions\n     *        The search criteria. Use ProcessSupervisorSearchDescriptor constants\n     * @see ProcessSupervisorSearchDescriptor\n     * @return The number and the list of processes supervisors.\n     * @throws SearchException\n     *         If an exception occurs when getting the processes supervisors.\n     * @since 6.0\n     */\n    SearchResult<ProcessSupervisor> searchProcessSupervisors(SearchOptions searchOptions) throws SearchException;\n\n    /**\n     * Returns a paged list of categories that are not associated with the process definition.\n     *\n     * @param processDefinitionId\n     *        The identifier of the process definition.\n     * @param startIndex\n     *        The number of the page (the first page number is 0).\n     * @param maxResults\n     *        The maximum number of categories.\n     * @param sortingCriterion\n     *        The sort criterion.\n     * @return The categories that are not associated with the process definition.\n     */\n    List<Category> getCategoriesUnrelatedToProcessDefinition(long processDefinitionId, int startIndex, int maxResults,\n            CategoryCriterion sortingCriterion);\n\n    /**\n     * Counts the number of process definitions that do not belong to the category.\n     *\n     * @param categoryId\n     *        The identifier of the category.\n     * @return The number of process definitions that have not the category.\n     */\n    long getNumberOfProcessDeploymentInfosUnrelatedToCategory(long categoryId);\n\n    /**\n     * Returns the paginated list of process deployment information items of the category.\n     *\n     * @param categoryId\n     *        The identifier of the category.\n     * @param startIndex\n     *        The number of the page (the first page number is 0).\n     * @param maxResults\n     *        The number of process deployment informations.\n     * @param sortingCriterion\n     *        The sort criterion.\n     * @return A list of process unrelated to the category which has categoryId as id\n     * @since 6.0\n     */\n    List<ProcessDeploymentInfo> getProcessDeploymentInfosUnrelatedToCategory(long categoryId, int startIndex,\n            int maxResults,\n            ProcessDeploymentInfoCriterion sortingCriterion);\n\n    /**\n     * Searches for the number and the list of users who can start the process.\n     * Note: managerUserId is a possible filter.\n     *\n     * @param processDefinitionId\n     *        The identifier of the process definition.\n     * @param searchOptions\n     *        The search criteria. Use UserSearchDescriptor constants\n     * @see ProcessDeploymentInfoSearchDescriptor\n     * @return The number and the list of users who can start the process.\n     * @throws SearchException\n     *         If an exception occurs when getting the users.\n     * @since 6.0\n     */\n    SearchResult<User> searchUsersWhoCanStartProcessDefinition(long processDefinitionId, SearchOptions searchOptions)\n            throws SearchException;\n\n    /**\n     * Get process deployment information from a list of processInstance id\n     *\n     * @param processInstanceIds\n     *        Identifier of the processInstance\n     * @return A map of &lt;processInstantsIds,ProcessDeploymentInfos&gt;, ordered by the name of the process ascending\n     * @since 6.0\n     */\n    Map<Long, ProcessDeploymentInfo> getProcessDeploymentInfosFromProcessInstanceIds(List<Long> processInstanceIds);\n\n    /**\n     * Get process deployment information from a list of archived processInstance ids\n     *\n     * @param archivedProcessInstantsIds\n     *        Identifier of the archived process instance\n     * @return A map of &lt;archivedProcessInstantsIds,ProcessDeploymentInfos&gt;\n     * @since 6.0\n     */\n    Map<Long, ProcessDeploymentInfo> getProcessDeploymentInfosFromArchivedProcessInstanceIds(\n            List<Long> archivedProcessInstantsIds);\n\n    /**\n     * Export processes of bar under home by a processDefinition id\n     *\n     * @param processDefinitionId\n     *        Identifier of the processDefinition\n     * @return An array of byte\n     * @throws ProcessExportException\n     *         If an export problem occurs\n     * @since 6.0\n     */\n    byte[] exportBarProcessContentUnderHome(long processDefinitionId) throws ProcessExportException;\n\n    /**\n     * Disables and deletes the process. Wrapping method for methods {@link #disableProcess(long)} and\n     * {@link #deleteProcessDefinition(long)};\n     *\n     * @param processDefinitionId\n     *        The process definition identifier.\n     * @throws ProcessDefinitionNotFoundException\n     *         If the identifier does not refer to an existing process definition.\n     * @throws ProcessActivationException\n     *         If an exception occurs while disabling the process.\n     * @throws DeletionException\n     *         If an exception occurs while deleting the process.\n     * @see #disableProcess(long)\n     * @see #deleteProcessDefinition(long)\n     * @since 6.1\n     */\n    void disableAndDeleteProcessDefinition(long processDefinitionId)\n            throws ProcessDefinitionNotFoundException, ProcessActivationException, DeletionException;\n\n    /**\n     * Lists the possible users (candidates) of the specified human task definition.\n     * <br>\n     * If the task contains a user filter, it is not executed.\n     * Users are ordered by user name.\n     * An empty list is returned if:\n     * - the task or the process does not exist\n     * - the flow node is not a human task\n     *\n     * @param processDefinitionId\n     *        The identifier of process definition\n     * @param humanTaskName\n     *        The name of the human task\n     * @param startIndex\n     *        The start index\n     * @param maxResults\n     *        The list of users\n     * @return The list of users.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @throws RetrieveException\n     *         If an exception occurs while retrieving the users\n     * @since 6.1\n     */\n    List<User> getPossibleUsersOfHumanTask(long processDefinitionId, String humanTaskName, int startIndex,\n            int maxResults);\n\n    /**\n     * Retrieves the list of user identifiers for the given actor and process.\n     *\n     * @param processDefinitionId\n     *        The process definition identifier\n     * @param actorName\n     *        The actor name\n     * @param startIndex\n     *        The start index (the first valid value is zero)\n     * @param maxResults\n     *        The max number of user identifiers to be retrieved\n     * @return Retrieves the list of user identifiers for the given actor and process.\n     * @since 6.3.2\n     */\n    List<Long> getUserIdsForActor(long processDefinitionId, String actorName, int startIndex, int maxResults);\n\n    /**\n     * Purges the classLoader of the process definition. The process must be disable and no instances should run,\n     * otherwise an UpdateException is thrown.\n     *\n     * @param processDefinitionId\n     *        The identifier of the process definition.\n     * @throws ProcessDefinitionNotFoundException\n     *         If the identifier does not refer to an existing process definition.\n     * @throws UpdateException\n     *         If the process is not disable or if instances are still running\n     * @since 6.4.0\n     */\n    void purgeClassLoader(long processDefinitionId) throws ProcessDefinitionNotFoundException, UpdateException;\n\n    /**\n     * Gets how many parameters the process definition contains.\n     *\n     * @param processDefinitionId\n     *        The identifier of the processDefinition\n     * @return The number of parameters of a process definition\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @since 7.0.0\n     */\n    int getNumberOfParameterInstances(long processDefinitionId);\n\n    /**\n     * Get a parameter instance by process definition UUID\n     *\n     * @param processDefinitionId\n     *        The identifier of the processDefinition\n     * @param parameterName\n     *        The parameter name for get ParameterInstance\n     * @return The ParameterInstance of the process with processDefinitionUUID and name parameterName\n     * @throws org.bonitasoft.engine.exception.NotFoundException\n     *         Error thrown if the given parameter is not found.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @since 7.0.0\n     */\n    ParameterInstance getParameterInstance(long processDefinitionId, String parameterName) throws NotFoundException;\n\n    /**\n     * Returns the parameters of a process definition or an empty map if the process does not contain any parameter.\n     *\n     * @param processDefinitionId\n     *        The identifier of the processDefinition\n     * @param startIndex\n     *        The index of the page to be returned. First page has index 0.\n     * @param maxResults\n     *        The number of result per page. Maximum number of result returned.\n     * @param sort\n     *        The criterion to sort the result\n     * @return The ordered list of parameter instances\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @since 7.0.0\n     */\n    List<ParameterInstance> getParameterInstances(long processDefinitionId, int startIndex, int maxResults,\n            ParameterCriterion sort);\n\n    /**\n     * Search for form mapping\n     *\n     * @param searchOptions\n     *        The search criteria. Use FormMappingSearchDescriptor constants\n     * @see FormMappingSearchDescriptor\n     * @return the result of the search\n     * @see org.bonitasoft.engine.form.FormMappingSearchDescriptor\n     * @see org.bonitasoft.engine.form.FormMappingType\n     * @since 7.0.0\n     */\n    SearchResult<FormMapping> searchFormMappings(SearchOptions searchOptions) throws SearchException;\n\n    /**\n     * @param formMappingId\n     *        the id of the form mapping to get\n     * @return\n     *         the form mapping\n     * @throws FormMappingNotFoundException\n     * @since 7.0.0\n     */\n    FormMapping getFormMapping(final long formMappingId) throws FormMappingNotFoundException;\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/ProcessRuntimeAPI.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\nimport java.io.Serializable;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.comment.ArchivedComment;\nimport org.bonitasoft.engine.bpm.comment.Comment;\nimport org.bonitasoft.engine.bpm.connector.ArchivedConnectorInstance;\nimport org.bonitasoft.engine.bpm.connector.ConnectorExecutionException;\nimport org.bonitasoft.engine.bpm.connector.ConnectorInstance;\nimport org.bonitasoft.engine.bpm.connector.ConnectorNotFoundException;\nimport org.bonitasoft.engine.bpm.contract.ContractDefinition;\nimport org.bonitasoft.engine.bpm.contract.ContractViolationException;\nimport org.bonitasoft.engine.bpm.data.ArchivedDataInstance;\nimport org.bonitasoft.engine.bpm.data.ArchivedDataNotFoundException;\nimport org.bonitasoft.engine.bpm.data.DataInstance;\nimport org.bonitasoft.engine.bpm.data.DataNotFoundException;\nimport org.bonitasoft.engine.bpm.flownode.*;\nimport org.bonitasoft.engine.bpm.process.*;\nimport org.bonitasoft.engine.exception.*;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionEvaluationException;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.identity.UserNotFoundException;\nimport org.bonitasoft.engine.job.FailedJob;\nimport org.bonitasoft.engine.operation.Operation;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.InvalidSessionException;\n\n/**\n * <code>ProcessRuntimeAPI</code> deals with Process runtime notions such as starting a new instance of a process,\n * retrieving and executing tasks, accessing to\n * all types of tasks, assigning a user to a task, retrieving archived versions of a task, accessing / updating data /\n * variable values, adding / retrieving\n * process comments ...\n * It generally allows all BPM runtime actions, that is, once process instances are running of finished executing.\n *\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Yanyan Liu\n * @author Zhao Na\n * @author Frederic Bouquet\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n * @author Emmanuel Duchastenier\n */\npublic interface ProcessRuntimeAPI {\n\n    /**\n     * List all open root process instances.\n     *\n     * @param searchOptions\n     *        The search criterion. See {@link org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor} for\n     *        valid fields for searching and sorting.\n     * @return A {@link ProcessInstance} object.\n     * @throws SearchException\n     *         If an exception occurs when getting the list of tasks.\n     * @since 6.0\n     */\n    SearchResult<ProcessInstance> searchOpenProcessInstances(SearchOptions searchOptions) throws SearchException;\n\n    /**\n     * List all process instances.\n     *\n     * @param searchOptions\n     *        The search criterion. See {@link org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor} for\n     *        valid fields for searching and sorting.\n     * @return A {@link ProcessInstance} object.\n     * @throws SearchException\n     *         If an exception occurs when getting the list of {@link ProcessInstance}.\n     * @since 6.2\n     */\n    SearchResult<ProcessInstance> searchProcessInstances(SearchOptions searchOptions) throws SearchException;\n\n    /**\n     * List all process instances with at least one failed task or the\n     * {@link org.bonitasoft.engine.bpm.process.ProcessInstanceState#ERROR} state.\n     *\n     * @param searchOptions\n     *        The search criterion. See {@link org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor} for\n     *        valid fields for searching and sorting.\n     * @return A {@link ProcessInstance} object.\n     * @throws SearchException\n     *         If an exception occurs when getting the list of {@link ProcessInstance}.\n     * @since 6.4.0\n     */\n    SearchResult<ProcessInstance> searchFailedProcessInstances(SearchOptions searchOptions) throws SearchException;\n\n    /**\n     * List all process instances with at least one failed task or the\n     * {@link org.bonitasoft.engine.bpm.process.ProcessInstanceState#ERROR} state that\n     * are supervised by the given user.\n     * If the specified userId does not correspond to a user, an empty SearchResult is returned.\n     *\n     * @param userId\n     *        The identifier of the user.\n     * @param searchOptions\n     *        The search criterion. See {@link org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor} for\n     *        valid fields for searching and sorting.\n     * @return The list of failed process instances supervised by the specified user.\n     * @throws SearchException\n     *         If an exception occurs when getting the list of process instances.\n     * @since 7.0\n     */\n    SearchResult<ProcessInstance> searchFailedProcessInstancesSupervisedBy(long userId, SearchOptions searchOptions)\n            throws SearchException;\n\n    /**\n     * List all open process instances supervised by a user.\n     * If the specified userId does not correspond to a user, an empty SearchResult is returned.\n     *\n     * @param userId\n     *        The identifier of the user.\n     * @param searchOptions\n     *        The search criterion. See {@link org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor} for\n     *        valid fields for searching and sorting.\n     * @return The list of process instances supervised by the specified user.\n     * @throws SearchException\n     *         If an exception occurs when getting the list of process instances.\n     * @since 6.0\n     */\n    SearchResult<ProcessInstance> searchOpenProcessInstancesSupervisedBy(long userId, SearchOptions searchOptions)\n            throws SearchException;\n\n    /**\n     * Get the number of process data instances by process id.\n     *\n     * @param processInstanceId\n     *        The identifier of the process instance.\n     * @return The number of process data instances.\n     * @throws ProcessInstanceNotFoundException\n     *         If the specified ProcessInstance does not refer to a process instance.\n     * @since 6.0\n     */\n    long getNumberOfProcessDataInstances(long processInstanceId) throws ProcessInstanceNotFoundException;\n\n    /**\n     * Get the number of activity data instances by activity id.\n     *\n     * @param activityInstanceId\n     *        The identifier of the activity instance.\n     * @return The number of activity data instances.\n     * @throws ActivityInstanceNotFoundException\n     *         If the specified activity instance does not refer to an activity instance.\n     * @since 6.0\n     */\n    long getNumberOfActivityDataInstances(long activityInstanceId) throws ActivityInstanceNotFoundException;\n\n    /**\n     * Get a paged list of all process instances.\n     *\n     * @param startIndex\n     *        The index of the first result (starting from 0).\n     * @param maxResults\n     *        The maximum number of results per page.\n     * @param criterion\n     *        The sort criterion.\n     * @return The list of process instances.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @since 6.0\n     */\n    List<ProcessInstance> getProcessInstances(int startIndex, int maxResults, ProcessInstanceCriterion criterion);\n\n    /**\n     * Get a paged list of archived completed process instances.\n     *\n     * @param startIndex\n     *        The index of the first result (starting from 0).\n     * @param maxResults\n     *        The maximum number of results per page.\n     * @param criterion\n     *        The sort criterion.\n     * @return The list of archived process instances.\n     * @since 6.0\n     * @deprecated since 10.3 use {@link #getCompletedProcessInstances(int, int, ProcessInstanceCriterion)} instead\n     */\n    @Deprecated(since = \"10.3.0\", forRemoval = true)\n    List<ArchivedProcessInstance> getArchivedProcessInstances(int startIndex, int maxResults,\n            ProcessInstanceCriterion criterion);\n\n    /**\n     * Get a paged list of archived completed process instances.\n     *\n     * @param startIndex\n     *        The index of the first result (starting from 0).\n     * @param maxResults\n     *        The maximum number of results per page.\n     * @param criterion\n     *        The sort criterion.\n     * @return The list of archived process instances.\n     * @since 10.3\n     */\n    List<ArchivedProcessInstance> getCompletedProcessInstances(final int startIndex, final int maxResults,\n            final ProcessInstanceCriterion criterion);\n\n    /**\n     * Get a paged list of archived activity instances for a process instance.\n     *\n     * @param sourceProcessInstanceId\n     *        The identifier of the source process instance (used as root container id of the archived activity\n     *        instances).\n     * @param startIndex\n     *        The index of the first result (starting from 0).\n     * @param maxResults\n     *        The maximum number of result per page.\n     * @param criterion\n     *        The sort criterion.\n     * @return The list of archived activity instances.\n     * @since 6.0\n     */\n    List<ArchivedActivityInstance> getArchivedActivityInstances(long sourceProcessInstanceId, int startIndex,\n            int maxResults,\n            ActivityInstanceCriterion criterion);\n\n    /**\n     * Retrieve a paged list of open activities for a given process instance.\n     *\n     * @param processInstanceId\n     *        The identifier of the process instance.\n     * @param startIndex\n     *        The index of the first result (starting from 0).\n     * @param maxResults\n     *        The maximum number of results per page.\n     * @param criterion\n     *        The sort criterion.\n     * @return The list of activity instances.\n     * @since 6.0\n     */\n    List<ActivityInstance> getOpenActivityInstances(long processInstanceId, int startIndex, int maxResults,\n            ActivityInstanceCriterion criterion);\n\n    /**\n     * Get the total number of open activity instances by process instance id.\n     *\n     * @param processInstanceId\n     *        The identifier of the process instance.\n     * @return The number of open activity instances.\n     * @throws ProcessInstanceNotFoundException\n     *         If no matching process instance is found for parameter processInstanceId\n     * @since 6.0\n     */\n    int getNumberOfOpenedActivityInstances(long processInstanceId) throws ProcessInstanceNotFoundException;\n\n    /**\n     * Get the number of open process instances.\n     * An open process instance is a process instance that has not been archived.\n     *\n     * @return The total number of open process instances.\n     * @since 6.0\n     */\n    long getNumberOfProcessInstances();\n\n    /**\n     * Get the number of archived completed process instances.\n     * Root process instances in state COMPLETED are counted. Process instances started by call activities won't be\n     * counted.\n     *\n     * @return The number of archived process instances.\n     * @since 6.0\n     * @deprecated since 10.3 use {@link #getNumberOfCompletedProcessInstances()} instead\n     */\n    @Deprecated(since = \"10.3.0\", forRemoval = true)\n    long getNumberOfArchivedProcessInstances();\n\n    /**\n     * Get the number of archived completed process instances.\n     * Root process instances in state COMPLETED are counted. Process instances started by call activities won't be\n     * counted.\n     *\n     * @return The number of archived process instances.\n     * @since 10.3\n     */\n    long getNumberOfCompletedProcessInstances();\n\n    /**\n     * Delete the specified process instance.\n     *\n     * @param processInstanceId\n     *        The identifier of the process instance to delete.\n     * @throws org.bonitasoft.engine.exception.ProcessInstanceHierarchicalDeletionException\n     *         If a process instance cannot be deleted because of a parent that is still active.\n     * @throws DeletionException\n     *         If an error occurs during deletion.\n     * @since 6.0\n     */\n    void deleteProcessInstance(long processInstanceId) throws DeletionException;\n\n    /**\n     * Delete active process instances, and their elements, of process definition given as input parameter respecting\n     * the pagination parameters.\n     * Passing {@link Integer#MAX_VALUE} as maxResults is discouraged as the amount of operations may be large and may\n     * thus result in timeout operation.\n     * Instead, to delete all Process instances of a specific process definition, should you should use a loop and\n     * delete instances in bulk.\n     *\n     * @param processDefinitionId\n     *        Identifier of the processDefinition\n     * @param startIndex\n     *        The index\n     * @param maxResults\n     *        The max number of elements to retrieve per page\n     * @return The number of elements that have been deleted\n     * @throws DeletionException\n     *         If a process instance can't be deleted because of a parent that is still active\n     * @since 6.1\n     */\n    long deleteProcessInstances(long processDefinitionId, int startIndex, int maxResults) throws DeletionException;\n\n    /**\n     * Delete archived process instances of process definition given as input parameter respecting the pagination\n     * parameters.\n     * Passing {@link Integer#MAX_VALUE} as maxResults is discouraged as the amount of operations may be large and may\n     * thus result in timeout operation.\n     * Instead, to delete all archived process instances of a specific process definition, you should use a loop and\n     * delete archived instances in bulk.\n     *\n     * @param processDefinitionId\n     *        Identifier of the processDefinition\n     * @param startIndex\n     *        The index\n     * @param maxResults\n     *        The max number of elements to retrieve per page\n     * @return The number of rows deleted from the archived process instance table related to the cases that were\n     *         deleted\n     * @throws DeletionException\n     *         If there is some e.g. connection issue to the database\n     * @since 6.1\n     */\n    long deleteArchivedProcessInstances(long processDefinitionId, int startIndex, int maxResults)\n            throws DeletionException;\n\n    /**\n     * Delete all archived process instance (different states) of the source identifier list.\n     * This work only with process instances that are root (not called by an other process). Using this method with ids\n     * of called processes (using call activity) will not delete them.\n     *\n     * @param sourceProcessInstanceIds\n     *        Identifiers corresponding to {@link ArchivedProcessInstance#getSourceObjectId()}. These ids needs to be\n     *        ids of root process instances\n     * @return The number of {@link ArchivedProcessInstance}s that have been deleted in any state. For example, process\n     *         instance can be archived in several\n     *         states: Cancelled, Aborted, Completed, Failed\n     * @throws DeletionException\n     *         If there is some e.g. connection issue to the database\n     * @since 6.4.0\n     */\n    long deleteArchivedProcessInstancesInAllStates(List<Long> sourceProcessInstanceIds) throws DeletionException;\n\n    /**\n     * Delete all archived process instance (different states) corresponding to the source identifier.\n     * This work only with process instances that are root (not called by an other process). Using this method with id\n     * of called process (using call activity) will not delete it.\n     *\n     * @param sourceProcessInstanceId\n     *        Identifier corresponding to {@link ArchivedProcessInstance#getSourceObjectId()}. These id needs to be the\n     *        id of a root process instances\n     * @return The number of {@link ArchivedProcessInstance}s that have been deleted in any state. For example, process\n     *         instance can be archived in several\n     *         states: Cancelled, Aborted, Completed, Failed\n     * @throws DeletionException\n     *         If there is some e.g. connection issue to the database\n     * @since 6.4.0\n     */\n    long deleteArchivedProcessInstancesInAllStates(long sourceProcessInstanceId) throws DeletionException;\n\n    /**\n     * Start an instance of the process with the specified process definition, using the current session user.\n     * <b>Note</b>: If the process instantiation\n     * contract requires inputs, you must use {@link #startProcessWithInputs(long, Map)} instead.\n     *\n     * @param processDefinitionId\n     *        The identifier of the process definition for which an instance will be started.\n     * @return An instance of the process.\n     * @throws ProcessDefinitionNotFoundException\n     *         If no matching process definition is found.\n     * @throws ProcessActivationException\n     *         If an exception occurs during activation.\n     * @throws ProcessExecutionException\n     *         If a problem occurs when starting the process.\n     * @since 6.0\n     */\n    ProcessInstance startProcess(long processDefinitionId)\n            throws ProcessDefinitionNotFoundException, ProcessActivationException, ProcessExecutionException;\n\n    /**\n     * Instantiates a process. <br />\n     * The process variables will be initialized by the <code>initialVariables</code> parameter. <b>Note</b>: If the\n     * process instantiation contract requires\n     * inputs, you must use {@link #startProcessWithInputs(long, Map)} instead, transforming\n     * <code>initialVariables</code> parameter as follows: in the process,\n     * define process variables initial values (expressions) with contract inputs.\n     *\n     * @param processDefinitionId\n     *        The identifier of the processDefinition\n     * @param initialVariables\n     *        The couples of initial variable/value\n     * @return A ProcessInstance object\n     * @throws ProcessDefinitionNotFoundException\n     *         If The identifier of process definition does not refer to any existing process definition\n     * @throws ProcessExecutionException\n     *         If the process fails to start\n     * @throws ProcessActivationException\n     *         If the process is disable\n     * @since 6.1\n     */\n    ProcessInstance startProcess(long processDefinitionId, Map<String, Serializable> initialVariables)\n            throws ProcessDefinitionNotFoundException,\n            ProcessActivationException, ProcessExecutionException;\n\n    /**\n     * Start an instance of the process with the specified process definition id, and set the initial values of the data\n     * with the given operations. <b>Note</b>:\n     * If the process instantiation contract requires inputs, you must use {@link #startProcessWithInputs(long, Map)}\n     * instead. The best practice is to define\n     * contract inputs in the process definition, and use these contract inputs in operations, instead of providing\n     * operations at start time.\n     *\n     * @param processDefinitionId\n     *        The identifier of the process definition for which an instance will be started.\n     * @param operations\n     *        The operations to execute to set the initial values of the data.\n     * @param context\n     *        The context in which operations are executed.\n     * @return An instance of the process.\n     * @throws ProcessDefinitionNotFoundException\n     *         If no matching process definition is found.\n     * @throws ProcessActivationException\n     *         If an exception occurs during activation.\n     * @throws ProcessExecutionException\n     *         If a problem occurs when starting the process.\n     * @since 6.0\n     */\n    ProcessInstance startProcess(long processDefinitionId, List<Operation> operations,\n            Map<String, Serializable> context)\n            throws ProcessDefinitionNotFoundException, ProcessActivationException, ProcessExecutionException;\n\n    /**\n     * Start an instance of the process with the specified process definition id on behalf of a given user. <b>Note</b>:\n     * If the process instantiation contract\n     * requires inputs, you must use {@link #startProcessWithInputs(long, long, Map)} instead.\n     *\n     * @param userId\n     *        The user id of the user.\n     * @param processDefinitionId\n     *        The identifier of the process definition for which an instance will be started.\n     * @return An instance of the process.\n     * @throws UserNotFoundException\n     *         If the given user does not exist.\n     * @throws ProcessDefinitionNotFoundException\n     *         If no matching process definition is found.\n     * @throws ProcessActivationException\n     *         If a problem occurs when starting the process.\n     * @throws ProcessExecutionException\n     *         If an execution problem occurs when starting the process.\n     * @since 6.0\n     */\n    ProcessInstance startProcess(long userId, long processDefinitionId)\n            throws UserNotFoundException, ProcessDefinitionNotFoundException,\n            ProcessActivationException, ProcessExecutionException;\n\n    /**\n     * Start an instance of the process with the specified process definition id on behalf of a given user, and set the\n     * initial values of the data with the\n     * given operations. <b>Note</b>: If the process instantiation contract requires inputs, you must use\n     * {@link #startProcessWithInputs(long, long, Map)}\n     * instead. The best practice is to design contract inputs in the process definition, and use these\n     * contract inputs in operations, instead of providing operations at start time.\n     *\n     * @param userId\n     *        The identifier of the user.\n     * @param processDefinitionId\n     *        The identifier of the process definition for which an instance will be started.\n     * @param operations\n     *        The operations to execute to set the initial values of the data.\n     * @param context\n     *        The context in which the operations are executed.\n     * @return An instance of the process.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws ProcessDefinitionNotFoundException\n     *         If no matching process definition is found.\n     * @throws ProcessActivationException\n     *         If an exception occurs during activation.\n     * @throws UserNotFoundException\n     *         If there is no user with the specified userId.\n     * @throws ProcessExecutionException\n     *         If a problem occurs when starting the process.\n     * @since 6.0\n     */\n    ProcessInstance startProcess(long userId, long processDefinitionId, List<Operation> operations,\n            Map<String, Serializable> context)\n            throws UserNotFoundException, ProcessDefinitionNotFoundException, ProcessActivationException,\n            ProcessExecutionException;\n\n    /**\n     * Start an instance of the process with the specified process definition id on behalf of a given user, and set the\n     * initial values of the data with the\n     * given initialVariables. <b>Note</b>: If the process instantiation contract requires inputs, you must use\n     * {@link #startProcessWithInputs(long, long, Map)}\n     * instead, using contract inputs stored in process variables, instead of using directly initialVariables.\n     *\n     * @param userId\n     *        The identifier of the user.\n     * @param processDefinitionId\n     *        The identifier of the process definition for which an instance will be started.\n     * @param initialVariables\n     *        The couples of initial variable/value\n     * @return An instance of the process.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws ProcessDefinitionNotFoundException\n     *         If no matching process definition is found.\n     * @throws ProcessActivationException\n     *         If an exception occurs during activation.\n     * @throws ProcessExecutionException\n     *         If a problem occurs when starting the process.\n     * @since 6.0\n     */\n    ProcessInstance startProcess(final long userId, final long processDefinitionId,\n            final Map<String, Serializable> initialVariables)\n            throws ProcessDefinitionNotFoundException, ProcessActivationException, ProcessExecutionException;\n\n    /**\n     * Start an instance of the process with the specified process definition id, and provides inputs to fulfill Process\n     * Contract.\n     * See {@link org.bonitasoft.engine.bpm.contract.ContractDefinition} for details on contracts.\n     *\n     * @param processDefinitionId\n     *        The identifier of the process definition for which an instance will be started.\n     * @param instantiationInputs\n     *        The couples of input name/value that allows to start a process with an instantiation contract.\n     * @return An instance of the process.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws ProcessDefinitionNotFoundException\n     *         If no matching process definition is found.\n     * @throws ProcessActivationException\n     *         If an exception occurs during activation.\n     * @throws ProcessExecutionException\n     *         If a problem occurs when starting the process.\n     * @throws ContractViolationException\n     *         If inputs don't fit with task contract\n     * @since 7.0.0\n     */\n    ProcessInstance startProcessWithInputs(final long processDefinitionId,\n            final Map<String, Serializable> instantiationInputs)\n            throws ProcessDefinitionNotFoundException, ProcessActivationException, ProcessExecutionException,\n            ContractViolationException;\n\n    /**\n     * Start an instance of the process with the specified process definition id on behalf of a given user, and provides\n     * inputs to fulfill Process Contract.\n     * See {@link org.bonitasoft.engine.bpm.contract.ContractDefinition} for details on contracts.\n     *\n     * @param userId The identifier of the user in the name of whom the process is started.\n     * @param processDefinitionId\n     *        The identifier of the process definition for which an instance will be started.\n     * @param instantiationInputs\n     *        The couples of input name/value that allows to start a process with an instantiation contract.\n     * @return An instance of the process.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws ProcessDefinitionNotFoundException\n     *         If no matching process definition is found.\n     * @throws ProcessActivationException\n     *         If an exception occurs during activation.\n     * @throws ProcessExecutionException\n     *         If a problem occurs when starting the process.\n     * @throws ContractViolationException\n     *         If inputs don't fit with process contract\n     * @since 7.0.0\n     */\n    ProcessInstance startProcessWithInputs(final long userId, final long processDefinitionId,\n            final Map<String, Serializable> instantiationInputs)\n            throws ProcessDefinitionNotFoundException, ProcessActivationException, ProcessExecutionException,\n            ContractViolationException;\n\n    /**\n     * <p>\n     * Executes a flow node that is in a stable state.\n     * Will move the activity to the next stable state and then continue the execution of the process.\n     * </p>\n     * <p>\n     * Use this method only if you need to manually control the execution of a flownode.\n     * <b>DO NOT USE THIS TO EXECUTE HUMAN TASKS.</b> Use {@link #executeUserTask(long, Map)} instead.\n     * </p>\n     *\n     * @param flownodeInstanceId\n     *        The identifier of the flow node to execute.\n     * @throws FlowNodeExecutionException\n     *         If an execution exception occurs.\n     * @since 6.0\n     */\n    void executeFlowNode(long flownodeInstanceId) throws FlowNodeExecutionException;\n\n    /**\n     * <p>\n     * Executes a flow node that is in a stable state on behalf of a given user\n     * Will make the flow node go in the next stable state and then continue the execution of the process\n     * If userId equals 0, the logged-in user is declared as the executer of the flow node.\n     * The user, who executed the flow node on behalf of a given user, is declared as a executer delegate.\n     * </p>\n     * <p>\n     * Use this method only if you need to manually control the execution of a flownode.\n     * <b>DO NOT USE THIS TO EXECUTE HUMAN TASKS.</b> Use {@link #executeUserTask(long, long, Map)} instead.\n     * </p>\n     *\n     * @param userId\n     *        The identifier of the user for which you want to execute the flow node\n     * @param flownodeInstanceId\n     *        The identifier of the flow node to execute\n     * @throws FlowNodeExecutionException\n     *         If an execution exception occurs\n     * @since 6.0.1\n     */\n    void executeFlowNode(long userId, long flownodeInstanceId) throws FlowNodeExecutionException;\n\n    /**\n     * Returns all activities (active and finished) of a process instance.\n     *\n     * @param processInstanceId\n     *        The identifier of the process instance,\n     * @param startIndex\n     *        The index of the first result (starting from 0).\n     * @param maxResults\n     *        The maximum number of results to get.\n     * @return The matching set of activity instances.\n     * @since 6.0\n     */\n    List<ActivityInstance> getActivities(long processInstanceId, int startIndex, int maxResults);\n\n    /**\n     * Get the specified process instance.\n     *\n     * @param processInstanceId\n     *        The identifier of the process instance.\n     * @return The matching instance of process.\n     * @throws ProcessInstanceNotFoundException\n     *         If there is no process instance with the specified identifier.\n     * @since 6.0\n     */\n    ProcessInstance getProcessInstance(long processInstanceId) throws ProcessInstanceNotFoundException;\n\n    /**\n     * Get the specified activity instance.\n     *\n     * @param activityInstanceId\n     *        The identifier of the activity instance.\n     * @return The matching activity instance.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws ActivityInstanceNotFoundException\n     *         If the activity cannot be found.\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If the activity instance cannot be retrieved.\n     * @since 6.0\n     */\n    ActivityInstance getActivityInstance(long activityInstanceId) throws ActivityInstanceNotFoundException;\n\n    /**\n     * Get a specified flow node instance.\n     *\n     * @param flowNodeInstanceId\n     *        The identifier of the flow node instance.\n     * @return The matching flow node instance.\n     * @throws FlowNodeInstanceNotFoundException\n     *         If the given flow node instance does not exist.\n     * @since 6.0\n     */\n    FlowNodeInstance getFlowNodeInstance(final long flowNodeInstanceId) throws FlowNodeInstanceNotFoundException;\n\n    /**\n     * Get an activity instance that is archived.\n     *\n     * @param sourceActivityInstanceId\n     *        The identifier of the source activity instance.\n     * @return The matching archived activity instance.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws ActivityInstanceNotFoundException\n     *         If the archived activity instance cannot be found.\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If the archived activity instance cannot be retrieved.\n     * @since 6.0\n     */\n    ArchivedActivityInstance getArchivedActivityInstance(long sourceActivityInstanceId)\n            throws ActivityInstanceNotFoundException;\n\n    /**\n     * Get the list of human task instances assigned to the specified user.\n     *\n     * @param userId\n     *        The identifier of the user.\n     * @param startIndex\n     *        The index of the first result (starting from 0).\n     * @param maxResults\n     *        The maximum number of elements to get per page.\n     * @param criterion\n     *        The sort criterion.\n     * @return The matching list of task instances.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Occurs when the session is invalid.\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If a task instance cannot be retrieved.\n     * @since 6.0\n     */\n    List<HumanTaskInstance> getAssignedHumanTaskInstances(long userId, int startIndex, int maxResults,\n            ActivityInstanceCriterion criterion);\n\n    /**\n     * Get the list of pending human task instances available to the specified user.\n     * A human task is pending for a given user if it is not yet assigned and if the\n     * user is a candidate either through an {@link org.bonitasoft.engine.bpm.actor.ActorMember} or through a\n     * {@link org.bonitasoft.engine.filter.UserFilter}.\n     *\n     * @param userId\n     *        The identifier of the user.\n     * @param startIndex\n     *        The index of the first result (starting from 0).\n     * @param maxResults\n     *        The maximum number of elements to get per page.\n     * @param pagingCriterion\n     *        The criterion for sorting the items over pages.\n     * @return The list of matching task instances.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Occurs when the session is invalid.\n     * @since 6.0\n     */\n    List<HumanTaskInstance> getPendingHumanTaskInstances(long userId, int startIndex, int maxResults,\n            ActivityInstanceCriterion pagingCriterion);\n\n    /**\n     * Count the total number of human task instances assigned to the specified user.\n     *\n     * @param userId\n     *        The identifier of a user.\n     * @return A number of human task instances assigned.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an error occurs while retrieving an instance of an activity.\n     * @since 6.0\n     */\n    long getNumberOfAssignedHumanTaskInstances(long userId);\n\n    /**\n     * For a specified list of users, get the number of pending tasks.\n     *\n     * @param userIds\n     *        A list of user identifiers.\n     * @return A map with userId as key and number of tasks as value.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         can't retrieve an instance of activity\n     * @since 6.0\n     */\n    Map<Long, Long> getNumberOfOpenTasks(List<Long> userIds);\n\n    /**\n     * Count the number of pending human task instances available to a specified user.\n     *\n     * @param userId\n     *        The identifier of a user.\n     * @return A number of pending human task instances.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an error occurs while retrieving an instance of an activity.\n     * @since 6.0\n     */\n    long getNumberOfPendingHumanTaskInstances(long userId);\n\n    /**\n     * Retrieve a human task instance by the corresponding activity instance id.\n     *\n     * @param activityInstanceId\n     *        The identifier of the activity instance.\n     * @return The matching instance of human task.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws ActivityInstanceNotFoundException\n     *         If the human task cannot be found.\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an error occurs while retrieving the instance of the activity.\n     * @since 6.0\n     */\n    HumanTaskInstance getHumanTaskInstance(long activityInstanceId) throws ActivityInstanceNotFoundException;\n\n    /**\n     * Get a list of event instances related to a process instance that match the specified conditions.\n     *\n     * @param rootContainerId\n     *        The identifier of the containing root process instance.\n     * @param startIndex\n     *        The index of the first result (starting from 0).\n     * @param maxResults\n     *        The maximum number of results to get.\n     * @param sortingType\n     *        The criterion for sorting event instances.\n     * @return The matching list of event instances.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @since 6.0\n     */\n    List<EventInstance> getEventInstances(long rootContainerId, int startIndex, int maxResults,\n            EventCriterion sortingType);\n\n    /**\n     * Assign a task to a user with given user identifier.\n     *\n     * @param userTaskId\n     *        The identifier of the user task.\n     * @param userId\n     *        The identifier of the user.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws UpdateException\n     *         If an error occurs while updating the activity instance.\n     * @since 6.0\n     */\n    void assignUserTask(long userTaskId, long userId) throws UpdateException;\n\n    /**\n     * Assign a task to a user with given user identifier if the task is not currently assigned to another user.\n     *\n     * @param userTaskId\n     *        The identifier of the user task.\n     * @param userId\n     *        The identifier of the user.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws UpdateException\n     *         If an error occurs while updating the activity instance.\n     * @since 7.6\n     */\n    void assignUserTaskIfNotAssigned(long userTaskId, long userId) throws UpdateException;\n\n    /**\n     * Updates the actors of the user task. It evaluates again the eligible users for that task.\n     *\n     * @param userTaskId\n     *        The identifier of the user task\n     * @throws UpdateException\n     *         If an exception occurs during the evaluation of actors.\n     * @since 6.1\n     */\n    void updateActorsOfUserTask(long userTaskId) throws UpdateException;\n\n    /**\n     * Returns all data of a process instance.\n     *\n     * @param processInstanceId\n     *        The identifier of the process instance.\n     * @param startIndex\n     *        The index of the page of results to get (starting from 0).\n     * @param maxResults\n     *        The maximum number of results to get.\n     * @return The matching list of dataInstances.\n     * @since 6.0\n     */\n    List<DataInstance> getProcessDataInstances(long processInstanceId, int startIndex, int maxResults);\n\n    /**\n     * Get the value of named data item from a specified process instance.\n     * The value is returned in a DataInstance object.\n     *\n     * @param dataName\n     *        The name of the data item.\n     * @param processInstanceId\n     *        The identifier of the process instance.\n     * @return An instance of the data\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws DataNotFoundException\n     *         If the specified data value cannot be found.\n     * @since 6.0\n     */\n    DataInstance getProcessDataInstance(String dataName, long processInstanceId) throws DataNotFoundException;\n\n    /**\n     * <p>\n     * Update the value of a named process variable of a specified process instance.\n     * </p>\n     * <p>\n     * <u>Warning</u>: there is a known limitation about the usage of this method: if the new data value is of a custom\n     * data type AND\n     * the call to this method is performed from a java client configured to access Bonita Engine through HTTP,\n     * the API method call fails because the class of the custom data type cannot be loaded yet.\n     * The workaround to this limitation is to put the jar file containing your custom data\n     * type classes into the classloader of Bonita ([BONITA_INSTALLATION_FOLDER]/server/lib/bonita folder in a standard\n     * installation),\n     * and REMOVE it from the libraries of all the processes that use them.\n     * </p>\n     * <p>\n     * If the call is made from a standard Bonita bundle, the calls to Engine APIs are performed locally (and not\n     * through HTTP),\n     * so it does NOT fall into this limitation. So in a standard Bonita installation, it is safe to call this\n     * method in code like\n     * Rest API Extensions, Groovy scripts, Custom connector implementations...\n     * </p>\n     *\n     * @param dataName\n     *        The name of the data item.\n     * @param processInstanceId\n     *        The identifier of the process instance.\n     * @param dataValue\n     *        The new value for the data item.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws UpdateException\n     *         If a problem occurs while updating the data value.\n     * @since 6.0\n     */\n    void updateProcessDataInstance(String dataName, long processInstanceId, Serializable dataValue)\n            throws UpdateException;\n\n    /**\n     * <p>\n     * Update the value of named process variables of a specified process instance.\n     * </p>\n     * <p>\n     * <u>Warning</u>: there is a known limitation about the usage of this method: if the new data value is of a custom\n     * data type AND\n     * the call to this method is performed from a java client configure to access Bonita Engine through HTTP,\n     * the API method call fails because the class of the custom data type cannot be loaded yet.\n     * The workaround to this limitation is to put the jar file containing your custom data\n     * type classes into the classloader of Bonita ([BONITA_INSTALLATION_FOLDER]/server/lib/bonita folder in a standard\n     * installation),\n     * and REMOVE it from the libraries of all the processes that use them.\n     * </p>\n     * <p>\n     * If the call is made from a standard Bonita bundle, the calls to Engine APIs are performed locally (and not\n     * through HTTP),\n     * so it does NOT fall into this limitation. So in a standard Bonita installation, it is safe to call this\n     * method in code like\n     * Rest API Extensions, Groovy scripts, Custom connector implementations...\n     * </p>\n     *\n     * @param processInstanceId\n     *        The identifier of the process instance.\n     * @param dataNameValues\n     *        The mapping between the data name and its value to update to.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws UpdateException\n     *         If a problem occurs while updating the data value.\n     * @since 6.2.3\n     */\n    void updateProcessDataInstances(final long processInstanceId, final Map<String, Serializable> dataNameValues)\n            throws UpdateException;\n\n    /**\n     * Get a list of the data instances from a specified activity instance.\n     *\n     * @param activityInstanceId\n     *        The identifier of the activity instance.\n     * @param startIndex\n     *        The index of the first result (starting at 0).\n     * @param maxResults\n     *        The maximum number of results to get.\n     * @return The list of matching DataInstances.\n     * @since 6.0\n     */\n    List<DataInstance> getActivityDataInstances(long activityInstanceId, int startIndex, int maxResults);\n\n    /**\n     * Get a named data instance from a specified activity instance.\n     *\n     * @param dataName\n     *        The name of the data item.\n     * @param activityInstanceId\n     *        The identifier of the activity instance.\n     * @return An instance of data.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws DataNotFoundException\n     *         If the specified data value cannot be found.\n     * @since 6.0\n     */\n    DataInstance getActivityDataInstance(String dataName, long activityInstanceId) throws DataNotFoundException;\n\n    /**\n     * <p>\n     * Update the value of a named activity variable of a specified activity instance.\n     * </p>\n     * <p>\n     * <u>Warning</u>: there is a known limitation about the usage of this method: if the new data value is of a custom\n     * data type AND\n     * the call to this method is performed from a java client configure to access Bonita Engine through HTTP,\n     * the API method call fails because the class of the custom data type cannot be loaded yet.\n     * The workaround to this limitation is to put the jar file containing your custom data\n     * type classes into the classloader of Bonita ([BONITA_INSTALLATION_FOLDER]/server/lib/bonita folder in a standard\n     * installation),\n     * and REMOVE it from the libraries of all the processes that use them.\n     * Or use {@link ProcessRuntimeAPI#updateActivityInstanceVariables(List, long, Map)} instead\n     * </p>\n     * <p>\n     * If the call is made from a standard Bonita bundle, the calls to Engine APIs are performed locally (and not\n     * through HTTP),\n     * so it does NOT fall into this limitation. So in a standard Bonita installation, it is safe to call this\n     * method in code like\n     * Rest API Extensions, Groovy scripts, Custom connector implementations...\n     * </p>\n     *\n     * @param dataName\n     *        The name of the data instance.\n     * @param activityInstanceId\n     *        The identifier of the activity instance.\n     * @param dataValue\n     *        The new value of the data to set.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws UpdateException\n     *         If an error occurs during the update.\n     * @since 6.0\n     */\n    void updateActivityDataInstance(String dataName, long activityInstanceId, Serializable dataValue)\n            throws UpdateException;\n\n    /**\n     * Update the value of a named transient data instance in a specified activity instance.\n     *\n     * @param dataName\n     *        The name of the data instance.\n     * @param activityInstanceId\n     *        The identifier of the activity instance.\n     * @param dataValue\n     *        The new value of the data to set.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws UpdateException\n     *         If an error occurs during the update.\n     * @since 6.0\n     */\n    void updateActivityTransientDataInstance(String dataName, long activityInstanceId, Serializable dataValue)\n            throws UpdateException;\n\n    /**\n     * Get a list of the transient data instances from a specified activity instance.\n     *\n     * @param activityInstanceId\n     *        The identifier of the activity instance.\n     * @param startIndex\n     *        The index of the first result (starting at 0).\n     * @param maxResults\n     *        The maximum number of results to get.\n     * @return The list of matching DataInstances.\n     * @since 6.0\n     */\n    List<DataInstance> getActivityTransientDataInstances(long activityInstanceId, int startIndex, int maxResults);\n\n    /**\n     * Get a named transient data instance from a specified activity instance.\n     *\n     * @param dataName\n     *        The name of the data item.\n     * @param activityInstanceId\n     *        The identifier of the activity instance.\n     * @return An instance of data.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws DataNotFoundException\n     *         If the specified data value cannot be found.\n     * @since 6.0\n     */\n    DataInstance getActivityTransientDataInstance(String dataName, long activityInstanceId)\n            throws DataNotFoundException;\n\n    /**\n     * Get the date when the specified activity instance reached the given state.\n     *\n     * @param activityInstanceId\n     *        The identifier of the activity instance.\n     * @param state\n     *        The state of interest.\n     * @return The date at which the activity instance reached the state.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an error occurs while retrieving the activity instance.\n     * @since 6.0\n     */\n    Date getActivityReachedStateDate(long activityInstanceId, String state);\n\n    /**\n     * <p>\n     * Update the value of named activity variables of a specified activity instance.\n     * </p>\n     * <p>\n     * <u>Warning</u>: there is a known limitation about the usage of this method: if the new data value is of a custom\n     * data type AND\n     * the call to this method is performed from a java client configure to access Bonita Engine through HTTP,\n     * the API method call fails because the class of the custom data type cannot be loaded yet.\n     * The workaround to this limitation is to put the jar file containing your custom data\n     * type classes into the classloader of Bonita ([BONITA_INSTALLATION_FOLDER]/server/lib/bonita folder in a standard\n     * installation),\n     * and REMOVE it from the libraries of all the processes that use them.\n     * Or use {@link ProcessRuntimeAPI#updateActivityInstanceVariables(List, long, Map)} instead\n     * </p>\n     * <p>\n     * If the call is made from a standard Bonita bundle, the calls to Engine APIs are performed locally (and not\n     * through HTTP),\n     * so it does NOT fall into this limitation. So in a standard Bonita installation, it is safe to call this\n     * method in code like Rest API Extensions, Groovy scripts, Custom connector implementations...\n     * </p>\n     *\n     * @param activityInstanceId\n     *        The activity identifier.\n     * @param variables\n     *        A map which contains several pairs of variable name and value.\n     * @throws UpdateException\n     *         If a problem occurs while updating one of the data instance value.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @since 6.0\n     */\n    void updateActivityInstanceVariables(long activityInstanceId, Map<String, Serializable> variables)\n            throws UpdateException;\n\n    /**\n     * Update the values of variables in an activity instance using expressions.\n     *\n     * @param operations\n     *        A sequence of operations on expressions that update the values variables.\n     * @param activityInstanceId\n     *        The identifier of the activity instance.\n     * @param expressionContexts\n     *        Store all information identifying the container that the data belongs to.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws UpdateException\n     *         If an error occurs during the update.\n     * @since 6.0\n     */\n    void updateActivityInstanceVariables(List<Operation> operations, long activityInstanceId,\n            Map<String, Serializable> expressionContexts) throws UpdateException;\n\n    /**\n     * Update the due date of a task.\n     *\n     * @param userTaskId\n     *        The identifier of the task to update.\n     * @param dueDate\n     *        The new due date for the task.\n     * @throws UpdateException\n     *         If the activity does not exist or the update cannot be fulfilled.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @since 6.0\n     */\n    void updateDueDateOfTask(long userTaskId, Date dueDate) throws UpdateException;\n\n    /**\n     * Get an instance of a task asssigned to a given user for the specified process instance.\n     *\n     * @param processInstanceId\n     *        The identifier of the process instance.\n     * @param userId\n     *        The identifier of the user.\n     * @return The identifier of a user task from the process instance that is assigned to the user.\n     * @throws ProcessInstanceNotFoundException\n     *         If the given process instance does not exist.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws UserNotFoundException\n     *         If there is no user with the specified id.\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an error occurs happen while retrieving the activity instance.\n     * @since 6.0\n     */\n    long getOneAssignedUserTaskInstanceOfProcessInstance(long processInstanceId, long userId)\n            throws ProcessInstanceNotFoundException, UserNotFoundException;\n\n    /**\n     * Get an instance of a task asssigned to a given user for the specified process definition.\n     *\n     * @param processDefinitionId\n     *        The identifier of the process definition.\n     * @param userId\n     *        The identifier of a user.\n     * @return The identifier of a user task from the process definition that is assigned to the user.\n     * @throws ProcessDefinitionNotFoundException\n     *         If the given process definition does not exist.\n     * @throws UserNotFoundException\n     *         If the given user does not exist.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an error occurs happen while retrieving the activity instance.\n     * @since 6.0\n     */\n    long getOneAssignedUserTaskInstanceOfProcessDefinition(long processDefinitionId, long userId)\n            throws ProcessDefinitionNotFoundException,\n            UserNotFoundException;\n\n    /**\n     * Get the state of a specified activity instance.\n     *\n     * @param activityInstanceId\n     *        The identifier of the activity instance.\n     * @return The state of the activity instance.\n     * @throws ActivityInstanceNotFoundException\n     *         If the activity cannot be found.\n     * @since 6.0\n     */\n    String getActivityInstanceState(long activityInstanceId) throws ActivityInstanceNotFoundException;\n\n    /**\n     * Check whether a specified task can be executed by a given user.\n     *\n     * @param activityInstanceId\n     *        The identifier of the activity instance.\n     * @param userId\n     *        The identifier of a user.\n     * @return A flag that indicates whether task can be executed by the user.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws ActivityInstanceNotFoundException\n     *         If the activity cannot be found.\n     * @throws UserNotFoundException\n     *         If there is no user with the specified userId.\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an error occurs happen while retrieving the activity instance.\n     * @since 6.0\n     */\n    boolean canExecuteTask(long activityInstanceId, long userId)\n            throws ActivityInstanceNotFoundException, UserNotFoundException;\n\n    /**\n     * Release a task (unclaim or unassign). After the operation, the task is in the pending task list.\n     *\n     * @param userTaskId\n     *        The identifier of the user task.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws ActivityInstanceNotFoundException\n     *         If the activity cannot be found.\n     * @throws UpdateException\n     *         If a problem occurs while release (un-assigning) the user task.\n     * @since 6.0\n     */\n    void releaseUserTask(long userTaskId) throws ActivityInstanceNotFoundException, UpdateException;\n\n    /**\n     * List the archived process instances for the specified process instance.\n     * A process instance is archived when it changes state, so there are several archived process instances for each\n     * process instance.\n     *\n     * @param processInstanceId\n     *        The identifier of the process instance.\n     * @param startIndex\n     *        The index of the page of results to get.\n     * @param maxResults\n     *        The maximum number of results to get.\n     * @return The list of archived process instances.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If no current valid session is found.\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If the search fails because an archived process instance cannot be read.\n     * @since 6.0\n     */\n    List<ArchivedProcessInstance> getArchivedProcessInstances(long processInstanceId, int startIndex, int maxResults);\n\n    /**\n     * Get the last archived instance of a process instance.\n     * A process instance is archived when it changes state, so there are several archived process instances for each\n     * process instance.\n     * The last archived instance is returned.\n     *\n     * @param sourceProcessInstanceId\n     *        The identifier of the source process instance, i.e. not an archived version, the original process instance\n     *        id.\n     * @return The archived process instance.\n     * @throws ArchivedProcessInstanceNotFoundException\n     *         If no archived process instance can be found with the provided Id.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If no current valid session is found.\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If the search fails because an archived process instance cannot be read.\n     * @since 6.0\n     */\n    ArchivedProcessInstance getFinalArchivedProcessInstance(long sourceProcessInstanceId)\n            throws ArchivedProcessInstanceNotFoundException;\n\n    /**\n     * Set the state of an activity instance.\n     *\n     * @param activityInstanceId\n     *        The identifier of the activity instance.\n     * @param stateId\n     *        The identifier of the required state.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If no current valid session is found.\n     * @throws UpdateException\n     *         If an error occurs during the update.\n     * @since 6.0\n     */\n    void setActivityStateById(long activityInstanceId, int stateId) throws UpdateException;\n\n    /**\n     * Set the state of an activity instance.\n     *\n     * @param activityInstanceId\n     *        The identifier of the activity instance.\n     * @param state\n     *        The name of the required state.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If no current valid session is found.\n     * @throws UpdateException\n     *         If an error occurs during the update.\n     * @since 6.0\n     */\n    void setActivityStateByName(long activityInstanceId, String state) throws UpdateException;\n\n    /**\n     * Set a state of a process instance.\n     *\n     * @param processInstance\n     *        The process instance.\n     * @param state\n     *        The name of the required state.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws UpdateException\n     *         If an error occurs during the update.\n     * @since 6.0\n     */\n    void setProcessInstanceState(ProcessInstance processInstance, String state) throws UpdateException;\n\n    /**\n     * Set the priority of a user task.\n     *\n     * @param userTaskInstanceId\n     *        The identifier of user task instance.\n     * @param priority\n     *        The new priority of this task.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws UpdateException\n     *         If an error occurs during the update.\n     * @since 6.0\n     */\n    void setTaskPriority(long userTaskInstanceId, TaskPriority priority) throws UpdateException;\n\n    /**\n     * Execute a connector in a specified processDefinition.\n     *\n     * @param connectorDefinitionId\n     *        The identifier of connector definition.\n     * @param connectorDefinitionVersion\n     *        The version of the connector definition.\n     * @param connectorInputParameters\n     *        The expressions related to the connector input paramters.\n     * @param inputValues\n     *        The parameters values for expression needed when evaluating the connector.\n     * @param processDefinitionId\n     *        The identifier of the process definition.\n     * @return A map with connector parameter names and parameter value objects.\n     * @throws ConnectorExecutionException\n     *         If an error occurs during connector execution.\n     * @throws ConnectorNotFoundException\n     *         If there is no connector definition with the specified identifier or version.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @since 6.0\n     */\n    Map<String, Serializable> executeConnectorOnProcessDefinition(String connectorDefinitionId,\n            String connectorDefinitionVersion,\n            Map<String, Expression> connectorInputParameters, Map<String, Map<String, Serializable>> inputValues,\n            long processDefinitionId)\n            throws ConnectorExecutionException, ConnectorNotFoundException;\n\n    /**\n     * Execute a connector in a specified processDefinition with operations.\n     *\n     * @param connectorDefinitionId\n     *        The identifier of connector definition.\n     * @param connectorDefinitionVersion\n     *        The version of the connector definition.\n     * @param connectorInputParameters\n     *        The expressions related to the connector input parameters.\n     * @param inputValues\n     *        The parameters values for expression needed when evaluating the connector.\n     * @param operations\n     *        The operations used when executing the connector.\n     * @param operationInputValues\n     *        The input values for the operations.\n     * @param processDefinitionId\n     *        The identifier of the process definition.\n     * @return A map with connector parameter names and parameter value objects after operations and connector\n     *         execution.\n     * @throws ConnectorExecutionException\n     *         If an error occurs during connector execution.\n     * @throws ConnectorNotFoundException\n     *         If there is no connector definition with the specified identifier or version.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @since 6.0\n     */\n    Map<String, Serializable> executeConnectorOnProcessDefinition(String connectorDefinitionId,\n            String connectorDefinitionVersion,\n            Map<String, Expression> connectorInputParameters, Map<String, Map<String, Serializable>> inputValues,\n            List<Operation> operations,\n            Map<String, Serializable> operationInputValues, long processDefinitionId)\n            throws ConnectorExecutionException, ConnectorNotFoundException;\n\n    /**\n     * Search the archived human tasks for tasks that match the search options.\n     *\n     * @param searchOptions\n     *        The search conditions and the options for sorting and paging the results. See\n     *        {@link org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor} for valid\n     *        fields\n     *        for searching and sorting.\n     * @return The archived human tasks that match the search conditions.\n     * @throws SearchException\n     *         If the search could not be completed correctly.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @since 6.0\n     */\n    SearchResult<ArchivedHumanTaskInstance> searchArchivedHumanTasks(SearchOptions searchOptions)\n            throws SearchException;\n\n    /**\n     * Search the assigned human tasks for tasks that match the search options and are administered by the specified\n     * user.\n     *\n     * @param managerUserId\n     *        The identifier of the user.\n     * @param searchOptions\n     *        The search conditions and the options for sorting and paging the results. See\n     *        {@link org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor} for valid fields for\n     *        searching and sorting.\n     * @return The assigned human tasks that match the search conditions and are supervised by the user.\n     * @throws SearchException\n     *         If there is an error in the search conditions.\n     * @since 6.0\n     */\n    SearchResult<HumanTaskInstance> searchAssignedTasksManagedBy(long managerUserId, SearchOptions searchOptions)\n            throws SearchException;\n\n    /**\n     * Search the pending human tasks for tasks that match the search options and are supervised by the specified user.\n     *\n     * @param userId\n     *        The identifier of the user.\n     * @param searchOptions\n     *        The search conditions and the options for sorting and paging the results. See\n     *        {@link org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor} for valid fields for\n     *        searching and sorting.\n     * @return The pending human tasks that match the search conditions and are supervised by the user.\n     * @throws SearchException\n     *         If there is an error in the search conditions.\n     * @since 6.0\n     */\n    SearchResult<HumanTaskInstance> searchPendingTasksSupervisedBy(long userId, SearchOptions searchOptions)\n            throws SearchException;\n\n    /**\n     * Search the pending human tasks for tasks available to the specified user.\n     *\n     * @param userId\n     *        The identifier of the user.\n     * @param searchOptions\n     *        The search conditions and the options for sorting and paging the results. See\n     *        {@link org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor} for valid fields for\n     *        searching and sorting.\n     * @return The pending human tasks that match the search conditions and are available to the user.\n     * @throws SearchException\n     *         If there is an error in the search conditions.\n     * @since 6.0\n     */\n    SearchResult<HumanTaskInstance> searchPendingTasksForUser(long userId, SearchOptions searchOptions)\n            throws SearchException;\n\n    /**\n     * Search the pending human tasks assigned to a specified user.\n     *\n     * @param userId\n     *        The identifier of the user.\n     * @param searchOptions\n     *        The search conditions and the options for sorting and paging the results. See\n     *        {@link org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor} for valid fields for\n     *        searching and sorting.\n     * @return The pending human tasks that match the search conditions and are assigned to the user.\n     * @throws SearchException\n     *         If there is an error in the search conditions.\n     * @since 7.5.5\n     */\n    SearchResult<HumanTaskInstance> searchPendingTasksAssignedToUser(long userId, SearchOptions searchOptions)\n            throws SearchException;\n\n    /**\n     * Search the pending human tasks for tasks that match the search options and are managed by the specified user.\n     *\n     * @param managerUserId\n     *        The identifier of the user.\n     * @param searchOptions\n     *        The search conditions and the options for sorting and paging the results. See\n     *        {@link org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor} for valid fields for\n     *        searching and sorting.\n     * @return The pending human tasks that match the search conditions and are managed by the user.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws SearchException\n     *         If there is an error in the search conditions.\n     * @since 6.0\n     */\n    SearchResult<HumanTaskInstance> searchPendingTasksManagedBy(long managerUserId, SearchOptions searchOptions)\n            throws SearchException;\n\n    /**\n     * Search the assigned and pending human tasks for the specified user, on the specified root process definition,\n     * corresponding to the options.\n     *\n     * @param rootProcessDefinitionId\n     *        The identifier of the root process definition\n     * @param userId\n     *        The identifier of the user\n     * @param searchOptions\n     *        The search conditions and the options for sorting and paging the results. See\n     *        {@link org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor} for valid fields for\n     *        searching and sorting.\n     * @return The assigned and pending human tasks\n     * @throws SearchException\n     *         If there is an error in the search conditions.\n     * @since 6.3.3\n     */\n    SearchResult<HumanTaskInstance> searchAssignedAndPendingHumanTasksFor(final long rootProcessDefinitionId,\n            final long userId,\n            final SearchOptions searchOptions) throws SearchException;\n\n    /**\n     * Search the assigned and pending human tasks for any user, on the specified root process definition, corresponding\n     * to the options.\n     *\n     * @param rootProcessDefinitionId\n     *        The identifier of the root process definition\n     * @param searchOptions\n     *        The search conditions and the options for sorting and paging the results. See\n     *        {@link org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor} for valid fields for\n     *        searching and sorting.\n     * @return The assigned and pending human tasks\n     * @throws SearchException\n     *         If there is an error in the search conditions.\n     * @since 6.3.3\n     */\n    SearchResult<HumanTaskInstance> searchAssignedAndPendingHumanTasks(final long rootProcessDefinitionId,\n            final SearchOptions searchOptions)\n            throws SearchException;\n\n    /**\n     * Search the assigned and pending human tasks for any user corresponding to the options.\n     *\n     * @param searchOptions The search conditions and the options for sorting and paging the results. See\n     *        {@link org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor} for valid fields for\n     *        searching and sorting.\n     * @return The assigned and pending human tasks\n     * @throws SearchException If there is an error in the search conditions.\n     * @since 7.6.1\n     */\n    SearchResult<HumanTaskInstance> searchAssignedAndPendingHumanTasks(final SearchOptions searchOptions)\n            throws SearchException;\n\n    /**\n     * Get the number of assigned and pending overdue tasks for the specified users.\n     *\n     * @param userIds\n     *        A list of user identifiers.\n     * @return A map of user identifiers and numbers of overdue tasks.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @since 6.0\n     */\n    Map<Long, Long> getNumberOfOverdueOpenTasks(List<Long> userIds);\n\n    /**\n     * Cancels the process instance and all of its active flow nodes.\n     *\n     * @param processInstanceId\n     *        The identifier of the root process instance.\n     * @throws ProcessInstanceNotFoundException\n     *         If the process instance identifier does not refer to a process instance.\n     * @throws UpdateException\n     *         If an exception occurs during the process instance canceling.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    void cancelProcessInstance(long processInstanceId) throws ProcessInstanceNotFoundException, UpdateException;\n\n    /**\n     * Reset the state of a failed {@link org.bonitasoft.engine.bpm.flownode.FlowNodeInstance} to its previous state and\n     * then execute it. Pre-condition: the\n     * {@code FlowNodeInstance} must be in state FAILED. If this condition is not respected, a\n     * ActivityExecutionException is thrown.\n     * <p>If the {@code FlowNodeInstance} contains failed\n     * {@link org.bonitasoft.engine.bpm.connector.ConnectorInstance}s, they will be re-executed. In the case\n     * where the connector execution fails again, the {@code FlowNodeInstance} will remain in failed state. There is not\n     * counter on the number of\n     * re-executions</p>\n     *\n     * @param activityInstanceId\n     *        The identifier of the {@code FlowNodeInstance} to be re-retried.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         when session is invalid, e.g. the session has expired.\n     * @throws ActivityInstanceNotFoundException\n     *         when no {@code FlowNodeInstance} is found with the specified identifier.\n     * @throws ActivityExecutionException\n     *         occurs if the current Flownode is not in FAILED state, or while resetting the state, or while executing\n     *         the {@code FlowNodeInstance}.\n     * @since 6.0\n     * @see org.bonitasoft.engine.bpm.flownode.FlowNodeInstance\n     * @see org.bonitasoft.engine.bpm.connector.ConnectorInstance\n     */\n    void retryTask(long activityInstanceId) throws ActivityInstanceNotFoundException, ActivityExecutionException;\n\n    /**\n     * When a matching BPM event couple messageInstance / waitingMessageEvent fails to execute and no failure handling\n     * has been successful, a log message\n     * indicates that this method can be called to try again the execution. The necessary parameters are also indicated.\n     *\n     * @param messageInstanceId the ID of the message instance to try to trigger again.\n     * @param waitingMessageEventId the ID of the waiting message event to try to trigger again.\n     * @throws ExecutionException if the execution failed. A more precise cause is given in the getCause() method.\n     */\n    void executeMessageCouple(long messageInstanceId, long waitingMessageEventId) throws ExecutionException;\n\n    /**\n     * Evaluate an expression in the context of the specified process.\n     * Some context values can also be provided\n     *\n     * @param expression\n     *        The expression to evaluate.\n     * @param context\n     *        The context values that are provided for evaluating the expression.\n     * @param processDefinitionId\n     *        The identifier of the process definition in which the expression is evaluated.\n     * @return The result of the evaluation.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If there is no current valid session.\n     * @throws ExpressionEvaluationException\n     *         If an error occurs while evaluating the expression.\n     * @since 6.0\n     */\n    Serializable evaluateExpressionOnProcessDefinition(Expression expression, Map<String, Serializable> context,\n            long processDefinitionId)\n            throws ExpressionEvaluationException;\n\n    /**\n     * Get the number of comments matching the search conditions.\n     *\n     * @param searchOptions\n     *        The search conditions and the options for sorting and paging the results.\n     * @return The number of comments matching the search conditions.\n     * @throws SearchException\n     *         If the search could not be completed correctly.\n     * @since 6.0\n     */\n    long countComments(SearchOptions searchOptions) throws SearchException;\n\n    /**\n     * Get the number of attachments matching the search conditions.\n     *\n     * @param searchOptions\n     *        The search conditions and the options for sorting and paging the results.\n     * @return The number of attachments matching the search conditions.\n     * @throws SearchException\n     *         If the search could not be completed correctly.\n     * @since 6.0\n     */\n\n    long countAttachments(SearchOptions searchOptions) throws SearchException;\n\n    /**\n     * Send a BPMN signal event. Invoking this method acts as executing a Throw Signal Event.\n     *\n     * @param signalName\n     *        The signal name.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If there is no current valid session.\n     * @throws SendEventException\n     *         If an exception occurs while sending signal.\n     * @since 6.0\n     */\n    void sendSignal(String signalName) throws SendEventException;\n\n    /**\n     * Send a BPMN message event. Invoking this method acts as executing a Throw Message Event.\n     *\n     * @param messageName\n     *        The message name.\n     * @param targetProcess\n     *        An expression representing the target process name.\n     * @param targetFlowNode\n     *        An expression representing the target flow node name. Optional parameter that can be used to explicitly\n     *        set the target flow node if the\n     *        message is caught by different flow nodes in the same process. If not necessary, pass null value.\n     * @param messageContent\n     *        A key-&gt;value map containing the message data, with the data name as key.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If there is no current valid session.\n     * @throws SendEventException\n     *         If an exception occurs while sending message.\n     * @since 6.0\n     */\n    void sendMessage(String messageName, Expression targetProcess, Expression targetFlowNode,\n            Map<Expression, Expression> messageContent)\n            throws SendEventException;\n\n    /**\n     * Send a BPMN message event, with message correlation. Invoking this method acts as executing a Throw Message\n     * Event.\n     *\n     * @param messageName\n     *        The message name.\n     * @param targetProcess\n     *        An expression representing the target process name.\n     * @param targetFlowNode\n     *        An expression representing the target flow node name. Optional parameter that can be used to explicitly\n     *        set the target flow node if the\n     *        message is caught by different flow nodes in the same process. If not necessary, pass null value.\n     * @param messageContent\n     *        A key-&gt;value map containing the message data, with the data name as key.\n     * @param correlations\n     *        The message correlations (five maximum).\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If there is no current valid session.\n     * @throws SendEventException\n     *         If there are too many correlations (more than 5) or an exception occurs while sending message.\n     * @since 6.0\n     */\n    void sendMessage(String messageName, Expression targetProcess, Expression targetFlowNode,\n            Map<Expression, Expression> messageContent,\n            Map<Expression, Expression> correlations) throws SendEventException;\n\n    /**\n     * Delete messages until date and matching to search condition.\n     *\n     * @param creationDate\n     *        The date until the messages will be deleted.\n     * @param searchOptions\n     *        The optional search conditions for retrieve messages that will be removed.\n     * @return The number of message deleted\n     * @throws ExecutionException\n     *         If the deletion could not be completed correctly.\n     * @since 7.10\n     */\n\n    int deleteMessageByCreationDate(long creationDate, SearchOptions searchOptions) throws ExecutionException;\n\n    /**\n     * Retrieve an <code>ArchivedProcessInstance</code> specified by its identifier.\n     *\n     * @param archivedProcessInstanceId\n     *        The identifier of the <code>ArchivedProcessInstance</code> to be retrieved.\n     * @return The <code>ArchivedProcessInstance</code> instance.\n     * @throws ArchivedProcessInstanceNotFoundException\n     *         If the <code>ArchivedProcessInstance</code> was not found.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an error occurs while trying to retrieve the <code>ArchivedProcessInstance</code>.\n     * @since 6.0\n     */\n    ArchivedProcessInstance getArchivedProcessInstance(long archivedProcessInstanceId)\n            throws ArchivedProcessInstanceNotFoundException;\n\n    /**\n     * Retrieve an <code>ArchivedFlowNodeInstance</code> specified by its identifier.\n     *\n     * @param archivedFlowNodeInstanceId\n     *        The identifier of the <code>ArchivedFlowNodeInstance</code> to be retrieved.\n     * @return The <code>ArchivedFlowNodeInstance</code> instance.\n     * @throws ArchivedFlowNodeInstanceNotFoundException\n     *         If the <code>ArchivedFlowNodeInstance</code> was not found.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an error occurs while trying to retrieve the <code>ArchivedFlowNodeInstance</code>.\n     * @since 6.0\n     */\n    ArchivedFlowNodeInstance getArchivedFlowNodeInstance(long archivedFlowNodeInstanceId)\n            throws ArchivedFlowNodeInstanceNotFoundException;\n\n    /**\n     * Retrieve an <code>ArchivedComment</code> specified by its identifier.\n     *\n     * @param archivedCommentId\n     *        The identifier of the <code>ArchivedComment</code> to be retrieved.\n     * @return The <code>ArchivedComment</code> instance.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an error occurs while trying to retrieve the <code>ArchivedComment</code>.\n     * @throws NotFoundException\n     *         If no <code>ArchivedComment</code> was found with the specified archivedCommentId.\n     * @since 6.0\n     */\n    ArchivedComment getArchivedComment(long archivedCommentId) throws NotFoundException;\n\n    /**\n     * Search for connector instances.\n     *\n     * @param searchOptions\n     *        The search conditions and the options for sorting and paging the results. See\n     *        {@link org.bonitasoft.engine.bpm.connector.ConnectorInstancesSearchDescriptor} for valid fields for\n     *        searching and sorting.\n     * @return The {@link SearchResult} containing the <code>ConnectorInstance</code>s matching the search options.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws SearchException\n     *         If the search could not be completed correctly.\n     * @since 6.0\n     */\n    SearchResult<ConnectorInstance> searchConnectorInstances(SearchOptions searchOptions) throws SearchException;\n\n    /**\n     * Search for archived connector instances.\n     *\n     * @param searchOptions\n     *        The search options parameters. See\n     *        {@link org.bonitasoft.engine.bpm.connector.ArchiveConnectorInstancesSearchDescriptor} for valid fields for\n     *        searching and sorting.\n     * @return The {@link SearchResult} containing the <code>ArchivedConnectorInstance</code>s matching the search\n     *         options.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws SearchException\n     *         If the search could not be completed correctly.\n     * @since 6.0\n     */\n    SearchResult<ArchivedConnectorInstance> searchArchivedConnectorInstances(SearchOptions searchOptions)\n            throws SearchException;\n\n    /**\n     * List the named human tasks belonging to the specified process instance.\n     *\n     * @param rootProcessInstanceId\n     *        The identifier of the root process instance.\n     * @param taskName\n     *        The name of the required human tasks.\n     * @param startIndex\n     *        The result start index (strating from 0).\n     * @param maxResults\n     *        The maximum number of results to retrieve.\n     * @return The list of matching human task instances.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @since 6.0\n     */\n    List<HumanTaskInstance> getHumanTaskInstances(long rootProcessInstanceId, String taskName, int startIndex,\n            int maxResults);\n\n    /**\n     * Return the last created human task instance with the specified name for the given process instance.\n     *\n     * @param rootProcessInstanceId\n     *        The identifier of the root process instance.\n     * @param taskName\n     *        The name of the required human task.\n     * @return A HumanTaskInstance, in its latest state.\n     * @throws NotFoundException\n     *         If no current task with provided name is found.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @since 6.0\n     */\n    HumanTaskInstance getLastStateHumanTaskInstance(long rootProcessInstanceId, String taskName)\n            throws NotFoundException;\n\n    /**\n     * Search for archived activity instances in terminal states. Archived activity instances in intermediate states are\n     * not considered.\n     *\n     * @param searchOptions\n     *        The criterion used to search for archived activity instances. See\n     *        {@link org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstanceSearchDescriptor} for valid fields for\n     *        searching and sorting.\n     * @return A {@link SearchResult} containing the search result.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws SearchException\n     *         If an exception occurs during the search.\n     * @since 6.0\n     */\n    SearchResult<ArchivedActivityInstance> searchArchivedActivities(SearchOptions searchOptions) throws SearchException;\n\n    /**\n     * Search for activity instances.\n     *\n     * @param searchOptions\n     *        The criterion used to search for activity instances. See\n     *        {@link org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor} for valid\n     *        fields for searching and sorting.\n     * @return A {@link SearchResult} containing the search result.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws SearchException\n     *         If the search could not be completed correctly.\n     * @since 6.0\n     */\n    SearchResult<ActivityInstance> searchActivities(SearchOptions searchOptions) throws SearchException;\n\n    /**\n     * Search for flow node instances (activities, gateways and events).\n     *\n     * @param searchOptions\n     *        The criterion used to search for flow node instances. See\n     *        {@link org.bonitasoft.engine.bpm.flownode.FlowNodeInstanceSearchDescriptor} for valid\n     *        fields for searching and sorting.\n     * @return A {@link SearchResult} containing the search result\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the ession is invalid, e.g session has expired.\n     * @throws SearchException\n     *         If an exception occurs during the search.\n     * @since 6.0\n     */\n    SearchResult<FlowNodeInstance> searchFlowNodeInstances(SearchOptions searchOptions) throws SearchException;\n\n    /**\n     * Search for archived flow node instances (activities, gateways and events)\n     *\n     * @param searchOptions\n     *        The options used to search for flow node instances. See\n     *        {@link org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstanceSearchDescriptor} for\n     *        valid fields for searching and sorting.\n     * @return A {@link SearchResult} containing the search result.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g session has expired.\n     * @throws SearchException\n     *         If an exception occurs during the search.\n     * @see ArchivedFlowNodeInstance\n     * @since 6.0\n     */\n    SearchResult<ArchivedFlowNodeInstance> searchArchivedFlowNodeInstances(SearchOptions searchOptions)\n            throws SearchException;\n\n    /**\n     * Search for all tasks available to a specified user.\n     * A task is available to a user if is assigned to the user or it is pending for that user.\n     *\n     * @param userId\n     *        The identifier of the user for whom the tasks are available.\n     * @param searchOptions\n     *        The options used to search for tasks. See\n     *        {@link org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor} for valid fields for\n     *        searching and sorting.\n     * @return The list of tasks matching the search options.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the current session is invalid.\n     * @throws SearchException\n     *         If an exception occurs during the search.\n     * @since 6.0\n     */\n    SearchResult<HumanTaskInstance> searchMyAvailableHumanTasks(long userId, SearchOptions searchOptions)\n            throws SearchException;\n\n    /**\n     * Search for all tasks assigned to the user or taken by others users, or pending for that user.\n     *\n     * @param userId\n     *        The identifier of the user for whom the tasks are available.\n     * @param searchOptions\n     *        The options used to search for tasks. See\n     *        {@link org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor} for valid fields for\n     *        searching and sorting.\n     * @return The list of tasks matching the search options.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the current session is invalid.\n     * @throws SearchException\n     *         If an exception occurs during the search.\n     * @since 7.15\n     */\n    SearchResult<HumanTaskInstance> searchPendingOrAssignedToUserOrAssignedToOthersTasks(long userId,\n            SearchOptions searchOptions)\n            throws SearchException;\n\n    /**\n     * Search for comments related to the specified process instance.\n     *\n     * @param searchOptions\n     *        The options used to search for comments. See\n     *        {@link org.bonitasoft.engine.bpm.comment.SearchCommentsDescriptor} for valid fields for searching\n     *        and sorting.\n     * @return The matching comments.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws SearchException\n     *         If an exception occurs during the search.\n     * @since 6.0\n     */\n    SearchResult<Comment> searchComments(SearchOptions searchOptions) throws SearchException;\n\n    /**\n     * Add a comment on a process instance. Be aware that operations inside tasks are executed by the System, not the\n     * task assignee.\n     * Therefore, when called from an operation inside a task, the user \"System\" will be marked as having made the\n     * comment. If you wish to add a comment made by the assigned user in those places,\n     * use addProcessCommentOnBehalfOfUser.\n     *\n     * @param processInstanceId\n     *        The identifier of the process instance.\n     * @param comment\n     *        The content of the comment.\n     * @return The newly created comment.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws CreationException\n     *         If the parameter processInstanceId does not refer to any active process instance (existing and\n     *         non-archived).\n     * @since 6.1\n     */\n    Comment addProcessComment(final long processInstanceId, final String comment) throws CreationException;\n\n    /**\n     * Add a comment on a process instance, on behalf of an user. Mainly intended to be used in groovy scripts when the\n     * behavior of the addProcessComment might be unsatisfactory.\n     *\n     * @param processInstanceId\n     *        The identifier of the process instance.\n     * @param comment\n     *        The content of the comment.\n     * @param userId\n     *        The user that will be said to have made the comment\n     * @return The newly created comment.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws CreationException\n     *         If the parameter processInstanceId does not refer to any active process instance (existing and\n     *         non-archived).\n     * @since 7.7\n     */\n    Comment addProcessCommentOnBehalfOfUser(final long processInstanceId, final String comment, long userId)\n            throws CreationException;\n\n    /**\n     * Get all the comments managed by the specified user.\n     * A comment is considered to be managed by user A if one or more of the following conditions is true:\n     * - the author of the comment is a subordinate of user A (A the author's manager).\n     * - the comment belongs to a process started by a subordinate of user A.\n     * - the comment belongs to a process where at least one human task is assigned to a subordinate of user A.\n     *\n     * @param managerUserId\n     *        The identifier of the user.\n     * @param searchOptions\n     *        The options used to search for comments. See\n     *        {@link org.bonitasoft.engine.bpm.comment.SearchCommentsDescriptor} for valid fields for searching and\n     *        sorting.\n     * @return The comments managed by the user that match the search options.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws SearchException\n     *         If the search could not be completed correctly.\n     * @since 6.0\n     */\n    SearchResult<Comment> searchCommentsManagedBy(long managerUserId, SearchOptions searchOptions)\n            throws SearchException;\n\n    /**\n     * Get the comments on process instances that the specified user can access.\n     *\n     * @param userId\n     *        The identifier of the user.\n     * @param searchOptions\n     *        The options used to search for comments. See\n     *        {@link org.bonitasoft.engine.bpm.comment.SearchCommentsDescriptor} for valid fields for searching and\n     *        sorting.\n     * @return The comments on process instances that the user can access.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws SearchException\n     *         If the search could not be completed correctly.\n     * @since 6.0\n     */\n    SearchResult<Comment> searchCommentsInvolvingUser(long userId, SearchOptions searchOptions) throws SearchException;\n\n    /**\n     * Get the children instances (sub process or call activity) of a process instance. The returned list is paginated.\n     * It does not return the process instance of the given id (itself).\n     *\n     * @param processInstanceId\n     *        The identifier of the process instance.\n     * @param startIndex\n     *        The index of the page to be returned (starting at 0).\n     * @param maxResults\n     *        The maximum number of results per page.\n     * @param criterion\n     *        The criterion used to sort the result.\n     * @return The list of children instance identifiers.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @since 6.0\n     */\n    List<Long> getChildrenInstanceIdsOfProcessInstance(long processInstanceId, int startIndex, int maxResults,\n            ProcessInstanceCriterion criterion);\n\n    /**\n     * Check whether a specific user is involved in a given process instance.<br/>\n     * User A is involved with a process instance if any of the following is true:\n     * <ul>\n     * <li>user A has started the process instance</li>\n     * <li>a task in the process instance is assigned to user A</li>\n     * <li>a task in the process instance is pending for user A</li>\n     * <li>a task in the process instance has been performed by user A</li>\n     * </ul>\n     * This method also applies to completed instances of process.\n     *\n     * @param userId\n     *        The identifier of the user.\n     * @param processInstanceId\n     *        The identifier of the process instance.\n     * @return True if the user is involved with the process instance.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws ProcessInstanceNotFoundException\n     *         If there is no processInstance with the specified identifier.\n     * @throws UserNotFoundException\n     *         If there is no user with the specified identifier.\n     * @since 6.0\n     * @see #isManagerOfUserInvolvedInProcessInstance(long, long)\n     */\n    boolean isInvolvedInProcessInstance(long userId, long processInstanceId)\n            throws ProcessInstanceNotFoundException, UserNotFoundException;\n\n    /**\n     * Check whether a specific user is involved in a given human task instance.<br/>\n     * User A is involved with a human task instance if any of the following is true:\n     * <ul>\n     * <li>the human task instance is assigned to user A</li>\n     * <li>the human task instance is pending for user A</li>\n     * </ul>\n     *\n     * @param userId\n     *        The identifier of the user.\n     * @param humanTaskInstanceId\n     *        The identifier of the human task instance.\n     * @return True if the user is involved with the human task instance.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws ActivityInstanceNotFoundException\n     *         If there is no the human task instance with the specified identifier.\n     * @since 6.5.1\n     * @see #isManagerOfUserInvolvedInProcessInstance(long, long)\n     */\n    boolean isInvolvedInHumanTaskInstance(long userId, long humanTaskInstanceId)\n            throws ActivityInstanceNotFoundException, UserNotFoundException;\n\n    /**\n     * Check whether a specific user has at least one subordinate (person he / she is the manager of) involved in a\n     * given process instance.<br/>\n     * User A is involved with a process instance if any of the following is true:\n     * <ul>\n     * <li>user A has started the process instance</li>\n     * <li>a task in the process instance is assigned to user A</li>\n     * <li>a task in the process instance is pending for user A</li>\n     * <li>a task in the process instance has been performed by user A</li>\n     * </ul>\n     * This method also applies to completed instances of process.\n     *\n     * @param managerUserId the ID of the manager of the user involved.\n     * @param processInstanceId the ID of the process instance we are interested in.\n     * @return true if the specified manager has subordinates involved in the given process instance.\n     * @throws ProcessInstanceNotFoundException if the process instance does not exist.\n     * @throws BonitaException if an error occurred while searching for users involved.\n     * @since 6.4.2\n     * @see #isInvolvedInProcessInstance(long, long)\n     */\n    boolean isManagerOfUserInvolvedInProcessInstance(long managerUserId, long processInstanceId) throws BonitaException;\n\n    /**\n     * Get the process instance id from an activity instance id.\n     *\n     * @param activityInstanceId\n     *        The identifier of the activity instance.\n     * @return The corresponding process instance id.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws ProcessInstanceNotFoundException\n     *         If there is no process instance with the specified identifier.\n     * @since 6.0\n     */\n    long getProcessInstanceIdFromActivityInstanceId(long activityInstanceId) throws ProcessInstanceNotFoundException;\n\n    /**\n     * Get the process definition id from an process instance id.\n     *\n     * @param processInstanceId\n     *        The identifier of the process instance.\n     * @return The corresponding process definition id.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws ProcessDefinitionNotFoundException\n     *         If there is no process definition with the specified identifier.\n     * @since 6.0\n     */\n    long getProcessDefinitionIdFromProcessInstanceId(long processInstanceId) throws ProcessDefinitionNotFoundException;\n\n    /**\n     * Get the process definition id from an activity instance id.\n     *\n     * @param activityInstanceId\n     *        The identifier of the activity instance.\n     * @return The corresponding process definition id.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws ProcessDefinitionNotFoundException\n     *         If no ProcessDefinition have an id corresponding to the parameter.\n     * @since 6.0\n     */\n    long getProcessDefinitionIdFromActivityInstanceId(long activityInstanceId)\n            throws ProcessDefinitionNotFoundException;\n\n    /**\n     * Search for archived comments.\n     * -- ex: Retrieve comments of a given archived case --\n     *\n     * <pre>\n     * {@code\n     *\n     * public List<ArchivedComment> retrieveComments(ProcessRuntimeAPI processRuntimeAPI,\n     *         ArchivedProcessInstance archivedProcessInstance) {\n     *     SearchOptions searchOptions = new SearchOptionsBuilder(0, Integer.MAX_VALUE)\n     *             .filter(ArchivedCommentsSearchDescriptor.PROCESS_INSTANCE_ID,\n     *                     archivedProcessInstance.getSourceObjectId())\n     *             .done();\n     *     return processRuntimeAPI.searchArchivedComments(searchOptions).getResult();\n     * }\n     * }\n     * </pre>\n     *\n     * @param searchOptions\n     *        The options used to search for comments. See\n     *        {@link org.bonitasoft.engine.bpm.comment.ArchivedCommentsSearchDescriptor} for valid fields for\n     *        searching and sorting.\n     * @throws InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @return The <code>ArchivedComment</code> items that match the search options.\n     * @throws SearchException\n     *         If the search could not be completed correctly.\n     * @since 6.0\n     */\n    SearchResult<ArchivedComment> searchArchivedComments(SearchOptions searchOptions) throws SearchException;\n\n    /**\n     * Search for archived human tasks managed by the specified user.\n     *\n     * @param managerUserId\n     *        The identifier of the user manager,\n     * @param searchOptions\n     *        The options used to search for tasks. See\n     *        {@link org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstanceSearchDescriptor} for valid fields\n     *        for searching and sorting.\n     * @return The archived humanTask instances managed by the specified user that match the search options.\n     * @throws SearchException\n     *         If the search could not be completed correctly.\n     * @since 6.0\n     */\n    SearchResult<ArchivedHumanTaskInstance> searchArchivedHumanTasksManagedBy(long managerUserId,\n            SearchOptions searchOptions) throws SearchException;\n\n    /**\n     * Search for open process instances that the specified user can access.\n     *\n     * @param userId\n     *        The identifier of the user.\n     * @param searchOptions\n     *        The options used to search for process instance. See\n     *        {@link org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor} for valid fields\n     *        for searching and sorting.\n     * @return The <code>ProcessInstance</code>s that match the search options.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws SearchException\n     *         If the search could not be completed correctly.\n     * @since 6.0\n     */\n    SearchResult<ProcessInstance> searchOpenProcessInstancesInvolvingUser(long userId, SearchOptions searchOptions)\n            throws SearchException;\n\n    /**\n     * Search for open process instances that all subordinates of the specified user can access.\n     *\n     * @param managerUserId\n     *        The identifier of the user manager.\n     * @param searchOptions\n     *        The search options (pagination, filter, order sort). See\n     *        {@link org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor} for valid\n     *        fields for searching and sorting.\n     * @return The <code>ProcessInstance</code>s that match the search options.\n     * @throws SearchException\n     *         If the search could not be completed correctly.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @since 6.0\n     */\n    SearchResult<ProcessInstance> searchOpenProcessInstancesInvolvingUsersManagedBy(long managerUserId,\n            SearchOptions searchOptions) throws SearchException;\n\n    /**\n     * Search for archived root process instances. Only archived process instances in states COMPLETED, ABORTED,\n     * CANCELED and FAILED will be retrieved. Process instances started by call activities won't be retrieved.\n     * See {@link #searchArchivedProcessInstancesInAllStates(SearchOptions) searchArchivedProcessInstancesInAllStates}\n     * to search amoung any archived instance.\n     *\n     * @param searchOptions\n     *        The search options (pagination, filter, order sort).\n     * @return The archived process instances that match the search options. See\n     *         {@link org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor} for valid fields for\n     *         searching and sorting.\n     * @throws SearchException\n     *         If the search could not be full filled correctly\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @since 6.0\n     */\n    SearchResult<ArchivedProcessInstance> searchArchivedProcessInstances(SearchOptions searchOptions)\n            throws SearchException;\n\n    /**\n     * Search for archived process instances (root and intermediate levels) in all states (even intermediate states).\n     * Depending on used filters several\n     * ArchivedProcessInstance will be\n     * retrieved for a single ProcessInstance (one for each reached state).\n     *\n     * @param searchOptions\n     *        The search options (pagination, filter, order sort). See\n     *        {@link org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor} for\n     *        valid fields for searching and sorting.\n     * @return The archived process instances in all states that match the search options.\n     * @throws SearchException\n     *         If the search could not be completed correctly.\n     * @since 6.2\n     */\n    SearchResult<ArchivedProcessInstance> searchArchivedProcessInstancesInAllStates(SearchOptions searchOptions)\n            throws SearchException;\n\n    /**\n     * Search for archived process instances supervised by the specified user.\n     *\n     * @param userId\n     *        The identifier of the user.\n     * @param searchOptions\n     *        The search options (pagination, filter, order sort). See\n     *        {@link org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor} for\n     *        valid fields for searching and sorting.\n     * @return The archived process instances supervised by the user that match the search options.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws SearchException\n     *         If the search could not be completed correctly.\n     * @since 6.0\n     */\n    SearchResult<ArchivedProcessInstance> searchArchivedProcessInstancesSupervisedBy(long userId,\n            SearchOptions searchOptions) throws SearchException;\n\n    /**\n     * Search for archived process instances that the specified user can access.\n     *\n     * @param userId\n     *        The identifier of the user.\n     * @param searchOptions\n     *        The search options (pagination, filter, order sort). See\n     *        {@link org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor} for\n     *        valid fields for searching and sorting.\n     * @return The archived process instances that the user can access that match the search options.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws SearchException\n     *         If the search could not be completed correctly.\n     * @since 6.0\n     */\n    SearchResult<ArchivedProcessInstance> searchArchivedProcessInstancesInvolvingUser(long userId,\n            SearchOptions searchOptions) throws SearchException;\n\n    /**\n     * Search for human task instances.\n     *\n     * @param searchOptions\n     *        The search options (pagination, filter, order sort). See\n     *        {@link org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor} for valid\n     *        fields for searching and sorting.\n     * @return The human task instances that match the search options.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws SearchException\n     *         If the search could not be completed correctly.\n     * @since 6.0\n     */\n    SearchResult<HumanTaskInstance> searchHumanTaskInstances(SearchOptions searchOptions) throws SearchException;\n\n    /**\n     * Search for tasks assigned to users supervised by the specified user.\n     *\n     * @param supervisorId\n     *        The identifier of supervising user.\n     * @param searchOptions\n     *        The search options (pagination, filter, order sort). See\n     *        {@link org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor} for valid\n     *        fields for searching and sorting.\n     * @return The human task instances that match the search options.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws SearchException\n     *         If the search could not be completed correctly.\n     * @since 6.0\n     */\n    SearchResult<HumanTaskInstance> searchAssignedTasksSupervisedBy(long supervisorId, SearchOptions searchOptions)\n            throws SearchException;\n\n    /**\n     * Search for archived tasks assigned to users supervised by the specified user.\n     *\n     * @param supervisorId\n     *        The identifier of the supervising user.\n     * @param searchOptions\n     *        The search options (pagination, filter, order sort). See\n     *        {@link org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstanceSearchDescriptor} for\n     *        valid fields for searching and sorting.\n     * @return The archived human task instances that match the search options.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid, e.g. the session has expired.\n     * @throws SearchException\n     *         If the search could not be completed correctly.\n     * @since 6.0\n     */\n    SearchResult<ArchivedHumanTaskInstance> searchArchivedHumanTasksSupervisedBy(long supervisorId,\n            SearchOptions searchOptions) throws SearchException;\n\n    /**\n     * Evaluate expressions with values valid at process instantiation scope.\n     *\n     * @param processInstanceId\n     *        The identifier of the process instance.\n     * @param expressions\n     *        The map of expressions to evaluate.\n     * @return The result of the expression execution. Content of the resulting map depends on the incoming expression\n     *         map. The returned key is The name of the\n     *         expression (or its content if name is empty), the returned value is the evaluated expression result.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         Generic exception thrown if API Session is invalid, e.g session has expired.\n     * @throws ExpressionEvaluationException\n     *         Occurs when an exception is thrown during expression evaluation.\n     * @since 6.0\n     */\n    Map<String, Serializable> evaluateExpressionsAtProcessInstanciation(long processInstanceId,\n            Map<Expression, Map<String, Serializable>> expressions)\n            throws ExpressionEvaluationException;\n\n    /**\n     * Evaluate expressions with values valid on a completed process instance scope.\n     *\n     * @param processInstanceId\n     *        The identifier of the process instance.\n     * @param expressions\n     *        The map of expressions to evaluate.\n     * @return The result of the expression execution. Content of the resulting map depends on the incoming expression\n     *         map. The returned key is The name of the\n     *         expression (or its content if name is empty), the returned value is the evaluated expression result.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the API session is invalid, e.g session has expired.\n     * @throws ExpressionEvaluationException\n     *         Occurs when an exception is thrown during expression evaluation.\n     * @since 6.0\n     */\n    Map<String, Serializable> evaluateExpressionOnCompletedProcessInstance(long processInstanceId,\n            Map<Expression, Map<String, Serializable>> expressions)\n            throws ExpressionEvaluationException;\n\n    /**\n     * Evaluate expressions with values valid on a process instance scope.\n     *\n     * @param processInstanceId\n     *        The identifier of the process instance.\n     * @param expressions\n     *        The map of expressions to evaluate.\n     * @return The result of the expression execution. Content of the resulting map depends on the incoming expression\n     *         map. The returned key is The name of the\n     *         expression (or its content if name is empty), the returned value is the evaluated expression result.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the API session is invalid, e.g session has expired.\n     * @throws ExpressionEvaluationException\n     *         Occurs when an exception is thrown during expression evaluation.\n     * @since 6.0\n     */\n    Map<String, Serializable> evaluateExpressionsOnProcessInstance(long processInstanceId,\n            Map<Expression, Map<String, Serializable>> expressions)\n            throws ExpressionEvaluationException;\n\n    /**\n     * Evaluate expressions with values valid on a process definition scope.\n     *\n     * @param processDefinitionId\n     *        The identifier of the process definition.\n     * @param expressions\n     *        The map of expressions to evaluate.\n     * @return The result of the expression execution. Content of the resulting map depends on the incoming expression\n     *         map. The returned key is The name of the\n     *         expression (or its content if name is empty), the returned value is the evaluated expression result.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the API session is invalid, e.g session has expired.\n     * @throws ExpressionEvaluationException\n     *         Occurs when an exception is thrown during expression evaluation.\n     * @since 6.0\n     */\n    Map<String, Serializable> evaluateExpressionsOnProcessDefinition(long processDefinitionId,\n            Map<Expression, Map<String, Serializable>> expressions)\n            throws ExpressionEvaluationException;\n\n    /**\n     * Evaluate expressions with values valid on an activity instance scope.\n     *\n     * @param activityInstanceId\n     *        The identifier of the activity instance.\n     * @param expressions\n     *        The map of expressions to evaluate.\n     * @return The result of the expression execution. Content of the resulting map depends on the incoming expression\n     *         map. The returned key is The name of the\n     *         expression (or its content if name is empty), the returned value is the evaluated expression result.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the API session is invalid, e.g session has expired.\n     * @throws ExpressionEvaluationException\n     *         Occurs when an exception is thrown during expression evaluation.\n     * @since 6.0\n     */\n    Map<String, Serializable> evaluateExpressionsOnActivityInstance(long activityInstanceId,\n            Map<Expression, Map<String, Serializable>> expressions)\n            throws ExpressionEvaluationException;\n\n    /**\n     * Evaluate expressions with values valid on a completed activity instance scope.\n     *\n     * @param activityInstanceId\n     *        The identifier of the activity instance.\n     * @param expressions\n     *        The map of expressions to evaluate.\n     * @return The result of the expression execution. Content of the resulting map depends on the incoming expression\n     *         map. The returned key is The name of the\n     *         expression (or its content if name is empty), the returned value is the evaluated expression result.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the API session is invalid, e.g session has expired.\n     * @throws ExpressionEvaluationException\n     *         Occurs when an exception is thrown during expression evaluation.\n     * @since 6.0\n     */\n    Map<String, Serializable> evaluateExpressionsOnCompletedActivityInstance(long activityInstanceId,\n            Map<Expression, Map<String, Serializable>> expressions)\n            throws ExpressionEvaluationException;\n\n    /**\n     * Returns the list of jobs that failed.\n     *\n     * @param startIndex\n     *        The result start index (starting from 0).\n     * @param maxResults\n     *        The maximum number of results to retrieve.\n     * @return The list of failed jobs.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.1\n     */\n    List<FailedJob> getFailedJobs(int startIndex, int maxResults);\n\n    /**\n     * Replay a job that failed.\n     * Job that failed can be found using {@link #getFailedJobs(int, int)}\n     * This will remove the traces of Failed Jobs and restart the job once.\n     * If you wish to re-execute multiple times the job that failed in case a recurrent job failed multiple times,\n     * call this method several times.\n     *\n     * @param jobDescriptorId\n     *        The identifier of the job descriptor.\n     * @since 6.1\n     */\n    void replayFailedJob(final long jobDescriptorId) throws ExecutionException;\n\n    /**\n     * Update parameters of a job and replay it.\n     * The specified parameters replace the stored parameters. i.e. If the job is recurrent, all\n     * future executions of this job will use the newly specified parameters.\n     *\n     * @param jobDescriptorId\n     *        The identifier of the job descriptor.\n     * @param parameters\n     *        The job parameters.\n     * @since 6.1\n     * @deprecated since 7.9, use {@link #replayFailedJob(long)} instead, parameters should not be modified.\n     */\n    @Deprecated(since = \"7.9\", forRemoval = true)\n    void replayFailedJob(final long jobDescriptorId, Map<String, Serializable> parameters) throws ExecutionException;\n\n    /**\n     * Gets the last archived data instance of the named data of the specified process instance.\n     *\n     * @param dataName\n     *        The name of the data\n     * @param sourceProcessInstanceId\n     *        The identifier of the source process instance (used as root container id of the archived activity\n     *        instances).\n     * @return An archived instance of data.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @throws ArchivedDataNotFoundException\n     *         If the specified data cannot be found.\n     * @since 6.1\n     */\n    ArchivedDataInstance getArchivedProcessDataInstance(String dataName, long sourceProcessInstanceId)\n            throws ArchivedDataNotFoundException;\n\n    /**\n     * Gets the last archived data instance of the named data of the specified activity instance.\n     *\n     * @param dataName\n     *        The name of the data\n     * @param sourceActivityInstanceId\n     *        The identifier of the source activity instance\n     * @return An archived instance of data.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @throws ArchivedDataNotFoundException\n     *         If the specified data cannot be found\n     * @since 6.1\n     */\n    ArchivedDataInstance getArchivedActivityDataInstance(String dataName, long sourceActivityInstanceId)\n            throws ArchivedDataNotFoundException;\n\n    /**\n     * Lists the last archived instances of data of the specified process instance.\n     *\n     * @param sourceProcessInstanceId\n     *        The identifier of the source process instance (used as root container id of the archived activity\n     *        instances).\n     * @param startIndex\n     *        The start index\n     * @param maxResults\n     *        The max number of archived data instances\n     * @return The list of archived data instances.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs while retrieving the archived instances of data\n     * @since 6.1\n     */\n    List<ArchivedDataInstance> getArchivedProcessDataInstances(long sourceProcessInstanceId, int startIndex,\n            int maxResults);\n\n    /**\n     * Lists the last archived instances of data of the specified activity instance.\n     *\n     * @param sourceActivityInstanceId\n     *        The identifier of the source activity instance\n     * @param startIndex\n     *        The start index\n     * @param maxResults\n     *        The max number of archived data instances\n     * @return The list of archived data instances.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs while retrieving the archived instances of data\n     * @since 6.1\n     */\n    List<ArchivedDataInstance> getArchivedActivityDataInstances(long sourceActivityInstanceId, int startIndex,\n            int maxResults);\n\n    /**\n     * Lists the possible users (candidates) of the specified human task instance.\n     * Users are ordered by user name.\n     *\n     * @param humanTaskInstanceId\n     *        The identifier of the human task instance\n     * @param startIndex\n     *        The start index\n     * @param maxResults\n     *        The max number of users\n     * @return The list of users.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs while retrieving the users\n     * @since 6.1\n     */\n    List<User> getPossibleUsersOfPendingHumanTask(long humanTaskInstanceId, int startIndex, int maxResults);\n\n    /**\n     * Lists the possible users (candidates) that can execute the specified human task instance.\n     * Users are ordered by user name.\n     *\n     * @param humanTaskInstanceId\n     *        The identifier of the human task instance\n     * @param searchOptions\n     *        the search options. See {@link org.bonitasoft.engine.identity.UserSearchDescriptor} for valid fields for\n     *        searching and sorting.\n     * @return The list of users.\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs while retrieving the users\n     * @since 6.3\n     */\n    SearchResult<User> searchUsersWhoCanExecutePendingHumanTask(final long humanTaskInstanceId,\n            SearchOptions searchOptions);\n\n    /**\n     * Search process definitions that have instances with assigned or pending human tasks for a specific user.\n     * The tasks are in stable state, not in terminal/executing state.\n     *\n     * @param userId\n     *        The identifier of the user.\n     * @param searchOptions\n     *        The search criterion. See {@link org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoSearchDescriptor}\n     *        for valid fields for searching and\n     *        sorting.\n     * @return The list of process definitions\n     * @throws SearchException\n     *         if an exception occurs when getting the process deployment information.\n     * @since 6.3.3\n     */\n    SearchResult<ProcessDeploymentInfo> searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(long userId,\n            SearchOptions searchOptions)\n            throws SearchException;\n\n    /**\n     * Search process definitions supervised by a specific user, that have instances with assigned or pending human\n     * tasks.\n     * The tasks are in stable state, not in terminal/executing state.\n     *\n     * @param supervisorId\n     *        The identifier of the user.\n     * @param searchOptions\n     *        The search criterion. See {@link org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoSearchDescriptor}\n     *        for valid fields for searching and\n     *        sorting.\n     * @return The list of process definitions\n     * @throws SearchException\n     *         if an exception occurs when getting the process deployment information.\n     * @since 6.3.3\n     */\n    SearchResult<ProcessDeploymentInfo> searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(\n            long supervisorId, SearchOptions searchOptions)\n            throws SearchException;\n\n    /**\n     * Search process definitions that have instances with assigned or pending human tasks.\n     * The tasks are in stable state, not in terminal/executing state.\n     *\n     * @param searchOptions\n     *        The search criterion. See {@link org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoSearchDescriptor}\n     *        for valid fields for searching and\n     *        sorting.\n     * @return The list of process definitions\n     * @throws SearchException\n     *         If an exception occurs when getting the process deployment information.\n     * @since 6.3.3\n     */\n    SearchResult<ProcessDeploymentInfo> searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks(\n            SearchOptions searchOptions) throws SearchException;\n\n    /**\n     * Retrieve, for a given process definition, the current counters on active (not archived) flownodes.\n     *\n     * @param processDefinitionId ID of the process definition for which to retrieve the current indicators.\n     * @return A map of counters: the key is the name of the flownode, as defined at design-time. the value is the\n     *         current counters for this flownode, that is,\n     *         a map of &lt;state name, number of current flownode in that state&gt; (possible state names are: ready,\n     *         executing, waiting, initializing, failed, completing)\n     *         If no results, returns an empty Map.\n     * @throws ProcessDefinitionNotFoundException\n     *         If not process definition exists with the ID processDefinitionId.\n     * @since 7.15.0\n     */\n    Map<String, Map<String, Long>> getActiveFlownodeStateCountersForProcessDefinition(final long processDefinitionId)\n            throws ProcessDefinitionNotFoundException;\n\n    /**\n     * Retrieve, for a given process instance, the current counters on flownodes. Please note: this method does not\n     * count the flownodes of sub-process instances\n     * of the given process instance.\n     *\n     * @param processInstanceId ID of the process instance for which to retrieve the current indicators.\n     * @return A map of counters: the key is the name of the flownode, as defined at design-time. the value is the\n     *         current counters for this flownode, that is,\n     *         a map of &lt;state name, number of current flownode in that state&gt; (possible state names are: ready,\n     *         executing, waiting, initializing, failed, completing, completed, skipped, cancelled, aborted)\n     *         If no results, returns an empty Map.\n     * @throws RetrieveException\n     *         If not process instance exists with the ID processInstanceId.\n     * @since 6.5.0\n     */\n    Map<String, Map<String, Long>> getFlownodeStateCounters(long processInstanceId);\n\n    /**\n     * Search the {@link TimerEventTriggerInstance} on the specific {@link ProcessInstance}.\n     *\n     * @param searchOptions\n     *        The search criterion.\n     * @return The list of the timer event triggers\n     * @throws SearchException\n     *         If an exception occurs when getting the timer event triggers.\n     * @since 6.4.0\n     */\n    SearchResult<TimerEventTriggerInstance> searchTimerEventTriggerInstances(long processInstanceId,\n            SearchOptions searchOptions) throws SearchException;\n\n    /**\n     * Change the date of the execution of a specific {@link TimerEventTriggerInstance}.\n     *\n     * @param timerEventTriggerInstanceId\n     *        The identifier of the {@link TimerEventTriggerInstance} to update\n     * @param executionDate\n     *        The new date of the execution of the {@link TimerEventTriggerInstance}\n     * @return The first fire time of the newly scheduled trigger is returned\n     * @throws TimerEventTriggerInstanceNotFoundException\n     *         If the {@link TimerEventTriggerInstance} doesn't exist\n     * @throws UpdateException\n     *         If an exception occurs when updating the {@link TimerEventTriggerInstance}\n     * @since 6.4.0\n     */\n    Date updateExecutionDateOfTimerEventTriggerInstance(long timerEventTriggerInstanceId, Date executionDate)\n            throws TimerEventTriggerInstanceNotFoundException, UpdateException;\n\n    /**\n     * Gets the contract of the user task.\n     *\n     * @param userTaskId the identifier of the user task.\n     * @return the contract of the user task\n     * @throws UserTaskNotFoundException\n     *         if identifier does not refer to a real user task.\n     */\n    ContractDefinition getUserTaskContract(long userTaskId) throws UserTaskNotFoundException;\n\n    /**\n     * Gets the process instantiation contract for a given process definition.\n     *\n     * @param processDefinitionId the identifier of the process definition.\n     * @return the contract of the given process\n     * @throws ProcessDefinitionNotFoundException\n     *         if identifier does not refer to an existing process definition.\n     */\n    ContractDefinition getProcessContract(long processDefinitionId) throws ProcessDefinitionNotFoundException;\n\n    /**\n     * Executes a user task that is in a stable state.\n     * Will move the activity to the next stable state and then continue the execution of the process.\n     *\n     * @param userTaskInstanceId\n     *        The identifier of the user task to execute.\n     * @param inputs\n     *        the inputs used for user task execution\n     * @throws UserTaskNotFoundException\n     *         If user task to execute is not found\n     * @throws ContractViolationException\n     *         If inputs don't fit with task contract\n     * @throws FlowNodeExecutionException\n     *         If an execution exception occurs\n     * @since 7.0\n     */\n    void executeUserTask(long userTaskInstanceId, Map<String, Serializable> inputs)\n            throws UserTaskNotFoundException, ContractViolationException,\n            FlowNodeExecutionException;\n\n    /**\n     * Executes a user task that is in a stable state on behalf of a given user\n     * Will make the task go in the next stable state and then continue the execution of the process\n     * If userId equals 0, the logged-in user is declared as the executer of the task.\n     * The user, who executed the task on behalf of a given user, is declared as a executer delegate.\n     *\n     * @param userId\n     *        The identifier of the user for which you want to execute the flow node\n     * @param userTaskInstanceId\n     *        The identifier of the user task to execute\n     * @param inputs\n     *        the input used for user task execution\n     * @throws UserTaskNotFoundException\n     *         If user task to execute is not found\n     * @throws ContractViolationException\n     *         If inputs don't fit with task contract\n     * @throws FlowNodeExecutionException\n     *         If an execution exception occurs\n     * @since 7.0\n     */\n    void executeUserTask(long userId, long userTaskInstanceId, Map<String, Serializable> inputs)\n            throws UserTaskNotFoundException, ContractViolationException,\n            FlowNodeExecutionException;\n\n    /**\n     * Assign a task to a user with given user identifier and executes a user task that is in a stable state on behalf\n     * of a given user\n     * Will make the task go in the next stable state and then continue the execution of the process\n     * If userId equals 0, the logged-in user is declared as the executer of the task.\n     * The user, who executed the task on behalf of a given user, is declared as a executer delegate.\n     *\n     * @param userId\n     *        The identifier of the user for which you want to assign and execute the flow node\n     * @param userTaskInstanceId\n     *        The identifier of the user task to execute\n     * @param inputs\n     *        the input used for user task execution\n     * @throws UpdateException\n     *         If an assignment exception occurs\n     * @throws UserTaskNotFoundException\n     *         If user task to execute is not found\n     * @throws ContractViolationException\n     *         If inputs don't fit with task contract\n     * @throws FlowNodeExecutionException\n     *         If an execution exception occurs\n     * @since 7.9\n     */\n    void assignAndExecuteUserTask(long userId, long userTaskInstanceId, Map<String, Serializable> inputs)\n            throws UpdateException, UserTaskNotFoundException, ContractViolationException,\n            FlowNodeExecutionException;\n\n    /**\n     * Gets the value of the variable of the user task contract.\n     *\n     * @param userTaskInstanceId\n     *        The identifier of the user task\n     * @param name\n     *        The name of the variable\n     * @return The identifier of the user task\n     * @throws ContractDataNotFoundException if no data found for the given user task instance and name.\n     */\n    Serializable getUserTaskContractVariableValue(long userTaskInstanceId, String name)\n            throws ContractDataNotFoundException;\n\n    /**\n     * Gets the value of a process instantiation input, during the phase of initializing. For instance, if a connector\n     * on_enter fails, this method can be called\n     * to check the current value.\n     *\n     * @param processInstanceId The identifier of the process instance\n     * @param name The name of the process input to retrieve\n     * @return The identifier of the user task\n     * @throws ContractDataNotFoundException if no data found for the given process instance and name.\n     */\n    Serializable getProcessInputValueDuringInitialization(long processInstanceId, String name)\n            throws ContractDataNotFoundException;\n\n    /**\n     * Gets the value of a process instantiation input, after initialization has finished. Requires Archiving feature to\n     * be enabled (default behaviour).\n     *\n     * @param processInstanceId The identifier of the process instance\n     * @param name The name of the process input to retrieve\n     * @return The identifier of the user task\n     * @throws ContractDataNotFoundException if identifier does not refer to an existing process instance.\n     */\n    Serializable getProcessInputValueAfterInitialization(long processInstanceId, String name)\n            throws ContractDataNotFoundException;\n\n    /**\n     * return the context defined in the process definition for this user task instance. Context includes:\n     * <ul>\n     * <li>Business data references (see {@link org.bonitasoft.engine.business.data.BusinessDataReference},\n     * {@link org.bonitasoft.engine.business.data.SimpleBusinessDataReference} and\n     * {@link org.bonitasoft.engine.business.data.MultipleBusinessDataReference}).\n     * Key of\n     * data reference is the name of the business data as declared in process definition followed by \"_ref\".\n     * <li>Documents reference (see {@link org.bonitasoft.engine.bpm.document.Document}). Naming convention is the same\n     * as for business data (\"_ref\" suffix).\n     * <li>For multi-instantiated task only: iterator name with \"_ref\" suffix (type of the value is\n     * {@link org.bonitasoft.engine.business.data.BusinessDataReference}). By default: multiInstanceIterator_ref. Only\n     * exist if iterator value is set using\n     * a business variable.\n     * <li>\n     * </ul>\n     *\n     * @param userTaskInstanceId the id of the user task instance\n     * @return a map containing the evaluated context\n     * @throws UserTaskNotFoundException if <code>userTaskInstanceId</code> does not reference any existing task.\n     */\n    Map<String, Serializable> getUserTaskExecutionContext(long userTaskInstanceId)\n            throws UserTaskNotFoundException, ExpressionEvaluationException;\n\n    /**\n     * return the context defined in the process definition for this user task instance. Context includes:\n     * <ul>\n     * <li>Business data references (see {@link org.bonitasoft.engine.business.data.BusinessDataReference},\n     * {@link org.bonitasoft.engine.business.data.SimpleBusinessDataReference} and\n     * {@link org.bonitasoft.engine.business.data.MultipleBusinessDataReference}).\n     * Key of\n     * data reference is the name of the business data as declared in process definition followed by \"_ref\".\n     * <li>Documents reference (see {@link org.bonitasoft.engine.bpm.document.Document}). Naming convention is the same\n     * as for business data (\"_ref\" suffix).\n     * <li>For multi-instantiated task only: iterator name with \"_ref\" suffix (type of the value is\n     * {@link org.bonitasoft.engine.business.data.BusinessDataReference}). By default: multiInstanceIterator_ref. Only\n     * exist if iterator value is set using\n     * a business variable.\n     * <li>\n     * </ul>\n     *\n     * @param archivedUserTaskInstanceId the id of the archived version of the user task instance\n     * @return a map containing the evaluated context\n     * @throws UserTaskNotFoundException if <code>archivedUserTaskInstanceId</code> does not reference any existing\n     *         archived task.\n     */\n    Map<String, Serializable> getArchivedUserTaskExecutionContext(long archivedUserTaskInstanceId)\n            throws UserTaskNotFoundException, ExpressionEvaluationException;\n\n    /**\n     * return the context defined in the process definition for this process instance. Context includes:\n     * <ul>\n     * <li>Business data references (see {@link org.bonitasoft.engine.business.data.BusinessDataReference},\n     * {@link org.bonitasoft.engine.business.data.SimpleBusinessDataReference} and\n     * {@link org.bonitasoft.engine.business.data.MultipleBusinessDataReference}).\n     * Key of\n     * data reference is the name of the business data as declared in process definition followed by \"_ref\".\n     * <li>Documents reference (see {@link org.bonitasoft.engine.bpm.document.Document}). Naming convention is the same\n     * as for business data (\"_ref\" suffix).\n     * </ul>\n     *\n     * @param processInstanceId the id of the process instance\n     * @return a map containing the evaluated context\n     * @throws ProcessInstanceNotFoundException if <code>processInstanceId</code> does not reference any existing\n     *         process.\n     */\n    Map<String, Serializable> getProcessInstanceExecutionContext(long processInstanceId)\n            throws ProcessInstanceNotFoundException, ExpressionEvaluationException;\n\n    /**\n     * return the context defined in the process definition for this process instance. Context includes:\n     * <ul>\n     * <li>Business data references (see {@link org.bonitasoft.engine.business.data.BusinessDataReference},\n     * {@link org.bonitasoft.engine.business.data.SimpleBusinessDataReference} and\n     * {@link org.bonitasoft.engine.business.data.MultipleBusinessDataReference}).\n     * Key of\n     * data reference is the name of the business data as declared in process definition followed by \"_ref\".\n     * <li>Documents reference (see {@link org.bonitasoft.engine.bpm.document.Document}). Naming convention is the same\n     * as for business data (\"_ref\" suffix).\n     * </ul>\n     *\n     * @param archivedProcessInstanceId the id of the archived version of a process instance. You can use\n     *        {@link #getFinalArchivedProcessInstance(long)} to get\n     *        the id of an archived instance based on the id of the same instance while it was running.\n     * @return a map containing the evaluated context\n     * @throws ProcessInstanceNotFoundException if <code>archivedProcessInstanceId</code> does not reference any\n     *         existing process.\n     */\n    Map<String, Serializable> getArchivedProcessInstanceExecutionContext(long archivedProcessInstanceId)\n            throws ProcessInstanceNotFoundException, ExpressionEvaluationException;\n\n    /**\n     * Update an instance of process with the given processInstanceId.\n     *\n     * @param processInstanceId\n     *        Identifier of the process instance\n     * @param updater\n     *        including new value of all attributes adaptable\n     * @return the updated process instance\n     * @throws ProcessInstanceNotFoundException if no process instance can be found with id processInstanceId.\n     * @throws UpdateException\n     *         if an error is thrown while updating the process instance.\n     * @throws InvalidSessionException\n     *         if the session is invalid, e.g. the session has expired.\n     * @since 6.0\n     */\n    ProcessInstance updateProcessInstance(long processInstanceId,\n            org.bonitasoft.engine.bpm.process.impl.ProcessInstanceUpdater updater)\n            throws ProcessInstanceNotFoundException, UpdateException;\n\n    /**\n     * Update a search key of a process instance, also known as string index.\n     *\n     * @param processInstanceId\n     *        identifier of the process instance\n     * @param index the search to update\n     * @param value\n     *        the new value for the search key\n     * @return the updated process instance\n     * @throws ProcessInstanceNotFoundException\n     *         Error thrown if no process instance have an id corresponding to the value of processInstanceId parameter.\n     * @throws UpdateException\n     *         if an error is thrown while updating the process instance.\n     * @throws InvalidSessionException\n     *         if the session is invalid, e.g. the session has expired.\n     * @since 7.12.0\n     */\n    ProcessInstance updateProcessInstanceIndex(long processInstanceId, Index index, String value)\n            throws ProcessInstanceNotFoundException, UpdateException;\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/ProfileAPI.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.profile.Profile;\nimport org.bonitasoft.engine.profile.ProfileCriterion;\nimport org.bonitasoft.engine.profile.ProfileMember;\nimport org.bonitasoft.engine.profile.ProfileMemberCreator;\nimport org.bonitasoft.engine.profile.ProfileNotFoundException;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\n\n/**\n * Profiles are a notion used in Bonita Portal to give and control access to some specific features of the Bonita suite.\n * Profiles are associated to Bonita Identity / Organization notions: users, groups, roles, memberships.\n * <code>ProfileAPI</code> gives access to some of the\n * profile administration: adding / removing members to / from a profile, retrieving / searching for profiles. <br>\n * Full control on profiles is part of <b>Subscription</b> editions of Bonita suite.\n *\n * @author Celine Souchet\n * @author Matthieu Chaffotte\n * @see SearchResult SearchResult for general knowledege on Search mechanism in Bonita.\n */\npublic interface ProfileAPI {\n\n    /**\n     * Retrieves the profile.\n     *\n     * @param id\n     *        The identifier of the profile\n     * @return the searched profile\n     * @throws ProfileNotFoundException\n     *         If the identifier does not refer to an existing profile\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs during the profile retrieving\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    Profile getProfile(long id) throws ProfileNotFoundException;\n\n    /**\n     * Retrieves the profiles of the user.\n     *\n     * @param userId\n     *        The identifier of the user\n     * @param startIndex\n     *        The index of the first result (starting from 0).\n     * @param maxResults\n     *        The maximum number of elements to get per page.\n     * @param criterion\n     *        The criterion for sorting the items over pages.\n     * @return The paginated and ordered profiles of the user\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs during the profile retrieving\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.3.2\n     */\n    List<Profile> getProfilesForUser(long userId, int startIndex, int maxResults, ProfileCriterion criterion);\n\n    /**\n     * Searches for {@link Profile}s with specific search criteria. Use\n     * {@link org.bonitasoft.engine.profile.ProfileSearchDescriptor} to\n     * know the available filters.\n     *\n     * @param options\n     *        The search criteria\n     * @return a {@link SearchResult} containing the list of {@code Profile}s matching the search criteria.\n     * @throws SearchException\n     *         If an exception occurs during the profile searching\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     * @see Profile\n     * @see org.bonitasoft.engine.profile.ProfileSearchDescriptor\n     * @see SearchResult\n     */\n    SearchResult<Profile> searchProfiles(SearchOptions options) throws SearchException;\n\n    /**\n     * Retrieves the number of profile members for the profiles. The map contains the couples\n     * profileId/numberOfProfileMembers.\n     * <p>\n     * If a profile does not exist, no exception is thrown and no value is added in the map.\n     * </p>\n     *\n     * @param profileIds\n     *        The identifiers of the profiles\n     * @return the number of profile members for the profiles\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs during the profile retrieving\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    Map<Long, Long> getNumberOfProfileMembers(List<Long> profileIds);\n\n    /**\n     * Searches for {@link ProfileMember}s with specific search criteria. Use\n     * {@link org.bonitasoft.engine.profile.ProfileMemberSearchDescriptor} to\n     * know the available filters.\n     *\n     * @param memberType\n     *        The member type, it can be: user, role, group, roleAndGroup.\n     * @param options\n     *        The search criteria\n     * @return a {@link SearchResult} containing the list of {@code ProfileMember}s matching the search criteria.\n     * @throws SearchException\n     *         If an exception occurs during the profile searching\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     * @see ProfileMember\n     * @see org.bonitasoft.engine.profile.ProfileMemberSearchDescriptor\n     * @see SearchResult\n     */\n    SearchResult<ProfileMember> searchProfileMembers(String memberType, SearchOptions options) throws SearchException;\n\n    /**\n     * Creates a profile member.\n     *\n     * @param profileId\n     *        The identifier of the profile\n     * @param userId\n     *        The identifier of the user\n     * @param groupId\n     *        The identifier of the group\n     * @param roleId\n     *        The identifier of the role\n     * @return the created profile member\n     * @throws AlreadyExistsException\n     *         If the tuple profileId/userId/roleId/groupId is already taken by an existing profile member\n     * @throws CreationException\n     *         If an exception occurs during the profile member creation\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    ProfileMember createProfileMember(Long profileId, Long userId, Long groupId, Long roleId)\n            throws CreationException, AlreadyExistsException;\n\n    /**\n     * Creates a profile member.\n     * <p>\n     * It takes the values of the creator in order to create the profile member.\n     * </p>\n     *\n     * @param creator\n     *        The profile member to create\n     * @return the created profile member\n     * @throws AlreadyExistsException\n     *         If the tuple profileId/userId/roleId/groupId is already taken by an existing profile member\n     * @throws CreationException\n     *         If an exception occurs during the profile member creation\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    ProfileMember createProfileMember(ProfileMemberCreator creator) throws CreationException, AlreadyExistsException;\n\n    /**\n     * Deletes the profile member.\n     *\n     * @param id\n     *        The identifier of the profile member\n     * @throws DeletionException\n     *         If an exception occurs during the profile member deletion\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    void deleteProfileMember(Long id) throws DeletionException;\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/RoleAPI.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.identity.Role;\nimport org.bonitasoft.engine.identity.RoleCreator;\nimport org.bonitasoft.engine.identity.RoleCriterion;\nimport org.bonitasoft.engine.identity.RoleNotFoundException;\nimport org.bonitasoft.engine.identity.RoleUpdater;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\n\n/**\n * RoleAPI forms part of the {@link OrganizationAPI} and gives access to all the Administration operations available on\n * Roles: creation, deletion,\n * search, etc...\n *\n * @author Feng Hui\n * @author Matthieu Chaffotte\n * @author Hongwen Zang\n * @author Emmanuel Duchastenier\n * @see SearchResult SearchResult for general knowledege on Search mechanism in Bonita.\n * @see Role\n */\npublic interface RoleAPI {\n\n    /**\n     * Creates a role.\n     *\n     * @param roleName\n     *        the name of the role\n     * @return the created role\n     * @throws AlreadyExistsException\n     *         If the name is already taken by an existing role\n     * @throws CreationException\n     *         If an exception occurs during the role creation\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    Role createRole(String roleName) throws AlreadyExistsException, CreationException;\n\n    /**\n     * Create the role.\n     * <p>\n     * It takes the values of the creator in order to create the role.\n     * </p>\n     *\n     * @param creator\n     *        the role creator\n     * @return the created role.\n     * @throws AlreadyExistsException\n     *         If the name is already taken by an existing role\n     * @throws CreationException\n     *         If an exception occurs during the role creation\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    Role createRole(RoleCreator creator) throws AlreadyExistsException, CreationException;\n\n    /**\n     * Updates the group according to the updater values.\n     * <p>\n     * This method also allow to update the icon of the role.\n     * When you update it, the iconId will be set on the role and you can later get it using\n     * {@link IdentityAPI#getIcon(long)}.\n     * Changing the content of the icon will create a new icon and change the iconId of the role.\n     * </p>\n     *\n     * @param roleId\n     *        the identifier of the role\n     * @param updater\n     *        the role updater\n     * @return the updated role\n     * @throws RoleNotFoundException\n     *         If the role identifier does not refer to an existing role\n     * @throws UpdateException\n     *         If an exception occurs during the role update\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    Role updateRole(long roleId, RoleUpdater updater) throws RoleNotFoundException, UpdateException;\n\n    /**\n     * Deletes the role.\n     *\n     * @param roleId\n     *        the role identifier\n     * @throws DeletionException\n     *         If an exception occurs during the role deletion\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    void deleteRole(long roleId) throws DeletionException;\n\n    /**\n     * Deletes the roles.\n     *\n     * @param roleIds\n     *        the list of role identifiers\n     * @throws DeletionException\n     *         If an exception occurs during the role deletion\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    void deleteRoles(List<Long> roleIds) throws DeletionException;\n\n    /**\n     * Retrieves the role.\n     *\n     * @param roleId\n     *        the identifier of the role\n     * @return the role\n     * @throws RoleNotFoundException\n     *         If the role identifier does not refer to an existing role\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs during the role retrieving\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    Role getRole(long roleId) throws RoleNotFoundException;\n\n    /**\n     * Retrieves the role.\n     *\n     * @param roleName\n     *        the name of the role.\n     * @return the role.\n     * @throws RoleNotFoundException\n     *         If the role name does not refer to an existing role\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs during the role retrieving\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    Role getRoleByName(String roleName) throws RoleNotFoundException;\n\n    /**\n     * Returns the total number of roles.\n     *\n     * @return the total number of roles\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs during the count retrieving\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    long getNumberOfRoles();\n\n    /**\n     * Retrieves the paginated list of roles.\n     * <p>\n     * It retrieves from the startIndex to the startIndex + maxResults.\n     * </p>\n     *\n     * @param startIndex\n     *        the start index\n     * @param maxResults\n     *        the max number of roles\n     * @param criterion\n     *        the sorting criterion\n     * @return the paginated list of roles\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs during the role retrieving\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    List<Role> getRoles(int startIndex, int maxResults, RoleCriterion criterion);\n\n    /**\n     * Retrieves the roles. The map contains the couples roleId/Role.\n     * <p>\n     * If a role does not exists, no exception is thrown and no value is added in the map.\n     * </p>\n     *\n     * @param roleIds\n     *        the identifiers of the roles\n     * @return the roles\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs during the role retrieving\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    Map<Long, Role> getRoles(List<Long> roleIds);\n\n    /**\n     * Searches roles according to the criteria containing in the options.\n     *\n     * @param options\n     *        the search criteria\n     * @return the search result\n     * @throws SearchException\n     *         If an exception occurs during the role searching\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    SearchResult<Role> searchRoles(SearchOptions options) throws SearchException;\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/TenantAdministrationAPI.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\nimport org.bonitasoft.engine.business.data.BusinessDataRepositoryDeploymentException;\nimport org.bonitasoft.engine.business.data.BusinessDataRepositoryException;\nimport org.bonitasoft.engine.business.data.InvalidBusinessDataModelException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.tenant.TenantResource;\n\n/**\n * This API gives access to tenant administration tasks.\n *\n * @author Matthieu Chaffotte\n * @author Baptiste Mesta\n */\npublic interface TenantAdministrationAPI {\n\n    /** A String key to designate the functional scope of updating the BDM */\n    public static final String UPDATE_BDM = \"UpdateBDM\";\n\n    /**\n     * @return\n     *         true if the tenant is paused.\n     * @deprecated since 9.0.0, use {@link MaintenanceAPI#getMaintenanceDetails()} instead.\n     */\n    @Deprecated(since = \"9.0.0\", forRemoval = true)\n    boolean isPaused();\n\n    /**\n     * Pause the tenant so nothing is executed anymore.\n     * when the tenant is paused:\n     * Only technical user can login when the tenant is paused.\n     * All users connected are disconnected (apart from the technical user).\n     * Only IdentityAPI and ProfileAPI are accessible.\n     *\n     * @deprecated since 9.0.0, use {@link MaintenanceAPI#enableMaintenanceMode()} instead.\n     * @throws org.bonitasoft.engine.exception.UpdateException\n     *         if the tenant cannot be paused.\n     */\n    @Deprecated(since = \"9.0.0\", forRemoval = true)\n    void pause() throws UpdateException;\n\n    /**\n     * Resume the tenant to a normal state after a pause.\n     *\n     * @deprecated since 9.0.0, use {@link MaintenanceAPI#disableMaintenanceMode()} instead.\n     * @throws org.bonitasoft.engine.exception.UpdateException\n     *         if the tenant cannot be resumed.\n     */\n    @Deprecated(since = \"9.0.0\", forRemoval = true)\n    void resume() throws UpdateException;\n\n    /**\n     * Uninstalls the business data model.\n     *\n     * @throws BusinessDataRepositoryDeploymentException\n     *         if the deployment cannot be fulfilled completely.\n     */\n    void uninstallBusinessDataModel() throws BusinessDataRepositoryDeploymentException;\n\n    /**\n     * Update the business data model.\n     *\n     * @param zip\n     *        the binary content of the business object model.\n     * @return the version of the Business Data Model just deployed.\n     * @throws InvalidBusinessDataModelException\n     *         if the Business Data Model content passed as parameter is invalid.\n     * @throws BusinessDataRepositoryDeploymentException\n     *         if the deployment cannot be fulfilled completely.\n     * @deprecated as of 9.0.0. The BDM should only be updated at startup.\n     */\n    @Deprecated(since = \"9.0.0\")\n    String updateBusinessDataModel(final byte[] zip)\n            throws InvalidBusinessDataModelException, BusinessDataRepositoryDeploymentException;\n\n    /**\n     * Deletes all business data and uninstalls the business data model.\n     *\n     * @throws BusinessDataRepositoryDeploymentException\n     *         if the deployment cannot be fulfilled completely.\n     */\n    void cleanAndUninstallBusinessDataModel() throws BusinessDataRepositoryDeploymentException;\n\n    /**\n     * Returns the generated BDM zip.\n     * This zip contains the jars with BDM Pojos and DAOs.\n     * usage:\n     * byte[] clientBDMZip = getTenantAdministrationAPI().getClientBDMZip();\n     * clientBDMZip will typically contain : \"README.md\", \"example-pom.xml\", \"bdm-dao.jar\", \"bdm-model.jar\", \"bom.zip\".\n     * See online <a\n     * href=\"https://documentation.ofelia.com/bonita/latest/how-a-bdm-is-deployed#_bdm_classes_generation\">a Java\n     * code example</a>\n     * on how to extract those artefacts from the Zip file.\n     *\n     * @return zip content of the deployed client Business data model, null if no Business data model has been deployed\n     * @throws BusinessDataRepositoryException\n     *         if the Business Data Model cannot be retrieved.\n     */\n    byte[] getClientBDMZip() throws BusinessDataRepositoryException;\n\n    /**\n     * Returns the current Business Data Model version, if any, or null if no Business Data Model is currently deployed.\n     *\n     * @return the current Business Data Model version, if any, or null if no Business Data Model is currently deployed\n     * @throws BusinessDataRepositoryException\n     *         if the BDM version cannot be retrieved properly.\n     */\n    String getBusinessDataModelVersion() throws BusinessDataRepositoryException;\n\n    /**\n     * Retrieves the BDM resource, as a tenant-level resource. Or NONE if no BDM is installed.\n     *\n     * @return a <code>TenantResource</code> representing the current BDM on the current tenant,\n     *         or TenantResource.NONE if no BDM is installed.\n     * @since 7.7\n     */\n    TenantResource getBusinessDataModelResource();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/UserAPI.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.business.application.Application;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.NotFoundException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.identity.ContactData;\nimport org.bonitasoft.engine.identity.Icon;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.identity.UserCreator;\nimport org.bonitasoft.engine.identity.UserCriterion;\nimport org.bonitasoft.engine.identity.UserNotFoundException;\nimport org.bonitasoft.engine.identity.UserUpdater;\nimport org.bonitasoft.engine.identity.UserWithContactData;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\n\n/**\n * UserAPI forms part of the {@link OrganizationAPI} and gives access to all the Administration operations available on\n * Users: creation, deletion, certain\n * specific getXXX() methods, generic search methods, etc...\n * It also to retrieve user ContactData.\n *\n * @author Feng Hui\n * @author Matthieu Chaffotte\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n * @see ContactData\n * @see SearchResult SearchResult for general knowledege on Search mechanism in Bonita.\n */\npublic interface UserAPI {\n\n    /**\n     * Creates a user.\n     * The password can't be empty.\n     *\n     * @param userName\n     *        The name of the user\n     * @param password\n     *        The password of the user\n     * @return The created user\n     * @throws AlreadyExistsException\n     *         If the name is already taken by an existing user\n     * @throws CreationException\n     *         If an exception occurs during the user creation\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    User createUser(String userName, String password) throws AlreadyExistsException, CreationException;\n\n    /**\n     * Creates a user.\n     * The password can't be empty.\n     *\n     * @param userName\n     *        The name of the user\n     * @param password\n     *        The password of the user\n     * @param firstName\n     *        The first name of the user\n     * @param lastName\n     *        The last name of the user\n     * @return The created user\n     * @throws AlreadyExistsException\n     *         If the name is already taken by an existing user\n     * @throws CreationException\n     *         If an exception occurs during the user creation\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    User createUser(String userName, String password, String firstName, String lastName)\n            throws AlreadyExistsException, CreationException;\n\n    /**\n     * Creates a user.\n     * It takes the values of the creator in order to create the user.\n     *\n     * @param creator\n     *        The user to create\n     * @return The created user\n     * @throws AlreadyExistsException\n     *         If the name is already taken by an existing user\n     * @throws CreationException\n     *         If an exception occurs during the user creation\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    User createUser(UserCreator creator) throws AlreadyExistsException, CreationException;\n\n    /**\n     * Updates the user according to the updater values.\n     * <p>\n     * This method also allow to update the icon of the user.\n     * When you update it, the iconId will be set on the user and you can later get it using\n     * {@link IdentityAPI#getIcon(long)}.\n     * Changing the content of the icon will create a new icon and change the iconId of the user.\n     * </p>\n     *\n     * @param userId\n     *        The identifier of the user\n     * @param updater\n     *        The user updater\n     * @return The updated user\n     * @throws UserNotFoundException\n     *         If the user identifier does not refer to an existing user\n     * @throws UpdateException\n     *         If an exception occurs during the user update\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    User updateUser(long userId, UserUpdater updater) throws UserNotFoundException, UpdateException;\n\n    /**\n     * Deletes the user.\n     * </p>\n     * <p>Use this method with caution: some artifacts like {@link Application}s and {@link DesignProcessDefinition}s\n     * may present display problems in the Bonita\n     * BPM Portal if the referenced user was deleted. Note that you can disable a user instead of deleting it. To do so,\n     * use the method\n     * {@link #updateUser(long, UserUpdater)} to set the attribute 'enabled' to false</p>.\n     *\n     * @param userId\n     *        The identifier of the user\n     * @throws DeletionException\n     *         If an exception occurs during the user deletion\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @see #updateUser(long, UserUpdater)\n     * @see Application\n     * @see DesignProcessDefinition\n     * @since 6.0\n     */\n    void deleteUser(long userId) throws DeletionException;\n\n    /**\n     * Deletes the user.\n     * </p>\n     * <p>Use this method with caution: some artifacts like {@link Application}s and {@link DesignProcessDefinition}s\n     * may present display problems in the Bonita\n     * BPM Portal if the referenced user was deleted. Note that you can disable a user instead of deleting it. To do so,\n     * use the method\n     * {@link #updateUser(long, UserUpdater)} to set the attribute 'enabled' to false</p>.\n     *\n     * @param userName\n     *        The name of the user\n     * @throws DeletionException\n     *         If an exception occurs during the user deletion\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @see #updateUser(long, UserUpdater)\n     * @see Application\n     * @see DesignProcessDefinition\n     * @since 6.0\n     */\n    void deleteUser(String userName) throws DeletionException;\n\n    /**\n     * Deletes the users.\n     * </p>\n     * <p>Use this method with caution: some artifacts like {@link Application}s and {@link DesignProcessDefinition}s\n     * may present display problems in the Bonita\n     * BPM Portal if the referenced user was deleted. Note that you can disable a user instead of deleting it. To do so,\n     * use the method\n     * {@link #updateUser(long, UserUpdater)} to set the attribute 'enabled' to false</p>.\n     *\n     * @param userIds\n     *        The identifiers of the users\n     * @throws DeletionException\n     *         If an exception occurs during the user deletion\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @see #updateUser(long, UserUpdater)\n     * @see Application\n     * @see DesignProcessDefinition\n     * @since 6.0\n     */\n    void deleteUsers(List<Long> userIds) throws DeletionException;\n\n    /**\n     * Retrieves the user.\n     * It throws a {@link UserNotFoundException} if the user identifier equals the technical user identifier (-1).\n     *\n     * @param userId\n     *        The identifier of the user\n     * @return The searched user\n     * @throws UserNotFoundException\n     *         If the user identifier does not refer to an existing user\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs during the user retrieving\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    User getUser(long userId) throws UserNotFoundException;\n\n    /**\n     * Retrieves the user.\n     *\n     * @param userName\n     *        The name of the user\n     * @return The role\n     * @throws UserNotFoundException\n     *         If the user name does not refer to an existing user\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs during the role retrieving\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    User getUserByUserName(String userName) throws UserNotFoundException;\n\n    /**\n     * Retrieves the professional details of the user.\n     *\n     * @param userId\n     *        The identifier of the user\n     * @return The user and the professional details\n     * @throws UserNotFoundException\n     *         If the user identifier does not refer to an existing user, or is -1 (the technical user identifier)\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs while retrieving the user\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.1\n     */\n    UserWithContactData getUserWithProfessionalDetails(long userId) throws UserNotFoundException;\n\n    /**\n     * Retrieves the contact data (personal or professional) of the user.\n     *\n     * @param userId\n     *        The identifier of the user\n     * @param personal\n     *        true if the contact data is the personal one\n     * @return The contact data\n     * @throws UserNotFoundException\n     *         If the user name does not refer to an existing user\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs during the role retrieving\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    ContactData getUserContactData(long userId, boolean personal) throws UserNotFoundException;\n\n    /**\n     * Returns the total number of users.\n     *\n     * @return The total number of users\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs during the count retrieving\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    long getNumberOfUsers();\n\n    /**\n     * Retrieves the paginated list of users. It retrieves from the startIndex to the startIndex + maxResults.\n     *\n     * @param startIndex\n     *        The start index\n     * @param maxResults\n     *        The max number of users\n     * @param criterion\n     *        The sorting criterion\n     * @return The paginated list of users\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs during the user retrieving\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    List<User> getUsers(int startIndex, int maxResults, UserCriterion criterion);\n\n    /**\n     * Retrieves the users. The map contains the couples userId/User.\n     * If a user does not exists, no exception is thrown and no value is added in the map.\n     *\n     * @param userIds\n     *        The identifiers of the users\n     * @return The users\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs during the user retrieving\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    Map<Long, User> getUsers(List<Long> userIds);\n\n    /**\n     * Retrieves the identifiers of the named users. The map contains the couples userName/User.\n     * If a user does not exists, no exception is thrown and no value is added in the map.\n     *\n     * @param userNames\n     *        The names of the users\n     * @return The users ordered by the user name ascending\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs during the user retrieving\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.1\n     */\n    Map<String, User> getUsersByUsernames(List<String> userNames);\n\n    /**\n     * Searches users according to the criteria containing in the options.\n     *\n     * @param options\n     *        The search criteria\n     * @return The search result\n     * @throws SearchException\n     *         If an exception occurs during the user searching\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    SearchResult<User> searchUsers(SearchOptions options) throws SearchException;\n\n    /**\n     * Returns the total number of users of the role.\n     *\n     * @param roleId\n     *        The identifier of the role\n     * @return The total number of users of the role\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs during the count retrieving\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    long getNumberOfUsersInRole(long roleId);\n\n    /**\n     * Retrieves the paginated list of users, regardless if they are active or not, in a given role.\n     * It retrieves from the startIndex to the startIndex + maxResults.\n     *\n     * @param roleId\n     *        The identifier of the role\n     * @param startIndex\n     *        The start index\n     * @param maxResults\n     *        The max number of users\n     * @param criterion\n     *        The sorting criterion\n     * @return The paginated list of users\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs during the retrieving of users\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    List<User> getUsersInRole(long roleId, int startIndex, int maxResults, UserCriterion criterion);\n\n    /**\n     * Retrieves the paginated list of active users in role.\n     * It retrieves from the startIndex to the startIndex + maxResults.\n     *\n     * @param roleId\n     *        The identifier of the role\n     * @param startIndex\n     *        The start index\n     * @param maxResults\n     *        The max number of users\n     * @param criterion\n     *        The sorting criterion\n     * @return The paginated list of users\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs during the retrieving of users\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 7.6\n     */\n    List<User> getActiveUsersInRole(long roleId, int startIndex, int maxResults, UserCriterion criterion);\n\n    /**\n     * Retrieves the paginated list of inactive users in role.\n     * It retrieves from the startIndex to the startIndex + maxResults.\n     *\n     * @param roleId\n     *        The identifier of the role\n     * @param startIndex\n     *        The start index\n     * @param maxResults\n     *        The max number of users\n     * @param criterion\n     *        The sorting criterion\n     * @return The paginated list of users\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs during the retrieving of users\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 7.6\n     */\n    List<User> getInactiveUsersInRole(long roleId, int startIndex, int maxResults, UserCriterion criterion);\n\n    /**\n     * Returns the total number of users of the group.\n     *\n     * @param groupId\n     *        The identifier of the group\n     * @return The total number of users of the group\n     * @throws BonitaException\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs during the count retrieving\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    long getNumberOfUsersInGroup(long groupId) throws BonitaException;\n\n    /**\n     * Retrieves the paginated list of users, both active and inactive, in a given group.\n     * It retrieves from the startIndex to the startIndex + maxResults.\n     *\n     * @param groupId\n     *        The identifier of the group\n     * @param startIndex\n     *        The start index\n     * @param maxResults\n     *        The max number of users\n     * @param criterion\n     *        The sorting criterion\n     * @return The paginated list of users\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs during the retrieving of users\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 6.0\n     */\n    List<User> getUsersInGroup(long groupId, int startIndex, int maxResults, UserCriterion criterion);\n\n    /**\n     * Retrieves the paginated list of active users in groups.\n     * It retrieves from the startIndex to the startIndex + maxResults.\n     *\n     * @param groupId\n     *        The identifier of the group\n     * @param startIndex\n     *        The start index\n     * @param maxResults\n     *        The max number of users\n     * @param criterion\n     *        The sorting criterion\n     * @return The paginated list of users\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs during the retrieving of users\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 7.6\n     */\n    List<User> getActiveUsersInGroup(long groupId, int startIndex, int maxResults, UserCriterion criterion);\n\n    /**\n     * Retrieves the paginated list of inactive users in groups.\n     * It retrieves from the startIndex to the startIndex + maxResults.\n     *\n     * @param groupId\n     *        The identifier of the group\n     * @param startIndex\n     *        The start index\n     * @param maxResults\n     *        The max number of users\n     * @param criterion\n     *        The sorting criterion\n     * @return The paginated list of users\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs during the retrieving of users\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 7.6\n     */\n    List<User> getInactiveUsersInGroup(long groupId, int startIndex, int maxResults, UserCriterion criterion);\n\n    /**\n     * Retrieves the paginated list of users of a manager.\n     * It retrieves from the startIndex to the startIndex + maxResults.\n     *\n     * @param managerId\n     *        The identifier of the manager\n     * @param startIndex\n     *        The start index\n     * @param maxResults\n     *        The max number of users\n     * @param criterion\n     *        The sorting criterion\n     * @return The paginated list of users\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs during the retrieving of users\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 7.6\n     */\n    List<User> getUsersWithManager(long managerId, int startIndex, int maxResults, UserCriterion criterion);\n\n    /**\n     * Retrieves the paginated list of active users of a manager.\n     * It retrieves from the startIndex to the startIndex + maxResults.\n     *\n     * @param managerId\n     *        The identifier of the manager\n     * @param startIndex\n     *        The start index\n     * @param maxResults\n     *        The max number of users\n     * @param criterion\n     *        The sorting criterion\n     * @return The paginated list of users\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs during the retrieving of users\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 7.6\n     */\n    List<User> getActiveUsersWithManager(long managerId, int startIndex, int maxResults, UserCriterion criterion);\n\n    /**\n     * Retrieves the paginated list of inactive users of a manager.\n     * It retrieves from the startIndex to the startIndex + maxResults.\n     *\n     * @param managerId\n     *        The identifier of the manager\n     * @param startIndex\n     *        The start index\n     * @param maxResults\n     *        The max number of users\n     * @param criterion\n     *        The sorting criterion\n     * @return The paginated list of users\n     * @throws org.bonitasoft.engine.exception.RetrieveException\n     *         If an exception occurs during the retrieving of users\n     * @throws org.bonitasoft.engine.session.InvalidSessionException\n     *         If the session is invalid (expired, unknown, ...)\n     * @since 7.6\n     */\n    List<User> getInactiveUsersWithManager(long managerId, int startIndex, int maxResults, UserCriterion criterion);\n\n    /**\n     * Retrieves the list of user identifiers containing the chosen custom user information with the given value.\n     *\n     * @param infoName\n     *        The custom user information name\n     * @param infoValue\n     *        The custom user information value\n     * @param usePartialMatch\n     *        Defines whether the custom user information value should use a partial match.\n     * @param startIndex\n     *        The start index (the first valid value is zero)\n     * @param maxResults\n     *        The max number of user identifiers to be retrieved\n     * @return the list of user identifiers containing the chosen custom user information with the given value.\n     * @since 6.3.2\n     */\n    List<Long> getUserIdsWithCustomUserInfo(String infoName, String infoValue, boolean usePartialMatch, int startIndex,\n            int maxResults);\n\n    /**\n     * get the icon having specified id\n     *\n     * @param id\n     *        the id of the icon\n     * @return the icon with its content\n     * @throws NotFoundException\n     * @since 7.3.0\n     */\n    Icon getIcon(long id) throws NotFoundException;\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/impl/ClientInterceptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport static java.util.logging.Level.FINEST;\n\nimport java.io.Serializable;\nimport java.lang.reflect.InvocationHandler;\nimport java.lang.reflect.Method;\nimport java.rmi.RemoteException;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.logging.Logger;\n\nimport org.bonitasoft.engine.api.internal.ServerAPI;\nimport org.bonitasoft.engine.api.internal.ServerWrappedException;\nimport org.bonitasoft.engine.session.Session;\n\n/**\n * API classes given to the client are proxies. That class is the {@link InvocationHandler} given when proxying the\n * client api classes\n * Its purpose is to keep the client session and give it to the {@link ServerAPI} call\n */\npublic class ClientInterceptor implements InvocationHandler, Serializable {\n\n    private static final long serialVersionUID = -6284726148297940515L;\n\n    private final ServerAPI api;\n\n    private final String interfaceName;\n\n    private final Session session;\n\n    private static final Logger LOGGER = Logger.getLogger(ClientInterceptor.class.getName());\n\n    public ClientInterceptor(final String interfaceName, final ServerAPI api, final Session session) {\n        this.api = api;\n        this.interfaceName = interfaceName;\n        this.session = session;\n    }\n\n    public ClientInterceptor(final String interfaceName, final ServerAPI api) {\n        this(interfaceName, api, null);\n    }\n\n    @Override\n    public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {\n        try {\n            final Class<?>[] parameterTypes = method.getParameterTypes();\n            final List<String> classNameParameters = new ArrayList<>();\n            for (final Class<?> parameterType : parameterTypes) {\n                classNameParameters.add(parameterType.getName());\n            }\n            if (LOGGER.isLoggable(FINEST)) {\n                LOGGER.log(FINEST, \"Calling method \" + method.getName() + \" on API \" + this.api.getClass().getName());\n            }\n            Map<String, Serializable> options;\n            options = new HashMap<>();\n            options.put(\"session\", this.session);\n            // invoke ServerAPI unique method\n            final Object object = this.api.invokeMethod(options, this.interfaceName, method.getName(),\n                    classNameParameters, args);\n            if (LOGGER.isLoggable(FINEST)) {\n                LOGGER.log(FINEST, \"Quitting method \" + method.getName() + \" on API \" + this.api.getClass().getName());\n            }\n            return object;\n        } catch (final ServerWrappedException | RemoteException e) {\n            if (LOGGER.isLoggable(FINEST)) {\n                LOGGER.log(FINEST, \"Quitting method \" + method.getName() + \" on API \" + this.api.getClass().getName()\n                        + \" with exception \" + e.getMessage());\n            }\n            throw e.getCause();\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/internal/ServerAPI.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.internal;\n\nimport java.io.Serializable;\nimport java.rmi.Remote;\nimport java.rmi.RemoteException;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * This interface defines a common entry point for APIs to be called by engine client.\n * Depending on what is the client connection mode, the client retrieve an implementation of this interface to call the\n * server side API.\n */\npublic interface ServerAPI extends Serializable, Remote {\n\n    Object invokeMethod(final Map<String, Serializable> options, final String apiInterfaceName, final String methodName,\n            final List<String> classNameParameters, final Object[] parametersValues)\n            throws ServerWrappedException, RemoteException;\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/internal/ServerWrappedException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.internal;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class ServerWrappedException extends Exception {\n\n    private static final long serialVersionUID = 2098815926771801085L;\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public ServerWrappedException(final Throwable cause) {\n        super(cause);\n    }\n\n    /**\n     * Constructs a new exception with the specified detail message and cause.\n     *\n     * @param message\n     *        The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method).\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public ServerWrappedException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/internal/package-info.java",
    "content": "/**\n * Copyright (C) 2015 BonitaSoft S.A.\n * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n/**\n * <p>\n * contains {@link org.bonitasoft.engine.api.internal.ServerAPI} interface\n * </p>\n */\npackage org.bonitasoft.engine.api.internal;\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/package-info.java",
    "content": "/**\n * Copyright (C) 2015 BonitaSoft S.A.\n * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n/**\n * <p>\n * The Bonita project has a clear separation between the API the users should be interacting with and the actual\n * implementation classes.\n * <br>\n * The public API exposes most of the features we believe \"normal\" users can safely use and should remain rather stable\n * across releases.\n * <br>\n * Expert users can still access internal classes but should be aware that they should know what they are doing and that\n * the internal API might still change in the future.\n * </p>\n */\npackage org.bonitasoft.engine.api;\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/permission/APICallContext.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.permission;\n\nimport java.io.Serializable;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Context of a call made on a REST API\n *\n * @author Baptiste Mesta\n */\npublic class APICallContext implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n    public static final String FILTER_KEY = \"f\";\n    public static final String SEARCH_TERM_KEY = \"s\";\n    public static final String DELIMITER = \"&\";\n\n    /*\n     * The http method\n     */\n    private String method;\n\n    /*\n     * the api name\n     */\n    private String apiName;\n\n    /*\n     * the resource name\n     */\n    private String resourceName;\n\n    /*\n     * id of the resource, can be multiple\n     */\n    private String resourceId;\n\n    /*\n     * query string of the api call\n     */\n    private String queryString;\n\n    /*\n     * body of the api call\n     */\n    private String body;\n    private Map<String, String> filters = new HashMap<>();\n    private final Map<String, String[]> parameters = new HashMap<>();\n    private String searchTerm;\n\n    /**\n     * @param method\n     *        the HTTP method\n     * @param apiName\n     *        the name of the api\n     * @param resourceName\n     *        the name of the resource\n     * @param resourceId\n     *        the id (or multiple id) of the resource if specified\n     */\n    public APICallContext(String method, String apiName, String resourceName, String resourceId) {\n        this(method, apiName, resourceName, resourceId, null, null);\n    }\n\n    /**\n     * @param method\n     *        the HTTP method\n     * @param apiName\n     *        the name of the api\n     * @param resourceName\n     *        the name of the resource\n     * @param resourceId\n     *        the id (or multiple id) of the resource if specified\n     * @param queryString\n     *        the query string of the api context if specified\n     * @param body\n     *        the body string of the api context if specified\n     */\n    public APICallContext(String method, String apiName, String resourceName, String resourceId, String queryString,\n            String body) {\n        this.method = method;\n        this.apiName = apiName;\n        this.resourceName = resourceName;\n        this.resourceId = resourceId;\n        this.queryString = queryString;\n        parseQueryString(queryString);\n        this.body = body;\n    }\n\n    private void parseQueryString(String queryString) {\n        this.filters = new HashMap<>();\n        if (queryString == null) {\n            return;\n        }\n        for (String element : queryString.split(DELIMITER)) {\n            int indexOfEquals = element.indexOf(\"=\");\n            if (indexOfEquals > 0 && indexOfEquals + 1 < element.length()) {\n                String key = element.substring(0, indexOfEquals);\n                String value = element.substring(indexOfEquals + 1);\n                if (FILTER_KEY.equals(key)) {\n                    addFilter(value);\n                } else if (SEARCH_TERM_KEY.equals(key)) {\n                    searchTerm = value;\n                }\n                if (parameters.containsKey(key)) {\n                    String[] currentValue = parameters.get(key);\n                    String[] newValue = Arrays.copyOf(currentValue, currentValue.length + 1);\n                    newValue[currentValue.length] = value;\n                    parameters.put(key, newValue);\n                } else {\n                    parameters.put(key, new String[] { value });\n                }\n            }\n        }\n    }\n\n    private void addFilter(String value) {\n        int indexOfEncodedEquals = Math.max(value.indexOf(\"%3d\"), value.indexOf(\"%3D\"));\n        if (indexOfEncodedEquals > 0) {\n            addFilterIfValueNotBlank(value, indexOfEncodedEquals, 3);\n        } else if (value.indexOf(\"=\") > 0) {\n            addFilterIfValueNotBlank(value, value.indexOf(\"=\"), 1);\n        }\n    }\n\n    private void addFilterIfValueNotBlank(String value, int indexOfEquals, int separatorSize) {\n        if (indexOfEquals + separatorSize < value.length()) {\n            filters.put(value.substring(0, indexOfEquals),\n                    value.substring(indexOfEquals + separatorSize));\n        }\n    }\n\n    /**\n     * empty constructor\n     */\n    public APICallContext() {\n    }\n\n    public String getMethod() {\n        return method;\n    }\n\n    /**\n     * @return true if method is GET\n     */\n    public boolean isGET() {\n        return \"GET\".equals(method);\n    }\n\n    /**\n     * @return true if method is PUT\n     */\n    public boolean isPUT() {\n        return \"PUT\".equals(method);\n    }\n\n    /**\n     * @return true if method is POST\n     */\n    public boolean isPOST() {\n        return \"POST\".equals(method);\n    }\n\n    /**\n     * @return true if method is DELETE\n     */\n    public boolean isDELETE() {\n        return \"DELETE\".equals(method);\n    }\n\n    public void setMethod(String method) {\n        this.method = method;\n    }\n\n    /**\n     * @return the name of API the resource is part of\n     */\n    public String getApiName() {\n        return apiName;\n    }\n\n    public void setApiName(String apiName) {\n        this.apiName = apiName;\n    }\n\n    /**\n     * @return the name of the main resource (if a subresource is queried, its name will be part of the compound\n     *         resource ID)\n     */\n    public String getResourceName() {\n        return resourceName;\n    }\n\n    public void setResourceName(String resourceName) {\n        this.resourceName = resourceName;\n    }\n\n    /**\n     * @return the list of identifiers composing the resource ID. This is useful for compound resource IDs (the\n     *         identifiers are separated with a / in the URL)\n     */\n    public List<String> getCompoundResourceId() {\n        return resourceId == null ? Collections.emptyList() : Arrays.asList(resourceId.split(\"/\"));\n    }\n\n    /**\n     * @return the full resource ID as a string. If the resource ID is compound, it will return as it is in the URL\n     *         (with a / as separator between the identifiers)\n     */\n    public String getResourceId() {\n        return resourceId;\n    }\n\n    public void setResourceId(String resourceId) {\n        this.resourceId = resourceId;\n    }\n\n    /**\n     * @return the full query string\n     */\n    public String getQueryString() {\n        return queryString;\n    }\n\n    public void setQueryString(String queryString) {\n        this.queryString = queryString;\n        parseQueryString(queryString);\n    }\n\n    /**\n     * @return the body of the request as a String\n     */\n    public String getBody() {\n        return body;\n    }\n\n    public void setBody(String body) {\n        this.body = body;\n    }\n\n    /**\n     * @return a Map containing the filters of the request as they are set in the query string (f parameter)\n     */\n    public Map<String, String> getFilters() {\n        return filters;\n    }\n\n    /**\n     * @return a string containing the search term of the request as it is set in the query string (s parameter)\n     */\n    public String getSearchTerm() {\n        return searchTerm;\n    }\n\n    /**\n     * @return a Map containing the parameters of the request as they are set in the query string (including the filters\n     *         and search terms)\n     */\n    public Map<String, String[]> getParameters() {\n        return parameters;\n    }\n\n    @Override\n    public String toString() {\n        return \"APICallContext{\" +\n                \"method='\" + method + '\\'' +\n                \", apiName='\" + apiName + '\\'' +\n                \", resourceName='\" + resourceName + '\\'' +\n                \", resourceId='\" + resourceId + '\\'' +\n                \", queryString='\" + queryString + '\\'' +\n                \", body='\" + body + '\\'' +\n                '}';\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (o == null || getClass() != o.getClass())\n            return false;\n\n        APICallContext that = (APICallContext) o;\n\n        if (apiName != null ? !apiName.equals(that.apiName) : that.apiName != null)\n            return false;\n        if (body != null ? !body.equals(that.body) : that.body != null)\n            return false;\n        if (filters != null ? !filters.equals(that.filters) : that.filters != null)\n            return false;\n        if (parameters != null ? !parameters.equals(that.parameters) : that.parameters != null)\n            return false;\n        if (method != null ? !method.equals(that.method) : that.method != null)\n            return false;\n        if (queryString != null ? !queryString.equals(that.queryString) : that.queryString != null)\n            return false;\n        if (resourceId != null ? !resourceId.equals(that.resourceId) : that.resourceId != null)\n            return false;\n        if (resourceName != null ? !resourceName.equals(that.resourceName) : that.resourceName != null)\n            return false;\n        if (searchTerm != null ? !searchTerm.equals(that.searchTerm) : that.searchTerm != null)\n            return false;\n\n        return true;\n    }\n\n    @Override\n    public int hashCode() {\n        int result = method != null ? method.hashCode() : 0;\n        result = 31 * result + (apiName != null ? apiName.hashCode() : 0);\n        result = 31 * result + (resourceName != null ? resourceName.hashCode() : 0);\n        result = 31 * result + (resourceId != null ? resourceId.hashCode() : 0);\n        result = 31 * result + (queryString != null ? queryString.hashCode() : 0);\n        result = 31 * result + (body != null ? body.hashCode() : 0);\n        result = 31 * result + (filters != null ? filters.hashCode() : 0);\n        result = 31 * result + (parameters != null ? parameters.hashCode() : 0);\n        result = 31 * result + (searchTerm != null ? searchTerm.hashCode() : 0);\n        return result;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/permission/PermissionRule.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.permission;\n\nimport org.bonitasoft.engine.api.APIAccessor;\nimport org.bonitasoft.engine.api.Logger;\nimport org.bonitasoft.engine.session.APISession;\n\n/**\n * Class to extend when implementing permission rule to be checked by {@link org.bonitasoft.engine.api.PermissionAPI}\n *\n * @author Baptiste Mesta\n */\npublic interface PermissionRule {\n\n    /**\n     * Called by the engine when using\n     * {@link org.bonitasoft.engine.api.PermissionAPI#isAuthorized(APICallContext)}\n     *\n     * @param apiSession\n     *        the api session from the user doing the api call\n     * @param apiCallContext\n     *        the context of the api call\n     * @param apiAccessor\n     *        an accessor to call apis\n     * @return\n     *         true if the user is allowed to access the api or false otherwise\n     */\n    boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger);\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/platform/PlatformInformationAPI.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.platform;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.platform.PlatformNotFoundException;\n\npublic interface PlatformInformationAPI {\n\n    Map<String, String> getPlatformInformation() throws PlatformNotFoundException;\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/result/ExecutionResult.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.result;\n\nimport static java.util.Arrays.asList;\nimport static java.util.Objects.requireNonNull;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.bonitasoft.engine.api.result.Status.Level;\n\n/**\n * Contains a list of {@link Status} as a result of an API execution\n */\npublic class ExecutionResult implements Serializable {\n\n    public static final ExecutionResult OK = new ExecutionResult();\n    private List<Status> statuses = new ArrayList<>();\n\n    public ExecutionResult(Status... statuses) {\n        this(asList(requireNonNull(statuses)));\n    }\n\n    public ExecutionResult(List<Status> statusList) {\n        this.statuses.addAll(requireNonNull(statusList));\n    }\n\n    public void addStatus(Status... statuses) {\n        this.statuses.addAll(asList(requireNonNull(statuses)));\n    }\n\n    public boolean isOk() {\n        return statuses.isEmpty() || statuses.stream()\n                .allMatch(status -> status.getLevel() == Level.OK);\n    }\n\n    public boolean hasErrors() {\n        return statuses.stream()\n                .anyMatch(status -> status.getLevel() == Level.ERROR);\n    }\n\n    public boolean hasWarnings() {\n        return statuses.stream()\n                .anyMatch(status -> status.getLevel() == Level.WARNING);\n    }\n\n    public boolean hasInfo() {\n        return statuses.stream()\n                .anyMatch(status -> status.getLevel() == Level.INFO);\n    }\n\n    public List<Status> getErrors() {\n        return statuses.stream()\n                .filter(status -> status.getLevel() == Level.ERROR)\n                .collect(Collectors.toList());\n    }\n\n    public List<Status> getWarnings() {\n        return statuses.stream()\n                .filter(status -> status.getLevel() == Level.WARNING)\n                .collect(Collectors.toList());\n    }\n\n    public List<Status> getInfo() {\n        return statuses.stream()\n                .filter(status -> status.getLevel() == Level.INFO)\n                .collect(Collectors.toList());\n    }\n\n    public List<Status> getAllStatus() {\n        return Collections.unmodifiableList(statuses);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/result/Status.java",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.result;\n\nimport static java.util.Objects.requireNonNull;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport lombok.ToString;\n\n/**\n * A status represents a generic result of some action.\n * It has a severity level, a technical message (non internationalized),\n * a unique status code used for internationalization and\n * an optional context saving the implicated item (eg: a data or process name)\n */\n@ToString\npublic class Status implements Serializable {\n\n    public enum Level {\n        OK, ERROR, WARNING, INFO\n    }\n\n    private Level level;\n\n    private String message;\n\n    private StatusContext context;\n\n    private StatusCode code;\n\n    public static Status okStatus() {\n        return new Status(Level.OK, StatusCode.OK, \"\", null);\n    }\n\n    public static Status errorStatus(StatusCode code, String message) {\n        return errorStatus(code, message, null);\n    }\n\n    public static Status errorStatus(StatusCode code, String message, Map<String, Serializable> context) {\n        return new Status(Level.ERROR, code, message, context);\n    }\n\n    public static Status warningStatus(StatusCode code, String message) {\n        return warningStatus(code, message, null);\n    }\n\n    public static Status warningStatus(StatusCode code, String message, Map<String, Serializable> context) {\n        return new Status(Level.WARNING, code, message, context);\n    }\n\n    public static Status infoStatus(StatusCode code, String message) {\n        return infoStatus(code, message, null);\n    }\n\n    public static Status infoStatus(StatusCode code, String message, Map<String, Serializable> context) {\n        return new Status(Level.INFO, code, message, context);\n    }\n\n    private Status(Level level, StatusCode code, String message, Map<String, Serializable> context) {\n        this.level = level;\n        this.code = code;\n        this.message = requireNonNull(message);\n        this.context = (context != null ? new StatusContext(context) : new StatusContext());\n    }\n\n    public StatusCode getCode() {\n        return code;\n    }\n\n    public String getMessage() {\n        return message;\n    }\n\n    public Level getLevel() {\n        return level;\n    }\n\n    public StatusContext getContext() {\n        return context;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/result/StatusCode.java",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.result;\n\npublic enum StatusCode {\n\n    /*****************************\n     * ACCESS CONTROL STATUS CODE\n     *****************************/\n\n    /**\n     * Everything is fine\n     */\n    OK,\n\n    /**\n     * Failed to deploy BDM Access Control\n     */\n    BDM_ACCESS_CONTROL_INSTALLATION_ERROR,\n\n    /**\n     * Uploaded file is a null byte array. Not supported.\n     */\n    BDM_ACCESS_CONTROL_FILE_EMPTY,\n\n    /**\n     * Detected a duplication of attributes in a BDM Access Control rule\n     */\n    BDM_ACCESS_CONTROL_ATTRIBUTES_DUPLICATION_ERROR,\n\n    /**\n     * Detected a duplication of static profiles in a BDM Access Control rule\n     */\n    BDM_ACCESS_CONTROL_STATIC_PROFILES_DUPLICATION_ERROR,\n\n    /**\n     * Detected a duplication of rule name in a BDM Access Control set of Business Object rules\n     */\n    BDM_ACCESS_CONTROL_RULE_NAME_DUPLICATION_ERROR,\n\n    /**\n     * A BDM Access Control rule refers to an unknown Business Object attribute\n     */\n    BDM_ACCESS_CONTROL_UNKNOWN_ATTRIBUTE_REFERENCE,\n\n    /**\n     * A BDM Access Control rule refers to an unknown Business Object\n     */\n    BDM_ACCESS_CONTROL_UNKNOWN_BUSINESS_OBJECT_REFERENCE,\n\n    /**\n     * A BDM Access Control rule refers to an unknown profile\n     */\n    BDM_ACCESS_CONTROL_UNKNOWN_PROFILE_REFERENCE,\n\n    /**\n     * A BDM Access Control rule grants access to no attribute at all\n     */\n    BDM_ACCESS_CONTROL_ZERO_ATTRIBUTE_GRANTED,\n\n    /**\n     * A Business Object has no rule defined at all\n     */\n    BDM_ACCESS_CONTROL_BUSINESS_OBJECT_WITHOUT_RULE,\n\n    /**\n     * A Business Object has empty rules\n     */\n    BDM_ACCESS_CONTROL_BUSINESS_OBJECT_WITH_EMPTY_RULES,\n\n    /**\n     * A BDM Access Control rule grants access to no profile at all\n     */\n    BDM_ACCESS_CONTROL_ZERO_PROFILE_GRANTED,\n\n    /**\n     * There is no BDM installed\n     */\n    BDM_NOT_INSTALLED,\n\n    /**\n     * An object in the BDM is referenced as if it was in a composition relationship, when it is not.\n     */\n    BDM_ACCESS_CONTROL_NON_COMPOSED_OBJECT_REFERENCED_IN_COMPOSITION,\n\n    /**\n     * An object used in composition in the BDM has rules on the first level of the Access Control file.\n     */\n    BDM_ACCESS_CONTROL_FIRST_LEVEL_RULES_ON_COMPOSED_OBJECT,\n\n    /**\n     * The naming of rules and objects in the access control file is inconsistent\n     */\n    BDM_ACCESS_CONTROL_INCONSISTENT_NAME_IN_FILE,\n\n    /**********************************\n     * Business Data Model Status Code\n     **********************************/\n\n    DUPLICATE_CONSTRAINT_OR_INDEX_NAME,\n\n    UNIQUE_CONSTRAINT_WITHOUT_NAME,\n\n    INVALID_SQL_IDENTIFIER_NAME,\n\n    DISCOURAGED_SQL_IDENTIFIER_NAME,\n\n    UNIQUE_CONSTRAINT_WITHOUT_FIELD,\n\n    FIELD_WITHOUT_NAME,\n\n    QUERY_WITHOUT_NAME,\n\n    INVALID_JAVA_IDENTIFIER_NAME,\n\n    DISCOURAGED_JAVA_IDENTIFIER_NAME,\n\n    QUERY_NAME_LENGTH_TO_HIGH,\n\n    QUERY_WITHOUT_CONTENT,\n\n    QUERY_WITHOUT_RETURN_TYPE,\n\n    QUERY_PARAMETER_WITHOUT_NAME,\n\n    FORBIDDEN_QUERY_PARAMETER_NAME,\n\n    QUERY_PARAMETER_WITHOUT_CLASS_NAME,\n\n    INDEX_WITHOUT_NAME,\n\n    INDEX_WITHOUT_FIELD,\n\n    INVALID_FIELD_IDENTIFIER,\n\n    DISCOURAGED_FIELD_IDENTIFIER,\n\n    BUSINESS_OBJECT_WITHOUT_NAME,\n\n    RESERVED_PACKAGE_NAME,\n\n    INVALID_CHARACTER_IN_BUSINESS_OBJECT_NAME,\n\n    BUSINESS_OBJECT_WITHOUT_FIELD,\n\n    DUPLICATE_QUERY_NAME,\n\n    DUPLICATE_CONSTRAINT_NAME,\n\n    UNKNOWN_FIELD_IN_CONSTRAINT,\n\n    EMPTY_BDM,\n\n    SEVERAL_COMPOSITION_REFERENCE_FOR_A_BUSINESS_OBJECT,\n\n    CIRCULAR_COMPOSITION_REFERENCE,\n\n    BUSINESS_OBJECT_USED_IN_COMPOSITION_AND_AGGREGATION,\n\n    MULTIPLE_AGGREGATION_RELATION_TO_ITSELF,\n\n    DUPLICATE_BUSINESS_OBJECT_NAME,\n\n    /**\n     * Living application deployment\n     */\n    LIVING_APP_DEPLOYMENT,\n\n    LIVING_APP_REFERENCES_UNKNOWN_APPLICATION_PAGE,\n\n    LIVING_APP_REFERENCES_UNKNOWN_PAGE,\n\n    LIVING_APP_REFERENCES_UNKNOWN_PROFILE,\n\n    LIVING_APP_REFERENCES_UNKNOWN_LAYOUT,\n\n    LIVING_APP_REFERENCES_UNKNOWN_THEME,\n\n    PROCESS_DEPLOYMENT_CREATE_NEW,\n\n    PROCESS_DEPLOYMENT_SKIP_INSTALL,\n\n    PROCESS_DEPLOYMENT_REPLACE_EXISTING,\n\n    PROCESS_DEPLOYMENT_ENABLEMENT_OK,\n\n    PROCESS_DEPLOYMENT_ENABLEMENT_KO,\n\n    PROCESS_DEPLOYMENT_DISABLEMENT_OK,\n\n    PROCESS_DEPLOYMENT_IMPOSSIBLE_UNRESOLVED,\n\n    PAGE_DEPLOYMENT_CREATE_NEW,\n\n    PAGE_DEPLOYMENT_UPDATE_EXISTING,\n\n    /****************************\n     * Organization Status Codes\n     ***************************/\n\n    /**\n     * General warning when something goes wrong when importing Organization\n     */\n    ORGANIZATION_IMPORT_WARNING\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/result/StatusContext.java",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.result;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class StatusContext extends HashMap<String, Serializable> {\n\n    public static final String BUSINESS_OBJECT_NAME_KEY = \"businessObjectName\";\n    public static final String ACCESS_RULE_NAME_KEY = \"accessRuleName\";\n    public static final String ATTRIBUTE_NAME_KEY = \"attributeName\";\n    public static final String PROFILE_NAME_KEY = \"profileName\";\n    public static final String BDM_ARTIFACT_KEY = \"bdmArtifact\";\n    public static final String BDM_ARTIFACT_NAME_KEY = \"bdmArtifactName\";\n    public static final String INVALID_NAME_KEY = \"invalidName\";\n\n    public static final String LIVING_APPLICATION_TOKEN_KEY = \"livingApplicationToken\";\n    public static final String LIVING_APPLICATION_IMPORT_STATUS_KEY = \"livingApplicationImportStatus\";\n    public static final String LIVING_APPLICATION_INVALID_ELEMENT_NAME = \"livingApplicationInvalidElementName\";\n    public static final String LIVING_APPLICATION_INVALID_ELEMENT_TYPE = \"livingApplicationInvalidElementType\";\n\n    public static final String PROCESS_NAME_KEY = \"processName\";\n    public static final String PROCESS_VERSION_KEY = \"processVersion\";\n    public static final String PROCESS_RESOLUTION_PROBLEM_RESOURCE_TYPE_KEY = \"processResolutionProblemResourceType\";\n    public static final String PROCESS_RESOLUTION_PROBLEM_RESOURCE_ID_KEY = \"processResolutionProblemResourceId\";\n    public static final String PROCESS_RESOLUTION_PROBLEM_DESCRIPTION_KEY = \"processResolutionProblemDescription\";\n    public static final String PROCESS_DEPLOYMENT_FAILURE_REASON_KEY = \"processVersion\";\n\n    public static final String PAGE_NAME_KEY = \"pageName\";\n\n    public StatusContext() {\n        super();\n    }\n\n    public StatusContext(Map<String, Serializable> context) {\n        super(context);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/BusinessObjectModelValidationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm;\n\nimport java.io.Serial;\n\nimport org.bonitasoft.engine.api.result.Status;\nimport org.bonitasoft.engine.api.result.Status.Level;\nimport org.bonitasoft.engine.bdm.validator.ValidationStatus;\n\n/**\n * @author Romain Bioteau\n */\npublic class BusinessObjectModelValidationException extends Exception {\n\n    @Serial\n    private static final long serialVersionUID = 1L;\n\n    private final ValidationStatus validationStatus;\n\n    public BusinessObjectModelValidationException(final ValidationStatus validationStatus) {\n        this.validationStatus = validationStatus;\n    }\n\n    @Override\n    public String getMessage() {\n        final StringBuilder sb = new StringBuilder();\n        validationStatus.getStatuses().stream().filter(status -> Level.ERROR.equals(status.getLevel()))\n                .map(Status::getMessage).forEach(s -> sb.append(\"\\n- \").append(s));\n        return sb.toString();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/Entity.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm;\n\nimport java.io.Serializable;\n\n/**\n * Generic interface for a Business Object.\n *\n * @author Romain Bioteau\n * @author Matthieu Chaffotte\n * @since 7.0\n */\npublic interface Entity extends Serializable {\n\n    Long getPersistenceId();\n\n    Long getPersistenceVersion();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/dao/BusinessObjectDAO.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.dao;\n\n/**\n * Common interface for business object DAOs.\n *\n * @author Romain Bioteau\n * @since 7.0\n */\npublic interface BusinessObjectDAO {\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/lazy/LazyLoaded.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.lazy;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * @author Colin Puy\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.METHOD)\npublic @interface LazyLoaded {\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/serialization/BusinessDataObjectMapper.java",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.serialization;\n\nimport java.io.IOException;\nimport java.io.Serializable;\nimport java.io.StringWriter;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.OffsetDateTime;\nimport java.util.List;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.SerializationFeature;\nimport com.fasterxml.jackson.databind.module.SimpleModule;\nimport org.bonitasoft.engine.bdm.Entity;\n\npublic class BusinessDataObjectMapper {\n\n    protected ObjectMapper objectMapper;\n\n    public BusinessDataObjectMapper() {\n        objectMapper = new ObjectMapper();\n        // avoid to fail when serializing proxy (proxy will be recreated client side) see BS-16031\n        objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);\n\n        SimpleModule module = new SimpleModule();\n        module.addSerializer(LocalDate.class, new CustomLocalDateSerializer());\n        module.addSerializer(LocalDateTime.class, new CustomLocalDateTimeSerializer());\n        module.addSerializer(OffsetDateTime.class, new CustomOffsetDateTimeSerializer());\n        module.addDeserializer(LocalDate.class, new CustomLocalDateDeserializer());\n        module.addDeserializer(LocalDateTime.class, new CustomLocalDateTimeDeserializer());\n        module.addDeserializer(OffsetDateTime.class, new CustomOffsetDateTimeDeserializer());\n        objectMapper.registerModule(module);\n    }\n\n    public void writeValue(StringWriter writer, Entity entity) throws IOException {\n        objectMapper.writeValue(writer, entity);\n    }\n\n    public void writeValue(StringWriter writer, List<? extends Entity> entities) throws IOException {\n        objectMapper.writeValue(writer, entities);\n    }\n\n    public <T extends Serializable> List<T> readListValue(byte[] result, Class<T> loadClass) throws IOException {\n        return objectMapper.readValue(result,\n                objectMapper.getTypeFactory().constructCollectionType(List.class, loadClass));\n    }\n\n    public <T extends Serializable> T readValue(byte[] result, Class<T> loadClass) throws IOException {\n        return objectMapper.readValue(result, loadClass);\n    }\n\n    public byte[] writeValueAsBytes(Serializable result) throws IOException {\n        return objectMapper.writeValueAsBytes(result);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/serialization/CustomLocalDateDeserializer.java",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.serialization;\n\nimport java.io.IOException;\nimport java.time.LocalDate;\n\nimport com.fasterxml.jackson.core.JsonParser;\nimport com.fasterxml.jackson.databind.DeserializationContext;\nimport com.fasterxml.jackson.databind.deser.std.StdDeserializer;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class CustomLocalDateDeserializer extends StdDeserializer<LocalDate> {\n\n    public CustomLocalDateDeserializer() {\n        super(LocalDate.class);\n    }\n\n    @Override\n    public LocalDate deserialize(JsonParser p, DeserializationContext context) throws IOException {\n        final String value = p.readValueAs(String.class);\n        if (value == null) {\n            return null;\n        }\n        return LocalDate.parse(value);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/serialization/CustomLocalDateSerializer.java",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.serialization;\n\nimport java.io.IOException;\nimport java.time.LocalDate;\nimport java.time.format.DateTimeFormatter;\n\nimport com.fasterxml.jackson.core.JsonGenerator;\nimport com.fasterxml.jackson.databind.SerializerProvider;\nimport com.fasterxml.jackson.databind.ser.std.StdSerializer;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class CustomLocalDateSerializer extends StdSerializer<LocalDate> {\n\n    public CustomLocalDateSerializer() {\n        this(null);\n    }\n\n    public CustomLocalDateSerializer(Class<LocalDate> t) {\n        super(t);\n    }\n\n    @Override\n    public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider provider) throws IOException {\n        gen.writeString((value != null) ? DateTimeFormatter.ISO_LOCAL_DATE.format(value) : null);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/serialization/CustomLocalDateTimeDeserializer.java",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.serialization;\n\nimport java.io.IOException;\nimport java.time.LocalDateTime;\n\nimport com.fasterxml.jackson.core.JsonParser;\nimport com.fasterxml.jackson.databind.DeserializationContext;\nimport com.fasterxml.jackson.databind.deser.std.StdDeserializer;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class CustomLocalDateTimeDeserializer extends StdDeserializer<LocalDateTime> {\n\n    public CustomLocalDateTimeDeserializer() {\n        super(LocalDateTime.class);\n    }\n\n    @Override\n    public LocalDateTime deserialize(JsonParser p, DeserializationContext context) throws IOException {\n        final String value = p.readValueAs(String.class);\n        if (value == null) {\n            return null;\n        }\n        return LocalDateTime.parse(value);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/serialization/CustomLocalDateTimeSerializer.java",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.serialization;\n\nimport java.io.IOException;\nimport java.time.LocalDateTime;\nimport java.time.format.DateTimeFormatter;\n\nimport com.fasterxml.jackson.core.JsonGenerator;\nimport com.fasterxml.jackson.databind.SerializerProvider;\nimport com.fasterxml.jackson.databind.ser.std.StdSerializer;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class CustomLocalDateTimeSerializer extends StdSerializer<LocalDateTime> {\n\n    public CustomLocalDateTimeSerializer() {\n        this(null);\n    }\n\n    public CustomLocalDateTimeSerializer(Class<LocalDateTime> t) {\n        super(t);\n    }\n\n    @Override\n    public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider provider) throws IOException {\n        gen.writeString((value != null) ? DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(value) : null);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/serialization/CustomOffsetDateTimeDeserializer.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.serialization;\n\nimport java.io.IOException;\nimport java.time.LocalDateTime;\nimport java.time.OffsetDateTime;\nimport java.time.ZoneOffset;\n\nimport com.fasterxml.jackson.core.JsonParser;\nimport com.fasterxml.jackson.databind.DeserializationContext;\nimport com.fasterxml.jackson.databind.deser.std.StdDeserializer;\n\n/**\n * @author Danila Mazour\n */\npublic class CustomOffsetDateTimeDeserializer extends StdDeserializer<OffsetDateTime> {\n\n    public CustomOffsetDateTimeDeserializer() {\n        super(LocalDateTime.class);\n    }\n\n    @Override\n    public OffsetDateTime deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {\n        final String value = jp.readValueAs(String.class);\n        if (value == null) {\n            return null;\n        }\n        return OffsetDateTime.parse(value).withOffsetSameInstant(ZoneOffset.UTC);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/serialization/CustomOffsetDateTimeSerializer.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.serialization;\n\nimport java.io.IOException;\nimport java.time.OffsetDateTime;\nimport java.time.ZoneOffset;\nimport java.time.format.DateTimeFormatter;\n\nimport com.fasterxml.jackson.core.JsonGenerator;\nimport com.fasterxml.jackson.databind.SerializerProvider;\nimport com.fasterxml.jackson.databind.ser.std.StdSerializer;\n\n/**\n * @author Danila Mazour\n */\npublic class CustomOffsetDateTimeSerializer extends StdSerializer<OffsetDateTime> {\n\n    public CustomOffsetDateTimeSerializer() {\n        this(null);\n    }\n\n    public CustomOffsetDateTimeSerializer(Class<OffsetDateTime> t) {\n        super(t);\n    }\n\n    @Override\n    public void serialize(OffsetDateTime value, JsonGenerator jgen, SerializerProvider provider) throws IOException {\n        jgen.writeString((value != null)\n                ? DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(value.withOffsetSameInstant(ZoneOffset.UTC)) : null);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/validator/BusinessObjectModelValidator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.model.Query;\nimport org.bonitasoft.engine.bdm.model.UniqueConstraint;\nimport org.bonitasoft.engine.bdm.validator.rule.BusinessObjectModelValidationRule;\nimport org.bonitasoft.engine.bdm.validator.rule.BusinessObjectValidationRule;\nimport org.bonitasoft.engine.bdm.validator.rule.FieldValidationRule;\nimport org.bonitasoft.engine.bdm.validator.rule.IndexValidationRule;\nimport org.bonitasoft.engine.bdm.validator.rule.MultipleAggregationToItselfValidationRule;\nimport org.bonitasoft.engine.bdm.validator.rule.QueryParameterValidationRule;\nimport org.bonitasoft.engine.bdm.validator.rule.QueryValidationRule;\nimport org.bonitasoft.engine.bdm.validator.rule.SimpleFieldValidationRule;\nimport org.bonitasoft.engine.bdm.validator.rule.UniqueConstraintValidationRule;\nimport org.bonitasoft.engine.bdm.validator.rule.UniqueSimpleNameValidationRule;\nimport org.bonitasoft.engine.bdm.validator.rule.ValidationRule;\nimport org.bonitasoft.engine.bdm.validator.rule.composition.AggregationAndCompositionValidationRule;\nimport org.bonitasoft.engine.bdm.validator.rule.composition.CyclicCompositionValidationRule;\nimport org.bonitasoft.engine.bdm.validator.rule.composition.UniquenessCompositionValidationRule;\n\n/**\n * @author Romain Bioteau\n */\npublic class BusinessObjectModelValidator {\n\n    private final List<ValidationRule<?, ValidationStatus>> rules = new ArrayList<>();\n\n    public BusinessObjectModelValidator() {\n        rules.add(new BusinessObjectModelValidationRule());\n        rules.add(new BusinessObjectValidationRule());\n        rules.add(new FieldValidationRule());\n        rules.add(new SimpleFieldValidationRule());\n        rules.add(new UniqueConstraintValidationRule());\n        rules.add(new IndexValidationRule());\n        rules.add(new QueryValidationRule());\n        rules.add(new QueryParameterValidationRule());\n        rules.add(new MultipleAggregationToItselfValidationRule());\n        rules.add(new UniquenessCompositionValidationRule());\n        rules.add(new CyclicCompositionValidationRule());\n        rules.add(new AggregationAndCompositionValidationRule());\n        rules.add(new UniqueSimpleNameValidationRule());\n    }\n\n    public ValidationStatus validate(final BusinessObjectModel bom) {\n        final Set<Object> objectsToValidate = buildModelTree(bom);\n        final ValidationStatus status = new ValidationStatus();\n        for (final Object modelElement : objectsToValidate) {\n            for (final ValidationRule<?, ? extends ValidationStatus> rule : rules) {\n                if (rule.appliesTo(modelElement)) {\n                    status.addValidationStatus(rule.checkRule(modelElement));\n                }\n            }\n        }\n        return status;\n    }\n\n    private Set<Object> buildModelTree(final BusinessObjectModel bom) {\n        final Set<Object> objectsToValidate = new HashSet<>();\n        objectsToValidate.add(bom);\n        for (final BusinessObject bo : bom.getBusinessObjects()) {\n            objectsToValidate.add(bo);\n            objectsToValidate.addAll(bo.getFields());\n            final List<UniqueConstraint> uniqueConstraints = bo.getUniqueConstraints();\n            objectsToValidate.addAll(uniqueConstraints);\n            final List<Query> queries = bo.getQueries();\n            for (final Query q : queries) {\n                objectsToValidate.add(q);\n                objectsToValidate.addAll(q.getQueryParameters());\n            }\n            objectsToValidate.addAll(bo.getIndexes());\n        }\n        return objectsToValidate;\n    }\n\n    public List<ValidationRule<?, ValidationStatus>> getRules() {\n        return Collections.unmodifiableList(rules);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/validator/UniqueNameValidator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator;\n\nimport java.io.Serializable;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.LinkedHashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.api.result.StatusCode;\nimport org.bonitasoft.engine.api.result.StatusContext;\nimport org.bonitasoft.engine.bdm.model.NamedElement;\n\n/**\n * @author Colin PUY\n */\npublic class UniqueNameValidator {\n\n    public ValidationStatus validate(Collection<? extends NamedElement> namedElements,\n            String namedElementTypePluralForm) {\n        ValidationStatus status = new ValidationStatus();\n        Set<String> duplicateNames = findDuplicateNames(namedElements);\n        Map<String, Serializable> context = new HashMap<>();\n        context.put(StatusContext.BDM_ARTIFACT_KEY, namedElementTypePluralForm);\n        for (String name : duplicateNames) {\n            context.put(StatusContext.BDM_ARTIFACT_NAME_KEY, name);\n            status.addError(StatusCode.DUPLICATE_CONSTRAINT_OR_INDEX_NAME,\n                    String.format(\"There are at least two %s with the same name : %s\", namedElementTypePluralForm,\n                            name),\n                    context);\n        }\n        return status;\n    }\n\n    private Set<String> findDuplicateNames(Collection<? extends NamedElement> list) {\n        Set<String> duplicates = new LinkedHashSet<>();\n        Set<String> uniqueNames = new HashSet<>();\n\n        for (NamedElement t : list) {\n            if (!uniqueNames.add(t.getName())) {\n                duplicates.add(t.getName());\n            }\n        }\n\n        return duplicates;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/validator/ValidationStatus.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.stream.Collectors;\n\nimport org.bonitasoft.engine.api.result.Status;\nimport org.bonitasoft.engine.api.result.StatusCode;\n\n/**\n * @author Romain Bioteau\n */\npublic class ValidationStatus {\n\n    private final List<Status> statusList;\n\n    public ValidationStatus() {\n        statusList = new ArrayList<>();\n    }\n\n    public void addError(StatusCode code, final String error) {\n        if (error == null || error.isEmpty()) {\n            throw new IllegalArgumentException(\"error message cannot be null or empty\");\n        }\n        statusList.add(Status.errorStatus(code, error));\n    }\n\n    public void addError(StatusCode code, final String error, Map<String, Serializable> context) {\n        if (error == null || error.isEmpty()) {\n            throw new IllegalArgumentException(\"error message cannot be null or empty\");\n        }\n        statusList.add(Status.errorStatus(code, error, context));\n    }\n\n    public void addWarning(StatusCode code, final String warning) {\n        if (warning == null || warning.isEmpty()) {\n            throw new IllegalArgumentException(\"warning message cannot be null or empty\");\n        }\n        statusList.add(Status.warningStatus(code, warning));\n    }\n\n    public void addWarning(StatusCode code, final String warning, Map<String, Serializable> context) {\n        if (warning == null || warning.isEmpty()) {\n            throw new IllegalArgumentException(\"warning message cannot be null or empty\");\n        }\n        statusList.add(Status.warningStatus(code, warning, context));\n    }\n\n    public boolean isOk() {\n        return statusList.stream().map(Status::getLevel).noneMatch(Status.Level.ERROR::equals);\n    }\n\n    public void addValidationStatus(final ValidationStatus status) {\n        statusList.addAll(status.getStatuses());\n    }\n\n    /**\n     * @Deprecated since release 7.7.0, replaced by {@link #getStatuses()}\n     */\n    @Deprecated(since = \"7.7.0\", forRemoval = true)\n    public List<String> getErrors() {\n        return statusList.stream().filter(status -> Objects.equals(Status.Level.ERROR, status.getLevel()))\n                .map(Status::getMessage).collect(Collectors.toList());\n    }\n\n    /**\n     * @Deprecated since release 7.7.0, replaced by {@link #getStatuses()}\n     */\n    @Deprecated(since = \"7.7.0\", forRemoval = true)\n    public List<String> getWarnings() {\n        return statusList.stream().filter(status -> Objects.equals(Status.Level.WARNING, status.getLevel()))\n                .map(Status::getMessage).collect(Collectors.toList());\n    }\n\n    public List<Status> getStatuses() {\n        return statusList;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (!(o instanceof ValidationStatus that))\n            return false;\n        return Objects.equals(statusList, that.statusList);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(statusList);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/validator/rule/BusinessObjectModelValidationRule.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator.rule;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.api.result.StatusCode;\nimport org.bonitasoft.engine.api.result.StatusContext;\nimport org.bonitasoft.engine.bdm.BDMQueryUtil;\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.model.Query;\nimport org.bonitasoft.engine.bdm.validator.ValidationStatus;\n\n/**\n * @author Romain Bioteau\n */\npublic class BusinessObjectModelValidationRule extends ValidationRule<BusinessObjectModel, ValidationStatus> {\n\n    public BusinessObjectModelValidationRule() {\n        super(BusinessObjectModel.class);\n    }\n\n    @Override\n    public ValidationStatus validate(final BusinessObjectModel bom) {\n        final ValidationStatus status = new ValidationStatus();\n        if (bom.getBusinessObjects().isEmpty()) {\n            status.addError(StatusCode.EMPTY_BDM,\n                    \"Business object model must have at least one business object declared\");\n        } else {\n            validateQueries(bom, status);\n        }\n        return status;\n    }\n\n    private void validateQueries(final BusinessObjectModel bom, final ValidationStatus status) {\n        for (final BusinessObject bo : bom.getBusinessObjects()) {\n            final List<Query> lazyQueries = BDMQueryUtil.createProvidedQueriesForLazyField(bom, bo);\n            final Set<String> lazyQueryNames = new HashSet<>();\n            for (final Query query : lazyQueries) {\n                lazyQueryNames.add(query.getName());\n            }\n            Map<String, Serializable> context = new HashMap<>();\n            context.put(StatusContext.BUSINESS_OBJECT_NAME_KEY, bo.getQualifiedName());\n            for (final Query q : bo.getQueries()) {\n                if (lazyQueryNames.contains(q.getName())) {\n                    context.put(StatusContext.BDM_ARTIFACT_NAME_KEY, q.getName());\n                    status.addError(StatusCode.DUPLICATE_QUERY_NAME,\n                            \"The query named \\\"\" + q.getName() + \"\\\" already exists for \" + bo.getQualifiedName(),\n                            context);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/validator/rule/BusinessObjectValidationRule.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator.rule;\n\nimport java.io.Serializable;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport javax.lang.model.SourceVersion;\n\nimport org.bonitasoft.engine.api.result.StatusCode;\nimport org.bonitasoft.engine.api.result.StatusContext;\nimport org.bonitasoft.engine.bdm.BDMQueryUtil;\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.Query;\nimport org.bonitasoft.engine.bdm.model.UniqueConstraint;\nimport org.bonitasoft.engine.bdm.model.field.Field;\nimport org.bonitasoft.engine.bdm.validator.SQLNameValidator;\nimport org.bonitasoft.engine.bdm.validator.SQLNameValidator.Grammar;\nimport org.bonitasoft.engine.bdm.validator.ValidationStatus;\n\n/**\n * @author Romain Bioteau\n */\npublic class BusinessObjectValidationRule extends ValidationRule<BusinessObject, ValidationStatus> {\n\n    private static final String[] RESERVED_PACKAGE_PREFIX = { \"com.bonitasoft.\", \"org.bonitasoft.\" };\n\n    private static final int MAX_TABLE_NAME_LENGTH = 30;\n\n    private final SQLNameValidator sqlNameValidator;\n\n    public BusinessObjectValidationRule() {\n        super(BusinessObject.class);\n        sqlNameValidator = new SQLNameValidator(MAX_TABLE_NAME_LENGTH);\n    }\n\n    @Override\n    public ValidationStatus validate(final BusinessObject bo) {\n        final ValidationStatus status = new ValidationStatus();\n        final String qualifiedName = bo.getQualifiedName();\n        if (qualifiedName == null) {\n            status.addError(StatusCode.BUSINESS_OBJECT_WITHOUT_NAME, \"A Business Object must have a qualified name\");\n            return status;\n        }\n\n        for (String reservedPrefix : RESERVED_PACKAGE_PREFIX) {\n            if (qualifiedName.startsWith(reservedPrefix)) {\n                status.addError(StatusCode.RESERVED_PACKAGE_NAME,\n                        String.format(\"Package %s is reserved. Please choose another package name\", reservedPrefix),\n                        Collections.singletonMap(StatusContext.BDM_ARTIFACT_NAME_KEY, reservedPrefix));\n            }\n        }\n\n        final String simpleName = bo.getSimpleName();\n        if (!SourceVersion.isName(qualifiedName) || !sqlNameValidator.isValid(simpleName)) {\n            status.addError(StatusCode.INVALID_JAVA_IDENTIFIER_NAME,\n                    String.format(\"%s is not a valid Java qualified name\", qualifiedName),\n                    Collections.singletonMap(StatusContext.BUSINESS_OBJECT_NAME_KEY, qualifiedName));\n            return status;\n        } else {\n            var discouragingGrammars = sqlNameValidator.isKeywordDiscouragedBy(simpleName);\n            if (!discouragingGrammars.isEmpty()) {\n                String msg = String.format(\"%1$s is discouraged as a business object's name. It is a keyword in %2$s.\",\n                        simpleName,\n                        discouragingGrammars.stream().map(Grammar::toString).collect(Collectors.joining(\", \")));\n                status.addWarning(StatusCode.DISCOURAGED_JAVA_IDENTIFIER_NAME, msg,\n                        Collections.singletonMap(StatusContext.BUSINESS_OBJECT_NAME_KEY, simpleName));\n            }\n        }\n\n        if (simpleName.contains(\"_\")) {\n            status.addError(StatusCode.INVALID_CHARACTER_IN_BUSINESS_OBJECT_NAME,\n                    \"_ is a forbidden character in business object's name\");\n        }\n\n        if (bo.getFields().isEmpty()) {\n            status.addError(StatusCode.BUSINESS_OBJECT_WITHOUT_FIELD,\n                    String.format(\"%s must have at least one field declared\", qualifiedName),\n                    Collections.singletonMap(StatusContext.BUSINESS_OBJECT_NAME_KEY, qualifiedName));\n        }\n\n        validateConstraints(bo, status);\n        validateQueries(bo, status);\n        return status;\n    }\n\n    private void validateQueries(final BusinessObject bo, final ValidationStatus status) {\n        final Set<String> queryNames = BDMQueryUtil.getAllProvidedQueriesNameForBusinessObject(bo);\n        Map<String, Serializable> context = new HashMap<>();\n        context.put(StatusContext.BUSINESS_OBJECT_NAME_KEY, bo.getQualifiedName());\n        for (final Query q : bo.getQueries()) {\n            if (queryNames.contains(q.getName())) {\n                context.put(StatusContext.BDM_ARTIFACT_NAME_KEY, q.getName());\n                status.addError(StatusCode.DUPLICATE_QUERY_NAME,\n                        \"The query named \\\"\" + q.getName() + \"\\\" already exists for \" + bo.getQualifiedName(),\n                        context);\n            } else {\n                queryNames.add(q.getName());\n            }\n        }\n    }\n\n    private void validateConstraints(final BusinessObject bo, final ValidationStatus status) {\n        final Set<String> constraintNames = new HashSet<>();\n        Map<String, Serializable> context = new HashMap<>();\n        context.put(StatusContext.BUSINESS_OBJECT_NAME_KEY, bo.getQualifiedName());\n        for (final UniqueConstraint uc : bo.getUniqueConstraints()) {\n            String name = uc.getName();\n            if (constraintNames.contains(name)) {\n                context.put(StatusContext.BDM_ARTIFACT_NAME_KEY, name);\n                status.addError(StatusCode.DUPLICATE_CONSTRAINT_NAME,\n                        \"The constraint named \\\"\" + name + \"\\\" already exists for \" + bo.getQualifiedName(),\n                        context);\n            } else {\n                constraintNames.add(name);\n            }\n            List<String> fieldNames = uc.getFieldNames();\n            if (fieldNames != null) {\n                for (final String fName : fieldNames) {\n                    if (getField(bo, fName) == null) {\n                        context.put(StatusContext.BDM_ARTIFACT_NAME_KEY, fName);\n                        status.addError(StatusCode.UNKNOWN_FIELD_IN_CONSTRAINT,\n                                String.format(\"The field named %s does not exist in %s\", fName, bo.getQualifiedName()),\n                                context);\n                    }\n                }\n            }\n        }\n    }\n\n    private Field getField(final BusinessObject bo, final String name) {\n        Field found = null;\n        int index = 0;\n        final List<Field> fields = bo.getFields();\n        while (found == null && index < fields.size()) {\n            final Field field = bo.getFields().get(index);\n            if (field.getName().equals(name)) {\n                found = field;\n            }\n            index++;\n        }\n        return found;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/validator/rule/FieldValidationRule.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator.rule;\n\nimport java.util.Collections;\nimport java.util.stream.Collectors;\n\nimport javax.lang.model.SourceVersion;\n\nimport org.bonitasoft.engine.api.result.StatusCode;\nimport org.bonitasoft.engine.api.result.StatusContext;\nimport org.bonitasoft.engine.bdm.model.field.Field;\nimport org.bonitasoft.engine.bdm.validator.SQLNameValidator;\nimport org.bonitasoft.engine.bdm.validator.SQLNameValidator.Grammar;\nimport org.bonitasoft.engine.bdm.validator.ValidationStatus;\n\n/**\n * @author Romain Bioteau\n */\npublic class FieldValidationRule extends ValidationRule<Field, ValidationStatus> {\n\n    private static final int MAX_COLUMNAME_LENGTH = 50;\n\n    private final SQLNameValidator sqlNameValidator;\n\n    public FieldValidationRule() {\n        super(Field.class);\n        sqlNameValidator = new SQLNameValidator(MAX_COLUMNAME_LENGTH);\n    }\n\n    @Override\n    public ValidationStatus validate(final Field field) {\n        final ValidationStatus status = new ValidationStatus();\n        final String name = field.getName();\n        if (name == null || !SourceVersion.isIdentifier(name) || SourceVersion.isKeyword(name)\n                || isForbiddenIdentifier(name)) {\n            status.addError(StatusCode.INVALID_FIELD_IDENTIFIER,\n                    String.format(\"%s is not a valid field identifier\", name),\n                    Collections.singletonMap(StatusContext.BDM_ARTIFACT_NAME_KEY, name));\n            return status;\n        } else {\n            var discouragingGrammars = sqlNameValidator.isKeywordDiscouragedBy(name);\n            if (!discouragingGrammars.isEmpty()) {\n                String msg = String.format(\"%1$s is discouraged as a field identifier. It is a keyword in %2$s.\",\n                        name, discouragingGrammars.stream().map(Grammar::toString).collect(Collectors.joining(\", \")));\n                status.addWarning(StatusCode.DISCOURAGED_FIELD_IDENTIFIER, msg,\n                        Collections.singletonMap(StatusContext.BDM_ARTIFACT_NAME_KEY, name));\n            }\n        }\n        return status;\n    }\n\n    private boolean isForbiddenIdentifier(final String name) {\n        return Field.PERSISTENCE_ID.equalsIgnoreCase(name) || Field.PERSISTENCE_VERSION.equalsIgnoreCase(name)\n                || !sqlNameValidator.isValid(name);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/validator/rule/IndexValidationRule.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator.rule;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.bonitasoft.engine.api.result.StatusCode;\nimport org.bonitasoft.engine.api.result.StatusContext;\nimport org.bonitasoft.engine.bdm.model.Index;\nimport org.bonitasoft.engine.bdm.validator.SQLNameValidator;\nimport org.bonitasoft.engine.bdm.validator.SQLNameValidator.Grammar;\nimport org.bonitasoft.engine.bdm.validator.ValidationStatus;\n\npublic class IndexValidationRule extends ValidationRule<Index, ValidationStatus> {\n\n    private static final int MAX_CONSTRAINTNAME_LENGTH = 25;\n\n    private final SQLNameValidator sqlNameValidator;\n\n    public IndexValidationRule() {\n        super(Index.class);\n        sqlNameValidator = new SQLNameValidator(MAX_CONSTRAINTNAME_LENGTH);\n    }\n\n    @Override\n    protected ValidationStatus validate(Index index) {\n        final ValidationStatus status = new ValidationStatus();\n        final String name = index.getName();\n        if (name == null || name.isEmpty()) {\n            status.addError(StatusCode.INDEX_WITHOUT_NAME, \"An index must have name\");\n            return status;\n        }\n        if (!sqlNameValidator.isValid(name)) {\n            status.addError(StatusCode.INVALID_SQL_IDENTIFIER_NAME,\n                    String.format(\"%s is not a valid SQL identifier\", name),\n                    Collections.singletonMap(StatusContext.BDM_ARTIFACT_NAME_KEY, name));\n        } else {\n            var discouragingGrammars = sqlNameValidator.isKeywordDiscouragedBy(name);\n            if (!discouragingGrammars.isEmpty()) {\n                String msg = String.format(\"%1$s is discouraged as an SQL identifier. It is a keyword in %2$s.\",\n                        name, discouragingGrammars.stream().map(Grammar::toString).collect(Collectors.joining(\", \")));\n                status.addWarning(StatusCode.DISCOURAGED_SQL_IDENTIFIER_NAME, msg,\n                        Collections.singletonMap(StatusContext.BDM_ARTIFACT_NAME_KEY, name));\n            }\n        }\n        List<String> fieldNames = index.getFieldNames();\n        if (fieldNames == null || fieldNames.isEmpty()) {\n            status.addError(StatusCode.INDEX_WITHOUT_FIELD,\n                    String.format(\"%s index must have at least one field declared\", name),\n                    Collections.singletonMap(StatusContext.BDM_ARTIFACT_NAME_KEY, name));\n        }\n        return status;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/validator/rule/MultipleAggregationToItselfValidationRule.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator.rule;\n\nimport static org.bonitasoft.engine.api.result.StatusCode.MULTIPLE_AGGREGATION_RELATION_TO_ITSELF;\nimport static org.bonitasoft.engine.bdm.model.field.RelationField.Type.AGGREGATION;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.result.StatusContext;\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.model.field.Field;\nimport org.bonitasoft.engine.bdm.model.field.RelationField;\nimport org.bonitasoft.engine.bdm.validator.ValidationStatus;\n\n/**\n * @author Danila Mazour\n */\npublic class MultipleAggregationToItselfValidationRule extends ValidationRule<BusinessObjectModel, ValidationStatus> {\n\n    public MultipleAggregationToItselfValidationRule() {\n        super(BusinessObjectModel.class);\n    }\n\n    @Override\n    protected ValidationStatus validate(BusinessObjectModel bom) {\n        ValidationStatus validationStatus = new ValidationStatus();\n        for (BusinessObject bo : bom.getBusinessObjects()) {\n            List<Field> boFields = bo.getFields();\n            for (Field boField : boFields) {\n                if (boField instanceof RelationField relationField) {\n                    String fieldReferenceQualifiedName = relationField.getReference().getQualifiedName();\n                    String boQualifiedName = bo.getQualifiedName();\n                    if (relationField.getType() == AGGREGATION && fieldReferenceQualifiedName.equals(boQualifiedName)\n                            && relationField.isCollection()) {\n                        validationStatus.addError(MULTIPLE_AGGREGATION_RELATION_TO_ITSELF, \"The object \"\n                                + boQualifiedName + \" is referencing itself in a multiple aggregation relation.\",\n                                Collections.singletonMap(StatusContext.BUSINESS_OBJECT_NAME_KEY, boQualifiedName));\n                    }\n                }\n            }\n        }\n        return validationStatus;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/validator/rule/QueryParameterValidationRule.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator.rule;\n\n/**\n * Tests the validity of Query parameters\n *\n * @author Emmanuel Duchastenier\n */\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport javax.lang.model.SourceVersion;\n\nimport org.bonitasoft.engine.api.result.StatusCode;\nimport org.bonitasoft.engine.api.result.StatusContext;\nimport org.bonitasoft.engine.bdm.BDMQueryUtil;\nimport org.bonitasoft.engine.bdm.model.QueryParameter;\nimport org.bonitasoft.engine.bdm.validator.ValidationStatus;\n\npublic class QueryParameterValidationRule extends ValidationRule<QueryParameter, ValidationStatus> {\n\n    public static final List<String> FORBIDDEN_PARAMETER_NAMES = Arrays.asList(BDMQueryUtil.START_INDEX_PARAM_NAME,\n            BDMQueryUtil.MAX_RESULTS_PARAM_NAME);\n\n    public QueryParameterValidationRule() {\n        super(QueryParameter.class);\n    }\n\n    @Override\n    public ValidationStatus validate(final QueryParameter parameter) {\n        final ValidationStatus status = new ValidationStatus();\n        final String name = parameter.getName();\n        if (name == null || name.isEmpty()) {\n            status.addError(StatusCode.QUERY_PARAMETER_WITHOUT_NAME, \"A parameter must have name\");\n            return status;\n        }\n        if (!SourceVersion.isIdentifier(name)) {\n            status.addError(StatusCode.INVALID_JAVA_IDENTIFIER_NAME,\n                    String.format(\"%s is not a valid Java identifier.\", name),\n                    Collections.singletonMap(StatusContext.BDM_ARTIFACT_NAME_KEY, name));\n        }\n        if (FORBIDDEN_PARAMETER_NAMES.contains(name)) {\n            status.addError(StatusCode.FORBIDDEN_QUERY_PARAMETER_NAME,\n                    String.format(\"%s is a reserved parameter name. Use a name different from: %s\", name,\n                            FORBIDDEN_PARAMETER_NAMES),\n                    Collections.singletonMap(StatusContext.BDM_ARTIFACT_NAME_KEY, name));\n        }\n        if (parameter.getClassName() == null || parameter.getClassName().isEmpty()) {\n            status.addError(StatusCode.QUERY_PARAMETER_WITHOUT_CLASS_NAME,\n                    String.format(\"%s query parameter must have a classname\", name),\n                    Collections.singletonMap(StatusContext.BDM_ARTIFACT_NAME_KEY, name));\n        }\n        return status;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/validator/rule/QueryValidationRule.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator.rule;\n\nimport java.util.Collections;\n\nimport javax.lang.model.SourceVersion;\n\nimport org.bonitasoft.engine.api.result.StatusCode;\nimport org.bonitasoft.engine.api.result.StatusContext;\nimport org.bonitasoft.engine.bdm.model.Query;\nimport org.bonitasoft.engine.bdm.validator.ValidationStatus;\n\npublic class QueryValidationRule extends ValidationRule<Query, ValidationStatus> {\n\n    private static final int MAX_QUERY_NAME_LENGTH = 150;\n\n    public QueryValidationRule() {\n        super(Query.class);\n    }\n\n    @Override\n    public ValidationStatus validate(final Query query) {\n        final ValidationStatus status = new ValidationStatus();\n        final String name = query.getName();\n        if (name == null || name.isEmpty()) {\n            status.addError(StatusCode.QUERY_WITHOUT_NAME, \"A query must have name\");\n            return status;\n        }\n        if (!SourceVersion.isIdentifier(name)) {\n            status.addError(StatusCode.INVALID_JAVA_IDENTIFIER_NAME,\n                    String.format(\"%s is not a valid Java identifier.\", name),\n                    Collections.singletonMap(StatusContext.BDM_ARTIFACT_NAME_KEY, name));\n        }\n        if (name.length() > MAX_QUERY_NAME_LENGTH) {\n            status.addError(StatusCode.QUERY_NAME_LENGTH_TO_HIGH,\n                    String.format(\"%s length must be lower than 150 characters.\", name),\n                    Collections.singletonMap(StatusContext.BDM_ARTIFACT_NAME_KEY, name));\n        }\n        if (query.getContent() == null || query.getContent().isEmpty()) {\n            status.addError(StatusCode.QUERY_WITHOUT_CONTENT,\n                    String.format(\"%s query must have a content defined\", name),\n                    Collections.singletonMap(StatusContext.BDM_ARTIFACT_NAME_KEY, name));\n        }\n        if (query.getReturnType() == null || query.getReturnType().isEmpty()) {\n            status.addError(StatusCode.QUERY_WITHOUT_RETURN_TYPE,\n                    String.format(\"%s query must have a return type defined\", name),\n                    Collections.singletonMap(StatusContext.BDM_ARTIFACT_NAME_KEY, name));\n        }\n        return status;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/validator/rule/SimpleFieldValidationRule.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator.rule;\n\nimport java.util.Collections;\n\nimport org.bonitasoft.engine.api.result.StatusCode;\nimport org.bonitasoft.engine.api.result.StatusContext;\nimport org.bonitasoft.engine.bdm.model.field.SimpleField;\nimport org.bonitasoft.engine.bdm.validator.ValidationStatus;\n\n/**\n * @author Colin PUY\n */\npublic class SimpleFieldValidationRule extends ValidationRule<SimpleField, ValidationStatus> {\n\n    public SimpleFieldValidationRule() {\n        super(SimpleField.class);\n    }\n\n    @Override\n    public ValidationStatus validate(SimpleField field) {\n        final ValidationStatus status = new ValidationStatus();\n        if (field.getType() == null) {\n            status.addError(StatusCode.FIELD_WITHOUT_NAME,\n                    String.format(\"%s must have a type declared\", field.getName()),\n                    Collections.singletonMap(StatusContext.BDM_ARTIFACT_NAME_KEY, field.getName()));\n        }\n        return status;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/validator/rule/UniqueConstraintValidationRule.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator.rule;\n\nimport static java.util.Collections.singletonMap;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.bonitasoft.engine.api.result.StatusCode;\nimport org.bonitasoft.engine.api.result.StatusContext;\nimport org.bonitasoft.engine.bdm.model.UniqueConstraint;\nimport org.bonitasoft.engine.bdm.validator.SQLNameValidator;\nimport org.bonitasoft.engine.bdm.validator.SQLNameValidator.Grammar;\nimport org.bonitasoft.engine.bdm.validator.ValidationStatus;\n\npublic class UniqueConstraintValidationRule extends ValidationRule<UniqueConstraint, ValidationStatus> {\n\n    private static final int MAX_CONSTRAINTNAME_LENGTH = 25;\n\n    private final SQLNameValidator sqlNameValidator;\n\n    public UniqueConstraintValidationRule() {\n        super(UniqueConstraint.class);\n        sqlNameValidator = new SQLNameValidator(MAX_CONSTRAINTNAME_LENGTH);\n    }\n\n    @Override\n    public ValidationStatus validate(final UniqueConstraint uc) {\n        final ValidationStatus status = new ValidationStatus();\n        final String name = uc.getName();\n        if (name == null || name.isEmpty()) {\n            status.addError(StatusCode.UNIQUE_CONSTRAINT_WITHOUT_NAME, \"A unique constraint must have name\");\n            return status;\n        }\n        if (!sqlNameValidator.isValid(name)) {\n            status.addError(StatusCode.INVALID_SQL_IDENTIFIER_NAME,\n                    String.format(\"%s is not a valid SQL identifier\", name),\n                    singletonMap(StatusContext.INVALID_NAME_KEY, name));\n        } else {\n            var discouragingGrammars = sqlNameValidator.isKeywordDiscouragedBy(name);\n            if (!discouragingGrammars.isEmpty()) {\n                String msg = String.format(\"%1$s is discouraged as an SQL identifier. It is a keyword in %2$s.\",\n                        name, discouragingGrammars.stream().map(Grammar::toString).collect(Collectors.joining(\", \")));\n                status.addWarning(StatusCode.DISCOURAGED_SQL_IDENTIFIER_NAME, msg,\n                        Collections.singletonMap(StatusContext.BDM_ARTIFACT_NAME_KEY, name));\n            }\n        }\n\n        List<String> fieldNames = uc.getFieldNames();\n        if (fieldNames == null || fieldNames.isEmpty()) {\n            status.addError(StatusCode.UNIQUE_CONSTRAINT_WITHOUT_FIELD,\n                    String.format(\"%s unique constraint must have at least one field declared\", name),\n                    singletonMap(StatusContext.BDM_ARTIFACT_NAME_KEY, name));\n        }\n        return status;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/validator/rule/UniqueNameValidationRule.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator.rule;\n\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.validator.UniqueNameValidator;\nimport org.bonitasoft.engine.bdm.validator.ValidationStatus;\n\n/**\n * @author Colin PUY\n */\npublic class UniqueNameValidationRule extends ValidationRule<BusinessObjectModel, ValidationStatus> {\n\n    private UniqueNameValidator uniqueNameValidator;\n\n    public UniqueNameValidationRule(UniqueNameValidator uniqueNameValidator) {\n        super(BusinessObjectModel.class);\n        this.uniqueNameValidator = uniqueNameValidator;\n    }\n\n    @Override\n    protected ValidationStatus validate(BusinessObjectModel bom) {\n        ValidationStatus validationStatus = new ValidationStatus();\n        validationStatus\n                .addValidationStatus(uniqueNameValidator.validate(bom.getUniqueConstraints(), \"unique contraints\"));\n        validationStatus.addValidationStatus(uniqueNameValidator.validate(bom.getIndexes(), \"indexes\"));\n        return validationStatus;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/validator/rule/UniqueSimpleNameValidationRule.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator.rule;\n\nimport static org.bonitasoft.engine.api.result.StatusCode.DUPLICATE_BUSINESS_OBJECT_NAME;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.validator.ValidationStatus;\n\n/**\n * @author Danila Mazour\n */\npublic class UniqueSimpleNameValidationRule extends ValidationRule<BusinessObjectModel, ValidationStatus> {\n\n    public UniqueSimpleNameValidationRule() {\n        super(BusinessObjectModel.class);\n    }\n\n    @Override\n    public ValidationStatus validate(final BusinessObjectModel bom) {\n        final ValidationStatus status = new ValidationStatus();\n        List<BusinessObject> businessObjects = bom.getBusinessObjects();\n        Set<String> businessObjectNames = new HashSet<>();\n        for (BusinessObject businessObject : businessObjects) {\n            if (!businessObjectNames.add(businessObject.getSimpleName().toLowerCase())) {\n                status.addError(DUPLICATE_BUSINESS_OBJECT_NAME,\n                        \" There are at least 2 objects in the BDM that are called : \" + businessObject.getSimpleName());\n            }\n        }\n        return status;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/validator/rule/ValidationRule.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator.rule;\n\n/**\n * Validates an object of type T and returns a validation result of type R\n *\n * @param <T> the type of element that this validation rule supports\n * @param <R> the type of the result of the validation\n * @author Romain Bioteau\n * @author Emmanuel Duchastenier\n */\npublic abstract class ValidationRule<T, R> {\n\n    private Class<T> classToApply;\n\n    public ValidationRule(Class<T> classToApply) {\n        this.classToApply = classToApply;\n    }\n\n    public boolean appliesTo(Object modelElement) {\n        return modelElement != null && classToApply.isAssignableFrom(modelElement.getClass());\n    }\n\n    protected abstract R validate(T modelElement);\n\n    @SuppressWarnings(\"unchecked\")\n    public R checkRule(Object modelElement) {\n        if (!appliesTo(modelElement)) {\n            throw new IllegalArgumentException(\n                    this.getClass().getName() + \" doesn't handle validation for \" + modelElement.getClass().getName());\n        }\n        return validate((T) modelElement);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/validator/rule/composition/AggregationAndCompositionValidationRule.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator.rule.composition;\n\nimport static org.bonitasoft.engine.bdm.model.field.RelationField.Type.AGGREGATION;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.result.StatusCode;\nimport org.bonitasoft.engine.api.result.StatusContext;\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.model.field.Field;\nimport org.bonitasoft.engine.bdm.model.field.RelationField;\nimport org.bonitasoft.engine.bdm.validator.ValidationStatus;\nimport org.bonitasoft.engine.bdm.validator.rule.ValidationRule;\n\n/**\n * @author Danila Mazour\n */\npublic class AggregationAndCompositionValidationRule extends ValidationRule<BusinessObjectModel, ValidationStatus> {\n\n    public AggregationAndCompositionValidationRule() {\n        super(BusinessObjectModel.class);\n    }\n\n    @Override\n    protected ValidationStatus validate(BusinessObjectModel bom) {\n        ValidationStatus validationStatus = new ValidationStatus();\n        List<BusinessObject> businessObjects = bom.getBusinessObjects();\n        List<BusinessObject> aggregatedBusinessObjects = new ArrayList<>();\n        List<BusinessObject> composedBusinessObjects = new ArrayList<>();\n        List<Field> fieldList = new ArrayList<>();\n        for (BusinessObject bo : businessObjects)\n            fieldList.addAll(bo.getFields());\n        for (Field field : fieldList) {\n            if (field instanceof RelationField relationField) {\n                if (relationField.getType() == AGGREGATION) {\n                    aggregatedBusinessObjects.add(relationField.getReference());\n                }\n            }\n        }\n        composedBusinessObjects.addAll(bom.getReferencedBusinessObjectsByComposition());\n        for (BusinessObject composedBo : composedBusinessObjects) {\n            if (aggregatedBusinessObjects.contains(composedBo)) {\n                validationStatus.addWarning(StatusCode.BUSINESS_OBJECT_USED_IN_COMPOSITION_AND_AGGREGATION,\n                        String.format(\n                                \"The object %s is referenced both in composition and in aggregation. This may lead to runtime errors and\"\n                                        + \" may lead to unpredictable behaviour of the AccessControl configuration.\",\n                                composedBo.getQualifiedName()),\n                        Collections.singletonMap(StatusContext.BUSINESS_OBJECT_NAME_KEY,\n                                composedBo.getQualifiedName()));\n            }\n        }\n        return validationStatus;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/validator/rule/composition/CyclicCompositionValidationRule.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator.rule.composition;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.result.StatusCode;\nimport org.bonitasoft.engine.api.result.StatusContext;\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.validator.ValidationStatus;\nimport org.bonitasoft.engine.bdm.validator.rule.ValidationRule;\n\n/**\n * Check that there are no circular references on referenced business objects by composition.\n *\n * @author Colin PUY\n */\npublic class CyclicCompositionValidationRule extends ValidationRule<BusinessObjectModel, ValidationStatus> {\n\n    public CyclicCompositionValidationRule() {\n        super(BusinessObjectModel.class);\n    }\n\n    @Override\n    protected ValidationStatus validate(BusinessObjectModel bom) {\n        ValidationStatus validationStatus = new ValidationStatus();\n        for (BusinessObject bo : bom.getBusinessObjects()) {\n            validationStatus.addValidationStatus(validateThatThereIsNoCycleDependencies(bo, new ArrayList<>()));\n        }\n        return validationStatus;\n    }\n\n    private ValidationStatus validateThatThereIsNoCycleDependencies(BusinessObject bo, List<BusinessObject> parentBOs) {\n        ValidationStatus validationStatus = new ValidationStatus();\n        parentBOs.add(bo);\n        Map<String, Serializable> context = new HashMap<>();\n        context.put(StatusContext.BDM_ARTIFACT_NAME_KEY, bo.getQualifiedName());\n        for (BusinessObject businessObject : bo.getReferencedBusinessObjectsByComposition()) {\n            if (parentBOs.contains(businessObject)) {\n                context.put(StatusContext.BUSINESS_OBJECT_NAME_KEY, businessObject.getQualifiedName());\n                validationStatus.addError(StatusCode.CIRCULAR_COMPOSITION_REFERENCE, String.format(\n                        \"Business object %s has a circular composition reference to itself or is referenced several times in the object %s\",\n                        businessObject.getQualifiedName(), bo.getQualifiedName()), context);\n            } else {\n                validationStatus.addValidationStatus(validateThatThereIsNoCycleDependencies(businessObject, parentBOs));\n            }\n        }\n        return validationStatus;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/validator/rule/composition/UniquenessCompositionValidationRule.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator.rule.composition;\n\nimport static java.util.Collections.frequency;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.result.StatusCode;\nimport org.bonitasoft.engine.api.result.StatusContext;\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.validator.ValidationStatus;\nimport org.bonitasoft.engine.bdm.validator.rule.ValidationRule;\n\n/**\n * Check that a composite bo is referenced in only one composition\n *\n * @author Colin PUY\n */\npublic class UniquenessCompositionValidationRule extends ValidationRule<BusinessObjectModel, ValidationStatus> {\n\n    public UniquenessCompositionValidationRule() {\n        super(BusinessObjectModel.class);\n    }\n\n    @Override\n    protected ValidationStatus validate(BusinessObjectModel bom) {\n        ValidationStatus validationStatus = new ValidationStatus();\n        // Make sure there are no duplicates object in composition in the model:\n        final List<BusinessObject> objectsByComposition = bom.getReferencedBusinessObjectsByComposition();\n        objectsByComposition.stream()\n                .distinct()\n                .filter(compositeBO -> frequency(objectsByComposition, compositeBO) > 1)\n                .forEach(compositeBO -> validationStatus.addError(\n                        StatusCode.SEVERAL_COMPOSITION_REFERENCE_FOR_A_BUSINESS_OBJECT,\n                        String.format(\n                                \"Business object %s is referenced by composition in two business objects, or is referenced several times in a single business object\",\n                                compositeBO.getQualifiedName()),\n                        Map.of(StatusContext.BUSINESS_OBJECT_NAME_KEY, compositeBO.getQualifiedName())));\n        return validationStatus;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/ArchivedElement.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm;\n\nimport java.util.Date;\n\n/**\n * Interface <code>ArchivedElement</code> is the root interface of the archived client model hierarchy of Bonita.\n *\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @since 6.0.0\n */\npublic interface ArchivedElement {\n\n    /**\n     * Gets the date when the element was archived.\n     *\n     * @return the archived date\n     */\n    Date getArchiveDate();\n\n    /**\n     * Gets the identifier of the <code>BaseElement</code> that the archived element comes from.\n     *\n     * @return the identifier of the source object\n     */\n    long getSourceObjectId();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/ArchivedRestElement.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm;\n\nimport java.util.Date;\n\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonView;\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\nimport com.fasterxml.jackson.databind.ser.std.ToStringSerializer;\n\n/**\n * Interface <code>ArchivedRestElement</code> identifies an archived <code>BonitaObject</code> that can be used in the\n * REST API.\n */\npublic interface ArchivedRestElement extends ArchivedElement {\n\n    /**\n     * Gets the unique object identifier.\n     * Serialized as a String in json as a Java long can be too big for JavaScript\n     *\n     * @return the unique object identifier\n     */\n    @JsonSerialize(using = ToStringSerializer.class)\n    @Override\n    long getSourceObjectId();\n\n    /**\n     * Gets the date when the element was archived in milliseconds since epoch.\n     * Serialized as a String in json as a Java long can be too big for JavaScript\n     *\n     * @return the unique object identifier\n     */\n    @JsonView\n    @JsonProperty(\"archiveDate\")\n    @JsonSerialize(using = ToStringSerializer.class)\n    long getArchiveDateAsLong();\n\n    /**\n     * Gets the date when the element was archived.\n     * Ignored in Json serialization as it is already serialized as a long\n     *\n     * @return the archived date\n     */\n    @JsonIgnore\n    @Override\n    Date getArchiveDate();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/BaseRestElement.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm;\n\nimport com.fasterxml.jackson.annotation.JsonPropertyOrder;\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\nimport com.fasterxml.jackson.databind.ser.std.ToStringSerializer;\n\n/**\n * Interface <code>BaseRestElement</code> identifies a <code>BonitaObject</code> that can be used in the REST API.\n */\n@JsonPropertyOrder(alphabetic = true)\npublic interface BaseRestElement extends BaseElement {\n\n    /**\n     * Gets the unique object identifier.\n     * Serialized as a String in json as a Java long can be too big for JavaScript\n     *\n     * @return the unique object identifier\n     */\n    @JsonSerialize(using = ToStringSerializer.class)\n    @Override\n    long getId();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/actor/ActorCriterion.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.actor;\n\n/**\n * Criterion to sort {@link ActorInstance}.\n *\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @since 6.0.0\n * @version 6.4.1\n */\npublic enum ActorCriterion {\n\n    /**\n     * By ascending name\n     */\n    NAME_ASC,\n\n    /**\n     * By descending name\n     */\n    NAME_DESC;\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/actor/ActorInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.actor;\n\nimport org.bonitasoft.engine.bpm.BaseElement;\nimport org.bonitasoft.engine.bpm.DescriptionElement;\n\n/**\n * Once the {@link org.bonitasoft.engine.bpm.process.ProcessDefinition} is deployed, the associated\n * {@link ActorDefinition}s are instantiated.\n * This object represents this instance of {@link ActorDefinition}.\n *\n * @author Matthieu Chaffotte\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n * @since 6.0.0\n * @version 6.4.1\n */\npublic interface ActorInstance extends DescriptionElement, BaseElement {\n\n    /**\n     * Get the identifier of the process definition where this actor is defined.\n     *\n     * @return The identifier of the process definition where this actor is defined.\n     */\n    long getProcessDefinitionId();\n\n    /**\n     * The display name of the actor defined in {@link ActorDefinition#getName()}.\n     *\n     * @return The display name of the actor.\n     */\n    String getDisplayName();\n\n    /**\n     * Can this actor start the process ?\n     * Defined in {@link ActorDefinition#isInitiator()}.\n     *\n     * @return <code>true</code>} if this actor can start the process, <code>false</code> otherwise.\n     */\n    boolean isInitiator();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/actor/ActorMappingExportException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.actor;\n\nimport org.bonitasoft.engine.exception.ExecutionException;\n\n/**\n * Thrown when it's not possible to export the actor mappings.\n *\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @since 6.0.0\n * @version 6.4.1\n */\npublic class ActorMappingExportException extends ExecutionException {\n\n    private static final long serialVersionUID = -8085581554578731228L;\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public ActorMappingExportException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/actor/ActorMappingImportException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.actor;\n\nimport org.bonitasoft.engine.exception.ExecutionException;\n\n/**\n * Thrown when it's not possible to import the actor mappings.\n *\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @since 6.0.0\n * @version 6.4.1\n */\npublic class ActorMappingImportException extends ExecutionException {\n\n    private static final long serialVersionUID = -415010231497905681L;\n\n    /**\n     * Constructs a new exception with the specified detail message and cause.\n     *\n     * @param message\n     *        The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method).\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public ActorMappingImportException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public ActorMappingImportException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/actor/ActorNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.actor;\n\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.exception.NotFoundException;\n\n/**\n * Thrown when it's not possible to find an actor mapping.\n *\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @since 6.0.0\n * @version 6.4.1\n */\npublic class ActorNotFoundException extends NotFoundException {\n\n    private static final long serialVersionUID = 2518419716527606128L;\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public ActorNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n    /**\n     * Constructs a new exception with the specified detail message.\n     *\n     * @param message\n     *        The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method).\n     */\n    public ActorNotFoundException(final String message) {\n        super(message);\n    }\n\n    /**\n     * Constructs a new exception and the specified detail message.\n     *\n     * @param actorName\n     *        The not found actor\n     * @param processDefinition\n     *        The process definition where we search the actor\n     */\n    public ActorNotFoundException(final String actorName, final ProcessDefinition processDefinition) {\n        super(\"Actor \" + actorName + \" not found\");\n        setProcessDefinitionNameOnContext(processDefinition.getName());\n        setProcessDefinitionIdOnContext(processDefinition.getId());\n        setProcessDefinitionVersionOnContext(processDefinition.getVersion());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/actor/ActorUpdater.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.actor;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * The descriptor which contains the fields to update an actor.\n *\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @since 6.0.0\n * @version 6.4.1\n */\npublic class ActorUpdater implements Serializable {\n\n    private static final long serialVersionUID = -8812266250085063562L;\n\n    /**\n     * The fields that can be updated.\n     */\n    public enum ActorField {\n        /**\n         * Corresponding to the display name of the actor\n         */\n        DISPLAY_NAME,\n        /**\n         * Corresponding to the description of the actor\n         */\n        DESCRIPTION;\n    }\n\n    private final Map<ActorField, Serializable> fields;\n\n    /**\n     * The default constructor\n     */\n    public ActorUpdater() {\n        fields = new HashMap<ActorField, Serializable>(ActorField.values().length);\n    }\n\n    /**\n     * Set the new display name.\n     *\n     * @param name\n     *        The new display name\n     */\n    public void setDisplayName(final String name) {\n        fields.put(ActorField.DISPLAY_NAME, name);\n    }\n\n    /**\n     * Set the new description.\n     *\n     * @param description\n     *        The new description\n     */\n    public void setDescription(final String description) {\n        fields.put(ActorField.DESCRIPTION, description);\n    }\n\n    /**\n     * Get the fields to update, and the new value.\n     *\n     * @return The map containing the pairs (field, new value) to update.\n     */\n    public Map<ActorField, Serializable> getFields() {\n        return fields;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/actor/impl/ActorInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.actor.impl;\n\nimport org.bonitasoft.engine.bpm.actor.ActorInstance;\nimport org.bonitasoft.engine.bpm.internal.NamedElementImpl;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class ActorInstanceImpl extends NamedElementImpl implements ActorInstance {\n\n    private static final long serialVersionUID = 8251013663118023803L;\n\n    private final String displayName;\n\n    private final long processDefinitionId;\n\n    private final boolean initiator;\n\n    private final String description;\n\n    public ActorInstanceImpl(final String name, final String description, final String displayName,\n            final long processDefinitionId, final boolean initiator) {\n        super(name);\n        this.description = description;\n        this.processDefinitionId = processDefinitionId;\n        this.displayName = displayName;\n        this.initiator = initiator;\n    }\n\n    @Override\n    public long getProcessDefinitionId() {\n        return this.processDefinitionId;\n    }\n\n    @Override\n    public String getDisplayName() {\n        return this.displayName;\n    }\n\n    @Override\n    public boolean isInitiator() {\n        return initiator;\n    }\n\n    @Override\n    public String getDescription() {\n        return description;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/businessdata/BusinessDataQueryMetadata.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.businessdata;\n\nimport java.io.Serializable;\n\n/**\n * @author Laurent Leseigneur\n */\npublic interface BusinessDataQueryMetadata extends Serializable {\n\n    Integer getStartIndex();\n\n    Integer getMaxResults();\n\n    Long getCount();\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/businessdata/BusinessDataQueryResult.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.businessdata;\n\nimport java.io.Serializable;\n\nimport org.bonitasoft.engine.search.SearchResult;\n\n/**\n * @author Laurent Leseigneur\n */\npublic interface BusinessDataQueryResult extends Serializable, SearchResult {\n\n    Serializable getJsonResults();\n\n    BusinessDataQueryMetadata getBusinessDataQueryMetadata();\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/businessdata/impl/BusinessDataQueryMetadataImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.businessdata.impl;\n\nimport org.bonitasoft.engine.bpm.businessdata.BusinessDataQueryMetadata;\n\npublic class BusinessDataQueryMetadataImpl implements BusinessDataQueryMetadata {\n\n    private final Integer startIndex;\n    private final Integer maxResults;\n    private final Long count;\n\n    public BusinessDataQueryMetadataImpl() {\n        this(null, null, null);\n    };\n\n    public BusinessDataQueryMetadataImpl(Integer startIndex, Integer maxResults, Long count) {\n        this.startIndex = startIndex;\n        this.maxResults = maxResults;\n        this.count = count;\n    }\n\n    public Integer getStartIndex() {\n        return startIndex;\n    }\n\n    public Integer getMaxResults() {\n        return maxResults;\n    }\n\n    public Long getCount() {\n        return count;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/businessdata/impl/BusinessDataQueryResultImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.businessdata.impl;\n\nimport java.io.Serializable;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.businessdata.BusinessDataQueryResult;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class BusinessDataQueryResultImpl implements BusinessDataQueryResult {\n\n    private final Serializable jsonResults;\n    private final BusinessDataQueryMetadataImpl businessDataQueryMetadata;\n\n    public BusinessDataQueryResultImpl() {\n        this(null, null);\n    };\n\n    public BusinessDataQueryResultImpl(Serializable jsonResults,\n            BusinessDataQueryMetadataImpl businessDataQueryMetadata) {\n        this.jsonResults = jsonResults;\n        this.businessDataQueryMetadata = businessDataQueryMetadata;\n    }\n\n    public Serializable getJsonResults() {\n        return jsonResults;\n    }\n\n    public BusinessDataQueryMetadataImpl getBusinessDataQueryMetadata() {\n        return businessDataQueryMetadata;\n    }\n\n    @Override\n    public long getCount() {\n        return 0;\n    }\n\n    @Override\n    public List getResult() {\n        return null;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/category/Category.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.category;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.bpm.BonitaObject;\n\n/**\n * Category forms part of the ProcessDefinition.\n *\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic interface Category extends BonitaObject {\n\n    /**\n     * @return The identifier of the category\n     */\n    long getId();\n\n    /**\n     * @return The name of the category\n     */\n    String getName();\n\n    /**\n     * @return The description of the category\n     */\n    String getDescription();\n\n    /**\n     * @return The identifier of the user that created the category\n     */\n    long getCreator();\n\n    /**\n     * @return The date of creation of the category\n     */\n    Date getCreationDate();\n\n    /**\n     * @return The last date when the category is updated\n     */\n    Date getLastUpdate();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/category/CategoryCriterion.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.category;\n\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.Sort;\n\n/**\n * Criterion to sort categories\n *\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic enum CategoryCriterion {\n\n    /**\n     * By ascending name\n     */\n    NAME_ASC(\"name\", Order.ASC),\n\n    /**\n     * By descending name\n     */\n    NAME_DESC(\"name\", Order.DESC);\n\n    private final String field;\n\n    private final Order order;\n\n    CategoryCriterion(final String field, final Order order) {\n        this.field = field;\n        this.order = order;\n    }\n\n    public Order getOrder() {\n        return order;\n    }\n\n    public String getField() {\n        return field;\n    }\n\n    public Sort getSort() {\n        return new Sort(order, field);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/category/CategoryNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.category;\n\nimport org.bonitasoft.engine.exception.NotFoundException;\n\n/**\n * Thrown when it's not possible to find a category.\n * The class CategoryNotFoundException is a form of Throwable that indicates conditions that a reasonable application\n * might want to catch.\n * The class CategoryNotFoundException that is not also subclasses of {@link RuntimeException} are checked exceptions.\n * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by\n * the execution of the method or\n * constructor and propagate outside the method or constructor boundary.\n *\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class CategoryNotFoundException extends NotFoundException {\n\n    private static final long serialVersionUID = 7030553624142851456L;\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public CategoryNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/category/CategoryUpdater.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.category;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class CategoryUpdater implements Serializable {\n\n    private static final long serialVersionUID = 8964190598176457557L;\n\n    /**\n     * The fields that can be updated.\n     */\n    public enum CategoryField {\n        /**\n         * The name of the field corresponding to the name of the category\n         */\n        NAME,\n\n        /**\n         * The name of the field corresponding to the description of the category\n         */\n        DESCRIPTION;\n    }\n\n    private final Map<CategoryField, Serializable> fields;\n\n    /**\n     * The default constructor\n     */\n    public CategoryUpdater() {\n        fields = new HashMap<CategoryField, Serializable>(5);\n    }\n\n    /**\n     * Set the new name\n     *\n     * @param name\n     *        The new name\n     * @return The CategoryUpdater containing the new name\n     */\n    public CategoryUpdater setName(final String name) {\n        fields.put(CategoryField.NAME, name);\n        return this;\n    }\n\n    /**\n     * Set the new description\n     *\n     * @param description\n     *        The new description\n     * @return The CategoryUpdater containing the new description\n     */\n    public CategoryUpdater setDescription(final String description) {\n        fields.put(CategoryField.DESCRIPTION, description);\n        return this;\n    }\n\n    /**\n     * Get the fields to update, and the new value\n     *\n     * @return The map containing the pairs (field, new value) to update.\n     */\n    public Map<CategoryField, Serializable> getFields() {\n        return fields;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/category/impl/CategoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.category.impl;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.bpm.category.Category;\n\n/**\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class CategoryImpl implements Category {\n\n    private static final long serialVersionUID = 5609899843000875034L;\n\n    private final long id;\n\n    private final String name;\n\n    private String description;\n\n    private long creator;\n\n    private Date creationDate;\n\n    private Date lastUpdate;\n\n    public CategoryImpl(final long id, final String name) {\n        super();\n        this.id = id;\n        this.name = name;\n    }\n\n    @Override\n    public long getId() {\n        return id;\n    }\n\n    @Override\n    public String getName() {\n        return name;\n    }\n\n    @Override\n    public String getDescription() {\n        return description;\n    }\n\n    @Override\n    public long getCreator() {\n        return creator;\n    }\n\n    @Override\n    public Date getCreationDate() {\n        return creationDate;\n    }\n\n    @Override\n    public Date getLastUpdate() {\n        return lastUpdate;\n    }\n\n    public void setDescription(final String description) {\n        this.description = description;\n    }\n\n    public void setCreator(final long creator) {\n        this.creator = creator;\n    }\n\n    public void setCreationDate(final Date creationDate) {\n        this.creationDate = creationDate;\n    }\n\n    public void setLastUpdate(final Date lastUpdate) {\n        this.lastUpdate = lastUpdate;\n    }\n\n    @Override\n    public String toString() {\n        return \"CategoryImpl [id=\" + id + \", name=\" + name + \", description=\" + description + \", creator=\" + creator\n                + \", creationDate=\" + creationDate\n                + \", lastUpdate=\" + lastUpdate + \"]\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/category/package-info.java",
    "content": "/**\n * Copyright (C) 2015 BonitaSoft S.A.\n * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n/**\n * <p>\n * This package contains everything concerning Category : Criterion, Exceptions, Updater...\n * </p>\n */\npackage org.bonitasoft.engine.bpm.category;\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/comment/ArchivedComment.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.comment;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.bpm.ArchivedElement;\nimport org.bonitasoft.engine.bpm.BaseElement;\nimport org.bonitasoft.engine.bpm.NamedElement;\n\n/**\n * The archived comment associated to a process instance\n *\n * @author Zhang Bole\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic interface ArchivedComment extends NamedElement, BaseElement, ArchivedElement {\n\n    /**\n     * @return The identifier of the user that posted the comment\n     */\n    Long getUserId();\n\n    /**\n     * @return The identifier of the process instance associated to the comment\n     */\n    long getProcessInstanceId();\n\n    /**\n     * @return The date to which the comment was posted.\n     */\n    Date getPostDate();\n\n    /**\n     * @return The content of the comment\n     */\n    String getContent();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/comment/ArchivedCommentsSearchDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.comment;\n\n/**\n * The fields on which a search can be made for the archived comments.\n *\n * @author Hongwen Zang\n * @author Celine Souchet\n */\npublic class ArchivedCommentsSearchDescriptor {\n\n    /**\n     * The name of the field corresponding to the identifier of the process instance associated to the comment\n     */\n    public static final String PROCESS_INSTANCE_ID = \"processInstanceId\";\n\n    /**\n     * The name of the field corresponding to the identifier of the user that posted the comment\n     */\n    public static final String POSTED_BY_ID = \"userId\";\n\n    /**\n     * The name of the field corresponding to the identifier of the archived comment\n     */\n    public static final String ID = \"id\";\n\n    /**\n     * The name of the field corresponding to the username of the user that posted the comment\n     */\n    public static final String USER_NAME = \"userName\";\n\n    /**\n     * The name of the field corresponding to the content of the comment\n     */\n    public static final String CONTENT = \"content\";\n\n    /**\n     * The name of the field corresponding to the date to which the comment was posted.\n     */\n    public static final String POSTDATE = \"postdate\";\n\n    /**\n     * The name of the field corresponding to the date to which the comment was archived.\n     */\n    public static final String ARCHIVE_DATE = \"archiveDate\";\n\n    /**\n     * The name of the field corresponding to the identifier of the comment (not archived)\n     */\n    public static final String SOURCE_OBJECT_ID = \"sourceObjectId\";\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/comment/Comment.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.comment;\n\nimport org.bonitasoft.engine.bpm.BonitaObject;\n\n/**\n * The comment associated to a process instance\n *\n * @author Hongwen Zang\n * @author Celine Souchet\n */\npublic interface Comment extends BonitaObject {\n\n    /**\n     * @return The identifier of the comment\n     */\n    long getId();\n\n    /**\n     * @return The identifier of the user that posted the comment\n     */\n    Long getUserId();\n\n    /**\n     * @return The identifier of the process instance associated to the comment\n     */\n    long getProcessInstanceId();\n\n    /**\n     * @return The date to which the comment was posted.\n     */\n    long getPostDate();\n\n    /**\n     * @return The content of the comment\n     */\n    String getContent();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/comment/SearchCommentsDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.comment;\n\n/**\n * The fields on which a search can be made for the comments.\n *\n * @author Hongwen Zang\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n */\npublic class SearchCommentsDescriptor {\n\n    /**\n     * The name of the field corresponding to the identifier of the process instance associated to the comment\n     */\n    public static final String PROCESS_INSTANCE_ID = \"processInstanceId\";\n\n    /**\n     * The name of the field corresponding to the identifier of the user that posted the comment\n     */\n    public static final String POSTED_BY_ID = \"userId\";\n\n    /**\n     * The name of the field corresponding to the identifier of the comment\n     */\n    public static final String ID = \"id\";\n\n    /**\n     * The name of the field corresponding to the username of the user that posted the comment\n     */\n    public static final String USER_NAME = \"userName\";\n\n    /**\n     * The name of the field corresponding to the content of the comment\n     */\n    public static final String CONTENT = \"content\";\n\n    /**\n     * The name of the field corresponding to the date to which the comment was posted.\n     */\n    public static final String POSTDATE = \"postdate\";\n\n    /**\n     * The name of the field corresponding to the kind of the comment\n     */\n    public static final String KIND = \"kind\";\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/comment/impl/ArchivedCommentImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.comment.impl;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.bpm.comment.ArchivedComment;\nimport org.bonitasoft.engine.bpm.internal.NamedElementImpl;\n\n/**\n * @author Zhang Bole\n */\npublic class ArchivedCommentImpl extends NamedElementImpl implements ArchivedComment {\n\n    private static final long serialVersionUID = -6573747806944970703L;\n\n    private long id;\n\n    private Date archiveDate;\n\n    private long processInstanceId;\n\n    private long sourceObjectId;\n\n    private Long userId;\n\n    private Date postDate;\n\n    private String content;\n\n    public ArchivedCommentImpl(final String name) {\n        super(name);\n    }\n\n    @Override\n    public long getId() {\n        return id;\n    }\n\n    @Override\n    public void setId(final long id) {\n        this.id = id;\n    }\n\n    @Override\n    public Date getArchiveDate() {\n        return archiveDate;\n    }\n\n    public void setArchiveDate(final Date archiveDate) {\n        this.archiveDate = archiveDate;\n    }\n\n    @Override\n    public long getProcessInstanceId() {\n        return processInstanceId;\n    }\n\n    public void setProcessInstanceId(final long processInstanceId) {\n        this.processInstanceId = processInstanceId;\n    }\n\n    @Override\n    public long getSourceObjectId() {\n        return sourceObjectId;\n    }\n\n    public void setSourceObjectId(final long sourceObjectId) {\n        this.sourceObjectId = sourceObjectId;\n    }\n\n    @Override\n    public Long getUserId() {\n        return userId;\n    }\n\n    public void setUserId(final Long userId) {\n        this.userId = userId;\n    }\n\n    @Override\n    public Date getPostDate() {\n        return postDate;\n    }\n\n    public void setPostDate(final Date postDate) {\n        this.postDate = postDate;\n    }\n\n    @Override\n    public String getContent() {\n        return content;\n    }\n\n    public void setContent(final String content) {\n        this.content = content;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/comment/impl/CommentImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.comment.impl;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.bpm.comment.Comment;\n\n/**\n * @author Hongwen Zang\n * @author Matthieu Chaffotte\n */\n@Data\n@NoArgsConstructor\npublic class CommentImpl implements Comment {\n\n    private static final long serialVersionUID = 2599025748483260550L;\n\n    private long id;\n\n    private Long userId;\n\n    private long processInstanceId;\n\n    private long postDate;\n\n    private String content;\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/comment/package-info.java",
    "content": "/**\n * Copyright (C) 2015 BonitaSoft S.A.\n * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n/**\n * <p>\n * This package contains everything concerning Comment : Archived, Search descriptor...\n * </p>\n */\npackage org.bonitasoft.engine.bpm.comment;\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/ArchiveConnectorInstancesSearchDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.connector;\n\n/**\n * The fields on which a search can be made for the archived connectors.\n *\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic final class ArchiveConnectorInstancesSearchDescriptor {\n\n    /**\n     * The name of the field corresponding to the name of the connector\n     */\n    public static final String NAME = \"name\";\n\n    /**\n     * The name of the field corresponding to the state of the connector\n     */\n    public static final String STATE = \"state\";\n\n    /**\n     * The name of the field corresponding to the event to activate the connector\n     */\n    public static final String ACTIVATION_EVENT = \"activationEvent\";\n\n    /**\n     * The name of the field corresponding to the type of the container of the connector\n     */\n    public static final String CONTAINER_TYPE = \"containerType\";\n\n    /**\n     * The name of the field corresponding to the identifier of the container of the connector\n     */\n    public static final String CONTAINER_ID = \"containerId\";\n\n    /**\n     * The name of the field corresponding to the identifier of the definition of the connector\n     */\n    public static final String CONNECTOR_DEFINITION_ID = \"connectorDefinitionId\";\n\n    /**\n     * The name of the field corresponding to the version of the definition of the connector\n     */\n    public static final String CONNECTOR_DEFINITION_VERSION = \"connectorDefinitionVersion\";\n\n    /**\n     * The name of the field corresponding to the date to which the connector was archived.\n     */\n    public static final String ARCHIVE_DATE = \"archiveDate\";\n\n    /**\n     * The name of the field corresponding to the identifier of the connector (not archived)\n     */\n    public static final String SOURCE_OBJECT_ID = \"sourceObjectId\";\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/ArchivedConnectorInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.connector;\n\nimport org.bonitasoft.engine.bpm.ArchivedElement;\nimport org.bonitasoft.engine.bpm.BaseElement;\nimport org.bonitasoft.engine.bpm.NamedElement;\n\n/**\n * The archived connector\n *\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic interface ArchivedConnectorInstance extends NamedElement, BaseElement, ArchivedElement {\n\n    /**\n     * Container type : FlowNode\n     */\n    String FLOWNODE_TYPE = \"flowNode\";\n\n    /**\n     * Container type : Process\n     */\n    String PROCESS_TYPE = \"process\";\n\n    /**\n     * @return The identifier of the container of the connector\n     */\n    long getContainerId();\n\n    /**\n     * @return The type of the container of the connector. The connector can be on a FlowNode or a Process.\n     */\n    String getContainerType();\n\n    /**\n     * @return The identifier of the container of the connector\n     */\n    String getConnectorId();\n\n    /**\n     * @return The version of the connector\n     */\n    String getVersion();\n\n    /**\n     * @return The event to activate the connector\n     */\n    ConnectorEvent getActivationEvent();\n\n    /**\n     * @return The state of the connector\n     */\n    ConnectorState getState();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/ConnectorCriterion.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.connector;\n\n/**\n * Criterion to sort connectors\n *\n * @author Yanyan Liu\n * @author Celine Souchet\n */\npublic enum ConnectorCriterion {\n    /**\n     * By ascending identifier of connector definition\n     */\n    DEFINITION_ID_ASC,\n\n    /**\n     * By descending identifier of connector definition\n     */\n    DEFINITION_ID_DESC,\n\n    /**\n     * By ascending version of connector definition\n     */\n    DEFINITION_VERSION_ASC,\n\n    /**\n     * By descending version of connector definition\n     */\n    DEFINITION_VERSION_DESC,\n\n    /**\n     * By ascending identifier of connector implementation\n     */\n\n    IMPLEMENTATION_ID_ASC,\n\n    /**\n     * By descending identifier of connector implementation\n     */\n    IMPLEMENTATION_ID_DESC,\n\n    /**\n     * By ascending version of connector implementation\n     */\n    IMPLEMENTATION_VERSION_ASC,\n\n    /**\n     * By descending version of connector implementation\n     */\n    IMPLEMENTATIONN_VERSION_DESC,\n\n    /**\n     * By ascending class name of connector implementation\n     */\n    IMPLEMENTATIONN_CLASS_NAME_ACS,\n\n    /**\n     * By descending class name of connector implementation\n     */\n    IMPLEMENTATIONN_CLASS_NAME_DESC,\n\n    /**\n     * By descending identifier of connector implementation\n     */\n    DEFAULT\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/ConnectorDefinitionWithInputValues.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.connector;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface ConnectorDefinitionWithInputValues extends Serializable {\n\n    /**\n     * @return\n     *         the connector definition\n     */\n    ConnectorDefinition getConnectorDefinition();\n\n    /**\n     * @return\n     *         the input values to evaluate input expressions of the connector\n     */\n    Map<String, Map<String, Serializable>> getInputValues();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/ConnectorExecutionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.connector;\n\nimport org.bonitasoft.engine.exception.ExecutionException;\n\n/**\n * Thrown when it's not possible to execute correctly the connector.\n * The class ConnectorExecutionException is a form of Throwable that indicates conditions that a reasonable application\n * might want to catch.\n * The class ConnectorExecutionException that is not also subclasses of {@link RuntimeException} are checked exceptions.\n * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by\n * the execution of the method or\n * constructor and propagate outside the method or constructor boundary.\n *\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class ConnectorExecutionException extends ExecutionException {\n\n    private static final long serialVersionUID = -5951610392778908072L;\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public ConnectorExecutionException(final Throwable cause) {\n        super(cause);\n    }\n\n    /**\n     * Constructs a new exception with the specified detail message and cause.\n     *\n     * @param message\n     *        The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method).\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public ConnectorExecutionException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    /**\n     * Constructs a new exception with the specified detail message.\n     *\n     * @param message\n     *        The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method).\n     */\n    public ConnectorExecutionException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/ConnectorImplementationDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.connector;\n\nimport java.io.Serializable;\nimport java.util.List;\n\n/**\n * The fields on which a search can be made for the connector implementation.\n *\n * @author Yanyan Liu\n * @author Celine Souchet\n */\npublic class ConnectorImplementationDescriptor implements Serializable {\n\n    private static final long serialVersionUID = -3988746732940581935L;\n\n    /**\n     * The name of the field corresponding to the class name of the implementation of the connector\n     */\n    public static final String IMPLEMENTATION_CLASS_NAME = \"implementationClassName\";\n\n    /**\n     * The name of the field corresponding to the identifier of the connector\n     */\n    public static final String ID = \"id\";\n\n    /**\n     * The name of the field corresponding to the version of the connector\n     */\n    public static final String VERSIOIN = \"version\";\n\n    /**\n     * The name of the field corresponding to the identifier of the definition of the connector\n     */\n    public static final String DEFINITION_ID = \"definitionId\";\n\n    /**\n     * The name of the field corresponding to the version of the definition of the connector\n     */\n    public static final String DEFINITION_VERSION = \"definitionVersion\";\n\n    private String implementationClassName;\n\n    private String id;\n\n    private String version;\n\n    private String definitionId;\n\n    private String definitionVersion;\n\n    private List<String> jarDependencies;\n\n    /**\n     * The default constructor\n     */\n    public ConnectorImplementationDescriptor() {\n        super();\n    }\n\n    /**\n     * @param implementationClassName\n     *        The implementation of the connector\n     * @param id\n     *        The identifier of the connector\n     * @param version\n     *        The version of the connector\n     * @param definitionId\n     *        The identifier of the definition of the connector\n     * @param definitionVersion\n     *        The version of the definition of the connector\n     * @param jarDependencies\n     *        The dependencies of the connector (path of the JAR files)\n     */\n    public ConnectorImplementationDescriptor(final String implementationClassName, final String id,\n            final String version, final String definitionId,\n            final String definitionVersion, final List<String> jarDependencies) {\n        super();\n        this.implementationClassName = implementationClassName;\n        this.id = id;\n        this.version = version;\n        this.definitionId = definitionId;\n        this.definitionVersion = definitionVersion;\n        this.jarDependencies = jarDependencies;\n    }\n\n    /**\n     * @return The implementation of the connector\n     */\n    public String getImplementationClassName() {\n        return implementationClassName;\n    }\n\n    /**\n     * @return\n     *         The identifier of the connector\n     */\n    public String getId() {\n        return id;\n    }\n\n    /**\n     * @return The version of the connector\n     */\n    public String getVersion() {\n        return version;\n    }\n\n    /**\n     * @return\n     *         The identifier of the definition of the connector\n     */\n    public String getDefinitionId() {\n        return definitionId;\n    }\n\n    /**\n     * @return\n     *         The version of the definition of the connector\n     */\n    public String getDefinitionVersion() {\n        return definitionVersion;\n    }\n\n    /**\n     * @return The list of the dependencies of the connector (path of the JAR files)\n     */\n    public List<String> getJarDependencies() {\n        return jarDependencies;\n    }\n\n    @Override\n    public String toString() {\n        return \"ConnectorImplementation [implementationClassName=\" + implementationClassName + \", id=\" + id\n                + \", version=\" + version + \", definitionId=\"\n                + definitionId + \", definitionVersion=\" + definitionVersion + \", jarDependencies=\" + jarDependencies\n                + \"]\";\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/ConnectorInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.connector;\n\nimport org.bonitasoft.engine.bpm.BaseElement;\nimport org.bonitasoft.engine.bpm.NamedElement;\n\n/**\n * Represents a connector, once instanciated by the containinig activity or process at runtime.\n *\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic interface ConnectorInstance extends NamedElement, BaseElement {\n\n    /**\n     *\n     */\n    String FLOWNODE_TYPE = \"flowNode\";\n\n    /**\n     *\n     */\n    String PROCESS_TYPE = \"process\";\n\n    /**\n     * @return The identifier of the containing element (process or activity)\n     */\n    long getContainerId();\n\n    /**\n     * @return The type of the connector container (PROCESS or ACTIVITY)\n     */\n    String getContainerType();\n\n    /**\n     * @return The identifier of the connector.\n     */\n    String getConnectorId();\n\n    /**\n     * @return the version of the connector.\n     */\n    String getVersion();\n\n    /**\n     * @return where this connector should be activated.\n     * @see ConnectorEvent\n     */\n    ConnectorEvent getActivationEvent();\n\n    /**\n     * @return the state of the connector ({@link ConnectorState#TO_BE_EXECUTED} before the first execution)\n     * @see ConnectorState\n     */\n    ConnectorState getState();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/ConnectorInstanceCriterion.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.connector;\n\n/**\n * @author Baptiste Mesta\n */\npublic enum ConnectorInstanceCriterion {\n\n    NAME_ASC, NAME_DESC,\n\n    CONTAINER_ID_ASC, CONTAINER_ID__DESC,\n\n    CONNECTOR_ID_ASC, CONNECTOR_ID__DESC,\n\n    VERSION_ASC, VERSION_DESC,\n\n    ACTIVATION_EVENT_ASC, ACTIVATION_EVENT_DESC,\n\n    STATE_ASC, STATE_DESC,\n\n    DEFAULT\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/ConnectorInstanceNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.connector;\n\nimport org.bonitasoft.engine.exception.NotFoundException;\n\n/**\n * Thrown when it's not possible to find the connector instance.\n * The class ConnectorInstanceNotFoundException is a form of Throwable that indicates conditions that a reasonable\n * application might want to catch.\n * The class ConnectorInstanceNotFoundException that is not also subclasses of {@link RuntimeException} are checked\n * exceptions.\n * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by\n * the execution of the method or\n * constructor and propagate outside the method or constructor boundary.\n *\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class ConnectorInstanceNotFoundException extends NotFoundException {\n\n    private static final long serialVersionUID = -2090512000101549113L;\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public ConnectorInstanceNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n    /**\n     * Constructs a new exception with the specified detail message.\n     *\n     * @param message\n     *        The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method).\n     */\n    public ConnectorInstanceNotFoundException(final String message) {\n        super(message);\n    }\n\n    /**\n     * Constructs a new exception with the specified detail message and cause.\n     *\n     * @param message\n     *        The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method).\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public ConnectorInstanceNotFoundException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/ConnectorInstanceWithFailureInfo.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.connector;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface ConnectorInstanceWithFailureInfo extends ConnectorInstance {\n\n    String getExceptionMessage();\n\n    String getStackTrace();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/ConnectorInstancesSearchDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.connector;\n\n/**\n * @author Baptiste Mesta\n */\npublic final class ConnectorInstancesSearchDescriptor {\n\n    public static final String NAME = \"name\";\n\n    // use ConnectorState.<VALUE>.name() for the value when using STATE as filter:\n    public static final String STATE = \"state\";\n\n    public static final String ACTIVATION_EVENT = \"activationEvent\";\n\n    public static final String CONTAINER_TYPE = \"containerType\";\n\n    public static final String CONTAINER_ID = \"containerId\";\n\n    public static final String CONNECTOR_DEFINITION_ID = \"connectorDefinitionId\";\n\n    public static final String CONNECTOR_DEFINITION_VERSION = \"connectorDefinitionVersion\";\n\n    public static final String EXECUTION_ORDER = \"executionOrder\";\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/ConnectorNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.connector;\n\nimport org.bonitasoft.engine.exception.NotFoundException;\n\n/**\n * Thrown when a referenced connector cannot be found (generally from its ID + Version).\n * The class ConnectorNotFoundException is a form of Throwable that indicates conditions that a reasonable application\n * might want to catch.\n * The class ConnectorNotFoundException that is not also subclasses of {@link RuntimeException} are checked exceptions.\n * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by\n * the execution of the method or\n * constructor and propagate outside the method or constructor boundary.\n *\n * @author Yanyan Liu\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n */\npublic class ConnectorNotFoundException extends NotFoundException {\n\n    private static final long serialVersionUID = 5630437277994804677L;\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public ConnectorNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/ConnectorState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.connector;\n\n/**\n * Current state of a connector instance.\n *\n * @author Baptiste Mesta\n */\npublic enum ConnectorState {\n\n    TO_BE_EXECUTED, EXECUTING, TO_RE_EXECUTE, DONE, FAILED, SKIPPED\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/ConnectorStateReset.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.connector;\n\n/**\n * @author Baptiste Mesta\n */\npublic enum ConnectorStateReset {\n\n    TO_RE_EXECUTE, SKIPPED\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/InvalidConnectorImplementationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.connector;\n\nimport org.bonitasoft.engine.exception.BonitaException;\n\n/**\n * Thrown when the connector implementation is invalid.\n * The class InvalidConnectorImplementationException is a form of Throwable that indicates conditions that a reasonable\n * application might want to catch.\n * The class InvalidConnectorImplementationException that is not also subclasses of {@link RuntimeException} are checked\n * exceptions.\n * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by\n * the execution of the method or\n * constructor and propagate outside the method or constructor boundary.\n *\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class InvalidConnectorImplementationException extends BonitaException {\n\n    private static final long serialVersionUID = -3389338645646919222L;\n\n    /**\n     * Constructs a new exception with the specified detail message.\n     *\n     * @param message\n     *        The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method).\n     */\n    public InvalidConnectorImplementationException(final String message) {\n        super(message);\n    }\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public InvalidConnectorImplementationException(final Throwable cause) {\n        super(cause);\n    }\n\n    /**\n     * Constructs a new exception with the specified detail message and cause.\n     *\n     * @param message\n     *        The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method).\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public InvalidConnectorImplementationException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/InvalidEvaluationConnectorConditionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.connector;\n\nimport org.bonitasoft.engine.exception.BonitaException;\n\n/**\n * Thrown when the evaluation of the condition of the connector instance is invalid.\n * The class InvalidEvaluationConnectorConditionException is a form of Throwable that indicates conditions that a\n * reasonable application might want to catch.\n * The class InvalidEvaluationConnectorConditionException that is not also subclasses of {@link RuntimeException} are\n * checked exceptions.\n * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by\n * the execution of the method or\n * constructor and propagate outside the method or constructor boundary.\n *\n * @author Zhao Na\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class InvalidEvaluationConnectorConditionException extends BonitaException {\n\n    private static final long serialVersionUID = -7035298849808114112L;\n\n    /**\n     * Constructs a new exception with the two conditions to compare\n     *\n     * @param condition1\n     *        The first condition\n     * @param condition2\n     *        The second condition\n     */\n    public InvalidEvaluationConnectorConditionException(final int condition1, final int condition2) {\n        super(condition1 + \" is not equal to \" + condition2 + \" .\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/impl/ArchivedConnectorInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.connector.impl;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.bpm.connector.ArchivedConnectorInstance;\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.bpm.connector.ConnectorState;\nimport org.bonitasoft.engine.bpm.internal.NamedElementImpl;\n\n/**\n * @author Baptiste Mesta\n */\npublic class ArchivedConnectorInstanceImpl extends NamedElementImpl implements ArchivedConnectorInstance {\n\n    private static final long serialVersionUID = 1740487116886845229L;\n\n    private final Date archiveDate;\n\n    private final long containerId;\n\n    private final String containerType;\n\n    private final String connectorId;\n\n    private final String version;\n\n    private final ConnectorEvent activationEvent;\n\n    private final ConnectorState state;\n\n    private final long sourceObjectId;\n\n    public ArchivedConnectorInstanceImpl(final String name, final Date archiveDate, final long containerId,\n            final String containerType,\n            final String connectorId, final String version, final ConnectorEvent activationEvent,\n            final ConnectorState state, final long sourceObjectId) {\n        super(name);\n        this.archiveDate = archiveDate;\n        this.containerId = containerId;\n        this.containerType = containerType;\n        this.connectorId = connectorId;\n        this.version = version;\n        this.activationEvent = activationEvent;\n        this.state = state;\n        this.sourceObjectId = sourceObjectId;\n    }\n\n    /**\n     * @return the archiveDate\n     */\n    @Override\n    public Date getArchiveDate() {\n        return archiveDate;\n    }\n\n    /**\n     * @return the containerId\n     */\n    @Override\n    public long getContainerId() {\n        return containerId;\n    }\n\n    /**\n     * @return the containerType\n     */\n    @Override\n    public String getContainerType() {\n        return containerType;\n    }\n\n    /**\n     * @return the connectorId\n     */\n    @Override\n    public String getConnectorId() {\n        return connectorId;\n    }\n\n    /**\n     * @return the version\n     */\n    @Override\n    public String getVersion() {\n        return version;\n    }\n\n    /**\n     * @return the activationEvent\n     */\n    @Override\n    public ConnectorEvent getActivationEvent() {\n        return activationEvent;\n    }\n\n    /**\n     * @return the state\n     */\n    @Override\n    public ConnectorState getState() {\n        return state;\n    }\n\n    @Override\n    public long getSourceObjectId() {\n        return sourceObjectId;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = super.hashCode();\n        result = prime * result + (activationEvent == null ? 0 : activationEvent.hashCode());\n        result = prime * result + (archiveDate == null ? 0 : archiveDate.hashCode());\n        result = prime * result + (connectorId == null ? 0 : connectorId.hashCode());\n        result = prime * result + (int) (containerId ^ containerId >>> 32);\n        result = prime * result + (containerType == null ? 0 : containerType.hashCode());\n        result = prime * result + (int) (sourceObjectId ^ sourceObjectId >>> 32);\n        result = prime * result + (state == null ? 0 : state.hashCode());\n        result = prime * result + (version == null ? 0 : version.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (!super.equals(obj)) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        final ArchivedConnectorInstanceImpl other = (ArchivedConnectorInstanceImpl) obj;\n        if (activationEvent != other.activationEvent) {\n            return false;\n        }\n        if (archiveDate == null) {\n            if (other.archiveDate != null) {\n                return false;\n            }\n        } else if (!archiveDate.equals(other.archiveDate)) {\n            return false;\n        }\n        if (connectorId == null) {\n            if (other.connectorId != null) {\n                return false;\n            }\n        } else if (!connectorId.equals(other.connectorId)) {\n            return false;\n        }\n        if (containerId != other.containerId) {\n            return false;\n        }\n        if (containerType == null) {\n            if (other.containerType != null) {\n                return false;\n            }\n        } else if (!containerType.equals(other.containerType)) {\n            return false;\n        }\n        if (sourceObjectId != other.sourceObjectId) {\n            return false;\n        }\n        if (state != other.state) {\n            return false;\n        }\n        if (version == null) {\n            if (other.version != null) {\n                return false;\n            }\n        } else if (!version.equals(other.version)) {\n            return false;\n        }\n        return true;\n    }\n\n    @Override\n    public String toString() {\n        return \"ArchivedConnectorInstance [archiveDate=\" + archiveDate + \", containerId=\" + containerId\n                + \", containerType=\" + containerType + \", connectorId=\"\n                + connectorId + \", version=\" + version + \", activationEvent=\" + activationEvent + \", state=\" + state\n                + \", sourceObjectId=\" + sourceObjectId\n                + \", name=\" + getName() + \"]\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/impl/ConnectorDefinitionWithInputValuesImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.connector.impl;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorDefinition;\nimport org.bonitasoft.engine.bpm.connector.ConnectorDefinitionWithInputValues;\n\n/**\n * @author Baptiste Mesta\n */\npublic class ConnectorDefinitionWithInputValuesImpl implements ConnectorDefinitionWithInputValues {\n\n    private static final long serialVersionUID = -8750501172227766274L;\n\n    private final ConnectorDefinition connectorDefinition;\n\n    private final Map<String, Map<String, Serializable>> inputValues;\n\n    public ConnectorDefinitionWithInputValuesImpl(final ConnectorDefinition connectorDefinition,\n            final Map<String, Map<String, Serializable>> inputValues) {\n        super();\n        this.connectorDefinition = connectorDefinition;\n        this.inputValues = inputValues;\n    }\n\n    @Override\n    public ConnectorDefinition getConnectorDefinition() {\n        return connectorDefinition;\n    }\n\n    @Override\n    public Map<String, Map<String, Serializable>> getInputValues() {\n        return inputValues;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/impl/ConnectorInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.connector.impl;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.bpm.connector.ConnectorInstance;\nimport org.bonitasoft.engine.bpm.connector.ConnectorState;\nimport org.bonitasoft.engine.bpm.internal.NamedElementImpl;\n\n/**\n * @author Baptiste Mesta\n */\npublic class ConnectorInstanceImpl extends NamedElementImpl implements ConnectorInstance {\n\n    private static final long serialVersionUID = 2148709030350403891L;\n\n    private final long containerId;\n\n    private final String containerType;\n\n    private final String connectorId;\n\n    private final String version;\n\n    private final ConnectorState state;\n\n    private final ConnectorEvent activationEvent;\n\n    public ConnectorInstanceImpl(final String name, final long containerId, final String containerType,\n            final String connectorId, final String version,\n            final ConnectorState state, final ConnectorEvent activationEvent) {\n        super(name);\n        this.containerId = containerId;\n        this.containerType = containerType;\n        this.connectorId = connectorId;\n        this.version = version;\n        this.state = state;\n        this.activationEvent = activationEvent;\n    }\n\n    @Override\n    public long getContainerId() {\n        return containerId;\n    }\n\n    @Override\n    public String getContainerType() {\n        return containerType;\n    }\n\n    @Override\n    public String getConnectorId() {\n        return connectorId;\n    }\n\n    @Override\n    public String getVersion() {\n        return version;\n    }\n\n    @Override\n    public ConnectorEvent getActivationEvent() {\n        return activationEvent;\n    }\n\n    @Override\n    public ConnectorState getState() {\n        return state;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/impl/ConnectorInstanceWithFailureInfoImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.connector.impl;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.bpm.connector.ConnectorInstanceWithFailureInfo;\nimport org.bonitasoft.engine.bpm.connector.ConnectorState;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ConnectorInstanceWithFailureInfoImpl extends ConnectorInstanceImpl\n        implements ConnectorInstanceWithFailureInfo {\n\n    private static final long serialVersionUID = 7158777025106286625L;\n\n    public ConnectorInstanceWithFailureInfoImpl(final String name, final long containerId, final String containerType,\n            final String connectorId,\n            String version, ConnectorState state,\n            final ConnectorEvent activationEvent, final String exceptionMessage, final String stackTrace) {\n        super(name, containerId, containerType, connectorId, version, state, activationEvent);\n        this.exceptionMessage = exceptionMessage;\n        this.stackTrace = stackTrace;\n    }\n\n    private final String exceptionMessage;\n\n    private final String stackTrace;\n\n    @Override\n    public String getExceptionMessage() {\n        return exceptionMessage;\n    }\n\n    @Override\n    public String getStackTrace() {\n        return stackTrace;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/contract/ContractViolationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.contract;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.exception.BonitaException;\n\n/**\n * Thrown when the {@link ContractDefinition} is not fulfilled.\n *\n * @author Matthieu Chaffotte\n * @since 7.0\n */\npublic class ContractViolationException extends BonitaException {\n\n    private static final long serialVersionUID = -5733414795158022044L;\n\n    private final List<String> explanations;\n    private final String simpleMessage;\n\n    /**\n     * Constructs an <code>ContractViolationException</code> with the specified detail message and the explanations.\n     *\n     * @param message the specified detail message\n     * @param explanations the explanations\n     */\n    public ContractViolationException(final String simpleMessage, final String message, final List<String> explanations,\n            final Throwable e) {\n        super(message, e);\n        this.simpleMessage = simpleMessage;\n        this.explanations = explanations;\n    }\n\n    /**\n     * Returns the explanations of why the contract is not fulfilled.\n     *\n     * @return the explanations\n     */\n    public List<String> getExplanations() {\n        return explanations;\n    }\n\n    public String getSimpleMessage() {\n        return simpleMessage;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/data/ArchivedDataInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.data;\n\nimport org.bonitasoft.engine.bpm.ArchivedElement;\n\n/**\n * Represents an archived {@link DataInstance}.\n *\n * @author Celine Souchet\n * @version 6.4.1\n * @since 6.0.0\n */\npublic interface ArchivedDataInstance extends DataInstance, ArchivedElement {\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/data/ArchivedDataNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.data;\n\nimport org.bonitasoft.engine.exception.NotFoundException;\n\n/**\n * Thrown when it's not possible to find the archived data instance.\n *\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @since 6.0.0\n * @version 6.4.1\n */\npublic class ArchivedDataNotFoundException extends NotFoundException {\n\n    private static final long serialVersionUID = -1668767134016412650L;\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public ArchivedDataNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n    public ArchivedDataNotFoundException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/data/DataInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.data;\n\nimport java.io.Serializable;\n\nimport org.bonitasoft.engine.bpm.BaseElement;\nimport org.bonitasoft.engine.bpm.NamedElement;\n\n/**\n * This object represents this instance of {@link DataDefinition} generated when the associated\n * {@link org.bonitasoft.engine.bpm.process.ProcessInstance} or\n * {@link org.bonitasoft.engine.bpm.flownode.FlowNodeInstance} is instantiated.\n *\n * @author Feng Hui\n * @author Celine Souchet\n * @since 6.0.0\n * @version 6.4.1\n */\npublic interface DataInstance extends NamedElement, BaseElement {\n\n    /**\n     * Get the description of the data defined in {@link DataDefinition}.\n     *\n     * @return The description of the data.\n     * @since 6.0.0\n     * @see DataDefinition#getDescription()\n     */\n    String getDescription();\n\n    /**\n     * Get the class name of the type of the data defined in {@link DataDefinition}.\n     *\n     * @return The class name of the type of the data.\n     * @since 6.0.0\n     * @see DataDefinition#getClassName()\n     */\n    String getClassName();\n\n    /**\n     * Is it transient?\n     *\n     * @return <code>true</code> if the data is transient, <code>false</code> otherwise.\n     * @since 6.0.0\n     * @see DataDefinition#isTransientData()\n     */\n    Boolean isTransientData();\n\n    /**\n     * Get the value of the data.\n     *\n     * @return The value of the data.\n     * @since 6.0.0\n     */\n    Serializable getValue();\n\n    /**\n     * Get the identifier of the element where the data is defined. The element can be a\n     * {@link org.bonitasoft.engine.bpm.process.ProcessInstance} or a\n     * {@link org.bonitasoft.engine.bpm.flownode.FlowNodeInstance}.\n     *\n     * @return The identifier of the container of the data.\n     * @since 6.0.0\n     * @see org.bonitasoft.engine.bpm.process.ProcessInstance#getId()\n     * @see org.bonitasoft.engine.bpm.flownode.FlowNodeInstance#getId()\n     */\n    long getContainerId();\n\n    /**\n     * Get the type of the element where the data is defined.\n     * The list of value for this field is :\n     * <ul>\n     * <li>\n     * <code>PROCESS_INSTANCE</code>\n     * </li>\n     * <li>\n     * <code>ACTIVITY_INSTANCE</code>\n     * </li>\n     * <li>\n     * <code>MESSAGE_INSTANCE</code>\n     * </li>\n     * </ul>\n     *\n     * @return The type of the container of the data.\n     * @since 6.0.0\n     */\n    String getContainerType();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/data/DataNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.data;\n\nimport org.bonitasoft.engine.exception.NotFoundException;\n\n/**\n * Thrown when it's not possible to find the data.\n *\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @since 6.0.0\n * @version 6.4.1\n */\npublic class DataNotFoundException extends NotFoundException {\n\n    private static final long serialVersionUID = 882339840751819686L;\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public DataNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/data/impl/ArchivedDataInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.data.impl;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\nimport org.bonitasoft.engine.bpm.data.ArchivedDataInstance;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class ArchivedDataInstanceImpl extends DataInstanceImpl implements ArchivedDataInstance {\n\n    private static final long serialVersionUID = 2770043188433234604L;\n\n    private Date archiveDate;\n\n    private long sourceObjectId;\n\n    private Serializable value;\n\n    @Override\n    public Date getArchiveDate() {\n        return archiveDate;\n    }\n\n    public void setArchiveDate(final Date archiveDate) {\n        this.archiveDate = archiveDate;\n    }\n\n    @Override\n    public long getSourceObjectId() {\n        return sourceObjectId;\n    }\n\n    public void setSourceObjectId(final long sourceObjectId) {\n        this.sourceObjectId = sourceObjectId;\n    }\n\n    @Override\n    public Serializable getValue() {\n        return value;\n    }\n\n    @Override\n    public void setValue(final Serializable value) {\n        this.value = value;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/data/impl/BlobDataInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.data.impl;\n\nimport java.io.Serializable;\n\nimport org.bonitasoft.engine.bpm.data.DataDefinition;\n\n/**\n * @author Feng Hui\n * @author Matthieu Chaffotte\n */\npublic class BlobDataInstanceImpl extends DataInstanceImpl {\n\n    private static final long serialVersionUID = 4369510522836874048L;\n\n    private Serializable value;\n\n    public BlobDataInstanceImpl() {\n        super();\n    }\n\n    public BlobDataInstanceImpl(final DataDefinition dataDefinition) {\n        super(dataDefinition);\n    }\n\n    public BlobDataInstanceImpl(final DataDefinition dataDefinition, final Serializable value) {\n        super(dataDefinition);\n        this.value = value;\n    }\n\n    @Override\n    public Serializable getValue() {\n        return value;\n    }\n\n    @Override\n    public void setValue(final Serializable value) {\n        this.value = value;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/data/impl/BooleanDataInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.data.impl;\n\nimport java.io.Serializable;\n\nimport org.bonitasoft.engine.bpm.data.DataDefinition;\n\n/**\n * @author Feng Hui\n * @author Matthieu Chaffotte\n */\npublic class BooleanDataInstanceImpl extends DataInstanceImpl {\n\n    private static final long serialVersionUID = 4369510522836874048L;\n\n    private Boolean value;\n\n    public BooleanDataInstanceImpl() {\n        super();\n    }\n\n    public BooleanDataInstanceImpl(final DataDefinition dataDefinition) {\n        super(dataDefinition);\n    }\n\n    public BooleanDataInstanceImpl(final DataDefinition dataDefinition, final Boolean value) {\n        super(dataDefinition);\n        this.value = value;\n    }\n\n    @Override\n    public void setValue(final Serializable value) {\n        this.value = (Boolean) value;\n    }\n\n    @Override\n    public Boolean getValue() {\n        return value;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/data/impl/DataInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.data.impl;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\nimport lombok.Setter;\nimport org.bonitasoft.engine.bpm.data.DataDefinition;\nimport org.bonitasoft.engine.bpm.data.DataInstance;\n\n/**\n * @author Feng Hui\n * @author Matthieu Chaffotte\n */\n@Setter\npublic abstract class DataInstanceImpl implements DataInstance {\n\n    @Serial\n    private static final long serialVersionUID = -3752347909196691889L;\n\n    private long id;\n\n    private String name;\n\n    private String description;\n\n    private boolean transientData;\n\n    private String className;\n\n    private long containerId;\n\n    private String containerType;\n\n    public DataInstanceImpl() {\n        super();\n    }\n\n    public DataInstanceImpl(final DataDefinition dataDefinition) {\n        name = dataDefinition.getName();\n        description = dataDefinition.getDescription();\n        transientData = dataDefinition.isTransientData();\n        className = dataDefinition.getClassName();\n    }\n\n    public void setDataTypeClassName(final String className) {\n        this.className = className;\n    }\n\n    @Override\n    public long getId() {\n        return id;\n    }\n\n    @Override\n    public String getName() {\n        return name;\n    }\n\n    @Override\n    public String getDescription() {\n        return description;\n    }\n\n    @Override\n    public abstract Serializable getValue();\n\n    public abstract void setValue(Serializable value);\n\n    @Override\n    public Boolean isTransientData() {\n        return transientData;\n    }\n\n    @Override\n    public String getClassName() {\n        return className;\n    }\n\n    @Override\n    public long getContainerId() {\n        return containerId;\n    }\n\n    @Override\n    public String getContainerType() {\n        return containerType;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/data/impl/DateDataInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.data.impl;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\nimport org.bonitasoft.engine.bpm.data.DataDefinition;\n\n/**\n * @author Feng Hui\n * @author Matthieu Chaffotte\n */\npublic class DateDataInstanceImpl extends DataInstanceImpl {\n\n    private static final long serialVersionUID = 4369510522836874048L;\n\n    private Date value;\n\n    public DateDataInstanceImpl() {\n        super();\n    }\n\n    public DateDataInstanceImpl(final DataDefinition dataDefinition) {\n        super(dataDefinition);\n    }\n\n    public DateDataInstanceImpl(final DataDefinition dataDefinition, final Date value) {\n        super(dataDefinition);\n        this.value = value;\n    }\n\n    @Override\n    public Date getValue() {\n        return value;\n    }\n\n    @Override\n    public void setValue(final Serializable value) {\n        this.value = (Date) value;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/data/impl/DoubleDataInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.data.impl;\n\nimport java.io.Serializable;\n\nimport org.bonitasoft.engine.bpm.data.DataDefinition;\n\n/**\n * @author Feng Hui\n * @author Matthieu Chaffotte\n */\npublic class DoubleDataInstanceImpl extends DataInstanceImpl {\n\n    private static final long serialVersionUID = 4369510522836874048L;\n\n    private Double value;\n\n    public DoubleDataInstanceImpl() {\n        super();\n    }\n\n    public DoubleDataInstanceImpl(final DataDefinition dataDefinition) {\n        super(dataDefinition);\n    }\n\n    public DoubleDataInstanceImpl(final DataDefinition dataDefinition, final Double value) {\n        super(dataDefinition);\n        this.value = value;\n    }\n\n    @Override\n    public void setValue(final Serializable value) {\n        this.value = (Double) value;\n    }\n\n    @Override\n    public Double getValue() {\n        return value;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/data/impl/FloatDataInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.data.impl;\n\nimport java.io.Serializable;\n\nimport org.bonitasoft.engine.bpm.data.DataDefinition;\n\n/**\n * @author Celine Souchet\n * @author Matthieu Chaffotte\n */\npublic class FloatDataInstanceImpl extends DataInstanceImpl {\n\n    private static final long serialVersionUID = 4369510522836874048L;\n\n    private Float value;\n\n    public FloatDataInstanceImpl() {\n        super();\n    }\n\n    public FloatDataInstanceImpl(final DataDefinition dataDefinition) {\n        super(dataDefinition);\n    }\n\n    public FloatDataInstanceImpl(final DataDefinition dataDefinition, final Float value) {\n        super(dataDefinition);\n        this.value = value;\n    }\n\n    @Override\n    public void setValue(final Serializable value) {\n        this.value = (Float) value;\n    }\n\n    @Override\n    public Float getValue() {\n        return value;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/data/impl/IntegerDataInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.data.impl;\n\nimport java.io.Serializable;\n\nimport org.bonitasoft.engine.bpm.data.DataDefinition;\n\n/**\n * @author Feng Hui\n * @author Matthieu Chaffotte\n */\npublic class IntegerDataInstanceImpl extends DataInstanceImpl {\n\n    private static final long serialVersionUID = 4369510522836874048L;\n\n    private Integer value;\n\n    public IntegerDataInstanceImpl() {\n        super();\n    }\n\n    public IntegerDataInstanceImpl(final DataDefinition dataDefinition) {\n        super(dataDefinition);\n    }\n\n    public IntegerDataInstanceImpl(final DataDefinition dataDefinition, final Integer value) {\n        super(dataDefinition);\n        this.value = value;\n    }\n\n    @Override\n    public Integer getValue() {\n        return value;\n    }\n\n    @Override\n    public void setValue(final Serializable value) {\n        this.value = (Integer) value;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/data/impl/LongDataInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.data.impl;\n\nimport java.io.Serializable;\n\nimport org.bonitasoft.engine.bpm.data.DataDefinition;\n\n/**\n * @author Feng Hui\n * @author Matthieu Chaffotte\n */\npublic class LongDataInstanceImpl extends DataInstanceImpl {\n\n    private static final long serialVersionUID = 4369510522836874048L;\n\n    private Long value;\n\n    public LongDataInstanceImpl() {\n        super();\n    }\n\n    public LongDataInstanceImpl(final DataDefinition dataDefinition) {\n        super(dataDefinition);\n    }\n\n    public LongDataInstanceImpl(final DataDefinition dataDefinition, final Long value) {\n        super(dataDefinition);\n        this.value = value;\n    }\n\n    @Override\n    public Long getValue() {\n        return value;\n    }\n\n    @Override\n    public void setValue(final Serializable value) {\n        this.value = (Long) value;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/data/impl/LongTextDataInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.data.impl;\n\nimport java.io.Serializable;\n\nimport org.bonitasoft.engine.bpm.data.DataDefinition;\n\n/**\n * @author Feng Hui\n * @author Matthieu Chaffotte\n */\npublic class LongTextDataInstanceImpl extends ShortTextDataInstanceImpl {\n\n    private static final long serialVersionUID = -3072085875249177140L;\n\n    public LongTextDataInstanceImpl() {\n        super();\n    }\n\n    public LongTextDataInstanceImpl(final DataDefinition dataDefinition) {\n        super(dataDefinition);\n    }\n\n    public LongTextDataInstanceImpl(final DataDefinition dataDefinition, final Serializable value) {\n        super(dataDefinition, (String) value);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/data/impl/ShortTextDataInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.data.impl;\n\nimport java.io.Serializable;\n\nimport org.bonitasoft.engine.bpm.data.DataDefinition;\n\n/**\n * @author Feng Hui\n * @author Matthieu Chaffotte\n */\npublic class ShortTextDataInstanceImpl extends DataInstanceImpl {\n\n    private static final long serialVersionUID = 919336967044796183L;\n\n    private String value;\n\n    public ShortTextDataInstanceImpl() {\n        super();\n    }\n\n    public ShortTextDataInstanceImpl(final DataDefinition dataDefinition) {\n        super(dataDefinition);\n    }\n\n    public ShortTextDataInstanceImpl(final DataDefinition dataDefinition, final String value) {\n        super(dataDefinition);\n        this.value = value;\n    }\n\n    @Override\n    public Serializable getValue() {\n        return value;\n    }\n\n    @Override\n    public void setValue(final Serializable value) {\n        this.value = (String) value;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/document/ArchivedDocument.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.document;\n\nimport org.bonitasoft.engine.bpm.ArchivedElement;\n\n/**\n * @author Zhang Bole\n * @author Matthieu Chaffotte\n */\npublic interface ArchivedDocument extends ArchivedElement, Document {\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/document/ArchivedDocumentNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.document;\n\nimport org.bonitasoft.engine.exception.NotFoundException;\n\n/**\n * Thrown when it's not possible to find the archived document.\n * The class ArchivedDocumentNotFoundException is a form of Throwable that indicates conditions that a reasonable\n * application might want to catch.\n * The class ArchivedDocumentNotFoundException that is not also subclasses of {@link RuntimeException} are checked\n * exceptions.\n * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by\n * the execution of the method or\n * constructor and propagate outside the method or constructor boundary.\n *\n * @author Zhang Bole\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class ArchivedDocumentNotFoundException extends NotFoundException {\n\n    private static final long serialVersionUID = 2913387213088474673L;\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public ArchivedDocumentNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/document/ArchivedDocumentsSearchDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.document;\n\n/**\n * @author Zhang Bole\n */\npublic final class ArchivedDocumentsSearchDescriptor {\n\n    public static final String PROCESSINSTANCE_ID = \"processinstanceid\";\n\n    public static final String SOURCEOBJECT_ID = \"sourceObjectId\";\n\n    public static final String ARCHIVE_DATE = \"archiveDate\";\n\n    public static final String DOCUMENT_NAME = \"documentName\";\n\n    public static final String DOCUMENT_DESCRIPTION = \"documentDescription\";\n\n    public static final String DOCUMENT_VERSION = \"version\";\n\n    public static final String DOCUMENT_AUTHOR = \"documentAuthor\";\n\n    public static final String DOCUMENT_CREATIONDATE = \"documentCreationDate\";\n\n    public static final String DOCUMENT_HAS_CONTENT = \"documentHasContent\";\n\n    public static final String DOCUMENT_CONTENT_FILENAME = \"documentContentFileName\";\n\n    public static final String DOCUMENT_CONTENT_MIMETYPE = \"documentContentMimeType\";\n\n    public static final String CONTENT_STORAGE_ID = \"contentStorageId\";\n\n    public static final String DOCUMENT_URL = \"documentURL\";\n\n    public static final String LIST_INDEX = \"index\";\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/document/DocumentAttachmentException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.document;\n\nimport org.bonitasoft.engine.exception.ExecutionException;\n\n/**\n * Thrown when it's not possible to attach a document to a process.\n * The class DocumentAttachmentException is a form of Throwable that indicates conditions that a reasonable application\n * might want to catch.\n * The class DocumentAttachmentException that is not also subclasses of {@link RuntimeException} are checked exceptions.\n * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by\n * the execution of the method or\n * constructor and propagate outside the method or constructor boundary.\n *\n * @author Nicolas Chabanoles\n * @author Celine Souchet\n */\npublic class DocumentAttachmentException extends ExecutionException {\n\n    private static final long serialVersionUID = -3376881143165731702L;\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public DocumentAttachmentException(final Throwable cause) {\n        super(cause);\n    }\n\n    public DocumentAttachmentException(String s) {\n        super(s);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/document/DocumentCriterion.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.document;\n\n/**\n * @author Baptiste Mesta\n */\npublic enum DocumentCriterion {\n\n    NAME_ASC, NAME_DESC,\n\n    AUTHOR_ASC, AUTHOR_DESC,\n\n    CREATION_DATE_ASC, CREATION_DATE_DESC,\n\n    FILENAME_ASC, FILENAME_DESC,\n\n    MIMETYPE_ASC, MIMETYPE_DESC,\n\n    URL_ASC, URL_DESC,\n\n    DEFAULT;\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/document/DocumentException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.document;\n\nimport org.bonitasoft.engine.exception.BonitaException;\n\n/**\n * Thrown when there is an error on the document.\n * The class DocumentException is a form of Throwable that indicates conditions that a reasonable application might want\n * to catch.\n * The class DocumentException that is not also subclasses of {@link RuntimeException} are checked exceptions.\n * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by\n * the execution of the method or\n * constructor and propagate outside the method or constructor boundary.\n *\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n */\npublic class DocumentException extends BonitaException {\n\n    private static final long serialVersionUID = 146457960113640054L;\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public DocumentException(final Throwable cause) {\n        super(cause);\n    }\n\n    /**\n     * Constructs a new exception with the specified detail message.\n     *\n     * @param message\n     *        The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method).\n     */\n    public DocumentException(final String message) {\n        super(message);\n    }\n\n    /**\n     * Constructs a new exception with the specified detail message and cause.\n     *\n     * @param message\n     *        The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method).\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public DocumentException(final String message, final Exception cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/document/DocumentNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.document;\n\nimport org.bonitasoft.engine.exception.NotFoundException;\n\n/**\n * Thrown when it's not possible to find the document.\n * The class DocumentNotFoundException is a form of Throwable that indicates conditions that a reasonable application\n * might want to catch.\n * The class DocumentNotFoundException that is not also subclasses of {@link RuntimeException} are checked exceptions.\n * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by\n * the execution of the method or\n * constructor and propagate outside the method or constructor boundary.\n *\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n */\npublic class DocumentNotFoundException extends NotFoundException {\n\n    private static final long serialVersionUID = -1501286386852952410L;\n\n    /**\n     * Constructs a new exception with the specified detail message.\n     *\n     * @param message\n     *        The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method).\n     */\n    public DocumentNotFoundException(String message) {\n        super(message);\n    }\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public DocumentNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n    /**\n     * Constructs a new exception with the specified detail message and cause.\n     *\n     * @param message\n     *        The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method).\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public DocumentNotFoundException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/document/DocumentValue.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.document;\n\nimport java.io.Serializable;\nimport java.util.Objects;\n\n/**\n * Class that holds a content + mime type and a name OR the url if it's an external document.\n * It is used by DOCUMENT_CREATE_UPDATE operations to create or update a document stored in bonita\n *\n * @author Baptiste Mesta\n */\npublic class DocumentValue implements Serializable {\n\n    private static final long serialVersionUID = -637083535741620642L;\n\n    private byte[] content;\n\n    private String mimeType;\n\n    private String fileName;\n\n    private String url;\n\n    private boolean hasContent;\n\n    private Long documentId;\n\n    private boolean hasChanged;\n\n    private int index = -1;\n\n    /**\n     * Represent the value of a document. Content, mime type and file name are given\n     *\n     * @param content\n     *        content of the document\n     * @param mimeType\n     *        mime type of the document\n     * @param fileName\n     *        file name of the document\n     * @throws IllegalArgumentException\n     *         if the content or the fileName is null or empty\n     */\n    public DocumentValue(final byte[] content, final String mimeType, final String fileName) {\n        super();\n\n        this.content = content;\n        this.mimeType = mimeType;\n        this.fileName = fileName;\n        hasContent = true;\n        hasChanged = false;\n        documentId = null;\n\n        checkIfTheFileNameIsFilledForADocumentWithContent();\n    }\n\n    /**\n     * Represent the value of an external document, only the url is given\n     *\n     * @param url\n     *        url of the document\n     */\n    public DocumentValue(final String url) {\n        super();\n        this.url = url;\n        hasContent = false;\n        hasChanged = false;\n        documentId = null;\n    }\n\n    /**\n     * Represent an existing document that did not changed.\n     * It is used only in operations to update document list.\n     *\n     * @param documentId\n     *        the id of the existing document (mapping)\n     */\n    public DocumentValue(final long documentId) {\n        super();\n        hasChanged = false;\n        this.documentId = documentId;\n    }\n\n    /**\n     * Represent an existing document that changed with the content and metadata in parameters.\n     * It is used only in operations to update document list.\n     *\n     * @param documentId\n     *        the id of the existing document (mapping)\n     * @param content\n     *        content of the document\n     * @param mimeType\n     *        mime type of the document\n     * @param fileName\n     *        file name of the document\n     * @throws IllegalArgumentException\n     *         if the content or the fileName is null or empty\n     */\n    public DocumentValue(final long documentId, final byte[] content, final String mimeType, final String fileName) {\n        super();\n        hasContent = true;\n        this.content = content;\n        this.mimeType = mimeType;\n        this.fileName = fileName;\n        hasChanged = true;\n        this.documentId = documentId;\n        checkIfTheFileNameIsFilledForADocumentWithContent();\n    }\n\n    /**\n     * Represent an existing document that changed to an external document.\n     * It is used only in operations to update document list.\n     *\n     * @param documentId\n     *        the id of the existing document (mapping)\n     */\n    public DocumentValue(final long documentId, final String url) {\n        super();\n        this.url = url;\n        hasContent = false;\n        hasChanged = true;\n        this.documentId = documentId;\n    }\n\n    public byte[] getContent() {\n        return content;\n    }\n\n    public String getMimeType() {\n        return mimeType;\n    }\n\n    public String getFileName() {\n        return fileName;\n    }\n\n    public String getUrl() {\n        return url;\n    }\n\n    /**\n     * @return true if the document to create is stored internally or externally with an URL\n     */\n    public boolean hasContent() {\n        return hasContent;\n    }\n\n    public void setContent(final byte[] content) {\n        hasContent = true;\n        this.content = content;\n        checkIfTheFileNameIsFilledForADocumentWithContent();\n    }\n\n    private void checkIfTheFileNameIsFilledForADocumentWithContent() {\n        if (hasContent && (fileName == null || fileName.isEmpty())) {\n            throw new IllegalArgumentException(\"The fileName field must be filled when the content is filled !!\");\n        }\n    }\n\n    public void setMimeType(final String mimeType) {\n        this.mimeType = mimeType;\n    }\n\n    public void setFileName(final String fileName) {\n        this.fileName = fileName;\n        checkIfTheFileNameIsFilledForADocumentWithContent();\n    }\n\n    public void setUrl(final String url) {\n        this.url = url;\n        hasContent = false;\n    }\n\n    public void setHasContent(final boolean hasContent) {\n        this.hasContent = hasContent;\n    }\n\n    /**\n     * Indicate which document will be updated <br>\n     * Used only when updating list of document\n     *\n     * @return the id of the document to update\n     */\n    public Long getDocumentId() {\n        return documentId;\n    }\n\n    public void setDocumentId(final Long documentId) {\n        this.documentId = documentId;\n    }\n\n    /**\n     * If the document value updates an existing document, this getter tels us if the content is modified and should be\n     * updated\n     *\n     * @return true if the content of the original document has changed\n     */\n    public boolean hasChanged() {\n        return hasChanged;\n    }\n\n    public void setHasChanged(final boolean hasChanged) {\n        this.hasChanged = hasChanged;\n    }\n\n    public int getIndex() {\n        return index;\n    }\n\n    /**\n     * Index of were to put the document inside the list. Only used when using API method attach document.\n     *\n     * @param index\n     *        index in the list\n     */\n    public DocumentValue setIndex(final int index) {\n        this.index = index;\n        return this;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (o == null || getClass() != o.getClass())\n            return false;\n        DocumentValue that = (DocumentValue) o;\n        return Objects.equals(hasContent, that.hasContent) &&\n                Objects.equals(hasChanged, that.hasChanged) &&\n                Objects.equals(index, that.index) &&\n                Objects.equals(content, that.content) &&\n                Objects.equals(mimeType, that.mimeType) &&\n                Objects.equals(fileName, that.fileName) &&\n                Objects.equals(url, that.url) &&\n                Objects.equals(documentId, that.documentId);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(content, mimeType, fileName, url, hasContent, documentId, hasChanged, index);\n    }\n\n    @Override\n    public String toString() {\n        return \"DocumentValue{\" +\n                \"mimeType='\" + mimeType + '\\'' +\n                \", fileName='\" + fileName + '\\'' +\n                \", url='\" + url + '\\'' +\n                \", hasContent=\" + hasContent +\n                \", documentId=\" + documentId +\n                \", hasChanged=\" + hasChanged +\n                \", index=\" + index +\n                '}';\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/document/DocumentsSearchDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.document;\n\n/**\n * @author Zhang Bole\n */\npublic final class DocumentsSearchDescriptor {\n\n    public static final String PROCESSINSTANCE_ID = \"processinstanceid\";\n\n    public static final String DOCUMENT_NAME = \"documentName\";\n\n    public static final String DOCUMENT_VERSION = \"version\";\n\n    public static final String DOCUMENT_DESCRIPTION = \"documentDescription\";\n\n    public static final String DOCUMENT_AUTHOR = \"documentAuthor\";\n\n    public static final String DOCUMENT_CREATIONDATE = \"documentCreationDate\";\n\n    public static final String DOCUMENT_HAS_CONTENT = \"documentHasContent\";\n\n    public static final String DOCUMENT_CONTENT_FILENAME = \"documentContentFileName\";\n\n    public static final String DOCUMENT_CONTENT_MIMETYPE = \"documentContentMimeType\";\n\n    public static final String CONTENT_STORAGE_ID = \"contentStorageId\";\n\n    public static final String DOCUMENT_URL = \"documentURL\";\n\n    public static final String LIST_INDEX = \"index\";\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/document/impl/ArchivedDocumentImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.document.impl;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.bpm.document.ArchivedDocument;\n\n/**\n * @author Zhang Bole\n * @author Baptiste Mesta\n */\npublic class ArchivedDocumentImpl extends DocumentImpl implements ArchivedDocument {\n\n    private static final long serialVersionUID = -6573747806944970704L;\n    private Date archiveDate;\n    private long sourceObjectId;\n\n    public ArchivedDocumentImpl() {\n        super();\n    }\n\n    public ArchivedDocumentImpl(final String name) {\n        super();\n        setName(name);\n    }\n\n    @Override\n    public Date getArchiveDate() {\n        return archiveDate;\n    }\n\n    @Override\n    public long getSourceObjectId() {\n        return sourceObjectId;\n    }\n\n    public void setArchiveDate(final Date archiveDate) {\n        this.archiveDate = archiveDate;\n    }\n\n    public void setSourceObjectId(final long sourceObjectId) {\n        this.sourceObjectId = sourceObjectId;\n    }\n\n    @Override\n    public boolean equals(final Object o) {\n        if (this == o) {\n            return true;\n        }\n        if (o == null || getClass() != o.getClass()) {\n            return false;\n        }\n        if (!super.equals(o)) {\n            return false;\n        }\n\n        final ArchivedDocumentImpl that = (ArchivedDocumentImpl) o;\n\n        if (sourceObjectId != that.sourceObjectId) {\n            return false;\n        }\n        if (archiveDate != null ? !archiveDate.equals(that.archiveDate) : that.archiveDate != null) {\n            return false;\n        }\n\n        return true;\n    }\n\n    @Override\n    public int hashCode() {\n        int result = super.hashCode();\n        result = 31 * result + (archiveDate != null ? archiveDate.hashCode() : 0);\n        result = 31 * result + (int) (sourceObjectId ^ sourceObjectId >>> 32);\n        return result;\n    }\n\n    @Override\n    public String toString() {\n        return \"ArchivedDocumentImpl{\" +\n                \"archiveDate=\" + archiveDate +\n                \", sourceObjectId=\" + sourceObjectId +\n                super.toString() + \"} \";\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ActivityDefinitionNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\nimport org.bonitasoft.engine.exception.NotFoundException;\n\n/**\n * Thrown when it's not possible to find the activity definition.\n * The class ActivityDefinitionNotFoundException is a form of Throwable that indicates conditions that a reasonable\n * application might want to catch.\n * The class ActivityDefinitionNotFoundException that is not also subclasses of {@link RuntimeException} are checked\n * exceptions.\n * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by\n * the execution of the method or\n * constructor and propagate outside the method or constructor boundary.\n *\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class ActivityDefinitionNotFoundException extends NotFoundException {\n\n    private static final long serialVersionUID = -5346930807820225783L;\n\n    /**\n     * Constructs a new exception with the specified detail message.\n     *\n     * @param activityName\n     *        The name of the searched activity definition\n     */\n    public ActivityDefinitionNotFoundException(final String activityName) {\n        super(\"Activity '\" + activityName + \"' not found\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ActivityExecutionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * Thrown when it's not possible to execute the activity.\n * The class ActivityExecutionException is a form of Throwable that indicates conditions that a reasonable application\n * might want to catch.\n * The class ActivityExecutionException that is not also subclasses of {@link RuntimeException} are checked exceptions.\n * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by\n * the execution of the method or\n * constructor and propagate outside the method or constructor boundary.\n *\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class ActivityExecutionException extends FlowNodeExecutionException {\n\n    private static final long serialVersionUID = -1112307466303034453L;\n\n    /**\n     * Constructs a new exception with the specified detail message.\n     *\n     * @param message\n     *        The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method).\n     */\n    public ActivityExecutionException(final String message) {\n        super(message);\n    }\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public ActivityExecutionException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ActivityInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * Corresponding to a instance of {@link ActivityDefinition}.\n *\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic interface ActivityInstance extends FlowNodeInstance {\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ActivityInstanceCriterion.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic enum ActivityInstanceCriterion {\n\n    /**\n     * Last update ascending order\n     */\n    LAST_UPDATE_ASC,\n\n    /**\n     * Started date ascending order\n     */\n    REACHED_STATE_DATE_ASC,\n\n    /**\n     * Expected End Date ascending order\n     */\n    EXPECTED_END_DATE_ASC,\n\n    /**\n     * Name ascending order\n     */\n    NAME_ASC,\n\n    /**\n     * Priority ascending order\n     */\n    PRIORITY_ASC,\n\n    /**\n     * Last update descending order\n     */\n    LAST_UPDATE_DESC,\n\n    /**\n     * Started date descending order\n     */\n    REACHED_STATE_DATE_DESC,\n\n    /**\n     * Expected End Date descending order\n     */\n    EXPECTED_END_DATE_DESC,\n\n    /**\n     * Name descending order\n     */\n    NAME_DESC,\n\n    /**\n     * Priority descending order\n     */\n    PRIORITY_DESC,\n\n    /**\n     *\n     */\n    DEFAULT\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ActivityInstanceNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\nimport java.io.Serial;\n\nimport org.bonitasoft.engine.exception.NotFoundException;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class ActivityInstanceNotFoundException extends NotFoundException {\n\n    @Serial\n    private static final long serialVersionUID = -5980531959067888526L;\n\n    /**\n     * Constructs a new exception with the specified detail message.\n     *\n     * @param activityInstanceId\n     *        The identifier of the searched activity displayed in the detail message (which is saved for later\n     *        retrieval by the\n     *        {@link Throwable#getMessage()} method).\n     */\n    public ActivityInstanceNotFoundException(final long activityInstanceId) {\n        super(\"activity with id \" + activityInstanceId + \" not found\");\n    }\n\n    /**\n     * Constructs a new exception with the specified detail message and cause.\n     *\n     * @param activityInstanceId\n     *        The identifier of the searched activity displayed in the detail message (which is saved for later\n     *        retrieval by the\n     *        {@link Throwable#getMessage()} method).\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public ActivityInstanceNotFoundException(final long activityInstanceId, final Exception cause) {\n        super(\"activity with id \" + activityInstanceId + \" not found\", cause);\n    }\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public ActivityInstanceNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ActivityInstanceSearchDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Yanyan Liu\n */\npublic class ActivityInstanceSearchDescriptor {\n\n    public static final String NAME = \"name\";\n\n    public static final String STATE_NAME = \"state\";\n\n    public static final String SUPERVISOR_ID = \"supervisorId\";\n\n    public static final String USER_ID = \"userId\";\n\n    public static final String GROUP_ID = \"groupId\";\n\n    public static final String ROLE_ID = \"roleId\";\n\n    public static final String PROCESS_DEFINITION_ID = \"processDefinitionId\";\n\n    public static final String PROCESS_INSTANCE_ID = \"processInstanceId\";\n\n    public static final String PARENT_PROCESS_INSTANCE_ID = \"parentProcessInstanceId\";\n\n    public static final String PARENT_ACTIVITY_INSTANCE_ID = \"parentActivityInstanceId\";\n\n    public static final String PARENT_CONTAINER_ID = \"parentContainerId\";\n\n    public static final String DISPLAY_NAME = \"displayName\";\n\n    /**\n     * Use this field to match a specific type of flow node. <br>\n     * Activity type filter can only be used once per query. <br>\n     * Example of invalid query:<br>\n     * <ul>\n     * <li>\n     * .leftParenthesis()\n     * .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.USER_TASK)\n     * .or()\n     * .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.HUMAN_TASK)\n     * .rightParenthesis()\n     * .done();\n     * </li>\n     * </ul>\n     */\n    public static final String ACTIVITY_TYPE = \"activityType\";\n\n    public static final String LAST_MODIFICATION_DATE = \"lastUpdateDate\";\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ActivityStates.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\n/**\n * Temporary class to be substituted by ActivityStateService.\n *\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n */\npublic class ActivityStates {\n\n    /**\n     * The state when a activity failed.\n     */\n    public static final String FAILED_STATE = \"failed\";\n\n    /**\n     * The state when a activity is initializing.\n     */\n    public static final String INITIALIZING_STATE = \"initializing\";\n\n    /**\n     * The state when a activity is ready.\n     */\n    public static final String READY_STATE = \"ready\";\n\n    /**\n     * The state when a activity is executing.\n     */\n    public static final String EXECUTING_STATE = \"executing\";\n\n    /**\n     * The state when a activity is completing.\n     */\n    public static final String COMPLETING_STATE = \"completing\";\n\n    /**\n     * The state when a activity is completed.\n     */\n    public static final String COMPLETED_STATE = \"completed\";\n\n    /**\n     * The state when a activity is waiting.\n     */\n    public static final String WAITING_STATE = \"waiting\";\n\n    /**\n     * The state when a activity is skipped.\n     */\n    public static final String SKIPPED_STATE = \"skipped\";\n\n    /**\n     * The state when a activity is cancelled.\n     */\n    public static final String CANCELLED_STATE = \"cancelled\";\n\n    /**\n     * The state when a activity is aborted.\n     */\n    public static final String ABORTED_STATE = \"aborted\";\n\n    /**\n     * The state when the subtask of a activity are cancelling.\n     *\n     * @deprecated this should not be used anymore client-side.\n     */\n    @Deprecated(since = \"7.12.0\")\n    public static final String CANCELLING_SUBTASKS_STATE = \"cancelling subtasks\";\n\n    /**\n     * The set of the states\n     */\n    public static final Set<String> STATES = new HashSet<>();\n\n    static {\n        STATES.add(FAILED_STATE);\n        STATES.add(INITIALIZING_STATE);\n        STATES.add(READY_STATE);\n        STATES.add(EXECUTING_STATE);\n        STATES.add(COMPLETING_STATE);\n        STATES.add(COMPLETED_STATE);\n        STATES.add(WAITING_STATE);\n        STATES.add(SKIPPED_STATE);\n        STATES.add(CANCELLED_STATE);\n        STATES.add(CANCELLING_SUBTASKS_STATE);\n        STATES.add(ABORTED_STATE);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedActivityInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffote\n */\npublic interface ArchivedActivityInstance extends ArchivedFlowNodeInstance {\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedActivityInstanceNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\nimport org.bonitasoft.engine.exception.NotFoundException;\n\n/**\n * Thrown when it's not possible to find the archived activity instance.\n * The class ArchivedActivityInstanceNotFoundException is a form of Throwable that indicates conditions that a\n * reasonable application might want to catch.\n * The class ArchivedActivityInstanceNotFoundException that is not also subclasses of {@link RuntimeException} are\n * checked exceptions.\n * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by\n * the execution of the method or\n * constructor and propagate outside the method or constructor boundary.\n *\n * @author Zhao Na\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class ArchivedActivityInstanceNotFoundException extends NotFoundException {\n\n    private static final long serialVersionUID = -2775691872771374110L;\n\n    /**\n     * Constructs a new exception with the specified detail message.\n     *\n     * @param activityInstanceId\n     *        The identifier of the archived activity instance include on the detail message (which is saved for later\n     *        retrieval by the\n     *        {@link Throwable#getMessage()} method).\n     */\n    public ArchivedActivityInstanceNotFoundException(final long activityInstanceId) {\n        super(\"Archive activity with id \" + activityInstanceId + \" not found\");\n    }\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public ArchivedActivityInstanceNotFoundException(final Exception cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedActivityInstanceSearchDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Yanyan Liu\n * @author Elias Ricken de Medeiros\n */\npublic final class ArchivedActivityInstanceSearchDescriptor {\n\n    public static final String NAME = \"name\";\n\n    public static final String PRIORITY = \"priority\";\n\n    public static final String STATE_NAME = \"state\";\n\n    public static final String SUPERVISOR_ID = \"supervisorId\";\n\n    public static final String ASSIGNEE_ID = \"assigneeId\";\n\n    public static final String PROCESS_DEFINITION_ID = \"processDefinitionId\";\n\n    public static final String ROOT_PROCESS_INSTANCE_ID = \"rootProcessInstanceId\";\n\n    public static final String PARENT_PROCESS_INSTANCE_ID = \"parentProcessInstanceId\";\n\n    public static final String PARENT_ACTIVITY_INSTANCE_ID = \"parentActivityInstanceId\";\n\n    public static final String DISPLAY_NAME = \"displayName\";\n\n    public static final String ACTIVITY_TYPE = \"activityType\";\n\n    public static final String REACHED_STATE_DATE = \"reachedStateDate\";\n\n    public static final String SOURCE_OBJECT_ID = \"sourceObjectId\";\n\n    public static final String ARCHIVE_DATE = \"archiveDate\";\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedAutomaticTaskInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface ArchivedAutomaticTaskInstance extends ArchivedActivityInstance {\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedCallActivityInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface ArchivedCallActivityInstance extends ArchivedActivityInstance {\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedFlowElementInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\nimport org.bonitasoft.engine.bpm.ArchivedElement;\nimport org.bonitasoft.engine.bpm.BaseElement;\nimport org.bonitasoft.engine.bpm.DescriptionElement;\n\n/**\n * @author Emmanuel Duchastenier\n * @author Matthieu Chaffotte\n */\npublic interface ArchivedFlowElementInstance extends DescriptionElement, BaseElement, ArchivedElement {\n\n    long getRootContainerId();\n\n    long getParentContainerId();\n\n    /**\n     * Is this flow element instance currently aborting?\n     *\n     * @return true if this flow element instance is currently aborting, false otherwise\n     * @since 6.0\n     */\n    boolean isAborting();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedFlowNodeInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.bpm.ArchivedElement;\nimport org.bonitasoft.engine.bpm.BaseElement;\nimport org.bonitasoft.engine.bpm.NamedElement;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic interface ArchivedFlowNodeInstance extends NamedElement, BaseElement, ArchivedElement {\n\n    long getParentContainerId();\n\n    long getRootContainerId();\n\n    long getProcessDefinitionId();\n\n    long getProcessInstanceId();\n\n    /**\n     * @return the parent activity instance id\n     * @since 6.2\n     */\n    long getParentActivityInstanceId();\n\n    String getState();\n\n    FlowNodeType getType();\n\n    String getDisplayName();\n\n    String getDisplayDescription();\n\n    /**\n     * @return The identifier of the user who executed the flow node\n     * @since 6.0.1\n     */\n    long getExecutedBy();\n\n    /**\n     * @return The identifier of the substitute user (as Process manager or Administrator) who executed the flow node.\n     * @since 6.3.0\n     */\n    long getExecutedBySubstitute();\n\n    String getDescription();\n\n    long getFlownodeDefinitionId();\n\n    /**\n     * @return true if this flow element is in a terminal state (= is finished)\n     */\n    boolean isTerminal();\n\n    /**\n     * @return the Date when this Archived flownode reached this state.\n     */\n    Date getReachedStateDate();\n\n    /**\n     * @return the Date when this Archived flownode was last updated.\n     */\n    Date getLastUpdateDate();\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedFlowNodeInstanceNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\nimport org.bonitasoft.engine.exception.NotFoundException;\n\n/**\n * Thrown when it's not possible to find the archived flow node instance.\n * The class ArchivedFlowNodeInstanceNotFoundException is a form of Throwable that indicates conditions that a\n * reasonable application might want to catch.\n * The class ArchivedFlowNodeInstanceNotFoundException that is not also subclasses of {@link RuntimeException} are\n * checked exceptions.\n * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by\n * the execution of the method or\n * constructor and propagate outside the method or constructor boundary.\n *\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n */\npublic class ArchivedFlowNodeInstanceNotFoundException extends NotFoundException {\n\n    private static final long serialVersionUID = -5164644976553290738L;\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public ArchivedFlowNodeInstanceNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n    /**\n     * Constructs a new exception with the specified detail message.\n     *\n     * @param id\n     *        The identifier of the archived flow node include in the detail message (which is saved for later retrieval\n     *        by the\n     *        {@link Throwable#getMessage()} method).\n     */\n    public ArchivedFlowNodeInstanceNotFoundException(final long id) {\n        super(\"Archived flow node with id \" + id + \" not found\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedFlowNodeInstanceSearchDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Emmanuel Duchastenier\n * @author Elias Ricken de Medeiros\n */\npublic class ArchivedFlowNodeInstanceSearchDescriptor {\n\n    public static final String NAME = \"name\";\n\n    public static final String STATE_NAME = \"state\";\n\n    public static final String PROCESS_DEFINITION_ID = \"processDefinitionId\";\n\n    public static final String PARENT_PROCESS_INSTANCE_ID = \"parentProcessInstanceId\";\n\n    public static final String PARENT_ACTIVITY_INSTANCE_ID = \"parentActivityInstanceId\";\n\n    public static final String ROOT_PROCESS_INSTANCE_ID = \"rootProcessInsanceId\";\n\n    public static final String DISPLAY_NAME = \"displayName\";\n\n    public static final String FLOW_NODE_TYPE = \"kind\";\n\n    public static final String ORIGINAL_FLOW_NODE_ID = \"sourceObjectId\";\n\n    public static final String TERMINAL = \"terminal\";\n\n    public static final String REACHED_STATE_DATE = \"reachedStateDate\";\n\n    public static final String ARCHIVE_DATE = \"archiveDate\";\n\n    public static final String STATE_ID = \"stateId\";\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedGatewayInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic interface ArchivedGatewayInstance extends ArchivedFlowNodeInstance {\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedHumanTaskInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\nimport java.util.Date;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface ArchivedHumanTaskInstance extends ArchivedTaskInstance {\n\n    long getActorId();\n\n    long getAssigneeId();\n\n    TaskPriority getPriority();\n\n    Date getExpectedEndDate();\n\n    Date getClaimedDate();\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedHumanTaskInstanceSearchDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Julien Mege\n * @author Emmanuel Duchastenier\n * @author Matthieu Chaffotte\n */\npublic final class ArchivedHumanTaskInstanceSearchDescriptor {\n\n    private ArchivedHumanTaskInstanceSearchDescriptor() {\n        // Utility class\n    }\n\n    public static final String NAME = \"name\";\n\n    public static final String PRIORITY = \"priority\";\n\n    public static final String STATE_NAME = \"state\";\n\n    public static final String SUPERVISOR_ID = \"supervisorId\";\n\n    public static final String ASSIGNEE_ID = \"assigneeId\";\n\n    public static final String PROCESS_DEFINITION_ID = \"processDefinitionId\";\n\n    public static final String ORIGINAL_HUMAN_TASK_ID = \"sourceObjectId\";\n\n    public static final String ROOT_PROCESS_INSTANCE_ID = \"rootProcessInstanceId\";\n\n    public static final String PARENT_PROCESS_INSTANCE_ID = \"parentProcessInstanceId\";\n\n    public static final String PARENT_ACTIVITY_INSTANCE_ID = \"parentActivityInstanceId\";\n\n    public static final String DISPLAY_NAME = \"displayName\";\n\n    public static final String REACHED_STATE_DATE = \"reachedStateDate\";\n\n    public static final String TERMINAL = \"terminal\";\n\n    public static final String ARCHIVE_DATE = \"archiveDate\";\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedLoopActivityInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface ArchivedLoopActivityInstance extends ArchivedActivityInstance {\n\n    int getLoopCounter();\n\n    int getLoopMax();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedManualTaskInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface ArchivedManualTaskInstance extends ArchivedHumanTaskInstance {\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedMultiInstanceActivityInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface ArchivedMultiInstanceActivityInstance extends ArchivedActivityInstance {\n\n    boolean isSequential();\n\n    String getLoopDataInputRef();\n\n    String getLoopDataOutputRef();\n\n    String getDataInputItemRef();\n\n    String getDataOutputItemRef();\n\n    int getLoopCardinality();\n\n    int getNumberOfInstances();\n\n    int getNumberOfActiveInstances();\n\n    int getNumberOfCompletedInstances();\n\n    int getNumberOfTerminatedInstances();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedReceiveTaskInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Julien Molinaro\n */\npublic interface ArchivedReceiveTaskInstance extends ArchivedActivityInstance {\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedSendTaskInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface ArchivedSendTaskInstance extends ArchivedActivityInstance {\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedSubProcessActivityInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface ArchivedSubProcessActivityInstance extends ArchivedActivityInstance {\n\n    boolean isTriggeredByEvent();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedTaskInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Julien Mege\n */\npublic interface ArchivedTaskInstance extends ArchivedActivityInstance {\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedUserTaskInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Julien Mege\n */\npublic interface ArchivedUserTaskInstance extends ArchivedHumanTaskInstance {\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/AutomaticTaskInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface AutomaticTaskInstance extends TaskInstance {\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/BPMEventType.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic enum BPMEventType {\n\n    START_EVENT,\n\n    INTERMEDIATE_CATCH_EVENT,\n\n    INTERMEDIATE_THROW_EVENT,\n\n    END_EVENT,\n\n    BOUNDARY_EVENT,\n\n    EVENT_SUB_PROCESS\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/BoundaryEventInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface BoundaryEventInstance extends CatchEventInstance {\n\n    String getActivityName();\n\n    long getActivityInstanceId();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/CallActivityInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface CallActivityInstance extends ActivityInstance {\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/CatchEventInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface CatchEventInstance extends EventInstance {\n\n    boolean isInterrupting();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/EndEventInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface EndEventInstance extends ThrowEventInstance {\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/EventCriterion.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic enum EventCriterion {\n\n    NAME_DESC, NAME_ASC;\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/EventInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface EventInstance extends FlowNodeInstance {\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/EventTriggerInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\nimport org.bonitasoft.engine.bpm.BaseElement;\n\n/**\n * @author Celine Souchet\n * @version 6.4.0\n * @since 6.4.0\n */\npublic interface EventTriggerInstance extends BaseElement {\n\n    /**\n     * Return the identifier of the {@link EventInstance} which it is attached.\n     *\n     * @return The identifier of the {@link EventInstance}\n     * @since 6.4.0\n     */\n    long getEventInstanceId();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/EventTriggerInstanceSearchDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * Fields to filter the {@link EventTriggerInstance} on search.\n *\n * @author Celine Souchet\n * @version 6.4.0\n * @since 6.4.0\n */\npublic class EventTriggerInstanceSearchDescriptor {\n\n    public static final String EVENT_INSTANCE_ID = \"eventInstanceId\";\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/FlowElementInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\nimport org.bonitasoft.engine.bpm.NamedElement;\n\n/**\n * @author Emmanuel Duchastenier\n * @author Matthieu Chaffotte\n */\npublic interface FlowElementInstance extends NamedElement {\n\n    long getRootContainerId();\n\n    long getParentContainerId();\n\n    /**\n     * Is this flow element instance currently aborting?\n     *\n     * @return true if this flow element instance is currently aborting, false otherwise\n     * @since 6.0\n     */\n    boolean isAborting();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/FlowElementInstanceSearchDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class FlowElementInstanceSearchDescriptor {\n\n    public static final String NAME = \"name\";\n\n    public static final String DESCRIPTION = \"description\";\n\n    // public static final String STATE_NAME = \"state\";\n\n    public static final String FLOW_NODE_TYPE = \"flowNodeType\"; // kind?\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/FlowNodeExecutionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\nimport org.bonitasoft.engine.exception.ExecutionException;\n\n/**\n * Thrown when a BPM flow node fails to execute.\n *\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n */\npublic class FlowNodeExecutionException extends ExecutionException {\n\n    private static final long serialVersionUID = 1873896404277831296L;\n\n    /**\n     * Constructs a new exception with the specified detail message.\n     *\n     * @param message\n     *        The detail message why the flow node failed to execute(which is saved for later retrieval by the\n     *        {@link Throwable#getMessage()} method).\n     */\n    public FlowNodeExecutionException(final String message) {\n        super(message);\n    }\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause\n     *        The cause of the flow node failure (which is saved for later retrieval by the {@link Throwable#getCause()}\n     *        method). (A null value is\n     *        permitted, and indicates that the cause is nonexistent or unknown.)\n     */\n    public FlowNodeExecutionException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/FlowNodeInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.bpm.BaseElement;\nimport org.bonitasoft.engine.bpm.DescriptionElement;\n\n/**\n * @author Baptiste Mesta\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n * @since 6.0.0\n * @version 6.4.1\n */\npublic interface FlowNodeInstance extends DescriptionElement, BaseElement {\n\n    /**\n     * Returns the task's direct container ID. For a sub-task or CallActivity, would point to the containing activity ID\n     * of the current element.\n     * For a normal Task / Activity, would point to the ID of the process instance containing the task / activity.\n     * For a multi-instanciated task, each task has its parentContainerId pointing to the containing multi-instance task\n     * (basic container for all task\n     * instances).\n     *\n     * @return the ID of the direct containing element (activity instance of process instance).\n     */\n    long getParentContainerId();\n\n    /**\n     * Returns the root container ID. It is the ID of the root-level containing process instance.\n     *\n     * @return the root container ID.\n     */\n    long getRootContainerId();\n\n    /**\n     * Returns the ID of the process definition where this <code>FlowNodeInstance</code> is defined.\n     *\n     * @return the ID of the process definition.\n     */\n    long getProcessDefinitionId();\n\n    /**\n     * Always returns the directly containing process instance ID (at the lower level, if several levels of containing\n     * processes).\n     *\n     * @return the ID of the lowest-level containing process instance.\n     */\n    long getParentProcessInstanceId();\n\n    /**\n     * Returns the name of the state the flow node instance is in.\n     * <p>\n     * List of existing states:\n     * <p>\n     * States that are transitional:\n     * <ul>\n     * <li>initializing: preparing for execution</li>\n     * <li>executing: executing logic, e.g. operations</li>\n     * <li>completing: executing logic before completion, e.g. ON_FINISH connectors of call activity</li>\n     * <li>completing activity with boundary: completing related boundary events</li>\n     * <li>cancelling: waiting for related elements to be cancelled</li>\n     * <li>aborting: waiting for related elements to be aborted</li>\n     * <li>aborting activity with boundary: waiting for boundaries to be aborted</li>\n     * <li>aborting call activity: waiting for called element to be aborted</li>\n     * <li>canceling call activity: waiting for called element to be cancelled</li>\n     * <li>cancelling subtasks: waiting for cancellation of sub tasks</li>\n     * </ul>\n     * <p>\n     * States where flow node is waiting for something:\n     * <ul>\n     * <li>ready: human task is ready to be executed</li>\n     * <li>waiting: flow node is waiting for a non-human interaction, e.g. a BPMN message</li>\n     * <li>failed: when an error occurred, flow node can be skipped or replayed</li>\n     * </ul>\n     * <p>\n     * Final States:\n     * <ul>\n     * <li>completed: final state</li>\n     * <li>aborted: flow node was aborted by another flow of the process</li>\n     * <li>skipped: flow node was manually skipped</li>\n     * <li>interrupted: final state, interrupted</li>\n     * <li>cancelled: flow node was cancelled by a human</li>\n     * </ul>\n     *\n     * @return this FlowNodeInstance state\n     */\n    String getState();\n\n    StateCategory getStateCategory();\n\n    /**\n     * Returns the <code>FlowNodeType</code> that precises the concrete type of this <code>FlowNodeInstance</code>.\n     *\n     * @return the <code>FlowNodeType</code>\n     */\n    FlowNodeType getType();\n\n    String getDisplayDescription();\n\n    String getDisplayName();\n\n    /**\n     * @return The identifier of the user who executed the flow node\n     * @since 6.0.1\n     */\n    long getExecutedBy();\n\n    /**\n     * @return The identifier of the substitute user (as Process manager or Administrator) who executed the flow node.\n     * @since 6.3.0\n     */\n    long getExecutedBySubstitute();\n\n    /**\n     * Returns the ID of the flow node definition of this instance.\n     *\n     * @return the ID of the flow node definition that this <code>FlowNodeInstance</code> is an instance of.\n     */\n    long getFlownodeDefinitionId();\n\n    /**\n     * @return The date when the flownode instance reached its state ({@link #getState()})\n     */\n    Date getReachedStateDate();\n\n    /**\n     * @return The last date when the activity instance was updated\n     */\n    Date getLastUpdateDate();\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/FlowNodeInstanceNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\nimport org.bonitasoft.engine.exception.NotFoundException;\n\n/**\n * Thrown when it's not possible to find the flow node instance.\n * The class FlowNodeInstanceNotFoundException is a form of Throwable that indicates conditions that a reasonable\n * application might want to catch.\n * The class FlowNodeInstanceNotFoundException that is not also subclasses of {@link RuntimeException} are checked\n * exceptions.\n * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by\n * the execution of the method or\n * constructor and propagate outside the method or constructor boundary.\n *\n * @author Celine Souchet\n */\npublic class FlowNodeInstanceNotFoundException extends NotFoundException {\n\n    private static final long serialVersionUID = -854820983838093797L;\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public FlowNodeInstanceNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/FlowNodeInstanceSearchDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class FlowNodeInstanceSearchDescriptor {\n\n    public static final String NAME = \"name\";\n\n    public static final String STATE_NAME = \"state\";\n\n    public static final String PROCESS_DEFINITION_ID = \"processDefinitionId\";\n\n    public static final String PARENT_PROCESS_INSTANCE_ID = \"parentProcessInstanceId\";\n\n    public static final String PARENT_ACTIVITY_INSTANCE_ID = \"parentActivityInstanceId\";\n\n    public static final String ROOT_PROCESS_INSTANCE_ID = \"rootProcessInstanceId\";\n\n    public static final String DISPLAY_NAME = \"displayName\";\n\n    public static final String STATE_CATEGORY = \"stateCategory\";\n\n    public static final String LAST_UPDATE_DATE = \"lastUpdateDate\";\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/FlowNodeType.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Zhang Bole\n * @author Baptitse Mesta\n * @author Elias Ricken de Medeiros\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n */\npublic enum FlowNodeType {\n\n    AUTOMATIC_TASK,\n\n    HUMAN_TASK,\n\n    USER_TASK,\n\n    MANUAL_TASK,\n\n    SEND_TASK,\n\n    RECEIVE_TASK,\n\n    CALL_ACTIVITY,\n\n    LOOP_ACTIVITY,\n\n    MULTI_INSTANCE_ACTIVITY,\n\n    SUB_PROCESS,\n\n    GATEWAY,\n\n    START_EVENT,\n\n    INTERMEDIATE_CATCH_EVENT,\n\n    BOUNDARY_EVENT,\n\n    INTERMEDIATE_THROW_EVENT,\n\n    END_EVENT;\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/GatewayInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface GatewayInstance extends FlowNodeInstance {\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/HumanTaskInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\nimport java.util.Date;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface HumanTaskInstance extends TaskInstance {\n\n    long getActorId();\n\n    long getAssigneeId();\n\n    TaskPriority getPriority();\n\n    Date getExpectedEndDate();\n\n    Date getClaimedDate();\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/HumanTaskInstanceSearchDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Julien Mege\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic final class HumanTaskInstanceSearchDescriptor {\n\n    public static final String NAME = \"name\";\n\n    public static final String PRIORITY = \"priority\";\n\n    public static final String DUE_DATE = \"dueDate\";\n\n    public static final String STATE_NAME = \"state\";\n\n    public static final String SUPERVISOR_ID = \"supervisorId\";\n\n    /**\n     * User who supervises the process definition\n     */\n    public static final String USER_ID = \"userId\";\n\n    /**\n     * Group who supervises the process definition\n     */\n    public static final String GROUP_ID = \"groupId\";\n\n    /**\n     * Role who supervises the process definition\n     */\n    public static final String ROLE_ID = \"roleId\";\n\n    public static final String PROCESS_DEFINITION_ID = \"processDefinitionId\";\n\n    public static final String PROCESS_INSTANCE_ID = \"processInstanceId\";\n\n    public static final String ROOT_PROCESS_INSTANCE_ID = \"rootProcessInstanceId\";\n\n    public static final String PARENT_PROCESS_INSTANCE_ID = \"parentProcessInstanceId\";\n\n    public static final String PARENT_ACTIVITY_INSTANCE_ID = \"parentActivityInstanceId\";\n\n    public static final String ASSIGNEE_ID = \"assigneeId\";\n\n    public static final String PARENT_CONTAINER_ID = \"parentContainerId\";\n\n    public static final String DISPLAY_NAME = \"displayName\";\n\n    public static final String REACHED_STATE_DATE = \"reachedStateDate\";\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/IntermediateCatchEventInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface IntermediateCatchEventInstance extends CatchEventInstance {\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/IntermediateThrowEventInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface IntermediateThrowEventInstance extends ThrowEventInstance {\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/LoopActivityInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface LoopActivityInstance extends ActivityInstance {\n\n    int getLoopCounter();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ManualTaskInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface ManualTaskInstance extends HumanTaskInstance {\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/MultiInstanceActivityInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface MultiInstanceActivityInstance extends ActivityInstance {\n\n    boolean isSequential();\n\n    String getLoopDataInputRef();\n\n    String getLoopDataOutputRef();\n\n    String getDataInputItemRef();\n\n    String getDataOutputItemRef();\n\n    int getLoopCardinality();\n\n    int getNumberOfInstances();\n\n    int getNumberOfActiveInstances();\n\n    int getNumberOfCompletedInstances();\n\n    int getNumberOfTerminatedInstances();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ReceiveTaskInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Julien Molinaro\n */\npublic interface ReceiveTaskInstance extends TaskInstance {\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/SendEventException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\nimport org.bonitasoft.engine.exception.ExecutionException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SendEventException extends ExecutionException {\n\n    private static final long serialVersionUID = 5262064474736586736L;\n\n    public SendEventException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SendEventException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/SendTaskInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface SendTaskInstance extends TaskInstance {\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/StartEventInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface StartEventInstance extends CatchEventInstance {\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/StateCategory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic enum StateCategory {\n    /**\n     * The state is part of normal states\n     */\n    NORMAL,\n\n    /**\n     * The state is part of aborting states\n     */\n    ABORTING,\n\n    /**\n     * The state is part o canceling states\n     */\n    CANCELLING\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/SubProcessActivityInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SubProcessActivityInstance extends ActivityInstance {\n\n    boolean isTriggeredByEvent();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/TaskInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface TaskInstance extends ActivityInstance {\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ThrowEventInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface ThrowEventInstance extends EventInstance {\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/TimerEventTriggerInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\nimport java.util.Date;\n\n/**\n * Represent the instance of the {@link TimerEventTriggerDefinition} (only for the type {@link TimerType#DATE} and\n * {@link TimerType#DURATION})\n *\n * @author Celine Souchet\n * @version 6.4.0\n * @since 6.4.0\n */\npublic interface TimerEventTriggerInstance extends EventTriggerInstance {\n\n    /**\n     * Return the date of the execution of the trigger.\n     *\n     * @return The date of the execution of the trigger.\n     * @since 6.4.0\n     */\n    Date getExecutionDate();\n\n    /**\n     * Return the name of the {@link EventInstance} which it is attached.\n     *\n     * @return The name of the {@link EventInstance}\n     * @since 6.4.0\n     */\n    String getEventInstanceName();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/TimerEventTriggerInstanceNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\nimport org.bonitasoft.engine.exception.NotFoundException;\n\n/**\n * Thrown when it's not possible to find the {@link TimerEventTriggerInstance}.\n *\n * @author Celine Souchet\n * @version 6.4.0\n * @since 6.4.0\n */\npublic class TimerEventTriggerInstanceNotFoundException extends NotFoundException {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public TimerEventTriggerInstanceNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n    /**\n     * @param timerEventTriggerInstanceId\n     *        The identifier of the timer trigger instance who doesn't find\n     */\n    public TimerEventTriggerInstanceNotFoundException(final long timerEventTriggerInstanceId) {\n        super(\"Can't find the TimerEventInstance with id=\" + timerEventTriggerInstanceId);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/TimerEventTriggerInstanceSearchDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * Fields to filter the {@link TimerEventTriggerInstance} on search.\n *\n * @author Celine Souchet\n * @version 6.4.0\n * @since 6.4.0\n */\npublic class TimerEventTriggerInstanceSearchDescriptor extends EventTriggerInstanceSearchDescriptor {\n\n    public static final String EVENT_INSTANCE_NAME = \"name\";\n\n    public static final String EXECUTION_DATE = \"executionDate\";\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/UserTaskInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface UserTaskInstance extends HumanTaskInstance {\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/UserTaskNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\nimport org.bonitasoft.engine.exception.NotFoundException;\n\n/**\n * Thrown when it's not possible to find the user task.\n * The class UserTaskNotFoundException is a form of Throwable that indicates conditions that a reasonable application\n * might want to catch.\n * The class UserTaskNotFoundException that is not also subclasses of {@link RuntimeException} are checked exceptions.\n * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by\n * the execution of the method or\n * constructor and propagate outside the method or constructor boundary.\n *\n * @author Zhang Bole\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class UserTaskNotFoundException extends NotFoundException {\n\n    private static final long serialVersionUID = -1165526736427481646L;\n\n    /**\n     * Constructs a new exception with the specified detail message.\n     *\n     * @param message\n     *        The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method).\n     */\n    public UserTaskNotFoundException(final String message) {\n        super(message);\n    }\n\n    public UserTaskNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/WaitingErrorEvent.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface WaitingErrorEvent extends WaitingEvent {\n\n    String getErrorCode();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/WaitingEvent.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\nimport org.bonitasoft.engine.bpm.BonitaObject;\n\n/**\n * @author Zhao Na\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n * @author Matthieu Chaffotte\n */\npublic interface WaitingEvent extends BonitaObject {\n\n    BPMEventType getEventType();\n\n    String getProcessName();\n\n    long getProcessDefinitionId();\n\n    long getRootProcessInstanceId();\n\n    long getParentProcessInstanceId();\n\n    long getFlowNodeDefinitionId();\n\n    long getFlowNodeInstanceId();\n\n    boolean isActive();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/WaitingEventSearchDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class WaitingEventSearchDescriptor {\n\n    public static final String BPM_EVENT_TYPE = \"bpmEventType\";\n\n    public static final String PROCESS_NAME = \"processName\";\n\n    public static final String FLOW_NODE_NAME = \"flowNodeName\";\n\n    public static final String PROCESS_DEFINITION_ID = \"processDefinitionId\";\n\n    public static final String ROOT_PROCESS_INSTANCE_ID = \"rootProcessInstanceId\";\n\n    public static final String PARENT_PROCESS_INSTANCE_ID = \"parentProcessInstanceId\";\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/WaitingMessageEvent.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface WaitingMessageEvent extends WaitingEvent {\n\n    String getMessageName();\n\n    boolean isLocked();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/WaitingSignalEvent.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface WaitingSignalEvent extends WaitingEvent {\n\n    String getSignalName();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/ActivityInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic abstract class ActivityInstanceImpl extends FlowNodeInstanceImpl implements ActivityInstance {\n\n    private static final long serialVersionUID = 8518693723223444468L;\n\n    public ActivityInstanceImpl(final String name, final long flownodeDefinitionId) {\n        super(name, flownodeDefinitionId);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/ArchivedActivityInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic abstract class ArchivedActivityInstanceImpl extends ArchivedFlowNodeInstanceImpl\n        implements ArchivedActivityInstance {\n\n    private static final long serialVersionUID = 2457027970594869050L;\n\n    public ArchivedActivityInstanceImpl(final String name) {\n        super(name);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/ArchivedAutomaticTaskInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport org.bonitasoft.engine.bpm.flownode.ArchivedAutomaticTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic class ArchivedAutomaticTaskInstanceImpl extends ArchivedActivityInstanceImpl\n        implements ArchivedAutomaticTaskInstance {\n\n    private static final long serialVersionUID = 9035445836798276633L;\n\n    public ArchivedAutomaticTaskInstanceImpl(final String name) {\n        super(name);\n    }\n\n    @Override\n    public FlowNodeType getType() {\n        return FlowNodeType.AUTOMATIC_TASK;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/ArchivedCallActivityInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport org.bonitasoft.engine.bpm.flownode.ArchivedCallActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ArchivedCallActivityInstanceImpl extends ArchivedActivityInstanceImpl\n        implements ArchivedCallActivityInstance {\n\n    private static final long serialVersionUID = 3788310745899679306L;\n\n    public ArchivedCallActivityInstanceImpl(final String name) {\n        super(name);\n    }\n\n    @Override\n    public FlowNodeType getType() {\n        return FlowNodeType.CALL_ACTIVITY;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/ArchivedFlowElementInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport org.bonitasoft.engine.bpm.flownode.ArchivedFlowElementInstance;\nimport org.bonitasoft.engine.bpm.internal.NamedElementImpl;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic abstract class ArchivedFlowElementInstanceImpl extends NamedElementImpl implements ArchivedFlowElementInstance {\n\n    private static final long serialVersionUID = -8382446613679794971L;\n\n    private long parentContainerId;\n\n    private long rootContainerId;\n\n    private boolean aborting;\n\n    private String description;\n\n    public ArchivedFlowElementInstanceImpl(final String name) {\n        super(name);\n    }\n\n    @Override\n    public long getRootContainerId() {\n        return rootContainerId;\n    }\n\n    public void setRootContainerId(final long rootContainerId) {\n        this.rootContainerId = rootContainerId;\n    }\n\n    @Override\n    public long getParentContainerId() {\n        return parentContainerId;\n    }\n\n    public void setParentContainerId(final long parentContainerId) {\n        this.parentContainerId = parentContainerId;\n    }\n\n    @Override\n    public boolean isAborting() {\n        return aborting;\n    }\n\n    public void setAborting(final boolean aborting) {\n        this.aborting = aborting;\n    }\n\n    @Override\n    public String getDescription() {\n        return description;\n    }\n\n    /**\n     * @param description\n     *        the description to set\n     */\n    public void setDescription(final String description) {\n        this.description = description;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/ArchivedFlowNodeInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport java.util.Date;\nimport java.util.Objects;\n\nimport lombok.ToString;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance;\nimport org.bonitasoft.engine.bpm.internal.NamedElementImpl;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\n@ToString(callSuper = true)\npublic abstract class ArchivedFlowNodeInstanceImpl extends NamedElementImpl implements ArchivedFlowNodeInstance {\n\n    private static final long serialVersionUID = -6573747806944970703L;\n    private long parentContainerId;\n\n    private Date archiveDate;\n\n    private String state;\n\n    private long rootContainerId;\n\n    private long processDefinitionId;\n\n    private long processInstanceId;\n\n    private long parentActivityInstanceId;\n\n    private String displayName;\n\n    private String displayDescription;\n\n    private long sourceObjectId;\n\n    private String description;\n\n    private long executedBy;\n\n    private long executedBySubstitute;\n\n    private long flownodeDefinitionId;\n\n    private boolean terminal;\n\n    protected Date reachedStateDate;\n\n    protected Date lastUpdateDate;\n\n    public ArchivedFlowNodeInstanceImpl(final String name) {\n        super(name);\n    }\n\n    @Override\n    public long getParentContainerId() {\n        return parentContainerId;\n    }\n\n    public void setParentContainerId(long parentContainerId) {\n        this.parentContainerId = parentContainerId;\n    }\n\n    @Override\n    public Date getArchiveDate() {\n        return archiveDate;\n    }\n\n    public void setArchiveDate(Date archiveDate) {\n        this.archiveDate = archiveDate;\n    }\n\n    @Override\n    public String getState() {\n        return state;\n    }\n\n    public void setState(String state) {\n        this.state = state;\n    }\n\n    @Override\n    public long getRootContainerId() {\n        return rootContainerId;\n    }\n\n    public void setRootContainerId(long rootContainerId) {\n        this.rootContainerId = rootContainerId;\n    }\n\n    @Override\n    public long getProcessDefinitionId() {\n        return processDefinitionId;\n    }\n\n    public void setProcessDefinitionId(long processDefinitionId) {\n        this.processDefinitionId = processDefinitionId;\n    }\n\n    @Override\n    public long getProcessInstanceId() {\n        return processInstanceId;\n    }\n\n    public void setProcessInstanceId(long processInstanceId) {\n        this.processInstanceId = processInstanceId;\n    }\n\n    @Override\n    public long getParentActivityInstanceId() {\n        return parentActivityInstanceId;\n    }\n\n    public void setParentActivityInstanceId(long parentActivityInstanceId) {\n        this.parentActivityInstanceId = parentActivityInstanceId;\n    }\n\n    @Override\n    public String getDisplayName() {\n        return displayName;\n    }\n\n    public void setDisplayName(String displayName) {\n        this.displayName = displayName;\n    }\n\n    @Override\n    public String getDisplayDescription() {\n        return displayDescription;\n    }\n\n    public void setDisplayDescription(String displayDescription) {\n        this.displayDescription = displayDescription;\n    }\n\n    @Override\n    public long getSourceObjectId() {\n        return sourceObjectId;\n    }\n\n    public void setSourceObjectId(long sourceObjectId) {\n        this.sourceObjectId = sourceObjectId;\n    }\n\n    @Override\n    public String getDescription() {\n        return description;\n    }\n\n    public void setDescription(String description) {\n        this.description = description;\n    }\n\n    @Override\n    public long getExecutedBy() {\n        return executedBy;\n    }\n\n    public void setExecutedBy(long executedBy) {\n        this.executedBy = executedBy;\n    }\n\n    @Override\n    public long getExecutedBySubstitute() {\n        return executedBySubstitute;\n    }\n\n    public void setExecutedBySubstitute(long executedBySubstitute) {\n        this.executedBySubstitute = executedBySubstitute;\n    }\n\n    @Override\n    public long getFlownodeDefinitionId() {\n        return flownodeDefinitionId;\n    }\n\n    public void setFlownodeDefinitionId(long flownodeDefinitionId) {\n        this.flownodeDefinitionId = flownodeDefinitionId;\n    }\n\n    @Override\n    public boolean isTerminal() {\n        return terminal;\n    }\n\n    public void setTerminal(boolean terminal) {\n        this.terminal = terminal;\n    }\n\n    public Date getReachedStateDate() {\n        return reachedStateDate;\n    }\n\n    public void setReachedStateDate(Date reachedStateDate) {\n        this.reachedStateDate = reachedStateDate;\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(super.hashCode(), parentContainerId, archiveDate, state, rootContainerId,\n                processDefinitionId, processInstanceId,\n                parentActivityInstanceId, displayName, displayDescription, sourceObjectId, description, executedBy,\n                executedBySubstitute, flownodeDefinitionId,\n                terminal, reachedStateDate, lastUpdateDate);\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (!(o instanceof ArchivedFlowNodeInstanceImpl that))\n            return false;\n        if (!super.equals(o))\n            return false;\n        return Objects.equals(parentContainerId, that.parentContainerId) &&\n                Objects.equals(rootContainerId, that.rootContainerId) &&\n                Objects.equals(processDefinitionId, that.processDefinitionId) &&\n                Objects.equals(processInstanceId, that.processInstanceId) &&\n                Objects.equals(parentActivityInstanceId, that.parentActivityInstanceId) &&\n                Objects.equals(sourceObjectId, that.sourceObjectId) &&\n                Objects.equals(executedBy, that.executedBy) &&\n                Objects.equals(executedBySubstitute, that.executedBySubstitute) &&\n                Objects.equals(flownodeDefinitionId, that.flownodeDefinitionId) &&\n                Objects.equals(terminal, that.terminal) &&\n                Objects.equals(archiveDate, that.archiveDate) &&\n                Objects.equals(state, that.state) &&\n                Objects.equals(displayName, that.displayName) &&\n                Objects.equals(displayDescription, that.displayDescription) &&\n                Objects.equals(description, that.description) &&\n                Objects.equals(reachedStateDate, that.reachedStateDate) &&\n                Objects.equals(lastUpdateDate, that.lastUpdateDate);\n    }\n\n    @Override\n    public Date getLastUpdateDate() {\n        return lastUpdateDate;\n    }\n\n    public void setLastUpdateDate(final Date lastUpdateDate) {\n        this.lastUpdateDate = lastUpdateDate;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/ArchivedGatewayInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport org.bonitasoft.engine.bpm.flownode.ArchivedGatewayInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class ArchivedGatewayInstanceImpl extends ArchivedFlowNodeInstanceImpl implements ArchivedGatewayInstance {\n\n    private static final long serialVersionUID = 9086744510541764127L;\n\n    public ArchivedGatewayInstanceImpl(final String name) {\n        super(name);\n    }\n\n    @Override\n    public FlowNodeType getType() {\n        return FlowNodeType.GATEWAY;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/ArchivedHumanTaskInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.TaskPriority;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic abstract class ArchivedHumanTaskInstanceImpl extends ArchivedActivityInstanceImpl\n        implements ArchivedHumanTaskInstance {\n\n    private static final long serialVersionUID = -5566250139796838252L;\n\n    private long actorId;\n\n    private long assigneeId;\n\n    private Date claimedDate;\n\n    private TaskPriority priority;\n\n    private Date expectedEndDate;\n\n    public ArchivedHumanTaskInstanceImpl(final String name) {\n        super(name);\n    }\n\n    @Override\n    public long getActorId() {\n        return actorId;\n    }\n\n    @Override\n    public long getAssigneeId() {\n        return assigneeId;\n    }\n\n    @Override\n    public Date getClaimedDate() {\n        return claimedDate;\n    }\n\n    public void setClaimedDate(Date claimedDate) {\n        this.claimedDate = claimedDate;\n    }\n\n    @Override\n    public Date getExpectedEndDate() {\n        return expectedEndDate;\n    }\n\n    @Override\n    public TaskPriority getPriority() {\n        return priority;\n    }\n\n    public void setActorId(final long actorId) {\n        this.actorId = actorId;\n    }\n\n    public void setAssigneeId(final long assigneeId) {\n        this.assigneeId = assigneeId;\n    }\n\n    public void setPriority(final TaskPriority priority) {\n        this.priority = priority;\n    }\n\n    public void setExpectedEndDate(final Date expectedEndDate) {\n        this.expectedEndDate = expectedEndDate;\n    }\n\n    @Override\n    public String toString() {\n        final StringBuilder builder = new StringBuilder();\n        builder.append(\"ArchivedHumanTaskInstanceImpl [actorId=\");\n        builder.append(actorId);\n        builder.append(\", assigneeId=\");\n        builder.append(assigneeId);\n        builder.append(\", priority=\");\n        builder.append(priority);\n        builder.append(\", expectedEndDate=\");\n        builder.append(expectedEndDate);\n        builder.append(\", getReachedStateDate()=\");\n        builder.append(getReachedStateDate());\n        builder.append(\", getLastUpdateDate()=\");\n        builder.append(getLastUpdateDate());\n        builder.append(\", getArchiveDate()=\");\n        builder.append(getArchiveDate());\n        builder.append(\", getState()=\");\n        builder.append(getState());\n        builder.append(\", getParentContainerId()=\");\n        builder.append(getParentContainerId());\n        builder.append(\", getRootContainerId()=\");\n        builder.append(getRootContainerId());\n        builder.append(\", getProcessDefinitionId()=\");\n        builder.append(getProcessDefinitionId());\n        builder.append(\", getProcessInstanceId()=\");\n        builder.append(getProcessInstanceId());\n        builder.append(\", getName()=\");\n        builder.append(getName());\n        builder.append(\", getId()=\");\n        builder.append(getId());\n        builder.append(\"]\");\n        return builder.toString();\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = super.hashCode();\n        result = prime * result + (int) (actorId ^ actorId >>> 32);\n        result = prime * result + (int) (assigneeId ^ assigneeId >>> 32);\n        result = prime * result + (expectedEndDate == null ? 0 : expectedEndDate.hashCode());\n        result = prime * result + (priority == null ? 0 : priority.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (!super.equals(obj)) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        final ArchivedHumanTaskInstanceImpl other = (ArchivedHumanTaskInstanceImpl) obj;\n        if (actorId != other.actorId) {\n            return false;\n        }\n        if (assigneeId != other.assigneeId) {\n            return false;\n        }\n        if (expectedEndDate == null) {\n            if (other.expectedEndDate != null) {\n                return false;\n            }\n        } else if (!expectedEndDate.equals(other.expectedEndDate)) {\n            return false;\n        }\n        if (priority != other.priority) {\n            return false;\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/ArchivedLoopActivityInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport org.bonitasoft.engine.bpm.flownode.ArchivedLoopActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\n\n/**\n * @author Baptiste Mesta\n */\npublic class ArchivedLoopActivityInstanceImpl extends ArchivedActivityInstanceImpl\n        implements ArchivedLoopActivityInstance {\n\n    private static final long serialVersionUID = -1606721131308631806L;\n\n    private int loopCounter;\n\n    private int loopMax;\n\n    public ArchivedLoopActivityInstanceImpl(final String name) {\n        super(name);\n    }\n\n    @Override\n    public FlowNodeType getType() {\n        return FlowNodeType.LOOP_ACTIVITY;\n    }\n\n    @Override\n    public int getLoopCounter() {\n        return loopCounter;\n    }\n\n    @Override\n    public int getLoopMax() {\n        return loopMax;\n    }\n\n    public void setLoopCounter(final int loopCounter) {\n        this.loopCounter = loopCounter;\n    }\n\n    public void setLoopMax(final int loopMax) {\n        this.loopMax = loopMax;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = super.hashCode();\n        result = prime * result + loopCounter;\n        result = prime * result + loopMax;\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (!super.equals(obj)) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        final ArchivedLoopActivityInstanceImpl other = (ArchivedLoopActivityInstanceImpl) obj;\n        if (loopCounter != other.loopCounter) {\n            return false;\n        }\n        if (loopMax != other.loopMax) {\n            return false;\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/ArchivedManualTaskInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport org.bonitasoft.engine.bpm.flownode.ArchivedManualTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\n\n/**\n * @author Baptiste Mesta\n */\npublic class ArchivedManualTaskInstanceImpl extends ArchivedHumanTaskInstanceImpl\n        implements ArchivedManualTaskInstance {\n\n    private static final long serialVersionUID = -5611907403212645478L;\n\n    public ArchivedManualTaskInstanceImpl(final String name) {\n        super(name);\n    }\n\n    @Override\n    public FlowNodeType getType() {\n        return FlowNodeType.MANUAL_TASK;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/ArchivedMultiInstanceActivityInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport org.bonitasoft.engine.bpm.flownode.ArchivedMultiInstanceActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic class ArchivedMultiInstanceActivityInstanceImpl extends ArchivedActivityInstanceImpl\n        implements ArchivedMultiInstanceActivityInstance {\n\n    private static final long serialVersionUID = -8302189436621268820L;\n\n    private final boolean sequential;\n\n    private final String loopDataInputRef;\n\n    private final String loopDataOutputRef;\n\n    private final String dataInputItemRef;\n\n    private final String dataOutputItemRef;\n\n    private final int numberOfActiveInstances;\n\n    private final int numberOfCompletedInstances;\n\n    private final int numberOfTerminatedInstances;\n\n    private final int loopCardinality;\n\n    public ArchivedMultiInstanceActivityInstanceImpl(final String name, final long flownodeDefinitionId,\n            final boolean sequential,\n            final String loopDataInputRef, final String loopDataOutputRef, final String dataInputItemRef,\n            final String dataOutputItemRef,\n            final int numberOfActiveInstances, final int numberOfCompletedInstances,\n            final int numberOfTerminatedInstances, final int loopCardinality) {\n        super(name);\n        setFlownodeDefinitionId(flownodeDefinitionId);\n        this.sequential = sequential;\n        this.loopDataInputRef = loopDataInputRef;\n        this.loopDataOutputRef = loopDataOutputRef;\n        this.dataInputItemRef = dataInputItemRef;\n        this.dataOutputItemRef = dataOutputItemRef;\n        this.numberOfActiveInstances = numberOfActiveInstances;\n        this.numberOfCompletedInstances = numberOfCompletedInstances;\n        this.numberOfTerminatedInstances = numberOfTerminatedInstances;\n        this.loopCardinality = loopCardinality;\n    }\n\n    /**\n     * @return the sequential\n     */\n    @Override\n    public boolean isSequential() {\n        return sequential;\n    }\n\n    /**\n     * @return the loopDataInputRef\n     */\n    @Override\n    public String getLoopDataInputRef() {\n        return loopDataInputRef;\n    }\n\n    /**\n     * @return the loopDataOutputRef\n     */\n    @Override\n    public String getLoopDataOutputRef() {\n        return loopDataOutputRef;\n    }\n\n    /**\n     * @return the dataInputItemRef\n     */\n    @Override\n    public String getDataInputItemRef() {\n        return dataInputItemRef;\n    }\n\n    /**\n     * @return the dataOutputItemRef\n     */\n    @Override\n    public String getDataOutputItemRef() {\n        return dataOutputItemRef;\n    }\n\n    /**\n     * @return the numberOfActiveInstances\n     */\n    @Override\n    public int getNumberOfActiveInstances() {\n        return numberOfActiveInstances;\n    }\n\n    /**\n     * @return the numberOfCompletedInstances\n     */\n    @Override\n    public int getNumberOfCompletedInstances() {\n        return numberOfCompletedInstances;\n    }\n\n    /**\n     * @return the numberOfTerminatedInstances\n     */\n    @Override\n    public int getNumberOfTerminatedInstances() {\n        return numberOfTerminatedInstances;\n    }\n\n    /**\n     * @return the loopCardinality\n     */\n    @Override\n    public int getLoopCardinality() {\n        return loopCardinality;\n    }\n\n    @Override\n    public FlowNodeType getType() {\n        return FlowNodeType.MULTI_INSTANCE_ACTIVITY;\n    }\n\n    @Override\n    public int getNumberOfInstances() {\n        return numberOfActiveInstances + numberOfCompletedInstances + numberOfTerminatedInstances;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/ArchivedReceiveTaskInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport org.bonitasoft.engine.bpm.flownode.ArchivedReceiveTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\n\n/**\n * @author Julien Molinaro\n */\npublic class ArchivedReceiveTaskInstanceImpl extends ArchivedHumanTaskInstanceImpl\n        implements ArchivedReceiveTaskInstance {\n\n    private static final long serialVersionUID = 7671768841192077283L;\n\n    public ArchivedReceiveTaskInstanceImpl(final String name) {\n        super(name);\n    }\n\n    @Override\n    public FlowNodeType getType() {\n        return FlowNodeType.RECEIVE_TASK;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/ArchivedSendTaskInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport org.bonitasoft.engine.bpm.flownode.ArchivedSendTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\n\n/**\n * @author Baptiste Mesta\n */\npublic class ArchivedSendTaskInstanceImpl extends ArchivedHumanTaskInstanceImpl implements ArchivedSendTaskInstance {\n\n    private static final long serialVersionUID = 7671768841192077283L;\n\n    public ArchivedSendTaskInstanceImpl(final String name) {\n        super(name);\n    }\n\n    @Override\n    public FlowNodeType getType() {\n        return FlowNodeType.SEND_TASK;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/ArchivedSubProcessActivityInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport org.bonitasoft.engine.bpm.flownode.ArchivedSubProcessActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ArchivedSubProcessActivityInstanceImpl extends ArchivedActivityInstanceImpl\n        implements ArchivedSubProcessActivityInstance {\n\n    private static final long serialVersionUID = 3524292074421289601L;\n\n    private final boolean triggeredByEvent;\n\n    public ArchivedSubProcessActivityInstanceImpl(final String name, final boolean triggeredByEvent) {\n        super(name);\n        this.triggeredByEvent = triggeredByEvent;\n    }\n\n    @Override\n    public FlowNodeType getType() {\n        return FlowNodeType.SUB_PROCESS;\n    }\n\n    @Override\n    public boolean isTriggeredByEvent() {\n        return triggeredByEvent;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/ArchivedUserTaskInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport org.bonitasoft.engine.bpm.flownode.ArchivedUserTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\n\n/**\n * @author Julien Mege\n */\npublic class ArchivedUserTaskInstanceImpl extends ArchivedHumanTaskInstanceImpl implements ArchivedUserTaskInstance {\n\n    private static final long serialVersionUID = -5611907403212645478L;\n\n    public ArchivedUserTaskInstanceImpl(final String name) {\n        super(name);\n    }\n\n    @Override\n    public FlowNodeType getType() {\n        return FlowNodeType.USER_TASK;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/AutomaticTaskInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport org.bonitasoft.engine.bpm.flownode.AutomaticTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class AutomaticTaskInstanceImpl extends TaskInstanceImpl implements AutomaticTaskInstance {\n\n    private static final long serialVersionUID = 4187673605124017954L;\n\n    public AutomaticTaskInstanceImpl(final String name, final long flownodeDefinitionId) {\n        super(name, flownodeDefinitionId);\n    }\n\n    @Override\n    public FlowNodeType getType() {\n        return FlowNodeType.AUTOMATIC_TASK;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/BoundaryEventInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport org.bonitasoft.engine.bpm.flownode.BoundaryEventInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class BoundaryEventInstanceImpl extends CatchEventInstanceImpl implements BoundaryEventInstance {\n\n    private static final long serialVersionUID = 1371653839686575195L;\n\n    private String activityName;\n\n    private long activityInstanceId;\n\n    public BoundaryEventInstanceImpl(final String name, final long flownodeDefinitionId,\n            final long activityInstanceId) {\n        super(name, flownodeDefinitionId);\n        this.activityInstanceId = activityInstanceId;\n    }\n\n    @Override\n    public FlowNodeType getType() {\n        return FlowNodeType.BOUNDARY_EVENT;\n    }\n\n    @Override\n    public String getActivityName() {\n        return activityName;\n    }\n\n    public void setActivityName(final String activityName) {\n        this.activityName = activityName;\n    }\n\n    @Override\n    public long getActivityInstanceId() {\n        return activityInstanceId;\n    }\n\n    public void setActivityInstanceId(final long activityInstanceId) {\n        this.activityInstanceId = activityInstanceId;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = super.hashCode();\n        result = prime * result + (activityName == null ? 0 : activityName.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (!super.equals(obj)) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        final BoundaryEventInstanceImpl other = (BoundaryEventInstanceImpl) obj;\n        if (activityName == null) {\n            if (other.activityName != null) {\n                return false;\n            }\n        } else if (!activityName.equals(other.activityName)) {\n            return false;\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/CallActivityInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport org.bonitasoft.engine.bpm.flownode.CallActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class CallActivityInstanceImpl extends ActivityInstanceImpl implements CallActivityInstance {\n\n    private static final long serialVersionUID = -1289654446660321732L;\n\n    public CallActivityInstanceImpl(final String name, final long flownodeDefinitionId) {\n        super(name, flownodeDefinitionId);\n    }\n\n    @Override\n    public FlowNodeType getType() {\n        return FlowNodeType.CALL_ACTIVITY;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/CatchEventInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport org.bonitasoft.engine.bpm.flownode.CatchEventInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic abstract class CatchEventInstanceImpl extends EventInstanceImpl implements CatchEventInstance {\n\n    private static final long serialVersionUID = 86467318586829475L;\n\n    private boolean interrupting;\n\n    public CatchEventInstanceImpl(final String name, final long flownodeDefinitionId) {\n        super(name, flownodeDefinitionId);\n    }\n\n    @Override\n    public boolean isInterrupting() {\n        return interrupting;\n    }\n\n    public void setInterrupting(final boolean interrupting) {\n        this.interrupting = interrupting;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = super.hashCode();\n        result = prime * result + (interrupting ? 1231 : 1237);\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (!super.equals(obj)) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        final CatchEventInstanceImpl other = (CatchEventInstanceImpl) obj;\n        if (interrupting != other.interrupting) {\n            return false;\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/EndEventInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport org.bonitasoft.engine.bpm.flownode.EndEventInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class EndEventInstanceImpl extends ThrowEventInstanceImpl implements EndEventInstance {\n\n    private static final long serialVersionUID = 5709016495551185514L;\n\n    public EndEventInstanceImpl(final String name, final long flownodeDefinitionId) {\n        super(name, flownodeDefinitionId);\n    }\n\n    @Override\n    public FlowNodeType getType() {\n        return FlowNodeType.END_EVENT;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/EventInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport org.bonitasoft.engine.bpm.flownode.EventInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic abstract class EventInstanceImpl extends FlowNodeInstanceImpl implements EventInstance {\n\n    private static final long serialVersionUID = 8286176446126292113L;\n\n    public EventInstanceImpl(final String name, final long flownodeDefinitionId) {\n        super(name, flownodeDefinitionId);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/EventTriggerInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport java.util.Objects;\n\nimport org.bonitasoft.engine.bpm.flownode.EventTriggerInstance;\nimport org.bonitasoft.engine.bpm.internal.BaseDefinitionElementImpl;\n\n/**\n * @author Celine Souchet\n * @version 6.4.0\n * @since 6.4.0\n */\npublic class EventTriggerInstanceImpl extends BaseDefinitionElementImpl implements EventTriggerInstance {\n\n    private static final long serialVersionUID = 1894571490582208753L;\n\n    private long eventInstanceId;\n\n    public EventTriggerInstanceImpl(final long id, final long eventInstanceId) {\n        super();\n        setId(id);\n        this.eventInstanceId = eventInstanceId;\n    }\n\n    @Override\n    public long getEventInstanceId() {\n        return this.eventInstanceId;\n    }\n\n    protected void setEventInstanceId(final long eventInstanceId) {\n        this.eventInstanceId = eventInstanceId;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (o == null || getClass() != o.getClass())\n            return false;\n        if (!super.equals(o))\n            return false;\n        EventTriggerInstanceImpl that = (EventTriggerInstanceImpl) o;\n        return Objects.equals(eventInstanceId, that.eventInstanceId);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(super.hashCode(), eventInstanceId);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/FlowElementInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport org.bonitasoft.engine.bpm.flownode.FlowElementInstance;\nimport org.bonitasoft.engine.bpm.internal.NamedElementImpl;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic abstract class FlowElementInstanceImpl extends NamedElementImpl implements FlowElementInstance {\n\n    private static final long serialVersionUID = -8382446613679794971L;\n\n    private long parentContainerId;\n\n    private long rootContainerId;\n\n    private boolean aborting;\n\n    public FlowElementInstanceImpl(final FlowElementInstance flowElementInstance) {\n        super(flowElementInstance.getName());\n        rootContainerId = flowElementInstance.getRootContainerId();\n        parentContainerId = flowElementInstance.getRootContainerId();\n    }\n\n    @Override\n    public long getRootContainerId() {\n        return rootContainerId;\n    }\n\n    public void setRootContainerId(final long rootContainerId) {\n        this.rootContainerId = rootContainerId;\n    }\n\n    @Override\n    public long getParentContainerId() {\n        return parentContainerId;\n    }\n\n    public void setParentContainerId(final long parentContainerId) {\n        this.parentContainerId = parentContainerId;\n    }\n\n    @Override\n    public boolean isAborting() {\n        return aborting;\n    }\n\n    public void setAborting(final boolean aborting) {\n        this.aborting = aborting;\n    }\n\n    @Override\n    public String toString() {\n        final StringBuilder stb = new StringBuilder(super.toString());\n        stb.append(\"parentContainerId: \");\n        stb.append(parentContainerId);\n        stb.append(\"\\n\");\n        stb.append(\"rootContainerId: \");\n        stb.append(rootContainerId);\n        stb.append(\"\\n\");\n        stb.append(\"aborting: \");\n        stb.append(aborting);\n        stb.append(\"\\n\");\n        return stb.toString();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/FlowNodeInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport java.util.Date;\nimport java.util.Objects;\n\nimport lombok.ToString;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstance;\nimport org.bonitasoft.engine.bpm.flownode.StateCategory;\nimport org.bonitasoft.engine.bpm.internal.NamedElementImpl;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\n@ToString(callSuper = true)\npublic abstract class FlowNodeInstanceImpl extends NamedElementImpl implements FlowNodeInstance {\n\n    private static final long serialVersionUID = -6573747806944970703L;\n\n    private long parentContainerId;\n\n    private String state;\n\n    private StateCategory stateCategory;\n\n    private long rootContainerId;\n\n    private long processDefinitionId;\n\n    private long parentProcessInstanceId;\n\n    private String displayDescription;\n\n    private String displayName;\n\n    private String description;\n\n    private long executedBy;\n\n    private long executedBySubstitute;\n\n    private long flownodeDefinitionId;\n\n    private Date reachedStateDate;\n\n    private Date lastUpdateDate;\n\n    public FlowNodeInstanceImpl(final String name, final long flownodeDefinitionId) {\n        super(name);\n\n        this.flownodeDefinitionId = flownodeDefinitionId;\n    }\n\n    @Override\n    public long getParentContainerId() {\n        return parentContainerId;\n    }\n\n    public void setParentContainerId(long parentContainerId) {\n        this.parentContainerId = parentContainerId;\n    }\n\n    @Override\n    public String getState() {\n        return state;\n    }\n\n    public void setState(String state) {\n        this.state = state;\n    }\n\n    @Override\n    public StateCategory getStateCategory() {\n        return stateCategory;\n    }\n\n    public void setStateCategory(StateCategory stateCategory) {\n        this.stateCategory = stateCategory;\n    }\n\n    @Override\n    public long getRootContainerId() {\n        return rootContainerId;\n    }\n\n    public void setRootContainerId(long rootContainerId) {\n        this.rootContainerId = rootContainerId;\n    }\n\n    @Override\n    public long getProcessDefinitionId() {\n        return processDefinitionId;\n    }\n\n    public void setProcessDefinitionId(long processDefinitionId) {\n        this.processDefinitionId = processDefinitionId;\n    }\n\n    @Override\n    public long getParentProcessInstanceId() {\n        return parentProcessInstanceId;\n    }\n\n    public void setParentProcessInstanceId(long parentProcessInstanceId) {\n        this.parentProcessInstanceId = parentProcessInstanceId;\n    }\n\n    @Override\n    public String getDisplayDescription() {\n        return displayDescription;\n    }\n\n    public void setDisplayDescription(String displayDescription) {\n        this.displayDescription = displayDescription;\n    }\n\n    @Override\n    public String getDisplayName() {\n        return displayName;\n    }\n\n    public void setDisplayName(String displayName) {\n        this.displayName = displayName;\n    }\n\n    @Override\n    public String getDescription() {\n        return description;\n    }\n\n    public void setDescription(String description) {\n        this.description = description;\n    }\n\n    @Override\n    public long getExecutedBy() {\n        return executedBy;\n    }\n\n    public void setExecutedBy(long executedBy) {\n        this.executedBy = executedBy;\n    }\n\n    @Override\n    public long getExecutedBySubstitute() {\n        return executedBySubstitute;\n    }\n\n    public void setExecutedBySubstitute(long executedBySubstitute) {\n        this.executedBySubstitute = executedBySubstitute;\n    }\n\n    @Override\n    public long getFlownodeDefinitionId() {\n        return flownodeDefinitionId;\n    }\n\n    public void setFlownodeDefinitionId(long flownodeDefinitionId) {\n        this.flownodeDefinitionId = flownodeDefinitionId;\n    }\n\n    public Date getReachedStateDate() {\n        return reachedStateDate;\n    }\n\n    public void setReachedSateDate(final Date reachedStateDate) {\n        this.reachedStateDate = reachedStateDate;\n    }\n\n    @Override\n    public Date getLastUpdateDate() {\n        return lastUpdateDate;\n    }\n\n    public void setLastUpdateDate(final Date lastUpdateDate) {\n        this.lastUpdateDate = lastUpdateDate;\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(super.hashCode(), parentContainerId, state, stateCategory, rootContainerId,\n                processDefinitionId, parentProcessInstanceId,\n                displayDescription, displayName, description, executedBy, executedBySubstitute, flownodeDefinitionId,\n                reachedStateDate, lastUpdateDate);\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (!(o instanceof FlowNodeInstanceImpl that))\n            return false;\n        if (!super.equals(o))\n            return false;\n        return Objects.equals(parentContainerId, that.parentContainerId) &&\n                Objects.equals(rootContainerId, that.rootContainerId) &&\n                Objects.equals(processDefinitionId, that.processDefinitionId) &&\n                Objects.equals(parentProcessInstanceId, that.parentProcessInstanceId) &&\n                Objects.equals(executedBy, that.executedBy) &&\n                Objects.equals(executedBySubstitute, that.executedBySubstitute) &&\n                Objects.equals(flownodeDefinitionId, that.flownodeDefinitionId) &&\n                Objects.equals(state, that.state) &&\n                Objects.equals(stateCategory, that.stateCategory) &&\n                Objects.equals(displayDescription, that.displayDescription) &&\n                Objects.equals(displayName, that.displayName) &&\n                Objects.equals(description, that.description) &&\n                Objects.equals(reachedStateDate, that.reachedStateDate) &&\n                Objects.equals(lastUpdateDate, that.lastUpdateDate);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/GatewayInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport lombok.ToString;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\nimport org.bonitasoft.engine.bpm.flownode.GatewayInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\n@ToString(callSuper = true)\npublic class GatewayInstanceImpl extends FlowNodeInstanceImpl implements GatewayInstance {\n\n    private static final long serialVersionUID = 8722950382913966260L;\n\n    public GatewayInstanceImpl(final String name, final long flownodeDefinitionId) {\n        super(name, flownodeDefinitionId);\n    }\n\n    @Override\n    public FlowNodeType getType() {\n        return FlowNodeType.GATEWAY;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/HumanTaskInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport java.util.Date;\n\nimport lombok.ToString;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.TaskPriority;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\n@ToString(callSuper = true)\npublic abstract class HumanTaskInstanceImpl extends TaskInstanceImpl implements HumanTaskInstance {\n\n    private static final long serialVersionUID = 5988594900787242204L;\n\n    private final long actorId;\n\n    private long assigneeId;\n\n    private TaskPriority priority;\n\n    private Date expectedEndDate;\n\n    private Date claimedDate;\n\n    public HumanTaskInstanceImpl(final String name, final long flownodeDefinitionId, final long actorId) {\n        super(name, flownodeDefinitionId);\n        this.actorId = actorId;\n    }\n\n    public void setAssigneeId(final long assigneeId) {\n        this.assigneeId = assigneeId;\n    }\n\n    @Override\n    public long getActorId() {\n        return actorId;\n    }\n\n    @Override\n    public long getAssigneeId() {\n        return assigneeId;\n    }\n\n    @Override\n    public TaskPriority getPriority() {\n        return priority;\n    }\n\n    @Override\n    public Date getExpectedEndDate() {\n        return expectedEndDate;\n    }\n\n    @Override\n    public Date getClaimedDate() {\n        return claimedDate;\n    }\n\n    public void setPriority(final TaskPriority priority) {\n        this.priority = priority;\n    }\n\n    public void setExpectedEndDate(final Date expectedEndDate) {\n        this.expectedEndDate = expectedEndDate;\n    }\n\n    public void setClaimedDate(final Date claimedDate) {\n        this.claimedDate = claimedDate;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/IntermediateCatchEventInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\nimport org.bonitasoft.engine.bpm.flownode.IntermediateCatchEventInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class IntermediateCatchEventInstanceImpl extends CatchEventInstanceImpl\n        implements IntermediateCatchEventInstance {\n\n    private static final long serialVersionUID = -1163487409085885481L;\n\n    public IntermediateCatchEventInstanceImpl(final String name, final long flownodeDefinitionId) {\n        super(name, flownodeDefinitionId);\n    }\n\n    @Override\n    public FlowNodeType getType() {\n        return FlowNodeType.INTERMEDIATE_CATCH_EVENT;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/IntermediateThrowEventInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\nimport org.bonitasoft.engine.bpm.flownode.IntermediateThrowEventInstance;\n\n/**\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class IntermediateThrowEventInstanceImpl extends ThrowEventInstanceImpl\n        implements IntermediateThrowEventInstance {\n\n    private static final long serialVersionUID = -3256549925414354772L;\n\n    public IntermediateThrowEventInstanceImpl(final String name, final long flownodeDefinitionId) {\n        super(name, flownodeDefinitionId);\n    }\n\n    @Override\n    public FlowNodeType getType() {\n        return FlowNodeType.INTERMEDIATE_THROW_EVENT;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/LoopActivityInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\nimport org.bonitasoft.engine.bpm.flownode.LoopActivityInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class LoopActivityInstanceImpl extends ActivityInstanceImpl implements LoopActivityInstance {\n\n    private static final long serialVersionUID = -1901009347639323157L;\n\n    private final int loopCounter;\n\n    public LoopActivityInstanceImpl(final String name, final long flownodeDefinitionId, final int loopCounter) {\n        super(name, flownodeDefinitionId);\n        this.loopCounter = loopCounter;\n    }\n\n    @Override\n    public FlowNodeType getType() {\n        return FlowNodeType.LOOP_ACTIVITY;\n    }\n\n    @Override\n    public int getLoopCounter() {\n        return loopCounter;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/ManualTaskInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\nimport org.bonitasoft.engine.bpm.flownode.ManualTaskInstance;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class ManualTaskInstanceImpl extends HumanTaskInstanceImpl implements ManualTaskInstance {\n\n    private static final long serialVersionUID = -1791657324743303807L;\n\n    public ManualTaskInstanceImpl(final String name, final long flownodeDefinitionId, final long actorId) {\n        super(name, flownodeDefinitionId, actorId);\n    }\n\n    @Override\n    public FlowNodeType getType() {\n        return FlowNodeType.MANUAL_TASK;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/MultiInstanceActivityInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\nimport org.bonitasoft.engine.bpm.flownode.MultiInstanceActivityInstance;\n\n/**\n * @author Baptiste Mesta\n */\npublic class MultiInstanceActivityInstanceImpl extends ActivityInstanceImpl implements MultiInstanceActivityInstance {\n\n    private static final long serialVersionUID = -5723988334123367841L;\n\n    private final boolean sequential;\n\n    private final String loopDataInputRef;\n\n    private final String loopDataOutputRef;\n\n    private final String dataInputItemRef;\n\n    private final String dataOutputItemRef;\n\n    private final int numberOfActiveInstances;\n\n    private final int numberOfCompletedInstances;\n\n    private final int numberOfTerminatedInstances;\n\n    private final int loopCardinality;\n\n    public MultiInstanceActivityInstanceImpl(final String name, final long flownodeDefinitionId,\n            final boolean sequential, final String loopDataInputRef,\n            final String loopDataOutputRef, final String dataInputItemRef, final String dataOutputItemRef,\n            final int numberOfActiveInstances,\n            final int numberOfCompletedInstances, final int numberOfTerminatedInstances, final int loopCardinality) {\n        super(name, flownodeDefinitionId);\n        this.sequential = sequential;\n        this.loopDataInputRef = loopDataInputRef;\n        this.loopDataOutputRef = loopDataOutputRef;\n        this.dataInputItemRef = dataInputItemRef;\n        this.dataOutputItemRef = dataOutputItemRef;\n        this.numberOfActiveInstances = numberOfActiveInstances;\n        this.numberOfCompletedInstances = numberOfCompletedInstances;\n        this.numberOfTerminatedInstances = numberOfTerminatedInstances;\n        this.loopCardinality = loopCardinality;\n    }\n\n    /**\n     * @return the sequential\n     */\n    @Override\n    public boolean isSequential() {\n        return sequential;\n    }\n\n    /**\n     * @return the loopDataInputRef\n     */\n    @Override\n    public String getLoopDataInputRef() {\n        return loopDataInputRef;\n    }\n\n    /**\n     * @return the loopDataOutputRef\n     */\n    @Override\n    public String getLoopDataOutputRef() {\n        return loopDataOutputRef;\n    }\n\n    /**\n     * @return the dataInputItemRef\n     */\n    @Override\n    public String getDataInputItemRef() {\n        return dataInputItemRef;\n    }\n\n    /**\n     * @return the dataOutputItemRef\n     */\n    @Override\n    public String getDataOutputItemRef() {\n        return dataOutputItemRef;\n    }\n\n    /**\n     * @return the numberOfActiveInstances\n     */\n    @Override\n    public int getNumberOfActiveInstances() {\n        return numberOfActiveInstances;\n    }\n\n    /**\n     * @return the numberOfCompletedInstances\n     */\n    @Override\n    public int getNumberOfCompletedInstances() {\n        return numberOfCompletedInstances;\n    }\n\n    /**\n     * @return the numberOfTerminatedInstances\n     */\n    @Override\n    public int getNumberOfTerminatedInstances() {\n        return numberOfTerminatedInstances;\n    }\n\n    /**\n     * @return the loopCardinality\n     */\n    @Override\n    public int getLoopCardinality() {\n        return loopCardinality;\n    }\n\n    @Override\n    public FlowNodeType getType() {\n        return FlowNodeType.MULTI_INSTANCE_ACTIVITY;\n    }\n\n    @Override\n    public int getNumberOfInstances() {\n        return numberOfActiveInstances + numberOfCompletedInstances + numberOfTerminatedInstances;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/ReceiveTaskInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\nimport org.bonitasoft.engine.bpm.flownode.ReceiveTaskInstance;\n\n/**\n * @author Julien Molinaro\n */\npublic class ReceiveTaskInstanceImpl extends TaskInstanceImpl implements ReceiveTaskInstance {\n\n    private static final long serialVersionUID = 4187673605124017954L;\n\n    public ReceiveTaskInstanceImpl(final String name, final long flownodeDefinitionId) {\n        super(name, flownodeDefinitionId);\n    }\n\n    @Override\n    public FlowNodeType getType() {\n        return FlowNodeType.RECEIVE_TASK;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/SendTaskInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\nimport org.bonitasoft.engine.bpm.flownode.SendTaskInstance;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SendTaskInstanceImpl extends TaskInstanceImpl implements SendTaskInstance {\n\n    private static final long serialVersionUID = -3792840569021028433L;\n\n    public SendTaskInstanceImpl(final String name, final long flownodeDefinitionId) {\n        super(name, flownodeDefinitionId);\n    }\n\n    @Override\n    public FlowNodeType getType() {\n        return FlowNodeType.SEND_TASK;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/StartEventInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\nimport org.bonitasoft.engine.bpm.flownode.StartEventInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class StartEventInstanceImpl extends CatchEventInstanceImpl implements StartEventInstance {\n\n    private static final long serialVersionUID = 1162574672955389640L;\n\n    public StartEventInstanceImpl(final String name, final long flownodeDefinitionId) {\n        super(name, flownodeDefinitionId);\n    }\n\n    @Override\n    public FlowNodeType getType() {\n        return FlowNodeType.START_EVENT;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/SubProcessActivityInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\nimport org.bonitasoft.engine.bpm.flownode.SubProcessActivityInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class SubProcessActivityInstanceImpl extends ActivityInstanceImpl implements SubProcessActivityInstance {\n\n    private static final long serialVersionUID = 2652709303320068129L;\n\n    private final boolean triggeredByEvent;\n\n    public SubProcessActivityInstanceImpl(final String name, final long flownodeDefinitionId,\n            final boolean triggeredByEvent) {\n        super(name, flownodeDefinitionId);\n        this.triggeredByEvent = triggeredByEvent;\n    }\n\n    @Override\n    public FlowNodeType getType() {\n        return FlowNodeType.SUB_PROCESS;\n    }\n\n    @Override\n    public boolean isTriggeredByEvent() {\n        return triggeredByEvent;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/TaskInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport org.bonitasoft.engine.bpm.flownode.TaskInstance;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic abstract class TaskInstanceImpl extends ActivityInstanceImpl implements TaskInstance {\n\n    private static final long serialVersionUID = -6250330349559993047L;\n\n    public TaskInstanceImpl(final String name, final long flownodeDefinitionId) {\n        super(name, flownodeDefinitionId);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/ThrowEventInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic abstract class ThrowEventInstanceImpl extends EventInstanceImpl {\n\n    private static final long serialVersionUID = 2454133141873552710L;\n\n    public ThrowEventInstanceImpl(final String name, final long flownodeDefinitionId) {\n        super(name, flownodeDefinitionId);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/TimerEventTriggerInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.bpm.flownode.TimerEventTriggerInstance;\n\n/**\n * @author Celine Souchet\n * @version 6.4.0\n * @since 6.4.0\n */\npublic class TimerEventTriggerInstanceImpl extends EventTriggerInstanceImpl implements TimerEventTriggerInstance {\n\n    private static final long serialVersionUID = -1000995843357026775L;\n\n    private Date executionDate;\n\n    private final String eventInstanceName;\n\n    public TimerEventTriggerInstanceImpl(final long id, final long eventInstanceId, final String eventInstanceName,\n            final Date executionDate) {\n        super(id, eventInstanceId);\n        this.eventInstanceName = eventInstanceName;\n        setExecutionDate(executionDate);\n    }\n\n    @Override\n    public Date getExecutionDate() {\n        return executionDate;\n    }\n\n    public void setExecutionDate(final Date executionDate) {\n        this.executionDate = executionDate;\n    }\n\n    @Override\n    public String getEventInstanceName() {\n        return eventInstanceName;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = super.hashCode();\n        result = prime * result + (eventInstanceName == null ? 0 : eventInstanceName.hashCode());\n        result = prime * result + (executionDate == null ? 0 : executionDate.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (!super.equals(obj)) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        final TimerEventTriggerInstanceImpl other = (TimerEventTriggerInstanceImpl) obj;\n        if (eventInstanceName == null) {\n            if (other.eventInstanceName != null) {\n                return false;\n            }\n        } else if (!eventInstanceName.equals(other.eventInstanceName)) {\n            return false;\n        }\n        if (executionDate == null) {\n            if (other.executionDate != null) {\n                return false;\n            }\n        } else if (!executionDate.equals(other.executionDate)) {\n            return false;\n        }\n        return true;\n    }\n\n    @Override\n    public String toString() {\n        final StringBuilder builder = new StringBuilder();\n        builder.append(\"TimerEventTriggerInstanceImpl [id=\").append(getId()).append(\", executionDate=\")\n                .append(executionDate).append(\", eventInstanceName=\")\n                .append(eventInstanceName).append(\", eventInstanceId=\").append(getEventInstanceId()).append(\"]\");\n        return builder.toString();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/UserTaskInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport lombok.ToString;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\nimport org.bonitasoft.engine.bpm.flownode.UserTaskInstance;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\n@ToString(callSuper = true)\npublic class UserTaskInstanceImpl extends HumanTaskInstanceImpl implements UserTaskInstance {\n\n    private static final long serialVersionUID = -1791657324743303807L;\n\n    /**\n     * @param name\n     * @param flownodeDefinitionId\n     * @param actorId\n     */\n    public UserTaskInstanceImpl(final String name, final long flownodeDefinitionId, final long actorId) {\n        super(name, flownodeDefinitionId, actorId);\n    }\n\n    @Override\n    public FlowNodeType getType() {\n        return FlowNodeType.USER_TASK;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/WaitingErrorEventImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport lombok.ToString;\nimport org.bonitasoft.engine.bpm.flownode.BPMEventType;\nimport org.bonitasoft.engine.bpm.flownode.WaitingErrorEvent;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\n@ToString(callSuper = true)\npublic class WaitingErrorEventImpl extends WaitingEventImpl implements WaitingErrorEvent {\n\n    private static final long serialVersionUID = 2453504392749981399L;\n\n    private String errorCode;\n\n    public WaitingErrorEventImpl() {\n    }\n\n    public WaitingErrorEventImpl(final BPMEventType eventType, final long processdefinitionId, final String processName,\n            final long flowNodeDefinitionId,\n            final String errorCode) {\n        super(eventType, processdefinitionId, processName, flowNodeDefinitionId);\n        this.errorCode = errorCode;\n    }\n\n    @Override\n    public String getErrorCode() {\n        return errorCode;\n    }\n\n    public void setErrorCode(final String errorCode) {\n        this.errorCode = errorCode;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/WaitingEventImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport lombok.ToString;\nimport org.bonitasoft.engine.bpm.flownode.BPMEventType;\nimport org.bonitasoft.engine.bpm.flownode.WaitingEvent;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\n@ToString\npublic abstract class WaitingEventImpl implements WaitingEvent {\n\n    private static final long serialVersionUID = -4479053804299166959L;\n\n    private BPMEventType eventType;\n\n    private String processName;\n\n    private long flowNodeDefinitionId = -1;\n\n    private long processDefinitionId;\n\n    private long rootProcessInstanceId = -1;\n\n    private long parentProcessInstanceId = -1;\n\n    private long flowNodeInstanceId = -1;\n\n    private boolean active = true;\n\n    public WaitingEventImpl() {\n    }\n\n    public WaitingEventImpl(final BPMEventType eventType, final long processDefinitionId, final String processName,\n            final long flowNodeDefinitionId) {\n        this.eventType = eventType;\n        this.processName = processName;\n        this.flowNodeDefinitionId = flowNodeDefinitionId;\n        this.processDefinitionId = processDefinitionId;\n    }\n\n    public WaitingEventImpl(final BPMEventType eventType) {\n        this.eventType = eventType;\n    }\n\n    @Override\n    public BPMEventType getEventType() {\n        return eventType;\n    }\n\n    public void setEventType(final BPMEventType eventType) {\n        this.eventType = eventType;\n    }\n\n    @Override\n    public String getProcessName() {\n        return processName;\n    }\n\n    @Override\n    public long getFlowNodeDefinitionId() {\n        return flowNodeDefinitionId;\n    }\n\n    public void setProcessName(final String processName) {\n        this.processName = processName;\n    }\n\n    public void setFlowNodeDefinitionId(final long flowNodeDefinitionId) {\n        this.flowNodeDefinitionId = flowNodeDefinitionId;\n    }\n\n    @Override\n    public long getProcessDefinitionId() {\n        return processDefinitionId;\n    }\n\n    public void setProcessDefinitionId(final long processDefinitionId) {\n        this.processDefinitionId = processDefinitionId;\n    }\n\n    @Override\n    public long getRootProcessInstanceId() {\n        return rootProcessInstanceId;\n    }\n\n    public void setRootProcessInstanceId(final long processInstanceId) {\n        rootProcessInstanceId = processInstanceId;\n    }\n\n    @Override\n    public long getParentProcessInstanceId() {\n        return parentProcessInstanceId;\n    }\n\n    public void setParentProcessInstanceId(final long parentProcessInstanceId) {\n        this.parentProcessInstanceId = parentProcessInstanceId;\n    }\n\n    @Override\n    public long getFlowNodeInstanceId() {\n        return flowNodeInstanceId;\n    }\n\n    public void setFlowNodeInstanceId(final long flowNodeInstanceId) {\n        this.flowNodeInstanceId = flowNodeInstanceId;\n    }\n\n    @Override\n    public boolean isActive() {\n        return active;\n    }\n\n    public void setActive(final boolean active) {\n        this.active = active;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/WaitingMessageEventImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport lombok.ToString;\nimport org.bonitasoft.engine.bpm.flownode.BPMEventType;\nimport org.bonitasoft.engine.bpm.flownode.WaitingMessageEvent;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\n@ToString(callSuper = true)\npublic class WaitingMessageEventImpl extends WaitingEventImpl implements WaitingMessageEvent {\n\n    private static final long serialVersionUID = 6036375135350657130L;\n\n    private String messageName;\n\n    private boolean locked = false;\n\n    public WaitingMessageEventImpl() {\n    }\n\n    public WaitingMessageEventImpl(final BPMEventType eventType, final long processdefinitionId,\n            final String processName, final long flowNodeDefinitionId,\n            final String messageName) {\n        super(eventType, processdefinitionId, processName, flowNodeDefinitionId);\n        this.messageName = messageName;\n    }\n\n    @Override\n    public String getMessageName() {\n        return messageName;\n    }\n\n    public void setMessageName(final String messageName) {\n        this.messageName = messageName;\n    }\n\n    @Override\n    public boolean isLocked() {\n        return locked;\n    }\n\n    public void setLocked(final boolean locked) {\n        this.locked = locked;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/WaitingSignalEventImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.flownode.impl.internal;\n\nimport lombok.ToString;\nimport org.bonitasoft.engine.bpm.flownode.BPMEventType;\nimport org.bonitasoft.engine.bpm.flownode.WaitingSignalEvent;\n\n/**\n * @author Zhao Na\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\n@ToString(callSuper = true)\npublic class WaitingSignalEventImpl extends WaitingEventImpl implements WaitingSignalEvent {\n\n    private static final long serialVersionUID = 6899269154378362388L;\n\n    private String signalName;\n\n    public WaitingSignalEventImpl() {\n        super();\n    }\n\n    public WaitingSignalEventImpl(final BPMEventType eventType, final long processdefinitionId,\n            final String processName, final long flowNodeDefinitionId,\n            final String signalName) {\n        super(eventType, processdefinitionId, processName, flowNodeDefinitionId);\n        this.signalName = signalName;\n    }\n\n    @Override\n    public String getSignalName() {\n        return signalName;\n    }\n\n    public void setSignalName(final String signalName) {\n        this.signalName = signalName;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/parameter/ParameterCriterion.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.parameter;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic enum ParameterCriterion {\n\n    NAME_ASC, NAME_DESC;\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/parameter/ParameterInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.parameter;\n\nimport org.bonitasoft.engine.bpm.DescriptionElement;\n\npublic interface ParameterInstance extends DescriptionElement {\n\n    Object getValue();\n\n    String getType();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/parameter/impl/ParameterImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.parameter.impl;\n\nimport org.bonitasoft.engine.bpm.internal.NamedElementImpl;\nimport org.bonitasoft.engine.bpm.parameter.ParameterInstance;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class ParameterImpl extends NamedElementImpl implements ParameterInstance {\n\n    private static final long serialVersionUID = 4096607590317516470L;\n\n    private final String description;\n\n    private final Object value;\n\n    private final String type;\n\n    public ParameterImpl(final String name, final String description, final Object value, final String type) {\n        super(name);\n        this.description = description;\n        this.value = value;\n        this.type = type;\n    }\n\n    @Override\n    public String getDescription() {\n        return description;\n    }\n\n    @Override\n    public Object getValue() {\n        return value;\n    }\n\n    @Override\n    public String getType() {\n        return type;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ActivationState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.process;\n\n/**\n * <p>Activation state of a process. <br>\n * A {@link ProcessDefinition} can be enabled or disabled, which toggles on/off the possibility to start a new instance\n * of the process.</p>\n * <p>Use {@link ProcessDeploymentInfo#getActivationState()} to retrieve the activation state for a process.</p>\n *\n * @see ProcessDeploymentInfo#getActivationState()\n * @author Celine Souchet\n * @author Emmanuel Duchastenier\n * @version 6.3.5\n * @since 6.0.0\n */\npublic enum ActivationState {\n\n    /**\n     * The {@link ProcessDeploymentInfo} is enabled and instances of the process can be started.\n     */\n    ENABLED,\n\n    /**\n     * The {@link ProcessDeploymentInfo} is disabled and no instance can be started.\n     */\n    DISABLED\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ArchivedProcessInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.process;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.bpm.ArchivedElement;\nimport org.bonitasoft.engine.bpm.BaseElement;\nimport org.bonitasoft.engine.bpm.NamedElement;\n\n/**\n * Represents an archived instance of a process.\n * Gives access to the information of the instance, whereas the state, the definition, the archived date of the process\n * instance...\n *\n * @author Baptiste Mesta\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @version 6.3.5\n * @since 6.0.0\n */\npublic interface ArchivedProcessInstance extends NamedElement, BaseElement, ArchivedElement {\n\n    /**\n     * Get the state of this process instance when it was archived.\n     *\n     * @return The state of this process instance when it was archived.\n     * @see ProcessInstanceState\n     * @since 6.0.0\n     */\n    String getState();\n\n    /**\n     * Get the identifier of the state of this process instance when it was archived.\n     *\n     * @return The identifier of the state of this process instance when it was archived.\n     * @see ProcessInstanceState\n     * @since 6.0.0\n     */\n    int getStateId();\n\n    /**\n     * Get the date when this process instance was started.\n     *\n     * @return The date when this process instance was started.\n     * @since 6.0.0\n     */\n    Date getStartDate();\n\n    /**\n     * Get the identifier of the user who started this process instance.\n     *\n     * @return The identifier of the user who started this process instance.\n     * @since 6.0.0\n     */\n    long getStartedBy();\n\n    /**\n     * Get the identifier of the substitute user (as Process manager or Administrator) who started this process\n     * instance.\n     *\n     * @return The identifier of the substitute user (as Process manager or Administrator) who started this process\n     *         instance.\n     * @since 6.3.0\n     */\n    long getStartedBySubstitute();\n\n    /**\n     * Get the date when this process instance was finished.\n     * It equals to null, if this process instance is not finished.\n     *\n     * @return The date when this process instance was finished.\n     * @since 6.0.0\n     */\n    Date getEndDate();\n\n    /**\n     * Get the date of the last update of this process instance, when the process instance was archived.\n     *\n     * @return The date of the last update of this process instance, when the process instance was archived.\n     * @since 6.0.0\n     */\n    Date getLastUpdate();\n\n    /**\n     * Get the identifier of the definition of this process.\n     *\n     * @return The identifier of the definition of this process.\n     * @see ProcessDefinition#getId()\n     * @since 6.0.0\n     */\n    long getProcessDefinitionId();\n\n    /**\n     * Get the description of this process instance, when the process instance was archived.\n     *\n     * @return The description of this process instance, when the process instance was archived.\n     * @since 6.0.0\n     */\n    String getDescription();\n\n    /**\n     * Get the identifier of the root {@link ProcessInstance} of this process instance.\n     * Is -1 if this process instance is not a child of another process instance.\n     *\n     * @return The identifier of the root {@link ProcessInstance} of this process instance.\n     * @see ProcessInstance#getId()\n     * @see ArchivedProcessInstance#getSourceObjectId()\n     * @since 6.0.0\n     */\n    long getRootProcessInstanceId();\n\n    /**\n     * Get the identifier of the flow node instance who starts this process instance.\n     * Is -1 if this process instance is not a child of another process instance.\n     *\n     * @return The identifier of the flow node instance who starts this process instance.\n     * @see org.bonitasoft.engine.bpm.flownode.CallActivityInstance#getId()\n     * @see SubProcessDefinition#getId()\n     * @since 6.0.0\n     */\n    long getCallerId();\n\n    /**\n     * The index must be between 1 and 5.\n     *\n     * @param index\n     *        The index of the value\n     * @return The value of the search key corresponding to the parameter\n     * @exception IndexOutOfBoundsException\n     *            It's thrown if the parameter is not between 1 and 5.\n     * @since 6.4.0\n     */\n    String getStringIndexValue(int index);\n\n    /**\n     * The index must be between 1 and 5.\n     *\n     * @param index\n     *        The index of the label\n     * @return The label of the search key corresponding to the parameter\n     * @exception IndexOutOfBoundsException\n     *            It's thrown if the parameter is not between 1 and 5.\n     * @since 6.4.0\n     */\n    String getStringIndexLabel(int index);\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ArchivedProcessInstanceNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.process;\n\nimport org.bonitasoft.engine.exception.NotFoundException;\n\n/**\n * <p>Thrown when it's not possible to find the archived process instance.</p>\n * <p>The class ArchivedProcessInstanceNotFoundException is a form of Throwable that indicates conditions that a\n * reasonable application might want to catch.<br>\n * The class ArchivedProcessInstanceNotFoundException that is not also subclasses of {@link RuntimeException} are\n * checked exceptions.<br>\n * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by\n * the execution of the method or\n * constructor and propagate outside the method or constructor boundary.</p>\n *\n * @author Zhao Na\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @version 6.3.5\n * @since 6.0.0\n */\npublic class ArchivedProcessInstanceNotFoundException extends NotFoundException {\n\n    private static final long serialVersionUID = 8745379570098982138L;\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public ArchivedProcessInstanceNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n    /**\n     * Constructs a new exception and its message\n     *\n     * @param archivedProcessInstanceId\n     *        The identifier of the archived process instance not found\n     */\n    public ArchivedProcessInstanceNotFoundException(final long archivedProcessInstanceId) {\n        super(\"Archived process instance with id <\" + archivedProcessInstanceId + \"> not found\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ArchivedProcessInstancesSearchDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.process;\n\n/**\n * Search descriptors are used to filter / sort results of a generic search. <br>\n * ArchivedProcessInstancesSearchDescriptor defines the fields that can be used as filters or sort fields on\n * <code>List&lt;ArchivedProcessInstance&gt;</code> returning methods.\n *\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @see org.bonitasoft.engine.api.ProcessRuntimeAPI\n * @see ArchivedProcessInstance\n * @version 6.3.5\n * @since 6.0.0\n */\npublic class ArchivedProcessInstancesSearchDescriptor {\n\n    /**\n     * The field corresponding to the date of the archiving of the process instance.\n     */\n    public static final String ARCHIVE_DATE = \"archiveDate\";\n\n    /**\n     * The field corresponding to the identifier of the archived process instance.\n     */\n    public static final String ID = \"id\";\n\n    /**\n     * The field corresponding to the name of the process.\n     */\n    public static final String NAME = \"name\";\n\n    /**\n     * The field corresponding to the identifier of the process definition.\n     */\n    public static final String PROCESS_DEFINITION_ID = \"processDefinitionId\";\n\n    /**\n     * The field corresponding to the identifier of the root process instance.\n     */\n    public static final String ROOT_PROCESS_INSTANCE_ID = \"rootProcessInstanceId\";\n\n    /**\n     * The field corresponding to the identifier of the state of the archived process instance.\n     */\n    public static final String STATE_ID = \"stateId\";\n\n    /**\n     * The field corresponding to the identifier of the running process instance.\n     */\n    public static final String SOURCE_OBJECT_ID = \"sourceObjectId\";\n\n    /**\n     * The field corresponding to the date of the end of the process instance.\n     */\n    public static final String END_DATE = \"endDate\";\n\n    /**\n     * The field corresponding to the identifier of the user who started the process instance.\n     */\n    public static final String STARTED_BY = \"startedBy\";\n\n    /**\n     * The field corresponding to the identifier of the user who started the process instance for the user in\n     * {@link ProcessInstance#getStartedBy()}.\n     */\n    public static final String STARTED_BY_SUBSTITUTE = \"startedBySubstitute\";\n\n    /**\n     * The field corresponding to the date when the process instance is started.\n     */\n    public static final String START_DATE = \"startDate\";\n\n    /**\n     * The field corresponding to the last date of the updating of the process instance.\n     */\n    public static final String LAST_UPDATE = \"lastUpdate\";\n\n    /**\n     * The field corresponding to the identifier of the user who supervised the process instance.\n     */\n    public static final String USER_ID = \"userId\";\n\n    /**\n     * The field corresponding to the identifier of the group who supervised the process instance.\n     */\n    public static final String GROUP_ID = \"groupId\";\n\n    /**\n     * The field corresponding to the identifier of the role who supervised the process instance.\n     */\n    public static final String ROLE_ID = \"roleId\";\n\n    /**\n     * The field corresponding to the identifier of the user assignee to a user task of the process instance.\n     */\n    public static final String ASSIGNEE_ID = \"assigneeId\";\n\n    /**\n     * The field corresponding to the identifier of the flow node that starts the process instance.\n     */\n    public static final String CALLER_ID = \"callerId\";\n\n    public static final String STRING_INDEX_1 = \"stringIndex1\";\n    public static final String STRING_INDEX_2 = \"stringIndex2\";\n    public static final String STRING_INDEX_3 = \"stringIndex3\";\n    public static final String STRING_INDEX_4 = \"stringIndex4\";\n    public static final String STRING_INDEX_5 = \"stringIndex5\";\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ConfigurationState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.process;\n\n/**\n * Autoset by Bonita Engine. Determines if a process is resolved / unresolved, that is, if a process can be started or\n * not.\n * Reasons for unresolved processes are:\n * <ul>\n * <li>Not all actors of the process have mappings to users / groups / roles / memberships</li>\n * <li>Some business data used in the process are not available on the currently deployed Business Data Model version\n * (Subscription editions only)</li>\n * <li>Not all connectors used in the process have an implementation defined</li>\n * <li>Not all user filters used in the process have an implementation defined</li>\n * <li>Not all parameters used in the process have a value defined (Subscription editions only)</li>\n * </ul>\n * <p>Use {@link ProcessDeploymentInfo#getConfigurationState()} to retrieve the configuration state for a process.</p>\n *\n * @see ProcessDeploymentInfo#getConfigurationState()\n * @author Celine Souchet\n * @author Emmanuel Duchastenier\n * @version 6.3.5\n * @since 6.0.0\n */\npublic enum ConfigurationState {\n\n    /**\n     * The process is unresolved, for one or more of the causes defined above.\n     */\n    UNRESOLVED,\n\n    /**\n     * The process is resolved, and can be started to create a new instance.\n     */\n    RESOLVED\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/IllegalProcessStateException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.process;\n\nimport org.bonitasoft.engine.exception.BonitaException;\n\n/**\n * <p>Thrown when the process instance is in illegal state.</p>\n * <p>The class IllegalProcessStateException is a form of Throwable that indicates conditions that a reasonable\n * application might want to catch.<br>\n * The class IllegalProcessStateException that is not also subclasses of {@link RuntimeException} are checked\n * exceptions.<br>\n * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by\n * the execution of the method or\n * constructor and propagate outside the method or constructor boundary.</p>\n *\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @version 6.3.5\n * @since 6.0.0\n */\npublic class IllegalProcessStateException extends BonitaException {\n\n    private static final long serialVersionUID = -7194818098032678910L;\n\n    /**\n     * Constructs a new exception with the specified detail message.\n     *\n     * @param message\n     *        The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method).\n     */\n    public IllegalProcessStateException(final String message) {\n        super(message);\n    }\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public IllegalProcessStateException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/Index.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.process;\n\n/**\n * Use to update the string indexes.\n *\n * @author Emmanuel Duchastenier\n * @since 7.12.0\n * @see ProcessInstance#getStringIndex1()\n * @see ProcessInstance#getStringIndex2()\n * @see ProcessInstance#getStringIndex3()\n * @see ProcessInstance#getStringIndex4()\n * @see ProcessInstance#getStringIndex5()\n * @see org.bonitasoft.engine.api.ProcessAPI#updateProcessInstanceIndex(long, Index, String)\n */\npublic enum Index {\n\n    /**\n     * Corresponding to the first search key\n     */\n    FIRST,\n    /**\n     * Corresponding to the second search key\n     */\n    SECOND,\n    /**\n     * Corresponding to the third search key\n     */\n    THIRD,\n    /**\n     * Corresponding to the fourth search key\n     */\n    FOURTH,\n    /**\n     * Corresponding to the fifth search key\n     */\n    FIFTH\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/Problem.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.process;\n\nimport java.io.Serializable;\n\n/**\n * A <code>Problem</code> explains the issue in the {@link DesignProcessDefinition} when it is not well designed.\n * It relates to :\n * <ul>\n * <li>structural problems such as a {@link org.bonitasoft.engine.bpm.flownode.BoundaryEventDefinition} without an\n * exception flow,</li>\n * <li>naming problems such as two {@link org.bonitasoft.engine.bpm.flownode.FlowNodeDefinition}s with the same\n * name,</li>\n * <li>type problems such as an {@link org.bonitasoft.engine.expression.Expression} which has not the right type,</li>\n * <li>...</li>\n * </ul>\n *\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @version 6.3.5\n * @since 6.0.0\n */\npublic interface Problem extends Serializable {\n\n    /**\n     * Defines a severity level.\n     *\n     * @author Matthieu Chaffotte\n     */\n    public enum Level {\n        /**\n         * The {@link DesignProcessDefinition} cannot be deployed.\n         */\n        ERROR,\n        /**\n         * The {@link DesignProcessDefinition} can be deployed and used but contains something that should be updated.\n         */\n        WARNING,\n        /**\n         * Not currently used.\n         */\n        INFO\n    }\n\n    /**\n     * Returns the severity of the problem.\n     *\n     * @return the severity\n     */\n    Level getLevel();\n\n    /**\n     * Returns the resource/concept name of the problem.\n     * It can be related to {@link org.bonitasoft.engine.bpm.data.DataDefinition},\n     * {@link org.bonitasoft.engine.bpm.flownode.FlowNodeDefinition}, ...\n     *\n     * @return the resource name\n     */\n    String getResource();\n\n    /**\n     * Returns the resource identifier.\n     * Generally, it is the name of the entity that has the problem in the {@link DesignProcessDefinition}.\n     * For example, the name of the {@link org.bonitasoft.engine.bpm.flownode.UserTaskDefinition}.\n     *\n     * @return the resource identifier\n     */\n    String getResourceId();\n\n    /**\n     * Returns the description/explanation of the problem.\n     *\n     * @return the explanation of the problem\n     */\n    String getDescription();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ProcessActivationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.process;\n\nimport org.bonitasoft.engine.exception.ExecutionException;\n\n/**\n * Thrown when a process definition cannot be enabled / disabled, or when a\n * {@link org.bonitasoft.engine.api.ProcessAPI#startProcess(long)} (and its variants)\n * cannot be performed\n * because the process definition is not enabled.\n *\n * @author Baptiste Mesta\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n * @see org.bonitasoft.engine.api.ProcessAPI#startProcess(long)\n * @see org.bonitasoft.engine.api.ProcessAPI#startProcess(long, long)\n * @see org.bonitasoft.engine.api.ProcessAPI#startProcess(long, java.util.Map)\n * @see org.bonitasoft.engine.api.ProcessAPI#startProcess(long, java.util.List, java.util.Map)\n * @see org.bonitasoft.engine.api.ProcessAPI#startProcess(long, long, java.util.Map)\n * @see org.bonitasoft.engine.api.ProcessAPI#startProcess(long, long, java.util.List, java.util.Map)\n * @version 6.3.5\n * @since 6.0.0\n */\npublic class ProcessActivationException extends ExecutionException {\n\n    private static final long serialVersionUID = -425713003229819771L;\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param e\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public ProcessActivationException(final Exception e) {\n        super(e);\n    }\n\n    /**\n     * Constructs a new exception with the specified detail message and cause.\n     *\n     * @param message\n     *        The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method).\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public ProcessActivationException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    /**\n     * Constructs a new exception with the specified detail message.\n     *\n     * @param message\n     *        The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method).\n     */\n    public ProcessActivationException(final String message) {\n        super(message);\n    }\n\n    /**\n     * Constructs a new exception and the message with the given informations of the process definition with the\n     * problem.\n     *\n     * @param processDefinitionId\n     *        The identifier of the process definition\n     * @param name\n     *        The name of the process definition\n     * @param version\n     *        The version of the process definition\n     */\n    public ProcessActivationException(final long processDefinitionId, final String name, final String version) {\n        super(\"The process definition is not enabled !!\");\n        setProcessDefinitionIdOnContext(processDefinitionId);\n        setProcessDefinitionNameOnContext(name);\n        setProcessDefinitionVersionOnContext(version);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ProcessDefinitionNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.process;\n\nimport org.bonitasoft.engine.exception.NotFoundException;\n\n/**\n * Thrown when a reference to a nonexistant {@link ProcessDefinition} is made, generally through its ID.\n *\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @version 6.3.5\n * @since 6.0.0\n */\npublic class ProcessDefinitionNotFoundException extends NotFoundException {\n\n    private static final long serialVersionUID = -6469281629161643837L;\n\n    /**\n     * Constructs a new exception with the specified detail message.\n     *\n     * @param message\n     *        The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method).\n     */\n    public ProcessDefinitionNotFoundException(final String message) {\n        super(message);\n    }\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public ProcessDefinitionNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n    /**\n     * Constructs a new exception with the specified detail cause, and constructs the message with the identifier of the\n     * process definition.\n     *\n     * @param processDefinitionId\n     *        The identifier of the process definition\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public ProcessDefinitionNotFoundException(final long processDefinitionId, final Throwable cause) {\n        super(\"Process definition not found with id: \" + processDefinitionId, cause);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ProcessDeployException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.process;\n\nimport org.bonitasoft.engine.exception.ExecutionException;\n\n/**\n * Thrown when a process fails to deploy.\n *\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n * @version 6.3.5\n * @since 6.0.0\n */\npublic class ProcessDeployException extends ExecutionException {\n\n    private static final long serialVersionUID = 3104389074405599228L;\n\n    /**\n     * Constructs a new exception with the specified detail message.\n     *\n     * @param message\n     *        The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method).\n     */\n    public ProcessDeployException(final String message) {\n        super(message);\n    }\n\n    public ProcessDeployException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public ProcessDeployException(final Throwable cause) {\n        super(cause);\n    }\n\n    /**\n     * Get the identifier of the process definition who failed.\n     *\n     * @return The identifier of the process definition who failed.\n     * @deprecated always return 0, as the processDefinitionId cannot be determined if process failed to deploy\n     */\n    @Deprecated(forRemoval = true, since = \"8.0\")\n    public Long getProcessDefinitionId() {\n        return 0L;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ProcessDeploymentInfo.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.process;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.bpm.BaseElement;\nimport org.bonitasoft.engine.bpm.DescriptionElement;\n\n/**\n * Gives access to the {@link ProcessDefinition} deployment information.<br>\n * A <code>ProcessDeploymentInfo</code> has a {@link ConfigurationState}, which says if the process is\n * <code>resolved</code> (all its basic configuration has a\n * proper value), or <code>unresolved</code> (some configuration has to be done before the process can be activated).\n * <p>\n * A <code>ProcessDeploymentInfo</code> has an {@link ActivationState}, which says if the process was set to\n * <code>enabled</code> (logged users can start\n * instances of this process), or <code>disabled</code> (no start can be performed on the process).\n *\n * @author Baptiste Mesta\n * @author Yanyan Liu\n * @author Hongwen Zang\n * @author Celine Souchet\n * @author Emmanuel Duchastenier\n * @see ProcessDefinition\n * @see ConfigurationState\n * @see ActivationState\n */\npublic interface ProcessDeploymentInfo extends DescriptionElement, BaseElement {\n\n    /**\n     * Retrieves the {@link ProcessDefinition} identifier\n     *\n     * @return a <code>long</code> representing the <code>ProcessDefinition</code> identifier\n     * @see ProcessDefinition\n     */\n    long getProcessId();\n\n    /**\n     * Retrieves the {@link ProcessDefinition} version\n     *\n     * @return a String representing the <code>ProcessDefinition</code> version.\n     * @see ProcessDefinition\n     */\n    String getVersion();\n\n    /**\n     * Retrieves the {@link ProcessDefinition} display description. Unlike <code>description</code> that is static, the\n     * <code>display description</code> can be\n     * updated via\n     * {@link org.bonitasoft.engine.api.ProcessManagementAPI#updateProcessDeploymentInfo(long, ProcessDeploymentInfoUpdater)}.\n     * <p>\n     * When set, this field is used by the Bonita Portal in the place of <code>description</code>.\n     *\n     * @return a String representing the {@link ProcessDefinition} display description.\n     * @see ProcessDefinition\n     * @see org.bonitasoft.engine.api.ProcessManagementAPI#updateProcessDeploymentInfo(long,\n     *      ProcessDeploymentInfoUpdater)\n     */\n    String getDisplayDescription();\n\n    /**\n     * Retrieves the Date when the underlining {@link ProcessDefinition} was deployed.\n     *\n     * @return the Date when the underlining <code>ProcessDefinition</code> was deployed.\n     * @see ProcessDefinition\n     */\n    Date getDeploymentDate();\n\n    /**\n     * Retrieves the identifier of the Bonita {@link org.bonitasoft.engine.identity.User} which deployed the\n     * {@link ProcessDefinition}\n     *\n     * @return a long representing the identifier of the <code>Bonita user</code> which deployed the process.\n     * @see org.bonitasoft.engine.identity.User\n     * @see ProcessDefinition\n     */\n    long getDeployedBy();\n\n    /**\n     * Retrieves the {@link ProcessDefinition} display name. Unlike <code>name</code> that is static, the <code>display\n     * name</code> can be\n     * updated via\n     * {@link org.bonitasoft.engine.api.ProcessManagementAPI#updateProcessDeploymentInfo(long, ProcessDeploymentInfoUpdater)}.\n     * <p>\n     * When set this field is used by the Bonita Portal in the place of <code>name</code>.\n     *\n     * @return a String representing the <code>ProcessDefinition</code> display name.\n     * @see ProcessDefinition\n     * @see org.bonitasoft.engine.api.ProcessManagementAPI#updateProcessDeploymentInfo(long,\n     *      ProcessDeploymentInfoUpdater)\n     */\n    String getDisplayName();\n\n    /**\n     * Retrieves the date of the last update statement\n     *\n     * @return the date of the last update statement\n     */\n    Date getLastUpdateDate();\n\n    /**\n     * Retrieves the process icon path. The path is relative to the <code>bonita.home</code> folder.\n     *\n     * @return a String representing the process icon path.\n     */\n    String getIconPath();\n\n    /**\n     * Retrieves the {@link ProcessDefinition} {@link ConfigurationState}.\n     *\n     * @return the <code>ProcessDefinition</code> <code>ConfigurationState</code>\n     * @see ProcessDefinition\n     * @see ConfigurationState\n     */\n    ConfigurationState getConfigurationState();\n\n    /**\n     * Retrieves the {@link ProcessDefinition} {@link ActivationState}.\n     *\n     * @return the <code>ProcessDefinition</code> <code>ActivationState</code>\n     */\n    ActivationState getActivationState();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ProcessDeploymentInfoCriterion.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.process;\n\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.Sort;\n\n/**\n * Sort criterion used to specify the sort order of the {@link ProcessDeploymentInfo}. <br>\n * Used by {@link org.bonitasoft.engine.api.ProcessManagementAPI} methods like\n * {@link org.bonitasoft.engine.api.ProcessManagementAPI#getProcessDeploymentInfos(int, int, ProcessDeploymentInfoCriterion)}\n * to\n * indicate in what order we should return the list of the results.\n *\n * @author Baptiste Mesta\n * @author Celine Souchet\n * @version 6.3.5\n * @since 6.0.0\n */\npublic enum ProcessDeploymentInfoCriterion {\n\n    /**\n     * Process name ascending order\n     */\n    NAME_ASC(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC),\n\n    /**\n     * Process version ascending order\n     */\n    VERSION_ASC(ProcessDeploymentInfoSearchDescriptor.VERSION, Order.ASC),\n\n    /**\n     * Process activation state ascending order\n     */\n    ACTIVATION_STATE_ASC(ProcessDeploymentInfoSearchDescriptor.ACTIVATION_STATE, Order.ASC),\n\n    /**\n     * Process configuration state ascending order\n     */\n    CONFIGURATION_STATE_ASC(ProcessDeploymentInfoSearchDescriptor.CONFIGURATION_STATE, Order.ASC),\n\n    /**\n     * Process name ascending order\n     */\n    NAME_DESC(ProcessDeploymentInfoSearchDescriptor.NAME, Order.DESC),\n\n    /**\n     * Process version ascending order\n     */\n    VERSION_DESC(ProcessDeploymentInfoSearchDescriptor.VERSION, Order.DESC),\n\n    /**\n     * Process activation state descending order\n     */\n    ACTIVATION_STATE_DESC(ProcessDeploymentInfoSearchDescriptor.ACTIVATION_STATE, Order.DESC),\n\n    /**\n     * Process configuration state descending order\n     */\n    CONFIGURATION_STATE_DESC(ProcessDeploymentInfoSearchDescriptor.CONFIGURATION_STATE, Order.DESC),\n\n    /**\n     * Default criterion : {@link ProcessDeploymentInfoCriterion#NAME_ASC}\n     */\n    DEFAULT(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC);\n\n    private final String field;\n\n    private final Order order;\n\n    ProcessDeploymentInfoCriterion(final String field, final Order order) {\n        this.field = field;\n        this.order = order;\n    }\n\n    /**\n     * Get the {@link Order} corresponding to this object.\n     *\n     * @return The {@link Order} corresponding to this object.\n     * @since 6.3.5\n     */\n    public Order getOrder() {\n        return order;\n    }\n\n    /**\n     * Get the name of the field used to sort the {@link ProcessDeploymentInfo}, corresponding to this object.\n     *\n     * @return The name of the field corresponding to this object.\n     * @since 6.3.5\n     */\n    public String getField() {\n        return field;\n    }\n\n    /**\n     * Build the {@link Sort} corresponding to this object.\n     *\n     * @return The {@link Sort} corresponding to this object.\n     * @since 6.3.5\n     */\n    public Sort getSort() {\n        return new Sort(order, field);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ProcessDeploymentInfoSearchDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.process;\n\n/**\n * Search descriptors are used to filter / sort results of a generic search. <br>\n * ProcessDeploymentInfoSearchDescriptor defines the fields that can be used as filters or sort fields on\n * <code>List&lt;ProcessDeploymentInfo&gt;</code>\n * returning methods.\n *\n * @author Zhao Na\n * @author Celine Souchet\n * @author Matthieu Chaffotte\n * @see org.bonitasoft.engine.api.ProcessAPI\n * @see ProcessDeploymentInfo\n * @see ProcessDefinition\n * @version 6.3.5\n * @since 6.0.0\n */\npublic class ProcessDeploymentInfoSearchDescriptor {\n\n    /**\n     * The field corresponding to the identifier of the process in the database.\n     */\n    public static final String ID = \"id\";\n\n    /**\n     * The field corresponding to the name of the process.\n     */\n    public static final String NAME = \"name\";\n\n    /**\n     * The field corresponding to the version of the process.\n     */\n    public static final String VERSION = \"version\";\n\n    /**\n     * The field corresponding to the date of the deployment of the process.\n     */\n    public static final String DEPLOYMENT_DATE = \"deploymentDate\";\n\n    /**\n     * The field corresponding to the identifier of the user who deployed the process.\n     */\n    public static final String DEPLOYED_BY = \"deployedBy\";\n\n    /**\n     * The field corresponding to the activation state of the process.\n     * To filter on this field, example :\n     * {@code searchOptionsBuilder.filter(ProcessDeploymentInfoSearchDescriptor.ACTIVATION_STATE, ActivationState.ENABLED.name());}\n     */\n    public static final String ACTIVATION_STATE = \"activationState\";\n\n    /**\n     * The field corresponding to the configuration state of the process.\n     */\n    public static final String CONFIGURATION_STATE = \"configurationState\";\n\n    /**\n     * The field corresponding to the identifier of the process definition (in the bonita home).\n     */\n    public static final String PROCESS_ID = \"processId\";\n\n    /**\n     * The field corresponding to the display name of the process.\n     */\n    public static final String DISPLAY_NAME = \"displayName\";\n\n    /**\n     * The field corresponding to the last date of the updating of the process.\n     */\n    public static final String LAST_UPDATE_DATE = \"lastUpdateDate\";\n\n    /**\n     * The field corresponding to the identifier of the category of the process.\n     */\n    public static final String CATEGORY_ID = \"categoryId\";\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ProcessDeploymentInfoUpdater.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.process;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Updater object to update {@link ProcessDeploymentInfo}.\n *\n * @author Emmanuel Duchastenier\n * @author Zhang Bole\n * @author Celine Souchet\n * @version 6.3.5\n * @since 6.0.0\n */\npublic class ProcessDeploymentInfoUpdater implements Serializable {\n\n    private static final long serialVersionUID = 8000868488852784706L;\n\n    /**\n     * Fields that can be updated on a {@link ProcessDeploymentInfo}.\n     *\n     * @author Emmanuel Duchastenier\n     */\n    public enum ProcessDeploymentInfoField {\n        /**\n         * Display name of the process\n         */\n        DISPLAY_NAME,\n\n        /**\n         * Display description of the process\n         */\n        DISPLAY_DESCRIPTION,\n\n        /**\n         * Path of the icon of the process\n         */\n        ICONPATH\n    }\n\n    private final Map<ProcessDeploymentInfoField, Serializable> fields;\n\n    /**\n     * Construct the updater object with no field to update.\n     */\n    public ProcessDeploymentInfoUpdater() {\n        fields = new HashMap<ProcessDeploymentInfoField, Serializable>(ProcessDeploymentInfoField.values().length);\n    }\n\n    /**\n     * Set the new display name of the process definition.\n     *\n     * @param name\n     *        The new display name of the process definition.\n     * @since 6.3.5\n     */\n    public void setDisplayName(final String name) {\n        fields.put(ProcessDeploymentInfoField.DISPLAY_NAME, name);\n    }\n\n    /**\n     * Set the new display description of the process definition.\n     *\n     * @param description\n     *        The new display description of the process definition.\n     * @since 6.3.5\n     */\n    public void setDisplayDescription(final String description) {\n        fields.put(ProcessDeploymentInfoField.DISPLAY_DESCRIPTION, description);\n    }\n\n    /**\n     * Set the new icon path of the process definition.\n     *\n     * @param iconPath\n     *        The new icon path of the process definition.\n     * @since 6.3.5\n     */\n    public void setIconPath(final String iconPath) {\n        fields.put(ProcessDeploymentInfoField.ICONPATH, iconPath);\n    }\n\n    /**\n     * Get the map of the fields to update with their new value.\n     *\n     * @return The map of the fields to update with their new value.\n     * @since 6.3.5\n     */\n    public Map<ProcessDeploymentInfoField, Serializable> getFields() {\n        return fields;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ProcessEnablementException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.process;\n\nimport org.bonitasoft.engine.exception.ExecutionException;\n\n/**\n * Thrown when a process cannot be enabled. This can happen if the process is already enabled, or if an unexpected error\n * occurs on methods like\n * {@link org.bonitasoft.engine.api.ProcessAPI#enableProcess(long)}.\n *\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @version 6.4.1\n * @since 6.0.0\n */\npublic class ProcessEnablementException extends ExecutionException {\n\n    private static final long serialVersionUID = 7175486722623260153L;\n\n    /**\n     * Constructs a new exception with the specified detail message.\n     *\n     * @param message\n     *        The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method).\n     */\n    public ProcessEnablementException(final String message) {\n        super(message);\n    }\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public ProcessEnablementException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ProcessExecutionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.process;\n\nimport lombok.Getter;\nimport org.bonitasoft.engine.exception.ExecutionException;\n\n/**\n * Thrown when a process instance fails when it's starting.\n *\n * @author Frédéric Bouquet\n * @author Celine Souchet\n * @version 6.3.5\n * @since 6.0.0\n */\npublic class ProcessExecutionException extends ExecutionException {\n\n    private static final long serialVersionUID = 4412292065541283593L;\n    @Getter\n    private long retryAfter = -1L;\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public ProcessExecutionException(Throwable cause) {\n        super(cause);\n    }\n\n    public ProcessExecutionException(Throwable cause, long retryAfter) {\n        super(cause);\n        this.retryAfter = retryAfter;\n    }\n\n    /**\n     * Constructs a new exception with the specified detail message.\n     *\n     * @param message\n     *        The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method).\n     */\n    public ProcessExecutionException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ProcessExportException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.process;\n\nimport org.bonitasoft.engine.exception.ExecutionException;\n\n/**\n * Thrown when the exporting of the content of the process definition under the bonita home fails.\n *\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n * @version 6.3.5\n * @since 6.0.0\n */\npublic class ProcessExportException extends ExecutionException {\n\n    private static final long serialVersionUID = -6577758579083715914L;\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public ProcessExportException(final Throwable cause) {\n        super(cause);\n    }\n\n    /**\n     * Constructs a new exception with the specified detail message and cause.\n     *\n     * @param message\n     *        The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method).\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public ProcessExportException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    /**\n     * Constructs a new exception with the specified detail message.\n     *\n     * @param message\n     *        The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method).\n     */\n    public ProcessExportException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ProcessInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.process;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.bpm.BaseElement;\nimport org.bonitasoft.engine.bpm.NamedElement;\n\n/**\n * Represents a running instance of a process.\n * Gives access to the information of the instance, whereas the state, the definition of the process instance...\n *\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @version 6.3.5\n * @since 6.0.0\n */\npublic interface ProcessInstance extends NamedElement, BaseElement {\n\n    /**\n     * Get the state of this process instance.\n     *\n     * @return The current state of this process instance.\n     * @see ProcessInstanceState\n     * @since 6.0.0\n     */\n    String getState();\n\n    /**\n     * Get the date when this process instance was started.\n     *\n     * @return The date when this process instance was started.\n     * @since 6.0.0\n     */\n    Date getStartDate();\n\n    /**\n     * Get the identifier of the user who started this process instance.\n     *\n     * @return The identifier of the user who started this process instance.\n     * @since 6.0.0\n     */\n    long getStartedBy();\n\n    /**\n     * Get the identifier of the substitute user (as Process manager or Administrator) who started this process\n     * instance.\n     *\n     * @return The identifier of the substitute user (as Process manager or Administrator) who started this process\n     *         instance.\n     * @since 6.3.0\n     */\n    long getStartedBySubstitute();\n\n    /**\n     * Get the date when this process instance was finished.\n     * It equals to null, if this process instance is not finished.\n     *\n     * @return The date when this process instance was finished.\n     * @since 6.0.0\n     */\n    Date getEndDate();\n\n    /**\n     * Get the date of the last update of this process instance.\n     *\n     * @return The date of the last update of this process instance.\n     * @since 6.0.0\n     */\n    Date getLastUpdate();\n\n    /**\n     * Get the identifier of the definition of this process.\n     *\n     * @return The identifier of the definition of this process.\n     * @see ProcessDefinition#getId()\n     * @since 6.0.0\n     */\n    long getProcessDefinitionId();\n\n    /**\n     * Get the description of this process instance.\n     *\n     * @return The description of this process instance.\n     * @since 6.0.0\n     */\n    String getDescription();\n\n    /**\n     * Get the identifier of the root {@link ProcessInstance} of this process instance.\n     * Is -1 if this process instance is not a child of another process instance.\n     *\n     * @return The identifier of the root process instance of this process instance.\n     * @see ProcessInstance#getId()\n     * @see ArchivedProcessInstance#getSourceObjectId()\n     * @since 6.0.0\n     */\n    long getRootProcessInstanceId();\n\n    /**\n     * Get the identifier of the flow node instance who starts this process instance.\n     * Is -1 if this process instance is not a child of another process instance.\n     *\n     * @return The identifier of the flow node instance who starts this process instance.\n     * @see org.bonitasoft.engine.bpm.flownode.CallActivityInstance#getId()\n     * @see SubProcessDefinition#getId()\n     * @since 6.0.0\n     */\n    long getCallerId();\n\n    /**\n     * Also known as search key, this field is used to extend the meta data of the process instance.\n     * It's the value corresponding to the search key label.\n     * Return an empty string, if there is no first search key.\n     *\n     * @return The first search key of this process instance.\n     * @see ProcessInstance#getStringIndexLabel(int)\n     * @since 6.0.0\n     */\n    String getStringIndex1();\n\n    /**\n     * Also known as search key, this field is used to extend the meta data of the process instance.\n     * It's the value corresponding to the search key label.\n     * Return an empty string, if there is no second search key.\n     *\n     * @return The second search key of this process instance.\n     * @see ProcessInstance#getStringIndexLabel(int)\n     * @since 6.0.0\n     */\n    String getStringIndex2();\n\n    /**\n     * Also known as search key, this field is used to extend the meta data of the process instance.\n     * It's the value corresponding to the search key label.\n     * Return an empty string, if there is no third search key.\n     *\n     * @return The third search key of this process instance.\n     * @see ProcessInstance#getStringIndexLabel(int)\n     * @since 6.0.0\n     */\n    String getStringIndex3();\n\n    /**\n     * Also known as search key, this field is used to extend the meta data of the process instance.\n     * It's the value corresponding to the search key label.\n     * Return an empty string, if there is no fourth search key.\n     *\n     * @return The fourth search key of this process instance.\n     * @see ProcessInstance#getStringIndexLabel(int)\n     * @since 6.0.0\n     */\n    String getStringIndex4();\n\n    /**\n     * Also known as search key, this field is used to extend the meta data of the process instance.\n     * It's the value corresponding to the search key label.\n     * Return an empty string, if there is no fifth search key.\n     *\n     * @return The fifth search key of this process instance.\n     * @see ProcessInstance#getStringIndexLabel(int)\n     * @since 6.0.0\n     */\n    String getStringIndex5();\n\n    /**\n     * The index must be between 1 and 5.\n     *\n     * @param index\n     *        The index of the label\n     * @return The label of the search key corresponding to the parameter\n     * @exception IndexOutOfBoundsException\n     *            It's thrown if the parameter is not between 1 and 5.\n     * @since 6.0.0\n     */\n    String getStringIndexLabel(int index);\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ProcessInstanceCriterion.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.process;\n\n/**\n * Sort criterion used to specify the sort order of the {@link ProcessInstance}. <br>\n * Used by {@link org.bonitasoft.engine.api.ProcessRuntimeAPI} methods like\n * {@link org.bonitasoft.engine.api.ProcessRuntimeAPI#getProcessInstances(int, int, ProcessInstanceCriterion)} to\n * indicate in what order we should return the\n * list of the results.\n *\n * @author Emmanuel Duchastenier\n * @author Yanyan Liu\n * @author Celine Souchet\n * @version 6.3.5\n * @since 6.0.0\n * @see org.bonitasoft.engine.api.ProcessRuntimeAPI\n */\npublic enum ProcessInstanceCriterion {\n\n    /**\n     * Process State ascending order\n     */\n    STATE_ASC,\n\n    /**\n     * Process State descending order\n     */\n    STATE_DESC,\n\n    /**\n     * Process instance name ascending order\n     */\n    NAME_ASC,\n\n    /**\n     * Process instance name descending order\n     */\n    NAME_DESC,\n\n    /**\n     * Creation date ascending order\n     */\n    CREATION_DATE_ASC,\n\n    /**\n     * Creation date descending order\n     */\n    CREATION_DATE_DESC,\n\n    /**\n     * Last update ascending order\n     */\n    LAST_UPDATE_ASC,\n\n    /**\n     * Last update descending order\n     */\n    LAST_UPDATE_DESC,\n\n    /**\n     * Archive date ascending order\n     */\n    ARCHIVE_DATE_ASC,\n\n    /**\n     * Archive date descending order\n     */\n    ARCHIVE_DATE_DESC,\n\n    /**\n     * Default sort criterion\n     */\n    DEFAULT;\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ProcessInstanceNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.process;\n\nimport org.bonitasoft.engine.exception.NotFoundException;\n\n/**\n * Thrown when a reference to an inexistant {@link ProcessInstance} is made, generally through its ID.\n *\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class ProcessInstanceNotFoundException extends NotFoundException {\n\n    private static final long serialVersionUID = 2829055979970789709L;\n\n    /**\n     * Constructs a new exception with the specified detail message.\n     *\n     * @param message\n     *        The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method).\n     */\n    public ProcessInstanceNotFoundException(final String message) {\n        super(message);\n    }\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public ProcessInstanceNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n    /**\n     * Constructs a new exception and the message with the identifier of the process instance.\n     *\n     * @param processInstanceId\n     *        The identifier of the process instance\n     */\n    public ProcessInstanceNotFoundException(final long processInstanceId) {\n        super(\"No process instance found !!\");\n        setProcessInstanceIdOnContext(processInstanceId);\n    }\n\n    /**\n     * Constructs a new exception and the message with the identifier of the process instance.\n     *\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     * @param processInstanceId\n     *        The identifier of the process instance\n     */\n    public ProcessInstanceNotFoundException(final Throwable cause, final long processInstanceId) {\n        super(cause);\n        setProcessInstanceIdOnContext(processInstanceId);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ProcessInstanceSearchDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.process;\n\n/**\n * Search descriptors are used to filter / sort results of a generic search. <br>\n * ProcessInstanceSearchDescriptor defines the fields that can be used as filters or sort fields on\n * <code>List&lt;ProcessInstance&gt;</code> returning\n * methods.\n *\n * @author Yanyan Liu\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n * @see org.bonitasoft.engine.api.ProcessRuntimeAPI\n * @see ProcessInstance\n * @version 6.3.5\n * @since 6.0.0\n */\npublic class ProcessInstanceSearchDescriptor {\n\n    /**\n     * The field corresponding to the identifier of the process definition.\n     */\n    public static final String PROCESS_DEFINITION_ID = \"processDefinitionId\";\n\n    /**\n     * The field corresponding to the name of the process.\n     */\n    public static final String NAME = \"name\";\n\n    /**\n     * The field corresponding to the identifier of the process instance.\n     */\n    public static final String ID = \"id\";\n\n    /**\n     * The field corresponding to the identifier of the root process instance.\n     */\n    public static final String ROOT_PROCESS_INSTANCE_ID = \"rootProcessInstanceId\";\n\n    /**\n     * The field corresponding to the identifier of the user assignee to a user task of the process instance.\n     */\n    public static final String ASSIGNEE_ID = \"assigneeId\";\n\n    /**\n     * The field corresponding to the identifier of the user who started the process instance.\n     */\n    public static final String STARTED_BY = \"startedBy\";\n\n    /**\n     * The field corresponding to the identifier of the user who started the process instance for the user in\n     * {@link ProcessInstance#getStartedBy()}.\n     */\n    public static final String STARTED_BY_SUBSTITUTE = \"startedBySubstitute\";\n\n    /**\n     * The field corresponding to the date when the process instance is started.\n     */\n    public static final String START_DATE = \"startDate\";\n\n    /**\n     * The field corresponding to the date when the process instance completed.\n     */\n    public static final String END_DATE = \"endDate\";\n\n    /**\n     * The field corresponding to the last date of the updating of the process instance.\n     */\n    public static final String LAST_UPDATE = \"lastUpdate\";\n\n    /**\n     * The field corresponding to the identifier of the user who supervised the process instance.\n     */\n    public static final String PROCESS_SUPERVISOR_USER_ID = \"userId\";\n\n    /**\n     * The field corresponding to the identifier of the group who supervised the process instance.\n     */\n    public static final String PROCESS_SUPERVISOR_GROUP_ID = \"groupId\";\n\n    /**\n     * The field corresponding to the identifier of the role who supervised the process instance.\n     */\n    public static final String PROCESS_SUPERVISOR_ROLE_ID = \"roleId\";\n\n    /**\n     * The field corresponding to the identifier of the state of the process instance.\n     */\n    public static final String STATE_ID = \"stateId\";\n\n    /**\n     * The field corresponding to the name of the state of the process instance.\n     * The value can be of the type String or ProcessInstanceState.\n     *\n     * @see ProcessInstanceState\n     */\n    public static final String STATE_NAME = \"stateName\";\n\n    /**\n     * The field corresponding to the identifier of the flow node that starts the process instance.\n     */\n    public static final String CALLER_ID = \"callerId\";\n\n    /**\n     * The field corresponding to the first search key of the process instance.\n     */\n    public static final String STRING_INDEX_1 = \"index1\";\n\n    /**\n     * The field corresponding to the second search key of the process instance.\n     */\n    public static final String STRING_INDEX_2 = \"index2\";\n\n    /**\n     * The field corresponding to the third search key of the process instance.\n     */\n    public static final String STRING_INDEX_3 = \"index3\";\n\n    /**\n     * The field corresponding to the fourth search key of the process instance.\n     */\n    public static final String STRING_INDEX_4 = \"index4\";\n\n    /**\n     * The field corresponding to the fifth search key of the process instance.\n     */\n    public static final String STRING_INDEX_5 = \"index5\";\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ProcessInstanceState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.process;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author Celine Souchet\n * @version 6.3.5\n * @since 6.0.0\n * @see ProcessInstance#getState()\n */\npublic enum ProcessInstanceState {\n\n    /**\n     * The initializing state. Id=0\n     */\n    INITIALIZING(0),\n\n    /**\n     * The started state. Id=1\n     */\n    STARTED(1),\n\n    /**\n     * The suspended state. Id=2\n     */\n    SUSPENDED(2),\n\n    /**\n     * The cancelled state. Id=3\n     */\n    CANCELLED(3),\n\n    /**\n     * The aborted state. Id=4\n     */\n    ABORTED(4),\n\n    /**\n     * The completing state. Id=5\n     */\n    COMPLETING(5),\n\n    /**\n     * The completed state. Id=6\n     */\n    COMPLETED(6),\n\n    /**\n     * The error state. Id=7\n     */\n    ERROR(7),\n\n    /**\n     * The aborting state. Id=11\n     */\n    ABORTING(11);\n\n    private static Map<Integer, ProcessInstanceState> map = new HashMap<Integer, ProcessInstanceState>(11);\n\n    private int id;\n\n    private ProcessInstanceState(final int id) {\n        this.id = id;\n    }\n\n    /**\n     * Get the identifier corresponding to the state.\n     *\n     * @return The identifier corresponding to the state.\n     * @since 6.3.5\n     */\n    public int getId() {\n        return id;\n    }\n\n    /**\n     * Get the {@link ProcessInstanceState} corresponding to the identifier.\n     *\n     * @return The {@link ProcessInstanceState} corresponding to the identifier.\n     * @since 6.3.5\n     */\n    public static ProcessInstanceState getFromId(final int id) {\n        if (!map.containsKey(id)) {\n            map.put(id, fromIdToProcessInstanceState(id));\n        }\n        return map.get(id);\n    }\n\n    private static ProcessInstanceState fromIdToProcessInstanceState(final int id) {\n        for (final ProcessInstanceState state : values()) {\n            if (id == state.getId()) {\n                return state;\n            }\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ProcessResourceNotFoundException.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.process;\n\nimport org.bonitasoft.engine.exception.NotFoundException;\n\n/**\n * Thrown when a resource cannot be found in a process definition's business archive.\n */\npublic class ProcessResourceNotFoundException extends NotFoundException {\n\n    private static final long serialVersionUID = 3032565336554438921L;\n\n    public ProcessResourceNotFoundException(final String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/V6FormDeployException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.process;\n\n/**\n * Thrown when a process fails to deploy because of V6 form presence in the bar to deploy.\n *\n * @author Danila Mazour\n * @since 7.8.0\n */\npublic class V6FormDeployException extends ProcessDeployException {\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public V6FormDeployException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/impl/ProcessInstanceUpdater.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.process.impl;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Updater object to update the string indexes.\n *\n * @author Danila Mazour\n * @author Emmanuel Duchastenier\n * @since 7.12.0\n * @see org.bonitasoft.engine.bpm.process.ProcessInstance\n */\npublic class ProcessInstanceUpdater implements Serializable {\n\n    public enum ProcessInstanceField {\n        /**\n         * Corresponding to the first search key\n         */\n        STRING_INDEX_1,\n        /**\n         * Corresponding to the second search key\n         */\n        STRING_INDEX_2,\n        /**\n         * Corresponding to the third search key\n         */\n        STRING_INDEX_3,\n        /**\n         * Corresponding to the fourth search key\n         */\n        STRING_INDEX_4,\n        /**\n         * Corresponding to the fifth search key\n         */\n        STRING_INDEX_5\n    }\n\n    private final Map<ProcessInstanceField, Serializable> fields;\n\n    /**\n     * Default Constructor with no field to update.\n     */\n    public ProcessInstanceUpdater() {\n        fields = new HashMap<>();\n    }\n\n    /**\n     * Set the new value for the first search key, also known as string index.\n     *\n     * @param stringIndex\n     *        The new value for the first search key.\n     */\n    public void setStringIndex1(final String stringIndex) {\n        fields.put(ProcessInstanceField.STRING_INDEX_1, stringIndex);\n    }\n\n    /**\n     * Set the new value for the second search key, also known as string index.\n     *\n     * @param stringIndex\n     *        The new value for the second search key.\n     */\n    public void setStringIndex2(final String stringIndex) {\n        fields.put(ProcessInstanceField.STRING_INDEX_2, stringIndex);\n    }\n\n    /**\n     * Set the new value for the third search key, also known as string index.\n     *\n     * @param stringIndex\n     *        The new value for the third search key.\n     */\n    public void setStringIndex3(final String stringIndex) {\n        fields.put(ProcessInstanceField.STRING_INDEX_3, stringIndex);\n    }\n\n    /**\n     * Set the new value for the fourth search key, also known as string index.\n     *\n     * @param stringIndex\n     *        The new value for the fourth search keyx.\n     */\n    public void setStringIndex4(final String stringIndex) {\n        fields.put(ProcessInstanceField.STRING_INDEX_4, stringIndex);\n    }\n\n    /**\n     * Set the new value for the fifth search key, also known as string index.\n     *\n     * @param stringIndex\n     *        The new value for the fifth search key.\n     */\n    public void setStringIndex5(final String stringIndex) {\n        fields.put(ProcessInstanceField.STRING_INDEX_5, stringIndex);\n    }\n\n    /**\n     * Get the map of the fields to update and their new value.\n     *\n     * @return The map of the fields to update and their new value.\n     */\n    public Map<ProcessInstanceField, Serializable> getFields() {\n        return fields;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/impl/internal/ArchivedProcessInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.process.impl.internal;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.bpm.internal.NamedElementImpl;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstance;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Yanyan Liu\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class ArchivedProcessInstanceImpl extends NamedElementImpl implements ArchivedProcessInstance {\n\n    private static final long serialVersionUID = -1924361771241157184L;\n\n    private String state;\n\n    private Date startDate;\n\n    private long startedBy;\n\n    private long startedBySubstitute;\n\n    private Date endDate;\n\n    private Date archiveDate;\n\n    private Date lastUpdate;\n\n    private long sourceObjectId;\n\n    private int stateId;\n\n    private long processDefinitionId;\n\n    private String description;\n\n    private long rootProcessInstanceId;\n\n    private long callerId;\n\n    private String stringIndexValue1;\n\n    private String stringIndexValue2;\n\n    private String stringIndexValue3;\n\n    private String stringIndexValue4;\n\n    private String stringIndexValue5;\n\n    private String stringIndexLabel1;\n\n    private String stringIndexLabel2;\n\n    private String stringIndexLabel3;\n\n    private String stringIndexLabel4;\n\n    private String stringIndexLabel5;\n\n    public ArchivedProcessInstanceImpl(final String name) {\n        super(name);\n    }\n\n    @Override\n    public String getState() {\n        return state;\n    }\n\n    public void setState(final String state) {\n        this.state = state;\n    }\n\n    @Override\n    public Date getStartDate() {\n        return startDate;\n    }\n\n    public void setStartDate(final Date startDate) {\n        this.startDate = startDate;\n    }\n\n    @Override\n    public long getStartedBy() {\n        return startedBy;\n    }\n\n    public void setStartedBy(final long startedBy) {\n        this.startedBy = startedBy;\n    }\n\n    @Override\n    public long getStartedBySubstitute() {\n        return startedBySubstitute;\n    }\n\n    public void setStartedBySubstitute(final long startedBySubstitute) {\n        this.startedBySubstitute = startedBySubstitute;\n    }\n\n    @Override\n    public Date getEndDate() {\n        return endDate;\n    }\n\n    public void setEndDate(final Date endDate) {\n        this.endDate = endDate;\n    }\n\n    @Override\n    public Date getArchiveDate() {\n        return archiveDate;\n    }\n\n    public void setArchiveDate(final Date archiveDate) {\n        this.archiveDate = archiveDate;\n    }\n\n    @Override\n    public Date getLastUpdate() {\n        return lastUpdate;\n    }\n\n    public void setLastUpdate(final Date lastUpdate) {\n        this.lastUpdate = lastUpdate;\n    }\n\n    @Override\n    public long getSourceObjectId() {\n        return sourceObjectId;\n    }\n\n    public void setSourceObjectId(final long sourceObjectId) {\n        this.sourceObjectId = sourceObjectId;\n    }\n\n    @Override\n    public int getStateId() {\n        return stateId;\n    }\n\n    public void setStateId(final int stateId) {\n        this.stateId = stateId;\n    }\n\n    @Override\n    public long getProcessDefinitionId() {\n        return processDefinitionId;\n    }\n\n    public void setProcessDefinitionId(final long processDefinitionId) {\n        this.processDefinitionId = processDefinitionId;\n    }\n\n    @Override\n    public String getDescription() {\n        return description;\n    }\n\n    public void setDescription(final String description) {\n        this.description = description;\n    }\n\n    @Override\n    public long getRootProcessInstanceId() {\n        return rootProcessInstanceId;\n    }\n\n    public void setRootProcessInstanceId(final long rootProcessInstanceId) {\n        this.rootProcessInstanceId = rootProcessInstanceId;\n    }\n\n    @Override\n    public long getCallerId() {\n        return callerId;\n    }\n\n    public void setCallerId(final long callerId) {\n        this.callerId = callerId;\n    }\n\n    public void setStringIndexValue(final int index, final String value) {\n        switch (index) {\n            case 1:\n                stringIndexValue1 = value;\n                break;\n            case 2:\n                stringIndexValue2 = value;\n                break;\n            case 3:\n                stringIndexValue3 = value;\n                break;\n            case 4:\n                stringIndexValue4 = value;\n                break;\n            case 5:\n                stringIndexValue5 = value;\n                break;\n            default:\n                throw new IndexOutOfBoundsException(\"string index value must be between 1 and 5 (included)\");\n        }\n    }\n\n    @Override\n    public String getStringIndexValue(final int index) {\n        switch (index) {\n            case 1:\n                return stringIndexValue1;\n            case 2:\n                return stringIndexValue2;\n            case 3:\n                return stringIndexValue3;\n            case 4:\n                return stringIndexValue4;\n            case 5:\n                return stringIndexValue5;\n            default:\n                throw new IndexOutOfBoundsException(\"string index value must be between 1 and 5 (included)\");\n        }\n    }\n\n    public void setStringIndexLabel(final int index, final String label) {\n        switch (index) {\n            case 1:\n                stringIndexLabel1 = label;\n                break;\n            case 2:\n                stringIndexLabel2 = label;\n                break;\n            case 3:\n                stringIndexLabel3 = label;\n                break;\n            case 4:\n                stringIndexLabel4 = label;\n                break;\n            case 5:\n                stringIndexLabel5 = label;\n                break;\n            default:\n                throw new IndexOutOfBoundsException(\"string index label must be between 1 and 5 (included)\");\n        }\n    }\n\n    @Override\n    public String getStringIndexLabel(final int index) {\n        switch (index) {\n            case 1:\n                return stringIndexLabel1;\n            case 2:\n                return stringIndexLabel2;\n            case 3:\n                return stringIndexLabel3;\n            case 4:\n                return stringIndexLabel4;\n            case 5:\n                return stringIndexLabel5;\n            default:\n                throw new IndexOutOfBoundsException(\"string index label must be between 1 and 5 (included)\");\n        }\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = super.hashCode();\n        result = prime * result + (archiveDate == null ? 0 : archiveDate.hashCode());\n        result = prime * result + (int) (callerId ^ callerId >>> 32);\n        result = prime * result + (endDate == null ? 0 : endDate.hashCode());\n        result = prime * result + (lastUpdate == null ? 0 : lastUpdate.hashCode());\n        result = prime * result + (int) (processDefinitionId ^ processDefinitionId >>> 32);\n        result = prime * result + (int) (rootProcessInstanceId ^ rootProcessInstanceId >>> 32);\n        result = prime * result + (int) (sourceObjectId ^ sourceObjectId >>> 32);\n        result = prime * result + (startDate == null ? 0 : startDate.hashCode());\n        result = prime * result + (int) (startedBy ^ startedBy >>> 32);\n        result = prime * result + (int) (startedBySubstitute ^ startedBySubstitute >>> 32);\n        result = prime * result + (state == null ? 0 : state.hashCode());\n        result = prime * result + stateId;\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (!super.equals(obj)) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        final ArchivedProcessInstanceImpl other = (ArchivedProcessInstanceImpl) obj;\n        if (archiveDate == null) {\n            if (other.archiveDate != null) {\n                return false;\n            }\n        } else if (!archiveDate.equals(other.archiveDate)) {\n            return false;\n        }\n        if (callerId != other.callerId) {\n            return false;\n        }\n        if (endDate == null) {\n            if (other.endDate != null) {\n                return false;\n            }\n        } else if (!endDate.equals(other.endDate)) {\n            return false;\n        }\n        if (lastUpdate == null) {\n            if (other.lastUpdate != null) {\n                return false;\n            }\n        } else if (!lastUpdate.equals(other.lastUpdate)) {\n            return false;\n        }\n        if (processDefinitionId != other.processDefinitionId) {\n            return false;\n        }\n        if (rootProcessInstanceId != other.rootProcessInstanceId) {\n            return false;\n        }\n        if (sourceObjectId != other.sourceObjectId) {\n            return false;\n        }\n        if (startDate == null) {\n            if (other.startDate != null) {\n                return false;\n            }\n        } else if (!startDate.equals(other.startDate)) {\n            return false;\n        }\n        if (startedBy != other.startedBy) {\n            return false;\n        }\n        if (startedBySubstitute != other.startedBySubstitute) {\n            return false;\n        }\n        if (state == null) {\n            if (other.state != null) {\n                return false;\n            }\n        } else if (!state.equals(other.state)) {\n            return false;\n        }\n        if (stateId != other.stateId) {\n            return false;\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/impl/internal/ProblemImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.process.impl.internal;\n\nimport org.bonitasoft.engine.bpm.process.Problem;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class ProblemImpl implements Problem {\n\n    private static final long serialVersionUID = 267956968537265036L;\n\n    private final Level level;\n\n    private final String resourceId;\n\n    private final String resource;\n\n    private final String description;\n\n    public ProblemImpl(final Level level, final String resourceId, final String resource, final String description) {\n        super();\n        this.level = level;\n        this.resourceId = resourceId;\n        this.resource = resource;\n        this.description = description;\n    }\n\n    public ProblemImpl(final Level level, final long resourceId, final String resource, final String description) {\n        super();\n        this.level = level;\n        this.resourceId = String.valueOf(resourceId);\n        this.resource = resource;\n        this.description = description;\n    }\n\n    @Override\n    public Level getLevel() {\n        return level;\n    }\n\n    @Override\n    public String getResourceId() {\n        return resourceId;\n    }\n\n    @Override\n    public String getResource() {\n        return resource;\n    }\n\n    @Override\n    public String getDescription() {\n        return description;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + (description == null ? 0 : description.hashCode());\n        result = prime * result + (level == null ? 0 : level.hashCode());\n        result = prime * result + (resource == null ? 0 : resource.hashCode());\n        result = prime * result + (resourceId == null ? 0 : resourceId.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        final ProblemImpl other = (ProblemImpl) obj;\n        if (description == null) {\n            if (other.description != null) {\n                return false;\n            }\n        } else if (!description.equals(other.description)) {\n            return false;\n        }\n        if (level != other.level) {\n            return false;\n        }\n        if (resource == null) {\n            if (other.resource != null) {\n                return false;\n            }\n        } else if (!resource.equals(other.resource)) {\n            return false;\n        }\n        if (resourceId == null) {\n            if (other.resourceId != null) {\n                return false;\n            }\n        } else if (!resourceId.equals(other.resourceId)) {\n            return false;\n        }\n        return true;\n    }\n\n    @Override\n    public String toString() {\n        return \"ProblemImpl [level=\" + level + \", resourceId=\" + resourceId + \", resource=\" + resource\n                + \", description=\" + description + \"]\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/impl/internal/ProcessDeploymentInfoImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.process.impl.internal;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.bpm.internal.NamedElementImpl;\nimport org.bonitasoft.engine.bpm.process.ActivationState;\nimport org.bonitasoft.engine.bpm.process.ConfigurationState;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\n\n/**\n * @author Baptiste Mesta\n * @author Hongwen Zang\n * @author Celine Souchet\n */\npublic class ProcessDeploymentInfoImpl extends NamedElementImpl implements ProcessDeploymentInfo {\n\n    private static final long serialVersionUID = 1138141364845684630L;\n\n    private final String version;\n\n    private final String displayDescription;\n\n    private final Date deploymentDate;\n\n    private final long deployedBy;\n\n    private final ConfigurationState configurationState;\n\n    private final ActivationState activationState;\n\n    private final long processId;\n\n    private final String displayName;\n\n    private final Date lastUpdateDate;\n\n    private final String iconPath;\n\n    private final String description;\n\n    public ProcessDeploymentInfoImpl(final long id, final long processId, final String name, final String version,\n            final String description,\n            final Date deploymentDate, final long deployedBy, final ActivationState activationState,\n            final ConfigurationState configurationState,\n            final String displayName, final Date lastUpdateDate, final String iconPath,\n            final String displayDescription) {\n        super(name);\n        this.description = description;\n        this.displayDescription = displayDescription;\n        setId(id);\n        this.processId = processId;\n        this.version = version;\n        this.deploymentDate = deploymentDate;\n        this.deployedBy = deployedBy;\n        this.configurationState = configurationState;\n        this.activationState = activationState;\n        this.displayName = displayName;\n        this.lastUpdateDate = lastUpdateDate;\n        this.iconPath = iconPath;\n    }\n\n    @Override\n    public String getVersion() {\n        return version;\n    }\n\n    @Override\n    public String getDisplayDescription() {\n        return displayDescription;\n    }\n\n    @Override\n    public Date getDeploymentDate() {\n        return deploymentDate;\n    }\n\n    @Override\n    public long getDeployedBy() {\n        return deployedBy;\n    }\n\n    @Override\n    public long getProcessId() {\n        return processId;\n    }\n\n    @Override\n    public String getDisplayName() {\n        return displayName;\n    }\n\n    @Override\n    public Date getLastUpdateDate() {\n        return lastUpdateDate;\n    }\n\n    @Override\n    public String getIconPath() {\n        return iconPath;\n    }\n\n    @Override\n    public String getDescription() {\n        return description;\n    }\n\n    @Override\n    public ConfigurationState getConfigurationState() {\n        return configurationState;\n    }\n\n    @Override\n    public ActivationState getActivationState() {\n        return activationState;\n    }\n\n    @Override\n    public String toString() {\n        return \"ProcessDeploymentInfoImpl{\" +\n                \"version='\" + version + '\\'' +\n                \", displayDescription='\" + displayDescription + '\\'' +\n                \", deploymentDate=\" + deploymentDate +\n                \", deployedBy=\" + deployedBy +\n                \", configurationState=\" + configurationState +\n                \", activationState=\" + activationState +\n                \", processId=\" + processId +\n                \", displayName='\" + displayName + '\\'' +\n                \", lastUpdateDate=\" + lastUpdateDate +\n                \", iconPath='\" + iconPath + '\\'' +\n                \", description='\" + description + '\\'' +\n                \", namedElement='\" + super.toString() + '\\'' +\n                '}';\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/impl/internal/ProcessInstanceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.process.impl.internal;\n\nimport java.util.Date;\n\nimport lombok.ToString;\nimport org.bonitasoft.engine.bpm.internal.NamedElementImpl;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\n@ToString(callSuper = true)\npublic class ProcessInstanceImpl extends NamedElementImpl implements ProcessInstance {\n\n    private static final long serialVersionUID = 7745012384930764581L;\n\n    private String state;\n\n    private Date startDate;\n\n    private long startedBy;\n\n    private long startedBySubstitute;\n\n    private Date endDate;\n\n    private Date lastUpdate;\n\n    private long processDefinitionId;\n\n    private String description;\n\n    private long rootProcessInstanceId;\n\n    private long callerId;\n\n    private String stringIndex1;\n\n    private String stringIndex2;\n\n    private String stringIndex3;\n\n    private String stringIndex4;\n\n    private String stringIndex5;\n\n    private String stringIndexLabel1;\n\n    private String stringIndexLabel2;\n\n    private String stringIndexLabel3;\n\n    private String stringIndexLabel4;\n\n    private String stringIndexLabel5;\n\n    public ProcessInstanceImpl(final String name) {\n        super(name);\n    }\n\n    @Override\n    public String getState() {\n        return state;\n    }\n\n    public void setState(final String state) {\n        this.state = state;\n    }\n\n    @Override\n    public Date getStartDate() {\n        return startDate;\n    }\n\n    public void setStartDate(final Date startDate) {\n        this.startDate = startDate;\n    }\n\n    @Override\n    public long getStartedBy() {\n        return startedBy;\n    }\n\n    public void setStartedBy(final long startedBy) {\n        this.startedBy = startedBy;\n    }\n\n    @Override\n    public long getStartedBySubstitute() {\n        return startedBySubstitute;\n    }\n\n    public void setStartedBySubstitute(final long startedBySubstitute) {\n        this.startedBySubstitute = startedBySubstitute;\n    }\n\n    @Override\n    public Date getEndDate() {\n        return endDate;\n    }\n\n    public void setEndDate(final Date endDate) {\n        this.endDate = endDate;\n    }\n\n    @Override\n    public Date getLastUpdate() {\n        return lastUpdate;\n    }\n\n    public void setLastUpdate(final Date lastUpdate) {\n        this.lastUpdate = lastUpdate;\n    }\n\n    @Override\n    public long getProcessDefinitionId() {\n        return processDefinitionId;\n    }\n\n    public void setProcessDefinitionId(final long processDefinitionId) {\n        this.processDefinitionId = processDefinitionId;\n    }\n\n    @Override\n    public String getDescription() {\n        return description;\n    }\n\n    public void setDescription(final String description) {\n        this.description = description;\n    }\n\n    @Override\n    public long getRootProcessInstanceId() {\n        return rootProcessInstanceId;\n    }\n\n    public void setRootProcessInstanceId(final long rootProcessInstanceId) {\n        this.rootProcessInstanceId = rootProcessInstanceId;\n    }\n\n    @Override\n    public long getCallerId() {\n        return callerId;\n    }\n\n    public void setCallerId(final long callerId) {\n        this.callerId = callerId;\n    }\n\n    @Override\n    public String getStringIndex1() {\n        return stringIndex1;\n    }\n\n    public void setStringIndex1(final String stringIndex1) {\n        this.stringIndex1 = stringIndex1;\n    }\n\n    @Override\n    public String getStringIndex2() {\n        return stringIndex2;\n    }\n\n    public void setStringIndex2(final String stringIndex2) {\n        this.stringIndex2 = stringIndex2;\n    }\n\n    @Override\n    public String getStringIndex3() {\n        return stringIndex3;\n    }\n\n    public void setStringIndex3(final String stringIndex3) {\n        this.stringIndex3 = stringIndex3;\n    }\n\n    @Override\n    public String getStringIndex4() {\n        return stringIndex4;\n    }\n\n    public void setStringIndex4(final String stringIndex4) {\n        this.stringIndex4 = stringIndex4;\n    }\n\n    @Override\n    public String getStringIndex5() {\n        return stringIndex5;\n    }\n\n    public void setStringIndex5(final String stringIndex5) {\n        this.stringIndex5 = stringIndex5;\n    }\n\n    public String getStringIndexLabel1() {\n        return stringIndexLabel1;\n    }\n\n    public void setStringIndexLabel1(final String stringIndexLabel1) {\n        this.stringIndexLabel1 = stringIndexLabel1;\n    }\n\n    public String getStringIndexLabel2() {\n        return stringIndexLabel2;\n    }\n\n    public void setStringIndexLabel2(final String stringIndexLabel2) {\n        this.stringIndexLabel2 = stringIndexLabel2;\n    }\n\n    public String getStringIndexLabel3() {\n        return stringIndexLabel3;\n    }\n\n    public void setStringIndexLabel3(final String stringIndexLabel3) {\n        this.stringIndexLabel3 = stringIndexLabel3;\n    }\n\n    public String getStringIndexLabel4() {\n        return stringIndexLabel4;\n    }\n\n    public void setStringIndexLabel4(final String stringIndexLabel4) {\n        this.stringIndexLabel4 = stringIndexLabel4;\n    }\n\n    public String getStringIndexLabel5() {\n        return stringIndexLabel5;\n    }\n\n    public void setStringIndexLabel5(final String stringIndexLabel5) {\n        this.stringIndexLabel5 = stringIndexLabel5;\n    }\n\n    public void setStringIndexLabel(final int index, final String label) {\n        switch (index) {\n            case 1:\n                stringIndexLabel1 = label;\n                break;\n            case 2:\n                stringIndexLabel2 = label;\n                break;\n            case 3:\n                stringIndexLabel3 = label;\n                break;\n            case 4:\n                stringIndexLabel4 = label;\n                break;\n            case 5:\n                stringIndexLabel5 = label;\n                break;\n            default:\n                throw new IndexOutOfBoundsException(\"search key label must be between 1 and 5 (included)\");\n        }\n    }\n\n    @Override\n    public String getStringIndexLabel(final int index) {\n        switch (index) {\n            case 1:\n                return stringIndexLabel1;\n            case 2:\n                return stringIndexLabel2;\n            case 3:\n                return stringIndexLabel3;\n            case 4:\n                return stringIndexLabel4;\n            case 5:\n                return stringIndexLabel5;\n            default:\n                throw new IndexOutOfBoundsException(\"search key label must be between 1 and 5 (included)\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/supervisor/ProcessSupervisor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.supervisor;\n\nimport org.bonitasoft.engine.bpm.BonitaObject;\n\n/**\n * A supervisor of a process is responsible for what happens to the process. A supervisor can see\n * the tasks in the process, and can carry out process administration. A supervisor is defined in a ProcessSupervisor\n * object as a mapping of users, groups, or roles to the process supervisor (similar to actor mapping).\n * A process has one ProcessSupervisor; however, as this can be mapped to several users, either explicitly or by\n * mapping groups or roles, the process can be supervised by several people.\n *\n * @author Yanyan Liu\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n * @author Emmanuel Duchastenier\n */\npublic interface ProcessSupervisor extends BonitaObject {\n\n    long getSupervisorId();\n\n    long getProcessDefinitionId();\n\n    long getUserId();\n\n    long getGroupId();\n\n    long getRoleId();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/supervisor/ProcessSupervisorSearchDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.supervisor;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class ProcessSupervisorSearchDescriptor {\n\n    public static final String USER_ID = \"userId\";\n\n    public static final String GROUP_ID = \"groupId\";\n\n    public static final String ROLE_ID = \"roleId\";\n\n    public static final String ID = \"id\";\n\n    public static final String PROCESS_DEFINITION_ID = \"processDefId\";\n\n    // public static final String USER_FISRT_NAME = \"firstName\";\n    //\n    // public static final String USER_LAST_NAME = \"lastName\";\n    //\n    // public static final String USERNAME = \"userName\";\n    //\n    // public static final String GROUP_NAME = \"name\";\n    //\n    // public static final String GROUP_PARENT_PATH = \"parentPath\";\n    //\n    // public static final String ROLE_NAME = \"name\";\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/supervisor/SupervisorNotFoundException.java",
    "content": "/**\n * Copyright (C) 2021 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.supervisor;\n\nimport org.bonitasoft.engine.exception.NotFoundException;\n\n/**\n * @author Anthony Birembaut\n */\npublic class SupervisorNotFoundException extends NotFoundException {\n\n    private static final long serialVersionUID = 1829055978870789719L;\n\n    /**\n     * Constructs a new exception with the specified detail message.\n     *\n     * @param message\n     *        The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method).\n     */\n    public SupervisorNotFoundException(final String message) {\n        super(message);\n    }\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public SupervisorNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/supervisor/impl/ProcessSupervisorImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.supervisor.impl;\n\nimport org.bonitasoft.engine.bpm.supervisor.ProcessSupervisor;\n\n/**\n * @author Yanyan Liu\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class ProcessSupervisorImpl implements ProcessSupervisor {\n\n    private static final long serialVersionUID = 1034289223722146165L;\n\n    private long supervisorId;\n\n    private long processDefinitionId;\n\n    private long userId;\n\n    private long groupId;\n\n    private long roleId;\n\n    public ProcessSupervisorImpl(final long supervisorId, final long processDefinitionId) {\n        super();\n        this.supervisorId = supervisorId;\n        this.processDefinitionId = processDefinitionId;\n    }\n\n    public ProcessSupervisorImpl() {\n    }\n\n    @Override\n    public long getSupervisorId() {\n        return supervisorId;\n    }\n\n    @Override\n    public long getProcessDefinitionId() {\n        return processDefinitionId;\n    }\n\n    @Override\n    public long getUserId() {\n        return userId;\n    }\n\n    public void setId(final long supervisorId) {\n        this.supervisorId = supervisorId;\n    }\n\n    public void setProcessDefinitionId(final long processDefinitionId) {\n        this.processDefinitionId = processDefinitionId;\n    }\n\n    public void setUserId(final long userId) {\n        this.userId = userId;\n    }\n\n    @Override\n    public long getGroupId() {\n        return groupId;\n    }\n\n    public void setGroupId(final long groupId) {\n        this.groupId = groupId;\n    }\n\n    @Override\n    public long getRoleId() {\n        return roleId;\n    }\n\n    public void setRoleId(final long roleId) {\n        this.roleId = roleId;\n    }\n\n    public void setSupervisorId(final long supervisorId) {\n        this.supervisorId = supervisorId;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/supervisor/package-info.java",
    "content": "/**\n * Copyright (C) 2015 BonitaSoft S.A.\n * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n/**\n * <p>\n * </p>\n */\npackage org.bonitasoft.engine.bpm.supervisor;\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/AbstractApplicationCreator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.profile.Profile;\n\n/**\n * Describes the information about an {@link IApplication} to be created\n *\n * @see IApplication\n * @deprecated This class should no longer be used. Since 9.0.0, Applications should be created at startup.\n * @param <T> the concrete subtype of {@link AbstractApplicationCreator} (for returning self, subclasses must ensure to\n *        use the concrete instance)\n */\n@Deprecated(since = \"10.2.0\")\npublic abstract class AbstractApplicationCreator<T extends AbstractApplicationCreator<T>> implements Serializable {\n\n    private static final long serialVersionUID = -8837280485050745580L;\n\n    private final Map<ApplicationField, Serializable> fields;\n\n    /**\n     * Creates an instance of <code>ApplicationCreator</code> containing mandatory information.\n     * <p>The created {@link Application} will used the default layout.</p>\n     *\n     * @param token the {@code Application} token. The token will be part of application URL. It cannot be null or empty\n     *        and should contain only alpha numeric\n     *        characters and the following special characters '-', '.', '_' or '~'. In addition, the following words are\n     *        reserved key words and cannot be used\n     *        as token: 'api', 'content', 'theme'.\n     * @param displayName the <code>Application</code> display name. It cannot be null or empty\n     * @param version the <code>Application</code> version\n     * @see Application\n     */\n    public AbstractApplicationCreator(final String token, final String displayName, final String version) {\n        fields = new HashMap<>(3);\n        fields.put(ApplicationField.TOKEN, token);\n        fields.put(ApplicationField.VERSION, version);\n        fields.put(ApplicationField.DISPLAY_NAME, displayName);\n    }\n\n    /**\n     * Retrieves the {@link Application} token\n     *\n     * @return the <code>Application</code> token\n     * @see Application\n     */\n    public String getToken() {\n        return (String) fields.get(ApplicationField.TOKEN);\n    }\n\n    /**\n     * Defines the {@link Application} description and returns the current <code>ApplicationCreator</code>\n     *\n     * @param description the <code>Application</code> description\n     * @return the current <code>ApplicationCreator</code>\n     * @see Application\n     */\n    public T setDescription(final String description) {\n        fields.put(ApplicationField.DESCRIPTION, description);\n        return (T) this;\n    }\n\n    /**\n     * Defines the {@link Application} icon path and returns the current <code>ApplicationCreator</code>\n     *\n     * @param iconPath the <code>Application</code> icon path\n     * @return the current <code>ApplicationCreator</code>\n     * @see Application\n     * @deprecated since 7.13.0, use {@link #setIcon(String, byte[])}\n     */\n    @Deprecated(since = \"7.13.0\")\n    public T setIconPath(final String iconPath) {\n        fields.put(ApplicationField.ICON_PATH, iconPath);\n        return (T) this;\n    }\n\n    /**\n     * Defines the icon for the {@link Application}.\n     *\n     * @param fileName of the icon\n     * @param content of the icon\n     * @return the current builder\n     * @since 7.13.0\n     */\n    public T setIcon(String fileName, byte[] content) {\n        fields.put(ApplicationField.ICON_FILE_NAME, fileName);\n        fields.put(ApplicationField.ICON_CONTENT, content);\n        return (T) this;\n    }\n\n    /**\n     * Defines the identifier of the {@link Profile} related to this {@link Application} and returns the current\n     * <code>ApplicationCreator</code>\n     *\n     * @param profileId the <code>Profile</code> identifier\n     * @return the current <code>ApplicationCreator</code>\n     * @see Application\n     * @see Profile\n     */\n    public T setProfileId(final Long profileId) {\n        fields.put(ApplicationField.PROFILE_ID, profileId);\n        return (T) this;\n    }\n\n    /**\n     * Retrieves all fields defined in this <code>ApplicationCreator</code>\n     *\n     * @return a {@link Map}<{@link ApplicationField}, {@link Serializable}> containing all fields defined in this\n     *         <code>ApplicationCreator</code>\n     * @see ApplicationField\n     */\n    public Map<ApplicationField, Serializable> getFields() {\n        return fields;\n    }\n\n    /** Test whether application is an application link. */\n    public abstract boolean isLink();\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = getClass().hashCode();\n        result = prime * result + (fields == null ? 0 : fields.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        final AbstractApplicationCreator other = (AbstractApplicationCreator) obj;\n        if (fields == null) {\n            if (other.fields != null) {\n                return false;\n            }\n        } else if (!fields.equals(other.fields)) {\n            return false;\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/AbstractApplicationUpdater.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.profile.Profile;\n\n/**\n * Allows to define which {@link IApplication} fields will be updated\n *\n * @see IApplication\n * @deprecated This class should no longer be used. Since 9.0.0, Applications should be updated at startup.\n * @param <T> the concrete subtype of {@link AbstractApplicationUpdater} (for returning self, subclasses must ensure to\n *        use the concrete instance)\n */\n@Deprecated(since = \"10.2.0\")\npublic abstract class AbstractApplicationUpdater<T extends AbstractApplicationUpdater<T>> implements Serializable {\n\n    private static final long serialVersionUID = -5301609836484089900L;\n\n    private final Map<ApplicationField, Serializable> fields;\n\n    /**\n     * Creates an instance of <code>ApplicationUpdater</code>\n     */\n    public AbstractApplicationUpdater() {\n        fields = new HashMap<>(8);\n    }\n\n    /**\n     * Retrieves all fields to be updated\n     *\n     * @return a {@link Map}<{@link ApplicationField}, {@link Serializable}> containing all fields to be updated\n     * @see ApplicationField\n     */\n    public Map<ApplicationField, Serializable> getFields() {\n        return fields;\n    }\n\n    /**\n     * Defines the new value for the {@link IApplication} token. It cannot be empty or null and should contain only\n     * alpha numeric characters and the following special characters '-', '.', '_' or '~'.\n     *\n     * @param token the new value for the {@code Application} token\n     * @return the current {@code ApplicationUpdater}\n     * @see IApplication\n     */\n    public T setToken(final String token) {\n        fields.put(ApplicationField.TOKEN, token);\n        return (T) this;\n    }\n\n    /**\n     * Defines the new value for the {@link IApplication} display name. It cannot be empty or null.\n     *\n     * @param displayName the new value for the {@code Application} display name\n     * @return the current {@code ApplicationUpdater}\n     * @see IApplication\n     */\n    public T setDisplayName(final String displayName) {\n        fields.put(ApplicationField.DISPLAY_NAME, displayName);\n        return (T) this;\n    }\n\n    /**\n     * Defines the new value for the {@link IApplication} version\n     *\n     * @param version the new value for the {@code Application} version\n     * @return the current {@code ApplicationUpdater}\n     * @see IApplication\n     */\n    public T setVersion(final String version) {\n        fields.put(ApplicationField.VERSION, version);\n        return (T) this;\n    }\n\n    /**\n     * Defines the new value for the {@link IApplication} description\n     *\n     * @param description the new value for the {@code Application} description\n     * @return the current {@code ApplicationUpdater}\n     * @see IApplication\n     */\n    public T setDescription(final String description) {\n        fields.put(ApplicationField.DESCRIPTION, description);\n        return (T) this;\n    }\n\n    /**\n     * Defines the new value for the {@link IApplication} icon path\n     *\n     * @param iconPath the new value for the {@code Application} icon path\n     * @return the current {@code ApplicationUpdater}\n     * @see IApplication\n     * @deprecated since 7.13.0, use {@link #setIcon(String, byte[])}\n     */\n    @Deprecated(since = \"7.13.0\")\n    public T setIconPath(final String iconPath) {\n        fields.put(ApplicationField.ICON_PATH, iconPath);\n        return (T) this;\n    }\n\n    /**\n     * Defines the new icon for the {@link IApplication}.\n     * <p/>\n     * The icons are accessible using {@link org.bonitasoft.engine.api.ApplicationAPI#getIconOfApplication(long)}\n     * Calling that method with {@code setIcon(null, null)} will remove the icon.\n     *\n     * @param iconFileName of the icon\n     * @param content of the icon\n     * @return the current builder\n     * @since 7.13.0\n     */\n    public T setIcon(String iconFileName, byte[] content) {\n        fields.put(ApplicationField.ICON_FILE_NAME, iconFileName);\n        fields.put(ApplicationField.ICON_CONTENT, content);\n        return (T) this;\n    }\n\n    /**\n     * Defines the new value for the {@link IApplication} state\n     *\n     * @param state the new value for the {@code Application} state\n     * @return the current {@code ApplicationUpdater}\n     * @see IApplication\n     */\n    public T setState(final String state) {\n        fields.put(ApplicationField.STATE, state);\n        return (T) this;\n    }\n\n    /**\n     * Defines the identifier of the new {@link Profile} associated to the {@link IApplication}\n     *\n     * @param profileId the identifier of {@code Profile} associated to the {@code Application}\n     * @return the current {@code ApplicationUpdater}\n     * @see IApplication\n     * @see Profile\n     */\n    public T setProfileId(final Long profileId) {\n        fields.put(ApplicationField.PROFILE_ID, profileId);\n        return (T) this;\n    }\n\n    /**\n     * Determines if this updater has at least one field to update\n     *\n     * @return true if there is at least one field to update; false otherwise\n     */\n    public boolean hasFields() {\n        return !getFields().isEmpty();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationCreator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application;\n\n/**\n * Describes the information about a Legacy {@link Application} to be created\n *\n * @author Elias Ricken de Medeiros\n * @see Application\n * @since 7.0.0\n * @deprecated This class should no longer be used. Since 9.0.0, Applications should be created at startup.\n */\n@Deprecated(since = \"10.2.0\")\npublic class ApplicationCreator extends AbstractApplicationCreator<ApplicationCreator> {\n\n    private static final long serialVersionUID = -916041825489100271L;\n\n    /**\n     * Creates an instance of <code>ApplicationCreator</code> containing mandatory information.\n     * <p>The created {@link Application} will used the default layout.</p>\n     *\n     * @param token the {@code Application} token. The token will be part of application URL. It cannot be null or empty\n     *        and should contain only alpha numeric\n     *        characters and the following special characters '-', '.', '_' or '~'. In addition, the following words are\n     *        reserved key words and cannot be used\n     *        as token: 'api', 'content', 'theme'.\n     * @param displayName the <code>Application</code> display name. It cannot be null or empty\n     * @param version the <code>Application</code> version\n     * @see Application\n     */\n    public ApplicationCreator(final String token, final String displayName, final String version) {\n        super(token, displayName, version);\n    }\n\n    @Override\n    public boolean isLink() {\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationField.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application;\n\nimport org.bonitasoft.engine.profile.Profile;\n\n/**\n * Contains fields that can be used by {@link ApplicationCreator} and {@link ApplicationUpdater}\n *\n * @author Elias Ricken de Medeiros\n * @see ApplicationCreator\n * @see ApplicationUpdater\n * @since 7.0.0\n * @deprecated This class should no longer be used. Since 9.0.0, Applications should be updated at startup.\n */\n@Deprecated(since = \"10.2.0\")\npublic enum ApplicationField {\n\n    /**\n     * References the {@link IApplication} token\n     *\n     * @see IApplication\n     */\n    TOKEN,\n\n    /**\n     * References the {@link IApplication} display name\n     *\n     * @see IApplication\n     */\n    DISPLAY_NAME,\n\n    /**\n     * References the {@link IApplication} version\n     *\n     * @see IApplication\n     */\n    VERSION,\n\n    /**\n     * References the {@link IApplication} description\n     *\n     * @see IApplication\n     */\n    DESCRIPTION,\n\n    /**\n     * References the {@link IApplication} icon path\n     *\n     * @see IApplication\n     * @deprecated since 7.13.0, use {@link #ICON_CONTENT} and {@link #ICON_FILE_NAME} instead\n     */\n    @Deprecated(since = \"7.13.0\")\n    ICON_PATH,\n\n    /**\n     * byte array content of the icon of the {@link IApplication}\n     *\n     * @since 7.13.0\n     */\n    ICON_CONTENT,\n\n    /**\n     * Filename of the icon of the {@link IApplication}\n     *\n     * @since 7.13.0\n     */\n    ICON_FILE_NAME,\n\n    /**\n     * References the {@link IApplication} state\n     *\n     * @see IApplication\n     */\n    STATE,\n\n    /**\n     * References the identifier of the {@link Profile} associated to the {@link IApplication}\n     *\n     * @see IApplication\n     * @see Profile\n     */\n    PROFILE_ID,\n\n    /**\n     * References the identifier of the {@link ApplicationPage} defined as the {@link Application} home page\n     *\n     * @see org.bonitasoft.engine.business.application.ApplicationPage\n     * @see org.bonitasoft.engine.business.application.Application\n     */\n    HOME_PAGE_ID(Application.class),\n\n    /**\n     * References the identifier of the {@link org.bonitasoft.engine.page.Page} defined as the {@link Application}\n     * layout.\n     *\n     * @see org.bonitasoft.engine.page.Page\n     * @see org.bonitasoft.engine.business.application.Application\n     * @since 7.0.0\n     */\n    LAYOUT_ID(Application.class),\n\n    /**\n     * References the identifier of the {@link org.bonitasoft.engine.page.Page} defined as the {@link Application}\n     * theme.\n     *\n     * @see org.bonitasoft.engine.page.Page\n     * @see org.bonitasoft.engine.business.application.Application\n     * @since 7.0.0\n     */\n    THEME_ID(Application.class);\n\n    /** The class which support this type of field */\n    private Class<? extends IApplication> supportingClass;\n\n    /**\n     * Private Constructor for fields which are suitable for all application types.\n     */\n    private ApplicationField() {\n        this(IApplication.class);\n    }\n\n    /**\n     * Private Constructor for fields which are suitable only for a particular application type (e.g. Legacy, but not\n     * Link).\n     *\n     * @param appropriateClazz the class which support this type of field.\n     */\n    private ApplicationField(Class<? extends IApplication> appropriateClazz) {\n        supportingClass = appropriateClazz;\n    }\n\n    /**\n     * Test whether this application field is suitable for a particular application type\n     *\n     * @param clazz the application type to test (usually {@link Application} for legacy applications of\n     *        {@link ApplicationLink})\n     * @return\n     */\n    public boolean isForClass(Class<? extends IApplication> clazz) {\n        return supportingClass.isAssignableFrom(clazz);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationImportPolicy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application;\n\n/**\n * Contains available policies during {@link Application}s import\n *\n * @author Elias Ricken de Medeiros\n * @see org.bonitasoft.engine.business.application.Application\n */\npublic enum ApplicationImportPolicy {\n\n    /**\n     * Import will fail on import an existent {@link org.bonitasoft.engine.business.application.Application} or\n     * {@link org.bonitasoft.engine.business.application.ApplicationPage}\n     */\n    FAIL_ON_DUPLICATES,\n\n    /**\n     * Import will replace the applications that exist with the same token.\n     */\n    REPLACE_DUPLICATES\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationLinkCreator.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\n/**\n * Describes the information about an {@link ApplicationLink} to be created\n *\n * @see ApplicationLink\n * @since 10.2.0\n * @deprecated This class should no longer be used. Since 9.0.0, Applications should be created at startup.\n */\n@Deprecated(since = \"10.2.0\")\npublic class ApplicationLinkCreator extends AbstractApplicationCreator<ApplicationLinkCreator> {\n\n    private static final long serialVersionUID = 5045658936235401181L;\n    private transient Map<ApplicationField, Serializable> fieldsCheckedMap;\n\n    /**\n     * Creates an instance of <code>ApplicationCreator</code> containing mandatory information.\n     *\n     * @param token the {@code ApplicationLink} token. The token will be part of application URL. It cannot be null or\n     *        empty and should contain only alpha numeric\n     *        characters and the following special characters '-', '.', '_' or '~'. In addition, the following words are\n     *        reserved key words and cannot be used\n     *        as token: 'api', 'content', 'theme'.\n     * @param displayName the <code>ApplicationLink</code> display name. It cannot be null or empty\n     * @param version the <code>ApplicationLink</code> version\n     * @see ApplicationLink\n     */\n    public ApplicationLinkCreator(final String token, final String displayName, final String version) {\n        super(token, displayName, version);\n    }\n\n    @Override\n    public boolean isLink() {\n        return true;\n    }\n\n    @Override\n    public Map<ApplicationField, Serializable> getFields() {\n        // make sure no field for Legacy Application and not suitable for Application Link will get inserted there\n        if (fieldsCheckedMap == null) {\n            fieldsCheckedMap = new CheckedApplicationFieldMap(super.getFields(),\n                    k -> k.isForClass(ApplicationLink.class));\n        }\n        return fieldsCheckedMap;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationLinkUpdater.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\n/**\n * Allows to define which {@link ApplicationLink} fields will be updated\n *\n * @see ApplicationLink\n * @since 10.2.0\n * @deprecated This class should no longer be used. Since 9.0.0, Applications should be updated at startup.\n */\n@Deprecated(since = \"10.2.0\")\npublic class ApplicationLinkUpdater extends AbstractApplicationUpdater<ApplicationLinkUpdater> {\n\n    private static final long serialVersionUID = 1732835829535757371L;\n    private transient Map<ApplicationField, Serializable> fieldsCheckedMap;\n\n    @Override\n    public Map<ApplicationField, Serializable> getFields() {\n        // make sure no field for Legacy Application and not suitable for Application Link will get inserted there\n        if (fieldsCheckedMap == null) {\n            fieldsCheckedMap = new CheckedApplicationFieldMap(super.getFields(),\n                    k -> k.isForClass(ApplicationLink.class));\n        }\n        return fieldsCheckedMap;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationMenuCreator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application;\n\nimport java.io.Serializable;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Describes the information about an {@link ApplicationMenu} to be created\n *\n * @author Elias Ricken de Medeiros\n * @since 6.4\n * @see ApplicationMenu\n */\npublic class ApplicationMenuCreator implements Serializable {\n\n    private static final long serialVersionUID = 5253969343647340983L;\n\n    private final Map<ApplicationMenuField, Serializable> fields;\n\n    /**\n     * Creates an instance of {@code ApplicationMenuCreator}\n     *\n     * @param applicationId the identifier of related {@link org.bonitasoft.engine.business.application.Application}\n     * @param displayName the {@link org.bonitasoft.engine.business.application.ApplicationMenu} display name\n     * @param applicationPageId the identifier of related\n     *        {@link org.bonitasoft.engine.business.application.ApplicationPage}\n     * @see ApplicationMenu\n     */\n    public ApplicationMenuCreator(final Long applicationId, final String displayName, final Long applicationPageId) {\n        this(applicationId, displayName);\n        fields.put(ApplicationMenuField.APPLICATION_PAGE_ID, applicationPageId);\n    }\n\n    /**\n     * Creates an instance of {@code ApplicationMenuCreator}\n     *\n     * @param applicationId the identifier of related {@link Application}\n     * @param displayName the {@link ApplicationMenu} display name\n     * @see ApplicationMenu\n     * @see org.bonitasoft.engine.business.application.Application\n     */\n    public ApplicationMenuCreator(final Long applicationId, final String displayName) {\n        fields = new HashMap<ApplicationMenuField, Serializable>(4);\n        fields.put(ApplicationMenuField.DISPLAY_NAME, displayName);\n        fields.put(ApplicationMenuField.APPLICATION_ID, applicationId);\n    }\n\n    /**\n     * Defines the identifier of parent {@link ApplicationMenu}\n     *\n     * @param parentId the identifier of parent {@code ApplicationMenu}\n     * @return\n     */\n    public ApplicationMenuCreator setParentId(final long parentId) {\n        fields.put(ApplicationMenuField.PARENT_ID, parentId);\n        return this;\n    }\n\n    /**\n     * Retrieves the identifier of the parent {@link ApplicationMenu}. If no parent is defined this method will return\n     * null.\n     *\n     * @return the identifier of the parent {@code ApplicationMenu} or null if no parent is defined\n     * @see org.bonitasoft.engine.business.application.ApplicationMenu\n     */\n    public Long getParentId() {\n        return (Long) fields.get(ApplicationMenuField.PARENT_ID);\n    }\n\n    /**\n     * Retrieves all fields defined in this {@code ApplicationMenuCreator}\n     *\n     * @return a {@link Map}<{@link ApplicationMenuField}, {@link Serializable}> containing all fields defined in this\n     *         {@code ApplicationMenuCreator}\n     */\n    public Map<ApplicationMenuField, Serializable> getFields() {\n        return Collections.unmodifiableMap(fields);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationMenuField.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application;\n\n/**\n * Contains fields used by {@link ApplicationMenuCreator} and {@link ApplicationMenuUpdater}\n *\n * @author Elias Ricken de Medeiros\n * @since 6.4\n */\npublic enum ApplicationMenuField {\n\n    /**\n     * References the {@link ApplicationMenu} display name\n     *\n     * @see ApplicationMenu\n     */\n    DISPLAY_NAME,\n\n    /**\n     * References the identifier of {@link Application} related to the {@link ApplicationMenu}\n     *\n     * @see ApplicationMenu\n     * @see Application\n     */\n    APPLICATION_ID,\n\n    /**\n     * References the identifier of {@link ApplicationPage} related to the {@link ApplicationMenu}\n     *\n     * @see ApplicationMenu\n     * @see ApplicationPage\n     */\n    APPLICATION_PAGE_ID,\n\n    /**\n     * References the identifier of parent {@link ApplicationMenu}\n     *\n     * @see ApplicationMenu\n     */\n    PARENT_ID,\n\n    /**\n     * References the {@link ApplicationMenu} index\n     *\n     * @see ApplicationMenu\n     */\n    INDEX;\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationMenuNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application;\n\nimport org.bonitasoft.engine.exception.NotFoundException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ApplicationMenuNotFoundException extends NotFoundException {\n\n    private static final long serialVersionUID = -5011088434149162619L;\n\n    public ApplicationMenuNotFoundException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationMenuSearchDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application;\n\nimport org.bonitasoft.engine.search.SearchOptions;\n\n/**\n * Defines the fields that can be used in the {@link SearchOptions} when searching for {@link ApplicationMenu}s\n *\n * @author Elias Ricken de Medeiros\n * @since 6.4\n * @see SearchOptions\n * @see ApplicationMenu\n * @see ApplicationAPI#searchApplicationMenus(SearchOptions)\n */\npublic class ApplicationMenuSearchDescriptor {\n\n    /**\n     * Used to filter or order by {@link ApplicationMenu} identifier\n     *\n     * @see ApplicationMenu\n     */\n    public static final String ID = \"id\";\n\n    /**\n     * Used to filter or order by {@link ApplicationMenu} display name\n     *\n     * @see ApplicationMenu\n     */\n    public static final String DISPLAY_NAME = \"displayName\";\n\n    /**\n     * Used to filter or order by the identifier of {@link ApplicationPage} related to the {@link ApplicationMenu}\n     *\n     * @see ApplicationMenu\n     * @see ApplicationPage\n     */\n    public static final String APPLICATION_PAGE_ID = \"applicationPageId\";\n\n    /**\n     * Used to filter or order by the identifier of {@link Application} containing the {@link ApplicationMenu}\n     *\n     * @see ApplicationMenu\n     * @see Application\n     */\n    public static final String APPLICATION_ID = \"applicationId\";\n\n    /**\n     * Used to filter or order by {@link ApplicationMenu} index\n     *\n     * @see ApplicationMenu\n     */\n    public static final String INDEX = \"index\";\n\n    /**\n     * Used to filter or order by the identifier of parent {@link ApplicationMenu}\n     *\n     * @see ApplicationMenu\n     */\n    public static final String PARENT_ID = \"parentId\";\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationMenuUpdater.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ApplicationMenuUpdater implements Serializable {\n\n    private final Map<ApplicationMenuField, Serializable> fields;\n\n    public ApplicationMenuUpdater() {\n        fields = new HashMap<ApplicationMenuField, Serializable>(4);\n    }\n\n    /**\n     * Retrieves all fields to be updated\n     *\n     * @return a {@link Map}<{@link ApplicationMenuField}, {@link Serializable}> containing all fields to be updated\n     * @see ApplicationMenuField\n     */\n    public Map<ApplicationMenuField, Serializable> getFields() {\n        return fields;\n    }\n\n    /**\n     * Defines the identifier of the new {@link ApplicationPage} related to the\n     * {@link org.bonitasoft.engine.business.application.ApplicationMenu}. Use\n     * {@code null} to reference no {@code ApplicationPage}.\n     *\n     * @param applicationPageId the identifier of new related {@code ApplicationPage}\n     * @return the current {@code ApplicationMenuUpdater}\n     * @see org.bonitasoft.engine.business.application.ApplicationPage\n     * @see org.bonitasoft.engine.business.application.ApplicationMenu\n     */\n    public ApplicationMenuUpdater setApplicationPageId(final Long applicationPageId) {\n        fields.put(ApplicationMenuField.APPLICATION_PAGE_ID, applicationPageId);\n        return this;\n    }\n\n    /**\n     * Defines the new value for the {@link ApplicationMenu} display name\n     *\n     * @param displayName the new value for the {@code ApplicationMenu} display name\n     * @return the current {@code ApplicationMenuUpdater}\n     * @see ApplicationMenu\n     */\n    public ApplicationMenuUpdater setDisplayName(final String displayName) {\n        fields.put(ApplicationMenuField.DISPLAY_NAME, displayName);\n        return this;\n    }\n\n    /**\n     * Defines the new value for the {@link ApplicationMenu} index\n     *\n     * @param index the new value for the {@code ApplicationMenu} index\n     * @return the current {@code ApplicationMenuUpdater}\n     * @see ApplicationMenu\n     */\n    public ApplicationMenuUpdater setIndex(final int index) {\n        fields.put(ApplicationMenuField.INDEX, index);\n        return this;\n    }\n\n    /**\n     * Defines the identifier of the new parent {@link ApplicationMenu}.Use {@code null} to reference no\n     * {@code ApplicationMenu}\n     *\n     * @param parentId the identifier of the new parent {@link ApplicationMenu}\n     * @return the current {@code ApplicationMenuUpdater}\n     * @see ApplicationMenu\n     */\n    public ApplicationMenuUpdater setParentId(final Long parentId) {\n        fields.put(ApplicationMenuField.PARENT_ID, parentId);\n        return this;\n    }\n\n    /**\n     * Determines if this updater has at least one field to update\n     *\n     * @return true if there is at least one field to update; false otherwise\n     */\n    public boolean hasFields() {\n        return !getFields().isEmpty();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application;\n\nimport org.bonitasoft.engine.exception.NotFoundException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ApplicationNotFoundException extends NotFoundException {\n\n    private static final long serialVersionUID = -4073233652785845623L;\n\n    public ApplicationNotFoundException(final long applicationId) {\n        super(\"Unable to find the application with id '\" + applicationId + \"'\");\n    }\n\n    public ApplicationNotFoundException(final String applicationToken) {\n        super(\"Unable to find the application with token '\" + applicationToken + \"'\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationPageNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application;\n\nimport org.bonitasoft.engine.exception.NotFoundException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ApplicationPageNotFoundException extends NotFoundException {\n\n    private static final long serialVersionUID = -5011088434149162619L;\n\n    public ApplicationPageNotFoundException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationPageSearchDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application;\n\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.engine.search.SearchOptions;\n\n/**\n * Defines the fields that can be used in the {@link SearchOptions} when searching for {@link ApplicationPage}s\n *\n * @author Elias Ricken de Medeiros\n * @since 6.4\n * @see ApplicationPage\n * @see SearchOptions\n * @see ApplicationAPI#searchApplicationPages(SearchOptions)\n */\npublic class ApplicationPageSearchDescriptor {\n\n    /**\n     * Used to filter or order by {@link ApplicationPage} identifier\n     *\n     * @see ApplicationPage\n     */\n    public static final String ID = \"id\";\n\n    /**\n     * Used to filter or order by {@link ApplicationPage} token\n     *\n     * @see ApplicationPage\n     */\n    public static final String TOKEN = \"token\";\n\n    /**\n     * Used to filter or order by the identifier of {@link Application} associated to the {@link ApplicationPage}\n     *\n     * @see ApplicationPage\n     * @see Application\n     */\n    public static final String APPLICATION_ID = \"applicationId\";\n\n    /**\n     * Used to filter or order by the identifier of {@link Page} referenced by the {@link ApplicationPage}\n     *\n     * @see ApplicationPage\n     * @see Page\n     */\n    public static final String PAGE_ID = \"pageId\";\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationSearchDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application;\n\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.engine.search.SearchOptions;\n\n/**\n * Defines the fields that can be used in the {@link org.bonitasoft.engine.search.SearchOptions} when searching for\n * {@link Application}s\n *\n * @author Elias Ricken de Medeiros\n * @since 6.4\n * @see org.bonitasoft.engine.search.SearchOptions\n * @see Application\n * @see org.bonitasoft.engine.api.ApplicationAPI#searchApplications(SearchOptions)\n */\npublic class ApplicationSearchDescriptor {\n\n    /**\n     * Used to filter or order by the <code>Application</code> identifier\n     */\n    public static final String ID = \"id\";\n\n    /**\n     * Used to filter or order by the <code>Application</code> token\n     */\n    public static final String TOKEN = \"token\";\n\n    /**\n     * Used to filter or order by the <code>Application</code> display name\n     */\n    public static final String DISPLAY_NAME = \"displayName\";\n\n    /**\n     * Used to filter or order by the <code>Application</code> version\n     */\n    public static final String VERSION = \"version\";\n\n    /**\n     * Used to filter or order by the <code>Application</code> icon path\n     */\n    public static final String ICON_PATH = \"iconPath\";\n\n    /**\n     * Used to filter or order by the <code>Application</code> creation date\n     */\n    public static final String CREATION_DATE = \"creationDate\";\n\n    /**\n     * Used to filter or order by the identifier of the user that created the <code>Application</code>\n     */\n    public static final String CREATED_BY = \"createdBy\";\n\n    /**\n     * Used to filter or order by the <code>Application</code> last update date\n     */\n    public static final String LAST_UPDATE_DATE = \"lastUpdateDate\";\n\n    /**\n     * Used to filter or order by the identifier of the user that last updated the <code>Application</code>\n     */\n    public static final String UPDATED_BY = \"updatedBy\";\n\n    /**\n     * Used to filter or order by the <code>Application</code> state. The possible values are\n     * {@link ApplicationState#ACTIVATED#name()} and\n     * {@link ApplicationState#DEACTIVATED#name()}\n     *\n     * @see ApplicationState\n     */\n    public static final String STATE = \"state\";\n\n    /**\n     * Used to filter or order by the identifier of {@link org.bonitasoft.engine.profile.Profile} associated to the\n     * {@link org.bonitasoft.engine.business.application.Application}.\n     *\n     * @see org.bonitasoft.engine.profile.Profile\n     * @see org.bonitasoft.engine.business.application.Application\n     */\n    public static final String PROFILE_ID = \"profileId\";\n\n    /**\n     * Used to filter or order by the identifier of {@link Page} set as {@link Application} layout.\n     *\n     * @since 7.0.0\n     * @see Page\n     * @see Application\n     */\n    public static final String LAYOUT_ID = \"layoutId\";\n\n    /**\n     * Used to filter or order by the identifier of {@link Page} set as {@link Application} theme.\n     *\n     * @since 7.0.0\n     * @see Page\n     * @see Application\n     */\n    public static final String THEME_ID = \"themeId\";\n\n    /**\n     * Used to filter by the identifier of {@link User} that has access to the {@link Application}.\n     *\n     * @since 7.13.0\n     * @see User\n     * @see Application\n     */\n    public static final String USER_ID = \"userId\";\n\n    /**\n     * Used to filter or order <code>Application</code> items by legacy or link criteria\n     *\n     * @since 10.2.0 for some Subscription editions only\n     */\n    public static final String LINK = \"link\";\n    // keeping this constant here for convenience reasons\n    // having a subscription descriptor with just this field would not be user-friendly\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationUpdater.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application;\n\n/**\n * Allows to define which {@link Application} fields will be updated\n *\n * @author Elias Ricken de Medeiros\n * @see Application\n * @since 7.0.0\n * @deprecated This class should no longer be used. Since 9.0.0, Applications should be updated at startup.\n */\n@Deprecated(since = \"10.2.0\")\npublic class ApplicationUpdater extends AbstractApplicationUpdater<ApplicationUpdater> {\n\n    private static final long serialVersionUID = 4565052647320534796L;\n\n    /**\n     * Defines the identifier of the new {@link org.bonitasoft.engine.business.application.ApplicationPage} defined as\n     * the {@link Application} home page\n     *\n     * @param applicationPageId the identifier of {@code ApplicationPage} associated to the {@code Application}\n     * @return the current {@code ApplicationUpdater}\n     * @see Application\n     * @see org.bonitasoft.engine.business.application.ApplicationPage\n     */\n    public ApplicationUpdater setHomePageId(final Long applicationPageId) {\n        getFields().put(ApplicationField.HOME_PAGE_ID, applicationPageId);\n        return this;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/CheckedApplicationFieldMap.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application;\n\nimport java.io.Serializable;\nimport java.text.MessageFormat;\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.function.Predicate;\n\n/**\n * A Map which first checks that the key {@link ApplicationField} is correct.\n */\nclass CheckedApplicationFieldMap implements Map<ApplicationField, Serializable> {\n\n    private final Map<ApplicationField, Serializable> m;\n    private final Predicate<ApplicationField> isValidKey;\n\n    CheckedApplicationFieldMap(Map<ApplicationField, Serializable> m, Predicate<ApplicationField> isValidKey) {\n        this.m = Objects.requireNonNull(m);\n        this.isValidKey = Objects.requireNonNull(isValidKey);\n    }\n\n    private ApplicationField checkKey(ApplicationField key) {\n        if (!isValidKey.test(key)) {\n            throw new IllegalArgumentException(\n                    MessageFormat.format(\"Attempt to insert {0} in a specialized map which does not support it.\",\n                            key.name()));\n        }\n        return key;\n    }\n\n    @Override\n    public int size() {\n        return m.size();\n    }\n\n    @Override\n    public boolean isEmpty() {\n        return m.isEmpty();\n    }\n\n    @Override\n    public boolean containsKey(Object key) {\n        return m.containsKey(key);\n    }\n\n    @Override\n    public boolean containsValue(Object value) {\n        return m.containsValue(value);\n    }\n\n    @Override\n    public Serializable get(Object key) {\n        return m.get(key);\n    }\n\n    @Override\n    public Serializable put(ApplicationField key, Serializable value) {\n        return m.put(checkKey(key), value);\n    }\n\n    @Override\n    public Serializable remove(Object key) {\n        return m.remove(key);\n    }\n\n    @Override\n    public void putAll(Map<? extends ApplicationField, ? extends Serializable> map) {\n        map.keySet().forEach(this::checkKey);\n        m.putAll(map);\n    }\n\n    @Override\n    public void clear() {\n        m.clear();\n    }\n\n    @Override\n    public Set<ApplicationField> keySet() {\n        return m.keySet();\n    }\n\n    @Override\n    public Collection<Serializable> values() {\n        return m.values();\n    }\n\n    @Override\n    public Set<Entry<ApplicationField, Serializable>> entrySet() {\n        return m.entrySet();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/InternalProfiles.java",
    "content": "/**\n * Copyright (C) 2021 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application;\n\npublic enum InternalProfiles {\n\n    INTERNAL_PROFILE_SUPER_ADMIN(\"_BONITA_INTERNAL_PROFILE_SUPER_ADMIN\", ApplicationVisibility.TECHNICAL_USER),\n\n    INTERNAL_PROFILE_ALL(\"_BONITA_INTERNAL_PROFILE_ALL\", ApplicationVisibility.ALL);\n\n    InternalProfiles(String profileName, ApplicationVisibility applicationVisibility) {\n        this.profileName = profileName;\n        this.applicationVisibility = applicationVisibility;\n    }\n\n    private final String profileName;\n\n    private final ApplicationVisibility applicationVisibility;\n\n    public String getProfileName() {\n        return profileName;\n    }\n\n    public ApplicationVisibility getApplicationVisibility() {\n        return applicationVisibility;\n    }\n\n    public static ApplicationVisibility getApplicationVisibilityByProfileName(String profileName) {\n        for (InternalProfiles internalProfile : InternalProfiles.values()) {\n            if (internalProfile.getProfileName().equals(profileName)) {\n                return internalProfile.applicationVisibility;\n            }\n        }\n        return ApplicationVisibility.RESTRICTED;\n    }\n\n    public static InternalProfiles getInternalProfileByProfileName(String profileName) {\n        for (InternalProfiles internalProfile : InternalProfiles.values()) {\n            if (internalProfile.getProfileName().equals(profileName)) {\n                return internalProfile;\n            }\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/data/BusinessDataCrudOperationException.java",
    "content": "/**\n * Copyright (C) 2025 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data;\n\n/**\n * Exception indicating an error occurred during CRUD (Create, Read, Update, Delete) operations on business data.\n * This exception is a specific type of {@link BusinessDataRepositoryException} and is used to signal issues\n * related to the manipulation of business data records in a repository.\n *\n * @author Emmanuel Duchastenier\n */\npublic class BusinessDataCrudOperationException extends BusinessDataRepositoryException {\n\n    public BusinessDataCrudOperationException(String message) {\n        super(message);\n    }\n\n    public BusinessDataCrudOperationException(Throwable cause) {\n        super(cause);\n    }\n\n    public BusinessDataCrudOperationException(String message, Throwable cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/data/BusinessDataNotFoundException.java",
    "content": "/**\n * Copyright (C) 2025 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data;\n\nimport org.bonitasoft.engine.exception.NotFoundException;\n\npublic class BusinessDataNotFoundException extends NotFoundException {\n\n    public BusinessDataNotFoundException(String message) {\n        super(message);\n    }\n\n    public BusinessDataNotFoundException(Throwable cause) {\n        super(cause);\n    }\n\n    public BusinessDataNotFoundException(String message, Throwable cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/data/BusinessDataReference.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data;\n\nimport java.io.Serializable;\n\n/**\n * A <code>BusinessDataReference</code> defines all needed fields to retrieve a business data.\n *\n * @author Matthieu Chaffotte\n */\npublic interface BusinessDataReference extends Serializable {\n\n    /**\n     * Returns the name of the business data.\n     *\n     * @return the name of the business data\n     */\n    String getName();\n\n    /**\n     * Returns the type of the business data.\n     *\n     * @return the type of the business data\n     */\n    String getType();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/data/BusinessDataRepositoryDeploymentException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data;\n\n/**\n * Thrown to indicate that the Business Data Model deployment failed.\n *\n * @author Colin Puy\n * @author Emmanuel Duchastenier\n * @author Matthieu Chaffotte\n */\npublic class BusinessDataRepositoryDeploymentException extends BusinessDataRepositoryException {\n\n    private static final long serialVersionUID = 1L;\n\n    public BusinessDataRepositoryDeploymentException(String message) {\n        super(message);\n    }\n\n    /**\n     * Constructs a BusinessDataRepositoryDeploymentException with the specified cause.\n     *\n     * @param cause the cause\n     */\n    public BusinessDataRepositoryDeploymentException(final Throwable cause) {\n        super(cause);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/data/BusinessDataRepositoryException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data;\n\nimport org.bonitasoft.engine.exception.BonitaException;\n\n/**\n * Thrown if an exception occurs dealing with the Business Data Repository.\n *\n * @author Romain Bioteau\n * @author Matthieu Chaffotte\n */\npublic class BusinessDataRepositoryException extends BonitaException {\n\n    private static final long serialVersionUID = -1056166500737611443L;\n\n    /**\n     * Constructs a BusinessDataRepositoryException with the specified cause.\n     *\n     * @param cause the cause\n     */\n    public BusinessDataRepositoryException(final Throwable cause) {\n        super(cause);\n    }\n\n    /**\n     * Constructs a BusinessDataRepositoryException with the specified detail message.\n     *\n     * @param message the detail message\n     */\n    public BusinessDataRepositoryException(final String message) {\n        super(message);\n    }\n\n    /**\n     * Constructs a BusinessDataRepositoryException with the specified detail message and cause.\n     *\n     * @param message the detail message\n     * @param cause the cause\n     */\n    public BusinessDataRepositoryException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/data/InvalidBusinessDataModelException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data;\n\nimport java.io.Serial;\n\n/**\n * Thrown to indicate that the Business Data Model is invalid. So it cannot be deployed.\n *\n * @author Colin Puy\n * @author Emmanuel Duchastenier\n * @author Matthieu Chaffotte\n */\npublic class InvalidBusinessDataModelException extends BusinessDataRepositoryException {\n\n    @Serial\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * Constructs an InvalidBusinessDataModelException with the specified cause.\n     *\n     * @param cause the cause\n     */\n    public InvalidBusinessDataModelException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/data/MultipleBusinessDataReference.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data;\n\nimport java.util.List;\n\n/**\n * A <code>MultipleBusinessDataReference</code> is a reference of a\n * {@link org.bonitasoft.engine.bpm.businessdata.BusinessDataDefinition} which is multiple.\n *\n * @author Matthieu Chaffotte\n */\npublic interface MultipleBusinessDataReference extends BusinessDataReference {\n\n    /**\n     * Lists the business data identifiers.\n     *\n     * @return the business data identifiers.\n     */\n    List<Long> getStorageIds();\n\n    /**\n     * Lists the business data identifiers.\n     *\n     * @return the business data identifiers as String .\n     */\n    List<String> getStorageIdsAsString();\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/data/SimpleBusinessDataReference.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data;\n\n/**\n * A <code>SimpleBusinessDataReference</code> is a reference of a\n * {@link org.bonitasoft.engine.bpm.businessdata.BusinessDataDefinition} which is not multiple.\n *\n * @author Matthieu Chaffotte\n */\npublic interface SimpleBusinessDataReference extends BusinessDataReference {\n\n    /**\n     * Returns the identifier of the business data.\n     * It can be null, if no business data is attached to the reference.\n     *\n     * @return the identifier of the business data\n     */\n    Long getStorageId();\n\n    /**\n     * Returns the identifier of the business data.\n     * It can be null, if no business data is attached to the reference.\n     *\n     * @return the identifier of the business data as String\n     */\n    String getStorageIdAsString();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/data/impl/BusinessDataReferenceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport java.util.Objects;\n\nimport org.bonitasoft.engine.business.data.BusinessDataReference;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class BusinessDataReferenceImpl implements BusinessDataReference {\n\n    private static final long serialVersionUID = -6913883854275484141L;\n\n    private final String name;\n\n    private final String type;\n\n    public BusinessDataReferenceImpl(final String name, final String type) {\n        super();\n        this.name = name;\n        this.type = type;\n    }\n\n    @Override\n    public String getName() {\n        return name;\n    }\n\n    @Override\n    public String getType() {\n        return type;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (o == null || getClass() != o.getClass())\n            return false;\n        BusinessDataReferenceImpl that = (BusinessDataReferenceImpl) o;\n        return Objects.equals(name, that.name) &&\n                Objects.equals(type, that.type);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(name, type);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/data/impl/MultipleBusinessDataReferenceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport org.bonitasoft.engine.business.data.MultipleBusinessDataReference;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class MultipleBusinessDataReferenceImpl extends BusinessDataReferenceImpl\n        implements MultipleBusinessDataReference {\n\n    private static final long serialVersionUID = -8221290488745270659L;\n\n    private final List<Long> storageIds;\n\n    @JsonProperty(\"storageIds_string\")\n    private final List<String> storageIdsAsString;\n\n    public MultipleBusinessDataReferenceImpl(final String name, final String type, final List<Long> storageIds) {\n        super(name, type);\n        this.storageIds = new ArrayList<>();\n        this.storageIdsAsString = new ArrayList<>();\n        for (final Long storageId : storageIds) {\n            this.storageIds.add(storageId);\n            if (storageId == null) {\n                this.storageIdsAsString.add(null);\n            } else {\n                this.storageIdsAsString.add(storageId.toString());\n            }\n        }\n    }\n\n    @Override\n    public List<Long> getStorageIds() {\n        return storageIds;\n    }\n\n    @Override\n    public List<String> getStorageIdsAsString() {\n        return storageIdsAsString;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (o == null || getClass() != o.getClass())\n            return false;\n        if (!super.equals(o))\n            return false;\n        MultipleBusinessDataReferenceImpl that = (MultipleBusinessDataReferenceImpl) o;\n        return Objects.equals(storageIds, that.storageIds);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(super.hashCode(), storageIds, storageIdsAsString);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/data/impl/SimpleBusinessDataReferenceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport java.util.Objects;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport org.bonitasoft.engine.business.data.SimpleBusinessDataReference;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SimpleBusinessDataReferenceImpl extends BusinessDataReferenceImpl implements SimpleBusinessDataReference {\n\n    private static final long serialVersionUID = -434357449996998735L;\n\n    private final Long storageId;\n\n    @JsonProperty(\"storageId_string\")\n    private String storageIdAsString = null;\n\n    public SimpleBusinessDataReferenceImpl(final String name, final String type, final Long storageId) {\n        super(name, type);\n        this.storageId = storageId;\n        if (storageId != null) {\n            this.storageIdAsString = storageId.toString();\n        }\n    }\n\n    @Override\n    public Long getStorageId() {\n        return storageId;\n    }\n\n    @Override\n    public String getStorageIdAsString() {\n        return storageIdAsString;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (o == null || getClass() != o.getClass())\n            return false;\n        if (!super.equals(o))\n            return false;\n        SimpleBusinessDataReferenceImpl that = (SimpleBusinessDataReferenceImpl) o;\n        return Objects.equals(storageId, that.storageId);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(super.hashCode(), storageId, storageIdAsString);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/data/package-info.java",
    "content": "/**\n * Copyright (C) 2015 BonitaSoft S.A.\n * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n/**\n * <p>\n * </p>\n */\npackage org.bonitasoft.engine.business.data;\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/command/CommandCriterion.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\n/**\n * Sort criterion used to specify the sort order of the {@link org.bonitasoft.engine.command.CommandDescriptor}. <br>\n * Used by {@link org.bonitasoft.engine.api.CommandAPI#getAllCommands(int, int, CommandCriterion)} and $\n * {@link org.bonitasoft.engine.api.CommandAPI#getUserCommands(int, int, CommandCriterion)} indicate in what order we\n * should return the list of the results.\n *\n * @author Matthieu Chaffotte\n * @since 6.0.0\n */\npublic enum CommandCriterion {\n\n    NAME_ASC, NAME_DESC\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/command/CommandDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport org.bonitasoft.engine.bpm.BonitaObject;\n\n/**\n * Represents the descriptor of a (tenant) command or a platform command.\n *\n * @author Zhang Bole\n * @author Emmanuel Duchastenier\n * @see org.bonitasoft.engine.api.CommandAPI\n * @since 6.0.0\n */\npublic interface CommandDescriptor extends BonitaObject {\n\n    /**\n     * Get the identifier of this <code>CommandDescriptor</code>\n     *\n     * @return the if of this <code>CommandDescriptor</code>\n     */\n    long getId();\n\n    /**\n     * Get the name of the command\n     *\n     * @return the name of the command\n     */\n    String getName();\n\n    /**\n     * Get the description of the command\n     *\n     * @return the description of the command\n     */\n    String getDescription();\n\n    /**\n     * Get the implementation class name of the command\n     *\n     * @return the implementation class name of the command\n     */\n    String getImplementation();\n\n    /**\n     * Is the command a default system command or a custom command.\n     *\n     * @return true if this is a default system command, false otherwise.\n     */\n    boolean isSystemCommand();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/command/CommandDescriptorImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\n/**\n * @author Zhang Bole\n * @author Matthieu Chaffotte\n */\npublic class CommandDescriptorImpl implements CommandDescriptor {\n\n    private static final long serialVersionUID = -8798112164975720185L;\n\n    private long id;\n\n    private String name;\n\n    private String description;\n\n    private String implementation;\n\n    private boolean system;\n\n    public CommandDescriptorImpl() {\n        super();\n    }\n\n    public CommandDescriptorImpl(final String name, final String description, final String implementation) {\n        this.name = name;\n        this.description = description;\n        this.implementation = implementation;\n    }\n\n    CommandDescriptorImpl(final CommandDescriptor command) {\n        name = command.getName();\n        description = command.getDescription();\n        implementation = command.getImplementation();\n        system = command.isSystemCommand();\n    }\n\n    @Override\n    public long getId() {\n        return id;\n    }\n\n    @Override\n    public String getName() {\n        return name;\n    }\n\n    @Override\n    public String getDescription() {\n        return description;\n    }\n\n    @Override\n    public String getImplementation() {\n        return implementation;\n    }\n\n    @Override\n    public boolean isSystemCommand() {\n        return system;\n    }\n\n    public void setId(final long id) {\n        this.id = id;\n    }\n\n    public void setName(final String name) {\n        this.name = name;\n    }\n\n    public void setDescription(final String description) {\n        this.description = description;\n    }\n\n    public void setImplementation(final String implementation) {\n        this.implementation = implementation;\n    }\n\n    public void setSystem(final boolean system) {\n        this.system = system;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + (description == null ? 0 : description.hashCode());\n        result = prime * result + (int) (id ^ id >>> 32);\n        result = prime * result + (implementation == null ? 0 : implementation.hashCode());\n        result = prime * result + (name == null ? 0 : name.hashCode());\n        result = prime * result + (system ? 1231 : 1237);\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        final CommandDescriptorImpl other = (CommandDescriptorImpl) obj;\n        if (description == null) {\n            if (other.description != null) {\n                return false;\n            }\n        } else if (!description.equals(other.description)) {\n            return false;\n        }\n        if (id != other.id) {\n            return false;\n        }\n        if (implementation == null) {\n            if (other.implementation != null) {\n                return false;\n            }\n        } else if (!implementation.equals(other.implementation)) {\n            return false;\n        }\n        if (name == null) {\n            if (other.name != null) {\n                return false;\n            }\n        } else if (!name.equals(other.name)) {\n            return false;\n        }\n        if (system != other.system) {\n            return false;\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/command/CommandExecutionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport org.bonitasoft.engine.exception.ExecutionException;\n\n/**\n * Happens when an exception occurs during the execution of a command\n *\n * @author Matthieu Chaffotte\n * @since 6.0.0\n */\npublic class CommandExecutionException extends ExecutionException {\n\n    private static final long serialVersionUID = 5863266847298950866L;\n\n    public CommandExecutionException(final String message) {\n        super(message);\n    }\n\n    public CommandExecutionException(final Throwable cause) {\n        super(cause);\n    }\n\n    public CommandExecutionException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/command/CommandNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport org.bonitasoft.engine.exception.NotFoundException;\n\n/**\n * Happens when a command is not found\n *\n * @author Matthieu Chaffotte\n * @since 6.0.0\n */\npublic class CommandNotFoundException extends NotFoundException {\n\n    private static final long serialVersionUID = -8387839876633445358L;\n\n    public CommandNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/command/CommandParameterizationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport org.bonitasoft.engine.exception.BonitaException;\n\n/**\n * Happens when an a command is executed with wrong parameters\n *\n * @author Matthieu Chaffotte\n * @since 6.0.0\n */\npublic class CommandParameterizationException extends BonitaException {\n\n    private static final long serialVersionUID = 5878600837095857930L;\n\n    public CommandParameterizationException(final String message) {\n        super(message);\n    }\n\n    public CommandParameterizationException(final Throwable cause) {\n        super(cause);\n    }\n\n    public CommandParameterizationException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/command/CommandSearchDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\n/**\n * Contains constants to search for commands using\n * {@link org.bonitasoft.engine.api.CommandAPI#searchCommands(org.bonitasoft.engine.search.SearchOptions)}\n *\n * @author Yanyan Liu\n * @see org.bonitasoft.engine.api.CommandAPI#searchCommands(org.bonitasoft.engine.search.SearchOptions)\n * @since 6.0.0\n */\npublic class CommandSearchDescriptor {\n\n    public static final String ID = \"id\";\n\n    public static final String NAME = \"name\";\n\n    public static final String DESCRIPTION = \"description\";\n\n    public static final String IMPLEMENTATION = \"implementation\";\n\n    public static final String SYSTEM = \"system\";\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/command/CommandUpdater.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Updater for <code>Commands</code> <br>\n *\n * @author Matthieu Chaffotte\n * @see org.bonitasoft.engine.api.CommandAPI#update(long, CommandUpdater)\n * @see org.bonitasoft.engine.api.CommandAPI#update(String, CommandUpdater)\n */\npublic class CommandUpdater implements Serializable {\n\n    private static final long serialVersionUID = 1326464578602375090L;\n\n    public enum CommandField {\n        NAME, DESCRIPTION\n    }\n\n    private final Map<CommandField, Serializable> fields;\n\n    public CommandUpdater() {\n        fields = new HashMap<CommandField, Serializable>(2);\n    }\n\n    public void setName(final String name) {\n        fields.put(CommandField.NAME, name);\n    }\n\n    public void setDescription(final String description) {\n        fields.put(CommandField.DESCRIPTION, description);\n    }\n\n    public Map<CommandField, Serializable> getFields() {\n        return fields;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/command/DependencyNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport org.bonitasoft.engine.exception.NotFoundException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class DependencyNotFoundException extends NotFoundException {\n\n    private static final long serialVersionUID = 4495595833583575078L;\n\n    public DependencyNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/command/package-info.java",
    "content": "/**\n * Copyright (C) 2015 BonitaSoft S.A.\n * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n/**\n * <p>\n * </p>\n */\npackage org.bonitasoft.engine.command;\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/connector/AbstractConnector.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connector;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.APIAccessor;\n\n/**\n * @author Feng Hui\n * @author Romain Bioteau\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic abstract class AbstractConnector implements Connector {\n\n    private final Map<String, Object> inputParameters;\n\n    private final Map<String, Object> outputParameters;\n\n    protected APIAccessor apiAccessor;\n\n    private EngineExecutionContext executionContext;\n\n    public AbstractConnector() {\n        inputParameters = new HashMap<String, Object>();\n        outputParameters = new HashMap<String, Object>();\n    }\n\n    @Override\n    public void setInputParameters(final Map<String, Object> parameters) {\n        inputParameters.putAll(parameters);\n    }\n\n    protected Object getInputParameter(final String paramName) {\n        return inputParameters.get(paramName);\n    }\n\n    /**\n     * get the input parameter or the default value if the parameter is not set\n     *\n     * @param parameterKey\n     *        name of the parameter\n     * @param defaultValue\n     *        value of the parameter if not set\n     * @return\n     *         the value of the parameter\n     */\n    protected Object getInputParameter(final String parameterKey, final Serializable defaultValue) {\n        final Object param = getInputParameter(parameterKey);\n        return param == null ? defaultValue : param;\n    }\n\n    protected void setOutputParameter(final String paramName, final Object value) {\n        outputParameters.put(paramName, value);\n    }\n\n    protected Map<String, Object> getOutputParameters() {\n        return outputParameters;\n    }\n\n    @Override\n    public final Map<String, Object> execute() throws ConnectorException {\n        executeBusinessLogic();\n        return getOutputParameters();\n    }\n\n    @Override\n    public void connect() throws ConnectorException {\n        // default implementation do nothing\n    }\n\n    @Override\n    public void disconnect() throws ConnectorException {\n        // default implementation do nothing\n    }\n\n    protected abstract void executeBusinessLogic() throws ConnectorException;\n\n    public void setAPIAccessor(final APIAccessor apiAccessor) {\n        this.apiAccessor = apiAccessor;\n    }\n\n    public APIAccessor getAPIAccessor() {\n        return apiAccessor;\n    }\n\n    public EngineExecutionContext getExecutionContext() {\n        return executionContext;\n    }\n\n    public void setExecutionContext(final EngineExecutionContext executionContext) {\n        this.executionContext = executionContext;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/connector/Connector.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connector;\n\nimport java.util.Map;\n\n/**\n * @author Feng Hui\n */\npublic interface Connector {\n\n    /**\n     * Set the input parameter for a connector.\n     *\n     * @param parameters\n     *        parameters is a map with parameter names and their value.\n     */\n    void setInputParameters(Map<String, Object> parameters);\n\n    /**\n     * Validate the input parameters. Check the parameters types and boundaries.\n     *\n     * @throws ConnectorValidationException\n     *         when the input parameters are not valid\n     */\n    void validateInputParameters() throws ConnectorValidationException;\n\n    /**\n     * Execute the connector.\n     *\n     * @return the connector outputs map corresponding to the output definition.\n     * @throws ConnectorException\n     *         when something went wrong during connector execution\n     */\n    Map<String, Object> execute() throws ConnectorException;\n\n    /**\n     * Called by the engine before the connector is executed\n     * This method can be implemented by connectors to handle here opening of connections like database connection\n     *\n     * @throws ConnectorException\n     *         when something went wrong during connector connection\n     */\n    void connect() throws ConnectorException;\n\n    /**\n     * Called by the engine after the connector and its output operations are executed\n     * This method can be implemented by connectors to close connections here.\n     * The typical use of this is to be able to return connected objects that will be used in output operation and then\n     * disconnect them.\n     *\n     * @throws ConnectorException\n     *         when something went wrong during connector disconnection\n     */\n    void disconnect() throws ConnectorException;\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/connector/ConnectorException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connector;\n\nimport org.bonitasoft.engine.exception.BonitaException;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic class ConnectorException extends BonitaException {\n\n    private static final long serialVersionUID = -4711678695785171528L;\n\n    public ConnectorException(final String message) {\n        super(message);\n    }\n\n    public ConnectorException(final Throwable cause) {\n        super(cause);\n    }\n\n    public ConnectorException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/connector/ConnectorValidationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connector;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.exception.BonitaException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class ConnectorValidationException extends BonitaException {\n\n    private static final long serialVersionUID = -7641578992494154217L;\n\n    public ConnectorValidationException(final String message) {\n        super(message);\n    }\n\n    public ConnectorValidationException(final Connector connector, final String... messages) {\n        super(getMessage(connector, Arrays.asList(messages)));\n    }\n\n    public ConnectorValidationException(final Connector connector, final List<String> messages) {\n        super(getMessage(connector, messages));\n    }\n\n    private static String getMessage(final Connector connector, final List<String> messages) {\n        final StringBuilder mergedMessages = new StringBuilder(\"Error validating connector \");\n        mergedMessages.append(connector.getClass().getName());\n        mergedMessages.append(\":\\n\");\n        for (final String message : messages) {\n            mergedMessages.append(message);\n            mergedMessages.append('\\n');\n        }\n        return mergedMessages.toString();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/connector/EngineExecutionContext.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connector;\n\nimport java.io.Serializable;\n\nimport org.bonitasoft.engine.expression.ExpressionConstants;\n\n/**\n * @author Emmanuel Duchastenier\n * @author Matthieu Chaffotte\n */\npublic class EngineExecutionContext implements Serializable {\n\n    private static final long serialVersionUID = -57239373625030379L;\n\n    private long processDefinitionId = -1;\n\n    private long activityInstanceId = -1;\n\n    private long processInstanceId = -1;\n\n    private long rootProcessInstanceId;\n\n    private long taskAssigneeId = -1;\n\n    /**\n     * @return the current process definition ID\n     */\n    public long getProcessDefinitionId() {\n        return processDefinitionId;\n    }\n\n    public void setProcessDefinitionId(final long processDefinitionId) {\n        this.processDefinitionId = processDefinitionId;\n    }\n\n    public long getProcessInstanceId() {\n        return processInstanceId;\n    }\n\n    /**\n     * Refers to the directly including process instance.\n     *\n     * @param processInstanceId\n     *        the ID of the directly including process instance\n     */\n    public void setProcessInstanceId(final long processInstanceId) {\n        this.processInstanceId = processInstanceId;\n    }\n\n    /**\n     * Refers to the directly including process instance.\n     *\n     * @return the ID of the directly including process instance\n     */\n    public long getActivityInstanceId() {\n        return activityInstanceId;\n    }\n\n    public void setActivityInstanceId(final long activityInstanceId) {\n        this.activityInstanceId = activityInstanceId;\n    }\n\n    /**\n     * Refers to the top-level including process instance.\n     *\n     * @param rootProcessInstanceId\n     *        the ID of the top-level including process instance\n     */\n    public void setRootProcessInstanceId(final long rootProcessInstanceId) {\n        this.rootProcessInstanceId = rootProcessInstanceId;\n    }\n\n    /**\n     * Refers to the top-level including process instance.\n     *\n     * @return\n     *         the ID of the top-level including process instance\n     */\n    public long getRootProcessInstanceId() {\n        return rootProcessInstanceId;\n    }\n\n    public Serializable getExpressionConstant(final ExpressionConstants constant) {\n        switch (constant) {\n            case ACTIVITY_INSTANCE_ID:\n                return activityInstanceId;\n            case PROCESS_INSTANCE_ID:\n                return processInstanceId;\n            case PROCESS_DEFINITION_ID:\n                return processDefinitionId;\n            case ROOT_PROCESS_INSTANCE_ID:\n                return rootProcessInstanceId;\n            case ENGINE_EXECUTION_CONTEXT:\n                return this;\n            case TASK_ASSIGNEE_ID:\n                return taskAssigneeId;\n            default:\n                return -1;\n        }\n    }\n\n    public long getTaskAssigneeId() {\n        return taskAssigneeId;\n    }\n\n    public void setTaskAssigneeId(final long taskAssigneeId) {\n        this.taskAssigneeId = taskAssigneeId;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/connector/sap/SAPMonoDestinationDataProvider.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connector.sap;\n\nimport java.util.Properties;\n\nimport com.sap.conn.jco.ext.DestinationDataEventListener;\nimport com.sap.conn.jco.ext.DestinationDataProvider;\nimport com.sap.conn.jco.ext.Environment;\n\n/**\n * Used by SAP connector in order to synchronize SAP {@code com.sap.conn.jco.ext.DestinationDataProvider} used\n * Warning: Internal use only!\n * This class is subject to change without notice\n *\n * @author Aurelien Pupier\n */\npublic class SAPMonoDestinationDataProvider implements DestinationDataProvider {\n\n    private static SAPMonoDestinationDataProvider destinationDataProvider = null;\n\n    private DestinationDataEventListener listener;\n\n    private Properties properties;\n\n    private final String destinationName;\n\n    private SAPMonoDestinationDataProvider(final String destinationName) {\n        super();\n        this.destinationName = destinationName;\n    }\n\n    /**\n     * BE CAREFUL: only one destinationName is possible\n     *\n     * @param destinationName\n     * @return\n     */\n    public static synchronized SAPMonoDestinationDataProvider getInstance(final String destinationName)\n            throws IllegalStateException {\n        // TODO: handle several destinationName correctly\n        if (destinationDataProvider == null) {\n            destinationDataProvider = new SAPMonoDestinationDataProvider(destinationName);\n            Environment.registerDestinationDataProvider(destinationDataProvider);\n        } else if (!destinationName.equals(destinationDataProvider.getDestinationName())) {\n            throw new IllegalStateException(\n                    \"You can use only one SAP destination (and they should use the configuration). The current one is named \"\n                            + destinationDataProvider.getDestinationName());\n        }\n        return destinationDataProvider;\n    }\n\n    @Override\n    public Properties getDestinationProperties(final String destinationName) {\n        if (destinationName.equals(this.destinationName) && properties != null) {\n            return properties;\n        }\n        throw new RuntimeException(\"Destination \" + destinationName + \" is not available\");\n    }\n\n    @Override\n    public void setDestinationDataEventListener(final DestinationDataEventListener eventListener) {\n        listener = eventListener;\n    }\n\n    @Override\n    public boolean supportsEvents() {\n        return true;\n    }\n\n    public void changeProperties(final Properties properties) {\n        if (properties == null) {\n            listener.deleted(destinationName);\n            this.properties = null;\n        } else {\n            if (listener != null && !properties.equals(this.properties)) {\n                listener.updated(destinationName);\n            }\n            this.properties = properties;\n        }\n    }\n\n    public String getDestinationName() {\n        return destinationName;\n    }\n\n    /**\n     * Use it carefully!!! If someone retrieved an Instance and you clear it it will break everything, currently usage\n     * planned only for test purpose.\n     */\n    public static void clear() {\n        if (destinationDataProvider != null) {\n            Environment.unregisterDestinationDataProvider(destinationDataProvider);\n            destinationDataProvider = null;\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/digest/DigestUtils.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.digest;\n\nimport static java.nio.charset.StandardCharsets.UTF_8;\n\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.Base64;\n\nimport org.bonitasoft.engine.api.Internal;\n\n@Internal\npublic class DigestUtils {\n\n    public static String encodeBase64AsUtf8String(byte[] bytes) {\n        return new String(Base64.getEncoder().encode(bytes), UTF_8);\n    }\n\n    public static byte[] md5(String source) {\n        return digest(\"MD5\", source);\n    }\n\n    public static byte[] sha1(String source) {\n        return digest(\"SHA1\", source);\n    }\n\n    private static byte[] digest(String algorithm, String source) {\n        return getDigest(algorithm).digest(utf8Bytes(source));\n    }\n\n    private static MessageDigest getDigest(String algorithm) {\n        try {\n            return MessageDigest.getInstance(algorithm);\n        } catch (NoSuchAlgorithmException e) {\n            throw new IllegalArgumentException(e);\n        }\n    }\n\n    private static byte[] utf8Bytes(String string) {\n        if (string == null) {\n            return null;\n        }\n        return string.getBytes(UTF_8);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/APIImplementationNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\n/**\n * Thrown when it's not possible to find a API implementation.\n * The class APIImplementationNotFoundException is a form of Throwable that indicates conditions that a reasonable\n * application might want to catch.\n * The class APIImplementationNotFoundException that is not also subclasses of {@link RuntimeException} are checked\n * exceptions.\n * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by\n * the execution of the method or\n * constructor and propagate outside the method or constructor boundary.\n *\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class APIImplementationNotFoundException extends BonitaException {\n\n    private static final long serialVersionUID = -8951476276466102532L;\n\n    /**\n     * Constructs a new exception with the specified detail message.\n     *\n     * @param message\n     *        The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method).\n     */\n    public APIImplementationNotFoundException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/AlreadyExistsException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\n/**\n * Thrown when it's not possible to create a object, because it already exists.\n * The class AlreadyExistsException is a form of Throwable that indicates conditions that a reasonable application might\n * want to catch.\n * The class AlreadyExistsException that is not also subclasses of {@link RuntimeException} are checked exceptions.\n * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by\n * the execution of the method or\n * constructor and propagate outside the method or constructor boundary.\n *\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class AlreadyExistsException extends CreationException {\n\n    private static final long serialVersionUID = 6709231545504567159L;\n    private String name;\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null\n     *        value is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public AlreadyExistsException(final Throwable cause) {\n        super(cause);\n    }\n\n    /**\n     * Constructs a new exception with the specified detail message.\n     *\n     * @param message The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()}\n     *        method).\n     */\n    public AlreadyExistsException(final String message) {\n        super(message);\n    }\n\n    /**\n     * Constructs a new exception with the specified detail message and name of existing object.\n     *\n     * @param message The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()}\n     *        method).\n     * @param name the name of the conflicting object\n     * @since 6.4\n     */\n    public AlreadyExistsException(final String message, String name) {\n        super(message);\n        this.name = name;\n    }\n\n    /**\n     * Retrieves the name of conflicting object or null if the name was not supplied\n     *\n     * @return the name of conflicting object or null if the name was not supplied.\n     * @since 6.4\n     */\n    public String getName() {\n        return name;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/ApplicationInstallationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\npublic class ApplicationInstallationException extends RuntimeException {\n\n    public ApplicationInstallationException(String message) {\n        super(message);\n    }\n\n    public ApplicationInstallationException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n    @Override\n    public String getMessage() {\n        String message = super.getMessage();\n        Throwable cause = getCause();\n        if (cause != null) {\n            message += \" - cause: \" + cause.getClass().getSimpleName() + \" - \" + cause.getMessage();\n        }\n        return message;\n    }\n\n    @Override\n    public String getLocalizedMessage() {\n        // ensure consistency\n        return getMessage();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/BonitaContextException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\n/**\n * Exception wrapper providing context on the exception.\n *\n * @author Aurelien Pupier\n * @author Celine Souchet\n */\npublic interface BonitaContextException {\n\n    String getUserName();\n\n    void setUserName(String userName);\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/BonitaException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\nimport java.io.Serializable;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.TreeMap;\n\n/**\n * The class BonitaException and its subclasses are a form of Throwable that indicates conditions that a reasonable\n * application might want to catch.\n * The class BonitaException and its subclasses that are not also subclasses of {@link RuntimeException} are checked\n * exceptions.\n * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by\n * the execution of the method or\n * constructor and propagate outside the method or constructor boundary.\n *\n * @author Matthieu Chaffotte\n * @author Baptiste Mesta\n * @author Celine Souchet\n * @author Aurelien Pupier\n */\npublic class BonitaException extends Exception implements BonitaContextException {\n\n    private static final long serialVersionUID = -5413586694735909486L;\n\n    private final Map<ExceptionContext, Serializable> context = new TreeMap<>();\n\n    private String userName = \"\";\n\n    /**\n     * Constructs a new exception with the specified detail message and cause.\n     *\n     * @param message\n     *        The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method).\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public BonitaException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    /**\n     * Constructs a new exception with the specified detail message.\n     *\n     * @param message\n     *        The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method).\n     */\n    public BonitaException(final String message) {\n        super(message);\n    }\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public BonitaException(final Throwable cause) {\n        super(cause);\n    }\n\n    /**\n     * @see org.bonitasoft.engine.exception.BonitaContextException#getUserName()\n     */\n    @Override\n    public String getUserName() {\n        return userName;\n    }\n\n    /**\n     * @see org.bonitasoft.engine.exception.BonitaContextException#setUserName(java.lang.String)\n     */\n    @Override\n    public void setUserName(final String userName) {\n        this.userName = userName;\n    }\n\n    /**\n     * @return The context of the exception\n     * @since 6.3\n     */\n    public Map<ExceptionContext, Serializable> getContext() {\n        return context;\n    }\n\n    /**\n     * @param id\n     *        The identifier of the process definition to set\n     * @since 6.3\n     */\n    public void setProcessDefinitionIdOnContext(final Long id) {\n        context.put(ExceptionContext.PROCESS_DEFINITION_ID, id);\n    }\n\n    /**\n     * @param name\n     *        The name of the process definition to set\n     * @since 6.3\n     */\n    public void setProcessDefinitionNameOnContext(final String name) {\n        context.put(ExceptionContext.PROCESS_NAME, name);\n    }\n\n    /**\n     * @param version\n     *        The version of the process definition to set\n     * @since 6.3\n     */\n    public void setProcessDefinitionVersionOnContext(final String version) {\n        context.put(ExceptionContext.PROCESS_VERSION, version);\n    }\n\n    /**\n     * @param id\n     *        The identifier of the process instance to set\n     * @since 6.3\n     */\n    public void setProcessInstanceIdOnContext(final Long id) {\n        context.put(ExceptionContext.PROCESS_INSTANCE_ID, id);\n    }\n\n    /**\n     * @param id\n     *        The identifier of the root process instance to set\n     * @since 6.3\n     */\n    public void setRootProcessInstanceIdOnContext(final Long id) {\n        context.put(ExceptionContext.ROOT_PROCESS_INSTANCE_ID, id);\n    }\n\n    /**\n     * @param id\n     *        The identifier of the connector definition\n     * @since 6.3\n     */\n    public void setConnectorDefinitionIdOnContext(final String id) {\n        context.put(ExceptionContext.CONNECTOR_DEFINITION_ID, id);\n    }\n\n    /**\n     * @param name\n     *        The class name of the implementation of the connector definition to set\n     * @since 6.3\n     */\n    public void setConnectorDefinitionImplementationClassNameOnContext(final String name) {\n        context.put(ExceptionContext.CONNECTOR_DEFINITION_IMPLEMENTATION_CLASS_NAME, name);\n    }\n\n    /**\n     * @param version\n     *        The version of the connector definition\n     * @since 6.3\n     */\n    public void setConnectorDefinitionVersionOnContext(final String version) {\n        context.put(ExceptionContext.CONNECTOR_DEFINITION_VERSION, version);\n    }\n\n    /**\n     * @param activationEvent\n     *        The event which activates the connector to set\n     * @since 6.3\n     */\n    public void setConnectorActivationEventOnContext(final String activationEvent) {\n        context.put(ExceptionContext.CONNECTOR_ACTIVATION_EVENT, activationEvent);\n    }\n\n    /**\n     * @param id\n     *        The identifier of the connector instance to set\n     * @since 6.3\n     */\n    public void setConnectorInstanceIdOnContext(final long id) {\n        context.put(ExceptionContext.CONNECTOR_INSTANCE_ID, id);\n    }\n\n    /**\n     * @param id\n     *        The identifier of the flow node definition to set\n     * @since 6.3\n     */\n    public void setFlowNodeDefinitionIdOnContext(final long id) {\n        context.put(ExceptionContext.FLOW_NODE_DEFINITION_ID, id);\n    }\n\n    /**\n     * @param id\n     *        The identifier of the flow node instance to set\n     * @since 6.3\n     */\n    public void setFlowNodeInstanceIdOnContext(final long id) {\n        context.put(ExceptionContext.FLOW_NODE_INSTANCE_ID, id);\n    }\n\n    /**\n     * @param name\n     *        The name of the flow node to set\n     * @since 6.3\n     */\n    public void setFlowNodeNameOnContext(final String name) {\n        context.put(ExceptionContext.FLOW_NODE_NAME, name);\n    }\n\n    /**\n     * @param name\n     *        The name of the message instance to set\n     * @since 6.3\n     */\n    public void setMessageInstanceNameOnContext(final String name) {\n        context.put(ExceptionContext.MESSAGE_INSTANCE_NAME, name);\n    }\n\n    /**\n     * @param name\n     *        The target process name of the message instance to set\n     * @since 6.3\n     */\n    public void setMessageInstanceTargetProcessOnContext(final String name) {\n        context.put(ExceptionContext.MESSAGE_INSTANCE_TARGET_PROCESS_NAME, name);\n    }\n\n    /**\n     * @param name\n     *        The target flow node name of the message instance to set\n     * @since 6.3\n     */\n    public void setMessageInstanceTargetFlowNodeOnContext(final String name) {\n        context.put(ExceptionContext.MESSAGE_INSTANCE_TARGET_FLOW_NODE_NAME, name);\n    }\n\n    /**\n     * @param eventType\n     *        The event type of the waiting message instance to set\n     * @since 6.3\n     */\n    public void setWaitingMessageEventTypeOnContext(final String eventType) {\n        context.put(ExceptionContext.WAITING_MESSAGE_INSTANCE_TYPE, eventType);\n    }\n\n    /**\n     * @param id\n     *        The identifier of the document\n     * @since 6.3\n     */\n    public void setDocumentIdOnContext(final long id) {\n        context.put(ExceptionContext.DOCUMENT_ID, id);\n    }\n\n    /**\n     * @param userId\n     *        The identifier of the user\n     * @since 6.3\n     */\n    public void setUserIdOnContext(final Long userId) {\n        context.put(ExceptionContext.USER_ID, userId);\n    }\n\n    /**\n     * @param groupId\n     *        The identifier of the group\n     * @since 6.3\n     */\n    public void setGroupIdOnContext(final Long groupId) {\n        context.put(ExceptionContext.GROUP_ID, groupId);\n    }\n\n    /**\n     * @param roleId\n     *        The identifier of the role\n     * @since 6.3\n     */\n    public void setRoleIdOnContext(final Long roleId) {\n        context.put(ExceptionContext.ROLE_ID, roleId);\n    }\n\n    /**\n     * @param name\n     *        The name of the data\n     * @since 6.4.2\n     */\n    public void setDataName(final String name) {\n        context.put(ExceptionContext.DATA_NAME, name);\n    }\n\n    /**\n     * @param dataClassName\n     *        The class name of the data\n     * @since 6.4.2\n     */\n    public void setDataClassName(final String dataClassName) {\n        context.put(ExceptionContext.DATA_CLASS_NAME, dataClassName);\n    }\n\n    @Override\n    public String getMessage() {\n        final StringBuilder stringBuilder = new StringBuilder();\n        stringBuilder.append(getUserNameMessage());\n        appendContextMessage(stringBuilder);\n        stringBuilder.append(super.getMessage());\n        return stringBuilder.toString();\n    }\n\n    private void appendContextMessage(final StringBuilder stringBuilder) {\n        if (!context.isEmpty()) {\n            for (final Entry<ExceptionContext, Serializable> entry : context.entrySet()) {\n                stringBuilder.append(entry.getKey() + \"=\" + entry.getValue() + \" | \");\n            }\n        }\n    }\n\n    private String getUserNameMessage() {\n        return userName != null && !userName.isEmpty() ? \"USERNAME=\" + userName + \" | \" : \"\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/BonitaHomeConfigurationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\n/**\n * @author Baptiste Mesta\n * @author Elias Ricken de Medeiros\n */\npublic class BonitaHomeConfigurationException extends BonitaException {\n\n    private static final long serialVersionUID = -6473712535410061835L;\n\n    public BonitaHomeConfigurationException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/BonitaHomeNotSetException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\n/**\n * @author Baptiste Mesta\n * @author Elias Ricken de Medeiros\n */\npublic class BonitaHomeNotSetException extends BonitaException {\n\n    private static final long serialVersionUID = -6473712535410061835L;\n\n    public BonitaHomeNotSetException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/BonitaRuntimeException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @author Aurelien Pupier\n */\npublic class BonitaRuntimeException extends RuntimeException implements BonitaContextException {\n\n    private static final long serialVersionUID = -5413586694735909486L;\n\n    private String userName = \"\";\n\n    public BonitaRuntimeException(final String message) {\n        super(message);\n    }\n\n    public BonitaRuntimeException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public BonitaRuntimeException(final Throwable cause) {\n        super(cause);\n    }\n\n    public BonitaRuntimeException(final String message, final Throwable cause, final boolean enableSuppression,\n            final boolean writableStackTrace) {\n        super(message, cause, enableSuppression, writableStackTrace);\n    }\n\n    /**\n     * @see org.bonitasoft.engine.exception.BonitaContextException#getUserName()\n     */\n    @Override\n    public String getUserName() {\n        return userName;\n    }\n\n    /**\n     * @see org.bonitasoft.engine.exception.BonitaContextException#setUserName(java.lang.String)\n     */\n    @Override\n    public void setUserName(String userName) {\n        this.userName = userName;\n    }\n\n    @Override\n    public String getMessage() {\n        return getUserNameMessage() + super.getMessage();\n    }\n\n    private String getUserNameMessage() {\n        return userName != null && !userName.isEmpty() ? \"USERNAME=\" + userName + \" | \" : \"\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/ClassLoaderException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ClassLoaderException extends BonitaException {\n\n    private static final long serialVersionUID = 6760479336490227757L;\n\n    public ClassLoaderException(final String message) {\n        super(message);\n    }\n\n    public ClassLoaderException(final Throwable t) {\n        super(t);\n    }\n\n    public ClassLoaderException(final String message, final Exception e) {\n        super(message, e);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/ContractDataNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\n/**\n * Thrown when a process / user task contract data cannot be found.\n * author Emmanuel Duchastenier\n */\npublic class ContractDataNotFoundException extends NotFoundException {\n\n    public ContractDataNotFoundException(Throwable cause) {\n        super(cause);\n    }\n\n    public ContractDataNotFoundException(String message) {\n        super(message);\n    }\n\n    public ContractDataNotFoundException(String message, Throwable cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/CreationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class CreationException extends BonitaException {\n\n    private static final long serialVersionUID = 7462326307205938638L;\n\n    public CreationException(final Throwable cause) {\n        super(cause);\n    }\n\n    public CreationException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public CreationException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/DeletionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class DeletionException extends BonitaException {\n\n    private static final long serialVersionUID = -5157179430526887606L;\n\n    public DeletionException(final Throwable cause) {\n        super(cause);\n    }\n\n    public DeletionException(final String message) {\n        super(message);\n    }\n\n    public DeletionException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/ExceptionContext.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\n/**\n * To define the context of an exception in the message.\n *\n * @author Celine Souchet\n */\npublic enum ExceptionContext {\n    /**\n     * Corresponding to the identifier of the process definition\n     */\n    PROCESS_DEFINITION_ID,\n    /**\n     * Corresponding to the name of the process definition\n     */\n    PROCESS_NAME,\n    /**\n     * Corresponding to the version of the process definition\n     */\n    PROCESS_VERSION,\n    /**\n     * Corresponding to the identifier of the process instance\n     */\n    PROCESS_INSTANCE_ID,\n    /**\n     * Corresponding to the identifier of the root process instance\n     */\n    ROOT_PROCESS_INSTANCE_ID,\n    /**\n     * Corresponding to the identifier of the flow node definition\n     */\n    FLOW_NODE_DEFINITION_ID,\n    /**\n     * Corresponding to the identifier of the flow node instance\n     */\n    FLOW_NODE_INSTANCE_ID,\n    /**\n     * Corresponding to the name of the flow node\n     */\n    FLOW_NODE_NAME,\n    /**\n     * Corresponding to the name of the Message Instance\n     */\n    MESSAGE_INSTANCE_NAME,\n    /**\n     * Corresponding to the target process name of the Message Instance\n     */\n    MESSAGE_INSTANCE_TARGET_PROCESS_NAME,\n    /**\n     * Corresponding to the target flow node name of the Message Instance\n     */\n    MESSAGE_INSTANCE_TARGET_FLOW_NODE_NAME,\n    /**\n     * Corresponding to the event type of the Waiting Message Instance\n     */\n    WAITING_MESSAGE_INSTANCE_TYPE,\n    /**\n     * Corresponding to the identifier of the connector definition\n     */\n    CONNECTOR_DEFINITION_ID,\n    /**\n     * Corresponding to the class name of the implementation of the connector definition\n     */\n    CONNECTOR_DEFINITION_IMPLEMENTATION_CLASS_NAME,\n    /**\n     * Corresponding to the version of the connector definition\n     */\n    CONNECTOR_DEFINITION_VERSION,\n    /**\n     * Corresponding to the event which activates the connector\n     */\n    CONNECTOR_ACTIVATION_EVENT,\n    /**\n     * Corresponding to the identifier of the connector instance\n     */\n    CONNECTOR_INSTANCE_ID,\n    /**\n     * Corresponding to the identifier of the user\n     */\n    USER_ID,\n    /**\n     * Corresponding to the identifier of the group\n     */\n    GROUP_ID,\n    /**\n     * Corresponding to the identifier of the role\n     */\n    ROLE_ID,\n    /**\n     * Corresponding to the identifier of the document\n     */\n    DOCUMENT_ID,\n    /**\n     * Corresponding to the name of the data\n     */\n    DATA_NAME,\n    /**\n     * Corresponding to the class name of the data\n     */\n    DATA_CLASS_NAME;\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/ExecutionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\n/**\n * Thrown when a problem occurs:\n * <ul>\n * <li>while executing a BPM entity (activity, process, event, ...)</li>\n * <li>while executing an administration operation (startPlatform, activateTenant, etc.)</li>\n * <li>...</li>\n * </ul>\n * The class ExecutionException and its subclasses are a form of Throwable that indicates conditions that a reasonable\n * application might want to catch.\n * The class ExecutionException and its subclasses that are not also subclasses of {@link RuntimeException} are checked\n * exceptions.\n * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by\n * the execution of the method or\n * constructor and propagate outside the method or constructor boundary.\n *\n * @author Matthieu Chaffotte\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n */\npublic class ExecutionException extends BonitaException {\n\n    private static final long serialVersionUID = -424782295937066031L;\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public ExecutionException(final Throwable cause) {\n        super(cause);\n    }\n\n    /**\n     * Constructs a new exception with the specified detail message and cause.\n     *\n     * @param message\n     *        The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method).\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public ExecutionException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    /**\n     * Constructs a new exception with the specified detail message.\n     *\n     * @param message\n     *        The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method).\n     */\n    public ExecutionException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/ExportException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\n/**\n * Thrown when it's not possible to import an element.\n *\n * @author Elias Ricken de Medeiros\n * @since 6.4\n */\npublic class ExportException extends BonitaException {\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public ExportException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/FormMappingNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\n/**\n * @author Baptiste Mesta\n */\npublic class FormMappingNotFoundException extends NotFoundException {\n\n    public FormMappingNotFoundException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/ImportException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\n/**\n * Thrown when it's not possible to import an element.\n *\n * @author Elias Ricken de Medeiros\n */\npublic class ImportException extends BonitaException {\n\n    /**\n     * Constructs a new exception with the specified detail cause.\n     *\n     * @param cause\n     *        The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value\n     *        is permitted, and indicates that the\n     *        cause is nonexistent or unknown.)\n     */\n    public ImportException(final Throwable cause) {\n        super(cause);\n    }\n\n    /**\n     * Constructs a new exception with the specified message.\n     *\n     * @param message\n     *        The message (which is saved for later retrieval by the {@link Throwable#getMessage()} method)\n     */\n    public ImportException(final String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/IncorrectParameterException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\n/**\n * Generic Bonita exception for all invalid Parameter when calling a constructor or a method on Bonita objects.\n *\n * @author Emmanuel Duchastenier\n */\npublic class IncorrectParameterException extends BonitaException {\n\n    private static final long serialVersionUID = -5875088313056748595L;\n\n    public IncorrectParameterException(final String message) {\n        super(message);\n    }\n\n    public IncorrectParameterException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/InvalidGroupNameException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\n/**\n * @author Danila Mazour\n */\npublic class InvalidGroupNameException extends CreationException {\n\n    public InvalidGroupNameException(String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/InvalidPageTokenException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\npublic class InvalidPageTokenException extends CreationException {\n\n    private static final long serialVersionUID = -4521026642699202555L;\n\n    public InvalidPageTokenException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public InvalidPageTokenException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/InvalidPageZipContentException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\npublic class InvalidPageZipContentException extends CreationException {\n\n    private static final long serialVersionUID = -4521026642699202555L;\n\n    public InvalidPageZipContentException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public InvalidPageZipContentException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/InvalidPageZipInconsistentException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\n/**\n * @author Baptiste Mesta\n */\npublic class InvalidPageZipInconsistentException extends InvalidPageZipContentException {\n\n    public InvalidPageZipInconsistentException(String message) {\n        super(message);\n    }\n\n    public InvalidPageZipInconsistentException(String string, Exception e) {\n        super(string, e);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/InvalidPageZipMissingAPropertyException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\n/**\n * @author Baptiste Mesta\n */\npublic class InvalidPageZipMissingAPropertyException extends InvalidPageZipContentException {\n\n    public InvalidPageZipMissingAPropertyException(String fields) {\n        super(\"Missing fields in the page.properties: \" + fields);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/InvalidPageZipMissingIndexException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\n/**\n * @author Baptiste Mesta\n */\npublic class InvalidPageZipMissingIndexException extends InvalidPageZipContentException {\n\n    public InvalidPageZipMissingIndexException() {\n        super(\"Missing Index.groovy or index.html\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/InvalidPageZipMissingPropertiesException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\n/**\n * @author Baptiste Mesta\n */\npublic class InvalidPageZipMissingPropertiesException extends InvalidPageZipContentException {\n\n    public InvalidPageZipMissingPropertiesException() {\n        super(\"Missing page.propeties\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/InvalidXMLException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\n/**\n * @author Danila Mazour\n */\npublic class InvalidXMLException extends BonitaException {\n\n    public InvalidXMLException(String msg, Exception e) {\n        super(msg, e);\n    }\n\n    public InvalidXMLException(Exception e) {\n        super(e);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/MissingServiceException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\n/**\n * Happens when a service is missing from the configuration\n *\n * @author Baptiste Mesta\n */\npublic class MissingServiceException extends RuntimeException {\n\n    private static final long serialVersionUID = 1L;\n\n    public MissingServiceException(final String string) {\n        super(string);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/NotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class NotFoundException extends BonitaException {\n\n    private static final long serialVersionUID = -1056166500737611443L;\n\n    public NotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n    public NotFoundException(final String message) {\n        super(message);\n    }\n\n    public NotFoundException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/NotSerializableException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\n/**\n * @author Celine Souchet\n */\npublic class NotSerializableException extends BonitaException {\n\n    private static final long serialVersionUID = -5541516162117265707L;\n\n    public NotSerializableException(final String connectorDefinitionId, final Throwable e) {\n        super(\"Connector \" + connectorDefinitionId\n                + \" executed successfully but output cannot be read correctly. See Bonita Engine log file for technical details.\",\n                e);\n    }\n\n    public NotSerializableException(final String connectorDefinitionId, final String connectorDefinitionVersion,\n            final String key, final Object value) {\n        super(createMessage(connectorDefinitionId, connectorDefinitionVersion, key, value));\n    }\n\n    private static String createMessage(final String connectorDefinitionId, final String connectorDefinitionVersion,\n            final String key, final Object value) {\n        final StringBuilder stringBuilder = new StringBuilder(\"the connector \");\n        stringBuilder.append(connectorDefinitionId);\n        stringBuilder.append(' ');\n        stringBuilder.append(connectorDefinitionVersion);\n        stringBuilder.append(\" have an unserializable output and was called directly from the api. name=\");\n        stringBuilder.append(key);\n        stringBuilder.append(\" value=\");\n        stringBuilder.append(value.toString());\n        return stringBuilder.toString();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/ProcessInstanceHierarchicalDeletionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\n/**\n * Happens when a process instance can't be there is a parent process instance that is still active\n * Delete this parent process first.\n *\n * @author Baptiste Mesta\n */\npublic class ProcessInstanceHierarchicalDeletionException extends DeletionException {\n\n    private static final long serialVersionUID = -5157179430526887606L;\n\n    private final long processInstanceId;\n\n    public ProcessInstanceHierarchicalDeletionException(final String message, final long processInstanceId) {\n        super(message);\n        this.processInstanceId = processInstanceId;\n    }\n\n    /**\n     * @return the processInstanceId that is the parent (root) of the process we try to delete\n     */\n    public long getProcessInstanceId() {\n        return processInstanceId;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/RetrieveException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class RetrieveException extends BonitaRuntimeException {\n\n    private static final long serialVersionUID = -9045954776853609628L;\n\n    public RetrieveException(final Throwable cause) {\n        super(cause);\n    }\n\n    public RetrieveException(final String message) {\n        super(message);\n    }\n\n    public RetrieveException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/SearchException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SearchException extends BonitaException {\n\n    private static final long serialVersionUID = 1700504009097375021L;\n\n    public SearchException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SearchException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/ServerAPIException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class ServerAPIException extends BonitaException {\n\n    private static final long serialVersionUID = 3589250940630558801L;\n\n    public ServerAPIException(final Throwable cause) {\n        super(cause);\n    }\n\n    public ServerAPIException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/StackTraceTransformer.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\nimport java.lang.reflect.Field;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.internal.ServerWrappedException;\n\n/**\n * Transform an exception having causes in one single exception with merged stack trace\n * This is done in order to avoid to throw to client server side exception with unknown class in client side\n *\n * @author Baptiste Mesta\n */\npublic class StackTraceTransformer {\n\n    private final ServerWrappedException e;\n\n    static Field field;\n\n    static {\n        try {\n            field = Throwable.class.getDeclaredField(\"cause\");\n            field.setAccessible(true);\n        } catch (final NoSuchFieldException e) {\n            e.printStackTrace();\n        } catch (final SecurityException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public StackTraceTransformer(final ServerWrappedException e) {\n        this.e = e;\n    }\n\n    /**\n     * @param e\n     *        the ServerWrappedException given by the server api\n     * @return\n     *         a safe to throw to client ServerWrappedException\n     */\n    public static ServerWrappedException mergeStackTraces(final ServerWrappedException e) {\n        try {\n            return new StackTraceTransformer(e).merge();\n        } catch (final Exception e1) {\n            System.err\n                    .println(\"Unable to throw the root exception: \" + e1.getClass().getName() + \": \" + e1.getMessage());\n            e1.printStackTrace();\n            return new ServerWrappedException(new BonitaRuntimeException(\n                    \"Unable to throw the root exception because of (see log for the original stack trace)\", e));\n        }\n    }\n\n    public static void addStackTo(final Throwable e, final StackTraceElement[] clientStackTrace) {\n        final StackTraceElement[] causeStack = e.getStackTrace();\n\n        final StackTraceElement[] newStack = new StackTraceElement[causeStack.length + clientStackTrace.length + 1];\n\n        System.arraycopy(clientStackTrace, 0, newStack, 0, clientStackTrace.length);\n        newStack[clientStackTrace.length] = new StackTraceElement(\n                \"\\t< ========== Beginning of the server stack trace ========== >\", \" \", \" \", -3);\n        System.arraycopy(causeStack, 0, newStack, clientStackTrace.length + 1, causeStack.length);\n        e.setStackTrace(newStack);\n    }\n\n    private ServerWrappedException merge() throws Exception {\n        final Throwable cause = e.getCause();\n        if (field != null) {\n            transfertStack(cause, cause);\n            field.set(cause, null);\n            return e;\n        }\n        Throwable newCause;\n        if (cause.getMessage() != null) {\n            newCause = cause.getClass().getConstructor(String.class).newInstance(e.getMessage());\n        } else {\n            newCause = cause.getClass().newInstance();\n        }\n        transfertStack(newCause, cause);\n        return new ServerWrappedException(newCause);\n    }\n\n    private void transfertStack(final Throwable mergeStackInside, final Throwable cause) {\n        Throwable subCause = cause.getCause();\n        if (subCause == null) {\n            // no stak to merge\n            return;\n        }\n        final StackTraceElement[] currentStack = cause.getStackTrace();\n        final List<StackTraceElement[]> causesStacks = new ArrayList<StackTraceElement[]>();\n        final List<Integer> framesInCommons = new ArrayList<Integer>();\n        final List<Throwable> exceptions = new ArrayList<Throwable>();\n        int causeslength = 0;\n        StackTraceElement[] lastStack = currentStack;\n        do {\n            final StackTraceElement[] trace = subCause.getStackTrace();\n            causesStacks.add(trace);\n            exceptions.add(subCause);\n\n            int m = trace.length - 1;\n            int n = lastStack.length - 1;\n            while (m >= 0 && n >= 0 && trace[m].equals(lastStack[n])) {\n                m--;\n                n--;\n            }\n            final int framesInCommon = trace.length - 1 - m;\n            framesInCommons.add(framesInCommon);\n            lastStack = trace;\n            // add remove the frames in common to the total length but add one to put the \"...23 more\" if there is some in common\n            causeslength += trace.length + 1 - framesInCommon + (framesInCommon == 0 ? 0 : 1);\n        } while ((subCause = subCause.getCause()) != null);\n        final StackTraceElement[] mergedStackTrace = new StackTraceElement[currentStack.length + causeslength];\n        System.arraycopy(currentStack, 0, mergedStackTrace, 0, currentStack.length);\n        int current = currentStack.length;\n        int i = 0;\n        for (final StackTraceElement[] stackTraceElements : causesStacks) {\n            final Integer framesInCommon = framesInCommons.get(i);\n            mergedStackTrace[current] = new StackTraceElement(\"\\tCaused by: \" + exceptions.get(i).getClass().getName(),\n                    \": \"\n                            + exceptions.get(i).getMessage() + \" \",\n                    \" \",\n                    -3);\n            current++;\n            System.arraycopy(stackTraceElements, 0, mergedStackTrace, current,\n                    stackTraceElements.length - framesInCommon);\n            current += stackTraceElements.length - framesInCommon;\n            if (framesInCommon != 0) {\n                mergedStackTrace[current] = new StackTraceElement(\"... \" + framesInCommon + \" more\", \" \", \" \", -3);\n                current++;\n            }\n            i++;\n        }\n        mergeStackInside.setStackTrace(mergedStackTrace);\n    }\n\n    /*\n     * Reduce stack length\n     */\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/TenantStatusException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\nimport java.io.Serial;\n\n/**\n * Thrown when we try to access on a paused tenant an API method that cannot be called on a paused tenant, or when we\n * try to access on a running tenant an API\n * method that cannot be called on a running tenant.\n *\n * @author Emmanuel Duchastenier\n */\npublic class TenantStatusException extends BonitaRuntimeException {\n\n    @Serial\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * @param message\n     *        the exception message\n     */\n    public TenantStatusException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/UnauthorizedAccessException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\n/**\n * @author Baptiste Mesta\n */\npublic class UnauthorizedAccessException extends BonitaException {\n\n    public UnauthorizedAccessException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n    public UnauthorizedAccessException(String message) {\n        super(message);\n    }\n\n    public UnauthorizedAccessException(Throwable cause) {\n        super(cause);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/UnavailableLockException.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\n/**\n * This exception indicates an update can not be performed because it requires a\n * functional lock which is already taken by another invocation. Hence this\n * update request should be rejected with a human-readable explanation.\n */\npublic class UnavailableLockException extends UpdateException {\n\n    private static final long serialVersionUID = -1034429723916469306L;\n\n    public UnavailableLockException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/UnknownElementType.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\n/**\n * @author Baptiste Mesta\n */\npublic class UnknownElementType extends BonitaRuntimeException {\n\n    private static final long serialVersionUID = 5458401765450999673L;\n\n    public UnknownElementType(final String type) {\n        super(\"Unknown element type: \" + type);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/UpdateException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class UpdateException extends BonitaException {\n\n    private static final long serialVersionUID = -6529401526595518267L;\n\n    public UpdateException(final Throwable cause) {\n        super(cause);\n    }\n\n    public UpdateException(final String message) {\n        super(message);\n    }\n\n    public UpdateException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public UpdateException() {\n        super(\"The update descriptor does not contain field updates\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/UpdatingWithInvalidPageTokenException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\npublic class UpdatingWithInvalidPageTokenException extends UpdateException {\n\n    private static final long serialVersionUID = -4521026642699202555L;\n\n    public UpdatingWithInvalidPageTokenException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/UpdatingWithInvalidPageZipContentException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\npublic class UpdatingWithInvalidPageZipContentException extends UpdateException {\n\n    private static final long serialVersionUID = -4521026642699202555L;\n\n    public UpdatingWithInvalidPageZipContentException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/package-info.java",
    "content": "/**\n * Copyright (C) 2015 BonitaSoft S.A.\n * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n/**\n * <p>\n * </p>\n */\npackage org.bonitasoft.engine.exception;\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/expression/ExpressionEvaluationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression;\n\nimport org.bonitasoft.engine.exception.BonitaException;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class ExpressionEvaluationException extends BonitaException {\n\n    private static final long serialVersionUID = 7295745453567432910L;\n\n    private String expressionName;\n\n    /**\n     * @param cause\n     * @param expressionName\n     *        The expression's name that failed on the evaluation.\n     */\n    public ExpressionEvaluationException(final Throwable cause, final String expressionName) {\n        super(cause);\n        this.expressionName = expressionName;\n    }\n\n    public ExpressionEvaluationException(Throwable cause) {\n        super(cause);\n    }\n\n    /**\n     * Return empty or null, when the context of evaluation is wrong.\n     *\n     * @return The expression's name that failed on the evaluation.\n     */\n    public String getExpressionName() {\n        return expressionName;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/filter/AbstractUserFilter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.filter;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.APIAccessor;\nimport org.bonitasoft.engine.connector.ConnectorValidationException;\nimport org.bonitasoft.engine.connector.EngineExecutionContext;\n\n/**\n * @author Feng Hui\n * @author Romain Bioteau\n * @author Baptiste Mesta\n */\npublic abstract class AbstractUserFilter implements UserFilter {\n\n    private final Map<String, Object> inputParameters;\n\n    private APIAccessor apiAccessor;\n\n    private EngineExecutionContext executionContext;\n\n    public AbstractUserFilter() {\n        inputParameters = new HashMap<String, Object>();\n    }\n\n    @Override\n    public void setInputParameters(final Map<String, Object> parameters) {\n        inputParameters.putAll(parameters);\n    }\n\n    protected Object getInputParameter(final String paramName) throws IllegalStateException {\n        return inputParameters.get(paramName);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected <T> T getOptinalInputParameter(final String paramName) {\n        return (T) inputParameters.get(paramName);\n    }\n\n    protected String getStringInputParameter(final String paramName) {\n        return (String) getInputParameter(paramName);\n    }\n\n    protected void validateStringInputParameterIsNotNulOrEmpty(final String paramName)\n            throws ConnectorValidationException {\n        final String paramValue = (String) getInputParameter(paramName);\n        if (paramValue == null || \"\".equals(paramValue.trim())) {\n            throw new ConnectorValidationException(\"The input parameter '\" + paramName + \"' cannot be null or empty\");\n        }\n    }\n\n    /**\n     * {@inheritDoc} Default implementation returns true\n     */\n    @Override\n    public boolean shouldAutoAssignTaskIfSingleResult() {\n        return true;\n    }\n\n    public void setAPIAccessor(final APIAccessor apiAccessor) {\n        this.apiAccessor = apiAccessor;\n    }\n\n    public void setExecutionContext(final EngineExecutionContext executionContext) {\n        this.executionContext = executionContext;\n    }\n\n    public APIAccessor getAPIAccessor() {\n        return apiAccessor;\n    }\n\n    public EngineExecutionContext getExecutionContext() {\n        return executionContext;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/filter/UserFilter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.filter;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.connector.ConnectorValidationException;\n\n/**\n * The execution of this filter returns a list of userId.\n * It is used for the filtering of who can claim a UserTask based on its actorId\n *\n * @author Baptiste Mesta\n */\npublic interface UserFilter {\n\n    /**\n     * Set the input parameter for the filter.\n     *\n     * @param parameters\n     *        parameters is a map with parameter names and their value.\n     */\n    void setInputParameters(Map<String, Object> parameters);\n\n    /**\n     * Validate the input parameters. Check the parameters types and boundaries.\n     *\n     * @throws ConnectorValidationException\n     */\n    void validateInputParameters() throws ConnectorValidationException;\n\n    /**\n     * Execute the filter.\n     *\n     * @param actorName\n     *        the actor name of the task\n     * @return the connector outputs map corresponding to the output definition.\n     * @throws UserFilterException\n     */\n    List<Long> filter(String actorName) throws UserFilterException;\n\n    /**\n     * This method make the engine assign automatically the task if the result of {@link #filter(String)} is only one\n     * element.\n     * i.e. when the task is filtered only for a single user\n     *\n     * @return true if we should assign task when there is only one result.\n     */\n    boolean shouldAutoAssignTaskIfSingleResult();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/filter/UserFilterException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.filter;\n\nimport org.bonitasoft.engine.exception.BonitaException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class UserFilterException extends BonitaException {\n\n    private static final long serialVersionUID = -5462275472282384752L;\n\n    public UserFilterException(final Throwable cause) {\n        super(cause);\n    }\n\n    public UserFilterException(final String message) {\n        super(message);\n    }\n\n    public UserFilterException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/form/FormMapping.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.form;\n\nimport java.util.Date;\nimport java.util.Objects;\n\nimport org.bonitasoft.engine.bpm.BaseElement;\n\n/**\n * @author Baptiste Mesta\n */\npublic class FormMapping implements BaseElement {\n\n    private static final long serialVersionUID = 1L;\n\n    private long id;\n    private long processDefinitionId;\n    private FormMappingType type;\n    private FormMappingTarget target;\n    private String task;\n    private Long pageId;\n    private String pageURL;\n    private String pageMappingKey;\n    private long lastUpdatedBy;\n    private Date lastUpdateDate;\n    private boolean formRequired;\n\n    public FormMapping() {\n    }\n\n    public FormMapping(long processDefinitionId, FormMappingType type, String task, String pageMappingKey) {\n        this.processDefinitionId = processDefinitionId;\n        this.type = type;\n        this.task = task;\n        this.pageMappingKey = pageMappingKey;\n    }\n\n    public FormMapping(long processDefinitionId, FormMappingType type, String pageMappingKey) {\n        this.type = type;\n        this.processDefinitionId = processDefinitionId;\n        this.pageMappingKey = pageMappingKey;\n    }\n\n    public long getProcessDefinitionId() {\n        return processDefinitionId;\n    }\n\n    public void setProcessDefinitionId(long processDefinitionId) {\n        this.processDefinitionId = processDefinitionId;\n    }\n\n    public String getPageMappingKey() {\n        return pageMappingKey;\n    }\n\n    public void setPageMappingKey(String pageMappingKey) {\n        this.pageMappingKey = pageMappingKey;\n    }\n\n    @Override\n    public long getId() {\n        return id;\n    }\n\n    public void setId(long id) {\n        this.id = id;\n    }\n\n    public String getTask() {\n        return task;\n    }\n\n    public void setTask(String task) {\n        this.task = task;\n    }\n\n    public Long getPageId() {\n        return pageId;\n    }\n\n    public void setPageId(Long pageId) {\n        this.pageId = pageId;\n    }\n\n    public String getURL() {\n        return pageURL;\n    }\n\n    public void setPageURL(String pageURL) {\n        this.pageURL = pageURL;\n    }\n\n    public FormMappingType getType() {\n        return type;\n    }\n\n    public void setType(FormMappingType type) {\n        this.type = type;\n    }\n\n    public long getLastUpdatedBy() {\n        return lastUpdatedBy;\n    }\n\n    public void setLastUpdatedBy(long lastUpdatedBy) {\n        this.lastUpdatedBy = lastUpdatedBy;\n    }\n\n    public Date getLastUpdateDate() {\n        return lastUpdateDate;\n    }\n\n    public void setLastUpdateDate(Date lastUpdateDate) {\n        this.lastUpdateDate = lastUpdateDate;\n    }\n\n    public FormMappingTarget getTarget() {\n        return target;\n    }\n\n    public void setTarget(FormMappingTarget target) {\n        this.target = target;\n    }\n\n    public boolean isFormRequired() {\n        return formRequired;\n    }\n\n    public void setFormRequired(boolean required) {\n        this.formRequired = required;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (!(o instanceof FormMapping that))\n            return false;\n        return Objects.equals(id, that.id) &&\n                Objects.equals(processDefinitionId, that.processDefinitionId) &&\n                Objects.equals(lastUpdatedBy, that.lastUpdatedBy) &&\n                Objects.equals(type, that.type) &&\n                Objects.equals(target, that.target) &&\n                Objects.equals(task, that.task) &&\n                Objects.equals(pageId, that.pageId) &&\n                Objects.equals(pageURL, that.pageURL) &&\n                Objects.equals(pageMappingKey, that.pageMappingKey) &&\n                Objects.equals(lastUpdateDate, that.lastUpdateDate);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(id, processDefinitionId, type, target, task, pageId, pageURL, pageMappingKey, lastUpdatedBy,\n                lastUpdateDate);\n    }\n\n    @Override\n    public String toString() {\n        return \"FormMapping{\" +\n                \"id=\" + id +\n                \", processDefinitionId=\" + processDefinitionId +\n                \", type=\" + type +\n                \", target=\" + target +\n                \", task='\" + task + '\\'' +\n                \", pageId=\" + pageId +\n                \", pageURL='\" + pageURL + '\\'' +\n                \", pageMappingKey='\" + pageMappingKey + '\\'' +\n                \", lastUpdatedBy=\" + lastUpdatedBy +\n                \", lastUpdateDate=\" + lastUpdateDate +\n                '}';\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/form/FormMappingSearchDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.form;\n\n/**\n * @author Baptiste Mesta\n */\npublic final class FormMappingSearchDescriptor {\n\n    public static final String ID = \"id\";\n\n    public static final String PROCESS_DEFINITION_ID = \"processDefinitionId\";\n\n    /**\n     * @see org.bonitasoft.engine.form.FormMappingType\n     */\n    public static final String TYPE = \"type\";\n\n    public static final String TASK = \"task\";\n\n    public static final String PAGE_ID = \"pageId\";\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/home/BonitaHome.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.home;\n\n/**\n * Utility class to retrieve the bonita home based on the system property <code>bonita.home</code>\n * <p>\n * The bonita home is the folder containing all configuration files and working directories\n * </p>\n *\n * @author Baptiste Mesta\n * @author Elias Ricken de Medeiros\n * @since 6.0.0\n */\npublic abstract class BonitaHome {\n\n    public static final String BONITA_HOME = \"bonita.home\";\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/home/package-info.java",
    "content": "/**\n * Copyright (C) 2015 BonitaSoft S.A.\n * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n/**\n * <p>\n * Contains classes related to the <code>Bonita Home</code>.\n * </p>\n *\n * @since 6.0.0\n */\npackage org.bonitasoft.engine.home;\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/ContactDataCreator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Represents a helper for creating {@link ContactData}. Chaining is possible with this creator to ease the ContactData\n * creation.\n * <br>\n * For instance, new ContactDataCreator().setEmail(\"john.doe@bonitasoft.com\").setPhoneNumber(\"012456789\");\n *\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @since 6.0.0\n */\npublic class ContactDataCreator implements Serializable {\n\n    private static final long serialVersionUID = -1414989152963184543L;\n\n    /**\n     * Represents the available {@link ContactData} fields\n     */\n    public enum ContactDataField {\n        EMAIL, PHONE, MOBILE, FAX, BUILDING, ROOM, ADDRESS, ZIP_CODE, CITY, STATE, COUNTRY, WEBSITE;\n    }\n\n    private final Map<ContactDataField, Serializable> fields;\n\n    /**\n     * Create a new creator instance\n     */\n    public ContactDataCreator() {\n        fields = new HashMap<ContactDataField, Serializable>(5);\n    }\n\n    /**\n     * @param email\n     *        The contact email address to create\n     * @return The current {@link ContactDataCreator}\n     */\n    public ContactDataCreator setEmail(final String email) {\n        fields.put(ContactDataField.EMAIL, email);\n        return this;\n    }\n\n    /**\n     * @param phoneNumber\n     *        The contact phone number to create\n     * @return The current {@link ContactDataCreator}\n     */\n    public ContactDataCreator setPhoneNumber(final String phoneNumber) {\n        fields.put(ContactDataField.PHONE, phoneNumber);\n        return this;\n    }\n\n    /**\n     * @param mobileNumber\n     *        The contact mobile number to create\n     * @return The current {@link ContactDataCreator}\n     */\n    public ContactDataCreator setMobileNumber(final String mobileNumber) {\n        fields.put(ContactDataField.MOBILE, mobileNumber);\n        return this;\n    }\n\n    /**\n     * @param faxNumber\n     *        The contact fax number to create\n     * @return The current {@link ContactDataCreator}\n     */\n    public ContactDataCreator setFaxNumber(final String faxNumber) {\n        fields.put(ContactDataField.FAX, faxNumber);\n        return this;\n    }\n\n    /**\n     * @param building\n     *        The contact building to create\n     * @return The current {@link ContactDataCreator}\n     */\n    public ContactDataCreator setBuilding(final String building) {\n        fields.put(ContactDataField.BUILDING, building);\n        return this;\n    }\n\n    /**\n     * @param room\n     *        The contact room to create\n     * @return The current {@link ContactDataCreator}\n     */\n    public ContactDataCreator setRoom(final String room) {\n        fields.put(ContactDataField.ROOM, room);\n        return this;\n    }\n\n    /**\n     * @param address\n     *        The contact address to create\n     * @return The current {@link ContactDataCreator}\n     */\n    public ContactDataCreator setAddress(final String address) {\n        fields.put(ContactDataField.ADDRESS, address);\n        return this;\n    }\n\n    /**\n     * @param zipCode\n     *        The contact ZIP code to create\n     * @return The current {@link ContactDataCreator}\n     */\n    public ContactDataCreator setZipCode(final String zipCode) {\n        fields.put(ContactDataField.ZIP_CODE, zipCode);\n        return this;\n    }\n\n    /**\n     * @param city\n     *        The contact city to create\n     * @return The current {@link ContactDataCreator}\n     */\n    public ContactDataCreator setCity(final String city) {\n        fields.put(ContactDataField.CITY, city);\n        return this;\n    }\n\n    /**\n     * @param state\n     *        The contact state to create\n     * @return The current {@link ContactDataCreator}\n     */\n    public ContactDataCreator setState(final String state) {\n        fields.put(ContactDataField.STATE, state);\n        return this;\n    }\n\n    /**\n     * @param country\n     *        The contact country to create\n     * @return The current {@link ContactDataCreator}\n     */\n    public ContactDataCreator setCountry(final String country) {\n        fields.put(ContactDataField.COUNTRY, country);\n        return this;\n    }\n\n    /**\n     * @param website\n     *        The contact web site address to create\n     * @return The current {@link ContactDataCreator}\n     */\n    public ContactDataCreator setWebsite(final String website) {\n        fields.put(ContactDataField.WEBSITE, website);\n        return this;\n    }\n\n    /**\n     * @return The current contact data information to create\n     */\n    public Map<ContactDataField, Serializable> getFields() {\n        return fields;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/ContactDataUpdater.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * represents a helper for updating a {@link ContactData}\n *\n * @author Emmanuel Duchastenier\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @see ContactData\n * @since 6.0.0\n */\npublic class ContactDataUpdater implements Serializable {\n\n    private static final long serialVersionUID = 7871478791386229141L;\n\n    /**\n     * The available contact fields\n     */\n    public enum ContactDataField {\n        EMAIL, PHONE, MOBILE, FAX, BUILDING, ROOM, ADDRESS, ZIP_CODE, CITY, STATE, COUNTRY, WEBSITE\n    }\n\n    private final Map<ContactDataField, Serializable> fields;\n\n    /**\n     * Default Constructor.\n     */\n    public ContactDataUpdater() {\n        fields = new HashMap<ContactDataField, Serializable>(5);\n    }\n\n    /**\n     * @param email\n     *        The contact email to create\n     */\n    public void setEmail(final String email) {\n        fields.put(ContactDataField.EMAIL, email);\n    }\n\n    /**\n     * @param phoneNumber\n     *        The contact phone number to create\n     */\n    public void setPhoneNumber(final String phoneNumber) {\n        fields.put(ContactDataField.PHONE, phoneNumber);\n    }\n\n    /**\n     * @param mobileNumber\n     *        The contact mobile number to create\n     */\n    public void setMobileNumber(final String mobileNumber) {\n        fields.put(ContactDataField.MOBILE, mobileNumber);\n    }\n\n    /**\n     * @param faxNumber\n     *        The contact fax number to create\n     */\n    public void setFaxNumber(final String faxNumber) {\n        fields.put(ContactDataField.FAX, faxNumber);\n    }\n\n    /**\n     * @param building\n     *        The contact building to create\n     */\n    public void setBuilding(final String building) {\n        fields.put(ContactDataField.BUILDING, building);\n    }\n\n    /**\n     * @param room\n     *        The contact room to create\n     */\n    public void setRoom(final String room) {\n        fields.put(ContactDataField.ROOM, room);\n    }\n\n    /**\n     * @param address\n     *        The contact address to create\n     */\n    public void setAddress(final String address) {\n        fields.put(ContactDataField.ADDRESS, address);\n    }\n\n    /**\n     * @param zipCode\n     *        The contact ZIP code to create\n     */\n    public void setZipCode(final String zipCode) {\n        fields.put(ContactDataField.ZIP_CODE, zipCode);\n    }\n\n    /**\n     * @param city\n     *        The contact city to create\n     */\n    public void setCity(final String city) {\n        fields.put(ContactDataField.CITY, city);\n    }\n\n    /**\n     * @param state\n     *        The contact state to create\n     */\n    public void setState(final String state) {\n        fields.put(ContactDataField.STATE, state);\n    }\n\n    /**\n     * @param country\n     *        The contact country to create\n     */\n    public void setCountry(final String country) {\n        fields.put(ContactDataField.COUNTRY, country);\n    }\n\n    /**\n     * @param website\n     *        The contact web site address to create\n     */\n    public void setWebsite(final String website) {\n        fields.put(ContactDataField.WEBSITE, website);\n    }\n\n    /**\n     * @return The current contact data information to update\n     */\n    public Map<ContactDataField, Serializable> getFields() {\n        return fields;\n    }\n\n    /**\n     * @return True if there are some contact data to update\n     */\n    public boolean hasFields() {\n        return !fields.isEmpty();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/CustomUserInfoDefinitionCreator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport java.io.Serializable;\nimport java.util.Objects;\n\n/**\n * represents a helper for creating a {@link CustomUserInfoDefinition}.\n *\n * @author Vincent Elcrin\n * @see CustomUserInfoDefinition\n * @since 6.3.1\n */\npublic class CustomUserInfoDefinitionCreator implements Serializable {\n\n    private static final long serialVersionUID = 6929368716340973445L;\n\n    private String name;\n\n    private String description;\n\n    /**\n     * creates a new {@link CustomUserInfoDefinitionCreator} with the specified name\n     *\n     * @param name the name to set\n     */\n    public CustomUserInfoDefinitionCreator(final String name) {\n        this.name = name;\n    }\n\n    /**\n     * creates a new {@link CustomUserInfoDefinitionCreator} with the specified name and description\n     *\n     * @param name the name to set\n     * @param description the description to set\n     */\n    public CustomUserInfoDefinitionCreator(final String name, final String description) {\n        this(name);\n        this.description = description;\n    }\n\n    /**\n     * @return the {@link CustomUserInfoDefinitionCreator}'s name to create\n     */\n    public String getName() {\n        return name;\n    }\n\n    /**\n     * @return the {@link CustomUserInfoDefinitionCreator}'s description to create\n     */\n    public String getDescription() {\n        return description;\n    }\n\n    /**\n     * @param name the {@link CustomUserInfoDefinitionCreator}'s name to create\n     */\n    public void setName(final String name) {\n        this.name = name;\n    }\n\n    /**\n     * @param description the {@link CustomUserInfoDefinitionCreator}'s name to create\n     */\n    public void setDescription(final String description) {\n        this.description = description;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (o == null || getClass() != o.getClass())\n            return false;\n        CustomUserInfoDefinitionCreator that = (CustomUserInfoDefinitionCreator) o;\n        return Objects.equals(name, that.name) &&\n                Objects.equals(description, that.description);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(name, description);\n    }\n\n    @Override\n    public String toString() {\n        return \"CustomUserInfoDefinitionCreator{\" +\n                \"name='\" + name + '\\'' +\n                \", description='\" + description + '\\'' +\n                '}';\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/CustomUserInfoValueSearchDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\n/**\n * Defines the fields that can be used as filters and soring by\n * {@link org.bonitasoft.engine.api.CustomUserInfoAPI#searchCustomUserInfoValues(org.bonitasoft.engine.search.SearchOptions)}.\n *\n * @author Vincent Elcrin\n * @since 6.3.1\n * @see org.bonitasoft.engine.api.CustomUserInfoAPI#searchCustomUserInfoValues(org.bonitasoft.engine.search.SearchOptions)\n */\npublic final class CustomUserInfoValueSearchDescriptor {\n\n    /**\n     * Refers to {@link org.bonitasoft.engine.identity.CustomUserInfoValue#getUserId()}\n     *\n     * @see CustomUserInfoValue#getUserId()\n     */\n    public static final String USER_ID = \"userId\";\n\n    /**\n     * Refers to {@link org.bonitasoft.engine.identity.CustomUserInfoValue#getDefinitionId()}\n     *\n     * @see CustomUserInfoValue#getDefinitionId()\n     */\n    public static final String DEFINITION_ID = \"definitionId\";\n\n    /**\n     * Refers to {@link org.bonitasoft.engine.identity.CustomUserInfoValue#getValue()}\n     *\n     * @see CustomUserInfoValue#getValue()\n     */\n    public static final String VALUE = \"value\";\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/CustomUserInfoValueUpdater.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport java.io.Serializable;\n\n/**\n * represents a helper for updating {@link CustomUserInfoValue}\n *\n * @author Vincent Elcrin\n * @see CustomUserInfoValue\n * @since 6.3.1\n */\npublic class CustomUserInfoValueUpdater implements Serializable {\n\n    private static final long serialVersionUID = -2699448153857398426L;\n\n    private final String value;\n\n    /**\n     * creates a new instance of {@link CustomUserInfoValueUpdater} with a value to update on a\n     * {@link CustomUserInfoValue}\n     *\n     * @param value the value to update on a {@link CustomUserInfoValue}\n     */\n    public CustomUserInfoValueUpdater(final String value) {\n        this.value = value;\n    }\n\n    /**\n     * @return the value to update on a {@link CustomUserInfoValue}\n     */\n    public String getValue() {\n        return value;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/GroupCreator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\n\n/**\n * represent a helper for creating a {@link Group}\n *\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @see Group\n * @since 6.0.0\n */\npublic class GroupCreator implements Serializable {\n\n    private static final long serialVersionUID = -1546623947528297571L;\n\n    /**\n     * represents the available {@link Group} field\n     */\n    public enum GroupField {\n        NAME, DISPLAY_NAME, DESCRIPTION, @Deprecated\n        ICON_NAME, @Deprecated\n        ICON_PATH, PARENT_PATH, ICON_FILENAME, ICON_CONTENT\n    }\n\n    private final Map<GroupField, Serializable> fields;\n\n    /**\n     * creates a new {@link GroupCreator} with a group name to create\n     *\n     * @param name\n     *        The name of the group that will be created\n     */\n    public GroupCreator(final String name) {\n        fields = new HashMap<>(3);\n        fields.put(GroupField.NAME, name);\n    }\n\n    /**\n     * @param parentPath\n     *        The group's parent path to create\n     */\n    public void setParentPath(final String parentPath) {\n        fields.put(GroupField.PARENT_PATH, parentPath);\n    }\n\n    /**\n     * @param displayName\n     *        The group's display to create\n     */\n    public GroupCreator setDisplayName(final String displayName) {\n        fields.put(GroupField.DISPLAY_NAME, displayName);\n        return this;\n    }\n\n    /**\n     * @param description\n     *        The group's description to create\n     */\n    public GroupCreator setDescription(final String description) {\n        fields.put(GroupField.DESCRIPTION, description);\n        return this;\n    }\n\n    /**\n     * @param iconName\n     *        The group's icon name to create\n     * @deprecated since 7.3.0 use #setIcon\n     */\n    @Deprecated\n    public GroupCreator setIconName(final String iconName) {\n        return this;\n    }\n\n    /**\n     * @param iconPath\n     *        The group's icon file path to create\n     * @deprecated since 7.3.0 use #setIcon\n     */\n    @Deprecated\n    public GroupCreator setIconPath(final String iconPath) {\n        return this;\n    }\n\n    public GroupCreator setIcon(String filename, byte[] content) {\n        fields.put(GroupField.ICON_FILENAME, filename);\n        fields.put(GroupField.ICON_CONTENT, content);\n        return this;\n    }\n\n    /**\n     * @return The information associated with the group to create\n     */\n    public Map<GroupField, Serializable> getFields() {\n        return fields;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (o == null || getClass() != o.getClass())\n            return false;\n        GroupCreator that = (GroupCreator) o;\n        return Objects.equals(fields, that.fields);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(fields);\n    }\n\n    @Override\n    public String toString() {\n        return \"GroupCreator{\" +\n                \"fields=\" + fields +\n                '}';\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/GroupCriterion.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\n/**\n * represents group criterion sort orders\n *\n * @author Lu Kai\n * @author Matthieu Chaffotte\n * @see Group\n * @since 6.0.0\n */\npublic enum GroupCriterion {\n    /**\n     * Name ascending order\n     */\n    NAME_ASC,\n    /**\n     * Label ascending order\n     */\n    LABEL_ASC,\n    /**\n     * Name descending order\n     */\n    NAME_DESC,\n    /**\n     * Label descending order\n     */\n    LABEL_DESC;\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/GroupNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport org.bonitasoft.engine.exception.NotFoundException;\n\n/**\n * thrown when a {@link Group} is not found in the organization\n *\n * @author Kai Lu\n * @author Matthieu Chaffotte\n * @see Group\n * @since 6.0.0\n */\npublic class GroupNotFoundException extends NotFoundException {\n\n    private static final long serialVersionUID = -3825194748072808390L;\n\n    /**\n     * creates a new instance with the cause of the exception\n     *\n     * @param cause the cause that raised this exception\n     */\n    public GroupNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/GroupSearchDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\n/**\n * holds constants about {@link Group} search filters\n *\n * @author Matthieu Chaffotte\n * @see Group\n * @since 6.0.0\n */\npublic class GroupSearchDescriptor {\n\n    /** filter search on Group's id */\n    public static final String ID = \"id\";\n\n    /** filter search on Group's name */\n    public static final String NAME = \"name\";\n\n    /** filter search on Group's parent path */\n    public static final String PARENT_PATH = \"parentPath\";\n\n    /** filter search on Group's display name */\n    public static final String DISPLAY_NAME = \"displayName\";\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/GroupUpdater.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\n\n/**\n * represent a helper fpr updating a {@link Group}\n *\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @see Group\n * @since 6.0.0\n */\npublic class GroupUpdater implements Serializable {\n\n    private static final long serialVersionUID = 728214104237982027L;\n\n    /**\n     * represents the available {@link Group} fields\n     */\n    public enum GroupField {\n        NAME, DISPLAY_NAME, DESCRIPTION, @Deprecated\n        ICON_NAME, @Deprecated\n        ICON_PATH, PARENT_PATH, ICON_FILENAME, ICON_CONTENT\n    }\n\n    private final Map<GroupField, Serializable> fields;\n\n    /**\n     * Default Constructor.\n     */\n    public GroupUpdater() {\n        fields = new HashMap<>(3);\n    }\n\n    /**\n     * @param name\n     *        The group's name to update\n     */\n    public GroupUpdater updateName(final String name) {\n        fields.put(GroupField.NAME, name);\n        return this;\n    }\n\n    /**\n     * @param displayName\n     *        The group's display name to update\n     */\n    public GroupUpdater updateDisplayName(final String displayName) {\n        fields.put(GroupField.DISPLAY_NAME, displayName);\n        return this;\n    }\n\n    /**\n     * @param description\n     *        The group's description to update\n     */\n    public GroupUpdater updateDescription(final String description) {\n        fields.put(GroupField.DESCRIPTION, description);\n        return this;\n    }\n\n    /**\n     * @param iconName\n     *        The group's icon name to update\n     * @deprecated since 7.3.0 use #updateIcon\n     */\n    @Deprecated\n    public GroupUpdater updateIconName(final String iconName) {\n        return this;\n    }\n\n    /**\n     * @param iconPath\n     *        The group's icon path to update\n     * @deprecated since 7.3.0 use #updateIcon\n     */\n    @Deprecated\n    public GroupUpdater updateIconPath(final String iconPath) {\n        return this;\n    }\n\n    public GroupUpdater updateIcon(String filename, byte[] content) {\n        fields.put(GroupField.ICON_FILENAME, filename);\n        fields.put(GroupField.ICON_CONTENT, content);\n        return this;\n    }\n\n    /**\n     * @param parentPath\n     *        The group's parent path to update\n     */\n    public GroupUpdater updateParentPath(final String parentPath) {\n        fields.put(GroupField.PARENT_PATH, parentPath);\n        return this;\n    }\n\n    /**\n     * @return The group's fields to update\n     */\n    public Map<GroupField, Serializable> getFields() {\n        return fields;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (o == null || getClass() != o.getClass())\n            return false;\n        GroupUpdater that = (GroupUpdater) o;\n        return Objects.equals(fields, that.fields);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(fields);\n    }\n\n    @Override\n    public String toString() {\n        return \"GroupUpdater{\" +\n                \"fields=\" + fields +\n                '}';\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/ImportPolicy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\n/**\n * Define how to handle conflicts when an organization is imported.\n * <br>\n * Check {@link org.bonitasoft.engine.api.OrganizationAPI#importOrganization(String)} and\n * {@link org.bonitasoft.engine.api.OrganizationAPI#importOrganization(String, ImportPolicy)} to see the usage.\n *\n * @see org.bonitasoft.engine.api.OrganizationAPI\n * @author Baptiste Mesta\n * @since 6.0.0\n */\npublic enum ImportPolicy {\n\n    /**\n     * Existing items in current organization are updated to have the values of the item in the given organization\n     */\n    MERGE_DUPLICATES,\n\n    /**\n     * If an item already exists the import fail and is reverted to previous state\n     */\n    FAIL_ON_DUPLICATES,\n\n    /**\n     * Existing items are kept\n     */\n    IGNORE_DUPLICATES\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/InvalidOrganizationFileFormatException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport org.bonitasoft.engine.InvalidFileFormatException;\n\n/**\n * thrown to indicate an organization import error caused by a parsing error\n *\n * @author Danila Mazour\n * @since 7.9.0\n */\npublic class InvalidOrganizationFileFormatException extends OrganizationImportException\n        implements InvalidFileFormatException {\n\n    /**\n     * create a new exception instance with the given message\n     *\n     * @param message the exception message\n     */\n    public InvalidOrganizationFileFormatException(final String message) {\n        super(message);\n    }\n\n    /**\n     * creates a new exception instance with the given exception as cause\n     *\n     * @param cause the exception cause\n     */\n    public InvalidOrganizationFileFormatException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/MembershipNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport org.bonitasoft.engine.exception.NotFoundException;\n\n/**\n * thrown when a {@link UserMembership} is not found\n *\n * @author Matthieu Chaffotte\n * @see UserMembership\n * @since 6.0.0\n */\npublic class MembershipNotFoundException extends NotFoundException {\n\n    private static final long serialVersionUID = -264860778869560830L;\n\n    /**\n     * creates a new instance of the exception with the given cause\n     *\n     * @param cause the cause of the exception\n     */\n    public MembershipNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/OrganizationExportException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport org.bonitasoft.engine.exception.ExecutionException;\n\n/**\n * thrown to indicate an organization export error\n *\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n * @since 6.0.0\n */\npublic class OrganizationExportException extends ExecutionException {\n\n    private static final long serialVersionUID = 3608804813319882731L;\n\n    /**\n     * creates a new exception instance with the given exception as cause\n     *\n     * @param cause the exception cause\n     */\n    public OrganizationExportException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/OrganizationImportException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport org.bonitasoft.engine.exception.ExecutionException;\n\n/**\n * thrown to indicate an organization import error\n *\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n * @since 6.0.0\n */\npublic class OrganizationImportException extends ExecutionException {\n\n    private static final long serialVersionUID = -6086310296760629566L;\n\n    /**\n     * create a new exception instance with the given message\n     *\n     * @param message the exception message\n     */\n    public OrganizationImportException(final String message) {\n        super(message);\n    }\n\n    /**\n     * creates a new exception instance with the given exception as cause\n     *\n     * @param cause the exception cause\n     */\n    public OrganizationImportException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/RoleCreator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\n\n/**\n * represents a helper for creating a {@link Role}. Chaining is possible with this creator to ease the {@link Role}\n * creation.\n * <br>\n * For instance, new RoleCreator(\"member\").setDisplayName(\"Member\").setIconName(\"userIcon\");\n *\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @see Role\n * @since 6.0.0\n */\npublic class RoleCreator implements Serializable {\n\n    private static final long serialVersionUID = -1414989152963184543L;\n\n    /**\n     * represents the available {@link Role} field\n     */\n    public enum RoleField {\n        NAME, DISPLAY_NAME, DESCRIPTION, @Deprecated\n        ICON_NAME, @Deprecated\n        ICON_PATH, ICON_FILENAME, ICON_CONTENT\n    }\n\n    private final Map<RoleField, Serializable> fields;\n\n    /**\n     * create a new creator instance with a given role name\n     *\n     * @param name\n     *        The name of the role to create\n     */\n    public RoleCreator(final String name) {\n        fields = new HashMap<>(5);\n        fields.put(RoleField.NAME, name);\n    }\n\n    /**\n     * @param displayName\n     *        The role's display name to create\n     * @return The current {@link RoleCreator}\n     */\n    public RoleCreator setDisplayName(final String displayName) {\n        fields.put(RoleField.DISPLAY_NAME, displayName);\n        return this;\n    }\n\n    /**\n     * @param description\n     *        The role's description to create\n     * @return The current {@link RoleCreator}\n     */\n    public RoleCreator setDescription(final String description) {\n        fields.put(RoleField.DESCRIPTION, description);\n        return this;\n    }\n\n    /**\n     * @param iconName\n     *        The role's icon name to create\n     * @return The current {@link RoleCreator}\n     * @deprecated since 7.3.0 use #setIcon\n     */\n    @Deprecated\n    public RoleCreator setIconName(final String iconName) {\n        return this;\n    }\n\n    /**\n     * @param iconPath\n     *        The role's icon path to create\n     * @return The current {@link RoleCreator}\n     * @deprecated since 7.3.0 use #setIcon\n     */\n    @Deprecated\n    public RoleCreator setIconPath(final String iconPath) {\n        return this;\n    }\n\n    /**\n     * set the icon on the role to be created\n     *\n     * @param filename the filename of the icon\n     * @param content the content of the icon\n     * @return the role created\n     */\n    public RoleCreator setIcon(String filename, byte[] content) {\n        fields.put(RoleField.ICON_FILENAME, filename);\n        fields.put(RoleField.ICON_CONTENT, content);\n        return this;\n    }\n\n    /**\n     * @return The current role's information to create\n     */\n    public Map<RoleField, Serializable> getFields() {\n        return fields;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (o == null || getClass() != o.getClass())\n            return false;\n        RoleCreator that = (RoleCreator) o;\n        return Objects.equals(fields, that.fields);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(fields);\n    }\n\n    @Override\n    public String toString() {\n        return \"RoleCreator{\" +\n                \"fields=\" + fields +\n                '}';\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/RoleCriterion.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\n/**\n * list the available {@link Role} sort orders\n *\n * @author Yanyan Liu\n * @see Role\n * @since 6.0.0\n */\npublic enum RoleCriterion {\n\n    /**\n     * Name ascending order\n     */\n    NAME_ASC,\n\n    /**\n     * Label ascending order\n     */\n    DISPLAY_NAME_ASC,\n\n    /**\n     * Name descending order\n     */\n    NAME_DESC,\n\n    /**\n     * Label descending order\n     */\n    DISPLAY_NAME_DESC,\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/RoleNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport org.bonitasoft.engine.exception.NotFoundException;\n\n/**\n * thrown when a {@link Role} is not found in organization\n *\n * @author Bole Zhang\n * @author Matthieu Chaffotte\n * @see Role\n * @since 6.0.0\n */\npublic class RoleNotFoundException extends NotFoundException {\n\n    private static final long serialVersionUID = 7582998881291080021L;\n\n    /**\n     * creates a new exception instance with the given exception as cause\n     *\n     * @param cause the exception cause\n     */\n    public RoleNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/RoleSearchDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\n/**\n * holds constants about {@link Role} search filters.\n *\n * @author Matthieu Chaffotte\n * @see Role\n * @since 6.0.0\n */\npublic class RoleSearchDescriptor {\n\n    /** filter search on Role's id */\n    public static final String ID = \"id\";\n\n    /** filter search on Role's name */\n    public static final String NAME = \"name\";\n\n    /** filter search on Role's display name */\n    public static final String DISPLAY_NAME = \"displayName\";\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/RoleUpdater.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\n\n/**\n * represents a helper for updating a {@link Role}. Chaining is possible with this updator to ease the {@link Role}\n * update.\n * <br>\n * For instance, new RoleUpdater(\"member\").setDisplayName(\"Member\").setIconName(\"userIcon\");\n *\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @see Role\n * @since 6.0.0\n */\npublic class RoleUpdater implements Serializable {\n\n    private static final long serialVersionUID = 728214104237982027L;\n\n    /**\n     * represent the available {@link Role} fields\n     */\n    public enum RoleField {\n        NAME, DISPLAY_NAME, DESCRIPTION, @Deprecated\n        ICON_NAME, @Deprecated\n        ICON_PATH, ICON_FILENAME, ICON_CONTENT\n    }\n\n    private final Map<RoleField, Serializable> fields;\n\n    /**\n     * Default Constructor.\n     */\n    public RoleUpdater() {\n        fields = new HashMap<>(5);\n    }\n\n    /**\n     * @param name\n     *        The role's new name to update\n     * @return The current {@link RoleUpdater} for chaining purpose\n     */\n    public RoleUpdater setName(final String name) {\n        fields.put(RoleField.NAME, name);\n        return this;\n    }\n\n    /**\n     * @param displayName\n     *        The role's display name to update\n     * @return The current {@link RoleUpdater} for chaining purpose\n     */\n    public RoleUpdater setDisplayName(final String displayName) {\n        fields.put(RoleField.DISPLAY_NAME, displayName);\n        return this;\n    }\n\n    /**\n     * @param description\n     *        The role's description to update\n     * @return The current {@link RoleUpdater} for chaining purpose\n     */\n    public RoleUpdater setDescription(final String description) {\n        fields.put(RoleField.DESCRIPTION, description);\n        return this;\n    }\n\n    /**\n     * @param iconName\n     *        The role's icon name to update\n     * @return The current {@link RoleUpdater} for chaining purpose\n     * @deprecated since 7.3.0 use #setIcon\n     */\n    @Deprecated\n    public RoleUpdater setIconName(final String iconName) {\n        return this;\n    }\n\n    /**\n     * @param iconPath\n     *        The role's icon path to update\n     * @return The current {@link RoleUpdater} for chaining purpose\n     * @deprecated since 7.3.0 use #setIcon\n     */\n    @Deprecated\n    public RoleUpdater setIconPath(final String iconPath) {\n        return this;\n    }\n\n    /**\n     * set the icon on the role to be created\n     *\n     * @param filename the filename of the icon\n     * @param content the content of the icon\n     * @return the role created\n     */\n    public RoleUpdater setIcon(String filename, byte[] content) {\n        fields.put(RoleUpdater.RoleField.ICON_FILENAME, filename);\n        fields.put(RoleUpdater.RoleField.ICON_CONTENT, content);\n        return this;\n    }\n\n    /**\n     * @return The role's fields to update\n     */\n    public Map<RoleField, Serializable> getFields() {\n        return fields;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (o == null || getClass() != o.getClass())\n            return false;\n        RoleUpdater that = (RoleUpdater) o;\n        return Objects.equals(fields, that.fields);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(fields);\n    }\n\n    @Override\n    public String toString() {\n        return \"RoleUpdater{\" +\n                \"fields=\" + fields +\n                '}';\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/UserCreator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport org.bonitasoft.engine.identity.ContactDataCreator.ContactDataField;\n\n/**\n * represents a helper for creating {@link User}. Chaining is possible with this creator to ease the {@link User}\n * creation.\n * <br>\n * For instance, new UserCreator(\"john.doe\", \"password\").setFirstName(\"John\").setLastName(\"Doe\");\n *\n * @author Matthieu Chaffotte\n * @see User\n * @since 6.0.0\n */\npublic class UserCreator implements Serializable {\n\n    private static final long serialVersionUID = -1414989152963184543L;\n\n    /**\n     * represents the available {@link User} field\n     */\n    public enum UserField {\n        NAME, PASSWORD, FIRST_NAME, LAST_NAME, @Deprecated\n        ICON_NAME, @Deprecated\n        ICON_PATH, TITLE, JOB_TITLE, MANAGER_ID, ENABLED, ICON_FILENAME, ICON_CONTENT\n    }\n\n    private final Map<UserField, Serializable> fields;\n\n    private final Map<ContactDataField, Serializable> persoFields;\n\n    private final Map<ContactDataField, Serializable> proFields;\n\n    /**\n     * create a new creator instance with a given user name and password\n     *\n     * @param name the name of the user to create\n     * @param password the password of the user to create\n     */\n    public UserCreator(final String name, final String password) {\n        fields = new HashMap<>(5);\n        fields.put(UserField.NAME, name);\n        fields.put(UserField.PASSWORD, password);\n        persoFields = new HashMap<>();\n        proFields = new HashMap<>();\n    }\n\n    /**\n     * @param firstName the user's firstname to create\n     * @return the current {@link UserCreator}\n     */\n    public UserCreator setFirstName(final String firstName) {\n        fields.put(UserField.FIRST_NAME, firstName);\n        return this;\n    }\n\n    /**\n     * @param lastName the user's lastName to create\n     * @return the current {@link UserCreator}\n     */\n    public UserCreator setLastName(final String lastName) {\n        fields.put(UserField.LAST_NAME, lastName);\n        return this;\n    }\n\n    /**\n     * @param iconName the user's icon name to create\n     * @return the current {@link UserCreator}\n     * @deprecated since 7.3.0 use #setIcon\n     */\n    @Deprecated\n    public UserCreator setIconName(final String iconName) {\n        return this;\n    }\n\n    /**\n     * @param iconPath the user's icon path to create\n     * @return the current {@link UserCreator}\n     * @deprecated since 7.3.0 use #setIcon\n     */\n    @Deprecated\n    public UserCreator setIconPath(final String iconPath) {\n        return this;\n    }\n\n    /**\n     * @param title the user's title to create\n     * @return the current {@link UserCreator}\n     */\n    public UserCreator setTitle(final String title) {\n        fields.put(UserField.TITLE, title);\n        return this;\n    }\n\n    /**\n     * @param jobTitle the user's jobTitle to create\n     * @return the current {@link UserCreator}\n     */\n    public UserCreator setJobTitle(final String jobTitle) {\n        fields.put(UserField.JOB_TITLE, jobTitle);\n        return this;\n    }\n\n    /**\n     * @param managerUserId the user's manager id to create\n     * @return the current {@link UserCreator}\n     */\n    public UserCreator setManagerUserId(final long managerUserId) {\n        fields.put(UserField.MANAGER_ID, managerUserId);\n        return this;\n    }\n\n    /**\n     * @param enabled enabled Boolean set to true if the user is enabled inside the organization\n     * @return the current {@link UserCreator}\n     */\n    public UserCreator setEnabled(final boolean enabled) {\n        fields.put(UserField.ENABLED, enabled);\n        return this;\n    }\n\n    /**\n     * @return the current user information to create\n     */\n    public Map<UserField, Serializable> getFields() {\n        return fields;\n    }\n\n    /**\n     * @return the current user personal information to create\n     */\n    public Map<ContactDataField, Serializable> getPersoFields() {\n        return persoFields;\n    }\n\n    /**\n     * @return the current user professional information to create\n     */\n    public Map<ContactDataField, Serializable> getProFields() {\n        return proFields;\n    }\n\n    /**\n     * @param creator the user's personal contact information to create\n     * @return the current {@link UserCreator}\n     */\n    public UserCreator setPersonalContactData(final ContactDataCreator creator) {\n        if (creator != null && creator.getFields() != null) {\n            persoFields.putAll(creator.getFields());\n        }\n        return this;\n    }\n\n    /**\n     * @param creator the user's professional contact information to create\n     * @return the current {@link UserCreator}\n     */\n    public UserCreator setProfessionalContactData(final ContactDataCreator creator) {\n        if (creator != null && creator.getFields() != null) {\n            proFields.putAll(creator.getFields());\n        }\n        return this;\n    }\n\n    public UserCreator setIcon(String filename, byte[] content) {\n        fields.put(UserField.ICON_FILENAME, filename);\n        fields.put(UserField.ICON_CONTENT, content);\n        return this;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (o == null || getClass() != o.getClass())\n            return false;\n        UserCreator that = (UserCreator) o;\n        return Objects.equals(fields, that.fields) &&\n                Objects.equals(persoFields, that.persoFields) &&\n                Objects.equals(proFields, that.proFields);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(fields, persoFields, proFields);\n    }\n\n    @Override\n    public String toString() {\n        return \"UserCreator{\" +\n                \"fields=\" + fields +\n                \", persoFields=\" + persoFields +\n                \", proFields=\" + proFields +\n                '}';\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/UserCriterion.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\n/**\n * lists the available {@link User} sort orders\n *\n * @author Matthieu Chaffotte\n * @see User\n * @since 6.0.0\n */\npublic enum UserCriterion {\n    /**\n     * First name ascending order\n     */\n    FIRST_NAME_ASC,\n    /**\n     * Last name ascending order\n     */\n    LAST_NAME_ASC,\n    /**\n     * User name ascending order\n     */\n    USER_NAME_ASC,\n    /**\n     * First name descending order\n     */\n    FIRST_NAME_DESC,\n    /**\n     * Last name descending order\n     */\n    LAST_NAME_DESC,\n    /**\n     * user name descending order\n     */\n    USER_NAME_DESC;\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/UserMembershipCriterion.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\n/**\n * lists the available {@link User} sort orders\n *\n * @author Bole Zhang\n * @author Matthieu Chaffotte\n * @author Baptiste Mesta\n * @see UserMembership\n * @since 6.0.0\n */\npublic enum UserMembershipCriterion {\n    /**\n     * roleName ascending order\n     */\n    ROLE_NAME_ASC,\n    /**\n     * groupName ascending order\n     */\n    GROUP_NAME_ASC,\n    /**\n     * roleName descending order\n     */\n    ROLE_NAME_DESC,\n    /**\n     * groupName descending order\n     */\n    GROUP_NAME_DESC,\n    /**\n     * assigned date ascending order\n     */\n    ASSIGNED_DATE_ASC,\n    /**\n     * assigned date descending order\n     */\n    ASSIGNED_DATE_DESC\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/UserNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport org.bonitasoft.engine.exception.NotFoundException;\n\n/**\n * thrown when a {@link User} is not found in the organization\n *\n * @author Matthieu Chaffotte\n * @see User\n * @since 6.0.0\n */\npublic class UserNotFoundException extends NotFoundException {\n\n    private static final long serialVersionUID = 8392620780257647179L;\n\n    /**\n     * creates a new exception instance with the given message\n     *\n     * @param message the exception message\n     */\n    public UserNotFoundException(final String message) {\n        super(message);\n    }\n\n    /**\n     * creates a new exception instance with the given exception as cause\n     *\n     * @param cause the exception cause\n     */\n    public UserNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/UserSearchDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\n/**\n * holds constants about {@link User} search filters.\n *\n * @author Matthieu Chaffotte\n * @author Emmanuel Duchastenier\n * @author Anthony Birembaut\n * @see User\n * @since 6.0.0\n */\npublic final class UserSearchDescriptor {\n\n    /** filter search on User's id */\n    public static final String ID = \"id\";\n\n    /** filter search on User's username */\n    public static final String USER_NAME = \"userName\";\n\n    /** filter search on User's firstname */\n    public static final String FIRST_NAME = \"firstName\";\n\n    /** filter search on User's lastname */\n    public static final String LAST_NAME = \"lastName\";\n\n    /** filter search on User's group id */\n    public static final String GROUP_ID = \"groupId\";\n\n    /** filter search on User's role id */\n    public static final String ROLE_ID = \"roleId\";\n\n    /** filter search on the User's manager user id */\n    public static final String MANAGER_USER_ID = \"managerUserId\";\n\n    /** filter search on User's activation */\n    public static final String ENABLED = \"enabled\";\n\n    /** filter search on User's last connection date */\n    public static final String LAST_CONNECTION = \"lastConnection\";\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/UserUpdater.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\n\n/**\n * represents a helper for updating a {@link User}. Chaining is possible with this updator to ease the {@link User}\n * update.\n * <br>\n * For instance, new UserUpdater.setUsername(\"john.doe\").setFirstname(\"John\").setLastname(\"Doe\");\n *\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @see User\n * @since 6.0.0\n */\npublic class UserUpdater implements Serializable {\n\n    private static final long serialVersionUID = 4565052647320534796L;\n\n    /**\n     * represent the available {@link User} fields\n     */\n    public enum UserField {\n        USER_NAME, PASSWORD, FIRST_NAME, LAST_NAME, @Deprecated\n        ICON_NAME, @Deprecated\n        ICON_PATH, TITLE, JOB_TITLE, MANAGER_ID, ENABLED, ICON_FILENAME, ICON_CONTENT\n    }\n\n    private final Map<UserField, Serializable> fields;\n\n    private ContactDataUpdater persoContactUpdater;\n\n    private ContactDataUpdater proContactUpdater;\n\n    /**\n     * Default Constructor.\n     */\n    public UserUpdater() {\n        fields = new HashMap<>(5);\n    }\n\n    /**\n     * @param name the user's username to update\n     * @return the current {@link UserUpdater} for chaining purpose\n     */\n    public UserUpdater setUserName(final String name) {\n        fields.put(UserField.USER_NAME, name);\n        return this;\n    }\n\n    /**\n     * @param password the user's password to update\n     * @return the current {@link UserUpdater} for chaining purpose\n     */\n    public UserUpdater setPassword(final String password) {\n        fields.put(UserField.PASSWORD, password);\n        return this;\n    }\n\n    /**\n     * @param firstName the user's firstname to update\n     * @return the current {@link UserUpdater} for chaining purpose\n     */\n    public UserUpdater setFirstName(final String firstName) {\n        fields.put(UserField.FIRST_NAME, firstName);\n        return this;\n    }\n\n    /**\n     * @param lastName the user's lastname to update\n     * @return the current {@link UserUpdater} for chaining purpose\n     */\n    public UserUpdater setLastName(final String lastName) {\n        fields.put(UserField.LAST_NAME, lastName);\n        return this;\n    }\n\n    /**\n     * @param iconName the user's icon name to update\n     * @return the current {@link UserUpdater} for chaining purpose\n     * @deprecated since 7.3.0 use #setIcon\n     */\n    @Deprecated\n    public UserUpdater setIconName(final String iconName) {\n        return this;\n    }\n\n    /**\n     * @param managerId the user's manager id to update\n     * @return the current {@link UserUpdater} for chaining purpose\n     */\n    public UserUpdater setManagerId(final long managerId) {\n        fields.put(UserField.MANAGER_ID, managerId);\n        return this;\n    }\n\n    /**\n     * @param iconPath the user's icon path to update\n     * @return the current {@link UserUpdater} for chaining purpose\n     * @deprecated since 7.3.0 use #setIcon\n     */\n    @Deprecated\n    public UserUpdater setIconPath(final String iconPath) {\n        return this;\n    }\n\n    /**\n     * @param title the user's title to update\n     * @return the current {@link UserUpdater} for chaining purpose\n     */\n    public UserUpdater setTitle(final String title) {\n        fields.put(UserField.TITLE, title);\n        return this;\n    }\n\n    /**\n     * @param jobTitle the user's job title to update\n     * @return the current {@link UserUpdater} for chaining purpose\n     */\n    public UserUpdater setJobTitle(final String jobTitle) {\n        fields.put(UserField.JOB_TITLE, jobTitle);\n        return this;\n    }\n\n    /**\n     * @param enabled allow to know if the current user is enabled inside organization\n     * @return the current {@link UserUpdater} for chaining purpose\n     */\n    public UserUpdater setEnabled(final boolean enabled) {\n        fields.put(UserField.ENABLED, enabled);\n        return this;\n    }\n\n    /**\n     * @return the current user information to udpate\n     */\n    public Map<UserField, Serializable> getFields() {\n        return fields;\n    }\n\n    /**\n     * @param persoContactUpdater the user's personal contact information to update\n     * @return the current {@link UserUpdater} for chaining purpose\n     */\n    public UserUpdater setPersonalContactData(final ContactDataUpdater persoContactUpdater) {\n        this.persoContactUpdater = persoContactUpdater;\n        return this;\n    }\n\n    /**\n     * @param proContactUpdater the user's professional contact information to update\n     * @return the current {@link UserUpdater} for chaining purpose\n     */\n    public UserUpdater setProfessionalContactData(final ContactDataUpdater proContactUpdater) {\n        this.proContactUpdater = proContactUpdater;\n        return this;\n    }\n\n    /**\n     * @return the user's personal contact updater object\n     */\n    public ContactDataUpdater getPersoContactUpdater() {\n        return persoContactUpdater;\n    }\n\n    /**\n     * @return the professional contact updater object\n     */\n    public ContactDataUpdater getProContactUpdater() {\n        return proContactUpdater;\n    }\n\n    /**\n     * Has this updater at least one field to update (directly or in its personal / professional contact data)?\n     *\n     * @return true if there is at least one field to update\n     */\n    public boolean hasFields() {\n        return !getFields().isEmpty() || getPersoContactUpdater() != null && getPersoContactUpdater().hasFields()\n                || getProContactUpdater() != null && getProContactUpdater().hasFields();\n    }\n\n    public UserUpdater setIcon(String filename, byte[] content) {\n        fields.put(UserField.ICON_FILENAME, filename);\n        fields.put(UserField.ICON_CONTENT, content);\n        return this;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (o == null || getClass() != o.getClass())\n            return false;\n        UserUpdater that = (UserUpdater) o;\n        return Objects.equals(fields, that.fields) &&\n                Objects.equals(persoContactUpdater, that.persoContactUpdater) &&\n                Objects.equals(proContactUpdater, that.proContactUpdater);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(fields, persoContactUpdater, proContactUpdater);\n    }\n\n    @Override\n    public String toString() {\n        return \"UserUpdater{\" +\n                \"fields=\" + fields +\n                \", persoContactUpdater=\" + persoContactUpdater +\n                \", proContactUpdater=\" + proContactUpdater +\n                '}';\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/package-info.java",
    "content": "/**\n * Copyright (C) 2015 BonitaSoft S.A.\n * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n/**\n * <p>Manages information about an organization, that is, the set of users who can act in processes and the groups and\n * the organization's roles and the relation\n * between them.\n * <p>Contains helpers to create and updates organization's groups, roles, users and memberships</p>\n *\n * @see org.bonitasoft.engine.identity.Group\n * @see org.bonitasoft.engine.identity.Role\n * @see org.bonitasoft.engine.identity.User\n * @see org.bonitasoft.engine.identity.UserMembership\n * @since 6.0.0\n */\n\npackage org.bonitasoft.engine.identity;\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/io/FileContent.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.io;\n\nimport java.io.InputStream;\n\npublic class FileContent {\n\n    protected String fileName;\n\n    protected InputStream inputStream;\n\n    protected String mimeType;\n\n    protected long size = -1L;\n\n    public FileContent(String fileName, InputStream inputStream, String mimeType) {\n        this.fileName = fileName;\n        this.inputStream = inputStream;\n        this.mimeType = mimeType;\n    }\n\n    public FileContent(String fileName, InputStream inputStream, String mimeType, long size) {\n        this.fileName = fileName;\n        this.inputStream = inputStream;\n        this.mimeType = mimeType;\n        this.size = size;\n    }\n\n    public String getFileName() {\n        return fileName;\n    }\n\n    public InputStream getInputStream() {\n        return inputStream;\n    }\n\n    public String getMimeType() {\n        return mimeType;\n    }\n\n    public long getSize() {\n        return size;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/io/FileOperations.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.io;\n\nimport static java.nio.charset.StandardCharsets.UTF_8;\nimport static java.nio.file.StandardCopyOption.REPLACE_EXISTING;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.file.FileSystem;\nimport java.nio.file.FileSystems;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.StandardOpenOption;\nimport java.util.UUID;\nimport java.util.zip.ZipFile;\n\n/**\n * @author Baptiste Mesta\n * @author Emmanuel Duchastenier\n * @author Danila Mazour\n */\npublic class FileOperations {\n\n    private FileOperations() {\n        // private constructor\n    }\n\n    /**\n     * Retrieve the content of a file inside a ZIP file.\n     *\n     * @param zip ZIP file to parse\n     * @param filePath path of the file to search inside the ZIP\n     * @return the bytes read from the searched file\n     * @throws FileNotFoundException if the ZIP file does not contain the searched file\n     * @throws java.util.zip.ZipException if a ZIP format error has occurred (e.g. the input zip file is not a ZIP)\n     * @throws IOException if an I/O error has occurred\n     */\n    public static byte[] getFileFromZip(File zip, String filePath) throws IOException {\n        try (var zipFile = new ZipFile(zip)) {\n            var entry = zipFile.getEntry(filePath);\n            if (entry == null) {\n                throw new FileNotFoundException(String.format(\"'%s' not found in %s\", filePath, zip.getName()));\n            }\n            try (var is = zipFile.getInputStream(entry)) {\n                return is.readAllBytes();\n            }\n        }\n    }\n\n    public static void updateFileContent(File zip, String filePath, InputStream newContent) throws IOException {\n        Path zipFilePath = zip.toPath();\n        try (FileSystem fs = FileSystems.newFileSystem(zipFilePath, (ClassLoader) null)) {\n            Path source = fs.getPath(filePath);\n            Path temp = fs.getPath(\"./temp_\" + UUID.randomUUID().toString());\n            Files.write(temp, newContent.readAllBytes(), StandardOpenOption.CREATE_NEW);\n            Files.move(temp, source, REPLACE_EXISTING);\n        }\n    }\n\n    public static String read(File file) throws IOException {\n        return Files.readString(file.toPath(), UTF_8);\n    }\n\n    public static byte[] readFully(File file) throws IOException {\n        return Files.readAllBytes(file.toPath());\n    }\n\n    public static byte[] readFully(InputStream in) throws IOException {\n        ByteArrayOutputStream out = new ByteArrayOutputStream();\n        byte[] buf = new byte[1024];\n        int n;\n        while ((n = in.read(buf)) > 0) {\n            out.write(buf, 0, n);\n        }\n        return out.toByteArray();\n    }\n\n    public static boolean isBarFile(String fileName) {\n        return fileName.endsWith(\".bar\");\n    }\n\n    public static boolean isXmlFile(String fileName) {\n        return fileName.endsWith(\".xml\");\n    }\n\n    public static boolean isZipFile(File file) {\n        return file.getName().endsWith(\".zip\");\n    }\n\n    public static InputStream asInputStream(byte[] bytes) {\n        return new ByteArrayInputStream(bytes);\n    }\n\n    public static InputStream asInputStream(String content) {\n        return new ByteArrayInputStream(content.getBytes(UTF_8));\n    }\n\n    public static InputStream resource(String name) {\n        return FileOperations.class.getResourceAsStream(name);\n    }\n\n    /**\n     * Gets the contents of a classpath resource as a byte array.\n     */\n    public static byte[] resourceAsBytes(String name) throws IOException {\n        return resource(name).readAllBytes();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/io/IOUtil.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.io;\n\nimport static java.util.Arrays.stream;\nimport static java.util.Collections.emptyList;\n\nimport java.io.BufferedInputStream;\nimport java.io.BufferedOutputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStreamWriter;\nimport java.net.URI;\nimport java.net.URL;\nimport java.nio.MappedByteBuffer;\nimport java.nio.channels.FileChannel;\nimport java.nio.channels.FileChannel.MapMode;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.AbstractMap;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.jar.JarEntry;\nimport java.util.jar.JarOutputStream;\nimport java.util.stream.Collectors;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipInputStream;\nimport java.util.zip.ZipOutputStream;\n\nimport javax.tools.JavaCompiler;\nimport javax.tools.JavaFileObject;\nimport javax.tools.SimpleJavaFileObject;\nimport javax.tools.StandardJavaFileManager;\nimport javax.tools.StandardLocation;\nimport javax.tools.ToolProvider;\n\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\n@Slf4j\npublic class IOUtil {\n\n    private IOUtil() {\n        // Utility class\n    }\n\n    public static final String TMP_DIRECTORY = System.getProperty(\"java.io.tmpdir\");\n    private static final int BUFFER_SIZE = 100000;\n    private static final String CLASS_EXT = \".class\";\n\n    public static byte[] generateJar(final Class<?>... classes) throws IOException {\n        return generateJar(getResources(classes));\n    }\n\n    public static byte[] generateJar(String className, String... content) throws IOException {\n        return generateJar(emptyList(), new AbstractMap.SimpleEntry<>(className, String.join(\"\\n\", content)));\n    }\n\n    static class StringJavaFileObject extends SimpleJavaFileObject {\n\n        private final String sourceCode;\n\n        StringJavaFileObject(String className, String content) {\n            super(URI.create(\"string:///\" + className.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);\n            this.sourceCode = content;\n        }\n\n        public CharSequence getCharContent(boolean var1) {\n            return this.sourceCode;\n        }\n    }\n\n    public static byte[] generateJar(Map.Entry<String, String>... classFiles) throws IOException {\n        return generateJar(emptyList(), classFiles);\n    }\n\n    public static byte[] generateJar(List<Path> additionalJar, String className, String... content) throws IOException {\n        return generateJar(additionalJar, new AbstractMap.SimpleEntry<>(className, String.join(\"\\n\", content)));\n    }\n\n    public static byte[] generateJar(List<Path> additionalJar, Map.Entry<String, String>... classFiles)\n            throws IOException {\n        List<StringJavaFileObject> sourceFiles = stream(classFiles).map(classFile -> {\n            StringJavaFileObject sourceFile = new StringJavaFileObject(classFile.getKey(), classFile.getValue());\n            return sourceFile;\n        }).collect(Collectors.toList());\n\n        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();\n        StandardJavaFileManager standardFileManager = compiler.getStandardFileManager(null, null, null);\n        updateClassPath(additionalJar, standardFileManager);\n        standardFileManager.setLocation(StandardLocation.CLASS_OUTPUT,\n                Collections.singleton(Files.createTempDirectory(\"compile-test\").toFile()));\n        JavaCompiler.CompilationTask run = compiler.getTask(null, standardFileManager, null, null, null,\n                sourceFiles);\n        Boolean call = run.call();\n        if (!call) {\n            throw new IllegalArgumentException(\"Unable to compile the file, see logs\");\n        }\n\n        Map<String, byte[]> resources = new HashMap<>();\n        for (Map.Entry<String, String> classFile : classFiles) {\n            String className = classFile.getKey();\n            JavaFileObject javaFileForInput = standardFileManager.getJavaFileForInput(StandardLocation.CLASS_OUTPUT,\n                    className, JavaFileObject.Kind.CLASS);\n            byte[] bytes = Files.readAllBytes(Paths.get(javaFileForInput.toUri()));\n            resources.put(className.replace(\".\", \"/\") + \".class\", bytes);\n        }\n        return generateJar(resources);\n    }\n\n    private static void updateClassPath(List<Path> additionalJar, StandardJavaFileManager standardFileManager)\n            throws IOException {\n        Iterable<? extends File> location = standardFileManager.getLocation(StandardLocation.CLASS_PATH);\n        ArrayList<File> classPath = new ArrayList<>();\n        location.forEach(classPath::add);\n        additionalJar.stream().map(Path::toFile).forEach(classPath::add);\n        standardFileManager.setLocation(StandardLocation.CLASS_PATH, classPath);\n    }\n\n    public static Map<String, byte[]> getResources(final Class<?>... classes) throws IOException {\n        if (classes == null || classes.length == 0) {\n            final String message = \"No classes available\";\n            throw new IOException(message);\n        }\n        final Map<String, byte[]> resources = new HashMap<>();\n        for (final Class<?> clazz : classes) {\n            resources.put(clazz.getName().replace(\".\", \"/\") + CLASS_EXT, getClassData(clazz));\n            for (final Class<?> internalClass : clazz.getDeclaredClasses()) {\n                resources.put(internalClass.getName().replace(\".\", \"/\") + CLASS_EXT, getClassData(internalClass));\n            }\n        }\n        return resources;\n    }\n\n    public static byte[] getClassData(final Class<?> clazz) throws IOException {\n        if (clazz == null) {\n            final String message = \"Class is null\";\n            throw new IOException(message);\n        }\n        final String resource = clazz.getName().replace('.', '/') + CLASS_EXT;\n        byte[] data;\n        try (InputStream inputStream = clazz.getClassLoader().getResourceAsStream(resource)) {\n            if (inputStream == null) {\n                throw new IOException(\n                        \"Impossible to get stream from class: \" + clazz.getName() + \", className= \" + resource);\n            }\n            data = IOUtil.getAllContentFrom(inputStream);\n        }\n        return data;\n    }\n\n    public static byte[] generateJar(final Map<String, byte[]> resources) throws IOException {\n        if (resources == null || resources.isEmpty()) {\n            final String message = \"No resources available\";\n            throw new IOException(message);\n        }\n\n        ByteArrayOutputStream baos = null;\n        JarOutputStream jarOutStream = null;\n        try {\n            baos = new ByteArrayOutputStream();\n            jarOutStream = new JarOutputStream(new BufferedOutputStream(baos));\n            for (final Map.Entry<String, byte[]> resource : resources.entrySet()) {\n                jarOutStream.putNextEntry(new JarEntry(resource.getKey()));\n                jarOutStream.write(resource.getValue());\n            }\n            jarOutStream.flush();\n            baos.flush();\n        } finally {\n            if (jarOutStream != null) {\n                jarOutStream.close();\n            }\n            if (baos != null) {\n                baos.close();\n            }\n        }\n\n        return baos.toByteArray();\n    }\n\n    /**\n     * Return the whole underlying stream content into a single String.\n     * Warning: the whole content of stream will be kept in memory!! Use with\n     * care!\n     *\n     * @param in the stream to read\n     * @return the whole content of the stream in a single String.\n     * @throws IOException if an I/O exception occurs\n     */\n    public static byte[] getAllContentFrom(final InputStream in) throws IOException {\n        if (in == null) {\n            throw new IOException(\"The InputStream is null!\");\n        }\n        final byte[] buffer = new byte[BUFFER_SIZE];\n        final byte[] resultArray;\n\n        try (BufferedInputStream bis = new BufferedInputStream(in);\n                ByteArrayOutputStream result = new ByteArrayOutputStream()) {\n            int amountRead;\n            while ((amountRead = bis.read(buffer)) > 0) {\n                result.write(buffer, 0, amountRead);\n            }\n            resultArray = result.toByteArray();\n            result.flush();\n        }\n        return resultArray;\n    }\n\n    /**\n     * Equivalent to {@link #getAllContentFrom(InputStream) getAllContentFrom(new\n     * FileInputStream(file))};\n     *\n     * @param file the file to read\n     * @return the whole content of the file in a single String.\n     * @throws IOException If an I/O exception occurs\n     */\n    public static byte[] getAllContentFrom(final File file) throws IOException {\n        try (InputStream in = new FileInputStream(file)) {\n            return getAllContentFrom(in);\n        }\n    }\n\n    /**\n     * Return the whole underlying stream content into a single String.\n     * Warning: the whole content of stream will be kept in memory!! Use with\n     * care!\n     *\n     * @param url the URL to read\n     * @return the whole content of the stream in a single String.\n     * @throws IOException if an I/O exception occurs\n     */\n    public static byte[] getAllContentFrom(final URL url) throws IOException {\n        try (InputStream in = url.openStream()) {\n            return getAllContentFrom(in);\n        }\n    }\n\n    public static File createTempDirectory(final URI directoryPath) {\n        final File tmpDir = new File(directoryPath);\n        tmpDir.setReadable(true);\n        tmpDir.setWritable(true);\n\n        mkdirs(tmpDir);\n\n        Files.isSymbolicLink(tmpDir.toPath());\n\n        try {\n            Runtime.getRuntime().addShutdownHook(new Thread(() -> {\n                try {\n                    final boolean deleted = deleteDir(tmpDir);\n                    if (!deleted) {\n                        var errorMsg = \"Unable to delete directory: \" + tmpDir;\n                        log.error(errorMsg);\n                        if (!tmpDir.exists()) {\n                            throw new FileNotFoundException(\"Directory does not exist: \" + tmpDir);\n                        }\n                        throw new IOException(errorMsg);\n                    }\n                } catch (final IOException e) {\n                    throw new RuntimeException(e);\n                }\n            }));\n        } catch (IllegalStateException ignored) {\n            // happen in case of hook already registered and when shutting down\n        }\n        return tmpDir;\n    }\n\n    public static boolean deleteDir(final File dir) throws IOException {\n        return deleteDir(dir, 1, 0);\n    }\n\n    public static boolean deleteDir(final File dir, final int attempts, final long sleepTime) throws IOException {\n        if (dir != null) {\n            boolean result = true;\n            if (!dir.exists()) {\n                return true; //already deleted\n            }\n            if (!dir.isDirectory()) {\n                throw new IOException(\"Unable to delete directory: \" + dir + \", it is not a directory\");\n            }\n            for (final File file : dir.listFiles()) {\n                if (file.isDirectory()) {\n                    result &= deleteDir(file, attempts, sleepTime);\n                } else {\n                    result &= deleteFile(file, attempts, sleepTime);\n                }\n            }\n            return result && deleteFile(dir, attempts, sleepTime);\n        }\n        return false;\n    }\n\n    public static boolean deleteFile(final File f, final int attempts, final long sleepTime) {\n        int retries = attempts;\n        while (retries > 0) {\n            if (f.delete()) {\n                break;\n            }\n            retries--;\n            try {\n                Thread.sleep(sleepTime);\n            } catch (final InterruptedException ignored) {\n            }\n        }\n        return retries > 0;\n    }\n\n    public static byte[] zip(final Map<String, byte[]> files) throws IOException {\n        final ByteArrayOutputStream baos = new ByteArrayOutputStream();\n        try (ZipOutputStream zos = new ZipOutputStream(baos)) {\n            for (final Entry<String, byte[]> file : files.entrySet()) {\n                zos.putNextEntry(new ZipEntry(file.getKey()));\n                zos.write(file.getValue());\n                zos.closeEntry();\n            }\n            return baos.toByteArray();\n        }\n    }\n\n    public static void unzipToFolder(final InputStream inputStream, final File outputFolder) throws IOException {\n        try (ZipInputStream zipInputstream = new ZipInputStream(inputStream)) {\n            extractZipEntries(zipInputstream, outputFolder);\n        }\n    }\n\n    private static void mkdirs(final File file) {\n        if (!file.exists()) {\n            file.mkdirs();\n        }\n    }\n\n    private static void extractZipEntries(final ZipInputStream zipInputstream, final File outputFolder)\n            throws IOException {\n        final String canonicalDestDir = outputFolder.getCanonicalPath() + File.separator;\n        ZipEntry zipEntry;\n        while ((zipEntry = zipInputstream.getNextEntry()) != null) {\n            try {\n                // For each entry, a file is created in the output directory \"folder\"\n                final File outputFile = new File(outputFolder.getAbsolutePath(), zipEntry.getName());\n                // Prevent Zip Slip: ensure the resolved path stays within the output folder\n                if (!outputFile.getCanonicalPath().startsWith(canonicalDestDir)) {\n                    throw new IOException(\"Zip entry is outside of the target directory\");\n                }\n                // If the entry is a directory, it creates in the output folder, and we go to the next entry (continue).\n                if (zipEntry.isDirectory()) {\n                    mkdirs(outputFile);\n                    continue;\n                }\n                writeZipInputToFile(zipInputstream, outputFile);\n            } finally {\n                zipInputstream.closeEntry();\n            }\n        }\n    }\n\n    private static void writeZipInputToFile(final ZipInputStream zipInputstream, final File outputFile)\n            throws FileNotFoundException, IOException {\n        // The input is a file. An FileOutputStream is created to write the content of the new file.\n        mkdirs(outputFile.getParentFile());\n        try (FileOutputStream fileOutputStream = new FileOutputStream(outputFile)) {\n            // The contents of the new file, that is read from the ZipInputStream using a buffer (byte []), is written.\n            int bytesRead;\n            final byte[] buffer = new byte[BUFFER_SIZE];\n            while ((bytesRead = zipInputstream.read(buffer)) > -1) {\n                fileOutputStream.write(buffer, 0, bytesRead);\n            }\n            fileOutputStream.flush();\n        } catch (final IOException ioe) {\n            // In case of error, the file is deleted\n            outputFile.delete();\n            throw ioe;\n        }\n    }\n\n    public static void writeContentToFile(final String content, final File outputFile) throws IOException {\n        final FileOutputStream fileOutput = new FileOutputStream(outputFile);\n        writeContentToFileOutputStream(content, fileOutput);\n    }\n\n    public static void writeContentToFileOutputStream(final String content, final FileOutputStream fileOutput)\n            throws IOException {\n        try (OutputStreamWriter out = new OutputStreamWriter(fileOutput, StandardCharsets.UTF_8)) {\n            out.write(content);\n            out.flush();\n        } finally {\n            fileOutput.close();\n        }\n    }\n\n    public static void write(final File file, final byte[] fileContent) throws IOException {\n        try (FileOutputStream fos = new FileOutputStream(file);\n                BufferedOutputStream bos = new BufferedOutputStream(fos)) {\n            bos.write(fileContent);\n            bos.flush();\n        }\n    }\n\n    public static byte[] getContent(final File file) throws IOException {\n        try (FileInputStream fin = new FileInputStream(file);\n                FileChannel ch = fin.getChannel()) {\n            final int size = (int) ch.size();\n            final MappedByteBuffer buf = ch.map(MapMode.READ_ONLY, 0, size);\n            final byte[] bytes = new byte[size];\n            buf.get(bytes);\n            return bytes;\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/io/IOUtils.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.io;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipInputStream;\nimport java.util.zip.ZipOutputStream;\n\nimport javax.xml.XMLConstants;\nimport javax.xml.transform.OutputKeys;\nimport javax.xml.transform.Transformer;\nimport javax.xml.transform.TransformerException;\nimport javax.xml.transform.TransformerFactory;\nimport javax.xml.transform.dom.DOMSource;\nimport javax.xml.transform.stream.StreamResult;\n\nimport org.w3c.dom.Document;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class IOUtils {\n\n    public static byte[] zip(final String fileName, final byte[] fileContent) throws IOException {\n        final ByteArrayOutputStream baos = new ByteArrayOutputStream();\n        final ZipOutputStream zos = new ZipOutputStream(baos);\n        try {\n            zos.putNextEntry(new ZipEntry(fileName));\n            zos.write(fileContent);\n        } finally {\n            zos.closeEntry();\n            zos.close();\n        }\n        return baos.toByteArray();\n    }\n\n    public static Map<String, byte[]> unzip(final byte[] zippedContent) throws IOException {\n        final ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(zippedContent));\n        final Map<String, byte[]> resources = new HashMap<>();\n        try {\n            ZipEntry entry = zis.getNextEntry();\n            while (entry != null) {\n                final ByteArrayOutputStream baos = new ByteArrayOutputStream();\n                try {\n                    int len;\n                    final byte[] buffer = new byte[1024];\n                    while ((len = zis.read(buffer)) > 0) {\n                        baos.write(buffer, 0, len);\n                    }\n                } finally {\n                    baos.close();\n                }\n                resources.put(entry.getName(), baos.toByteArray());\n                entry = zis.getNextEntry();\n            }\n        } finally {\n            zis.closeEntry();\n            zis.close();\n        }\n        return resources;\n    }\n\n    public static File createTempDirectory(final String prefix) throws IOException {\n        final File tmpDirectory = File.createTempFile(prefix, null);\n        return createDirectory(tmpDirectory);\n    }\n\n    public static File createSubDirectory(final File directory, final String child) {\n        final File subDir = new File(directory, child);\n        return createDirectory(subDir);\n    }\n\n    public static File createDirectoryIfNotExists(File dir) {\n        if (!dir.exists()) {\n            return createDirectory(dir);\n        }\n        return dir;\n    }\n\n    private static File createDirectory(final File dir) {\n        dir.delete();\n        dir.mkdir();\n        return dir;\n    }\n\n    public static void saveDocument(final Document document, final File destination)\n            throws IOException, TransformerException {\n        if (document == null) {\n            throw new IllegalArgumentException(\"Document should not be null.\");\n        }\n        final TransformerFactory transformerFactory = TransformerFactory.newInstance();\n        try {\n            transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, \"\"); // security-compliant\n            transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, \"\"); // security-compliant\n        } catch (IllegalArgumentException e) {\n            //ignored, if not supported by the implementation\n        }\n        final Transformer tf = transformerFactory.newTransformer();\n        tf.setOutputProperty(OutputKeys.ENCODING, \"UTF-8\");\n        tf.setOutputProperty(OutputKeys.INDENT, \"yes\");\n        try (FileOutputStream fos = new FileOutputStream(destination)) {\n            final StreamResult outputTarget = new StreamResult(fos);\n            tf.transform(new DOMSource(document), outputTarget);\n        }\n    }\n\n    public static File createTempFile(String name, String suffix, byte[] content) throws IOException {\n        return Files.write(Files.createTempFile(name, suffix), content).toFile();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/io/PropertiesManager.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.io;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.FileReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.io.Reader;\nimport java.net.URL;\nimport java.util.Properties;\n\n/**\n * @author Matthieu Chaffotte\n * @author Frederic Bouquet\n * @author Celine Souchet\n */\npublic class PropertiesManager {\n\n    public static void saveProperties(final Properties properties, final File file) throws IOException {\n        try (FileOutputStream outputStream = new FileOutputStream(file)) {\n            properties.store(outputStream, \"Storing modified properties\");\n        }\n    }\n\n    public static Properties getProperties(final URL url) throws IOException {\n        try (InputStreamReader reader = new InputStreamReader(url.openStream())) {\n            return getProperties(reader);\n        }\n    }\n\n    public static Properties getProperties(final String fileName) throws IOException {\n        return getProperties(new File(fileName));\n    }\n\n    public static Properties getProperties(final File file) throws IOException {\n        try (FileReader reader = new FileReader(file)) {\n            return getProperties(reader);\n        }\n    }\n\n    private static Properties getProperties(final Reader reader) throws IOException {\n        final Properties properties = new Properties();\n        properties.load(reader);\n        return properties;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/job/FailedJob.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.job;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * Represent failure(s) that happened to a certain job (e.g. Timer execution)\n */\npublic interface FailedJob extends Serializable {\n\n    long getJobDescriptorId();\n\n    String getJobName();\n\n    String getDescription();\n\n    /**\n     * @return the exception thrown by the last failing execution\n     */\n    String getLastMessage();\n\n    /**\n     * @return the number of times a job failed before replaying it manually\n     */\n    int getNumberOfFailures();\n\n    /**\n     * @return the Date of the last failure\n     */\n    Date getLastUpdateDate();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/job/impl/FailedJobImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.job.impl;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.job.FailedJob;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class FailedJobImpl implements FailedJob {\n\n    private static final long serialVersionUID = 8739476111495272543L;\n\n    private final long jobDescriptorId;\n\n    private final String jobName;\n\n    private String description;\n\n    private Date lastUpdateDate;\n\n    private String lastMessage;\n    private int numberOfFailures;\n\n    public FailedJobImpl(final long jobDescriptorId, final String jobName) {\n        this.jobDescriptorId = jobDescriptorId;\n        this.jobName = jobName;\n    }\n\n    public void setLastUpdateDate(final Date lastUpdateDate) {\n        this.lastUpdateDate = lastUpdateDate;\n\n    }\n\n    public void setDescription(final String description) {\n        this.description = description;\n\n    }\n\n    public void setLastMessage(final String lastMessage) {\n        this.lastMessage = lastMessage;\n\n    }\n\n    public void setNumberOfFailures(int numberOfFailures) {\n        this.numberOfFailures = numberOfFailures;\n    }\n\n    @Override\n    public long getJobDescriptorId() {\n        return jobDescriptorId;\n    }\n\n    @Override\n    public String getJobName() {\n        return jobName;\n    }\n\n    @Override\n    public String getDescription() {\n        return description;\n    }\n\n    @Override\n    public String getLastMessage() {\n        return lastMessage;\n    }\n\n    @Override\n    public int getNumberOfFailures() {\n        return numberOfFailures;\n    }\n\n    @Override\n    public Date getLastUpdateDate() {\n        return lastUpdateDate;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/job/package-info.java",
    "content": "/**\n * Copyright (C) 2015 BonitaSoft S.A.\n * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n/**\n * <p>\n * </p>\n */\npackage org.bonitasoft.engine.job;\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/maintenance/MaintenanceDetails.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.maintenance;\n\nimport org.bonitasoft.engine.bpm.BonitaObject;\n\n/**\n * This object holds maintenance details such as maintenance state and message.\n */\npublic interface MaintenanceDetails extends BonitaObject {\n\n    public State getMaintenanceState();\n\n    public String getMaintenanceMessage();\n\n    public Boolean isMaintenanceMessageActive();\n\n    /**\n     * Activation state of platform maintenance\n     */\n    public enum State {\n\n        ENABLED, DISABLED\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/maintenance/MaintenanceDetailsNotFoundException.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.maintenance;\n\nimport org.bonitasoft.engine.exception.NotFoundException;\n\npublic class MaintenanceDetailsNotFoundException extends NotFoundException {\n\n    public MaintenanceDetailsNotFoundException(String message) {\n        super(message);\n    }\n\n    public MaintenanceDetailsNotFoundException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/maintenance/impl/MaintenanceDetailsImpl.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.maintenance.impl;\n\nimport lombok.Builder;\nimport org.bonitasoft.engine.maintenance.MaintenanceDetails;\n\n@Builder\npublic class MaintenanceDetailsImpl implements MaintenanceDetails {\n\n    private State maintenanceState;\n    private String maintenanceMessage;\n    private boolean maintenanceMessageActive;\n\n    public State getMaintenanceState() {\n        return maintenanceState;\n    }\n\n    @Override\n    public String getMaintenanceMessage() {\n        return maintenanceMessage;\n    }\n\n    public Boolean isMaintenanceMessageActive() {\n        return maintenanceMessageActive;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/page/AuthorizationRuleConstants.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\n/**\n * author Emmanuel Duchastenier\n */\npublic class AuthorizationRuleConstants {\n\n    public static final String IS_ADMIN = \"IS_ADMIN\";\n    public static final String IS_PROCESS_OWNER = \"IS_PROCESS_OWNER\";\n    public static final String IS_PROCESS_INITIATOR = \"IS_PROCESS_INITIATOR\";\n    public static final String IS_INVOLVED_IN_PROCESS_INSTANCE = \"IS_INVOLVED_IN_PROCESS_INSTANCE\";\n    public static final String IS_MANAGER_OF_USER_INVOLVED_IN_PROCESS_INSTANCE = \"IS_MANAGER_OF_USER_INVOLVED_IN_PROCESS_INSTANCE\";\n    public static final String IS_TASK_AVAILABLE_FOR_USER = \"IS_TASK_AVAILABLE_FOR_USER\";\n    public static final String IS_ACTOR_INITIATOR = \"IS_ACTOR_INITIATOR\";\n    public static final String IS_TASK_PERFORMER = \"IS_TASK_PERFORMER\";\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/page/ContentType.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\n/**\n * @author Laurent Leseigneur\n */\npublic interface ContentType {\n\n    String PAGE = \"page\";\n\n    String FORM = \"form\";\n\n    String API_EXTENSION = \"apiExtension\";\n\n    String LAYOUT = \"layout\";\n\n    String THEME = \"theme\";\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/page/Page.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.bpm.BaseElement;\n\n/**\n * A Page is a way to store, amongst other things, a binary content.\n * It is used by Bonita Portal to display specific custom content\n *\n * @author Laurent Leseigneur\n */\npublic interface Page extends BaseElement {\n\n    /**\n     * Get the name of this <code>Page</code>.\n     *\n     * @return the logical name of this <code>Page</code>.\n     */\n    String getName();\n\n    /**\n     * Get the display name of this <code>Page</code>.\n     *\n     * @return the display name of this <code>Page</code>.\n     */\n    String getDisplayName();\n\n    /**\n     * Is this page provided by default.\n     *\n     * @return true if this page is provided by default, false otherwise.\n     */\n    boolean isProvided();\n\n    /**\n     * Get the description of this <code>Page</code>.\n     *\n     * @return the description of this <code>Page</code>.\n     */\n    String getDescription();\n\n    /**\n     * Get the date when this page was initially installed into the Engine.\n     *\n     * @return the date when this page was initially installed into the Engine.\n     */\n    Date getInstallationDate();\n\n    /**\n     * Get the ID of the user that installed the page.\n     *\n     * @return the ID of the user that installed the page, or -1 if it is a default page.\n     */\n    long getInstalledBy();\n\n    /**\n     * Get the date when this page was last modified.\n     *\n     * @return the date when this page was last modified.\n     */\n    Date getLastModificationDate();\n\n    /**\n     * Get the userId of the user that last updated this page.\n     *\n     * @return the user id of the user that last updated this page.\n     */\n    long getLastUpdatedBy();\n\n    /**\n     * Get the name of the zip file.\n     *\n     * @return the name of the zip file of this <code>Page</code>.\n     */\n    String getContentName();\n\n    /**\n     * Get the type of this <code>Page</code>.\n     *\n     * @return the type of this <code>Page</code>. see {@link ContentType} for available values\n     * @since 7.0\n     */\n    String getContentType();\n\n    /**\n     * Get the process definition ID of this <code>Page</code>.\n     *\n     * @return the process definition ID of this <code>Page</code>, or null if not set.\n     * @since 7.0\n     */\n    Long getProcessDefinitionId();\n\n    /**\n     * Whether this page is editable\n     *\n     * @return Whether this page is editable\n     */\n    boolean isEditable();\n\n    /**\n     * Whether this page is removable\n     *\n     * @return Whether this page is removable\n     */\n    boolean isRemovable();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/page/PageCreator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class PageCreator implements Serializable {\n\n    public enum PageField {\n        NAME, DISPLAY_NAME, DESCRIPTION, CONTENT_NAME, CONTENT_TYPE, PROCESS_DEFINITION_ID\n    }\n\n    private static final long serialVersionUID = 8174091386958635983L;\n\n    private final Map<PageField, Serializable> fields;\n\n    public PageCreator(final String name, final String zipName) {\n        fields = new HashMap<>();\n        fields.put(PageField.NAME, name);\n        fields.put(PageField.CONTENT_NAME, zipName);\n        setContentType(ContentType.PAGE);\n    }\n\n    public PageCreator(String name, String zipName, String contentType, Long processDefinitionId) {\n        this(name, zipName);\n        setContentType(contentType);\n        setProcessDefinitionId(processDefinitionId);\n    }\n\n    public String getName() {\n        return fields.get(PageField.NAME).toString();\n    }\n\n    public PageCreator setDescription(final String description) {\n        fields.put(PageField.DESCRIPTION, description);\n        return this;\n    }\n\n    public PageCreator setDisplayName(final String displayName) {\n        fields.put(PageField.DISPLAY_NAME, displayName);\n        return this;\n    }\n\n    public PageCreator setContentType(final String contentType) {\n        fields.put(PageField.CONTENT_TYPE, contentType);\n        return this;\n    }\n\n    public PageCreator setProcessDefinitionId(final Long processDefinitionId) {\n        fields.put(PageField.PROCESS_DEFINITION_ID, processDefinitionId);\n        return this;\n    }\n\n    public Map<PageField, Serializable> getFields() {\n        return fields;\n    }\n\n    @Override\n    public String toString() {\n        return \"PageCreator [fields=\" + fields + \"]\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/page/PageNotFoundException.java",
    "content": "/**\n * Copyright (C) 2015 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport org.bonitasoft.engine.exception.NotFoundException;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class PageNotFoundException extends NotFoundException {\n\n    private static final long serialVersionUID = 2842457668242337487L;\n\n    public enum PageAttribute {\n        NAME, MAPPING_KEY\n    }\n\n    /**\n     * @deprecated Use {@link #PageNotFoundException(String, PageAttribute)} constructor instead.\n     */\n    @Deprecated\n    public PageNotFoundException(final String name) {\n        this(name, PageAttribute.NAME);\n    }\n\n    public PageNotFoundException(final String value, PageAttribute pageAttribute) {\n        super(String.format(\"Unable to find page with %s: %s\", pageAttribute.name().toLowerCase(), value));\n    }\n\n    public PageNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/page/PageSearchDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class PageSearchDescriptor {\n\n    public static final String ID = \"id\";\n\n    public static final String NAME = \"name\";\n\n    public static final String DISPLAY_NAME = \"displayName\";\n\n    public static final String INSTALLED_BY = \"installedBy\";\n\n    public static final String PROVIDED = \"provided\";\n\n    public static final String INSTALLATION_DATE = \"installationDate\";\n\n    public static final String LAST_MODIFICATION_DATE = \"lastModificationDate\";\n\n    public static final String CONTENT_TYPE = \"contentType\";\n\n    public static final String PROCESS_DEFINITION_ID = \"processDefinitionId\";\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/page/PageURL.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport java.io.Serializable;\nimport java.util.Objects;\n\n/**\n * author Emmanuel Duchastenier\n */\npublic class PageURL implements Serializable {\n\n    private String url;\n\n    private Long pageId;\n\n    public String getUrl() {\n        return url;\n    }\n\n    public Long getPageId() {\n        return pageId;\n    }\n\n    public PageURL(String url, Long pageId) {\n        this.url = url;\n        this.pageId = pageId;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (o == null || getClass() != o.getClass())\n            return false;\n        PageURL pageURL = (PageURL) o;\n        return Objects.equals(url, pageURL.url) &&\n                Objects.equals(pageId, pageURL.pageId);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(url, pageId);\n    }\n\n    @Override\n    public String toString() {\n        return \"PageURL{\" +\n                \"url='\" + url + '\\'' +\n                \", pageId=\" + pageId +\n                '}';\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/page/PageUpdater.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class PageUpdater implements Serializable {\n\n    private static final long serialVersionUID = 4295108162470507415L;\n\n    public enum PageUpdateField {\n\n        /**\n         * @deprecated since bonita 7.13 the update of name is no more supported, it will not be taken into account on\n         *             the page update\n         **/\n        @Deprecated\n        NAME, DISPLAY_NAME, DESCRIPTION, CONTENT_NAME, CONTENT_TYPE, PROCESS_DEFINITION_ID\n    }\n\n    private final Map<PageUpdateField, Serializable> fields;\n\n    public PageUpdater() {\n        fields = new HashMap<>();\n    }\n\n    public PageUpdater setDescription(final String description) {\n        fields.put(PageUpdateField.DESCRIPTION, description);\n        return this;\n    }\n\n    /**\n     * @deprecated since bonita 7.13 the update of name is no more supported, it will not be taken into account on the\n     *             page update\n     */\n    @Deprecated\n    public PageUpdater setName(final String name) {\n        fields.put(PageUpdateField.NAME, name);\n        return this;\n    }\n\n    public PageUpdater setDisplayName(final String displayName) {\n        fields.put(PageUpdateField.DISPLAY_NAME, displayName);\n        return this;\n    }\n\n    public PageUpdater setContentName(final String contentName) {\n        fields.put(PageUpdateField.CONTENT_NAME, contentName);\n        return this;\n    }\n\n    public PageUpdater setContentType(final String contentType) {\n        fields.put(PageUpdateField.CONTENT_TYPE, contentType);\n        return this;\n    }\n\n    public PageUpdater setProcessDefinitionId(final Long processDefinitionId) {\n        fields.put(PageUpdateField.PROCESS_DEFINITION_ID, processDefinitionId);\n        return this;\n    }\n\n    public Map<PageUpdateField, Serializable> getFields() {\n        return fields;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/page/URLAdapterConstants.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface URLAdapterConstants {\n\n    String LEGACY_URL_ADAPTER = \"legacy\";\n    String EXTERNAL_URL_ADAPTER = \"external\";\n    String CONTEXT_PATH = \"contextPath\";\n    //    String IS_ADMIN = \"isAmin\";\n    String LOCALE = \"locale\";\n    String QUERY_PARAMETERS = \"queryParameters\";\n    String ID_QUERY_PARAM = \"id\";\n    String ASSIGN_TASK_QUERY_PARAM = \"assignTask\";\n    String USER_QUERY_PARAM = \"user\";\n    String TENANT_QUERY_PARAM = \"tenant\";\n    String MODE_QUERY_PARAM = \"mode\";\n    String AUTO_INSTANTIATE_QUERY_PARAM = \"autoInstantiate\";\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/page/impl/PageImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page.impl;\n\nimport java.util.Date;\n\nimport lombok.ToString;\nimport org.bonitasoft.engine.page.Page;\n\n/**\n * @author Laurent Leseigneur\n */\n@ToString\npublic class PageImpl implements Page {\n\n    private static final long serialVersionUID = 5785414687043871169L;\n\n    private final long pageId;\n\n    private final String name;\n\n    private final boolean provided;\n    private final String description;\n    private final Date installationDate;\n    private final long installedBy;\n    private final Date lastModificationDate;\n    private final String displayName;\n    private final long lastUpdatedBy;\n    private final String contentName;\n    private final String contentType;\n    private final Long processDefinitionId;\n    private final boolean editable;\n    private final boolean removable;\n\n    /**\n     * Builds a Page that is editable and removable by default\n     */\n    public PageImpl(final long pageId, final String name, final String displayName, final boolean provided,\n            final String description, final long installationDate, final long installedBy,\n            final long lastModificationDate, final long lastUpdatedBy, final String zipName, String contentType,\n            Long processDefinitionId) {\n        this(pageId, name, displayName, provided, true, true, description, installationDate, installedBy,\n                lastModificationDate, lastUpdatedBy, zipName, contentType, processDefinitionId);\n    }\n\n    public PageImpl(final long pageId, final String name, final String displayName, final boolean provided,\n            boolean editable, boolean removable, final String description,\n            final long installationDate,\n            final long installedBy, final long lastModificationDate, final long lastUpdatedBy, final String zipName,\n            String contentType,\n            Long processDefinitionId) {\n        this.pageId = pageId;\n        this.name = name;\n        this.displayName = displayName;\n        this.provided = provided;\n        this.description = description;\n        this.lastUpdatedBy = lastUpdatedBy;\n        this.contentName = zipName;\n        this.contentType = contentType;\n        this.processDefinitionId = processDefinitionId;\n        this.installationDate = new Date(installationDate);\n        this.installedBy = installedBy;\n        this.lastModificationDate = new Date(lastModificationDate);\n        this.editable = editable;\n        this.removable = removable;\n    }\n\n    @Override\n    public long getId() {\n        return pageId;\n    }\n\n    @Override\n    public String getName() {\n        return name;\n    }\n\n    @Override\n    public boolean isProvided() {\n        return provided;\n    }\n\n    @Override\n    public String getDescription() {\n        return description;\n    }\n\n    @Override\n    public Date getInstallationDate() {\n        return installationDate;\n    }\n\n    @Override\n    public long getInstalledBy() {\n        return installedBy;\n    }\n\n    @Override\n    public Date getLastModificationDate() {\n        return lastModificationDate;\n    }\n\n    @Override\n    public String getDisplayName() {\n        return displayName;\n    }\n\n    public long getPageId() {\n        return pageId;\n    }\n\n    @Override\n    public long getLastUpdatedBy() {\n        return lastUpdatedBy;\n    }\n\n    @Override\n    public String getContentName() {\n        return contentName;\n    }\n\n    @Override\n    public String getContentType() {\n        return contentType;\n    }\n\n    @Override\n    public Long getProcessDefinitionId() {\n        return processDefinitionId;\n    }\n\n    @Override\n    public boolean isEditable() {\n        return editable;\n    }\n\n    @Override\n    public boolean isRemovable() {\n        return removable;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/platform/IllegalNodeStateException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform;\n\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\n\n/**\n * Indicates that an operation incompatible with the current node state was called.\n *\n * @author Emmanuel Duchastenier\n */\npublic class IllegalNodeStateException extends BonitaRuntimeException {\n\n    private static final long serialVersionUID = 7043887433404383538L;\n\n    /**\n     * @param message a String indicating the exception message\n     */\n    public IllegalNodeStateException(final String message) {\n        super(message);\n    }\n\n    /**\n     * @param message a String indicating the exception message\n     * @param cause a Throwable indicating the root cause\n     */\n    public IllegalNodeStateException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    /**\n     * @param cause a Throwable indicating the root cause\n     */\n    public IllegalNodeStateException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/platform/InvalidPlatformCredentialsException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform;\n\n/**\n * @author Baptiste Mesta\n */\npublic class InvalidPlatformCredentialsException extends PlatformLoginException {\n\n    public InvalidPlatformCredentialsException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/platform/LoginException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform;\n\nimport org.bonitasoft.engine.exception.BonitaException;\n\n/**\n * Indicates that a problem occurred during tenant login action\n *\n * @author Matthieu Chaffotte\n */\npublic class LoginException extends BonitaException {\n\n    private static final long serialVersionUID = 2644120305805282693L;\n\n    /**\n     * @param message a String indicating the exception message\n     */\n    public LoginException(final String message) {\n        super(message);\n    }\n\n    /**\n     * @param cause a Throwable indicating the root cause\n     */\n    public LoginException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/platform/LogoutException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform;\n\nimport org.bonitasoft.engine.exception.BonitaException;\n\n/**\n * Indicates that a problem occurred during tenant logout action\n *\n * @author Matthieu Chaffotte\n */\npublic class LogoutException extends BonitaException {\n\n    private static final long serialVersionUID = -5327606222750615923L;\n\n    /**\n     * @param message a String indicating the exception message\n     */\n    public LogoutException(final String message) {\n        super(message);\n    }\n\n    /**\n     * @param cause a Throwable indicating the root cause\n     */\n    public LogoutException(final Throwable cause) {\n        super(cause);\n    }\n\n    /**\n     * @param message a String indicating the exception message\n     * @param cause a Throwable indicating the root cause\n     */\n    public LogoutException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/platform/NodeNotStartedException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform;\n\n/**\n * Indicates that an operation needing a started node was called in a stopped node.\n *\n * @author Emmanuel Duchastenier\n */\npublic class NodeNotStartedException extends IllegalNodeStateException {\n\n    private static final long serialVersionUID = -1L;\n\n    public NodeNotStartedException() {\n        super(\"The current node has not been started yet. Method PlatformAPI.startNode() must be called previously.\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/platform/Platform.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform;\n\nimport java.io.Serializable;\n\n/**\n * Contains information about the <code>Bonita Platform</code>.\n * <p>\n * The <code>platform</code> is the base on which runs the <code>Bonita Engine</code>. There is only one platform for a\n * running <code>Bonita\n * Engine</code>.\n * <p>\n * In order to perform actions on the platform, please, refer to the {@link org.bonitasoft.engine.api.PlatformAPI}\n *\n * @author Matthieu Chaffotte\n * @see org.bonitasoft.engine.api.PlatformAPI\n * @since 6.0.0\n */\npublic interface Platform extends Serializable {\n\n    /**\n     * Retrieves the <code>platform</code> version\n     *\n     * @return a String representing the <code>platform</code> version\n     * @see #getInitialVersion()\n     */\n    String getVersion();\n\n    /**\n     * Retrieves the <code>platform</code> initial version. That is, the Bonita version in which you have initially\n     * created the platform before migrating\n     * to the current version.\n     * <p>\n     * For instance, if you have created your platform in the version 6.1.0 and have migrated to the version 6.3.4 using\n     * the <code>Bonita Migration\n     * Tool</code>, {@code getInitialVersion} will return 6.1.0 and {@link #getVersion()} will return 6.3.4.\n     *\n     * @return a String representing the <code>platform</code> initial version\n     * @see #getVersion()\n     */\n    String getInitialVersion();\n\n    /**\n     * Retrieves the timestamp at which the platform was created\n     *\n     * @return a long representing the timestamp at which the platform was created\n     */\n    long getCreated();\n\n    /**\n     * Retrieves the name of the platform technical user that created the platform\n     *\n     * @return a String representing the name of the platform technical user that created the platform\n     */\n    String getCreatedBy();\n\n    /**\n     * Return whether this platform is in maintenance or not.\n     * When the platform is in maintenance, the services are paused, and most features are disabled.\n     * Only the Technical Admin can log in during the maintenance time.\n     */\n    boolean isMaintenanceEnabled();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/platform/PlatformLoginException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform;\n\nimport org.bonitasoft.engine.exception.BonitaException;\n\n/**\n * Indicates that a problem occurred during platform login action\n *\n * @author Matthieu Chaffotte\n */\npublic class PlatformLoginException extends BonitaException {\n\n    private static final long serialVersionUID = 3075478879044920526L;\n\n    /**\n     * @param message a String indicating the exception message\n     */\n    public PlatformLoginException(final String message) {\n        super(message);\n    }\n\n    /**\n     * @param cause a Throwable indicating the root cause\n     */\n    public PlatformLoginException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/platform/PlatformLogoutException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform;\n\nimport org.bonitasoft.engine.exception.BonitaException;\n\n/**\n * Indicates that a problem occurred during tenant logout action\n *\n * @author Matthieu Chaffotte\n */\npublic class PlatformLogoutException extends BonitaException {\n\n    private static final long serialVersionUID = 8429124800360665988L;\n\n    /**\n     * @param message a String indicating the exception message\n     */\n    public PlatformLogoutException(final String message) {\n        super(message);\n    }\n\n    /**\n     * @param cause a Throwable indicating the root cause\n     */\n    public PlatformLogoutException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/platform/PlatformNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform;\n\nimport org.bonitasoft.engine.exception.NotFoundException;\n\n/**\n * Indicates that the platform does not exist\n *\n * @author Lu Kai\n * @author Emmanuel Duchastenier\n */\npublic class PlatformNotFoundException extends NotFoundException {\n\n    private static final long serialVersionUID = -7397972100656765511L;\n\n    /**\n     * @param message a String indicating the exception message\n     */\n    public PlatformNotFoundException(final String message) {\n        super(message);\n    }\n\n    /**\n     * @param message a String indicating the exception message\n     * @param cause a Throwable indicating the root cause\n     */\n    public PlatformNotFoundException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    /**\n     * @param cause a Throwable indicating the root cause\n     */\n    public PlatformNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/platform/PlatformState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform;\n\n/**\n * Describes the possible states of the Platform.\n */\npublic enum PlatformState {\n\n    /**\n     * The Platform is started:\n     * - Services are running\n     * - Tenants that are Activated are STARTED or are STARTING\n     * - Calls can be made on platform APIs\n     */\n    STARTED,\n\n    /**\n     * The Platform is transitioning from state STOPPED to state STARTED\n     * - Tenants are still STOPPED ( will be started once the Platform is STARTED)\n     * - No calls can be made on APIs\n     */\n    STARTING,\n\n    /**\n     * The Platform is stopped:\n     * - Services are stopped\n     * - All Tenants are also STOPPED\n     * - No call to APIs can be made (except to login and start platform)\n     */\n    STOPPED,\n\n    /**\n     * The Platform is transitioning from state STARTED to state STOPPED\n     * - Tenants are STOPPED or STOPPING\n     * - No calls can be made on APIs\n     */\n    STOPPING,\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/platform/StartNodeException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform;\n\nimport org.bonitasoft.engine.exception.ExecutionException;\n\n/**\n * Indicates that a problem occurred when starting the node\n *\n * @author Matthieu Chaffotte\n */\npublic class StartNodeException extends ExecutionException {\n\n    private static final long serialVersionUID = 2714223184438785735L;\n\n    /**\n     * @param message a String indicating the exception message\n     */\n    public StartNodeException(final String message) {\n        super(message);\n    }\n\n    /**\n     * @param cause a Throwable indicating the root cause\n     */\n    public StartNodeException(final Throwable cause) {\n        super(cause);\n    }\n\n    /**\n     * @param message a String indicating the exception messages\n     * @param cause a Throwable indicating the root cause\n     */\n    public StartNodeException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/platform/StopNodeException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform;\n\nimport org.bonitasoft.engine.exception.ExecutionException;\n\n/**\n * Indicates that a problem occurred when stopping the node\n *\n * @author Matthieu Chaffotte\n */\npublic class StopNodeException extends ExecutionException {\n\n    private static final long serialVersionUID = -8344736311926111229L;\n\n    /**\n     * @param message a String indicating the exception message\n     */\n    public StopNodeException(final String message) {\n        super(message);\n    }\n\n    /**\n     * @param cause a Throwable indicating the root cause\n     */\n    public StopNodeException(final Throwable cause) {\n        super(cause);\n    }\n\n    /**\n     * @param message a String indicating the exception message\n     * @param cause a Throwable indicating the root cause\n     */\n    public StopNodeException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/platform/UnknownUserException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform;\n\n/**\n * Indicates that the user trying to login is unknown to bonita\n *\n * @author Anthony Birembaut\n */\npublic class UnknownUserException extends LoginException {\n\n    private static final long serialVersionUID = 9074655520113937276L;\n\n    /**\n     * @param message a String indicating the exception message\n     */\n    public UnknownUserException(final String message) {\n        super(message);\n    }\n\n    /**\n     * @param cause a Throwable indicating the root cause\n     */\n    public UnknownUserException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/platform/command/package-info.java",
    "content": "/**\n * Copyright (C) 2015 BonitaSoft S.A.\n * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n/**\n * <p>\n * Contains classes related to <code>Platform commands</code>.\n * </p>\n *\n * @since 6.0.0\n */\npackage org.bonitasoft.engine.platform.command;\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/platform/impl/PlatformImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.impl;\n\nimport java.io.Serial;\n\nimport lombok.Getter;\nimport lombok.ToString;\nimport org.bonitasoft.engine.platform.Platform;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@ToString\n@Getter\npublic class PlatformImpl implements Platform {\n\n    @Serial\n    private static final long serialVersionUID = -8493649294374229877L;\n\n    private final long created;\n\n    private final String createdBy;\n\n    private final String initialVersion;\n\n    private final String version;\n\n    private final boolean maintenanceEnabled;\n\n    public PlatformImpl(final String version, final String initialVersion, final String createdBy, final long created,\n            final boolean maintenanceEnabled) {\n        this.version = version;\n        this.initialVersion = initialVersion;\n        this.createdBy = createdBy;\n        this.created = created;\n        this.maintenanceEnabled = maintenanceEnabled;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/platform/package-info.java",
    "content": "/**\n * Copyright (C) 2015 BonitaSoft S.A.\n * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n/**\n * <p>\n * Contains classes and interfaces related to the <code>Bonita Platform</code>.\n * </p>\n *\n * @since 6.0.0\n */\npackage org.bonitasoft.engine.platform;\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/profile/ProfileCriterion.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile;\n\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.Sort;\n\n/**\n * @author Celine Souchet\n */\npublic enum ProfileCriterion {\n\n    /**\n     * Profile identifier ascending order\n     */\n    ID_ASC(ProfileSearchDescriptor.ID, Order.ASC),\n\n    /**\n     * Profile identifier descending order\n     */\n    ID_DESC(ProfileSearchDescriptor.ID, Order.DESC),\n\n    /**\n     * Profile is default ascending order\n     */\n    IS_DEFAULT_ASC(ProfileSearchDescriptor.IS_DEFAULT, Order.ASC),\n\n    /**\n     * Profile is default descending order\n     */\n    IS_DEFAULT_DESC(ProfileSearchDescriptor.IS_DEFAULT, Order.DESC),\n\n    /**\n     * Profile name ascending order\n     */\n    NAME_ASC(ProfileSearchDescriptor.NAME, Order.ASC),\n\n    /**\n     * Profile name descending order\n     */\n    NAME_DESC(ProfileSearchDescriptor.NAME, Order.DESC);\n\n    private final String field;\n\n    private final Order order;\n\n    ProfileCriterion(final String field, final Order order) {\n        this.field = field;\n        this.order = order;\n    }\n\n    public Order getOrder() {\n        return order;\n    }\n\n    public String getField() {\n        return field;\n    }\n\n    public Sort getSort() {\n        return new Sort(order, field);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/profile/ProfileMemberCreator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author Celine Souchet\n */\npublic class ProfileMemberCreator implements Serializable {\n\n    private static final long serialVersionUID = -1414989152963184543L;\n\n    public enum ProfileMemberField {\n        PROFILE_ID, USER_ID, GROUP_ID, ROLE_ID;\n    }\n\n    private final Map<ProfileMemberField, Serializable> fields;\n\n    public ProfileMemberCreator(final long profileId) {\n        fields = new HashMap<ProfileMemberField, Serializable>(5);\n        fields.put(ProfileMemberField.PROFILE_ID, profileId);\n    }\n\n    public ProfileMemberCreator setUserId(final long userId) {\n        fields.put(ProfileMemberField.USER_ID, userId);\n        return this;\n    }\n\n    public ProfileMemberCreator setGroupId(final long groupId) {\n        fields.put(ProfileMemberField.GROUP_ID, groupId);\n        return this;\n    }\n\n    public ProfileMemberCreator setRoleId(final long roleId) {\n        fields.put(ProfileMemberField.ROLE_ID, roleId);\n        return this;\n    }\n\n    public Map<ProfileMemberField, Serializable> getFields() {\n        return fields;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/profile/ProfileMemberNotFoundException.java",
    "content": "/**\n * Copyright (C) 2021 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile;\n\nimport org.bonitasoft.engine.exception.NotFoundException;\n\n/**\n * @author Anthony Birembaut\n */\npublic class ProfileMemberNotFoundException extends NotFoundException {\n\n    private static final long serialVersionUID = -7240871235816448511L;\n\n    public ProfileMemberNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/profile/ProfileMemberSearchDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile;\n\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.search.SearchOptions;\n\n/**\n * Defines the fields that can be used in the {@link org.bonitasoft.engine.search.SearchOptions} when searching for\n * {@link ProfileMember}s\n *\n * @author Julien Mege\n * @author Celine Souchet\n * @see SearchOptions\n * @see org.bonitasoft.engine.api.ProfileAPI#searchProfileMembers(String, SearchOptions)\n */\npublic final class ProfileMemberSearchDescriptor {\n\n    /**\n     * Used to filter or order by the {@link ProfileMember} identifier\n     *\n     * @see ProfileMember\n     */\n    public static final String ID = \"id\";\n\n    /**\n     * Used to filter or order by the identifier of the related {@link Profile}\n     *\n     * @see Profile\n     * @see ProfileMember\n     */\n    public static final String PROFILE_ID = \"profileId\";\n\n    /**\n     * Used to filter or order by the first part of the display name of {@link ProfileMember}\n     *\n     * @see ProfileMember#getDisplayNamePart1()\n     */\n    public static final String DISPLAY_NAME_PART1 = \"displayNamePart1\";\n\n    /**\n     * Used to filter or order by the second part of the display name of {@link ProfileMember}\n     *\n     * @see ProfileMember#getDisplayNamePart2()\n     */\n    public static final String DISPLAY_NAME_PART2 = \"displayNamePart2\";\n\n    /**\n     * Used to filter or order by the third part of the display name of {@link ProfileMember}\n     *\n     * @see ProfileMember#getDisplayNamePart3()\n     */\n    public static final String DISPLAY_NAME_PART3 = \"displayNamePart3\";\n\n    /**\n     * Used to filter or order by the identifier of the {@link User} related to the {@link ProfileMember}\n     *\n     * @see User\n     * @see ProfileMember\n     */\n    public static final String USER_ID = \"userId\";\n\n    /**\n     * Used to filter or order by the identifier of the {@link org.bonitasoft.engine.identity.Group} related to the\n     * {@link ProfileMember}\n     *\n     * @see org.bonitasoft.engine.identity.Group\n     * @see ProfileMember\n     */\n    public static final String GROUP_ID = \"groupId\";\n\n    /**\n     * Used to filter or order by the identifier of the {@link org.bonitasoft.engine.identity.Role} related to the\n     * {@link ProfileMember}\n     *\n     * @see org.bonitasoft.engine.identity.Role\n     * @see ProfileMember\n     */\n    public static final String ROLE_ID = \"roleId\";\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/profile/ProfileNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile;\n\nimport org.bonitasoft.engine.exception.NotFoundException;\n\n/**\n * @author Celine Souchet\n * @author Matthieu Chaffotte\n */\npublic class ProfileNotFoundException extends NotFoundException {\n\n    private static final long serialVersionUID = -7220871135816448510L;\n\n    public ProfileNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/profile/ProfileSearchDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile;\n\nimport org.bonitasoft.engine.search.SearchOptions;\n\n/**\n * Defines the fields that can be used in the {@link org.bonitasoft.engine.search.SearchOptions} when searching for\n * {@link Profile}s\n *\n * @author Julien Mege\n * @author Celine Souchet\n * @see SearchOptions\n * @see Profile\n * @see org.bonitasoft.engine.api.ProfileAPI#searchProfiles(SearchOptions)\n */\npublic final class ProfileSearchDescriptor {\n\n    /**\n     * Used to filter or order by the {@link Profile} identifier\n     */\n    public static final String ID = \"id\";\n\n    /**\n     * Used to filter or order by the {@link Profile} name\n     */\n    public static final String NAME = \"name\";\n\n    /**\n     * Used to filter or order by the flag {@link Profile#isDefault()}\n     */\n    public static final String IS_DEFAULT = \"isDefault\";\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/search/Order.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic enum Order {\n    DESC, ASC, ASC_NULLS_LAST, DESC_NULLS_FIRST, ASC_NULLS_FIRST, DESC_NULLS_LAST\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/search/SearchFilterOperation.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic enum SearchFilterOperation {\n    EQUALS, GREATER_THAN, LESS_THAN, GREATER_OR_EQUAL, LESS_OR_EQUAL, DIFFERENT, BETWEEN, OR, AND, L_PARENTHESIS, R_PARENTHESIS\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/search/SearchOptions.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport java.io.Serializable;\nimport java.util.List;\n\nimport org.bonitasoft.engine.search.impl.SearchFilter;\n\n/**\n * A <code>SearchOptions</code> object helps define the generic options of the search mechanism.\n * A <code>SearchOptions</code> has a 'start index' field and a 'max results' field that define where to start and where\n * to stop to return results that match\n * the provided search criteria.\n * It is composed of a list of <code>SearchFilter</code> objects defining the restrictive criteria that a result must\n * match to fulfill the search.\n * It is also composed of a 'search term', which is a free text that can be search for in a certain amount of fields,\n * depending on what object is the search\n * applied on.\n * Finally, a search can define a list of {@link Sort} options to define the order in which the matching results will be\n * returned.\n * Use {@link SearchOptionsBuilder} to build a SearchOptions object.\n *\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @author Emmanuel Duchastenier\n * @see SearchOptionsBuilder\n * @see SearchResult\n */\npublic interface SearchOptions extends Serializable {\n\n    /**\n     * Gets the list of <code>SearchFilter</code> objects defining the restrictive criteria that a result must match to\n     * fulfill the search.\n     *\n     * @return the list of <code>SearchFilter</code> objects\n     */\n    List<SearchFilter> getFilters();\n\n    /**\n     * Gets the search term (free text that can be search for in a certain amount of properties, depending on what\n     * object is the search applied on)\n     *\n     * @return the search term (as a String) that will be searched for.\n     */\n    String getSearchTerm();\n\n    /**\n     * The result start index, that is the first result that matches the search criteria that we want to retrieve.\n     *\n     * @return the defined start index\n     */\n    int getStartIndex();\n\n    /**\n     * The maximum results to return. The actual number can be smaller, if the end of the list has been reached.\n     *\n     * @return The maximum number of results\n     */\n    int getMaxResults();\n\n    /**\n     * Gets the list of sort criteria\n     *\n     * @return the list of <code>Sort</code> to order the results\n     */\n    List<Sort> getSorts();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/search/SearchOptionsBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport java.io.Serializable;\nimport java.util.List;\n\nimport org.bonitasoft.engine.search.impl.SearchFilter;\nimport org.bonitasoft.engine.search.impl.SearchOptionsImpl;\n\n/**\n * Builder for {@link SearchOptions} objects. It can be used to define paged searches returning {@link SearchResult}s.\n * When several filters are added, implicit {@code AND} operators are used if not specified.\n * See {@link SearchOptions} for deeper details on search mechanism options.\n *\n * @author Matthieu Chaffotte\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n * @see SearchOptions\n */\npublic class SearchOptionsBuilder {\n\n    private final SearchOptionsImpl options;\n\n    /**\n     * Builds a new {@link SearchOptions} with results limited to {@code startIndex} and {@code maxResults}. If you are\n     * interested only in the number of\n     * elements matching with the given criteria without knowing the elements details, it's possible to use 0 (zero) as\n     * {@code maxResults}:\n     * {@link SearchResult#getResult()} will send an empty list and {@link SearchResult#getCount()} will return the\n     * number of matching elements.\n     *\n     * @param startIndex the first result to return\n     * @param maxResults the maximum results to return. The actual number can be smaller, if the end of the list has\n     *        been reached.\n     *        If 0 (zero) or a negative number is provided, only the result count will be pertinent, and the result list\n     *        itself will be empty.\n     * @see SearchOptions\n     * @see SearchResult#getResult()\n     * @see SearchResult#getCount()\n     */\n    public SearchOptionsBuilder(final int startIndex, final int maxResults) {\n        options = new SearchOptionsImpl(startIndex, maxResults);\n    }\n\n    /**\n     * Creates a new <code>SearchOptionsBuilder</code> from another instance by\n     *\n     * @param searchOptions\n     */\n    public SearchOptionsBuilder(final SearchOptions searchOptions) {\n        options = new SearchOptionsImpl(searchOptions.getStartIndex(), searchOptions.getMaxResults());\n        options.setFilters(searchOptions.getFilters());\n        options.setSorts(searchOptions.getSorts());\n        options.setSearchTerm(searchOptions.getSearchTerm());\n    }\n\n    /**\n     * Filter the results to the specific value for the specific field (equality)\n     *\n     * @param field\n     *        The name of the field to filter on. Depending on the search parameter, specify the field by accessing the\n     *        relevant xxxSearchDescriptor classes.\n     *        For example, <code>HumanTaskInstanceSearchDescriptor.NAME</code> and\n     *        <code>HumanTaskInstanceSearchDescriptor.PROCESS_DEFINITION_ID</code>.\n     * @param value\n     *        the single value to filter on that field name\n     * @return this builder itself\n     * @since 6.0\n     */\n    public SearchOptionsBuilder filter(final String field, final Serializable value) {\n        options.addFilter(field, value);\n        return this;\n    }\n\n    /**\n     * Filters search results with a greaterThan comparison operation.\n     *\n     * @param field\n     *        the field name to compare to.\n     * @param value\n     *        the value to compare.\n     * @return this builder itself\n     * @see SearchOptionsBuilder#filter(String, java.io.Serializable) for field values\n     */\n    public SearchOptionsBuilder greaterThan(final String field, final Serializable value) {\n        options.addGreaterThanFilter(field, value);\n        return this;\n    }\n\n    /**\n     * @param field\n     * @param value\n     * @return this builder itself\n     * @see SearchOptionsBuilder#filter(String, java.io.Serializable) for field values\n     */\n    public SearchOptionsBuilder greaterOrEquals(final String field, final Serializable value) {\n        options.addGreaterOrEqualsFilter(field, value);\n        return this;\n    }\n\n    /**\n     * @param field the field that should be less than\n     * @param value\n     * @return this builder itself\n     * @see SearchOptionsBuilder#filter(String, java.io.Serializable) for field values\n     */\n    public SearchOptionsBuilder lessThan(final String field, final Serializable value) {\n        options.addLessThanFilter(field, value);\n        return this;\n    }\n\n    /**\n     * @param field the field that should be less or equals\n     * @param value the value\n     * @return this builder itself\n     * @see SearchOptionsBuilder#filter(String, java.io.Serializable) for field values\n     */\n    public SearchOptionsBuilder lessOrEquals(final String field, final Serializable value) {\n        options.addLessOrEqualsFilter(field, value);\n        return this;\n    }\n\n    /**\n     * @param field the field that should be between\n     * @param from from this value\n     * @param to to this value\n     * @return this builder itself\n     * @see SearchOptionsBuilder#filter(String, java.io.Serializable) for field values\n     */\n    public SearchOptionsBuilder between(final String field, final Serializable from, final Serializable to) {\n        options.addBetweenFilter(field, from, to);\n        return this;\n    }\n\n    /**\n     * @param field\n     * @param value\n     * @return this builder itself\n     * @see SearchOptionsBuilder#filter(String, java.io.Serializable) for field values\n     */\n    public SearchOptionsBuilder differentFrom(final String field, final Serializable value) {\n        options.addDifferentFromFilter(field, value);\n        return this;\n    }\n\n    /**\n     * @return this builder itself\n     */\n    public SearchOptionsBuilder or() {\n        if (options.getFilters().size() == 0) {\n            throw new IllegalArgumentException(\"OR operator cannot be the first filter in the list.\");\n        }\n        options.addOrFilter();\n        return this;\n    }\n\n    /**\n     * @return this builder itself\n     */\n    public SearchOptionsBuilder and() {\n        if (options.getFilters().size() == 0) {\n            throw new IllegalArgumentException(\"AND operator cannot be the first filter in the list.\");\n        }\n        options.addAndFilter();\n        return this;\n    }\n\n    public SearchOptionsBuilder leftParenthesis() {\n        options.addLeftParenthesis();\n        return this;\n    }\n\n    public SearchOptionsBuilder rightParenthesis() {\n        options.addRightParenthesis();\n        return this;\n    }\n\n    /**\n     * @param value the search term\n     * @return this builder itself\n     */\n    public SearchOptionsBuilder searchTerm(final String value) {\n        options.setSearchTerm(value);\n        return this;\n    }\n\n    /**\n     * Adds a sort order option to the list of sort options\n     *\n     * @param field\n     *        the field name to sort by\n     * @param order\n     *        the order of the sort (ASCENDING, DESCENDING)\n     * @return the current SearchOptionsBuilder\n     */\n    public SearchOptionsBuilder sort(final String field, final Order order) {\n        options.addSort(field, order);\n        return this;\n    }\n\n    /**\n     * @param filters the filters to set\n     * @return this builder itself\n     */\n    public SearchOptionsBuilder setFilters(final List<SearchFilter> filters) {\n        options.setFilters(filters);\n        return this;\n    }\n\n    /**\n     * @param sorts the sorts to set\n     * @return this builder itself\n     */\n    public SearchOptionsBuilder setSort(final List<Sort> sorts) {\n        options.setSorts(sorts);\n        return this;\n    }\n\n    /**\n     * @return the <code>SearchOptions</code> finally built using this builder.\n     */\n    public SearchOptions done() {\n        return options;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/search/SearchResult.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport java.io.Serializable;\nimport java.util.List;\n\n/**\n * Represents the result of a Search. For details on 'Search mechanism', see {@link SearchOptionsBuilder} and\n * {@link SearchOptions}.\n * A <code>SearchResult</code> is composed of a result list {@link #getResult()} that is the paginated list of results\n * matching the provided criteria, and a\n * result count {@link #getCount()} that is the total number of results matching the provided criteria.\n *\n * @param <T>\n *        the type of the objects being returned by the search.\n * @author Emmanuel Duchastenier\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n * @see SearchOptions\n * @see SearchOptionsBuilder\n */\npublic interface SearchResult<T extends Serializable> extends Serializable {\n\n    /**\n     * Get the total number of matching result in the data base. This number can be greater than the number of elements\n     * retrieved in the search depending on\n     * paging criterion.\n     *\n     * @return The total number of matching result in the data base.\n     * @since 6.0\n     */\n    long getCount();\n\n    /**\n     * Get the list of elements retrieved by the search.\n     *\n     * @return The list of elements retrieved by the search.\n     * @since 6.0\n     */\n    List<T> getResult();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/search/Sort.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport java.io.Serializable;\n\n/**\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n */\npublic class Sort implements Serializable {\n\n    private static final long serialVersionUID = -8803283884140398833L;\n\n    private final Order order;\n\n    private final String field;\n\n    public Sort(final Order order, final String field) {\n        super();\n        this.order = order;\n        this.field = field;\n    }\n\n    public Order getOrder() {\n        return order;\n    }\n\n    public String getField() {\n        return field;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + (field == null ? 0 : field.hashCode());\n        result = prime * result + (order == null ? 0 : order.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        final Sort other = (Sort) obj;\n        if (field == null) {\n            if (other.field != null) {\n                return false;\n            }\n        } else if (!field.equals(other.field)) {\n            return false;\n        }\n        if (order != other.order) {\n            return false;\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/search/impl/SearchFilter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.impl;\n\nimport java.io.Serializable;\n\nimport org.bonitasoft.engine.exception.IncorrectParameterException;\nimport org.bonitasoft.engine.search.SearchFilterOperation;\n\n/**\n * @author Emmanuel Duchastenier\n * @author Matthieu Chaffotte\n */\npublic class SearchFilter implements Serializable {\n\n    private static final long serialVersionUID = 2476946810762051485L;\n\n    private String field;\n\n    private SearchFilterOperation operation;\n\n    private Serializable value;\n\n    private Serializable from;\n\n    private Serializable to;\n\n    /**\n     * @param field\n     *        the field to filter on\n     * @param operation\n     *        the operation to filter on\n     * @param value\n     *        the value of the field to filter on\n     * @see SearchFilterOperation\n     */\n    public SearchFilter(final String field, final SearchFilterOperation operation, final Serializable value) {\n        this.field = field;\n        this.operation = operation;\n        this.value = value;\n    }\n\n    public SearchFilter(final String field, final Serializable from, final Serializable to) {\n        this.field = field;\n        operation = SearchFilterOperation.BETWEEN;\n        this.from = from;\n        this.to = to;\n    }\n\n    public SearchFilter(final SearchFilterOperation operation) throws IncorrectParameterException {\n        this.operation = operation;\n        if (!isUndefinedFieldNameAuthorized()) {\n            throw new IncorrectParameterException(\n                    \"search operator can only be AND, OR, L_PARENTHESIS, R_PARENTHESIS on the one-parameter SearchFilter constructor\");\n        }\n    }\n\n    public boolean isUndefinedFieldNameAuthorized() {\n        switch (operation) {\n            case AND:\n            case OR:\n            case L_PARENTHESIS:\n            case R_PARENTHESIS:\n                return true;\n\n            default:\n                return false;\n        }\n    }\n\n    /**\n     * @return the field name\n     */\n    public String getField() {\n        return field;\n    }\n\n    /**\n     * @param field\n     *        the field name to set\n     */\n    public void setField(final String field) {\n        this.field = field;\n    }\n\n    /**\n     * @return the operation\n     */\n    public SearchFilterOperation getOperation() {\n        return operation;\n    }\n\n    /**\n     * @param operation\n     *        the operation to set\n     */\n    public void setOperation(final SearchFilterOperation operation) {\n        this.operation = operation;\n    }\n\n    /**\n     * @return the value\n     */\n    public Serializable getValue() {\n        return value;\n    }\n\n    /**\n     * @param value\n     *        the value to set\n     */\n    public void setValue(final Serializable value) {\n        this.value = value;\n    }\n\n    /**\n     * @return the from\n     */\n    public Serializable getFrom() {\n        return from;\n    }\n\n    /**\n     * @return the to\n     */\n    public Serializable getTo() {\n        return to;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + (field == null ? 0 : field.hashCode());\n        result = prime * result + (from == null ? 0 : from.hashCode());\n        result = prime * result + (operation == null ? 0 : operation.hashCode());\n        result = prime * result + (to == null ? 0 : to.hashCode());\n        result = prime * result + (value == null ? 0 : value.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        final SearchFilter other = (SearchFilter) obj;\n        if (field == null) {\n            if (other.field != null) {\n                return false;\n            }\n        } else if (!field.equals(other.field)) {\n            return false;\n        }\n        if (from == null) {\n            if (other.from != null) {\n                return false;\n            }\n        } else if (!from.equals(other.from)) {\n            return false;\n        }\n        if (operation != other.operation) {\n            return false;\n        }\n        if (to == null) {\n            if (other.to != null) {\n                return false;\n            }\n        } else if (!to.equals(other.to)) {\n            return false;\n        }\n        if (value == null) {\n            if (other.value != null) {\n                return false;\n            }\n        } else if (!value.equals(other.value)) {\n            return false;\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/search/impl/SearchOptionsImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.impl;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.exception.IncorrectParameterException;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchFilterOperation;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.Sort;\n\n/**\n * @author Matthieu Chaffotte\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n */\npublic class SearchOptionsImpl implements SearchOptions {\n\n    private static final long serialVersionUID = 1967932608495889373L;\n\n    private List<SearchFilter> filters;\n\n    private String searchTerm;\n\n    private final int startIndex;\n\n    private final int numberOfResults;\n\n    private List<Sort> sorts;\n\n    public SearchOptionsImpl(final int startIndex, final int numberOfResults) {\n        filters = new ArrayList<SearchFilter>(5);\n        sorts = new ArrayList<Sort>(2);\n        this.startIndex = startIndex;\n        this.numberOfResults = numberOfResults;\n    }\n\n    public void setFilters(final List<SearchFilter> filters) {\n        this.filters = filters;\n    }\n\n    @Override\n    public List<SearchFilter> getFilters() {\n        return filters;\n    }\n\n    @Override\n    public String getSearchTerm() {\n        return searchTerm;\n    }\n\n    @Override\n    public int getStartIndex() {\n        return startIndex;\n    }\n\n    @Override\n    public int getMaxResults() {\n        return numberOfResults;\n    }\n\n    @Override\n    public List<Sort> getSorts() {\n        return sorts;\n    }\n\n    public void setSearchTerm(final String value) {\n        searchTerm = value;\n    }\n\n    public void addGreaterThanFilter(final String field, final Serializable value) {\n        filters.add(new SearchFilter(field, SearchFilterOperation.GREATER_THAN, value));\n    }\n\n    public void addGreaterOrEqualsFilter(final String field, final Serializable value) {\n        filters.add(new SearchFilter(field, SearchFilterOperation.GREATER_OR_EQUAL, value));\n    }\n\n    public void addLessThanFilter(final String field, final Serializable value) {\n        filters.add(new SearchFilter(field, SearchFilterOperation.LESS_THAN, value));\n    }\n\n    public void addLessOrEqualsFilter(final String field, final Serializable value) {\n        filters.add(new SearchFilter(field, SearchFilterOperation.LESS_OR_EQUAL, value));\n    }\n\n    public void addBetweenFilter(final String field, final Serializable from, final Serializable to) {\n        filters.add(new SearchFilter(field, from, to));\n    }\n\n    public void addDifferentFromFilter(final String field, final Serializable value) {\n        filters.add(new SearchFilter(field, SearchFilterOperation.DIFFERENT, value));\n    }\n\n    public void addFilter(final String field, final Serializable value) {\n        filters.add(new SearchFilter(field, SearchFilterOperation.EQUALS, value));\n    }\n\n    public final void addOrFilter() {\n        try {\n            filters.add(new SearchFilter(SearchFilterOperation.OR));\n        } catch (final IncorrectParameterException e) {\n            // Cannot happen as we force a correct value\n        }\n    }\n\n    public final void addAndFilter() {\n        try {\n            filters.add(new SearchFilter(SearchFilterOperation.AND));\n        } catch (final IncorrectParameterException e) {\n            // Cannot happen as we force a correct value\n        }\n    }\n\n    public final void addLeftParenthesis() {\n        try {\n            filters.add(new SearchFilter(SearchFilterOperation.L_PARENTHESIS));\n        } catch (final IncorrectParameterException e) {\n            // Cannot happen as we force a correct value\n        }\n    }\n\n    public final void addRightParenthesis() {\n        try {\n            filters.add(new SearchFilter(SearchFilterOperation.R_PARENTHESIS));\n        } catch (final IncorrectParameterException e) {\n            // Cannot happen as we force a correct value\n        }\n    }\n\n    public void addSort(final String field, final Order order) {\n        sorts.add(new Sort(order, field));\n    }\n\n    public void setSorts(final List<Sort> sorts) {\n        this.sorts = sorts;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + (filters == null ? 0 : filters.hashCode());\n        result = prime * result + numberOfResults;\n        result = prime * result + (searchTerm == null ? 0 : searchTerm.hashCode());\n        result = prime * result + (sorts == null ? 0 : sorts.hashCode());\n        result = prime * result + startIndex;\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        final SearchOptionsImpl other = (SearchOptionsImpl) obj;\n        if (filters == null) {\n            if (other.filters != null) {\n                return false;\n            }\n        } else if (!filters.equals(other.filters)) {\n            return false;\n        }\n        if (numberOfResults != other.numberOfResults) {\n            return false;\n        }\n        if (searchTerm == null) {\n            if (other.searchTerm != null) {\n                return false;\n            }\n        } else if (!searchTerm.equals(other.searchTerm)) {\n            return false;\n        }\n        if (sorts == null) {\n            if (other.sorts != null) {\n                return false;\n            }\n        } else if (!sorts.equals(other.sorts)) {\n            return false;\n        }\n        if (startIndex != other.startIndex) {\n            return false;\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/search/impl/SearchResultImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.impl;\n\nimport java.io.Serializable;\nimport java.util.List;\n\nimport org.bonitasoft.engine.search.SearchResult;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class SearchResultImpl<T extends Serializable> implements SearchResult<T> {\n\n    private static final long serialVersionUID = -685595668360293014L;\n\n    private final long count;\n\n    private final List<T> list;\n\n    public SearchResultImpl(final long count, final List<T> list) {\n        super();\n        this.count = count;\n        this.list = list;\n    }\n\n    @Override\n    public long getCount() {\n        return count;\n    }\n\n    @Override\n    public List<T> getResult() {\n        return list;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + (int) (count ^ count >>> 32);\n        result = prime * result + (list == null ? 0 : list.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        final SearchResultImpl<?> other = (SearchResultImpl<?>) obj;\n        if (count != other.count) {\n            return false;\n        }\n        if (list == null) {\n            if (other.list != null) {\n                return false;\n            }\n        } else if (!list.equals(other.list)) {\n            return false;\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/search/impl/package-info.java",
    "content": "/**\n * Copyright (C) 2015 BonitaSoft S.A.\n * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n/**\n * <p>\n * </p>\n */\npackage org.bonitasoft.engine.search.impl;\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/search/package-info.java",
    "content": "/**\n * Copyright (C) 2015 BonitaSoft S.A.\n * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n/**\n * <p>\n * Contains classes to use when invoking search methods\n * </p>\n */\npackage org.bonitasoft.engine.search;\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/session/APISession.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.session;\n\nimport java.util.List;\n\n/**\n * Informations concerning the connected tenant to the APIs\n *\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic interface APISession extends Session {\n\n    /**\n     * @return The list of profiles of the user\n     */\n    List<String> getProfiles();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/session/InvalidSessionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.session;\n\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class InvalidSessionException extends BonitaRuntimeException {\n\n    private static final long serialVersionUID = 652973644884297283L;\n\n    public InvalidSessionException(final String message) {\n        super(message);\n    }\n\n    public InvalidSessionException(final Throwable e) {\n        super(e);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/session/PlatformSession.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.session;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface PlatformSession extends Session {\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/session/Session.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.session;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * Informations concerning the connected user\n *\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic interface Session extends Serializable {\n\n    /**\n     * @return The identifier of the session\n     */\n    long getId();\n\n    /**\n     * @return The creation date of the session\n     */\n    Date getCreationDate();\n\n    /**\n     * @return The duration of the session\n     */\n    long getDuration();\n\n    /**\n     * @return The user name associated to this session\n     */\n    String getUserName();\n\n    /**\n     * @return The identifier of the user associated to this session, if available (-1 if not)\n     */\n    long getUserId();\n\n    /**\n     * @return True if the logged user is the technical one, False otherwise.\n     */\n    boolean isTechnicalUser();\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/session/SessionNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.session;\n\nimport org.bonitasoft.engine.exception.BonitaException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SessionNotFoundException extends BonitaException {\n\n    private static final long serialVersionUID = -7611408247856380936L;\n\n    public SessionNotFoundException(final String message) {\n        super(message);\n    }\n\n    public SessionNotFoundException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SessionNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/session/impl/APISessionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.session.impl;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport org.bonitasoft.engine.session.APISession;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class APISessionImpl extends SessionImpl implements APISession {\n\n    private List<String> profiles;\n\n    public APISessionImpl(final long id, final Date creationDate, final long duration, final String userName,\n            final long userId) {\n        super(id, creationDate, duration, userName, userId);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/session/impl/PlatformSessionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.session.impl;\n\nimport java.util.Date;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport org.bonitasoft.engine.session.PlatformSession;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class PlatformSessionImpl extends SessionImpl implements PlatformSession {\n\n    public PlatformSessionImpl(final long id, final Date creationDate, final long duration, final String userName,\n            final long userId) {\n        super(id, creationDate, duration, userName, userId);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/session/impl/SessionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.session.impl;\n\nimport java.util.Date;\n\nimport lombok.Data;\nimport org.bonitasoft.engine.session.Session;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\n@Data\npublic abstract class SessionImpl implements Session {\n\n    private long id;\n    private Date creationDate;\n    private long duration;\n    private String userName;\n    private long userId;\n    private boolean technicalUser;\n\n    public SessionImpl(final long id, final Date creationDate, final long duration, final String userName,\n            final long userId) {\n        super();\n        this.id = id;\n        this.creationDate = creationDate;\n        this.duration = duration;\n        this.userName = userName;\n        this.userId = userId;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/session/package-info.java",
    "content": "/**\n * Copyright (C) 2015 BonitaSoft S.A.\n * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n/**\n * <p>\n * </p>\n */\npackage org.bonitasoft.engine.session;\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/tenant/TenantResource.java",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tenant;\n\nimport java.time.Instant;\nimport java.time.OffsetDateTime;\nimport java.time.ZoneOffset;\n\nimport lombok.Getter;\nimport org.bonitasoft.engine.bpm.BonitaObject;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@Getter\npublic class TenantResource implements BonitaObject {\n\n    public static final TenantResource NONE = new TenantResource(0, \"\", null, 0, 0, null);\n\n    private final long id;\n    private final String name;\n    private final TenantResourceType type;\n    private final OffsetDateTime lastUpdateDate;\n    private final long lastUpdatedBy;\n    private final TenantResourceState state;\n\n    public TenantResource(long id, String name, TenantResourceType type, long lastUpdateDate, long lastUpdatedBy,\n            TenantResourceState state) {\n        this.id = id;\n        this.name = name;\n        this.type = type;\n        this.lastUpdateDate = OffsetDateTime.ofInstant(Instant.ofEpochMilli(lastUpdateDate), ZoneOffset.UTC);\n        this.lastUpdatedBy = lastUpdatedBy;\n        this.state = state;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/tenant/TenantResourceState.java",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tenant;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic enum TenantResourceState {\n    INSTALLING, INSTALLED\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/tenant/TenantResourceType.java",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tenant;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic enum TenantResourceType {\n    BDM, BDM_ACCESS_CTRL\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/util/package-info.java",
    "content": "/**\n * Copyright (C) 2015 BonitaSoft S.A.\n * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n/**\n * <p>\n * </p>\n */\npackage org.bonitasoft.engine.util;\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/xml/DocumentManager.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.xml;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.StringWriter;\n\nimport javax.xml.XMLConstants;\nimport javax.xml.parsers.DocumentBuilder;\nimport javax.xml.parsers.DocumentBuilderFactory;\nimport javax.xml.parsers.ParserConfigurationException;\nimport javax.xml.transform.OutputKeys;\nimport javax.xml.transform.Transformer;\nimport javax.xml.transform.TransformerException;\nimport javax.xml.transform.TransformerFactory;\nimport javax.xml.transform.TransformerFactoryConfigurationError;\nimport javax.xml.transform.dom.DOMSource;\nimport javax.xml.transform.stream.StreamResult;\n\nimport org.w3c.dom.Document;\nimport org.xml.sax.SAXException;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class DocumentManager {\n\n    private DocumentManager() {\n        // For Sonar\n    }\n\n    public static Document generateDocument(final String s)\n            throws ParserConfigurationException, SAXException, IOException {\n        final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();\n        try {\n            documentBuilderFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, \"\"); // security-compliant\n            documentBuilderFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, \"\"); // security-compliant\n        } catch (IllegalArgumentException e) {\n            //ignored, if not supported by the implementation\n        }\n        final DocumentBuilder builder = documentBuilderFactory.newDocumentBuilder();\n        try (InputStream is = new ByteArrayInputStream(s.getBytes())) {\n            return builder.parse(is, null);\n        }\n    }\n\n    public static String getDocumentContent(final Document document)\n            throws TransformerFactoryConfigurationError, TransformerException, IOException {\n        final TransformerFactory transformerFactory = TransformerFactory.newInstance();\n        try {\n            transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, \"\"); // security-compliant\n            transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, \"\"); // security-compliant\n        } catch (IllegalArgumentException e) {\n            //ignored, if not supported by the implementation\n        }\n        final Transformer transformer = transformerFactory.newTransformer();\n        transformer.setOutputProperty(OutputKeys.INDENT, \"yes\");\n        transformer.setOutputProperty(OutputKeys.ENCODING, \"UTF-8\");\n        try (StringWriter writer = new StringWriter()) {\n            final StreamResult result = new StreamResult(writer);\n            final DOMSource source = new DOMSource(document);\n            transformer.transform(source, result);\n            return result.getWriter().toString();\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/java/org/bonitasoft/engine/xml/XStreamDenyList.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.xml;\n\nimport java.util.Arrays;\n\n/**\n * Configurable XStream deserialization deny list.\n * <p>\n * Provides the list of wildcard patterns used by {@code XStream.denyTypesByWildcard()} to block\n * known deserialization gadget chain libraries and prevent RCE.\n * <p>\n * The deny list can be overridden at runtime via the system property\n * {@value #DENY_PACKAGES_PROPERTY} (comma-separated wildcard patterns).\n * When set, it fully replaces the hardcoded defaults.\n * <p>\n * <b>Important:</b> The system property is read at first XStream usage. It should be set at\n * JVM startup (e.g. via {@code -D} flag) before any serialization/deserialization occurs.\n */\npublic final class XStreamDenyList {\n\n    public static final String DENY_PACKAGES_PROPERTY = \"bonita.xstream.deny.packages\";\n\n    private static final String[] DEFAULT_DENY_PATTERNS = {\n            \"org.apache.commons.collections.functors.**\",\n            \"org.apache.commons.collections4.functors.**\",\n            \"org.apache.commons.collections4.comparators.**\",\n            \"org.apache.commons.beanutils.**\",\n            \"com.sun.org.apache.xalan.**\",\n            \"com.sun.org.apache.bcel.**\",\n            \"javassist.**\",\n            \"groovy.lang.MethodClosure\",\n            \"groovy.lang.GroovyShell\",\n            \"groovy.lang.GroovyClassLoader\",\n            \"org.codehaus.groovy.runtime.**\",\n            \"javax.script.**\",\n            \"java.net.URL*\",\n            \"java.net.InetAddress*\",\n            \"javax.naming.**\",\n            \"com.sun.jndi.**\",\n            \"java.rmi.**\",\n            \"sun.rmi.**\",\n            \"com.mchange.**\",\n            \"org.apache.xalan.**\",\n    };\n\n    private XStreamDenyList() {\n    }\n\n    /**\n     * Returns the deny patterns to use, reading from the system property if set,\n     * falling back to hardcoded defaults otherwise.\n     *\n     * @return a fresh copy of the deny patterns array\n     */\n    public static String[] getDenyPatterns() {\n        String property = System.getProperty(DENY_PACKAGES_PROPERTY);\n        if (property != null && !property.isBlank()) {\n            return parsePatterns(property);\n        }\n        return DEFAULT_DENY_PATTERNS.clone();\n    }\n\n    private static String[] parsePatterns(String commaDelimited) {\n        return Arrays.stream(commaDelimited.trim().split(\"\\\\s*,\\\\s*\"))\n                .filter(s -> !s.isEmpty())\n                .toArray(String[]::new);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/main/javadoc/overview.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Frameset//EN\" \"http://www.w3.org/TR/html4/frameset.dtd\">\n<html lang=\"en\">\n<head>\n    <meta http-equiv=\"Content-Type\" content=\"text/html\" charset=\"UTF-8\">\n    <title>package.html</title>\n</head>\n<body>\n<p>This is an overview of Bonita Community common API.</p>\n</body>\n</html>"
  },
  {
    "path": "bpm/bonita-common/src/main/javadoc/stylesheet.css",
    "content": "/* Javadoc style sheet */\n/*\nOverall document style\n*/\nbody {\n    background-color:#ffffff;\n    color:#003355;\n    font-family:\"Trebuchet MS\",Arial,Helvetica,sans-serif;\n    font-size:76%;\n    margin:0;\n}\na:link, a:visited {\n    text-decoration:none;\n    color:#4c6b87;\n}\na:hover, a:focus {\n    text-decoration:none;\n    color:#DD0033;\n}\na:hover{\n    text-decoration: underline;\n}\na:active {\n    text-decoration:none;\n    color:#4c6b87;\n}\na[name] {\n    color:#353833;\n}\na[name]:hover {\n    text-decoration:none;\n    color:#353833;\n}\npre {\n    font-size:1.3em;\n}\nh1 {\n    font-size: 36px;\n}\nh2 {\n    font-size:1.5em;\n}\nh3 {\n    font-size:1.4em;\n}\nh4 {\n    font-size:1.3em;\n}\nh5 {\n    font-size:1.2em;\n}\nh6 {\n    font-size:1.1em;\n}\nul {\n    list-style-type:disc;\n}\ncode, tt {\n    font-size:1.2em;\n}\ndt code {\n    font-size:1.2em;\n}\ntable tr td dt code {\n    font-size:1.2em;\n    vertical-align:top;\n}\nsup {\n    font-size:.6em;\n}\n/*\nDocument title and Copyright styles\n*/\n.clear {\n    clear:both;\n    height:0px;\n    overflow:hidden;\n}\n.aboutLanguage {\n    float:right;\n    padding:0px 21px;\n    font-size:.8em;\n    z-index:200;\n    margin-top:-7px;\n}\n.legalCopy {\n    margin-left:.5em;\n}\n.bar a, .bar a:link, .bar a:visited, .bar a:active {\n    color:#FFFFFF;\n    text-decoration:none;\n}\n.bar a:hover, .bar a:focus {\n    color:#DD0033;\n}\n.tab {\n    background-color:#0066FF;\n    background-image:url(resources/titlebar.gif);\n    background-position:left top;\n    background-repeat:no-repeat;\n    color:#ffffff;\n    padding:8px;\n    width:5em;\n    font-weight:bold;\n}\n/*\nNavigation bar styles\n*/\n.bar {\n    background:#00395A;\n    color:#FFFFFF;\n    padding:.8em .5em .4em .8em;\n    height:auto;/*height:1.8em;*/\n    font-size:1em;\n    margin:0;\n}\n.topNav, .bottomNav {\n    background: #00395A;\n    color:#FFFFFF;\n    float:left;\n    padding:0;\n    width:100%;\n    clear:right;\n    overflow: visible;\n    height: 45px;\n}\n\n.subNav {\n    background-color:#dee3e9;\n    border-bottom:1px solid #9eadc0;\n    float:left;\n    width:100%;\n    overflow:hidden;\n}\n.subNav div {\n    clear:left;\n    float:left;\n    padding:0 0 5px 6px;\n}\nul.navList, ul.subNavList {\n    font-size: 15px;\n    margin:0 auto;\n    padding:0;\n    height: 100%;\n    width: 960px;\n}\nul.navList li{\n    vertical-align: middle;\n    text-align: center;\n    list-style:none;\n    float:left;\n    height: 100%;\n    padding: 0 16px;\n    line-height: 45px;\n}\nul.navList li a{\n    height: 100%;\n    display: block;\n}\nul.subNavList li{\n    list-style:none;\n    float:left;\n    font-size:90%;\n    padding-right: 5px;\n}\n.topNav a:link, .topNav a:active, .topNav a:visited, .bottomNav a:link, .bottomNav a:active, .bottomNav a:visited {\n    color:#FFFFFF;\n    text-decoration:none;\n\n}\n.topNav a:hover, .bottomNav a:hover, .topNav li:hover, .bottomNav li:hover{\n    text-decoration:none;\n    background: #DD0033;\n}\n.navBarCell1Rev {\n    background-color:#DD0033;\n    color:#FFFFFF;\n    margin: 0;\n    position: relative;\n}\n\n.topNav .navBarCell1Rev:after {\n    top: 100%;\n    left:50%;\n    border: solid transparent;\n    content: \" \"; height: 0;\n    width: 0;\n    position: absolute;\n    pointer-events: none;\n    border-color: rgba(221, 0, 51, 0);\n    border-top-color: #DD0033;\n    border-width: 10px;\n    margin-left: -10px;\n}\n.bottomNav .navBarCell1Rev:after {\n    bottom: 100%;\n    left: 50%; \n    border: solid transparent; \n    content: \" \"; \n    height: 0; \n    width: 0; \n    position: absolute; \n    pointer-events: none; \n    border-color: rgba(221, 0, 51, 0); \n    border-bottom-color: #DD0033; \n    border-width: 10px; \n    margin-left: -10px; \n    \n}\n/*\nPage header and footer styles\n*/\n.header, .footer {\n    clear:both;\n    margin:0 20px;\n    padding:5px 0 0 0;\n}\n.indexHeader {\n    margin:10px;\n    position:relative;\n}\n.indexHeader h1 {\n    font-size:1.3em;\n}\n.title {\n    color:#2c4557;\n    margin:10px 0;\n}\n.subTitle {\n    margin:5px 0 0 0;\n}\n.header ul {\n    margin:0 0 25px 0;\n    padding:0;\n}\n.footer ul {\n    margin:20px 0 5px 0;\n}\n.header ul li, .footer ul li {\n    list-style:none;\n    font-size:1.2em;\n}\n/*\nHeading styles\n*/\ndiv.details ul.blockList ul.blockList ul.blockList li.blockList h4, div.details ul.blockList ul.blockList ul.blockListLast li.blockList h4 {\n    background-color:#dee3e9;\n    border-top:1px solid #9eadc0;\n    border-bottom:1px solid #9eadc0;\n    margin:0 0 6px -8px;\n    padding:2px 5px;\n}\nul.blockList ul.blockList ul.blockList li.blockList h3 {\n    background-color:#dee3e9;\n    border-top:1px solid #9eadc0;\n    border-bottom:1px solid #9eadc0;\n    margin:0 0 6px -8px;\n    padding:2px 5px;\n}\nul.blockList ul.blockList li.blockList h3 {\n    padding:0;\n    margin:15px 0;\n}\nul.blockList li.blockList h2 {\n    padding:0px 0 20px 0;\n}\n/*\nPage layout container styles\n*/\n.contentContainer, .sourceContainer, .classUseContainer, .serializedFormContainer, .constantValuesContainer {\n    clear:both;\n    padding:10px 20px;\n    position:relative;\n}\n.indexContainer {\n    margin:10px;\n    position:relative;\n    font-size:1.0em;\n}\n.indexContainer h2 {\n    font-size:1.1em;\n    padding:0 0 3px 0;\n}\n.indexContainer ul {\n    margin:0;\n    padding:0;\n}\n.indexContainer ul li {\n    list-style:none;\n}\n.contentContainer .description dl dt, .contentContainer .details dl dt, .serializedFormContainer dl dt {\n    font-size:1.1em;\n    font-weight:bold;\n    margin:10px 0 0 0;\n    color:#DD0033;\n}\n.contentContainer .description dl dd, .contentContainer .details dl dd, .serializedFormContainer dl dd {\n    margin:10px 0 10px 20px;\n}\n.serializedFormContainer dl.nameValue dt {\n    margin-left:1px;\n    font-size:1.1em;\n    display:inline;\n    font-weight:bold;\n}\n.serializedFormContainer dl.nameValue dd {\n    margin:0 0 0 1px;\n    font-size:1.1em;\n    display:inline;\n}\n/*\nList styles\n*/\nul.horizontal li {\n    display:inline;\n    font-size:0.9em;\n}\nul.inheritance {\n    margin:0;\n    padding:0;\n}\nul.inheritance li {\n    display:inline;\n    list-style:none;\n}\nul.inheritance li ul.inheritance {\n    margin-left:15px;\n    padding-left:15px;\n    padding-top:1px;\n}\nul.blockList, ul.blockListLast {\n    margin:10px 0 10px 0;\n    padding:0;\n}\nul.blockList li.blockList, ul.blockListLast li.blockList {\n    list-style:none;\n    margin-bottom:25px;\n}\nul.blockList ul.blockList li.blockList, ul.blockList ul.blockListLast li.blockList {\n    padding:0px 20px 5px 10px;\n    border:1px solid #9eadc0;\n    background-color:#f9f9f9;\n}\nul.blockList ul.blockList ul.blockList li.blockList, ul.blockList ul.blockList ul.blockListLast li.blockList {\n    padding:0 0 5px 8px;\n    background-color:#ffffff;\n    border:1px solid #9eadc0;\n    border-top:none;\n}\nul.blockList ul.blockList ul.blockList ul.blockList li.blockList {\n    margin-left:0;\n    padding-left:0;\n    padding-bottom:15px;\n    border:none;\n    border-bottom:1px solid #9eadc0;\n}\nul.blockList ul.blockList ul.blockList ul.blockList li.blockListLast {\n    list-style:none;\n    border-bottom:none;\n    padding-bottom:0;\n}\ntable tr td dl, table tr td dl dt, table tr td dl dd {\n    margin-top:0;\n    margin-bottom:1px;\n}\n/*\nTable styles\n*/\n.contentContainer table, .classUseContainer table, .constantValuesContainer table {\n    border-bottom:1px solid #9eadc0;\n    width:100%;\n}\n.contentContainer ul li table, .classUseContainer ul li table, .constantValuesContainer ul li table {\n    width:100%;\n}\n.contentContainer .description table, .contentContainer .details table {\n    border-bottom:none;\n}\n.contentContainer ul li table th.colOne, .contentContainer ul li table th.colFirst, .contentContainer ul li table th.colLast, .classUseContainer ul li table th, .constantValuesContainer ul li table th, .contentContainer ul li table td.colOne, .contentContainer ul li table td.colFirst, .contentContainer ul li table td.colLast, .classUseContainer ul li table td, .constantValuesContainer ul li table td{\n    vertical-align:top;\n    padding-right:20px;\n}\n.contentContainer ul li table th.colLast, .classUseContainer ul li table th.colLast,.constantValuesContainer ul li table th.colLast,\n.contentContainer ul li table td.colLast, .classUseContainer ul li table td.colLast,.constantValuesContainer ul li table td.colLast,\n.contentContainer ul li table th.colOne, .classUseContainer ul li table th.colOne,\n.contentContainer ul li table td.colOne, .classUseContainer ul li table td.colOne {\n    padding-right:3px;\n}\n.overviewSummary caption, .packageSummary caption, .contentContainer ul.blockList li.blockList caption, .summary caption, .classUseContainer caption, .constantValuesContainer caption {\n    position:relative;\n    text-align:left;\n    background-repeat:no-repeat;\n    color:#FFFFFF;\n    font-weight:bold;\n    clear:none;\n    overflow:hidden;\n    padding:0px;\n    margin:0px;\n}\ncaption a:link, caption a:hover, caption a:active, caption a:visited {\n    color:#FFFFFF;\n}\n.contentContainer > ul.blockList caption span{\n  color: #DD0033;\n  border: 1px solid #DD0033;\n}\n.contentContainer > ul:nth-of-type(9n+1).blockList caption span{\n  border: 1px solid #1A2574;\n  color: #1A2574;\n}\n.contentContainer > ul:nth-of-type(9n+2).blockList caption span {\n  border: 1px solid #77BB33;\n  color: #77BB33;\n}\n.contentContainer > ul:nth-of-type(9n+3).blockList caption span {\n  border: 1px solid #F89828;\n  color: #F89828;\n}\n.contentContainer > ul:nth-of-type(9n+4).blockList caption span {\n  border: 1px solid #DD0033;\n  color: #DD0033;\n}\n.contentContainer > ul:nth-of-type(9n+5).blockList caption span {\n  border: 1px solid #1A2574;\n  color: #1A2574;\n}\n.contentContainer > ul:nth-of-type(9n+6).blockList caption span {\n  border: 1px solid #77BB33;\n  color: #77BB33;\n}\n.contentContainer > ul:nth-of-type(9n+7).blockList caption span {\n  border: 1px solid #F89828;\n  color: #F89828;\n}\n.contentContainer > ul:nth-of-type(9n+8).blockList caption span {\n  border: 1px solid #DD0033;\n  color: #DD0033;\n}\n.contentContainer > ul:nth-of-type(9n+9).blockList caption span {\n  border: 1px solid #1A2574;\n  color: #1A2574;\n}\n\n.overviewSummary caption span, .packageSummary caption span, .contentContainer ul.blockList li.blockList caption span, .summary caption span, .classUseContainer caption span, .constantValuesContainer caption span {\n    white-space:nowrap;\n    padding: 2px 4px;\n    display:block;\n    float:left;\n\n    height:18px;\n    margin-bottom: 10px;\n}\n.overviewSummary .tabEnd, .packageSummary .tabEnd, .contentContainer ul.blockList li.blockList .tabEnd, .summary .tabEnd, .classUseContainer .tabEnd, .constantValuesContainer .tabEnd {\n    display:none;\n}\nul.blockList ul.blockList li.blockList table {\n    margin:0 0 12px 0px;\n    width:100%;\n}\n.tableSubHeadingColor {\n    background-color: #EEEEFF;\n}\n.altColor {\n    background-color:#E6EBF1;\n}\n.altColor:hover, .rowColor:hover{\n    background-color:#F09383;\n}\n.altColor:hover a, .rowColor:hover a,.rowColor:hover code{\n    color:#FFFFFF;\n    text-decoration: none;\n}\n.rowColor {\n    background-color:#ffffff;\n}\n.overviewSummary td, .packageSummary td, .contentContainer ul.blockList li.blockList td, .summary td, .classUseContainer td, .constantValuesContainer td {\n    text-align:left;\n    padding:3px 3px 3px 7px;\n}\nth.colFirst, th.colLast, th.colOne, .constantValuesContainer th {\n    background:#dee3e9;\n    border-top:1px solid #9eadc0;\n    border-bottom:1px solid #9eadc0;\n    text-align:left;\n    padding:3px 3px 3px 7px;\n}\ntd.colOne a:link, td.colOne a:active, td.colOne a:visited, td.colOne a:hover, td.colFirst a:link, td.colFirst a:active, td.colFirst a:visited, td.colFirst a:hover, td.colLast a:link, td.colLast a:active, td.colLast a:visited, td.colLast a:hover, .constantValuesContainer td a:link, .constantValuesContainer td a:active, .constantValuesContainer td a:visited, .constantValuesContainer td a:hover {\n    font-weight:bold;\n}\ntd.colFirst, th.colFirst {\n    border-left:1px solid #9eadc0;\n    white-space:nowrap;\n}\ntd.colLast, th.colLast {\n    border-right:1px solid #9eadc0;\n}\ntd.colOne, th.colOne {\n    border-right:1px solid #9eadc0;\n    border-left:1px solid #9eadc0;\n}\ntable.overviewSummary  {\n    padding:0px;\n    margin-left:0px;\n}\ntable.overviewSummary td.colFirst, table.overviewSummary th.colFirst,\ntable.overviewSummary td.colOne, table.overviewSummary th.colOne {\n    width:25%;\n    vertical-align:middle;\n}\ntable.packageSummary td.colFirst, table.overviewSummary th.colFirst {\n    width:25%;\n    vertical-align:middle;\n}\n/*\nContent styles\n*/\n.description pre {\n    margin-top:0;\n}\n.deprecatedContent {\n    margin:0;\n    padding:10px 0;\n}\n.docSummary {\n    padding:0;\n}\n/*\nFormatting effect styles\n*/\n.sourceLineNo {\n    color:green;\n    padding:0 30px 0 0;\n}\nh1.hidden {\n    visibility:hidden;\n    overflow:hidden;\n    font-size:.9em;\n}\n.block {\n    display:block;\n    margin:3px 0 0 0;\n}\n.strong {\n    font-weight:bold;\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/api/permission/APICallContextTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.permission;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.junit.Test;\n\npublic class APICallContextTest {\n\n    @Test\n    public void getFilters() {\n        final APICallContext apiCallContext = new APICallContext();\n        apiCallContext.setQueryString(\"p=0&c=10&o=priority%20DESC&f=state%3dready&f=user_id%3d104&d=processId\");\n\n        final Map<String, String> filters = apiCallContext.getFilters();\n\n        assertThat(filters).containsOnly(entry(\"user_id\", \"104\"), entry(\"state\", \"ready\"));\n    }\n\n    @Test\n    public void getFilters_with_multi_value() {\n        final APICallContext apiCallContext = new APICallContext();\n        apiCallContext.setQueryString(\"p=0&c=10&f=state%3dready,completed&f=user_id%3d104\");\n\n        final Map<String, String> filters = apiCallContext.getFilters();\n\n        assertThat(filters).containsOnly(entry(\"user_id\", \"104\"), entry(\"state\", \"ready,completed\"));\n    }\n\n    @Test\n    public void getFilters_with_uncoded_equals() {\n        final APICallContext apiCallContext = new APICallContext();\n        apiCallContext.setQueryString(\"p=0&c=10&f=state=ready,completed&f=user_id=104\");\n\n        final Map<String, String> filters = apiCallContext.getFilters();\n\n        assertThat(filters).containsOnly(entry(\"user_id\", \"104\"), entry(\"state\", \"ready,completed\"));\n    }\n\n    @Test\n    public void getFilters_with_UpperCase_in_separator() {\n        final APICallContext apiCallContext = new APICallContext();\n        apiCallContext.setQueryString(\"p=0&c=10&o=priority%20DESC&f=state%3dready&f=user_id%3D104&d=processId\");\n\n        final Map<String, String> filters = apiCallContext.getFilters();\n\n        assertThat(filters).containsOnly(entry(\"user_id\", \"104\"), entry(\"state\", \"ready\"));\n    }\n\n    @Test\n    public void getCompoundResourceId() {\n        final APICallContext apiCallContext = new APICallContext();\n        apiCallContext.setResourceId(\"1/2/3\");\n\n        final List<String> compoundResourceId = apiCallContext.getCompoundResourceId();\n\n        assertThat(compoundResourceId).containsExactly(\"1\", \"2\", \"3\");\n    }\n\n    @Test\n    public void getCompoundResourceId_when_single_id() {\n        final APICallContext apiCallContext = new APICallContext();\n        apiCallContext.setResourceId(\"1\");\n\n        final List<String> compoundResourceId = apiCallContext.getCompoundResourceId();\n\n        assertThat(compoundResourceId).containsExactly(\"1\");\n    }\n\n    @Test\n    public void getCompoundResourceId_when_no_resource_id() {\n        final APICallContext apiCallContext = new APICallContext();\n\n        final List<String> compoundResourceId = apiCallContext.getCompoundResourceId();\n\n        assertThat(compoundResourceId).isEmpty();\n    }\n\n    @Test\n    public void getFilters_with_corrupt_filters() {\n        final APICallContext apiCallContext = new APICallContext();\n        apiCallContext.setQueryString(\"p=0&c=10&o=priority%20DESC&f=state%3dready&f=user_id%3d&d=processId\");\n\n        final Map<String, String> filters = apiCallContext.getFilters();\n\n        assertThat(filters).containsOnly(entry(\"state\", \"ready\"));\n    }\n\n    @Test\n    public void getFilters_with_corrupt_filters_not_encoded() {\n        final APICallContext apiCallContext = new APICallContext();\n        apiCallContext.setQueryString(\"p=0&c=10&o=priority%20DESC&f=state%3dready&f=user_id=&d=processId\");\n\n        final Map<String, String> filters = apiCallContext.getFilters();\n\n        assertThat(filters).containsOnly(entry(\"state\", \"ready\"));\n    }\n\n    @Test\n    public void getFilters_with_value_not_encoded() {\n        final APICallContext apiCallContext = new APICallContext();\n        apiCallContext.setQueryString(\"p=0&c=10&o=priority%20DESC&f=state%3dready&f=user_id=101&d=processId\");\n\n        final Map<String, String> filters = apiCallContext.getFilters();\n\n        assertThat(filters).containsOnly(entry(\"state\", \"ready\"), entry(\"user_id\", \"101\"));\n    }\n\n    @Test\n    public void getSearchTerm() {\n        final APICallContext apiCallContext = new APICallContext();\n        apiCallContext.setQueryString(\"p=0&c=10&o=priority%20DESC&f=state%3dready&f=user_id%3d104&d=processId&s=toto\");\n\n        final String searchTerm = apiCallContext.getSearchTerm();\n\n        assertThat(searchTerm).isEqualTo(\"toto\");\n    }\n\n    @Test\n    public void should_return_allparameters() {\n        final APICallContext apiCallContext = new APICallContext();\n        apiCallContext.setQueryString(\"p=0&c=10&o=priority%20DESC&f=state%3dready&f=user_id%3d3&d=processId&param=1\");\n\n        final Map<String, String[]> parameters = apiCallContext.getParameters();\n\n        assertThat(parameters).contains(entry(\"p\", new String[] { \"0\" }));\n        assertThat(parameters).contains(entry(\"c\", new String[] { \"10\" }));\n        assertThat(parameters).contains(entry(\"o\", new String[] { \"priority%20DESC\" }));\n        assertThat(parameters).contains(entry(\"f\", new String[] { \"state%3dready\", \"user_id%3d3\" }));\n        assertThat(parameters).contains(entry(\"d\", new String[] { \"processId\" }));\n        assertThat(parameters).contains(entry(\"param\", new String[] { \"1\" }));\n    }\n\n    @Test\n    public void getSearchTerm_with_corrupt_search_term() {\n        final APICallContext apiCallContext = new APICallContext();\n        apiCallContext.setQueryString(\"p=0&c=10&o=priority%20DESC&f=state%3dready&f=user_id%3d104&d=processId&s=\");\n\n        final String searchTerm = apiCallContext.getSearchTerm();\n\n        assertThat(searchTerm).isEqualTo(null);\n    }\n\n    @Test\n    public void getSearchTerm_with_corrupt_query() {\n        final APICallContext apiCallContext = new APICallContext();\n        apiCallContext.setQueryString(\"p=0&c=10&o=priority%20DESC&f=state%3dready&f=user_id%3d104&d=processId&s\");\n\n        final String searchTerm = apiCallContext.getSearchTerm();\n\n        assertThat(searchTerm).isEqualTo(null);\n    }\n\n    @Test\n    public void construct_with_null_body_and_query() {\n        final APICallContext apiCallContext = new APICallContext(\"GET\", \"identity\", \"user\", \"1\");\n\n        final String searchTerm = apiCallContext.getSearchTerm();\n        final Map<String, String> filters = apiCallContext.getFilters();\n        final String body = apiCallContext.getBody();\n\n        assertThat(searchTerm).isEqualTo(null);\n        assertThat(filters).isEmpty();\n        assertThat(body).isEqualTo(null);\n    }\n\n    @Test\n    public void should_isGETMethod_return_true() {\n        final APICallContext apiCallContext = new APICallContext();\n        apiCallContext.setMethod(\"GET\");\n\n        final boolean getMethod = apiCallContext.isGET();\n\n        assertThat(getMethod).isTrue();\n    }\n\n    @Test\n    public void should_isGETMethod_return_false() {\n        final APICallContext apiCallContext = new APICallContext();\n\n        final boolean getMethod = apiCallContext.isGET();\n\n        assertThat(getMethod).isFalse();\n    }\n\n    @Test\n    public void should_isPOSTMethod_return_true() {\n        final APICallContext apiCallContext = new APICallContext();\n        apiCallContext.setMethod(\"POST\");\n\n        final boolean getMethod = apiCallContext.isPOST();\n\n        assertThat(getMethod).isTrue();\n    }\n\n    @Test\n    public void should_isPOSTMethod_return_false() {\n        final APICallContext apiCallContext = new APICallContext();\n\n        final boolean getMethod = apiCallContext.isPOST();\n\n        assertThat(getMethod).isFalse();\n    }\n\n    @Test\n    public void should_isPUTMethod_return_true() {\n        final APICallContext apiCallContext = new APICallContext();\n        apiCallContext.setMethod(\"PUT\");\n\n        final boolean getMethod = apiCallContext.isPUT();\n\n        assertThat(getMethod).isTrue();\n    }\n\n    @Test\n    public void should_isPUTMethod_return_false() {\n        final APICallContext apiCallContext = new APICallContext();\n\n        final boolean getMethod = apiCallContext.isPUT();\n\n        assertThat(getMethod).isFalse();\n    }\n\n    @Test\n    public void should_isDELETEMethod_return_true() {\n        final APICallContext apiCallContext = new APICallContext();\n        apiCallContext.setMethod(\"DELETE\");\n\n        final boolean getMethod = apiCallContext.isDELETE();\n\n        assertThat(getMethod).isTrue();\n    }\n\n    @Test\n    public void should_isDELETEMethod_return_false() {\n        final APICallContext apiCallContext = new APICallContext();\n\n        final boolean getMethod = apiCallContext.isDELETE();\n\n        assertThat(getMethod).isFalse();\n    }\n\n    @Test\n    public void should_equals_works() {\n        final APICallContext apiCallContext = new APICallContext();\n        apiCallContext.setMethod(\"GET\");\n        apiCallContext.setApiName(\"apiName\");\n        apiCallContext.setResourceName(\"myResource\");\n        apiCallContext.setResourceId(\"125\");\n\n        assertThat(apiCallContext).isEqualTo(new APICallContext(\"GET\", \"apiName\", \"myResource\", \"125\"));\n        assertThat(apiCallContext.hashCode())\n                .isEqualTo(new APICallContext(\"GET\", \"apiName\", \"myResource\", \"125\").hashCode());\n        assertThat(apiCallContext.toString())\n                .isEqualTo(new APICallContext(\"GET\", \"apiName\", \"myResource\", \"125\").toString());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/serialization/BusinessDataObjectMapperTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.serialization;\n\nimport static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.io.StringWriter;\nimport java.time.LocalDate;\n\nimport org.bonitasoft.engine.bdm.serialization.model.Invoice;\nimport org.junit.Test;\n\npublic class BusinessDataObjectMapperTest {\n\n    private final BusinessDataObjectMapper objectMapper = new BusinessDataObjectMapper();\n\n    @Test\n    public void should_serialize_object_with_custom_local_date_and_time_serializers() throws Exception {\n        // given:\n        Invoice invoice = new Invoice();\n        invoice.setCustomerName(\"Bonitasoft\");\n        invoice.setDate(LocalDate.of(2018, 8, 15));\n        invoice.setComments(null);\n\n        String expectedJson;\n        try (var inputStream = BusinessDataObjectMapperTest.class.getResourceAsStream(\"simpleInvoice.json\")) {\n            assertThat(inputStream).isNotNull();\n            expectedJson = new String(inputStream.readAllBytes());\n        }\n\n        // when:\n        StringWriter writer = new StringWriter();\n        objectMapper.writeValue(writer, invoice);\n\n        // then:\n        assertThatJson(writer.toString()).as(\"Serialization uses date and time custom serializers\")\n                .isEqualTo(expectedJson);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/serialization/CustomLocalDateDeserializerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.serialization;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.doReturn;\n\nimport java.time.LocalDate;\n\nimport com.fasterxml.jackson.core.JsonParser;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class CustomLocalDateDeserializerTest {\n\n    @Mock\n    private JsonParser jsonParser;\n\n    @Test\n    public void deserialize_should_convert_to_ISO_8601() throws Exception {\n        // given:\n        doReturn(\"2018-09-01\").when(jsonParser).readValueAs(String.class);\n        final CustomLocalDateDeserializer serializer = new CustomLocalDateDeserializer();\n\n        // when:\n        final LocalDate deserialized = serializer.deserialize(jsonParser, null);\n\n        // then:\n        assertThat(deserialized).isEqualTo(LocalDate.of(2018, 9, 1));\n    }\n\n    @Test\n    public void deserialize_should_return_null_for_a_null_date() throws Exception {\n        // given:\n        doReturn(null).when(jsonParser).readValueAs(String.class);\n\n        // when:\n        final LocalDate localDate = new CustomLocalDateDeserializer().deserialize(jsonParser, null);\n\n        // then:\n        assertThat(localDate).isNull();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/serialization/CustomLocalDateSerializerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.serialization;\n\nimport static org.mockito.Mockito.verify;\n\nimport java.time.LocalDate;\n\nimport com.fasterxml.jackson.core.JsonGenerator;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class CustomLocalDateSerializerTest {\n\n    @Mock\n    JsonGenerator jsonGenerator;\n\n    @Test\n    public void serialize_should_write_date_to_ISO_8601() throws Exception {\n        // given:\n        final CustomLocalDateSerializer serializer = new CustomLocalDateSerializer();\n\n        // when:\n        serializer.serialize(LocalDate.of(2018, 8, 15), jsonGenerator, null);\n\n        // then:\n        verify(jsonGenerator).writeString(\"2018-08-15\");\n    }\n\n    @Test\n    public void serialize_should_write_null_for_a_null_date() throws Exception {\n        // when:\n        new CustomLocalDateSerializer().serialize(null, jsonGenerator, null);\n\n        // then:\n        verify(jsonGenerator).writeString((String) null);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/serialization/CustomLocalDateTimeDeserializerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.serialization;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.doReturn;\n\nimport java.time.LocalDateTime;\n\nimport com.fasterxml.jackson.core.JsonParser;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class CustomLocalDateTimeDeserializerTest {\n\n    @Mock\n    private JsonParser jsonParser;\n\n    @Test\n    public void deserialize_should_convert_to_ISO_8601() throws Exception {\n        // given:\n        doReturn(\"2018-09-01T09:21:08\").when(jsonParser).readValueAs(String.class);\n        final CustomLocalDateTimeDeserializer serializer = new CustomLocalDateTimeDeserializer();\n\n        // when:\n        final LocalDateTime deserialized = serializer.deserialize(jsonParser, null);\n\n        // then:\n        assertThat(deserialized).isEqualTo(LocalDateTime.of(2018, 9, 1, 9, 21, 8));\n    }\n\n    @Test\n    public void deserialize_should_return_null_for_a_null_date() throws Exception {\n        // given:\n        doReturn(null).when(jsonParser).readValueAs(String.class);\n\n        // when:\n        final LocalDateTime localDateTimeTime = new CustomLocalDateTimeDeserializer().deserialize(jsonParser, null);\n\n        // then:\n        assertThat(localDateTimeTime).isNull();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/serialization/CustomLocalDateTimeSerializerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.serialization;\n\nimport static org.mockito.Mockito.verify;\n\nimport java.time.LocalDateTime;\n\nimport com.fasterxml.jackson.core.JsonGenerator;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class CustomLocalDateTimeSerializerTest {\n\n    @Mock\n    JsonGenerator jsonGenerator;\n\n    @Test\n    public void serialize_should_write_date_to_ISO_8601() throws Exception {\n        // given:\n        final CustomLocalDateTimeSerializer serializer = new CustomLocalDateTimeSerializer();\n\n        // when:\n        serializer.serialize(LocalDateTime.of(2018, 10, 23, 11, 21, 7), jsonGenerator, null);\n\n        // then:\n        verify(jsonGenerator).writeString(\"2018-10-23T11:21:07\");\n    }\n\n    @Test\n    public void serialize_should_write_null_for_a_null_date() throws Exception {\n        // when:\n        new CustomLocalDateTimeSerializer().serialize(null, jsonGenerator, null);\n\n        // then:\n        verify(jsonGenerator).writeString((String) null);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/serialization/CustomOffsetDateTimeDeserializerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.serialization;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.doReturn;\n\nimport java.time.LocalDateTime;\nimport java.time.OffsetDateTime;\nimport java.time.ZoneOffset;\n\nimport com.fasterxml.jackson.core.JsonParser;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class CustomOffsetDateTimeDeserializerTest {\n\n    @Mock\n    private JsonParser jsonParser;\n\n    @Test\n    public void deserialize_should_convert_to_UTC() throws Exception {\n        // given:\n        doReturn(\"2018-09-01T09:21:08+07:00\").when(jsonParser).readValueAs(String.class);\n        final CustomOffsetDateTimeDeserializer serializer = new CustomOffsetDateTimeDeserializer();\n\n        // when:\n        final OffsetDateTime deserialized = serializer.deserialize(jsonParser, null);\n\n        // then:\n        assertThat(deserialized)\n                .isEqualTo(OffsetDateTime.of(LocalDateTime.of(2018, 9, 1, 2, 21, 8), ZoneOffset.ofHours(0)));\n    }\n\n    @Test\n    public void deserialize_should_return_null_for_a_null_date() throws Exception {\n        // given:\n        doReturn(null).when(jsonParser).readValueAs(String.class);\n\n        // when:\n        final OffsetDateTime offsetDateTime = new CustomOffsetDateTimeDeserializer().deserialize(jsonParser, null);\n\n        // then:\n        assertThat(offsetDateTime).isNull();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/serialization/CustomOffsetDateTimeSerializerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.serialization;\n\nimport static org.mockito.Mockito.verify;\n\nimport java.time.LocalDateTime;\nimport java.time.OffsetDateTime;\nimport java.time.ZoneOffset;\n\nimport com.fasterxml.jackson.core.JsonGenerator;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class CustomOffsetDateTimeSerializerTest {\n\n    @Mock\n    JsonGenerator jsonGenerator;\n\n    @Test\n    public void serialize_should_write_date_converted_to_UTC() throws Exception {\n        // given:\n        final CustomOffsetDateTimeSerializer serializer = new CustomOffsetDateTimeSerializer();\n\n        // when:\n        serializer.serialize(OffsetDateTime.of(LocalDateTime.of(2018, 10, 23, 11, 21, 7), ZoneOffset.ofHours(2)),\n                jsonGenerator, null);\n\n        // then:\n        verify(jsonGenerator).writeString(\"2018-10-23T09:21:07Z\");\n    }\n\n    @Test\n    public void serialize_should_write_null_for_a_null_date() throws Exception {\n        // when:\n        new CustomOffsetDateTimeSerializer().serialize(null, jsonGenerator, null);\n\n        // then:\n        verify(jsonGenerator).writeString((String) null);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/serialization/model/Invoice.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.serialization.model;\n\nimport java.time.LocalDate;\n\nimport org.bonitasoft.engine.bdm.Entity;\n\npublic class Invoice implements Entity {\n\n    private Long persistenceId;\n    private Long persistenceVersion;\n    private String customerName;\n    private LocalDate date;\n    private Object comments;\n\n    @Override\n    public Long getPersistenceId() {\n        return persistenceId;\n    }\n\n    @Override\n    public Long getPersistenceVersion() {\n        return persistenceVersion;\n    }\n\n    public String getCustomerName() {\n        return customerName;\n    }\n\n    public void setCustomerName(String customerName) {\n        this.customerName = customerName;\n    }\n\n    public LocalDate getDate() {\n        return date;\n    }\n\n    public void setDate(LocalDate date) {\n        this.date = date;\n    }\n\n    public Object getComments() {\n        return comments;\n    }\n\n    public void setComments(Object comments) {\n        this.comments = comments;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/validator/BusinessObjectModelValidatorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.bdm.validator.assertion.RuleOfCondition.ruleOf;\n\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.model.field.FieldType;\nimport org.bonitasoft.engine.bdm.model.field.SimpleField;\nimport org.bonitasoft.engine.bdm.validator.rule.composition.CyclicCompositionValidationRule;\nimport org.bonitasoft.engine.bdm.validator.rule.composition.UniquenessCompositionValidationRule;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Romain Bioteau\n */\npublic class BusinessObjectModelValidatorTest {\n\n    private BusinessObjectModelValidator validator;\n\n    @Before\n    public void setUp() {\n        validator = new BusinessObjectModelValidator();\n    }\n\n    @Test\n    public void shouldConstructor_FillListOfRules() {\n        assertThat(validator.getRules()).isNotEmpty();\n    }\n\n    @Test\n    public void shouldValidate_ReturnsAValidStatus() {\n        BusinessObjectModel bom = new BusinessObjectModel();\n        BusinessObject bo = new BusinessObject();\n        bo.setQualifiedName(\"org.bonita.Car\");\n        SimpleField nameField = new SimpleField();\n        nameField.setName(\"bmw\");\n        nameField.setType(FieldType.STRING);\n        bo.addField(nameField);\n        bom.addBusinessObject(bo);\n        assertThat(validator.validate(bom).isOk()).isTrue();\n    }\n\n    @Test\n    public void shouldValidate_ReturnsAFailedStatus() {\n        BusinessObjectModel bom = new BusinessObjectModel();\n        BusinessObject bo = new BusinessObject();\n        bo.setQualifiedName(\"org.bonita.Car\");\n        SimpleField fieldWithTwoErrors = new SimpleField();\n        fieldWithTwoErrors.setName(\"bmw 5\");\n        bo.getFields().add(fieldWithTwoErrors);\n        bom.addBusinessObject(bo);\n        ValidationStatus validationStatus = validator.validate(bom);\n        assertThat(validationStatus.isOk()).isFalse();\n        assertThat(validationStatus.getErrors()).hasSize(2);\n    }\n\n    @Test\n    public void should_validate_cyclic_composition() {\n        assertThat(validator.getRules()).haveAtLeast(1, ruleOf(CyclicCompositionValidationRule.class));\n    }\n\n    @Test\n    public void should_validate_composition_uniqueness() {\n        assertThat(validator.getRules()).haveAtLeast(1, ruleOf(UniquenessCompositionValidationRule.class));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/validator/UniqueNameValidatorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator;\n\nimport static java.util.Arrays.asList;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.bdm.validator.assertion.ValidationStatusAssert.assertThat;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bdm.model.NamedElement;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class UniqueNameValidatorTest {\n\n    private UniqueNameValidator validator;\n\n    @Before\n    public void initValidator() {\n        validator = new UniqueNameValidator();\n    }\n\n    @Test\n    public void should_validate_a_list_with_no_duplicated_names() {\n        List<NamedElement> listWithNoDuplicatedNames = asList(aNamedElement(\"aName\"), aNamedElement(\"anOtherName\"),\n                aNamedElement(\"yetDifferentName\"));\n\n        ValidationStatus status = validator.validate(listWithNoDuplicatedNames, \"named elements\");\n\n        assertThat(status).isOk();\n    }\n\n    @Test\n    public void should_not_validate_a_list_duplicated_names() {\n        String duplicatedName = \"duplicatedName\";\n        List<NamedElement> listWithNoDuplicatedNames = asList(aNamedElement(\"notDuplicatedName\"),\n                aNamedElement(duplicatedName), aNamedElement(duplicatedName),\n                aNamedElement(duplicatedName));\n\n        ValidationStatus status = validator.validate(listWithNoDuplicatedNames, \"named elements\");\n\n        assertThat(status).isNotOk();\n        assertThat(status.getErrors()).hasSize(1);\n    }\n\n    public NamedElement aNamedElement(String name) {\n        return new FakeNamedElement(name);\n    }\n\n    public static class FakeNamedElement implements NamedElement {\n\n        private final String name;\n\n        public FakeNamedElement(String name) {\n            this.name = name;\n        }\n\n        @Override\n        public String getName() {\n            return name;\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/validator/ValidationStatusTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Romain Bioteau\n */\npublic class ValidationStatusTest {\n\n    private ValidationStatus validationStatus;\n\n    @Before\n    public void setUp() {\n        validationStatus = new ValidationStatus();\n    }\n\n    @Test\n    public void isOk_should_return_true() {\n        assertThat(validationStatus.isOk()).isTrue();\n    }\n\n    @Test\n    public void isOk_should_return_false_if_status_contains_errors() {\n        validationStatus.addError(null, \"an error\");\n        assertThat(validationStatus.isOk()).isFalse();\n    }\n\n    @Test\n    public void isOk_should_return_false_if_add_a_status_with_errors() {\n        ValidationStatus status = new ValidationStatus();\n        status.addError(null, \"an error\");\n        validationStatus.addValidationStatus(status);\n        assertThat(validationStatus.isOk()).isFalse();\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void shouldAddError_throw_an_IllegalArgumentException_for_null_input() {\n        validationStatus.addError(null, null);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void shouldAddError_throw_an_IllegalArgumentException_for_empty_input() {\n        validationStatus.addError(null, null);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/validator/rule/BusinessObjectModelValidationRuleTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator.rule;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.bdm.validator.assertion.ValidationStatusAssert.assertThat;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.model.UniqueConstraint;\nimport org.bonitasoft.engine.bdm.model.field.FieldType;\nimport org.bonitasoft.engine.bdm.model.field.RelationField;\nimport org.bonitasoft.engine.bdm.model.field.RelationField.FetchType;\nimport org.bonitasoft.engine.bdm.model.field.SimpleField;\nimport org.bonitasoft.engine.bdm.validator.ValidationStatus;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Romain Bioteau\n */\npublic class BusinessObjectModelValidationRuleTest {\n\n    private BusinessObjectModelValidationRule businessObjectModelValidationRule;\n\n    @Before\n    public void setUp() {\n        businessObjectModelValidationRule = new BusinessObjectModelValidationRule();\n    }\n\n    @Test\n    public void should_apply_to_businessObjectModel() {\n        assertThat(businessObjectModelValidationRule.appliesTo(new BusinessObject())).isFalse();\n        assertThat(businessObjectModelValidationRule.appliesTo(new SimpleField())).isFalse();\n        assertThat(businessObjectModelValidationRule.appliesTo(new UniqueConstraint())).isFalse();\n\n        assertThat(businessObjectModelValidationRule.appliesTo(new BusinessObjectModel())).isTrue();\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void shouddCheckRule_throw_IllegalArgumentException() {\n        businessObjectModelValidationRule.checkRule(new SimpleField());\n    }\n\n    @Test\n    public void should_validate_that_bom_has_at_least_one_businessObject() {\n        final BusinessObjectModel bom = new BusinessObjectModel();\n\n        final ValidationStatus validationStatus = businessObjectModelValidationRule.validate(bom);\n\n        assertThat(validationStatus).isNotOk();\n    }\n\n    @Test\n    public void should_return_a_valid_status_when_bom_is_valid() {\n        final BusinessObjectModel bom = new BusinessObjectModel();\n        bom.addBusinessObject(new BusinessObject());\n\n        final ValidationStatus validationStatus = businessObjectModelValidationRule.validate(bom);\n\n        assertThat(validationStatus).isOk();\n    }\n\n    @Test\n    public void should_return_a_error_status_when_bom_contains_invalid_query_names() {\n        final BusinessObjectModel bom = new BusinessObjectModel();\n\n        final BusinessObject employee = new BusinessObject();\n        employee.setQualifiedName(\"Employee\");\n        final BusinessObject address = new BusinessObject();\n        address.setQualifiedName(\"Address\");\n\n        final SimpleField street = new SimpleField();\n        street.setName(\"street\");\n        street.setType(FieldType.STRING);\n        address.addField(street);\n\n        final RelationField addresses = new RelationField();\n        addresses.setName(\"addresses\");\n        addresses.setCollection(true);\n        addresses.setFetchType(FetchType.LAZY);\n        addresses.setReference(address);\n\n        employee.addField(addresses);\n\n        address.addQuery(\"findAddressesByEmployeePersistenceId\", \"\", List.class.getName());//Duplicated query name\n\n        bom.addBusinessObject(employee);\n        bom.addBusinessObject(address);\n\n        final ValidationStatus validationStatus = businessObjectModelValidationRule.validate(bom);\n\n        assertThat(validationStatus).isNotOk();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/validator/rule/BusinessObjectValidationRuleTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator.rule;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.bdm.builder.BusinessObjectBuilder.aBO;\nimport static org.bonitasoft.engine.bdm.builder.FieldBuilder.aBooleanField;\nimport static org.bonitasoft.engine.bdm.validator.assertion.ValidationStatusAssert.assertThat;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.model.UniqueConstraint;\nimport org.bonitasoft.engine.bdm.model.field.SimpleField;\nimport org.bonitasoft.engine.bdm.validator.ValidationStatus;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Romain Bioteau\n */\npublic class BusinessObjectValidationRuleTest {\n\n    private BusinessObjectValidationRule businessObjectValidationRule;\n\n    @Before\n    public void setUp() {\n        businessObjectValidationRule = new BusinessObjectValidationRule();\n    }\n\n    @Test\n    public void should_apply_to_businessObject() {\n        assertThat(businessObjectValidationRule.appliesTo(new BusinessObjectModel())).isFalse();\n        assertThat(businessObjectValidationRule.appliesTo(new SimpleField())).isFalse();\n        assertThat(businessObjectValidationRule.appliesTo(new UniqueConstraint())).isFalse();\n\n        assertThat(businessObjectValidationRule.appliesTo(new BusinessObject())).isTrue();\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void shouddCheckRule_throw_IllegalArgumentException() {\n        businessObjectValidationRule.checkRule(new SimpleField());\n    }\n\n    @Test\n    public void should_validate_that_qualified_name_is_not_null() {\n        final BusinessObject bo = new BusinessObject();\n        bo.setQualifiedName(null);\n\n        final ValidationStatus validationStatus = businessObjectValidationRule.validate(bo);\n\n        assertThat(validationStatus).isNotOk();\n    }\n\n    @Test\n    public void should_validate_that_qualified_name_is_not_starting_by_org_bonitasoft() throws Exception {\n        checkQualifiedNameValidationStatus(\"com.bonitasoft.Forbidden\", false);\n\n        checkQualifiedNameValidationStatus(\"com.bonitasoftextended\", true);\n        checkQualifiedNameValidationStatus(\"org.com.bonitasoft.model\", true);\n        checkQualifiedNameValidationStatus(\"org.bonitasoft.model\", false);\n        checkQualifiedNameValidationStatus(\"org.bonitasoftextended\", true);\n    }\n\n    private void checkQualifiedNameValidationStatus(final String qualifiedName, final boolean expectedValidation) {\n        // given\n        final BusinessObject bo = aValidBusinesObject();\n        bo.setQualifiedName(qualifiedName);\n\n        // when\n        final ValidationStatus validationStatus = businessObjectValidationRule.validate(bo);\n\n        // then\n        if (expectedValidation) {\n            assertThat(validationStatus).isOk(\"should valid business object with qualified name:\" + qualifiedName);\n        } else {\n            assertThat(validationStatus)\n                    .isNotOk(\"should not valid business object with qualified name:\" + qualifiedName);\n        }\n    }\n\n    @Test\n    public void shouldCheckRule_returns_valid_status() {\n        final BusinessObject bo = aValidBusinesObject();\n\n        ValidationStatus validationStatus = businessObjectValidationRule.validate(bo);\n        assertThat(validationStatus).isOk();\n\n        bo.addUniqueConstraint(\"_UC_1\", \"firstName\");\n        validationStatus = businessObjectValidationRule.validate(bo);\n        assertThat(validationStatus).isOk();\n    }\n\n    private BusinessObject aValidBusinesObject() {\n        final BusinessObject bo = new BusinessObject();\n        bo.setQualifiedName(\"org.bonita.Bo\");\n\n        final SimpleField field = new SimpleField();\n        field.setName(\"firstName\");\n\n        bo.addField(field);\n        return bo;\n    }\n\n    @Test\n    public void shoudCheckRule_returns_error_status() {\n        final BusinessObject bo = new BusinessObject();\n        bo.setQualifiedName(\"org.bonita.Bo2\");\n        ValidationStatus validationStatus = businessObjectValidationRule.validate(bo);\n        assertThat(validationStatus).isNotOk();\n\n        bo.setQualifiedName(\"org.bonita.Bo 2\");\n        final SimpleField field = new SimpleField();\n        field.setName(\"firstName\");\n        bo.addField(field);\n        validationStatus = businessObjectValidationRule.validate(bo);\n        assertThat(validationStatus).isNotOk();\n\n        bo.setQualifiedName(\"org.bonita.Bo2\");\n        bo.addUniqueConstraint(\"_UC_1\", \"dontExists\");\n        validationStatus = businessObjectValidationRule.validate(bo);\n        assertThat(validationStatus).isNotOk();\n\n    }\n\n    @Test\n    public void shoudCheckRule_returns_error_status_for_duplicated_query_name() {\n        final BusinessObject bo = new BusinessObject();\n        bo.setQualifiedName(\"org.bonita.Bo2\");\n        final SimpleField field = new SimpleField();\n        field.setName(\"firstName\");\n        bo.addField(field);\n        bo.addQuery(\"toto\", \"titi\", List.class.getName());\n        bo.addQuery(\"toto\", \"titi\", List.class.getName());\n        final ValidationStatus validationStatus = businessObjectValidationRule.validate(bo);\n        assertThat(validationStatus).isNotOk();\n    }\n\n    @Test\n    public void shoudCheckRule_returns_error_status_for_duplicated_constraint_name() {\n        final BusinessObject bo = new BusinessObject();\n        bo.setQualifiedName(\"org.bonita.Bo2\");\n        final SimpleField field = new SimpleField();\n        field.setName(\"firstName\");\n        bo.addField(field);\n        bo.addUniqueConstraint(\"toto\", \"firstName\");\n        bo.addUniqueConstraint(\"toto\", \"firstName\");\n        final ValidationStatus validationStatus = businessObjectValidationRule.validate(bo);\n        assertThat(validationStatus).isNotOk();\n    }\n\n    @Test\n    public void should_validate_that_simple_name_contain_no_underscore() {\n        final BusinessObject businessObject = aBO(\"Name_withUnderscore\").withField(aBooleanField(\"field\")).build();\n\n        final ValidationStatus validationStatus = businessObjectValidationRule.validate(businessObject);\n\n        assertThat(validationStatus).isNotOk();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/validator/rule/FieldValidationRuleTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator.rule;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.bdm.validator.assertion.ValidationStatusAssert.assertThat;\n\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.model.UniqueConstraint;\nimport org.bonitasoft.engine.bdm.model.field.Field;\nimport org.bonitasoft.engine.bdm.validator.ValidationStatus;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Romain Bioteau\n */\npublic class FieldValidationRuleTest {\n\n    private FieldValidationRule fieldValidationRule;\n\n    @Before\n    public void setUp() {\n        fieldValidationRule = new FieldValidationRule();\n    }\n\n    private Field aFieldWithName(String name) {\n        FakeField field = new FakeField();\n        field.setName(name);\n        return field;\n    }\n\n    @Test\n    public void should_apply_to_fields() {\n        assertThat(fieldValidationRule.appliesTo(new BusinessObjectModel())).isFalse();\n        assertThat(fieldValidationRule.appliesTo(new BusinessObject())).isFalse();\n        assertThat(fieldValidationRule.appliesTo(new UniqueConstraint())).isFalse();\n\n        assertThat(fieldValidationRule.appliesTo(aFieldWithName(\"aName\"))).isTrue();\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void shouldCheckRule_throw_IllegalArgumentException() {\n        fieldValidationRule.checkRule(new BusinessObject());\n    }\n\n    @Test\n    public void should_return_a_valid_status_when_name_is_not_a_forbidden_one() {\n        Field field = aFieldWithName(\"aGoodName\");\n\n        ValidationStatus validationStatus = fieldValidationRule.validate(field);\n\n        assertThat(validationStatus).isOk();\n    }\n\n    @Test\n    public void should_validate_that_name_is_not_empty() {\n        Field field = aFieldWithName(\"\");\n\n        ValidationStatus validationStatus = fieldValidationRule.validate(field);\n\n        assertThat(validationStatus).isNotOk();\n    }\n\n    @Test\n    public void should_validate_that_name_is_not_null() {\n        Field field = aFieldWithName(null);\n\n        ValidationStatus validationStatus = fieldValidationRule.validate(field);\n\n        assertThat(validationStatus).isNotOk();\n    }\n\n    @Test\n    public void should_validate_that_name_has_no_whitespace() {\n        Field field = aFieldWithName(\"with whitespaces \");\n\n        ValidationStatus validationStatus = fieldValidationRule.validate(field);\n\n        assertThat(validationStatus).isNotOk();\n    }\n\n    @Test\n    public void should_validate_that_name_is_not_a_java_keyword() {\n        Field field = aFieldWithName(\"import\");\n\n        ValidationStatus validationStatus = fieldValidationRule.validate(field);\n\n        assertThat(validationStatus).isNotOk();\n    }\n\n    @Test\n    public void should_validate_that_name_is_not_persistenceId_wich_is_a_business_data_model_keyword() {\n        Field field = aFieldWithName(Field.PERSISTENCE_ID.toUpperCase());\n\n        ValidationStatus validationStatus = fieldValidationRule.validate(field);\n\n        assertThat(validationStatus).isNotOk();\n    }\n\n    @Test\n    public void should_validate_that_name_is_not_persistenceVersion_wich_is_a_business_data_model_keyword() {\n        Field field = aFieldWithName(Field.PERSISTENCE_VERSION.toLowerCase());\n\n        ValidationStatus validationStatus = fieldValidationRule.validate(field);\n\n        assertThat(validationStatus).isNotOk();\n    }\n\n    /**\n     * Fake field used for tests. Extending abstract class Field with no extras attributes\n     *\n     * @author Colin PUY\n     */\n    private class FakeField extends Field {\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/validator/rule/IndexValidationRuleTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator.rule;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.bdm.builder.IndexBuilder.anIndex;\n\nimport org.bonitasoft.engine.bdm.model.Index;\nimport org.bonitasoft.engine.bdm.validator.ValidationStatus;\nimport org.junit.Test;\n\npublic class IndexValidationRuleTest {\n\n    @Test\n    public void should_add_an_error_message_when_index_fields_are_null() throws Exception {\n        final Index index = anIndex().withName(\"nameIndex\").build();\n\n        ValidationStatus status = new IndexValidationRule().checkRule(index);\n\n        assertThat(status.getErrors()).contains(\"nameIndex index must have at least one field declared\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/validator/rule/MultipleAggregationToItselfValidationRuleTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator.rule;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.bdm.builder.BusinessObjectBuilder.aBO;\nimport static org.bonitasoft.engine.bdm.builder.BusinessObjectModelBuilder.aBOM;\nimport static org.bonitasoft.engine.bdm.model.field.RelationField.FetchType.EAGER;\n\nimport org.bonitasoft.engine.bdm.builder.FieldBuilder;\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.validator.ValidationStatus;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Danila Mazour\n */\npublic class MultipleAggregationToItselfValidationRuleTest {\n\n    private MultipleAggregationToItselfValidationRule rule;\n\n    @Before\n    public void setUp() {\n        rule = new MultipleAggregationToItselfValidationRule();\n    }\n\n    @Test\n    public void shouldDetectMultipleAggregationToItself() {\n        BusinessObject daughter = aBO(\"daughter\").build();\n        daughter = aBO(\"daughter\")\n                .withField(new FieldBuilder.RelationFieldBuilder().withName(\"daughter\").aggregation().multiple()\n                        .referencing(daughter).fetchType(\n                                EAGER))\n                .build();\n\n        final BusinessObjectModel bom = aBOM().withBOs(daughter).build();\n        final ValidationStatus validationStatus = rule.validate(bom);\n\n        assertThat(validationStatus.isOk()).isFalse();\n        assertThat(validationStatus.getErrors()).hasSize(1);\n        assertThat(validationStatus.getWarnings()).hasSize(0);\n        assertThat(validationStatus.getErrors().get(0))\n                .isEqualTo(\"The object daughter is referencing itself in a multiple aggregation relation.\");\n\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/validator/rule/QueryParameterValidationRuleTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator.rule;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.bdm.model.QueryParameter;\nimport org.bonitasoft.engine.bdm.validator.ValidationStatus;\nimport org.junit.Test;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class QueryParameterValidationRuleTest {\n\n    @Test\n    public void checkRuleShouldForbidStartIndexAsQueryParameterName() {\n        QueryParameter queryParam = new QueryParameter(\"startIndex\", Object.class.getName());\n\n        ValidationStatus status = new QueryParameterValidationRule().validate(queryParam);\n\n        assertThat(status.getErrors().get(0)).contains(\"is a reserved parameter name\");\n    }\n\n    @Test\n    public void checkRuleShouldForbidMaxResultsAsQueryParameterName() {\n        QueryParameter queryParam = new QueryParameter(\"maxResults\", Object.class.getName());\n\n        ValidationStatus status = new QueryParameterValidationRule().validate(queryParam);\n\n        assertThat(status.getErrors()).hasSize(1);\n        assertThat(status.getErrors().get(0)).contains(\"is a reserved parameter name\");\n    }\n\n    @Test\n    public void aQueryParameterNameShouldHaveAName() {\n        QueryParameter queryParam = new QueryParameter();\n\n        ValidationStatus status = new QueryParameterValidationRule().validate(queryParam);\n\n        assertThat(status.getErrors().get(0)).contains(\"must have name\");\n    }\n\n    @Test\n    public void aQueryParameterNameShouldHaveANonEmptyName() {\n        QueryParameter queryParam = new QueryParameter(\"\", Object.class.getName());\n\n        ValidationStatus status = new QueryParameterValidationRule().validate(queryParam);\n\n        assertThat(status.getErrors().get(0)).contains(\"must have name\");\n    }\n\n    @Test\n    public void aQueryParameterNameShouldHaveAVAlidJavaIdentifierName() {\n        QueryParameter queryParam = new QueryParameter(\"1_manu\", Object.class.getName());\n\n        ValidationStatus status = new QueryParameterValidationRule().validate(queryParam);\n\n        assertThat(status.getErrors().get(0)).contains(\"is not a valid Java identifier\");\n    }\n\n    @Test\n    public void aQueryParameterClassNameShouldHaveAName() {\n        QueryParameter queryParam = new QueryParameter(\"aValidParamName\", null);\n\n        ValidationStatus status = new QueryParameterValidationRule().validate(queryParam);\n\n        assertThat(status.getErrors().get(0)).contains(\"query parameter must have a classname\");\n    }\n\n    @Test\n    public void aQueryParameterClassNameShouldHaveANonEmptyName() {\n        QueryParameter queryParam = new QueryParameter(\"aValidParamName\", \"\");\n\n        ValidationStatus status = new QueryParameterValidationRule().validate(queryParam);\n\n        assertThat(status.getErrors().get(0)).contains(\"query parameter must have a classname\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/validator/rule/QueryValidationRuleTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator.rule;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.bdm.validator.assertion.ValidationStatusAssert.assertThat;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.model.Query;\nimport org.bonitasoft.engine.bdm.model.UniqueConstraint;\nimport org.bonitasoft.engine.bdm.model.field.SimpleField;\nimport org.bonitasoft.engine.bdm.validator.ValidationStatus;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Romain Bioteau\n */\npublic class QueryValidationRuleTest {\n\n    private QueryValidationRule queryValidationRule;\n\n    /**\n     * @throws java.lang.Exception\n     */\n    @Before\n    public void setUp() {\n        queryValidationRule = new QueryValidationRule();\n    }\n\n    @Test\n    public void shouldAppliesTo_UniqueConstraint() {\n        assertThat(queryValidationRule.appliesTo(new BusinessObjectModel())).isFalse();\n        assertThat(queryValidationRule.appliesTo(new BusinessObject())).isFalse();\n        assertThat(queryValidationRule.appliesTo(new SimpleField())).isFalse();\n        assertThat(queryValidationRule.appliesTo(new UniqueConstraint())).isFalse();\n        assertThat(queryValidationRule.appliesTo(new Query())).isTrue();\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void shouldCheckRule_throw_IllegalArgumentException() {\n        queryValidationRule.checkRule(new BusinessObject());\n    }\n\n    @Test\n    public void should_validate_that_query_name_is_a_valid_java_identifier() {\n        Query query = new Query();\n        query.setName(\" a not valid java identifier\");\n\n        ValidationStatus validationStatus = queryValidationRule.validate(query);\n\n        assertThat(validationStatus).isNotOk();\n    }\n\n    @Test\n    public void shouldCheckRule_return_valid_status() {\n        Query q = new Query(\"findByName\", \"Select toto where titi = toto\", List.class.getName());\n        ValidationStatus checkRule = queryValidationRule.validate(q);\n        assertThat(checkRule.isOk()).isTrue();\n    }\n\n    @Test\n    public void shouldCheckRule_return_error_status_if_no_name() {\n        Query q = new Query(\"\", \"Select toto where titi = toto\", List.class.getName());\n        ValidationStatus checkRule = queryValidationRule.validate(q);\n        assertThat(checkRule.isOk()).isFalse();\n\n        q = new Query(null, \"Select toto where titi = toto\", List.class.getName());\n        checkRule = queryValidationRule.validate(q);\n        assertThat(checkRule.isOk()).isFalse();\n    }\n\n    @Test\n    public void shouldCheckRule_return_error_status_if_name_too_long() {\n        Query q = new Query(\n                \"dsfhsdjkhfdjskfhjksdhfjksdhfjkshfjksdhfjksdhfjksdhfjksdhfjkdsfhjkdshfjjkdshfjskdhfjksdhfdjskhfhfjkhsdfkjsdhfksduzeiryzueiryuzieryuigsdhjgfhjgriuzegrjkg\",\n                \"Select toto where titi = toto\", List.class.getName());\n        ValidationStatus checkRule = queryValidationRule.validate(q);\n        assertThat(checkRule.isOk()).isFalse();\n    }\n\n    @Test\n    public void shouldCheckRule_return_error_status_if_no_content() {\n        Query q = new Query(\"toto\", \"\", List.class.getName());\n        ValidationStatus checkRule = queryValidationRule.validate(q);\n        assertThat(checkRule.isOk()).isFalse();\n\n        q = new Query(\"toto\", null, List.class.getName());\n        checkRule = queryValidationRule.validate(q);\n        assertThat(checkRule.isOk()).isFalse();\n    }\n\n    @Test\n    public void shouldCheckRule_return_error_status_if_no_returnType() {\n        Query q = new Query(\"toto\", \"select\", \"\");\n        ValidationStatus checkRule = queryValidationRule.validate(q);\n        assertThat(checkRule.isOk()).isFalse();\n\n        q = new Query(\"toto\", \"select\", \"\");\n        checkRule = queryValidationRule.validate(q);\n        assertThat(checkRule.isOk()).isFalse();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/validator/rule/SimpleFieldValidationRuleTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator.rule;\n\nimport static org.bonitasoft.engine.bdm.validator.assertion.ValidationStatusAssert.assertThat;\n\nimport org.bonitasoft.engine.bdm.model.field.FieldType;\nimport org.bonitasoft.engine.bdm.model.field.SimpleField;\nimport org.bonitasoft.engine.bdm.validator.ValidationStatus;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Colin PUY\n */\npublic class SimpleFieldValidationRuleTest {\n\n    private SimpleFieldValidationRule simpleFieldValidationRule;\n\n    @Before\n    public void setUp() {\n        simpleFieldValidationRule = new SimpleFieldValidationRule();\n    }\n\n    @Test\n    public void should_validate_that_type_is_not_empty() {\n        SimpleField simpleField = new SimpleField();\n\n        ValidationStatus validationStatus = simpleFieldValidationRule.validate(simpleField);\n\n        assertThat(validationStatus).isNotOk();\n    }\n\n    @Test\n    public void should_return_a_valid_status_when_type_is_filled() {\n        SimpleField simpleField = new SimpleField();\n        simpleField.setType(FieldType.BOOLEAN);\n\n        ValidationStatus validationStatus = simpleFieldValidationRule.validate(simpleField);\n\n        assertThat(validationStatus).isOk();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/validator/rule/UniqueConstraintValidationRuleTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator.rule;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.bdm.validator.assertion.ValidationStatusAssert.assertThat;\n\nimport java.util.Arrays;\n\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.model.UniqueConstraint;\nimport org.bonitasoft.engine.bdm.model.field.SimpleField;\nimport org.bonitasoft.engine.bdm.validator.ValidationStatus;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Romain Bioteau\n */\npublic class UniqueConstraintValidationRuleTest {\n\n    private UniqueConstraintValidationRule uniqueConstraintValidationRule;\n\n    @Before\n    public void setUp() {\n        uniqueConstraintValidationRule = new UniqueConstraintValidationRule();\n    }\n\n    @Test\n    public void shoudAppliesTo_UniqueConstraint() {\n        assertThat(uniqueConstraintValidationRule.appliesTo(new BusinessObjectModel())).isFalse();\n        assertThat(uniqueConstraintValidationRule.appliesTo(new BusinessObject())).isFalse();\n        assertThat(uniqueConstraintValidationRule.appliesTo(new SimpleField())).isFalse();\n        assertThat(uniqueConstraintValidationRule.appliesTo(new UniqueConstraint())).isTrue();\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void shouddCheckRule_throw_IllegalArgumentException() {\n        uniqueConstraintValidationRule.checkRule(new BusinessObject());\n    }\n\n    @Test\n    public void should_return_a_valid_status() {\n        UniqueConstraint uc = new UniqueConstraint();\n        uc.setName(\"MY_CONSTRAINT_\");\n        uc.setFieldNames(Arrays.asList(\"f1\"));\n        ValidationStatus validationStatus = uniqueConstraintValidationRule.validate(uc);\n        assertThat(validationStatus).isOk();\n    }\n\n    @Test\n    public void should_add_an_error_message_when_constraint_name_is_null() throws Exception {\n        UniqueConstraint uc = new UniqueConstraint();\n        uc.setName(null);\n        uc.setFieldNames(Arrays.asList(\"f1\"));\n\n        ValidationStatus validationStatus = uniqueConstraintValidationRule.validate(uc);\n\n        assertThat(validationStatus).isNotOk().hasError(\"A unique constraint must have name\");\n    }\n\n    @Test\n    public void should_add_an_error_message_when_constraint_name_contains_whitespaces() throws Exception {\n        UniqueConstraint uc = new UniqueConstraint();\n        uc.setName(\"with whitespaces\");\n        uc.setFieldNames(Arrays.asList(\"f1\"));\n\n        ValidationStatus validationStatus = uniqueConstraintValidationRule.validate(uc);\n\n        assertThat(validationStatus).isNotOk().hasError(\"with whitespaces is not a valid SQL identifier\");\n    }\n\n    @Test\n    public void should_add_an_error_message_when_constraint_name_is_empty() throws Exception {\n        UniqueConstraint uc = new UniqueConstraint();\n        uc.setName(\"\");\n        uc.setFieldNames(Arrays.asList(\"f1\"));\n\n        ValidationStatus validationStatus = uniqueConstraintValidationRule.validate(uc);\n\n        assertThat(validationStatus).isNotOk().hasError(\"A unique constraint must have name\");\n    }\n\n    @Test\n    public void should_add_an_error_message_when_constraint_fields_are_null() throws Exception {\n        UniqueConstraint uc = new UniqueConstraint();\n        uc.setName(\"MY_CONSTRAINT_\");\n        uc.setFieldNames(null);\n\n        ValidationStatus validationStatus = uniqueConstraintValidationRule.validate(uc);\n\n        assertThat(validationStatus).isNotOk()\n                .hasError(\"MY_CONSTRAINT_ unique constraint must have at least one field declared\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/validator/rule/UniqueNameValidationRuleTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator.rule;\n\nimport static java.util.Arrays.asList;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.bdm.builder.BusinessObjectBuilder.aBO;\nimport static org.bonitasoft.engine.bdm.builder.BusinessObjectModelBuilder.aBOM;\nimport static org.bonitasoft.engine.bdm.builder.IndexBuilder.anIndex;\nimport static org.bonitasoft.engine.bdm.builder.UniqueConstraintBuilder.aUniqueConstraint;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport java.util.Collection;\n\nimport org.bonitasoft.engine.api.result.StatusCode;\nimport org.bonitasoft.engine.bdm.builder.BusinessObjectBuilder;\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.model.Index;\nimport org.bonitasoft.engine.bdm.model.UniqueConstraint;\nimport org.bonitasoft.engine.bdm.validator.UniqueNameValidator;\nimport org.bonitasoft.engine.bdm.validator.ValidationStatus;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Colin PUY\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class UniqueNameValidationRuleTest {\n\n    @Mock\n    private UniqueNameValidator uniqueNameValidator;\n\n    private UniqueNameValidationRule validationRule;\n\n    @Before\n    @SuppressWarnings(\"unchecked\")\n    public void initValidationRule() {\n        when(uniqueNameValidator.validate(any(Collection.class), any(String.class))).thenReturn(new ValidationStatus());\n        validationRule = new UniqueNameValidationRule(uniqueNameValidator);\n    }\n\n    private BusinessObject aBoWithConstraints(final UniqueConstraint... uniqueConstraints) {\n        BusinessObjectBuilder boBuilder = aBO(\"aBoWithConstraints\");\n        for (final UniqueConstraint uniqueConstraint : uniqueConstraints) {\n            boBuilder = boBuilder.withUniqueConstraint(uniqueConstraint);\n        }\n        return boBuilder.build();\n    }\n\n    private BusinessObject aBoWithIndexes(final Index... indexes) {\n        BusinessObjectBuilder bo = aBO(\"aBoWithIndexes\");\n        for (final Index index : indexes) {\n            bo = bo.withIndex(index);\n        }\n        return bo.build();\n    }\n\n    private ValidationStatus anErrorStatus() {\n        final ValidationStatus validationStatus = new ValidationStatus();\n        validationStatus.addError(StatusCode.DUPLICATE_CONSTRAINT_OR_INDEX_NAME, \"an error\");\n        return validationStatus;\n    }\n\n    @Test\n    public void should_validate_names_unicity_for_unique_constraints() {\n        final UniqueConstraint uniqueConstraint = aUniqueConstraint().withName(\"aUniqueConstraint\").build();\n        final UniqueConstraint uniqueConstraint2 = aUniqueConstraint().withName(\"anotherUniqueConstraint\").build();\n        final BusinessObject bo = aBoWithConstraints(uniqueConstraint, uniqueConstraint2);\n\n        validationRule.checkRule(aBOM().withBO(bo).build());\n\n        verify(uniqueNameValidator).validate(asList(uniqueConstraint, uniqueConstraint2), \"unique contraints\");\n    }\n\n    @Test\n    public void should_validate_names_unicity_for_indexes() {\n        final Index index = anIndex().withName(\"anIndex\").build();\n        final Index anotherIndex = anIndex().withName(\"anotherIndex\").build();\n        final BusinessObjectModel bom = aBOM().withBO(aBoWithIndexes(index, anotherIndex)).build();\n\n        validationRule.checkRule(bom);\n\n        verify(uniqueNameValidator).validate(asList(index, anotherIndex), \"indexes\");\n    }\n\n    @Test\n    public void should_concatenate_validation_errors() {\n        final Index index = anIndex().withName(\"index\").build();\n        final UniqueConstraint uniqueConstraint = aUniqueConstraint().withName(\"constraint\").build();\n        final BusinessObjectModel bom = aBOM()\n                .withBO(aBO(\"bo\").withIndex(index).withUniqueConstraint(uniqueConstraint).build()).build();\n        when(uniqueNameValidator.validate(eq(asList(index)), anyString())).thenReturn(anErrorStatus());\n        when(uniqueNameValidator.validate(eq(asList(uniqueConstraint)), anyString())).thenReturn(anErrorStatus());\n\n        final ValidationStatus checkRule = validationRule.checkRule(bom);\n\n        assertThat(checkRule.getErrors()).hasSize(2);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/validator/rule/UniqueSimpleNameValidationRuleTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator.rule;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.api.result.Status.Level.ERROR;\n\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.validator.ValidationStatus;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Danila Mazour\n */\npublic class UniqueSimpleNameValidationRuleTest {\n\n    private UniqueSimpleNameValidationRule uniqueSimpleNameValidationRule;\n\n    private BusinessObject customerPackageA;\n\n    private BusinessObject customerPackageB;\n\n    private BusinessObject customerUpperCase;\n\n    private BusinessObject client;\n\n    @Before\n    public void setUp() {\n        uniqueSimpleNameValidationRule = new UniqueSimpleNameValidationRule();\n        createObjects();\n    }\n\n    private void createObjects() {\n        customerPackageA = new BusinessObject();\n        customerPackageB = new BusinessObject();\n        customerUpperCase = new BusinessObject();\n        client = new BusinessObject();\n        customerUpperCase.setQualifiedName(\"net.company.model.CUSTOMER\");\n        customerPackageA.setQualifiedName(\"com.company.model.Customer\");\n        customerPackageB.setQualifiedName(\"net.company.model.Customer\");\n        client.setQualifiedName(\"org.company.model.Client\");\n    }\n\n    @Test\n    public void should_return_error_when_same_business_object_name() {\n        //given\n        BusinessObjectModel bom = new BusinessObjectModel();\n        bom.addBusinessObject(customerPackageA);\n        bom.addBusinessObject(customerPackageB);\n\n        //when\n        ValidationStatus validationStatus = uniqueSimpleNameValidationRule.validate(bom);\n\n        //then\n        assertThat(validationStatus.getStatuses().size()).isEqualTo(1);\n        assertThat(validationStatus.getStatuses().get(0).getLevel()).isEqualTo(ERROR);\n\n    }\n\n    @Test\n    public void should_return_error_when_same_business_object_name_but_upperCase() {\n        //given\n        BusinessObjectModel bom = new BusinessObjectModel();\n        bom.addBusinessObject(customerPackageA);\n        bom.addBusinessObject(customerUpperCase);\n\n        //when\n        ValidationStatus validationStatus = uniqueSimpleNameValidationRule.validate(bom);\n\n        //then\n        assertThat(validationStatus.getStatuses().size()).isEqualTo(1);\n        assertThat(validationStatus.getStatuses().get(0).getLevel()).isEqualTo(ERROR);\n\n    }\n\n    @Test\n    public void should_return_a_valid_status_when_no_identical_object_name() {\n        //given\n        BusinessObjectModel bom = new BusinessObjectModel();\n        bom.addBusinessObject(customerPackageA);\n        bom.addBusinessObject(client);\n\n        //when\n        ValidationStatus validationStatus = uniqueSimpleNameValidationRule.validate(bom);\n\n        //then\n        assertThat(validationStatus.getStatuses()).isEmpty();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/validator/rule/ValidationRuleTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator.rule;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.bdm.validator.ValidationStatus;\nimport org.junit.Test;\n\n/**\n * @author Colin PUY\n */\npublic class ValidationRuleTest {\n\n    @Test\n    public void should_apply_to_type_parameter_class() {\n        ExceptionRule objectRule = new ExceptionRule();\n\n        boolean apply = objectRule.appliesTo(new Exception());\n\n        assertThat(apply).isTrue();\n    }\n\n    @Test\n    public void should_apply_to_type_parameter_subclass() {\n        ExceptionRule objectRule = new ExceptionRule();\n\n        boolean apply = objectRule.appliesTo(new RuntimeException());\n\n        assertThat(apply).isTrue();\n    }\n\n    @Test\n    public void should_not_apply_to_other_type() {\n        ExceptionRule objectRule = new ExceptionRule();\n\n        boolean apply = objectRule.appliesTo(new Throwable());\n\n        assertThat(apply).isFalse();\n    }\n\n    @Test\n    public void should_not_apply_to_null() {\n        ExceptionRule objectRule = new ExceptionRule();\n\n        boolean apply = objectRule.appliesTo(null);\n\n        assertThat(apply).isFalse();\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void should_throw_exception_when_trying_to_check_rule_on_object_that_rule_cannot_apply_on() {\n        ExceptionRule objectRule = new ExceptionRule();\n\n        objectRule.checkRule(new String());\n    }\n\n    @Test\n    public void should_validate_object_according_to_the_implemented_validation_strategy() {\n        ValidationStatus expectedValidationStatus = new ValidationStatus();\n        ExceptionRule objectRule = new ExceptionRule(expectedValidationStatus);\n\n        ValidationStatus status = objectRule.checkRule(new Exception());\n\n        assertThat(status).isEqualTo(expectedValidationStatus);\n    }\n\n    /**\n     * ValidationRule test implementation - return the given validationStatus\n     *\n     * @author Colin PUY\n     */\n    private class ExceptionRule extends ValidationRule<Exception, ValidationStatus> {\n\n        private ValidationStatus validationStatus;\n\n        public ExceptionRule() {\n            super(Exception.class);\n        }\n\n        public ExceptionRule(ValidationStatus validationStatus) {\n            this();\n            this.validationStatus = validationStatus;\n        }\n\n        @Override\n        protected ValidationStatus validate(Exception modelElement) {\n            return validationStatus;\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/validator/rule/composition/AggregationAndCompositionValidationRuleTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator.rule.composition;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.bdm.builder.BusinessObjectBuilder.aBO;\nimport static org.bonitasoft.engine.bdm.builder.BusinessObjectModelBuilder.aBOM;\nimport static org.bonitasoft.engine.bdm.builder.FieldBuilder.aCompositionField;\nimport static org.bonitasoft.engine.bdm.builder.FieldBuilder.anAggregationField;\n\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.validator.ValidationStatus;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Danila Mazour\n */\npublic class AggregationAndCompositionValidationRuleTest {\n\n    private AggregationAndCompositionValidationRule rule;\n\n    @Before\n    public void setUp() {\n        rule = new AggregationAndCompositionValidationRule();\n    }\n\n    @Test\n    public void shouldDetectAggregationAndComposition() {\n\n        final BusinessObject daughter = aBO(\"daughter\").build();\n        final BusinessObject mother = aBO(\"mother\").withField(aCompositionField(\"daughter\", daughter)).build();\n        final BusinessObject grandMother = aBO(\"grandMother\").withField(anAggregationField(\"daughter\", daughter))\n                .build();\n        final BusinessObjectModel bom = aBOM().withBOs(grandMother, mother, daughter).build();\n        final ValidationStatus validationStatus = rule.validate(bom);\n\n        assertThat(validationStatus.isOk()).isTrue();\n        assertThat(validationStatus.getErrors()).isEmpty();\n        assertThat(validationStatus.getWarnings().size()).isEqualTo(1);\n        assertThat(validationStatus.getWarnings().get(0)).isEqualTo(\n                \"The object daughter is referenced both in composition and in aggregation. This may lead to runtime errors and may lead to unpredictable behaviour of the AccessControl configuration.\");\n\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/validator/rule/composition/CyclicCompositionValidationRuleTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator.rule.composition;\n\nimport static org.bonitasoft.engine.bdm.builder.BusinessObjectBuilder.aBO;\nimport static org.bonitasoft.engine.bdm.builder.BusinessObjectModelBuilder.aBOM;\nimport static org.bonitasoft.engine.bdm.builder.FieldBuilder.aCompositionField;\nimport static org.bonitasoft.engine.bdm.validator.assertion.ValidationStatusAssert.assertThat;\n\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.validator.ValidationStatus;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Colin PUY\n */\npublic class CyclicCompositionValidationRuleTest {\n\n    private CyclicCompositionValidationRule rule;\n\n    @Before\n    public void initRule() {\n        rule = new CyclicCompositionValidationRule();\n    }\n\n    @Test\n    public void should_validate_that_a_composite_object_cannot_have_one_of_its_ancestor_as_a_child() {\n        final BusinessObject daughter = aBO(\"daughter\").build();\n        final BusinessObject mother = aBO(\"mother\").withField(aCompositionField(\"daughter\", daughter)).build();\n        final BusinessObject grandMother = aBO(\"grandMother\").withField(aCompositionField(\"mother\", mother)).build();\n\n        daughter.addField(aCompositionField(\"forbiddenChild\", grandMother));\n        final BusinessObjectModel bom = aBOM().withBOs(grandMother, mother, daughter).build();\n\n        final ValidationStatus validationStatus = rule.validate(bom);\n\n        assertThat(validationStatus).isNotOk();\n    }\n\n    @Test\n    public void should_validate_that_a_bo_cannot_compose_itself() {\n        final BusinessObject daughter = aBO(\"daughter\").build();\n        daughter.addField(aCompositionField(\"toto\", daughter));\n        final BusinessObjectModel bom = aBOM().withBOs(daughter).build();\n\n        final ValidationStatus validationStatus = rule.validate(bom);\n\n        assertThat(validationStatus).isNotOk();\n    }\n\n    @Test\n    public void should_validate_that_a_bom_with_the_same_bo_composed_two_times_is_invalid() {\n        //given\n        final BusinessObject wheel = aBO(\"wheel\").build();\n        final BusinessObject bycicle = aBO(\"bicycle\").withField(aCompositionField(\"frontwheel\", wheel))\n                .withField(aCompositionField(\"backwheel\", wheel)).build();\n        final BusinessObjectModel bom = aBOM().withBOs(wheel, bycicle).build();\n        //when\n        ValidationStatus validationStatus = rule.validate(bom);\n        //then\n        assertThat(validationStatus).isNotOk();\n        assertThat(validationStatus).hasError(\"Business object \" + \"wheel\"\n                + \" has a circular composition reference to itself or is referenced several times in the object \"\n                + \"bicycle\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/validator/rule/composition/UniquenessCompositionValidationRuleTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator.rule.composition;\n\nimport static org.bonitasoft.engine.bdm.builder.BusinessObjectBuilder.aBO;\nimport static org.bonitasoft.engine.bdm.builder.BusinessObjectModelBuilder.aBOM;\nimport static org.bonitasoft.engine.bdm.builder.FieldBuilder.aCompositionField;\nimport static org.bonitasoft.engine.bdm.builder.FieldBuilder.anAggregationField;\nimport static org.bonitasoft.engine.bdm.validator.assertion.ValidationStatusAssert.assertThat;\n\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.validator.ValidationStatus;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Colin PUY\n */\npublic class UniquenessCompositionValidationRuleTest {\n\n    private UniquenessCompositionValidationRule rule;\n\n    @Before\n    public void initRule() {\n        rule = new UniquenessCompositionValidationRule();\n    }\n\n    @Test\n    public void should_validate_that_a_bom_with_no_relation_fields_is_valid() {\n        final BusinessObjectModel bom = aBOM().withBO(aBO(\"aBo\").build()).build();\n\n        final ValidationStatus validationStatus = rule.validate(bom);\n\n        assertThat(validationStatus).isOk();\n    }\n\n    @Test\n    public void should_validate_that_a_bom_with_no_composition_is_valid() {\n        final BusinessObject aggregated = aBO(\"aggregated\").build();\n        final BusinessObject bo = aBO(\"aBo\").withField(anAggregationField(\"aggreg\", aggregated)).build();\n        final BusinessObjectModel bom = aBOM().withBOs(bo, aggregated).build();\n\n        final ValidationStatus validationStatus = rule.validate(bom);\n\n        assertThat(validationStatus).isOk();\n    }\n\n    @Test\n    public void should_validate_that_a_bom_with_composed_bo_in_only_one_bo_is_valid() {\n        final BusinessObject composite = aBO(\"composite\").build();\n        final BusinessObject bo = aBO(\"aBo\").withField(aCompositionField(\"composite\", composite)).build();\n        final BusinessObject composite2 = aBO(\"composite2\").build();\n        final BusinessObject bo2 = aBO(\"aBo2\").withField(aCompositionField(\"composite2\", composite2)).build();\n        final BusinessObjectModel bom = aBOM().withBOs(bo, composite, bo2, composite2).build();\n\n        final ValidationStatus validationStatus = rule.validate(bom);\n\n        assertThat(validationStatus).isOk();\n    }\n\n    @Test\n    public void should_validate_that_a_bom_with_a_bo_composed_in_two_bos_is_invalid() {\n        final BusinessObject composite = aBO(\"composite\").build();\n        final BusinessObject firstBO = aBO(\"firstBO\").withField(aCompositionField(\"boOneComposite\", composite)).build();\n        final BusinessObject secondBO = aBO(\"secondBO\").withField(aCompositionField(\"boTwoComposite\", composite))\n                .build();\n        final BusinessObjectModel bom = aBOM().withBOs(firstBO, secondBO, composite).build();\n\n        final ValidationStatus validationStatus = rule.validate(bom);\n\n        assertThat(validationStatus).isNotOk();\n    }\n\n    @Test\n    public void should_validate_that_a_bom_with_the_same_bo_composed_two_times_is_invalid() {\n        //given\n        final BusinessObject wheel = aBO(\"wheel\").build();\n        final BusinessObject bycicle = aBO(\"bicycle\").withField(aCompositionField(\"frontwheel\", wheel))\n                .withField(aCompositionField(\"backwheel\", wheel)).build();\n        final BusinessObjectModel bom = aBOM().withBOs(wheel, bycicle).build();\n        //when\n        ValidationStatus validationStatus = rule.validate(bom);\n        //then\n        assertThat(validationStatus).isNotOk();\n        assertThat(validationStatus).hasError(\"Business object \" + \"wheel\"\n                + \" is referenced by composition in two business objects, or is referenced several times in a single business object\");\n        assertThat(validationStatus).hasErrorSize(1);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/bpm/document/DocumentValueTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.document;\n\nimport static org.junit.Assert.assertEquals;\n\nimport org.junit.Test;\n\n/**\n * @author Celine Souchet\n * @version 6.4.2\n * @since 6.4.2\n */\npublic class DocumentValueTest {\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.bpm.document.DocumentValue#DocumentValue(byte[], java.lang.String, java.lang.String)}.\n     */\n    @Test(expected = IllegalArgumentException.class)\n    public final void cant_construct_DocumentValue_with_content_and_mimeType_without_file_name() {\n        new DocumentValue(\"content\".getBytes(), \"mimeType\", null);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.bpm.document.DocumentValue#DocumentValue(byte[], java.lang.String, java.lang.String)}.\n     */\n    @Test(expected = IllegalArgumentException.class)\n    public final void cant_construct_DocumentValue_with_content_and_mimeType_with_empty_file_name() {\n        new DocumentValue(\"content\".getBytes(), \"mimeType\", \"\");\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.bpm.document.DocumentValue#DocumentValue(byte[], java.lang.String, java.lang.String)}.\n     */\n    @Test\n    public final void should_be_able_to_construct_DocumentValue_without_content_but_with_mimeType_and_file_name() {\n        new DocumentValue(null, \"mimeType\", \"filename\");\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.bpm.document.DocumentValue#DocumentValue(byte[], java.lang.String, java.lang.String)}.\n     */\n    @Test\n    public final void should_be_able_to_construct_DocumentValue_with_empty_content_with_mimeType_and_file_name() {\n        new DocumentValue(\"\".getBytes(), \"mimeType\", \"filename\");\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.bpm.document.DocumentValue#DocumentValue(byte[], java.lang.String, java.lang.String)}.\n     */\n    @Test\n    public final void can_construct_DocumentValue_with_content_and_mimeType_and_file_name() {\n        new DocumentValue(\"tyjiy\".getBytes(), \"mimeType\", \"filename\");\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.bpm.document.DocumentValue#DocumentValue(long, byte[], java.lang.String, java.lang.String)}.\n     */\n    @Test(expected = IllegalArgumentException.class)\n    public final void cant_construct_DocumentValue_with_documentId_and_content_and_mimeType_without_file_name() {\n        new DocumentValue(1, \"content\".getBytes(), \"mimeType\", null);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.bpm.document.DocumentValue#DocumentValue(long, byte[], java.lang.String, java.lang.String)}.\n     */\n    @Test(expected = IllegalArgumentException.class)\n    public final void cant_construct_DocumentValue_with_documentId_and_content_and_mimeType_with_empty_file_name() {\n        new DocumentValue(1, \"content\".getBytes(), \"mimeType\", \"\");\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.bpm.document.DocumentValue#DocumentValue(long, byte[], java.lang.String, java.lang.String)}.\n     */\n    @Test\n    public final void should_be_able_to_construct_DocumentValue_without_content_with_documentId_and_mimeType_and_file_name() {\n        new DocumentValue(1, null, \"mimeType\", \"filename\");\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.bpm.document.DocumentValue#DocumentValue(long, byte[], java.lang.String, java.lang.String)}.\n     */\n    @Test\n    public final void should_be_able_to_construct_DocumentValue_with_empty_content_with_documentId_and_mimeType_and_file_name() {\n        new DocumentValue(1, \"\".getBytes(), \"mimeType\", \"filename\");\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.bpm.document.DocumentValue#DocumentValue(long, byte[], java.lang.String, java.lang.String)}.\n     */\n    @Test\n    public final void can_construct_DocumentValue_with_content_and_documentId_and_mimeType_and_file_name() {\n        new DocumentValue(1, \"plop\".getBytes(), \"mimeType\", \"filename\");\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.bpm.document.DocumentValue#setContent(byte[])}.\n     */\n    @Test(expected = IllegalArgumentException.class)\n    public final void setContent_should_throw_exception_if_filename_is_empty_and_content_not() {\n        // Given\n        final DocumentValue documentValue = new DocumentValue(2);\n\n        // When\n        documentValue.setContent(\"plop\".getBytes());\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.bpm.document.DocumentValue#setContent(byte[])}.\n     */\n    @Test(expected = IllegalArgumentException.class)\n    public final void setContent_should_throw_exception_if_filename_is_null_and_content_not() {\n        // Given\n        final DocumentValue documentValue = new DocumentValue(2);\n\n        // When\n        documentValue.setContent(\"plop\".getBytes());\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.bpm.document.DocumentValue#setContent(byte[])}.\n     */\n    @Test\n    public final void setContent_should_set_content_with_filename_and_content() {\n        // Given\n        final DocumentValue documentValue = new DocumentValue(\"yujyt\".getBytes(), \"mimeType\", \"filename\");\n        final byte[] content = \"plop\".getBytes();\n\n        // When\n        documentValue.setContent(content);\n\n        // Then\n        assertEquals(content, documentValue.getContent());\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.bpm.document.DocumentValue#setFileName(java.lang.String)}.\n     */\n    @Test(expected = IllegalArgumentException.class)\n    public final void setFileName_should_throw_exception_if_filename_is_empty_and_content_not() {\n        // Given\n        final DocumentValue documentValue = new DocumentValue(\"yujyt\".getBytes(), \"mimeType\", \"filename\");\n\n        // When\n        documentValue.setFileName(\"\");\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.bpm.document.DocumentValue#setFileName(java.lang.String)}.\n     */\n    @Test(expected = IllegalArgumentException.class)\n    public final void setFileName_should_throw_exception_if_filename_is_null_and_content_not() {\n        // Given\n        final DocumentValue documentValue = new DocumentValue(\"yujyt\".getBytes(), \"mimeType\", \"filename\");\n\n        // When\n        documentValue.setFileName(null);\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.bpm.document.DocumentValue#setFileName(java.lang.String)}.\n     */\n    @Test\n    public final void setFileName_should_set_content_with_filename_and_content() {\n        // Given\n        final DocumentValue documentValue = new DocumentValue(\"yujyt\".getBytes(), \"mimeType\", \"filename\");\n        final String fileName = \"new\";\n\n        // When\n        documentValue.setFileName(fileName);\n\n        // Then\n        assertEquals(fileName, documentValue.getFileName());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/business/application/ApplicationLinkUpdaterTest.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application;\n\nimport static org.junit.Assert.assertThrows;\n\nimport org.junit.Test;\n\npublic class ApplicationLinkUpdaterTest {\n\n    @Test\n    public void should_not_update_home_page_field() {\n        ApplicationLinkUpdater linkUpdater = new ApplicationLinkUpdater();\n\n        assertThrows(IllegalArgumentException.class,\n                () -> linkUpdater.getFields().put(ApplicationField.HOME_PAGE_ID, 2L));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/business/application/ApplicationUpdaterTest.java",
    "content": "/**\n * Copyright (C) 2021 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\n\nimport org.junit.Test;\n\npublic class ApplicationUpdaterTest {\n\n    @Test\n    public void should_set_icon_fields_when_updating_icon() {\n        ApplicationUpdater applicationUpdater = new ApplicationUpdater();\n\n        applicationUpdater.setIcon(\"myIcon.png\", new byte[] { 1, 2, 3 });\n\n        assertThat(applicationUpdater.getFields()).containsOnly(\n                entry(ApplicationField.ICON_FILE_NAME, \"myIcon.png\"),\n                entry(ApplicationField.ICON_CONTENT, new byte[] { 1, 2, 3 }));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/connector/sap/SAPMonoDestinationDataProviderTest.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connector.sap;\n\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\n\nimport com.sap.conn.jco.ext.DestinationDataProvider;\nimport com.sap.conn.jco.ext.Environment;\nimport mockit.Mock;\nimport mockit.MockUp;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * <b>IMPORTANT</b>: this test class uses jmockit. If you run it in your IDE, you may need to configure a java agent\n * (its\n * seems working out of the box in IntelliJ). See the maven pom file.\n *\n * @author Aurelien Pupier\n * @author Baptiste Mesta\n */\npublic class SAPMonoDestinationDataProviderTest {\n\n    @Before\n    public void setupMocks() {\n        // Ugly stuff that does not respect the \"don't mock code you don't own\"\n        new MockUp<Environment>() {\n\n            @Mock\n            public void registerDestinationDataProvider(DestinationDataProvider destinationDataProvider) {\n                // do nothing\n            }\n\n            @Mock\n            public void unregisterDestinationDataProvider(DestinationDataProvider destinationDataProvider) {\n                // do nothing\n            }\n\n        };\n    }\n\n    @Before\n    public void setup() {\n        SAPMonoDestinationDataProvider.clear();\n    }\n\n    @After\n    public void tearDown() {\n        SAPMonoDestinationDataProvider.clear();\n    }\n\n    @Test\n    public void should_fail_to_set_multitple_different_destinations() throws IllegalStateException {\n        SAPMonoDestinationDataProvider.getInstance(\"name1\"); // 1st one, should succeed\n\n        assertThatThrownBy(() -> SAPMonoDestinationDataProvider.getInstance(\"name2\"))\n                .isInstanceOf(IllegalStateException.class)\n                .hasMessageStartingWith(\"You can use only one SAP destination\");\n    }\n\n    @Test\n    public void should_succeed_to_set_the_same_destination_several_times() throws IllegalStateException {\n        SAPMonoDestinationDataProvider.getInstance(\"nameSimilar\");\n        SAPMonoDestinationDataProvider.getInstance(\"nameSimilar\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/digest/DigestUtilsTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.digest;\n\nimport static java.nio.charset.StandardCharsets.UTF_8;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\nimport static org.bonitasoft.engine.digest.DigestUtils.*;\n\nimport org.junit.Test;\n\npublic class DigestUtilsTest {\n\n    @Test\n    public void should_encode_base64_when_source_with_accents() {\n        assertThat(encodeBase64AsUtf8String(bytes(\"I love Bonita héhé\"))).isEqualTo(\"SSBsb3ZlIEJvbml0YSBow6low6k=\");\n    }\n\n    @Test\n    public void should_generate_md5_when_source_with_accents() {\n        assertThat(md5(\"I lôve Bonita hïhà\"))\n                .isEqualTo(new byte[] { 32, 69, -29, -53, 5, 12, -97, 109, 27, -99, 16, -27, 35, 71, 92, -46 });\n    }\n\n    @Test\n    public void should_generate_md5_fail_when_source_is_null() {\n        assertThatThrownBy(() -> md5(null)).isInstanceOf(NullPointerException.class);\n    }\n\n    @Test\n    public void should_generate_sha1_when_source_with_accents() {\n        assertThat(sha1(\"You Lik~e Bonita ù?\"))\n                .isEqualTo(new byte[] { 87, 92, -26, 118, -34, -105, -48, 1, 124, 85, 62, 10, -24, -87, -11, -22, 112,\n                        69, -51, 63 });\n    }\n\n    @Test\n    public void should_generate_sha1_fail_when_source_is_null() {\n        assertThatThrownBy(() -> sha1(null)).isInstanceOf(NullPointerException.class);\n    }\n\n    private static byte[] bytes(String string) {\n        return string.getBytes(UTF_8);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/exception/BonitaExceptionTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\nimport org.junit.Test;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class BonitaExceptionTest {\n\n    @Test\n    public void newBonitaExceptionWithNullCauseShouldNotThrowNPE() {\n        new BonitaException(\"any message\", null);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/exception/StackTraceTransformerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.exception;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNull;\nimport static org.junit.Assert.assertTrue;\n\nimport java.lang.reflect.Field;\nimport java.util.HashSet;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport org.bonitasoft.engine.api.internal.ServerWrappedException;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceNotFoundException;\nimport org.junit.Test;\n\npublic class StackTraceTransformerTest {\n\n    @Test\n    public void merge_stack_keep_only_current_exception() {\n        ServerWrappedException e = new ServerWrappedException(\n                new IllegalStateException(new NoClassDefFoundError(\"org.Unknown\")));\n\n        ServerWrappedException newE = StackTraceTransformer.mergeStackTraces(e);\n\n        assertNull(newE.getCause().getCause());\n    }\n\n    @Test\n    public void merge_stack_keep_stack_of_cause() {\n        ProcessInstanceNotFoundException cause3 = new ProcessInstanceNotFoundException(\"the process\");\n        BonitaRuntimeException cause2 = new BonitaRuntimeException(\"org.Unknown\", cause3);\n        IllegalStateException cause = new IllegalStateException(cause2);\n        ServerWrappedException e = new ServerWrappedException(cause);\n\n        ServerWrappedException newE = StackTraceTransformer.mergeStackTraces(e);\n        assertTrue(containsStack(newE.getCause().getStackTrace(), \"BonitaRuntimeException\"));\n        assertTrue(containsStack(newE.getCause().getStackTrace(), \"ProcessInstanceNotFoundException\"));\n    }\n\n    @Test\n    public void merge_stack_reduce_stack_length() throws Exception {\n        ProcessInstanceNotFoundException cause3 = new ProcessInstanceNotFoundException(\"the process\");\n        BonitaRuntimeException cause2 = new BonitaRuntimeException(\"org.Unknown\", cause3);\n        IllegalStateException cause = new IllegalStateException(cause2);\n        ServerWrappedException e = new ServerWrappedException(cause);\n        cause.printStackTrace();\n        ServerWrappedException newE = StackTraceTransformer.mergeStackTraces(e);\n        newE.getCause().printStackTrace();\n        noDuplicate(newE.getCause());\n    }\n\n    private void noDuplicate(final Throwable throwable) throws Exception {\n        HashSet<StackTraceElement> hashSet = new HashSet<StackTraceElement>();\n        for (StackTraceElement stackTraceElement : throwable.getStackTrace()) {\n            if (!hashSet.add(stackTraceElement) && stackTraceElement.toString().contains(\"org.bonitasoft\")) {\n                Logger.getLogger(this.getClass().getName()).log(Level.SEVERE,\n                        \"the stacktrace contains 2 times \" + stackTraceElement, throwable);\n                throw new Exception(\"the stacktrace contains 2 times \" + stackTraceElement, throwable);\n            }\n        }\n\n    }\n\n    private boolean containsStack(final StackTraceElement[] stackTrace, final String string) {\n        for (StackTraceElement stackTraceElement : stackTrace) {\n            if (stackTraceElement.getClassName().contains(string)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    @Test\n    public void merge_stack_keep_message_of_cause() {\n        ProcessInstanceNotFoundException cause3 = new ProcessInstanceNotFoundException(\"the process\");\n        BonitaRuntimeException cause2 = new BonitaRuntimeException(\"org.Unknown\", cause3);\n        IllegalStateException cause = new IllegalStateException(cause2);\n        ServerWrappedException e = new ServerWrappedException(cause);\n\n        ServerWrappedException newE = StackTraceTransformer.mergeStackTraces(e);\n\n        assertTrue(containsMessage(newE.getCause().getStackTrace(), \"org.Unknown\"));\n        assertTrue(containsMessage(newE.getCause().getStackTrace(), \"the process\"));\n    }\n\n    @Test\n    public void merge_stack_when_no_cause() {\n        ProcessInstanceNotFoundException cause = new ProcessInstanceNotFoundException(\"the process\");\n        ServerWrappedException e = new ServerWrappedException(cause);\n\n        ServerWrappedException newE = StackTraceTransformer.mergeStackTraces(e);\n\n        assertEquals(\"the process\", cause.getMessage());\n        assertEquals(cause, newE.getCause());\n    }\n\n    @Test\n    public void merge_stack_work_even_with_security_restriction() {\n        ProcessInstanceNotFoundException cause3 = new ProcessInstanceNotFoundException(\"the process\");\n        BonitaRuntimeException cause2 = new BonitaRuntimeException(\"org.Unknown\", cause3);\n        IllegalStateException cause = new IllegalStateException(cause2);\n        ServerWrappedException e = new ServerWrappedException(cause);\n        Field field = StackTraceTransformer.field;\n        StackTraceTransformer.field = null;\n        ServerWrappedException newE = StackTraceTransformer.mergeStackTraces(e);\n        StackTraceTransformer.field = field;\n\n        assertTrue(containsMessage(newE.getCause().getStackTrace(), \"org.Unknown\"));\n        assertTrue(containsMessage(newE.getCause().getStackTrace(), \"the process\"));\n    }\n\n    private boolean containsMessage(final StackTraceElement[] stackTrace, final String string) {\n        for (StackTraceElement stackTraceElement : stackTrace) {\n            if (stackTraceElement.getMethodName().contains(string)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/expression/ExpressionEvaluationExceptionTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression;\n\nimport static org.junit.Assert.assertEquals;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Celine Souchet\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class ExpressionEvaluationExceptionTest {\n\n    @Mock\n    private Throwable cause;\n\n    private final String expressionName = \"plop\";\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.expression.ExpressionEvaluationException#getExpressionName()}.\n     */\n    @Test\n    public final void getExpressionName() {\n        final ExpressionEvaluationException expressionEvaluationException = new ExpressionEvaluationException(cause,\n                expressionName);\n\n        final String result = expressionEvaluationException.getExpressionName();\n        assertEquals(expressionName, result);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/filter/AbstractUserFilterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.filter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.List;\n\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class AbstractUserFilterTest {\n\n    private AbstractUserFilter abstractUserFilterTest;\n\n    @Before\n    public void setUp() {\n        abstractUserFilterTest = new AbstractUserFilter() {\n\n            @Override\n            public void validateInputParameters() {\n\n            }\n\n            @SuppressWarnings(\"unused\")\n            @Override\n            public List<Long> filter(final String actorName) {\n                return null;\n            }\n\n        };\n    }\n\n    @After\n    public void tearDown() {\n    }\n\n    @Test\n    public void should_getInputParameter_return_null_if_map_does_not_contain_key() throws Exception {\n        assertThat(abstractUserFilterTest.getInputParameter(\"aInput\")).isNull();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/io/FileOperationsTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.io;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.io.FileAndContentUtils.*;\n\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.nio.file.Files;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.TemporaryFolder;\n\n/**\n * @author Baptiste Mesta\n */\npublic class FileOperationsTest {\n\n    @Rule\n    public TemporaryFolder temporaryFolder = new TemporaryFolder();\n\n    @Test\n    public void should_extract_file_from_zip() throws Exception {\n        File zipFile = aZipWithFiles();\n\n        byte[] contentOfFile2 = FileOperations.getFileFromZip(zipFile, \"/file2\");\n\n        assertThat(new String(contentOfFile2)).isEqualTo(\"the content of file 2\");\n    }\n\n    private File aZipWithFiles() throws IOException {\n        File zipFile;\n        byte[] zip = zip(file(\"/file1\", \"the content of file 1\"),\n                file(\"/file2\", \"the content of file 2\"));\n        zipFile = temporaryFolder.newFile();\n        Files.write(zipFile.toPath(), zip);\n        return zipFile;\n    }\n\n    @Test(expected = FileNotFoundException.class)\n    public void should_throw_file_not_found_when_zip_do_not_contains_file() throws Exception {\n        byte[] zip = zip(file(\"/file1\", \"the content of file 1\"));\n        File zipFile = temporaryFolder.newFile();\n        Files.write(zipFile.toPath(), zip);\n\n        FileOperations.getFileFromZip(zipFile, \"/file2\");\n    }\n\n    @Test(expected = IOException.class)\n    public void should_throw_IOException_when_file_is_not_a_zip() throws Exception {\n        File zipFile = temporaryFolder.newFile();\n        Files.write(zipFile.toPath(), new byte[] { 1, 2, 3 });\n\n        FileOperations.getFileFromZip(zipFile, \"toto\");\n    }\n\n    @Test\n    public void should_getContent_of_file_in_sub_folder() throws Exception {\n        byte[] zip = zip(file(\"/file1\", \"the content of file 1\"),\n                directory(\"/sub/\"),\n                file(\"/sub/file2\", \"the content of file 2\"));\n        File zipFile = temporaryFolder.newFile();\n        Files.write(zipFile.toPath(), zip);\n\n        byte[] contentOfFile2 = FileOperations.getFileFromZip(zipFile, \"/sub/file2\");\n\n        assertThat(new String(contentOfFile2)).isEqualTo(\"the content of file 2\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/page/PageCreatorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.page.PageCreator.PageField;\nimport org.junit.Test;\n\npublic class PageCreatorTest {\n\n    private static final String ZIP_FILE_NAME = \"content.zip\";\n\n    private static final String DISPLAY_NAME = \"display name\";\n\n    private static final String NAME = \"page name\";\n\n    private static final String DESCRIPTION = \"page description\";\n    public static final long PROCESS_DEFINITION_ID = 123L;\n\n    @Test\n    public void should_create_page_with_default_content_type() {\n        // given\n        final PageCreator pageCreator = new PageCreator(NAME, ZIP_FILE_NAME).setDisplayName(DISPLAY_NAME)\n                .setDescription(DESCRIPTION);\n\n        // when\n        final Map<PageField, Serializable> fields = pageCreator.getFields();\n\n        // then\n        assertThat(fields).as(\"should set content type\").containsOnly(entry(PageField.NAME, NAME),\n                entry(PageField.DISPLAY_NAME, DISPLAY_NAME),\n                entry(PageField.DESCRIPTION, DESCRIPTION),\n                entry(PageField.CONTENT_TYPE, ContentType.PAGE),\n                entry(PageField.CONTENT_NAME, ZIP_FILE_NAME));\n\n        assertThat(pageCreator.getName()).isEqualTo(NAME);\n    }\n\n    @Test\n    public void should_create_page_with_process_definition() {\n        // given\n        final PageCreator pageCreator = new PageCreator(NAME, ZIP_FILE_NAME).setDisplayName(DISPLAY_NAME)\n                .setDescription(DESCRIPTION)\n                .setProcessDefinitionId(PROCESS_DEFINITION_ID)\n                .setContentType(ContentType.FORM);\n\n        // when\n        final Map<PageField, Serializable> fields = pageCreator.getFields();\n\n        // then\n        assertThat(fields).as(\"should set content type\").containsOnly(entry(PageField.NAME, NAME),\n                entry(PageField.DISPLAY_NAME, DISPLAY_NAME),\n                entry(PageField.DESCRIPTION, DESCRIPTION),\n                entry(PageField.CONTENT_NAME, ZIP_FILE_NAME),\n                entry(PageField.CONTENT_TYPE, ContentType.FORM),\n                entry(PageField.PROCESS_DEFINITION_ID, PROCESS_DEFINITION_ID));\n\n        assertThat(pageCreator.getName()).isEqualTo(NAME);\n    }\n\n    @Test\n    public void should_create_page_with_form_content_type() {\n        // given\n        final PageCreator pageCreator = new PageCreator(NAME, ZIP_FILE_NAME, ContentType.FORM, 12345L)\n                .setDisplayName(DISPLAY_NAME);\n\n        // when\n        final Map<PageField, Serializable> fields = pageCreator.getFields();\n\n        // then\n        assertThat(fields).as(\"should set content type\").containsOnly(\n                entry(PageField.NAME, NAME),\n                entry(PageField.DISPLAY_NAME, DISPLAY_NAME),\n                entry(PageField.CONTENT_TYPE, ContentType.FORM),\n                entry(PageField.PROCESS_DEFINITION_ID, 12345L),\n                entry(PageField.CONTENT_NAME, ZIP_FILE_NAME));\n\n    }\n\n    @Test\n    public void should_print_all_fields() {\n        // given\n        final PageCreator pageCreator = new PageCreator(NAME, ZIP_FILE_NAME, ContentType.FORM, 12345L)\n                .setDisplayName(DISPLAY_NAME).setDescription(\n                        DESCRIPTION);\n\n        // when\n        final Map<PageField, Serializable> fields = pageCreator.getFields();\n\n        // then\n        assertThat(pageCreator).as(\"should print human readable to string\")\n                .hasToString(\"PageCreator [fields=\" + fields + \"]\");\n\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/page/PageUpdaterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.junit.Test;\n\npublic class PageUpdaterTest {\n\n    private static final String ZIP_FILE_NAME = \"content.zip\";\n\n    private static final String DISPLAY_NAME = \"display name\";\n\n    private static final String NAME = \"page name\";\n\n    private static final String DESCRIPTION = \"page description\";\n    public static final long PROCESS_DEFINITION_ID = 1L;\n\n    @Test\n    public void should_create_page_with_default_fields() {\n        // given\n        final PageUpdater pageUpdater = new PageUpdater().setName(NAME).setContentName(ZIP_FILE_NAME)\n                .setDisplayName(DISPLAY_NAME).setDescription(DESCRIPTION)\n                .setContentType(ContentType.FORM).setProcessDefinitionId(PROCESS_DEFINITION_ID);\n\n        // when\n        final Map<PageUpdater.PageUpdateField, Serializable> fields = pageUpdater.getFields();\n\n        // then\n        assertThat(fields).as(\"should set content type\").containsOnly(entry(PageUpdater.PageUpdateField.NAME, NAME),\n                entry(PageUpdater.PageUpdateField.DISPLAY_NAME, DISPLAY_NAME),\n                entry(PageUpdater.PageUpdateField.DESCRIPTION, DESCRIPTION),\n                entry(PageUpdater.PageUpdateField.CONTENT_TYPE, ContentType.FORM),\n                entry(PageUpdater.PageUpdateField.CONTENT_NAME, ZIP_FILE_NAME),\n                entry(PageUpdater.PageUpdateField.PROCESS_DEFINITION_ID, PROCESS_DEFINITION_ID),\n                entry(PageUpdater.PageUpdateField.NAME, NAME));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/page/impl/PageImplAssert.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page.impl;\n\nimport static java.lang.String.format;\n\nimport java.util.Date;\n\nimport org.assertj.core.api.AbstractAssert;\n\n/**\n * {@link PageImpl} specific assertions - Generated by CustomAssertionGenerator.\n */\npublic class PageImplAssert extends AbstractAssert<PageImplAssert, PageImpl> {\n\n    /**\n     * Creates a new </code>{@link PageImplAssert}</code> to make assertions on actual PageImpl.\n     *\n     * @param actual the PageImpl we want to make assertions on.\n     */\n    public PageImplAssert(PageImpl actual) {\n        super(actual, PageImplAssert.class);\n    }\n\n    /**\n     * An entry point for PageImplAssert to follow AssertJ standard <code>assertThat()</code> statements.<br>\n     * With a static import, one's can write directly : <code>assertThat(myPageImpl)</code> and get specific assertion\n     * with code completion.\n     *\n     * @param actual the PageImpl we want to make assertions on.\n     * @return a new </code>{@link PageImplAssert}</code>\n     */\n    public static PageImplAssert assertThat(PageImpl actual) {\n        return new PageImplAssert(actual);\n    }\n\n    /**\n     * Verifies that the actual PageImpl's contentName is equal to the given one.\n     *\n     * @param contentName the given contentName to compare the actual PageImpl's contentName to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual PageImpl's contentName is not equal to the given one.\n     */\n    public PageImplAssert hasContentName(String contentName) {\n        // check that actual PageImpl we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> contentName to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                contentName, actual.getContentName());\n\n        // check\n        if (!actual.getContentName().equals(contentName)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual PageImpl's contentType is equal to the given one.\n     *\n     * @param contentType the given contentType to compare the actual PageImpl's contentType to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual PageImpl's contentType is not equal to the given one.\n     */\n    public PageImplAssert hasContentType(String contentType) {\n        // check that actual PageImpl we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> contentType to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                contentType, actual.getContentType());\n\n        // check\n        if (!actual.getContentType().equals(contentType)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual PageImpl's description is equal to the given one.\n     *\n     * @param description the given description to compare the actual PageImpl's description to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual PageImpl's description is not equal to the given one.\n     */\n    public PageImplAssert hasDescription(String description) {\n        // check that actual PageImpl we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> description to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                description, actual.getDescription());\n\n        // check\n        if (!actual.getDescription().equals(description)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual PageImpl's displayName is equal to the given one.\n     *\n     * @param displayName the given displayName to compare the actual PageImpl's displayName to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual PageImpl's displayName is not equal to the given one.\n     */\n    public PageImplAssert hasDisplayName(String displayName) {\n        // check that actual PageImpl we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> displayName to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                displayName, actual.getDisplayName());\n\n        // check\n        if (!actual.getDisplayName().equals(displayName)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual PageImpl's id is equal to the given one.\n     *\n     * @param id the given id to compare the actual PageImpl's id to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual PageImpl's id is not equal to the given one.\n     */\n    public PageImplAssert hasId(long id) {\n        // check that actual PageImpl we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> id to be:\\n  <%s>\\n but was:\\n  <%s>\", actual, id,\n                actual.getId());\n\n        // check\n        if (actual.getId() != id) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual PageImpl's installationDate is equal to the given one.\n     *\n     * @param installationDate the given installationDate to compare the actual PageImpl's installationDate to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual PageImpl's installationDate is not equal to the given one.\n     */\n    public PageImplAssert hasInstallationDate(Date installationDate) {\n        // check that actual PageImpl we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> installationDate to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                installationDate, actual.getInstallationDate());\n\n        // check\n        if (!actual.getInstallationDate().equals(installationDate)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual PageImpl's installedBy is equal to the given one.\n     *\n     * @param installedBy the given installedBy to compare the actual PageImpl's installedBy to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual PageImpl's installedBy is not equal to the given one.\n     */\n    public PageImplAssert hasInstalledBy(long installedBy) {\n        // check that actual PageImpl we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> installedBy to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                installedBy, actual.getInstalledBy());\n\n        // check\n        if (actual.getInstalledBy() != installedBy) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual PageImpl's lastModificationDate is equal to the given one.\n     *\n     * @param lastModificationDate the given lastModificationDate to compare the actual PageImpl's lastModificationDate\n     *        to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual PageImpl's lastModificationDate is not equal to the given one.\n     */\n    public PageImplAssert hasLastModificationDate(Date lastModificationDate) {\n        // check that actual PageImpl we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> lastModificationDate to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                lastModificationDate, actual.getLastModificationDate());\n\n        // check\n        if (!actual.getLastModificationDate().equals(lastModificationDate)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual PageImpl's lastUpdatedBy is equal to the given one.\n     *\n     * @param lastUpdatedBy the given lastUpdatedBy to compare the actual PageImpl's lastUpdatedBy to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual PageImpl's lastUpdatedBy is not equal to the given one.\n     */\n    public PageImplAssert hasLastUpdatedBy(long lastUpdatedBy) {\n        // check that actual PageImpl we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> lastUpdatedBy to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                lastUpdatedBy, actual.getLastUpdatedBy());\n\n        // check\n        if (actual.getLastUpdatedBy() != lastUpdatedBy) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual PageImpl's name is equal to the given one.\n     *\n     * @param name the given name to compare the actual PageImpl's name to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual PageImpl's name is not equal to the given one.\n     */\n    public PageImplAssert hasName(String name) {\n        // check that actual PageImpl we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> name to be:\\n  <%s>\\n but was:\\n  <%s>\", actual, name,\n                actual.getName());\n\n        // check\n        if (!actual.getName().equals(name)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual PageImpl's pageId is equal to the given one.\n     *\n     * @param pageId the given pageId to compare the actual PageImpl's pageId to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual PageImpl's pageId is not equal to the given one.\n     */\n    public PageImplAssert hasPageId(long pageId) {\n        // check that actual PageImpl we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> pageId to be:\\n  <%s>\\n but was:\\n  <%s>\", actual, pageId,\n                actual.getPageId());\n\n        // check\n        if (actual.getPageId() != pageId) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual PageImpl's processDefinitionId is equal to the given one.\n     *\n     * @param processDefinitionId the given processDefinitionId to compare the actual PageImpl's processDefinitionId to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual PageImpl's processDefinitionId is not equal to the given one.\n     */\n    public PageImplAssert hasProcessDefinitionId(Long processDefinitionId) {\n        // check that actual PageImpl we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> processDefinitionId to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                processDefinitionId, actual.getProcessDefinitionId());\n\n        // check\n        if (!actual.getProcessDefinitionId().equals(processDefinitionId)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual PageImpl is provided.\n     *\n     * @return this assertion object.\n     * @throws AssertionError - if the actual PageImpl is not provided.\n     */\n    public PageImplAssert isProvided() {\n        // check that actual PageImpl we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"Expected actual PageImpl to be provided but was not.\", actual);\n\n        // check\n        if (!actual.isProvided())\n            throw new AssertionError(errorMessage);\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual PageImpl is editable.\n     *\n     * @return this assertion object.\n     * @throws AssertionError - if the actual PageImpl is not provided.\n     */\n    public PageImplAssert isEditable() {\n        // check that actual PageImpl we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"Expected actual PageImpl to be editable but was not.\", actual);\n\n        // check\n        if (!actual.isEditable())\n            throw new AssertionError(errorMessage);\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual PageImpl is removable.\n     *\n     * @return this assertion object.\n     * @throws AssertionError - if the actual PageImpl is not provided.\n     */\n    public PageImplAssert isRemovable() {\n        // check that actual PageImpl we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"Expected actual PageImpl to be removable but was not.\", actual);\n\n        // check\n        if (!actual.isRemovable())\n            throw new AssertionError(errorMessage);\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual PageImpl is not provided.\n     *\n     * @return this assertion object.\n     * @throws AssertionError - if the actual PageImpl is provided.\n     */\n    public PageImplAssert isNotProvided() {\n        // check that actual PageImpl we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"Expected actual PageImpl not to be provided but was.\", actual);\n\n        // check\n        if (actual.isProvided())\n            throw new AssertionError(errorMessage);\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/page/impl/PageImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.page.ContentType;\nimport org.junit.Test;\n\npublic class PageImplTest {\n\n    private static final long USER_ID = 1L;\n\n    private static final String DESCRIPTION = \"description\";\n\n    private static final boolean PROVIDED = true;\n\n    private static final boolean EDITABLE = true;\n\n    private static final boolean REMOVABLE = true;\n\n    private static final String NAME = \"name\";\n\n    private static final String DISPLAY_NAME = \"display name\";\n\n    public static final long PROCESS_DEFINITION_ID = 456789L;\n\n    @Test\n    public void should_set_all_fields() {\n\n        Date date = new Date(1);\n        Date modificationDate = new Date(2);\n\n        PageImplAssert\n                .assertThat(\n                        new PageImpl(-1L, NAME, DISPLAY_NAME, PROVIDED, EDITABLE, REMOVABLE, DESCRIPTION,\n                                date.getTime(), USER_ID,\n                                modificationDate.getTime(), USER_ID,\n                                \"content.zip\", ContentType.PAGE, null))\n                .hasId(-1L)\n                .hasName(NAME)\n                .hasDisplayName(DISPLAY_NAME)\n                .isProvided()\n                .isEditable()\n                .isRemovable()\n                .hasDescription(DESCRIPTION).hasInstallationDate(date)\n                .hasLastModificationDate(modificationDate)\n                .hasContentType(ContentType.PAGE);\n\n    }\n\n    @Test\n    public void should_set_all_form_fields() {\n\n        Date date = new Date(1);\n        Date modificationDate = new Date(2);\n\n        PageImplAssert\n                .assertThat(\n                        new PageImpl(-1l, NAME, DISPLAY_NAME, PROVIDED, EDITABLE, REMOVABLE, DESCRIPTION,\n                                date.getTime(), USER_ID,\n                                modificationDate.getTime(), USER_ID,\n                                \"content.zip\", ContentType.FORM, PROCESS_DEFINITION_ID))\n                .hasId(-1l)\n                .hasName(NAME)\n                .hasDisplayName(DISPLAY_NAME)\n                .isProvided()\n                .isRemovable()\n                .isEditable()\n                .hasDescription(DESCRIPTION).hasInstallationDate(date)\n                .hasLastModificationDate(modificationDate)\n                .hasProcessDefinitionId(PROCESS_DEFINITION_ID)\n                .hasContentType(ContentType.FORM);\n\n    }\n\n    @Test\n    public void should_display_all_fields_in_to_string() {\n\n        Date date = new Date(1);\n        Date modificationDate = new Date(2);\n\n        assertThat(\n                new PageImpl(-1L, NAME, DISPLAY_NAME, PROVIDED, EDITABLE, REMOVABLE, DESCRIPTION,\n                        date.getTime(), USER_ID,\n                        modificationDate.getTime(), USER_ID,\n                        \"content.zip\", ContentType.FORM, PROCESS_DEFINITION_ID).toString())\n                .contains(String.valueOf(PROCESS_DEFINITION_ID)).contains(\n                        ContentType.FORM);\n\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/search/impl/SearchOptionsImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.Test;\n\npublic class SearchOptionsImplTest {\n\n    @Test\n    public void equals_should_check_the_whole_objects() {\n        final SearchOptionsImpl options1 = buildSearchOptions();\n        final SearchOptionsImpl options2 = buildSearchOptions();\n\n        assertThat(options1).isEqualTo(options2);\n    }\n\n    @Test\n    public void hashCode_should_check_the_whole_objects() {\n        final SearchOptionsImpl options1 = buildSearchOptions();\n        final SearchOptionsImpl options2 = buildSearchOptions();\n\n        assertThat(options1.hashCode()).isEqualTo(options2.hashCode());\n    }\n\n    private SearchOptionsImpl buildSearchOptions() {\n        final SearchOptionsImpl options = new SearchOptionsImpl(0, 2000);\n        options.addFilter(\"field1\", \"value\");\n        options.addFilter(\"field3\", 8);\n        options.addFilter(\"field2\", true);\n        return options;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/java/org/bonitasoft/engine/util/IOUtilTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.util;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\nimport static org.junit.Assert.assertArrayEquals;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipOutputStream;\n\nimport org.bonitasoft.engine.io.IOUtil;\nimport org.junit.Test;\n\n/**\n * @author Baptiste Mesta\n */\npublic class IOUtilTest {\n\n    private static String lineSeparator = System.getProperty(\"line.separator\");\n\n    @Test\n    public void testGetResources() throws Exception {\n        final Map<String, byte[]> resources = IOUtil.getResources(IOUtilTest.class, IOUtil.class);\n\n        assertNotNull(resources.get(IOUtil.class.getName().replace('.', '/') + \".class\"));\n    }\n\n    @Test\n    public void testGetClassData() throws Exception {\n        assertNotNull(IOUtil.getClassData(this.getClass()));\n    }\n\n    @Test\n    public void testGetAllContentFromInputStream() throws Exception {\n        final byte[] bytes = \"theContent\\nVeryGreatContent\".getBytes();\n        final ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);\n\n        final byte[] read = IOUtil.getAllContentFrom(inputStream);\n        inputStream.close();\n\n        assertArrayEquals(bytes, read);\n    }\n\n    @Test\n    public void testGetAllContentFromFile() throws Exception {\n        final File file = File.createTempFile(\"test\", \"test\");\n        IOUtil.writeContentToFile(\"theContent\\nVeryGreatContent\", file);\n\n        assertArrayEquals(\"theContent\\nVeryGreatContent\".getBytes(), IOUtil.getAllContentFrom(file));\n\n        file.delete();\n    }\n\n    @Test\n    public void testGetAllContentFromURL() throws Exception {\n\n        final File file = File.createTempFile(\"test\", \"test\");\n        IOUtil.writeContentToFile(\"theContent\\nVeryGreatContent\", file);\n\n        assertArrayEquals(\"theContent\\nVeryGreatContent\".getBytes(), IOUtil.getAllContentFrom(file.toURI().toURL()));\n\n        file.delete();\n    }\n\n    @Test\n    public void testDeleteDirFile() throws Exception {\n        final File folder = File.createTempFile(\"folder\", \"test\");\n        folder.delete();\n        folder.mkdir();\n        final File file = new File(folder, \"aFile\");\n        IOUtil.writeContentToFile(\"content\", file);\n\n        assertTrue(IOUtil.deleteDir(folder));\n\n        assertFalse(file.exists());\n        assertFalse(folder.exists());\n    }\n\n    @Test\n    public void testDeleteDirFileWithRetry() throws Exception {\n        final File folder = File.createTempFile(\"folder\", \"test\");\n        folder.delete();\n        folder.mkdir();\n        final File file = new File(folder, \"aFile\");\n        IOUtil.writeContentToFile(\"content\", file);\n\n        assertTrue(IOUtil.deleteDir(folder, 5, 1));\n\n        assertFalse(file.exists());\n        assertFalse(folder.exists());\n    }\n\n    @Test\n    public void testDeleteFile() {\n        final File file = mock(File.class);\n        when(file.delete()).thenReturn(true);\n        final boolean deleteFile = IOUtil.deleteFile(file, 2, 1);\n\n        assertTrue(deleteFile);\n    }\n\n    @Test\n    public void testDeleteFileNotDeleted() {\n        final File file = mock(File.class);\n        when(file.delete()).thenReturn(false);\n        final boolean deleteFile = IOUtil.deleteFile(file, 2, 1);\n\n        assertFalse(deleteFile);\n    }\n\n    @Test\n    public void testDeleteFiledeletedAfterFewTry() {\n        final File file = mock(File.class);\n        when(file.delete()).thenReturn(false, false, true);\n        final boolean deleteFile = IOUtil.deleteFile(file, 6, 1);\n\n        assertTrue(deleteFile);\n    }\n\n    @Test\n    public void testZip() throws Exception {\n        final HashMap<String, byte[]> hashMap = new HashMap<String, byte[]>(2);\n        hashMap.put(\"file1.txt\", \"content1\".getBytes());\n        hashMap.put(\"file2.txt\", \"content2\\ncontent2\".getBytes());\n        final byte[] zip = IOUtil.zip(hashMap);\n        assertNotNull(zip);\n    }\n\n    @Test\n    public void testWriteFileString() throws Exception {\n        final File file = File.createTempFile(\"test\", \"test\");\n\n        IOUtil.writeContentToFile(\"theContent\\ncontent\", file);\n\n        assertEquals(\"theContent\\ncontent\", new String(IOUtil.getAllContentFrom(file)));\n        file.delete();\n    }\n\n    @Test\n    public void unzipToFolder() throws Exception {\n        final HashMap<String, byte[]> hashMap = new HashMap<String, byte[]>(2);\n        hashMap.put(\"file1.txt\", \"content1\".getBytes());\n        hashMap.put(\"file2.txt\", \"content2\\ncontent2\".getBytes());\n        final byte[] zip = IOUtil.zip(hashMap);\n        final File folder = File.createTempFile(\"folder\", \"tmp\");\n        folder.delete();\n        folder.mkdirs();\n\n        IOUtil.unzipToFolder(new ByteArrayInputStream(zip), folder);\n\n        final String[] files = folder.list();\n        assertEquals(2, files.length);\n        assertEquals(\"content1\", new String(IOUtil.getAllContentFrom(new File(folder, \"file1.txt\"))));\n        assertEquals(\"content2\\ncontent2\", new String(IOUtil.getAllContentFrom(new File(folder, \"file2.txt\"))));\n\n        IOUtil.deleteDir(folder);\n    }\n\n    @Test\n    public void unzipToFolder_should_reject_zip_slip_file_attack() throws Exception {\n        final byte[] maliciousZip = createZipWithEntry(\"../../evil.txt\", \"malicious content\");\n        final File folder = createTempFolder();\n\n        try {\n            assertThatThrownBy(() -> IOUtil.unzipToFolder(new ByteArrayInputStream(maliciousZip), folder))\n                    .isInstanceOf(IOException.class)\n                    .hasMessageContaining(\"outside of the target directory\");\n        } finally {\n            IOUtil.deleteDir(folder);\n        }\n    }\n\n    @Test\n    public void unzipToFolder_should_reject_zip_slip_directory_attack() throws Exception {\n        final byte[] maliciousZip = createZipWithDirectoryEntry(\"../../evil_dir/\");\n        final File folder = createTempFolder();\n\n        try {\n            assertThatThrownBy(() -> IOUtil.unzipToFolder(new ByteArrayInputStream(maliciousZip), folder))\n                    .isInstanceOf(IOException.class)\n                    .hasMessageContaining(\"outside of the target directory\");\n        } finally {\n            IOUtil.deleteDir(folder);\n        }\n    }\n\n    @Test\n    public void unzipToFolder_should_allow_legitimate_nested_paths() throws Exception {\n        final HashMap<String, byte[]> hashMap = new HashMap<>(1);\n        hashMap.put(\"subdir/nested/file.txt\", \"nested content\".getBytes());\n        final byte[] zip = IOUtil.zip(hashMap);\n        final File folder = createTempFolder();\n\n        try {\n            IOUtil.unzipToFolder(new ByteArrayInputStream(zip), folder);\n\n            final File nestedFile = new File(folder, \"subdir/nested/file.txt\");\n            assertThat(nestedFile).exists();\n            assertThat(new String(IOUtil.getAllContentFrom(nestedFile))).isEqualTo(\"nested content\");\n        } finally {\n            IOUtil.deleteDir(folder);\n        }\n    }\n\n    private static File createTempFolder() throws IOException {\n        return Files.createTempDirectory(\"folder\").toFile();\n    }\n\n    private static byte[] createZipWithEntry(String entryName, String content) throws IOException {\n        final ByteArrayOutputStream baos = new ByteArrayOutputStream();\n        try (ZipOutputStream zos = new ZipOutputStream(baos)) {\n            zos.putNextEntry(new ZipEntry(entryName));\n            zos.write(content.getBytes());\n            zos.closeEntry();\n        }\n        return baos.toByteArray();\n    }\n\n    private static byte[] createZipWithDirectoryEntry(String dirName) throws IOException {\n        final ByteArrayOutputStream baos = new ByteArrayOutputStream();\n        try (ZipOutputStream zos = new ZipOutputStream(baos)) {\n            zos.putNextEntry(new ZipEntry(dirName));\n            zos.closeEntry();\n        }\n        return baos.toByteArray();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/test/resources/logback-test.xml",
    "content": "<configuration debug=\"false\" scan=\"false\">\n\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->\n        <encoder>\n            <pattern>|%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <logger name=\"org.bonitasoft\" level=\"INFO\" />\n\n    <root level=\"WARN\">\n        <appender-ref ref=\"STDOUT\" />\n    </root>\n\n</configuration>\n"
  },
  {
    "path": "bpm/bonita-common/src/test/resources/org/bonitasoft/engine/bdm/serialization/simpleInvoice.json",
    "content": "{\n  \"persistenceId\": null,\n  \"persistenceVersion\": null,\n  \"customerName\": \"Bonitasoft\",\n  \"date\": \"2018-08-15\",\n  \"comments\": null\n}"
  },
  {
    "path": "bpm/bonita-common/src/testFixtures/java/org/bonitasoft/engine/bdm/validator/assertion/RuleOfCondition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator.assertion;\n\nimport org.assertj.core.api.Condition;\nimport org.bonitasoft.engine.bdm.validator.ValidationStatus;\nimport org.bonitasoft.engine.bdm.validator.rule.ValidationRule;\n\n/**\n * @author Colin PUY\n */\npublic class RuleOfCondition extends Condition<ValidationRule<?, ValidationStatus>> {\n\n    public static RuleOfCondition ruleOf(Class<? extends ValidationRule<?, ValidationStatus>> ruleClass) {\n        return new RuleOfCondition(ruleClass);\n    }\n\n    private Class<?> ruleClass;\n\n    public RuleOfCondition(Class<?> ruleClass) {\n        this.ruleClass = ruleClass;\n    }\n\n    @Override\n    public boolean matches(ValidationRule<?, ValidationStatus> rule) {\n        return rule.getClass().equals(ruleClass);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/testFixtures/java/org/bonitasoft/engine/bdm/validator/assertion/ValidationStatusAssert.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.validator.assertion;\n\nimport static org.bonitasoft.engine.api.result.Status.Level.ERROR;\n\nimport java.util.Objects;\n\nimport org.assertj.core.api.AbstractAssert;\nimport org.assertj.core.api.Assertions;\nimport org.bonitasoft.engine.api.result.Status;\nimport org.bonitasoft.engine.bdm.validator.ValidationStatus;\n\npublic class ValidationStatusAssert extends AbstractAssert<ValidationStatusAssert, ValidationStatus> {\n\n    protected ValidationStatusAssert(ValidationStatus actual) {\n        super(actual, ValidationStatusAssert.class);\n    }\n\n    public static ValidationStatusAssert assertThat(ValidationStatus actual) {\n        return new ValidationStatusAssert(actual);\n    }\n\n    public ValidationStatusAssert isOk() {\n        Assertions.assertThat(actual.isOk()).isTrue();\n        return this;\n    }\n\n    public ValidationStatusAssert isOk(String description) {\n        Assertions.assertThat(actual.isOk()).as(description).isTrue();\n        return this;\n    }\n\n    public ValidationStatusAssert isNotOk() {\n        Assertions.assertThat(actual.isOk()).isFalse();\n        return this;\n    }\n\n    public ValidationStatusAssert isNotOk(String description) {\n        Assertions.assertThat(actual.isOk()).as(description).isFalse();\n        return this;\n    }\n\n    public ValidationStatusAssert hasError(String errorMessage) {\n        Assertions.assertThat(\n                actual.getStatuses().stream().filter(status -> Objects.equals(ERROR, status.getLevel()))\n                        .map(Status::getMessage).toList())\n                .contains(errorMessage);\n        return this;\n    }\n\n    public ValidationStatusAssert hasErrorSize(int size) {\n        Assertions.assertThat(actual.getStatuses().stream()\n                .filter(status -> Objects.equals(ERROR, status.getLevel()))\n                .count()).isEqualTo(size);\n        return this;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-common/src/testFixtures/java/org/bonitasoft/engine/io/FileAndContentUtils.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.io;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipOutputStream;\n\n/**\n * @author Baptiste Mesta.\n */\npublic class FileAndContentUtils {\n\n    public static byte[] zip(FileAndContent... files) throws IOException {\n        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();\n                ZipOutputStream zos = new ZipOutputStream(baos)) {\n            for (FileAndContent file : files) {\n                ZipEntry e = new ZipEntry(file.getFileName());\n                zos.putNextEntry(e);\n                if (!e.isDirectory()) {\n                    zos.write(file.getContent());\n                }\n                zos.closeEntry();\n            }\n            zos.finish();\n            return baos.toByteArray();\n        }\n    }\n\n    public static FileAndContent file(String fileName, String content) {\n        return new FileAndContent(fileName, content.getBytes());\n    }\n\n    public static FileAndContent file(String fileName, byte[] content) {\n        return new FileAndContent(fileName, content);\n    }\n\n    public static FileAndContent directory(String fileName) {\n        return new FileAndContent(fileName, null);\n    }\n\n    public static FileAndContent file(String fileName, InputStream content) throws IOException {\n        return new FileAndContent(fileName, content.readAllBytes());\n    }\n\n    public static class FileAndContent {\n\n        private String fileName;\n        private byte[] content;\n\n        public FileAndContent(String fileName, byte[] content) {\n            this.fileName = fileName;\n            this.content = content;\n        }\n\n        public String getFileName() {\n            return fileName;\n        }\n\n        public byte[] getContent() {\n            return content;\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-actor-mapping/build.gradle",
    "content": "\n\ndependencies {\n    api project(':services:bonita-identity')\n    api project(':services:bonita-commons')\n    api project(':services:bonita-builder')\n    api project(':services:bonita-events')\n    api project(':services:bonita-log')\n    api project(':services:bonita-persistence')\n    testImplementation libs.mockitoCore\n    testImplementation libs.assertj\n\n    annotationProcessor libs.lombok\n    compileOnly libs.lombok\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/ActorMappingService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.actor.mapping;\n\nimport java.util.List;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.actor.mapping.model.SActor;\nimport org.bonitasoft.engine.actor.mapping.model.SActorMember;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n * @since 6.0\n */\npublic interface ActorMappingService {\n\n    String ACTOR = \"ACTOR\";\n\n    String ACTOR_MEMBER = \"ACTOR_MEMBER\";\n\n    /**\n     * Create an actor by given actor\n     *\n     * @param actor\n     *        The given actor without id\n     * @return the new created actor with id\n     * @throws SActorCreationException\n     */\n    SActor addActor(SActor actor) throws SActorCreationException;\n\n    /**\n     * Create actors by given actors\n     *\n     * @param actors\n     *        The given actors without IDs\n     * @return The set of the new created actors\n     * @throws SActorCreationException\n     */\n    Set<SActor> addActors(Set<SActor> actors) throws SActorCreationException;\n\n    /**\n     * Get actor by actor id\n     * If the actor by a given actorId is not found, it will throw SActorNotFoundException\n     *\n     * @param actorId\n     *        Id of actor\n     * @return SActor object response to the given actorId\n     * @throws SActorNotFoundException\n     * @throws SBonitaReadException\n     */\n    SActor getActor(long actorId) throws SActorNotFoundException, SBonitaReadException;\n\n    /**\n     * Get actor by actor name and scope id\n     * If the actor by a given actorName and scopeId is not found, it will throw SActorNotFoundException\n     *\n     * @param actorName\n     *        Name of actor\n     * @param scopeId\n     *        Id of scope, it can be processDefinitionId\n     * @return SActor object corresponding to the given actorName and scopeId\n     * @throws SActorNotFoundException\n     *         Error thrown if no actor have an id corresponding to the parameter.\n     */\n    SActor getActor(String actorName, long scopeId) throws SActorNotFoundException;\n\n    /**\n     * Get a list of all actors for the id specified user in certain scopes specified by scopeIds\n     *\n     * @param scopeIds\n     *        Ids of scope, it can be processDefinitionId\n     * @param userId\n     *        Id of user which is added to actor\n     * @return The list of SActor Objects\n     * @throws SBonitaReadException\n     */\n    List<SActor> getActors(Set<Long> scopeIds, Long userId) throws SBonitaReadException;\n\n    /**\n     * Update actor by its id\n     * If the actor by a given actorId is not found, it will throw processDefinitionNotFountExcetion\n     *\n     * @param actorId\n     *        Id of actor\n     * @param updateDescriptor\n     *        Update description\n     * @return the updated actor\n     * @throws SActorNotFoundException\n     *         Error thrown if no actor have an id corresponding to the parameter actorId.\n     * @throws SActorUpdateException\n     *         Error thrown if has exceptions while try to update an actor\n     * @throws SBonitaReadException\n     */\n    SActor updateActor(long actorId, EntityUpdateDescriptor updateDescriptor)\n            throws SActorNotFoundException, SActorUpdateException, SBonitaReadException;\n\n    /**\n     * Delete actors in the id specified scope\n     *\n     * @param scopeId\n     *        Id of scope, it can be processDefinitionId\n     * @throws SActorDeletionException\n     *         Error thrown if has exceptions while try to delete actors\n     */\n    void deleteActors(long scopeId) throws SActorDeletionException;\n\n    /**\n     * Add the userId specified user to the actorId specified actor\n     *\n     * @param actorId\n     *        Id of actor\n     * @param userId\n     *        Id of user\n     * @return SActorMember object\n     * @throws SActorNotFoundException\n     *         Error thrown if no actor have an id corresponding to the parameter actorId.\n     * @throws SActorMemberCreationException\n     *         Error thrown if has exceptions while try to create the SActorMember object\n     */\n    SActorMember addUserToActor(long actorId, long userId)\n            throws SActorNotFoundException, SActorMemberCreationException;\n\n    /**\n     * Add the groupId specified group to the actorId specified actor\n     *\n     * @param actorId\n     *        Id of actor\n     * @param groupId\n     *        Id of group\n     * @return SActorMember object\n     * @throws SActorNotFoundException\n     *         Error thrown if no actor have an id corresponding to the parameter actorId.\n     * @throws SActorMemberCreationException\n     *         Error thrown if has exceptions while try to create the SActorMember object\n     */\n    SActorMember addGroupToActor(long actorId, long groupId)\n            throws SActorNotFoundException, SActorMemberCreationException;\n\n    /**\n     * Add the roleId specified role to the actorId specified actor\n     *\n     * @param actorId\n     *        Id of actor\n     * @param roleId\n     *        Id of role\n     * @return SActorMember object\n     * @throws SActorNotFoundException\n     *         Error thrown if no actor have an id corresponding to the parameter actorId.\n     * @throws SActorMemberCreationException\n     *         Error thrown if has exceptions while try to create the SActorMember object\n     */\n    SActorMember addRoleToActor(long actorId, long roleId)\n            throws SActorNotFoundException, SActorMemberCreationException;\n\n    /**\n     * Add the roleId and groupId specified relationship to the actorId specified actor\n     *\n     * @param actorId\n     *        Id of actor\n     * @param roleId\n     *        Id of role\n     * @param groupId\n     *        Id of group\n     * @return SActorMember object\n     * @throws SActorNotFoundException\n     *         Error thrown if no actor have an id corresponding to the parameter actorId.\n     * @throws SActorMemberCreationException\n     *         Error thrown if has exceptions while try to create the SActorMember object\n     */\n    SActorMember addRoleAndGroupToActor(long actorId, long roleId, long groupId)\n            throws SActorNotFoundException, SActorMemberCreationException;\n\n    /**\n     * Remove actorMember for the give actorMemberId\n     *\n     * @param actorMemberId\n     *        Id of actorMember\n     * @throws SActorMemberNotFoundException\n     *         Error thrown if no actorMember have an id corresponding to the parameter actorMemberId.\n     * @throws SActorMemberDeletionException\n     *         Error thrown if has exceptions while try to remove the SActorMember object\n     */\n    SActorMember deleteActorMember(long actorMemberId)\n            throws SActorMemberNotFoundException, SActorMemberDeletionException;\n\n    /**\n     * Remove an actor member\n     *\n     * @param actorMember\n     *        the actorMember to remove\n     * @throws SActorMemberDeletionException\n     *         Error thrown if has exceptions while try to remove the SActorMember object\n     */\n    void deleteActorMember(final SActorMember actorMember) throws SActorMemberDeletionException;\n\n    /**\n     * Get list of SActorMember objects by pagination\n     *\n     * @param actorId\n     *        Id of actor\n     * @param index\n     *        Index of the record to be retrieved from. First record has pageNumber 0.\n     * @param numberOfActorMembers\n     *        Number of result we want to get. Maximum number of result returned.\n     * @return List of SActorMember objects, ordered by id ascending\n     * @throws SBonitaReadException\n     */\n    List<SActorMember> getActorMembers(long actorId, int index, int numberOfActorMembers) throws SBonitaReadException;\n\n    /**\n     * Get number of ActorMembers for give actorId\n     *\n     * @param actorId\n     *        Id of actor\n     * @return the number of ActorMembers\n     * @throws SBonitaReadException\n     */\n    long getNumberOfActorMembers(long actorId) throws SBonitaReadException;\n\n    /**\n     * Get a list of SActorMember objects for given userId\n     *\n     * @param userId\n     *        Id of user\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has pageNumber 0.\n     * @param numberOfActorMembers\n     *        Number of result we want to get. Maximum number of result returned.\n     * @return List of SActorMember objects, ordered by id ascending\n     * @throws SBonitaReadException\n     */\n    List<SActorMember> getActorMembersOfUser(long userId, int fromIndex, int numberOfActorMembers)\n            throws SBonitaReadException;\n\n    /**\n     * Get a list of SActorMember objects for given groupId\n     *\n     * @param groupId\n     *        Id of group\n     * @return a list of SActorMember objects, ordered by id ascending\n     * @throws SBonitaReadException\n     */\n    List<SActorMember> getActorMembersOfGroup(long groupId, int index, int numberOfActorMembers)\n            throws SBonitaReadException;\n\n    /**\n     * Get a list of SActorMember objects for given roleId\n     *\n     * @param roleId\n     *        Id of role\n     * @return a list of SActorMember objects, ordered by id ascending\n     * @throws SBonitaReadException\n     */\n    List<SActorMember> getActorMembersOfRole(long roleId, int fromIndex, int numberOfActorMembers)\n            throws SBonitaReadException;\n\n    /**\n     * Is a specified user allowed to start a process?\n     *\n     * @param userId\n     *        Id of user\n     * @param processDefinitionId Id of processDefinition\n     * @return a list of SActor objects\n     * @throws SBonitaReadException\n     */\n    boolean canUserStartProcessDefinition(long userId, long processDefinitionId) throws SBonitaReadException;\n\n    /**\n     * Get a list of actors by the given list of actor ids\n     *\n     * @param actorIds\n     *        the list of actor ids to retrieve\n     * @return a list of actors\n     * @throws SActorNotFoundException\n     * @throws SBonitaReadException\n     */\n    List<SActor> getActors(List<Long> actorIds) throws SActorNotFoundException, SBonitaReadException;\n\n    /**\n     * Get paginated actors\n     *\n     * @param processDefinitionId\n     *        identifier of process definition\n     * @return the list of actors\n     * @throws SBonitaReadException\n     */\n    List<SActor> getActors(long processDefinitionId, QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * Return the number of users corresponding to an actor\n     *\n     * @param actorId\n     *        the id of the actor to retrieve the users from\n     * @return Number of users mapped to actor\n     */\n    long getNumberOfUsersOfActor(long actorId);\n\n    /**\n     * Get the number of roles of an actor\n     *\n     * @param actorId\n     *        the id corresponding to an actor\n     * @return Number of roles mapped to actor\n     */\n    long getNumberOfRolesOfActor(long actorId);\n\n    /**\n     * Get the number of groups corresponding to an actor\n     *\n     * @param actorId\n     *        the id of the actor to retrieve the groups from\n     * @return Number of groups mapped to actor\n     * @throws RuntimeException\n     */\n    long getNumberOfGroupsOfActor(long actorId) throws RuntimeException;\n\n    /**\n     * Get the number of memberships (role and group) of an actor\n     *\n     * @param actorId\n     *        the id of the actor to retrieve the memberships from\n     * @return Number of memberships mapped to actor\n     */\n    long getNumberOfMembershipsOfActor(long actorId);\n\n    /**\n     * Delete all actor members for the connected tenant\n     *\n     * @throws SActorMemberDeletionException\n     * @since 6.1\n     */\n    void deleteAllActorMembers() throws SActorMemberDeletionException;\n\n    List<Long> getPossibleUserIdsOfActorId(long actorId, int startIndex, int maxResults) throws SBonitaReadException;\n\n    /**\n     * Get the actor member\n     *\n     * @param actorId\n     *        The identifier of the actor\n     * @param userId\n     *        The identifier of the user\n     * @param groupId\n     *        The identifier of the group\n     * @param roleId\n     *        The identifier of the role\n     * @return The corresponding actor member\n     * @throws SBonitaReadException\n     * @since 6.3\n     */\n    SActorMember getActorMember(long actorId, long userId, long groupId, long roleId) throws SBonitaReadException;\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/SActorCreationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.actor.mapping;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SActorCreationException extends SBonitaException {\n\n    private static final long serialVersionUID = -3098152902768681292L;\n\n    public SActorCreationException(final String message) {\n        super(message);\n    }\n\n    public SActorCreationException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SActorCreationException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/SActorDeletionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.actor.mapping;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SActorDeletionException extends SBonitaException {\n\n    private static final long serialVersionUID = -7837485664040613405L;\n\n    public SActorDeletionException(final String message) {\n        super(message);\n    }\n\n    public SActorDeletionException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SActorDeletionException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/SActorMemberAlreadyExistsException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.actor.mapping;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SActorMemberAlreadyExistsException extends SBonitaException {\n\n    private static final long serialVersionUID = 4634806521985418000L;\n\n    public SActorMemberAlreadyExistsException(final String message) {\n        super(message);\n    }\n\n    public SActorMemberAlreadyExistsException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SActorMemberAlreadyExistsException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/SActorMemberCreationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.actor.mapping;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SActorMemberCreationException extends SBonitaException {\n\n    private static final long serialVersionUID = -6902253339463831324L;\n\n    public SActorMemberCreationException(final String message) {\n        super(message);\n    }\n\n    public SActorMemberCreationException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SActorMemberCreationException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/SActorMemberDeletionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.actor.mapping;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SActorMemberDeletionException extends SBonitaException {\n\n    private static final long serialVersionUID = 7422218038178486137L;\n\n    public SActorMemberDeletionException(final String message) {\n        super(message);\n    }\n\n    public SActorMemberDeletionException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SActorMemberDeletionException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/SActorMemberNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.actor.mapping;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SActorMemberNotFoundException extends SBonitaException {\n\n    private static final long serialVersionUID = -2713597313687259749L;\n\n    public SActorMemberNotFoundException(final String message) {\n        super(message);\n    }\n\n    public SActorMemberNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SActorMemberNotFoundException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/SActorNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.actor.mapping;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SActorNotFoundException extends SBonitaException {\n\n    private static final long serialVersionUID = 6582240316506445320L;\n\n    public SActorNotFoundException(final String message) {\n        super(message);\n    }\n\n    public SActorNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SActorNotFoundException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/SActorUpdateException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.actor.mapping;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SActorUpdateException extends SBonitaException {\n\n    private static final long serialVersionUID = -8419335214835063318L;\n\n    public SActorUpdateException(final String message) {\n        super(message);\n    }\n\n    public SActorUpdateException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SActorUpdateException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/impl/ActorMappingServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.actor.mapping.impl;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.actor.mapping.SActorCreationException;\nimport org.bonitasoft.engine.actor.mapping.SActorDeletionException;\nimport org.bonitasoft.engine.actor.mapping.SActorMemberCreationException;\nimport org.bonitasoft.engine.actor.mapping.SActorMemberDeletionException;\nimport org.bonitasoft.engine.actor.mapping.SActorMemberNotFoundException;\nimport org.bonitasoft.engine.actor.mapping.SActorNotFoundException;\nimport org.bonitasoft.engine.actor.mapping.SActorUpdateException;\nimport org.bonitasoft.engine.actor.mapping.model.SActor;\nimport org.bonitasoft.engine.actor.mapping.model.SActorLogBuilder;\nimport org.bonitasoft.engine.actor.mapping.model.SActorLogBuilderFactory;\nimport org.bonitasoft.engine.actor.mapping.model.SActorMember;\nimport org.bonitasoft.engine.actor.mapping.persistence.SelectDescriptorBuilder;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.SIdentityException;\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity;\nimport org.bonitasoft.engine.queriablelogger.model.builder.ActionType;\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteAllRecord;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\n\n/**\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class ActorMappingServiceImpl implements ActorMappingService {\n\n    private static final int BATCH_SIZE = 100;\n\n    private final ReadPersistenceService persistenceService;\n\n    private final Recorder recorder;\n\n    private final EventService eventService;\n\n    private final QueriableLoggerService queriableLoggerService;\n\n    private final IdentityService identityService;\n\n    public ActorMappingServiceImpl(final ReadPersistenceService persistenceService, final Recorder recorder,\n            final EventService eventService,\n            final QueriableLoggerService queriableLoggerService, final IdentityService identityService) {\n        this.persistenceService = persistenceService;\n        this.recorder = recorder;\n        this.eventService = eventService;\n        this.queriableLoggerService = queriableLoggerService;\n        this.identityService = identityService;\n    }\n\n    @Override\n    public Set<SActor> addActors(final Set<SActor> actors) throws SActorCreationException {\n        final Set<SActor> sActors = new HashSet<SActor>();\n        for (final SActor actor : actors) {\n            sActors.add(addActor(actor));\n        }\n        return sActors;\n    }\n\n    @Override\n    public SActor addActor(final SActor actor) throws SActorCreationException {\n        final SActorLogBuilder logBuilder = getQueriableLog(ActionType.CREATED, \"Creating a new actor\");\n        try {\n            recorder.recordInsert(new InsertRecord(actor), ACTOR);\n            initiateLogBuilder(actor.getId(), SQueriableLog.STATUS_OK, logBuilder, \"addActor\");\n            return actor;\n        } catch (final SRecorderException re) {\n            initiateLogBuilder(actor.getId(), SQueriableLog.STATUS_FAIL, logBuilder, \"addActor\");\n            throw new SActorCreationException(re);\n        }\n    }\n\n    private SActorLogBuilder getQueriableLog(final ActionType actionType, final String message) {\n        final SActorLogBuilder logBuilder = BuilderFactory.get(SActorLogBuilderFactory.class).createNewInstance();\n        this.initializeLogBuilder(logBuilder, message);\n        this.updateLog(actionType, logBuilder);\n        return logBuilder;\n    }\n\n    private <T extends SLogBuilder> void initializeLogBuilder(final T logBuilder, final String message) {\n        logBuilder.actionStatus(SQueriableLog.STATUS_FAIL).severity(SQueriableLogSeverity.INTERNAL).rawMessage(message);\n    }\n\n    private <T extends HasCRUDEAction> void updateLog(final ActionType actionType, final T logBuilder) {\n        logBuilder.setActionType(actionType);\n    }\n\n    @Override\n    public SActor getActor(final long actorId) throws SActorNotFoundException, SBonitaReadException {\n        final SelectByIdDescriptor<SActor> selectByIdDescriptor = SelectDescriptorBuilder.getActor(actorId);\n        final SActor actor = persistenceService.selectById(selectByIdDescriptor);\n        if (actor == null) {\n            throw new SActorNotFoundException(actorId + \" does not refer to any actor\");\n        }\n        return actor;\n    }\n\n    @Override\n    public long getNumberOfUsersOfActor(final long actorId) {\n        final SelectOneDescriptor<Long> descriptor = SelectDescriptorBuilder.getNumberOfUsersOfActor(actorId);\n        try {\n            return persistenceService.selectOne(descriptor);\n        } catch (final SBonitaReadException bre) {\n            throw new RuntimeException(bre);\n        }\n    }\n\n    @Override\n    public long getNumberOfRolesOfActor(final long actorId) {\n        final SelectOneDescriptor<Long> descriptor = SelectDescriptorBuilder.getNumberOfRolesOfActor(actorId);\n        try {\n            return persistenceService.selectOne(descriptor);\n        } catch (final SBonitaReadException bre) {\n            throw new RuntimeException(bre);\n        }\n    }\n\n    @Override\n    public long getNumberOfGroupsOfActor(final long actorId) {\n        final SelectOneDescriptor<Long> descriptor = SelectDescriptorBuilder.getNumberOfGroupsOfActor(actorId);\n        try {\n            return persistenceService.selectOne(descriptor);\n        } catch (final SBonitaReadException bre) {\n            throw new RuntimeException(bre);\n        }\n    }\n\n    @Override\n    public long getNumberOfMembershipsOfActor(final long actorId) {\n        final SelectOneDescriptor<Long> descriptor = SelectDescriptorBuilder.getNumberOfMembershipsOfActor(actorId);\n        try {\n            return persistenceService.selectOne(descriptor);\n        } catch (final SBonitaReadException bre) {\n            throw new RuntimeException(bre);\n        }\n    }\n\n    @Override\n    public List<SActor> getActors(final List<Long> actorIds) throws SBonitaReadException {\n        if (actorIds == null || actorIds.isEmpty()) {\n            return Collections.emptyList();\n        }\n        return persistenceService.selectList(SelectDescriptorBuilder.getElementsByIds(SActor.class, \"Actor\", actorIds));\n    }\n\n    @Override\n    public SActor getActor(final String actorName, final long scopeId) throws SActorNotFoundException {\n        final SelectOneDescriptor<SActor> selectOneDescriptor = SelectDescriptorBuilder.getActor(actorName, scopeId);\n        try {\n            final SActor actor = persistenceService.selectOne(selectOneDescriptor);\n            if (actor == null) {\n                throw new SActorNotFoundException(\n                        \"Actor not found with name: \" + actorName + \" of scopeId: \" + scopeId);\n            }\n            return actor;\n        } catch (final SBonitaReadException bre) {\n            throw new SActorNotFoundException(bre);\n        }\n    }\n\n    @Override\n    public SActor updateActor(final long actorId, final EntityUpdateDescriptor descriptor)\n            throws SActorNotFoundException, SActorUpdateException,\n            SBonitaReadException {\n        final SActor actor = getActor(actorId);\n        final SActorLogBuilder logBuilder = getQueriableLog(ActionType.UPDATED, \"Updating an actor\");\n        try {\n            recorder.recordUpdate(UpdateRecord.buildSetFields(actor, descriptor), ACTOR);\n            initiateLogBuilder(actorId, SQueriableLog.STATUS_OK, logBuilder, \"updateActor\");\n        } catch (final SRecorderException e) {\n            initiateLogBuilder(actorId, SQueriableLog.STATUS_FAIL, logBuilder, \"updateActor\");\n            throw new SActorUpdateException(e);\n        }\n        return actor;\n    }\n\n    @Override\n    public void deleteActors(final long scopeId) throws SActorDeletionException {\n        try {\n            final QueryOptions queryOptions = new QueryOptions(0, 100, SActor.class, \"id\", OrderByType.ASC);\n            List<SActor> actors = getActors(scopeId, queryOptions);\n            while (!actors.isEmpty()) {\n                for (final SActor actor : actors) {\n                    // First delete its members:\n                    deleteActorMembers(actor);\n                    // then the actor itself:\n                    deleteActor(actor);\n                }\n                actors = getActors(scopeId, queryOptions);\n            }\n        } catch (final SBonitaReadException bre) {\n            throw new SActorDeletionException(bre);\n        } catch (final SActorMemberDeletionException e) {\n            throw new SActorDeletionException(e);\n        }\n    }\n\n    private void deleteActorMembers(final SActor actor) throws SBonitaReadException, SActorMemberDeletionException {\n        List<SActorMember> actorMembers;\n        do {\n            actorMembers = getActorMembers(actor.getId(), 0, BATCH_SIZE);\n            for (final SActorMember sActorMember : actorMembers) {\n                deleteActorMember(sActorMember);\n            }\n        } while (actorMembers.size() > 0);\n    }\n\n    private void deleteActor(final SActor actor) throws SActorDeletionException {\n        final SActorLogBuilder logBuilder = getQueriableLog(ActionType.DELETED, \"Deleting an actor\");\n        try {\n            recorder.recordDelete(new DeleteRecord(actor), ACTOR);\n            initiateLogBuilder(actor.getId(), SQueriableLog.STATUS_OK, logBuilder, \"deleteActor\");\n        } catch (final SRecorderException re) {\n            initiateLogBuilder(actor.getId(), SQueriableLog.STATUS_FAIL, logBuilder, \"deleteActor\");\n            throw new SActorDeletionException(re);\n        }\n    }\n\n    @Override\n    public List<SActor> getActors(final Set<Long> scopeIds, final Long userId) throws SBonitaReadException {\n        final SelectListDescriptor<SActor> descriptor = SelectDescriptorBuilder.getFullActorsListOfUser(scopeIds,\n                userId);\n        return persistenceService.selectList(descriptor);\n    }\n\n    @Override\n    public SActorMember addUserToActor(final long actorId, final long userId) throws SActorMemberCreationException {\n        final SActorMember actorMember = new SActorMember();\n        actorMember.setActorId(actorId);\n        actorMember.setUserId(userId);\n        return addActorMember(actorMember);\n    }\n\n    private SActorMember addActorMember(final SActorMember actorMember) throws SActorMemberCreationException {\n        final SActorLogBuilder logBuilder = getQueriableLog(ActionType.CREATED, \"Creating a new actor member\");\n        try {\n            recorder.recordInsert(new InsertRecord(actorMember), ACTOR_MEMBER);\n            initiateLogBuilder(actorMember.getId(), SQueriableLog.STATUS_OK, logBuilder, \"addActorMember\");\n            return actorMember;\n        } catch (final SRecorderException re) {\n            initiateLogBuilder(actorMember.getId(), SQueriableLog.STATUS_FAIL, logBuilder, \"addActorMember\");\n            throw new SActorMemberCreationException(re);\n        }\n    }\n\n    @Override\n    public SActorMember addGroupToActor(final long actorId, final long groupId)\n            throws SActorNotFoundException, SActorMemberCreationException {\n        try {\n            final SActorMember addActorMember = addOnlyThisGroupToActor(actorId, groupId);\n            int i = 0;\n            List<SGroup> groupChildren;\n            do {\n                groupChildren = identityService.getGroupChildren(groupId, i, BATCH_SIZE);\n                for (final SGroup child : groupChildren) {\n                    // Only insert if sub-group not mapped already to this Actor :\n                    if (getNumberOfActorMembersOfGroupWithActor(child.getId(), actorId) == 0) {\n                        addGroupToActor(actorId, child.getId());\n                    }\n                }\n                i += BATCH_SIZE;\n            } while (groupChildren.size() == BATCH_SIZE);\n            return addActorMember;\n        } catch (final SIdentityException e) {\n            throw new SActorMemberCreationException(e);\n        } catch (final SBonitaReadException e) {\n            throw new SActorMemberCreationException(e);\n        }\n    }\n\n    private SActorMember addOnlyThisGroupToActor(final long actorId, final long groupId)\n            throws SActorMemberCreationException {\n        final SActorMember actorMember = new SActorMember();\n        actorMember.setActorId(actorId);\n        actorMember.setGroupId(groupId);\n        return addActorMember(actorMember);\n    }\n\n    @Override\n    public SActorMember addRoleToActor(final long actorId, final long roleId) throws SActorMemberCreationException {\n        final SActorMember actorMember = new SActorMember();\n        actorMember.setActorId(actorId);\n        actorMember.setRoleId(roleId);\n        return addActorMember(actorMember);\n    }\n\n    @Override\n    public SActorMember addRoleAndGroupToActor(final long actorId, final long roleId, final long groupId)\n            throws SActorNotFoundException,\n            SActorMemberCreationException {\n        try {\n            final SActorMember addActorMember = addOnlyThisRoleAndGroupToActor(actorId, roleId, groupId);\n            int i = 0;\n            List<SGroup> groupChildren;\n            do {\n                groupChildren = identityService.getGroupChildren(groupId, i, BATCH_SIZE);\n                for (final SGroup child : groupChildren) {\n                    addRoleAndGroupToActor(actorId, roleId, child.getId());\n                }\n                i += BATCH_SIZE;\n            } while (groupChildren.size() == BATCH_SIZE);\n            return addActorMember;\n        } catch (final SIdentityException e) {\n            throw new SActorMemberCreationException(e);\n        }\n    }\n\n    private SActorMember addOnlyThisRoleAndGroupToActor(final long actorId, final long roleId, final long groupId)\n            throws SActorMemberCreationException {\n        final SActorMember actorMember = new SActorMember();\n        actorMember.setActorId(actorId);\n        actorMember.setRoleId(roleId);\n        actorMember.setGroupId(groupId);\n        return addActorMember(actorMember);\n    }\n\n    @Override\n    public SActorMember deleteActorMember(final long actorMemberId)\n            throws SActorMemberNotFoundException, SActorMemberDeletionException {\n        final SActorLogBuilder logBuilder = getQueriableLog(ActionType.DELETED, \"Deleting an actor member\");\n        try {\n            final SActorMember actorMember = getActorMember(actorMemberId);\n            deleteActorMember(actorMember);\n            return actorMember;\n        } catch (final SBonitaReadException e) {\n            initiateLogBuilder(actorMemberId, SQueriableLog.STATUS_FAIL, logBuilder, \"removeActorMember\");\n            throw new SActorMemberDeletionException(e);\n        }\n    }\n\n    @Override\n    public void deleteActorMember(final SActorMember sActorMember) throws SActorMemberDeletionException {\n        final SActorLogBuilder logBuilder = getQueriableLog(ActionType.DELETED, \"Deleting an actor member\");\n        final long actorMemberId = sActorMember.getId();\n        try {\n            recorder.recordDelete(new DeleteRecord(sActorMember), ACTOR_MEMBER);\n            initiateLogBuilder(actorMemberId, SQueriableLog.STATUS_OK, logBuilder, \"removeActorMember\");\n        } catch (final SRecorderException re) {\n            initiateLogBuilder(actorMemberId, SQueriableLog.STATUS_FAIL, logBuilder, \"removeActorMember\");\n            throw new SActorMemberDeletionException(re);\n        }\n    }\n\n    private SActorMember getActorMember(final long actorMemberId)\n            throws SActorMemberNotFoundException, SBonitaReadException {\n        final SelectByIdDescriptor<SActorMember> selectByIdDescriptor = SelectDescriptorBuilder\n                .getActorMember(actorMemberId);\n        final SActorMember actor = persistenceService.selectById(selectByIdDescriptor);\n        if (actor == null) {\n            throw new SActorMemberNotFoundException(actorMemberId + \" does not refer to any actor member\");\n        }\n        return actor;\n    }\n\n    @Override\n    public SActorMember getActorMember(final long actorId, final long userId, final long groupId, final long roleId)\n            throws SBonitaReadException {\n        final SelectOneDescriptor<SActorMember> descriptor = SelectDescriptorBuilder.getActorMember(actorId, userId,\n                groupId, roleId);\n        return persistenceService.selectOne(descriptor);\n    }\n\n    @Override\n    public long getNumberOfActorMembers(final long actorId) throws SBonitaReadException {\n        final SelectOneDescriptor<Long> descriptor = SelectDescriptorBuilder.getNumberOfActorMembers(actorId);\n        return persistenceService.selectOne(descriptor);\n    }\n\n    @Override\n    public List<SActorMember> getActorMembers(final long actorId, final int fromIndex, final int numberOfActorMembers)\n            throws SBonitaReadException {\n        final SelectListDescriptor<SActorMember> descriptor = SelectDescriptorBuilder.getActorMembers(actorId,\n                fromIndex, numberOfActorMembers);\n        return persistenceService.selectList(descriptor);\n    }\n\n    @Override\n    public List<SActorMember> getActorMembersOfUser(final long userId, final int fromIndex,\n            final int numberOfActorMembers) throws SBonitaReadException {\n        final SelectListDescriptor<SActorMember> descriptor = SelectDescriptorBuilder.getActorMembersOfUser(userId,\n                fromIndex, numberOfActorMembers);\n        return persistenceService.selectList(descriptor);\n    }\n\n    @Override\n    public List<SActorMember> getActorMembersOfGroup(final long groupId, final int fromIndex,\n            final int numberOfActorMembers) throws SBonitaReadException {\n        final SelectListDescriptor<SActorMember> descriptor = SelectDescriptorBuilder.getActorMembersOfGroup(groupId,\n                fromIndex, numberOfActorMembers);\n        return persistenceService.selectList(descriptor);\n    }\n\n    private long getNumberOfActorMembersOfGroupWithActor(final long groupId, final long actorId)\n            throws SBonitaReadException {\n        final SelectOneDescriptor<Long> descriptor = SelectDescriptorBuilder\n                .getNumberOfActorMembersOfGroupWithActor(groupId, actorId);\n        return persistenceService.selectOne(descriptor);\n    }\n\n    @Override\n    public List<SActorMember> getActorMembersOfRole(final long roleId, final int fromIndex,\n            final int numberOfActorMembers) throws SBonitaReadException {\n        final SelectListDescriptor<SActorMember> descriptor = SelectDescriptorBuilder.getActorMembersOfRole(roleId,\n                fromIndex, numberOfActorMembers);\n        return persistenceService.selectList(descriptor);\n    }\n\n    @Override\n    public boolean canUserStartProcessDefinition(final long userId, final long processDefinitionId)\n            throws SBonitaReadException {\n        final SelectListDescriptor<Long> descriptor = SelectDescriptorBuilder.getActorMembersInitiatorForProcess(\n                processDefinitionId, 0,\n                QueryOptions.UNLIMITED_NUMBER_OF_RESULTS);\n        final List<Long> actorMembersForProcess = persistenceService.selectList(descriptor);\n        final int BATCH_SIZE = 80;\n        boolean found = false;\n        while (!found && actorMembersForProcess.size() > 0) {\n            found = 0 < persistenceService\n                    .selectOne(SelectDescriptorBuilder.getNumberOfUserMembersForUserOrManagerForActorMembers(userId,\n                            retrieveFirstResultsAndRemoveFromOriginalList(BATCH_SIZE, actorMembersForProcess)));\n        }\n        return found;\n    }\n\n    private List<Long> retrieveFirstResultsAndRemoveFromOriginalList(int howMany, List<Long> ids) {\n        List<Long> subList = new ArrayList<Long>(howMany);\n        for (int i = 0; i < howMany && ids.size() > 0; i++) {\n            subList.add(ids.remove(0));\n        }\n        return subList;\n    }\n\n    @Override\n    public List<SActor> getActors(final long processDefinitionId, final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        final SelectListDescriptor<SActor> descriptor = SelectDescriptorBuilder.getActorsOfScope(processDefinitionId,\n                queryOptions);\n        return persistenceService.selectList(descriptor);\n    }\n\n    private void initiateLogBuilder(final long objectId, final int sQueriableLogStatus,\n            final SPersistenceLogBuilder logBuilder, final String callerMethodName) {\n        logBuilder.actionScope(String.valueOf(objectId));\n        logBuilder.actionStatus(sQueriableLogStatus);\n        logBuilder.objectId(objectId);\n        final SQueriableLog log = logBuilder.build();\n        if (queriableLoggerService.isLoggable(log.getActionType(), log.getSeverity())) {\n            queriableLoggerService.log(this.getClass().getName(), callerMethodName, log);\n        }\n    }\n\n    @Override\n    public void deleteAllActorMembers() throws SActorMemberDeletionException {\n        try {\n            final DeleteAllRecord record = new DeleteAllRecord(SActorMember.class, null);\n            recorder.recordDeleteAll(record);\n        } catch (final SRecorderException e) {\n            throw new SActorMemberDeletionException(\"Can't delete all actor members.\", e);\n        }\n    }\n\n    @Override\n    public List<Long> getPossibleUserIdsOfActorId(final long actorId, final int startIndex, final int maxResults)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"actorId\", (Object) actorId);\n        final SelectListDescriptor<Long> descriptor = new SelectListDescriptor<Long>(\"getPossibleUserIdsOfActorId\",\n                parameters, SActor.class, new QueryOptions(\n                        startIndex, maxResults));\n        return persistenceService.selectList(descriptor);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/impl/SActorLogBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.actor.mapping.impl;\n\nimport org.bonitasoft.engine.actor.mapping.model.SActorLogBuilder;\nimport org.bonitasoft.engine.actor.mapping.model.SActorLogBuilderFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SActorLogBuilderFactoryImpl extends CRUDELogBuilderFactory implements SActorLogBuilderFactory {\n\n    public SActorLogBuilder createNewInstance() {\n        return new SActorLogBuilderImpl();\n    }\n\n    @Override\n    public String getObjectIdKey() {\n        return \"numericIndex1\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/impl/SActorLogBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.actor.mapping.impl;\n\nimport org.bonitasoft.engine.actor.mapping.model.SActorLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SActorLogBuilderImpl extends CRUDELogBuilder implements SActorLogBuilder {\n\n    private static final int ACTOR_INDEX = 0;\n\n    @Override\n    public SPersistenceLogBuilder objectId(final long objectId) {\n        queriableLogBuilder.numericIndex(ACTOR_INDEX, objectId);\n        return this;\n    }\n\n    @Override\n    protected String getActionTypePrefix() {\n        return \"ACTOR\";\n    }\n\n    @Override\n    protected void checkExtraRules(final SQueriableLog log) {\n        if (log.getActionStatus() != SQueriableLog.STATUS_FAIL && log.getNumericIndex(ACTOR_INDEX) == 0L) {\n            throw new MissingMandatoryFieldsException(\"Some mandatory fields are missing: \" + \"Actor Id\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/model/SActor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.actor.mapping.model;\n\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@Builder\n@Entity\n@Table(name = \"actor\")\npublic class SActor implements PersistentObject {\n\n    @Id\n    private long id;\n    @Column\n    private long scopeId;\n    @Column\n    private String name;\n    @Column\n    private String displayName;\n    @Column\n    private String description;\n    @Column\n    private boolean initiator;\n\n    public SActor(final String name, final long scopeId, final boolean initiator) {\n        this.name = name;\n        this.scopeId = scopeId;\n        this.initiator = initiator;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/model/SActorFilter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.actor.mapping.model;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface SActorFilter {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/model/SActorLogBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.actor.mapping.model;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface SActorLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/model/SActorLogBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.actor.mapping.model;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface SActorLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory {\n\n    @Override\n    SActorLogBuilder createNewInstance();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/model/SActorMember.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.actor.mapping.model;\n\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n@Data\n@NoArgsConstructor\n@Entity\n@Table(name = \"actormember\")\npublic class SActorMember implements PersistentObject {\n\n    @Id\n    private long id;\n    @Column\n    private long actorId;\n    @Column\n    private long userId = -1;\n    @Column\n    private long groupId = -1;\n    @Column\n    private long roleId = -1;\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/model/SActorUpdateBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.actor.mapping.model;\n\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface SActorUpdateBuilder {\n\n    EntityUpdateDescriptor done();\n\n    SActorUpdateBuilder updateDisplayName(final String displayName);\n\n    SActorUpdateBuilder updateDescription(final String description);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/model/SActorUpdateBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.actor.mapping.model;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface SActorUpdateBuilderFactory {\n\n    SActorUpdateBuilder createNewInstance();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/model/SMemberType.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.actor.mapping.model;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic enum SMemberType {\n\n    USER, ROLE, GROUP, MEMBERSHIP;\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/model/impl/SActorUpdateBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.actor.mapping.model.impl;\n\nimport org.bonitasoft.engine.actor.mapping.model.SActorUpdateBuilder;\nimport org.bonitasoft.engine.actor.mapping.model.SActorUpdateBuilderFactory;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SActorUpdateBuilderFactoryImpl implements SActorUpdateBuilderFactory {\n\n    public SActorUpdateBuilder createNewInstance() {\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        return new SActorUpdateBuilderImpl(descriptor);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/model/impl/SActorUpdateBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.actor.mapping.model.impl;\n\nimport org.bonitasoft.engine.actor.mapping.model.SActorUpdateBuilder;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SActorUpdateBuilderImpl implements SActorUpdateBuilder {\n\n    private final EntityUpdateDescriptor descriptor;\n\n    public SActorUpdateBuilderImpl(final EntityUpdateDescriptor descriptor) {\n        super();\n        this.descriptor = descriptor;\n    }\n\n    @Override\n    public EntityUpdateDescriptor done() {\n        return this.descriptor;\n    }\n\n    @Override\n    public SActorUpdateBuilder updateDisplayName(final String displayName) {\n        this.descriptor.addField(\"displayName\", displayName);\n        return this;\n    }\n\n    @Override\n    public SActorUpdateBuilder updateDescription(final String description) {\n        this.descriptor.addField(\"description\", description);\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/persistence/SelectDescriptorBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.actor.mapping.persistence;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.actor.mapping.model.SActor;\nimport org.bonitasoft.engine.actor.mapping.model.SActorMember;\nimport org.bonitasoft.engine.identity.model.SUserMembership;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class SelectDescriptorBuilder {\n\n    public static SelectByIdDescriptor<SActor> getActor(final long actorId) {\n        return new SelectByIdDescriptor<>(SActor.class, actorId);\n    }\n\n    public static SelectOneDescriptor<SActor> getActor(final String actorName, final long scopeId) {\n        final Map<String, Object> parameters = new HashMap<>();\n        parameters.put(\"name\", actorName);\n        parameters.put(\"scopeId\", scopeId);\n        return new SelectOneDescriptor<>(\"getActorFromNameAndScopeId\", parameters, SActor.class);\n    }\n\n    public static SelectByIdDescriptor<SActorMember> getActorMember(final long actorMemberId) {\n        return new SelectByIdDescriptor<>(SActorMember.class, actorMemberId);\n    }\n\n    public static SelectOneDescriptor<SActorMember> getActorMember(final long actorId, final long userId,\n            final long groupId, final long roleId) {\n        final Map<String, Object> parameters = new HashMap<>();\n        parameters.put(\"actorId\", actorId);\n        parameters.put(\"userId\", userId);\n        parameters.put(\"groupId\", groupId);\n        parameters.put(\"roleId\", roleId);\n        return new SelectOneDescriptor<>(\"getActorMember\", parameters, SActorMember.class);\n    }\n\n    public static SelectListDescriptor<SActorMember> getActorMembers(final int fromIndex, final int numberOfElements) {\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfElements);\n        final Map<String, Object> parameters = Collections.emptyMap();\n        return new SelectListDescriptor<>(\"getActorMembers\", parameters, SActorMember.class, queryOptions);\n    }\n\n    public static SelectListDescriptor<SActorMember> getActorMembers(final long actorId, final int fromIndex,\n            final int numberOfElements) {\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfElements);\n        final Map<String, Object> parameters = Collections.singletonMap(\"actorId\", (Object) actorId);\n        return new SelectListDescriptor<>(\"getActorMembersOfActor\", parameters, SActorMember.class,\n                queryOptions);\n    }\n\n    public static SelectListDescriptor<SActorMember> getActorMembersOfGroup(final long groupId, final int fromIndex,\n            final int numberOfActorMembers) {\n        final Map<String, Object> parameters = new HashMap<>();\n        parameters.put(\"groupId\", groupId);\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfActorMembers);\n        return new SelectListDescriptor<>(\"getActorMembersOfGroup\", parameters, SActorMember.class,\n                queryOptions);\n    }\n\n    public static SelectListDescriptor<SActorMember> getActorMembersOfRole(final long roleId, final int fromIndex,\n            final int numberOfActorMembers) {\n        final Map<String, Object> parameters = new HashMap<>();\n        parameters.put(\"roleId\", roleId);\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfActorMembers);\n        return new SelectListDescriptor<>(\"getActorMembersOfRole\", parameters, SActorMember.class,\n                queryOptions);\n    }\n\n    public static SelectListDescriptor<SActorMember> getActorMembersOfUser(final long userId, final int fromIndex,\n            final int numberOfActorMembers) {\n        final Map<String, Object> parameters = new HashMap<>();\n        parameters.put(\"userId\", userId);\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfActorMembers);\n        return new SelectListDescriptor<>(\"getActorMembersOfUser\", parameters, SActorMember.class,\n                queryOptions);\n    }\n\n    public static SelectListDescriptor<Long> getActorMembersInitiatorForProcess(final long processDefinitionId,\n            final int index,\n            final int numberPerPage) {\n        final Map<String, Object> parameters = new HashMap<>(1);\n        parameters.put(\"processDefinitionId\", processDefinitionId);\n        final QueryOptions queryOptions = new QueryOptions(index, numberPerPage, SActorMember.class, \"id\",\n                OrderByType.ASC);\n        return new SelectListDescriptor<>(\"getActorMembersInitiatorForProcess\", parameters, SActorMember.class,\n                queryOptions);\n    }\n\n    public static SelectOneDescriptor<Long> getNumberOfUserMembersForUserOrManagerForActorMembers(final long userId,\n            final List<Long> actorMemberIds) {\n        final Map<String, Object> parameters = new HashMap<>(2);\n        parameters.put(\"userId\", userId);\n        parameters.put(\"actorMemberIds\", actorMemberIds);\n        return new SelectOneDescriptor<>(\"getNumberOfUserMembersForUserOrManagerForActorMembers\", parameters,\n                SUserMembership.class);\n    }\n\n    public static SelectListDescriptor<SActor> getActorsOfScope(final long scopeId, final QueryOptions queryOptions) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"scopeId\", (Object) scopeId);\n        return new SelectListDescriptor<>(\"getActorsOfScope\", parameters, SActor.class, queryOptions);\n    }\n\n    public static <T extends PersistentObject> SelectListDescriptor<T> getElementsByIds(final Class<T> clazz,\n            final String elementName,\n            final Collection<Long> ids) {\n        final QueryOptions queryOptions = new QueryOptions(0, ids.size(), clazz, \"id\", OrderByType.ASC);\n        final Map<String, Object> parameters = Collections.singletonMap(\"ids\", (Object) ids);\n        return new SelectListDescriptor<>(\"get\" + elementName + \"sByIds\", parameters, clazz, queryOptions);\n    }\n\n    public static SelectListDescriptor<SActor> getFullActorsListOfUser(final Set<Long> scopeIds, final long userId) {\n        final Map<String, Object> parameters = new HashMap<>();\n        parameters.put(\"scopeIds\", scopeIds);\n        parameters.put(\"userId\", userId);\n        final QueryOptions queryOptions = new QueryOptions(SActor.class, \"name\", OrderByType.ASC);\n        return new SelectListDescriptor<>(\"getActorsOfUser\", parameters, SActor.class, queryOptions);\n    }\n\n    public static SelectOneDescriptor<Long> getNumberOfActorMembers(final long actorId) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"actorId\", (Object) actorId);\n        return new SelectOneDescriptor<>(\"getNumberOfActorMembersOfActor\", parameters, SActorMember.class);\n    }\n\n    public static SelectOneDescriptor<Long> getNumberOfActorMembersOfGroupWithActor(final long groupId,\n            final long actorId) {\n        final Map<String, Object> parameters = new HashMap<>(2);\n        parameters.put(\"groupId\", groupId);\n        parameters.put(\"actorId\", actorId);\n        return new SelectOneDescriptor<>(\"getNumberOfActorMembersOfGroupWithActor\", parameters, SActorMember.class);\n    }\n\n    public static SelectOneDescriptor<Long> getNumberOfGroupsOfActor(final long actorId) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"actorId\", (Object) actorId);\n        return new SelectOneDescriptor<>(\"getNumberOfGroupsOfActor\", parameters, SActorMember.class);\n    }\n\n    public static SelectOneDescriptor<Long> getNumberOfMembershipsOfActor(final long actorId) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"actorId\", (Object) actorId);\n        return new SelectOneDescriptor<>(\"getNumberOfMembershipsOfActor\", parameters, SActorMember.class);\n    }\n\n    public static SelectOneDescriptor<Long> getNumberOfRolesOfActor(final long actorId) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"actorId\", (Object) actorId);\n        return new SelectOneDescriptor<>(\"getNumberOfRolesOfActor\", parameters, SActorMember.class);\n    }\n\n    public static SelectOneDescriptor<Long> getNumberOfUsersOfActor(final long actorId) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"actorId\", (Object) actorId);\n        return new SelectOneDescriptor<>(\"getNumberOfUsersOfActor\", parameters, SActorMember.class);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-actor-mapping/src/main/resources/org/bonitasoft/engine/actor/mapping/model/impl/hibernate/actor.queries.hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\" \"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd\">\n<hibernate-mapping auto-import=\"false\">\n\n    <query name=\"getActorsByIds\">\n        SELECT actor\n        FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor\n        WHERE actor.id IN (:ids)\n    </query>\n\n    <query name=\"getActorFromNameAndScopeId\">\n        SELECT actor\n        FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor\n        WHERE actor.scopeId = :scopeId\n        AND actor.name = :name\n    </query>\n\n    <query name=\"getActorsOfScope\">\n        SELECT actor\n        FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor\n        WHERE actor.scopeId = :scopeId\n    </query>\n\n    <query name=\"getNumberOfActorMembersOfActor\">\n        SELECT COUNT(actormember.id)\n        FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n        WHERE actormember.actorId = :actorId\n    </query>\n\n    <query name=\"getActorMembersOfActor\">\n        SELECT actormember\n        FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n        WHERE actormember.actorId = :actorId\n        ORDER BY actormember.id ASC\n    </query>\n\n    <query name=\"getActorMember\">\n        SELECT actormember\n        FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n        WHERE actormember.actorId = :actorId\n        AND actormember.userId = :userId\n        AND actormember.groupId = :groupId\n        AND actormember.roleId = :roleId\n        ORDER BY actormember.id ASC\n    </query>\n\n    <query name=\"getActorMembers\">\n        SELECT actormember\n        FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n        ORDER BY actormember.id ASC\n    </query>\n\n    <query name=\"getActorsOfUser\">\n        SELECT actor\n        FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor,\n        org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n        WHERE actor.id = actormember.actorId\n        AND actor.scopeId IN (:scopeIds)\n        AND ( actormember.userId = :userId\n        OR actormember.id IN (\n        SELECT actormember.id\n        FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember,\n        org.bonitasoft.engine.identity.model.SUserMembership as um\n        WHERE um.userId = :userId\n        AND (\n        (actormember.groupId = um.groupId AND actormember.roleId = -1)\n        OR (actormember.roleId = um.roleId AND actormember.groupId = -1)\n        OR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId)\n        )\n        )\n        )\n    </query>\n\n\n    <query name=\"getNumberOfUsersOfActor\">\n        SELECT COUNT(user.id)\n        FROM org.bonitasoft.engine.identity.model.SUser AS user,\n        org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n        WHERE actormember.actorId = :actorId\n        AND user.id = actormember.userId\n    </query>\n\n    <query name=\"getNumberOfRolesOfActor\">\n        SELECT COUNT(role.id)\n        FROM org.bonitasoft.engine.identity.model.SRole AS role,\n        org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n        WHERE actormember.actorId = :actorId\n        AND role.id = actormember.roleId\n        AND actormember.groupId = -1\n    </query>\n\n    <query name=\"getNumberOfGroupsOfActor\">\n        SELECT COUNT(group_.id)\n        FROM org.bonitasoft.engine.identity.model.SGroup AS group_,\n        org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n        WHERE actormember.actorId = :actorId\n        AND group_.id = actormember.groupId\n        AND actormember.roleId = -1\n    </query>\n\n    <query name=\"getNumberOfMembershipsOfActor\">\n        SELECT COUNT(actormember.id)\n        FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n        WHERE actormember.actorId = :actorId\n        AND actormember.userId = -1\n        AND actormember.groupId &gt; -1\n        AND actormember.roleId &gt; -1\n    </query>\n\n    <query name=\"getActorMembersInitiatorForProcess\">\n        SELECT actormember.id\n        FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor,\n             org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n        WHERE actor.id = actormember.actorId\n        AND actor.scopeId =:processDefinitionId AND actor.initiator = TRUE\n    </query>\n\n    <query name=\"getNumberOfUserMembersForUserOrManagerForActorMembers\">\n        SELECT count(*)\n        FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS am,\n             org.bonitasoft.engine.identity.model.SUser AS user\n        WHERE (user.id = :userId OR user.managerUserId = :userId)\n        AND am.id IN (:actorMemberIds)\n        AND (\n            am.userId = user.id\n            OR exists (\n                SELECT um.id\n                FROM   org.bonitasoft.engine.identity.model.SUserMembership AS um\n                WHERE um.userId = user.id\n                AND (\n                       (am.groupId = um.groupId AND am.roleId &lt;= 0)\n                    OR (am.roleId = um.roleId AND am.groupId &lt;= 0)\n                    OR (am.groupId = um.groupId AND am.roleId = um.roleId)\n                )\n            )\n        )\n    </query>\n\n    <query name=\"getActorMembersOfUser\">\n        SELECT actormember\n        FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n        WHERE actormember.userId = :userId\n        ORDER BY actormember.id ASC\n    </query>\n\n    <query name=\"getActorMembersOfGroup\">\n        SELECT actormember\n        FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n        WHERE actormember.groupId = :groupId\n        ORDER BY actormember.id ASC\n    </query>\n\n    <query name=\"getNumberOfActorMembersOfGroupWithActor\">\n        SELECT count(actormember.id)\n        FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n        WHERE actormember.actorId = :actorId\n        AND actormember.groupId = :groupId\n        AND actormember.roleId = -1\n        AND actormember.userId = -1\n    </query>\n\n    <query name=\"getActorMembersOfRole\">\n        SELECT actormember\n        FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n        WHERE actormember.roleId = :roleId\n        ORDER BY actormember.id ASC\n    </query>\n\n    <sql-query name=\"getPossibleUserIdsOfActorId\">\n        <return-scalar column=\"id\" type=\"long\" />\n        SELECT user_.id, user_.username FROM (\n            SELECT user_.id, user_.username\n            FROM user_,\n            actormember,\n            actor\n            WHERE actor.id = :actorId\n            AND actor.id = actormember.actorId\n            AND actormember.userId = user_.id\n            UNION (\n                SELECT user_.id, user_.username\n                FROM user_,\n                actormember,\n                actor,\n                user_membership um\n                WHERE actor.id = :actorId\n                AND um.userId = user_.id\n                AND actor.id = actormember.actorId\n                AND (\n        (actormember.groupId = um.groupId AND actormember.roleId = -1)\n        OR (actormember.roleId = um.roleId AND actormember.groupId = -1)\n                    OR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId)\n                )\n            )\n        ) user_\n        ORDER BY user_.username\n    </sql-query>\n</hibernate-mapping>\n"
  },
  {
    "path": "bpm/bonita-core/bonita-actor-mapping/src/test/java/org/bonitasoft/engine/actor/mapping/impl/ActorMappingServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.actor.mapping.impl;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.assertj.core.api.Assertions;\nimport org.bonitasoft.engine.actor.mapping.SActorCreationException;\nimport org.bonitasoft.engine.actor.mapping.SActorDeletionException;\nimport org.bonitasoft.engine.actor.mapping.SActorMemberDeletionException;\nimport org.bonitasoft.engine.actor.mapping.SActorNotFoundException;\nimport org.bonitasoft.engine.actor.mapping.SActorUpdateException;\nimport org.bonitasoft.engine.actor.mapping.model.SActor;\nimport org.bonitasoft.engine.actor.mapping.model.SActorMember;\nimport org.bonitasoft.engine.actor.mapping.model.SActorUpdateBuilder;\nimport org.bonitasoft.engine.actor.mapping.model.SActorUpdateBuilderFactory;\nimport org.bonitasoft.engine.actor.mapping.persistence.SelectDescriptorBuilder;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteAllRecord;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentMatchers;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Celine Souchet\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class ActorMappingServiceImplTest {\n\n    @Mock\n    private Recorder recorder;\n    @Mock\n    private ReadPersistenceService persistenceService;\n    @Mock\n    private EventService eventService;\n    @Mock\n    private QueriableLoggerService queriableLoggerService;\n    @Mock\n    private IdentityService identityService;\n    @InjectMocks\n    private ActorMappingServiceImpl actorMappingServiceImpl;\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#getActor(long)}.\n     *\n     * @throws SBonitaReadException\n     * @throws SActorNotFoundException\n     */\n    @Test\n    public final void getActorById() throws SActorNotFoundException, SBonitaReadException {\n        final SActor actor = mock(SActor.class);\n        when(persistenceService.selectById(ArgumentMatchers.<SelectByIdDescriptor<SActor>> any())).thenReturn(actor);\n\n        Assert.assertEquals(actor, actorMappingServiceImpl.getActor(456L));\n    }\n\n    @Test(expected = SActorNotFoundException.class)\n    public final void getActorByIdNotExists() throws SBonitaReadException, SActorNotFoundException {\n        when(persistenceService.selectById(ArgumentMatchers.<SelectByIdDescriptor<SActor>> any())).thenReturn(null);\n\n        actorMappingServiceImpl.getActor(456L);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#getNumberOfActorMembers(long)}.\n     *\n     * @throws SBonitaReadException\n     */\n    @Test\n    public final void getNumberOfActorMembers() throws SBonitaReadException {\n        final long actorId = 456L;\n        final long numberOfActorMemebers = 1L;\n        when(persistenceService.selectOne(ArgumentMatchers.<SelectOneDescriptor<Long>> any()))\n                .thenReturn(numberOfActorMemebers);\n\n        Assert.assertEquals(numberOfActorMemebers, actorMappingServiceImpl.getNumberOfActorMembers(actorId));\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#getNumberOfUsersOfActor(long)}.\n     *\n     * @throws SBonitaReadException\n     */\n    @Test\n    public final void getNumberOfUsersOfActor() throws SBonitaReadException {\n        final long numberOfUsersOfActor = 155L;\n        when(persistenceService.selectOne(ArgumentMatchers.<SelectOneDescriptor<Long>> any()))\n                .thenReturn(numberOfUsersOfActor);\n\n        Assert.assertEquals(numberOfUsersOfActor, actorMappingServiceImpl.getNumberOfUsersOfActor(456L));\n    }\n\n    @Test(expected = RuntimeException.class)\n    public final void getNumberOfUsersOfActorThrowException() throws SBonitaReadException {\n        when(persistenceService.selectOne(ArgumentMatchers.<SelectOneDescriptor<SActor>> any()))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        actorMappingServiceImpl.getNumberOfUsersOfActor(456L);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#getNumberOfRolesOfActor(long)}.\n     *\n     * @throws SBonitaReadException\n     */\n    @Test\n    public final void getNumberOfRolesOfActor() throws SBonitaReadException {\n        final long numberOfRolesOfActor = 155L;\n        when(persistenceService.selectOne(ArgumentMatchers.<SelectOneDescriptor<Long>> any()))\n                .thenReturn(numberOfRolesOfActor);\n\n        Assert.assertEquals(numberOfRolesOfActor, actorMappingServiceImpl.getNumberOfRolesOfActor(456L));\n    }\n\n    @Test(expected = RuntimeException.class)\n    public final void getNumberOfRolesOfActorThrowException() throws SBonitaReadException {\n        when(persistenceService.selectOne(ArgumentMatchers.<SelectOneDescriptor<SActor>> any()))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        actorMappingServiceImpl.getNumberOfRolesOfActor(456L);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#getNumberOfGroupsOfActor(long)}.\n     *\n     * @throws SBonitaReadException\n     */\n    @Test\n    public final void getNumberOfGroupsOfActor() throws SBonitaReadException {\n        final long numberOfGroupsOfActor = 155L;\n        when(persistenceService.selectOne(ArgumentMatchers.<SelectOneDescriptor<Long>> any()))\n                .thenReturn(numberOfGroupsOfActor);\n\n        Assert.assertEquals(numberOfGroupsOfActor, actorMappingServiceImpl.getNumberOfGroupsOfActor(456L));\n    }\n\n    @Test(expected = RuntimeException.class)\n    public final void getNumberOfGroupsOfActorThrowException() throws SBonitaReadException {\n        when(persistenceService.selectOne(ArgumentMatchers.<SelectOneDescriptor<SActor>> any()))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        actorMappingServiceImpl.getNumberOfGroupsOfActor(456L);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#getNumberOfMembershipsOfActor(long)}.\n     *\n     * @throws SBonitaReadException\n     */\n    @Test\n    public final void getNumberOfMembershipsOfActor() throws SBonitaReadException {\n        final long numberOfGroupsOfActor = 155L;\n        when(persistenceService.selectOne(ArgumentMatchers.<SelectOneDescriptor<Long>> any()))\n                .thenReturn(numberOfGroupsOfActor);\n\n        Assert.assertEquals(numberOfGroupsOfActor, actorMappingServiceImpl.getNumberOfMembershipsOfActor(456L));\n    }\n\n    @Test(expected = RuntimeException.class)\n    public final void getNumberOfMembershipsOfActorThrowException() throws SBonitaReadException {\n        when(persistenceService.selectOne(ArgumentMatchers.<SelectOneDescriptor<SActor>> any()))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        actorMappingServiceImpl.getNumberOfMembershipsOfActor(456L);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#getActor(java.lang.String, long)}.\n     *\n     * @throws SBonitaReadException\n     * @throws SActorNotFoundException\n     */\n    @Test\n    public final void getActorByNameAndScopeId() throws SActorNotFoundException, SBonitaReadException {\n        final SActor actor = mock(SActor.class);\n        when(persistenceService.selectOne(ArgumentMatchers.<SelectOneDescriptor<SActor>> any())).thenReturn(actor);\n\n        Assert.assertEquals(actor, actorMappingServiceImpl.getActor(\"actorName\", 69L));\n    }\n\n    @Test(expected = SActorNotFoundException.class)\n    public final void getActorByNameAndScopeIdNotExists() throws SActorNotFoundException, SBonitaReadException {\n        when(persistenceService.selectOne(ArgumentMatchers.<SelectOneDescriptor<SActor>> any())).thenReturn(null);\n\n        actorMappingServiceImpl.getActor(\"actorName\", 69L);\n    }\n\n    @Test(expected = SActorNotFoundException.class)\n    public final void getActorByNameAndScopeIdThrowException() throws SActorNotFoundException, SBonitaReadException {\n        when(persistenceService.selectOne(ArgumentMatchers.<SelectOneDescriptor<SActor>> any()))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        actorMappingServiceImpl.getActor(\"actorName\", 69L);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#getActorMembers(long, int, int)}.\n     *\n     * @throws SBonitaReadException\n     */\n    @Test\n    public final void getActorMembersByActorPaginated() throws SBonitaReadException {\n        final List<SActorMember> actors = new ArrayList<SActorMember>();\n        when(persistenceService.selectList(ArgumentMatchers.<SelectListDescriptor<SActorMember>> any()))\n                .thenReturn(actors);\n\n        Assert.assertEquals(actors, actorMappingServiceImpl.getActorMembers(4115L, 0, 1));\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#getActorMembersOfGroup(long, int, int)}.\n     *\n     * @throws SBonitaReadException\n     */\n    @Test\n    public final void getActorMembersOfGroup() throws SBonitaReadException {\n        final List<SActorMember> actors = new ArrayList<SActorMember>(6);\n        when(persistenceService.selectList(ArgumentMatchers.<SelectListDescriptor<SActorMember>> any()))\n                .thenReturn(actors);\n\n        Assert.assertEquals(actors, actorMappingServiceImpl.getActorMembersOfGroup(41L, 0, 1));\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#getActorMembersOfRole(long, int, int)}.\n     *\n     * @throws SBonitaReadException\n     */\n    @Test\n    public final void getActorMembersOfRole() throws SBonitaReadException {\n        final List<SActorMember> actors = new ArrayList<SActorMember>(3);\n        when(persistenceService.selectList(ArgumentMatchers.<SelectListDescriptor<SActorMember>> any()))\n                .thenReturn(actors);\n\n        Assert.assertEquals(actors, actorMappingServiceImpl.getActorMembersOfRole(41L, 0, 1));\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#getActorMember(long, long, long, long)}.\n     */\n    @Test\n    public final void getActorMember() throws SBonitaReadException {\n        // Given\n        final SActorMember actor = mock(SActorMember.class);\n        final long actorId = 1L;\n        final long userId = 2L;\n        final long groupId = 3L;\n        final long roleId = 4L;\n        when(persistenceService.selectOne(SelectDescriptorBuilder.getActorMember(actorId, userId, groupId, roleId)))\n                .thenReturn(actor);\n\n        // When\n        final SActorMember sActorMember = actorMappingServiceImpl.getActorMember(actorId, userId, groupId, roleId);\n\n        // Then\n        Assert.assertEquals(actor, sActorMember);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public final void getActorMemberThrowException() throws SBonitaReadException {\n        // Given\n        final long actorId = 1L;\n        final long userId = 2L;\n        final long groupId = 3L;\n        final long roleId = 4L;\n        when(persistenceService.selectOne(SelectDescriptorBuilder.getActorMember(actorId, userId, groupId, roleId)))\n                .thenThrow(new SBonitaReadException(\"plop\"));\n\n        // When\n        actorMappingServiceImpl.getActorMember(actorId, userId, groupId, roleId);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#getActors(java.util.List)}.\n     *\n     * @throws SBonitaReadException\n     */\n    @Test\n    public final void getActorsByListOfIds() throws SBonitaReadException {\n        final List<SActor> actors = new ArrayList<SActor>(3);\n        when(persistenceService.selectList(ArgumentMatchers.<SelectListDescriptor<SActor>> any())).thenReturn(actors);\n\n        final List<Long> actorIds = new ArrayList<Long>(1);\n        actorIds.add(589L);\n        Assert.assertEquals(actors, actorMappingServiceImpl.getActors(actorIds));\n    }\n\n    @Test\n    public final void getActorsByListOfIdsWithEmptyList() throws SBonitaReadException {\n        Assert.assertEquals(Collections.emptyList(), actorMappingServiceImpl.getActors(new ArrayList<Long>(0)));\n    }\n\n    @Test\n    public final void getActorsByListOfIdsWithNullList() throws SBonitaReadException {\n        Assert.assertEquals(Collections.emptyList(), actorMappingServiceImpl.getActors(null));\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#getActors(long, org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     *\n     * @throws SBonitaReadException\n     */\n    @Test\n    public final void getActors() throws SBonitaReadException {\n        final QueryOptions queryOptions = new QueryOptions(0, 100, SActor.class, \"id\", OrderByType.ASC);\n        final List<SActor> actors = new ArrayList<SActor>(3);\n        when(persistenceService.selectList(ArgumentMatchers.<SelectListDescriptor<SActor>> any())).thenReturn(actors);\n\n        Assert.assertEquals(actors, actorMappingServiceImpl.getActors(41564L, queryOptions));\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#canUserStartProcessDefinition(long, long)}.\n     *\n     * @throws SBonitaReadException\n     */\n    @Test\n    public final void shouldBeAllowedToStartProcessDefinition() throws SBonitaReadException {\n        final List<Long> actorMembers = new ArrayList<Long>(1);\n        actorMembers.add(123L);\n        when(persistenceService.selectList(ArgumentMatchers.<SelectListDescriptor<Long>> any()))\n                .thenReturn(actorMembers);\n        when(persistenceService.selectOne(ArgumentMatchers.<SelectOneDescriptor<Long>> any())).thenReturn(3L);\n\n        Assertions.assertThat(actorMappingServiceImpl.canUserStartProcessDefinition(315L, 5484L))\n                .as(\"Should be allowed to start Process\").isTrue();\n    }\n\n    @Test\n    public final void shouldNotBeAllowedToStartProcessDefinitionIfNoActorMembers() throws SBonitaReadException {\n        final List<Long> actorMembers = new ArrayList<Long>(0);\n        when(persistenceService.selectList(ArgumentMatchers.<SelectListDescriptor<Long>> any()))\n                .thenReturn(actorMembers);\n\n        Assertions.assertThat(actorMappingServiceImpl.canUserStartProcessDefinition(315L, 5484L))\n                .as(\"Should NOT be allowed to start Process\").isFalse();\n    }\n\n    @Test\n    public final void shouldNotBeAllowedToStartProcessDefinitionIfNoUserMemberships() throws SBonitaReadException {\n        final List<Long> actorMembers = new ArrayList<Long>(1);\n        actorMembers.add(123L);\n        when(persistenceService.selectList(ArgumentMatchers.<SelectListDescriptor<Long>> any()))\n                .thenReturn(actorMembers);\n        when(persistenceService.selectOne(ArgumentMatchers.<SelectOneDescriptor<Long>> any())).thenReturn(0L);\n\n        Assertions.assertThat(actorMappingServiceImpl.canUserStartProcessDefinition(315L, 5484L))\n                .as(\"Should NOT be allowed to start Process\").isFalse();\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#getActors(java.util.Set, java.lang.Long)}.\n     *\n     * @throws SBonitaReadException\n     */\n    @Test\n    public final void getActorsByScopeIdsAndUserId() throws SBonitaReadException {\n        final List<SActor> actors = new ArrayList<SActor>(3);\n        when(persistenceService.selectList(ArgumentMatchers.<SelectListDescriptor<SActor>> any())).thenReturn(actors);\n\n        Assert.assertEquals(actors, actorMappingServiceImpl.getActors(new HashSet<Long>(), 5484L));\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#addActors(java.util.Set)}.\n     *\n     * @throws SActorCreationException\n     */\n    @Test\n    public final void addActors() throws SActorCreationException {\n        final Set<SActor> actors = new HashSet<SActor>();\n        actors.add(mock(SActor.class));\n\n        final ActorMappingServiceImpl mockedActorMappingServiceImpl = mock(ActorMappingServiceImpl.class,\n                withSettings().spiedInstance(actorMappingServiceImpl));\n        final SActor sActor = mock(SActor.class);\n        when(mockedActorMappingServiceImpl.addActor(any(SActor.class))).thenReturn(sActor);\n\n        // Let's call it for real:\n        doCallRealMethod().when(mockedActorMappingServiceImpl).addActors(actors);\n        final Set<SActor> result = mockedActorMappingServiceImpl.addActors(actors);\n        assertNotNull(result);\n        assertEquals(1, result.size());\n        assertEquals(sActor, result.toArray()[0]);\n\n        // and check methods are called:\n        verify(mockedActorMappingServiceImpl, times(1)).addActor(any(SActor.class));\n    }\n\n    @Test\n    public final void addActorsEmptyList() throws SActorCreationException {\n        final Set<SActor> actors = new HashSet<SActor>();\n\n        final Set<SActor> result = actorMappingServiceImpl.addActors(actors);\n        assertNotNull(result);\n        assertEquals(0, result.size());\n    }\n\n    @Test(expected = SActorCreationException.class)\n    public final void addActorsThrowException() throws SActorCreationException {\n        final Set<SActor> actors = new HashSet<SActor>();\n        actors.add(mock(SActor.class));\n\n        final ActorMappingServiceImpl mockedActorMappingServiceImpl = mock(ActorMappingServiceImpl.class,\n                withSettings().spiedInstance(actorMappingServiceImpl));\n        when(mockedActorMappingServiceImpl.addActor(any(SActor.class))).thenThrow(new SActorCreationException(\"\"));\n\n        // Let's call it for real:\n        doCallRealMethod().when(mockedActorMappingServiceImpl).addActors(actors);\n        mockedActorMappingServiceImpl.addActors(actors);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#addActor(org.bonitasoft.engine.actor.mapping.model.SActor)}.\n     *\n     * @throws Exception\n     */\n    @Test\n    public final void addActor() throws Exception {\n        final SActor sActor = mock(SActor.class);\n        doReturn(1L).when(sActor).getId();\n\n        final SActor result = actorMappingServiceImpl.addActor(sActor);\n        assertNotNull(result);\n        assertEquals(sActor, result);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public final void addNullActor() throws Exception {\n        actorMappingServiceImpl.addActor(null);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#updateActor(long, org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor)}.\n     *\n     * @throws SBonitaReadException\n     * @throws SActorUpdateException\n     * @throws SActorNotFoundException\n     * @throws SRecorderException\n     */\n    @Test\n    public final void updateActor() throws SActorNotFoundException, SActorUpdateException, SBonitaReadException {\n        final SActor sActor = mock(SActor.class);\n\n        final SActorUpdateBuilder sActorUpdateBuilder = BuilderFactory.get(SActorUpdateBuilderFactory.class)\n                .createNewInstance();\n        sActorUpdateBuilder.updateDescription(\"newDescription\");\n        sActorUpdateBuilder.updateDisplayName(\"newDisplayName\");\n\n        doReturn(sActor).when(persistenceService).selectById(ArgumentMatchers.<SelectByIdDescriptor<SActor>> any());\n\n        final SActor result = actorMappingServiceImpl.updateActor(3, sActorUpdateBuilder.done());\n        assertNotNull(result);\n        assertEquals(sActor, result);\n    }\n\n    @Test(expected = SActorNotFoundException.class)\n    public final void updateActorNotExists()\n            throws SActorUpdateException, SActorNotFoundException, SBonitaReadException {\n        final SActorUpdateBuilder sActorUpdateBuilder = BuilderFactory.get(SActorUpdateBuilderFactory.class)\n                .createNewInstance();\n        doReturn(null).when(persistenceService).selectById(ArgumentMatchers.<SelectByIdDescriptor<SActor>> any());\n\n        actorMappingServiceImpl.updateActor(4, sActorUpdateBuilder.done());\n    }\n\n    @Test(expected = SActorUpdateException.class)\n    public final void updateActorThrowException()\n            throws SActorUpdateException, SActorNotFoundException, SBonitaReadException, SRecorderException {\n        final SActor sActor = mock(SActor.class);\n\n        final SActorUpdateBuilder sActorUpdateBuilder = BuilderFactory.get(SActorUpdateBuilderFactory.class)\n                .createNewInstance();\n        sActorUpdateBuilder.updateDescription(\"newDescription\");\n        sActorUpdateBuilder.updateDisplayName(\"newDisplayName\");\n\n        doReturn(sActor).when(persistenceService).selectById(any());\n        doThrow(new SRecorderException(\"plop\")).when(recorder).recordUpdate(any(UpdateRecord.class),\n                nullable(String.class));\n\n        actorMappingServiceImpl.updateActor(3, sActorUpdateBuilder.done());\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#deleteActors(long)}.\n     *\n     * @throws Exception\n     */\n    @Test\n    public final void deleteActors() throws Exception {\n        final int scopeId = 9;\n        final SActor sActor = mock(SActor.class);\n        doReturn(3L).when(sActor).getId();\n\n        final List<SActorMember> sActorMembers = new ArrayList<SActorMember>();\n        final SActorMember sActorMember = mock(SActorMember.class);\n        doReturn(4L).when(sActorMember).getId();\n        sActorMembers.add(sActorMember);\n\n        doReturn(Arrays.asList(sActor)).doReturn(new ArrayList<SActor>()).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SActorMember>> any());\n        doReturn(sActorMembers).doReturn(new ArrayList<SActorMember>()).when(persistenceService)\n                .selectList(SelectDescriptorBuilder.getActorMembers(3, 0, 50));\n\n        actorMappingServiceImpl.deleteActors(scopeId);\n    }\n\n    @Test\n    public final void deleteNoActorMembers() throws SBonitaReadException, SRecorderException, SActorDeletionException {\n        final int scopeId = 9;\n        final SActor sActor = mock(SActor.class);\n        doReturn(3L).when(sActor).getId();\n\n        final List<SActorMember> sActorMembers = new ArrayList<SActorMember>();\n\n        doReturn(Arrays.asList(sActor)).doReturn(new ArrayList<SActor>()).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SActorMember>> any());\n        doReturn(sActorMembers).when(persistenceService).selectList(SelectDescriptorBuilder.getActorMembers(3, 0, 50));\n\n        actorMappingServiceImpl.deleteActors(scopeId);\n    }\n\n    @Test\n    public final void deleteNoActors() throws SBonitaReadException, SRecorderException, SActorDeletionException {\n        final int scopeId = 9;\n\n        doReturn(new ArrayList<SActor>()).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SActorMember>> any());\n\n        actorMappingServiceImpl.deleteActors(scopeId);\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#deleteAllActorMembers()}.\n     *\n     * @throws SRecorderException\n     * @throws SActorMemberDeletionException\n     */\n    @Test\n    public final void deleteAllActorMembers() throws SRecorderException, SActorMemberDeletionException {\n        doNothing().when(recorder).recordDeleteAll(any(DeleteAllRecord.class));\n\n        actorMappingServiceImpl.deleteAllActorMembers();\n    }\n\n    @Test(expected = SActorMemberDeletionException.class)\n    public final void deleteAllActorMembersThrowException() throws SRecorderException, SActorMemberDeletionException {\n        doThrow(new SRecorderException(\"plop\")).when(recorder).recordDeleteAll(any(DeleteAllRecord.class));\n\n        actorMappingServiceImpl.deleteAllActorMembers();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-category/build.gradle",
    "content": "\n\ndependencies {\n    api project(':services:bonita-session')\n    api project(':services:bonita-commons')\n    api project(':services:bonita-builder')\n    api project(':services:bonita-events')\n    api project(':services:bonita-log')\n    api project(':services:bonita-persistence')\n    testImplementation libs.mockitoCore\n\n    annotationProcessor libs.lombok\n    compileOnly libs.lombok\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/CategoryService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.category;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.category.exception.SCategoryAlreadyExistsException;\nimport org.bonitasoft.engine.core.category.exception.SCategoryCreationException;\nimport org.bonitasoft.engine.core.category.exception.SCategoryDeletionException;\nimport org.bonitasoft.engine.core.category.exception.SCategoryException;\nimport org.bonitasoft.engine.core.category.exception.SCategoryInProcessAlreadyExistsException;\nimport org.bonitasoft.engine.core.category.exception.SCategoryNotFoundException;\nimport org.bonitasoft.engine.core.category.exception.SIndexOutOfRangeException;\nimport org.bonitasoft.engine.core.category.exception.SPageOutOfRangeException;\nimport org.bonitasoft.engine.core.category.model.SCategory;\nimport org.bonitasoft.engine.core.category.model.SProcessCategoryMapping;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @since 6.0\n */\npublic interface CategoryService {\n\n    String CATEGORY = \"CATEGORY\";\n\n    /**\n     * Create a category by give name and description\n     *\n     * @param name\n     *        The name of category\n     * @param description\n     *        The description of category\n     * @return the category object with id\n     * @throws SCategoryAlreadyExistsException\n     *         Error thrown if category is already exist\n     * @throws SCategoryCreationException\n     *         Error thrown if has exceptions during the category creation.\n     */\n    SCategory createCategory(String name, String description)\n            throws SCategoryAlreadyExistsException, SCategoryCreationException;\n\n    /**\n     * Get category by its id\n     *\n     * @param id\n     *        Identifier of the category\n     * @return a category object\n     * @throws SCategoryNotFoundException\n     *         Error thrown if no category have an id corresponding to the parameter.\n     */\n    SCategory getCategory(long id) throws SCategoryNotFoundException;\n\n    /**\n     * Get category by its name\n     *\n     * @param name\n     *        Name of the category\n     * @return a category object\n     * @throws SCategoryNotFoundException\n     *         Error thrown if no category have a name corresponding to the parameter.\n     */\n    SCategory getCategoryByName(String name) throws SCategoryNotFoundException;\n\n    /**\n     * Update a category by its id\n     *\n     * @param categoryId\n     *        Identifier of the category\n     * @param newCategory\n     *        An category object used to update the categoryId specified category\n     * @throws SCategoryNotFoundException\n     *         Error thrown if no category have an id corresponding to the parameter.\n     * @throws SCategoryException\n     *         Error thrown if has exception during the category update\n     */\n    void updateCategory(long categoryId, EntityUpdateDescriptor descriptor)\n            throws SCategoryNotFoundException, SCategoryException;\n\n    /**\n     * Delete a category by its id\n     *\n     * @param categoryId\n     *        Identifier of the category\n     * @throws SCategoryNotFoundException\n     *         Error thrown if no category have an id corresponding to the parameter.\n     * @throws SCategoryDeletionException\n     *         Error thrown if has exception during the category deletion\n     */\n    void deleteCategory(long categoryId) throws SCategoryNotFoundException, SCategoryDeletionException;\n\n    /**\n     * Get the total number of categories\n     *\n     * @return The total number of Categories\n     * @throws SCategoryException\n     */\n    long getNumberOfCategories() throws SCategoryException;\n\n    /**\n     * Retrieves a list of categories, The returned list is paginated\n     *\n     * @param fromIndex\n     *        Index of the record to be returned. First record has index 0.\n     * @param numberOfCategories\n     *        Number of categories per page. Maximum number of categories returned.\n     * @param field\n     *        The field used by the list order\n     * @param order\n     *        ASC or DESC\n     * @return The list of category objects\n     * @throws SPageOutOfRangeException\n     *         Error thrown if page is out of the range.\n     * @throws SCategoryException\n     *         Error thrown if has exception during the category retrieve\n     */\n    List<SCategory> getCategories(int fromIndex, int numberOfCategories, String field, OrderByType order)\n            throws SPageOutOfRangeException, SCategoryException;\n\n    /**\n     * Add a process definition to a category\n     *\n     * @param categoryId\n     *        Identifier of the category\n     * @param processDefinitionId\n     *        Identifier of the process definition\n     * @throws SCategoryNotFoundException\n     *         Error thrown if no category have an id corresponding to the parameter.\n     * @throws SCategoryException\n     *         Error thrown if has exception during the adding action\n     */\n    void addProcessDefinitionToCategory(long categoryId, long processDefinitionId)\n            throws SCategoryNotFoundException, SCategoryInProcessAlreadyExistsException,\n            SCategoryException;\n\n    /**\n     * Add process definitions to a category\n     *\n     * @param categoryId\n     *        Identifier of the category\n     * @param processDefinitionIds\n     *        Identifier of the process definition\n     * @throws SCategoryNotFoundException\n     *         Error thrown if no category have an id corresponding to the parameter.\n     * @throws SCategoryException\n     *         Error thrown if has exception during the adding action\n     * @throws SCategoryInProcessAlreadyExistsException\n     */\n    void addProcessDefinitionsToCategory(long categoryId, List<Long> processDefinitionIds)\n            throws SCategoryNotFoundException, SCategoryException,\n            SCategoryInProcessAlreadyExistsException;\n\n    /**\n     * Get number of categories of the specific process definition\n     *\n     * @param processDefinitionId\n     *        Identifier of the process definition\n     * @return number of categories\n     * @throws SCategoryException\n     *         Error thrown if has exception during the category number retrieve action\n     */\n    long getNumberOfCategoriesOfProcess(long processDefinitionId) throws SCategoryException;\n\n    /**\n     * Get categories for specific process definition, the result list is paginated\n     *\n     * @param processId\n     *        Identifier of the process definition\n     * @param fromIndex\n     *        Start index of satisfied record\n     * @param numberOfCategories\n     *        Number of categories per page. Maximum number of categories returned.\n     * @param order\n     *        Criterion for order, default order by name\n     * @return The matching list of category\n     * @throws SCategoryException\n     *         Error thrown if has exception during the category retrieve action\n     * @throws SIndexOutOfRangeException\n     *         Error thrown if index is out of the range.\n     */\n    List<SCategory> getCategoriesOfProcessDefinition(long processId, int fromIndex, int numberOfCategories,\n            OrderByType order) throws SCategoryException,\n            SIndexOutOfRangeException;\n\n    /**\n     * Get number of categorized processes\n     *\n     * @param processIds\n     *        Identifier of the process definition\n     * @return the number of categorized processes\n     * @throws SCategoryException\n     */\n    long getNumberOfCategorizedProcessIds(List<Long> processIds) throws SCategoryException;\n\n    /**\n     * Get the number of process definition for specific category\n     *\n     * @param categoryId\n     *        Identifier of the category\n     * @return The number of process definition for specific category\n     * @throws SCategoryNotFoundException\n     *         Error thrown if no category have an id corresponding to the parameter.\n     * @throws SCategoryException\n     *         Error thrown if has exception during the process definition id retrieve\n     */\n    long getNumberOfProcessDeploymentInfosOfCategory(long categoryId) throws SBonitaReadException;\n\n    /**\n     * Remove specific categories for specific process definition\n     *\n     * @param processId\n     *        Identifier of the process definition\n     * @param categoryIds\n     *        Identifiers of the categories\n     * @throws SCategoryException\n     */\n    void removeCategoriesFromProcessDefinition(long processId, List<Long> categoryIds) throws SCategoryException;\n\n    /**\n     * Get categories not attached for specific process definition, the result list is paginated\n     *\n     * @param processDefinitionId\n     * @param fromIndex\n     * @param numberOfCategories\n     * @param order\n     * @return The matching list of category\n     * @throws SCategoryException\n     */\n    List<SCategory> getCategoriesUnrelatedToProcessDefinition(long processDefinitionId, int fromIndex,\n            int numberOfCategories, OrderByType order)\n            throws SCategoryException;\n\n    /**\n     * Get number of categories not attached of the specific process definition\n     *\n     * @param processDefinitionId\n     * @return number of categories\n     * @throws SCategoryException\n     *         Error thrown if has exception during the category number retrieve action\n     */\n    long getNumberOfCategoriesUnrelatedToProcess(long processDefinitionId) throws SCategoryException;\n\n    /**\n     * Search process category mappings corresponding to criteria\n     *\n     * @param queryOptions\n     * @return List of process category mappings\n     * @throws SBonitaReadException\n     * @since 6.1\n     */\n    List<SProcessCategoryMapping> searchProcessCategoryMappings(QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * @param mappings\n     * @return Number of deleted category mapping\n     * @since 6.1\n     */\n    long deleteProcessCategoryMappings(List<SProcessCategoryMapping> mappings);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/SCategoryCriterion.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.category;\n\n/**\n * @author Yanyan Liu\n */\npublic enum SCategoryCriterion {\n    NAME_DESC, NAME_ASC;\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/exception/SCategoryAlreadyExistsException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.category.exception;\n\n/**\n * @author Yanyan Liu\n */\npublic class SCategoryAlreadyExistsException extends SCategoryException {\n\n    private static final long serialVersionUID = 8463473790848613835L;\n\n    public SCategoryAlreadyExistsException(final String message) {\n        super(message);\n    }\n\n    public SCategoryAlreadyExistsException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SCategoryAlreadyExistsException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/exception/SCategoryCreationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.category.exception;\n\n/**\n * @author Yanyan Liu\n */\npublic class SCategoryCreationException extends SCategoryException {\n\n    private static final long serialVersionUID = -6791829619840778679L;\n\n    public SCategoryCreationException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SCategoryCreationException(final String message) {\n        super(message);\n    }\n\n    public SCategoryCreationException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/exception/SCategoryDeletionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.category.exception;\n\n/**\n * @author Yanyan Liu\n */\npublic class SCategoryDeletionException extends SCategoryException {\n\n    private static final long serialVersionUID = 2659887815564433751L;\n\n    public SCategoryDeletionException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SCategoryDeletionException(final String message) {\n        super(message);\n    }\n\n    public SCategoryDeletionException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/exception/SCategoryException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.category.exception;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Yanyan Liu\n */\npublic class SCategoryException extends SBonitaException {\n\n    private static final long serialVersionUID = -7279379575300389907L;\n\n    public SCategoryException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SCategoryException(final String message) {\n        super(message);\n    }\n\n    public SCategoryException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/exception/SCategoryInProcessAlreadyExistsException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.category.exception;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SCategoryInProcessAlreadyExistsException extends SCategoryException {\n\n    private static final long serialVersionUID = -6455208415564288711L;\n\n    public SCategoryInProcessAlreadyExistsException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SCategoryInProcessAlreadyExistsException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/exception/SCategoryNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.category.exception;\n\n/**\n * @author Yanyan Liu\n */\npublic class SCategoryNotFoundException extends SCategoryException {\n\n    private static final long serialVersionUID = 5143299844735860984L;\n\n    public SCategoryNotFoundException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SCategoryNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SCategoryNotFoundException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/exception/SIndexOutOfRangeException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.category.exception;\n\n/**\n * @author Yanyan Liu\n */\npublic class SIndexOutOfRangeException extends SCategoryException {\n\n    private static final long serialVersionUID = 5146584159115092732L;\n\n    public SIndexOutOfRangeException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SIndexOutOfRangeException(final String message) {\n        super(message);\n    }\n\n    public SIndexOutOfRangeException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/exception/SPageOutOfRangeException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.category.exception;\n\n/**\n * @author Yanyan Liu\n */\npublic class SPageOutOfRangeException extends SCategoryException {\n\n    private static final long serialVersionUID = -7486082562017547179L;\n\n    public SPageOutOfRangeException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SPageOutOfRangeException(final String message) {\n        super(message);\n    }\n\n    public SPageOutOfRangeException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/impl/CategoryServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.category.impl;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.category.CategoryService;\nimport org.bonitasoft.engine.core.category.exception.SCategoryAlreadyExistsException;\nimport org.bonitasoft.engine.core.category.exception.SCategoryCreationException;\nimport org.bonitasoft.engine.core.category.exception.SCategoryDeletionException;\nimport org.bonitasoft.engine.core.category.exception.SCategoryException;\nimport org.bonitasoft.engine.core.category.exception.SCategoryInProcessAlreadyExistsException;\nimport org.bonitasoft.engine.core.category.exception.SCategoryNotFoundException;\nimport org.bonitasoft.engine.core.category.model.SCategory;\nimport org.bonitasoft.engine.core.category.model.SProcessCategoryMapping;\nimport org.bonitasoft.engine.core.category.model.builder.SCategoryLogBuilder;\nimport org.bonitasoft.engine.core.category.model.builder.SCategoryLogBuilderFactory;\nimport org.bonitasoft.engine.core.category.model.builder.SProcessCategoryMappingBuilderFactory;\nimport org.bonitasoft.engine.core.category.persistence.SelectDescriptorBuilder;\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity;\nimport org.bonitasoft.engine.queriablelogger.model.builder.ActionType;\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor;\n\n/**\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class CategoryServiceImpl implements CategoryService {\n\n    private final ReadPersistenceService persistenceService;\n\n    private final Recorder recorder;\n\n    private final EventService eventService;\n\n    private final SessionService sessionService;\n\n    private final ReadSessionAccessor sessionAccessor;\n\n    private final QueriableLoggerService queriableLoggerService;\n\n    public CategoryServiceImpl(final ReadPersistenceService persistenceService, final Recorder recorder,\n            final EventService eventService,\n            final SessionService sessionService, final ReadSessionAccessor sessionAccessor,\n            final QueriableLoggerService queriableLoggerService) {\n        super();\n        this.persistenceService = persistenceService;\n        this.recorder = recorder;\n        this.eventService = eventService;\n        this.sessionService = sessionService;\n        this.sessionAccessor = sessionAccessor;\n        this.queriableLoggerService = queriableLoggerService;\n    }\n\n    @Override\n    public SCategory createCategory(final String name, final String description)\n            throws SCategoryAlreadyExistsException, SCategoryCreationException {\n        if (name == null) {\n            throw new SCategoryCreationException(\"Category name can not be null!\");\n        }\n        try {\n            getCategoryByName(name);\n            throw new SCategoryAlreadyExistsException(\"Category with name \" + name + \" already exists!\");\n        } catch (final SCategoryNotFoundException scnfe) {\n            return addCategory(name, description);\n        }\n    }\n\n    private SCategory addCategory(final String name, final String description) throws SCategoryCreationException {\n        final SCategoryLogBuilder logBuilder = getQueriableLog(ActionType.CREATED,\n                \"Creating a new category with name \" + name);\n        final long creator;\n        creator = getCreator();\n        final long now = System.currentTimeMillis();\n        final SCategory sCategory = SCategory.builder().name(name)\n                .creator(creator)\n                .creationDate(now)\n                .lastUpdateDate(now).description(description).build();\n        final InsertRecord insertRecord = new InsertRecord(sCategory);\n        try {\n            recorder.recordInsert(insertRecord, CATEGORY);\n            initiateLogBuilder(insertRecord.getEntity().getId(), SQueriableLog.STATUS_OK, logBuilder, \"addCategory\");\n            return sCategory;\n        } catch (final SRecorderException e) {\n            initiateLogBuilder(insertRecord.getEntity().getId(), SQueriableLog.STATUS_FAIL, logBuilder, \"addCategory\");\n            throw new SCategoryCreationException(e);\n        }\n    }\n\n    private long getCreator() {\n        return sessionService.getLoggedUserFromSession(sessionAccessor);\n    }\n\n    @Override\n    public SCategory getCategory(final long id) throws SCategoryNotFoundException {\n        final SelectByIdDescriptor<SCategory> selectByIdDescriptor = SelectDescriptorBuilder.getCategory(id);\n        try {\n            final SCategory category = persistenceService.selectById(selectByIdDescriptor);\n            if (category == null) {\n                throw new SCategoryNotFoundException(id + \" does not refer to any category\");\n            }\n            return category;\n        } catch (final SBonitaReadException bre) {\n            throw new SCategoryNotFoundException(bre);\n        }\n    }\n\n    @Override\n    public SCategory getCategoryByName(final String name) throws SCategoryNotFoundException {\n        final SelectOneDescriptor<SCategory> descriptor = SelectDescriptorBuilder.getCategory(name);\n        try {\n            final SCategory category = persistenceService.selectOne(descriptor);\n            if (category == null) {\n                throw new SCategoryNotFoundException(\"Category not found with name: \" + name);\n            }\n            return category;\n        } catch (final SBonitaReadException bre) {\n            throw new SCategoryNotFoundException(bre);\n        }\n    }\n\n    @Override\n    public void updateCategory(final long categoryId, final EntityUpdateDescriptor descriptor)\n            throws SCategoryException {\n        final SCategoryLogBuilder logBuilder = getQueriableLog(ActionType.UPDATED, \"Updating category\");\n        final SCategory persistedCategory = getCategory(categoryId);\n        try {\n            recorder.recordUpdate(UpdateRecord.buildSetFields(persistedCategory, descriptor), CATEGORY);\n            initiateLogBuilder(categoryId, SQueriableLog.STATUS_OK, logBuilder, \"updateCategory\");\n        } catch (final SRecorderException e) {\n            initiateLogBuilder(categoryId, SQueriableLog.STATUS_FAIL, logBuilder, \"updateCategory\");\n            throw new SCategoryException(\"Can't update category \" + persistedCategory, e);\n        } catch (final Exception e) {\n            throw new SCategoryException(\"Can't update category \" + persistedCategory, e);\n        }\n    }\n\n    @Override\n    public void deleteCategory(final long categoryId) throws SCategoryNotFoundException, SCategoryDeletionException {\n        final SCategory sCategory = getCategory(categoryId);\n        final SCategoryLogBuilder logBuilder = getQueriableLog(ActionType.DELETED, \"Deleting a category\");\n        try {\n            recorder.recordDelete(new DeleteRecord(sCategory), CATEGORY);\n            initiateLogBuilder(categoryId, SQueriableLog.STATUS_OK, logBuilder, \"deleteCategory\");\n        } catch (final SRecorderException e) {\n            initiateLogBuilder(categoryId, SQueriableLog.STATUS_FAIL, logBuilder, \"deleteCategory\");\n            throw new SCategoryDeletionException(\"Can't delete process category \" + sCategory, e);\n        }\n    }\n\n    @Override\n    public long getNumberOfCategories() throws SCategoryException {\n        try {\n            return persistenceService\n                    .selectOne(SelectDescriptorBuilder.getNumberOfElement(\"Category\", SCategory.class));\n        } catch (final SBonitaReadException e) {\n            throw new SCategoryException(\"Can't get the number of process category\", e);\n        }\n    }\n\n    @Override\n    public List<SCategory> getCategories(final int fromIndex, final int numberOfCategories, final String field,\n            final OrderByType order)\n            throws SCategoryException {\n        final SelectListDescriptor<SCategory> descriptor = SelectDescriptorBuilder.getCategories(field, order,\n                fromIndex, numberOfCategories);\n        try {\n            return persistenceService.selectList(descriptor);\n        } catch (final SBonitaReadException e) {\n            throw new SCategoryException(e);\n        }\n    }\n\n    @Override\n    public void addProcessDefinitionToCategory(final long categoryId, final long processDefinitionId)\n            throws SCategoryException {\n        final SCategory category = getCategory(categoryId);\n        if (isCategoryExistsInProcess(categoryId, processDefinitionId)) {\n            throw new SCategoryInProcessAlreadyExistsException(\n                    \"The category '\" + category.getName() + \"' with the id = \" + categoryId\n                            + \" is already in process  with the id = \" + processDefinitionId);\n        }\n        final SProcessCategoryMapping mapping = BuilderFactory.get(SProcessCategoryMappingBuilderFactory.class)\n                .createNewInstance(categoryId, processDefinitionId).done();\n        final String logMessage = \"Creating a new category mapping {categoryId:\" + categoryId\n                + \" --> processDefinitionId:\" + processDefinitionId + \"}\";\n        final SCategoryLogBuilder logBuilder = getQueriableLog(ActionType.CREATED, logMessage);\n        try {\n            recorder.recordInsert(new InsertRecord(mapping), CATEGORY);\n            initiateLogBuilder(categoryId, SQueriableLog.STATUS_OK, logBuilder, \"addProcessDefinitionToCategory\");\n        } catch (final SRecorderException e) {\n            initiateLogBuilder(categoryId, SQueriableLog.STATUS_FAIL, logBuilder, \"addProcessDefinitionToCategory\");\n            throw new SCategoryException(e);\n        }\n    }\n\n    private boolean isCategoryExistsInProcess(final long categoryId, final long processDefinitionId)\n            throws SCategoryException {\n        final SelectOneDescriptor<Long> descriptor = SelectDescriptorBuilder.isCategoryExistsInProcess(categoryId,\n                processDefinitionId);\n        try {\n            return persistenceService.selectOne(descriptor) == 0 ? false : true;\n        } catch (final SBonitaReadException e) {\n            throw new SCategoryException(e);\n        }\n    }\n\n    @Override\n    public void addProcessDefinitionsToCategory(final long categoryId, final List<Long> processDefinitionIds)\n            throws SCategoryException {\n        for (final long processDefinitionId : processDefinitionIds) {\n            addProcessDefinitionToCategory(categoryId, processDefinitionId);\n        }\n    }\n\n    @Override\n    public long getNumberOfCategoriesOfProcess(final long processDefinitionId) throws SCategoryException {\n        final SelectOneDescriptor<Long> descriptor = SelectDescriptorBuilder\n                .getNumberOfCategoriesOfProcess(processDefinitionId);\n        try {\n            return persistenceService.selectOne(descriptor);\n        } catch (final SBonitaReadException e) {\n            throw new SCategoryException(e);\n        }\n    }\n\n    @Override\n    public long getNumberOfCategoriesUnrelatedToProcess(final long processDefinitionId) throws SCategoryException {\n        final SelectOneDescriptor<Long> descriptor = SelectDescriptorBuilder\n                .getNumberOfCategoriesUnrelatedToProcess(processDefinitionId);\n        try {\n            return persistenceService.selectOne(descriptor);\n        } catch (final SBonitaReadException e) {\n            throw new SCategoryException(e);\n        }\n    }\n\n    @Override\n    public List<SCategory> getCategoriesOfProcessDefinition(final long processDefinitionId, final int fromIndex,\n            final int numberOfCategories,\n            final OrderByType order) throws SCategoryException {\n        final SelectListDescriptor<SCategory> descriptor = SelectDescriptorBuilder.getCategoriesOfProcess(\n                processDefinitionId, fromIndex, numberOfCategories,\n                order);\n        try {\n            return persistenceService.selectList(descriptor);\n        } catch (final SBonitaReadException e) {\n            throw new SCategoryException(e);\n        }\n    }\n\n    @Override\n    public List<SCategory> getCategoriesUnrelatedToProcessDefinition(final long processDefinitionId,\n            final int fromIndex, final int numberOfCategories,\n            final OrderByType order) throws SCategoryException {\n        final SelectListDescriptor<SCategory> descriptor = SelectDescriptorBuilder.getCategoriesUnrelatedToProcess(\n                processDefinitionId, fromIndex,\n                numberOfCategories, order);\n        try {\n            return persistenceService.selectList(descriptor);\n        } catch (final SBonitaReadException e) {\n            throw new SCategoryException(e);\n        }\n    }\n\n    @Override\n    public void removeCategoriesFromProcessDefinition(final long processDefinitionId, final List<Long> categoryIds)\n            throws SCategoryException {\n        final SelectListDescriptor<SProcessCategoryMapping> descriptor = SelectDescriptorBuilder\n                .getCategoryMappingOfProcessAndCategories(processDefinitionId,\n                        categoryIds, 0, 100);\n        try {\n            List<SProcessCategoryMapping> mappings = persistenceService.selectList(descriptor);\n            while (!mappings.isEmpty()) {\n                deleteProcessCategoryMappings(mappings);\n                mappings = persistenceService.selectList(descriptor);\n            }\n        } catch (final SBonitaReadException e) {\n            throw new SCategoryException(e);\n        }\n    }\n\n    @Override\n    public List<SProcessCategoryMapping> searchProcessCategoryMappings(final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        return persistenceService.searchEntity(SProcessCategoryMapping.class, queryOptions, null);\n    }\n\n    @Override\n    public long deleteProcessCategoryMappings(final List<SProcessCategoryMapping> mappings) {\n        long nbDeleted = 0;\n        for (final SProcessCategoryMapping mapping : mappings) {\n            try {\n                deleteProcessCategoryMapping(mapping);\n                nbDeleted = +1;\n            } catch (final SBonitaException e) {\n                // FIXME : Nothing to do, or add logs ??\n            }\n        }\n        return nbDeleted;\n    }\n\n    private void deleteProcessCategoryMapping(final SProcessCategoryMapping mapping) throws SCategoryException {\n        final String logMessage = \"Deleting a category mapping {processDefinitionId:\" + mapping.getProcessId()\n                + \" --> categoryId\" + mapping.getCategoryId()\n                + \"}\";\n        final SCategoryLogBuilder logBuilder = getQueriableLog(ActionType.DELETED, logMessage);\n        try {\n            recorder.recordDelete(new DeleteRecord(mapping), CATEGORY);\n            initiateLogBuilder(mapping.getId(), SQueriableLog.STATUS_OK, logBuilder, \"deleteProcessCategoryMapping\");\n        } catch (final SRecorderException e) {\n            initiateLogBuilder(mapping.getId(), SQueriableLog.STATUS_FAIL, logBuilder, \"deleteProcessCategoryMapping\");\n            throw new SCategoryException(e);\n        }\n    }\n\n    private SCategoryLogBuilder getQueriableLog(final ActionType actionType, final String message) {\n        final SCategoryLogBuilder logBuilder = BuilderFactory.get(SCategoryLogBuilderFactory.class).createNewInstance();\n        this.initializeLogBuilder(logBuilder, message);\n        this.updateLog(actionType, logBuilder);\n        return logBuilder;\n    }\n\n    private <T extends SLogBuilder> void initializeLogBuilder(final T logBuilder, final String message) {\n        logBuilder.actionStatus(SQueriableLog.STATUS_FAIL).severity(SQueriableLogSeverity.INTERNAL).rawMessage(message);\n    }\n\n    private <T extends HasCRUDEAction> void updateLog(final ActionType actionType, final T logBuilder) {\n        logBuilder.setActionType(actionType);\n    }\n\n    @Override\n    public long getNumberOfCategorizedProcessIds(final List<Long> processIds) throws SCategoryException {\n        if (processIds == null || processIds.size() <= 0) {\n            return 0; // Should return 0 or throw Exception?\n        }\n        final SelectOneDescriptor<Long> descriptor = SelectDescriptorBuilder\n                .getNumberOfCategorizedProcessIds(processIds);\n        try {\n            return persistenceService.selectOne(descriptor);\n        } catch (final SBonitaReadException e) {\n            throw new SCategoryException(e);\n        }\n    }\n\n    @Override\n    public long getNumberOfProcessDeploymentInfosOfCategory(final long categoryId) throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"categoryId\", (Object) categoryId);\n        final SelectOneDescriptor<Long> descriptor = new SelectOneDescriptor<Long>(\n                \"getNumberOfProcessDefinitionsOfCategory\", parameters,\n                SProcessCategoryMapping.class, Long.class);\n\n        return persistenceService.selectOne(descriptor);\n    }\n\n    private void initiateLogBuilder(final long objectId, final int sQueriableLogStatus,\n            final SPersistenceLogBuilder logBuilder, final String callerMethodName) {\n        logBuilder.actionScope(String.valueOf(objectId));\n        logBuilder.actionStatus(sQueriableLogStatus);\n        logBuilder.objectId(objectId);\n        final SQueriableLog log = logBuilder.build();\n        if (queriableLoggerService.isLoggable(log.getActionType(), log.getSeverity())) {\n            queriableLoggerService.log(this.getClass().getName(), callerMethodName, log);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/model/SCategory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.category.model;\n\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@Builder(toBuilder = true)\n@Entity\n@Table(name = \"category\")\npublic class SCategory implements PersistentObject {\n\n    public static final String ID = \"id\";\n    public static final String NAME = \"name\";\n    public static final String DESCRIPTION = \"description\";\n    public static final String CREATOR = \"creator\";\n    public static final String CREATION_DATE = \"creationDate\";\n    public static final String LAST_UPDATE_DATE = \"lastUpdateDate\";\n    @Id\n    private long id;\n    @Column\n    private String name;\n    @Column\n    private String description;\n    @Column\n    private long creator;\n    @Column\n    private long creationDate;\n    @Column\n    private long lastUpdateDate;\n\n    public SCategory(final String name) {\n        this.name = name;\n    }\n\n    public SCategory(final SCategory category) {\n        this.id = category.getId();\n        this.name = category.getName();\n        this.description = category.getDescription();\n        this.creator = category.getCreator();\n        this.creationDate = category.getCreationDate();\n        this.lastUpdateDate = category.getLastUpdateDate();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/model/SProcessCategoryMapping.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.category.model;\n\nimport javax.persistence.Cacheable;\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Matthieu Chaffotte\n */\n@Data\n@NoArgsConstructor\n@Entity\n@Table(name = \"processcategorymapping\")\n@Cacheable(false)\npublic class SProcessCategoryMapping implements PersistentObject {\n\n    @Id\n    private long id;\n    @Column\n    private long categoryId;\n    @Column\n    private long processId;\n\n    public SProcessCategoryMapping(final long categoryId, final long processId) {\n        this.categoryId = categoryId;\n        this.processId = processId;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/model/builder/SCategoryLogBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.category.model.builder;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\n\n/**\n * @author Yanyan Liu\n */\npublic interface SCategoryLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/model/builder/SCategoryLogBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.category.model.builder;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory;\n\n/**\n * @author Yanyan Liu\n */\npublic interface SCategoryLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory {\n\n    SCategoryLogBuilder createNewInstance();\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/model/builder/SCategoryUpdateBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.category.model.builder;\n\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Yanyan Liu\n */\npublic interface SCategoryUpdateBuilder {\n\n    SCategoryUpdateBuilder updateName(final String name);\n\n    SCategoryUpdateBuilder updateDescription(final String description);\n\n    EntityUpdateDescriptor done();\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/model/builder/SCategoryUpdateBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.category.model.builder;\n\n/**\n * @author Yanyan Liu\n */\npublic interface SCategoryUpdateBuilderFactory {\n\n    SCategoryUpdateBuilder createNewInstance();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/model/builder/SProcessCategoryMappingBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.category.model.builder;\n\nimport org.bonitasoft.engine.core.category.model.SProcessCategoryMapping;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface SProcessCategoryMappingBuilder {\n\n    SProcessCategoryMapping done();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/model/builder/SProcessCategoryMappingBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.category.model.builder;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface SProcessCategoryMappingBuilderFactory {\n\n    SProcessCategoryMappingBuilder createNewInstance(long categoryId, long processId);\n\n    String getIdKey();\n\n    String getCategoryIdKey();\n\n    String getProcessIdKey();\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/model/builder/impl/SCategoryLogBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.category.model.builder.impl;\n\nimport org.bonitasoft.engine.core.category.model.builder.SCategoryLogBuilderFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory;\n\n/**\n * @author Yanyan Liu\n */\npublic class SCategoryLogBuilderFactoryImpl extends CRUDELogBuilderFactory implements SCategoryLogBuilderFactory {\n\n    @Override\n    public SCategoryLogBuilderImpl createNewInstance() {\n        return new SCategoryLogBuilderImpl();\n    }\n\n    @Override\n    public String getObjectIdKey() {\n        return SCategoryLogIndexesMapper.CATEGORY_INDEX_NAME;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/model/builder/impl/SCategoryLogBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.category.model.builder.impl;\n\nimport org.bonitasoft.engine.core.category.model.builder.SCategoryLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException;\n\n/**\n * @author Yanyan Liu\n */\npublic class SCategoryLogBuilderImpl extends CRUDELogBuilder implements SCategoryLogBuilder {\n\n    private static final String PREFIX = \"CATEGORY\";\n\n    public SCategoryLogBuilderImpl() {\n        super();\n    }\n\n    @Override\n    protected String getActionTypePrefix() {\n        return PREFIX;\n    }\n\n    @Override\n    public SPersistenceLogBuilder objectId(final long objectId) {\n        this.queriableLogBuilder.numericIndex(SCategoryLogIndexesMapper.CATEGORY_INDEX, objectId);\n        return this;\n    }\n\n    @Override\n    protected void checkExtraRules(final SQueriableLog log) {\n        if (log.getActionStatus() != SQueriableLog.STATUS_FAIL) {\n            if (log.getNumericIndex(SCategoryLogIndexesMapper.CATEGORY_INDEX) == 0L) {\n                throw new MissingMandatoryFieldsException(\"Some mandatory fields are missing: \" + \"Category Id\");\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/model/builder/impl/SCategoryLogIndexesMapper.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.category.model.builder.impl;\n\n/**\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n */\npublic class SCategoryLogIndexesMapper {\n\n    public static final int CATEGORY_INDEX = 0;\n\n    public static final String CATEGORY_INDEX_NAME = \"numericIndex1\";\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/model/builder/impl/SCategoryUpdateBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.category.model.builder.impl;\n\nimport org.bonitasoft.engine.core.category.model.builder.SCategoryUpdateBuilder;\nimport org.bonitasoft.engine.core.category.model.builder.SCategoryUpdateBuilderFactory;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Yanyan Liu\n */\npublic class SCategoryUpdateBuilderFactoryImpl implements SCategoryUpdateBuilderFactory {\n\n    public SCategoryUpdateBuilder createNewInstance() {\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        return new SCategoryUpdateBuilderImpl(descriptor);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/model/builder/impl/SCategoryUpdateBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.category.model.builder.impl;\n\nimport org.bonitasoft.engine.core.category.model.SCategory;\nimport org.bonitasoft.engine.core.category.model.builder.SCategoryUpdateBuilder;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Yanyan Liu\n */\npublic class SCategoryUpdateBuilderImpl implements SCategoryUpdateBuilder {\n\n    private final EntityUpdateDescriptor descriptor;\n\n    public SCategoryUpdateBuilderImpl(final EntityUpdateDescriptor descriptor) {\n        super();\n        this.descriptor = descriptor;\n    }\n\n    @Override\n    public EntityUpdateDescriptor done() {\n        return this.descriptor;\n    }\n\n    @Override\n    public SCategoryUpdateBuilder updateName(final String name) {\n        this.descriptor.addField(SCategory.NAME, name);\n        return this;\n    }\n\n    @Override\n    public SCategoryUpdateBuilder updateDescription(final String description) {\n        this.descriptor.addField(SCategory.DESCRIPTION, description);\n        return this;\n    }\n\n    public static SCategoryUpdateBuilder getInstance() {\n        return new SCategoryUpdateBuilderImpl(null);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/model/builder/impl/SProcessCategoryMappingBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.category.model.builder.impl;\n\nimport org.bonitasoft.engine.core.category.model.SProcessCategoryMapping;\nimport org.bonitasoft.engine.core.category.model.builder.SProcessCategoryMappingBuilder;\nimport org.bonitasoft.engine.core.category.model.builder.SProcessCategoryMappingBuilderFactory;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SProcessCategoryMappingBuilderFactoryImpl implements SProcessCategoryMappingBuilderFactory {\n\n    @Override\n    public SProcessCategoryMappingBuilder createNewInstance(final long categoryId, final long processId) {\n        final SProcessCategoryMapping entity = new SProcessCategoryMapping(categoryId, processId);\n        return new SProcessCategoryMappingBuilderImpl(entity);\n    }\n\n    @Override\n    public String getIdKey() {\n        return \"id\";\n    }\n\n    @Override\n    public String getCategoryIdKey() {\n        return \"categoryId\";\n    }\n\n    @Override\n    public String getProcessIdKey() {\n        return \"processId\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/model/builder/impl/SProcessCategoryMappingBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.category.model.builder.impl;\n\nimport org.bonitasoft.engine.core.category.model.SProcessCategoryMapping;\nimport org.bonitasoft.engine.core.category.model.builder.SProcessCategoryMappingBuilder;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SProcessCategoryMappingBuilderImpl implements SProcessCategoryMappingBuilder {\n\n    private final SProcessCategoryMapping entity;\n\n    public SProcessCategoryMappingBuilderImpl(final SProcessCategoryMapping entity) {\n        super();\n        this.entity = entity;\n    }\n\n    @Override\n    public SProcessCategoryMapping done() {\n        return entity;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/persistence/SelectDescriptorBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.category.persistence;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.core.category.model.SCategory;\nimport org.bonitasoft.engine.core.category.model.SProcessCategoryMapping;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\n\n/**\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class SelectDescriptorBuilder {\n\n    private static final String PROCESS_ID = \"processId\";\n\n    public static SelectByIdDescriptor<SCategory> getCategory(final long categoryId) {\n        return new SelectByIdDescriptor<>(SCategory.class, categoryId);\n    }\n\n    public static SelectOneDescriptor<SCategory> getCategory(final String categoryName) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"name\", (Object) categoryName);\n        return new SelectOneDescriptor<>(\"getCategoryByName\", parameters, SCategory.class);\n    }\n\n    public static SelectOneDescriptor<Long> getNumberOfElement(final String elementName,\n            final Class<? extends PersistentObject> clazz) {\n        final Map<String, Object> parameters = Collections.emptyMap();\n        return new SelectOneDescriptor<>(\"getNumberOf\" + elementName, parameters, clazz, Long.class);\n    }\n\n    public static SelectListDescriptor<SCategory> getCategories(final String field, final OrderByType order,\n            final int fromIndex, final int numberOfProcesses) {\n        final Map<String, Object> parameters = Collections.emptyMap();\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfProcesses, SCategory.class, field, order);\n        return new SelectListDescriptor<>(\"getCategories\", parameters, SCategory.class, queryOptions);\n    }\n\n    public static SelectListDescriptor<SCategory> getCategoriesOfProcess(final long processId, final int fromIndex,\n            final int numberOfCategories,\n            final OrderByType order) {\n        final Map<String, Object> parameters = Collections.singletonMap(PROCESS_ID, (Object) processId);\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfCategories, SCategory.class, \"name\",\n                order);\n        return new SelectListDescriptor<>(\"getCategoriesOfProcess\", parameters, SCategory.class, queryOptions);\n    }\n\n    public static SelectListDescriptor<SCategory> getCategoriesUnrelatedToProcess(final long processId,\n            final int fromIndex, final int numberOfCategories,\n            final OrderByType order) {\n        final Map<String, Object> parameters = Collections.singletonMap(PROCESS_ID, (Object) processId);\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfCategories, SCategory.class, \"name\",\n                order);\n        return new SelectListDescriptor<>(\"getCategoriesUnrelatedToProcess\", parameters, SCategory.class,\n                queryOptions);\n    }\n\n    public static SelectOneDescriptor<Long> getNumberOfCategoriesOfProcess(final long processId) {\n        final Map<String, Object> parameters = Collections.singletonMap(PROCESS_ID, (Object) processId);\n        return new SelectOneDescriptor<>(\"getNumberOfCategoriesOfProcess\", parameters,\n                SProcessCategoryMapping.class);\n    }\n\n    public static SelectOneDescriptor<Long> getNumberOfCategoriesUnrelatedToProcess(final long processId) {\n        final Map<String, Object> parameters = Collections.singletonMap(PROCESS_ID, (Object) processId);\n        return new SelectOneDescriptor<>(\"getNumberOfCategoriesUnrelatedToProcess\", parameters,\n                SProcessCategoryMapping.class);\n    }\n\n    public static SelectOneDescriptor<Long> getNumberOfCategorizedProcessIds(final List<Long> processIds) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"processIds\", (Object) processIds);\n        return new SelectOneDescriptor<>(\"getNumberOfCategorizedProcessIds\", parameters,\n                SProcessCategoryMapping.class, Long.class);\n    }\n\n    public static SelectOneDescriptor<Long> isCategoryExistsInProcess(final long categoryId,\n            final long processDefinitionId) {\n        final Map<String, Object> parameters = new HashMap<>(2);\n        parameters.put(\"categoryId\", categoryId);\n        parameters.put(\"processDefinitionId\", processDefinitionId);\n        return new SelectOneDescriptor<>(\"isCategoryExistsInProcess\", parameters, SProcessCategoryMapping.class);\n    }\n\n    public static SelectListDescriptor<SProcessCategoryMapping> getCategoryMappingOfProcessAndCategories(\n            final long processDefinitionId,\n            final List<Long> categoryIds, final int fromIndex, final int maxResults) {\n        final QueryOptions queryOptions = buildQueryOptionsForCategoryMappingOrderedByCategoryId(fromIndex, maxResults,\n                OrderByType.ASC);\n        final Map<String, Object> parameters = new HashMap<>(2);\n        parameters.put(\"categoryIds\", categoryIds);\n        parameters.put(\"processDefinitionId\", processDefinitionId);\n        return new SelectListDescriptor<>(\"getCategoryMappingOfProcessAndCategories\", parameters,\n                SProcessCategoryMapping.class,\n                queryOptions);\n    }\n\n    public static QueryOptions buildQueryOptionsForCategoryMappingOrderedByCategoryId(final int fromIndex,\n            final int maxResults, final OrderByType order) {\n        return new QueryOptions(fromIndex, maxResults, SProcessCategoryMapping.class, \"categoryId\", order);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-category/src/main/resources/org/bonitasoft/engine/core/category/model/impl/hibernate/category.queries.hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\" \"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd\">\n<hibernate-mapping auto-import=\"false\">\n\n\t<!-- category -->\n\n\t<query name=\"getCategoryByName\">\n\t\tSELECT category\n\t\tFROM org.bonitasoft.engine.core.category.model.SCategory AS category\n\t\tWHERE category.name = :name\n\t</query>\n\n\t<query name=\"getNumberOfCategory\">\n\t\tSELECT COUNT(category.id)\n\t\tFROM org.bonitasoft.engine.core.category.model.SCategory AS category\n\t</query>\n\n\t<query name=\"getCategories\">\n\t\tSELECT category\n\t\tFROM org.bonitasoft.engine.core.category.model.SCategory AS category\n\t</query>\n\n\t<query name=\"getCategoriesOfProcess\">\n\t\tSELECT category\n\t\tFROM org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping,\n\t\t     org.bonitasoft.engine.core.category.model.SCategory AS category\n\t\tWHERE categorymapping.processId = :processId\n\t\tAND category.id = categorymapping.categoryId\n\t</query>\n\t\n\t<query name=\"getCategoriesUnrelatedToProcess\">\n\t\tSELECT category\n\t\tFROM org.bonitasoft.engine.core.category.model.SCategory AS category\n\t\tWHERE category.id NOT IN (\n\t\t\tSELECT categorymapping.categoryId\n\t\t\tFROM org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping\n\t\t\tWHERE categorymapping.processId = :processId)\n\t</query>\n\t\n\t<query name=\"getNumberOfCategoriesUnrelatedToProcess\">\n\t\tSELECT COUNT(category.id)\n\t\tFROM org.bonitasoft.engine.core.category.model.SCategory AS category\n\t\tWHERE category.id NOT IN (\n\t\t\tSELECT categorymapping.categoryId\n\t\t\tFROM org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping\n\t\t\tWHERE categorymapping.processId = :processId)\n\t</query>\n\t\n\t<!--                                         Category Mapping                                            -->\n\t<query name=\"searchSProcessCategoryMapping\">\n\t\tSELECT categorymapping\n\t\tFROM org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping\n\t</query>\n\t\n\t<query name=\"getCategoryMappingOfProcessAndCategories\">\n\t\tSELECT categorymapping\n\t\tFROM org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping\n\t\tWHERE categorymapping.processId = :processDefinitionId\n\t\tAND categorymapping.categoryId IN (:categoryIds)\n\t</query>\n\t\n\t<query name=\"getNumberOfCategoriesOfProcess\">\n\t\tSELECT COUNT(categorymapping.id)\n\t\tFROM org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping\n\t\tWHERE categorymapping.processId = :processId\n\t</query>\n    \n    <query name=\"getNumberOfProcessDefinitionsOfCategory\">\n        SELECT COUNT(categorymapping.id)\n        FROM org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping\n        WHERE categorymapping.categoryId = :categoryId\n    </query>\n\n\t<query name=\"getNumberOfCategorizedProcessIds\">\n\t\tSELECT COUNT(categorymapping.id)\n\t\tFROM org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping\n\t\tWHERE categorymapping.processId IN (:processIds)\n\t</query>\n\n\t<query name=\"isCategoryExistsInProcess\">\n\t\tSELECT COUNT(categorymapping.id)\n\t\tFROM org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping\n\t\tWHERE categorymapping.processId = :processDefinitionId\n\t\tAND categorymapping.categoryId = :categoryId\n\t</query>\n    \n</hibernate-mapping>\n"
  },
  {
    "path": "bpm/bonita-core/bonita-category/src/test/java/org/bonitasoft/engine/core/category/impl/CategoryServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.category.impl;\n\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.doThrow;\nimport static org.mockito.Mockito.mock;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.category.exception.SCategoryException;\nimport org.bonitasoft.engine.core.category.exception.SCategoryNotFoundException;\nimport org.bonitasoft.engine.core.category.model.SCategory;\nimport org.bonitasoft.engine.core.category.persistence.SelectDescriptorBuilder;\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\n/**\n * @author Celine Souchet\n */\npublic class CategoryServiceImplTest {\n\n    @Mock\n    private Recorder recorder;\n\n    @Mock\n    private ReadPersistenceService persistenceService;\n\n    @Mock\n    private EventService eventService;\n\n    @Mock\n    private QueriableLoggerService queriableLoggerService;\n\n    @Mock\n    private SessionService sessionService;\n\n    @Mock\n    private ReadSessionAccessor sessionAccessor;\n\n    @InjectMocks\n    private CategoryServiceImpl categoryServiceImpl;\n\n    @Before\n    public void setUp() {\n        MockitoAnnotations.initMocks(this);\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.core.category.impl.CategoryServiceImpl#getCategory(long)}.\n     *\n     * @throws SCategoryNotFoundException\n     * @throws SBonitaReadException\n     */\n    @Test\n    public final void getCategoryById() throws SCategoryNotFoundException, SBonitaReadException {\n        // Given\n        final long id = 456L;\n        final SCategory sCategory = mock(SCategory.class);\n        doReturn(sCategory).when(persistenceService).selectById(SelectDescriptorBuilder.getCategory(id));\n\n        // When\n        final SCategory category = categoryServiceImpl.getCategory(id);\n\n        // Then\n        Assert.assertEquals(sCategory, category);\n    }\n\n    @Test(expected = SCategoryNotFoundException.class)\n    public final void getCategoryByIdNotExists() throws SBonitaReadException, SCategoryNotFoundException {\n        // Given\n        final long id = 456L;\n        doReturn(null).when(persistenceService).selectById(SelectDescriptorBuilder.getCategory(id));\n\n        // When\n        categoryServiceImpl.getCategory(id);\n    }\n\n    @Test(expected = SCategoryNotFoundException.class)\n    public final void getCategoryByIdThrowException() throws SBonitaReadException, SCategoryNotFoundException {\n        // Given\n        final long id = 456L;\n        doThrow(new SBonitaReadException(\"\")).when(persistenceService)\n                .selectById(SelectDescriptorBuilder.getCategory(id));\n\n        // When\n        categoryServiceImpl.getCategory(id);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.category.impl.CategoryServiceImpl#getCategories(int, int, java.lang.String, org.bonitasoft.engine.persistence.OrderByType)}\n     * .\n     *\n     * @throws SCategoryException\n     * @throws SBonitaReadException\n     */\n    @Test\n    public final void getCategories() throws SCategoryException, SBonitaReadException {\n        // Given\n        final List<SCategory> sCategories = new ArrayList<SCategory>();\n        final String field = \"field\";\n        final OrderByType order = OrderByType.ASC;\n        final int fromIndex = 0;\n        final int numberOfCategories = 1;\n        doReturn(sCategories).when(persistenceService)\n                .selectList(SelectDescriptorBuilder.getCategories(field, order, fromIndex, numberOfCategories));\n\n        // When\n        final List<SCategory> categories = categoryServiceImpl.getCategories(fromIndex, numberOfCategories, field,\n                order);\n\n        // Then\n        Assert.assertEquals(sCategories, categories);\n    }\n\n    @Test(expected = SCategoryException.class)\n    public final void getCategoriesThrowException() throws SBonitaReadException, SCategoryException {\n        // Given\n        final String field = \"field\";\n        final OrderByType order = OrderByType.ASC;\n        final int fromIndex = 0;\n        final int numberOfCategories = 1;\n        doThrow(new SBonitaReadException(\"\")).when(persistenceService).selectList(\n                SelectDescriptorBuilder.getCategories(field, order, fromIndex, numberOfCategories));\n\n        // When\n        categoryServiceImpl.getCategories(fromIndex, numberOfCategories, field, order);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.category.impl.CategoryServiceImpl#getCategoriesOfProcessDefinition(long, int, int, org.bonitasoft.engine.persistence.OrderByType)}\n     * .\n     *\n     * @throws SCategoryException\n     * @throws SBonitaReadException\n     */\n    @Test\n    public final void getCategoriesOfProcessDefinition() throws SCategoryException, SBonitaReadException {\n        // Given\n        final List<SCategory> sCategories = new ArrayList<SCategory>();\n        final long processDefinitionId = 2L;\n        final int fromIndex = 0;\n        final int numberOfCategories = 1;\n        final OrderByType order = OrderByType.ASC;\n        doReturn(sCategories).when(persistenceService).selectList(\n                SelectDescriptorBuilder.getCategoriesOfProcess(processDefinitionId, fromIndex, numberOfCategories,\n                        order));\n\n        // When\n        final List<SCategory> categoriesOfProcessDefinition = categoryServiceImpl.getCategoriesOfProcessDefinition(\n                processDefinitionId, fromIndex,\n                numberOfCategories, order);\n\n        // Then\n        Assert.assertEquals(sCategories, categoriesOfProcessDefinition);\n    }\n\n    @Test(expected = SCategoryException.class)\n    public final void getCategoriesOfProcessDefinitionThrowException() throws SBonitaReadException, SCategoryException {\n        // Given\n        final long processDefinitionId = 2L;\n        final int fromIndex = 0;\n        final int numberOfCategories = 1;\n        final OrderByType order = OrderByType.ASC;\n        doThrow(new SBonitaReadException(\"\")).when(persistenceService).selectList(\n                SelectDescriptorBuilder.getCategoriesOfProcess(processDefinitionId, fromIndex, numberOfCategories,\n                        order));\n\n        // When\n        categoryServiceImpl.getCategoriesOfProcessDefinition(processDefinitionId, fromIndex, numberOfCategories, order);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.category.impl.CategoryServiceImpl#getCategoryByName(java.lang.String)}.\n     *\n     * @throws SCategoryNotFoundException\n     * @throws SBonitaReadException\n     */\n    @Test\n    public final void getCategoryByName() throws SCategoryNotFoundException, SBonitaReadException {\n        // Given\n        final SCategory sCategory = mock(SCategory.class);\n        final String name = \"name\";\n        doReturn(sCategory).when(persistenceService).selectOne(SelectDescriptorBuilder.getCategory(name));\n\n        // When\n        final SCategory categoryByName = categoryServiceImpl.getCategoryByName(name);\n\n        // Then\n        Assert.assertEquals(sCategory, categoryByName);\n    }\n\n    @Test(expected = SCategoryNotFoundException.class)\n    public final void getCategoryByNameNotExists() throws SBonitaReadException, SCategoryNotFoundException {\n        // Given\n        final String name = \"name\";\n        doReturn(null).when(persistenceService).selectOne(SelectDescriptorBuilder.getCategory(name));\n\n        // When\n        categoryServiceImpl.getCategoryByName(name);\n    }\n\n    @Test(expected = SCategoryNotFoundException.class)\n    public final void getCategoryByNameThrowException() throws SBonitaReadException, SCategoryNotFoundException {\n        // Given\n        final String name = \"name\";\n        doThrow(new SBonitaReadException(\"\")).when(persistenceService)\n                .selectOne(SelectDescriptorBuilder.getCategory(name));\n\n        // When\n        categoryServiceImpl.getCategoryByName(name);\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.core.category.impl.CategoryServiceImpl#getNumberOfCategories()}.\n     *\n     * @throws SBonitaReadException\n     * @throws SCategoryException\n     */\n    @Test\n    public final void getNumberOfCategories() throws SBonitaReadException, SCategoryException {\n        // Given\n        final long numberOfCategories = 3L;\n        doReturn(numberOfCategories).when(persistenceService)\n                .selectOne(SelectDescriptorBuilder.getNumberOfElement(\"Category\", SCategory.class));\n\n        // When\n        final long result = categoryServiceImpl.getNumberOfCategories();\n\n        // Then\n        Assert.assertEquals(numberOfCategories, result);\n    }\n\n    @Test(expected = SCategoryException.class)\n    public final void getNumberOfCategoriesThrowException() throws SCategoryException, SBonitaReadException {\n        // Given\n        doThrow(new SBonitaReadException(\"\")).when(persistenceService)\n                .selectOne(SelectDescriptorBuilder.getNumberOfElement(\"Category\", SCategory.class));\n\n        // When\n        categoryServiceImpl.getNumberOfCategories();\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.category.impl.CategoryServiceImpl#getNumberOfCategoriesOfProcess(long)}.\n     *\n     * @throws SCategoryException\n     * @throws SBonitaReadException\n     */\n    @Test\n    public final void getNumberOfCategoriesOfProcess() throws SCategoryException, SBonitaReadException {\n        // Given\n        final long numberOfCategories = 3L;\n        final long processDefinitionId = 1589L;\n        doReturn(numberOfCategories).when(persistenceService)\n                .selectOne(SelectDescriptorBuilder.getNumberOfCategoriesOfProcess(processDefinitionId));\n\n        // When\n        final long result = categoryServiceImpl.getNumberOfCategoriesOfProcess(processDefinitionId);\n\n        // Then\n        Assert.assertEquals(numberOfCategories, result);\n    }\n\n    @Test(expected = SCategoryException.class)\n    public final void getNumberOfCategoriesOfProcessThrowException() throws SCategoryException, SBonitaReadException {\n        // Given\n        final long processDefinitionId = 1589L;\n        doThrow(new SBonitaReadException(\"\")).when(persistenceService)\n                .selectOne(SelectDescriptorBuilder.getNumberOfCategoriesOfProcess(processDefinitionId));\n\n        // When\n        categoryServiceImpl.getNumberOfCategoriesOfProcess(processDefinitionId);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.category.impl.CategoryServiceImpl#getNumberOfCategoriesUnrelatedToProcess(long)}.\n     *\n     * @throws SCategoryException\n     * @throws SBonitaReadException\n     */\n    @Test\n    public final void getNumberOfCategoriesUnrelatedToProcess() throws SCategoryException, SBonitaReadException {\n        // Given\n        final long numberOfCategories = 3L;\n        final long processDefinitionId = 1589L;\n        doReturn(numberOfCategories).when(persistenceService)\n                .selectOne(SelectDescriptorBuilder.getNumberOfCategoriesUnrelatedToProcess(processDefinitionId));\n\n        // When\n        final long numberOfCategoriesUnrelatedToProcess = categoryServiceImpl\n                .getNumberOfCategoriesUnrelatedToProcess(processDefinitionId);\n\n        // Then\n        Assert.assertEquals(numberOfCategories, numberOfCategoriesUnrelatedToProcess);\n    }\n\n    @Test(expected = SCategoryException.class)\n    public final void getNumberOfCategoriesUnrelatedToProcessThrowException()\n            throws SCategoryException, SBonitaReadException {\n        // Given\n        final long processDefinitionId = 1589L;\n        doThrow(new SBonitaReadException(\"\")).when(persistenceService).selectOne(\n                SelectDescriptorBuilder.getNumberOfCategoriesUnrelatedToProcess(processDefinitionId));\n\n        // When\n        categoryServiceImpl.getNumberOfCategoriesUnrelatedToProcess(processDefinitionId);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.category.impl.CategoryServiceImpl#getNumberOfCategorizedProcessIds(java.util.List)}.\n     *\n     * @throws SCategoryException\n     * @throws SBonitaReadException\n     */\n    @Test\n    public final void getNumberOfCategorizedProcessIds() throws SCategoryException, SBonitaReadException {\n        // Given\n        final List<Long> processIds = new ArrayList<Long>();\n        processIds.add(14654L);\n        final long numberOfCategories = 3L;\n        doReturn(numberOfCategories).when(persistenceService)\n                .selectOne(SelectDescriptorBuilder.getNumberOfCategorizedProcessIds(processIds));\n\n        // When\n        final long numberOfCategorizedProcessIds = categoryServiceImpl.getNumberOfCategorizedProcessIds(processIds);\n\n        // Then\n        Assert.assertEquals(numberOfCategories, numberOfCategorizedProcessIds);\n    }\n\n    @Test\n    public final void getNumberOfCategorizedProcessIdsWithNullList() throws SCategoryException {\n        // When\n        final long numberOfCategorizedProcessIds = categoryServiceImpl.getNumberOfCategorizedProcessIds(null);\n\n        // Then\n        Assert.assertEquals(0, numberOfCategorizedProcessIds);\n    }\n\n    @Test\n    public final void getNumberOfCategorizedProcessIdsWithEmptyList() throws SCategoryException {\n        // When\n        final long numberOfCategorizedProcessIds = categoryServiceImpl\n                .getNumberOfCategorizedProcessIds(new ArrayList<Long>());\n\n        // Then\n        Assert.assertEquals(0, numberOfCategorizedProcessIds);\n    }\n\n    @Test(expected = SCategoryException.class)\n    public final void getNumberOfCategorizedProcessIdsThrowException() throws SBonitaReadException, SCategoryException {\n        // Given\n        final List<Long> processIds = new ArrayList<Long>();\n        processIds.add(14654L);\n        doThrow(new SBonitaReadException(\"\")).when(persistenceService)\n                .selectOne(SelectDescriptorBuilder.getNumberOfCategorizedProcessIds(processIds));\n\n        // When\n        categoryServiceImpl.getNumberOfCategorizedProcessIds(processIds);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.category.impl.CategoryServiceImpl#getCategoriesUnrelatedToProcessDefinition(long, int, int, org.bonitasoft.engine.persistence.OrderByType)}\n     * .\n     */\n    @Test\n    public final void getCategoriesUnrelatedToProcessDefinition() throws SBonitaReadException, SCategoryException {\n        // Given\n        final List<SCategory> sCategories = new ArrayList<SCategory>();\n        final long processDefinitionId = 54894L;\n        final int fromIndex = 0;\n        final int numberOfCategories = 10;\n        final OrderByType order = OrderByType.ASC;\n        doReturn(sCategories).when(persistenceService)\n                .selectList(SelectDescriptorBuilder.getCategoriesUnrelatedToProcess(processDefinitionId, fromIndex,\n                        numberOfCategories, order));\n\n        // When\n        final List<SCategory> categoriesUnrelatedToProcessDefinition = categoryServiceImpl\n                .getCategoriesUnrelatedToProcessDefinition(processDefinitionId,\n                        fromIndex, numberOfCategories, order);\n\n        // Then\n        Assert.assertEquals(sCategories, categoriesUnrelatedToProcessDefinition);\n    }\n\n    @Test(expected = SCategoryException.class)\n    public final void getCategoriesUnrelatedToProcessDefinitionThrowException()\n            throws SBonitaReadException, SCategoryException {\n        // Given\n        final long processDefinitionId = 54894L;\n        final int fromIndex = 0;\n        final int numberOfCategories = 10;\n        final OrderByType order = OrderByType.ASC;\n        doThrow(new SBonitaReadException(\"\")).when(persistenceService).selectList(\n                SelectDescriptorBuilder.getCategoriesUnrelatedToProcess(processDefinitionId, fromIndex,\n                        numberOfCategories, order));\n\n        // When\n        categoryServiceImpl.getCategoriesUnrelatedToProcessDefinition(processDefinitionId, fromIndex,\n                numberOfCategories, order);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-contract-data/build.gradle",
    "content": "\n\ndependencies {\n    api project(':services:bonita-commons')\n    api project(':services:bonita-builder')\n    api project(':services:bonita-log')\n    api project(':services:bonita-archive')\n    api project(':services:bonita-persistence')\n\n    implementation platform(libs.bonitaArtifactsModelBom)\n    implementation 'org.bonitasoft.engine:bonita-process-definition-model'\n\n    testImplementation libs.assertj\n    testImplementation libs.mockitoCore\n\n    annotationProcessor libs.lombok\n    compileOnly libs.lombok\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-contract-data/src/main/java/org/bonitasoft/engine/core/contract/data/ContractDataService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.contract.data;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface ContractDataService {\n\n    void addUserTaskData(final long userTaskId, Map<String, Serializable> data) throws SContractDataCreationException;\n\n    Serializable getUserTaskDataValue(final long userTaskId, String dataName)\n            throws SContractDataNotFoundException, SBonitaReadException;\n\n    void deleteUserTaskData(final long userTaskId) throws SContractDataDeletionException;\n\n    /**\n     * delete all archived user task data from the list of user task ids (sourceObjectIds)\n     *\n     * @param sourceUserTaskIds list of user tasks ids (non-archived)\n     * @throws SContractDataDeletionException\n     */\n    void deleteArchivedUserTaskData(List<Long> sourceUserTaskIds) throws SContractDataDeletionException;\n\n    void archiveAndDeleteUserTaskData(final long userTaskId, final long archiveDate)\n            throws SObjectModificationException;\n\n    Serializable getArchivedUserTaskDataValue(final long userTaskId, String dataName)\n            throws SContractDataNotFoundException, SBonitaReadException;\n\n    void addProcessData(long processInstanceId, Map<String, Serializable> data) throws SContractDataCreationException;\n\n    Serializable getProcessDataValue(long processInstanceId, String dataName)\n            throws SContractDataNotFoundException, SBonitaReadException;\n\n    void deleteProcessData(long processInstanceId) throws SContractDataDeletionException;\n\n    /**\n     * delete all archived user task data from the list of process instance ids (sourceObjectIds)\n     *\n     * @param sourceProcessInstanceIds list of process instance ids (non-archived)\n     * @throws SContractDataDeletionException\n     */\n    void deleteArchivedProcessData(List<Long> sourceProcessInstanceIds) throws SContractDataDeletionException;\n\n    void archiveAndDeleteProcessData(long processInstanceId, long archiveDate) throws SObjectModificationException;\n\n    Serializable getArchivedProcessDataValue(long processInstanceId, String dataName)\n            throws SContractDataNotFoundException,\n            SBonitaReadException;\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-contract-data/src/main/java/org/bonitasoft/engine/core/contract/data/ContractDataServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.contract.data;\n\nimport static java.util.Collections.singletonMap;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.bonitasoft.engine.archive.ArchiveInsertRecord;\nimport org.bonitasoft.engine.archive.ArchiveService;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity;\nimport org.bonitasoft.engine.queriablelogger.model.builder.ActionType;\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class ContractDataServiceImpl implements ContractDataService {\n\n    private static final String PROCESS_CONTRACT_DATA = \"PROCESS_CONTRACT_DATA\";\n    private static final String USERTASK_CONTRACT_DATA = \"USERTASK_CONTRACT_DATA\";\n\n    private final ReadPersistenceService persistenceService;\n\n    private final Recorder recorder;\n\n    private final QueriableLoggerService queriableLoggerService;\n\n    private final ArchiveService archiveService;\n\n    public ContractDataServiceImpl(final ReadPersistenceService persistenceService, final Recorder recorder,\n            final QueriableLoggerService queriableLoggerService, final ArchiveService archiveService) {\n        this.persistenceService = persistenceService;\n        this.recorder = recorder;\n        this.queriableLoggerService = queriableLoggerService;\n        this.archiveService = archiveService;\n    }\n\n    @Override\n    public void addUserTaskData(final long userTaskId, final Map<String, Serializable> data)\n            throws SContractDataCreationException {\n        if (data == null) {\n            return;\n        }\n        for (final Entry<String, Serializable> datum : data.entrySet()) {\n            addUserTaskData(new STaskContractData(userTaskId, datum.getKey(), datum.getValue()));\n        }\n    }\n\n    protected void addUserTaskData(final STaskContractData taskContractData) throws SContractDataCreationException {\n        final SContractDataLogBuilder logBuilder = getQueriableLog(ActionType.CREATED,\n                \"Creating a new user task contract data\", USERTASK_CONTRACT_DATA);\n        try {\n            recorder.recordInsert(new InsertRecord(taskContractData), USERTASK_CONTRACT_DATA);\n            initiateLogBuilder(taskContractData.getId(), SQueriableLog.STATUS_OK, logBuilder, \"addUserTaskData\");\n        } catch (final SRecorderException re) {\n            initiateLogBuilder(taskContractData.getId(), SQueriableLog.STATUS_FAIL, logBuilder, \"addUserTaskData\");\n            throw new SContractDataCreationException(re);\n        }\n    }\n\n    @Override\n    public Serializable getUserTaskDataValue(final long userTaskId, final String dataName)\n            throws SContractDataNotFoundException, SBonitaReadException {\n        final Map<String, Object> parameters = new HashMap<String, Object>();\n        parameters.put(\"name\", dataName);\n        parameters.put(\"scopeId\", userTaskId);\n        final SelectOneDescriptor<STaskContractData> descriptor = new SelectOneDescriptor<STaskContractData>(\n                \"getContractDataByUserTaskIdAndDataName\",\n                parameters, STaskContractData.class);\n        final STaskContractData contractData = persistenceService.selectOne(descriptor);\n        if (contractData == null) {\n            throw new SContractDataNotFoundException(\n                    \"No contract data found named: \" + dataName + \" of user task: \" + userTaskId);\n        }\n        return contractData.getValue();\n    }\n\n    @Override\n    public void deleteUserTaskData(final long userTaskId) throws SContractDataDeletionException {\n        try {\n            final List<STaskContractData> contractData = getContractDataOfUserTask(userTaskId);\n            for (final STaskContractData data : contractData) {\n                deleteUserTaskData(data);\n            }\n        } catch (final SBonitaReadException sbre) {\n            throw new SContractDataDeletionException(sbre);\n        }\n    }\n\n    @Override\n    public void deleteArchivedUserTaskData(List<Long> sourceUserTaskIds) throws SContractDataDeletionException {\n        try {\n            archiveService.deleteFromQuery(\"deleteArchivedTaskContractData\",\n                    singletonMap(\"scopeIds\", sourceUserTaskIds));\n        } catch (SRecorderException e) {\n            throw new SContractDataDeletionException(e);\n        }\n    }\n\n    protected void deleteUserTaskData(final STaskContractData taskContractData) throws SContractDataDeletionException {\n        try {\n            recorder.recordDelete(new DeleteRecord(taskContractData), USERTASK_CONTRACT_DATA);\n        } catch (final SRecorderException sre) {\n            throw new SContractDataDeletionException(sre);\n        }\n    }\n\n    @Override\n    public void archiveAndDeleteUserTaskData(final long userTaskId, final long archiveDate)\n            throws SObjectModificationException {\n        try {\n            final List<STaskContractData> contractData = getContractDataOfUserTask(userTaskId);\n            if (!contractData.isEmpty()) {\n                if (archiveService.isArchivable(SContractData.class)) {\n                    final ArchiveInsertRecord[] records = buildArchiveUserTaskRecords(contractData);\n                    archiveService.recordInserts(archiveDate, records);\n                }\n                for (STaskContractData taskContractData : contractData) {\n                    deleteUserTaskData(taskContractData);\n                }\n            }\n        } catch (final SBonitaException sbe) {\n            throw new SObjectModificationException(sbe);\n        }\n    }\n\n    private ArchiveInsertRecord[] buildArchiveUserTaskRecords(final List<STaskContractData> taskContractData) {\n        final ArchiveInsertRecord[] records = new ArchiveInsertRecord[taskContractData.size()];\n        int i = 0;\n        for (final STaskContractData data : taskContractData) {\n            if (data != null) {\n                final SATaskContractData aData = new SATaskContractData(data);\n                records[i] = new ArchiveInsertRecord(aData);\n                i++;\n            }\n        }\n        return records;\n    }\n\n    private List<STaskContractData> getContractDataOfUserTask(final long userTaskId) throws SBonitaReadException {\n        final Map<String, Object> parameters = new HashMap<String, Object>();\n        parameters.put(\"scopeId\", userTaskId);\n        final QueryOptions queryOptions = new QueryOptions(0, 10000);\n        final SelectListDescriptor<STaskContractData> descriptor = new SelectListDescriptor<STaskContractData>(\n                \"getContractDataByUserTaskId\", parameters,\n                STaskContractData.class, queryOptions);\n        return persistenceService.selectList(descriptor);\n    }\n\n    private SContractDataLogBuilder getQueriableLog(final ActionType actionType, final String message,\n            String contractDataPrefix) {\n        final SContractDataLogBuilder logBuilder = new SContractDataLogBuilder(contractDataPrefix);\n        this.initializeLogBuilder(logBuilder, message);\n        this.updateLog(actionType, logBuilder);\n        return logBuilder;\n    }\n\n    private <T extends SLogBuilder> void initializeLogBuilder(final T logBuilder, final String message) {\n        logBuilder.actionStatus(SQueriableLog.STATUS_FAIL).severity(SQueriableLogSeverity.INTERNAL).rawMessage(message);\n    }\n\n    private <T extends HasCRUDEAction> void updateLog(final ActionType actionType, final T logBuilder) {\n        logBuilder.setActionType(actionType);\n    }\n\n    private void initiateLogBuilder(final long objectId, final int sQueriableLogStatus,\n            final SPersistenceLogBuilder logBuilder, final String callerMethodName) {\n        logBuilder.actionScope(String.valueOf(objectId));\n        logBuilder.actionStatus(sQueriableLogStatus);\n        logBuilder.objectId(objectId);\n        final SQueriableLog log = logBuilder.build();\n        if (queriableLoggerService.isLoggable(log.getActionType(), log.getSeverity())) {\n            queriableLoggerService.log(this.getClass().getName(), callerMethodName, log);\n        }\n    }\n\n    @Override\n    public Serializable getArchivedUserTaskDataValue(final long userTaskId, final String dataName)\n            throws SContractDataNotFoundException, SBonitaReadException {\n        final ReadPersistenceService readPersistenceService = archiveService\n                .getDefinitiveArchiveReadPersistenceService();\n        final Map<String, Object> parameters = new HashMap<String, Object>();\n        parameters.put(\"scopeId\", userTaskId);\n        parameters.put(\"name\", dataName);\n        final SelectOneDescriptor<SATaskContractData> descriptor = new SelectOneDescriptor<SATaskContractData>(\n                \"getArchivedContractDataByUserTaskIdAndDataName\", parameters, SATaskContractData.class);\n        final SATaskContractData contractData = readPersistenceService.selectOne(descriptor);\n        if (contractData == null) {\n            throw new SContractDataNotFoundException(\n                    \"No contract data found named: \" + dataName + \" of user task: \" + userTaskId);\n        }\n        return contractData.getValue();\n    }\n\n    @Override\n    public void addProcessData(final long processInstanceId, final Map<String, Serializable> data)\n            throws SContractDataCreationException {\n        if (data == null) {\n            return;\n        }\n        for (final Entry<String, Serializable> datum : data.entrySet()) {\n            addProcessData(new SProcessContractData(processInstanceId, datum.getKey(), datum.getValue()));\n        }\n    }\n\n    protected void addProcessData(SProcessContractData processContractData) throws SContractDataCreationException {\n        final SContractDataLogBuilder logBuilder = getQueriableLog(ActionType.CREATED,\n                \"Creating a new process contract data\", PROCESS_CONTRACT_DATA);\n        try {\n            recorder.recordInsert(new InsertRecord(processContractData), PROCESS_CONTRACT_DATA);\n            initiateLogBuilder(processContractData.getId(), SQueriableLog.STATUS_OK, logBuilder, \"addProcessData\");\n        } catch (final SRecorderException re) {\n            initiateLogBuilder(processContractData.getId(), SQueriableLog.STATUS_FAIL, logBuilder, \"addProcessData\");\n            throw new SContractDataCreationException(re);\n        }\n    }\n\n    @Override\n    public Serializable getProcessDataValue(final long processInstanceId, final String dataName)\n            throws SContractDataNotFoundException, SBonitaReadException {\n        final Map<String, Object> parameters = new HashMap<String, Object>();\n        parameters.put(\"name\", dataName);\n        parameters.put(\"scopeId\", processInstanceId);\n        final SelectOneDescriptor<SProcessContractData> descriptor = new SelectOneDescriptor<>(\n                \"getContractDataByProcessInstanceIdAndDataName\", parameters,\n                SProcessContractData.class);\n        final SProcessContractData contractData = persistenceService.selectOne(descriptor);\n        if (contractData == null) {\n            throw new SContractDataNotFoundException(\"No contract data found named: \" + dataName\n                    + \" for process instance with id: \" + processInstanceId);\n        }\n        return contractData.getValue();\n    }\n\n    @Override\n    public void deleteProcessData(final long processInstanceId) throws SContractDataDeletionException {\n        try {\n            final List<SProcessContractData> contractData = getContractDataOfProcess(processInstanceId);\n            for (final SProcessContractData data : contractData) {\n                deleteProcessData(data);\n            }\n        } catch (final SBonitaReadException sbre) {\n            throw new SContractDataDeletionException(sbre);\n        }\n    }\n\n    @Override\n    public void deleteArchivedProcessData(List<Long> sourceProcessInstanceIds) throws SContractDataDeletionException {\n        try {\n            archiveService.deleteFromQuery(\"deleteArchivedProcessContractData\",\n                    singletonMap(\"scopeIds\", sourceProcessInstanceIds));\n        } catch (SRecorderException e) {\n            throw new SContractDataDeletionException(e);\n        }\n    }\n\n    protected void deleteProcessData(final SProcessContractData processContractData)\n            throws SContractDataDeletionException {\n        try {\n            recorder.recordDelete(new DeleteRecord(processContractData), PROCESS_CONTRACT_DATA);\n        } catch (final SRecorderException sre) {\n            throw new SContractDataDeletionException(sre);\n        }\n    }\n\n    @Override\n    public void archiveAndDeleteProcessData(final long processInstanceId, final long archiveDate)\n            throws SObjectModificationException {\n        try {\n            final List<SProcessContractData> contractData = getContractDataOfProcess(processInstanceId);\n            if (!contractData.isEmpty()) {\n                if (archiveService.isArchivable(SContractData.class)) {\n                    final ArchiveInsertRecord[] records = buildArchiveProcessRecords(contractData);\n                    archiveService.recordInserts(archiveDate, records);\n                }\n                for (SProcessContractData processContractData : contractData) {\n                    deleteProcessData(processContractData);\n                }\n            }\n        } catch (final SBonitaException sbe) {\n            throw new SObjectModificationException(sbe);\n        }\n    }\n\n    private ArchiveInsertRecord[] buildArchiveProcessRecords(final List<SProcessContractData> processContractData) {\n        final ArchiveInsertRecord[] records = new ArchiveInsertRecord[processContractData.size()];\n        int i = 0;\n        for (final SProcessContractData data : processContractData) {\n            if (data != null) {\n                final SAProcessContractData aData = new SAProcessContractData(data);\n                records[i] = new ArchiveInsertRecord(aData);\n                i++;\n            }\n        }\n        return records;\n    }\n\n    private List<SProcessContractData> getContractDataOfProcess(final long processInstanceId)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = new HashMap<String, Object>();\n        parameters.put(\"scopeId\", processInstanceId);\n        final QueryOptions queryOptions = new QueryOptions(0, 10000);\n        final SelectListDescriptor<SProcessContractData> descriptor = new SelectListDescriptor<>(\n                \"getContractDataByProcessInstanceId\",\n                parameters, SProcessContractData.class, queryOptions);\n        return persistenceService.selectList(descriptor);\n    }\n\n    @Override\n    public Serializable getArchivedProcessDataValue(final long processInstanceId, final String dataName)\n            throws SContractDataNotFoundException,\n            SBonitaReadException {\n        final ReadPersistenceService readPersistenceService = archiveService\n                .getDefinitiveArchiveReadPersistenceService();\n        final Map<String, Object> parameters = new HashMap<String, Object>();\n        parameters.put(\"scopeId\", processInstanceId);\n        parameters.put(\"name\", dataName);\n        final SelectOneDescriptor<SAProcessContractData> descriptor = new SelectOneDescriptor<>(\n                \"getArchivedContractDataByProcessInstanceIdAndDataName\", parameters, SAProcessContractData.class);\n        final SAProcessContractData contractData = readPersistenceService.selectOne(descriptor);\n        if (contractData == null) {\n            throw new SContractDataNotFoundException(\n                    \"No archived contract data found named: \" + dataName + \" of process instance: \"\n                            + processInstanceId);\n        }\n        return contractData.getValue();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-contract-data/src/main/java/org/bonitasoft/engine/core/contract/data/SAContractData.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.contract.data;\n\nimport java.io.Serializable;\nimport java.util.Collection;\nimport java.util.Map;\n\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorColumn;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Inheritance;\nimport javax.persistence.InheritanceType;\nimport javax.persistence.Table;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.bpm.contract.FileInputValue;\nimport org.bonitasoft.engine.persistence.ArchivedPersistentObject;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.hibernate.annotations.Type;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode\n@Entity\n@Inheritance(strategy = InheritanceType.SINGLE_TABLE)\n@DiscriminatorColumn(name = \"kind\")\n@Table(name = \"arch_contract_data\")\npublic abstract class SAContractData implements ArchivedPersistentObject {\n\n    @Id\n    protected long id;\n    @Column\n    protected long archiveDate;\n    @Column\n    protected long sourceObjectId;\n    @Column\n    protected String name;\n    @Column(name = \"val\")\n    @Type(type = \"xml_blob\")\n    protected Serializable value;\n    @Column\n    protected long scopeId;\n\n    protected SAContractData(long sourceObjectId, String name, Serializable value, long scopeId) {\n        this.sourceObjectId = sourceObjectId;\n        this.name = name;\n        this.scopeId = scopeId;\n        this.value = clearFileInputContent(value);\n    }\n\n    /**\n     * Remove the {@link FileInputValue} content from Archived Contract Data\n     *\n     * @param value, The contract input value\n     * @return The contract input value without file content in case of a {@link FileInputValue}\n     */\n    private static Serializable clearFileInputContent(Serializable value) {\n        if (value instanceof FileInputValue inputValue) {\n            inputValue.setContent(null);\n        } else if (value instanceof Map<?, ?>) {\n            ((Map<?, ?>) value).values().stream()\n                    .filter(Serializable.class::isInstance)\n                    .map(Serializable.class::cast)\n                    .forEach(v -> clearFileInputContent(v));\n        } else if (value instanceof Collection<?>) {\n            ((Collection<?>) value).stream()\n                    .filter(Serializable.class::isInstance)\n                    .map(Serializable.class::cast)\n                    .forEach(v -> clearFileInputContent(v));\n        }\n        return value;\n    }\n\n    protected SAContractData(SContractData contractData) {\n        this(contractData.getId(), contractData.getName(), contractData.getValue(), contractData.getScopeId());\n    }\n\n    @Override\n    public Class<? extends PersistentObject> getPersistentObjectInterface() {\n        return SContractData.class;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-contract-data/src/main/java/org/bonitasoft/engine/core/contract/data/SAProcessContractData.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.contract.data;\n\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\n\n/**\n * author Emmanuel Duchastenier\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"PROCESS\")\npublic class SAProcessContractData extends SAContractData {\n\n    public SAProcessContractData(SProcessContractData processContract) {\n        super(processContract);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-contract-data/src/main/java/org/bonitasoft/engine/core/contract/data/SATaskContractData.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.contract.data;\n\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\n\n/**\n * @author Matthieu Chaffotte\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"TASK\")\npublic class SATaskContractData extends SAContractData {\n\n    public SATaskContractData(final SContractData contractData) {\n        super(contractData);\n    }\n\n    public SATaskContractData(final STaskContractData taskContractData) {\n        super(taskContractData);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-contract-data/src/main/java/org/bonitasoft/engine/core/contract/data/SContractData.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.contract.data;\n\nimport java.io.Serializable;\n\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorColumn;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Inheritance;\nimport javax.persistence.InheritanceType;\nimport javax.persistence.Table;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.hibernate.annotations.Type;\n\n/**\n * @author Matthieu Chaffotte\n */\n@Data\n@NoArgsConstructor\n@Entity\n@Inheritance(strategy = InheritanceType.SINGLE_TABLE)\n@DiscriminatorColumn(name = \"kind\")\n@Table(name = \"contract_data\")\n@SuperBuilder\npublic abstract class SContractData implements PersistentObject {\n\n    @Id\n    private long id;\n    @Column\n    private String name;\n    @Column(name = \"val\")\n    @Type(type = \"xml_blob\")\n    private Serializable value;\n    @Column\n    private long scopeId;\n\n    protected SContractData(final String name, final Serializable value, long scopeId) {\n        this.name = name;\n        this.scopeId = scopeId;\n        this.value = value;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-contract-data/src/main/java/org/bonitasoft/engine/core/contract/data/SContractDataCreationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.contract.data;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SContractDataCreationException extends SBonitaException {\n\n    private static final long serialVersionUID = -850750385519813019L;\n\n    public SContractDataCreationException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-contract-data/src/main/java/org/bonitasoft/engine/core/contract/data/SContractDataDeletionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.contract.data;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SContractDataDeletionException extends SBonitaException {\n\n    private static final long serialVersionUID = -6713711673811669252L;\n\n    public SContractDataDeletionException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-contract-data/src/main/java/org/bonitasoft/engine/core/contract/data/SContractDataLogBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.contract.data;\n\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SContractDataLogBuilder extends CRUDELogBuilder implements SPersistenceLogBuilder, HasCRUDEAction {\n\n    private static final int CONTRACT_DATA_INDEX = 0;\n\n    private String contractDataPrefix; // so that this object can be used for usertasks and process as well\n\n    public SContractDataLogBuilder(String contractDataPrefix) {\n        this.contractDataPrefix = contractDataPrefix;\n    }\n\n    @Override\n    public SPersistenceLogBuilder objectId(final long objectId) {\n        queriableLogBuilder.numericIndex(CONTRACT_DATA_INDEX, objectId);\n        return this;\n    }\n\n    @Override\n    protected String getActionTypePrefix() {\n        return contractDataPrefix;\n    }\n\n    @Override\n    protected void checkExtraRules(final SQueriableLog log) {\n        if (log.getActionStatus() != SQueriableLog.STATUS_FAIL && log.getNumericIndex(CONTRACT_DATA_INDEX) == 0L) {\n            throw new MissingMandatoryFieldsException(\"Some mandatory fields are missing: Contract data Id\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-contract-data/src/main/java/org/bonitasoft/engine/core/contract/data/SContractDataNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.contract.data;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SContractDataNotFoundException extends SBonitaException {\n\n    private static final long serialVersionUID = -6617607757355205604L;\n\n    public SContractDataNotFoundException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-contract-data/src/main/java/org/bonitasoft/engine/core/contract/data/SProcessContractData.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.contract.data;\n\nimport java.io.Serializable;\n\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * author Emmanuel Duchastenier\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"PROCESS\")\n@SuperBuilder\npublic class SProcessContractData extends SContractData {\n\n    public SProcessContractData(final long processInstanceId, final String name, final Serializable value) {\n        super(name, value, processInstanceId);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-contract-data/src/main/java/org/bonitasoft/engine/core/contract/data/STaskContractData.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.contract.data;\n\nimport java.io.Serializable;\n\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * @author Nicolas Tith\n * @author Emmanuel Duchastenier\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"TASK\")\n@SuperBuilder\npublic class STaskContractData extends SContractData {\n\n    public STaskContractData(final long userTaskId, final String name, final Serializable value) {\n        super(name, value, userTaskId);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-contract-data/src/main/resources/org/bonitasoft/engine/core/contract/data/model/impl/hibernate/contract.queries.hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\" \"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd\">\n<hibernate-mapping auto-import=\"false\">\n\n    <query name=\"getContractDataByUserTaskId\">\n        SELECT tcd\n        FROM org.bonitasoft.engine.core.contract.data.STaskContractData AS tcd\n        WHERE tcd.scopeId = :scopeId\n    </query>\n\n    <query name=\"getContractDataByUserTaskIdAndDataName\">\n        SELECT tcd\n        FROM org.bonitasoft.engine.core.contract.data.STaskContractData AS tcd\n        WHERE tcd.scopeId = :scopeId\n        AND name = :name\n    </query>\n\n    <query name=\"getArchivedContractDataByUserTaskIdAndDataName\">\n        SELECT atcd\n        FROM org.bonitasoft.engine.core.contract.data.SATaskContractData AS atcd\n        WHERE atcd.scopeId = :scopeId\n        AND atcd.name = :name\n    </query>\n\n    <query name=\"getContractDataByProcessInstanceId\">\n        SELECT p\n        FROM org.bonitasoft.engine.core.contract.data.SProcessContractData AS p\n        WHERE p.scopeId = :scopeId\n    </query>\n\n    <query name=\"getContractDataByProcessInstanceIdAndDataName\">\n        SELECT p\n        FROM org.bonitasoft.engine.core.contract.data.SProcessContractData AS p\n        WHERE p.scopeId = :scopeId\n        AND name = :name\n    </query>\n\n    <query name=\"getArchivedContractDataByProcessInstanceIdAndDataName\">\n        SELECT ap\n        FROM org.bonitasoft.engine.core.contract.data.SAProcessContractData AS ap\n        WHERE ap.scopeId = :scopeId\n        AND ap.name = :name\n    </query>\n\n    <query name=\"deleteArchivedProcessContractData\">\n        DELETE\n        FROM org.bonitasoft.engine.core.contract.data.SAProcessContractData AS c\n        WHERE c.scopeId IN (:scopeIds)\n    </query>\n    <query name=\"deleteArchivedTaskContractData\">\n        DELETE\n        FROM org.bonitasoft.engine.core.contract.data.SATaskContractData AS c\n        WHERE c.scopeId IN (:scopeIds)\n    </query>\n\n</hibernate-mapping>\n"
  },
  {
    "path": "bpm/bonita-core/bonita-contract-data/src/test/java/org/bonitasoft/engine/core/contract/data/ContractDataServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.contract.data;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.*;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.archive.ArchiveInsertRecord;\nimport org.bonitasoft.engine.archive.ArchiveService;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ContractDataServiceImplTest {\n\n    @Mock\n    private ReadPersistenceService persistenceService;\n\n    @Mock\n    private Recorder recorder;\n\n    @Mock\n    private EventService eventService;\n\n    @Mock\n    private QueriableLoggerService queriableLoggerService;\n\n    @Mock\n    private ArchiveService archiveService;\n\n    @InjectMocks\n    private ContractDataServiceImpl contractDataService;\n\n    @Before\n    public void setUp() {\n        when(archiveService.getDefinitiveArchiveReadPersistenceService()).thenReturn(persistenceService);\n        when(archiveService.isArchivable(SContractData.class)).thenReturn(true);\n    }\n\n    @Test\n    public void getUserTaskData_returns_the_stored_value() throws Exception {\n        final STaskContractData contractData = new STaskContractData(1983L, \"id\", 10L);\n        when(persistenceService.selectOne(any(SelectOneDescriptor.class))).thenReturn(contractData);\n\n        final Long id = (Long) contractDataService.getUserTaskDataValue(1983L, \"id\");\n\n        assertThat(id).isEqualTo(10L);\n    }\n\n    @Test(expected = SContractDataNotFoundException.class)\n    public void getUserTaskData_throws_an_exception_when_data_not_found() throws Exception {\n        when(persistenceService.selectOne(any(SelectOneDescriptor.class))).thenReturn(null);\n\n        contractDataService.getUserTaskDataValue(1983L, \"id\");\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getUserTaskData_throws_an_exception_with_a_read_exception() throws Exception {\n        when(persistenceService.selectOne(any(SelectOneDescriptor.class)))\n                .thenThrow(new SBonitaReadException(\"exception\"));\n\n        contractDataService.getUserTaskDataValue(1983L, \"id\");\n    }\n\n    @Test\n    public void addUserTaskData_store_values_for_the_user_task() throws Exception {\n        final STaskContractData contractData = new STaskContractData(1983L, \"id\", 54L);\n        contractData.setId(10L);\n        when(queriableLoggerService.isLoggable(anyString(), any(SQueriableLogSeverity.class))).thenReturn(false);\n\n        contractDataService.addUserTaskData(contractData);\n\n        verify(recorder).recordInsert(any(InsertRecord.class), nullable(String.class));\n    }\n\n    @Test\n    public void addUserTaskData_accept_null_inputs() throws Exception {\n        contractDataService.addUserTaskData(54L, null);\n\n        verify(recorder, times(0)).recordInsert(any(InsertRecord.class), nullable(String.class));\n    }\n\n    @Test(expected = SContractDataCreationException.class)\n    public void addUserTaskData_throws_an_exception_if_not_able_to_store_the_data() throws Exception {\n        final STaskContractData contractData = new STaskContractData(1983L, \"id\", 54L);\n        contractData.setId(10L);\n        when(queriableLoggerService.isLoggable(anyString(), any(SQueriableLogSeverity.class))).thenReturn(false);\n        doThrow(new SRecorderException(\"exception\")).when(recorder).recordInsert(any(InsertRecord.class),\n                nullable(String.class));\n\n        contractDataService.addUserTaskData(contractData);\n    }\n\n    @Test\n    public void deleteUserTaskData_deletes_data() throws Exception {\n        final List<STaskContractData> data = new ArrayList<STaskContractData>();\n        data.add(new STaskContractData(1983L, \"id\", 456478L));\n        data.add(new STaskContractData(1983L, \"id2\", 4564456478L));\n        doReturn(data).when(persistenceService).selectList(any());\n\n        contractDataService.deleteUserTaskData(1983L);\n\n        verify(recorder, times(2)).recordDelete(any(DeleteRecord.class), nullable(String.class));\n    }\n\n    @Test(expected = SContractDataDeletionException.class)\n    public void deleteUserTaskData_throws_exception() throws Exception {\n        final List<STaskContractData> data = new ArrayList<STaskContractData>();\n        data.add(new STaskContractData(1983L, \"id\", 456478L));\n        data.add(new STaskContractData(1983L, \"id2\", 4564456478L));\n        doReturn(data).when(persistenceService).selectList(any());\n        doThrow(new SRecorderException(\"exception\")).when(recorder).recordDelete(any(DeleteRecord.class),\n                nullable(String.class));\n\n        contractDataService.deleteUserTaskData(1983L);\n    }\n\n    @Test(expected = SContractDataDeletionException.class)\n    public void deleteUserTaskData_throws_exception_when_retrieving_data() throws Exception {\n        when(persistenceService.selectList(any(SelectListDescriptor.class)))\n                .thenThrow(new SBonitaReadException(\"exception\"));\n\n        contractDataService.deleteUserTaskData(1983L);\n    }\n\n    @Test\n    public void archiveUserTaskData_should_create_archive_data() throws Exception {\n        final long usertTaskId = 1983L;\n        final long time = 1010101010101001L;\n        final List<STaskContractData> data = new ArrayList<STaskContractData>();\n        final STaskContractData scd1 = new STaskContractData(usertTaskId, \"id\", 456478L);\n        final STaskContractData scd2 = new STaskContractData(usertTaskId, \"id2\", 4564456478L);\n        data.add(scd1);\n        data.add(scd2);\n        final ArchiveInsertRecord[] archivedata = new ArchiveInsertRecord[2];\n        archivedata[0] = new ArchiveInsertRecord(new SATaskContractData(scd1));\n        archivedata[1] = new ArchiveInsertRecord(new SATaskContractData(scd2));\n        doReturn(data).when(persistenceService).selectList(any());\n\n        contractDataService.archiveAndDeleteUserTaskData(usertTaskId, time);\n\n        verify(archiveService).recordInserts(time, archivedata);\n    }\n\n    @Test\n    public void archiveContractData_should_not_create_archive_data_when_archivingContractData_is_disabled()\n            throws Exception {\n        final long usertTaskId = 1983L;\n        final long time = 1010101010101001L;\n        final List<STaskContractData> data = new ArrayList<STaskContractData>();\n        final STaskContractData scd1 = new STaskContractData(usertTaskId, \"id\", 456478L);\n        final STaskContractData scd2 = new STaskContractData(usertTaskId, \"id2\", 4564456478L);\n        data.add(scd1);\n        data.add(scd2);\n        final ArchiveInsertRecord[] archivedata = new ArchiveInsertRecord[2];\n        archivedata[0] = new ArchiveInsertRecord(new SATaskContractData(scd1));\n        archivedata[1] = new ArchiveInsertRecord(new SATaskContractData(scd2));\n        when(archiveService.isArchivable(SContractData.class)).thenReturn(false);\n        doReturn(data).when(persistenceService).selectList(any());\n\n        contractDataService.archiveAndDeleteUserTaskData(usertTaskId, time);\n\n        verify(archiveService, never()).recordInserts(time, archivedata);\n    }\n\n    @Test\n    public void archiveUserTaskData_should_not_create_archive_data_if_no_data_defined() throws Exception {\n        final long usertTaskId = 1983L;\n        final long time = 1010101010101001L;\n        final List<STaskContractData> data = new ArrayList<STaskContractData>();\n        doReturn(data).when(persistenceService).selectList(any());\n\n        contractDataService.archiveAndDeleteUserTaskData(usertTaskId, time);\n\n        verifyNoInteractions(archiveService);\n    }\n\n    @Test(expected = SObjectModificationException.class)\n    public void archiveUserTaskData_should_throw_exception_when_an_exception_occurs_when_getting_data()\n            throws Exception {\n        final long usertTaskId = 1983L;\n        final long time = 1010101010101001L;\n        when(persistenceService.selectList(any(SelectListDescriptor.class)))\n                .thenThrow(new SBonitaReadException(\"exception\"));\n\n        contractDataService.archiveAndDeleteUserTaskData(usertTaskId, time);\n    }\n\n    @Test\n    public void getArchivedUserTaskData_returns_the_stored_value() throws Exception {\n        final STaskContractData contractData = new STaskContractData(1983L, \"id\", 10L);\n        final SATaskContractData saTaskContractData = new SATaskContractData(contractData);\n        saTaskContractData.setArchiveDate(768468743687L);\n        when(persistenceService.selectOne(any(SelectOneDescriptor.class))).thenReturn(saTaskContractData);\n\n        final Long id = (Long) contractDataService.getArchivedUserTaskDataValue(1983L, \"id\");\n\n        assertThat(id).isEqualTo(10L);\n    }\n\n    @Test(expected = SContractDataNotFoundException.class)\n    public void getArchivedUserTaskData_throws_an_exception_when_data_not_found() throws Exception {\n        when(persistenceService.selectOne(any(SelectOneDescriptor.class))).thenReturn(null);\n\n        contractDataService.getArchivedUserTaskDataValue(1983L, \"id\");\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getArchivedUserTaskData_throws_an_exception_with_a_read_exception() throws Exception {\n        when(persistenceService.selectOne(any(SelectOneDescriptor.class)))\n                .thenThrow(new SBonitaReadException(\"exception\"));\n\n        contractDataService.getArchivedUserTaskDataValue(1983L, \"id\");\n    }\n\n    /************* Process Data tests *************/\n\n    @Test\n    public void addProcessDataStoreValuesForTheProcessInstance() throws Exception {\n        final SProcessContractData contractData = new SProcessContractData(1983L, \"id\", 54L);\n        contractData.setId(10L);\n        when(queriableLoggerService.isLoggable(anyString(), any(SQueriableLogSeverity.class))).thenReturn(false);\n\n        contractDataService.addProcessData(contractData);\n\n        verify(recorder).recordInsert(any(InsertRecord.class), nullable(String.class));\n    }\n\n    @Test\n    public void addProcessData_accept_null_inputs() throws Exception {\n        contractDataService.addProcessData(54L, null);\n\n        verify(recorder, times(0)).recordInsert(any(InsertRecord.class), nullable(String.class));\n    }\n\n    @Test(expected = SContractDataCreationException.class)\n    public void addProcessData_throws_an_exception_if_not_able_to_store_the_data() throws Exception {\n        final SProcessContractData contractData = new SProcessContractData(1983L, \"id\", 54L);\n        contractData.setId(10L);\n        when(queriableLoggerService.isLoggable(anyString(), any(SQueriableLogSeverity.class))).thenReturn(false);\n        doThrow(new SRecorderException(\"exception\")).when(recorder).recordInsert(any(InsertRecord.class),\n                nullable(String.class));\n\n        contractDataService.addProcessData(contractData);\n    }\n\n    @Test\n    public void getProcessDataReturnsTheStoredValue() throws Exception {\n        long processInstanceId = 1117L;\n        final SProcessContractData contractData = new SProcessContractData(processInstanceId, \"TheName\", \"TheValue\");\n        when(persistenceService.selectOne(any(SelectOneDescriptor.class))).thenReturn(contractData);\n\n        final String dataValue = (String) contractDataService.getProcessDataValue(processInstanceId, \"TheName\");\n\n        assertThat(dataValue).isEqualTo(\"TheValue\");\n    }\n\n    @Test(expected = SContractDataNotFoundException.class)\n    public void getProcessDataShouldThrowContractDataNotFoundWhenNoDataIsReturned() throws Exception {\n        when(persistenceService.selectOne(any(SelectOneDescriptor.class))).thenReturn(null);\n\n        contractDataService.getProcessDataValue(147L, null);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getProcessData_throws_an_exception_with_a_read_exception() throws Exception {\n        when(persistenceService.selectOne(any(SelectOneDescriptor.class)))\n                .thenThrow(new SBonitaReadException(\"exception\"));\n\n        contractDataService.getProcessDataValue(1983L, \"id\");\n    }\n\n    @Test\n    public void deleteProcessData_deletes_data() throws Exception {\n        final List<SProcessContractData> data = new ArrayList<SProcessContractData>();\n        data.add(new SProcessContractData(1983L, \"id\", 456478L));\n        data.add(new SProcessContractData(1983L, \"id2\", 4564456478L));\n        when(persistenceService.selectList(any(SelectListDescriptor.class))).thenReturn(data);\n\n        contractDataService.deleteProcessData(1983L);\n\n        verify(recorder, times(2)).recordDelete(any(DeleteRecord.class), nullable(String.class));\n    }\n\n    @Test(expected = SContractDataDeletionException.class)\n    public void deleteProcessData_throws_exception() throws Exception {\n        final List<SProcessContractData> data = new ArrayList<SProcessContractData>();\n        data.add(new SProcessContractData(1983L, \"id\", 456478L));\n        data.add(new SProcessContractData(1983L, \"id2\", 4564456478L));\n        when(persistenceService.selectList(any(SelectListDescriptor.class))).thenReturn(data);\n        doThrow(new SRecorderException(\"exception\")).when(recorder).recordDelete(any(DeleteRecord.class),\n                nullable(String.class));\n\n        contractDataService.deleteProcessData(1983L);\n    }\n\n    @Test(expected = SContractDataDeletionException.class)\n    public void deleteProcessData_throws_exception_when_retrieving_data() throws Exception {\n        when(persistenceService.selectList(any(SelectListDescriptor.class)))\n                .thenThrow(new SBonitaReadException(\"exception\"));\n\n        contractDataService.deleteProcessData(1983L);\n    }\n\n    @Test\n    public void archiveProcessData_should_create_archive_data() throws Exception {\n        final long processInstanceId = 1983L;\n        final long time = 1010101010101001L;\n        final List<SProcessContractData> data = new ArrayList<SProcessContractData>();\n        final SProcessContractData scd1 = new SProcessContractData(processInstanceId, \"id\", 456478L);\n        final SProcessContractData scd2 = new SProcessContractData(processInstanceId, \"id2\", 4564456478L);\n        data.add(scd1);\n        data.add(scd2);\n        final ArchiveInsertRecord[] archivedata = new ArchiveInsertRecord[2];\n        archivedata[0] = new ArchiveInsertRecord(new SAProcessContractData(scd1));\n        archivedata[1] = new ArchiveInsertRecord(new SAProcessContractData(scd2));\n        when(persistenceService.selectList(any(SelectListDescriptor.class))).thenReturn(data);\n\n        contractDataService.archiveAndDeleteProcessData(processInstanceId, time);\n\n        verify(archiveService).recordInserts(time, archivedata);\n    }\n\n    @Test\n    public void archiveProcessData_should_not_create_archive_data_if_no_data_defined() throws Exception {\n        final long usertProcessId = 1983L;\n        final long time = 1010101010101001L;\n        final List<SProcessContractData> data = new ArrayList<SProcessContractData>();\n        when(persistenceService.selectList(any(SelectListDescriptor.class))).thenReturn(data);\n\n        contractDataService.archiveAndDeleteProcessData(usertProcessId, time);\n\n        verifyNoInteractions(archiveService);\n    }\n\n    @Test(expected = SObjectModificationException.class)\n    public void archiveProcessData_should_throw_exception_when_an_exception_occurs_when_getting_data()\n            throws Exception {\n        final long usertProcessId = 1983L;\n        final long time = 1010101010101001L;\n        when(persistenceService.selectList(any(SelectListDescriptor.class)))\n                .thenThrow(new SBonitaReadException(\"exception\"));\n\n        contractDataService.archiveAndDeleteProcessData(usertProcessId, time);\n    }\n\n    @Test\n    public void getArchivedProcessData_returns_the_stored_value() throws Exception {\n        final SProcessContractData contractData = new SProcessContractData(1983L, \"id\", 10L);\n        final SAProcessContractData saProcessContractData = new SAProcessContractData(contractData);\n        saProcessContractData.setArchiveDate(768468743687L);\n        when(persistenceService.selectOne(any(SelectOneDescriptor.class))).thenReturn(saProcessContractData);\n\n        final Long id = (Long) contractDataService.getArchivedProcessDataValue(1983L, \"id\");\n\n        assertThat(id).isEqualTo(10L);\n    }\n\n    @Test(expected = SContractDataNotFoundException.class)\n    public void getArchivedProcessData_throws_an_exception_when_data_not_found() throws Exception {\n        when(persistenceService.selectOne(any(SelectOneDescriptor.class))).thenReturn(null);\n\n        contractDataService.getArchivedProcessDataValue(1983L, \"id\");\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getArchivedProcessData_throws_an_exception_with_a_read_exception() throws Exception {\n        when(persistenceService.selectOne(any(SelectOneDescriptor.class)))\n                .thenThrow(new SBonitaReadException(\"exception\"));\n\n        contractDataService.getArchivedProcessDataValue(1983L, \"id\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-contract-data/src/test/java/org/bonitasoft/engine/core/contract/data/SAProcessContractDataTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.contract.data;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.io.Serializable;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.contract.FileInputValue;\nimport org.junit.jupiter.api.Test;\n\nclass SAProcessContractDataTest {\n\n    @Test\n    void clearFileInputContent() {\n        var contractData = new SProcessContractData();\n        contractData.setId(1);\n        contractData.setName(\"myFile\");\n        contractData.setValue(new FileInputValue(\"theFile\", \"content\".getBytes()));\n\n        var archivedContractData = new SAProcessContractData(contractData);\n\n        assertThat(archivedContractData.getValue()).isInstanceOf(FileInputValue.class)\n                .extracting(\"content\")\n                .isNull();\n    }\n\n    @Test\n    void clearMultipleFileInputContent() {\n        var contractData = new SProcessContractData();\n        contractData.setId(1);\n        contractData.setName(\"myFile\");\n        contractData.setValue((Serializable) List.of(\n                new FileInputValue(\"theFile\", \"content\".getBytes()),\n                new FileInputValue(\"theFile1\", \"content1\".getBytes())));\n\n        var archivedContractData = new SAProcessContractData(contractData);\n\n        assertThat(archivedContractData.getValue()).isInstanceOf(Collection.class);\n        assertThat((Collection<?>) archivedContractData.getValue())\n                .extracting(\"content\")\n                .containsNull();\n    }\n\n    @Test\n    void clearComplexInputWithFileInputContent() {\n        var contractData = new SProcessContractData();\n        contractData.setId(1);\n        contractData.setName(\"parent\");\n        contractData.setValue(\n                (Serializable) Map.ofEntries(Map.entry(\"myFile\", new FileInputValue(\"theFile\", \"content\".getBytes()))));\n\n        var archivedContractData = new SAProcessContractData(contractData);\n\n        assertThat(archivedContractData.getValue()).isInstanceOf(Map.class);\n        assertThat((Map) archivedContractData.getValue())\n                .hasEntrySatisfying(\"myFile\", value -> assertThat(value).isInstanceOf(FileInputValue.class)\n                        .extracting(\"content\")\n                        .isNull());\n    }\n\n    @Test\n    void creatingSAProcessContractDataShouldCopyNonArchivedValues() {\n        long processInstanceId = 555L;\n        String someName = \"some_name\";\n        long value = 999999L;\n        final SProcessContractData processContractData = new SProcessContractData(processInstanceId, someName, value);\n        long originalProcessDataId = 7548463269L;\n        processContractData.setId(originalProcessDataId);\n\n        final SAProcessContractData saProcessContractData = new SAProcessContractData(processContractData);\n\n        assertThat(saProcessContractData.getId()).isZero();\n        assertThat(saProcessContractData.getName()).isEqualTo(someName);\n        assertThat(saProcessContractData.getScopeId()).isEqualTo(processInstanceId);\n        assertThat(saProcessContractData.getArchiveDate()).isZero();\n        assertThat(saProcessContractData.getSourceObjectId()).isEqualTo(originalProcessDataId);\n        assertThat(saProcessContractData.getValue()).isEqualTo(value);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-contract-data/src/test/java/org/bonitasoft/engine/core/contract/data/SATaskContractDataTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.contract.data;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.Test;\n\npublic class SATaskContractDataTest {\n\n    @Test\n    public void create_should_copy_values() {\n        long userTaskId = 14444L;\n        final STaskContractData taskContractData = new STaskContractData(userTaskId, \"id\", 15124245748545L);\n        taskContractData.setId(7548463269L);\n\n        final SATaskContractData saTaskContractData = new SATaskContractData(taskContractData);\n\n        assertThat(saTaskContractData.getId()).isZero();\n        assertThat(saTaskContractData.getName()).isEqualTo(\"id\");\n        assertThat(saTaskContractData.getScopeId()).isEqualTo(userTaskId);\n        assertThat(saTaskContractData.getArchiveDate()).isZero();\n        assertThat(saTaskContractData.getSourceObjectId()).isEqualTo(7548463269L);\n        assertThat(saTaskContractData.getValue()).isEqualTo(15124245748545L);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-core-data/build.gradle",
    "content": "dependencies {\n    api project(':bpm:bonita-core:bonita-process-instance')\n    api project(':services:bonita-data-instance')\n    api project(':services:bonita-expression')\n    api project(':services:bonita-commons')\n    api libs.commonsLang\n    api libs.commonsText\n    api project(':services:bonita-cache')\n    api project(':services:bonita-persistence')\n    testImplementation libs.assertj\n    testImplementation libs.mockitoCore\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-core-data/src/main/java/org/bonitasoft/engine/core/data/instance/TransientDataService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.data.instance;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.data.instance.exception.SDataInstanceException;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\npublic interface TransientDataService {\n\n    /**\n     * @param dataNames\n     * @param containerId\n     * @param containerType\n     * @return\n     * @throws SDataInstanceException\n     */\n    List<SDataInstance> getDataInstances(List<String> dataNames, long containerId, String containerType)\n            throws SDataInstanceException;\n\n    /**\n     * @param dataInstance\n     * @throws SDataInstanceException\n     */\n    void createDataInstance(SDataInstance dataInstance) throws SDataInstanceException;\n\n    /**\n     * @param dataInstance\n     * @param descriptor\n     * @throws SDataInstanceException\n     */\n    void updateDataInstance(SDataInstance dataInstance, EntityUpdateDescriptor descriptor)\n            throws SDataInstanceException;\n\n    /**\n     * @param dataInstance\n     * @throws SDataInstanceException\n     */\n    void deleteDataInstance(SDataInstance dataInstance) throws SDataInstanceException;\n\n    /**\n     * @param dataInstanceId\n     * @return\n     * @throws SDataInstanceException\n     */\n    SDataInstance getDataInstance(long dataInstanceId) throws SDataInstanceException;\n\n    /**\n     * @param dataName\n     * @param containerId\n     * @param containerType\n     * @return\n     * @throws SDataInstanceException\n     */\n    SDataInstance getDataInstance(String dataName, long containerId, String containerType)\n            throws SDataInstanceException;\n\n    /**\n     * @param containerId\n     * @param containerType\n     * @param fromIndex\n     * @param numberOfResults\n     * @return\n     * @throws SDataInstanceException\n     */\n    List<SDataInstance> getDataInstances(long containerId, String containerType, int fromIndex, int numberOfResults)\n            throws SDataInstanceException;\n\n    /**\n     * @param dataInstanceIds\n     * @return\n     */\n    List<SDataInstance> getDataInstances(List<Long> dataInstanceIds);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-core-data/src/main/java/org/bonitasoft/engine/core/data/instance/impl/TransientDataExpressionExecutorStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.data.instance.impl;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport org.bonitasoft.engine.core.data.instance.TransientDataService;\nimport org.bonitasoft.engine.data.instance.exception.SDataInstanceException;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\nimport org.bonitasoft.engine.expression.ContainerState;\nimport org.bonitasoft.engine.expression.NonEmptyContentExpressionExecutorStrategy;\nimport org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.model.ExpressionKind;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Baptiste Mesta\n */\npublic class TransientDataExpressionExecutorStrategy extends NonEmptyContentExpressionExecutorStrategy {\n\n    private final TransientDataService transientDataService;\n\n    public TransientDataExpressionExecutorStrategy(final TransientDataService transientDataService) {\n        this.transientDataService = transientDataService;\n    }\n\n    @Override\n    public Object evaluate(final SExpression expression, final Map<String, Object> dependencyValues,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState)\n            throws SExpressionEvaluationException, SExpressionDependencyMissingException {\n        return evaluate(Arrays.asList(expression), dependencyValues, resolvedExpressions, containerState).get(0);\n    }\n\n    @Override\n    public ExpressionKind getExpressionKind() {\n        return KIND_TRANSIENT_VARIABLE;\n    }\n\n    @Override\n    public List<Object> evaluate(final List<SExpression> expressions, final Map<String, Object> dependencyValues,\n            final Map<Integer, Object> resolvedExpressions, final ContainerState containerState)\n            throws SExpressionEvaluationException,\n            SExpressionDependencyMissingException {\n        long containerId;\n        String containerType;\n        final int maxExpressionSize = expressions.size();\n        final ArrayList<String> dataNames = new ArrayList<String>(maxExpressionSize);\n        final HashMap<String, Serializable> results = new HashMap<String, Serializable>(maxExpressionSize);\n        for (final SExpression sExpression : expressions) {\n            final String dataName = sExpression.getContent();\n            if (dependencyValues.containsKey(dataName)) {\n                final Serializable value = (Serializable) dependencyValues.get(dataName);\n                results.put(dataName, value);\n            } else {\n                dataNames.add(dataName);\n            }\n        }\n\n        if (dataNames.isEmpty()) {\n            return buildExpressionResultSameOrderAsInputList(expressions, results);\n        }\n        if (dependencyValues != null && dependencyValues.containsKey(CONTAINER_ID_KEY)\n                && dependencyValues.containsKey(CONTAINER_TYPE_KEY)) {\n            String currentData = null;\n            try {\n                containerId = (Long) dependencyValues.get(CONTAINER_ID_KEY);\n                containerType = (String) dependencyValues.get(CONTAINER_TYPE_KEY);\n                for (final String name : dataNames) {\n                    currentData = name;\n                    SDataInstance dataInstance = transientDataService.getDataInstance(name, containerId, containerType);\n                    results.put(dataInstance.getName(), dataInstance.getValue());\n                }\n                return buildExpressionResultSameOrderAsInputList(expressions, results);\n            } catch (final SDataInstanceException e) {\n                throw new SExpressionEvaluationException(\"Can't read transient data\", e, currentData);\n            }\n        }\n        throw new SExpressionDependencyMissingException(\n                \"The context to evaluate the data '\" + dataNames + \"' was not set\");\n    }\n\n    private List<Object> buildExpressionResultSameOrderAsInputList(final List<SExpression> expressions,\n            final Map<String, Serializable> results) {\n        return expressions.stream()\n                .map(exp -> results.get(exp.getContent()))\n                .collect(Collectors.toList());\n    }\n\n    @Override\n    public boolean mustPutEvaluatedExpressionInContext() {\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-core-data/src/main/java/org/bonitasoft/engine/core/data/instance/impl/TransientDataServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.data.instance.impl;\n\nimport static java.util.stream.Collectors.collectingAndThen;\nimport static java.util.stream.Collectors.toList;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.UUID;\nimport java.util.stream.Collectors;\n\nimport org.apache.commons.text.WordUtils;\nimport org.bonitasoft.engine.cache.CacheService;\nimport org.bonitasoft.engine.cache.SCacheException;\nimport org.bonitasoft.engine.commons.ClassReflector;\nimport org.bonitasoft.engine.commons.exceptions.SReflectException;\nimport org.bonitasoft.engine.core.data.instance.TransientDataService;\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.core.process.definition.model.SActivityDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.data.instance.exception.SDataInstanceException;\nimport org.bonitasoft.engine.data.instance.exception.SDataInstanceNotFoundException;\nimport org.bonitasoft.engine.data.instance.exception.SDataInstanceReadException;\nimport org.bonitasoft.engine.data.instance.exception.SUpdateDataInstanceException;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SDataInstanceBuilder;\nimport org.bonitasoft.engine.data.instance.model.exceptions.SDataInstanceNotWellFormedException;\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\nimport org.bonitasoft.engine.expression.exception.SExpressionException;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Baptiste Mesta\n */\n\npublic class TransientDataServiceImpl implements TransientDataService {\n\n    private static final Logger log = LoggerFactory.getLogger(TransientDataServiceImpl.class);\n    static final String TRANSIENT_DATA_CACHE_NAME = \"transient_data\";\n\n    private final CacheService cacheService;\n    private final ExpressionResolverService expressionResolverService;\n\n    private final FlowNodeInstanceService flowNodeInstanceService;\n    private final ProcessDefinitionService processDefinitionService;\n\n    public TransientDataServiceImpl(final CacheService cacheService,\n            ExpressionResolverService expressionResolverService,\n            FlowNodeInstanceService flowNodeInstanceService,\n            ProcessDefinitionService processDefinitionService) {\n        this.cacheService = cacheService;\n        this.expressionResolverService = expressionResolverService;\n        this.flowNodeInstanceService = flowNodeInstanceService;\n        this.processDefinitionService = processDefinitionService;\n    }\n\n    @Override\n    public List<SDataInstance> getDataInstances(final List<String> dataNames, final long containerId,\n            final String containerType) throws SDataInstanceException {\n        final ArrayList<SDataInstance> data = new ArrayList<>(dataNames.size());\n        for (final String dataName : dataNames) {\n            data.add(getDataInstance(dataName, containerId, containerType));\n        }\n        return data;\n    }\n\n    private static String getKey(final String dataInstanceName, final long containerId, final String containerType) {\n        return dataInstanceName + \":\" + containerId + \":\" + containerType;\n    }\n\n    static String getKey(final SDataInstance dataInstance) {\n        return getKey(dataInstance.getName(), dataInstance.getContainerId(), dataInstance.getContainerType());\n    }\n\n    @Override\n    public void createDataInstance(final SDataInstance dataInstance) throws SDataInstanceException {\n        try {\n            final String dataInstanceKey = getKey(dataInstance);\n            setId(dataInstance);\n            cacheService.store(TRANSIENT_DATA_CACHE_NAME, dataInstanceKey, dataInstance);\n        } catch (final Exception e) {\n            throw new SDataInstanceException(\"Impossible to store transient data\", e);\n        }\n    }\n\n    private void setId(final SDataInstance dataInstance)\n            throws SecurityException, IllegalArgumentException, SReflectException {\n        // FIXME: probably the id will be used, so not necessary to be set\n        final long id = Math.abs(UUID.randomUUID().getMostSignificantBits());\n        ClassReflector.invokeSetter(dataInstance, \"setId\", long.class, id);\n    }\n\n    @Override\n    public void updateDataInstance(final SDataInstance dataInstance, final EntityUpdateDescriptor descriptor)\n            throws SDataInstanceException {\n        try {\n            final String key = getKey(dataInstance);\n\n            for (final Map.Entry<String, Object> field : descriptor.getFields().entrySet()) {\n                try {\n                    final String setterName = \"set\" + WordUtils.capitalize(field.getKey());\n                    ClassReflector.invokeMethodByName(dataInstance, setterName, field.getValue());\n                } catch (final Exception e) {\n                    throw new SUpdateDataInstanceException(\"Problem while updating entity: \" + dataInstance\n                            + \" with id: \" + dataInstance.getId() + \" in TransientDataInstanceDataSource.\", e);\n                }\n            }\n            cacheService.store(TRANSIENT_DATA_CACHE_NAME, key, dataInstance);\n        } catch (final SCacheException e) {\n            throw new SDataInstanceException(\"Impossible to update transient data\", e);\n        }\n    }\n\n    @Override\n    public void deleteDataInstance(final SDataInstance dataInstance) throws SDataInstanceException {\n        try {\n            final String key = getKey(dataInstance);\n            cacheService.remove(TRANSIENT_DATA_CACHE_NAME, key);\n        } catch (final SCacheException e) {\n            throw new SDataInstanceException(\"Impossible to delete transient data\", e);\n        }\n    }\n\n    @Override\n    public SDataInstance getDataInstance(final long dataInstanceId) throws SDataInstanceException {\n        try {\n            final List<?> cacheKeys = getCacheKeys(TRANSIENT_DATA_CACHE_NAME);\n            for (final Object key : cacheKeys) {\n                final SDataInstance dataInstance = (SDataInstance) cacheService.get(TRANSIENT_DATA_CACHE_NAME, key);\n                if (dataInstance != null && dataInstance.getId() == dataInstanceId) {\n                    return dataInstance;\n                }\n            }\n        } catch (final SCacheException e) {\n            throw new SDataInstanceException(\"Impossible to get transient data: \", e);\n        }\n        throw new SDataInstanceNotFoundException(\"No data found. Id: \" + dataInstanceId);\n    }\n\n    @Override\n    public SDataInstance getDataInstance(final String dataName, final long containerId, final String containerType)\n            throws SDataInstanceException {\n        try {\n            final List<?> cacheKeys = getCacheKeys(TRANSIENT_DATA_CACHE_NAME);\n            final String key = getKey(dataName, containerId, containerType);\n\n            if (!cacheKeys.contains(key)) {\n                reevaluateTransientData(dataName, containerId, containerType);\n            }\n\n            return (SDataInstance) cacheService.get(TRANSIENT_DATA_CACHE_NAME, key);\n        } catch (final SCacheException | SProcessDefinitionNotFoundException | SBonitaReadException\n                | SFlowNodeNotFoundException | SFlowNodeReadException | SExpressionException e) {\n            throw new SDataInstanceException(\"Impossible to get transient data: \", e);\n        }\n    }\n\n    private List<?> getCacheKeys(final String cacheName) throws SCacheException {\n        List<?> cacheKeys = Collections.emptyList();\n        if (cacheService.getCachesNames().contains(cacheName)) {\n            cacheKeys = cacheService.getKeys(cacheName);\n        }\n        return cacheKeys;\n    }\n\n    private void reevaluateTransientData(final String name, final long containerId, final String containerType)\n            throws SProcessDefinitionNotFoundException, SBonitaReadException, SFlowNodeNotFoundException,\n            SFlowNodeReadException, SDataInstanceException, SExpressionException {\n\n        SFlowNodeInstance flowNodeInstance = flowNodeInstanceService.getFlowNodeInstance(containerId);\n        final long flowNodeDefinitionId = flowNodeInstance.getFlowNodeDefinitionId();\n        final long processDefinitionId = flowNodeInstance.getProcessDefinitionId();\n        final SProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(processDefinitionId);\n        final SActivityDefinition flowNode = (SActivityDefinition) processDefinition.getProcessContainer()\n                .getFlowNode(flowNodeDefinitionId);\n        SDataDefinition dataDefinition = getTransientData(containerId).stream()\n                .filter(data -> Objects.equals(name, data.getName()))\n                .findFirst()\n                .orElseThrow(() -> new SDataInstanceNotFoundException(\n                        \"Transient data was not found and we were unable to reevaluate it because it was not found in the definition, name=<\"\n                                + name + \"> process definition=<\" + processDefinition.getName() + \",\"\n                                + processDefinition.getVersion() + \"> flow node=<\" + flowNode.getName() + \">\"));\n        createDataInstance(dataDefinition, containerId, DataInstanceContainer.ACTIVITY_INSTANCE,\n                new SExpressionContext(containerId, containerType, processDefinitionId));\n    }\n\n    private List<SDataDefinition> getTransientData(long containerId) throws SFlowNodeNotFoundException,\n            SFlowNodeReadException, SProcessDefinitionNotFoundException, SBonitaReadException {\n        SFlowNodeInstance flowNodeInstance = flowNodeInstanceService.getFlowNodeInstance(containerId);\n        final long flowNodeDefinitionId = flowNodeInstance.getFlowNodeDefinitionId();\n        final long processDefinitionId = flowNodeInstance.getProcessDefinitionId();\n        final SProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(processDefinitionId);\n        final SActivityDefinition flowNode = (SActivityDefinition) processDefinition.getProcessContainer()\n                .getFlowNode(flowNodeDefinitionId);\n        return flowNode != null ? flowNode.getSDataDefinitions()\n                .stream()\n                .filter(SDataDefinition::isTransientData)\n                .collect(Collectors.toList()) : Collections.emptyList();\n    }\n\n    private void createDataInstance(SDataDefinition dataDefinition, final long containerId,\n            final DataInstanceContainer containerType, final SExpressionContext expressionContext)\n            throws SDataInstanceException, SExpressionException {\n        Serializable dataValue = null;\n        final SExpression defaultValueExpression = dataDefinition.getDefaultValueExpression();\n        if (defaultValueExpression != null) {\n            log.warn(String.format(\n                    \"The value of the transient data %s of %s %s is reevaluated from its default value expression.\",\n                    dataDefinition.getName(), containerId, containerType));\n\n            dataValue = (Serializable) expressionResolverService.evaluate(dataDefinition.getDefaultValueExpression(),\n                    expressionContext);\n        } else {\n            log.warn(\"Creating a transient data instance with a null expression is not a good practice.\");\n        }\n\n        try {\n            createDataInstance(SDataInstanceBuilder.createNewInstance(dataDefinition, containerId, containerType.name(),\n                    dataValue));\n        } catch (final SDataInstanceNotWellFormedException e) {\n            throw new SDataInstanceReadException(e);\n        }\n    }\n\n    @Override\n    public List<SDataInstance> getDataInstances(final long containerId, final String containerType, final int fromIndex,\n            final int numberOfResults) throws SDataInstanceException {\n        try {\n            return getTransientData(containerId).stream()\n                    .skip(fromIndex * numberOfResults)\n                    .limit(numberOfResults)\n                    .map(data -> {\n                        try {\n                            return getDataInstance(data.getName(), containerId, containerType);\n                        } catch (SDataInstanceException e) {\n                            throw new BonitaRuntimeException(\n                                    String.format(\"Transient data '%s' not found for container %s with type %s\",\n                                            data.getName(), containerId, containerType),\n                                    e);\n                        }\n                    })\n                    .collect(collectingAndThen(toList(), Collections::unmodifiableList));\n        } catch (SProcessDefinitionNotFoundException | SFlowNodeNotFoundException | SFlowNodeReadException\n                | SBonitaReadException e) {\n            throw new SDataInstanceException(\n                    String.format(\"An error occurred while retrieving transient data for container %s with type %s\",\n                            containerId, containerType),\n                    e);\n        }\n    }\n\n    @Override\n    public List<SDataInstance> getDataInstances(final List<Long> dataInstanceIds) {\n        final List<SDataInstance> results = new ArrayList<>(dataInstanceIds.size());\n        for (final Long dataInstanceId : dataInstanceIds) {\n            try {\n                results.add(getDataInstance(dataInstanceId));\n            } catch (final SDataInstanceException e) {\n            }\n        }\n        return results;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-core-data/src/test/java/org/bonitasoft/engine/core/data/instance/impl/TransientDataServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.data.instance.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.ArgumentMatchers.notNull;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.cache.CacheService;\nimport org.bonitasoft.engine.cache.SCacheException;\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SActivityDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SProcessDefinitionImpl;\nimport org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\nimport org.bonitasoft.engine.data.instance.exception.SDataInstanceException;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SShortTextDataInstance;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class TransientDataServiceImplTest {\n\n    @Mock\n    private CacheService cacheService;\n\n    @Mock\n    private ExpressionResolverService expressionResolverService;\n\n    @Mock\n    private FlowNodeInstanceService flowNodeInstanceService;\n\n    @Mock\n    private ProcessDefinitionService processDefinitionService;\n\n    @InjectMocks\n    @Spy\n    private TransientDataServiceImpl transientDataServiceImpl;\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Before\n    public void before() {\n        when(cacheService.getCachesNames()).thenReturn(Arrays.asList(\"transient_data\"));\n    }\n\n    @Test\n    public void should_createDataInstance_add_in_cache() throws Exception {\n        // given\n        SShortTextDataInstance data = createData(12, 42, \"name\", \"containerType\");\n\n        // when\n        transientDataServiceImpl.createDataInstance(data);\n\n        // then\n        assertThat(data.getId()).isGreaterThan(0);\n        verify(cacheService, times(1)).store(\"transient_data\", \"name:42:containerType\", data);\n    }\n\n    private SShortTextDataInstance createData(final long id, final int containerId, final String name,\n            final String containerType) throws SCacheException {\n        SShortTextDataInstance data = new SShortTextDataInstance();\n        data.setId(id);\n        data.setName(name);\n        data.setTransientData(true);\n        data.setContainerId(containerId);\n        data.setContainerType(containerType);\n        data.setValue(\"A value\");\n        when(cacheService.get(\"transient_data\", name + \":\" + containerId + \":\" + containerType)).thenReturn(data);\n        return data;\n    }\n\n    @Test\n    public void testUpdateDataInstance() throws Exception {\n        // given\n        SShortTextDataInstance data = createData(12, 42, \"name\", \"ctype\");\n        when(cacheService.getKeys(\"transient_data\")).thenReturn(Arrays.asList((Object) \"name:42:ctype\"));\n\n        // when\n        EntityUpdateDescriptor entityUpdateDescriptor = new EntityUpdateDescriptor();\n        entityUpdateDescriptor.addField(\"value\", \"newValue\");\n        transientDataServiceImpl.updateDataInstance(data, entityUpdateDescriptor);\n\n        // then\n        assertThat(transientDataServiceImpl.getDataInstance(12).getValue()).isEqualTo(\"newValue\");\n        verify(cacheService, times(1)).store(\"transient_data\", \"name:42:ctype\", data);\n    }\n\n    @Test\n    public void testDeleteDataInstance() throws Exception {\n        SShortTextDataInstance data = createData(12, 42, \"name\", \"ctype\");\n\n        transientDataServiceImpl.deleteDataInstance(data);\n\n        verify(cacheService).remove(\"transient_data\", \"name:42:ctype\");\n    }\n\n    @Test\n    public void should_getDataInstanceById_return_the_data() throws Exception {\n        // given\n        SShortTextDataInstance data = createData(12, 42, \"name\", \"ctype\");\n        when(cacheService.getKeys(\"transient_data\")).thenReturn(Arrays.asList((Object) \"name:42:ctype\"));\n\n        // when\n        SDataInstance result = transientDataServiceImpl.getDataInstance(12);\n\n        // then\n        assertThat(result).isEqualTo(data);\n    }\n\n    @Test\n    public void testGetDataInstanceStringLongString() throws Exception {\n        // given\n        SShortTextDataInstance data = createData(12, 42, \"name\", \"ctype\");\n        when(cacheService.getKeys(\"transient_data\")).thenReturn(Arrays.asList((Object) \"name:42:ctype\"));\n\n        // when\n        SDataInstance result = transientDataServiceImpl.getDataInstance(\"name\", 42, \"ctype\");\n\n        // then\n        assertThat(result).isEqualTo(data);\n    }\n\n    @Test\n    public void should_get_multiple_data_instances_from_a_container() throws Exception {\n        SFlowNodeInstance flowNodeInstance = flowNodeInstance(42, 1);\n        SActivityDefinition activityDefinition = flowNodeDefinition(dataWithName(\"name\", null),\n                dataWithName(\"name1\", null),\n                dataWithName(\"name2\", null));\n        SProcessDefinition processDef = mock(SProcessDefinition.class);\n        SFlowElementContainerDefinition container = mock(SFlowElementContainerDefinition.class);\n        when(processDef.getProcessContainer()).thenReturn(container);\n        when(container.getFlowNode(42)).thenReturn(activityDefinition);\n        when(processDefinitionService.getProcessDefinition(1)).thenReturn(processDef);\n        when(flowNodeInstanceService.getFlowNodeInstance(42)).thenReturn(flowNodeInstance);\n        SShortTextDataInstance data = createData(12, 42, \"name\", \"ctype\");\n        SShortTextDataInstance data1 = createData(13, 42, \"name1\", \"ctype\");\n        SShortTextDataInstance data2 = createData(14, 42, \"name2\", \"ctype\");\n        when(cacheService.getKeys(\"transient_data\"))\n                .thenReturn(Arrays.asList(\"name:42:ctype\", \"name:44:ctype\", \"name:48:ctype\"));\n\n        List<SDataInstance> dataInstances = transientDataServiceImpl.getDataInstances(42, \"ctype\", 0, 10);\n\n        assertThat(dataInstances.size()).isEqualTo(3);\n        assertThat(dataInstances).contains(data, data1, data2);\n    }\n\n    @Test\n    public void should_return_empty_data_list_for_flownodes_not_in_definition() throws Exception {\n        //happens when flow node is e.g. a manual task\n        SProcessDefinitionImpl processDefinition = new SProcessDefinitionImpl(\"p\", \"1\");\n        SFlowNodeInstance flowNodeInstance = flowNodeInstance(42, 1);\n        when(processDefinitionService.getProcessDefinition(1)).thenReturn(processDefinition);\n        when(flowNodeInstanceService.getFlowNodeInstance(42)).thenReturn(flowNodeInstance);\n\n        List<SDataInstance> dataInstances = transientDataServiceImpl.getDataInstances(42, \"ctype\", 0, 10);\n\n        assertThat(dataInstances).isEmpty();\n    }\n\n    private SDataDefinition dataWithName(String dataName, SExpression defaultValueExpression) {\n        SDataDefinition dataDef = mock(SDataDefinition.class);\n        when(dataDef.isTransientData()).thenReturn(true);\n        when(dataDef.getName()).thenReturn(dataName);\n        if (defaultValueExpression != null) {\n            when(dataDef.getDefaultValueExpression()).thenReturn(defaultValueExpression);\n        }\n        return dataDef;\n    }\n\n    @Test\n    public void should_paginate_result_when_retrieving_multiple_DataInstance() throws Exception {\n        SFlowNodeInstance flowNodeInstance = flowNodeInstance(42, 1);\n        SDataDefinition dataDef = dataWithName(\"name\", null);\n        SActivityDefinition activityDefinition = flowNodeDefinition(dataDef);\n        SProcessDefinition processDef = mock(SProcessDefinition.class);\n        SFlowElementContainerDefinition container = mock(SFlowElementContainerDefinition.class);\n        when(processDef.getProcessContainer()).thenReturn(container);\n        when(container.getFlowNode(42)).thenReturn(activityDefinition);\n        when(processDefinitionService.getProcessDefinition(1)).thenReturn(processDef);\n        when(flowNodeInstanceService.getFlowNodeInstance(42)).thenReturn(flowNodeInstance);\n        when(cacheService.getKeys(\"transient_data\")).thenReturn(Arrays.asList((Object) \"name:42:ctype\"));\n\n        assertThat(transientDataServiceImpl.getDataInstances(42, \"ctype\", 0, 10)).hasSize(1);\n        assertThat(transientDataServiceImpl.getDataInstances(42, \"ctype\", 0, 1)).hasSize(1);\n        assertThat(transientDataServiceImpl.getDataInstances(42, \"ctype\", 1, 1)).isEmpty();\n    }\n\n    @Test\n    public void should_reevaluate_a_transient_data_instance_if_not_found_in_cache_but_data_definition_exists()\n            throws Exception {\n        // given\n        SFlowNodeInstance flowNodeInstance = flowNodeInstance(42, 1);\n        SExpression defaultValueExpression = mock(SExpression.class);\n        SDataDefinition dataDef = dataWithName(\"name\", defaultValueExpression);\n        SActivityDefinition activityDefinition = flowNodeDefinition(dataDef);\n        SProcessDefinition processDef = mock(SProcessDefinition.class);\n        SFlowElementContainerDefinition container = mock(SFlowElementContainerDefinition.class);\n        when(processDef.getProcessContainer()).thenReturn(container);\n        when(container.getFlowNode(42)).thenReturn(activityDefinition);\n        when(processDefinitionService.getProcessDefinition(1)).thenReturn(processDef);\n        when(flowNodeInstanceService.getFlowNodeInstance(42)).thenReturn(flowNodeInstance);\n        when(expressionResolverService.evaluate(eq(defaultValueExpression), notNull())).thenReturn(\"new data value\");\n\n        // when\n        transientDataServiceImpl.getDataInstance(\"name\", 42, \"ctype\");\n\n        // then\n        verify(expressionResolverService).evaluate(eq(defaultValueExpression), notNull());\n        ArgumentCaptor<SDataInstance> argumentCaptor = ArgumentCaptor.forClass(SDataInstance.class);\n        verify(transientDataServiceImpl).createDataInstance(argumentCaptor.capture());\n        SDataInstance dataInstance = argumentCaptor.getValue();\n        verify(cacheService).store(TransientDataServiceImpl.TRANSIENT_DATA_CACHE_NAME,\n                TransientDataServiceImpl.getKey(dataInstance), dataInstance);\n    }\n\n    @Test\n    public void should_throw_a_SDataInstanceException_when_trying_reevaluate_a_data_not_defined()\n            throws Exception {\n        // given\n        SFlowNodeInstance flowNodeInstance = flowNodeInstance(42, 1);\n        SActivityDefinition activityDefinition = flowNodeDefinition();\n        SProcessDefinition processDef = mock(SProcessDefinition.class);\n        SFlowElementContainerDefinition container = mock(SFlowElementContainerDefinition.class);\n        when(processDef.getProcessContainer()).thenReturn(container);\n        when(container.getFlowNode(42)).thenReturn(activityDefinition);\n        when(processDefinitionService.getProcessDefinition(1)).thenReturn(processDef);\n        when(flowNodeInstanceService.getFlowNodeInstance(42)).thenReturn(flowNodeInstance);\n\n        // then\n        expectedException.expect(SDataInstanceException.class);\n\n        // when\n        transientDataServiceImpl.getDataInstance(\"name\", 42, \"ctype\");\n    }\n\n    private SFlowNodeInstance flowNodeInstance(long defId, long processDefId) {\n        SFlowNodeInstance instance = mock(SFlowNodeInstance.class);\n        when(instance.getFlowNodeDefinitionId()).thenReturn(defId);\n        when(instance.getProcessDefinitionId()).thenReturn(processDefId);\n        return instance;\n    }\n\n    private SActivityDefinition flowNodeDefinition(SDataDefinition... dataDefs) {\n        SActivityDefinition definition = mock(SActivityDefinition.class);\n        if (dataDefs.length > 0) {\n            when(definition.getSDataDefinitions()).thenReturn(Arrays.asList(dataDefs));\n        } else {\n            when(definition.getSDataDefinitions()).thenReturn(Collections.emptyList());\n        }\n        return definition;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-form-mapping/build.gradle",
    "content": "\n\ndependencies {\n    api project(':bpm:bonita-common')\n    api project(':services:bonita-session')\n    api project(':bpm:bonita-core:bonita-process-definition')\n    api project(':services:bonita-page')\n    api project(':services:bonita-builder')\n    api project(':services:bonita-persistence')\n    api project(':services:bonita-commons')\n    api project(':services:bonita-events')\n    api libs.hibernateCore\n    testImplementation libs.assertj\n    testImplementation libs.mockitoCore\n\n    annotationProcessor libs.lombok\n    compileOnly libs.lombok\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-form-mapping/src/main/java/org/bonitasoft/engine/core/form/AuthorizationRuleMapping.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.form;\n\nimport java.util.List;\n\n/**\n * @author Laurent Leseigneur\n */\npublic interface AuthorizationRuleMapping {\n\n    /**\n     * @return a list of rule identifiers applied when a form or page is used to start a process\n     */\n    List<String> getProcessStartRuleKeys();\n\n    /**\n     * @return a list of rule identifiers applied when a form or page is used to display process overview\n     */\n    List<String> getProcessOverviewRuleKeys();\n\n    /**\n     * @return a list of rule identifiers applied when a form or page is used to execute a task\n     */\n    List<String> getTaskRuleKeys();\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-form-mapping/src/main/java/org/bonitasoft/engine/core/form/FormMappingKeyGenerator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.form;\n\nimport org.bonitasoft.engine.commons.exceptions.SObjectCreationException;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface FormMappingKeyGenerator {\n\n    String generateKey(long processDefinitionId, String task, Integer type) throws SObjectCreationException;\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-form-mapping/src/main/java/org/bonitasoft/engine/core/form/FormMappingService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.form;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.exceptions.SObjectCreationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface FormMappingService {\n\n    SFormMapping create(long processDefinitionId, String task, Integer type, String target, String form)\n            throws SObjectCreationException, SBonitaReadException;\n\n    void update(SFormMapping formMapping, String url, Long pageId) throws SObjectModificationException;\n\n    void delete(SFormMapping formMapping) throws SObjectModificationException;\n\n    SFormMapping get(long formMappingId) throws SBonitaReadException, SObjectNotFoundException;\n\n    SFormMapping get(String key) throws SBonitaReadException, SObjectNotFoundException;\n\n    SFormMapping get(long processDefinitionId, Integer type, String task)\n            throws SBonitaReadException, SObjectNotFoundException;\n\n    SFormMapping get(long processDefinitionId, Integer type) throws SBonitaReadException, SObjectNotFoundException;\n\n    List<SFormMapping> list(long processDefinitionId, int fromIndex, int numberOfResults) throws SBonitaReadException;\n\n    List<SFormMapping> list(int fromIndex, int numberOfResults) throws SBonitaReadException;\n\n    List<SFormMapping> searchFormMappings(QueryOptions queryOptions) throws SBonitaReadException;\n\n    long getNumberOfFormMappings(QueryOptions queryOptions) throws SBonitaReadException;\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-form-mapping/src/main/java/org/bonitasoft/engine/core/form/SFormMapping.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.form;\n\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.JoinColumn;\nimport javax.persistence.ManyToOne;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.form.FormMappingType;\nimport org.bonitasoft.engine.page.SPageMapping;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n@Entity\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\n@Builder\n@Table(name = \"form_mapping\")\npublic class SFormMapping implements PersistentObject {\n\n    public static final String TARGET_INTERNAL = \"INTERNAL\";\n    public static final String TARGET_URL = \"URL\";\n    public static final String TARGET_LEGACY = \"LEGACY\";\n    public static final String TARGET_UNDEFINED = \"UNDEFINED\";\n    public static final String TARGET_NONE = \"NONE\";\n    public static final int TYPE_PROCESS_START = 1;\n    public static final int TYPE_PROCESS_OVERVIEW = 2;\n    public static final int TYPE_TASK = 3;\n\n    @Id\n    private long id;\n    @Column(name = \"process\")\n    private long processDefinitionId;\n    private String task;\n    private String target;\n    @ManyToOne\n    @JoinColumn(name = \"page_mapping_id\", referencedColumnName = \"id\")\n    private SPageMapping pageMapping;\n    private Integer type;\n    private long lastUpdateDate;\n    private long lastUpdatedBy;\n\n    public SFormMapping(long processDefinitionId, Integer type, String task, String target) {\n        this.processDefinitionId = processDefinitionId;\n        this.task = task;\n        this.type = type;\n        this.target = target;\n    }\n\n    public String getProcessElementName() {\n        switch (FormMappingType.getTypeFromId(this.getType())) {\n            case TASK:\n                return this.getTask();\n            case PROCESS_OVERVIEW:\n                return FormMappingType.PROCESS_OVERVIEW.toString();\n            case PROCESS_START:\n                return FormMappingType.PROCESS_START.toString();\n            default:\n                return null;\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-form-mapping/src/main/java/org/bonitasoft/engine/core/form/impl/AuthorizationRuleMappingImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.form.impl;\n\nimport static org.bonitasoft.engine.page.AuthorizationRuleConstants.*;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.form.AuthorizationRuleMapping;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class AuthorizationRuleMappingImpl implements AuthorizationRuleMapping {\n\n    @Override\n    public List<String> getProcessStartRuleKeys() {\n        return Arrays.asList(IS_ADMIN, IS_PROCESS_OWNER, IS_ACTOR_INITIATOR);\n    }\n\n    @Override\n    public List<String> getProcessOverviewRuleKeys() {\n        return Arrays.asList(IS_ADMIN, IS_PROCESS_OWNER, IS_PROCESS_INITIATOR, IS_TASK_PERFORMER,\n                IS_INVOLVED_IN_PROCESS_INSTANCE);\n    }\n\n    @Override\n    public List<String> getTaskRuleKeys() {\n        return Arrays.asList(IS_ADMIN, IS_PROCESS_OWNER, IS_TASK_AVAILABLE_FOR_USER);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-form-mapping/src/main/java/org/bonitasoft/engine/core/form/impl/FormMappingKeyGeneratorImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.form.impl;\n\nimport org.bonitasoft.engine.commons.exceptions.SObjectCreationException;\nimport org.bonitasoft.engine.core.form.FormMappingKeyGenerator;\nimport org.bonitasoft.engine.core.form.SFormMapping;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class FormMappingKeyGeneratorImpl implements FormMappingKeyGenerator {\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    public FormMappingKeyGeneratorImpl(ProcessDefinitionService processDefinitionService) {\n        this.processDefinitionService = processDefinitionService;\n    }\n\n    @Override\n    public String generateKey(long processDefinitionId, String task, Integer type) throws SObjectCreationException {\n        SProcessDefinition processDefinition;\n        try {\n            processDefinition = processDefinitionService.getProcessDefinition(processDefinitionId);\n            if (processDefinition == null) {\n                throw new SObjectCreationException(\"Process with id \" + processDefinitionId + \" does not exists\");\n            }\n        } catch (SProcessDefinitionNotFoundException | SBonitaReadException e) {\n            throw new SObjectCreationException(\"Unable to get the process with id \" + processDefinitionId, e);\n        }\n        switch (type) {\n            case SFormMapping.TYPE_PROCESS_OVERVIEW:\n                return \"processInstance/\" + processDefinition.getName() + \"/\" + processDefinition.getVersion();\n            case SFormMapping.TYPE_PROCESS_START:\n                return \"process/\" + processDefinition.getName() + \"/\" + processDefinition.getVersion();\n            case SFormMapping.TYPE_TASK:\n                if (task == null || task.isEmpty()) {\n                    throw new SObjectCreationException(\"The task name is not set\");\n                }\n                return \"taskInstance/\" + processDefinition.getName() + \"/\" + processDefinition.getVersion() + \"/\"\n                        + task;\n        }\n        throw new SObjectCreationException(\"Unable to generate the key for the unknown type \" + type);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-form-mapping/src/main/java/org/bonitasoft/engine/core/form/impl/FormMappingServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.form.impl;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SDeletionException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectCreationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.core.form.AuthorizationRuleMapping;\nimport org.bonitasoft.engine.core.form.FormMappingKeyGenerator;\nimport org.bonitasoft.engine.core.form.FormMappingService;\nimport org.bonitasoft.engine.core.form.SFormMapping;\nimport org.bonitasoft.engine.form.FormMappingType;\nimport org.bonitasoft.engine.page.PageMappingService;\nimport org.bonitasoft.engine.page.PageService;\nimport org.bonitasoft.engine.page.SPage;\nimport org.bonitasoft.engine.page.SPageMapping;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity;\nimport org.bonitasoft.engine.queriablelogger.model.builder.ActionType;\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\nimport org.bonitasoft.engine.session.SSessionNotFoundException;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor;\nimport org.bonitasoft.engine.sessionaccessor.SessionIdNotSetException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class FormMappingServiceImpl implements FormMappingService {\n\n    public static final String FORM_MAPPING = \"FORM_MAPPING\";\n\n    private final Recorder recorder;\n    private final ReadPersistenceService persistenceService;\n    private final SessionService sessionService;\n    private final ReadSessionAccessor sessionAccessor;\n    private final PageMappingService pageMappingService;\n    private final PageService pageService;\n    private final FormMappingKeyGenerator formMappingKeyGenerator;\n    private final String externalUrlAdapter;\n    private final String legacyUrlAdapter;\n    private final Map<FormMappingType, List<String>> authorizationRulesMap;\n    private final QueriableLoggerService queriableLoggerService;\n\n    public FormMappingServiceImpl(Recorder recorder, ReadPersistenceService persistenceService,\n            SessionService sessionService,\n            ReadSessionAccessor sessionAccessor, PageMappingService pageMappingService, PageService pageService,\n            FormMappingKeyGenerator formMappingKeyGenerator, String externalUrlAdapter, String legacyUrlAdapter,\n            QueriableLoggerService queriableLoggerService, AuthorizationRuleMapping authorizationRuleMapping) {\n        this.recorder = recorder;\n        this.persistenceService = persistenceService;\n        this.sessionService = sessionService;\n        this.sessionAccessor = sessionAccessor;\n        this.pageMappingService = pageMappingService;\n        this.pageService = pageService;\n        this.formMappingKeyGenerator = formMappingKeyGenerator;\n        this.externalUrlAdapter = externalUrlAdapter;\n        this.legacyUrlAdapter = legacyUrlAdapter;\n        this.queriableLoggerService = queriableLoggerService;\n\n        authorizationRulesMap = new HashMap<>(3);\n        authorizationRulesMap.put(FormMappingType.PROCESS_START, authorizationRuleMapping.getProcessStartRuleKeys());\n        authorizationRulesMap.put(FormMappingType.TASK, authorizationRuleMapping.getTaskRuleKeys());\n        authorizationRulesMap.put(FormMappingType.PROCESS_OVERVIEW,\n                authorizationRuleMapping.getProcessOverviewRuleKeys());\n\n    }\n\n    @Override\n    public SFormMapping create(long processDefinitionId, String task, Integer type, String target, String form)\n            throws SBonitaReadException, SObjectCreationException {\n        if (target == null) {\n            throw new IllegalArgumentException(\"Form target is null\");\n        }\n        SPageMapping sPageMapping;\n        String key = formMappingKeyGenerator.generateKey(processDefinitionId, task, type);\n        List<String> authorizationRules = buildAuthorizationRules(type);\n        switch (target) {\n            case SFormMapping.TARGET_INTERNAL:\n                sPageMapping = pageMappingService.create(key, getPageIdOrNull(form, processDefinitionId),\n                        authorizationRules);\n                break;\n            case SFormMapping.TARGET_URL:\n                sPageMapping = pageMappingService.create(key, form, externalUrlAdapter, authorizationRules);\n                break;\n            case SFormMapping.TARGET_LEGACY:\n                sPageMapping = pageMappingService.create(key, null, legacyUrlAdapter, null);\n                break;\n            case SFormMapping.TARGET_UNDEFINED:\n                sPageMapping = null;\n                break;\n            case SFormMapping.TARGET_NONE:\n                sPageMapping = pageMappingService.create(key, null, authorizationRules);\n                break;\n            default:\n                throw new IllegalArgumentException(\"Illegal form target \" + target);\n\n        }\n        SFormMapping sFormMapping = new SFormMapping(processDefinitionId, type, task, target);\n        insertFormMapping(sFormMapping, sPageMapping);\n        return sFormMapping;\n    }\n\n    private List<String> buildAuthorizationRules(Integer type) {\n        return authorizationRulesMap.get(FormMappingType.getTypeFromId(type));\n    }\n\n    Long getPageIdOrNull(String form, long processDefinitionId) throws SBonitaReadException {\n        SPage pageByName = pageService.getPageByNameAndProcessDefinitionId(form, processDefinitionId);\n        if (pageByName == null) {\n            pageByName = pageService.getPageByName(form);\n        }\n        return pageByName == null ? null : pageByName.getId();\n    }\n\n    private void insertFormMapping(SFormMapping sFormMapping, SPageMapping sPageMapping)\n            throws SObjectCreationException {\n        sFormMapping.setPageMapping(sPageMapping);\n        FormMappingLogBuilder logBuilder = getLogBuilder(ActionType.CREATED);\n        try {\n            recorder.recordInsert(new InsertRecord(sFormMapping), FORM_MAPPING);\n            log(sFormMapping, SQueriableLog.STATUS_OK, logBuilder, \"insertFormMapping\", \"create\");\n        } catch (SRecorderException e) {\n            log(sFormMapping, SQueriableLog.STATUS_FAIL, logBuilder, \"insertFormMapping\", \"failed to create\");\n            throw new SObjectCreationException(e);\n        }\n    }\n\n    @Override\n    public void update(SFormMapping formMapping, String url, Long pageId) throws SObjectModificationException {\n        String target = getFormMappingTarget(url, pageId);\n        String urlAdapter = checkAndGetUrlAdapter(url);\n        checkThatInternalPageExists(pageId);\n        FormMappingLogBuilder logBuilder = getLogBuilder(ActionType.UPDATED);\n        EntityUpdateDescriptor entityUpdateDescriptor = new EntityUpdateDescriptor();\n        try {\n            Long oldPageId = null;\n            String oldUrlAdapter = null;\n            String oldUrl = null;\n            entityUpdateDescriptor.addField(\"target\", target);\n            entityUpdateDescriptor.addField(\"lastUpdatedBy\", getSessionUserId());\n            entityUpdateDescriptor.addField(\"lastUpdateDate\", System.currentTimeMillis());\n            // case where page mapping did not exist already (TARGET == UNDEFINED):\n            if (formMapping.getPageMapping() == null) {\n                SPageMapping sPageMapping = createPageMappingForExistingFormMapping(formMapping, url, pageId);\n                formMapping.setPageMapping(sPageMapping);\n            } else {\n                oldPageId = formMapping.getPageMapping().getPageId();\n                oldUrlAdapter = formMapping.getPageMapping().getUrlAdapter();\n                oldUrl = formMapping.getPageMapping().getUrl();\n                // Update the existing page mapping:\n                entityUpdateDescriptor.addField(\"pageMapping.url\", url);\n                entityUpdateDescriptor.addField(\"pageMapping.urlAdapter\", urlAdapter);\n                entityUpdateDescriptor.addField(\"pageMapping.pageId\", pageId);\n            }\n            recorder.recordUpdate(UpdateRecord.buildSetFields(formMapping, entityUpdateDescriptor), FORM_MAPPING);\n            final String rawMessage = \"Previous: pageId=<\" + oldPageId + \"> urlAdapter=<\" + oldUrlAdapter + \"> url=<\"\n                    + oldUrl + \">\";\n            log(formMapping, SQueriableLog.STATUS_OK, logBuilder, \"update\", truncate(rawMessage));\n        } catch (SBonitaException e) {\n            log(formMapping, SQueriableLog.STATUS_FAIL, logBuilder, \"update\", \"failed to update\");\n            throw new SObjectModificationException(e);\n        }\n    }\n\n    String getFormMappingTarget(String url, Long pageId) throws SObjectModificationException {\n        if (url != null && pageId != null) {\n            throw new SObjectModificationException(\"Can't update the form mapping with both url and pageId\");\n        }\n        String target = SFormMapping.TARGET_NONE;\n        if (url != null) {\n            target = SFormMapping.TARGET_URL;\n        } else if (pageId != null) {\n            target = SFormMapping.TARGET_INTERNAL;\n        }\n        return target;\n    }\n\n    String truncate(String rawMessage) {\n        return rawMessage.substring(0, Math.min(255, rawMessage.length()));\n    }\n\n    private <T extends SLogBuilder> void initializeLogBuilder(final T logBuilder) {\n        logBuilder.actionStatus(SQueriableLog.STATUS_FAIL).severity(SQueriableLogSeverity.INTERNAL);\n    }\n\n    private <T extends HasCRUDEAction> void updateLog(final ActionType actionType, final T logBuilder) {\n        logBuilder.setActionType(actionType);\n    }\n\n    private FormMappingLogBuilder getLogBuilder(final ActionType actionType) {\n        final FormMappingLogBuilder logBuilder = new FormMappingLogBuilder();\n        this.initializeLogBuilder(logBuilder);\n        this.updateLog(actionType, logBuilder);\n        return logBuilder;\n    }\n\n    private void log(final SFormMapping formMapping, final int sQueriableLogStatus,\n            final FormMappingLogBuilder logBuilder, final String callerMethodName,\n            String rawMessage) {\n        logBuilder.actionScope(String.valueOf(formMapping.getProcessDefinitionId()));\n        logBuilder.actionStatus(sQueriableLogStatus);\n        logBuilder.objectId(formMapping.getId());\n        logBuilder.rawMessage(rawMessage);\n        final SQueriableLog log = logBuilder.build();\n        if (queriableLoggerService.isLoggable(log.getActionType(), log.getSeverity())) {\n            queriableLoggerService.log(this.getClass().getName(), callerMethodName, log);\n        }\n    }\n\n    protected SPageMapping createPageMappingForExistingFormMapping(SFormMapping formMapping, String url, Long pageId)\n            throws SObjectCreationException {\n        String key = formMappingKeyGenerator.generateKey(formMapping.getProcessDefinitionId(), formMapping.getTask(),\n                formMapping.getType());\n        if (url != null) {\n            return pageMappingService.create(key, url, externalUrlAdapter,\n                    buildAuthorizationRules(formMapping.getType()));\n        } else {\n            return pageMappingService.create(key, pageId, buildAuthorizationRules(formMapping.getType()));\n        }\n    }\n\n    protected void checkThatInternalPageExists(Long pageId) throws SObjectModificationException {\n        if (pageId != null) {\n            try {\n                pageService.getPage(pageId);\n            } catch (SBonitaReadException | SObjectNotFoundException e) {\n                throw new SObjectModificationException(\"the page with id \" + pageId + \" does not exists\");\n            }\n        }\n    }\n\n    protected String checkAndGetUrlAdapter(String url) throws SObjectModificationException {\n        if (url == null) {\n            return null;\n        }\n        checkUrlNotEmpty(url);\n        return externalUrlAdapter;\n    }\n\n    protected void checkUrlNotEmpty(String url) throws SObjectModificationException {\n        if (url.isEmpty()) {\n            throw new SObjectModificationException(\"Can't have an empty url\");\n        }\n    }\n\n    private long getSessionUserId() throws SSessionNotFoundException, SessionIdNotSetException {\n        return sessionService.getLoggedUserFromSession(sessionAccessor);\n    }\n\n    @Override\n    public void delete(SFormMapping formMapping) throws SObjectModificationException {\n        FormMappingLogBuilder logBuilder = getLogBuilder(ActionType.DELETED);\n        try {\n            recorder.recordDelete(new DeleteRecord(formMapping), FORM_MAPPING);\n            if (formMapping.getPageMapping() != null) {\n                pageMappingService.delete(formMapping.getPageMapping());\n            }\n            log(formMapping, SQueriableLog.STATUS_OK, logBuilder, \"delete\", \"delete\");\n        } catch (SRecorderException | SDeletionException e) {\n            log(formMapping, SQueriableLog.STATUS_FAIL, logBuilder, \"delete\", \"failed to delete\");\n            throw new SObjectModificationException(e);\n        }\n    }\n\n    @Override\n    public SFormMapping get(long formMappingId) throws SBonitaReadException, SObjectNotFoundException {\n        SFormMapping getFormMappingById = persistenceService.selectById(new SelectByIdDescriptor<>(SFormMapping.class,\n                formMappingId));\n        if (getFormMappingById == null) {\n            throw new SObjectNotFoundException(formMappingId);\n        }\n        return getFormMappingById;\n    }\n\n    @Override\n    public SFormMapping get(String key) throws SBonitaReadException, SObjectNotFoundException {\n        return persistenceService.selectOne(new SelectOneDescriptor<SFormMapping>(\"getFormMappingByKey\",\n                Collections.<String, Object> singletonMap(\"key\", key),\n                SFormMapping.class));\n    }\n\n    @Override\n    public SFormMapping get(long processDefinitionId, Integer type, String task) throws SBonitaReadException {\n        Map<String, Object> parameters = new HashMap<String, Object>(3);\n        parameters.put(\"processDefinitionId\", processDefinitionId);\n        parameters.put(\"type\", type);\n        parameters.put(\"task\", task);\n        return persistenceService.selectOne(new SelectOneDescriptor<SFormMapping>(\n                \"getFormMappingOfProcessDefinitionOnTask\", parameters, SFormMapping.class));\n    }\n\n    @Override\n    public SFormMapping get(long processDefinitionId, Integer type) throws SBonitaReadException {\n        Map<String, Object> parameters = new HashMap<String, Object>(3);\n        parameters.put(\"processDefinitionId\", processDefinitionId);\n        parameters.put(\"type\", type);\n        return persistenceService.selectOne(new SelectOneDescriptor<SFormMapping>(\"getFormMappingOfProcessDefinition\",\n                parameters, SFormMapping.class));\n    }\n\n    @Override\n    public List<SFormMapping> list(long processDefinitionId, int fromIndex, int numberOfResults)\n            throws SBonitaReadException {\n        Map<String, Object> parameters = new HashMap<String, Object>(3);\n        parameters.put(\"processDefinitionId\", processDefinitionId);\n        return persistenceService.selectList(new SelectListDescriptor<SFormMapping>(\n                \"getFormMappingsOfProcessDefinition\", parameters, SFormMapping.class,\n                new QueryOptions(\n                        fromIndex, numberOfResults)));\n    }\n\n    @Override\n    public List<SFormMapping> list(int fromIndex, int numberOfResults) throws SBonitaReadException {\n        Map<String, Object> parameters = new HashMap<String, Object>(3);\n        return persistenceService.selectList(new SelectListDescriptor<SFormMapping>(\"getFormMappings\", parameters,\n                SFormMapping.class, new QueryOptions(\n                        fromIndex, numberOfResults)));\n    }\n\n    @Override\n    public long getNumberOfFormMappings(QueryOptions queryOptions) throws SBonitaReadException {\n        return persistenceService.getNumberOfEntities(SFormMapping.class, queryOptions,\n                Collections.<String, Object> emptyMap());\n    }\n\n    @Override\n    public List<SFormMapping> searchFormMappings(QueryOptions queryOptions) throws SBonitaReadException {\n        return persistenceService.searchEntity(SFormMapping.class, queryOptions,\n                Collections.<String, Object> emptyMap());\n    }\n\n    private static class FormMappingLogBuilder extends CRUDELogBuilder implements SPersistenceLogBuilder {\n\n        /*\n         * resulting log example\n         * 1 54 1433257801158 2015 6 153 23 william.jobs 1 7.0.0-SNAPSHOT INTERNAL FORM_MAPPING_UPDATED 1 1 update the\n         * formmapping\n         * org.bonitasoft.engine.core.form.impl.FormMappingServiceImpl update -1 -1 1 -1 -1\n         */\n        @Override\n        protected String getActionTypePrefix() {\n            return FORM_MAPPING;\n        }\n\n        @Override\n        protected void checkExtraRules(SQueriableLog log) {\n        }\n\n        @Override\n        public SPersistenceLogBuilder objectId(long objectId) {\n            queriableLogBuilder.numericIndex(2, objectId);\n            return this;\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-form-mapping/src/main/java/org/bonitasoft/engine/core/form/impl/ManagerInvolvedAuthorizationRuleMappingImpl.java",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.form.impl;\n\nimport static org.bonitasoft.engine.page.AuthorizationRuleConstants.*;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.form.AuthorizationRuleMapping;\n\n/**\n * Modified version of the default implementation, with the following change:\n * For case overview, also authorize access for a manager of a user involved in the case.\n *\n * @author Emmanuel Duchastenier\n * @author Danila Mazour\n */\npublic class ManagerInvolvedAuthorizationRuleMappingImpl implements AuthorizationRuleMapping {\n\n    @Override\n    public List<String> getProcessStartRuleKeys() {\n        return Arrays.asList(IS_ADMIN, IS_PROCESS_OWNER, IS_ACTOR_INITIATOR);\n    }\n\n    @Override\n    public List<String> getProcessOverviewRuleKeys() {\n        return Arrays.asList(IS_ADMIN, IS_PROCESS_OWNER, IS_PROCESS_INITIATOR, IS_TASK_PERFORMER,\n                IS_INVOLVED_IN_PROCESS_INSTANCE,\n                IS_MANAGER_OF_USER_INVOLVED_IN_PROCESS_INSTANCE);\n    }\n\n    @Override\n    public List<String> getTaskRuleKeys() {\n        return Arrays.asList(IS_ADMIN, IS_PROCESS_OWNER, IS_TASK_AVAILABLE_FOR_USER);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-form-mapping/src/main/resources/org/bonitasoft/engine/core/form/impl/form-mapping.queries.hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!--~\n  ~ Copyright (C) 2015 BonitaSoft S.A.\n  ~ BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n  ~ This program is free software: you can redistribute it and/or modify\n  ~ it under the terms of the GNU General Public License as published by\n  ~ the Free Software Foundation, either version 2.0 of the License, or\n  ~ (at your option) any later version.\n  ~ This program is distributed in the hope that it will be useful,\n  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of\n  ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n  ~ GNU General Public License for more details.\n  ~ You should have received a copy of the GNU General Public License\n  ~ along with this program. If not, see <http://www.gnu.org/licenses/>.\n  ~-->\n\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\" \"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd\">\n<hibernate-mapping auto-import=\"false\">\n\n    <query name=\"getFormMappings\">\n        SELECT formmap\n        FROM org.bonitasoft.engine.core.form.SFormMapping AS formmap\n    </query>\n\n    <query name=\"getFormMappingsOfProcessDefinition\">\n        SELECT formmap\n        FROM org.bonitasoft.engine.core.form.SFormMapping AS formmap\n        WHERE formmap.processDefinitionId = :processDefinitionId\n    </query>\n\n    <query name=\"getFormMappingOfProcessDefinitionOnTask\">\n        SELECT formmap\n        FROM org.bonitasoft.engine.core.form.SFormMapping AS formmap\n        WHERE formmap.processDefinitionId = :processDefinitionId\n        AND formmap.type = :type\n        AND formmap.task = :task\n    </query>\n\n    <query name=\"getFormMappingByKey\">\n        SELECT formmap\n        FROM org.bonitasoft.engine.core.form.SFormMapping AS formmap\n        WHERE formmap.pageMapping.key = :key\n    </query>\n\n    <query name=\"getFormMappingOfProcessDefinition\">\n        SELECT formmap\n        FROM org.bonitasoft.engine.core.form.SFormMapping AS formmap\n        WHERE formmap.processDefinitionId = :processDefinitionId\n        AND formmap.type = :type\n    </query>\n\n    <query name=\"getNumberOfSFormMapping\">\n        SELECT COUNT(formmap.id)\n        FROM org.bonitasoft.engine.core.form.SFormMapping AS formmap\n    </query>\n\n    <query name=\"searchSFormMapping\">\n        SELECT formmap\n        FROM org.bonitasoft.engine.core.form.SFormMapping AS formmap\n    </query>\n\n    <query name=\"getNumberOfSFormMappingwithSPageMapping\">\n        SELECT count(formmap.id)\n        FROM org.bonitasoft.engine.core.form.SFormMapping AS formmap\n        LEFT JOIN formmap.pageMapping AS pagemap\n    </query>\n\n    <query name=\"searchSFormMappingwithSPageMapping\">\n        SELECT formmap\n        FROM org.bonitasoft.engine.core.form.SFormMapping AS formmap\n        LEFT JOIN formmap.pageMapping AS pagemap\n    </query>\n\n</hibernate-mapping>\n"
  },
  {
    "path": "bpm/bonita-core/bonita-form-mapping/src/test/java/org/bonitasoft/engine/core/form/impl/FormMappingKeyGeneratorImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.form.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.doThrow;\n\nimport org.bonitasoft.engine.commons.exceptions.SObjectCreationException;\nimport org.bonitasoft.engine.core.form.SFormMapping;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SProcessDefinitionImpl;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Baptiste Mesta\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class FormMappingKeyGeneratorImplTest {\n\n    private static final long PROCESS_DEFINITION_ID = 123456l;\n    private static final String PROCESS_NAME = \"myProcess\";\n    private static final String PROCESS_VERSION = \"12.589\";\n    @Mock\n    private ProcessDefinitionService processDefinitionService;\n\n    @InjectMocks\n    private FormMappingKeyGeneratorImpl formMappingKeyGenerator;\n\n    @Before\n    public void before() throws Exception {\n        SProcessDefinitionImpl toBeReturned = new SProcessDefinitionImpl(PROCESS_NAME, PROCESS_VERSION);\n\n        doReturn(toBeReturned).when(processDefinitionService).getProcessDefinition(PROCESS_DEFINITION_ID);\n    }\n\n    @Test\n    public void generateKey_on_process_overview() throws Exception {\n        String generateKey = formMappingKeyGenerator.generateKey(PROCESS_DEFINITION_ID, null,\n                SFormMapping.TYPE_PROCESS_OVERVIEW);\n\n        assertThat(generateKey).isEqualTo(\"processInstance/myProcess/12.589\");\n    }\n\n    @Test(expected = SObjectCreationException.class)\n    public void generateKey_for_un_unknown_type() throws Exception {\n        formMappingKeyGenerator.generateKey(PROCESS_DEFINITION_ID, null, 12);\n    }\n\n    @Test\n    public void generateKey_on_task() throws Exception {\n        String generateKey = formMappingKeyGenerator.generateKey(PROCESS_DEFINITION_ID, \"step1\",\n                SFormMapping.TYPE_TASK);\n\n        assertThat(generateKey).isEqualTo(\"taskInstance/myProcess/12.589/step1\");\n    }\n\n    @Test\n    public void generateKey_on_process_start() throws Exception {\n        String generateKey = formMappingKeyGenerator.generateKey(PROCESS_DEFINITION_ID, \"step1\",\n                SFormMapping.TYPE_PROCESS_START);\n\n        assertThat(generateKey).isEqualTo(\"process/myProcess/12.589\");\n    }\n\n    @Test(expected = SObjectCreationException.class)\n    public void generateKey_when_definition_not_found() throws Exception {\n        formMappingKeyGenerator.generateKey(4444l, \"step1\", SFormMapping.TYPE_PROCESS_START);\n    }\n\n    @Test(expected = SObjectCreationException.class)\n    public void generateKey_on_task_when_no_task_name() throws Exception {\n        formMappingKeyGenerator.generateKey(PROCESS_DEFINITION_ID, null, SFormMapping.TYPE_TASK);\n    }\n\n    @Test(expected = SObjectCreationException.class)\n    public void generateKeyShouldThrowObjectCreationExceptionWhenProcessDefNotFound() throws Exception {\n        doThrow(SProcessDefinitionNotFoundException.class).when(processDefinitionService)\n                .getProcessDefinition(PROCESS_DEFINITION_ID);\n        formMappingKeyGenerator.generateKey(PROCESS_DEFINITION_ID, \"someTaskName\", SFormMapping.TYPE_TASK);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-form-mapping/src/test/java/org/bonitasoft/engine/core/form/impl/FormMappingServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.form.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.bonitasoft.engine.page.AuthorizationRuleConstants.*;\nimport static org.mockito.ArgumentMatchers.anyLong;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Arrays;\nimport java.util.Collections;\n\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.core.form.FormMappingKeyGenerator;\nimport org.bonitasoft.engine.core.form.SFormMapping;\nimport org.bonitasoft.engine.events.model.SUpdateEvent;\nimport org.bonitasoft.engine.form.FormMappingType;\nimport org.bonitasoft.engine.page.PageMappingService;\nimport org.bonitasoft.engine.page.PageService;\nimport org.bonitasoft.engine.page.SPage;\nimport org.bonitasoft.engine.page.SPageMapping;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * author Emmanuel Duchastenier\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class FormMappingServiceImplTest {\n\n    private static final long PROCESS_DEFINITION_ID = 456123L;\n    private static final long ID = 154L;\n    private static final Long PAGE_ID = 554L;\n    private static final String EXTERNAL = \"external\";\n    private static final String LEGACY = \"legacy\";\n    @Mock\n    private Recorder recorder;\n    @Mock\n    private ReadPersistenceService persistenceService;\n    @Mock\n    private SessionService sessionService;\n    @Mock\n    private ReadSessionAccessor sessionAccessor;\n    @Mock\n    private FormMappingKeyGenerator formMappingKeyGenerator;\n    @Mock\n    private PageMappingService pageMappingService;\n    @Mock\n    private QueriableLoggerService queriableLoggerService;\n    @Mock\n    private PageService pageService;\n    @Captor\n    private ArgumentCaptor<SUpdateEvent> updateEventCaptor;\n    @Captor\n    private ArgumentCaptor<UpdateRecord> updateRecordCaptor;\n\n    @Spy\n    private AuthorizationRuleMappingImpl authorizationRuleMapping;\n\n    @InjectMocks\n    private FormMappingServiceImpl formMappingService;\n\n    @Before\n    public void before() throws Exception {\n        formMappingService = new FormMappingServiceImpl(recorder, persistenceService, sessionService, sessionAccessor,\n                pageMappingService, pageService,\n                formMappingKeyGenerator, EXTERNAL, LEGACY, queriableLoggerService, authorizationRuleMapping);\n        doThrow(SObjectNotFoundException.class).when(pageService).getPage(anyLong());\n        doReturn(new SPage(\"myPage\", 0, 0, false, \"page.zip\")).when(pageService).getPage(PAGE_ID);\n    }\n\n    @Test\n    public void createFormMappingWithUndefinedTargetShouldNotAddPageMapping() throws Exception {\n        doReturn(\"mockedKey\").when(formMappingKeyGenerator).generateKey(PROCESS_DEFINITION_ID, \"someHumanTask\", 84);\n\n        formMappingService.create(PROCESS_DEFINITION_ID, \"someHumanTask\", 84, SFormMapping.TARGET_UNDEFINED, null);\n\n        verifyNoInteractions(pageMappingService);\n    }\n\n    @Test\n    public void createFormMapping_with_none_should_create_empty_form_mapping() throws Exception {\n        doReturn(\"mockedKey\").when(formMappingKeyGenerator).generateKey(PROCESS_DEFINITION_ID, \"someHumanTask\", 84);\n\n        formMappingService.create(PROCESS_DEFINITION_ID, \"someHumanTask\", 84, SFormMapping.TARGET_NONE, null);\n\n        verify(pageMappingService).create(eq(\"mockedKey\"), isNull(), isNull());\n    }\n\n    @Test\n    public void createWithInternalPageShouldCallPageMapping_create() throws Exception {\n        doReturn(\"theKey\").when(formMappingKeyGenerator).generateKey(PROCESS_DEFINITION_ID, \"step1\", 2);\n\n        formMappingService.create(PROCESS_DEFINITION_ID, \"step1\", 2, SFormMapping.TARGET_INTERNAL, null);\n\n        verify(pageMappingService).create(eq(\"theKey\"), nullable(Long.class), anyList());\n    }\n\n    @Test\n    public void createForTaskShouldAddCorrectAuthorizations() throws Exception {\n        doReturn(\"clef\").when(formMappingKeyGenerator).generateKey(PROCESS_DEFINITION_ID, \"task\",\n                FormMappingType.TASK.getId());\n\n        formMappingService.create(PROCESS_DEFINITION_ID, \"task\", FormMappingType.TASK.getId(),\n                SFormMapping.TARGET_INTERNAL, null);\n\n        verify(pageMappingService).create(\"clef\", null,\n                Arrays.asList(IS_ADMIN, IS_PROCESS_OWNER, IS_TASK_AVAILABLE_FOR_USER));\n    }\n\n    @Test\n    public void createForProcessStartShouldAddCorrectAuthorizations() throws Exception {\n        doReturn(\"keye\").when(formMappingKeyGenerator).generateKey(PROCESS_DEFINITION_ID, null,\n                FormMappingType.PROCESS_START.getId());\n\n        formMappingService.create(PROCESS_DEFINITION_ID, null, FormMappingType.PROCESS_START.getId(),\n                SFormMapping.TARGET_URL, null);\n\n        verify(pageMappingService).create(\"keye\", null, EXTERNAL,\n                Arrays.asList(IS_ADMIN, IS_PROCESS_OWNER, IS_ACTOR_INITIATOR));\n    }\n\n    @Test\n    public void createLegacyFormShouldNotAddCorrectAuthorizations() throws Exception {\n        doReturn(\"keye\").when(formMappingKeyGenerator).generateKey(PROCESS_DEFINITION_ID, null,\n                FormMappingType.PROCESS_START.getId());\n\n        formMappingService.create(PROCESS_DEFINITION_ID, null, FormMappingType.PROCESS_START.getId(),\n                SFormMapping.TARGET_LEGACY, null);\n\n        verify(pageMappingService).create(\"keye\", null, LEGACY, null);\n    }\n\n    @Test\n    public void createForProcessOverviewShouldAddCorrectAuthorizations() throws Exception {\n        doReturn(\"clave\").when(formMappingKeyGenerator).generateKey(PROCESS_DEFINITION_ID, null,\n                FormMappingType.PROCESS_OVERVIEW.getId());\n\n        formMappingService.create(PROCESS_DEFINITION_ID, null, FormMappingType.PROCESS_OVERVIEW.getId(),\n                SFormMapping.TARGET_URL, null);\n\n        verify(pageMappingService).create(\"clave\", null, EXTERNAL,\n                Arrays.asList(IS_ADMIN, IS_PROCESS_OWNER, IS_PROCESS_INITIATOR, IS_TASK_PERFORMER,\n                        IS_INVOLVED_IN_PROCESS_INSTANCE));\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void testCreateWithNullTarget() throws Exception {\n        formMappingService.create(1654L, \"step1\", 1, null, null);\n    }\n\n    @Test\n    public void testGetNumberOfFormMappings() throws Exception {\n        QueryOptions queryOptions = mock(QueryOptions.class);\n        formMappingService.getNumberOfFormMappings(queryOptions);\n\n        verify(persistenceService).getNumberOfEntities(SFormMapping.class, queryOptions,\n                Collections.<String, Object> emptyMap());\n    }\n\n    @Test\n    public void testGetByKey() throws Exception {\n        formMappingService.get(\"theKey\");\n\n        verify(persistenceService).selectOne(\n                new SelectOneDescriptor<SFormMapping>(\"getFormMappingByKey\",\n                        Collections.<String, Object> singletonMap(\"key\", \"theKey\"), SFormMapping.class));\n    }\n\n    @Test\n    public void testSearchFormMappings() throws Exception {\n        QueryOptions queryOptions = mock(QueryOptions.class);\n        formMappingService.searchFormMappings(queryOptions);\n\n        verify(persistenceService).searchEntity(SFormMapping.class, queryOptions,\n                Collections.<String, Object> emptyMap());\n    }\n\n    @Test\n    public void test_update_with_url() throws Exception {\n        SFormMapping formMapping = createFormMapping(ID);\n\n        formMappingService.update(formMapping, \"http://fake.url\", null);\n\n        verify(recorder).recordUpdate(updateRecordCaptor.capture(), anyString());\n        assertThat(updateRecordCaptor.getValue().getFields()).contains(entry(\"target\", SFormMapping.TARGET_URL),\n                entry(\"pageMapping.url\", \"http://fake.url\"),\n                entry(\"pageMapping.pageId\", null),\n                entry(\"pageMapping.urlAdapter\", EXTERNAL));\n    }\n\n    @Test\n    public void test_update_with_no_page_nor_url() throws Exception {\n        SFormMapping formMapping = createFormMapping(ID);\n\n        formMappingService.update(formMapping, null, null);\n\n        verify(recorder).recordUpdate(updateRecordCaptor.capture(), anyString());\n        assertThat(updateRecordCaptor.getValue().getFields()).contains(entry(\"target\", SFormMapping.TARGET_NONE),\n                entry(\"pageMapping.url\", null),\n                entry(\"pageMapping.pageId\", null),\n                entry(\"pageMapping.urlAdapter\", null));\n    }\n\n    private SFormMapping createFormMapping(long id) {\n        SFormMapping sFormMapping = new SFormMapping();\n        sFormMapping.setId(id);\n        SPageMapping pageMapping = new SPageMapping();\n        sFormMapping.setPageMapping(pageMapping);\n        return sFormMapping;\n    }\n\n    @Test\n    public void test_update_with_page() throws Exception {\n        SFormMapping formMapping = createFormMapping(ID);\n\n        formMappingService.update(formMapping, null, PAGE_ID);\n\n        verify(recorder).recordUpdate(updateRecordCaptor.capture(), anyString());\n        assertThat(updateRecordCaptor.getValue().getFields()).contains(entry(\"target\", SFormMapping.TARGET_INTERNAL),\n                entry(\"pageMapping.url\", null),\n                entry(\"pageMapping.pageId\", PAGE_ID),\n                entry(\"pageMapping.urlAdapter\", null));\n    }\n\n    @Test(expected = SObjectModificationException.class)\n    public void test_update_with_page_that_does_not_exists() throws Exception {\n        SFormMapping formMapping = createFormMapping(ID);\n\n        formMappingService.update(formMapping, null, 557441l);\n    }\n\n    @Test(expected = SObjectModificationException.class)\n    public void test_update_with_invalid_parameters1() throws Exception {\n        SFormMapping formMapping = createFormMapping(ID);\n\n        formMappingService.update(formMapping, \"plop\", PAGE_ID);\n    }\n\n    @Test(expected = SObjectModificationException.class)\n    public void test_update_with_invalid_parameters2() throws Exception {\n        SFormMapping formMapping = createFormMapping(ID);\n\n        formMappingService.update(formMapping, \"\", PAGE_ID);\n    }\n\n    @Test(expected = SObjectModificationException.class)\n    public void test_update_with_invalid_parameters3() throws Exception {\n        SFormMapping formMapping = createFormMapping(ID);\n\n        formMappingService.update(formMapping, \"\", null);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-form-mapping/src/test/java/org/bonitasoft/engine/core/form/impl/ManagerInvolvedAuthorizationRuleMappingImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.form.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.page.AuthorizationRuleConstants.IS_MANAGER_OF_USER_INVOLVED_IN_PROCESS_INSTANCE;\n\nimport java.util.List;\n\nimport org.junit.Test;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class ManagerInvolvedAuthorizationRuleMappingImplTest {\n\n    @Test\n    public void should_allow_manager_of_user_involved() throws Exception {\n        // given:\n        final ManagerInvolvedAuthorizationRuleMappingImpl ruleMapping = new ManagerInvolvedAuthorizationRuleMappingImpl();\n\n        // when:\n        final List<String> rules = ruleMapping.getProcessOverviewRuleKeys();\n\n        // then:\n        assertThat(rules).contains(IS_MANAGER_OF_USER_INVOLVED_IN_PROCESS_INSTANCE);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-home-server/build.gradle",
    "content": "\ndependencies {\n    api project(':services:bonita-commons')\n    api project(':bpm:bonita-common')\n    api libs.commonsIO\n    api project(':platform:platform-resources')\n    testImplementation libs.assertj\n    testImplementation libs.mockitoCore\n    testImplementation libs.systemRules\n    testRuntimeOnly libs.logback\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-home-server/src/main/java/org/bonitasoft/engine/home/BonitaHomeServer.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.home;\n\nimport static java.util.Collections.singletonList;\nimport static java.util.stream.Collectors.toMap;\nimport static org.bonitasoft.engine.home.FolderMgr.getFolder;\nimport static org.bonitasoft.engine.home.FolderMgr.getPlatformTempFolder;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URI;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Properties;\n\nimport javax.naming.NamingException;\n\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.io.IOUtil;\nimport org.bonitasoft.platform.configuration.ConfigurationService;\nimport org.bonitasoft.platform.configuration.model.BonitaConfiguration;\nimport org.bonitasoft.platform.setup.PlatformSetupAccessor;\nimport org.springframework.core.io.ClassPathResource;\n\n/**\n * Retrieve configuration files from database and from classpath\n */\npublic class BonitaHomeServer {\n\n    public static final BonitaHomeServer INSTANCE = new BonitaHomeServer();\n\n    /**\n     * property name of the server api implementation class name\n     */\n    private static final String SERVER_API_IMPLEMENTATION = \"serverApi\";\n    private final ProfileStorage profileStorage;\n    private ConfigurationService configurationService;\n\n    private BonitaHomeServer() {\n        profileStorage = new ProfileStorage();\n    }\n\n    public static BonitaHomeServer getInstance() {\n        return INSTANCE;\n    }\n\n    private ConfigurationService getConfigurationService() {\n        if (configurationService == null) { //should be given by spring\n            try {\n                configurationService = PlatformSetupAccessor.getConfigurationService();\n            } catch (NamingException e) {\n                throw new IllegalStateException(e);\n            }\n        }\n        return configurationService;\n    }\n\n    /**\n     * Properties inheritance is defined like that:\n     * <ol>\n     * <li>platform properties in database overrides platform properties in classpath</li>\n     * </ol>\n     */\n    public Properties getPlatformProperties() throws IOException {\n        return mergeProperties(getPropertiesFromClassPath(\"bonita-platform-community.properties\",\n                \"bonita-platform-private-community.properties\",\n                \"bonita-platform-private-sp.properties\",\n                \"bonita-platform-sp.properties\",\n                \"bonita-platform-sp-cluster.properties\"), getConfigurationService().getPlatformEngineConf());\n    }\n\n    /**\n     * Properties inheritance is defined like that:\n     * <ol>\n     * <li>tenant properties in database overrides tenant properties in classpath</li>\n     * <li>tenant properties in classpath overrides platform properties in database</li>\n     * <li>platform properties in database overrides platform properties in classpath</li>\n     * </ol>\n     */\n    public Properties getTenantProperties() throws IOException {\n        Properties allProperties = getPlatformProperties();\n        Properties tenantProperties = mergeProperties(getPropertiesFromClassPath(\n                \"bonita-tenant-community.properties\",\n                \"bonita-tenant-sp.properties\",\n                \"bonita-tenant-sp-cluster.properties\"), getConfigurationService().getTenantEngineConf());\n        allProperties.putAll(tenantProperties);\n        return allProperties;\n    }\n\n    public Properties getPropertiesFromClassPath(String... files) throws IOException {\n        Properties properties = new Properties();\n        for (String file : files) {\n            Properties fileProperties = new Properties();\n            ClassPathResource classPathResource = new ClassPathResource(file);\n            if (!classPathResource.exists()) {\n                continue;\n            }\n            fileProperties.load(classPathResource.getInputStream());\n            for (String property : fileProperties.stringPropertyNames()) {\n                properties.put(property, fileProperties.getProperty(property));\n            }\n        }\n        return properties;\n    }\n\n    public List<BonitaConfiguration> getPlatformConfiguration() {\n        return getAllXmlConfiguration(getConfigurationService().getPlatformEngineConf());\n    }\n\n    public List<BonitaConfiguration> getTenantConfiguration() {\n        return getAllXmlConfiguration(getConfigurationService().getTenantEngineConf());\n    }\n\n    private Properties mergeProperties(Properties mergeInto, List<BonitaConfiguration> configurationFiles)\n            throws IOException {\n        for (BonitaConfiguration bonitaConfiguration : configurationFiles) {\n            if (bonitaConfiguration.getResourceName().endsWith(\".properties\")) {\n                Properties properties = new Properties();\n                properties.load(new ByteArrayInputStream(bonitaConfiguration.getResourceContent()));\n                mergeInto.putAll(properties);\n            }\n        }\n        return mergeInto;\n    }\n\n    private List<BonitaConfiguration> getAllXmlConfiguration(List<BonitaConfiguration> configurationFiles) {\n        List<BonitaConfiguration> configurations = new ArrayList<>();\n        for (BonitaConfiguration bonitaConfiguration : configurationFiles) {\n            if (bonitaConfiguration.getResourceName().endsWith(\".xml\")) {\n                configurations.add(bonitaConfiguration);\n            }\n        }\n        return configurations;\n    }\n\n    /*\n     * =================================================\n     * process/tenant management\n     * =================================================\n     */\n\n    public ProfileStorage getProfileStorage() {\n        return profileStorage;\n    }\n\n    /**\n     * get the name of the implementation of {@link org.bonitasoft.engine.api.internal.ServerAPI} based on the current\n     * configuration of\n     * <code>bonita-platform.properties</code>\n     *\n     * @return the name of the class implementing {@link org.bonitasoft.engine.api.internal.ServerAPI}\n     * @throws IllegalStateException if the name of the implementation cannot be retrieved\n     */\n    public String getServerAPIImplementation() throws IllegalStateException {\n        try {\n            return getPlatformProperties().getProperty(SERVER_API_IMPLEMENTATION);\n        } catch (IOException e) {\n            throw new IllegalStateException(e);\n        }\n    }\n\n    /*\n     * =================================================\n     * temporary files\n     * =================================================\n     */\n\n    public File getPlatformTempFile(final String fileName) throws IOException {\n        final Folder tempFolder = getPlatformTempFolder();\n        final File file = tempFolder.getFile(fileName);\n        file.delete();\n        file.createNewFile();\n        return file;\n    }\n\n    public URI getLocalTemporaryFolder(final String artifactType) throws IOException {\n        return FolderMgr.getPlatformLocalClassLoaderFolder(artifactType).toURI();\n    }\n\n    public URI getLocalTemporaryFolder(final String artifactType, final long artifactId) throws IOException {\n        return FolderMgr.getPlatformLocalClassLoaderFolder(artifactType, artifactId).toURI();\n    }\n\n    public File getSecurityScriptsFolder() throws BonitaHomeNotSetException, IOException {\n        final Folder tenantSecurityScriptsFolder = getFolder(getPlatformTempFolder(), \"security-scripts\")\n                .createIfNotExists();\n        List<BonitaConfiguration> tenantSecurityScripts = getConfigurationService().getTenantSecurityScripts();\n        writeBonitaConfiguration(tenantSecurityScriptsFolder.getFile(), tenantSecurityScripts);\n        return tenantSecurityScriptsFolder.getFile();\n    }\n\n    private void writeBonitaConfiguration(File folder, List<BonitaConfiguration> bonitaConfigurations)\n            throws IOException {\n        for (BonitaConfiguration bonitaConfiguration : bonitaConfigurations) {\n            String[] pathArray = bonitaConfiguration.getResourceName().split(\"/\");\n            Path path = folder.toPath();\n            for (String pathChunk : pathArray) {\n                path = path.resolve(pathChunk);\n            }\n            path.toFile().getParentFile().mkdirs();\n            IOUtil.write(path.toFile(), bonitaConfiguration.getResourceContent());\n        }\n    }\n\n    public Map<String, byte[]> getClientPlatformConfigurations() {\n        return getConfigurationService().getPlatformPortalConf().stream()\n                .collect(toMap(BonitaConfiguration::getResourceName, BonitaConfiguration::getResourceContent));\n    }\n\n    public Map<String, byte[]> getTenantPortalConfigurations() {\n        return getConfigurationService().getTenantPortalConf().stream()\n                .collect(toMap(BonitaConfiguration::getResourceName, BonitaConfiguration::getResourceContent));\n    }\n\n    public byte[] getTenantPortalConfiguration(String file) {\n        return getConfigurationService().getTenantPortalConfiguration(file).getResourceContent();\n    }\n\n    public void updateTenantPortalConfigurationFile(String file, byte[] content) throws UpdateException {\n        BonitaConfiguration configuration = getConfigurationService().getTenantPortalConfiguration(file);\n        if (configuration != null) {\n            configuration.setResourceContent(content);\n            getConfigurationService().storeTenantPortalConf(singletonList(configuration));\n        } else {\n            throw new UpdateException(\"Cannot update non-existing configuration file \" + file);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-home-server/src/main/java/org/bonitasoft/engine/home/BonitaResource.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.home;\n\nimport java.util.Objects;\n\n/**\n * @author Baptiste Mesta\n */\npublic class BonitaResource {\n\n    private final String name;\n    private final byte[] content;\n\n    public static BonitaResource resource(String name, byte[] content) {\n        return new BonitaResource(name, content);\n    }\n\n    public BonitaResource(String name, byte[] content) {\n        this.name = name;\n        this.content = content;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public byte[] getContent() {\n        return content;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (o == null || getClass() != o.getClass())\n            return false;\n        BonitaResource that = (BonitaResource) o;\n        return Objects.equals(name, that.name) &&\n                Objects.equals(content, that.content);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(name, content);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-home-server/src/main/java/org/bonitasoft/engine/home/ProfileStorage.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.home;\n\nimport java.io.File;\nimport java.io.IOException;\n\n/**\n * Handles storage and retrieval of profile temporary md5 file.\n *\n * @author Baptiste Mesta\n */\npublic class ProfileStorage {\n\n    public File getProfileMD5() throws IOException {\n        Folder tenantWorkFolder = FolderMgr.getPlatformTempFolder();\n        return tenantWorkFolder.getFile(\"profiles.md5\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-home-server/src/main/java/org/bonitasoft/engine/home/Util.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.home;\n\nimport java.io.File;\n\nimport org.bonitasoft.engine.commons.StringUtils;\n\n/**\n * @author Charles Souillard\n */\npublic class Util {\n\n    public static String generateRelativeResourcePath(final File folder, final File file) {\n        String path = file.getAbsolutePath().replace(folder.getAbsolutePath(), \"\");\n        path = StringUtils.uniformizePathPattern(path);\n        // remove first slash, if any:\n        if (path.startsWith(\"/\")) {\n            path = path.substring(1);\n        }\n        return path;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-home-server/src/test/java/org/bonitasoft/engine/home/BonitaHomeServerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.home;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.verify;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Properties;\nimport java.util.stream.Collectors;\n\nimport org.apache.commons.io.FileUtils;\nimport org.bonitasoft.engine.commons.io.IOUtil;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.platform.configuration.ConfigurationService;\nimport org.bonitasoft.platform.configuration.model.BonitaConfiguration;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.TemporaryFolder;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Baptiste Mesta\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class BonitaHomeServerTest {\n\n    @InjectMocks\n    @Spy\n    BonitaHomeServer bonitaHomeServer;\n    @Mock\n    ConfigurationService configurationService;\n    @Rule\n    public TemporaryFolder temporaryFolder = new TemporaryFolder();\n\n    @Test\n    public void should_updateTenantPortalConfigurationFile_update_the_files() throws Exception {\n        //given\n        doReturn(conf(\"myFile.properties\", \"previous content\".getBytes()))\n                .when(configurationService).getTenantPortalConfiguration(\"myFile.properties\");\n\n        //when\n        bonitaHomeServer.updateTenantPortalConfigurationFile(\"myFile.properties\",\n                \"the updated content\".getBytes());\n        //then\n        verify(configurationService).storeTenantPortalConf(\n                Collections.singletonList(conf(\"myFile.properties\", \"the updated content\".getBytes())));\n    }\n\n    @Test(expected = UpdateException.class)\n    public void should_updateTenantPortalConfigurationFile_throws_UpdateException_if_not_found() throws Exception {\n        //given\n        doReturn(null).when(configurationService).getTenantPortalConfiguration(\"myFile.properties\");\n\n        //when\n        bonitaHomeServer.updateTenantPortalConfigurationFile(\"myFile.properties\",\n                \"the updated content\".getBytes());\n    }\n\n    private BonitaConfiguration conf(String file1, byte[] bytes) {\n        return new BonitaConfiguration(file1, bytes);\n    }\n\n    @Test\n    public void should_context_have_properties_overridden_with_database_properties() throws Exception {\n        //given\n        Properties databaseProperties = new Properties();\n        databaseProperties.setProperty(\"overriddenProperty\", \"databaseValue\");\n        databaseProperties.setProperty(\"databaseProperty\", \"aValueInDb\");\n        ByteArrayOutputStream out = new ByteArrayOutputStream();\n        databaseProperties.store(out, \"\");\n        doReturn(List.of(new BonitaConfiguration(\"myProp.properties\", out.toByteArray())))\n                .when(configurationService).getPlatformEngineConf();\n        Properties classPathProperties = new Properties();\n        classPathProperties.setProperty(\"overriddenProperty\", \"classPathValue\");\n        classPathProperties.setProperty(\"classPathProperty\", \"aValueInClassPath\");\n        doReturn(classPathProperties).when(bonitaHomeServer)\n                .getPropertiesFromClassPath(any(String[].class));\n        //when\n        Properties allProperties = bonitaHomeServer.getPlatformProperties();\n        //then\n        assertThat(allProperties).containsOnly(entry(\"overriddenProperty\", \"databaseValue\"),\n                entry(\"databaseProperty\", \"aValueInDb\"),\n                entry(\"classPathProperty\", \"aValueInClassPath\"));\n    }\n\n    @Test\n    public void tenant_properties_should_inherit_from_platform_properties() throws Exception {\n        //given\n        Thread.currentThread().setContextClassLoader(getClassLoaderWithProperties(\n                new BonitaConfiguration(\"bonita-platform-community.properties\",\n                        getPropertiesAsByteArray(\"prop1=prop1PlatformCP\", \"prop2=prop2PlatformCP\",\n                                \"prop3=prop3PlatformCP\", \"prop4=prop4PlatformCP\")),\n                new BonitaConfiguration(\"bonita-tenant-community.properties\",\n                        getPropertiesAsByteArray(\"prop3=prop3TenantCP\", \"prop4=prop4TenantCP\"))));\n\n        doReturn(Collections.singletonList(new BonitaConfiguration(\"platform.properties\",\n                getPropertiesAsByteArray(\"prop2=prop2PlatformDB\", \"prop3=prop3PlatformDB\", \"prop4=prop4PlatformDB\"))))\n                .when(configurationService).getPlatformEngineConf();\n\n        doReturn(Collections.singletonList(new BonitaConfiguration(\"tenant.properties\",\n                getPropertiesAsByteArray(\"prop4=prop4TenantDB\"))))\n                .when(configurationService).getTenantEngineConf();\n        //when\n        Properties allProperties = bonitaHomeServer.getTenantProperties();\n        //then\n        assertThat(allProperties).containsOnly(\n                entry(\"prop1\", \"prop1PlatformCP\"),\n                entry(\"prop2\", \"prop2PlatformDB\"),\n                entry(\"prop3\", \"prop3TenantCP\"),\n                entry(\"prop4\", \"prop4TenantDB\"));\n    }\n\n    private byte[] getPropertiesAsByteArray(String... propertiesV) {\n        return String.join(\"\\n\", propertiesV).getBytes(StandardCharsets.UTF_8);\n    }\n\n    private ClassLoader getClassLoaderWithProperties(BonitaConfiguration... bonitaConfigurations) throws IOException {\n        File jar1 = temporaryFolder.newFile(\"myJar1.jar\");\n        FileUtils.writeByteArrayToFile(jar1, IOUtil.generateJar(\n                Arrays.stream(bonitaConfigurations).collect(Collectors.toMap(BonitaConfiguration::getResourceName,\n                        BonitaConfiguration::getResourceContent))));\n        return new URLClassLoader(new URL[] { jar1.toURI().toURL() }, Thread.currentThread().getContextClassLoader());\n    }\n\n    @Test\n    public void should_getPropertiesFromClassPath_get_properties_of_all_files_from_classpath() throws Exception {\n        //given\n        File jar1 = temporaryFolder.newFile(\"myJar1.jar\");\n        FileUtils.writeByteArrayToFile(jar1, IOUtil\n                .generateJar(Collections.singletonMap(\"myPropertiesFile1.properties\", \"prop1=value1\".getBytes())));\n        File jar2 = temporaryFolder.newFile(\"myJar2.jar\");\n        FileUtils.writeByteArrayToFile(jar2, IOUtil\n                .generateJar(Collections.singletonMap(\"myPropertiesFile2.properties\", \"prop2=value2\".getBytes())));\n        File jar3 = temporaryFolder.newFile(\"myJar3.jar\");\n        FileUtils.writeByteArrayToFile(jar3, IOUtil\n                .generateJar(Collections.singletonMap(\"myPropertiesFile3.properties\", \"prop3=value3\".getBytes())));\n        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        URLClassLoader urlClassLoader = new URLClassLoader(\n                new URL[] { jar1.toURI().toURL(), jar2.toURI().toURL(), jar3.toURI().toURL() }, contextClassLoader);\n        try {\n            Thread.currentThread().setContextClassLoader(urlClassLoader);\n            //when\n            Properties propertiesFromClassPath = bonitaHomeServer.getPropertiesFromClassPath(\n                    \"myPropertiesFile1.properties\",\n                    \"myPropertiesFile2.properties\", \"anUnexistingProperty.properties\");\n            //then\n            assertThat(propertiesFromClassPath).containsOnly(entry(\"prop1\", \"value1\"), entry(\"prop2\", \"value2\"));\n        } finally {\n            Thread.currentThread().setContextClassLoader(contextClassLoader);\n        }\n    }\n\n    @Test\n    public void should_getTenantPortal_Configuration() throws Exception {\n        //given\n        final String configFile = \"a portal config file\";\n        BonitaConfiguration tenantTemplateConf = conf(configFile, \"{}\".getBytes());\n        doReturn(tenantTemplateConf).when(configurationService).getTenantPortalConfiguration(configFile);\n\n        //when\n        byte[] content = bonitaHomeServer.getTenantPortalConfiguration(configFile);\n\n        //then\n        verify(configurationService).getTenantPortalConfiguration(configFile);\n        assertThat(content).isEqualTo(\"{}\".getBytes());\n\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-home-server/src/test/java/org/bonitasoft/engine/home/ProfileStorageTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.home;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.io.File;\nimport java.nio.file.Files;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.TemporaryFolder;\nimport org.junit.runner.RunWith;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Laurent Leseigneur\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class ProfileStorageTest {\n\n    @Rule\n    public TemporaryFolder temporaryFolder = new TemporaryFolder();;\n\n    @Spy\n    ProfileStorage profileStorage = new ProfileStorage();\n\n    @Test\n    public void should_return_profiles_md5_file() throws Exception {\n        //given\n        Files.write(profileStorage.getProfileMD5().toPath(), \"md5\".getBytes());\n\n        //when\n        final File profileMD5 = profileStorage.getProfileMD5();\n\n        //then\n        assertThat(profileMD5).as(\"should retrieve file\").exists().hasBinaryContent(\"md5\".getBytes());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-home-server/src/test/java/org/bonitasoft/engine/home/UtilTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.home;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.io.File;\n\nimport org.junit.Test;\n\n/**\n * @author Baptiste Mesta\n */\npublic class UtilTest {\n\n    @Test\n    public void generateRelativeResourcePathShouldHandleBackslashOS() {\n        // given:\n        final String pathname = \"C:\\\\hello\\\\hi\\\\folder\";\n        final String resourceRelativePath = \"resource/toto.lst\";\n\n        // when:\n        final String generatedRelativeResourcePath = Util.generateRelativeResourcePath(new File(pathname),\n                new File(pathname + File.separator\n                        + resourceRelativePath));\n\n        // then:\n        assertThat(generatedRelativeResourcePath).isEqualTo(resourceRelativePath);\n    }\n\n    @Test\n    public void generateRelativeResourcePathShouldHandleNetWorkBackslash() {\n        // given:\n        final String pathname = \"\\\\\\\\hello\\\\hi\\\\folder\";\n        final String resourceRelativePath = \"resource/toto.lst\";\n\n        // when:\n        final String generatedRelativeResourcePath = Util.generateRelativeResourcePath(new File(pathname),\n                new File(pathname + File.separator\n                        + resourceRelativePath));\n\n        // then:\n        assertThat(generatedRelativeResourcePath).isEqualTo(resourceRelativePath);\n    }\n\n    @Test\n    public void generateRelativeResourcePathShouldNotContainFirstSlash() {\n        // given:\n        final String pathname = \"/home/target/some_folder/\";\n        final String resourceRelativePath = \"resource/toto.lst\";\n\n        // when:\n        final String generatedRelativeResourcePath = Util.generateRelativeResourcePath(new File(pathname),\n                new File(pathname + File.separator\n                        + resourceRelativePath));\n\n        // then:\n        assertThat(generatedRelativeResourcePath).isEqualTo(resourceRelativePath);\n    }\n\n    @Test\n    public void generateRelativeResourcePathShouldWorkWithRelativeInitialPath() {\n        // given:\n        final String pathname = \"target/nuns\";\n        final String resourceRelativePath = \"resource/toto.lst\";\n\n        // when:\n        final String generatedRelativeResourcePath = Util.generateRelativeResourcePath(new File(pathname),\n                new File(pathname + File.separator\n                        + resourceRelativePath));\n\n        // then:\n        assertThat(generatedRelativeResourcePath).isEqualTo(resourceRelativePath);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-login/build.gradle",
    "content": "dependencies {\n    api project(':services:bonita-session')\n    api project(':services:bonita-identity')\n    api project(':services:bonita-commons')\n    api project(':bpm:bonita-core:bonita-home-server')\n    api project(':services:bonita-authentication')\n    api project(':services:bonita-profile')\n    api project(':services:bonita-authorization')\n    testImplementation libs.mockitoCore\n    testImplementation libs.assertj\n    testImplementation libs.logback\n\n    compileOnly libs.lombok\n    annotationProcessor libs.lombok\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-login/src/main/java/org/bonitasoft/engine/core/login/LoginService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.login;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.identity.SUserNotFoundException;\nimport org.bonitasoft.engine.session.SSessionNotFoundException;\nimport org.bonitasoft.engine.session.model.SSession;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface LoginService {\n\n    /**\n     * generic login approach to handle outer authentication service like CAS or OAuth or whatever...\n     *\n     * @param credentials\n     *        the parameters to use to login\n     * @return the session created if login succeeds\n     * @throws SLoginException\n     *         if login fails\n     * @throws SUserNotFoundException\n     *         if the user does not exist in the database\n     */\n    SSession login(Map<String, Serializable> credentials) throws SLoginException, SUserNotFoundException;\n\n    boolean isValid(final long sessionId);\n\n    void logout(final long sessionId) throws SLoginException, SSessionNotFoundException;\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-login/src/main/java/org/bonitasoft/engine/core/login/SLoginException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.login;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SLoginException extends SBonitaException {\n\n    private static final long serialVersionUID = -8130716781791219493L;\n\n    public SLoginException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SLoginException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-login/src/main/java/org/bonitasoft/engine/core/login/SecuredLoginServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.login;\n\nimport static org.bonitasoft.engine.authentication.AuthenticationConstants.BASIC_USERNAME;\nimport static org.bonitasoft.engine.identity.model.builder.impl.SUserUpdateBuilderImpl.updateBuilder;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.bonitasoft.engine.authentication.AuthenticationConstants;\nimport org.bonitasoft.engine.authentication.AuthenticationException;\nimport org.bonitasoft.engine.authentication.GenericAuthenticationService;\nimport org.bonitasoft.engine.authorization.PermissionsBuilder;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.SUserNotFoundException;\nimport org.bonitasoft.engine.identity.SUserUpdateException;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.profile.ProfileService;\nimport org.bonitasoft.engine.profile.model.SProfile;\nimport org.bonitasoft.engine.session.SSessionException;\nimport org.bonitasoft.engine.session.SSessionNotFoundException;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.session.model.SSession;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.stereotype.Service;\n\n/**\n * @author Matthieu Chaffotte\n * @author Anthony Birembaut\n */\n@Service\npublic class SecuredLoginServiceImpl implements LoginService {\n\n    private static final Logger log = LoggerFactory.getLogger(SecuredLoginServiceImpl.class);\n    private final GenericAuthenticationService authenticationService;\n    private final SessionService sessionService;\n    private final IdentityService identityService;\n    private final TechnicalUser technicalUser;\n    private final ProfileService profileService;\n    private final PermissionsBuilder permissionsBuilder;\n\n    public SecuredLoginServiceImpl(\n            @Qualifier(\"entryPointAuthenticationService\") final GenericAuthenticationService authenticationService,\n            final SessionService sessionService,\n            final IdentityService identityService,\n            TechnicalUser technicalUser, ProfileService profileService, PermissionsBuilder permissionsBuilder) {\n        this.authenticationService = authenticationService;\n        this.sessionService = sessionService;\n        this.identityService = identityService;\n        this.technicalUser = technicalUser;\n        this.profileService = profileService;\n        this.permissionsBuilder = permissionsBuilder;\n    }\n\n    @Override\n    public SSession login(final Map<String, Serializable> credentials) throws SLoginException, SUserNotFoundException {\n        debugLog(\"Logging in\");\n        checkNull(credentials);\n        if (isTechnicalUser(credentials)) {\n            debugLog(\"Authenticated as technical user\");\n            return createSession(extractUserName(credentials), -1L, true);\n        }\n        String userName = verifyCredentials(credentials);\n        checkIsNotBlank(userName);\n        debugLog(\"Authenticated as regular user\");\n        SUser user = getUser(userName);\n        checkIsEnabled(user);\n        SSession session = createSession(userName, user.getId(), false);\n        updateLastConnectionDate(user);\n        return session;\n    }\n\n    private void updateLastConnectionDate(SUser user) throws SLoginException {\n        try {\n            identityService.updateUser(user, updateBuilder().updateLastConnection(System.currentTimeMillis()).done());\n        } catch (SUserUpdateException e) {\n            throw new SLoginException(e);\n        }\n    }\n\n    private void checkNull(Map<String, Serializable> credentials) throws SLoginException {\n        if (credentials == null) {\n            throw new SLoginException(\"invalid credentials, map is null\");\n        }\n    }\n\n    private SSession createSession(String userName, long id, boolean isTechnicalUser)\n            throws SLoginException {\n        try {\n            List<SProfile> profilesOfUser = profileService.getProfilesOfUser(id);\n            List<String> profiles = profilesOfUser.stream().map(SProfile::getName).collect(Collectors.toList());\n            Set<String> permissions = permissionsBuilder.getPermissions(isTechnicalUser, profiles, userName);\n            return sessionService.createSession(id, userName, isTechnicalUser, profiles, permissions);\n        } catch (SSessionException | SBonitaReadException e) {\n            throw new SLoginException(e);\n        }\n    }\n\n    private void checkIsEnabled(SUser user) throws SLoginException {\n        if (!user.isEnabled()) {\n            throw new SLoginException(\"Unable to login : the user is disable.\");\n        }\n    }\n\n    private void checkIsNotBlank(String userName) throws SLoginException {\n        if (StringUtils.isBlank(userName)) {\n            debugLog(\"Authentication failed\");\n            // now we are sure authentication Failed\n            throw new SLoginException(\"User name or password is not valid!\");\n        }\n    }\n\n    private SUser getUser(String userName) throws SUserNotFoundException {\n        try {\n            return identityService.getUserByUserName(userName);\n        } catch (SUserNotFoundException e) {\n            debugLog(\"Unable to find user with username \" + userName + \" in database.\");\n            throw e;\n        }\n    }\n\n    private String verifyCredentials(Map<String, Serializable> credentials) throws SLoginException {\n        try {\n            return authenticationService.checkUserCredentials(credentials);\n        } catch (AuthenticationException e) {\n            debugLog(\"Unable to authenticate user with username \" + credentials.get(BASIC_USERNAME));\n            throw new SLoginException(e);\n        }\n    }\n\n    private boolean isTechnicalUser(Map<String, Serializable> credentials) {\n        return technicalUser.getUserName().equals(extractUserName(credentials))\n                && technicalUser.getPassword()\n                        .equals(String.valueOf(credentials.get(AuthenticationConstants.BASIC_PASSWORD)));\n    }\n\n    private String extractUserName(Map<String, Serializable> credentials) {\n        if (credentials.containsKey(BASIC_USERNAME) && credentials.get(BASIC_USERNAME) != null) {\n            return String.valueOf(credentials.get(BASIC_USERNAME));\n        }\n        return null;\n    }\n\n    @Override\n    public void logout(final long sessionId) throws SSessionNotFoundException {\n        sessionService.deleteSession(sessionId);\n    }\n\n    @Override\n    public boolean isValid(final long sessionId) {\n        try {\n            return sessionService.isValid(sessionId);\n        } catch (final SSessionNotFoundException e) {\n            return false;\n        }\n    }\n\n    private void debugLog(String message) {\n        log.debug(message);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-login/src/main/java/org/bonitasoft/engine/core/login/TechnicalUser.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.login;\n\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Matthieu Chaffotte\n */\n@lombok.Data\n@Component\npublic class TechnicalUser {\n\n    private String userName;\n\n    private String password;\n\n    public TechnicalUser(\n            @Value(\"${bonita.runtime.admin.username}\") final String userName,\n            @Value(\"${bonita.runtime.admin.password}\") final String password) {\n        super();\n        this.userName = userName;\n        this.password = password;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-login/src/test/java/org/bonitasoft/engine/core/login/SecuredLoginServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.login;\n\nimport static java.util.Collections.emptyList;\nimport static java.util.Collections.emptySet;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.fail;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.*;\nimport static org.mockito.Mockito.anyList;\nimport static org.mockito.Mockito.anyMap;\n\nimport java.io.Serializable;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.UUID;\nimport java.util.stream.Collectors;\n\nimport org.bonitasoft.engine.authentication.AuthenticationConstants;\nimport org.bonitasoft.engine.authentication.AuthenticationException;\nimport org.bonitasoft.engine.authentication.GenericAuthenticationService;\nimport org.bonitasoft.engine.authorization.PermissionsBuilder;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.SUserNotFoundException;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.profile.ProfileService;\nimport org.bonitasoft.engine.profile.model.SProfile;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.session.model.SSession;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class SecuredLoginServiceImplTest {\n\n    private static final String TECH_USER_NAME = \"install\";\n    private static final String TECH_USER_PASS = \"install\";\n    private static final Long USER_ID = (long) -1;\n    private SecuredLoginServiceImpl securedLoginServiceImpl;\n    @Mock\n    private GenericAuthenticationService genericAuthenticationService;\n    @Mock\n    private SessionService sessionService;\n    @Mock\n    private IdentityService identityService;\n    @Mock\n    private ProfileService profileService;\n    @Mock\n    private PermissionsBuilder permissionsBuilder;\n\n    @Before\n    public void setUp() throws Exception {\n        securedLoginServiceImpl = new SecuredLoginServiceImpl(genericAuthenticationService, sessionService,\n                identityService, new TechnicalUser(TECH_USER_NAME, TECH_USER_PASS), profileService,\n                permissionsBuilder);\n        //return a session with given arguments\n        when(sessionService.createSession(anyLong(), anyString(), anyBoolean(), anyList(), anySet()))\n                .thenAnswer(invok -> SSession.builder()\n                        .id(UUID.randomUUID().getLeastSignificantBits())\n                        .applicationName(\"myApp\")\n                        .userId(invok.getArgument(0))\n                        .userName(invok.getArgument(1))\n                        .technicalUser(invok.getArgument(2))\n                        .profiles(invok.getArgument(3))\n                        .build());\n    }\n\n    @Test\n    public void testSecuredLoginServiceWithNullCredentials() throws SUserNotFoundException {\n        try {\n            securedLoginServiceImpl.login(null);\n            fail();\n        } catch (final SLoginException e) {\n            assertThat(e.getMessage()).isEqualToIgnoringCase(\"invalid credentials, map is null\");\n        }\n    }\n\n    @Test\n    public void testSecuredLoginServiceWithNullLogin() throws SUserNotFoundException {\n        try {\n            final Map<String, Serializable> credentials = new HashMap<>();\n            securedLoginServiceImpl.login(credentials);\n            fail();\n        } catch (final SLoginException e) {\n            assertThat(e.getMessage()).isEqualToIgnoringCase(\"User name or password is not valid!\");\n        }\n    }\n\n    @Test\n    public void testSecuredLoginServiceWithWrongCredentials() {\n        try {\n            final Map<String, Serializable> credentials = new HashMap<>();\n            final String login = \"login\";\n            final String password = \"password\";\n            credentials.put(AuthenticationConstants.BASIC_USERNAME, login);\n            credentials.put(AuthenticationConstants.BASIC_PASSWORD, password);\n            securedLoginServiceImpl.login(credentials);\n            fail();\n        } catch (final Exception e) {\n            assertThat(e.getMessage()).isEqualToIgnoringCase(\"User name or password is not valid!\");\n        }\n    }\n\n    @Test(expected = SUserNotFoundException.class)\n    public void testSecuredLoginServiceWithUnknownUserThrowSUserNotFoundException() throws Exception {\n        final Map<String, Serializable> credentials = new HashMap<>();\n        final String login = \"login\";\n        final String password = \"password\";\n        credentials.put(AuthenticationConstants.BASIC_USERNAME, login);\n        credentials.put(AuthenticationConstants.BASIC_PASSWORD, password);\n\n        when(genericAuthenticationService.checkUserCredentials(credentials)).thenReturn(login);\n        when(identityService.getUserByUserName(login)).thenThrow(new SUserNotFoundException(login));\n\n        securedLoginServiceImpl.login(credentials);\n    }\n\n    @Test\n    public void testSecuredLoginServiceWithInvalidPlatformCredentials() {\n        final Map<String, Serializable> credentials = new HashMap<>();\n        final String password = \"poutpout\";\n        credentials.put(AuthenticationConstants.BASIC_USERNAME, TECH_USER_NAME);\n        credentials.put(AuthenticationConstants.BASIC_PASSWORD, password);\n        try {\n            securedLoginServiceImpl.login(credentials);\n            fail();\n        } catch (final Exception e) {\n            assertThat(e.getMessage()).isEqualToIgnoringCase(\"User name or password is not valid!\");\n        }\n    }\n\n    @Test\n    public void testSecuredLoginServiceWithInvalidPlatformCredentialsWithGenericAuthenticationService()\n            throws Exception {\n        final Map<String, Serializable> credentials = new HashMap<>();\n        final long userId = -1L;\n        final String login = \"julien\";\n        final String password = \"julien\";\n        credentials.put(AuthenticationConstants.BASIC_USERNAME, login);\n        credentials.put(AuthenticationConstants.BASIC_PASSWORD, password);\n        when(genericAuthenticationService.checkUserCredentials(anyMap())).thenThrow(new AuthenticationException());\n\n        try {\n            securedLoginServiceImpl.login(credentials);\n        } catch (final SLoginException e) {\n            verify(genericAuthenticationService, times(1)).checkUserCredentials(anyMap());\n            verify(sessionService, times(0)).createSession(userId, login, true);\n            assertThat(e).hasRootCauseExactlyInstanceOf(AuthenticationException.class);\n            return;\n        }\n        fail();\n\n    }\n\n    @Test\n    public void testSecuredLoginServiceWithPlatformCredentialsWithGenericAuthenticationService() throws Exception {\n        final Map<String, Serializable> credentials = credentials(TECH_USER_NAME, TECH_USER_PASS);\n        final SSession sSession = mock(SSession.class);\n        when(sessionService.createSession(-1L, TECH_USER_NAME, true, emptyList(), emptySet()))\n                .thenReturn(sSession);\n\n        final SSession sSessionResult = securedLoginServiceImpl.login(credentials);\n\n        verify(genericAuthenticationService, times(0)).checkUserCredentials(anyMap());\n        verify(sessionService, times(1)).createSession(-1L, TECH_USER_NAME, true, emptyList(), emptySet());\n        assertThat(sSessionResult).isSameAs(sSession);\n    }\n\n    @Test\n    public void testSecuredLoginServiceWithPlatformCredentials() throws Exception {\n        final Map<String, Serializable> credentials = credentials(TECH_USER_NAME, TECH_USER_PASS);\n\n        final SSession sSession = mock(SSession.class);\n        when(sessionService.createSession(USER_ID, TECH_USER_NAME, true, emptyList(), emptySet()))\n                .thenReturn(sSession);\n\n        final SSession sSessionResult = securedLoginServiceImpl.login(credentials);\n\n        verify(genericAuthenticationService, never()).checkUserCredentials(credentials);\n        verify(sessionService).createSession(USER_ID, TECH_USER_NAME, true, emptyList(), emptySet());\n        assertThat(sSessionResult).isSameAs(sSession);\n    }\n\n    @Test\n    public void testSecuredLoginServiceWithStandardUserCredentials() throws Exception {\n        final Map<String, Serializable> credentials = credentials(\"julien\", \"julien\");\n\n        final SSession sSession = mock(SSession.class);\n        final SUser sUser = mock(SUser.class);\n        doReturn(true).when(sUser).isEnabled();\n\n        when(sUser.getId()).thenReturn(112345L);\n        when(genericAuthenticationService.checkUserCredentials(credentials)).thenReturn(\"julien\");\n        when(sessionService.createSession(112345L, \"julien\", false, emptyList(), emptySet()))\n                .thenReturn(sSession);\n        when(identityService.getUserByUserName(\"julien\")).thenReturn(sUser);\n\n        final SSession sSessionResult = securedLoginServiceImpl.login(credentials);\n\n        verify(genericAuthenticationService, times(1)).checkUserCredentials(credentials);\n        verify(sessionService, times(1)).createSession(112345L, \"julien\", false, emptyList(), emptySet());\n        assertThat(sSessionResult).isSameAs(sSession);\n    }\n\n    @Test\n    public void should_fail_if_no_password_is_provided() {\n        final Map<String, Serializable> credentials = new HashMap<>();\n        final String password = null;\n        credentials.put(AuthenticationConstants.BASIC_PASSWORD, password);\n        try {\n            securedLoginServiceImpl.login(credentials);\n            fail();\n        } catch (final Exception e) {\n            assertThat(e.getMessage()).isEqualToIgnoringCase(\"User name or password is not valid!\");\n        }\n    }\n\n    @Test\n    public void should_fail_if_credentials_are_empty() {\n        final Map<String, Serializable> credentials = new HashMap<>();\n        try {\n            securedLoginServiceImpl.login(credentials);\n            fail();\n        } catch (final Exception e) {\n            assertThat(e.getMessage()).isEqualToIgnoringCase(\"User name or password is not valid!\");\n        }\n    }\n\n    @Test\n    public void should_fail_if_username_is_blank() throws Exception {\n        havingUser(\"   \", \"password\");\n        try {\n            securedLoginServiceImpl.login(credentials(\"   \", \"password\"));\n            fail();\n        } catch (final Exception e) {\n            assertThat(e.getMessage()).isEqualToIgnoringCase(\"User name or password is not valid!\");\n        }\n    }\n\n    @Test\n    public void should_fail_if_password_does_not_match() throws Exception {\n        havingUser(\"a\", \"password1\");\n        try {\n            securedLoginServiceImpl.login(credentials(\"a\", \"password2\"));\n            fail();\n        } catch (final Exception e) {\n            assertThat(e.getMessage()).isEqualToIgnoringCase(\"User name or password is not valid!\");\n        }\n    }\n\n    @Test\n    public void should_fail_when_given_null_credentials() {\n        try {\n            securedLoginServiceImpl.login(null);\n            fail();\n        } catch (final SLoginException | SUserNotFoundException e) {\n            assertThat(e.getMessage()).isEqualToIgnoringCase(\"invalid credentials, map is null\");\n        }\n    }\n\n    @Test\n    public void should_login_with_technical_user() throws Exception {\n        SSession session = securedLoginServiceImpl.login(credentials(TECH_USER_NAME, TECH_USER_PASS));\n\n        assertThat(session)\n                .hasFieldOrPropertyWithValue(\"userName\", TECH_USER_NAME)\n                .hasFieldOrPropertyWithValue(\"userId\", -1L)\n                .hasFieldOrPropertyWithValue(\"technicalUser\", true);\n    }\n\n    @Test\n    public void should_login_with_existing_user() throws Exception {\n        SUser user = havingUser(\"john\", \"bpm\");\n\n        SSession session = securedLoginServiceImpl.login(credentials(\"john\", \"bpm\"));\n\n        assertThat(session)\n                .hasFieldOrPropertyWithValue(\"userName\", \"john\")\n                .hasFieldOrPropertyWithValue(\"userId\", user.getId())\n                .hasFieldOrPropertyWithValue(\"technicalUser\", false);\n    }\n\n    @Test\n    public void should_fail_if_user_is_disabled() throws Exception {\n        SUser user = havingUser(\"john\", \"bpm\");\n        user.setEnabled(false);\n        try {\n            securedLoginServiceImpl.login(credentials(\"john\", \"bpm\"));\n            fail();\n        } catch (SLoginException e) {\n            assertThat(e.getMessage()).isEqualToIgnoringCase(\"Unable to login : the user is disable.\");\n        }\n    }\n\n    @Test\n    public void should_update_last_connection_date_when_successfully_connected() throws Exception {\n        SUser user = havingUser(\"john\", \"bpm\");\n\n        securedLoginServiceImpl.login(credentials(\"john\", \"bpm\"));\n\n        verify(identityService).updateUser(eq(user),\n                argThat(e -> e.getFields().keySet().equals(Collections.singleton(\"lastConnection\"))));\n    }\n\n    @Test\n    public void should_have_profiles_in_session() throws Exception {\n        SUser user = havingUser(\"myUser\", \"myPass\");\n        doReturn(profiles(\"User\", \"Administrator\")).when(profileService).getProfilesOfUser(user.getId());\n\n        SSession session = securedLoginServiceImpl.login(credentials(\"myUser\", \"myPass\"));\n\n        assertThat(session.getProfiles()).containsExactlyInAnyOrder(\"User\", \"Administrator\");\n    }\n\n    private List<SProfile> profiles(String... profiles) {\n        return Arrays.stream(profiles)\n                .map(this::profile)\n                .collect(Collectors.toList());\n    }\n\n    private SProfile profile(String name) {\n        SProfile sProfile = new SProfile();\n        sProfile.setName(name);\n        return sProfile;\n    }\n\n    private Map<String, Serializable> credentials(String username, String password) {\n        final Map<String, Serializable> credentials = new HashMap<>();\n        credentials.put(AuthenticationConstants.BASIC_USERNAME, username);\n        credentials.put(AuthenticationConstants.BASIC_PASSWORD, password);\n        return credentials;\n    }\n\n    private SUser havingUser(String username, String password) throws Exception {\n        SUser user = new SUser();\n        user.setId(UUID.randomUUID().getLeastSignificantBits());\n        user.setUserName(username);\n        user.setPassword(password);\n        user.setEnabled(true);\n        doReturn(user).when(identityService).getUserByUserName(username);\n\n        doReturn(username).when(genericAuthenticationService)\n                .checkUserCredentials(eq(credentials(username, password)));\n\n        return user;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-parameter/build.gradle",
    "content": "\n\ndependencies {\n    api project(':services:bonita-session')\n    api project(':bpm:bonita-core:bonita-home-server')\n    api project(':services:bonita-commons')\n    api project(':services:bonita-cache')\n    api project(':services:bonita-builder')\n    api project(':services:bonita-persistence')\n    testImplementation libs.mockitoCore\n    testImplementation libs.assertj\n    testImplementation libs.systemRules\n    testRuntimeOnly libs.logback\n\n    annotationProcessor libs.lombok\n    compileOnly libs.lombok\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-parameter/src/main/java/org/bonitasoft/engine/parameter/OrderBy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.parameter;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic enum OrderBy {\n\n    NAME_ASC, NAME_DESC, VALUE_ASC, VALUE_DESC;\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-parameter/src/main/java/org/bonitasoft/engine/parameter/ParameterService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.parameter;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.commons.exceptions.SObjectCreationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\n/**\n * @author Matthieu Chaffotte\n * @since 6.0\n */\npublic interface ParameterService {\n\n    /**\n     * Update specific parameter value in a process\n     *\n     * @param processDefinitionId\n     *        identifier of processDefinition\n     * @param parameterName\n     *        name of the parameter will be updated\n     * @param parameterValue\n     *        new value of the parameter\n     * @throws SParameterNameNotFoundException\n     *         error thrown if no parameter found for the specific parameterName\n     */\n    void update(final long processDefinitionId, final String parameterName, final String parameterValue)\n            throws SParameterNameNotFoundException, SBonitaReadException, SObjectModificationException;\n\n    /**\n     * Merge given parameters with existing ones.\n     * Unknown parameters are ignored.\n     *\n     * @param processDefinitionId\n     *        identifier of processDefinition\n     * @param parameters\n     *        parameters to merge\n     * @throws SBonitaReadException\n     *         error thrown if an error occurred while retrieving the process definition\n     * @throws SObjectModificationException\n     *         error thrown if an error occurred while updating the parameter value\n     */\n    void merge(long processDefinitionId, Map<String, String> parameters)\n            throws SBonitaReadException, SObjectModificationException;\n\n    /**\n     * Store all parameters provided to the specific process in file system\n     *\n     * @param processDefinitionId\n     *        identifier of processDefinition\n     * @param parameters\n     *        parameters will be stored in file system\n     * @throws SParameterProcessNotFoundException\n     *         error thrown if no parameters configuration file found in file system\n     */\n    void addAll(final long processDefinitionId, final Map<String, String> parameters)\n            throws SParameterProcessNotFoundException, SObjectCreationException, SBonitaReadException,\n            SObjectModificationException;\n\n    /**\n     * return all parameters in a map\n     *\n     * @param processDefinitionId\n     */\n    Map<String, String> getAll(long processDefinitionId)\n            throws SParameterProcessNotFoundException, SBonitaReadException;\n\n    /**\n     * Delete all parameters for a specific processDefinition\n     *\n     * @param processDefinitionId ID of processDefinition\n     * @throws SParameterProcessNotFoundException\n     *         error thrown if no parameters configuration file found in file system\n     */\n    void deleteAll(final long processDefinitionId)\n            throws SParameterProcessNotFoundException, SBonitaReadException, SObjectModificationException;\n\n    /**\n     * Get parameters in a specific interval for specific process, this is used for pagination\n     *\n     * @param processDefinitionId\n     *        identifier of processDefinition\n     * @param fromIndex\n     *        index of the record to be retrieved from. First record has index 0\n     * @param numberOfResult\n     *        number of result we want to get. Maximum number of result returned.\n     * @param order\n     *        OrderBy object, contains information to do order\n     * @return a list of SParameter objects\n     * @throws SOutOfBoundException\n     *         error throw if fromIndex >= total size of parameters\n     */\n    List<SParameter> get(final long processDefinitionId, final int fromIndex, final int numberOfResult,\n            final OrderBy order)\n            throws SOutOfBoundException, SBonitaReadException;\n\n    /**\n     * Get parameter by name in specific process\n     *\n     * @param processDefinitionId\n     *        identifier of processDefinition\n     * @param parameterName\n     *        name of parameter\n     * @return the parameter or null if it does not exists\n     */\n    SParameter get(final long processDefinitionId, final String parameterName) throws SBonitaReadException;\n\n    /**\n     * Get a list of parameters will null values in order in specific process\n     *\n     * @param processDefinitionId\n     *        identifier of processDefinition\n     * @param fromIndex\n     *        index of the record to be retrieved from. First record has index 0\n     * @param numberOfResult\n     *        number of result we want to get. Maximum number of result returned.\n     * @param order\n     *        OrderBy object, contains information to do order\n     * @return a list of parameters\n     * @throws SParameterProcessNotFoundException\n     *         error thrown if no parameters configuration file found in file system\n     * @throws SOutOfBoundException\n     *         error throw if fromIndex >= total size of parameters\n     */\n    List<SParameter> getNullValues(final long processDefinitionId, final int fromIndex, final int numberOfResult,\n            final OrderBy order)\n            throws SParameterProcessNotFoundException, SOutOfBoundException, SBonitaReadException;\n\n    /**\n     * Check if the specific process contains null-valued parameter or not.\n     *\n     * @param processDefinitionId The ID of the process definition\n     * @return true if at least one parameter contains a null value, false otherwise.\n     * @throws SParameterProcessNotFoundException\n     */\n    boolean containsNullValues(final long processDefinitionId) throws SBonitaReadException;\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-parameter/src/main/java/org/bonitasoft/engine/parameter/ParameterServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.parameter;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.bpm.bar.ParameterContribution;\nimport org.bonitasoft.engine.commons.exceptions.SObjectCreationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\n\n/**\n * @author Baptiste Mesta\n */\n@Slf4j\npublic class ParameterServiceImpl implements ParameterService {\n\n    private static final String VALUE_KEY = \"value\";\n    private static final String PROCESS_DEFINITION_ID_KEY = \"processDefinitionId\";\n\n    public static final int PAGE_SIZE = 100;\n    public static final String PARAMETER = \"PARAMETER\";\n\n    private final Recorder recorder;\n    private final ReadPersistenceService persistenceService;\n\n    public ParameterServiceImpl(Recorder recorder, ReadPersistenceService persistenceService) {\n        this.recorder = recorder;\n        this.persistenceService = persistenceService;\n\n    }\n\n    @Override\n    public void update(long processDefinitionId, String parameterName, String parameterValue)\n            throws SParameterNameNotFoundException, SBonitaReadException, SObjectModificationException {\n        final SParameter sParameter = get(processDefinitionId, parameterName);\n        if (sParameter == null) {\n            throw new SParameterNameNotFoundException(\n                    String.format(\"No parameter <%s> found in the process <%s>\", parameterName, processDefinitionId));\n        }\n        update(sParameter, parameterValue);\n    }\n\n    void update(SParameter sParameter, String parameterValue) throws SObjectModificationException {\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        descriptor.addField(VALUE_KEY, interpretParameterValue(parameterValue));\n        try {\n            recorder.recordUpdate(UpdateRecord.buildSetFields(sParameter, descriptor), PARAMETER);\n        } catch (SRecorderException e) {\n            throw new SObjectModificationException(e);\n        }\n    }\n\n    public void merge(long processDefinitionId, Map<String, String> parameters)\n            throws SBonitaReadException, SObjectModificationException {\n        for (Entry<String, String> parameter : parameters.entrySet()) {\n            final SParameter sParameter = get(processDefinitionId, parameter.getKey());\n            if (sParameter != null) {\n                update(sParameter, parameters.get(parameter.getKey()));\n            } else {\n                log.debug(\"Parameter <{}> doesn't exist in process definition <{}> and has not been merged.\",\n                        parameter.getKey(), processDefinitionId);\n            }\n        }\n    }\n\n    /**\n     * Handle null values. If input is {@link org.bonitasoft.engine.bpm.bar.ParameterContribution#NULL}, convert it to\n     * <code>null</code>\n     */\n    protected String interpretParameterValue(String s) {\n        return ParameterContribution.NULL.equals(s) ? null : s;\n    }\n\n    @Override\n    public void addAll(long processDefinitionId, Map<String, String> parameters)\n            throws SObjectCreationException, SBonitaReadException, SObjectModificationException {\n        for (Map.Entry<String, String> entry : parameters.entrySet()) {\n            addOrUpdate(processDefinitionId, entry.getKey(), entry.getValue());\n        }\n    }\n\n    void addOrUpdate(long processDefinitionId, String name, String value)\n            throws SObjectCreationException, SBonitaReadException, SObjectModificationException {\n        final SParameter currentParameter = get(processDefinitionId, name);\n        if (currentParameter != null) {\n            update(currentParameter, value);\n        } else {\n            add(processDefinitionId, name, value);\n        }\n    }\n\n    void add(long processDefinitionId, String name, String value) throws SObjectCreationException {\n        final SParameter sParameter = new SParameter(name, interpretParameterValue(value), processDefinitionId);\n        try {\n            recorder.recordInsert(new InsertRecord(sParameter), PARAMETER);\n        } catch (SRecorderException e) {\n            throw new SObjectCreationException(e);\n        }\n    }\n\n    @Override\n    public Map<String, String> getAll(long processDefinitionId)\n            throws SParameterProcessNotFoundException, SBonitaReadException {\n        Map<String, String> parameters = new HashMap<>();\n        int fromIndex = 0;\n        List<SParameter> sParameters;\n        do {\n            sParameters = get(processDefinitionId, fromIndex, PAGE_SIZE, null);\n            for (SParameter sParameter : sParameters) {\n                parameters.put(sParameter.getName(), sParameter.getValue());\n            }\n            fromIndex += PAGE_SIZE;\n\n        } while (sParameters.size() == PAGE_SIZE);\n        return parameters;\n    }\n\n    @Override\n    public void deleteAll(long processDefinitionId)\n            throws SParameterProcessNotFoundException, SBonitaReadException, SObjectModificationException {\n        List<SParameter> toDelete;\n        do {\n            toDelete = get(processDefinitionId, 0, PAGE_SIZE, null);\n            for (SParameter sParameter : toDelete) {\n                try {\n                    recorder.recordDelete(new DeleteRecord(sParameter), PARAMETER);\n                } catch (SRecorderException e) {\n                    throw new SObjectModificationException(e);\n                }\n            }\n        } while (toDelete.size() == PAGE_SIZE);\n    }\n\n    @Override\n    public List<SParameter> get(long processDefinitionId, int fromIndex, int numberOfResult, OrderBy order)\n            throws SBonitaReadException {\n        return persistenceService.selectList(\n                new SelectListDescriptor<>(\"getParameters\",\n                        Collections.singletonMap(PROCESS_DEFINITION_ID_KEY, processDefinitionId),\n                        SParameter.class, new QueryOptions(fromIndex, numberOfResult, getOrderByOptions(order))));\n    }\n\n    @Override\n    public SParameter get(long processDefinitionId, String parameterName) throws SBonitaReadException {\n        final Map<String, Object> parameters = new HashMap<>();\n        parameters.put(PROCESS_DEFINITION_ID_KEY, processDefinitionId);\n        parameters.put(\"name\", parameterName);\n        return persistenceService\n                .selectOne(new SelectOneDescriptor<>(\"getParameterByName\", parameters, SParameter.class));\n    }\n\n    @Override\n    public List<SParameter> getNullValues(long processDefinitionId, int fromIndex, int numberOfResult, OrderBy order)\n            throws SParameterProcessNotFoundException, SBonitaReadException {\n        return persistenceService.selectList(new SelectListDescriptor<>(\"getParametersWithNullValues\",\n                Collections.singletonMap(\n                        PROCESS_DEFINITION_ID_KEY, processDefinitionId),\n                SParameter.class, new QueryOptions(fromIndex, numberOfResult, getOrderByOptions(order))));\n    }\n\n    private List<OrderByOption> getOrderByOptions(OrderBy order) {\n        OrderByType type = OrderByType.ASC;\n        String fieldName = \"name\";\n        if (order != null) {\n            switch (order) {\n                case VALUE_ASC:\n                    fieldName = VALUE_KEY;\n                    break;\n                case VALUE_DESC:\n                    fieldName = VALUE_KEY;\n                    type = OrderByType.DESC;\n                    break;\n                case NAME_DESC:\n                    type = OrderByType.DESC;\n                    break;\n                default:\n            }\n        }\n        return Collections.singletonList(new OrderByOption(SParameter.class, fieldName, type));\n    }\n\n    @Override\n    public boolean containsNullValues(long processDefinitionId) throws SBonitaReadException {\n        return !persistenceService.selectList(new SelectListDescriptor<SParameter>(\"getParametersWithNullValues\",\n                Collections.singletonMap(PROCESS_DEFINITION_ID_KEY, processDefinitionId), SParameter.class,\n                new QueryOptions(0, 1))).isEmpty();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-parameter/src/main/java/org/bonitasoft/engine/parameter/SOutOfBoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.parameter;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SOutOfBoundException extends SBonitaException {\n\n    private static final long serialVersionUID = -2729330464039642649L;\n\n    public SOutOfBoundException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-parameter/src/main/java/org/bonitasoft/engine/parameter/SParameter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.parameter;\n\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.hibernate.annotations.Type;\n\n/**\n * @author Matthieu Chaffotte\n */\n@Data\n@NoArgsConstructor\n@Entity\n@Table(name = \"proc_parameter\")\npublic class SParameter implements PersistentObject {\n\n    @Id\n    private long id;\n\n    private String name;\n    @Column\n    @Type(type = \"materialized_clob\")\n    private String value;\n    @Column(name = \"process_id\")\n    private long processDefinitionId;\n\n    public SParameter(String name, String value, long processDefinitionId) {\n        this.name = name;\n        this.value = value;\n        this.processDefinitionId = processDefinitionId;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-parameter/src/main/java/org/bonitasoft/engine/parameter/SParameterNameNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.parameter;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SParameterNameNotFoundException extends SBonitaException {\n\n    private static final long serialVersionUID = 6019783138024113896L;\n\n    public SParameterNameNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SParameterNameNotFoundException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-parameter/src/main/java/org/bonitasoft/engine/parameter/SParameterProcessNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.parameter;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SParameterProcessNotFoundException extends SBonitaException {\n\n    private static final long serialVersionUID = 946351177446570851L;\n\n    public SParameterProcessNotFoundException(final String message) {\n        super(message);\n    }\n\n    public SParameterProcessNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-parameter/src/main/resources/org/bonitasoft/engine/parameter/parameter.queries.hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!--~\n  ~ Copyright (C) 2015 Bonitasoft S.A.\n  ~ Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n  ~ This library is free software; you can redistribute it and/or modify it under the terms\n  ~ of the GNU Lesser General Public License as published by the Free Software Foundation\n  ~ version 2.1 of the License.\n  ~ This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n  ~ without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n  ~ See the GNU Lesser General Public License for more details.\n  ~ You should have received a copy of the GNU Lesser General Public License along with this\n  ~ program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n  ~ Floor, Boston, MA 02110-1301, USA.\n  ~-->\n\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\" \"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd\">\n<hibernate-mapping auto-import=\"false\">\n\n    <query name=\"getParametersWithNullValues\">\n        SELECT param\n        FROM org.bonitasoft.engine.parameter.SParameter AS param\n        WHERE param.processDefinitionId = :processDefinitionId AND param.value is null\n    </query>\n    <query name=\"getParameterByName\">\n        SELECT param\n        FROM org.bonitasoft.engine.parameter.SParameter AS param\n        WHERE param.processDefinitionId = :processDefinitionId AND param.name = :name\n    </query>\n    <query name=\"getParameters\">\n        SELECT param\n        FROM org.bonitasoft.engine.parameter.SParameter AS param\n        WHERE param.processDefinitionId = :processDefinitionId\n    </query>\n\n</hibernate-mapping>\n"
  },
  {
    "path": "bpm/bonita-core/bonita-parameter/src/test/java/org/bonitasoft/engine/parameter/ParameterServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.parameter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyInt;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.ArgumentMatchers.nullable;\nimport static org.mockito.Mockito.*;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.SystemOutRule;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * author Emmanuel Duchastenier\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class ParameterServiceImplTest {\n\n    @Rule\n    public SystemOutRule systemOutRule = new SystemOutRule().enableLog();\n    @Mock\n    private Recorder recorder;\n    @Mock\n    ReadPersistenceService persistenceService;\n\n    @Captor\n    private ArgumentCaptor<SelectListDescriptor<SParameter>> getSelectDescriptor;\n\n    @InjectMocks\n    @Spy\n    private ParameterServiceImpl parameterService;\n\n    @Test\n    public void update_should_call_internal_update_with_retrieved_existing_value() throws Exception {\n        final String aParam = \"aParam\";\n        final long processDefinitionId = 1544878L;\n        final SParameter sParameter = new SParameter(aParam, \"value\", processDefinitionId);\n        doReturn(sParameter).when(parameterService).get(processDefinitionId, aParam);\n\n        final String newValue = \"newValue\";\n        parameterService.update(processDefinitionId, aParam, newValue);\n\n        verify(parameterService).update(sParameter, newValue);\n    }\n\n    @Test\n    public void update_should_convert_parameter_value() throws Exception {\n        final String newValue = \"newValue\";\n\n        parameterService.update(new SParameter(\"parameter\", \"defaultValue\", 9874654654L), newValue);\n\n        verify(parameterService).interpretParameterValue(newValue);\n    }\n\n    @Test\n    public void add_should_convert_parameter_value() throws Exception {\n        parameterService.add(9874654654L, \"parameter\", \"defaultValue\");\n\n        verify(parameterService).interpretParameterValue(\"defaultValue\");\n    }\n\n    @Test\n    public void interpretParameterValue_should_convert_NULL_VALUES() {\n        assertThat(parameterService.interpretParameterValue(\"\")).isEqualTo(\"\");\n        assertThat(parameterService.interpretParameterValue(null)).isEqualTo(null);\n        assertThat(parameterService.interpretParameterValue(\"someValue\")).isEqualTo(\"someValue\");\n        assertThat(parameterService.interpretParameterValue(\"-==NULLL==-\")).isEqualTo(null);\n    }\n\n    @Test\n    public void update_should_call_recordUpdate_on_recorder() throws Exception {\n        parameterService.update(mock(SParameter.class), \"value\");\n        verify(recorder).recordUpdate(any(UpdateRecord.class), nullable(String.class));\n    }\n\n    @Test(expected = SParameterNameNotFoundException.class)\n    public void updateNonExistingParameter() throws Exception {\n        parameterService.update(123L, \"aParam\", \"newValue\");\n    }\n\n    @Test\n    public void addAll_should_call_record_for_all_parameters() throws Exception {\n        HashMap<String, String> parameters = new HashMap<>(3);\n        parameters.put(\"param1\", \"value1\");\n        parameters.put(\"param2\", \"value2\");\n        parameters.put(\"param3\", \"value3\");\n        parameterService.addAll(123L, parameters);\n        verify(recorder, times(3)).recordInsert(any(InsertRecord.class), nullable(String.class));\n    }\n\n    @Test\n    public void deleteAll_should_call_recordDelete() throws Exception {\n        final long processDefinitionId = 123L;\n        doReturn(Collections.singletonList(new SParameter())).when(parameterService).get(eq(processDefinitionId),\n                anyInt(), anyInt(), nullable(OrderBy.class));\n        parameterService.deleteAll(processDefinitionId);\n        verify(recorder).recordDelete(any(DeleteRecord.class), nullable(String.class));\n    }\n\n    @Test\n    public void containsNullValues_should_return_true_for_non_empty_persistenceService_result() throws Exception {\n        final ArrayList<SParameter> toBeReturned = new ArrayList<>(1);\n        toBeReturned.add(new SParameter());\n        doReturn(toBeReturned).when(persistenceService).selectList(any());\n        assertThat(parameterService.containsNullValues(123L)).isTrue();\n    }\n\n    @Test\n    public void containsNullValues_should_return_false_for_empty_persistenceService_result() throws Exception {\n        doReturn(Collections.emptyList()).when(persistenceService).selectList(any());\n        assertThat(parameterService.containsNullValues(123L)).isFalse();\n    }\n\n    @Test\n    public void getParameter_should_call_persistenceService() throws Exception {\n        parameterService.get(123L, \"aParam\");\n        verify(persistenceService).selectOne(any(SelectOneDescriptor.class));\n    }\n\n    @Test\n    public void getNonExistingParameter_should_return_null() throws Exception {\n        assertThat(parameterService.get(123L, \"aParam\")).isNull();\n    }\n\n    @Test\n    public void getParameters_should_call_persistenceService_respecting_given_order() throws Exception {\n        parameterService.get(123L, 0, 10, OrderBy.NAME_DESC);\n        verify(persistenceService).selectList(getSelectDescriptor.capture());\n        assertThat(getSelectDescriptor.getValue().getQueryOptions().getOrderByOptions().get(0))\n                .isEqualTo(new OrderByOption(SParameter.class, \"name\", OrderByType.DESC));\n    }\n\n    @Test\n    public void getParameters_should_use_pagination() throws Exception {\n        parameterService.get(16546235L, 2, 7, OrderBy.NAME_ASC);\n        verify(persistenceService).selectList(getSelectDescriptor.capture());\n        assertThat(getSelectDescriptor.getValue().getQueryOptions().getFromIndex()).isEqualTo(2);\n        assertThat(getSelectDescriptor.getValue().getQueryOptions().getNumberOfResults()).isEqualTo(7);\n    }\n\n    @Test\n    public void getNullValues_should_call_persistenceService_query_getParametersWithNullValues() throws Exception {\n        final long processDefinitionId = 1561654L;\n        parameterService.getNullValues(processDefinitionId, 0, 12, OrderBy.NAME_ASC);\n        verify(persistenceService).selectList(getSelectDescriptor.capture());\n        assertThat(getSelectDescriptor.getValue().getQueryName()).isEqualTo(\"getParametersWithNullValues\");\n    }\n\n    @Test\n    public void addOrUpdate_should_update_if_parameter_already_exists() throws Exception {\n        final String paramName = \"paramName\";\n        final long processDefinitionId = 5457878L;\n        final String paramValue = \"paramValue\";\n        final SParameter sParameter = mock(SParameter.class);\n        doReturn(sParameter).when(parameterService).get(processDefinitionId, paramName);\n        parameterService.addOrUpdate(processDefinitionId, paramName, paramValue);\n        verify(parameterService).update(sParameter, paramValue);\n    }\n\n    @Test\n    public void getAll_should_get_paginated_method() throws Exception {\n        final long processDefinitionId = 4656556L;\n        parameterService.getAll(processDefinitionId);\n        verify(parameterService).get(eq(processDefinitionId), eq(0), anyInt(), nullable(OrderBy.class));\n    }\n\n    @Test\n    public void should_merge_parameters_with_existing_parameters() throws Exception {\n        final long processDefinitionId = 4656556L;\n        SParameter hostParameter = sParameter(\"host\", \"localhost\", processDefinitionId);\n        doReturn(hostParameter).when(parameterService).get(processDefinitionId, \"host\");\n\n        Map<String, String> parameters = new HashMap<>();\n        parameters.put(\"host\", \"192.168.0.1\");\n        parameters.put(\"password\", \"kittycat\");\n        systemOutRule.clearLog();\n\n        parameterService.merge(processDefinitionId, parameters);\n\n        verify(parameterService).update(hostParameter, \"192.168.0.1\");\n        verify(parameterService, never()).add(processDefinitionId, \"password\", \"kittycat\");\n        assertThat(systemOutRule.getLog()).contains(\n                \"Parameter <password> doesn't exist in process definition <4656556> and has not been merged.\");\n    }\n\n    private SParameter sParameter(String name, String value, long processDefinitionId) {\n        return new SParameter(name, value, processDefinitionId);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-platform-login/build.gradle",
    "content": "\n\ndependencies {\n    api project(':services:bonita-commons')\n    api project(':services:bonita-platform-session')\n    api project(':services:bonita-platform-authentication')\n    testImplementation libs.mockitoCore\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-platform-login/src/main/java/org/bonitasoft/engine/core/platform/login/PlatformLoginService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.platform.login;\n\nimport org.bonitasoft.engine.platform.session.SSessionNotFoundException;\nimport org.bonitasoft.engine.platform.session.model.SPlatformSession;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @since 6.0\n */\npublic interface PlatformLoginService {\n\n    /**\n     * login to the platform by userName and password\n     *\n     * @param userName\n     *        name of user\n     * @param password\n     *        password of user\n     * @return an SPlatformSession object\n     * @see SPlatformSession\n     * @throws SPlatformLoginException\n     */\n    SPlatformSession login(String userName, String password)\n            throws SPlatformLoginException, SInvalidPlatformCredentialsException;\n\n    /**\n     * logout the platform by sessionId\n     *\n     * @param sessionId\n     *        identifier of platform session\n     * @throws SSessionNotFoundException\n     */\n    void logout(final long sessionId) throws SSessionNotFoundException;\n\n    /**\n     * Verify if a session is valid\n     *\n     * @param sessionId\n     *        identifier of platform session\n     * @return true if session is valid, false otherwise.\n     */\n    boolean isValid(final long sessionId);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-platform-login/src/main/java/org/bonitasoft/engine/core/platform/login/SInvalidPlatformCredentialsException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.platform.login;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SInvalidPlatformCredentialsException extends SBonitaException {\n\n    public SInvalidPlatformCredentialsException(Throwable e) {\n        super(e);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-platform-login/src/main/java/org/bonitasoft/engine/core/platform/login/SPlatformLoginException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.platform.login;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SPlatformLoginException extends SBonitaException {\n\n    private static final long serialVersionUID = 4420091633702041899L;\n\n    public SPlatformLoginException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-platform-login/src/main/java/org/bonitasoft/engine/core/platform/login/impl/PlatformLoginServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.platform.login.impl;\n\nimport org.bonitasoft.engine.core.platform.login.PlatformLoginService;\nimport org.bonitasoft.engine.core.platform.login.SInvalidPlatformCredentialsException;\nimport org.bonitasoft.engine.core.platform.login.SPlatformLoginException;\nimport org.bonitasoft.engine.platform.authentication.PlatformAuthenticationService;\nimport org.bonitasoft.engine.platform.authentication.SInvalidPasswordException;\nimport org.bonitasoft.engine.platform.authentication.SInvalidUserException;\nimport org.bonitasoft.engine.platform.session.PlatformSessionService;\nimport org.bonitasoft.engine.platform.session.SSessionException;\nimport org.bonitasoft.engine.platform.session.SSessionNotFoundException;\nimport org.bonitasoft.engine.platform.session.model.SPlatformSession;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Feng Hui\n * @author Matthieu Chaffotte\n */\npublic class PlatformLoginServiceImpl implements PlatformLoginService {\n\n    private final PlatformAuthenticationService authenticationService;\n\n    private final PlatformSessionService sessionService;\n\n    public PlatformLoginServiceImpl(final PlatformAuthenticationService platformAuthenticationService,\n            final PlatformSessionService platformSessionService) {\n        authenticationService = platformAuthenticationService;\n        sessionService = platformSessionService;\n    }\n\n    @Override\n    public SPlatformSession login(final String userName, final String password)\n            throws SPlatformLoginException, SInvalidPlatformCredentialsException {\n        try {\n            authenticationService.checkUserCredentials(userName, password);\n            return sessionService.createSession(userName);\n        } catch (final SInvalidUserException | SInvalidPasswordException e) {\n            throw new SInvalidPlatformCredentialsException(e);\n        } catch (final SSessionException e) {\n            throw new SPlatformLoginException(e);\n        }\n    }\n\n    @Override\n    public void logout(final long sessionId) throws SSessionNotFoundException {\n        sessionService.deleteSession(sessionId);\n    }\n\n    @Override\n    public boolean isValid(final long sessionId) {\n        try {\n            return sessionService.isValid(sessionId);\n        } catch (final SSessionNotFoundException ssnfe) {\n            return false;\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-platform-login/src/test/java/org/bonitasoft/engine/core/platform/login/impl/PlatformLoginServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.platform.login.impl;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.Mockito.doNothing;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.doThrow;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport org.bonitasoft.engine.core.platform.login.SInvalidPlatformCredentialsException;\nimport org.bonitasoft.engine.core.platform.login.SPlatformLoginException;\nimport org.bonitasoft.engine.platform.authentication.PlatformAuthenticationService;\nimport org.bonitasoft.engine.platform.authentication.SInvalidPasswordException;\nimport org.bonitasoft.engine.platform.authentication.SInvalidUserException;\nimport org.bonitasoft.engine.platform.session.PlatformSessionService;\nimport org.bonitasoft.engine.platform.session.SSessionException;\nimport org.bonitasoft.engine.platform.session.SSessionNotFoundException;\nimport org.bonitasoft.engine.platform.session.model.SPlatformSession;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\n/**\n * @author Celine Souchet\n */\npublic class PlatformLoginServiceImplTest {\n\n    @Mock\n    private PlatformAuthenticationService authenticationService;\n\n    @Mock\n    private PlatformSessionService sessionService;\n\n    @InjectMocks\n    private PlatformLoginServiceImpl platformLoginServiceImpl;\n\n    @Before\n    public void setUp() {\n        MockitoAnnotations.initMocks(this);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.platform.login.impl.PlatformLoginServiceImpl#login(java.lang.String, java.lang.String)}.\n     */\n    @Test\n    public final void login() throws Exception {\n        final String userName = \"userName\";\n        final String password = \"pwd\";\n        doNothing().when(authenticationService).checkUserCredentials(userName, password);\n        final SPlatformSession sPlatformSession = mock(SPlatformSession.class);\n        doReturn(sPlatformSession).when(sessionService).createSession(userName);\n\n        assertEquals(sPlatformSession, platformLoginServiceImpl.login(userName, password));\n    }\n\n    @Test(expected = SInvalidPlatformCredentialsException.class)\n    public final void loginThrowSInvalidUserException() throws Exception {\n        final String userName = \"userName\";\n        final String password = \"pwd\";\n        doThrow(new SInvalidUserException(\"\")).when(authenticationService).checkUserCredentials(userName, password);\n\n        platformLoginServiceImpl.login(userName, password);\n    }\n\n    @Test(expected = SInvalidPlatformCredentialsException.class)\n    public final void loginThrowSInvalidPasswordException() throws Exception {\n        final String userName = \"userName\";\n        final String password = \"pwd\";\n        doThrow(new SInvalidPasswordException(\"\")).when(authenticationService).checkUserCredentials(userName, password);\n\n        platformLoginServiceImpl.login(userName,\n                password);\n    }\n\n    @Test(expected = SPlatformLoginException.class)\n    public final void loginThrowSSessionException() throws Exception {\n        final String userName = \"userName\";\n        final String password = \"pwd\";\n        doNothing().when(authenticationService).checkUserCredentials(userName, password);\n        final SPlatformSession sPlatformSession = mock(SPlatformSession.class);\n        when(sessionService.createSession(userName)).thenReturn(sPlatformSession);\n        doThrow(new SSessionException(\"\")).when(sessionService).createSession(userName);\n\n        assertEquals(sPlatformSession, platformLoginServiceImpl.login(userName, password));\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.core.platform.login.impl.PlatformLoginServiceImpl#logout(long)}.\n     */\n    @Test\n    public final void logout() throws SSessionNotFoundException {\n        final long sessionId = 415L;\n        doNothing().when(sessionService).deleteSession(sessionId);\n\n        platformLoginServiceImpl.logout(sessionId);\n    }\n\n    @Test(expected = SSessionNotFoundException.class)\n    public final void logoutThrowSSessionException() throws SSessionNotFoundException {\n        final long sessionId = 415L;\n        doThrow(new SSessionNotFoundException(\"\")).when(sessionService).deleteSession(sessionId);\n\n        platformLoginServiceImpl.logout(sessionId);\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.core.platform.login.impl.PlatformLoginServiceImpl#isValid(long)}.\n     */\n    @Test\n    public final void isValid() throws SSessionNotFoundException {\n        final long sessionId = 415L;\n        doReturn(true).when(sessionService).isValid(sessionId);\n\n        assertTrue(platformLoginServiceImpl.isValid(sessionId));\n    }\n\n    @Test\n    public final void isNotValid() throws SSessionNotFoundException {\n        final long sessionId = 415L;\n        doReturn(false).when(sessionService).isValid(sessionId);\n\n        assertFalse(platformLoginServiceImpl.isValid(sessionId));\n    }\n\n    @Test\n    public final void isValidThrowException() throws SSessionNotFoundException {\n        final long sessionId = 415L;\n        doThrow(new SSessionNotFoundException(\"\")).when(sessionService).isValid(sessionId);\n\n        assertFalse(platformLoginServiceImpl.isValid(sessionId));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-comment/build.gradle",
    "content": "\n\ndependencies {\n    api project(':services:bonita-session')\n    api project(':services:bonita-commons')\n    api project(':services:bonita-builder')\n    api project(':services:bonita-events')\n    api project(':services:bonita-log')\n    api project(':services:bonita-archive')\n    api project(':services:bonita-persistence')\n\n    annotationProcessor libs.lombok\n    compileOnly libs.lombok\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-comment/src/main/java/org/bonitasoft/engine/core/process/comment/api/SCommentAddException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.comment.api;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Hongwen Zang\n * @author Celine Souchet\n */\npublic class SCommentAddException extends SBonitaException {\n\n    private static final long serialVersionUID = 6387069363737827464L;\n\n    public SCommentAddException(String message, Exception cause) {\n        super(message, cause);\n    }\n\n    public SCommentAddException(String message) {\n        super(message);\n    }\n\n    public SCommentAddException(Exception cause) {\n        super(cause);\n    }\n\n    public SCommentAddException(long processInstanceId, final String commentType, final Exception e) {\n        super(\"Can't create a \" + commentType + \" comment on the process instance.\", e);\n        setProcessInstanceIdOnContext(processInstanceId);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-comment/src/main/java/org/bonitasoft/engine/core/process/comment/api/SCommentDeletionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.comment.api;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SCommentDeletionException extends SBonitaException {\n\n    private static final long serialVersionUID = -7042764134896151527L;\n\n    public SCommentDeletionException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SCommentDeletionException(final String message) {\n        super(message);\n    }\n\n    public SCommentDeletionException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-comment/src/main/java/org/bonitasoft/engine/core/process/comment/api/SCommentException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.comment.api;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Hongwen Zang\n */\npublic class SCommentException extends SBonitaException {\n\n    private static final long serialVersionUID = -6492733973957442676L;\n\n    public SCommentException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n    public SCommentException(String message) {\n        super(message);\n    }\n\n    public SCommentException(Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-comment/src/main/java/org/bonitasoft/engine/core/process/comment/api/SCommentNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.comment.api;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Hongwen Zang\n */\npublic class SCommentNotFoundException extends SBonitaException {\n\n    private static final long serialVersionUID = -8268354026852102242L;\n\n    public SCommentNotFoundException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SCommentNotFoundException(final String message) {\n        super(message);\n    }\n\n    public SCommentNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-comment/src/main/java/org/bonitasoft/engine/core/process/comment/api/SCommentService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.comment.api;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.core.process.comment.model.SComment;\nimport org.bonitasoft.engine.core.process.comment.model.archive.SAComment;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\n/**\n * @author Hongwen Zang\n * @author Zhang Bole\n * @author Matthieu Chaffotte\n * @since 6.0\n */\npublic interface SCommentService {\n\n    String COMMENT = \"COMMENT\";\n\n    String COMMMENT_IS_DELETED = \"deleting a comment\";\n\n    /**\n     * List all comments related to the specified query options.\n     *\n     * @param options\n     *        a QueryOptions object, containing some query conditions\n     * @return a list of SComment objects corresponding to the criteria\n     * @throws SBonitaReadException\n     */\n    List<SComment> searchComments(QueryOptions options) throws SBonitaReadException;\n\n    /**\n     * Number of all comments related to the specified query options.\n     *\n     * @param queryOptions\n     *        a QueryOptions object, containing some query conditions\n     * @return number of all comments corresponding to the criteria.\n     * @throws SBonitaReadException\n     */\n    long getNumberOfComments(QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * Add a comment on process instance\n     *\n     * @param processInstanceId\n     *        identifier of processInstance\n     * @param comment\n     *        the comment you want to add\n     * @param userId\n     *        The user that will be said to have made the comment\n     * @throws SCommentAddException\n     */\n    SComment addComment(long processInstanceId, String comment, long userId) throws SCommentAddException;\n\n    /**\n     * Add a system comment on process instance\n     *\n     * @param processInstanceId\n     *        identifier of processInstance\n     * @param comment\n     *        the comment you want to add\n     * @throws SCommentAddException\n     */\n    SComment addSystemComment(long processInstanceId, String comment) throws SCommentAddException;\n\n    /**\n     * Get comments for the given processInstance\n     *\n     * @param processInstanceId\n     *        identifier of processInstance\n     * @param queryOptions\n     *        the query options to filter the results\n     * @return a list of SComment object\n     * @throws SBonitaReadException\n     *         in case of read error\n     * @since 6.1\n     */\n    List<SComment> getComments(long processInstanceId, QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * Search number of Comment for a specific supervisor\n     *\n     * @param supervisorId\n     *        the identifier of supervisor\n     * @param queryOptions\n     *        a map of specific parameters of a query\n     * @return number of Comment for a specific supervisor\n     * @throws SBonitaReadException\n     *         if a Read exception occurs\n     */\n    long getNumberOfCommentsSupervisedBy(long supervisorId, QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * Search an Comment for a specific supervisor\n     *\n     * @param supervisorId\n     *        the identifier of supervisor\n     * @param queryOptions\n     *        a map of specific parameters of a query\n     * @return an Comment for a specific supervisor\n     * @throws SBonitaReadException\n     *         if a Read exception occurs\n     */\n    List<SComment> searchCommentsSupervisedBy(long supervisorId, QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * Search number of all the comments on process instants that the user can access\n     *\n     * @param userId\n     *        identifier of user\n     * @param searchOptions\n     *        a QueryOptions object, containing some query conditions\n     * @return number of all the comments on process instants that the user can access\n     * @throws SBonitaReadException\n     */\n    long getNumberOfCommentsInvolvingUser(long userId, QueryOptions searchOptions) throws SBonitaReadException;\n\n    /**\n     * List all the comments on process instants that the user can access\n     *\n     * @param userId\n     *        identifier of user\n     * @param queryOptions\n     *        a QueryOptions object, containing some query conditions\n     * @return a list of comments on process instants that the user can access\n     * @throws SBonitaReadException\n     */\n    List<SComment> searchCommentsInvolvingUser(long userId, QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * Search number of the comments visible by delegates of managerUserId\n     *\n     * @param managerUserId\n     *        identifier of a manager user\n     * @param searchOptions\n     *        a QueryOptions object, containing some query conditions\n     * @return number of the comments visible by delegates of managerUserId\n     * @throws SBonitaReadException\n     */\n    long getNumberOfCommentsManagedBy(long managerUserId, QueryOptions searchOptions) throws SBonitaReadException;\n\n    /**\n     * Search comments visible by delegates of managerUserId\n     *\n     * @param managerUserId\n     *        identifier of a manager user\n     * @param searchOptions\n     *        a QueryOptions object, containing some query conditions\n     * @return a list of comments visible by delegates of managerUserId\n     * @throws SBonitaReadException\n     */\n    List<SComment> searchCommentsManagedBy(long managerUserId, QueryOptions searchOptions) throws SBonitaReadException;\n\n    /**\n     * Search number of archived Comments\n     *\n     * @param searchOptions\n     * @throws SBonitaReadException\n     */\n    long getNumberOfArchivedComments(QueryOptions searchOptions) throws SBonitaReadException;\n\n    /**\n     * Search archived Comments\n     *\n     * @param searchOptions\n     *        a QueryOptions object, containing some query conditions\n     * @return a list with archived Comments\n     * @throws SBonitaReadException\n     */\n    List<SAComment> searchArchivedComments(QueryOptions searchOptions) throws SBonitaReadException;\n\n    /**\n     * Returning true if the system comments are enabled for the specific SystemCommentType.\n     *\n     * @param sct\n     *        A Enum for system comments\n     * @return Returning true if the system comments are enabled for the specific SystemCommentType.\n     * @since 6.0\n     */\n    boolean isCommentEnabled(SystemCommentType sct);\n\n    /**\n     * Delete the comment\n     *\n     * @param comment\n     * @throws SCommentDeletionException\n     */\n    void delete(SComment comment) throws SCommentDeletionException;\n\n    /**\n     * @param archivedCommentId\n     * @throws SCommentNotFoundException\n     * @throws SBonitaReadException\n     */\n    SAComment getArchivedComment(long archivedCommentId) throws SCommentNotFoundException, SBonitaReadException;\n\n    /**\n     * Delete archived comments for specified process instances\n     *\n     * @param processInstanceIds\n     */\n    void deleteArchivedComments(List<Long> processInstanceIds) throws SBonitaException;\n\n    /**\n     * Delete comments for a specified process instance\n     *\n     * @param processInstanceId\n     * @throws SBonitaException\n     * @since 6.1\n     */\n    void deleteComments(long processInstanceId) throws SBonitaException;\n\n    /**\n     * @param archiveDate\n     * @param sComment\n     * @throws SObjectModificationException\n     * @since 6.4.0\n     */\n    void archive(long archiveDate, SComment sComment) throws SObjectModificationException;\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-comment/src/main/java/org/bonitasoft/engine/core/process/comment/api/SystemCommentType.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.comment.api;\n\n/**\n * @author Hongwen Zang\n */\npublic enum SystemCommentType {\n    STATE_CHANGE,\n    // PRIORITY_CHANGE,\n    // DUE_DATE_CHANGE\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-comment/src/main/java/org/bonitasoft/engine/core/process/comment/api/impl/SCommentServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.comment.api.impl;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.archive.ArchiveInsertRecord;\nimport org.bonitasoft.engine.archive.ArchiveService;\nimport org.bonitasoft.engine.commons.NullCheckingUtil;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.core.process.comment.api.SCommentAddException;\nimport org.bonitasoft.engine.core.process.comment.api.SCommentDeletionException;\nimport org.bonitasoft.engine.core.process.comment.api.SCommentNotFoundException;\nimport org.bonitasoft.engine.core.process.comment.api.SCommentService;\nimport org.bonitasoft.engine.core.process.comment.api.SystemCommentType;\nimport org.bonitasoft.engine.core.process.comment.model.SComment;\nimport org.bonitasoft.engine.core.process.comment.model.SHumanComment;\nimport org.bonitasoft.engine.core.process.comment.model.SSystemComment;\nimport org.bonitasoft.engine.core.process.comment.model.archive.SAComment;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor;\n\n/**\n * @author Hongwen Zang\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class SCommentServiceImpl implements SCommentService {\n\n    private static final String SUPERVISED_BY = \"SupervisedBy\";\n\n    private static final String INVOLVING_USER = \"InvolvingUser\";\n\n    private static final String MANAGED_BY = \"ManagedBy\";\n\n    private final Recorder recorder;\n\n    private final ReadPersistenceService persistenceService;\n\n    private final SessionService sessionService;\n\n    private final ReadSessionAccessor sessionAccessor;\n\n    private final Map<SystemCommentType, Boolean> systemCommentType;\n\n    private final ArchiveService archiveService;\n\n    public SCommentServiceImpl(final Recorder recorder, final ReadPersistenceService persistenceService,\n            final ArchiveService archiveService,\n            final SessionService sessionService, final ReadSessionAccessor sessionAccessor,\n            final Map<SystemCommentType, Boolean> systemCommentType) {\n        super();\n        this.recorder = recorder;\n        this.persistenceService = persistenceService;\n        this.sessionService = sessionService;\n        this.sessionAccessor = sessionAccessor;\n        this.systemCommentType = systemCommentType;\n        this.archiveService = archiveService;\n    }\n\n    @Override\n    public List<SComment> searchComments(final QueryOptions options) throws SBonitaReadException {\n        return persistenceService.searchEntity(SComment.class, options, null);\n    }\n\n    @Override\n    public long getNumberOfComments(final QueryOptions options) throws SBonitaReadException {\n        return persistenceService.getNumberOfEntities(SComment.class, options, null);\n    }\n\n    @Override\n    public List<SComment> getComments(final long processInstanceId, final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"processInstanceId\",\n                (Object) processInstanceId);\n        final SelectListDescriptor<SComment> selectDescriptor = new SelectListDescriptor<SComment>(\"getSComments\",\n                parameters, SComment.class, queryOptions);\n        return persistenceService.selectList(selectDescriptor);\n    }\n\n    @Override\n    public SComment addComment(long processInstanceId, String comment, long userId) throws SCommentAddException {\n        NullCheckingUtil.checkArgsNotNull(processInstanceId);\n        NullCheckingUtil.checkArgsNotNull(comment);\n        try {\n            final SComment sComment = new SHumanComment(processInstanceId, comment, userId);\n            recorder.recordInsert(new InsertRecord(sComment), COMMENT);\n            return sComment;\n        } catch (final SRecorderException e) {\n            throw new SCommentAddException(processInstanceId, \"human\", e);\n        }\n    }\n\n    @Override\n    public SComment addSystemComment(final long processInstanceId, final String comment) throws SCommentAddException {\n        NullCheckingUtil.checkArgsNotNull(processInstanceId);\n        NullCheckingUtil.checkArgsNotNull(comment);\n        try {\n            final SComment sComment = new SSystemComment(processInstanceId, comment);\n            recorder.recordInsert(new InsertRecord(sComment), COMMENT);\n            return sComment;\n        } catch (final SRecorderException e) {\n            throw new SCommentAddException(processInstanceId, \"system\", e);\n        }\n    }\n\n    @Override\n    public void delete(final SComment comment) throws SCommentDeletionException {\n        NullCheckingUtil.checkArgsNotNull(comment);\n        try {\n            recorder.recordDelete(new DeleteRecord(comment), COMMENT);\n        } catch (final SRecorderException e) {\n            throw new SCommentDeletionException(\"Can't delete the comment \" + comment, e);\n        }\n    }\n\n    @Override\n    public void deleteComments(final long processInstanceId) throws SBonitaException {\n        final QueryOptions queryOptions = new QueryOptions(0, 100, SComment.class, \"id\", OrderByType.ASC);\n\n        List<SComment> sComments = null;\n        do {\n            sComments = getComments(processInstanceId, queryOptions);\n            if (sComments != null) {\n                for (final SComment sComment : sComments) {\n                    delete(sComment);\n                }\n            }\n        } while (sComments != null && sComments.size() > 0);\n    }\n\n    private long getUserId() {\n        return sessionService.getLoggedUserFromSession(sessionAccessor);\n    }\n\n    @Override\n    public long getNumberOfCommentsSupervisedBy(final long supervisorId, final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        try {\n            final Map<String, Object> parameters = Collections.singletonMap(\"supervisorId\", (Object) supervisorId);\n            return persistenceService.getNumberOfEntities(SComment.class, SUPERVISED_BY, queryOptions, parameters);\n        } catch (final SBonitaReadException bre) {\n            throw new SBonitaReadException(bre);\n        }\n    }\n\n    @Override\n    public List<SComment> searchCommentsSupervisedBy(final long supervisorId, final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"supervisorId\", (Object) supervisorId);\n        return persistenceService.searchEntity(SComment.class, SUPERVISED_BY, queryOptions, parameters);\n    }\n\n    @Override\n    public long getNumberOfCommentsInvolvingUser(final long userId, final QueryOptions searchOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"userId\", (Object) userId);\n        return persistenceService.getNumberOfEntities(SComment.class, INVOLVING_USER, searchOptions, parameters);\n    }\n\n    @Override\n    public List<SComment> searchCommentsInvolvingUser(final long userId, final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"userId\", (Object) userId);\n        return persistenceService.searchEntity(SComment.class, INVOLVING_USER, queryOptions, parameters);\n    }\n\n    @Override\n    public long getNumberOfCommentsManagedBy(final long managerUserId, final QueryOptions searchOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"managerUserId\", (Object) managerUserId);\n        return persistenceService.getNumberOfEntities(SComment.class, MANAGED_BY, searchOptions, parameters);\n    }\n\n    @Override\n    public List<SComment> searchCommentsManagedBy(final long managerUserId, final QueryOptions searchOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"managerUserId\", (Object) managerUserId);\n        return persistenceService.searchEntity(SComment.class, MANAGED_BY, searchOptions, parameters);\n    }\n\n    @Override\n    public long getNumberOfArchivedComments(final QueryOptions searchOptions) throws SBonitaReadException {\n        final ReadPersistenceService persistenceService = archiveService.getDefinitiveArchiveReadPersistenceService();\n        return persistenceService.getNumberOfEntities(SAComment.class, searchOptions, null);\n    }\n\n    @Override\n    public List<SAComment> searchArchivedComments(final QueryOptions searchOptions) throws SBonitaReadException {\n        final ReadPersistenceService persistenceService = archiveService.getDefinitiveArchiveReadPersistenceService();\n        return persistenceService.searchEntity(SAComment.class, searchOptions, null);\n    }\n\n    @Override\n    public boolean isCommentEnabled(final SystemCommentType sct) {\n        if (systemCommentType.containsKey(sct)) {\n            return systemCommentType.get(sct);\n        }\n        return false;\n    }\n\n    @Override\n    public SAComment getArchivedComment(final long archivedCommentId)\n            throws SCommentNotFoundException, SBonitaReadException {\n        final ReadPersistenceService persistenceService = archiveService.getDefinitiveArchiveReadPersistenceService();\n        final SAComment selectById = persistenceService.selectById(new SelectByIdDescriptor<SAComment>(SAComment.class,\n                archivedCommentId));\n        if (selectById == null) {\n            throw new SCommentNotFoundException(\"Archived comment not found with id=\" + archivedCommentId);\n        }\n        return selectById;\n    }\n\n    @Override\n    public void deleteArchivedComments(List<Long> processInstanceIds) throws SBonitaException {\n        archiveService.deleteFromQuery(\"deleteArchiveCommentsOfProcessInstances\",\n                Collections.singletonMap(\"processInstanceIds\", processInstanceIds));\n    }\n\n    @Override\n    public void archive(final long archiveDate, final SComment sComment) throws SObjectModificationException {\n        final ArchiveInsertRecord insertRecord = new ArchiveInsertRecord(new SAComment(sComment));\n        try {\n            archiveService.recordInsert(archiveDate, insertRecord);\n        } catch (final SRecorderException e) {\n            throw new SObjectModificationException(\"Unable to archive the comment with id = <\" + sComment.getId() + \">\",\n                    e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-comment/src/main/java/org/bonitasoft/engine/core/process/comment/model/SComment.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.comment.model;\n\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorColumn;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Inheritance;\nimport javax.persistence.InheritanceType;\nimport javax.persistence.Table;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Data\n@NoArgsConstructor\n@Entity\n@Inheritance(strategy = InheritanceType.SINGLE_TABLE)\n@DiscriminatorColumn(name = \"kind\")\n@Table(name = \"process_comment\")\npublic class SComment implements PersistentObject {\n\n    public static final String ID_KEY = \"id\";\n    public static final String USERID_KEY = \"userId\";\n    public static final String PROCESSINSTANCEID_KEY = \"processInstanceId\";\n    public static final String POSTDATE_KEY = \"postDate\";\n    public static final String CONTENT_KEY = \"content\";\n    public static final String KIND_KEY = \"kind\";\n    @Id\n    private long id;\n    @Column\n    private Long userId;\n    @Column\n    private long processInstanceId;\n    @Column\n    private long postDate;\n    @Column\n    private String content;\n\n    public SComment(final long processInstanceId, final String content) {\n        super();\n        this.processInstanceId = processInstanceId;\n        this.content = content;\n        this.postDate = System.currentTimeMillis();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-comment/src/main/java/org/bonitasoft/engine/core/process/comment/model/SHumanComment.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.comment.model;\n\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\n\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"human\")\npublic class SHumanComment extends SComment {\n\n    public SHumanComment(long processInstanceId, String content, Long userId) {\n        super(processInstanceId, content);\n        this.setUserId(userId);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-comment/src/main/java/org/bonitasoft/engine/core/process/comment/model/SSystemComment.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.comment.model;\n\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\n\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"system\")\npublic class SSystemComment extends SComment {\n\n    public SSystemComment(final long processInstanceId, final String content) {\n        super(processInstanceId, content);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-comment/src/main/java/org/bonitasoft/engine/core/process/comment/model/archive/SAComment.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.comment.model.archive;\n\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.comment.model.SComment;\nimport org.bonitasoft.engine.persistence.ArchivedPersistentObject;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n@Data\n@NoArgsConstructor\n@Entity\n@Table(name = \"arch_process_comment\")\npublic class SAComment implements ArchivedPersistentObject {\n\n    public static final String ID_KEY = \"id\";\n    public static final String USERID_KEY = \"userId\";\n    public static final String PROCESSINSTANCEID_KEY = \"processInstanceId\";\n    public static final String POSTDATE_KEY = \"postDate\";\n    public static final String CONTENT_KEY = \"content\";\n    public static final String ARCHIVEDATE_KEY = \"archiveDate\";\n    public static final String SOURCEOBJECTID_KEY = \"sourceObjectId\";\n    @Id\n    private long id;\n    private Long userId;\n    private long processInstanceId;\n    private long sourceObjectId;\n    private long postDate;\n    private long archiveDate;\n    private String content;\n\n    public SAComment(final SComment sComment) {\n        content = sComment.getContent();\n        postDate = sComment.getPostDate();\n        sourceObjectId = sComment.getId();\n        processInstanceId = sComment.getProcessInstanceId();\n        userId = sComment.getUserId();\n    }\n\n    @Override\n    public Class<? extends PersistentObject> getPersistentObjectInterface() {\n        return SComment.class;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-comment/src/main/java/org/bonitasoft/engine/core/process/comment/model/builder/SCommentLogBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.comment.model.builder;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory;\n\n/**\n * @author Hongwen Zang\n */\npublic interface SCommentLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-comment/src/main/java/org/bonitasoft/engine/core/process/comment/model/builder/impl/SCommentLogBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.comment.model.builder.impl;\n\nimport org.bonitasoft.engine.core.process.comment.model.builder.SCommentLogBuilderFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory;\n\n/**\n * @author Hongwen Zang\n * @author Matthieu Chaffotte\n */\npublic class SCommentLogBuilderFactoryImpl extends CRUDELogBuilderFactory implements SCommentLogBuilderFactory {\n\n    public static final String COMMENT = \"COMMENT\";\n\n    public static final String COMMENT_INDEX_NAME = \"numericIndex1\";\n\n    @Override\n    public String getObjectIdKey() {\n        return COMMENT_INDEX_NAME;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-comment/src/main/resources/org/bonitasoft/engine/core/process/comment/model/impl/hibernate/archive.comment.queries.hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\"\n                                   \"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd\">\n<hibernate-mapping auto-import=\"false\">\n\n  <query name=\"getNumberOfSAComment\">\n\tSELECT COUNT(DISTINCT saComment.id)\n\tFROM org.bonitasoft.engine.core.process.comment.model.archive.SAComment AS saComment\n\tWHERE saComment.archiveDate = (\n\t\t\tSELECT MAX(sac.archiveDate) FROM org.bonitasoft.engine.core.process.comment.model.archive.SAComment AS sac\n\t\t\tWHERE sac.sourceObjectId = saComment.sourceObjectId\n\t)\n  </query>\n  \n  <query name=\"searchSAComment\">\n\tSELECT saComment\n\tFROM org.bonitasoft.engine.core.process.comment.model.archive.SAComment AS saComment\n\tWHERE saComment.archiveDate = (\n\t\t\tSELECT MAX(sac.archiveDate) FROM org.bonitasoft.engine.core.process.comment.model.archive.SAComment AS sac\n\t\t\tWHERE sac.sourceObjectId = saComment.sourceObjectId\n\t)\n  </query>\n  \n  <query name=\"getArchivedCommentById\">\n\tSELECT saComment\n\tFROM org.bonitasoft.engine.core.process.comment.model.archive.SAComment AS saComment\n\tWHERE saComment.id = :id\n  </query>\n\n\t<query name=\"deleteArchiveCommentsOfProcessInstances\">\n\t\tDELETE\n\t\tFROM org.bonitasoft.engine.core.process.comment.model.archive.SAComment AS saComment\n\t\tWHERE saComment.processInstanceId IN (:processInstanceIds)\n\t</query>\n</hibernate-mapping>\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-comment/src/main/resources/org/bonitasoft/engine/core/process/comment/model/impl/hibernate/comment.queries.hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\" \"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd\">\n<hibernate-mapping auto-import=\"false\">\n\n\t<query name=\"searchSComment\">\n\t\tSELECT comment\n\t\tFROM org.bonitasoft.engine.core.process.comment.model.SComment AS comment\n\t</query>\n\n\t<query name=\"getNumberOfSComment\">\n\t\tSELECT COUNT(comment.id)\n\t\tFROM org.bonitasoft.engine.core.process.comment.model.SComment AS comment\n\t</query>\n\n\t<query name=\"getSComments\">\n\t\tSELECT comment\n\t\tFROM org.bonitasoft.engine.core.process.comment.model.SComment AS comment\n\t\tWHERE comment.processInstanceId = (:processInstanceId)\n\t</query>\t\n\t\n\t<query name=\"searchSCommentwithSUser\">\n\t\tSELECT comment\n\t\tFROM org.bonitasoft.engine.core.process.comment.model.SComment AS comment,\n\t\t     org.bonitasoft.engine.identity.model.SUser AS user\n\t\tWHERE user.id = comment.userId\n\t</query>\n\n\t<query name=\"getNumberOfSCommentwithSUser\">\n\t\tSELECT count(comment.id)\n\t\tFROM org.bonitasoft.engine.core.process.comment.model.SComment AS comment,\n\t\t     org.bonitasoft.engine.identity.model.SUser AS user\n\t\tWHERE user.id = comment.userId\n\t</query>\n\n\t<!-- External Service Queries -->\n\t<query name=\"getNumberOfSCommentSupervisedBy\">\n\t\tSELECT COUNT(DISTINCT comment.id)\n\t\tFROM org.bonitasoft.engine.core.process.comment.model.SHumanComment AS comment,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor\n\t\tWHERE (\n\t\t\tsupervisor.userId = :supervisorId\n\t\t\tOR supervisor.id IN (\n\t\t\t\tSELECT DISTINCT processsupervisor.id\n\t\t\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor,\n\t\t\t\t\t org.bonitasoft.engine.identity.model.SUserMembership AS user_membership\n\t\t\t\tWHERE user_membership.userId = :supervisorId\n\t\t\t\tAND (\n\t\t\t\t\t(processsupervisor.groupId = user_membership.groupId AND processsupervisor.roleId &lt;= 0)\n\t\t\t\t\tOR (processsupervisor.roleId = user_membership.roleId AND processsupervisor.groupId &lt;= 0)\n\t\t\t\t\tOR (processsupervisor.roleId = user_membership.roleId AND processsupervisor.groupId = user_membership.groupId)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t\tAND (\n\t\t\tcomment.processInstanceId IN (\n\t\t\t\tSELECT DISTINCT pi.id\n\t\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS pi\n\t\t\t\tWHERE pi.processDefinitionId = supervisor.processDefId\n\t\t\t)\n\t\t\tOR comment.processInstanceId IN (\n\t\t\t\tSELECT DISTINCT api.sourceObjectId\n\t\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS api\n\t\t\t\tWHERE api.processDefinitionId = supervisor.processDefId\n\t\t\t)\n\t\t)\n\t</query>\n\n\t<query name=\"searchSCommentSupervisedBy\">\n\t\tSELECT DISTINCT comment\n\t\tFROM org.bonitasoft.engine.core.process.comment.model.SHumanComment AS comment,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor\n\t\tWHERE (\n\t\t\tsupervisor.userId = :supervisorId\n\t\t\tOR supervisor.id IN (\n\t\t\t\tSELECT DISTINCT processsupervisor.id\n\t\t\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor,\n\t\t\t\t\t org.bonitasoft.engine.identity.model.SUserMembership AS user_membership\n\t\t\t\tWHERE user_membership.userId = :supervisorId\n\t\t\t\tAND (\n\t\t\t\t\t(processsupervisor.groupId = user_membership.groupId AND processsupervisor.roleId &lt;= 0)\n\t\t\t\t\tOR (processsupervisor.roleId = user_membership.roleId AND processsupervisor.groupId &lt;= 0)\n\t\t\t\t\tOR (processsupervisor.roleId = user_membership.roleId AND processsupervisor.groupId = user_membership.groupId)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t\tAND (\n\t\t\tcomment.processInstanceId IN (\n\t\t\t\tSELECT DISTINCT pi.id\n\t\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS pi\n\t\t\t\tWHERE pi.processDefinitionId = supervisor.processDefId\n\t\t\t)\n\t\t\tOR comment.processInstanceId IN (\n\t\t\t\tSELECT DISTINCT api.sourceObjectId\n\t\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS api\n\t\t\t\tWHERE api.processDefinitionId = supervisor.processDefId\n\t\t\t)\n\t\t)\n\t</query>\n\n\t<query name=\"getNumberOfSCommentInvolvingUser\">\n\t\tSELECT count(DISTINCT comment.id)\n\t\tFROM org.bonitasoft.engine.core.process.comment.model.SHumanComment AS comment,\n\t\torg.bonitasoft.engine.identity.model.SUser AS u\n\t\tWHERE comment.userId = u.id\n\t\tAND ( comment.userId = :userId\n\t\tOR comment.processInstanceId IN (\n\t\t\tSELECT usertask.logicalGroup2\n\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS usertask\n\t\t\tWHERE usertask.assigneeId = :userId\n\t\t)\n\t\tOR comment.processInstanceId IN (\n\t\t\tSELECT at.logicalGroup2\n\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance AS at\n\t\t\tWHERE at.assigneeId = :userId\n\t\t)\n\t\tOR comment.processInstanceId IN (\n\t\t\tSELECT p.id\n\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p,\n\t\t\t\t org.bonitasoft.engine.identity.model.SUser AS user\n\t\t\tWHERE p.startedBy = user.id\n\t\t\tAND user.id = comment.userId\n\t\t\tAND user.id = :userId\n\t\t)\n\t\tOR comment.processInstanceId IN (\n\t\t\tSELECT pi.id\n\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS pi,\n\t\t\t\t org.bonitasoft.engine.identity.model.SUser AS user\n\t\t\tWHERE pi.startedBy = user.id\n\t\t\tAND user.id = comment.userId\n\t\t\tAND user.id = :userId\n\t\t))\n\t</query>\n\n\t<query name=\"searchSCommentInvolvingUser\">\n\t\tSELECT DISTINCT comment\n\t\tFROM org.bonitasoft.engine.core.process.comment.model.SHumanComment AS comment,\n\t\torg.bonitasoft.engine.identity.model.SUser AS u\n\t\tWHERE comment.userId = u.id\n\t\tAND (comment.userId = :userId\n\t\tOR comment.processInstanceId IN (\n\t\t\tSELECT usertask.logicalGroup2\n\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS usertask\n\t\t\tWHERE usertask.assigneeId = :userId\n\t\t)\n\t\tOR comment.processInstanceId IN (\n\t\t\tSELECT at.logicalGroup2\n\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance AS at\n\t\t\tWHERE at.assigneeId = :userId\n\t\t)\n\t\tOR comment.processInstanceId IN (\n\t\t\tSELECT p.id\n\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p,\n\t\t\t\t org.bonitasoft.engine.identity.model.SUser AS user\n\t\t\tWHERE p.startedBy = user.id\n\t\t\tAND user.id = comment.userId\n\t\t\tAND user.id = :userId\n\t\t)\n\t\tOR comment.processInstanceId IN (\n\t\t\tSELECT pi.id\n\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS pi,\n\t\t\t\t org.bonitasoft.engine.identity.model.SUser AS user\n\t\t\tWHERE pi.startedBy = user.id\n\t\t\tAND user.id = comment.userId\n\t\t\tAND user.id = :userId\n\t\t))\n\t</query>\n\n\t<query name=\"getNumberOfSCommentManagedBy\">\n\t\tSELECT count(DISTINCT comment.id)\n\t\tFROM org.bonitasoft.engine.core.process.comment.model.SHumanComment AS comment,\n\t\t\torg.bonitasoft.engine.identity.model.SUser AS u\n\t\tWHERE u.managerUserId = :managerUserId\n\t\tAND (comment.userId = u.id\n\t\t\tOR comment.processInstanceId IN (\n\t\t\t\tSELECT usertask.logicalGroup2\n\t\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS usertask\n\t\t\t\tWHERE usertask.assigneeId = u.id\n\t\t\t)\n\t\t\tOR comment.processInstanceId IN (\n\t\t\t\tSELECT at.logicalGroup2\n\t\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance AS at\n\t\t\t\tWHERE at.assigneeId = u.id\n\t\t\t)\n\t\t\tOR comment.processInstanceId IN (\n\t\t\t\tSELECT p.id\n\t\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p,\n\t\t\t\t\t org.bonitasoft.engine.identity.model.SUser AS user\n\t\t\t\tWHERE p.startedBy = u.id\n\t\t\t)\n\t\t\tOR comment.processInstanceId IN (\n\t\t\t\tSELECT pi.sourceObjectId\n\t\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS pi,\n\t\t\t\t\t org.bonitasoft.engine.identity.model.SUser AS user\n\t\t\t\tWHERE pi.startedBy = u.id\n\t\t\t)\n\t\t)\n\t</query>\n\n\t<query name=\"searchSCommentManagedBy\">\n\t\tSELECT comment\n\t\tFROM org.bonitasoft.engine.core.process.comment.model.SHumanComment AS comment,\n\t\t\torg.bonitasoft.engine.identity.model.SUser AS u\n\t\tWHERE u.managerUserId = :managerUserId\n\t\tAND (comment.userId = u.id\n\t\t\tOR comment.processInstanceId IN (\n\t\t\t\tSELECT usertask.logicalGroup2\n\t\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS usertask\n\t\t\t\tWHERE usertask.assigneeId = u.id\n\t\t\t)\n\t\t\tOR comment.processInstanceId IN (\n\t\t\t\tSELECT at.logicalGroup2\n\t\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance AS at\n\t\t\t\tWHERE at.assigneeId = u.id\n\t\t\t)\n\t\t\tOR comment.processInstanceId IN (\n\t\t\t\tSELECT p.id\n\t\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p,\n\t\t\t\t\t org.bonitasoft.engine.identity.model.SUser AS user\n\t\t\t\tWHERE p.startedBy = u.id\n\t\t\t)\n\t\t\tOR comment.processInstanceId IN (\n\t\t\t\tSELECT pi.sourceObjectId\n\t\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS pi,\n\t\t\t\t\t org.bonitasoft.engine.identity.model.SUser AS user\n\t\t\t\tWHERE pi.startedBy = u.id\n\t\t\t)\n\t\t)\n\t</query>\n\n\n</hibernate-mapping>\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/build.gradle",
    "content": "\n\ndependencies {\n    api project(':bpm:bonita-common')\n    api project(':services:bonita-identity')\n    api project(':services:bonita-expression')\n    api project(':services:bonita-data-definition')\n    api project(':services:bonita-commons')\n    api project(':services:bonita-log')\n    api project(':services:bonita-persistence')\n    api project(':services:bonita-time-tracker')\n    api project(':services:bonita-data-instance')\n    api project(':services:bonita-classloader')\n    api project(':services:bonita-builder')\n    testImplementation libs.assertj\n    testImplementation libs.mockitoCore\n    testImplementation libs.logback\n\n    annotationProcessor libs.lombok\n    compileOnly libs.lombok\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/expression/control/api/ExpressionResolverService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.expression.control.api;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Zhao Na\n * @author Baptiste Mesta\n * @since 6.0\n */\npublic interface ExpressionResolverService {\n\n    /**\n     * Evaluate the specific expression\n     *\n     * @param expression\n     *        the expression will be evaluated\n     * @return the evaluated expression result\n     * @throws SExpressionTypeUnknownException\n     * @throws SExpressionEvaluationException\n     * @throws SExpressionDependencyMissingException\n     * @throws SInvalidExpressionException\n     */\n    Object evaluate(final SExpression expression) throws SExpressionTypeUnknownException,\n            SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException;\n\n    /**\n     * Evaluate the specific expression with the given expressionContext.\n     *\n     * @param expression\n     *        the expression will be evaluated\n     * @param contextDependency\n     *        the expressionContext, it can contain some value informations or evaluated enviorenment for expressions\n     * @return the evaluated expression result\n     * @throws SExpressionTypeUnknownException\n     * @throws SExpressionEvaluationException\n     * @throws SExpressionDependencyMissingException\n     * @throws SInvalidExpressionException\n     */\n    Object evaluate(final SExpression expression, final SExpressionContext contextDependency)\n            throws SExpressionTypeUnknownException, SExpressionEvaluationException,\n            SExpressionDependencyMissingException, SInvalidExpressionException;\n\n    /**\n     * Evaluate the specific expressions with the given expressionContext.\n     *\n     * @param expressions\n     *        a list of expressions will be evaluated\n     * @param contextDependency\n     *        the expressionContext, it can contain some value information or evaluated environment for expressions\n     * @return the evaluated expression result in same order as expressions parameter\n     * @throws SExpressionTypeUnknownException\n     * @throws SExpressionEvaluationException\n     * @throws SExpressionDependencyMissingException\n     * @throws SInvalidExpressionException\n     */\n    List<Object> evaluate(final List<SExpression> expressions, final SExpressionContext contextDependency)\n            throws SExpressionTypeUnknownException,\n            SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException;\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/expression/control/api/impl/ExpressionResolverServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.expression.control.api.impl;\n\nimport static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.classloader.SClassLoaderException;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.dependency.model.ScopeType;\nimport org.bonitasoft.engine.expression.ContainerState;\nimport org.bonitasoft.engine.expression.ExpressionExecutorStrategy;\nimport org.bonitasoft.engine.expression.ExpressionService;\nimport org.bonitasoft.engine.expression.ExpressionType;\nimport org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.ExpressionKind;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.tracking.TimeTracker;\nimport org.bonitasoft.engine.tracking.TimeTrackerRecords;\n\n/**\n * @author Zhao Na\n * @author Emmanuel Duchastenier\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class ExpressionResolverServiceImpl implements ExpressionResolverService {\n\n    private static final SExpressionContext EMPTY_CONTEXT = new SExpressionContext();\n\n    private final ExpressionService expressionService;\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    private final ClassLoaderService classLoaderService;\n\n    private final TimeTracker timeTracker;\n\n    public ExpressionResolverServiceImpl(final ExpressionService expressionService,\n            final ProcessDefinitionService processDefinitionService,\n            final ClassLoaderService classLoaderService, final TimeTracker timeTracker) {\n        this.expressionService = expressionService;\n        this.processDefinitionService = processDefinitionService;\n        this.classLoaderService = classLoaderService;\n        this.timeTracker = timeTracker;\n    }\n\n    @Override\n    public Object evaluate(final SExpression expression)\n            throws SExpressionTypeUnknownException, SExpressionEvaluationException,\n            SExpressionDependencyMissingException, SInvalidExpressionException {\n        return evaluate(expression, EMPTY_CONTEXT);\n    }\n\n    @Override\n    public Object evaluate(final SExpression expression, final SExpressionContext evaluationContext)\n            throws SExpressionTypeUnknownException, SExpressionEvaluationException,\n            SExpressionDependencyMissingException, SInvalidExpressionException {\n        final long startTime = System.currentTimeMillis();\n        try {\n            return evaluateExpressionsFlatten(Collections.singletonList(expression), evaluationContext).get(0);\n        } finally {\n            if (timeTracker.isTrackable(TimeTrackerRecords.EVALUATE_EXPRESSION_INCLUDING_CONTEXT)) {\n                final long endTime = System.currentTimeMillis();\n                final StringBuilder desc = new StringBuilder();\n                desc.append(\"Expression: \");\n                desc.append(expression);\n                desc.append(\" - \");\n                desc.append(\"evaluationContext: \");\n                desc.append(evaluationContext);\n                timeTracker.track(TimeTrackerRecords.EVALUATE_EXPRESSION_INCLUDING_CONTEXT, desc.toString(),\n                        endTime - startTime);\n            }\n        }\n    }\n\n    private List<Object> evaluateExpressionsFlatten(final List<SExpression> expressions,\n            final SExpressionContext evaluationContext)\n            throws SInvalidExpressionException, SExpressionTypeUnknownException, SExpressionEvaluationException,\n            SExpressionDependencyMissingException {\n        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        SExpressionContext newEvaluationContext = EMPTY_CONTEXT;\n        try {\n            final Map<String, Object> dependencyValues = new HashMap<>();\n            if (evaluationContext != null) {\n                newEvaluationContext = evaluationContext;\n                fillContext(newEvaluationContext, dependencyValues);\n            }\n\n            loadProcessClassLoader(newEvaluationContext);\n\n            final Map<SExpression, SExpression> dataReplacement = new HashMap<>();\n            // We incrementally build the Map of already resolved expressions:\n            final Map<Integer, Object> resolvedExpressions = new HashMap<>();\n            // Let's evaluate all expressions with no dependencies first:\n            resolvedExpressions.putAll(evaluateAllExpressionsWithNoDependencies(dependencyValues, dataReplacement,\n                    expressions, newEvaluationContext));\n\n            for (final SExpression sExpression : expressions) {\n                if (sExpression != null) {\n                    // Then evaluate recursively all remaining expressions:\n                    resolvedExpressions.putAll(evaluateExpressionWithResolvedDependencies(sExpression, dependencyValues,\n                            dataReplacement, resolvedExpressions,\n                            newEvaluationContext.getContainerState()));\n                }\n            }\n            final List<Object> results = new ArrayList<>(expressions.size());\n            for (final SExpression sExpression : expressions) {\n                if (sExpression != null) {\n                    final int key = sExpression.getDiscriminant();\n                    final Object res = resolvedExpressions.get(key);\n                    if (res == null && !resolvedExpressions.containsKey(key)) {\n                        throw new SExpressionEvaluationException(\"No result found for the expression \" + sExpression,\n                                sExpression.getName());\n                    }\n                    results.add(res);\n                } else {\n                    results.add(null);\n                }\n            }\n            return results;\n        } catch (final SProcessDefinitionNotFoundException | SBonitaReadException e) {\n            throw buildSExpressionEvaluationExceptionWhenNotFindProcess(newEvaluationContext, e);\n        } catch (final SClassLoaderException e) {\n            throw new SExpressionEvaluationException(e, null);\n        } finally {\n            Thread.currentThread().setContextClassLoader(contextClassLoader);\n        }\n    }\n\n    private void loadProcessClassLoader(final SExpressionContext evaluationContext) throws SClassLoaderException {\n        Long processId;\n        if (evaluationContext.getParentProcessDefinitionId() != null) {\n            processId = evaluationContext.getParentProcessDefinitionId();\n        } else {\n            processId = evaluationContext.getProcessDefinitionId();\n        }\n        if (processId != null) {\n            Thread.currentThread().setContextClassLoader(\n                    classLoaderService.getClassLoader(identifier(ScopeType.PROCESS, processId)));\n        }\n    }\n\n    private SExpressionEvaluationException buildSExpressionEvaluationExceptionWhenNotFindProcess(\n            final SExpressionContext evaluationContext,\n            final SBonitaException e) {\n        final SExpressionEvaluationException exception = new SExpressionEvaluationException(\n                \"The process definition was not found.\", e, null);\n        exception.setProcessDefinitionIdOnContext(evaluationContext.getProcessDefinitionId());\n        return exception;\n    }\n\n    private Map<Integer, Object> evaluateAllExpressionsWithNoDependencies(final Map<String, Object> dependencyValues,\n            final Map<SExpression, SExpression> dataReplacement, final List<SExpression> expressions,\n            final SExpressionContext evaluationContext)\n            throws SExpressionTypeUnknownException, SExpressionEvaluationException,\n            SExpressionDependencyMissingException, SInvalidExpressionException {\n        final Map<Integer, Object> resolvedExpressions = new HashMap<>();\n        final Map<ExpressionKind, List<SExpression>> expressionMapByKind = flattenDependencies(expressions);\n        final List<SExpression> variableExpressions = expressionMapByKind\n                .get(new ExpressionKind(ExpressionType.TYPE_VARIABLE.name()));\n\n        if (evaluationContext.isEvaluateInDefinition() && variableExpressions != null\n                && !variableExpressions.isEmpty()) {\n            final SExpression expressionNotProvided = variablesAreAllProvided(variableExpressions, evaluationContext);\n            if (expressionNotProvided != null) {\n                // We forbid the evaluation of expressions of type VARIABLE at process definition level:\n                throw new SExpressionEvaluationException(\n                        \"Evaluation of expressions of type VARIABLE is forbidden at process definition level.\",\n                        expressionNotProvided.getName());\n            }\n        }\n        for (final ExpressionKind kind : ExpressionExecutorStrategy.NO_DEPENDENCY_EXPRESSION_EVALUATION_ORDER) {\n            resolvedExpressions.putAll(evaluateExpressionsOfKind(dependencyValues, expressionMapByKind.get(kind), kind,\n                    dataReplacement, resolvedExpressions,\n                    evaluationContext.getContainerState()));\n            expressionMapByKind.remove(kind);\n        }\n        return resolvedExpressions;\n    }\n\n    private SExpression variablesAreAllProvided(final List<SExpression> variableExpressions,\n            final SExpressionContext evaluationContext) {\n        final Iterator<SExpression> iterator = variableExpressions.iterator();\n        final Map<String, Object> inputValues = evaluationContext.getInputValues();\n        while (iterator.hasNext()) {\n            final SExpression next = iterator.next();\n            if (!inputValues.containsKey(next.getContent())) {\n                return next;\n            }\n        }\n        return null;\n    }\n\n    private Map<? extends Integer, ? extends Object> evaluateExpressionWithResolvedDependencies(\n            final SExpression sExpression,\n            final Map<String, Object> dependencyValues, final Map<SExpression, SExpression> dataReplacement,\n            final Map<Integer, Object> alreadyResolvedExpressions, final ContainerState containerState)\n            throws SExpressionTypeUnknownException,\n            SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException {\n        final Map<Integer, Object> resolvedExpressions = new HashMap<>(alreadyResolvedExpressions);\n        // Evaluate the dependencies first:\n        for (final SExpression dep : sExpression.getDependencies()) {\n            resolvedExpressions.putAll(evaluateExpressionWithResolvedDependencies(dep, dependencyValues,\n                    dataReplacement, resolvedExpressions, containerState));\n        }\n        // Then evaluate the expression itself:\n        if (!resolvedExpressions.containsKey(sExpression.getDiscriminant())) {\n            // Let's evaluate the expression only if it is not already in the list of resolved dependencies:\n            final Object exprResult = expressionService.evaluate(sExpression, dependencyValues, resolvedExpressions,\n                    containerState);\n            addResultToMap(resolvedExpressions, dataReplacement, sExpression, exprResult, dependencyValues);\n        }\n        return resolvedExpressions;\n    }\n\n    private Map<Integer, Object> evaluateExpressionsOfKind(final Map<String, Object> dependencyValues,\n            final List<SExpression> expressionsOfKind,\n            final ExpressionKind kind, final Map<SExpression, SExpression> dataReplacement,\n            final Map<Integer, Object> alreadyResolvedExpressions,\n            final ContainerState containerState) throws SExpressionTypeUnknownException, SExpressionEvaluationException,\n            SExpressionDependencyMissingException, SInvalidExpressionException {\n        final Map<Integer, Object> resolvedExpressions = new HashMap<>();\n        if (expressionsOfKind != null) {\n            final List<Object> evaluationResults = expressionService.evaluate(kind, expressionsOfKind, dependencyValues,\n                    alreadyResolvedExpressions,\n                    containerState);\n            final Iterator<SExpression> variableIterator = expressionsOfKind.iterator();\n            for (final Object evaluationResult : evaluationResults) {\n                final SExpression expression = variableIterator.next();\n                addResultToMap(resolvedExpressions, dataReplacement, expression, evaluationResult, dependencyValues);\n            }\n        }\n        return resolvedExpressions;\n    }\n\n    private void addResultToMap(final Map<Integer, Object> resolvedExpressions,\n            final Map<SExpression, SExpression> dataReplacement,\n            final SExpression expression, final Object expressionResult, final Map<String, Object> dependencyValues) {\n        resolvedExpressions.put(expression.getDiscriminant(), expressionResult);\n        if (expressionService.mustPutEvaluatedExpressionInContext(expression.getExpressionKind())) {\n            dependencyValues.put(expression.getContent(), expressionResult);\n        }\n        final SExpression replacement = dataReplacement.get(expression);\n        if (replacement != null) {\n            resolvedExpressions.put(replacement.getDiscriminant(), expressionResult);\n            if (expressionService.mustPutEvaluatedExpressionInContext(expression.getExpressionKind())) {\n                dependencyValues.put(replacement.getContent(), expressionResult);\n            }\n        }\n    }\n\n    private Map<ExpressionKind, List<SExpression>> flattenDependencies(final Collection<SExpression> collection) {\n        final Map<ExpressionKind, List<SExpression>> expressionMapByKind = new HashMap<>();\n        for (final SExpression sExpression : collection) {\n            if (sExpression == null) {\n                continue;\n            }\n            final ExpressionKind expressionKind = sExpression.getExpressionKind();\n            // Get from the map the list of expressions of given ExpressionKind\n            List<SExpression> exprList = expressionMapByKind.get(expressionKind);\n\n            // If no present, put it in the map\n            if (exprList == null) {\n                exprList = new ArrayList<>();\n                expressionMapByKind.put(expressionKind, exprList);\n            }\n            if (!exprList.contains(sExpression)) {\n                exprList.add(sExpression);\n            }\n            if (sExpression.getDependencies() != null) {\n                expressionMapByKind.putAll(flattenDependencies(sExpression.getDependencies()));\n            }\n        }\n        return expressionMapByKind;\n    }\n\n    private void fillContext(final SExpressionContext evaluationContext, final Map<String, Object> dependencyValues)\n            throws SProcessDefinitionNotFoundException, SBonitaReadException {\n        if (evaluationContext.getContainerId() == null && evaluationContext.getProcessDefinitionId() != null) {\n            final SProcessDefinition processDefinition = processDefinitionService\n                    .getProcessDefinition(evaluationContext.getProcessDefinitionId());\n            evaluationContext.setProcessDefinition(processDefinition);\n            evaluationContext.setEvaluateInDefinition(true);\n        }\n        if (evaluationContext.getContainerId() != null) {\n            dependencyValues.put(SExpressionContext.CONTAINER_ID_KEY, evaluationContext.getContainerId());\n        }\n        if (evaluationContext.getContainerType() != null) {\n            dependencyValues.put(SExpressionContext.CONTAINER_TYPE_KEY, evaluationContext.getContainerType());\n        }\n        if (evaluationContext.getProcessDefinitionId() != null) {\n            dependencyValues.put(SExpressionContext.PROCESS_DEFINITION_ID_KEY,\n                    evaluationContext.getProcessDefinitionId());\n        }\n        if (evaluationContext.getTime() != 0) {\n            dependencyValues.put(SExpressionContext.TIME_KEY, evaluationContext.getTime());\n        }\n        if (evaluationContext.getInputValues() != null) {\n            dependencyValues.putAll(evaluationContext.getInputValues());\n        }\n    }\n\n    @Override\n    public List<Object> evaluate(final List<SExpression> expressions, final SExpressionContext contextDependency)\n            throws SExpressionTypeUnknownException,\n            SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException {\n        return evaluateExpressionsFlatten(expressions, contextDependency);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/expression/control/model/SExpressionContext.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.expression.control.model;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\nimport org.bonitasoft.engine.expression.ContainerState;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Zhao Na\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class SExpressionContext implements Serializable {\n\n    private static final long serialVersionUID = 6417383664862870145L;\n\n    public static final String CONTAINER_ID_KEY = \"containerId\";\n\n    public static final String CONTAINER_TYPE_KEY = \"containerType\";\n\n    public static final String TIME_KEY = \"time\";\n\n    public static final String PROCESS_DEFINITION_ID_KEY = \"processDefinitionId\";\n\n    public static final String PROCESS_DEFINITION_KEY = \"processDefinition\";\n\n    private Long containerId;\n\n    private String containerType;\n\n    private ContainerState containerState;\n\n    private long time;\n\n    private Long processDefinitionId;\n\n    private Long parentProcessDefinitionId;\n\n    private SProcessDefinition processDefinition;\n\n    private Map<String, Object> inputValues;\n\n    private boolean evaluateInDefinition = false;\n\n    private Map<String, SExpression> dataMap;\n\n    private Map<SExpression, String> invertedDataMap;\n\n    public SExpressionContext() {\n        inputValues = new HashMap<>();\n    }\n\n    public SExpressionContext(final Long containerId, final String containerType, final Long processDefinitionId) {\n        this(containerId, containerType, processDefinitionId, null);\n    }\n\n    public SExpressionContext(final long containerId, final String containerType, final Long processDefinitionId,\n            final Map<String, Object> inputValues) {\n        this.containerId = containerId;\n        this.containerType = containerType;\n        this.processDefinitionId = processDefinitionId;\n        if (inputValues == null) {\n            this.inputValues = new HashMap<>();\n        } else {\n            this.inputValues = new HashMap<>(inputValues);\n        }\n    }\n\n    public Long getProcessDefinitionId() {\n        return processDefinitionId;\n    }\n\n    public void setProcessDefinitionId(final Long processDefinitionId) {\n        this.processDefinitionId = processDefinitionId;\n    }\n\n    public long getTime() {\n        return time;\n    }\n\n    public void setTime(final long time) {\n        this.time = time;\n    }\n\n    public Long getContainerId() {\n        return containerId;\n    }\n\n    public void setContainerId(final Long containerId) {\n        this.containerId = containerId;\n    }\n\n    public String getContainerType() {\n        return containerType;\n    }\n\n    public void setContainerType(final String containerType) {\n        this.containerType = containerType;\n    }\n\n    public ContainerState getContainerState() {\n        return containerState;\n    }\n\n    public void setContainerState(final ContainerState containerState) {\n        this.containerState = containerState;\n    }\n\n    public Map<String, Object> getInputValues() {\n        return inputValues;\n    }\n\n    public SProcessDefinition getProcessDefinition() {\n        return processDefinition;\n    }\n\n    public void setProcessDefinition(final SProcessDefinition processDefinition) {\n        this.processDefinition = processDefinition;\n        final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer();\n        final List<SDataDefinition> dataDefinitions = processContainer.getDataDefinitions();\n        dataMap = new HashMap<>(dataDefinitions.size());\n        invertedDataMap = new HashMap<>(dataDefinitions.size());\n        for (final SDataDefinition dataDef : dataDefinitions) {\n            dataMap.put(dataDef.getName(), dataDef.getDefaultValueExpression());\n            invertedDataMap.put(dataDef.getDefaultValueExpression(), dataDef.getName());\n        }\n    }\n\n    public SExpression getDefaultValueFor(final String name) {\n        if (evaluateInDefinition) {\n            return dataMap.get(name);\n        }\n        return null;\n    }\n\n    public String isDefaultValueOf(final SExpression exp) {\n        if (evaluateInDefinition) {\n            return invertedDataMap.get(exp);\n        }\n        return null;\n    }\n\n    public void setInputValues(final Map<String, Object> inputValues) {\n        if (inputValues == null) {\n            this.inputValues = new HashMap<>();\n        } else {\n            this.inputValues = new HashMap<>(inputValues);\n        }\n    }\n\n    public void putAllInputValues(final Map<String, Object> inputValues) {\n        if (inputValues != null) {\n            this.inputValues.putAll(inputValues);\n        }\n    }\n\n    public void setSerializableInputValues(final Map<String, Serializable> inputValues) {\n        if (inputValues != null) {\n            this.inputValues.putAll(inputValues);\n        }\n    }\n\n    public void setEvaluateInDefinition(final boolean evaluateInDefinition) {\n        this.evaluateInDefinition = evaluateInDefinition;\n    }\n\n    public boolean isEvaluateInDefinition() {\n        return evaluateInDefinition;\n    }\n\n    @Override\n    public String toString() {\n        return \"context [containerId=\" + containerId + \", containerType=\" + containerType + \", processDefinitionId=\"\n                + processDefinitionId\n                + (processDefinition != null\n                        ? \", processDefinition=\" + processDefinition.getName() + \" -- \" + processDefinition.getVersion()\n                        : \"\")\n                + \"]\";\n    }\n\n    public Long getParentProcessDefinitionId() {\n        return parentProcessDefinitionId;\n    }\n\n    public void setParentProcessDefinitionId(final Long parentProcessDefinitionId) {\n        this.parentProcessDefinitionId = parentProcessDefinitionId;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (!(o instanceof SExpressionContext that))\n            return false;\n        return Objects.equals(time, that.time) &&\n                Objects.equals(evaluateInDefinition, that.evaluateInDefinition) &&\n                Objects.equals(containerId, that.containerId) &&\n                Objects.equals(containerType, that.containerType) &&\n                Objects.equals(containerState, that.containerState) &&\n                Objects.equals(processDefinitionId, that.processDefinitionId) &&\n                Objects.equals(parentProcessDefinitionId, that.parentProcessDefinitionId) &&\n                Objects.equals(processDefinition, that.processDefinition) &&\n                Objects.equals(inputValues, that.inputValues) &&\n                Objects.equals(dataMap, that.dataMap) &&\n                Objects.equals(invertedDataMap, that.invertedDataMap);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(containerId, containerType, containerState, time, processDefinitionId,\n                parentProcessDefinitionId, processDefinition, inputValues,\n                evaluateInDefinition, dataMap, invertedDataMap);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/LeftOperandHandler.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.operation.model.SLeftOperand;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\n/**\n * Tells the engine how to retrieve and update a left operand having the specified type\n * e.g. a data left operand handler will get data from database and set the data back in database\n *\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface LeftOperandHandler {\n\n    String getType();\n\n    /**\n     * @param inputValues contains value(s) given by the strategy to update the left operand with\n     * @param sLeftOperand the left operand\n     * @param newValue the value to set the element with\n     * @param containerId the container id\n     * @param containerType the container type\n     * @throws SOperationExecutionException\n     */\n    Object update(SLeftOperand sLeftOperand, Map<String, Object> inputValues, Object newValue, long containerId,\n            String containerType) throws SOperationExecutionException;\n\n    void delete(SLeftOperand sLeftOperand, long containerId, String containerType) throws SOperationExecutionException;\n\n    /**\n     * retrieve the left operand and put it in context as needed by the left operand\n     *\n     * @param sLeftOperand the left operand\n     * @param leftOperandContainerId the left operand container id. Used to execute the left Operand in the correct\n     *        context\n     * @param leftOperandContainerType the left operand container type. Used to execute the left Operand in the correct\n     *        context\n     * @param contextToSet the context to add the value i\n     * @throws SBonitaReadException\n     */\n    void loadLeftOperandInContext(SLeftOperand sLeftOperand, final long leftOperandContainerId,\n            final String leftOperandContainerType, SExpressionContext contextToSet) throws SBonitaReadException;\n\n    void loadLeftOperandInContext(List<SLeftOperand> sLeftOperandList, final long leftOperandContainerId,\n            final String leftOperandContainerType, SExpressionContext contextToSet) throws SBonitaReadException;\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/OperationExecutorStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation;\n\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\n\n/**\n * @author Zhang Bole\n * @author Elias Ricken de Medeiros\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface OperationExecutorStrategy {\n\n    /**\n     * Calculates the new value of the left operand base of right operand expression value\n     *\n     * @param operation the operation in progress\n     * @param rightOperandValue\n     *        result of the evaluation of right operand expression\n     * @param expressionContext the expression context\n     * @param shouldPersistValue true if the right operand must be persisted (Business Data)\n     * @return\n     *         the new value to set the left operand with\n     * @throws SOperationExecutionException\n     */\n    Object computeNewValueForLeftOperand(SOperation operation, Object rightOperandValue,\n            SExpressionContext expressionContext, final boolean shouldPersistValue)\n            throws SOperationExecutionException;\n\n    String getOperationType();\n\n    /**\n     * Determines if the operation right value should be persisted when the value is evaluated to null.\n     *\n     * @return true if the the evaluated right value should be persisted; false otherwise.\n     */\n    boolean shouldPersistOnNullValue();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/OperationExecutorStrategyProvider.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.operation.model.SLeftOperand;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.core.operation.model.SOperatorType;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class OperationExecutorStrategyProvider {\n\n    private final Map<String, OperationExecutorStrategy> operationStrategies;\n\n    public OperationExecutorStrategyProvider(final List<OperationExecutorStrategy> operationExecutors) {\n        operationStrategies = new HashMap<>(operationExecutors.size());\n        for (final OperationExecutorStrategy operationExecutorStrategy : operationExecutors) {\n            operationStrategies.put(operationExecutorStrategy.getOperationType(), operationExecutorStrategy);\n        }\n    }\n\n    public OperationExecutorStrategy getOperationExecutorStrategy(final SOperation operation)\n            throws SOperationExecutionException {\n        final String operatorTypeName = getStrategyKey(operation);\n        final OperationExecutorStrategy operationExecutorStrategy = operationStrategies.get(operatorTypeName);\n        if (operationExecutorStrategy == null) {\n            throw new SOperationExecutionException(\"Unable to find an executor for operation type \" + operatorTypeName);\n        }\n        return operationExecutorStrategy;\n    }\n\n    protected String getStrategyKey(final SOperation operation) {\n        String key = operation.getType().name();\n        String leftOperandType = operation.getLeftOperand().getType();\n        if (leftOperandType.equals(SLeftOperand.TYPE_BUSINESS_DATA) && key.equals(SOperatorType.ASSIGNMENT.name())) {\n            key += \"_\" + leftOperandType;\n        }\n        return key;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/OperationResult.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation;\n\nimport java.io.Serializable;\n\n/**\n * @author Zhang Bole\n */\npublic class OperationResult {\n\n    private Serializable result;\n\n    private String returnType; // do we really need it?\n\n    public OperationResult() {\n    }\n\n    public OperationResult(final Serializable result, final String returnType) {\n        this.result = result;\n        this.returnType = returnType;\n    }\n\n    public Serializable getResult() {\n        return result;\n    }\n\n    public void setResult(final Serializable result) {\n        this.result = result;\n    }\n\n    public String getReturnType() {\n        return returnType;\n    }\n\n    public void setReturnType(final String returnType) {\n        this.returnType = returnType;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/OperationService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\n\n/**\n * @author Zhang Bole\n * @author Elias Ricken de Medeiros\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @since 6.0\n */\npublic interface OperationService {\n\n    /**\n     * Execute the given operation in the given context and update data that are in the given data container\n     *\n     * @param operation\n     *        the operation to execute\n     * @param dataContainerId\n     *        the id of the data container (used for left operand)\n     * @param dataContainerType\n     *        the type of the data container (used for left operand)\n     * @param expressionContext\n     *        the context in which execute the operation\n     * @throws SOperationExecutionException\n     */\n    void execute(SOperation operation, long dataContainerId, String dataContainerType,\n            SExpressionContext expressionContext)\n            throws SOperationExecutionException;\n\n    /**\n     * Execute the given operation in the given context and update data that are in the given data container\n     *\n     * @param operations\n     *        the operations to execute\n     * @param leftOperandContainerId\n     *        the id of the container (used for left operand)\n     * @param leftOperandContainerType\n     *        the type of the container (used for left operand)\n     * @param expressionContext\n     *        the context in which execute the operation\n     * @throws SOperationExecutionException\n     */\n    void execute(List<SOperation> operations, long leftOperandContainerId, final String leftOperandContainerType,\n            SExpressionContext expressionContext)\n            throws SOperationExecutionException;\n\n    /**\n     * Execute the given operation in the given context and update data that are in the same context\n     *\n     * @param operations\n     * @param expressionContext\n     * @throws SOperationExecutionException\n     */\n    void execute(List<SOperation> operations, SExpressionContext expressionContext) throws SOperationExecutionException;\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/exception/SOperationExecutionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation.exception;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Zhang Bole\n */\npublic class SOperationExecutionException extends SBonitaException {\n\n    private static final long serialVersionUID = 4480803850726634142L;\n\n    public SOperationExecutionException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SOperationExecutionException(final String message) {\n        super(message);\n    }\n\n    public SOperationExecutionException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/impl/AssignmentOperationExecutorStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation.impl;\n\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.OperationExecutorStrategy;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.operation.model.SLeftOperand;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.core.operation.model.SOperatorType;\nimport org.springframework.stereotype.Component;\n\n/**\n * AssignmentOperationExecutorStrategy is the default Bonita strategy to execute data assignment operations\n *\n * @author Zhang Bole\n * @author Elias Ricken de Medeiros\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\n@Component\npublic class AssignmentOperationExecutorStrategy implements OperationExecutorStrategy {\n\n    /**\n     * Builds a new AssignmentOperationExecutorStrategy, which is the strategy to execute data assignment operations\n     */\n    public AssignmentOperationExecutorStrategy() {\n    }\n\n    @Override\n    public Object computeNewValueForLeftOperand(final SOperation operation, final Object value,\n            final SExpressionContext expressionContext,\n            final boolean shouldPersistValue) throws SOperationExecutionException {\n        // do not check if value is external, see ENGINE-1739\n        if (operation.getLeftOperand().getType().equals(SLeftOperand.TYPE_DATA)) {\n            checkReturnType(value, operation, expressionContext);\n        }\n        // no processing on the value, just return it\n        return value;\n    }\n\n    private void checkReturnType(final Object value, final SOperation operation,\n            final SExpressionContext expressionContext)\n            throws SOperationExecutionException {\n        if (value != null) {\n            final String name = operation.getLeftOperand().getName();\n            final Object object = expressionContext.getInputValues().get(name);\n            /*\n             * if the object is null (data is not initialized) the return type is not checked\n             * but the data instance service should throw an exception\n             */\n            if (object != null) {\n                final Class<?> dataEffectiveType = object.getClass();\n                final Class<?> evaluatedReturnedType = value.getClass();\n                if (!(dataEffectiveType.isAssignableFrom(evaluatedReturnedType)\n                        || dataEffectiveType.equals(evaluatedReturnedType))) {\n                    throw new SOperationExecutionException(\n                            \"Incompatible assignment operation type: Left operand \" + dataEffectiveType\n                                    + \" is not compatible with right operand \" + evaluatedReturnedType\n                                    + \" for expression with name '\" + expressionContext + \"'\");\n                }\n            }\n        }\n    }\n\n    @Override\n    public String getOperationType() {\n        return SOperatorType.ASSIGNMENT.name();\n    }\n\n    @Override\n    public boolean shouldPersistOnNullValue() {\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/impl/ExternalDataLeftOperandHandler.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation.impl;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.LeftOperandHandler;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.operation.model.SLeftOperand;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\n@Component\npublic class ExternalDataLeftOperandHandler implements LeftOperandHandler {\n\n    @Override\n    public String getType() {\n        return \"EXTERNAL_DATA\";\n    }\n\n    @Override\n    public Object update(final SLeftOperand leftOperand, Map<String, Object> inputValues, final Object newValue,\n            final long containerId, final String containerType) {\n        // nothing to do, the value is already changed in the context\n        return newValue;\n    }\n\n    @Override\n    public void delete(final SLeftOperand leftOperand, final long containerId, final String containerType)\n            throws SOperationExecutionException {\n        throw new SOperationExecutionException(\"Deleting an external data is not supported\");\n    }\n\n    @Override\n    public void loadLeftOperandInContext(final SLeftOperand sLeftOperand, final long leftOperandContainerId,\n            final String leftOperandContainerType, final SExpressionContext expressionContext) {\n        String name = sLeftOperand.getName();\n        if (!expressionContext.getInputValues().containsKey(name)) {\n            expressionContext.getInputValues().put(name, expressionContext.getInputValues().get(name));\n        }\n    }\n\n    @Override\n    public void loadLeftOperandInContext(final List<SLeftOperand> sLeftOperand, final long leftOperandContainerId,\n            final String leftOperandContainerType, final SExpressionContext expressionContext)\n            throws SBonitaReadException {\n        for (SLeftOperand leftOperand : sLeftOperand) {\n            loadLeftOperandInContext(leftOperand, leftOperandContainerId, leftOperandContainerType, expressionContext);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/impl/JavaMethodOperationExecutorStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation.impl;\n\nimport org.bonitasoft.engine.commons.JavaMethodInvoker;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.OperationExecutorStrategy;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\n\n/**\n * This stategy is not used directly,\n * BusinessDataJavaMethodOperationExecutorStrategy is first executed and then delegate to that one.\n */\npublic abstract class JavaMethodOperationExecutorStrategy implements OperationExecutorStrategy {\n\n    public static final String TYPE_JAVA_METHOD = \"JAVA_METHOD\";\n\n    public JavaMethodOperationExecutorStrategy() {\n    }\n\n    @Override\n    public Object computeNewValueForLeftOperand(final SOperation operation, final Object valueToSetObjectWith,\n            final SExpressionContext expressionContext,\n            final boolean shouldPersistValue)\n            throws SOperationExecutionException {\n        final Object objectToInvokeJavaMethodOn;\n        objectToInvokeJavaMethodOn = extractObjectToInvokeFromContext(operation, expressionContext);\n        final String methodName = extractMethodName(operation);\n        final String operatorType = extractParameterType(operation);\n        try {\n            return new JavaMethodInvoker().invokeJavaMethod(operation.getRightOperand().getReturnType(),\n                    valueToSetObjectWith, objectToInvokeJavaMethodOn,\n                    methodName, operatorType);\n        } catch (final Exception e) {\n            throw new SOperationExecutionException(\"Unable to evaluate operation \" + operation, e);\n        }\n    }\n\n    protected String extractParameterType(final SOperation operation) {\n        final String[] split = operation.getOperator().split(\":\", 2);\n        if (split.length > 1) {\n            return split[1];\n        }\n        return null;\n    }\n\n    protected String extractMethodName(final SOperation operation) {\n        final String[] split = operation.getOperator().split(\":\", 2);\n        return split[0];\n    }\n\n    protected Object extractObjectToInvokeFromContext(final SOperation operation,\n            final SExpressionContext expressionContext)\n            throws SOperationExecutionException {\n        final Object objectToInvokeJavaMethodOn;\n        final String dataToSet = operation.getLeftOperand().getName();\n        objectToInvokeJavaMethodOn = expressionContext.getInputValues().get(dataToSet);\n        if (objectToInvokeJavaMethodOn == null) {\n            throw new SOperationExecutionException(\"data \" + dataToSet + \" does not exist\");\n        }\n        return objectToInvokeJavaMethodOn;\n    }\n\n    @Override\n    public String getOperationType() {\n        return TYPE_JAVA_METHOD;\n    }\n\n    @Override\n    public boolean shouldPersistOnNullValue() {\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/impl/LeftOperandIndexes.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation.impl;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class LeftOperandIndexes {\n\n    private int lastIndex = -1;\n\n    private int nextIndex = -1;\n\n    public int getLastIndex() {\n        return lastIndex;\n    }\n\n    public void setLastIndex(final int lastIndex) {\n        this.lastIndex = lastIndex;\n    }\n\n    public int getNextIndex() {\n        return nextIndex;\n    }\n\n    public void setNextIndex(final int nextIndex) {\n        this.nextIndex = nextIndex;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/impl/LeftOperandUpdateStatus.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation.impl;\n\nimport org.bonitasoft.engine.core.operation.model.SOperatorType;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class LeftOperandUpdateStatus {\n\n    private boolean shouldUpdate;\n\n    public LeftOperandUpdateStatus(SOperatorType operatorType) {\n        shouldUpdate = operatorType != SOperatorType.DELETION;\n    }\n\n    public boolean shouldUpdate() {\n        return shouldUpdate;\n    }\n\n    public boolean shouldDelete() {\n        return !shouldUpdate;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/impl/OperationServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation.impl;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.LeftOperandHandler;\nimport org.bonitasoft.engine.core.operation.OperationExecutorStrategy;\nimport org.bonitasoft.engine.core.operation.OperationExecutorStrategyProvider;\nimport org.bonitasoft.engine.core.operation.OperationService;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.operation.model.SLeftOperand;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.core.operation.model.SOperatorType;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.springframework.stereotype.Service;\n\n/**\n * @author Zhang Bole\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n * @author Colin Puy\n * @author Matthieu Chaffotte\n */\n@Service(\"operationService\")\n@Slf4j\npublic class OperationServiceImpl implements OperationService {\n\n    private final Map<String, LeftOperandHandler> leftOperandHandlersMap;\n    private final ExpressionResolverService expressionResolverService;\n    private final PersistRightOperandResolver persistRightOperandResolver;\n    private final OperationExecutorStrategyProvider operationExecutorStrategyProvider;\n\n    public OperationServiceImpl(OperationExecutorStrategyProvider operationExecutorStrategyProvider,\n            List<LeftOperandHandler> leftOperandHandlers,\n            ExpressionResolverService expressionResolverService,\n            PersistRightOperandResolver persistRightOperandResolver) {\n        super();\n        this.operationExecutorStrategyProvider = operationExecutorStrategyProvider;\n        this.expressionResolverService = expressionResolverService;\n        this.persistRightOperandResolver = persistRightOperandResolver;\n        leftOperandHandlersMap = new HashMap<>(leftOperandHandlers.size());\n        for (final LeftOperandHandler leftOperandHandler : leftOperandHandlers) {\n            leftOperandHandlersMap.put(leftOperandHandler.getType(), leftOperandHandler);\n        }\n    }\n\n    @Override\n    public void execute(final SOperation operation, final long containerId, final String containerType,\n            final SExpressionContext expressionContext)\n            throws SOperationExecutionException {\n        execute(Arrays.asList(operation), containerId, containerType, expressionContext);\n    }\n\n    @Override\n    public void execute(final List<SOperation> operations, final SExpressionContext expressionContext)\n            throws SOperationExecutionException {\n        execute(operations, expressionContext.getContainerId(), expressionContext.getContainerType(),\n                expressionContext);\n    }\n\n    @Override\n    public void execute(final List<SOperation> operations, final long leftOperandContainerId,\n            final String leftOperandContainerType,\n            final SExpressionContext expressionContext) throws SOperationExecutionException {\n        if (operations.isEmpty()) {\n            return;\n        }\n        // retrieve all left operand to set and put it in context\n        retrieveLeftOperandsAndPutItInExpressionContextIfNotIn(operations, leftOperandContainerId,\n                leftOperandContainerType, expressionContext);\n\n        // execute operation and put it in context again\n        final Map<SLeftOperand, LeftOperandUpdateStatus> leftOperandUpdates = executeOperators(operations,\n                expressionContext);\n        // update data\n        updateLeftOperands(leftOperandUpdates, leftOperandContainerId, leftOperandContainerType, expressionContext);\n    }\n\n    Map<SLeftOperand, LeftOperandUpdateStatus> executeOperators(final List<SOperation> operations,\n            final SExpressionContext expressionContext)\n            throws SOperationExecutionException {\n        final Map<SLeftOperand, LeftOperandUpdateStatus> leftOperandsToUpdate = new HashMap<>();\n        for (int i = 0; i < operations.size(); i++) {\n            final SOperation currentOperation = operations.get(i);\n            final boolean shouldPersistValue = persistRightOperandResolver.shouldPersistByPosition(i, operations);\n            final LeftOperandUpdateStatus currentUpdateStatus = calculateRightOperandValue(currentOperation,\n                    expressionContext, shouldPersistValue);\n            if (shouldUpdateLeftOperandContext(leftOperandsToUpdate, currentOperation.getLeftOperand(),\n                    currentUpdateStatus)) {\n                leftOperandsToUpdate.put(currentOperation.getLeftOperand(), currentUpdateStatus);\n            }\n        }\n        return leftOperandsToUpdate;\n    }\n\n    private LeftOperandUpdateStatus calculateRightOperandValue(final SOperation operation,\n            final SExpressionContext expressionContext,\n            boolean shouldPersistValue)\n            throws SOperationExecutionException {\n        final SLeftOperand leftOperand = operation.getLeftOperand();\n        final LeftOperandUpdateStatus currentUpdateStatus = new LeftOperandUpdateStatus(operation.getType());\n        if (currentUpdateStatus.shouldUpdate()) {\n            final OperationExecutorStrategy operationExecutorStrategy = operationExecutorStrategyProvider\n                    .getOperationExecutorStrategy(operation);\n            final Object rightOperandValue = evaluateRightOperandExpression(operation, expressionContext,\n                    operation.getRightOperand());\n            shouldPersistValue = persistRightOperandResolver\n                    .shouldPersistByValue(rightOperandValue, shouldPersistValue,\n                            operationExecutorStrategy.shouldPersistOnNullValue());\n            final Object value = operationExecutorStrategy.computeNewValueForLeftOperand(operation, rightOperandValue,\n                    expressionContext,\n                    shouldPersistValue);\n            expressionContext.getInputValues().put(leftOperand.getName(), value);\n            if (log.isDebugEnabled()) {\n                log.debug(buildLogMessage(operation, rightOperandValue, expressionContext));\n            }\n        }\n        return currentUpdateStatus;\n    }\n\n    boolean shouldUpdateLeftOperandContext(final Map<SLeftOperand, LeftOperandUpdateStatus> updateLeftOperands,\n            final SLeftOperand leftOperand,\n            final LeftOperandUpdateStatus currentUpdateStatus) {\n        final LeftOperandUpdateStatus previousStatus = updateLeftOperands.get(leftOperand);\n        return previousStatus == null || !previousStatus.shouldDelete() && currentUpdateStatus.shouldDelete();\n    }\n\n    void updateLeftOperands(final Map<SLeftOperand, LeftOperandUpdateStatus> leftOperandUpdates,\n            final long leftOperandContainerId,\n            final String leftOperandContainerType, final SExpressionContext expressionContext)\n            throws SOperationExecutionException {\n        for (final Entry<SLeftOperand, LeftOperandUpdateStatus> update : leftOperandUpdates.entrySet()) {\n            final SLeftOperand leftOperand = update.getKey();\n            final LeftOperandHandler leftOperandHandler = getLeftOperandHandler(leftOperand.getType());\n            if (update.getValue().shouldUpdate()) {\n                leftOperandHandler.update(leftOperand, expressionContext.getInputValues(),\n                        expressionContext.getInputValues().get(leftOperand.getName()),\n                        leftOperandContainerId, leftOperandContainerType);\n            } else {\n                leftOperandHandler.delete(leftOperand, leftOperandContainerId, leftOperandContainerType);\n            }\n        }\n    }\n\n    private LeftOperandHandler getLeftOperandHandler(final String type) throws SOperationExecutionException {\n        final LeftOperandHandler leftOperandHandler = leftOperandHandlersMap.get(type);\n        if (leftOperandHandler == null) {\n            throw new SOperationExecutionException(\"Left operand type not found: \" + type);\n        }\n        return leftOperandHandler;\n    }\n\n    void retrieveLeftOperandsAndPutItInExpressionContextIfNotIn(final List<SOperation> operations,\n            final long leftOperandContainerId,\n            final String leftOperandContainerType,\n            final SExpressionContext expressionContext) throws SOperationExecutionException {\n\n        //if the container where we execute the operation is not the same than the container of the expression (call activity data mapping) we skip the loading of left operand\n        final String containerType = expressionContext.getContainerType();\n        final Long containerId = expressionContext.getContainerId();\n        if (containerId == null || containerType == null) {\n            return;\n        }\n        final HashMap<String, List<SLeftOperand>> leftOperandHashMap = new HashMap<>();\n        for (final SOperation operation : operations) {\n            if (!operation.getType().equals(SOperatorType.ASSIGNMENT)) {\n                // this operation will set a data, we retrieve it and put it in context\n                final SLeftOperand leftOperand = operation.getLeftOperand();\n                if (!leftOperandHashMap.containsKey(leftOperand.getType())) {\n                    leftOperandHashMap.put(leftOperand.getType(), new ArrayList<>());\n                }\n                leftOperandHashMap.get(leftOperand.getType()).add(leftOperand);\n            }\n        }\n        for (final Entry<String, List<SLeftOperand>> leftOperandByType : leftOperandHashMap.entrySet()) {\n            try {\n                getLeftOperandHandler(leftOperandByType.getKey())\n                        .loadLeftOperandInContext(leftOperandByType.getValue(), leftOperandContainerId,\n                                leftOperandContainerType, expressionContext);\n            } catch (final SBonitaReadException e) {\n                throw new SOperationExecutionException(\n                        \"Unable to retrieve value for operation \" + leftOperandByType.getValue(), e);\n            }\n        }\n\n    }\n\n    protected Object evaluateRightOperandExpression(final SOperation operation,\n            final SExpressionContext expressionContext, final SExpression sExpression)\n            throws SOperationExecutionException {\n        if (sExpression == null) {\n            return null;\n        }\n        try {\n            return expressionResolverService.evaluate(sExpression, expressionContext);\n        } catch (final ClassCastException e) {\n            throw new SOperationExecutionException(\n                    \"Unable to execute operation on \" + operation.getLeftOperand().getName()\n                            + \" with a new value which is not Serializable\",\n                    e);\n        } catch (final SBonitaException e) {\n            throw new SOperationExecutionException(e);\n        }\n    }\n\n    private String buildLogMessage(final SOperation operation, final Object operationValue,\n            final SExpressionContext expressionContext) {\n        String stb = \"Executed operation on container [id: '\" +\n                expressionContext.getContainerId() +\n                \"', type: '\" +\n                expressionContext.getContainerType() +\n                \"']. Operation: [left operand: '\" +\n                operation.getLeftOperand().getName() +\n                \"', operator: '\" +\n                operation.getOperator() +\n                \"', operation value: '\" +\n                operationValue +\n                \"']\";\n        return stb;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/impl/OperationsAnalyzer.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation.impl;\n\nimport java.util.Iterator;\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.operation.model.SLeftOperand;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.expression.ExpressionType;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Component\npublic class OperationsAnalyzer {\n\n    /**\n     * Finds the index of operation of type {@link SLeftOperand#TYPE_BUSINESS_DATA} that references the expression of\n     * type\n     * {@link ExpressionType#TYPE_BUSINESS_DATA} with given name directly on the right operand or in transitive\n     * dependencies.\n     *\n     * @param businessDataName the expression content\n     * @param fromIndex index of operation from which the analyse must begins\n     * @param operations list of operations @return the index of operation that references the expression with given\n     *        name and type.\n     */\n    public int findBusinessDataDependencyIndex(String businessDataName, int fromIndex,\n            final List<SOperation> operations) {\n        if (fromIndex >= operations.size()) {\n            return -1;\n        }\n        for (int i = fromIndex; i < operations.size(); i++) {\n            SOperation operation = operations.get(i);\n            SExpression rightOperand = operation.getRightOperand();\n            if (matches(businessDataName, operation, rightOperand)) {\n                return i;\n            }\n        }\n        return -1;\n    }\n\n    private boolean matches(final String businessDataName, final SOperation operation, final SExpression expression) {\n        if (expression == null) {\n            return false;\n        }\n        boolean matches = SLeftOperand.TYPE_BUSINESS_DATA.equals(operation.getLeftOperand().getType())\n                && businessDataName.equals(expression.getContent())\n                && ExpressionType.TYPE_BUSINESS_DATA.name().equals(expression.getExpressionType());\n        if (!matches && expression.hasDependencies()) {\n            Iterator<SExpression> iterator = expression.getDependencies().iterator();\n            while (iterator.hasNext() && !matches) {\n                matches = matches(businessDataName, operation, iterator.next());\n            }\n        }\n        return matches;\n    }\n\n    /**\n     * Calculates the next and the last indexes where the left operand located at the given index is used\n     *\n     * @param indexOfCurrentOperation index of current operation\n     * @param operations all operations\n     * @return the next and the last indexes where the left operand located at the given index is used\n     */\n    public LeftOperandIndexes calculateIndexes(int indexOfCurrentOperation, List<SOperation> operations) {\n        LeftOperandIndexes indexes = new LeftOperandIndexes();\n        indexes.setLastIndex(indexOfCurrentOperation);\n        SOperation currentOperation = operations.get(indexOfCurrentOperation);\n\n        //start from operation that follows the current one\n        for (int i = indexOfCurrentOperation + 1; i < operations.size(); i++) {\n            SOperation operation = operations.get(i);\n            if (operation.getLeftOperand().equals(currentOperation.getLeftOperand())) {\n                indexes.setLastIndex(i);\n                if (indexes.getNextIndex() == -1) {\n                    indexes.setNextIndex(i);\n                }\n            }\n        }\n        return indexes;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/impl/PersistRightOperandResolver.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation.impl;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.operation.model.SLeftOperand;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Component\npublic class PersistRightOperandResolver {\n\n    private OperationsAnalyzer operationsAnalyzer;\n\n    public PersistRightOperandResolver(final OperationsAnalyzer operationsAnalyzer) {\n        this.operationsAnalyzer = operationsAnalyzer;\n    }\n\n    /**\n     * Check if the right operand should be persisted based on its position (it's the last operation for the given left\n     * operand)\n     *\n     * @param currentIndex the index of current operation\n     * @param operations the list of all operations\n     * @return true if the right operand should be persisted; false otherwise\n     */\n    public boolean shouldPersistByPosition(int currentIndex, List<SOperation> operations) {\n        SOperation currentOperation = operations.get(currentIndex);\n        if (!SLeftOperand.TYPE_BUSINESS_DATA.equals(currentOperation.getLeftOperand().getType())) {\n            return false;\n        }\n        LeftOperandIndexes leftOperandIndexes = operationsAnalyzer.calculateIndexes(currentIndex, operations);\n        if (currentIndex == leftOperandIndexes.getLastIndex()) {\n            return true;\n        }\n        //get the index of next operation that will use the current left operand as dependency to set another business data\n        int dependencyIndex = operationsAnalyzer.findBusinessDataDependencyIndex(\n                currentOperation.getLeftOperand().getName(), currentIndex + 1, operations);\n        return dependencyIndex != -1 && dependencyIndex < leftOperandIndexes.getNextIndex();\n    }\n\n    /**\n     * Check if the right operand should be persisted based of the right operand value\n     *\n     * @param rightOperandValue the right operand value\n     * @param shouldPersistByPosition should persist based on operation position (position in the list of operations)\n     * @param shouldPersistOnNull should persist on null result\n     * @return true if the right operand should be persisted; false otherwise\n     */\n    public boolean shouldPersistByValue(Object rightOperandValue, boolean shouldPersistByPosition,\n            final boolean shouldPersistOnNull) {\n        if (rightOperandValue == null && shouldPersistByPosition) {\n            return shouldPersistOnNull;\n        }\n        return shouldPersistByPosition;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/impl/XpathUpdateQueryOperationExecutorStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation.impl;\n\nimport java.io.IOException;\nimport java.io.StringReader;\n\nimport javax.xml.XMLConstants;\nimport javax.xml.parsers.DocumentBuilder;\nimport javax.xml.parsers.DocumentBuilderFactory;\nimport javax.xml.parsers.ParserConfigurationException;\nimport javax.xml.transform.TransformerException;\nimport javax.xml.transform.TransformerFactoryConfigurationError;\nimport javax.xml.xpath.XPath;\nimport javax.xml.xpath.XPathConstants;\nimport javax.xml.xpath.XPathExpressionException;\nimport javax.xml.xpath.XPathFactory;\n\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.OperationExecutorStrategy;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.xml.DocumentManager;\nimport org.springframework.stereotype.Component;\nimport org.w3c.dom.Attr;\nimport org.w3c.dom.Document;\nimport org.w3c.dom.Element;\nimport org.w3c.dom.Node;\nimport org.w3c.dom.Text;\nimport org.xml.sax.InputSource;\nimport org.xml.sax.SAXException;\n\n/**\n * @author Zhang Bole\n * @author Matthieu Chaffotte\n * @author Baptiste Mesta\n */\n@Component\npublic class XpathUpdateQueryOperationExecutorStrategy implements OperationExecutorStrategy {\n\n    public static final String TYPE_XPATH_UPDATE_QUERY = \"XPATH_UPDATE_QUERY\";\n\n    public XpathUpdateQueryOperationExecutorStrategy() {\n    }\n\n    @Override\n    public String getOperationType() {\n        return TYPE_XPATH_UPDATE_QUERY;\n    }\n\n    private String getStringValue(final Object variableValue) {\n        if (variableValue instanceof String) {\n            return (String) variableValue;\n        }\n        return String.valueOf(variableValue);\n    }\n\n    private boolean isSetAttribute(final String xpathExpression, final Object variableValue) {\n        if (variableValue instanceof Attr) {\n            return true;\n        }\n        final String[] segments = xpathExpression.split(\"/\");\n        return segments[segments.length - 1].startsWith(\"@\");\n    }\n\n    @Override\n    public Object computeNewValueForLeftOperand(final SOperation operation, final Object value,\n            final SExpressionContext expressionContext,\n            final boolean shouldPersistValue) throws SOperationExecutionException {\n        try {\n            final String dataInstanceName = operation.getLeftOperand().getName();\n            // should be a String because the data is an xml expression\n            final String dataValue = (String) expressionContext.getInputValues().get(dataInstanceName);\n\n            final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();\n            try {\n                factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, \"\"); // security-compliant\n                factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, \"\"); // security-compliant\n            } catch (IllegalArgumentException e) {\n                //ignored, if not supported by the implementation\n            }\n            final DocumentBuilder builder = factory.newDocumentBuilder();\n            final Document document = builder.parse(new InputSource(new StringReader(dataValue)));\n            final XPath xpath = XPathFactory.newInstance().newXPath();\n            final String xpathExpression = operation.getOperator();\n            final Node node = (Node) xpath.compile(xpathExpression).evaluate(document, XPathConstants.NODE);\n            if (isSetAttribute(xpathExpression, value)) {\n                if (node == null) { // Create the attribute\n                    final String parentPath = xpathExpression.substring(0, xpathExpression.lastIndexOf('/'));\n                    final String attributeName = xpathExpression.substring(xpathExpression.lastIndexOf('/') + 2); // +1 for @\n                    final Node parentNode = (Node) xpath.compile(parentPath).evaluate(document, XPathConstants.NODE);\n                    if (parentNode instanceof Element element) {\n                        if (value instanceof String) {\n                            element.setAttribute(attributeName, getStringValue(value));\n                        } else if (value instanceof Attr) {\n                            element.setAttribute(((Attr) value).getName(),\n                                    ((Attr) value).getTextContent());\n                        }\n                    }\n                } else if (node instanceof Attr) { // Set an existing attribute\n                    if (value instanceof Attr) {\n                        node.setTextContent(((Attr) value).getTextContent());\n                    } else {\n                        node.setTextContent(getStringValue(value));\n                    }\n                } else if (node instanceof Element) { // add attribute to an element\n                    final Attr attr = (Attr) value;\n                    ((Element) node).setAttribute(attr.getName(), attr.getValue());\n                }\n            } else if (node instanceof Text) {\n                node.setTextContent(getStringValue(value));\n            } else if (node instanceof Element) {\n                Node newNode = null;\n                if (value instanceof Node) {\n                    newNode = document.importNode((Node) value, true);\n                } else if (value instanceof String) {\n                    newNode = document.importNode(\n                            DocumentManager.generateDocument(getStringValue(value)).getDocumentElement(), true);\n                }\n\n                // if (isAppend) {\n                // node.appendChild(newNode);\n                // } else { // replace\n                final Node parentNode = node.getParentNode();\n                parentNode.removeChild(node);\n                parentNode.appendChild(newNode);\n                // }\n            } else if (node == null && xpathExpression.endsWith(\"/text()\") && value instanceof String) {\n                final String parentPath = xpathExpression.substring(0, xpathExpression.lastIndexOf('/'));\n                final Node parentNode = (Node) xpath.compile(parentPath).evaluate(document, XPathConstants.NODE);\n                parentNode.appendChild(document.createTextNode(getStringValue(value)));\n            }\n            return DocumentManager.getDocumentContent(document);\n        } catch (final ParserConfigurationException | SAXException | IOException | XPathExpressionException\n                | TransformerFactoryConfigurationError | TransformerException pce) {\n            throw new SOperationExecutionException(pce);\n        }\n    }\n\n    @Override\n    public boolean shouldPersistOnNullValue() {\n        return false;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/model/SLeftOperand.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation.model;\n\nimport java.io.Serializable;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface SLeftOperand extends Serializable {\n\n    String TYPE_EXTERNAL_DATA = \"EXTERNAL_DATA\";\n\n    String TYPE_DOCUMENT = \"DOCUMENT\";\n\n    String TYPE_SEARCH_INDEX = \"SEARCH_INDEX\";\n\n    String TYPE_DATA = \"DATA\";\n\n    String TYPE_BUSINESS_DATA = \"BUSINESS_DATA\";\n\n    String getName();\n\n    String getType();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/model/SOperation.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation.model;\n\nimport java.io.Serializable;\n\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Baptiste Mesta\n */\npublic interface SOperation extends Serializable {\n\n    SLeftOperand getLeftOperand();\n\n    SOperatorType getType();\n\n    String getOperator();\n\n    SExpression getRightOperand();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/model/SOperatorType.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation.model;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Emmanuel Duchastenier\n */\npublic enum SOperatorType {\n\n    ASSIGNMENT,\n\n    JAVA_METHOD,\n\n    XPATH_UPDATE_QUERY,\n\n    DELETION\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/model/builder/SLeftOperandBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation.model.builder;\n\nimport org.bonitasoft.engine.core.operation.model.SLeftOperand;\n\n/**\n * @author Zhang Bole\n * @author Baptiste Mesta\n */\npublic interface SLeftOperandBuilder {\n\n    SLeftOperandBuilder setName(final String dataName);\n\n    SLeftOperandBuilder setType(final String type);\n\n    SLeftOperand done();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/model/builder/SLeftOperandBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation.model.builder;\n\n/**\n * @author Zhang Bole\n */\npublic interface SLeftOperandBuilderFactory {\n\n    SLeftOperandBuilder createNewInstance();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/model/builder/SOperationBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation.model.builder;\n\nimport org.bonitasoft.engine.core.operation.model.SLeftOperand;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.core.operation.model.SOperatorType;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Zhang Bole\n * @author Baptiste Mesta\n */\npublic interface SOperationBuilder {\n\n    SOperationBuilder setLeftOperand(final SLeftOperand leftOperand);\n\n    SOperationBuilder setType(final SOperatorType operatorType);\n\n    SOperationBuilder setOperator(final String operator);\n\n    SOperationBuilder setRightOperand(final SExpression rightOperand);\n\n    SOperation done();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/model/builder/SOperationBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation.model.builder;\n\n/**\n * @author Zhang Bole\n * @author Baptiste Mesta\n */\npublic interface SOperationBuilderFactory {\n\n    SOperationBuilder createNewInstance();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/model/builder/impl/SLeftOperandBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation.model.builder.impl;\n\nimport org.bonitasoft.engine.core.operation.model.builder.SLeftOperandBuilder;\nimport org.bonitasoft.engine.core.operation.model.builder.SLeftOperandBuilderFactory;\nimport org.bonitasoft.engine.core.operation.model.impl.SLeftOperandImpl;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Baptiste Mesta\n */\npublic class SLeftOperandBuilderFactoryImpl implements SLeftOperandBuilderFactory {\n\n    @Override\n    public SLeftOperandBuilder createNewInstance() {\n        final SLeftOperandImpl leftOperand = new SLeftOperandImpl();\n        return new SLeftOperandBuilderImpl(leftOperand);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/model/builder/impl/SLeftOperandBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation.model.builder.impl;\n\nimport org.bonitasoft.engine.core.operation.model.SLeftOperand;\nimport org.bonitasoft.engine.core.operation.model.builder.SLeftOperandBuilder;\nimport org.bonitasoft.engine.core.operation.model.impl.SLeftOperandImpl;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Baptiste Mesta\n */\npublic class SLeftOperandBuilderImpl implements SLeftOperandBuilder {\n\n    private final SLeftOperandImpl leftOperand;\n\n    public SLeftOperandBuilderImpl(final SLeftOperandImpl leftOperand) {\n        super();\n        this.leftOperand = leftOperand;\n    }\n\n    @Override\n    public SLeftOperandBuilder setName(final String name) {\n        leftOperand.setName(name);\n        return this;\n    }\n\n    @Override\n    public SLeftOperand done() {\n        return leftOperand;\n    }\n\n    @Override\n    public SLeftOperandBuilder setType(final String type) {\n        leftOperand.setType(type);\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/model/builder/impl/SOperationBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation.model.builder.impl;\n\nimport org.bonitasoft.engine.core.operation.model.builder.SOperationBuilder;\nimport org.bonitasoft.engine.core.operation.model.builder.SOperationBuilderFactory;\nimport org.bonitasoft.engine.core.operation.model.impl.SOperationImpl;\n\n/**\n * @author Zhang Bole\n */\npublic class SOperationBuilderFactoryImpl implements SOperationBuilderFactory {\n\n    @Override\n    public SOperationBuilder createNewInstance() {\n        final SOperationImpl operation = new SOperationImpl();\n        return new SOperationBuilderImpl(operation);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/model/builder/impl/SOperationBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation.model.builder.impl;\n\nimport org.bonitasoft.engine.core.operation.model.SLeftOperand;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.core.operation.model.SOperatorType;\nimport org.bonitasoft.engine.core.operation.model.builder.SOperationBuilder;\nimport org.bonitasoft.engine.core.operation.model.impl.SOperationImpl;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Zhang Bole\n */\npublic class SOperationBuilderImpl implements SOperationBuilder {\n\n    private final SOperationImpl operation;\n\n    public SOperationBuilderImpl(final SOperationImpl operation) {\n        super();\n        this.operation = operation;\n    }\n\n    @Override\n    public SOperationBuilder setLeftOperand(final SLeftOperand leftOperand) {\n        operation.setLeftOperand(leftOperand);\n        return this;\n    }\n\n    @Override\n    public SOperationBuilder setType(final SOperatorType operatorType) {\n        operation.setType(operatorType);\n        return this;\n    }\n\n    @Override\n    public SOperationBuilder setOperator(final String operator) {\n        operation.setOperator(operator);\n        return this;\n    }\n\n    @Override\n    public SOperationBuilder setRightOperand(final SExpression rightOperand) {\n        operation.setRightOperand(rightOperand);\n        return this;\n    }\n\n    @Override\n    public SOperation done() {\n        return operation;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/model/impl/SLeftOperandImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation.model.impl;\n\nimport org.bonitasoft.engine.core.operation.model.SLeftOperand;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Baptiste Mesta\n */\npublic class SLeftOperandImpl implements SLeftOperand {\n\n    private static final long serialVersionUID = 1L;\n\n    private String name;\n\n    private String type;\n\n    public SLeftOperandImpl() {\n        type = SLeftOperand.TYPE_DATA;\n    }\n\n    @Override\n    public String getType() {\n        return type;\n    }\n\n    public void setType(final String type) {\n        this.type = type;\n    }\n\n    public void setName(final String name) {\n        this.name = name;\n    }\n\n    @Override\n    public String getName() {\n        return name;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + (name == null ? 0 : name.hashCode());\n        result = prime * result + (type == null ? 0 : type.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        SLeftOperandImpl other = (SLeftOperandImpl) obj;\n        if (name == null) {\n            if (other.name != null) {\n                return false;\n            }\n        } else if (!name.equals(other.name)) {\n            return false;\n        }\n        if (type == null) {\n            if (other.type != null) {\n                return false;\n            }\n        } else if (!type.equals(other.type)) {\n            return false;\n        }\n        return true;\n    }\n\n    @Override\n    public String toString() {\n        return \"SLeftOperandImpl [name=\" + name + \", type=\" + type + \"]\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/model/impl/SOperationImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation.model.impl;\n\nimport org.bonitasoft.engine.core.operation.model.SLeftOperand;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.core.operation.model.SOperatorType;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Baptiste Mesta\n */\npublic class SOperationImpl implements SOperation {\n\n    private static final long serialVersionUID = 1L;\n\n    private SLeftOperand leftOperand;\n\n    private SOperatorType type;\n\n    private String operator;\n\n    private SExpression rightOperand;\n\n    public void setLeftOperand(final SLeftOperand leftOperand) {\n        this.leftOperand = leftOperand;\n    }\n\n    public void setOperator(final String operator) {\n        this.operator = operator;\n    }\n\n    public void setType(final SOperatorType type) {\n        this.type = type;\n    }\n\n    public void setRightOperand(final SExpression rightOperand) {\n        this.rightOperand = rightOperand;\n    }\n\n    @Override\n    public SLeftOperand getLeftOperand() {\n        return leftOperand;\n    }\n\n    @Override\n    public SOperatorType getType() {\n        return type;\n    }\n\n    @Override\n    public String getOperator() {\n        return operator;\n    }\n\n    @Override\n    public SExpression getRightOperand() {\n        return rightOperand;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + (leftOperand == null ? 0 : leftOperand.hashCode());\n        result = prime * result + (operator == null ? 0 : operator.hashCode());\n        result = prime * result + (rightOperand == null ? 0 : rightOperand.hashCode());\n        result = prime * result + (type == null ? 0 : type.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        final SOperationImpl other = (SOperationImpl) obj;\n        if (leftOperand == null) {\n            if (other.leftOperand != null) {\n                return false;\n            }\n        } else if (!leftOperand.equals(other.leftOperand)) {\n            return false;\n        }\n        if (operator == null) {\n            if (other.operator != null) {\n                return false;\n            }\n        } else if (!operator.equals(other.operator)) {\n            return false;\n        }\n        if (rightOperand == null) {\n            if (other.rightOperand != null) {\n                return false;\n            }\n        } else if (!rightOperand.equals(other.rightOperand)) {\n            return false;\n        }\n        if (type != other.type) {\n            return false;\n        }\n        return true;\n    }\n\n    @Override\n    public String toString() {\n        return \"SOperationImpl [leftOperand=\" + leftOperand + \", type=\" + type + \", operator=\" + operator\n                + \", rightOperand=\" + rightOperand + \"]\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/ProcessDefinitionService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.process.ActivationState;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.core.process.definition.exception.SDeletingEnabledProcessException;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionException;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDeletionException;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDeploymentInfoUpdateException;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDisablementException;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessEnablementException;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Matthieu Chaffotte\n * @author Yanyan Liu\n * @author Celine Souchet\n * @author Arthur Freycon\n * @since 6.0\n */\npublic interface ProcessDefinitionService {\n\n    String PROCESSDEFINITION = \"PROCESSDEFINITION\";\n    String PROCESSDEFINITION_CONTENT = \"PROCESSDEFINITION_CONTENT\";\n\n    String PROCESSDEFINITION_IS_ENABLED = \"PROCESSDEFINITION_IS_ENABLED\";\n\n    String PROCESSDEFINITION_IS_DISABLED = \"PROCESSDEFINITION_IS_DISABLED\";\n\n    String PROCESSDEFINITION_DEPLOY_INFO = \"PROCESSDEFINITION_DEPLOY_INFO\";\n\n    String PROCESSDEFINITION_IS_RESOLVED = \"PROCESSDEFINITION_IS_RESOLVED\";\n\n    String PROCESSDEFINITION_IS_UNRESOLVED = \"PROCESSDEFINITION_IS_UNRESOLVED\";\n\n    static String PROCESS_CACHE_NAME = \"_PROCESSDEF\";\n\n    String UNCATEGORIZED_SUFFIX = \"Uncategorized\";\n\n    String UNCATEGORIZED_SUPERVISED_BY_SUFFIX = \"UncategorizedAndWithSupervisor\";\n\n    String UNCATEGORIZED_USERCANSTART_SUFFIX = \"UncategorizedUserCanStart\";\n\n    String WHOCANSTART_PROCESS_SUFFIX = \"WhoCanStartProcess\";\n\n    String STARTED_BY_SUFFIX = \"StartedBy\";\n\n    String PROCESS_DEFINITION_ID = \"processId\";\n\n    String USER_ID = \"userId\";\n\n    String ROLE_ID = \"roleId\";\n\n    String GROUP_ID = \"groupId\";\n\n    /**\n     * Store the processDefinition to file system and its deploy info to DB.\n     *\n     * @param designProcessDefinition\n     *        the processDefinition will be stored\n     * @return the definition will an id\n     * @throws SProcessDefinitionException\n     */\n    SProcessDefinition store(DesignProcessDefinition designProcessDefinition) throws SProcessDefinitionException;\n\n    /**\n     * Get processDefinition by its id\n     *\n     * @param processDefinitionId\n     *        identifier of processDefinition\n     * @return the processDefinition corresponding to the parameter processId\n     * @throws SProcessDefinitionNotFoundException\n     *         error thrown if no process definition found\n     * @throws SBonitaReadException\n     */\n    SProcessDefinition getProcessDefinition(long processDefinitionId)\n            throws SProcessDefinitionNotFoundException, SBonitaReadException;\n\n    /**\n     * Get processDefinition by its id, if it is enabled. Throws SProcessDefinitionException otherwise.\n     *\n     * @param processDefinitionId\n     *        The identifier of processDefinition\n     * @return The processDefinition corresponding to the parameter processId\n     * @throws SBonitaReadException\n     * @throws SProcessDefinitionException if process is not enabled.\n     * @since 6.4.0\n     */\n    SProcessDefinition getProcessDefinitionIfIsEnabled(long processDefinitionId)\n            throws SBonitaReadException, SProcessDefinitionException;\n\n    /**\n     * Get deployment info of the process definition having the id given in parameter\n     *\n     * @param processId\n     *        id of the process definition on which we want deployment information\n     * @return an SProcessDefinitionDeployInfo object to the process definition\n     * @throws SProcessDefinitionNotFoundException\n     *         error thrown if no process definition found\n     * @throws SBonitaReadException\n     */\n    SProcessDefinitionDeployInfo getProcessDeploymentInfo(long processId)\n            throws SProcessDefinitionNotFoundException, SBonitaReadException;\n\n    /**\n     * Delete the id specified process definition and its deploy info\n     *\n     * @param processId\n     *        identifier of processDefinition\n     * @throws SProcessDefinitionNotFoundException\n     *         error thrown if no process definition found\n     * @throws SProcessDeletionException\n     * @throws SDeletingEnabledProcessException\n     *         error throw if the process still enabled\n     */\n    void delete(long processId)\n            throws SProcessDefinitionNotFoundException, SProcessDeletionException, SDeletingEnabledProcessException;\n\n    /**\n     * Get process definition deploy info in a specific interval with order, this can be used for pagination\n     *\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param numberPerPage\n     *        Number of result we want to get. Maximum number of result returned\n     * @param field\n     *        the field user to do order\n     * @param order\n     *        ASC or DESC\n     * @return a list of SProcessDefinitionDeployInfo object\n     * @throws SBonitaReadException\n     */\n    List<SProcessDefinitionDeployInfo> getProcessDeploymentInfos(int fromIndex, int numberPerPage, String field,\n            OrderByType order)\n            throws SBonitaReadException;\n\n    /**\n     * Enable the specific process definition, set the process as ENABLED when it is in RESOLVED state.\n     * If process is already enabled, this method fails with SProcessEnablementException\n     *\n     * @param processId\n     *        identifier of processDefinition\n     * @throws SProcessDefinitionNotFoundException\n     *         error thrown if no process definition found for the given processId\n     * @throws SProcessEnablementException if process is already enabled or if process is not resolved\n     * @see #resolveProcess(long) Resolving a process\n     * @see #enableProcess(long, boolean)\n     */\n    void enableProcessDeploymentInfo(long processId)\n            throws SProcessDefinitionNotFoundException, SProcessEnablementException;\n\n    /**\n     * Enable the specific process definition, when it is in RESOLVED state.\n     *\n     * @param processId\n     *        identifier of processDefinition\n     * @param failIfAlreadyEnabled should we fail if process is already enabled?\n     * @throws SProcessDefinitionNotFoundException\n     *         error thrown if no process definition found for the given processId\n     * @throws SProcessEnablementException if process is already enabled and failIfAlreadyEnabled == true, or if process\n     *         is not resolved\n     */\n    void enableProcess(long processId, boolean failIfAlreadyEnabled)\n            throws SProcessDefinitionNotFoundException, SProcessEnablementException;\n\n    /**\n     * Disable the process passed as parameter.\n     * If process is already disabled, this method fails with SProcessDisablementException.\n     *\n     * @param processId\n     *        identifier of process definition\n     * @throws SProcessDefinitionNotFoundException\n     *         error thrown if no process definition found for the given processId\n     * @throws SProcessDisablementException if process is already disabled\n     * @see #disableProcess(long, boolean)\n     */\n    void disableProcessDeploymentInfo(long processId)\n            throws SProcessDefinitionNotFoundException, SProcessDisablementException;\n\n    /**\n     * Disable the process passed as parameter.\n     *\n     * @param processId\n     *        identifier of process definition\n     * @param failIfAlreadyDisabled should we fail if process is already disabled?\n     * @throws SProcessDefinitionNotFoundException\n     *         error thrown if no process definition found for the given processId\n     * @throws SProcessDisablementException if process is already disabled and failIfAlreadyDisabled == true\n     */\n    void disableProcess(long processId, boolean failIfAlreadyDisabled)\n            throws SProcessDefinitionNotFoundException, SProcessDisablementException;\n\n    /**\n     * set the process as RESOLVED when it is in UNRESOLVED state\n     *\n     * @param processId\n     *        identifier of process definition\n     * @throws SProcessDefinitionNotFoundException\n     *         error thrown if no process definition found for the given processId\n     * @throws SProcessDisablementException\n     */\n    void resolveProcess(long processId) throws SProcessDefinitionNotFoundException, SProcessDisablementException;\n\n    /**\n     * Gets how many processes are in the given state.\n     *\n     * @param activationState\n     *        the activation state\n     * @return number of processes are in the given state or 0;\n     * @throws SBonitaReadException\n     */\n    long getNumberOfProcessDeploymentInfosByActivationState(ActivationState activationState)\n            throws SBonitaReadException;\n\n    /**\n     * Gets how many processes are defined.\n     *\n     * @return the number of process definitions;\n     * @throws SBonitaReadException\n     *         occurs when an exception is thrown during method execution\n     */\n    long getNumberOfProcessDeploymentInfos() throws SBonitaReadException;\n\n    /**\n     * Get the process definition identifiers in the given state.\n     *\n     * @param activationState\n     *        the activation state\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param numberOfResult\n     *        Number of result we want to get. Maximum number of result returned\n     * @return the paginated list of process definition identifiers or an empty list\n     * @throws SBonitaReadException\n     */\n    List<Long> getProcessDefinitionIds(ActivationState activationState, int fromIndex, int numberOfResult)\n            throws SBonitaReadException;\n\n    /**\n     * Get the process definition identifiers.\n     * * @param fromIndex\n     * Index of the record to be retrieved from. First record has index 0\n     *\n     * @param numberOfResult\n     *        Number of result we want to get. Maximum number of result returned\n     * @return the paginated list of process definition identifiers or an empty list\n     * @throws SBonitaReadException\n     */\n    List<Long> getProcessDefinitionIds(int fromIndex, int numberOfResult) throws SBonitaReadException;\n\n    /**\n     * Get target flow node for the given source flow node in the specific process\n     *\n     * @param definition\n     *        the process definition containing source flow node\n     * @param source\n     *        a flow node in process definition\n     * @return target flow node of the given source\n     */\n    SFlowNodeDefinition getNextFlowNode(SProcessDefinition definition, String source);\n\n    /**\n     * get sub set of processDefinitionDeployInfos in specific order\n     *\n     * @param processIds\n     *        identifiers of process definition\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param numberOfProcesses\n     *        Number of result we want to get. Maximum number of result returned\n     * @param field\n     *        filed user to do order\n     * @param order\n     *        ASC or DESC\n     * @return a list of SProcessDefinitionDeployInfo objects\n     * @throws SProcessDefinitionNotFoundException\n     * @throws SBonitaReadException\n     */\n    List<SProcessDefinitionDeployInfo> getProcessDeploymentInfos(List<Long> processIds, int fromIndex,\n            int numberOfProcesses, String field, OrderByType order)\n            throws SProcessDefinitionNotFoundException, SBonitaReadException;\n\n    List<SProcessDefinitionDeployInfo> getProcessDeploymentInfos(List<Long> processIds)\n            throws SProcessDefinitionNotFoundException,\n            SBonitaReadException;\n\n    /**\n     * Get the processDefinitionId of the most recent version of the process\n     *\n     * @param processName\n     *        name of process definition\n     * @return the latest process definition\n     * @throws SBonitaReadException\n     */\n    long getLatestProcessDefinitionId(String processName)\n            throws SBonitaReadException, SProcessDefinitionNotFoundException;\n\n    /**\n     * Get the processDefinitionId by name and version\n     *\n     * @param name\n     *        name of process definition\n     * @param version\n     *        version or process definition\n     * @return identifier of process definition\n     * @throws SBonitaReadException\n     * @throws SProcessDefinitionNotFoundException\n     */\n    long getProcessDefinitionId(String name, String version)\n            throws SBonitaReadException, SProcessDefinitionNotFoundException;\n\n    /**\n     * Update deployment info of the process definition having the id given in parameter\n     *\n     * @param processId\n     *        identifier of process deploy info\n     * @param descriptor\n     *        update description\n     * @throws SProcessDefinitionNotFoundException\n     *         error thrown when no process deploy info found with the give processId\n     * @throws SProcessDeploymentInfoUpdateException\n     */\n    SProcessDefinitionDeployInfo updateProcessDefinitionDeployInfo(long processId, EntityUpdateDescriptor descriptor)\n            throws SProcessDefinitionNotFoundException,\n            SProcessDeploymentInfoUpdateException;\n\n    /**\n     * Search all process deploy info started by the specific user\n     *\n     * @param startedBy\n     *        the name of user who started the process\n     * @param searchOptions\n     *        a QueryOptions object containing some query conditions\n     * @return a list of SProcessDefinitionDeployInfo objects\n     * @throws SBonitaReadException\n     */\n    List<SProcessDefinitionDeployInfo> searchProcessDeploymentInfosStartedBy(long startedBy, QueryOptions searchOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Get number of all process deploy info started by the specific user\n     *\n     * @param startedBy\n     *        the name of user who started the process\n     * @param countOptions\n     *        a QueryOptions object containing some query conditions\n     * @return number of all process deploy info to the criteria\n     * @throws SBonitaReadException\n     */\n    long getNumberOfProcessDeploymentInfosStartedBy(long startedBy, QueryOptions countOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Search all process definition deploy infos according to the specific search criteria\n     *\n     * @param searchOptions\n     *        a QueryOptions object containing search criteria\n     * @return a list of SProcessDefinitionDeployInfo object\n     * @throws SBonitaReadException\n     */\n    List<SProcessDefinitionDeployInfo> searchProcessDeploymentInfos(QueryOptions searchOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Get number of all process definition deploy infos according to the specific search criteria\n     *\n     * @param countOptions\n     *        a QueryOptions object containing query criteria\n     * @return number of all process definition deploy infos corresponding to the criteria\n     * @throws SBonitaReadException\n     */\n    long getNumberOfProcessDeploymentInfos(QueryOptions countOptions) throws SBonitaReadException;\n\n    /**\n     * Get total number of uncategorized process definitions by given query criteria\n     *\n     * @param countOptions\n     *        a QueryOptions object containing query criteria\n     * @return total number of uncategorized process definitions suit to query criteria\n     * @throws SBonitaReadException\n     */\n    long getNumberOfUncategorizedProcessDeploymentInfos(QueryOptions countOptions) throws SBonitaReadException;\n\n    /**\n     * Get total number of uncategorized process definitions by given query criteria for specific supervisor\n     *\n     * @param userId\n     *        identifier of a supervisor user\n     * @param countOptions\n     *        a QueryOptions object containing query criteria\n     * @return number of uncategorized process definitions managed by the specific supervisor\n     * @throws SBonitaReadException\n     */\n    long getNumberOfUncategorizedProcessDeploymentInfosSupervisedBy(long userId, QueryOptions countOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Search all uncategorized process definitions according to the search criteria.\n     *\n     * @param searchOptions\n     *        a QueryOptions object containing query criteria\n     * @return a list of SProcessDefinitionDeployInfo objects\n     * @throws SBonitaReadException\n     */\n    List<SProcessDefinitionDeployInfo> searchUncategorizedProcessDeploymentInfos(QueryOptions searchOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Search all process definitions for a specific category.\n     *\n     * @param categoryId\n     *        Identifier of the category\n     * @param queryOptions\n     *        a QueryOptions object containing query criteria\n     * @return a list of SProcessDefinitionDeployInfo objects\n     * @throws SBonitaReadException\n     */\n    List<SProcessDefinitionDeployInfo> searchProcessDeploymentInfosOfCategory(long categoryId,\n            QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * Search all uncategorized process definitions by given query criteria for specific supervisor\n     *\n     * @param userId\n     *        identifier of a supervisor user\n     * @param searchOptions\n     *        a QueryOptions object containing query criteria\n     * @return a list of SProcessDefinitionDeployInfo object\n     * @throws SBonitaReadException\n     */\n    List<SProcessDefinitionDeployInfo> searchUncategorizedProcessDeploymentInfosSupervisedBy(long userId,\n            QueryOptions searchOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Search all process definitions for the specific user who can start\n     *\n     * @param userId\n     *        identifier of user\n     * @param searchOptions\n     *        a QueryOptions object containing query criteria\n     * @return a list of SProcessDefinitionDeployInfo objects\n     * @throws SBonitaReadException\n     */\n    List<SProcessDefinitionDeployInfo> searchProcessDeploymentInfosCanBeStartedBy(long userId,\n            QueryOptions searchOptions) throws SBonitaReadException;\n\n    /**\n     * Get number of all process definitions for the specific user who can start\n     *\n     * @param userId\n     *        identifier of user\n     * @param countOptions\n     *        a QueryOptions object containing query criteria\n     * @return number of all process definitions for the specific user who can start\n     * @throws SBonitaReadException\n     */\n    long getNumberOfProcessDeploymentInfosCanBeStartedBy(long userId, QueryOptions countOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Search all process definitions for the users managed by specific manager, or manager who can start\n     *\n     * @param managerUserId\n     *        identifier of manager\n     * @param searchOptions\n     *        a QueryOptions object containing query criteria\n     * @return a list of SProcessDefinitionDeployInfo objects\n     * @throws SBonitaReadException\n     */\n    List<SProcessDefinitionDeployInfo> searchProcessDeploymentInfosCanBeStartedByUsersManagedBy(long managerUserId,\n            QueryOptions searchOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Get number of all process definitions for the users managed by specific manager, or manager who can start\n     *\n     * @param managerUserId\n     *        identifier of manager\n     * @param countOptions\n     *        a QueryOptions object containing query criteria\n     * @return Number of all process definitions for the users managed by specific manager, or manager who can start\n     * @throws SBonitaReadException\n     */\n    long getNumberOfProcessDeploymentInfosCanBeStartedByUsersManagedBy(long managerUserId, QueryOptions countOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Search all process definitions for the specific user who can perform the \"querySuffix\" specified action\n     *\n     * @param userId\n     *        identifier of user\n     * @param searchOptions\n     *        a QueryOptions object containing query criteria\n     * @param querySuffix\n     *        query suffix to specify the thing the user can do, it can be \"UserSupervised\" or \"UserCanStart\"\n     * @return a list of SProcessDefinitionDeployInfo object\n     * @throws SBonitaReadException\n     */\n    List<SProcessDefinitionDeployInfo> searchProcessDeploymentInfos(long userId, QueryOptions searchOptions,\n            String querySuffix) throws SBonitaReadException;\n\n    /**\n     * Get total number of process definitions for the specific user who can perform the \"querySuffix\" specified action\n     *\n     * @param userId\n     *        identifier of user\n     * @param countOptions\n     *        a QueryOptions object containing query criteria\n     * @param querySuffix\n     *        query suffix to specify the thing the user can do, it can be \"UserSupervised\" or \"UserCanStart\"\n     * @return number of process definitions for the specific user with specific action\n     * @throws SBonitaReadException\n     */\n    long getNumberOfProcessDeploymentInfos(long userId, QueryOptions countOptions, String querySuffix)\n            throws SBonitaReadException;\n\n    /**\n     * Search all uncategorized process definitions for the specific user who can start\n     *\n     * @param userId\n     *        identifier of user\n     * @param searchOptions\n     *        a QueryOptions object containing query criteria\n     * @return a list of SProcessDefinitionDeployInfo object\n     * @throws SBonitaReadException\n     */\n    List<SProcessDefinitionDeployInfo> searchUncategorizedProcessDeploymentInfosCanBeStartedBy(long userId,\n            QueryOptions searchOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Get total number of uncategorized process definitions for the specific user who can start\n     *\n     * @param userId\n     *        identifier of user\n     * @param countOptions\n     *        a QueryOptions object containing query criteria\n     * @return number of uncategorized process definitions for the specific user who can start\n     * @throws SBonitaReadException\n     */\n    long getNumberOfUncategorizedProcessDeploymentInfosCanBeStartedBy(long userId, QueryOptions countOptions)\n            throws SBonitaReadException;\n\n    /**\n     * A list of SProcessDefinitionDeployInfos for the specific processInstances\n     *\n     * @param processInstanceIds\n     *        identifier of process instances\n     * @return a map containing identifiers of process instance and the corresponding SProcessDefinitionDeployInfo\n     *         object\n     * @throws SBonitaReadException\n     */\n    Map<Long, SProcessDefinitionDeployInfo> getProcessDeploymentInfosFromProcessInstanceIds(\n            List<Long> processInstanceIds) throws SBonitaReadException;\n\n    /**\n     * Get A list of SProcessDefinitionDeployInfos for the specific archived processInstances\n     *\n     * @param archivedProcessInstantsIds\n     *        identifiers of archived processInstance\n     * @return a map containing identifiers of archived process instance and the corresponding\n     *         SProcessDefinitionDeployInfo object\n     * @throws SBonitaReadException\n     */\n    Map<Long, SProcessDefinitionDeployInfo> getProcessDeploymentInfosFromArchivedProcessInstanceIds(\n            List<Long> archivedProcessInstantsIds)\n            throws SBonitaReadException;\n\n    /**\n     * Get A list of SProcessDefinitionDeployInfos unrelated to the specific category\n     *\n     * @param categoryId\n     * @param pagingCriterion\n     * @param numberPerPage\n     * @param pageIndex\n     * @return A list of SProcessDefinitionDeployInfos unrelated to the specific category\n     * @throws SBonitaReadException\n     */\n    List<SProcessDefinitionDeployInfo> getProcessDeploymentInfosUnrelatedToCategory(long categoryId, int pageIndex,\n            int numberPerPage,\n            ProcessDeploymentInfoCriterion pagingCriterion) throws SBonitaReadException;\n\n    /**\n     * Get number of SProcessDefinitionDeployInfos unrelated to the specific category\n     *\n     * @param categoryId\n     * @return Number of SProcessDefinitionDeployInfos unrelated to the specific category\n     * @throws SBonitaReadException\n     */\n    Long getNumberOfProcessDeploymentInfosUnrelatedToCategory(long categoryId) throws SBonitaReadException;\n\n    /**\n     * Get process definition deploy info in a specific interval with order, this can be used for pagination\n     *\n     * @param queryOptions\n     *        object containing query criteria\n     * @return a list of SProcessDefinitionDeployInfo corresponding to the criteria\n     * @throws SBonitaReadException\n     */\n    List<SProcessDefinitionDeployInfo> getProcessDeploymentInfos(QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * List all processes that contain at least one task which actor is mapped only to the specified group.\n     *\n     * @param groupId\n     *        the Id of the group from which to retrieve the processes with tasks only it can do.\n     * @param queryOptions\n     *        object containing query criteria\n     * @return the list of matching processes, as a List of <code>SProcessDefinitionDeployInfo</code>\n     * @throws SBonitaReadException\n     *         in case a read problem occurs\n     */\n    List<SProcessDefinitionDeployInfo> getProcessDeploymentInfosWithActorOnlyForGroup(long groupId,\n            QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    /**\n     * List all processes that contain at least one task which actor is mapped only to the specified groups.\n     *\n     * @param groupIds\n     *        the Ids of the groups from which to retrieve the processes with tasks only they can do.\n     * @param queryOptions\n     *        object containing query criteria\n     * @return the list of matching processes, as a List of <code>SProcessDefinitionDeployInfo</code>\n     * @throws SBonitaReadException\n     *         in case a read problem occurs\n     */\n    List<SProcessDefinitionDeployInfo> getProcessDeploymentInfosWithActorOnlyForGroups(List<Long> groupIds,\n            QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    /**\n     * List all processes that contain at least one task which actor is mapped only to the specified role.\n     *\n     * @param roleId\n     *        the Id of the role from which to retrieve the processes with tasks only it can do.\n     * @param queryOptions\n     *        object containing query criteria\n     * @return the list of matching processes, as a List of <code>SProcessDefinitionDeployInfo</code>\n     * @throws SBonitaReadException\n     *         in case a read problem occurs\n     */\n    List<SProcessDefinitionDeployInfo> getProcessDeploymentInfosWithActorOnlyForRole(long roleId,\n            QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    /**\n     * List all processes that contain at least one task which actor is mapped only to the specified roles.\n     *\n     * @param roleIds\n     *        the Ids of the roles from which to retrieve the processes with tasks only they can do.\n     * @param queryOptions\n     *        object containing query criteria\n     * @return the list of matching processes, as a List of <code>SProcessDefinitionDeployInfo</code>\n     * @throws SBonitaReadException\n     *         in case a read problem occurs\n     */\n    List<SProcessDefinitionDeployInfo> getProcessDeploymentInfosWithActorOnlyForRoles(List<Long> roleIds,\n            QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    /**\n     * List all processes that contain at least one task which actor is mapped only to the specified user.\n     *\n     * @param userId\n     *        the Id of the user from which to retrieve the processes with tasks only he / she can do.\n     * @param queryOptions\n     *        object containing query criteria\n     * @return the list of matching processes, as a List of <code>SProcessDefinitionDeployInfo</code>\n     * @throws SBonitaReadException\n     *         in case a read problem occurs\n     */\n    List<SProcessDefinitionDeployInfo> getProcessDeploymentInfosWithActorOnlyForUser(long userId,\n            QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    /**\n     * List all processes that contain at least one task which actor is mapped only to the specified users.\n     *\n     * @param userIds\n     *        the Ids of the users from which to retrieve the processes with tasks only they can do.\n     * @param queryOptions\n     *        object containing query criteria\n     * @return the list of matching processes, as a List of <code>SProcessDefinitionDeployInfo</code>\n     * @throws SBonitaReadException\n     *         in case a read problem occurs\n     */\n    List<SProcessDefinitionDeployInfo> getProcessDeploymentInfosWithActorOnlyForUsers(List<Long> userIds,\n            QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Get total number of users according to specific query options, and who can start the given process definition\n     *\n     * @param processDefinitionId\n     *        Identifier of the process definition\n     * @param queryOptions\n     *        The QueryOptions object containing some query conditions\n     * @return\n     * @throws SBonitaReadException\n     */\n    long getNumberOfUsersWhoCanStartProcessDeploymentInfo(long processDefinitionId, QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Search users according to specific query options, and who can start the given process definition\n     *\n     * @param processDefinitionId\n     *        Identifier of the process definition\n     * @param queryOptions\n     *        The QueryOptions object containing some query conditions\n     * @return\n     * @throws SBonitaReadException\n     */\n    List<SUser> searchUsersWhoCanStartProcessDeploymentInfo(long processDefinitionId, QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Get the total number of the process definitions that have one or more human tasks assigned/pending for a specific\n     * user.\n     * The tasks are in stable state, not in terminal/executing state.\n     *\n     * @param userId\n     *        The identifier of the user.\n     * @param queryOptions\n     *        The QueryOptions object containing some query conditions\n     * @return The number of the process definition\n     * @throws SBonitaReadException\n     */\n    long getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(long userId, QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Search all process definitions that have one or more human tasks assigned/pending for a specific user.\n     * The tasks are in stable state, not in terminal/executing state.\n     *\n     * @param userId\n     *        The identifier of the user.\n     * @param queryOptions\n     *        The QueryOptions object containing some query conditions\n     * @return The list of process definitions\n     * @throws SBonitaReadException\n     */\n    List<SProcessDefinitionDeployInfo> searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(long userId,\n            QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Get the total number of the process definitions supervised by a specific user, that have instances with one or\n     * more human tasks assigned/pending.\n     * The tasks are in stable state, not in terminal/executing state.\n     *\n     * @param userId\n     *        The identifier of the user.\n     * @param queryOptions\n     *        The QueryOptions object containing some query conditions\n     * @return The number of the process definition\n     * @throws SBonitaReadException\n     *         if an exception occurs when getting the process deployment information.\n     * @since 6.3.3\n     */\n    long getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(long userId,\n            QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * Search all process definitions supervised by a specific user, that have instances with one or more human tasks\n     * assigned/pending.\n     * The tasks are in stable state, not in terminal/executing state.\n     *\n     * @param userId\n     *        The identifier of the user.\n     * @param queryOptions\n     *        The QueryOptions object containing some query conditions\n     * @return The list of process definitions\n     * @throws SBonitaReadException\n     *         if an exception occurs when getting the process deployment information.\n     * @since 6.3.3\n     */\n    List<SProcessDefinitionDeployInfo> searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(\n            long userId, QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Get the total number of the process definitions that have instances with one or more human tasks\n     * assigned/pending.\n     * The tasks are in stable state, not in terminal/executing state.\n     *\n     * @param queryOptions\n     *        The QueryOptions object containing some query conditions\n     * @return The number of the process definition\n     * @throws SBonitaReadException\n     *         if an exception occurs when getting the process deployment information.\n     * @since 6.3.3\n     */\n    long getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasks(QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Search all process definitions that have instances with one or more human tasks assigned/pending.\n     * The tasks are in stable state, not in terminal/executing state.\n     *\n     * @param queryOptions\n     *        The QueryOptions object containing some query conditions\n     * @return The list of process definitions\n     * @throws SBonitaReadException\n     *         if an exception occurs when getting the process deployment information.\n     * @since 6.3.3\n     */\n    List<SProcessDefinitionDeployInfo> searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks(\n            QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * Updates the content of an Expression, for a given process definition. Any further use of this expresssion will\n     * then use the new content, as if it was\n     * designed like this in the first place.\n     *\n     * @param processDefinitionId the ID of the process definition on which the Expression content will be updated.\n     * @param expressionDefinitionId the ID of the expression definition to update\n     * @param content the new String content of the expression\n     * @throws SProcessDefinitionNotFoundException if the referenced process definition does not exist.\n     * @throws SObjectModificationException if the update cannot be performed successfully.\n     */\n    void updateExpressionContent(long processDefinitionId, long expressionDefinitionId, String content)\n            throws SProcessDefinitionNotFoundException,\n            SObjectModificationException;\n\n    /**\n     * Returns a specific process definition that include informations such as tasks definition, actors...\n     *\n     * @param processDefinitionId\n     *        Identifier of process definition\n     * @return The corresponding process definition with informations.\n     * @throws SProcessDefinitionNotFoundException\n     *         If the process definition doesn't exist.\n     * @throws SBonitaReadException\n     *         If the process definition design cannot be read\n     * @since 7.0\n     */\n    DesignProcessDefinition getDesignProcessDefinition(long processDefinitionId)\n            throws SProcessDefinitionNotFoundException, SBonitaReadException;\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/ProcessDefinitionServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition;\n\nimport static org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo.*;\n\nimport java.io.IOException;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.UUID;\n\nimport org.bonitasoft.engine.bpm.bar.ProcessDefinitionBARContribution;\nimport org.bonitasoft.engine.bpm.process.ActivationState;\nimport org.bonitasoft.engine.bpm.process.ConfigurationState;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion;\nimport org.bonitasoft.engine.bpm.process.impl.internal.ExpressionFinder;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.cache.CacheService;\nimport org.bonitasoft.engine.cache.SCacheException;\nimport org.bonitasoft.engine.commons.ClassReflector;\nimport org.bonitasoft.engine.commons.NullCheckingUtil;\nimport org.bonitasoft.engine.commons.Pair;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.commons.exceptions.SReflectException;\nimport org.bonitasoft.engine.core.process.definition.exception.SDeletingEnabledProcessException;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionException;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDeletionException;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDeploymentInfoUpdateException;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDisablementException;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessEnablementException;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDesignContent;\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionBuilderFactory;\nimport org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionDeployInfoUpdateBuilderFactory;\nimport org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionLogBuilder;\nimport org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionLogBuilderFactory;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionType;\nimport org.bonitasoft.engine.expression.impl.ExpressionImpl;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity;\nimport org.bonitasoft.engine.queriablelogger.model.builder.ActionType;\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Zhao Na\n * @author Yanyan Liu\n * @author Hongwen Zang\n * @author Celine Souchet\n * @author Arthur Freycon\n */\npublic class ProcessDefinitionServiceImpl implements ProcessDefinitionService {\n\n    private final Recorder recorder;\n    private final ReadPersistenceService persistenceService;\n    private final SessionService sessionService;\n    private final ReadSessionAccessor sessionAccessor;\n    private final QueriableLoggerService queriableLoggerService;\n    private final CacheService cacheService;\n    protected ProcessDefinitionBARContribution processDefinitionBARContribution;\n\n    public ProcessDefinitionServiceImpl(final Recorder recorder, final ReadPersistenceService persistenceService,\n            final SessionService sessionService,\n            final ReadSessionAccessor sessionAccessor, final QueriableLoggerService queriableLoggerService,\n            CacheService cacheService) {\n        this.recorder = recorder;\n        this.persistenceService = persistenceService;\n        this.sessionService = sessionService;\n        this.sessionAccessor = sessionAccessor;\n        this.queriableLoggerService = queriableLoggerService;\n        this.cacheService = cacheService;\n        processDefinitionBARContribution = new ProcessDefinitionBARContribution();\n    }\n\n    @Override\n    public void delete(final long processId)\n            throws SProcessDefinitionNotFoundException, SProcessDeletionException, SDeletingEnabledProcessException {\n        final SProcessDefinitionLogBuilder logBuilder = getQueriableLog(ActionType.DELETED,\n                \"Deleting a Process definition\");\n        final SProcessDefinitionDeployInfo processDefinitionDeployInfo;\n        try {\n            processDefinitionDeployInfo = getProcessDeploymentInfo(processId);\n            if (ActivationState.ENABLED.name().equals(processDefinitionDeployInfo.getActivationState())) {\n                throw new SDeletingEnabledProcessException(\"Process is enabled.\", processDefinitionDeployInfo);\n            }\n        } catch (final SBonitaReadException e) {\n            log(processId, SQueriableLog.STATUS_FAIL, logBuilder, \"delete\");\n            throw new SProcessDeletionException(e, processId);\n        }\n\n        try {\n            recorder.recordDelete(new DeleteRecord(processDefinitionDeployInfo), PROCESSDEFINITION);\n            log(processId, SQueriableLog.STATUS_OK, logBuilder, \"delete\");\n        } catch (final SRecorderException e) {\n            log(processId, SQueriableLog.STATUS_FAIL, logBuilder, \"delete\");\n            throw new SProcessDeletionException(e, processDefinitionDeployInfo);\n        }\n    }\n\n    @Override\n    public void disableProcessDeploymentInfo(final long processId)\n            throws SProcessDefinitionNotFoundException, SProcessDisablementException {\n        disableProcess(processId, true);\n    }\n\n    @Override\n    public void disableProcess(long processId, boolean failIfAlreadyDisabled)\n            throws SProcessDefinitionNotFoundException, SProcessDisablementException {\n        SProcessDefinitionDeployInfo processDefinitionDeployInfo;\n        try {\n            processDefinitionDeployInfo = getProcessDeploymentInfo(processId);\n        } catch (final SBonitaReadException e) {\n            throw new SProcessDisablementException(e);\n        }\n        if (failIfAlreadyDisabled\n                && ActivationState.DISABLED.name().equals(processDefinitionDeployInfo.getActivationState())) {\n            throw new SProcessDisablementException(\"Process \" + processDefinitionDeployInfo.getName() + \" with version \"\n                    + processDefinitionDeployInfo.getVersion() + \" is already disabled\");\n        }\n\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        descriptor.addField(SProcessDefinitionDeployInfo.ACTIVATION_STATE_KEY, ActivationState.DISABLED.name());\n\n        final SPersistenceLogBuilder logBuilder = getQueriableLog(ActionType.UPDATED, \"Disabling the process\");\n        try {\n            update(processId, processDefinitionDeployInfo, getUpdateRecord(descriptor, processDefinitionDeployInfo),\n                    PROCESSDEFINITION_IS_DISABLED);\n            log(processDefinitionDeployInfo.getId(), SQueriableLog.STATUS_OK, logBuilder, \"disableProcess\");\n        } catch (final SRecorderException | SCacheException e) {\n            log(processDefinitionDeployInfo.getId(), SQueriableLog.STATUS_FAIL, logBuilder, \"disableProcess\");\n            throw new SProcessDisablementException(e);\n        }\n    }\n\n    void updateSProcessDefinitionTimestampInCache(long processId,\n            SProcessDefinitionDeployInfo processDefinitionDeployInfo) throws SCacheException {\n        final Pair<Long, SProcessDefinition> fromCache = getSProcessDefinitionFromCache(processId);\n        if (fromCache != null) {\n            storeProcessDefinitionInCache(fromCache.getValue(), processDefinitionDeployInfo.getLastUpdateDate());\n        }\n    }\n\n    @Override\n    public void enableProcessDeploymentInfo(final long processId)\n            throws SProcessDefinitionNotFoundException, SProcessEnablementException {\n        enableProcess(processId, true);\n    }\n\n    @Override\n    public void enableProcess(final long processId, boolean failIfAlreadyEnabled)\n            throws SProcessDefinitionNotFoundException, SProcessEnablementException {\n        SProcessDefinitionDeployInfo processDefinitionDeployInfo;\n        try {\n            processDefinitionDeployInfo = getProcessDeploymentInfo(processId);\n        } catch (final SBonitaReadException e) {\n            throw new SProcessEnablementException(e);\n        }\n        if (failIfAlreadyEnabled\n                && ActivationState.ENABLED.name().equals(processDefinitionDeployInfo.getActivationState())) {\n            throw new SProcessEnablementException(\"Process \" + processDefinitionDeployInfo.getName() + \" with version \"\n                    + processDefinitionDeployInfo.getVersion() + \" is already enabled\");\n        }\n        if (ConfigurationState.UNRESOLVED.name().equals(processDefinitionDeployInfo.getConfigurationState())) {\n            throw new SProcessEnablementException(\"Process \" + processDefinitionDeployInfo.getName() + \" with version \"\n                    + processDefinitionDeployInfo.getVersion()\n                    + \" can't be enabled since all dependencies are not resolved yet\");\n        }\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        descriptor.addField(SProcessDefinitionDeployInfo.ACTIVATION_STATE_KEY, ActivationState.ENABLED.name());\n\n        final SPersistenceLogBuilder logBuilder = getQueriableLog(ActionType.UPDATED, \"Enabling the process\");\n        try {\n            update(processId, processDefinitionDeployInfo, getUpdateRecord(descriptor, processDefinitionDeployInfo),\n                    PROCESSDEFINITION_IS_ENABLED);\n            log(processId, SQueriableLog.STATUS_OK, logBuilder, \"enableProcess\");\n        } catch (final SRecorderException | SCacheException e) {\n            log(processId, SQueriableLog.STATUS_FAIL, logBuilder, \"enableProcess\");\n            throw new SProcessEnablementException(e);\n        }\n    }\n\n    private SProcessDefinitionLogBuilder getQueriableLog(final ActionType actionType, final String message) {\n        final SProcessDefinitionLogBuilder logBuilder = BuilderFactory.get(SProcessDefinitionLogBuilderFactory.class)\n                .createNewInstance();\n        this.initializeLogBuilder(logBuilder, message);\n        this.updateLog(actionType, logBuilder);\n        return logBuilder;\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> getProcessDeploymentInfos(final int fromIndex, final int numberPerPage,\n            final String field,\n            final OrderByType order) throws SBonitaReadException {\n        final Map<String, Object> emptyMap = Collections.emptyMap();\n        return persistenceService.selectList(\n                new SelectListDescriptor<SProcessDefinitionDeployInfo>(\"\", emptyMap, SProcessDefinitionDeployInfo.class,\n                        new QueryOptions(fromIndex, numberPerPage, SProcessDefinitionDeployInfo.class, field, order)));\n    }\n\n    @Override\n    public long getNumberOfProcessDeploymentInfos() throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.emptyMap();\n        final SelectOneDescriptor<Long> selectDescriptor = new SelectOneDescriptor<>(\"getNumberOfProcessDefinitions\",\n                parameters,\n                SProcessDefinitionDeployInfo.class);\n        return persistenceService.selectOne(selectDescriptor);\n    }\n\n    @Override\n    public SProcessDefinition getProcessDefinition(final long processId)\n            throws SProcessDefinitionNotFoundException, SBonitaReadException {\n        try {\n            //get from database\n            final SProcessDefinitionDeployInfo processDeploymentInfo = getProcessDeploymentInfo(processId);\n            //get from cache\n            final Pair<Long, SProcessDefinition> processWithTimestamp = getSProcessDefinitionFromCache(processId);\n            //read SProcessDefinition if needed\n            if (isSProcessDefinitionUpToDate(processDeploymentInfo, processWithTimestamp)) {\n                return readSProcessDefinitionFromDatabase(processId, processDeploymentInfo);\n            } else {\n                return processWithTimestamp.getValue();\n            }\n        } catch (IOException | SReflectException | SCacheException e) {\n            throw new SBonitaReadException(e);\n        }\n    }\n\n    SProcessDefinition readSProcessDefinitionFromDatabase(long processId,\n            SProcessDefinitionDeployInfo processDeploymentInfo) throws IOException, SReflectException, SCacheException {\n        final DesignProcessDefinition objectFromXML = processDefinitionBARContribution\n                .convertXmlToProcess(processDeploymentInfo.getDesignContent()\n                        .getContent());\n        SProcessDefinition sProcessDefinition = convertDesignProcessDefinition(objectFromXML);\n        setIdOnProcessDefinition(sProcessDefinition, processId);\n        storeProcessDefinitionInCache(sProcessDefinition, processDeploymentInfo.getLastUpdateDate());\n        return sProcessDefinition;\n    }\n\n    boolean isSProcessDefinitionUpToDate(SProcessDefinitionDeployInfo processDeploymentInfo,\n            Pair<Long, SProcessDefinition> processWithTimestamp) {\n        return processWithTimestamp == null\n                || processWithTimestamp.getKey() != processDeploymentInfo.getLastUpdateDate();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    Pair<Long, SProcessDefinition> getSProcessDefinitionFromCache(long processId) throws SCacheException {\n        return (Pair<Long, SProcessDefinition>) cacheService.get(PROCESS_CACHE_NAME, processId);\n    }\n\n    @Override\n    public SProcessDefinition getProcessDefinitionIfIsEnabled(final long processDefinitionId)\n            throws SBonitaReadException,\n            SProcessDefinitionException {\n        final SProcessDefinitionDeployInfo deployInfo = getProcessDeploymentInfo(processDefinitionId);\n        if (ActivationState.DISABLED.name().equals(deployInfo.getActivationState())) {\n            throw new SProcessDefinitionException(\"The process definition is not enabled !!\", deployInfo.getProcessId(),\n                    deployInfo.getName(),\n                    deployInfo.getVersion());\n        }\n        return getProcessDefinition(processDefinitionId);\n    }\n\n    private void setIdOnProcessDefinition(final SProcessDefinition sProcessDefinition, long id)\n            throws SReflectException {\n        ClassReflector.invokeSetter(sProcessDefinition, \"setId\", Long.class, id);\n    }\n\n    protected long generateId() {\n        return Math.abs(UUID.randomUUID().getLeastSignificantBits());\n    }\n\n    @Override\n    public SProcessDefinitionDeployInfo getProcessDeploymentInfo(final long processId)\n            throws SProcessDefinitionNotFoundException,\n            SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"processId\", (Object) processId);\n        final SelectOneDescriptor<SProcessDefinitionDeployInfo> descriptor = new SelectOneDescriptor<>(\n                \"getDeployInfoByProcessDefId\", parameters, SProcessDefinitionDeployInfo.class);\n        final SProcessDefinitionDeployInfo processDefinitionDeployInfo = persistenceService.selectOne(descriptor);\n        if (processDefinitionDeployInfo == null) {\n            throw new SProcessDefinitionNotFoundException(\"Unable to find the process definition deployment info.\",\n                    processId);\n        }\n        return processDefinitionDeployInfo;\n    }\n\n    private <T extends SLogBuilder> void initializeLogBuilder(final T logBuilder, final String message) {\n        logBuilder.actionStatus(SQueriableLog.STATUS_FAIL).severity(SQueriableLogSeverity.INTERNAL).rawMessage(message);\n    }\n\n    @Override\n    public SProcessDefinition store(final DesignProcessDefinition designProcessDefinition)\n            throws SProcessDefinitionException {\n        NullCheckingUtil.checkArgsNotNull(designProcessDefinition);\n\n        // create the runtime process definition\n        final SProcessDefinition definition = convertDesignProcessDefinition(designProcessDefinition);\n\n        final SProcessDefinitionLogBuilder logBuilder = getQueriableLog(ActionType.CREATED,\n                \"Creating a new Process definition\");\n        try {\n            final String processDefinitionContent = getProcessContent(designProcessDefinition);\n            final SProcessDefinitionDesignContent sProcessDefinitionDesignContent = new SProcessDefinitionDesignContent();\n            sProcessDefinitionDesignContent.setContent(processDefinitionContent);\n\n            recorder.recordInsert(new InsertRecord(sProcessDefinitionDesignContent), PROCESSDEFINITION_CONTENT);\n\n            final long processId = generateId();\n            setIdOnProcessDefinition(definition, processId);\n            final SProcessDefinitionDeployInfo sProcessDefinitionDeployInfo = createProcessDefinitionDeployInfo(\n                    designProcessDefinition, definition,\n                    sProcessDefinitionDesignContent, processId);\n\n            recorder.recordInsert(new InsertRecord(sProcessDefinitionDeployInfo), PROCESSDEFINITION);\n            //storeProcessDefinitionInCache(definition, sProcessDefinitionDeployInfo.getLastUpdateDate());\n            log(definition.getId(), SQueriableLog.STATUS_OK, logBuilder, \"store\");\n        } catch (final Exception e) {\n            log(definition.getId(), SQueriableLog.STATUS_FAIL, logBuilder, \"store\");\n            throw new SProcessDefinitionException(e);\n        }\n        return definition;\n    }\n\n    SProcessDefinitionDeployInfo createProcessDefinitionDeployInfo(DesignProcessDefinition designProcessDefinition,\n            SProcessDefinition definition,\n            SProcessDefinitionDesignContent sProcessDefinitionDesignContent, long processId) {\n        String displayName = designProcessDefinition.getDisplayName();\n        if (displayName == null || displayName.isEmpty()) {\n            displayName = definition.getName();\n        }\n        String displayDescription = designProcessDefinition.getDisplayDescription();\n        if (displayDescription == null || displayDescription.isEmpty()) {\n            displayDescription = definition.getDescription();\n        }\n        final SProcessDefinitionDeployInfo sProcessDefinitionDeployInfo = new SProcessDefinitionDeployInfo();\n        sProcessDefinitionDeployInfo.setName(definition.getName());\n        sProcessDefinitionDeployInfo.setVersion(definition.getVersion());\n        sProcessDefinitionDeployInfo.setProcessId(processId);\n        sProcessDefinitionDeployInfo.setDescription(definition.getDescription());\n        sProcessDefinitionDeployInfo.setDeployedBy(getUserId());\n        sProcessDefinitionDeployInfo.setDeploymentDate(System.currentTimeMillis());\n        sProcessDefinitionDeployInfo.setActivationState(ActivationState.DISABLED.name());\n        sProcessDefinitionDeployInfo.setConfigurationState(ConfigurationState.UNRESOLVED.name());\n        sProcessDefinitionDeployInfo.setDisplayName(displayName);\n        sProcessDefinitionDeployInfo.setDisplayDescription(displayDescription);\n        sProcessDefinitionDeployInfo.setDesignContent(sProcessDefinitionDesignContent);\n        return sProcessDefinitionDeployInfo;\n    }\n\n    void storeProcessDefinitionInCache(SProcessDefinition definition, Long lastUpdateDate) throws SCacheException {\n        cacheService.store(PROCESS_CACHE_NAME, definition.getId(), Pair.of(lastUpdateDate, definition));\n    }\n\n    String getProcessContent(DesignProcessDefinition designProcessDefinition) throws IOException {\n        return processDefinitionBARContribution.convertProcessToXml(designProcessDefinition);\n    }\n\n    SProcessDefinition convertDesignProcessDefinition(DesignProcessDefinition designProcessDefinition) {\n        return BuilderFactory.get(SProcessDefinitionBuilderFactory.class).createNewInstance(designProcessDefinition)\n                .done();\n    }\n\n    private long getUserId() {\n        return sessionService.getLoggedUserFromSession(sessionAccessor);\n    }\n\n    private <T extends HasCRUDEAction> void updateLog(final ActionType actionType, final T logBuilder) {\n        logBuilder.setActionType(actionType);\n    }\n\n    @Override\n    public void resolveProcess(final long processId)\n            throws SProcessDefinitionNotFoundException, SProcessDisablementException {\n        SProcessDefinitionDeployInfo processDefinitionDeployInfo;\n        try {\n            processDefinitionDeployInfo = getProcessDeploymentInfo(processId);\n        } catch (final SBonitaReadException e) {\n            throw new SProcessDisablementException(e);\n        }\n        if (!ConfigurationState.UNRESOLVED.name().equals(processDefinitionDeployInfo.getConfigurationState())) {\n            throw new SProcessDisablementException(\"Process \" + processDefinitionDeployInfo.getName() + \" with version\"\n                    + processDefinitionDeployInfo.getVersion() + \" is not unresolved\");\n        }\n\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        descriptor\n                .addField(SProcessDefinitionDeployInfo.CONFIGURATION_STATE_KEY, ConfigurationState.RESOLVED.name());\n\n        final SPersistenceLogBuilder logBuilder = getQueriableLog(ActionType.UPDATED, \"Resolved the process\");\n\n        try {\n            update(processId, processDefinitionDeployInfo, getUpdateRecord(descriptor, processDefinitionDeployInfo),\n                    PROCESSDEFINITION_IS_RESOLVED);\n            log(processDefinitionDeployInfo.getId(), SQueriableLog.STATUS_OK, logBuilder, \"resolveProcess\");\n        } catch (final SRecorderException | SCacheException e) {\n            log(processDefinitionDeployInfo.getId(), SQueriableLog.STATUS_FAIL, logBuilder, \"resolveProcess\");\n            throw new SProcessDisablementException(e);\n        }\n    }\n\n    @Override\n    public long getNumberOfProcessDeploymentInfosByActivationState(final ActivationState activationState)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"activationState\",\n                (Object) activationState.name());\n        final SelectOneDescriptor<Long> selectDescriptor = new SelectOneDescriptor<>(\n                \"getNumberOfProcessDefinitionsInActivationState\", parameters,\n                SProcessDefinitionDeployInfo.class);\n        return persistenceService.selectOne(selectDescriptor);\n    }\n\n    @Override\n    public List<Long> getProcessDefinitionIds(final ActivationState activationState, final int fromIndex,\n            final int numberOfResults)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"activationState\",\n                (Object) activationState.name());\n        final List<OrderByOption> orderByOptions = Collections\n                .singletonList(new OrderByOption(SProcessDefinitionDeployInfo.class, \"id\", OrderByType.ASC));\n        final SelectListDescriptor<Long> selectDescriptor = new SelectListDescriptor<>(\n                \"getProcessDefinitionsIdsInActivationState\", parameters,\n                SProcessDefinitionDeployInfo.class, new QueryOptions(fromIndex, numberOfResults, orderByOptions));\n        return persistenceService.selectList(selectDescriptor);\n    }\n\n    @Override\n    public List<Long> getProcessDefinitionIds(final int fromIndex, final int numberOfResults)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.emptyMap();\n        final List<OrderByOption> orderByOptions = Collections\n                .singletonList(new OrderByOption(SProcessDefinitionDeployInfo.class, \"id\", OrderByType.ASC));\n        final SelectListDescriptor<Long> selectDescriptor = new SelectListDescriptor<>(\"getProcessDefinitionsIds\",\n                parameters,\n                SProcessDefinitionDeployInfo.class, new QueryOptions(fromIndex, numberOfResults, orderByOptions));\n        return persistenceService.selectList(selectDescriptor);\n    }\n\n    @Override\n    public SFlowNodeDefinition getNextFlowNode(final SProcessDefinition definition, final String source) {\n        final SFlowElementContainerDefinition processContainer = definition.getProcessContainer();\n        final STransitionDefinition sourceNode = processContainer.getTransition(source);\n        final long targetId = sourceNode.getTarget();\n        return processContainer.getFlowNode(targetId);\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> getProcessDeploymentInfos(final List<Long> processIds,\n            final int fromIndex, final int numberOfProcesses,\n            final String field, final OrderByType order) throws SBonitaReadException {\n        if (processIds == null || processIds.size() == 0) {\n            return Collections.emptyList();\n        }\n        final Map<String, Object> emptyMap = Collections.singletonMap(\"processIds\", (Object) processIds);\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfProcesses,\n                SProcessDefinitionDeployInfo.class, field, order);\n        return persistenceService.selectList(new SelectListDescriptor<SProcessDefinitionDeployInfo>(\n                \"getSubSetOfProcessDefinitionDeployInfos\", emptyMap, SProcessDefinitionDeployInfo.class, queryOptions));\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> getProcessDeploymentInfos(final List<Long> processIds)\n            throws SBonitaReadException {\n        if (processIds == null || processIds.size() == 0) {\n            return Collections.emptyList();\n        }\n        final QueryOptions queryOptions = new QueryOptions(0, processIds.size(), SProcessDefinitionDeployInfo.class,\n                \"name\", OrderByType.ASC);\n        final Map<String, Object> emptyMap = Collections.singletonMap(\"processIds\", (Object) processIds);\n        return persistenceService.selectList(new SelectListDescriptor<SProcessDefinitionDeployInfo>(\n                \"getSubSetOfProcessDefinitionDeployInfos\", emptyMap,\n                SProcessDefinitionDeployInfo.class, queryOptions));\n    }\n\n    @Override\n    public long getLatestProcessDefinitionId(final String processName)\n            throws SBonitaReadException, SProcessDefinitionNotFoundException {\n        final List<SProcessDefinitionDeployInfo> sProcessDefinitionDeployInfos = getProcessDeploymentInfosOrderByTimeDesc(\n                processName, 0, 1);\n        if (sProcessDefinitionDeployInfos.isEmpty()) {\n            throw new SProcessDefinitionNotFoundException(processName);\n        }\n        return sProcessDefinitionDeployInfos.get(0).getProcessId();\n    }\n\n    private List<SProcessDefinitionDeployInfo> getProcessDeploymentInfosOrderByTimeDesc(final String processName,\n            final int startIndex, final int maxResults)\n            throws SBonitaReadException {\n        final QueryOptions queryOptions = new QueryOptions(startIndex, maxResults, SProcessDefinitionDeployInfo.class,\n                \"deploymentDate\", OrderByType.DESC);\n        final Map<String, Object> parameters = Collections.singletonMap(\"name\", (Object) processName);\n        final SelectListDescriptor<SProcessDefinitionDeployInfo> selectDescriptor = new SelectListDescriptor<>(\n                \"getProcessDefinitionDeployInfosByName\", parameters, SProcessDefinitionDeployInfo.class, queryOptions);\n        return persistenceService.selectList(selectDescriptor);\n    }\n\n    @Override\n    public long getProcessDefinitionId(final String name, final String version)\n            throws SBonitaReadException, SProcessDefinitionNotFoundException {\n        final Map<String, Object> parameters = new HashMap<>();\n        parameters.put(\"name\", name);\n        parameters.put(\"version\", version);\n        final Long processDefId = persistenceService\n                .selectOne(new SelectOneDescriptor<>(\"getProcessDefinitionIdByNameAndVersion\", parameters,\n                        SProcessDefinitionDeployInfo.class, Long.class));\n        if (processDefId == null) {\n            final SProcessDefinitionNotFoundException exception = new SProcessDefinitionNotFoundException(\n                    \"Process definition id not found.\");\n            exception.setProcessDefinitionNameOnContext(name);\n            exception.setProcessDefinitionVersionOnContext(version);\n            throw exception;\n        }\n        return processDefId;\n    }\n\n    @Override\n    public SProcessDefinitionDeployInfo updateProcessDefinitionDeployInfo(final long processId,\n            final EntityUpdateDescriptor descriptor)\n            throws SProcessDefinitionNotFoundException, SProcessDeploymentInfoUpdateException {\n        final String logMessage = \"Updating a processDefinitionDeployInfo\";\n        return updateProcessDefinitionDeployInfo(processId, descriptor, logMessage);\n    }\n\n    SProcessDefinitionDeployInfo updateProcessDefinitionDeployInfo(long processId, EntityUpdateDescriptor descriptor,\n            String logMessage) throws SProcessDefinitionNotFoundException, SProcessDeploymentInfoUpdateException {\n        SProcessDefinitionDeployInfo processDefinitionDeployInfo;\n        try {\n            processDefinitionDeployInfo = getProcessDeploymentInfo(processId);\n        } catch (final SBonitaReadException e) {\n            throw new SProcessDefinitionNotFoundException(e, processId);\n        }\n        final SProcessDefinitionLogBuilder logBuilder = getQueriableLog(ActionType.UPDATED, truncate(logMessage));\n        try {\n            update(processId, processDefinitionDeployInfo, getUpdateRecord(descriptor, processDefinitionDeployInfo),\n                    PROCESSDEFINITION_DEPLOY_INFO);\n            log(processId, SQueriableLog.STATUS_OK, logBuilder, \"updateProcessDeploymentInfo\");\n        } catch (final SRecorderException | SCacheException e) {\n            log(processId, SQueriableLog.STATUS_FAIL, logBuilder, \"updateProcessDeploymentInfo\");\n            throw new SProcessDeploymentInfoUpdateException(e);\n        }\n        return processDefinitionDeployInfo;\n    }\n\n    void update(long processId, SProcessDefinitionDeployInfo processDefinitionDeployInfo, UpdateRecord updateRecord,\n            String eventType)\n            throws SRecorderException, SCacheException {\n        recorder.recordUpdate(updateRecord, eventType);\n        if (!updateRecord.getFields().containsKey(SProcessDefinitionDeployInfo.DESIGN_CONTENT)) {\n            updateSProcessDefinitionTimestampInCache(processId, processDefinitionDeployInfo);\n        }\n    }\n\n    private UpdateRecord getUpdateRecord(final EntityUpdateDescriptor descriptor,\n            final SProcessDefinitionDeployInfo processDefinitionDeployInfo) {\n        final long now = System.currentTimeMillis();\n        descriptor.addField(LAST_UPDATE_DATE_KEY, now);\n        return UpdateRecord.buildSetFields(processDefinitionDeployInfo, descriptor);\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> searchProcessDeploymentInfosStartedBy(final long startedBy,\n            final QueryOptions searchOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"startedBy\", (Object) startedBy);\n        return persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, STARTED_BY_SUFFIX, searchOptions,\n                parameters);\n    }\n\n    @Override\n    public long getNumberOfProcessDeploymentInfosStartedBy(final long startedBy, final QueryOptions countOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"startedBy\", (Object) startedBy);\n        return persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, STARTED_BY_SUFFIX,\n                countOptions, parameters);\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> searchProcessDeploymentInfos(final QueryOptions searchOptions)\n            throws SBonitaReadException {\n        return persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, searchOptions, null);\n    }\n\n    @Override\n    public long getNumberOfProcessDeploymentInfos(final QueryOptions countOptions) throws SBonitaReadException {\n        return persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, countOptions, null);\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> searchProcessDeploymentInfosCanBeStartedBy(final long userId,\n            final QueryOptions searchOptions)\n            throws SBonitaReadException {\n        return persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, \"UserCanStart\", searchOptions,\n                Collections.singletonMap(USER_ID, (Object) userId));\n    }\n\n    @Override\n    public long getNumberOfProcessDeploymentInfosCanBeStartedBy(final long userId, final QueryOptions countOptions)\n            throws SBonitaReadException {\n        return persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, \"UserCanStart\", countOptions,\n                Collections.singletonMap(USER_ID, (Object) userId));\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> searchProcessDeploymentInfosCanBeStartedByUsersManagedBy(\n            final long managerUserId,\n            final QueryOptions searchOptions)\n            throws SBonitaReadException {\n        return persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, \"UsersManagedByCanStart\",\n                searchOptions,\n                Collections.singletonMap(\"managerUserId\", (Object) managerUserId));\n    }\n\n    @Override\n    public long getNumberOfProcessDeploymentInfosCanBeStartedByUsersManagedBy(final long managerUserId,\n            final QueryOptions countOptions)\n            throws SBonitaReadException {\n        return persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, \"UsersManagedByCanStart\",\n                countOptions,\n                Collections.singletonMap(\"managerUserId\", (Object) managerUserId));\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> searchProcessDeploymentInfos(final long userId,\n            final QueryOptions searchOptions, final String querySuffix)\n            throws SBonitaReadException {\n        return persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, querySuffix, searchOptions,\n                Collections.singletonMap(USER_ID, (Object) userId));\n    }\n\n    @Override\n    public long getNumberOfProcessDeploymentInfos(final long userId, final QueryOptions countOptions,\n            final String querySuffix) throws SBonitaReadException {\n        return persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, querySuffix, countOptions,\n                Collections.singletonMap(USER_ID, (Object) userId));\n    }\n\n    @Override\n    public long getNumberOfUncategorizedProcessDeploymentInfos(final QueryOptions countOptions)\n            throws SBonitaReadException {\n        return persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, UNCATEGORIZED_SUFFIX,\n                countOptions, null);\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> searchUncategorizedProcessDeploymentInfos(\n            final QueryOptions searchOptions) throws SBonitaReadException {\n        return persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, UNCATEGORIZED_SUFFIX, searchOptions,\n                null);\n    }\n\n    @Override\n    public long getNumberOfUncategorizedProcessDeploymentInfosSupervisedBy(final long userId,\n            final QueryOptions countOptions) throws SBonitaReadException {\n        return persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class,\n                UNCATEGORIZED_SUPERVISED_BY_SUFFIX, countOptions,\n                Collections.singletonMap(USER_ID, (Object) userId));\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> searchUncategorizedProcessDeploymentInfosSupervisedBy(final long userId,\n            final QueryOptions searchOptions)\n            throws SBonitaReadException {\n        return persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, UNCATEGORIZED_SUPERVISED_BY_SUFFIX,\n                searchOptions,\n                Collections.singletonMap(USER_ID, (Object) userId));\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> searchUncategorizedProcessDeploymentInfosCanBeStartedBy(final long userId,\n            final QueryOptions searchOptions)\n            throws SBonitaReadException {\n        return persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, UNCATEGORIZED_USERCANSTART_SUFFIX,\n                searchOptions,\n                Collections.singletonMap(USER_ID, (Object) userId));\n    }\n\n    @Override\n    public long getNumberOfUncategorizedProcessDeploymentInfosCanBeStartedBy(final long userId,\n            final QueryOptions countOptions) throws SBonitaReadException {\n        return persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class,\n                UNCATEGORIZED_USERCANSTART_SUFFIX, countOptions,\n                Collections.singletonMap(USER_ID, (Object) userId));\n    }\n\n    @Override\n    public Map<Long, SProcessDefinitionDeployInfo> getProcessDeploymentInfosFromProcessInstanceIds(\n            final List<Long> processInstanceIds)\n            throws SBonitaReadException {\n        if (processInstanceIds == null || processInstanceIds.size() == 0) {\n            return Collections.emptyMap();\n        }\n        final QueryOptions queryOptions = new QueryOptions(0, processInstanceIds.size(),\n                SProcessDefinitionDeployInfo.class, \"name\", OrderByType.ASC);\n        final Map<String, Object> parameters = Collections.singletonMap(\"processInstanceIds\",\n                (Object) processInstanceIds);\n        final List<Map<String, Object>> result = persistenceService\n                .selectList(new SelectListDescriptor<Map<String, Object>>(\n                        \"getProcessDeploymentInfoFromProcessInstanceIds\", parameters,\n                        SProcessDefinitionDeployInfo.class, queryOptions));\n        if (result != null && result.size() > 0) {\n            return getProcessDeploymentInfosFromMap(result);\n        }\n        return Collections.emptyMap();\n    }\n\n    @Override\n    public Map<Long, SProcessDefinitionDeployInfo> getProcessDeploymentInfosFromArchivedProcessInstanceIds(\n            final List<Long> archivedProcessInstantsIds)\n            throws SBonitaReadException {\n        if (archivedProcessInstantsIds == null || archivedProcessInstantsIds.size() == 0) {\n            return Collections.emptyMap();\n        }\n        final QueryOptions queryOptions = new QueryOptions(0, archivedProcessInstantsIds.size(),\n                SProcessDefinitionDeployInfo.class, \"name\",\n                OrderByType.ASC);\n        final Map<String, Object> parameters = Collections.singletonMap(\"archivedProcessInstanceIds\",\n                (Object) archivedProcessInstantsIds);\n        final List<Map<String, Object>> result = persistenceService\n                .selectList(new SelectListDescriptor<Map<String, Object>>(\n                        \"getProcessDeploymentInfoFromArchivedProcessInstanceIds\", parameters,\n                        SProcessDefinitionDeployInfo.class, queryOptions));\n        if (result != null && result.size() > 0) {\n            return getProcessDeploymentInfosFromMap(result);\n        }\n        return Collections.emptyMap();\n    }\n\n    private Map<Long, SProcessDefinitionDeployInfo> getProcessDeploymentInfosFromMap(\n            final List<Map<String, Object>> sProcessDeploymentInfos) {\n        final Map<Long, SProcessDefinitionDeployInfo> mProcessDeploymentInfos = new HashMap<>();\n        for (final Map<String, Object> sProcessDeploymentInfo : sProcessDeploymentInfos) {\n            final Long archivedProcessInstanceId = (Long) sProcessDeploymentInfo.get(\"archivedProcessInstanceId\");\n            final Long processInstanceId = (Long) sProcessDeploymentInfo.get(\"processInstanceId\");\n            final SProcessDefinitionDeployInfo sProcessDefinitionDeployInfo = buildSProcessDefinitionDeployInfo(\n                    sProcessDeploymentInfo);\n            mProcessDeploymentInfos.put(processInstanceId != null ? processInstanceId : archivedProcessInstanceId,\n                    sProcessDefinitionDeployInfo);\n        }\n        return mProcessDeploymentInfos;\n    }\n\n    private SProcessDefinitionDeployInfo buildSProcessDefinitionDeployInfo(\n            final Map<String, Object> sProcessDeploymentInfo) {\n\n        return SProcessDefinitionDeployInfo.builder()\n                .id((Long) sProcessDeploymentInfo.get(ID_KEY))\n                .name((String) sProcessDeploymentInfo.get(NAME_KEY))\n                .version((String) sProcessDeploymentInfo.get(VERSION_KEY))\n                .description((String) sProcessDeploymentInfo.get(VERSION_KEY))\n                .displayDescription((String) sProcessDeploymentInfo.get(DISPLAY_DESCRIPTION_KEY))\n                .activationState((String) sProcessDeploymentInfo.get(ACTIVATION_STATE_KEY))\n                .configurationState((String) sProcessDeploymentInfo.get(CONFIGURATION_STATE_KEY))\n                .deployedBy((Long) sProcessDeploymentInfo.get(DEPLOYED_BY_KEY))\n                .processId((Long) sProcessDeploymentInfo.get(PROCESS_ID_KEY))\n                .lastUpdateDate((Long) sProcessDeploymentInfo.get(LAST_UPDATE_DATE_KEY))\n                .displayName((String) sProcessDeploymentInfo.get(DISPLAY_NAME_KEY))\n                .deploymentDate((Long) sProcessDeploymentInfo.get(DEPLOYMENT_DATE_KEY))\n                .iconPath((String) sProcessDeploymentInfo.get(ICON_PATH)).build();\n    }\n\n    private void log(final long objectId, final int sQueriableLogStatus, final SPersistenceLogBuilder logBuilder,\n            final String callerMethodName) {\n        logBuilder.actionScope(String.valueOf(objectId));\n        logBuilder.actionStatus(sQueriableLogStatus);\n        logBuilder.objectId(objectId);\n        final SQueriableLog log = logBuilder.build();\n        if (queriableLoggerService.isLoggable(log.getActionType(), log.getSeverity())) {\n            queriableLoggerService.log(this.getClass().getName(), callerMethodName, log);\n        }\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> getProcessDeploymentInfosUnrelatedToCategory(final long categoryId,\n            final int pageIndex, final int numberPerPage,\n            final ProcessDeploymentInfoCriterion pagingCriterion) throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"categoryId\", (Object) categoryId);\n        final QueryOptions queryOptions = createQueryOptions(pageIndex, numberPerPage, pagingCriterion);\n        final SelectListDescriptor<SProcessDefinitionDeployInfo> selectDescriptor = new SelectListDescriptor<>(\n                \"searchSProcessDefinitionDeployInfoUnrelatedToCategory\", parameters, SProcessDefinitionDeployInfo.class,\n                queryOptions);\n        return persistenceService.selectList(selectDescriptor);\n    }\n\n    @Override\n    public Long getNumberOfProcessDeploymentInfosUnrelatedToCategory(final long categoryId)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"categoryId\", (Object) categoryId);\n        final SelectOneDescriptor<Long> selectDescriptor = new SelectOneDescriptor<>(\n                \"getNumberOfSProcessDefinitionDeployInfoUnrelatedToCategory\",\n                parameters, SProcessDefinitionDeployInfo.class);\n        return persistenceService.selectOne(selectDescriptor);\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> searchProcessDeploymentInfosOfCategory(final long categoryId,\n            final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"categoryId\", (Object) categoryId);\n        final SelectListDescriptor<SProcessDefinitionDeployInfo> selectDescriptor = new SelectListDescriptor<>(\n                \"searchSProcessDeploymentInfosOfCategory\", parameters, SProcessDefinitionDeployInfo.class,\n                queryOptions);\n        return persistenceService.selectList(selectDescriptor);\n    }\n\n    private QueryOptions createQueryOptions(final int pageIndex, final int numberPerPage,\n            final ProcessDeploymentInfoCriterion pagingCriterion) {\n        String field;\n        OrderByType order;\n        switch (pagingCriterion) {\n            case NAME_ASC:\n                field = SProcessDefinitionDeployInfo.NAME_KEY;\n                order = OrderByType.ASC;\n                break;\n            case NAME_DESC:\n                field = SProcessDefinitionDeployInfo.NAME_KEY;\n                order = OrderByType.DESC;\n                break;\n            case ACTIVATION_STATE_ASC:\n                field = SProcessDefinitionDeployInfo.ACTIVATION_STATE_KEY;\n                order = OrderByType.ASC;\n                break;\n            case ACTIVATION_STATE_DESC:\n                field = SProcessDefinitionDeployInfo.ACTIVATION_STATE_KEY;\n                order = OrderByType.DESC;\n                break;\n            case CONFIGURATION_STATE_ASC:\n                field = SProcessDefinitionDeployInfo.CONFIGURATION_STATE_KEY;\n                order = OrderByType.ASC;\n                break;\n            case CONFIGURATION_STATE_DESC:\n                field = SProcessDefinitionDeployInfo.CONFIGURATION_STATE_KEY;\n                order = OrderByType.DESC;\n                break;\n            case VERSION_ASC:\n                field = SProcessDefinitionDeployInfo.VERSION_KEY;\n                order = OrderByType.ASC;\n                break;\n            case VERSION_DESC:\n                field = SProcessDefinitionDeployInfo.VERSION_KEY;\n                order = OrderByType.DESC;\n                break;\n            case DEFAULT:\n            default:\n                field = pagingCriterion.getField();\n                order = OrderByType.valueOf(pagingCriterion.getOrder().name());\n                break;\n        }\n\n        return new QueryOptions(pageIndex, numberPerPage, SProcessDefinitionDeployInfo.class, field, order);\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> getProcessDeploymentInfos(final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        return persistenceService\n                .selectList(\n                        new SelectListDescriptor<SProcessDefinitionDeployInfo>(\"getProcessDefinitionDeployInfos\",\n                                Collections\n                                        .<String, Object> emptyMap(),\n                                SProcessDefinitionDeployInfo.class, queryOptions));\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> getProcessDeploymentInfosWithActorOnlyForGroup(final long groupId,\n            final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(GROUP_ID, (Object) groupId);\n        final SelectListDescriptor<SProcessDefinitionDeployInfo> selectDescriptor = new SelectListDescriptor<>(\n                \"getProcessesWithActorOnlyForGroup\", parameters, SProcessDefinitionDeployInfo.class, queryOptions);\n        return persistenceService.selectList(selectDescriptor);\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> getProcessDeploymentInfosWithActorOnlyForGroups(final List<Long> groupIds,\n            final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"groupIds\", (Object) groupIds);\n        final SelectListDescriptor<SProcessDefinitionDeployInfo> selectDescriptor = new SelectListDescriptor<>(\n                \"getProcessesWithActorOnlyForGroups\", parameters, SProcessDefinitionDeployInfo.class, queryOptions);\n        return persistenceService.selectList(selectDescriptor);\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> getProcessDeploymentInfosWithActorOnlyForRole(final long roleId,\n            final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(ROLE_ID, (Object) roleId);\n        final SelectListDescriptor<SProcessDefinitionDeployInfo> selectDescriptor = new SelectListDescriptor<>(\n                \"getProcessesWithActorOnlyForRole\", parameters, SProcessDefinitionDeployInfo.class, queryOptions);\n        return persistenceService.selectList(selectDescriptor);\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> getProcessDeploymentInfosWithActorOnlyForRoles(final List<Long> roleIds,\n            final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"roleIds\", (Object) roleIds);\n        final SelectListDescriptor<SProcessDefinitionDeployInfo> selectDescriptor = new SelectListDescriptor<>(\n                \"getProcessesWithActorOnlyForRoles\", parameters, SProcessDefinitionDeployInfo.class, queryOptions);\n        return persistenceService.selectList(selectDescriptor);\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> getProcessDeploymentInfosWithActorOnlyForUser(final long userId,\n            final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(USER_ID, (Object) userId);\n        final SelectListDescriptor<SProcessDefinitionDeployInfo> selectDescriptor = new SelectListDescriptor<>(\n                \"getProcessesWithActorOnlyForUser\", parameters, SProcessDefinitionDeployInfo.class, queryOptions);\n        return persistenceService.selectList(selectDescriptor);\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> getProcessDeploymentInfosWithActorOnlyForUsers(final List<Long> userIds,\n            final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"userIds\", (Object) userIds);\n        final SelectListDescriptor<SProcessDefinitionDeployInfo> selectDescriptor = new SelectListDescriptor<>(\n                \"getProcessesWithActorOnlyForUsers\", parameters, SProcessDefinitionDeployInfo.class, queryOptions);\n        return persistenceService.selectList(selectDescriptor);\n    }\n\n    @Override\n    public long getNumberOfUsersWhoCanStartProcessDeploymentInfo(final long processDefinitionId,\n            final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(PROCESS_DEFINITION_ID,\n                (Object) processDefinitionId);\n        return persistenceService.getNumberOfEntities(SUser.class, WHOCANSTART_PROCESS_SUFFIX, queryOptions,\n                parameters);\n    }\n\n    @Override\n    public List<SUser> searchUsersWhoCanStartProcessDeploymentInfo(final long processDefinitionId,\n            final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(PROCESS_DEFINITION_ID,\n                (Object) processDefinitionId);\n        return persistenceService.searchEntity(SUser.class, WHOCANSTART_PROCESS_SUFFIX, queryOptions, parameters);\n    }\n\n    @Override\n    public long getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(final long userId,\n            final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(USER_ID, (Object) userId);\n        return persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class,\n                \"WithAssignedOrPendingHumanTasksFor\", queryOptions,\n                parameters);\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(\n            final long userId,\n            final QueryOptions queryOptions) throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(USER_ID, (Object) userId);\n        return persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, \"WithAssignedOrPendingHumanTasksFor\",\n                queryOptions, parameters);\n    }\n\n    @Override\n    public long getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(final long userId,\n            final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(USER_ID, (Object) userId);\n        return persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class,\n                \"WithAssignedOrPendingHumanTasksSupervisedBy\", queryOptions,\n                parameters);\n\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(\n            final long userId,\n            final QueryOptions queryOptions) throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(USER_ID, (Object) userId);\n        return persistenceService.searchEntity(SProcessDefinitionDeployInfo.class,\n                \"WithAssignedOrPendingHumanTasksSupervisedBy\", queryOptions, parameters);\n    }\n\n    @Override\n    public long getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasks(final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        return persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class,\n                \"WithAssignedOrPendingHumanTasks\", queryOptions, null);\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks(\n            final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        return persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, \"WithAssignedOrPendingHumanTasks\",\n                queryOptions, null);\n    }\n\n    @Override\n    public DesignProcessDefinition getDesignProcessDefinition(long processDefinitionId)\n            throws SProcessDefinitionNotFoundException,\n            SBonitaReadException {\n        try {\n            return processDefinitionBARContribution\n                    .convertXmlToProcess(getProcessDeploymentInfo(processDefinitionId).getDesignContent().getContent());\n        } catch (IOException e) {\n            throw new SBonitaReadException(e);\n        }\n    }\n\n    @Override\n    public void updateExpressionContent(long processDefinitionId, long expressionDefinitionId, String content)\n            throws SProcessDefinitionNotFoundException,\n            SObjectModificationException {\n        try {\n            final DesignProcessDefinition designProcessDefinition = getDesignProcessDefinition(processDefinitionId);\n            final ExpressionImpl expression = (ExpressionImpl) getExpression(designProcessDefinition,\n                    expressionDefinitionId);\n            if (expression == null) {\n                throw new SObjectModificationException(\"No expression with ID \" + expressionDefinitionId\n                        + \" found on process \"\n                        + designProcessDefinition.getDisplayName() + \" (\" + designProcessDefinition.getVersion() + \")\");\n            }\n            if (!isValidExpressionTypeToUpdate(expression.getExpressionType())) {\n                throw new SObjectModificationException(\n                        \"Updating an Expression of type \" + expression.getExpressionType()\n                                + \" is not supported. Only Groovy scripts and constants are allowed.\");\n            }\n            final String oldContent = expression.getContent();\n            expression.setContent(content);\n            final String processDefinitionAsXMLString = getProcessContent(designProcessDefinition);\n            final EntityUpdateDescriptor updateDescriptor = BuilderFactory\n                    .get(SProcessDefinitionDeployInfoUpdateBuilderFactory.class)\n                    .createNewInstance().updateDesignContent(processDefinitionAsXMLString).done();\n            updateProcessDefinitionDeployInfo(processDefinitionId, updateDescriptor,\n                    \"Update expression <\" + expressionDefinitionId + \">, old content is <\" + oldContent + \">\");\n        } catch (IOException e) {\n            throw new SProcessDefinitionNotFoundException(e, processDefinitionId);\n        } catch (SBonitaReadException | SProcessDeploymentInfoUpdateException e) {\n            throw new SObjectModificationException(e);\n        }\n    }\n\n    String truncate(String logMessage) {\n        return logMessage.substring(0, Math.min(255, logMessage.length()));\n    }\n\n    protected boolean isValidExpressionTypeToUpdate(String type) {\n        return ExpressionType.TYPE_READ_ONLY_SCRIPT.name().equals(type)\n                || ExpressionType.TYPE_CONSTANT.name().equals(type);\n    }\n\n    protected Expression getExpression(DesignProcessDefinition processDefinition, long expressionDefinitionId) {\n        final ExpressionFinder expressionFinder = new ExpressionFinder();\n        expressionFinder.find(processDefinition, expressionDefinitionId);\n        return expressionFinder.getFoundExpression();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/exception/SDeletingEnabledProcessException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.exception;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class SDeletingEnabledProcessException extends SBonitaException {\n\n    private static final long serialVersionUID = -8265428109141653941L;\n\n    public SDeletingEnabledProcessException(final String message,\n            final SProcessDefinitionDeployInfo processDefinitionDeployInfo) {\n        super(message);\n\n        setProcessDefinitionIdOnContext(processDefinitionDeployInfo.getId());\n        setProcessDefinitionNameOnContext(processDefinitionDeployInfo.getName());\n        setProcessDefinitionVersionOnContext(processDefinitionDeployInfo.getVersion());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/exception/SProcessDefinitionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.exception;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SProcessDefinitionException extends SBonitaException {\n\n    private static final long serialVersionUID = -118684656125186636L;\n\n    public SProcessDefinitionException(final String message) {\n        super(message);\n    }\n\n    public SProcessDefinitionException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SProcessDefinitionException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SProcessDefinitionException(final String message, final long processDefinitionId, final String name,\n            final String version) {\n        super(message);\n        setProcessDefinitionIdOnContext(processDefinitionId);\n        setProcessDefinitionNameOnContext(name);\n        setProcessDefinitionVersionOnContext(version);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/exception/SProcessDefinitionNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.exception;\n\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class SProcessDefinitionNotFoundException extends SProcessDefinitionException {\n\n    private static final long serialVersionUID = 1702422492322010491L;\n\n    public SProcessDefinitionNotFoundException(final String message, final long id) {\n        super(message);\n        setProcessDefinitionIdOnContext(id);\n    }\n\n    public SProcessDefinitionNotFoundException(final Throwable cause, final long id) {\n        super(cause);\n        setProcessDefinitionIdOnContext(id);\n    }\n\n    public SProcessDefinitionNotFoundException(final String message, final Throwable e, final long id) {\n        super(message, e);\n        setProcessDefinitionIdOnContext(id);\n    }\n\n    public SProcessDefinitionNotFoundException(final Throwable cause,\n            final SProcessDefinitionDeployInfo processDefinitionDeployInfo) {\n        this(cause, processDefinitionDeployInfo.getId());\n        setProcessDefinitionNameOnContext(processDefinitionDeployInfo.getName());\n        setProcessDefinitionVersionOnContext(processDefinitionDeployInfo.getVersion());\n    }\n\n    public SProcessDefinitionNotFoundException(final String processName) {\n        super(\"Can't find the process.\");\n        setProcessDefinitionNameOnContext(processName);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/exception/SProcessDeletionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.exception;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class SProcessDeletionException extends SBonitaException {\n\n    private static final long serialVersionUID = 4908892108076783889L;\n\n    public SProcessDeletionException(final Throwable cause,\n            final SProcessDefinitionDeployInfo processDefinitionDeployInfo) {\n        this(cause, processDefinitionDeployInfo.getId());\n\n        setProcessDefinitionNameOnContext(processDefinitionDeployInfo.getName());\n        setProcessDefinitionVersionOnContext(processDefinitionDeployInfo.getVersion());\n    }\n\n    public SProcessDeletionException(final Throwable cause, final long processId) {\n        super(cause);\n        setProcessDefinitionIdOnContext(processId);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/exception/SProcessDeploymentInfoUpdateException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.exception;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Yanyan Liu\n */\npublic class SProcessDeploymentInfoUpdateException extends SBonitaException {\n\n    private static final long serialVersionUID = 3938659493530825527L;\n\n    public SProcessDeploymentInfoUpdateException(final String message) {\n        super(message);\n    }\n\n    public SProcessDeploymentInfoUpdateException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SProcessDeploymentInfoUpdateException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/exception/SProcessDisablementException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.exception;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SProcessDisablementException extends SBonitaException {\n\n    private static final long serialVersionUID = 7858287891716546529L;\n\n    public SProcessDisablementException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SProcessDisablementException(final String message) {\n        super(message);\n    }\n\n    public SProcessDisablementException(String message, Throwable cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/exception/SProcessEnablementException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.exception;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SProcessEnablementException extends SBonitaException {\n\n    private static final long serialVersionUID = -8844149610202633672L;\n\n    public SProcessEnablementException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SProcessEnablementException(final String message) {\n        super(message);\n    }\n\n    public SProcessEnablementException(String message, Throwable cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SActivityDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.core.process.definition.model.event.SBoundaryEventDefinition;\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Zhao Na\n * @author Matthieu Chaffotte\n */\npublic interface SActivityDefinition extends SFlowNodeDefinition {\n\n    SLoopCharacteristics getLoopCharacteristics();\n\n    List<SDataDefinition> getSDataDefinitions();\n\n    List<SBusinessDataDefinition> getBusinessDataDefinitions();\n\n    List<SOperation> getSOperations();\n\n    List<SBoundaryEventDefinition> getBoundaryEventDefinitions();\n\n    SBoundaryEventDefinition getBoundaryEventDefinition(String name) throws SBoundaryEventNotFoundException;\n\n    SBusinessDataDefinition getBusinessDataDefinition(String name);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SActorDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface SActorDefinition extends SNamedElement {\n\n    String getDescription();\n\n    boolean isInitiator();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SAutomaticTaskDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface SAutomaticTaskDefinition extends SActivityDefinition {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SBaseElement.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\nimport java.io.Serializable;\n\n// FIXME put in a common module\n/**\n * @author Matthieu Chaffotte\n */\npublic interface SBaseElement extends Serializable {\n\n    Long getId();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SBoundaryEventNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SBoundaryEventNotFoundException extends SBonitaException {\n\n    private static final long serialVersionUID = -3896999167981089031L;\n\n    public SBoundaryEventNotFoundException(final String boundaryEventName, final String activityName) {\n        super(\"No boundary event named \" + boundaryEventName + \" found in activity \" + activityName);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SBusinessDataDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\nimport java.io.Serializable;\n\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic interface SBusinessDataDefinition extends Serializable {\n\n    String getName();\n\n    String getDescription();\n\n    String getClassName();\n\n    SExpression getDefaultValueExpression();\n\n    boolean isMultiple();\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SCallActivityDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SCallActivityDefinition extends SActivityDefinition {\n\n    SExpression getCallableElement();\n\n    SExpression getCallableElementVersion();\n\n    List<SOperation> getDataInputOperations();\n\n    List<SOperation> getDataOutputOperations();\n\n    SCallableElementType getCallableElementType();\n\n    Map<String, SExpression> getProcessStartContractInputs();\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SCallableElementType.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic enum SCallableElementType {\n\n    PROCESS\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SConnectorDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.bpm.connector.FailAction;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface SConnectorDefinition extends SNamedElement {\n\n    String getConnectorId();\n\n    String getVersion();\n\n    ConnectorEvent getActivationEvent();\n\n    Map<String, SExpression> getInputs();\n\n    List<SOperation> getOutputs();\n\n    FailAction getFailAction();\n\n    String getErrorCode();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SConstraintDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\nimport java.util.List;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface SConstraintDefinition extends SNamedElement {\n\n    String getExpression();\n\n    String getExplanation();\n\n    List<String> getInputNames();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SContextEntry.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\nimport java.io.Serializable;\n\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface SContextEntry extends Serializable {\n\n    String getKey();\n\n    SExpression getExpression();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SContractDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\nimport java.util.List;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface SContractDefinition extends SInputContainerDefinition {\n\n    List<SConstraintDefinition> getConstraints();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SDocumentDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface SDocumentDefinition extends SNamedElement {\n\n    /**\n     * URL for an external document\n     *\n     * @return\n     */\n    String getUrl();\n\n    /**\n     * File reference in the process resources\n     *\n     * @return\n     */\n    String getFile();\n\n    /**\n     * mime type of the document's content.\n     */\n    String getMimeType();\n\n    /**\n     * @return\n     */\n    String getDescription();\n\n    /**\n     * @return\n     */\n    String getFileName();\n\n    SExpression getInitialValue();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SDocumentListDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * A document list is a named element that define a list of document on a process\n * It contains 0 or more document and have a name to reference it in the process instance\n * It is initialized when the process start using the {@link #getExpression()} expression\n *\n * @author Baptiste Mesta\n * @since 6.4.0\n */\npublic interface SDocumentListDefinition extends SNamedElement {\n\n    /**\n     * @return the description of the document list\n     */\n    String getDescription();\n\n    /**\n     * The expression that will be evaluated when we initialize the document list\n     *\n     * @return the initial value expression\n     */\n    SExpression getExpression();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SFlowElementContainerDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\nimport java.util.List;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.core.process.definition.model.event.SBoundaryEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SEndEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SIntermediateCatchEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SIntermediateThrowEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SStartEventDefinition;\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic interface SFlowElementContainerDefinition extends SBaseElement {\n\n    Set<SFlowNodeDefinition> getFlowNodes();\n\n    List<SBoundaryEventDefinition> getBoundaryEvents();\n\n    SBoundaryEventDefinition getBoundaryEvent(String name) throws SBoundaryEventNotFoundException;\n\n    SFlowNodeDefinition getFlowNode(long id);\n\n    Set<SActivityDefinition> getActivities();\n\n    Set<STransitionDefinition> getTransitions();\n\n    STransitionDefinition getTransition(String transitionId);\n\n    Set<SGatewayDefinition> getGateways();\n\n    SGatewayDefinition getGateway(String name);\n\n    List<SStartEventDefinition> getStartEvents();\n\n    List<SIntermediateCatchEventDefinition> getIntermediateCatchEvents();\n\n    List<SIntermediateThrowEventDefinition> getIntermdiateThrowEvents();\n\n    List<SEndEventDefinition> getEndEvents();\n\n    List<SDataDefinition> getDataDefinitions();\n\n    List<SBusinessDataDefinition> getBusinessDataDefinitions();\n\n    SBusinessDataDefinition getBusinessDataDefinition(String name);\n\n    List<SDocumentDefinition> getDocumentDefinitions();\n\n    /**\n     * @param name\n     *        the name of the connector definition\n     * @return\n     *         the connector definition having that name\n     * @since 6.1\n     */\n    SConnectorDefinition getConnectorDefinition(String name);\n\n    List<SConnectorDefinition> getConnectors(ConnectorEvent connectorEvent);\n\n    List<SConnectorDefinition> getConnectors();\n\n    SFlowNodeDefinition getFlowNode(String targetFlowNode);\n\n    boolean containsInclusiveGateway();\n\n    /**\n     * @return\n     * @since 6.4.0\n     */\n    Set<SSubProcessDefinition> getSubProcessDefinitions();\n\n    /**\n     * @return the document list definitions\n     * @since 6.4.0\n     */\n    List<SDocumentListDefinition> getDocumentListDefinitions();\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SFlowNodeDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Feng Hui\n * @author Zhao Na\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface SFlowNodeDefinition extends SNamedElement {\n\n    SFlowElementContainerDefinition getParentContainer();\n\n    /**\n     * Gets the outgoing transitions of the activity.\n     *\n     * @return the outgoing transitions of the activity\n     */\n    List<STransitionDefinition> getOutgoingTransitions();\n\n    /**\n     * Gets the incoming transitions of the activity.\n     *\n     * @return the incoming transitions of the activity\n     */\n    List<STransitionDefinition> getIncomingTransitions();\n\n    /**\n     * Checks whether the activity has outgoing transitions.\n     *\n     * @return true if the activity contains outgoing transitions; false otherwise;\n     */\n    boolean hasOutgoingTransitions();\n\n    /**\n     * Checks whether the activity contains incoming transitions.\n     *\n     * @return true if the activity contains incoming transitions; false otherwise\n     */\n    boolean hasIncomingTransitions();\n\n    STransitionDefinition getDefaultTransition();\n\n    List<SConnectorDefinition> getConnectors();\n\n    /**\n     * @return\n     * @since 6.3\n     */\n    boolean hasConnectors();\n\n    /**\n     * @param name\n     * @return\n     * @since 6.1\n     */\n    SConnectorDefinition getConnectorDefinition(String name);\n\n    SFlowNodeType getType();\n\n    String getDescription();\n\n    SExpression getDisplayDescription();\n\n    SExpression getDisplayDescriptionAfterCompletion();\n\n    SExpression getDisplayName();\n\n    List<SConnectorDefinition> getConnectors(ConnectorEvent connectorEvent);\n\n    int getTransitionIndex(Long transitionId);\n\n    boolean isStartable();\n\n    boolean isParalleleOrInclusive();\n\n    boolean isExclusive();\n\n    boolean isInterrupting();\n\n    boolean isBoundaryEvent();\n\n    boolean isEventSubProcess();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SFlowNodeType.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\n/**\n * @author Zhang Bole\n * @author Baptitse Mesta\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic enum SFlowNodeType {\n\n    AUTOMATIC_TASK(\"automatic task\"),\n\n    USER_TASK(\"user task\"),\n\n    MANUAL_TASK(\"manual task\"),\n\n    RECEIVE_TASK(\"receive task\"),\n\n    SEND_TASK(\"send task\"),\n\n    GATEWAY(\"gateway\"),\n\n    START_EVENT(\"start event\"),\n\n    INTERMEDIATE_CATCH_EVENT(\"intermediate catch event\"),\n\n    BOUNDARY_EVENT(\"boundary event\"),\n\n    INTERMEDIATE_THROW_EVENT(\"intermediate throw event\"),\n\n    END_EVENT(\"end event\"),\n\n    LOOP_ACTIVITY(\"loop activity\"),\n\n    MULTI_INSTANCE_ACTIVITY(\"multi-instance activity\"),\n\n    CALL_ACTIVITY(\"call activity\"),\n\n    SUB_PROCESS(\"sub process\");\n\n    private final String value;\n\n    private SFlowNodeType(final String value) {\n        this.value = value;\n    }\n\n    public String getValue() {\n        return value;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SGatewayDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\n/**\n * @author Feng Hui\n * @author Matthieu Chaffotte\n */\npublic interface SGatewayDefinition extends SFlowNodeDefinition {\n\n    SGatewayType getGatewayType();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SGatewayType.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\n/**\n * @author Feng Hui\n * @author Matthieu Chaffotte\n */\npublic enum SGatewayType {\n    PARALLEL, INCLUSIVE, EXCLUSIVE\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SHumanTaskDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface SHumanTaskDefinition extends STaskDefinition {\n\n    String getActorName();\n\n    SUserFilterDefinition getSUserFilterDefinition();\n\n    SExpression getExpectedDuration();\n\n    String getPriority();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SInputContainerDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\nimport java.util.List;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface SInputContainerDefinition extends SBaseElement {\n\n    List<SInputDefinition> getInputDefinitions();\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SInputDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface SInputDefinition extends SNamedElement, SInputContainerDefinition {\n\n    String getDescription();\n\n    boolean isMultiple();\n\n    SType getType();\n\n    boolean hasChildren();\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SLoopCharacteristics.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\nimport java.io.Serializable;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface SLoopCharacteristics extends Serializable {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SManualTaskDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface SManualTaskDefinition extends SHumanTaskDefinition {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SMultiInstanceLoopCharacteristics.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface SMultiInstanceLoopCharacteristics extends SLoopCharacteristics {\n\n    boolean isSequential();\n\n    SExpression getLoopCardinality();\n\n    SExpression getCompletionCondition();\n\n    String getLoopDataInputRef();\n\n    String getLoopDataOutputRef();\n\n    String getDataInputItemRef();\n\n    String getDataOutputItemRef();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SNamedElement.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\n// FIXME put in a common module\n/**\n * @author Matthieu Chaffotte\n */\npublic interface SNamedElement extends SBaseElement {\n\n    /**\n     * Gets the name of the element.\n     *\n     * @return the element name\n     */\n    String getName();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SParameterDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface SParameterDefinition extends SNamedElement {\n\n    String getDescription();\n\n    String getType();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SProcessDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\nimport java.util.List;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic interface SProcessDefinition extends SNamedElement {\n\n    String getVersion();\n\n    String getDescription();\n\n    SFlowElementContainerDefinition getProcessContainer();\n\n    SActorDefinition getActorInitiator();\n\n    Set<SActorDefinition> getActors();\n\n    String getStringIndexLabel(int index);\n\n    SExpression getStringIndexValue(int index);\n\n    Set<SParameterDefinition> getParameters();\n\n    SParameterDefinition getParameter(String parameterName);\n\n    boolean hasConnectors();\n\n    SContractDefinition getContract();\n\n    List<SContextEntry> getContext();\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SProcessDefinitionDeployInfo.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.JoinColumn;\nimport javax.persistence.ManyToOne;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@Builder\n@Entity\n@Table(name = \"process_definition\")\npublic class SProcessDefinitionDeployInfo implements PersistentObject {\n\n    public static final String DESCRIPTION = \"description\";\n    public static final String ID_KEY = \"id\";\n    public static final String NAME_KEY = \"name\";\n    public static final String VERSION_KEY = \"version\";\n    public static final String DEPLOYMENT_DATE_KEY = \"deploymentDate\";\n    public static final String DEPLOYED_BY_KEY = \"deployedBy\";\n    public static final String ACTIVATION_STATE_KEY = \"activationState\";\n    public static final String CONFIGURATION_STATE_KEY = \"configurationState\";\n    public static final String PROCESS_ID_KEY = \"processId\";\n    public static final String DISPLAY_NAME_KEY = \"displayName\";\n    public static final String DISPLAY_DESCRIPTION_KEY = \"displayDescription\";\n    public static final String LAST_UPDATE_DATE_KEY = \"lastUpdateDate\";\n    public static final String ICON_PATH = \"iconPath\";\n    public static final String DESIGN_CONTENT = \"designContent.content\";\n    public static final String LABEL = \"label\";\n\n    private String name;\n    @Id\n    private long id;\n    private long deploymentDate;\n    private long deployedBy;\n    private String version;\n    private String description;\n    private String configurationState;\n    private String activationState;\n    private long processId;\n    private String displayName;\n    private long lastUpdateDate;\n    private String iconPath;\n    private String displayDescription;\n    @ManyToOne\n    @JoinColumn(name = \"content_id\", referencedColumnName = \"id\")\n    private SProcessDefinitionDesignContent designContent;\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SProcessDefinitionDesignContent.java",
    "content": "/**\n * Copyright (C) 2015 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@Entity\n@Table(name = \"process_content\")\n@Builder\npublic class SProcessDefinitionDesignContent implements PersistentObject {\n\n    @Id\n    private long id;\n    private String content;\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SReceiveTaskDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SCatchMessageEventTriggerDefinition;\n\n/**\n * @author Julien Molinaro\n */\npublic interface SReceiveTaskDefinition extends SActivityDefinition {\n\n    SCatchMessageEventTriggerDefinition getTrigger();\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SSendTaskDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowMessageEventTriggerDefinition;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface SSendTaskDefinition extends SActivityDefinition {\n\n    SThrowMessageEventTriggerDefinition getMessageTrigger();\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SStandardLoopCharacteristics.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface SStandardLoopCharacteristics extends SLoopCharacteristics {\n\n    SExpression getLoopCondition();\n\n    boolean isTestBefore();\n\n    SExpression getLoopMax();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SSubProcessDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface SSubProcessDefinition extends SActivityDefinition {\n\n    boolean isTriggeredByEvent();\n\n    SFlowElementContainerDefinition getSubProcessContainer();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/STaskDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface STaskDefinition extends SActivityDefinition {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/STransitionDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic interface STransitionDefinition extends SNamedElement {\n\n    long getSource();\n\n    long getTarget();\n\n    SExpression getCondition();\n\n    boolean hasCondition();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SType.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\nimport java.math.BigDecimal;\nimport java.math.BigInteger;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.OffsetDateTime;\nimport java.util.Arrays;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.contract.FileInputValue;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic enum SType {\n\n    TEXT(String.class, Character.class), BOOLEAN(Boolean.class), DATE(Date.class), INTEGER(Integer.class, Long.class,\n            BigInteger.class, Short.class, Byte.class), DECIMAL(Float.class, Double.class, BigDecimal.class,\n                    Integer.class, Long.class, BigInteger.class, Short.class,\n                    Byte.class), BYTE_ARRAY(byte[].class), FILE(FileInputValue.class), LONG(Long.class, Integer.class,\n                            BigInteger.class, Short.class, Byte.class), LOCALDATE(LocalDate.class), LOCALDATETIME(\n                                    LocalDateTime.class), OFFSETDATETIME(OffsetDateTime.class);\n\n    private final List<Class<?>> assignableTypes;\n\n    SType(final Class<?>... assignableTypes) {\n        this.assignableTypes = Arrays.asList(assignableTypes);\n    }\n\n    public boolean validate(final Object object) {\n        if (object == null) {\n            return true;\n        }\n        for (final Class<?> clazz : assignableTypes) {\n            if (object.getClass().isAssignableFrom(clazz)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SUserFilterDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface SUserFilterDefinition extends SNamedElement {\n\n    String getUserFilterId();\n\n    String getVersion();\n\n    Map<String, SExpression> getInputs();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SUserTaskDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\nimport java.util.List;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface SUserTaskDefinition extends SHumanTaskDefinition {\n\n    SContractDefinition getContract();\n\n    List<SContextEntry> getContext();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/TransitionState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\n/**\n * @author Zhao Na\n * @author Elias Ricken de Medeiros\n */\npublic enum TransitionState {\n    TAKEN, ABORTED\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/SActorLogBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface SActorLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/SActorLogBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface SActorLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/SBusinessDataDefinitionBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder;\n\nimport org.bonitasoft.engine.core.process.definition.model.SBusinessDataDefinition;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic interface SBusinessDataDefinitionBuilder {\n\n    SBusinessDataDefinitionBuilder setDescription(String description);\n\n    SBusinessDataDefinitionBuilder setDefaultValue(SExpression expression);\n\n    SBusinessDataDefinitionBuilder setMultiple(boolean isMultiple);\n\n    SBusinessDataDefinition done();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/SBusinessDataDefinitionBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic interface SBusinessDataDefinitionBuilderFactory {\n\n    SBusinessDataDefinitionBuilder createNewInstance(String name, String className);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/SProcessDefinitionBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder;\n\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface SProcessDefinitionBuilder {\n\n    SProcessDefinition done();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/SProcessDefinitionBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder;\n\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface SProcessDefinitionBuilderFactory {\n\n    SProcessDefinitionBuilder createNewInstance(DesignProcessDefinition processDefinition);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/SProcessDefinitionDeployInfoUpdateBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder;\n\nimport org.bonitasoft.engine.bpm.process.ActivationState;\nimport org.bonitasoft.engine.bpm.process.ConfigurationState;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Yanyan Liu\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic interface SProcessDefinitionDeployInfoUpdateBuilder {\n\n    EntityUpdateDescriptor done();\n\n    SProcessDefinitionDeployInfoUpdateBuilder updateDisplayName(final String displayName);\n\n    SProcessDefinitionDeployInfoUpdateBuilder updateActivationState(final ActivationState activationState);\n\n    SProcessDefinitionDeployInfoUpdateBuilder updateConfigurationState(final ConfigurationState configurationState);\n\n    SProcessDefinitionDeployInfoUpdateBuilder updateIconPath(final String iconPath);\n\n    SProcessDefinitionDeployInfoUpdateBuilder updateDisplayDescription(String value);\n\n    SProcessDefinitionDeployInfoUpdateBuilder updateDesignContent(String processDefinitionAsXMLString);\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/SProcessDefinitionDeployInfoUpdateBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder;\n\n/**\n * @author Yanyan Liu\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic interface SProcessDefinitionDeployInfoUpdateBuilderFactory {\n\n    SProcessDefinitionDeployInfoUpdateBuilder createNewInstance();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/SProcessDefinitionLogBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SProcessDefinitionLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/SProcessDefinitionLogBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SProcessDefinitionLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory {\n\n    SProcessDefinitionLogBuilder createNewInstance();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/ServerModelConvertor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.businessdata.BusinessDataDefinition;\nimport org.bonitasoft.engine.bpm.data.DataDefinition;\nimport org.bonitasoft.engine.bpm.data.TextDataDefinition;\nimport org.bonitasoft.engine.bpm.data.XMLDataDefinition;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.core.operation.model.SOperatorType;\nimport org.bonitasoft.engine.core.operation.model.builder.SLeftOperandBuilderFactory;\nimport org.bonitasoft.engine.core.operation.model.builder.SOperationBuilderFactory;\nimport org.bonitasoft.engine.core.process.definition.model.SBusinessDataDefinition;\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\nimport org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilder;\nimport org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilderFactory;\nimport org.bonitasoft.engine.data.definition.model.builder.SXMLDataDefinitionBuilder;\nimport org.bonitasoft.engine.data.definition.model.builder.SXMLDataDefinitionBuilderFactory;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.expression.model.builder.SExpressionBuilderFactory;\nimport org.bonitasoft.engine.operation.Operation;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic class ServerModelConvertor {\n\n    public static SExpression convertExpression(final Expression value) {\n        if (value == null) {\n            return null;\n        }\n        final ArrayList<SExpression> dependencies = new ArrayList<SExpression>();\n        for (final Expression expression : value.getDependencies()) {\n            dependencies.add(convertExpression(expression));\n        }\n        try {\n            return BuilderFactory.get(SExpressionBuilderFactory.class).createNewInstance().setName(value.getName())\n                    .setContent(value.getContent())\n                    .setExpressionType(value.getExpressionType()).setInterpreter(value.getInterpreter())\n                    .setReturnType(value.getReturnType())\n                    .setDependencies(dependencies).done();\n        } catch (final SInvalidExpressionException e) {\n            throw new IllegalArgumentException(\"Error building SExpression\", e);\n        }\n    }\n\n    public static SOperation convertOperation(final Operation operation) {\n        if (operation == null) {\n            return null;\n        }\n        return BuilderFactory\n                .get(SOperationBuilderFactory.class)\n                .createNewInstance()\n                .setOperator(operation.getOperator())\n                .setType(SOperatorType.valueOf(operation.getType().name()))\n                .setRightOperand(ServerModelConvertor.convertExpression(operation.getRightOperand()))\n                .setLeftOperand(BuilderFactory.get(SLeftOperandBuilderFactory.class).createNewInstance()\n                        .setName(operation.getLeftOperand().getName())\n                        .setType(operation.getLeftOperand().getType())\n                        .done())\n                .done();\n    }\n\n    public static List<SOperation> convertOperations(final List<Operation> operations) {\n        if (operations == null) {\n            return Collections.emptyList();\n        }\n        final List<SOperation> sOperations = new ArrayList<SOperation>(operations.size());\n        for (final Operation operation : operations) {\n            sOperations.add(convertOperation(operation));\n        }\n        return sOperations;\n    }\n\n    public static SDataDefinition convertDataDefinition(final DataDefinition dataDefinition) {\n        if (dataDefinition instanceof XMLDataDefinition xmlDataDef) {\n            final SXMLDataDefinitionBuilderFactory fact = BuilderFactory.get(SXMLDataDefinitionBuilderFactory.class);\n            final SXMLDataDefinitionBuilder builder = fact.createNewXMLData(dataDefinition.getName())\n                    .setElement(xmlDataDef.getElement())\n                    .setNamespace(xmlDataDef.getNamespace());\n            builder.setDefaultValue(ServerModelConvertor.convertExpression(dataDefinition.getDefaultValueExpression()));\n            builder.setDescription(dataDefinition.getDescription());\n            builder.setTransient(dataDefinition.isTransientData());\n            return builder.done();\n        }\n        final SDataDefinitionBuilderFactory fact = BuilderFactory.get(SDataDefinitionBuilderFactory.class);\n        SDataDefinitionBuilder builder;\n        if (dataDefinition instanceof TextDataDefinition textDataDefinition) {\n            builder = fact.createNewTextData(dataDefinition.getName()).setAsLongText(textDataDefinition.isLongText());\n        } else {\n            builder = fact.createNewInstance(dataDefinition.getName(), dataDefinition.getClassName());\n        }\n        builder.setDefaultValue(ServerModelConvertor.convertExpression(dataDefinition.getDefaultValueExpression()));\n        builder.setDescription(dataDefinition.getDescription());\n        builder.setTransient(dataDefinition.isTransientData());\n        return builder.done();\n    }\n\n    public static SBusinessDataDefinition convertBusinessDataDefinition(\n            final BusinessDataDefinition businessDataDefinition) {\n        if (businessDataDefinition == null) {\n            return null;\n        }\n        final SBusinessDataDefinitionBuilder builder = getSBusinessDataDefinitionBuilder(businessDataDefinition);\n        builder.setDefaultValue(\n                ServerModelConvertor.convertExpression(businessDataDefinition.getDefaultValueExpression()));\n        builder.setDescription(businessDataDefinition.getDescription());\n        builder.setMultiple(businessDataDefinition.isMultiple());\n        return builder.done();\n    }\n\n    protected static SBusinessDataDefinitionBuilder getSBusinessDataDefinitionBuilder(\n            final BusinessDataDefinition businessDataDefinition) {\n        final SBusinessDataDefinitionBuilderFactory fact = BuilderFactory\n                .get(SBusinessDataDefinitionBuilderFactory.class);\n        return fact.createNewInstance(businessDataDefinition.getName(), businessDataDefinition.getClassName());\n    }\n\n    public static Map<String, SExpression> convertContractInputs(Map<String, Expression> processStartContractInputs) {\n        final HashMap<String, SExpression> serverContractInputs = new HashMap<>(processStartContractInputs.size());\n        for (Map.Entry<String, Expression> entry : processStartContractInputs.entrySet()) {\n            serverContractInputs.put(entry.getKey(), convertExpression(entry.getValue()));\n        }\n        return serverContractInputs;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/event/trigger/SEndEventDefinitionBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder.event.trigger;\n\nimport org.bonitasoft.engine.core.process.definition.model.event.SEndEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowErrorEventTriggerDefinition;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface SEndEventDefinitionBuilder {\n\n    SEndEventDefinition done();\n\n    SEndEventDefinitionBuilder addErrorEventTriggerDefinition(SThrowErrorEventTriggerDefinition errorEventTrigger);\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/event/trigger/SEndEventDefinitionBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder.event.trigger;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface SEndEventDefinitionBuilderFactory {\n\n    SEndEventDefinitionBuilder createNewInstance(String name);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/event/trigger/SThrowErrorEventTriggerDefinitionBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder.event.trigger;\n\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowErrorEventTriggerDefinition;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface SThrowErrorEventTriggerDefinitionBuilder {\n\n    SThrowErrorEventTriggerDefinition done();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/event/trigger/SThrowErrorEventTriggerDefinitionBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder.event.trigger;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface SThrowErrorEventTriggerDefinitionBuilderFactory {\n\n    SThrowErrorEventTriggerDefinitionBuilder createNewInstance(final String errorCode);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/event/trigger/SThrowMessageEventTriggerDefinitionBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder.event.trigger;\n\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowMessageEventTriggerDefinition;\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SThrowMessageEventTriggerDefinitionBuilder {\n\n    SThrowMessageEventTriggerDefinitionBuilder addCorrelation(final SExpression key, final SExpression value);\n\n    SThrowMessageEventTriggerDefinitionBuilder addData(SDataDefinition dataDefinition);\n\n    SThrowMessageEventTriggerDefinition done();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/event/trigger/SThrowMessageEventTriggerDefinitionBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder.event.trigger;\n\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SThrowMessageEventTriggerDefinitionBuilderFactory {\n\n    SThrowMessageEventTriggerDefinitionBuilder createNewInstance(final String name, final SExpression targetProcessName,\n            final SExpression targetFlowNodeName);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/event/trigger/SThrowSignalEventTriggerDefinitionBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder.event.trigger;\n\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowSignalEventTriggerDefinition;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SThrowSignalEventTriggerDefinitionBuilder {\n\n    SThrowSignalEventTriggerDefinition done();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/event/trigger/SThrowSignalEventTriggerDefinitionBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder.event.trigger;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SThrowSignalEventTriggerDefinitionBuilderFactory {\n\n    SThrowSignalEventTriggerDefinitionBuilder createNewInstance(String signalName);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/event/trigger/impl/SEndEventDefinitionBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.impl;\n\nimport java.util.UUID;\n\nimport org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SEndEventDefinitionBuilder;\nimport org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SEndEventDefinitionBuilderFactory;\nimport org.bonitasoft.engine.core.process.definition.model.event.impl.SEndEventDefinitionImpl;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SEndEventDefinitionBuilderFactoryImpl implements SEndEventDefinitionBuilderFactory {\n\n    @Override\n    public SEndEventDefinitionBuilder createNewInstance(final String name) {\n        final SEndEventDefinitionImpl entity = new SEndEventDefinitionImpl(UUID.randomUUID().getLeastSignificantBits(),\n                name);\n        return new SEndEventDefinitionBuilderImpl(entity);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/event/trigger/impl/SEndEventDefinitionBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.impl;\n\nimport org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SEndEventDefinitionBuilder;\nimport org.bonitasoft.engine.core.process.definition.model.event.SEndEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.impl.SEndEventDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowErrorEventTriggerDefinition;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SEndEventDefinitionBuilderImpl implements SEndEventDefinitionBuilder {\n\n    private final SEndEventDefinitionImpl entity;\n\n    public SEndEventDefinitionBuilderImpl(final SEndEventDefinitionImpl entity) {\n        super();\n        this.entity = entity;\n    }\n\n    public SEndEventDefinition done() {\n        return entity;\n    }\n\n    @Override\n    public SEndEventDefinitionBuilder addErrorEventTriggerDefinition(\n            final SThrowErrorEventTriggerDefinition errorEventTrigger) {\n        entity.addErrorEventTriggerDefinition(errorEventTrigger);\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/event/trigger/impl/SThrowErrorEventTriggerDefinitionBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.impl;\n\nimport org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SThrowErrorEventTriggerDefinitionBuilder;\nimport org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SThrowErrorEventTriggerDefinitionBuilderFactory;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowErrorEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.SThrowErrorEventTriggerDefinitionImpl;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SThrowErrorEventTriggerDefinitionBuilderFactoryImpl\n        implements SThrowErrorEventTriggerDefinitionBuilderFactory {\n\n    @Override\n    public SThrowErrorEventTriggerDefinitionBuilder createNewInstance(final String errorCode) {\n        final SThrowErrorEventTriggerDefinition entity = new SThrowErrorEventTriggerDefinitionImpl(errorCode);\n        return new SThrowErrorEventTriggerDefinitionBuilderImpl(entity);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/event/trigger/impl/SThrowErrorEventTriggerDefinitionBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.impl;\n\nimport org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SThrowErrorEventTriggerDefinitionBuilder;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowErrorEventTriggerDefinition;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SThrowErrorEventTriggerDefinitionBuilderImpl implements SThrowErrorEventTriggerDefinitionBuilder {\n\n    private final SThrowErrorEventTriggerDefinition entity;\n\n    public SThrowErrorEventTriggerDefinitionBuilderImpl(final SThrowErrorEventTriggerDefinition entity) {\n        super();\n        this.entity = entity;\n    }\n\n    @Override\n    public SThrowErrorEventTriggerDefinition done() {\n        return entity;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/event/trigger/impl/SThrowMessageEventTriggerDefinitionBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.impl;\n\nimport org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SThrowMessageEventTriggerDefinitionBuilder;\nimport org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SThrowMessageEventTriggerDefinitionBuilderFactory;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.SThrowMessageEventTriggerDefinitionImpl;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SThrowMessageEventTriggerDefinitionBuilderFactoryImpl\n        implements SThrowMessageEventTriggerDefinitionBuilderFactory {\n\n    @Override\n    public SThrowMessageEventTriggerDefinitionBuilder createNewInstance(final String name,\n            final SExpression targetProcessName,\n            final SExpression targetFlowNodeName) {\n        final SThrowMessageEventTriggerDefinitionImpl entity = new SThrowMessageEventTriggerDefinitionImpl();\n        entity.setMessageName(name);\n        entity.setTargetProcess(targetProcessName);\n        entity.setTargetFlowNode(targetFlowNodeName);\n        return new SThrowMessageEventTriggerDefinitionBuilderImpl(entity);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/event/trigger/impl/SThrowMessageEventTriggerDefinitionBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.impl;\n\nimport org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SThrowMessageEventTriggerDefinitionBuilder;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowMessageEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.SCorrelationDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.SThrowMessageEventTriggerDefinitionImpl;\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SThrowMessageEventTriggerDefinitionBuilderImpl implements SThrowMessageEventTriggerDefinitionBuilder {\n\n    private final SThrowMessageEventTriggerDefinitionImpl entity;\n\n    public SThrowMessageEventTriggerDefinitionBuilderImpl(final SThrowMessageEventTriggerDefinitionImpl entity) {\n        super();\n        this.entity = entity;\n    }\n\n    @Override\n    public SThrowMessageEventTriggerDefinitionBuilder addCorrelation(final SExpression key, final SExpression value) {\n        entity.addCorrelation(new SCorrelationDefinitionImpl(key, value));\n        return this;\n    }\n\n    @Override\n    public SThrowMessageEventTriggerDefinitionBuilder addData(final SDataDefinition dataDefinition) {\n        entity.addDataDefinition(dataDefinition);\n        return this;\n    }\n\n    @Override\n    public SThrowMessageEventTriggerDefinition done() {\n        return entity;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/event/trigger/impl/SThrowSignalEventTriggerDefinitionBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.impl;\n\nimport org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SThrowSignalEventTriggerDefinitionBuilder;\nimport org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SThrowSignalEventTriggerDefinitionBuilderFactory;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.SThrowSignalEventTriggerDefinitionImpl;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SThrowSignalEventTriggerDefinitionBuilderFactoryImpl\n        implements SThrowSignalEventTriggerDefinitionBuilderFactory {\n\n    @Override\n    public SThrowSignalEventTriggerDefinitionBuilder createNewInstance(final String signalName) {\n        final SThrowSignalEventTriggerDefinitionImpl entity = new SThrowSignalEventTriggerDefinitionImpl(signalName);\n        return new SThrowSignalEventTriggerDefinitionBuilderImpl(entity);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/event/trigger/impl/SThrowSignalEventTriggerDefinitionBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.impl;\n\nimport org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SThrowSignalEventTriggerDefinitionBuilder;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowSignalEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.SThrowSignalEventTriggerDefinitionImpl;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SThrowSignalEventTriggerDefinitionBuilderImpl implements SThrowSignalEventTriggerDefinitionBuilder {\n\n    private final SThrowSignalEventTriggerDefinitionImpl entity;\n\n    public SThrowSignalEventTriggerDefinitionBuilderImpl(final SThrowSignalEventTriggerDefinitionImpl entity) {\n        super();\n        this.entity = entity;\n    }\n\n    @Override\n    public SThrowSignalEventTriggerDefinition done() {\n        return entity;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/impl/ProcessDefinitionLogIndexesMapper.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder.impl;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic class ProcessDefinitionLogIndexesMapper {\n\n    public static final int PROCESS_DEFINITION_INDEX = 0;\n\n    public static final String PROCESS_DEFINITION_NAME = \"numericIndex1\";\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/impl/SBusinessDataDefinitionBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder.impl;\n\nimport org.bonitasoft.engine.core.process.definition.model.builder.SBusinessDataDefinitionBuilder;\nimport org.bonitasoft.engine.core.process.definition.model.builder.SBusinessDataDefinitionBuilderFactory;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SBusinessDataDefinitionImpl;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class SBusinessDataDefinitionBuilderFactoryImpl implements SBusinessDataDefinitionBuilderFactory {\n\n    @Override\n    public SBusinessDataDefinitionBuilder createNewInstance(final String name, final String className) {\n        final SBusinessDataDefinitionImpl businessDataDefinitionImpl = new SBusinessDataDefinitionImpl();\n        businessDataDefinitionImpl.setName(name);\n        businessDataDefinitionImpl.setClassName(className);\n        return new SBusinessDataDefinitionBuilderImpl(businessDataDefinitionImpl);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/impl/SBusinessDataDefinitionBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder.impl;\n\nimport org.bonitasoft.engine.core.process.definition.model.SBusinessDataDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.builder.SBusinessDataDefinitionBuilder;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SBusinessDataDefinitionImpl;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class SBusinessDataDefinitionBuilderImpl implements SBusinessDataDefinitionBuilder {\n\n    private final SBusinessDataDefinitionImpl businessDataDefinitionImpl;\n\n    public SBusinessDataDefinitionBuilderImpl(final SBusinessDataDefinitionImpl businessDataDefinitionImpl) {\n        this.businessDataDefinitionImpl = businessDataDefinitionImpl;\n    }\n\n    public static SBusinessDataDefinitionBuilder getInstance() {\n        return new SBusinessDataDefinitionBuilderImpl(null);\n    }\n\n    @Override\n    public SBusinessDataDefinitionBuilder setDescription(final String description) {\n        businessDataDefinitionImpl.setDescription(description);\n        return this;\n    }\n\n    @Override\n    public SBusinessDataDefinitionBuilder setDefaultValue(final SExpression expression) {\n        businessDataDefinitionImpl.setDefaultValueExpression(expression);\n        return this;\n    }\n\n    @Override\n    public SBusinessDataDefinitionBuilder setMultiple(boolean isMultiple) {\n        businessDataDefinitionImpl.setMultiple(isMultiple);\n        return this;\n    }\n\n    @Override\n    public SBusinessDataDefinition done() {\n        return businessDataDefinitionImpl;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/impl/SProcessDefinitionBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder.impl;\n\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionBuilder;\nimport org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionBuilderFactory;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SProcessDefinitionImpl;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SProcessDefinitionBuilderFactoryImpl implements SProcessDefinitionBuilderFactory {\n\n    @Override\n    public SProcessDefinitionBuilder createNewInstance(final DesignProcessDefinition processDefinition) {\n        final SProcessDefinitionImpl entity = new SProcessDefinitionImpl(processDefinition);\n        return new SProcessDefinitionBuilderImpl(entity);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/impl/SProcessDefinitionBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder.impl;\n\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionBuilder;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SProcessDefinitionImpl;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SProcessDefinitionBuilderImpl implements SProcessDefinitionBuilder {\n\n    private final SProcessDefinitionImpl entity;\n\n    public SProcessDefinitionBuilderImpl(final SProcessDefinitionImpl entity) {\n        super();\n        this.entity = entity;\n    }\n\n    @Override\n    public SProcessDefinition done() {\n        return entity;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/impl/SProcessDefinitionDeployInfoUpdateBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder.impl;\n\nimport org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionDeployInfoUpdateBuilder;\nimport org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionDeployInfoUpdateBuilderFactory;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Yanyan Liu\n * @author Zhang Bole\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class SProcessDefinitionDeployInfoUpdateBuilderFactoryImpl\n        implements SProcessDefinitionDeployInfoUpdateBuilderFactory {\n\n    public SProcessDefinitionDeployInfoUpdateBuilder createNewInstance() {\n        return new SProcessDefinitionDeployInfoUpdateBuilderImpl(new EntityUpdateDescriptor());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/impl/SProcessDefinitionDeployInfoUpdateBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder.impl;\n\nimport org.bonitasoft.engine.bpm.process.ActivationState;\nimport org.bonitasoft.engine.bpm.process.ConfigurationState;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionDeployInfoUpdateBuilder;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Yanyan Liu\n * @author Zhang Bole\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class SProcessDefinitionDeployInfoUpdateBuilderImpl implements SProcessDefinitionDeployInfoUpdateBuilder {\n\n    private final EntityUpdateDescriptor descriptor;\n\n    public SProcessDefinitionDeployInfoUpdateBuilderImpl(final EntityUpdateDescriptor descriptor) {\n        this.descriptor = descriptor;\n    }\n\n    @Override\n    public EntityUpdateDescriptor done() {\n        return descriptor;\n    }\n\n    @Override\n    public SProcessDefinitionDeployInfoUpdateBuilder updateDisplayName(final String displayName) {\n        descriptor.addField(SProcessDefinitionDeployInfo.DISPLAY_NAME_KEY, displayName);\n        return this;\n    }\n\n    @Override\n    public SProcessDefinitionDeployInfoUpdateBuilder updateDisplayDescription(final String description) {\n        descriptor.addField(SProcessDefinitionDeployInfo.DISPLAY_DESCRIPTION_KEY, description);\n        return this;\n    }\n\n    @Override\n    public SProcessDefinitionDeployInfoUpdateBuilder updateActivationState(final ActivationState activationState) {\n        descriptor.addField(SProcessDefinitionDeployInfo.ACTIVATION_STATE_KEY, activationState.name());\n        return this;\n    }\n\n    @Override\n    public SProcessDefinitionDeployInfoUpdateBuilder updateConfigurationState(\n            final ConfigurationState configurationState) {\n        descriptor.addField(SProcessDefinitionDeployInfo.CONFIGURATION_STATE_KEY, configurationState.name());\n        return this;\n    }\n\n    @Override\n    public SProcessDefinitionDeployInfoUpdateBuilder updateIconPath(final String iconPath) {\n        descriptor.addField(SProcessDefinitionDeployInfo.ICON_PATH, iconPath);\n        return this;\n    }\n\n    @Override\n    public SProcessDefinitionDeployInfoUpdateBuilder updateDesignContent(String processDefinitionAsXMLString) {\n        descriptor.addField(SProcessDefinitionDeployInfo.DESIGN_CONTENT, processDefinitionAsXMLString);\n        return this;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/impl/SProcessDefinitionLogBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder.impl;\n\nimport org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionLogBuilder;\nimport org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionLogBuilderFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SProcessDefinitionLogBuilderFactoryImpl extends CRUDELogBuilderFactory\n        implements SProcessDefinitionLogBuilderFactory {\n\n    @Override\n    public SProcessDefinitionLogBuilder createNewInstance() {\n        return new SProcessDefinitionLogBuilderImpl();\n    }\n\n    @Override\n    public String getObjectIdKey() {\n        return ProcessDefinitionLogIndexesMapper.PROCESS_DEFINITION_NAME;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/impl/SProcessDefinitionLogBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder.impl;\n\nimport org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SProcessDefinitionLogBuilderImpl extends CRUDELogBuilder implements SProcessDefinitionLogBuilder {\n\n    private static final String PROCESS_DEFINITION = \"PROCESS_DEFINITION\";\n\n    @Override\n    public SPersistenceLogBuilder objectId(final long objectId) {\n        queriableLogBuilder.numericIndex(ProcessDefinitionLogIndexesMapper.PROCESS_DEFINITION_INDEX, objectId);\n        return this;\n    }\n\n    @Override\n    protected String getActionTypePrefix() {\n        return PROCESS_DEFINITION;\n    }\n\n    @Override\n    protected void checkExtraRules(final SQueriableLog log) {\n        if (log.getActionStatus() != SQueriableLog.STATUS_FAIL) {\n            if (log.getNumericIndex(ProcessDefinitionLogIndexesMapper.PROCESS_DEFINITION_INDEX) == 0L) {\n                throw new MissingMandatoryFieldsException(\n                        \"Some mandatory fields are missing: ProcessDefinition deployment info Id\");\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/SBoundaryEventDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SBoundaryEventDefinition extends SCatchEventDefinition {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/SCatchEventDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SCatchErrorEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SCatchMessageEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SCatchSignalEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.STimerEventTriggerDefinition;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic interface SCatchEventDefinition extends SEventDefinition {\n\n    List<STimerEventTriggerDefinition> getTimerEventTriggerDefinitions();\n\n    List<SCatchMessageEventTriggerDefinition> getMessageEventTriggerDefinitions();\n\n    SCatchMessageEventTriggerDefinition getMessageEventTriggerDefinition(final String messageName);\n\n    List<SCatchSignalEventTriggerDefinition> getSignalEventTriggerDefinitions();\n\n    List<SCatchErrorEventTriggerDefinition> getErrorEventTriggerDefinitions();\n\n    SCatchErrorEventTriggerDefinition getErrorEventTriggerDefinition(final String errorCode);\n\n    boolean isInterrupting();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/SEndEventDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.STerminateEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowErrorEventTriggerDefinition;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic interface SEndEventDefinition extends SThrowEventDefinition {\n\n    STerminateEventTriggerDefinition getTerminateEventTriggerDefinition();\n\n    List<SThrowErrorEventTriggerDefinition> getErrorEventTriggerDefinitions();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/SEventDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerDefinition;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SEventDefinition extends SFlowNodeDefinition {\n\n    /**\n     * Retrieve an unmodifiable list of event triggers associate to this event. If none, an empty list is returned.\n     *\n     * @return an unmodifiable list of event triggers associate to this event\n     */\n    List<SEventTriggerDefinition> getEventTriggers();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/SImplicitThrowEventDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SImplicitThrowEventDefinition extends SThrowEventDefinition {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/SIntermediateCatchEventDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SIntermediateCatchEventDefinition extends SCatchEventDefinition {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/SIntermediateThrowEventDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SIntermediateThrowEventDefinition extends SThrowEventDefinition {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/SStartEventDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SStartEventDefinition extends SCatchEventDefinition {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/SThrowEventDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowMessageEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowSignalEventTriggerDefinition;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic interface SThrowEventDefinition extends SEventDefinition {\n\n    List<SThrowMessageEventTriggerDefinition> getMessageEventTriggerDefinitions();\n\n    SThrowMessageEventTriggerDefinition getMessageEventTriggerDefinition(final String messageName);\n\n    List<SThrowSignalEventTriggerDefinition> getSignalEventTriggerDefinitions();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/impl/SBoundaryEventDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.impl;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.flownode.CatchEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SBoundaryEventDefinition;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class SBoundaryEventDefinitionImpl extends SCatchEventDefinitionImpl implements SBoundaryEventDefinition {\n\n    private static final long serialVersionUID = 7591508888593777075L;\n\n    public SBoundaryEventDefinitionImpl(final long id, final String name) {\n        super(id, name);\n    }\n\n    public SBoundaryEventDefinitionImpl(final CatchEventDefinition eventDefinition,\n            final Map<String, STransitionDefinition> transitionsMap) {\n        super(eventDefinition, transitionsMap);\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.BOUNDARY_EVENT;\n    }\n\n    @Override\n    public boolean isStartable() {\n        return false;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/impl/SCatchEventDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.impl;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.flownode.CatchEventDefinition;\nimport org.bonitasoft.engine.bpm.flownode.CatchMessageEventTriggerDefinition;\nimport org.bonitasoft.engine.bpm.flownode.ErrorEventTriggerDefinition;\nimport org.bonitasoft.engine.bpm.flownode.SignalEventTriggerDefinition;\nimport org.bonitasoft.engine.bpm.flownode.TimerEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SCatchEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SCatchErrorEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SCatchMessageEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SCatchSignalEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.STimerEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.SCatchErrorEventTriggerDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.SCatchMessageEventTriggerDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.SCatchSignalEventTriggerDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.STimerEventTriggerDefinitionImpl;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic abstract class SCatchEventDefinitionImpl extends SEventDefinitionImpl implements SCatchEventDefinition {\n\n    private static final long serialVersionUID = -2803848099720886033L;\n\n    private final List<STimerEventTriggerDefinition> timerEventTriggers;\n\n    private final List<SCatchMessageEventTriggerDefinition> messageEventTriggers;\n\n    private final List<SCatchSignalEventTriggerDefinition> signalEventTriggers;\n\n    private final List<SCatchErrorEventTriggerDefinition> errorEventTriggers;\n\n    private boolean isInterrupting = true;\n\n    public SCatchEventDefinitionImpl(final CatchEventDefinition eventDefinition,\n            final Map<String, STransitionDefinition> transitionsMap) {\n        super(eventDefinition, transitionsMap);\n        isInterrupting = eventDefinition.isInterrupting();\n        timerEventTriggers = new ArrayList<STimerEventTriggerDefinition>(\n                eventDefinition.getTimerEventTriggerDefinitions().size());\n        for (final TimerEventTriggerDefinition timerTrigger : eventDefinition.getTimerEventTriggerDefinitions()) {\n            addTimerEventTrigger(new STimerEventTriggerDefinitionImpl(timerTrigger));\n        }\n        messageEventTriggers = new ArrayList<SCatchMessageEventTriggerDefinition>(\n                eventDefinition.getMessageEventTriggerDefinitions().size());\n        for (final CatchMessageEventTriggerDefinition catchMessageTrigger : eventDefinition\n                .getMessageEventTriggerDefinitions()) {\n            addMessageEventTrigger(new SCatchMessageEventTriggerDefinitionImpl(catchMessageTrigger));\n        }\n        signalEventTriggers = new ArrayList<SCatchSignalEventTriggerDefinition>(\n                eventDefinition.getSignalEventTriggerDefinitions().size());\n        for (final SignalEventTriggerDefinition signalTrigger : eventDefinition.getSignalEventTriggerDefinitions()) {\n            addSignalEventTrigger(new SCatchSignalEventTriggerDefinitionImpl(signalTrigger.getSignalName()));\n        }\n\n        errorEventTriggers = new ArrayList<SCatchErrorEventTriggerDefinition>(\n                eventDefinition.getErrorEventTriggerDefinitions().size());\n        for (final ErrorEventTriggerDefinition errorTrigger : eventDefinition.getErrorEventTriggerDefinitions()) {\n            addErrorEventTrigger(new SCatchErrorEventTriggerDefinitionImpl(errorTrigger.getErrorCode()));\n        }\n    }\n\n    public SCatchEventDefinitionImpl(final long id, final String name) {\n        super(id, name);\n        timerEventTriggers = new ArrayList<STimerEventTriggerDefinition>(1);\n        messageEventTriggers = new ArrayList<SCatchMessageEventTriggerDefinition>(5);\n        signalEventTriggers = new ArrayList<SCatchSignalEventTriggerDefinition>(1);\n        errorEventTriggers = new ArrayList<SCatchErrorEventTriggerDefinition>(1);\n    }\n\n    @Override\n    public List<STimerEventTriggerDefinition> getTimerEventTriggerDefinitions() {\n        return Collections.unmodifiableList(timerEventTriggers);\n    }\n\n    public void addTimerEventTrigger(final STimerEventTriggerDefinition timerEventTrigger) {\n        timerEventTriggers.add(timerEventTrigger);\n        addEventTriggerDefinition(timerEventTrigger);\n    }\n\n    @Override\n    public List<SCatchMessageEventTriggerDefinition> getMessageEventTriggerDefinitions() {\n        return Collections.unmodifiableList(messageEventTriggers);\n    }\n\n    public void addMessageEventTrigger(final SCatchMessageEventTriggerDefinition messageEventTrigger) {\n        messageEventTriggers.add(messageEventTrigger);\n        addEventTriggerDefinition(messageEventTrigger);\n    }\n\n    @Override\n    public SCatchMessageEventTriggerDefinition getMessageEventTriggerDefinition(final String messageName) {\n        final Iterator<SCatchMessageEventTriggerDefinition> iterator = messageEventTriggers.iterator();\n        boolean found = false;\n        SCatchMessageEventTriggerDefinition messageTrigger = null;\n        while (iterator.hasNext() && !found) {\n            final SCatchMessageEventTriggerDefinition sCatchMessageEventTriggerDefinition = iterator.next();\n            if (sCatchMessageEventTriggerDefinition.getMessageName().equals(messageName)) {\n                found = true;\n                messageTrigger = sCatchMessageEventTriggerDefinition;\n            }\n\n        }\n        return messageTrigger;\n    }\n\n    @Override\n    public List<SCatchSignalEventTriggerDefinition> getSignalEventTriggerDefinitions() {\n        return Collections.unmodifiableList(signalEventTriggers);\n    }\n\n    public void addSignalEventTrigger(final SCatchSignalEventTriggerDefinition signalEventTrigger) {\n        signalEventTriggers.add(signalEventTrigger);\n        addEventTriggerDefinition(signalEventTrigger);\n    }\n\n    public void setInterrupting(final boolean isInterrupting) {\n        this.isInterrupting = isInterrupting;\n    }\n\n    @Override\n    public boolean isInterrupting() {\n        return isInterrupting;\n    }\n\n    @Override\n    public List<SCatchErrorEventTriggerDefinition> getErrorEventTriggerDefinitions() {\n        return Collections.unmodifiableList(errorEventTriggers);\n    }\n\n    @Override\n    public SCatchErrorEventTriggerDefinition getErrorEventTriggerDefinition(final String errorCode) {\n        final Iterator<SCatchErrorEventTriggerDefinition> iterator = errorEventTriggers.iterator();\n        boolean found = false;\n        SCatchErrorEventTriggerDefinition trigger = null;\n        while (iterator.hasNext() && !found) {\n            final SCatchErrorEventTriggerDefinition currentTrigger = iterator.next();\n            if (currentTrigger.getErrorCode() == null && errorCode == null || currentTrigger.getErrorCode() != null\n                    && currentTrigger.getErrorCode().equals(errorCode)) {\n                found = true;\n                trigger = currentTrigger;\n            }\n\n        }\n        return trigger;\n    }\n\n    public void addErrorEventTrigger(final SCatchErrorEventTriggerDefinition errorEventTrigger) {\n        errorEventTriggers.add(errorEventTrigger);\n        addEventTriggerDefinition(errorEventTrigger);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/impl/SEndEventDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.impl;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.flownode.EndEventDefinition;\nimport org.bonitasoft.engine.bpm.flownode.TerminateEventTriggerDefinition;\nimport org.bonitasoft.engine.bpm.flownode.ThrowErrorEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SEndEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.STerminateEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowErrorEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.STerminateEventTriggerDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.SThrowErrorEventTriggerDefinitionImpl;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class SEndEventDefinitionImpl extends SThrowEventDefinitionImpl implements SEndEventDefinition {\n\n    private static final long serialVersionUID = 6671309950777321636L;\n\n    private STerminateEventTriggerDefinition sTerminateEventTriggerDefinition;\n\n    private final List<SThrowErrorEventTriggerDefinition> sErrorEventTriggerDefinitions;\n\n    public SEndEventDefinitionImpl(final EndEventDefinition eventDefinition,\n            final Map<String, STransitionDefinition> transitionsMap) {\n        super(eventDefinition, transitionsMap);\n        final TerminateEventTriggerDefinition terminateEventTriggerDefinition = eventDefinition\n                .getTerminateEventTriggerDefinition();\n        if (terminateEventTriggerDefinition != null) {\n            setTerminateEventTriggerDefinition(new STerminateEventTriggerDefinitionImpl());\n        }\n        final List<ThrowErrorEventTriggerDefinition> errorEventTriggerDefinitions = eventDefinition\n                .getErrorEventTriggerDefinitions();\n        sErrorEventTriggerDefinitions = new ArrayList<SThrowErrorEventTriggerDefinition>(\n                errorEventTriggerDefinitions.size());\n        for (final ThrowErrorEventTriggerDefinition throwErrorEventTriggerDefinition : errorEventTriggerDefinitions) {\n            addErrorEventTriggerDefinition(\n                    new SThrowErrorEventTriggerDefinitionImpl(throwErrorEventTriggerDefinition.getErrorCode()));\n        }\n    }\n\n    public SEndEventDefinitionImpl(final long id, final String name) {\n        super(id, name);\n        sErrorEventTriggerDefinitions = new ArrayList<SThrowErrorEventTriggerDefinition>(1);\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.END_EVENT;\n    }\n\n    @Override\n    public STerminateEventTriggerDefinition getTerminateEventTriggerDefinition() {\n        return sTerminateEventTriggerDefinition;\n    }\n\n    public void setTerminateEventTriggerDefinition(\n            final STerminateEventTriggerDefinition sTerminateEventTriggerDefinition) {\n        this.sTerminateEventTriggerDefinition = sTerminateEventTriggerDefinition;\n        if (sTerminateEventTriggerDefinition != null) {\n            addEventTriggerDefinition(sTerminateEventTriggerDefinition);\n        }\n    }\n\n    @Override\n    public List<SThrowErrorEventTriggerDefinition> getErrorEventTriggerDefinitions() {\n        return Collections.unmodifiableList(sErrorEventTriggerDefinitions);\n    }\n\n    public void addErrorEventTriggerDefinition(final SThrowErrorEventTriggerDefinition errorEventTrigger) {\n        sErrorEventTriggerDefinitions.add(errorEventTrigger);\n        addEventTriggerDefinition(errorEventTrigger);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/impl/SEventDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.impl;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.flownode.EventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SFlowNodeDefinitionImpl;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic abstract class SEventDefinitionImpl extends SFlowNodeDefinitionImpl implements SEventDefinition {\n\n    private static final long serialVersionUID = -5019901548085906144L;\n\n    private final List<SEventTriggerDefinition> eventTriggers;\n\n    public SEventDefinitionImpl(final EventDefinition eventDefinition,\n            final Map<String, STransitionDefinition> transitionsMap) {\n        super(eventDefinition, transitionsMap);\n        // initialize the list have an initial capacity of 1: most of time there will be zero or one event trigger\n        eventTriggers = new ArrayList<SEventTriggerDefinition>(1);\n    }\n\n    public SEventDefinitionImpl(final long id, final String name) {\n        super(id, name);\n        eventTriggers = new ArrayList<SEventTriggerDefinition>(1);\n    }\n\n    @Override\n    public List<SEventTriggerDefinition> getEventTriggers() {\n        return Collections.unmodifiableList(eventTriggers);\n    }\n\n    protected void addEventTriggerDefinition(final SEventTriggerDefinition eventTrigger) {\n        eventTriggers.add(eventTrigger);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/impl/SIntermediateCatchEventDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.impl;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.flownode.CatchEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SIntermediateCatchEventDefinition;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class SIntermediateCatchEventDefinitionImpl extends SCatchEventDefinitionImpl\n        implements SIntermediateCatchEventDefinition {\n\n    private static final long serialVersionUID = -5011521635705533392L;\n\n    public SIntermediateCatchEventDefinitionImpl(final CatchEventDefinition eventDefinition,\n            final Map<String, STransitionDefinition> transitionsMap) {\n        super(eventDefinition, transitionsMap);\n    }\n\n    public SIntermediateCatchEventDefinitionImpl(final long id, final String name) {\n        super(id, name);\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.INTERMEDIATE_CATCH_EVENT;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/impl/SIntermediateThrowEventDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.impl;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.flownode.ThrowEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SIntermediateThrowEventDefinition;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class SIntermediateThrowEventDefinitionImpl extends SThrowEventDefinitionImpl\n        implements SIntermediateThrowEventDefinition {\n\n    private static final long serialVersionUID = 5010523330087778508L;\n\n    public SIntermediateThrowEventDefinitionImpl(final ThrowEventDefinition eventDefinition,\n            final Map<String, STransitionDefinition> transitionsMap) {\n        super(eventDefinition, transitionsMap);\n    }\n\n    public SIntermediateThrowEventDefinitionImpl(final long id, final String name) {\n        super(id, name);\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.INTERMEDIATE_THROW_EVENT;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/impl/SStartEventDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.impl;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.flownode.StartEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SStartEventDefinition;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class SStartEventDefinitionImpl extends SCatchEventDefinitionImpl implements SStartEventDefinition {\n\n    private static final long serialVersionUID = -8788360140531631436L;\n\n    public SStartEventDefinitionImpl(final StartEventDefinition eventDefinition,\n            final Map<String, STransitionDefinition> transitionsMap) {\n        super(eventDefinition, transitionsMap);\n    }\n\n    public SStartEventDefinitionImpl(final long id, final String name) {\n        super(id, name);\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.START_EVENT;\n    }\n\n    @Override\n    public boolean isStartable() {\n        return getEventTriggers().isEmpty() && super.isStartable();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/impl/SThrowEventDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.impl;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.flownode.ThrowEventDefinition;\nimport org.bonitasoft.engine.bpm.flownode.ThrowMessageEventTriggerDefinition;\nimport org.bonitasoft.engine.bpm.flownode.ThrowSignalEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SThrowEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowMessageEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowSignalEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.SThrowMessageEventTriggerDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.SThrowSignalEventTriggerDefinitionImpl;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic abstract class SThrowEventDefinitionImpl extends SEventDefinitionImpl implements SThrowEventDefinition {\n\n    private static final long serialVersionUID = -3682882065277857160L;\n\n    private final List<SThrowMessageEventTriggerDefinition> sMessageEventTriggerDefinitions;\n\n    private final List<SThrowSignalEventTriggerDefinition> sSignalEventTriggerDefinitions;\n\n    public SThrowEventDefinitionImpl(final ThrowEventDefinition eventDefinition,\n            final Map<String, STransitionDefinition> transitionsMap) {\n        super(eventDefinition, transitionsMap);\n\n        final List<ThrowMessageEventTriggerDefinition> messageEventTriggerDefinitions = eventDefinition\n                .getMessageEventTriggerDefinitions();\n        sMessageEventTriggerDefinitions = new ArrayList<SThrowMessageEventTriggerDefinition>(\n                messageEventTriggerDefinitions.size());\n        for (final ThrowMessageEventTriggerDefinition throwMessageEventTriggerDefinition : messageEventTriggerDefinitions) {\n            addMessageEventTriggerDefinition(\n                    new SThrowMessageEventTriggerDefinitionImpl(throwMessageEventTriggerDefinition));\n        }\n\n        final List<ThrowSignalEventTriggerDefinition> signalEventTriggerDefinitions = eventDefinition\n                .getSignalEventTriggerDefinitions();\n        sSignalEventTriggerDefinitions = new ArrayList<SThrowSignalEventTriggerDefinition>(\n                signalEventTriggerDefinitions.size());\n        for (final ThrowSignalEventTriggerDefinition throwSignalEventTriggerDefinition : signalEventTriggerDefinitions) {\n            addSignalEventTriggerDefinition(\n                    new SThrowSignalEventTriggerDefinitionImpl(throwSignalEventTriggerDefinition.getSignalName()));\n        }\n    }\n\n    public SThrowEventDefinitionImpl(final long id, final String name) {\n        super(id, name);\n        sMessageEventTriggerDefinitions = new ArrayList<SThrowMessageEventTriggerDefinition>(5);\n        sSignalEventTriggerDefinitions = new ArrayList<SThrowSignalEventTriggerDefinition>(1);\n    }\n\n    @Override\n    public List<SThrowMessageEventTriggerDefinition> getMessageEventTriggerDefinitions() {\n        return Collections.unmodifiableList(sMessageEventTriggerDefinitions);\n    }\n\n    public void addMessageEventTriggerDefinition(\n            final SThrowMessageEventTriggerDefinition messageEventTriggerDefinition) {\n        addEventTriggerDefinition(messageEventTriggerDefinition);\n        sMessageEventTriggerDefinitions.add(messageEventTriggerDefinition);\n    }\n\n    @Override\n    public SThrowMessageEventTriggerDefinition getMessageEventTriggerDefinition(final String messageName) {\n        final Iterator<SThrowMessageEventTriggerDefinition> iterator = sMessageEventTriggerDefinitions.iterator();\n        boolean found = false;\n        SThrowMessageEventTriggerDefinition messageTrigger = null;\n        while (iterator.hasNext() && !found) {\n            final SThrowMessageEventTriggerDefinition sThrowMessageEventTriggerDefinition = iterator.next();\n            if (sThrowMessageEventTriggerDefinition.getMessageName().equals(messageName)) {\n                found = true;\n                messageTrigger = sThrowMessageEventTriggerDefinition;\n            }\n        }\n        return messageTrigger;\n    }\n\n    @Override\n    public List<SThrowSignalEventTriggerDefinition> getSignalEventTriggerDefinitions() {\n        return Collections.unmodifiableList(sSignalEventTriggerDefinitions);\n    }\n\n    public void addSignalEventTriggerDefinition(final SThrowSignalEventTriggerDefinition signalEventTriggerDefinition) {\n        addEventTriggerDefinition(signalEventTriggerDefinition);\n        sSignalEventTriggerDefinitions.add(signalEventTriggerDefinition);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/SCatchErrorEventTriggerDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.trigger;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SCatchErrorEventTriggerDefinition extends SErrorEventTriggerDefinition {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/SCatchMessageEventTriggerDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.trigger;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.operation.model.SOperation;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SCatchMessageEventTriggerDefinition extends SMessageEventTriggerDefinition {\n\n    List<SOperation> getOperations();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/SCatchSignalEventTriggerDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.trigger;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface SCatchSignalEventTriggerDefinition extends SSignalEventTriggerDefinition {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/SCorrelationDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.trigger;\n\nimport java.io.Serializable;\n\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface SCorrelationDefinition extends Serializable {\n\n    SExpression getKey();\n\n    SExpression getValue();\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/SErrorEventTriggerDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.trigger;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SErrorEventTriggerDefinition extends SEventTriggerDefinition {\n\n    String getErrorCode();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/SEventTriggerDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.trigger;\n\nimport org.bonitasoft.engine.core.process.definition.model.SBaseElement;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SEventTriggerDefinition extends SBaseElement {\n\n    SEventTriggerType getEventTriggerType();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/SEventTriggerType.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.trigger;\n\n/**\n * @author Baptiste Mesta\n */\npublic enum SEventTriggerType {\n\n    TIMER, ERROR, SIGNAL, MESSAGE, TERMINATE\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/SMessageEventTriggerDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.trigger;\n\nimport java.util.List;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SMessageEventTriggerDefinition extends SEventTriggerDefinition {\n\n    String getMessageName();\n\n    List<SCorrelationDefinition> getCorrelations();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/SSignalEventTriggerDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.trigger;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic interface SSignalEventTriggerDefinition extends SEventTriggerDefinition {\n\n    String getSignalName();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/STerminateEventTriggerDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.trigger;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface STerminateEventTriggerDefinition extends SEventTriggerDefinition {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/SThrowErrorEventTriggerDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.trigger;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SThrowErrorEventTriggerDefinition extends SErrorEventTriggerDefinition {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/SThrowMessageEventTriggerDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.trigger;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Yanyan Liu\n */\npublic interface SThrowMessageEventTriggerDefinition extends SMessageEventTriggerDefinition {\n\n    SExpression getTargetProcess();\n\n    SExpression getTargetFlowNode();\n\n    List<SDataDefinition> getDataDefinitions();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/SThrowSignalEventTriggerDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.trigger;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface SThrowSignalEventTriggerDefinition extends SSignalEventTriggerDefinition {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/STimerEventTriggerDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.trigger;\n\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface STimerEventTriggerDefinition extends SEventTriggerDefinition {\n\n    STimerType getTimerType();\n\n    SExpression getTimerExpression();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/STimerType.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.trigger;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic enum STimerType {\n\n    CYCLE,\n\n    DATE,\n\n    DURATION\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/impl/SCatchErrorEventTriggerDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.trigger.impl;\n\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SCatchErrorEventTriggerDefinition;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SCatchErrorEventTriggerDefinitionImpl extends SErrorEventTriggerDefinitionImpl\n        implements SCatchErrorEventTriggerDefinition {\n\n    private static final long serialVersionUID = -8087566164595708656L;\n\n    public SCatchErrorEventTriggerDefinitionImpl(final String errorCode) {\n        super(errorCode);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/impl/SCatchMessageEventTriggerDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.trigger.impl;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.flownode.CatchMessageEventTriggerDefinition;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.core.operation.model.SLeftOperand;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.core.operation.model.SOperatorType;\nimport org.bonitasoft.engine.core.operation.model.builder.SLeftOperandBuilderFactory;\nimport org.bonitasoft.engine.core.operation.model.builder.SOperationBuilderFactory;\nimport org.bonitasoft.engine.core.process.definition.model.builder.ServerModelConvertor;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SCatchMessageEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SCorrelationDefinition;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.operation.LeftOperand;\nimport org.bonitasoft.engine.operation.Operation;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SCatchMessageEventTriggerDefinitionImpl extends SMessageEventTriggerDefinitionImpl\n        implements SCatchMessageEventTriggerDefinition {\n\n    private static final long serialVersionUID = 8502224424679479589L;\n\n    private final List<SOperation> sOperations;\n\n    public SCatchMessageEventTriggerDefinitionImpl(final String messageName, final List<SOperation> operations,\n            final List<SCorrelationDefinition> correlations) {\n        super(messageName, correlations);\n        sOperations = operations;\n    }\n\n    public SCatchMessageEventTriggerDefinitionImpl() {\n        sOperations = new ArrayList<SOperation>();\n    }\n\n    public SCatchMessageEventTriggerDefinitionImpl(final CatchMessageEventTriggerDefinition messageEventTrigger) {\n        super(messageEventTrigger);\n        final List<Operation> operations = messageEventTrigger.getOperations();\n        sOperations = new ArrayList<SOperation>(operations.size());\n        for (final Operation operation : operations) {\n            sOperations.add(toSOperation(operation));\n        }\n    }\n\n    public SCatchMessageEventTriggerDefinitionImpl(\n            SCatchMessageEventTriggerDefinition catchMessageEventTriggerDefinition) {\n        super(catchMessageEventTriggerDefinition);\n        sOperations = catchMessageEventTriggerDefinition.getOperations();\n    }\n\n    private SOperation toSOperation(final Operation operation) {\n        final SExpression rightOperand = ServerModelConvertor.convertExpression(operation.getRightOperand());\n        final SOperatorType operatorType = SOperatorType.valueOf(operation.getType().name());\n        final SLeftOperand sLeftOperand = toSLeftOperand(operation.getLeftOperand());\n        final SOperation sOperation = BuilderFactory.get(SOperationBuilderFactory.class).createNewInstance()\n                .setOperator(operation.getOperator())\n                .setRightOperand(rightOperand).setType(operatorType).setLeftOperand(sLeftOperand).done();\n        return sOperation;\n    }\n\n    private SLeftOperand toSLeftOperand(final LeftOperand variableToSet) {\n        return BuilderFactory.get(SLeftOperandBuilderFactory.class).createNewInstance().setName(variableToSet.getName())\n                .setType(variableToSet.getType()).done();\n    }\n\n    @Override\n    public List<SOperation> getOperations() {\n        return Collections.unmodifiableList(sOperations);\n    }\n\n    public void addOperation(final SOperation operation) {\n        sOperations.add(operation);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/impl/SCatchSignalEventTriggerDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.trigger.impl;\n\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SCatchSignalEventTriggerDefinition;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SCatchSignalEventTriggerDefinitionImpl extends SSignalEventTriggerDefinitionImpl\n        implements SCatchSignalEventTriggerDefinition {\n\n    private static final long serialVersionUID = 6910759554558831366L;\n\n    public SCatchSignalEventTriggerDefinitionImpl(final String signalName) {\n        super(signalName);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/impl/SCorrelationDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.trigger.impl;\n\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SCorrelationDefinition;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SCorrelationDefinitionImpl implements SCorrelationDefinition {\n\n    private static final long serialVersionUID = -8249911809919027354L;\n\n    private SExpression key;\n\n    private SExpression value;\n\n    public SCorrelationDefinitionImpl(final SExpression key, final SExpression value) {\n        super();\n        this.key = key;\n        this.value = value;\n    }\n\n    @Override\n    public SExpression getKey() {\n        return key;\n    }\n\n    @Override\n    public SExpression getValue() {\n        return value;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + (key == null ? 0 : key.hashCode());\n        result = prime * result + (value == null ? 0 : value.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        final SCorrelationDefinitionImpl other = (SCorrelationDefinitionImpl) obj;\n        if (key == null) {\n            if (other.key != null) {\n                return false;\n            }\n        } else if (!key.equals(other.key)) {\n            return false;\n        }\n        if (value == null) {\n            if (other.value != null) {\n                return false;\n            }\n        } else if (!value.equals(other.value)) {\n            return false;\n        }\n        return true;\n    }\n\n    public void setKey(final SExpression key) {\n        this.key = key;\n    }\n\n    public void setValue(final SExpression value) {\n        this.value = value;\n    }\n\n    @Override\n    public String toString() {\n        return \"SCorrelationDefinitionImpl [key=\" + key + \", value=\" + value + \"]\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/impl/SErrorEventTriggerDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.trigger.impl;\n\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SErrorEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerType;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic abstract class SErrorEventTriggerDefinitionImpl extends SEventTriggerDefinitionImpl\n        implements SErrorEventTriggerDefinition {\n\n    private static final long serialVersionUID = -8002085238119587513L;\n\n    private final String errorCode;\n\n    public SErrorEventTriggerDefinitionImpl(final String errorCode) {\n        this.errorCode = errorCode;\n    }\n\n    @Override\n    public String getErrorCode() {\n        return errorCode;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + (errorCode == null ? 0 : errorCode.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        final SErrorEventTriggerDefinitionImpl other = (SErrorEventTriggerDefinitionImpl) obj;\n        if (errorCode == null) {\n            if (other.errorCode != null) {\n                return false;\n            }\n        } else if (!errorCode.equals(other.errorCode)) {\n            return false;\n        }\n        return true;\n    }\n\n    @Override\n    public SEventTriggerType getEventTriggerType() {\n        return SEventTriggerType.ERROR;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/impl/SEventTriggerDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.trigger.impl;\n\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SBaseElementImpl;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic abstract class SEventTriggerDefinitionImpl extends SBaseElementImpl implements SEventTriggerDefinition {\n\n    private static final long serialVersionUID = -5798250125022606994L;\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/impl/SMessageEventTriggerDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.trigger.impl;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.data.DataDefinition;\nimport org.bonitasoft.engine.bpm.data.TextDataDefinition;\nimport org.bonitasoft.engine.bpm.data.XMLDataDefinition;\nimport org.bonitasoft.engine.bpm.flownode.CorrelationDefinition;\nimport org.bonitasoft.engine.bpm.flownode.MessageEventTriggerDefinition;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.core.process.definition.model.builder.ServerModelConvertor;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SCatchMessageEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SCorrelationDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerType;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SMessageEventTriggerDefinition;\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\nimport org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilder;\nimport org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilderFactory;\nimport org.bonitasoft.engine.data.definition.model.builder.SXMLDataDefinitionBuilder;\nimport org.bonitasoft.engine.data.definition.model.builder.SXMLDataDefinitionBuilderFactory;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic class SMessageEventTriggerDefinitionImpl extends SEventTriggerDefinitionImpl\n        implements SMessageEventTriggerDefinition {\n\n    private static final long serialVersionUID = 4603391860834299674L;\n\n    private String messageName;\n\n    private final List<SCorrelationDefinition> correlations;\n\n    public SMessageEventTriggerDefinitionImpl(final String name, final List<SCorrelationDefinition> correlations) {\n        messageName = name;\n        this.correlations = correlations;\n    }\n\n    public SMessageEventTriggerDefinitionImpl() {\n        correlations = new ArrayList<SCorrelationDefinition>(1);\n    }\n\n    public SMessageEventTriggerDefinitionImpl(final SCatchMessageEventTriggerDefinition trigger) {\n        messageName = trigger.getMessageName();\n        correlations = trigger.getCorrelations();\n    }\n\n    public SMessageEventTriggerDefinitionImpl(final MessageEventTriggerDefinition messageEventTrigger) {\n        messageName = messageEventTrigger.getMessageName();\n        correlations = new ArrayList<SCorrelationDefinition>(messageEventTrigger.getCorrelations().size());\n        for (final CorrelationDefinition correlation : messageEventTrigger.getCorrelations()) {\n            correlations\n                    .add(new SCorrelationDefinitionImpl(ServerModelConvertor.convertExpression(correlation.getKey()),\n                            ServerModelConvertor.convertExpression(correlation.getValue())));\n        }\n    }\n\n    @Override\n    public String getMessageName() {\n        return messageName;\n    }\n\n    @Override\n    public List<SCorrelationDefinition> getCorrelations() {\n        return Collections.unmodifiableList(correlations);\n    }\n\n    public void setMessageName(final String name) {\n        messageName = name;\n    }\n\n    public void addCorrelation(final SCorrelationDefinition correlation) {\n        correlations.add(correlation);\n    }\n\n    protected SDataDefinition buildSDataDefinition(final DataDefinition dataDefinition) {\n        if (isXMLDataDefinition(dataDefinition)) {\n            final XMLDataDefinition xmlDataDef = (XMLDataDefinition) dataDefinition;\n            final SXMLDataDefinitionBuilderFactory fact = BuilderFactory.get(SXMLDataDefinitionBuilderFactory.class);\n            final SXMLDataDefinitionBuilder builder = fact.createNewXMLData(messageName)\n                    .setElement(xmlDataDef.getElement())\n                    .setNamespace(xmlDataDef.getNamespace());\n            builder.setDefaultValue(ServerModelConvertor.convertExpression(dataDefinition.getDefaultValueExpression()));\n            builder.setDescription(dataDefinition.getDescription());\n            builder.setTransient(dataDefinition.isTransientData());\n            return builder.done();\n        }\n        final SDataDefinitionBuilderFactory fact = BuilderFactory.get(SDataDefinitionBuilderFactory.class);\n        SDataDefinitionBuilder builder = null;\n        if (isTextDataDefinition(dataDefinition)) {\n            final TextDataDefinition textDataDefinition = (TextDataDefinition) dataDefinition;\n            builder = fact.createNewTextData(dataDefinition.getName()).setAsLongText(textDataDefinition.isLongText());\n        } else {\n            builder = fact.createNewInstance(dataDefinition.getName(), dataDefinition.getClassName());\n        }\n        builder.setDefaultValue(ServerModelConvertor.convertExpression(dataDefinition.getDefaultValueExpression()));\n        builder.setDescription(dataDefinition.getDescription());\n        builder.setTransient(dataDefinition.isTransientData());\n        return builder.done();\n    }\n\n    private boolean isXMLDataDefinition(final DataDefinition dataDefinition) {\n        return dataDefinition instanceof XMLDataDefinition;\n    }\n\n    private boolean isTextDataDefinition(final DataDefinition dataDefinition) {\n        return dataDefinition instanceof TextDataDefinition;\n    }\n\n    @Override\n    public SEventTriggerType getEventTriggerType() {\n        return SEventTriggerType.MESSAGE;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/impl/SSignalEventTriggerDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.trigger.impl;\n\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerType;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SSignalEventTriggerDefinition;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SSignalEventTriggerDefinitionImpl extends SEventTriggerDefinitionImpl\n        implements SSignalEventTriggerDefinition {\n\n    private static final long serialVersionUID = 1762616027337330056L;\n\n    private String signalName;\n\n    public SSignalEventTriggerDefinitionImpl(final String signalName) {\n        super();\n        this.signalName = signalName;\n    }\n\n    @Override\n    public String getSignalName() {\n        return signalName;\n    }\n\n    public void setSignalName(final String name) {\n        signalName = name;\n    }\n\n    @Override\n    public SEventTriggerType getEventTriggerType() {\n        return SEventTriggerType.SIGNAL;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/impl/STerminateEventTriggerDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.trigger.impl;\n\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerType;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.STerminateEventTriggerDefinition;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class STerminateEventTriggerDefinitionImpl extends SEventTriggerDefinitionImpl\n        implements STerminateEventTriggerDefinition {\n\n    private static final long serialVersionUID = -8156042655414582732L;\n\n    @Override\n    public SEventTriggerType getEventTriggerType() {\n        return SEventTriggerType.TERMINATE;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/impl/SThrowErrorEventTriggerDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.trigger.impl;\n\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerType;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowErrorEventTriggerDefinition;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SThrowErrorEventTriggerDefinitionImpl extends SErrorEventTriggerDefinitionImpl\n        implements SThrowErrorEventTriggerDefinition {\n\n    private static final long serialVersionUID = -8087566164595708656L;\n\n    public SThrowErrorEventTriggerDefinitionImpl(final String errorCode) {\n        super(errorCode);\n    }\n\n    @Override\n    public SEventTriggerType getEventTriggerType() {\n        return SEventTriggerType.ERROR;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/impl/SThrowMessageEventTriggerDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.trigger.impl;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.data.DataDefinition;\nimport org.bonitasoft.engine.bpm.flownode.ThrowMessageEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.builder.ServerModelConvertor;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowMessageEventTriggerDefinition;\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Yanyan Liu\n */\npublic class SThrowMessageEventTriggerDefinitionImpl extends SMessageEventTriggerDefinitionImpl\n        implements SThrowMessageEventTriggerDefinition {\n\n    private static final long serialVersionUID = -513177194601607560L;\n\n    private SExpression targetProcess;\n\n    private SExpression targetFlowNode;\n\n    private final List<SDataDefinition> sDataDefinitions;\n\n    public SThrowMessageEventTriggerDefinitionImpl() {\n        sDataDefinitions = new ArrayList<SDataDefinition>();\n    }\n\n    public SThrowMessageEventTriggerDefinitionImpl(final ThrowMessageEventTriggerDefinition throwMessageEventTrigger) {\n        super(throwMessageEventTrigger);\n        final List<DataDefinition> dataDefinitions = throwMessageEventTrigger.getDataDefinitions();\n        sDataDefinitions = new ArrayList<SDataDefinition>(dataDefinitions.size());\n        for (final DataDefinition dataDefinition : dataDefinitions) {\n            sDataDefinitions.add(buildSDataDefinition(dataDefinition));\n        }\n        targetProcess = ServerModelConvertor.convertExpression(throwMessageEventTrigger.getTargetProcess());\n        targetFlowNode = ServerModelConvertor.convertExpression(throwMessageEventTrigger.getTargetFlowNode());\n    }\n\n    @Override\n    public SExpression getTargetProcess() {\n        return targetProcess;\n    }\n\n    @Override\n    public SExpression getTargetFlowNode() {\n        return targetFlowNode;\n    }\n\n    @Override\n    public List<SDataDefinition> getDataDefinitions() {\n        return Collections.unmodifiableList(sDataDefinitions);\n    }\n\n    public void setTargetProcess(final SExpression targetProcess) {\n        this.targetProcess = targetProcess;\n    }\n\n    public void setTargetFlowNode(final SExpression targetFlowNode) {\n        this.targetFlowNode = targetFlowNode;\n    }\n\n    public void addDataDefinition(final SDataDefinition datadefiniton) {\n        sDataDefinitions.add(datadefiniton);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/impl/SThrowSignalEventTriggerDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.trigger.impl;\n\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowSignalEventTriggerDefinition;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SThrowSignalEventTriggerDefinitionImpl extends SSignalEventTriggerDefinitionImpl\n        implements SThrowSignalEventTriggerDefinition {\n\n    private static final long serialVersionUID = -6873752170541970655L;\n\n    public SThrowSignalEventTriggerDefinitionImpl(final String name) {\n        super(name);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/impl/STimerEventTriggerDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.trigger.impl;\n\nimport org.bonitasoft.engine.bpm.flownode.TimerEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.builder.ServerModelConvertor;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerType;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.STimerEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.STimerType;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class STimerEventTriggerDefinitionImpl extends SEventTriggerDefinitionImpl\n        implements STimerEventTriggerDefinition {\n\n    private static final long serialVersionUID = 1240658984583267877L;\n\n    private final STimerType timerType;\n\n    private final SExpression timerExpression;\n\n    public STimerEventTriggerDefinitionImpl(final TimerEventTriggerDefinition timerTrigger) {\n        timerType = STimerType.valueOf(timerTrigger.getTimerType().name());\n        final Expression expression = timerTrigger.getTimerExpression();\n        timerExpression = ServerModelConvertor.convertExpression(expression);\n    }\n\n    public STimerEventTriggerDefinitionImpl(final STimerType timerType, final SExpression timerExpression) {\n        this.timerType = timerType;\n        this.timerExpression = timerExpression;\n    }\n\n    @Override\n    public STimerType getTimerType() {\n        return timerType;\n    }\n\n    @Override\n    public SExpression getTimerExpression() {\n        return timerExpression;\n    }\n\n    @Override\n    public SEventTriggerType getEventTriggerType() {\n        return SEventTriggerType.TIMER;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SActivityDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.businessdata.BusinessDataDefinition;\nimport org.bonitasoft.engine.bpm.data.DataDefinition;\nimport org.bonitasoft.engine.bpm.flownode.ActivityDefinition;\nimport org.bonitasoft.engine.bpm.flownode.BoundaryEventDefinition;\nimport org.bonitasoft.engine.bpm.flownode.LoopCharacteristics;\nimport org.bonitasoft.engine.bpm.flownode.MultiInstanceLoopCharacteristics;\nimport org.bonitasoft.engine.bpm.flownode.StandardLoopCharacteristics;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.core.process.definition.model.SActivityDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SBoundaryEventNotFoundException;\nimport org.bonitasoft.engine.core.process.definition.model.SBusinessDataDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SLoopCharacteristics;\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.builder.ServerModelConvertor;\nimport org.bonitasoft.engine.core.process.definition.model.event.SBoundaryEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.impl.SBoundaryEventDefinitionImpl;\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\nimport org.bonitasoft.engine.operation.Operation;\n\n/**\n * @author Matthieu Chaffotte\n * @author Frederic Bouquet\n * @author Celine Souchet\n */\npublic abstract class SActivityDefinitionImpl extends SFlowNodeDefinitionImpl implements SActivityDefinition {\n\n    private static final long serialVersionUID = 8767258220640127769L;\n\n    protected List<SDataDefinition> sDataDefinitions = new ArrayList<SDataDefinition>();\n\n    private final List<SBusinessDataDefinition> businessDataDefinitions = new ArrayList<SBusinessDataDefinition>();\n\n    protected List<SOperation> sOperations = new ArrayList<SOperation>();\n\n    protected SLoopCharacteristics loopCharacteristics;\n\n    private final List<SBoundaryEventDefinition> sBoundaryEventDefinitions = new ArrayList<SBoundaryEventDefinition>();\n\n    public SActivityDefinitionImpl(final long id, final String name) {\n        super(id, name);\n    }\n\n    public SActivityDefinitionImpl(final ActivityDefinition activityDefinition,\n            final Map<String, STransitionDefinition> transitionsMap) {\n        super(activityDefinition, transitionsMap);\n\n        final List<DataDefinition> dataDefinitions = activityDefinition.getDataDefinitions();\n        for (final DataDefinition dataDefinition : dataDefinitions) {\n            sDataDefinitions.add(ServerModelConvertor.convertDataDefinition(dataDefinition));\n        }\n        for (final BusinessDataDefinition businessDataDefinition : activityDefinition.getBusinessDataDefinitions()) {\n            businessDataDefinitions.add(ServerModelConvertor.convertBusinessDataDefinition(businessDataDefinition));\n        }\n        final List<Operation> operations = activityDefinition.getOperations();\n        for (final Operation operation : operations) {\n            sOperations.add(ServerModelConvertor.convertOperation(operation));\n        }\n        final LoopCharacteristics loop = activityDefinition.getLoopCharacteristics();\n        if (loop != null) {\n            if (loop instanceof StandardLoopCharacteristics) {\n                loopCharacteristics = new SStandardLoopCharacteristicsImpl((StandardLoopCharacteristics) loop);\n            } else {\n                loopCharacteristics = new SMultiInstanceLoopCharacteristicsImpl(\n                        (MultiInstanceLoopCharacteristics) loop);\n            }\n        }\n        addBoundaryEvents(activityDefinition, transitionsMap);\n    }\n\n    private void addBoundaryEvents(final ActivityDefinition activityDefinition,\n            final Map<String, STransitionDefinition> transitionsMap) {\n        final List<BoundaryEventDefinition> boundaryEventDefinitions = activityDefinition.getBoundaryEventDefinitions();\n        for (final BoundaryEventDefinition boundaryEventDefinition : boundaryEventDefinitions) {\n            addBoundaryEventDefinition(new SBoundaryEventDefinitionImpl(boundaryEventDefinition, transitionsMap));\n        }\n    }\n\n    @Override\n    public List<SOperation> getSOperations() {\n        return sOperations;\n    }\n\n    @Override\n    public List<SDataDefinition> getSDataDefinitions() {\n        return sDataDefinitions;\n    }\n\n    public void addSDataDefinition(final SDataDefinition sDataDefinition) {\n        sDataDefinitions.add(sDataDefinition);\n    }\n\n    @Override\n    public List<SBoundaryEventDefinition> getBoundaryEventDefinitions() {\n        return Collections.unmodifiableList(sBoundaryEventDefinitions);\n    }\n\n    @Override\n    public SBoundaryEventDefinition getBoundaryEventDefinition(final String name)\n            throws SBoundaryEventNotFoundException {\n        boolean found = false;\n        SBoundaryEventDefinition boundary = null;\n        final Iterator<SBoundaryEventDefinition> iterator = sBoundaryEventDefinitions.iterator();\n        while (iterator.hasNext() && !found) {\n            final SBoundaryEventDefinition currentBoundary = iterator.next();\n            if (currentBoundary.getName().equals(name)) {\n                boundary = currentBoundary;\n                found = true;\n            }\n        }\n        if (boundary == null) {\n            throw new SBoundaryEventNotFoundException(name, getName());\n        }\n        return boundary;\n    }\n\n    public void addBoundaryEventDefinition(final SBoundaryEventDefinition boundaryEventDefinition) {\n        sBoundaryEventDefinitions.add(boundaryEventDefinition);\n    }\n\n    @Override\n    public SLoopCharacteristics getLoopCharacteristics() {\n        return loopCharacteristics;\n    }\n\n    public void setLoopCharacteristics(final SLoopCharacteristics loopCharacteristics) {\n        this.loopCharacteristics = loopCharacteristics;\n    }\n\n    @Override\n    public List<SBusinessDataDefinition> getBusinessDataDefinitions() {\n        return businessDataDefinitions;\n    }\n\n    @Override\n    public SBusinessDataDefinition getBusinessDataDefinition(final String name) {\n        if (name == null) {\n            return null;\n        }\n        boolean found = false;\n        SBusinessDataDefinition businessData = null;\n        final Iterator<SBusinessDataDefinition> iterator = businessDataDefinitions.iterator();\n        while (iterator.hasNext() && !found) {\n            final SBusinessDataDefinition currentBusinessData = iterator.next();\n            if (currentBusinessData.getName().equals(name)) {\n                found = true;\n                businessData = currentBusinessData;\n            }\n        }\n        return businessData;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SActorDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport org.bonitasoft.engine.bpm.actor.ActorDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SActorDefinition;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SActorDefinitionImpl extends SNamedElementImpl implements SActorDefinition {\n\n    private static final long serialVersionUID = -3781827896225458787L;\n\n    private String description;\n\n    private boolean initiator;\n\n    public SActorDefinitionImpl(final ActorDefinition actor) {\n        super(actor.getName());\n        description = actor.getDescription();\n        initiator = actor.isInitiator();\n    }\n\n    @Override\n    public String getDescription() {\n        return description;\n    }\n\n    public void setDescription(final String description) {\n        this.description = description;\n    }\n\n    @Override\n    public boolean isInitiator() {\n        return initiator;\n    }\n\n    public void setInitiator(boolean initiator) {\n        this.initiator = initiator;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SAutomaticTaskDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.flownode.ActivityDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SAutomaticTaskDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class SAutomaticTaskDefinitionImpl extends SActivityDefinitionImpl implements SAutomaticTaskDefinition {\n\n    private static final long serialVersionUID = 96851790923787649L;\n\n    public SAutomaticTaskDefinitionImpl(final ActivityDefinition activityDefinition,\n            final Map<String, STransitionDefinition> transitionsMap) {\n        super(activityDefinition, transitionsMap);\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.AUTOMATIC_TASK;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SBaseElementImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport org.bonitasoft.engine.core.process.definition.model.SBaseElement;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SBaseElementImpl implements SBaseElement {\n\n    private static final long serialVersionUID = -2401748455848222695L;\n\n    private Long id;\n\n    protected enum EQUALS_STATE {\n        CONTINUE, RETURN_FALSE, RETURN_TRUE\n    }\n\n    @Override\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(final Long id) {\n        this.id = id;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + (id == null ? 0 : id.hashCode());\n        return result;\n    }\n\n    protected EQUALS_STATE checkNaiveEquality(final Object obj) {\n        if (this == obj) {\n            return EQUALS_STATE.RETURN_TRUE;\n        } else if (obj == null) {\n            return EQUALS_STATE.RETURN_FALSE;\n        } else if (getClass() != obj.getClass()) {\n            return EQUALS_STATE.RETURN_FALSE;\n        } else {\n            return EQUALS_STATE.CONTINUE;\n        }\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        switch (checkNaiveEquality(obj)) {\n            case RETURN_FALSE:\n                return false;\n            case RETURN_TRUE:\n                return true;\n            case CONTINUE:\n            default:\n                break;\n        }\n        final SBaseElementImpl other = (SBaseElementImpl) obj;\n        if (id == null) {\n            if (other.id != null) {\n                return false;\n            }\n        } else if (!id.equals(other.id)) {\n            return false;\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SBusinessDataDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport org.bonitasoft.engine.core.process.definition.model.SBusinessDataDefinition;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class SBusinessDataDefinitionImpl implements SBusinessDataDefinition {\n\n    private static final long serialVersionUID = 1L;\n\n    private String name;\n\n    private String description;\n\n    private String className;\n\n    private SExpression defaultValueExpression;\n\n    private boolean multiple;\n\n    public SBusinessDataDefinitionImpl() {\n        super();\n    }\n\n    @Override\n    public String getName() {\n        return name;\n    }\n\n    @Override\n    public String getDescription() {\n        return description;\n    }\n\n    @Override\n    public String getClassName() {\n        return className;\n    }\n\n    @Override\n    public SExpression getDefaultValueExpression() {\n        return defaultValueExpression;\n    }\n\n    public void setName(final String name) {\n        this.name = name;\n    }\n\n    public void setDescription(final String description) {\n        this.description = description;\n    }\n\n    public void setDefaultValueExpression(final SExpression defaultValueExpression) {\n        this.defaultValueExpression = defaultValueExpression;\n    }\n\n    public void setClassName(final String className) {\n        this.className = className;\n    }\n\n    @Override\n    public boolean isMultiple() {\n        return multiple;\n    }\n\n    public void setMultiple(final boolean multiple) {\n        this.multiple = multiple;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SCallActivityDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.flownode.CallActivityDefinition;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.core.process.definition.model.SCallActivityDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SCallableElementType;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.builder.ServerModelConvertor;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class SCallActivityDefinitionImpl extends SActivityDefinitionImpl implements SCallActivityDefinition {\n\n    private static final long serialVersionUID = -5347512435504138388L;\n\n    private SExpression callableElement;\n\n    private SExpression callableElementVersion;\n\n    private final List<SOperation> dataInputOperations;\n\n    private final Map<String, SExpression> contractInputs;\n\n    private final List<SOperation> dataOutputOperations;\n\n    private SCallableElementType callableElementType;\n\n    public SCallActivityDefinitionImpl(final long id, final String name) {\n        super(id, name);\n        dataInputOperations = new ArrayList<>(3);\n        contractInputs = new HashMap<>();\n        dataOutputOperations = new ArrayList<>(3);\n    }\n\n    public SCallActivityDefinitionImpl(final CallActivityDefinition activityDefinition,\n            final Map<String, STransitionDefinition> transitionsMap) {\n        super(activityDefinition, transitionsMap);\n        callableElement = ServerModelConvertor.convertExpression(activityDefinition.getCallableElement());\n        callableElementVersion = ServerModelConvertor.convertExpression(activityDefinition.getCallableElementVersion());\n        dataInputOperations = ServerModelConvertor.convertOperations(activityDefinition.getDataInputOperations());\n        contractInputs = ServerModelConvertor.convertContractInputs(activityDefinition.getProcessStartContractInputs());\n        dataOutputOperations = ServerModelConvertor.convertOperations(activityDefinition.getDataOutputOperations());\n        callableElementType = SCallableElementType.valueOf(activityDefinition.getCallableElementType().name());\n    }\n\n    @Override\n    public SExpression getCallableElement() {\n        return callableElement;\n    }\n\n    @Override\n    public SExpression getCallableElementVersion() {\n        return callableElementVersion;\n    }\n\n    @Override\n    public List<SOperation> getDataInputOperations() {\n        return Collections.unmodifiableList(dataInputOperations);\n    }\n\n    @Override\n    public List<SOperation> getDataOutputOperations() {\n        return Collections.unmodifiableList(dataOutputOperations);\n    }\n\n    @Override\n    public SCallableElementType getCallableElementType() {\n        return callableElementType;\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.CALL_ACTIVITY;\n    }\n\n    @Override\n    public Map<String, SExpression> getProcessStartContractInputs() {\n        return contractInputs;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = super.hashCode();\n        result = prime * result + (callableElement == null ? 0 : callableElement.hashCode());\n        result = prime * result + (callableElementType == null ? 0 : callableElementType.hashCode());\n        result = prime * result + (callableElementVersion == null ? 0 : callableElementVersion.hashCode());\n        result = prime * result + (dataInputOperations == null ? 0 : dataInputOperations.hashCode());\n        result = prime * result + (dataOutputOperations == null ? 0 : dataOutputOperations.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (!super.equals(obj)) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        final SCallActivityDefinitionImpl other = (SCallActivityDefinitionImpl) obj;\n        if (callableElement == null) {\n            if (other.callableElement != null) {\n                return false;\n            }\n        } else if (!callableElement.equals(other.callableElement)) {\n            return false;\n        }\n        if (callableElementType != other.callableElementType) {\n            return false;\n        }\n        if (callableElementVersion == null) {\n            if (other.callableElementVersion != null) {\n                return false;\n            }\n        } else if (!callableElementVersion.equals(other.callableElementVersion)) {\n            return false;\n        }\n        if (dataInputOperations == null) {\n            if (other.dataInputOperations != null) {\n                return false;\n            }\n        } else if (!dataInputOperations.equals(other.dataInputOperations)) {\n            return false;\n        }\n        if (dataOutputOperations == null) {\n            if (other.dataOutputOperations != null) {\n                return false;\n            }\n        } else if (!dataOutputOperations.equals(other.dataOutputOperations)) {\n            return false;\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SConnectorDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorDefinition;\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.bpm.connector.FailAction;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.core.process.definition.model.SConnectorDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.builder.ServerModelConvertor;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.operation.Operation;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SConnectorDefinitionImpl extends SNamedElementImpl implements SConnectorDefinition {\n\n    private static final long serialVersionUID = 7953224084604802080L;\n\n    private final ConnectorEvent activationEvent;\n\n    private final Map<String, SExpression> inputs;\n\n    private final List<SOperation> outputs;\n\n    private final String connectorId;\n\n    private final String version;\n\n    private FailAction failAction;\n\n    private String errorCode;\n\n    public SConnectorDefinitionImpl(final ConnectorDefinition connector) {\n        super(connector.getName());\n        activationEvent = connector.getActivationEvent();\n        connectorId = connector.getConnectorId();\n        version = connector.getVersion();\n        failAction = connector.getFailAction();\n        errorCode = connector.getErrorCode();\n        inputs = new HashMap<>(connector.getInputs().size());\n        for (final Entry<String, Expression> input : connector.getInputs().entrySet()) {\n            final Expression value = input.getValue();\n            if (value != null) {\n                final SExpression sExpression = ServerModelConvertor.convertExpression(value);\n                inputs.put(input.getKey(), sExpression);// creates SExpression\n            }\n        }\n        outputs = new ArrayList<>(connector.getOutputs().size());\n        for (final Operation operation : connector.getOutputs()) {\n            final SOperation sOperation = ServerModelConvertor.convertOperation(operation);\n            outputs.add(sOperation);\n        }\n        // setId(connector.getId()); TODO : Implement generation of id\n    }\n\n    public SConnectorDefinitionImpl(final String name, final String connectorId, final String version,\n            final ConnectorEvent activationEvent) {\n        super(name);\n        this.connectorId = connectorId;\n        this.version = version;\n        this.activationEvent = activationEvent;\n        inputs = new HashMap<>();\n        outputs = new ArrayList<>();\n    }\n\n    @Override\n    public String getConnectorId() {\n        return connectorId;\n    }\n\n    @Override\n    public String getVersion() {\n        return version;\n    }\n\n    @Override\n    public ConnectorEvent getActivationEvent() {\n        return activationEvent;\n    }\n\n    @Override\n    public Map<String, SExpression> getInputs() {\n        return inputs;\n    }\n\n    @Override\n    public List<SOperation> getOutputs() {\n        return outputs;\n    }\n\n    public void setErrorCode(final String errorCode) {\n        this.errorCode = errorCode;\n    }\n\n    @Override\n    public FailAction getFailAction() {\n        return failAction;\n    }\n\n    @Override\n    public String getErrorCode() {\n        return errorCode;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SConstraintDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.contract.ConstraintDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SConstraintDefinition;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SConstraintDefinitionImpl extends SNamedElementImpl implements SConstraintDefinition {\n\n    private static final long serialVersionUID = 1663246823634006952L;\n\n    private final String expression;\n\n    private final String explanation;\n\n    private final List<String> inputNames;\n\n    public SConstraintDefinitionImpl(final String name, final String expression, final String explanation) {\n        super(name);\n        this.explanation = explanation;\n        this.expression = expression;\n        inputNames = new ArrayList<>();\n    }\n\n    public SConstraintDefinitionImpl(final ConstraintDefinition rule) {\n        this(rule.getName(), rule.getExpression(), rule.getExplanation());\n        for (final String inputName : rule.getInputNames()) {\n            inputNames.add(inputName);\n        }\n    }\n\n    @Override\n    public String getExpression() {\n        return expression;\n    }\n\n    @Override\n    public String getExplanation() {\n        return explanation;\n    }\n\n    @Override\n    public List<String> getInputNames() {\n        return inputNames;\n    }\n\n    public void addInputName(final String inputName) {\n        inputNames.add(inputName);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SContextEntryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport java.util.Objects;\n\nimport org.bonitasoft.engine.core.process.definition.model.SContextEntry;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SContextEntryImpl implements SContextEntry {\n\n    private String key;\n    private SExpression expression;\n\n    public SContextEntryImpl(String key, SExpression expression) {\n        this.key = key;\n        this.expression = expression;\n    }\n\n    @Override\n    public String getKey() {\n        return key;\n    }\n\n    public void setKey(String key) {\n        this.key = key;\n    }\n\n    @Override\n    public SExpression getExpression() {\n        return expression;\n    }\n\n    public void setExpression(SExpression expression) {\n        this.expression = expression;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (o == null || getClass() != o.getClass())\n            return false;\n        SContextEntryImpl that = (SContextEntryImpl) o;\n        return Objects.equals(key, that.key) &&\n                Objects.equals(expression, that.expression);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(key, expression);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SContractDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\n\nimport org.bonitasoft.engine.bpm.contract.ConstraintDefinition;\nimport org.bonitasoft.engine.bpm.contract.ContractDefinition;\nimport org.bonitasoft.engine.bpm.contract.InputDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SConstraintDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SContractDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SInputDefinition;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SContractDefinitionImpl extends SBaseElementImpl implements SContractDefinition {\n\n    private static final long serialVersionUID = -5281686322739618159L;\n\n    private final List<SInputDefinition> inputDefinitions;\n\n    private final List<SConstraintDefinition> constraints;\n\n    public SContractDefinitionImpl() {\n        super();\n        inputDefinitions = new ArrayList<>();\n        constraints = new ArrayList<>();\n    }\n\n    public SContractDefinitionImpl(final ContractDefinition contract) {\n        this();\n        for (final InputDefinition input : contract.getInputs()) {\n            inputDefinitions.add(new SInputDefinitionImpl(input));\n        }\n        for (final ConstraintDefinition rule : contract.getConstraints()) {\n            constraints.add(new SConstraintDefinitionImpl(rule));\n        }\n    }\n\n    @Override\n    public List<SInputDefinition> getInputDefinitions() {\n        return inputDefinitions;\n    }\n\n    public void addInput(final SInputDefinition input) {\n        inputDefinitions.add(input);\n    }\n\n    @Override\n    public List<SConstraintDefinition> getConstraints() {\n        return constraints;\n    }\n\n    public void addConstraint(final SConstraintDefinition rule) {\n        constraints.add(rule);\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (o == null || getClass() != o.getClass())\n            return false;\n        if (!super.equals(o))\n            return false;\n        SContractDefinitionImpl that = (SContractDefinitionImpl) o;\n        return Objects.equals(inputDefinitions, that.inputDefinitions) &&\n                Objects.equals(constraints, that.constraints);\n    }\n\n    @Override\n    public String toString() {\n        return \"SContractDefinitionImpl{\" +\n                \"inputDefinitions=\" + inputDefinitions +\n                \", constraints=\" + constraints +\n                \"} \" + super.toString();\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(super.hashCode(), inputDefinitions, constraints);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SDocumentDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport java.util.Objects;\n\nimport org.bonitasoft.engine.bpm.document.DocumentDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SDocumentDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.builder.ServerModelConvertor;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SDocumentDefinitionImpl extends SNamedElementImpl implements SDocumentDefinition {\n\n    private static final long serialVersionUID = 6622182823980202155L;\n\n    private String url;\n    private String file;\n    private String mimeType;\n    private String description;\n    private String fileName;\n    private SExpression initialValue;\n\n    /**\n     * @param name\n     */\n    public SDocumentDefinitionImpl(final String name) {\n        super(name);\n    }\n\n    /**\n     * @param documentDefinition\n     */\n    public SDocumentDefinitionImpl(final DocumentDefinition documentDefinition) {\n        super(documentDefinition.getName());\n        url = documentDefinition.getUrl();\n        file = documentDefinition.getFile();\n        description = documentDefinition.getDescription();\n        mimeType = documentDefinition.getContentMimeType();\n        fileName = documentDefinition.getFileName();\n        initialValue = ServerModelConvertor.convertExpression(documentDefinition.getInitialValue());\n    }\n\n    @Override\n    public String getUrl() {\n        return url;\n    }\n\n    @Override\n    public String getFile() {\n        return file;\n    }\n\n    @Override\n    public String getMimeType() {\n        return mimeType;\n    }\n\n    @Override\n    public String getDescription() {\n        return description;\n    }\n\n    public void setMimeType(final String mimeType) {\n        this.mimeType = mimeType;\n    }\n\n    public void setUrl(final String url) {\n        this.url = url;\n    }\n\n    public void setFile(final String file) {\n        this.file = file;\n    }\n\n    public void setDescription(final String description) {\n        this.description = description;\n    }\n\n    @Override\n    public String getFileName() {\n        return fileName;\n    }\n\n    public void setFileName(final String fileName) {\n        this.fileName = fileName;\n    }\n\n    @Override\n    public SExpression getInitialValue() {\n        return initialValue;\n    }\n\n    public void setInitialValue(SExpression initialValue) {\n        this.initialValue = initialValue;\n    }\n\n    @Override\n    public String toString() {\n        return \"SDocumentDefinitionImpl{\" +\n                \"url='\" + url + '\\'' +\n                \", file='\" + file + '\\'' +\n                \", mimeType='\" + mimeType + '\\'' +\n                \", description='\" + description + '\\'' +\n                \", fileName='\" + fileName + '\\'' +\n                \", initialValue=\" + initialValue +\n                \"} \" + super.toString();\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (o == null || getClass() != o.getClass())\n            return false;\n        if (!super.equals(o))\n            return false;\n        SDocumentDefinitionImpl that = (SDocumentDefinitionImpl) o;\n        return Objects.equals(url, that.url) &&\n                Objects.equals(file, that.file) &&\n                Objects.equals(mimeType, that.mimeType) &&\n                Objects.equals(description, that.description) &&\n                Objects.equals(fileName, that.fileName) &&\n                Objects.equals(initialValue, that.initialValue);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(super.hashCode(), url, file, mimeType, description, fileName, initialValue);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SDocumentListDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport org.bonitasoft.engine.bpm.document.DocumentListDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SDocumentListDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.builder.ServerModelConvertor;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SDocumentListDefinitionImpl extends SNamedElementImpl implements SDocumentListDefinition {\n\n    private static final long serialVersionUID = 1L;\n\n    private String description;\n\n    private SExpression expression;\n\n    /**\n     * @param name\n     *        the name of the list\n     */\n    public SDocumentListDefinitionImpl(final String name) {\n        super(name);\n    }\n\n    public SDocumentListDefinitionImpl(DocumentListDefinition documentListDefinition) {\n        super(documentListDefinition.getName());\n        description = documentListDefinition.getDescription();\n        expression = ServerModelConvertor.convertExpression(documentListDefinition.getExpression());\n    }\n\n    public String getDescription() {\n        return description;\n    }\n\n    public void setDescription(String description) {\n        this.description = description;\n    }\n\n    public SExpression getExpression() {\n        return expression;\n    }\n\n    public void setExpression(SExpression expression) {\n        this.expression = expression;\n    }\n\n    @Override\n    public String toString() {\n        return \"SDocumentDefinitionImpl{\" +\n                \"description='\" + description + '\\'' +\n                \", expression=\" + expression +\n                \"} \" + super.toString();\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (o == null || getClass() != o.getClass())\n            return false;\n        if (!super.equals(o))\n            return false;\n\n        SDocumentListDefinitionImpl that = (SDocumentListDefinitionImpl) o;\n\n        if (description != null ? !description.equals(that.description) : that.description != null)\n            return false;\n        if (expression != null ? !expression.equals(that.expression) : that.expression != null)\n            return false;\n\n        return true;\n    }\n\n    @Override\n    public int hashCode() {\n        int result = super.hashCode();\n        result = 31 * result + (description != null ? description.hashCode() : 0);\n        result = 31 * result + (expression != null ? expression.hashCode() : 0);\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SFlowElementContainerDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.bpm.businessdata.BusinessDataDefinition;\nimport org.bonitasoft.engine.bpm.connector.ConnectorDefinition;\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.bpm.data.DataDefinition;\nimport org.bonitasoft.engine.bpm.document.DocumentDefinition;\nimport org.bonitasoft.engine.bpm.document.DocumentListDefinition;\nimport org.bonitasoft.engine.bpm.flownode.ActivityDefinition;\nimport org.bonitasoft.engine.bpm.flownode.CallActivityDefinition;\nimport org.bonitasoft.engine.bpm.flownode.EndEventDefinition;\nimport org.bonitasoft.engine.bpm.flownode.FlowElementContainerDefinition;\nimport org.bonitasoft.engine.bpm.flownode.GatewayDefinition;\nimport org.bonitasoft.engine.bpm.flownode.IntermediateCatchEventDefinition;\nimport org.bonitasoft.engine.bpm.flownode.IntermediateThrowEventDefinition;\nimport org.bonitasoft.engine.bpm.flownode.StartEventDefinition;\nimport org.bonitasoft.engine.bpm.flownode.TransitionDefinition;\nimport org.bonitasoft.engine.bpm.flownode.UserTaskDefinition;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.AutomaticTaskDefinitionImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.HumanTaskDefinitionImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.ManualTaskDefinitionImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.ReceiveTaskDefinitionImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.SendTaskDefinitionImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.UserTaskDefinitionImpl;\nimport org.bonitasoft.engine.bpm.process.SubProcessDefinition;\nimport org.bonitasoft.engine.bpm.userfilter.UserFilterDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SActivityDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SBusinessDataDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SConnectorDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SDocumentDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SDocumentListDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.definition.model.SGatewayDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SGatewayType;\nimport org.bonitasoft.engine.core.process.definition.model.SSubProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.builder.ServerModelConvertor;\nimport org.bonitasoft.engine.core.process.definition.model.event.SBoundaryEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SEndEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SIntermediateCatchEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SIntermediateThrowEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SStartEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.impl.SEndEventDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.event.impl.SIntermediateCatchEventDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.event.impl.SIntermediateThrowEventDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.event.impl.SStartEventDefinitionImpl;\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class SFlowElementContainerDefinitionImpl extends SBaseElementImpl implements SFlowElementContainerDefinition {\n\n    private static final long serialVersionUID = -40122166157566478L;\n\n    private final Map<ConnectorEvent, List<SConnectorDefinition>> connectorsMap;\n\n    private final Map<String, SFlowNodeDefinition> allElementsMapString;\n\n    private final Map<String, SGatewayDefinition> gatewaysMap;\n\n    private final Map<Long, SFlowNodeDefinition> allElementsMap;\n\n    private final Map<String, SConnectorDefinition> allConnectorsMap;\n\n    private final Map<String, STransitionDefinition> transitionsMap;\n\n    private final Set<SActivityDefinition> activities;\n\n    private final Set<SSubProcessDefinition> subProcessDefinitions;\n\n    private final Set<STransitionDefinition> transitions;\n\n    private final Set<SGatewayDefinition> gateways;\n\n    private final Set<SFlowNodeDefinition> allElements;\n\n    private final List<SConnectorDefinition> connectors;\n\n    private final List<SStartEventDefinition> sStartEvents;\n\n    private final List<SIntermediateCatchEventDefinition> sIntermediateCatchEvents;\n\n    private final List<SBoundaryEventDefinition> sBoundaryEvents;\n\n    private final List<SEndEventDefinition> sEndEvents;\n\n    private final List<SIntermediateThrowEventDefinition> sIntermediateThrowEvents;\n\n    private final List<SDataDefinition> sDataDefinitions;\n\n    private final List<SBusinessDataDefinition> sBusinessDataDefinitions;\n\n    private final List<SDocumentDefinition> sDocumentDefinitions;\n\n    private final List<SDocumentListDefinition> sDocumentListDefinitions;\n\n    private boolean containsInclusiveGateway = false;\n\n    public SFlowElementContainerDefinitionImpl() {\n        activities = new HashSet<>();\n        subProcessDefinitions = new HashSet<>(0);\n        transitions = new HashSet<>();\n        transitionsMap = new HashMap<>();\n        gateways = new HashSet<>();\n        gatewaysMap = new HashMap<>();\n        allElements = new HashSet<>();\n        allElementsMap = new HashMap<>();\n        allElementsMapString = new HashMap<>();\n        connectors = new ArrayList<>();\n        connectorsMap = new HashMap<>(2);\n        connectorsMap.put(ConnectorEvent.ON_ENTER, new ArrayList<SConnectorDefinition>());\n        connectorsMap.put(ConnectorEvent.ON_FINISH, new ArrayList<SConnectorDefinition>());\n        allConnectorsMap = new HashMap<>(2);\n        sStartEvents = new ArrayList<>();\n        sIntermediateCatchEvents = new ArrayList<>();\n        sIntermediateThrowEvents = new ArrayList<>();\n        sEndEvents = new ArrayList<>();\n        sDataDefinitions = new ArrayList<>();\n        sBusinessDataDefinitions = new ArrayList<>();\n        sDocumentDefinitions = new ArrayList<>();\n        sDocumentListDefinitions = new ArrayList<>();\n        sBoundaryEvents = new ArrayList<>();\n    }\n\n    public SFlowElementContainerDefinitionImpl(final FlowElementContainerDefinition container) {\n        transitions = new HashSet<>();\n        transitionsMap = new HashMap<>();\n        for (final TransitionDefinition transition : container.getTransitions()) {\n            final STransitionDefinitionImpl sTransitionDefinitionImpl = new STransitionDefinitionImpl(transition);\n            addTransition(sTransitionDefinitionImpl);\n        }\n\n        allElements = new HashSet<>();\n        allElementsMap = new HashMap<>();\n        allElementsMapString = new HashMap<>();\n        final List<ActivityDefinition> activities2 = container.getActivities();\n        sBoundaryEvents = new ArrayList<>();\n        activities = new HashSet<>(activities2.size());\n        subProcessDefinitions = new HashSet<>(0);\n        initializeActivities(activities2);\n\n        final List<GatewayDefinition> gateways2 = container.getGatewaysList();\n        gateways = new HashSet<>(gateways2.size());\n        gatewaysMap = new HashMap<>(gateways2.size());\n        final Iterator<GatewayDefinition> iterator1 = gateways2.iterator();\n        while (iterator1.hasNext()) {\n            final GatewayDefinition gatewayDefinition = iterator1.next();\n            final SGatewayDefinitionImpl gateway;\n            gateway = new SGatewayDefinitionImpl(gatewayDefinition, transitionsMap);\n            addGateway(gateway);\n        }\n\n        final List<ConnectorDefinition> connectors2 = container.getConnectors();\n        final List<SConnectorDefinition> mConnectors = new ArrayList<>(connectors2.size());\n        connectorsMap = new HashMap<>(2);\n        connectorsMap.put(ConnectorEvent.ON_ENTER, new ArrayList<SConnectorDefinition>());\n        connectorsMap.put(ConnectorEvent.ON_FINISH, new ArrayList<SConnectorDefinition>());\n        allConnectorsMap = new HashMap<>(2);\n        for (final ConnectorDefinition connector : connectors2) {\n            final SConnectorDefinitionImpl e = new SConnectorDefinitionImpl(connector);\n            mConnectors.add(e);\n            connectorsMap.get(e.getActivationEvent()).add(e);\n            allConnectorsMap.put(e.getName(), e);\n        }\n        connectors = Collections.unmodifiableList(mConnectors);\n\n        sStartEvents = initializeStartEvents(container.getStartEvents(), transitionsMap);\n        sIntermediateCatchEvents = initializeIntermediateCatchEvents(container.getIntermediateCatchEvents(),\n                transitionsMap);\n        sIntermediateThrowEvents = initializeIntermediateThrowEvents(container.getIntermediateThrowEvents(),\n                transitionsMap);\n        sEndEvents = initializeEndEvents(container.getEndEvents(), transitionsMap);\n\n        final List<BusinessDataDefinition> businessDataDefinitions = container.getBusinessDataDefinitions();\n        final List<SBusinessDataDefinition> mBusinessDataDefinitions = new ArrayList<>(businessDataDefinitions.size());\n        for (final BusinessDataDefinition businessDataDefinition : businessDataDefinitions) {\n            mBusinessDataDefinitions.add(ServerModelConvertor.convertBusinessDataDefinition(businessDataDefinition));\n        }\n        sBusinessDataDefinitions = Collections.unmodifiableList(mBusinessDataDefinitions);\n\n        final List<DataDefinition> processDataDefinitions = container.getDataDefinitions();\n        final List<SDataDefinition> mDataDefinitions = new ArrayList<>(processDataDefinitions.size());\n        for (final DataDefinition dataDefinition : processDataDefinitions) {\n            mDataDefinitions.add(ServerModelConvertor.convertDataDefinition(dataDefinition));\n        }\n        sDataDefinitions = Collections.unmodifiableList(mDataDefinitions);\n        final List<DocumentDefinition> documentDefinitions2 = container.getDocumentDefinitions();\n        final List<SDocumentDefinition> mDocumentDefinitions = new ArrayList<>(documentDefinitions2.size());\n        for (final DocumentDefinition documentDefinition : documentDefinitions2) {\n            mDocumentDefinitions.add(new SDocumentDefinitionImpl(documentDefinition));\n        }\n        sDocumentDefinitions = Collections.unmodifiableList(mDocumentDefinitions);\n        final List<DocumentListDefinition> documentListDefinitions2 = container.getDocumentListDefinitions();\n        final ArrayList<SDocumentListDefinition> mDocumentListDefinitions = new ArrayList<>(\n                documentListDefinitions2.size());\n        for (final DocumentListDefinition documentListDefinition : documentListDefinitions2) {\n            mDocumentListDefinitions.add(new SDocumentListDefinitionImpl(documentListDefinition));\n        }\n        sDocumentListDefinitions = Collections.unmodifiableList(mDocumentListDefinitions);\n\n    }\n\n    private void initializeActivities(final List<ActivityDefinition> activities2) {\n        final Iterator<ActivityDefinition> iterator = activities2.iterator();\n        while (iterator.hasNext()) {\n            final ActivityDefinition activityDefinition = iterator.next();\n            final SActivityDefinitionImpl activity;\n            if (activityDefinition instanceof AutomaticTaskDefinitionImpl) {\n                activity = new SAutomaticTaskDefinitionImpl(activityDefinition, transitionsMap);\n            } else if (activityDefinition instanceof HumanTaskDefinitionImpl humanTaskDefinitionImpl) {\n                if (activityDefinition instanceof UserTaskDefinitionImpl) {\n                    activity = new SUserTaskDefinitionImpl((UserTaskDefinition) activityDefinition, transitionsMap);\n                } else {\n                    activity = new SManualTaskDefinitionImpl((ManualTaskDefinitionImpl) activityDefinition,\n                            transitionsMap);\n                }\n                final UserFilterDefinition userFilter = humanTaskDefinitionImpl.getUserFilter();\n                final SHumanTaskDefinitionImpl sHumanTaskDefinitionImpl = (SHumanTaskDefinitionImpl) activity;\n                if (userFilter != null) {\n                    sHumanTaskDefinitionImpl.setUserFilter(new SUserFilterDefinitionImpl(userFilter));\n                }\n                sHumanTaskDefinitionImpl.setPriority(humanTaskDefinitionImpl.getPriority());\n                sHumanTaskDefinitionImpl.setExpectedDuration(\n                        ServerModelConvertor.convertExpression(humanTaskDefinitionImpl.getExpectedDuration()));\n            } else if (activityDefinition instanceof ReceiveTaskDefinitionImpl) {\n                activity = new SReceiveTaskDefinitionImpl((ReceiveTaskDefinitionImpl) activityDefinition,\n                        transitionsMap);\n            } else if (activityDefinition instanceof SendTaskDefinitionImpl) {\n                activity = new SSendTaskDefinitionImpl((SendTaskDefinitionImpl) activityDefinition, transitionsMap);\n            } else if (activityDefinition instanceof CallActivityDefinition) {\n                activity = new SCallActivityDefinitionImpl((CallActivityDefinition) activityDefinition, transitionsMap);\n            } else if (activityDefinition instanceof SubProcessDefinition) {\n                activity = new SSubProcessDefinitionImpl((SubProcessDefinition) activityDefinition);\n            } else {\n                throw new BonitaRuntimeException(\n                        \"Can't find the client type for \" + activityDefinition.getClass().getName());\n            }\n            addActivity(activity);\n        }\n    }\n\n    private List<SEndEventDefinition> initializeEndEvents(final List<EndEventDefinition> endEvents,\n            final Map<String, STransitionDefinition> transitionsMap) {\n        final List<SEndEventDefinition> sEndEvents = new ArrayList<>(endEvents.size());\n        for (final EndEventDefinition endEventDefinition : endEvents) {\n            final SEndEventDefinitionImpl sEndEvent = new SEndEventDefinitionImpl(endEventDefinition, transitionsMap);\n            sEndEvents.add(sEndEvent);\n            addFlowNode(sEndEvent);\n        }\n        return sEndEvents;\n    }\n\n    private List<SStartEventDefinition> initializeStartEvents(final List<StartEventDefinition> startEvents,\n            final Map<String, STransitionDefinition> transitionsMap) {\n        final List<SStartEventDefinition> sStartEvents = new ArrayList<>(startEvents.size());\n        for (final StartEventDefinition startEventDefinition : startEvents) {\n            final SStartEventDefinitionImpl sStartEventDefinitionImpl = new SStartEventDefinitionImpl(\n                    startEventDefinition, transitionsMap);\n            sStartEvents.add(sStartEventDefinitionImpl);\n            addFlowNode(sStartEventDefinitionImpl);\n        }\n        return sStartEvents;\n    }\n\n    private List<SIntermediateCatchEventDefinition> initializeIntermediateCatchEvents(\n            final List<IntermediateCatchEventDefinition> intermediateCatchEvents,\n            final Map<String, STransitionDefinition> transitionsMap) {\n        final List<SIntermediateCatchEventDefinition> sIntermediateCatchEvents = new ArrayList<>(\n                intermediateCatchEvents.size());\n        for (final IntermediateCatchEventDefinition intermediateCatchEventDefinition : intermediateCatchEvents) {\n            final SIntermediateCatchEventDefinitionImpl sIntermediateCatchEvent = new SIntermediateCatchEventDefinitionImpl(\n                    intermediateCatchEventDefinition,\n                    transitionsMap);\n            sIntermediateCatchEvents.add(sIntermediateCatchEvent);\n            addFlowNode(sIntermediateCatchEvent);\n        }\n        return sIntermediateCatchEvents;\n    }\n\n    private List<SIntermediateThrowEventDefinition> initializeIntermediateThrowEvents(\n            final List<IntermediateThrowEventDefinition> intermediateThrowEvents,\n            final Map<String, STransitionDefinition> transitionsMap) {\n        final List<SIntermediateThrowEventDefinition> sIntermediateThrowEvents = new ArrayList<>(\n                intermediateThrowEvents.size());\n        for (final IntermediateThrowEventDefinition intermediateThrowEventDefinition : intermediateThrowEvents) {\n            final SIntermediateThrowEventDefinitionImpl sIntermediateThrowEvent = new SIntermediateThrowEventDefinitionImpl(\n                    intermediateThrowEventDefinition,\n                    transitionsMap);\n            sIntermediateThrowEvents.add(sIntermediateThrowEvent);\n            addFlowNode(sIntermediateThrowEvent);\n        }\n        return sIntermediateThrowEvents;\n    }\n\n    public void addTransition(final STransitionDefinition transition) {\n        transitions.add(transition);\n        transitionsMap.put(transition.getId().toString(), transition);\n    }\n\n    public void addActivity(final SActivityDefinition activity) {\n        sBoundaryEvents.addAll(activity.getBoundaryEventDefinitions());\n        for (final SBoundaryEventDefinition boundary : activity.getBoundaryEventDefinitions()) {\n            sBoundaryEvents.add(boundary);\n            allElements.add(boundary);\n            allElementsMap.put(boundary.getId(), boundary);\n        }\n        activities.add(activity);\n        addFlowNode(activity);\n        if (activity instanceof SSubProcessDefinition) {\n            addSubProcess((SSubProcessDefinition) activity);\n        }\n    }\n\n    public void addEvent(SEventDefinition eventDefinition) {\n        addFlowNode(eventDefinition);\n    }\n\n    private void addFlowNode(SFlowNodeDefinition flowNodeDefinition) {\n        allElements.add(flowNodeDefinition);\n        allElementsMap.put(flowNodeDefinition.getId(), flowNodeDefinition);\n        allElementsMapString.put(flowNodeDefinition.getName(), flowNodeDefinition);\n    }\n\n    public void addSubProcess(final SSubProcessDefinition sSubProcessDefinition) {\n        subProcessDefinitions.add(sSubProcessDefinition);\n    }\n\n    public void addGateway(final SGatewayDefinition gateway) {\n        gateways.add(gateway);\n        if (gateway.getGatewayType() == SGatewayType.INCLUSIVE) {\n            containsInclusiveGateway = true;\n        }\n        gatewaysMap.put(gateway.getName(), gateway);\n        addFlowNode(gateway);\n    }\n\n    public void addBusinessDataDefinition(final SBusinessDataDefinition businessDataDefinition) {\n        sBusinessDataDefinitions.add(businessDataDefinition);\n    }\n\n    @Override\n    public Set<SGatewayDefinition> getGateways() {\n        return gateways;\n    }\n\n    @Override\n    public SGatewayDefinition getGateway(final String name) {\n        SGatewayDefinition sGatewayDefinition = gatewaysMap.get(name);\n        if (sGatewayDefinition == null) {\n            sGatewayDefinition = getGatewayFromSubProcesses(name);\n        }\n        return sGatewayDefinition;\n    }\n\n    private SGatewayDefinition getGatewayFromSubProcesses(final String name) {\n        boolean found = false;\n        SGatewayDefinition gateway = null;\n        final Iterator<SActivityDefinition> iterator = activities.iterator();\n        while (iterator.hasNext() && !found) {\n            final SActivityDefinition activityDefinition = iterator.next();\n            if (SFlowNodeType.SUB_PROCESS.equals(activityDefinition.getType())) {\n                gateway = ((SSubProcessDefinition) activityDefinition).getSubProcessContainer().getGateway(name);\n                if (gateway != null) {\n                    found = true;\n                }\n            }\n        }\n        return gateway;\n    }\n\n    @Override\n    public Set<SFlowNodeDefinition> getFlowNodes() {\n        return allElements;\n    }\n\n    @Override\n    public SFlowNodeDefinition getFlowNode(final long id) {\n        SFlowNodeDefinition flowNodeDefinition = allElementsMap.get(id);\n        if (flowNodeDefinition == null) {\n            flowNodeDefinition = getFlowNodeFromSubProcesses(id);\n        }\n        return flowNodeDefinition;\n    }\n\n    @Override\n    public SFlowNodeDefinition getFlowNode(final String targetFlowNode) {\n        SFlowNodeDefinition flowNodeDefinition = allElementsMapString.get(targetFlowNode);\n        if (flowNodeDefinition == null) {\n            flowNodeDefinition = getFlowNodeFromSubProcesses(targetFlowNode);\n        }\n        return flowNodeDefinition;\n    }\n\n    private SFlowNodeDefinition getFlowNodeFromSubProcesses(final long id) {\n        boolean found = false;\n        SFlowNodeDefinition flowNode = null;\n        final Iterator<SActivityDefinition> iterator = activities.iterator();\n        while (iterator.hasNext() && !found) {\n            final SActivityDefinition activityDefinition = iterator.next();\n            if (SFlowNodeType.SUB_PROCESS.equals(activityDefinition.getType())) {\n                flowNode = ((SSubProcessDefinition) activityDefinition).getSubProcessContainer().getFlowNode(id);\n                if (flowNode != null) {\n                    found = true;\n                }\n            }\n        }\n        return flowNode;\n    }\n\n    private SFlowNodeDefinition getFlowNodeFromSubProcesses(final String targetFlowNode) {\n        boolean found = false;\n        SFlowNodeDefinition flowNode = null;\n        final Iterator<SActivityDefinition> iterator = activities.iterator();\n        while (iterator.hasNext() && !found) {\n            final SActivityDefinition activityDefinition = iterator.next();\n            if (SFlowNodeType.SUB_PROCESS.equals(activityDefinition.getType())) {\n                flowNode = ((SSubProcessDefinition) activityDefinition).getSubProcessContainer()\n                        .getFlowNode(targetFlowNode);\n                if (flowNode != null) {\n                    found = true;\n                }\n            }\n        }\n        return flowNode;\n    }\n\n    @Override\n    public STransitionDefinition getTransition(final String transitionId) {\n        STransitionDefinition transitionDefinition = transitionsMap.get(transitionId);\n        if (transitionDefinition == null) {\n            transitionDefinition = getTransitionFromSubProcesses(transitionId);\n        }\n        return transitionDefinition;\n    }\n\n    private STransitionDefinition getTransitionFromSubProcesses(final String transitionId) {\n        boolean found = false;\n        STransitionDefinition transition = null;\n        final Iterator<SActivityDefinition> iterator = activities.iterator();\n        while (iterator.hasNext() && !found) {\n            final SActivityDefinition activityDefinition = iterator.next();\n            if (SFlowNodeType.SUB_PROCESS.equals(activityDefinition.getType())) {\n                transition = ((SSubProcessDefinition) activityDefinition).getSubProcessContainer()\n                        .getTransition(transitionId);\n                if (transition != null) {\n                    found = true;\n                }\n            }\n        }\n        return transition;\n    }\n\n    @Override\n    public List<SConnectorDefinition> getConnectors() {\n        return connectors;\n    }\n\n    @Override\n    // public SConnectorDefinition getConnectorDefinition(final long id) {// FIXME: Uncomment when generate id\n    public SConnectorDefinition getConnectorDefinition(final String name) {\n        return allConnectorsMap.get(name);\n    }\n\n    @Override\n    public List<SConnectorDefinition> getConnectors(final ConnectorEvent connectorEvent) {\n        return connectorsMap.get(connectorEvent);\n    }\n\n    @Override\n    public List<SStartEventDefinition> getStartEvents() {\n        return sStartEvents;\n    }\n\n    @Override\n    public List<SIntermediateCatchEventDefinition> getIntermediateCatchEvents() {\n        return sIntermediateCatchEvents;\n    }\n\n    @Override\n    public List<SEndEventDefinition> getEndEvents() {\n        return sEndEvents;\n    }\n\n    @Override\n    public List<SBusinessDataDefinition> getBusinessDataDefinitions() {\n        return sBusinessDataDefinitions;\n    }\n\n    @Override\n    public SBusinessDataDefinition getBusinessDataDefinition(final String name) {\n        if (name == null) {\n            return null;\n        }\n        boolean found = false;\n        SBusinessDataDefinition businessData = null;\n        final Iterator<SBusinessDataDefinition> iterator = sBusinessDataDefinitions.iterator();\n        while (iterator.hasNext() && !found) {\n            final SBusinessDataDefinition currentBusinessData = iterator.next();\n            if (currentBusinessData.getName().equals(name)) {\n                found = true;\n                businessData = currentBusinessData;\n            }\n        }\n        return businessData;\n    }\n\n    @Override\n    public List<SDataDefinition> getDataDefinitions() {\n        return sDataDefinitions;\n    }\n\n    @Override\n    public List<SIntermediateThrowEventDefinition> getIntermdiateThrowEvents() {\n        return Collections.unmodifiableList(sIntermediateThrowEvents);\n    }\n\n    @Override\n    public List<SDocumentDefinition> getDocumentDefinitions() {\n        return sDocumentDefinitions;\n    }\n\n    @Override\n    public Set<SActivityDefinition> getActivities() {\n        return activities;\n    }\n\n    @Override\n    public Set<SSubProcessDefinition> getSubProcessDefinitions() {\n        return subProcessDefinitions;\n    }\n\n    @Override\n    public Set<STransitionDefinition> getTransitions() {\n        return transitions;\n    }\n\n    @Override\n    public List<SBoundaryEventDefinition> getBoundaryEvents() {\n        return Collections.unmodifiableList(sBoundaryEvents);\n    }\n\n    @Override\n    public SBoundaryEventDefinition getBoundaryEvent(final String name) {\n        boolean found = false;\n        SBoundaryEventDefinition boundaryEvent = null;\n        final Iterator<SBoundaryEventDefinition> iterator = sBoundaryEvents.iterator();\n        while (iterator.hasNext() && !found) {\n            final SBoundaryEventDefinition currentBoundaryEvent = iterator.next();\n            if (currentBoundaryEvent.getName().equals(name)) {\n                found = true;\n                boundaryEvent = currentBoundaryEvent;\n            }\n        }\n        return boundaryEvent;\n    }\n\n    @Override\n    public boolean containsInclusiveGateway() {\n        return containsInclusiveGateway;\n    }\n\n    /**\n     * @return the document list definitions\n     * @since 6.4.0\n     */\n    @Override\n    public List<SDocumentListDefinition> getDocumentListDefinitions() {\n        return sDocumentListDefinitions;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (o == null || getClass() != o.getClass())\n            return false;\n        if (!super.equals(o))\n            return false;\n        SFlowElementContainerDefinitionImpl that = (SFlowElementContainerDefinitionImpl) o;\n        return containsInclusiveGateway == that.containsInclusiveGateway &&\n                Objects.equals(connectorsMap, that.connectorsMap) &&\n                Objects.equals(allElementsMapString, that.allElementsMapString) &&\n                Objects.equals(gatewaysMap, that.gatewaysMap) &&\n                Objects.equals(allElementsMap, that.allElementsMap) &&\n                Objects.equals(allConnectorsMap, that.allConnectorsMap) &&\n                Objects.equals(transitionsMap, that.transitionsMap) &&\n                Objects.equals(activities, that.activities) &&\n                Objects.equals(subProcessDefinitions, that.subProcessDefinitions) &&\n                Objects.equals(transitions, that.transitions) &&\n                Objects.equals(gateways, that.gateways) &&\n                Objects.equals(allElements, that.allElements) &&\n                Objects.equals(connectors, that.connectors) &&\n                Objects.equals(sStartEvents, that.sStartEvents) &&\n                Objects.equals(sIntermediateCatchEvents, that.sIntermediateCatchEvents) &&\n                Objects.equals(sBoundaryEvents, that.sBoundaryEvents) &&\n                Objects.equals(sEndEvents, that.sEndEvents) &&\n                Objects.equals(sIntermediateThrowEvents, that.sIntermediateThrowEvents) &&\n                Objects.equals(sDataDefinitions, that.sDataDefinitions) &&\n                Objects.equals(sBusinessDataDefinitions, that.sBusinessDataDefinitions) &&\n                Objects.equals(sDocumentDefinitions, that.sDocumentDefinitions) &&\n                Objects.equals(sDocumentListDefinitions, that.sDocumentListDefinitions);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(super.hashCode(), connectorsMap, allElementsMapString, gatewaysMap, allElementsMap,\n                allConnectorsMap, transitionsMap, activities, subProcessDefinitions, transitions, gateways, allElements,\n                connectors, sStartEvents, sIntermediateCatchEvents, sBoundaryEvents, sEndEvents,\n                sIntermediateThrowEvents, sDataDefinitions, sBusinessDataDefinitions, sDocumentDefinitions,\n                sDocumentListDefinitions, containsInclusiveGateway);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SFlowNodeDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorDefinition;\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeDefinition;\nimport org.bonitasoft.engine.bpm.flownode.TransitionDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SConnectorDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.builder.ServerModelConvertor;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Feng Hui\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic abstract class SFlowNodeDefinitionImpl extends SNamedElementImpl implements SFlowNodeDefinition {\n\n    private static final long serialVersionUID = 7475429470423228259L;\n\n    private final List<STransitionDefinition> incomings;\n\n    private final List<STransitionDefinition> outgoings;\n\n    private STransitionDefinition defaultTransition;\n\n    private final List<SConnectorDefinition> connectors;\n\n    private final Map<String, SConnectorDefinition> allConnectorsMap;\n\n    private String description;\n\n    private SExpression displayDescription;\n\n    private SExpression displayDescriptionAfterCompletion;\n\n    private SExpression displayName;\n\n    private SFlowElementContainerDefinition parentContainer;\n\n    private final Map<ConnectorEvent, List<SConnectorDefinition>> connectorsMap;\n\n    public SFlowNodeDefinitionImpl(final FlowNodeDefinition flowNodeDefinition,\n            final Map<String, STransitionDefinition> sTransitionsMap) {\n        super(flowNodeDefinition.getName());\n        incomings = buildIncomingTransitions(flowNodeDefinition, sTransitionsMap);\n        outgoings = buildOutGoingTransitions(flowNodeDefinition, sTransitionsMap);\n        if (flowNodeDefinition.getDefaultTransition() != null) {\n            defaultTransition = sTransitionsMap.get(String.valueOf(flowNodeDefinition.getDefaultTransition().getId()));\n        }\n        final List<ConnectorDefinition> connectors2 = flowNodeDefinition.getConnectors();\n        final ArrayList<SConnectorDefinition> mConnectors = new ArrayList<>(connectors2.size());\n        connectorsMap = new HashMap<>(2);\n        connectorsMap.put(ConnectorEvent.ON_ENTER, new ArrayList<SConnectorDefinition>());\n        connectorsMap.put(ConnectorEvent.ON_FINISH, new ArrayList<SConnectorDefinition>());\n        allConnectorsMap = new HashMap<>(2);\n        for (final ConnectorDefinition connector : connectors2) {\n            final SConnectorDefinitionImpl e = new SConnectorDefinitionImpl(connector);\n            mConnectors.add(e);\n            connectorsMap.get(e.getActivationEvent()).add(e);\n            allConnectorsMap.put(e.getName(), e);\n        }\n        connectors = Collections.unmodifiableList(mConnectors);\n\n        description = flowNodeDefinition.getDescription();\n        displayDescription = ServerModelConvertor.convertExpression(flowNodeDefinition.getDisplayDescription());\n        displayDescriptionAfterCompletion = ServerModelConvertor\n                .convertExpression(flowNodeDefinition.getDisplayDescriptionAfterCompletion());\n        displayName = ServerModelConvertor.convertExpression(flowNodeDefinition.getDisplayName());\n        setId(flowNodeDefinition.getId());\n    }\n\n    public SFlowNodeDefinitionImpl(final long id, final String name) {\n        super(name);\n        setId(id);\n        incomings = new ArrayList<>();\n        outgoings = new ArrayList<>();\n        connectors = new ArrayList<>();\n        connectorsMap = new HashMap<>(2);\n        connectorsMap.put(ConnectorEvent.ON_ENTER, new ArrayList<SConnectorDefinition>());\n        connectorsMap.put(ConnectorEvent.ON_FINISH, new ArrayList<SConnectorDefinition>());\n        allConnectorsMap = new HashMap<>(2);\n    }\n\n    @Override\n    public SFlowElementContainerDefinition getParentContainer() {\n        return parentContainer;\n    }\n\n    private List<STransitionDefinition> buildOutGoingTransitions(final FlowNodeDefinition nodeDefinition,\n            final Map<String, STransitionDefinition> sTransitionsMap) {\n        Iterator<TransitionDefinition> iterator;\n        final List<TransitionDefinition> outgoingTransitions = nodeDefinition.getOutgoingTransitions();\n        final List<STransitionDefinition> outgoings = new ArrayList<>();\n        iterator = outgoingTransitions.iterator();\n        while (iterator.hasNext()) {\n            final TransitionDefinition sTransition = iterator.next();\n            final STransitionDefinition outgoing = sTransitionsMap.get(String.valueOf(sTransition.getId()));\n            outgoings.add(outgoing);\n        }\n        return outgoings;\n    }\n\n    private List<STransitionDefinition> buildIncomingTransitions(final FlowNodeDefinition nodeDefinition,\n            final Map<String, STransitionDefinition> sTransitionsMap) {\n        final List<TransitionDefinition> incomingTransitions = nodeDefinition.getIncomingTransitions();\n        final List<STransitionDefinition> incomings = new ArrayList<>();\n        final Iterator<TransitionDefinition> iterator = incomingTransitions.iterator();\n        while (iterator.hasNext()) {\n            final TransitionDefinition sTransition = iterator.next();\n            final STransitionDefinition incoming = sTransitionsMap.get(String.valueOf(sTransition.getId()));\n            incomings.add(incoming);\n        }\n        return incomings;\n    }\n\n    @Override\n    public List<STransitionDefinition> getOutgoingTransitions() {\n        return Collections.unmodifiableList(outgoings);\n    }\n\n    @Override\n    public List<STransitionDefinition> getIncomingTransitions() {\n        return Collections.unmodifiableList(incomings);\n    }\n\n    @Override\n    public List<SConnectorDefinition> getConnectors() {\n        return Collections.unmodifiableList(connectors);\n    }\n\n    @Override\n    public boolean hasConnectors() {\n        return connectors.size() > 0;\n    }\n\n    @Override\n    // public SConnectorDefinition getConnectorDefinition(final long id) { // FIXME: Uncomment when generate id\n    public SConnectorDefinition getConnectorDefinition(final String name) {\n        return allConnectorsMap.get(name);\n    }\n\n    @Override\n    public STransitionDefinition getDefaultTransition() {\n        return defaultTransition;\n    }\n\n    @Override\n    public boolean hasIncomingTransitions() {\n        return !incomings.isEmpty();\n    }\n\n    @Override\n    public boolean hasOutgoingTransitions() {\n        return !outgoings.isEmpty();\n    }\n\n    @Override\n    public List<SConnectorDefinition> getConnectors(final ConnectorEvent connectorEvent) {\n        return connectorsMap.get(connectorEvent);\n    }\n\n    public void addOutgoingTransition(final STransitionDefinition sTransition) {\n        if (!outgoings.contains(sTransition)) {\n            outgoings.add(sTransition);\n        }\n    }\n\n    public void addIncomingTransition(final STransitionDefinition sTransition) {\n        if (!incomings.contains(sTransition)) {\n            incomings.add(sTransition);\n        }\n    }\n\n    @Override\n    public String getDescription() {\n        return description;\n    }\n\n    public void setDescription(final String description) {\n        this.description = description;\n    }\n\n    @Override\n    public SExpression getDisplayDescription() {\n        return displayDescription;\n    }\n\n    public void setDisplayDescription(final SExpression displayDescription) {\n        this.displayDescription = displayDescription;\n    }\n\n    @Override\n    public SExpression getDisplayDescriptionAfterCompletion() {\n        return displayDescriptionAfterCompletion;\n    }\n\n    @Override\n    public SExpression getDisplayName() {\n        return displayName;\n    }\n\n    public void setDisplayName(final SExpression displayName) {\n        this.displayName = displayName;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = super.hashCode();\n        result = prime * result + (connectors == null ? 0 : connectors.hashCode());\n        result = prime * result + (defaultTransition == null ? 0 : defaultTransition.hashCode());\n        result = prime * result + (description == null ? 0 : description.hashCode());\n        result = prime * result + (displayDescription == null ? 0 : displayDescription.hashCode());\n        result = prime * result\n                + (displayDescriptionAfterCompletion == null ? 0 : displayDescriptionAfterCompletion.hashCode());\n        result = prime * result + (displayName == null ? 0 : displayName.hashCode());\n        result = prime * result + (incomings == null ? 0 : incomings.hashCode());\n        result = prime * result + (outgoings == null ? 0 : outgoings.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (!super.equals(obj)) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        final SFlowNodeDefinitionImpl other = (SFlowNodeDefinitionImpl) obj;\n        if (connectors == null) {\n            if (other.connectors != null) {\n                return false;\n            }\n        } else if (!connectors.equals(other.connectors)) {\n            return false;\n        }\n        if (defaultTransition == null) {\n            if (other.defaultTransition != null) {\n                return false;\n            }\n        } else if (!defaultTransition.equals(other.defaultTransition)) {\n            return false;\n        }\n        if (description == null) {\n            if (other.description != null) {\n                return false;\n            }\n        } else if (!description.equals(other.description)) {\n            return false;\n        }\n        if (displayDescription == null) {\n            if (other.displayDescription != null) {\n                return false;\n            }\n        } else if (!displayDescription.equals(other.displayDescription)) {\n            return false;\n        }\n        if (displayDescriptionAfterCompletion == null) {\n            if (other.displayDescriptionAfterCompletion != null) {\n                return false;\n            }\n        } else if (!displayDescriptionAfterCompletion.equals(other.displayDescriptionAfterCompletion)) {\n            return false;\n        }\n        if (displayName == null) {\n            if (other.displayName != null) {\n                return false;\n            }\n        } else if (!displayName.equals(other.displayName)) {\n            return false;\n        }\n        if (incomings == null) {\n            if (other.incomings != null) {\n                return false;\n            }\n        } else if (!incomings.equals(other.incomings)) {\n            return false;\n        }\n        if (outgoings == null) {\n            if (other.outgoings != null) {\n                return false;\n            }\n        } else if (!outgoings.equals(other.outgoings)) {\n            return false;\n        }\n        return true;\n    }\n\n    @Override\n    public int getTransitionIndex(final Long transitionId) {\n        int index = 1;\n        boolean found = false;\n        final Iterator<STransitionDefinition> iterator = incomings.iterator();\n        while (!found && iterator.hasNext()) {\n            final STransitionDefinition next = iterator.next();\n            if (next.getId().equals(transitionId)) {\n                found = true;\n            } else {\n                index++;\n            }\n        }\n        return index;\n    }\n\n    @Override\n    public boolean isStartable() {\n        return !hasIncomingTransitions();\n    }\n\n    @Override\n    public boolean isParalleleOrInclusive() {\n        return false;\n    }\n\n    @Override\n    public boolean isExclusive() {\n        return false;\n    }\n\n    @Override\n    public boolean isBoundaryEvent() {\n        return SFlowNodeType.BOUNDARY_EVENT.equals(getType());\n    }\n\n    @Override\n    public boolean isInterrupting() {\n        return false;\n    }\n\n    @Override\n    public boolean isEventSubProcess() {\n        return false;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SGatewayDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.flownode.GatewayDefinition;\nimport org.bonitasoft.engine.bpm.flownode.GatewayType;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.definition.model.SGatewayDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SGatewayType;\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\n\n/**\n * @author Feng Hui\n * @author Zhao Na\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class SGatewayDefinitionImpl extends SFlowNodeDefinitionImpl implements SGatewayDefinition {\n\n    private static final long serialVersionUID = -5765195373419051716L;\n\n    private final SGatewayType gatewayType;\n\n    public SGatewayDefinitionImpl(final GatewayDefinition gatewayDefinition,\n            final Map<String, STransitionDefinition> transitionsMap) {\n        super(gatewayDefinition, transitionsMap);\n        final GatewayType type = gatewayDefinition.getGatewayType();\n        gatewayType = SGatewayType.valueOf(type.toString());\n    }\n\n    public SGatewayDefinitionImpl(final long id, final String name, final SGatewayType gatewayType) {\n        super(id, name);\n        this.gatewayType = gatewayType;\n    }\n\n    @Override\n    public SGatewayType getGatewayType() {\n        return gatewayType;\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.GATEWAY;\n    }\n\n    @Override\n    public boolean isParalleleOrInclusive() {\n        final SGatewayType gatewayType = this.getGatewayType();\n        return SGatewayType.PARALLEL.equals(gatewayType) || SGatewayType.INCLUSIVE.equals(gatewayType);\n    }\n\n    @Override\n    public boolean isExclusive() {\n        final SGatewayType gatewayType = this.getGatewayType();\n        return SGatewayType.EXCLUSIVE.equals(gatewayType);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SHumanTaskDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SHumanTaskDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SUserFilterDefinition;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic abstract class SHumanTaskDefinitionImpl extends SActivityDefinitionImpl implements SHumanTaskDefinition {\n\n    private static final long serialVersionUID = -4475497975786419487L;\n\n    private final String actorName;\n\n    private SUserFilterDefinition sUserFilterDefinition;\n\n    private String priority;\n\n    private SExpression expectedDuration;\n\n    public SHumanTaskDefinitionImpl(final HumanTaskDefinition userTaskDefinition,\n            final Map<String, STransitionDefinition> transitionsMap) {\n        super(userTaskDefinition, transitionsMap);\n        actorName = userTaskDefinition.getActorName();\n    }\n\n    public SHumanTaskDefinitionImpl(final long id, final String name, final String actorName) {\n        super(id, name);\n        this.actorName = actorName;\n    }\n\n    @Override\n    public String getActorName() {\n        return actorName;\n    }\n\n    public void setUserFilter(final SUserFilterDefinition sUserFilterDefinition) {\n        this.sUserFilterDefinition = sUserFilterDefinition;\n    }\n\n    @Override\n    public SUserFilterDefinition getSUserFilterDefinition() {\n        return sUserFilterDefinition;\n    }\n\n    @Override\n    public SExpression getExpectedDuration() {\n        return expectedDuration;\n    }\n\n    @Override\n    public String getPriority() {\n        return priority;\n    }\n\n    public void setPriority(final String priority) {\n        this.priority = priority;\n    }\n\n    public void setExpectedDuration(final SExpression expectedDuration) {\n        this.expectedDuration = expectedDuration;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SInputDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\n\nimport org.bonitasoft.engine.bpm.contract.InputDefinition;\nimport org.bonitasoft.engine.bpm.contract.Type;\nimport org.bonitasoft.engine.core.process.definition.model.SInputDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SType;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SInputDefinitionImpl extends SNamedElementImpl implements SInputDefinition {\n\n    private static final long serialVersionUID = -5021740296501498639L;\n    protected final List<SInputDefinition> inputDefinitions;\n    protected final String description;\n    protected final SType type;\n    protected final boolean multiple;\n\n    public SInputDefinitionImpl(final String name, final String description) {\n        this(name, null, description, false, null);\n    }\n\n    public SInputDefinitionImpl(final String name, final SType type, final String description, final boolean multiple,\n            final List<SInputDefinition> inputDefinitions) {\n        super(name);\n        this.description = description;\n        this.multiple = multiple;\n        this.type = type;\n        this.inputDefinitions = new ArrayList<>();\n        if (inputDefinitions != null) {\n            this.inputDefinitions.addAll(inputDefinitions);\n        }\n    }\n\n    public SInputDefinitionImpl(final String name, final SType type, final String description) {\n        this(name, type, description, false);\n    }\n\n    public SInputDefinitionImpl(final String name, final SType type, final String description, final boolean multiple) {\n        this(name, type, description, multiple, null);\n    }\n\n    public SInputDefinitionImpl(final InputDefinition input) {\n        this(input.getName(), convertTypeToSType(input.getType()), input.getDescription(), input.isMultiple(), null);\n        convertAndAddInputDefinitions(input);\n    }\n\n    public SInputDefinitionImpl(String name, String description, boolean multiple,\n            List<SInputDefinition> inputDefinitions) {\n        this(name, null, description, multiple, inputDefinitions);\n    }\n\n    protected static SType convertTypeToSType(final Type type2) {\n        if (type2 == null) {\n            return null;\n        }\n        return SType.valueOf(type2.toString());\n    }\n\n    private void convertAndAddInputDefinitions(final InputDefinition input) {\n        for (final InputDefinition inputDefinition : input.getInputs()) {\n            inputDefinitions.add(new SInputDefinitionImpl(inputDefinition));\n        }\n    }\n\n    @Override\n    public String getDescription() {\n        return description;\n    }\n\n    @Override\n    public SType getType() {\n        return type;\n    }\n\n    @Override\n    public boolean hasChildren() {\n        return !inputDefinitions.isEmpty();\n    }\n\n    @Override\n    public List<SInputDefinition> getInputDefinitions() {\n        return inputDefinitions;\n    }\n\n    @Override\n    public boolean isMultiple() {\n        return multiple;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (o == null || getClass() != o.getClass())\n            return false;\n        if (!super.equals(o))\n            return false;\n        SInputDefinitionImpl that = (SInputDefinitionImpl) o;\n        return Objects.equals(multiple, that.multiple) &&\n                Objects.equals(inputDefinitions, that.inputDefinitions) &&\n                Objects.equals(description, that.description) &&\n                Objects.equals(type, that.type);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(super.hashCode(), inputDefinitions, description, type, multiple);\n    }\n\n    @Override\n    public String toString() {\n        return \"SInputDefinitionImpl{\" +\n                \"inputDefinitions=\" + inputDefinitions +\n                \", description='\" + description + '\\'' +\n                \", type=\" + type +\n                \", multiple=\" + multiple +\n                \"} \" + super.toString();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SManualTaskDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.flownode.ManualTaskDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.definition.model.SManualTaskDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class SManualTaskDefinitionImpl extends SHumanTaskDefinitionImpl implements SManualTaskDefinition {\n\n    private static final long serialVersionUID = 4800299070670205477L;\n\n    /**\n     * @param manualTaskDefinition\n     * @param transitionsMap\n     */\n    public SManualTaskDefinitionImpl(final ManualTaskDefinition manualTaskDefinition,\n            final Map<String, STransitionDefinition> transitionsMap) {\n        super(manualTaskDefinition, transitionsMap);\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.MANUAL_TASK;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SMultiInstanceLoopCharacteristicsImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport org.bonitasoft.engine.bpm.flownode.MultiInstanceLoopCharacteristics;\nimport org.bonitasoft.engine.core.process.definition.model.SMultiInstanceLoopCharacteristics;\nimport org.bonitasoft.engine.core.process.definition.model.builder.ServerModelConvertor;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SMultiInstanceLoopCharacteristicsImpl implements SMultiInstanceLoopCharacteristics {\n\n    private static final long serialVersionUID = 5900494662430961903L;\n\n    private boolean isSequential;\n\n    private SExpression loopCardinality;\n\n    private SExpression completionCondition;\n\n    private String loopDataInputRef;\n\n    private String loopDataOutputRef;\n\n    private String dataInputItemRef;\n\n    private String dataOutputItemRef;\n\n    public SMultiInstanceLoopCharacteristicsImpl(\n            final MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics) {\n        isSequential = multiInstanceLoopCharacteristics.isSequential();\n        loopDataInputRef = multiInstanceLoopCharacteristics.getLoopDataInputRef();\n        loopDataOutputRef = multiInstanceLoopCharacteristics.getLoopDataOutputRef();\n        dataInputItemRef = multiInstanceLoopCharacteristics.getDataInputItemRef();\n        dataOutputItemRef = multiInstanceLoopCharacteristics.getDataOutputItemRef();\n        loopCardinality = ServerModelConvertor.convertExpression(multiInstanceLoopCharacteristics.getLoopCardinality());\n        completionCondition = ServerModelConvertor\n                .convertExpression(multiInstanceLoopCharacteristics.getCompletionCondition());\n    }\n\n    @Override\n    public boolean isSequential() {\n        return isSequential;\n    }\n\n    @Override\n    public SExpression getLoopCardinality() {\n        return loopCardinality;\n    }\n\n    @Override\n    public SExpression getCompletionCondition() {\n        return completionCondition;\n    }\n\n    @Override\n    public String getLoopDataInputRef() {\n        return loopDataInputRef;\n    }\n\n    @Override\n    public String getLoopDataOutputRef() {\n        return loopDataOutputRef;\n    }\n\n    @Override\n    public String getDataInputItemRef() {\n        return dataInputItemRef;\n    }\n\n    @Override\n    public String getDataOutputItemRef() {\n        return dataOutputItemRef;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + (completionCondition == null ? 0 : completionCondition.hashCode());\n        result = prime * result + (dataInputItemRef == null ? 0 : dataInputItemRef.hashCode());\n        result = prime * result + (dataOutputItemRef == null ? 0 : dataOutputItemRef.hashCode());\n        result = prime * result + (isSequential ? 1231 : 1237);\n        result = prime * result + (loopCardinality == null ? 0 : loopCardinality.hashCode());\n        result = prime * result + (loopDataInputRef == null ? 0 : loopDataInputRef.hashCode());\n        result = prime * result + (loopDataOutputRef == null ? 0 : loopDataOutputRef.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        final SMultiInstanceLoopCharacteristicsImpl other = (SMultiInstanceLoopCharacteristicsImpl) obj;\n        if (completionCondition == null) {\n            if (other.completionCondition != null) {\n                return false;\n            }\n        } else if (!completionCondition.equals(other.completionCondition)) {\n            return false;\n        }\n        if (dataInputItemRef == null) {\n            if (other.dataInputItemRef != null) {\n                return false;\n            }\n        } else if (!dataInputItemRef.equals(other.dataInputItemRef)) {\n            return false;\n        }\n        if (dataOutputItemRef == null) {\n            if (other.dataOutputItemRef != null) {\n                return false;\n            }\n        } else if (!dataOutputItemRef.equals(other.dataOutputItemRef)) {\n            return false;\n        }\n        if (isSequential != other.isSequential) {\n            return false;\n        }\n        if (loopCardinality == null) {\n            if (other.loopCardinality != null) {\n                return false;\n            }\n        } else if (!loopCardinality.equals(other.loopCardinality)) {\n            return false;\n        }\n        if (loopDataInputRef == null) {\n            if (other.loopDataInputRef != null) {\n                return false;\n            }\n        } else if (!loopDataInputRef.equals(other.loopDataInputRef)) {\n            return false;\n        }\n        if (loopDataOutputRef == null) {\n            if (other.loopDataOutputRef != null) {\n                return false;\n            }\n        } else if (!loopDataOutputRef.equals(other.loopDataOutputRef)) {\n            return false;\n        }\n        return true;\n    }\n\n    @Override\n    public String toString() {\n        final StringBuilder builder = new StringBuilder();\n        builder.append(\"SMultiInstanceLoopCharacteristicsImpl [isSequential=\");\n        builder.append(isSequential);\n        builder.append(\", loopCardinality=\");\n        builder.append(loopCardinality);\n        builder.append(\", completionCondition=\");\n        builder.append(completionCondition);\n        builder.append(\", loopDataInputRef=\");\n        builder.append(loopDataInputRef);\n        builder.append(\", loopDataOutputRef=\");\n        builder.append(loopDataOutputRef);\n        builder.append(\", dataInputItemRef=\");\n        builder.append(dataInputItemRef);\n        builder.append(\", dataOutputItemRef=\");\n        builder.append(dataOutputItemRef);\n        builder.append(\"]\");\n        return builder.toString();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SNamedElementImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport org.bonitasoft.engine.core.process.definition.model.SNamedElement;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SNamedElementImpl extends SBaseElementImpl implements SNamedElement {\n\n    private static final long serialVersionUID = 4789196762554891321L;\n\n    private final String name;\n\n    public SNamedElementImpl(final String name) {\n        this.name = name;\n    }\n\n    @Override\n    public String getName() {\n        return name;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = super.hashCode();\n        result = prime * result + (name == null ? 0 : name.hashCode());\n        return result;\n    }\n\n    protected EQUALS_STATE checkFurtherNaiveEquality(final Object obj) {\n        EQUALS_STATE state;\n        if ((state = super.checkNaiveEquality(obj)) != EQUALS_STATE.CONTINUE) {\n            return state;\n        } else if (!super.equals(obj)) {\n            return EQUALS_STATE.RETURN_FALSE;\n        } else {\n            return EQUALS_STATE.CONTINUE;\n        }\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        switch (checkFurtherNaiveEquality(obj)) {\n            case RETURN_FALSE:\n                return false;\n            case RETURN_TRUE:\n                return true;\n            case CONTINUE:\n            default:\n                break;\n        }\n        final SNamedElementImpl other = (SNamedElementImpl) obj;\n        if (name == null) {\n            if (other.name != null) {\n                return false;\n            }\n        } else if (!name.equals(other.name)) {\n            return false;\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SParameterDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport org.bonitasoft.engine.bpm.parameter.ParameterDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SParameterDefinition;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SParameterDefinitionImpl extends SNamedElementImpl implements SParameterDefinition {\n\n    private static final long serialVersionUID = -6048365663287821057L;\n\n    private String description;\n\n    private final String type;\n\n    public SParameterDefinitionImpl(final ParameterDefinition parameterDefinition) {\n        super(parameterDefinition.getName());\n        description = parameterDefinition.getDescription();\n        type = parameterDefinition.getType();\n    }\n\n    @Override\n    public String getDescription() {\n        return description;\n    }\n\n    @Override\n    public String getType() {\n        return type;\n    }\n\n    public void setDescription(final String description) {\n        this.description = description;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SProcessDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.bpm.actor.ActorDefinition;\nimport org.bonitasoft.engine.bpm.context.ContextEntry;\nimport org.bonitasoft.engine.bpm.contract.ContractDefinition;\nimport org.bonitasoft.engine.bpm.parameter.ParameterDefinition;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SActorDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SContextEntry;\nimport org.bonitasoft.engine.core.process.definition.model.SContractDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SParameterDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.builder.ServerModelConvertor;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Matthieu Chaffotte\n * @author Zhao Na\n * @author Elias Ricken de Medeiros\n * @author Yanyan Liu\n * @author Celine Souchet\n */\npublic class SProcessDefinitionImpl extends SNamedElementImpl implements SProcessDefinition {\n\n    private static final long serialVersionUID = -8961400385282752731L;\n\n    private final String version;\n    private final Set<SParameterDefinition> parameters;\n    private final Set<SActorDefinition> actors;\n    private final List<SContextEntry> context = new ArrayList<>();\n    private String description;\n    private SActorDefinition sActorInitiator;\n    private SFlowElementContainerDefinition container;\n    private String stringIndexLabel1;\n    private String stringIndexLabel2;\n    private String stringIndexLabel3;\n    private String stringIndexLabel4;\n    private String stringIndexLabel5;\n    private SExpression stringIndexValue1;\n    private SExpression stringIndexValue2;\n    private SExpression stringIndexValue3;\n    private SExpression stringIndexValue4;\n    private SExpression stringIndexValue5;\n    private SContractDefinition contract;\n\n    public SProcessDefinitionImpl(final DesignProcessDefinition processDefinition) {\n        super(processDefinition.getName());\n        description = processDefinition.getDescription();\n        version = processDefinition.getVersion();\n        actors = new HashSet<>();\n        stringIndexLabel1 = processDefinition.getStringIndexLabel(1);\n        stringIndexLabel2 = processDefinition.getStringIndexLabel(2);\n        stringIndexLabel3 = processDefinition.getStringIndexLabel(3);\n        stringIndexLabel4 = processDefinition.getStringIndexLabel(4);\n        stringIndexLabel5 = processDefinition.getStringIndexLabel(5);\n        stringIndexValue1 = ServerModelConvertor.convertExpression(processDefinition.getStringIndexValue(1));\n        stringIndexValue2 = ServerModelConvertor.convertExpression(processDefinition.getStringIndexValue(2));\n        stringIndexValue3 = ServerModelConvertor.convertExpression(processDefinition.getStringIndexValue(3));\n        stringIndexValue4 = ServerModelConvertor.convertExpression(processDefinition.getStringIndexValue(4));\n        stringIndexValue5 = ServerModelConvertor.convertExpression(processDefinition.getStringIndexValue(5));\n        for (final ActorDefinition actor : processDefinition.getActorsList()) {\n            actors.add(new SActorDefinitionImpl(actor));\n        }\n\n        parameters = new HashSet<>();\n        for (final ParameterDefinition parameterDefinition : processDefinition.getParameters()) {\n            parameters.add(new SParameterDefinitionImpl(parameterDefinition));\n        }\n\n        final ActorDefinition actorInitiator = processDefinition.getActorInitiator();\n        if (actorInitiator != null) {\n            sActorInitiator = new SActorDefinitionImpl(actorInitiator);\n        }\n        final ContractDefinition contract = processDefinition.getContract();\n        if (contract != null) {\n            setContract(new SContractDefinitionImpl(contract));\n        }\n        for (ContextEntry contextEntry : processDefinition.getContext()) {\n            context.add(new SContextEntryImpl(contextEntry.getKey(),\n                    ServerModelConvertor.convertExpression(contextEntry.getExpression())));\n        }\n        container = new SFlowElementContainerDefinitionImpl(processDefinition.getFlowElementContainer());\n\n    }\n\n    public SProcessDefinitionImpl(final String name, final String version) {\n        super(name);\n        this.version = version;\n        actors = new HashSet<>();\n        parameters = new HashSet<>();\n        container = new SFlowElementContainerDefinitionImpl();\n    }\n\n    @Override\n    public String getVersion() {\n        return version;\n    }\n\n    @Override\n    public Set<SParameterDefinition> getParameters() {\n        return parameters;\n    }\n\n    @Override\n    public SParameterDefinition getParameter(final String parameterName) {\n        final Iterator<SParameterDefinition> iterator = parameters.iterator();\n        SParameterDefinition found = null;\n        while (found == null && iterator.hasNext()) {\n            final SParameterDefinition next = iterator.next();\n            if (next.getName().equals(parameterName)) {\n                found = next;\n            }\n        }\n        return found;\n    }\n\n    @Override\n    public Set<SActorDefinition> getActors() {\n        return actors;\n    }\n\n    public void addActor(final SActorDefinition actor) {\n        actors.add(actor);\n    }\n\n    @Override\n    public SActorDefinition getActorInitiator() {\n        return sActorInitiator;\n    }\n\n    @Override\n    public String getDescription() {\n        return description;\n    }\n\n    public void setDescription(final String description) {\n        this.description = description;\n    }\n\n    @Override\n    public SFlowElementContainerDefinition getProcessContainer() {\n        return container;\n    }\n\n    public void setProcessContainer(final SFlowElementContainerDefinition processContainer) {\n        container = processContainer;\n    }\n\n    @Override\n    public String getStringIndexLabel(final int index) {\n        switch (index) {\n            case 1:\n                return stringIndexLabel1;\n            case 2:\n                return stringIndexLabel2;\n            case 3:\n                return stringIndexLabel3;\n            case 4:\n                return stringIndexLabel4;\n            case 5:\n                return stringIndexLabel5;\n            default:\n                throw new IndexOutOfBoundsException(\"search key label must be between 1 and 5 (included)\");\n        }\n    }\n\n    public void setStringIndex(final int index, final String label, final SExpression initialValue) {\n        switch (index) {\n            case 1:\n                stringIndexLabel1 = label;\n                stringIndexValue1 = initialValue;\n                break;\n            case 2:\n                stringIndexLabel2 = label;\n                stringIndexValue2 = initialValue;\n                break;\n            case 3:\n                stringIndexLabel3 = label;\n                stringIndexValue3 = initialValue;\n                break;\n            case 4:\n                stringIndexLabel4 = label;\n                stringIndexValue4 = initialValue;\n                break;\n            case 5:\n                stringIndexLabel5 = label;\n                stringIndexValue5 = initialValue;\n                break;\n            default:\n                throw new IndexOutOfBoundsException(\"search key label must be between 1 and 5 (included)\");\n        }\n    }\n\n    @Override\n    public SExpression getStringIndexValue(final int index) {\n        switch (index) {\n            case 1:\n                return stringIndexValue1;\n            case 2:\n                return stringIndexValue2;\n            case 3:\n                return stringIndexValue3;\n            case 4:\n                return stringIndexValue4;\n            case 5:\n                return stringIndexValue5;\n            default:\n                throw new IndexOutOfBoundsException(\"search key label must be between 1 and 5 (included)\");\n        }\n    }\n\n    @Override\n    public boolean hasConnectors() {\n        return getProcessContainer().getConnectors().size() > 0;\n    }\n\n    @Override\n    public SContractDefinition getContract() {\n        return contract;\n    }\n\n    public void setContract(SContractDefinition contract) {\n        this.contract = contract;\n    }\n\n    @Override\n    public List<SContextEntry> getContext() {\n        return context;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SReceiveTaskDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.ReceiveTaskDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.definition.model.SReceiveTaskDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SCatchMessageEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.SCatchMessageEventTriggerDefinitionImpl;\n\n/**\n * @author Julien Molinaro\n * @author Celine Souchet\n */\npublic class SReceiveTaskDefinitionImpl extends SActivityDefinitionImpl implements SReceiveTaskDefinition {\n\n    private static final long serialVersionUID = 8112705930442175231L;\n\n    private final SCatchMessageEventTriggerDefinitionImpl trigger;\n\n    public SReceiveTaskDefinitionImpl(final ReceiveTaskDefinitionImpl activityDefinition,\n            final Map<String, STransitionDefinition> transitionsMap) {\n        super(activityDefinition, transitionsMap);\n        trigger = new SCatchMessageEventTriggerDefinitionImpl(activityDefinition.getTrigger());\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.RECEIVE_TASK;\n    }\n\n    @Override\n    public SCatchMessageEventTriggerDefinition getTrigger() {\n        return trigger;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SSendTaskDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.SendTaskDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.definition.model.SSendTaskDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowMessageEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.SThrowMessageEventTriggerDefinitionImpl;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class SSendTaskDefinitionImpl extends SActivityDefinitionImpl implements SSendTaskDefinition {\n\n    private static final long serialVersionUID = 8112705930442175231L;\n\n    private final SThrowMessageEventTriggerDefinition trigger;\n\n    public SSendTaskDefinitionImpl(final SendTaskDefinitionImpl activityDefinition,\n            final Map<String, STransitionDefinition> transitionsMap) {\n        super(activityDefinition, transitionsMap);\n        trigger = new SThrowMessageEventTriggerDefinitionImpl(activityDefinition.getMessageTrigger());\n    }\n\n    public SSendTaskDefinitionImpl(final long id, final String name,\n            final SThrowMessageEventTriggerDefinition throwMessageEventTriggerDefinition) {\n        super(id, name);\n        trigger = throwMessageEventTriggerDefinition;\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.SEND_TASK;\n    }\n\n    @Override\n    public SThrowMessageEventTriggerDefinition getMessageTrigger() {\n        return trigger;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SStandardLoopCharacteristicsImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport org.bonitasoft.engine.bpm.flownode.StandardLoopCharacteristics;\nimport org.bonitasoft.engine.core.process.definition.model.SStandardLoopCharacteristics;\nimport org.bonitasoft.engine.core.process.definition.model.builder.ServerModelConvertor;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SStandardLoopCharacteristicsImpl implements SStandardLoopCharacteristics {\n\n    private static final long serialVersionUID = 2762060718511545677L;\n\n    private final SExpression loopCondition;\n\n    private final boolean testBefore;\n\n    private final SExpression loopMax;\n\n    public SStandardLoopCharacteristicsImpl(final SExpression loopCondition, final boolean testBefore) {\n        super();\n        this.loopCondition = loopCondition;\n        this.testBefore = testBefore;\n        loopMax = null;\n    }\n\n    public SStandardLoopCharacteristicsImpl(final StandardLoopCharacteristics loopCharacteristics) {\n        super();\n        testBefore = loopCharacteristics.isTestBefore();\n        final Expression loopMaxExpression = loopCharacteristics.getLoopMax();\n        loopMax = ServerModelConvertor.convertExpression(loopMaxExpression);\n        final Expression condition = loopCharacteristics.getLoopCondition();\n        loopCondition = ServerModelConvertor.convertExpression(condition);\n\n    }\n\n    @Override\n    public SExpression getLoopCondition() {\n        return loopCondition;\n    }\n\n    @Override\n    public boolean isTestBefore() {\n        return testBefore;\n    }\n\n    @Override\n    public SExpression getLoopMax() {\n        return loopMax;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SSubProcessDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport org.bonitasoft.engine.bpm.process.SubProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.definition.model.SSubProcessDefinition;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class SSubProcessDefinitionImpl extends SActivityDefinitionImpl implements SSubProcessDefinition {\n\n    private static final long serialVersionUID = -244737326513630024L;\n\n    private final boolean triggeredByEvent;\n\n    private SFlowElementContainerDefinition subProcessContainer;\n\n    public SSubProcessDefinitionImpl(final SubProcessDefinition subProcess) {\n        super(subProcess.getId(), subProcess.getName());\n        triggeredByEvent = subProcess.isTriggeredByEvent();\n        subProcessContainer = new SFlowElementContainerDefinitionImpl(subProcess.getSubProcessContainer());\n    }\n\n    public SSubProcessDefinitionImpl(final long id, final String name, final boolean triggeredByEvent) {\n        super(id, name);\n        this.triggeredByEvent = triggeredByEvent;\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.SUB_PROCESS;\n    }\n\n    @Override\n    public boolean isTriggeredByEvent() {\n        return triggeredByEvent;\n    }\n\n    @Override\n    public SFlowElementContainerDefinition getSubProcessContainer() {\n        return subProcessContainer;\n    }\n\n    public void setSubProcessContainer(final SFlowElementContainerDefinition container) {\n        subProcessContainer = container;\n    }\n\n    @Override\n    public boolean isStartable() {\n        return !isTriggeredByEvent() && super.isStartable();\n    }\n\n    @Override\n    public boolean isEventSubProcess() {\n        return isTriggeredByEvent();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/STransitionDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport org.bonitasoft.engine.bpm.flownode.TransitionDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.builder.ServerModelConvertor;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Matthieu Chaffotte\n * @author Zhao Na\n * @author Celine Souchet\n */\npublic class STransitionDefinitionImpl extends SNamedElementImpl implements STransitionDefinition {\n\n    private static final long serialVersionUID = -5150684543828946974L;\n\n    private final long source;\n\n    private final long target;\n\n    private SExpression condition;\n\n    public STransitionDefinitionImpl(final TransitionDefinition transition) {\n        this(transition.getName(), transition.getSource(), transition.getTarget());\n        setId(transition.getId());\n        if (transition.getCondition() != null) {\n            condition = ServerModelConvertor.convertExpression(transition.getCondition());\n        }\n    }\n\n    public STransitionDefinitionImpl(final String name) {\n        this(name, -1, -1);\n    }\n\n    public STransitionDefinitionImpl(final String name, final long source, final long target) {\n        super(name);\n        this.source = source;\n        this.target = target;\n    }\n\n    @Override\n    public long getSource() {\n        return source;\n    }\n\n    @Override\n    public long getTarget() {\n        return target;\n    }\n\n    public void setCondition(final SExpression condition) {\n        this.condition = condition;\n    }\n\n    @Override\n    public SExpression getCondition() {\n        return condition;\n    }\n\n    @Override\n    public boolean hasCondition() {\n        return condition != null;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = super.hashCode();\n        result = prime * result + (condition == null ? 0 : condition.hashCode());\n        result = prime * result + (int) (source ^ source >>> 32);\n        result = prime * result + (int) (target ^ target >>> 32);\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (!super.equals(obj)) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        final STransitionDefinitionImpl other = (STransitionDefinitionImpl) obj;\n        if (condition == null) {\n            if (other.condition != null) {\n                return false;\n            }\n        } else if (!condition.equals(other.condition)) {\n            return false;\n        }\n        if (source != other.source) {\n            return false;\n        }\n        if (target != other.target) {\n            return false;\n        }\n        return true;\n    }\n\n    @Override\n    public String toString() {\n        return \"[\" + getName() + \"]\";\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SUserFilterDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.bonitasoft.engine.bpm.userfilter.UserFilterDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SUserFilterDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.builder.ServerModelConvertor;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SUserFilterDefinitionImpl extends SNamedElementImpl implements SUserFilterDefinition {\n\n    private static final long serialVersionUID = -6045216424839658552L;\n\n    private final String filterId;\n\n    private final String version;\n\n    private final Map<String, SExpression> inputs;\n\n    /**\n     * @param userFilter\n     */\n    public SUserFilterDefinitionImpl(final UserFilterDefinition userFilter) {\n        super(userFilter.getName());\n        filterId = userFilter.getUserFilterId();\n        version = userFilter.getVersion();\n        inputs = new HashMap<>(userFilter.getInputs().size());\n        for (final Entry<String, Expression> input : userFilter.getInputs().entrySet()) {\n            final Expression value = input.getValue();\n            final SExpression sExpression = ServerModelConvertor.convertExpression(value);\n            inputs.put(input.getKey(), sExpression);\n        }\n    }\n\n    @Override\n    public String getUserFilterId() {\n        return filterId;\n    }\n\n    @Override\n    public Map<String, SExpression> getInputs() {\n        return inputs;\n    }\n\n    @Override\n    public String getVersion() {\n        return version;\n    }\n\n    @Override\n    public String toString() {\n        final StringBuilder sb = new StringBuilder(\"SUserFilterDefinitionImpl{\");\n        sb.append(\"filterId='\").append(filterId).append('\\'');\n        sb.append(\", version='\").append(version).append('\\'');\n        sb.append(\", inputs=\").append(inputs);\n        sb.append('}');\n        return sb.toString();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SUserTaskDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.context.ContextEntry;\nimport org.bonitasoft.engine.bpm.contract.ContractDefinition;\nimport org.bonitasoft.engine.bpm.flownode.UserTaskDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SContextEntry;\nimport org.bonitasoft.engine.core.process.definition.model.SContractDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SUserTaskDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.builder.ServerModelConvertor;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class SUserTaskDefinitionImpl extends SHumanTaskDefinitionImpl implements SUserTaskDefinition {\n\n    private static final long serialVersionUID = 9039679250456947450L;\n\n    private SContractDefinition contract;\n    private final List<SContextEntry> context = new ArrayList<>();\n\n    public SUserTaskDefinitionImpl(final UserTaskDefinition userTaskDefinition,\n            final Map<String, STransitionDefinition> transitionsMap) {\n        super(userTaskDefinition, transitionsMap);\n        final ContractDefinition contract = userTaskDefinition.getContract();\n        if (contract != null) {\n            setContract(new SContractDefinitionImpl(contract));\n        }\n        for (ContextEntry contextEntry : userTaskDefinition.getContext()) {\n            context.add(new SContextEntryImpl(contextEntry.getKey(),\n                    ServerModelConvertor.convertExpression(contextEntry.getExpression())));\n        }\n    }\n\n    public SUserTaskDefinitionImpl(final long id, final String name, final String actorName) {\n        super(id, name, actorName);\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.USER_TASK;\n    }\n\n    @Override\n    public SContractDefinition getContract() {\n        if (contract == null) {\n            return new SContractDefinitionImpl();\n        }\n        return contract;\n    }\n\n    @Override\n    public List<SContextEntry> getContext() {\n        return context;\n    }\n\n    public void setContract(final SContractDefinition contract) {\n        this.contract = contract;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/main/resources/org/bonitasoft/engine/core/process/definition/model/impl/hibernate/process.definition.queries.hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\" \"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd\">\n<hibernate-mapping auto-import=\"false\">\n\n\t<query name=\"getDeployInfoByProcessDefId\">\n\t\tSELECT process_definition\n\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition\n\t\tWHERE process_definition.processId = :processId\n\t</query>\n\n\t<query name=\"getProcessDefinitionDeployInfosByName\">\n\t\tSELECT process_definition\n\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition\n\t\tWHERE process_definition.name = :name\n\t\tAND process_definition.activationState = 'ENABLED'\n\t</query>\n\n\t<query name=\"getProcessesWithActorOnlyForUser\">\n\t\tSELECT DISTINCT process_definition\n\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition,\n\t\t\t org.bonitasoft.engine.actor.mapping.model.SActor AS actor,\n\t\t\t org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n\t\tWHERE process_definition.processId = actor.scopeId\n\t\tAND actor.id = actormember.actorId\n\t\tAND actormember.userId = :userId\n\t\tAND actormember.actorId NOT IN (\n\t\t\tSELECT actorId\n\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS am2\n\t\t\tWHERE am2.userId != actormember.userId\n\t\t)\n\t</query>\n\n\t<query name=\"getProcessesWithActorOnlyForUsers\">\n\t\tSELECT DISTINCT process_definition\n\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition,\n\t\t\t org.bonitasoft.engine.actor.mapping.model.SActor AS actor,\n\t\t\t org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n\t\tWHERE process_definition.processId = actor.scopeId\n\t\tAND actor.id = actormember.actorId\n\t\tAND actormember.userId IN (:userIds)\n\t\tAND actormember.actorId NOT IN (\n\t\t\tSELECT actorId\n\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS am2\n\t\t\tWHERE am2.userId NOT IN (:userIds)\n\t\t)\n\t</query>\n\n\t<query name=\"getProcessesWithActorOnlyForRole\">\n\t\tSELECT DISTINCT process_definition\n\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor,\n\t\t\t org.bonitasoft.engine.actor.mapping.model.SActorMember AS actor_member,\n\t\t\t org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition\n\t\tWHERE actor.scopeId = process_definition.processId\n\t\tAND actor.id = actor_member.actorId\n\t\tAND actor_member.roleId = :roleId\n\t\tAND\tactor_member.actorId NOT IN (\n\t\t\t\tSELECT am2.actorId\n\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS am2\n\t\t\t\tWHERE am2.roleId != actor_member.roleId\n\t\t\t\t)\n\t</query>\n\n\t<query name=\"getProcessesWithActorOnlyForRoles\">\n\t\tSELECT DISTINCT process_definition\n\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor,\n\t\t\t org.bonitasoft.engine.actor.mapping.model.SActorMember AS actor_member,\n\t\t\t org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition\n\t\tWHERE actor.scopeId = process_definition.processId\n\t\tAND actor.id = actor_member.actorId\n\t\tAND actor_member.roleId IN :roleIds\n\t\tAND\tactor_member.actorId NOT IN (\n\t\t\t\tSELECT am2.actorId\n\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS am2\n\t\t\t\tWHERE am2.roleId NOT IN (:roleIds)\n\t\t\t\t)\n\t</query>\n\n\t<query name=\"getProcessesWithActorOnlyForGroup\">\n\t\tSELECT DISTINCT process_definition\n\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor,\n\t\t\t org.bonitasoft.engine.actor.mapping.model.SActorMember AS actor_member,\n\t\t\t org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition\n\t\tWHERE actor.scopeId = process_definition.processId\n\t\tAND actor.id = actor_member.actorId\n\t\tAND actor_member.groupId IN (\n\t\t\tSELECT groupe.id\n        \tFROM org.bonitasoft.engine.identity.model.SGroup AS groupe,\n        \t\t org.bonitasoft.engine.identity.model.SGroup AS group0\n        \tWHERE ( CONCAT(groupe.parentPath,'/',groupe.name,'/') LIKE CONCAT('/',group0.name,'/','%')\n\t\t\t\t\tOR groupe.id = :groupId )\n        \tAND group0.id = :groupId\n        \t)\n        AND actor_member.actorId NOT IN (\n\t\t\tSELECT am2.actorId\n\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS am2\n        \tWHERE am2.groupId != actor_member.groupId\n        \tAND am2.groupId NOT IN (\n            \tSELECT groupe.id\n                FROM org.bonitasoft.engine.identity.model.SGroup AS groupe,\n                org.bonitasoft.engine.identity.model.SGroup AS group0\n                WHERE ( CONCAT(groupe.parentPath,'/',groupe.name,'/') LIKE CONCAT('/',group0.name,'/','%')\n\t\t\t\t\tOR groupe.id = :groupId )\n        \tAND group0.id = :groupId\n                )\n\t\t\t)\n\t</query>\n\n\t<query name=\"getProcessesWithActorOnlyForGroups\">\n\t\tSELECT DISTINCT process_definition\n\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor,\n\t\t\t org.bonitasoft.engine.actor.mapping.model.SActorMember AS actor_member,\n\t\t\t org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition\n\t\tWHERE actor.scopeId = process_definition.processId\n\t\tAND actor.id = actor_member.actorId\n\t\tAND actor_member.groupId IN (\n\t\t\tSELECT groupe.id\n        \tFROM org.bonitasoft.engine.identity.model.SGroup AS groupe,\n        \t\t org.bonitasoft.engine.identity.model.SGroup AS group0\n        \tWHERE ( CONCAT(groupe.parentPath,'/',groupe.name,'/') LIKE CONCAT('/',group0.name,'/','%')\n\t\t\t\t\tOR groupe.id IN (:groupIds) )\n\t\t\tAND group0.id IN (:groupIds)\n        \t)\n        AND actor_member.actorId NOT IN (\n\t\t\tSELECT am2.actorId\n\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS am2\n        \tWHERE am2.groupId != actor_member.groupId\n        \tAND am2.groupId NOT IN (\n            \tSELECT groupe.id\n                FROM org.bonitasoft.engine.identity.model.SGroup AS groupe,\n                org.bonitasoft.engine.identity.model.SGroup AS group0\n                WHERE ( CONCAT(groupe.parentPath,'/',groupe.name,'/') LIKE CONCAT('/',group0.name,'/','%')\n\t\t\t\t\t\tOR groupe.id IN (:groupIds) )\n                AND group0.id IN (:groupIds)\n                )\n\t\t\t)\n\t</query>\n\n\t<query name=\"getProcessDefinitionIdByNameAndVersion\">\n\t\tSELECT process_definition.processId\n\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition\n\t\tWHERE process_definition.name = :name\n\t\tAND process_definition.version = :version\n\t</query>\n\n\t<query name=\"getProcessDefinitionDeployInfos\">\n\t\tSELECT process_definition\n\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition\n\t</query>\n\n\t<query name=\"getNumberOfProcessDefinitions\">\n\t\tSELECT COUNT(process_definition.id)\n\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition\n\t</query>\n\n\t<query name=\"getNumberOfProcessDefinitionsInActivationState\">\n\t\tSELECT COUNT(process_definition.id)\n\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition\n\t\tWHERE process_definition.activationState = :activationState\n\t</query>\n\n\t<query name=\"getProcessDefinitionsIds\">\n\t\tSELECT process_definition.processId\n\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition\n\t</query>\n\n\t<query name=\"getProcessDefinitionsIdsInActivationState\">\n\t\tSELECT process_definition.processId\n\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition\n\t\tWHERE process_definition.activationState = :activationState\n\t</query>\n\n\t<query name=\"getSubSetOfProcessDefinitionDeployInfos\">\n\t\tSELECT process_definition\n\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition\n\t\tWHERE process_definition.processId IN (:processIds)\n\t</query>\n\n\t<query name=\"searchSProcessDefinitionDeployInfoStartedBy\">\n\t\tSELECT process_definition\n\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition\n\t\tWHERE process_definition.activationState = 'ENABLED'\n\t\tAND (process_definition.processId IN (\n\t\t\t\tSELECT aprocessInstance.processDefinitionId\n\t\t        FROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS aprocessInstance\n\t\t        WHERE aprocessInstance.startedBy = :startedBy)\n\t        OR\n\t\t    process_definition.processId IN (\n\t\t        SELECT processInstance.processDefinitionId\n\t\t        FROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS processInstance\n\t\t        WHERE processInstance.startedBy = :startedBy)\n\t        )\n\t</query>\n\n\t<query name=\"getNumberOfSProcessDefinitionDeployInfoStartedBy\">\n\t\tSELECT COUNT(process_definition.id)\n\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition\n\t\tWHERE process_definition.activationState = 'ENABLED'\n\t\tAND (process_definition.processId IN (\n\t\t\t\tSELECT aprocessInstance.processDefinitionId\n\t\t        FROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS aprocessInstance\n\t\t        WHERE aprocessInstance.startedBy = :startedBy)\n\t        OR\n\t\t    process_definition.processId IN (\n\t\t        SELECT processInstance.processDefinitionId\n\t\t        FROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS processInstance\n\t\t        WHERE processInstance.startedBy = :startedBy)\n\t        )\n\t</query>\n\n\t<query name=\"searchSProcessDefinitionDeployInfo\">\n\t\tSELECT process_definition\n\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition\n\t</query>\n\n\t<query name=\"getNumberOfSProcessDefinitionDeployInfo\">\n\t\tSELECT COUNT(process_definition.id)\n\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition\n\t</query>\n\n\t<query name=\"getNumberOfSProcessDefinitionDeployInfoUncategorized\">\n\t\tSELECT COUNT(process_definition.id)\n\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition\n\t\tWHERE NOT EXISTS (\n\t\t\tSELECT categorymapping.id\n\t\t    FROM org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping\n\t\t    WHERE categorymapping.processId = process_definition.processId\n\t\t)\n\t</query>\n\n\t<query name=\"searchSProcessDefinitionDeployInfoUncategorized\">\n\t\tSELECT process_definition\n\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition\n\t\tWHERE NOT EXISTS (\n\t\t\tSELECT categorymapping.id\n\t\t\tFROM org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping\n\t\t\tWHERE categorymapping.processId = process_definition.processId\n\t\t)\n\t</query>\n\n\t<query name=\"getNumberOfSProcessDefinitionDeployInfoUnrelatedToCategory\">\n\t\tSELECT COUNT(process_definition.id)\n\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition\n\t\tWHERE process_definition.processId NOT IN (\n\t\t\t\tSELECT categorymapping.processId\n\t\t\t\tFROM org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping\n\t\t\t\tWHERE categorymapping.categoryId = :categoryId\n\t\t)\n\t</query>\n\n\t<query name=\"searchSProcessDefinitionDeployInfoUnrelatedToCategory\">\n\t\tSELECT process_definition\n\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition\n\t\tWHERE process_definition.processId NOT IN (\n\t\t\t\tSELECT categorymapping.processId\n\t\t\t\tFROM org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping\n\t\t\t\tWHERE categorymapping.categoryId = :categoryId\n\t\t)\n\t</query>\n\n\t<query name=\"getNumberOfSProcessDefinitionDeployInfoUncategorizedUserCanStart\">\n\t\tSELECT COUNT(process_definition.id)\n\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition\n\t\tWHERE NOT EXISTS (\n\t\t\tSELECT categorymapping.id\n\t\t\tFROM org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping\n\t\t\tWHERE categorymapping.processId = process_definition.processId\n\t\t)\n\t\tAND activationState = 'ENABLED'\n\t\tAND process_definition.processId IN (\n\t\t\tSELECT actor.scopeId\n\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor\n\t\t\tWHERE actor.initiator = TRUE\n\t\t\t\tAND actor.id IN (\n\t\t\t\tSELECT actor.id\n\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor,\n\t\t\t\torg.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n\t\t\t\tWHERE actor.id = actormember.actorId\n\t\t\t\tAND ( actormember.userId = :userId\n\t\t\t\t \t  OR actormember.id IN (\n\t\t\t\t\t\t\tSELECT actormember.id\n\t\t\t\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember, org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\t\t\t\tWHERE um.userId = :userId\n\t\t\t\t\t\t\tAND (\n\t\t\t\t\t\t\t\t(actormember.groupId = um.groupId AND actormember.roleId = -1)\n\t\t\t\t\t\t\t\tOR (actormember.roleId = um.roleId AND actormember.groupId = -1)\n\t\t\t\t\t\t\t\tOR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId))\n\t\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t)\n\n\t\t)\n\t</query>\n\n\t<query name=\"searchSProcessDefinitionDeployInfoUncategorizedUserCanStart\">\n\t\tSELECT process_definition\n\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition\n\t\tWHERE not exists (SELECT categorymapping.id\n\t\t                  FROM org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping\n\t\t                  WHERE categorymapping.processId = process_definition.processId)\n\t\tAND activationState = 'ENABLED'\n\t\tAND process_definition.processId IN (\n\t\t\tSELECT actor.scopeId\n\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor\n\t\t\tWHERE actor.initiator = TRUE\n\t\t\t\tAND actor.id IN (\n\t\t\t\tSELECT actor.id\n\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor,\n\t\t\t\torg.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n\t\t\t\tWHERE actor.id = actormember.actorId\n\t\t\t\tAND ( actormember.userId = :userId\n\t\t\t\t \t  OR actormember.id IN (\n\t\t\t\t\t\t\tSELECT actormember.id\n\t\t\t\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember, org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\t\t\t\tWHERE um.userId = :userId\n\t\t\t\t\t\t\tAND (\n\t\t\t\t\t\t\t\t(actormember.groupId = um.groupId AND actormember.roleId = -1)\n\t\t\t\t\t\t\t\tOR (actormember.roleId = um.roleId AND actormember.groupId = -1)\n\t\t\t\t\t\t\t\tOR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId))\n\t\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t)\n\n\t\t)\n\t</query>\n\n\t<query name=\"searchSProcessDefinitionDeployInfoUserCanStart\">\n\t\tSELECT process_definition\n\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition\n\t\tWHERE activationState = 'ENABLED'\n\t\tAND process_definition.processId IN (\n\t\t\tSELECT actor.scopeId\n\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor\n\t\t\tWHERE actor.initiator = TRUE\n\t\t\t\tAND actor.id IN (\n\t\t\t\tSELECT actor.id\n\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor,\n\t\t\t\torg.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n\t\t\t\tWHERE actor.id = actormember.actorId\n\t\t\t\tAND ( actormember.userId = :userId\n\t\t\t\t \t  OR actormember.id IN (\n\t\t\t\t\t\t\tSELECT actormember.id\n\t\t\t\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember, org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\t\t\t\tWHERE um.userId = :userId\n\t\t\t\t\t\t\tAND (\n\t\t\t\t\t\t\t\t(actormember.groupId = um.groupId AND actormember.roleId = -1)\n\t\t\t\t\t\t\t\tOR (actormember.roleId = um.roleId AND actormember.groupId = -1)\n\t\t\t\t\t\t\t\tOR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId))\n\t\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t)\n\n\t\t)\n\t</query>\n\n\t<query name=\"getNumberOfSProcessDefinitionDeployInfoUserCanStart\">\n\t\tSELECT COUNT(process_definition.id)\n\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition\n\t\tWHERE activationState = 'ENABLED'\n\t\tAND process_definition.processId IN (\n\t\t\tSELECT actor.scopeId\n\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor\n\t\t\tWHERE actor.initiator = TRUE\n\t\t\t\tAND actor.id IN (\n\t\t\t\tSELECT actor.id\n\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor,\n\t\t\t\torg.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n\t\t\t\tWHERE actor.id = actormember.actorId\n\t\t\t\tAND ( actormember.userId = :userId\n\t\t\t\t \t  OR actormember.id IN (\n\t\t\t\t\t\t\tSELECT actormember.id\n\t\t\t\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember, org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\t\t\t\tWHERE um.userId = :userId\n\t\t\t\t\t\t\tAND (\n\t\t\t\t\t\t\t\t(actormember.groupId = um.groupId AND actormember.roleId = -1)\n\t\t\t\t\t\t\t\tOR (actormember.roleId = um.roleId AND actormember.groupId = -1)\n\t\t\t\t\t\t\t\tOR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId))\n\t\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t)\n\t\t)\n\t</query>\n\n\t<query name=\"searchSProcessDefinitionDeployInfoUsersManagedByCanStart\">\n\t\tSELECT process_definition\n\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition\n\t\tWHERE activationState = 'ENABLED'\n\t\tAND process_definition.processId IN (\n\t\t\tSELECT actor.scopeId\n\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor\n\t\t\tWHERE actor.initiator = TRUE\n\t\t\tAND EXISTS (\n\t\t\t\tSELECT actor.id\n\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n\t\t\t\tWHERE actor.id = actormember.actorId\n\t\t\t\tAND (\n\t\t\t\t\tactormember.userId = :managerUserId\n\t\t\t\t\tOR EXISTS (\n\t\t\t\t\t\tSELECT actormember.userId\n\t\t\t\t\t\tFROM org.bonitasoft.engine.identity.model.SUser AS user\n\t\t\t\t\t\tWHERE actormember.userId = user.id\n\t\t\t\t\t\tAND user.managerUserId = :managerUserId\n\t\t\t\t\t)\n\t\t\t\t\tOR EXISTS (\n\t\t\t\t\t\tSELECT actormember.id\n\t\t\t\t\t\tFROM org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\t\t\tWHERE (\n\t\t\t\t\t\t\tum.userId = :managerUserId\n\t\t\t\t\t\t\tOR EXISTS (\n\t\t\t\t\t\t\t\tSELECT user.id\n\t\t\t\t\t\t\t\tFROM org.bonitasoft.engine.identity.model.SUser AS user\n\t\t\t\t\t\t\t\tWHERE um.userId = user.id\n\t\t\t\t\t\t\t\tAND user.managerUserId = :managerUserId\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t)\n\t\t\t\t\t\tAND (\n\t\t\t\t\t\t\t(actormember.groupId = um.groupId AND actormember.roleId = -1)\n\t\t\t\t\t\t\tOR (actormember.roleId = um.roleId AND actormember.groupId = -1)\n\t\t\t\t\t\t\tOR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId)\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t</query>\n\n\t<query name=\"getNumberOfSProcessDefinitionDeployInfoUsersManagedByCanStart\">\n\t\tSELECT COUNT(process_definition.id)\n\t\t\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition\n\t\tWHERE activationState = 'ENABLED'\n\t\tAND process_definition.processId IN (\n\t\t\tSELECT actor.scopeId\n\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor\n\t\t\tWHERE actor.initiator = TRUE\n\t\t\tAND EXISTS (\n\t\t\t\tSELECT actor.id\n\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n\t\t\t\tWHERE actor.id = actormember.actorId\n\t\t\t\tAND (\n\t\t\t\t\tactormember.userId = :managerUserId\n\t\t\t\t\tOR EXISTS (\n\t\t\t\t\t\tSELECT actormember.userId\n\t\t\t\t\t\tFROM org.bonitasoft.engine.identity.model.SUser AS user\n\t\t\t\t\t\tWHERE actormember.userId = user.id\n\t\t\t\t\t\tAND user.managerUserId = :managerUserId\n\t\t\t\t\t)\n\t\t\t\t\tOR EXISTS (\n\t\t\t\t\t\tSELECT actormember.id\n\t\t\t\t\t\tFROM org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\t\t\tWHERE (\n\t\t\t\t\t\t\tum.userId = :managerUserId\n\t\t\t\t\t\t\tOR EXISTS (\n\t\t\t\t\t\t\t\tSELECT user.id\n\t\t\t\t\t\t\t\tFROM org.bonitasoft.engine.identity.model.SUser AS user\n\t\t\t\t\t\t\t\tWHERE um.userId = user.id\n\t\t\t\t\t\t\t\tAND user.managerUserId = :managerUserId\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t)\n\t\t\t\t\t\tAND (\n\t\t\t\t\t\t\t(actormember.groupId = um.groupId AND actormember.roleId = -1)\n\t\t\t\t\t\t\tOR (actormember.roleId = um.roleId AND actormember.groupId = -1)\n\t\t\t\t\t\t\tOR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId)\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t</query>\n\n\t<query name=\"getNumberOfSProcessDefinitionDeployInfowithSProcessCategoryMappingUserCanStart\">\n\t\tSELECT COUNT(process_definition.id)\n\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition,\n\t\t\t org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping\n\t\tWHERE activationState = 'ENABLED'\n\t\tAND categorymapping.processId = process_definition.processId\n\t\tAND process_definition.processId IN (\n\t\t\tSELECT actor.scopeId\n\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor\n\t\t\tWHERE actor.initiator = TRUE\n\t\t\tAND actor.id IN (\n\t\t\t\tSELECT actor.id\n\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor,\n\t\t\t\t\t org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n\t\t\t\tWHERE actor.id = actormember.actorId\n\t\t\t\tAND ( actormember.userId = :userId\n\t\t\t\t\tOR actormember.id IN (\n\t\t\t\t\t\tSELECT actormember.id\n\t\t\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember, org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\t\t\tWHERE um.userId = :userId\n\t\t\t\t\t\tAND (\n\t\t\t\t\t\t\t(actormember.groupId = um.groupId AND actormember.roleId = -1)\n\t\t\t\t\t\t\tOR (actormember.roleId = um.roleId AND actormember.groupId = -1)\n\t\t\t\t\t\t\tOR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId)\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t</query>\n\n\t<query name=\"searchSProcessDefinitionDeployInfowithSProcessCategoryMappingUserCanStart\">\n\t\tSELECT process_definition\n\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition,\n\t\t\t org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping\n\t\tWHERE activationState = 'ENABLED'\n\t\tAND categorymapping.processId = process_definition.processId\n\t\tAND process_definition.processId IN (\n\t\t\tSELECT actor.scopeId\n\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor\n\t\t\tWHERE actor.initiator = TRUE\n\t\t\tAND actor.id IN (\n\t\t\t\tSELECT actor.id\n\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor,\n\t\t\t\t\t org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n\t\t\t\tWHERE actor.id = actormember.actorId\n\t\t\t\tAND ( actormember.userId = :userId\n\t\t\t\t\tOR actormember.id IN (\n\t\t\t\t\t \tSELECT actormember.id\n\t\t\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember, org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\t\t\tWHERE um.userId = :userId\n\t\t\t\t\t\tAND (\n\t\t\t\t\t\t\t(actormember.groupId = um.groupId AND actormember.roleId = -1)\n\t\t\t\t\t\t\tOR (actormember.roleId = um.roleId AND actormember.groupId = -1)\n\t\t\t\t\t\t\tOR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId)\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t</query>\n\n\t<query name=\"getNumberOfSProcessDefinitionDeployInfowithSProcessCategoryMappingUserSupervised\">\n\t\tSELECT COUNT(process_definition.id)\n\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition,\n\t\t\t org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping\n\t\tWHERE categorymapping.processId = process_definition.processId\n\t\tAND process_definition.processId IN (\n\t\t\tSELECT supervisor.processDefId\n\t\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor\n\t\t\tWHERE supervisor.userId = :userId\n\t\t\tOR (\n\t\t\t\tsupervisor.id IN (\n\t\t\t\t\tSELECT supervisor.id\n\t\t\t\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\t\tWHERE um.userId = :userId\n\t\t\t\t\tAND (\n\t\t\t\t\t\t(supervisor.groupId = um.groupId AND supervisor.roleId &lt;= 0)\n\t\t\t\t\t\tOR (supervisor.roleId = um.roleId AND supervisor.groupId &lt;= 0)\n\t\t\t\t\t\tOR (supervisor.groupId = um.groupId AND supervisor.roleId = um.roleId)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t</query>\n\n\t<query name=\"searchSProcessDefinitionDeployInfowithSProcessCategoryMappingUserSupervised\">\n\t\tSELECT process_definition\n\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition,\n\t\t\t org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping\n\t\tWHERE categorymapping.processId = process_definition.processId\n\t\tAND process_definition.processId IN (\n\t\t\tSELECT supervisor.processDefId\n\t\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor\n\t\t\tWHERE supervisor.userId = :userId\n\t\t\tOR (\n\t\t\t\tsupervisor.id IN (\n\t\t\t\t\tSELECT supervisor.id\n\t\t\t\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\t\tWHERE um.userId = :userId\n\t\t\t\t\tAND (\n\t\t\t\t\t\t(supervisor.groupId = um.groupId AND supervisor.roleId &lt;= 0)\n\t\t\t\t\t\tOR (supervisor.roleId = um.roleId AND supervisor.groupId &lt;= 0)\n\t\t\t\t\t\tOR (supervisor.groupId = um.groupId AND supervisor.roleId = um.roleId)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t</query>\n\n\t<query name=\"getNumberOfSProcessDefinitionDeployInfoUserSupervised\">\n\t\tSELECT COUNT(process_definition.id)\n\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition\n\t\tWHERE process_definition.processId IN (\n\t\t\tSELECT supervisor.processDefId\n\t\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor\n\t\t\tWHERE supervisor.userId = :userId\n\t\t\tOR (\n\t\t\t\tsupervisor.id IN (\n\t\t\t\t\tSELECT supervisor.id\n\t\t\t\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\t\tWHERE um.userId = :userId\n\t\t\t\t\tAND (\n\t\t\t\t\t\t(supervisor.groupId = um.groupId AND supervisor.roleId &lt;= 0)\n\t\t\t\t\t\tOR (supervisor.roleId = um.roleId AND supervisor.groupId &lt;= 0)\n\t\t\t\t\t\tOR (supervisor.groupId = um.groupId AND supervisor.roleId = um.roleId)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t</query>\n\n\t<query name=\"searchSProcessDefinitionDeployInfoUserSupervised\">\n\t\tSELECT process_definition\n\t\t\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition\n\t\tWHERE process_definition.processId IN (\n\t\t\tSELECT supervisor.processDefId\n\t\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor\n\t\t\tWHERE supervisor.userId = :userId\n\t\t\tOR (\n\t\t\t\tsupervisor.id IN (\n\t\t\t\t\tSELECT supervisor.id\n\t\t\t\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\t\tWHERE um.userId = :userId\n\t\t\t\t\tAND (\n\t\t\t\t\t\t(supervisor.groupId = um.groupId AND supervisor.roleId &lt;= 0)\n\t\t\t\t\t\tOR (supervisor.roleId = um.roleId AND supervisor.groupId &lt;= 0)\n\t\t\t\t\t\tOR (supervisor.groupId = um.groupId AND supervisor.roleId = um.roleId)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t</query>\n\n\t<query name=\"getNumberOfSProcessDefinitionDeployInfoUncategorizedAndWithSupervisor\">\n\t\tSELECT COUNT(process_definition.id)\n\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition\n\t\tWHERE not exists (SELECT categorymapping.id\n\t\t                  FROM org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping\n\t\t                  WHERE categorymapping.processId = process_definition.processId)\n\t\tAND process_definition.processId IN (\n\t\t\tSELECT supervisor.processDefId\n\t\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor\n\t\t\tWHERE supervisor.userId = :userId\n\t\t\tOR (\n\t\t\t\tsupervisor.id IN (\n\t\t\t\t\tSELECT supervisor.id\n\t\t\t\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\t\tWHERE um.userId = :userId\n\t\t\t\t\tAND (\n\t\t\t\t\t\t(supervisor.groupId = um.groupId AND supervisor.roleId &lt;= 0)\n\t\t\t\t\t\tOR (supervisor.roleId = um.roleId AND supervisor.groupId &lt;= 0)\n\t\t\t\t\t\tOR (supervisor.groupId = um.groupId AND supervisor.roleId = um.roleId)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t</query>\n\n\t<query name=\"searchSProcessDefinitionDeployInfoUncategorizedAndWithSupervisor\">\n\t\tSELECT process_definition\n\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition\n\t\tWHERE not exists (SELECT categorymapping.id\n\t\t                  FROM org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping\n\t\t                  WHERE categorymapping.processId = process_definition.processId)\n\t\tAND process_definition.processId IN (\n\t\t\tSELECT supervisor.processDefId\n\t\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor\n\t\t\tWHERE supervisor.userId = :userId\n\t\t\tOR (\n\t\t\t\tsupervisor.id IN (\n\t\t\t\t\tSELECT supervisor.id\n\t\t\t\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\t\tWHERE um.userId = :userId\n\t\t\t\t\tAND (\n\t\t\t\t\t\t(supervisor.groupId = um.groupId AND supervisor.roleId &lt;= 0)\n\t\t\t\t\t\tOR (supervisor.roleId = um.roleId AND supervisor.groupId &lt;= 0)\n\t\t\t\t\t\tOR (supervisor.groupId = um.groupId AND supervisor.roleId = um.roleId)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t</query>\n\n\t<query name=\"getProcessDeploymentInfoFromProcessInstanceIds\">\n\t\tSELECT new map(p.id AS processInstanceId, process_definition.id AS id,process_definition.processId AS processId,process_definition.name AS name,process_definition.version AS version, process_definition.description AS description,process_definition.deploymentDate AS deploymentDate, process_definition.deployedBy AS deployedBy,\n\t\tprocess_definition.activationState AS activationState, process_definition.configurationState AS configurationState, process_definition.displayName AS displayName,process_definition.lastUpdateDate AS lastUpdateDate, process_definition.iconPath AS iconPath, process_definition.displayDescription AS displayDescription)\n\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition,\n\t\t\t org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p\n\t\tWHERE process_definition.processId = p.processDefinitionId\n\t\tAND p.id IN (:processInstanceIds)\n\t</query>\n\n\t<query name=\"getProcessDeploymentInfoFromArchivedProcessInstanceIds\">\n\t\tSELECT new map(ap.id AS archivedProcessInstanceId, process_definition.id AS id,process_definition.processId AS processId,process_definition.name AS name,process_definition.version AS version, process_definition.description AS description,process_definition.deploymentDate AS deploymentDate, process_definition.deployedBy AS deployedBy,\n\t\tprocess_definition.activationState AS activationState, process_definition.configurationState AS configurationState, process_definition.displayName AS displayName,process_definition.lastUpdateDate AS lastUpdateDate, process_definition.iconPath AS iconPath, process_definition.displayDescription AS displayDescription)\n\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition,\n\t\t\t org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap\n\t\tWHERE process_definition.processId = ap.processDefinitionId\n\t\tAND ap.id IN (:archivedProcessInstanceIds)\n\t</query>\n\n\t<query name=\"searchSProcessDefinitionDeployInfowithSProcessCategoryMapping\">\n\t\tSELECT process_definition\n\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition,\n\t\t\t org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping\n\t\tWHERE categorymapping.processId = process_definition.processId\n\t</query>\n\n\t<query name=\"getNumberOfSProcessDefinitionDeployInfowithSProcessCategoryMapping\">\n\t\tSELECT COUNT(process_definition.id)\n\t\tFROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition,\n\t\t\t org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping\n\t\tWHERE categorymapping.processId = process_definition.processId\n\t</query>\n\n    <query name=\"searchSProcessDeploymentInfosOfCategory\">\n        SELECT process_definition\n        FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition,\n             org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping\n        WHERE categorymapping.processId = process_definition.processId\n        AND categorymapping.categoryId = :categoryId\n    </query>\n\n    <sql-query name=\"getNumberOfSUserWhoCanStartProcess\">\n        <return-scalar column=\"count\" type=\"long\" />\n        SELECT count(id) as count FROM (\n\t\t\tSELECT DISTINCT user_.*\n\t\t\tFROM actormember\n\t\t\tJOIN actor ON (\n\t\t\t\tactor.id = actormember.actorid\n\t\t\t\tand actor.scopeid = :processId\n\t\t\t\tand actor.initiator = :trueValue\n\t\t\t) JOIN user_membership ON (\n\t\t\t\t (\n                 (actormember.groupId = user_membership.groupId and actormember.roleId = -1)\n\t\t\t\t\tor\n                 (actormember.roleId = user_membership.roleId and actormember.groupId = -1)\n\t\t\t\t\tor\n\t\t\t\t\t(actormember.groupId = user_membership.groupId and actormember.roleId = user_membership.roleId)\n\t\t\t\t)\n\t\t\t) JOIN user_ ON (\n\t\t\t\tuser_membership.userid = user_.id\n\t\t\t\tand user_.enabled = :trueValue\n\t\t\t)\n\t\t\tUNION\n\t\t\tSELECT DISTINCT user_.*\n\t\t\tFROM actormember\n\t\t\tJOIN actor ON (\n\t\t\t\tactor.id = actormember.actorid\n\t\t\t\tand actor.scopeid = :processId\n\t\t\t\tand actor.initiator = :trueValue\n\t\t\t) JOIN user_ ON (\n\t\t\t\tactormember.userid = user_.id\n\t\t\t\tand user_.enabled = :trueValue\n\t\t\t)\n\t\t) user_\n    </sql-query>\n\n    <sql-query name=\"searchSUserWhoCanStartProcess\">\n        <return alias=\"user_\" class=\"org.bonitasoft.engine.identity.model.SUser\" />\n\t\tSELECT user_.* FROM (\n\t\t\tSELECT DISTINCT user_.*\n\t\t\tFROM actormember\n\t\t\tJOIN actor ON (\n\t\t\t\tactor.id = actormember.actorid\n\t\t\t\tand actor.scopeid = :processId\n\t\t\t\tand actor.initiator = :trueValue\n\t\t\t) JOIN user_membership ON (\n\t\t\t\t(\n                 (actormember.groupId = user_membership.groupId and actormember.roleId = -1)\n\t\t\t\t\t  or\n                 (actormember.roleId = user_membership.roleId and actormember.groupId = -1)\n\t\t\t\t\t  or\n\t\t\t\t\t (actormember.groupId = user_membership.groupId and actormember.roleId = user_membership.roleId)\n\t\t\t\t)\n\t\t\t) JOIN user_ ON (\n\t\t\t\tuser_membership.userid = user_.id\n\t\t\t\tand user_.enabled = :trueValue\n\t\t\t)\n\t\t\tUNION\n\t\t\tSELECT DISTINCT user_.*\n\t\t\tFROM actormember\n\t\t\tJOIN actor ON (\n\t\t\t\tactor.id = actormember.actorid\n\t\t\t\tand actor.scopeid = :processId\n\t\t\t\tand actor.initiator = :trueValue\n\t\t\t) JOIN user_ ON (\n\t\t\t\tactormember.userid = user_.id\n\t\t\t\tand user_.enabled = :trueValue\n\t\t\t)\n\t\t) user_\n    </sql-query>\n\n    <query name=\"getNumberOfSProcessDefinitionDeployInfoWithAssignedOrPendingHumanTasksFor\">\n        SELECT COUNT(DISTINCT process_definition.id)\n        FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition,\n        \t org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p,\n        \t org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a\n        WHERE process_definition.processId = p.processDefinitionId\n        AND p.id = a.logicalGroup2\n        AND a.stable = TRUE\n\t\tAND a.stateExecuting = FALSE\n\t\tAND a.terminal = FALSE\n\t\tAND (\n\t\t\ta.assigneeId = :userId\n\t\t\tOR (\n\t\t\t\ta.assigneeId = 0\n\t\t\t\tAND EXISTS (\n\t\t\t\t\tSELECT mapping.id\n\t\t\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping\n\t\t\t\t\tWHERE mapping.activityId = a.id\n\t\t\t\t\tAND (\n\t\t\t\t\t\tmapping.userId = :userId\n\t\t\t\t\t\tOR EXISTS (\n\t\t\t\t\t\t\tSELECT actor.id\n\t\t\t\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor,\n\t\t\t\t\t\t\t\t org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n\t\t\t\t\t\t\tWHERE mapping.actorId = actor.id\n\t\t\t\t\t\t\tAND  actor.id = actormember.actorId\n\t\t\t\t\t\t\tAND (\n\t\t\t\t\t\t\t\tactormember.userId = :userId\n\t\t \t  \t\t\t\t\tOR EXISTS (\n\t\t\t\t\t\t\t\t\tSELECT um.id\n\t\t\t\t\t\t\t\t\tFROM org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\t\t\t\t\t\tWHERE um.userId = :userId\n\t\t\t\t\t\t\t\t\tAND (\n\t\t\t\t\t\t\t\t\t\t(actormember.groupId = um.groupId AND actormember.roleId = -1)\n\t\t\t\t\t\t\t\t\t\tOR (actormember.roleId = um.roleId AND actormember.groupId = -1)\n\t\t\t\t\t\t\t\t\t\tOR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId)\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t)\n\t\t     \t\t\t\t)\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n    </query>\n\n\t<query name=\"searchSProcessDefinitionDeployInfoWithAssignedOrPendingHumanTasksFor\">\n        SELECT DISTINCT(process_definition)\n        FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition,\n        \t org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p,\n        \t org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a\n        WHERE process_definition.processId = p.processDefinitionId\n        AND p.id = a.logicalGroup2\n        AND a.stable = TRUE\n\t\tAND a.stateExecuting = FALSE\n\t\tAND a.terminal = FALSE\n\t\tAND (\n\t\t\ta.assigneeId = :userId\n\t\t\tOR (\n\t\t\t\ta.assigneeId = 0\n\t\t\t\tAND EXISTS (\n\t\t\t\t\tSELECT mapping.id\n\t\t\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping\n\t\t\t\t\tWHERE mapping.activityId = a.id\n\t\t\t\t\tAND (\n\t\t\t\t\t\tmapping.userId = :userId\n\t\t\t\t\t\tOR EXISTS (\n\t\t\t\t\t\t\tSELECT actor.id\n\t\t\t\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor,\n\t\t\t\t\t\t\t\t org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n\t\t\t\t\t\t\tWHERE mapping.actorId = actor.id\n\t\t\t\t\t\t\tAND  actor.id = actormember.actorId\n\t\t\t\t\t\t\tAND (\n\t\t\t\t\t\t\t\tactormember.userId = :userId\n\t\t \t  \t\t\t\t\tOR EXISTS (\n\t\t\t\t\t\t\t\t\tSELECT um.id\n\t\t\t\t\t\t\t\t\tFROM org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\t\t\t\t\t\tWHERE um.userId = :userId\n\t\t\t\t\t\t\t\t\tAND (\n\t\t\t\t\t\t\t\t\t\t(actormember.groupId = um.groupId AND actormember.roleId = -1)\n\t\t\t\t\t\t\t\t\t\tOR (actormember.roleId = um.roleId AND actormember.groupId = -1)\n\t\t\t\t\t\t\t\t\t\tOR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId)\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t)\n\t\t     \t\t\t\t)\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n    </query>\n\n\t<query name=\"getNumberOfSProcessDefinitionDeployInfoWithAssignedOrPendingHumanTasksSupervisedBy\">\n        SELECT COUNT(DISTINCT process_definition.id)\n        FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition,\n        \t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor,\n        \t org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p,\n        \t org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a,\n        \t org.bonitasoft.engine.identity.model.SUser as user\n        WHERE process_definition.processId = supervisor.processDefId\n\t\tAND (supervisor.userId = :userId\n\t\t\tOR EXISTS (\n\t\t\t\tSELECT supervisor.id\n\t\t\t\tFROM org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\tWHERE um.userId = :userId\n\t\t\t\tAND (\n\t\t\t\t\t(supervisor.groupId = um.groupId AND supervisor.roleId &lt;= 0)\n\t\t\t\t\tOR (supervisor.roleId = um.roleId AND supervisor.groupId &lt;= 0)\n\t\t\t\t\tOR (supervisor.groupId = um.groupId AND supervisor.roleId = um.roleId)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n        AND process_definition.processId = p.processDefinitionId\n        AND p.id = a.logicalGroup2\n        AND a.stable = TRUE\n\t\tAND a.stateExecuting = FALSE\n\t\tAND a.terminal = FALSE\n\t\tAND (\n\t\t\ta.assigneeId = user.id\n\t\t\tOR (\n\t\t\t\ta.assigneeId = 0\n\t\t\t\tAND EXISTS (\n\t\t\t\t\tSELECT mapping.id\n\t\t\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping\n\t\t\t\t\tWHERE mapping.activityId = a.id\n\t\t\t\t\tAND (\n\t\t\t\t\t\tmapping.userId = user.id\n\t\t\t\t\t\tOR EXISTS (\n\t\t\t\t\t\t\tSELECT actor.id\n\t\t\t\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor,\n\t\t\t\t\t\t\t\t org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n\t\t\t\t\t\t\tWHERE mapping.actorId = actor.id\n\t\t\t\t\t\t\tAND  actor.id = actormember.actorId\n\t\t\t\t\t\t\tAND (\n\t\t\t\t\t\t\t\tactormember.userId = user.id\n\t\t\t \t  \t\t\t\tOR EXISTS (\n\t\t\t\t\t\t\t\t\tSELECT um.id\n\t\t\t\t\t\t\t\t\tFROM org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\t\t\t\t\t\tWHERE um.userId = user.id\n\t\t\t\t\t\t\t\t\tAND (\n\t\t\t\t\t\t\t\t\t\t(actormember.groupId = um.groupId AND actormember.roleId = -1)\n\t\t\t\t\t\t\t\t\t\tOR (actormember.roleId = um.roleId AND actormember.groupId = -1)\n\t\t\t\t\t\t\t\t\t\tOR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId)\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t)\n\t\t\t     \t\t\t)\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n    </query>\n\n\t<query name=\"searchSProcessDefinitionDeployInfoWithAssignedOrPendingHumanTasksSupervisedBy\">\n        SELECT DISTINCT(process_definition)\n        FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition,\n        \t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor,\n        \t org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p,\n        \t org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a,\n        \t org.bonitasoft.engine.identity.model.SUser as user\n        WHERE process_definition.processId = supervisor.processDefId\n\t\tAND (supervisor.userId = :userId\n\t\t\tOR EXISTS (\n\t\t\t\tSELECT supervisor.id\n\t\t\t\tFROM org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\tWHERE um.userId = :userId\n\t\t\t\tAND (\n\t\t\t\t\t(supervisor.groupId = um.groupId AND supervisor.roleId &lt;= 0)\n\t\t\t\t\tOR (supervisor.roleId = um.roleId AND supervisor.groupId &lt;= 0)\n\t\t\t\t\tOR (supervisor.groupId = um.groupId AND supervisor.roleId = um.roleId)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n        AND process_definition.processId = p.processDefinitionId\n        AND p.id = a.logicalGroup2\n        AND a.stable = TRUE\n\t\tAND a.stateExecuting = FALSE\n\t\tAND a.terminal = FALSE\n\t\tAND (\n\t\t\ta.assigneeId = user.id\n\t\t\tOR (\n\t\t\t\ta.assigneeId = 0\n\t\t\t\tAND EXISTS (\n\t\t\t\t\tSELECT mapping.id\n\t\t\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping\n\t\t\t\t\tWHERE mapping.activityId = a.id\n\t\t\t\t\tAND (\n\t\t\t\t\t\tmapping.userId = user.id\n\t\t\t\t\t\tOR EXISTS (\n\t\t\t\t\t\t\tSELECT actor.id\n\t\t\t\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor,\n\t\t\t\t\t\t\t\t org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n\t\t\t\t\t\t\tWHERE mapping.actorId = actor.id\n\t\t\t\t\t\t\tAND  actor.id = actormember.actorId\n\t\t\t\t\t\t\tAND (\n\t\t\t\t\t\t\t\tactormember.userId = user.id\n\t\t\t \t  \t\t\t\tOR EXISTS (\n\t\t\t\t\t\t\t\t\tSELECT um.id\n\t\t\t\t\t\t\t\t\tFROM org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\t\t\t\t\t\tWHERE um.userId = user.id\n\t\t\t\t\t\t\t\t\tAND (\n\t\t\t\t\t\t\t\t\t\t(actormember.groupId = um.groupId AND actormember.roleId = -1)\n\t\t\t\t\t\t\t\t\t\tOR (actormember.roleId = um.roleId AND actormember.groupId = -1)\n\t\t\t\t\t\t\t\t\t\tOR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId)\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t)\n\t\t\t     \t\t\t)\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n    </query>\n\n \t<query name=\"getNumberOfSProcessDefinitionDeployInfoWithAssignedOrPendingHumanTasks\">\n        SELECT COUNT(DISTINCT process_definition.id)\n        FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition,\n        \t org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a\n\t\tWHERE process_definition.processId = a.logicalGroup1\n        AND a.stable = TRUE\n\t\tAND a.stateExecuting = FALSE\n\t\tAND a.terminal = FALSE\n\t\tAND a.stateId = 4\n    </query>\n\n\t<query name=\"searchSProcessDefinitionDeployInfoWithAssignedOrPendingHumanTasks\">\n        SELECT DISTINCT(process_definition)\n        FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition,\n        \t org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a\n        WHERE process_definition.processId = a.logicalGroup1\n        AND a.stable = TRUE\n\t\tAND a.stateExecuting = FALSE\n\t\tAND a.terminal = FALSE\n\t\tAND a.stateId = 4\n    </query>\n</hibernate-mapping>\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/expression/control/api/impl/ExpressionResolverServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.expression.control.api.impl;\n\nimport static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\n\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.dependency.model.ScopeType;\nimport org.bonitasoft.engine.expression.ExpressionService;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.tracking.TimeTracker;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ExpressionResolverServiceImplTest {\n\n    @Mock\n    private ExpressionService expressionService;\n\n    @Mock\n    private ProcessDefinitionService processDefinitionService;\n\n    @Mock\n    private ClassLoaderService classLoaderService;\n\n    @Mock\n    private TimeTracker timeTracker;\n\n    @InjectMocks\n    private ExpressionResolverServiceImpl resolverService;\n\n    @Mock\n    private SExpression expression;\n\n    @Test\n    public void evaluate_should_load_class_loader_of_process_definition() throws Exception {\n        final long processDefinitionId = 83L;\n        final SExpressionContext context = new SExpressionContext(45L, \"PROCESS\", processDefinitionId);\n\n        resolverService.evaluate(expression, context);\n\n        verify(classLoaderService).getClassLoader(identifier(ScopeType.PROCESS, processDefinitionId));\n    }\n\n    @Test\n    public void evaluate_should_load_class_loader_of_parent_process_definition() throws Exception {\n        final long parentProcessDefinitionId = 19L;\n        final long processDefinitionId = 83L;\n        final SExpressionContext context = new SExpressionContext(45L, \"PROCESS\", processDefinitionId);\n        context.setParentProcessDefinitionId(parentProcessDefinitionId);\n\n        resolverService.evaluate(expression, context);\n\n        verify(classLoaderService)\n                .getClassLoader(identifier(ScopeType.PROCESS, parentProcessDefinitionId));\n    }\n\n    @Test\n    public void evaluate_should_not_load_class_loader_when_no_definition_is_defined() throws Exception {\n        final SExpressionContext context = new SExpressionContext();\n\n        resolverService.evaluate(expression, context);\n\n        verify(classLoaderService, never()).getClassLoader(any());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/expression/control/model/SExpressionContextTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.expression.control.model;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.mock;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class SExpressionContextTest {\n\n    @Test\n    public void should_toString_return_a_human_readable_context() {\n        // given\n        final SExpressionContext expressionContext = new SExpressionContext(123L, \"typeOfTheContainer\", 456L);\n        final SProcessDefinition processDefinition = mock(SProcessDefinition.class);\n        doReturn(\"ProcessName\").when(processDefinition).getName();\n        doReturn(\"1.0\").when(processDefinition).getVersion();\n        doReturn(mock(SFlowElementContainerDefinition.class)).when(processDefinition).getProcessContainer();\n        expressionContext.setProcessDefinition(processDefinition);\n\n        // when\n        final String string = expressionContext.toString();\n        // then\n        assertThat(string).isEqualTo(\n                \"context [containerId=123, containerType=typeOfTheContainer, processDefinitionId=456, processDefinition=ProcessName -- 1.0]\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/operation/impl/AssignmentOperationExecutorStrategyTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.util.Collections;\n\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.operation.model.SLeftOperand;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.core.operation.model.SOperatorType;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceService;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class AssignmentOperationExecutorStrategyTest {\n\n    @Mock\n    private DataInstanceService dataInstanceService;\n\n    @InjectMocks\n    private AssignmentOperationExecutorStrategy assignmentOperationExecutorStrategy;\n\n    private SLeftOperand leftOperand;\n\n    private SOperation operation;\n\n    private String value;\n\n    private SExpressionContext expressionContext;\n\n    @Before\n    public void initMocks() {\n        operation = mock(SOperation.class);\n        value = \"value\";\n        expressionContext = mock(SExpressionContext.class);\n        leftOperand = mock(SLeftOperand.class);\n        when(operation.getLeftOperand()).thenReturn(leftOperand);\n        when(leftOperand.getName()).thenReturn(\"var\");\n    }\n\n    @Test\n    public void testGetValue() throws Exception {\n        when(expressionContext.getInputValues()).thenReturn(Collections.<String, Object> singletonMap(\"var\", \"value\"));\n        when(leftOperand.getType()).thenReturn(SLeftOperand.TYPE_DATA);\n        Object returnedValue = assignmentOperationExecutorStrategy.computeNewValueForLeftOperand(operation, value,\n                expressionContext, false);\n        assertThat(returnedValue).isEqualTo(\"value\");\n    }\n\n    @Test\n    public void testGetValueOnExternalData() throws Exception {\n        // return type is not compatible\n        when(leftOperand.getType()).thenReturn(SLeftOperand.TYPE_EXTERNAL_DATA);\n        Object returnedValue = assignmentOperationExecutorStrategy.computeNewValueForLeftOperand(operation, value,\n                expressionContext, false);\n        assertThat(returnedValue).isEqualTo(\"value\");\n    }\n\n    @Test(expected = SOperationExecutionException.class)\n    public void testGetValueWithWrongType() throws Exception {\n        // return type is not compatible\n        when(expressionContext.getInputValues())\n                .thenReturn(Collections.<String, Object> singletonMap(\"var\", new java.util.TreeMap<String, Object>()));\n        when(leftOperand.getType()).thenReturn(SLeftOperand.TYPE_DATA);\n        assignmentOperationExecutorStrategy.computeNewValueForLeftOperand(operation, value, expressionContext, false);\n    }\n\n    @Test\n    public void operationType_should_be_ASSIGNMENT() throws Exception {\n        assertThat(assignmentOperationExecutorStrategy.getOperationType()).isEqualTo(SOperatorType.ASSIGNMENT.name());\n    }\n\n    @Test\n    public void should_not_persist_on_null() throws Exception {\n        assertThat(assignmentOperationExecutorStrategy.shouldPersistOnNullValue()).isFalse();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/operation/impl/ExternalDataLeftOperandHandlerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation.impl;\n\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.operation.model.impl.SLeftOperandImpl;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class ExternalDataLeftOperandHandlerTest {\n\n    private ExternalDataLeftOperandHandler handler;\n\n    @Before\n    public void setUp() {\n        handler = new ExternalDataLeftOperandHandler();\n    }\n\n    private SLeftOperandImpl createLeftOperand(final String name) {\n        final SLeftOperandImpl sLeftOperand = new SLeftOperandImpl();\n        sLeftOperand.setName(name);\n        return sLeftOperand;\n    }\n\n    @Test(expected = SOperationExecutionException.class)\n    public void deleteThrowsAnExceptionNotYetSupported() throws Exception {\n        handler.delete(createLeftOperand(\"myData\"), 45l, \"container\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/operation/impl/JavaMethodOperationExecutorStrategyTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.operation.model.SLeftOperand;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class JavaMethodOperationExecutorStrategyTest {\n\n    private JavaMethodOperationExecutorStrategy strategy;\n\n    @Before\n    public void setUp() throws Exception {\n        strategy = new JavaMethodOperationExecutorStrategy() {\n\n        };\n    }\n\n    @Test(expected = SOperationExecutionException.class)\n    public void dontThrowNPEIfObjectDoesNotExist() throws Exception {\n        final SOperation operation = mock(SOperation.class);\n        final SLeftOperand leftOperand = mock(SLeftOperand.class);\n        final SExpression rightOperand = mock(SExpression.class);\n\n        when(operation.getLeftOperand()).thenReturn(leftOperand);\n        when(leftOperand.getName()).thenReturn(\"unknownData\");\n\n        final SExpressionContext expressionContext = new SExpressionContext(123L,\n                DataInstanceContainer.PROCESS_INSTANCE.name(), 1234L);\n        expressionContext.setInputValues(Collections.<String, Object> emptyMap());\n        strategy.computeNewValueForLeftOperand(operation, \"Update\", expressionContext, false);\n    }\n\n    @Test(expected = SOperationExecutionException.class)\n    public void shouldThrowExceptionWhenInvokeJavaMethodFails() throws Exception {\n        final SOperation operation = mock(SOperation.class);\n        final SLeftOperand leftOperand = mock(SLeftOperand.class);\n        final SExpression rightOperand = mock(SExpression.class);\n\n        when(operation.getLeftOperand()).thenReturn(leftOperand);\n        when(leftOperand.getName()).thenReturn(\"myData\");\n        when(operation.getRightOperand()).thenReturn(rightOperand);\n        when(operation.getOperator()).thenReturn(\"setThing:int\");\n        when(rightOperand.getReturnType()).thenReturn(Object.class.getName());\n\n        final SExpressionContext expressionContext = new SExpressionContext(123L,\n                DataInstanceContainer.PROCESS_INSTANCE.name(), 1234L);\n        final Map<String, Object> map = new HashMap<>();\n        map.put(\"myData\", new MyClassThatThrowException());\n        expressionContext.setInputValues(map);\n\n        strategy.computeNewValueForLeftOperand(operation, \"Update\", expressionContext, false);\n    }\n\n    @Test\n    public void computeValue() throws Exception {\n        final SOperation operation = mock(SOperation.class);\n        final SLeftOperand leftOperand = mock(SLeftOperand.class);\n        final SExpression rightOperand = mock(SExpression.class);\n\n        when(operation.getLeftOperand()).thenReturn(leftOperand);\n        when(leftOperand.getName()).thenReturn(\"myData\");\n        when(operation.getRightOperand()).thenReturn(rightOperand);\n        when(operation.getOperator()).thenReturn(\"setThing:int\");\n        when(rightOperand.getReturnType()).thenReturn(Integer.class.getName());\n\n        final SExpressionContext expressionContext = new SExpressionContext(123L,\n                DataInstanceContainer.PROCESS_INSTANCE.name(), 1234L);\n        final Map<String, Object> map = new HashMap<>();\n        map.put(\"myData\", new MyClass());\n        expressionContext.setInputValues(map);\n        final MyClass updated = (MyClass) strategy.computeNewValueForLeftOperand(operation, 12, expressionContext,\n                false);\n\n        assertEquals(12, updated.getThing());\n    }\n\n    @Test\n    public void shouldExtractParameterType() throws Exception {\n        final SOperation operation = mock(SOperation.class);\n\n        when(operation.getOperator()).thenReturn(\"setThing\");\n\n        assertThat(strategy.extractParameterType(operation)).as(\"should have no return type\").isNull();\n    }\n\n    @Test\n    public void getOperationType() throws Exception {\n\n        //when\n        final String operationType = strategy.getOperationType();\n\n        //then\n        assertThat(operationType).as(\"should get operation type\")\n                .isEqualTo(JavaMethodOperationExecutorStrategy.TYPE_JAVA_METHOD);\n\n    }\n\n    @Test\n    public void should_not_persist_on_null() throws Exception {\n        assertThat(strategy.shouldPersistOnNullValue()).isFalse();\n    }\n\n    public class MyClass {\n\n        private int thing = 0;\n\n        public void setThing(final int thing) {\n            this.thing = thing;\n        }\n\n        public int getThing() {\n            return thing;\n        }\n\n    }\n\n    public class MyClassThatThrowException {\n\n        private final int thing = 0;\n\n        public void setThing(final int thing) {\n            throw new RuntimeException(\"bad luck\");\n        }\n\n        public int getThing() {\n            return thing;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/operation/impl/LeftOperandIndexesTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.Test;\n\npublic class LeftOperandIndexesTest {\n\n    @Test\n    public void default_constructor_should_create_index_with_negative_values() throws Exception {\n        //when\n        LeftOperandIndexes indexes = new LeftOperandIndexes();\n\n        //then\n        assertThat(indexes.getLastIndex()).isEqualTo(-1);\n        assertThat(indexes.getNextIndex()).isEqualTo(-1);\n    }\n\n    @Test\n    public void setLastIndex_should_modify_lastIndex() throws Exception {\n        //given\n        LeftOperandIndexes indexes = new LeftOperandIndexes();\n\n        //when\n        indexes.setLastIndex(4);\n\n        //then\n        assertThat(indexes.getLastIndex()).isEqualTo(4);\n        assertThat(indexes.getNextIndex()).isEqualTo(-1);\n\n    }\n\n    @Test\n    public void setNextIndex_should_modify_nextIndex() throws Exception {\n        //given\n        LeftOperandIndexes indexes = new LeftOperandIndexes();\n\n        //when\n        indexes.setNextIndex(4);\n\n        //then\n        assertThat(indexes.getLastIndex()).isEqualTo(-1);\n        assertThat(indexes.getNextIndex()).isEqualTo(4);\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/operation/impl/LeftOperandUpdateStatusTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.core.operation.model.SOperatorType;\nimport org.junit.Test;\n\npublic class LeftOperandUpdateStatusTest {\n\n    @Test\n    public void shouldUpdate_on_assignment() throws Exception {\n        //given\n        LeftOperandUpdateStatus updateStatus = new LeftOperandUpdateStatus(SOperatorType.ASSIGNMENT);\n\n        //then\n        assertThat(updateStatus.shouldUpdate()).isTrue();\n        assertThat(updateStatus.shouldDelete()).isFalse();\n    }\n\n    @Test\n    public void shouldUpdate_on_java_method() throws Exception {\n        //given\n        LeftOperandUpdateStatus updateStatus = new LeftOperandUpdateStatus(SOperatorType.JAVA_METHOD);\n\n        //then\n        assertThat(updateStatus.shouldUpdate()).isTrue();\n        assertThat(updateStatus.shouldDelete()).isFalse();\n    }\n\n    @Test\n    public void shouldUpdate_on_XPath() throws Exception {\n        //given\n        LeftOperandUpdateStatus updateStatus = new LeftOperandUpdateStatus(SOperatorType.XPATH_UPDATE_QUERY);\n\n        //then\n        assertThat(updateStatus.shouldUpdate()).isTrue();\n        assertThat(updateStatus.shouldDelete()).isFalse();\n    }\n\n    @Test\n    public void shouldDelete_on_deletion() throws Exception {\n        //given\n        LeftOperandUpdateStatus updateStatus = new LeftOperandUpdateStatus(SOperatorType.DELETION);\n\n        //then\n        assertThat(updateStatus.shouldUpdate()).isFalse();\n        assertThat(updateStatus.shouldDelete()).isTrue();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/operation/impl/OperationMockBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation.impl;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.operation.model.impl.SLeftOperandImpl;\nimport org.bonitasoft.engine.core.operation.model.impl.SOperationImpl;\nimport org.bonitasoft.engine.expression.ExpressionType;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.expression.model.impl.SExpressionImpl;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class OperationMockBuilder {\n\n    public static SExpressionImpl buildExpression(final String content, final ExpressionType type,\n            final List<SExpression> dependencies) {\n        return new SExpressionImpl(content, content, type.name(), \"\", null, dependencies);\n    }\n\n    public static SOperationImpl buildMockOperation(final String leftOperandType, final SExpressionImpl rightOperand) {\n        SOperationImpl operation = buildMockOperation(leftOperandType);\n        operation.setRightOperand(rightOperand);\n        return operation;\n    }\n\n    public static SOperationImpl buildMockOperation(final String leftOperandType) {\n        SOperationImpl operation = new SOperationImpl();\n        SLeftOperandImpl leftOperand = buildMockLeftOperand(leftOperandType);\n        operation.setLeftOperand(leftOperand);\n        return operation;\n    }\n\n    public static SLeftOperandImpl buildMockLeftOperand(final String leftOperandType) {\n        SLeftOperandImpl leftOperand = new SLeftOperandImpl();\n        leftOperand.setType(leftOperandType);\n        return leftOperand;\n    }\n\n    public static SOperationImpl buildMockOperation(final String leftOperandType, String leftOperandName) {\n        SLeftOperandImpl leftOperand = buildMockLeftOperand(leftOperandType, leftOperandName);\n        return buildMockOperation(leftOperand);\n    }\n\n    public static SOperationImpl buildMockOperation(final SLeftOperandImpl leftOperand) {\n        SOperationImpl operation = new SOperationImpl();\n        operation.setLeftOperand(leftOperand);\n        return operation;\n    }\n\n    public static SLeftOperandImpl buildMockLeftOperand(final String leftOperandType, final String leftOperandName) {\n        SLeftOperandImpl leftOperand = buildMockLeftOperand(leftOperandType);\n        leftOperand.setName(leftOperandName);\n        return leftOperand;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/operation/impl/OperationServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation.impl;\n\nimport static java.util.Arrays.asList;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.core.operation.model.SOperatorType.*;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.argThat;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.*;\nimport static org.mockito.Mockito.anyList;\nimport static org.mockito.Mockito.anyLong;\nimport static org.mockito.Mockito.anyMap;\nimport static org.mockito.Mockito.anyString;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.LeftOperandHandler;\nimport org.bonitasoft.engine.core.operation.OperationExecutorStrategy;\nimport org.bonitasoft.engine.core.operation.OperationExecutorStrategyProvider;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.operation.model.SLeftOperand;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.core.operation.model.SOperatorType;\nimport org.bonitasoft.engine.core.operation.model.impl.SLeftOperandImpl;\nimport org.bonitasoft.engine.core.operation.model.impl.SOperationImpl;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class OperationServiceImplTest {\n\n    private static final String TYPE_1 = \"type1\";\n    private static final String TYPE_2 = \"type2\";\n    private static final String TYPE_3 = \"type3\";\n    private static final String TYPE_4 = \"type4\";\n\n    @Mock\n    private ExpressionResolverService expressionResolverService;\n\n    @Mock\n    private LeftOperandHandler leftOperandHandler1;\n\n    @Mock\n    private LeftOperandHandler leftOperandHandler2;\n\n    @Mock\n    private LeftOperandHandler leftOperandHandler3;\n\n    @Mock\n    private LeftOperandHandler leftOperandHandler4;\n\n    @Mock\n    private OperationExecutorStrategyProvider operationExecutorStrategyProvider;\n\n    @Mock\n    private OperationExecutorStrategy assignmentOperationExecutorStrategy;\n\n    @Mock\n    private OperationExecutorStrategy xPathOperationExecutorStrategy;\n\n    @Mock\n    private OperationExecutorStrategy javaMethodOperationExecutorStrategy;\n\n    @Mock\n    private PersistRightOperandResolver persistRightOperandResolver;\n\n    @Captor\n    private ArgumentCaptor<List<SLeftOperand>> leftOperandCaptor1;\n    @Captor\n    private ArgumentCaptor<List<SLeftOperand>> leftOperandCaptor2;\n\n    private OperationServiceImpl operationServiceImpl;\n\n    private SLeftOperandImpl buildLeftOperand(final String type, final String dataName) {\n        final SLeftOperandImpl leftOperand = new SLeftOperandImpl();\n        leftOperand.setType(type);\n        leftOperand.setName(dataName);\n        return leftOperand;\n    }\n\n    private SOperationImpl buildOperation(final String type, final String dataName, final SOperatorType operatorType) {\n        final SOperationImpl sOperationImpl = new SOperationImpl();\n        sOperationImpl.setLeftOperand(buildLeftOperand(type, dataName));\n        sOperationImpl.setType(operatorType);\n        return sOperationImpl;\n    }\n\n    private SOperationImpl buildOperation(final String type, final String dataName, final SOperatorType operatorType,\n            final SExpression rightOperand) {\n        final SOperationImpl operation = buildOperation(type, dataName, operatorType);\n        operation.setRightOperand(rightOperand);\n        return operation;\n    }\n\n    @Before\n    public void before() throws SOperationExecutionException {\n        doReturn(TYPE_1).when(leftOperandHandler1).getType();\n        doReturn(TYPE_2).when(leftOperandHandler2).getType();\n        doReturn(TYPE_3).when(leftOperandHandler3).getType();\n        doReturn(TYPE_4).when(leftOperandHandler4).getType();\n        doReturn(assignmentOperationExecutorStrategy).when(operationExecutorStrategyProvider)\n                .getOperationExecutorStrategy(\n                        argThat(operation -> ASSIGNMENT.equals(operation.getType())));\n        doReturn(xPathOperationExecutorStrategy).when(operationExecutorStrategyProvider).getOperationExecutorStrategy(\n                argThat(operation -> XPATH_UPDATE_QUERY.equals(operation.getType())));\n        doReturn(javaMethodOperationExecutorStrategy).when(operationExecutorStrategyProvider)\n                .getOperationExecutorStrategy(\n                        argThat(operation -> JAVA_METHOD.equals(operation.getType())));\n\n        given(assignmentOperationExecutorStrategy.shouldPersistOnNullValue()).willReturn(true);\n        given(xPathOperationExecutorStrategy.shouldPersistOnNullValue()).willReturn(true);\n        given(javaMethodOperationExecutorStrategy.shouldPersistOnNullValue()).willReturn(true);\n        operationServiceImpl = new OperationServiceImpl(operationExecutorStrategyProvider,\n                asList(leftOperandHandler1, leftOperandHandler2, leftOperandHandler3, leftOperandHandler4),\n                expressionResolverService,\n                persistRightOperandResolver);\n    }\n\n    @Test\n    public void should_UpdateLeftOperands_call_leftOperandHandlers() throws Exception {\n        // given\n        final Map<String, Object> inputValues = new HashMap<>();\n        inputValues.put(\"data1\", \"value1\");\n        inputValues.put(\"data2\", \"value2\");\n        final SExpressionContext expressionContext = new SExpressionContext(123L, \"containerType\", 987L, inputValues);\n        final Map<SLeftOperand, LeftOperandUpdateStatus> updates = new HashMap<>();\n        updates.put(buildLeftOperand(TYPE_1, \"data1\"), new LeftOperandUpdateStatus(ASSIGNMENT));\n        updates.put(buildLeftOperand(TYPE_2, \"data2\"), new LeftOperandUpdateStatus(SOperatorType.DELETION));\n\n        // when\n        operationServiceImpl.updateLeftOperands(updates, 123, \"containerType\", expressionContext);\n\n        // then\n        verify(leftOperandHandler1).update(argThat(leftOperand -> leftOperand.getName().equals(\"data1\")), anyMap(),\n                eq(\"value1\"), eq(123L),\n                eq(\"containerType\"));\n        verify(leftOperandHandler2).delete(argThat(leftOperand -> leftOperand.getName().equals(\"data2\")), eq(123L),\n                eq(\"containerType\"));\n    }\n\n    @Test\n    public void executeOperator_should_call_OperationStrategies() throws Exception {\n        // given\n        final SExpression sExpression = mock(SExpression.class);\n        final SOperation op1 = buildOperation(TYPE_1, \"data1\", ASSIGNMENT, sExpression);\n        final SOperation op2 = buildOperation(TYPE_2, \"data2\", SOperatorType.XPATH_UPDATE_QUERY, sExpression);\n        final SOperation op3 = buildOperation(TYPE_2, \"data2\", SOperatorType.JAVA_METHOD, sExpression);\n        final Map<String, Object> inputValues = new HashMap<>();\n        final SExpressionContext expressionContext = new SExpressionContext(123l, \"containerType\", 987L, inputValues);\n\n        final List<SOperation> operations = asList(op1, op2, op3);\n        given(persistRightOperandResolver.shouldPersistByPosition(0, operations)).willReturn(true);\n        given(persistRightOperandResolver.shouldPersistByPosition(1, operations)).willReturn(false);\n        given(persistRightOperandResolver.shouldPersistByPosition(2, operations)).willReturn(true);\n        given(persistRightOperandResolver.shouldPersistByValue(any(), eq(true), eq(true))).willReturn(true);\n        given(persistRightOperandResolver.shouldPersistByValue(any(), eq(false), eq(true))).willReturn(false);\n        given(expressionResolverService.evaluate(sExpression, expressionContext)).willReturn(1983L);\n\n        doReturn(\"value1\").when(assignmentOperationExecutorStrategy).computeNewValueForLeftOperand(op1, 1983L,\n                expressionContext, true);\n        doReturn(\"value3\").when(javaMethodOperationExecutorStrategy).computeNewValueForLeftOperand(op3, 1983L,\n                expressionContext, true);\n\n        // when\n        operationServiceImpl.executeOperators(operations, expressionContext);\n\n        // then\n        assertThat(expressionContext.getInputValues().get(\"data1\")).isEqualTo(\"value1\");\n        assertThat(expressionContext.getInputValues().get(\"data2\")).isEqualTo(\"value3\");\n    }\n\n    @Test\n    public void should_retrieveLeftOperandsAndPutItInExpressionContextIfNotIn_do_not_override_value_in_map()\n            throws Exception {\n        // given\n        final SOperation op1 = buildOperation(TYPE_2, \"data1\", SOperatorType.XPATH_UPDATE_QUERY);\n        final SExpressionContext expressionContext = new SExpressionContext(123l, \"containerType\", 987L,\n                Collections.singletonMap(\"data1\",\n                        \"originalValue\"));\n\n        // when\n        operationServiceImpl.retrieveLeftOperandsAndPutItInExpressionContextIfNotIn(asList(op1), 123,\n                \"containerType\", expressionContext);\n\n        // then\n        verify(leftOperandHandler2, times(1)).loadLeftOperandInContext(eq(asList(op1.getLeftOperand())),\n                anyLong(), anyString(),\n                any(SExpressionContext.class));\n        assertThat(expressionContext.getInputValues().get(\"data1\")).isEqualTo(\"originalValue\");\n    }\n\n    @Test\n    public void executeOperatorsShouldReturnASingleUpdateForTheSameDataOfTheSameOperator() throws Exception {\n        // given\n        final SOperation operation1 = buildOperation(TYPE_1, \"data1\", SOperatorType.JAVA_METHOD);\n        final SOperation operation2 = buildOperation(TYPE_1, \"data1\", SOperatorType.JAVA_METHOD);\n        final List<SOperation> operations = asList(operation1, operation2);\n        final SExpressionContext expressionContext = new SExpressionContext(123l, \"containerType\", 987L,\n                Collections.<String, Object> singletonMap(\"data1\",\n                        \"givenValue\"));\n\n        // when\n        final Map<SLeftOperand, LeftOperandUpdateStatus> updates = operationServiceImpl.executeOperators(operations,\n                expressionContext);\n\n        // then\n        assertThat(updates).hasSize(1);\n        final SLeftOperandImpl leftOperand = buildLeftOperand(\"type1\", \"data1\");\n        assertThat(updates.containsKey(leftOperand));\n        assertThat(updates.get(leftOperand))\n                .isEqualToComparingFieldByField(new LeftOperandUpdateStatus(SOperatorType.JAVA_METHOD));\n    }\n\n    @Test\n    public void executeOperatorsShouldReturnATwoUpdatesForTheDifferentDataOfTheSameOperator() throws Exception {\n        // given\n        final List<SOperation> operations = new ArrayList<>();\n        operations.add(buildOperation(TYPE_1, \"data2\", SOperatorType.JAVA_METHOD));\n        operations.add(buildOperation(TYPE_1, \"data1\", SOperatorType.JAVA_METHOD));\n        final SExpressionContext expressionContext = new SExpressionContext(123l, \"containerType\", 987L,\n                Collections.<String, Object> singletonMap(\"data1\",\n                        \"givenValue\"));\n        final OperationServiceImpl spy = spy(operationServiceImpl);\n\n        // when\n        final Map<SLeftOperand, LeftOperandUpdateStatus> updates = spy.executeOperators(operations, expressionContext);\n\n        // then\n        assertThat(updates).hasSize(2);\n        final SLeftOperandImpl data2Key = buildLeftOperand(\"type1\", \"data2\");\n        final SLeftOperandImpl data1Key = buildLeftOperand(\"type1\", \"data1\");\n        assertThat(updates).containsKeys(data2Key, data1Key);\n        assertThat(updates.get(data1Key))\n                .isEqualToComparingFieldByField(new LeftOperandUpdateStatus(SOperatorType.JAVA_METHOD));\n        assertThat(updates.get(data2Key))\n                .isEqualToComparingFieldByField(new LeftOperandUpdateStatus(SOperatorType.JAVA_METHOD));\n    }\n\n    @Test\n    public void executeOperationShouldDoBatchGet() throws SOperationExecutionException, SBonitaReadException {\n        //given\n        final List<SOperation> operations = new ArrayList<>();\n        operations.add(buildOperation(TYPE_1, \"data1\", SOperatorType.JAVA_METHOD));\n        operations.add(buildOperation(TYPE_1, \"data2\", SOperatorType.XPATH_UPDATE_QUERY));\n        operations.add(buildOperation(TYPE_2, \"data3\", SOperatorType.JAVA_METHOD));\n        operations.add(buildOperation(TYPE_2, \"data4\", SOperatorType.XPATH_UPDATE_QUERY));\n        operations.add(buildOperation(TYPE_2, \"data5\", SOperatorType.DELETION));\n        final HashMap<String, Object> inputValues = new HashMap<>();\n        final SExpressionContext expressionContext = new SExpressionContext(123l, \"containerType\", 987L, inputValues);\n\n        //when\n        operationServiceImpl.retrieveLeftOperandsAndPutItInExpressionContextIfNotIn(operations,\n                123l/* data container */, \"containerType\", expressionContext);\n\n        //then\n        verify(leftOperandHandler1, times(1)).loadLeftOperandInContext(leftOperandCaptor1.capture(), anyLong(),\n                anyString(), eq(expressionContext));\n\n        final List<SLeftOperand> value1 = leftOperandCaptor1.getValue();\n        assertThat(value1).extracting(\"name\").containsOnly(\"data1\", \"data2\");\n\n        verify(leftOperandHandler2, times(1)).loadLeftOperandInContext(leftOperandCaptor2.capture(), anyLong(),\n                anyString(), eq(expressionContext));\n        final List<SLeftOperand> value2 = leftOperandCaptor2.getValue();\n        assertThat(value2).extracting(\"name\").containsOnly(\"data3\", \"data4\", \"data5\");\n\n    }\n\n    @Test\n    public void should_not_load_assignment_operations() throws SOperationExecutionException, SBonitaReadException {\n\n        //given\n        final List<SOperation> operations = new ArrayList<>();\n        operations.add(buildOperation(TYPE_1, \"data1\", JAVA_METHOD));\n        operations.add(buildOperation(TYPE_1, \"data2\", ASSIGNMENT));\n        operations.add(buildOperation(TYPE_2, \"data3\", JAVA_METHOD));\n        operations.add(buildOperation(TYPE_2, \"data4\", ASSIGNMENT));\n        operations.add(buildOperation(TYPE_2, \"data5\", ASSIGNMENT));\n        final HashMap<String, Object> inputValues = new HashMap<>();\n        final SExpressionContext expressionContext = new SExpressionContext(123l, \"containerType\", 987L, inputValues);\n\n        //when\n        operationServiceImpl.retrieveLeftOperandsAndPutItInExpressionContextIfNotIn(operations,\n                123l/* data container */, \"containerType\", expressionContext);\n\n        //then\n        verify(leftOperandHandler1, times(1)).loadLeftOperandInContext(leftOperandCaptor1.capture(), anyLong(),\n                anyString(), eq(expressionContext));\n        verify(leftOperandHandler2, times(1)).loadLeftOperandInContext(leftOperandCaptor2.capture(), anyLong(),\n                anyString(), eq(expressionContext));\n        verify(leftOperandHandler3, times(0)).loadLeftOperandInContext(anyList(), anyLong(), anyString(),\n                eq(expressionContext));\n        final List<SLeftOperand> value1 = leftOperandCaptor1.getValue();\n        final List<SLeftOperand> value2 = leftOperandCaptor2.getValue();\n        assertThat(value1).extracting(\"name\").containsOnly(\"data1\");\n        assertThat(value2).extracting(\"name\").containsOnly(\"data3\");\n\n    }\n\n    @Test\n    public void should_load_same_data_only_once() throws SOperationExecutionException, SBonitaReadException {\n\n        //given\n        final List<SOperation> operations = new ArrayList<>();\n        operations.add(buildOperation(TYPE_1, \"data1\", SOperatorType.JAVA_METHOD));\n        operations.add(buildOperation(TYPE_1, \"data1\", SOperatorType.XPATH_UPDATE_QUERY));\n        operations.add(buildOperation(TYPE_2, \"data2\", SOperatorType.JAVA_METHOD));\n        final HashMap<String, Object> inputValues = new HashMap<>();\n        final SExpressionContext expressionContext = new SExpressionContext(123l, \"containerType\", 987L, inputValues);\n\n        //when\n        operationServiceImpl.retrieveLeftOperandsAndPutItInExpressionContextIfNotIn(operations,\n                123l/* data container */, \"containerType\", expressionContext);\n\n        //then\n        verify(leftOperandHandler1, times(1)).loadLeftOperandInContext(leftOperandCaptor1.capture(), anyLong(),\n                anyString(), eq(expressionContext));\n        verify(leftOperandHandler2, times(1)).loadLeftOperandInContext(leftOperandCaptor2.capture(), anyLong(),\n                anyString(), eq(expressionContext));\n        assertThat(leftOperandCaptor1.getValue()).extracting(\"name\").containsOnly(\"data1\");\n\n    }\n\n    @Test\n    public void should_not_update_left_operand_context_when_new_update() throws Exception {\n        //given\n        final Map<SLeftOperand, LeftOperandUpdateStatus> leftOperands = Collections\n                .<SLeftOperand, LeftOperandUpdateStatus> singletonMap(\n                        buildLeftOperand(\"type1\", \"data1\"), new LeftOperandUpdateStatus(ASSIGNMENT));\n\n        //when\n        final boolean shouldUpdateLeftOperandContext = operationServiceImpl.shouldUpdateLeftOperandContext(leftOperands,\n                buildLeftOperand(\"type1\", \"data1\"),\n                new LeftOperandUpdateStatus(\n                        SOperatorType.JAVA_METHOD));\n\n        //then\n        assertThat(shouldUpdateLeftOperandContext).isFalse();\n    }\n\n    @Test\n    public void should_not_update_left_operand_context_when_previous_was_a_deletion() throws Exception {\n        //given\n        final Map<SLeftOperand, LeftOperandUpdateStatus> leftOperands = Collections\n                .<SLeftOperand, LeftOperandUpdateStatus> singletonMap(\n                        buildLeftOperand(\"type1\", \"data1\"), new LeftOperandUpdateStatus(SOperatorType.DELETION));\n\n        //when\n        final boolean shouldUpdateLeftOperandContext = operationServiceImpl.shouldUpdateLeftOperandContext(leftOperands,\n                buildLeftOperand(\"type1\", \"data1\"),\n                new LeftOperandUpdateStatus(\n                        SOperatorType.JAVA_METHOD));\n\n        //then\n        assertThat(shouldUpdateLeftOperandContext).isFalse();\n    }\n\n    @Test\n    public void should_update_left_operand_context_when_not_in_the_context() throws Exception {\n        //given\n        final Map<SLeftOperand, LeftOperandUpdateStatus> leftOperands = Collections.emptyMap();\n\n        //when\n        final boolean shouldUpdateLeftOperandContext = operationServiceImpl.shouldUpdateLeftOperandContext(leftOperands,\n                buildLeftOperand(\"type1\", \"data1\"),\n                new LeftOperandUpdateStatus(\n                        SOperatorType.JAVA_METHOD));\n\n        //then\n        assertThat(shouldUpdateLeftOperandContext).isTrue();\n    }\n\n    @Test\n    public void should_update_left_operand_context_when_new_operation_is_deletion() throws Exception {\n        //given\n        final LeftOperandUpdateStatus previousUpdateState = new LeftOperandUpdateStatus(SOperatorType.JAVA_METHOD);\n        final Map<SLeftOperand, LeftOperandUpdateStatus> leftOperands = Collections\n                .<SLeftOperand, LeftOperandUpdateStatus> singletonMap(\n                        buildLeftOperand(\"type1\", \"data1\"), previousUpdateState);\n\n        //when new java method must be persisted again\n        final boolean shouldUpdateLeftOperandContext = operationServiceImpl.shouldUpdateLeftOperandContext(leftOperands,\n                buildLeftOperand(\"type1\", \"data1\"),\n                new LeftOperandUpdateStatus(\n                        SOperatorType.DELETION));\n\n        //then\n        assertThat(shouldUpdateLeftOperandContext).isTrue();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/operation/impl/OperationsAnalyzerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.core.operation.impl.OperationMockBuilder.buildExpression;\nimport static org.bonitasoft.engine.core.operation.impl.OperationMockBuilder.buildMockLeftOperand;\nimport static org.bonitasoft.engine.core.operation.impl.OperationMockBuilder.buildMockOperation;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.operation.model.SLeftOperand;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.core.operation.model.impl.SLeftOperandImpl;\nimport org.bonitasoft.engine.core.operation.model.impl.SOperationImpl;\nimport org.bonitasoft.engine.expression.ExpressionType;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.expression.model.impl.SExpressionImpl;\nimport org.junit.Test;\n\npublic class OperationsAnalyzerTest {\n\n    private OperationsAnalyzer dependencyFinder = new OperationsAnalyzer();\n\n    @Test\n    public void findDependencyIndex_should_return_minus_one_if_index_is_out_of_list() throws Exception {\n        //when\n        int index = dependencyFinder.findBusinessDataDependencyIndex(\"address\", 1,\n                Collections.<SOperation> emptyList());\n\n        //then\n        assertThat(index).isEqualTo(-1);\n    }\n\n    @Test\n    public void findDependencyIndex_should_return_minus_one_if_right_operand_is_null() throws Exception {\n        //when\n        int index = dependencyFinder.findBusinessDataDependencyIndex(\"address\", 0,\n                Arrays.<SOperation> asList(\n                        buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, (SExpressionImpl) null)));\n\n        //then\n        assertThat(index).isEqualTo(-1);\n    }\n\n    @Test\n    public void findDependencyIndex_should_return_zero_if_expression_is_dependency_of_first_operation()\n            throws Exception {\n        //given\n        String dataName1 = \"address1\";\n        String dataName2 = \"address2\";\n        ExpressionType type = ExpressionType.TYPE_BUSINESS_DATA;\n        List<SExpression> dependencies = Collections.emptyList();\n        SExpressionImpl rightOperand1 = buildExpression(dataName1, type, dependencies);\n        SExpressionImpl rightOperand2 = buildExpression(dataName2, type, dependencies);\n        List<SOperation> operations = Arrays.<SOperation> asList(\n                buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, rightOperand1),\n                buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, rightOperand2));\n\n        //when\n        int index = dependencyFinder.findBusinessDataDependencyIndex(dataName1, 0, operations);\n\n        //then\n        assertThat(index).isEqualTo(0);\n    }\n\n    @Test\n    public void findDependencyIndex_should_return_lastIndex_if_expression_is_dependency_of_last_operation()\n            throws Exception {\n        //given\n        String dataName1 = \"address1\";\n        String dataName2 = \"address2\";\n        ExpressionType type = ExpressionType.TYPE_BUSINESS_DATA;\n        List<SExpression> dependencies = Collections.emptyList();\n        SExpressionImpl rightOperand1 = buildExpression(dataName1, type, dependencies);\n        SExpressionImpl rightOperand2 = buildExpression(dataName2, type, dependencies);\n        List<SOperation> operations = Arrays.<SOperation> asList(\n                buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, rightOperand1),\n                buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, rightOperand2));\n\n        //when\n        int index = dependencyFinder.findBusinessDataDependencyIndex(dataName2, 0, operations);\n\n        //then\n        assertThat(index).isEqualTo(1);\n    }\n\n    @Test\n    public void findDependencyIndex_should_return_minus_one_if_find_expression_with_same_name_but_different_type()\n            throws Exception {\n        //given\n        String dataName1 = \"address1\";\n        List<SExpression> dependencies = Collections.emptyList();\n        SExpressionImpl rightOperand1 = buildExpression(dataName1, ExpressionType.TYPE_CONDITION, dependencies);\n        List<SOperation> operations = Arrays\n                .<SOperation> asList(buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, rightOperand1));\n\n        //when\n        int index = dependencyFinder.findBusinessDataDependencyIndex(dataName1, 0, operations);\n\n        //then\n        assertThat(index).isEqualTo(-1);\n    }\n\n    @Test\n    public void findDependencyIndex_should_return_minus_one_if_expression_is_dependency_at_a_index_before_fromIndex()\n            throws Exception {\n        //given\n        String dataName1 = \"address1\";\n        String dataName2 = \"address2\";\n        ExpressionType type = ExpressionType.TYPE_BUSINESS_DATA;\n        List<SExpression> dependencies = Collections.emptyList();\n        SExpressionImpl rightOperand1 = buildExpression(dataName1, type, dependencies);\n        SExpressionImpl rightOperand2 = buildExpression(dataName2, type, dependencies);\n        List<SOperation> operations = Arrays.<SOperation> asList(\n                buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, rightOperand1),\n                buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, rightOperand2));\n\n        //when\n        int index = dependencyFinder.findBusinessDataDependencyIndex(dataName1, 1, operations);\n\n        //then\n        assertThat(index).isEqualTo(-1);\n    }\n\n    @Test\n    public void findDependencyIndex_should_look_at_first_dependency_level() throws Exception {\n        //given\n        String dataName1 = \"address1\";\n        ExpressionType type = ExpressionType.TYPE_BUSINESS_DATA;\n        SExpressionImpl dataExpression = buildExpression(dataName1, type, Collections.<SExpression> emptyList());\n        SExpressionImpl rightOperand = buildExpression(\"myScript\", ExpressionType.TYPE_READ_ONLY_SCRIPT,\n                Collections.<SExpression> singletonList(dataExpression));\n        List<SOperation> operations = Arrays\n                .<SOperation> asList(buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, rightOperand));\n\n        //when\n        int index = dependencyFinder.findBusinessDataDependencyIndex(dataName1, 0, operations);\n\n        //then\n        assertThat(index).isEqualTo(0);\n    }\n\n    @Test\n    public void findDependencyIndex_should_look_at_all_dependency_levels() throws Exception {\n        //given\n        String dataName1 = \"address1\";\n        ExpressionType type = ExpressionType.TYPE_BUSINESS_DATA;\n        SExpressionImpl dataExpression = buildExpression(dataName1, type, Collections.<SExpression> emptyList());\n        SExpressionImpl dependencyL11 = buildExpression(\"dep11\", type, Collections.<SExpression> emptyList());\n        SExpressionImpl dependencyL12 = buildExpression(\"dep12\", ExpressionType.TYPE_READ_ONLY_SCRIPT,\n                Collections.<SExpression> singletonList(dataExpression));\n        SExpressionImpl rightOperand = buildExpression(\"myScript\", ExpressionType.TYPE_READ_ONLY_SCRIPT,\n                Arrays.<SExpression> asList(dependencyL11, dependencyL12));\n        List<SOperation> operations = Arrays\n                .<SOperation> asList(buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, rightOperand));\n\n        //when\n        int index = dependencyFinder.findBusinessDataDependencyIndex(dataName1, 0, operations);\n\n        //then\n        assertThat(index).isEqualTo(0);\n    }\n\n    @Test\n    public void calculate_indexes_should_return_positive_values_when_left_operand_is_found() throws Exception {\n        //given\n        SLeftOperandImpl data1 = buildMockLeftOperand(SLeftOperand.TYPE_BUSINESS_DATA, \"data1\");\n        SOperationImpl op1 = buildMockOperation(data1);\n        SOperationImpl op2 = buildMockOperation(data1);\n        SOperationImpl op3 = buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, \"data2\");\n        SOperationImpl op4 = buildMockOperation(data1);\n\n        //when\n        LeftOperandIndexes indexes = dependencyFinder.calculateIndexes(0,\n                Arrays.<SOperation> asList(op1, op2, op3, op4));\n\n        //then\n        assertThat(indexes.getNextIndex()).isEqualTo(1);\n        assertThat(indexes.getLastIndex()).isEqualTo(3);\n    }\n\n    @Test\n    public void calculate_indexes_should_return_negative_value_for_nextIndex_when_is_last() throws Exception {\n        //given\n        SLeftOperandImpl data1 = buildMockLeftOperand(SLeftOperand.TYPE_BUSINESS_DATA, \"data1\");\n        SOperationImpl op1 = buildMockOperation(data1);\n        SOperationImpl op2 = buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, \"data2\");\n\n        //when\n        LeftOperandIndexes indexes = dependencyFinder.calculateIndexes(1, Arrays.<SOperation> asList(op1, op2));\n\n        //then\n        assertThat(indexes.getNextIndex()).isEqualTo(-1);\n        assertThat(indexes.getLastIndex()).isEqualTo(1);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/operation/impl/PersistRightOperandResolverTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.core.operation.impl.OperationMockBuilder.buildMockOperation;\nimport static org.mockito.ArgumentMatchers.anyInt;\nimport static org.mockito.ArgumentMatchers.anyList;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.operation.model.SLeftOperand;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.core.operation.model.impl.SOperationImpl;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class PersistRightOperandResolverTest {\n\n    @Mock\n    private OperationsAnalyzer operationsAnalyzer;\n\n    @InjectMocks\n    private PersistRightOperandResolver resolver;\n\n    @Test\n    public void should_not_persist_if_not_business_data() throws Exception {\n        //when\n        boolean persist = resolver.shouldPersistByPosition(0,\n                Arrays.<SOperation> asList(buildMockOperation(SLeftOperand.TYPE_DATA)));\n\n        //then\n        assertThat(persist).isFalse();\n    }\n\n    @Test\n    public void should_persist_bo_if_is_last_operation_for_this_left_operand() throws Exception {\n        //given\n        SOperationImpl addressOp1 = buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, \"address\");\n        SOperationImpl employeeOp1 = buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, \"employee\");\n        SOperationImpl addressOp2 = buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, \"address\");\n\n        List<SOperation> operations = Arrays.<SOperation> asList(addressOp1, employeeOp1, addressOp2);\n        LeftOperandIndexes indexes = new LeftOperandIndexes();\n        indexes.setLastIndex(2);\n        given(operationsAnalyzer.calculateIndexes(2, operations)).willReturn(indexes);\n\n        //when\n        boolean persist = resolver.shouldPersistByPosition(2, operations);\n\n        //then\n        assertThat(persist).isTrue();\n        verify(operationsAnalyzer, never()).findBusinessDataDependencyIndex(anyString(), anyInt(), anyList());\n\n    }\n\n    @Test\n    public void should_persist_bo_when_next_operation_on_this_left_operand_is_after_next_usage_as_dependency()\n            throws Exception {\n        //given\n        SOperationImpl addressOp1 = buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, \"address\");\n        SOperationImpl employeeOp1 = buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, \"employee\");\n        SOperationImpl addressOp2 = buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, \"address\");\n        SOperationImpl employeeOp2 = buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, \"employee\");\n\n        List<SOperation> operations = Arrays.<SOperation> asList(addressOp1, employeeOp1, addressOp2, employeeOp2);\n\n        int currentIndex = 0;\n        given(operationsAnalyzer.findBusinessDataDependencyIndex(\"address\", currentIndex + 1, operations))\n                .willReturn(1);\n        LeftOperandIndexes indexes = new LeftOperandIndexes();\n        indexes.setNextIndex(2);\n        given(operationsAnalyzer.calculateIndexes(0, operations)).willReturn(indexes);\n\n        //when\n        boolean persist = resolver.shouldPersistByPosition(currentIndex, operations);\n\n        //then\n        assertThat(persist).isTrue();\n    }\n\n    @Test\n    public void should_not_persist_bo_when_is_not_last_operation_for_this_left_operand_neither_is_dependency()\n            throws Exception {\n        //given\n        SOperationImpl addressOp1 = buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, \"address\");\n        SOperationImpl employeeOp1 = buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, \"employee\");\n        SOperationImpl addressOp2 = buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, \"address\");\n\n        int indexOfCurrentOperation = 0;\n        List<SOperation> operations = Arrays.<SOperation> asList(addressOp1, employeeOp1, addressOp2);\n        LeftOperandIndexes indexes = new LeftOperandIndexes();\n        indexes.setLastIndex(2);\n        indexes.setNextIndex(2);\n        given(operationsAnalyzer.calculateIndexes(indexOfCurrentOperation, operations)).willReturn(indexes);\n\n        given(operationsAnalyzer.findBusinessDataDependencyIndex(\"address\", indexOfCurrentOperation + 1, operations))\n                .willReturn(-1);\n\n        //when\n        boolean persist = resolver.shouldPersistByPosition(indexOfCurrentOperation, operations);\n\n        //then\n        assertThat(persist).isFalse();\n\n    }\n\n    @Test\n    public void should_not_persist_when_right_operand_is_null_and_should_not_persist_on_null() throws Exception {\n        //then\n        assertThat(resolver.shouldPersistByValue(null, true, false)).isFalse();\n    }\n\n    @Test\n    public void should_not_persist_when_right_operand_is_null_and_should_not_persist_by_position() throws Exception {\n        //then\n        assertThat(resolver.shouldPersistByValue(null, false, true)).isFalse();\n    }\n\n    @Test\n    public void should_persist_when_right_operand_is_null_and_should_not_persist_by_position_and_on_null()\n            throws Exception {\n        //then\n        assertThat(resolver.shouldPersistByValue(null, true, true)).isTrue();\n    }\n\n    @Test\n    public void should_persist_if_right_operand_is_not_null_and_should_persist_by_position() throws Exception {\n        //then\n        assertThat(resolver.shouldPersistByValue(\"anyNotNullValue\", true, true)).isTrue();\n    }\n\n    @Test\n    public void should_not_persist_if_right_operand_is_not_null_and_should_not_persist_by_position() throws Exception {\n        //then\n        assertThat(resolver.shouldPersistByValue(\"anyNotNullValue\", false, true)).isFalse();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/operation/impl/XpathUpdateQueryOperationExecutorStrategyTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.operation.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.Test;\n\npublic class XpathUpdateQueryOperationExecutorStrategyTest {\n\n    @Test\n    public void should_not_persist_on_null() throws Exception {\n        assertThat(new XpathUpdateQueryOperationExecutorStrategy().shouldPersistOnNullValue()).isFalse();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/ProcessDefinitionServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\nimport static org.mockito.Mockito.*;\n\nimport java.io.IOException;\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.bar.ProcessDefinitionBARContribution;\nimport org.bonitasoft.engine.bpm.process.ActivationState;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion;\nimport org.bonitasoft.engine.bpm.process.impl.internal.DesignProcessDefinitionImpl;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.cache.CacheService;\nimport org.bonitasoft.engine.commons.Pair;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDeploymentInfoUpdateException;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDesignContent;\nimport org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionDeployInfoUpdateBuilder;\nimport org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionDeployInfoUpdateBuilderFactory;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SProcessDefinitionImpl;\nimport org.bonitasoft.engine.expression.ExpressionType;\nimport org.bonitasoft.engine.expression.impl.ExpressionImpl;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.ArgumentMatchers;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Celine Souchet\n * @author Emmanuel Duchastenier\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class ProcessDefinitionServiceImplTest {\n\n    private static final long PROCESS_DEFINITION_DEPLOY_ID = 3L;\n    private static final long PROCESS_ID = 42L;\n    private static final String THE_PROCESS_XML_CONTENT = \"THE PROCESS XML CONTENT\";\n    @Mock\n    private CacheService cacheService;\n    @Mock\n    private ReadPersistenceService persistenceService;\n    @Mock\n    private QueriableLoggerService queriableLoggerService;\n    @Mock\n    private Recorder recorder;\n    @Mock\n    private ReadSessionAccessor sessionAccessor;\n    @Mock\n    private SessionService sessionService;\n    @Mock\n    private ProcessDefinitionBARContribution processDefinitionBARContribution;\n    @InjectMocks\n    @Spy\n    private ProcessDefinitionServiceImpl processDefinitionServiceImpl;\n    private SProcessDefinitionDeployInfo sProcessDefinitionDeployInfo;\n    private DesignProcessDefinition designProcessDefinition;\n\n    @Before\n    public void before() throws SProcessDefinitionNotFoundException, SBonitaReadException, IOException {\n        sProcessDefinitionDeployInfo = new SProcessDefinitionDeployInfo();\n        sProcessDefinitionDeployInfo.setId(PROCESS_DEFINITION_DEPLOY_ID);\n        final SProcessDefinitionDesignContent designContent = new SProcessDefinitionDesignContent();\n        designContent.setContent(THE_PROCESS_XML_CONTENT);\n        sProcessDefinitionDeployInfo.setDesignContent(designContent);\n        doReturn(sProcessDefinitionDeployInfo).when(processDefinitionServiceImpl).getProcessDeploymentInfo(PROCESS_ID);\n        designProcessDefinition = new DesignProcessDefinitionImpl(\"THE NAME\", \"THE VERSION\");\n        doReturn(designProcessDefinition).when(processDefinitionBARContribution)\n                .convertXmlToProcess(THE_PROCESS_XML_CONTENT);\n        processDefinitionServiceImpl.processDefinitionBARContribution = processDefinitionBARContribution;\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getProcessDeploymentInfos(int, int, java.lang.String, org.bonitasoft.engine.persistence.OrderByType)}\n     * .\n     */\n    @Test\n    public void getProcessDeploymentInfos() throws Exception {\n        // Given\n        final List<SProcessDefinitionDeployInfo> sProcessDefinitionDeployInfos = new ArrayList<>(3);\n        doReturn(sProcessDefinitionDeployInfos).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SProcessDefinitionDeployInfo>> any());\n\n        // When\n        final List<SProcessDefinitionDeployInfo> processDeploymentInfos = processDefinitionServiceImpl\n                .getProcessDeploymentInfos(0, 10, \"id\", OrderByType.ASC);\n\n        // Then\n        assertEquals(sProcessDefinitionDeployInfos, processDeploymentInfos);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getProcessDeploymentInfosThrowException() throws Exception {\n        // Given\n        doThrow(new SBonitaReadException(\"plop\")).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SProcessDefinitionDeployInfo>> any());\n\n        // When\n        processDefinitionServiceImpl.getProcessDeploymentInfos(0, 10, \"id\", OrderByType.ASC);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getNumberOfProcessDeploymentInfos()}.\n     */\n    @Test\n    public void getNumberOfProcessDeploymentInfos() throws Exception {\n        // Given\n        final long numberOfProcessDeploymentInfos = 9;\n        doReturn(numberOfProcessDeploymentInfos).when(persistenceService)\n                .selectOne(ArgumentMatchers.<SelectOneDescriptor<Long>> any());\n\n        // When\n        final long result = processDefinitionServiceImpl.getNumberOfProcessDeploymentInfos();\n\n        // Then\n        assertEquals(numberOfProcessDeploymentInfos, result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getNumberOfProcessDeploymentInfosThrowException() throws Exception {\n        // Given\n        doThrow(new SBonitaReadException(\"plop\")).when(persistenceService)\n                .selectOne(ArgumentMatchers.<SelectOneDescriptor<Long>> any());\n\n        // When\n        processDefinitionServiceImpl.getNumberOfProcessDeploymentInfos();\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getProcessDeploymentInfo(long)}.\n     */\n    @Test\n    public void getProcessDeploymentInfoById() throws Exception {\n        // Given\n        final SProcessDefinitionDeployInfo sProcessDefinitionDeployInfo = mock(SProcessDefinitionDeployInfo.class);\n        doReturn(sProcessDefinitionDeployInfo).when(persistenceService)\n                .selectOne(ArgumentMatchers.<SelectOneDescriptor<SProcessDefinitionDeployInfo>> any());\n\n        // When\n        final SProcessDefinitionDeployInfo result = processDefinitionServiceImpl.getProcessDeploymentInfo(2);\n\n        // Then\n        assertEquals(sProcessDefinitionDeployInfo, result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getProcessDeploymentInfoByIdThrowException() throws Exception {\n        // Given\n        doThrow(new SBonitaReadException(\"plop\")).when(persistenceService)\n                .selectOne(ArgumentMatchers.<SelectOneDescriptor<SProcessDefinitionDeployInfo>> any());\n\n        // When\n        processDefinitionServiceImpl.getProcessDeploymentInfo(2);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getNumberOfProcessDeploymentInfosByActivationState(org.bonitasoft.engine.bpm.process.ActivationState)}\n     * .\n     */\n    @Test\n    public void getNumberOfProcessDeploymentInfosByActivationState() throws Exception {\n        // Given\n        final long numberOfProcessDeploymentInfos = 9;\n        doReturn(numberOfProcessDeploymentInfos).when(persistenceService)\n                .selectOne(ArgumentMatchers.<SelectOneDescriptor<Long>> any());\n\n        // When\n        final long result = processDefinitionServiceImpl\n                .getNumberOfProcessDeploymentInfosByActivationState(ActivationState.DISABLED);\n\n        // Then\n        assertEquals(numberOfProcessDeploymentInfos, result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getNumberOfProcessDeploymentInfosByActivationStateThrowException() throws Exception {\n        // Given\n        doThrow(new SBonitaReadException(\"plop\")).when(persistenceService)\n                .selectOne(ArgumentMatchers.<SelectOneDescriptor<Long>> any());\n\n        // When\n        processDefinitionServiceImpl.getNumberOfProcessDeploymentInfosByActivationState(ActivationState.DISABLED);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getProcessDefinitionIds(org.bonitasoft.engine.bpm.process.ActivationState, int, int)}\n     * .\n     */\n    @Test\n    public void getProcessDefinitionIdsByActivationState() throws Exception {\n        // Given\n        final List<Long> processDefinitionIds = Arrays.asList(3L);\n        doReturn(processDefinitionIds).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<Long>> any());\n\n        // When\n        final List<Long> result = processDefinitionServiceImpl.getProcessDefinitionIds(ActivationState.DISABLED, 0, 10);\n\n        // Then\n        assertEquals(processDefinitionIds, result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getProcessDefinitionIdsByActivationStateThrowException() throws Exception {\n        // Given\n        doThrow(new SBonitaReadException(\"plop\")).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<Long>> any());\n\n        // When\n        processDefinitionServiceImpl.getProcessDefinitionIds(ActivationState.DISABLED, 0, 10);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getProcessDefinitionIds(int, int)}.\n     */\n    @Test\n    public void getProcessDefinitionIds() throws Exception {\n        // Given\n        final List<Long> processDefinitionIds = Arrays.asList(3L);\n        doReturn(processDefinitionIds).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<Long>> any());\n\n        // When\n        final List<Long> result = processDefinitionServiceImpl.getProcessDefinitionIds(0, 10);\n\n        // Then\n        assertEquals(processDefinitionIds, result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getProcessDefinitionIdsThrowException() throws Exception {\n        // Given\n        doThrow(new SBonitaReadException(\"plop\")).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<Long>> any());\n\n        // When\n        processDefinitionServiceImpl.getProcessDefinitionIds(0, 10);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getLatestProcessDefinitionId(java.lang.String)}.\n     */\n    @Test\n    public void getLatestProcessDefinitionId() throws Exception {\n        // Given\n        final SProcessDefinitionDeployInfo sProcessDefinitionDeployInfo = mock(SProcessDefinitionDeployInfo.class);\n        doReturn(6L).when(sProcessDefinitionDeployInfo).getProcessId();\n        final List<SProcessDefinitionDeployInfo> sProcessDefinitionDeployInfos = Arrays\n                .asList(sProcessDefinitionDeployInfo);\n        doReturn(sProcessDefinitionDeployInfos).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SProcessDefinitionDeployInfo>> any());\n\n        // When\n        final long processDeploymentInfoId = processDefinitionServiceImpl.getLatestProcessDefinitionId(\"name\");\n\n        // Then\n        assertEquals(sProcessDefinitionDeployInfos.get(0).getProcessId(), processDeploymentInfoId);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getLatestProcessDefinitionIdThrowException() throws Exception {\n        // Given\n        doThrow(new SBonitaReadException(\"plop\")).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SProcessDefinitionDeployInfo>> any());\n\n        // When\n        processDefinitionServiceImpl.getLatestProcessDefinitionId(\"name\");\n    }\n\n    /**\n     * Test method for {@link ProcessDefinitionService#getProcessDefinitionId(String, String)}.\n     */\n    @Test\n    public void getProcessDefinitionId_should_return_id_of_process_definition_with_given_name_and_version()\n            throws Exception {\n        // Given\n        final Map<String, Object> parameters = new HashMap<>();\n        String name = \"proc\";\n        String version = \"1.0\";\n        parameters.put(\"name\", name);\n        parameters.put(\"version\", version);\n        SelectOneDescriptor<Long> selectOneDescriptor = new SelectOneDescriptor<>(\n                \"getProcessDefinitionIdByNameAndVersion\", parameters,\n                SProcessDefinitionDeployInfo.class, Long.class);\n\n        final long processId = 9;\n        doReturn(processId).when(persistenceService).selectOne(selectOneDescriptor);\n\n        // When\n        final long result = processDefinitionServiceImpl.getProcessDefinitionId(name, version);\n\n        // Then\n        assertEquals(processId, result);\n    }\n\n    @Test(expected = SProcessDefinitionNotFoundException.class)\n    public void getProcessDefinitionId_should_return_throw_SProcessDefinitionNotFoundException_when_persistenceSservice_returns_null()\n            throws Exception {\n        // Given\n        final Map<String, Object> parameters = new HashMap<>();\n        String name = \"proc\";\n        String version = \"1.0\";\n        parameters.put(\"name\", name);\n        parameters.put(\"version\", version);\n        SelectOneDescriptor<Long> selectOneDescriptor = new SelectOneDescriptor<>(\n                \"getProcessDefinitionIdByNameAndVersion\", parameters,\n                SProcessDefinitionDeployInfo.class, Long.class);\n\n        doReturn(null).when(persistenceService).selectOne(selectOneDescriptor);\n\n        // When\n        processDefinitionServiceImpl.getProcessDefinitionId(\"name\", \"version\");\n\n        // Then exception\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getProcessDefinitionId_should_throw_SBonitaReadException_when_persistenceSservice_throws_exception()\n            throws Exception {\n        // Given\n        doThrow(new SBonitaReadException(\"plop\")).when(persistenceService)\n                .selectOne(ArgumentMatchers.<SelectOneDescriptor<Long>> any());\n\n        // When\n        processDefinitionServiceImpl.getProcessDefinitionId(\"name\", \"version\");\n    }\n\n    @Test\n    public void getProcessDefinition_from_cache() throws Exception {\n        sProcessDefinitionDeployInfo.setLastUpdateDate(5478L);\n        final SProcessDefinitionImpl sProcessDefinition = new SProcessDefinitionImpl(\"a\", \"b\");\n        doReturn(new Pair<Long, SProcessDefinition>(5478L, sProcessDefinition)).when(processDefinitionServiceImpl)\n                .getSProcessDefinitionFromCache(PROCESS_ID);\n\n        final SProcessDefinition processDefinition = processDefinitionServiceImpl.getProcessDefinition(PROCESS_ID);\n\n        assertThat(processDefinition).as(\"returned process definition from cache\").isEqualTo(sProcessDefinition);\n        verify(cacheService, times(0)).store(anyString(), any(Serializable.class), any());\n    }\n\n    @Test\n    public void getProcessDefinition_from_database() throws Exception {\n        sProcessDefinitionDeployInfo.setLastUpdateDate(5478L);\n        final SProcessDefinitionImpl sProcessDefinition = new SProcessDefinitionImpl(\"a\", \"b\");\n        doReturn(sProcessDefinition).when(processDefinitionServiceImpl)\n                .convertDesignProcessDefinition(designProcessDefinition);\n\n        final SProcessDefinition processDefinition = processDefinitionServiceImpl.getProcessDefinition(PROCESS_ID);\n\n        assertThat(processDefinition).as(\"returned process definition from database\").isEqualTo(sProcessDefinition);\n        verify(cacheService, times(1)).store(anyString(), any(Serializable.class), any());\n    }\n\n    @Test\n    public void getProcessDefinition_from_database_when_cache_is_outdated() throws Exception {\n        sProcessDefinitionDeployInfo.setLastUpdateDate(5478L);\n        final SProcessDefinitionImpl processDefinitionB = new SProcessDefinitionImpl(\"a\", \"b\");\n        final SProcessDefinitionImpl processDefinitionC = new SProcessDefinitionImpl(\"a\", \"c\");\n        doReturn(new Pair<Long, SProcessDefinition>(5477L, processDefinitionB)).when(processDefinitionServiceImpl)\n                .getSProcessDefinitionFromCache(PROCESS_ID);\n        doReturn(processDefinitionC).when(processDefinitionServiceImpl)\n                .convertDesignProcessDefinition(designProcessDefinition);\n        final SProcessDefinition processDefinition = processDefinitionServiceImpl.getProcessDefinition(PROCESS_ID);\n\n        assertThat(processDefinition).as(\"returned process definition from cache\").isEqualTo(processDefinitionC);\n        verify(cacheService, times(1)).store(anyString(), any(Serializable.class), any());\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#updateProcessDefinitionDeployInfo(long, org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor)}\n     * .\n     */\n    @Test\n    public void updateProcessDefinitionDeployInfo() throws Exception {\n        // Given\n        final SProcessDefinitionDeployInfo sProcessDefinitionDeployInfo = mock(SProcessDefinitionDeployInfo.class);\n\n        final SProcessDefinitionDeployInfoUpdateBuilder updateBuilder = BuilderFactory\n                .get(SProcessDefinitionDeployInfoUpdateBuilderFactory.class)\n                .createNewInstance();\n        updateBuilder.updateDisplayName(\"newDisplayName\");\n\n        doReturn(sProcessDefinitionDeployInfo).when(persistenceService)\n                .selectOne(ArgumentMatchers.<SelectOneDescriptor<SProcessDefinitionDeployInfo>> any());\n\n        // When\n        final SProcessDefinitionDeployInfo result = processDefinitionServiceImpl.updateProcessDefinitionDeployInfo(3,\n                updateBuilder.done());\n\n        // Then\n        assertNotNull(result);\n        assertEquals(sProcessDefinitionDeployInfo, result);\n    }\n\n    @Test\n    public void updateProcessDefinitionDeployInfo_create_business_log() throws Exception {\n        // Given\n        doReturn(true).when(queriableLoggerService).isLoggable(anyString(), any(SQueriableLogSeverity.class));\n        final SProcessDefinitionDeployInfo sProcessDefinitionDeployInfo = mock(SProcessDefinitionDeployInfo.class);\n        final SProcessDefinitionDeployInfoUpdateBuilder updateBuilder = BuilderFactory\n                .get(SProcessDefinitionDeployInfoUpdateBuilderFactory.class)\n                .createNewInstance();\n        updateBuilder.updateDisplayName(\"newDisplayName\");\n        doReturn(sProcessDefinitionDeployInfo).when(persistenceService)\n                .selectOne(ArgumentMatchers.<SelectOneDescriptor<SProcessDefinitionDeployInfo>> any());\n\n        // When\n        processDefinitionServiceImpl.updateProcessDefinitionDeployInfo(3, updateBuilder.done(), \"the business log\");\n\n        verify(queriableLoggerService).log(anyString(), eq(\"updateProcessDeploymentInfo\"),\n                ArgumentMatchers.<SQueriableLog> argThat(logs -> logs.getRawMessage().equals(\"the business log\")));\n    }\n\n    @Test\n    public void updateProcessDefinitionDeployInfo_truncate_log_when_too_long() throws Exception {\n        // Given\n        doReturn(true).when(queriableLoggerService).isLoggable(anyString(), any(SQueriableLogSeverity.class));\n        final SProcessDefinitionDeployInfo sProcessDefinitionDeployInfo = mock(SProcessDefinitionDeployInfo.class);\n        final SProcessDefinitionDeployInfoUpdateBuilder updateBuilder = BuilderFactory\n                .get(SProcessDefinitionDeployInfoUpdateBuilderFactory.class)\n                .createNewInstance();\n        updateBuilder.updateDisplayName(\"newDisplayName\");\n        doReturn(sProcessDefinitionDeployInfo).when(persistenceService).selectOne(any());\n\n        // When\n        final StringBuilder string1024 = new StringBuilder();\n        for (int i = 0; i < 1024; i++) {\n            string1024.append(\"H\");\n        }\n        processDefinitionServiceImpl.updateProcessDefinitionDeployInfo(3, updateBuilder.done(), string1024.toString());\n\n        verify(queriableLoggerService).log(anyString(), eq(\"updateProcessDeploymentInfo\"), ArgumentMatchers\n                .<SQueriableLog> argThat(log -> log.getRawMessage().equals(string1024.substring(0, 255))));\n    }\n\n    @Test\n    public void updateLastUpdateDateInCache_should_update_the_lastUpdateDate_in_cache_if_exists() throws Exception {\n        // Given\n        final SProcessDefinitionDeployInfo sProcessDefinitionDeployInfo = mock(SProcessDefinitionDeployInfo.class);\n        doReturn(5478L).when(sProcessDefinitionDeployInfo).getLastUpdateDate();\n        final SProcessDefinitionImpl sProcessDefinition = new SProcessDefinitionImpl(\"a\", \"b\");\n        sProcessDefinition.setId(56L);\n        doReturn(new Pair<Long, SProcessDefinition>(541L, sProcessDefinition)).when(cacheService).get(anyString(),\n                any());\n        // When\n        processDefinitionServiceImpl.updateSProcessDefinitionTimestampInCache(56L, sProcessDefinitionDeployInfo);\n        // Then\n        verify(cacheService).store(ProcessDefinitionService.PROCESS_CACHE_NAME, 56L,\n                new Pair<Long, SProcessDefinition>(5478L, sProcessDefinition));\n    }\n\n    @Test\n    public void updateLastUpdateDateInCache_should_update_the_lastUpdateDate_in_cache_if_not_exists() throws Exception {\n        // Given\n        final SProcessDefinitionDeployInfo sProcessDefinitionDeployInfo = mock(SProcessDefinitionDeployInfo.class);\n        // When\n        processDefinitionServiceImpl.updateSProcessDefinitionTimestampInCache(3L, sProcessDefinitionDeployInfo);\n        // Then\n        verify(cacheService, times(0)).store(anyString(), any(Serializable.class), any());\n    }\n\n    @Test(expected = SProcessDefinitionNotFoundException.class)\n    public final void updateProcessDefinitionDeployInfoNotExists() throws Exception {\n        // Given\n        final SProcessDefinitionDeployInfoUpdateBuilder updateBuilder = BuilderFactory\n                .get(SProcessDefinitionDeployInfoUpdateBuilderFactory.class)\n                .createNewInstance();\n        doReturn(null).when(persistenceService)\n                .selectOne(ArgumentMatchers.<SelectOneDescriptor<SProcessDefinitionDeployInfo>> any());\n\n        // When\n        processDefinitionServiceImpl.updateProcessDefinitionDeployInfo(4, updateBuilder.done());\n    }\n\n    @Test(expected = SProcessDeploymentInfoUpdateException.class)\n    public final void updateProcessDefinitionDeployInfoThrowException() throws Exception {\n        // Given\n        final SProcessDefinitionDeployInfo sProcessDefinitionDeployInfo = mock(SProcessDefinitionDeployInfo.class);\n\n        final SProcessDefinitionDeployInfoUpdateBuilder updateBuilder = BuilderFactory\n                .get(SProcessDefinitionDeployInfoUpdateBuilderFactory.class)\n                .createNewInstance();\n        updateBuilder.updateDisplayName(\"newDisplayName\");\n\n        doReturn(sProcessDefinitionDeployInfo).when(persistenceService).selectOne(any());\n        doThrow(new SRecorderException(\"plop\")).when(recorder).recordUpdate(any(UpdateRecord.class),\n                nullable(String.class));\n\n        // When\n        processDefinitionServiceImpl.updateProcessDefinitionDeployInfo(3, updateBuilder.done());\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#searchProcessDeploymentInfosStartedBy(long, org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public void searchProcessDeploymentInfosStartedBy() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long startedBy = 9;\n        when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, \"StartedBy\", options,\n                Collections.singletonMap(\"startedBy\", (Object) startedBy)))\n                .thenReturn(new ArrayList<SProcessDefinitionDeployInfo>());\n\n        // When\n        final List<SProcessDefinitionDeployInfo> result = processDefinitionServiceImpl\n                .searchProcessDeploymentInfosStartedBy(startedBy, options);\n\n        // Then\n        assertNotNull(result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void searchProcessDeploymentInfosStartedByThrowException() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long startedBy = 9;\n        when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, \"StartedBy\", options,\n                Collections.singletonMap(\"startedBy\", (Object) startedBy))).thenThrow(new SBonitaReadException(\"\"));\n\n        // When\n        processDefinitionServiceImpl.searchProcessDeploymentInfosStartedBy(startedBy, options);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getNumberOfProcessDeploymentInfosStartedBy(long, org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public void getNumberOfProcessDeploymentInfosStartedBy() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long startedBy = 6;\n        when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, \"StartedBy\", options,\n                Collections.singletonMap(\"startedBy\", (Object) startedBy))).thenReturn(1L);\n\n        // When\n        final long result = processDefinitionServiceImpl.getNumberOfProcessDeploymentInfosStartedBy(startedBy, options);\n\n        // Then\n        assertEquals(1L, result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getNumberOfProcessDeploymentInfosStartedByThrowException() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long startedBy = 6;\n        when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, \"StartedBy\", options,\n                Collections.singletonMap(\"startedBy\", (Object) startedBy))).thenThrow(new SBonitaReadException(\"\"));\n\n        // When\n        processDefinitionServiceImpl.getNumberOfProcessDeploymentInfosStartedBy(startedBy, options);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#searchProcessDeploymentInfos(org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public void searchProcessDeploymentInfos() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, options, null))\n                .thenReturn(new ArrayList<SProcessDefinitionDeployInfo>());\n\n        // When\n        final List<SProcessDefinitionDeployInfo> result = processDefinitionServiceImpl\n                .searchProcessDeploymentInfos(options);\n\n        // Then\n        assertNotNull(result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void searchProcessDeploymentInfosThrowException() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, options, null))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        // When\n        processDefinitionServiceImpl.searchProcessDeploymentInfos(options);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getNumberOfProcessDeploymentInfos(org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public void getNumberOfProcessDeploymentInfosByOptions() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, options, null)).thenReturn(1L);\n\n        // When\n        final long result = processDefinitionServiceImpl.getNumberOfProcessDeploymentInfos(options);\n\n        // Then\n        assertEquals(1L, result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getNumberOfProcessDeploymentInfosByOptionsThrowException() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, options, null))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        // When\n        processDefinitionServiceImpl.getNumberOfProcessDeploymentInfos(options);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#searchProcessDeploymentInfosCanBeStartedBy(long, org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public void searchProcessDeploymentInfosCanBeStartedBy() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long userId = 9;\n        when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, \"UserCanStart\", options,\n                Collections.singletonMap(\"userId\", (Object) userId)))\n                .thenReturn(new ArrayList<SProcessDefinitionDeployInfo>());\n\n        // When\n        final List<SProcessDefinitionDeployInfo> result = processDefinitionServiceImpl\n                .searchProcessDeploymentInfosCanBeStartedBy(userId, options);\n\n        // Then\n        assertNotNull(result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void searchProcessDeploymentInfosCanBeStartedByThrowException() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long userId = 9;\n        when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, \"UserCanStart\", options,\n                Collections.singletonMap(\"userId\", (Object) userId))).thenThrow(new SBonitaReadException(\"\"));\n\n        // When\n        processDefinitionServiceImpl.searchProcessDeploymentInfosCanBeStartedBy(userId, options);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getNumberOfProcessDeploymentInfosCanBeStartedBy(long, org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public void getNumberOfProcessDeploymentInfosCanBeStartedBy() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long userId = 9;\n        when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, \"UserCanStart\", options,\n                Collections.singletonMap(\"userId\", (Object) userId))).thenReturn(1L);\n\n        // When\n        final long result = processDefinitionServiceImpl.getNumberOfProcessDeploymentInfosCanBeStartedBy(userId,\n                options);\n\n        // Then\n        assertEquals(1L, result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getNumberOfProcessDeploymentInfosCanBeStartedByThrowException() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long userId = 9;\n        when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, \"UserCanStart\", options,\n                Collections.singletonMap(\"userId\", (Object) userId))).thenThrow(new SBonitaReadException(\"\"));\n\n        // When\n        processDefinitionServiceImpl.getNumberOfProcessDeploymentInfosCanBeStartedBy(userId, options);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#searchProcessDeploymentInfosCanBeStartedByUsersManagedBy(long, org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public void searchProcessDeploymentInfosCanStartForUsersManagedBy() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long userId = 9;\n        when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, \"UsersManagedByCanStart\", options,\n                Collections.singletonMap(\"managerUserId\", (Object) userId)))\n                .thenReturn(new ArrayList<SProcessDefinitionDeployInfo>());\n\n        // When\n        final List<SProcessDefinitionDeployInfo> result = processDefinitionServiceImpl\n                .searchProcessDeploymentInfosCanBeStartedByUsersManagedBy(userId, options);\n\n        // Then\n        assertNotNull(result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void searchProcessDeploymentInfosCanBeStartedByUsersManagedByThrowException() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long userId = 9;\n        when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, \"UsersManagedByCanStart\", options,\n                Collections.singletonMap(\"managerUserId\", (Object) userId))).thenThrow(new SBonitaReadException(\"\"));\n\n        // When\n        processDefinitionServiceImpl.searchProcessDeploymentInfosCanBeStartedByUsersManagedBy(userId, options);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getNumberOfProcessDeploymentInfosCanBeStartedByUsersManagedBy(long, org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public void getNumberOfProcessDeploymentInfosCanBeStartedByUsersManagedBy() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long userId = 9;\n        when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, \"UsersManagedByCanStart\",\n                options,\n                Collections.singletonMap(\"managerUserId\", (Object) userId))).thenReturn(1L);\n\n        // When\n        final long result = processDefinitionServiceImpl\n                .getNumberOfProcessDeploymentInfosCanBeStartedByUsersManagedBy(userId, options);\n\n        // Then\n        assertEquals(1L, result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getNumberOfProcessDeploymentInfosCanBeStartedByUsersManagedByThrowException() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long userId = 9;\n        when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, \"UsersManagedByCanStart\",\n                options,\n                Collections.singletonMap(\"managerUserId\", (Object) userId))).thenThrow(new SBonitaReadException(\"\"));\n\n        // When\n        processDefinitionServiceImpl.getNumberOfProcessDeploymentInfosCanBeStartedByUsersManagedBy(userId, options);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#searchProcessDeploymentInfos(long, org.bonitasoft.engine.persistence.QueryOptions, java.lang.String)}\n     * .\n     */\n    @Test\n    public void searchProcessDeploymentInfosWithParameters() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long userId = 9;\n        final String querySuffix = \"suffix\";\n        when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, querySuffix, options,\n                Collections.singletonMap(\"userId\", (Object) userId)))\n                .thenReturn(new ArrayList<SProcessDefinitionDeployInfo>());\n\n        // When\n        final List<SProcessDefinitionDeployInfo> result = processDefinitionServiceImpl\n                .searchProcessDeploymentInfos(userId, options, querySuffix);\n\n        // Then\n        assertNotNull(result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void searchProcessDeploymentInfosWithParametersThrowException() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long userId = 9;\n        final String querySuffix = \"suffix\";\n        when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, querySuffix, options,\n                Collections.singletonMap(\"userId\", (Object) userId))).thenThrow(new SBonitaReadException(\"\"));\n\n        // When\n        processDefinitionServiceImpl.searchProcessDeploymentInfos(userId, options, querySuffix);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getNumberOfProcessDeploymentInfos(long, org.bonitasoft.engine.persistence.QueryOptions, java.lang.String)}\n     * .\n     */\n    @Test\n    public void getNumberOfProcessDeploymentInfosWithParameters() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long userId = 9;\n        final String querySuffix = \"suffix\";\n        when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, querySuffix, options,\n                Collections.singletonMap(\"userId\", (Object) userId))).thenReturn(1L);\n\n        // When\n        final long result = processDefinitionServiceImpl.getNumberOfProcessDeploymentInfos(userId, options,\n                querySuffix);\n\n        // Then\n        assertEquals(1L, result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getNumberOfProcessDeploymentInfosWithParametersThrowException() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long userId = 9;\n        final String querySuffix = \"suffix\";\n        when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, querySuffix, options,\n                Collections.singletonMap(\"userId\", (Object) userId))).thenThrow(new SBonitaReadException(\"\"));\n\n        // When\n        processDefinitionServiceImpl.getNumberOfProcessDeploymentInfos(userId, options, querySuffix);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#searchUncategorizedProcessDeploymentInfos(org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public void searchUncategorizedProcessDeploymentInfos() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, \"Uncategorized\", options, null))\n                .thenReturn(\n                        new ArrayList<SProcessDefinitionDeployInfo>());\n\n        // When\n        final List<SProcessDefinitionDeployInfo> result = processDefinitionServiceImpl\n                .searchUncategorizedProcessDeploymentInfos(options);\n\n        // Then\n        assertNotNull(result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void searchUncategorizedProcessDeploymentInfosThrowException() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, \"Uncategorized\", options, null))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        // When\n        processDefinitionServiceImpl.searchUncategorizedProcessDeploymentInfos(options);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getNumberOfUncategorizedProcessDeploymentInfos(org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public void getNumberOfUncategorizedProcessDeploymentInfos() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, \"Uncategorized\", options, null))\n                .thenReturn(1L);\n\n        // When\n        final long result = processDefinitionServiceImpl.getNumberOfUncategorizedProcessDeploymentInfos(options);\n\n        // Then\n        assertEquals(1L, result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getNumberOfUncategorizedProcessDeploymentInfosThrowException() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, \"Uncategorized\", options, null))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        // When\n        processDefinitionServiceImpl.getNumberOfUncategorizedProcessDeploymentInfos(options);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#searchUncategorizedProcessDeploymentInfosSupervisedBy(long, org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public void searchUncategorizedProcessDeploymentInfosSupervisedBy() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long userId = 9;\n        when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, \"UncategorizedAndWithSupervisor\",\n                options,\n                Collections.singletonMap(\"userId\", (Object) userId)))\n                .thenReturn(new ArrayList<SProcessDefinitionDeployInfo>());\n\n        // When\n        final List<SProcessDefinitionDeployInfo> result = processDefinitionServiceImpl\n                .searchUncategorizedProcessDeploymentInfosSupervisedBy(userId, options);\n\n        // Then\n        assertNotNull(result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void searchUncategorizedProcessDeploymentInfosSupervisedByThrowException() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long userId = 9;\n        when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, \"UncategorizedAndWithSupervisor\",\n                options,\n                Collections.singletonMap(\"userId\", (Object) userId))).thenThrow(new SBonitaReadException(\"\"));\n\n        // When\n        processDefinitionServiceImpl.searchUncategorizedProcessDeploymentInfosSupervisedBy(userId, options);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getNumberOfUncategorizedProcessDeploymentInfosSupervisedBy(long, org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public void getNumberOfUncategorizedProcessDeploymentInfosSupervisedBy() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long userId = 9;\n        when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class,\n                \"UncategorizedAndWithSupervisor\", options,\n                Collections.singletonMap(\"userId\", (Object) userId))).thenReturn(1L);\n\n        // When\n        final long result = processDefinitionServiceImpl\n                .getNumberOfUncategorizedProcessDeploymentInfosSupervisedBy(userId, options);\n\n        // Then\n        assertEquals(1L, result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getNumberOfUncategorizedProcessDeploymentInfosSupervisedByThrowException() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long userId = 9;\n        when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class,\n                \"UncategorizedAndWithSupervisor\", options,\n                Collections.singletonMap(\"userId\", (Object) userId))).thenThrow(new SBonitaReadException(\"\"));\n\n        // When\n        processDefinitionServiceImpl.getNumberOfUncategorizedProcessDeploymentInfosSupervisedBy(userId, options);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#searchUncategorizedProcessDeploymentInfosCanBeStartedBy(long, org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public void searchUncategorizedProcessDeploymentInfosCanBeStartedBy() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long userId = 9;\n        when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, \"UncategorizedUserCanStart\", options,\n                Collections.singletonMap(\"userId\", (Object) userId)))\n                .thenReturn(new ArrayList<SProcessDefinitionDeployInfo>());\n\n        // When\n        final List<SProcessDefinitionDeployInfo> result = processDefinitionServiceImpl\n                .searchUncategorizedProcessDeploymentInfosCanBeStartedBy(userId, options);\n\n        // Then\n        assertNotNull(result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void searchUncategorizedProcessDeploymentInfosCanBeStartedByThrowException() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long userId = 9;\n        when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, \"UncategorizedUserCanStart\", options,\n                Collections.singletonMap(\"userId\", (Object) userId))).thenThrow(new SBonitaReadException(\"\"));\n\n        // When\n        processDefinitionServiceImpl.searchUncategorizedProcessDeploymentInfosCanBeStartedBy(userId, options);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getNumberOfUncategorizedProcessDeploymentInfosCanBeStartedBy(long, org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public void getNumberOfUncategorizedProcessDeploymentInfosCanBeStartedBy() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long userId = 9;\n        when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, \"UncategorizedUserCanStart\",\n                options,\n                Collections.singletonMap(\"userId\", (Object) userId))).thenReturn(1L);\n\n        // When\n        final long result = processDefinitionServiceImpl\n                .getNumberOfUncategorizedProcessDeploymentInfosCanBeStartedBy(userId, options);\n\n        // Then\n        assertEquals(1L, result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getNumberOfUncategorizedProcessDeploymentInfosCanBeStartedByThrowException() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long userId = 9;\n        when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, \"UncategorizedUserCanStart\",\n                options,\n                Collections.singletonMap(\"userId\", (Object) userId))).thenThrow(new SBonitaReadException(\"\"));\n\n        // When\n        processDefinitionServiceImpl.getNumberOfUncategorizedProcessDeploymentInfosCanBeStartedBy(userId, options);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getProcessDeploymentInfosUnrelatedToCategory(long, int, int, org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion)}\n     * .\n     */\n    @Test\n    public void getProcessDeploymentInfosUnrelatedToCategory() throws Exception {\n        // Given\n        final List<SProcessDefinitionDeployInfo> sProcessDefinitionDeployInfos = new ArrayList<>(3);\n        doReturn(sProcessDefinitionDeployInfos).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SProcessDefinitionDeployInfo>> any());\n\n        // When\n        final List<SProcessDefinitionDeployInfo> processDeploymentInfos = processDefinitionServiceImpl\n                .getProcessDeploymentInfosUnrelatedToCategory(9, 0, 10,\n                        ProcessDeploymentInfoCriterion.ACTIVATION_STATE_ASC);\n\n        // Then\n        assertEquals(sProcessDefinitionDeployInfos, processDeploymentInfos);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getProcessDeploymentInfosUnrelatedToCategoryThrowException() throws Exception {\n        // Given\n        doThrow(new SBonitaReadException(\"plop\")).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SProcessDefinitionDeployInfo>> any());\n\n        // When\n        processDefinitionServiceImpl.getProcessDeploymentInfosUnrelatedToCategory(9, 0, 10,\n                ProcessDeploymentInfoCriterion.ACTIVATION_STATE_ASC);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getNumberOfProcessDeploymentInfosUnrelatedToCategory(long)}.\n     */\n    @Test\n    public void getNumberOfProcessDeploymentInfosUnrelatedToCategory() throws Exception {\n        // Given\n        final long numberOfProcessDeploymentInfos = 9;\n        doReturn(numberOfProcessDeploymentInfos).when(persistenceService)\n                .selectOne(ArgumentMatchers.<SelectOneDescriptor<Long>> any());\n\n        // When\n        final long result = processDefinitionServiceImpl.getNumberOfProcessDeploymentInfosUnrelatedToCategory(9);\n\n        // Then\n        assertEquals(numberOfProcessDeploymentInfos, result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getNumberOfProcessDeploymentInfosUnrelatedToCategoryThrowException() throws Exception {\n        // Given\n        doThrow(new SBonitaReadException(\"plop\")).when(persistenceService)\n                .selectOne(ArgumentMatchers.<SelectOneDescriptor<Long>> any());\n\n        // When\n        processDefinitionServiceImpl.getNumberOfProcessDeploymentInfosUnrelatedToCategory(9);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#searchProcessDeploymentInfosOfCategory(long, org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public void searchProcessDeploymentInfosOfCategory() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final List<SProcessDefinitionDeployInfo> sProcessDefinitionDeployInfos = new ArrayList<>(3);\n        doReturn(sProcessDefinitionDeployInfos).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SProcessDefinitionDeployInfo>> any());\n\n        // When\n        final List<SProcessDefinitionDeployInfo> processDeploymentInfos = processDefinitionServiceImpl\n                .searchProcessDeploymentInfosOfCategory(9, options);\n\n        // Then\n        assertEquals(sProcessDefinitionDeployInfos, processDeploymentInfos);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void searchProcessDeploymentInfosOfCategoryThrowException() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        doThrow(new SBonitaReadException(\"plop\")).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SProcessDefinitionDeployInfo>> any());\n\n        // When\n        processDefinitionServiceImpl.searchProcessDeploymentInfosOfCategory(9, options);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getProcessDeploymentInfos(org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public void getProcessDeploymentInfosWithOptions() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final List<SProcessDefinitionDeployInfo> sProcessDefinitionDeployInfos = new ArrayList<>(3);\n        doReturn(sProcessDefinitionDeployInfos).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SProcessDefinitionDeployInfo>> any());\n\n        // When\n        final List<SProcessDefinitionDeployInfo> processDeploymentInfos = processDefinitionServiceImpl\n                .getProcessDeploymentInfos(options);\n\n        // Then\n        assertEquals(sProcessDefinitionDeployInfos, processDeploymentInfos);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getProcessDeploymentInfosWithOptionsThrowException() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        doThrow(new SBonitaReadException(\"plop\")).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SProcessDefinitionDeployInfo>> any());\n\n        // When\n        processDefinitionServiceImpl.getProcessDeploymentInfos(options);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getProcessDeploymentInfosWithActorOnlyForGroup(long, org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public void getProcessDeploymentInfosWithActorOnlyForGroup() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final List<SProcessDefinitionDeployInfo> sProcessDefinitionDeployInfos = new ArrayList<>(3);\n        doReturn(sProcessDefinitionDeployInfos).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SProcessDefinitionDeployInfo>> any());\n\n        // When\n        final List<SProcessDefinitionDeployInfo> processDeploymentInfos = processDefinitionServiceImpl\n                .getProcessDeploymentInfosWithActorOnlyForGroup(9,\n                        options);\n\n        // Then\n        assertEquals(sProcessDefinitionDeployInfos, processDeploymentInfos);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getProcessDeploymentInfosWithActorOnlyForGroupThrowException() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        doThrow(new SBonitaReadException(\"plop\")).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SProcessDefinitionDeployInfo>> any());\n\n        // When\n        processDefinitionServiceImpl.getProcessDeploymentInfosWithActorOnlyForGroup(9, options);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getProcessDeploymentInfosWithActorOnlyForGroups(java.util.List, org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public void getProcessDeploymentInfosWithActorOnlyForGroups() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final List<SProcessDefinitionDeployInfo> sProcessDefinitionDeployInfos = new ArrayList<>(3);\n        doReturn(sProcessDefinitionDeployInfos).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SProcessDefinitionDeployInfo>> any());\n\n        // When\n        final List<SProcessDefinitionDeployInfo> processDeploymentInfos = processDefinitionServiceImpl\n                .getProcessDeploymentInfosWithActorOnlyForGroups(\n                        Arrays.asList(9L), options);\n\n        // Then\n        assertEquals(sProcessDefinitionDeployInfos, processDeploymentInfos);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getProcessDeploymentInfosWithActorOnlyForGroupsThrowException() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        doThrow(new SBonitaReadException(\"plop\")).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SProcessDefinitionDeployInfo>> any());\n\n        // When\n        processDefinitionServiceImpl.getProcessDeploymentInfosWithActorOnlyForGroups(Arrays.asList(9L), options);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getProcessDeploymentInfosWithActorOnlyForRole(long, org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public void getProcessDeploymentInfosWithActorOnlyForRole() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final List<SProcessDefinitionDeployInfo> sProcessDefinitionDeployInfos = new ArrayList<>(3);\n        doReturn(sProcessDefinitionDeployInfos).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SProcessDefinitionDeployInfo>> any());\n\n        // When\n        final List<SProcessDefinitionDeployInfo> processDeploymentInfos = processDefinitionServiceImpl\n                .getProcessDeploymentInfosWithActorOnlyForRole(9,\n                        options);\n\n        // Then\n        assertEquals(sProcessDefinitionDeployInfos, processDeploymentInfos);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getProcessDeploymentInfosWithActorOnlyForRoleThrowException() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        doThrow(new SBonitaReadException(\"plop\")).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SProcessDefinitionDeployInfo>> any());\n\n        // When\n        processDefinitionServiceImpl.getProcessDeploymentInfosWithActorOnlyForRole(9, options);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getProcessDeploymentInfosWithActorOnlyForRoles(java.util.List, org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public void getProcessDeploymentInfosWithActorOnlyForRoles() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final List<SProcessDefinitionDeployInfo> sProcessDefinitionDeployInfos = new ArrayList<>(3);\n        doReturn(sProcessDefinitionDeployInfos).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SProcessDefinitionDeployInfo>> any());\n\n        // When\n        final List<SProcessDefinitionDeployInfo> processDeploymentInfos = processDefinitionServiceImpl\n                .getProcessDeploymentInfosWithActorOnlyForRoles(\n                        Arrays.asList(9L), options);\n\n        // Then\n        assertEquals(sProcessDefinitionDeployInfos, processDeploymentInfos);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getProcessDeploymentInfosWithActorOnlyForRolesThrowException() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        doThrow(new SBonitaReadException(\"plop\")).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SProcessDefinitionDeployInfo>> any());\n\n        // When\n        processDefinitionServiceImpl.getProcessDeploymentInfosWithActorOnlyForRoles(Arrays.asList(9L), options);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getProcessDeploymentInfosWithActorOnlyForUser(long, org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public void getProcessDeploymentInfosWithActorOnlyForUser() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final List<SProcessDefinitionDeployInfo> sProcessDefinitionDeployInfos = new ArrayList<>(3);\n        doReturn(sProcessDefinitionDeployInfos).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SProcessDefinitionDeployInfo>> any());\n\n        // When\n        final List<SProcessDefinitionDeployInfo> processDeploymentInfos = processDefinitionServiceImpl\n                .getProcessDeploymentInfosWithActorOnlyForUser(9,\n                        options);\n\n        // Then\n        assertEquals(sProcessDefinitionDeployInfos, processDeploymentInfos);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getProcessDeploymentInfosWithActorOnlyForUserThrowException() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        doThrow(new SBonitaReadException(\"plop\")).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SProcessDefinitionDeployInfo>> any());\n\n        // When\n        processDefinitionServiceImpl.getProcessDeploymentInfosWithActorOnlyForUser(9, options);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getProcessDeploymentInfosWithActorOnlyForUsers(java.util.List, org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public void getProcessDeploymentInfosWithActorOnlyForUsers() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final List<SProcessDefinitionDeployInfo> sProcessDefinitionDeployInfos = new ArrayList<>(3);\n        doReturn(sProcessDefinitionDeployInfos).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SProcessDefinitionDeployInfo>> any());\n\n        // When\n        final List<SProcessDefinitionDeployInfo> processDeploymentInfos = processDefinitionServiceImpl\n                .getProcessDeploymentInfosWithActorOnlyForUsers(\n                        Arrays.asList(9L), options);\n\n        // Then\n        assertEquals(sProcessDefinitionDeployInfos, processDeploymentInfos);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getProcessDeploymentInfosWithActorOnlyForUsersThrowException() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        doThrow(new SBonitaReadException(\"plop\")).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SProcessDefinitionDeployInfo>> any());\n\n        // When\n        processDefinitionServiceImpl.getProcessDeploymentInfosWithActorOnlyForUsers(Arrays.asList(9L), options);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#searchUsersWhoCanStartProcessDeploymentInfo(long, org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public void searchUsersWhoCanStartProcessDeploymentInfo() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long processDefinitionId = 9;\n        when(persistenceService.searchEntity(SUser.class, \"WhoCanStartProcess\", options,\n                Collections.singletonMap(\"processId\", (Object) processDefinitionId)))\n                .thenReturn(new ArrayList<SUser>());\n\n        // When\n        final List<SUser> result = processDefinitionServiceImpl\n                .searchUsersWhoCanStartProcessDeploymentInfo(processDefinitionId, options);\n\n        // Then\n        assertNotNull(result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void searchUsersWhoCanStartProcessDeploymentInfoThrowException() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long processDefinitionId = 9;\n        when(persistenceService.searchEntity(SUser.class, \"WhoCanStartProcess\", options,\n                Collections.singletonMap(\"processId\", (Object) processDefinitionId)))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        // When\n        processDefinitionServiceImpl.searchUsersWhoCanStartProcessDeploymentInfo(processDefinitionId, options);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getNumberOfUsersWhoCanStartProcessDeploymentInfo(long, org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public void getNumberOfUsersWhoCanStartProcessDeploymentInfo() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long processDefinitionId = 9;\n        when(persistenceService.getNumberOfEntities(SUser.class, \"WhoCanStartProcess\", options,\n                Collections.singletonMap(\"processId\", (Object) processDefinitionId))).thenReturn(1L);\n\n        // When\n        final long result = processDefinitionServiceImpl\n                .getNumberOfUsersWhoCanStartProcessDeploymentInfo(processDefinitionId, options);\n\n        // Then\n        assertEquals(1L, result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getNumberOfUsersWhoCanStartProcessDeploymentInfoThrowException() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long processDefinitionId = 9;\n        when(persistenceService.getNumberOfEntities(SUser.class, \"WhoCanStartProcess\", options,\n                Collections.singletonMap(\"processId\", (Object) processDefinitionId)))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        // When\n        processDefinitionServiceImpl.getNumberOfUsersWhoCanStartProcessDeploymentInfo(processDefinitionId, options);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(long, org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long userId = 9;\n        when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, \"WithAssignedOrPendingHumanTasksFor\",\n                options,\n                Collections.singletonMap(\"userId\", (Object) userId)))\n                .thenReturn(new ArrayList<SProcessDefinitionDeployInfo>());\n\n        // When\n        final List<SProcessDefinitionDeployInfo> result = processDefinitionServiceImpl\n                .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(\n                        userId,\n                        options);\n\n        // Then\n        assertNotNull(result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksForThrowException() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long userId = 9;\n        when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, \"WithAssignedOrPendingHumanTasksFor\",\n                options,\n                Collections.singletonMap(\"userId\", (Object) userId))).thenThrow(new SBonitaReadException(\"\"));\n\n        // When\n        processDefinitionServiceImpl.searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(userId, options);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(long, org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long userId = 9;\n        when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class,\n                \"WithAssignedOrPendingHumanTasksFor\", options,\n                Collections.singletonMap(\"userId\", (Object) userId))).thenReturn(1L);\n\n        // When\n        final long result = processDefinitionServiceImpl\n                .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(userId, options);\n\n        // Then\n        assertEquals(1L, result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksForThrowException() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long userId = 9;\n        when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class,\n                \"WithAssignedOrPendingHumanTasksFor\", options,\n                Collections.singletonMap(\"userId\", (Object) userId))).thenThrow(new SBonitaReadException(\"\"));\n\n        // When\n        processDefinitionServiceImpl.getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(userId,\n                options);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(long, org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long userId = 9;\n        when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class,\n                \"WithAssignedOrPendingHumanTasksSupervisedBy\", options,\n                Collections.singletonMap(\"userId\", (Object) userId)))\n                .thenReturn(new ArrayList<SProcessDefinitionDeployInfo>());\n\n        // When\n        final List<SProcessDefinitionDeployInfo> result = processDefinitionServiceImpl\n                .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(\n                        userId,\n                        options);\n\n        // Then\n        assertNotNull(result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedByThrowException()\n            throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long userId = 9;\n        when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class,\n                \"WithAssignedOrPendingHumanTasksSupervisedBy\", options,\n                Collections.singletonMap(\"userId\", (Object) userId))).thenThrow(new SBonitaReadException(\"\"));\n\n        // When\n        processDefinitionServiceImpl.searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(userId,\n                options);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(long, org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long userId = 9;\n        when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class,\n                \"WithAssignedOrPendingHumanTasksSupervisedBy\", options,\n                Collections.singletonMap(\"userId\", (Object) userId))).thenReturn(1L);\n\n        // When\n        final long result = processDefinitionServiceImpl\n                .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(userId, options);\n\n        // Then\n        assertEquals(1L, result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedByThrowException()\n            throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long userId = 9;\n        when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class,\n                \"WithAssignedOrPendingHumanTasksSupervisedBy\", options,\n                Collections.singletonMap(\"userId\", (Object) userId))).thenThrow(new SBonitaReadException(\"\"));\n\n        // When\n        processDefinitionServiceImpl\n                .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(userId, options);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks(org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, \"WithAssignedOrPendingHumanTasks\",\n                options, null)).thenReturn(\n                        new ArrayList<SProcessDefinitionDeployInfo>());\n\n        // When\n        final List<SProcessDefinitionDeployInfo> result = processDefinitionServiceImpl\n                .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks(options);\n\n        // Then\n        assertNotNull(result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksThrowException() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, \"WithAssignedOrPendingHumanTasks\",\n                options, null)).thenThrow(\n                        new SBonitaReadException(\"\"));\n\n        // When\n        processDefinitionServiceImpl.searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks(options);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasks(org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasks() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class,\n                \"WithAssignedOrPendingHumanTasks\", options, null)).thenReturn(1L);\n\n        // When\n        final long result = processDefinitionServiceImpl\n                .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasks(options);\n\n        // Then\n        assertEquals(1L, result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksThrowException() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class,\n                \"WithAssignedOrPendingHumanTasks\", options, null)).thenThrow(\n                        new SBonitaReadException(\"\"));\n\n        // When\n        processDefinitionServiceImpl.getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasks(options);\n    }\n\n    @Test(expected = SObjectModificationException.class)\n    public void updateExpressionContentShouldThrowProcessNotFoundIfReadException() throws Exception {\n        final long processDefinitionId = 415L;\n        doThrow(SBonitaReadException.class).when(processDefinitionServiceImpl)\n                .getProcessDeploymentInfo(processDefinitionId);\n\n        processDefinitionServiceImpl.updateExpressionContent(processDefinitionId, 77L, \"string\");\n    }\n\n    @Test(expected = SObjectModificationException.class)\n    public void updateExpressionContentShouldThrowObjectModificationIfUpdateException() throws Exception {\n        final long processDefinitionId = 415L;\n        final DesignProcessDefinition designProcessDefinition = mock(DesignProcessDefinition.class);\n        doReturn(designProcessDefinition).when(processDefinitionServiceImpl)\n                .getDesignProcessDefinition(processDefinitionId);\n\n        processDefinitionServiceImpl.updateExpressionContent(processDefinitionId, 77L, \"string\");\n    }\n\n    @Test(expected = SObjectModificationException.class)\n    public void updateExpressionContentShouldThrowObjectModificationIfExpressionNotFound() throws Exception {\n        final long processDefinitionId = 415L;\n        final long expressionDefinitionId = 77L;\n        final DesignProcessDefinition designProcessDefinition = mock(DesignProcessDefinition.class);\n        doReturn(designProcessDefinition).when(processDefinitionServiceImpl)\n                .getDesignProcessDefinition(processDefinitionId);\n        doReturn(null).when(processDefinitionServiceImpl).getExpression(designProcessDefinition,\n                expressionDefinitionId);\n\n        processDefinitionServiceImpl.updateExpressionContent(processDefinitionId, expressionDefinitionId, \"string\");\n    }\n\n    @Test(expected = SObjectModificationException.class)\n    public void updateExpressionContentShouldThrowObjectModificationUpdateFails() throws Exception {\n        final long processDefinitionId = 415L;\n        final long expressionDefinitionId = 77L;\n        final DesignProcessDefinition designProcessDefinition = mock(DesignProcessDefinition.class);\n        doReturn(designProcessDefinition).when(processDefinitionServiceImpl)\n                .getDesignProcessDefinition(processDefinitionId);\n        doReturn(mock(ExpressionImpl.class)).when(processDefinitionServiceImpl).getExpression(designProcessDefinition,\n                expressionDefinitionId);\n\n        processDefinitionServiceImpl.updateExpressionContent(processDefinitionId, expressionDefinitionId, \"string\");\n    }\n\n    @Test(expected = SProcessDefinitionNotFoundException.class)\n    public void getDesignProcessDefinition_Should_Throw_Exception_On_Unknown_Process()\n            throws SProcessDefinitionNotFoundException,\n            SBonitaReadException {\n        int processDefinitionId = 456;\n        doThrow(new SProcessDefinitionNotFoundException(\"impossible to find process\"))\n                .when(processDefinitionServiceImpl).getProcessDeploymentInfo(\n                        processDefinitionId);\n        processDefinitionServiceImpl.getDesignProcessDefinition(processDefinitionId);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getDesignProcessDefinition_Should_Throw_Exception_On_UnparsableContent() throws Exception {\n        int processDefinitionId = 123;\n        SProcessDefinitionDeployInfo processDefinitionDeployInfo = mock(SProcessDefinitionDeployInfo.class);\n        doReturn(processDefinitionDeployInfo).when(processDefinitionServiceImpl)\n                .getProcessDeploymentInfo(processDefinitionId);\n        SProcessDefinitionDesignContent processDefinitionDesignContent = mock(SProcessDefinitionDesignContent.class);\n        when(processDefinitionDeployInfo.getDesignContent()).thenReturn(processDefinitionDesignContent);\n        when(processDefinitionBARContribution.convertXmlToProcess(null))\n                .thenThrow(new IOException(\"impossible to parse content\"));\n        processDefinitionServiceImpl.getDesignProcessDefinition(processDefinitionId);\n    }\n\n    @Test\n    public void getDesignProcessDefinition_Should_return_XML_correctly() throws Exception {\n        int processDefinitionId = 123;\n        SProcessDefinitionDeployInfo processDefinitionDeployInfo = mock(SProcessDefinitionDeployInfo.class);\n        doReturn(processDefinitionDeployInfo).when(processDefinitionServiceImpl)\n                .getProcessDeploymentInfo(processDefinitionId);\n        SProcessDefinitionDesignContent processDefinitionDesignContent = mock(SProcessDefinitionDesignContent.class);\n        when(processDefinitionDeployInfo.getDesignContent()).thenReturn(processDefinitionDesignContent);\n        DesignProcessDefinition designProcessDefinition = mock(DesignProcessDefinition.class);\n        when(processDefinitionBARContribution.convertXmlToProcess(null)).thenReturn(designProcessDefinition);\n        DesignProcessDefinition designProcessDefinitionResult = processDefinitionServiceImpl\n                .getDesignProcessDefinition(processDefinitionId);\n        assertThat(designProcessDefinitionResult).isSameAs(designProcessDefinition);\n    }\n\n    @Test\n    public void updateShouldWorkForGroovyExpression() throws Exception {\n        final long expressionDefinitionId = 77L;\n        final DesignProcessDefinition designProcessDefinition = mock(DesignProcessDefinition.class);\n        doReturn(designProcessDefinition).when(processDefinitionServiceImpl).getDesignProcessDefinition(PROCESS_ID);\n        final ExpressionImpl expression = mock(ExpressionImpl.class);\n        doReturn(expression).when(processDefinitionServiceImpl).getExpression(designProcessDefinition,\n                expressionDefinitionId);\n        doReturn(\"someXMLContent\").when(processDefinitionServiceImpl).getProcessContent(designProcessDefinition);\n        doReturn(true).when(processDefinitionServiceImpl).isValidExpressionTypeToUpdate(nullable(String.class));\n\n        processDefinitionServiceImpl.updateExpressionContent(PROCESS_ID, expressionDefinitionId, \"string\");\n\n        verify(processDefinitionServiceImpl).updateProcessDefinitionDeployInfo(eq(PROCESS_ID),\n                any(EntityUpdateDescriptor.class),\n                eq(\"Update expression <77>, old content is <null>\"));\n    }\n\n    @Test(expected = SObjectModificationException.class)\n    public void updateShouldForbidExpressionsOfTypeDifferentFromGroovyAndConstant() throws Exception {\n        final long processDefinitionId = 415L;\n        final long expressionDefinitionId = 77L;\n        final DesignProcessDefinition designProcessDefinition = mock(DesignProcessDefinition.class);\n        doReturn(designProcessDefinition).when(processDefinitionServiceImpl)\n                .getDesignProcessDefinition(processDefinitionId);\n        final ExpressionImpl expression = mock(ExpressionImpl.class);\n        doReturn(ExpressionType.TYPE_VARIABLE.name()).when(expression).getExpressionType();\n        doReturn(expression).when(processDefinitionServiceImpl).getExpression(designProcessDefinition,\n                expressionDefinitionId);\n\n        processDefinitionServiceImpl.updateExpressionContent(processDefinitionId, expressionDefinitionId, \"string\");\n    }\n\n    @Test\n    public void isValidExpressionTypeShouldOnlySupportGroovyScriptAndConstant() throws Exception {\n        for (ExpressionType expressionType : ExpressionType.values()) {\n            final boolean isValid = processDefinitionServiceImpl.isValidExpressionTypeToUpdate(expressionType.name());\n            switch (expressionType) {\n                case TYPE_CONSTANT:\n                case TYPE_READ_ONLY_SCRIPT:\n                    assertThat(isValid).as(\"Expression of type \" + expressionType + \" should be valid for update\")\n                            .isTrue();\n                    break;\n                default:\n                    assertThat(isValid).as(\"Expression of type \" + expressionType + \" should NOT be valid for update\")\n                            .isFalse();\n            }\n        }\n    }\n\n    @Test\n    public void getLatestProcessDefinitionId_should_query_processes_order_by_deploymentDate_DESC() throws Exception {\n        // given:\n        final List<SProcessDefinitionDeployInfo> processes = Collections\n                .<SProcessDefinitionDeployInfo> singletonList(new SProcessDefinitionDeployInfo());\n        doReturn(processes).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SProcessDefinitionDeployInfo>> any());\n\n        // when:\n        processDefinitionServiceImpl.getLatestProcessDefinitionId(\"MySimpleProcess\");\n\n        // then:\n        final ArgumentCaptor<SelectListDescriptor> captor = ArgumentCaptor.forClass(SelectListDescriptor.class);\n        verify(persistenceService).selectList(captor.capture());\n        final OrderByOption orderByOption = captor.getValue().getQueryOptions().getOrderByOptions().get(0);\n        assertThat(orderByOption.getFieldName()).isEqualTo(\"deploymentDate\");\n        assertThat(orderByOption.getOrderByType()).isEqualTo(OrderByType.DESC);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/STypeBooleanValidationTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.Test;\n\npublic class STypeBooleanValidationTest {\n\n    @Test\n    public void boolean_are_valid() throws Exception {\n\n        boolean validation = SType.BOOLEAN.validate(true);\n\n        assertThat(validation).isTrue();\n    }\n\n    @Test\n    public void Boolean_are_valid() throws Exception {\n\n        boolean validation = SType.BOOLEAN.validate(Boolean.FALSE);\n\n        assertThat(validation).isTrue();\n    }\n\n    @Test\n    public void null_is_valid() throws Exception {\n\n        boolean validation = SType.BOOLEAN.validate(null);\n\n        assertThat(validation).isTrue();\n    }\n\n    @Test\n    public void other_type_are_not_valid() throws Exception {\n\n        boolean integerValidation = SType.BOOLEAN.validate(12);\n        assertThat(integerValidation).isFalse();\n\n        boolean stringValidation = SType.BOOLEAN.validate(\"false\");\n        assertThat(stringValidation).isFalse();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/STypeByteArrayValidationTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.ArrayList;\n\nimport org.junit.Test;\n\npublic class STypeByteArrayValidationTest {\n\n    @Test\n    public void byte_array_is_valid() throws Exception {\n        final boolean validation = SType.BYTE_ARRAY.validate(new byte[] { 0, 1, 0, 0, 1, 0, 1 });\n\n        assertThat(validation).isTrue();\n    }\n\n    @Test\n    public void Byte_array_is_invalid() throws Exception {\n        final boolean validation = SType.BYTE_ARRAY.validate(new Byte[0]);\n\n        assertThat(validation).isFalse();\n    }\n\n    @Test\n    public void Byte_list_is_invalid() throws Exception {\n        final boolean validation = SType.BYTE_ARRAY.validate(new ArrayList<Byte>());\n\n        assertThat(validation).isFalse();\n    }\n\n    @Test\n    public void null_is_valid() throws Exception {\n        final boolean validation = SType.BYTE_ARRAY.validate(null);\n\n        assertThat(validation).isTrue();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/STypeDateValidationTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.Date;\n\nimport org.junit.Test;\n\npublic class STypeDateValidationTest {\n\n    @Test\n    public void date_are_valid() throws Exception {\n\n        final boolean validation = SType.DATE.validate(new Date());\n\n        assertThat(validation).isTrue();\n    }\n\n    @Test\n    public void null_is_valid() throws Exception {\n\n        final boolean validation = SType.DATE.validate(null);\n\n        assertThat(validation).isTrue();\n    }\n\n    @Test\n    public void other_type_are_not_valid() throws Exception {\n\n        final boolean stringValidation = SType.DATE.validate(\"2014/08/09\");\n        assertThat(stringValidation).isFalse();\n\n        final boolean longValidation = SType.DATE.validate(1410862853708L);\n        assertThat(longValidation).isFalse();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/STypeDecimalValidationTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.math.BigDecimal;\n\nimport org.junit.Test;\n\npublic class STypeDecimalValidationTest {\n\n    @Test\n    public void float_are_valid() throws Exception {\n\n        boolean validation = SType.DECIMAL.validate(45.2f);\n\n        assertThat(validation).isTrue();\n    }\n\n    @Test\n    public void Float_are_valid() throws Exception {\n\n        boolean validation = SType.DECIMAL.validate(Float.valueOf(47.65f));\n\n        assertThat(validation).isTrue();\n    }\n\n    @Test\n    public void double_are_valid() throws Exception {\n\n        boolean validation = SType.DECIMAL.validate(5684.23d);\n\n        assertThat(validation).isTrue();\n    }\n\n    @Test\n    public void Double_are_valid() throws Exception {\n\n        boolean validation = SType.DECIMAL.validate(Double.valueOf(6548.236d));\n\n        assertThat(validation).isTrue();\n    }\n\n    @Test\n    public void null_is_valid() throws Exception {\n\n        boolean validation = SType.DECIMAL.validate(null);\n\n        assertThat(validation).isTrue();\n    }\n\n    @Test\n    public void BigDecimal_are_valid() throws Exception {\n\n        boolean validation = SType.DECIMAL.validate(BigDecimal.valueOf(1235.321d));\n\n        assertThat(validation).isTrue();\n    }\n\n    @Test\n    public void Integer_are_valid() throws Exception {\n\n        boolean intValidation = SType.DECIMAL.validate(53);\n\n        assertThat(intValidation).isTrue();\n    }\n\n    @Test\n    public void other_types_are_not_valid() throws Exception {\n\n        boolean stringValidation = SType.DECIMAL.validate(\"54\");\n        assertThat(stringValidation).isFalse();\n\n        boolean booleanValidation = SType.DECIMAL.validate(true);\n        assertThat(booleanValidation).isFalse();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/STypeIntegerValidationTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.math.BigInteger;\n\nimport org.junit.Test;\n\npublic class STypeIntegerValidationTest {\n\n    @Test\n    public void integer_are_valid() throws Exception {\n\n        boolean validation = SType.INTEGER.validate(12);\n\n        assertThat(validation).isTrue();\n    }\n\n    @Test\n    public void Integer_are_valid() throws Exception {\n\n        boolean validation = SType.INTEGER.validate(Integer.valueOf(12));\n\n        assertThat(validation).isTrue();\n    }\n\n    @Test\n    public void long_are_valid() throws Exception {\n\n        boolean validation = SType.INTEGER.validate(12l);\n\n        assertThat(validation).isTrue();\n    }\n\n    @Test\n    public void Long_are_valid() throws Exception {\n\n        boolean validation = SType.INTEGER.validate(Long.valueOf(12));\n\n        assertThat(validation).isTrue();\n    }\n\n    @Test\n    public void BigInteger_are_valid() throws Exception {\n\n        boolean validation = SType.INTEGER.validate(BigInteger.valueOf(45));\n\n        assertThat(validation).isTrue();\n    }\n\n    @Test\n    public void short_are_valid() throws Exception {\n\n        boolean validation = SType.INTEGER.validate((short) 65);\n\n        assertThat(validation).isTrue();\n    }\n\n    @Test\n    public void Short_are_valid() throws Exception {\n\n        boolean validation = SType.INTEGER.validate(Short.valueOf((short) 87));\n\n        assertThat(validation).isTrue();\n    }\n\n    @Test\n    public void byte_are_valid() throws Exception {\n\n        boolean validation = SType.INTEGER.validate((byte) 8);\n\n        assertThat(validation).isTrue();\n    }\n\n    @Test\n    public void Byte_are_valid() throws Exception {\n\n        boolean validation = SType.INTEGER.validate(Byte.valueOf((byte) 2));\n\n        assertThat(validation).isTrue();\n    }\n\n    @Test\n    public void null_is_valid() throws Exception {\n\n        boolean validation = SType.INTEGER.validate(null);\n\n        assertThat(validation).isTrue();\n    }\n\n    @Test\n    public void other_types_are_not_valid() throws Exception {\n\n        boolean stringValidation = SType.INTEGER.validate(\"54\");\n        assertThat(stringValidation).isFalse();\n\n        boolean doubleValidation = SType.INTEGER.validate(53.2d);\n        assertThat(doubleValidation).isFalse();\n\n        boolean booleanValidation = SType.INTEGER.validate(true);\n        assertThat(booleanValidation).isFalse();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/STypeLocalDateTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.time.LocalDate;\n\nimport org.junit.Test;\n\n/**\n * @author Danila Mazour\n */\npublic class STypeLocalDateTest {\n\n    @Test\n    public void localDate_are_valid() throws Exception {\n\n        boolean validation = SType.LOCALDATE.validate(LocalDate.now());\n\n        assertThat(validation).isTrue();\n    }\n\n    @Test\n    public void null_is_valid() throws Exception {\n\n        boolean validation = SType.LOCALDATE.validate(null);\n\n        assertThat(validation).isTrue();\n    }\n\n    @Test\n    public void other_type_are_not_valid() throws Exception {\n\n        boolean integerValidation = SType.LOCALDATE.validate(12);\n        assertThat(integerValidation).isFalse();\n\n        boolean stringValidation = SType.LOCALDATE.validate(\"false\");\n        assertThat(stringValidation).isFalse();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/STypeLocalDateTimeTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.time.LocalDateTime;\n\nimport org.junit.Test;\n\n/**\n * @author Danila Mazour\n */\npublic class STypeLocalDateTimeTest {\n\n    @Test\n    public void localDateTime_are_valid() throws Exception {\n\n        boolean validation = SType.LOCALDATETIME.validate(LocalDateTime.now());\n\n        assertThat(validation).isTrue();\n    }\n\n    @Test\n    public void null_is_valid() throws Exception {\n\n        boolean validation = SType.LOCALDATETIME.validate(null);\n\n        assertThat(validation).isTrue();\n    }\n\n    @Test\n    public void other_type_are_not_valid() throws Exception {\n\n        boolean integerValidation = SType.LOCALDATETIME.validate(12);\n        assertThat(integerValidation).isFalse();\n\n        boolean stringValidation = SType.LOCALDATETIME.validate(\"false\");\n        assertThat(stringValidation).isFalse();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/STypeOffsetDateTimeTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.time.OffsetDateTime;\n\nimport org.junit.Test;\n\n/**\n * @author Danila Mazour\n */\npublic class STypeOffsetDateTimeTest {\n\n    @Test\n    public void offsetDateTime_are_valid() throws Exception {\n\n        boolean validation = SType.OFFSETDATETIME.validate(OffsetDateTime.now());\n\n        assertThat(validation).isTrue();\n    }\n\n    @Test\n    public void null_is_valid() throws Exception {\n\n        boolean validation = SType.OFFSETDATETIME.validate(null);\n\n        assertThat(validation).isTrue();\n    }\n\n    @Test\n    public void other_type_are_not_valid() throws Exception {\n\n        boolean integerValidation = SType.OFFSETDATETIME.validate(12);\n        assertThat(integerValidation).isFalse();\n\n        boolean stringValidation = SType.OFFSETDATETIME.validate(\"false\");\n        assertThat(stringValidation).isFalse();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/STypeTextValidationTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.Test;\n\npublic class STypeTextValidationTest {\n\n    @Test\n    public void string_are_valid() throws Exception {\n\n        boolean validation = SType.TEXT.validate(\"this is a String\");\n\n        assertThat(validation).isTrue();\n    }\n\n    @Test\n    public void character_are_valid() throws Exception {\n\n        boolean validation = SType.TEXT.validate('a');\n\n        assertThat(validation).isTrue();\n    }\n\n    @Test\n    public void null_is_valid() throws Exception {\n\n        boolean validation = SType.TEXT.validate(null);\n\n        assertThat(validation).isTrue();\n    }\n\n    @Test\n    public void other_types_are_not_valid() throws Exception {\n\n        boolean intValidation = SType.TEXT.validate(54);\n        assertThat(intValidation).isFalse();\n\n        boolean doubleValidation = SType.TEXT.validate(53.2d);\n        assertThat(doubleValidation).isFalse();\n\n        boolean booleanValidation = SType.TEXT.validate(true);\n        assertThat(booleanValidation).isFalse();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/builder/ServerModelConvertorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.core.process.definition.model.SBusinessDataDefinition;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionType;\nimport org.bonitasoft.engine.expression.impl.ExpressionImpl;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.expression.model.impl.SExpressionImpl;\nimport org.junit.Test;\n\npublic class ServerModelConvertorTest {\n\n    Expression buildClientExpression(final String name, final String content, final String expressionType,\n            final String returnType,\n            final List<SExpression> dependencies) {\n        final ExpressionImpl expression = new ExpressionImpl();\n        expression.setName(name);\n        expression.setContent(content);\n        expression.setExpressionType(expressionType);\n        expression.setReturnType(returnType);\n        return expression;\n    }\n\n    @Test\n    public void convert_a_business_query_to_a_server_one() {\n        final String name = \"expressionName\";\n        final String content = \"getEmployees\";\n        final String expressionType = ExpressionType.TYPE_QUERY_BUSINESS_DATA.name();\n        final String returnType = \"org.bonitasoft.Employee\";\n        final Expression clientExpression = buildClientExpression(name, content, expressionType, returnType, null);\n        final SExpressionImpl expected = new SExpressionImpl(name, content, expressionType, returnType, null,\n                Collections.<SExpression> emptyList());\n\n        final SExpression expression = ServerModelConvertor.convertExpression(clientExpression);\n\n        assertThat(expression).isEqualTo(expected);\n    }\n\n    @Test\n    public void return_null_when_converting_a_null_business_data() {\n        final SBusinessDataDefinition businessDataDefinition = ServerModelConvertor.convertBusinessDataDefinition(null);\n        assertThat(businessDataDefinition).isNull();\n    }\n\n    @Test\n    public void shouldConvertContractInputs() throws Exception {\n        final HashMap<String, Expression> contractInputs = new HashMap<>();\n        final ExpressionImpl expression1 = new ExpressionImpl();\n        expression1.setReturnType(\"SomeType\");\n        final ExpressionImpl expression2 = new ExpressionImpl();\n        expression2.setReturnType(\"SomeReturnType\");\n        contractInputs.put(\"inputname1\", expression1);\n        contractInputs.put(\"inputname2\", expression2);\n\n        final Map<String, SExpression> convertedContractInputs = ServerModelConvertor\n                .convertContractInputs(contractInputs);\n\n        assertThat(convertedContractInputs.get(\"inputname1\"))\n                .isEqualTo(ServerModelConvertor.convertExpression(expression1));\n        assertThat(convertedContractInputs.get(\"inputname2\"))\n                .isEqualTo(ServerModelConvertor.convertExpression(expression2));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/builder/impl/SBusinessDataDefinitionBuilderFactoryImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.builder.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.core.process.definition.model.builder.SBusinessDataDefinitionBuilderFactory;\nimport org.junit.Test;\n\npublic class SBusinessDataDefinitionBuilderFactoryImplTest {\n\n    @Test\n    public void getSBusinessDataDefinitionBuilderFactoryInterfaceShouldReturnsSBusinessDataDefinitionBuilderFactoryImpl() {\n        // when:\n        final SBusinessDataDefinitionBuilderFactory factory = BuilderFactory\n                .get(SBusinessDataDefinitionBuilderFactory.class);\n        // then:\n        assertThat(factory).isInstanceOf(SBusinessDataDefinitionBuilderFactoryImpl.class);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/event/impl/SBoundaryEventDefinitionImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.impl;\n\nimport static org.junit.Assert.assertTrue;\n\nimport org.junit.Test;\n\n/**\n * @author Celine Souchet\n */\npublic class SBoundaryEventDefinitionImplTest {\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.definition.model.impl.SFlowNodeDefinitionImpl#isBoundaryEvent()}.\n     */\n    @Test\n    public void boundary_if_is_a_boundary() {\n        final SBoundaryEventDefinitionImpl sBoundaryEventDefinitionImpl = new SBoundaryEventDefinitionImpl(6, \"name\");\n\n        assertTrue(sBoundaryEventDefinitionImpl.isBoundaryEvent());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/event/impl/SStartEventDefinitionImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.event.impl;\n\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertTrue;\n\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.SCatchErrorEventTriggerDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.impl.STransitionDefinitionImpl;\nimport org.junit.Test;\n\n/**\n * Created by Vincent Elcrin\n * Date: 18/12/13\n * Time: 14:50\n *\n * @author Celine Souchet\n */\npublic class SStartEventDefinitionImplTest {\n\n    SStartEventDefinitionImpl startEvent = new SStartEventDefinitionImpl(1L, \"name\");\n\n    @Test\n    public void isStartable_return_false_if_start_event_has_trigger_events() {\n        startEvent.addEventTriggerDefinition(new SCatchErrorEventTriggerDefinitionImpl(\"error\"));\n\n        assertFalse(startEvent.isStartable());\n    }\n\n    @Test\n    public void isStartable_return_false_if_start_event_has_incoming_transitions() {\n        startEvent.addIncomingTransition(new STransitionDefinitionImpl(\"incoming\"));\n\n        assertFalse(startEvent.isStartable());\n    }\n\n    @Test\n    public void isStartable_return_true_if_start_event_has_no_incoming_transitions_and_no_trigger_events() {\n        assertTrue(startEvent.isStartable());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/impl/SCatchEventDefinitionImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertTrue;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.definition.model.event.impl.SCatchEventDefinitionImpl;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class SCatchEventDefinitionImplTest {\n\n    private SCatchEventDefinitionImpl catchEvent;\n\n    @Before\n    public void before() {\n        catchEvent = new SCatchEventDefinitionImpl(9, \"name\") {\n\n            private static final long serialVersionUID = 8249595229324418282L;\n\n            @Override\n            public SFlowNodeType getType() {\n                return SFlowNodeType.AUTOMATIC_TASK;\n            }\n        };\n    }\n\n    @Test\n    public void is_interrupting_if_interrupting_catch_event() {\n        catchEvent.setInterrupting(true);\n\n        assertTrue(catchEvent.isInterrupting());\n    }\n\n    @Test\n    public void is_not_interrupting_if_non_interrupting_catch_event() {\n        catchEvent.setInterrupting(false);\n\n        assertFalse(catchEvent.isInterrupting());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/impl/SConstraintDefinitionImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.Test;\n\npublic class SConstraintDefinitionImplTest {\n\n    @Test\n    public void should_add_input_name() throws Exception {\n        //given\n        final SConstraintDefinitionImpl sConstraintDefinition = new SConstraintDefinitionImpl(\"name\", \"expression\",\n                \"explanation\");\n\n        //when\n        sConstraintDefinition.addInputName(\"inputName\");\n\n        //then\n        assertThat(sConstraintDefinition.getInputNames()).isNotNull().hasSize(1).containsExactly(\"inputName\");\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/impl/SFlowNodeDefinitionImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertTrue;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Vincent Elcrin\n * @author Celine Souchet\n */\npublic class SFlowNodeDefinitionImplTest {\n\n    private SFlowNodeDefinitionImpl flowNode;\n\n    @Before\n    public void before() {\n        flowNode = new SFlowNodeDefinitionImpl(1L, \"name\") {\n\n            private static final long serialVersionUID = -1297746953646018494L;\n\n            @Override\n            public SFlowNodeType getType() {\n                return null;\n            }\n        };\n    }\n\n    @Test\n    public void isStartable_return_false_if_flow_node_has_incoming_transitions() {\n        flowNode.addIncomingTransition(new STransitionDefinitionImpl(\"incoming\"));\n\n        assertFalse(flowNode.isStartable());\n    }\n\n    @Test\n    public void isStartable_return_true_if_flow_node_has_no_incoming_transitions() {\n        assertTrue(flowNode.isStartable());\n    }\n\n    @Test\n    public void is_not_interrupting_if_not_catch_event() {\n        assertFalse(flowNode.isInterrupting());\n    }\n\n    @Test\n    public void hasIncommingTransitions_return_true_if_flownode_hasIncommingTransitions() {\n        flowNode.addIncomingTransition(new STransitionDefinitionImpl(\"incoming\"));\n\n        assertTrue(flowNode.hasIncomingTransitions());\n    }\n\n    @Test\n    public void hasIncommingTransitions_return_false_if_flownode_doesnt_have_IncommingTransitions() {\n        assertFalse(flowNode.hasIncomingTransitions());\n    }\n\n    @Test\n    public void isEventSubProcess_return_false_if_is_not_sub_process() {\n        assertFalse(flowNode.isEventSubProcess());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/impl/SGatewayDefinitionImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertTrue;\n\nimport org.bonitasoft.engine.core.process.definition.model.SGatewayType;\nimport org.junit.Test;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class SGatewayDefinitionImplTest {\n\n    @Test\n    public void not_exclusive_if_parallel_gateway() {\n        final SGatewayDefinitionImpl gateway = new SGatewayDefinitionImpl(6, \"name\", SGatewayType.PARALLEL);\n\n        assertFalse(gateway.isExclusive());\n    }\n\n    @Test\n    public void not_exclusive_if_inclusive_gateway() {\n        final SGatewayDefinitionImpl gateway = new SGatewayDefinitionImpl(6, \"name\", SGatewayType.INCLUSIVE);\n\n        assertFalse(gateway.isExclusive());\n    }\n\n    @Test\n    public void exclusive_if_exclusive_gateway() {\n        final SGatewayDefinitionImpl gateway = new SGatewayDefinitionImpl(6, \"name\", SGatewayType.EXCLUSIVE);\n\n        assertTrue(gateway.isExclusive());\n    }\n\n    @Test\n    public void parallelOrInclusive_if_parallel_gateway() {\n        final SGatewayDefinitionImpl gateway = new SGatewayDefinitionImpl(6, \"name\", SGatewayType.PARALLEL);\n\n        assertTrue(gateway.isParalleleOrInclusive());\n    }\n\n    @Test\n    public void parallelOrInclusive_if_inclusive_gateway() {\n        final SGatewayDefinitionImpl gateway = new SGatewayDefinitionImpl(6, \"name\", SGatewayType.PARALLEL);\n\n        assertTrue(gateway.isParalleleOrInclusive());\n    }\n\n    @Test\n    public void not_parallelOrInclusive_if_exclusive_gateway() {\n        final SGatewayDefinitionImpl gateway = new SGatewayDefinitionImpl(6, \"name\", SGatewayType.EXCLUSIVE);\n\n        assertFalse(gateway.isParalleleOrInclusive());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/impl/SInputDefinitionImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.Arrays;\n\nimport org.bonitasoft.engine.bpm.contract.InputDefinition;\nimport org.bonitasoft.engine.bpm.contract.Type;\nimport org.bonitasoft.engine.bpm.contract.impl.InputDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.SInputDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SType;\nimport org.junit.Test;\n\npublic class SInputDefinitionImplTest {\n\n    private static final String DESCRIPTION = \"description\";\n    private static final String NAME = \"name\";\n\n    @Test\n    public void contructor_with_name() throws Exception {\n        //given\n        final SInputDefinitionImpl sInputDefinitionImpl = new SInputDefinitionImpl(NAME, SType.TEXT, DESCRIPTION);\n\n        //then\n        assertThat(sInputDefinitionImpl.isMultiple()).isFalse();\n    }\n\n    @Test\n    public void constructor_with_input_definition() throws Exception {\n        //given\n        final InputDefinition simpleInput = new InputDefinitionImpl(NAME, Type.TEXT, DESCRIPTION, true);\n\n        //when\n        final SInputDefinitionImpl sInputDefinitionImpl = new SInputDefinitionImpl(simpleInput);\n\n        //then\n        assertThat(sInputDefinitionImpl.isMultiple()).isTrue();\n        assertThat(sInputDefinitionImpl.getName()).isEqualTo(NAME);\n        assertThat(sInputDefinitionImpl.getDescription()).isEqualTo(DESCRIPTION);\n        assertThat(sInputDefinitionImpl.getType()).isEqualTo(SType.TEXT);\n\n    }\n\n    @Test\n    public void construtor_should_initialize_members() throws Exception {\n        //when\n        final SInputDefinitionImpl sComplexInputDefinitionImpl = new SInputDefinitionImpl(NAME, \"\");\n\n        //then\n        assertThat(sComplexInputDefinitionImpl.isMultiple()).isFalse();\n        assertThat(sComplexInputDefinitionImpl.getName()).isEqualTo(NAME);\n        assertThat(sComplexInputDefinitionImpl.getInputDefinitions()).isNotNull().isEmpty();\n\n    }\n\n    @Test\n    public void construtor_with_input_definition() throws Exception {\n        //given\n        final SInputDefinition name = new SInputDefinitionImpl(\"name\", SType.TEXT, DESCRIPTION);\n\n        final SInputDefinition city = new SInputDefinitionImpl(\"city\", SType.TEXT, DESCRIPTION);\n        final SInputDefinition zip = new SInputDefinitionImpl(\"zip\", SType.INTEGER, DESCRIPTION);\n        final SInputDefinition adress = new SInputDefinitionImpl(\"adress\", DESCRIPTION, false,\n                Arrays.asList(city, zip));\n\n        //when\n        final SInputDefinitionImpl sComplexInputDefinitionImpl = new SInputDefinitionImpl(NAME, DESCRIPTION, false,\n                Arrays.asList(name, adress));\n\n        //then\n        assertThat(sComplexInputDefinitionImpl.isMultiple()).isFalse();\n        assertThat(sComplexInputDefinitionImpl.getName()).isEqualTo(NAME);\n        assertThat(sComplexInputDefinitionImpl.getInputDefinitions()).isNotEmpty().containsExactly(name, adress);\n\n    }\n\n    @Test\n    public void construtor_with_inputDefinition() throws Exception {\n\n        //when\n        final SInputDefinitionImpl sComplexInputDefinitionImpl = new SInputDefinitionImpl(createComplexInputs());\n\n        //then\n        assertThat(sComplexInputDefinitionImpl.isMultiple()).isTrue();\n        assertThat(sComplexInputDefinitionImpl.getName()).isEqualTo(\"expense\");\n        assertThat(sComplexInputDefinitionImpl.getDescription()).isEqualTo(DESCRIPTION);\n\n        assertThat(sComplexInputDefinitionImpl.getInputDefinitions()).as(\"should contain name, amount and date\")\n                .isNotEmpty().hasSize(4);\n\n    }\n\n    private InputDefinition createComplexInputs() {\n        final InputDefinition name = new InputDefinitionImpl(\"name\", Type.TEXT, DESCRIPTION);\n        final InputDefinition amount = new InputDefinitionImpl(\"amount\", Type.DECIMAL, DESCRIPTION);\n        final InputDefinition date = new InputDefinitionImpl(\"date\", Type.DATE, DESCRIPTION);\n\n        final InputDefinition city = new InputDefinitionImpl(\"city\", Type.TEXT, DESCRIPTION);\n        final InputDefinition zip = new InputDefinitionImpl(\"zip\", Type.INTEGER, DESCRIPTION);\n\n        final InputDefinition adress = new InputDefinitionImpl(\"adress\", DESCRIPTION, Arrays.asList(city, zip));\n\n        final InputDefinition expense = new InputDefinitionImpl(\"expense\", DESCRIPTION, true,\n                Arrays.asList(name, amount, date, adress));\n        return expense;\n    }\n\n    @Test\n    public void constructor_without_multiple() throws Exception {\n        //given\n        final SInputDefinitionImpl sInputDefinitionImpl = new SInputDefinitionImpl(NAME, DESCRIPTION);\n\n        //then\n        assertThat(sInputDefinitionImpl.isMultiple()).as(\"should not be multiple\").isFalse();\n\n    }\n\n    @Test\n    public void constructor_with_description_without_multiple() throws Exception {\n        //given\n        final SInputDefinitionImpl sInputDefinitionImpl = new SInputDefinitionImpl(NAME, DESCRIPTION);\n\n        //then\n        assertThat(sInputDefinitionImpl.isMultiple()).as(\"should not be multiple\").isFalse();\n\n    }\n\n    @Test\n    public void constructor_with_multiple() throws Exception {\n        //given\n        final SInputDefinitionImpl sInputDefinitionImpl = new SInputDefinitionImpl(NAME, SType.BOOLEAN, DESCRIPTION,\n                true);\n\n        //then\n        assertThat(sInputDefinitionImpl.isMultiple()).as(\"should be multiple\").isTrue();\n\n    }\n\n    @Test\n    public void constructor_with_name_and_description() throws Exception {\n        //given\n        final SInputDefinitionImpl sInputDefinitionImpl = new SInputDefinitionImpl(NAME, DESCRIPTION);\n\n        //then\n        assertThat(sInputDefinitionImpl.getName()).as(\"should get name\").isEqualTo(NAME);\n        assertThat(sInputDefinitionImpl.getDescription()).as(\"should get name\").isEqualTo(DESCRIPTION);\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/impl/SSubProcessDefinitionImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertTrue;\n\nimport org.junit.Test;\n\n/**\n * @author Vincent Elcrin\n */\npublic class SSubProcessDefinitionImplTest {\n\n    SSubProcessDefinitionImpl subProcessTriggeredByEvent = new SSubProcessDefinitionImpl(1L, \"name\", true);\n\n    SSubProcessDefinitionImpl subProcess = new SSubProcessDefinitionImpl(1L, \"name\", false);\n\n    @Test\n    public void isStartable_return_false_if_sub_process_is_triggered_by_event() {\n        assertFalse(subProcessTriggeredByEvent.isStartable());\n    }\n\n    @Test\n    public void isStartable_return_false_if_sub_process_has_incoming_transitions() {\n        subProcess.addIncomingTransition(new STransitionDefinitionImpl(\"incoming\"));\n\n        assertFalse(subProcess.isStartable());\n    }\n\n    @Test\n    public void isStartable_return_true_if_sub_process_has_no_incoming_transitions_and_is_not_triggered_by_event() {\n        assertTrue(subProcess.isStartable());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/impl/STransitionDefinitionImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\n\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.TransitionDefinitionImpl;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.junit.Test;\n\npublic class STransitionDefinitionImplTest {\n\n    @Test\n    public void hasCondition_should_return_true_when_condition_is_not_null() throws Exception {\n        //given\n        STransitionDefinitionImpl transition = new STransitionDefinitionImpl(\"t\");\n        transition.setCondition(mock(SExpression.class));\n\n        //when\n        boolean hasCondition = transition.hasCondition();\n\n        //then\n        assertThat(hasCondition).isTrue();\n    }\n\n    @Test\n    public void hasCondition_should_return_false_when_condition_is_null() throws Exception {\n        //given\n        STransitionDefinitionImpl transition = new STransitionDefinitionImpl(\"t\");\n\n        //when\n        boolean hasCondition = transition.hasCondition();\n\n        //then\n        assertThat(hasCondition).isFalse();\n    }\n\n    @Test\n    public void constructor_should_correctly_set_the_id() {\n\n        //given\n        STransitionDefinitionImpl transition = new STransitionDefinitionImpl(new TransitionDefinitionImpl());\n\n        //when\n        long id = transition.getId();\n\n        //then\n        assertThat(id).isNotNull();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/impl/SUserTaskDefinitionImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.definition.model.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.core.process.definition.model.SType;\nimport org.junit.Test;\n\n/**\n * @author Celine Souchet\n */\npublic class SUserTaskDefinitionImplTest {\n\n    @Test\n    public void not_exclusive_if_not_gateway() {\n        final SUserTaskDefinitionImpl userTask = new SUserTaskDefinitionImpl(5, \"name\", \"actorName\");\n\n        assertThat(userTask.isExclusive()).isFalse();\n    }\n\n    @Test\n    public void not_parallelOrInclusive_if_not_gateway() {\n        final SUserTaskDefinitionImpl userTask = new SUserTaskDefinitionImpl(5, \"name\", \"actorName\");\n\n        assertThat(userTask.isParalleleOrInclusive()).isFalse();\n    }\n\n    @Test\n    public void aUserTaskWithANullContractReturnsAnEmptyOne() throws Exception {\n        final SUserTaskDefinitionImpl userTask = new SUserTaskDefinitionImpl(5, \"name\", \"actorName\");\n\n        assertThat(userTask.getContract()).isNotNull().isEqualTo(new SContractDefinitionImpl());\n    }\n\n    @Test\n    public void aUserTaskWithAContractReturnsThatOne() throws Exception {\n        final SContractDefinitionImpl contract = new SContractDefinitionImpl();\n        contract.addInput(new SInputDefinitionImpl(\"valid\", SType.TEXT, \"descripti\"));\n        final SUserTaskDefinitionImpl userTask = new SUserTaskDefinitionImpl(5, \"name\", \"actorName\");\n        userTask.setContract(contract);\n\n        assertThat(userTask.getContract()).isNotNull().isEqualTo(contract);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/FlowStatesMapping.txt",
    "content": "WARNING: If some states change, it requires migration\n\nPROCESS\n0 Initializing\n1 Started\n2 Suspended\n3 Cancelled\n4 Aborted\n5 Completing\n6 Completed\n7 Failed / Error\n11 Aborting\n\nACTIVITY (some constants are defined in the State interface)\n0 InitializingActivityState\n1 ExecutingFlowNodeState\n2 CompletedActivityState\n3 FailedActivityState\n4 ReadyActivityState\n9 CompletingActivityState\n10 WaitingFlowNodeState\n12 SkippedFlowNodeState\n13 AbortingSubTaskState\n14 CancelledFlowNodeState\n15 AbortingFlowNodeContainerState\n16 AbortedFlowNodeState\n17 CancellingFlowNodeContainerChildrenState\n18 CancellingFlowNodeState\n19 CancellingCallActivityState\n20 AbortingCallActivityState\n21 AbortingFlowNodeState\n22 CancellingBoundaryAndIntermediateCatchEventState\n23 InitializingLoopActivityState\n24 ExecutingLoopActivityState\n25 ExecutingActivityState\n26 ExecutingThrowEventState\n27 InitializingMultiInstanceActivityState\n28 ExecutingMultiInstanceActivityState\n30 CompletingCallActivityState\n31 ExecutingCallActivityState\n32 InitializingActivityWithBoundaryEventsState\n33 InitializingBoundaryEventState\n34 AbortingBoundaryEventsOnCompletingActivityState\n35 AbortingActivityWithBoundaryState\n36 CancellingActivityWithBoundaryState\n37 ExecutingAutomaticActivityState\n\n38 AbortingReceiveTaskState\n39 CancellingReceiveTaskState\n\n60 AbortingBoundaryAndIntermediateCatchEventState\n61 InitializingAndExecutingFlowNodeState\n65 ExecutingBoundaryEventState\n\n// up-to-date on 2020-09-15"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/build.gradle",
    "content": "plugins {\n    id 'groovy'\n}\n\ndependencies {\n    api platform(libs.bonitaArtifactsModelBom)\n\n    api libs.semver4j\n    api \"org.bonitasoft.engine:bonita-organization-model\"\n    api project(':bpm:bonita-core:bonita-home-server')\n    api project(':bpm:bonita-core:bonita-actor-mapping')\n    api project(':bpm:bonita-core:bonita-category')\n    api project(':bpm:bonita-core:bonita-process-instance')\n    api project(':bpm:bonita-core:bonita-contract-data')\n    api project(':bpm:bonita-core:bonita-user-filter')\n    api project(':bpm:bonita-core:bonita-login')\n    api project(':bpm:bonita-core:bonita-process-definition')\n    api project(':bpm:bonita-core:bonita-process-comment')\n    api project(':bpm:bonita-core:bonita-platform-login')\n    api project(':bpm:bonita-core:bonita-core-data')\n    api project(':bpm:bonita-core:bonita-supervisor-mapping')\n    api project(':bpm:bonita-synchro-repository:bonita-synchro-service')\n    api project(':services:bonita-builder')\n    api project(':services:bonita-commons')\n    api project(':services:bonita-archive')\n    api project(':services:bonita-authentication')\n    api project(':services:bonita-authorization')\n    api project(':services:bonita-cache')\n    api project(':services:bonita-classloader')\n    api project(':services:bonita-command')\n    api project(':services:bonita-connector-executor')\n    api project(':services:bonita-data-definition')\n    api project(':services:bonita-data-instance')\n    api project(':services:bonita-events')\n    api project(':services:bonita-expression')\n    api project(':services:bonita-identity')\n    api project(':services:bonita-incident')\n    api project(':services:bonita-lock')\n    api project(':services:bonita-log')\n    api project(':services:bonita-page')\n    api project(':bpm:bonita-core:bonita-parameter')\n    api project(':services:bonita-persistence')\n    api project(':services:bonita-platform')\n    api project(':services:bonita-platform-authentication')\n    api project(':services:bonita-platform-command')\n    api project(':services:bonita-platform-session')\n    api project(':services:bonita-profile')\n    api project(':services:bonita-scheduler')\n    api project(':services:bonita-session')\n    api project(':services:bonita-authorization')\n    api project(':services:bonita-time-tracker')\n    api project(':services:bonita-transaction')\n    api project(':services:bonita-work')\n    api project(':services:bonita-business-application')\n    api project(':bpm:bonita-core:bonita-form-mapping')\n    api project(':services:bonita-business-data:bonita-business-data-api')\n    api project(':services:bonita-resources')\n    api project(':services:bonita-temporary-content')\n    api \"com.fasterxml.jackson.core:jackson-databind\"\n    api project(':platform:platform-resources')\n    api libs.commonsIO\n    api libs.springContext\n    api libs.springSessionCore\n    api libs.springWeb\n    api libs.bundles.groovy\n    api libs.micrometerCore\n\n    // Dependency on javax.annotations as it is not provided anymore in Java 11:\n    api(libs.javaxAnnotations)\n\n    annotationProcessor libs.lombok\n    compileOnly libs.lombok\n\n    testImplementation libs.junit5params\n    testAnnotationProcessor libs.lombok\n\n    testImplementation libs.assertj\n    testImplementation libs.systemRules      // works with Junit4\n    testImplementation libs.systemLambda    // works with Junit5\n    testImplementation libs.springTest\n    compileOnly project(':bpm:bonita-common')\n    testImplementation testFixtures(project(':bpm:bonita-common'))\n    testImplementation libs.awaitility\n    testRuntimeOnly libs.logback\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/ActorMemberPermissionRule.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\n\n\nimport com.fasterxml.jackson.databind.ObjectMapper\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.api.permission.PermissionRule\nimport org.bonitasoft.engine.exception.NotFoundException\nimport org.bonitasoft.engine.session.APISession\n\n/**\n *\n * Let a user add an actorMember only if he is process supervisor\n *\n * <ul>\n *     <li>bpm/actorMember</li>\n *     <li>bpm/delegation</li>\n * </ul>\n *\n *\n *\n * @author Baptiste Mesta\n */\nclass ActorMemberPermissionRule implements PermissionRule {\n\n    public static final String ACTOR_ID = \"actor_id\"\n\n    @Override\n    public boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) {\n        long currentUserId = apiSession.getUserId()\n        if (apiCallContext.isPOST()) {\n            return checkPostMethod(apiCallContext, apiAccessor, currentUserId)\n        } else if (apiCallContext.isGET()) {\n            return checkGetMethod(apiCallContext, apiAccessor, currentUserId)\n        } else if (apiCallContext.isDELETE()) {\n            //TODO unable to find an actor member with the API!\n            return false\n        }\n        //it's ok to read\n        return true\n    }\n\n    private boolean checkPostMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId) {\n\n        ObjectMapper mapper = new ObjectMapper()\n        def list = mapper.readValue(apiCallContext.getBody(), List.class)\n\n        for (int i = 0; i < list.size(); i++) {\n            def object = list.get(i)\n\n            def get = object.get(ACTOR_ID)\n            if(get == null){\n                continue\n            }\n            def actorId = Long.valueOf(get.toString())\n            if (actorId <= 0) {\n                continue\n            }\n            def processAPI = apiAccessor.getProcessAPI()\n            try {\n                def actor = processAPI.getActor(actorId)\n                def processDefinitionId = actor.getProcessDefinitionId()\n                if (!processAPI.isUserProcessSupervisor(processDefinitionId, currentUserId)) {\n                    return false\n                }\n            } catch (NotFoundException e) {\n                return true\n            }\n        }\n        return true\n    }\n\n    private boolean checkGetMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId) {\n        try {\n            def filters = apiCallContext.getFilters()\n            if (filters.containsKey(ACTOR_ID)) {\n                def processAPI = apiAccessor.getProcessAPI()\n                def actor = processAPI.getActor(Long.parseLong(filters.get(ACTOR_ID)))\n                def processDefinitionId = actor.getProcessDefinitionId()\n                return processAPI.isUserProcessSupervisor(processDefinitionId, currentUserId)\n            }\n            return true\n        } catch (NotFoundException e) {\n            return true\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/ActorPermissionRule.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\n\n\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.api.permission.PermissionRule\nimport org.bonitasoft.engine.bpm.actor.ActorNotFoundException\nimport org.bonitasoft.engine.session.APISession\n\n/**\n *\n * Let a user manage actors only if he is process supervisor\n *\n * <ul>\n *     <li>bpm/actor</li>\n * </ul>\n *\n *\n *\n * @author Anthony Birembaut\n */\nclass ActorPermissionRule implements PermissionRule {\n\n    public static final String PROCESS_ID = \"process_id\"\n\n    @Override\n    public boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) {\n        long currentUserId = apiSession.getUserId()\n        if (apiCallContext.isGET()) {\n            return checkGetMethod(apiCallContext, apiAccessor, currentUserId)\n        } else if (apiCallContext.isPUT()) {\n            return checkPutMethod(apiCallContext, apiAccessor, currentUserId)\n        }\n        return true\n    }\n\n    private boolean checkGetMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId) {\n        def resourceId = apiCallContext.getResourceId()\n        if (resourceId == null || resourceId.isEmpty()) {\n            def filters = apiCallContext.getFilters()\n            if(filters.containsKey(PROCESS_ID)){\n                def processAPI = apiAccessor.getProcessAPI()\n                return processAPI.isUserProcessSupervisor(Long.valueOf(filters.get(PROCESS_ID)),currentUserId)\n            }\n            return true\n        } else {\n            try {\n                return isProcessOwnerOfTheProcess(apiAccessor, resourceId, currentUserId)\n            } catch (ActorNotFoundException e) {\n                logger.debug(\"actor does not exists\")\n            }\n            return true\n        }\n    }\n\n    private boolean checkPutMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId) {\n        def resourceId = apiCallContext.getResourceId()\n        try {\n            return isProcessOwnerOfTheProcess(apiAccessor, resourceId, currentUserId)\n        } catch (ActorNotFoundException e) {\n            logger.debug(\"actor does not exists\")\n            return true\n        }\n    }\n\n    private isProcessOwnerOfTheProcess(APIAccessor apiAccessor, String actorId, long currentUserId) throws ActorNotFoundException {\n        def processAPI = apiAccessor.getProcessAPI()\n        def actor = processAPI.getActor(Long.valueOf(actorId))\n        return processAPI.isUserProcessSupervisor(actor.getProcessDefinitionId(), currentUserId)\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/ApplicationMenuPermissionRule.groovy",
    "content": "/**\n * Copyright (C) 2021 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\n\n\nimport org.bonitasoft.engine.api.ApplicationAPI\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.api.permission.PermissionRule\nimport org.bonitasoft.engine.exception.NotFoundException\nimport org.bonitasoft.engine.session.APISession\n\n/**\n *\n * Secure profile related resources\n *\n * can be added to\n * <ul>\n *     <li>GET|living/application-menu</li>\n * </ul>\n * @author Anthony Birembaut\n */\nclass ApplicationMenuPermissionRule extends ApplicationPermissionCommon implements PermissionRule {\n\n    @Override\n    boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) {\n        try {\n            if (apiCallContext.isGET()) {\n                def applicationAPI = apiAccessor.getLivingApplicationAPI()\n                def applicationId = getApplicationId(apiCallContext, applicationAPI)\n                if (applicationId != -1l) {\n                    return isAppAllowed(applicationAPI.getApplication(applicationId),apiAccessor,apiSession)\n                }\n            }\n            return false\n        } catch (NotFoundException e) {\n            logger.debug(\"application menu not found: is allowed\")\n            //let the API handle the 404\n            return true\n        }\n    }\n\n    private long getApplicationId(APICallContext apiCallContext, ApplicationAPI applicationAPI) {\n        def applicationId = -1l\n        if (apiCallContext.getResourceId() != null) {\n            def menuId = Long.valueOf(apiCallContext.getResourceId())\n            def applicationMenu = applicationAPI.getApplicationMenu(menuId)\n            applicationId = applicationMenu.getApplicationId()\n        } else {\n            def filters = apiCallContext.getFilters()\n            if (filters.containsKey(\"applicationId\")){\n                applicationId = Long.valueOf(filters.get(\"applicationId\"))\n            }\n        }\n        return applicationId\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/ApplicationPermissionCommon.groovy",
    "content": "package org.bonitasoft.permissions\n\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.business.application.Application\nimport org.bonitasoft.engine.business.application.ApplicationVisibility\nimport org.bonitasoft.engine.profile.ProfileCriterion\nimport org.bonitasoft.engine.session.APISession\n\nclass ApplicationPermissionCommon {\n\n    boolean isAppAllowed(Application application, APIAccessor apiAccessor, APISession apiSession) {\n        def profileId = application.getProfileId()\n        def profileAPI = apiAccessor.getProfileAPI()\n        def applicationVisibility = application.getVisibility()\n        if (applicationVisibility != null) {\n            if (applicationVisibility == ApplicationVisibility.ALL) {\n                return true\n            } else if (applicationVisibility == ApplicationVisibility.TECHNICAL_USER) {\n                return apiSession.isTechnicalUser()\n            }\n        }\n        def index = 0\n        def profile\n        def list = []\n        while ((list = profileAPI.getProfilesForUser(apiSession.getUserId(), index, 100, ProfileCriterion.ID_ASC)).size() == 100 && (profile = list.find { it.getId() == profileId }) == null) {\n            index += 100\n        }\n        return profile != null || list.find { it.getId() == profileId } != null\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/ApplicationPermissionRule.groovy",
    "content": "/**\n * Copyright (C) 2021 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\n\n\n\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.api.permission.PermissionRule\nimport org.bonitasoft.engine.business.application.Application\nimport org.bonitasoft.engine.exception.NotFoundException\nimport org.bonitasoft.engine.session.APISession\n\n/**\n *\n * Secure profile related resources\n *\n * can be added to\n * <ul>\n *     <li>GET|living/application</li>\n * </ul>\n * @author Anthony Birembaut\n */\nclass ApplicationPermissionRule extends ApplicationPermissionCommon implements PermissionRule {\n\n    @Override\n    boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) {\n        try {\n            if (apiCallContext.isGET()) {\n                def application =  getApplicationFromIdOrToken(apiCallContext, apiAccessor)\n                if (application != null) {\n                    return isAppAllowed(application, apiAccessor, apiSession)\n                } else {\n                    return apiSession.getUserId().toString().equals(apiCallContext.getFilters().get(\"userId\"))\n                }\n            }\n            return false\n        } catch (NotFoundException e) {\n            logger.debug(\"application not found: is allowed\")\n            //let the API handle the 404\n            return true\n        }\n    }\n\n    private Application getApplicationFromIdOrToken(APICallContext apiCallContext, APIAccessor apiAccessor) {\n        def application\n        def applicationAPI = apiAccessor.getLivingApplicationAPI()\n        if (apiCallContext.getResourceId() != null) {\n            def applicationId = Long.valueOf(apiCallContext.getResourceId())\n            application = applicationAPI.getApplication(applicationId)\n        } else if (apiCallContext.getFilters().get(\"token\") != null) {\n            application = applicationAPI.getApplicationByToken(apiCallContext.getFilters().get(\"token\"))\n        }\n        return application\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/CaseContextPermissionRule.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.permissions\n\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.api.permission.PermissionRule\nimport org.bonitasoft.engine.exception.BonitaException\nimport org.bonitasoft.engine.session.APISession\n\n/**\n *\n * Let a user access only cases that he is involved in and start cases that he can start\n *\n * <ul>\n *     <li>bpm/case/[id]/context</li>\n *     <li>bpm/archivedCase/[id]/context</li>\n * </ul>\n *\n * @author Anthony Birembaut\n */\nclass CaseContextPermissionRule implements PermissionRule {\n\n    @Override\n    public boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) {\n        long currentUserId = apiSession.getUserId()\n        def processAPI = apiAccessor.getProcessAPI()\n        try {\n            def caseId = getCaseId(apiCallContext)\n            if (caseId <= 0) {\n                return true\n            }\n            def originalCaseId\n            if (apiCallContext.getResourceName().startsWith(\"archived\")) {\n                originalCaseId = processAPI.getArchivedProcessInstance(caseId).getSourceObjectId()\n            } else {\n                originalCaseId = caseId\n            }\n            // isInvolvedInProcessInstance() already checks the archived and non-archived involvement\n            def isInvolved = processAPI.isInvolvedInProcessInstance(currentUserId, originalCaseId) || processAPI.isManagerOfUserInvolvedInProcessInstance(currentUserId, originalCaseId)\n            if (isInvolved) {\n                return true\n            }\n            def processDefinitionId\n            if (apiCallContext.getResourceName().startsWith(\"archived\")) {\n                processDefinitionId = processAPI.getArchivedProcessInstance(caseId).getProcessDefinitionId()\n            } else {\n                processDefinitionId = processAPI.getProcessInstance(caseId).getProcessDefinitionId()\n            }\n            return processAPI.isUserProcessSupervisor(processDefinitionId, currentUserId)\n        } catch (BonitaException e) {\n            //exception, allow user to have the 404 when the rest api will look for the resource:\n            return true\n        }\n    }\n\n    private long getCaseId(APICallContext apiCallContext) {\n        def compoundResourceId = apiCallContext.getCompoundResourceId()\n        if (compoundResourceId == null || compoundResourceId.isEmpty()) {\n            return -1L\n        }\n        return Long.valueOf(compoundResourceId.get(0))\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/CasePermissionRule.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\n\n\nimport com.fasterxml.jackson.databind.ObjectMapper\n\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.ProcessAPI\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.api.permission.PermissionRule\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor\nimport org.bonitasoft.engine.exception.BonitaException\nimport org.bonitasoft.engine.exception.NotFoundException\nimport org.bonitasoft.engine.identity.User\nimport org.bonitasoft.engine.identity.UserSearchDescriptor\nimport org.bonitasoft.engine.search.SearchOptionsBuilder\nimport org.bonitasoft.engine.search.SearchResult\nimport org.bonitasoft.engine.session.APISession\n\n/**\n *\n * Let a user access only cases that he is involved in and start cases that he can start\n *\n * <ul>\n *     <li>bpm/case</li>\n *     <li>bpm/archivedCase</li>\n * </ul>\n *\n *\n *\n * @author Baptiste Mesta\n * @author Anthony Birembaut\n */\nclass CasePermissionRule implements PermissionRule {\n\n\n    @Override\n    public boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) {\n        long currentUserId = apiSession.getUserId()\n        if (apiCallContext.isGET()) {\n            return checkGetMethod(apiCallContext, apiAccessor, currentUserId, logger)\n        } else if (apiCallContext.isPOST()) {\n            return checkPostMethod(apiCallContext, apiAccessor, currentUserId, logger)\n        } else if (apiCallContext.isPUT()) {\n            return checkPutMethod(apiCallContext, apiAccessor, currentUserId, logger)\n        }\n        return false\n    }\n\n    private boolean checkPostMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId, Logger logger) {\n\n        ObjectMapper mapper = new ObjectMapper()\n        def map = mapper.readValue(apiCallContext.getBody(), Map.class)\n\n        def string = map.get(\"processDefinitionId\")\n        if (string == null || string.toString().isEmpty()) {\n            return true\n        }\n        def processDefinitionId = Long.valueOf(string.toString())\n        if (processDefinitionId <= 0) {\n            return true\n        }\n        def processAPI = apiAccessor.getProcessAPI()\n        def identityAPI = apiAccessor.getIdentityAPI()\n        User user = identityAPI.getUser(currentUserId)\n        SearchOptionsBuilder searchOptionBuilder = new SearchOptionsBuilder(0, 10)\n        searchOptionBuilder.filter(UserSearchDescriptor.USER_NAME, user.getUserName())\n        SearchResult<User> listUsers = processAPI.searchUsersWhoCanStartProcessDefinition(processDefinitionId, searchOptionBuilder.done())\n        logger.debug(\"RuleCase : nb Result [\" + listUsers.getCount() + \"] ?\")\n        def canStart = listUsers.getCount() == 1\n        logger.debug(\"RuleCase : User allowed to start? \" + canStart)\n        return canStart\n    }\n\n    private boolean isInvolved(ProcessAPI processAPI, long currentUserId, long processInstanceId) {\n        return processAPI.isInvolvedInProcessInstance(currentUserId, processInstanceId) || processAPI.isManagerOfUserInvolvedInProcessInstance(currentUserId, processInstanceId)\n    }\n\n    private boolean checkGetMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId, Logger logger) {\n        def processAPI = apiAccessor.getProcessAPI()\n        def filters = apiCallContext.getFilters()\n        try {\n            if (apiCallContext.getResourceId() != null) {\n                def processDefinitionId\n                if (apiCallContext.getResourceName().startsWith(\"archived\")) {\n                    def archivedProcessInstanceId = Long.valueOf(apiCallContext.getResourceId())\n                    def archivedProcessInstance = processAPI.getArchivedProcessInstance(archivedProcessInstanceId)\n                    def processInstanceId = archivedProcessInstance.getSourceObjectId()\n                    if (isInvolved(processAPI, currentUserId, processInstanceId)) {\n                        return true\n                    }\n                    processDefinitionId = archivedProcessInstance.getProcessDefinitionId()\n                } else {\n                    def processInstanceId = Long.valueOf(apiCallContext.getResourceId())\n                    if (isInvolved(processAPI, currentUserId, processInstanceId)) {\n                        return true\n                    }\n                    processDefinitionId = processAPI.getProcessInstance(processInstanceId).getProcessDefinitionId()\n                }\n                logger.debug(\"RuleCase : allowed because get on process that user is involved in\")\n                return processAPI.isUserProcessSupervisor(processDefinitionId, currentUserId)\n            } else {\n                def stringUserId = String.valueOf(currentUserId)\n                if (stringUserId.equals(filters.get(\"started_by\")) || stringUserId.equals(filters.get(\"user_id\")) || stringUserId.equals(filters.get(\"supervisor_id\"))) {\n                    logger.debug(\"RuleCase : allowed because searching filters contains user id\")\n                    return true\n                }\n                if (filters.containsKey(\"processDefinitionId\")) {\n                    return processAPI.isUserProcessSupervisor(Long.valueOf(filters.get(\"processDefinitionId\")), currentUserId)\n                }\n                if (\"archivedCase\".equals(apiCallContext.getResourceName()) && filters.containsKey(\"sourceObjectId\")) {\n                    def sourceCase = Long.valueOf(filters.get(\"sourceObjectId\"))\n                    final SearchOptionsBuilder opts = new SearchOptionsBuilder(0, 1)\n                    opts.filter(ArchivedProcessInstancesSearchDescriptor.SOURCE_OBJECT_ID, sourceCase)\n                    def result = processAPI.searchArchivedProcessInstancesInvolvingUser(currentUserId, opts.done())\n                    def archivedProcessInstance = processAPI.getFinalArchivedProcessInstance(sourceCase)\n                    return result.getCount() == 1 || processAPI.isUserProcessSupervisor(archivedProcessInstance.getProcessDefinitionId(), currentUserId)\n                }\n            }\n        } catch (BonitaException e) {\n            //exception, allow user to have the 404 when the rest api will look for the resource:\n            return true\n        }\n        return false\n    }\n\n    private boolean checkPutMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId, Logger logger) {\n        def processAPI = apiAccessor.getProcessAPI()\n        def processInstanceId = Long.valueOf(apiCallContext.getResourceId())\n        def processDefinitionId = processAPI.getProcessInstance(processInstanceId).getProcessDefinitionId()\n        return processAPI.isUserProcessSupervisor(processDefinitionId, currentUserId)\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/CaseVariablePermissionRule.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\n\n\n\n\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.api.permission.PermissionRule\nimport org.bonitasoft.engine.exception.NotFoundException\nimport org.bonitasoft.engine.session.APISession\n\n/**\n *\n * Let a user get and update a variable of a case only if he is the process supervisor\n *\n * <ul>\n *     <li>bpm/caseVariable</li>\n * </ul>\n *\n *\n *\n * @author Baptiste Mesta\n */\nclass CaseVariablePermissionRule implements PermissionRule {\n\n\n    @Override\n    public boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) {\n        long currentUserId = apiSession.getUserId()\n        def resourceId = apiCallContext.getResourceId()\n        def processAPI = apiAccessor.getProcessAPI()\n        try {\n            if ((apiCallContext.isPUT() || apiCallContext.isGET()) && resourceId != null) {\n                // Resource format: <processInstanceId>/<caseVariableName>\n                def caseId = Long.valueOf(resourceId.tokenize(\"/\").first())\n                def processInstance = processAPI.getProcessInstance(caseId)\n                return processAPI.isUserProcessSupervisor(processInstance.getProcessDefinitionId(), currentUserId)\n            }\n\n            def filters = apiCallContext.getFilters()\n            if (apiCallContext.isGET() && filters.containsKey(\"case_id\")) {\n                def caseId = Long.valueOf(filters.get(\"case_id\"))\n                def processInstance = processAPI.getProcessInstance(caseId)\n                return processAPI.isUserProcessSupervisor(processInstance.getProcessDefinitionId(), currentUserId)\n            }\n            return false\n        } catch (NotFoundException e) {\n            return true\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/CommentPermissionRule.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\n\nimport com.fasterxml.jackson.databind.ObjectMapper\n\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.ProcessAPI\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.api.permission.PermissionRule\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor\nimport org.bonitasoft.engine.exception.BonitaException\nimport org.bonitasoft.engine.exception.NotFoundException\nimport org.bonitasoft.engine.search.SearchOptionsBuilder\nimport org.bonitasoft.engine.session.APISession\n\n/**\n *\n * Let a user access only comments on cases that he is involved in\n *\n * <ul>\n *     <li>bpm/comment</li>\n *     <li>bpm/archivedComment</li>\n * </ul>\n *\n *\n *\n * @author Baptiste Mesta\n */\nclass CommentPermissionRule implements PermissionRule {\n\n\n    @Override\n    public boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) {\n        long currentUserId = apiSession.getUserId()\n        if (apiCallContext.isGET()) {\n            return checkGetMethod(apiCallContext, apiAccessor, currentUserId)\n        } else if (apiCallContext.isPOST()) {\n            return checkPostMethod(apiCallContext, apiAccessor, currentUserId, logger)\n        }\n        return false\n    }\n\n    private boolean checkPostMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId, Logger logger) {\n\n        ObjectMapper mapper = new ObjectMapper()\n        def map = mapper.readValue(apiCallContext.getBody(), Map.class)\n\n        def string = map.get(\"processInstanceId\")\n        if (string == null || string.toString().isEmpty()) {\n            return true\n        }\n        def processInstanceId = Long.valueOf(string.toString())\n        if (processInstanceId <= 0) {\n            return true\n        }\n        def processAPI = apiAccessor.getProcessAPI()\n        return isInvolved(processAPI, currentUserId, processInstanceId) || isSupervisor(processAPI, processInstanceId, currentUserId)\n    }\n\n    private boolean checkGetMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId) {\n        def filters = apiCallContext.getFilters()\n        def stringUserId = String.valueOf(currentUserId)\n        if (stringUserId.equals(filters.get(\"team_manager_id\")) || stringUserId.equals(filters.get(\"user_id\")) || stringUserId.equals(filters.get(\"supervisor_id\"))) {\n            return true\n        }\n        if (filters.containsKey(\"processInstanceId\")) {\n            def processInstanceId = Long.valueOf(filters.get(\"processInstanceId\"))\n\n            def processAPI = apiAccessor.getProcessAPI()\n            return isInvolved(processAPI, currentUserId, processInstanceId) || isSupervisor(processAPI, processInstanceId, currentUserId)\n        }\n        return false\n    }\n\n    private boolean isInvolved(ProcessAPI processAPI, long currentUserId, long processInstanceId) {\n        try {\n            return processAPI.isInvolvedInProcessInstance(currentUserId, processInstanceId) || processAPI.isManagerOfUserInvolvedInProcessInstance(currentUserId, processInstanceId)\n        } catch (BonitaException e) {\n            return true\n        }\n    }\n\n    private boolean isSupervisor(ProcessAPI processAPI, long processInstanceId, long currentUserId) {\n        def processDefinitionId\n        try {\n            processDefinitionId = processAPI.getProcessInstance(processInstanceId).getProcessDefinitionId()\n        } catch (NotFoundException e) {\n            try {\n                processDefinitionId = processAPI.getFinalArchivedProcessInstance(processInstanceId).getProcessDefinitionId()\n            } catch (NotFoundException e1) {\n                return true\n            }\n        }\n        return processAPI.isUserProcessSupervisor(processDefinitionId, currentUserId)\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/ConnectorInstancePermissionRule.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\n\n\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.api.permission.PermissionRule\nimport org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstanceSearchDescriptor\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstanceNotFoundException\nimport org.bonitasoft.engine.exception.SearchException\nimport org.bonitasoft.engine.search.SearchOptionsBuilder\nimport org.bonitasoft.engine.session.APISession\n\n/**\n *\n * Let a user see process configuration only if he is process supervisor\n *\n * <ul>\n *     <li>bpm/connectorInstance</li>\n *     <li>bpm/archivedConnectorInstance</li>\n * </ul>\n *\n *\n *\n * @author Anthony Birembaut\n */\nclass ConnectorInstancePermissionRule implements PermissionRule {\n\n    public static final String CONTAINER_ID = \"containerId\"\n\n    @Override\n    public boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) {\n        long currentUserId = apiSession.getUserId()\n        if (apiCallContext.isGET()) {\n            return checkGetMethod(apiCallContext, apiAccessor, currentUserId, logger)\n        } else if (apiCallContext.isPUT()) {\n            //TODO unable to find a connector instance with the API!\n            return false\n        }\n        return true\n    }\n\n    private boolean checkGetMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId, Logger logger) {\n        def filters = apiCallContext.getFilters()\n        if(filters.containsKey(CONTAINER_ID)){\n            def processAPI = apiAccessor.getProcessAPI()\n            def processID\n            if (apiCallContext.getResourceName().startsWith(\"archived\")) {\n                try {\n                    def searchOptions = new SearchOptionsBuilder(0, 1)\n                    searchOptions.filter(ArchivedFlowNodeInstanceSearchDescriptor.ORIGINAL_FLOW_NODE_ID, Long.valueOf(filters.get(CONTAINER_ID)))\n                    def searchResult = processAPI.searchArchivedFlowNodeInstances(searchOptions.done())\n                    def archivedFlowNodeInstances = searchResult.getResult()\n                    if (archivedFlowNodeInstances.isEmpty()) {\n                        logger.debug(\"archived flow node does not exists\")\n                        return true\n                    } else {\n                        processID = archivedFlowNodeInstances.get(0).getProcessDefinitionId()\n                    }\n                } catch(SearchException e) {\n                    logger.debug(\"error while retrieving the archived flow node\")\n                    return true\n                }\n            } else {\n                try{\n                    def flowNodeInstance = processAPI.getFlowNodeInstance(Long.valueOf(filters.get(CONTAINER_ID)))\n                    processID = flowNodeInstance.getProcessDefinitionId()\n                } catch(FlowNodeInstanceNotFoundException e) {\n                    logger.debug(\"flow node does not exists\")\n                    return true\n                }\n            }\n            return processAPI.isUserProcessSupervisor(processID,currentUserId)\n        }\n        return false\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/DocumentPermissionRule.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\n\nimport com.fasterxml.jackson.databind.ObjectMapper\n\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.ProcessAPI\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.api.permission.PermissionRule\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor\nimport org.bonitasoft.engine.exception.BonitaException\nimport org.bonitasoft.engine.exception.NotFoundException\nimport org.bonitasoft.engine.search.SearchOptionsBuilder\nimport org.bonitasoft.engine.session.APISession\n\n/**\n *\n * Let a user access only document on cases that he is involved in\n *\n * <ul>\n *     <li>bpm/document</li>\n *     <li>bpm/archivedDocument</li>\n *     <li>bpm/caseDocument</li>\n * </ul>\n *\n *\n *\n * @author Baptiste Mesta\n * @author Truc Nguyen\n */\nclass DocumentPermissionRule implements PermissionRule {\n\n    public static final String CASE_ID = \"caseId\"\n    public static final String ARCHIVED_CASE_ID = \"archivedCaseId\"\n\n    @Override\n    public boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) {\n        long currentUserId = apiSession.getUserId()\n\n        def resourceId = apiCallContext.getResourceId()\n        if (resourceId != null) {\n            return checkMethodWithResourceId(resourceId, apiAccessor, currentUserId, logger)\n        }\n\n        if (apiCallContext.isGET()) {\n            return checkGetMethod(apiCallContext, apiAccessor, currentUserId, logger)\n        } else if (apiCallContext.isPOST()) {\n            return checkPostMethod(apiCallContext, apiAccessor, currentUserId, logger)\n        }\n\n        return false\n    }\n\n    private boolean checkMethodWithResourceId(String resourceId, APIAccessor apiAccessor, long currentUserId, Logger logger) {\n        def processAPI = apiAccessor.getProcessAPI()\n        try {\n            long documentId = Long.valueOf(resourceId)\n            def processInstanceId = processAPI.getDocument(documentId).getProcessInstanceId()\n            return isInvolved(processAPI, currentUserId, processInstanceId, logger) ||\n                    isSupervisor(processAPI, currentUserId, processInstanceId, logger)\n        }\n        catch (NumberFormatException e) {\n            logger.debug(\"documentId \" + documentIdStr + \" is not a number\")\n            return false\n        }\n    }\n\n    private boolean checkPostMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId, Logger logger) {\n\n        ObjectMapper mapper = new ObjectMapper()\n        def map = mapper.readValue(apiCallContext.getBody(), Map.class)\n\n        def processInstanceIdAsString = map.get(CASE_ID)\n        if (processInstanceIdAsString == null || processInstanceIdAsString.toString().isEmpty()) {\n            return true\n        }\n        def processInstanceId = Long.valueOf(processInstanceIdAsString.toString())\n        if (processInstanceId <= 0) {\n            return true\n        }\n        try {\n            def processAPI = apiAccessor.getProcessAPI()\n            def processDefinitionId = processAPI.getProcessInstance(processInstanceId).getProcessDefinitionId()\n            return isInvolved(processAPI, currentUserId, processInstanceId, logger) ||\n                    processAPI.isUserProcessSupervisor(processDefinitionId, currentUserId)\n        } catch (NotFoundException e) {\n            logger.debug(\"Process instance of document not found.\")\n            return true\n        }\n    }\n\n    private boolean checkGetMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId, Logger logger) {\n        def filters = apiCallContext.getFilters()\n        def processAPI = apiAccessor.getProcessAPI()\n\n        long processInstanceId = -1\n        long processDefinitionId = -1\n\n        def archivedCaseIdAsString = filters.get(ARCHIVED_CASE_ID)\n        if (archivedCaseIdAsString != null) {\n            def archivedCaseId = Long.valueOf(archivedCaseIdAsString)\n            processInstanceId = processAPI.getArchivedProcessInstance(archivedCaseId).getSourceObjectId()\n            processDefinitionId = processAPI.getFinalArchivedProcessInstance(processInstanceId).getProcessDefinitionId()\n        }\n        else {\n            def processInstanceIdAsString = filters.get(CASE_ID)\n            if (processInstanceIdAsString != null) {\n                processInstanceId = Long.valueOf(processInstanceIdAsString)\n                processDefinitionId = processAPI.getProcessInstance(processInstanceId).getProcessDefinitionId()\n            }\n        }\n\n        if (processInstanceId > 0 && processDefinitionId > 0) {\n            return isInvolved(processAPI, currentUserId, processInstanceId, logger) ||\n                    processAPI.isUserProcessSupervisor(processDefinitionId, currentUserId)\n        }\n\n        return false\n    }\n\n\n    private boolean isInvolved(ProcessAPI processAPI, long currentUserId, long processInstanceId, Logger logger) {\n        try {\n            return processAPI.isInvolvedInProcessInstance(currentUserId, processInstanceId) ||\n                    processAPI.isManagerOfUserInvolvedInProcessInstance(currentUserId, processInstanceId) ||\n                    hasPendingTaskInCurrentSubprocess(processAPI, currentUserId, processInstanceId, logger)\n        } catch (BonitaException e) {\n            logger.debug(\"Error checking if user is involved in process instance of document.\", e)\n            return false\n        }\n    }\n\n    private boolean isSupervisor(ProcessAPI processAPI, long currentUserId, long processInstanceId, Logger logger) {\n        def processDefinitionId\n        try {\n            processDefinitionId = processAPI.getProcessInstance(processInstanceId).getProcessDefinitionId()\n        } catch (NotFoundException e) {\n            try {\n                processDefinitionId = processAPI.getFinalArchivedProcessInstance(processInstanceId).getProcessDefinitionId()\n            } catch (NotFoundException e1) {\n                logger.debug(\"Process instance of document not found.\")\n                return false\n            }\n        }\n        return processAPI.isUserProcessSupervisor(processDefinitionId, currentUserId)\n    }\n\n    private boolean hasPendingTaskInCurrentSubprocess(ProcessAPI processAPI, long currentUserId, long processInstanceIdAsSubprocess, Logger logger) {\n        try {\n            SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 0)\n            builder.filter(HumanTaskInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID, processInstanceIdAsSubprocess)\n            def taskSearchResult = processAPI.searchMyAvailableHumanTasks(currentUserId, builder.done())\n            return taskSearchResult.getCount() > 0\n        } catch (BonitaException e) {\n            logger.debug(\"Error checking if user is involved in process instance of document.\", e)\n            return false\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/DownloadDocumentPermissionRule.groovy",
    "content": "/**\n * Copyright (C) 2017 BonitaSoft S.A.\n * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2.0 of the License, or\n * (at your option) any later version.\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n **/\n\npackage org.bonitasoft.permissions\n\nimport com.fasterxml.jackson.databind.ObjectMapper\n\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.ProcessAPI\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.api.permission.PermissionRule\nimport org.bonitasoft.engine.bpm.document.DocumentsSearchDescriptor\nimport org.bonitasoft.engine.bpm.document.DocumentNotFoundException\nimport org.bonitasoft.engine.bpm.document.ArchivedDocumentNotFoundException\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor\nimport org.bonitasoft.engine.exception.BonitaException\nimport org.bonitasoft.engine.exception.NotFoundException\nimport org.bonitasoft.engine.search.SearchOptionsBuilder\nimport org.bonitasoft.engine.session.APISession\n\n/**\n *\n * Let a user access only document on cases that he is involved in\n *\n * <ul>\n *     <li>portal/documentDownload</li>\n *     <li>portal/downloadDocument</li>\n *     <li>portal/formsDocumentDownload</li>\n * </ul>\n *\n *\n *\n * @author Anthony Birembaut\n */\nclass DownloadDocumentPermissionRule implements PermissionRule {\n\n    public static final String DOCUMENT_ID_PARAM = \"document\"\n\n    public static final String CONTENT_STORAGE_ID_PARAM = \"contentStorageId\"\n\n    public static final String FILE_NAME_PARAM = \"fileName\"\n\n    @Override\n    public boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) {\n        long currentUserId = apiSession.getUserId()\n\n        if (apiCallContext.isGET()) {\n            def processInstanceId = null\n            def contentStorageId = apiCallContext.getParameters().get(CONTENT_STORAGE_ID_PARAM)\n            def fileName = apiCallContext.getParameters().get(FILE_NAME_PARAM)\n            def documentId = apiCallContext.getParameters().get(DOCUMENT_ID_PARAM)\n            // Download servlets requires either contentStorageId + fileName parameters or a document parameter\n            if (fileName != null && contentStorageId != null && contentStorageId.length > 0) {\n                processInstanceId = getProcessInstanceIdFromContentStorageId(contentStorageId[0], apiAccessor, logger)\n            } else if (documentId != null && documentId.length > 0) {\n                try {\n                    long longDocumentId = Long.valueOf(documentId[0])\n                    processInstanceId = getProcessInstanceIdFromDocumentId(longDocumentId, apiAccessor, logger)\n                } catch (NumberFormatException e) {\n                    logger.debug(\"documentId \" + documentId + \" is not a number\")\n                    return false\n                }\n            }\n            if (processInstanceId != -1l) {\n                return checkInvolvement(processInstanceId, apiAccessor, currentUserId, logger)\n            }\n            return true\n        }\n        return false\n    }\n\n    private long getProcessInstanceIdFromDocumentId(long documentId, APIAccessor apiAccessor, Logger logger) {\n        def processInstanceId = -1l\n        def processAPI = apiAccessor.getProcessAPI()\n        try {\n            processInstanceId = processAPI.getDocument(documentId).getProcessInstanceId()\n        } catch (DocumentNotFoundException dnfe) {\n            try {\n                processInstanceId = processAPI.getArchivedVersionOfProcessDocument(documentId).getProcessInstanceId()\n            } catch (ArchivedDocumentNotFoundException e) {\n                logger.debug(\"No document or archived document found with Id \" + documentId)\n            }\n        }\n        return processInstanceId\n    }\n\n    private long getProcessInstanceIdFromContentStorageId(String contentStorageIdStr, APIAccessor apiAccessor, Logger logger) {\n        def processInstanceId = -1l\n        def processAPI = apiAccessor.getProcessAPI()\n        try {\n            long contentStorageId = Long.valueOf(contentStorageIdStr)\n            SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 1)\n            builder.filter(DocumentsSearchDescriptor.CONTENT_STORAGE_ID, contentStorageId)\n            def documentSearchResult = processAPI.searchDocuments(builder.done())\n            if (documentSearchResult.getCount() > 0) {\n                processInstanceId = documentSearchResult.getResult().get(0).getProcessInstanceId()\n            } else {\n                def archivedDocumentSearchResult = processAPI.searchArchivedDocuments(builder.done())\n                if (archivedDocumentSearchResult.getCount() > 0) {\n                    processInstanceId = archivedDocumentSearchResult.getResult().get(0).getProcessInstanceId()\n                }\n            }\n            if (processInstanceId == -1l) {\n                logger.debug(\"No document or archived document found for contentStorageId \" + contentStorageIdStr)\n            }\n        }\n        catch (NumberFormatException e) {\n            logger.debug(\"contentStorageId is not a number\")\n        }\n        return processInstanceId\n    }\n\n    private boolean checkInvolvement(long processInstanceId, APIAccessor apiAccessor, long currentUserId, Logger logger) {\n        def processAPI = apiAccessor.getProcessAPI()\n        return isInvolved(processAPI, currentUserId, processInstanceId, logger) ||\n                isSupervisor(processAPI, currentUserId, processInstanceId, logger)\n    }\n\n    private boolean isInvolved(ProcessAPI processAPI, long currentUserId, long processInstanceId, Logger logger) {\n        try {\n            return processAPI.isInvolvedInProcessInstance(currentUserId, processInstanceId) ||\n                    processAPI.isManagerOfUserInvolvedInProcessInstance(currentUserId, processInstanceId) ||\n                    hasPendingTaskInCurrentSubprocess(processAPI, currentUserId, processInstanceId, logger)\n        } catch (BonitaException e) {\n            logger.debug(\"Error checking if user is involved in process instance of document.\", e)\n            return false\n        }\n    }\n\n    private boolean isSupervisor(ProcessAPI processAPI, long currentUserId, long processInstanceId, Logger logger) {\n        def processDefinitionId\n        try {\n            processDefinitionId = processAPI.getProcessInstance(processInstanceId).getProcessDefinitionId()\n        } catch (NotFoundException e) {\n            try {\n                processDefinitionId = processAPI.getFinalArchivedProcessInstance(processInstanceId).getProcessDefinitionId()\n            } catch (NotFoundException e1) {\n                logger.debug(\"Process instance of document not found.\")\n                return false\n            }\n        }\n        return processAPI.isUserProcessSupervisor(processDefinitionId, currentUserId)\n    }\n\n    private boolean hasPendingTaskInCurrentSubprocess(ProcessAPI processAPI, long currentUserId, long processInstanceIdAsSubprocess, Logger logger) {\n        try {\n            SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 0)\n            builder.filter(HumanTaskInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID, processInstanceIdAsSubprocess)\n            def taskSearchResult = processAPI.searchMyAvailableHumanTasks(currentUserId, builder.done())\n            return taskSearchResult.getCount() > 0\n        } catch (BonitaException e) {\n            logger.debug(\"Error checking if user is involved in process instance of document.\", e)\n            return false\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/ProcessConfigurationPermissionRule.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\n\n\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.api.permission.PermissionRule\nimport org.bonitasoft.engine.session.APISession\n\n/**\n *\n * Let a user manage process connectors and parameters only if he is process supervisor\n *\n * <ul>\n *     <li>bpm/processConnector</li>\n *     <li>bpm/processParameter</li>\n * </ul>\n *\n *\n *\n * @author Anthony Birembaut\n */\nclass ProcessConfigurationPermissionRule implements PermissionRule {\n\n    public static final String PROCESS_ID = \"process_id\"\n\n    @Override\n    public boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) {\n        long currentUserId = apiSession.getUserId()\n        if (apiCallContext.isGET()) {\n            return checkGetMethod(apiCallContext, apiAccessor, currentUserId, logger)\n        } else if (apiCallContext.isPUT()) {\n            return checkPutMethod(apiCallContext, apiAccessor, currentUserId, logger)\n        }\n        return true\n    }\n\n    private boolean checkGetMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId, Logger logger) {\n        def resourceIds = apiCallContext.getCompoundResourceId()\n        if (resourceIds.isEmpty()) {\n            def filters = apiCallContext.getFilters()\n            if(filters.containsKey(PROCESS_ID)){\n                def processAPI = apiAccessor.getProcessAPI()\n                return processAPI.isUserProcessSupervisor(Long.valueOf(filters.get(PROCESS_ID)),currentUserId)\n            }\n            return false\n        } else {\n            return isProcessOwnerOfTheProcess(apiAccessor, resourceIds, currentUserId)\n        }\n    }\n\n    private boolean checkPutMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId, Logger logger) {\n        def resourceIds = apiCallContext.getCompoundResourceId()\n        return isProcessOwnerOfTheProcess(apiAccessor, resourceIds, currentUserId)\n    }\n\n    private isProcessOwnerOfTheProcess(APIAccessor apiAccessor, List<String> resourceIds, long currentUserId) {\n        def processAPI = apiAccessor.getProcessAPI()\n        def processID = Long.parseLong(resourceIds.get(0))\n        return processAPI.isUserProcessSupervisor(processID, currentUserId)\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/ProcessConnectorDependencyPermissionRule.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\n\n\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.api.permission.PermissionRule\nimport org.bonitasoft.engine.session.APISession\n\n/**\n *\n * Let a user see process connector dependency problem only if he is process supervisor\n *\n * <ul>\n *     <li>bpm/processConnectorDependency</li>\n * </ul>\n *\n *\n *\n * @author Anthony Birembaut\n */\nclass ProcessConnectorDependencyPermissionRule implements PermissionRule {\n\n    public static final String PROCESS_ID = \"connector_process_id\"\n\n    @Override\n    public boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) {\n        long currentUserId = apiSession.getUserId()\n        if (apiCallContext.isGET()) {\n            return checkGetMethod(apiCallContext, apiAccessor, currentUserId, logger)\n        }\n        return true\n    }\n\n    private boolean checkGetMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId, Logger logger) {\n        def filters = apiCallContext.getFilters()\n        if(filters.containsKey(PROCESS_ID)){\n            def processAPI = apiAccessor.getProcessAPI()\n            return processAPI.isUserProcessSupervisor(Long.valueOf(filters.get(PROCESS_ID)),currentUserId)\n        }\n        return false\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/ProcessInstantiationPermissionRule.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\n\n\nimport com.fasterxml.jackson.databind.ObjectMapper\n\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.api.permission.PermissionRule\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor\nimport org.bonitasoft.engine.exception.BonitaException\nimport org.bonitasoft.engine.exception.NotFoundException\nimport org.bonitasoft.engine.identity.User\nimport org.bonitasoft.engine.identity.UserSearchDescriptor\nimport org.bonitasoft.engine.search.SearchOptionsBuilder\nimport org.bonitasoft.engine.search.SearchResult\nimport org.bonitasoft.engine.session.APISession\n\n/**\n *\n * Let a user access only cases that he is involved in and start cases that he can start\n *\n * <ul>\n *     <li>bpm/process/[id]/contract</li>\n *     <li>bpm/process/[id]/instantiation</li>\n * </ul>\n *\n *\n *\n * @author Anthony Birembaut\n */\nclass ProcessInstantiationPermissionRule implements PermissionRule {\n\n    @Override\n    public boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) {\n        long currentUserId = apiSession.getUserId()\n        def processDefinitionId = getProcessDefinitionId(apiCallContext)\n        if (processDefinitionId <= 0) {\n            return true\n        }\n        try {\n            def processAPI = apiAccessor.getProcessAPI()\n            def identityAPI = apiAccessor.getIdentityAPI()\n            User user = identityAPI.getUser(currentUserId)\n            SearchOptionsBuilder searchOptionBuilder = new SearchOptionsBuilder(0, 1)\n\n            if(apiCallContext.getParameters().get(\"user\") != null\n                    && apiCallContext.getParameters().get(\"user\").length > 0) {\n                if (!processAPI.isUserProcessSupervisor(processDefinitionId, currentUserId)) {\n                    return false\n                }\n                searchOptionBuilder.filter(UserSearchDescriptor.ID, apiCallContext.getParameters().get(\"user\")[0])\n            } else {\n                searchOptionBuilder.filter(UserSearchDescriptor.ID, user.getId())\n            }\n\n            SearchResult<User> listUsers = processAPI.searchUsersWhoCanStartProcessDefinition(processDefinitionId, searchOptionBuilder.done())\n            logger.debug(\"RuleCase : nb Result [\" + listUsers.getCount() + \"] ?\")\n            def canStart = listUsers.getCount() == 1\n            logger.debug(\"RuleCase : User allowed to start? \" + canStart)\n            return canStart\n        } catch (NotFoundException e) {\n            //exception, allow user to have the 404 when the rest api will look for the resource:\n            return true\n        }\n    }\n\n    private long getProcessDefinitionId(APICallContext apiCallContext) {\n        def compoundResourceId = apiCallContext.getCompoundResourceId()\n        if (compoundResourceId == null || compoundResourceId.isEmpty()) {\n            return -1L\n        }\n        return Long.valueOf(compoundResourceId.get(0))\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/ProcessPermissionRule.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\n\n\n\n\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.api.permission.PermissionRule\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoSearchDescriptor\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor\nimport org.bonitasoft.engine.search.SearchOptionsBuilder\nimport org.bonitasoft.engine.session.APISession\n\n/**\n *\n * Let the user do get only on processes he deployed or that he supervised\n *\n *\n * can be added to\n * <ul>\n *     <li>bpm/process</li>\n * </ul>\n *\n *\n *\n * @author Baptiste Mesta\n */\nclass ProcessPermissionRule implements PermissionRule {\n\n\n    @Override\n    public boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) {\n        long currentUserId = apiSession.getUserId()\n        if (apiCallContext.isGET()) {\n            return checkGetMethod(apiCallContext, apiAccessor, currentUserId, logger)\n        }\n        if (apiCallContext.isPUT()) {\n            return checkPutMethod(apiCallContext, apiAccessor, currentUserId, logger)\n        }\n        return false\n    }\n\n\n    private boolean checkGetMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId, Logger logger) {\n        def processAPI = apiAccessor.getProcessAPI()\n        def filters = apiCallContext.getFilters()\n        def resourceIds = apiCallContext.getCompoundResourceId()\n        if (!resourceIds.isEmpty()) {\n            def processId = Long.parseLong(resourceIds.get(0))\n            def processDefinition = processAPI.getProcessDeploymentInfo(processId)\n            def deployedByUser = processDefinition.getDeployedBy() == currentUserId\n            if (deployedByUser) {\n                logger.debug(\"deployed by the current user\")\n                return true\n            }\n            def canStart = processAPI.searchProcessDeploymentInfosCanBeStartedBy(currentUserId, new SearchOptionsBuilder(0, 0).filter(ProcessDeploymentInfoSearchDescriptor.PROCESS_ID, processDefinition.getProcessId()).done())\n            if (canStart.getCount() > 0) {\n                logger.debug(\"can start the process, so can get it\")\n                return true\n            }\n            // look for available tasks with this direct process ID including processes which are started by call activities (subprocesses)\n            def canExecuteTask = processAPI.searchMyAvailableHumanTasks(currentUserId, new SearchOptionsBuilder(0, 0).filter(HumanTaskInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processId).done())\n            if(canExecuteTask.getCount() > 0) {\n                logger.debug(\"has at least one task on process, so can get it\")\n                return true\n            }\n            def isSupervisor = processAPI.isUserProcessSupervisor(processId, currentUserId)\n            if (isSupervisor) {\n                logger.debug(\"is supervisor of the process\")\n                return true\n            }\n            // look for available tasks with root process ID including the subprocesses of the process with this ID\n            def hasTasksInRootOrSubprocesses = processAPI.searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(currentUserId, new SearchOptionsBuilder(0, 0).filter(ProcessDeploymentInfoSearchDescriptor.PROCESS_ID, processDefinition.getProcessId()).done())\n            if (hasTasksInRootOrSubprocesses.getCount() > 0) {\n                logger.debug(\"can execute process tasks, so can get the process\")\n                return true\n            }\n            return false\n        } else {\n            def stringUserId = String.valueOf(currentUserId)\n            if (stringUserId.equals(filters.get(\"team_manager_id\")) || stringUserId.equals(filters.get(\"supervisor_id\")) || stringUserId.equals(filters.get(\"user_id\"))) {\n                logger.debug(\"allowed because searching filters contains user id\")\n                return true\n            }\n        }\n        return false\n    }\n    private boolean checkPutMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId, Logger logger) {\n        def resourceIds = apiCallContext.getCompoundResourceId()\n        if (!resourceIds.isEmpty()) {\n            def processId = Long.parseLong(resourceIds.get(0))\n            def processAPI = apiAccessor.getProcessAPI()\n            def isSupervisor = processAPI.isUserProcessSupervisor(processId, currentUserId)\n            if(isSupervisor){\n                logger.debug(\"is supervisor of the process\")\n                return true\n            }\n            return false\n        }\n        return true\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/ProcessResolutionProblemPermissionRule.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\n\n\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.api.permission.PermissionRule\nimport org.bonitasoft.engine.session.APISession\n\n/**\n *\n * Let a user see process resolution problem only if he is process supervisor\n *\n * <ul>\n *     <li>bpm/processResolutionProblem</li>\n * </ul>\n *\n *\n *\n * @author Anthony Birembaut\n */\nclass ProcessResolutionProblemPermissionRule implements PermissionRule {\n\n    public static final String PROCESS_ID = \"process_id\"\n\n    @Override\n    public boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) {\n        long currentUserId = apiSession.getUserId()\n        if (apiCallContext.isGET()) {\n            return checkGetMethod(apiCallContext, apiAccessor, currentUserId, logger)\n        }\n        return true\n    }\n\n    private boolean checkGetMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId, Logger logger) {\n        def filters = apiCallContext.getFilters()\n        if(filters.containsKey(PROCESS_ID)){\n            def processAPI = apiAccessor.getProcessAPI()\n            return processAPI.isUserProcessSupervisor(Long.valueOf(filters.get(PROCESS_ID)),currentUserId)\n        }\n        return false\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/ProcessSupervisorPermissionRule.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\n\n\nimport com.fasterxml.jackson.databind.ObjectMapper\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.api.permission.PermissionRule\nimport org.bonitasoft.engine.session.APISession\n\n/**\n *\n * Let a user view and add process only if he is process supervisor\n *\n * <ul>\n *     <li>bpm/processSupervisor</li>\n * </ul>\n *\n *\n *\n * @author Anthony Birembaut\n */\nclass ProcessSupervisorPermissionRule implements PermissionRule {\n\n    public static final String PROCESS_ID = \"process_id\"\n\n    @Override\n    public boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) {\n        long currentUserId = apiSession.getUserId()\n        if (apiCallContext.isPOST()) {\n            return checkPostMethod(apiCallContext, apiAccessor, currentUserId, logger)\n        } else if (apiCallContext.isGET()) {\n            return checkGetMethod(apiCallContext, apiAccessor, currentUserId, logger)\n        } else if (apiCallContext.isDELETE()) {\n            return checkDeleteMethod(apiCallContext, apiAccessor, currentUserId, logger)\n        }\n        //it's ok to read\n        return true\n    }\n\n    private boolean checkPostMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId, Logger logger) {\n\n        ObjectMapper mapper = new ObjectMapper()\n        def map = mapper.readValue(apiCallContext.getBody(), Map.class)\n\n        def processAPI = apiAccessor.getProcessAPI()\n\n        def processIdString = map.get(\"process_id\")\n        if (processIdString == null || processIdString.toString().isEmpty()) {\n            return false\n        }\n        def processId = Long.valueOf(processIdString.toString())\n        if (processId <= 0) {\n            return false\n        }\n\n        return processAPI.isUserProcessSupervisor(processId, currentUserId)\n    }\n\n    private boolean checkGetMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId, Logger logger) {\n        def filters = apiCallContext.getFilters()\n        if (filters.containsKey(PROCESS_ID)) {\n            def processAPI = apiAccessor.getProcessAPI()\n            return processAPI.isUserProcessSupervisor(Long.parseLong(filters.get(PROCESS_ID)), currentUserId)\n        }\n        return true\n    }\n\n    private boolean checkDeleteMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId, Logger logger) {\n        def resourceIds = apiCallContext.getCompoundResourceId()\n        if (!resourceIds.isEmpty()) {\n            def processAPI = apiAccessor.getProcessAPI()\n            return processAPI.isUserProcessSupervisor(Long.parseLong(resourceIds.get(0)), currentUserId)\n        }\n        return true\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/ProfilePermissionRule.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\n\n\n\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.api.permission.PermissionRule\nimport org.bonitasoft.engine.exception.NotFoundException\nimport org.bonitasoft.engine.profile.ProfileCriterion\nimport org.bonitasoft.engine.session.APISession\n\n/**\n *\n * Secure profile related resources\n *\n * can be added to\n * <ul>\n *     <li>portal/profile</li>\n * </ul>\n * @author Baptiste Mesta\n */\nclass ProfilePermissionRule  implements PermissionRule {\n\n    @Override\n    boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) {\n        try {\n            if (apiCallContext.isGET()) {\n                if(apiCallContext.getResourceId() != null){\n                    def profileId = Long.valueOf(apiCallContext.getResourceId())\n                    def processAPI = apiAccessor.getProfileAPI()\n\n                    def index = 0\n                    def profile\n                    def list = []\n                    while ((list = processAPI.getProfilesForUser(apiSession.getUserId(),index,100,ProfileCriterion.ID_ASC)).size() == 100 && (profile = list.find{it.getId() == profileId}) == null){\n                        index += 100\n                    }\n                    return profile != null || list.find{it.getId() == profileId} != null\n                } else {\n                    return apiSession.getUserId().toString().equals(apiCallContext.getFilters().get(\"user_id\"))\n                }\n            }\n            return false\n        } catch (NotFoundException e) {\n            logger.debug(\"profile not found: is allowed\")\n            //let the API handle the 404\n            return true\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/TaskExecutionPermissionRule.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\n\n\nimport com.fasterxml.jackson.databind.ObjectMapper\n\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.ProcessAPI\nimport org.bonitasoft.engine.api.IdentityAPI\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.api.permission.PermissionRule\nimport org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstance\nimport org.bonitasoft.engine.bpm.flownode.ArchivedManualTaskInstance\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance\nimport org.bonitasoft.engine.bpm.flownode.ManualTaskInstance\nimport org.bonitasoft.engine.exception.NotFoundException\nimport org.bonitasoft.engine.identity.User\nimport org.bonitasoft.engine.identity.UserSearchDescriptor\nimport org.bonitasoft.engine.search.SearchOptionsBuilder\nimport org.bonitasoft.engine.session.APISession\n\n/**\n *\n * Let a user access only tasks that are assigned or pending to him\n *\n *\n * can be added to\n * <ul>\n *     <li>bpm/archivedUserTask/[id]/context</li>\n *     <li>bpm/userTask/[id]/context</li>\n *     <li>bpm/userTask/[id]/contract</li>\n *     <li>bpm/userTask/[id]/execution</li>\n * </ul>\n *\n *\n * @author Anthony Birembaut\n */\nclass TaskExecutionPermissionRule implements PermissionRule {\n\n    private static final String USER_PARAM = \"user\"\n\n    @Override\n    public boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) {\n        long currentUserId = apiSession.getUserId()\n        def userName = apiSession.getUserName()\n        def processAPI = apiAccessor.getProcessAPI()\n        def identityAPI = apiAccessor.getIdentityAPI()\n        try {\n            return isTaskAccessibleByUser(processAPI, identityAPI, apiCallContext, logger, currentUserId, userName)\n        } catch (NotFoundException e) {\n            logger.debug(\"flow node not found: is allowed\")\n            return true\n        }\n    }\n\n    protected boolean isTaskAccessibleByUser(ProcessAPI processAPI, IdentityAPI identityAPI, APICallContext apiCallContext, Logger logger, long currentUserId, String username) throws NotFoundException {\n        def taskInstanceId = getTaskInstanceId(apiCallContext)\n        if (taskInstanceId <= 0) {\n            return true\n        }\n        if (apiCallContext.getResourceName().startsWith(\"archived\")) {\n            return isArchivedFlowNodeAccessible(processAPI, taskInstanceId, currentUserId, username, logger)\n        } else {\n            def assignedUser = getAssignedUser(apiCallContext, identityAPI, logger)\n            return isTaskAccessible(processAPI, taskInstanceId, currentUserId, username, assignedUser, logger)\n        }\n    }\n\n    private User getAssignedUser(APICallContext apiCallContext, IdentityAPI identityAPI, Logger logger) {\n        def assignedId = apiCallContext.getParameters().get(USER_PARAM)\n        if (assignedId != null && assignedId.length > 0) {\n            try {\n                def assignedUserId = Long.valueOf(assignedId[0])\n                return identityAPI.getUser(assignedUserId)\n            } catch (Exception e) {\n                logger.debug(\"user Id is not a long or does not match an existing user\")\n            }\n        }\n        return null\n    }\n\n    private boolean isArchivedFlowNodeAccessible(ProcessAPI processAPI, long taskId, long currentUserId, String username, Logger logger) throws NotFoundException {\n        def archivedFlowNodeInstance = processAPI.getArchivedFlowNodeInstance(taskId)\n        if (FlowNodeType.MANUAL_TASK.equals(archivedFlowNodeInstance.getType()) || FlowNodeType.USER_TASK.equals(archivedFlowNodeInstance.getType())) {\n            if (currentUserId == archivedFlowNodeInstance.getExecutedBy()) {\n                return true\n            }\n            //get the last flow node in journal\n            if (archivedFlowNodeInstance.getExecutedBy() == 0){\n                try {\n                    def instance1 = processAPI.getHumanTaskInstance(archivedFlowNodeInstance.getSourceObjectId())\n                    if(currentUserId == instance1.getAssigneeId()){\n                        return true\n                    }\n                } catch(NotFoundException e){\n                    //do nothing\n                }\n            }\n        }\n        if (FlowNodeType.MANUAL_TASK.equals(archivedFlowNodeInstance.getType())) {\n            try {\n                def parentTask = processAPI.getHumanTaskInstance(archivedFlowNodeInstance.getParentContainerId())\n                if (parentTask.assigneeId > 0) {\n                    if (parentTask.assigneeId == currentUserId) {\n                        return true\n                    }\n                } else {\n                    final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 1)\n                    builder.filter(UserSearchDescriptor.USER_NAME, username)\n                    def searchResult = processAPI.searchUsersWhoCanExecutePendingHumanTask(parentTask.id, builder.done())\n                    if (searchResult.getCount() == 1l) {\n                        logger.debug(\"The parent task is pending for user\")\n                        return true\n                    }\n                }\n            } catch (NotFoundException e) {\n                try {\n                    def instance = processAPI.getArchivedActivityInstance(archivedFlowNodeInstance.getParentContainerId())\n                    //return false because it means the parent is not found, not the element itself\n                    if ((FlowNodeType.MANUAL_TASK.equals(instance.getType()) || FlowNodeType.USER_TASK.equals(instance.getType())) &&  instance.assigneeId > 0) {\n                        if (instance.assigneeId == currentUserId) {\n                            return true\n                        }\n                    }\n                } catch (NotFoundException e1) {\n                    //return false because it means the parent is not found, not the element itself\n                    return false\n                }\n            }\n        }\n        def processDefinitionId = archivedFlowNodeInstance.getProcessDefinitionId()\n        return processAPI.isUserProcessSupervisor(processDefinitionId, currentUserId)\n    }\n\n    private boolean isTaskAccessible(ProcessAPI processAPI, long flowNodeId, long currentUserId, String username, User assignedUser, Logger logger) throws NotFoundException {\n        def instance = processAPI.getFlowNodeInstance(flowNodeId)\n        if (FlowNodeType.MANUAL_TASK.equals(instance.getType()) || FlowNodeType.USER_TASK.equals(instance.getType())) {\n            if (instance.assigneeId > 0) {\n                if (instance.assigneeId == currentUserId) {\n                    return true\n                }\n            } else {\n                final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 1)\n                builder.filter(UserSearchDescriptor.USER_NAME, username)\n                def searchResult = processAPI.searchUsersWhoCanExecutePendingHumanTask(flowNodeId, builder.done())\n                if (searchResult.getCount() == 1l) {\n                    logger.debug(\"The task is pending for user\")\n                    return true\n                }\n            }\n            //we can access the task if we can access the parent of the subtask\n            if (FlowNodeType.MANUAL_TASK.equals(instance.getType())) {\n                try {\n\n                    def parentTask = processAPI.getHumanTaskInstance(instance.getParentContainerId())\n                    if (parentTask.assigneeId > 0) {\n                        if (parentTask.assigneeId == currentUserId) {\n                            return true\n                        }\n                    } else {\n                        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 1)\n                        builder.filter(UserSearchDescriptor.USER_NAME, username)\n                        def searchResult = processAPI.searchUsersWhoCanExecutePendingHumanTask(parentTask.id, builder.done())\n                        if (searchResult.getCount() == 1l) {\n                            logger.debug(\"The parent task is pending for user\")\n                            return true\n                        }\n                    }\n                } catch (NotFoundException e) {\n                    //return false because it means the parent is not found, not the element itself\n                    return false\n                }\n            }\n        }\n        def processDefinitionId = instance.getProcessDefinitionId()\n        if (processAPI.isUserProcessSupervisor(processDefinitionId, currentUserId)) {\n            if (assignedUser != null){\n                return isTaskAccessible(processAPI, flowNodeId, assignedUser.getId(), assignedUser.getUserName(), null, logger)\n            }\n            return true\n        }\n        return false\n    }\n\n    private long getTaskInstanceId(APICallContext apiCallContext) {\n        def compoundResourceId = apiCallContext.getCompoundResourceId()\n        if (compoundResourceId == null || compoundResourceId.isEmpty()) {\n            return -1L\n        }\n        return Long.valueOf(compoundResourceId.get(0))\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/TaskPermissionRule.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\n\n\nimport com.fasterxml.jackson.databind.ObjectMapper\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.ProcessAPI\nimport org.bonitasoft.engine.api.IdentityAPI\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.api.permission.PermissionRule\nimport org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstance\nimport org.bonitasoft.engine.bpm.flownode.ArchivedManualTaskInstance\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance\nimport org.bonitasoft.engine.bpm.flownode.ManualTaskInstance\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType\nimport org.bonitasoft.engine.exception.NotFoundException\nimport org.bonitasoft.engine.identity.User\nimport org.bonitasoft.engine.identity.UserSearchDescriptor\nimport org.bonitasoft.engine.search.SearchOptionsBuilder\nimport org.bonitasoft.engine.session.APISession\n\n/**\n *\n * Let a user access only tasks that are assigned or pending to him\n *\n *\n * can be added to\n * <ul>\n *     <li>bpm/humanTask</li>\n *     <li>bpm/userTask</li>\n *     <li>bpm/archivedHumanTask</li>\n *     <li>bpm/archivedUserTask</li>\n *     <li>bpm/activity</li>\n *     <li>bpm/activityReplay</li>\n *     <li>bpm/archivedActivity</li>\n *     <li>bpm/task</li>\n *     <li>bpm/archivedTask</li>\n *     <li>bpm/flowNode</li>\n *     <li>bpm/archivedFlowNode</li>\n *     <li>bpm/manualTask</li>\n *     <li>bpm/archivedManualTask</li>\n *     <li>bpm/archivedTask</li>\n * </ul>\n *\n *\n * @author Baptiste Mesta\n */\nclass TaskPermissionRule implements PermissionRule {\n\n    @Override\n    public boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) {\n        long currentUserId = apiSession.getUserId()\n        def userName = apiSession.getUserName()\n        def processAPI = apiAccessor.getProcessAPI()\n        def filters = apiCallContext.getFilters()\n        try {\n            if (apiCallContext.isGET()) {\n                return checkGetMethod(apiCallContext, processAPI, logger, currentUserId, userName, filters)\n            } else if (apiCallContext.isPUT() && apiCallContext.getResourceId() != null) {\n                def assignUser = getAssignedUser(apiCallContext, apiAccessor, logger)\n                return isTaskAccessibleByUser(processAPI, apiCallContext, logger, currentUserId, userName, assignUser)\n            } else if (apiCallContext.isPOST()) {\n                return checkPostMethod(apiCallContext, currentUserId, processAPI, userName, logger)\n            }\n        } catch (NotFoundException e) {\n            logger.debug(\"flow node not found: is allowed\")\n            return true\n        }\n        return true\n    }\n\n    private User getAssignedUser(APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) {\n        if (apiCallContext.getBody() != null) {\n            ObjectMapper mapper = new ObjectMapper()\n            def map = mapper.readValue(apiCallContext.getBody(), Map.class)\n            if (map != null) {\n                def assignedId = map.get(\"assigned_id\")\n                if (assignedId != null && !assignedId.toString().isEmpty()) {\n                    try {\n                        def assignedUserId = Long.valueOf(assignedId.toString())\n                        def identityAPI = apiAccessor.getIdentityAPI()\n                        return identityAPI.getUser(assignedUserId)\n                    } catch (Exception e) {\n                        logger.debug(\"assigned Id is not a long or does not match an existing user\")\n                    }\n                }\n            }\n        }\n        return null\n    }\n\n    private boolean checkGetMethod(APICallContext apiCallContext, ProcessAPI processAPI, Logger logger, long currentUserId, String userName, Map<String, String> filters) {\n        if (apiCallContext.getResourceId() != null) {\n            return isTaskAccessibleByUser(processAPI, apiCallContext, logger, currentUserId, userName, null)\n        } else if (hasFilter(currentUserId, filters, \"assigned_id\") || hasFilter(currentUserId, filters, \"user_id\") || hasFilter(currentUserId, filters, \"hidden_user_id\") || hasFilter(currentUserId, filters, \"supervisor_id\")) {\n            logger.debug(\"FilterOnUser or FilterOnAssignUser\")\n            return true\n        } else if (filters.containsKey(\"parentTaskId\")) {\n            def long parentTaskId = Long.parseLong(filters.get(\"parentTaskId\"))\n            try {\n                return isTaskAccessible(processAPI, filters.get(\"parentTaskId\"), currentUserId, userName, null, logger)\n            } catch (NotFoundException e) {\n                return isArchivedFlowNodeAccessible(processAPI, parentTaskId, currentUserId, userName, logger)\n            }\n        } else if (filters.containsKey(\"processId\")) {\n            def long processId = Long.valueOf(filters.get(\"processId\"))\n            return processAPI.isUserProcessSupervisor(processId, currentUserId)\n        } else if (filters.containsKey(\"caseId\") || filters.containsKey(\"parentCaseId\")) {\n            def long caseId = filters.containsKey(\"caseId\") ? Long.parseLong(filters.get(\"caseId\")) : Long.parseLong(filters.get(\"parentCaseId\"))\n            return processAPI.isUserProcessSupervisor(processAPI.getProcessInstance(caseId).getProcessDefinitionId(), currentUserId)\n        } else {\n            return false\n        }\n    }\n\n    private boolean checkPostMethod(APICallContext apiCallContext, long currentUserId, ProcessAPI processAPI, String userName, Logger logger) {\n        if (\"manualTask\".equals(apiCallContext.getResourceName())) {\n            ObjectMapper mapper = new ObjectMapper()\n            def map = mapper.readValue(apiCallContext.getBody(), Map.class)\n\n            def string = map.get(\"parentTaskId\").toString()\n            if (string == null || string.isEmpty()) {\n                return true\n            }\n            def parentTaskId = Long.valueOf(string)\n            def flowNodeInstance = processAPI.getFlowNodeInstance(parentTaskId)\n            return flowNodeInstance instanceof HumanTaskInstance && flowNodeInstance.getAssigneeId()\n        }\n        return false\n    }\n\n    private boolean hasFilter(long currentUserId, Map<String, String> filters, String assigned_id) {\n        return String.valueOf(currentUserId).equals(filters.get(assigned_id))\n    }\n\n    protected boolean isTaskAccessibleByUser(ProcessAPI processAPI, APICallContext apiCallContext, Logger logger, long currentUserId, String username, User assignedUser) throws NotFoundException {\n        if (apiCallContext.getResourceName().startsWith(\"archived\")) {\n            return isArchivedFlowNodeAccessible(processAPI, Long.valueOf(apiCallContext.getResourceId()), currentUserId, username, logger)\n        } else {\n            return isTaskAccessible(processAPI, apiCallContext.getResourceId(), currentUserId, username, assignedUser, logger)\n        }\n    }\n\n    private boolean isArchivedFlowNodeAccessible(ProcessAPI processAPI, long taskId, long currentUserId, String username, Logger logger) throws NotFoundException {\n        def archivedFlowNodeInstance = processAPI.getArchivedFlowNodeInstance(taskId)\n        if (FlowNodeType.MANUAL_TASK.equals(archivedFlowNodeInstance.getType()) || FlowNodeType.USER_TASK.equals(archivedFlowNodeInstance.getType())) {\n            if (currentUserId == archivedFlowNodeInstance.getExecutedBy()) {\n                return true\n            }\n            //get the last flow node in journal\n            if(archivedFlowNodeInstance.getExecutedBy() == 0){\n                try {\n                    def instance1 = processAPI.getHumanTaskInstance(archivedFlowNodeInstance.getSourceObjectId())\n                    if(currentUserId == instance1.getAssigneeId()){\n                        return true\n                    }\n                } catch(NotFoundException e){\n                    //do nothing\n                }\n            }\n        }\n        if (FlowNodeType.MANUAL_TASK.equals(archivedFlowNodeInstance.getType())) {\n            try {\n                def parentTask = processAPI.getHumanTaskInstance(archivedFlowNodeInstance.getParentContainerId())\n                if (parentTask.assigneeId > 0) {\n                    if (parentTask.assigneeId == currentUserId) {\n                        return true\n                    }\n                } else {\n                    final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 1)\n                    builder.filter(UserSearchDescriptor.USER_NAME, username)\n                    def searchResult = processAPI.searchUsersWhoCanExecutePendingHumanTask(parentTask.id, builder.done())\n                    if (searchResult.getCount() == 1l) {\n                        logger.debug(\"The parent task is pending for user\")\n                        return true\n                    }\n                }\n            } catch (NotFoundException e) {\n                try {\n                    def instance = processAPI.getArchivedActivityInstance(archivedFlowNodeInstance.getParentContainerId())\n                    //return false because it means the parent is not found, not the element itself\n                    if ((FlowNodeType.MANUAL_TASK.equals(instance.getType()) || FlowNodeType.USER_TASK.equals(instance.getType())) &&  instance.assigneeId > 0) {\n                        if (instance.assigneeId == currentUserId) {\n                            return true\n                        }\n                    }\n                } catch (NotFoundException e1) {\n                    //return false because it means the parent is not found, not the element itself\n                    return false\n                }\n            }\n        }\n        def processDefinitionId = archivedFlowNodeInstance.getProcessDefinitionId()\n        return processAPI.isUserProcessSupervisor(processDefinitionId, currentUserId)\n    }\n\n    private boolean isTaskAccessible(ProcessAPI processAPI, String flowNodeIdAsString, long currentUserId, String username, User assignedUser, Logger logger) throws NotFoundException {\n        def long flowNodeId = Long.valueOf(flowNodeIdAsString)\n        def instance = processAPI.getFlowNodeInstance(flowNodeId)\n        if (instance instanceof HumanTaskInstance) {\n            if (instance.assigneeId > 0) {\n                if (instance.assigneeId == currentUserId) {\n                    return true\n                }\n            } else {\n                final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 1)\n                builder.filter(UserSearchDescriptor.USER_NAME, username)\n                def searchResult = processAPI.searchUsersWhoCanExecutePendingHumanTask(flowNodeId, builder.done())\n                if (searchResult.getCount() == 1l) {\n                    logger.debug(\"The task is pending for user\")\n                    return true\n                }\n            }\n            //we can access the task if we can access the parent of the subtask\n            if (instance instanceof ManualTaskInstance) {\n                try {\n\n                    def parentTask = processAPI.getHumanTaskInstance(instance.getParentContainerId())\n                    if (parentTask.assigneeId > 0) {\n                        if (parentTask.assigneeId == currentUserId) {\n                            return true\n                        }\n                    } else {\n                        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 1)\n                        builder.filter(UserSearchDescriptor.USER_NAME, username)\n                        def searchResult = processAPI.searchUsersWhoCanExecutePendingHumanTask(parentTask.id, builder.done())\n                        if (searchResult.getCount() == 1l) {\n                            logger.debug(\"The parent task is pending for user\")\n                            return true\n                        }\n                    }\n                } catch (NotFoundException e) {\n                    //return false because it means the parent is not found, not the element itself\n                    return false\n                }\n            }\n        }\n        def processDefinitionId = instance.getProcessDefinitionId()\n        if (processAPI.isUserProcessSupervisor(processDefinitionId, currentUserId)) {\n            if (assignedUser != null){\n                return isTaskAccessible(processAPI, flowNodeIdAsString, assignedUser.getId(), assignedUser.getUserName(), null, logger)\n            }\n            return true\n        }\n        return false\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/UserPermissionRule.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\n\n\n\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.api.permission.PermissionRule\nimport org.bonitasoft.engine.exception.NotFoundException\nimport org.bonitasoft.engine.session.APISession\n\n/**\n *\n * Let the user access and modify only himself\n *\n * can be added to\n * <ul>\n *     <li>identity/user</li>\n *     <li>identity/professionalcontactdata</li>\n *     <li>identity/personalcontactdata</li>\n * </ul>\n *\n * @author Baptiste Mesta\n */\nclass UserPermissionRule implements PermissionRule {\n\n\n    @Override\n    boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) {\n        APISession session = apiSession\n        long currentUserId = session.getUserId()\n        if (apiCallContext.getResourceId() != null) {\n            def resourceId = Long.valueOf(apiCallContext.getResourceId())\n            if (resourceId.equals(currentUserId)) {\n                return true\n            }\n            return false\n        } else {\n            if (apiCallContext.getQueryString() != null\n                    && (apiCallContext.getQueryString().contains(\"d=professional_data\")\n                    || apiCallContext.getQueryString().contains(\"d=personnal_data\"))) {\n                return false\n            }\n            def filters = apiCallContext.getFilters()\n            //search by task id for the do for\n            if (filters.containsKey(\"task_id\")) {\n                def taskId = Long.valueOf(filters.get(\"task_id\"))\n                def processAPI = apiAccessor.getProcessAPI()\n                try {\n                    def flowNodeInstance = processAPI.getFlowNodeInstance(taskId)\n                    return processAPI.isUserProcessSupervisor(flowNodeInstance.getProcessDefinitionId(), currentUserId)\n                } catch (NotFoundException e) {\n                    return true\n                }\n            }\n            if (filters.containsKey(\"process_id\")) {\n                def processId = Long.valueOf(filters.get(\"process_id\"))\n                def processAPI = apiAccessor.getProcessAPI()\n                return processAPI.isUserProcessSupervisor(processId, currentUserId)\n            }\n            return false\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/EngineConfiguration.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\nimport java.util.Properties;\n\nimport io.micrometer.core.instrument.MeterRegistry;\nimport io.micrometer.core.instrument.simple.SimpleMeterRegistry;\nimport lombok.Data;\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.events.impl.EventServiceImpl;\nimport org.bonitasoft.engine.monitoring.ExecutorServiceMetricsProvider;\nimport org.bonitasoft.engine.monitoring.NoOpExecutorServiceMetricsProvider;\nimport org.bonitasoft.engine.persistence.HibernateConfigurationProvider;\nimport org.bonitasoft.engine.persistence.HibernateMetricsBinder;\nimport org.bonitasoft.engine.persistence.HibernatePersistenceService;\nimport org.bonitasoft.engine.persistence.QueryBuilderFactory;\nimport org.bonitasoft.engine.sequence.SequenceManager;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.EnableAspectJAutoProxy;\nimport org.springframework.scheduling.annotation.EnableScheduling;\n\n@EnableScheduling\n@Configuration\n@ComponentScan(\"org.bonitasoft.engine\")\n@EnableAspectJAutoProxy // Uses JDK dynamic proxies (interface-based)\n@Data\npublic class EngineConfiguration {\n\n    /**\n     * Whether the runtime should exit after its initialization or not.\n     */\n    @Value(\"${bonita.runtime.startup.update-only:false}\")\n    private boolean updateOnlyAtStartup;\n\n    @Bean(\"platformEventService\")\n    @ConditionalOnMissingBean(name = \"platformEventService\")\n    EventService platformEventService() {\n        return new EventServiceImpl();\n    }\n\n    @Bean\n    @ConditionalOnMissingBean\n    HibernatePersistenceService persistenceService(final HibernateConfigurationProvider hbmConfigurationProvider,\n            final Properties extraHibernateProperties,\n            final SequenceManager sequenceManager, HibernateMetricsBinder hibernateMetricsBinder,\n            QueryBuilderFactory queryBuilderFactory) {\n        return new HibernatePersistenceService(hbmConfigurationProvider,\n                extraHibernateProperties, sequenceManager, queryBuilderFactory, hibernateMetricsBinder);\n    }\n\n    @Bean\n    @ConditionalOnMissingBean\n    MeterRegistry meterRegistry() {\n        return new SimpleMeterRegistry();\n    }\n\n    @Bean\n    @ConditionalOnMissingBean\n    public HibernateMetricsBinder noOpHibernateMetrics() {\n        return (sessionFactory -> {\n        });\n    }\n\n    @Bean\n    @ConditionalOnMissingBean\n    public ExecutorServiceMetricsProvider noOpExecutorServiceBinder() {\n        return new NoOpExecutorServiceMetricsProvider();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/EngineInitializer.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\nimport java.io.IOException;\n\nimport org.bonitasoft.engine.api.PlatformAPI;\nimport org.bonitasoft.engine.api.impl.PlatformAPIImpl;\nimport org.bonitasoft.engine.event.PlatformStartedEvent;\nimport org.bonitasoft.engine.exception.BonitaHomeConfigurationException;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\nimport org.bonitasoft.engine.platform.PlatformManager;\nimport org.bonitasoft.engine.platform.PlatformNotFoundException;\nimport org.bonitasoft.engine.platform.session.PlatformSessionService;\nimport org.bonitasoft.engine.platform.session.SSessionException;\nimport org.bonitasoft.engine.platform.session.SSessionNotFoundException;\nimport org.bonitasoft.engine.platform.session.model.SPlatformSession;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.impl.ServiceAccessorFactory;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Orchestrates the Bonita Engine initialization and manages the platform node lifecycle.\n * <p>\n * This class is responsible for starting and stopping the Bonita Platform node (the current JVM instance).\n * It creates a temporary platform session, verifies the platform exists in the database, starts all\n * platform services, and publishes the platform started event.\n * <p>\n * <b>Initialization Flow:</b>\n * <ol>\n * <li>Obtains platform services via {@link ServiceAccessorFactory}</li>\n * <li>Creates a temporary \"SYSTEM\" platform session for initialization</li>\n * <li>Instantiates {@link PlatformAPI} for platform operations</li>\n * <li>Verifies the platform is created in the database via {@link PlatformAPI#isPlatformCreated()}</li>\n * <li>Starts the node via {@link PlatformAPI#startNode()}, which triggers {@link PlatformManager#start()}</li>\n * <li>Publishes {@link PlatformStartedEvent} to notify subscribers</li>\n * <li>Deletes the temporary platform session</li>\n * </ol>\n * <p>\n * <b>Responsibilities:</b>\n * <ul>\n * <li>Creates and manages temporary platform sessions for initialization/shutdown operations</li>\n * <li>Coordinates with {@link PlatformAPI} to start/stop the platform node</li>\n * <li>Publishes platform lifecycle events via {@link org.bonitasoft.engine.service.ServiceAccessor}</li>\n * <li>Handles graceful shutdown of the engine via {@link #unloadEngine()}</li>\n * <li>Logs engine edition and data collection information messages</li>\n * </ul>\n * <p>\n * <b>Note:</b> This class does NOT create the platform or database tables. Platform creation is handled\n * by {@link org.bonitasoft.platform.setup.PlatformSetup} before this initializer runs.\n * <p>\n * <b>Usage:</b> This class is typically invoked by\n * {@link org.bonitasoft.engine.api.internal.servlet.EngineInitializerListener}\n * during servlet container startup.\n *\n * @author Baptiste Mesta\n * @see PlatformAPI\n * @see PlatformManager\n * @see ServiceAccessorFactory\n * @see org.bonitasoft.platform.setup.PlatformSetup\n */\npublic class EngineInitializer {\n\n    protected static final Logger LOGGER = LoggerFactory.getLogger(EngineInitializer.class.getName());\n\n    private static PlatformAPI platformAPI;\n\n    public EngineInitializer() {\n        super();\n    }\n\n    public void initializeEngine() throws Exception {\n        LOGGER.info(\"Initializing Bonita Engine...\");\n        final long before = System.currentTimeMillis();\n        // create a session to call the engine\n        final ServiceAccessor platformService = getPlatformService();\n        final PlatformSessionService platformSessionService = platformService.getPlatformSessionService();\n        final SessionAccessor sessionAccessor = getSessionAccessor();\n        final long sessionId = createPlatformSession(platformSessionService, sessionAccessor);\n        final PlatformAPI platformAPI = getPlatformAPI();\n\n        try {\n            if (!platformAPI.isPlatformCreated()) {\n                throw new PlatformNotFoundException(\"Can't start or stop platform if it is not created.\");\n            }\n            LOGGER.info(\"Starting node...\");\n\n            logEditionMessage();\n            logDataCollectionMessage();\n\n            platformAPI.startNode();\n            LOGGER.info(\"Node started successfully.\");\n            final long after = System.currentTimeMillis();\n            LOGGER.info(\"Initialization of Bonita Engine done! (took \" + (after - before) + \"ms)\");\n\n            LOGGER.debug(\"Publishing platform started event\");\n            platformService.publishEvent(new PlatformStartedEvent());\n        } finally {\n            deletePlatformSession(platformSessionService, sessionAccessor, sessionId);\n        }\n    }\n\n    protected void logEditionMessage() {\n        LOGGER.info(\"  ____              _ _           _____                                      _ _         \");\n        LOGGER.info(\" |  _ \\\\            (_) |         / ____|                                    (_) |        \");\n        LOGGER.info(\" | |_) | ___  _ __  _| |_ __ _  | |     ___  _ __ ___  _ __ ___  _   _ _ __  _| |_ _   _ \");\n        LOGGER.info(\" |  _ < / _ \\\\| '_ \\\\| | __/ _` | | |    / _ \\\\| '_ ` _ \\\\| '_ ` _ \\\\| | | | '_ \\\\| | __| | | |\");\n        LOGGER.info(\" | |_) | (_) | | | | | || (_| | | |___| (_) | | | | | | | | | | | |_| | | | | | |_| |_| |\");\n        LOGGER.info(\n                \" |____/ \\\\___/|_| |_|_|\\\\__\\\\__,_|  \\\\_____\\\\___/|_| |_| |_|_| |_| |_|\\\\__,_|_| |_|_|\\\\__|\\\\__, |\");\n        LOGGER.info(\"                                                                                    __/ |\");\n        LOGGER.info(\"                                                                                   |___/ \");\n    }\n\n    public void logDataCollectionMessage() {\n        LOGGER.info(\"-----------------------------------------------------------------------------------------\");\n        LOGGER.info(\"Anonymous Data Collection for Product Improvement\");\n        LOGGER.info(\"\");\n        LOGGER.info(\"Dear User,\");\n        LOGGER.info(\"\");\n        LOGGER.info(\"We collect strictly anonymous usage data from Bonita Studio and the Runtime (production\");\n        LOGGER.info(\"environment) to help us continuously improve the product and enhance the user experience.\");\n        LOGGER.info(\"The data collected is fully anonymous and cannot be used to identify you in any way.\");\n        LOGGER.info(\"\");\n        LOGGER.info(\"This data helps us understand how the product is used in both development and production\");\n        LOGGER.info(\"settings, allowing us to optimize performance, fix bugs, and introduce new features that\");\n        LOGGER.info(\"benefit all users.\");\n        LOGGER.info(\"\");\n        LOGGER.info(\"For more information on what data we collect and how to opt-out, please visit our\");\n        LOGGER.info(\"Product Documentation (https://documentation.ofelia.com/bonita/latest).\");\n        LOGGER.info(\"\");\n        LOGGER.info(\"Thank you for supporting the ongoing improvement of our product!\");\n        LOGGER.info(\"-----------------------------------------------------------------------------------------\");\n    }\n\n    SessionAccessor getSessionAccessor() throws BonitaHomeNotSetException, IOException,\n            BonitaHomeConfigurationException, ReflectiveOperationException {\n        return getServiceAccessorFactory().createSessionAccessor();\n    }\n\n    ServiceAccessor getPlatformService() {\n        try {\n            return getServiceAccessorFactory().createServiceAccessor();\n        } catch (final Exception e) {\n            throw new BonitaRuntimeException(e);\n        }\n    }\n\n    // Visible for testing\n    PlatformAPI getPlatformAPI() {\n        if (platformAPI == null) {\n            //in local only\n            platformAPI = newPlatformAPI();\n        }\n        return platformAPI;\n    }\n\n    protected PlatformAPI newPlatformAPI() {\n        return new PlatformAPIImpl();\n    }\n\n    private void deletePlatformSession(final PlatformSessionService platformSessionService,\n            final SessionAccessor sessionAccessor, final long sessionId)\n            throws SSessionNotFoundException {\n        platformSessionService.deleteSession(sessionId);\n        sessionAccessor.deleteSessionId();\n    }\n\n    private long createPlatformSession(final PlatformSessionService platformSessionService,\n            final SessionAccessor sessionAccessor) throws SSessionException {\n        final SPlatformSession createSession = platformSessionService.createSession(\"SYSTEM\");\n        final long sessionId = createSession.getId();\n        sessionAccessor.setSessionId(sessionId);\n        return sessionId;\n    }\n\n    public void unloadEngine() throws Exception {\n        LOGGER.info(\"Stopping Bonita Engine...\");\n        // create a session to call the engine\n        final SessionAccessor sessionAccessor = getSessionAccessor();\n        final PlatformSessionService platformSessionService = getPlatformService().getPlatformSessionService();\n        final long sessionId = createPlatformSession(platformSessionService, sessionAccessor);\n        final PlatformAPI platformAPI = getPlatformAPI();\n        try {\n            if (!platformAPI.isNodeStarted()) {\n                LOGGER.info(\"Node is not started, nothing to do.\");\n                return;\n            }\n            LOGGER.info(\"Stopping node...\");\n            platformAPI.stopNode();\n        } catch (final Throwable e) {\n            LOGGER.warn(\"Error while stopping the platform\", e);\n        } finally {\n            deletePlatformSession(platformSessionService, sessionAccessor, sessionId);\n            // after that the engine is unloaded\n            getServiceAccessorFactory().destroyAccessors();\n            LOGGER.info(\"Bonita Engine stopped!\");\n        }\n\n    }\n\n    ServiceAccessorFactory getServiceAccessorFactory() {\n        return ServiceAccessorFactory.getInstance();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/LocalLoginMechanism.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.platform.PlatformLoginException;\nimport org.bonitasoft.engine.platform.session.PlatformSessionService;\nimport org.bonitasoft.engine.platform.session.model.SPlatformSession;\nimport org.bonitasoft.engine.service.impl.ServiceAccessorFactory;\nimport org.bonitasoft.engine.session.PlatformSession;\nimport org.bonitasoft.engine.session.impl.PlatformSessionImpl;\n\n/**\n * Use to login on the platform when inside the same JVM\n *\n * @author Baptiste Mesta\n */\n//Do not remove\n//used by reflection when on the same JVM\n//see org.bonitasoft.console.common.server.utils.PlatformManagementUtils.platformLogin() in bonita-web\npublic class LocalLoginMechanism {\n\n    public PlatformSession login() throws PlatformLoginException {\n        try {\n            final PlatformSessionService platformSessionService = ServiceAccessorFactory.getInstance()\n                    .createServiceAccessor().getPlatformSessionService();\n            SPlatformSession platformSession = platformSessionService.createSession(\"local\");\n            final Date creationDate = platformSession.getCreationDate();\n            return new PlatformSessionImpl(platformSession.getId(), creationDate, platformSession.getDuration(),\n                    \"local\", platformSession.getUserId());\n        } catch (final Exception e) {\n            throw new PlatformLoginException(e);\n        }\n\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/SArchivingException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SArchivingException extends SBonitaException {\n\n    private static final long serialVersionUID = -1985173032064351538L;\n\n    public SArchivingException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SArchivingException(final String message) {\n        super(message);\n    }\n\n    public SArchivingException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/APIAccessorImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport org.bonitasoft.engine.api.APIAccessor;\nimport org.bonitasoft.engine.api.ApplicationAPI;\nimport org.bonitasoft.engine.api.BusinessDataAPI;\nimport org.bonitasoft.engine.api.CommandAPI;\nimport org.bonitasoft.engine.api.IdentityAPI;\nimport org.bonitasoft.engine.api.PageAPI;\nimport org.bonitasoft.engine.api.PermissionAPI;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.ProfileAPI;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @author Baptiste Mesta\n */\npublic class APIAccessorImpl implements APIAccessor {\n\n    private static final long serialVersionUID = -3602975597536895697L;\n\n    @Override\n    public IdentityAPI getIdentityAPI() {\n        return new IdentityAPIImpl();\n    }\n\n    @Override\n    public ProcessAPI getProcessAPI() {\n        return new ProcessAPIImpl();\n    }\n\n    @Override\n    public CommandAPI getCommandAPI() {\n        return new CommandAPIImpl();\n    }\n\n    @Override\n    public ProfileAPI getProfileAPI() {\n        return new ProfileAPIImpl();\n    }\n\n    @Override\n    public PermissionAPI getPermissionAPI() {\n        return new PermissionAPIImpl();\n    }\n\n    public PageAPI getCustomPageAPI() {\n        return new PageAPIImpl();\n    }\n\n    @Override\n    public ApplicationAPI getLivingApplicationAPI() {\n        return new ApplicationAPIImpl();\n    }\n\n    @Override\n    public BusinessDataAPI getBusinessDataAPI() {\n        return new BusinessDataAPIImpl();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/APIUtils.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.ServiceAccessorSingleton;\n\n/**\n * @author Baptiste Mesta\n */\npublic class APIUtils {\n\n    protected static long getUserId() {\n        return SessionInfos.getUserIdFromSession();\n    }\n\n    protected static ServiceAccessor getServiceAccessor() {\n        try {\n            return ServiceAccessorSingleton.getInstance();\n        } catch (final Exception e) {\n            throw new RuntimeException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/ApplicationAPIImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport static org.bonitasoft.engine.business.application.ApplicationSearchDescriptor.TOKEN;\nimport static org.bonitasoft.engine.business.application.ApplicationSearchDescriptor.USER_ID;\nimport static org.bonitasoft.engine.business.application.InternalProfiles.INTERNAL_PROFILE_SUPER_ADMIN;\nimport static org.bonitasoft.engine.search.descriptor.SearchApplicationDescriptor.APPLICATION_VISIBILITY;\n\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.stream.Collectors;\n\nimport org.bonitasoft.engine.api.ApplicationAPI;\nimport org.bonitasoft.engine.api.ImportStatus;\nimport org.bonitasoft.engine.api.impl.converter.ApplicationMenuModelConverter;\nimport org.bonitasoft.engine.api.impl.converter.ApplicationModelConverter;\nimport org.bonitasoft.engine.api.impl.converter.ApplicationPageModelConverter;\nimport org.bonitasoft.engine.api.impl.livingapplication.LivingApplicationAPIDelegate;\nimport org.bonitasoft.engine.api.impl.livingapplication.LivingApplicationExporterDelegate;\nimport org.bonitasoft.engine.api.impl.livingapplication.LivingApplicationMenuAPIDelegate;\nimport org.bonitasoft.engine.api.impl.livingapplication.LivingApplicationPageAPIDelegate;\nimport org.bonitasoft.engine.api.impl.transaction.application.SearchApplicationMenus;\nimport org.bonitasoft.engine.api.impl.transaction.application.SearchApplicationPages;\nimport org.bonitasoft.engine.api.impl.transaction.application.SearchApplications;\nimport org.bonitasoft.engine.api.impl.transaction.application.SearchApplicationsOfUser;\nimport org.bonitasoft.engine.business.application.Application;\nimport org.bonitasoft.engine.business.application.ApplicationCreator;\nimport org.bonitasoft.engine.business.application.ApplicationImportPolicy;\nimport org.bonitasoft.engine.business.application.ApplicationLink;\nimport org.bonitasoft.engine.business.application.ApplicationLinkCreator;\nimport org.bonitasoft.engine.business.application.ApplicationLinkUpdater;\nimport org.bonitasoft.engine.business.application.ApplicationMenu;\nimport org.bonitasoft.engine.business.application.ApplicationMenuCreator;\nimport org.bonitasoft.engine.business.application.ApplicationMenuNotFoundException;\nimport org.bonitasoft.engine.business.application.ApplicationMenuUpdater;\nimport org.bonitasoft.engine.business.application.ApplicationNotFoundException;\nimport org.bonitasoft.engine.business.application.ApplicationPage;\nimport org.bonitasoft.engine.business.application.ApplicationPageNotFoundException;\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.ApplicationUpdater;\nimport org.bonitasoft.engine.business.application.IApplication;\nimport org.bonitasoft.engine.business.application.Icon;\nimport org.bonitasoft.engine.business.application.converter.ApplicationMenuToNodeConverter;\nimport org.bonitasoft.engine.business.application.converter.ApplicationPageToNodeConverter;\nimport org.bonitasoft.engine.business.application.converter.ApplicationToNodeConverter;\nimport org.bonitasoft.engine.business.application.converter.ApplicationsToNodeContainerConverter;\nimport org.bonitasoft.engine.business.application.converter.NodeToApplicationConverter;\nimport org.bonitasoft.engine.business.application.exporter.ApplicationContainerExporter;\nimport org.bonitasoft.engine.business.application.exporter.ApplicationExporter;\nimport org.bonitasoft.engine.business.application.importer.StrategySelector;\nimport org.bonitasoft.engine.business.application.importer.validator.ApplicationImportValidator;\nimport org.bonitasoft.engine.business.application.importer.validator.ApplicationMenuCreatorValidator;\nimport org.bonitasoft.engine.business.application.importer.validator.ApplicationTokenValidator;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.ExportException;\nimport org.bonitasoft.engine.exception.ImportException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.page.PageService;\nimport org.bonitasoft.engine.profile.ProfileService;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.search.descriptor.SearchApplicationDescriptor;\nimport org.bonitasoft.engine.search.descriptor.SearchApplicationMenuDescriptor;\nimport org.bonitasoft.engine.search.descriptor.SearchApplicationPageDescriptor;\nimport org.bonitasoft.engine.search.impl.SearchFilter;\nimport org.bonitasoft.engine.search.impl.SearchResultImpl;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.impl.ServiceAccessorFactory;\nimport org.bonitasoft.engine.session.SessionService;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@AvailableInMaintenanceMode\npublic class ApplicationAPIImpl implements ApplicationAPI {\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    @Deprecated(since = \"9.0.0\")\n    public Application createApplication(final ApplicationCreator applicationCreator)\n            throws CreationException {\n        return getLivingApplicationAPIDelegate().createApplication(applicationCreator);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    @Deprecated(since = \"10.2.0\")\n    public ApplicationLink createApplicationLink(final ApplicationLinkCreator applicationLinkCreator)\n            throws CreationException {\n        return getLivingApplicationAPIDelegate().createApplicationLink(applicationLinkCreator);\n    }\n\n    private LivingApplicationAPIDelegate getLivingApplicationAPIDelegate() {\n        return new LivingApplicationAPIDelegate(getServiceAccessor(),\n                getApplicationModelConverter(getServiceAccessor().getPageService()),\n                SessionInfos.getUserIdFromSession(), new ApplicationTokenValidator());\n    }\n\n    protected ApplicationModelConverter getApplicationModelConverter(final PageService pageService) {\n        return new ApplicationModelConverter(pageService);\n    }\n\n    private LivingApplicationPageAPIDelegate getApplicationPageAPIDelegate() {\n        return new LivingApplicationPageAPIDelegate(getServiceAccessor(), new ApplicationPageModelConverter(),\n                SessionInfos.getUserIdFromSession(),\n                new ApplicationTokenValidator());\n    }\n\n    private LivingApplicationMenuAPIDelegate getApplicationMenuAPIDelegate() {\n        return new LivingApplicationMenuAPIDelegate(getServiceAccessor(), new ApplicationMenuModelConverter(),\n                new ApplicationMenuCreatorValidator(),\n                SessionInfos.getUserIdFromSession());\n    }\n\n    private LivingApplicationExporterDelegate getLivingApplicationExporterDelegate() {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ApplicationService applicationService = serviceAccessor.getApplicationService();\n        final PageService pageService = serviceAccessor.getPageService();\n        ApplicationToNodeConverter applicationToNodeConverter = new ApplicationToNodeConverter(\n                serviceAccessor.getProfileService(), applicationService,\n                new ApplicationPageToNodeConverter(pageService), new ApplicationMenuToNodeConverter(applicationService),\n                pageService);\n        final ApplicationsToNodeContainerConverter applicationsToNodeContainerConverter = new ApplicationsToNodeContainerConverter(\n                applicationToNodeConverter);\n        final ApplicationContainerExporter applicationContainerExporter = new ApplicationContainerExporter();\n        final ApplicationExporter applicationExporter = new ApplicationExporter(applicationsToNodeContainerConverter,\n                applicationContainerExporter);\n        return new LivingApplicationExporterDelegate(serviceAccessor.getApplicationService(), applicationExporter);\n    }\n\n    protected NodeToApplicationConverter getNodeToApplicationConverter(final PageService pageService,\n            final ProfileService profileService, final ApplicationImportValidator importValidator) {\n        return new NodeToApplicationConverter(profileService, pageService, importValidator);\n\n    }\n\n    @Override\n    public IApplication getIApplication(final long applicationId) throws ApplicationNotFoundException {\n        return getLivingApplicationAPIDelegate().getIApplication(applicationId);\n    }\n\n    @Override\n    public IApplication getIApplicationByToken(final String applicationToken) throws ApplicationNotFoundException {\n        return getLivingApplicationAPIDelegate().getIApplicationByToken(applicationToken);\n    }\n\n    @Override\n    public void deleteApplication(final long applicationId) throws DeletionException {\n        getLivingApplicationAPIDelegate().deleteApplication(applicationId);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    @Deprecated(since = \"9.0.0\")\n    public Application updateApplication(final long applicationId, final ApplicationUpdater updater)\n            throws ApplicationNotFoundException, UpdateException,\n            AlreadyExistsException {\n        return getLivingApplicationAPIDelegate().updateApplication(applicationId, updater);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    @Deprecated(since = \"10.2.0\")\n    public ApplicationLink updateApplicationLink(final long applicationId, final ApplicationLinkUpdater updater)\n            throws ApplicationNotFoundException, UpdateException,\n            AlreadyExistsException {\n        return getLivingApplicationAPIDelegate().updateApplicationLink(applicationId, updater);\n    }\n\n    protected ServiceAccessor getServiceAccessor() {\n        try {\n            return ServiceAccessorFactory.getInstance().createServiceAccessor();\n        } catch (final Exception e) {\n            throw new BonitaRuntimeException(e);\n        }\n    }\n\n    /**\n     * @deprecated as of 10.2.0, use {@link #searchIApplications(SearchOptions)} instead to include application links.\n     */\n    @Deprecated(since = \"10.2.0\")\n    @Override\n    public SearchResult<Application> searchApplications(final SearchOptions searchOptions) throws SearchException {\n        if (searchOptions.getFilters().size() == 1) {\n            // Avoid a search query for a search by token to benefit from the cache optimization\n            final SearchFilter searchFilter = searchOptions.getFilters().get(0);\n            if (TOKEN.equals(searchFilter.getField())) {\n                try {\n                    return new SearchResultImpl<>(1, List.of(getApplicationByToken((String) searchFilter.getValue())));\n                } catch (ApplicationNotFoundException e) {\n                    return new SearchResultImpl<>(0, List.of());\n                }\n            }\n        }\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final SearchApplicationDescriptor appSearchDescriptor = serviceAccessor.getSearchEntitiesDescriptor()\n                .getSearchApplicationDescriptor();\n        final ApplicationModelConverter converter = getApplicationModelConverter(serviceAccessor.getPageService());\n        final ApplicationService applicationService = serviceAccessor.getApplicationService();\n        final Optional<SearchFilter> filterOnUserId = searchOptions.getFilters().stream()\n                .filter(s -> s.getField().equals(USER_ID)).findFirst();\n        if (filterOnUserId.isPresent()) {\n            final SearchOptionsBuilder searchOptionsWithoutUserId = new SearchOptionsBuilder(searchOptions)\n                    .setFilters(searchOptions.getFilters().stream().filter(s -> !s.getField().equals(USER_ID))\n                            .collect(Collectors.toList()));\n            final String userId = String.valueOf(filterOnUserId.get().getValue());\n            if (String.valueOf(SessionService.SYSTEM_ID).equals(userId)) { // It's the tenant admin user\n                return getLivingApplicationAPIDelegate().searchIApplications(new SearchApplications(\n                        Application.class,\n                        applicationService, appSearchDescriptor,\n                        searchOptionsWithoutUserId\n                                .filter(APPLICATION_VISIBILITY, INTERNAL_PROFILE_SUPER_ADMIN.getProfileName()).done(),\n                        converter));\n            } else {\n                return getLivingApplicationAPIDelegate().searchIApplications(new SearchApplicationsOfUser(\n                        Application.class,\n                        Long.parseLong(userId), applicationService, appSearchDescriptor,\n                        searchOptionsWithoutUserId.done(), converter));\n            }\n        }\n        return getLivingApplicationAPIDelegate()\n                .searchIApplications(new SearchApplications(Application.class, applicationService, appSearchDescriptor,\n                        searchOptions, converter));\n    }\n\n    @Override\n    public SearchResult<IApplication> searchIApplications(final SearchOptions searchOptions) throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final SearchApplicationDescriptor appSearchDescriptor = serviceAccessor.getSearchEntitiesDescriptor()\n                .getSearchApplicationDescriptor();\n        return internalSearchIApplications(serviceAccessor, appSearchDescriptor, searchOptions);\n    }\n\n    protected SearchResult<IApplication> internalSearchIApplications(ServiceAccessor serviceAccessor,\n            SearchApplicationDescriptor appSearchDescriptor, SearchOptions searchOptions) throws SearchException {\n        if (searchOptions.getFilters().size() == 1) {\n            // Avoid a search query for a search by token to benefit from the cache optimization\n            final SearchFilter searchFilter = searchOptions.getFilters().get(0);\n            if (TOKEN.equals(searchFilter.getField())) {\n                try {\n                    return new SearchResultImpl<>(1, List.of(getIApplicationByToken((String) searchFilter.getValue())));\n                } catch (ApplicationNotFoundException e) {\n                    return new SearchResultImpl<>(0, List.of());\n                }\n            }\n        }\n        final ApplicationModelConverter converter = getApplicationModelConverter(serviceAccessor.getPageService());\n        final ApplicationService applicationService = serviceAccessor.getApplicationService();\n        final Optional<SearchFilter> filterOnUserId = searchOptions.getFilters().stream()\n                .filter(s -> s.getField().equals(USER_ID)).findFirst();\n        if (filterOnUserId.isPresent()) {\n            final SearchOptionsBuilder searchOptionsWithoutUserId = new SearchOptionsBuilder(searchOptions)\n                    .setFilters(searchOptions.getFilters().stream().filter(s -> !s.getField().equals(USER_ID))\n                            .collect(Collectors.toList()));\n            final String userId = String.valueOf(filterOnUserId.get().getValue());\n            if (String.valueOf(SessionService.SYSTEM_ID).equals(userId)) { // It's the tenant admin user\n                return getLivingApplicationAPIDelegate()\n                        .searchIApplications(SearchApplications.defaultSearchApplications(\n                                applicationService, appSearchDescriptor,\n                                searchOptionsWithoutUserId\n                                        .filter(APPLICATION_VISIBILITY, INTERNAL_PROFILE_SUPER_ADMIN.getProfileName())\n                                        .done(),\n                                converter));\n            } else {\n                return getLivingApplicationAPIDelegate()\n                        .searchIApplications(SearchApplicationsOfUser.defaultSearchApplicationsOfUser(\n                                Long.parseLong(userId), applicationService, appSearchDescriptor,\n                                searchOptionsWithoutUserId.done(), converter));\n            }\n        }\n        return getLivingApplicationAPIDelegate()\n                .searchIApplications(\n                        SearchApplications.defaultSearchApplications(applicationService, appSearchDescriptor,\n                                searchOptions, converter));\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    @Deprecated(since = \"9.0.0\")\n    public ApplicationPage createApplicationPage(final long applicationId, final long pageId, final String token)\n            throws CreationException {\n        return getApplicationPageAPIDelegate().createApplicationPage(applicationId, pageId, token);\n    }\n\n    @Override\n    public ApplicationPage getApplicationPage(final String applicationName, final String applicationPageToken)\n            throws ApplicationPageNotFoundException {\n        return getApplicationPageAPIDelegate().getApplicationPage(applicationName, applicationPageToken);\n    }\n\n    @Override\n    public SearchResult<ApplicationPage> searchApplicationPages(final SearchOptions searchOptions)\n            throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final SearchApplicationPageDescriptor appPageSearchDescriptor = serviceAccessor.getSearchEntitiesDescriptor()\n                .getSearchApplicationPageDescriptor();\n        final ApplicationPageModelConverter converter = new ApplicationPageModelConverter();\n        final ApplicationService applicationService = serviceAccessor.getApplicationService();\n        final SearchApplicationPages searchApplicationPages = new SearchApplicationPages(applicationService, converter,\n                appPageSearchDescriptor, searchOptions);\n        return getApplicationPageAPIDelegate().searchApplicationPages(searchApplicationPages);\n    }\n\n    @Override\n    public ApplicationPage getApplicationPage(final long applicationPageId) throws ApplicationPageNotFoundException {\n        return getApplicationPageAPIDelegate().getApplicationPage(applicationPageId);\n    }\n\n    @Override\n    public void deleteApplicationPage(final long applicationPageId) throws DeletionException {\n        getApplicationPageAPIDelegate().deleteApplicationPage(applicationPageId);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    @Deprecated(since = \"9.0.0\")\n    public void setApplicationHomePage(final long applicationId, final long applicationPageId)\n            throws UpdateException, ApplicationNotFoundException {\n        getApplicationPageAPIDelegate().setApplicationHomePage(applicationId, applicationPageId);\n    }\n\n    @Override\n    public ApplicationPage getApplicationHomePage(final long applicationId) throws ApplicationPageNotFoundException {\n        return getApplicationPageAPIDelegate().getApplicationHomePage(applicationId);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    @Deprecated(since = \"9.0.0\")\n    public ApplicationMenu createApplicationMenu(final ApplicationMenuCreator applicationMenuCreator)\n            throws CreationException {\n        return getApplicationMenuAPIDelegate().createApplicationMenu(applicationMenuCreator);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    @Deprecated(since = \"9.0.0\")\n    public ApplicationMenu updateApplicationMenu(final long applicationMenuId, final ApplicationMenuUpdater updater)\n            throws ApplicationMenuNotFoundException,\n            UpdateException {\n        return getApplicationMenuAPIDelegate().updateApplicationMenu(applicationMenuId, updater);\n    }\n\n    @Override\n    public ApplicationMenu getApplicationMenu(final long applicationMenuId) throws ApplicationMenuNotFoundException {\n        return getApplicationMenuAPIDelegate().getApplicationMenu(applicationMenuId);\n    }\n\n    @Override\n    public void deleteApplicationMenu(final long applicationMenuId) throws DeletionException {\n        getApplicationMenuAPIDelegate().deleteApplicationMenu(applicationMenuId);\n    }\n\n    @Override\n    public SearchResult<ApplicationMenu> searchApplicationMenus(final SearchOptions searchOptions)\n            throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ApplicationService applicationService = serviceAccessor.getApplicationService();\n        final ApplicationMenuModelConverter converter = new ApplicationMenuModelConverter();\n        final SearchApplicationMenuDescriptor searchDescriptor = serviceAccessor.getSearchEntitiesDescriptor()\n                .getSearchApplicationMenuDescriptor();\n        final SearchApplicationMenus searchApplicationMenus = new SearchApplicationMenus(applicationService, converter,\n                searchDescriptor, searchOptions);\n        return getApplicationMenuAPIDelegate().searchApplicationMenus(searchApplicationMenus);\n    }\n\n    @Override\n    public List<String> getAllPagesForProfile(final long profileId) {\n        return getApplicationPageAPIDelegate().getAllPagesForProfile(profileId);\n    }\n\n    @Override\n    public List<String> getAllPagesForProfile(String profile) {\n        return getApplicationPageAPIDelegate().getAllPagesForProfile(profile);\n    }\n\n    @Override\n    public byte[] exportApplications(final long... applicationIds) throws ExportException {\n        return getLivingApplicationExporterDelegate().exportApplications(applicationIds);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    @Deprecated(since = \"9.0.0\")\n    public List<ImportStatus> importApplications(final byte[] xmlContent, final ApplicationImportPolicy policy)\n            throws ImportException, AlreadyExistsException {\n        return getServiceAccessor().getApplicationImporter().importApplications(xmlContent, null, null,\n                SessionInfos.getUserIdFromSession(),\n                new StrategySelector().selectStrategy(policy));\n    }\n\n    @Override\n    public Icon getIconOfApplication(long applicationId) throws ApplicationNotFoundException {\n        return getLivingApplicationAPIDelegate().getIconOfApplication(applicationId);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/AvailableInMaintenanceMode.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * Used to identify API methods that can be called if the maintenance mode is enabled. All other API method\n * calls will be rejected.\n * Used by the Bonita Engine server interceptor.\n *\n * @author Emmanuel Duchastenier\n * @author Matthieu Chaffotte\n * @author Anthony Birembaut\n */\n@Target({ ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface AvailableInMaintenanceMode {\n\n    boolean onlyAvailableInMaintenanceMode() default false;\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/AvailableOnStoppedNode.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * Used to identify API methods that can be called if a Node is not started. All other API method calls will be\n * rejected.\n * Used by the Bonita Engine server interceptor.\n *\n * @author Emmanuel Duchastenier\n */\n@Target(ElementType.METHOD)\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface AvailableOnStoppedNode {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/BusinessDataAPIImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.BusinessDataAPI;\nimport org.bonitasoft.engine.bpm.data.DataNotFoundException;\nimport org.bonitasoft.engine.business.data.BusinessDataReference;\nimport org.bonitasoft.engine.business.data.converter.BusinessDataModelConverter;\nimport org.bonitasoft.engine.core.process.instance.api.RefBusinessDataService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.exception.RetrieveException;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.ServiceAccessorSingleton;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class BusinessDataAPIImpl implements BusinessDataAPI {\n\n    protected ServiceAccessor getServiceAccessor() {\n        return ServiceAccessorSingleton.getInstance();\n    }\n\n    @Override\n    public BusinessDataReference getProcessBusinessDataReference(final String businessDataName,\n            final long processInstanceId) throws DataNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        try {\n            final RefBusinessDataService refBusinessDataService = serviceAccessor.getRefBusinessDataService();\n            final SRefBusinessDataInstance sReference = refBusinessDataService.getRefBusinessDataInstance(\n                    businessDataName,\n                    processInstanceId);\n            if (sReference instanceof SSimpleRefBusinessDataInstance) {\n                return BusinessDataModelConverter\n                        .toSimpleBusinessDataReference((SSimpleRefBusinessDataInstance) sReference);\n            } else {\n                return BusinessDataModelConverter\n                        .toMultipleBusinessDataReference((SProcessMultiRefBusinessDataInstance) sReference);\n            }\n        } catch (final SRefBusinessDataInstanceNotFoundException srbdnfe) {\n            throw new DataNotFoundException(srbdnfe);\n        } catch (final SBonitaReadException sbre) {\n            throw new RetrieveException(sbre);\n        }\n    }\n\n    @Override\n    public List<BusinessDataReference> getProcessBusinessDataReferences(final long processInstanceId,\n            final int startIndex, final int maxResults) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        try {\n            final RefBusinessDataService refBusinessDataService = serviceAccessor.getRefBusinessDataService();\n            final List<SRefBusinessDataInstance> sReferences = refBusinessDataService\n                    .getRefBusinessDataInstances(processInstanceId, startIndex, maxResults);\n            final List<BusinessDataReference> references = new ArrayList<BusinessDataReference>();\n            for (final SRefBusinessDataInstance sReference : sReferences) {\n                if (sReference instanceof SSimpleRefBusinessDataInstance) {\n                    references.add(BusinessDataModelConverter\n                            .toSimpleBusinessDataReference((SSimpleRefBusinessDataInstance) sReference));\n                } else {\n                    references.add(BusinessDataModelConverter\n                            .toMultipleBusinessDataReference((SProcessMultiRefBusinessDataInstance) sReference));\n                }\n            }\n            return references;\n        } catch (final SBonitaReadException sbre) {\n            throw new RetrieveException(sbre);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/CommandAPIImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.bonitasoft.engine.api.CommandAPI;\nimport org.bonitasoft.engine.api.impl.transaction.CustomTransactions;\nimport org.bonitasoft.engine.api.impl.transaction.command.DeleteSCommand;\nimport org.bonitasoft.engine.api.impl.transaction.command.GetCommands;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.classloader.ClassLoaderIdentifier;\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.classloader.SClassLoaderException;\nimport org.bonitasoft.engine.command.CommandCriterion;\nimport org.bonitasoft.engine.command.CommandDescriptor;\nimport org.bonitasoft.engine.command.CommandExecutionException;\nimport org.bonitasoft.engine.command.CommandNotFoundException;\nimport org.bonitasoft.engine.command.CommandParameterizationException;\nimport org.bonitasoft.engine.command.CommandService;\nimport org.bonitasoft.engine.command.CommandUpdater;\nimport org.bonitasoft.engine.command.CommandUpdater.CommandField;\nimport org.bonitasoft.engine.command.DependencyNotFoundException;\nimport org.bonitasoft.engine.command.RuntimeCommand;\nimport org.bonitasoft.engine.command.SCommandDeletionException;\nimport org.bonitasoft.engine.command.SCommandExecutionException;\nimport org.bonitasoft.engine.command.SCommandNotFoundException;\nimport org.bonitasoft.engine.command.SCommandParameterizationException;\nimport org.bonitasoft.engine.command.SCommandUpdateException;\nimport org.bonitasoft.engine.command.model.SCommand;\nimport org.bonitasoft.engine.command.model.SCommandCriterion;\nimport org.bonitasoft.engine.command.model.SCommandUpdateBuilder;\nimport org.bonitasoft.engine.command.model.SCommandUpdateBuilderFactory;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.dependency.DependencyService;\nimport org.bonitasoft.engine.dependency.SDependencyAlreadyExistsException;\nimport org.bonitasoft.engine.dependency.SDependencyException;\nimport org.bonitasoft.engine.dependency.SDependencyNotFoundException;\nimport org.bonitasoft.engine.dependency.model.ScopeType;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.RetrieveException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.search.SearchCommands;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor;\nimport org.bonitasoft.engine.service.ModelConvertor;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.ServiceAccessorSingleton;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\n\n/**\n * @author Zhang Bole\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @author Emmanuel Duchastenier\n * @author Laurent Vaills\n */\npublic class CommandAPIImpl implements CommandAPI {\n\n    protected static ServiceAccessor getServiceAccessor() {\n        return ServiceAccessorSingleton.getInstance();\n    }\n\n    @Override\n    public void addDependency(final String name, final byte[] jar) throws CreationException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final DependencyService dependencyService = serviceAccessor.getDependencyService();\n        final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService();\n        try {\n            dependencyService.createMappedDependency(name, jar, name + \".jar\", -1L, ScopeType.TENANT);\n            classLoaderService\n                    .refreshClassLoaderAfterUpdate(ClassLoaderIdentifier.TENANT);\n        } catch (final SDependencyAlreadyExistsException e) {\n            throw new AlreadyExistsException(e);\n        } catch (final SDependencyException | SClassLoaderException sbe) {\n            throw new CreationException(sbe);\n        }\n    }\n\n    @Override\n    public void removeDependency(final String name) throws DependencyNotFoundException, DeletionException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final DependencyService dependencyService = serviceAccessor.getDependencyService();\n        final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService();\n        try {\n            dependencyService.deleteDependency(name);\n            classLoaderService\n                    .refreshClassLoaderAfterUpdate(ClassLoaderIdentifier.TENANT);\n        } catch (final SDependencyNotFoundException e) {\n            throw new DependencyNotFoundException(e);\n        } catch (final SBonitaException e) {\n            throw new DeletionException(e);\n        }\n    }\n\n    @Override\n    public CommandDescriptor register(final String name, final String description, final String implementation)\n            throws CreationException {\n        CommandDescriptor existingCommandDescriptor = null;\n        try {\n            existingCommandDescriptor = getCommand(name);\n        } catch (final CommandNotFoundException notFoundE) {\n            // Nothing to do : no command with that name exists.\n        }\n        if (existingCommandDescriptor != null) {\n            throw new AlreadyExistsException(\"A command with name \\\"\" + name + \"\\\" already exists\");\n        }\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final CommandService commandService = serviceAccessor.getCommandService();\n        final SCommand sCommand = SCommand.builder()\n                .name(name)\n                .description(description)\n                .implementation(implementation).isSystem(false).build();\n        try {\n            commandService.create(sCommand);\n            return ModelConvertor.toCommandDescriptor(sCommand);\n        } catch (final SBonitaException sbe) {\n            throw new CreationException(sbe);\n        }\n    }\n\n    private RuntimeCommand fetchRuntimeCommand(final SCommandFetcher commandFetcher,\n            final boolean transactionManagedManually)\n            throws SCommandNotFoundException, SCommandParameterizationException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        try {\n            final SCommand sCommand;\n            if (transactionManagedManually) {\n                sCommand = commandFetcher.fetchInTransaction(serviceAccessor.getUserTransactionService(),\n                        serviceAccessor.getCommandService());\n            } else {\n                sCommand = commandFetcher.fetch(serviceAccessor.getCommandService());\n            }\n\n            final String runtimeCommandClassName = sCommand.getImplementation();\n            final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n            return (RuntimeCommand) contextClassLoader.loadClass(runtimeCommandClassName).newInstance();\n        } catch (final ClassNotFoundException | InstantiationException | IllegalAccessException e) {\n            throw new SCommandParameterizationException(e);\n        }\n    }\n\n    @Override\n    public Serializable execute(final String commandName, final Map<String, Serializable> parameters)\n            throws CommandNotFoundException,\n            CommandParameterizationException, CommandExecutionException {\n        return execute(new SCommandFetcherByName(commandName), parameters);\n    }\n\n    @Override\n    public Serializable execute(final long commandId, final Map<String, Serializable> parameters)\n            throws CommandNotFoundException,\n            CommandParameterizationException, CommandExecutionException {\n        return execute(new SCommandFetcherById(commandId), parameters);\n    }\n\n    private Serializable execute(final SCommandFetcher commandFetcher, final Map<String, Serializable> parameters)\n            throws CommandNotFoundException,\n            CommandParameterizationException, CommandExecutionException {\n        return executeCommand(commandFetcher, parameters, false);\n    }\n\n    @Override\n    @CustomTransactions\n    public Serializable executeWithUserTransactions(final String commandName,\n            final Map<String, Serializable> parameters) throws CommandNotFoundException,\n            CommandParameterizationException, CommandExecutionException {\n        return executeWithUserTransactions(new SCommandFetcherByName(commandName), parameters);\n    }\n\n    @Override\n    @CustomTransactions\n    public Serializable executeWithUserTransactions(final long commandId, final Map<String, Serializable> parameters)\n            throws CommandNotFoundException,\n            CommandParameterizationException, CommandExecutionException {\n        return executeWithUserTransactions(new SCommandFetcherById(commandId), parameters);\n    }\n\n    private Serializable executeWithUserTransactions(final SCommandFetcher commandFetcher,\n            final Map<String, Serializable> parameters)\n            throws CommandNotFoundException, CommandParameterizationException, CommandExecutionException {\n        return executeCommand(commandFetcher, parameters, true);\n    }\n\n    private Serializable executeCommand(final SCommandFetcher commandFetcher,\n            final Map<String, Serializable> parameters,\n            final boolean transactionManagedManually)\n            throws CommandNotFoundException, CommandParameterizationException, CommandExecutionException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        try {\n            final RuntimeCommand runtimeCommand = fetchRuntimeCommand(commandFetcher, transactionManagedManually);\n            return runtimeCommand.execute(parameters, serviceAccessor);\n        } catch (final SCommandExecutionException scee) {\n            throw new CommandExecutionException(scee);\n        } catch (final SCommandParameterizationException scpe) {\n            throw new CommandParameterizationException(scpe);\n        } catch (final SCommandNotFoundException e) {\n            throw new CommandNotFoundException(e);\n        }\n    }\n\n    @Override\n    public void unregister(final long commandId) throws CommandNotFoundException, DeletionException {\n        final CommandService commandService = getServiceAccessor().getCommandService();\n        final DeleteSCommand deleteCommand = new DeleteSCommand(commandService, commandId);\n        unregister(deleteCommand);\n    }\n\n    @Override\n    public void unregister(final String name) throws CommandNotFoundException, DeletionException {\n        if (name == null) {\n            // FIXME: throw IllegalArgumentException instead, and make bonita interceptor catch all exceptions and wrap it into BonitaRuntimeException:\n            throw new DeletionException(\"Command name can not be null!\");\n        }\n        final CommandService commandService = getServiceAccessor().getCommandService();\n        final DeleteSCommand deleteCommand = new DeleteSCommand(commandService, name);\n        unregister(deleteCommand);\n    }\n\n    private void unregister(final DeleteSCommand deleteCommand) throws CommandNotFoundException, DeletionException {\n        try {\n            deleteCommand.execute();\n        } catch (final SCommandNotFoundException scnfe) {\n            throw new CommandNotFoundException(scnfe);\n        } catch (final SBonitaException sbe) {\n            throw new DeletionException(sbe);\n        }\n    }\n\n    @Override\n    public void unregisterAll() throws DeletionException {\n        final CommandService commandService = getServiceAccessor().getCommandService();\n        try {\n            commandService.deleteAll();\n        } catch (final SCommandDeletionException sde) {\n            throw new DeletionException(sde);\n        }\n    }\n\n    @Override\n    public CommandDescriptor get(final long commandId) throws CommandNotFoundException {\n        return getCommand(new SCommandFetcherById(commandId));\n    }\n\n    @Override\n    public CommandDescriptor getCommand(final String commandName) throws CommandNotFoundException {\n        return getCommand(new SCommandFetcherByName(commandName));\n    }\n\n    private CommandDescriptor getCommand(final SCommandFetcher commandFetcher) throws CommandNotFoundException {\n        final CommandService commandService = getServiceAccessor().getCommandService();\n        try {\n            final SCommand sCommand = commandFetcher.fetch(commandService);\n            return ModelConvertor.toCommandDescriptor(sCommand);\n        } catch (final SBonitaException e) {\n            throw new CommandNotFoundException(e);\n        }\n    }\n\n    @Override\n    public List<CommandDescriptor> getAllCommands(final int startIndex, final int maxResults,\n            final CommandCriterion sort) {\n        SCommandCriterion sCommandCriterion;\n        if (CommandCriterion.NAME_ASC.equals(sort)) {\n            sCommandCriterion = SCommandCriterion.NAME_ASC;\n        } else {\n            sCommandCriterion = SCommandCriterion.NAME_DESC;\n        }\n\n        final CommandService commandService = getServiceAccessor().getCommandService();\n        try {\n            final List<SCommand> commands = commandService.getAllCommands(startIndex, maxResults, sCommandCriterion);\n            return ModelConvertor.toCommandDescriptors(commands);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public void update(final long commandId, final CommandUpdater updateDescriptor) throws UpdateException {\n        update(new SCommandFetcherById(commandId), updateDescriptor);\n    }\n\n    @Override\n    public void update(final String commandName, final CommandUpdater updateDescriptor) throws UpdateException {\n        update(new SCommandFetcherByName(commandName), updateDescriptor);\n    }\n\n    private void update(final SCommandFetcher commandFetcher, final CommandUpdater updateDescriptor)\n            throws UpdateException {\n        if (updateDescriptor == null || updateDescriptor.getFields().isEmpty()) {\n            throw new UpdateException(\"The update descriptor does not contain field updates\");\n        }\n\n        final SCommandUpdateBuilderFactory fact = BuilderFactory.get(SCommandUpdateBuilderFactory.class);\n        final SCommandUpdateBuilder commandUpdateBuilder = fact.createNewInstance();\n        final CommandService commandService = getServiceAccessor().getCommandService();\n        try {\n            final EntityUpdateDescriptor changeDescriptor = getCommandUpdateDescriptor(updateDescriptor,\n                    commandUpdateBuilder);\n            final SCommand sCommand = commandFetcher.fetch(commandService);\n            commandService.update(sCommand, changeDescriptor);\n        } catch (final SCommandNotFoundException | SCommandUpdateException e) {\n            throw new UpdateException(e);\n        }\n    }\n\n    private EntityUpdateDescriptor getCommandUpdateDescriptor(final CommandUpdater updateDescriptor,\n            final SCommandUpdateBuilder commandUpdateBuilder) {\n        final Map<CommandField, Serializable> fields = updateDescriptor.getFields();\n        for (final Entry<CommandField, Serializable> field : fields.entrySet()) {\n            final String value = (String) field.getValue();\n            switch (field.getKey()) {\n                case NAME:\n                    commandUpdateBuilder.updateName(value);\n                    break;\n                case DESCRIPTION:\n                    commandUpdateBuilder.updateDescription(value);\n                    break;\n                default:\n                    throw new IllegalStateException();\n            }\n        }\n        return commandUpdateBuilder.done();\n    }\n\n    @Override\n    public List<CommandDescriptor> getUserCommands(final int startIndex, final int maxResults,\n            final CommandCriterion sort) {\n        final CommandService commandService = getServiceAccessor().getCommandService();\n        try {\n            final GetCommands getCommands = new GetCommands(commandService, startIndex, maxResults, sort);\n            getCommands.execute();\n            return ModelConvertor.toCommandDescriptors(getCommands.getResult());\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public SearchResult<CommandDescriptor> searchCommands(final SearchOptions searchOptions) throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final CommandService commandService = serviceAccessor.getCommandService();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final SearchCommands searchCommands = new SearchCommands(commandService,\n                searchEntitiesDescriptor.getSearchCommandDescriptor(), searchOptions);\n        try {\n            searchCommands.execute();\n            return searchCommands.getResult();\n        } catch (final SBonitaException sbe) {\n            throw new SearchException(sbe);\n        }\n    }\n\n    // Utility classes to factorize how we fetch a RuntimeCommand\n    private abstract static class SCommandFetcher {\n\n        abstract SCommand fetch(final CommandService commandService) throws SCommandNotFoundException;\n\n        SCommand fetchInTransaction(final UserTransactionService userTransactionService,\n                final CommandService commandService) throws SCommandNotFoundException {\n            try {\n                return userTransactionService.executeInTransaction(() -> fetch(commandService));\n            } catch (final Exception e) {\n                throw new SCommandNotFoundException(e);\n            }\n        }\n    }\n\n    private static class SCommandFetcherByName extends SCommandFetcher {\n\n        private final String commandName;\n\n        public SCommandFetcherByName(final String commandName) {\n            this.commandName = commandName;\n        }\n\n        @Override\n        SCommand fetch(final CommandService commandService) throws SCommandNotFoundException {\n            return commandService.get(commandName);\n        }\n    }\n\n    private static class SCommandFetcherById extends SCommandFetcher {\n\n        private final long commandId;\n\n        public SCommandFetcherById(final long commandId) {\n            this.commandId = commandId;\n        }\n\n        @Override\n        SCommand fetch(final CommandService commandService) throws SCommandNotFoundException {\n            return commandService.get(commandId);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/CustomUserInfoAPIDelegate.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.identity.CustomUserInfo;\nimport org.bonitasoft.engine.identity.CustomUserInfoValue;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.SIdentityException;\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoValue;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Vincent Elcrin\n */\npublic class CustomUserInfoAPIDelegate {\n\n    private final IdentityService service;\n\n    public CustomUserInfoAPIDelegate(final IdentityService service) {\n        this.service = service;\n    }\n\n    public List<CustomUserInfo> list(final long userId, final int startIndex, final int maxResult)\n            throws SIdentityException, SBonitaReadException {\n        List<SCustomUserInfoDefinition> definitions = service.getCustomUserInfoDefinitions(startIndex, maxResult);\n        if (definitions.size() == 0) {\n            return Collections.emptyList();\n        }\n        Map<Long, CustomUserInfoValue> values = transform(searchCorrespondingValues(userId, definitions));\n        List<CustomUserInfo> info = new ArrayList<CustomUserInfo>();\n        for (SCustomUserInfoDefinition definition : definitions) {\n            info.add(new CustomUserInfo(userId, ModelConvertor.convert(definition), values.get(definition.getId())));\n        }\n        return info;\n    }\n\n    private Map<Long, CustomUserInfoValue> transform(final List<SCustomUserInfoValue> values) {\n        Map<Long, CustomUserInfoValue> map = new HashMap<Long, CustomUserInfoValue>();\n        for (SCustomUserInfoValue value : values) {\n            map.put(value.getDefinitionId(), ModelConvertor.convert(value));\n        }\n        return map;\n    }\n\n    private List<SCustomUserInfoValue> searchCorrespondingValues(final long userId,\n            final List<SCustomUserInfoDefinition> definitions)\n            throws SBonitaReadException {\n        return service.getCustomUserInfoValueOfUserAndDefinitions(userId, getIds(definitions));\n    }\n\n    private List<Long> getIds(final List<SCustomUserInfoDefinition> definitions) {\n        List<Long> ids = new ArrayList<Long>(definitions.size());\n        for (SCustomUserInfoDefinition definition : definitions) {\n            ids.add(definition.getId());\n        }\n        return ids;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/CustomUserInfoDefinitionAPIDelegate.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.RetrieveException;\nimport org.bonitasoft.engine.identity.CustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.CustomUserInfoDefinitionCreator;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.SCustomUserInfoDefinitionAlreadyExistsException;\nimport org.bonitasoft.engine.identity.SIdentityException;\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Vincent Elcrin\n * @author Elias Ricken de Medeiros\n */\npublic class CustomUserInfoDefinitionAPIDelegate {\n\n    private static final int MAX_NAME_LENGHT = 75;\n\n    private final IdentityService service;\n\n    public CustomUserInfoDefinitionAPIDelegate(final IdentityService service) {\n        this.service = service;\n    }\n\n    public CustomUserInfoDefinition create(final CustomUserInfoDefinitionCreator creator)\n            throws CreationException {\n        checkParameter(creator);\n\n        final SCustomUserInfoDefinition.SCustomUserInfoDefinitionBuilder builder = SCustomUserInfoDefinition.builder();\n        builder.name(creator.getName());\n        builder.description(creator.getDescription());\n        try {\n            return ModelConvertor.convert(service.createCustomUserInfoDefinition(builder.build()));\n        } catch (SCustomUserInfoDefinitionAlreadyExistsException e) {\n            throw new AlreadyExistsException(e.getMessage());\n        } catch (SIdentityException e) {\n            throw new CreationException(e);\n        }\n    }\n\n    private void checkParameter(final CustomUserInfoDefinitionCreator creator) throws CreationException {\n        if (creator == null) {\n            throw new CreationException(\"Can not create null custom user details.\");\n        }\n        if (creator.getName() == null || creator.getName().trim().isEmpty()) {\n            throw new CreationException(\"The definition name cannot be null or empty.\");\n        }\n        if (creator.getName().length() > MAX_NAME_LENGHT) {\n            throw new CreationException(\"The definition name cannot be longer then 75 characters.\");\n        }\n\n    }\n\n    public void delete(final long id) throws DeletionException {\n        try {\n            service.deleteCustomUserInfoDefinition(id);\n        } catch (SIdentityException e) {\n            throw new DeletionException(e);\n        }\n    }\n\n    public List<CustomUserInfoDefinition> list(final int startIndex, final int maxResult) {\n        try {\n            List<CustomUserInfoDefinition> definitions = new ArrayList<CustomUserInfoDefinition>();\n            for (SCustomUserInfoDefinition sDefinition : service.getCustomUserInfoDefinitions(startIndex, maxResult)) {\n                definitions.add(ModelConvertor.convert(sDefinition));\n            }\n            return definitions;\n        } catch (SIdentityException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    public long count() {\n        try {\n            return service.getNumberOfCustomUserInfoDefinition();\n        } catch (SIdentityException e) {\n            throw new RetrieveException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/DocumentAPIImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.DocumentAPI;\nimport org.bonitasoft.engine.api.impl.transaction.document.GetDocumentByNameAtProcessInstantiation;\nimport org.bonitasoft.engine.bpm.document.*;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectAlreadyExistsException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.core.document.api.DocumentService;\nimport org.bonitasoft.engine.core.document.api.impl.DocumentHelper;\nimport org.bonitasoft.engine.core.document.model.AbstractSMappedDocument;\nimport org.bonitasoft.engine.core.document.model.SDocument;\nimport org.bonitasoft.engine.core.document.model.SMappedDocument;\nimport org.bonitasoft.engine.core.document.model.builder.SDocumentBuilderFactory;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance;\nimport org.bonitasoft.engine.exception.*;\nimport org.bonitasoft.engine.persistence.OrderAndField;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor;\nimport org.bonitasoft.engine.search.document.SearchArchivedDocuments;\nimport org.bonitasoft.engine.search.document.SearchArchivedDocumentsSupervisedBy;\nimport org.bonitasoft.engine.search.document.SearchDocuments;\nimport org.bonitasoft.engine.search.document.SearchDocumentsSupervisedBy;\nimport org.bonitasoft.engine.service.ModelConvertor;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.ServiceAccessorSingleton;\n\n/**\n * @author Baptiste Mesta\n */\npublic class DocumentAPIImpl implements DocumentAPI {\n\n    public DocumentAPIImpl() {\n    }\n\n    @Override\n    public Document attachDocument(final long processInstanceId, final String documentName, final String fileName,\n            final String mimeType, final String url)\n            throws DocumentAttachmentException {\n        final DocumentValue documentValue = new DocumentValue(url);\n        documentValue.setFileName(fileName);\n        documentValue.setMimeType(mimeType);\n        try {\n            return addDocument(processInstanceId, documentName, null, documentValue);\n        } catch (final BonitaException e) {\n            throw new DocumentAttachmentException(e);\n        }\n    }\n\n    /*\n     * If the target document is a list of document then we append it to the list\n     * If the target document is a list of document and the index is set on the document value then we insert the\n     * element in the list at the specified index\n     * If the target single document or is non existent in the definition we create it\n     * If the target single document and is already existent an exception is thrown\n     */\n    @Override\n    public Document addDocument(final long processInstanceId, final String documentName, final String description,\n            final DocumentValue documentValue)\n            throws DocumentAttachmentException, AlreadyExistsException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final DocumentService documentService = serviceAccessor.getDocumentService();\n\n        final DocumentHelper documentHelper = new DocumentHelper(documentService,\n                serviceAccessor.getProcessDefinitionService(),\n                serviceAccessor.getProcessInstanceService());\n        final SDocument sDocument = buildSDocument(documentValue);\n        int index = documentValue.getIndex();\n        try {\n            if (documentHelper.isListDefinedInDefinition(documentName, processInstanceId)) {\n                final List<AbstractSMappedDocument> allDocumentOfTheList = documentHelper\n                        .getAllDocumentOfTheList(processInstanceId, documentName);\n                if (index == -1) {\n                    index = allDocumentOfTheList.size();\n                } else {\n                    if (index > allDocumentOfTheList.size()) {\n                        throw new DocumentAttachmentException(\n                                \"Can't attach a document on the list \" + documentName + \" on process instance \"\n                                        + processInstanceId + \" the index is out of range, list size is \"\n                                        + allDocumentOfTheList.size());\n                    }\n                    for (int i = index; i < allDocumentOfTheList.size(); i++) {\n                        documentService.updateDocumentIndex(allDocumentOfTheList.get(i), i + 1);\n                    }\n                }\n            } else {\n                if (index >= 0) {\n                    throw new DocumentAttachmentException(\n                            \"Unable to add a document with an index if it is a single document\");\n                }\n            }\n            final SMappedDocument mappedDocument = documentService.attachDocumentToProcessInstance(sDocument,\n                    processInstanceId, documentName, description,\n                    index);\n            return ModelConvertor.toDocument(mappedDocument, documentService);\n\n        } catch (final SObjectAlreadyExistsException e) {\n            throw new AlreadyExistsException(e.getMessage());\n        } catch (final SBonitaException e) {\n            throw new DocumentAttachmentException(e);\n        }\n\n    }\n\n    @Override\n    public Document updateDocument(final long documentId, final DocumentValue documentValue)\n            throws DocumentAttachmentException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final DocumentService documentService = serviceAccessor.getDocumentService();\n        try {\n            final SMappedDocument document = documentService.updateDocument(documentId, buildSDocument(documentValue));\n            return ModelConvertor.toDocument(document, documentService);\n        } catch (final SBonitaException e) {\n            throw new DocumentAttachmentException(e);\n        }\n    }\n\n    private SDocument buildSDocument(final DocumentValue documentValue) {\n        if (documentValue.hasContent()) {\n            return buildProcessDocument(documentValue.getFileName(), documentValue.getMimeType(), getUserId(),\n                    documentValue.getContent());\n        }\n        return buildExternalProcessDocumentReference(documentValue.getFileName(), documentValue.getMimeType(),\n                getUserId(), documentValue.getUrl());\n    }\n\n    private long getUserId() {\n        return APIUtils.getUserId();\n    }\n\n    private SDocument buildExternalProcessDocumentReference(final String fileName, final String mimeType,\n            final long authorId, final String url) {\n        return new SDocumentBuilderFactory()\n                .createNewExternalProcessDocumentReference(fileName, mimeType, authorId, url).done();\n    }\n\n    private SDocument buildProcessDocument(final String fileName, final String mimeType, final long authorId,\n            final byte[] content) {\n        return new SDocumentBuilderFactory().createNewProcessDocument(fileName, mimeType, authorId, content).done();\n    }\n\n    @Override\n    public Document attachDocument(final long processInstanceId, final String documentName, final String fileName,\n            final String mimeType,\n            final byte[] documentContent) throws DocumentAttachmentException {\n        final DocumentValue documentValue = new DocumentValue(documentContent, mimeType, fileName);\n        try {\n            return addDocument(processInstanceId, documentName, null, documentValue);\n        } catch (final BonitaException e) {\n            throw new DocumentAttachmentException(e);\n        }\n    }\n\n    ServiceAccessor getServiceAccessor() {\n        return ServiceAccessorSingleton.getInstance();\n    }\n\n    @Override\n    public Document attachNewDocumentVersion(final long processInstanceId, final String documentName,\n            final String fileName, final String mimeType,\n            final String url) throws DocumentAttachmentException {\n        final DocumentService documentService = getServiceAccessor().getDocumentService();\n        try {\n            return ModelConvertor.toDocument(\n                    documentService.updateDocument(documentService.getMappedDocument(processInstanceId, documentName),\n                            buildExternalProcessDocumentReference(fileName, mimeType, getUserId(), url)),\n                    documentService);\n        } catch (final Exception e) {\n            throw new DocumentAttachmentException(e);\n        }\n    }\n\n    @Override\n    public Document attachNewDocumentVersion(final long processInstanceId, final String documentName,\n            final String contentFileName,\n            final String contentMimeType, final byte[] documentContent) throws DocumentAttachmentException {\n        final DocumentService documentService = getServiceAccessor().getDocumentService();\n        try {\n            return ModelConvertor.toDocument(\n                    documentService.updateDocument(documentService.getMappedDocument(processInstanceId, documentName),\n                            buildProcessDocument(contentFileName, contentMimeType, getUserId(), documentContent)),\n                    documentService);\n        } catch (final Exception e) {\n            throw new DocumentAttachmentException(e);\n        }\n    }\n\n    @Override\n    public Document getDocument(final long documentId) throws DocumentNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final DocumentService documentService = serviceAccessor.getDocumentService();\n        try {\n            return ModelConvertor.toDocument(documentService.getMappedDocument(documentId), documentService);\n        } catch (final SObjectNotFoundException | SBonitaReadException e) {\n            throw new DocumentNotFoundException(e);\n        }\n    }\n\n    @Override\n    public List<Document> getLastVersionOfDocuments(final long processInstanceId, final int pageIndex,\n            final int numberPerPage,\n            final DocumentCriterion pagingCriterion) throws DocumentException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final DocumentService documentService = serviceAccessor.getDocumentService();\n\n        final OrderAndField orderAndField = OrderAndFields.getOrderAndFieldForDocument(pagingCriterion);\n        try {\n            final List<SMappedDocument> mappedDocuments = documentService.getDocumentsOfProcessInstance(\n                    processInstanceId, pageIndex, numberPerPage,\n                    orderAndField.getField(), orderAndField.getOrder());\n            if (mappedDocuments != null && !mappedDocuments.isEmpty()) {\n                final List<Document> result = new ArrayList<>(mappedDocuments.size());\n                for (final SMappedDocument mappedDocument : mappedDocuments) {\n                    result.add(ModelConvertor.toDocument(mappedDocument, documentService));\n                }\n                return result;\n            }\n            return Collections.emptyList();\n        } catch (final SBonitaReadException e) {\n            throw new DocumentException(e);\n        }\n    }\n\n    @Override\n    public byte[] getDocumentContent(final String documentStorageId) throws DocumentNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final DocumentService documentService = serviceAccessor.getDocumentService();\n        try {\n            return documentService.getDocumentContent(documentStorageId);\n        } catch (final SObjectNotFoundException sbe) {\n            throw new DocumentNotFoundException(sbe);\n        }\n    }\n\n    @Override\n    public Document getLastDocument(final long processInstanceId, final String documentName)\n            throws DocumentNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final DocumentService documentService = serviceAccessor.getDocumentService();\n        try {\n            return ModelConvertor.toDocument(documentService.getMappedDocument(processInstanceId, documentName),\n                    documentService);\n        } catch (final SObjectNotFoundException | SBonitaReadException sbe) {\n            throw new DocumentNotFoundException(sbe);\n        }\n    }\n\n    @Override\n    public long getNumberOfDocuments(final long processInstanceId) throws DocumentException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final DocumentService documentService = serviceAccessor.getDocumentService();\n        try {\n            return documentService.getNumberOfDocumentsOfProcessInstance(processInstanceId);\n        } catch (final SBonitaReadException e) {\n            throw new DocumentException(e);\n        }\n    }\n\n    @Override\n    public Document getDocumentAtProcessInstantiation(final long processInstanceId, final String documentName)\n            throws DocumentNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final DocumentService documentService = serviceAccessor.getDocumentService();\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n\n        try {\n            final GetDocumentByNameAtProcessInstantiation transactionContent = new GetDocumentByNameAtProcessInstantiation(\n                    documentService,\n                    processInstanceService, serviceAccessor.getProcessDefinitionService(), searchEntitiesDescriptor,\n                    processInstanceId, documentName);\n            transactionContent.execute();\n            final AbstractSMappedDocument attachment = transactionContent.getResult();\n            return ModelConvertor.toDocument(attachment, documentService);\n        } catch (final SBonitaException sbe) {\n            throw new DocumentNotFoundException(sbe);\n        }\n    }\n\n    @Override\n    public Document getDocumentAtActivityInstanceCompletion(final long activityInstanceId, final String documentName)\n            throws DocumentNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final DocumentService documentService = serviceAccessor.getDocumentService();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        try {\n            final SAActivityInstance instance = activityInstanceService\n                    .getMostRecentArchivedActivityInstance(activityInstanceId);\n            final AbstractSMappedDocument document = documentService.getMappedDocument(instance.getRootContainerId(),\n                    documentName, instance.getArchiveDate());\n            return ModelConvertor.toDocument(document, documentService);\n        } catch (final SBonitaException sbe) {\n            throw new DocumentNotFoundException(sbe);\n        }\n    }\n\n    @Override\n    public SearchResult<Document> searchDocuments(final SearchOptions searchOptions) throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final DocumentService documentService = serviceAccessor.getDocumentService();\n\n        final SearchDocuments searchDocuments = new SearchDocuments(documentService,\n                searchEntitiesDescriptor.getSearchDocumentDescriptor(),\n                searchOptions);\n        try {\n            searchDocuments.execute();\n        } catch (final SBonitaException e) {\n            throw new SearchException(e);\n        }\n        return searchDocuments.getResult();\n    }\n\n    @Override\n    public SearchResult<Document> searchDocumentsSupervisedBy(final long userId, final SearchOptions searchOptions)\n            throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final DocumentService documentService = serviceAccessor.getDocumentService();\n\n        final SearchDocumentsSupervisedBy searchDocuments = new SearchDocumentsSupervisedBy(documentService,\n                searchEntitiesDescriptor.getSearchDocumentDescriptor(), searchOptions, userId);\n        try {\n            searchDocuments.execute();\n        } catch (final SBonitaException e) {\n            throw new SearchException(e);\n        }\n        return searchDocuments.getResult();\n    }\n\n    @Override\n    public SearchResult<ArchivedDocument> searchArchivedDocuments(final SearchOptions searchOptions)\n            throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final DocumentService documentService = serviceAccessor.getDocumentService();\n        final SearchArchivedDocuments searchDocuments = new SearchArchivedDocuments(documentService,\n                searchEntitiesDescriptor.getSearchArchivedDocumentDescriptor(), searchOptions);\n        try {\n            searchDocuments.execute();\n        } catch (final SBonitaException e) {\n            throw new SearchException(e);\n        }\n        return searchDocuments.getResult();\n    }\n\n    @Override\n    public SearchResult<ArchivedDocument> searchArchivedDocumentsSupervisedBy(final long userId,\n            final SearchOptions searchOptions) throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final DocumentService documentService = serviceAccessor.getDocumentService();\n        final SearchArchivedDocumentsSupervisedBy searchDocuments = new SearchArchivedDocumentsSupervisedBy(userId,\n                documentService,\n                searchEntitiesDescriptor.getSearchArchivedDocumentDescriptor(), searchOptions);\n        try {\n            searchDocuments.execute();\n        } catch (final SBonitaException e) {\n            throw new SearchException(e);\n        }\n        return searchDocuments.getResult();\n    }\n\n    @Override\n    public ArchivedDocument getArchivedVersionOfProcessDocument(final long sourceObjectId)\n            throws ArchivedDocumentNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final DocumentService documentService = serviceAccessor.getDocumentService();\n\n        try {\n            return ModelConvertor.toArchivedDocument(\n                    documentService.getArchivedVersionOfProcessDocument(sourceObjectId), documentService);\n        } catch (final SObjectNotFoundException e) {\n            throw new ArchivedDocumentNotFoundException(e);\n        }\n    }\n\n    @Override\n    public ArchivedDocument getArchivedProcessDocument(final long archivedProcessDocumentId)\n            throws ArchivedDocumentNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final DocumentService documentService = serviceAccessor.getDocumentService();\n        try {\n            return ModelConvertor.toArchivedDocument(documentService.getArchivedDocument(archivedProcessDocumentId),\n                    documentService);\n        } catch (final SObjectNotFoundException e) {\n            throw new ArchivedDocumentNotFoundException(e);\n        }\n    }\n\n    @Override\n    public Document removeDocument(final long documentId) throws DocumentNotFoundException, DeletionException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final DocumentService documentService = serviceAccessor.getDocumentService();\n        final DocumentHelper documentHelper = new DocumentHelper(documentService,\n                serviceAccessor.getProcessDefinitionService(),\n                serviceAccessor.getProcessInstanceService());\n        try {\n            final SMappedDocument document = documentService.getMappedDocument(documentId);\n            final int index = document.getIndex();\n            if (index != -1) {\n                //document is in list\n                final List<AbstractSMappedDocument> allDocumentOfTheList = documentHelper\n                        .getAllDocumentOfTheList(document.getProcessInstanceId(), document.getName());\n\n                for (int i = index + 1; i < allDocumentOfTheList.size(); i++) {\n                    documentService.updateDocumentIndex(allDocumentOfTheList.get(i), i - 1);\n                }\n            }\n            // archive and delete mapping on the process but keep the content of the document itself\n            documentService.removeCurrentVersion(document);\n            return ModelConvertor.toDocument(document, documentService);\n        } catch (final SObjectNotFoundException e) {\n            throw new DocumentNotFoundException(\n                    \"Unable to delete the document \" + documentId + \" because it does not exists\", e);\n        } catch (final SBonitaException e) {\n            throw new DeletionException(\"Unable to delete the document \" + documentId, e);\n        }\n    }\n\n    @Override\n    public List<Document> getDocumentList(final long processInstanceId, final String name, final int fromIndex,\n            final int numberOfResult)\n            throws DocumentNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final DocumentService documentService = serviceAccessor.getDocumentService();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n        final DocumentHelper documentHelper = new DocumentHelper(documentService, processDefinitionService,\n                processInstanceService);\n        try {\n            final List<SMappedDocument> documentList = documentService.getDocumentList(name, processInstanceId,\n                    fromIndex, numberOfResult);\n            if (documentList.isEmpty()\n                    && !documentHelper.isListDefinedInDefinition(name, processInstanceId)) {\n                throw new DocumentNotFoundException(\"doc not found\");\n            }\n            return ModelConvertor.toDocuments(new ArrayList<>(documentList), documentService);\n        } catch (final org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException e) {\n            throw new DocumentNotFoundException(e);\n        } catch (final SBonitaReadException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public void setDocumentList(final long processInstanceId, final String name,\n            final List<DocumentValue> documentsValues) throws DocumentException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final DocumentHelper documentHelper = new DocumentHelper(serviceAccessor.getDocumentService(),\n                serviceAccessor.getProcessDefinitionService(),\n                serviceAccessor.getProcessInstanceService());\n        try {\n            documentHelper.setDocumentList(documentsValues, name, processInstanceId, getUserId());\n        } catch (final SBonitaException e) {\n            throw new DocumentException(\"Unable to set the list \" + name + \" on process instance \" + processInstanceId,\n                    e);\n        }\n    }\n\n    @Override\n    public void deleteContentOfArchivedDocument(final long archivedDocumentId)\n            throws DocumentException, DocumentNotFoundException {\n        final ServiceAccessor serviceAccessor = ServiceAccessorSingleton.getInstance();\n        final DocumentService documentService = serviceAccessor.getDocumentService();\n        try {\n            documentService.deleteContentOfArchivedDocument(archivedDocumentId);\n        } catch (final SObjectNotFoundException e) {\n            throw new DocumentNotFoundException(\"The document with id \" + archivedDocumentId + \" could not be found\",\n                    e);\n        } catch (final SBonitaException e) {\n            throw new DocumentException(\"Unable to delete content of all version of the document \" + archivedDocumentId,\n                    e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/IdentityAPIImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport java.io.Serializable;\nimport java.util.*;\nimport java.util.Map.Entry;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.actor.mapping.model.SActor;\nimport org.bonitasoft.engine.api.IdentityAPI;\nimport org.bonitasoft.engine.api.impl.organization.OrganizationAPIDelegate;\nimport org.bonitasoft.engine.api.impl.transaction.actor.GetActor;\nimport org.bonitasoft.engine.api.impl.transaction.identity.*;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.NullCheckingUtil;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContent;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.exception.*;\nimport org.bonitasoft.engine.identity.*;\nimport org.bonitasoft.engine.identity.ContactDataUpdater.ContactDataField;\nimport org.bonitasoft.engine.identity.GroupUpdater.GroupField;\nimport org.bonitasoft.engine.identity.RoleUpdater.RoleField;\nimport org.bonitasoft.engine.identity.UserUpdater.UserField;\nimport org.bonitasoft.engine.identity.impl.UserWithContactDataImpl;\nimport org.bonitasoft.engine.identity.model.*;\nimport org.bonitasoft.engine.identity.model.builder.*;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.profile.ProfileService;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor;\nimport org.bonitasoft.engine.search.identity.SearchGroups;\nimport org.bonitasoft.engine.search.identity.SearchRoles;\nimport org.bonitasoft.engine.search.identity.SearchUsers;\nimport org.bonitasoft.engine.service.ModelConvertor;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.impl.ServiceAccessorFactory;\n\n/**\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n * @author Feng Hui\n * @author Zhang Bole\n * @author Yanyan Liu\n * @author Lu Kai\n * @author Hongwen Zang\n * @author Celine Souchet\n */\n@AvailableInMaintenanceMode\n@Slf4j\npublic class IdentityAPIImpl implements IdentityAPI {\n\n    protected ServiceAccessor getServiceAccessor() {\n        try {\n            return ServiceAccessorFactory.getInstance().createServiceAccessor();\n        } catch (final Exception e) {\n            throw new BonitaRuntimeException(e);\n        }\n    }\n\n    protected OrganizationAPIDelegate getOrganizationAPIDelegate() {\n        return OrganizationAPIDelegate.getInstance();\n    }\n\n    @Override\n    public User createUser(final String userName, final String password) throws CreationException {\n        final UserCreator creator = new UserCreator(userName, password);\n        creator.setEnabled(true);\n        return createUser(creator);\n    }\n\n    @Override\n    public User createUser(final String userName, final String password, final String firstName, final String lastName)\n            throws CreationException {\n        final UserCreator creator = new UserCreator(userName, password);\n        creator.setFirstName(firstName).setLastName(lastName);\n        creator.setEnabled(true);\n        return createUser(creator);\n    }\n\n    @Override\n    public User createUser(final UserCreator creator) throws CreationException {\n        validateUserCreator(creator);\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        IdentityService identityService = serviceAccessor.getIdentityService();\n        if (creator.getFields().containsKey(UserCreator.UserField.ICON_NAME)\n                || creator.getFields().containsKey(UserCreator.UserField.ICON_PATH)) {\n            log.warn(\"setIconName and setIconPath are deprecated, use setIcon instead\");\n        }\n        final SUser sUser = ModelConvertor.constructSUser(creator);\n        final SContactInfo personalContactInfo = ModelConvertor.constructSUserContactInfo(creator, sUser.getId(), true);\n        final SContactInfo proContactInfo = ModelConvertor.constructSUserContactInfo(creator, sUser.getId(), false);\n        try {\n            SUser user = identityService.createUser(sUser, personalContactInfo, proContactInfo,\n                    (String) creator.getFields().get(UserCreator.UserField.ICON_FILENAME),\n                    (byte[]) creator.getFields().get(UserCreator.UserField.ICON_CONTENT));\n            return ModelConvertor.toUser(user);\n        } catch (final SBonitaException sbe) {\n            throw new CreationException(sbe);\n        }\n    }\n\n    private void validateUserCreator(UserCreator creator) throws CreationException {\n        if (creator == null) {\n            throw new CreationException(\"Can not create a null user.\");\n        }\n        final Map<UserCreator.UserField, Serializable> fields = creator.getFields();\n        final String userName = (String) fields.get(UserCreator.UserField.NAME);\n        if (userName == null || userName.trim().isEmpty()) {\n            throw new CreationException(\"The user name cannot be null or empty.\");\n        }\n        final String password = (String) fields.get(UserCreator.UserField.PASSWORD);\n        if (password == null || password.trim().isEmpty()) {\n            throw new CreationException(\"The password cannot be null or empty.\");\n        }\n        try {\n            getUserByUserName(userName);\n            throw new AlreadyExistsException(\"A user with name \\\"\" + userName + \"\\\" already exists\");\n        } catch (final UserNotFoundException ignored) {\n        }\n    }\n\n    @Override\n    public User updateUser(final long userId, final UserUpdater updater) throws UserNotFoundException, UpdateException {\n        if (updater == null || !updater.hasFields()) {\n            throw new UpdateException(\"The update descriptor does not contain field updates\");\n        }\n\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n\n        // User change\n        final EntityUpdateDescriptor userUpdateDescriptor = getUserUpdateDescriptor(updater);\n        // Personal data change\n        final EntityUpdateDescriptor personalDataUpdateDescriptor = getUserContactInfoUpdateDescriptor(\n                updater.getPersoContactUpdater());\n        // Professional data change\n        final EntityUpdateDescriptor professionalDataUpdateDescriptor = getUserContactInfoUpdateDescriptor(\n                updater.getProContactUpdater());\n        final EntityUpdateDescriptor iconUpdater = getIconUpdater(updater);\n        try {\n            SUser sUser = identityService.updateUser(userId, userUpdateDescriptor, personalDataUpdateDescriptor,\n                    professionalDataUpdateDescriptor, iconUpdater);\n            return ModelConvertor.toUser(sUser);\n        } catch (final SUserNotFoundException e) {\n            throw new UserNotFoundException(e);\n        } catch (final SBonitaException e) {\n            throw new UpdateException(e);\n        }\n    }\n\n    private EntityUpdateDescriptor getIconUpdater(UserUpdater updater) {\n        EntityUpdateDescriptor entityUpdateDescriptor = new EntityUpdateDescriptor();\n        if (updater.getFields().containsKey(UserField.ICON_CONTENT)) {\n            entityUpdateDescriptor.addField(\"filename\", updater.getFields().get(UserField.ICON_FILENAME));\n            entityUpdateDescriptor.addField(\"content\", updater.getFields().get(UserField.ICON_CONTENT));\n        }\n        return entityUpdateDescriptor;\n    }\n\n    private EntityUpdateDescriptor getIconUpdater(GroupUpdater updater) {\n        EntityUpdateDescriptor entityUpdateDescriptor = new EntityUpdateDescriptor();\n        if (updater.getFields().containsKey(GroupField.ICON_CONTENT)) {\n            entityUpdateDescriptor.addField(\"filename\", updater.getFields().get(GroupField.ICON_FILENAME));\n            entityUpdateDescriptor.addField(\"content\", updater.getFields().get(GroupField.ICON_CONTENT));\n        }\n        return entityUpdateDescriptor;\n    }\n\n    private EntityUpdateDescriptor getIconUpdater(RoleUpdater updater) {\n        EntityUpdateDescriptor entityUpdateDescriptor = new EntityUpdateDescriptor();\n        if (updater.getFields().containsKey(RoleUpdater.RoleField.ICON_CONTENT)) {\n            entityUpdateDescriptor.addField(\"filename\", updater.getFields().get(RoleUpdater.RoleField.ICON_FILENAME));\n            entityUpdateDescriptor.addField(\"content\", updater.getFields().get(RoleUpdater.RoleField.ICON_CONTENT));\n        }\n        return entityUpdateDescriptor;\n    }\n\n    private EntityUpdateDescriptor getUserUpdateDescriptor(final UserUpdater updateDescriptor) {\n        final SUserUpdateBuilder userUpdateBuilder = BuilderFactory.get(SUserUpdateBuilderFactory.class)\n                .createNewInstance();\n        if (updateDescriptor != null) {\n            final Map<UserField, Serializable> fields = updateDescriptor.getFields();\n            for (final Entry<UserField, Serializable> field : fields.entrySet()) {\n                switch (field.getKey()) {\n                    case USER_NAME:\n                        userUpdateBuilder.updateUserName((String) field.getValue());\n                        break;\n                    case PASSWORD:\n                        userUpdateBuilder.updatePassword((String) field.getValue());\n                        break;\n                    case FIRST_NAME:\n                        userUpdateBuilder.updateFirstName((String) field.getValue());\n                        break;\n                    case LAST_NAME:\n                        userUpdateBuilder.updateLastName((String) field.getValue());\n                        break;\n                    case MANAGER_ID:\n                        userUpdateBuilder.updateManagerUserId((Long) field.getValue());\n                        break;\n                    case ICON_NAME:\n                        log.warn(\"setIconName is deprecated, use setIcon instead\");\n                        break;\n                    case ICON_PATH:\n                        log.warn(\"setIconPath is deprecated, use setIcon instead\");\n                        break;\n                    case TITLE:\n                        userUpdateBuilder.updateTitle((String) field.getValue());\n                        break;\n                    case JOB_TITLE:\n                        userUpdateBuilder.updateJobTitle((String) field.getValue());\n                        break;\n                    case ENABLED:\n                        userUpdateBuilder.updateEnabled((Boolean) field.getValue());\n                        break;\n                    case ICON_FILENAME:\n                    case ICON_CONTENT:\n                        break;\n                    default:\n                        throw new IllegalStateException();\n                }\n            }\n            userUpdateBuilder.updateLastUpdate(System.currentTimeMillis());\n            return userUpdateBuilder.done();\n        }\n        return null;\n    }\n\n    private EntityUpdateDescriptor getUserContactInfoUpdateDescriptor(final ContactDataUpdater updater) {\n        final SContactInfoUpdateBuilder updateBuilder = BuilderFactory.get(SContactInfoUpdateBuilderFactory.class)\n                .createNewInstance();\n        if (updater != null) {\n            final Map<ContactDataField, Serializable> fields = updater.getFields();\n            for (final Entry<ContactDataField, Serializable> field : fields.entrySet()) {\n                switch (field.getKey()) {\n                    case EMAIL:\n                        updateBuilder.updateEmail((String) field.getValue());\n                        break;\n                    case PHONE:\n                        updateBuilder.updatePhoneNumber((String) field.getValue());\n                        break;\n                    case MOBILE:\n                        updateBuilder.updateMobileNumber((String) field.getValue());\n                        break;\n                    case FAX:\n                        updateBuilder.updateFaxNumber((String) field.getValue());\n                        break;\n                    case BUILDING:\n                        updateBuilder.updateBuilding((String) field.getValue());\n                        break;\n                    case ROOM:\n                        updateBuilder.updateRoom((String) field.getValue());\n                        break;\n                    case ADDRESS:\n                        updateBuilder.updateAddress((String) field.getValue());\n                        break;\n                    case ZIP_CODE:\n                        updateBuilder.updateZipCode((String) field.getValue());\n                        break;\n                    case CITY:\n                        updateBuilder.updateCity((String) field.getValue());\n                        break;\n                    case STATE:\n                        updateBuilder.updateState((String) field.getValue());\n                        break;\n                    case COUNTRY:\n                        updateBuilder.updateCountry((String) field.getValue());\n                        break;\n                    case WEBSITE:\n                        updateBuilder.updateWebsite((String) field.getValue());\n                        break;\n                    default:\n                        throw new IllegalStateException();\n                }\n            }\n            return updateBuilder.done();\n        }\n        return null;\n    }\n\n    @Override\n    public void deleteUser(final long userId) throws DeletionException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService();\n        final ProfileService profileService = serviceAccessor.getProfileService();\n        try {\n            final DeleteUser deleteUser = new DeleteUser(identityService, actorMappingService, profileService, userId);\n            deleteUser.execute();\n            final Set<Long> removedActorIds = deleteUser.getRemovedActorIds();\n            updateActorProcessDependencies(serviceAccessor, actorMappingService, removedActorIds);\n        } catch (final SBonitaException sbe) {\n            throw new DeletionException(sbe);\n        }\n    }\n\n    @Override\n    public void deleteUser(final String userName) throws DeletionException {\n        if (userName == null) {\n            throw new DeletionException(\"User name can not be null!\");\n        }\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService();\n        final ProfileService profileService = serviceAccessor.getProfileService();\n        try {\n            final DeleteUser deleteUser = new DeleteUser(identityService, actorMappingService, profileService,\n                    userName);\n            deleteUser.execute();\n            final Set<Long> removedActorIds = deleteUser.getRemovedActorIds();\n            updateActorProcessDependencies(serviceAccessor, actorMappingService, removedActorIds);\n        } catch (final SBonitaException sbe) {\n            throw new DeletionException(sbe);\n        }\n    }\n\n    @Override\n    public void deleteUsers(final List<Long> userIds) throws DeletionException {\n        if (userIds != null && !userIds.isEmpty()) {\n            final ServiceAccessor serviceAccessor = getServiceAccessor();\n            final IdentityService identityService = serviceAccessor.getIdentityService();\n            final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService();\n            final ProfileService profileService = serviceAccessor.getProfileService();\n            try {\n                final DeleteUsers deleteUsers = new DeleteUsers(identityService, actorMappingService, profileService,\n                        userIds);\n                deleteUsers.execute();\n            } catch (final SBonitaException sbe) {\n                throw new DeletionException(sbe);\n            }\n        }\n    }\n\n    @Override\n    public User getUser(final long userId) throws UserNotFoundException {\n        if (userId == -1) {\n            throw new UserNotFoundException(\"The technical user is not a usable user\");\n        }\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        try {\n            final GetSUser transactionContent = new GetSUser(identityService, userId);\n            transactionContent.execute();\n            return ModelConvertor.toUser(transactionContent.getResult());\n        } catch (final SUserNotFoundException sunfe) {\n            throw new UserNotFoundException(sunfe);\n        } catch (final SBonitaException sbe) {\n            throw new RetrieveException(sbe);\n        }\n    }\n\n    @Override\n    public User getUserByUserName(final String userName) throws UserNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        try {\n            final GetSUser transactionContent = new GetSUser(identityService, userName);\n            transactionContent.execute();\n            return ModelConvertor.toUser(transactionContent.getResult());\n        } catch (final SUserNotFoundException sunfe) {\n            throw new UserNotFoundException(sunfe);\n        } catch (final SBonitaException sbe) {\n            throw new RetrieveException(sbe);\n        }\n    }\n\n    @Override\n    public UserWithContactData getUserWithProfessionalDetails(final long userId) throws UserNotFoundException {\n        final User user = getUser(userId);\n        final ContactData contactData = getUserContactData(userId, false);\n        return new UserWithContactDataImpl(user, contactData);\n    }\n\n    @Override\n    public ContactData getUserContactData(final long userId, final boolean personal) throws UserNotFoundException {\n        if (userId == -1) {\n            throw new UserNotFoundException(\"The technical user is not a usable user\");\n        }\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        try {\n            final GetSContactInfo txContent = new GetSContactInfo(userId, identityService, personal);\n            txContent.execute();\n            final SContactInfo result = txContent.getResult();\n            if (result == null) {\n                return null;\n            }\n            return ModelConvertor.toUserContactData(result);\n        } catch (final SBonitaException sbe) {\n            throw new RetrieveException(sbe);\n        }\n    }\n\n    @Override\n    public long getNumberOfUsers() {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        try {\n            final GetNumberOfInstance transactionContent = new GetNumberOfInstance(\"getNumberOfUsers\", identityService);\n            transactionContent.execute();\n            return transactionContent.getResult();\n        } catch (final SBonitaException sbe) {\n            throw new RetrieveException(sbe);\n        }\n    }\n\n    @Override\n    // FIXME rewrite ME!!!\n    public List<User> getUsers(final int startIndex, final int maxResults, final UserCriterion criterion) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        try {\n            final IdentityService identityService = serviceAccessor.getIdentityService();\n            return getUsersWithOrder(startIndex, maxResults, criterion, identityService);\n        } catch (final SBonitaException sbe) {\n            throw new RetrieveException(sbe);\n        }\n    }\n\n    @Override\n    public Map<Long, User> getUsers(final List<Long> userIds) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        final Map<Long, User> users = new HashMap<>();\n        try {\n            final List<SUser> sUsers = identityService.getUsers(userIds);\n            for (final SUser sUser : sUsers) {\n                users.put(sUser.getId(), ModelConvertor.toUser(sUser));\n            }\n            return users;\n        } catch (final SUserNotFoundException sunfe) {\n            throw new RetrieveException(sunfe);\n        }\n    }\n\n    @Override\n    public Map<String, User> getUsersByUsernames(final List<String> userNames) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        final Map<String, User> users = new HashMap<>();\n        try {\n            final List<SUser> sUsers = identityService.getUsersByUsername(userNames);\n            for (final SUser sUser : sUsers) {\n                users.put(sUser.getUserName(), ModelConvertor.toUser(sUser));\n            }\n            return users;\n        } catch (final SIdentityException sunfe) {\n            throw new RetrieveException(sunfe);\n        }\n    }\n\n    @Override\n    public SearchResult<User> searchUsers(final SearchOptions options) throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final SearchUsers searchUsers = new SearchUsers(identityService,\n                searchEntitiesDescriptor.getSearchUserDescriptor(), options);\n        try {\n            searchUsers.execute();\n            return searchUsers.getResult();\n        } catch (final SBonitaException sbe) {\n            throw new SearchException(sbe);\n        }\n    }\n\n    @Override\n    public long getNumberOfUsersInRole(final long roleId) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        try {\n            final GetNumberOfUsersInType transactionContentWithResult = new GetNumberOfUsersInType(roleId,\n                    \"getNumberOfUsersInRole\", identityService);\n            transactionContentWithResult.execute();\n            return transactionContentWithResult.getResult();\n        } catch (final SBonitaException sbe) {\n            throw new RetrieveException(sbe);\n        }\n    }\n\n    @Override\n    public List<User> getUsersInRole(final long roleId, final int startIndex, final int maxResults,\n            final UserCriterion criterion) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        Sort sort = getSortFromCriterion(criterion);\n        try {\n            return ModelConvertor.toUsers(\n                    identityService.getUsersWithRole(roleId, startIndex, maxResults, sort.getField(), sort.getOrder()));\n        } catch (final SBonitaException sbe) {\n            throw new RetrieveException(sbe);\n        }\n    }\n\n    @Override\n    public List<User> getActiveUsersInRole(long roleId, int startIndex, int maxResults, UserCriterion criterion) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        Sort sort = getSortFromCriterion(criterion);\n        try {\n            return ModelConvertor.toUsers(identityService.getActiveUsersWithRole(roleId, startIndex, maxResults,\n                    sort.getField(), sort.getOrder()));\n        } catch (final SBonitaException sbe) {\n            throw new RetrieveException(sbe);\n        }\n    }\n\n    @Override\n    public List<User> getInactiveUsersInRole(long roleId, int startIndex, int maxResults, UserCriterion criterion) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        Sort sort = getSortFromCriterion(criterion);\n        try {\n            return ModelConvertor.toUsers(identityService.getInactiveUsersWithRole(roleId, startIndex, maxResults,\n                    sort.getField(), sort.getOrder()));\n        } catch (final SBonitaException sbe) {\n            throw new RetrieveException(sbe);\n        }\n    }\n\n    @Override\n    public List<User> getUsersWithManager(long managerId, int startIndex, int maxResults, UserCriterion criterion) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        Sort sort = getSortFromCriterion(criterion);\n        try {\n            return ModelConvertor.toUsers(identityService.getUsersWithManager(managerId, startIndex, maxResults,\n                    sort.getField(), sort.getOrder()));\n        } catch (final SBonitaException sbe) {\n            throw new RetrieveException(sbe);\n        }\n    }\n\n    @Override\n    public List<User> getActiveUsersWithManager(long managerId, int startIndex, int maxResults,\n            UserCriterion criterion) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        Sort sort = getSortFromCriterion(criterion);\n        try {\n            return ModelConvertor.toUsers(identityService.getActiveUsersWithManager(managerId, startIndex, maxResults,\n                    sort.getField(), sort.getOrder()));\n        } catch (final SBonitaException sbe) {\n            throw new RetrieveException(sbe);\n        }\n    }\n\n    @Override\n    public List<User> getInactiveUsersWithManager(long managerId, int startIndex, int maxResults,\n            UserCriterion criterion) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        Sort sort = getSortFromCriterion(criterion);\n        try {\n            return ModelConvertor.toUsers(identityService.getInactiveUsersWithManager(managerId, startIndex, maxResults,\n                    sort.getField(), sort.getOrder()));\n        } catch (final SBonitaException sbe) {\n            throw new RetrieveException(sbe);\n        }\n    }\n\n    @Override\n    public long getNumberOfUsersInGroup(final long groupId) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        final GetNumberOfUsersInType transactionContentWithResult = new GetNumberOfUsersInType(groupId,\n                \"getNumberOfUsersInGroup\", identityService);\n        try {\n            transactionContentWithResult.execute();\n            return transactionContentWithResult.getResult();\n        } catch (final SBonitaException sbe) {\n            throw new RetrieveException(sbe);\n        }\n    }\n\n    @Override\n    public List<User> getUsersInGroup(final long groupId, final int startIndex, final int maxResults,\n            final UserCriterion criterion) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        Sort sort = getSortFromCriterion(criterion);\n        try {\n            return ModelConvertor\n                    .toUsers(identityService.getUsersInGroup(groupId, startIndex, maxResults, sort.getField(),\n                            sort.getOrder()));\n        } catch (final SBonitaException sbe) {\n            throw new RetrieveException(sbe);\n        }\n    }\n\n    @Override\n    public List<User> getActiveUsersInGroup(long groupId, int startIndex, int maxResults, UserCriterion criterion) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        Sort sort = getSortFromCriterion(criterion);\n        try {\n            return ModelConvertor\n                    .toUsers(identityService.getActiveUsersInGroup(groupId, startIndex, maxResults, sort.getField(),\n                            sort.getOrder()));\n        } catch (final SBonitaException sbe) {\n            throw new RetrieveException(sbe);\n        }\n\n    }\n\n    @Override\n    public List<User> getInactiveUsersInGroup(long groupId, int startIndex, int maxResults, UserCriterion criterion) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        Sort sort = getSortFromCriterion(criterion);\n        try {\n            return ModelConvertor.toUsers(identityService.getInactiveUsersInGroup(groupId, startIndex, maxResults,\n                    sort.getField(), sort.getOrder()));\n        } catch (final SBonitaException sbe) {\n            throw new RetrieveException(sbe);\n        }\n    }\n\n    @Override\n    public List<Long> getUserIdsWithCustomUserInfo(String infoName, String infoValue, boolean usePartialMatch,\n            int startIndex, int maxResults) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        try {\n            return identityService.getUserIdsWithCustomUserInfo(infoName, infoValue, usePartialMatch, startIndex,\n                    maxResults);\n        } catch (SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public Role createRole(final String roleName) throws CreationException {\n        return createRole(new RoleCreator(roleName));\n    }\n\n    @Override\n    public Role createRole(final RoleCreator creator) throws CreationException {\n        if (creator == null) {\n            throw new CreationException(\"Unable to create a role with a null RoleCreator object\");\n        }\n        if (creator.getFields().get(org.bonitasoft.engine.identity.RoleCreator.RoleField.NAME) == null) {\n            throw new CreationException(\"Unable to create a role with a null name\");\n        }\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        if (creator.getFields().containsKey(RoleCreator.RoleField.ICON_NAME)\n                || creator.getFields().containsKey(RoleCreator.RoleField.ICON_PATH)) {\n            log.warn(\"setIconName and setIconPath are deprecated, use setIcon instead\");\n        }\n        final SRole sRole = ModelConvertor.constructSRole(creator);\n        try {\n            getRoleByName(sRole.getName());\n            throw new AlreadyExistsException(\"A role named \\\"\" + sRole.getName() + \"\\\" already exists\");\n        } catch (final RoleNotFoundException ignored) {\n            // Ok, role can now be created.\n        }\n        try {\n            identityService.createRole(sRole,\n                    (String) creator.getFields().get(RoleCreator.RoleField.ICON_FILENAME),\n                    (byte[]) creator.getFields().get(RoleCreator.RoleField.ICON_CONTENT));\n            return ModelConvertor.toRole(sRole);\n        } catch (final SIdentityException e) {\n            throw new CreationException(\"Role create exception!\", e);\n        }\n    }\n\n    @Override\n    public Role updateRole(final long roleId, final RoleUpdater updateDescriptor)\n            throws RoleNotFoundException, UpdateException {\n        if (updateDescriptor == null || updateDescriptor.getFields().isEmpty()) {\n            throw new UpdateException(\"The update descriptor does not contain field updates\");\n        }\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        try {\n            final EntityUpdateDescriptor changeDescriptor = getRoleUpdateDescriptor(updateDescriptor);\n            return ModelConvertor.toRole(identityService.updateRole(identityService.getRole(roleId), changeDescriptor,\n                    getIconUpdater(updateDescriptor)));\n        } catch (final SRoleNotFoundException e) {\n            throw new RoleNotFoundException(e);\n        } catch (SIdentityException e) {\n            throw new UpdateException(e);\n        }\n    }\n\n    private EntityUpdateDescriptor getRoleUpdateDescriptor(final RoleUpdater updateDescriptor) {\n        final SRoleUpdateBuilder roleUpdateBuilder = BuilderFactory.get(SRoleUpdateBuilderFactory.class)\n                .createNewInstance();\n        final Map<RoleField, Serializable> fields = updateDescriptor.getFields();\n        for (final Entry<RoleField, Serializable> field : fields.entrySet()) {\n            switch (field.getKey()) {\n                case NAME:\n                    roleUpdateBuilder.updateName((String) field.getValue());\n                    break;\n                case DISPLAY_NAME:\n                    roleUpdateBuilder.updateDisplayName((String) field.getValue());\n                    break;\n                case DESCRIPTION:\n                    roleUpdateBuilder.updateDescription((String) field.getValue());\n                    break;\n                case ICON_NAME:\n                    log.warn(\"setIconName is deprecated, use setIcon instead\");\n                    break;\n                case ICON_PATH:\n                    log.warn(\"setIconPath is deprecated, use setIcon instead\");\n                    break;\n                default:\n                    break;\n            }\n        }\n        roleUpdateBuilder.updateLastUpdate(System.currentTimeMillis());\n        return roleUpdateBuilder.done();\n    }\n\n    @Override\n    public void deleteRole(final long roleId) throws DeletionException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService();\n\n        final ProfileService profileService = serviceAccessor.getProfileService();\n        final DeleteRole deleteRole = new DeleteRole(identityService, actorMappingService, profileService, roleId);\n        try {\n            deleteRole.execute();\n            final Set<Long> removedActorIds = deleteRole.getRemovedActorIds();\n            updateActorProcessDependencies(serviceAccessor, actorMappingService, removedActorIds);\n        } catch (final SRoleNotFoundException ignored) {\n        } catch (final SBonitaException sbe) {\n            throw new DeletionException(sbe);\n        }\n    }\n\n    @Override\n    public void deleteRoles(final List<Long> roleIds) throws DeletionException {\n        try {\n            NullCheckingUtil.checkArgsNotNull(roleIds);\n        } catch (final IllegalArgumentException e) {\n            throw new DeletionException(e);\n        }\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService();\n        final ProfileService profileService = serviceAccessor.getProfileService();\n        final DeleteRoles deleteRoles = new DeleteRoles(identityService, actorMappingService, profileService, roleIds);\n        try {\n            deleteRoles.execute();\n        } catch (final SBonitaException e) {\n            throw new DeletionException(e);\n        }\n    }\n\n    @Override\n    public Role getRole(final long roleId) throws RoleNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        try {\n            return ModelConvertor.toRole(identityService.getRole(roleId));\n        } catch (SRoleNotFoundException e) {\n            throw new RoleNotFoundException(e);\n        }\n    }\n\n    @Override\n    public Role getRoleByName(final String roleName) throws RoleNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        try {\n            return ModelConvertor.toRole(identityService.getRoleByName(roleName));\n        } catch (final SRoleNotFoundException e) {\n            throw new RoleNotFoundException(e);\n        }\n    }\n\n    @Override\n    public long getNumberOfRoles() {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        try {\n            final GetNumberOfInstance getNumberOfInstance = new GetNumberOfInstance(\"getNumberOfRoles\",\n                    identityService);\n            getNumberOfInstance.execute();\n            return getNumberOfInstance.getResult();\n        } catch (final SBonitaException e) {\n            return 0;\n        }\n    }\n\n    @Override\n    public List<Role> getRoles(final int startIndex, final int maxResults, final RoleCriterion criterion) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        String field;\n        OrderByType order;\n        switch (criterion) {\n            case NAME_ASC:\n                field = SRole.NAME;\n                order = OrderByType.ASC;\n                break;\n            case NAME_DESC:\n                field = SRole.NAME;\n                order = OrderByType.DESC;\n                break;\n            case DISPLAY_NAME_ASC:\n                field = SRole.DISPLAY_NAME;\n                order = OrderByType.ASC;\n                break;\n            case DISPLAY_NAME_DESC:\n                field = SRole.DISPLAY_NAME;\n                order = OrderByType.DESC;\n                break;\n            default:\n                throw new IllegalStateException();\n        }\n        try {\n            final GetRoles getRolesWithOrder = new GetRoles(identityService, startIndex, maxResults, field, order);\n            getRolesWithOrder.execute();\n            return ModelConvertor.toRoles(getRolesWithOrder.getResult());\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public Map<Long, Role> getRoles(final List<Long> roleIds) {\n        final Map<Long, Role> roles = new HashMap<>();\n        for (final Long roleId : roleIds) {\n            try {\n                final Role role = getRole(roleId);\n                roles.put(roleId, role);\n            } catch (final RoleNotFoundException e) {\n                // if the role does not exist; skip the role\n            }\n        }\n        return roles;\n    }\n\n    @Override\n    public SearchResult<Role> searchRoles(final SearchOptions options) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final SearchRoles searchRoles = new SearchRoles(identityService,\n                searchEntitiesDescriptor.getSearchRoleDescriptor(), options);\n        try {\n            searchRoles.execute();\n            return searchRoles.getResult();\n        } catch (final SBonitaException sbe) {\n            throw new BonitaRuntimeException(sbe);\n        }\n    }\n\n    @Override\n    public Group createGroup(final String name, final String parentPath) throws CreationException {\n        final GroupCreator groupCreator = new GroupCreator(name);\n        groupCreator.setParentPath(parentPath);\n        return createGroup(groupCreator);\n    }\n\n    @Override\n    public Group createGroup(final GroupCreator creator) throws CreationException {\n        if (creator == null) {\n            throw new CreationException(\"Cannot create a null group\");\n        }\n        String groupName = creator.getFields().get(GroupCreator.GroupField.NAME).toString();\n        if (groupName.contains(\"/\")) {\n            throw new InvalidGroupNameException(\"Cannot create a group with '/' in its name\");\n        }\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        if (creator.getFields().containsKey(GroupCreator.GroupField.ICON_NAME)\n                || creator.getFields().containsKey(GroupCreator.GroupField.ICON_PATH)) {\n            log.warn(\"setIconName and setIconPath are deprecated, use setIcon instead\");\n        }\n        final SGroup sGroup = ModelConvertor.constructSGroup(creator);\n        try {\n            identityService.getGroupByPath(sGroup.getPath());\n            throw new AlreadyExistsException(\"Group named \\\"\" + sGroup.getName() + \"\\\" already exists\");\n        } catch (final SGroupNotFoundException ignored) {\n\n        }\n        try {\n            identityService.createGroup(sGroup,\n                    (String) creator.getFields().get(GroupCreator.GroupField.ICON_FILENAME),\n                    (byte[]) creator.getFields().get(GroupCreator.GroupField.ICON_CONTENT));\n        } catch (SGroupCreationException e) {\n            throw new CreationException(e);\n        }\n        return ModelConvertor.toGroup(sGroup);\n    }\n\n    @Override\n    public Group updateGroup(final long groupId, final GroupUpdater updater)\n            throws GroupNotFoundException, UpdateException, AlreadyExistsException {\n        if (updater == null || updater.getFields().isEmpty()) {\n            throw new UpdateException(\"The update descriptor does not contain field updates\");\n        }\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        try {\n            checkPathUniqueness(groupId, updater, identityService);\n            final EntityUpdateDescriptor changeDescriptor = getGroupUpdateDescriptor(updater);\n            return ModelConvertor.toGroup(\n                    new UpdateGroup(groupId, changeDescriptor, identityService, getIconUpdater(updater)).update());\n        } catch (final SGroupNotFoundException e) {\n            throw new GroupNotFoundException(e);\n        } catch (final SIdentityException sbe) {\n            throw new UpdateException(sbe);\n        }\n    }\n\n    private void checkPathUniqueness(long groupId, GroupUpdater updater, IdentityService identityService)\n            throws SGroupNotFoundException, AlreadyExistsException {\n        final Serializable updatedName = updater.getFields().get(GroupField.NAME);\n        SGroup sGroupToBeUpdated = identityService.getGroup(groupId);\n        String name = updatedName != null ? updatedName.toString() : sGroupToBeUpdated.getName();\n        StringBuilder sb = new StringBuilder();\n        String parentPath = updater.getFields().get(GroupField.PARENT_PATH) != null\n                ? updater.getFields().get(GroupField.PARENT_PATH).toString() : \"\";\n        sb.append(parentPath).append(\"/\").append(name);\n        try {\n            if (updatedName != null) {\n                SGroup group = identityService.getGroupByPath(sb.toString());\n                if (group.getId() != groupId) {\n                    throw new AlreadyExistsException(\"Group named \\\"\" + name + \"\\\" already exists\");\n                }\n            }\n        } catch (final SGroupNotFoundException ignored) {\n        }\n    }\n\n    private EntityUpdateDescriptor getGroupUpdateDescriptor(final GroupUpdater updateDescriptor)\n            throws UpdateException {\n        final SGroupUpdateBuilder groupUpdateBuilder = BuilderFactory.get(SGroupUpdateBuilderFactory.class)\n                .createNewInstance();\n        final Map<GroupField, Serializable> fields = updateDescriptor.getFields();\n        for (final Entry<GroupField, Serializable> field : fields.entrySet()) {\n            final Serializable value = field.getValue();\n            switch (field.getKey()) {\n                case NAME:\n                    groupUpdateBuilder.updateName((String) value);\n                    break;\n                case DISPLAY_NAME:\n                    groupUpdateBuilder.updateDisplayName((String) value);\n                    break;\n                case DESCRIPTION:\n                    groupUpdateBuilder.updateDescription((String) value);\n                    break;\n                case ICON_NAME:\n                    log.warn(\"updateIconName is deprecated, use updateIcon instead\");\n                    break;\n                case ICON_PATH:\n                    log.warn(\"updateIconPath is deprecated, use updateIcon instead\");\n                    break;\n                case ICON_CONTENT:\n                case ICON_FILENAME:\n                    break;\n                case PARENT_PATH:\n                    groupUpdateBuilder\n                            .updateParentPath((value != null && ((String) value).isEmpty()) ? null : (String) value);\n                    break;\n                default:\n                    throw new UpdateException(\"Invalid field: \" + field.getKey().name());\n            }\n        }\n        groupUpdateBuilder.updateLastUpdate(System.currentTimeMillis());\n        return groupUpdateBuilder.done();\n    }\n\n    @Override\n    public void deleteGroup(final long groupId) throws DeletionException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService();\n        final ProfileService profileService = serviceAccessor.getProfileService();\n        final DeleteGroup deleteGroup = new DeleteGroup(identityService, actorMappingService, profileService, groupId);\n        try {\n            deleteGroup.execute();\n            updateActorProcessDependencies(serviceAccessor, actorMappingService, deleteGroup.getRemovedActorIds());\n        } catch (final SGroupNotFoundException sgnfe) {\n            throw new DeletionException(new GroupNotFoundException(sgnfe));\n        } catch (final SBonitaException sbe) {\n            throw new DeletionException(sbe);\n        }\n    }\n\n    @Override\n    public void deleteGroups(final List<Long> groupIds) throws DeletionException {\n        if (groupIds == null) {\n            throw new IllegalArgumentException(\"the list of groups is null\");\n        }\n        if (!groupIds.isEmpty()) {\n            final ServiceAccessor serviceAccessor = getServiceAccessor();\n            final IdentityService identityService = serviceAccessor.getIdentityService();\n            final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService();\n            final ProfileService profileService = serviceAccessor.getProfileService();\n            try {\n                final DeleteGroups deleteGroups = new DeleteGroups(identityService, actorMappingService, profileService,\n                        groupIds);\n                deleteGroups.execute();\n                updateActorProcessDependencies(serviceAccessor, actorMappingService, deleteGroups.getRemovedActorIds());\n            } catch (final SGroupNotFoundException sgnfe) {\n                throw new DeletionException(new GroupNotFoundException(sgnfe));\n            } catch (final SBonitaException e) {\n                throw new DeletionException(e);\n            }\n        }\n    }\n\n    @Override\n    public Group getGroup(final long groupId) throws GroupNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        try {\n            return ModelConvertor.toGroup(serviceAccessor.getIdentityService().getGroup(groupId));\n        } catch (final SGroupNotFoundException e) {\n            throw new GroupNotFoundException(e);\n        }\n    }\n\n    @Override\n    public Group getGroupByPath(final String groupPath) throws GroupNotFoundException {\n        try {\n            return ModelConvertor.toGroup(getServiceAccessor().getIdentityService().getGroupByPath(groupPath));\n        } catch (final SGroupNotFoundException e) {\n            throw new GroupNotFoundException(e);\n        }\n    }\n\n    @Override\n    public long getNumberOfGroups() {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        try {\n            final GetNumberOfInstance getNumberOfInstance = new GetNumberOfInstance(\"getNumberOfGroups\",\n                    identityService);\n            getNumberOfInstance.execute();\n            return getNumberOfInstance.getResult();\n        } catch (final SBonitaException sbe) {\n            throw new RetrieveException(sbe);\n        }\n    }\n\n    @Override\n    public Map<Long, Group> getGroups(final List<Long> groupIds) {\n        final Map<Long, Group> groups = new HashMap<>();\n        for (final Long groupId : groupIds) {\n            try {\n                final Group group = getGroup(groupId);\n                groups.put(groupId, group);\n            } catch (final GroupNotFoundException e) {\n                // if the group does not exist; skip the group\n            }\n        }\n        return groups;\n    }\n\n    @Override\n    public List<Group> getGroups(final int startIndex, final int maxResults, final GroupCriterion pagingCriterion) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        String field;\n        OrderByType order;\n        switch (pagingCriterion) {\n            case NAME_ASC:\n                field = SGroup.NAME;\n                order = OrderByType.ASC;\n                break;\n            case LABEL_ASC:\n                field = SGroup.DISPLAY_NAME;\n                order = OrderByType.ASC;\n                break;\n            case NAME_DESC:\n                field = SGroup.NAME;\n                order = OrderByType.DESC;\n                break;\n            case LABEL_DESC:\n                field = SGroup.DISPLAY_NAME;\n                order = OrderByType.DESC;\n                break;\n            default:\n                throw new IllegalStateException();\n        }\n        try {\n            final GetGroups getGroups = new GetGroups(identityService, startIndex, maxResults, order, field);\n            getGroups.execute();\n            return ModelConvertor.toGroups(getGroups.getResult());\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public SearchResult<Group> searchGroups(final SearchOptions options) throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final SearchGroups searchGroups = new SearchGroups(identityService,\n                searchEntitiesDescriptor.getSearchGroupDescriptor(), options);\n        try {\n            searchGroups.execute();\n            return searchGroups.getResult();\n        } catch (final SBonitaException sbe) {\n            throw new SearchException(sbe);\n        }\n    }\n\n    /**\n     * Check / update process resolution information, for all processes in a list of actor IDs.\n     */\n    private void updateActorProcessDependencies(final ServiceAccessor serviceAccessor,\n            final ActorMappingService actorMappingService,\n            final Set<Long> removedActorIds) throws SBonitaException {\n        final Set<Long> processDefinitionIds = new HashSet<>(removedActorIds.size());\n        for (final Long actorId : removedActorIds) {\n            final GetActor getActor = new GetActor(actorMappingService, actorId);\n            getActor.execute();\n            final SActor actor = getActor.getResult();\n            final Long processDefId = actor.getScopeId();\n            if (!processDefinitionIds.contains(processDefId)) {\n                processDefinitionIds.add(processDefId);\n                serviceAccessor.getBusinessArchiveArtifactsManager().resolveDependencies(actor.getScopeId(),\n                        serviceAccessor);\n            }\n        }\n    }\n\n    private List<User> getUsersWithOrder(final int startIndex, final int maxResults,\n            final UserCriterion pagingCriterion, final IdentityService identityService)\n            throws SIdentityException {\n        final String field = getUserFieldKey(pagingCriterion);\n        final OrderByType order = getUserOrderByType(pagingCriterion);\n        if (field == null) {\n            return ModelConvertor.toUsers(identityService.getUsers(startIndex, maxResults));\n        }\n        return ModelConvertor.toUsers(identityService.getUsers(startIndex, maxResults, field, order));\n    }\n\n    private OrderByType getUserOrderByType(final UserCriterion pagingCriterion) {\n        OrderByType order;\n        switch (pagingCriterion) {\n            case USER_NAME_ASC:\n                order = OrderByType.ASC;\n                break;\n            case FIRST_NAME_ASC:\n                order = OrderByType.ASC;\n                break;\n            case LAST_NAME_ASC:\n                order = OrderByType.ASC;\n                break;\n            case FIRST_NAME_DESC:\n                order = OrderByType.DESC;\n                break;\n            case LAST_NAME_DESC:\n                order = OrderByType.DESC;\n                break;\n            case USER_NAME_DESC:\n                order = OrderByType.DESC;\n                break;\n            default:\n                throw new IllegalStateException();\n        }\n        return order;\n    }\n\n    private String getUserFieldKey(final UserCriterion pagingCriterion) {\n        String field;\n        switch (pagingCriterion) {\n            case USER_NAME_ASC:\n                field = SUser.USER_NAME;\n                break;\n            case FIRST_NAME_ASC:\n                field = SUser.FIRST_NAME;\n                break;\n            case LAST_NAME_ASC:\n                field = SUser.LAST_NAME;\n                break;\n            case FIRST_NAME_DESC:\n                field = SUser.FIRST_NAME;\n                break;\n            case LAST_NAME_DESC:\n                field = SUser.LAST_NAME;\n                break;\n            case USER_NAME_DESC:\n                field = SUser.USER_NAME;\n                break;\n            default:\n                throw new IllegalStateException();\n        }\n        return field;\n    }\n\n    @Override\n    public UserMembership addUserMembership(final long userId, final long groupId, final long roleId)\n            throws CreationException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        final long assignedBy = SessionInfos.getUserIdFromSession();\n        try {\n            final GetUserMembership getUserMembership = new GetUserMembership(userId, groupId, roleId, identityService);\n            getUserMembership.execute();\n            if (getUserMembership.getResult() != null) {\n                throw new AlreadyExistsException(\"A userMembership with userId \\\"\" + userId + \"\\\", groupId \\\"\" + groupId\n                        + \"\\\" and roleId \\\"\" + roleId\n                        + \"\\\" already exists\");\n            }\n        } catch (final SBonitaException e) {\n            // Membership does not exists but was unable to be created\n        }\n        try {\n            final AddUserMembership createUserMembership = new AddUserMembership(userId, groupId, roleId, assignedBy,\n                    identityService);\n            createUserMembership.execute();\n            final SUserMembership sUserMembership = createUserMembership.getResult();\n            return ModelConvertor.toUserMembership(sUserMembership);\n        } catch (final SBonitaException sbe) {\n            throw new CreationException(sbe);\n        }\n    }\n\n    @Override\n    public void addUserMemberships(final List<Long> userIds, final long groupId, final long roleId)\n            throws CreationException {\n        // FIXME rewrite\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        final long currentUserId = SessionInfos.getUserIdFromSession();\n        try {\n            final AddUserMemberships transactionContent = new AddUserMemberships(groupId, roleId, userIds,\n                    identityService, currentUserId);\n            transactionContent.execute();\n        } catch (final SBonitaException sbe) {\n            throw new CreationException(sbe);\n        }\n    }\n\n    @Override\n    public UserMembership updateUserMembership(final long userMembershipId, final long newGroupId, final long newRoleId)\n            throws UpdateException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        final EntityUpdateDescriptor changeDescriptor = BuilderFactory.get(SUserMembershipUpdateBuilderFactory.class)\n                .createNewInstance()\n                .updateGroupId(newGroupId).updateRoleId(newRoleId).done();\n        try {\n            final TransactionContent transactionContent = new UpdateMembershipByRoleIdAndGroupId(userMembershipId,\n                    identityService, changeDescriptor);\n            transactionContent.execute();\n            final GetUserMembership getMembershipAfterUpdate = new GetUserMembership(userMembershipId, identityService);\n            getMembershipAfterUpdate.execute();\n            final SUserMembership sMembershipAfterUpdate = getMembershipAfterUpdate.getResult();\n            return ModelConvertor.toUserMembership(sMembershipAfterUpdate);\n        } catch (final SBonitaException sbe) {\n            throw new UpdateException(sbe);\n        }\n    }\n\n    @Override\n    public void deleteUserMembership(final long userMembershipId) throws DeletionException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        try {\n            identityService.deleteUserMembership(userMembershipId);\n        } catch (final SIdentityException e) {\n            throw new DeletionException(e);\n        }\n    }\n\n    @Override\n    public void deleteUserMembership(final long userId, final long groupId, final long roleId)\n            throws DeletionException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        try {\n            identityService.deleteLightUserMembership(identityService.getLightUserMembership(userId, groupId, roleId));\n        } catch (final SIdentityException e) {\n            throw new DeletionException(e);\n        }\n    }\n\n    @Override\n    public void deleteUserMemberships(final List<Long> userIds, final long groupId, final long roleId)\n            throws DeletionException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        try {\n            for (final long userId : userIds) {\n                final SUserMembership userMembership = identityService.getLightUserMembership(userId, groupId, roleId);\n                identityService.deleteLightUserMembership(userMembership);\n            }\n        } catch (final SIdentityException e) {\n            throw new DeletionException(e);\n        }\n    }\n\n    @Override\n    public UserMembership getUserMembership(final long userMembershipId) throws MembershipNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        final GetUserMembership getUserMembership = new GetUserMembership(userMembershipId, identityService);\n        try {\n            getUserMembership.execute();\n            final SUserMembership sMembership = getUserMembership.getResult();\n            return ModelConvertor.toUserMembership(sMembership);\n        } catch (final SBonitaException sbe) {\n            throw new MembershipNotFoundException(sbe);\n        }\n    }\n\n    @Override\n    public long getNumberOfUserMemberships(final long userId) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        try {\n            final TransactionContentWithResult<Long> transactionContent = new GetNumberOfUserMemberships(userId,\n                    identityService);\n            transactionContent.execute();\n            return transactionContent.getResult();\n        } catch (final SBonitaException sbe) {\n            throw new RetrieveException(sbe);\n        }\n    }\n\n    @Override\n    public List<UserMembership> getUserMemberships(final long userId, final int startIndex, final int maxResults,\n            final UserMembershipCriterion pagingCrterion) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        try {\n            final IdentityService identityService = serviceAccessor.getIdentityService();\n            OrderByOption orderByOption;\n            switch (pagingCrterion) {\n                case ROLE_NAME_DESC:\n                    orderByOption = new OrderByOption(SRole.class, SRole.NAME, OrderByType.DESC);\n                    break;\n                case GROUP_NAME_ASC:\n                    orderByOption = new OrderByOption(SGroup.class, SGroup.NAME, OrderByType.ASC);\n                    break;\n                case GROUP_NAME_DESC:\n                    orderByOption = new OrderByOption(SGroup.class, SGroup.NAME, OrderByType.DESC);\n                    break;\n                // case ASSIGNED_BY_ASC:\n                // orderByOption = new OrderByOption(SUserMembership.class, modelBuilder.getUserMembershipBuilder().getAssignedByKey(), OrderByType.ASC);\n                // break;\n                // case ASSIGNED_BY_DESC:\n                // orderByOption = new OrderByOption(SUserMembership.class, modelBuilder.getUserMembershipBuilder().getAssignedByKey(), OrderByType.DESC);\n                // break;\n                case ASSIGNED_DATE_ASC:\n                    orderByOption = new OrderByOption(SUserMembership.class, SUserMembership.ASSIGNED_DATE,\n                            OrderByType.ASC);\n                    break;\n                case ASSIGNED_DATE_DESC:\n                    orderByOption = new OrderByOption(SUserMembership.class, SUserMembership.ASSIGNED_DATE,\n                            OrderByType.DESC);\n                    break;\n                case ROLE_NAME_ASC:\n                default:\n                    orderByOption = new OrderByOption(SRole.class, SRole.NAME, OrderByType.ASC);\n                    break;\n            }\n\n            return getUserMemberships(userId, startIndex, maxResults, orderByOption, identityService);\n        } catch (final SBonitaException sbe) {\n            throw new RetrieveException(sbe);\n        }\n    }\n\n    private List<UserMembership> getUserMemberships(final long userId, final int startIndex, final int maxResults,\n            final OrderByOption orderByOption, final IdentityService identityService) throws SBonitaException {\n        List<SUserMembership> sUserMemberships;\n        if (userId == -1) {\n            sUserMemberships = identityService.getUserMemberships(startIndex, maxResults, orderByOption);\n        } else if (orderByOption != null) {\n            sUserMemberships = identityService.getUserMembershipsOfUser(userId, startIndex, maxResults, orderByOption);\n        } else {\n            sUserMemberships = identityService.getUserMembershipsOfUser(userId, startIndex, maxResults);\n        }\n        return ModelConvertor.toUserMembership(sUserMemberships);\n    }\n\n    @Override\n    public List<UserMembership> getUserMembershipsByGroup(final long groupId, final int startIndex,\n            final int maxResults) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        final GetUserMembershipsOfGroup transactionContentWithResult = new GetUserMembershipsOfGroup(groupId,\n                identityService, startIndex, maxResults);\n        try {\n            transactionContentWithResult.execute();\n            return ModelConvertor.toUserMembership(transactionContentWithResult.getResult());\n        } catch (final SBonitaException sbe) {\n            throw new RetrieveException(sbe);\n        }\n    }\n\n    @Override\n    public List<UserMembership> getUserMembershipsByRole(final long roleId, final int startIndex,\n            final int maxResults) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        try {\n            final GetUserMembershipsOfRole transactionContentWithResult = new GetUserMembershipsOfRole(roleId,\n                    identityService, startIndex, maxResults);\n            transactionContentWithResult.execute();\n            return ModelConvertor.toUserMembership(transactionContentWithResult.getResult());\n        } catch (final SBonitaException sbe) {\n            throw new RetrieveException(sbe);\n        }\n    }\n\n    @Override\n    public void deleteOrganization() throws DeletionException {\n        final OrganizationAPIImpl organizationAPIImpl = new OrganizationAPIImpl(getServiceAccessor(), 100);\n        organizationAPIImpl.deleteOrganization();\n    }\n\n    @Override\n    public void importOrganization(final String organizationContent) throws OrganizationImportException {\n        importOrganizationWithWarnings(organizationContent, ImportPolicy.MERGE_DUPLICATES);\n    }\n\n    @Override\n    public List<String> importOrganizationWithWarnings(String organizationContent, ImportPolicy policy)\n            throws OrganizationImportException {\n        return getOrganizationAPIDelegate().importOrganizationWithWarnings(organizationContent, policy);\n    }\n\n    @Override\n    public String exportOrganization() throws OrganizationExportException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final int maxResults = 100;\n        final ExportOrganization exportOrganization = new ExportOrganization(serviceAccessor.getIdentityService(),\n                maxResults);\n        try {\n            exportOrganization.execute();\n            return exportOrganization.getResult();\n        } catch (final SBonitaException e) {\n            throw new OrganizationExportException(e);\n        }\n    }\n\n    @Override\n    public CustomUserInfoDefinition createCustomUserInfoDefinition(final CustomUserInfoDefinitionCreator creator)\n            throws CreationException {\n        return createCustomUserInfoDefinitionAPI().create(creator);\n    }\n\n    @Override\n    public List<CustomUserInfoDefinition> getCustomUserInfoDefinitions(final int startIndex, final int maxResult)\n            throws RetrieveException {\n        return createCustomUserInfoDefinitionAPI().list(startIndex, maxResult);\n    }\n\n    @Override\n    public long getNumberOfCustomInfoDefinitions() {\n        return createCustomUserInfoDefinitionAPI().count();\n    }\n\n    @Override\n    public void deleteCustomUserInfoDefinition(final long id) throws DeletionException {\n        createCustomUserInfoDefinitionAPI().delete(id);\n    }\n\n    @Override\n    public List<CustomUserInfo> getCustomUserInfo(final long userId, final int startIndex, final int maxResult) {\n        try {\n            return createCustomUserInfoAPI().list(userId, startIndex, maxResult);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public SearchResult<CustomUserInfoValue> searchCustomUserInfoValues(final SearchOptions options) {\n        try {\n            return createCustomUserInfoValueAPI().search(\n                    getServiceAccessor().getSearchEntitiesDescriptor().getSearchCustomUserInfoValueDescriptor(),\n                    options);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public CustomUserInfoValue setCustomUserInfoValue(final long definitionId, final long userId, final String value)\n            throws UpdateException {\n        try {\n            return ModelConvertor.convert(createCustomUserInfoValueAPI().set(definitionId, userId, value));\n        } catch (final SBonitaException e) {\n            throw new UpdateException(e);\n        }\n    }\n\n    @Override\n    public Icon getIcon(long id) throws NotFoundException {\n        try {\n            SIcon icon = getServiceAccessor().getIconService().getIcon(id);\n            if (icon == null) {\n                throw new NotFoundException(\"unable to find icon with id \" + id);\n            }\n            return ModelConvertor.toIcon(icon);\n        } catch (SBonitaReadException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    private Sort getSortFromCriterion(UserCriterion criterion) {\n        Sort sort = new Sort();\n        switch (criterion) {\n            case FIRST_NAME_ASC:\n                sort.setField(SUser.FIRST_NAME);\n                sort.setOrder(OrderByType.ASC);\n                break;\n            case LAST_NAME_ASC:\n                sort.setField(SUser.LAST_NAME);\n                sort.setOrder(OrderByType.ASC);\n                break;\n            case USER_NAME_ASC:\n                sort.setField(SUser.USER_NAME);\n                sort.setOrder(OrderByType.ASC);\n                break;\n            case FIRST_NAME_DESC:\n                sort.setField(SUser.FIRST_NAME);\n                sort.setOrder(OrderByType.DESC);\n                break;\n            case LAST_NAME_DESC:\n                sort.setField(SUser.LAST_NAME);\n                sort.setOrder(OrderByType.DESC);\n                break;\n            case USER_NAME_DESC:\n                sort.setField(SUser.USER_NAME);\n                sort.setOrder(OrderByType.DESC);\n                break;\n            default:\n                throw new IllegalStateException();\n        }\n        return sort;\n    }\n\n    private CustomUserInfoAPIDelegate createCustomUserInfoAPI() {\n        return new CustomUserInfoAPIDelegate(getServiceAccessor().getIdentityService());\n    }\n\n    private CustomUserInfoDefinitionAPIDelegate createCustomUserInfoDefinitionAPI() {\n        return new CustomUserInfoDefinitionAPIDelegate(getServiceAccessor().getIdentityService());\n    }\n\n    private SCustomUserInfoValueAPI createCustomUserInfoValueAPI() {\n        return new SCustomUserInfoValueAPI(getServiceAccessor().getIdentityService(),\n                BuilderFactory.get(SCustomUserInfoValueUpdateBuilderFactory.class));\n    }\n\n    private static class Sort {\n\n        private String field = null;\n        private OrderByType order = null;\n\n        private Sort() {\n        }\n\n        private String getField() {\n            return field;\n        }\n\n        private void setField(String field) {\n            this.field = field;\n        }\n\n        private OrderByType getOrder() {\n            return order;\n        }\n\n        private void setOrder(OrderByType order) {\n            this.order = order;\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/LoginAPIImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport java.io.IOException;\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.LoginAPI;\nimport org.bonitasoft.engine.api.impl.transaction.CustomTransactions;\nimport org.bonitasoft.engine.authentication.AuthenticationConstants;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException;\nimport org.bonitasoft.engine.core.login.LoginService;\nimport org.bonitasoft.engine.core.login.SLoginException;\nimport org.bonitasoft.engine.exception.BonitaHomeConfigurationException;\nimport org.bonitasoft.engine.platform.LoginException;\nimport org.bonitasoft.engine.platform.LogoutException;\nimport org.bonitasoft.engine.platform.PlatformService;\nimport org.bonitasoft.engine.platform.model.SPlatform;\nimport org.bonitasoft.engine.service.ModelConvertor;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.impl.ServiceAccessorFactory;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.SSessionNotFoundException;\nimport org.bonitasoft.engine.session.SessionNotFoundException;\nimport org.bonitasoft.engine.session.model.SSession;\nimport org.bonitasoft.engine.transaction.TransactionService;\nimport org.springframework.util.CollectionUtils;\n\n/**\n * @author Matthieu Chaffotte\n * @author Zhang Bole\n */\npublic class LoginAPIImpl implements LoginAPI {\n\n    @Override\n    @CustomTransactions\n    @AvailableInMaintenanceMode\n    public APISession login(final String userName, final String password) throws LoginException {\n        try {\n            return loginInternal(userName, password);\n        } catch (final LoginException e) {\n            throw e;\n        } catch (final Exception e) {\n            throw new LoginException(e);\n        }\n    }\n\n    @CustomTransactions\n    @AvailableInMaintenanceMode\n    protected APISession login(final String userName, final String password, final Long tenantId)\n            throws LoginException {\n        try {\n            return loginInternal(userName, password);\n        } catch (final LoginException e) {\n            throw e;\n        } catch (final Exception e) {\n            throw new LoginException(e);\n        }\n    }\n\n    @Override\n    @CustomTransactions\n    @AvailableInMaintenanceMode\n    public APISession login(final Map<String, Serializable> credentials) throws LoginException {\n        checkCredentialsAreNotNullOrEmpty(credentials);\n        try {\n            return loginInternal(credentials);\n        } catch (final LoginException e) {\n            throw e;\n        } catch (final Exception e) {\n            throw new LoginException(e);\n        }\n    }\n\n    protected APISession loginInternal(final String userName, final String password)\n            throws Exception {\n        checkUsernameAndPassword(userName, password);\n        final Map<String, Serializable> credentials = new HashMap<>();\n        credentials.put(AuthenticationConstants.BASIC_USERNAME, userName);\n        credentials.put(AuthenticationConstants.BASIC_PASSWORD, password);\n        return loginInternal(credentials);\n    }\n\n    protected APISession loginInternal(final Map<String, Serializable> credentials)\n            throws Exception {\n        final ServiceAccessor serviceAccessor = ServiceAccessorFactory.getInstance().createServiceAccessor();\n\n        final LoginService loginService = serviceAccessor.getLoginService();\n        final TransactionService transactionService = serviceAccessor.getTransactionService();\n\n        final SSession sSession = transactionService\n                .executeInTransaction(() -> loginService.login(credentials));\n        return ModelConvertor.toAPISession(sSession);\n    }\n\n    private SPlatform getPlatform(final ServiceAccessor serviceAccessor)\n            throws SBonitaException {\n        final PlatformService platformService = serviceAccessor.getPlatformService();\n        try {\n            return serviceAccessor.getTransactionService().executeInTransaction(platformService::getPlatform);\n        } catch (SBonitaException | RuntimeException e) {\n            throw e;\n        } catch (Exception e) {\n            throw new SBonitaRuntimeException(e);\n        }\n    }\n\n    protected void checkUsernameAndPassword(final String userName, final String password) throws LoginException {\n        if (userName == null || userName.isEmpty()) {\n            throw new LoginException(\"User name is null or empty !! \");\n        }\n        if (password == null || password.isEmpty()) {\n            throw new LoginException(\"Password is null or empty !!\");\n        }\n    }\n\n    protected void checkCredentialsAreNotNullOrEmpty(final Map<String, Serializable> credentials)\n            throws LoginException {\n        if (CollectionUtils.isEmpty(credentials)) {\n            throw new LoginException(\"Credentials are null or empty !!\");\n        }\n    }\n\n    protected ServiceAccessor getServiceAccessor() {\n        try {\n            return ServiceAccessorFactory.getInstance().createServiceAccessor();\n        } catch (BonitaHomeConfigurationException | IOException | ReflectiveOperationException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    @Override\n    @CustomTransactions\n    public void logout(final APISession session) throws LogoutException, SessionNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        try {\n            serviceAccessor.getLoginService().logout(session.getId());\n        } catch (final SSessionNotFoundException sbe) {\n            throw new SessionNotFoundException(sbe);\n        } catch (final SLoginException sbe) {\n            throw new LogoutException(sbe);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/MaintenanceAPIImpl.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport org.bonitasoft.engine.api.MaintenanceAPI;\nimport org.bonitasoft.engine.api.impl.transaction.CustomTransactions;\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.maintenance.MaintenanceDetails;\nimport org.bonitasoft.engine.maintenance.impl.MaintenanceDetailsImpl;\nimport org.bonitasoft.engine.platform.PlatformNotFoundException;\nimport org.bonitasoft.engine.platform.PlatformService;\nimport org.bonitasoft.engine.platform.exception.SPlatformNotFoundException;\nimport org.bonitasoft.engine.platform.exception.SPlatformUpdateException;\nimport org.bonitasoft.engine.platform.model.SPlatform;\nimport org.bonitasoft.engine.platform.model.builder.SPlatformUpdateBuilder;\nimport org.bonitasoft.engine.platform.model.builder.impl.SPlatformUpdateBuilderImpl;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.impl.ServiceAccessorFactory;\nimport org.bonitasoft.engine.tenant.TenantStateManager;\n\n/**\n * This API gives access to maintenance administration tasks.\n */\n@AvailableInMaintenanceMode\npublic class MaintenanceAPIImpl implements MaintenanceAPI {\n\n    public MaintenanceAPIImpl() {\n    }\n\n    protected ServiceAccessor getServiceAccessor() {\n        try {\n            return ServiceAccessorFactory.getInstance().createServiceAccessor();\n        } catch (final Exception e) {\n            throw new BonitaRuntimeException(e);\n        }\n    }\n\n    @Override\n    public MaintenanceDetails getMaintenanceDetails() throws PlatformNotFoundException {\n        try {\n            PlatformService platformService = getServiceAccessor().getPlatformService();\n            MaintenanceDetails.State state = platformService.getPlatform().isMaintenanceEnabled()\n                    ? MaintenanceDetails.State.ENABLED\n                    : MaintenanceDetails.State.DISABLED;\n            SPlatform platform = platformService.getPlatform();\n            return MaintenanceDetailsImpl.builder()\n                    .maintenanceMessage(platform.getMaintenanceMessage())\n                    .maintenanceMessageActive(platform.isMaintenanceMessageActive())\n                    .maintenanceState(state)\n                    .build();\n        } catch (SPlatformNotFoundException e) {\n            throw new PlatformNotFoundException(e.getMessage(), e);\n        }\n    }\n\n    @Override\n    @CustomTransactions\n    public void enableMaintenanceMode() throws UpdateException {\n        try {\n            TenantStateManager tenantStateManager = getServiceAccessor().getTenantStateManager();\n            tenantStateManager.pause();\n        } catch (Exception e) {\n            throw new UpdateException(e);\n        }\n    }\n\n    @Override\n    @CustomTransactions\n    public void disableMaintenanceMode() throws UpdateException {\n        try {\n            TenantStateManager tenantStateManager = getServiceAccessor().getTenantStateManager();\n            tenantStateManager.resume();\n\n            getServiceAccessor().getTransactionService().executeInTransaction(() -> {\n                disableMaintenanceMessage();\n                return null;\n            });\n\n        } catch (Exception e) {\n            throw new UpdateException(e);\n        }\n    }\n\n    @Override\n    public void updateMaintenanceMessage(String message) throws UpdateException {\n        try {\n            PlatformService platformService = getServiceAccessor().getPlatformService();\n            platformService.updatePlatform(getPlatformUpdateBuilder()\n                    .setMaintenanceMessage(message).done());\n        } catch (SPlatformUpdateException e) {\n            throw new UpdateException(e);\n        }\n    }\n\n    @Override\n    public void enableMaintenanceMessage() throws UpdateException {\n        try {\n            PlatformService platformService = getServiceAccessor().getPlatformService();\n            platformService.updatePlatform(getPlatformUpdateBuilder()\n                    .setMaintenanceMessageActive(true).done());\n        } catch (SPlatformUpdateException e) {\n            throw new UpdateException(e);\n        }\n    }\n\n    @Override\n    public void disableMaintenanceMessage() throws UpdateException {\n        try {\n            PlatformService platformService = getServiceAccessor().getPlatformService();\n            platformService.updatePlatform(getPlatformUpdateBuilder()\n                    .setMaintenanceMessageActive(false).done());\n        } catch (SPlatformUpdateException e) {\n            throw new UpdateException(e);\n        }\n    }\n\n    protected SPlatformUpdateBuilder getPlatformUpdateBuilder() {\n        return SPlatformUpdateBuilderImpl.builder()\n                .descriptor(new EntityUpdateDescriptor())\n                .build();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/OrderAndFields.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorCriterion;\nimport org.bonitasoft.engine.bpm.connector.ConnectorImplementationDescriptor;\nimport org.bonitasoft.engine.bpm.document.DocumentCriterion;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion;\nimport org.bonitasoft.engine.bpm.flownode.EventCriterion;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceCriterion;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.core.document.model.SDocument;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAProcessInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SUserTaskInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SEndEventInstanceBuilderFactory;\nimport org.bonitasoft.engine.persistence.OrderAndField;\nimport org.bonitasoft.engine.persistence.OrderByType;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic class OrderAndFields {\n\n    static OrderAndField getOrderAndFieldForConnectorImplementation(final ConnectorCriterion pagingCriterion) {\n        String filed = null;\n        OrderByType orderBy = null;\n        ConnectorCriterion criterion = pagingCriterion;\n        if (criterion == null) {\n            criterion = ConnectorCriterion.DEFAULT;\n        }\n        switch (criterion) {\n            case DEFINITION_ID_ASC:\n                filed = ConnectorImplementationDescriptor.DEFINITION_ID;\n                orderBy = OrderByType.ASC;\n                break;\n            case DEFINITION_ID_DESC:\n                filed = ConnectorImplementationDescriptor.DEFINITION_ID;\n                orderBy = OrderByType.DESC;\n                break;\n            case DEFINITION_VERSION_ASC:\n                filed = ConnectorImplementationDescriptor.DEFINITION_VERSION;\n                orderBy = OrderByType.ASC;\n                break;\n            case DEFINITION_VERSION_DESC:\n                filed = ConnectorImplementationDescriptor.DEFINITION_VERSION;\n                orderBy = OrderByType.DESC;\n                break;\n            case IMPLEMENTATION_VERSION_ASC:\n                filed = ConnectorImplementationDescriptor.VERSIOIN;\n                orderBy = OrderByType.ASC;\n                break;\n            case IMPLEMENTATIONN_VERSION_DESC:\n                filed = ConnectorImplementationDescriptor.VERSIOIN;\n                orderBy = OrderByType.DESC;\n                break;\n            case IMPLEMENTATIONN_CLASS_NAME_ACS:\n                filed = ConnectorImplementationDescriptor.IMPLEMENTATION_CLASS_NAME;\n                orderBy = OrderByType.ASC;\n                break;\n            case IMPLEMENTATIONN_CLASS_NAME_DESC:\n                filed = ConnectorImplementationDescriptor.IMPLEMENTATION_CLASS_NAME;\n                orderBy = OrderByType.DESC;\n                break;\n            case IMPLEMENTATION_ID_DESC:\n                filed = ConnectorImplementationDescriptor.ID;\n                orderBy = OrderByType.DESC;\n                break;\n            case IMPLEMENTATION_ID_ASC:\n            case DEFAULT:\n            default:\n                filed = ConnectorImplementationDescriptor.ID;\n                orderBy = OrderByType.ASC;\n                break;\n\n        }\n        return new OrderAndField(orderBy, filed);\n    }\n\n    static OrderAndField getOrderAndFieldForProcessInstance(final ProcessInstanceCriterion criterion) {\n        final SAProcessInstanceBuilderFactory keyProvider = BuilderFactory.get(SAProcessInstanceBuilderFactory.class);\n        String field = null;\n        OrderByType order = null;\n        switch (criterion) {\n            case STATE_ASC:\n                field = keyProvider.getStateIdKey();\n                order = OrderByType.ASC;\n                break;\n            case STATE_DESC:\n                field = keyProvider.getStateIdKey();\n                order = OrderByType.DESC;\n                break;\n            case ARCHIVE_DATE_ASC:\n                field = keyProvider.getArchiveDateKey();\n                order = OrderByType.ASC;\n                break;\n            case ARCHIVE_DATE_DESC:\n                field = keyProvider.getArchiveDateKey();\n                order = OrderByType.DESC;\n                break;\n            case LAST_UPDATE_DESC:\n                field = keyProvider.getLastUpdateKey();\n                order = OrderByType.DESC;\n                break;\n            case LAST_UPDATE_ASC:\n                field = keyProvider.getLastUpdateKey();\n                order = OrderByType.ASC;\n                break;\n            case CREATION_DATE_ASC:\n                field = keyProvider.getStartDateKey();\n                order = OrderByType.ASC;\n                break;\n            case CREATION_DATE_DESC:\n            case DEFAULT:\n                field = keyProvider.getStartDateKey();\n                order = OrderByType.DESC;\n                break;\n            case NAME_ASC:\n                field = keyProvider.getNameKey();\n                order = OrderByType.ASC;\n                break;\n            case NAME_DESC:\n                field = keyProvider.getNameKey();\n                order = OrderByType.DESC;\n                break;\n            default:\n                break;\n        }\n        return new OrderAndField(order, field);\n    }\n\n    static OrderAndField getOrderAndFieldForEvent(final EventCriterion sortingType) {\n        final SEndEventInstanceBuilderFactory keyProvider = BuilderFactory.get(SEndEventInstanceBuilderFactory.class);\n        OrderByType orderByType = null;\n        String fieldName = null;\n        switch (sortingType) {\n            case NAME_DESC:\n                orderByType = OrderByType.DESC;\n                fieldName = keyProvider.getNameKey();\n                break;\n            default:\n                orderByType = OrderByType.ASC;\n                fieldName = keyProvider.getNameKey();\n                break;\n        }\n        return new OrderAndField(orderByType, fieldName);\n    }\n\n    static OrderAndField getOrderAndFieldForActivityInstance(ActivityInstanceCriterion pagingCriterion) {\n        final SUserTaskInstanceBuilderFactory keyProvider = BuilderFactory.get(SUserTaskInstanceBuilderFactory.class);\n        String field = null;\n        OrderByType order = null;\n        if (pagingCriterion == null) {\n            pagingCriterion = ActivityInstanceCriterion.DEFAULT;\n        }\n        switch (pagingCriterion) {\n            case DEFAULT:\n                field = keyProvider.getPriorityKey();\n                order = OrderByType.DESC;\n                break;\n            case NAME_DESC:\n                field = keyProvider.getNameKey();\n                order = OrderByType.DESC;\n                break;\n            case NAME_ASC:\n                field = keyProvider.getNameKey();\n                order = OrderByType.ASC;\n                break;\n            case LAST_UPDATE_ASC:\n                field = keyProvider.getLastUpdateDateKey();\n                order = OrderByType.ASC;\n                break;\n            case LAST_UPDATE_DESC:\n                field = keyProvider.getLastUpdateDateKey();\n                order = OrderByType.DESC;\n                break;\n            case PRIORITY_ASC:\n                field = keyProvider.getPriorityKey();\n                order = OrderByType.ASC;\n                break;\n            case PRIORITY_DESC:\n                field = keyProvider.getPriorityKey();\n                order = OrderByType.DESC;\n                break;\n            case REACHED_STATE_DATE_ASC:\n                field = keyProvider.getReachStateDateKey();\n                order = OrderByType.ASC;\n                break;\n            case REACHED_STATE_DATE_DESC:\n                field = keyProvider.getReachStateDateKey();\n                order = OrderByType.DESC;\n                break;\n            case EXPECTED_END_DATE_ASC:\n                field = keyProvider.getExpectedEndDateKey();\n                order = OrderByType.ASC;\n                break;\n            case EXPECTED_END_DATE_DESC:\n                field = keyProvider.getExpectedEndDateKey();\n                order = OrderByType.DESC;\n                break;\n            default:\n                break;\n        }\n        return new OrderAndField(order, field);\n    }\n\n    /**\n     * @param pagingCriterion\n     * @param builder\n     * @return\n     */\n    public static OrderAndField getOrderAndFieldForDocument(DocumentCriterion pagingCriterion) {\n        String field;\n        OrderByType order;\n        if (pagingCriterion == null) {\n            pagingCriterion = DocumentCriterion.DEFAULT;\n        }\n        switch (pagingCriterion) {\n            case DEFAULT:\n                field = \"document.\" + SDocument.CREATION_DATE;\n                order = OrderByType.DESC;\n                break;\n            case AUTHOR_ASC:\n                field = \"document.\" + SDocument.AUTHOR;\n                order = OrderByType.ASC;\n                break;\n            case AUTHOR_DESC:\n                field = \"document.\" + SDocument.AUTHOR;\n                order = OrderByType.DESC;\n                break;\n            case FILENAME_ASC:\n                field = \"document.\" + SDocument.FILENAME;\n                order = OrderByType.ASC;\n                break;\n            case FILENAME_DESC:\n                field = \"document.\" + SDocument.FILENAME;\n                order = OrderByType.DESC;\n                break;\n            case MIMETYPE_ASC:\n                field = \"document.\" + SDocument.MIMETYPE;\n                order = OrderByType.ASC;\n                break;\n            case MIMETYPE_DESC:\n                field = \"document.\" + SDocument.MIMETYPE;\n                order = OrderByType.DESC;\n                break;\n            case CREATION_DATE_ASC:\n                field = \"document.\" + SDocument.CREATION_DATE;\n                order = OrderByType.ASC;\n                break;\n            case CREATION_DATE_DESC:\n                field = \"document.\" + SDocument.CREATION_DATE;\n                order = OrderByType.DESC;\n                break;\n            case NAME_ASC:\n                field = SDocument.NAME;\n                order = OrderByType.ASC;\n                break;\n            case NAME_DESC:\n                field = SDocument.NAME;\n                order = OrderByType.DESC;\n                break;\n            case URL_ASC:\n                field = \"document.\" + SDocument.URL;\n                order = OrderByType.ASC;\n                break;\n            case URL_DESC:\n                field = \"document.\" + SDocument.URL;\n                order = OrderByType.DESC;\n                break;\n            default:\n                throw new IllegalStateException();\n        }\n        return new OrderAndField(order, field);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/OrganizationAPIImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.api.impl.resolver.ActorBusinessArchiveArtifactManager;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.comment.api.SCommentService;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.SIdentityException;\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.profile.ProfileService;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.supervisor.mapping.SupervisorMappingService;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@AvailableInMaintenanceMode\npublic class OrganizationAPIImpl {\n\n    private final ServiceAccessor serviceAccessor;\n\n    private final int pageSize;\n\n    public OrganizationAPIImpl(ServiceAccessor serviceAccessor, int pageSize) {\n        this.serviceAccessor = serviceAccessor;\n        this.pageSize = pageSize;\n    }\n\n    public void deleteOrganization() throws DeletionException {\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n        final SCommentService commentService = serviceAccessor.getCommentService();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n\n        try {\n            final QueryOptions queryOptions = new QueryOptions(0, 1);\n            boolean canDeleteOrganization = processInstanceService.getNumberOfProcessInstances(queryOptions) == 0\n                    && activityInstanceService.getNumberOfHumanTasks(queryOptions) == 0\n                    && commentService.getNumberOfComments(queryOptions) == 0;\n            if (canDeleteOrganization) {\n                deleteOrganizationElements(activityInstanceService);\n                updateActorProcessDependenciesForAllActors(serviceAccessor);\n            } else {\n                throw new DeletionException(\n                        \"Can't delete a organization when a process, a human tasks, or a comment is active !!.\");\n            }\n        } catch (final SBonitaException e) {\n            throw new DeletionException(e);\n        }\n    }\n\n    private void deleteOrganizationElements(final ActivityInstanceService activityInstanceService)\n            throws SBonitaException {\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService();\n        final ProfileService profileService = serviceAccessor.getProfileService();\n        final SupervisorMappingService supervisorService = serviceAccessor.getSupervisorService();\n\n        deleteCustomUserInfo(identityService);\n        actorMappingService.deleteAllActorMembers();\n        profileService.deleteAllProfileMembers();\n        activityInstanceService.deleteAllPendingMappings();\n        supervisorService.deleteAllProcessSupervisors();\n        identityService.deleteAllUserMemberships();\n        identityService.deleteAllGroups();\n        identityService.deleteAllRoles();\n        identityService.deleteAllUsers();\n    }\n\n    private void deleteCustomUserInfo(IdentityService identityService) throws SIdentityException {\n        // only definitions will be deleted because values are deleted on cascade from DB\n        List<SCustomUserInfoDefinition> customUserInfoDefinitions;\n        do {\n            // the start index is always zero because the current page will be deleted\n            customUserInfoDefinitions = identityService.getCustomUserInfoDefinitions(0, pageSize);\n            deleteCustomUserInfo(customUserInfoDefinitions, identityService);\n        } while (customUserInfoDefinitions.size() == pageSize);\n    }\n\n    private void deleteCustomUserInfo(List<SCustomUserInfoDefinition> customUserInfoDefinitions,\n            IdentityService identityService) throws SIdentityException {\n        for (SCustomUserInfoDefinition definition : customUserInfoDefinitions) {\n            identityService.deleteCustomUserInfoDefinition(definition.getId());\n        }\n\n    }\n\n    /**\n     * Check / update process resolution information, for all processes in a list of actor IDs.\n     */\n    private void updateActorProcessDependenciesForAllActors(final ServiceAccessor serviceAccessor)\n            throws SBonitaException {\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        List<Long> processDefinitionIds;\n        final ActorBusinessArchiveArtifactManager dependencyResolver = new ActorBusinessArchiveArtifactManager(\n                serviceAccessor.getActorMappingService(), serviceAccessor.getIdentityService());\n        do {\n            processDefinitionIds = processDefinitionService.getProcessDefinitionIds(0, 100);\n            for (final Long processDefinitionId : processDefinitionIds) {\n                serviceAccessor.getBusinessArchiveArtifactsManager().resolveDependencies(processDefinitionId,\n                        serviceAccessor, dependencyResolver);\n            }\n        } while (processDefinitionIds.size() == 100);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/PageAPIImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Properties;\n\nimport org.bonitasoft.engine.api.PageAPI;\nimport org.bonitasoft.engine.api.impl.page.PageAPIDelegate;\nimport org.bonitasoft.engine.commons.exceptions.SExecutionException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.exception.*;\nimport org.bonitasoft.engine.page.*;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.service.ModelConvertor;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.ServiceAccessorSingleton;\n\n@AvailableInMaintenanceMode\npublic class PageAPIImpl implements PageAPI {\n\n    protected ServiceAccessor getServiceAccessor() {\n        try {\n            return ServiceAccessorSingleton.getInstance();\n        } catch (final Exception e) {\n            throw new BonitaRuntimeException(e);\n        }\n    }\n\n    protected PageAPIDelegate getPageAPIDelegate() {\n        return PageAPIDelegate.getInstance();\n    }\n\n    @Override\n    public Page getPage(final long pageId) throws PageNotFoundException {\n        return getPageAPIDelegate().getPage(pageId);\n    }\n\n    @Override\n    public byte[] getPageContent(final long pageId) throws PageNotFoundException {\n        return getPageAPIDelegate().getPageContent(pageId);\n    }\n\n    @Override\n    public SearchResult<Page> searchPages(final SearchOptions searchOptions) throws SearchException {\n        return getPageAPIDelegate().searchPages(searchOptions);\n    }\n\n    @Override\n    public Page createPage(final PageCreator pageCreator, final byte[] content) throws CreationException {\n        return getPageAPIDelegate().createPage(pageCreator, content, SessionInfos.getUserIdFromSession());\n    }\n\n    @Override\n    public Page createPage(final String contentName, final byte[] content) throws CreationException {\n        return getPageAPIDelegate().createPage(contentName, content, SessionInfos.getUserIdFromSession());\n    }\n\n    @Override\n    public void deletePage(final long pageId) throws DeletionException {\n        getPageAPIDelegate().deletePage(pageId);\n    }\n\n    @Override\n    public void deletePages(final List<Long> pageIds) throws DeletionException {\n        getPageAPIDelegate().deletePages(pageIds);\n    }\n\n    @Override\n    public Page getPageByName(final String name) throws PageNotFoundException {\n        return getPageAPIDelegate().getPageByName(name);\n    }\n\n    @Override\n    public Page getPageByNameAndProcessDefinitionId(String name, long processDefinitionId)\n            throws PageNotFoundException {\n        return getPageAPIDelegate().getPageByNameAndProcessDefinition(name, processDefinitionId);\n    }\n\n    @Override\n    public Page updatePage(final long pageId, final PageUpdater pageUpdater)\n            throws UpdateException, AlreadyExistsException {\n        return getPageAPIDelegate().updatePage(pageId, pageUpdater, SessionInfos.getUserIdFromSession());\n    }\n\n    @Override\n    public void updatePageContent(final long pageId, final byte[] content) throws UpdateException {\n        getPageAPIDelegate().updatePageContent(pageId, content, SessionInfos.getUserIdFromSession());\n    }\n\n    @Override\n    public Properties getPageProperties(final byte[] content, final boolean checkIfItAlreadyExists)\n            throws InvalidPageTokenException, AlreadyExistsException,\n            InvalidPageZipMissingPropertiesException, InvalidPageZipMissingIndexException,\n            InvalidPageZipInconsistentException,\n            InvalidPageZipMissingAPropertyException {\n        return getPageAPIDelegate().getPageProperties(content, checkIfItAlreadyExists);\n    }\n\n    @Override\n    public PageURL resolvePageOrURL(String key, Map<String, Serializable> context, boolean executeAuthorizationRules)\n            throws NotFoundException, ExecutionException, UnauthorizedAccessException {\n        final PageMappingService pageMappingService = retrievePageMappingService();\n        try {\n            return ModelConvertor.toPageURL(\n                    pageMappingService.resolvePageURL(pageMappingService.get(key), context, executeAuthorizationRules));\n        } catch (final SObjectNotFoundException e) {\n            throw new NotFoundException(e);\n        } catch (final SBonitaReadException e) {\n            throw new RetrieveException(e);\n        } catch (final SExecutionException e) {\n            throw new ExecutionException(e);\n        } catch (final SAuthorizationException e) {\n            throw new UnauthorizedAccessException(e);\n        }\n    }\n\n    PageMappingService retrievePageMappingService() {\n        return getServiceAccessor().getPageMappingService();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/PermissionAPIImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport java.util.Set;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.api.PermissionAPI;\nimport org.bonitasoft.engine.api.permission.APICallContext;\nimport org.bonitasoft.engine.commons.exceptions.SExecutionException;\nimport org.bonitasoft.engine.exception.ExecutionException;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.ServiceAccessorSingleton;\n\n/**\n * @author Baptiste Mesta\n */\n@Slf4j\n@AvailableInMaintenanceMode\npublic class PermissionAPIImpl implements PermissionAPI {\n\n    @Override\n    public boolean isAuthorized(APICallContext apiCallContext) throws ExecutionException {\n        ServiceAccessor serviceAccessor = getServiceAccessor();\n        try {\n            return serviceAccessor.getPermissionService().isAuthorized(apiCallContext);\n        } catch (SExecutionException e) {\n            throw new ExecutionException(e);\n        }\n    }\n\n    @Override\n    public Set<String> getResourcePermissions(String resourceKey) {\n        ServiceAccessor serviceAccessor = getServiceAccessor();\n        return serviceAccessor.getPermissionService().getResourcePermissions(resourceKey);\n    }\n\n    ServiceAccessor getServiceAccessor() {\n        return ServiceAccessorSingleton.getInstance();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/PlatformAPIImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport java.io.IOException;\nimport java.util.Map;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.api.PlatformAPI;\nimport org.bonitasoft.engine.api.impl.transaction.CustomTransactions;\nimport org.bonitasoft.engine.api.impl.transaction.platform.GetPlatformContent;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.exception.BonitaHomeConfigurationException;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.home.BonitaHomeServer;\nimport org.bonitasoft.engine.platform.Platform;\nimport org.bonitasoft.engine.platform.PlatformManager;\nimport org.bonitasoft.engine.platform.PlatformNotFoundException;\nimport org.bonitasoft.engine.platform.PlatformService;\nimport org.bonitasoft.engine.platform.PlatformState;\nimport org.bonitasoft.engine.platform.StartNodeException;\nimport org.bonitasoft.engine.platform.StopNodeException;\nimport org.bonitasoft.engine.platform.model.SPlatform;\nimport org.bonitasoft.engine.service.ModelConvertor;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.impl.ServiceAccessorFactory;\n\n/**\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n * @author Lu Kai\n * @author Zhang Bole\n * @author Yanyan Liu\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n */\n@Slf4j\npublic class PlatformAPIImpl implements PlatformAPI {\n\n    public PlatformAPIImpl() {\n        super();\n    }\n\n    @Override\n    @CustomTransactions\n    @AvailableOnStoppedNode\n    public void initializePlatform() {\n        //nothing to do\n    }\n\n    protected ServiceAccessor getServiceAccessor()\n            throws BonitaHomeNotSetException, IOException, BonitaHomeConfigurationException,\n            ReflectiveOperationException {\n        return ServiceAccessorFactory.getInstance().createServiceAccessor();\n    }\n\n    @Override\n    @CustomTransactions\n    @AvailableOnStoppedNode\n    public void startNode() throws StartNodeException {\n        PlatformManager platformManager;\n        try {\n            platformManager = getServiceAccessor().getPlatformManager();\n        } catch (final Exception e) {\n            throw new StartNodeException(e);\n        }\n        boolean isStarted;\n        try {\n            isStarted = platformManager.start();\n        } catch (final Exception e) {\n            throw new StartNodeException(\"Platform starting failed.\", e);\n        }\n        if (!isStarted) {\n            throw new StartNodeException(\n                    \"Platform is in state \" + platformManager.getState() + \" and cannot be started\");\n        }\n    }\n\n    @Override\n    @CustomTransactions\n    @AvailableOnStoppedNode\n    public void stopNode() throws StopNodeException {\n        try {\n            getServiceAccessor().getPlatformManager().stop();\n        } catch (final StopNodeException e) {\n            throw e;\n        } catch (final Exception e) {\n            throw new StopNodeException(e);\n        }\n    }\n\n    @Override\n    @AvailableOnStoppedNode\n    public Platform getPlatform() throws PlatformNotFoundException {\n        ServiceAccessor platformAccessor;\n        try {\n            platformAccessor = getServiceAccessor();\n        } catch (final Exception e) {\n            throw new PlatformNotFoundException(e);\n        }\n        final PlatformService platformService = platformAccessor.getPlatformService();\n        final GetPlatformContent transactionContent = new GetPlatformContent(platformService);\n        try {\n            transactionContent.execute();\n        } catch (final SBonitaException e) {\n            throw new PlatformNotFoundException(e);\n        }\n        final SPlatform sPlatform = transactionContent.getResult();\n        return ModelConvertor.toPlatform(sPlatform, platformAccessor.getPlatformService().getSPlatformProperties());\n    }\n\n    @Override\n    @CustomTransactions\n    @AvailableOnStoppedNode\n    public boolean isPlatformCreated() {\n        try {\n            final ServiceAccessor serviceAccessor = getServiceAccessor();\n            final PlatformService platformService = serviceAccessor.getPlatformService();\n            return serviceAccessor.getTransactionService().executeInTransaction(platformService::isPlatformCreated);\n        } catch (Exception e) {\n            throw new BonitaRuntimeException(e);\n        }\n    }\n\n    @Override\n    @CustomTransactions\n    @AvailableOnStoppedNode\n    public PlatformState getPlatformState() {\n        try {\n            return getServiceAccessor().getPlatformManager().getState();\n        } catch (Exception e) {\n            throw new IllegalStateException(e);\n        }\n    }\n\n    /**\n     * @return true if the current node is started, false otherwise\n     */\n    @Override\n    @AvailableOnStoppedNode\n    public boolean isNodeStarted() {\n        return getPlatformState() == PlatformState.STARTED;\n    }\n\n    @Override\n    public void rescheduleErroneousTriggers() throws UpdateException {\n        try {\n            getServiceAccessor().getSchedulerService().rescheduleErroneousTriggers();\n        } catch (final Exception e) {\n            throw new UpdateException(e);\n        }\n    }\n\n    @Override\n    public Map<String, byte[]> getClientPlatformConfigurations() {\n        return getBonitaHomeServer().getClientPlatformConfigurations();\n    }\n\n    @Override\n    public Map<String, byte[]> getClientTenantConfigurations() {\n        return getBonitaHomeServer().getTenantPortalConfigurations();\n    }\n\n    @Override\n    public byte[] getClientTenantConfiguration(String file) {\n        return getBonitaHomeServer().getTenantPortalConfiguration(file);\n    }\n\n    protected BonitaHomeServer getBonitaHomeServer() {\n        return BonitaHomeServer.getInstance();\n    }\n\n    @Override\n    public void updateClientTenantConfigurationFile(String file, byte[] content) throws UpdateException {\n        getBonitaHomeServer().updateTenantPortalConfigurationFile(file, content);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/PlatformCommandAPIImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.*;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.PlatformCommandAPI;\nimport org.bonitasoft.engine.api.impl.transaction.platform.DeleteSPlatformCommand;\nimport org.bonitasoft.engine.api.impl.transaction.platform.GetSPlatformCommands;\nimport org.bonitasoft.engine.api.impl.transaction.platform.UpdateSPlatformCommand;\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.classloader.SClassLoaderException;\nimport org.bonitasoft.engine.command.CommandCriterion;\nimport org.bonitasoft.engine.command.CommandDescriptor;\nimport org.bonitasoft.engine.command.CommandExecutionException;\nimport org.bonitasoft.engine.command.CommandNotFoundException;\nimport org.bonitasoft.engine.command.CommandParameterizationException;\nimport org.bonitasoft.engine.command.CommandUpdater;\nimport org.bonitasoft.engine.command.DependencyNotFoundException;\nimport org.bonitasoft.engine.command.RuntimeCommand;\nimport org.bonitasoft.engine.command.SCommandNotFoundException;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.dependency.DependencyService;\nimport org.bonitasoft.engine.dependency.SDependencyException;\nimport org.bonitasoft.engine.dependency.SDependencyNotFoundException;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.RetrieveException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.platform.command.PlatformCommandService;\nimport org.bonitasoft.engine.platform.command.SPlatformCommandGettingException;\nimport org.bonitasoft.engine.platform.command.SPlatformCommandNotFoundException;\nimport org.bonitasoft.engine.platform.command.model.SPlatformCommand;\nimport org.bonitasoft.engine.service.ModelConvertor;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.impl.ServiceAccessorFactory;\n\n/**\n * @author Matthieu Chaffotte\n * @author Zhang Bole\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n */\npublic class PlatformCommandAPIImpl implements PlatformCommandAPI {\n\n    private static ServiceAccessor getServiceAccessor() throws RetrieveException {\n        try {\n            return ServiceAccessorFactory.getInstance().createServiceAccessor();\n        } catch (final Exception e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public void addDependency(final String name, final byte[] jar) throws CreationException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final DependencyService dependencyService = serviceAccessor.getPlatformDependencyService();\n        final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService();\n        try {\n            dependencyService.createMappedDependency(name, jar, name, GLOBAL_ID, GLOBAL_TYPE);\n            classLoaderService.refreshClassLoaderAfterUpdate(GLOBAL);\n        } catch (SDependencyException | SClassLoaderException e) {\n            throw new CreationException(e);\n        }\n    }\n\n    @Override\n    public void removeDependency(final String name) throws DependencyNotFoundException, DeletionException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final DependencyService dependencyService = serviceAccessor.getPlatformDependencyService();\n        ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService();\n\n        try {\n            dependencyService.deleteDependency(name);\n            classLoaderService.refreshClassLoaderAfterUpdate(GLOBAL);\n        } catch (final SDependencyNotFoundException e) {\n            throw new DependencyNotFoundException(e);\n        } catch (final SBonitaException e) {\n            throw new DeletionException(e);\n        }\n    }\n\n    @Override\n    public CommandDescriptor register(final String name, final String description, final String implementation)\n            throws CreationException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final PlatformCommandService platformCommandService = serviceAccessor.getPlatformCommandService();\n        try {\n            platformCommandService.getPlatformCommand(name);\n            throw new AlreadyExistsException(\"A command with name \\\"\" + name + \"\\\" already exists\");\n        } catch (SPlatformCommandNotFoundException ignored) {\n        } catch (SPlatformCommandGettingException e) {\n            throw new CreationException(\"Unable to create the platform command\", e);\n        }\n        final SPlatformCommand sPlatformCommand = new SPlatformCommand(name, description, implementation);\n        try {\n            platformCommandService.create(sPlatformCommand);\n            return ModelConvertor.toCommandDescriptor(sPlatformCommand);\n        } catch (final SBonitaException e) {\n            throw new CreationException(e);\n        }\n    }\n\n    @Override\n    public Serializable execute(final String platformCommandName, final Map<String, Serializable> parameters)\n            throws CommandNotFoundException,\n            CommandParameterizationException, CommandExecutionException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final PlatformCommandService platformCommandService = serviceAccessor.getPlatformCommandService();\n        try {\n            SPlatformCommand sPlatformCommand = platformCommandService.getPlatformCommand(platformCommandName);\n            final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n            RuntimeCommand command = (RuntimeCommand) contextClassLoader\n                    .loadClass(sPlatformCommand.getImplementation()).newInstance();\n            return command.execute(parameters, serviceAccessor);\n        } catch (final SPlatformCommandNotFoundException e) {\n            throw new CommandNotFoundException(e);\n        } catch (final ClassNotFoundException | InstantiationException | IllegalAccessException e) {\n            throw new CommandParameterizationException(e);\n        } catch (final SBonitaException e) {\n            throw new CommandExecutionException(e);\n        }\n    }\n\n    @Override\n    public void unregister(final String platformCommandName) throws CommandNotFoundException, DeletionException {\n        if (platformCommandName == null) {\n            throw new DeletionException(\"Command name can not be null!\");\n        }\n        try {\n            final ServiceAccessor serviceAccessor = getServiceAccessor();\n            final PlatformCommandService platformCommandService = serviceAccessor.getPlatformCommandService();\n\n            final DeleteSPlatformCommand deletePlatformCommand = new DeleteSPlatformCommand(platformCommandService,\n                    platformCommandName);\n            deletePlatformCommand.execute();\n        } catch (final SCommandNotFoundException scnfe) {\n            throw new CommandNotFoundException(scnfe);\n        } catch (final SBonitaException sbe) {\n            throw new DeletionException(sbe);\n        }\n    }\n\n    @Override\n    public CommandDescriptor getCommand(final String platformCommandName) throws CommandNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final PlatformCommandService platformCommandService = serviceAccessor.getPlatformCommandService();\n        try {\n            return ModelConvertor.toCommandDescriptor(platformCommandService.getPlatformCommand(platformCommandName));\n        } catch (final SBonitaException e) {\n            throw new CommandNotFoundException(e);\n        }\n    }\n\n    @Override\n    public List<CommandDescriptor> getCommands(final int startIndex, final int maxResults,\n            final CommandCriterion sort) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final PlatformCommandService platformCommandService = serviceAccessor.getPlatformCommandService();\n\n        try {\n            final GetSPlatformCommands getPlatformCommands = new GetSPlatformCommands(platformCommandService,\n                    startIndex, maxResults, sort);\n            getPlatformCommands.execute();\n            return ModelConvertor.toPlatformCommandDescriptors(getPlatformCommands.getResult());\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public void update(final String platformCommandName, final CommandUpdater updater) throws UpdateException {\n        if (updater == null || updater.getFields().isEmpty()) {\n            throw new UpdateException(\"The update descriptor does not contain field updates\");\n        }\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final PlatformCommandService platformCommandService = serviceAccessor.getPlatformCommandService();\n\n        try {\n            final UpdateSPlatformCommand updatePlatformCommand = new UpdateSPlatformCommand(platformCommandService,\n                    platformCommandName, updater);\n            updatePlatformCommand.execute();\n        } catch (final SCommandNotFoundException scnfe) {\n            throw new UpdateException(scnfe);\n        } catch (final SBonitaException e) {\n            throw new UpdateException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/PlatformLoginAPIImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.api.PlatformLoginAPI;\nimport org.bonitasoft.engine.api.impl.transaction.CustomTransactions;\nimport org.bonitasoft.engine.core.platform.login.PlatformLoginService;\nimport org.bonitasoft.engine.core.platform.login.SInvalidPlatformCredentialsException;\nimport org.bonitasoft.engine.core.platform.login.SPlatformLoginException;\nimport org.bonitasoft.engine.platform.InvalidPlatformCredentialsException;\nimport org.bonitasoft.engine.platform.PlatformLoginException;\nimport org.bonitasoft.engine.platform.PlatformLogoutException;\nimport org.bonitasoft.engine.platform.session.SSessionNotFoundException;\nimport org.bonitasoft.engine.platform.session.model.SPlatformSession;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.impl.ServiceAccessorFactory;\nimport org.bonitasoft.engine.session.PlatformSession;\nimport org.bonitasoft.engine.session.SessionNotFoundException;\nimport org.bonitasoft.engine.session.impl.PlatformSessionImpl;\n\n/**\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n */\npublic class PlatformLoginAPIImpl implements PlatformLoginAPI {\n\n    @Override\n    @CustomTransactions\n    @AvailableOnStoppedNode\n    public PlatformSession login(final String userName, final String password) throws PlatformLoginException {\n        ServiceAccessor serviceAccessor;\n        try {\n            serviceAccessor = ServiceAccessorFactory.getInstance().createServiceAccessor();\n        } catch (final Exception e) {\n            throw new PlatformLoginException(e);\n        }\n        final PlatformLoginService platformLoginService = serviceAccessor.getPlatformLoginService();\n\n        final SPlatformSession platformSession;\n        try {\n            platformSession = platformLoginService.login(userName, password);\n        } catch (SPlatformLoginException e) {\n            throw new PlatformLoginException(e);\n        } catch (SInvalidPlatformCredentialsException ignored) {\n            throw new InvalidPlatformCredentialsException(\"Wrong username of password\");\n        }\n        final long id = platformSession.getId();\n        final Date creationDate = platformSession.getCreationDate();\n        final long duration = platformSession.getDuration();\n        final long userId = platformSession.getUserId();\n        return new PlatformSessionImpl(id, creationDate, duration, userName, userId);\n    }\n\n    @Override\n    @CustomTransactions\n    @AvailableOnStoppedNode\n    public void logout(final PlatformSession session) throws PlatformLogoutException, SessionNotFoundException {\n        ServiceAccessor serviceAccessor;\n        try {\n            serviceAccessor = ServiceAccessorFactory.getInstance().createServiceAccessor();\n        } catch (final Exception e) {\n            throw new PlatformLogoutException(e);\n        }\n        final PlatformLoginService platformLoginService = serviceAccessor.getPlatformLoginService();\n        try {\n            platformLoginService.logout(session.getId());\n        } catch (final SSessionNotFoundException e) {\n            throw new SessionNotFoundException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/ProcessAPIImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport static java.nio.charset.StandardCharsets.UTF_8;\nimport static java.util.Collections.singletonMap;\nimport static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier;\nimport static org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance.EXECUTION_DATE;\nimport static org.bonitasoft.engine.search.AbstractSearchEntity.search;\n\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.concurrent.Callable;\nimport java.util.stream.Collectors;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.io.FileUtils;\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.actor.mapping.SActorNotFoundException;\nimport org.bonitasoft.engine.actor.mapping.model.SActor;\nimport org.bonitasoft.engine.actor.mapping.model.SActorMember;\nimport org.bonitasoft.engine.actor.mapping.model.SActorUpdateBuilder;\nimport org.bonitasoft.engine.actor.mapping.model.SActorUpdateBuilderFactory;\nimport org.bonitasoft.engine.api.DocumentAPI;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.impl.connector.ConnectorReseter;\nimport org.bonitasoft.engine.api.impl.connector.ResetAllFailedConnectorStrategy;\nimport org.bonitasoft.engine.api.impl.flownode.FlowNodeRetrier;\nimport org.bonitasoft.engine.api.impl.transaction.CustomTransactions;\nimport org.bonitasoft.engine.api.impl.transaction.activity.GetArchivedActivityInstance;\nimport org.bonitasoft.engine.api.impl.transaction.activity.GetArchivedActivityInstances;\nimport org.bonitasoft.engine.api.impl.transaction.activity.GetNumberOfActivityInstance;\nimport org.bonitasoft.engine.api.impl.transaction.actor.*;\nimport org.bonitasoft.engine.api.impl.transaction.category.*;\nimport org.bonitasoft.engine.api.impl.transaction.connector.GetConnectorImplementation;\nimport org.bonitasoft.engine.api.impl.transaction.event.GetEventInstances;\nimport org.bonitasoft.engine.api.impl.transaction.expression.EvaluateExpressionsDefinitionLevel;\nimport org.bonitasoft.engine.api.impl.transaction.expression.EvaluateExpressionsInstanceLevel;\nimport org.bonitasoft.engine.api.impl.transaction.expression.EvaluateExpressionsInstanceLevelAndArchived;\nimport org.bonitasoft.engine.api.impl.transaction.flownode.SetExpectedEndDate;\nimport org.bonitasoft.engine.api.impl.transaction.identity.GetSUser;\nimport org.bonitasoft.engine.api.impl.transaction.process.*;\nimport org.bonitasoft.engine.api.impl.transaction.task.*;\nimport org.bonitasoft.engine.archive.ArchiveService;\nimport org.bonitasoft.engine.bpm.actor.*;\nimport org.bonitasoft.engine.bpm.actor.ActorUpdater.ActorField;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveFactory;\nimport org.bonitasoft.engine.bpm.bar.InvalidBusinessArchiveFormatException;\nimport org.bonitasoft.engine.bpm.category.Category;\nimport org.bonitasoft.engine.bpm.category.CategoryCriterion;\nimport org.bonitasoft.engine.bpm.category.CategoryNotFoundException;\nimport org.bonitasoft.engine.bpm.category.CategoryUpdater;\nimport org.bonitasoft.engine.bpm.category.CategoryUpdater.CategoryField;\nimport org.bonitasoft.engine.bpm.comment.ArchivedComment;\nimport org.bonitasoft.engine.bpm.comment.Comment;\nimport org.bonitasoft.engine.bpm.connector.*;\nimport org.bonitasoft.engine.bpm.contract.ContractDefinition;\nimport org.bonitasoft.engine.bpm.contract.ContractViolationException;\nimport org.bonitasoft.engine.bpm.contract.validation.ContractValidator;\nimport org.bonitasoft.engine.bpm.contract.validation.ContractValidatorFactory;\nimport org.bonitasoft.engine.bpm.data.*;\nimport org.bonitasoft.engine.bpm.document.*;\nimport org.bonitasoft.engine.bpm.flownode.*;\nimport org.bonitasoft.engine.bpm.parameter.ParameterCriterion;\nimport org.bonitasoft.engine.bpm.parameter.ParameterInstance;\nimport org.bonitasoft.engine.bpm.process.*;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessInstanceUpdater;\nimport org.bonitasoft.engine.bpm.supervisor.ProcessSupervisor;\nimport org.bonitasoft.engine.bpm.supervisor.ProcessSupervisorSearchDescriptor;\nimport org.bonitasoft.engine.bpm.supervisor.SupervisorNotFoundException;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.classloader.SClassLoaderException;\nimport org.bonitasoft.engine.commons.ExceptionUtils;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContent;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.core.category.CategoryService;\nimport org.bonitasoft.engine.core.category.exception.SCategoryAlreadyExistsException;\nimport org.bonitasoft.engine.core.category.exception.SCategoryInProcessAlreadyExistsException;\nimport org.bonitasoft.engine.core.category.exception.SCategoryNotFoundException;\nimport org.bonitasoft.engine.core.category.model.SCategory;\nimport org.bonitasoft.engine.core.category.model.SProcessCategoryMapping;\nimport org.bonitasoft.engine.core.category.model.builder.SCategoryUpdateBuilder;\nimport org.bonitasoft.engine.core.category.model.builder.SCategoryUpdateBuilderFactory;\nimport org.bonitasoft.engine.core.category.model.builder.SProcessCategoryMappingBuilderFactory;\nimport org.bonitasoft.engine.core.connector.ConnectorInstanceService;\nimport org.bonitasoft.engine.core.connector.ConnectorResult;\nimport org.bonitasoft.engine.core.connector.ConnectorService;\nimport org.bonitasoft.engine.core.connector.exception.SConnectorException;\nimport org.bonitasoft.engine.core.connector.parser.SConnectorImplementationDescriptor;\nimport org.bonitasoft.engine.core.contract.data.ContractDataService;\nimport org.bonitasoft.engine.core.contract.data.SContractDataNotFoundException;\nimport org.bonitasoft.engine.core.data.instance.TransientDataService;\nimport org.bonitasoft.engine.core.document.api.DocumentService;\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.filter.FilterResult;\nimport org.bonitasoft.engine.core.filter.UserFilterService;\nimport org.bonitasoft.engine.core.filter.exception.SUserFilterExecutionException;\nimport org.bonitasoft.engine.core.operation.OperationService;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.core.process.comment.api.SCommentNotFoundException;\nimport org.bonitasoft.engine.core.process.comment.api.SCommentService;\nimport org.bonitasoft.engine.core.process.comment.model.SComment;\nimport org.bonitasoft.engine.core.process.comment.model.archive.SAComment;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.core.process.definition.model.*;\nimport org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SThrowMessageEventTriggerDefinitionBuilder;\nimport org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SThrowMessageEventTriggerDefinitionBuilderFactory;\nimport org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SThrowSignalEventTriggerDefinitionBuilderFactory;\nimport org.bonitasoft.engine.core.process.definition.model.event.SBoundaryEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SCatchEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SIntermediateCatchEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SStartEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowMessageEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowSignalEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.*;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceDeletionException;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.model.*;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAProcessInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SAutomaticTaskInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.event.SCatchEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance;\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\nimport org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilder;\nimport org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilderFactory;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceService;\nimport org.bonitasoft.engine.data.instance.api.ParentContainerResolver;\nimport org.bonitasoft.engine.data.instance.exception.SDataInstanceException;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.SADataInstance;\nimport org.bonitasoft.engine.dependency.model.ScopeType;\nimport org.bonitasoft.engine.exception.*;\nimport org.bonitasoft.engine.execution.ProcessInstanceInterruptor;\nimport org.bonitasoft.engine.execution.SUnreleasableTaskException;\nimport org.bonitasoft.engine.execution.event.EventsHandler;\nimport org.bonitasoft.engine.execution.job.JobNameBuilder;\nimport org.bonitasoft.engine.execution.state.FlowNodeStateManager;\nimport org.bonitasoft.engine.execution.work.BPMWorkFactory;\nimport org.bonitasoft.engine.expression.*;\nimport org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.form.FormMapping;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.identity.UserNotFoundException;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.job.FailedJob;\nimport org.bonitasoft.engine.lock.BonitaLock;\nimport org.bonitasoft.engine.lock.LockService;\nimport org.bonitasoft.engine.lock.SLockException;\nimport org.bonitasoft.engine.lock.SLockTimeoutException;\nimport org.bonitasoft.engine.log.LogMessageBuilder;\nimport org.bonitasoft.engine.mdc.FlowNodeInstanceMDC;\nimport org.bonitasoft.engine.message.MessagesHandlingService;\nimport org.bonitasoft.engine.operation.LeftOperand;\nimport org.bonitasoft.engine.operation.Operation;\nimport org.bonitasoft.engine.operation.OperationBuilder;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.OrderAndField;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.resources.BARResourceType;\nimport org.bonitasoft.engine.resources.SBARResource;\nimport org.bonitasoft.engine.scheduler.JobService;\nimport org.bonitasoft.engine.scheduler.SchedulerService;\nimport org.bonitasoft.engine.scheduler.exception.SSchedulerException;\nimport org.bonitasoft.engine.scheduler.model.SFailedJob;\nimport org.bonitasoft.engine.scheduler.model.SJobParameter;\nimport org.bonitasoft.engine.search.*;\nimport org.bonitasoft.engine.search.activity.SearchActivityInstances;\nimport org.bonitasoft.engine.search.activity.SearchArchivedActivityInstances;\nimport org.bonitasoft.engine.search.comment.SearchArchivedComments;\nimport org.bonitasoft.engine.search.comment.SearchComments;\nimport org.bonitasoft.engine.search.comment.SearchCommentsInvolvingUser;\nimport org.bonitasoft.engine.search.comment.SearchCommentsManagedBy;\nimport org.bonitasoft.engine.search.connector.SearchArchivedConnectorInstance;\nimport org.bonitasoft.engine.search.descriptor.*;\nimport org.bonitasoft.engine.search.events.trigger.SearchTimerEventTriggerInstances;\nimport org.bonitasoft.engine.search.flownode.SearchArchivedFlowNodeInstances;\nimport org.bonitasoft.engine.search.flownode.SearchFlowNodeInstances;\nimport org.bonitasoft.engine.search.identity.SearchUsersWhoCanExecutePendingHumanTaskDeploymentInfo;\nimport org.bonitasoft.engine.search.identity.SearchUsersWhoCanStartProcessDeploymentInfo;\nimport org.bonitasoft.engine.search.impl.SearchFilter;\nimport org.bonitasoft.engine.search.impl.SearchResultImpl;\nimport org.bonitasoft.engine.search.process.*;\nimport org.bonitasoft.engine.search.supervisor.SearchArchivedHumanTasksSupervisedBy;\nimport org.bonitasoft.engine.search.supervisor.SearchProcessDeploymentInfosSupervised;\nimport org.bonitasoft.engine.search.supervisor.SearchSupervisors;\nimport org.bonitasoft.engine.search.task.SearchArchivedTasks;\nimport org.bonitasoft.engine.search.task.SearchArchivedTasksManagedBy;\nimport org.bonitasoft.engine.service.ModelConvertor;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.ServiceAccessorSingleton;\nimport org.bonitasoft.engine.session.model.SSession;\nimport org.bonitasoft.engine.supervisor.mapping.SSupervisorDeletionException;\nimport org.bonitasoft.engine.supervisor.mapping.SSupervisorNotFoundException;\nimport org.bonitasoft.engine.supervisor.mapping.SupervisorMappingService;\nimport org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.bonitasoft.engine.work.WorkDescriptor;\nimport org.bonitasoft.engine.work.WorkService;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Yanyan Liu\n * @author Elias Ricken de Medeiros\n * @author Zhao Na\n * @author Zhang Bole\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n * @author Arthur Freycon\n * @author Haroun EL ALAMI\n */\n@Slf4j\npublic class ProcessAPIImpl implements ProcessAPI {\n\n    private static final int BATCH_SIZE = 500;\n\n    private static final String CONTAINER_TYPE_PROCESS_INSTANCE = \"PROCESS_INSTANCE\";\n\n    private static final String CONTAINER_TYPE_ACTIVITY_INSTANCE = \"ACTIVITY_INSTANCE\";\n\n    private static final String PENDING_OR_ASSIGNED = \"PendingOrAssigned\";\n\n    private static final String PENDING_OR_ASSIGNED_OR_ASSIGNED_TO_OTHERS = \"PendingOrAssignedOrAssignedToOthers\";\n\n    protected final ProcessConfigurationAPIImpl processConfigurationAPI;\n    private final ProcessManagementAPIImplDelegate processManagementAPIImplDelegate;\n    private final DocumentAPI documentAPI;\n    private final TaskInvolvementDelegate taskInvolvementDelegate;\n    private final ProcessInvolvementDelegate processInvolvementDelegate;\n    private final ProcessDeploymentAPIDelegate processDeploymentAPIDelegate;\n\n    public ProcessAPIImpl() {\n        this(new ProcessManagementAPIImplDelegate(), new DocumentAPIImpl(), new ProcessConfigurationAPIImpl(),\n                new TaskInvolvementDelegate(), new ProcessInvolvementDelegate());\n    }\n\n    public ProcessAPIImpl(final ProcessManagementAPIImplDelegate processManagementAPIDelegate,\n            final DocumentAPI documentAPI,\n            ProcessConfigurationAPIImpl processConfigurationAPI, TaskInvolvementDelegate taskInvolvementDelegate,\n            ProcessInvolvementDelegate processInvolvementDelegate) {\n        this.processManagementAPIImplDelegate = processManagementAPIDelegate;\n        this.documentAPI = documentAPI;\n        this.processConfigurationAPI = processConfigurationAPI;\n        this.taskInvolvementDelegate = taskInvolvementDelegate;\n        this.processInvolvementDelegate = processInvolvementDelegate;\n        this.processDeploymentAPIDelegate = ProcessDeploymentAPIDelegate.getInstance();\n    }\n\n    @Override\n    public SearchResult<HumanTaskInstance> searchHumanTaskInstances(final SearchOptions searchOptions)\n            throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager();\n        return AbstractHumanTaskInstanceSearchEntity\n                .searchHumanTaskInstance(searchEntitiesDescriptor.getSearchHumanTaskInstanceDescriptor(),\n                        searchOptions,\n                        flowNodeStateManager,\n                        activityInstanceService::getNumberOfHumanTasks,\n                        activityInstanceService::searchHumanTasks)\n                .search();\n    }\n\n    @Override\n    public void deleteProcessDefinition(final long processDefinitionId) throws DeletionException {\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 1);\n        builder.filter(ProcessInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDefinitionId);\n        final SearchOptions searchOptions = builder.done();\n        try {\n            final boolean hasOpenProcessInstances = searchProcessInstances(getServiceAccessor(), searchOptions)\n                    .getCount() > 0;\n            checkIfItIsPossibleToDeleteProcessInstance(processDefinitionId, hasOpenProcessInstances);\n            final boolean hasArchivedProcessInstances = searchArchivedProcessInstancesInAllStates(searchOptions)\n                    .getCount() > 0;\n            checkIfItIsPossibleToDeleteProcessInstance(processDefinitionId, hasArchivedProcessInstances);\n\n            removeAllCategoriesFromProcessDefinition(processDefinitionId);\n            deleteAllSupervisorsOfProcess(processDefinitionId);\n\n            processManagementAPIImplDelegate.deleteProcessDefinition(processDefinitionId);\n        } catch (final SProcessDefinitionNotFoundException spdnfe) {\n            throw new DeletionException(new ProcessDefinitionNotFoundException(spdnfe));\n        } catch (final Exception e) {\n            throw new DeletionException(e);\n        }\n    }\n\n    private void checkIfItIsPossibleToDeleteProcessInstance(final long processDefinitionId,\n            final boolean canThrowException) throws DeletionException {\n        if (canThrowException) {\n            throw new DeletionException(\"Some active process instances are still found, process #\" + processDefinitionId\n                    + \" can't be deleted.\");\n        }\n    }\n\n    @Override\n    public void deleteProcessDefinitions(final List<Long> processDefinitionIds) throws DeletionException {\n        for (final Long processDefinitionId : processDefinitionIds) {\n            deleteProcessDefinition(processDefinitionId);\n        }\n    }\n\n    private void releaseLocks(final LockService lockService, final List<BonitaLock> locks) {\n        if (locks == null) {\n            return;\n        }\n        for (final BonitaLock lock : locks) {\n            try {\n                lockService.unlock(lock);\n            } catch (final SLockException e) {\n                logError(e);\n            }\n        }\n    }\n\n    @Override\n    public ProcessDefinition deployAndEnableProcess(final DesignProcessDefinition designProcessDefinition)\n            throws ProcessDeployException,\n            ProcessEnablementException, AlreadyExistsException, InvalidProcessDefinitionException {\n        BusinessArchive businessArchive;\n        try {\n            businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                    .setProcessDefinition(designProcessDefinition).done();\n        } catch (final InvalidBusinessArchiveFormatException e) {\n            throw new InvalidProcessDefinitionException(e.getMessage());\n        }\n        return deployAndEnableProcess(businessArchive);\n    }\n\n    @Override\n    public ProcessDefinition deployAndEnableProcess(final BusinessArchive businessArchive)\n            throws ProcessDeployException, ProcessEnablementException, AlreadyExistsException {\n        return processDeploymentAPIDelegate.deployAndEnableProcess(businessArchive);\n    }\n\n    @Override\n    public ProcessDefinition deploy(final DesignProcessDefinition designProcessDefinition)\n            throws AlreadyExistsException, ProcessDeployException {\n        try {\n            final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                    .setProcessDefinition(designProcessDefinition)\n                    .done();\n            return deploy(businessArchive);\n        } catch (final InvalidBusinessArchiveFormatException e) {\n            throw new ProcessDeployException(e);\n        }\n    }\n\n    @Override\n    public ProcessDefinition deploy(final BusinessArchive businessArchive)\n            throws ProcessDeployException, AlreadyExistsException {\n        return processDeploymentAPIDelegate.deploy(businessArchive);\n    }\n\n    @Override\n    public void importActorMapping(final long pDefinitionId, final byte[] actorMappingXML)\n            throws ActorMappingImportException {\n        if (actorMappingXML != null) {\n            importActorMapping(pDefinitionId, new String(actorMappingXML, UTF_8));\n        }\n    }\n\n    @Override\n    public byte[] exportBarProcessContentUnderHome(final long processDefinitionId) throws ProcessExportException {\n        File barExport = null;\n        try {\n            barExport = File.createTempFile(\"barExport\", \".bar\");\n            barExport.delete();\n            final BusinessArchive export = getServiceAccessor().getBusinessArchiveService().export(processDefinitionId);\n            BusinessArchiveFactory.writeBusinessArchiveToFile(export, barExport);\n            return FileUtils.readFileToByteArray(barExport);\n        } catch (IOException | InvalidBusinessArchiveFormatException | SBonitaException e) {\n            throw new ProcessExportException(e);\n        } finally {\n            if (barExport != null && barExport.exists()) {\n                barExport.delete();\n            }\n        }\n    }\n\n    @Override\n    public void disableAndDeleteProcessDefinition(final long processDefinitionId)\n            throws ProcessDefinitionNotFoundException, ProcessActivationException,\n            DeletionException {\n        disableProcess(processDefinitionId);\n        deleteProcessDefinition(processDefinitionId);\n    }\n\n    @Override\n    public void disableProcess(final long processDefinitionId)\n            throws ProcessDefinitionNotFoundException, ProcessActivationException {\n        try {\n            processManagementAPIImplDelegate.disableProcess(processDefinitionId);\n        } catch (final SProcessDefinitionNotFoundException e) {\n            throw new ProcessDefinitionNotFoundException(e);\n        } catch (final SBonitaException e) {\n            throw new ProcessActivationException(e);\n        }\n    }\n\n    @Override\n    public void enableProcess(final long processDefinitionId)\n            throws ProcessDefinitionNotFoundException, ProcessEnablementException {\n        processDeploymentAPIDelegate.enableProcess(processDefinitionId);\n    }\n\n    SSession getSession() {\n        return SessionInfos.getSession();\n    }\n\n    @Override\n    public void executeFlowNode(final long flownodeInstanceId) throws FlowNodeExecutionException {\n        executeFlowNode(0, flownodeInstanceId);\n    }\n\n    @Override\n    public void executeFlowNode(final long userId, final long flownodeInstanceId) throws FlowNodeExecutionException {\n        try {\n            ActivityInstanceService activityInstanceService = getServiceAccessor().getActivityInstanceService();\n            SFlowNodeInstance flowNodeInstance = activityInstanceService.getFlowNodeInstance(flownodeInstanceId);\n            log.warn(String.format(\n                    \"executeFlowNode was called: <%s> is forcing the execution of the flow node with name = <%s> id = <%d> and type <%s> in current state <%s: %s>. \"\n                            +\n                            \"Executing a flow node directly through that API method is not recommended and can affect the normal behavior of the platform. \"\n                            +\n                            \"If that flow node was already scheduled for execution, there is no guarantee on how that flow node will behave anymore. \"\n                            +\n                            \"If you are trying to execute a user task, please use executeUserTask method instead.\",\n                    getLoggedUsername(), flowNodeInstance.getName(), flowNodeInstance.getId(),\n                    flowNodeInstance.getType().name(),\n                    flowNodeInstance.getStateId(), flowNodeInstance.getStateName()));\n            executeFlowNode(userId, flownodeInstanceId, new HashMap<>(), false);\n        } catch (final ContractViolationException | SBonitaException e) {\n            throw new FlowNodeExecutionException(e);\n        }\n    }\n\n    @Override\n    public List<ActivityInstance> getActivities(final long processInstanceId, final int startIndex,\n            final int maxResults) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager();\n        try {\n            return ModelConvertor.toActivityInstances(\n                    activityInstanceService.getActivityInstances(processInstanceId, startIndex, maxResults),\n                    flowNodeStateManager);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public long getNumberOfProcessDeploymentInfos() {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        final TransactionContentWithResult<Long> transactionContentWithResult = new GetNumberOfProcessDeploymentInfos(\n                processDefinitionService);\n        try {\n            transactionContentWithResult.execute();\n            return transactionContentWithResult.getResult();\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public ProcessDefinition getProcessDefinition(final long processDefinitionId)\n            throws ProcessDefinitionNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        try {\n            final SProcessDefinition sProcessDefinition = processDefinitionService\n                    .getProcessDefinition(processDefinitionId);\n            return ModelConvertor.toProcessDefinition(sProcessDefinition);\n        } catch (final SProcessDefinitionNotFoundException e) {\n            throw new ProcessDefinitionNotFoundException(e);\n        } catch (final SBonitaReadException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public DesignProcessDefinition getDesignProcessDefinition(final long processDefinitionId)\n            throws ProcessDefinitionNotFoundException {\n        try {\n            return getServiceAccessor().getProcessDefinitionService().getDesignProcessDefinition(processDefinitionId);\n        } catch (SBonitaReadException | SProcessDefinitionNotFoundException e) {\n            throw new ProcessDefinitionNotFoundException(processDefinitionId, e);\n        }\n    }\n\n    @Override\n    public ProcessDeploymentInfo getProcessDeploymentInfo(final long processDefinitionId)\n            throws ProcessDefinitionNotFoundException {\n        return processDeploymentAPIDelegate.getProcessDeploymentInfo(processDefinitionId);\n    }\n\n    private void logError(final Exception e) {\n        if (log.isErrorEnabled()) {\n            log.error(\"\", e);\n        }\n    }\n\n    private void logInstanceNotFound(final SBonitaException e) {\n        if (log.isDebugEnabled()) {\n            log.debug(e.getMessage() + \". It may have been completed.\");\n        }\n    }\n\n    @Override\n    public ProcessInstance getProcessInstance(final long processInstanceId) throws ProcessInstanceNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        try {\n            final SProcessInstance sProcessInstance = getSProcessInstance(processInstanceId);\n            return ModelConvertor\n                    .toProcessInstances(Collections.singletonList(sProcessInstance), processDefinitionService).get(0);\n        } catch (final SProcessInstanceNotFoundException notFound) {\n            throw new ProcessInstanceNotFoundException(notFound);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    protected SProcessInstance getSProcessInstance(final long processInstanceId)\n            throws SProcessInstanceNotFoundException, SProcessInstanceReadException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n        return processInstanceService.getProcessInstance(processInstanceId);\n    }\n\n    @Override\n    public List<ArchivedProcessInstance> getArchivedProcessInstances(final long processInstanceId, final int startIndex,\n            final int maxResults) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final GetArchivedProcessInstanceList getProcessInstanceList = new GetArchivedProcessInstanceList(\n                processInstanceService,\n                serviceAccessor.getProcessDefinitionService(), searchEntitiesDescriptor, processInstanceId, startIndex,\n                maxResults);\n        try {\n            getProcessInstanceList.execute();\n        } catch (final SBonitaException e) {\n            logError(e);\n            throw new RetrieveException(e);\n        }\n        return getProcessInstanceList.getResult();\n    }\n\n    @Override\n    public ArchivedProcessInstance getArchivedProcessInstance(final long id)\n            throws ArchivedProcessInstanceNotFoundException, RetrieveException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        try {\n            final SAProcessInstance archivedProcessInstance = processInstanceService.getArchivedProcessInstance(id);\n            if (archivedProcessInstance == null) {\n                throw new ArchivedProcessInstanceNotFoundException(id);\n            }\n            final SProcessDefinition sProcessDefinition = processDefinitionService\n                    .getProcessDefinition(archivedProcessInstance\n                            .getProcessDefinitionId());\n            return toArchivedProcessInstance(archivedProcessInstance, sProcessDefinition);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    /**\n     * internal use for mocking purpose\n     */\n    protected ArchivedProcessInstance toArchivedProcessInstance(final SAProcessInstance archivedProcessInstance,\n            final SProcessDefinition sProcessDefinition) {\n        return ModelConvertor.toArchivedProcessInstance(archivedProcessInstance, sProcessDefinition);\n    }\n\n    @Override\n    public ArchivedProcessInstance getFinalArchivedProcessInstance(final long sourceProcessInstanceId)\n            throws ArchivedProcessInstanceNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n\n        final GetLastArchivedProcessInstance getProcessInstance = new GetLastArchivedProcessInstance(\n                processInstanceService,\n                serviceAccessor.getProcessDefinitionService(), sourceProcessInstanceId,\n                serviceAccessor.getSearchEntitiesDescriptor());\n        try {\n            getProcessInstance.execute();\n        } catch (final SProcessInstanceNotFoundException e) {\n            logInstanceNotFound(e);\n            throw new ArchivedProcessInstanceNotFoundException(e);\n        } catch (final SBonitaException e) {\n            logError(e);\n            throw new RetrieveException(e);\n        }\n        return getProcessInstance.getResult();\n    }\n\n    @Override\n    public ProcessInstance startProcess(final long processDefinitionId)\n            throws ProcessActivationException, ProcessExecutionException {\n        try {\n            return startProcess(getUserId(), processDefinitionId);\n        } catch (final ProcessDefinitionNotFoundException e) {\n            throw new ProcessExecutionException(e);\n        }\n    }\n\n    @Override\n    public ProcessInstance startProcess(final long userId, final long processDefinitionId)\n            throws ProcessDefinitionNotFoundException,\n            ProcessExecutionException, ProcessActivationException {\n        return startProcess(userId, processDefinitionId, null, null);\n    }\n\n    @Override\n    public int getNumberOfActors(final long processDefinitionId) throws ProcessDefinitionNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n\n        final GetNumberOfActors getNumberofActors = new GetNumberOfActors(processDefinitionService,\n                processDefinitionId);\n        try {\n            getNumberofActors.execute();\n        } catch (final SBonitaException e) {\n            throw new ProcessDefinitionNotFoundException(e);\n        }\n        return getNumberofActors.getResult();\n    }\n\n    @Override\n    public List<ActorInstance> getActors(final long processDefinitionId, final int startIndex, final int maxResults,\n            final ActorCriterion sort) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService();\n        try {\n            final QueryOptions queryOptions = getQueryOptions(startIndex, maxResults, sort);\n            final List<SActor> actors = actorMappingService.getActors(processDefinitionId, queryOptions);\n            return ModelConvertor.toActors(actors);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    private static QueryOptions getQueryOptions(int startIndex, int maxResults, ActorCriterion sort) {\n        OrderByType order;\n        if (sort == null) {\n            order = OrderByType.ASC;\n        } else {\n            switch (sort) {\n                case NAME_ASC:\n                    order = OrderByType.ASC;\n                    break;\n                default:\n                    order = OrderByType.DESC;\n                    break;\n            }\n        }\n        return new QueryOptions(startIndex, maxResults, SActor.class, \"name\", order);\n    }\n\n    @Override\n    public List<ActorMember> getActorMembers(final long actorId, final int startIndex, final int maxResults) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService();\n\n        try {\n            final List<SActorMember> actorMembers = actorMappingService.getActorMembers(actorId, startIndex,\n                    maxResults);\n            return ModelConvertor.toActorMembers(actorMembers);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n\n    }\n\n    @Override\n    public long getNumberOfActorMembers(final long actorId) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService();\n        final GetNumberOfActorMembers numberOfActorMembers = new GetNumberOfActorMembers(actorMappingService, actorId);\n        try {\n            numberOfActorMembers.execute();\n            return numberOfActorMembers.getResult();\n        } catch (final SBonitaException sbe) {\n            return 0; // FIXME throw retrieve exception\n        }\n    }\n\n    @Override\n    public long getNumberOfUsersOfActor(final long actorId) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService();\n        final GetNumberOfUsersOfActor numberOfUsersOfActor = new GetNumberOfUsersOfActor(actorMappingService, actorId);\n        numberOfUsersOfActor.execute();\n        return numberOfUsersOfActor.getResult();\n    }\n\n    @Override\n    public long getNumberOfRolesOfActor(final long actorId) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService();\n        final GetNumberOfRolesOfActor numberOfRolesOfActor = new GetNumberOfRolesOfActor(actorMappingService, actorId);\n        numberOfRolesOfActor.execute();\n        return numberOfRolesOfActor.getResult();\n    }\n\n    @Override\n    public long getNumberOfGroupsOfActor(final long actorId) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService();\n        final GetNumberOfGroupsOfActor numberOfGroupsOfActor = new GetNumberOfGroupsOfActor(actorMappingService,\n                actorId);\n        numberOfGroupsOfActor.execute();\n        return numberOfGroupsOfActor.getResult();\n    }\n\n    @Override\n    public long getNumberOfMembershipsOfActor(final long actorId) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService();\n        final GetNumberOfMembershipsOfActor getNumber = new GetNumberOfMembershipsOfActor(actorMappingService, actorId);\n        getNumber.execute();\n        return getNumber.getResult();\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    @Deprecated(since = \"9.0.0\")\n    public ActorInstance updateActor(final long actorId, final ActorUpdater descriptor)\n            throws ActorNotFoundException, UpdateException {\n        if (descriptor == null || descriptor.getFields().isEmpty()) {\n            throw new UpdateException(\"The update descriptor does not contain field updates\");\n        }\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService();\n        final SActorUpdateBuilder actorUpdateBuilder = BuilderFactory.get(SActorUpdateBuilderFactory.class)\n                .createNewInstance();\n        final Map<ActorField, Serializable> fields = descriptor.getFields();\n        for (final Entry<ActorField, Serializable> field : fields.entrySet()) {\n            switch (field.getKey()) {\n                case DISPLAY_NAME:\n                    actorUpdateBuilder.updateDisplayName((String) field.getValue());\n                    break;\n                case DESCRIPTION:\n                    actorUpdateBuilder.updateDescription((String) field.getValue());\n                    break;\n                default:\n                    break;\n            }\n        }\n        final EntityUpdateDescriptor updateDescriptor = actorUpdateBuilder.done();\n        SActor updateActor;\n        try {\n            updateActor = actorMappingService.updateActor(actorId, updateDescriptor);\n            return ModelConvertor.toActorInstance(updateActor);\n        } catch (final SActorNotFoundException e) {\n            throw new ActorNotFoundException(e);\n        } catch (final SBonitaException e) {\n            throw new UpdateException(e);\n        }\n    }\n\n    @Override\n    public ActorMember addUserToActor(final long actorId, final long userId) throws CreationException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService();\n\n        try {\n            // Check if the mapping already to throw a specific exception\n            checkIfActorMappingForUserAlreadyExists(actorId, userId);\n\n            final SActorMember actorMember = actorMappingService.addUserToActor(actorId, userId);\n            final long processDefinitionId = actorMappingService.getActor(actorId).getScopeId();\n            final ActorMember clientActorMember = ModelConvertor.toActorMember(actorMember);\n            serviceAccessor.getBusinessArchiveArtifactsManager().resolveDependencies(processDefinitionId,\n                    serviceAccessor);\n            return clientActorMember;\n        } catch (final SBonitaException sbe) {\n            throw new CreationException(sbe);\n        }\n    }\n\n    private void checkIfActorMappingForUserAlreadyExists(final long actorId, final long userId)\n            throws AlreadyExistsException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService();\n\n        try {\n            final SActorMember sActorMember = actorMappingService.getActorMember(actorId, userId, -1, -1);\n            if (sActorMember != null) {\n                throw new AlreadyExistsException(\"The mapping already exists for the actor id = <\" + actorId\n                        + \">, the user id = <\" + userId + \">\");\n            }\n        } catch (final SBonitaReadException e) {\n            // Do nothing\n        }\n    }\n\n    @Override\n    public ActorMember addUserToActor(final String actorName, final ProcessDefinition processDefinition,\n            final long userId) throws CreationException,\n            ActorNotFoundException {\n        final List<ActorInstance> actors = getActors(processDefinition.getId(), 0, Integer.MAX_VALUE,\n                ActorCriterion.NAME_ASC);\n        for (final ActorInstance ai : actors) {\n            if (actorName.equals(ai.getName())) {\n                return addUserToActor(ai.getId(), userId);\n            }\n        }\n        throw new ActorNotFoundException(\n                \"Actor \" + actorName + \" not found in process definition \" + processDefinition.getName());\n    }\n\n    @Override\n    public ActorMember addGroupToActor(final long actorId, final long groupId) throws CreationException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService();\n        try {\n            // Check if the mapping already to throw a specific exception\n            checkIfActorMappingForGroupAlreadyExists(actorId, groupId);\n\n            final SActorMember actorMember = actorMappingService.addGroupToActor(actorId, groupId);\n            final long processDefinitionId = actorMappingService.getActor(actorId).getScopeId();\n            final ActorMember clientActorMember = ModelConvertor.toActorMember(actorMember);\n            serviceAccessor.getBusinessArchiveArtifactsManager().resolveDependencies(processDefinitionId,\n                    serviceAccessor);\n            return clientActorMember;\n        } catch (final SBonitaException e) {\n            throw new CreationException(e);\n        }\n    }\n\n    private void checkIfActorMappingForGroupAlreadyExists(final long actorId, final long groupId)\n            throws AlreadyExistsException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService();\n\n        try {\n            final SActorMember sActorMember = actorMappingService.getActorMember(actorId, -1, groupId, -1);\n            if (sActorMember != null) {\n                throw new AlreadyExistsException(\"The mapping already exists for the actor id = <\" + actorId\n                        + \">, the group id = <\" + groupId + \">\");\n            }\n        } catch (final SBonitaReadException e) {\n            // Do nothing\n        }\n    }\n\n    @Override\n    public ActorMember addGroupToActor(final String actorName, final long groupId,\n            final ProcessDefinition processDefinition) throws CreationException,\n            ActorNotFoundException {\n        final List<ActorInstance> actors = getActors(processDefinition.getId(), 0, Integer.MAX_VALUE,\n                ActorCriterion.NAME_ASC);\n        for (final ActorInstance actorInstance : actors) {\n            if (actorName.equals(actorInstance.getName())) {\n                return addGroupToActor(actorInstance.getId(), groupId);\n            }\n        }\n        throw new ActorNotFoundException(\n                \"Actor \" + actorName + \" not found in process definition \" + processDefinition.getName());\n    }\n\n    @Override\n    public ActorMember addRoleToActor(final long actorId, final long roleId) throws CreationException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService();\n\n        try {\n            // Check if the mapping already to throw a specific exception\n            checkIfActorMappingForRoleAlreadyExists(actorId, roleId);\n\n            final SActorMember actorMember = actorMappingService.addRoleToActor(actorId, roleId);\n            final long processDefinitionId = actorMappingService.getActor(actorId).getScopeId();\n            final ActorMember clientActorMember = ModelConvertor.toActorMember(actorMember);\n            serviceAccessor.getBusinessArchiveArtifactsManager().resolveDependencies(processDefinitionId,\n                    serviceAccessor);\n            return clientActorMember;\n        } catch (final SBonitaException sbe) {\n            throw new CreationException(sbe);\n        }\n    }\n\n    private void checkIfActorMappingForRoleAlreadyExists(final long actorId, final long roleId)\n            throws AlreadyExistsException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService();\n\n        try {\n            final SActorMember sActorMember = actorMappingService.getActorMember(actorId, -1, -1, roleId);\n            if (sActorMember != null) {\n                throw new AlreadyExistsException(\"The mapping already exists for the actor id = <\" + actorId\n                        + \">, the role id = <\" + roleId + \">\");\n            }\n        } catch (final SBonitaReadException e) {\n            // Do nothing\n        }\n    }\n\n    @Override\n    public ActorMember addRoleToActor(final String actorName, final ProcessDefinition processDefinition,\n            final long roleId) throws ActorNotFoundException,\n            CreationException {\n        final List<ActorInstance> actors = getActors(processDefinition.getId(), 0, Integer.MAX_VALUE,\n                ActorCriterion.NAME_ASC);\n        for (final ActorInstance ai : actors) {\n            if (actorName.equals(ai.getName())) {\n                return addRoleToActor(ai.getId(), roleId);\n            }\n        }\n        throw new ActorNotFoundException(\n                \"Actor \" + actorName + \" not found in process definition \" + processDefinition.getName());\n    }\n\n    @Override\n    public ActorMember addRoleAndGroupToActor(final String actorName, final ProcessDefinition processDefinition,\n            final long roleId, final long groupId)\n            throws ActorNotFoundException, CreationException {\n        final List<ActorInstance> actors = getActors(processDefinition.getId(), 0, Integer.MAX_VALUE,\n                ActorCriterion.NAME_ASC);\n        for (final ActorInstance ai : actors) {\n            if (actorName.equals(ai.getName())) {\n                return addRoleAndGroupToActor(ai.getId(), roleId, groupId);\n            }\n        }\n        throw new ActorNotFoundException(\n                \"Actor \" + actorName + \" not found in process definition \" + processDefinition.getName());\n    }\n\n    @Override\n    public ActorMember addRoleAndGroupToActor(final long actorId, final long roleId, final long groupId)\n            throws CreationException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService();\n\n        try {\n            checkIfActorMappingForMembershipAlreadyExists(actorId, roleId, groupId);\n\n            final SActorMember actorMember = actorMappingService.addRoleAndGroupToActor(actorId, roleId, groupId);\n            final long processDefinitionId = actorMappingService.getActor(actorId).getScopeId();\n            final ActorMember clientActorMember = ModelConvertor.toActorMember(actorMember);\n            serviceAccessor.getBusinessArchiveArtifactsManager().resolveDependencies(processDefinitionId,\n                    serviceAccessor);\n            return clientActorMember;\n        } catch (final SBonitaException sbe) {\n            throw new CreationException(sbe);\n        }\n    }\n\n    private void checkIfActorMappingForMembershipAlreadyExists(final long actorId, final long roleId,\n            final long groupId) throws AlreadyExistsException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService();\n\n        try {\n            final SActorMember sActorMember = actorMappingService.getActorMember(actorId, -1, groupId, roleId);\n            if (sActorMember != null) {\n                throw new AlreadyExistsException(\n                        \"The mapping already exists for the actor id = <\" + actorId + \">, the role id = <\" + roleId\n                                + \">, the group id = <\" + groupId + \">\");\n            }\n        } catch (final SBonitaReadException e) {\n            // Do nothing\n        }\n    }\n\n    @Override\n    public void removeActorMember(final long actorMemberId) throws DeletionException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService();\n        final RemoveActorMember removeActorMember = new RemoveActorMember(actorMappingService, actorMemberId);\n        // FIXME remove an actor member when process is running!\n        try {\n            removeActorMember.execute();\n            final SActorMember actorMember = removeActorMember.getResult();\n            final long processDefinitionId = getActor(actorMember.getActorId()).getProcessDefinitionId();\n            serviceAccessor.getBusinessArchiveArtifactsManager().resolveDependencies(processDefinitionId,\n                    serviceAccessor);\n        } catch (final SBonitaException | ActorNotFoundException sbe) {\n            throw new DeletionException(sbe);\n        }\n    }\n\n    @Override\n    public ActorInstance getActor(final long actorId) throws ActorNotFoundException {\n        try {\n            final ServiceAccessor serviceAccessor = getServiceAccessor();\n            final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService();\n\n            final GetActor getActor = new GetActor(actorMappingService, actorId);\n            getActor.execute();\n            return ModelConvertor.toActorInstance(getActor.getResult());\n        } catch (final SBonitaException e) {\n            throw new ActorNotFoundException(e);\n        }\n    }\n\n    @Override\n    public ActivityInstance getActivityInstance(final long activityInstanceId)\n            throws ActivityInstanceNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager();\n        final SActivityInstance sActivityInstance;\n        try {\n            sActivityInstance = getSActivityInstance(activityInstanceId);\n        } catch (final SActivityInstanceNotFoundException e) {\n            throw new ActivityInstanceNotFoundException(activityInstanceId);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n        return ModelConvertor.toActivityInstance(sActivityInstance, flowNodeStateManager);\n    }\n\n    protected SActivityInstance getSActivityInstance(final long activityInstanceId)\n            throws SActivityInstanceNotFoundException, SActivityReadException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        return activityInstanceService.getActivityInstance(activityInstanceId);\n    }\n\n    @Override\n    public FlowNodeInstance getFlowNodeInstance(final long flowNodeInstanceId)\n            throws FlowNodeInstanceNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager();\n        try {\n            final SFlowNodeInstance flowNodeInstance = activityInstanceService.getFlowNodeInstance(flowNodeInstanceId);\n            return ModelConvertor.toFlowNodeInstance(flowNodeInstance, flowNodeStateManager);\n        } catch (final SFlowNodeNotFoundException e) {\n            throw new FlowNodeInstanceNotFoundException(e);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public List<HumanTaskInstance> getAssignedHumanTaskInstances(final long userId, final int startIndex,\n            final int maxResults,\n            final ActivityInstanceCriterion pagingCriterion) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager();\n        final OrderAndField orderAndField = OrderAndFields.getOrderAndFieldForActivityInstance(pagingCriterion);\n\n        final ActivityInstanceService instanceService = serviceAccessor.getActivityInstanceService();\n        try {\n            final GetAssignedTasks getAssignedTasks = new GetAssignedTasks(instanceService, userId, startIndex,\n                    maxResults, orderAndField.getField(),\n                    orderAndField.getOrder());\n            getAssignedTasks.execute();\n            final List<SHumanTaskInstance> assignedTasks = getAssignedTasks.getResult();\n            return ModelConvertor.toHumanTaskInstances(assignedTasks, flowNodeStateManager);\n        } catch (final SBonitaException e) {\n            return Collections.emptyList();\n        }\n    }\n\n    @Override\n    public long getNumberOfPendingHumanTaskInstances(final long userId) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n\n        final ProcessDefinitionService processDefService = serviceAccessor.getProcessDefinitionService();\n        try {\n            final Set<Long> actorIds = getActorsForUser(userId, actorMappingService, processDefService);\n            if (actorIds.isEmpty()) {\n                return 0L;\n            }\n            return activityInstanceService.getNumberOfPendingTasksForUser(userId, QueryOptions.countQueryOptions());\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public List<HumanTaskInstance> getPendingHumanTaskInstances(final long userId, final int startIndex,\n            final int maxResults,\n            final ActivityInstanceCriterion pagingCriterion) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService();\n        final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager();\n        final OrderAndField orderAndField = OrderAndFields.getOrderAndFieldForActivityInstance(pagingCriterion);\n\n        final ProcessDefinitionService definitionService = serviceAccessor.getProcessDefinitionService();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        try {\n            final Set<Long> actorIds = getActorsForUser(userId, actorMappingService, definitionService);\n            final List<SHumanTaskInstance> pendingTasks = activityInstanceService.getPendingTasks(userId, actorIds,\n                    startIndex, maxResults,\n                    orderAndField.getField(), orderAndField.getOrder());\n            return ModelConvertor.toHumanTaskInstances(pendingTasks, flowNodeStateManager);\n        } catch (final SBonitaException e) {\n            return Collections.emptyList();\n        }\n    }\n\n    private Set<Long> getActorsForUser(final long userId, final ActorMappingService actorMappingService,\n            final ProcessDefinitionService definitionService)\n            throws SBonitaReadException {\n        final Set<Long> actorIds = new HashSet<>();\n        final List<Long> processDefIds = definitionService.getProcessDefinitionIds(0, Integer.MAX_VALUE);\n        if (!processDefIds.isEmpty()) {\n            final Set<Long> processDefinitionIds = new HashSet<>(processDefIds);\n            final List<SActor> actors = actorMappingService.getActors(processDefinitionIds, userId);\n            for (final SActor sActor : actors) {\n                actorIds.add(sActor.getId());\n            }\n        }\n        return actorIds;\n    }\n\n    @Override\n    public ArchivedActivityInstance getArchivedActivityInstance(final long sourceActivityInstanceId)\n            throws ActivityInstanceNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager();\n        final GetArchivedActivityInstance getActivityInstance = new GetArchivedActivityInstance(activityInstanceService,\n                sourceActivityInstanceId);\n        try {\n            getActivityInstance.execute();\n        } catch (final SActivityInstanceNotFoundException e) {\n            throw new ActivityInstanceNotFoundException(sourceActivityInstanceId, e);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n        return ModelConvertor.toArchivedActivityInstance(getActivityInstance.getResult(), flowNodeStateManager);\n    }\n\n    @Override\n    public ArchivedFlowNodeInstance getArchivedFlowNodeInstance(final long archivedFlowNodeInstanceId)\n            throws ArchivedFlowNodeInstanceNotFoundException,\n            RetrieveException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager();\n\n        try {\n            final SAFlowNodeInstance archivedFlowNodeInstance = activityInstanceService\n                    .getArchivedFlowNodeInstance(archivedFlowNodeInstanceId);\n            return ModelConvertor.toArchivedFlowNodeInstance(archivedFlowNodeInstance, flowNodeStateManager);\n        } catch (final SFlowNodeNotFoundException e) {\n            throw new ArchivedFlowNodeInstanceNotFoundException(archivedFlowNodeInstanceId);\n        } catch (final SFlowNodeReadException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public List<ProcessInstance> getProcessInstances(final int startIndex, final int maxResults,\n            final ProcessInstanceCriterion criterion) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final OrderAndField orderAndField = OrderAndFields.getOrderAndFieldForProcessInstance(criterion);\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(startIndex, maxResults);\n        searchOptionsBuilder.sort(orderAndField.getField(), Order.valueOf(orderAndField.getOrder().name()));\n        List<ProcessInstance> result;\n        try {\n            result = searchProcessInstances(serviceAccessor, searchOptionsBuilder.done()).getResult();\n        } catch (final SBonitaException e) {\n            result = Collections.emptyList();\n        }\n        return result;\n    }\n\n    @Override\n    public long getNumberOfProcessInstances() {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n\n        try {\n            final TransactionContentWithResult<Long> transactionContent = new GetNumberOfProcessInstance(\n                    processInstanceService, processDefinitionService,\n                    searchEntitiesDescriptor);\n            transactionContent.execute();\n            return transactionContent.getResult();\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    protected SearchResult<ProcessInstance> searchProcessInstances(final ServiceAccessor serviceAccessor,\n            final SearchOptions searchOptions)\n            throws SBonitaException {\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n\n        final SearchProcessInstances searchProcessInstances = new SearchProcessInstances(processInstanceService,\n                searchEntitiesDescriptor.getSearchProcessInstanceDescriptor(), searchOptions, processDefinitionService);\n        searchProcessInstances.execute();\n        return searchProcessInstances.getResult();\n    }\n\n    @Deprecated\n    @Override\n    public List<ArchivedProcessInstance> getArchivedProcessInstances(final int startIndex, final int maxResults,\n            final ProcessInstanceCriterion criterion) {\n        return getCompletedProcessInstances(startIndex, maxResults, criterion);\n    }\n\n    @Override\n    public List<ArchivedProcessInstance> getCompletedProcessInstances(final int startIndex, final int maxResults,\n            final ProcessInstanceCriterion criterion) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final OrderAndField orderAndField = OrderAndFields.getOrderAndFieldForProcessInstance(criterion);\n\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(startIndex, maxResults);\n        searchOptionsBuilder.sort(orderAndField.getField(), Order.valueOf(orderAndField.getOrder().name()));\n        searchOptionsBuilder.filter(ArchivedProcessInstancesSearchDescriptor.STATE_ID,\n                ProcessInstanceState.COMPLETED.getId());\n        searchOptionsBuilder.filter(ArchivedProcessInstancesSearchDescriptor.CALLER_ID, -1);\n        final SearchArchivedProcessInstances searchArchivedProcessInstances = searchArchivedProcessInstances(\n                serviceAccessor, searchOptionsBuilder.done());\n        try {\n            searchArchivedProcessInstances.execute();\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n\n        return searchArchivedProcessInstances.getResult().getResult();\n    }\n\n    private SearchArchivedProcessInstances searchArchivedProcessInstances(final ServiceAccessor serviceAccessor,\n            final SearchOptions searchOptions)\n            throws RetrieveException {\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        return new SearchArchivedProcessInstances(processInstanceService, serviceAccessor.getProcessDefinitionService(),\n                searchEntitiesDescriptor.getSearchArchivedProcessInstanceDescriptor(), searchOptions);\n    }\n\n    @Deprecated\n    @Override\n    public long getNumberOfArchivedProcessInstances() {\n        return getNumberOfCompletedProcessInstances();\n    }\n\n    @Override\n    public long getNumberOfCompletedProcessInstances() {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n        try {\n            final SAProcessInstanceBuilderFactory saProcessInstanceBuilder = BuilderFactory\n                    .get(SAProcessInstanceBuilderFactory.class);\n            final List<FilterOption> filterOptions = new ArrayList<>(2);\n            filterOptions.add(new FilterOption(SAProcessInstance.class, saProcessInstanceBuilder.getStateIdKey(),\n                    ProcessInstanceState.COMPLETED.getId()));\n            filterOptions.add(new FilterOption(SAProcessInstance.class, saProcessInstanceBuilder.getCallerIdKey(), -1));\n            final QueryOptions queryOptions = new QueryOptions(0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS,\n                    Collections.emptyList(), filterOptions, null);\n            return processInstanceService.getNumberOfArchivedProcessInstances(queryOptions);\n        } catch (final SBonitaException e) {\n            logError(e);\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public SearchResult<ArchivedProcessInstance> searchArchivedProcessInstances(final SearchOptions searchOptions)\n            throws RetrieveException, SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final SearchArchivedProcessInstancesWithoutSubProcess searchArchivedProcessInstances = new SearchArchivedProcessInstancesWithoutSubProcess(\n                processInstanceService, serviceAccessor.getProcessDefinitionService(),\n                searchEntitiesDescriptor.getSearchArchivedProcessInstanceDescriptor(),\n                searchOptions);\n        try {\n            searchArchivedProcessInstances.execute();\n        } catch (final SBonitaException e) {\n            throw new SearchException(e);\n        }\n        return searchArchivedProcessInstances.getResult();\n    }\n\n    @Override\n    public SearchResult<ArchivedProcessInstance> searchArchivedProcessInstancesInAllStates(\n            final SearchOptions searchOptions) throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final SearchArchivedProcessInstances searchArchivedProcessInstances = new SearchArchivedProcessInstances(\n                processInstanceService,\n                serviceAccessor.getProcessDefinitionService(),\n                searchEntitiesDescriptor.getSearchArchivedProcessInstanceDescriptor(), searchOptions);\n        try {\n            searchArchivedProcessInstances.execute();\n        } catch (final SBonitaException e) {\n            throw new SearchException(e);\n        }\n        return searchArchivedProcessInstances.getResult();\n    }\n\n    @Override\n    public List<ActivityInstance> getOpenActivityInstances(final long processInstanceId, final int startIndex,\n            final int maxResults,\n            final ActivityInstanceCriterion criterion) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager();\n\n        try {\n            final int totalNumber = activityInstanceService.getNumberOfOpenActivityInstances(processInstanceId);\n            // If there are no instances, return an empty list:\n            if (totalNumber == 0) {\n                return Collections.emptyList();\n            }\n            final OrderAndField orderAndField = OrderAndFields.getOrderAndFieldForActivityInstance(criterion);\n            return ModelConvertor.toActivityInstances(\n                    activityInstanceService.getOpenActivityInstances(processInstanceId, startIndex, maxResults,\n                            orderAndField.getField(),\n                            orderAndField.getOrder()),\n                    flowNodeStateManager);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public List<ArchivedActivityInstance> getArchivedActivityInstances(final long processInstanceId,\n            final int startIndex, final int maxResults, final ActivityInstanceCriterion criterion) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        return getArchivedActivityInstances(processInstanceId, startIndex, maxResults, criterion, serviceAccessor);\n    }\n\n    private List<ArchivedActivityInstance> getArchivedActivityInstances(final long processInstanceId,\n            final int pageIndex, final int numberPerPage,\n            final ActivityInstanceCriterion pagingCriterion, final ServiceAccessor serviceAccessor)\n            throws RetrieveException {\n\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager();\n        final OrderAndField orderAndField = OrderAndFields.getOrderAndFieldForActivityInstance(pagingCriterion);\n        final GetArchivedActivityInstances getActivityInstances = new GetArchivedActivityInstances(\n                activityInstanceService, processInstanceId, pageIndex,\n                numberPerPage, orderAndField.getField(), orderAndField.getOrder());\n        try {\n            getActivityInstances.execute();\n        } catch (final SBonitaException e) {\n            logError(e);\n            throw new RetrieveException(e);\n        }\n        return ModelConvertor.toArchivedActivityInstances(getActivityInstances.getResult(), flowNodeStateManager);\n    }\n\n    @Override\n    public int getNumberOfOpenedActivityInstances(final long processInstanceId) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n\n        final TransactionContentWithResult<Integer> transactionContentWithResult = new GetNumberOfActivityInstance(\n                processInstanceId, activityInstanceService);\n        try {\n            transactionContentWithResult.execute();\n            return transactionContentWithResult.getResult();\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public Category createCategory(final String name, final String description) throws CreationException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final CategoryService categoryService = serviceAccessor.getCategoryService();\n\n        try {\n            final CreateCategory createCategory = new CreateCategory(name, description, categoryService);\n            createCategory.execute();\n            return ModelConvertor.toCategory(createCategory.getResult());\n        } catch (final SCategoryAlreadyExistsException scaee) {\n            throw new AlreadyExistsException(scaee);\n        } catch (final SBonitaException e) {\n            throw new CreationException(\"Category create exception!\", e);\n        }\n    }\n\n    @Override\n    public Category getCategory(final long categoryId) throws CategoryNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final CategoryService categoryService = serviceAccessor.getCategoryService();\n        try {\n            final GetCategory getCategory = new GetCategory(categoryService, categoryId);\n            getCategory.execute();\n            final SCategory sCategory = getCategory.getResult();\n            return ModelConvertor.toCategory(sCategory);\n        } catch (final SBonitaException sbe) {\n            throw new CategoryNotFoundException(sbe);\n        }\n    }\n\n    @Override\n    public long getNumberOfCategories() {\n        final CategoryService categoryService = getServiceAccessor().getCategoryService();\n        try {\n            final GetNumberOfCategories getNumberOfCategories = new GetNumberOfCategories(categoryService);\n            getNumberOfCategories.execute();\n            return getNumberOfCategories.getResult();\n        } catch (final SBonitaException e) {\n            return 0;\n        }\n    }\n\n    @Override\n    public List<Category> getCategories(final int startIndex, final int maxResults,\n            final CategoryCriterion sortCriterion) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final CategoryService categoryService = serviceAccessor.getCategoryService();\n        String field;\n        OrderByType order;\n        switch (sortCriterion) {\n            case NAME_ASC:\n                field = SCategory.NAME;\n                order = OrderByType.ASC;\n                break;\n            case NAME_DESC:\n                field = SCategory.NAME;\n                order = OrderByType.DESC;\n                break;\n            default:\n                throw new IllegalStateException();\n        }\n        try {\n            final GetCategories getCategories = new GetCategories(startIndex, maxResults, field, categoryService,\n                    order);\n            getCategories.execute();\n            return ModelConvertor.toCategories(getCategories.getResult());\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public void addCategoriesToProcess(final long processDefinitionId, final List<Long> categoryIds)\n            throws CreationException {\n        try {\n            final CategoryService categoryService = getServiceAccessor().getCategoryService();\n            final ProcessDefinitionService processDefinitionService = getServiceAccessor()\n                    .getProcessDefinitionService();\n            for (final Long categoryId : categoryIds) {\n                new AddProcessDefinitionToCategory(categoryId, processDefinitionId, categoryService,\n                        processDefinitionService).execute();\n            }\n        } catch (final SCategoryInProcessAlreadyExistsException scipaee) {\n            throw new AlreadyExistsException(scipaee);\n        } catch (final SBonitaException sbe) {\n            throw new CreationException(sbe);\n        }\n    }\n\n    @Override\n    public void removeCategoriesFromProcess(final long processDefinitionId, final List<Long> categoryIds)\n            throws DeletionException {\n        try {\n            final CategoryService categoryService = getServiceAccessor().getCategoryService();\n            final TransactionContent transactionContent = new RemoveCategoriesFromProcessDefinition(processDefinitionId,\n                    categoryIds, categoryService);\n            transactionContent.execute();\n        } catch (final SBonitaException sbe) {\n            throw new DeletionException(sbe);\n        }\n    }\n\n    @Override\n    public void addProcessDefinitionToCategory(final long categoryId, final long processDefinitionId)\n            throws CreationException {\n        try {\n            final CategoryService categoryService = getServiceAccessor().getCategoryService();\n            final ProcessDefinitionService processDefinitionService = getServiceAccessor()\n                    .getProcessDefinitionService();\n            final TransactionContent transactionContent = new AddProcessDefinitionToCategory(categoryId,\n                    processDefinitionId, categoryService,\n                    processDefinitionService);\n            transactionContent.execute();\n        } catch (final SCategoryInProcessAlreadyExistsException cipaee) {\n            throw new AlreadyExistsException(cipaee);\n        } catch (final SBonitaException sbe) {\n            throw new CreationException(sbe);\n        }\n    }\n\n    @Override\n    public void addProcessDefinitionsToCategory(final long categoryId, final List<Long> processDefinitionIds)\n            throws CreationException {\n        try {\n            final CategoryService categoryService = getServiceAccessor().getCategoryService();\n            final ProcessDefinitionService processDefinitionService = getServiceAccessor()\n                    .getProcessDefinitionService();\n            for (final Long processDefinitionId : processDefinitionIds) {\n                new AddProcessDefinitionToCategory(categoryId, processDefinitionId, categoryService,\n                        processDefinitionService).execute();\n            }\n        } catch (final SCategoryInProcessAlreadyExistsException cipaee) {\n            throw new AlreadyExistsException(cipaee);\n        } catch (final SBonitaException sbe) {\n            throw new CreationException(sbe);\n        }\n    }\n\n    @Override\n    public long getNumberOfCategories(final long processDefinitionId) {\n        try {\n            final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n            final CategoryService categoryService = serviceAccessor.getCategoryService();\n            final GetNumberOfCategoriesOfProcess getNumberOfCategoriesOfProcess = new GetNumberOfCategoriesOfProcess(\n                    categoryService, processDefinitionId);\n            getNumberOfCategoriesOfProcess.execute();\n            return getNumberOfCategoriesOfProcess.getResult();\n        } catch (final SBonitaException sbe) {\n            throw new RetrieveException(sbe);\n        }\n    }\n\n    @Override\n    public long getNumberOfProcessDefinitionsOfCategory(final long categoryId) {\n        try {\n            final CategoryService categoryService = getServiceAccessor().getCategoryService();\n            return categoryService.getNumberOfProcessDeploymentInfosOfCategory(categoryId);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public List<ProcessDeploymentInfo> getProcessDeploymentInfosOfCategory(final long categoryId, final int startIndex,\n            final int maxResults,\n            final ProcessDeploymentInfoCriterion sortCriterion) {\n        if (sortCriterion == null) {\n            throw new IllegalArgumentException(\"You must to have a criterion to sort your result.\");\n        }\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        final OrderByType order = buildOrderByType(sortCriterion.getOrder());\n        final String field = sortCriterion.getField();\n        try {\n            final QueryOptions queryOptions = new QueryOptions(startIndex, maxResults,\n                    SProcessDefinitionDeployInfo.class, field, order);\n            final List<SProcessDefinitionDeployInfo> sProcessDefinitionDeployInfos = processDefinitionService\n                    .searchProcessDeploymentInfosOfCategory(categoryId, queryOptions);\n            return ModelConvertor.toProcessDeploymentInfo(sProcessDefinitionDeployInfos);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public List<Category> getCategoriesOfProcessDefinition(final long processDefinitionId, final int startIndex,\n            final int maxResults,\n            final CategoryCriterion sortingCriterion) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final CategoryService categoryService = serviceAccessor.getCategoryService();\n        try {\n            final OrderByType order = buildOrderByType(sortingCriterion.getOrder());\n            return ModelConvertor.toCategories(categoryService.getCategoriesOfProcessDefinition(processDefinitionId,\n                    startIndex, maxResults, order));\n        } catch (final SBonitaException sbe) {\n            throw new RetrieveException(sbe);\n        }\n    }\n\n    private OrderByType buildOrderByType(final Order order) {\n        return OrderByType.valueOf(order.name());\n    }\n\n    @Override\n    public List<Category> getCategoriesUnrelatedToProcessDefinition(final long processDefinitionId,\n            final int startIndex, final int maxResults,\n            final CategoryCriterion sortingCriterion) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final CategoryService categoryService = serviceAccessor.getCategoryService();\n        try {\n            final OrderByType order = buildOrderByType(sortingCriterion.getOrder());\n            return ModelConvertor.toCategories(categoryService\n                    .getCategoriesUnrelatedToProcessDefinition(processDefinitionId, startIndex, maxResults, order));\n        } catch (final SBonitaException sbe) {\n            throw new RetrieveException(sbe);\n        }\n    }\n\n    @Override\n    public void updateCategory(final long categoryId, final CategoryUpdater updater)\n            throws CategoryNotFoundException, UpdateException {\n        if (updater == null || updater.getFields().isEmpty()) {\n            throw new UpdateException(\"The update descriptor does not contain field updates\");\n        }\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final CategoryService categoryService = serviceAccessor.getCategoryService();\n\n        try {\n            final SCategoryUpdateBuilderFactory fact = BuilderFactory.get(SCategoryUpdateBuilderFactory.class);\n            final EntityUpdateDescriptor updateDescriptor = getCategoryUpdateDescriptor(fact.createNewInstance(),\n                    updater);\n            final UpdateCategory updateCategory = new UpdateCategory(categoryService, categoryId, updateDescriptor);\n            updateCategory.execute();\n        } catch (final SCategoryNotFoundException scnfe) {\n            throw new CategoryNotFoundException(scnfe);\n        } catch (final SBonitaException sbe) {\n            throw new UpdateException(sbe);\n        }\n    }\n\n    private EntityUpdateDescriptor getCategoryUpdateDescriptor(final SCategoryUpdateBuilder descriptorBuilder,\n            final CategoryUpdater updater) {\n        final Map<CategoryField, Serializable> fields = updater.getFields();\n        final String name = (String) fields.get(CategoryField.NAME);\n        if (name != null) {\n            descriptorBuilder.updateName(name);\n        }\n        final String description = (String) fields.get(CategoryField.DESCRIPTION);\n        if (description != null) {\n            descriptorBuilder.updateDescription(description);\n        }\n        return descriptorBuilder.done();\n    }\n\n    @Override\n    public void deleteCategory(final long categoryId) throws DeletionException {\n        if (categoryId <= 0) {\n            throw new DeletionException(\"Category id can not be less than 0!\");\n        }\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final CategoryService categoryService = serviceAccessor.getCategoryService();\n\n        final DeleteSCategory deleteSCategory = new DeleteSCategory(categoryService, categoryId);\n        try {\n            deleteSCategory.execute();\n        } catch (final SCategoryNotFoundException scnfe) {\n            throw new DeletionException(new CategoryNotFoundException(scnfe));\n        } catch (final SBonitaException sbe) {\n            throw new DeletionException(sbe);\n        }\n    }\n\n    @Override\n    public long getNumberOfUncategorizedProcessDefinitions() {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        final CategoryService categoryService = serviceAccessor.getCategoryService();\n        try {\n            final List<Long> processDefinitionIds = processDefinitionService.getProcessDefinitionIds(0,\n                    Integer.MAX_VALUE);\n            long number;\n            if (processDefinitionIds.isEmpty()) {\n                number = 0;\n            } else {\n                number = processDefinitionIds.size()\n                        - categoryService.getNumberOfCategorizedProcessIds(processDefinitionIds);\n            }\n            return number;\n        } catch (final SBonitaException e) {\n            throw new BonitaRuntimeException(e);// TODO refactor exceptions\n        }\n    }\n\n    @Override\n    public List<ProcessDeploymentInfo> getUncategorizedProcessDeploymentInfos(final int startIndex,\n            final int maxResults,\n            final ProcessDeploymentInfoCriterion sortCriterion) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        try {\n            final QueryOptions queryOptions = new QueryOptions(startIndex, maxResults,\n                    SProcessDefinitionDeployInfo.class, sortCriterion.getField(),\n                    buildOrderByType(sortCriterion.getOrder()));\n            final List<SProcessDefinitionDeployInfo> processDefinitionDeployInfos = processDefinitionService\n                    .searchUncategorizedProcessDeploymentInfos(queryOptions);\n            return ModelConvertor.toProcessDeploymentInfo(processDefinitionDeployInfos);\n        } catch (final SBonitaException sbe) {\n            throw new RetrieveException(sbe);\n        }\n    }\n\n    @Override\n    public long getNumberOfProcessDeploymentInfosUnrelatedToCategory(final long categoryId) {\n        try {\n            final ServiceAccessor serviceAccessor = getServiceAccessor();\n            final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n\n            final GetNumberOfProcessDeploymentInfosUnrelatedToCategory transactionContentWithResult = new GetNumberOfProcessDeploymentInfosUnrelatedToCategory(\n                    categoryId, processDefinitionService);\n            transactionContentWithResult.execute();\n            return transactionContentWithResult.getResult();\n        } catch (final SBonitaException sbe) {\n            throw new RetrieveException(sbe);\n        }\n    }\n\n    @Override\n    public List<ProcessDeploymentInfo> getProcessDeploymentInfosUnrelatedToCategory(final long categoryId,\n            final int startIndex, final int maxResults,\n            final ProcessDeploymentInfoCriterion sortingCriterion) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n\n        try {\n            return ModelConvertor.toProcessDeploymentInfo(\n                    processDefinitionService.getProcessDeploymentInfosUnrelatedToCategory(categoryId, startIndex,\n                            maxResults, sortingCriterion));\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public long removeCategoriesFromProcessDefinition(final long processDefinitionId, final int startIndex,\n            final int maxResults) throws DeletionException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final CategoryService categoryService = serviceAccessor.getCategoryService();\n        final SProcessCategoryMappingBuilderFactory fact = BuilderFactory\n                .get(SProcessCategoryMappingBuilderFactory.class);\n\n        try {\n            final FilterOption filterOption = new FilterOption(SProcessCategoryMapping.class, fact.getProcessIdKey(),\n                    processDefinitionId);\n            final OrderByOption order = new OrderByOption(SProcessCategoryMapping.class, fact.getIdKey(),\n                    OrderByType.ASC);\n            final QueryOptions queryOptions = new QueryOptions(startIndex, maxResults, Collections.singletonList(order),\n                    Collections.singletonList(filterOption), null);\n            final List<SProcessCategoryMapping> processCategoryMappings = categoryService\n                    .searchProcessCategoryMappings(queryOptions);\n            return categoryService.deleteProcessCategoryMappings(processCategoryMappings);\n        } catch (final SBonitaException e) {\n            throw new DeletionException(e);\n        }\n    }\n\n    private void removeAllCategoriesFromProcessDefinition(final long processDefinitionId)\n            throws DeletionException {\n        removeCategoriesFromProcessDefinition(processDefinitionId, 0, Integer.MAX_VALUE);\n    }\n\n    @Override\n    public long removeProcessDefinitionsFromCategory(final long categoryId, final int startIndex, final int maxResults)\n            throws DeletionException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final CategoryService categoryService = serviceAccessor.getCategoryService();\n        final SProcessCategoryMappingBuilderFactory fact = BuilderFactory\n                .get(SProcessCategoryMappingBuilderFactory.class);\n\n        try {\n            final FilterOption filterOption = new FilterOption(SProcessCategoryMapping.class, fact.getCategoryIdKey(),\n                    categoryId);\n            final OrderByOption order = new OrderByOption(SProcessCategoryMapping.class, fact.getIdKey(),\n                    OrderByType.ASC);\n            final QueryOptions queryOptions = new QueryOptions(startIndex, maxResults, Collections.singletonList(order),\n                    Collections.singletonList(filterOption), null);\n            final List<SProcessCategoryMapping> processCategoryMappings = categoryService\n                    .searchProcessCategoryMappings(queryOptions);\n            return categoryService.deleteProcessCategoryMappings(processCategoryMappings);\n        } catch (final SBonitaException e) {\n            throw new DeletionException(e);\n        }\n    }\n\n    @Override\n    public List<EventInstance> getEventInstances(final long rootContainerId, final int startIndex, final int maxResults,\n            final EventCriterion criterion) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final EventInstanceService eventInstanceService = serviceAccessor.getEventInstanceService();\n        final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager();\n        final OrderAndField orderAndField = OrderAndFields.getOrderAndFieldForEvent(criterion);\n        final GetEventInstances getEventInstances = new GetEventInstances(eventInstanceService, rootContainerId,\n                startIndex, maxResults,\n                orderAndField.getField(), orderAndField.getOrder());\n        try {\n            getEventInstances.execute();\n            final List<SEventInstance> result = getEventInstances.getResult();\n            return ModelConvertor.toEventInstances(result, flowNodeStateManager);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public void assignUserTask(final long userTaskId, final long userId) throws UpdateException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        try {\n            final AssignOrUnassignUserTask assignUserTask = new AssignOrUnassignUserTask(userId, userTaskId,\n                    activityInstanceService, serviceAccessor.getFlowNodeStateManager().getStateBehaviors());\n            assignUserTask.execute();\n        } catch (final SBonitaException sbe) {\n            throw new UpdateException(sbe);\n        }\n    }\n\n    @Override\n    public void assignUserTaskIfNotAssigned(final long userTaskId, final long userId) throws UpdateException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        try {\n            final AssignUserTaskIfNotAssigned assignUserTask = new AssignUserTaskIfNotAssigned(userId, userTaskId,\n                    activityInstanceService, serviceAccessor.getFlowNodeStateManager().getStateBehaviors());\n            assignUserTask.execute();\n        } catch (final SBonitaException sbe) {\n            throw new UpdateException(\"Unable to assign user task (id: \" + userTaskId + \")\"\n                    + \" to user (id: \" + userId + \") | \" + sbe.getMessage(), sbe);\n        }\n    }\n\n    @CustomTransactions\n    @Override\n    public void assignAndExecuteUserTask(long userId, long userTaskInstanceId, Map<String, Serializable> inputs)\n            throws UserTaskNotFoundException, ContractViolationException, FlowNodeExecutionException {\n        try {\n            inTx(() -> {\n                assignUserTask(userTaskInstanceId, userId);\n                executeFlowNode(userId, userTaskInstanceId, inputs, true);\n                return null;\n            });\n        } catch (final ContractViolationException e) {\n            throw e;\n        } catch (final SFlowNodeNotFoundException e) {\n            throw new UserTaskNotFoundException(\n                    String.format(\"User task %s is not found, it might already be executed\", userTaskInstanceId));\n        } catch (final Exception e) {\n            verifyIfTheActivityWasInTheCorrectStateAndThrowException(userTaskInstanceId, e);\n        }\n    }\n\n    @Override\n    public void updateActorsOfUserTask(final long userTaskId) throws UpdateException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        try {\n            final SHumanTaskInstance humanTaskInstance = getSHumanTaskInstance(userTaskId);\n            if (humanTaskInstance.getStateId() != 4 || humanTaskInstance.isStateExecuting()) {\n                throw new UpdateException(\n                        \"Unable to update actors of the task \" + userTaskId + \" because it is not in ready state\");\n            }\n            final long processDefinitionId = humanTaskInstance.getLogicalGroup(0);\n            final SProcessDefinition processDefinition = serviceAccessor.getProcessDefinitionService()\n                    .getProcessDefinition(processDefinitionId);\n            final SHumanTaskDefinition humanTaskDefinition = (SHumanTaskDefinition) processDefinition\n                    .getProcessContainer().getFlowNode(\n                            humanTaskInstance.getFlowNodeDefinitionId());\n            final long humanTaskInstanceId = humanTaskInstance.getId();\n            if (humanTaskDefinition != null) {\n                final SUserFilterDefinition sUserFilterDefinition = humanTaskDefinition.getSUserFilterDefinition();\n                if (sUserFilterDefinition != null) {\n                    cleanPendingMappingsAndUnassignHumanTask(userTaskId, humanTaskInstance);\n\n                    final FilterResult result = executeFilter(processDefinitionId, humanTaskInstanceId,\n                            humanTaskDefinition.getActorName(),\n                            sUserFilterDefinition);\n                    final List<Long> userIds = result.getResult();\n                    if (userIds == null || userIds.isEmpty() || userIds.contains(0L) || userIds.contains(-1L)) {\n                        throw new UpdateException(\n                                \"no user id returned by the user filter \" + sUserFilterDefinition + \" on activity \"\n                                        + humanTaskDefinition.getName());\n                    }\n                    createPendingMappingsAndAssignHumanTask(humanTaskInstanceId, result);\n                }\n            }\n            log.info(\"User '\" + getUserNameFromSession() + \"' has re-executed assignation on activity \"\n                    + humanTaskInstanceId +\n                    \" of process instance \" + humanTaskInstance.getLogicalGroup(1) + \" of process named '\" +\n                    processDefinition.getName() + \"' in version \" + processDefinition.getVersion());\n        } catch (final SBonitaException sbe) {\n            throw new UpdateException(sbe);\n        }\n    }\n\n    String getUserNameFromSession() {\n        return SessionInfos.getUserNameFromSession();\n    }\n\n    private void createPendingMappingsAndAssignHumanTask(final long humanTaskInstanceId, final FilterResult result)\n            throws SBonitaException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final List<Long> userIds = result.getResult();\n        for (final Long userId : userIds) {\n            createPendingMappingForUser(humanTaskInstanceId, userId);\n        }\n        if (userIds.size() == 1 && result.shouldAutoAssignTaskIfSingleResult()) {\n            serviceAccessor.getActivityInstanceService().assignHumanTask(humanTaskInstanceId, userIds.get(0));\n        }\n    }\n\n    private FilterResult executeFilter(final long processDefinitionId, final long humanTaskInstanceId,\n            final String actorName,\n            final SUserFilterDefinition sUserFilterDefinition)\n            throws SUserFilterExecutionException, SClassLoaderException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService();\n        final UserFilterService userFilterService = serviceAccessor.getUserFilterService();\n        final ClassLoader processClassloader = classLoaderService.getClassLoader(\n                identifier(ScopeType.PROCESS, processDefinitionId));\n        final SExpressionContext expressionContext = new SExpressionContext(humanTaskInstanceId,\n                DataInstanceContainer.ACTIVITY_INSTANCE.name(),\n                processDefinitionId);\n        return userFilterService.executeFilter(processDefinitionId, sUserFilterDefinition,\n                sUserFilterDefinition.getInputs(), processClassloader,\n                expressionContext, actorName);\n    }\n\n    private void cleanPendingMappingsAndUnassignHumanTask(final long userTaskId,\n            final SHumanTaskInstance humanTaskInstance) throws SFlowNodeNotFoundException,\n            SFlowNodeReadException, SActivityModificationException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        // release\n        if (humanTaskInstance.getAssigneeId() > 0) {\n            activityInstanceService.assignHumanTask(userTaskId, 0);\n        }\n        activityInstanceService.deletePendingMappings(humanTaskInstance.getId());\n    }\n\n    private SHumanTaskInstance getSHumanTaskInstance(final long userTaskId)\n            throws SFlowNodeNotFoundException, SFlowNodeReadException, UpdateException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final SFlowNodeInstance flowNodeInstance = serviceAccessor.getActivityInstanceService()\n                .getFlowNodeInstance(userTaskId);\n        if (!(flowNodeInstance instanceof SHumanTaskInstance)) {\n            throw new UpdateException(\"The identifier does not refer to a human task\");\n        }\n        return (SHumanTaskInstance) flowNodeInstance;\n    }\n\n    private void createPendingMappingForUser(final long humanTaskInstanceId, final Long userId)\n            throws SBonitaException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final SPendingActivityMapping mapping = SPendingActivityMapping.builder().activityId(humanTaskInstanceId)\n                .userId(userId)\n                .build();\n        activityInstanceService.addPendingActivityMappings(mapping);\n    }\n\n    @Override\n    public List<DataDefinition> getActivityDataDefinitions(final long processDefinitionId, final String activityName,\n            final int startIndex,\n            final int maxResults)\n            throws ActivityDefinitionNotFoundException, ProcessDefinitionNotFoundException {\n        List<DataDefinition> subDataDefinitionList;\n        List<SDataDefinition> sdataDefinitionList = Collections.emptyList();\n        final ServiceAccessor serviceAccessor;\n        serviceAccessor = getServiceAccessor();\n\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        try {\n            final SProcessDefinition sProcessDefinition = processDefinitionService\n                    .getProcessDefinition(processDefinitionId);\n            boolean activityFound = false;\n            final SFlowElementContainerDefinition processContainer = sProcessDefinition.getProcessContainer();\n            final Set<SActivityDefinition> activityDefList = processContainer.getActivities();\n            for (final SActivityDefinition sActivityDefinition : activityDefList) {\n                if (activityName.equals(sActivityDefinition.getName())) {\n                    sdataDefinitionList = sActivityDefinition.getSDataDefinitions();\n                    activityFound = true;\n                    break;\n                }\n            }\n            if (!activityFound) {\n                throw new ActivityDefinitionNotFoundException(activityName);\n            }\n            final List<DataDefinition> dataDefinitionList = ModelConvertor.toDataDefinitions(sdataDefinitionList);\n            if (startIndex >= dataDefinitionList.size()) {\n                return Collections.emptyList();\n            }\n            final int toIndex = Math.min(dataDefinitionList.size(), startIndex + maxResults);\n            subDataDefinitionList = new ArrayList<>(dataDefinitionList.subList(startIndex, toIndex));\n        } catch (final SProcessDefinitionNotFoundException e) {\n            throw new ProcessDefinitionNotFoundException(e);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n        return subDataDefinitionList;\n    }\n\n    @Override\n    public List<DataDefinition> getProcessDataDefinitions(final long processDefinitionId, final int startIndex,\n            final int maxResults)\n            throws ProcessDefinitionNotFoundException {\n        List<DataDefinition> subDataDefinitionList;\n        final ServiceAccessor serviceAccessor;\n        serviceAccessor = getServiceAccessor();\n\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        try {\n            final SProcessDefinition sProcessDefinition = processDefinitionService\n                    .getProcessDefinition(processDefinitionId);\n            final SFlowElementContainerDefinition processContainer = sProcessDefinition.getProcessContainer();\n            final List<SDataDefinition> sdataDefinitionList = processContainer.getDataDefinitions();\n            final List<DataDefinition> dataDefinitionList = ModelConvertor.toDataDefinitions(sdataDefinitionList);\n            if (startIndex >= dataDefinitionList.size()) {\n                return Collections.emptyList();\n            }\n            final int toIndex = Math.min(dataDefinitionList.size(), startIndex + maxResults);\n            subDataDefinitionList = new ArrayList<>(dataDefinitionList.subList(startIndex, toIndex));\n            return subDataDefinitionList;\n        } catch (final SProcessDefinitionNotFoundException e) {\n            throw new ProcessDefinitionNotFoundException(e);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public HumanTaskInstance getHumanTaskInstance(final long activityInstanceId)\n            throws ActivityInstanceNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager();\n        final GetHumanTaskInstance getHumanTaskInstance = new GetHumanTaskInstance(activityInstanceService,\n                activityInstanceId);\n        try {\n            getHumanTaskInstance.execute();\n        } catch (final SActivityInstanceNotFoundException e) {\n            throw new ActivityInstanceNotFoundException(activityInstanceId, e);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n        return ModelConvertor.toHumanTaskInstance(getHumanTaskInstance.getResult(), flowNodeStateManager);\n    }\n\n    @Override\n    public long getNumberOfAssignedHumanTaskInstances(final long userId) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        try {\n            return activityInstanceService.getNumberOfAssignedHumanTaskInstances(userId);\n        } catch (final SActivityReadException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public Map<Long, Long> getNumberOfOpenTasks(final List<Long> userIds) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n\n        try {\n            final GetNumberOfOpenTasksForUsers transactionContent = new GetNumberOfOpenTasksForUsers(userIds,\n                    activityInstanceService);\n            transactionContent.execute();\n            return transactionContent.getResult();\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public Map<String, byte[]> getProcessResources(final long processDefinitionId, final String filenamesPattern)\n            throws RetrieveException {\n        try {\n            BusinessArchive businessArchive = getServiceAccessor().getBusinessArchiveService()\n                    .export(processDefinitionId);\n            return businessArchive.getResources(filenamesPattern);\n        } catch (SBonitaException | InvalidBusinessArchiveFormatException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public byte[] getExternalProcessResource(final long processDefinitionId, final String fileName)\n            throws RetrieveException, FileNotFoundException {\n        SBARResource resource;\n        try {\n            resource = getServiceAccessor().getProcessResourcesService().get(processDefinitionId,\n                    BARResourceType.EXTERNAL, fileName);\n        } catch (SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n        if (resource == null) {\n            throw new FileNotFoundException(\"No resource named \" + fileName + \" in process \" + processDefinitionId);\n        }\n        return resource.getContent();\n    }\n\n    @Override\n    public byte[] getDocumentProcessResource(final long processDefinitionId, final String fileName)\n            throws RetrieveException, ProcessResourceNotFoundException {\n        SBARResource resource;\n        try {\n            resource = getServiceAccessor().getProcessResourcesService().get(processDefinitionId,\n                    BARResourceType.DOCUMENT, fileName);\n        } catch (SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n        if (resource == null) {\n            throw new ProcessResourceNotFoundException(\n                    \"No resource named \" + fileName + \" in the documents of process \" + processDefinitionId);\n        }\n        return resource.getContent();\n    }\n\n    @Override\n    public long getLatestProcessDefinitionId(final String processName) throws ProcessDefinitionNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n\n        final TransactionContentWithResult<Long> transactionContent = new GetLatestProcessDefinitionId(\n                processDefinitionService, processName);\n        try {\n            transactionContent.execute();\n        } catch (final SBonitaException e) {\n            throw new ProcessDefinitionNotFoundException(e);\n        }\n        return transactionContent.getResult();\n    }\n\n    @Override\n    public List<DataInstance> getProcessDataInstances(final long processInstanceId, final int startIndex,\n            final int maxResults) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final DataInstanceService dataInstanceService = serviceAccessor.getDataInstanceService();\n        final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService();\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n        final ParentContainerResolver parentContainerResolver = serviceAccessor.getParentContainerResolver();\n        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        try {\n            final long processDefinitionId = processInstanceService.getProcessInstance(processInstanceId)\n                    .getProcessDefinitionId();\n            final ClassLoader processClassLoader = classLoaderService.getClassLoader(\n                    identifier(ScopeType.PROCESS, processDefinitionId));\n            Thread.currentThread().setContextClassLoader(processClassLoader);\n            final List<SDataInstance> dataInstances = dataInstanceService.getDataInstances(processInstanceId,\n                    DataInstanceContainer.PROCESS_INSTANCE.name(),\n                    parentContainerResolver,\n                    startIndex, maxResults);\n            return convertModelToDataInstances(dataInstances);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        } finally {\n            Thread.currentThread().setContextClassLoader(contextClassLoader);\n        }\n    }\n\n    @Override\n    public DataInstance getProcessDataInstance(final String dataName, final long processInstanceId)\n            throws DataNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final DataInstanceService dataInstanceService = serviceAccessor.getDataInstanceService();\n        final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService();\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n        final ParentContainerResolver parentContainerResolver = serviceAccessor.getParentContainerResolver();\n        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        try {\n            final long processDefinitionId = processInstanceService.getProcessInstance(processInstanceId)\n                    .getProcessDefinitionId();\n            final ClassLoader processClassLoader = classLoaderService.getClassLoader(\n                    identifier(ScopeType.PROCESS, processDefinitionId));\n            Thread.currentThread().setContextClassLoader(processClassLoader);\n            final SDataInstance sDataInstance = dataInstanceService.getDataInstance(dataName, processInstanceId,\n                    DataInstanceContainer.PROCESS_INSTANCE.toString(), parentContainerResolver);\n            return convertModeltoDataInstance(sDataInstance);\n        } catch (final SBonitaException e) {\n            throw new DataNotFoundException(e);\n        } finally {\n            Thread.currentThread().setContextClassLoader(contextClassLoader);\n        }\n    }\n\n    @Override\n    public void updateProcessDataInstance(final String dataName, final long processInstanceId,\n            final Serializable dataValue) throws UpdateException {\n        updateProcessDataInstances(processInstanceId, singletonMap(dataName, dataValue));\n    }\n\n    @Override\n    public void updateProcessDataInstances(final long processInstanceId, final Map<String, Serializable> dataNameValues)\n            throws UpdateException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final DataInstanceService dataInstanceService = serviceAccessor.getDataInstanceService();\n        final ParentContainerResolver parentContainerResolver = serviceAccessor.getParentContainerResolver();\n        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        try {\n            final ClassLoader processClassLoader = getProcessInstanceClassloader(serviceAccessor, processInstanceId);\n            Thread.currentThread().setContextClassLoader(processClassLoader);\n            final List<String> dataNames = new ArrayList<>(dataNameValues.keySet());\n            final List<SDataInstance> sDataInstances = dataInstanceService.getDataInstances(dataNames,\n                    processInstanceId,\n                    DataInstanceContainer.PROCESS_INSTANCE.toString(), parentContainerResolver);\n            updateDataInstances(sDataInstances, dataNameValues, processClassLoader);\n        } catch (final SBonitaException | ClassNotFoundException e) {\n            throw new UpdateException(e);\n        } finally {\n            Thread.currentThread().setContextClassLoader(contextClassLoader);\n        }\n    }\n\n    protected void updateDataInstances(final List<SDataInstance> sDataInstances,\n            final Map<String, Serializable> dataNameValues, ClassLoader classLoader)\n            throws ClassNotFoundException,\n            UpdateException, SDataInstanceException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final DataInstanceService dataInstanceService = serviceAccessor.getDataInstanceService();\n        for (final SDataInstance sDataInstance : sDataInstances) {\n            final Serializable dataValue = dataNameValues.get(sDataInstance.getName());\n            updateDataInstance(dataInstanceService, sDataInstance, dataValue, classLoader);\n        }\n    }\n\n    protected void updateDataInstance(final DataInstanceService dataInstanceService, final SDataInstance sDataInstance,\n            final Serializable dataNewValue,\n            ClassLoader classLoader)\n            throws UpdateException, SDataInstanceException {\n        verifyTypeOfNewDataValue(sDataInstance, dataNewValue, classLoader);\n\n        final EntityUpdateDescriptor entityUpdateDescriptor = buildEntityUpdateDescriptorForData(dataNewValue);\n        dataInstanceService.updateDataInstance(sDataInstance, entityUpdateDescriptor);\n    }\n\n    protected void verifyTypeOfNewDataValue(final SDataInstance sDataInstance, final Serializable dataValue,\n            ClassLoader classLoader) throws UpdateException {\n        final String dataClassName = sDataInstance.getClassName();\n        Class<?> dataClass;\n        try {\n            dataClass = classLoader.loadClass(dataClassName);\n        } catch (final ClassNotFoundException e) {\n            throw new UpdateException(e);\n        }\n        if (!dataClass.isInstance(dataValue)) {\n            final UpdateException e = new UpdateException(\"The type of new value [\" + dataValue.getClass().getName()\n                    + \"] is not compatible with the type of the data.\");\n            e.setDataName(sDataInstance.getName());\n            e.setDataClassName(dataClassName);\n            throw e;\n        }\n    }\n\n    private EntityUpdateDescriptor buildEntityUpdateDescriptorForData(final Serializable dataValue) {\n        final EntityUpdateDescriptor entityUpdateDescriptor = new EntityUpdateDescriptor();\n        entityUpdateDescriptor.addField(\"value\", dataValue);\n        return entityUpdateDescriptor;\n    }\n\n    protected ClassLoader getProcessInstanceClassloader(final ServiceAccessor serviceAccessor,\n            final long processInstanceId)\n            throws SProcessInstanceNotFoundException, SProcessInstanceReadException, SClassLoaderException {\n        final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService();\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n        final long processDefinitionId = processInstanceService.getProcessInstance(processInstanceId)\n                .getProcessDefinitionId();\n        return classLoaderService.getClassLoader(identifier(ScopeType.PROCESS, processDefinitionId));\n    }\n\n    @Override\n    public List<DataInstance> getActivityDataInstances(final long activityInstanceId, final int startIndex,\n            final int maxResults) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final DataInstanceService dataInstanceService = serviceAccessor.getDataInstanceService();\n        final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService();\n        final ParentContainerResolver parentContainerResolver = serviceAccessor.getParentContainerResolver();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final int processDefinitionIndex = BuilderFactory.get(SAutomaticTaskInstanceBuilderFactory.class)\n                .getProcessDefinitionIndex();\n        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        try {\n            final long parentProcessInstanceId = activityInstanceService.getFlowNodeInstance(activityInstanceId)\n                    .getLogicalGroup(processDefinitionIndex);\n            final ClassLoader processClassLoader = classLoaderService.getClassLoader(\n                    identifier(ScopeType.PROCESS, parentProcessInstanceId));\n            Thread.currentThread().setContextClassLoader(processClassLoader);\n            final List<SDataInstance> dataInstances = dataInstanceService.getDataInstances(activityInstanceId,\n                    DataInstanceContainer.ACTIVITY_INSTANCE.name(),\n                    parentContainerResolver,\n                    startIndex, maxResults);\n            return convertModelToDataInstances(dataInstances);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        } finally {\n            Thread.currentThread().setContextClassLoader(contextClassLoader);\n        }\n    }\n\n    @Override\n    public DataInstance getActivityDataInstance(final String dataName, final long activityInstanceId)\n            throws DataNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final DataInstanceService dataInstanceService = serviceAccessor.getDataInstanceService();\n        final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService();\n        final ParentContainerResolver parentContainerResolver = serviceAccessor.getParentContainerResolver();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final int processDefinitionIndex = BuilderFactory.get(SAutomaticTaskInstanceBuilderFactory.class)\n                .getProcessDefinitionIndex();\n        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        try {\n            final SFlowNodeInstance flowNodeInstance = activityInstanceService.getFlowNodeInstance(activityInstanceId);\n            SDataInstance data;\n            final long parentProcessInstanceId = flowNodeInstance.getLogicalGroup(processDefinitionIndex);\n            final ClassLoader processClassLoader = classLoaderService.getClassLoader(\n                    identifier(ScopeType.PROCESS, parentProcessInstanceId));\n            Thread.currentThread().setContextClassLoader(processClassLoader);\n            data = dataInstanceService.getDataInstance(dataName, activityInstanceId,\n                    DataInstanceContainer.ACTIVITY_INSTANCE.toString(),\n                    parentContainerResolver);\n            return convertModeltoDataInstance(data);\n        } catch (final SBonitaException e) {\n            throw new DataNotFoundException(e);\n        } finally {\n            Thread.currentThread().setContextClassLoader(contextClassLoader);\n        }\n    }\n\n    @Override\n    public void updateActivityDataInstance(final String dataName, final long activityInstanceId,\n            final Serializable dataValue) throws UpdateException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final DataInstanceService dataInstanceService = serviceAccessor.getDataInstanceService();\n        final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService();\n        final ParentContainerResolver parentContainerResolver = serviceAccessor.getParentContainerResolver();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final int processDefinitionIndex = BuilderFactory.get(SAutomaticTaskInstanceBuilderFactory.class)\n                .getProcessDefinitionIndex();\n        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        try {\n            final SFlowNodeInstance flowNodeInstance = activityInstanceService.getFlowNodeInstance(activityInstanceId);\n            final long parentProcessInstanceId = flowNodeInstance.getLogicalGroup(processDefinitionIndex);\n            final ClassLoader processClassLoader = classLoaderService.getClassLoader(\n                    identifier(ScopeType.PROCESS, parentProcessInstanceId));\n            Thread.currentThread().setContextClassLoader(processClassLoader);\n            final SDataInstance sDataInstance = dataInstanceService.getDataInstance(dataName, activityInstanceId,\n                    DataInstanceContainer.ACTIVITY_INSTANCE.toString(), parentContainerResolver);\n            updateDataInstance(dataInstanceService, sDataInstance, dataValue, processClassLoader);\n        } catch (final SBonitaException e) {\n            throw new UpdateException(e);\n        } finally {\n            Thread.currentThread().setContextClassLoader(contextClassLoader);\n        }\n    }\n\n    @Override\n    public DataInstance getActivityTransientDataInstance(final String dataName, final long activityInstanceId)\n            throws DataNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final TransientDataService transientDataService = serviceAccessor.getTransientDataService();\n        final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final int processDefinitionIndex = BuilderFactory.get(SAutomaticTaskInstanceBuilderFactory.class)\n                .getProcessDefinitionIndex();\n        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        try {\n            final SFlowNodeInstance flowNodeInstance = activityInstanceService.getFlowNodeInstance(activityInstanceId);\n            SDataInstance data;\n            final long parentProcessInstanceId = flowNodeInstance.getLogicalGroup(processDefinitionIndex);\n            final ClassLoader processClassLoader = classLoaderService.getClassLoader(\n                    identifier(ScopeType.PROCESS, parentProcessInstanceId));\n            Thread.currentThread().setContextClassLoader(processClassLoader);\n            data = transientDataService.getDataInstance(dataName, activityInstanceId,\n                    DataInstanceContainer.ACTIVITY_INSTANCE.toString());\n            return convertModeltoDataInstance(data);\n        } catch (final SBonitaException e) {\n            throw new DataNotFoundException(e);\n        } finally {\n            Thread.currentThread().setContextClassLoader(contextClassLoader);\n        }\n    }\n\n    /**\n     * isolate static call for mocking reasons\n     */\n    protected DataInstance convertModeltoDataInstance(final SDataInstance data) {\n        return ModelConvertor.toDataInstance(data);\n    }\n\n    @Override\n    public List<DataInstance> getActivityTransientDataInstances(final long activityInstanceId, final int startIndex,\n            final int maxResults) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final TransientDataService transientDataService = serviceAccessor.getTransientDataService();\n        final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final int processDefinitionIndex = BuilderFactory.get(SAutomaticTaskInstanceBuilderFactory.class)\n                .getProcessDefinitionIndex();\n        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        try {\n            final long parentProcessInstanceId = activityInstanceService.getFlowNodeInstance(activityInstanceId)\n                    .getLogicalGroup(processDefinitionIndex);\n            final ClassLoader processClassLoader = classLoaderService.getClassLoader(\n                    identifier(ScopeType.PROCESS, parentProcessInstanceId));\n            Thread.currentThread().setContextClassLoader(processClassLoader);\n            final List<SDataInstance> dataInstances = transientDataService.getDataInstances(activityInstanceId,\n                    DataInstanceContainer.ACTIVITY_INSTANCE.name(),\n                    startIndex, maxResults);\n            return convertModelToDataInstances(dataInstances);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        } finally {\n            Thread.currentThread().setContextClassLoader(contextClassLoader);\n        }\n    }\n\n    /**\n     * isolate static call for mocking reasons\n     */\n    protected List<DataInstance> convertModelToDataInstances(final List<SDataInstance> dataInstances) {\n        return ModelConvertor.toDataInstances(dataInstances);\n    }\n\n    @Override\n    public void updateActivityTransientDataInstance(final String dataName, final long activityInstanceId,\n            final Serializable dataValue) throws UpdateException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final TransientDataService transientDataService = serviceAccessor.getTransientDataService();\n        final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final int processDefinitionIndex = BuilderFactory.get(SAutomaticTaskInstanceBuilderFactory.class)\n                .getProcessDefinitionIndex();\n        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        try {\n            final SFlowNodeInstance flowNodeInstance = activityInstanceService.getFlowNodeInstance(activityInstanceId);\n            final long parentProcessInstanceId = flowNodeInstance.getLogicalGroup(processDefinitionIndex);\n            final ClassLoader processClassLoader = classLoaderService.getClassLoader(\n                    identifier(ScopeType.PROCESS, parentProcessInstanceId));\n            Thread.currentThread().setContextClassLoader(processClassLoader);\n            updateTransientData(dataName, activityInstanceId, dataValue, transientDataService, processClassLoader);\n        } catch (final SBonitaException e) {\n            throw new UpdateException(e);\n        } finally {\n            Thread.currentThread().setContextClassLoader(contextClassLoader);\n        }\n\n    }\n\n    protected void updateTransientData(final String dataName, final long activityInstanceId,\n            final Serializable dataValue,\n            final TransientDataService transientDataInstanceService, ClassLoader classLoader)\n            throws SDataInstanceException, UpdateException {\n        final SDataInstance sDataInstance = transientDataInstanceService.getDataInstance(dataName, activityInstanceId,\n                DataInstanceContainer.ACTIVITY_INSTANCE.toString());\n        verifyTypeOfNewDataValue(sDataInstance, dataValue, classLoader);\n        final EntityUpdateDescriptor entityUpdateDescriptor = buildEntityUpdateDescriptorForData(dataValue);\n        transientDataInstanceService.updateDataInstance(sDataInstance, entityUpdateDescriptor);\n    }\n\n    @Override\n    public void importActorMapping(final long processDefinitionId, final String xmlContent)\n            throws ActorMappingImportException {\n        if (xmlContent != null) {\n            final ServiceAccessor serviceAccessor = getServiceAccessor();\n            final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService();\n            final IdentityService identityService = serviceAccessor.getIdentityService();\n            try {\n                new ImportActorMapping(actorMappingService, identityService).importActorMappingFromXml(xmlContent,\n                        processDefinitionId);\n                serviceAccessor.getBusinessArchiveArtifactsManager().resolveDependencies(processDefinitionId,\n                        serviceAccessor);\n            } catch (final SBonitaException sbe) {\n                throw new ActorMappingImportException(sbe);\n            }\n        }\n    }\n\n    @Override\n    public String exportActorMapping(final long processDefinitionId) throws ActorMappingExportException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        try {\n            final ExportActorMapping exportActorMapping = new ExportActorMapping(actorMappingService, identityService,\n                    processDefinitionId);\n            exportActorMapping.execute();\n            return exportActorMapping.getResult();\n        } catch (final SBonitaException sbe) {\n            throw new ActorMappingExportException(sbe);\n        }\n    }\n\n    @Override\n    public boolean isInvolvedInProcessInstance(final long userId, final long processInstanceId)\n            throws ProcessInstanceNotFoundException {\n        return processInvolvementDelegate.isInvolvedInProcessInstance(userId, processInstanceId);\n    }\n\n    public boolean isInvolvedInHumanTaskInstance(long userId, long humanTaskInstanceId)\n            throws ActivityInstanceNotFoundException {\n        return taskInvolvementDelegate.isInvolvedInHumanTaskInstance(userId, humanTaskInstanceId);\n    }\n\n    @Override\n    public boolean isManagerOfUserInvolvedInProcessInstance(final long managerUserId, final long processInstanceId)\n            throws BonitaException {\n        return processInvolvementDelegate.isManagerOfUserInvolvedInProcessInstance(managerUserId, processInstanceId);\n    }\n\n    @Override\n    public long getProcessInstanceIdFromActivityInstanceId(final long activityInstanceId)\n            throws ProcessInstanceNotFoundException {\n        try {\n            final SActivityInstance sActivityInstance = getSActivityInstance(activityInstanceId);\n            return sActivityInstance.getRootContainerId();\n        } catch (final SActivityInstanceNotFoundException e) {\n            logInstanceNotFound(e);\n            throw new ProcessInstanceNotFoundException(e);\n        } catch (final SBonitaException e) {\n            logError(e);\n            throw new ProcessInstanceNotFoundException(e);\n        }\n    }\n\n    @Override\n    public long getProcessDefinitionIdFromActivityInstanceId(final long activityInstanceId)\n            throws ProcessDefinitionNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n        try {\n            final SActivityInstance sActivityInstance = getSActivityInstance(activityInstanceId);\n            return processInstanceService.getProcessInstance(sActivityInstance.getParentProcessInstanceId())\n                    .getProcessDefinitionId();\n        } catch (final SBonitaException e) {\n            throw new ProcessDefinitionNotFoundException(e);\n        }\n    }\n\n    @Override\n    public long getProcessDefinitionIdFromProcessInstanceId(final long processInstanceId)\n            throws ProcessDefinitionNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        try {\n            final SProcessInstance sProcessInstance = getSProcessInstance(processInstanceId);\n            final ProcessInstance processInstance = ModelConvertor\n                    .toProcessInstances(Collections.singletonList(sProcessInstance),\n                            processDefinitionService)\n                    .get(0);\n            return processInstance.getProcessDefinitionId();\n        } catch (final SProcessInstanceNotFoundException e) {\n            logInstanceNotFound(e);\n            throw new ProcessDefinitionNotFoundException(e);\n        } catch (final SBonitaException e) {\n            logError(e);\n            throw new ProcessDefinitionNotFoundException(e);\n        }\n    }\n\n    @Override\n    public Date getActivityReachedStateDate(final long activityInstanceId, final String stateName) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n\n        final int stateId = ModelConvertor.getServerActivityStateId(stateName);\n        final GetArchivedActivityInstance getArchivedActivityInstance = new GetArchivedActivityInstance(\n                activityInstanceId, stateId, activityInstanceService);\n        try {\n            getArchivedActivityInstance.execute();\n            final long reachedDate = getArchivedActivityInstance.getResult().getReachedStateDate();\n            return new Date(reachedDate);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public Set<String> getSupportedStates(final FlowNodeType nodeType) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager();\n        return flowNodeStateManager.getSupportedState(SFlowNodeType.valueOf(nodeType.toString()));\n    }\n\n    @Override\n    public void updateActivityInstanceVariables(final long activityInstanceId,\n            final Map<String, Serializable> variables) throws UpdateException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final DataInstanceService dataInstanceService = serviceAccessor.getDataInstanceService();\n        final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService();\n        final ParentContainerResolver parentContainerResolver = serviceAccessor.getParentContainerResolver();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final int processDefinitionIndex = BuilderFactory.get(SAutomaticTaskInstanceBuilderFactory.class)\n                .getProcessDefinitionIndex();\n        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        try {\n            final long parentProcessInstanceId = activityInstanceService.getFlowNodeInstance(activityInstanceId)\n                    .getLogicalGroup(processDefinitionIndex);\n            final ClassLoader processClassLoader = classLoaderService.getClassLoader(\n                    identifier(ScopeType.PROCESS, parentProcessInstanceId));\n            Thread.currentThread().setContextClassLoader(processClassLoader);\n            final List<SDataInstance> dataInstances = dataInstanceService.getDataInstances(\n                    new ArrayList<>(variables.keySet()), activityInstanceId,\n                    DataInstanceContainer.ACTIVITY_INSTANCE.toString(), parentContainerResolver);\n            if (dataInstances.size() < variables.size()) {\n                throw new UpdateException(\"Some data does not exists, wanted to update \" + variables.keySet()\n                        + \" but there is only \" + dataInstances);\n            }\n            for (final SDataInstance dataInstance : dataInstances) {\n                final Serializable newValue = variables.get(dataInstance.getName());\n                final EntityUpdateDescriptor entityUpdateDescriptor = buildEntityUpdateDescriptorForData(newValue);\n                dataInstanceService.updateDataInstance(dataInstance, entityUpdateDescriptor);\n            }\n        } catch (final SBonitaException e) {\n            throw new UpdateException(e);\n        } finally {\n            Thread.currentThread().setContextClassLoader(contextClassLoader);\n        }\n    }\n\n    @Override\n    public void updateActivityInstanceVariables(final List<Operation> operations, final long activityInstanceId,\n            final Map<String, Serializable> expressionContexts) throws UpdateException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final OperationService operationService = serviceAccessor.getOperationService();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService();\n        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        try {\n            final SActivityInstance activityInstance = activityInstanceService.getActivityInstance(activityInstanceId);\n            final ClassLoader processClassLoader = classLoaderService.getClassLoader(\n                    identifier(ScopeType.PROCESS, activityInstance.getProcessDefinitionId()));\n            Thread.currentThread().setContextClassLoader(processClassLoader);\n            final List<SOperation> sOperations = convertOperations(operations);\n            final SExpressionContext sExpressionContext = new SExpressionContext(activityInstanceId,\n                    DataInstanceContainer.ACTIVITY_INSTANCE.toString(),\n                    activityInstance.getProcessDefinitionId());\n            sExpressionContext.setSerializableInputValues(expressionContexts);\n\n            operationService.execute(sOperations, sExpressionContext);\n        } catch (final SBonitaException e) {\n            throw new UpdateException(e);\n        } finally {\n            Thread.currentThread().setContextClassLoader(contextClassLoader);\n        }\n    }\n\n    protected List<SOperation> convertOperations(final List<Operation> operations) {\n        return ModelConvertor.convertOperations(operations);\n    }\n\n    @Override\n    public long getOneAssignedUserTaskInstanceOfProcessInstance(final long processInstanceId, final long userId)\n            throws RetrieveException {\n        // FIXME: write specific query that should be more efficient:\n        final int assignedUserTaskInstanceNumber = (int) getNumberOfAssignedHumanTaskInstances(userId);\n        final List<HumanTaskInstance> userTaskInstances = getAssignedHumanTaskInstances(userId, 0,\n                assignedUserTaskInstanceNumber,\n                ActivityInstanceCriterion.DEFAULT);\n        for (final HumanTaskInstance userTaskInstance : userTaskInstances) {\n            final String stateName = userTaskInstance.getState();\n            final long userTaskInstanceId = userTaskInstance.getId();\n            if (stateName.equals(ActivityStates.READY_STATE)\n                    && userTaskInstance.getParentContainerId() == processInstanceId) {\n                return userTaskInstanceId;\n            }\n        }\n        return -1;\n    }\n\n    @Override\n    public long getOneAssignedUserTaskInstanceOfProcessDefinition(final long processDefinitionId, final long userId) {\n        final int assignedUserTaskInstanceNumber = (int) getNumberOfAssignedHumanTaskInstances(userId);\n        final List<HumanTaskInstance> userTaskInstances = getAssignedHumanTaskInstances(userId, 0,\n                assignedUserTaskInstanceNumber,\n                ActivityInstanceCriterion.DEFAULT);\n        if (!userTaskInstances.isEmpty()) {\n            for (final HumanTaskInstance userTaskInstance : userTaskInstances) {\n                final String stateName = userTaskInstance.getState();\n                try {\n                    final SProcessInstance sProcessInstance = getSProcessInstance(\n                            userTaskInstance.getRootContainerId());\n                    if (stateName.equals(ActivityStates.READY_STATE)\n                            && sProcessInstance.getProcessDefinitionId() == processDefinitionId) {\n                        return userTaskInstance.getId();\n                    }\n                } catch (final SBonitaException e) {\n                    throw new RetrieveException(e);\n                }\n            }\n        }\n        return -1;\n    }\n\n    @Override\n    public String getActivityInstanceState(final long activityInstanceId) throws ActivityInstanceNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager();\n        try {\n            final SActivityInstance sActivityInstance = getSActivityInstance(activityInstanceId);\n            final ActivityInstance activityInstance = ModelConvertor.toActivityInstance(sActivityInstance,\n                    flowNodeStateManager);\n            return activityInstance.getState();\n        } catch (final SActivityInstanceNotFoundException e) {\n            logInstanceNotFound(e);\n            throw new ActivityInstanceNotFoundException(activityInstanceId);\n        } catch (final SBonitaException e) {\n            logError(e);\n            throw new ActivityInstanceNotFoundException(activityInstanceId, e);\n        }\n    }\n\n    @Override\n    public boolean canExecuteTask(final long activityInstanceId, final long userId)\n            throws ActivityInstanceNotFoundException, RetrieveException {\n        final HumanTaskInstance userTaskInstance = getHumanTaskInstance(activityInstanceId);\n        return userTaskInstance.getState().equalsIgnoreCase(ActivityStates.READY_STATE)\n                && userTaskInstance.getAssigneeId() == userId;\n    }\n\n    @Override\n    public long getProcessDefinitionId(final String name, final String version)\n            throws ProcessDefinitionNotFoundException {\n        return processDeploymentAPIDelegate.getProcessDefinitionId(name, version);\n    }\n\n    @Override\n    public void releaseUserTask(final long userTaskId) throws ActivityInstanceNotFoundException, UpdateException {\n        final ServiceAccessor serviceAccessor;\n        serviceAccessor = getServiceAccessor();\n\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        try {\n            final AssignOrUnassignUserTask assignUserTask = new AssignOrUnassignUserTask(0, userTaskId,\n                    activityInstanceService, serviceAccessor\n                            .getFlowNodeStateManager().getStateBehaviors());\n            assignUserTask.execute();\n        } catch (final SUnreleasableTaskException e) {\n            throw new UpdateException(e);\n        } catch (final SActivityInstanceNotFoundException e) {\n            throw new ActivityInstanceNotFoundException(userTaskId, e);\n        } catch (final SBonitaException e) {\n            logError(e);\n            throw new UpdateException(e);\n        }\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    @Deprecated(since = \"9.0.0\")\n    public void updateProcessDeploymentInfo(final long processDefinitionId,\n            final ProcessDeploymentInfoUpdater processDeploymentInfoUpdater)\n            throws ProcessDefinitionNotFoundException, UpdateException {\n        if (processDeploymentInfoUpdater == null || processDeploymentInfoUpdater.getFields().isEmpty()) {\n            throw new UpdateException(\"The update descriptor does not contain field updates\");\n        }\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n\n        final UpdateProcessDeploymentInfo updateProcessDeploymentInfo = new UpdateProcessDeploymentInfo(\n                processDefinitionService, processDefinitionId,\n                processDeploymentInfoUpdater);\n        try {\n            updateProcessDeploymentInfo.execute();\n        } catch (final SProcessDefinitionNotFoundException e) {\n            throw new ProcessDefinitionNotFoundException(e);\n        } catch (final SBonitaException sbe) {\n            throw new UpdateException(sbe);\n        }\n    }\n\n    @Override\n    public List<ProcessDeploymentInfo> getStartableProcessDeploymentInfosForActors(final Set<Long> actorIds,\n            final int startIndex, final int maxResults,\n            final ProcessDeploymentInfoCriterion sortingCriterion) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        try {\n            final Set<Long> processDefIds = getIdOfStartableProcessDeploymentInfosForActors(actorIds);\n\n            String field = null;\n            OrderByType order = null;\n            switch (sortingCriterion) {\n                case DEFAULT:\n                    break;\n                case NAME_ASC:\n                    field = SProcessDefinitionDeployInfo.NAME_KEY;\n                    order = OrderByType.ASC;\n                    break;\n                case NAME_DESC:\n                    field = SProcessDefinitionDeployInfo.NAME_KEY;\n                    order = OrderByType.DESC;\n                    break;\n                case ACTIVATION_STATE_ASC:\n                    field = SProcessDefinitionDeployInfo.ACTIVATION_STATE_KEY;\n                    order = OrderByType.ASC;\n                    break;\n                case ACTIVATION_STATE_DESC:\n                    field = SProcessDefinitionDeployInfo.ACTIVATION_STATE_KEY;\n                    order = OrderByType.DESC;\n                    break;\n                case CONFIGURATION_STATE_ASC:\n                    field = SProcessDefinitionDeployInfo.CONFIGURATION_STATE_KEY;\n                    order = OrderByType.ASC;\n                    break;\n                case CONFIGURATION_STATE_DESC:\n                    field = SProcessDefinitionDeployInfo.CONFIGURATION_STATE_KEY;\n                    order = OrderByType.DESC;\n                    break;\n                case VERSION_ASC:\n                    field = SProcessDefinitionDeployInfo.VERSION_KEY;\n                    order = OrderByType.ASC;\n                    break;\n                case VERSION_DESC:\n                    field = SProcessDefinitionDeployInfo.VERSION_KEY;\n                    order = OrderByType.DESC;\n                    break;\n                default:\n                    break;\n            }\n\n            final List<SProcessDefinitionDeployInfo> processDefinitionDeployInfos = processDefinitionService\n                    .getProcessDeploymentInfos(new ArrayList<>(\n                            processDefIds), startIndex, maxResults, field, order);\n            return ModelConvertor.toProcessDeploymentInfo(processDefinitionDeployInfos);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    private Set<Long> getIdOfStartableProcessDeploymentInfosForActors(final Set<Long> actorIds)\n            throws SActorNotFoundException, SBonitaReadException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService();\n\n        final List<SActor> actors = actorMappingService.getActors(new ArrayList<>(actorIds));\n        final Set<Long> processDefIds = new HashSet<>(actors.size());\n        for (final SActor sActor : actors) {\n            if (sActor.isInitiator()) {\n                processDefIds.add(sActor.getScopeId());\n            }\n        }\n        return processDefIds;\n    }\n\n    @Override\n    public boolean isAllowedToStartProcess(final long processDefinitionId, final Set<Long> actorIds) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService();\n\n        final GetActorsByActorIds getActorsByActorIds = new GetActorsByActorIds(actorMappingService,\n                new ArrayList<>(actorIds));\n        try {\n            getActorsByActorIds.execute();\n            final List<SActor> actors = getActorsByActorIds.getResult();\n            boolean isAllowedToStartProcess = true;\n            final Iterator<SActor> iterator = actors.iterator();\n            while (isAllowedToStartProcess && iterator.hasNext()) {\n                final SActor actor = iterator.next();\n                if (actor.getScopeId() != processDefinitionId || !actor.isInitiator()) {\n                    isAllowedToStartProcess = false;\n                }\n            }\n            return isAllowedToStartProcess;\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public ActorInstance getActorInitiator(final long processDefinitionId)\n            throws ActorNotFoundException, ProcessDefinitionNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService();\n        ActorInstance actorInstance;\n        try {\n            final SProcessDefinition definition = processDefinitionService.getProcessDefinition(processDefinitionId);\n            final SActorDefinition sActorDefinition = definition.getActorInitiator();\n            if (sActorDefinition == null) {\n                throw new ActorNotFoundException(\"No actor initiator defined on the process\");\n            }\n            final String name = sActorDefinition.getName();\n            final SActor sActor = actorMappingService.getActor(name, processDefinitionId);\n            actorInstance = ModelConvertor.toActorInstance(sActor);\n        } catch (final SProcessDefinitionNotFoundException e) {\n            // no rollback need, we only read\n            throw new ProcessDefinitionNotFoundException(e);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n        return actorInstance;\n    }\n\n    @Override\n    public int getNumberOfActivityDataDefinitions(final long processDefinitionId, final String activityName)\n            throws ProcessDefinitionNotFoundException,\n            ActivityDefinitionNotFoundException {\n        List<SDataDefinition> sdataDefinitionList = Collections.emptyList();\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        try {\n            final SProcessDefinition sProcessDefinition = processDefinitionService\n                    .getProcessDefinition(processDefinitionId);\n            boolean found = false;\n            final SFlowElementContainerDefinition processContainer = sProcessDefinition.getProcessContainer();\n            final Set<SActivityDefinition> activityDefList = processContainer.getActivities();\n            for (final SActivityDefinition sActivityDefinition : activityDefList) {\n                if (activityName.equals(sActivityDefinition.getName())) {\n                    sdataDefinitionList = sActivityDefinition.getSDataDefinitions();\n                    found = true;\n                    break;\n                }\n            }\n            if (!found) {\n                throw new ActivityDefinitionNotFoundException(activityName);\n            }\n            return sdataDefinitionList.size();\n\n        } catch (final SProcessDefinitionNotFoundException e) {\n            throw new ProcessDefinitionNotFoundException(e);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public int getNumberOfProcessDataDefinitions(final long processDefinitionId)\n            throws ProcessDefinitionNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        try {\n            final SProcessDefinition sProcessDefinition = processDefinitionService\n                    .getProcessDefinition(processDefinitionId);\n            final SFlowElementContainerDefinition processContainer = sProcessDefinition.getProcessContainer();\n            return processContainer.getDataDefinitions().size();\n        } catch (final SProcessDefinitionNotFoundException e) {\n            throw new ProcessDefinitionNotFoundException(e);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public ProcessInstance startProcess(final long processDefinitionId,\n            final Map<String, Serializable> initialVariables)\n            throws ProcessDefinitionNotFoundException, ProcessActivationException, ProcessExecutionException {\n        final List<Operation> operations = createSetDataOperation(processDefinitionId, initialVariables);\n        return startProcess(processDefinitionId, operations, initialVariables);\n    }\n\n    @Override\n    public ProcessInstance startProcessWithInputs(final long processDefinitionId,\n            final Map<String, Serializable> instantiationInputs)\n            throws ProcessDefinitionNotFoundException, ProcessActivationException, ProcessExecutionException,\n            ContractViolationException {\n        return startProcessWithInputs(0, processDefinitionId, instantiationInputs);\n    }\n\n    @Override\n    public ProcessInstance startProcessWithInputs(final long userId, final long processDefinitionId,\n            final Map<String, Serializable> instantiationInputs)\n            throws ProcessDefinitionNotFoundException, ProcessActivationException, ProcessExecutionException,\n            ContractViolationException {\n        return new ProcessStarter(userId, processDefinitionId, instantiationInputs).start();\n    }\n\n    @Override\n    public ProcessInstance startProcess(final long userId, final long processDefinitionId,\n            final Map<String, Serializable> initialVariables)\n            throws ProcessDefinitionNotFoundException, ProcessActivationException, ProcessExecutionException {\n        final List<Operation> operations = createSetDataOperation(processDefinitionId, initialVariables);\n        return startProcess(userId, processDefinitionId, operations, initialVariables);\n    }\n\n    protected List<Operation> createSetDataOperation(final long processDefinitionId,\n            final Map<String, Serializable> initialVariables)\n            throws ProcessExecutionException {\n        final ClassLoaderService classLoaderService = getServiceAccessor().getClassLoaderService();\n        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        final List<Operation> operations = new ArrayList<>();\n        try {\n            final ClassLoader localClassLoader = classLoaderService.getClassLoader(\n                    identifier(ScopeType.PROCESS, processDefinitionId));\n            Thread.currentThread().setContextClassLoader(localClassLoader);\n            if (initialVariables != null) {\n                for (final Entry<String, Serializable> initialVariable : initialVariables.entrySet()) {\n                    final String name = initialVariable.getKey();\n                    final Serializable value = initialVariable.getValue();\n                    final Expression expression = new ExpressionBuilder().createExpression(name, name,\n                            value.getClass().getName(), ExpressionType.TYPE_INPUT);\n                    final Operation operation = new OperationBuilder().createSetDataOperation(name, expression);\n                    operations.add(operation);\n                }\n            }\n        } catch (final SClassLoaderException | InvalidExpressionException cle) {\n            throw new ProcessExecutionException(cle);\n        } finally {\n            Thread.currentThread().setContextClassLoader(contextClassLoader);\n        }\n        return operations;\n    }\n\n    @Override\n    public ProcessInstance startProcess(final long processDefinitionId, final List<Operation> operations,\n            final Map<String, Serializable> context)\n            throws ProcessExecutionException, ProcessDefinitionNotFoundException, ProcessActivationException {\n        try {\n            return startProcess(0, processDefinitionId, operations, context);\n        } catch (final RetrieveException e) {\n            throw new ProcessExecutionException(e);\n        }\n    }\n\n    @Override\n    public ProcessInstance startProcess(final long userId, final long processDefinitionId,\n            final List<Operation> operations,\n            final Map<String, Serializable> context)\n            throws ProcessDefinitionNotFoundException, ProcessActivationException, ProcessExecutionException {\n        final ProcessStarter starter = new ProcessStarter(userId, processDefinitionId, operations, context);\n        try {\n            return starter.start();\n        } catch (ContractViolationException e) {\n            // To not have an API break, we need to wrapped this new Exception:\n            throw new ProcessExecutionException(e);\n        }\n    }\n\n    @Override\n    public long getNumberOfActivityDataInstances(final long activityInstanceId)\n            throws ActivityInstanceNotFoundException {\n        try {\n            return getNumberOfDataInstancesOfContainer(activityInstanceId, DataInstanceContainer.ACTIVITY_INSTANCE);\n        } catch (final SBonitaException e) {\n            throw new ActivityInstanceNotFoundException(activityInstanceId);\n        }\n    }\n\n    private long getNumberOfDataInstancesOfContainer(final long instanceId, final DataInstanceContainer containerType)\n            throws SBonitaException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ParentContainerResolver parentContainerResolver = serviceAccessor.getParentContainerResolver();\n        final DataInstanceService dataInstanceService = serviceAccessor.getDataInstanceService();\n        return dataInstanceService.getNumberOfDataInstances(instanceId, containerType.name(), parentContainerResolver);\n    }\n\n    @Override\n    public long getNumberOfProcessDataInstances(final long processInstanceId) throws ProcessInstanceNotFoundException {\n        try {\n            return getNumberOfDataInstancesOfContainer(processInstanceId, DataInstanceContainer.PROCESS_INSTANCE);\n        } catch (final SBonitaException e) {\n            throw new ProcessInstanceNotFoundException(e);\n        }\n    }\n\n    protected Map<String, Serializable> executeOperations(final ConnectorResult connectorResult,\n            final List<Operation> operations,\n            final Map<String, Serializable> operationInputValues, final SExpressionContext expressionContext,\n            final ClassLoader classLoader,\n            final ServiceAccessor serviceAccessor) throws SBonitaException {\n        final ConnectorService connectorService = serviceAccessor.getConnectorService();\n        final OperationService operationService = serviceAccessor.getOperationService();\n        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        try {\n            Thread.currentThread().setContextClassLoader(classLoader);\n            final Map<String, Serializable> externalDataValue = new HashMap<>(operations.size());\n            // convert the client operation to server operation\n            final List<SOperation> sOperations = convertOperations(operations);\n            // set input values of expression with connector result + provided input for this operation\n            final HashMap<String, Object> inputValues = new HashMap<>(operationInputValues);\n            inputValues.putAll(connectorResult.getResult());\n            expressionContext.setInputValues(inputValues);\n            // execute\n            final Long containerId = expressionContext.getContainerId();\n            operationService.execute(sOperations, containerId == null ? -1 : containerId,\n                    expressionContext.getContainerType(), expressionContext);\n            // return the value of the data if it's an external data\n            for (final Operation operation : operations) {\n                final LeftOperand leftOperand = operation.getLeftOperand();\n                if (LeftOperand.TYPE_EXTERNAL_DATA.equals(leftOperand.getType())) {\n                    externalDataValue.put(leftOperand.getName(),\n                            (Serializable) expressionContext.getInputValues().get(leftOperand.getName()));\n                }\n            }\n            return externalDataValue;\n        } finally {\n            // we finally disconnect the connector\n            connectorService.disconnect(connectorResult);\n            Thread.currentThread().setContextClassLoader(contextClassLoader);\n        }\n    }\n\n    private Map<String, Serializable> toSerializableMap(final Map<String, Object> map,\n            final String connectorDefinitionId,\n            final String connectorDefinitionVersion) throws NotSerializableException {\n        final HashMap<String, Serializable> resMap = new HashMap<>(map.size());\n        for (final Entry<String, Object> entry : map.entrySet()) {\n            try {\n                resMap.put(entry.getKey(), (Serializable) entry.getValue());\n            } catch (final ClassCastException e) {\n                throw new NotSerializableException(connectorDefinitionId, connectorDefinitionVersion, entry.getKey(),\n                        entry.getValue());\n            }\n        }\n        return resMap;\n    }\n\n    @Override\n    public Map<String, Serializable> executeConnectorOnProcessDefinition(final String connectorDefinitionId,\n            final String connectorDefinitionVersion,\n            final Map<String, Expression> connectorInputParameters,\n            final Map<String, Map<String, Serializable>> inputValues, final long processDefinitionId)\n            throws ConnectorExecutionException {\n        return executeConnectorOnProcessDefinitionWithOrWithoutOperations(connectorDefinitionId,\n                connectorDefinitionVersion, connectorInputParameters,\n                inputValues, null, null, processDefinitionId);\n    }\n\n    @Override\n    public Map<String, Serializable> executeConnectorOnProcessDefinition(final String connectorDefinitionId,\n            final String connectorDefinitionVersion,\n            final Map<String, Expression> connectorInputParameters,\n            final Map<String, Map<String, Serializable>> inputValues, final List<Operation> operations,\n            final Map<String, Serializable> operationInputValues, final long processDefinitionId)\n            throws ConnectorExecutionException {\n        return executeConnectorOnProcessDefinitionWithOrWithoutOperations(connectorDefinitionId,\n                connectorDefinitionVersion, connectorInputParameters,\n                inputValues, operations, operationInputValues, processDefinitionId);\n    }\n\n    /**\n     * execute the connector and return connector output if there is no operation or operation output if there is\n     * operation\n     */\n    private Map<String, Serializable> executeConnectorOnProcessDefinitionWithOrWithoutOperations(\n            final String connectorDefinitionId,\n            final String connectorDefinitionVersion, final Map<String, Expression> connectorInputParameters,\n            final Map<String, Map<String, Serializable>> inputValues, final List<Operation> operations,\n            final Map<String, Serializable> operationInputValues,\n            final long processDefinitionId) throws ConnectorExecutionException {\n        checkConnectorParameters(connectorInputParameters, inputValues);\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        final ConnectorService connectorService = serviceAccessor.getConnectorService();\n        final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService();\n\n        try {\n            final ClassLoader classLoader = classLoaderService.getClassLoader(\n                    identifier(ScopeType.PROCESS, processDefinitionId));\n\n            final Map<String, SExpression> connectorsExps = ModelConvertor\n                    .constructExpressions(connectorInputParameters);\n            final SExpressionContext expcontext = new SExpressionContext();\n            expcontext.setProcessDefinitionId(processDefinitionId);\n            expcontext.setContainerState(ContainerState.ACTIVE);\n            final SProcessDefinition processDef = processDefinitionService.getProcessDefinition(processDefinitionId);\n            if (processDef != null) {\n                expcontext.setProcessDefinition(processDef);\n            }\n            final ConnectorResult connectorResult = connectorService.executeMultipleEvaluation(processDefinitionId,\n                    connectorDefinitionId,\n                    connectorDefinitionVersion, connectorsExps, inputValues, classLoader, expcontext);\n            if (operations != null) {\n                // execute operations\n                return executeOperations(connectorResult, operations, operationInputValues, expcontext, classLoader,\n                        serviceAccessor);\n            }\n            return getSerializableResultOfConnector(connectorDefinitionVersion, connectorResult, connectorService);\n        } catch (final SBonitaException | NotSerializableException e) {\n            throw new ConnectorExecutionException(e);\n        }\n    }\n\n    protected Map<String, Serializable> getSerializableResultOfConnector(final String connectorDefinitionVersion,\n            final ConnectorResult connectorResult,\n            final ConnectorService connectorService) throws NotSerializableException, SConnectorException {\n        connectorService.disconnect(connectorResult);\n        return toSerializableMap(connectorResult.getResult(), connectorDefinitionVersion, connectorDefinitionVersion);\n    }\n\n    protected void checkConnectorParameters(final Map<String, Expression> connectorInputParameters,\n            final Map<String, Map<String, Serializable>> inputValues)\n            throws ConnectorExecutionException {\n        if (connectorInputParameters.size() != inputValues.size()) {\n            throw new ConnectorExecutionException(\n                    \"The number of input parameters is not consistent with the number of input values. Input parameters: \"\n                            + connectorInputParameters.size() + \", number of input values: \" + inputValues.size());\n        }\n    }\n\n    @Override\n    public void setActivityStateByName(final long activityInstanceId, final String state) throws UpdateException {\n        setActivityStateById(activityInstanceId, ModelConvertor.getServerActivityStateId(state));\n    }\n\n    @Override\n    public void setActivityStateById(final long activityInstanceId, final int stateId) throws UpdateException {\n        try {\n            ActivityInstanceService activityInstanceService = getServiceAccessor().getActivityInstanceService();\n            SFlowNodeInstance flowNodeInstance = activityInstanceService.getFlowNodeInstance(activityInstanceId);\n            FlowNodeState state = getServiceAccessor().getFlowNodeStateManager().getState(stateId);\n            log.warn(String.format(\n                    \"setActivityStateById was called: <%s> is forcing state of the flow node with name = <%s> id = <%d> and type <%s> in current state <%s: %s> to the state <%s: %s>. \"\n                            +\n                            \"Setting the state of a flow node through the API is not recommended and can affect the normal behavior of the platform. \"\n                            +\n                            \"If that flow node was already scheduled for execution, there is no guarantee on how that flow node will behave anymore.\",\n                    getLoggedUsername(), flowNodeInstance.getName(), flowNodeInstance.getId(),\n                    flowNodeInstance.getType().name(),\n                    flowNodeInstance.getStateId(), flowNodeInstance.getStateName(),\n                    stateId, state.getName()));\n            getServiceAccessor().getFlowNodeExecutor().setStateByStateId(activityInstanceId, stateId);\n        } catch (final SBonitaException e) {\n            throw new UpdateException(e);\n        }\n    }\n\n    private String getLoggedUsername() {\n        SSession session = getSession();\n        if (session == null || session.getUserId() <= 0) {\n            return \"SYSTEM\";\n        }\n        long userId = session.getUserId();\n        try {\n            return getServiceAccessor().getIdentityService().getUser(userId).getUserName();\n        } catch (Exception e) {\n            log.warn(\"Unable to retrieve user with id \" + userId\n                    + \" exception: \" + e.getClass().getName() + \" \" + e.getMessage());\n        }\n        return String.valueOf(userId);\n    }\n\n    @Override\n    public void setTaskPriority(final long humanTaskInstanceId, final TaskPriority priority) throws UpdateException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n\n        try {\n            final SetTaskPriority transactionContent = new SetTaskPriority(activityInstanceService, humanTaskInstanceId,\n                    STaskPriority.valueOf(priority.name()));\n            transactionContent.execute();\n        } catch (final SBonitaException e) {\n            throw new UpdateException(e);\n        }\n    }\n\n    private void deleteProcessInstanceInTransaction(final ServiceAccessor serviceAccessor,\n            final long processInstanceId) throws SBonitaException {\n        final UserTransactionService userTransactionService = serviceAccessor.getUserTransactionService();\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n        final DocumentService documentService = serviceAccessor.getDocumentService();\n\n        try {\n            userTransactionService.executeInTransaction((Callable<Void>) () -> {\n                final SProcessInstance sProcessInstance = processInstanceService.getProcessInstance(processInstanceId);\n                deleteJobsOnProcessInstance(sProcessInstance);\n                // Documents are only deleted when deleting the unfinished process instance from the API,\n                // and not when the finished process instance is archived:\n                documentService.deleteDocumentContentsForProcessInstance(sProcessInstance.getId());\n                processInstanceService.deleteParentProcessInstanceAndElements(sProcessInstance);\n                return null;\n            });\n        } catch (final SBonitaException e) {\n            throw e;\n        } catch (final Exception e) {\n            throw new SBonitaRuntimeException(\"Error while deleting the parent process instance and elements.\", e);\n        }\n    }\n\n    @Override\n    @CustomTransactions\n    public long deleteProcessInstances(final long processDefinitionId, final int startIndex, final int maxResults)\n            throws DeletionException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n        final UserTransactionService userTxService = serviceAccessor.getUserTransactionService();\n        try {\n            final Map<SProcessInstance, List<Long>> processInstancesWithChildrenIds = userTxService\n                    .executeInTransaction(() -> {\n                        final List<SProcessInstance> sProcessInstances1 = searchProcessInstancesFromProcessDefinition(\n                                processInstanceService,\n                                processDefinitionId, startIndex, maxResults);\n                        final Map<SProcessInstance, List<Long>> sProcessInstanceListHashMap = new LinkedHashMap<>(\n                                sProcessInstances1.size());\n                        for (final SProcessInstance rootProcessInstance : sProcessInstances1) {\n                            List<Long> tmpList;\n                            final List<Long> childrenProcessInstanceIds = new ArrayList<>();\n                            int fromIndex = 0;\n                            do {\n                                // from index always will be zero because elements will be deleted\n                                tmpList = processInstanceService\n                                        .getArchivedChildrenSourceObjectIdsFromRootProcessInstance(\n                                                rootProcessInstance.getId(),\n                                                fromIndex, BATCH_SIZE, OrderByType.ASC);\n                                childrenProcessInstanceIds.addAll(tmpList);\n                                fromIndex += BATCH_SIZE;\n                            } while (tmpList.size() == BATCH_SIZE);\n                            sProcessInstanceListHashMap.put(rootProcessInstance, childrenProcessInstanceIds);\n                        }\n                        return sProcessInstanceListHashMap;\n                    });\n\n            if (processInstancesWithChildrenIds.isEmpty()) {\n                return 0;\n            }\n\n            final LockService lockService = serviceAccessor.getLockService();\n            final DocumentService documentService = serviceAccessor.getDocumentService();\n            final String objectType = SFlowElementsContainerType.PROCESS.name();\n            List<BonitaLock> locks = null;\n            try {\n                locks = createLockProcessInstances(lockService, objectType, processInstancesWithChildrenIds);\n                return userTxService.executeInTransaction(() -> {\n                    final List<SProcessInstance> sProcessInstances = new ArrayList<>(\n                            processInstancesWithChildrenIds.keySet());\n                    deleteJobsOnProcessInstance(processDefinitionId, sProcessInstances);\n                    for (SProcessInstance spi : sProcessInstances) {\n                        // Documents are only deleted when deleting the unfinished process instance from the API,\n                        // and not when the finished process instance is archived:\n                        documentService.deleteDocumentContentsForProcessInstance(spi.getId());\n                    }\n                    return processInstanceService.deleteParentProcessInstanceAndElements(sProcessInstances);\n                });\n            } finally {\n                releaseLocks(lockService, locks);\n            }\n\n        } catch (final SProcessInstanceHierarchicalDeletionException e) {\n            throw new ProcessInstanceHierarchicalDeletionException(e.getMessage(), e.getProcessInstanceId());\n        } catch (final Exception e) {\n            throw new DeletionException(e);\n        }\n    }\n\n    private void deleteJobsOnEventSubProcess(final SProcessDefinition processDefinition,\n            final SProcessInstance sProcessInstance) {\n        final Set<SSubProcessDefinition> sSubProcessDefinitions = processDefinition.getProcessContainer()\n                .getSubProcessDefinitions();\n        for (final SSubProcessDefinition sSubProcessDefinition : sSubProcessDefinitions) {\n            final List<SStartEventDefinition> startEventsOfSubProcess = sSubProcessDefinition.getSubProcessContainer()\n                    .getStartEvents();\n            deleteJobsOnEventSubProcess(processDefinition, sProcessInstance, sSubProcessDefinition,\n                    startEventsOfSubProcess);\n\n            final List<SIntermediateCatchEventDefinition> intermediateCatchEvents = sSubProcessDefinition\n                    .getSubProcessContainer().getIntermediateCatchEvents();\n            deleteJobsOnEventSubProcess(processDefinition, sProcessInstance, sSubProcessDefinition,\n                    intermediateCatchEvents);\n\n            final List<SBoundaryEventDefinition> sBoundaryEventDefinitions = sSubProcessDefinition\n                    .getSubProcessContainer().getBoundaryEvents();\n            deleteJobsOnEventSubProcess(processDefinition, sProcessInstance, sSubProcessDefinition,\n                    sBoundaryEventDefinitions);\n        }\n    }\n\n    private void deleteJobsOnEventSubProcess(final SProcessDefinition processDefinition,\n            final SProcessInstance sProcessInstance,\n            final SSubProcessDefinition sSubProcessDefinition,\n            final List<? extends SCatchEventDefinition> sCatchEventDefinitions) {\n        final SchedulerService schedulerService = getServiceAccessor().getSchedulerService();\n\n        for (final SCatchEventDefinition sCatchEventDefinition : sCatchEventDefinitions) {\n            try {\n                if (!sCatchEventDefinition.getTimerEventTriggerDefinitions().isEmpty()) {\n                    final String jobName = JobNameBuilder.getTimerEventJobName(processDefinition.getId(),\n                            sCatchEventDefinition, sProcessInstance.getId(),\n                            sSubProcessDefinition.getId());\n                    final boolean delete = schedulerService.delete(jobName);\n                    if (!delete && schedulerService.isExistingJob(jobName)) {\n                        if (log.isWarnEnabled()) {\n                            log.warn(\"No job found with name '\" + jobName\n                                    + \"' when interrupting timer catch event named '\"\n                                    + sCatchEventDefinition.getName()\n                                    + \"' on event sub process with the id '\" + sSubProcessDefinition.getId()\n                                    + \"'. It was probably already triggered.\");\n                        }\n                    }\n                }\n            } catch (final Exception e) {\n                log.warn(ExceptionUtils.printLightWeightStacktrace(e));\n            }\n        }\n    }\n\n    void deleteJobsOnProcessInstance(final SProcessInstance sProcessInstance) throws SBonitaException {\n        deleteJobsOnProcessInstance(sProcessInstance.getProcessDefinitionId(),\n                Collections.singletonList(sProcessInstance));\n    }\n\n    private void deleteJobsOnProcessInstance(final long processDefinitionId,\n            final List<SProcessInstance> sProcessInstances)\n            throws SBonitaException {\n        final SProcessDefinition processDefinition = getServiceAccessor().getProcessDefinitionService()\n                .getProcessDefinition(processDefinitionId);\n\n        for (final SProcessInstance sProcessInstance : sProcessInstances) {\n            deleteJobsOnProcessInstance(processDefinition, sProcessInstance);\n        }\n    }\n\n    private void deleteJobsOnProcessInstance(final SProcessDefinition processDefinition,\n            final SProcessInstance sProcessInstance)\n            throws SBonitaReadException {\n\n        deleteJobsOnCallActivitiesOfProcessInstance(sProcessInstance.getId());\n\n        final List<SStartEventDefinition> startEventsOfSubProcess = processDefinition.getProcessContainer()\n                .getStartEvents();\n        deleteJobsOnProcessInstance(processDefinition, sProcessInstance, startEventsOfSubProcess);\n\n        final List<SIntermediateCatchEventDefinition> intermediateCatchEvents = processDefinition.getProcessContainer()\n                .getIntermediateCatchEvents();\n        deleteJobsOnProcessInstance(processDefinition, sProcessInstance, intermediateCatchEvents);\n\n        final List<SBoundaryEventDefinition> sBoundaryEventDefinitions = processDefinition.getProcessContainer()\n                .getBoundaryEvents();\n        deleteJobsOnProcessInstance(processDefinition, sProcessInstance, sBoundaryEventDefinitions);\n\n        deleteJobsOnEventSubProcess(processDefinition, sProcessInstance);\n    }\n\n    private void deleteJobsOnCallActivitiesOfProcessInstance(final long processInstanceId) throws SBonitaReadException {\n        List<ActivityInstance> flowNodeInstances;\n        int index = 0;\n        final ProcessInstanceService processInstanceService = getServiceAccessor().getProcessInstanceService();\n        do {\n            try {\n                flowNodeInstances = searchActivities(new SearchOptionsBuilder(index, index + BATCH_SIZE)\n                        .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, processInstanceId)\n                        .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.CALL_ACTIVITY)\n                        .done()).getResult();\n            } catch (SearchException e) {\n                throw new SBonitaReadException(\n                        \"Unable to delete jobs on call activities of process instance id \" + processInstanceId, e);\n            }\n            for (ActivityInstance callActivityInstance : flowNodeInstances) {\n                try {\n                    deleteJobsOnProcessInstance(\n                            processInstanceService.getChildOfActivity(callActivityInstance.getId()));\n                } catch (SBonitaException e) {\n                    log.info(\n                            \"Can't find the process instance called by the activity. This process may be already finished. {}\",\n                            ExceptionUtils.printLightWeightStacktrace(e));\n\n                }\n            }\n            index = index + BATCH_SIZE;\n        } while (flowNodeInstances.size() == BATCH_SIZE);\n\n    }\n\n    private void deleteJobsOnProcessInstance(final SProcessDefinition processDefinition,\n            final SProcessInstance sProcessInstance,\n            final List<? extends SCatchEventDefinition> sCatchEventDefinitions) throws SBonitaReadException {\n        for (final SCatchEventDefinition sCatchEventDefinition : sCatchEventDefinitions) {\n            deleteJobsOnFlowNodeInstances(processDefinition, sCatchEventDefinition, sProcessInstance);\n        }\n    }\n\n    private void deleteJobsOnFlowNodeInstances(final SProcessDefinition processDefinition,\n            final SCatchEventDefinition sCatchEventDefinition,\n            final SProcessInstance sProcessInstance) throws SBonitaReadException {\n        final ActivityInstanceService activityInstanceService = getServiceAccessor().getActivityInstanceService();\n\n        final List<OrderByOption> orderByOptions = Collections\n                .singletonList(new OrderByOption(SCatchEventInstance.class, \"id\", OrderByType.ASC));\n        final FilterOption filterOption1 = new FilterOption(SCatchEventInstance.class, \"flowNodeDefinitionId\",\n                sCatchEventDefinition.getId());\n        final FilterOption filterOption2 = new FilterOption(SCatchEventInstance.class, \"logicalGroup4\",\n                sProcessInstance.getId());\n        QueryOptions queryOptions = new QueryOptions(0, 100, orderByOptions,\n                Arrays.asList(filterOption1, filterOption2), null);\n        List<SCatchEventInstance> sCatchEventInstances = activityInstanceService\n                .searchFlowNodeInstances(SCatchEventInstance.class, queryOptions);\n        while (!sCatchEventInstances.isEmpty()) {\n            for (final SCatchEventInstance sCatchEventInstance : sCatchEventInstances) {\n                deleteJobsOnFlowNodeInstance(processDefinition, sCatchEventDefinition, sCatchEventInstance);\n            }\n            queryOptions = QueryOptions.getNextPage(queryOptions);\n            sCatchEventInstances = activityInstanceService.searchFlowNodeInstances(SCatchEventInstance.class,\n                    queryOptions);\n        }\n    }\n\n    private void deleteJobsOnFlowNodeInstance(final SProcessDefinition processDefinition,\n            final SCatchEventDefinition sCatchEventDefinition,\n            final SCatchEventInstance sCatchEventInstance) {\n        final SchedulerService schedulerService = getServiceAccessor().getSchedulerService();\n        EventInstanceService eventInstanceService = getServiceAccessor().getEventInstanceService();\n        try {\n            if (!sCatchEventDefinition.getTimerEventTriggerDefinitions().isEmpty()) {\n                final String jobName = JobNameBuilder.getTimerEventJobName(processDefinition.getId(),\n                        sCatchEventDefinition, sCatchEventInstance);\n                final boolean delete = schedulerService.delete(jobName);\n                try {\n                    eventInstanceService.deleteEventTriggerInstanceOfFlowNode(sCatchEventInstance.getId());\n                } catch (SEventTriggerInstanceDeletionException e) {\n                    log.warn(\n                            \"Unable to delete event trigger of flow node instance \" + sCatchEventInstance + \" because: \"\n                                    + e.getMessage());\n                }\n                if (!delete && schedulerService.isExistingJob(jobName)) {\n                    if (log.isWarnEnabled()) {\n                        log.warn(\"No job found with name '\" + jobName + \"' when interrupting timer catch event named '\"\n                                + sCatchEventDefinition.getName()\n                                + \"' and id '\" + sCatchEventInstance.getId()\n                                + \"'. It was probably already triggered.\");\n                    }\n                }\n            }\n        } catch (final Exception e) {\n            log.warn(ExceptionUtils.printLightWeightStacktrace(e));\n        }\n    }\n\n    private List<SProcessInstance> searchProcessInstancesFromProcessDefinition(\n            final ProcessInstanceService processInstanceService,\n            final long processDefinitionId, final int startIndex, final int maxResults) throws SBonitaReadException {\n        final FilterOption filterOption = new FilterOption(SProcessInstance.class, SProcessInstance.PROCESSDEF_ID_KEY,\n                processDefinitionId);\n        final OrderByOption order2 = new OrderByOption(SProcessInstance.class, SProcessInstance.ID_KEY,\n                OrderByType.ASC);\n        // Order by caller id ASC because we need to have parent process deleted before their sub processes\n        final OrderByOption order = new OrderByOption(SProcessInstance.class, SProcessInstance.CALLER_ID,\n                OrderByType.ASC);\n        final QueryOptions queryOptions = new QueryOptions(startIndex, maxResults, Arrays.asList(order, order2),\n                Collections.singletonList(filterOption), null);\n        return processInstanceService.searchProcessInstances(queryOptions);\n    }\n\n    @Override\n    public long deleteArchivedProcessInstances(final long processDefinitionId, final int startIndex,\n            final int maxResults) throws DeletionException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n\n        try {\n            List<Long> sourceProcessInstanceIds = processInstanceService\n                    .getSourceProcessInstanceIdsOfArchProcessInstancesFromDefinition(processDefinitionId, startIndex,\n                            maxResults, null);\n            if (sourceProcessInstanceIds.isEmpty()) {\n                return 0;\n            }\n            return deleteArchivedProcessInstancesInAllStates(sourceProcessInstanceIds);\n        } catch (final SBonitaException e) {\n            throw new DeletionException(e);\n        }\n    }\n\n    @Override\n    public long deleteArchivedProcessInstancesInAllStates(final List<Long> sourceProcessInstanceIds)\n            throws DeletionException {\n        if (sourceProcessInstanceIds == null || sourceProcessInstanceIds.isEmpty()) {\n            throw new IllegalArgumentException(\n                    \"The identifier of the archived process instances to deleted are missing !!\");\n        }\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n\n        try {\n            return processInstanceService.deleteArchivedProcessInstances(sourceProcessInstanceIds);\n        } catch (final SProcessInstanceHierarchicalDeletionException e) {\n            throw new ProcessInstanceHierarchicalDeletionException(e.getMessage(), e.getProcessInstanceId());\n        } catch (final SBonitaException e) {\n            throw new DeletionException(e);\n        }\n    }\n\n    @Override\n    public long deleteArchivedProcessInstancesInAllStates(final long sourceProcessInstanceId) throws DeletionException {\n        return deleteArchivedProcessInstancesInAllStates(Collections.singletonList(sourceProcessInstanceId));\n    }\n\n    private List<BonitaLock> createLockProcessInstances(final LockService lockService, final String objectType,\n            final Map<SProcessInstance, List<Long>> sProcessInstances) throws SLockException, SLockTimeoutException {\n        final List<BonitaLock> locks = new ArrayList<>();\n        final HashSet<Long> uniqueIds = new HashSet<>();\n        for (final Entry<SProcessInstance, List<Long>> processInstanceWithChildrenIds : sProcessInstances.entrySet()) {\n            uniqueIds.add(processInstanceWithChildrenIds.getKey().getId());\n            uniqueIds.addAll(processInstanceWithChildrenIds.getValue());\n        }\n        for (final Long id : uniqueIds) {\n            final BonitaLock childLock = lockService.lock(id, objectType);\n            locks.add(childLock);\n        }\n        return locks;\n    }\n\n    @Override\n    @CustomTransactions\n    public void deleteProcessInstance(final long processInstanceId) throws DeletionException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final LockService lockService = serviceAccessor.getLockService();\n        final String objectType = SFlowElementsContainerType.PROCESS.name();\n        BonitaLock lock = null;\n        try {\n            lock = lockService.lock(processInstanceId, objectType);\n            deleteProcessInstanceInTransaction(serviceAccessor, processInstanceId);\n        } catch (final SProcessInstanceHierarchicalDeletionException e) {\n            throw new ProcessInstanceHierarchicalDeletionException(e.getMessage(), e.getProcessInstanceId());\n        } catch (final SProcessInstanceNotFoundException spinfe) {\n            throw new DeletionException(new ProcessInstanceNotFoundException(spinfe));\n        } catch (final SBonitaException e) {\n            throw new DeletionException(e);\n        } finally {\n            if (lock != null) {\n                try {\n                    lockService.unlock(lock);\n                } catch (final SLockException e) {\n                    throw new DeletionException(\n                            \"Lock was not released. Object type: \" + objectType + \", id: \" + processInstanceId, e);\n                }\n            }\n        }\n    }\n\n    @Override\n    public SearchResult<ProcessInstance> searchOpenProcessInstances(final SearchOptions searchOptions)\n            throws SearchException {\n        // To select all finished process instances, without subprocess\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(searchOptions);\n        searchOptionsBuilder.differentFrom(ProcessInstanceSearchDescriptor.STATE_ID,\n                ProcessInstanceState.COMPLETED.getId())\n                .differentFrom(ProcessInstanceSearchDescriptor.STATE_ID, ProcessInstanceState.ABORTED.getId())\n                .differentFrom(ProcessInstanceSearchDescriptor.STATE_ID, ProcessInstanceState.CANCELLED.getId());\n        searchOptionsBuilder.filter(ProcessInstanceSearchDescriptor.CALLER_ID, -1);\n        try {\n            return searchProcessInstances(getServiceAccessor(), searchOptionsBuilder.done());\n        } catch (final SBonitaException e) {\n            throw new SearchException(e);\n        }\n    }\n\n    @Override\n    public SearchResult<ProcessInstance> searchProcessInstances(final SearchOptions searchOptions)\n            throws SearchException {\n        try {\n            return searchProcessInstances(getServiceAccessor(), searchOptions);\n        } catch (final SBonitaException e) {\n            throw new SearchException(e);\n        }\n    }\n\n    @Override\n    public SearchResult<ProcessInstance> searchFailedProcessInstances(final SearchOptions searchOptions)\n            throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n\n        try {\n            final SearchFailedProcessInstances searchProcessInstances = new SearchFailedProcessInstances(\n                    processInstanceService,\n                    searchEntitiesDescriptor.getSearchProcessInstanceDescriptor(), searchOptions,\n                    processDefinitionService);\n            searchProcessInstances.execute();\n            return searchProcessInstances.getResult();\n        } catch (final SBonitaException sbe) {\n            throw new SearchException(sbe);\n        }\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see org.bonitasoft.engine.api.ProcessRuntimeAPI#searchFailedProcessInstancesSupervisedBy(long,\n     * org.bonitasoft.engine.search.SearchOptions)\n     */\n    @Override\n    public SearchResult<ProcessInstance> searchFailedProcessInstancesSupervisedBy(final long userId,\n            final SearchOptions searchOptions) throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        final GetSUser getUser = createTxUserGetter(userId, identityService);\n        try {\n            getUser.execute();\n        } catch (final SBonitaException e) {\n            return new SearchResultImpl<>(0, Collections.emptyList());\n        }\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        final SearchFailedProcessInstancesSupervisedBy searchFailedProcessInstances = createSearchFailedProcessInstancesSupervisedBy(\n                userId, searchOptions,\n                processInstanceService, searchEntitiesDescriptor, processDefinitionService);\n        try {\n            searchFailedProcessInstances.execute();\n            return searchFailedProcessInstances.getResult();\n        } catch (final SBonitaException sbe) {\n            throw new SearchException(sbe);\n        }\n    }\n\n    protected SearchFailedProcessInstancesSupervisedBy createSearchFailedProcessInstancesSupervisedBy(final long userId,\n            final SearchOptions searchOptions,\n            final ProcessInstanceService processInstanceService,\n            final SearchEntitiesDescriptor searchEntitiesDescriptor,\n            final ProcessDefinitionService processDefinitionService) {\n        return new SearchFailedProcessInstancesSupervisedBy(processInstanceService,\n                searchEntitiesDescriptor.getSearchProcessInstanceDescriptor(), userId, searchOptions,\n                processDefinitionService);\n    }\n\n    protected GetSUser createTxUserGetter(final long userId, final IdentityService identityService) {\n        return new GetSUser(identityService, userId);\n    }\n\n    @Override\n    public SearchResult<ProcessInstance> searchOpenProcessInstancesSupervisedBy(final long userId,\n            final SearchOptions searchOptions) throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        final GetSUser getUser = createTxUserGetter(userId, identityService);\n        try {\n            getUser.execute();\n        } catch (final SBonitaException e) {\n            return new SearchResultImpl<>(0, Collections.emptyList());\n        }\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        final SearchOpenProcessInstancesSupervisedBy searchOpenProcessInstances = new SearchOpenProcessInstancesSupervisedBy(\n                processInstanceService,\n                searchEntitiesDescriptor.getSearchProcessInstanceDescriptor(), userId, searchOptions,\n                processDefinitionService);\n        try {\n            searchOpenProcessInstances.execute();\n            return searchOpenProcessInstances.getResult();\n        } catch (final SBonitaException sbe) {\n            throw new SearchException(sbe);\n        }\n    }\n\n    @Override\n    public SearchResult<ProcessDeploymentInfo> searchProcessDeploymentInfosStartedBy(final long userId,\n            final SearchOptions searchOptions)\n            throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        final SearchProcessDefinitionsDescriptor searchDescriptor = searchEntitiesDescriptor\n                .getSearchProcessDefinitionsDescriptor();\n        final SearchProcessDeploymentInfosStartedBy searcher = new SearchProcessDeploymentInfosStartedBy(\n                processDefinitionService, searchDescriptor, userId,\n                searchOptions);\n        try {\n            searcher.execute();\n        } catch (final SBonitaException e) {\n            throw new SearchException(\"Can't get ProcessDeploymentInfo startedBy userid \" + userId, e);\n        }\n        return searcher.getResult();\n    }\n\n    @Override\n    public SearchResult<ProcessDeploymentInfo> searchProcessDeploymentInfos(final SearchOptions searchOptions)\n            throws SearchException {\n        return processDeploymentAPIDelegate.searchProcessDeploymentInfos(searchOptions);\n    }\n\n    @Override\n    public SearchResult<ProcessDeploymentInfo> searchProcessDeploymentInfosCanBeStartedBy(final long userId,\n            final SearchOptions searchOptions)\n            throws RetrieveException, SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        final SearchProcessDefinitionsDescriptor searchDescriptor = searchEntitiesDescriptor\n                .getSearchProcessDefinitionsDescriptor();\n        final SearchProcessDeploymentInfosCanBeStartedBy transactionSearch = new SearchProcessDeploymentInfosCanBeStartedBy(\n                processDefinitionService,\n                searchDescriptor, searchOptions, userId);\n        try {\n            transactionSearch.execute();\n        } catch (final SBonitaException e) {\n            throw new SearchException(\"Error while retrieving process definitions: \" + e.getMessage(), e);\n        }\n        return transactionSearch.getResult();\n    }\n\n    @Override\n    public SearchResult<ProcessDeploymentInfo> searchProcessDeploymentInfosCanBeStartedByUsersManagedBy(\n            final long managerUserId,\n            final SearchOptions searchOptions) throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final SearchProcessDefinitionsDescriptor searchDescriptor = searchEntitiesDescriptor\n                .getSearchProcessDefinitionsDescriptor();\n        final SearchProcessDeploymentInfosCanBeStartedByUsersManagedBy transactionSearch = new SearchProcessDeploymentInfosCanBeStartedByUsersManagedBy(\n                processDefinitionService, searchDescriptor, searchOptions, managerUserId);\n        try {\n            transactionSearch.execute();\n        } catch (final SBonitaException e) {\n            throw new SearchException(e);\n        }\n        return transactionSearch.getResult();\n    }\n\n    @Override\n    public SearchResult<ProcessDeploymentInfo> searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(\n            final long userId,\n            final SearchOptions searchOptions) throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final SearchProcessDefinitionsDescriptor searchDescriptor = searchEntitiesDescriptor\n                .getSearchProcessDefinitionsDescriptor();\n        final SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor searcher = new SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(\n                processDefinitionService, searchDescriptor, searchOptions, userId);\n        try {\n            searcher.execute();\n        } catch (final SBonitaException sbe) {\n            throw new SearchException(sbe);\n        }\n        return searcher.getResult();\n    }\n\n    @Override\n    public SearchResult<ProcessDeploymentInfo> searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(\n            final long supervisorId,\n            final SearchOptions searchOptions) throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final SearchProcessDefinitionsDescriptor searchDescriptor = searchEntitiesDescriptor\n                .getSearchProcessDefinitionsDescriptor();\n        final SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy searcher = new SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(\n                processDefinitionService, searchDescriptor, searchOptions, supervisorId);\n        try {\n            searcher.execute();\n        } catch (final SBonitaException sbe) {\n            throw new SearchException(sbe);\n        }\n        return searcher.getResult();\n    }\n\n    @Override\n    public SearchResult<ProcessDeploymentInfo> searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks(\n            final SearchOptions searchOptions)\n            throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final SearchProcessDefinitionsDescriptor searchDescriptor = searchEntitiesDescriptor\n                .getSearchProcessDefinitionsDescriptor();\n        final SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasks searcher = new SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasks(\n                processDefinitionService, searchDescriptor, searchOptions);\n        try {\n            searcher.execute();\n        } catch (final SBonitaException sbe) {\n            throw new SearchException(sbe);\n        }\n        return searcher.getResult();\n    }\n\n    @Override\n    public Map<String, Map<String, Long>> getActiveFlownodeStateCountersForProcessDefinition(\n            final long processDefinitionId) throws ProcessDefinitionNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final HashMap<String, Map<String, Long>> countersForProcessDefinition = new HashMap<>();\n        try {\n            //make sure a process definition with this Id exists\n            serviceAccessor.getProcessDefinitionService().getProcessDeploymentInfo(processDefinitionId);\n            // Active flownodes:\n            final List<SFlowNodeInstanceStateCounter> flownodes = serviceAccessor.getActivityInstanceService()\n                    .getNumberOfFlownodesOfProcessDefinitionInAllStates(\n                            processDefinitionId);\n            for (final SFlowNodeInstanceStateCounter nodeCounter : flownodes) {\n                final String flownodeName = nodeCounter.getFlownodeName();\n                final Map<String, Long> flownodeCounters = getFlowNodeCounters(countersForProcessDefinition,\n                        flownodeName);\n                flownodeCounters.put(nodeCounter.getStateName(), nodeCounter.getNumberOf());\n                countersForProcessDefinition.put(flownodeName, flownodeCounters);\n            }\n        } catch (final SBonitaReadException e) {\n            throw new RetrieveException(e);\n        } catch (SProcessDefinitionNotFoundException e) {\n            throw new ProcessDefinitionNotFoundException(processDefinitionId, e);\n        }\n        return countersForProcessDefinition;\n    }\n\n    @Override\n    public Map<String, Map<String, Long>> getFlownodeStateCounters(final long processInstanceId) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final HashMap<String, Map<String, Long>> countersForProcessInstance = new HashMap<>();\n        try {\n            //make sure a process instance with this Id exists\n            // -- BEWARE: when a process instance is started by a Start Timer Event,\n            // -- there is NO 'stateid=0' archived in the ARCH_PROCESS_INSTANCE table\n            // -- Therefore, if such a process instance is still running, there won't be any record\n            // -- in the ARCH_PROCESS_INSTANCE table.\n            // -- Hence the need to also check in the PROCESS_INSTANCE table.\n            if (serviceAccessor.getProcessInstanceService().getLastArchivedProcessInstance(processInstanceId) == null\n                    && serviceAccessor.getProcessInstanceService().getProcessInstance(processInstanceId) == null) {\n                throw new SProcessInstanceNotFoundException(processInstanceId);\n            }\n            // Active flownodes:\n            final List<SFlowNodeInstanceStateCounter> flownodes = serviceAccessor.getActivityInstanceService()\n                    .getNumberOfFlownodesInAllStates(\n                            processInstanceId);\n            for (final SFlowNodeInstanceStateCounter nodeCounter : flownodes) {\n                if (nodeCounter.getStateName() != null) {\n                    final String flownodeName = nodeCounter.getFlownodeName();\n                    final Map<String, Long> flownodeCounters = getFlowNodeCounters(countersForProcessInstance,\n                            flownodeName);\n                    flownodeCounters.put(nodeCounter.getStateName(), nodeCounter.getNumberOf());\n                    countersForProcessInstance.put(flownodeName, flownodeCounters);\n                }\n            }\n            // Archived flownodes:\n            final List<SFlowNodeInstanceStateCounter> archivedFlownodes = serviceAccessor.getActivityInstanceService()\n                    .getNumberOfArchivedFlownodesInAllStates(\n                            processInstanceId);\n            for (final SFlowNodeInstanceStateCounter nodeCounter : archivedFlownodes) {\n                if (nodeCounter.getStateName() != null) {\n                    final String flownodeName = nodeCounter.getFlownodeName();\n                    final Map<String, Long> flownodeCounters = getFlowNodeCounters(countersForProcessInstance,\n                            flownodeName);\n                    flownodeCounters.put(nodeCounter.getStateName(), nodeCounter.getNumberOf());\n                    countersForProcessInstance.put(flownodeName, flownodeCounters);\n                }\n            }\n        } catch (final SProcessInstanceNotFoundException | SProcessInstanceReadException e) {\n            //cannot throw directly a ProcessInstanceNotFoundException to avoid API break\n            throw new RetrieveException(new ProcessInstanceNotFoundException(e, processInstanceId));\n        } catch (final SBonitaReadException e) {\n            throw new RetrieveException(e);\n        }\n        return countersForProcessInstance;\n    }\n\n    private Map<String, Long> getFlowNodeCounters(final HashMap<String, Map<String, Long>> counters,\n            final String flowNodeName) {\n        return counters.computeIfAbsent(flowNodeName, k -> new HashMap<>());\n    }\n\n    @Override\n    public SearchResult<ProcessDeploymentInfo> searchProcessDeploymentInfosSupervisedBy(final long userId,\n            final SearchOptions searchOptions)\n            throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final SearchProcessDefinitionsDescriptor searchDescriptor = searchEntitiesDescriptor\n                .getSearchProcessDefinitionsDescriptor();\n        final SearchProcessDeploymentInfosSupervised searcher = new SearchProcessDeploymentInfosSupervised(\n                processDefinitionService, searchDescriptor,\n                searchOptions, userId);\n        try {\n            searcher.execute();\n        } catch (final SBonitaException sbe) {\n            throw new SearchException(sbe);\n        }\n        return searcher.getResult();\n    }\n\n    @Override\n    public SearchResult<HumanTaskInstance> searchAssignedTasksSupervisedBy(final long supervisorId,\n            final SearchOptions searchOptions) throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        return AbstractHumanTaskInstanceSearchEntity.searchHumanTaskInstance(\n                searchEntitiesDescriptor.getSearchHumanTaskInstanceDescriptor(),\n                searchOptions,\n                flowNodeStateManager,\n                (queryOptions) -> activityInstanceService.getNumberOfAssignedTasksSupervisedBy(supervisorId,\n                        queryOptions),\n                (queryOptions) -> activityInstanceService.searchAssignedTasksSupervisedBy(supervisorId, queryOptions))\n                .search();\n    }\n\n    @Override\n    public SearchResult<ArchivedHumanTaskInstance> searchArchivedHumanTasksSupervisedBy(final long supervisorId,\n            final SearchOptions searchOptions)\n            throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final SearchArchivedHumanTasksSupervisedBy searchedTasksTransaction = new SearchArchivedHumanTasksSupervisedBy(\n                supervisorId, activityInstanceService,\n                flowNodeStateManager, searchEntitiesDescriptor.getSearchArchivedHumanTaskInstanceDescriptor(),\n                searchOptions);\n\n        try {\n            searchedTasksTransaction.execute();\n        } catch (final SBonitaException sbe) {\n            throw new SearchException(sbe);\n        }\n        return searchedTasksTransaction.getResult();\n    }\n\n    @Override\n    public SearchResult<ProcessSupervisor> searchProcessSupervisors(final SearchOptions searchOptions)\n            throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final SupervisorMappingService supervisorService = serviceAccessor.getSupervisorService();\n        final SearchProcessSupervisorDescriptor searchDescriptor = new SearchProcessSupervisorDescriptor();\n        final SearchSupervisors searchSupervisorsTransaction = new SearchSupervisors(supervisorService,\n                searchDescriptor, searchOptions);\n        try {\n            searchSupervisorsTransaction.execute();\n            return searchSupervisorsTransaction.getResult();\n        } catch (final SBonitaException e) {\n            throw new SearchException(e);\n        }\n    }\n\n    @Override\n    public boolean isUserProcessSupervisor(final long processDefinitionId, final long userId) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final SupervisorMappingService supervisorService = serviceAccessor.getSupervisorService();\n        try {\n            return supervisorService.isProcessSupervisor(processDefinitionId, userId);\n        } catch (final SBonitaReadException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public void deleteSupervisor(final long supervisorId) throws DeletionException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final SupervisorMappingService supervisorService = serviceAccessor.getSupervisorService();\n        try {\n            supervisorService.deleteProcessSupervisor(supervisorId);\n            log.info(\"The process manager has been deleted with id = <\" + supervisorId + \">.\");\n\n        } catch (final SSupervisorNotFoundException spsnfe) {\n            throw new DeletionException(new SupervisorNotFoundException(\n                    \"The process manager was not found with id = <\" + supervisorId + \">\"));\n        } catch (final SSupervisorDeletionException e) {\n            throw new DeletionException(e);\n        }\n    }\n\n    @Override\n    public void deleteSupervisor(final Long processDefinitionId, final Long userId, final Long roleId,\n            final Long groupId) throws DeletionException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final SupervisorMappingService supervisorService = serviceAccessor.getSupervisorService();\n\n        try {\n            final List<SProcessSupervisor> sProcessSupervisors = searchSProcessSupervisors(processDefinitionId, userId,\n                    groupId, roleId);\n\n            if (!sProcessSupervisors.isEmpty()) {\n                // Then, delete it\n                supervisorService.deleteProcessSupervisor(sProcessSupervisors.get(0));\n                log.info(\"The process manager has been deleted with process definition id = <\"\n                        + processDefinitionId + \">, user id = <\" + userId + \">, group id = <\" + groupId\n                        + \">, and role id = <\" + roleId + \">.\");\n            } else {\n                throw new DeletionException(new SupervisorNotFoundException(\n                        \"The process manager was not found with process definition id = <\"\n                                + processDefinitionId + \">, user id = <\" + userId + \">, group id = <\" + groupId\n                                + \">, and role id = <\" + roleId + \">.\"));\n            }\n        } catch (final SBonitaException e) {\n            throw new DeletionException(e);\n        }\n    }\n\n    private void deleteAllSupervisorsOfProcess(final long processDefinitionId) throws DeletionException {\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, Integer.MAX_VALUE);\n        builder.filter(ProcessSupervisorSearchDescriptor.PROCESS_DEFINITION_ID, processDefinitionId);\n        try {\n            SearchResult<ProcessSupervisor> processSupervisors = searchProcessSupervisors(builder.done());\n\n            List<Long> supervisorIds = processSupervisors.getResult().stream().map(ProcessSupervisor::getSupervisorId)\n                    .collect(Collectors.toList());\n\n            for (Long id : supervisorIds) {\n                deleteSupervisor(id);\n            }\n        } catch (SearchException e) {\n            throw new DeletionException(e);\n        }\n    }\n\n    @Override\n    public ProcessSupervisor createProcessSupervisorForUser(final long processDefinitionId, final long userId)\n            throws CreationException {\n        return createSupervisor(SProcessSupervisor.builder().processDefId(processDefinitionId).userId(userId).build());\n    }\n\n    @Override\n    public ProcessSupervisor createProcessSupervisorForRole(final long processDefinitionId, final long roleId)\n            throws CreationException {\n        return createSupervisor(SProcessSupervisor.builder().processDefId(processDefinitionId).roleId(roleId).build());\n    }\n\n    @Override\n    public ProcessSupervisor createProcessSupervisorForGroup(final long processDefinitionId, final long groupId)\n            throws CreationException {\n        return createSupervisor(\n                SProcessSupervisor.builder().processDefId(processDefinitionId).groupId(groupId).build());\n    }\n\n    @Override\n    public ProcessSupervisor createProcessSupervisorForMembership(final long processDefinitionId, final long groupId,\n            final long roleId) throws CreationException {\n        return createSupervisor(\n                SProcessSupervisor.builder().processDefId(processDefinitionId).groupId(groupId).roleId(roleId).build());\n    }\n\n    private ProcessSupervisor createSupervisor(final SProcessSupervisor sProcessSupervisor) throws CreationException {\n        final ServiceAccessor ServiceAccessor = getServiceAccessor();\n        final SupervisorMappingService supervisorService = ServiceAccessor.getSupervisorService();\n\n        try {\n            checkIfProcessSupervisorAlreadyExists(sProcessSupervisor.getProcessDefId(), sProcessSupervisor.getUserId(),\n                    sProcessSupervisor.getGroupId(),\n                    sProcessSupervisor.getRoleId());\n\n            final SProcessSupervisor supervisor = supervisorService.createProcessSupervisor(sProcessSupervisor);\n            log.info(\"The process manager has been created with process definition id = <\"\n                    + sProcessSupervisor.getProcessDefId() + \">, user id = <\"\n                    + sProcessSupervisor.getUserId() + \">, group id = <\"\n                    + sProcessSupervisor.getGroupId() + \">, and role id = <\"\n                    + sProcessSupervisor.getRoleId() + \">\");\n            return ModelConvertor.toProcessSupervisor(supervisor);\n        } catch (final SBonitaException e) {\n            throw new CreationException(e);\n        }\n    }\n\n    private void checkIfProcessSupervisorAlreadyExists(final long processDefinitionId, final Long userId,\n            final Long groupId, final Long roleId)\n            throws AlreadyExistsException {\n        try {\n            final List<SProcessSupervisor> processSupervisors = searchSProcessSupervisors(processDefinitionId, userId,\n                    groupId, roleId);\n            if (!processSupervisors.isEmpty()) {\n                throw new AlreadyExistsException(\"This supervisor already exists for process definition id = <\"\n                        + processDefinitionId + \">, user id = <\"\n                        + userId + \">, group id = <\" + groupId + \">, role id = <\" + roleId + \">\");\n            }\n        } catch (final SBonitaReadException e1) {\n            // Nothing to do\n        }\n    }\n\n    protected List<SProcessSupervisor> searchSProcessSupervisors(final Long processDefinitionId, final Long userId,\n            final Long groupId, final Long roleId)\n            throws SBonitaReadException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final SupervisorMappingService supervisorService = serviceAccessor.getSupervisorService();\n\n        final List<OrderByOption> oderByOptions = Collections.singletonList(\n                new OrderByOption(SProcessSupervisor.class, SProcessSupervisor.USER_ID_KEY, OrderByType.DESC));\n        final List<FilterOption> filterOptions = new ArrayList<>();\n        filterOptions.add(new FilterOption(SProcessSupervisor.class, SProcessSupervisor.PROCESS_DEF_ID_KEY,\n                processDefinitionId == null ? -1 : processDefinitionId));\n        filterOptions.add(new FilterOption(SProcessSupervisor.class, SProcessSupervisor.USER_ID_KEY,\n                userId == null ? -1 : userId));\n        filterOptions.add(new FilterOption(SProcessSupervisor.class, SProcessSupervisor.GROUP_ID_KEY,\n                groupId == null ? -1 : groupId));\n        filterOptions.add(new FilterOption(SProcessSupervisor.class, SProcessSupervisor.ROLE_ID_KEY,\n                roleId == null ? -1 : roleId));\n\n        return supervisorService.searchProcessSupervisors(new QueryOptions(0, 1, oderByOptions, filterOptions, null));\n    }\n\n    @Override\n    public SearchResult<ProcessDeploymentInfo> searchUncategorizedProcessDeploymentInfosCanBeStartedBy(\n            final long userId, final SearchOptions searchOptions)\n            throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        final SearchProcessDefinitionsDescriptor searchDescriptor = searchEntitiesDescriptor\n                .getSearchProcessDefinitionsDescriptor();\n        final SearchUncategorizedProcessDeploymentInfosCanBeStartedBy transactionSearch = new SearchUncategorizedProcessDeploymentInfosCanBeStartedBy(\n                processDefinitionService, searchDescriptor, searchOptions, userId);\n        try {\n            transactionSearch.execute();\n        } catch (final SBonitaException e) {\n            throw new SearchException(\"Error while retrieving process definitions\", e);\n        }\n        return transactionSearch.getResult();\n    }\n\n    @Override\n    public SearchResult<ArchivedHumanTaskInstance> searchArchivedHumanTasksManagedBy(final long managerUserId,\n            final SearchOptions searchOptions)\n            throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final SearchArchivedTasksManagedBy searchTransaction = new SearchArchivedTasksManagedBy(managerUserId,\n                searchOptions, activityInstanceService,\n                flowNodeStateManager, searchEntitiesDescriptor.getSearchArchivedHumanTaskInstanceDescriptor());\n        try {\n            searchTransaction.execute();\n        } catch (final SBonitaException e) {\n            throw new SearchException(e);\n        }\n        return searchTransaction.getResult();\n    }\n\n    @Override\n    public SearchResult<ProcessInstance> searchOpenProcessInstancesInvolvingUser(final long userId,\n            final SearchOptions searchOptions) throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        final SearchOpenProcessInstancesInvolvingUser searchOpenProcessInstances = new SearchOpenProcessInstancesInvolvingUser(\n                processInstanceService,\n                searchEntitiesDescriptor.getSearchProcessInstanceDescriptor(), userId, searchOptions,\n                processDefinitionService);\n        try {\n            searchOpenProcessInstances.execute();\n            return searchOpenProcessInstances.getResult();\n        } catch (final SBonitaException sbe) {\n            throw new SearchException(sbe);\n        }\n    }\n\n    @Override\n    public SearchResult<ProcessInstance> searchOpenProcessInstancesInvolvingUsersManagedBy(final long managerUserId,\n            final SearchOptions searchOptions)\n            throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        final SearchOpenProcessInstancesInvolvingUsersManagedBy searchOpenProcessInstances = new SearchOpenProcessInstancesInvolvingUsersManagedBy(\n                processInstanceService, searchEntitiesDescriptor.getSearchProcessInstanceDescriptor(), managerUserId,\n                searchOptions, processDefinitionService);\n        try {\n            searchOpenProcessInstances.execute();\n            return searchOpenProcessInstances.getResult();\n        } catch (final SBonitaException sbe) {\n            throw new SearchException(sbe);\n        }\n    }\n\n    @Override\n    public SearchResult<ArchivedHumanTaskInstance> searchArchivedHumanTasks(final SearchOptions searchOptions)\n            throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n\n        final SearchArchivedTasks searchArchivedTasks = new SearchArchivedTasks(activityInstanceService,\n                flowNodeStateManager,\n                searchEntitiesDescriptor.getSearchArchivedHumanTaskInstanceDescriptor(), searchOptions);\n        try {\n            searchArchivedTasks.execute();\n        } catch (final SBonitaException sbe) {\n            throw new SearchException(sbe);\n        }\n        return searchArchivedTasks.getResult();\n    }\n\n    @Override\n    public SearchResult<HumanTaskInstance> searchAssignedTasksManagedBy(final long managerUserId,\n            final SearchOptions searchOptions) throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager();\n        return AbstractHumanTaskInstanceSearchEntity.searchHumanTaskInstance(\n                searchEntitiesDescriptor.getSearchHumanTaskInstanceDescriptor(),\n                searchOptions,\n                flowNodeStateManager,\n                (queryOptions) -> activityInstanceService.getNumberOfAssignedTasksManagedBy(managerUserId,\n                        queryOptions),\n                (queryOptions) -> activityInstanceService.searchAssignedTasksManagedBy(managerUserId, queryOptions))\n                .search();\n    }\n\n    @Override\n    public SearchResult<ArchivedProcessInstance> searchArchivedProcessInstancesSupervisedBy(final long userId,\n            final SearchOptions searchOptions)\n            throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final SearchArchivedProcessInstancesSupervisedBy searchArchivedProcessInstances = new SearchArchivedProcessInstancesSupervisedBy(\n                userId,\n                processInstanceService, serviceAccessor.getProcessDefinitionService(),\n                searchEntitiesDescriptor.getSearchArchivedProcessInstanceDescriptor(),\n                searchOptions);\n        try {\n            searchArchivedProcessInstances.execute();\n            return searchArchivedProcessInstances.getResult();\n        } catch (final SBonitaException sbe) {\n            throw new SearchException(sbe);\n        }\n    }\n\n    @Override\n    public SearchResult<ArchivedProcessInstance> searchArchivedProcessInstancesInvolvingUser(final long userId,\n            final SearchOptions searchOptions)\n            throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final SearchArchivedProcessInstancesInvolvingUser searchArchivedProcessInstances = new SearchArchivedProcessInstancesInvolvingUser(\n                userId,\n                processInstanceService, serviceAccessor.getProcessDefinitionService(),\n                searchEntitiesDescriptor.getSearchArchivedProcessInstanceDescriptor(),\n                searchOptions);\n        try {\n            searchArchivedProcessInstances.execute();\n            return searchArchivedProcessInstances.getResult();\n        } catch (final SBonitaException sbe) {\n            throw new SearchException(sbe);\n        }\n    }\n\n    @Override\n    public SearchResult<HumanTaskInstance> searchPendingTasksForUser(final long userId,\n            final SearchOptions searchOptions) throws SearchException {\n        return searchTasksForUser(userId, searchOptions, \"\");\n    }\n\n    /**\n     * @param queryType can be :\n     *        PendingOrAssignedOrAssignedToOthers if we also want to retrieve tasks directly assigned to this user or\n     *        tasks assigned\n     *        to other users.\n     *        PendingOrAssigned if we also want to retrieve tasks directly assigned to this user.\n     *        empty for the default behaviour; retrieve pending tasks for user.\n     */\n    private SearchResult<HumanTaskInstance> searchTasksForUser(final long userId, final SearchOptions searchOptions,\n            final String queryType) throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n\n        switch (queryType) {\n            case PENDING_OR_ASSIGNED_OR_ASSIGNED_TO_OTHERS:\n                return AbstractHumanTaskInstanceSearchEntity.searchHumanTaskInstance(\n                        searchEntitiesDescriptor.getSearchHumanTaskInstanceDescriptor(),\n                        searchOptions,\n                        flowNodeStateManager,\n                        (queryOptions) -> activityInstanceService.getNumberOfPendingOrAssignedOrAssignedToOthersTasks(\n                                userId,\n                                queryOptions),\n                        (queryOptions) -> activityInstanceService.searchPendingOrAssignedOrAssignedToOthersTasks(userId,\n                                queryOptions))\n                        .search();\n            case PENDING_OR_ASSIGNED:\n                return AbstractHumanTaskInstanceSearchEntity.searchHumanTaskInstance(\n                        searchEntitiesDescriptor.getSearchHumanTaskInstanceDescriptor(),\n                        searchOptions,\n                        flowNodeStateManager,\n                        (queryOptions) -> activityInstanceService.getNumberOfPendingOrAssignedTasks(userId,\n                                queryOptions),\n                        (queryOptions) -> activityInstanceService.searchPendingOrAssignedTasks(userId, queryOptions))\n                        .search();\n            default:\n                return AbstractHumanTaskInstanceSearchEntity\n                        .searchHumanTaskInstance(searchEntitiesDescriptor.getSearchHumanTaskInstanceDescriptor(),\n                                searchOptions,\n                                flowNodeStateManager,\n                                (queryOptions) -> activityInstanceService.getNumberOfPendingTasksForUser(userId,\n                                        queryOptions),\n                                (queryOptions) -> activityInstanceService.searchPendingTasksForUser(userId,\n                                        queryOptions))\n                        .search();\n        }\n    }\n\n    @Override\n    public SearchResult<HumanTaskInstance> searchPendingTasksAssignedToUser(final long userId,\n            final SearchOptions searchOptions) throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        return AbstractHumanTaskInstanceSearchEntity\n                .searchHumanTaskInstance(searchEntitiesDescriptor.getSearchHumanTaskInstanceDescriptor(),\n                        searchOptions,\n                        flowNodeStateManager,\n                        (queryOptions) -> activityInstanceService.getNumberOfPendingTasksAssignedTo(userId,\n                                queryOptions),\n                        (queryOptions) -> activityInstanceService.searchPendingTasksAssignedTo(userId, queryOptions))\n                .search();\n    }\n\n    @Override\n    public SearchResult<HumanTaskInstance> searchMyAvailableHumanTasks(final long userId,\n            final SearchOptions searchOptions) throws SearchException {\n        return searchTasksForUser(userId, searchOptions, PENDING_OR_ASSIGNED);\n    }\n\n    @Override\n    public SearchResult<HumanTaskInstance> searchPendingOrAssignedToUserOrAssignedToOthersTasks(final long userId,\n            final SearchOptions searchOptions) throws SearchException {\n        return searchTasksForUser(userId, searchOptions, PENDING_OR_ASSIGNED_OR_ASSIGNED_TO_OTHERS);\n    }\n\n    @Override\n    public SearchResult<HumanTaskInstance> searchPendingTasksSupervisedBy(final long userId,\n            final SearchOptions searchOptions) throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        return AbstractHumanTaskInstanceSearchEntity\n                .searchHumanTaskInstance(searchEntitiesDescriptor.getSearchHumanTaskInstanceDescriptor(),\n                        searchOptions,\n                        flowNodeStateManager,\n                        (queryOptions) -> activityInstanceService.getNumberOfPendingTasksSupervisedBy(userId,\n                                queryOptions),\n                        (queryOptions) -> activityInstanceService.searchPendingTasksSupervisedBy(userId, queryOptions))\n                .search();\n    }\n\n    @Override\n    public SearchResult<Comment> searchComments(final SearchOptions searchOptions) throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final SCommentService commentService = serviceAccessor.getCommentService();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final SearchComments searchComments = new SearchComments(searchEntitiesDescriptor.getSearchCommentDescriptor(),\n                searchOptions, commentService);\n        try {\n            searchComments.execute();\n            return searchComments.getResult();\n        } catch (final SBonitaException sbe) {\n            throw new SearchException(sbe);\n        }\n    }\n\n    @Override\n    public Comment addProcessComment(final long processInstanceId, final String comment) throws CreationException {\n        return addProcessCommentOnBehalfOfUser(processInstanceId, comment, getUserId());\n    }\n\n    @Override\n    public Comment addProcessCommentOnBehalfOfUser(final long processInstanceId, final String comment, long userId)\n            throws CreationException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        try {\n            serviceAccessor.getProcessInstanceService().getProcessInstance(processInstanceId);\n        } catch (final SProcessInstanceReadException | SProcessInstanceNotFoundException e) {\n            throw new RetrieveException(buildCantAddCommentOnProcessInstance(), e); // FIXME: should be another exception\n        }\n        final SCommentService commentService = serviceAccessor.getCommentService();\n        try {\n            SComment sComment = commentService.addComment(processInstanceId, comment, userId);\n            return ModelConvertor.toComment(sComment);\n        } catch (final SBonitaException e) {\n            throw new CreationException(buildCantAddCommentOnProcessInstance(), e.getCause());\n        }\n    }\n\n    private String buildCantAddCommentOnProcessInstance() {\n        return \"Cannot add a comment on a finished or inexistant process instance\";\n    }\n\n    @Override\n    public Document attachDocument(final long processInstanceId, final String documentName, final String fileName,\n            final String mimeType, final String url)\n            throws DocumentAttachmentException, ProcessInstanceNotFoundException {\n        return documentAPI.attachDocument(processInstanceId, documentName, fileName, mimeType, url);\n    }\n\n    @Override\n    public Document attachDocument(final long processInstanceId, final String documentName, final String fileName,\n            final String mimeType,\n            final byte[] documentContent) throws DocumentAttachmentException, ProcessInstanceNotFoundException {\n        return documentAPI.attachDocument(processInstanceId, documentName, fileName, mimeType, documentContent);\n    }\n\n    @Override\n    public Document attachNewDocumentVersion(final long processInstanceId, final String documentName,\n            final String fileName, final String mimeType,\n            final String url) throws DocumentAttachmentException {\n        return documentAPI.attachNewDocumentVersion(processInstanceId, documentName, fileName, mimeType, url);\n    }\n\n    @Override\n    public Document attachNewDocumentVersion(final long processInstanceId, final String documentName,\n            final String contentFileName,\n            final String contentMimeType, final byte[] documentContent) throws DocumentAttachmentException {\n        return documentAPI.attachNewDocumentVersion(processInstanceId, documentName, contentFileName, contentMimeType,\n                documentContent);\n    }\n\n    @Override\n    public Document getDocument(final long documentId) throws DocumentNotFoundException {\n        return documentAPI.getDocument(documentId);\n    }\n\n    @Override\n    public List<Document> getLastVersionOfDocuments(final long processInstanceId, final int pageIndex,\n            final int numberPerPage,\n            final DocumentCriterion pagingCriterion) throws DocumentException, ProcessInstanceNotFoundException {\n        return documentAPI.getLastVersionOfDocuments(processInstanceId, pageIndex, numberPerPage, pagingCriterion);\n    }\n\n    @Override\n    public byte[] getDocumentContent(final String documentStorageId) throws DocumentNotFoundException {\n        return documentAPI.getDocumentContent(documentStorageId);\n    }\n\n    @Override\n    public Document getLastDocument(final long processInstanceId, final String documentName)\n            throws DocumentNotFoundException {\n        return documentAPI.getLastDocument(processInstanceId, documentName);\n    }\n\n    @Override\n    public long getNumberOfDocuments(final long processInstanceId) throws DocumentException {\n        return documentAPI.getNumberOfDocuments(processInstanceId);\n    }\n\n    @Override\n    public Document getDocumentAtProcessInstantiation(final long processInstanceId, final String documentName)\n            throws DocumentNotFoundException {\n\n        return documentAPI.getDocumentAtProcessInstantiation(processInstanceId, documentName);\n    }\n\n    @Override\n    public Document getDocumentAtActivityInstanceCompletion(final long activityInstanceId, final String documentName)\n            throws DocumentNotFoundException {\n        return documentAPI.getDocumentAtActivityInstanceCompletion(activityInstanceId, documentName);\n    }\n\n    @Override\n    public SearchResult<HumanTaskInstance> searchPendingTasksManagedBy(final long managerUserId,\n            final SearchOptions searchOptions) throws SearchException {\n        return taskInvolvementDelegate.searchPendingTasksManagedBy(managerUserId, searchOptions);\n    }\n\n    @Override\n    public Map<Long, Long> getNumberOfOverdueOpenTasks(final List<Long> userIds) throws RetrieveException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n\n        try {\n            return activityInstanceService.getNumberOfOverdueOpenTasksForUsers(userIds);\n        } catch (final SBonitaException e) {\n            logError(e);\n            throw new RetrieveException(e.getMessage());\n        }\n    }\n\n    @Override\n    public SearchResult<ProcessDeploymentInfo> searchUncategorizedProcessDeploymentInfos(\n            final SearchOptions searchOptions) throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        final SearchProcessDefinitionsDescriptor searchDescriptor = searchEntitiesDescriptor\n                .getSearchProcessDefinitionsDescriptor();\n        final SearchUncategorizedProcessDeploymentInfos transactionSearch = new SearchUncategorizedProcessDeploymentInfos(\n                processDefinitionService,\n                searchDescriptor, searchOptions);\n        try {\n            transactionSearch.execute();\n        } catch (final SBonitaException e) {\n            throw new SearchException(\"Problem encountered while searching for Uncategorized Process Definitions\", e);\n        }\n        return transactionSearch.getResult();\n    }\n\n    @Override\n    public SearchResult<Comment> searchCommentsManagedBy(final long managerUserId, final SearchOptions searchOptions)\n            throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final SCommentService commentService = serviceAccessor.getCommentService();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final SearchCommentsManagedBy searchComments = new SearchCommentsManagedBy(\n                searchEntitiesDescriptor.getSearchCommentDescriptor(), searchOptions,\n                commentService, managerUserId);\n        try {\n            searchComments.execute();\n            return searchComments.getResult();\n        } catch (final SBonitaException sbe) {\n            throw new SearchException(sbe);\n        }\n    }\n\n    @Override\n    public SearchResult<Comment> searchCommentsInvolvingUser(final long userId, final SearchOptions searchOptions)\n            throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final SCommentService commentService = serviceAccessor.getCommentService();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final SearchCommentsInvolvingUser searchComments = new SearchCommentsInvolvingUser(\n                searchEntitiesDescriptor.getSearchCommentDescriptor(),\n                searchOptions, commentService, userId);\n        try {\n            searchComments.execute();\n            return searchComments.getResult();\n        } catch (final SBonitaException sbe) {\n            throw new SearchException(sbe);\n        }\n    }\n\n    @Override\n    public List<Long> getChildrenInstanceIdsOfProcessInstance(final long processInstanceId, final int startIndex,\n            final int maxResults,\n            final ProcessInstanceCriterion criterion) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n\n        long totalNumber;\n        try {\n            totalNumber = processInstanceService.getNumberOfChildInstancesOfProcessInstance(processInstanceId);\n            if (totalNumber == 0) {\n                return Collections.emptyList();\n            }\n            final OrderAndField orderAndField = OrderAndFields.getOrderAndFieldForProcessInstance(criterion);\n            return processInstanceService.getChildInstanceIdsOfProcessInstance(processInstanceId, startIndex,\n                    maxResults, orderAndField.getField(),\n                    orderAndField.getOrder());\n        } catch (final SProcessInstanceReadException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public SearchResult<ProcessDeploymentInfo> searchUncategorizedProcessDeploymentInfosSupervisedBy(final long userId,\n            final SearchOptions searchOptions)\n            throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        final SearchProcessDefinitionsDescriptor searchDescriptor = searchEntitiesDescriptor\n                .getSearchProcessDefinitionsDescriptor();\n        final SearchUncategorizedProcessDeploymentInfosSupervisedBy transactionSearch = new SearchUncategorizedProcessDeploymentInfosSupervisedBy(\n                processDefinitionService, searchDescriptor, searchOptions, userId);\n        try {\n            transactionSearch.execute();\n        } catch (final SBonitaException e) {\n            throw new SearchException(\n                    \"Problem encountered while searching for Uncategorized Process Definitions for a supervisor\", e);\n        }\n        return transactionSearch.getResult();\n    }\n\n    @Override\n    public Map<Long, ProcessDeploymentInfo> getProcessDeploymentInfosFromIds(final List<Long> processDefinitionIds) {\n        return ProcessDeploymentAPIDelegate.getInstance().getProcessDeploymentInfosFromIds(processDefinitionIds);\n    }\n\n    @Override\n    public List<ConnectorImplementationDescriptor> getConnectorImplementations(final long processDefinitionId,\n            final int startIndex, final int maxsResults,\n            final ConnectorCriterion sortingCriterion) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final ConnectorService connectorService = serviceAccessor.getConnectorService();\n        final OrderAndField orderAndField = OrderAndFields.getOrderAndFieldForConnectorImplementation(sortingCriterion);\n        try {\n            final List<SConnectorImplementationDescriptor> sConnectorImplementationDescriptors = connectorService\n                    .getConnectorImplementations(processDefinitionId, startIndex, maxsResults, orderAndField.getField(),\n                            orderAndField.getOrder());\n            return ModelConvertor.toConnectorImplementationDescriptors(sConnectorImplementationDescriptors);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public long getNumberOfConnectorImplementations(final long processDefinitionId) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final ConnectorService connectorService = serviceAccessor.getConnectorService();\n        try {\n            return connectorService.getNumberOfConnectorImplementations(processDefinitionId);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public SearchResult<ActivityInstance> searchActivities(final SearchOptions searchOptions) throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager();\n        final SearchActivityInstances searchActivityInstancesTransaction;\n        try {\n            searchActivityInstancesTransaction = new SearchActivityInstances(activityInstanceService,\n                    flowNodeStateManager,\n                    searchEntitiesDescriptor.getSearchActivityInstanceDescriptor(), searchOptions);\n            searchActivityInstancesTransaction.execute();\n        } catch (final SBonitaException e) {\n            throw new SearchException(e);\n        }\n        return searchActivityInstancesTransaction.getResult();\n    }\n\n    @Override\n    public SearchResult<ArchivedFlowNodeInstance> searchArchivedFlowNodeInstances(final SearchOptions searchOptions)\n            throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager();\n        final SearchArchivedFlowNodeInstances searchTransaction = new SearchArchivedFlowNodeInstances(\n                activityInstanceService, flowNodeStateManager,\n                searchEntitiesDescriptor.getSearchArchivedFlowNodeInstanceDescriptor(), searchOptions);\n        try {\n            searchTransaction.execute();\n        } catch (final SBonitaException e) {\n            throw new SearchException(e);\n        }\n        return searchTransaction.getResult();\n    }\n\n    @Override\n    public SearchResult<FlowNodeInstance> searchFlowNodeInstances(final SearchOptions searchOptions)\n            throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager();\n        final SearchFlowNodeInstances searchFlowNodeInstancesTransaction = new SearchFlowNodeInstances(\n                activityInstanceService, flowNodeStateManager,\n                searchEntitiesDescriptor.getSearchFlowNodeInstanceDescriptor(), searchOptions);\n        try {\n            searchFlowNodeInstancesTransaction.execute();\n        } catch (final SBonitaException e) {\n            throw new SearchException(e);\n        }\n        return searchFlowNodeInstancesTransaction.getResult();\n    }\n\n    @Override\n    public SearchResult<TimerEventTriggerInstance> searchTimerEventTriggerInstances(final long processInstanceId,\n            final SearchOptions searchOptions)\n            throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final EventInstanceService eventInstanceService = serviceAccessor.getEventInstanceService();\n        final SearchTimerEventTriggerInstances transaction = new SearchTimerEventTriggerInstances(eventInstanceService,\n                searchEntitiesDescriptor.getSearchEventTriggerInstanceDescriptor(), processInstanceId, searchOptions);\n        try {\n            transaction.execute();\n        } catch (final SBonitaException e) {\n            throw new SearchException(e);\n        }\n        return transaction.getResult();\n    }\n\n    @Override\n    public Date updateExecutionDateOfTimerEventTriggerInstance(final long timerEventTriggerInstanceId,\n            final Date executionDate)\n            throws TimerEventTriggerInstanceNotFoundException, UpdateException {\n        if (executionDate == null) {\n            throw new UpdateException(\"The date must be not null !!\");\n        }\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final EventInstanceService eventInstanceService = serviceAccessor.getEventInstanceService();\n        final SchedulerService schedulerService = serviceAccessor.getSchedulerService();\n\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        descriptor.addField(EXECUTION_DATE, executionDate.getTime());\n\n        try {\n            final STimerEventTriggerInstance sTimerEventTriggerInstance = eventInstanceService.getEventTriggerInstance(\n                    STimerEventTriggerInstance.class,\n                    timerEventTriggerInstanceId);\n            if (sTimerEventTriggerInstance == null) {\n                throw new TimerEventTriggerInstanceNotFoundException(timerEventTriggerInstanceId);\n            }\n            eventInstanceService.updateEventTriggerInstance(sTimerEventTriggerInstance, descriptor);\n            return schedulerService.rescheduleJob(sTimerEventTriggerInstance.getJobTriggerName(), executionDate);\n        } catch (final SBonitaException sbe) {\n            throw new UpdateException(sbe);\n        }\n    }\n\n    @Override\n    public SearchResult<ArchivedActivityInstance> searchArchivedActivities(final SearchOptions searchOptions)\n            throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager();\n        final SearchArchivedActivityInstances searchActivityInstancesTransaction = new SearchArchivedActivityInstances(\n                activityInstanceService,\n                flowNodeStateManager, searchEntitiesDescriptor.getSearchArchivedActivityInstanceDescriptor(),\n                searchOptions);\n        try {\n            searchActivityInstancesTransaction.execute();\n        } catch (final SBonitaException e) {\n            throw new SearchException(e);\n        }\n        return searchActivityInstancesTransaction.getResult();\n    }\n\n    @Override\n    public ConnectorImplementationDescriptor getConnectorImplementation(final long processDefinitionId,\n            final String connectorId, final String connectorVersion)\n            throws ConnectorNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final ConnectorService connectorService = serviceAccessor.getConnectorService();\n        final GetConnectorImplementation transactionContent = new GetConnectorImplementation(connectorService,\n                processDefinitionId, connectorId,\n                connectorVersion);\n        try {\n            transactionContent.execute();\n            final SConnectorImplementationDescriptor sConnectorImplementationDescriptor = transactionContent\n                    .getResult();\n            return ModelConvertor.toConnectorImplementationDescriptor(sConnectorImplementationDescriptor);\n        } catch (final SBonitaException e) {\n            throw new ConnectorNotFoundException(e);\n        }\n    }\n\n    @Override\n    @CustomTransactions\n    public void cancelProcessInstance(final long processInstanceId)\n            throws ProcessInstanceNotFoundException, UpdateException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final LockService lockService = serviceAccessor.getLockService();\n        final ProcessInstanceInterruptor processInstanceInterruptor = serviceAccessor.getProcessInstanceInterruptor();\n        BonitaLock lock = null;\n        try {\n            // lock process execution\n            lock = lockService.lock(processInstanceId, SFlowElementsContainerType.PROCESS.name());\n            inTx(() -> {\n                try {\n                    return processInstanceInterruptor.interruptProcessInstance(processInstanceId,\n                            SStateCategory.CANCELLING);\n                } catch (final SProcessInstanceNotFoundException spinfe) {\n                    throw new ProcessInstanceNotFoundException(processInstanceId);\n                } catch (final SBonitaException e) {\n                    throw new UpdateException(e);\n                }\n            });\n        } catch (ProcessInstanceNotFoundException | UpdateException e) {\n            throw e;\n        } catch (Exception e) {\n            throw new BonitaRuntimeException(String.format(\"Failed to cancel process instance %s\", processInstanceId),\n                    e);\n        } finally {\n            // unlock process execution\n            try {\n                lockService.unlock(lock);\n            } catch (final SLockException e) {\n                // ignore it\n            }\n        }\n    }\n\n    @Override\n    public void setProcessInstanceState(final ProcessInstance processInstance, final String state)\n            throws UpdateException {\n        // NOW, is only available for COMPLETED, ABORTED, CANCELLED, STARTED\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n        try {\n            final ProcessInstanceState processInstanceState = ModelConvertor.getProcessInstanceState(state);\n            final SetProcessInstanceState transactionContent = new SetProcessInstanceState(processInstanceService,\n                    processInstance.getId(),\n                    processInstanceState);\n            transactionContent.execute();\n        } catch (final IllegalArgumentException | SBonitaException e) {\n            throw new UpdateException(e.getMessage());\n        }\n    }\n\n    @Override\n    public Map<Long, ProcessDeploymentInfo> getProcessDeploymentInfosFromProcessInstanceIds(\n            final List<Long> processInstanceIds) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n\n        try {\n            final Map<Long, SProcessDefinitionDeployInfo> sProcessDeploymentInfos = processDefinitionService\n                    .getProcessDeploymentInfosFromProcessInstanceIds(processInstanceIds);\n            return ModelConvertor.toProcessDeploymentInfos(sProcessDeploymentInfos);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public Map<Long, ProcessDeploymentInfo> getProcessDeploymentInfosFromArchivedProcessInstanceIds(\n            final List<Long> archivedProcessInstantsIds) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n\n        try {\n            final Map<Long, SProcessDefinitionDeployInfo> sProcessDeploymentInfos = processDefinitionService\n                    .getProcessDeploymentInfosFromArchivedProcessInstanceIds(archivedProcessInstantsIds);\n            return ModelConvertor.toProcessDeploymentInfos(sProcessDeploymentInfos);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public SearchResult<Document> searchDocuments(final SearchOptions searchOptions) throws SearchException {\n\n        return documentAPI.searchDocuments(searchOptions);\n    }\n\n    @Override\n    public SearchResult<Document> searchDocumentsSupervisedBy(final long userId, final SearchOptions searchOptions)\n            throws SearchException,\n            UserNotFoundException {\n        return documentAPI.searchDocumentsSupervisedBy(userId, searchOptions);\n    }\n\n    @Override\n    public SearchResult<ArchivedDocument> searchArchivedDocuments(final SearchOptions searchOptions)\n            throws SearchException {\n\n        return documentAPI.searchArchivedDocuments(searchOptions);\n    }\n\n    @Override\n    public SearchResult<ArchivedDocument> searchArchivedDocumentsSupervisedBy(final long userId,\n            final SearchOptions searchOptions) throws SearchException,\n            UserNotFoundException {\n        return documentAPI.searchArchivedDocumentsSupervisedBy(userId, searchOptions);\n    }\n\n    @Override\n    public void retryTask(final long activityInstanceId)\n            throws ActivityExecutionException, ActivityInstanceNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        ConnectorInstanceService connectorInstanceService = serviceAccessor.getConnectorInstanceService();\n        ResetAllFailedConnectorStrategy strategy = new ResetAllFailedConnectorStrategy(connectorInstanceService,\n                new ConnectorReseter(connectorInstanceService), BATCH_SIZE);\n        FlowNodeRetrier flowNodeRetrier = new FlowNodeRetrier(serviceAccessor.getContainerRegistry(),\n                serviceAccessor.getFlowNodeExecutor(),\n                serviceAccessor.getActivityInstanceService(), serviceAccessor.getFlowNodeStateManager(), strategy);\n        flowNodeRetrier.retry(activityInstanceId);\n    }\n\n    @Override\n    public void executeMessageCouple(final long messageInstanceId, final long waitingMessageId)\n            throws ExecutionException {\n        MessagesHandlingService messagesHandlingService = getServiceAccessor().getMessagesHandlingService();\n        try {\n            messagesHandlingService.resetMessageCouple(messageInstanceId, waitingMessageId);\n            messagesHandlingService.triggerMatchingOfMessages();\n        } catch (SBonitaException e) {\n            throw new ExecutionException(\n                    \"Failed to execute Event Message couple: messageInstanceId=\" + messageInstanceId\n                            + \", waitingMessageId=\" + waitingMessageId,\n                    e);\n        }\n    }\n\n    @Override\n    public ArchivedDocument getArchivedVersionOfProcessDocument(final long sourceObjectId)\n            throws ArchivedDocumentNotFoundException {\n        return documentAPI.getArchivedVersionOfProcessDocument(sourceObjectId);\n    }\n\n    @Override\n    public ArchivedDocument getArchivedProcessDocument(final long archivedProcessDocumentId)\n            throws ArchivedDocumentNotFoundException {\n        return documentAPI.getArchivedProcessDocument(archivedProcessDocumentId);\n    }\n\n    @Override\n    public SearchResult<ArchivedComment> searchArchivedComments(final SearchOptions searchOptions)\n            throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final SCommentService sCommentService = serviceAccessor.getCommentService();\n        final SearchArchivedComments searchArchivedComments = new SearchArchivedComments(sCommentService,\n                searchEntitiesDescriptor.getSearchArchivedCommentsDescriptor(), searchOptions);\n        try {\n            searchArchivedComments.execute();\n        } catch (final SBonitaException e) {\n            throw new SearchException(e);\n        }\n        return searchArchivedComments.getResult();\n    }\n\n    @Override\n    public ArchivedComment getArchivedComment(final long archivedCommentId)\n            throws RetrieveException, NotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final SCommentService sCommentService = serviceAccessor.getCommentService();\n        try {\n            final SAComment archivedComment = sCommentService.getArchivedComment(archivedCommentId);\n            return ModelConvertor.toArchivedComment(archivedComment);\n        } catch (final SCommentNotFoundException e) {\n            throw new NotFoundException(e);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public Map<Long, ActorInstance> getActorsFromActorIds(final List<Long> actorIds) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final Map<Long, ActorInstance> res = new HashMap<>();\n        final ActorMappingService actormappingService = serviceAccessor.getActorMappingService();\n        final GetActorsByActorIds getActorsByActorIds = new GetActorsByActorIds(actormappingService, actorIds);\n        try {\n            getActorsByActorIds.execute();\n        } catch (final SBonitaException e1) {\n            throw new RetrieveException(e1);\n        }\n        final List<SActor> actors = getActorsByActorIds.getResult();\n        for (final SActor actor : actors) {\n            res.put(actor.getId(), ModelConvertor.toActorInstance(actor));\n        }\n        return res;\n    }\n\n    @Override\n    public Serializable evaluateExpressionOnProcessDefinition(final Expression expression,\n            final Map<String, Serializable> context,\n            final long processDefinitionId) throws ExpressionEvaluationException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ExpressionResolverService expressionResolverService = serviceAccessor.getExpressionResolverService();\n\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        final SExpression sExpression = ModelConvertor.constructSExpression(expression);\n        final SExpressionContext expcontext = new SExpressionContext();\n        expcontext.setProcessDefinitionId(processDefinitionId);\n        SProcessDefinition processDef;\n        try {\n            processDef = processDefinitionService.getProcessDefinition(processDefinitionId);\n            if (processDef != null) {\n                expcontext.setProcessDefinition(processDef);\n            }\n            final HashMap<String, Object> hashMap = new HashMap<>(context);\n            expcontext.setInputValues(hashMap);\n            return (Serializable) expressionResolverService.evaluate(sExpression, expcontext);\n        } catch (final SExpressionEvaluationException e) {\n            throw new ExpressionEvaluationException(e, e.getExpressionName());\n        } catch (final SInvalidExpressionException e) {\n            throw new ExpressionEvaluationException(e, e.getExpressionName());\n        } catch (final SBonitaException e) {\n            throw new ExpressionEvaluationException(e, null);\n        }\n    }\n\n    @Override\n    public void updateDueDateOfTask(final long userTaskId, final Date dueDate) throws UpdateException {\n        final ActivityInstanceService activityInstanceService = getServiceAccessor().getActivityInstanceService();\n        try {\n            final SetExpectedEndDate updateProcessInstance = new SetExpectedEndDate(activityInstanceService, userTaskId,\n                    dueDate);\n            updateProcessInstance.execute();\n        } catch (final SBonitaException e) {\n            throw new UpdateException(e);\n        }\n    }\n\n    @Override\n    public long countComments(final SearchOptions searchOptions) throws SearchException {\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 0)\n                .setFilters(searchOptions.getFilters()).searchTerm(\n                        searchOptions.getSearchTerm());\n        final SearchResult<Comment> searchResult = searchComments(searchOptionsBuilder.done());\n        return searchResult.getCount();\n    }\n\n    @Override\n    public long countAttachments(final SearchOptions searchOptions) throws SearchException {\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 0)\n                .setFilters(searchOptions.getFilters()).searchTerm(\n                        searchOptions.getSearchTerm());\n        final SearchResult<Document> searchResult = documentAPI.searchDocuments(searchOptionsBuilder.done());\n        return searchResult.getCount();\n    }\n\n    @Override\n    public void sendSignal(final String signalName) throws SendEventException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final EventsHandler eventsHandler = serviceAccessor.getEventsHandler();\n        final SThrowSignalEventTriggerDefinition signalEventTriggerDefinition = BuilderFactory\n                .get(SThrowSignalEventTriggerDefinitionBuilderFactory.class)\n                .createNewInstance(signalName).done();\n        try {\n            eventsHandler.handleThrowEvent(signalEventTriggerDefinition);\n        } catch (final SBonitaException e) {\n            throw new SendEventException(e);\n        }\n    }\n\n    @Override\n    public void sendMessage(final String messageName, final Expression targetProcess, final Expression targetFlowNode,\n            final Map<Expression, Expression> messageContent) throws SendEventException {\n        sendMessage(messageName, targetProcess, targetFlowNode, messageContent, null);\n    }\n\n    @Override\n    public void sendMessage(final String messageName, final Expression targetProcess, final Expression targetFlowNode,\n            final Map<Expression, Expression> messageContent, final Map<Expression, Expression> correlations)\n            throws SendEventException {\n        if (correlations != null && correlations.size() > 5) {\n            throw new SendEventException(\"Too many correlations: a message can not have more than 5 correlations.\");\n        }\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final EventsHandler eventsHandler = serviceAccessor.getEventsHandler();\n        final ExpressionResolverService expressionResolverService = serviceAccessor.getExpressionResolverService();\n\n        final SExpression targetProcessNameExp = ModelConvertor.constructSExpression(targetProcess);\n        SExpression targetFlowNodeNameExp = null;\n        if (targetFlowNode != null) {\n            targetFlowNodeNameExp = ModelConvertor.constructSExpression(targetFlowNode);\n        }\n        final SThrowMessageEventTriggerDefinitionBuilder builder = BuilderFactory\n                .get(SThrowMessageEventTriggerDefinitionBuilderFactory.class)\n                .createNewInstance(messageName, targetProcessNameExp, targetFlowNodeNameExp);\n        if (correlations != null && !correlations.isEmpty()) {\n            addMessageCorrelations(builder, correlations);\n        }\n        try {\n            if (messageContent != null && !messageContent.isEmpty()) {\n                addMessageContent(builder, expressionResolverService, messageContent);\n            }\n            final SThrowMessageEventTriggerDefinition messageEventTriggerDefinition = builder.done();\n            eventsHandler.handleThrowEvent(messageEventTriggerDefinition);\n        } catch (final SBonitaException e) {\n            throw new SendEventException(e);\n        }\n\n    }\n\n    @Override\n    public int deleteMessageByCreationDate(final long creationDate, SearchOptions searchOptions)\n            throws ExecutionException {\n        try {\n            return getServiceAccessor().getEventInstanceService()\n                    .deleteMessageAndDataInstanceOlderThanCreationDate(creationDate, buildQueryOptions(searchOptions,\n                            getServiceAccessor().getSearchEntitiesDescriptor().getSearchMessageInstanceDescriptor()));\n        } catch (SBonitaException e) {\n            throw new ExecutionException(e);\n        }\n    }\n\n    /**\n     * Utility method to convert a SearchOptions into a QueryOptions\n     */\n    private QueryOptions buildQueryOptions(SearchOptions searchOptions,\n            SearchEntityDescriptor searchEntityDescriptor) {\n        if (searchOptions != null) {\n            final List<SearchFilter> filters = searchOptions.getFilters();\n\n            final List<FilterOption> filterOptions = new ArrayList<>(filters.size());\n            for (final SearchFilter filter : filters) {\n                final FilterOption option = searchEntityDescriptor.getEntityFilter(filter);\n                if (option != null) {// in case of a unknown filter on state\n                    filterOptions.add(option);\n                }\n            }\n\n            return new QueryOptions(searchOptions.getStartIndex(), searchOptions.getMaxResults(),\n                    Collections.emptyList(), filterOptions, null);\n        } else {\n            return QueryOptions.ALL_RESULTS;\n        }\n    }\n\n    private void addMessageContent(\n            final SThrowMessageEventTriggerDefinitionBuilder messageEventTriggerDefinitionBuilder,\n            final ExpressionResolverService expressionResolverService, final Map<Expression, Expression> messageContent)\n            throws SBonitaException {\n        for (final Entry<Expression, Expression> entry : messageContent.entrySet()) {\n            expressionResolverService.evaluate(ModelConvertor.constructSExpression(entry.getKey()));\n            final SDataDefinitionBuilder dataDefinitionBuilder = BuilderFactory.get(SDataDefinitionBuilderFactory.class)\n                    .createNewInstance(\n                            entry.getKey().getContent(), entry.getValue().getReturnType());\n            dataDefinitionBuilder.setDefaultValue(ModelConvertor.constructSExpression(entry.getValue()));\n            messageEventTriggerDefinitionBuilder.addData(dataDefinitionBuilder.done());\n        }\n\n    }\n\n    private void addMessageCorrelations(\n            final SThrowMessageEventTriggerDefinitionBuilder messageEventTriggerDefinitionBuilder,\n            final Map<Expression, Expression> messageCorrelations) {\n        for (final Entry<Expression, Expression> entry : messageCorrelations.entrySet()) {\n            messageEventTriggerDefinitionBuilder.addCorrelation(ModelConvertor.constructSExpression(entry.getKey()),\n                    ModelConvertor.constructSExpression(entry.getValue()));\n        }\n    }\n\n    @Override\n    public List<Problem> getProcessResolutionProblems(final long processDefinitionId)\n            throws ProcessDefinitionNotFoundException {\n        return processDeploymentAPIDelegate.getProcessResolutionProblems(processDefinitionId);\n    }\n\n    @Override\n    public List<ProcessDeploymentInfo> getProcessDeploymentInfos(final int startIndex, final int maxResults,\n            final ProcessDeploymentInfoCriterion pagingCriterion) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n\n        final SearchProcessDefinitionsDescriptor processDefinitionsDescriptor = serviceAccessor\n                .getSearchEntitiesDescriptor()\n                .getSearchProcessDefinitionsDescriptor();\n        final GetProcessDefinitionDeployInfos transactionContentWithResult = new GetProcessDefinitionDeployInfos(\n                processDefinitionService,\n                processDefinitionsDescriptor, startIndex, maxResults, pagingCriterion);\n        try {\n            transactionContentWithResult.execute();\n            return transactionContentWithResult.getResult();\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public List<ProcessDeploymentInfo> getProcessDeploymentInfosWithActorOnlyForGroup(final long groupId,\n            final int startIndex, final int maxResults,\n            final ProcessDeploymentInfoCriterion sortingCriterion) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n\n        final SearchProcessDefinitionsDescriptor processDefinitionsDescriptor = serviceAccessor\n                .getSearchEntitiesDescriptor()\n                .getSearchProcessDefinitionsDescriptor();\n        final GetProcessDefinitionDeployInfosWithActorOnlyForGroup transactionContentWithResult = new GetProcessDefinitionDeployInfosWithActorOnlyForGroup(\n                processDefinitionService, processDefinitionsDescriptor, startIndex, maxResults, sortingCriterion,\n                groupId);\n        try {\n            transactionContentWithResult.execute();\n            return transactionContentWithResult.getResult();\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public List<ProcessDeploymentInfo> getProcessDeploymentInfosWithActorOnlyForGroups(final List<Long> groupIds,\n            final int startIndex, final int maxResults,\n            final ProcessDeploymentInfoCriterion sortingCriterion) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n\n        final SearchProcessDefinitionsDescriptor processDefinitionsDescriptor = serviceAccessor\n                .getSearchEntitiesDescriptor()\n                .getSearchProcessDefinitionsDescriptor();\n        final GetProcessDefinitionDeployInfosWithActorOnlyForGroups transactionContentWithResult = new GetProcessDefinitionDeployInfosWithActorOnlyForGroups(\n                processDefinitionService, processDefinitionsDescriptor, startIndex, maxResults, sortingCriterion,\n                groupIds);\n        try {\n            transactionContentWithResult.execute();\n            return transactionContentWithResult.getResult();\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public List<ProcessDeploymentInfo> getProcessDeploymentInfosWithActorOnlyForRole(final long roleId,\n            final int startIndex, final int maxResults,\n            final ProcessDeploymentInfoCriterion sortingCriterion) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n\n        final SearchProcessDefinitionsDescriptor processDefinitionsDescriptor = serviceAccessor\n                .getSearchEntitiesDescriptor()\n                .getSearchProcessDefinitionsDescriptor();\n        final GetProcessDefinitionDeployInfosWithActorOnlyForRole transactionContentWithResult = new GetProcessDefinitionDeployInfosWithActorOnlyForRole(\n                processDefinitionService, processDefinitionsDescriptor, startIndex, maxResults, sortingCriterion,\n                roleId);\n        try {\n\n            transactionContentWithResult.execute();\n            return transactionContentWithResult.getResult();\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public List<ProcessDeploymentInfo> getProcessDeploymentInfosWithActorOnlyForRoles(final List<Long> roleIds,\n            final int startIndex, final int maxResults,\n            final ProcessDeploymentInfoCriterion sortingCriterion) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n\n        final SearchProcessDefinitionsDescriptor processDefinitionsDescriptor = serviceAccessor\n                .getSearchEntitiesDescriptor()\n                .getSearchProcessDefinitionsDescriptor();\n        final GetProcessDefinitionDeployInfosWithActorOnlyForRoles transactionContentWithResult = new GetProcessDefinitionDeployInfosWithActorOnlyForRoles(\n                processDefinitionService, processDefinitionsDescriptor, startIndex, maxResults, sortingCriterion,\n                roleIds);\n        try {\n            transactionContentWithResult.execute();\n            return transactionContentWithResult.getResult();\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public List<ProcessDeploymentInfo> getProcessDeploymentInfosWithActorOnlyForUser(final long userId,\n            final int startIndex, final int maxResults,\n            final ProcessDeploymentInfoCriterion sortingCriterion) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        final SearchProcessDefinitionsDescriptor processDefinitionsDescriptor = serviceAccessor\n                .getSearchEntitiesDescriptor()\n                .getSearchProcessDefinitionsDescriptor();\n        final GetProcessDefinitionDeployInfosWithActorOnlyForUser transactionContentWithResult = new GetProcessDefinitionDeployInfosWithActorOnlyForUser(\n                processDefinitionService, processDefinitionsDescriptor, startIndex, maxResults, sortingCriterion,\n                userId);\n        try {\n            transactionContentWithResult.execute();\n            return transactionContentWithResult.getResult();\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public List<ProcessDeploymentInfo> getProcessDeploymentInfosWithActorOnlyForUsers(final List<Long> userIds,\n            final int startIndex, final int maxResults,\n            final ProcessDeploymentInfoCriterion sortingCriterion) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        final SearchProcessDefinitionsDescriptor processDefinitionsDescriptor = serviceAccessor\n                .getSearchEntitiesDescriptor()\n                .getSearchProcessDefinitionsDescriptor();\n        final GetProcessDefinitionDeployInfosWithActorOnlyForUsers transactionContentWithResult = new GetProcessDefinitionDeployInfosWithActorOnlyForUsers(\n                processDefinitionService, processDefinitionsDescriptor, startIndex, maxResults, sortingCriterion,\n                userIds);\n        try {\n            transactionContentWithResult.execute();\n            return transactionContentWithResult.getResult();\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public SearchResult<ConnectorInstance> searchConnectorInstances(final SearchOptions searchOptions)\n            throws RetrieveException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final ConnectorInstanceService connectorInstanceService = serviceAccessor.getConnectorInstanceService();\n        if (searchOptions.getSorts().isEmpty()) {\n            searchOptions.getSorts().add(new Sort(Order.ASC, ConnectorInstancesSearchDescriptor.EXECUTION_ORDER));\n        }\n        try {\n            return search(searchEntitiesDescriptor.getSearchConnectorInstanceDescriptor(),\n                    searchOptions,\n                    ModelConvertor::toConnectorInstances,\n                    connectorInstanceService::getNumberOfConnectorInstances,\n                    connectorInstanceService::searchConnectorInstances);\n        } catch (SearchException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public SearchResult<ArchivedConnectorInstance> searchArchivedConnectorInstances(final SearchOptions searchOptions)\n            throws RetrieveException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final ConnectorInstanceService connectorInstanceService = serviceAccessor.getConnectorInstanceService();\n        final ArchiveService archiveService = serviceAccessor.getArchiveService();\n        final ReadPersistenceService persistenceService = archiveService.getDefinitiveArchiveReadPersistenceService();\n        final SearchArchivedConnectorInstance searchArchivedConnectorInstance = new SearchArchivedConnectorInstance(\n                connectorInstanceService,\n                searchEntitiesDescriptor.getSearchArchivedConnectorInstanceDescriptor(), searchOptions,\n                persistenceService);\n        try {\n            searchArchivedConnectorInstance.execute();\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n        return searchArchivedConnectorInstance.getResult();\n    }\n\n    @Override\n    public List<HumanTaskInstance> getHumanTaskInstances(final long rootProcessInstanceId, final String taskName,\n            final int startIndex, final int maxResults) {\n        try {\n            final List<HumanTaskInstance> humanTaskInstances = getHumanTaskInstances(rootProcessInstanceId, taskName,\n                    startIndex, maxResults,\n                    HumanTaskInstanceSearchDescriptor.PROCESS_INSTANCE_ID, Order.ASC);\n            return Objects.requireNonNullElse(humanTaskInstances, Collections.emptyList());\n        } catch (final SearchException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public HumanTaskInstance getLastStateHumanTaskInstance(final long rootProcessInstanceId, final String taskName)\n            throws NotFoundException {\n        try {\n            final List<HumanTaskInstance> humanTaskInstances = getHumanTaskInstances(rootProcessInstanceId, taskName, 0,\n                    1,\n                    HumanTaskInstanceSearchDescriptor.REACHED_STATE_DATE, Order.DESC);\n            if (humanTaskInstances == null || humanTaskInstances.isEmpty()) {\n                throw new NotFoundException(\"Task '\" + taskName + \"' not found\");\n            }\n            return humanTaskInstances.get(0);\n        } catch (final SearchException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    private List<HumanTaskInstance> getHumanTaskInstances(final long processInstanceId, final String taskName,\n            final int startIndex, final int maxResults,\n            final String field, final Order order) throws SearchException {\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(startIndex, maxResults);\n        builder.filter(HumanTaskInstanceSearchDescriptor.PROCESS_INSTANCE_ID, processInstanceId)\n                .filter(HumanTaskInstanceSearchDescriptor.NAME, taskName);\n        builder.sort(field, order);\n        final SearchResult<HumanTaskInstance> searchHumanTasks = searchHumanTaskInstances(builder.done());\n        return searchHumanTasks.getResult();\n    }\n\n    @Override\n    public SearchResult<User> searchUsersWhoCanStartProcessDefinition(final long processDefinitionId,\n            final SearchOptions searchOptions)\n            throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final SearchUserDescriptor searchDescriptor = searchEntitiesDescriptor.getSearchUserDescriptor();\n        final SearchUsersWhoCanStartProcessDeploymentInfo transactionSearch = new SearchUsersWhoCanStartProcessDeploymentInfo(\n                processDefinitionService,\n                searchDescriptor, processDefinitionId, searchOptions);\n        try {\n            transactionSearch.execute();\n        } catch (final SBonitaException e) {\n            throw new SearchException(e);\n        }\n        return transactionSearch.getResult();\n    }\n\n    @Override\n    public Map<String, Serializable> evaluateExpressionsAtProcessInstanciation(final long processInstanceId,\n            final Map<Expression, Map<String, Serializable>> expressions) throws ExpressionEvaluationException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n        try {\n            try {\n                final SProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);\n                // if it exists and is initializing or started\n                final int stateId = processInstance.getStateId();\n                if (stateId == 0/* initializing */ || stateId == 1/* started */) {\n                    // the evaluation date is either now (initializing) or the start date if available\n                    final long evaluationDate = stateId == 0 ? System.currentTimeMillis()\n                            : processInstance.getStartDate();\n                    return evaluateExpressionsInstanceLevelAndArchived(expressions, processInstanceId,\n                            CONTAINER_TYPE_PROCESS_INSTANCE,\n                            processInstance.getProcessDefinitionId(), evaluationDate);\n                }\n            } catch (final SProcessInstanceNotFoundException spinfe) {\n                // get it in the archive\n            }\n            final ArchivedProcessInstance archiveProcessInstance = getStartedArchivedProcessInstance(processInstanceId);\n            return evaluateExpressionsInstanceLevelAndArchived(expressions, processInstanceId,\n                    CONTAINER_TYPE_PROCESS_INSTANCE, archiveProcessInstance.getProcessDefinitionId(),\n                    archiveProcessInstance.getStartDate().getTime());\n        } catch (final SExpressionEvaluationException e) {\n            throw new ExpressionEvaluationException(e, e.getExpressionName());\n        } catch (final SInvalidExpressionException e) {\n            throw new ExpressionEvaluationException(e, e.getExpressionName());\n        } catch (final SBonitaException e) {\n            throw new ExpressionEvaluationException(e, null);\n        }\n    }\n\n    @Override\n    public Map<String, Serializable> evaluateExpressionOnCompletedProcessInstance(final long processInstanceId,\n            final Map<Expression, Map<String, Serializable>> expressions) throws ExpressionEvaluationException {\n        try {\n            final ArchivedProcessInstance lastArchivedProcessInstance = getLastArchivedProcessInstance(\n                    processInstanceId);\n            return evaluateExpressionsInstanceLevelAndArchived(expressions, processInstanceId,\n                    CONTAINER_TYPE_PROCESS_INSTANCE,\n                    lastArchivedProcessInstance.getProcessDefinitionId(),\n                    lastArchivedProcessInstance.getArchiveDate().getTime());\n        } catch (final SExpressionEvaluationException e) {\n            throw new ExpressionEvaluationException(e, e.getExpressionName());\n        } catch (final SInvalidExpressionException e) {\n            throw new ExpressionEvaluationException(e, e.getExpressionName());\n        } catch (final SBonitaException e) {\n            throw new ExpressionEvaluationException(e, null);\n        }\n    }\n\n    @Override\n    public Map<String, Serializable> evaluateExpressionsOnProcessInstance(final long processInstanceId,\n            final Map<Expression, Map<String, Serializable>> expressions) throws ExpressionEvaluationException {\n        try {\n            final SProcessInstance sProcessInstance = getSProcessInstance(processInstanceId);\n            return evaluateExpressionsInstanceLevel(expressions, processInstanceId, CONTAINER_TYPE_PROCESS_INSTANCE,\n                    sProcessInstance.getProcessDefinitionId());\n        } catch (final SExpressionEvaluationException e) {\n            throw new ExpressionEvaluationException(e, e.getExpressionName());\n        } catch (final SInvalidExpressionException e) {\n            throw new ExpressionEvaluationException(e, e.getExpressionName());\n        } catch (final SBonitaException e) {\n            throw new ExpressionEvaluationException(e, null);\n        }\n    }\n\n    @Override\n    public Map<String, Serializable> evaluateExpressionsOnProcessDefinition(final long processDefinitionId,\n            final Map<Expression, Map<String, Serializable>> expressions) throws ExpressionEvaluationException {\n        try {\n            return evaluateExpressionsDefinitionLevel(expressions, processDefinitionId);\n        } catch (final SExpressionEvaluationException e) {\n            throw new ExpressionEvaluationException(e, e.getExpressionName());\n        } catch (final SInvalidExpressionException e) {\n            throw new ExpressionEvaluationException(e, e.getExpressionName());\n        } catch (final SBonitaException e) {\n            throw new ExpressionEvaluationException(e, null);\n        }\n    }\n\n    @Override\n    public Map<String, Serializable> evaluateExpressionsOnActivityInstance(final long activityInstanceId,\n            final Map<Expression, Map<String, Serializable>> expressions) throws ExpressionEvaluationException {\n        try {\n            final SActivityInstance sActivityInstance = getSActivityInstance(activityInstanceId);\n            final SProcessInstance sProcessInstance = getSProcessInstance(\n                    sActivityInstance.getParentProcessInstanceId());\n            return evaluateExpressionsInstanceLevel(expressions, activityInstanceId, CONTAINER_TYPE_ACTIVITY_INSTANCE,\n                    sProcessInstance.getProcessDefinitionId());\n        } catch (final SExpressionEvaluationException e) {\n            throw new ExpressionEvaluationException(e, e.getExpressionName());\n        } catch (final SInvalidExpressionException e) {\n            throw new ExpressionEvaluationException(e, e.getExpressionName());\n        } catch (final SBonitaException e) {\n            throw new ExpressionEvaluationException(e, null);\n        }\n    }\n\n    @Override\n    public Map<String, Serializable> evaluateExpressionsOnCompletedActivityInstance(final long activityInstanceId,\n            final Map<Expression, Map<String, Serializable>> expressions) throws ExpressionEvaluationException {\n        try {\n            final ArchivedActivityInstance activityInstance = getArchivedActivityInstance(activityInstanceId);\n            // same archive time to process even if there're many activities in the process\n            final ArchivedProcessInstance lastArchivedProcessInstance = getLastArchivedProcessInstance(\n                    activityInstance.getProcessInstanceId());\n\n            return evaluateExpressionsInstanceLevelAndArchived(expressions, activityInstanceId,\n                    CONTAINER_TYPE_ACTIVITY_INSTANCE,\n                    lastArchivedProcessInstance.getProcessDefinitionId(), activityInstance.getArchiveDate().getTime());\n        } catch (final SExpressionEvaluationException e) {\n            throw new ExpressionEvaluationException(e, e.getExpressionName());\n        } catch (final SInvalidExpressionException e) {\n            throw new ExpressionEvaluationException(e, e.getExpressionName());\n        } catch (final ActivityInstanceNotFoundException | SBonitaException e) {\n            throw new ExpressionEvaluationException(e, null);\n        }\n    }\n\n    private Map<String, Serializable> evaluateExpressionsDefinitionLevel(\n            final Map<Expression, Map<String, Serializable>> expressionsAndTheirPartialContext,\n            final long processDefinitionId) throws SBonitaException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ExpressionResolverService expressionResolverService = serviceAccessor.getExpressionResolverService();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        final EvaluateExpressionsDefinitionLevel evaluations = createDefinitionLevelExpressionEvaluator(\n                expressionsAndTheirPartialContext, processDefinitionId,\n                expressionResolverService, processDefinitionService);\n        evaluations.execute();\n        return evaluations.getResult();\n    }\n\n    protected EvaluateExpressionsDefinitionLevel createDefinitionLevelExpressionEvaluator(\n            final Map<Expression, Map<String, Serializable>> expressionsAndTheirPartialContext,\n            final long processDefinitionId,\n            final ExpressionResolverService expressionResolverService,\n            final ProcessDefinitionService processDefinitionService) {\n        return new EvaluateExpressionsDefinitionLevel(expressionsAndTheirPartialContext, processDefinitionId,\n                expressionResolverService, processDefinitionService, getServiceAccessor().getBusinessDataRepository());\n    }\n\n    private Map<String, Serializable> evaluateExpressionsInstanceLevel(\n            final Map<Expression, Map<String, Serializable>> expressions, final long containerId,\n            final String containerType, final long processDefinitionId) throws SBonitaException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ExpressionResolverService expressionService = serviceAccessor.getExpressionResolverService();\n\n        final EvaluateExpressionsInstanceLevel evaluations = createInstanceLevelExpressionEvaluator(expressions,\n                containerId, containerType,\n                processDefinitionId, expressionService);\n        evaluations.execute();\n        return evaluations.getResult();\n    }\n\n    protected EvaluateExpressionsInstanceLevel createInstanceLevelExpressionEvaluator(\n            final Map<Expression, Map<String, Serializable>> expressions,\n            final long containerId, final String containerType, final long processDefinitionId,\n            final ExpressionResolverService expressionService) {\n        return new EvaluateExpressionsInstanceLevel(expressions, containerId, containerType, processDefinitionId,\n                expressionService, getServiceAccessor().getBusinessDataRepository());\n    }\n\n    private Map<String, Serializable> evaluateExpressionsInstanceLevelAndArchived(\n            final Map<Expression, Map<String, Serializable>> expressions,\n            final long containerId, final String containerType, final long processDefinitionId, final long time)\n            throws SBonitaException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ExpressionResolverService expressionService = serviceAccessor.getExpressionResolverService();\n        final EvaluateExpressionsInstanceLevelAndArchived evaluations = createInstanceAndArchivedLevelExpressionEvaluator(\n                expressions, containerId,\n                containerType, processDefinitionId, time, expressionService);\n        evaluations.execute();\n        return evaluations.getResult();\n    }\n\n    protected EvaluateExpressionsInstanceLevelAndArchived createInstanceAndArchivedLevelExpressionEvaluator(\n            final Map<Expression, Map<String, Serializable>> expressions, final long containerId,\n            final String containerType, final long processDefinitionId,\n            final long time, final ExpressionResolverService expressionService) {\n        return new EvaluateExpressionsInstanceLevelAndArchived(expressions, containerId, containerType,\n                processDefinitionId, time, expressionService,\n                getServiceAccessor().getBusinessDataRepository());\n    }\n\n    private ArchivedProcessInstance getStartedArchivedProcessInstance(final long processInstanceId)\n            throws SBonitaException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 2);\n        searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.ARCHIVE_DATE, Order.ASC);\n        searchOptionsBuilder.filter(ArchivedProcessInstancesSearchDescriptor.SOURCE_OBJECT_ID, processInstanceId);\n        searchOptionsBuilder.filter(ArchivedProcessInstancesSearchDescriptor.STATE_ID,\n                ProcessInstanceState.STARTED.getId());\n        final SearchArchivedProcessInstances searchArchivedProcessInstances = new SearchArchivedProcessInstances(\n                processInstanceService,\n                serviceAccessor.getProcessDefinitionService(),\n                searchEntitiesDescriptor.getSearchArchivedProcessInstanceDescriptor(),\n                searchOptionsBuilder.done());\n        searchArchivedProcessInstances.execute();\n\n        try {\n            return searchArchivedProcessInstances.getResult().getResult().get(0);\n        } catch (final IndexOutOfBoundsException e) {\n            throw new SAProcessInstanceNotFoundException(processInstanceId, ProcessInstanceState.STARTED.name());\n        }\n\n    }\n\n    protected ArchivedProcessInstance getLastArchivedProcessInstance(final long processInstanceId)\n            throws SBonitaException {\n        return processInvolvementDelegate.getLastArchivedProcessInstance(processInstanceId);\n    }\n\n    @Override\n    public List<FailedJob> getFailedJobs(final int startIndex, final int maxResults) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final JobService jobService = serviceAccessor.getJobService();\n        try {\n            final List<SFailedJob> failedJobs = jobService.getFailedJobs(startIndex, maxResults);\n            return ModelConvertor.toFailedJobs(failedJobs);\n        } catch (final SSchedulerException sse) {\n            throw new RetrieveException(sse);\n        }\n    }\n\n    @Override\n    public void replayFailedJob(final long jobDescriptorId) throws ExecutionException {\n        replayFailedJob(jobDescriptorId, null);\n    }\n\n    @Override\n    public void replayFailedJob(final long jobDescriptorId, final Map<String, Serializable> parameters)\n            throws ExecutionException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final SchedulerService schedulerService = serviceAccessor.getSchedulerService();\n        try {\n            if (parameters == null || parameters.isEmpty()) {\n                schedulerService.retryJobThatFailed(jobDescriptorId);\n            } else {\n                final List<SJobParameter> jobParameters = buildJobParametersFromMap(parameters);\n                schedulerService.retryJobThatFailed(jobDescriptorId, jobParameters);\n            }\n        } catch (final SSchedulerException sse) {\n            throw new ExecutionException(sse);\n        }\n    }\n\n    protected List<SJobParameter> buildJobParametersFromMap(final Map<String, Serializable> parameters) {\n        final List<SJobParameter> jobParameters = new ArrayList<>();\n        for (final Entry<String, Serializable> parameter : parameters.entrySet()) {\n            jobParameters.add(buildSJobParameter(parameter.getKey(), parameter.getValue()));\n        }\n        return jobParameters;\n    }\n\n    protected SJobParameter buildSJobParameter(final String parameterKey, final Serializable parameterValue) {\n        return SJobParameter.builder()\n                .key(parameterKey)\n                .value(parameterValue).build();\n    }\n\n    @Override\n    public ArchivedDataInstance getArchivedProcessDataInstance(final String dataName,\n            final long sourceProcessInstanceId) throws ArchivedDataNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ParentContainerResolver parentContainerResolver = serviceAccessor.getParentContainerResolver();\n        final DataInstanceService dataInstanceService = serviceAccessor.getDataInstanceService();\n        final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService();\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        try {\n            final SAProcessInstance lastArchivedProcessInstance = processInstanceService\n                    .getLastArchivedProcessInstance(sourceProcessInstanceId);\n            if (lastArchivedProcessInstance == null) {\n                throw new ArchivedDataNotFoundException(\n                        \"Archived process instance not found: \" + sourceProcessInstanceId);\n            }\n            final long processDefinitionId = lastArchivedProcessInstance.getProcessDefinitionId();\n            final ClassLoader processClassLoader = classLoaderService.getClassLoader(\n                    identifier(ScopeType.PROCESS, processDefinitionId));\n            Thread.currentThread().setContextClassLoader(processClassLoader);\n            final SADataInstance dataInstance = dataInstanceService.getLastSADataInstance(dataName,\n                    sourceProcessInstanceId,\n                    DataInstanceContainer.PROCESS_INSTANCE.toString(), parentContainerResolver);\n            return ModelConvertor.toArchivedDataInstance(dataInstance);\n        } catch (final SDataInstanceException e) {\n            throw new ArchivedDataNotFoundException(e);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        } finally {\n            Thread.currentThread().setContextClassLoader(contextClassLoader);\n        }\n    }\n\n    @Override\n    public ArchivedDataInstance getArchivedActivityDataInstance(final String dataName,\n            final long sourceActivityInstanceId)\n            throws ArchivedDataNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final DataInstanceService dataInstanceService = serviceAccessor.getDataInstanceService();\n        final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService();\n        final ParentContainerResolver parentContainerResolver = serviceAccessor.getParentContainerResolver();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final int processDefinitionIndex = BuilderFactory.get(SAutomaticTaskInstanceBuilderFactory.class)\n                .getProcessDefinitionIndex();\n        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        try {\n            final SAFlowNodeInstance lastArchivedFlowNodeInstance = activityInstanceService\n                    .getLastArchivedFlowNodeInstance(SAFlowNodeInstance.class, sourceActivityInstanceId);\n            if (lastArchivedFlowNodeInstance == null) {\n                throw new ArchivedDataNotFoundException(\n                        new ArchivedActivityInstanceNotFoundException(sourceActivityInstanceId));\n            }\n            final long parentProcessInstanceId = lastArchivedFlowNodeInstance\n                    .getLogicalGroup(processDefinitionIndex);\n            final ClassLoader processClassLoader = classLoaderService.getClassLoader(\n                    identifier(ScopeType.PROCESS, parentProcessInstanceId));\n            Thread.currentThread().setContextClassLoader(processClassLoader);\n            final SADataInstance dataInstance = dataInstanceService.getLastSADataInstance(dataName,\n                    sourceActivityInstanceId,\n                    DataInstanceContainer.ACTIVITY_INSTANCE.toString(), parentContainerResolver);\n            return ModelConvertor.toArchivedDataInstance(dataInstance);\n        } catch (final SDataInstanceException e) {\n            throw new ArchivedDataNotFoundException(e);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        } finally {\n            Thread.currentThread().setContextClassLoader(contextClassLoader);\n        }\n    }\n\n    @Override\n    public List<ArchivedDataInstance> getArchivedProcessDataInstances(final long sourceProcessInstanceId,\n            final int startIndex, final int maxResults) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final DataInstanceService dataInstanceService = serviceAccessor.getDataInstanceService();\n        final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService();\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        try {\n            final SAProcessInstance lastArchivedProcessInstance = processInstanceService\n                    .getLastArchivedProcessInstance(sourceProcessInstanceId);\n            if (lastArchivedProcessInstance == null) {\n                throw new RetrieveException(\"Archived process instance not found: \" + sourceProcessInstanceId);\n            }\n            final long processDefinitionId = lastArchivedProcessInstance.getProcessDefinitionId();\n            final ClassLoader processClassLoader = classLoaderService.getClassLoader(\n                    identifier(ScopeType.PROCESS, processDefinitionId));\n            Thread.currentThread().setContextClassLoader(processClassLoader);\n            final List<SADataInstance> dataInstances = dataInstanceService.getLastLocalSADataInstances(\n                    sourceProcessInstanceId,\n                    DataInstanceContainer.PROCESS_INSTANCE.toString(), startIndex, maxResults);\n            return ModelConvertor.toArchivedDataInstances(dataInstances);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        } finally {\n            Thread.currentThread().setContextClassLoader(contextClassLoader);\n        }\n    }\n\n    @Override\n    public List<ArchivedDataInstance> getArchivedActivityDataInstances(final long sourceActivityInstanceId,\n            final int startIndex, final int maxResults) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final DataInstanceService dataInstanceService = serviceAccessor.getDataInstanceService();\n        final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final int processDefinitionIndex = BuilderFactory.get(SAutomaticTaskInstanceBuilderFactory.class)\n                .getProcessDefinitionIndex();\n        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        try {\n            final long parentProcessInstanceId = activityInstanceService\n                    .getLastArchivedFlowNodeInstance(SAFlowNodeInstance.class, sourceActivityInstanceId)\n                    .getLogicalGroup(processDefinitionIndex);\n            final ClassLoader processClassLoader = classLoaderService.getClassLoader(\n                    identifier(ScopeType.PROCESS, parentProcessInstanceId));\n            Thread.currentThread().setContextClassLoader(processClassLoader);\n\n            final List<SADataInstance> dataInstances = dataInstanceService.getLastLocalSADataInstances(\n                    sourceActivityInstanceId,\n                    DataInstanceContainer.ACTIVITY_INSTANCE.toString(), startIndex, maxResults);\n            return ModelConvertor.toArchivedDataInstances(dataInstances);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        } finally {\n            Thread.currentThread().setContextClassLoader(contextClassLoader);\n        }\n    }\n\n    @Override\n    public List<User> getPossibleUsersOfPendingHumanTask(final long humanTaskInstanceId, final int startIndex,\n            final int maxResults) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        try {\n            // pagination of this method is based on order by username:\n            final List<Long> userIds = activityInstanceService.getPossibleUserIdsOfPendingTasks(humanTaskInstanceId,\n                    startIndex, maxResults);\n            final IdentityService identityService = getServiceAccessor().getIdentityService();\n            // This method below is also ordered by username, so the order is preserved:\n            final List<SUser> sUsers = identityService.getUsers(userIds);\n            return ModelConvertor.toUsers(sUsers);\n        } catch (final SBonitaException sbe) {\n            throw new RetrieveException(sbe);\n        }\n    }\n\n    @Override\n    public List<User> getPossibleUsersOfHumanTask(final long processDefinitionId, final String humanTaskName,\n            final int startIndex, final int maxResults) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        try {\n            final SProcessDefinition processDefinition = processDefinitionService\n                    .getProcessDefinition(processDefinitionId);\n            final SFlowNodeDefinition flowNode = processDefinition.getProcessContainer().getFlowNode(humanTaskName);\n            if (!(flowNode instanceof SHumanTaskDefinition humanTask)) {\n                return Collections.emptyList();\n            }\n            final String actorName = humanTask.getActorName();\n            final List<Long> userIds = getUserIdsForActor(serviceAccessor, processDefinitionId, actorName, startIndex,\n                    maxResults);\n            final List<SUser> users = serviceAccessor.getIdentityService().getUsers(userIds);\n            return ModelConvertor.toUsers(users);\n        } catch (final SProcessDefinitionNotFoundException spdnfe) {\n            return Collections.emptyList();\n        } catch (final SBonitaException sbe) {\n            throw new RetrieveException(sbe);\n        }\n    }\n\n    private List<Long> getUserIdsForActor(final ServiceAccessor serviceAccessor, final long processDefinitionId,\n            final String actorName,\n            final int startIndex, final int maxResults) throws SActorNotFoundException, SBonitaReadException {\n        final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService();\n        final SActor actor = actorMappingService.getActor(actorName, processDefinitionId);\n        return actorMappingService.getPossibleUserIdsOfActorId(actor.getId(), startIndex, maxResults);\n    }\n\n    @Override\n    public List<Long> getUserIdsForActor(final long processDefinitionId, final String actorName, final int startIndex,\n            final int maxResults) {\n        try {\n            return getUserIdsForActor(getServiceAccessor(), processDefinitionId, actorName, startIndex, maxResults);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    @Override\n    public SearchResult<User> searchUsersWhoCanExecutePendingHumanTask(final long humanTaskInstanceId,\n            final SearchOptions searchOptions) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final SearchUserDescriptor searchDescriptor = searchEntitiesDescriptor.getSearchUserDescriptor();\n        final SearchUsersWhoCanExecutePendingHumanTaskDeploymentInfo searcher = new SearchUsersWhoCanExecutePendingHumanTaskDeploymentInfo(\n                humanTaskInstanceId, activityInstanceService, searchDescriptor, searchOptions);\n        try {\n            searcher.execute();\n        } catch (final SBonitaException sbe) {\n            throw new RetrieveException(sbe);\n        }\n        return searcher.getResult();\n    }\n\n    @Override\n    public SearchResult<HumanTaskInstance> searchAssignedAndPendingHumanTasksFor(final long rootProcessDefinitionId,\n            final long userId,\n            final SearchOptions searchOptions) throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager();\n        final SearchHumanTaskInstanceDescriptor searchDescriptor = searchEntitiesDescriptor\n                .getSearchHumanTaskInstanceDescriptor();\n        return AbstractHumanTaskInstanceSearchEntity.searchHumanTaskInstance(searchDescriptor,\n                searchOptions,\n                flowNodeStateManager,\n                (queryOptions) -> activityInstanceService\n                        .getNumberOfAssignedAndPendingHumanTasksFor(rootProcessDefinitionId, userId, queryOptions),\n                (queryOptions) -> activityInstanceService.searchAssignedAndPendingHumanTasksFor(rootProcessDefinitionId,\n                        userId, queryOptions))\n                .search();\n    }\n\n    @Override\n    public SearchResult<HumanTaskInstance> searchAssignedAndPendingHumanTasks(final long rootProcessDefinitionId,\n            final SearchOptions searchOptions)\n            throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager();\n        final SearchHumanTaskInstanceDescriptor searchDescriptor = searchEntitiesDescriptor\n                .getSearchHumanTaskInstanceDescriptor();\n        return AbstractHumanTaskInstanceSearchEntity.searchHumanTaskInstance(searchDescriptor,\n                searchOptions,\n                flowNodeStateManager,\n                (queryOptions) -> activityInstanceService\n                        .getNumberOfAssignedAndPendingHumanTasks(rootProcessDefinitionId, queryOptions),\n                (queryOptions) -> activityInstanceService.searchAssignedAndPendingHumanTasks(rootProcessDefinitionId,\n                        queryOptions))\n                .search();\n    }\n\n    @Override\n    public SearchResult<HumanTaskInstance> searchAssignedAndPendingHumanTasks(final SearchOptions searchOptions)\n            throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager();\n        final SearchHumanTaskInstanceDescriptor searchDescriptor = searchEntitiesDescriptor\n                .getSearchHumanTaskInstanceDescriptor();\n        return AbstractHumanTaskInstanceSearchEntity.searchHumanTaskInstance(searchDescriptor,\n                searchOptions,\n                flowNodeStateManager,\n                activityInstanceService::getNumberOfAssignedAndPendingHumanTasks,\n                activityInstanceService::searchAssignedAndPendingHumanTasks).search();\n    }\n\n    @Override\n    public ContractDefinition getUserTaskContract(final long userTaskId) throws UserTaskNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        try {\n            final SHumanTaskInstance taskInstance = activityInstanceService.getHumanTaskInstance(userTaskId);\n            if (!(taskInstance instanceof SUserTaskInstance)) {\n                throw new UserTaskNotFoundException(\"Impossible to find a user task with id: \" + userTaskId);\n            }\n            final SProcessDefinition processDefinition = getServiceAccessor().getProcessDefinitionService()\n                    .getProcessDefinition(\n                            taskInstance.getProcessDefinitionId());\n            final SUserTaskDefinition userTask = (SUserTaskDefinition) processDefinition.getProcessContainer()\n                    .getFlowNode(\n                            taskInstance.getFlowNodeDefinitionId());\n            return ModelConvertor.toContract(userTask.getContract());\n        } catch (final SActivityInstanceNotFoundException | SProcessDefinitionNotFoundException | SActivityReadException\n                | SBonitaReadException e) {\n            throw new UserTaskNotFoundException(e.getMessage());\n        }\n    }\n\n    @Override\n    public ContractDefinition getProcessContract(final long processDefinitionId)\n            throws ProcessDefinitionNotFoundException {\n        try {\n            final SProcessDefinition processDefinition = getServiceAccessor().getProcessDefinitionService()\n                    .getProcessDefinition(processDefinitionId);\n            return ModelConvertor.toContract(processDefinition.getContract());\n        } catch (final SProcessDefinitionNotFoundException | SBonitaReadException e) {\n            throw new ProcessDefinitionNotFoundException(e.getMessage());\n        }\n    }\n\n    @Override\n    @CustomTransactions\n    public void executeUserTask(final long flownodeInstanceId, final Map<String, Serializable> inputs)\n            throws FlowNodeExecutionException,\n            ContractViolationException,\n            UserTaskNotFoundException {\n        executeUserTask(0, flownodeInstanceId, inputs);\n    }\n\n    @Override\n    @CustomTransactions\n    public void executeUserTask(final long userId, final long flownodeInstanceId,\n            final Map<String, Serializable> inputs) throws FlowNodeExecutionException,\n            ContractViolationException, UserTaskNotFoundException {\n        try {\n            inTx(() -> {\n                executeFlowNode(userId, flownodeInstanceId, inputs, true);\n                return null;\n            });\n        } catch (final ContractViolationException e) {\n            throw e;\n        } catch (final SFlowNodeNotFoundException e) {\n            throw new UserTaskNotFoundException(\n                    String.format(\"User task %s is not found, it might already be executed\", flownodeInstanceId));\n        } catch (final Exception e) {\n            verifyIfTheActivityWasInTheCorrectStateAndThrowException(flownodeInstanceId, e);\n        }\n    }\n\n    private void verifyIfTheActivityWasInTheCorrectStateAndThrowException(long flownodeInstanceId, Exception e)\n            throws UserTaskNotFoundException, FlowNodeExecutionException {\n        SFlowNodeInstance flowNodeInstance;\n        try {\n            flowNodeInstance = inTx(\n                    () -> getServiceAccessor().getActivityInstanceService().getFlowNodeInstance(flownodeInstanceId));\n        } catch (SActivityInstanceNotFoundException e1) {\n            throw new UserTaskNotFoundException(\n                    String.format(\"User task %s is not found, it might already be executed\", flownodeInstanceId));\n        } catch (Exception e1) {\n            throw new FlowNodeExecutionException(e);\n        }\n        if (flowNodeInstance.getStateId() != FlowNodeState.ID_ACTIVITY_READY || flowNodeInstance.isStateExecuting()) {\n            //this in a not found because that task was not visible anymore\n            throw new UserTaskNotFoundException(\n                    String.format(\n                            \"User task is not executable (currently in state '%s'), this might be because someone else already executed it.\",\n                            (flowNodeInstance.isStateExecuting() ? \"executing \" : \"\")\n                                    + flowNodeInstance.getStateName()));\n        }\n        throw new FlowNodeExecutionException(e);\n    }\n\n    private <T> T inTx(Callable<T> booleanCallable) throws Exception {\n        return getServiceAccessor().getUserTransactionService().executeInTransaction(booleanCallable);\n    }\n\n    private void checkIsHumanTaskInReadyState(SFlowNodeInstance flowNodeInstance) throws SFlowNodeExecutionException {\n        if (!(flowNodeInstance instanceof SHumanTaskInstance)) {\n            throw new SFlowNodeExecutionException(\n                    \"Unable to execute flownode \" + flowNodeInstance.getId() + \" because is not a user task\");\n        }\n        if (flowNodeInstance.getStateId() != FlowNodeState.ID_ACTIVITY_READY || flowNodeInstance.isStateExecuting()) {\n            throw new SFlowNodeExecutionException(\n                    \"Unable to execute flow node \" + flowNodeInstance.getId()\n                            + \" because it is in an incompatible state (\"\n                            + (flowNodeInstance.isStateExecuting() ? \"transitioning from state \" : \"on state \")\n                            + flowNodeInstance.getStateName() + \"). Someone probably already called execute on it.\");\n        }\n    }\n\n    /**\n     * Execute a flow node. All methods that executes flow nodes and human tasks uses this one.\n     *\n     * @param userId the id of the user executing the task\n     * @param shouldBeReadyTask if true the method will only accept to execute human task in ready state\n     */\n    protected void executeFlowNode(final long userId, final long flowNodeInstanceId,\n            final Map<String, Serializable> inputs, boolean shouldBeReadyTask)\n            throws ContractViolationException, SBonitaException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        ContractDataService contractDataService = serviceAccessor.getContractDataService();\n        IdentityService identityService = serviceAccessor.getIdentityService();\n        SCommentService commentService = serviceAccessor.getCommentService();\n        WorkService workService = serviceAccessor.getWorkService();\n        BPMWorkFactory workFactory = serviceAccessor.getBPMWorkFactory();\n\n        SFlowNodeInstance flowNodeInstance = activityInstanceService.getFlowNodeInstance(flowNodeInstanceId);\n        final SSession session = getSession();\n        final Optional<Long> executerSubstituteUserId = Optional.ofNullable(session).map(SSession::getUserId);\n        final Optional<Long> executerUserId = session == null ? Optional.empty()\n                : userId == 0L ? executerSubstituteUserId : Optional.of(userId);\n        try (var flowNodeInstanceMDC = new FlowNodeInstanceMDC(flowNodeInstanceId,\n                executerUserId,\n                executerSubstituteUserId,\n                flowNodeInstance.getProcessDefinitionId(),\n                flowNodeInstance.getParentProcessInstanceId(),\n                flowNodeInstance.getRootProcessInstanceId())) {\n            if (shouldBeReadyTask) {\n                /*\n                 * this is to protect from concurrent execution of the task when 2 users call execute user task at the\n                 * same\n                 * time\n                 * it still might have concurrency issue but:\n                 * - if the second client call execute with contract inputs, on commit there will be a constraint\n                 * violation\n                 * + rollback\n                 * - if there is no contract input, the work will check that the activity is in ready state before\n                 * calling\n                 * execute.\n                 * The only left issue is that on this last case the executor will change to the last one.\n                 */\n                checkIsHumanTaskInReadyState(flowNodeInstance);\n            }\n            if (flowNodeInstance instanceof SUserTaskInstance) {\n                try {\n                    throwContractViolationExceptionIfContractIsInvalid(inputs, serviceAccessor, flowNodeInstance);\n                } catch (SContractViolationException e) {\n                    throw new ContractViolationException(e.getSimpleMessage(), e.getMessage(), e.getExplanations(),\n                            e.getCause());\n                }\n            }\n            if (flowNodeInstance instanceof SHumanTaskInstance\n                    && ((SHumanTaskInstance) flowNodeInstance).getAssigneeId() <= 0) {\n                throw new SFlowNodeExecutionException(\"The user task \" + flowNodeInstanceId + \" is not assigned\");\n            }\n\n            if (session != null) {\n                final boolean isFirstState = flowNodeInstance.getStateId() == 0;\n\n                if (flowNodeInstance instanceof SUserTaskInstance) {\n                    contractDataService.addUserTaskData(flowNodeInstance.getId(), inputs);\n                }\n                // TODO: the following 4 instructions seem to be redundant with stepForward:\n                // Cannot we factorize this?\n                serviceAccessor.getBPMArchiverService().archiveFlowNodeInstance(flowNodeInstance);\n                // flag as executing\n                activityInstanceService.setExecuting(flowNodeInstance);\n                activityInstanceService.setExecutedBy(flowNodeInstance, executerUserId.orElse(0L));\n                activityInstanceService.setExecutedBySubstitute(flowNodeInstance, executerSubstituteUserId.orElse(0L));\n                WorkDescriptor work = workFactory.createExecuteFlowNodeWorkDescriptor(flowNodeInstance);\n                workService.registerWork(work);\n                if (log.isInfoEnabled() && !isFirstState /*\n                                                          * don't log when create\n                                                          * subtask\n                                                          */) {\n                    final String message = LogMessageBuilder.buildExecuteTaskContextMessage(flowNodeInstance,\n                            session.getUserName(), executerUserId.orElse(0L),\n                            executerSubstituteUserId.orElse(0L), inputs);\n                    log.info(message);\n                } else if (log.isDebugEnabled()) {\n                    log.debug(\"Executing state \" + flowNodeInstance.getStateName() + \" (\"\n                            + flowNodeInstance.getStateId()\n                            + \") for flownode \" + LogMessageBuilder.buildFlowNodeContextMessage(flowNodeInstance));\n                }\n                if (!executerUserId.equals(executerSubstituteUserId)) {\n                    try {\n                        final SUser executorUser = identityService.getUser(executerUserId.orElse(0L));\n                        String stb = \"The user \" + session.getUserName() + \" \" + \"acting as delegate of the user \"\n                                + executorUser.getUserName() + \" \"\n                                + \"has done the task \\\"\" + flowNodeInstance.getDisplayName() + \"\\\".\";\n                        commentService.addSystemComment(flowNodeInstance.getParentProcessInstanceId(), stb);\n                    } catch (final SBonitaException e) {\n                        log.error(\n                                \"Error when adding a comment on the process instance.\", e);\n                    }\n                }\n            }\n        }\n    }\n\n    private void throwContractViolationExceptionIfContractIsInvalid(final Map<String, Serializable> inputs,\n            final ServiceAccessor serviceAccessor, final SFlowNodeInstance flowNodeInstance)\n            throws SBonitaReadException, SProcessDefinitionNotFoundException, SContractViolationException {\n        final SProcessDefinition processDefinition = serviceAccessor.getProcessDefinitionService()\n                .getProcessDefinition(flowNodeInstance.getProcessDefinitionId());\n        final SUserTaskDefinition userTaskDefinition = (SUserTaskDefinition) processDefinition.getProcessContainer()\n                .getFlowNode(\n                        flowNodeInstance.getFlowNodeDefinitionId());\n        final SContractDefinition contractDefinition = userTaskDefinition.getContract();\n        final ContractValidator validator = new ContractValidatorFactory()\n                .createContractValidator(serviceAccessor.getExpressionService());\n        validator.validate(flowNodeInstance.getProcessDefinitionId(), contractDefinition, inputs);\n\n    }\n\n    @Override\n    public Document removeDocument(final long documentId) throws DocumentNotFoundException, DeletionException {\n        return documentAPI.removeDocument(documentId);\n    }\n\n    @Override\n    public List<Document> getDocumentList(final long processInstanceId, final String name, final int from,\n            final int numberOfResult)\n            throws DocumentNotFoundException {\n        return documentAPI.getDocumentList(processInstanceId, name, from, numberOfResult);\n    }\n\n    @Override\n    public void setDocumentList(final long processInstanceId, final String name,\n            final List<DocumentValue> documentsValues) throws DocumentException,\n            DocumentNotFoundException {\n        documentAPI.setDocumentList(processInstanceId, name, documentsValues);\n    }\n\n    @Override\n    public void deleteContentOfArchivedDocument(final long archivedDocumentId)\n            throws DocumentException, DocumentNotFoundException {\n        documentAPI.deleteContentOfArchivedDocument(archivedDocumentId);\n    }\n\n    protected ServiceAccessor getServiceAccessor() {\n        return ServiceAccessorSingleton.getInstance();\n    }\n\n    long getUserId() {\n        return APIUtils.getUserId();\n    }\n\n    @Override\n    public Document addDocument(final long processInstanceId, final String documentName, final String description,\n            final DocumentValue documentValue)\n            throws ProcessInstanceNotFoundException, DocumentAttachmentException, AlreadyExistsException {\n        return documentAPI.addDocument(processInstanceId, documentName, description, documentValue);\n    }\n\n    @Override\n    public Document updateDocument(final long documentId, final DocumentValue documentValue)\n            throws ProcessInstanceNotFoundException,\n            DocumentAttachmentException,\n            AlreadyExistsException {\n        return documentAPI.updateDocument(documentId, documentValue);\n    }\n\n    @Override\n    public void purgeClassLoader(final long processDefinitionId)\n            throws ProcessDefinitionNotFoundException, UpdateException {\n        processManagementAPIImplDelegate.purgeClassLoader(processDefinitionId);\n    }\n\n    @Override\n    public Serializable getUserTaskContractVariableValue(final long userTaskInstanceId, final String name)\n            throws ContractDataNotFoundException {\n        final ContractDataService contractDataService = getServiceAccessor().getContractDataService();\n        try {\n            return contractDataService.getArchivedUserTaskDataValue(userTaskInstanceId, name);\n        } catch (final SContractDataNotFoundException scdnfe) {\n            throw new ContractDataNotFoundException(scdnfe);\n        } catch (final SBonitaReadException sbe) {\n            throw new RetrieveException(sbe);\n        }\n    }\n\n    @Override\n    public Serializable getProcessInputValueDuringInitialization(long processInstanceId, String name)\n            throws ContractDataNotFoundException {\n        try {\n            return getServiceAccessor().getContractDataService().getProcessDataValue(processInstanceId, name);\n        } catch (SContractDataNotFoundException | SBonitaReadException e) {\n            throw new ContractDataNotFoundException(e);\n        }\n    }\n\n    @Override\n    public Serializable getProcessInputValueAfterInitialization(long processInstanceId, String name)\n            throws ContractDataNotFoundException {\n        try {\n            return getServiceAccessor().getContractDataService().getArchivedProcessDataValue(processInstanceId, name);\n        } catch (SContractDataNotFoundException | SBonitaReadException e) {\n            throw new ContractDataNotFoundException(e);\n        }\n    }\n\n    @Override\n    public int getNumberOfParameterInstances(final long processDefinitionId) {\n        return processManagementAPIImplDelegate.getNumberOfParameterInstances(processDefinitionId);\n    }\n\n    @Override\n    public ParameterInstance getParameterInstance(final long processDefinitionId, final String parameterName)\n            throws NotFoundException {\n        return processManagementAPIImplDelegate.getParameterInstance(processDefinitionId, parameterName);\n    }\n\n    @Override\n    public List<ParameterInstance> getParameterInstances(final long processDefinitionId, final int startIndex,\n            final int maxResults,\n            final ParameterCriterion sort) {\n        return processManagementAPIImplDelegate.getParameterInstances(processDefinitionId, startIndex, maxResults,\n                sort);\n    }\n\n    @Override\n    public Map<String, Serializable> getUserTaskExecutionContext(long userTaskInstanceId)\n            throws UserTaskNotFoundException, ExpressionEvaluationException {\n        ServiceAccessor serviceAccessor = getServiceAccessor();\n        try {\n            SFlowNodeInstance activityInstance = serviceAccessor.getActivityInstanceService()\n                    .getFlowNodeInstance(userTaskInstanceId);\n            SProcessDefinition processDefinition = serviceAccessor.getProcessDefinitionService()\n                    .getProcessDefinition(activityInstance.getProcessDefinitionId());\n            final SExpressionContext expressionContext = createExpressionContext(userTaskInstanceId, processDefinition,\n                    CONTAINER_TYPE_ACTIVITY_INSTANCE, null);\n            SFlowNodeDefinition flowNode = processDefinition.getProcessContainer()\n                    .getFlowNode(activityInstance.getFlowNodeDefinitionId());\n            return evaluateContext(serviceAccessor.getExpressionResolverService(), expressionContext,\n                    ((SUserTaskDefinition) flowNode).getContext());\n        } catch (SFlowNodeNotFoundException | SBonitaReadException | SFlowNodeReadException\n                | SProcessDefinitionNotFoundException e) {\n            throw new UserTaskNotFoundException(e);\n        } catch (SInvalidExpressionException | SExpressionEvaluationException | SExpressionDependencyMissingException\n                | SExpressionTypeUnknownException e) {\n            throw new ExpressionEvaluationException(e);\n        }\n    }\n\n    @Override\n    public Map<String, Serializable> getArchivedUserTaskExecutionContext(long archivedUserTaskInstanceId)\n            throws UserTaskNotFoundException,\n            ExpressionEvaluationException {\n        ServiceAccessor serviceAccessor = getServiceAccessor();\n        try {\n            SAFlowNodeInstance archivedActivityInstance = serviceAccessor.getActivityInstanceService()\n                    .getArchivedFlowNodeInstance(archivedUserTaskInstanceId);\n            SProcessDefinition processDefinition = serviceAccessor.getProcessDefinitionService()\n                    .getProcessDefinition(archivedActivityInstance.getProcessDefinitionId());\n            final SExpressionContext expressionContext = createExpressionContext(\n                    archivedActivityInstance.getSourceObjectId(), processDefinition,\n                    CONTAINER_TYPE_ACTIVITY_INSTANCE, archivedActivityInstance.getArchiveDate());\n            SFlowNodeDefinition flowNode = processDefinition.getProcessContainer()\n                    .getFlowNode(archivedActivityInstance.getFlowNodeDefinitionId());\n            return evaluateContext(serviceAccessor.getExpressionResolverService(), expressionContext,\n                    ((SUserTaskDefinition) flowNode).getContext());\n        } catch (SFlowNodeNotFoundException | SBonitaReadException | SFlowNodeReadException\n                | SProcessDefinitionNotFoundException e) {\n            throw new UserTaskNotFoundException(e);\n        } catch (SInvalidExpressionException | SExpressionEvaluationException | SExpressionDependencyMissingException\n                | SExpressionTypeUnknownException e) {\n            throw new ExpressionEvaluationException(e);\n        }\n    }\n\n    @Override\n    public Map<String, Serializable> getProcessInstanceExecutionContext(long processInstanceId)\n            throws ProcessInstanceNotFoundException,\n            ExpressionEvaluationException {\n        ServiceAccessor serviceAccessor = getServiceAccessor();\n        try {\n            SProcessInstance processInstance = getProcessInstanceService(serviceAccessor)\n                    .getProcessInstance(processInstanceId);\n            if (processInstance == null) {\n                throw new ProcessInstanceNotFoundException(\"Process Instance not found \" + processInstanceId);\n            }\n            SProcessDefinition processDefinition = serviceAccessor.getProcessDefinitionService()\n                    .getProcessDefinition(processInstance.getProcessDefinitionId());\n            final SExpressionContext expressionContext = createExpressionContext(processInstanceId, processDefinition,\n                    CONTAINER_TYPE_PROCESS_INSTANCE, null);\n            return evaluateContext(serviceAccessor.getExpressionResolverService(), expressionContext,\n                    processDefinition.getContext());\n        } catch (SProcessInstanceNotFoundException | SBonitaReadException | SProcessInstanceReadException\n                | SProcessDefinitionNotFoundException e) {\n            throw new ProcessInstanceNotFoundException(e);\n        } catch (SInvalidExpressionException | SExpressionEvaluationException | SExpressionDependencyMissingException\n                | SExpressionTypeUnknownException e) {\n            throw new ExpressionEvaluationException(e);\n        }\n    }\n\n    @Override\n    public Map<String, Serializable> getArchivedProcessInstanceExecutionContext(long archivedProcessInstanceId)\n            throws ProcessInstanceNotFoundException,\n            ExpressionEvaluationException {\n        ServiceAccessor serviceAccessor = getServiceAccessor();\n        try {\n            SAProcessInstance processInstance = getProcessInstanceService(serviceAccessor)\n                    .getArchivedProcessInstance(archivedProcessInstanceId);\n            if (processInstance == null) {\n                throw new ProcessInstanceNotFoundException(\n                        \"Archived Process Instance not found \" + archivedProcessInstanceId);\n            }\n            SProcessDefinition processDefinition = serviceAccessor.getProcessDefinitionService()\n                    .getProcessDefinition(processInstance.getProcessDefinitionId());\n            final SExpressionContext expressionContext = createExpressionContext(processInstance.getSourceObjectId(),\n                    processDefinition,\n                    CONTAINER_TYPE_PROCESS_INSTANCE, processInstance.getArchiveDate());\n            return evaluateContext(serviceAccessor.getExpressionResolverService(), expressionContext,\n                    processDefinition.getContext());\n        } catch (SBonitaReadException | SProcessInstanceReadException | SProcessDefinitionNotFoundException e) {\n            throw new ProcessInstanceNotFoundException(e);\n        } catch (SInvalidExpressionException | SExpressionEvaluationException | SExpressionDependencyMissingException\n                | SExpressionTypeUnknownException e) {\n            throw new ExpressionEvaluationException(e);\n        }\n    }\n\n    Map<String, Serializable> evaluateContext(ExpressionResolverService expressionResolverService,\n            SExpressionContext expressionContext,\n            List<SContextEntry> context) throws SExpressionTypeUnknownException, SExpressionEvaluationException,\n            SExpressionDependencyMissingException,\n            SInvalidExpressionException {\n        if (context.isEmpty()) {\n            return Collections.emptyMap();\n        }\n        List<SExpression> expressions = toExpressionList(context);\n        List<Object> evaluate = expressionResolverService.evaluate(expressions, expressionContext);\n        return toResultMap(context, evaluate);\n    }\n\n    List<SExpression> toExpressionList(List<SContextEntry> context) {\n        List<SExpression> expressions = new ArrayList<>();\n        for (SContextEntry sContextEntry : context) {\n            expressions.add(sContextEntry.getExpression());\n        }\n        return expressions;\n    }\n\n    HashMap<String, Serializable> toResultMap(List<SContextEntry> context, List<Object> evaluate) {\n        HashMap<String, Serializable> result = new HashMap<>(evaluate.size());\n        for (int i = 0; i < evaluate.size(); i++) {\n            result.put(context.get(i).getKey(), (Serializable) evaluate.get(i));\n        }\n        return result;\n    }\n\n    ProcessInstanceService getProcessInstanceService(ServiceAccessor serviceAccessor) {\n        return serviceAccessor.getProcessInstanceService();\n    }\n\n    SExpressionContext createExpressionContext(long processInstanceId, SProcessDefinition processDefinition,\n            String type, Long time) {\n        final SExpressionContext expressionContext = new SExpressionContext();\n        expressionContext.setContainerId(processInstanceId);\n        expressionContext.setContainerType(type);\n        expressionContext.setProcessDefinitionId(processDefinition.getId());\n        if (time != null) {\n            expressionContext.setTime(time);\n        }\n        return expressionContext;\n    }\n\n    @Override\n    public SearchResult<FormMapping> searchFormMappings(SearchOptions searchOptions) throws SearchException {\n        return processConfigurationAPI.searchFormMappings(searchOptions);\n    }\n\n    @Override\n    public FormMapping getFormMapping(long formMappingId) throws FormMappingNotFoundException {\n        return processConfigurationAPI.getFormMapping(formMappingId);\n    }\n\n    @Override\n    public ProcessInstance updateProcessInstance(final long processInstanceId, final ProcessInstanceUpdater updater)\n            throws ProcessInstanceNotFoundException, UpdateException {\n        if (updater == null || updater.getFields().isEmpty()) {\n            throw new UpdateException(\"The update descriptor does not contain field updates\");\n        }\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n        try {\n            final SProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);\n            EntityUpdateDescriptor entityUpdateDescriptor = new EntityUpdateDescriptor();\n            final Map<org.bonitasoft.engine.bpm.process.impl.ProcessInstanceUpdater.ProcessInstanceField, Serializable> fields = updater\n                    .getFields();\n            for (final Entry<org.bonitasoft.engine.bpm.process.impl.ProcessInstanceUpdater.ProcessInstanceField, Serializable> field : fields\n                    .entrySet()) {\n                entityUpdateDescriptor.addField(SProcessInstance.STRING_INDEX_KEY + (field.getKey().ordinal() + 1),\n                        field.getValue());\n            }\n            processInstanceService.updateProcess(processInstance, entityUpdateDescriptor);\n            return getProcessInstance(processInstanceId);\n        } catch (final SProcessInstanceNotFoundException spinfe) {\n            throw new ProcessInstanceNotFoundException(spinfe);\n        } catch (final SBonitaException | RetrieveException sbe) {\n            throw new UpdateException(sbe);\n        }\n    }\n\n    @Override\n    public ProcessInstance updateProcessInstanceIndex(final long processInstanceId, final Index index,\n            final String value) throws ProcessInstanceNotFoundException, UpdateException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n        try {\n            final SProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);\n            EntityUpdateDescriptor entityUpdateDescriptor = new EntityUpdateDescriptor();\n            entityUpdateDescriptor.addField(SProcessInstance.STRING_INDEX_KEY + (index.ordinal() + 1), value);\n            processInstanceService.updateProcess(processInstance, entityUpdateDescriptor);\n            return getProcessInstance(processInstanceId);\n        } catch (final SProcessInstanceNotFoundException notFound) {\n            throw new ProcessInstanceNotFoundException(notFound);\n        } catch (final SBonitaException | RetrieveException sbe) {\n            throw new UpdateException(sbe);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/ProcessConfigurationAPIImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.core.form.FormMappingService;\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\nimport org.bonitasoft.engine.exception.FormMappingNotFoundException;\nimport org.bonitasoft.engine.exception.RetrieveException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.form.FormMapping;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor;\nimport org.bonitasoft.engine.search.form.SearchFormMappings;\nimport org.bonitasoft.engine.service.*;\n\n/**\n * @author Baptiste Mesta\n */\npublic class ProcessConfigurationAPIImpl {\n\n    protected ServiceAccessor getServiceAccessor() {\n        try {\n            return ServiceAccessorSingleton.getInstance();\n        } catch (final Exception e) {\n            throw new BonitaRuntimeException(e);\n        }\n    }\n\n    public SearchResult<FormMapping> searchFormMappings(final SearchOptions searchOptions) throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        FormMappingService formMappingService = serviceAccessor.getFormMappingService();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final SearchFormMappings searchFormMappings = new SearchFormMappings(formMappingService,\n                getServiceAccessor().getProcessDefinitionService(),\n                searchEntitiesDescriptor.getSearchFormMappingDescriptor(),\n                searchOptions);\n        try {\n            searchFormMappings.execute();\n            return searchFormMappings.getResult();\n        } catch (final SBonitaException sbe) {\n            throw new SearchException(sbe);\n        }\n    }\n\n    public FormMapping getFormMapping(long formMappingId) throws FormMappingNotFoundException {\n        final FormMappingService formMappingService = getServiceAccessor().getFormMappingService();\n        try {\n            return ModelConvertor.toFormMapping(formMappingService.get(formMappingId),\n                    new FormRequiredAnalyzer(getServiceAccessor()\n                            .getProcessDefinitionService()));\n        } catch (SBonitaReadException e) {\n            throw new RetrieveException(e);\n        } catch (SObjectNotFoundException e) {\n            throw new FormMappingNotFoundException(\"no form mapping found with id\" + formMappingId);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/ProcessDeploymentAPIDelegate.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport lombok.Getter;\nimport org.bonitasoft.engine.api.impl.transaction.process.EnableProcess;\nimport org.bonitasoft.engine.bar.BusinessArchiveService;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.process.*;\nimport org.bonitasoft.engine.commons.exceptions.SAlreadyExistsException;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectCreationException;\nimport org.bonitasoft.engine.commons.exceptions.SV6FormsDeployException;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.RetrieveException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.execution.event.EventsHandler;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor;\nimport org.bonitasoft.engine.search.descriptor.SearchProcessDefinitionsDescriptor;\nimport org.bonitasoft.engine.search.process.SearchProcessDeploymentInfos;\nimport org.bonitasoft.engine.service.ModelConvertor;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.ServiceAccessorSingleton;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class ProcessDeploymentAPIDelegate {\n\n    @Getter\n    private static final ProcessDeploymentAPIDelegate instance = new ProcessDeploymentAPIDelegate();\n\n    private ProcessDeploymentAPIDelegate() {\n    }\n\n    protected ServiceAccessor getServiceAccessor() {\n        return ServiceAccessorSingleton.getInstance();\n    }\n\n    public ProcessDefinition deploy(final BusinessArchive businessArchive)\n            throws ProcessDeployException, AlreadyExistsException {\n        validateBusinessArchive(businessArchive);\n        final BusinessArchiveService businessArchiveService = getServiceAccessor().getBusinessArchiveService();\n        try {\n            return ModelConvertor.toProcessDefinition(businessArchiveService.deploy(businessArchive));\n        } catch (SV6FormsDeployException e) {\n            throw new V6FormDeployException(e);\n        } catch (SObjectCreationException e) {\n            throw new ProcessDeployException(e);\n        } catch (SAlreadyExistsException e) {\n            throw new AlreadyExistsException(e.getMessage());\n        }\n    }\n\n    public ProcessDefinition deployAndEnableProcess(final BusinessArchive businessArchive)\n            throws ProcessDeployException, ProcessEnablementException, AlreadyExistsException {\n        final ProcessDefinition processDefinition = deploy(businessArchive);\n        try {\n            enableProcess(processDefinition.getId());\n        } catch (final ProcessDefinitionNotFoundException e) {\n            throw new ProcessEnablementException(e.getMessage());\n        }\n        return processDefinition;\n    }\n\n    void validateBusinessArchive(BusinessArchive businessArchive) throws ProcessDeployException {\n        for (Map.Entry<String, byte[]> resource : businessArchive.getResources().entrySet()) {\n            final byte[] resourceContent = resource.getValue();\n            if (resourceContent == null || resourceContent.length == 0) {\n                throw new ProcessDeployException(\n                        \"The BAR file you are trying to deploy contains an empty file: \" + resource.getKey()\n                                + \". The process cannot be deployed. Fix it or remove it from the BAR.\");\n            }\n        }\n    }\n\n    public void enableProcess(final long processDefinitionId)\n            throws ProcessDefinitionNotFoundException, ProcessEnablementException {\n        final ProcessDefinitionService processDefinitionService = getServiceAccessor()\n                .getProcessDefinitionService();\n        final EventsHandler eventsHandler = getServiceAccessor().getEventsHandler();\n        try {\n            new EnableProcess(processDefinitionService, processDefinitionId,\n                    eventsHandler, SessionInfos.getUserNameFromSession()).execute();\n        } catch (final SProcessDefinitionNotFoundException e) {\n            throw new ProcessDefinitionNotFoundException(e);\n        } catch (final Exception e) {\n            throw new ProcessEnablementException(e);\n        }\n    }\n\n    public long getProcessDefinitionId(final String name, final String version)\n            throws ProcessDefinitionNotFoundException {\n        try {\n            return getServiceAccessor().getProcessDefinitionService().getProcessDefinitionId(name, version);\n        } catch (final SProcessDefinitionNotFoundException e) {\n            throw new ProcessDefinitionNotFoundException(e);\n        } catch (final SBonitaReadException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    public List<Problem> getProcessResolutionProblems(final long processDefinitionId)\n            throws ProcessDefinitionNotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        try {\n            SProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(processDefinitionId);\n            return serviceAccessor.getBusinessArchiveArtifactsManager().getProcessResolutionProblems(processDefinition);\n        } catch (final SProcessDefinitionNotFoundException | SBonitaReadException e) {\n            throw new ProcessDefinitionNotFoundException(e);\n        }\n    }\n\n    public Map<Long, ProcessDeploymentInfo> getProcessDeploymentInfosFromIds(final List<Long> processDefinitionIds) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        try {\n            final List<SProcessDefinitionDeployInfo> processDefinitionDeployInfos = processDefinitionService\n                    .getProcessDeploymentInfos(processDefinitionIds);\n            final List<ProcessDeploymentInfo> processDeploymentInfos = ModelConvertor\n                    .toProcessDeploymentInfo(processDefinitionDeployInfos);\n            final Map<Long, ProcessDeploymentInfo> mProcessDefinitions = new HashMap<>();\n            for (final ProcessDeploymentInfo p : processDeploymentInfos) {\n                mProcessDefinitions.put(p.getProcessId(), p);\n            }\n            return mProcessDefinitions;\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    public ProcessDeploymentInfo getProcessDeploymentInfo(final long processDefinitionId)\n            throws ProcessDefinitionNotFoundException {\n        final ProcessDefinitionService processDefinitionService = getServiceAccessor()\n                .getProcessDefinitionService();\n        try {\n            return ModelConvertor\n                    .toProcessDeploymentInfo(processDefinitionService.getProcessDeploymentInfo(processDefinitionId));\n        } catch (final SProcessDefinitionNotFoundException e) {\n            throw new ProcessDefinitionNotFoundException(e);\n        } catch (final SBonitaReadException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    public SearchResult<ProcessDeploymentInfo> searchProcessDeploymentInfos(final SearchOptions searchOptions)\n            throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        final SearchProcessDefinitionsDescriptor searchDescriptor = searchEntitiesDescriptor\n                .getSearchProcessDefinitionsDescriptor();\n        final SearchProcessDeploymentInfos transactionSearch = new SearchProcessDeploymentInfos(\n                processDefinitionService, searchDescriptor, searchOptions);\n        try {\n            transactionSearch.execute();\n        } catch (final SBonitaException e) {\n            throw new SearchException(\"Can't get processDefinition's executing searchProcessDefinitions()\", e);\n        }\n        return transactionSearch.getResult();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/ProcessInvolvementDelegate.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.impl.transaction.process.GetLastArchivedProcessInstance;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SExecutionException;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAUserTaskInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SUserTaskInstanceBuilderFactory;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.RetrieveException;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.SUserNotFoundException;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.persistence.*;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.ServiceAccessorSingleton;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class ProcessInvolvementDelegate {\n\n    private static final int BATCH_SIZE = 100;\n\n    protected ServiceAccessor getServiceAccessor() {\n        return ServiceAccessorSingleton.getInstance();\n    }\n\n    private static QueryOptions buildArchivedTasksQueryOptions(final long processInstanceId) {\n        final SAUserTaskInstanceBuilderFactory archUserTaskKeyFactory = BuilderFactory\n                .get(SAUserTaskInstanceBuilderFactory.class);\n        final String humanTaskIdKey = archUserTaskKeyFactory.getIdKey();\n        final String parentProcessInstanceKey = archUserTaskKeyFactory.getParentProcessInstanceKey();\n        final List<OrderByOption> archivedOrderByOptions = Collections\n                .singletonList(new OrderByOption(SAHumanTaskInstance.class, humanTaskIdKey, OrderByType.ASC));\n        final List<FilterOption> archivedFilterOptions = Collections\n                .singletonList(\n                        new FilterOption(SAHumanTaskInstance.class, parentProcessInstanceKey, processInstanceId));\n        return new QueryOptions(0, BATCH_SIZE, archivedOrderByOptions, archivedFilterOptions, null);\n    }\n\n    public boolean isInvolvedInProcessInstance(final long userId, final long processInstanceId)\n            throws ProcessInstanceNotFoundException {\n        final TaskInvolvementDelegate taskInvolvementDelegate = new TaskInvolvementDelegate();\n        // IS_PROCESS_INITIATOR rule\n        if (isProcessOrArchivedProcessInitiator(userId, processInstanceId)) {\n            return true;\n        }\n        try {\n            // IS_TASK_PERFORMER rule\n            if (taskInvolvementDelegate.isExecutorOfArchivedTaskOfProcess(userId, processInstanceId)) {\n                return true;\n            }\n        } catch (SBonitaReadException e) {\n            throw new RetrieveException(e);\n        }\n\n        try {\n            // IS_INVOLVED_IN_PROCESS_INSTANCE rule\n            if (taskInvolvementDelegate.hasUserPendingOrAssignedTasks(userId, processInstanceId)) {\n                return true;\n            }\n        } catch (SExecutionException e) {\n            throw new RetrieveException(e);\n        }\n\n        return false;\n\n    }\n\n    public boolean isProcessOrArchivedProcessInitiator(long userId, long processInstanceId)\n            throws ProcessInstanceNotFoundException {\n        try {\n            return isProcessInitiator(userId, processInstanceId);\n        } catch (SProcessInstanceNotFoundException e) {\n            return isArchivedProcessInitiator(userId, processInstanceId);\n        } catch (SProcessInstanceReadException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    private boolean isProcessInitiator(long userId, Long processInstanceId)\n            throws SProcessInstanceNotFoundException, SProcessInstanceReadException {\n        final ProcessInstanceService processInstanceService = getServiceAccessor().getProcessInstanceService();\n        final SProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);\n        return userId == processInstance.getStartedBy();\n    }\n\n    boolean isArchivedProcessInitiator(long userId, long processInstanceId) throws ProcessInstanceNotFoundException {\n        final ProcessInstanceService processInstanceService = getServiceAccessor().getProcessInstanceService();\n        final List<OrderByOption> orderByOptions = Arrays.asList(\n                new OrderByOption(SAProcessInstance.class, ArchivedProcessInstancesSearchDescriptor.ARCHIVE_DATE,\n                        OrderByType.DESC),\n                new OrderByOption(SAProcessInstance.class, ArchivedProcessInstancesSearchDescriptor.END_DATE,\n                        OrderByType.DESC));\n        final List<FilterOption> filterOptions = Collections.singletonList(new FilterOption(SAProcessInstance.class,\n                ArchivedProcessInstancesSearchDescriptor.SOURCE_OBJECT_ID, processInstanceId));\n        final QueryOptions queryOptions = new QueryOptions(0, 1, orderByOptions, filterOptions, null);\n\n        final List<SAProcessInstance> saProcessInstances;\n        try {\n            saProcessInstances = processInstanceService.searchArchivedProcessInstances(queryOptions);\n        } catch (SBonitaReadException e) {\n            throw new RetrieveException(e);\n        }\n        if (saProcessInstances.isEmpty()) {\n            throw new ProcessInstanceNotFoundException(processInstanceId);\n        }\n        return userId == (saProcessInstances.get(0).getStartedBy());\n    }\n\n    public boolean isManagerOfUserInvolvedInProcessInstance(final long managerUserId, final long processInstanceId)\n            throws BonitaException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n        final IdentityService identityService = serviceAccessor.getIdentityService();\n        final TaskInvolvementDelegate taskInvolvementDelegate = new TaskInvolvementDelegate();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n\n        final List<SUser> subordinates = getSubordinates(managerUserId, identityService);\n\n        try {\n            try {\n\n                // Part specific to active process instances:\n                final SProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);\n                if (isUserManagerOfProcessInstanceInitiator(managerUserId, processInstance.getStartedBy())) {\n                    return true;\n                }\n\n                // Has the manager at least one subordinates with at least one pending task in this process instance:\n                if (taskInvolvementDelegate.searchPendingTasksManagedBy(managerUserId,\n                        new SearchOptionsBuilder(0, 1)\n                                .filter(HumanTaskInstanceSearchDescriptor.PROCESS_INSTANCE_ID, processInstanceId)\n                                .done())\n                        .getCount() > 0) {\n                    return true;\n                }\n\n                QueryOptions queryOptions = buildActiveTasksQueryOptions(processInstanceId);\n                List<SHumanTaskInstance> sHumanTaskInstances = activityInstanceService.searchHumanTasks(queryOptions);\n                while (!sHumanTaskInstances.isEmpty()) {\n                    for (final SHumanTaskInstance sHumanTaskInstance : sHumanTaskInstances) {\n                        if (isTaskAssignedToAUserInTheList(sHumanTaskInstance, subordinates)) {\n                            return true;\n                        }\n                    }\n                    queryOptions = QueryOptions.getNextPage(queryOptions);\n                    sHumanTaskInstances = activityInstanceService.searchHumanTasks(queryOptions);\n                }\n            } catch (final SProcessInstanceNotFoundException exc) {\n                // process instance may be completed already:\n\n                // Part specific to archived process instances:\n                try {\n                    final ArchivedProcessInstance archProcessInstance = getLastArchivedProcessInstance(\n                            processInstanceId);\n                    if (isUserManagerOfProcessInstanceInitiator(managerUserId, archProcessInstance.getStartedBy())) {\n                        return true;\n                    }\n                } catch (final SBonitaException e) {\n                    throw new ProcessInstanceNotFoundException(processInstanceId);\n                }\n            }\n\n            // Part common to active and archived process instances:\n            return isArchivedTaskDoneByOneOfTheSubordinates(processInstanceId, activityInstanceService, subordinates);\n\n        } catch (final SBonitaException e) {\n            throw new BonitaException(\n                    \"Problem while searching for users involved in process instance through their manager\", e);\n        }\n    }\n\n    private QueryOptions buildActiveTasksQueryOptions(final long processInstanceId) {\n        final SUserTaskInstanceBuilderFactory userTaskKeyFactory = BuilderFactory\n                .get(SUserTaskInstanceBuilderFactory.class);\n        final String humanTaskIdKey = userTaskKeyFactory.getIdKey();\n        final String parentProcessInstanceKey = userTaskKeyFactory.getParentProcessInstanceKey();\n        final List<OrderByOption> orderByOptions = Collections\n                .singletonList(new OrderByOption(SHumanTaskInstance.class, humanTaskIdKey, OrderByType.ASC));\n        final List<FilterOption> filterOptions = Collections\n                .singletonList(new FilterOption(SHumanTaskInstance.class, parentProcessInstanceKey, processInstanceId));\n        return new QueryOptions(0, BATCH_SIZE, orderByOptions, filterOptions, null);\n    }\n\n    private List<SUser> getSubordinates(final long managerUserId, final IdentityService identityService) {\n        final List<OrderByOption> userOrderBys = Collections\n                .singletonList(new OrderByOption(SUser.class, SUser.ID,\n                        OrderByType.ASC));\n        final List<FilterOption> userFilters = Collections\n                .singletonList(new FilterOption(SUser.class, SUser.MANAGER_USER_ID, managerUserId));\n        try {\n            return identityService.searchUsers(new QueryOptions(0, Integer.MAX_VALUE, userOrderBys, userFilters, null));\n        } catch (final SBonitaReadException e) {\n            return Collections.emptyList();\n        }\n    }\n\n    private boolean isArchivedTaskDoneByOneOfTheSubordinates(final long processInstanceId,\n            final ActivityInstanceService activityInstanceService,\n            final List<SUser> subordinates) throws SBonitaReadException {\n        QueryOptions archivedQueryOptions = buildArchivedTasksQueryOptions(processInstanceId);\n\n        List<SAHumanTaskInstance> sArchivedHumanTasks = activityInstanceService\n                .searchArchivedTasks(archivedQueryOptions);\n        while (!sArchivedHumanTasks.isEmpty()) {\n            for (final SAHumanTaskInstance sArchivedHumanTask : sArchivedHumanTasks) {\n                if (isTaskDoneByAUserInTheList(sArchivedHumanTask, subordinates)) {\n                    return true;\n                }\n            }\n            archivedQueryOptions = QueryOptions.getNextPage(archivedQueryOptions);\n            sArchivedHumanTasks = activityInstanceService.searchArchivedTasks(archivedQueryOptions);\n        }\n        return false;\n    }\n\n    private boolean isTaskDoneByAUserInTheList(final SAHumanTaskInstance sArchivedHumanTask, final List<SUser> users) {\n        for (final SUser user : users) {\n            if (user.getId() == sArchivedHumanTask.getExecutedBy()) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    private boolean isTaskAssignedToAUserInTheList(final SHumanTaskInstance humanTask, final List<SUser> users) {\n        for (final SUser user : users) {\n            if (user.getId() == humanTask.getAssigneeId()) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    private boolean isUserManagerOfProcessInstanceInitiator(final long userId, final long startedByUserId) {\n        final IdentityService identityService = getServiceAccessor().getIdentityService();\n        SUser sUser;\n        try {\n            sUser = identityService.getUser(startedByUserId);\n        } catch (final SUserNotFoundException e) {\n            return false;\n        }\n        return userId == sUser.getManagerUserId();\n    }\n\n    public ArchivedProcessInstance getLastArchivedProcessInstance(final long processInstanceId)\n            throws SBonitaException {\n        final ProcessInstanceService processInstanceService = getServiceAccessor().getProcessInstanceService();\n        final ProcessDefinitionService processDefinitionService = getServiceAccessor()\n                .getProcessDefinitionService();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = getServiceAccessor()\n                .getSearchEntitiesDescriptor();\n\n        final GetLastArchivedProcessInstance searchArchivedProcessInstances = new GetLastArchivedProcessInstance(\n                processInstanceService,\n                processDefinitionService, processInstanceId, searchEntitiesDescriptor);\n\n        searchArchivedProcessInstances.execute();\n        return searchArchivedProcessInstances.getResult();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/ProcessManagementAPIImplDelegate.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier;\nimport static org.bonitasoft.engine.dependency.model.ScopeType.PROCESS;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.api.impl.transaction.process.DisableProcess;\nimport org.bonitasoft.engine.bpm.parameter.ParameterCriterion;\nimport org.bonitasoft.engine.bpm.parameter.ParameterInstance;\nimport org.bonitasoft.engine.bpm.parameter.impl.ParameterImpl;\nimport org.bonitasoft.engine.bpm.process.ActivationState;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.classloader.SClassLoaderException;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.core.process.definition.model.SParameterDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.NotFoundException;\nimport org.bonitasoft.engine.exception.RetrieveException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.parameter.OrderBy;\nimport org.bonitasoft.engine.parameter.ParameterService;\nimport org.bonitasoft.engine.parameter.SParameter;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.scheduler.SchedulerService;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.impl.ServiceAccessorFactory;\n\n/**\n * @author Matthieu Chaffotte\n */\n// Uncomment the \"implements\" when this delegate implements all the methods.\n@Slf4j\npublic class ProcessManagementAPIImplDelegate /* implements ProcessManagementAPI */ {\n\n    private static final ProcessManagementAPIImplDelegate instance = new ProcessManagementAPIImplDelegate();\n\n    public static ProcessManagementAPIImplDelegate getInstance() {\n        return instance;\n    }\n\n    protected ServiceAccessor getServiceAccessor() {\n        try {\n            return ServiceAccessorFactory.getInstance().createServiceAccessor();\n        } catch (final Exception e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static SProcessDefinition getServerProcessDefinition(final long processDefinitionId,\n            final ProcessDefinitionService processDefinitionService)\n            throws SProcessDefinitionNotFoundException, SBonitaReadException {\n        return processDefinitionService.getProcessDefinition(processDefinitionId);\n    }\n\n    public void deleteProcessDefinition(final long processDefinitionId)\n            throws SBonitaException, BonitaHomeNotSetException, IOException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        serviceAccessor.getBusinessArchiveService().delete(processDefinitionId);\n\n        log.info(\"The user <\" + SessionInfos.getUserNameFromSession() + \"> has deleted process with id = <\"\n                + processDefinitionId + \">\");\n    }\n\n    public void disableProcess(final long processId) throws SBonitaException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        final EventInstanceService eventInstanceService = serviceAccessor.getEventInstanceService();\n        final SchedulerService schedulerService = serviceAccessor.getSchedulerService();\n\n        final DisableProcess disableProcess = new DisableProcess(processDefinitionService, processId,\n                eventInstanceService,\n                schedulerService, SessionInfos.getUserNameFromSession());\n        disableProcess.execute();\n    }\n\n    public void purgeClassLoader(final long processDefinitionId)\n            throws ProcessDefinitionNotFoundException, UpdateException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        try {\n            final SProcessDefinitionDeployInfo processDeploymentInfo = processDefinitionService\n                    .getProcessDeploymentInfo(processDefinitionId);\n            if (!ActivationState.DISABLED.name().equals(processDeploymentInfo.getActivationState())) {\n                throw new UpdateException(\"Purge can only be done on a disabled process\");\n            }\n            final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n            final long numberOfProcessInstances = processInstanceService\n                    .getNumberOfProcessInstances(processDefinitionId);\n            if (numberOfProcessInstances != 0) {\n                throw new UpdateException(\"Purge can only be done on a disabled process with no running instances\");\n            }\n            serviceAccessor.getClassLoaderService().removeLocalClassloader(identifier(PROCESS, processDefinitionId));\n        } catch (final SProcessDefinitionNotFoundException e) {\n            throw new ProcessDefinitionNotFoundException(e);\n        } catch (final SBonitaReadException e) {\n            throw new RetrieveException(e);\n        } catch (SClassLoaderException e) {\n            throw new UpdateException(e);\n        }\n    }\n\n    public List<ParameterInstance> getParameterInstances(final long processDefinitionId, final int startIndex,\n            final int maxResults, final ParameterCriterion sort) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ParameterService parameterService = serviceAccessor.getParameterService();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        try {\n            OrderBy order;\n            switch (sort) {\n                case NAME_DESC:\n                    order = OrderBy.NAME_DESC;\n                    break;\n                default:\n                    order = OrderBy.NAME_ASC;\n                    break;\n            }\n\n            final SProcessDefinition sProcessDefinition = getServerProcessDefinition(processDefinitionId,\n                    processDefinitionService);\n            if (sProcessDefinition.getParameters().isEmpty()) {\n                return Collections.emptyList();\n            }\n            final List<SParameter> parameters = parameterService.get(processDefinitionId, startIndex, maxResults,\n                    order);\n            final List<ParameterInstance> parameterInstances = new ArrayList<>();\n            for (final SParameter parameter : parameters) {\n                final String name = parameter.getName();\n                final String value = parameter.getValue();\n                final SParameterDefinition parameterDefinition = sProcessDefinition.getParameter(name);\n                final String description = parameterDefinition.getDescription();\n                final String type = parameterDefinition.getType();\n                parameterInstances.add(new ParameterImpl(name, description, value, type));\n            }\n            return parameterInstances;\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    public int getNumberOfParameterInstances(final long processDefinitionId) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        try {\n            final SProcessDefinition sProcessDefinition = getServerProcessDefinition(processDefinitionId,\n                    processDefinitionService);\n            return sProcessDefinition.getParameters().size();\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    public ParameterInstance getParameterInstance(final long processDefinitionId, final String parameterName)\n            throws NotFoundException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ParameterService parameterService = serviceAccessor.getParameterService();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        try {\n            final SProcessDefinition sProcessDefinition = getServerProcessDefinition(processDefinitionId,\n                    processDefinitionService);\n            final SParameter parameter = parameterService.get(processDefinitionId, parameterName);\n            if (parameter == null) {\n                throw new NotFoundException(\"the parameter with name \" + parameterName + \" and process with id \"\n                        + processDefinitionId + \" was not found.\");\n            }\n            final String name = parameter.getName();\n            final String value = parameter.getValue();\n            final SParameterDefinition parameterDefinition = sProcessDefinition.getParameter(name);\n            final String description = parameterDefinition.getDescription();\n            final String type = parameterDefinition.getType();\n            return new ParameterImpl(name, description, value, type);\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/ProcessStarter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport java.io.Serializable;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.function.Supplier;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.bpm.connector.ConnectorDefinitionWithInputValues;\nimport org.bonitasoft.engine.bpm.contract.ContractViolationException;\nimport org.bonitasoft.engine.bpm.process.ProcessActivationException;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ProcessExecutionException;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.core.process.comment.api.SCommentService;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionException;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SContractViolationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceCreationException;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\nimport org.bonitasoft.engine.exception.RetrieveException;\nimport org.bonitasoft.engine.execution.Filter;\nimport org.bonitasoft.engine.execution.FlowNodeNameFilter;\nimport org.bonitasoft.engine.execution.FlowNodeSelector;\nimport org.bonitasoft.engine.execution.ProcessExecutor;\nimport org.bonitasoft.engine.execution.StartFlowNodeFilter;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.mdc.MDCHelper;\nimport org.bonitasoft.engine.mdc.MDCHelper.CheckedCallable4;\nimport org.bonitasoft.engine.mdc.ProcessInstanceMDC;\nimport org.bonitasoft.engine.operation.Operation;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.service.ModelConvertor;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.ServiceAccessorSingleton;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Vincent Elcrin\n * @author Matthieu Chaffotte\n */\n@Slf4j\npublic class ProcessStarter {\n\n    private final long userId;\n\n    private final long processDefinitionId;\n\n    private final List<Operation> operations;\n\n    private final Map<String, Serializable> context;\n\n    private final Filter<SFlowNodeDefinition> filter;\n\n    private final Map<String, Serializable> processContractInputs;\n\n    private ProcessStarter(final long userId, final long processDefinitionId, final List<Operation> operations,\n            final Map<String, Serializable> context, final Filter<SFlowNodeDefinition> filter,\n            final Map<String, Serializable> processContractInputs) {\n        this.userId = userId;\n        this.processDefinitionId = processDefinitionId;\n        this.operations = operations;\n        this.context = context;\n        this.filter = filter;\n        this.processContractInputs = processContractInputs;\n    }\n\n    public ProcessStarter(final long userId, final long processDefinitionId, final List<Operation> operations,\n            final Map<String, Serializable> context) {\n        this(userId, processDefinitionId, operations, context, new StartFlowNodeFilter(), null);\n    }\n\n    public ProcessStarter(final long userId, final long processDefinitionId, final List<Operation> operations,\n            final Map<String, Serializable> context,\n            final List<String> activityNames, Map<String, Serializable> processContractInputs) {\n        this(userId, processDefinitionId, operations, context, new FlowNodeNameFilter(activityNames),\n                processContractInputs);\n    }\n\n    public ProcessStarter(final long userId, final long processDefinitionId,\n            final Map<String, Serializable> processContractInputs) {\n        this(userId, processDefinitionId, null, null, new StartFlowNodeFilter(), processContractInputs);\n    }\n\n    public ProcessInstance start()\n            throws ProcessDefinitionNotFoundException, ProcessActivationException, ProcessExecutionException,\n            ContractViolationException {\n        try {\n            return start(null);\n        } catch (final SContractViolationException e) {\n            throw new ContractViolationException(e.getSimpleMessage(), e.getMessage(), e.getExplanations(),\n                    e.getCause());\n        } catch (final SProcessDefinitionNotFoundException e) {\n            throw new ProcessDefinitionNotFoundException(e);\n        } catch (final SBonitaReadException e) {\n            throw new RetrieveException(e);\n        } catch (final SProcessDefinitionException e) {\n            throw new ProcessActivationException(e);\n        } catch (final SProcessInstanceCreationException e) {\n            if (e.getRetryAfter() != -1L) {\n                throw new ProcessExecutionException(e, e.getRetryAfter());\n            } else {\n                throw new ProcessExecutionException(e);\n            }\n        }\n    }\n\n    // For commands\n    public ProcessInstance start(final List<ConnectorDefinitionWithInputValues> connectorsWithInput)\n            throws SProcessInstanceCreationException,\n            SBonitaReadException, SProcessDefinitionException, SContractViolationException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ProcessExecutor processExecutor = serviceAccessor.getProcessExecutor();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n\n        final SProcessDefinition sProcessDefinition = processDefinitionService\n                .getProcessDefinitionIfIsEnabled(processDefinitionId);\n        final Map<String, Object> operationContext = getContext();\n        final long starterSubstituteUserId = SessionInfos.getUserIdFromSession();\n        final long starterUserId = getStarterUserId(starterSubstituteUserId);\n\n        Supplier<ProcessInstanceMDC> processInstanceMDC = () -> new ProcessInstanceMDC(0, Optional.of(starterUserId),\n                Optional.of(starterSubstituteUserId),\n                sProcessDefinition.getId(), 0);\n        CheckedCallable4<ProcessInstance, SProcessInstanceCreationException, SBonitaReadException, SProcessDefinitionException, SContractViolationException> call = () -> {\n            try {\n                final List<SOperation> sOperations = ModelConvertor.convertOperations(operations);\n                final SProcessInstance startedSProcessInstance = processExecutor.start(starterUserId,\n                        starterSubstituteUserId, sOperations,\n                        operationContext, connectorsWithInput,\n                        new FlowNodeSelector(sProcessDefinition, filter), processContractInputs);\n                logProcessInstanceStartedAndAddComment(sProcessDefinition, starterUserId, starterSubstituteUserId,\n                        startedSProcessInstance);\n                return ModelConvertor.toProcessInstance(sProcessDefinition, startedSProcessInstance);\n            } catch (final SProcessInstanceCreationException e) {\n                e.setProcessDefinitionIdOnContext(sProcessDefinition.getId());\n                e.setProcessDefinitionNameOnContext(sProcessDefinition.getName());\n                e.setProcessDefinitionVersionOnContext(sProcessDefinition.getVersion());\n                throw e;\n            }\n        };\n\n        return MDCHelper.tryWithMDC(processInstanceMDC, call);\n    }\n\n    protected long getStarterUserId(final long starterSubstituteUserId) {\n        if (userId == 0) {\n            return starterSubstituteUserId;\n        }\n        return userId;\n    }\n\n    protected Map<String, Object> getContext() {\n        if (context != null) {\n            return new HashMap<>(context);\n        }\n        return Collections.emptyMap();\n    }\n\n    private void logProcessInstanceStartedAndAddComment(final SProcessDefinition sProcessDefinition,\n            final long starterId, final long starterSubstituteId,\n            final SProcessInstance sProcessInstance) {\n        final StringBuilder stb = new StringBuilder();\n        stb.append(\"The user <\");\n        stb.append(SessionInfos.getUserNameFromSession());\n        if (starterId != starterSubstituteId) {\n            stb.append(\"> acting as delegate of user with id <\");\n            stb.append(starterId);\n        }\n        stb.append(\"> has started the process instance <\");\n        stb.append(sProcessInstance.getId());\n        stb.append(\"> of process <\");\n        stb.append(sProcessDefinition.getName());\n        stb.append(\"> in version <\");\n        stb.append(sProcessDefinition.getVersion());\n        stb.append(\"> and id <\");\n        stb.append(sProcessDefinition.getId());\n        stb.append(\">\");\n\n        log.info(stb.toString());\n\n        addSystemCommentOnProcessInstanceWhenStartingProcessFor(sProcessInstance, starterId, starterSubstituteId);\n    }\n\n    protected void addSystemCommentOnProcessInstanceWhenStartingProcessFor(final SProcessInstance sProcessInstance,\n            final long starterId,\n            final long starterSubstituteId) {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final SCommentService commentService = serviceAccessor.getCommentService();\n\n        if (starterId != starterSubstituteId) {\n            final IdentityService identityService = serviceAccessor.getIdentityService();\n            try {\n                final SUser starter = identityService.getUser(starterId);\n                commentService.addSystemComment(sProcessInstance.getId(),\n                        \"The user \" + SessionInfos.getUserNameFromSession()\n                                + \" acting as delegate of the user \" + starter.getUserName()\n                                + \" has started the case.\");\n            } catch (final SBonitaException e) {\n                log.error(\"Error when adding a comment on the process instance.\", e);\n            }\n        }\n    }\n\n    protected ServiceAccessor getServiceAccessor() {\n        try {\n            return ServiceAccessorSingleton.getInstance();\n        } catch (final Exception e) {\n            throw new BonitaRuntimeException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/ProfileAPIImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.ProfileAPI;\nimport org.bonitasoft.engine.api.impl.profile.ProfileAPIDelegate;\nimport org.bonitasoft.engine.api.utils.VisibleForTesting;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.identity.MemberType;\nimport org.bonitasoft.engine.profile.Profile;\nimport org.bonitasoft.engine.profile.ProfileCriterion;\nimport org.bonitasoft.engine.profile.ProfileMember;\nimport org.bonitasoft.engine.profile.ProfileMemberCreator;\nimport org.bonitasoft.engine.profile.ProfileNotFoundException;\nimport org.bonitasoft.engine.profile.model.SProfileMember;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Celine Souchet\n * @author Matthieu Chaffotte\n */\n@AvailableInMaintenanceMode\npublic class ProfileAPIImpl implements ProfileAPI {\n\n    @VisibleForTesting\n    ProfileAPIDelegate getProfileAPIDelegate() {\n        return ProfileAPIDelegate.getInstance();\n    }\n\n    @Override\n    public Profile getProfile(final long id) throws ProfileNotFoundException {\n        return getProfileAPIDelegate().getProfile(id);\n    }\n\n    @Override\n    public List<Profile> getProfilesForUser(final long userId, final int startIndex, final int maxResults,\n            final ProfileCriterion criterion) {\n        return getProfileAPIDelegate().getProfilesForUser(userId, startIndex, maxResults, criterion);\n    }\n\n    @Override\n    public SearchResult<Profile> searchProfiles(final SearchOptions options) throws SearchException {\n        return getProfileAPIDelegate().searchProfiles(options);\n    }\n\n    @Override\n    public Map<Long, Long> getNumberOfProfileMembers(final List<Long> profileIds) {\n        return getProfileAPIDelegate().getNumberOfProfileMembers(profileIds);\n    }\n\n    @Override\n    public SearchResult<ProfileMember> searchProfileMembers(final String memberType, final SearchOptions options)\n            throws SearchException {\n        return getProfileAPIDelegate().searchProfileMembers(memberType, options);\n    }\n\n    @Override\n    public ProfileMember createProfileMember(final Long profileId, final Long userId, final Long groupId,\n            final Long roleId) throws CreationException {\n        return getProfileAPIDelegate().createProfileMember(profileId, userId, groupId, roleId);\n\n    }\n\n    @Override\n    public ProfileMember createProfileMember(final ProfileMemberCreator creator)\n            throws CreationException, AlreadyExistsException {\n        if (creator == null) {\n            throw new CreationException(\"Unable to create a profile member with a null creator!\");\n        }\n\n        final SProfileMember sProfileMember = ModelConvertor.constructSProfileMember(creator);\n        return createProfileMember(sProfileMember.getProfileId(), sProfileMember.getUserId(),\n                sProfileMember.getGroupId(), sProfileMember.getRoleId());\n    }\n\n    public MemberType getMemberType(final Long userId, final Long groupId, final Long roleId) throws CreationException {\n        return getProfileAPIDelegate().getMemberType(userId, groupId, roleId);\n    }\n\n    @Override\n    public void deleteProfileMember(final Long profileMemberId) throws DeletionException {\n        getProfileAPIDelegate().deleteProfileMember(profileMemberId);\n    }\n\n    @VisibleForTesting\n    public long getUserIdFromSession() {\n        return SessionInfos.getUserIdFromSession();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/SCustomUserInfoValueAPI.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.identity.CustomUserInfoValue;\nimport org.bonitasoft.engine.identity.CustomUserInfoValueUpdater;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.SIdentityException;\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoValue;\nimport org.bonitasoft.engine.identity.model.builder.SCustomUserInfoValueUpdateBuilderFactory;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\nimport org.bonitasoft.engine.search.identity.SearchCustomUserInfoValues;\n\n/**\n * @author Vincent Elcrin\n */\npublic class SCustomUserInfoValueAPI {\n\n    private final SCustomUserInfoValueUpdateBuilderFactory updaterFactory;\n\n    private final IdentityService service;\n\n    public SCustomUserInfoValueAPI(IdentityService service,\n            SCustomUserInfoValueUpdateBuilderFactory updaterFactory) {\n        this.updaterFactory = updaterFactory;\n        this.service = service;\n    }\n\n    public SearchResult<CustomUserInfoValue> search(SearchEntityDescriptor descriptor, final SearchOptions options)\n            throws SBonitaException {\n        SearchCustomUserInfoValues search = new SearchCustomUserInfoValues(service, descriptor, options);\n        search.execute();\n        return search.getResult();\n    }\n\n    public SCustomUserInfoValue update(SCustomUserInfoValue value, CustomUserInfoValueUpdater updater)\n            throws SIdentityException {\n        assertNoNull(\"Cannot update a value based on null parameters\", value, updater);\n        service.updateCustomUserInfoValue(value, updaterFactory.createNewInstance()\n                .updateValue(updater.getValue())\n                .done());\n        return service.getCustomUserInfoValue(value.getId());\n    }\n\n    public SCustomUserInfoValue set(long definitionId, long userId, String value) throws SIdentityException,\n            SBonitaReadException {\n\n        SCustomUserInfoValue customUserInfoValue = searchValue(definitionId, userId);\n        if (value == null || value.isEmpty()) {\n            delete(customUserInfoValue);\n            return createValue(definitionId, userId, value);\n        }\n        if (customUserInfoValue != null) {\n            return update(customUserInfoValue, new CustomUserInfoValueUpdater(value));\n        }\n        return create(definitionId, userId, value);\n    }\n\n    public SCustomUserInfoValue create(long definitionId, long userId, String value) throws SIdentityException {\n        return service.createCustomUserInfoValue(createValue(definitionId, userId, value));\n    }\n\n    public void delete(SCustomUserInfoValue value) throws SIdentityException {\n        if (value != null) {\n            service.deleteCustomUserInfoValue(value);\n        }\n    }\n\n    private SCustomUserInfoValue createValue(long definitionId, long userId, String value) {\n        return SCustomUserInfoValue.builder()\n                .definitionId(definitionId)\n                .userId(userId)\n                .value(value).build();\n    }\n\n    private SCustomUserInfoValue searchValue(long definitionId, long userId) throws SBonitaReadException {\n        List<SCustomUserInfoValue> result = service.getCustomUserInfoValueOfUserAndDefinitions(userId,\n                Arrays.asList(definitionId));\n        if (result.size() == 0) {\n            return null;\n        }\n        return result.get(0);\n    }\n\n    private void assertNoNull(String message, Object... objects) {\n        for (Object object : objects) {\n            if (object == null) {\n                throw new IllegalArgumentException(message);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/ServerAPIFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport org.bonitasoft.engine.api.internal.ServerAPI;\nimport org.bonitasoft.engine.home.BonitaHomeServer;\n\n/**\n * This class load the server api implementation configured using the property `serverApi`.\n * All server-side code goes through this class to retrieve the server api implementation.\n */\npublic class ServerAPIFactory {\n\n    private static final String SERVER_API_CLASS_NOT_FOUND = \"Cannot load class %s. Platform property 'serverApi' may not be set.\";\n\n    private static ServerAPIFactory INSTANCE = new ServerAPIFactory(BonitaHomeServer.getInstance());\n\n    private final BonitaHomeServer bonitaHomeServer;\n    private Class<?> serverApiClass;\n\n    ServerAPIFactory(final BonitaHomeServer bonitaHomeServer) {\n        this.bonitaHomeServer = bonitaHomeServer;\n    }\n\n    public static ServerAPI getServerAPI() {\n        return ServerAPIFactory.getInstance().getServerAPIImplementation();\n    }\n\n    public static ServerAPI getServerAPI(final boolean cleanSession) {\n        return ServerAPIFactory.getInstance().getServerAPIImplementation(cleanSession);\n    }\n\n    ServerAPI getServerAPIImplementation() {\n        Class<?> aClass = getServerAPIClass();\n        try {\n            return (ServerAPI) aClass.newInstance();\n        } catch (Exception e) {\n            throw new ExceptionInInitializerError(String.format(SERVER_API_CLASS_NOT_FOUND, aClass.getName()));\n        }\n    }\n\n    private Class<?> getServerAPIClass() {\n        if (serverApiClass == null) {\n            String serverAPIClassName = bonitaHomeServer.getServerAPIImplementation();\n            try {\n                serverApiClass = Class.forName(serverAPIClassName);\n            } catch (Exception e) {\n                throw new ExceptionInInitializerError(String.format(SERVER_API_CLASS_NOT_FOUND, serverAPIClassName));\n            }\n        }\n        return serverApiClass;\n    }\n\n    private ServerAPI getServerAPIImplementation(final boolean cleanSession) {\n        Class<?> serverApiClass = getServerAPIClass();\n        try {\n            return (ServerAPI) serverApiClass.getConstructor(boolean.class).newInstance(cleanSession);\n        } catch (Exception e) {\n            throw new ExceptionInInitializerError(String.format(SERVER_API_CLASS_NOT_FOUND, serverApiClass.getName()));\n        }\n    }\n\n    public static ServerAPIFactory getInstance() {\n        return INSTANCE;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/ServerAPIImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport java.io.IOException;\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.UndeclaredThrowableException;\nimport java.text.MessageFormat;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.TimeUnit;\nimport java.util.function.Supplier;\n\nimport org.bonitasoft.engine.api.MaintenanceAPI;\nimport org.bonitasoft.engine.api.NoSessionRequired;\nimport org.bonitasoft.engine.api.PlatformAPI;\nimport org.bonitasoft.engine.api.impl.transaction.CustomTransactions;\nimport org.bonitasoft.engine.api.internal.ServerAPI;\nimport org.bonitasoft.engine.api.internal.ServerWrappedException;\nimport org.bonitasoft.engine.classloader.BonitaClassLoader;\nimport org.bonitasoft.engine.classloader.ClassLoaderIdentifier;\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.classloader.SClassLoaderException;\nimport org.bonitasoft.engine.commons.ClassReflector;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.login.LoginService;\nimport org.bonitasoft.engine.core.platform.login.PlatformLoginService;\nimport org.bonitasoft.engine.exception.BonitaContextException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.BonitaHomeConfigurationException;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\nimport org.bonitasoft.engine.exception.TenantStatusException;\nimport org.bonitasoft.engine.exception.UnavailableLockException;\nimport org.bonitasoft.engine.lock.BonitaLock;\nimport org.bonitasoft.engine.lock.LockService;\nimport org.bonitasoft.engine.maintenance.MaintenanceDetails;\nimport org.bonitasoft.engine.mdc.MDCHelper;\nimport org.bonitasoft.engine.mdc.UserIdMDC;\nimport org.bonitasoft.engine.platform.NodeNotStartedException;\nimport org.bonitasoft.engine.platform.PlatformService;\nimport org.bonitasoft.engine.platform.PlatformState;\nimport org.bonitasoft.engine.platform.session.PlatformSessionService;\nimport org.bonitasoft.engine.platform.session.SSessionException;\nimport org.bonitasoft.engine.scheduler.SchedulerService;\nimport org.bonitasoft.engine.scheduler.exception.SSchedulerException;\nimport org.bonitasoft.engine.service.APIAccessResolver;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.impl.ServiceAccessorFactory;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.bonitasoft.engine.session.PlatformSession;\nimport org.bonitasoft.engine.session.Session;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * This class is in charge of calling APIs while adding behavior:\n * <ul>\n * <li>It sets the classloader to the one from platform or tenant</li>\n * <li>When the method is <b>NOT</b> annotated with {@link NoSessionRequired}, it verifies that the given session is\n * valid, is on the right scope (tenant or platform), and renew it</li>\n * <li>When the method is <b>NOT</b> annotated with {@link CustomTransactions}, it opens a transaction</li>\n * <li>When the method is deprecated, it print a warning</li>\n * <li>When the method or class is <b>NOT</b> annotated with {@link AvailableInMaintenanceMode}, it verifies the\n * maintenance mode\n * is disabled</li>\n * <li>When the method or class is annotated with {@link AvailableInMaintenanceMode} and onlyAvailableInMaintenanceMode\n * is set\n * to true, it verifies the maintenance mode is enabled</li>\n * <li>When the method is <b>NOT</b> annotated with {@link AvailableOnStoppedNode}, it verifies the platform is\n * running</li>\n * </ul>\n */\npublic class ServerAPIImpl implements ServerAPI {\n\n    private static final Logger logger = LoggerFactory.getLogger(ServerAPIImpl.class);\n\n    private static final String SESSION = \"session\";\n\n    @Serial\n    private static final long serialVersionUID = -161775388604256321L;\n\n    private final APIAccessResolver accessResolver;\n\n    private final boolean cleanSession;\n\n    public ServerAPIImpl(boolean cleanSession) {\n        try {\n            this.cleanSession = cleanSession;\n            accessResolver = getServiceAccessorFactoryInstance().createAPIAccessResolver();\n        } catch (final Exception e) {\n            throw new BonitaRuntimeException(e);\n        }\n    }\n\n    public ServerAPIImpl() {\n        this(true);\n    }\n\n    /**\n     * For Test Mock usage\n     */\n    public ServerAPIImpl(APIAccessResolver accessResolver) {\n        this.cleanSession = true;\n        this.accessResolver = accessResolver;\n    }\n\n    ServiceAccessorFactory getServiceAccessorFactoryInstance() {\n        return ServiceAccessorFactory.getInstance();\n    }\n\n    @Override\n    public Object invokeMethod(final Map<String, Serializable> options, final String apiInterfaceName,\n            final String methodName,\n            final List<String> classNameParameters, final Object[] parametersValues) throws ServerWrappedException {\n        logger.trace(\"Starting Server API call {} {}\", apiInterfaceName, methodName);\n\n        final ClassLoader baseClassLoader = Thread.currentThread().getContextClassLoader();\n        SessionAccessor sessionAccessor = null;\n        Session session = null;\n        try {\n            final Object api = accessResolver.getAPIImplementation(Class.forName(apiInterfaceName));\n            try {\n                session = (Session) options.get(SESSION);\n                sessionAccessor = beforeInvokeMethod(session, api);\n                return invokeAPI(api, apiInterfaceName, methodName, classNameParameters, parametersValues, session);\n            } catch (final ServerAPIRuntimeException e) {\n                throw e.getCause();\n            }\n        } catch (final BonitaRuntimeException | BonitaException bre) {\n            fillGlobalContextForException(session, bre);\n            throw createServerWrappedException(bre);\n        } catch (final UndeclaredThrowableException ute) {\n            throw createServerWrappedException(ute);\n        } catch (final Throwable cause) {\n            // Note: at this point the transaction outcome is uncertain — it may have\n            // committed before the failure occurred. The client will receive an error\n            // regardless.\n            logger.error(\"Unexpected failure during API call {}.{}() (session={})\",\n                    apiInterfaceName, methodName, session, cause);\n            final BonitaRuntimeException throwableToWrap = wrapThrowable(cause);\n            fillGlobalContextForException(session, throwableToWrap);\n            throw createServerWrappedException(throwableToWrap);\n        } finally {\n            cleanSessionIfNeeded(sessionAccessor);\n            // Reset the original classloader when not an equivalent BonitaClassLoader\n            // We do not reset the the classloader if the original classloader and the current classloader are BonitaClassloader having the same name\n            // e.g. for the cleanAndUninstallBusinessDataModel API method that reset the current classloader\n            if (shouldResetClassloader(baseClassLoader, Thread.currentThread().getContextClassLoader())) {\n                Thread.currentThread().setContextClassLoader(baseClassLoader);\n            }\n            logger.trace(\"End Server API call {} {}\", apiInterfaceName, methodName);\n        }\n    }\n\n    private boolean shouldResetClassloader(ClassLoader baseClassLoader, ClassLoader currentClassloader) {\n        if (currentClassloader instanceof BonitaClassLoader bonitaClassLoader\n                && baseClassLoader instanceof BonitaClassLoader bonitaBaseClassLoader) {\n            // Classloader name is different, reset it\n            return !Objects.equals(bonitaClassLoader.getName(), bonitaBaseClassLoader.getName());\n        } else {\n            return true;\n        }\n    }\n\n    protected BonitaRuntimeException wrapThrowable(final Throwable cause) {\n        return new BonitaRuntimeException(cause);\n    }\n\n    private ServerWrappedException createServerWrappedException(final Throwable throwableToWrap) {\n        return new ServerWrappedException(throwableToWrap);\n    }\n\n    private void fillGlobalContextForException(final Session session, final BonitaContextException be) {\n        fillUserNameContextForException(session, be);\n    }\n\n    private void fillUserNameContextForException(final Session session, final BonitaContextException be) {\n        if (session != null) {\n            final String userName = session.getUserName();\n            if (userName != null) {\n                be.setUserName(userName);\n            }\n        }\n    }\n\n    private void cleanSessionIfNeeded(SessionAccessor sessionAccessor) {\n        if (cleanSession) {\n            // clean session id\n            if (sessionAccessor != null) {\n                sessionAccessor.deleteSessionId();\n            }\n        }\n    }\n\n    private SessionAccessor beforeInvokeMethod(final Session session, final Object api)\n            throws BonitaHomeNotSetException, BonitaHomeConfigurationException, IOException, SBonitaException,\n            ReflectiveOperationException {\n        SessionAccessor sessionAccessor = null;\n\n        final ServiceAccessorFactory serviceAccessorFactory = getServiceAccessorFactoryInstance();\n        final ServiceAccessor serviceAccessor = serviceAccessorFactory.createServiceAccessor();\n\n        ClassLoader serverClassLoader = null;\n        if (session != null) {\n            final SessionType sessionType = getSessionType(session);\n            sessionAccessor = serviceAccessorFactory.createSessionAccessor();\n            serverClassLoader = switch (sessionType) {\n                case PLATFORM -> beforeInvokeMethodForPlatformSession(sessionAccessor, serviceAccessor,\n                        session);\n                case API -> beforeInvokeMethodForAPISession(sessionAccessor, serviceAccessor,\n                        session);\n            };\n        } else if (needSession(api)) {\n            throw new InvalidSessionException(\"Session is null!\");\n        }\n        if (serverClassLoader != null) {\n            Thread.currentThread().setContextClassLoader(serverClassLoader);\n        }\n        return sessionAccessor;\n    }\n\n    private boolean needSession(Object api) {\n        //require a session if \"NoSessionRequired\" is not present\n        Class<?>[] interfaces = api.getClass().getInterfaces();\n        for (Class<?> anInterface : interfaces) {\n            if (anInterface.isAnnotationPresent(NoSessionRequired.class)) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    private ClassLoader beforeInvokeMethodForAPISession(SessionAccessor sessionAccessor,\n            ServiceAccessor serviceAccessor, Session session) throws SBonitaException {\n        checkTenantSession(serviceAccessor, session);\n        SessionService sessionService = serviceAccessor.getSessionService();\n        sessionService.renewSession(session.getId());\n        sessionAccessor.setSessionId(session.getId());\n        return getTenantClassLoader(serviceAccessor, session);\n    }\n\n    private ClassLoader beforeInvokeMethodForPlatformSession(final SessionAccessor sessionAccessor,\n            final ServiceAccessor serviceAccessor,\n            final Session session) throws SSessionException, SClassLoaderException {\n        final PlatformSessionService platformSessionService = serviceAccessor.getPlatformSessionService();\n        final PlatformLoginService loginService = serviceAccessor.getPlatformLoginService();\n\n        if (!loginService.isValid(session.getId())) {\n            throw new InvalidSessionException(\"Invalid session\");\n        }\n        platformSessionService.renewSession(session.getId());\n        sessionAccessor.setSessionId(session.getId());\n        return getPlatformClassLoader(serviceAccessor);\n    }\n\n    private SessionType getSessionType(final Session session) {\n        SessionType sessionType = null;\n        if (session instanceof PlatformSession) {\n            sessionType = SessionType.PLATFORM;\n        } else if (session instanceof APISession) {\n            sessionType = SessionType.API;\n        }\n        return sessionType;\n    }\n\n    private Object invokeAPI(Object api, String apiInterfaceName, final String methodName,\n            final List<String> classNameParameters, final Object[] parametersValues, final Session session)\n            throws Throwable {\n        var userId = Optional.ofNullable(session).map(Session::getUserId).filter(l -> l >= 0L);\n        Supplier<UserIdMDC> withAuthenticatedUser = UserIdMDC.supplierFromOptionalId(userId);\n\n        return MDCHelper.tryWithMDC(withAuthenticatedUser, () -> {\n\n            final Class<?>[] parameterTypes = getParameterTypes(classNameParameters);\n\n            final Method method = ClassReflector.getMethod(api.getClass(), methodName, parameterTypes);\n            // first, check if a lock is needed before opening any transaction\n            // and get the key which defines the functional scope\n            Optional<String> lockKey = Optional.ofNullable(method.getAnnotation(WithLock.class)).map(WithLock::key);\n            // try and acquire a lock with this scope if necessary\n            Supplier<String> failureMessage = () -> MessageFormat.format(\n                    \"Operation ''{0}.{1}'' requires exclusive access. Another operation is already launched with the same ''{2}'' access scope. You may try again after the other operation has finished.\",\n                    apiInterfaceName, methodName, lockKey.orElse(\"\"));\n            try (AutoCloseable ignored = withEventualLock(lockKey, failureMessage)) {\n                // No session required means that there is no transaction\n                if (method.isAnnotationPresent(CustomTransactions.class)\n                        || Class.forName(apiInterfaceName).isAnnotationPresent(NoSessionRequired.class)) {\n                    return invokeAPIOutsideTransaction(parametersValues, api, method, apiInterfaceName, session);\n                } else {\n                    return invokeAPIInTransaction(parametersValues, api, method, session, apiInterfaceName);\n                }\n            }\n        });\n    }\n\n    /**\n     * Eventually get a functional lock auto-closeable resource.\n     *\n     * @param lockKey the functional key for the lock scope or an empty\n     *        optional when no lock is necessary\n     * @param failureMessage builds the failure message when lock is already taken\n     * @return the auto-closeable resource or a stub ineffective resource when\n     *         lockKey is empty\n     * @throws UnavailableLockException error with built message when\n     *         lock is already taken.\n     */\n    private AutoCloseable withEventualLock(final Optional<String> lockKey, Supplier<String> failureMessage)\n            throws Throwable {\n        if (lockKey.isPresent()) {\n            // try and acquire a lock with this scope\n            LockService lockService = getServiceAccessorFactoryInstance().createServiceAccessor().getLockService();\n            BonitaLock lock = lockService.tryLock(1L, lockKey.get(), 1L, TimeUnit.MILLISECONDS);\n            if (lock == null) {\n                // timeout expired, we should not pursue this way\n                throw new UnavailableLockException(failureMessage.get());\n            }\n            return () -> lockService.unlock(lock);\n        } else {\n            // ineffective resource\n            return () -> {\n            };\n        }\n    }\n\n    private Object invokeAPIOutsideTransaction(Object[] parametersValues, Object apiImpl, Method method,\n            String apiInterfaceName, Session session)\n            throws Throwable {\n        checkMethodAccessibility(apiImpl, apiInterfaceName, method, session, /* Not in transaction */false);\n        return invokeAPI(method, apiImpl, parametersValues);\n    }\n\n    protected void checkMethodAccessibility(final Object apiImpl, final String apiInterfaceName, final Method method,\n            final Session session,\n            boolean isAlreadyInTransaction) {\n        final MethodAvailability methodAvailability = getMethodAvailability(apiImpl, method);\n        if (methodAvailability.isDeprecated) {\n            logger.warn(\"The API method {}.{} is deprecated. It will be deleted in a future release. \" +\n                    \"Please plan to update your code to use the replacement method instead. Check the Javadoc for more details.\",\n                    apiInterfaceName, method.getName());\n        }\n        if (!methodAvailability.isAvailableWhenPlatformIsStopped && !isNodeStarted()) {\n            logger.error(\n                    \"Node not started. Method '{}. {}' cannot be called until node has been started (PlatformAPI.startNode()). Exact class: {}\",\n                    apiInterfaceName, method.getName(), method.getDeclaringClass().getName());\n            throw new NodeNotStartedException();\n        }\n        // we don't check maintenance mode at platform level and when there is no session\n        // when there is no session means that we are trying to log in, in this case it is the LoginApiExt that check if the user is the technical user\n        // For tenant level method call:\n        if (!(session instanceof APISession)) {\n            return;\n        }\n\n        if (methodAvailability.isAvailableInMaintenanceMode\n                && methodAvailability.isAvailableWhenMaintenanceModeIsDisabled) {\n            //method can be called when maintenance is enabled or disabled.\n            return;\n        }\n        boolean isMaintenanceModeEnabled = isMaintenanceModeEnabled(session, isAlreadyInTransaction);\n        if (isMaintenanceModeEnabled && !methodAvailability.isAvailableInMaintenanceMode) {\n            throw new TenantStatusException(\n                    MessageFormat.format(\"Unable to call API method {0}.{1}, Maintenance mode is enabled.\",\n                            apiInterfaceName, method.getName()));\n        }\n        if (!isMaintenanceModeEnabled && !methodAvailability.isAvailableWhenMaintenanceModeIsDisabled) {\n            throw new TenantStatusException(MessageFormat.format(\n                    \"Unable to call API method {0}.{1}, Maintenance mode is disabled and this method can only be called when Maintenance mode is enabled.\",\n                    apiInterfaceName, method.getName()));\n        }\n    }\n\n    private static class MethodAvailability {\n\n        boolean isDeprecated;\n        boolean isAvailableWhenMaintenanceModeIsDisabled = true;\n        boolean isAvailableInMaintenanceMode = true;\n        boolean isAvailableWhenPlatformIsStopped;\n    }\n\n    private MethodAvailability getMethodAvailability(Object apiInstance, Method method) {\n        AvailableInMaintenanceMode availableInMaintenanceMode = Optional\n                .ofNullable(method.getAnnotation(AvailableInMaintenanceMode.class))\n                .orElseGet(() -> apiInstance.getClass().getAnnotation(AvailableInMaintenanceMode.class));\n        AvailableOnStoppedNode availableOnStoppedNode = method.getAnnotation(AvailableOnStoppedNode.class);\n        MethodAvailability methodAvailability = new MethodAvailability();\n        // Deprecation\n        methodAvailability.isDeprecated = method.isAnnotationPresent(Deprecated.class);\n\n        // Maintenance mode\n        if (availableInMaintenanceMode == null) {\n            methodAvailability.isAvailableInMaintenanceMode = false;\n        } else if (availableInMaintenanceMode.onlyAvailableInMaintenanceMode()) {\n            methodAvailability.isAvailableWhenMaintenanceModeIsDisabled = false;\n        }\n        // Platform status\n        if (availableOnStoppedNode != null) {\n            methodAvailability.isAvailableWhenPlatformIsStopped = true;\n        }\n        return methodAvailability;\n    }\n\n    /**\n     * @param session the session to user\n     * @param isAlreadyInTransaction if the request is made in a transaction\n     * @return true if the maintenance mode is enabled, false otherwise\n     */\n    protected boolean isMaintenanceModeEnabled(final Session session, boolean isAlreadyInTransaction) {\n        try {\n            MaintenanceAPI maintenanceAPI = accessResolver\n                    .getAPIImplementation(MaintenanceAPI.class);\n            if (isAlreadyInTransaction) {\n                return MaintenanceDetails.State.ENABLED\n                        .equals(maintenanceAPI.getMaintenanceDetails().getMaintenanceState());\n            } else {\n                return selectUserTransactionService(session, getSessionType(session))\n                        .executeInTransaction(() -> MaintenanceDetails.State.ENABLED\n                                .equals(maintenanceAPI.getMaintenanceDetails().getMaintenanceState()));\n            }\n        } catch (final Throwable e) {\n            throw new BonitaRuntimeException(\"Cannot determine if the Maintenance mode is enabled\", e);\n        }\n    }\n\n    /**\n     * @return true if the node is started, false otherwise.\n     */\n    private boolean isNodeStarted() {\n        try {\n            return accessResolver.getAPIImplementation(PlatformAPI.class).isNodeStarted();\n        } catch (final Throwable e) {\n            return false;\n        }\n    }\n\n    protected Object invokeAPIInTransaction(final Object[] parametersValues, final Object apiImpl, final Method method,\n            final Session session,\n            final String apiInterfaceName) throws Throwable {\n        if (session == null) {\n            throw new BonitaRuntimeException(\"session is null\");\n        }\n        final UserTransactionService userTransactionService = selectUserTransactionService(session,\n                getSessionType(session));\n\n        return userTransactionService.executeInTransaction(() -> {\n            try {\n                checkMethodAccessibility(apiImpl, apiInterfaceName, method, session,\n                        /* Already in a transaction */true);\n                return invokeAPI(method, apiImpl, parametersValues);\n            } catch (final Throwable cause) {\n                throw new ServerAPIRuntimeException(cause);\n            }\n        });\n    }\n\n    UserTransactionService selectUserTransactionService(final Session session, final SessionType sessionType)\n            throws BonitaHomeNotSetException, IOException, BonitaHomeConfigurationException,\n            ReflectiveOperationException {\n        UserTransactionService transactionService;\n        final ServiceAccessorFactory serviceAccessorFactory = getServiceAccessorFactoryInstance();\n        final ServiceAccessor serviceAccessor = serviceAccessorFactory.createServiceAccessor();\n        transactionService = switch (sessionType) {\n            case PLATFORM -> serviceAccessor.getTransactionService();\n            case API -> serviceAccessor.getUserTransactionService();\n        };\n        return transactionService;\n    }\n\n    protected Object invokeAPI(final Method method, final Object apiImpl, final Object... parametersValues)\n            throws Throwable {\n        try {\n            return method.invoke(apiImpl, parametersValues);\n        } catch (final InvocationTargetException e) {\n            throw e.getCause();\n        }\n    }\n\n    private Class<?>[] getParameterTypes(final List<String> classNameParameters) throws ClassNotFoundException {\n        Class<?>[] parameterTypes = null;\n        if (classNameParameters != null && !classNameParameters.isEmpty()) {\n            parameterTypes = new Class<?>[classNameParameters.size()];\n            for (int i = 0; i < parameterTypes.length; i++) {\n                final String className = classNameParameters.get(i);\n                Class<?> classType;\n                if (\"int\".equals(className)) {\n                    classType = int.class;\n                } else if (\"long\".equals(className)) {\n                    classType = long.class;\n                } else if (\"boolean\".equals(className)) {\n                    classType = boolean.class;\n                } else {\n                    classType = Class.forName(className);\n                }\n                parameterTypes[i] = classType;\n            }\n        }\n        return parameterTypes;\n    }\n\n    private void checkTenantSession(final ServiceAccessor serviceAccessor, final Session session)\n            throws SSchedulerException {\n        final SchedulerService schedulerService = serviceAccessor.getSchedulerService();\n        if (!schedulerService.isStarted()) {\n            logger.debug(\"The scheduler is not started!\");\n        }\n        final APISession apiSession = (APISession) session;\n        final LoginService tenantLoginService = serviceAccessor.getLoginService();\n        if (!tenantLoginService.isValid(apiSession.getId())) {\n            throw new InvalidSessionException(\"Invalid session\");\n        }\n    }\n\n    private ClassLoader getTenantClassLoader(final ServiceAccessor serviceAccessor, final Session session)\n            throws SClassLoaderException {\n        final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService();\n        return classLoaderService.getClassLoader(ClassLoaderIdentifier.TENANT);\n    }\n\n    private ClassLoader getPlatformClassLoader(final ServiceAccessor serviceAccessor)\n            throws SClassLoaderException {\n        ClassLoader classLoader = null;\n        PlatformState state = serviceAccessor.getPlatformManager().getState();\n        if (state != PlatformState.STARTED) {\n            // We do not retrieve the platform classloader when the platform is not yet started\n            // It needs to have services to be started to retrieve it\n            // Returning null will cause the context classloader to be left untouched\n            logger.debug(\"Tried to retrieve platform classloader on a not started platform, state = {}\", state);\n            return null;\n        }\n        final PlatformService platformService = serviceAccessor.getPlatformService();\n        // get the platform to put it in cache if needed\n        if (!platformService.isPlatformCreated()) {\n            try {\n                serviceAccessor.getTransactionService().executeInTransaction((Callable<Void>) () -> {\n                    platformService.getPlatform();\n                    return null;\n                });\n            } catch (final Exception ignored) {\n                // do not throw exceptions: it's just in case the platform was not in cache\n            }\n        }\n        if (platformService.isPlatformCreated()) {\n            final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService();\n            classLoader = classLoaderService.getClassLoader(ClassLoaderIdentifier.GLOBAL);\n        }\n        return classLoader;\n    }\n\n    protected enum SessionType {\n        PLATFORM, API\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/ServerAPIRuntimeException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\n/**\n * @author Baptiste Mesta\n */\npublic class ServerAPIRuntimeException extends RuntimeException {\n\n    private static final long serialVersionUID = -5675131531953146131L;\n\n    public ServerAPIRuntimeException(final Throwable cause) {\n        super(cause);\n\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/SessionInfos.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\nimport org.bonitasoft.engine.service.impl.ServiceAccessorFactory;\nimport org.bonitasoft.engine.session.SSessionNotFoundException;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.session.model.SSession;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.bonitasoft.engine.sessionaccessor.SessionIdNotSetException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SessionInfos {\n\n    private final String username;\n\n    private final long userId;\n\n    public SessionInfos(final String username, final long userId) {\n        this.username = username;\n        this.userId = userId;\n    }\n\n    public long getUserId() {\n        return userId;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public static SessionInfos getSessionInfos() {\n        SSession session = getSession();\n        if (session != null) {\n            return new SessionInfos(session.getUserName(), session.getUserId());\n        }\n        return new SessionInfos(SessionService.SYSTEM, -1);\n    }\n\n    public static SSession getSession() {\n        SSession session;\n        try {\n            final long sessionId = getSessionAccessor().getSessionId();\n            session = getSessionService().getSession(sessionId);\n        } catch (final SessionIdNotSetException | SSessionNotFoundException e) {\n            return null;\n        }\n        return session;\n    }\n\n    private static SessionService getSessionService() {\n        try {\n            return ServiceAccessorFactory.getInstance().createServiceAccessor().getSessionService();\n        } catch (final Exception e) {\n            throw new BonitaRuntimeException(e);\n        }\n    }\n\n    private static SessionAccessor getSessionAccessor() {\n        try {\n            return ServiceAccessorFactory.getInstance().createSessionAccessor();\n        } catch (final Exception e) {\n            throw new BonitaRuntimeException(e);\n        }\n    }\n\n    public static long getUserIdFromSession() {\n        return getSessionService().getLoggedUserFromSession(getSessionAccessor());\n    }\n\n    public static String getUserNameFromSession() {\n        SSession session = getSession();\n        if (session == null) {\n            // system\n            return SessionService.SYSTEM;\n        }\n        return session.getUserName();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/StarterThread.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.platform.PlatformService;\nimport org.bonitasoft.engine.platform.model.SPlatform;\nimport org.bonitasoft.engine.tenant.restart.TenantRestartHandler;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Thread start when the engine is ready.\n * Its purpose is to start elements to be recovered from the previous run of the engine.\n *\n * @author Baptiste Mesta\n */\npublic class StarterThread extends Thread {\n\n    private static final Logger logger = LoggerFactory.getLogger(StarterThread.class);\n\n    private final List<TenantRestartHandler> tenantRestartHandlers;\n    private final UserTransactionService transactionService;\n    private final PlatformService platformService;\n\n    public StarterThread(UserTransactionService transactionService, PlatformService platformService,\n            List<TenantRestartHandler> tenantRestartHandlers) {\n        super(\"Starter Thread created\");\n        this.tenantRestartHandlers = tenantRestartHandlers;\n        this.transactionService = transactionService;\n        this.platformService = platformService;\n    }\n\n    @Override\n    public void run() {\n        SPlatform platform = getPlatform();\n        logger.info(\"Restarting elements of platform that were not finished at the last shutdown\");\n        if (platform.isMaintenanceEnabled()) {\n            logger.warn(\"Unable to restart elements of platform because platform is {}\", platform.getPausedStatus());\n            return;\n        }\n        executeHandlers();\n    }\n\n    private void executeHandlers() {\n        for (final TenantRestartHandler restartHandler : tenantRestartHandlers) {\n            try {\n                logger.info(\"Executing Restart Handler {}\", restartHandler.getClass().getName());\n                restartHandler.afterServicesStart();\n            } catch (Exception e) {\n                logger.error(\"The Restart Handler {} failed\", restartHandler.getClass().getName(), e);\n            }\n        }\n    }\n\n    SPlatform getPlatform() {\n        try {\n            return transactionService.executeInTransaction(platformService::getPlatform);\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/TaskInvolvementDelegate.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceNotFoundException;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.exceptions.SExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityReadException;\nimport org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAUserTaskInstanceBuilderFactory;\nimport org.bonitasoft.engine.exception.RetrieveException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.execution.state.FlowNodeStateManager;\nimport org.bonitasoft.engine.persistence.*;\nimport org.bonitasoft.engine.search.AbstractHumanTaskInstanceSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.ServiceAccessorSingleton;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class TaskInvolvementDelegate {\n\n    private static final int BATCH_SIZE = 100;\n\n    protected ServiceAccessor getServiceAccessor() {\n        return ServiceAccessorSingleton.getInstance();\n    }\n\n    public boolean isExecutorOfArchivedTaskOfProcess(long userId, Long rootProcessInstanceId)\n            throws SBonitaReadException {\n        final ActivityInstanceService activityInstanceService = getServiceAccessor().getActivityInstanceService();\n\n        QueryOptions archivedQueryOptions = buildArchivedTasksQueryOptions(rootProcessInstanceId);\n        List<SAHumanTaskInstance> sArchivedHumanTasks = activityInstanceService\n                .searchArchivedTasks(archivedQueryOptions);\n        while (!sArchivedHumanTasks.isEmpty()) {\n            for (final SAHumanTaskInstance sArchivedHumanTask : sArchivedHumanTasks) {\n                if (userId == sArchivedHumanTask.getExecutedBy()) {\n                    return true;\n                }\n            }\n            archivedQueryOptions = QueryOptions.getNextPage(archivedQueryOptions);\n            sArchivedHumanTasks = activityInstanceService.searchArchivedTasks(archivedQueryOptions);\n        }\n        return false;\n    }\n\n    private static QueryOptions buildArchivedTasksQueryOptions(final long rootProcessInstanceId) {\n        final SAUserTaskInstanceBuilderFactory archUserTaskKeyFactory = BuilderFactory\n                .get(SAUserTaskInstanceBuilderFactory.class);\n        final String humanTaskIdKey = archUserTaskKeyFactory.getIdKey();\n        final String parentProcessInstanceKey = archUserTaskKeyFactory.getRootProcessInstanceKey();\n        final List<OrderByOption> archivedOrderByOptions = Collections\n                .singletonList(new OrderByOption(SAHumanTaskInstance.class, humanTaskIdKey, OrderByType.ASC));\n        final List<FilterOption> archivedFilterOptions = Collections\n                .singletonList(\n                        new FilterOption(SAHumanTaskInstance.class, parentProcessInstanceKey, rootProcessInstanceId));\n        return new QueryOptions(0, BATCH_SIZE, archivedOrderByOptions, archivedFilterOptions, null);\n    }\n\n    public boolean isInvolvedInHumanTaskInstance(long userId, long humanTaskInstanceId)\n            throws ActivityInstanceNotFoundException {\n        final ActivityInstanceService activityInstanceService = getServiceAccessor().getActivityInstanceService();\n        try {\n            long assigneeId;\n            final SHumanTaskInstance humanTaskInstance = activityInstanceService\n                    .getHumanTaskInstance(humanTaskInstanceId);\n            assigneeId = humanTaskInstance.getAssigneeId();\n            if (assigneeId > 0) {\n                //check if the user is the assigned user\n                return userId == assigneeId;\n            } else {\n                //if the task is not assigned check if the user is mapped to the actor of the task\n                return activityInstanceService.isTaskPendingForUser(humanTaskInstanceId, userId);\n            }\n        } catch (SActivityInstanceNotFoundException e) {\n            throw new ActivityInstanceNotFoundException(humanTaskInstanceId);\n        } catch (SBonitaReadException | SActivityReadException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    public boolean hasUserPendingOrAssignedTasks(long userId, Long processInstanceId) throws SExecutionException {\n        final ActivityInstanceService activityInstanceService = getServiceAccessor().getActivityInstanceService();\n        // is user assigned or has pending tasks on this process instance:\n        final QueryOptions queryOptions = new QueryOptions(0, 1, Collections.EMPTY_LIST,\n                List.of(new FilterOption(SHumanTaskInstance.class,\n                        \"logicalGroup2\", processInstanceId)),\n                null);\n        try {\n            return activityInstanceService.getNumberOfPendingOrAssignedTasks(userId, queryOptions) > 0;\n        } catch (SBonitaReadException e) {\n            throw new SExecutionException(e);\n        }\n    }\n\n    public SearchResult<HumanTaskInstance> searchPendingTasksManagedBy(final long managerUserId,\n            final SearchOptions searchOptions) throws SearchException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager();\n        return AbstractHumanTaskInstanceSearchEntity.searchHumanTaskInstance(\n                searchEntitiesDescriptor.getSearchHumanTaskInstanceDescriptor(),\n                searchOptions,\n                flowNodeStateManager,\n                (queryOptions) -> activityInstanceService.searchNumberOfPendingTasksManagedBy(managerUserId,\n                        queryOptions),\n                (queryOptions) -> activityInstanceService.searchPendingTasksManagedBy(managerUserId, queryOptions))\n                .search();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/TenantAdministrationAPIImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport java.io.IOException;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.api.TenantAdministrationAPI;\nimport org.bonitasoft.engine.api.impl.transaction.CustomTransactions;\nimport org.bonitasoft.engine.business.data.BusinessDataModelRepository;\nimport org.bonitasoft.engine.business.data.BusinessDataRepositoryDeploymentException;\nimport org.bonitasoft.engine.business.data.BusinessDataRepositoryException;\nimport org.bonitasoft.engine.business.data.InvalidBusinessDataModelException;\nimport org.bonitasoft.engine.business.data.SBusinessDataRepositoryDeploymentException;\nimport org.bonitasoft.engine.business.data.SBusinessDataRepositoryException;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.exception.BonitaHomeConfigurationException;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\nimport org.bonitasoft.engine.exception.RetrieveException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.resources.STenantResourceLight;\nimport org.bonitasoft.engine.resources.TenantResourcesService;\nimport org.bonitasoft.engine.service.ModelConvertor;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.impl.ServiceAccessorFactory;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.bonitasoft.engine.tenant.TenantResource;\nimport org.bonitasoft.engine.tenant.TenantResourceType;\nimport org.bonitasoft.engine.tenant.TenantStateManager;\n\n/**\n * @author Matthieu Chaffotte\n * @author Baptiste Mesta\n */\n\n@Slf4j\npublic class TenantAdministrationAPIImpl implements TenantAdministrationAPI {\n\n    protected ServiceAccessor getServiceAccessorNoException() {\n        try {\n            return ServiceAccessorFactory.getInstance().createServiceAccessor();\n        } catch (final Exception e) {\n            throw new BonitaRuntimeException(e);\n        }\n    }\n\n    @Override\n    @AvailableInMaintenanceMode\n    public boolean isPaused() {\n        try {\n            return getServiceAccessorNoException().getPlatformService().getPlatform().isMaintenanceEnabled();\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(\"Unable to retrieve the tenant status\", e);\n        }\n    }\n\n    @Override\n    @AvailableInMaintenanceMode\n    @CustomTransactions\n    public void pause() throws UpdateException {\n        ServiceAccessor serviceAccessor = getServiceAccessorNoException();\n        try {\n            serviceAccessor.getTenantStateManager().pause();\n        } catch (Exception e) {\n            throw new UpdateException(e);\n        }\n    }\n\n    @Override\n    @AvailableInMaintenanceMode\n    @CustomTransactions\n    public void resume() throws UpdateException {\n        ServiceAccessor serviceAccessor = getServiceAccessorNoException();\n        try {\n            serviceAccessor.getTenantStateManager().resume();\n            serviceAccessor.getUserTransactionService().executeInTransaction(() -> {\n                resolveDependenciesForAllProcesses();\n                return null;\n            });\n        } catch (Exception e) {\n            throw new UpdateException(e);\n        }\n    }\n\n    private void resolveDependenciesForAllProcesses() {\n        getServiceAccessor().getBusinessArchiveArtifactsManager()\n                .resolveDependenciesForAllProcesses(getServiceAccessor());\n    }\n\n    @Override\n    @AvailableInMaintenanceMode\n    public TenantResource getBusinessDataModelResource() {\n        return getTenantResource(TenantResourceType.BDM);\n    }\n\n    protected TenantResource getTenantResource(TenantResourceType type) {\n        TenantResourcesService tenantResourcesService = getServiceAccessor().getTenantResourcesService();\n        org.bonitasoft.engine.resources.TenantResourceType resourceType = org.bonitasoft.engine.resources.TenantResourceType\n                .valueOf(type.name());\n        try {\n            STenantResourceLight tenantResource = tenantResourcesService.getSingleLightResource(resourceType);\n            return ModelConvertor.toTenantResource(tenantResource);\n        } catch (SBonitaReadException e) {\n            return TenantResource.NONE;\n        }\n    }\n\n    //visible for testing\n    @Override\n    @AvailableInMaintenanceMode\n    public String getBusinessDataModelVersion() throws BusinessDataRepositoryException {\n        try {\n            final BusinessDataModelRepository modelRepository = getServiceAccessor().getBusinessDataModelRepository();\n            return modelRepository.getInstalledBDMVersion();\n        } catch (final SBusinessDataRepositoryException e) {\n            throw new BusinessDataRepositoryException(e);\n        }\n    }\n\n    private String installBusinessDataModel(final byte[] zip)\n            throws InvalidBusinessDataModelException, BusinessDataRepositoryDeploymentException {\n        log.info(\"Starting the installation of the BDM.\");\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        final long userId;\n        try {\n            userId = getUserId();\n        } catch (IllegalStateException e) {\n            throw new BusinessDataRepositoryDeploymentException(\"Unable to determine user ID\");\n        }\n        try {\n            final BusinessDataModelRepository bdmRepository = serviceAccessor.getBusinessDataModelRepository();\n            TenantStateManager tenantStateManager = serviceAccessor.getTenantStateManager();\n            String bdm_version = tenantStateManager.executeManagementOperation(\"BDM Installation\",\n                    () -> bdmRepository.install(zip, userId));\n            log.info(\"Installation of the BDM completed.\");\n            return bdm_version;\n        } catch (final SBusinessDataRepositoryDeploymentException e) {\n            throw new BusinessDataRepositoryDeploymentException(e);\n        } catch (final InvalidBusinessDataModelException e) {\n            throw e;\n        } catch (final Exception e) {\n            throw new BonitaRuntimeException(e);\n        }\n    }\n\n    @Override\n    @AvailableInMaintenanceMode(onlyAvailableInMaintenanceMode = true)\n    @WithLock(key = UPDATE_BDM)\n    public void uninstallBusinessDataModel() throws BusinessDataRepositoryDeploymentException {\n        log.info(\"Uninstalling the currently deployed BDM\");\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        try {\n            final BusinessDataModelRepository bdmRepository = serviceAccessor.getBusinessDataModelRepository();\n            TenantStateManager tenantStateManager = serviceAccessor.getTenantStateManager();\n            tenantStateManager.executeManagementOperation(\"BDM Uninstallation\", () -> {\n                bdmRepository.uninstall();\n                return null;\n            });\n            log.info(\"BDM successfully uninstalled\");\n        } catch (final SBusinessDataRepositoryException sbdre) {\n            throw new BusinessDataRepositoryDeploymentException(sbdre);\n        } catch (final Exception e) {\n            throw new BonitaRuntimeException(e);\n        }\n    }\n\n    @Override\n    @AvailableInMaintenanceMode(onlyAvailableInMaintenanceMode = true)\n    @WithLock(key = UPDATE_BDM)\n    @Deprecated(since = \"9.0.0\")\n    public String updateBusinessDataModel(final byte[] zip)\n            throws BusinessDataRepositoryDeploymentException, InvalidBusinessDataModelException {\n        String bdmVersion;\n        try {\n            uninstallBusinessDataModel();\n            bdmVersion = installBusinessDataModel(zip);\n        } catch (Exception e) {\n            log.warn(\n                    \"Caught an error when installing/updating the BDM, the transaction will be reverted and the previous BDM restored.\");\n            throw e;\n        }\n        log.info(\"Update operation completed, the BDM was successfully updated\");\n        return bdmVersion;\n    }\n\n    @Override\n    @AvailableInMaintenanceMode(onlyAvailableInMaintenanceMode = true)\n    @WithLock(key = UPDATE_BDM)\n    public void cleanAndUninstallBusinessDataModel() throws BusinessDataRepositoryDeploymentException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        try {\n            final BusinessDataModelRepository bdmRepository = serviceAccessor.getBusinessDataModelRepository();\n            TenantStateManager tenantStateManager = serviceAccessor.getTenantStateManager();\n            tenantStateManager.executeManagementOperation(\"BDM Cleanup and uninstallation\", () -> {\n                bdmRepository.dropAndUninstall();\n                return null;\n            });\n        } catch (final SBusinessDataRepositoryException sbdre) {\n            throw new BusinessDataRepositoryDeploymentException(sbdre);\n        } catch (final Exception e) {\n            throw new BonitaRuntimeException(e);\n        }\n    }\n\n    @Override\n    @AvailableInMaintenanceMode\n    public byte[] getClientBDMZip() throws BusinessDataRepositoryException {\n        final BusinessDataModelRepository bdmRepository = getServiceAccessor().getBusinessDataModelRepository();\n        try {\n            return bdmRepository.getClientBDMZip();\n        } catch (final SBusinessDataRepositoryException e) {\n            throw new BusinessDataRepositoryException(e);\n        }\n    }\n\n    protected ServiceAccessor getServiceAccessor() {\n        try {\n            return ServiceAccessorFactory.getInstance().createServiceAccessor();\n        } catch (final Exception e) {\n            throw new BonitaRuntimeException(e);\n        }\n    }\n\n    private SessionAccessor getSessionAccessor() throws IOException, BonitaHomeConfigurationException,\n            BonitaHomeNotSetException, ReflectiveOperationException {\n        return ServiceAccessorFactory.getInstance().createSessionAccessor();\n    }\n\n    protected long getUserId() throws IllegalStateException {\n        try {\n            return getServiceAccessor().getSessionService().getSession(getSessionAccessor().getSessionId()).getUserId();\n        } catch (final Exception e) {\n            throw new BonitaRuntimeException(e.getMessage());\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/WithLock.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * Used to identify tenant-level API methods that require taking a functional\n * lock when they are called. When the lock with this key is already taken by\n * another call, the API method will fail immediately.\n * Used by the Bonita Engine server interceptor.\n */\n@Target({ ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface WithLock {\n\n    /** A unique key to identify the lock (non-null) */\n    String key();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/application/installer/ApplicationArchive.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.application.installer;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Optional;\n\nimport com.vdurmont.semver4j.Semver;\nimport lombok.*;\nimport lombok.extern.slf4j.Slf4j;\n\n@Slf4j\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ApplicationArchive implements AutoCloseable {\n\n    private String fileName;\n    private File organization;\n    private File bdm;\n    private List<File> processes = new ArrayList<>();\n    private List<File> restAPIExtensions = new ArrayList<>();\n    private List<File> pages = new ArrayList<>();\n    private List<File> layouts = new ArrayList<>();\n    private List<File> themes = new ArrayList<>();\n    private List<File> applications = new ArrayList<>();\n    private List<File> applicationIcons = new ArrayList<>();\n    private List<File> ignoredFiles = new ArrayList<>();\n\n    private File configurationFile;\n\n    private String version;\n\n    @Setter(AccessLevel.NONE)\n    @Getter(AccessLevel.NONE)\n    private Semver semver;\n\n    public ApplicationArchive addPage(File page) {\n        pages.add(page);\n        return this;\n    }\n\n    public ApplicationArchive addLayout(File layout) {\n        layouts.add(layout);\n        return this;\n    }\n\n    public ApplicationArchive addTheme(File theme) {\n        themes.add(theme);\n        return this;\n    }\n\n    public ApplicationArchive addRestAPIExtension(File extension) {\n        restAPIExtensions.add(extension);\n        return this;\n    }\n\n    public ApplicationArchive addApplication(File livingApplication) {\n        applications.add(livingApplication);\n        return this;\n    }\n\n    public ApplicationArchive addApplicationIcon(File icon) {\n        applicationIcons.add(icon);\n        return this;\n    }\n\n    public ApplicationArchive addProcess(File process) {\n        processes.add(process);\n        return this;\n    }\n\n    public ApplicationArchive addIgnoredFile(File file) {\n        ignoredFiles.add(file);\n        return this;\n    }\n\n    public ApplicationArchive setBdm(File bdm) {\n        this.bdm = bdm;\n        return this;\n    }\n\n    public Optional<File> getConfigurationFile() {\n        return Optional.ofNullable(configurationFile);\n    }\n\n    public void setVersion(String version) {\n        this.version = version;\n        if (version != null) {\n            this.semver = new Semver(version, Semver.SemverType.LOOSE);\n        }\n    }\n\n    public boolean hasVersionGreaterThan(String version) {\n        if (semver == null) {\n            return false;\n        }\n        return semver.isGreaterThan(version);\n    }\n\n    public boolean hasVersionEquivalentTo(String version) {\n        if (semver == null) {\n            return false;\n        }\n        return semver.isEquivalentTo(version);\n    }\n\n    public boolean hasVersion() {\n        return semver != null;\n    }\n\n    /**\n     * @return <code>true</code> if the application archive has no artifact\n     */\n    public boolean isEmpty() {\n        return organization == null &&\n                bdm == null &&\n                processes.isEmpty() &&\n                restAPIExtensions.isEmpty() &&\n                pages.isEmpty() &&\n                layouts.isEmpty() &&\n                themes.isEmpty() &&\n                applications.isEmpty();\n    }\n\n    @Override\n    public void close() throws Exception {\n        cleanPhysicalArtifacts();\n    }\n\n    protected void cleanPhysicalArtifacts() throws IOException {\n        if (organization != null) {\n            Files.deleteIfExists(organization.toPath());\n        }\n        if (bdm != null) {\n            Files.deleteIfExists(bdm.toPath());\n        }\n        deletePhysicalFilesFromList(processes);\n        deletePhysicalFilesFromList(restAPIExtensions);\n        deletePhysicalFilesFromList(pages);\n        deletePhysicalFilesFromList(layouts);\n        deletePhysicalFilesFromList(themes);\n        deletePhysicalFilesFromList(applications);\n        deletePhysicalFilesFromList(applicationIcons);\n        deletePhysicalFilesFromList(ignoredFiles);\n    }\n\n    protected void deletePhysicalFilesFromList(List<File> list) throws IOException {\n        for (File f : list) {\n            Files.deleteIfExists(f.toPath());\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/application/installer/ApplicationArchiveReader.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.application.installer;\n\nimport java.io.*;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.stream.Stream;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.api.impl.application.installer.detector.ArtifactTypeDetector;\nimport org.bonitasoft.engine.io.IOUtil;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Baptiste Mesta.\n */\n@Slf4j\n@Component\n@ConditionalOnSingleCandidate(ApplicationArchiveReader.class)\npublic class ApplicationArchiveReader {\n\n    private final ArtifactTypeDetector artifactTypeDetector;\n\n    public ApplicationArchiveReader(ArtifactTypeDetector artifactTypeDetector) {\n        this.artifactTypeDetector = artifactTypeDetector;\n    }\n\n    public ApplicationArchive read(byte[] applicationArchiveFile) throws IOException {\n        try (InputStream inputStream = new ByteArrayInputStream(applicationArchiveFile)) {\n            return read(inputStream);\n        }\n    }\n\n    public ApplicationArchive read(InputStream inputStream) throws IOException {\n        final ApplicationArchive applicationArchive = createNewApplicationArchive();\n        File destDir = IOUtil.createTempDirectory(Files.createTempDirectory(\"temp-custom-application\").toUri());\n        IOUtil.unzipToFolder(inputStream, destDir);\n        try (Stream<Path> walker = Files.walk(destDir.toPath())) {\n            walker.filter(p -> p.toFile().isFile()).forEach(path -> {\n                try {\n                    artifactTypeDetector.checkFileAndAddToArchive(path.toFile(), applicationArchive);\n                } catch (IOException e) {\n                    throw new RuntimeException(e);\n                }\n            });\n        }\n        return applicationArchive;\n    }\n\n    protected ApplicationArchive createNewApplicationArchive() {\n        return new ApplicationArchive();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/application/installer/ApplicationInstaller.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.application.installer;\n\nimport java.io.File;\n\nimport org.bonitasoft.engine.api.result.ExecutionResult;\nimport org.bonitasoft.engine.exception.ApplicationInstallationException;\n\npublic interface ApplicationInstaller {\n\n    void install(ApplicationArchive applicationArchive) throws ApplicationInstallationException;\n\n    void update(ApplicationArchive applicationArchive) throws ApplicationInstallationException;\n\n    void updateConfiguration(File configurationFileArchive, ExecutionResult executionResult) throws Exception;\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/application/installer/ApplicationInstallerImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.application.installer;\n\nimport static java.lang.String.format;\nimport static java.lang.System.lineSeparator;\nimport static java.util.stream.Collectors.joining;\nimport static org.apache.commons.lang3.StringUtils.isNotBlank;\nimport static org.bonitasoft.engine.api.result.Status.*;\nimport static org.bonitasoft.engine.api.result.StatusCode.*;\nimport static org.bonitasoft.engine.api.result.StatusContext.*;\nimport static org.bonitasoft.engine.bpm.process.ActivationState.DISABLED;\nimport static org.bonitasoft.engine.bpm.process.ConfigurationState.RESOLVED;\nimport static org.bonitasoft.engine.business.application.ApplicationImportPolicy.FAIL_ON_DUPLICATES;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.Serializable;\nimport java.net.URLConnection;\nimport java.nio.file.Files;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Properties;\nimport java.util.concurrent.Callable;\nimport java.util.stream.Collectors;\n\nimport javax.xml.bind.JAXBException;\n\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NonNull;\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.api.ImportError;\nimport org.bonitasoft.engine.api.ImportStatus;\nimport org.bonitasoft.engine.api.impl.ProcessDeploymentAPIDelegate;\nimport org.bonitasoft.engine.api.impl.ProcessManagementAPIImplDelegate;\nimport org.bonitasoft.engine.api.impl.organization.OrganizationAPIDelegate;\nimport org.bonitasoft.engine.api.impl.page.PageAPIDelegate;\nimport org.bonitasoft.engine.api.impl.resolver.BusinessArchiveArtifactsManager;\nimport org.bonitasoft.engine.api.result.ExecutionResult;\nimport org.bonitasoft.engine.api.result.Status;\nimport org.bonitasoft.engine.api.result.StatusCode;\nimport org.bonitasoft.engine.api.utils.VisibleForTesting;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveFactory;\nimport org.bonitasoft.engine.bpm.bar.InvalidBusinessArchiveFormatException;\nimport org.bonitasoft.engine.bpm.process.ActivationState;\nimport org.bonitasoft.engine.bpm.process.Problem;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ProcessDeployException;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessEnablementException;\nimport org.bonitasoft.engine.business.application.ApplicationImportPolicy;\nimport org.bonitasoft.engine.business.application.exporter.ApplicationNodeContainerConverter;\nimport org.bonitasoft.engine.business.application.importer.ApplicationImporter;\nimport org.bonitasoft.engine.business.application.importer.StrategySelector;\nimport org.bonitasoft.engine.business.application.xml.AbstractApplicationNode;\nimport org.bonitasoft.engine.business.data.BusinessDataModelRepository;\nimport org.bonitasoft.engine.business.data.BusinessDataRepositoryDeploymentException;\nimport org.bonitasoft.engine.business.data.InvalidBusinessDataModelException;\nimport org.bonitasoft.engine.business.data.SBusinessDataRepositoryDeploymentException;\nimport org.bonitasoft.engine.business.data.SBusinessDataRepositoryException;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.ApplicationInstallationException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.ImportException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.identity.ImportPolicy;\nimport org.bonitasoft.engine.identity.OrganizationImportException;\nimport org.bonitasoft.engine.io.FileOperations;\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.engine.page.PageCreator;\nimport org.bonitasoft.engine.page.PageSearchDescriptor;\nimport org.bonitasoft.engine.page.PageUpdater;\nimport org.bonitasoft.engine.platform.PlatformService;\nimport org.bonitasoft.engine.platform.exception.SPlatformUpdateException;\nimport org.bonitasoft.engine.platform.model.builder.SPlatformUpdateBuilder;\nimport org.bonitasoft.engine.platform.model.builder.impl.SPlatformUpdateBuilderImpl;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.service.InstallationFailedException;\nimport org.bonitasoft.engine.service.InstallationService;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.ServiceAccessorSingleton;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.session.model.SSession;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.bonitasoft.engine.tenant.TenantStateManager;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.bonitasoft.platform.exception.PlatformException;\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;\nimport org.springframework.stereotype.Component;\nimport org.xml.sax.SAXException;\n\n/**\n * Main entry point to deploy an {@link ApplicationArchive}.\n *\n * @author Baptiste Mesta.\n * @author Danila Mazour\n * @author Haroun El Alami\n */\n@Slf4j\n@Component\n@ConditionalOnSingleCandidate(ApplicationInstaller.class)\npublic class ApplicationInstallerImpl implements ApplicationInstaller {\n\n    private static final String PAGE_TOKEN_PROPERTY = \"name\";\n    private static final String PAGE_DISPLAY_NAME_PROPERTY = \"displayName\";\n    private static final String PAGE_DESCRIPTION_PROPERTY = \"description\";\n    private static final String PAGE_CONTENT_TYPE_PROPERTY = \"contentType\";\n\n    private final InstallationService installationService;\n    private final BusinessDataModelRepository bdmRepository;\n    private final UserTransactionService transactionService;\n    private final TenantStateManager tenantStateManager;\n    private final SessionAccessor sessionAccessor;\n    private final SessionService sessionService;\n    private final BusinessArchiveArtifactsManager businessArchiveArtifactsManager;\n    private final ApplicationImporter applicationImporter;\n    private final ApplicationNodeContainerConverter appXmlConverter = new ApplicationNodeContainerConverter();\n\n    public ApplicationInstallerImpl(InstallationService installationService,\n            @Qualifier(\"businessDataModelRepository\") BusinessDataModelRepository bdmRepository,\n            UserTransactionService transactionService, SessionAccessor sessionAccessor, SessionService sessionService,\n            TenantStateManager tenantStateManager,\n            @Qualifier(\"dependencyResolver\") BusinessArchiveArtifactsManager businessArchiveArtifactsManager,\n            ApplicationImporter applicationImporter) {\n        this.installationService = installationService;\n        this.bdmRepository = bdmRepository;\n        this.transactionService = transactionService;\n        this.sessionAccessor = sessionAccessor;\n        this.sessionService = sessionService;\n        this.tenantStateManager = tenantStateManager;\n        this.businessArchiveArtifactsManager = businessArchiveArtifactsManager;\n        this.applicationImporter = applicationImporter;\n    }\n\n    private PageAPIDelegate getPageAPIDelegate() {\n        return PageAPIDelegate.getInstance();\n    }\n\n    private OrganizationAPIDelegate getOrganizationImporter() {\n        return OrganizationAPIDelegate.getInstance();\n    }\n\n    @Override\n    public void install(ApplicationArchive applicationArchive) throws ApplicationInstallationException {\n        if (applicationArchive.isEmpty()) {\n            throw new ApplicationInstallationException(\"The Application Archive contains no valid artifact to install\");\n        }\n        final ExecutionResult executionResult = new ExecutionResult();\n        try {\n            final long startPoint = System.currentTimeMillis();\n            log.info(\"Starting Application Archive installation...\");\n            installBusinessDataModel(applicationArchive);\n            inSession(() -> inTransaction(() -> {\n                var newlyInstalledProcessIds = installArtifacts(applicationArchive, executionResult);\n                enableResolvedProcesses(newlyInstalledProcessIds, executionResult);\n                updateApplicationVersion(applicationArchive.getVersion());\n                return null;\n            }));\n            log.info(\"The Application Archive (version {}) has been installed successfully in {} ms.\",\n                    applicationArchive.getVersion(), (System.currentTimeMillis() - startPoint));\n        } catch (Exception e) {\n            throw new ApplicationInstallationException(\"The Application Archive install operation has been aborted\", e);\n        } finally {\n            logInstallationResult(executionResult);\n        }\n        if (executionResult.hasErrors()) {\n            throw new ApplicationInstallationException(\"The Application Archive install operation has been aborted\");\n        }\n    }\n\n    protected List<Long> installArtifacts(ApplicationArchive applicationArchive, ExecutionResult executionResult)\n            throws Exception {\n        installOrganization(applicationArchive, executionResult);\n        installOrUpdateRestApiExtensions(applicationArchive, executionResult);\n        installOrUpdatePages(applicationArchive, executionResult);\n        installOrUpdateLayouts(applicationArchive, executionResult);\n        installOrUpdateThemes(applicationArchive, executionResult);\n        installLivingApplications(applicationArchive, executionResult, FAIL_ON_DUPLICATES);\n        var installedProcessIds = installProcesses(applicationArchive, executionResult);\n        applicationArchive.getConfigurationFile().ifPresent(configFile -> installConfiguration(configFile,\n                executionResult));\n        return installedProcessIds;\n    }\n\n    @Override\n    public void update(ApplicationArchive applicationArchive) throws ApplicationInstallationException {\n        if (applicationArchive.isEmpty()) {\n            throw new ApplicationInstallationException(\"The Application Archive contains no valid artifact to install\");\n        }\n        final ExecutionResult executionResult = new ExecutionResult();\n        try {\n            final long startPoint = System.currentTimeMillis();\n            log.info(\"Starting Application Archive installation...\");\n            installBusinessDataModel(applicationArchive);\n            inSession(() -> inTransaction(() -> {\n                List<Long> newlyInstalledProcessIds = updateArtifacts(applicationArchive, executionResult);\n                disableOldProcesses(newlyInstalledProcessIds, executionResult);\n                enableResolvedProcesses(newlyInstalledProcessIds, executionResult);\n                updateApplicationVersion(applicationArchive.getVersion());\n                return null;\n            }));\n            log.info(\"The Application Archive has been installed successfully in {} ms.\",\n                    (System.currentTimeMillis() - startPoint));\n        } catch (Exception e) {\n            throw new ApplicationInstallationException(\"The Application Archive update operation has been aborted\", e);\n        } finally {\n            logInstallationResult(executionResult);\n        }\n        if (executionResult.hasErrors()) {\n            throw new ApplicationInstallationException(\"The Application Archive update operation has been aborted\");\n        }\n    }\n\n    @VisibleForTesting\n    public void resumeTenantInSession() throws Exception {\n        inSession(() -> {\n            try {\n                if (tenantStateManager.isPaused()) {\n                    tenantStateManager.resume();\n                    transactionService.executeInTransaction(() -> {\n                        businessArchiveArtifactsManager.resolveDependenciesForAllProcesses(getServiceAccessor());\n                        return null;\n                    });\n                }\n            } catch (Exception e) {\n                throw new UpdateException(e);\n            }\n            return null;\n        });\n    }\n\n    @VisibleForTesting\n    public void pauseTenantInSession() throws Exception {\n        inSession(() -> {\n            try {\n                if (!tenantStateManager.isPaused()) {\n                    tenantStateManager.pause();\n                }\n            } catch (Exception e) {\n                throw new UpdateException(e);\n            }\n            return null;\n        });\n    }\n\n    protected List<Long> updateArtifacts(ApplicationArchive applicationArchive, ExecutionResult executionResult)\n            throws Exception {\n        updateOrganization(applicationArchive, executionResult);\n\n        installOrUpdateRestApiExtensions(applicationArchive, executionResult);\n        installOrUpdatePages(applicationArchive, executionResult);\n        installOrUpdateLayouts(applicationArchive, executionResult);\n        installOrUpdateThemes(applicationArchive, executionResult);\n        installLivingApplications(applicationArchive, executionResult, ApplicationImportPolicy.REPLACE_DUPLICATES);\n\n        List<Long> newlyInstalledProcessIds = installProcesses(applicationArchive, executionResult);\n        applicationArchive.getConfigurationFile().ifPresent(configFile -> installConfiguration(configFile,\n                executionResult));\n        return newlyInstalledProcessIds;\n    }\n\n    @VisibleForTesting\n    public void updateApplicationVersion(String version) throws PlatformException {\n        try {\n            PlatformService platformService = getServiceAccessor().getPlatformService();\n            platformService.updatePlatform(getPlatformUpdateBuilder()\n                    .setApplicationVersion(version).done());\n        } catch (SPlatformUpdateException e) {\n            throw new PlatformException(\"Error when updating Platform application version\", e);\n        }\n    }\n\n    protected SPlatformUpdateBuilder getPlatformUpdateBuilder() {\n        return SPlatformUpdateBuilderImpl.builder()\n                .descriptor(new EntityUpdateDescriptor())\n                .build();\n    }\n\n    @VisibleForTesting\n    void disableProcess(long processDefinitionId) throws SBonitaException {\n        getProcessManagementAPIDelegate().disableProcess(processDefinitionId);\n    }\n\n    @VisibleForTesting\n    List<Long> getDeployedProcessIds() throws SearchException {\n        SearchOptionsBuilder optsBuilder = new SearchOptionsBuilder(0, Integer.MAX_VALUE);\n        return getProcessDeploymentAPIDelegate()\n                .searchProcessDeploymentInfos(optsBuilder.done()).getResult()\n                .stream().map(ProcessDeploymentInfo::getProcessId).collect(Collectors.toList());\n    }\n\n    @VisibleForTesting\n    Optional<Long> getDeployedProcessId(String name, String version) {\n        try {\n            return Optional.of(getProcessDeploymentAPIDelegate().getProcessDefinitionId(name, version));\n        } catch (ProcessDefinitionNotFoundException e) {\n            return Optional.empty();\n        }\n    }\n\n    public void disableOldProcesses(List<Long> installedProcessIds, ExecutionResult executionResult)\n            throws SearchException, SBonitaException, ProcessDefinitionNotFoundException {\n        List<Long> deployedProcessIds = getDeployedProcessIds();\n        // remove installed process ids\n        deployedProcessIds.removeAll(installedProcessIds);\n        // disable all processes\n        for (Long processId : deployedProcessIds) {\n            // get process Info\n            ProcessDeploymentInfo info = getProcessDeploymentInfo(processId);\n            if (info.getActivationState() == ActivationState.ENABLED) {\n                disableProcess(processId);\n                executionResult.addStatus(infoStatus(PROCESS_DEPLOYMENT_DISABLEMENT_OK,\n                        format(\"Process %s (%s) has been disabled successfully\",\n                                info.getDisplayName(), info.getVersion())));\n            }\n        }\n    }\n\n    @VisibleForTesting\n    ProcessDeploymentInfo getProcessDeploymentInfo(Long processId) throws ProcessDefinitionNotFoundException {\n        return getProcessDeploymentAPIDelegate().getProcessDeploymentInfo(processId);\n    }\n\n    public void enableResolvedProcesses(List<Long> processDefinitionIds, ExecutionResult executionResult)\n            throws ProcessDeployException {\n        Collection<ProcessDeploymentInfo> processDeploymentInfos = getProcessDeploymentAPIDelegate()\n                .getProcessDeploymentInfosFromIds(processDefinitionIds)\n                .values();\n\n        boolean atLeastOneBlockingProblem = false;\n        // for all deployed process\n        // if resolved and not already enabled,\n        // enable it => if exception, add error status\n        // if enablement ok, add info status Ok\n        // if not resolved, add error status and list resolution problems\n        // At the end, if at least one process disabled, throw Exception to cancel deployment and startup\n        for (ProcessDeploymentInfo info : processDeploymentInfos) {\n            if (info.getConfigurationState() == RESOLVED) {\n                if (info.getActivationState() == DISABLED) {\n                    try {\n                        getProcessDeploymentAPIDelegate().enableProcess(info.getProcessId());\n                    } catch (ProcessDefinitionNotFoundException | ProcessEnablementException e) {\n                        final Map<String, Serializable> context = new HashMap<>();\n                        context.put(PROCESS_NAME_KEY, info.getName());\n                        context.put(PROCESS_VERSION_KEY, info.getVersion());\n                        executionResult.addStatus(errorStatus(PROCESS_DEPLOYMENT_ENABLEMENT_KO,\n                                format(\"Process %s (%s) could not be enabled\", info.getName(), info.getVersion()),\n                                context));\n                        atLeastOneBlockingProblem = true;\n                        continue;\n                    }\n                }\n                executionResult.addStatus(infoStatus(PROCESS_DEPLOYMENT_ENABLEMENT_OK,\n                        format(\"Process %s (%s) has been enabled successfully\",\n                                info.getDisplayName(), info.getVersion())));\n            } else {\n                try {\n                    atLeastOneBlockingProblem = true;\n                    List<Problem> problems = getProcessDeploymentAPIDelegate()\n                            .getProcessResolutionProblems(info.getProcessId());\n                    String message = format(\n                            \"Process '%s' (%s) is unresolved. It cannot be enabled for now.\",\n                            info.getDisplayName(), info.getVersion());\n                    String description = message + lineSeparator()\n                            + problems.stream().map(Problem::getDescription)\n                                    .collect(joining(lineSeparator()));\n                    executionResult.addStatus(errorStatus(PROCESS_DEPLOYMENT_ENABLEMENT_KO, description));\n                } catch (ProcessDefinitionNotFoundException e) {\n                    executionResult\n                            .addStatus(errorStatus(PROCESS_DEPLOYMENT_ENABLEMENT_KO, \"Process definition not found\"));\n                }\n            }\n        }\n\n        if (atLeastOneBlockingProblem) {\n            throw new ProcessDeployException(\"At least one process failed to deploy / enable. Canceling installation.\");\n        }\n    }\n\n    @VisibleForTesting\n    List<String> importOrganization(File organization, ImportPolicy failOnDuplicates)\n            throws OrganizationImportException, IOException {\n        return getOrganizationImporter().importOrganizationWithWarnings(\n                Files.readString(organization.toPath()),\n                failOnDuplicates);\n    }\n\n    protected void installOrganization(ApplicationArchive applicationArchive, ExecutionResult executionResult)\n            throws Exception {\n        final List<String> warnings;\n        if (applicationArchive.getOrganization() == null) {\n            executionResult.addStatus(Status.infoStatus(ORGANIZATION_IMPORT_WARNING,\n                    \"No organization found. Use the technical user to configure the organization.\"));\n            return;\n        }\n        try {\n            warnings = importOrganization(applicationArchive.getOrganization(), ImportPolicy.FAIL_ON_DUPLICATES);\n        } catch (IOException e) {\n            throw new OrganizationImportException(e);\n        }\n        for (String warning : warnings) {\n            executionResult.addStatus(warningStatus(ORGANIZATION_IMPORT_WARNING, warning));\n        }\n    }\n\n    protected void updateOrganization(ApplicationArchive applicationArchive, ExecutionResult executionResult)\n            throws Exception {\n        final List<String> warnings;\n        if (applicationArchive.getOrganization() == null) {\n            log.info(\"There is no organization file in the archive. Ignoring the organization update step.\");\n            return;\n        }\n        try {\n            warnings = importOrganization(applicationArchive.getOrganization(), ImportPolicy.IGNORE_DUPLICATES);\n        } catch (IOException e) {\n            throw new OrganizationImportException(e);\n        }\n        for (String warning : warnings) {\n            executionResult.addStatus(warningStatus(ORGANIZATION_IMPORT_WARNING, warning));\n        }\n    }\n\n    protected void installBusinessDataModel(ApplicationArchive applicationArchive) throws Exception {\n        if (applicationArchive.getBdm() != null) {\n            var alreadyDeployed = sameBdmContentDeployed(applicationArchive.getBdm());\n            if (alreadyDeployed) {\n                log.info(\"Installed and current BDM are equivalent. No BDM update required.\");\n                return;\n            }\n            log.info(\"BDM must be installed or updated...\");\n            pauseTenantInSession();\n            final String bdmVersion = inSession(\n                    () -> inTransaction(() -> updateBusinessDataModel(applicationArchive)));\n            log.info(\"BDM successfully installed (version({})\", bdmVersion);\n            resumeTenantInSession();\n        }\n    }\n\n    boolean sameBdmContentDeployed(File bdmArchive) throws Exception {\n        return inSession(() -> inTransaction(() -> {\n            log.info(\"Comparing BDM to install with current BDM...\");\n            return bdmRepository\n                    .isDeployed(Files.readAllBytes(bdmArchive.toPath()));\n        }));\n    }\n\n    protected String updateBusinessDataModel(ApplicationArchive applicationArchive)\n            throws InvalidBusinessDataModelException, BusinessDataRepositoryDeploymentException {\n        String bdmVersion;\n        try {\n            uninstallBusinessDataModel();\n            bdmVersion = installBusinessDataModel(Files.readAllBytes(applicationArchive.getBdm().toPath()));\n        } catch (IOException e) {\n            log.warn(\"Cannot read the BDM file on disk\");\n            log.warn(\n                    \"Caught an error when installing/updating the BDM, the transaction will be reverted and the previous BDM restored.\");\n            throw new BusinessDataRepositoryDeploymentException(e);\n        } catch (Exception e) {\n            log.warn(\n                    \"Caught an error when installing/updating the BDM, the transaction will be reverted and the previous BDM restored.\");\n            throw e;\n        }\n        log.info(\"Update operation completed, the BDM was successfully updated\");\n        return bdmVersion;\n    }\n\n    protected void uninstallBusinessDataModel() throws BusinessDataRepositoryDeploymentException {\n        log.info(\"Uninstalling the currently deployed BDM\");\n        try {\n            tenantStateManager.executeManagementOperation(\"BDM Uninstallation\", () -> {\n                bdmRepository.uninstall();\n                return null;\n            });\n            log.info(\"BDM successfully uninstalled\");\n        } catch (final SBusinessDataRepositoryException sbdre) {\n            throw new BusinessDataRepositoryDeploymentException(sbdre);\n        } catch (final Exception e) {\n            throw new BonitaRuntimeException(e);\n        }\n    }\n\n    protected String installBusinessDataModel(final byte[] zip)\n            throws InvalidBusinessDataModelException, BusinessDataRepositoryDeploymentException {\n        log.info(\"Starting the installation of the BDM.\");\n        try {\n            String bdmVersion = tenantStateManager.executeManagementOperation(\"BDM Installation\",\n                    () -> bdmRepository.install(zip, SessionService.SYSTEM_ID));\n            log.info(\"Installation of the BDM completed.\");\n            return bdmVersion;\n        } catch (final SBusinessDataRepositoryDeploymentException e) {\n            throw new BusinessDataRepositoryDeploymentException(e);\n        } catch (final InvalidBusinessDataModelException e) {\n            throw e;\n        } catch (final Exception e) {\n            throw new BonitaRuntimeException(e);\n        }\n    }\n\n    protected void installLivingApplications(ApplicationArchive applicationArchive, ExecutionResult executionResult,\n            ApplicationImportPolicy policy)\n            throws AlreadyExistsException, ImportException, ApplicationInstallationException {\n        try {\n            boolean atLeastOneBlockingProblem = false;\n            for (File livingApplicationFile : applicationArchive.getApplications()) {\n                log.info(\"Installing Living Application from file '{}'\", livingApplicationFile.getName());\n                var appContainer = appXmlConverter\n                        .unmarshallFromXML(Files.readAllBytes(livingApplicationFile.toPath()));\n                for (var application : appContainer.getAllApplications()) {\n                    var status = importApplication(application,\n                            getIconContent(application, applicationArchive), policy);\n                    final Map<String, Serializable> context = new HashMap<>();\n                    context.put(LIVING_APPLICATION_TOKEN_KEY, status.getName());\n                    context.put(LIVING_APPLICATION_IMPORT_STATUS_KEY, status.getStatus());\n                    final List<ImportError> errors = status.getErrors();\n                    if (errors != null && !errors.isEmpty()) {\n                        errors.forEach(error -> {\n                            Status errorStatus = buildErrorStatus(error, livingApplicationFile.getName());\n                            executionResult.addStatus(errorStatus);\n                        });\n\n                        atLeastOneBlockingProblem = true;\n                        continue;\n                    }\n\n                    executionResult.addStatus(\n                            infoStatus(LIVING_APP_DEPLOYMENT,\n                                    format(\"Application '%s' has been %s\", status.getName(),\n                                            status.getStatus().name().toLowerCase()),\n                                    context));\n                }\n            }\n\n            if (atLeastOneBlockingProblem) {\n                throw new ApplicationInstallationException(\n                        \"At least one application failed to be installed. Canceling installation.\");\n            }\n        } catch (IOException | JAXBException | SAXException e) {\n            throw new ImportException(e);\n        }\n    }\n\n    private IconContent getIconContent(AbstractApplicationNode application, ApplicationArchive applicationArchive) {\n        var iconPath = application.getIconPath();\n        if (iconPath != null && !iconPath.isBlank()) {\n            var icon = applicationArchive.getApplicationIcons().stream()\n                    .filter(iconFile -> Objects.equals(iconPath, iconFile.getName()))\n                    .findFirst()\n                    .map(File::toPath)\n                    .orElse(null);\n            try {\n                if (icon != null) {\n                    log.info(\"Application icon {} found for {}\", icon.getFileName().toString(),\n                            application.getDisplayName());\n                    var bytes = Files.readAllBytes(icon);\n                    return IconContent.builder()\n                            .bytes(bytes)\n                            .mimeType(URLConnection.guessContentTypeFromName(icon.getFileName().toString()))\n                            .build();\n                }\n            } catch (IOException e) {\n                log.warn(\"Failed to read icon {}\", icon, e);\n            }\n        }\n        return IconContent.builder().build();\n    }\n\n    private Status buildErrorStatus(ImportError importError, @NonNull String applicationName) {\n        StatusCode code = null;\n        switch (importError.getType()) {\n            case PAGE:\n                code = LIVING_APP_REFERENCES_UNKNOWN_PAGE;\n                break;\n            case PROFILE:\n                code = LIVING_APP_REFERENCES_UNKNOWN_PROFILE;\n                break;\n            case APPLICATION_PAGE:\n                code = LIVING_APP_REFERENCES_UNKNOWN_APPLICATION_PAGE;\n                break;\n            case LAYOUT:\n                code = LIVING_APP_REFERENCES_UNKNOWN_LAYOUT;\n                break;\n            case THEME:\n                code = LIVING_APP_REFERENCES_UNKNOWN_THEME;\n                break;\n            default:\n                break;\n        }\n        final Map<String, Serializable> context = new HashMap<>();\n        context.put(LIVING_APPLICATION_TOKEN_KEY, applicationName);\n        context.put(LIVING_APPLICATION_INVALID_ELEMENT_NAME, importError.getName());\n        context.put(LIVING_APPLICATION_INVALID_ELEMENT_TYPE, importError.getType());\n        return errorStatus(\n                code,\n                String.format(\"Unknown %s named '%s'\", importError.getType().name(), importError.getName()),\n                context);\n    }\n\n    ImportStatus importApplication(final AbstractApplicationNode application, IconContent iconContent,\n            ApplicationImportPolicy policy)\n            throws ImportException, AlreadyExistsException {\n        return applicationImporter.importApplication(application, true, SessionService.SYSTEM_ID,\n                iconContent.getBytes(), iconContent.getMimeType(),\n                true,\n                new StrategySelector().selectStrategy(policy));\n    }\n\n    protected void installOrUpdatePages(ApplicationArchive applicationArchive, ExecutionResult executionResult)\n            throws IOException, BonitaException {\n        for (File pageFile : applicationArchive.getPages()) {\n            installUnitPage(pageFile, \"page\", executionResult);\n        }\n    }\n\n    protected void installOrUpdateLayouts(ApplicationArchive applicationArchive, ExecutionResult executionResult)\n            throws IOException, BonitaException {\n        for (File layoutFile : applicationArchive.getLayouts()) {\n            installUnitPage(layoutFile, \"layout\", executionResult);\n        }\n    }\n\n    protected void installOrUpdateThemes(ApplicationArchive applicationArchive, ExecutionResult executionResult)\n            throws IOException, BonitaException {\n        for (File pageFile : applicationArchive.getThemes()) {\n            installUnitPage(pageFile, \"theme\", executionResult);\n        }\n    }\n\n    protected void installOrUpdateRestApiExtensions(ApplicationArchive applicationArchive,\n            ExecutionResult executionResult)\n            throws IOException, BonitaException {\n        for (File pageFile : applicationArchive.getRestAPIExtensions()) {\n            installUnitPage(pageFile, \"REST API extension\", executionResult);\n        }\n    }\n\n    /**\n     * From the Engine perspective, all custom pages, layouts, themes, custom Rest APIs are of type <code>Page</code>\n     */\n    protected void installUnitPage(File pageFile, String precisePageType, ExecutionResult executionResult)\n            throws IOException, BonitaException {\n        var pageProperties = loadPageProperties(pageFile);\n        var pageToken = pageProperties.getProperty(PAGE_TOKEN_PROPERTY);\n        final Map<String, Serializable> context = new HashMap<>();\n        context.put(PAGE_NAME_KEY, pageToken);\n        Page existingPage = getPageIfExist(pageToken);\n        if (existingPage == null) {\n            final Page page = createPage(pageFile, pageProperties);\n            log.info(\"Creating new {} '{}'\", precisePageType, getPageName(page));\n            executionResult.addStatus(infoStatus(PAGE_DEPLOYMENT_CREATE_NEW,\n                    format(\"New %s '%s' has been installed\", precisePageType, getPageName(page)),\n                    context));\n        } else {\n            updatePageContent(pageFile, existingPage.getId());\n        }\n    }\n\n    @VisibleForTesting\n    void updatePageContent(File pageFile, long pageId) throws UpdateException, IOException, AlreadyExistsException {\n        getPageAPIDelegate().updatePageContent(pageId, Files.readAllBytes(pageFile.toPath()), SessionService.SYSTEM_ID);\n        // update content name\n        final PageUpdater pageUpdater = new PageUpdater();\n        pageUpdater.setContentName(pageFile.getName());\n        getPageAPIDelegate().updatePage(pageId, pageUpdater, SessionService.SYSTEM_ID);\n    }\n\n    @VisibleForTesting\n    Page getPageIfExist(String pageToken) throws SearchException {\n        List<Page> pageResearch = getPageAPIDelegate().searchPages(new SearchOptionsBuilder(0, 1)\n                .filter(PageSearchDescriptor.NAME, pageToken).done()).getResult();\n        if (!pageResearch.isEmpty()) {\n            return pageResearch.get(0);\n        }\n        return null;\n    }\n\n    Page createPage(File pageFile, Properties pageProperties) throws CreationException {\n        try {\n            var pageCreator = new PageCreator(pageProperties.getProperty(PAGE_TOKEN_PROPERTY), pageFile.getName())\n                    .setContentType(pageProperties.getProperty(PAGE_CONTENT_TYPE_PROPERTY))\n                    .setDisplayName(pageProperties.getProperty(PAGE_DISPLAY_NAME_PROPERTY))\n                    .setDescription(pageProperties.getProperty(PAGE_DESCRIPTION_PROPERTY));\n            return getPageAPIDelegate().createPage(pageCreator,\n                    Files.readAllBytes(pageFile.toPath()),\n                    SessionService.SYSTEM_ID);\n        } catch (IOException e) {\n            throw new CreationException(\"Failed to read custom page content\", e);\n        }\n    }\n\n    private String getPageName(Page page) {\n        return isNotBlank(page.getDisplayName()) ? page.getDisplayName() : page.getName();\n    }\n\n    private Properties loadPageProperties(File zipFile) throws IOException {\n        var properties = new Properties();\n        try (var pagePropertiesIs = new ByteArrayInputStream(\n                FileOperations.getFileFromZip(zipFile, \"page.properties\"))) {\n            properties.load(pagePropertiesIs);\n            return validatePageProperties(zipFile, properties);\n        }\n    }\n\n    private Properties validatePageProperties(File file, Properties properties) {\n        String name = properties.getProperty(PAGE_TOKEN_PROPERTY);\n        if (name == null || name.isEmpty()) {\n            throw new IllegalArgumentException(\n                    format(\"Invalid page %s, page.properties file do not contain mandatory '%s' attribute\",\n                            file.getName(), PAGE_TOKEN_PROPERTY));\n        }\n        String type = properties.getProperty(PAGE_CONTENT_TYPE_PROPERTY);\n        if (type == null || type.isEmpty()) {\n            throw new IllegalArgumentException(\n                    format(\"Invalid page %s, page.properties file do not contain mandatory '%s' attribute\",\n                            file.getName(), PAGE_CONTENT_TYPE_PROPERTY));\n        }\n        return properties;\n    }\n\n    protected List<Long> installProcesses(ApplicationArchive applicationArchive, ExecutionResult executionResult)\n            throws InvalidBusinessArchiveFormatException, IOException, ProcessDeployException {\n        List<Long> processDefinitionIds = new ArrayList<>();\n        for (File processFile : applicationArchive.getProcesses()) {\n            try (var is = new FileInputStream(processFile)) {\n                final BusinessArchive businessArchive = BusinessArchiveFactory\n                        .readBusinessArchive(is);\n                String name = businessArchive.getProcessDefinition().getName();\n                String version = businessArchive.getProcessDefinition().getVersion();\n\n                final Map<String, Serializable> context = new HashMap<>();\n                context.put(PROCESS_NAME_KEY, name);\n                context.put(PROCESS_VERSION_KEY, version);\n\n                Optional<Long> deployedProcessId = getDeployedProcessId(name, version);\n                if (deployedProcessId.isPresent()) {\n                    // skip install\n                    processDefinitionIds.add(deployedProcessId.get());\n                    executionResult.addStatus(infoStatus(PROCESS_DEPLOYMENT_SKIP_INSTALL,\n                            format(\"Process %s (%s) already exists in the database. Skipping installation.\", name,\n                                    version),\n                            context));\n                } else {\n                    processDefinitionIds.add(deployProcess(businessArchive, executionResult));\n                }\n            }\n        }\n        return processDefinitionIds;\n    }\n\n    /**\n     * Must be called in a transaction with active session\n     *\n     * @param configurationFileArchive\n     * @param executionResult\n     * @throws ApplicationInstallationException\n     */\n    void installConfiguration(File configurationFileArchive,\n            ExecutionResult executionResult)\n            throws ApplicationInstallationException {\n        try (var is = Files.newInputStream(configurationFileArchive.toPath())) {\n            log.info(\"Installing application configuration from file\");\n            installationService.install(null, is.readAllBytes());\n            executionResult.addStatus(Status.infoStatus(Status.okStatus().getCode(),\n                    \"Configuration file has been imported\"));\n        } catch (IOException | InstallationFailedException e) {\n            throw new ApplicationInstallationException(\"The Application Archive install operation has been aborted\", e);\n        }\n    }\n\n    /**\n     * Update configuration with the given bconf file\n     *\n     * @param configurationFileArchive A bconf file\n     * @param executionResult\n     * @throws Exception\n     */\n    @Override\n    public void updateConfiguration(File configurationFileArchive, ExecutionResult executionResult) throws Exception {\n        inSession(() -> inTransaction(() -> {\n            installConfiguration(configurationFileArchive, executionResult);\n            return null;\n        }));\n    }\n\n    protected Long deployProcess(BusinessArchive businessArchive, ExecutionResult executionResult)\n            throws ProcessDeployException {\n        final String processName = businessArchive.getProcessDefinition().getName();\n        final String processVersion = businessArchive.getProcessDefinition().getVersion();\n        long processDefinitionId;\n\n        final Map<String, Serializable> context = new HashMap<>();\n        context.put(PROCESS_NAME_KEY, processName);\n        context.put(PROCESS_VERSION_KEY, processVersion);\n        try {\n            // Let's try to deploy the process, even if it already exists:\n            processDefinitionId = getProcessDeploymentAPIDelegate().deploy(businessArchive).getId();\n            executionResult.addStatus(infoStatus(PROCESS_DEPLOYMENT_CREATE_NEW,\n                    format(\"New process %s (%s) has been installed successfully\", processName, processVersion),\n                    context));\n\n        } catch (AlreadyExistsException e) {\n            final String message = format(\"Process %s - %s already exists. Abandoning.\", processName, processVersion);\n            log.error(message);\n            throw new ProcessDeployException(message);\n        }\n        return processDefinitionId;\n    }\n\n    @VisibleForTesting\n    ProcessDeploymentAPIDelegate getProcessDeploymentAPIDelegate() {\n        return ProcessDeploymentAPIDelegate.getInstance();\n    }\n\n    @VisibleForTesting\n    ProcessManagementAPIImplDelegate getProcessManagementAPIDelegate() {\n        return ProcessManagementAPIImplDelegate.getInstance();\n    }\n\n    @VisibleForTesting\n    public <T> T inSession(Callable<T> callable) throws Exception {\n        final SSession session = sessionService.createSession(SessionService.SYSTEM);\n        final long sessionId = session.getId();\n        log.trace(\"New session created with id {}\", sessionId);\n        try {\n            sessionAccessor.setSessionId(sessionId);\n            return callable.call();\n        } finally {\n            sessionAccessor.deleteSessionId();\n        }\n    }\n\n    protected <T> T inTransaction(Callable<T> callable) throws ApplicationInstallationException {\n        try {\n            return transactionService.executeInTransaction(callable);\n        } catch (Exception e) {\n            throw new ApplicationInstallationException(\"Problem installing application\", e);\n        }\n    }\n\n    private ServiceAccessor getServiceAccessor() {\n        return ServiceAccessorSingleton.getInstance();\n    }\n\n    void logInstallationResult(ExecutionResult result) {\n        log.info(\"Result of the installation of the application:\");\n        for (Status s : result.getAllStatus()) {\n            var message = s.getContext() != null && !s.getContext().isEmpty()\n                    ? String.format(\"%s - %s - %s\", s.getCode(), s.getMessage(),\n                            s.getContext().toString())\n                    : String.format(\"%s - %s\", s.getCode(), s.getMessage());\n            switch (s.getLevel()) {\n                case ERROR:\n                    log.error(message);\n                    break;\n                case WARNING:\n                    log.warn(message);\n                    break;\n                case INFO:\n                case OK:\n                default:\n                    log.info(message);\n                    break;\n            }\n        }\n    }\n\n    @Builder\n    @Data\n    static class IconContent {\n\n        private byte[] bytes;\n        private String mimeType;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/application/installer/CustomOrDefaultApplicationInstaller.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.application.installer;\n\nimport static java.lang.String.format;\n\nimport java.io.IOException;\nimport java.io.UncheckedIOException;\nimport java.util.Optional;\n\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.api.result.ExecutionResult;\nimport org.bonitasoft.engine.api.utils.VisibleForTesting;\nimport org.bonitasoft.engine.business.application.importer.DefaultLivingApplicationImporter;\nimport org.bonitasoft.engine.business.application.importer.MandatoryLivingApplicationImporter;\nimport org.bonitasoft.engine.event.PlatformStartedEvent;\nimport org.bonitasoft.engine.exception.ApplicationInstallationException;\nimport org.bonitasoft.engine.platform.PlatformService;\nimport org.bonitasoft.engine.tenant.TenantServicesManager;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.event.EventListener;\nimport org.springframework.core.io.Resource;\nimport org.springframework.core.io.support.PathMatchingResourcePatternResolver;\nimport org.springframework.core.io.support.ResourcePatternResolver;\nimport org.springframework.stereotype.Component;\n\n/**\n * Install custom application if one exists under a specific folder. If none, install default provided applications.\n * <br>\n * This installer listens to the event {@link PlatformStartedEvent} to ensure the platform is started before launching\n * any application installation.\n */\n@Component\n@Slf4j\n@RequiredArgsConstructor\npublic class CustomOrDefaultApplicationInstaller {\n\n    public static final String CUSTOM_APPLICATION_DEFAULT_FOLDER = \"my-application\";\n\n    @Value(\"${bonita.runtime.custom-application.install-folder:\" + CUSTOM_APPLICATION_DEFAULT_FOLDER + \"}\")\n    @Getter\n    protected String applicationInstallFolder;\n\n    protected final ApplicationInstallerImpl applicationInstaller;\n\n    private final DefaultLivingApplicationImporter defaultLivingApplicationImporter;\n    private final MandatoryLivingApplicationImporter mandatoryLivingApplicationImporter;\n\n    private final TenantServicesManager tenantServicesManager;\n\n    protected final ResourcePatternResolver cpResourceResolver = new PathMatchingResourcePatternResolver(\n            CustomOrDefaultApplicationInstaller.class.getClassLoader());\n    private final ApplicationArchiveReader applicationArchiveReader;\n\n    private final PlatformService platformService;\n\n    @EventListener\n    public void autoDeployDetectedCustomApplication(PlatformStartedEvent event)\n            throws Exception {\n        final Resource customApplication = detectCustomApplication();\n        if (customApplication == null) {\n            if (isPlatformFirstInitialization()) {\n                // install default provided applications if custom application does not exist\n                log.info(\"No custom application detected under folder {}. Continuing with default Bonita startup.\",\n                        applicationInstallFolder);\n                installDefaultProvidedApplications();\n            }\n            return;\n        }\n\n        try (var applicationArchive = createApplicationArchive(customApplication)) {\n            if (!applicationArchive.hasVersion()) {\n                throw new ApplicationInstallationException(\n                        \"Application version not found. Abort installation.\");\n            }\n            log.info(\"Custom application detected with name '{}' under folder '{}'\",\n                    customApplication.getFilename(),\n                    applicationInstallFolder);\n            if (isPlatformFirstInitialization()) {\n                // install application if it exists and if it is the first init of the platform\n                log.info(\"Bonita now tries to install it automatically...\");\n                applicationInstaller.install(applicationArchive);\n            } else {\n                var currentVersion = getInstalledApplicationVersion();\n                log.info(\"Detected application version: '{}'; Current deployed version: '{}'\",\n                        applicationArchive.getVersion(),\n                        currentVersion);\n                if (applicationArchive.hasVersionGreaterThan(currentVersion)) {\n                    log.info(\"Updating the application...\");\n                    applicationInstaller.update(applicationArchive);\n                } else if (applicationArchive.hasVersionEquivalentTo(currentVersion)) {\n                    log.info(\"Updating process configuration only...\");\n                    findAndUpdateConfiguration();\n                } else {\n                    throw new ApplicationInstallationException(\"An application has been detected, but its newVersion \"\n                            + applicationArchive.getVersion() + \" is inferior to the one deployed: \" + currentVersion\n                            + \". Nothing will be updated, and the Bonita engine startup has been aborted.\");\n                }\n            }\n        }\n    }\n\n    String getInstalledApplicationVersion() throws Exception {\n        return applicationInstaller.inSession(\n                () -> applicationInstaller.inTransaction(() -> platformService.getPlatform().getApplicationVersion()));\n    }\n\n    boolean isPlatformFirstInitialization() {\n        return mandatoryLivingApplicationImporter.isFirstRun();\n    }\n\n    /**\n     * @return custom application resource if one is found, <code>null</code> if none\n     * @throws IOException if a resource cannot be resolved\n     * @throws ApplicationInstallationException if more than one application is detected\n     */\n    @VisibleForTesting\n    Resource detectCustomApplication() throws IOException, ApplicationInstallationException {\n        log.debug(\"Trying to detect custom application (.zip file from folder {})\", applicationInstallFolder);\n        return getResourceFromClasspath(getCustomAppResourcesFromClasspath(), \"application zip\");\n    }\n\n    protected static Resource getResourceFromClasspath(Resource[] resources, String type)\n            throws IOException, ApplicationInstallationException {\n        // loop over resources to find an existing, readable and not empty resource\n        Resource customRsource = null;\n        var nbZipApplication = 0;\n        if (resources != null) {\n            for (Resource resource : resources) {\n                if (resource.exists() && resource.isReadable() && resource.contentLength() > 0) {\n                    nbZipApplication++;\n                    customRsource = resource;\n                } else {\n                    log.warn(\"A custom resource file '{}' is found but it cannot be read. It will be ignored.\",\n                            resource.getFilename());\n                }\n            }\n            // if more than one application detected, stop execution by raising an exception\n            if (nbZipApplication > 1) {\n                throw new ApplicationInstallationException(\n                        format(\"More than one resource of type %s detected. Abort startup.\", type));\n            }\n        }\n        return customRsource;\n    }\n\n    @VisibleForTesting\n    Resource[] getCustomAppResourcesFromClasspath() throws IOException {\n        return cpResourceResolver\n                .getResources(\n                        ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + \"/\" + applicationInstallFolder + \"/*.zip\");\n    }\n\n    private void setConfigurationFile(ApplicationArchive applicationArchive)\n            throws IOException {\n        detectConfigurationFile().ifPresent(resource -> {\n            try {\n                log.debug(\"Found application configuration file \" + resource.getFilename());\n                applicationArchive.setConfigurationFile(resource.getFile());\n            } catch (IOException e) {\n                throw new UncheckedIOException(e);\n            }\n        });\n    }\n\n    protected ApplicationArchive createApplicationArchive(Resource customApplication) {\n        try (var applicationZipFileStream = customApplication.getInputStream()) {\n            var applicationArchive = applicationArchiveReader.read(applicationZipFileStream);\n            setConfigurationFile(applicationArchive);\n            return applicationArchive;\n        } catch (IOException e) {\n            throw new ApplicationInstallationException(\n                    String.format(\"Unable to read the %s application archive.\", customApplication.getFilename()), e);\n        }\n    }\n\n    @VisibleForTesting\n    void installDefaultProvidedApplications() throws ApplicationInstallationException {\n        try {\n            // default app importer requires a tenant session and to be executed inside a transaction\n            tenantServicesManager.inSessionTransaction(() -> {\n                defaultLivingApplicationImporter.execute();\n                return null;\n            });\n        } catch (Exception e) {\n            throw new ApplicationInstallationException(\"Unable to import default living applications\", e);\n        }\n    }\n\n    protected Optional<Resource> detectConfigurationFile() throws IOException {\n        log.debug(\"Trying to detect configuration file (.bconf file from folder {})\", applicationInstallFolder);\n        return Optional.ofNullable(\n                getResourceFromClasspath(getConfigurationFileResourcesFromClasspath(), \"configuration file .bconf\"));\n    }\n\n    @VisibleForTesting\n    Resource[] getConfigurationFileResourcesFromClasspath() throws IOException {\n        return cpResourceResolver\n                .getResources(\n                        ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + \"/\" + applicationInstallFolder + \"/*.bconf\");\n    }\n\n    protected void findAndUpdateConfiguration() throws ApplicationInstallationException, IOException {\n        final ExecutionResult executionResult = new ExecutionResult();\n        detectConfigurationFile().ifPresent(resource -> {\n            try {\n                log.debug(\"Found application configuration file \" + resource.getFilename());\n                applicationInstaller.updateConfiguration(resource.getFile(), executionResult);\n            } catch (Exception e) {\n                throw new ApplicationInstallationException(\"Failed to update configuration.\", e);\n            }\n        });\n        applicationInstaller.logInstallationResult(executionResult);\n        if (executionResult.hasErrors()) {\n            throw new ApplicationInstallationException(\"The Application Archive install operation has been aborted\");\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/application/installer/detector/ArtifactDetector.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.application.installer.detector;\n\nimport java.io.File;\nimport java.io.IOException;\n\npublic interface ArtifactDetector {\n\n    boolean isCompliant(File file) throws IOException;\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/application/installer/detector/ArtifactTypeDetector.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.application.installer.detector;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.util.Objects;\nimport java.util.Properties;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.api.impl.application.installer.ApplicationArchive;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;\nimport org.springframework.stereotype.Component;\n\n@Component\n@ConditionalOnSingleCandidate(ArtifactTypeDetector.class)\n@Slf4j\npublic class ArtifactTypeDetector {\n\n    private static final String VERSION_PROPERTY = \"version\";\n    private static final String APPLICATION_PROPERTIES_FILENAME = \"application.properties\";\n\n    private static final String REST_API_EXTENSION_CONTENT_TYPE = \"apiExtension\";\n\n    private final BdmDetector bdmDetector;\n\n    private final LivingApplicationDetector livingApplicationDetector;\n    private final OrganizationDetector organizationDetector;\n    private final CustomPageDetector customPageDetector;\n    private final ProcessDetector processDetector;\n    private final ThemeDetector themeDetector;\n    private final PageAndFormDetector pageAndFormDetector;\n    private final LayoutDetector layoutDetector;\n    private final IconDetector iconDetector;\n\n    public ArtifactTypeDetector(BdmDetector bdmDetector, LivingApplicationDetector livingApplicationDetector,\n            OrganizationDetector organizationDetector, CustomPageDetector customPageDetector,\n            ProcessDetector processDetector, ThemeDetector themeDetector, PageAndFormDetector pageAndFormDetector,\n            LayoutDetector layoutDetector, IconDetector iconDetector) {\n        this.bdmDetector = bdmDetector;\n        this.livingApplicationDetector = livingApplicationDetector;\n        this.organizationDetector = organizationDetector;\n        this.customPageDetector = customPageDetector;\n        this.processDetector = processDetector;\n        this.themeDetector = themeDetector;\n        this.pageAndFormDetector = pageAndFormDetector;\n        this.layoutDetector = layoutDetector;\n        this.iconDetector = iconDetector;\n    }\n\n    public boolean isApplication(File file) throws IOException {\n        return livingApplicationDetector.isCompliant(file);\n    }\n\n    public boolean isApplicationIcon(File file) throws IOException {\n        return iconDetector.isCompliant(file);\n    }\n\n    public boolean isOrganization(File file) throws IOException {\n        return organizationDetector.isCompliant(file);\n    }\n\n    public boolean isRestApiExtension(File file) throws IOException {\n        return customPageDetector.isCompliant(file, REST_API_EXTENSION_CONTENT_TYPE);\n    }\n\n    public boolean isPage(File file) throws IOException {\n        return pageAndFormDetector.isCompliant(file);\n    }\n\n    public boolean isLayout(File file) throws IOException {\n        return layoutDetector.isCompliant(file);\n    }\n\n    public boolean isTheme(File file) throws IOException {\n        return themeDetector.isCompliant(file);\n    }\n\n    public boolean isBdm(File file) {\n        return bdmDetector.isCompliant(file);\n    }\n\n    public boolean isProcess(File file) throws IOException {\n        return processDetector.isCompliant(file);\n    }\n\n    private boolean isApplicationProperties(File file) {\n        return file.isFile() && Objects.equals(APPLICATION_PROPERTIES_FILENAME, file.getName());\n    }\n\n    public void checkFileAndAddToArchive(File file, ApplicationArchive applicationArchive) throws IOException {\n        if (isApplication(file)) {\n            log.debug(\"Found application file: '{}'. \", file.getName());\n            applicationArchive.addApplication(file);\n        } else if (isApplicationIcon(file)) {\n            log.debug(\"Found icon file: '{}'. \", file.getName());\n            applicationArchive.addApplicationIcon(file);\n        } else if (isProcess(file)) {\n            log.debug(\"Found process file: '{}'. \", file.getName());\n            applicationArchive.addProcess(file);\n        } else if (isOrganization(file)) {\n            log.debug(\"Found organization file: '{}'. \", file.getName());\n            if (applicationArchive.getOrganization() != null) {\n                log.warn(\"An organization file has already been set. Using {}\",\n                        applicationArchive.getOrganization());\n                ignoreFile(file, applicationArchive);\n            } else {\n                applicationArchive.setOrganization(file);\n            }\n        } else if (isPage(file)) {\n            log.debug(\"Found page file: '{}'. \", file.getName());\n            applicationArchive.addPage(file);\n        } else if (isLayout(file)) {\n            log.debug(\"Found layout file: '{}'. \", file.getName());\n            applicationArchive.addLayout(file);\n        } else if (isTheme(file)) {\n            log.debug(\"Found theme file: '{}'. \", file.getName());\n            applicationArchive.addTheme(file);\n        } else if (isRestApiExtension(file)) {\n            log.debug(\"Found rest api extension file: '{}'. \", file.getName());\n            applicationArchive.addRestAPIExtension(file);\n        } else if (isBdm(file)) {\n            log.debug(\"Found business data model file: '{}'. \", file.getName());\n            applicationArchive.setBdm(file);\n        } else if (isApplicationProperties(file)) {\n            applicationArchive.setVersion(readVersion(file));\n        } else {\n            ignoreFile(file, applicationArchive);\n        }\n    }\n\n    String readVersion(File applicationPropertiesFile) throws IOException {\n        try (var is = Files.newInputStream(applicationPropertiesFile.toPath())) {\n            var properties = new Properties();\n            properties.load(is);\n            return properties.getProperty(VERSION_PROPERTY, null);\n        }\n    }\n\n    private void ignoreFile(File file, ApplicationArchive applicationArchive) {\n        log.debug(\"Ignoring file '{}'.\", file.getName());\n        applicationArchive.addIgnoredFile(file);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/application/installer/detector/BdmDetector.java",
    "content": "/**\n * Copyright (C) 2018 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.application.installer.detector;\n\nimport static java.nio.charset.StandardCharsets.UTF_8;\n\nimport java.io.File;\nimport java.io.IOException;\n\nimport org.bonitasoft.engine.io.FileOperations;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class BdmDetector implements ArtifactDetector {\n\n    @Override\n    public boolean isCompliant(File file) {\n        byte[] bdm;\n        try {\n            bdm = FileOperations.getFileFromZip(file, \"bom.xml\");\n        } catch (IOException e) {\n            return false;\n        }\n        // The bdm doesn't contain its namespace, so we can't perform the usual xml validation.\n        // So we look for the tag 'businessObjectModel', better than nothing:\n        return new String(bdm, UTF_8).contains(\"<businessObjectModel\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/application/installer/detector/CustomPageDetector.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.application.installer.detector;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Properties;\n\nimport org.bonitasoft.engine.io.FileOperations;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class CustomPageDetector {\n\n    private static final String CONTENT_TYPE_PROPERTY = \"contentType\";\n    private static final String PAGE_PROPERTIES_FILE = \"page.properties\";\n\n    public boolean isCompliant(File file, String... contentTypes) throws IOException {\n        return getPageProperties(file)\n                .map(properties -> properties.getProperty(CONTENT_TYPE_PROPERTY))\n                .filter(Objects::nonNull)\n                .filter(type -> Arrays.asList(contentTypes).contains(type))\n                .isPresent();\n    }\n\n    private Optional<Properties> getPageProperties(File file) {\n        try {\n            byte[] fileFromZip = FileOperations.getFileFromZip(file, PAGE_PROPERTIES_FILE);\n            Properties properties = new Properties();\n            properties.load(new ByteArrayInputStream(fileFromZip));\n            return Optional.of(properties);\n        } catch (IOException e) {\n            return Optional.empty();\n        }\n    }\n\n    public boolean isFilePresent(File file, String filename) {\n        try {\n            FileOperations.getFileFromZip(file, filename);\n            return true;\n        } catch (IOException e) {\n            return false;\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/application/installer/detector/IconDetector.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.application.installer.detector;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URLConnection;\n\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class IconDetector implements ArtifactDetector {\n\n    /**\n     * Determine the compliance using the guessed content-type of a given file name.\n     */\n    @Override\n    public boolean isCompliant(File file) throws IOException {\n        if (file.isFile()) {\n            var contentType = URLConnection.guessContentTypeFromName(file.getName());\n            return contentType != null && contentType.startsWith(\"image/\");\n        }\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/application/installer/detector/LayoutDetector.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.application.installer.detector;\n\nimport java.io.File;\nimport java.io.IOException;\n\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@Component\npublic class LayoutDetector extends CustomPageDetector {\n\n    private static final String INDEX_GROOVY = \"resources/Index.groovy\";\n    private static final String INDEX_HTML = \"resources/index.html\";\n\n    private static final String LAYOUT_CONTENT_TYPE = \"layout\";\n\n    public boolean isCompliant(File file) throws IOException {\n        return super.isCompliant(file, LAYOUT_CONTENT_TYPE)\n                && (isFilePresent(file, INDEX_HTML) || isFilePresent(file, INDEX_GROOVY));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/application/installer/detector/LivingApplicationDetector.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.application.installer.detector;\n\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class LivingApplicationDetector extends XmlDetector implements ArtifactDetector {\n\n    private static final String APPLICATION_NAMESPACE = \"http://documentation.bonitasoft.com/application-xml-schema/1.1\";\n\n    public LivingApplicationDetector() {\n        super(APPLICATION_NAMESPACE);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/application/installer/detector/OrganizationDetector.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.application.installer.detector;\n\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class OrganizationDetector extends XmlDetector implements ArtifactDetector {\n\n    private static final String ORGANIZATION_NAMESPACE = \"http://documentation.bonitasoft.com/organization-xml-schema\";\n\n    public OrganizationDetector() {\n        super(ORGANIZATION_NAMESPACE);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/application/installer/detector/PageAndFormDetector.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.application.installer.detector;\n\nimport java.io.File;\nimport java.io.IOException;\n\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@Component\npublic class PageAndFormDetector extends CustomPageDetector {\n\n    private static final String PAGE_CONTENT_TYPE = \"page\";\n    private static final String FORM_CONTENT_TYPE = \"form\";\n\n    private static final String INDEX_GROOVY = \"resources/Index.groovy\";\n    private static final String INDEX_HTML = \"resources/index.html\";\n\n    public boolean isCompliant(File file) throws IOException {\n        return super.isCompliant(file, PAGE_CONTENT_TYPE, FORM_CONTENT_TYPE)\n                && (isFilePresent(file, INDEX_HTML) || isFilePresent(file, INDEX_GROOVY));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/application/installer/detector/ProcessDetector.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.application.installer.detector;\n\nimport static org.bonitasoft.engine.io.FileOperations.isBarFile;\n\nimport java.io.File;\nimport java.io.IOException;\n\nimport org.bonitasoft.engine.io.FileOperations;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class ProcessDetector extends XmlDetector {\n\n    private static final String PROCESS_DEFINITION_NAMESPACE = \"http://www.bonitasoft.org/ns/process/client/\";\n    private static final String PROCESS_DESIGN_DEFINITION = \"process-design.xml\";\n\n    public ProcessDetector() {\n        super(PROCESS_DEFINITION_NAMESPACE);\n    }\n\n    public boolean isCompliant(File file) {\n        if (isBarFile(file.getName())) {\n            try {\n                return super.isCompliant(FileOperations.getFileFromZip(file, PROCESS_DESIGN_DEFINITION));\n            } catch (IOException ignored) {\n                return false;\n            }\n        }\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/application/installer/detector/ThemeDetector.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.application.installer.detector;\n\nimport java.io.File;\nimport java.io.IOException;\n\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@Component\npublic class ThemeDetector extends CustomPageDetector {\n\n    private static final String THEME_CSS = \"resources/theme.css\";\n    private static final String THEME_CONTENT_TYPE = \"theme\";\n\n    public boolean isCompliant(File file) throws IOException {\n        return super.isCompliant(file, THEME_CONTENT_TYPE) && isFilePresent(file, THEME_CSS);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/application/installer/detector/XmlDetector.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.application.installer.detector;\n\nimport static org.bonitasoft.engine.io.FileOperations.isXmlFile;\n\nimport java.io.*;\nimport java.nio.file.Files;\n\nimport javax.xml.XMLConstants;\nimport javax.xml.parsers.DocumentBuilder;\nimport javax.xml.parsers.DocumentBuilderFactory;\nimport javax.xml.parsers.ParserConfigurationException;\n\nimport org.w3c.dom.Element;\nimport org.xml.sax.SAXException;\n\npublic abstract class XmlDetector implements ArtifactDetector {\n\n    protected DocumentBuilder documentBuilder;\n\n    private final String namespace;\n\n    protected XmlDetector(String namespace) {\n        this.namespace = namespace;\n        initDocumentBuilder();\n    }\n\n    private void initDocumentBuilder() {\n        try {\n            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();\n            try {\n                documentBuilderFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, \"\"); // security-compliant\n                documentBuilderFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, \"\"); // security-compliant\n            } catch (IllegalArgumentException e) {\n                //ignored, if not supported by the implementation\n            }\n            documentBuilderFactory.setNamespaceAware(true);\n            this.documentBuilder = documentBuilderFactory.newDocumentBuilder();\n        } catch (ParserConfigurationException e) {\n            // should never occur as we use a simple configuration which can always be honored\n            throw new IllegalStateException(\"Unable to create a document builder during XmlValidator creation\", e);\n        }\n    }\n\n    @Override\n    public boolean isCompliant(File file) throws IOException {\n        if (isXmlFile(file.getName())) {\n            return isCompliant(Files.readAllBytes(file.toPath()));\n        }\n        return false;\n    }\n\n    protected boolean isCompliant(byte[] fileContentBytes) {\n        try (InputStream is = new ByteArrayInputStream(fileContentBytes)) {\n            final Element documentElement = documentBuilder.parse(is).getDocumentElement();\n            // it should be an equals,\n            // but it seems that some organization files have a /1.1 at the end and some other no ...\n            // same for process definition as version depends on Bonita version\n            return documentElement.getNamespaceURI() != null && documentElement.getNamespaceURI().contains(namespace);\n        } catch (SAXException | IOException e) {\n            return false;\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/connector/ConnectorResetStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.connector;\n\nimport org.bonitasoft.engine.bpm.flownode.ActivityExecutionException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface ConnectorResetStrategy {\n\n    public void resetConnectorsOf(long flowNodeInstanceId) throws ActivityExecutionException;\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/connector/ConnectorReseter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.connector;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorStateReset;\nimport org.bonitasoft.engine.bpm.flownode.ActivityExecutionException;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.connector.ConnectorInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstanceWithFailureInfo;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ConnectorReseter {\n\n    private ConnectorInstanceService connectorInstanceService;\n\n    public ConnectorReseter(final ConnectorInstanceService connectorInstanceService) {\n        this.connectorInstanceService = connectorInstanceService;\n    }\n\n    /**\n     * Reset the {@link org.bonitasoft.engine.core.process.instance.model.SConnectorInstanceWithFailureInfo} to the\n     * given state\n     *\n     * @param connectorInstanceWithFailure {@code SConnectorInstance} identifier\n     * @param state the new {@code SConnectorInstance} state\n     * @throws ActivityExecutionException\n     */\n    public void resetState(SConnectorInstanceWithFailureInfo connectorInstanceWithFailure, ConnectorStateReset state)\n            throws ActivityExecutionException {\n        try {\n            // set state\n            connectorInstanceService.setState(connectorInstanceWithFailure, state.name());\n            // clean stack trace if necessary\n            if (connectorInstanceWithFailure.getStackTrace() != null) {\n                connectorInstanceService.setConnectorInstanceFailureException(connectorInstanceWithFailure, null);\n            }\n        } catch (SBonitaException e) {\n            throw new ActivityExecutionException(e);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/connector/ResetAllFailedConnectorStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.connector;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorState;\nimport org.bonitasoft.engine.bpm.connector.ConnectorStateReset;\nimport org.bonitasoft.engine.bpm.flownode.ActivityExecutionException;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.connector.ConnectorInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstanceWithFailureInfo;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ResetAllFailedConnectorStrategy implements ConnectorResetStrategy {\n\n    private final ConnectorInstanceService connectorInstanceService;\n    private final ConnectorReseter connectorReseter;\n    private final int maxResults;\n\n    public ResetAllFailedConnectorStrategy(ConnectorInstanceService connectorInstanceService,\n            ConnectorReseter connectorReseter, int maxResults) {\n        this.connectorInstanceService = connectorInstanceService;\n        this.connectorReseter = connectorReseter;\n        this.maxResults = maxResults;\n    }\n\n    /**\n     * Reset all {@link org.bonitasoft.engine.core.process.instance.model.SConnectorInstance}s related to the given\n     * activity that are in the failed state\n     * to the state {@link org.bonitasoft.engine.bpm.connector.ConnectorStateReset#TO_RE_EXECUTE}\n     *\n     * @param flowNodeInstanceId the identifier of the\n     *        {@link org.bonitasoft.engine.core.process.instance.model.SActivityInstance} where the connectors are\n     *        attached to.\n     */\n    @Override\n    public void resetConnectorsOf(final long flowNodeInstanceId) throws ActivityExecutionException {\n        try {\n            int startIndex = 0;\n            List<SConnectorInstanceWithFailureInfo> failedConnectors;\n            do {\n                failedConnectors = connectorInstanceService.getConnectorInstancesWithFailureInfo(flowNodeInstanceId,\n                        SConnectorInstance.FLOWNODE_TYPE,\n                        ConnectorState.FAILED.name(), startIndex, maxResults);\n                resetCurrentPage(failedConnectors);\n                startIndex += maxResults;\n            } while (failedConnectors.size() == maxResults);\n        } catch (SBonitaException e) {\n            throw new ActivityExecutionException(e);\n        }\n    }\n\n    private void resetCurrentPage(final List<SConnectorInstanceWithFailureInfo> failedConnectors)\n            throws ActivityExecutionException {\n        for (SConnectorInstanceWithFailureInfo failedConnector : failedConnectors) {\n            connectorReseter.resetState(failedConnector, ConnectorStateReset.TO_RE_EXECUTE);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/converter/ApplicationMenuModelConverter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.converter;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.business.application.ApplicationMenu;\nimport org.bonitasoft.engine.business.application.ApplicationMenuCreator;\nimport org.bonitasoft.engine.business.application.ApplicationMenuField;\nimport org.bonitasoft.engine.business.application.ApplicationMenuUpdater;\nimport org.bonitasoft.engine.business.application.impl.ApplicationMenuImpl;\nimport org.bonitasoft.engine.business.application.model.SApplicationMenu;\nimport org.bonitasoft.engine.business.application.model.builder.SApplicationMenuUpdateBuilder;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ApplicationMenuModelConverter {\n\n    public SApplicationMenu buildSApplicationMenu(final ApplicationMenuCreator creator, int menuIndex) {\n        final Map<ApplicationMenuField, Serializable> fields = creator.getFields();\n        final String displayName = (String) fields.get(ApplicationMenuField.DISPLAY_NAME);\n        final Long applicationId = (Long) fields.get(ApplicationMenuField.APPLICATION_ID);\n        final Long applicationPageId = (Long) fields.get(ApplicationMenuField.APPLICATION_PAGE_ID);\n        final Long parentId = (Long) fields.get(ApplicationMenuField.PARENT_ID);\n\n        final SApplicationMenu.SApplicationMenuBuilder builder = SApplicationMenu.builder().displayName(displayName)\n                .applicationId(applicationId).applicationPageId(applicationPageId).index(menuIndex);\n        if (parentId != null) {\n            builder.parentId(parentId);\n        }\n        return builder.build();\n    }\n\n    public ApplicationMenu toApplicationMenu(final SApplicationMenu sApplicationMenu) {\n        final ApplicationMenuImpl menu = new ApplicationMenuImpl(sApplicationMenu.getDisplayName(),\n                sApplicationMenu.getApplicationId(),\n                sApplicationMenu.getApplicationPageId(),\n                sApplicationMenu.getIndex());\n        menu.setId(sApplicationMenu.getId());\n        menu.setParentId(sApplicationMenu.getParentId());\n        return menu;\n    }\n\n    public List<ApplicationMenu> toApplicationMenu(final List<SApplicationMenu> sApplicationMenus)\n            throws SBonitaException {\n        final List<ApplicationMenu> menus = new ArrayList<ApplicationMenu>(sApplicationMenus.size());\n        for (final SApplicationMenu sMenu : sApplicationMenus) {\n            menus.add(toApplicationMenu(sMenu));\n        }\n        return menus;\n    }\n\n    public EntityUpdateDescriptor toApplicationMenuUpdateDescriptor(final ApplicationMenuUpdater updater) {\n        SApplicationMenuUpdateBuilder builder = new SApplicationMenuUpdateBuilder();\n\n        for (Map.Entry<ApplicationMenuField, Serializable> entry : updater.getFields().entrySet()) {\n            switch (entry.getKey()) {\n                case APPLICATION_PAGE_ID:\n                    builder.updateApplicationPageId((Long) entry.getValue());\n                    break;\n                case DISPLAY_NAME:\n                    builder.updateDisplayName((String) entry.getValue());\n                    break;\n                case INDEX:\n                    builder.updateIndex((Integer) entry.getValue());\n                    break;\n                case PARENT_ID:\n                    builder.updateParentId((Long) entry.getValue());\n                    break;\n            }\n        }\n\n        return builder.done();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/converter/ApplicationModelConverter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.converter;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.business.application.*;\nimport org.bonitasoft.engine.business.application.impl.*;\nimport org.bonitasoft.engine.business.application.model.AbstractSApplication;\nimport org.bonitasoft.engine.business.application.model.SApplication;\nimport org.bonitasoft.engine.business.application.model.SApplicationState;\nimport org.bonitasoft.engine.business.application.model.SApplicationWithIcon;\nimport org.bonitasoft.engine.business.application.model.builder.SApplicationUpdateBuilder;\nimport org.bonitasoft.engine.commons.io.IOUtil;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.page.PageService;\nimport org.bonitasoft.engine.page.SPage;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Slf4j\npublic class ApplicationModelConverter {\n\n    private final PageService pageService;\n\n    public ApplicationModelConverter(PageService pageService) {\n        this.pageService = pageService;\n    }\n\n    public SApplicationWithIcon buildSApplication(final AbstractApplicationCreator<?> creator, final long creatorUserId)\n            throws CreationException {\n        final Map<ApplicationField, Serializable> fields = creator.getFields();\n        final String description = (String) fields.get(ApplicationField.DESCRIPTION);\n        final String iconPath = (String) fields.get(ApplicationField.ICON_PATH);\n        final Long profileId = (Long) fields.get(ApplicationField.PROFILE_ID);\n        final long now = System.currentTimeMillis();\n        final boolean isLink = creator.isLink();\n        SApplicationWithIcon application = new SApplicationWithIcon();\n        application.setLink(isLink);\n        application.setToken((String) fields.get(ApplicationField.TOKEN));\n        application.setDisplayName((String) fields.get(ApplicationField.DISPLAY_NAME));\n        application.setVersion((String) fields.get(ApplicationField.VERSION));\n        application.setCreationDate(now);\n        application.setLastUpdateDate(now);\n        application.setCreatedBy(creatorUserId);\n        application.setState(SApplicationState.ACTIVATED.name());\n        if (creator instanceof ApplicationCreator) {\n            application.setLayoutId(getLayoutId((ApplicationCreator) creator));\n            application.setThemeId(getThemeId((ApplicationCreator) creator));\n        }\n        application.setUpdatedBy(creatorUserId);\n        byte[] iconContent = (byte[]) fields.get(ApplicationField.ICON_CONTENT);\n        String iconFileName = (String) fields.get(ApplicationField.ICON_FILE_NAME);\n        if (iconContent != null && iconContent.length > 0) {\n            if (iconFileName != null && !iconFileName.isBlank()) {\n                application.setIconContent(iconContent);\n                application.setIconMimeType(IOUtil.getContentTypeForIcon(iconFileName));\n            } else {\n                log.warn(\"Constructing application {} with {} set but without {} set. Icon is ignored.\",\n                        application.getToken(), ApplicationField.ICON_CONTENT, ApplicationField.ICON_FILE_NAME);\n            }\n        }\n        application.setDescription(description);\n        application.setIconPath(iconPath);\n        application.setProfileId(profileId);\n        return application;\n    }\n\n    protected Long getLayoutId(final ApplicationCreator creator) throws CreationException {\n        Long layoutId = (Long) creator.getFields().get(ApplicationField.LAYOUT_ID);\n        if (layoutId == null) {\n            layoutId = getPageId((String) creator.getFields().get(ApplicationField.TOKEN),\n                    ApplicationService.DEFAULT_LAYOUT_NAME);\n        }\n        return layoutId;\n    }\n\n    protected Long getThemeId(final ApplicationCreator creator) throws CreationException {\n        Long themeId = (Long) creator.getFields().get(ApplicationField.THEME_ID);\n        if (themeId == null) {\n            themeId = getPageId((String) creator.getFields().get(ApplicationField.TOKEN),\n                    ApplicationService.DEFAULT_THEME_NAME);\n        }\n        return themeId;\n    }\n\n    private Long getPageId(final String applicationToken, final String pageName) throws CreationException {\n        try {\n            SPage defaultLayout = pageService.getPageByName(pageName);\n            if (defaultLayout == null) {\n                throw new CreationException(String.format(\n                        \"Unable to created application with token '%s' because the page '%s' was not found.\",\n                        applicationToken, pageName));\n            }\n            return defaultLayout.getId();\n        } catch (SBonitaReadException e) {\n            throw new CreationException(e);\n        }\n    }\n\n    public IApplication toApplication(final AbstractSApplication abstractSApplication) {\n        final AbstractApplicationImpl application;\n        if (abstractSApplication.isLink()) {\n            final ApplicationLinkImpl applicationLink = new ApplicationLinkImpl(\n                    abstractSApplication.getToken(),\n                    abstractSApplication.getVersion(),\n                    abstractSApplication.getDescription());\n            application = applicationLink;\n        } else {\n            ApplicationImpl legacyApplication = new ApplicationImpl(abstractSApplication.getToken(),\n                    abstractSApplication.getVersion(),\n                    abstractSApplication.getDescription(),\n                    abstractSApplication.getLayoutId(), abstractSApplication.getThemeId());\n            legacyApplication.setHomePageId(abstractSApplication.getHomePageId());\n            application = legacyApplication;\n        }\n        application.setId(abstractSApplication.getId());\n        application.setDisplayName(abstractSApplication.getDisplayName());\n        application.setCreatedBy(abstractSApplication.getCreatedBy());\n        application.setCreationDate(new Date(abstractSApplication.getCreationDate()));\n        application.setUpdatedBy(abstractSApplication.getUpdatedBy());\n        application.setLastUpdateDate(new Date(abstractSApplication.getLastUpdateDate()));\n        application.setState(abstractSApplication.getState());\n        application.setIconPath(abstractSApplication.getIconPath());\n        application.setProfileId(abstractSApplication.getProfileId());\n        application.setHasIcon(abstractSApplication.hasIcon());\n        application.setEditable(abstractSApplication.isEditable());\n        application.setVisibility(\n                InternalProfiles.getApplicationVisibilityByProfileName(abstractSApplication.getInternalProfile()));\n        return application;\n    }\n\n    public List<IApplication> toApplication(final List<SApplication> sApplications) {\n        final List<IApplication> applications = new ArrayList<>(sApplications.size());\n        for (final SApplication sApplication : sApplications) {\n            applications.add(toApplication(sApplication));\n        }\n        return applications;\n    }\n\n    public EntityUpdateDescriptor toApplicationUpdateDescriptor(final AbstractApplicationUpdater<?> updater,\n            final long updaterUserId) {\n        final SApplicationUpdateBuilder builder = new SApplicationUpdateBuilder(updaterUserId);\n        updateFields(updater, builder);\n\n        return builder.done();\n    }\n\n    protected void updateFields(final AbstractApplicationUpdater<?> updater, final SApplicationUpdateBuilder builder) {\n        for (final Entry<ApplicationField, Serializable> entry : updater.getFields().entrySet()) {\n            switch (entry.getKey()) {\n                case TOKEN:\n                    builder.updateToken((String) entry.getValue());\n                    break;\n                case DESCRIPTION:\n                    builder.updateDescription((String) entry.getValue());\n                    break;\n                case DISPLAY_NAME:\n                    builder.updateDisplayName((String) entry.getValue());\n                    break;\n                case ICON_PATH:\n                    builder.updateIconPath((String) entry.getValue());\n                    break;\n                case ICON_FILE_NAME:\n                    builder.updateIconMimeType(\n                            entry.getValue() == null || ((String) entry.getValue()).isBlank() ? null\n                                    : IOUtil.getContentTypeForIcon((String) entry.getValue()));\n                    break;\n                case ICON_CONTENT:\n                    builder.updateIconContent((byte[]) entry.getValue());\n                    break;\n                case PROFILE_ID:\n                    builder.updateProfileId((Long) entry.getValue());\n                    break;\n                case STATE:\n                    builder.updateState((String) entry.getValue());\n                    break;\n                case VERSION:\n                    builder.updateVersion((String) entry.getValue());\n                    break;\n                case HOME_PAGE_ID:\n                    builder.updateHomePageId((Long) entry.getValue());\n                    break;\n                case THEME_ID:\n                case LAYOUT_ID:\n                    break;\n                default:\n                    throw new IllegalArgumentException(\"Unknown application field \" + entry.getKey());\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/converter/ApplicationPageModelConverter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.converter;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.business.application.ApplicationPage;\nimport org.bonitasoft.engine.business.application.impl.ApplicationPageImpl;\nimport org.bonitasoft.engine.business.application.model.SApplicationPage;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ApplicationPageModelConverter {\n\n    public ApplicationPage toApplicationPage(final SApplicationPage sApplicationPage) {\n        final ApplicationPageImpl appPage = new ApplicationPageImpl(sApplicationPage.getApplicationId(),\n                sApplicationPage.getPageId(),\n                sApplicationPage.getToken());\n        appPage.setId(sApplicationPage.getId());\n        return appPage;\n    }\n\n    public List<ApplicationPage> toApplicationPage(final List<SApplicationPage> sApplicationPages) {\n        final List<ApplicationPage> appPages = new ArrayList<ApplicationPage>(sApplicationPages.size());\n        for (final SApplicationPage sApplicationPage : sApplicationPages) {\n            appPages.add(toApplicationPage(sApplicationPage));\n        }\n        return appPages;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/converter/PageModelConverter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.converter;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.engine.page.PageCreator;\nimport org.bonitasoft.engine.page.PageUpdater;\nimport org.bonitasoft.engine.page.SPage;\nimport org.bonitasoft.engine.page.impl.PageImpl;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class PageModelConverter {\n\n    public SPage constructSPage(final PageCreator pageCreator, final long creatorUserId) {\n        final Map<PageCreator.PageField, Serializable> fields = pageCreator.getFields();\n        final String name = (String) fields.get(PageCreator.PageField.NAME);\n        final String description = (String) fields.get(PageCreator.PageField.DESCRIPTION);\n        final String displayName = (String) fields.get(PageCreator.PageField.DISPLAY_NAME);\n        final String contentName = (String) fields.get(PageCreator.PageField.CONTENT_NAME);\n        final String contentType = (String) fields.get(PageCreator.PageField.CONTENT_TYPE);\n        final Long processDefinitionId = (Long) fields.get(PageCreator.PageField.PROCESS_DEFINITION_ID);\n        return buildSPage(creatorUserId, name, description, displayName, contentName, contentType, processDefinitionId);\n    }\n\n    public SPage constructSPage(final PageUpdater pageUpdater, final long creatorUserId) {\n        final Map<PageUpdater.PageUpdateField, Serializable> fields = pageUpdater.getFields();\n        final String name = (String) fields.get(PageUpdater.PageUpdateField.NAME);\n        final String description = (String) fields.get(PageUpdater.PageUpdateField.DESCRIPTION);\n        final String displayName = (String) fields.get(PageUpdater.PageUpdateField.DISPLAY_NAME);\n        final String contentName = (String) fields.get(PageUpdater.PageUpdateField.CONTENT_NAME);\n        final String contentType = (String) fields.get(PageUpdater.PageUpdateField.CONTENT_TYPE);\n        final Long processDefinitionId = (Long) fields.get(PageUpdater.PageUpdateField.PROCESS_DEFINITION_ID);\n        return buildSPage(creatorUserId, name, description, displayName, contentName, contentType, processDefinitionId);\n    }\n\n    private SPage buildSPage(long creatorUserId, String name, String description, String displayName,\n            String contentName, String contentType,\n            Long processDefinitionId) {\n        return SPage.builder().name(name).description(description).displayName(displayName)\n                .installationDate(System.currentTimeMillis()).lastModificationDate(System.currentTimeMillis())\n                .installedBy(creatorUserId).lastUpdatedBy(creatorUserId)\n                .provided(false)\n                .contentName(contentName)\n                .contentType(contentType)\n                .editable(true)\n                .removable(true)\n                .processDefinitionId(processDefinitionId != null ? processDefinitionId : 0)\n                .build();\n    }\n\n    public Page toPage(final SPage sPage) {\n        Long processDefinitionId = sPage.getProcessDefinitionId() > 0 ? sPage.getProcessDefinitionId() : null;\n        return new PageImpl(sPage.getId(), sPage.getName(), sPage.getDisplayName(), sPage.isProvided(),\n                sPage.isEditable(), sPage.isRemovable(), sPage.getDescription(),\n                sPage.getInstallationDate(), sPage.getInstalledBy(), sPage.getLastModificationDate(),\n                sPage.getLastUpdatedBy(), sPage.getContentName(),\n                sPage.getContentType(), processDefinitionId);\n    }\n\n    public List<Page> toPages(final List<SPage> sPages) {\n        final List<Page> pages = new ArrayList<>(sPages.size());\n        for (final SPage sPage : sPages) {\n            pages.add(toPage(sPage));\n        }\n        return pages;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/flownode/FlowNodeRetrier.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.flownode;\n\nimport org.bonitasoft.engine.api.impl.connector.ConnectorResetStrategy;\nimport org.bonitasoft.engine.bpm.flownode.ActivityExecutionException;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceNotFoundException;\nimport org.bonitasoft.engine.bpm.flownode.ActivityStates;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.execution.ContainerRegistry;\nimport org.bonitasoft.engine.execution.FlowNodeExecutor;\nimport org.bonitasoft.engine.execution.state.FlowNodeStateManager;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class FlowNodeRetrier {\n\n    private final ContainerRegistry containerRegistry;\n    private final FlowNodeExecutor flowNodeExecutor;\n    private final ActivityInstanceService activityInstanceService;\n    private final FlowNodeStateManager stateManager;\n    private final ConnectorResetStrategy strategy;\n\n    public FlowNodeRetrier(ContainerRegistry containerRegistry, FlowNodeExecutor flowNodeExecutor,\n            ActivityInstanceService activityInstanceService,\n            FlowNodeStateManager stateManager, final ConnectorResetStrategy strategy) {\n        this.containerRegistry = containerRegistry;\n        this.flowNodeExecutor = flowNodeExecutor;\n        this.activityInstanceService = activityInstanceService;\n        this.stateManager = stateManager;\n        this.strategy = strategy;\n    }\n\n    public void retry(long flowNodeInstanceId) throws ActivityExecutionException, ActivityInstanceNotFoundException {\n\n        try {\n            final SFlowNodeInstance flowNodeInstance = activityInstanceService.getFlowNodeInstance(flowNodeInstanceId);\n            FlowNodeState previousState = stateManager.getState(flowNodeInstance.getPreviousStateId());\n            validateCurrentState(flowNodeInstance);\n            strategy.resetConnectorsOf(flowNodeInstanceId);\n            flowNodeExecutor.setStateByStateId(flowNodeInstanceId, flowNodeInstance.getPreviousStateId());\n\n            if (!previousState.isTerminal()) {\n                containerRegistry.executeFlowNode(flowNodeInstance);\n            }\n        } catch (final SFlowNodeNotFoundException e) {\n            throw new ActivityInstanceNotFoundException(flowNodeInstanceId, e);\n        } catch (final SBonitaException e) {\n            throw new ActivityExecutionException(e);\n        }\n\n    }\n\n    private void validateCurrentState(final SFlowNodeInstance flowNodeInstance) throws ActivityExecutionException {\n        FlowNodeState currentState = stateManager.getState(flowNodeInstance.getStateId());\n        if (!ActivityStates.FAILED_STATE.equals(currentState.getName())) {\n            throw new ActivityExecutionException(\n                    \"Unable to retry the flow node instance [name=\" + flowNodeInstance.getName() + \", id=\"\n                            + flowNodeInstance.getId()\n                            + \"] because it is not in failed state. The current state for this flow node instance is '\"\n                            + currentState.getName() + \"'\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/livingapplication/LivingApplicationAPIDelegate.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.livingapplication;\n\nimport java.util.Optional;\nimport java.util.function.Predicate;\n\nimport org.bonitasoft.engine.api.impl.converter.ApplicationModelConverter;\nimport org.bonitasoft.engine.business.application.*;\nimport org.bonitasoft.engine.business.application.impl.IconImpl;\nimport org.bonitasoft.engine.business.application.importer.validator.ApplicationTokenValidator;\nimport org.bonitasoft.engine.business.application.importer.validator.ValidationStatus;\nimport org.bonitasoft.engine.business.application.model.AbstractSApplication;\nimport org.bonitasoft.engine.business.application.model.SApplication;\nimport org.bonitasoft.engine.business.application.model.SApplicationWithIcon;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectAlreadyExistsException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.exception.*;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractSearchEntity;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.service.ServiceAccessor;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class LivingApplicationAPIDelegate {\n\n    private final ApplicationModelConverter converter;\n    private final long loggedUserId;\n    private final ApplicationService applicationService;\n    private final ApplicationTokenValidator tokenValidator;\n\n    public LivingApplicationAPIDelegate(final ServiceAccessor accessor, final ApplicationModelConverter converter,\n            final long loggedUserId, final ApplicationTokenValidator tokenValidator) {\n        this.tokenValidator = tokenValidator;\n        applicationService = accessor.getApplicationService();\n        this.converter = converter;\n        this.loggedUserId = loggedUserId;\n    }\n\n    /**\n     * @deprecated as of 9.0.0, Applications should be created at startup.\n     */\n    @Deprecated(since = \"9.0.0\")\n    public Application createApplication(final ApplicationCreator applicationCreator)\n            throws CreationException {\n        try {\n            validateCreator(applicationCreator);\n            final SApplicationWithIcon sApplicationWithIcon = applicationService\n                    .createApplication(converter.buildSApplication(applicationCreator, loggedUserId));\n            var converted = converter.toApplication(sApplicationWithIcon);\n            if (converted instanceof Application res) {\n                return res;\n            } else {\n                // should not occur anyway\n                throw new CreationException(\"Use dedicated API for application links.\");\n            }\n        } catch (final SObjectAlreadyExistsException e) {\n            throw new AlreadyExistsException(e.getMessage());\n        } catch (final SBonitaException e) {\n            throw new CreationException(e);\n        }\n    }\n\n    /**\n     * @deprecated as of 9.0.0, Applications should be created at startup. This also concerns application links\n     *             introduced in 10.2.0.\n     */\n    @Deprecated(since = \"10.2.0\")\n    public ApplicationLink createApplicationLink(final ApplicationLinkCreator applicationLinkCreator)\n            throws CreationException {\n        try {\n            validateCreator(applicationLinkCreator);\n            final SApplicationWithIcon sApplicationWithIcon = applicationService\n                    .createApplication(converter.buildSApplication(applicationLinkCreator, loggedUserId));\n            var converted = converter.toApplication(sApplicationWithIcon);\n            if (converted instanceof ApplicationLink res) {\n                return res;\n            } else {\n                // should not occur anyway\n                throw new CreationException(\"Use dedicated API for legacy applications.\");\n            }\n        } catch (final SObjectAlreadyExistsException e) {\n            throw new AlreadyExistsException(e.getMessage());\n        } catch (final SBonitaException e) {\n            throw new CreationException(e);\n        }\n    }\n\n    private void validateCreator(final AbstractApplicationCreator<?> applicationCreator) throws CreationException {\n        ValidationStatus validationStatus = tokenValidator.validate(applicationCreator.getToken());\n        if (!validationStatus.isValid()) {\n            throw new CreationException(validationStatus.getMessage());\n        }\n        final String displayName = (String) applicationCreator.getFields().get(ApplicationField.DISPLAY_NAME);\n        if (displayName == null || displayName.trim().isEmpty()) {\n            throw new CreationException(\"The application display name can not be null or empty\");\n        }\n        Class<? extends IApplication> appClass = applicationCreator.isLink() ? ApplicationLink.class\n                : Application.class;\n        if (!applicationCreator.getFields().keySet().stream().allMatch(k -> k.isForClass(appClass))) {\n            throw new CreationException(\"The application fields used must be valid for the application type\");\n        }\n    }\n\n    public IApplication getIApplication(final long applicationId) throws ApplicationNotFoundException {\n        try {\n            return converter.toApplication(applicationService.getApplication(applicationId));\n        } catch (final SBonitaReadException e) {\n            throw new RetrieveException(e);\n        } catch (final SObjectNotFoundException e) {\n            throw new ApplicationNotFoundException(applicationId);\n        }\n    }\n\n    public IApplication getIApplicationByToken(final String applicationToken) throws ApplicationNotFoundException {\n        try {\n            SApplication applicationByToken = applicationService.getApplicationByToken(applicationToken);\n            if (applicationByToken == null) {\n                throw new ApplicationNotFoundException(applicationToken);\n            }\n            return converter.toApplication(applicationByToken);\n        } catch (final SBonitaReadException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    public Icon getIconOfApplication(final long applicationId) throws ApplicationNotFoundException {\n        try {\n            SApplicationWithIcon application = applicationService.getApplicationWithIcon(applicationId);\n            if (application.hasIcon()) {\n                return new IconImpl(application.getIconMimeType(), application.getIconContent());\n            }\n            return null;\n        } catch (final SBonitaReadException e) {\n            throw new RetrieveException(e);\n        } catch (final SObjectNotFoundException e) {\n            throw new ApplicationNotFoundException(applicationId);\n        }\n    }\n\n    public void deleteApplication(final long applicationId) throws DeletionException {\n        try {\n            applicationService.deleteApplication(applicationId);\n        } catch (final SObjectNotFoundException sonfe) {\n            throw new DeletionException(new ApplicationNotFoundException(applicationId));\n        } catch (final SBonitaException e) {\n            throw new DeletionException(e);\n        }\n    }\n\n    public <E extends IApplication> SearchResult<E> searchIApplications(\n            final AbstractSearchEntity<E, SApplication> searchApplications)\n            throws SearchException {\n        try {\n            searchApplications.execute();\n            return searchApplications.getResult();\n        } catch (final SBonitaException e) {\n            throw new SearchException(e);\n        }\n    }\n\n    /**\n     * @deprecated as of 9.0.0, Applications should be updated at startup.\n     */\n    @Deprecated(since = \"9.0.0\")\n    public Application updateApplication(final long applicationId, final ApplicationUpdater updater)\n            throws UpdateException,\n            AlreadyExistsException, ApplicationNotFoundException {\n        try {\n            validateUpdater(updater);\n            AbstractSApplication application;\n            if (!updater.getFields().isEmpty()) {\n                /*\n                 * This API may be called within our without a transaction.\n                 * So we must check first whether the application is a link to have a consistent behavior\n                 * and never update the application link.\n                 */\n                if (Optional.ofNullable(applicationService.getApplicationWithIcon(applicationId))\n                        .filter(AbstractSApplication::isLink).isPresent()) {\n                    throw new UpdateException(\"Use dedicated API for application links.\");\n                }\n                application = applicationService.updateApplication(applicationId,\n                        converter.toApplicationUpdateDescriptor(updater, loggedUserId));\n            } else {\n                application = applicationService.getApplication(applicationId);\n            }\n            var converted = converter.toApplication(application);\n            if (converted instanceof Application res) {\n                return res;\n            } else {\n                throw new UpdateException(\"Use dedicated API for application links.\");\n            }\n        } catch (final SObjectAlreadyExistsException e) {\n            throw new AlreadyExistsException(e.getMessage());\n        } catch (final SObjectNotFoundException e) {\n            throw new ApplicationNotFoundException(applicationId);\n        } catch (final SBonitaException e) {\n            throw new UpdateException(e);\n        }\n    }\n\n    /**\n     * @deprecated as of 9.0.0, Applications should be updated at startup. This also concerns application links\n     *             introduced in 10.2.0.\n     */\n    @Deprecated(since = \"10.2.0\")\n    public ApplicationLink updateApplicationLink(final long applicationId, final ApplicationLinkUpdater updater)\n            throws UpdateException,\n            AlreadyExistsException, ApplicationNotFoundException {\n        try {\n            validateUpdater(updater);\n            AbstractSApplication application;\n            if (!updater.getFields().isEmpty()) {\n                /*\n                 * This API may be called within our without a transaction.\n                 * So we must check first whether the application is a link to have a consistent behavior\n                 * and never update the legacy application.\n                 */\n                Predicate<SApplicationWithIcon> isLink = AbstractSApplication::isLink;\n                if (Optional.ofNullable(applicationService.getApplicationWithIcon(applicationId))\n                        .filter(isLink.negate()).isPresent()) {\n                    throw new UpdateException(\"Use dedicated API for legacy applications.\");\n                }\n                application = applicationService.updateApplication(applicationId,\n                        converter.toApplicationUpdateDescriptor(updater, loggedUserId));\n            } else {\n                application = applicationService.getApplication(applicationId);\n            }\n            var converted = converter.toApplication(application);\n            if (converted instanceof ApplicationLink res) {\n                return res;\n            } else {\n                throw new UpdateException(\"Use dedicated API for legacy applications.\");\n            }\n        } catch (final SObjectAlreadyExistsException e) {\n            throw new AlreadyExistsException(e.getMessage());\n        } catch (final SObjectNotFoundException e) {\n            throw new ApplicationNotFoundException(applicationId);\n        } catch (final SBonitaException e) {\n            throw new UpdateException(e);\n        }\n    }\n\n    private void validateUpdater(final AbstractApplicationUpdater<?> updater) throws UpdateException {\n        validateToken(updater);\n        validateDisplayName(updater);\n        Class<? extends IApplication> appClass = updater instanceof ApplicationLinkUpdater ? ApplicationLink.class\n                : Application.class;\n        if (!updater.getFields().keySet().stream().allMatch(k -> k.isForClass(appClass))) {\n            throw new UpdateException(\"The application fields used must be valid for the application type\");\n        }\n    }\n\n    private void validateDisplayName(final AbstractApplicationUpdater<?> updater) throws UpdateException {\n        if (updater.getFields().keySet().contains(ApplicationField.DISPLAY_NAME)) {\n            final String displayName = (String) updater.getFields().get(ApplicationField.DISPLAY_NAME);\n            if (displayName == null || displayName.trim().isEmpty()) {\n                throw new UpdateException(\"The application display name can not be null or empty\");\n            }\n        }\n    }\n\n    private void validateToken(final AbstractApplicationUpdater<?> updater) throws UpdateException {\n        if (updater.getFields().keySet().contains(ApplicationField.TOKEN)) {\n            final String token = (String) updater.getFields().get(ApplicationField.TOKEN);\n            ValidationStatus validationStatus = tokenValidator.validate(token);\n            if (!validationStatus.isValid()) {\n                throw new UpdateException(validationStatus.getMessage());\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/livingapplication/LivingApplicationExporterDelegate.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.livingapplication;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.exporter.ApplicationExporter;\nimport org.bonitasoft.engine.business.application.model.SApplication;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.exception.ExportException;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class LivingApplicationExporterDelegate {\n\n    private final ApplicationService applicationService;\n    private final ApplicationExporter exporter;\n\n    public LivingApplicationExporterDelegate(ApplicationService applicationService, ApplicationExporter exporter) {\n        this.applicationService = applicationService;\n        this.exporter = exporter;\n    }\n\n    public byte[] exportApplications(long... applicationIds) throws ExportException {\n        try {\n            List<SApplication> applications = new ArrayList<>();\n            for (long applicationId : applicationIds) {\n                applications.add(applicationService.getApplication(applicationId));\n            }\n            return exporter.export(applications);\n        } catch (SObjectNotFoundException | SBonitaReadException e) {\n            throw new ExportException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/livingapplication/LivingApplicationMenuAPIDelegate.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.livingapplication;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.impl.converter.ApplicationMenuModelConverter;\nimport org.bonitasoft.engine.api.impl.transaction.application.SearchApplicationMenus;\nimport org.bonitasoft.engine.business.application.*;\nimport org.bonitasoft.engine.business.application.importer.validator.ApplicationMenuCreatorValidator;\nimport org.bonitasoft.engine.business.application.model.SApplicationMenu;\nimport org.bonitasoft.engine.business.application.model.builder.SApplicationUpdateBuilder;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.exception.*;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.service.ServiceAccessor;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class LivingApplicationMenuAPIDelegate {\n\n    private final ApplicationMenuModelConverter converter;\n    private final ApplicationService applicationService;\n    private final ApplicationMenuCreatorValidator creatorValidator;\n    private final long loggedUserId;\n\n    public LivingApplicationMenuAPIDelegate(final ServiceAccessor accessor,\n            final ApplicationMenuModelConverter converter,\n            final ApplicationMenuCreatorValidator creatorValidator, final long loggedUserId) {\n        this.creatorValidator = creatorValidator;\n        this.loggedUserId = loggedUserId;\n        applicationService = accessor.getApplicationService();\n        this.converter = converter;\n    }\n\n    /**\n     * @deprecated as of 9.0.0, Application menu should be created at startup.\n     */\n    @Deprecated(since = \"9.0.0\")\n    public ApplicationMenu createApplicationMenu(final ApplicationMenuCreator applicationMenuCreator)\n            throws CreationException {\n        try {\n            List<String> errors = creatorValidator.isValid(applicationMenuCreator);\n            if (!errors.isEmpty()) {\n                throw new CreationException(\n                        \"The ApplicationMenuCreator is invalid. Problems: \" + errors);\n            }\n            final int index = applicationService.getNextAvailableIndex(applicationMenuCreator.getParentId());\n            final SApplicationMenu sApplicationMenu = applicationService\n                    .createApplicationMenu(converter.buildSApplicationMenu(applicationMenuCreator, index));\n            applicationService.updateApplication(sApplicationMenu.getApplicationId(),\n                    new SApplicationUpdateBuilder(loggedUserId).done());\n            return converter.toApplicationMenu(sApplicationMenu);\n        } catch (final SBonitaException e) {\n            throw new CreationException(e);\n        }\n    }\n\n    /**\n     * @deprecated as of 9.0.0, Application menu should be updated at startup.\n     */\n    @Deprecated(since = \"9.0.0\")\n    public ApplicationMenu updateApplicationMenu(final long applicationMenuId, final ApplicationMenuUpdater updater)\n            throws ApplicationMenuNotFoundException,\n            UpdateException {\n        final EntityUpdateDescriptor updateDescriptor = converter.toApplicationMenuUpdateDescriptor(updater);\n        try {\n            final SApplicationMenu sApplicationMenu = applicationService.updateApplicationMenu(applicationMenuId,\n                    updateDescriptor);\n            applicationService.updateApplication(sApplicationMenu.getApplicationId(),\n                    new SApplicationUpdateBuilder(loggedUserId).done());\n            return converter.toApplicationMenu(sApplicationMenu);\n        } catch (final SObjectNotFoundException e) {\n            throw new ApplicationMenuNotFoundException(e.getMessage());\n        } catch (final SBonitaException e) {\n            throw new UpdateException(e);\n        }\n    }\n\n    public ApplicationMenu getApplicationMenu(final long applicationMenuId) throws ApplicationMenuNotFoundException {\n        try {\n            final SApplicationMenu sApplicationMenu = applicationService.getApplicationMenu(applicationMenuId);\n            return converter.toApplicationMenu(sApplicationMenu);\n        } catch (final SObjectNotFoundException e) {\n            throw new ApplicationMenuNotFoundException(e.getMessage());\n        } catch (final SBonitaException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    public void deleteApplicationMenu(final long applicationMenuId) throws DeletionException {\n        try {\n            final SApplicationMenu deletedApplicationMenu = applicationService.deleteApplicationMenu(applicationMenuId);\n            applicationService.updateApplication(deletedApplicationMenu.getApplicationId(),\n                    new SApplicationUpdateBuilder(loggedUserId).done());\n        } catch (final SObjectNotFoundException sonfe) {\n            throw new DeletionException(new ApplicationMenuNotFoundException(sonfe.getMessage()));\n        } catch (final SBonitaException e) {\n            throw new DeletionException(e);\n        }\n    }\n\n    public SearchResult<ApplicationMenu> searchApplicationMenus(final SearchApplicationMenus searchApplicationMenus)\n            throws SearchException {\n        try {\n            searchApplicationMenus.execute();\n            return searchApplicationMenus.getResult();\n        } catch (final SBonitaException e) {\n            throw new SearchException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/livingapplication/LivingApplicationPageAPIDelegate.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.livingapplication;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.impl.converter.ApplicationPageModelConverter;\nimport org.bonitasoft.engine.api.impl.transaction.application.SearchApplicationPages;\nimport org.bonitasoft.engine.business.application.ApplicationNotFoundException;\nimport org.bonitasoft.engine.business.application.ApplicationPage;\nimport org.bonitasoft.engine.business.application.ApplicationPageNotFoundException;\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.importer.validator.ApplicationTokenValidator;\nimport org.bonitasoft.engine.business.application.importer.validator.ValidationStatus;\nimport org.bonitasoft.engine.business.application.model.SApplicationPage;\nimport org.bonitasoft.engine.business.application.model.builder.SApplicationUpdateBuilder;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectAlreadyExistsException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.RetrieveException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.service.ServiceAccessor;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class LivingApplicationPageAPIDelegate {\n\n    private final ApplicationPageModelConverter converter;\n    private final ApplicationService applicationService;\n    private final long loggedUserId;\n    private final ApplicationTokenValidator tokenValidator;\n\n    public LivingApplicationPageAPIDelegate(final ServiceAccessor accessor,\n            final ApplicationPageModelConverter converter, final long loggedUserId,\n            final ApplicationTokenValidator tokenValidator) {\n        this.tokenValidator = tokenValidator;\n        applicationService = accessor.getApplicationService();\n        this.converter = converter;\n        this.loggedUserId = loggedUserId;\n    }\n\n    /**\n     * @deprecated as of 9.0.0, Application home page should be defined at startup.\n     */\n    @Deprecated(since = \"9.0.0\")\n    public void setApplicationHomePage(final long applicationId, final long applicationPageId)\n            throws UpdateException, ApplicationNotFoundException {\n        final SApplicationUpdateBuilder sApplicationUpdateBuilder = new SApplicationUpdateBuilder(loggedUserId)\n                .updateHomePageId(applicationPageId);\n        try {\n            applicationService.updateApplication(applicationId, sApplicationUpdateBuilder.done());\n        } catch (final SObjectNotFoundException e) {\n            throw new ApplicationNotFoundException(applicationId);\n        } catch (final SBonitaException e) {\n            throw new UpdateException(e);\n        }\n    }\n\n    /**\n     * @deprecated as of 9.0.0, Application page should be created at startup.\n     */\n    @Deprecated(since = \"9.0.0\")\n    public ApplicationPage createApplicationPage(final long applicationId, final long pageId, final String token)\n            throws CreationException {\n        validateToken(token);\n        final SApplicationPage.SApplicationPageBuilder pageBuilder = SApplicationPage.builder()\n                .applicationId(applicationId).pageId(pageId).token(token);\n        SApplicationPage sAppPage;\n        try {\n            sAppPage = applicationService.createApplicationPage(pageBuilder.build());\n            applicationService.updateApplication(applicationId,\n                    new SApplicationUpdateBuilder(loggedUserId)\n                            .done());\n            return converter.toApplicationPage(sAppPage);\n        } catch (final SObjectAlreadyExistsException e) {\n            throw new AlreadyExistsException(e.getMessage());\n        } catch (final SBonitaException e) {\n            throw new CreationException(e);\n        }\n    }\n\n    private void validateToken(final String token) throws CreationException {\n        ValidationStatus validationStatus = tokenValidator.validate(token);\n        if (!validationStatus.isValid()) {\n            throw new CreationException(validationStatus.getMessage());\n        }\n    }\n\n    public ApplicationPage getApplicationPage(final String applicationName, final String applicationPageToken)\n            throws ApplicationPageNotFoundException {\n        try {\n            final SApplicationPage sAppPage = applicationService.getApplicationPage(applicationName,\n                    applicationPageToken);\n            return converter.toApplicationPage(sAppPage);\n        } catch (final SBonitaReadException e) {\n            throw new RetrieveException(e);\n        } catch (final SObjectNotFoundException e) {\n            throw new ApplicationPageNotFoundException(e.getMessage());\n        }\n    }\n\n    public ApplicationPage getApplicationPage(final long applicationPageId) throws ApplicationPageNotFoundException {\n        try {\n            final SApplicationPage sApplicationPage = applicationService.getApplicationPage(applicationPageId);\n            return converter.toApplicationPage(sApplicationPage);\n        } catch (final SBonitaReadException e) {\n            throw new RetrieveException(e);\n        } catch (final SObjectNotFoundException e) {\n            throw new ApplicationPageNotFoundException(e.getMessage());\n        }\n    }\n\n    public void deleteApplicationPage(final long applicationPageId) throws DeletionException {\n        try {\n            final SApplicationPage deletedApplicationPage = applicationService.deleteApplicationPage(applicationPageId);\n            final SApplicationUpdateBuilder appBuilder = new SApplicationUpdateBuilder(loggedUserId);\n            applicationService.updateApplication(deletedApplicationPage.getApplicationId(), appBuilder.done());\n        } catch (final SObjectNotFoundException sonfe) {\n            throw new DeletionException(new ApplicationPageNotFoundException(sonfe.getMessage()));\n        } catch (final SBonitaException e) {\n            throw new DeletionException(e);\n        }\n    }\n\n    public ApplicationPage getApplicationHomePage(final long applicationId) throws ApplicationPageNotFoundException {\n        try {\n            SApplicationPage sHomePage = applicationService.getApplicationHomePage(applicationId);\n            return converter.toApplicationPage(sHomePage);\n        } catch (final SBonitaReadException e) {\n            throw new RetrieveException(e);\n        } catch (final SObjectNotFoundException e) {\n            throw new ApplicationPageNotFoundException(e.getMessage());\n        }\n    }\n\n    public SearchResult<ApplicationPage> searchApplicationPages(final SearchApplicationPages searchApplicationPages)\n            throws SearchException {\n        try {\n            searchApplicationPages.execute();\n            return searchApplicationPages.getResult();\n        } catch (final SBonitaException e) {\n            throw new SearchException(e);\n        }\n    }\n\n    public List<String> getAllPagesForProfile(final long profileId) {\n        try {\n            return applicationService.getAllPagesForProfile(profileId);\n        } catch (final SBonitaReadException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    public List<String> getAllPagesForProfile(String profile) {\n        try {\n            return applicationService.getAllPagesForProfile(profile);\n        } catch (final SBonitaReadException e) {\n            throw new RetrieveException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/organization/OrganizationAPIDelegate.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.organization;\n\nimport java.util.List;\n\nimport javax.xml.bind.JAXBException;\n\nimport org.bonitasoft.engine.api.impl.SCustomUserInfoValueAPI;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.identity.*;\nimport org.bonitasoft.engine.identity.model.builder.SCustomUserInfoValueUpdateBuilderFactory;\nimport org.bonitasoft.engine.service.ServiceAccessorSingleton;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class OrganizationAPIDelegate {\n\n    private static OrganizationAPIDelegate organizationAPIDelegate = null;\n    private final IdentityService identityService;\n\n    private OrganizationAPIDelegate(IdentityService identityService) {\n        this.identityService = identityService;\n    }\n\n    public static OrganizationAPIDelegate getInstance() {\n        if (organizationAPIDelegate == null) {\n            organizationAPIDelegate = new OrganizationAPIDelegate(ServiceAccessorSingleton.getInstance()\n                    .getIdentityService());\n        }\n        return organizationAPIDelegate;\n    }\n\n    public List<String> importOrganizationWithWarnings(String organizationContent, ImportPolicy policy)\n            throws OrganizationImportException {\n        try {\n            final SCustomUserInfoValueUpdateBuilderFactory updaterFactor = BuilderFactory\n                    .get(SCustomUserInfoValueUpdateBuilderFactory.class);\n            final SCustomUserInfoValueAPI customUserInfoValueAPI = new SCustomUserInfoValueAPI(\n                    identityService, updaterFactor);\n            ImportOrganization importedOrganization = new ImportOrganization(identityService, organizationContent,\n                    policy, customUserInfoValueAPI);\n            return importedOrganization.execute();\n        } catch (JAXBException e) {\n            throw new InvalidOrganizationFileFormatException(e);\n        } catch (final SBonitaException e) {\n            throw new OrganizationImportException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/page/PageAPIDelegate.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.page;\n\nimport java.io.Serializable;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Properties;\nimport java.util.Set;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.api.impl.converter.PageModelConverter;\nimport org.bonitasoft.engine.api.impl.resolver.BusinessArchiveArtifactsManager;\nimport org.bonitasoft.engine.api.impl.transaction.page.SearchPages;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectAlreadyExistsException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.core.form.FormMappingService;\nimport org.bonitasoft.engine.core.form.SFormMapping;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.InvalidPageTokenException;\nimport org.bonitasoft.engine.exception.InvalidPageZipContentException;\nimport org.bonitasoft.engine.exception.InvalidPageZipInconsistentException;\nimport org.bonitasoft.engine.exception.InvalidPageZipMissingAPropertyException;\nimport org.bonitasoft.engine.exception.InvalidPageZipMissingIndexException;\nimport org.bonitasoft.engine.exception.InvalidPageZipMissingPropertiesException;\nimport org.bonitasoft.engine.exception.RetrieveException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.exception.UpdatingWithInvalidPageTokenException;\nimport org.bonitasoft.engine.exception.UpdatingWithInvalidPageZipContentException;\nimport org.bonitasoft.engine.form.FormMappingSearchDescriptor;\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.engine.page.PageCreator;\nimport org.bonitasoft.engine.page.PageMappingService;\nimport org.bonitasoft.engine.page.PageNotFoundException;\nimport org.bonitasoft.engine.page.PageNotFoundException.PageAttribute;\nimport org.bonitasoft.engine.page.PageService;\nimport org.bonitasoft.engine.page.PageUpdater;\nimport org.bonitasoft.engine.page.SInvalidPageTokenException;\nimport org.bonitasoft.engine.page.SInvalidPageZipException;\nimport org.bonitasoft.engine.page.SInvalidPageZipInconsistentException;\nimport org.bonitasoft.engine.page.SInvalidPageZipMissingAPropertyException;\nimport org.bonitasoft.engine.page.SInvalidPageZipMissingIndexException;\nimport org.bonitasoft.engine.page.SInvalidPageZipMissingPropertiesException;\nimport org.bonitasoft.engine.page.SPage;\nimport org.bonitasoft.engine.page.SPageMapping;\nimport org.bonitasoft.engine.page.SPageUpdateBuilder;\nimport org.bonitasoft.engine.page.SPageUpdateBuilderFactory;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.ServiceAccessorSingleton;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@Slf4j\npublic class PageAPIDelegate {\n\n    private static PageAPIDelegate pageAPIDelegate = null;\n\n    private final PageService pageService;\n    private final SearchEntitiesDescriptor searchEntitiesDescriptor;\n    private final ServiceAccessor serviceAccessor;\n    private final PageMappingService pageMappingService;\n    private final FormMappingService formMappingService;\n    private final BusinessArchiveArtifactsManager businessArchiveArtifactsManager;\n\n    private PageAPIDelegate(ServiceAccessor serviceAccessor,\n            BusinessArchiveArtifactsManager businessArchiveArtifactsManager,\n            PageService pageService, PageMappingService pageMappingService,\n            FormMappingService formMappingService, SearchEntitiesDescriptor searchEntitiesDescriptor) {\n        this.serviceAccessor = serviceAccessor;\n        this.businessArchiveArtifactsManager = businessArchiveArtifactsManager;\n        this.pageService = pageService;\n        this.pageMappingService = pageMappingService;\n        this.formMappingService = formMappingService;\n        this.searchEntitiesDescriptor = searchEntitiesDescriptor;\n    }\n\n    public static PageAPIDelegate getInstance() {\n        if (pageAPIDelegate == null) {\n            ServiceAccessor serviceAccessor = ServiceAccessorSingleton.getInstance();\n            pageAPIDelegate = new PageAPIDelegate(serviceAccessor,\n                    serviceAccessor.getBusinessArchiveArtifactsManager(),\n                    serviceAccessor.getPageService(),\n                    serviceAccessor.getPageMappingService(),\n                    serviceAccessor.getFormMappingService(),\n                    serviceAccessor.getSearchEntitiesDescriptor());\n        }\n        return pageAPIDelegate;\n    }\n\n    public Page getPage(final long pageId) throws PageNotFoundException {\n        try {\n            return convertToPage(pageService.getPage(pageId));\n        } catch (final SBonitaReadException e) {\n            throw new PageNotFoundException(e);\n        } catch (final SObjectNotFoundException e) {\n            throw new PageNotFoundException(e);\n        }\n    }\n\n    public byte[] getPageContent(final long pageId) throws PageNotFoundException {\n        try {\n            return pageService.getPageContent(pageId);\n        } catch (final SBonitaReadException e) {\n            throw new PageNotFoundException(e);\n\n        } catch (final SObjectNotFoundException e) {\n            throw new PageNotFoundException(e);\n        }\n    }\n\n    public SearchResult<Page> searchPages(final SearchOptions searchOptions) throws SearchException {\n        final SearchPages searchPages = getSearchPages(searchOptions);\n        try {\n            searchPages.execute();\n            return searchPages.getResult();\n        } catch (final SBonitaException sbe) {\n            throw new SearchException(sbe);\n        }\n    }\n\n    protected SearchPages getSearchPages(final SearchOptions searchOptions) {\n        return new SearchPages(pageService, searchEntitiesDescriptor.getSearchPageDescriptor(), searchOptions);\n    }\n\n    public Page createPage(final PageCreator pageCreator, final byte[] content, long userIdFromSession)\n            throws AlreadyExistsException, CreationException, InvalidPageTokenException,\n            InvalidPageZipContentException {\n        final SPage sPage = constructPage(pageCreator, userIdFromSession);\n\n        try {\n            final SPage addPage = pageService.addPage(sPage, content);\n            return convertToPage(addPage);\n        } catch (final SObjectAlreadyExistsException e) {\n            throw new AlreadyExistsException(\"A page already exists with the name \" + pageCreator.getName());\n        } catch (final SInvalidPageTokenException e) {\n            throw new InvalidPageTokenException(e.getMessage(), e);\n        } catch (final SInvalidPageZipException e) {\n            throw convertException(e);\n        } catch (final SBonitaException e) {\n            throw new CreationException(e);\n        }\n    }\n\n    public Page createPage(final String contentName, final byte[] content, long userIdFromSession)\n            throws AlreadyExistsException, CreationException, InvalidPageTokenException,\n            InvalidPageZipContentException {\n        try {\n            return convertToPage(pageService.addPage(content, contentName, userIdFromSession));\n        } catch (final SObjectAlreadyExistsException e) {\n            throw new AlreadyExistsException(\"A page already exists with the name defined in page zip content\");\n        } catch (final SInvalidPageTokenException e) {\n            throw new InvalidPageTokenException(e.getMessage(), e);\n        } catch (final SInvalidPageZipException e) {\n            throw convertException(e);\n        } catch (final SBonitaException e) {\n            throw new CreationException(e);\n        }\n    }\n\n    public void deletePage(final long pageId) throws DeletionException {\n        try {\n            pageService.deletePage(pageId);\n            final Set<Long> processDefinitionIds = updatePageMappings(pageId);\n            for (final Long processDefinitionId : processDefinitionIds) {\n                updateProcessResolution(processDefinitionId);\n            }\n        } catch (final SObjectNotFoundException sonfe) {\n            throw new DeletionException(new PageNotFoundException(sonfe));\n        } catch (final SBonitaException e) {\n            throw new DeletionException(e);\n        }\n    }\n\n    Set<Long> updatePageMappings(long pageId)\n            throws SBonitaReadException, SObjectModificationException, SObjectNotFoundException {\n        List<SFormMapping> formMappings;\n        QueryOptions queryOptions = new QueryOptions(0, 20,\n                Collections.singletonList(new OrderByOption(SFormMapping.class,\n                        FormMappingSearchDescriptor.ID, OrderByType.ASC)),\n                Collections.singletonList(new FilterOption(SPageMapping.class,\n                        FormMappingSearchDescriptor.PAGE_ID, pageId)),\n                null);\n        final Set<Long> processDefinitionIds = new HashSet<>();\n        do {\n            formMappings = formMappingService.searchFormMappings(queryOptions);\n            for (final SFormMapping formMapping : formMappings) {\n                pageMappingService.update(formMapping.getPageMapping(), null);\n                processDefinitionIds.add(formMapping.getProcessDefinitionId());\n            }\n            queryOptions = QueryOptions.getNextPage(queryOptions);\n        } while (formMappings.size() == 20);\n        return processDefinitionIds;\n    }\n\n    private void updateProcessResolution(Long processDefinitionId) {\n        businessArchiveArtifactsManager.resolveDependencies(processDefinitionId, serviceAccessor);\n    }\n\n    public void deletePages(final List<Long> pageIds) throws DeletionException {\n        for (final Long pageId : pageIds) {\n            deletePage(pageId);\n        }\n    }\n\n    public Page getPageByName(final String name) throws PageNotFoundException {\n        try {\n            final SPage sPage = pageService.getPageByName(name);\n            if (sPage == null) {\n                throw new PageNotFoundException(name, PageAttribute.NAME);\n            }\n            return convertToPage(sPage);\n        } catch (final SBonitaReadException e) {\n            throw new PageNotFoundException(e);\n        }\n    }\n\n    public Page updatePage(final long pageId, final PageUpdater pageUpdater, long userIdFromSession)\n            throws UpdateException, AlreadyExistsException {\n        if (pageUpdater == null || pageUpdater.getFields().isEmpty()) {\n            throw new UpdateException(\"The pageUpdater descriptor does not contain field updates\");\n        }\n        final SPage sPage = constructPage(pageUpdater, userIdFromSession);\n        final SPageUpdateBuilder pageUpdateBuilder = getPageUpdateBuilder();\n        final Map<PageUpdater.PageUpdateField, Serializable> fields = pageUpdater.getFields();\n        for (final Entry<PageUpdater.PageUpdateField, Serializable> field : fields.entrySet()) {\n            switch (field.getKey()) {\n                case NAME:\n                    log.warn(\"Since 7.13 updating the name of a page is no more possible, the update will ignore it.\");\n                    break;\n                case DISPLAY_NAME:\n                    pageUpdateBuilder.updateDisplayName(sPage.getDisplayName());\n                    break;\n                case DESCRIPTION:\n                    pageUpdateBuilder.updateDescription(sPage.getDescription());\n                    break;\n                case CONTENT_NAME:\n                    pageUpdateBuilder.updateContentName(sPage.getContentName());\n                    break;\n                case CONTENT_TYPE:\n                    pageUpdateBuilder.updateContentType(sPage.getContentType());\n                    break;\n                case PROCESS_DEFINITION_ID:\n                    pageUpdateBuilder.updateProcessDefinitionId(sPage.getProcessDefinitionId());\n                    break;\n                default:\n                    break;\n            }\n        }\n        pageUpdateBuilder.updateLastModificationDate(System.currentTimeMillis());\n        pageUpdateBuilder.updateLastUpdatedBy(userIdFromSession);\n        pageUpdateBuilder.markNonProvided();\n        SPage updatedPage;\n        try {\n            updatedPage = pageService.updatePage(pageId, pageUpdateBuilder.done());\n            return convertToPage(updatedPage);\n        } catch (final SObjectModificationException e) {\n            throw new UpdateException(e);\n        } catch (final SObjectAlreadyExistsException e) {\n            throw new AlreadyExistsException(e);\n        } catch (final SInvalidPageTokenException e) {\n            throw new UpdatingWithInvalidPageTokenException(e.getMessage(), e);\n        }\n    }\n\n    public void updatePageContent(final long pageId, final byte[] content, long userIdFromSession)\n            throws UpdateException, UpdatingWithInvalidPageTokenException,\n            UpdatingWithInvalidPageZipContentException {\n        final SPageUpdateBuilder pageUpdateBuilder = getPageUpdateBuilder();\n        pageUpdateBuilder.updateLastModificationDate(System.currentTimeMillis());\n        pageUpdateBuilder.updateLastUpdatedBy(userIdFromSession);\n        pageUpdateBuilder.markNonProvided();\n        try {\n            pageService.updatePageContent(pageId, content, null, pageUpdateBuilder);\n        } catch (final SInvalidPageTokenException e) {\n            throw new UpdatingWithInvalidPageTokenException(e.getMessage(), e);\n        } catch (final SInvalidPageZipException e) {\n            throw new UpdatingWithInvalidPageZipContentException(e.getMessage(), e);\n        } catch (final SBonitaException sBonitaException) {\n            throw new UpdateException(sBonitaException);\n        }\n    }\n\n    public Properties getPageProperties(final byte[] content, final boolean checkIfItAlreadyExists)\n            throws InvalidPageTokenException, AlreadyExistsException,\n            InvalidPageZipMissingPropertiesException, InvalidPageZipMissingIndexException,\n            InvalidPageZipInconsistentException,\n            InvalidPageZipMissingAPropertyException {\n        try {\n            return getProperties(content, checkIfItAlreadyExists, pageService);\n        } catch (final SInvalidPageTokenException e) {\n            throw new InvalidPageTokenException(e.getMessage());\n        } catch (final SBonitaReadException e) {\n            throw new RetrieveException(e);\n        } catch (final SInvalidPageZipMissingAPropertyException e) {\n            throw new InvalidPageZipMissingAPropertyException(e.getFields());\n        } catch (final SInvalidPageZipInconsistentException e) {\n            throw new InvalidPageZipInconsistentException(e.getMessage(), e);\n        } catch (final SInvalidPageZipMissingIndexException e) {\n            throw new InvalidPageZipMissingIndexException();\n        } catch (final SInvalidPageZipMissingPropertiesException e) {\n            throw new InvalidPageZipMissingPropertiesException();\n        }\n    }\n\n    protected SPageUpdateBuilder getPageUpdateBuilder() {\n        return BuilderFactory.get(SPageUpdateBuilderFactory.class).createNewInstance(new EntityUpdateDescriptor());\n    }\n\n    protected Page convertToPage(final SPage addPage) {\n        return new PageModelConverter().toPage(addPage);\n    }\n\n    protected SPage constructPage(final PageCreator pageCreator, final long userId) {\n        return new PageModelConverter().constructSPage(pageCreator, userId);\n    }\n\n    protected SPage constructPage(final PageUpdater pageUpdater, final long userId) {\n        return new PageModelConverter().constructSPage(pageUpdater, userId);\n    }\n\n    private Properties getProperties(final byte[] content, final boolean checkIfItAlreadyExists,\n            final PageService pageService)\n            throws SInvalidPageZipMissingIndexException,\n            SInvalidPageZipMissingAPropertyException, SInvalidPageZipInconsistentException,\n            SInvalidPageZipMissingPropertiesException,\n            SInvalidPageTokenException, SBonitaReadException, AlreadyExistsException {\n        final Properties properties = pageService.readPageZip(content);\n        if (checkIfItAlreadyExists) {\n            final String name = properties.getProperty(PageService.PROPERTIES_NAME);\n            final SPage pageByName = pageService.getPageByName(name);\n            if (pageByName != null) {\n                throw new AlreadyExistsException(\"A page with name \" + name + \" already exists\");\n            }\n        }\n        return properties;\n    }\n\n    private InvalidPageZipContentException convertException(final SInvalidPageZipException e) {\n        if (e instanceof SInvalidPageZipMissingPropertiesException) {\n            return new InvalidPageZipMissingPropertiesException();\n        }\n        if (e instanceof SInvalidPageZipMissingAPropertyException) {\n            return new InvalidPageZipMissingAPropertyException(\n                    ((SInvalidPageZipMissingAPropertyException) e).getFields());\n        }\n        if (e instanceof SInvalidPageZipInconsistentException) {\n            return new InvalidPageZipInconsistentException(e.getMessage(), e);\n        }\n        if (e instanceof SInvalidPageZipMissingIndexException) {\n            return new InvalidPageZipMissingIndexException();\n        }\n        return new InvalidPageZipContentException(e.getMessage(), e);\n    }\n\n    public Page getPageByNameAndProcessDefinition(String name, long processDefinitionId) throws PageNotFoundException {\n        try {\n            final SPage sPage = pageService.getPageByNameAndProcessDefinitionId(name, processDefinitionId);\n            if (sPage == null) {\n                throw new PageNotFoundException(name, PageAttribute.NAME);\n            }\n            return convertToPage(sPage);\n        } catch (final SBonitaReadException e) {\n            throw new PageNotFoundException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/platform/PlatformInformationAPIImpl.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.platform;\n\nimport static java.lang.String.valueOf;\nimport static org.bonitasoft.engine.execution.ProcessStarterVerifierImpl.LIMIT;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.impl.AvailableInMaintenanceMode;\nimport org.bonitasoft.engine.api.platform.PlatformInformationAPI;\nimport org.bonitasoft.engine.execution.ProcessStarterVerifier;\nimport org.bonitasoft.engine.service.ServiceAccessorSingleton;\n\n/**\n * Provides runtime information about the platform.\n * Information returned is dependent on the edition (Community or Subscription)\n * and the platform configuration / license.\n *\n * @author Emmanuel Duchastenier\n */\n@AvailableInMaintenanceMode\npublic class PlatformInformationAPIImpl implements PlatformInformationAPI {\n\n    private ProcessStarterVerifier processStarterVerifier;\n\n    public PlatformInformationAPIImpl() {\n        // Keep this empty constructor for compatibility with the APIAccessResolverImpl\n    }\n\n    PlatformInformationAPIImpl(ProcessStarterVerifier processStarterVerifier) {\n        this.processStarterVerifier = processStarterVerifier;\n    }\n\n    @Override\n    public Map<String, String> getPlatformInformation() {\n        if (processStarterVerifier == null) {\n            this.processStarterVerifier = ServiceAccessorSingleton.getInstance().getProcessStarterVerifier();\n        }\n        return Map.of(\n                \"edition\", \"Community\",\n                \"caseCounter\", valueOf(processStarterVerifier.getCurrentNumberOfStartedProcessInstances()),\n                \"caseCounterLimit\", valueOf(LIMIT));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/profile/ProfileAPIDelegate.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.profile;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.impl.transaction.profile.CreateProfileMember;\nimport org.bonitasoft.engine.api.impl.transaction.profile.ProfileMemberUtils;\nimport org.bonitasoft.engine.api.utils.VisibleForTesting;\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.command.SGroupProfileMemberAlreadyExistsException;\nimport org.bonitasoft.engine.command.SRoleProfileMemberAlreadyExistsException;\nimport org.bonitasoft.engine.command.SUserMembershipProfileMemberAlreadyExistsException;\nimport org.bonitasoft.engine.command.SUserProfileMemberAlreadyExistsException;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.exception.*;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.MemberType;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.profile.*;\nimport org.bonitasoft.engine.profile.exception.profile.SProfileNotFoundException;\nimport org.bonitasoft.engine.profile.exception.profilemember.SProfileMemberNotFoundException;\nimport org.bonitasoft.engine.profile.model.SProfileMember;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\nimport org.bonitasoft.engine.search.profile.SearchProfileMembersForProfile;\nimport org.bonitasoft.engine.search.profile.SearchProfiles;\nimport org.bonitasoft.engine.service.ModelConvertor;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.ServiceAccessorSingleton;\nimport org.bonitasoft.engine.session.SessionService;\n\npublic class ProfileAPIDelegate {\n\n    private static ProfileAPIDelegate profileAPIDelegate;\n\n    protected ProfileService profileService;\n\n    protected ApplicationService applicationService;\n\n    private SearchEntitiesDescriptor searchEntitiesDescriptor;\n    private IdentityService identityService;\n\n    @VisibleForTesting\n    protected ProfileAPIDelegate(ProfileService profileService, IdentityService identityService,\n            SearchEntitiesDescriptor searchEntitiesDescriptor, ApplicationService applicationService) {\n        this.profileService = profileService;\n        this.identityService = identityService;\n        this.searchEntitiesDescriptor = searchEntitiesDescriptor;\n        this.applicationService = applicationService;\n    }\n\n    public static ServiceAccessor getServiceAccessor() {\n        return ServiceAccessorSingleton.getInstance();\n    }\n\n    public static ProfileAPIDelegate getInstance() {\n        if (profileAPIDelegate == null) {\n            ServiceAccessor serviceAccessor = getServiceAccessor();\n            profileAPIDelegate = new ProfileAPIDelegate(serviceAccessor.getProfileService(),\n                    serviceAccessor.getIdentityService(),\n                    serviceAccessor.getSearchEntitiesDescriptor(),\n                    serviceAccessor.getApplicationService());\n        }\n        return profileAPIDelegate;\n    }\n\n    public SearchResult<Profile> searchProfiles(final SearchOptions options) throws SearchException {\n        final SearchProfiles searchProfileTransaction = new SearchProfiles(profileService,\n                searchEntitiesDescriptor.getSearchProfileDescriptor(), options);\n        try {\n            searchProfileTransaction.execute();\n            return searchProfileTransaction.getResult();\n        } catch (final SBonitaException sbe) {\n            throw new SearchException(sbe);\n        }\n    }\n\n    public Map<Long, Long> getNumberOfProfileMembers(final List<Long> profileIds) {\n        try {\n            final List<SProfileMember> listOfProfileMembers = profileService.getProfileMembers(profileIds);\n            final Map<Long, SProfileMember> profileMembers = new HashMap<>();\n            final Map<Long, Long> result = new HashMap<>();\n            for (final SProfileMember p : listOfProfileMembers) {\n                profileMembers.put(p.getProfileId(), p);\n            }\n\n            for (final Long obj : profileMembers.keySet()) {\n                long number = 0;\n                for (final SProfileMember listOfProfileMember : listOfProfileMembers) {\n                    final long tempProfileId = listOfProfileMember.getProfileId();\n                    if (obj == tempProfileId) {\n                        number++;\n                    }\n                }\n                result.put(obj, number);\n            }\n            return result;\n        } catch (final SBonitaException sbe) {\n            throw new RetrieveException(sbe);\n        }\n    }\n\n    public Profile getProfile(final long id) throws ProfileNotFoundException {\n        try {\n            return ModelConvertor.toProfile(profileService.getProfile(id));\n        } catch (final SProfileNotFoundException e) {\n            throw new ProfileNotFoundException(e);\n        }\n    }\n\n    public List<Profile> getProfilesForUser(final long userId, final int startIndex, final int maxResults,\n            final ProfileCriterion criterion) {\n        if (SessionService.SYSTEM_ID == userId) { // It's the tenant admin user\n            return Collections.emptyList();\n        }\n        try {\n            return ModelConvertor.toProfiles(\n                    profileService.searchProfilesOfUser(userId, startIndex, maxResults, criterion.getField(),\n                            OrderByType.valueOf(criterion.getOrder().name())));\n        } catch (final SBonitaReadException e) {\n            throw new RetrieveException(e);\n        }\n    }\n\n    public SearchResult<ProfileMember> searchProfileMembers(final String memberType, final SearchOptions options)\n            throws SearchException {\n        try {\n            final SearchProfileMembersForProfile searchProfileMembersForProfile = new SearchProfileMembersForProfile(\n                    getProfileMemberQuerySuffix(memberType),\n                    profileService, getProfileMemberDescriptor(searchEntitiesDescriptor, memberType), options);\n            searchProfileMembersForProfile.execute();\n            return searchProfileMembersForProfile.getResult();\n        } catch (final SBonitaException sbe) {\n            throw new SearchException(sbe);\n        }\n    }\n\n    public ProfileMember createProfileMember(final Long profileId, final Long userId, final Long groupId,\n            final Long roleId) throws CreationException {\n        try {\n            final MemberType memberType = getMemberType(userId, groupId, roleId);\n            final CreateProfileMember createProfileMember = new CreateProfileMember(profileService, identityService,\n                    profileId, userId, groupId, roleId,\n                    memberType);\n            try {\n                checkIfProfileMemberExists(profileId, userId, groupId, roleId,\n                        memberType);\n            } catch (final SBonitaException e1) {\n                throw new AlreadyExistsException(e1);\n            }\n            createProfileMember.execute();\n            return convertToProfileMember(createProfileMember);\n        } catch (final SBonitaException e) {\n            throw new CreationException(e);\n        }\n\n    }\n\n    public void deleteProfileMember(final Long profileMemberId) throws DeletionException {\n        try {\n            // add a lock because the update profile call getProfile then update profile -> deadlock...\n            final SProfileMember profileMember = profileService.getProfileMemberWithoutDisplayName(profileMemberId);\n            profileService.updateProfileMetaData(profileMember.getProfileId());\n            profileService.deleteProfileMember(profileMember.getId());\n        } catch (final SProfileMemberNotFoundException spmnfe) {\n            throw new DeletionException(new ProfileMemberNotFoundException(spmnfe));\n        } catch (final SBonitaException e) {\n            throw new DeletionException(e);\n        }\n    }\n\n    protected ProfileMember convertToProfileMember(final CreateProfileMember createProfileMember) {\n        return ModelConvertor.toProfileMember(createProfileMember.getResult());\n    }\n\n    protected void checkIfProfileMemberExists(final Long profileId,\n            final Long userId, final Long groupId, final Long roleId, final MemberType memberType)\n            throws SBonitaException {\n        final SearchEntityDescriptor searchDescriptor = searchEntitiesDescriptor.getSearchProfileMemberUserDescriptor();\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 1);\n        searchOptionsBuilder.filter(ProfileMemberSearchDescriptor.PROFILE_ID, profileId);\n\n        SearchProfileMembersForProfile searchProfileMembersForProfile;\n\n        switch (memberType) {\n            case USER:\n                searchProfileMembersForProfile = new SearchProfileMembersForProfile(\"ForUser\", profileService,\n                        searchDescriptor, searchOptionsBuilder.done());\n                searchOptionsBuilder.filter(ProfileMemberSearchDescriptor.USER_ID, userId);\n                searchProfileMembersForProfile.execute();\n                if (searchProfileMembersForProfile.getResult().getCount() != 0) {\n                    throw new SUserProfileMemberAlreadyExistsException(\n                            \"A profileMember with userId \\\"\" + userId + \"\\\" already exists\");\n                }\n                break;\n            case GROUP:\n                searchProfileMembersForProfile = new SearchProfileMembersForProfile(\"ForGroup\", profileService,\n                        searchDescriptor, searchOptionsBuilder.done());\n                searchOptionsBuilder.filter(ProfileMemberSearchDescriptor.GROUP_ID, groupId);\n                searchProfileMembersForProfile.execute();\n\n                if (searchProfileMembersForProfile.getResult().getCount() != 0) {\n                    throw new SGroupProfileMemberAlreadyExistsException(\n                            \"A profileMember with groupId \\\"\" + groupId + \"\\\" already exists\");\n                }\n                break;\n            case ROLE:\n                searchProfileMembersForProfile = new SearchProfileMembersForProfile(\"ForRole\", profileService,\n                        searchDescriptor, searchOptionsBuilder.done());\n                searchOptionsBuilder.filter(ProfileMemberSearchDescriptor.ROLE_ID, roleId);\n                searchProfileMembersForProfile.execute();\n\n                if (searchProfileMembersForProfile.getResult().getCount() != 0) {\n                    throw new SRoleProfileMemberAlreadyExistsException(\n                            \"A profileMember with roleId \\\"\" + roleId + \"\\\" already exists\");\n                }\n                break;\n            default:\n                searchProfileMembersForProfile = new SearchProfileMembersForProfile(\"ForRoleAndGroup\", profileService,\n                        searchDescriptor,\n                        searchOptionsBuilder.done());\n                searchOptionsBuilder.filter(ProfileMemberSearchDescriptor.ROLE_ID, roleId);\n                searchOptionsBuilder.filter(ProfileMemberSearchDescriptor.GROUP_ID, groupId);\n                searchProfileMembersForProfile.execute();\n\n                if (searchProfileMembersForProfile.getResult().getCount() != 0) {\n                    throw new SUserMembershipProfileMemberAlreadyExistsException(\n                            \"A profileMember with groupId \\\"\" + groupId + \"\\\" and roleId \\\"\" + roleId\n                                    + \"\\\" already exists\");\n                }\n                break;\n        }\n    }\n\n    private boolean isPositiveLong(final Long value) {\n        return value != null && value > 0;\n    }\n\n    public MemberType getMemberType(final Long userId, final Long groupId, final Long roleId) throws CreationException {\n        MemberType memberType;\n        if (isPositiveLong(userId)) {\n            memberType = MemberType.USER;\n        } else if (isPositiveLong(groupId) && !isPositiveLong(roleId)) {\n            memberType = MemberType.GROUP;\n        } else if (isPositiveLong(roleId) && !isPositiveLong(groupId)) {\n            memberType = MemberType.ROLE;\n        } else if (isPositiveLong(roleId) && isPositiveLong(groupId)) {\n            memberType = MemberType.MEMBERSHIP;\n        } else {\n            throw new CreationException(String.format(\"Parameters map must contain at least one of entries: %s, %s, %s\",\n                    ProfileMemberUtils.USER_ID, ProfileMemberUtils.GROUP_ID, ProfileMemberUtils.ROLE_ID));\n        }\n        return memberType;\n    }\n\n    private SearchEntityDescriptor getProfileMemberDescriptor(final SearchEntitiesDescriptor searchEntitiesDescriptor,\n            final String memberType)\n            throws SBonitaReadException {\n        if (ProfileMemberUtils.USER_TYPE.equals(memberType)) {\n            return searchEntitiesDescriptor.getSearchProfileMemberUserDescriptor();\n        } else if (ProfileMemberUtils.GROUP_TYPE.equals(memberType)) {\n            return searchEntitiesDescriptor.getSearchProfileMemberGroupDescriptor();\n        } else if (ProfileMemberUtils.ROLE_TYPE.equals(memberType)) {\n            return searchEntitiesDescriptor.getSearchProfileMemberRoleDescriptor();\n        } else if (ProfileMemberUtils.ROLE_AND_GROUP_TYPE.equals(memberType)) {\n            return searchEntitiesDescriptor.getSearchProfileMemberRoleAndGroupDescriptor();\n        } else {\n            throw new SBonitaReadException(\"Member type must be equalse to user,group,role or roleAndGroup.\");\n        }\n    }\n\n    private String getProfileMemberQuerySuffix(final String memberType) throws SBonitaReadException {\n        if (ProfileMemberUtils.USER_TYPE.equals(memberType)) {\n            return ProfileMemberUtils.USER_SUFFIX;\n        } else if (ProfileMemberUtils.GROUP_TYPE.equals(memberType)) {\n            return ProfileMemberUtils.GROUP_SUFFIX;\n        } else if (ProfileMemberUtils.ROLE_TYPE.equals(memberType)) {\n            return ProfileMemberUtils.ROLE_SUFFIX;\n        } else if (ProfileMemberUtils.ROLE_AND_GROUP_TYPE.equals(memberType)) {\n            return ProfileMemberUtils.ROLE_AND_GROUP_SUFFIX;\n        } else {\n            throw new SBonitaReadException(\"Member type must be equalse to user,group,role or roleAndGroup.\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/resolver/ActorBusinessArchiveArtifactManager.java",
    "content": "/**\n * Copyright (C) 2015 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.resolver;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.actor.mapping.SActorDeletionException;\nimport org.bonitasoft.engine.actor.mapping.model.SActor;\nimport org.bonitasoft.engine.actor.mapping.model.SActorMember;\nimport org.bonitasoft.engine.api.impl.transaction.actor.ExportActorMapping;\nimport org.bonitasoft.engine.api.impl.transaction.actor.ImportActorMapping;\nimport org.bonitasoft.engine.bpm.actor.ActorMappingImportException;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.bar.actorMapping.ActorMapping;\nimport org.bonitasoft.engine.bpm.process.Problem;\nimport org.bonitasoft.engine.bpm.process.Problem.Level;\nimport org.bonitasoft.engine.bpm.process.impl.internal.ProblemImpl;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.core.process.definition.model.SActorDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\n@Slf4j\npublic class ActorBusinessArchiveArtifactManager implements BusinessArchiveArtifactManager {\n\n    private final ActorMappingService actorMappingService;\n    private final IdentityService identityService;\n\n    public ActorBusinessArchiveArtifactManager(ActorMappingService actorMappingService,\n            IdentityService identityService) {\n        this.actorMappingService = actorMappingService;\n        this.identityService = identityService;\n\n    }\n\n    @Override\n    public boolean deploy(final BusinessArchive businessArchive, final SProcessDefinition processDefinition)\n            throws ActorMappingImportException {\n        final Set<SActorDefinition> actors = processDefinition.getActors();\n        final Set<SActor> sActors = new HashSet<>(actors.size() + 1);\n        final SActorDefinition actorInitiator = processDefinition.getActorInitiator();\n        String initiatorName = null;\n        if (actorInitiator != null) {\n            initiatorName = actorInitiator.getName();\n            sActors.add(SActor.builder().name(initiatorName).scopeId(processDefinition.getId()).initiator(true)\n                    .description(actorInitiator.getDescription()).build());\n        }\n        for (final SActorDefinition actor : actors) {\n            if (initiatorName == null || !initiatorName.equals(actor.getName())) {\n                sActors.add((SActor.builder().name(actor.getName()).scopeId(processDefinition.getId()).initiator(false)\n                        .description(actor.getDescription())).build());\n            }\n        }\n        try {\n            actorMappingService.addActors(sActors);\n            ActorMapping actorMapping = businessArchive.getActorMapping();\n            if (actorMapping != null) {\n                final ImportActorMapping importActorMapping = new ImportActorMapping(actorMappingService,\n                        identityService);\n                importActorMapping.execute(actorMapping, processDefinition.getId());\n            }\n            // ignored\n        } catch (SBonitaException e) {\n            log.warn(\"Error in importing the actor-mapping: {}\", e.getMessage());\n        }\n        return checkResolution(actorMappingService, processDefinition.getId()).isEmpty();\n    }\n\n    @Override\n    public List<Problem> checkResolution(final SProcessDefinition processDefinition) {\n        final long processDefinitionId = processDefinition.getId();\n        return checkResolution(actorMappingService, processDefinitionId);\n    }\n\n    @Override\n    public void delete(SProcessDefinition processDefinition) throws SObjectModificationException {\n        try {\n            actorMappingService.deleteActors(processDefinition.getId());\n        } catch (SActorDeletionException e) {\n            throw new SObjectModificationException(\n                    \"Unable to delete actors of the process definition <\" + processDefinition.getName() + \">\", e);\n        }\n    }\n\n    @Override\n    public void exportToBusinessArchive(long processDefinitionId, BusinessArchiveBuilder businessArchiveBuilder)\n            throws SBonitaException {\n        final ExportActorMapping exportActorMapping = new ExportActorMapping(actorMappingService, identityService,\n                processDefinitionId);\n        businessArchiveBuilder.setActorMapping(exportActorMapping.getActorMapping());\n    }\n\n    public List<Problem> checkResolution(final ActorMappingService actorMappingService,\n            final long processDefinitionId) {\n        try {\n            final List<Problem> problems = new ArrayList<Problem>();\n            QueryOptions queryOptions = new QueryOptions(0, 100, SActor.class, \"id\", OrderByType.ASC);\n            List<SActor> actors = actorMappingService.getActors(processDefinitionId, queryOptions);\n            while (!actors.isEmpty()) {\n                for (final SActor sActor : actors) {\n                    checkIfAActorMemberExists(actorMappingService, problems, sActor);\n                }\n                queryOptions = QueryOptions.getNextPage(queryOptions);\n                actors = actorMappingService.getActors(processDefinitionId, queryOptions);\n            }\n            return problems;\n        } catch (final SBonitaReadException e) {\n            return Collections.singletonList(\n                    (Problem) new ProblemImpl(Level.ERROR, processDefinitionId, \"process\", \"Unable to read actors\"));\n        }\n    }\n\n    private void checkIfAActorMemberExists(final ActorMappingService actorMappingService, final List<Problem> problems,\n            final SActor sActor)\n            throws SBonitaReadException {\n        final List<SActorMember> actorMembers = actorMappingService.getActorMembers(sActor.getId(), 0, 1);\n        if (actorMembers.isEmpty()) {\n            final Problem problem = new ProblemImpl(Level.ERROR, sActor.getId(), \"actor\", \"Actor '\" + sActor.getName()\n                    + \"' does not contain any members\");\n            problems.add(problem);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/resolver/BARResourceArtifactManager.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.resolver;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.bar.BarResource;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.resources.BARResourceType;\nimport org.bonitasoft.engine.resources.ProcessResourcesService;\nimport org.bonitasoft.engine.resources.SBARResource;\n\n/**\n * @author Baptiste Mesta\n */\npublic abstract class BARResourceArtifactManager implements BusinessArchiveArtifactManager {\n\n    protected final ProcessResourcesService processResourcesService;\n\n    public BARResourceArtifactManager(ProcessResourcesService processResourcesService) {\n        this.processResourcesService = processResourcesService;\n    }\n\n    void saveResources(BusinessArchive businessArchive, SProcessDefinition processDefinition, String folder,\n            BARResourceType type) throws SRecorderException {\n        final Map<String, byte[]> resources = businessArchive.getResources(\"^\" + folder + \"/.*$\");\n        for (Map.Entry<String, byte[]> entry : resources.entrySet()) {\n            processResourcesService.add(processDefinition.getId(), entry.getKey().substring((folder + \"/\").length()),\n                    type, entry.getValue());\n        }\n    }\n\n    void exportResourcesToBusinessArchive(long processDefinitionId, BusinessArchiveBuilder businessArchiveBuilder,\n            BARResourceType type) throws SBonitaReadException {\n        List<SBARResource> resources;\n        int from = 0;\n        final int pageSize = 10;\n        do {\n            resources = processResourcesService.get(processDefinitionId, type, from, pageSize);\n            addToBusinessArchive(businessArchiveBuilder, resources);\n            from += pageSize;\n        } while (resources.size() == pageSize);\n    }\n\n    void addToBusinessArchive(BusinessArchiveBuilder businessArchiveBuilder, List<SBARResource> resources) {\n        for (SBARResource resource : resources) {\n            addToBusinessArchive(businessArchiveBuilder, new BarResource(resource.getName(), resource.getContent()));\n        }\n    }\n\n    abstract void addToBusinessArchive(BusinessArchiveBuilder businessArchiveBuilder, BarResource resource);\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/resolver/BusinessArchiveArtifactManager.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.resolver;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.process.Problem;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.SRecorderException;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic interface BusinessArchiveArtifactManager {\n\n    /**\n     * deploy a dedicated part of the process\n     * e.g. load connectors\n     * Must throw an exception is something is not resolved in the process\n     *\n     * @param businessArchive\n     *        the business archive containing the dependency\n     * @param processDefinition\n     *        the process definition\n     * @return true if the process is resolved for this deployer, false otherwise\n     * @throws BonitaException\n     */\n    boolean deploy(BusinessArchive businessArchive, SProcessDefinition processDefinition)\n            throws BonitaException, SBonitaException;\n\n    /**\n     * @param processDefinition\n     *        the process definition\n     * @return\n     *         a list of resolution problems or an empty list is there is no issue for this artifact\n     */\n    List<Problem> checkResolution(final SProcessDefinition processDefinition);\n\n    void delete(final SProcessDefinition processDefinition)\n            throws SObjectModificationException, SBonitaReadException, SRecorderException;\n\n    void exportToBusinessArchive(long processDefinitionId, BusinessArchiveBuilder businessArchiveBuilder)\n            throws SBonitaException;\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/resolver/BusinessArchiveArtifactsManager.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.resolver;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.bar.InvalidBusinessArchiveFormatException;\nimport org.bonitasoft.engine.bpm.process.ConfigurationState;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.Problem;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionDeployInfoUpdateBuilderFactory;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.service.ServiceAccessor;\n\n/**\n * Handles the resolution of Process Dependencies. A process can have a list of <code>ProcessDependencyResolver</code>s\n * which validates different aspects of the\n * process to validate (or \"resolve\")\n *\n * @author Emmanuel Duchastenier\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\n@Slf4j\npublic class BusinessArchiveArtifactsManager {\n\n    private final List<BusinessArchiveArtifactManager> dependencyResolvers;\n\n    public BusinessArchiveArtifactsManager(final List<BusinessArchiveArtifactManager> dependencyResolvers) {\n        this.dependencyResolvers = dependencyResolvers;\n    }\n\n    public boolean resolveDependencies(final BusinessArchive businessArchive, final SProcessDefinition sDefinition) {\n        final List<BusinessArchiveArtifactManager> artifactManagers = getArtifactManagers();\n        boolean resolved = true;\n        for (final BusinessArchiveArtifactManager artifactManager : artifactManagers) {\n            try {\n                resolved &= artifactManager.deploy(businessArchive, sDefinition);\n                if (!resolved) {\n                    for (Problem problem : artifactManager.checkResolution(sDefinition)) {\n                        log.info(problem.getDescription());\n                    }\n                }\n            } catch (BonitaException | SBonitaException e) {\n                // not logged, we will check later why the process is not resolved\n                log.error(\"Unable to deploy process\", e);\n                resolved = false;\n            }\n        }\n        return resolved;\n    }\n\n    public void resolveDependenciesForAllProcesses(ServiceAccessor serviceAccessor) {\n        try {\n            List<Long> processDefinitionIds = serviceAccessor.getProcessDefinitionService().getProcessDefinitionIds(0,\n                    Integer.MAX_VALUE);\n            resolveDependencies(processDefinitionIds, serviceAccessor);\n        } catch (SBonitaReadException e) {\n            log.error(\"Unable to retrieve tenant process definitions, dependency resolution aborted\");\n        }\n    }\n\n    private void resolveDependencies(final List<Long> processDefinitionIds, final ServiceAccessor serviceAccessor) {\n        for (Long id : processDefinitionIds) {\n            resolveDependencies(id, serviceAccessor);\n        }\n    }\n\n    public void deleteDependencies(final SProcessDefinition processDefinition)\n            throws SObjectModificationException, SBonitaReadException, SRecorderException {\n        final List<BusinessArchiveArtifactManager> resolvers = getArtifactManagers();\n        for (BusinessArchiveArtifactManager resolver : resolvers) {\n            resolver.delete(processDefinition);\n        }\n    }\n\n    /*\n     * Done in a separated transaction\n     * We try here to check if now the process is resolved so it must not be done in the same transaction that did the\n     * modification\n     * this does not throw exception, it only log because it can be retried after.\n     */\n    public void resolveDependencies(final long processDefinitionId, final ServiceAccessor serviceAccessor) {\n        resolveDependencies(processDefinitionId, serviceAccessor,\n                getArtifactManagers().toArray(new BusinessArchiveArtifactManager[0]));\n    }\n\n    public void resolveDependencies(final long processDefinitionId, final ServiceAccessor serviceAccessor,\n            final BusinessArchiveArtifactManager... resolvers) {\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        try {\n            boolean resolved = true;\n            for (final BusinessArchiveArtifactManager dependencyResolver : resolvers) {\n                final SProcessDefinition processDefinition = processDefinitionService\n                        .getProcessDefinition(processDefinitionId);\n                resolved &= dependencyResolver.checkResolution(processDefinition).isEmpty();\n            }\n            changeResolutionStatus(processDefinitionId, processDefinitionService, resolved);\n        } catch (final SBonitaException e) {\n            if (log.isDebugEnabled()) {\n                log.debug(e.toString());\n            }\n            log.warn(\"Unable to resolve dependencies after they were modified because of \" + e.getMessage()\n                    + \". Please retry it manually\");\n        }\n    }\n\n    public void changeResolutionStatus(final long processDefinitionId,\n            final ProcessDefinitionService processDefinitionService,\n            final boolean resolved) throws SBonitaException {\n        final SProcessDefinitionDeployInfo processDefinitionDeployInfo = processDefinitionService\n                .getProcessDeploymentInfo(processDefinitionId);\n        if (resolved) {\n            if (ConfigurationState.UNRESOLVED.name().equals(processDefinitionDeployInfo.getConfigurationState())) {\n                processDefinitionService.resolveProcess(processDefinitionId);\n            }\n        } else {\n            if (ConfigurationState.RESOLVED.name().equals(processDefinitionDeployInfo.getConfigurationState())) {\n                final EntityUpdateDescriptor updateDescriptor = BuilderFactory\n                        .get(SProcessDefinitionDeployInfoUpdateBuilderFactory.class).createNewInstance()\n                        .updateConfigurationState(ConfigurationState.UNRESOLVED).done();\n                processDefinitionService.updateProcessDefinitionDeployInfo(processDefinitionId, updateDescriptor);\n            }\n        }\n    }\n\n    public List<BusinessArchiveArtifactManager> getArtifactManagers() {\n        return dependencyResolvers;\n    }\n\n    public BusinessArchive exportBusinessArchive(long processDefinitionId,\n            DesignProcessDefinition designProcessDefinition)\n            throws InvalidBusinessArchiveFormatException, SBonitaException {\n        final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive();\n        businessArchiveBuilder.setProcessDefinition(designProcessDefinition);\n        for (BusinessArchiveArtifactManager businessArchiveArtifactManager : getArtifactManagers()) {\n            businessArchiveArtifactManager.exportToBusinessArchive(processDefinitionId, businessArchiveBuilder);\n        }\n        return businessArchiveBuilder.done();\n    }\n\n    public List<Problem> getProcessResolutionProblems(SProcessDefinition processDefinition) {\n        return getArtifactManagers().stream().flatMap(resolver -> resolver.checkResolution(processDefinition).stream())\n                .collect(Collectors.toList());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/resolver/BusinessDataBusinessArchiveArtifactManager.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.resolver;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.process.Problem;\nimport org.bonitasoft.engine.bpm.process.Problem.Level;\nimport org.bonitasoft.engine.bpm.process.impl.internal.ProblemImpl;\nimport org.bonitasoft.engine.business.data.BusinessDataRepository;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.core.process.definition.model.SBusinessDataDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class BusinessDataBusinessArchiveArtifactManager implements BusinessArchiveArtifactManager {\n\n    private final BusinessDataRepository businessDataRepository;\n\n    public BusinessDataBusinessArchiveArtifactManager(BusinessDataRepository businessDataRepository) {\n        this.businessDataRepository = businessDataRepository;\n    }\n\n    @Override\n    public boolean deploy(final BusinessArchive businessArchive, final SProcessDefinition processDefinition) {\n        return checkResolution(processDefinition).isEmpty();\n    }\n\n    @Override\n    public List<Problem> checkResolution(final SProcessDefinition processDefinition) {\n        final List<SBusinessDataDefinition> businessDataDefinitions = processDefinition.getProcessContainer()\n                .getBusinessDataDefinitions();\n        if (businessDataDefinitions.isEmpty()) {\n            return Collections.emptyList();\n        }\n        final List<Problem> problems = new ArrayList<>();\n        final Set<String> entityClassNames = businessDataRepository.getEntityClassNames();\n        for (final SBusinessDataDefinition sBusinessDataDefinition : businessDataDefinitions) {\n            final String className = sBusinessDataDefinition.getClassName();\n            if (!entityClassNames.contains(className)) {\n                final Problem problem = new ProblemImpl(Level.ERROR, sBusinessDataDefinition.getName(), \"business data\",\n                        String.format(\n                                \"Unknown Business Object type '%s'. Deploy a Business Data Model defining this type first.\",\n                                className));\n                problems.add(problem);\n            }\n        }\n        return problems;\n    }\n\n    @Override\n    public void delete(SProcessDefinition processDefinition) throws SObjectModificationException {\n\n    }\n\n    @Override\n    public void exportToBusinessArchive(long processDefinitionId, BusinessArchiveBuilder businessArchiveBuilder)\n            throws SBonitaException {\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/resolver/ClasspathArtifactManager.java",
    "content": "/**\n * Copyright (C) 2015 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.resolver;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.bar.BarResource;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.process.Problem;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.connector.ConnectorException;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.dependency.DependencyService;\nimport org.bonitasoft.engine.dependency.SDependencyException;\nimport org.bonitasoft.engine.dependency.model.AbstractSDependency;\nimport org.bonitasoft.engine.dependency.model.SDependencyMapping;\nimport org.bonitasoft.engine.dependency.model.ScopeType;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.QueryOptions;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class ClasspathArtifactManager implements BusinessArchiveArtifactManager {\n\n    private final DependencyService dependencyService;\n\n    public ClasspathArtifactManager(DependencyService dependencyService) {\n        this.dependencyService = dependencyService;\n    }\n\n    @Override\n    public boolean deploy(final BusinessArchive businessArchive, final SProcessDefinition processDefinition)\n            throws ConnectorException, SBonitaException {\n        final Map<String, byte[]> resources = businessArchive.getResources(\"^classpath/.*$\");\n\n        // remove the classpath/ on path of dependencies\n        final Map<String, byte[]> resourcesWithRealName = new HashMap<>(resources.size());\n        for (final Map.Entry<String, byte[]> resource : resources.entrySet()) {\n            final String name = resource.getKey().substring(10);\n            final byte[] jarContent = resource.getValue();\n            resourcesWithRealName.put(name, jarContent);\n        }\n        addDependencies(resourcesWithRealName, dependencyService, processDefinition.getId());\n        return true;\n    }\n\n    @Override\n    public List<Problem> checkResolution(final SProcessDefinition processDefinition) {\n        return Collections.emptyList();\n    }\n\n    @Override\n    public void delete(SProcessDefinition processDefinition) throws SObjectModificationException {\n        try {\n            dependencyService.deleteDependencies(processDefinition.getId(), ScopeType.PROCESS);\n        } catch (SDependencyException e) {\n            throw new SObjectModificationException(\n                    \"Unable to delete dependencies of process \" + processDefinition.getId(), e);\n        }\n\n    }\n\n    @Override\n    public void exportToBusinessArchive(long processDefinitionId, BusinessArchiveBuilder businessArchiveBuilder)\n            throws SBonitaException {\n        final ArrayList<FilterOption> filters = new ArrayList<>();\n        filters.add(new FilterOption(SDependencyMapping.class, \"artifactId\", processDefinitionId));\n        filters.add(new FilterOption(SDependencyMapping.class, \"artifactType\", ScopeType.PROCESS.name()));\n        final List<SDependencyMapping> dependencyMappings = dependencyService\n                .getDependencyMappings(new QueryOptions(0, Integer.MAX_VALUE, null, filters, null));\n        for (SDependencyMapping dependencyMapping : dependencyMappings) {\n            final AbstractSDependency dependency = dependencyService.getDependency(dependencyMapping.getDependencyId());\n            businessArchiveBuilder\n                    .addClasspathResource(new BarResource(dependency.getFileName(), dependency.getValue()));\n        }\n    }\n\n    private void addDependencies(final Map<String, byte[]> resources, final DependencyService dependencyService,\n            final long processDefinitionId) throws SBonitaException {\n        final List<Long> dependencyIds = getDependencyMappingsOfProcess(dependencyService, processDefinitionId);\n        final List<String> dependencies = getDependenciesOfProcess(dependencyService, dependencyIds);\n\n        for (Map.Entry<String, byte[]> entry : resources.entrySet()) {\n            if (!dependencies.contains(getDependencyName(processDefinitionId, entry.getKey()))) {\n                final String name = entry.getKey();\n                dependencyService.createMappedDependency(name, entry.getValue(), name /* it is the real filename */,\n                        processDefinitionId, ScopeType.PROCESS);\n            }\n        }\n    }\n\n    private String getDependencyName(final long processDefinitionId, final String name) {\n        return processDefinitionId + \"_\" + name;\n    }\n\n    private List<String> getDependenciesOfProcess(final DependencyService dependencyService,\n            final List<Long> dependencyIds) throws SBonitaException {\n        if (dependencyIds.isEmpty()) {\n            return Collections.emptyList();\n        }\n        final List<AbstractSDependency> dependencies = dependencyService.getDependencies(dependencyIds);\n        final ArrayList<String> dependencyNames = new ArrayList<>(dependencies.size());\n        for (final AbstractSDependency sDependency : dependencies) {\n            dependencyNames.add(sDependency.getName());\n        }\n        return dependencyNames;\n    }\n\n    private List<Long> getDependencyMappingsOfProcess(final DependencyService dependencyService,\n            final long processDefinitionId) throws SDependencyException {\n        final List<Long> dependencyIds = new ArrayList<>();\n        int fromIndex = 0;\n        final int pageSize = 100;\n        List<Long> currentPage;\n        do {\n            currentPage = dependencyService.getDependencyIds(processDefinitionId, ScopeType.PROCESS, fromIndex,\n                    pageSize);\n            dependencyIds.addAll(currentPage);\n            fromIndex += pageSize;\n        } while (currentPage.size() == pageSize);\n        return dependencyIds;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/resolver/ConnectorBusinessArchiveArtifactManager.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.resolver;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.bpm.bar.BarResource;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.process.Problem;\nimport org.bonitasoft.engine.bpm.process.Problem.Level;\nimport org.bonitasoft.engine.bpm.process.impl.internal.ProblemImpl;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.connector.ConnectorException;\nimport org.bonitasoft.engine.core.connector.ConnectorService;\nimport org.bonitasoft.engine.core.connector.exception.SConnectorException;\nimport org.bonitasoft.engine.core.process.definition.model.SConnectorDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.resources.SBARResource;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class ConnectorBusinessArchiveArtifactManager implements BusinessArchiveArtifactManager {\n\n    public static final String CONNECTOR = \"connector\";\n    public static final int BATCH_SIZE = 10;\n    private final ConnectorService connectorService;\n\n    public ConnectorBusinessArchiveArtifactManager(ConnectorService connectorService) {\n        this.connectorService = connectorService;\n    }\n\n    void addToBusinessArchive(BusinessArchiveBuilder businessArchiveBuilder, List<SBARResource> resources) {\n        for (SBARResource resource : resources) {\n            businessArchiveBuilder\n                    .addConnectorImplementation(new BarResource(resource.getName(), resource.getContent()));\n        }\n    }\n\n    @Override\n    public boolean deploy(final BusinessArchive businessArchive, final SProcessDefinition processDefinition)\n            throws ConnectorException, SRecorderException {\n        try {\n            final Map<String, byte[]> resources = businessArchive.getResources(\"^\" + CONNECTOR + \"/.*$\");\n            for (Map.Entry<String, byte[]> entry : resources.entrySet()) {\n                connectorService.addConnectorImplementation(processDefinition.getId(),\n                        entry.getKey().substring((CONNECTOR + \"/\").length()), entry.getValue());\n            }\n            return connectorService.loadConnectors(processDefinition)\n                    && checkAllConnectorsHaveImplementation(connectorService, processDefinition).isEmpty();\n        } catch (final SConnectorException e) {\n            throw new ConnectorException(e);\n        }\n    }\n\n    private List<Problem> checkAllConnectorsHaveImplementation(final ConnectorService connectorService,\n            final SProcessDefinition processDefinition) {\n        final List<SConnectorDefinition> processConnectors = processDefinition.getProcessContainer().getConnectors();\n        final List<Problem> problems = new ArrayList<Problem>();\n        if (processConnectors != null) {\n            for (final SConnectorDefinition sConnectorDefinition : processConnectors) {\n                try {\n                    connectorService.getConnectorImplementation(processDefinition.getId(),\n                            sConnectorDefinition.getConnectorId(),\n                            sConnectorDefinition.getVersion());\n                } catch (final SConnectorException e) {\n                    final Problem problem = new ProblemImpl(Level.ERROR, sConnectorDefinition.getName(), \"connector\",\n                            \"The process connector '\"\n                                    + sConnectorDefinition.getName() + \"' has no implementation\");\n                    problems.add(problem);\n                }\n            }\n        }\n        final Set<SFlowNodeDefinition> flowNodes = processDefinition.getProcessContainer().getFlowNodes();\n        if (flowNodes != null) {\n            for (final SFlowNodeDefinition sFlowNodeDefinition : flowNodes) {\n                final List<SConnectorDefinition> flowNodeConnectors = sFlowNodeDefinition.getConnectors();\n                if (flowNodeConnectors != null) {\n                    for (final SConnectorDefinition sConnectorDefinition : flowNodeConnectors) {\n                        try {\n                            connectorService.getConnectorImplementation(processDefinition.getId(),\n                                    sConnectorDefinition.getConnectorId(),\n                                    sConnectorDefinition.getVersion());\n                        } catch (final SConnectorException e) {\n                            final Problem problem = new ProblemImpl(Level.ERROR, sConnectorDefinition.getName(),\n                                    \"connector\", \"The connector '\"\n                                            + sConnectorDefinition.getName() + \"' of flow node '\"\n                                            + sFlowNodeDefinition.getName() + \"' has no implementation\");\n                            problems.add(problem);\n                        }\n                    }\n                }\n            }\n        }\n        return problems;\n    }\n\n    @Override\n    public List<Problem> checkResolution(final SProcessDefinition processDefinition) {\n        return checkAllConnectorsHaveImplementation(connectorService, processDefinition);\n    }\n\n    @Override\n    public void delete(SProcessDefinition processDefinition)\n            throws SObjectModificationException, SBonitaReadException, SRecorderException {\n        connectorService.removeConnectorImplementations(processDefinition.getId());\n    }\n\n    @Override\n    public void exportToBusinessArchive(long processDefinitionId, BusinessArchiveBuilder businessArchiveBuilder)\n            throws SBonitaException {\n        List<SBARResource> resources = getConnectorImplementations(processDefinitionId);\n        addToBusinessArchive(businessArchiveBuilder, resources);\n    }\n\n    List<SBARResource> getConnectorImplementations(long processDefinitionId) throws SBonitaReadException {\n        List<SBARResource> allResources = new ArrayList<>();\n        List<SBARResource> resources;\n        int from = 0;\n        do {\n            resources = connectorService.getConnectorImplementations(processDefinitionId, from, BATCH_SIZE);\n            from += BATCH_SIZE;\n            allResources.addAll(resources);\n        } while (resources.size() == BATCH_SIZE);\n        return allResources;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/resolver/DocumentInitialValueArtifactManager.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.resolver;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.bar.BarResource;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.process.Problem;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.resources.BARResourceType;\nimport org.bonitasoft.engine.resources.ProcessResourcesService;\n\n/**\n * @author Baptiste Mesta\n */\npublic class DocumentInitialValueArtifactManager extends BARResourceArtifactManager {\n\n    public static final String FOLDER = \"documents\";\n\n    public DocumentInitialValueArtifactManager(ProcessResourcesService processResourcesService) {\n        super(processResourcesService);\n    }\n\n    @Override\n    public boolean deploy(BusinessArchive businessArchive, SProcessDefinition processDefinition)\n            throws BonitaException, SBonitaException {\n        saveResources(businessArchive, processDefinition, FOLDER, BARResourceType.DOCUMENT);\n        return true;\n    }\n\n    @Override\n    public List<Problem> checkResolution(SProcessDefinition processDefinition) {\n        return Collections.emptyList();\n    }\n\n    @Override\n    public void delete(SProcessDefinition processDefinition)\n            throws SObjectModificationException, SBonitaReadException, SRecorderException {\n        processResourcesService.removeAll(processDefinition.getId(), BARResourceType.DOCUMENT);\n    }\n\n    @Override\n    public void exportToBusinessArchive(long processDefinitionId, BusinessArchiveBuilder businessArchiveBuilder)\n            throws SBonitaException {\n        exportResourcesToBusinessArchive(processDefinitionId, businessArchiveBuilder, BARResourceType.DOCUMENT);\n    }\n\n    @Override\n    void addToBusinessArchive(BusinessArchiveBuilder businessArchiveBuilder, BarResource resource) {\n        businessArchiveBuilder.addDocumentResource(resource);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/resolver/ExternalResourceArtifactManager.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.resolver;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.bar.BarResource;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.process.Problem;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.resources.BARResourceType;\nimport org.bonitasoft.engine.resources.ProcessResourcesService;\n\n/**\n * @author Baptiste Mesta\n */\npublic class ExternalResourceArtifactManager extends BARResourceArtifactManager {\n\n    public static final String RESOURCES = \"resources\";\n\n    public ExternalResourceArtifactManager(ProcessResourcesService processResourcesService) {\n        super(processResourcesService);\n    }\n\n    @Override\n    void addToBusinessArchive(BusinessArchiveBuilder businessArchiveBuilder, BarResource resource) {\n        businessArchiveBuilder.addExternalResource(resource);\n    }\n\n    @Override\n    public boolean deploy(BusinessArchive businessArchive, SProcessDefinition processDefinition)\n            throws BonitaException, SBonitaException {\n        saveResources(businessArchive, processDefinition, RESOURCES, BARResourceType.EXTERNAL);\n        return true;\n    }\n\n    @Override\n    public List<Problem> checkResolution(SProcessDefinition processDefinition) {\n        return Collections.emptyList();\n    }\n\n    @Override\n    public void delete(SProcessDefinition processDefinition)\n            throws SObjectModificationException, SBonitaReadException, SRecorderException {\n        processResourcesService.removeAll(processDefinition.getId(), BARResourceType.EXTERNAL);\n    }\n\n    @Override\n    public void exportToBusinessArchive(long processDefinitionId, BusinessArchiveBuilder businessArchiveBuilder)\n            throws SBonitaException {\n        exportResourcesToBusinessArchive(processDefinitionId, businessArchiveBuilder, BARResourceType.EXTERNAL);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/resolver/FormMappingAndPageArtifactManager.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.resolver;\n\nimport static org.bonitasoft.engine.form.FormMappingType.PROCESS_OVERVIEW;\nimport static org.bonitasoft.engine.form.FormMappingType.PROCESS_START;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.api.impl.converter.PageModelConverter;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.bar.form.model.FormMappingDefinition;\nimport org.bonitasoft.engine.bpm.bar.form.model.FormMappingModel;\nimport org.bonitasoft.engine.bpm.flownode.ActivityDefinition;\nimport org.bonitasoft.engine.bpm.flownode.FlowElementContainerDefinition;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskDefinition;\nimport org.bonitasoft.engine.bpm.process.Problem;\nimport org.bonitasoft.engine.bpm.process.ProcessDeployException;\nimport org.bonitasoft.engine.bpm.process.SubProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.impl.internal.ProblemImpl;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectCreationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.core.form.FormMappingService;\nimport org.bonitasoft.engine.core.form.SFormMapping;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.form.FormMapping;\nimport org.bonitasoft.engine.form.FormMappingTarget;\nimport org.bonitasoft.engine.form.FormMappingType;\nimport org.bonitasoft.engine.page.*;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.service.FormRequiredAnalyzer;\nimport org.bonitasoft.engine.service.ModelConvertor;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\n\n/**\n * @author Laurent Leseigneur\n */\n@Slf4j\npublic class FormMappingAndPageArtifactManager implements BusinessArchiveArtifactManager {\n\n    public static final String ERROR_MESSAGE_FORM_NOT_SET = \"Error while resolving form mapping for processDefinitionId=%s and task=%s. The target bonita form is not defined\";\n    public static final String ERROR_MESSAGE_FORM_NOT_FOUND = \"Error while resolving form mapping %s. The target bonita form with ID %s does not exist.\";\n    public static final String ERROR_MESSAGE_FORM_UNDEFINED = \"Error while resolving form mapping processDefinitionId=%s and task=%s. The target bonita form is explicitly not yet defined but IS necessary for the process to be resolved\";\n\n    private static final String REGEX = \"^resources/customPages/(custompage_.*)\\\\.(zip)$\";\n    private final SessionService sessionService;\n    private final SessionAccessor sessionAccessor;\n    private final PageService pageService;\n    private final FormMappingService formMappingService;\n    private final ProcessDefinitionService processDefinitionService;\n    public static final int NUMBER_OF_RESULTS = 100;\n\n    public FormMappingAndPageArtifactManager(SessionService sessionService, SessionAccessor sessionAccessor,\n            PageService pageService, FormMappingService formMappingService,\n            ProcessDefinitionService processDefinitionService) {\n        this.sessionService = sessionService;\n        this.sessionAccessor = sessionAccessor;\n        this.pageService = pageService;\n        this.formMappingService = formMappingService;\n        this.processDefinitionService = processDefinitionService;\n    }\n\n    @Override\n    public boolean deploy(final BusinessArchive businessArchive, final SProcessDefinition processDefinition)\n            throws ProcessDeployException {\n\n        deployProcessPages(businessArchive, processDefinition.getId(),\n                sessionService.getLoggedUserFromSession(sessionAccessor));\n        deployFormMappings(businessArchive, processDefinition.getId());\n        return checkResolution(processDefinition).isEmpty();\n    }\n\n    public void deployProcessPages(BusinessArchive businessArchive, Long processDefinitionId, long userId) {\n        final Map<String, byte[]> pageResources = getPageResources(businessArchive);\n        for (final Map.Entry<String, byte[]> resource : pageResources.entrySet()) {\n            try {\n                // TODO: pages are stored twice in Database: once as as page and once as an external resource (in ExternalResourceArtifactManager).\n                // Remove this notion of external resource for custom pages.\n                deployPage(resource.getKey(), resource.getValue(), processDefinitionId, userId, pageService);\n            } catch (SBonitaException e) {\n                log.error(\"Unable to deploy all pages\", e);\n            }\n        }\n    }\n\n    protected Map<String, byte[]> getPageResources(BusinessArchive businessArchive) {\n        return businessArchive.getResources(REGEX);\n    }\n\n    private void deployPage(String resourcePath, byte[] pageContent, Long processDefinitionId, long userId,\n            PageService pageService) throws SBonitaException {\n        final Matcher pathMatcher = getPathMatcher(resourcePath);\n        if (pathMatcher.matches()) {\n            final String pageName = pathMatcher.group(1);\n            final String extension = pathMatcher.group(2);\n            String contentName = pageName + \".\" + extension;\n            final SPage sPage = pageService.getPageByNameAndProcessDefinitionId(pageName, processDefinitionId);\n            if (sPage != null) {\n                pageService.updatePageContent(sPage.getId(), pageContent, contentName);\n            } else {\n                final Properties pageProperties = pageService.readPageZip(pageContent);\n                final PageCreator pageCreator = new PageCreator(pageName, contentName, ContentType.FORM,\n                        processDefinitionId)\n                        .setDisplayName(pageProperties.getProperty(PageService.PROPERTIES_DISPLAY_NAME))\n                        .setDescription(pageProperties.getProperty(PageService.PROPERTIES_DESCRIPTION));\n                final SPage newPage = new PageModelConverter().constructSPage(pageCreator, userId);\n                pageService.addPage(newPage, pageContent);\n            }\n        }\n    }\n\n    private Matcher getPathMatcher(String resourcePath) {\n        final Pattern pattern = Pattern.compile(REGEX);\n        return pattern.matcher(resourcePath);\n    }\n\n    @Override\n    public List<Problem> checkResolution(final SProcessDefinition processDefinition) {\n        List<Problem> problems = new ArrayList<>();\n        try {\n            problems = checkPageProcessResolution(processDefinition);\n        } catch (SBonitaReadException | SObjectNotFoundException e) {\n            problems.add(\n                    new ProblemImpl(Problem.Level.ERROR, null, null, \"unable to resolve form mapping dependencies\"));\n        }\n        return problems;\n    }\n\n    @Override\n    public void delete(SProcessDefinition processDefinition) throws SObjectModificationException {\n        try {\n            deleteFormMapping(processDefinition.getId());\n            deleteProcessPages(processDefinition.getId());\n        } catch (SBonitaReadException | SObjectNotFoundException e) {\n            throw new SObjectModificationException(\n                    \"Unable to delete forms and pages of the process definition <\" + processDefinition.getName() + \">\",\n                    e);\n        }\n    }\n\n    @Override\n    public void exportToBusinessArchive(long processDefinitionId, BusinessArchiveBuilder businessArchiveBuilder)\n            throws SBonitaException {\n        // TODO: when custom pages stop being external resources, add them here:\n        final FormMappingModel formMappingModel = new FormMappingModel();\n        final List<SFormMapping> formMappings = formMappingService.list(processDefinitionId, 0, Integer.MAX_VALUE);\n        for (SFormMapping sFormMapping : formMappings) {\n            final FormMapping formMapping = ModelConvertor.toFormMapping(sFormMapping,\n                    new FormRequiredAnalyzer(processDefinitionService));\n            String form = null;\n            switch (formMapping.getTarget()) {\n                case INTERNAL:\n                    if (formMapping.getPageId() != null) {\n                        final SPage page = pageService.getPage(formMapping.getPageId());\n                        form = page.getName();\n                    }\n                    break;\n                case URL:\n                    form = formMapping.getURL();\n                    break;\n            }\n            final FormMappingDefinition mapping = new FormMappingDefinition(form, formMapping.getType(),\n                    formMapping.getTarget(), formMapping.getTask());\n            formMappingModel.addFormMapping(mapping);\n        }\n        businessArchiveBuilder.setFormMappings(formMappingModel);\n    }\n\n    protected void deleteFormMapping(Long processDefinitionId)\n            throws SBonitaReadException, SObjectModificationException {\n        List<SFormMapping> formMappings;\n        do {\n            formMappings = formMappingService.list(processDefinitionId, 0, NUMBER_OF_RESULTS);\n            for (SFormMapping formMapping : formMappings) {\n                formMappingService.delete(formMapping);\n            }\n        } while (formMappings.size() == NUMBER_OF_RESULTS);\n    }\n\n    private void deleteProcessPages(Long processDefinitionId)\n            throws SBonitaReadException, SObjectModificationException, SObjectNotFoundException {\n        List<SPage> sPages;\n        do {\n            sPages = pageService.getPageByProcessDefinitionId(processDefinitionId, 0, NUMBER_OF_RESULTS);\n            for (SPage sPage : sPages) {\n                pageService.deletePage(sPage.getId());\n            }\n        } while (sPages.size() == NUMBER_OF_RESULTS);\n    }\n\n    protected List<Problem> checkPageProcessResolution(SProcessDefinition sProcessDefinition)\n            throws SBonitaReadException,\n            SObjectNotFoundException {\n        final List<Problem> problems = new ArrayList<>();\n        List<SFormMapping> formMappings;\n        do {\n            formMappings = formMappingService.list(sProcessDefinition.getId(), 0, NUMBER_OF_RESULTS);\n            for (SFormMapping formMapping : formMappings) {\n                checkFormMappingResolution(formMapping, problems);\n            }\n        } while (formMappings.size() == NUMBER_OF_RESULTS);\n        return problems;\n    }\n\n    protected void checkFormMappingResolution(SFormMapping formMapping, List<Problem> problems)\n            throws SBonitaReadException, SObjectNotFoundException {\n        String errorMessage;\n        if (isMappingRelatedToCustomPage(formMapping)) {\n            SPageMapping pageMapping = formMapping.getPageMapping();\n            if (pageMapping == null) {\n                errorMessage = String.format(ERROR_MESSAGE_FORM_NOT_SET, formMapping.getProcessDefinitionId(),\n                        formMapping.getTask());\n                addProblem(formMapping, problems, errorMessage);\n                return;\n            }\n            final Long pageId = pageMapping.getPageId();\n            if (pageId == null || pageService.getPage(pageId) == null) {\n                errorMessage = String.format(ERROR_MESSAGE_FORM_NOT_FOUND, pageMapping.getKey(), pageId);\n                addProblem(formMapping, problems, errorMessage);\n            }\n        } else if (isUndefined(formMapping)) {\n            errorMessage = String.format(ERROR_MESSAGE_FORM_UNDEFINED, formMapping.getProcessDefinitionId(),\n                    formMapping.getTask());\n            addProblem(formMapping, problems, errorMessage);\n        }\n    }\n\n    private void addProblem(SFormMapping formMapping, List<Problem> problems, String errorMessage) {\n        problems.add(new ProblemImpl(Problem.Level.ERROR, formMapping.getProcessElementName(), \"form mapping\",\n                errorMessage));\n    }\n\n    private boolean isMappingRelatedToCustomPage(SFormMapping formMapping) {\n        return FormMappingTarget.INTERNAL.name().equals(formMapping.getTarget());\n    }\n\n    private boolean isUndefined(SFormMapping formMapping) {\n        return FormMappingTarget.UNDEFINED.name().equals(formMapping.getTarget());\n    }\n\n    public void deployFormMappings(final BusinessArchive businessArchive, final long processDefinitionId)\n            throws ProcessDeployException {\n        final List<FormMappingDefinition> formMappings = businessArchive.getFormMappingModel().getFormMappings();\n        final FlowElementContainerDefinition flowElementContainer = businessArchive.getProcessDefinition()\n                .getFlowElementContainer();\n        final List<ActivityDefinition> activities = flowElementContainer.getActivities();\n        try {\n            // Deals with human tasks declared in process definition:\n            for (final ActivityDefinition activity : activities) {\n                createFormMapping(processDefinitionId, formMappingService, formMappings, activity);\n            }\n            // Deals with the process start / process overview forms:\n            createFormMapping(formMappingService, processDefinitionId,\n                    getFormMappingForType(formMappings, PROCESS_START), PROCESS_START.getId(), null);\n            createFormMapping(formMappingService, processDefinitionId,\n                    getFormMappingForType(formMappings, PROCESS_OVERVIEW), PROCESS_OVERVIEW.getId(), null);\n        } catch (final SObjectCreationException | SBonitaReadException e) {\n            throw new ProcessDeployException(e);\n        }\n    }\n\n    void createFormMapping(long processDefinitionId, FormMappingService formMappingService,\n            List<FormMappingDefinition> formMappings,\n            ActivityDefinition activity) throws SObjectCreationException, SBonitaReadException {\n        if (isHumanTask(activity)) {\n            // create mapping as declared in form mapping:\n            createFormMapping(formMappingService, processDefinitionId,\n                    getFormMappingForHumanTask(activity.getName(), formMappings),\n                    FormMappingType.TASK.getId(), activity.getName());\n        } else if (activity instanceof SubProcessDefinition) {\n            final org.bonitasoft.engine.bpm.flownode.FlowElementContainerDefinition subProcessContainer = ((SubProcessDefinition) activity)\n                    .getSubProcessContainer();\n            for (ActivityDefinition activityDefinition : subProcessContainer.getActivities()) {\n                createFormMapping(processDefinitionId, formMappingService, formMappings, activityDefinition);\n            }\n        }\n    }\n\n    private void createFormMapping(FormMappingService formMappingService, long processDefinitionId,\n            FormMappingDefinition formMappingDefinition, Integer type,\n            String taskName)\n            throws SObjectCreationException, SBonitaReadException {\n        if (formMappingDefinition != null) {\n            createSFormMapping(formMappingService, processDefinitionId, formMappingDefinition);\n        } else {\n            formMappingService.create(processDefinitionId, taskName, type, FormMappingTarget.NONE.name(), null);\n        }\n    }\n\n    private SFormMapping createSFormMapping(FormMappingService formMappingService, long processDefinitionId,\n            FormMappingDefinition formMappingDefinition)\n            throws SObjectCreationException,\n            SBonitaReadException {\n        return formMappingService.create(processDefinitionId, formMappingDefinition.getTaskname(),\n                formMappingDefinition.getType().getId(),\n                formMappingDefinition.getTarget().name(), formMappingDefinition.getForm());\n    }\n\n    private boolean isHumanTask(final ActivityDefinition activity) {\n        return activity instanceof HumanTaskDefinition;\n    }\n\n    /**\n     * @return the found mapping for the given human task, or null is not found\n     */\n    private FormMappingDefinition getFormMappingForHumanTask(final String name,\n            final List<FormMappingDefinition> formMappings) {\n        for (final FormMappingDefinition formMapping : formMappings) {\n            if (name.equals(formMapping.getTaskname())) {\n                return formMapping;\n            }\n        }\n        return null;\n    }\n\n    private FormMappingDefinition getFormMappingForType(final List<FormMappingDefinition> formMappings,\n            final FormMappingType type) {\n        for (final FormMappingDefinition formMapping : formMappings) {\n            if (type == formMapping.getType()) {\n                return formMapping;\n            }\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/resolver/ParameterBusinessArchiveArtifactManager.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.resolver;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.process.Problem;\nimport org.bonitasoft.engine.bpm.process.Problem.Level;\nimport org.bonitasoft.engine.bpm.process.impl.internal.ProblemImpl;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.core.process.definition.model.SParameterDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.parameter.OrderBy;\nimport org.bonitasoft.engine.parameter.ParameterService;\nimport org.bonitasoft.engine.parameter.SParameter;\nimport org.bonitasoft.engine.parameter.SParameterProcessNotFoundException;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class ParameterBusinessArchiveArtifactManager implements BusinessArchiveArtifactManager {\n\n    private final ParameterService parameterService;\n\n    public ParameterBusinessArchiveArtifactManager(ParameterService parameterService) {\n        this.parameterService = parameterService;\n    }\n\n    @Override\n    public boolean deploy(final BusinessArchive businessArchive, final SProcessDefinition processDefinition)\n            throws CreationException {\n        final Set<SParameterDefinition> declaredParameters = processDefinition.getParameters();\n        boolean resolved = true;\n        if (declaredParameters.isEmpty()) {\n            return true;\n        }\n        final Map<String, String> defaultParameterValues = businessArchive.getParameters();\n        final Map<String, String> storedParameters = new HashMap<>();\n        for (final SParameterDefinition sParameterDefinition : declaredParameters) {\n            final String name = sParameterDefinition.getName();\n            final String value = defaultParameterValues.get(sParameterDefinition.getName());\n            if (value == null) {\n                resolved = false;\n            }\n            storedParameters.put(name, value);\n        }\n        try {\n            parameterService.addAll(processDefinition.getId(), storedParameters);\n        } catch (SBonitaException e) {\n            throw new CreationException(e);\n        }\n        return resolved;\n    }\n\n    @Override\n    public List<Problem> checkResolution(final SProcessDefinition processDefinition) {\n        if (processDefinition.getParameters().isEmpty()) {\n            return Collections.emptyList();\n        }\n        int NUMBER_OF_RESULT = 100;\n        List<SParameter> parameters;\n        final ArrayList<Problem> problems = new ArrayList<>();\n        int i = 0;\n        do {\n            try {\n                parameters = parameterService.getNullValues(processDefinition.getId(), i, NUMBER_OF_RESULT,\n                        OrderBy.NAME_ASC);\n            } catch (final SBonitaException e) {\n                return Collections\n                        .singletonList(new ProblemImpl(Level.ERROR, null, \"parameter\", \"Unable to get parameter !!\"));\n            }\n            i += NUMBER_OF_RESULT;\n            for (final SParameter parameter : parameters) {\n                if (parameter.getValue() == null) {\n                    final Problem problem = new ProblemImpl(Level.ERROR, null, \"parameter\",\n                            \"Parameter '\" + parameter.getName() + \"' is not set.\");\n                    problems.add(problem);\n                }\n            }\n        } while (parameters.size() == NUMBER_OF_RESULT);\n        return problems;\n    }\n\n    @Override\n    public void delete(SProcessDefinition processDefinition) throws SObjectModificationException {\n        try {\n            parameterService.deleteAll(processDefinition.getId());\n        } catch (SParameterProcessNotFoundException | SBonitaReadException e) {\n            throw new SObjectModificationException(\n                    \"Unable to delete parameters of the process definition <\" + processDefinition.getName() + \">\", e);\n        }\n    }\n\n    @Override\n    public void exportToBusinessArchive(long processDefinitionId, BusinessArchiveBuilder businessArchiveBuilder)\n            throws SBonitaException {\n        final List<SParameter> sParameter = parameterService.get(processDefinitionId, 0, Integer.MAX_VALUE, null);\n        Map<String, String> map = new HashMap<>();\n        for (SParameter parameter : sParameter) {\n            map.put(parameter.getName(), parameter.getValue());\n        }\n        businessArchiveBuilder.setParameters(map);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/resolver/UserFilterBusinessArchiveArtifactManager.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.resolver;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.bar.BarResource;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.process.Problem;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.core.filter.UserFilterService;\nimport org.bonitasoft.engine.core.filter.exception.SUserFilterLoadingException;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.filter.UserFilterException;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.resources.BARResourceType;\nimport org.bonitasoft.engine.resources.ProcessResourcesService;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class UserFilterBusinessArchiveArtifactManager extends BARResourceArtifactManager {\n\n    private final UserFilterService userFilterService;\n\n    public UserFilterBusinessArchiveArtifactManager(UserFilterService userFilterService,\n            ProcessResourcesService processResourcesService) {\n        super(processResourcesService);\n        this.userFilterService = userFilterService;\n    }\n\n    @Override\n    public boolean deploy(final BusinessArchive businessArchive, final SProcessDefinition processDefinition)\n            throws UserFilterException, SRecorderException {\n        try {\n            saveResources(businessArchive, processDefinition, \"userFilters\", BARResourceType.USER_FILTER);\n            return userFilterService.loadUserFilters(processDefinition.getId());\n        } catch (final SUserFilterLoadingException e) {\n            throw new UserFilterException(e);\n        }\n    }\n\n    @Override\n    public List<Problem> checkResolution(final SProcessDefinition processDefinition) {\n        return Collections.emptyList();\n    }\n\n    @Override\n    public void delete(SProcessDefinition processDefinition)\n            throws SObjectModificationException, SBonitaReadException, SRecorderException {\n        userFilterService.removeUserFilters(processDefinition.getId());\n    }\n\n    @Override\n    public void exportToBusinessArchive(long processDefinitionId, BusinessArchiveBuilder businessArchiveBuilder)\n            throws SBonitaException {\n        exportResourcesToBusinessArchive(processDefinitionId, businessArchiveBuilder, BARResourceType.USER_FILTER);\n    }\n\n    @Override\n    void addToBusinessArchive(BusinessArchiveBuilder businessArchiveBuilder, BarResource resource) {\n        businessArchiveBuilder.addUserFilters(resource);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/AbstractGetEntity.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction;\n\nimport java.io.Serializable;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.Sort;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\n\n/**\n * Abstract class to allow to search server object and convert them to client object\n *\n * @author Celine Souchet\n * @param <C>\n *        The client object\n * @param <S>\n *        The server object\n */\npublic abstract class AbstractGetEntity<C extends Serializable, S extends PersistentObject>\n        implements TransactionContentWithResult<List<C>> {\n\n    private final SearchEntityDescriptor searchDescriptor;\n\n    private final int numberOfResults;\n\n    private final int fromIndex;\n\n    private final Sort sort;\n\n    private List<C> clientObjects;\n\n    /**\n     * @param searchDescriptor\n     *        The descriptor corresponding to the searched object\n     * @param fromIndex\n     *        The specified position to begin the search\n     * @param numberOfResults\n     *        The number of elements to return\n     * @param sort\n     *        How to sort the result\n     */\n    public AbstractGetEntity(final SearchEntityDescriptor searchDescriptor, final int fromIndex,\n            final int numberOfResults, final Sort sort) {\n        this.searchDescriptor = searchDescriptor;\n        this.numberOfResults = numberOfResults;\n        this.fromIndex = fromIndex;\n        this.sort = sort;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        if (searchDescriptor == null || sort == null) {\n            throw new SBonitaReadException(\"Sort and SearchDescriptor cannot be null\");\n        }\n\n        final OrderByOption orderByOption = searchDescriptor.getEntityOrder(sort);\n        final QueryOptions searchOptions = new QueryOptions(fromIndex, numberOfResults,\n                Collections.singletonList(orderByOption));\n        List<S> serverObjects = executeGet(searchOptions);\n        clientObjects = convertToClientObjects(serverObjects);\n    }\n\n    /**\n     * Execute the search here\n     *\n     * @param queryOptions\n     *        The query options to execute the search with\n     * @return The searched result\n     * @throws SBonitaException\n     */\n    public abstract List<S> executeGet(final QueryOptions queryOptions) throws SBonitaException;\n\n    /**\n     * Must convert server objects in client objects here\n     *\n     * @param serverObjects\n     *        The server object to convert\n     * @return The list of the client objects\n     */\n    public abstract List<C> convertToClientObjects(final List<S> serverObjects);\n\n    @Override\n    public List<C> getResult() {\n        return clientObjects;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/CustomTransactions.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * Indicates that a Bonita API method handles transactions itself, and thus does not need to use the generic transaction\n * mechanism.\n *\n * @author Emmanuel Duchastenier\n */\n@Target(ElementType.METHOD)\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface CustomTransactions {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/activity/GetActivityInstances.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.activity;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\nimport org.bonitasoft.engine.persistence.OrderByType;\n\n/**\n * @author Baptiste Mesta\n */\npublic class GetActivityInstances implements TransactionContentWithResult<List<SActivityInstance>> {\n\n    private List<SActivityInstance> activities;\n\n    private final ActivityInstanceService activityInstanceService;\n\n    private final long processInstanceUUID;\n\n    private final int pageIndex;\n\n    private final int numberPerPage;\n\n    private final String field;\n\n    private final OrderByType order;\n\n    public GetActivityInstances(final ActivityInstanceService activityInstanceService, final long processInstanceUUID,\n            final int pageIndex,\n            final int numberPerPage, final String field, final OrderByType order) {\n        this.activityInstanceService = activityInstanceService;\n        this.processInstanceUUID = processInstanceUUID;\n        this.pageIndex = pageIndex;\n        this.numberPerPage = numberPerPage;\n        this.field = field;\n        this.order = order;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        activities = activityInstanceService.getOpenActivityInstances(processInstanceUUID, pageIndex, numberPerPage,\n                field, order);\n    }\n\n    @Override\n    public List<SActivityInstance> getResult() {\n        return activities;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/activity/GetArchivedActivityInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.activity;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class GetArchivedActivityInstance implements TransactionContentWithResult<SAActivityInstance> {\n\n    private final ActivityInstanceService activityInstanceService;\n\n    private final int stateId;\n\n    private final long activityInstanceId;\n\n    private SAActivityInstance activity;\n\n    public GetArchivedActivityInstance(final ActivityInstanceService activityInstanceService,\n            final long activityInstanceId) {\n        this.activityInstanceService = activityInstanceService;\n        this.activityInstanceId = activityInstanceId;\n        stateId = -1;\n    }\n\n    public GetArchivedActivityInstance(final long activityInstanceId, final int stateId,\n            final ActivityInstanceService activityInstanceService) {\n        this.activityInstanceId = activityInstanceId;\n        this.stateId = stateId;\n        this.activityInstanceService = activityInstanceService;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        if (stateId > -1) {\n            activity = activityInstanceService.getArchivedActivityInstance(activityInstanceId, stateId);\n        } else {\n            activity = activityInstanceService.getMostRecentArchivedActivityInstance(activityInstanceId);\n        }\n    }\n\n    @Override\n    public SAActivityInstance getResult() {\n        return activity;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/activity/GetArchivedActivityInstances.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.activity;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class GetArchivedActivityInstances implements TransactionContentWithResult<List<SAActivityInstance>> {\n\n    private final ActivityInstanceService activityInstanceService;\n\n    private final long processInstanceId;\n\n    private final int pageIndex;\n\n    private final int numberPerPage;\n\n    private final String field;\n\n    private final OrderByType order;\n\n    private List<SAActivityInstance> archivedActivityInstances;\n\n    public GetArchivedActivityInstances(final ActivityInstanceService activityInstanceService,\n            final long processInstanceId,\n            final int pageIndex, final int numberPerPage, final String field, final OrderByType order) {\n        this.activityInstanceService = activityInstanceService;\n        this.processInstanceId = processInstanceId;\n        this.pageIndex = pageIndex;\n        this.numberPerPage = numberPerPage;\n        this.field = field;\n        this.order = order;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        final QueryOptions queryOptions = new QueryOptions(pageIndex * numberPerPage, numberPerPage,\n                SAActivityInstance.class, field, order);\n        archivedActivityInstances = activityInstanceService.getArchivedActivityInstances(processInstanceId,\n                queryOptions);\n    }\n\n    @Override\n    public List<SAActivityInstance> getResult() {\n        return archivedActivityInstances;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/activity/GetContractOfUserTaskInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.activity;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SContractDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SUserTaskDefinition;\nimport org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class GetContractOfUserTaskInstance implements TransactionContentWithResult<SContractDefinition> {\n\n    private final ProcessDefinitionService definitionService;\n\n    private final SUserTaskInstance userTaskInstance;\n\n    private SContractDefinition contract;\n\n    public GetContractOfUserTaskInstance(final ProcessDefinitionService definitionService,\n            final SUserTaskInstance userTaskInstance) {\n        this.definitionService = definitionService;\n        this.userTaskInstance = userTaskInstance;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        final SProcessDefinition processDefinition = definitionService\n                .getProcessDefinition(userTaskInstance.getProcessDefinitionId());\n        final SUserTaskDefinition userTaskDefinition = (SUserTaskDefinition) processDefinition.getProcessContainer()\n                .getFlowNode(\n                        userTaskInstance.getFlowNodeDefinitionId());\n        contract = userTaskDefinition.getContract();\n    }\n\n    @Override\n    public SContractDefinition getResult() {\n        return contract;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/activity/GetNumberOfActivityInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.activity;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class GetNumberOfActivityInstance implements TransactionContentWithResult<Integer> {\n\n    private final ActivityInstanceService activityInstanceService;\n\n    private int number;\n\n    private final long processInstanceId;\n\n    public GetNumberOfActivityInstance(final long processInstanceId,\n            final ActivityInstanceService activityInstanceService) {\n        this.processInstanceId = processInstanceId;\n        this.activityInstanceService = activityInstanceService;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        number = activityInstanceService.getNumberOfOpenActivityInstances(processInstanceId);\n    }\n\n    @Override\n    public Integer getResult() {\n        return number;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/actor/AddActor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.actor;\n\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.actor.mapping.model.SActor;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContent;\n\n/**\n * @author Zhao Na\n */\npublic class AddActor implements TransactionContent {\n\n    private final ActorMappingService actorMappingService;\n\n    private final SActor actor;\n\n    public AddActor(final ActorMappingService actorMappingService, final SActor actor) {\n        super();\n        this.actorMappingService = actorMappingService;\n        this.actor = actor;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        actorMappingService.addActor(actor);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/actor/AddActorMember.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.actor;\n\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.actor.mapping.model.SActorMember;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContent;\nimport org.bonitasoft.engine.identity.MemberType;\n\n/**\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n */\npublic class AddActorMember implements TransactionContent {\n\n    private final ActorMappingService actorMappingService;\n\n    private final long actorId;\n\n    private final long userId;\n\n    private final long groupId;\n\n    private final long roleId;\n\n    private SActorMember actorMember;\n\n    private final MemberType memberType;\n\n    public AddActorMember(final ActorMappingService actorMappingService, final long actorId, final long userId,\n            final long groupId, final long roleId,\n            final MemberType memberType) {\n        super();\n        this.actorMappingService = actorMappingService;\n        this.actorId = actorId;\n        this.userId = userId;\n        this.groupId = groupId;\n        this.roleId = roleId;\n        this.memberType = memberType;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        switch (memberType) {\n            case USER:\n                actorMember = actorMappingService.addUserToActor(actorId, userId);\n                break;\n            case GROUP:\n                actorMember = actorMappingService.addGroupToActor(actorId, groupId);\n                break;\n            case ROLE:\n                actorMember = actorMappingService.addRoleToActor(actorId, roleId);\n                break;\n            case MEMBERSHIP:\n                actorMember = actorMappingService.addRoleAndGroupToActor(actorId, roleId, groupId);\n                break;\n            default:\n                throw new IllegalStateException();\n        }\n    }\n\n    public SActorMember getActorMember() {\n        return actorMember;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/actor/ExportActorMapping.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.actor;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.actor.mapping.model.SActor;\nimport org.bonitasoft.engine.actor.mapping.model.SActorMember;\nimport org.bonitasoft.engine.bpm.bar.ActorMappingMarshaller;\nimport org.bonitasoft.engine.bpm.bar.XmlMarshallException;\nimport org.bonitasoft.engine.bpm.bar.actorMapping.Actor;\nimport org.bonitasoft.engine.bpm.bar.actorMapping.ActorMapping;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.SGroupNotFoundException;\nimport org.bonitasoft.engine.identity.SRoleNotFoundException;\nimport org.bonitasoft.engine.identity.SUserNotFoundException;\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.identity.model.SRole;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class ExportActorMapping implements TransactionContentWithResult<String> {\n\n    private final ActorMappingService actorMappingService;\n\n    private final IdentityService identityService;\n\n    private final long processDefinitionId;\n\n    private String xmlContent;\n\n    public ExportActorMapping(final ActorMappingService actorMappingService, final IdentityService identityService,\n            final long processDefinitionId) {\n        super();\n        this.actorMappingService = actorMappingService;\n        this.identityService = identityService;\n        this.processDefinitionId = processDefinitionId;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        final ActorMapping mapping = getActorMapping();\n        ActorMappingMarshaller marshaller = new ActorMappingMarshaller();\n\n        try {\n            xmlContent = new String(marshaller.serializeToXML(mapping));\n        } catch (XmlMarshallException e) {\n            throw new SBonitaReadException(\"Failed to generate xml from actorMapping\", e);\n        }\n    }\n\n    public ActorMapping getActorMapping() throws SBonitaException {\n        final ActorMapping actorMapping = new ActorMapping();\n        QueryOptions queryOptions = new QueryOptions(0, 100, SActor.class, \"id\", OrderByType.ASC);\n        List<SActor> actors = actorMappingService.getActors(processDefinitionId, queryOptions);\n        while (!actors.isEmpty()) {\n            for (final SActor sActor : actors) {\n                final Actor actor = new Actor(sActor.getName());\n                final List<SActorMember> actorMembers = actorMappingService.getActorMembers(sActor.getId(), 0,\n                        Integer.MAX_VALUE);\n                for (final SActorMember sActorMember : actorMembers) {\n                    addUser(actor, sActorMember);\n                    addGroup(actor, sActorMember);\n                    addRole(actor, sActorMember);\n                    addMembership(actor, sActorMember);\n                }\n                actorMapping.addActor(actor);\n            }\n            queryOptions = QueryOptions.getNextPage(queryOptions);\n            actors = actorMappingService.getActors(processDefinitionId, queryOptions);\n        }\n        return actorMapping;\n    }\n\n    private void addUser(final Actor actor, final SActorMember sActorMember) throws SUserNotFoundException {\n        if (sActorMember.getUserId() > 0) {\n            final SUser user = identityService.getUser(sActorMember.getUserId());\n            actor.addUser(user.getUserName());\n        }\n    }\n\n    private void addGroup(final Actor actor, final SActorMember sActorMember) throws SGroupNotFoundException {\n        if (sActorMember.getGroupId() > 0 && sActorMember.getRoleId() <= 0) {\n            final SGroup group = identityService.getGroup(sActorMember.getGroupId());\n            actor.addGroup(group.getPath());\n        }\n    }\n\n    private void addRole(final Actor actor, final SActorMember sActorMember) throws SRoleNotFoundException {\n        if (sActorMember.getRoleId() > 0 && sActorMember.getGroupId() <= 0) {\n            final SRole role = identityService.getRole(sActorMember.getRoleId());\n            actor.addRole(role.getName());\n        }\n    }\n\n    private void addMembership(final Actor actor, final SActorMember sActorMember)\n            throws SRoleNotFoundException, SGroupNotFoundException {\n        if (sActorMember.getRoleId() > 0 && sActorMember.getGroupId() > 0) {\n            final SRole role = identityService.getRole(sActorMember.getRoleId());\n            final SGroup group = identityService.getGroup(sActorMember.getGroupId());\n            actor.addMembership(group.getPath(), role.getName());\n        }\n    }\n\n    @Override\n    public String getResult() {\n        return xmlContent;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/actor/GetActor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.actor;\n\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.actor.mapping.model.SActor;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class GetActor implements TransactionContentWithResult<SActor> {\n\n    private final ActorMappingService actorMappingService;\n\n    private final long actorId;\n\n    private final String actorName;\n\n    private final long scopeId;\n\n    private SActor actor;\n\n    public GetActor(final ActorMappingService actorMappingService, final long actorId) {\n        super();\n        this.actorMappingService = actorMappingService;\n        this.actorId = actorId;\n        actorName = null;\n        scopeId = -1;\n    }\n\n    public GetActor(final ActorMappingService actorMappingService, final String actorName, final long scopeId) {\n        super();\n        this.actorMappingService = actorMappingService;\n        this.actorName = actorName;\n        this.scopeId = scopeId;\n        actorId = -1;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        if (actorId > 0) {\n            actor = actorMappingService.getActor(actorId);\n        } else {\n            actor = actorMappingService.getActor(actorName, scopeId);\n        }\n    }\n\n    @Override\n    public SActor getResult() {\n        return actor;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/actor/GetActorInitiators.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.actor;\n\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.actor.mapping.model.SActor;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\n\n/**\n * @author Feng Hui\n */\npublic class GetActorInitiators implements TransactionContentWithResult<List<SActor>> {\n\n    private final ActorMappingService actorMappingService;\n\n    private final Set<Long> actorIds;\n\n    private final List<SActor> sActors = new ArrayList<SActor>();\n\n    public GetActorInitiators(final ActorMappingService actorMappingService, final Set<Long> actorIds) {\n        this.actorMappingService = actorMappingService;\n        this.actorIds = actorIds;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        for (final Iterator<Long> iterator = actorIds.iterator(); iterator.hasNext();) {\n            final SActor sActor = actorMappingService.getActor(iterator.next());\n            if (sActor.isInitiator()) {\n                sActors.add(sActor);\n            }\n        }\n    }\n\n    @Override\n    public List<SActor> getResult() {\n        return sActors;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/actor/GetActorsByActorIds.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.actor;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.actor.mapping.model.SActor;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\n\n/**\n * @author Zhao Na\n */\npublic class GetActorsByActorIds implements TransactionContentWithResult<List<SActor>> {\n\n    private final ActorMappingService actorMappingService;\n\n    private final List<Long> actorIds;\n\n    private List<SActor> actors;\n\n    public GetActorsByActorIds(final ActorMappingService actorMappingService, final List<Long> actorIds) {\n        this.actorMappingService = actorMappingService;\n        this.actorIds = actorIds;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        actors = actorMappingService.getActors(actorIds);\n    }\n\n    @Override\n    public List<SActor> getResult() {\n        return actors;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/actor/GetNumberOfActorMembers.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.actor;\n\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class GetNumberOfActorMembers implements TransactionContentWithResult<Long> {\n\n    private final ActorMappingService actorMappingService;\n\n    private final long actorId;\n\n    private long numberOfActorMembers;\n\n    public GetNumberOfActorMembers(final ActorMappingService actorMappingService, final long actorId) {\n        this.actorMappingService = actorMappingService;\n        this.actorId = actorId;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        numberOfActorMembers = actorMappingService.getNumberOfActorMembers(actorId);\n    }\n\n    @Override\n    public Long getResult() {\n        return numberOfActorMembers;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/actor/GetNumberOfActors.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.actor;\n\nimport java.util.Set;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SActorDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\n\n/**\n * @author Yanyan Liu\n * @author Emmanuel Duchastenier\n */\npublic class GetNumberOfActors implements TransactionContentWithResult<Integer> {\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    private final long processDefinitionId;\n\n    private int numberOfActors;\n\n    public GetNumberOfActors(final ProcessDefinitionService processDefinitionService, final long processDefinitionId) {\n        this.processDefinitionService = processDefinitionService;\n        this.processDefinitionId = processDefinitionId;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        final SProcessDefinition definition = processDefinitionService.getProcessDefinition(processDefinitionId);\n        SActorDefinition actorInitiator = definition.getActorInitiator();\n        Set<SActorDefinition> actors = definition.getActors();\n        numberOfActors = actors.size();\n        if ((actorInitiator != null) && !actors.contains(actorInitiator)) {\n            numberOfActors++;\n        }\n    }\n\n    @Override\n    public Integer getResult() {\n        return numberOfActors;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/actor/GetNumberOfGroupsOfActor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.actor;\n\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\n\n/**\n * @author Celine Souchet\n */\npublic class GetNumberOfGroupsOfActor implements TransactionContentWithResult<Long> {\n\n    private final ActorMappingService actorMappingService;\n\n    private final long actorId;\n\n    private long numberOfGroups;\n\n    public GetNumberOfGroupsOfActor(final ActorMappingService actorMappingService, final long actorId) {\n        this.actorMappingService = actorMappingService;\n        this.actorId = actorId;\n    }\n\n    @Override\n    public void execute() {\n        numberOfGroups = actorMappingService.getNumberOfGroupsOfActor(actorId);\n    }\n\n    @Override\n    public Long getResult() {\n        return numberOfGroups;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/actor/GetNumberOfMembershipsOfActor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.actor;\n\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\n\n/**\n * @author Celine Souchet\n * @author Matthieu Chaffotte\n */\npublic class GetNumberOfMembershipsOfActor implements TransactionContentWithResult<Long> {\n\n    private final ActorMappingService actorMappingService;\n\n    private final long actorId;\n\n    private long numberOfUserMemberships;\n\n    public GetNumberOfMembershipsOfActor(final ActorMappingService actorMappingService, final long actorId) {\n        this.actorMappingService = actorMappingService;\n        this.actorId = actorId;\n    }\n\n    @Override\n    public void execute() {\n        numberOfUserMemberships = actorMappingService.getNumberOfMembershipsOfActor(actorId);\n    }\n\n    @Override\n    public Long getResult() {\n        return numberOfUserMemberships;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/actor/GetNumberOfRolesOfActor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.actor;\n\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\n\n/**\n * @author Celine Souchet\n */\npublic class GetNumberOfRolesOfActor implements TransactionContentWithResult<Long> {\n\n    private final ActorMappingService actorMappingService;\n\n    private final long actorId;\n\n    private long numberOfRoles;\n\n    public GetNumberOfRolesOfActor(final ActorMappingService actorMappingService, final long actorId) {\n        this.actorMappingService = actorMappingService;\n        this.actorId = actorId;\n    }\n\n    @Override\n    public void execute() {\n        numberOfRoles = actorMappingService.getNumberOfRolesOfActor(actorId);\n    }\n\n    @Override\n    public Long getResult() {\n        return numberOfRoles;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/actor/GetNumberOfUsersOfActor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.actor;\n\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\n\n/**\n * @author Celine Souchet\n */\npublic class GetNumberOfUsersOfActor implements TransactionContentWithResult<Long> {\n\n    private final ActorMappingService actorMappingService;\n\n    private final long actorId;\n\n    private long numberOfUsers;\n\n    public GetNumberOfUsersOfActor(final ActorMappingService actorMappingService, final long actorId) {\n        this.actorMappingService = actorMappingService;\n        this.actorId = actorId;\n    }\n\n    @Override\n    public void execute() {\n        numberOfUsers = actorMappingService.getNumberOfUsersOfActor(actorId);\n    }\n\n    @Override\n    public Long getResult() {\n        return numberOfUsers;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/actor/ImportActorMapping.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.actor;\n\nimport java.util.List;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.actor.mapping.SActorMemberAlreadyExistsException;\nimport org.bonitasoft.engine.actor.mapping.model.SActor;\nimport org.bonitasoft.engine.actor.mapping.model.SActorMember;\nimport org.bonitasoft.engine.bpm.bar.ActorMappingMarshaller;\nimport org.bonitasoft.engine.bpm.bar.XmlMarshallException;\nimport org.bonitasoft.engine.bpm.bar.actorMapping.Actor;\nimport org.bonitasoft.engine.bpm.bar.actorMapping.ActorMapping;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.identity.model.SRole;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class ImportActorMapping {\n\n    private final ActorMappingService actorMappingService;\n\n    private final IdentityService identityService;\n\n    public ImportActorMapping(final ActorMappingService actorMappingService, IdentityService identityService) {\n        this.actorMappingService = actorMappingService;\n        this.identityService = identityService;\n    }\n\n    public void importActorMappingFromXml(String xmlContent, long processDefinitionId) throws SBonitaException {\n        ActorMapping actorMapping = getActorMappingFromXML(xmlContent);\n        execute(actorMapping, processDefinitionId);\n    }\n\n    public void execute(ActorMapping actorMapping, long processDefinitionId) throws SBonitaException {\n        final List<Actor> actors = actorMapping.getActors();\n        for (final Actor actor : actors) {\n            final SActor sActor = actorMappingService.getActor(actor.getName(), processDefinitionId);\n            final long actorId = sActor.getId();\n            final Set<String> userNames = actor.getUsers();\n            for (final String userName : userNames) {\n                final SUser user = identityService.getUserByUserName(userName);\n                checkAlreadyExistingUserMapping(actorId, user.getId());\n                actorMappingService.addUserToActor(actorId, user.getId());\n            }\n            final Set<String> roleNames = actor.getRoles();\n            for (final String roleName : roleNames) {\n                final SRole role = identityService.getRoleByName(roleName);\n                checkAlreadyExistingRoleMapping(actorId, role.getId());\n                actorMappingService.addRoleToActor(actorId, role.getId());\n            }\n            final Set<String> groupPaths = actor.getGroups();\n            for (final String groupPath : groupPaths) {\n                final SGroup group = identityService.getGroupByPath(groupPath);\n                checkAlreadyExistingGroupMapping(actorId, group.getId());\n                actorMappingService.addGroupToActor(actorId, group.getId());\n            }\n            final Set<Actor.Membership> memberships = actor.getMemberships();\n            for (final Actor.Membership membership : memberships) {\n                final SGroup group = identityService.getGroupByPath(membership.getGroup());\n                final SRole role = identityService.getRoleByName(membership.getRole());\n                checkAlreadyExistingMembershipMapping(actorId, group.getId(), role.getId());\n                actorMappingService.addRoleAndGroupToActor(actorId, role.getId(), group.getId());\n            }\n        }\n    }\n\n    private void checkAlreadyExistingUserMapping(final long actorId, final long userId)\n            throws SActorMemberAlreadyExistsException, SBonitaReadException {\n        List<SActorMember> actorMembersOfUser;\n        int startIndex = 0;\n        do {\n            actorMembersOfUser = actorMappingService.getActorMembers(actorId, startIndex, 50);\n            for (final SActorMember sActorMember : actorMembersOfUser) {\n                if (sActorMember.getUserId() == userId && sActorMember.getRoleId() == -1\n                        && sActorMember.getGroupId() == -1) {\n                    throw new SActorMemberAlreadyExistsException(\n                            \"This user / actor mapping already exists: actorId=\" + actorId + \", userId=\" + userId);\n                }\n            }\n            startIndex += 50;\n        } while (actorMembersOfUser.size() > 0);\n    }\n\n    private void checkAlreadyExistingGroupMapping(final long actorId, final long groupId)\n            throws SActorMemberAlreadyExistsException, SBonitaReadException {\n        List<SActorMember> actorMembersOfGroup;\n        int startIndex = 0;\n        do {\n            actorMembersOfGroup = actorMappingService.getActorMembers(actorId, startIndex, 50);\n            for (final SActorMember sActorMember : actorMembersOfGroup) {\n                if (sActorMember.getGroupId() == groupId && sActorMember.getRoleId() == -1\n                        && sActorMember.getUserId() == -1) {\n                    throw new SActorMemberAlreadyExistsException(\n                            \"This group / actor mapping already exists: actorId=\" + actorId + \", groupId=\" + groupId);\n                }\n            }\n            startIndex += 50;\n        } while (actorMembersOfGroup.size() > 0);\n    }\n\n    private void checkAlreadyExistingRoleMapping(final long actorId, final long roleId)\n            throws SActorMemberAlreadyExistsException, SBonitaReadException {\n        List<SActorMember> actorMembersOfRole;\n        int startIndex = 0;\n        do {\n            actorMembersOfRole = actorMappingService.getActorMembers(actorId, startIndex, 50);\n            for (final SActorMember sActorMember : actorMembersOfRole) {\n                if (sActorMember.getRoleId() == roleId && sActorMember.getGroupId() == -1\n                        && sActorMember.getUserId() == -1) {\n                    throw new SActorMemberAlreadyExistsException(\n                            \"This role / actor mapping already exists: actorId=\" + actorId + \", roleId=\" + roleId);\n                }\n            }\n            startIndex += 50;\n        } while (actorMembersOfRole.size() > 0);\n    }\n\n    private void checkAlreadyExistingMembershipMapping(final long actorId, final long groupId, final long roleId)\n            throws SActorMemberAlreadyExistsException,\n            SBonitaReadException {\n        List<SActorMember> actorMembersOfMembership;\n        int startIndex = 0;\n        do {\n            actorMembersOfMembership = actorMappingService.getActorMembers(actorId, startIndex, 50);\n            for (final SActorMember sActorMember : actorMembersOfMembership) {\n                if (sActorMember.getRoleId() == roleId && sActorMember.getGroupId() == groupId) {\n                    throw new SActorMemberAlreadyExistsException(\n                            \"This membership / actor mapping already exists: actorId=\" + actorId + \", groupId=\"\n                                    + groupId\n                                    + \", roleId=\" + roleId);\n                }\n            }\n            startIndex += 50;\n        } while (actorMembersOfMembership.size() > 0);\n    }\n\n    private ActorMapping getActorMappingFromXML(String xmlContent) throws SBonitaException {\n        byte[] b = xmlContent.getBytes();\n        try {\n            return new ActorMappingMarshaller().deserializeFromXML(b);\n        } catch (XmlMarshallException e) {\n            throw new SBonitaReadException(\"Unable to read the actor mapping xml\", e);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/actor/RemoveActorMember.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.actor;\n\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.actor.mapping.model.SActorMember;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\n\n/**\n * @author Matthieu Chaffotte\n * @author Emmanuel Duchastenier\n */\npublic class RemoveActorMember implements TransactionContentWithResult<SActorMember> {\n\n    private final ActorMappingService actorMappingService;\n\n    private final long actorMemberId;\n\n    private SActorMember removedMember;\n\n    public RemoveActorMember(final ActorMappingService actorMappingService, final long actorMemberId) {\n        this.actorMappingService = actorMappingService;\n        this.actorMemberId = actorMemberId;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        removedMember = actorMappingService.deleteActorMember(actorMemberId);\n    }\n\n    @Override\n    public SActorMember getResult() {\n        return removedMember;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/application/SearchApplicationMenus.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.application;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.impl.converter.ApplicationMenuModelConverter;\nimport org.bonitasoft.engine.business.application.ApplicationMenu;\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.model.SApplicationMenu;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SearchApplicationMenus extends AbstractSearchEntity<ApplicationMenu, SApplicationMenu> {\n\n    private final ApplicationService applicationService;\n    private final ApplicationMenuModelConverter convertor;\n\n    public SearchApplicationMenus(final ApplicationService applicationService,\n            final ApplicationMenuModelConverter convertor,\n            final SearchEntityDescriptor searchDescriptor, final SearchOptions options) {\n        super(searchDescriptor, options);\n        this.applicationService = applicationService;\n        this.convertor = convertor;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions queryOptions) throws SBonitaReadException {\n        return applicationService.getNumberOfApplicationMenus(queryOptions);\n    }\n\n    @Override\n    public List<SApplicationMenu> executeSearch(final QueryOptions queryOptions) throws SBonitaReadException {\n        return applicationService.searchApplicationMenus(queryOptions);\n    }\n\n    @Override\n    public List<ApplicationMenu> convertToClientObjects(final List<SApplicationMenu> serverObjects)\n            throws SBonitaException {\n        return convertor.toApplicationMenu(serverObjects);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/application/SearchApplicationPages.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.application;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.impl.converter.ApplicationPageModelConverter;\nimport org.bonitasoft.engine.business.application.ApplicationPage;\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.model.SApplicationPage;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SearchApplicationPages extends AbstractSearchEntity<ApplicationPage, SApplicationPage> {\n\n    private final ApplicationService applicationService;\n    private final ApplicationPageModelConverter convertor;\n\n    public SearchApplicationPages(final ApplicationService applicationService,\n            final ApplicationPageModelConverter convertor,\n            final SearchEntityDescriptor searchDescriptor, final SearchOptions options) {\n        super(searchDescriptor, options);\n        this.applicationService = applicationService;\n        this.convertor = convertor;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions queryOptions) throws SBonitaReadException {\n        return applicationService.getNumberOfApplicationPages(queryOptions);\n    }\n\n    @Override\n    public List<SApplicationPage> executeSearch(final QueryOptions queryOptions) throws SBonitaReadException {\n        return applicationService.searchApplicationPages(queryOptions);\n    }\n\n    @Override\n    public List<ApplicationPage> convertToClientObjects(final List<SApplicationPage> serverObjects) {\n        return convertor.toApplicationPage(serverObjects);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/application/SearchApplications.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.application;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.impl.converter.ApplicationModelConverter;\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.IApplication;\nimport org.bonitasoft.engine.business.application.model.SApplication;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SearchApplications<E extends IApplication> extends AbstractSearchEntity<E, SApplication> {\n\n    private final Class<E> applicationClass;\n    private final ApplicationService applicationService;\n    private final ApplicationModelConverter convertor;\n\n    public static SearchApplications<IApplication> defaultSearchApplications(\n            final ApplicationService applicationService,\n            final SearchEntityDescriptor searchDescriptor, final SearchOptions options,\n            final ApplicationModelConverter convertor) {\n        return new SearchApplications<IApplication>(IApplication.class, applicationService, searchDescriptor, options,\n                convertor);\n    }\n\n    public SearchApplications(final Class<E> applicationClass, final ApplicationService applicationService,\n            final SearchEntityDescriptor searchDescriptor, final SearchOptions options,\n            final ApplicationModelConverter convertor) {\n        super(searchDescriptor, options);\n        this.applicationClass = applicationClass;\n        this.applicationService = applicationService;\n        this.convertor = convertor;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions queryOptions) throws SBonitaReadException {\n        return applicationService.getNumberOfApplications(queryOptions);\n    }\n\n    @Override\n    public List<SApplication> executeSearch(final QueryOptions queryOptions) throws SBonitaReadException {\n        return applicationService.searchApplications(queryOptions);\n    }\n\n    @Override\n    public List<E> convertToClientObjects(final List<SApplication> serverObjects) {\n        if (IApplication.class.equals(applicationClass)) {\n            return (List<E>) convertor.toApplication(serverObjects);\n        } else {\n            return convertor.toApplication(serverObjects).stream().filter(applicationClass::isInstance)\n                    .map(applicationClass::cast).toList();\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/application/SearchApplicationsOfUser.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.application;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.impl.converter.ApplicationModelConverter;\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.IApplication;\nimport org.bonitasoft.engine.business.application.model.SApplication;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\n\npublic class SearchApplicationsOfUser<E extends IApplication> extends AbstractSearchEntity<E, SApplication> {\n\n    private final Class<E> applicationClass;\n    private long userId;\n    private final ApplicationService applicationService;\n    private final ApplicationModelConverter convertor;\n\n    public static SearchApplicationsOfUser<IApplication> defaultSearchApplicationsOfUser(\n            final long userId,\n            final ApplicationService applicationService,\n            final SearchEntityDescriptor searchDescriptor,\n            final SearchOptions options,\n            final ApplicationModelConverter convertor) {\n        return new SearchApplicationsOfUser<IApplication>(IApplication.class, userId, applicationService,\n                searchDescriptor, options, convertor);\n    }\n\n    public SearchApplicationsOfUser(final Class<E> applicationClass, final long userId,\n            final ApplicationService applicationService,\n            final SearchEntityDescriptor searchDescriptor, final SearchOptions options,\n            final ApplicationModelConverter convertor) {\n        super(searchDescriptor, options);\n        this.applicationClass = applicationClass;\n        this.userId = userId;\n        this.applicationService = applicationService;\n        this.convertor = convertor;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions queryOptions) throws SBonitaReadException {\n        return applicationService.getNumberOfApplicationsOfUser(userId, queryOptions);\n    }\n\n    @Override\n    public List<SApplication> executeSearch(final QueryOptions queryOptions) throws SBonitaReadException {\n        return applicationService.searchApplicationsOfUser(userId, queryOptions);\n    }\n\n    @Override\n    public List<E> convertToClientObjects(final List<SApplication> serverObjects) {\n        if (IApplication.class.equals(applicationClass)) {\n            return (List<E>) convertor.toApplication(serverObjects);\n        } else {\n            return convertor.toApplication(serverObjects).stream().filter(applicationClass::isInstance)\n                    .map(applicationClass::cast).toList();\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/category/CreateCategory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.category;\n\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.core.category.CategoryService;\nimport org.bonitasoft.engine.core.category.exception.SCategoryAlreadyExistsException;\nimport org.bonitasoft.engine.core.category.exception.SCategoryCreationException;\nimport org.bonitasoft.engine.core.category.model.SCategory;\n\n/**\n * @author Yanyan Liu\n */\npublic class CreateCategory implements TransactionContentWithResult<SCategory> {\n\n    private final CategoryService categoryService;\n\n    private final String name;\n\n    private final String description;\n\n    private SCategory sCategory;\n\n    public CreateCategory(final String name, final String description, final CategoryService categoryService) {\n        this.name = name;\n        this.description = description;\n        this.categoryService = categoryService;\n    }\n\n    @Override\n    public void execute() throws SCategoryAlreadyExistsException, SCategoryCreationException {\n        sCategory = categoryService.createCategory(name, description);\n    }\n\n    @Override\n    public SCategory getResult() {\n        return sCategory;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/category/DeleteSCategory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.category;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContent;\nimport org.bonitasoft.engine.core.category.CategoryService;\n\n/**\n * @author Yanyan Liu\n */\npublic class DeleteSCategory implements TransactionContent {\n\n    private final CategoryService categoryService;\n\n    private final long categoryId;\n\n    public DeleteSCategory(final CategoryService categoryService, final long processId) {\n        super();\n        this.categoryService = categoryService;\n        categoryId = processId;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        categoryService.deleteCategory(categoryId);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/category/GetCategories.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.category;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.core.category.CategoryService;\nimport org.bonitasoft.engine.core.category.model.SCategory;\nimport org.bonitasoft.engine.persistence.OrderByType;\n\n/**\n * @author Yanyan Liu\n */\npublic class GetCategories implements TransactionContentWithResult<List<SCategory>> {\n\n    private final int startIndex;\n\n    private final String fieldExecutor;\n\n    private final CategoryService categoryService;\n\n    private final int maxResults;\n\n    private final OrderByType orderExecutor;\n\n    private List<SCategory> categoryList;\n\n    public GetCategories(final int startIndex, final int maxResults, final String fieldExecutor,\n            final CategoryService categoryService,\n            final OrderByType orderExecutor) {\n        super();\n        this.startIndex = startIndex;\n        this.fieldExecutor = fieldExecutor;\n        this.categoryService = categoryService;\n        this.maxResults = maxResults;\n        this.orderExecutor = orderExecutor;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        if (fieldExecutor == null) {\n            categoryList = categoryService.getCategories(startIndex, maxResults, \"name\", OrderByType.ASC);\n        } else {\n            categoryList = categoryService.getCategories(startIndex, maxResults, fieldExecutor, orderExecutor);\n        }\n    }\n\n    @Override\n    public List<SCategory> getResult() {\n        return categoryList;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/category/GetCategory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.category;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.core.category.CategoryService;\nimport org.bonitasoft.engine.core.category.model.SCategory;\n\n/**\n * @author Yanyan Liu\n */\npublic class GetCategory implements TransactionContentWithResult<SCategory> {\n\n    private final CategoryService categoryService;\n\n    private final String categoryName;\n\n    private final long categoryId;\n\n    private SCategory sCategory;\n\n    public GetCategory(final CategoryService categoryService, final String categoryName) {\n        this.categoryService = categoryService;\n        this.categoryName = categoryName;\n        categoryId = -1;\n    }\n\n    public GetCategory(final CategoryService categoryService, final long categoryId) {\n        this.categoryService = categoryService;\n        this.categoryId = categoryId;\n        categoryName = null;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        if (categoryName != null) {\n            sCategory = categoryService.getCategoryByName(categoryName);\n        } else {\n            sCategory = categoryService.getCategory(categoryId);\n        }\n    }\n\n    @Override\n    public SCategory getResult() {\n        return sCategory;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/category/GetNumberOfCategories.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.category;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.core.category.CategoryService;\n\n/**\n * @author Yanyan Liu\n */\npublic class GetNumberOfCategories implements TransactionContentWithResult<Long> {\n\n    private final CategoryService categoryService;\n\n    private long categoryCount;\n\n    public GetNumberOfCategories(final CategoryService categoryService) {\n        super();\n        this.categoryService = categoryService;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        categoryCount = categoryService.getNumberOfCategories();\n    }\n\n    @Override\n    public Long getResult() {\n        return categoryCount;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/category/GetNumberOfCategoriesOfProcess.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.category;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.core.category.CategoryService;\n\n/**\n * @author Yanyan Liu\n */\npublic class GetNumberOfCategoriesOfProcess implements TransactionContentWithResult<Long> {\n\n    private final CategoryService categoryService;\n\n    private final long processDefinitionId;\n\n    private long numberOfCategories;\n\n    public GetNumberOfCategoriesOfProcess(final CategoryService categoryService, final long processDefinitionId) {\n        super();\n        this.categoryService = categoryService;\n        this.processDefinitionId = processDefinitionId;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        numberOfCategories = categoryService.getNumberOfCategoriesOfProcess(processDefinitionId);\n    }\n\n    @Override\n    public Long getResult() {\n        return numberOfCategories;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/category/GetNumberOfCategoriesUnrelatedToProcess.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.category;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.core.category.CategoryService;\n\n/**\n * @author Celine Souchet\n */\npublic class GetNumberOfCategoriesUnrelatedToProcess implements TransactionContentWithResult<Long> {\n\n    private final CategoryService categoryService;\n\n    private final long processDefinitionId;\n\n    private long numberOfCategories;\n\n    public GetNumberOfCategoriesUnrelatedToProcess(final CategoryService categoryService,\n            final long processDefinitionId) {\n        super();\n        this.categoryService = categoryService;\n        this.processDefinitionId = processDefinitionId;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        numberOfCategories = categoryService.getNumberOfCategoriesUnrelatedToProcess(processDefinitionId);\n    }\n\n    @Override\n    public Long getResult() {\n        return numberOfCategories;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/category/RemoveCategoriesFromProcessDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.category;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContent;\nimport org.bonitasoft.engine.core.category.CategoryService;\nimport org.bonitasoft.engine.core.category.exception.SCategoryException;\n\n/**\n * Transaction content to remove a list of categories from a Process definition.\n *\n * @author Emmanuel Duchastenier\n */\npublic class RemoveCategoriesFromProcessDefinition implements TransactionContent {\n\n    private final long processDefinitionId;\n\n    private final CategoryService categoryService;\n\n    private final List<Long> categoryIds;\n\n    public RemoveCategoriesFromProcessDefinition(final long processDefinitionId, final List<Long> categoryIds,\n            final CategoryService categoryService) {\n        this.processDefinitionId = processDefinitionId;\n        this.categoryIds = categoryIds;\n        this.categoryService = categoryService;\n    }\n\n    @Override\n    public void execute() throws SCategoryException, SBonitaException {\n        categoryService.removeCategoriesFromProcessDefinition(processDefinitionId, categoryIds);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/category/RemoveProcessDefinitionsOfCategory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.category;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContent;\nimport org.bonitasoft.engine.core.category.CategoryService;\nimport org.bonitasoft.engine.core.category.model.SProcessCategoryMapping;\nimport org.bonitasoft.engine.core.category.model.builder.SProcessCategoryMappingBuilderFactory;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\n\n/**\n * @author Yanyan Liu\n * @author Celine Souchet\n */\npublic class RemoveProcessDefinitionsOfCategory implements TransactionContent {\n\n    private final CategoryService categoryService;\n\n    private final long categoryId;\n\n    private final long processDefinitionId;\n\n    public RemoveProcessDefinitionsOfCategory(final CategoryService categoryService, final long categoryId) {\n        this.categoryService = categoryService;\n        this.categoryId = categoryId;\n        processDefinitionId = -1;\n    }\n\n    public RemoveProcessDefinitionsOfCategory(final long processDefinitionId, final CategoryService categoryService) {\n        this.processDefinitionId = processDefinitionId;\n        this.categoryService = categoryService;\n        categoryId = -1;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        final FilterOption filterOption;\n        if (categoryId != -1) {\n            filterOption = new FilterOption(SProcessCategoryMapping.class,\n                    BuilderFactory.get(SProcessCategoryMappingBuilderFactory.class).getCategoryIdKey(), categoryId);\n        } else {\n            filterOption = new FilterOption(SProcessCategoryMapping.class,\n                    BuilderFactory.get(SProcessCategoryMappingBuilderFactory.class).getProcessIdKey(),\n                    processDefinitionId);\n        }\n        final OrderByOption order = new OrderByOption(SProcessCategoryMapping.class,\n                BuilderFactory.get(SProcessCategoryMappingBuilderFactory.class).getIdKey(), OrderByType.ASC);\n        final QueryOptions queryOptions = new QueryOptions(0, 100, Collections.singletonList(order),\n                Collections.singletonList(filterOption), null);\n\n        long deletedProcessCategoryMappings;\n        do {\n            final List<SProcessCategoryMapping> processCategoryMappings = categoryService\n                    .searchProcessCategoryMappings(queryOptions);\n            deletedProcessCategoryMappings = categoryService.deleteProcessCategoryMappings(processCategoryMappings);\n        } while (deletedProcessCategoryMappings > 0);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/category/UpdateCategory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.category;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContent;\nimport org.bonitasoft.engine.core.category.CategoryService;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n */\npublic class UpdateCategory implements TransactionContent {\n\n    private final CategoryService categoryService;\n\n    private final long categoryId;\n\n    private final EntityUpdateDescriptor updateDescriptor;\n\n    public UpdateCategory(final CategoryService categoryService, final long categoryId,\n            final EntityUpdateDescriptor updateDescriptor) {\n        super();\n        this.categoryService = categoryService;\n        this.categoryId = categoryId;\n        this.updateDescriptor = updateDescriptor;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        categoryService.updateCategory(categoryId, updateDescriptor);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/command/DeleteSCommand.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.command;\n\nimport org.bonitasoft.engine.command.CommandService;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContent;\n\n/**\n * @author Matthieu Chaffotte\n * @author Yanyan Liu\n */\npublic class DeleteSCommand implements TransactionContent {\n\n    private final String name;\n\n    private final CommandService commandService;\n\n    private long commandId;\n\n    public DeleteSCommand(final CommandService commandService, final String name) {\n        this.commandService = commandService;\n        this.name = name;\n    }\n\n    public DeleteSCommand(final CommandService commandService, final long commandId) {\n        this.commandService = commandService;\n        this.commandId = commandId;\n        name = null;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        if (name == null) {\n            commandService.delete(commandId);\n        } else {\n            commandService.delete(name);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/command/GetCommands.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.command;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.command.CommandCriterion;\nimport org.bonitasoft.engine.command.CommandService;\nimport org.bonitasoft.engine.command.model.SCommand;\nimport org.bonitasoft.engine.command.model.SCommandCriterion;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\n\n/**\n * @author Hongwen Zang\n * @since 6.0\n */\npublic class GetCommands implements TransactionContentWithResult<List<SCommand>> {\n\n    private final CommandService commandService;\n\n    private final int startIndex;\n\n    private final int maxResults;\n\n    private final CommandCriterion sort;\n\n    private List<SCommand> commands;\n\n    public GetCommands(final CommandService commandService, final int startIndex, final int maxResults,\n            final CommandCriterion sort) {\n        super();\n        this.commandService = commandService;\n        this.startIndex = startIndex;\n        this.maxResults = maxResults;\n        this.sort = sort;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        SCommandCriterion sCommandCriterion = null;\n        if (CommandCriterion.NAME_ASC.equals(sort)) {\n            sCommandCriterion = SCommandCriterion.NAME_ASC;\n        } else {\n            sCommandCriterion = SCommandCriterion.NAME_DESC;\n        }\n        commands = commandService.getUserCommands(startIndex, maxResults, sCommandCriterion);\n    }\n\n    @Override\n    public List<SCommand> getResult() {\n        return commands;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/connector/GetConnectorImplementation.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.connector;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.core.connector.ConnectorService;\nimport org.bonitasoft.engine.core.connector.parser.SConnectorImplementationDescriptor;\n\n/**\n * @author Yanyan Liu\n */\npublic class GetConnectorImplementation implements TransactionContentWithResult<SConnectorImplementationDescriptor> {\n\n    private final ConnectorService connectorService;\n\n    private final long processDefinitionId;\n\n    private final String connectorId;\n\n    private final String connectorVersion;\n\n    private SConnectorImplementationDescriptor connectorImplementation;\n\n    public GetConnectorImplementation(final ConnectorService connectorService, final long processDefinitionId,\n            final String connectorId,\n            final String connectorVersion) {\n        this.connectorService = connectorService;\n        this.processDefinitionId = processDefinitionId;\n        this.connectorId = connectorId;\n        this.connectorVersion = connectorVersion;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        connectorImplementation = connectorService.getConnectorImplementation(processDefinitionId, connectorId,\n                connectorVersion);\n    }\n\n    @Override\n    public SConnectorImplementationDescriptor getResult() {\n        return connectorImplementation;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/document/GetDocumentByNameAtProcessInstantiation.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.document;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.api.impl.transaction.process.GetArchivedProcessInstanceList;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstance;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.core.document.api.DocumentService;\nimport org.bonitasoft.engine.core.document.model.AbstractSMappedDocument;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAProcessInstanceBuilderFactory;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class GetDocumentByNameAtProcessInstantiation implements TransactionContentWithResult<AbstractSMappedDocument> {\n\n    private final DocumentService documentService;\n\n    private final ProcessInstanceService processInstanceService;\n\n    private final SearchEntitiesDescriptor searchEntitiesDescriptor;\n\n    private final long processInstanceId;\n\n    private AbstractSMappedDocument result;\n\n    private final String documentName;\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    public GetDocumentByNameAtProcessInstantiation(final DocumentService documentService,\n            final ProcessInstanceService processInstanceService,\n            final ProcessDefinitionService processDefinitionService,\n            final SearchEntitiesDescriptor searchEntitiesDescriptor, final long processInstanceId,\n            final String documentName) {\n        this.documentService = documentService;\n        this.processDefinitionService = processDefinitionService;\n        this.processInstanceId = processInstanceId;\n        this.documentName = documentName;\n        this.processInstanceService = processInstanceService;\n        this.searchEntitiesDescriptor = searchEntitiesDescriptor;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        final GetArchivedProcessInstanceList getArchivedProcessInstanceList = new GetArchivedProcessInstanceList(\n                processInstanceService,\n                processDefinitionService, searchEntitiesDescriptor, processInstanceId, 0, 1,\n                BuilderFactory.get(SAProcessInstanceBuilderFactory.class)\n                        .getIdKey(),\n                OrderByType.ASC);\n        getArchivedProcessInstanceList.execute();\n        final ArchivedProcessInstance saProcessInstance = getArchivedProcessInstanceList.getResult().get(0);\n        final Date startDate = saProcessInstance.getStartDate();\n        final long startTime = startDate != null ? startDate.getTime() : 0;\n        result = documentService.getMappedDocument(processInstanceId, documentName, startTime);\n    }\n\n    @Override\n    public AbstractSMappedDocument getResult() {\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/event/GetEventInstances.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.event;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.event.SEventInstance;\nimport org.bonitasoft.engine.persistence.OrderByType;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic final class GetEventInstances implements TransactionContentWithResult<List<SEventInstance>> {\n\n    private final long rootContainerId;\n\n    private final EventInstanceService eventInstanceService;\n\n    private final int fromIndex;\n\n    private final int maxResults;\n\n    private final String fieldName;\n\n    private final OrderByType orderByType;\n\n    private List<SEventInstance> eventInstances;\n\n    public GetEventInstances(final EventInstanceService eventInstanceService, final long rootContainerId,\n            final int fromIndex, final int maxResults,\n            final String fieldName, final OrderByType orderByType) {\n        this.rootContainerId = rootContainerId;\n        this.eventInstanceService = eventInstanceService;\n        this.fromIndex = fromIndex;\n        this.maxResults = maxResults;\n        this.fieldName = fieldName;\n        this.orderByType = orderByType;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        this.eventInstances = this.eventInstanceService.getEventInstances(rootContainerId, fromIndex, maxResults,\n                fieldName, orderByType);\n    }\n\n    @Override\n    public List<SEventInstance> getResult() {\n        return this.eventInstances;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/expression/AbstractEvaluateExpressionsInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.expression;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.business.data.BusinessDataRepository;\nimport org.bonitasoft.engine.expression.Expression;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic abstract class AbstractEvaluateExpressionsInstance {\n\n    private final EntityMerger entityMerger;\n\n    public AbstractEvaluateExpressionsInstance(final BusinessDataRepository bdrService) {\n        entityMerger = new EntityMerger(bdrService);\n    }\n\n    protected String buildName(final Expression exp) {\n        String value = null;\n        if (exp != null) {\n            value = exp.getName() != null ? exp.getName() : exp.getContent();\n        }\n        return value;\n    }\n\n    protected Map<String, Serializable> getPartialContext(final Map<Expression, Map<String, Serializable>> expressions,\n            final Expression exp) {\n        Map<String, Serializable> partialContext = expressions.get(exp);\n        if (partialContext == null || partialContext.isEmpty()) {\n            return partialContext;\n        }\n\n        final Map<String, Serializable> result = new HashMap<String, Serializable>();\n        for (final Map.Entry<String, Serializable> entry : partialContext.entrySet()) {\n            result.put(entry.getKey(), entityMerger.merge(entry.getValue()));\n        }\n\n        return result;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/expression/EntityMerger.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.expression;\n\nimport java.io.Serializable;\nimport java.util.Collection;\n\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.bonitasoft.engine.business.data.BusinessDataRepository;\nimport org.bonitasoft.engine.business.data.proxy.ServerLazyLoader;\nimport org.bonitasoft.engine.business.data.proxy.ServerProxyfier;\n\n/**\n * @author Romain Bioteau\n */\npublic class EntityMerger {\n\n    private final BusinessDataRepository bdrService;\n\n    public EntityMerger(final BusinessDataRepository bdrService) {\n        this.bdrService = bdrService;\n    }\n\n    public Serializable merge(final Serializable value) {\n        if (isACollectionOfEntities(value)) {\n            final Collection<?> collection = (Collection<?>) value;\n            try {\n                @SuppressWarnings(\"unchecked\")\n                final Collection<Entity> newCollection = collection.getClass().newInstance();\n\n                ServerProxyfier proxyfier = new ServerProxyfier(new ServerLazyLoader(bdrService));\n                for (final Object item : collection) {\n                    newCollection.add(proxyfier.proxify((Entity) item));\n                }\n                return (Serializable) newCollection;\n            } catch (final InstantiationException | IllegalAccessException e) {\n                throw new IllegalStateException(e);\n            }\n        } else if (isAnEntity(value)) {\n            ServerProxyfier proxyfier = new ServerProxyfier(new ServerLazyLoader(bdrService));\n            return proxyfier.proxify((Entity) value);\n        } else {\n            return value;\n        }\n    }\n\n    private boolean isAnEntity(final Serializable value) {\n        return value instanceof Entity;\n    }\n\n    protected boolean isACollectionOfEntities(final Serializable value) {\n        return value instanceof Collection<?> && !((Collection<?>) value).isEmpty()\n                && ((Collection<?>) value).iterator().next() instanceof Entity;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/expression/EvaluateExpression.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.expression;\n\nimport java.io.Serializable;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class EvaluateExpression implements TransactionContentWithResult<Serializable> {\n\n    private final ExpressionResolverService expressionResolverService;\n\n    private final SExpressionContext expressionContext;\n\n    private final SExpression expression;\n\n    private Serializable result;\n\n    public EvaluateExpression(final ExpressionResolverService expressionResolverService,\n            final SExpressionContext expressionContext,\n            final SExpression expression) {\n        this.expressionResolverService = expressionResolverService;\n        this.expressionContext = expressionContext;\n        this.expression = expression;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        result = (Serializable) expressionResolverService.evaluate(expression, expressionContext);\n    }\n\n    @Override\n    public Serializable getResult() {\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/expression/EvaluateExpressionsDefinitionLevel.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.expression;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.business.data.BusinessDataRepository;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Zhao Na\n * @author Matthieu Chaffotte\n */\npublic class EvaluateExpressionsDefinitionLevel extends AbstractEvaluateExpressionsInstance\n        implements TransactionContentWithResult<Map<String, Serializable>> {\n\n    private final Map<Expression, Map<String, Serializable>> expressionsAndTheirPartialContext;\n\n    private final long processDefinitionId;\n\n    private final ExpressionResolverService expressionResolver;\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    private final Map<String, Serializable> results = new HashMap<String, Serializable>(0);\n\n    public EvaluateExpressionsDefinitionLevel(final Map<Expression, Map<String, Serializable>> expressions,\n            final long processDefinitionId,\n            final ExpressionResolverService expressionResolverService,\n            final ProcessDefinitionService processDefinitionService,\n            final BusinessDataRepository bdrService) {\n        super(bdrService);\n        expressionsAndTheirPartialContext = expressions;\n        this.processDefinitionId = processDefinitionId;\n        expressionResolver = expressionResolverService;\n        this.processDefinitionService = processDefinitionService;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        // FIXME: call the appropriate method(s) from the right service(s):\n        if (expressionsAndTheirPartialContext != null && !expressionsAndTheirPartialContext.isEmpty()) {\n            // how to deal with containerType and containerId\n            final SExpressionContext context = new SExpressionContext();\n            if (processDefinitionId != 0) {\n                final SProcessDefinition processDefinition = processDefinitionService\n                        .getProcessDefinition(processDefinitionId);\n                final Set<Expression> exps = expressionsAndTheirPartialContext.keySet();\n                for (final Expression exp : exps) {\n                    Map<String, Serializable> inputValues = getPartialContext(expressionsAndTheirPartialContext, exp);\n                    if (inputValues == null) {\n                        inputValues = new HashMap<String, Serializable>();\n                    }\n                    inputValues.put(SExpressionContext.PROCESS_DEFINITION_KEY, processDefinition);\n                    context.setProcessDefinitionId(processDefinitionId);\n                    context.setSerializableInputValues(inputValues);\n                    final SExpression sexp = ModelConvertor.constructSExpression(exp);\n                    final Serializable res = evaluateExpression(context, sexp, processDefinition);\n                    results.put(buildName(exp), res);\n                }\n            }\n        }\n\n    }\n\n    protected Serializable evaluateExpression(final SExpressionContext context, final SExpression sexp,\n            final SProcessDefinition processDefinition)\n            throws SExpressionTypeUnknownException, SExpressionEvaluationException,\n            SExpressionDependencyMissingException, SInvalidExpressionException {\n        try {\n            return (Serializable) expressionResolver.evaluate(sexp, context);\n        } catch (final SExpressionTypeUnknownException e) {\n            throw enrichExceptionContext(e, processDefinition);\n        } catch (final SExpressionEvaluationException e) {\n            throw enrichExceptionContext(e, processDefinition);\n        } catch (final SExpressionDependencyMissingException e) {\n            throw enrichExceptionContext(e, processDefinition);\n        } catch (final SInvalidExpressionException e) {\n            throw enrichExceptionContext(e, processDefinition);\n        }\n    }\n\n    private <T extends SBonitaException> T enrichExceptionContext(final T e,\n            final SProcessDefinition processDefinition) {\n        e.setProcessDefinitionIdOnContext(processDefinition.getId());\n        e.setProcessDefinitionNameOnContext(processDefinition.getName());\n        e.setProcessDefinitionVersionOnContext(processDefinition.getVersion());\n        return e;\n    }\n\n    @Override\n    public Map<String, Serializable> getResult() {\n        return results;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/expression/EvaluateExpressionsInstanceLevel.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.expression;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.business.data.BusinessDataRepository;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Zhao Na\n * @author Matthieu Chaffotte\n */\npublic class EvaluateExpressionsInstanceLevel extends AbstractEvaluateExpressionsInstance\n        implements TransactionContentWithResult<Map<String, Serializable>> {\n\n    private final Map<Expression, Map<String, Serializable>> expressions;\n\n    private final long containerId;\n\n    private final long processDefinitionId;\n\n    private final String containerType;\n\n    private final ExpressionResolverService expressionResolver;\n\n    private final Map<String, Serializable> results = new HashMap<String, Serializable>(0);\n\n    public EvaluateExpressionsInstanceLevel(final Map<Expression, Map<String, Serializable>> expressions,\n            final long containerId, final String containerType,\n            final long processDefinitionId, final ExpressionResolverService expressionService,\n            final BusinessDataRepository bdrService) {\n        super(bdrService);\n        this.expressions = expressions;\n        this.containerId = containerId;\n        expressionResolver = expressionService;\n        this.processDefinitionId = processDefinitionId;\n        this.containerType = containerType;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        // FIXME: call the appropriate method(s) from the right service(s):\n        if (expressions != null && !expressions.isEmpty()) {\n            final SExpressionContext context = new SExpressionContext();\n            context.setContainerId(containerId);\n            context.setContainerType(containerType);\n            context.setProcessDefinitionId(processDefinitionId);\n\n            final Set<Expression> exps = expressions.keySet();\n            for (final Expression exp : exps) {\n                final Map<String, Serializable> partialContext = getPartialContext(expressions, exp);\n                context.setSerializableInputValues(partialContext);\n                final SExpression sexp = ModelConvertor.constructSExpression(exp);\n                final Serializable res = (Serializable) expressionResolver.evaluate(sexp, context);\n                results.put(buildName(exp), res);// MAYBE instead of exp.getNAME\n            }\n        }\n    }\n\n    @Override\n    public Map<String, Serializable> getResult() {\n        return results;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/expression/EvaluateExpressionsInstanceLevelAndArchived.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.expression;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.business.data.BusinessDataRepository;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Zhao Na\n * @author Matthieu Chaffotte\n */\npublic class EvaluateExpressionsInstanceLevelAndArchived extends AbstractEvaluateExpressionsInstance implements\n        TransactionContentWithResult<Map<String, Serializable>> {\n\n    private final Map<Expression, Map<String, Serializable>> expressions;\n\n    private final long containerId;\n\n    private final long processDefinitionId;\n\n    private final String containerType;\n\n    private final long time;\n\n    private final ExpressionResolverService expressionResolver;\n\n    private final Map<String, Serializable> results = new HashMap<String, Serializable>(0);\n\n    public EvaluateExpressionsInstanceLevelAndArchived(final Map<Expression, Map<String, Serializable>> expressions,\n            final long containerId,\n            final String containerType, final long processDefinitionId, final long time,\n            final ExpressionResolverService expressionService,\n            final BusinessDataRepository bdrService) {\n        super(bdrService);\n        this.expressions = expressions;\n        this.containerId = containerId;\n        expressionResolver = expressionService;\n        this.processDefinitionId = processDefinitionId;\n        this.containerType = containerType;\n        this.time = time;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        // FIXME: call the appropriate method(s) from the right service(s):\n        if (expressions != null && !expressions.isEmpty()) {\n            // how to deal with containerType and containerId\n            final SExpressionContext context = new SExpressionContext();\n            context.setContainerId(containerId);\n            context.setContainerType(containerType);\n            context.setProcessDefinitionId(processDefinitionId);\n            context.setTime(time);\n\n            final Set<Expression> exps = expressions.keySet();\n            for (final Expression exp : exps) {\n                final Map<String, Serializable> partialContext = getPartialContext(expressions, exp);\n                context.setSerializableInputValues(partialContext);\n                final SExpression sexp = ModelConvertor.constructSExpression(exp);\n                final Serializable res = (Serializable) expressionResolver.evaluate(sexp, context);\n                results.put(buildName(exp), res);// MAYBE instead of exp.getNAME\n            }\n        }\n    }\n\n    @Override\n    public Map<String, Serializable> getResult() {\n        return results;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/flownode/GetFlowNodeInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.flownode;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class GetFlowNodeInstance implements TransactionContentWithResult<SFlowNodeInstance> {\n\n    private SFlowNodeInstance flowNodeInstance = null;\n\n    private final long flowNodeInstanceId;\n\n    private final ActivityInstanceService activityInstanceService;\n\n    public GetFlowNodeInstance(final ActivityInstanceService activityInstanceService, final long flowNodeInstanceId) {\n        this.activityInstanceService = activityInstanceService;\n        this.flowNodeInstanceId = flowNodeInstanceId;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        flowNodeInstance = activityInstanceService.getFlowNodeInstance(flowNodeInstanceId);\n    }\n\n    @Override\n    public SFlowNodeInstance getResult() {\n        return flowNodeInstance;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/flownode/SearchFlowNodeInstances.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.flownode;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.QueryOptions;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SearchFlowNodeInstances<T extends SFlowNodeInstance> implements TransactionContentWithResult<List<T>> {\n\n    private final FlowNodeInstanceService flowNodeInstanceService;\n\n    private final QueryOptions queryOptions;\n\n    private final Class<T> entityClass;\n\n    private List<T> result;\n\n    private long count;\n\n    public SearchFlowNodeInstances(final FlowNodeInstanceService flowNodeInstanceService,\n            final QueryOptions queryOptions,\n            final Class<T> entityClass) {\n        super();\n        this.flowNodeInstanceService = flowNodeInstanceService;\n        this.queryOptions = queryOptions;\n        this.entityClass = entityClass;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        result = flowNodeInstanceService.searchFlowNodeInstances(entityClass, queryOptions);\n        final QueryOptions countOptions = new QueryOptions(0, 1, Collections.<OrderByOption> emptyList(),\n                queryOptions.getFilters(),\n                queryOptions.getMultipleFilter());\n        count = flowNodeInstanceService.getNumberOfFlowNodeInstances(entityClass, countOptions);\n    }\n\n    @Override\n    public List<T> getResult() {\n        return result;\n    }\n\n    public long getCount() {\n        return count;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/flownode/SetExpectedEndDate.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.flownode;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContent;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SetExpectedEndDate implements TransactionContent {\n\n    private final ActivityInstanceService activityInstanceService;\n\n    private final long userTaskId;\n\n    private final Date dueDate;\n\n    public SetExpectedEndDate(final ActivityInstanceService activityInstanceService, final long userTaskId,\n            final Date dueDate) {\n        this.activityInstanceService = activityInstanceService;\n        this.userTaskId = userTaskId;\n        this.dueDate = dueDate;\n\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        final SFlowNodeInstance flowNodeInstance = activityInstanceService.getFlowNodeInstance(userTaskId);\n        activityInstanceService.setExpectedEndDate(flowNodeInstance, convertToLong());\n    }\n\n    public Long convertToLong() {\n        if (dueDate == null) {\n            return null;\n        }\n        return dueDate.getTime();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/AddUserMembership.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.identity;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.identity.model.SRole;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.identity.model.SUserMembership;\n\n/**\n * @author Bole Zhang\n * @author Matthieu Chaffotte\n */\npublic class AddUserMembership implements TransactionContentWithResult<SUserMembership> {\n\n    private final IdentityService identityService;\n\n    private final long userId;\n\n    private final long groupId;\n\n    private final long roleId;\n\n    private final long assignedBy;\n\n    private SUserMembership userMembership;\n\n    public AddUserMembership(final long userId, final long groupId, final long roleId, final long assignedBy,\n            final IdentityService identityService) {\n        this.assignedBy = assignedBy;\n        this.identityService = identityService;\n        this.userId = userId;\n        this.groupId = groupId;\n        this.roleId = roleId;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        // FIXME: if RDBMS has foreign keys, getUser, getRole, getGroup can be omitted:\n        final SUser user = identityService.getUser(userId);\n        final SRole role = identityService.getRole(roleId);\n        final SGroup group = identityService.getGroup(groupId);\n        userMembership = SUserMembership.builder().userId(user.getId()).groupId(group.getId()).roleId(role.getId())\n                .assignedBy(assignedBy)\n                .assignedDate(System.currentTimeMillis()).build();\n        identityService.createUserMembership(userMembership);\n    }\n\n    @Override\n    public SUserMembership getResult() {\n        return userMembership;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/AddUserMemberships.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.identity;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContent;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.model.SUserMembership;\n\n/**\n * @author Lu Kai\n * @author Matthieu Chaffotte\n */\npublic class AddUserMemberships implements TransactionContent {\n\n    private final List<Long> userIds;\n\n    private final IdentityService identityService;\n\n    private final long groupId;\n\n    private final long roleId;\n\n    private final long currentUserId;\n\n    public AddUserMemberships(final long groupId, final long roleId, final List<Long> userIds,\n            final IdentityService identityService, final long currentUserId) {\n        this.groupId = groupId;\n        this.roleId = roleId;\n        this.userIds = userIds;\n        this.identityService = identityService;\n        this.currentUserId = currentUserId;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n\n        for (final long userId : userIds) {\n            final SUserMembership userMembership = SUserMembership.builder().userId(userId).groupId(groupId)\n                    .roleId(roleId)\n                    .assignedBy(currentUserId).build();\n            identityService.createUserMembership(userMembership);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/DeleteGroup.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.identity;\n\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContent;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.profile.ProfileService;\n\n/**\n * @author Lu Kai\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n * @author Emmanuel Duchastenier\n */\npublic class DeleteGroup extends DeleteWithActorMembers implements TransactionContent {\n\n    private final long groupId;\n\n    public DeleteGroup(final IdentityService identityService, final ActorMappingService actorMappingService,\n            final ProfileService profileService,\n            final long groupId) {\n        super(actorMappingService, profileService, identityService);\n        this.groupId = groupId;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        deleteMembershipsByGroup(groupId);\n        deleteActorMembersOfGroup(groupId);\n        deleteProfileMembersOfGroup(groupId);\n        getIdentityService().deleteChildrenGroup(groupId);\n        getIdentityService().deleteGroup(groupId);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/DeleteGroups.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.identity;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContent;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.profile.ProfileService;\n\n/**\n * @author Lu Kai\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class DeleteGroups extends DeleteWithActorMembers implements TransactionContent {\n\n    private final List<Long> groupIds;\n\n    public DeleteGroups(final IdentityService identityService, final ActorMappingService actorMappingService,\n            final ProfileService profileService,\n            final List<Long> groupIds) {\n        super(actorMappingService, profileService, identityService);\n        this.groupIds = groupIds;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        final ArrayList<Long> list = new ArrayList<Long>(groupIds);\n        while (!list.isEmpty()) {\n            final Long groupId = list.get(0);\n            deleteMembershipsByGroup(groupId);\n            deleteActorMembersOfGroup(groupId);\n            deleteProfileMembersOfGroup(groupId);\n            final List<Long> deleteChildrenGroup = getIdentityService().deleteChildrenGroup(groupId);\n            list.removeAll(deleteChildrenGroup);\n            getIdentityService().deleteGroup(groupId);\n            list.remove(0);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/DeleteRole.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.identity;\n\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContent;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.profile.ProfileService;\n\n/**\n * @author Lu Kai\n * @author Matthieu Chaffotte\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n */\npublic class DeleteRole extends DeleteWithActorMembers implements TransactionContent {\n\n    private final long roleId;\n\n    public DeleteRole(final IdentityService identityService, final ActorMappingService actorMappingService,\n            final ProfileService profileService,\n            final long roleId) {\n        super(actorMappingService, profileService, identityService);\n        this.roleId = roleId;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        deleteMembershipsByRole(roleId);\n        deleteActorMembersOfRole(roleId);\n        deleteProfileMembersOfRole(roleId);\n        getIdentityService().deleteRole(roleId);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/DeleteRoles.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.identity;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContent;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.profile.ProfileService;\n\n/**\n * @author Lu Kai\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class DeleteRoles extends DeleteWithActorMembers implements TransactionContent {\n\n    private final List<Long> roleIds;\n\n    public DeleteRoles(final IdentityService identityService, final ActorMappingService actorMappingService,\n            final ProfileService profileService,\n            final List<Long> roleIds) {\n        super(actorMappingService, profileService, identityService);\n        this.roleIds = roleIds;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        for (final Long roleId : roleIds) {\n            // FIXME: refactor to use the same code as DeleteRole, same thing for DeleteGroups and DeleteUsers\n            deleteMembershipsByRole(roleId);\n            deleteActorMembersOfRole(roleId);\n            deleteProfileMembersOfRole(roleId);\n            getIdentityService().deleteRole(roleId);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/DeleteUser.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.identity;\n\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContent;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.SUserNotFoundException;\nimport org.bonitasoft.engine.profile.ProfileService;\n\n/**\n * @author Lu Kai\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class DeleteUser extends DeleteWithActorMembers implements TransactionContent {\n\n    private final long userId;\n\n    private final String userName;\n\n    public DeleteUser(final IdentityService identityService, final ActorMappingService actorMappingService,\n            final ProfileService profileService,\n            final long userId) {\n        super(actorMappingService, profileService, identityService);\n        this.userId = userId;\n        userName = null;\n    }\n\n    public DeleteUser(final IdentityService identityService, final ActorMappingService actorMappingService,\n            final ProfileService profileService,\n            final String userName) {\n        super(actorMappingService, profileService, identityService);\n        userId = -1;\n        this.userName = userName;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        try {\n            long id = userId;\n            if (id == -1) {\n                id = getIdentityService().getUserByUserName(userName).getId();\n            }\n            deleteUserMembershipsByUser(id);\n            deleteActorMembersOfUser(id);\n            deleteProfileMembersOfUser(id);\n            getIdentityService().deleteUser(id);\n        } catch (SUserNotFoundException notFound) {\n            // not found, don't do anything specific\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/DeleteUsers.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.identity;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContent;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.profile.ProfileService;\n\n/**\n * @author Lu Kai\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @author Emmanuel Duchastenier\n */\npublic class DeleteUsers extends DeleteWithActorMembers implements TransactionContent {\n\n    private final List<Long> userIds;\n\n    public DeleteUsers(final IdentityService identityService, final ActorMappingService actorMappingService,\n            final ProfileService profileService,\n            final List<Long> userIds) {\n        super(actorMappingService, profileService, identityService);\n        this.userIds = userIds;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        for (final Long userId : userIds) {\n            deleteUserMembershipsByUser(userId);\n            deleteActorMembersOfUser(userId);\n            deleteProfileMembersOfUser(userId);\n            getIdentityService().deleteUser(userId);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/DeleteWithActorMembers.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.identity;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.actor.mapping.SActorMemberDeletionException;\nimport org.bonitasoft.engine.actor.mapping.SActorMemberNotFoundException;\nimport org.bonitasoft.engine.actor.mapping.model.SActorMember;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.SIdentityException;\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.identity.model.SUserMembership;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.profile.ProfileService;\nimport org.bonitasoft.engine.profile.model.SProfileMember;\n\n/**\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n */\npublic class DeleteWithActorMembers {\n\n    protected static final int BATCH_SIZE = 100;\n\n    private final ActorMappingService actorMappingService;\n\n    private final ProfileService profileService;\n\n    private final IdentityService identityService;\n\n    private final Set<Long> removedActorIds = new HashSet<Long>();\n\n    public DeleteWithActorMembers(final ActorMappingService actorMappingService, final ProfileService profileService,\n            final IdentityService identityService) {\n        super();\n        this.actorMappingService = actorMappingService;\n        this.profileService = profileService;\n        this.identityService = identityService;\n    }\n\n    public Set<Long> getRemovedActorIds() {\n        return removedActorIds;\n    }\n\n    protected void setActorIdsOfRemovedElements(final SActorMember removedActorMember) {\n        removedActorIds.add(removedActorMember.getActorId());\n    }\n\n    protected void deleteActorMembersOfUser(final long userId)\n            throws SActorMemberNotFoundException, SActorMemberDeletionException, SBonitaReadException {\n        List<SActorMember> actorMembers = actorMappingService.getActorMembersOfUser(userId, 0, BATCH_SIZE);\n        while (!actorMembers.isEmpty()) {\n            for (final SActorMember sActorMember : actorMembers) {\n                setActorIdsOfRemovedElements(actorMappingService.deleteActorMember(sActorMember.getId()));\n            }\n            actorMembers = actorMappingService.getActorMembersOfUser(userId, 0, BATCH_SIZE);\n        }\n    }\n\n    protected void deleteProfileMembersOfUser(final long id) throws SBonitaException {\n        List<SProfileMember> profileMembersOfUser;\n        do {\n            profileMembersOfUser = profileService.getProfileMembersOfUser(id, 0, BATCH_SIZE, SProfileMember.ID,\n                    OrderByType.ASC);\n            for (final SProfileMember sProfileMember : profileMembersOfUser) {\n                profileService.deleteProfileMember(sProfileMember);\n            }\n        } while (profileMembersOfUser.size() == BATCH_SIZE);\n    }\n\n    protected void deleteUserMembershipsByUser(final long id) throws SIdentityException {\n        List<SUserMembership> sUserMemberships = identityService.getUserMembershipsOfUser(id, 0, BATCH_SIZE);\n        while (!sUserMemberships.isEmpty()) {\n            for (final SUserMembership sUserMembership : sUserMemberships) {\n                identityService.deleteUserMembership(sUserMembership.getId());\n            }\n            sUserMemberships = identityService.getUserMembershipsOfUser(id, 0, BATCH_SIZE);\n        }\n    }\n\n    protected void deleteActorMembersOfGroup(final long groupId)\n            throws SActorMemberNotFoundException, SActorMemberDeletionException, SBonitaReadException,\n            SIdentityException {\n        List<SActorMember> actorMembers;\n        do {\n            actorMembers = actorMappingService.getActorMembersOfGroup(groupId, 0, BATCH_SIZE);\n            for (final SActorMember sActorMember : actorMembers) {\n                setActorIdsOfRemovedElements(actorMappingService.deleteActorMember(sActorMember.getId()));\n            }\n        } while (actorMembers.size() == BATCH_SIZE);\n\n        deleteActorMembersOfGroupChildren(groupId);\n    }\n\n    private void deleteActorMembersOfGroupChildren(final long groupId)\n            throws SIdentityException, SActorMemberNotFoundException, SActorMemberDeletionException,\n            SBonitaReadException {\n        int i = 0;\n        List<SGroup> childrenGroup;\n        do {\n            childrenGroup = identityService.getGroupChildren(groupId, i, BATCH_SIZE);\n            i += BATCH_SIZE;\n            for (final SGroup sGroup : childrenGroup) {\n                deleteActorMembersOfGroup(sGroup.getId());\n            }\n        } while (childrenGroup.size() == BATCH_SIZE);\n    }\n\n    protected void deleteProfileMembersOfGroup(final long groupId) throws SBonitaException {\n        List<SProfileMember> profileMembers;\n        do {\n            profileMembers = profileService.getProfileMembersOfGroup(groupId, 0, BATCH_SIZE, SProfileMember.ID,\n                    OrderByType.ASC);\n            for (final SProfileMember sProfileMember : profileMembers) {\n                profileService.deleteProfileMember(sProfileMember);\n            }\n        } while (profileMembers.size() == BATCH_SIZE);\n\n        deleteProfileMembersOfGroupChildren(groupId);\n    }\n\n    private void deleteProfileMembersOfGroupChildren(final long groupId) throws SIdentityException, SBonitaException {\n        int i = 0;\n        List<SGroup> childrenGroup;\n        do {\n            childrenGroup = identityService.getGroupChildren(groupId, i, BATCH_SIZE);\n            i += BATCH_SIZE;\n            for (final SGroup sGroup : childrenGroup) {\n                deleteProfileMembersOfGroup(sGroup.getId());\n            }\n        } while (childrenGroup.size() == BATCH_SIZE);\n    }\n\n    protected void deleteMembershipsByGroup(final long groupId) throws SBonitaException {\n        List<SUserMembership> memberships;\n        do {\n            memberships = identityService.getUserMembershipsOfGroup(groupId, 0, BATCH_SIZE);\n            for (final SUserMembership sUserMembership : memberships) {\n                identityService.deleteUserMembership(sUserMembership.getId());\n            }\n        } while (memberships.size() == BATCH_SIZE);\n    }\n\n    protected void deleteActorMembersOfRole(final long roleId)\n            throws SActorMemberNotFoundException, SActorMemberDeletionException, SBonitaReadException {\n        List<SActorMember> actorMembers;\n        do {\n            actorMembers = actorMappingService.getActorMembersOfRole(roleId, 0, BATCH_SIZE);\n            for (final SActorMember sActorMember : actorMembers) {\n                setActorIdsOfRemovedElements(actorMappingService.deleteActorMember(sActorMember.getId()));\n            }\n        } while (actorMembers.size() == BATCH_SIZE);\n    }\n\n    protected void deleteProfileMembersOfRole(final long roleId) throws SBonitaException {\n        List<SProfileMember> profileMembers;\n        do {\n            profileMembers = profileService.getProfileMembersOfRole(roleId, 0, BATCH_SIZE, SProfileMember.ID,\n                    OrderByType.ASC);\n            for (final SProfileMember sProfileMember : profileMembers) {\n                profileService.deleteProfileMember(sProfileMember);\n            }\n        } while (profileMembers.size() == BATCH_SIZE);\n    }\n\n    protected void deleteMembershipsByRole(final long roleId) throws SBonitaException {\n        List<SUserMembership> memberships;\n        do {\n            memberships = identityService.getUserMembershipsOfRole(roleId, 0, BATCH_SIZE);\n            for (final SUserMembership sUserMembership : memberships) {\n                identityService.deleteUserMembership(sUserMembership.getId());\n            }\n        } while (memberships.size() == BATCH_SIZE);\n    }\n\n    public ActorMappingService getActorMappingService() {\n        return actorMappingService;\n    }\n\n    public ProfileService getProfileService() {\n        return profileService;\n    }\n\n    public IdentityService getIdentityService() {\n        return identityService;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/GetGroups.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.identity;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.persistence.OrderByType;\n\n/**\n * @author Lu Kai\n * @author Matthieu Chaffotte\n */\npublic class GetGroups implements TransactionContentWithResult<List<SGroup>> {\n\n    private final IdentityService identityService;\n\n    private final int startIndex;\n\n    private final int maxResults;\n\n    private final OrderByType orderExecutor;\n\n    private final String fieldExecutor;\n\n    private List<SGroup> groups;\n\n    public GetGroups(final IdentityService identityService, final int startIndex, final int maxResults,\n            final OrderByType orderExecutor,\n            final String fieldExecutor) {\n        this.identityService = identityService;\n        this.startIndex = startIndex;\n        this.maxResults = maxResults;\n        this.orderExecutor = orderExecutor;\n        this.fieldExecutor = fieldExecutor;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        if (fieldExecutor == null) {\n            groups = identityService.getGroups(startIndex, maxResults);\n        } else {\n            groups = identityService.getGroups(startIndex, maxResults, fieldExecutor, orderExecutor);\n        }\n    }\n\n    @Override\n    public List<SGroup> getResult() {\n        return groups;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/GetNumberOfInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.identity;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.identity.IdentityService;\n\n/**\n * @author Lu Kai\n */\npublic class GetNumberOfInstance implements TransactionContentWithResult<Long> {\n\n    private final IdentityService identityService;\n\n    private long number;\n\n    private final String instanceName;\n\n    public GetNumberOfInstance(final String instanceName, final IdentityService identityService) {\n        this.instanceName = instanceName;\n        this.identityService = identityService;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        if (instanceName.equals(\"getNumberOfGroups\")) {\n            number = identityService.getNumberOfGroups();\n        }\n        if (instanceName.equals(\"getNumberOfRoles\")) {\n            number = identityService.getNumberOfRoles();\n        }\n        if (instanceName.equals(\"getNumberOfUsers\")) {\n            number = identityService.getNumberOfUsers();\n        }\n    }\n\n    @Override\n    public Long getResult() {\n        return number;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/GetNumberOfUserMemberships.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.identity;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.identity.IdentityService;\n\n/**\n * @author Bole Zhang\n * @author Baptiste Mesta\n */\npublic class GetNumberOfUserMemberships implements TransactionContentWithResult<Long> {\n\n    private final IdentityService identityService;\n\n    private long number;\n\n    private final long userId;\n\n    public GetNumberOfUserMemberships(final long userId, final IdentityService identityService) {\n        this.userId = userId;\n        this.identityService = identityService;\n    }\n\n    public GetNumberOfUserMemberships(final IdentityService identityService) {\n        userId = -1;\n        this.identityService = identityService;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        if (userId == -1) {\n            number = identityService.getNumberOfUserMemberships();\n        } else {\n            number = identityService.getNumberOfUserMembershipsOfUser(userId);\n        }\n    }\n\n    @Override\n    public Long getResult() {\n        return number;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/GetNumberOfUsersInType.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.identity;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.identity.IdentityService;\n\n/**\n * @author Lu Kai\n */\npublic class GetNumberOfUsersInType implements TransactionContentWithResult<Long> {\n\n    private final long id;\n\n    private final IdentityService identityService;\n\n    private long userNumber;\n\n    private final String instanceName;\n\n    public GetNumberOfUsersInType(final long id, final String instanceName, final IdentityService identityService) {\n        this.id = id;\n        this.instanceName = instanceName;\n        this.identityService = identityService;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        if (instanceName.equals(\"getNumberOfUsersInGroup\")) {\n            userNumber = identityService.getNumberOfUsersByGroup(id);\n        }\n        if (instanceName.equals(\"getNumberOfUsersInRole\")) {\n            userNumber = identityService.getNumberOfUsersByRole(id);\n        }\n    }\n\n    @Override\n    public Long getResult() {\n        return userNumber;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/GetRoles.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.identity;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.model.SRole;\nimport org.bonitasoft.engine.persistence.OrderByType;\n\n/**\n * @author Lu Kai\n * @author Matthieu Chaffotte\n */\npublic class GetRoles implements TransactionContentWithResult<List<SRole>> {\n\n    private final int startIndex;\n\n    private final String fieldExecutor;\n\n    private final IdentityService identityService;\n\n    private final int maxResults;\n\n    private final OrderByType orderExecutor;\n\n    private List<SRole> roleList;\n\n    public GetRoles(final IdentityService identityService, final int startIndex, final int maxResults,\n            final String fieldExecutor,\n            final OrderByType orderExecutor) {\n        this.startIndex = startIndex;\n        this.fieldExecutor = fieldExecutor;\n        this.identityService = identityService;\n        this.maxResults = maxResults;\n        this.orderExecutor = orderExecutor;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        if (fieldExecutor == null) {\n            roleList = identityService.getRoles(startIndex, maxResults);\n        } else {\n            roleList = identityService.getRoles(startIndex, maxResults, fieldExecutor, orderExecutor);\n        }\n    }\n\n    @Override\n    public List<SRole> getResult() {\n        return roleList;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/GetSContactInfo.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.identity;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.model.SContactInfo;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class GetSContactInfo implements TransactionContentWithResult<SContactInfo> {\n\n    private final IdentityService identityService;\n\n    private final long userId;\n\n    private final boolean personal;\n\n    private SContactInfo sContactInfo;\n\n    public GetSContactInfo(final long userId, final IdentityService identityService, final boolean personal) {\n        super();\n        this.userId = userId;\n        this.identityService = identityService;\n        this.personal = personal;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        sContactInfo = identityService.getUserContactInfo(userId, personal);\n    }\n\n    @Override\n    public SContactInfo getResult() {\n        return sContactInfo;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/GetSUser.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.identity;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.model.SUser;\n\n/**\n * @author Lu Kai\n * @author Matthieu Chaffotte\n */\npublic class GetSUser implements TransactionContentWithResult<SUser> {\n\n    private final IdentityService identityService;\n\n    private final String userName;\n\n    private final long userId;\n\n    private SUser sUser;\n\n    public GetSUser(final IdentityService identityService, final String userName) {\n        this.identityService = identityService;\n        this.userName = userName;\n        userId = -1;\n    }\n\n    public GetSUser(final IdentityService identityService, final long userId) {\n        this.identityService = identityService;\n        this.userId = userId;\n        userName = null;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        if (userName != null) {\n            sUser = identityService.getUserByUserName(userName);\n        } else {\n            sUser = identityService.getUser(userId);\n        }\n    }\n\n    @Override\n    public SUser getResult() {\n        return sUser;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/GetUserMembership.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.identity;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.model.SUserMembership;\n\n/**\n * @author Bole Zhang\n */\npublic class GetUserMembership implements TransactionContentWithResult<SUserMembership> {\n\n    private final IdentityService identityService;\n\n    private final long userId;\n\n    private SUserMembership sUserMembership;\n\n    private final long groupId;\n\n    private final long roleId;\n\n    private final long userMembershipId;\n\n    public GetUserMembership(final long userId, final long groupId, final long roleId,\n            final IdentityService identityService) {\n        this.userId = userId;\n        this.groupId = groupId;\n        this.roleId = roleId;\n        this.identityService = identityService;\n        userMembershipId = -1;\n    }\n\n    public GetUserMembership(final long userMembershipId, final IdentityService identityService) {\n        this.userMembershipId = userMembershipId;\n        this.identityService = identityService;\n        userId = -1;\n        groupId = -1;\n        roleId = -1;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        if (userMembershipId == -1) {\n            sUserMembership = identityService.getUserMembership(userId, groupId, roleId);\n        } else {\n            sUserMembership = identityService.getUserMembership(userMembershipId);\n        }\n    }\n\n    @Override\n    public SUserMembership getResult() {\n        return sUserMembership;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/GetUserMembershipsOfGroup.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.identity;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.model.SUserMembership;\n\n/**\n * @author Bole Zhang\n * @author Baptiste Mesta\n */\npublic class GetUserMembershipsOfGroup implements TransactionContentWithResult<List<SUserMembership>> {\n\n    private final long groupId;\n\n    private final IdentityService identityService;\n\n    private List<SUserMembership> sMembership;\n\n    private final int startIndex;\n\n    private final int maxResults;\n\n    public GetUserMembershipsOfGroup(final long groupId, final IdentityService identityService, final int startIndex,\n            final int maxResults) {\n        this.groupId = groupId;\n        this.identityService = identityService;\n        this.startIndex = startIndex;\n        this.maxResults = maxResults;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        sMembership = identityService.getUserMembershipsOfGroup(groupId, startIndex, maxResults);\n    }\n\n    @Override\n    public List<SUserMembership> getResult() {\n        return sMembership;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/GetUserMembershipsOfRole.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.identity;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.model.SUserMembership;\n\n/**\n * @author Bole Zhang\n * @author Baptiste Mesta\n */\npublic class GetUserMembershipsOfRole implements TransactionContentWithResult<List<SUserMembership>> {\n\n    private final long roleId;\n\n    private final IdentityService identityService;\n\n    private List<SUserMembership> sMembership;\n\n    private final int startIndex;\n\n    private final int maxResults;\n\n    public GetUserMembershipsOfRole(final long roleId, final IdentityService identityService, final int startIndex,\n            final int maxResults) {\n        this.roleId = roleId;\n        this.identityService = identityService;\n        this.startIndex = startIndex;\n        this.maxResults = maxResults;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        sMembership = identityService.getUserMembershipsOfRole(roleId, startIndex, maxResults);\n    }\n\n    @Override\n    public List<SUserMembership> getResult() {\n        return sMembership;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/UpdateGroup.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.identity;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.SIdentityException;\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Lu Kai\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n */\npublic class UpdateGroup {\n\n    private static final int BATCH_SIZE = 100;\n\n    private final long groupId;\n\n    private final EntityUpdateDescriptor changeDescriptor;\n\n    private final IdentityService identityService;\n    private EntityUpdateDescriptor iconUpdater;\n\n    public UpdateGroup(final long groupId, final EntityUpdateDescriptor changeDescriptor,\n            final IdentityService identityService,\n            EntityUpdateDescriptor iconUpdater) {\n        this.groupId = groupId;\n        this.changeDescriptor = changeDescriptor;\n        this.identityService = identityService;\n        this.iconUpdater = iconUpdater;\n    }\n\n    public SGroup update() throws SIdentityException {\n        SGroup sGroup = identityService.getGroup(groupId);\n\n        // if the parent path changes it's also necessary to change the children's parent path\n        final String parentPathKey = SGroup.PARENT_PATH;\n        final String nameKey = SGroup.NAME;\n        final Map<String, Object> fields = changeDescriptor.getFields();\n        if (fields.containsKey(parentPathKey) || fields.containsKey(nameKey)) {\n            final String parentPath = fields.containsKey(parentPathKey) ? (String) fields.get(parentPathKey)\n                    : sGroup.getParentPath();\n            final String groupName = fields.containsKey(nameKey) ? (String) fields.get(nameKey) : sGroup.getName();\n            updateChildren(sGroup, parentPath, SGroup.ID, parentPathKey, groupName);\n        }\n        identityService.updateGroup(sGroup, changeDescriptor, iconUpdater);\n        return sGroup;\n    }\n\n    private void updateChildren(final SGroup group, final String parentPath, final String idKey,\n            final String parentPathKey, final String groupName)\n            throws SIdentityException {\n        List<SGroup> groupChildren;\n        int i = 0;\n        do {\n            groupChildren = identityService.getGroupChildren(group.getId(), i * BATCH_SIZE, BATCH_SIZE, idKey,\n                    OrderByType.ASC);\n            i++;\n            for (final SGroup child : groupChildren) {\n                if (parentPath != null) {\n                    updateChildren(child, parentPath + '/' + groupName, idKey, parentPathKey, child.getName());\n                } else {\n                    updateChildren(child, '/' + groupName, idKey, parentPathKey, child.getName());\n                }\n            }\n        } while (!groupChildren.isEmpty());\n        final EntityUpdateDescriptor updateDescriptor = new EntityUpdateDescriptor();\n        updateDescriptor.addField(parentPathKey, parentPath);\n        identityService.updateGroup(group, updateDescriptor, null);\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/UpdateMembershipByRoleIdAndGroupId.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.identity;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContent;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Bole Zhang\n */\npublic class UpdateMembershipByRoleIdAndGroupId implements TransactionContent {\n\n    private final IdentityService identityService;\n\n    private final EntityUpdateDescriptor changeDescriptor;\n\n    private final long userMemebershipId;\n\n    public UpdateMembershipByRoleIdAndGroupId(final long userMemebershipId, final IdentityService identityService,\n            final EntityUpdateDescriptor changeDescriptor) {\n        this.userMemebershipId = userMemebershipId;\n        this.identityService = identityService;\n        this.changeDescriptor = changeDescriptor;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        identityService.updateUserMembership(identityService.getLightUserMembership(userMemebershipId),\n                changeDescriptor);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/page/SearchPages.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.page;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.impl.converter.PageModelConverter;\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.engine.page.PageService;\nimport org.bonitasoft.engine.page.SPage;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SearchPages extends AbstractSearchEntity<Page, SPage> {\n\n    private final PageService pageService;\n\n    public SearchPages(final PageService pageService, final SearchEntityDescriptor searchDescriptor,\n            final SearchOptions options) {\n        super(searchDescriptor, options);\n        this.pageService = pageService;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions queryOptions) throws SBonitaReadException {\n        try {\n            return pageService.getNumberOfPages(queryOptions);\n        } catch (final SBonitaReadException e) {\n            throw new SBonitaReadException(e);\n        }\n    }\n\n    @Override\n    public List<SPage> executeSearch(final QueryOptions queryOptions) throws SBonitaReadException {\n        return pageService.searchPages(queryOptions);\n    }\n\n    @Override\n    public List<Page> convertToClientObjects(final List<SPage> pages) {\n        return new PageModelConverter().toPages(pages);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/platform/DeleteSPlatformCommand.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.platform;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContent;\nimport org.bonitasoft.engine.platform.command.PlatformCommandService;\n\n/**\n * @author Zhang Bole\n */\npublic class DeleteSPlatformCommand implements TransactionContent {\n\n    private final String name;\n\n    private final PlatformCommandService platformCommandService;\n\n    public DeleteSPlatformCommand(final PlatformCommandService platformCommandService, final String name) {\n        this.platformCommandService = platformCommandService;\n        this.name = name;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        platformCommandService.delete(name);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/platform/GetPlatformContent.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.platform;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.platform.PlatformService;\nimport org.bonitasoft.engine.platform.model.SPlatform;\n\n/**\n * @author Lu Kai\n */\npublic class GetPlatformContent implements TransactionContentWithResult<SPlatform> {\n\n    private final PlatformService platformService;\n\n    private SPlatform sPlatform;\n\n    public GetPlatformContent(final PlatformService platformService) {\n        this.platformService = platformService;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        sPlatform = platformService.getPlatform();\n    }\n\n    @Override\n    public SPlatform getResult() {\n        return sPlatform;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/platform/GetSPlatformCommands.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.platform;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.command.CommandCriterion;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.platform.command.PlatformCommandService;\nimport org.bonitasoft.engine.platform.command.model.SPlatformCommand;\n\n/**\n * @author Zhang Bole\n */\npublic class GetSPlatformCommands implements TransactionContentWithResult<List<SPlatformCommand>> {\n\n    private final PlatformCommandService platformCommandService;\n\n    private final int startIndex;\n\n    private final int maxResults;\n\n    private final CommandCriterion sort;\n\n    private List<SPlatformCommand> platformCommands;\n\n    public GetSPlatformCommands(final PlatformCommandService platformCommandService, final int startIndex,\n            final int maxResults, final CommandCriterion sort) {\n        super();\n        this.platformCommandService = platformCommandService;\n        this.startIndex = startIndex;\n        this.maxResults = maxResults;\n        this.sort = sort;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        final QueryOptions queryOptions;\n        switch (sort) {\n            case NAME_ASC:\n                queryOptions = new QueryOptions(startIndex, maxResults, SPlatformCommand.class, \"name\",\n                        OrderByType.ASC);\n                break;\n            case NAME_DESC:\n            default:\n                queryOptions = new QueryOptions(startIndex, maxResults, SPlatformCommand.class, \"name\",\n                        OrderByType.DESC);\n                break;\n        }\n        platformCommands = platformCommandService.getPlatformCommands(queryOptions);\n    }\n\n    @Override\n    public List<SPlatformCommand> getResult() {\n        return platformCommands;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/platform/UpdateSPlatformCommand.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.platform;\n\nimport java.io.Serializable;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.command.CommandUpdater;\nimport org.bonitasoft.engine.command.CommandUpdater.CommandField;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContent;\nimport org.bonitasoft.engine.platform.command.PlatformCommandService;\nimport org.bonitasoft.engine.platform.command.model.SPlatformCommand;\nimport org.bonitasoft.engine.platform.command.model.SPlatformCommandUpdateBuilder;\nimport org.bonitasoft.engine.platform.command.model.SPlatformCommandUpdateBuilderFactory;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Zhang Bole\n */\npublic class UpdateSPlatformCommand implements TransactionContent {\n\n    private final PlatformCommandService platformCommandService;\n\n    private final String name;\n\n    private final CommandUpdater updateDescriptor;\n\n    public UpdateSPlatformCommand(final PlatformCommandService platformCommandService,\n            final String name, final CommandUpdater updateDescriptor) {\n        this.platformCommandService = platformCommandService;\n        this.name = name;\n        this.updateDescriptor = updateDescriptor;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        final EntityUpdateDescriptor changeDescriptor = getCommandUpdateDescriptor();\n        final SPlatformCommand sPlatformCommand = platformCommandService.getPlatformCommand(name);\n        platformCommandService.update(sPlatformCommand, changeDescriptor);\n    }\n\n    private EntityUpdateDescriptor getCommandUpdateDescriptor() {\n        final SPlatformCommandUpdateBuilder platformCommandUpdateBuilder = BuilderFactory\n                .get(SPlatformCommandUpdateBuilderFactory.class).createNewInstance();\n        final Map<CommandField, Serializable> fields = updateDescriptor.getFields();\n        for (final Entry<CommandField, Serializable> field : fields.entrySet()) {\n            switch (field.getKey()) {\n                case NAME:\n                    platformCommandUpdateBuilder.updateName((String) field.getValue());\n                    break;\n                case DESCRIPTION:\n                    platformCommandUpdateBuilder.updateDescription((String) field.getValue());\n                    break;\n                default:\n                    throw new IllegalStateException();\n            }\n        }\n        return platformCommandUpdateBuilder.done();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/AbstractGetProcessDeploymentInfo.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.process;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.impl.transaction.AbstractGetEntity;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Celine Souchet\n */\npublic abstract class AbstractGetProcessDeploymentInfo\n        extends AbstractGetEntity<ProcessDeploymentInfo, SProcessDefinitionDeployInfo> {\n\n    public AbstractGetProcessDeploymentInfo(final SearchEntityDescriptor searchDescriptor, final int fromIndex,\n            final int numberOfResults,\n            final ProcessDeploymentInfoCriterion criterion) {\n        super(searchDescriptor, fromIndex, numberOfResults, criterion.getSort());\n    }\n\n    @Override\n    public List<ProcessDeploymentInfo> convertToClientObjects(final List<SProcessDefinitionDeployInfo> serverObjects) {\n        return ModelConvertor.toProcessDeploymentInfo(serverObjects);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/AddProcessDefinitionToCategory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.process;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContent;\nimport org.bonitasoft.engine.core.category.CategoryService;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\n\n/**\n * @author Yanyan Liu\n */\npublic class AddProcessDefinitionToCategory implements TransactionContent {\n\n    private final long categoryId;\n\n    private final long processDefinitionId;\n\n    private final CategoryService categoryService;\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    public AddProcessDefinitionToCategory(final long categoryId, final long processDefinitionId,\n            final CategoryService categoryService,\n            final ProcessDefinitionService processDefinitionService) {\n        this.categoryId = categoryId;\n        this.processDefinitionId = processDefinitionId;\n        this.categoryService = categoryService;\n        this.processDefinitionService = processDefinitionService;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        processDefinitionService.getProcessDefinition(processDefinitionId);\n        categoryService.addProcessDefinitionToCategory(categoryId, processDefinitionId);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/DisableProcess.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.process;\n\nimport java.util.List;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContent;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SStartEventDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventModificationException;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent;\nimport org.bonitasoft.engine.execution.job.JobNameBuilder;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.scheduler.SchedulerService;\nimport org.bonitasoft.engine.scheduler.exception.SSchedulerException;\n\n/**\n * @author Baptiste Mesta\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\n@Slf4j\npublic final class DisableProcess implements TransactionContent {\n\n    private final ProcessDefinitionService processDefinitionService;\n    private final EventInstanceService eventInstanceService;\n    private final long processDefinitionId;\n    private final SchedulerService scheduler;\n    private final String username;\n\n    public DisableProcess(final ProcessDefinitionService processDefinitionService, final long processId,\n            final EventInstanceService eventInstanceService,\n            final SchedulerService scheduler, final String username) {\n        this.processDefinitionService = processDefinitionService;\n        this.eventInstanceService = eventInstanceService;\n        this.processDefinitionId = processId;\n        this.scheduler = scheduler;\n        this.username = username;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        processDefinitionService.disableProcessDeploymentInfo(processDefinitionId);\n        final SProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(processDefinitionId);\n        disableStartEvents(processDefinition);\n        log.info(\"The user <\" + username + \"> has disabled process <\" + processDefinition.getName()\n                + \"> in version <\" + processDefinition.getVersion() + \"> with id <\"\n                + processDefinition.getId() + \">\");\n\n    }\n\n    private void disableStartEvents(final SProcessDefinition processDefinition) throws SBonitaException {\n        deleteWaitingEvents();\n        deleteJobs(processDefinition);\n    }\n\n    private void deleteJobs(final SProcessDefinition processDefinition) throws SSchedulerException {\n        final List<SStartEventDefinition> startEvents = processDefinition.getProcessContainer().getStartEvents();\n        for (final SStartEventDefinition startEvent : startEvents) {\n            if (!startEvent.getTimerEventTriggerDefinitions().isEmpty()) {\n                scheduler.delete(JobNameBuilder.getTimerEventJobName(processDefinitionId, startEvent, null));\n            }\n        }\n    }\n\n    private void deleteWaitingEvents() throws SWaitingEventModificationException, SBonitaReadException {\n        List<SWaitingEvent> waitingEvents = eventInstanceService\n                .getStartWaitingEventsOfProcessDefinition(processDefinitionId);\n        for (final SWaitingEvent startEvent : waitingEvents) {\n            eventInstanceService.deleteWaitingEvent(startEvent);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/EnableProcess.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.process;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContent;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SStartEventDefinition;\nimport org.bonitasoft.engine.execution.event.EventsHandler;\n\n/**\n * @author Baptiste Mesta\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\n@Slf4j\npublic final class EnableProcess implements TransactionContent {\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    private final long processId;\n\n    private final EventsHandler eventsHandler;\n    private final String userName;\n\n    public EnableProcess(final ProcessDefinitionService processDefinitionService,\n            final long processId,\n            final EventsHandler eventsHandler, final String userName) {\n        this.processDefinitionService = processDefinitionService;\n        this.processId = processId;\n        this.eventsHandler = eventsHandler;\n        this.userName = userName;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        final SProcessDefinition sProcessDefinition = processDefinitionService.getProcessDefinition(processId);\n        handleStartEvents(sProcessDefinition);\n        processDefinitionService.enableProcessDeploymentInfo(processId);\n\n        log.info(\"The user <\" + userName + \"> has enabled process <\" + sProcessDefinition.getName()\n                + \"> in version <\" + sProcessDefinition.getVersion() + \"> with id <\"\n                + sProcessDefinition.getId() + \">\");\n    }\n\n    private void handleStartEvents(final SProcessDefinition sProcessDefinition) throws SBonitaException {\n        final SFlowElementContainerDefinition processContainer = sProcessDefinition.getProcessContainer();\n        for (final SStartEventDefinition sStartEventDefinition : processContainer.getStartEvents()) {\n            eventsHandler.handleCatchEvent(sProcessDefinition, sStartEventDefinition, null);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/GetArchivedProcessInstanceList.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.process;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor;\nimport org.bonitasoft.engine.search.process.SearchArchivedProcessInstances;\n\n/**\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n * @author Matthieu Chaffotte\n */\npublic class GetArchivedProcessInstanceList implements TransactionContentWithResult<List<ArchivedProcessInstance>> {\n\n    private final ProcessInstanceService processInstanceService;\n\n    private final SearchEntitiesDescriptor searchEntitiesDescriptor;\n\n    private final SearchOptionsBuilder searchOptionsBuilder;\n\n    private List<ArchivedProcessInstance> processInstanceList;\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    public GetArchivedProcessInstanceList(final ProcessInstanceService processInstanceService,\n            final ProcessDefinitionService processDefinitionService,\n            final SearchEntitiesDescriptor searchEntitiesDescriptor, final long processInstanceId, final int startIndex,\n            final int maxResults) {\n        this.processInstanceService = processInstanceService;\n        this.processDefinitionService = processDefinitionService;\n        this.searchEntitiesDescriptor = searchEntitiesDescriptor;\n        searchOptionsBuilder = new SearchOptionsBuilder(startIndex, maxResults);\n        searchOptionsBuilder.filter(ArchivedProcessInstancesSearchDescriptor.SOURCE_OBJECT_ID, processInstanceId);\n    }\n\n    public GetArchivedProcessInstanceList(final ProcessInstanceService processInstanceService,\n            final ProcessDefinitionService processDefinitionService,\n            final SearchEntitiesDescriptor searchEntitiesDescriptor, final long processInstanceId, final int startIndex,\n            final int maxResults,\n            final String field, final OrderByType order) {\n        this(processInstanceService, processDefinitionService, searchEntitiesDescriptor, processInstanceId, startIndex,\n                maxResults);\n        searchOptionsBuilder.sort(field, Order.valueOf(order.name()));\n    }\n\n    public GetArchivedProcessInstanceList(final ProcessInstanceService processInstanceService,\n            final ProcessDefinitionService processDefinitionService,\n            final SearchEntitiesDescriptor searchEntitiesDescriptor, final SearchOptions searchOptions) {\n        this.processInstanceService = processInstanceService;\n        this.processDefinitionService = processDefinitionService;\n        this.searchEntitiesDescriptor = searchEntitiesDescriptor;\n        searchOptionsBuilder = new SearchOptionsBuilder(searchOptions);\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        final SearchArchivedProcessInstances searchArchivedProcessInstances = new SearchArchivedProcessInstances(\n                processInstanceService,\n                processDefinitionService, searchEntitiesDescriptor.getSearchArchivedProcessInstanceDescriptor(),\n                searchOptionsBuilder.done());\n        searchArchivedProcessInstances.execute();\n        processInstanceList = searchArchivedProcessInstances.getResult().getResult();\n    }\n\n    @Override\n    public List<ArchivedProcessInstance> getResult() {\n        return processInstanceList;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/GetChildInstanceIdsOfProcessInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.process;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.persistence.OrderByType;\n\n/**\n * @author Yanyan Liu\n */\npublic class GetChildInstanceIdsOfProcessInstance implements TransactionContentWithResult<List<Long>> {\n\n    private final ProcessInstanceService processInstanceService;\n\n    private final long processInstanceId;\n\n    private final int pageIndex;\n\n    private final int numberPerPage;\n\n    private final String sortingField;\n\n    private final OrderByType sortingOrder;\n\n    private List<Long> childInstanceIds;\n\n    public GetChildInstanceIdsOfProcessInstance(final ProcessInstanceService processInstanceService,\n            final long processInstanceId, final int pageIndex,\n            final int numberPerPage, final String field, final OrderByType order) {\n        this.processInstanceService = processInstanceService;\n        this.processInstanceId = processInstanceId;\n        this.pageIndex = pageIndex;\n        this.numberPerPage = numberPerPage;\n        this.sortingField = field;\n        this.sortingOrder = order;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        this.childInstanceIds = this.processInstanceService.getChildInstanceIdsOfProcessInstance(this.processInstanceId,\n                this.pageIndex * this.numberPerPage,\n                this.numberPerPage, this.sortingField, this.sortingOrder);\n    }\n\n    @Override\n    public List<Long> getResult() {\n        return this.childInstanceIds;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/GetLastArchivedProcessInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.process;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor;\nimport org.bonitasoft.engine.search.process.SearchArchivedProcessInstances;\n\n/**\n * Returns the most recent archived process instance from the archives.\n *\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n * @author Matthieu Chaffotte\n */\npublic class GetLastArchivedProcessInstance implements TransactionContentWithResult<ArchivedProcessInstance> {\n\n    private ArchivedProcessInstance processInstance;\n\n    private final ProcessInstanceService processInstanceService;\n\n    private final long processInstanceId;\n\n    private final SearchEntitiesDescriptor searchEntitiesDescriptor;\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    public GetLastArchivedProcessInstance(final ProcessInstanceService processInstanceService,\n            final ProcessDefinitionService processDefinitionService,\n            final long processInstanceId, final SearchEntitiesDescriptor searchEntitiesDescriptor) {\n        this.processInstanceService = processInstanceService;\n        this.processDefinitionService = processDefinitionService;\n        this.processInstanceId = processInstanceId;\n        this.searchEntitiesDescriptor = searchEntitiesDescriptor;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 2);\n        searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.ARCHIVE_DATE, Order.DESC);\n        searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.END_DATE, Order.DESC);\n        searchOptionsBuilder.filter(ArchivedProcessInstancesSearchDescriptor.SOURCE_OBJECT_ID, processInstanceId);\n\n        final SearchArchivedProcessInstances searchArchivedProcessInstances = new SearchArchivedProcessInstances(\n                processInstanceService,\n                processDefinitionService, searchEntitiesDescriptor.getSearchArchivedProcessInstanceDescriptor(),\n                searchOptionsBuilder.done());\n        searchArchivedProcessInstances.execute();\n        final List<ArchivedProcessInstance> processInstances = searchArchivedProcessInstances.getResult().getResult();\n        if (processInstances.isEmpty()) {\n            throw new SProcessInstanceNotFoundException(processInstanceId);\n        }\n        processInstance = processInstances.get(0);\n    }\n\n    @Override\n    public ArchivedProcessInstance getResult() {\n        return processInstance;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/GetLatestProcessDefinitionId.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.process;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\n\n/**\n * @author Yanyan Liu\n */\npublic class GetLatestProcessDefinitionId implements TransactionContentWithResult<Long> {\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    private final String processName;\n\n    private long processDefId;\n\n    public GetLatestProcessDefinitionId(final ProcessDefinitionService processDefinitionService,\n            final String processName) {\n        this.processDefinitionService = processDefinitionService;\n        this.processName = processName;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        this.processDefId = this.processDefinitionService.getLatestProcessDefinitionId(this.processName);\n    }\n\n    @Override\n    public Long getResult() {\n        return this.processDefId;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/GetNumberOfProcessDeploymentInfos.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.process;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\n\n/**\n * @author Baptiste Mesta\n */\npublic final class GetNumberOfProcessDeploymentInfos implements TransactionContentWithResult<Long> {\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    private long numberOfProcesses;\n\n    public GetNumberOfProcessDeploymentInfos(final ProcessDefinitionService processDefinitionService) {\n        this.processDefinitionService = processDefinitionService;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        numberOfProcesses = processDefinitionService.getNumberOfProcessDeploymentInfos();\n    }\n\n    @Override\n    public Long getResult() {\n        return numberOfProcesses;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/GetNumberOfProcessDeploymentInfosUnrelatedToCategory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.process;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\n\n/**\n * @author Celine Souchet\n * @author Matthieu Chaffotte\n */\npublic class GetNumberOfProcessDeploymentInfosUnrelatedToCategory implements TransactionContentWithResult<Long> {\n\n    private final long categoryId;\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    private Long count;\n\n    public GetNumberOfProcessDeploymentInfosUnrelatedToCategory(final long categoryId,\n            final ProcessDefinitionService processDefinitionService) {\n        this.categoryId = categoryId;\n        this.processDefinitionService = processDefinitionService;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        count = processDefinitionService.getNumberOfProcessDeploymentInfosUnrelatedToCategory(categoryId);\n    }\n\n    @Override\n    public Long getResult() {\n        return count;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/GetNumberOfProcessInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.process;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor;\nimport org.bonitasoft.engine.search.process.SearchProcessInstances;\n\n/**\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n */\npublic class GetNumberOfProcessInstance implements TransactionContentWithResult<Long> {\n\n    private final ProcessInstanceService processInstanceService;\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    private final SearchEntitiesDescriptor searchEntitiesDescriptor;\n\n    private long number;\n\n    public GetNumberOfProcessInstance(final ProcessInstanceService processInstanceService,\n            final ProcessDefinitionService processDefinitionService,\n            final SearchEntitiesDescriptor searchEntitiesDescriptor) {\n        this.processInstanceService = processInstanceService;\n        this.processDefinitionService = processDefinitionService;\n        this.searchEntitiesDescriptor = searchEntitiesDescriptor;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        final SearchProcessInstances searchProcessInstances = new SearchProcessInstances(processInstanceService,\n                searchEntitiesDescriptor.getSearchProcessInstanceDescriptor(), null, processDefinitionService);\n        final QueryOptions queryOptions = new QueryOptions(0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS);\n        number = searchProcessInstances.executeCount(queryOptions);\n    }\n\n    @Override\n    public Long getResult() {\n        return number;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/GetProcessDefinitionDeployInfos.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.process;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic final class GetProcessDefinitionDeployInfos extends AbstractGetProcessDeploymentInfo {\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    public GetProcessDefinitionDeployInfos(final ProcessDefinitionService processDefinitionService,\n            final SearchEntityDescriptor searchDescriptor,\n            final int fromIndex, final int numberOfResults, final ProcessDeploymentInfoCriterion criterion) {\n        super(searchDescriptor, fromIndex, numberOfResults, criterion);\n        this.processDefinitionService = processDefinitionService;\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> executeGet(final QueryOptions queryOptions) throws SBonitaException {\n        return processDefinitionService.getProcessDeploymentInfos(queryOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/GetProcessDefinitionDeployInfosWithActorOnlyForGroup.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.process;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\n\n/**\n * @author Arthur Freycon\n * @author Celine Souchet\n */\npublic class GetProcessDefinitionDeployInfosWithActorOnlyForGroup extends AbstractGetProcessDeploymentInfo {\n\n    private final long groupId;\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    public GetProcessDefinitionDeployInfosWithActorOnlyForGroup(final ProcessDefinitionService processDefinitionService,\n            final SearchEntityDescriptor searchDescriptor,\n            final int fromIndex, final int numberOfResults, final ProcessDeploymentInfoCriterion criterion,\n            final long groupId) {\n        super(searchDescriptor, fromIndex, numberOfResults, criterion);\n        this.groupId = groupId;\n        this.processDefinitionService = processDefinitionService;\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> executeGet(final QueryOptions queryOptions) throws SBonitaException {\n        return processDefinitionService.getProcessDeploymentInfosWithActorOnlyForGroup(groupId, queryOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/GetProcessDefinitionDeployInfosWithActorOnlyForGroups.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.process;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\n\n/**\n * @author Arthur Freycon\n * @author Celine Souchet\n */\npublic class GetProcessDefinitionDeployInfosWithActorOnlyForGroups extends AbstractGetProcessDeploymentInfo {\n\n    private final List<Long> groupIds;\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    public GetProcessDefinitionDeployInfosWithActorOnlyForGroups(\n            final ProcessDefinitionService processDefinitionService, final SearchEntityDescriptor searchDescriptor,\n            final int fromIndex, final int numberOfResults, final ProcessDeploymentInfoCriterion criterion,\n            final List<Long> groupIds) {\n        super(searchDescriptor, fromIndex, numberOfResults, criterion);\n        this.groupIds = groupIds;\n        this.processDefinitionService = processDefinitionService;\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> executeGet(final QueryOptions queryOptions) throws SBonitaException {\n        return processDefinitionService.getProcessDeploymentInfosWithActorOnlyForGroups(groupIds, queryOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/GetProcessDefinitionDeployInfosWithActorOnlyForRole.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.process;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\n\n/**\n * @author Arthur Freycon\n * @author Celine Souchet\n */\npublic class GetProcessDefinitionDeployInfosWithActorOnlyForRole extends AbstractGetProcessDeploymentInfo {\n\n    private final long roleId;\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    public GetProcessDefinitionDeployInfosWithActorOnlyForRole(final ProcessDefinitionService processDefinitionService,\n            final SearchEntityDescriptor searchDescriptor,\n            final int fromIndex, final int numberOfResults, final ProcessDeploymentInfoCriterion criterion,\n            final long roleId) {\n        super(searchDescriptor, fromIndex, numberOfResults, criterion);\n        this.roleId = roleId;\n        this.processDefinitionService = processDefinitionService;\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> executeGet(final QueryOptions queryOptions) throws SBonitaException {\n        return processDefinitionService.getProcessDeploymentInfosWithActorOnlyForRole(roleId, queryOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/GetProcessDefinitionDeployInfosWithActorOnlyForRoles.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.process;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\n\n/**\n * @author Arthur Freycon\n * @author Celine Souchet\n */\npublic class GetProcessDefinitionDeployInfosWithActorOnlyForRoles extends AbstractGetProcessDeploymentInfo {\n\n    private final List<Long> roleIds;\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    public GetProcessDefinitionDeployInfosWithActorOnlyForRoles(final ProcessDefinitionService processDefinitionService,\n            final SearchEntityDescriptor searchDescriptor,\n            final int fromIndex, final int numberOfResults, final ProcessDeploymentInfoCriterion criterion,\n            final List<Long> roleIds) {\n        super(searchDescriptor, fromIndex, numberOfResults, criterion);\n        this.roleIds = roleIds;\n        this.processDefinitionService = processDefinitionService;\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> executeGet(final QueryOptions queryOptions) throws SBonitaException {\n        return processDefinitionService.getProcessDeploymentInfosWithActorOnlyForRoles(roleIds, queryOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/GetProcessDefinitionDeployInfosWithActorOnlyForUser.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.process;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\n\n/**\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n */\npublic final class GetProcessDefinitionDeployInfosWithActorOnlyForUser extends AbstractGetProcessDeploymentInfo {\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    private final long userId;\n\n    public GetProcessDefinitionDeployInfosWithActorOnlyForUser(final ProcessDefinitionService processDefinitionService,\n            final SearchEntityDescriptor searchDescriptor,\n            final int fromIndex, final int numberOfResults, final ProcessDeploymentInfoCriterion criterion,\n            final long userId) {\n        super(searchDescriptor, fromIndex, numberOfResults, criterion);\n        this.userId = userId;\n        this.processDefinitionService = processDefinitionService;\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> executeGet(final QueryOptions queryOptions) throws SBonitaException {\n        return processDefinitionService.getProcessDeploymentInfosWithActorOnlyForUser(userId, queryOptions);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/GetProcessDefinitionDeployInfosWithActorOnlyForUsers.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.process;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\n\n/**\n * @author Arthur Freycon\n * @author Celine Souchet\n */\npublic class GetProcessDefinitionDeployInfosWithActorOnlyForUsers extends AbstractGetProcessDeploymentInfo {\n\n    private final List<Long> userIds;\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    public GetProcessDefinitionDeployInfosWithActorOnlyForUsers(final ProcessDefinitionService processDefinitionService,\n            final SearchEntityDescriptor searchDescriptor,\n            final int fromIndex, final int numberOfResults, final ProcessDeploymentInfoCriterion criterion,\n            final List<Long> userIds) {\n        super(searchDescriptor, fromIndex, numberOfResults, criterion);\n        this.userIds = userIds;\n        this.processDefinitionService = processDefinitionService;\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> executeGet(final QueryOptions queryOptions) throws SBonitaException {\n        return processDefinitionService.getProcessDeploymentInfosWithActorOnlyForUsers(userIds, queryOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/SetProcessInstanceState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.process;\n\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\n\n/**\n * @author Baptiste Mesta\n */\npublic final class SetProcessInstanceState implements TransactionContentWithResult<SProcessInstance> {\n\n    private final long processInstanceId;\n\n    private final ProcessInstanceState state;\n\n    private final ProcessInstanceService processInstanceService;\n\n    private SProcessInstance result;\n\n    public SetProcessInstanceState(final ProcessInstanceService processInstanceService, final long processInstanceId,\n            final ProcessInstanceState state) {\n        this.processInstanceService = processInstanceService;\n        this.processInstanceId = processInstanceId;\n        this.state = state;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        result = processInstanceService.getProcessInstance(processInstanceId);\n        processInstanceService.setState(result, state);\n    }\n\n    @Override\n    public SProcessInstance getResult() {\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/UpdateProcessDeploymentInfo.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.process;\n\nimport java.io.Serializable;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoUpdater;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoUpdater.ProcessDeploymentInfoField;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContent;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionDeployInfoUpdateBuilder;\nimport org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionDeployInfoUpdateBuilderFactory;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Yanyan Liu\n * @author Zhang Bole\n * @author Celine Souchet\n */\npublic class UpdateProcessDeploymentInfo implements TransactionContent {\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    private final long processId;\n\n    private final ProcessDeploymentInfoUpdater processDeploymentInfoUpdateDescriptor;\n\n    public UpdateProcessDeploymentInfo(final ProcessDefinitionService processDefinitionService,\n            final long processId,\n            final ProcessDeploymentInfoUpdater processDeploymentInfoUpdateDescriptor) {\n        this.processDefinitionService = processDefinitionService;\n        this.processId = processId;\n        this.processDeploymentInfoUpdateDescriptor = processDeploymentInfoUpdateDescriptor;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        processDefinitionService.updateProcessDefinitionDeployInfo(processId, buildDescriptor());\n    }\n\n    private EntityUpdateDescriptor buildDescriptor() {\n        final SProcessDefinitionDeployInfoUpdateBuilder processDeploymentInfoUpdateBuilder = BuilderFactory\n                .get(SProcessDefinitionDeployInfoUpdateBuilderFactory.class).createNewInstance();\n        final Map<ProcessDeploymentInfoField, Serializable> updatedFieldsMap = processDeploymentInfoUpdateDescriptor\n                .getFields();\n        for (final Entry<ProcessDeploymentInfoField, Serializable> field : updatedFieldsMap.entrySet()) {\n            switch (field.getKey()) {\n                case DISPLAY_NAME:\n                    processDeploymentInfoUpdateBuilder.updateDisplayName((String) field.getValue());\n                    break;\n                case DISPLAY_DESCRIPTION:\n                    processDeploymentInfoUpdateBuilder.updateDisplayDescription((String) field.getValue());\n                    break;\n                case ICONPATH:\n                    processDeploymentInfoUpdateBuilder.updateIconPath((String) field.getValue());\n                    break;\n                default:\n                    break;\n            }\n        }\n        return processDeploymentInfoUpdateBuilder.done();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/profile/CreateProfileMember.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.profile;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.MemberType;\nimport org.bonitasoft.engine.identity.SGroupNotFoundException;\nimport org.bonitasoft.engine.identity.SRoleNotFoundException;\nimport org.bonitasoft.engine.identity.SUserNotFoundException;\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.identity.model.SRole;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.profile.ProfileService;\nimport org.bonitasoft.engine.profile.exception.profilemember.SProfileMemberCreationException;\nimport org.bonitasoft.engine.profile.model.SProfileMember;\n\n/**\n * @author Julien Mege\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class CreateProfileMember implements TransactionContentWithResult<SProfileMember> {\n\n    private final ProfileService profileService;\n\n    private final IdentityService identityService;\n\n    private final long profileId;\n\n    private final Long userId;\n\n    private final Long groupId;\n\n    private final Long roleId;\n\n    private final MemberType memberType;\n\n    private SProfileMember sProfileMember;\n\n    public CreateProfileMember(final ProfileService profileService, final IdentityService identityService,\n            final long profileId, final Long userId,\n            final Long groupId, final Long roleId, final MemberType memberType) {\n        super();\n        this.profileService = profileService;\n        this.identityService = identityService;\n        this.profileId = profileId;\n        this.userId = userId;\n        this.groupId = groupId;\n        this.roleId = roleId;\n        this.memberType = memberType;\n\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        profileService.updateProfileMetaData(profileId);\n\n        switch (memberType) {\n            case USER:\n                if (isNotNullOrEmpty(userId)) {\n                    addUserToProfile();\n                }\n                break;\n            case GROUP:\n                if (isNotNullOrEmpty(groupId)) {\n                    addGroupToProfile();\n                }\n                break;\n            case ROLE:\n                if (isNotNullOrEmpty(roleId)) {\n                    addRoleToProfile();\n                }\n                break;\n            default:\n                if (isNotNullOrEmpty(groupId) && isNotNullOrEmpty(roleId)) {\n                    addRoleAndGroupToProfile();\n                }\n                break;\n        }\n    }\n\n    private void addRoleAndGroupToProfile()\n            throws SGroupNotFoundException, SRoleNotFoundException, SProfileMemberCreationException {\n        final SGroup group = identityService.getGroup(groupId);\n        final SRole role = identityService.getRole(roleId);\n        if (group != null && role != null) {\n            sProfileMember = profileService\n                    .addRoleAndGroupToProfile(profileId, roleId, groupId, role.getName(), group.getName(),\n                            group.getParentPath());\n        }\n    }\n\n    private void addRoleToProfile() throws SRoleNotFoundException, SProfileMemberCreationException {\n        final SRole role = identityService.getRole(roleId);\n\n        if (role != null) {\n            sProfileMember = profileService.addRoleToProfile(profileId, roleId, role.getName());\n        }\n    }\n\n    private void addGroupToProfile() throws SGroupNotFoundException, SProfileMemberCreationException {\n        final SGroup group = identityService.getGroup(groupId);\n\n        if (group != null) {\n            sProfileMember = profileService.addGroupToProfile(profileId, groupId, group.getName(),\n                    group.getParentPath());\n        }\n    }\n\n    private void addUserToProfile() throws SUserNotFoundException, SProfileMemberCreationException {\n        final SUser user = identityService.getUser(userId);\n\n        if (user != null) {\n            sProfileMember = profileService.addUserToProfile(profileId, userId, user.getUserName(), user.getLastName(),\n                    user.getUserName());\n        }\n    }\n\n    private boolean isNotNullOrEmpty(final Long id) {\n        return id != null && id > 0;\n    }\n\n    @Override\n    public SProfileMember getResult() {\n        return sProfileMember;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/profile/ProfileMemberUtils.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.profile;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.profile.model.SProfileMember;\n\n/**\n * @author Julien Mege\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class ProfileMemberUtils {\n\n    public static final String PROFILE_ID = \"profileId\";\n\n    public static final String USER_ID = \"userId\";\n\n    public static final String GROUP_ID = \"groupId\";\n\n    public static final String ROLE_ID = \"roleId\";\n\n    public static final String PROFILE_MEMBER_ID = \"profileMemberId\";\n\n    public static final String DISPLAY_NAME_PART1 = \"displayNamePart1\";\n\n    public static final String DISPLAY_NAME_PART2 = \"displayNamePart2\";\n\n    public static final String DISPLAY_NAME_PART3 = \"displayNamePart3\";\n\n    public static final String ROLE_AND_GROUP_TYPE = \"roleAndGroup\";\n\n    public static final String ROLE_TYPE = \"role\";\n\n    public static final String GROUP_TYPE = \"group\";\n\n    public static final String USER_TYPE = \"user\";\n\n    /**\n     * Search command parameters\n     */\n    public static final String PROFILE_MEMBER_SEARCH_INDEX = \"fromIndex\";\n\n    public static final String PROFILE_MEMBER_SEARCH_NUMBER = \"numberOfProfiles\";\n\n    public static final String PROFILE_MEMBER_SEARCH_FIELD = \"field\";\n\n    public static final String PROFILE_MEMBER_SEARCH_ORDER = \"order\";\n\n    public static final String PROFILE_MEMBER_SEARCH_OPTIONS_KEY = \"searchOptions\";\n\n    public static final String PROFILE_MEMBER_TYPE = \"memberType\";\n\n    /**\n     * Query Suffix\n     */\n    public static final String USER_SUFFIX = \"ForUser\";\n\n    public static final String GROUP_SUFFIX = \"ForGroup\";\n\n    public static final String ROLE_SUFFIX = \"ForRole\";\n\n    public static final String ROLE_AND_GROUP_SUFFIX = \"ForRoleAndGroup\";\n\n    private ProfileMemberUtils() {\n        // For Sonar\n    }\n\n    public static Map<String, Serializable> memberAsProfileMembersMap(final SProfileMember profileMember) {\n        final Map<String, Serializable> profileMemeber = new HashMap<String, Serializable>();\n        profileMemeber.put(PROFILE_ID, profileMember.getProfileId());\n        profileMemeber.put(USER_ID, profileMember.getUserId());\n        profileMemeber.put(GROUP_ID, profileMember.getGroupId());\n        profileMemeber.put(ROLE_ID, profileMember.getRoleId());\n        profileMemeber.put(PROFILE_MEMBER_ID, profileMember.getId());\n        profileMemeber.put(DISPLAY_NAME_PART1, profileMember.getDisplayNamePart1());\n        profileMemeber.put(DISPLAY_NAME_PART2, profileMember.getDisplayNamePart2());\n        profileMemeber.put(DISPLAY_NAME_PART3, profileMember.getDisplayNamePart3());\n        return profileMemeber;\n    }\n\n    public static List<Map<String, Serializable>> membersAsProfileMembersMapList(\n            final List<SProfileMember> serverObjects) {\n        final List<Map<String, Serializable>> profileMemberMaps = new ArrayList<Map<String, Serializable>>();\n        for (final SProfileMember profileMember : serverObjects) {\n            profileMemberMaps.add(memberAsProfileMembersMap(profileMember));\n        }\n        return profileMemberMaps;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/task/AssignOrUnassignUserTask.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.task;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContent;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\nimport org.bonitasoft.engine.execution.SUnreleasableTaskException;\nimport org.bonitasoft.engine.execution.StateBehaviors;\n\n/**\n * @author Baptiste Mesta\n */\npublic final class AssignOrUnassignUserTask implements TransactionContent {\n\n    private final long userId;\n\n    private final long userTaskId;\n\n    private final ActivityInstanceService activityInstanceService;\n\n    private final StateBehaviors stateBehaviors;\n\n    public AssignOrUnassignUserTask(final long userId, final long userTaskId,\n            final ActivityInstanceService activityInstanceService,\n            StateBehaviors stateBehaviors) {\n        this.userId = userId;\n        this.userTaskId = userTaskId;\n        this.activityInstanceService = activityInstanceService;\n        this.stateBehaviors = stateBehaviors;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        final SActivityInstance activityInstance = activityInstanceService.getActivityInstance(userTaskId);\n        if (userId == 0 && SFlowNodeType.MANUAL_TASK.equals(activityInstance.getType())) {\n            throw new SUnreleasableTaskException(\"The activity with id \" + activityInstance.getId()\n                    + \" can't be assigned because it is a manual sub task\");\n        }\n        activityInstanceService.assignHumanTask(userTaskId, userId);\n        if (userId > 0) {\n            stateBehaviors.addAssignmentSystemComment(activityInstance, userId);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/task/AssignUserTaskIfNotAssigned.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.task;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContent;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\nimport org.bonitasoft.engine.execution.SUnreleasableTaskException;\nimport org.bonitasoft.engine.execution.StateBehaviors;\n\n/**\n * @author Pablo Alonso de Linaje García\n */\npublic class AssignUserTaskIfNotAssigned implements TransactionContent {\n\n    private final long userId;\n\n    private final long userTaskId;\n\n    private final ActivityInstanceService activityInstanceService;\n\n    private final StateBehaviors stateBehaviors;\n\n    public AssignUserTaskIfNotAssigned(final long userId, final long userTaskId,\n            final ActivityInstanceService activityInstanceService,\n            StateBehaviors stateBehaviors) {\n        this.userId = userId;\n        this.userTaskId = userTaskId;\n        this.activityInstanceService = activityInstanceService;\n        this.stateBehaviors = stateBehaviors;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        final SActivityInstance activityInstance = activityInstanceService.getActivityInstance(userTaskId);\n        if (userId == 0 && SFlowNodeType.MANUAL_TASK.equals(activityInstance.getType())) {\n            throw new SUnreleasableTaskException(\"The activity with id \" + activityInstance.getId()\n                    + \" can't be unassigned because it is a manual sub task\");\n        }\n        activityInstanceService.assignHumanTaskIfNotAssigned(userTaskId, userId);\n        if (userId > 0) {\n            stateBehaviors.addAssignmentSystemComment(activityInstance, userId);\n        }\n\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/task/GetAssignedTasks.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.task;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance;\nimport org.bonitasoft.engine.persistence.OrderByType;\n\n/**\n * @author Matthieu Chaffotte\n * @author Emmanuel Duchastenier\n */\npublic class GetAssignedTasks implements TransactionContentWithResult<List<SHumanTaskInstance>> {\n\n    private final ActivityInstanceService instanceService;\n\n    private final long userId;\n\n    private final int fromIndex;\n\n    private final int maxResults;\n\n    private final String sortFieldName;\n\n    private final OrderByType order;\n\n    private List<SHumanTaskInstance> userTasks;\n\n    public GetAssignedTasks(final ActivityInstanceService instanceService, final long userId, final int fromIndex,\n            final int maxResults,\n            final String sortFieldName, final OrderByType order) {\n        super();\n        this.instanceService = instanceService;\n        this.userId = userId;\n        this.fromIndex = fromIndex;\n        this.maxResults = maxResults;\n        this.sortFieldName = sortFieldName;\n        this.order = order;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        userTasks = instanceService.getAssignedUserTasks(userId, fromIndex, maxResults, sortFieldName, order);\n    }\n\n    @Override\n    public List<SHumanTaskInstance> getResult() {\n        return userTasks;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/task/GetHumanTaskInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.task;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance;\n\n/**\n * @author Zhang Bole\n */\npublic class GetHumanTaskInstance implements TransactionContentWithResult<SHumanTaskInstance> {\n\n    private SHumanTaskInstance humanTaskInstance;\n\n    private final ActivityInstanceService activityInstanceService;\n\n    private final long activityInstanceId;\n\n    public GetHumanTaskInstance(final ActivityInstanceService activityInstanceService, final long activityInstanceId) {\n        this.activityInstanceService = activityInstanceService;\n        this.activityInstanceId = activityInstanceId;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        humanTaskInstance = activityInstanceService.getHumanTaskInstance(activityInstanceId);\n    }\n\n    @Override\n    public SHumanTaskInstance getResult() {\n        return humanTaskInstance;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/task/GetNumberOfOpenTasksForUsers.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.task;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\n\n/**\n * @author Yanyan Liu\n */\npublic class GetNumberOfOpenTasksForUsers implements TransactionContentWithResult<Map<Long, Long>> {\n\n    private final List<Long> userIds;\n    private final ActivityInstanceService activityInstanceService;\n    private Map<Long, Long> result;\n\n    public GetNumberOfOpenTasksForUsers(final List<Long> userIds,\n            final ActivityInstanceService activityInstanceService) {\n        this.userIds = userIds;\n        this.activityInstanceService = activityInstanceService;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        result = activityInstanceService.getNumberOfOpenTasksForUsers(userIds);\n    }\n\n    @Override\n    public Map<Long, Long> getResult() {\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/task/SetTaskPriority.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.task;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContent;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.STaskPriority;\n\n/**\n * @author Zhang Bole\n */\npublic final class SetTaskPriority implements TransactionContent {\n\n    private final long activityInstanceId;\n\n    private final STaskPriority priority;\n\n    private final ActivityInstanceService activityInstanceService;\n\n    public SetTaskPriority(final ActivityInstanceService activityInstanceService, final long activityInstanceId,\n            final STaskPriority sTaskPriority) {\n        this.activityInstanceService = activityInstanceService;\n        this.activityInstanceId = activityInstanceId;\n        priority = sTaskPriority;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        final SActivityInstance activity = activityInstanceService.getActivityInstance(activityInstanceId);\n        activityInstanceService.setTaskPriority(activity, priority);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/utils/VisibleForTesting.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.utils;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic @interface VisibleForTesting {\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/authorization/PermissionServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.authorization;\n\nimport static java.lang.String.format;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport groovy.lang.GroovyClassLoader;\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.api.impl.APIAccessorImpl;\nimport org.bonitasoft.engine.api.permission.APICallContext;\nimport org.bonitasoft.engine.api.permission.PermissionRule;\nimport org.bonitasoft.engine.authorization.properties.CompoundPermissionsMapping;\nimport org.bonitasoft.engine.authorization.properties.CustomPermissionsMapping;\nimport org.bonitasoft.engine.authorization.properties.DynamicPermissionsChecks;\nimport org.bonitasoft.engine.authorization.properties.PropertiesWithSet;\nimport org.bonitasoft.engine.authorization.properties.ResourcesPermissionsMapping;\nimport org.bonitasoft.engine.classloader.ClassLoaderIdentifier;\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SExecutionException;\nimport org.bonitasoft.engine.page.ContentType;\nimport org.bonitasoft.engine.page.PageService;\nimport org.bonitasoft.engine.properties.BooleanProperty;\nimport org.bonitasoft.engine.service.ModelConvertor;\nimport org.bonitasoft.engine.service.impl.ServerLoggerWrapper;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.SSessionNotFoundException;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.session.model.SSession;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.bonitasoft.engine.sessionaccessor.SessionIdNotSetException;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;\nimport org.springframework.stereotype.Component;\n\n/**\n * Permission service implementation\n *\n * @author Baptiste Mesta\n */\n@Component\n@Slf4j\n@ConditionalOnSingleCandidate(PermissionService.class)\npublic class PermissionServiceImpl implements PermissionService {\n\n    public static final String PROPERTY_TO_ENABLE_DYNAMIC_PERMISSIONS = \"bonita.runtime.authorization.dynamic-check.enabled\";\n    public static final String RESOURCES_PROPERTY = \"resources\";\n    public static final String PROPERTY_CONTENT_TYPE = \"contentType\";\n    public static final String PROPERTY_API_EXTENSIONS = \"apiExtensions\";\n    public static final String PROPERTY_METHOD_MASK = \"%s.method\";\n    public static final String PROPERTY_PATH_TEMPLATE_MASK = \"%s.pathTemplate\";\n    public static final String PROPERTY_PERMISSIONS_MASK = \"%s.permissions\";\n    public static final String RESOURCE_PERMISSION_KEY_MASK = \"%s|extension/%s\";\n    public static final String RESOURCE_PERMISSION_VALUE = \"[%s]\";\n    public static final String EXTENSION_SEPARATOR = \",\";\n\n    private final ClassLoaderService classLoaderService;\n    private final SessionAccessor sessionAccessor;\n    private final SessionService sessionService;\n    protected GroovyClassLoader groovyClassLoader;\n    private final CompoundPermissionsMapping compoundPermissionsMapping;\n    private final ResourcesPermissionsMapping resourcesPermissionsMapping;\n    private final CustomPermissionsMapping customPermissionsMapping;\n    protected final DynamicPermissionsChecks dynamicPermissionsChecks;\n    protected final BooleanProperty dynamicPermissionCheck;\n\n    public PermissionServiceImpl(final ClassLoaderService classLoaderService, final SessionAccessor sessionAccessor,\n            final SessionService sessionService, CompoundPermissionsMapping compoundPermissionsMapping,\n            ResourcesPermissionsMapping resourcesPermissionsMapping,\n            CustomPermissionsMapping customPermissionsMapping,\n            DynamicPermissionsChecks dynamicPermissionsChecks,\n            @Value(\"${bonita.runtime.authorization.dynamic-check.enabled:true}\") boolean dynamicPermissionCheck) {\n        this.classLoaderService = classLoaderService;\n        this.sessionAccessor = sessionAccessor;\n        this.sessionService = sessionService;\n        this.compoundPermissionsMapping = compoundPermissionsMapping;\n        this.resourcesPermissionsMapping = resourcesPermissionsMapping;\n        this.customPermissionsMapping = customPermissionsMapping;\n        this.dynamicPermissionsChecks = dynamicPermissionsChecks;\n        this.dynamicPermissionCheck = initDynamicPermissionsEnabledProperty(dynamicPermissionCheck);\n    }\n\n    BooleanProperty initDynamicPermissionsEnabledProperty(boolean dynamicPermissionsEnabled) {\n        return new BooleanProperty(\"Dynamic permissions\", PROPERTY_TO_ENABLE_DYNAMIC_PERMISSIONS,\n                dynamicPermissionsEnabled);\n    }\n\n    protected boolean checkAPICallWithScript(final String className, final APICallContext context)\n            throws SExecutionException, ClassNotFoundException {\n        checkStarted();\n        //groovy class loader load class from files and cache then when loaded, no need to do some lazy loading or load all class on start\n        Class<?> aClass = getRuleClass(className);\n        if (!PermissionRule.class.isAssignableFrom(aClass)) {\n            throw new SExecutionException(\"The class \" + aClass.getName()\n                    + \" does not implements org.bonitasoft.engine.api.permission.PermissionRule\");\n        }\n        SSession session = getSession();\n        try {\n            final APISession apiSession = ModelConvertor.toAPISession(session);\n            final PermissionRule permissionRule = (PermissionRule) aClass.getDeclaredConstructor().newInstance();\n            return permissionRule.isAllowed(apiSession, context, createAPIAccessorImpl(),\n                    new ServerLoggerWrapper(permissionRule.getClass(), log));\n        } catch (final Throwable e) {\n            throw new SExecutionException(\"The permission rule \" + aClass.getName() + \" threw an exception\", e);\n        }\n    }\n\n    protected Class<?> getRuleClass(String className) throws SExecutionException, ClassNotFoundException {\n        return Class.forName(className, true, groovyClassLoader);\n    }\n\n    public SSession getSession() throws SExecutionException {\n        try {\n            return sessionService.getSession(sessionAccessor.getSessionId());\n        } catch (SSessionNotFoundException | SessionIdNotSetException e) {\n            throw new SExecutionException(\"The session is not set.\", e);\n        }\n    }\n\n    public void reload() throws SExecutionException {\n        stop();\n        try {\n            start();\n        } catch (SBonitaException e) {\n            throw new SExecutionException(\"The permission rule service could not be reloaded\", e);\n        }\n    }\n\n    protected APIAccessorImpl createAPIAccessorImpl() {\n        return new APIAccessorImpl();\n    }\n\n    private void checkStarted() throws SExecutionException {\n        if (groovyClassLoader == null) {\n            throw new SExecutionException(\"The permission rule service is not started\");\n        }\n    }\n\n    @Override\n    public void start() throws SBonitaException {\n        groovyClassLoader = new GroovyClassLoader(\n                classLoaderService.getClassLoader(ClassLoaderIdentifier.TENANT));\n        groovyClassLoader.setShouldRecompile(true);\n    }\n\n    @Override\n    public void stop() {\n        if (groovyClassLoader != null) {\n            groovyClassLoader.clearCache();\n            groovyClassLoader = null;\n        }\n    }\n\n    @Override\n    public boolean isAuthorized(APICallContext apiCallContext) throws SExecutionException {\n        if (dynamicPermissionCheck.isEnabled()) {\n            // Check if there is an active dynamic permission for this resource:\n            final Set<String> resourceDynamicPermissions = getDeclaredPermissions(apiCallContext.getApiName(),\n                    apiCallContext.getResourceName(), apiCallContext.getMethod(), apiCallContext.getResourceId(),\n                    dynamicPermissionsChecks);\n            if (!resourceDynamicPermissions.isEmpty()) {\n                // if there is a dynamic rule, use it to check the permissions\n                if (log.isTraceEnabled()) {\n                    log.trace(\"Dynamic REST API permissions check\");\n                }\n                return isAuthorizedByDynamicPermissions(apiCallContext,\n                        getSession().getUserPermissions(),\n                        resourceDynamicPermissions);\n            }\n        }\n        // if there is no dynamic rule, use the static permissions\n        return isAuthorizedByStaticPermissions(apiCallContext);\n    }\n\n    protected boolean isAuthorizedByStaticPermissions(APICallContext apiCallContext)\n            throws SExecutionException {\n        if (log.isTraceEnabled()) {\n            log.trace(\"Static REST API permissions check\");\n        }\n        final Set<String> resourcePermissions = getDeclaredPermissions(apiCallContext.getApiName(),\n                apiCallContext.getResourceName(), apiCallContext.getMethod(), apiCallContext.getResourceId(),\n                resourcesPermissionsMapping);\n        final Set<String> userPermissions = getSession().getUserPermissions();\n        for (final String resourcePermission : resourcePermissions) {\n            if (userPermissions.contains(resourcePermission)) {\n                return true;\n            }\n        }\n        log.debug(\n                \"Unauthorized access to \" + apiCallContext.getMethod() + \" \" + apiCallContext.getApiName() + \"/\"\n                        + apiCallContext.getResourceName()\n                        + (apiCallContext.getResourceId() != null ? \"/\" + apiCallContext.getResourceId() : \"\")\n                        + \" attempted by \" + getSession().getUserName()\n                        + \", required permissions: \" + resourcePermissions);\n\n        return false;\n    }\n\n    protected boolean isAuthorizedByDynamicPermissions(APICallContext apiCallContext, Set<String> userPermissions,\n            Set<String> resourceDynamicPermissions) throws SExecutionException {\n        checkResourceAuthorizationsSyntax(resourceDynamicPermissions);\n        if (checkDynamicPermissionsWithProfilesOrUsername(resourceDynamicPermissions, userPermissions)) {\n            return true;\n        }\n        final String resourceClassName = getResourceClassName(resourceDynamicPermissions);\n        if (resourceClassName != null) {\n            try {\n                return checkDynamicPermissionsWithScript(apiCallContext, resourceClassName);\n            } catch (ClassNotFoundException e) {\n                throw new SExecutionException(\n                        \"Unable to execute the security rule \" + resourceClassName + \" for the api call \"\n                                + apiCallContext + \" because the class \" + resourceClassName + \" is not found\",\n                        e);\n            }\n        }\n        return false;\n    }\n\n    protected void checkResourceAuthorizationsSyntax(final Set<String> resourceAuthorizations) {\n        for (final String resourceAuthorization : resourceAuthorizations) {\n            if (!resourceAuthorization.matches(\"(\" + USER_TYPE_AUTHORIZATION_PREFIX + \"|\"\n                    + PROFILE_TYPE_AUTHORIZATION_PREFIX + \"|\" + SCRIPT_TYPE_AUTHORIZATION_PREFIX + \")\\\\|.+\")) {\n                if (log.isWarnEnabled()) {\n                    log.warn(\"Error while getting dynamic authorizations. Unknown syntax: \" + resourceAuthorization\n                            + \" defined in dynamic-permissions-checks.properties\");\n                }\n                throw new IllegalArgumentException(\n                        format(\"Dynamic permission rule %s is not valid\", resourceAuthorization));\n            }\n        }\n    }\n\n    protected Set<String> getResourceAuthorizationsForProfileOrUser(final Set<String> resourcePermissions) {\n        return resourcePermissions.stream()\n                .filter(item -> item.startsWith(PROFILE_TYPE_AUTHORIZATION_PREFIX + \"|\")\n                        || item.startsWith(USER_TYPE_AUTHORIZATION_PREFIX + \"|\"))\n                .collect(Collectors.toSet());\n    }\n\n    protected boolean checkDynamicPermissionsWithProfilesOrUsername(final Set<String> resourceAuthorizations,\n            final Set<String> userPermissions) {\n        return getResourceAuthorizationsForProfileOrUser(resourceAuthorizations).stream()\n                .anyMatch(userPermissions::contains);\n    }\n\n    protected boolean checkDynamicPermissionsWithScript(final APICallContext apiCallContext,\n            final String resourceClassName) throws SExecutionException, ClassNotFoundException {\n        final boolean authorized = checkAPICallWithScript(resourceClassName, apiCallContext);\n        if (!authorized) {\n            if (log.isDebugEnabled()) {\n                StringBuilder msg = new StringBuilder().append(\"Unauthorized access to \")\n                        .append(apiCallContext.getMethod()).append(\" \").append(apiCallContext.getApiName())\n                        .append(\"/\").append(apiCallContext.getResourceName())\n                        .append(apiCallContext.getResourceId() != null ? \"/\" + apiCallContext.getResourceId()\n                                : \"\")\n                        .append(\", Permission script: \").append(resourceClassName);\n                msg.append(\", attempted by \").append(getSession().getUserName());\n                log.debug(msg.toString());\n            }\n        }\n        return authorized;\n    }\n\n    protected String getResourceClassName(final Set<String> resourcePermissions) {\n        String className = null;\n        for (final String resourcePermission : resourcePermissions) {\n            if (resourcePermission.startsWith(SCRIPT_TYPE_AUTHORIZATION_PREFIX + \"|\")) {\n                className = resourcePermission.substring((SCRIPT_TYPE_AUTHORIZATION_PREFIX + \"|\").length());\n            }\n        }\n        return className;\n    }\n\n    public Set<String> getResourceDynamicPermissions(final String resourceKey) {\n        return dynamicPermissionsChecks.getPropertyAsSet(resourceKey);\n    }\n\n    protected Set<String> getDeclaredPermissions(final String apiName, final String resourceName, final String method,\n            final String resourceQualifiers, final ResourcesPermissionsMapping resourcesPermissionsMapping) {\n        List<String> resourceQualifiersIds = null;\n        if (resourceQualifiers != null) {\n            resourceQualifiersIds = Arrays\n                    .asList(resourceQualifiers.split(ResourcesPermissionsMapping.RESOURCE_IDS_SEPARATOR));\n        }\n        Set<String> resourcePermissions = resourcesPermissionsMapping.getResourcePermissions(method, apiName,\n                resourceName, resourceQualifiersIds);\n        if (resourcePermissions.isEmpty()) {\n            resourcePermissions = resourcesPermissionsMapping.getResourcePermissionsWithWildCard(method, apiName,\n                    resourceName, resourceQualifiersIds);\n        }\n        if (resourcePermissions.isEmpty()) {\n            resourcePermissions = resourcesPermissionsMapping.getResourcePermissions(method, apiName, resourceName);\n        }\n        return resourcePermissions;\n    }\n\n    @Override\n    public void addPermissions(final String pageName, final Properties pageProperties) {\n        Set<String> customPagePermissions = getCustomPagePermissions(\n                pageProperties.getProperty(RESOURCES_PROPERTY),\n                resourcesPermissionsMapping);\n        addRestApiExtensionPermissions(resourcesPermissionsMapping, pageProperties);\n        addPagePermissions(pageName, pageProperties, customPagePermissions);\n    }\n\n    private void addPagePermissions(String pageName, Properties pageProperties, Set<String> customPagePermissions) {\n        if (ContentType.PAGE.equals(pageProperties.getProperty(PROPERTY_CONTENT_TYPE))\n                || ContentType.LAYOUT.equals(pageProperties.getProperty(PROPERTY_CONTENT_TYPE))) {\n            compoundPermissionsMapping.setInternalPropertyAsSet(pageName, customPagePermissions);\n        }\n    }\n\n    @Override\n    public void removePermissions(Properties pageProperties) {\n        for (String key : getApiExtensionResourcesPermissionsMapping(pageProperties).keySet()) {\n            resourcesPermissionsMapping.removeInternalProperty(key);\n        }\n        compoundPermissionsMapping.removeInternalProperty(pageProperties.getProperty(PageService.PROPERTIES_NAME));\n    }\n\n    public Set<String> getCustomPagePermissions(final String declaredPageResources,\n            final ResourcesPermissionsMapping resourcesPermissionsMapping) {\n        final Set<String> pageRestResources = PropertiesWithSet.stringToSet(declaredPageResources);\n        final Set<String> permissions = new HashSet<>();\n        for (final String pageRestResource : pageRestResources) {\n            final Set<String> resourcePermissions = resourcesPermissionsMapping.getPropertyAsSet(pageRestResource);\n            if (resourcePermissions.isEmpty()) {\n                log.warn(\"Error while getting resources permissions. Unknown resource: {} defined in page.properties\",\n                        pageRestResource);\n            }\n            permissions.addAll(resourcePermissions);\n        }\n        return permissions;\n    }\n\n    void addRestApiExtensionPermissions(final ResourcesPermissionsMapping resourcesPermissionsMapping,\n            final Properties pageProperties) {\n        final Map<String, String> permissionsMapping = getApiExtensionResourcesPermissionsMapping(\n                pageProperties);\n        permissionsMapping.keySet()\n                .forEach(key -> resourcesPermissionsMapping.setInternalProperty(key, permissionsMapping.get(key)));\n    }\n\n    private Map<String, String> getApiExtensionResourcesPermissionsMapping(Properties pageProperties) {\n        final Properties propertiesWithSet = new PropertiesWithSet(pageProperties);\n        final Map<String, String> permissionsMap = new HashMap<>();\n        if (ContentType.API_EXTENSION.equals(propertiesWithSet.getProperty(PROPERTY_CONTENT_TYPE))) {\n            final String apiExtensionList = propertiesWithSet.getProperty(PROPERTY_API_EXTENSIONS);\n            final String[] apiExtensions = apiExtensionList.split(EXTENSION_SEPARATOR);\n            for (final String apiExtension : apiExtensions) {\n                final String method = propertiesWithSet\n                        .getProperty(String.format(PROPERTY_METHOD_MASK, apiExtension.trim()));\n                String pathTemplate = propertiesWithSet\n                        .getProperty(String.format(PROPERTY_PATH_TEMPLATE_MASK, apiExtension.trim()));\n                // Remove '/' prefix if declared in page.properties\n                if (pathTemplate != null && pathTemplate.startsWith(\"/\")) {\n                    pathTemplate = pathTemplate.substring(1);\n                }\n                final String permissions = propertiesWithSet\n                        .getProperty(String.format(PROPERTY_PERMISSIONS_MASK, apiExtension.trim()));\n                permissionsMap.put(String.format(RESOURCE_PERMISSION_KEY_MASK, method, pathTemplate),\n                        String.format(RESOURCE_PERMISSION_VALUE, permissions));\n            }\n        }\n        return permissionsMap;\n    }\n\n    @Override\n    public Set<String> getResourcePermissions(final String resourceKey) {\n        return resourcesPermissionsMapping.getPropertyAsSet(resourceKey);\n    }\n\n    public void addCustomEntityPermissions(final String entity, final Set<String> resourcePermissions) {\n        customPermissionsMapping.setPropertyAsSet(entity, resourcePermissions);\n    }\n\n    public void removeCustomEntityPermissions(String entity) {\n        customPermissionsMapping.removeProperty(entity);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/bar/BusinessArchiveService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bar;\n\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.InvalidBusinessArchiveFormatException;\nimport org.bonitasoft.engine.commons.exceptions.SAlreadyExistsException;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectCreationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface BusinessArchiveService {\n\n    SProcessDefinition deploy(BusinessArchive businessArchive) throws SObjectCreationException, SAlreadyExistsException;\n\n    BusinessArchive export(long processDefinitionId) throws SBonitaException, InvalidBusinessArchiveFormatException;\n\n    void delete(long processDefinitionId) throws SProcessDefinitionNotFoundException, SObjectModificationException;\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/bar/BusinessArchiveServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bar;\n\nimport static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier;\nimport static org.bonitasoft.engine.form.FormMappingTarget.LEGACY;\n\nimport java.util.List;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.api.impl.SessionInfos;\nimport org.bonitasoft.engine.api.impl.resolver.BusinessArchiveArtifactsManager;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.InvalidBusinessArchiveFormatException;\nimport org.bonitasoft.engine.bpm.bar.form.model.FormMappingDefinition;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.classloader.SClassLoaderException;\nimport org.bonitasoft.engine.commons.exceptions.SAlreadyExistsException;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectCreationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.commons.exceptions.SV6FormsDeployException;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.exception.SDeletingEnabledProcessException;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDeletionException;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.dependency.model.ScopeType;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.SRecorderException;\n\n/**\n * @author Baptiste Mesta\n */\n@Slf4j\npublic class BusinessArchiveServiceImpl implements BusinessArchiveService {\n\n    private final ProcessDefinitionService processDefinitionService;\n    private final BusinessArchiveArtifactsManager businessArchiveArtifactsManager;\n    private final ClassLoaderService classLoaderService;\n\n    public BusinessArchiveServiceImpl(ProcessDefinitionService processDefinitionService,\n            BusinessArchiveArtifactsManager businessArchiveArtifactsManager, ClassLoaderService classLoaderService) {\n        this.processDefinitionService = processDefinitionService;\n        this.businessArchiveArtifactsManager = businessArchiveArtifactsManager;\n        this.classLoaderService = classLoaderService;\n    }\n\n    @Override\n    public SProcessDefinition deploy(BusinessArchive businessArchive)\n            throws SObjectCreationException, SAlreadyExistsException {\n\n        final DesignProcessDefinition designProcessDefinition = businessArchive.getProcessDefinition();\n        SProcessDefinition sProcessDefinition;\n        try {\n            checkIfExists(designProcessDefinition);\n            checkNoV6Forms(businessArchive);\n            sProcessDefinition = processDefinitionService.store(designProcessDefinition);\n\n            final boolean isResolved = businessArchiveArtifactsManager.resolveDependencies(businessArchive,\n                    sProcessDefinition);\n            if (isResolved) {\n                processDefinitionService.resolveProcess(sProcessDefinition.getId());\n            }\n            classLoaderService.refreshClassLoaderAfterUpdate(identifier(ScopeType.PROCESS, sProcessDefinition.getId()));\n        } catch (SV6FormsDeployException | SAlreadyExistsException e) {\n            throw e;\n        } catch (final SBonitaException e) {\n            throw new SObjectCreationException(e);\n        }\n        info(sProcessDefinition);\n        return sProcessDefinition;\n    }\n\n    private void checkNoV6Forms(BusinessArchive businessArchive) throws SV6FormsDeployException {\n\n        List<FormMappingDefinition> formMappings = businessArchive.getFormMappingModel().getFormMappings();\n        for (FormMappingDefinition formMapping : formMappings) {\n            if (formMapping.getTarget() == LEGACY) {\n                throw new SV6FormsDeployException(\"The process contains v6 forms\");\n            }\n        }\n    }\n\n    void checkIfExists(DesignProcessDefinition designProcessDefinition)\n            throws SBonitaReadException, SAlreadyExistsException {\n        try {\n            processDefinitionService.getProcessDefinitionId(designProcessDefinition.getName(),\n                    designProcessDefinition.getVersion());\n            throw new SAlreadyExistsException(\"The process \" + designProcessDefinition.getName() + \" in version \"\n                    + designProcessDefinition.getVersion()\n                    + \" already exists.\");\n        } catch (final SProcessDefinitionNotFoundException e) {\n            // ok\n        }\n    }\n\n    void info(SProcessDefinition sProcessDefinition) {\n        log.info(\"The user <{}> has installed process <{}> in version <{}> with id <{}>\",\n                SessionInfos.getUserNameFromSession(), sProcessDefinition.getName(),\n                sProcessDefinition.getVersion(), sProcessDefinition.getId());\n    }\n\n    @Override\n    public BusinessArchive export(long processDefinitionId)\n            throws SBonitaException, InvalidBusinessArchiveFormatException {\n        final DesignProcessDefinition designProcessDefinition = processDefinitionService\n                .getDesignProcessDefinition(processDefinitionId);\n\n        return businessArchiveArtifactsManager.exportBusinessArchive(processDefinitionId, designProcessDefinition);\n    }\n\n    @Override\n    public void delete(long processDefinitionId)\n            throws SProcessDefinitionNotFoundException, SObjectModificationException {\n        try {\n            final SProcessDefinition processDefinition = processDefinitionService\n                    .getProcessDefinition(processDefinitionId);\n            businessArchiveArtifactsManager.deleteDependencies(processDefinition);\n            processDefinitionService.delete(processDefinition.getId());\n            classLoaderService.removeLocalClassloader(identifier(ScopeType.PROCESS, processDefinition.getId()));\n        } catch (SBonitaReadException | SProcessDeletionException | SDeletingEnabledProcessException\n                | SRecorderException | SClassLoaderException e) {\n            throw new SObjectModificationException(\n                    \"Unable to delete the process definition <\" + processDefinitionId + \">\", e);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/bpm/contract/validation/ConstraintsDefinitionHelper.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.contract.validation;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.model.SContractDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SInputDefinition;\n\npublic class ConstraintsDefinitionHelper {\n\n    protected SInputDefinition getInputDefinition(final SContractDefinition contract, final String inputName) {\n        final List<SInputDefinition> inputs = contract.getInputDefinitions();\n        return getInputDefinition(inputName, inputs);\n\n    }\n\n    private SInputDefinition getInputDefinition(final String inputName, final List<SInputDefinition> inputs) {\n        SInputDefinition inputDefinition = getInputDefinitionInSimple(inputName, inputs);\n        if (inputDefinition == null) {\n            inputDefinition = getInputDefinitionInComplex(inputName, inputs);\n        }\n        return inputDefinition;\n    }\n\n    private SInputDefinition getInputDefinitionInSimple(final String inputName,\n            final List<SInputDefinition> simpleInputs) {\n        for (final SInputDefinition sInputDefinition : simpleInputs) {\n            if (sInputDefinition.getName().equals(inputName)) {\n                return sInputDefinition;\n            }\n        }\n        return null;\n    }\n\n    private SInputDefinition getInputDefinitionInComplex(final String inputName,\n            final List<SInputDefinition> complexInputs) {\n        for (final SInputDefinition sInputDefinition : complexInputs) {\n            if (sInputDefinition.getName().equals(inputName)) {\n                return sInputDefinition;\n            }\n            final SInputDefinition inputDefinition = getInputDefinition(inputName,\n                    sInputDefinition.getInputDefinitions());\n            if (inputDefinition != null) {\n                return inputDefinition;\n            }\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/bpm/contract/validation/ContractConstraintsValidator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.contract.validation;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.core.process.definition.model.SConstraintDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SContractDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SContractViolationException;\nimport org.bonitasoft.engine.expression.ContainerState;\nimport org.bonitasoft.engine.expression.ExpressionExecutorStrategy;\nimport org.bonitasoft.engine.expression.ExpressionService;\nimport org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.expression.model.builder.SExpressionBuilderFactory;\n\n@Slf4j\npublic class ContractConstraintsValidator {\n\n    private final ExpressionService expressionService;\n\n    public ContractConstraintsValidator(ExpressionService expressionService) {\n        this.expressionService = expressionService;\n    }\n\n    public void validate(long processDefinitionId, final SContractDefinition contract,\n            final Map<String, Serializable> variables)\n            throws SContractViolationException {\n        final Map<String, Serializable> vars = (variables == null ? Collections.<String, Serializable> emptyMap()\n                : variables);\n        final List<String> comments = new ArrayList<>();\n        final Map<String, Object> context = new HashMap<>(vars.size());\n        context.putAll(vars);\n        context.put(ExpressionExecutorStrategy.DEFINITION_ID, processDefinitionId);\n        for (final SConstraintDefinition constraint : contract.getConstraints()) {\n            log.debug(\"Evaluating constraint [{}] on input(s) {}\", constraint.getName(), constraint.getInputNames());\n\n            validateConstraint(comments, constraint, context);\n        }\n        if (!comments.isEmpty()) {\n            throw new SContractViolationException(\"Error while validating constraints\", comments);\n        }\n    }\n\n    private void validateConstraint(final List<String> comments, final SConstraintDefinition constraint,\n            final Map<String, Object> variables)\n            throws SContractViolationException {\n        Boolean valid;\n        try {\n            valid = (Boolean) expressionService.evaluate(createGroovyExpression(constraint), variables,\n                    Collections.<Integer, Object> emptyMap(),\n                    ContainerState.ACTIVE);\n        } catch (SExpressionTypeUnknownException | SExpressionEvaluationException\n                | SExpressionDependencyMissingException | SInvalidExpressionException e) {\n            log.error(\"Constraint [{}] on input(s) {} evaluation failed.\",\n                    constraint.getName(), constraint.getInputNames(), e);\n            throw new SContractViolationException(\"Exception while validating constraints\", e);\n        }\n        if (!valid) {\n            log.warn(\"Constraint [{}] on input(s) {} is not valid\", constraint.getName(), constraint.getInputNames());\n            comments.add(constraint.getExplanation());\n        }\n    }\n\n    private SExpression createGroovyExpression(SConstraintDefinition constraint) throws SInvalidExpressionException {\n        return BuilderFactory.get(SExpressionBuilderFactory.class).createNewInstance().setName(constraint.getName())\n                .setContent(constraint.getExpression())\n                .setExpressionType(ExpressionExecutorStrategy.TYPE_READ_ONLY_CONDITION_SCRIPT)\n                .setInterpreter(ExpressionExecutorStrategy.INTERPRETER_GROOVY)\n                .setReturnType(Boolean.class.getName()).done();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/bpm/contract/validation/ContractStructureValidator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.contract.validation;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.core.process.definition.model.SContractDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SInputContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SInputDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SContractViolationException;\n\n@Slf4j\npublic class ContractStructureValidator {\n\n    private final ContractTypeValidator typeValidator;\n\n    public ContractStructureValidator(final ContractTypeValidator typeValidator) {\n        this.typeValidator = typeValidator;\n    }\n\n    public void validate(final SContractDefinition contract, final Map<String, Serializable> inputs)\n            throws SContractViolationException {\n        final ErrorReporter errorReporter = new ErrorReporter();\n        validateInputContainer(contract, inputs, errorReporter);\n        if (errorReporter.hasError()) {\n            throw new SContractViolationException(\"Error while validating expected inputs\", errorReporter.getErrors());\n        }\n    }\n\n    void validateInputContainer(SInputContainerDefinition inputContainer, Map<String, Serializable> inputs,\n            ErrorReporter errorReporter) {\n        logInputsWhichAreNotInContract(inputContainer.getInputDefinitions(), inputs);\n        for (final SInputDefinition inputDefinition : inputContainer.getInputDefinitions()) {\n            // For each input, fills the errorReporter if some rule is not valid:\n            validateInput(inputs != null ? inputs : Collections.<String, Serializable> emptyMap(), errorReporter,\n                    inputDefinition);\n        }\n    }\n\n    private void validateInput(Map<String, Serializable> inputs, ErrorReporter errorReporter,\n            SInputDefinition inputDefinition) {\n        final String inputName = inputDefinition.getName();\n        if (expectedInputIsProvided(inputs, errorReporter, inputName)) {\n            final Serializable input = inputs.get(inputName);\n            if (input != null && typeValidator.validate(inputDefinition, input, errorReporter)) {\n                validateChildren(inputs, errorReporter, inputDefinition);\n            }\n        }\n    }\n\n    private void validateChildren(Map<String, Serializable> inputs, ErrorReporter errorReporter,\n            SInputDefinition inputDefinition) {\n        if (inputDefinition.hasChildren() && inputDefinition.getType() == null) {\n            if (inputDefinition.isMultiple()) {\n                for (final Map<String, Serializable> complexItem : (List<Map<String, Serializable>>) inputs\n                        .get(inputDefinition.getName())) {\n                    // we tolerate (and ignore) null values in lists:\n                    if (complexItem != null) {\n                        validateInputContainer(inputDefinition, complexItem, errorReporter);\n                    }\n                }\n            } else {\n                validateInputContainer(inputDefinition,\n                        (Map<String, Serializable>) inputs.get(inputDefinition.getName()), errorReporter);\n            }\n        }\n    }\n\n    private boolean expectedInputIsProvided(Map<String, Serializable> inputs, ErrorReporter errorReporter,\n            String inputName) {\n        if (!inputs.containsKey(inputName)) {\n            errorReporter.addError(\"Expected input [\" + inputName + \"] is missing\");\n            return false;\n        }\n        return true;\n    }\n\n    // Log when some inputs were not expected but provided:\n    private void logInputsWhichAreNotInContract(final List<SInputDefinition> simpleInputs,\n            final Map<String, Serializable> inputs) {\n        if (log.isDebugEnabled()) {\n            for (final String input : getInputsWhichAreNotInContract(simpleInputs, inputs)) {\n                log.debug(\"Unexpected input [\" + input + \"] provided\");\n            }\n        }\n    }\n\n    private List<String> getInputsWhichAreNotInContract(final List<SInputDefinition> simpleInputs,\n            final Map<String, Serializable> inputs) {\n        if (inputs == null || inputs.isEmpty()) {\n            return Collections.emptyList();\n        }\n        final List<String> keySet = new ArrayList<>(inputs.keySet());\n        for (final SInputDefinition def : simpleInputs) {\n            keySet.remove(def.getName());\n        }\n        return keySet;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/bpm/contract/validation/ContractTypeValidator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.contract.validation;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.core.process.definition.model.SInputDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SType;\n\n/**\n * Validate that a value is assignable to a given contract type\n *\n * @author Colin Puy\n */\npublic class ContractTypeValidator {\n\n    public boolean validate(final SInputDefinition definition, final Object object, ErrorReporter errorReporter) {\n        if (definition.hasChildren() && definition.getType() == null) {\n            if (!isValidForComplexType(definition, object, errorReporter)) {\n                errorReporter.addError(object + \" cannot be assigned to \"\n                        + (definition.isMultiple() ? \"multiple \" : \"\")\n                        + \"COMPLEX type\");\n                return false;\n            }\n        } else {\n            if (!isValidForSimpleType(definition, object)) {\n                errorReporter.addError(object\n                        + \" cannot be assigned to \"\n                        + (definition.isMultiple() ? \"multiple \" : \"\")\n                        + definition.getType());\n                return false;\n            }\n        }\n        return true;\n    }\n\n    private boolean isValidForSimpleType(final SInputDefinition definition, final Object object) {\n        if (definition.isMultiple()) {\n            return isValidForMultipleSimpleType(definition, object);\n        } else {\n            SType type = definition.getType();\n            return type != null && type.validate(object);\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private boolean isValidForMultipleSimpleType(final SInputDefinition definition, final Object object) {\n        if (!(object instanceof List<?>)) {\n            return false;\n        }\n        for (final Object item : (List<Object>) object) {\n            if (!definition.getType().validate(item)) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    private boolean isValidForComplexType(final SInputDefinition definition, final Object object,\n            ErrorReporter errorReporter) {\n        if (definition.isMultiple()) {\n            return isValidForMultipleComplexType(definition, object, errorReporter);\n        } else {\n            return isValidForSimpleComplexType(definition, object, errorReporter);\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private boolean isValidForMultipleComplexType(final SInputDefinition definition, final Object object,\n            ErrorReporter errorReporter) {\n        if (!(object instanceof List<?>)) {\n            return false;\n        }\n        for (final Object item : (List<Object>) object) {\n            //throws exception if invalid\n            isValidForSimpleComplexType(definition, item, errorReporter);\n        }\n        return true;\n    }\n\n    private boolean isValidForSimpleComplexType(final SInputDefinition definition, final Object object,\n            ErrorReporter errorReporter) {\n        try {\n            @SuppressWarnings(\"unchecked\")\n            final Map<String, Object> map = (Map<String, Object>) object;\n            for (final SInputDefinition sInputDefinition : definition.getInputDefinitions()) {\n                Object value = (map == null) ? null : map.get(sInputDefinition.getName());\n                if (value != null) {\n                    validate(sInputDefinition, value, errorReporter);\n                }\n            }\n            return !errorReporter.hasError();\n        } catch (final ClassCastException e) {\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/bpm/contract/validation/ContractValidator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.contract.validation;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.core.process.definition.model.SContractDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SContractViolationException;\n\n/**\n * Validate tasks inputs according to given contract\n *\n * @author Matthieu Chaffotte\n * @author Colin Puy\n */\npublic class ContractValidator {\n\n    private final ContractStructureValidator structureValidator;\n    private final ContractConstraintsValidator rulesValidator;\n\n    public ContractValidator(final ContractStructureValidator contractStructureValidator,\n            final ContractConstraintsValidator contractRulesValidator) {\n        structureValidator = contractStructureValidator;\n        rulesValidator = contractRulesValidator;\n    }\n\n    public void validate(long processDefinitionId, final SContractDefinition contract,\n            final Map<String, Serializable> variables)\n            throws SContractViolationException {\n        structureValidator.validate(contract, variables);\n        rulesValidator.validate(processDefinitionId, contract, variables);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/bpm/contract/validation/ContractValidatorFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.contract.validation;\n\nimport org.bonitasoft.engine.expression.ExpressionService;\n\n/**\n * Contract validator factory - might be replaced by a spring configuration\n *\n * @author Colin Puy\n */\npublic class ContractValidatorFactory {\n\n    public ContractValidator createContractValidator(ExpressionService expressionService) {\n        return new ContractValidator(new ContractStructureValidator(new ContractTypeValidator()),\n                new ContractConstraintsValidator(expressionService));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/bpm/contract/validation/ContractVariableHelper.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.contract.validation;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.core.process.definition.model.SConstraintDefinition;\n\npublic class ContractVariableHelper {\n\n    /**\n     * @param constraint\n     * @param contractVariables\n     * @return\n     *         list of elements that must be checked by this mandatory constraint\n     */\n    public List<Map<String, Serializable>> buildMandatoryMultipleInputVariables(final SConstraintDefinition constraint,\n            final Map<String, Serializable> contractVariables) {\n        final List<Map<String, Serializable>> constraintVariablesList = new ArrayList<Map<String, Serializable>>();\n        for (final String inputName : constraint.getInputNames()) {\n            buildRecursiveVariableList(contractVariables, constraintVariablesList, inputName);\n        }\n        return constraintVariablesList;\n\n    }\n\n    private List<Map<String, Serializable>> buildRecursiveVariableList(final Map<String, Serializable> currentVariables,\n            final List<Map<String, Serializable>> constraintVariablesList,\n            final String inputName) {\n        if (currentVariables.containsKey(inputName)) {\n            final Map<String, Serializable> value = new HashMap<String, Serializable>();\n            value.put(inputName, currentVariables.get(inputName));\n            constraintVariablesList.add(value);\n        } else {\n            buildRecursiveComplexVariableList(currentVariables, constraintVariablesList, inputName);\n        }\n        return constraintVariablesList;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private Collection<? extends Map<String, Serializable>> buildRecursiveComplexVariableList(\n            final Map<String, Serializable> currentVariables,\n            final List<Map<String, Serializable>> constraintVariablesList, final String inputName) {\n        for (final Entry<String, Serializable> variableEntry : currentVariables.entrySet()) {\n            final Object variableValue = variableEntry.getValue();\n            if (variableValue instanceof Map<?, ?>) {\n                //complex case\n                buildRecursiveVariableList((Map<String, Serializable>) variableValue, constraintVariablesList,\n                        inputName);\n            }\n            if (variableValue instanceof List<?>) {\n                //multiple case\n                for (final Serializable variableValueItem : (List<Serializable>) variableValue) {\n                    if (variableValueItem instanceof Map<?, ?>) {\n                        //complex case\n                        buildRecursiveVariableList((Map<String, Serializable>) variableValueItem,\n                                constraintVariablesList, inputName);\n                    }\n                }\n            }\n        }\n        return constraintVariablesList;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public List<Map<String, Serializable>> convertMultipleToList(final Map<String, Serializable> multipleVariable) {\n        final List<Map<String, Serializable>> multipleVariables = new ArrayList<Map<String, Serializable>>();\n        final Set<Entry<String, Serializable>> entrySet = multipleVariable.entrySet();\n        for (final Entry<String, Serializable> entry : entrySet) {\n            if (entry.getValue() instanceof List<?>) {\n                for (final Serializable value : (List<Serializable>) entry.getValue()) {\n                    final Map<String, Serializable> item = new HashMap<String, Serializable>();\n                    item.put(entry.getKey(), value);\n                    multipleVariables.add(item);\n                }\n            }\n        }\n        return multipleVariables;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/bpm/contract/validation/ErrorReporter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.contract.validation;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * @author Baptiste Mesta\n */\npublic class ErrorReporter {\n\n    private final List<String> errors = new ArrayList<>();\n\n    public void addError(String error) {\n        errors.add(error);\n    }\n\n    public List<String> getErrors() {\n        return errors;\n    }\n\n    public boolean hasError() {\n        return !errors.isEmpty();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/bpm/contract/validation/InputValidationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.contract.validation;\n\n@SuppressWarnings(\"serial\")\npublic class InputValidationException extends Exception {\n\n    public InputValidationException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/bpm/model/impl/BPMInstancesCreator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.model.impl;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.actor.mapping.SActorNotFoundException;\nimport org.bonitasoft.engine.actor.mapping.model.SActor;\nimport org.bonitasoft.engine.api.impl.transaction.actor.GetActor;\nimport org.bonitasoft.engine.bpm.connector.ConnectorState;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.business.data.RefBusinessDataRetriever;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.ScopedException;\nimport org.bonitasoft.engine.core.connector.ConnectorInstanceService;\nimport org.bonitasoft.engine.core.data.instance.TransientDataService;\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.model.SLeftOperand;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.core.operation.model.SOperatorType;\nimport org.bonitasoft.engine.core.process.definition.model.SActivityDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SBusinessDataDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SCallActivityDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SConnectorDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.definition.model.SGatewayDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SHumanTaskDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SLoopCharacteristics;\nimport org.bonitasoft.engine.core.process.definition.model.SMultiInstanceLoopCharacteristics;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SStandardLoopCharacteristics;\nimport org.bonitasoft.engine.core.process.definition.model.SSubProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SBoundaryEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SEndEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SIntermediateCatchEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SIntermediateThrowEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SStartEventDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.GatewayInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.RefBusinessDataService;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.*;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceCreationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowElementsContainerType;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SGatewayInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.core.process.instance.model.STaskPriority;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SActivityInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SAutomaticTaskInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SAutomaticTaskInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SCallActivityInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SCallActivityInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SFlowNodeInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SGatewayInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SGatewayInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SHumanTaskInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SLoopActivityInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SLoopActivityInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SManualTaskInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SManualTaskInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SMultiInstanceActivityInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SMultiInstanceActivityInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SReceiveTaskInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SReceiveTaskInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SSendTaskInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SSendTaskInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SSubProcessActivityInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SSubProcessActivityInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SUserTaskInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.business.data.SRefBusinessDataInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SBoundaryEventInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SBoundaryEventInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SEndEventInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SEndEventInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SIntermediateCatchEventInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SIntermediateCatchEventInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SIntermediateThrowEventInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SIntermediateThrowEventInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SStartEventInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SStartEventInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SEventInstance;\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceService;\nimport org.bonitasoft.engine.data.instance.api.ParentContainerResolver;\nimport org.bonitasoft.engine.data.instance.exception.SDataInstanceException;\nimport org.bonitasoft.engine.data.instance.exception.SDataInstanceReadException;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SDataInstanceBuilder;\nimport org.bonitasoft.engine.data.instance.model.exceptions.SDataInstanceNotWellFormedException;\nimport org.bonitasoft.engine.execution.state.FlowNodeStateManager;\nimport org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.exception.SExpressionException;\nimport org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.log.LogMessageBuilder;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\n@Slf4j\npublic class BPMInstancesCreator {\n\n    private final ActivityInstanceService activityInstanceService;\n\n    private final ActorMappingService actorMappingService;\n\n    private final GatewayInstanceService gatewayInstanceService;\n\n    private final EventInstanceService eventInstanceService;\n\n    private final ConnectorInstanceService connectorInstanceService;\n\n    private final ExpressionResolverService expressionResolverService;\n\n    private final DataInstanceService dataInstanceService;\n\n    private final TransientDataService transientDataService;\n\n    private final ParentContainerResolver parentContainerResolver;\n\n    private final RefBusinessDataService refBusinessDataService;\n\n    private final RefBusinessDataRetriever refBusinessDataRetriever;\n\n    private FlowNodeStateManager stateManager;\n\n    public BPMInstancesCreator(final ActivityInstanceService activityInstanceService,\n            final ActorMappingService actorMappingService, final GatewayInstanceService gatewayInstanceService,\n            final EventInstanceService eventInstanceService, final ConnectorInstanceService connectorInstanceService,\n            final ExpressionResolverService expressionResolverService,\n            final DataInstanceService dataInstanceService, final TransientDataService transientDataService,\n            final ParentContainerResolver parentContainerResolver, RefBusinessDataService refBusinessDataService,\n            final RefBusinessDataRetriever refBusinessDataRetriever) {\n        super();\n        this.activityInstanceService = activityInstanceService;\n        this.actorMappingService = actorMappingService;\n        this.gatewayInstanceService = gatewayInstanceService;\n        this.eventInstanceService = eventInstanceService;\n        this.connectorInstanceService = connectorInstanceService;\n        this.expressionResolverService = expressionResolverService;\n        this.dataInstanceService = dataInstanceService;\n        this.transientDataService = transientDataService;\n        this.parentContainerResolver = parentContainerResolver;\n        this.refBusinessDataService = refBusinessDataService;\n        this.refBusinessDataRetriever = refBusinessDataRetriever;\n    }\n\n    public void setStateManager(final FlowNodeStateManager stateManager) {\n        this.stateManager = stateManager;\n    }\n\n    public List<SFlowNodeInstance> createFlowNodeInstances(final Long processDefinitionId, final long rootContainerId,\n            final long parentContainerId,\n            final List<SFlowNodeDefinition> flowNodeDefinitions, final long rootProcessInstanceId,\n            final long parentProcessInstanceId,\n            final SStateCategory stateCategory) throws SBonitaException {\n        final List<SFlowNodeInstance> flownNodeInstances = new ArrayList<>(flowNodeDefinitions.size());\n        for (final SFlowNodeDefinition sFlowNodeDefinition : flowNodeDefinitions) {\n            flownNodeInstances.add(createFlowNodeInstance(processDefinitionId, rootContainerId, parentContainerId,\n                    SFlowElementsContainerType.PROCESS,\n                    sFlowNodeDefinition, rootProcessInstanceId, parentProcessInstanceId, false, -1, stateCategory, -1));\n        }\n        return flownNodeInstances;\n    }\n\n    public SFlowNodeInstance createFlowNodeInstance(final long processDefinitionId, final long rootContainerId,\n            final long parentContainerId,\n            final SFlowElementsContainerType parentContainerType, final SFlowNodeDefinition sFlowNodeDefinition,\n            final long rootProcessInstanceId,\n            final long parentProcessInstanceId, final boolean createInnerActivity, final int loopCounter,\n            final SStateCategory stateCategory,\n            final long relatedActivityInstanceId) throws SBonitaException {\n        final SFlowNodeInstance flownNodeInstance = toFlowNodeInstance(processDefinitionId, rootContainerId,\n                parentContainerId, parentContainerType,\n                sFlowNodeDefinition, rootProcessInstanceId, parentProcessInstanceId, createInnerActivity, loopCounter,\n                stateCategory,\n                relatedActivityInstanceId);\n        if (SFlowNodeType.GATEWAY.equals(flownNodeInstance.getType())) {\n            gatewayInstanceService.createGatewayInstance((SGatewayInstance) flownNodeInstance);\n        } else if (flownNodeInstance instanceof SActivityInstance) {\n            activityInstanceService.createActivityInstance((SActivityInstance) flownNodeInstance);\n        } else {\n            eventInstanceService.createEventInstance((SEventInstance) flownNodeInstance);\n        }\n        createConnectorInstances(flownNodeInstance, sFlowNodeDefinition.getConnectors(),\n                SConnectorInstance.FLOWNODE_TYPE);\n        return flownNodeInstance;\n    }\n\n    public SFlowNodeInstance toFlowNodeInstance(final long processDefinitionId, final long rootContainerId,\n            final long parentContainerId,\n            final SFlowElementsContainerType parentContainerType, final SFlowNodeDefinition sFlowNodeDefinition,\n            final long rootProcessInstanceId,\n            final long parentProcessInstanceId, final boolean createInnerActivity, final int loopCounter,\n            final SStateCategory stateCategory,\n            final long relatedActivityInstanceId) throws SActorNotFoundException, SActivityReadException {\n        if (!createInnerActivity && sFlowNodeDefinition instanceof SActivityDefinition activityDefinition) {\n            final SLoopCharacteristics loopCharacteristics = activityDefinition.getLoopCharacteristics();\n            if (loopCharacteristics != null) {\n                SFlowNodeInstanceBuilder builder;\n                if (loopCharacteristics instanceof SStandardLoopCharacteristics) {\n                    builder = createLoopActivityInstance(processDefinitionId, rootContainerId, parentContainerId,\n                            rootProcessInstanceId,\n                            parentProcessInstanceId, activityDefinition);\n                } else {\n                    builder = createMultiInstanceActivityInstance(processDefinitionId, rootContainerId,\n                            parentContainerId, rootProcessInstanceId,\n                            parentProcessInstanceId, activityDefinition,\n                            (SMultiInstanceLoopCharacteristics) loopCharacteristics);\n                }\n                builder.setState(stateManager.getFirstState(builder.getFlowNodeType()));\n                builder.setStateCategory(stateCategory);\n                return builder.done();\n            }\n        }\n\n        SFlowNodeInstanceBuilder builder;\n        switch (sFlowNodeDefinition.getType()) {\n            case AUTOMATIC_TASK:\n                builder = createAutomaticTaskInstance(processDefinitionId, rootContainerId, parentContainerId,\n                        parentContainerType, sFlowNodeDefinition,\n                        parentProcessInstanceId);\n                break;\n            case END_EVENT:\n                builder = createEndEventInstance(processDefinitionId, rootContainerId, parentContainerId,\n                        sFlowNodeDefinition, rootProcessInstanceId,\n                        parentProcessInstanceId);\n                break;\n            case GATEWAY:\n                builder = createGatewayInstance(processDefinitionId, rootContainerId, parentContainerId,\n                        parentContainerType, sFlowNodeDefinition,\n                        rootProcessInstanceId, parentProcessInstanceId);\n                break;\n            case INTERMEDIATE_CATCH_EVENT:\n                builder = createIntermediateCatchEventInstance(processDefinitionId, rootContainerId, parentContainerId,\n                        parentContainerType,\n                        sFlowNodeDefinition, rootProcessInstanceId, parentProcessInstanceId);\n                break;\n            case INTERMEDIATE_THROW_EVENT:\n                builder = createIntermediateThrowEventInstance(processDefinitionId, rootContainerId, parentContainerId,\n                        parentContainerType,\n                        sFlowNodeDefinition, rootProcessInstanceId, parentProcessInstanceId);\n                break;\n            case MANUAL_TASK:\n                builder = createManualTaskInstance(processDefinitionId, rootContainerId, parentContainerId,\n                        parentContainerType, sFlowNodeDefinition,\n                        rootProcessInstanceId, parentProcessInstanceId);\n                break;\n            case START_EVENT:\n                builder = createStartEventInstance(processDefinitionId, rootContainerId, parentContainerId,\n                        parentContainerType, sFlowNodeDefinition,\n                        rootProcessInstanceId, parentProcessInstanceId);\n                break;\n            case USER_TASK:\n                builder = createUserTaskInstance(processDefinitionId, rootContainerId, parentContainerId,\n                        parentContainerType, sFlowNodeDefinition,\n                        rootProcessInstanceId, parentProcessInstanceId);\n                break;\n            case RECEIVE_TASK:\n                builder = createReceiveTaskInstance(processDefinitionId, rootContainerId, parentContainerId,\n                        parentContainerType, sFlowNodeDefinition,\n                        parentProcessInstanceId);\n                break;\n            case SEND_TASK:\n                builder = createSendTaskInstance(processDefinitionId, rootContainerId, parentContainerId,\n                        parentContainerType, sFlowNodeDefinition,\n                        parentProcessInstanceId);\n                break;\n            case CALL_ACTIVITY:\n                builder = createCallActivityInstance(processDefinitionId, rootContainerId, parentContainerId,\n                        parentContainerType, sFlowNodeDefinition,\n                        rootProcessInstanceId, parentProcessInstanceId);\n                break;\n            case SUB_PROCESS:\n                builder = createSubProcessActivityInstance(processDefinitionId, rootContainerId, parentContainerId,\n                        parentContainerType, sFlowNodeDefinition,\n                        rootProcessInstanceId, parentProcessInstanceId);\n                break;\n            case BOUNDARY_EVENT:\n                builder = createBoundaryEventInstance(processDefinitionId, rootContainerId, parentContainerId,\n                        parentContainerType,\n                        (SBoundaryEventDefinition) sFlowNodeDefinition, rootProcessInstanceId, parentProcessInstanceId,\n                        relatedActivityInstanceId);\n                break;\n            default:\n                throw new SActivityReadException(\"Activity type not found : \" + sFlowNodeDefinition.getType());\n        }\n        builder.setLoopCounter(loopCounter);\n        builder.setState(stateManager.getFirstState(builder.getFlowNodeType()));\n        builder.setStateCategory(stateCategory);\n        return builder.done();\n    }\n\n    private SCallActivityInstanceBuilder createCallActivityInstance(final long processDefinitionId,\n            final long rootContainerId, final long parentContainerId,\n            final SFlowElementsContainerType parentContainerType, final SFlowNodeDefinition sFlowNodeDefinition,\n            final long rootProcessInstanceId,\n            final long parentProcessInstanceId) {\n        final SCallActivityDefinition callActivityDef = (SCallActivityDefinition) sFlowNodeDefinition;\n        final SCallActivityInstanceBuilderFactory builderFact = BuilderFactory\n                .get(SCallActivityInstanceBuilderFactory.class);\n        final SCallActivityInstanceBuilder builder = builderFact.createNewCallActivityInstance(\n                callActivityDef.getName(), callActivityDef.getId(),\n                rootContainerId, parentContainerId, processDefinitionId,\n                rootProcessInstanceId, parentProcessInstanceId);\n        updateActivityInstance(parentContainerId, parentContainerType, sFlowNodeDefinition, builder);\n        return builder;\n    }\n\n    private SSubProcessActivityInstanceBuilder createSubProcessActivityInstance(final long processDefinitionId,\n            final long rootContainerId,\n            final long parentContainerId, final SFlowElementsContainerType parentContainerType,\n            final SFlowNodeDefinition sFlowNodeDefinition,\n            final long rootProcessInstanceId, final long parentProcessInstanceId) {\n        final SSubProcessDefinition subProcessActivityDef = (SSubProcessDefinition) sFlowNodeDefinition;\n        final SSubProcessActivityInstanceBuilderFactory builderFact = BuilderFactory\n                .get(SSubProcessActivityInstanceBuilderFactory.class);\n        final SSubProcessActivityInstanceBuilder builder = builderFact.createNewSubProcessActivityInstance(\n                subProcessActivityDef.getName(),\n                subProcessActivityDef.getId(), rootContainerId, parentContainerId,\n                processDefinitionId, rootProcessInstanceId, parentProcessInstanceId,\n                subProcessActivityDef.isTriggeredByEvent());\n        updateActivityInstance(parentContainerId, parentContainerType, sFlowNodeDefinition, builder);\n        return builder;\n    }\n\n    private SHumanTaskInstanceBuilder createUserTaskInstance(final long processDefinitionId, final long rootContainerId,\n            final long parentContainerId,\n            final SFlowElementsContainerType parentContainerType, final SFlowNodeDefinition sFlowNodeDefinition,\n            final long rootProcessInstanceId,\n            final long parentProcessInstanceId) throws SActorNotFoundException {\n        final SHumanTaskInstanceBuilder builder = createHumanTaskInstance(processDefinitionId, rootContainerId,\n                parentContainerId, sFlowNodeDefinition,\n                rootProcessInstanceId, parentProcessInstanceId);\n        updateActivityInstance(parentContainerId, parentContainerType, sFlowNodeDefinition, builder);\n        return builder;\n    }\n\n    private SStartEventInstanceBuilder createStartEventInstance(final long processDefinitionId,\n            final long rootContainerId, final long parentContainerId,\n            final SFlowElementsContainerType parentContainerType, final SFlowNodeDefinition sFlowNodeDefinition,\n            final long rootProcessInstanceId,\n            final long parentProcessInstanceId) {\n        final SStartEventDefinition startEventDef = (SStartEventDefinition) sFlowNodeDefinition;\n        final SStartEventInstanceBuilder builder = BuilderFactory.get(SStartEventInstanceBuilderFactory.class)\n                .createNewStartEventInstance(\n                        startEventDef.getName(), startEventDef.getId(),\n                        rootContainerId, parentContainerId, processDefinitionId, rootProcessInstanceId,\n                        parentProcessInstanceId);\n        updateFlowNodeInstance(parentContainerId, parentContainerType, builder);\n        return builder;\n    }\n\n    private SHumanTaskInstanceBuilder createManualTaskInstance(final long processDefinitionId,\n            final long rootContainerId, final long parentContainerId,\n            final SFlowElementsContainerType parentContainerType, final SFlowNodeDefinition sFlowNodeDefinition,\n            final long rootProcessInstanceId,\n            final long parentProcessInstanceId) throws SActorNotFoundException {\n        final SHumanTaskDefinition humanTaskDefinition = (SHumanTaskDefinition) sFlowNodeDefinition;\n        final String actorName = humanTaskDefinition.getActorName();\n\n        final SActor actor = getActor(processDefinitionId, actorName);\n        final SHumanTaskInstanceBuilder builder = BuilderFactory.get(SManualTaskInstanceBuilderFactory.class)\n                .createNewManualTaskInstance(\n                        humanTaskDefinition.getName(), humanTaskDefinition.getId(), rootContainerId, parentContainerId,\n                        actor.getId(), processDefinitionId,\n                        rootProcessInstanceId, parentProcessInstanceId);\n        fillHumanTask(humanTaskDefinition, builder);\n        updateActivityInstance(parentContainerId, parentContainerType, sFlowNodeDefinition, builder);\n        return builder;\n    }\n\n    public SManualTaskInstance createManualTaskInstance(final long parentUserTaskId, final String name,\n            final long flowNodeDefinitionId,\n            final String displayName, final long userId, final String description, final long dueDate,\n            final STaskPriority priority)\n            throws SFlowNodeNotFoundException, SFlowNodeReadException {\n        final SHumanTaskInstance parentUserTask = (SHumanTaskInstance) activityInstanceService\n                .getFlowNodeInstance(parentUserTaskId);\n        final SManualTaskInstanceBuilderFactory manualTaskInstanceBuilderFact = BuilderFactory\n                .get(SManualTaskInstanceBuilderFactory.class);\n        final long processDefinitionId = parentUserTask\n                .getLogicalGroup(manualTaskInstanceBuilderFact.getProcessDefinitionIndex());\n        final long rootProcessInstanceId = parentUserTask\n                .getLogicalGroup(manualTaskInstanceBuilderFact.getRootProcessInstanceIndex());\n        final long parentProcessInstanceId = parentUserTask\n                .getLogicalGroup(manualTaskInstanceBuilderFact.getParentProcessInstanceIndex());\n        final SManualTaskInstanceBuilder builder = manualTaskInstanceBuilderFact.createNewManualTaskInstance(name,\n                flowNodeDefinitionId,\n                parentUserTask.getRootContainerId(), parentUserTaskId, parentUserTask.getActorId(), processDefinitionId,\n                rootProcessInstanceId,\n                parentProcessInstanceId);\n        builder.setParentContainerId(parentUserTaskId);\n        builder.setParentActivityInstanceId(parentUserTaskId);\n        builder.setAssigneeId(userId);\n        builder.setExpectedEndDate(dueDate);\n        builder.setDescription(description);\n        builder.setDisplayDescription(description);\n        builder.setDisplayName(displayName);\n        builder.setPriority(priority);\n        builder.setState(stateManager.getFirstState(builder.getFlowNodeType()));\n        return builder.done();\n    }\n\n    private SActor getActor(final long processDefinitionId, final String actorName) throws SActorNotFoundException {\n        final GetActor getSActor = new GetActor(actorMappingService, actorName, processDefinitionId);\n        try {\n            getSActor.execute();\n        } catch (final SBonitaException sbe) {\n            throw new SActorNotFoundException(sbe);\n        }\n        return getSActor.getResult();\n    }\n\n    private SIntermediateThrowEventInstanceBuilder createIntermediateThrowEventInstance(final long processDefinitionId,\n            final long rootContainerId,\n            final long parentContainerId, final SFlowElementsContainerType parentContainerType,\n            final SFlowNodeDefinition sFlowNodeDefinition,\n            final long rootProcessInstanceId, final long parentProcessInstanceId) {\n        final SIntermediateThrowEventDefinition intermediateThrowEvent = (SIntermediateThrowEventDefinition) sFlowNodeDefinition;\n        final SIntermediateThrowEventInstanceBuilder builder = BuilderFactory\n                .get(SIntermediateThrowEventInstanceBuilderFactory.class)\n                .createNewIntermediateThrowEventInstance(\n                        intermediateThrowEvent.getName(), intermediateThrowEvent.getId(), rootContainerId,\n                        parentContainerId, processDefinitionId,\n                        rootProcessInstanceId, parentProcessInstanceId);\n        updateFlowNodeInstance(parentContainerId, parentContainerType, builder);\n        return builder;\n    }\n\n    private SIntermediateCatchEventInstanceBuilder createIntermediateCatchEventInstance(final long processDefinitionId,\n            final long rootContainerId,\n            final long parentContainerId, final SFlowElementsContainerType parentContainerType,\n            final SFlowNodeDefinition sFlowNodeDefinition,\n            final long rootProcessInstanceId, final long parentProcessInstanceId) {\n        final SIntermediateCatchEventDefinition intermediateCatchEvent = (SIntermediateCatchEventDefinition) sFlowNodeDefinition;\n        final SIntermediateCatchEventInstanceBuilder builder = BuilderFactory\n                .get(SIntermediateCatchEventInstanceBuilderFactory.class)\n                .createNewIntermediateCatchEventInstance(intermediateCatchEvent.getName(),\n                        intermediateCatchEvent.getId(), rootContainerId, parentContainerId,\n                        processDefinitionId, rootProcessInstanceId, parentProcessInstanceId);\n        updateFlowNodeInstance(parentContainerId, parentContainerType, builder);\n        return builder;\n    }\n\n    private SBoundaryEventInstanceBuilder createBoundaryEventInstance(final long processDefinitionId,\n            final long rootContainerId, final long parentContainerId,\n            final SFlowElementsContainerType parentContainerType, final SBoundaryEventDefinition boundaryEvent,\n            final long rootProcessInstanceId,\n            final long parentProcessInstanceId, final long activityInstanceId) {\n        final SBoundaryEventInstanceBuilder builder = BuilderFactory.get(SBoundaryEventInstanceBuilderFactory.class)\n                .createNewBoundaryEventInstance(\n                        boundaryEvent.getName(), boundaryEvent.isInterrupting(), boundaryEvent.getId(), rootContainerId,\n                        parentContainerId, processDefinitionId,\n                        rootProcessInstanceId, parentProcessInstanceId, activityInstanceId);\n        updateFlowNodeInstance(parentContainerId, parentContainerType, builder);\n        return builder;\n    }\n\n    private SGatewayInstanceBuilder createGatewayInstance(final long processDefinitionId, final long rootContainerId,\n            final long parentContainerId,\n            final SFlowElementsContainerType parentContainerType, final SFlowNodeDefinition sFlowNodeDefinition,\n            final long rootProcessInstanceId,\n            final long parentProcessInstanceId) {\n        final SGatewayInstanceBuilder builder = BuilderFactory.get(SGatewayInstanceBuilderFactory.class)\n                .createNewInstance(sFlowNodeDefinition.getName(),\n                        sFlowNodeDefinition.getId(), rootContainerId, parentContainerId,\n                        ((SGatewayDefinition) sFlowNodeDefinition).getGatewayType(),\n                        processDefinitionId, rootProcessInstanceId, parentProcessInstanceId);\n        updateFlowNodeInstance(parentContainerId, parentContainerType, builder);\n        return builder;\n    }\n\n    protected SEndEventInstanceBuilder createEndEventInstance(final long processDefinitionId,\n            final long rootContainerId, final long parentContainerId,\n            final SFlowNodeDefinition sFlowNodeDefinition, final long rootProcessInstanceId,\n            final long parentProcessInstanceId) {\n        final SEndEventDefinition endEventDef = (SEndEventDefinition) sFlowNodeDefinition;\n        return BuilderFactory.get(SEndEventInstanceBuilderFactory.class).createNewEndEventInstance(\n                endEventDef.getName(), endEventDef.getId(), rootContainerId,\n                parentContainerId,\n                processDefinitionId, rootProcessInstanceId, parentProcessInstanceId);\n    }\n\n    private SAutomaticTaskInstanceBuilder createAutomaticTaskInstance(final long processDefinitionId,\n            final long rootContainerId, final long parentContainerId,\n            final SFlowElementsContainerType parentContainerType, final SFlowNodeDefinition sFlowNodeDefinition,\n            final long parentProcessInstanceId) {\n        final SAutomaticTaskInstanceBuilder builder = BuilderFactory.get(SAutomaticTaskInstanceBuilderFactory.class)\n                .createNewAutomaticTaskInstance(\n                        sFlowNodeDefinition.getName(), sFlowNodeDefinition.getId(), rootContainerId, parentContainerId,\n                        processDefinitionId, rootContainerId,\n                        parentProcessInstanceId);\n        updateActivityInstance(parentContainerId, parentContainerType, sFlowNodeDefinition, builder);\n        return builder;\n    }\n\n    private SFlowNodeInstanceBuilder createReceiveTaskInstance(final long processDefinitionId,\n            final long rootContainerId, final long parentContainerId,\n            final SFlowElementsContainerType parentContainerType, final SFlowNodeDefinition sFlowNodeDefinition,\n            final long parentProcessInstanceId) {\n        final SReceiveTaskInstanceBuilder builder = BuilderFactory.get(SReceiveTaskInstanceBuilderFactory.class)\n                .createNewReceiveTaskInstance(\n                        sFlowNodeDefinition.getName(), sFlowNodeDefinition.getId(), rootContainerId, parentContainerId,\n                        processDefinitionId, rootContainerId,\n                        parentProcessInstanceId);\n        updateActivityInstance(parentContainerId, parentContainerType, sFlowNodeDefinition, builder);\n        return builder;\n    }\n\n    private SFlowNodeInstanceBuilder createSendTaskInstance(final long processDefinitionId, final long rootContainerId,\n            final long parentContainerId,\n            final SFlowElementsContainerType parentContainerType, final SFlowNodeDefinition sFlowNodeDefinition,\n            final long parentProcessInstanceId) {\n        final SSendTaskInstanceBuilder builder = BuilderFactory.get(SSendTaskInstanceBuilderFactory.class)\n                .createNewSendTaskInstance(\n                        sFlowNodeDefinition.getName(),\n                        sFlowNodeDefinition.getId(), rootContainerId, parentContainerId, processDefinitionId,\n                        rootContainerId, parentProcessInstanceId);\n        updateActivityInstance(parentContainerId, parentContainerType, sFlowNodeDefinition, builder);\n        return builder;\n    }\n\n    private void updateActivityInstance(final long parentContainerId,\n            final SFlowElementsContainerType parentContainerType,\n            final SFlowNodeDefinition sFlowNodeDefinition, final SActivityInstanceBuilder builder) {\n        updateFlowNodeInstance(parentContainerId, parentContainerType, builder);\n        builder.setDescription(sFlowNodeDefinition.getDescription());\n    }\n\n    private void updateFlowNodeInstance(final long parentContainerId,\n            final SFlowElementsContainerType parentContainerType,\n            final SFlowNodeInstanceBuilder builder) {\n        long logicalGroup3;\n        if (SFlowElementsContainerType.FLOWNODE.equals(parentContainerType)) {\n            logicalGroup3 = parentContainerId;\n        } else {\n            logicalGroup3 = 0;\n        }\n        builder.setParentActivityInstanceId(logicalGroup3);\n    }\n\n    private SMultiInstanceActivityInstanceBuilder createMultiInstanceActivityInstance(final long processDefinitionId,\n            final long rootContainerId,\n            final long parentContainerId, final long rootProcessInstanceId, final long parentProcessInstanceId,\n            final SActivityDefinition activityDefinition,\n            final SMultiInstanceLoopCharacteristics loopCharacteristics) {\n        final SMultiInstanceActivityInstanceBuilder builder = BuilderFactory\n                .get(SMultiInstanceActivityInstanceBuilderFactory.class)\n                .createNewOuterTaskInstance(\n                        activityDefinition.getName(), activityDefinition.getId(), rootContainerId, parentContainerId,\n                        processDefinitionId,\n                        rootProcessInstanceId,\n                        parentProcessInstanceId, loopCharacteristics.isSequential());\n        builder.setLoopDataInputRef(loopCharacteristics.getLoopDataInputRef());\n        builder.setLoopDataOutputRef(loopCharacteristics.getLoopDataOutputRef());\n        builder.setDataInputItemRef(loopCharacteristics.getDataInputItemRef());\n        builder.setDataOutputItemRef(loopCharacteristics.getDataOutputItemRef());\n        return builder;\n    }\n\n    public SLoopActivityInstanceBuilder createLoopActivityInstance(final long processDefinitionId,\n            final long rootContainerId, final long parentContainerId,\n            final long rootProcessInstanceId, final long parentProcessInstanceId,\n            final SActivityDefinition activityDefinition) {\n        return BuilderFactory.get(SLoopActivityInstanceBuilderFactory.class).createNewOuterTaskInstance(\n                activityDefinition.getName(), activityDefinition.getId(), rootContainerId, parentContainerId,\n                processDefinitionId, rootProcessInstanceId,\n                parentProcessInstanceId);\n    }\n\n    private SHumanTaskInstanceBuilder createHumanTaskInstance(final long processDefinitionId,\n            final long rootContainerId, final long parentContainerId,\n            final SFlowNodeDefinition sFlowNodeDefinition, final long rootProcessInstanceId,\n            final long parentProcessInstanceId)\n            throws SActorNotFoundException {\n        final SHumanTaskDefinition humanTaskDefinition = (SHumanTaskDefinition) sFlowNodeDefinition;\n        final String actorName = humanTaskDefinition.getActorName();\n\n        final SActor actor = getActor(processDefinitionId, actorName);\n        final SHumanTaskInstanceBuilder builder = BuilderFactory.get(SUserTaskInstanceBuilderFactory.class)\n                .createNewUserTaskInstance(\n                        humanTaskDefinition.getName(), humanTaskDefinition.getId(), rootContainerId, parentContainerId,\n                        actor.getId(), processDefinitionId,\n                        rootProcessInstanceId, parentProcessInstanceId);\n        fillHumanTask(humanTaskDefinition, builder);\n        return builder;\n    }\n\n    private void fillHumanTask(final SHumanTaskDefinition humanTaskDefinition,\n            final SHumanTaskInstanceBuilder builder) {\n        // Creation date:\n        builder.setReachedStateDate(System.currentTimeMillis());\n        final String priority = humanTaskDefinition.getPriority();\n        if (priority != null) {\n            // FIXME: use enum STaskPriority in client definition model:\n            final STaskPriority sPriority = STaskPriority.valueOf(priority);\n            builder.setPriority(sPriority);\n        }\n    }\n\n    public void createConnectorInstances(final PersistentObject container,\n            final List<SConnectorDefinition> connectors,\n            final String containerType)\n            throws SBonitaException {\n        final List<SConnectorInstance> connectorInstances = new ArrayList<>(connectors.size());\n        int executionOrder = 0;\n        for (final SConnectorDefinition sConnectorDefinition : connectors) {\n            final SConnectorInstance connectorInstance = createConnectorInstanceObject(container, containerType,\n                    sConnectorDefinition, executionOrder++);\n            connectorInstances.add(connectorInstance);\n        }\n        for (final SConnectorInstance connectorInstance : connectorInstances) {\n            connectorInstanceService.createConnectorInstance(connectorInstance);\n        }\n    }\n\n    SConnectorInstance createConnectorInstanceObject(PersistentObject container, String containerType,\n            SConnectorDefinition sConnectorDefinition,\n            int executionOrder) {\n        return SConnectorInstance.builder().name(sConnectorDefinition.getName())\n                .containerId(container.getId())\n                .containerType(containerType)\n                .connectorId(sConnectorDefinition.getConnectorId())\n                .version(sConnectorDefinition.getVersion())\n                .activationEvent(sConnectorDefinition.getActivationEvent())\n                .state(ConnectorState.TO_BE_EXECUTED.name())\n                .executionOrder(executionOrder)\n                .build();\n    }\n\n    public void createDataInstances(final SProcessInstance processInstance,\n            final SFlowElementContainerDefinition processContainer,\n            final SProcessDefinition processDefinition, final SExpressionContext expressionContext,\n            final List<SOperation> operations,\n            final Map<String, Object> context, SExpressionContext expressionContextToEvaluateOperations)\n            throws SDataInstanceNotWellFormedException, SExpressionTypeUnknownException, SExpressionEvaluationException,\n            SExpressionDependencyMissingException, SInvalidExpressionException, SDataInstanceException,\n            SFlowNodeNotFoundException, SFlowNodeReadException {\n        final List<SDataDefinition> sDataDefinitions = processContainer.getDataDefinitions();\n        final List<SDataInstance> sDataInstances = new ArrayList<>(sDataDefinitions.size());\n        for (final SDataDefinition sDataDefinition : sDataDefinitions) {\n            sDataInstances\n                    .add(createDataInstance(processInstance, expressionContext, operations, context,\n                            expressionContextToEvaluateOperations, sDataDefinition));\n\n        }\n        if (hasLocalOrInheritedData(processDefinition, processContainer)) {\n            // we create here only normal data, not transient because there is no transient on process\n            createDataForProcess(sDataInstances);\n        }\n        debugLogVariableInitialized(processInstance, processDefinition);\n    }\n\n    SDataInstance createDataInstance(SProcessInstance processInstance, SExpressionContext expressionContext,\n            List<SOperation> operations,\n            Map<String, Object> context, SExpressionContext expressionContextToEvaluateOperations,\n            SDataDefinition sDataDefinition)\n            throws SExpressionTypeUnknownException, SExpressionEvaluationException,\n            SExpressionDependencyMissingException, SInvalidExpressionException,\n            SDataInstanceNotWellFormedException {\n        SExpression expression;\n        SExpressionContext currentExpressionContext;\n        final SOperation operation;\n        if ((operation = getOperationToSetData(sDataDefinition.getName(), operations)) != null) {\n            expression = operation.getRightOperand();\n            currentExpressionContext = expressionContextToEvaluateOperations != null\n                    ? expressionContextToEvaluateOperations : expressionContext;\n            currentExpressionContext.setInputValues(context);\n            operations.remove(operation);\n        } else {\n            expression = sDataDefinition.getDefaultValueExpression();\n            currentExpressionContext = expressionContext;\n        }\n        return createDataInstanceObject(processInstance, sDataDefinition,\n                evaluateExpression(sDataDefinition, expression, currentExpressionContext));\n    }\n\n    private Serializable evaluateExpression(SDataDefinition sDataDefinition, SExpression expression,\n            SExpressionContext currentExpressionContext)\n            throws SExpressionTypeUnknownException, SExpressionEvaluationException,\n            SExpressionDependencyMissingException, SInvalidExpressionException {\n        if (expression != null) {\n            return (Serializable) expressionResolverService.evaluate(expression, currentExpressionContext);\n        } else if (sDataDefinition.isTransientData()) {\n            warningWhenTransientDataWithNullValue();\n        }\n        return null;\n    }\n\n    void debugLogVariableInitialized(SProcessInstance processInstance, SProcessDefinition processDefinition) {\n        if (log.isDebugEnabled()) {\n            final StringBuilder stb = new StringBuilder();\n            stb.append(\"Initialized variables for process instance [name: <\");\n            stb.append(processInstance.getName());\n            stb.append(\">, version: <\");\n            stb.append(processDefinition.getVersion());\n            stb.append(\">, id: <\");\n            stb.append(processInstance.getId());\n            stb.append(\">, root process instance: <\");\n            stb.append(processInstance.getRootProcessInstanceId());\n            stb.append(\">, process definition: <\");\n            stb.append(processInstance.getProcessDefinitionId());\n            if (processInstance.getCallerId() > 0) {\n                stb.append(\">, caller id: <\");\n                stb.append(processInstance.getCallerId());\n                stb.append(\">, caller type: <\");\n                stb.append(processInstance.getCallerType());\n            }\n            stb.append(\">]\");\n            log.debug(stb.toString());\n        }\n    }\n\n    SDataInstance createDataInstanceObject(SProcessInstance processInstance, SDataDefinition sDataDefinition,\n            Serializable dataValue)\n            throws SDataInstanceNotWellFormedException {\n        try {\n            return SDataInstanceBuilder.createNewInstance(sDataDefinition, processInstance.getId(),\n                    DataInstanceContainer.PROCESS_INSTANCE.name(), dataValue);\n        } catch (final ClassCastException e) {\n            throw new SDataInstanceNotWellFormedException(\n                    \"Trying to set variable \\\"\" + sDataDefinition.getName() + \"\\\" with incompatible type: \"\n                            + e.getMessage());\n        }\n    }\n\n    void warningWhenTransientDataWithNullValue() {\n        log.warn(\"Creating a transient data instance with a null expression is not a good practice.\");\n    }\n\n    private void createDataForProcess(final List<SDataInstance> sDataInstances)\n            throws SDataInstanceException {\n        if (!sDataInstances.isEmpty()) {\n            for (final SDataInstance sDataInstance : sDataInstances) {\n                dataInstanceService.createDataInstance(sDataInstance);\n            }\n        }\n    }\n\n    private boolean hasLocalOrInheritedData(final SProcessDefinition processDefinition,\n            final SFlowElementContainerDefinition processContainer) {\n        // processContainer is different of processDefinition.getProcessContainer() if it's a sub-process\n        return !processContainer.getDataDefinitions().isEmpty()\n                || !processDefinition.getProcessContainer().getDataDefinitions().isEmpty();\n    }\n\n    SOperation getOperationToSetData(final String dataName, final List<SOperation> operations) {\n        SOperation dataOperation = null;\n        final Iterator<SOperation> iterator = operations.iterator();\n        boolean found = false;\n        while (iterator.hasNext() && !found) {\n            final SOperation operation = iterator.next();\n            if (SOperatorType.ASSIGNMENT.equals(operation.getType())\n                    && SLeftOperand.TYPE_DATA.equals(operation.getLeftOperand().getType())\n                    && dataName.equals(operation.getLeftOperand().getName())) {\n                found = true;\n                dataOperation = operation;\n            }\n        }\n        return dataOperation;\n    }\n\n    private void createDataInstances(final List<SDataDefinition> dataDefinitions, final long containerId,\n            final DataInstanceContainer containerType,\n            final SExpressionContext expressionContext, final String loopDataInputRef, final int index,\n            final String dataInputRef, final long parentContainerId)\n            throws SDataInstanceException, SExpressionException {\n        for (final SDataDefinition dataDefinition : dataDefinitions) {\n            Serializable dataValue = null;\n            if (dataDefinition.getName().equals(dataInputRef)) {\n                final SDataInstance dataInstance = dataInstanceService.getDataInstance(loopDataInputRef,\n                        parentContainerId,\n                        DataInstanceContainer.ACTIVITY_INSTANCE.name(), parentContainerResolver);// in a multi instance\n                if (dataInstance != null) {\n                    try {\n                        final List<Serializable> list = (List<Serializable>) dataInstance.getValue();\n                        if (!list.isEmpty()) {\n                            dataValue = list.get(index);\n                        }\n                    } catch (final ClassCastException e) {\n                        throw new SDataInstanceReadException(\"LoopDataInput ref named \" + loopDataInputRef + \" in \"\n                                + containerId + \" \" + containerType\n                                + \" is not a list or the value is not serializable.\");\n                    }\n                } else {\n                    throw new SDataInstanceReadException(\n                            \"LoopDataInput ref named \" + loopDataInputRef + \" is not visible for \" + containerId + \" \"\n                                    + containerType);\n                }\n            } else {\n                final SExpression defaultValueExpression = dataDefinition.getDefaultValueExpression();\n                if (defaultValueExpression != null) {\n                    dataValue = (Serializable) expressionResolverService\n                            .evaluate(dataDefinition.getDefaultValueExpression(), expressionContext);\n                } else if (dataDefinition.isTransientData()) {\n                    warningWhenTransientDataWithNullValue();\n                }\n            }\n            final SDataInstance dataInstance;\n            try {\n                dataInstance = buildDataInstance(dataDefinition, containerId, containerType, dataValue);\n            } catch (final SDataInstanceNotWellFormedException e) {\n                throw new SDataInstanceReadException(e);\n            }\n            if (dataInstance.isTransientData()) {\n                transientDataService.createDataInstance(dataInstance);\n            } else {\n                dataInstanceService.createDataInstance(dataInstance);\n            }\n        }\n    }\n\n    public void createDataInstances(final List<SDataDefinition> dataDefinitions, final long containerId,\n            final DataInstanceContainer containerType,\n            final SExpressionContext expressionContext) throws SDataInstanceException, SExpressionException {\n        createDataInstances(dataDefinitions, containerId, containerType, expressionContext, null, -1, null, -1);\n    }\n\n    private SDataInstance buildDataInstance(final SDataDefinition dataDefinition, final long dataContainerId,\n            final DataInstanceContainer dataContainerType,\n            final Serializable dataValue) throws SDataInstanceNotWellFormedException {\n        return SDataInstanceBuilder.createNewInstance(dataDefinition, dataContainerId, dataContainerType.name(),\n                dataValue);\n    }\n\n    public boolean createDataInstances(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException {\n        final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer();\n        final SActivityDefinition activityDefinition = (SActivityDefinition) processContainer\n                .getFlowNode(flowNodeInstance.getFlowNodeDefinitionId());\n        if (activityDefinition != null) {// can be null if the activity was added in runtime\n            return createDataInstances(activityDefinition, flowNodeInstance,\n                    new SExpressionContext(flowNodeInstance.getId(), DataInstanceContainer.ACTIVITY_INSTANCE.name(),\n                            processDefinition.getId()));\n        }\n        return false;\n    }\n\n    private boolean createDataInstances(final SActivityDefinition activityDefinition,\n            final SFlowNodeInstance flowNodeInstance, final SExpressionContext expressionContext)\n            throws SActivityStateExecutionException {\n        final List<SDataDefinition> sDataDefinitions = activityDefinition.getSDataDefinitions();\n        final SLoopCharacteristics loopCharacteristics = activityDefinition.getLoopCharacteristics();\n\n        if (loopCharacteristics instanceof SMultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics\n                && (multiInstanceLoopCharacteristics.getDataInputItemRef() != null\n                        || multiInstanceLoopCharacteristics.getDataOutputItemRef() != null)) {\n            try {\n                createDataInstancesForMultiInstance(activityDefinition, flowNodeInstance, expressionContext);\n            } catch (final SBonitaException e) {\n                throw new SActivityStateExecutionException(\n                        \"Failed to initialize multi instance variables of \" + flowNodeInstance,\n                        ScopedException.ITERATION, e);\n            }\n        } else {\n            try {\n                createDataInstances(sDataDefinitions, flowNodeInstance.getId(), DataInstanceContainer.ACTIVITY_INSTANCE,\n                        expressionContext);\n            } catch (final SBonitaException e) {\n                throw new SActivityStateExecutionException(\"Failed to initialize variables of \" + flowNodeInstance,\n                        ScopedException.DATA, e);\n            }\n        }\n        if (!sDataDefinitions.isEmpty() && log.isDebugEnabled()) {\n            final String message = \"Initialized variables for flow node\"\n                    + LogMessageBuilder.buildFlowNodeContextMessage(flowNodeInstance);\n            log.debug(message);\n        }\n        return !sDataDefinitions.isEmpty();\n\n    }\n\n    protected void createDataInstancesForMultiInstance(final SActivityDefinition activityDefinition,\n            final SFlowNodeInstance flowNodeInstance,\n            final SExpressionContext expressionContext) throws SDataInstanceException, SExpressionException {\n        final SLoopCharacteristics loopCharacteristics = activityDefinition.getLoopCharacteristics();\n        final SMultiInstanceLoopCharacteristics miLoop = (SMultiInstanceLoopCharacteristics) loopCharacteristics;\n        createBusinessDataInstancesForMultiInstance(activityDefinition, flowNodeInstance, miLoop);\n        createDataInstances(activityDefinition.getSDataDefinitions(), flowNodeInstance.getId(),\n                DataInstanceContainer.ACTIVITY_INSTANCE, expressionContext,\n                miLoop.getLoopDataInputRef(), flowNodeInstance.getLoopCounter(), miLoop.getDataInputItemRef(),\n                flowNodeInstance.getParentContainerId());\n    }\n\n    private void createBusinessDataInstancesForMultiInstance(SActivityDefinition activityDefinition,\n            SFlowNodeInstance flowNodeInstance,\n            SMultiInstanceLoopCharacteristics miLoop) throws SDataInstanceException {\n        final SBusinessDataDefinition outputBusinessData = activityDefinition\n                .getBusinessDataDefinition(miLoop.getDataOutputItemRef());\n        final SRefBusinessDataInstanceBuilderFactory instanceFactory = BuilderFactory\n                .get(SRefBusinessDataInstanceBuilderFactory.class);\n        if (outputBusinessData != null) {\n            final SRefBusinessDataInstance outputRefInstance = instanceFactory\n                    .createNewInstanceForFlowNode(outputBusinessData.getName(),\n                            flowNodeInstance.getId(), null, outputBusinessData.getClassName())\n                    .done();\n            addRefBusinessData(outputRefInstance);\n        }\n        final SBusinessDataDefinition inputBusinessData = activityDefinition\n                .getBusinessDataDefinition(miLoop.getDataInputItemRef());\n        if (inputBusinessData != null) {\n            try {\n                final SProcessMultiRefBusinessDataInstance loopDataRefInstance = (SProcessMultiRefBusinessDataInstance) refBusinessDataService\n                        .getRefBusinessDataInstance(\n                                miLoop.getLoopDataInputRef(),\n                                refBusinessDataRetriever.getProcessInstanceIdThatCanContainBusinessData(\n                                        flowNodeInstance.getParentProcessInstanceId()));\n                final List<Long> dataIds = loopDataRefInstance.getDataIds();\n                final SRefBusinessDataInstance inputRefInstance = instanceFactory\n                        .createNewInstanceForFlowNode(inputBusinessData.getName(),\n                                flowNodeInstance.getId(), dataIds.get(flowNodeInstance.getLoopCounter()),\n                                inputBusinessData.getClassName())\n                        .done();\n                addRefBusinessData(inputRefInstance);\n            } catch (final SRefBusinessDataInstanceNotFoundException | SBonitaReadException\n                    | SProcessInstanceReadException | SProcessInstanceNotFoundException | SFlowNodeReadException\n                    | SFlowNodeNotFoundException e) {\n                throw new SDataInstanceException(e);\n            }\n        }\n    }\n\n    private void addRefBusinessData(final SRefBusinessDataInstance instance) throws SDataInstanceException {\n        try {\n            refBusinessDataService.addRefBusinessDataInstance(instance);\n        } catch (final SRefBusinessDataInstanceCreationException e) {\n            throw new SDataInstanceException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/bpm/process/impl/ProcessInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.process.impl;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.impl.internal.ProcessInstanceImpl;\n\n/**\n * @author Emmanuel Duchastenier\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class ProcessInstanceBuilder {\n\n    protected final ProcessInstanceImpl processInstance;\n\n    private ProcessInstanceBuilder() {\n        this.processInstance = null;\n    }\n\n    private ProcessInstanceBuilder(final ProcessInstanceImpl processInstance) {\n        super();\n        this.processInstance = processInstance;\n    }\n\n    public ProcessInstance done() {\n        return processInstance;\n    }\n\n    public ProcessInstanceBuilder createNewInstance(final String name) {\n        final ProcessInstanceImpl processInstance = new ProcessInstanceImpl(name);\n        return new ProcessInstanceBuilder(processInstance);\n    }\n\n    public ProcessInstanceBuilder setState(final String state) {\n        processInstance.setState(state);\n        return this;\n    }\n\n    public ProcessInstanceBuilder setStartDate(final long startDate) {\n        processInstance.setStartDate(new Date(startDate));\n        return this;\n    }\n\n    public ProcessInstanceBuilder setStartedBy(final long startedBy) {\n        processInstance.setStartedBy(startedBy);\n        return this;\n    }\n\n    public ProcessInstanceBuilder setStartedBySubstitute(final long startedBySubstitute) {\n        processInstance.setStartedBySubstitute(startedBySubstitute);\n        return this;\n    }\n\n    public ProcessInstanceBuilder setEndDate(final long endDate) {\n        processInstance.setEndDate(new Date(endDate));\n        return this;\n    }\n\n    public ProcessInstanceBuilder setLastUpdate(final long lastUpdate) {\n        processInstance.setLastUpdate(new Date(lastUpdate));\n        return this;\n    }\n\n    public ProcessInstanceBuilder setProcessDefinitionId(final long processDefinitionId) {\n        processInstance.setProcessDefinitionId(processDefinitionId);\n        return this;\n    }\n\n    public ProcessInstanceBuilder setDescription(final String description) {\n        processInstance.setDescription(description);\n        return this;\n    }\n\n    public ProcessInstanceBuilder setId(final long id) {\n        processInstance.setId(id);\n        return this;\n    }\n\n    public ProcessInstanceBuilder setRootProcessInstanceId(final long rootProcessInstanceId) {\n        processInstance.setRootProcessInstanceId(rootProcessInstanceId);\n        return this;\n    }\n\n    public ProcessInstanceBuilder setCallerId(final long callerId) {\n        processInstance.setCallerId(callerId);\n        return this;\n    }\n\n    public ProcessInstanceBuilder setStringIndex1(final String stringIndex1) {\n        processInstance.setStringIndex1(stringIndex1);\n        return this;\n    }\n\n    public ProcessInstanceBuilder setStringIndex2(final String stringIndex2) {\n        processInstance.setStringIndex2(stringIndex2);\n        return this;\n    }\n\n    public ProcessInstanceBuilder setStringIndex3(final String stringIndex3) {\n        processInstance.setStringIndex3(stringIndex3);\n        return this;\n    }\n\n    public ProcessInstanceBuilder setStringIndex4(final String stringIndex4) {\n        processInstance.setStringIndex4(stringIndex4);\n        return this;\n    }\n\n    public ProcessInstanceBuilder setStringIndex5(final String stringIndex5) {\n        processInstance.setStringIndex5(stringIndex5);\n        return this;\n    }\n\n    public void setStringIndexLabel(final int index, final String stringIndexLabel) {\n        processInstance.setStringIndexLabel(index, stringIndexLabel);\n    }\n\n    public static ProcessInstanceBuilder getInstance() {\n        return new ProcessInstanceBuilder(null);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/converter/ApplicationMenuToNodeConverter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.converter;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.model.SApplicationMenu;\nimport org.bonitasoft.engine.business.application.xml.ApplicationMenuNode;\nimport org.bonitasoft.engine.business.application.xml.ApplicationNode;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class ApplicationMenuToNodeConverter {\n\n    private final ApplicationService applicationService;\n\n    public ApplicationMenuToNodeConverter(final ApplicationService applicationService) {\n        this.applicationService = applicationService;\n    }\n\n    /**\n     * RECURSIVELY convert menu (and sub-menus) to xml node.\n     *\n     * @param menu the menu to convert\n     * @return the converted menu.\n     * @throws SObjectNotFoundException if the referenced menu does not exist.\n     * @throws SBonitaReadException if the referenced menu cannot be retrieved.\n     */\n    protected ApplicationMenuNode toMenu(final SApplicationMenu menu)\n            throws SBonitaReadException, SObjectNotFoundException {\n        if (menu == null) {\n            throw new IllegalArgumentException(\"Application menu to convert cannot be null\");\n        }\n        final ApplicationMenuNode menuNode = new ApplicationMenuNode();\n        // applicationPage attribute in the menu is the token in the referenced application page:\n        final Long applicationPageId = menu.getApplicationPageId();\n        if (applicationPageId != null) {\n            menuNode.setApplicationPage(applicationService.getApplicationPage(applicationPageId).getToken());\n        }\n        menuNode.setDisplayName(menu.getDisplayName());\n        return menuNode;\n    }\n\n    /**\n     * @param applicationId application ID.\n     * @param parentMenuId Id of the parent menu, use <code>null</code> for explicit no parent.\n     * @param startIndex pagination start index.\n     * @param maxResults pagination max results to retrieve.\n     * @return the newly built {@link QueryOptions}\n     */\n    protected QueryOptions buildApplicationMenusQueryOptions(final long applicationId, final Long parentMenuId,\n            final int startIndex, final int maxResults) {\n        final List<OrderByOption> orderByOptions = Collections\n                .singletonList(new OrderByOption(SApplicationMenu.class, SApplicationMenu.INDEX, OrderByType.ASC));\n\n        final List<FilterOption> filters = Arrays.asList(\n                new FilterOption(SApplicationMenu.class, SApplicationMenu.APPLICAITON_ID, applicationId),\n                new FilterOption(SApplicationMenu.class, SApplicationMenu.PARENT_ID, parentMenuId));\n\n        return new QueryOptions(startIndex, maxResults, orderByOptions, filters, null);\n    }\n\n    /**\n     * RECURSIVELY add menu elements (and sub-menus) to xml node, from menu identified by parentMenuId.\n     *\n     * @param applicationId ID of the application.\n     * @param parentMenuId Id of the parent menu, use <code>null</code> for explicit no parent.\n     * @param applicationNode\n     * @param menuNode the menu node to add new menu elements to. Pass null if new menu node must be added to root\n     *        application node.\n     * @throws SBonitaReadException\n     * @throws SObjectNotFoundException\n     */\n    public void addMenusToApplicationNode(final long applicationId, final Long parentMenuId,\n            final ApplicationNode applicationNode,\n            final ApplicationMenuNode menuNode)\n            throws SBonitaReadException, SObjectNotFoundException {\n        int startIndex = 0;\n        final int maxResults = 50;\n        List<SApplicationMenu> menus;\n        do {\n            menus = applicationService.searchApplicationMenus(\n                    buildApplicationMenusQueryOptions(applicationId, parentMenuId, startIndex, maxResults));\n            for (final SApplicationMenu menu : menus) {\n                // Add converted current menu...\n                final ApplicationMenuNode menuNode2 = toMenu(menu);\n                if (menuNode == null) {\n                    applicationNode.addApplicationMenu(menuNode2);\n                } else {\n                    menuNode.addApplicationMenu(menuNode2);\n                }\n                // ... and recursively add sub-menu:\n                addMenusToApplicationNode(applicationId, menu.getId(), applicationNode, menuNode2);\n            }\n            startIndex += maxResults;\n        } while (menus.size() == maxResults);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/converter/ApplicationPageToNodeConverter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.converter;\n\nimport org.bonitasoft.engine.business.application.model.SApplicationPage;\nimport org.bonitasoft.engine.business.application.xml.ApplicationPageNode;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.page.PageService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class ApplicationPageToNodeConverter {\n\n    private final PageService pageService;\n\n    public ApplicationPageToNodeConverter(final PageService pageService) {\n        this.pageService = pageService;\n    }\n\n    /**\n     * @param page the application page to convert to xml node.\n     * @return the converted page.\n     * @throws SObjectNotFoundException if the referenced page does not exist.\n     * @throws SBonitaReadException if the referenced page cannot be retrieved.\n     */\n    public ApplicationPageNode toPage(final SApplicationPage page)\n            throws SBonitaReadException, SObjectNotFoundException {\n        if (page == null) {\n            throw new IllegalArgumentException(\"Application page to convert cannot be null\");\n        }\n        final ApplicationPageNode pageNode = new ApplicationPageNode();\n        pageNode.setToken(page.getToken());\n        pageNode.setCustomPage(pageService.getPage(page.getPageId()).getName());\n        return pageNode;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/converter/ApplicationToNodeConverter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.converter;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.model.SApplication;\nimport org.bonitasoft.engine.business.application.model.SApplicationPage;\nimport org.bonitasoft.engine.business.application.xml.AbstractApplicationNode;\nimport org.bonitasoft.engine.business.application.xml.ApplicationLinkNode;\nimport org.bonitasoft.engine.business.application.xml.ApplicationNode;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.exception.ExportException;\nimport org.bonitasoft.engine.page.PageService;\nimport org.bonitasoft.engine.page.SPage;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.profile.ProfileService;\nimport org.bonitasoft.engine.profile.exception.profile.SProfileNotFoundException;\nimport org.bonitasoft.engine.profile.model.SProfile;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ApplicationToNodeConverter {\n\n    private final ProfileService profileService;\n    private final ApplicationService applicationService;\n    private final ApplicationPageToNodeConverter applicationPageToNodeConverter;\n    private final ApplicationMenuToNodeConverter applicationMenuToNodeConverter;\n    private final PageService pageService;\n\n    public ApplicationToNodeConverter(final ProfileService profileService, final ApplicationService applicationService,\n            final ApplicationPageToNodeConverter applicationPageToNodeConverter,\n            final ApplicationMenuToNodeConverter applicationMenuToNodeConverter,\n            final PageService pageService) {\n        this.profileService = profileService;\n        this.applicationService = applicationService;\n        this.applicationPageToNodeConverter = applicationPageToNodeConverter;\n        this.applicationMenuToNodeConverter = applicationMenuToNodeConverter;\n        this.pageService = pageService;\n    }\n\n    public AbstractApplicationNode toNode(final SApplication application) throws ExportException {\n        try {\n            final AbstractApplicationNode applicationNode;\n            if (application.isLink()) {\n                applicationNode = new ApplicationLinkNode();\n            } else {\n                ApplicationNode legacyApplicationNode = new ApplicationNode();\n                setLayout(application, legacyApplicationNode);\n                setTheme(application, legacyApplicationNode);\n                setHomePage(application, legacyApplicationNode);\n                setPages(application.getId(), legacyApplicationNode);\n                applicationMenuToNodeConverter.addMenusToApplicationNode(application.getId(), null,\n                        legacyApplicationNode, null);\n                applicationNode = legacyApplicationNode;\n            }\n            applicationNode.setToken(application.getToken());\n            applicationNode.setDisplayName(application.getDisplayName());\n            applicationNode.setVersion(application.getVersion());\n            applicationNode.setDescription(application.getDescription());\n            applicationNode.setState(application.getState());\n            applicationNode.setIconPath(application.getIconPath());\n            setProfile(application, applicationNode);\n            return applicationNode;\n        } catch (SBonitaException e) {\n            throw new ExportException(e);\n        }\n    }\n\n    private void setTheme(final SApplication application, final ApplicationNode applicationNode)\n            throws SBonitaReadException, SObjectNotFoundException {\n        if (application.getThemeId() != null) {\n            SPage page = pageService.getPage(application.getThemeId());\n            applicationNode.setTheme(page.getName());\n        }\n    }\n\n    private void setLayout(final SApplication application, final ApplicationNode applicationNode)\n            throws SBonitaReadException, SObjectNotFoundException {\n        if (application.getLayoutId() != null) {\n            SPage page = pageService.getPage(application.getLayoutId());\n            applicationNode.setLayout(page.getName());\n        }\n    }\n\n    private void setPages(final long applicationId, final ApplicationNode applicationNode)\n            throws SBonitaReadException, SObjectNotFoundException {\n        int startIndex = 0;\n        final int maxResults = 50;\n        List<SApplicationPage> pages;\n        do {\n            pages = applicationService\n                    .searchApplicationPages(buildApplicationPagesQueryOptions(applicationId, startIndex, maxResults));\n            for (final SApplicationPage page : pages) {\n                applicationNode.addApplicationPage(applicationPageToNodeConverter.toPage(page));\n            }\n            startIndex += maxResults;\n        } while (pages.size() == maxResults);\n    }\n\n    public QueryOptions buildApplicationPagesQueryOptions(final long applicationId, final int startIndex,\n            final int pageSize) {\n        final List<OrderByOption> orderByOptions = Collections\n                .singletonList(new OrderByOption(SApplicationPage.class, SApplicationPage.ID, OrderByType.ASC));\n        final List<FilterOption> filters = Collections.singletonList(\n                new FilterOption(SApplicationPage.class, SApplicationPage.APPLICATION_ID, applicationId));\n        return new QueryOptions(startIndex, pageSize, orderByOptions, filters, null);\n    }\n\n    private void setHomePage(final SApplication application, final ApplicationNode applicationNode)\n            throws SBonitaReadException, SObjectNotFoundException {\n        if (application.getHomePageId() != null) {\n            final SApplicationPage homePage = applicationService.getApplicationPage(application.getHomePageId());\n            applicationNode.setHomePage(homePage.getToken());\n        }\n    }\n\n    private void setProfile(final SApplication application, final AbstractApplicationNode applicationNode)\n            throws SProfileNotFoundException {\n        if (application.getProfileId() != null) {\n            final SProfile profile = profileService.getProfile(application.getProfileId());\n            applicationNode.setProfile(profile.getName());\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/converter/ApplicationsToNodeContainerConverter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.converter;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.business.application.model.SApplication;\nimport org.bonitasoft.engine.business.application.xml.ApplicationNodeContainer;\nimport org.bonitasoft.engine.exception.ExportException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ApplicationsToNodeContainerConverter {\n\n    private final ApplicationToNodeConverter applicationToNodeConverter;\n\n    public ApplicationsToNodeContainerConverter(final ApplicationToNodeConverter applicationToNodeConverter) {\n        this.applicationToNodeConverter = applicationToNodeConverter;\n    }\n\n    public ApplicationNodeContainer toNode(final List<SApplication> applications) throws ExportException {\n        final ApplicationNodeContainer container = new ApplicationNodeContainer();\n        for (final SApplication application : applications) {\n            container.addApplication(applicationToNodeConverter.toNode(application));\n        }\n        return container;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/converter/NodeToApplicationConverter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.converter;\n\nimport org.bonitasoft.engine.api.ImportError;\nimport org.bonitasoft.engine.api.ImportStatus;\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.InternalProfiles;\nimport org.bonitasoft.engine.business.application.importer.ImportResult;\nimport org.bonitasoft.engine.business.application.importer.validator.ApplicationImportValidator;\nimport org.bonitasoft.engine.business.application.model.SApplicationWithIcon;\nimport org.bonitasoft.engine.business.application.xml.AbstractApplicationNode;\nimport org.bonitasoft.engine.business.application.xml.ApplicationLinkNode;\nimport org.bonitasoft.engine.business.application.xml.ApplicationNode;\nimport org.bonitasoft.engine.exception.ImportException;\nimport org.bonitasoft.engine.page.PageService;\nimport org.bonitasoft.engine.page.SPage;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.profile.ProfileService;\nimport org.bonitasoft.engine.profile.exception.profile.SProfileNotFoundException;\nimport org.bonitasoft.engine.profile.model.SProfile;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Component\npublic class NodeToApplicationConverter {\n\n    private final ProfileService profileService;\n    private final PageService pageService;\n    private final ApplicationImportValidator validator;\n\n    public NodeToApplicationConverter(final ProfileService profileService, final PageService pageService,\n            final ApplicationImportValidator validator) {\n        this.profileService = profileService;\n        this.pageService = pageService;\n        this.validator = validator;\n    }\n\n    public ImportResult toSApplication(final AbstractApplicationNode applicationNode, byte[] iconContent,\n            String iconMimeType, final long createdBy, final boolean editable)\n            throws SBonitaReadException, ImportException {\n        String token = applicationNode.getToken();\n        validator.validate(token);\n\n        final ImportStatus importStatus = new ImportStatus(token);\n        final long currentDate = System.currentTimeMillis();\n        SApplicationWithIcon application = new SApplicationWithIcon();\n        application.setToken(token);\n        application.setDisplayName(applicationNode.getDisplayName());\n        application.setVersion(applicationNode.getVersion());\n        application.setCreationDate(currentDate);\n        application.setLastUpdateDate(currentDate);\n        application.setCreatedBy(createdBy);\n        application.setUpdatedBy(createdBy);\n        application.setIconPath(applicationNode.getIconPath());\n        application.setDescription(applicationNode.getDescription());\n        application.setState(applicationNode.getState());\n        application.setEditable(editable);\n        application.setLink(applicationNode instanceof ApplicationLinkNode);\n\n        if (applicationNode instanceof ApplicationNode legacy) {\n            application.setLayoutId(getLayoutId(getLayoutName(legacy), token, importStatus));\n            application.setThemeId(getThemeId(getThemeName(legacy), token, importStatus));\n        }\n        application.setIconContent(iconContent);\n        application.setIconMimeType(iconMimeType);\n\n        if (applicationNode.getProfile() != null) {\n            setProfile(applicationNode.getProfile(), application, importStatus);\n        }\n\n        return new ImportResult(application, importStatus);\n    }\n\n    private Long getLayoutId(final String layoutName, final String applicationToken, final ImportStatus importStatus)\n            throws SBonitaReadException, ImportException {\n        SPage layout = pageService.getPageByName(layoutName);\n        if (layout == null) {\n            return handleMissingLayout(layoutName, applicationToken, importStatus);\n        }\n        return layout.getId();\n    }\n\n    private Long getThemeId(final String themeName, final String applicationToken, final ImportStatus importStatus)\n            throws SBonitaReadException, ImportException {\n        SPage theme = pageService.getPageByName(themeName);\n        if (theme == null) {\n            return handleMissingTheme(themeName, applicationToken, importStatus);\n        }\n        return theme.getId();\n    }\n\n    protected Long handleMissingLayout(final String layoutName, final String applicationToken,\n            final ImportStatus importStatus) throws ImportException, SBonitaReadException {\n        throw new ImportException(\n                String.format(\"Unable to import application with token '%s' because the layout '%s' was not found.\",\n                        applicationToken, layoutName));\n    }\n\n    protected Long handleMissingTheme(final String themeName, final String applicationToken,\n            final ImportStatus importStatus) throws ImportException, SBonitaReadException {\n        throw new ImportException(\n                String.format(\"Unable to import application with token '%s' because the theme '%s' was not found.\",\n                        applicationToken, themeName));\n    }\n\n    private String getLayoutName(final ApplicationNode applicationNode) {\n        return applicationNode.getLayout() != null ? applicationNode.getLayout()\n                : ApplicationService.DEFAULT_LAYOUT_NAME;\n    }\n\n    private String getThemeName(final ApplicationNode applicationNode) {\n        return applicationNode.getTheme() != null ? applicationNode.getTheme() : ApplicationService.DEFAULT_THEME_NAME;\n    }\n\n    private void setProfile(String profileName, SApplicationWithIcon application, ImportStatus importStatus) {\n        InternalProfiles internalProfileByApplicationNodeProfile = InternalProfiles\n                .getInternalProfileByProfileName(profileName);\n        if (internalProfileByApplicationNodeProfile == null) {\n            try {\n                final SProfile profile = profileService.getProfileByName(profileName);\n                application.setProfileId(profile.getId());\n            } catch (final SProfileNotFoundException | SBonitaReadException e) {\n                importStatus.addError(new ImportError(profileName, ImportError.Type.PROFILE));\n            }\n        } else {\n            application.setInternalProfile(internalProfileByApplicationNodeProfile.getProfileName());\n            application.setProfileId(null);\n        }\n    }\n\n    protected PageService getPageService() {\n        return pageService;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/converter/NodeToApplicationMenuConverter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.converter;\n\nimport org.bonitasoft.engine.api.ImportError;\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.importer.ApplicationMenuImportResult;\nimport org.bonitasoft.engine.business.application.model.SApplicationMenu;\nimport org.bonitasoft.engine.business.application.model.SApplicationPage;\nimport org.bonitasoft.engine.business.application.model.SApplicationWithIcon;\nimport org.bonitasoft.engine.business.application.xml.ApplicationMenuNode;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@Component\npublic class NodeToApplicationMenuConverter {\n\n    private final ApplicationService applicationService;\n\n    public NodeToApplicationMenuConverter(final ApplicationService applicationService) {\n        this.applicationService = applicationService;\n    }\n\n    /**\n     * Convert an {@link ApplicationMenuNode} to\n     * {@link SApplicationMenu}\n     *\n     * @param applicationMenuNode the XML node to convert\n     * @param application the application where the menu will be attached to\n     * @param parentMenu the parent menu. Null if no parent\n     * @return the application where the menu will be attached to\n     * @throws SBonitaReadException\n     */\n    public ApplicationMenuImportResult toSApplicationMenu(ApplicationMenuNode applicationMenuNode,\n            SApplicationWithIcon application, SApplicationMenu parentMenu)\n            throws SBonitaReadException {\n        Long appPageId = null;\n        ImportError error = null;\n        if (applicationMenuNode.getApplicationPage() != null) {\n            try {\n                SApplicationPage applicationPage = applicationService.getApplicationPage(application.getToken(),\n                        applicationMenuNode.getApplicationPage());\n                appPageId = applicationPage.getId();\n            } catch (SObjectNotFoundException e) {\n                error = new ImportError(applicationMenuNode.getApplicationPage(), ImportError.Type.APPLICATION_PAGE);\n            }\n        }\n        int index = getIndex(parentMenu);\n        SApplicationMenu applicationMenu = buildApplicationMenu(applicationMenuNode, application, parentMenu, appPageId,\n                index);\n        return new ApplicationMenuImportResult(error, applicationMenu);\n    }\n\n    private int getIndex(final SApplicationMenu parentMenu) throws SBonitaReadException {\n        int index;\n        if (parentMenu == null) {\n            index = applicationService.getNextAvailableIndex(null);\n        } else {\n            index = applicationService.getNextAvailableIndex(parentMenu.getId());\n        }\n        return index;\n    }\n\n    private SApplicationMenu buildApplicationMenu(final ApplicationMenuNode applicationMenuNode,\n            final SApplicationWithIcon application,\n            final SApplicationMenu parentMenu, final Long appPageId, final int index) {\n        SApplicationMenu.SApplicationMenuBuilder builder = SApplicationMenu.builder()\n                .displayName(applicationMenuNode.getDisplayName()).applicationId(application.getId())\n                .applicationPageId(appPageId).index(index);\n        if (parentMenu != null) {\n            builder.parentId(parentMenu.getId());\n        }\n        return builder.build();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/converter/NodeToApplicationPageConverter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.converter;\n\nimport org.bonitasoft.engine.api.ImportError;\nimport org.bonitasoft.engine.business.application.importer.ApplicationPageImportResult;\nimport org.bonitasoft.engine.business.application.importer.validator.ApplicationImportValidator;\nimport org.bonitasoft.engine.business.application.model.SApplicationPage;\nimport org.bonitasoft.engine.business.application.model.SApplicationWithIcon;\nimport org.bonitasoft.engine.business.application.xml.ApplicationPageNode;\nimport org.bonitasoft.engine.exception.ImportException;\nimport org.bonitasoft.engine.page.PageService;\nimport org.bonitasoft.engine.page.SPage;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@Component\npublic class NodeToApplicationPageConverter {\n\n    private final PageService pageService;\n    private final ApplicationImportValidator importValidator;\n\n    public NodeToApplicationPageConverter(final PageService pageService,\n            final ApplicationImportValidator importValidator) {\n        this.pageService = pageService;\n        this.importValidator = importValidator;\n    }\n\n    /**\n     * @param applicationPageNode the XML node to convert to {@link SApplicationPage}\n     * @param application the {@link SApplicationWithIcon} where the {@code SApplicationPage} will be attached\n     * @return an ApplicationPageImportResult containing the converted {@code SApplicationPage} and an error (if any)\n     */\n    public ApplicationPageImportResult toSApplicationPage(ApplicationPageNode applicationPageNode,\n            SApplicationWithIcon application) throws SBonitaReadException, ImportException {\n        String token = applicationPageNode.getToken();\n        importValidator.validate(token);\n        long pageId = 0;\n        ImportError importError = null;\n        SPage page = pageService.getPageByName(applicationPageNode.getCustomPage());\n        if (page != null) {\n            pageId = page.getId();\n        } else {\n            importError = new ImportError(applicationPageNode.getCustomPage(), ImportError.Type.PAGE);\n        }\n        return new ApplicationPageImportResult(\n                SApplicationPage.builder().applicationId(application.getId()).pageId(pageId).token(token).build(),\n                importError);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/exporter/ApplicationContainerExporter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.exporter;\n\nimport org.bonitasoft.engine.business.application.xml.ApplicationNodeContainer;\nimport org.bonitasoft.engine.exception.ExportException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ApplicationContainerExporter {\n\n    public byte[] export(final ApplicationNodeContainer applicationNodeContainer) throws ExportException {\n        try {\n            return new ApplicationNodeContainerConverter().marshallToXML(applicationNodeContainer);\n        } catch (final Exception e) {\n            throw new ExportException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/exporter/ApplicationExporter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.exporter;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.business.application.converter.ApplicationsToNodeContainerConverter;\nimport org.bonitasoft.engine.business.application.model.SApplication;\nimport org.bonitasoft.engine.business.application.xml.ApplicationNodeContainer;\nimport org.bonitasoft.engine.exception.ExportException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ApplicationExporter {\n\n    private final ApplicationsToNodeContainerConverter converter;\n    private final ApplicationContainerExporter exporter;\n\n    public ApplicationExporter(ApplicationsToNodeContainerConverter converter, ApplicationContainerExporter exporter) {\n        this.converter = converter;\n        this.exporter = exporter;\n    }\n\n    public byte[] export(List<SApplication> applications) throws ExportException {\n        ApplicationNodeContainer container = converter.toNode(applications);\n        return exporter.export(container);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/ApplicationImportStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.importer;\n\nimport org.bonitasoft.engine.business.application.model.SApplication;\nimport org.bonitasoft.engine.business.application.model.SApplicationWithIcon;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface ApplicationImportStrategy {\n\n    enum ImportStrategy {\n        FAIL, SKIP, REPLACE\n    }\n\n    /**\n     * Determine what to do when the application already exists.\n     */\n    ImportStrategy whenApplicationExists(SApplication existing, SApplicationWithIcon toBeImported);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/ApplicationImporter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.importer;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.api.ImportError;\nimport org.bonitasoft.engine.api.ImportStatus;\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.converter.NodeToApplicationConverter;\nimport org.bonitasoft.engine.business.application.exporter.ApplicationNodeContainerConverter;\nimport org.bonitasoft.engine.business.application.model.SApplication;\nimport org.bonitasoft.engine.business.application.model.SApplicationPage;\nimport org.bonitasoft.engine.business.application.model.SApplicationWithIcon;\nimport org.bonitasoft.engine.business.application.model.builder.SApplicationUpdateBuilder;\nimport org.bonitasoft.engine.business.application.xml.AbstractApplicationNode;\nimport org.bonitasoft.engine.business.application.xml.ApplicationNode;\nimport org.bonitasoft.engine.business.application.xml.ApplicationNodeContainer;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.ImportException;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Component\n@Slf4j\npublic class ApplicationImporter {\n\n    private final ApplicationService applicationService;\n    private final NodeToApplicationConverter nodeToApplicationConverter;\n    private final ApplicationPageImporter applicationPageImporter;\n    private final ApplicationMenuImporter applicationMenuImporter;\n\n    public ApplicationImporter(ApplicationService applicationService,\n            NodeToApplicationConverter nodeToApplicationConverter, ApplicationPageImporter applicationPageImporter,\n            ApplicationMenuImporter applicationMenuImporter) {\n        this.applicationService = applicationService;\n        this.nodeToApplicationConverter = nodeToApplicationConverter;\n        this.applicationPageImporter = applicationPageImporter;\n        this.applicationMenuImporter = applicationMenuImporter;\n    }\n\n    private void updateHomePage(final SApplicationWithIcon application, final ApplicationNode applicationNode,\n            final long createdBy, final ImportResult importResult) throws SBonitaException {\n        if (applicationNode.getHomePage() != null) {\n            try {\n                SApplicationPage homePage = applicationService.getApplicationPage(applicationNode.getToken(),\n                        applicationNode.getHomePage());\n                SApplicationUpdateBuilder updateBuilder = new SApplicationUpdateBuilder(createdBy);\n                updateBuilder.updateHomePageId(homePage.getId());\n                applicationService.updateApplication(application, updateBuilder.done());\n            } catch (SObjectNotFoundException e) {\n                importResult.getImportStatus()\n                        .addErrorsIfNotExists(List.of(\n                                new ImportError(applicationNode.getHomePage(), ImportError.Type.APPLICATION_PAGE)));\n            }\n        }\n    }\n\n    public ImportStatus importApplication(AbstractApplicationNode applicationNode, boolean editable, long createdBy,\n            byte[] iconContent, String iconMimeType, boolean addIfMissing, ApplicationImportStrategy strategy)\n            throws ImportException, AlreadyExistsException {\n        try {\n            ImportResult importResult = nodeToApplicationConverter.toSApplication(applicationNode, iconContent,\n                    iconMimeType, createdBy, editable);\n\n            SApplicationWithIcon applicationToBeImported = importResult.getApplication();\n            ImportStatus importStatus = importResult.getImportStatus();\n\n            importStatus.setStatus(ImportStatus.Status.ADDED);\n\n            // import status will change depending on the chosen strategy, or will throw exception\n            applyStrategyWhenApplicationExists(strategy, applicationToBeImported, importStatus);\n\n            if (!addIfMissing) {\n                importStatus.setStatus(ImportStatus.Status.SKIPPED);\n            }\n\n            if (importStatus.getStatus() != ImportStatus.Status.SKIPPED) {\n                applicationService.createApplication(applicationToBeImported);\n\n                // import more elements for applications built with legacy UID\n                if (applicationNode instanceof ApplicationNode legacy) {\n                    importStatus.addErrorsIfNotExists(\n                            applicationPageImporter\n                                    .importApplicationPages(legacy.getApplicationPages(), applicationToBeImported));\n                    importStatus.addErrorsIfNotExists(\n                            applicationMenuImporter\n                                    .importApplicationMenus(legacy.getApplicationMenus(), applicationToBeImported));\n                    updateHomePage(applicationToBeImported, legacy, createdBy, importResult);\n                }\n            }\n\n            return importStatus;\n        } catch (SBonitaException e) {\n            throw new ImportException(e);\n        }\n    }\n\n    private void applyStrategyWhenApplicationExists(ApplicationImportStrategy strategy,\n            SApplicationWithIcon applicationToBeImported, ImportStatus importStatus)\n            throws SBonitaReadException, AlreadyExistsException, SObjectModificationException {\n        SApplication conflictingApplication = applicationService\n                .getApplicationByToken(applicationToBeImported.getToken());\n        if (conflictingApplication != null) {\n            switch (strategy.whenApplicationExists(conflictingApplication, applicationToBeImported)) {\n                case FAIL:\n                    throw new AlreadyExistsException(\n                            \"An application with token '\" + conflictingApplication.getToken() + \"' already exists\",\n                            conflictingApplication.getToken());\n                case REPLACE:\n                    importStatus.setStatus(ImportStatus.Status.REPLACED);\n                    applicationService.forceDeleteApplication(conflictingApplication);\n                    break;\n                case SKIP:\n                    importStatus.setStatus(ImportStatus.Status.SKIPPED);\n                    break;\n            }\n        }\n    }\n\n    public List<ImportStatus> importApplications(final byte[] xmlContent, byte[] iconContent, String iconMimeType,\n            long createdBy, ApplicationImportStrategy strategy) throws ImportException, AlreadyExistsException {\n        ApplicationNodeContainer applicationNodeContainer = getApplicationNodeContainer(xmlContent);\n        List<ImportStatus> importStatus = new ArrayList<>();\n        for (AbstractApplicationNode applicationNode : applicationNodeContainer.getAllApplications()) {\n            importStatus.add(\n                    importApplication(applicationNode, true, createdBy, iconContent, iconMimeType, true, strategy));\n        }\n        return importStatus;\n    }\n\n    public ApplicationNodeContainer getApplicationNodeContainer(byte[] xmlContent) throws ImportException {\n        try {\n            return new ApplicationNodeContainerConverter().unmarshallFromXML(xmlContent);\n        } catch (final Exception e) {\n            throw new ImportException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/ApplicationMenuImportResult.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.importer;\n\nimport org.bonitasoft.engine.api.ImportError;\nimport org.bonitasoft.engine.business.application.model.SApplicationMenu;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ApplicationMenuImportResult {\n\n    private final ImportError error;\n    private final SApplicationMenu applicationMenu;\n\n    public ApplicationMenuImportResult(ImportError error, SApplicationMenu applicationMenu) {\n        this.error = error;\n        this.applicationMenu = applicationMenu;\n    }\n\n    public ImportError getError() {\n        return error;\n    }\n\n    public SApplicationMenu getApplicationMenu() {\n        return applicationMenu;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/ApplicationMenuImporter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.importer;\n\nimport java.util.*;\n\nimport org.bonitasoft.engine.api.ImportError;\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.converter.NodeToApplicationMenuConverter;\nimport org.bonitasoft.engine.business.application.model.SApplicationMenu;\nimport org.bonitasoft.engine.business.application.model.SApplicationWithIcon;\nimport org.bonitasoft.engine.business.application.xml.ApplicationMenuNode;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.exception.ImportException;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Component\npublic class ApplicationMenuImporter {\n\n    private ApplicationService applicationService;\n    private NodeToApplicationMenuConverter converter;\n\n    public ApplicationMenuImporter(ApplicationService applicationService, NodeToApplicationMenuConverter converter) {\n        this.applicationService = applicationService;\n        this.converter = converter;\n    }\n\n    public List<ImportError> importApplicationMenu(ApplicationMenuNode applicationMenuNode,\n            SApplicationWithIcon application,\n            SApplicationMenu parentMenu)\n            throws ImportException {\n        List<ImportError> errors = new ArrayList<ImportError>();\n        try {\n            ApplicationMenuImportResult importResult = converter.toSApplicationMenu(applicationMenuNode, application,\n                    parentMenu);\n            if (importResult.getError() == null) {\n                SApplicationMenu applicationMenu = applicationService\n                        .createApplicationMenu(importResult.getApplicationMenu());\n                for (ApplicationMenuNode subMenuNode : applicationMenuNode.getApplicationMenus()) {\n                    errors.addAll(importApplicationMenu(subMenuNode, application, applicationMenu));\n                }\n            } else {\n                errors.add(importResult.getError());\n            }\n            return errors;\n        } catch (SBonitaException e) {\n            throw new ImportException(e);\n        }\n    }\n\n    public List<ImportError> importApplicationMenus(List<ApplicationMenuNode> applicationMenus,\n            SApplicationWithIcon application) throws ImportException {\n        List<ImportError> importErrors = new ArrayList<>();\n        for (ApplicationMenuNode applicationMenuNode : applicationMenus) {\n            importErrors.addAll(importApplicationMenu(applicationMenuNode, application, null));\n        }\n        return importErrors;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/ApplicationPageImportResult.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.importer;\n\nimport org.bonitasoft.engine.api.ImportError;\nimport org.bonitasoft.engine.business.application.model.SApplicationPage;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ApplicationPageImportResult {\n\n    private final SApplicationPage applicationPage;\n    private final ImportError error;\n\n    public ApplicationPageImportResult(SApplicationPage applicationPage, ImportError error) {\n        this.applicationPage = applicationPage;\n        this.error = error;\n    }\n\n    public SApplicationPage getApplicationPage() {\n        return applicationPage;\n    }\n\n    public ImportError getError() {\n        return error;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/ApplicationPageImporter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.importer;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.ImportError;\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.converter.NodeToApplicationPageConverter;\nimport org.bonitasoft.engine.business.application.model.SApplicationWithIcon;\nimport org.bonitasoft.engine.business.application.xml.ApplicationPageNode;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.exception.ImportException;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Component\npublic class ApplicationPageImporter {\n\n    private final ApplicationService applicationService;\n    private final NodeToApplicationPageConverter nodeToApplicationPageConverter;\n\n    public ApplicationPageImporter(ApplicationService applicationService,\n            NodeToApplicationPageConverter nodeToApplicationPageConverter) {\n        this.applicationService = applicationService;\n        this.nodeToApplicationPageConverter = nodeToApplicationPageConverter;\n    }\n\n    public ImportError importApplicationPage(ApplicationPageNode applicationPageNode,\n            SApplicationWithIcon application)\n            throws ImportException {\n        try {\n            ApplicationPageImportResult importResult = nodeToApplicationPageConverter\n                    .toSApplicationPage(applicationPageNode, application);\n            if (importResult.getError() == null) {\n                applicationService.createApplicationPage(importResult.getApplicationPage());\n            }\n            return importResult.getError();\n        } catch (SBonitaException e) {\n            throw new ImportException(e);\n        }\n    }\n\n    public List<ImportError> importApplicationPages(final List<ApplicationPageNode> applicationPageNodes,\n            final SApplicationWithIcon application) throws ImportException {\n        List<ImportError> importErrors = new ArrayList<>();\n        for (ApplicationPageNode applicationPageNode : applicationPageNodes) {\n            ImportError importError = importApplicationPage(applicationPageNode, application);\n            if (importError != null) {\n                importErrors.add(importError);\n            }\n        }\n        return importErrors;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/ApplicationZipContent.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.importer;\n\nimport static org.bonitasoft.engine.commons.io.IOUtil.unzip;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport lombok.Data;\nimport org.bonitasoft.engine.exception.ImportException;\n\n@Data\nclass ApplicationZipContent {\n\n    private final byte[] iconRaw;\n\n    private final byte[] xmlRaw;\n\n    private final String pngName;\n\n    static ApplicationZipContent getApplicationZipContent(String resourceName, InputStream resourceAsStream)\n            throws IOException, ImportException {\n        final byte[] content = org.apache.commons.io.IOUtils.toByteArray(resourceAsStream);\n        Map<String, byte[]> zipContent = unzip(content);\n        List<String> pngFileNamesList = zipContent.keySet().stream().filter(l -> l.endsWith(\".png\"))\n                .collect(Collectors.toList());\n        List<String> xmlFileNamesList = zipContent.keySet().stream().filter(l -> l.endsWith(\".xml\"))\n                .collect(Collectors.toList());\n        if (xmlFileNamesList.size() > 1) {\n            throw new ImportException(\"The application zip \" + resourceName\n                    + \" contains more than one xml descriptor, and therefore has an invalid format\");\n        } else if (pngFileNamesList.size() > 1) {\n            throw new ImportException(\"The application zip \" + resourceName\n                    + \" contains more than one icon file, and therefore has an invalid format\");\n        }\n        String pngName = pngFileNamesList.get(0);\n        String xmlName = xmlFileNamesList.get(0);\n        return new ApplicationZipContent(zipContent.get(pngName), zipContent.get(xmlName), pngName);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/DefaultLivingApplicationImporter.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.importer;\n\nimport java.io.IOException;\nimport java.util.List;\n\nimport lombok.Setter;\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.api.ImportStatus;\nimport org.bonitasoft.engine.commons.ExceptionUtils;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.page.PageService;\nimport org.springframework.core.io.support.ResourcePatternResolver;\nimport org.springframework.stereotype.Component;\n\n/**\n * Service used to import default provided living applications at startup, such as:\n * <ul>\n * <li>Bonita Admin Application</li>\n * <li>Bonita User Application</li>\n * </ul>\n */\n@Component\n@Slf4j\npublic class DefaultLivingApplicationImporter extends LivingApplicationImporter {\n\n    private static final String EDITABLE_REMOVABLE_PAGES_PATH = \"org/bonitasoft/web/page\";\n\n    private static final String PROVIDED_REMOVABLE_APPLICATIONS_PATH = \"org/bonitasoft/web/application\";\n\n    /** Boolean used to import provided removable pages if there are missing (mostly when it is a first install) */\n    @Setter\n    private boolean addRemovablePagesIfMissing;\n\n    /**\n     * Boolean used to import provided editable applications if there are missing (mostly when it is a first install)\n     */\n    @Setter\n    private boolean addEditableApplicationsIfMissing;\n\n    public DefaultLivingApplicationImporter(final PageService pageService,\n            final ApplicationImporter applicationImporter) {\n        super(pageService, applicationImporter);\n    }\n\n    /**\n     * Main function to import default living applications with their associated pages.\n     */\n    public void execute() {\n        // Step 1: import default pages\n        log.info(\"Importing Bonita default pages\");\n        importDefaultPages();\n        log.info(\"Import of Bonita default pages completed\");\n\n        // Step 2: import default living apps\n        log.info(\"Importing Bonita default applications\");\n        importDefaultApplications();\n        log.info(\"Import of Bonita default applications completed\");\n    }\n\n    private void importDefaultPages() {\n        try {\n            if (addRemovablePagesIfMissing) {\n                log.info(\"Detected a first run (a tenant creation or an installation from scratch), \"\n                        + \"importing provided removable pages\");\n            } else {\n                log.info(\"Updating provided removable pages if they exist and are outdated\");\n            }\n            // import the provided pages as removable and editable\n            List<ImportStatus> importStatuses = importProvidedPagesFromClasspath(\n                    ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + \"/\" + EDITABLE_REMOVABLE_PAGES_PATH + \"/*.zip\",\n                    true, true, addRemovablePagesIfMissing);\n\n            List<String> createdOrReplaced = getNonSkippedImportedResources(importStatuses);\n            if (createdOrReplaced.isEmpty()) {\n                log.info(\"No default pages updated\");\n            } else {\n                log.info(\"Default pages updated or created: {}\", createdOrReplaced);\n            }\n        } catch (BonitaException | IOException e) {\n            log.error(ExceptionUtils.printLightWeightStacktrace(e));\n            log.debug(\"Stacktrace of the import issue is:\", e);\n        }\n    }\n\n    private void importDefaultApplications() {\n        try {\n            if (addEditableApplicationsIfMissing) {\n                log.info(\"Detected a first run since a Bonita update, a Bonita upgrade, \" +\n                        \"a tenant creation or an installation from scratch. Importing default applications\");\n            } else {\n                log.info(\"Updating provided default applications if they exist and are outdated\");\n            }\n            // import the provided applications as editable\n            List<ImportStatus> importStatuses = importProvidedApplicationsFromClasspath(\n                    ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + \"/\"\n                            + PROVIDED_REMOVABLE_APPLICATIONS_PATH + \"/*.zip\",\n                    true, addEditableApplicationsIfMissing);\n\n            List<String> createdOrReplaced = getNonSkippedImportedResources(importStatuses);\n            if (createdOrReplaced.isEmpty()) {\n                log.info(\"No default applications updated\");\n            } else {\n                log.info(\"Default applications updated or created: {}\", createdOrReplaced);\n            }\n        } catch (Exception e) {\n            log.error(\"Cannot load provided default applications at startup. Root cause: {}\",\n                    ExceptionUtils.printRootCauseOnly(e));\n            log.debug(\"Full stack:\", e);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/FailOnDuplicateApplicationImportStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.importer;\n\nimport org.bonitasoft.engine.business.application.model.SApplication;\nimport org.bonitasoft.engine.business.application.model.SApplicationWithIcon;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class FailOnDuplicateApplicationImportStrategy implements ApplicationImportStrategy {\n\n    @Override\n    public ImportStrategy whenApplicationExists(SApplication existing, SApplicationWithIcon toBeImported) {\n        return ImportStrategy.FAIL;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/ImportResult.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.importer;\n\nimport org.bonitasoft.engine.api.ImportStatus;\nimport org.bonitasoft.engine.business.application.model.SApplicationWithIcon;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ImportResult {\n\n    private final SApplicationWithIcon application;\n    private final ImportStatus importStatus;\n\n    public ImportResult(SApplicationWithIcon application, ImportStatus importStatus) {\n        this.application = application;\n        this.importStatus = importStatus;\n    }\n\n    public SApplicationWithIcon getApplication() {\n        return application;\n    }\n\n    public ImportStatus getImportStatus() {\n        return importStatus;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/LivingApplicationImporter.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.importer;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.stream.Collectors;\n\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.io.IOUtils;\nimport org.bonitasoft.engine.api.ImportStatus;\nimport org.bonitasoft.engine.business.application.xml.AbstractApplicationNode;\nimport org.bonitasoft.engine.business.application.xml.ApplicationNodeContainer;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.ImportException;\nimport org.bonitasoft.engine.page.PageService;\nimport org.bonitasoft.engine.page.SPage;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.springframework.core.io.Resource;\nimport org.springframework.core.io.support.PathMatchingResourcePatternResolver;\nimport org.springframework.core.io.support.ResourcePatternResolver;\nimport org.springframework.util.DigestUtils;\n\n/**\n * Abstract class to regroup common code used by subclasses to import living applications.\n */\n@RequiredArgsConstructor\n@Slf4j\npublic abstract class LivingApplicationImporter {\n\n    protected final PageService pageService;\n\n    protected final ApplicationImporter applicationImporter;\n\n    private final ResourcePatternResolver cpResourceResolver = new PathMatchingResourcePatternResolver(\n            LivingApplicationImporter.class.getClassLoader());\n\n    protected List<ImportStatus> importProvidedPagesFromClasspath(final String locationPattern, final boolean removable,\n            final boolean editable, final boolean addIfMissing) throws IOException, BonitaException {\n        List<ImportStatus> importStatuses = new ArrayList<>();\n        Resource[] resources = cpResourceResolver.getResources(locationPattern);\n        for (Resource resource : resources) {\n            if (resource.exists() && resource.isReadable() && resource.contentLength() > 0) {\n                String resourceName = resource.getFilename();\n                try (InputStream resourceAsStream = resource.getInputStream()) {\n                    log.debug(\"Found provided page '{}' in classpath\", resourceName);\n                    final byte[] content = IOUtils.toByteArray(resourceAsStream);\n                    importStatuses.add(\n                            importProvidedPage(resourceName, content, removable, editable, addIfMissing));\n                } catch (IOException | SBonitaException e) {\n                    throw new BonitaException(\"Unable to import the page \" + resourceName, e);\n                }\n            } else {\n                throw new BonitaException(\n                        \"A resource \" + resource.getDescription() + \" could not be read when loading default pages\");\n            }\n        }\n        return importStatuses;\n    }\n\n    protected ImportStatus importProvidedPage(String pageZipName, final byte[] providedPageContent,\n            boolean removable, boolean editable, boolean addIfMissing) throws SBonitaException {\n\n        SPage page = pageService.buildPage(providedPageContent, pageZipName, SessionService.SYSTEM_ID, true, removable,\n                editable);\n        ImportStatus importStatus = new ImportStatus(page.getName());\n        SPage sPageInDb = pageService.checkIfPageAlreadyExists(page);\n        if (sPageInDb == null && addIfMissing) {\n            log.debug(\"Provided page {} does not exist yet, importing it.\", page.getName());\n            page.setPageHash(DigestUtils.md5DigestAsHex(providedPageContent));\n            pageService.insertPage(page, providedPageContent);\n        } else if (sPageInDb == null) {\n            log.debug(\"Provided page {} has been deleted by the user, and will not be imported\", page.getName());\n            importStatus.setStatus(ImportStatus.Status.SKIPPED);\n        } else if (sPageInDb.isProvided()) {\n            String md5Sum = DigestUtils.md5DigestAsHex(providedPageContent);\n            if (Objects.equals(sPageInDb.getPageHash(), md5Sum)) {\n                log.debug(\"Provided page exists and is up to date, nothing to do\");\n                importStatus.setStatus(ImportStatus.Status.SKIPPED);\n            } else {\n                log.info(\"Provided page {} exists but the content is not up to date, updating it.\", page.getName());\n                pageService.updatePageContent(sPageInDb.getId(), providedPageContent, pageZipName);\n                importStatus.setStatus(ImportStatus.Status.REPLACED);\n            }\n        } else {\n            log.debug(\"Page {} was updated by the user, and will not be updated\", page.getName());\n            importStatus.setStatus(ImportStatus.Status.SKIPPED);\n        }\n        return importStatus;\n    }\n\n    protected List<ImportStatus> importProvidedApplicationsFromClasspath(final String locationPattern,\n            final boolean editable, final boolean addIfMissing) throws IOException, ImportException {\n        List<ImportStatus> importStatuses = new ArrayList<>();\n        Resource[] resources = cpResourceResolver.getResources(locationPattern);\n        for (Resource resource : resources) {\n            if (resource.exists() && resource.isReadable() && resource.contentLength() > 0) {\n                String resourceName = resource.getFilename();\n                log.debug(\"Found provided applications '{}' in classpath\", resourceName);\n                try (InputStream resourceAsStream = resource.getInputStream()) {\n                    ApplicationZipContent zipContent = ApplicationZipContent.getApplicationZipContent(resourceName,\n                            resourceAsStream);\n                    importStatuses.addAll(\n                            importProvidedApplications(zipContent.getXmlRaw(), zipContent.getIconRaw(),\n                                    zipContent.getPngName(), editable, addIfMissing));\n                } catch (IOException | ImportException | AlreadyExistsException e) {\n                    throw new ImportException(e);\n                }\n            } else {\n                throw new ImportException(\n                        \"A resource \" + resource + \" could not be read when loading default applications\");\n            }\n        }\n        return importStatuses;\n\n    }\n\n    protected List<ImportStatus> importProvidedApplications(final byte[] xmlContent, final byte[] iconContent,\n            final String iconMimeType, final boolean editable, final boolean addIfMissing)\n            throws ImportException, AlreadyExistsException {\n        List<ImportStatus> importStatuses = new ArrayList<>();\n        ApplicationNodeContainer applicationNodeContainer = applicationImporter.getApplicationNodeContainer(xmlContent);\n        for (AbstractApplicationNode applicationNode : applicationNodeContainer.getAllApplications()) {\n            // set the strategy to skip it if a version already exists\n            importStatuses.add(\n                    applicationImporter.importApplication(applicationNode, editable, SessionService.SYSTEM_ID,\n                            iconContent, iconMimeType, addIfMissing, new UpdateNewerNonEditableApplicationStrategy()));\n        }\n        return importStatuses;\n    }\n\n    /**\n     * Parses the given list of import statuses of living applications resources to filter those that are marked as\n     * SKIPPED and returns the result in a readable format.\n     *\n     * @param importStatuses list of statuses that result from the import of living applications resources\n     * @return a list of non-skipped imported resources in a format that concatenates their name and status\n     */\n    protected List<String> getNonSkippedImportedResources(final List<ImportStatus> importStatuses) {\n        return importStatuses.stream()\n                .filter(importStatus -> importStatus.getStatus() != ImportStatus.Status.SKIPPED)\n                .map(importStatus -> importStatus.getName() + \" \" + importStatus.getStatus())\n                .collect(Collectors.toList());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/MandatoryLivingApplicationImporter.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.importer;\n\nimport java.io.IOException;\nimport java.util.List;\n\nimport lombok.Getter;\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.api.ImportStatus;\nimport org.bonitasoft.engine.commons.ExceptionUtils;\nimport org.bonitasoft.engine.commons.TenantLifecycleService;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.page.PageService;\nimport org.springframework.core.io.support.ResourcePatternResolver;\nimport org.springframework.stereotype.Component;\n\n/**\n * Service used to import provided living applications that are mandatory for the platform to start, such as:\n * <ul>\n * <li>Bonita Super Admin Application</li>\n * <li>Bonita Application Directory</li>\n * </ul>\n */\n@Component\n@Slf4j\npublic class MandatoryLivingApplicationImporter extends LivingApplicationImporter implements TenantLifecycleService {\n\n    private static final String NON_EDITABLE_NON_REMOVABLE_PAGES_PATH = \"org/bonitasoft/web/page/final\";\n\n    private static final String EDITABLE_NON_REMOVABLE_PAGES_PATH = \"org/bonitasoft/web/page/editonly\";\n\n    private static final String PROVIDED_FINAL_APPLICATIONS_PATH = \"org/bonitasoft/web/application/final\";\n\n    private final DefaultLivingApplicationImporter defaultLivingApplicationImporter;\n\n    @Getter\n    private boolean firstRun;\n\n    public MandatoryLivingApplicationImporter(final PageService pageService,\n            final ApplicationImporter applicationImporter,\n            final DefaultLivingApplicationImporter defaultLivingApplicationImporter) {\n        super(pageService, applicationImporter);\n        this.defaultLivingApplicationImporter = defaultLivingApplicationImporter;\n    }\n\n    @Override\n    public void init() throws SBonitaException {\n        // Step 1: import mandatory pages\n        log.info(\"Importing Bonita mandatory pages\");\n        importMandatoryPages();\n        log.info(\"Import of Bonita mandatory pages completed\");\n\n        // Step 2: import mandatory living apps\n        log.info(\"Importing Bonita mandatory applications\");\n        importMandatoryApplications();\n        log.info(\"Import of Bonita mandatory applications completed\");\n    }\n\n    private void importMandatoryPages() {\n        try {\n            List<ImportStatus> importStatuses = importProvidedNonRemovableNonEditablePagesFromClasspath();\n\n            // If all mandatory pages have been added OR replaced, then it is considered a first startup\n            this.firstRun = importStatuses.stream().map(ImportStatus::getStatus)\n                    .noneMatch(importStatus -> importStatus == ImportStatus.Status.SKIPPED);\n\n            defaultLivingApplicationImporter.setAddRemovablePagesIfMissing(firstRun);\n\n            importStatuses.addAll(importProvidedNonRemovableEditablePagesFromClasspath());\n\n            List<String> createdOrReplaced = getNonSkippedImportedResources(importStatuses);\n            if (createdOrReplaced.isEmpty()) {\n                log.debug(\"No mandatory pages updated\");\n            } else {\n                log.debug(\"Mandatory pages updated or created: {}\", createdOrReplaced);\n            }\n        } catch (BonitaException | IOException e) {\n            log.error(ExceptionUtils.printLightWeightStacktrace(e));\n            log.debug(\"Stacktrace of the import issue is:\", e);\n        }\n    }\n\n    private List<ImportStatus> importProvidedNonRemovableNonEditablePagesFromClasspath()\n            throws BonitaException, IOException {\n        // import the provided pages as non-removable, non-editable and add them if they are missing\n        return importProvidedPagesFromClasspath(\n                ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + \"/\"\n                        + NON_EDITABLE_NON_REMOVABLE_PAGES_PATH + \"/*.zip\",\n                false, false, true);\n    }\n\n    private List<ImportStatus> importProvidedNonRemovableEditablePagesFromClasspath()\n            throws IOException, BonitaException {\n        // import the provided pages as non-removable, editable and add them if they are missing\n        return importProvidedPagesFromClasspath(\n                ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + \"/\"\n                        + EDITABLE_NON_REMOVABLE_PAGES_PATH + \"/*.zip\",\n                false, true, true);\n    }\n\n    private void importMandatoryApplications() {\n        try {\n            // import the provided applications as non-editable and add them if they are missing\n            List<ImportStatus> importStatuses = importProvidedApplicationsFromClasspath(\n                    ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + \"/\"\n                            + PROVIDED_FINAL_APPLICATIONS_PATH + \"/*.zip\",\n                    false, true);\n\n            defaultLivingApplicationImporter.setAddEditableApplicationsIfMissing(importStatuses.stream()\n                    .map(ImportStatus::getStatus)\n                    .noneMatch(status -> status == ImportStatus.Status.SKIPPED));\n\n            List<String> createdOrReplaced = getNonSkippedImportedResources(importStatuses);\n            if (createdOrReplaced.isEmpty()) {\n                log.info(\"No mandatory applications updated\");\n            } else {\n                log.info(\"Mandatory applications updated or created: {}\", createdOrReplaced);\n            }\n        } catch (Exception e) {\n            log.error(\"Cannot load provided mandatory applications at startup. Root cause: {}\",\n                    ExceptionUtils.printRootCauseOnly(e));\n            log.debug(\"Full stack:\", e);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/ReplaceDuplicateApplicationImportStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.importer;\n\nimport org.bonitasoft.engine.business.application.model.SApplication;\nimport org.bonitasoft.engine.business.application.model.SApplicationWithIcon;\n\n/**\n * @author Pascal Garcia\n * @author Emmanuel Duchastenier\n */\npublic class ReplaceDuplicateApplicationImportStrategy implements ApplicationImportStrategy {\n\n    ReplaceDuplicateApplicationImportStrategy() {\n    }\n\n    @Override\n    public ImportStrategy whenApplicationExists(SApplication existing, SApplicationWithIcon toBeImported) {\n        return ImportStrategy.REPLACE;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/StrategySelector.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.importer;\n\nimport org.bonitasoft.engine.business.application.ApplicationImportPolicy;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class StrategySelector {\n\n    public StrategySelector() {\n\n    }\n\n    public ApplicationImportStrategy selectStrategy(ApplicationImportPolicy policy) {\n        switch (policy) {\n            case REPLACE_DUPLICATES:\n                return new ReplaceDuplicateApplicationImportStrategy();\n            case FAIL_ON_DUPLICATES:\n            default:\n                return new FailOnDuplicateApplicationImportStrategy();\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/UpdateNewerNonEditableApplicationStrategy.java",
    "content": "/**\n * Copyright (C) 2021 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.importer;\n\nimport org.bonitasoft.engine.business.application.model.SApplication;\nimport org.bonitasoft.engine.business.application.model.SApplicationWithIcon;\n\npublic class UpdateNewerNonEditableApplicationStrategy implements ApplicationImportStrategy {\n\n    UpdateNewerNonEditableApplicationStrategy() {\n    }\n\n    @Override\n    public ImportStrategy whenApplicationExists(SApplication existing, SApplicationWithIcon toBeImported) {\n        if (existing != null) {\n            if (!existing.isEditable() && !existing.getVersion().equals(toBeImported.getVersion())) {\n                return ImportStrategy.REPLACE;\n            }\n        }\n        return ImportStrategy.SKIP;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/validator/ApplicationImportValidator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.importer.validator;\n\nimport org.bonitasoft.engine.exception.ImportException;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Component\npublic class ApplicationImportValidator {\n\n    private final ApplicationTokenValidator tokenValidator;\n\n    public ApplicationImportValidator(ApplicationTokenValidator tokenValidator) {\n        this.tokenValidator = tokenValidator;\n    }\n\n    public void validate(String token) throws ImportException {\n        ValidationStatus validationStatus = tokenValidator.validate(token);\n        if (!validationStatus.isValid()) {\n            throw new ImportException(validationStatus.getMessage());\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/validator/ApplicationMenuCreatorValidator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.importer.validator;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.business.application.ApplicationMenuCreator;\nimport org.bonitasoft.engine.business.application.ApplicationMenuField;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Component\npublic class ApplicationMenuCreatorValidator {\n\n    public List<String> isValid(final ApplicationMenuCreator creator) {\n        List<String> problems = new ArrayList<>();\n        final Map<ApplicationMenuField, Serializable> fields = creator.getFields();\n        if (fields.get(ApplicationMenuField.APPLICATION_ID) == null) {\n            problems.add(\"The applicationId cannot be null\");\n        }\n        return problems;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/validator/ApplicationTokenValidator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.importer.validator;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Component\npublic class ApplicationTokenValidator {\n\n    public ValidationStatus validate(String token) {\n        List<String> keywords = Arrays.asList(\"content\", \"api\", \"theme\");\n        if (token == null || !token.matches(\"((\\\\p{Alnum})|-|\\\\.|_|~)+\") || keywords.contains(token.toLowerCase())) {\n            StringBuilder stb = new StringBuilder(\"The token '\");\n            stb.append(token);\n            stb.append(\n                    \"' is invalid: the token can not be null or empty and should contain only alpha numeric characters and the following \");\n            stb.append(\n                    \"special characters '-', '.', '_' or '~'. In addition, the following words are reserved keywords and cannot be used as token: 'api', 'content', 'theme'.\");\n            return new ValidationStatus(false, stb.toString());\n        }\n        return new ValidationStatus(true);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/validator/ValidationStatus.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.importer.validator;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ValidationStatus {\n\n    boolean valid;\n\n    private String message;\n\n    public ValidationStatus(final boolean valid) {\n        this.valid = valid;\n    }\n\n    public ValidationStatus(boolean valid, final String message) {\n        this.valid = valid;\n        this.message = message;\n    }\n\n    public String getMessage() {\n        return message;\n    }\n\n    public boolean isValid() {\n        return valid;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/data/BusinessDataRetriever.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.bonitasoft.engine.business.data.proxy.ServerProxyfier;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class BusinessDataRetriever {\n\n    private final BusinessDataRepository businessDataRepository;\n    private final ServerProxyfier proxyfier;\n\n    public BusinessDataRetriever(BusinessDataRepository businessDataRepository, ServerProxyfier proxyfier) {\n        this.businessDataRepository = businessDataRepository;\n        this.proxyfier = proxyfier;\n    }\n\n    /**\n     * Retrieves the Business Data related to the given {@link SSimpleRefBusinessDataInstance}. If the\n     * {@code SSimpleRefBusinessDataInstance} does not\n     * references any Business Data the result will be null.\n     *\n     * @param dataRef the business data reference\n     * @param bizClass the business data class\n     * @return the Business Data related to the given {@code SSimpleRefBusinessDataInstance} or null if no Business Data\n     *         is referenced.\n     * @throws SBusinessDataNotFoundException when no Business Data is found for the given id\n     */\n    public Entity getSimpleBusinessData(SSimpleRefBusinessDataInstance dataRef, Class<? extends Entity> bizClass)\n            throws SBusinessDataNotFoundException {\n        if (dataRef.getDataId() == null) {\n            return null;\n        }\n        final Entity entity = businessDataRepository.findById(bizClass, dataRef.getDataId());\n        return proxyfier.proxify(entity);\n    }\n\n    /**\n     * Retrieves the list of Business Data related to the given {@link SProcessMultiRefBusinessDataInstance}. If the\n     * {@code SMultiRefBusinessDataInstance} does not\n     * references any Business Data the result will em empty list.\n     *\n     * @param dataRef the multi business data reference\n     * @param bizClass the business data class\n     * @return the list of Business Data related to the given {@code SMultiRefBusinessDataInstance} or empty list if no\n     *         Business Data is referenced.\n     */\n    public List<Entity> getMultiBusinessData(SProcessMultiRefBusinessDataInstance dataRef,\n            Class<? extends Entity> bizClass) {\n        if (dataRef.getDataIds() == null || dataRef.getDataIds().isEmpty()) {\n            return new ArrayList<>();\n        }\n        final List<? extends Entity> entities = businessDataRepository.findByIds(bizClass, dataRef.getDataIds());\n\n        final List<Entity> e = new ArrayList<>();\n        for (final Entity entity : entities) {\n            e.add(proxyfier.proxify(entity));\n        }\n        return e;\n    }\n\n    /**\n     * Retrieves a Business Data or a List of Business Data related to the given {@link SRefBusinessDataInstance}\n     * depending on its type (single {@link Entity}\n     * if it's a {@link SSimpleRefBusinessDataInstance} or a List<Entity> if it's a\n     * {@link SProcessMultiRefBusinessDataInstance}.\n     * This method will use {@link #getSimpleBusinessData(SSimpleRefBusinessDataInstance, Class)} or\n     * {@link #getMultiBusinessData(SProcessMultiRefBusinessDataInstance, Class)} based on the data reference type\n     *\n     * @param refBusinessDataInstance the business data reference\n     * @return The {@code Entity} or {@code List<Entity>} if the business data reference is a\n     *         {@code SSimpleRefBusinessDataInstance} or a\n     *         {@code SMultiRefBusinessDataInstance} respectively\n     * @throws SBusinessDataNotFoundException\n     * @throws SExpressionEvaluationException\n     */\n    public Object getBusinessData(final SRefBusinessDataInstance refBusinessDataInstance)\n            throws SBusinessDataNotFoundException, SExpressionEvaluationException {\n        try {\n            final Class<Entity> bizClass = (Class<Entity>) Thread.currentThread().getContextClassLoader()\n                    .loadClass(refBusinessDataInstance.getDataClassName());\n            if (refBusinessDataInstance instanceof SSimpleRefBusinessDataInstance) {\n                return getSimpleBusinessData((SSimpleRefBusinessDataInstance) refBusinessDataInstance, bizClass);\n            }\n            final SProcessMultiRefBusinessDataInstance reference = (SProcessMultiRefBusinessDataInstance) refBusinessDataInstance;\n            return getMultiBusinessData(reference, bizClass);\n        } catch (final ClassNotFoundException e) {\n            throw new SExpressionEvaluationException(\n                    \"Unable to load class for the business data having reference '\" + refBusinessDataInstance.getName()\n                            + \"'\",\n                    e, refBusinessDataInstance.getName());\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/data/RefBusinessDataRetriever.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.RefBusinessDataService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.business.data.SARefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.operation.BusinessDataContext;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Emmanuel Duchastenier\n */\npublic class RefBusinessDataRetriever {\n\n    private RefBusinessDataService refBusinessDataService;\n    private final FlowNodeInstanceService flowNodeInstanceService;\n    private ProcessInstanceService processInstanceService;\n\n    public RefBusinessDataRetriever(final RefBusinessDataService refBusinessDataService,\n            FlowNodeInstanceService flowNodeInstanceService,\n            ProcessInstanceService processInstanceService) {\n        this.refBusinessDataService = refBusinessDataService;\n        this.flowNodeInstanceService = flowNodeInstanceService;\n        this.processInstanceService = processInstanceService;\n    }\n\n    public SRefBusinessDataInstance getRefBusinessDataInstance(BusinessDataContext context) throws SBonitaException {\n        if (isProcessContext(context)) {\n            return getRefBusinessDataUsingProcessContext(context);\n        }\n        return getRefBusinessDataUsingFlowNodeContext(context);\n\n    }\n\n    private SRefBusinessDataInstance getRefBusinessDataUsingFlowNodeContext(BusinessDataContext context)\n            throws SBonitaReadException, SFlowNodeReadException, SRefBusinessDataInstanceNotFoundException,\n            SProcessInstanceReadException {\n        SRefBusinessDataInstance refBusinessDataInstance = getRefBusinessDataInFlowNode(context);\n        if (refBusinessDataInstance != null) {\n            return refBusinessDataInstance;\n        }\n        return getRefBusinessDataInProcess(context, getProcessInstanceIdFromFlowNode(context));\n    }\n\n    private SRefBusinessDataInstance getRefBusinessDataInProcess(BusinessDataContext context,\n            long rootProcessInstanceId)\n            throws SBonitaReadException, SRefBusinessDataInstanceNotFoundException {\n        try {\n            return refBusinessDataService.getRefBusinessDataInstance(context.getName(), rootProcessInstanceId);\n        } catch (SRefBusinessDataInstanceNotFoundException e) {\n            SARefBusinessDataInstance saRefBusinessDataInstance = refBusinessDataService\n                    .getSARefBusinessDataInstance(context.getName(), rootProcessInstanceId);\n            if (saRefBusinessDataInstance == null) {\n                return null;\n            }\n            return saRefBusinessDataInstance.toSRefBusinessDataInstance();\n        }\n    }\n\n    private boolean isProcessContext(BusinessDataContext context) {\n        return DataInstanceContainer.PROCESS_INSTANCE.name().equals(context.getContainer().getType());\n    }\n\n    private long getProcessInstanceIdFromFlowNode(BusinessDataContext context)\n            throws SFlowNodeReadException, SBonitaReadException, SProcessInstanceReadException {\n        try {\n            SFlowNodeInstance flowNodeInstance = flowNodeInstanceService\n                    .getFlowNodeInstance(context.getContainer().getId());\n            SProcessInstance processInstance = processInstanceService\n                    .getProcessInstance(flowNodeInstance.getParentProcessInstanceId());\n            if (isSubProcess(processInstance)) {\n                return getParentOfSubProcess(processInstance);\n            }\n            return flowNodeInstance.getParentProcessInstanceId();\n        } catch (SFlowNodeNotFoundException | SProcessInstanceNotFoundException e) {\n            SAFlowNodeInstance lastArchivedFlowNodeInstance = flowNodeInstanceService\n                    .getLastArchivedFlowNodeInstance(SAFlowNodeInstance.class, context.getContainer().getId());\n            //No caller type in archived process instance, get archive business data not supported from event subprocess\n            return lastArchivedFlowNodeInstance.getParentProcessInstanceId();\n        }\n    }\n\n    private SRefBusinessDataInstance getRefBusinessDataInFlowNode(BusinessDataContext context)\n            throws SBonitaReadException {\n        try {\n            return refBusinessDataService.getFlowNodeRefBusinessDataInstance(context.getName(),\n                    context.getContainer().getId());\n        } catch (final SRefBusinessDataInstanceNotFoundException sbe) {\n            try {\n                SARefBusinessDataInstance saFlowNodeRefBusinessDataInstance = refBusinessDataService\n                        .getSAFlowNodeRefBusinessDataInstance(context.getName(), context.getContainer().getId());\n                if (saFlowNodeRefBusinessDataInstance == null) {\n                    return null;\n                }\n                return saFlowNodeRefBusinessDataInstance.toSRefBusinessDataInstance();\n            } catch (SRefBusinessDataInstanceNotFoundException e) {\n                return null;\n            }\n        }\n    }\n\n    private SRefBusinessDataInstance getRefBusinessDataUsingProcessContext(BusinessDataContext context)\n            throws SBonitaReadException, SRefBusinessDataInstanceNotFoundException, SProcessInstanceReadException,\n            SProcessInstanceNotFoundException,\n            SFlowNodeReadException, SFlowNodeNotFoundException {\n        return getRefBusinessDataInProcess(context,\n                getProcessInstanceIdThatCanContainBusinessData(context.getContainer().getId()));\n    }\n\n    /*\n     * get the first process in hierarchy that can contains business data, i.e. the process instance itself or its\n     * parent if it is an event subprocess\n     */\n    public long getProcessInstanceIdThatCanContainBusinessData(long processInstanceId)\n            throws SProcessInstanceReadException, SBonitaReadException, SProcessInstanceNotFoundException,\n            SFlowNodeReadException, SFlowNodeNotFoundException {\n        try {\n            SProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);\n            if (isSubProcess(processInstance)) {\n                return getParentOfSubProcess(processInstance);\n            }\n            return processInstanceId;\n        } catch (SProcessInstanceNotFoundException e) {\n            //No caller type in archived process instance, get archive business data not supported from event subprocess\n            return processInstanceId;\n        }\n    }\n\n    private boolean isSubProcess(SProcessInstance processInstance) {\n        return SFlowNodeType.SUB_PROCESS.equals(processInstance.getCallerType());\n    }\n\n    private long getParentOfSubProcess(SProcessInstance processInstance)\n            throws SFlowNodeNotFoundException, SFlowNodeReadException {\n        return flowNodeInstanceService.getFlowNodeInstance(processInstance.getCallerId()).getParentProcessInstanceId();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/data/converter/BusinessDataModelConverter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.converter;\n\nimport org.bonitasoft.engine.business.data.MultipleBusinessDataReference;\nimport org.bonitasoft.engine.business.data.SimpleBusinessDataReference;\nimport org.bonitasoft.engine.business.data.impl.MultipleBusinessDataReferenceImpl;\nimport org.bonitasoft.engine.business.data.impl.SimpleBusinessDataReferenceImpl;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SSimpleRefBusinessDataInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class BusinessDataModelConverter {\n\n    public static SimpleBusinessDataReference toSimpleBusinessDataReference(\n            final SSimpleRefBusinessDataInstance sReference) {\n        return new SimpleBusinessDataReferenceImpl(sReference.getName(), sReference.getDataClassName(),\n                sReference.getDataId());\n    }\n\n    public static MultipleBusinessDataReference toMultipleBusinessDataReference(\n            final SProcessMultiRefBusinessDataInstance sReference) {\n        return new MultipleBusinessDataReferenceImpl(sReference.getName(), sReference.getDataClassName(),\n                sReference.getDataIds());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/command/AbstractStartProcessCommand.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.impl.ProcessStarter;\nimport org.bonitasoft.engine.bpm.contract.ContractViolationException;\nimport org.bonitasoft.engine.bpm.process.ProcessActivationException;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ProcessExecutionException;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.execution.AdvancedStartProcessValidator;\nimport org.bonitasoft.engine.operation.Operation;\nimport org.bonitasoft.engine.service.ServiceAccessor;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic abstract class AbstractStartProcessCommand extends RuntimeCommand {\n\n    public static final String STARTED_BY = \"started_by\";\n    public static final String PROCESS_DEFINITION_ID = \"process_definition_id\";\n    public static final String OPERATIONS = \"operations\";\n    public static final String CONTEXT = \"context\";\n    public static final String PROCESS_CONTRACT_INPUTS = \"process_contract_inputs\";\n\n    @Override\n    public Serializable execute(final Map<String, Serializable> parameters, final ServiceAccessor serviceAccessor)\n            throws SCommandParameterizationException, SCommandExecutionException {\n        // get parameters\n        final long processDefinitionId = getProcessDefinitionId(parameters);\n        final List<String> activityNames = getActivityNames(parameters);\n        final long startedBy = getStartedBy(parameters);\n        final Map<String, Serializable> context = getContext(parameters);\n        final List<Operation> operations = getOperations(parameters);\n        Map<String, Serializable> processContractInputs = getProcessContractInputs(parameters);\n        try {\n            validateInputs(serviceAccessor, processDefinitionId, activityNames, processContractInputs);\n\n            return startProcess(processDefinitionId, activityNames, startedBy, context, operations,\n                    processContractInputs);\n        } catch (final SCommandExecutionException e) {\n            throw e;\n        } catch (final Exception e) {\n            throw new SCommandExecutionException(e);\n        }\n    }\n\n    private ProcessInstance startProcess(final long processDefinitionId, final List<String> activityNames,\n            final long startedBy,\n            final Map<String, Serializable> context, final List<Operation> operations,\n            Map<String, Serializable> processContractInputs)\n            throws ProcessDefinitionNotFoundException, ProcessActivationException,\n            ProcessExecutionException, ContractViolationException {\n        final ProcessStarter starter = new ProcessStarter(startedBy, processDefinitionId, operations, context,\n                activityNames, processContractInputs);\n        return starter.start();\n    }\n\n    private void validateInputs(final ServiceAccessor serviceAccessor, final long processDefinitionId,\n            final List<String> activityNames,\n            Map<String, Serializable> processContractInputs)\n            throws SBonitaException {\n        final AdvancedStartProcessValidator validator = new AdvancedStartProcessValidator(\n                serviceAccessor.getProcessDefinitionService(), processDefinitionId,\n                serviceAccessor.getExpressionService());\n        final List<String> problems = validator.validate(activityNames, processContractInputs);\n        handleProblems(problems);\n    }\n\n    private void handleProblems(final List<String> problems) throws SCommandExecutionException {\n        if (!problems.isEmpty()) {\n            final StringBuilder stb = new StringBuilder();\n            for (final String problem : problems) {\n                stb.append(problem);\n                stb.append(\"\\n\");\n            }\n            throw new SCommandExecutionException(stb.toString());\n        }\n    }\n\n    private Long getStartedBy(final Map<String, Serializable> parameters) throws SCommandParameterizationException {\n        return getLongMandatoryParameter(parameters, STARTED_BY);\n    }\n\n    private Long getProcessDefinitionId(final Map<String, Serializable> parameters)\n            throws SCommandParameterizationException {\n        return getLongMandatoryParameter(parameters, PROCESS_DEFINITION_ID);\n    }\n\n    private List<Operation> getOperations(final Map<String, Serializable> parameters)\n            throws SCommandParameterizationException {\n        return getParameter(parameters, OPERATIONS);\n    }\n\n    private Map<String, Serializable> getContext(final Map<String, Serializable> parameters)\n            throws SCommandParameterizationException {\n        return getParameter(parameters, CONTEXT);\n    }\n\n    private Map<String, Serializable> getProcessContractInputs(final Map<String, Serializable> parameters)\n            throws SCommandParameterizationException {\n        return getParameter(parameters, PROCESS_CONTRACT_INPUTS);\n    }\n\n    protected abstract List<String> getActivityNames(Map<String, Serializable> parameters)\n            throws SCommandParameterizationException;\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/command/AdvancedStartProcessCommand.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport java.io.Serializable;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * This command starts the process in the specified activity (if you need to specify several activities as start points,\n * please, use\n * {@link MultipleStartPointsProcessCommand}). Connectors on process start will be executed.\n * <p>It can be executed using the {@link org.bonitasoft.engine.api.CommandAPI#execute(String, java.util.Map)}.\n * Example: {@code commandAPI.execute(\"advancedStartProcessCommand\", parameters)}</p>\n * Parameters:\n * <ul>\n * <li> started_by: the user id (long) is used as the process starter. It's a mandatory parameter.</li>\n * <li> process_definition_id: the process definition id (long) identifies the process to start. It's a mandatory\n * parameter.</li>\n * <li> activity_name: the name of the activity (String) where the process will start the execution. It's a mandatory\n * parameter.</li>\n * <li> operations: the operations (ArrayList<Operation>) are executed when the process starts (set variables and\n * documents). It's an optional parameter.</li>\n * <li> context: the context (HashMap<String, Serializable>) is used during operations execution. It's an optional\n * parameter.</li>\n * </ul>\n * Limitations:\n * <ul>\n * <li> It is not possible to start the execution of a process from a gateway, a boundary event or an event\n * sub-process</li>\n * <li> The process must be started when there is only one active branch. Otherwise use\n * {@code MultipleStartPointsProcessCommand}</li>\n * </ul>\n * Example:\n * start -> step1 -> gateway1 -> (step2 || step3) -> gateway2 -> step4 -> end\n * <ul>\n * <li> Ok: start from \"start\" or \"step1\" or \"step4\" or \"end\"</li>\n * <li> All other start points are invalid.</li>\n * </ul>\n *\n * @author Vincent Elcrin\n * @see org.bonitasoft.engine.command.MultipleStartPointsProcessCommand\n */\npublic class AdvancedStartProcessCommand extends AbstractStartProcessCommand {\n\n    public static final String ACTIVITY_NAME = \"activity_name\";\n\n    protected List<String> getActivityNames(final Map<String, Serializable> parameters)\n            throws SCommandParameterizationException {\n        return Collections.singletonList(getStringMandatoryParameter(parameters, ACTIVITY_NAME));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/command/BusinessDataCommandField.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\n/**\n * @author Laurent Leseigneur\n */\npublic interface BusinessDataCommandField {\n\n    String BUSINESS_DATA_URI_PATTERN = \"businessDataURIPattern\";\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/command/Command.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.CommandAPI;\nimport org.bonitasoft.engine.service.ServiceAccessor;\n\n/**\n * A command is a class that is called from the API and executed on the server side.<br>\n * It is used to extend the engine behavior. See {@link org.bonitasoft.engine.api.CommandAPI} for explanations of how to\n * deploy, undeploy and execute a command.\n * <br>\n * This class should not be directly subclassed by implementors: use {@link RuntimeCommand} instead\n *\n * @see org.bonitasoft.engine.api.CommandAPI\n * @see org.bonitasoft.engine.command.RuntimeCommand\n * @author Matthieu Chaffotte\n */\npublic interface Command<T extends ServiceAccessor> {\n\n    /**\n     * Method that is called by the engine on the server side when the client calls\n     * {@link CommandAPI#execute(String, Map)} with the name or id of this\n     * command.\n     * Implementors of commands must put here the code to be executed on the server side\n     *\n     * @param parameters\n     *        a map of parameters that can be used by the command and that is given by the client when executing the\n     *        command\n     * @param serviceAccessor\n     *        the ServiceAccessor that provides access to the engine's server-side services\n     * @return\n     *         a result that will be returned to the client\n     * @throws SCommandParameterizationException\n     *         can be thrown if insufficient or wrong parameters are given by the client\n     * @throws SCommandExecutionException\n     *         can be thrown when something unexpected happens while executing the command\n     */\n    Serializable execute(Map<String, Serializable> parameters, T serviceAccessor)\n            throws SCommandParameterizationException, SCommandExecutionException;\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/command/DeletePlatformSessionCommand.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.platform.session.PlatformSessionService;\nimport org.bonitasoft.engine.platform.session.SSessionNotFoundException;\nimport org.bonitasoft.engine.service.ServiceAccessor;\n\n/**\n * @author Charles Souillard\n */\npublic class DeletePlatformSessionCommand extends RuntimeCommand {\n\n    public Serializable execute(Map parameters, ServiceAccessor serviceAccessor)\n            throws SCommandParameterizationException, SCommandExecutionException {\n        Long sessionId = (Long) parameters.get(\"sessionId\");\n        PlatformSessionService sessionService = serviceAccessor.getPlatformSessionService();\n        try {\n            sessionService.deleteSession(sessionId.longValue());\n        } catch (SSessionNotFoundException e) {\n            throw new SCommandExecutionException(e);\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/command/ExecuteBDMQueryCommand.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport static org.apache.commons.lang3.BooleanUtils.toBoolean;\n\nimport java.io.IOException;\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bdm.serialization.BusinessDataObjectMapper;\nimport org.bonitasoft.engine.business.data.BusinessDataRepository;\nimport org.bonitasoft.engine.business.data.NonUniqueResultException;\nimport org.bonitasoft.engine.service.ServiceAccessor;\n\n/**\n * @author Romain Bioteau\n * @author Matthieu Chaffotte\n */\npublic class ExecuteBDMQueryCommand extends RuntimeCommand {\n\n    public static final String RETURNS_LIST = \"returnsList\";\n\n    public static final String QUERY_PARAMETERS = \"queryParameters\";\n\n    public static final String RETURN_TYPE = \"returnType\";\n\n    public static final String QUERY_NAME = \"queryName\";\n\n    public static final String START_INDEX = \"startIndex\";\n\n    public static final String MAX_RESULTS = \"maxResults\";\n\n    // Avoid multiple instantiations for better performance, see BS-16794\n    private static final BusinessDataObjectMapper businessDataObjectMapper = new BusinessDataObjectMapper();\n\n    @Override\n    public Serializable execute(final Map<String, Serializable> parameters, final ServiceAccessor serviceAccessor)\n            throws SCommandParameterizationException, SCommandExecutionException {\n        final String queryName = getStringMandatoryParameter(parameters, QUERY_NAME);\n        @SuppressWarnings(\"unchecked\")\n        final Map<String, Serializable> queryParameters = (Map<String, Serializable>) parameters.get(QUERY_PARAMETERS);\n        final String returnType = getStringMandatoryParameter(parameters, RETURN_TYPE);\n        Class<? extends Serializable> resultClass = loadClass(returnType);\n        final BusinessDataRepository businessDataRepository = serviceAccessor.getBusinessDataRepository();\n        final Boolean returnsList = (Boolean) parameters.get(RETURNS_LIST);\n\n        final Serializable result;\n        if (toBoolean(returnsList)) {\n            final Integer startIndex = getIntegerMandatoryParameter(parameters, START_INDEX);\n            final Integer maxResults = getIntegerMandatoryParameter(parameters, MAX_RESULTS);\n            result = (Serializable) businessDataRepository.findListByNamedQuery(queryName, resultClass, queryParameters,\n                    startIndex, maxResults);\n        } else {\n            try {\n                result = businessDataRepository.findByNamedQuery(queryName, resultClass, queryParameters);\n            } catch (final NonUniqueResultException e) {\n                throw new SCommandExecutionException(e);\n            }\n        }\n        return serializeResult(result);\n    }\n\n    private static byte[] serializeResult(final Serializable result) throws SCommandExecutionException {\n        try {\n            return businessDataObjectMapper.writeValueAsBytes(result);\n        } catch (final IOException jpe) {\n            throw new SCommandExecutionException(jpe);\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private Class<? extends Serializable> loadClass(final String returnType) throws SCommandParameterizationException {\n        try {\n            return (Class<? extends Serializable>) Thread.currentThread().getContextClassLoader().loadClass(returnType);\n        } catch (final ClassNotFoundException e) {\n            throw new SCommandParameterizationException(\"Unable to load class for type \" + returnType, e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/command/GetBusinessDataByIdCommand.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.bonitasoft.engine.business.data.BusinessDataService;\nimport org.bonitasoft.engine.business.data.SBusinessDataNotFoundException;\nimport org.bonitasoft.engine.business.data.SBusinessDataRepositoryException;\nimport org.bonitasoft.engine.service.ServiceAccessor;\n\n/**\n * @author Matthieu Chaffotte\n * @author Laurent Leseigneur\n */\npublic class GetBusinessDataByIdCommand extends RuntimeCommand {\n\n    public static final String ENTITY_CLASS_NAME = \"entityClassName\";\n    public static final String BUSINESS_DATA_ID = \"businessDataId\";\n    public static final String BUSINESS_DATA_CHILD_NAME = \"businessDataChildName\";\n\n    @Override\n    public Serializable execute(final Map<String, Serializable> parameters, final ServiceAccessor serviceAccessor)\n            throws SCommandParameterizationException, SCommandExecutionException {\n\n        final BusinessDataService businessDataService = serviceAccessor.getBusinessDataService();\n\n        final Long identifier = getLongMandatoryParameter(parameters, BUSINESS_DATA_ID);\n        final String entityClassName = getStringMandatoryParameter(parameters, ENTITY_CLASS_NAME);\n        final String businessDataURIPattern = getStringMandatoryParameter(parameters,\n                BusinessDataCommandField.BUSINESS_DATA_URI_PATTERN);\n        final String childName = getParameter(parameters, BUSINESS_DATA_CHILD_NAME);\n        try {\n            if (StringUtils.isNotEmpty(childName)) {\n                return businessDataService.getJsonChildEntity(entityClassName, identifier, childName,\n                        businessDataURIPattern);\n            } else {\n                return businessDataService.getJsonEntity(entityClassName, identifier, businessDataURIPattern);\n            }\n        } catch (final SBusinessDataNotFoundException e) {\n            throw new SCommandExecutionException(e.convertToClientException());\n        } catch (final SBusinessDataRepositoryException e) {\n            throw new SCommandExecutionException(e.convertToClientException());\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/command/GetBusinessDataByIdsCommand.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.business.data.BusinessDataService;\nimport org.bonitasoft.engine.business.data.SBusinessDataRepositoryException;\nimport org.bonitasoft.engine.service.ServiceAccessor;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class GetBusinessDataByIdsCommand extends RuntimeCommand {\n\n    public static final String ENTITY_CLASS_NAME = \"entityClassName\";\n\n    public static final String BUSINESS_DATA_IDS = \"businessDataIds\";\n\n    @Override\n    public Serializable execute(final Map<String, Serializable> parameters, final ServiceAccessor serviceAccessor)\n            throws SCommandParameterizationException, SCommandExecutionException {\n        final BusinessDataService businessDataService = serviceAccessor.getBusinessDataService();\n        final List<Long> identifiers = getMandatoryParameter(parameters, BUSINESS_DATA_IDS,\n                \"Parameters map must contain an entry \" + BUSINESS_DATA_IDS\n                        + \" with a Long List value.\");\n        final String entityClassName = getStringMandatoryParameter(parameters, ENTITY_CLASS_NAME);\n        final String businessDataURIPattern = getStringMandatoryParameter(parameters,\n                BusinessDataCommandField.BUSINESS_DATA_URI_PATTERN);\n        try {\n            return businessDataService.getJsonEntities(entityClassName, identifiers, businessDataURIPattern);\n        } catch (final SBusinessDataRepositoryException e) {\n            throw new SCommandExecutionException(e.convertToClientException());\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/command/GetBusinessDataByQueryCommand.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.business.data.SBusinessDataRepositoryException;\nimport org.bonitasoft.engine.service.ServiceAccessor;\n\n/**\n * @author Matthieu Chaffotte\n * @author Laurent Leseigneur\n */\npublic class GetBusinessDataByQueryCommand extends RuntimeCommand {\n\n    public static final String QUERY_PARAMETERS = \"queryParameters\";\n    public static final String ENTITY_CLASS_NAME = \"entityClassName\";\n    public static final String QUERY_NAME = \"queryName\";\n\n    public static final String START_INDEX = \"startIndex\";\n    public static final String MAX_RESULTS = \"maxResults\";\n\n    @Override\n    public Serializable execute(final Map<String, Serializable> parameters, final ServiceAccessor serviceAccessor)\n            throws SCommandParameterizationException, SCommandExecutionException {\n\n        final String queryName = getStringMandatoryParameter(parameters, QUERY_NAME);\n        @SuppressWarnings(\"unchecked\")\n        final Map<String, Serializable> queryParameters = (Map<String, Serializable>) parameters.get(QUERY_PARAMETERS);\n        final String entityClassName = getStringMandatoryParameter(parameters, ENTITY_CLASS_NAME);\n        final Integer startIndex = getIntegerMandatoryParameter(parameters, START_INDEX);\n        final Integer maxResults = super.getIntegerMandatoryParameter(parameters, MAX_RESULTS);\n        String businessDataURIPattern = getStringMandatoryParameter(parameters,\n                BusinessDataCommandField.BUSINESS_DATA_URI_PATTERN);\n        try {\n            return serviceAccessor.getBusinessDataService().getJsonQueryEntities(entityClassName, queryName,\n                    queryParameters, startIndex, maxResults,\n                    businessDataURIPattern);\n        } catch (SBusinessDataRepositoryException e) {\n            throw new SCommandExecutionException(e.convertToClientException());\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/command/MultipleStartPointsProcessCommand.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * This command starts the process in the specified activity(ies). Connectors on process start will be executed.\n * <p>It can be executed using the {@link org.bonitasoft.engine.api.CommandAPI#execute(String, java.util.Map)}.\n * Example: {@code commandAPI.execute(\"multipleStartPointsProcessCommand\", parameters)}</p>\n * Parameters:\n * <ul>\n * <li> started_by: the user id (long) is used as the process starter. It's a mandatory parameter.</li>\n * <li> process_definition_id: the process definition id (long) identifies the process to start. It's a mandatory\n * parameter.</li>\n * <li> activity_names: list of activity names (ArrayList<String>) defining where the process will start the execution.\n * It's a mandatory parameter.</li>\n * <li> operations: the operations (ArrayList<Operation>) are executed when the process starts (set variables and\n * documents). It's an optional parameter.</li>\n * <li> context: the context (HashMap<String, Serializable>) is used during operations execution. It's an optional\n * parameter.</li>\n * </ul>\n * Limitations: It is not possible to start the execution of a process from a gateway, a boundary event or an event\n * sub-process\n * <p>Use this command carefully: note that no validation will be done concerning the start points coherence.</p>\n *\n * @author Elias Ricken de Medeiros\n * @since 6.5.0\n */\npublic class MultipleStartPointsProcessCommand extends AbstractStartProcessCommand {\n\n    public static final String ACTIVITY_NAMES = \"activity_names\";\n\n    @Override\n    protected List<String> getActivityNames(final Map<String, Serializable> parameters)\n            throws SCommandParameterizationException {\n        return getMandatoryParameter(parameters, ACTIVITY_NAMES, \"Missing mandatory field: \" + ACTIVITY_NAMES);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/command/RuntimeCommand.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport java.io.Serializable;\nimport java.util.Map;\nimport java.util.function.Supplier;\n\nimport org.bonitasoft.engine.service.ServiceAccessor;\n\n/**\n * @author Danila Mazour\n */\npublic abstract class RuntimeCommand implements Command<ServiceAccessor> {\n\n    @SuppressWarnings(\"unchecked\")\n    protected <T> T getParameter(final Map<String, Serializable> parameters, final String parameterName,\n            final String message)\n            throws SCommandParameterizationException {\n        try {\n            return (T) parameters.get(parameterName);\n        } catch (final Exception e) {\n            throw new SCommandParameterizationException(message);\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected <T> T getParameter(final Map<String, Serializable> parameters, final String parameterName,\n            final Supplier<String> messageSupplier)\n            throws SCommandParameterizationException {\n        try {\n            return (T) parameters.get(parameterName);\n        } catch (final Exception e) {\n            throw new SCommandParameterizationException(messageSupplier.get());\n        }\n    }\n\n    protected <T> T getParameter(final Map<String, Serializable> parameters, final String parameterName)\n            throws SCommandParameterizationException {\n        return getParameter(parameters, parameterName, \"An error occurred while parsing \" + parameterName);\n    }\n\n    protected Long getLongMandatoryParameter(final Map<String, Serializable> parameters, final String field)\n            throws SCommandParameterizationException {\n        final String message = \"Parameters map must contain an entry \" + field + \" with a long value.\";\n        final Long mandatoryParameter = getMandatoryParameter(parameters, field, message);\n        if (mandatoryParameter == 0L) {\n            throw new SCommandParameterizationException(message);\n        }\n        return mandatoryParameter;\n    }\n\n    protected Integer getIntegerMandatoryParameter(final Map<String, Serializable> parameters, final String field)\n            throws SCommandParameterizationException {\n        final String message = \"Parameters map must contain an entry \" + field + \" with a int value.\";\n        return getMandatoryParameter(parameters, field, message);\n    }\n\n    protected String getStringMandatoryParameter(final Map<String, Serializable> parameters, final String field)\n            throws SCommandParameterizationException {\n        final String message = \"Parameters map must contain an entry \" + field + \" with a String value.\";\n        return getMandatoryParameter(parameters, field, message);\n    }\n\n    protected Map<String, Serializable> getMapMandatoryParameter(final Map<String, Serializable> parameters,\n            final String field)\n            throws SCommandParameterizationException {\n        return getMandatoryParameter(parameters, field, () -> \"Parameters map must contain an entry \" + field\n                + \" with a value of type Map<String, Serializable>.\");\n    }\n\n    protected <T> T getMandatoryParameter(final Map<String, Serializable> parameters, final String field,\n            final String message)\n            throws SCommandParameterizationException {\n        final T value = getParameter(parameters, field, message);\n        if (value == null) {\n            throw new SCommandParameterizationException(message);\n        }\n        return value;\n    }\n\n    protected <T> T getMandatoryParameter(final Map<String, Serializable> parameters, final String field,\n            final Supplier<String> messageSupplier) throws SCommandParameterizationException {\n        final T value = getParameter(parameters, field, messageSupplier);\n        if (value == null) {\n            throw new SCommandParameterizationException(messageSupplier.get());\n        }\n        return value;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/command/SCommandExecutionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * Thrown by a {@link Command} when something unexpected happens during execution.\n *\n * @author Matthieu Chaffotte\n */\npublic class SCommandExecutionException extends SBonitaException {\n\n    private static final long serialVersionUID = -6359883557268565892L;\n\n    public SCommandExecutionException(final String message) {\n        super(message);\n    }\n\n    public SCommandExecutionException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SCommandExecutionException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/command/SCommandParameterizationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * Thrown by a {@link Command} that is called using insufficient or wrong parameters.\n *\n * @author Matthieu Chaffotte\n */\npublic class SCommandParameterizationException extends SBonitaException {\n\n    private static final long serialVersionUID = -9049241280889323348L;\n\n    public SCommandParameterizationException(final String message) {\n        super(message);\n    }\n\n    public SCommandParameterizationException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SCommandParameterizationException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/command/SGroupProfileMemberAlreadyExistsException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\n/**\n * @author Celine Souchet\n */\npublic class SGroupProfileMemberAlreadyExistsException extends SCommandExecutionException {\n\n    private static final long serialVersionUID = -6359883557268565892L;\n\n    public SGroupProfileMemberAlreadyExistsException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/command/SRoleProfileMemberAlreadyExistsException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\n/**\n * @author Celine Souchet\n */\npublic class SRoleProfileMemberAlreadyExistsException extends SCommandExecutionException {\n\n    private static final long serialVersionUID = -6359883557268565892L;\n\n    public SRoleProfileMemberAlreadyExistsException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/command/SUserMembershipProfileMemberAlreadyExistsException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\n/**\n * @author Celine Souchet\n */\npublic class SUserMembershipProfileMemberAlreadyExistsException extends SCommandExecutionException {\n\n    private static final long serialVersionUID = -6359883557268565892L;\n\n    public SUserMembershipProfileMemberAlreadyExistsException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/command/SUserProfileMemberAlreadyExistsException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\n/**\n * @author Celine Souchet\n */\npublic class SUserProfileMemberAlreadyExistsException extends SCommandExecutionException {\n\n    private static final long serialVersionUID = -6359883557268565892L;\n\n    public SUserProfileMemberAlreadyExistsException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/command/system/SearchWaitingEventsCommand.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.system;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.command.RuntimeCommand;\nimport org.bonitasoft.engine.command.SCommandExecutionException;\nimport org.bonitasoft.engine.command.SCommandParameterizationException;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchWaitingEventSerchDescriptor;\nimport org.bonitasoft.engine.search.events.trigger.SearchWaitingEvents;\nimport org.bonitasoft.engine.service.ServiceAccessor;\n\n/**\n * Search Waiting events\n * Parameters ->\n * searchOptions: the searchOptions\n *\n * @author Elias Ricken de Medeiros\n */\npublic class SearchWaitingEventsCommand extends RuntimeCommand {\n\n    private static final String SEARCH_OPTIONS_KEY = \"searchOptions\";\n\n    /**\n     * @param parameters\n     *        searchOptions: the searchOptions\n     */\n    @Override\n    public Serializable execute(final Map<String, Serializable> parameters, final ServiceAccessor serviceAccessor)\n            throws SCommandParameterizationException, SCommandExecutionException {\n        final EventInstanceService eventInstanceService = serviceAccessor.getEventInstanceService();\n        final SearchOptions searchOptions = getMandatoryParameter(parameters, SEARCH_OPTIONS_KEY,\n                \"Missing mandatory field: \" + SEARCH_OPTIONS_KEY);\n        final SearchWaitingEvents searchWaitingEvents = new SearchWaitingEvents(new SearchWaitingEventSerchDescriptor(),\n                searchOptions,\n                eventInstanceService);\n        try {\n            searchWaitingEvents.execute();\n        } catch (final SBonitaException e) {\n            throw new SCommandExecutionException(e);\n        }\n        return searchWaitingEvents.getResult();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/connector/ConnectorAPIAccessorImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connector;\n\nimport java.io.Serial;\nimport java.lang.reflect.Proxy;\n\nimport org.bonitasoft.engine.api.APIAccessor;\nimport org.bonitasoft.engine.api.ApplicationAPI;\nimport org.bonitasoft.engine.api.BusinessDataAPI;\nimport org.bonitasoft.engine.api.CommandAPI;\nimport org.bonitasoft.engine.api.IdentityAPI;\nimport org.bonitasoft.engine.api.PageAPI;\nimport org.bonitasoft.engine.api.PermissionAPI;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.ProfileAPI;\nimport org.bonitasoft.engine.api.impl.ClientInterceptor;\nimport org.bonitasoft.engine.api.impl.ServerAPIFactory;\nimport org.bonitasoft.engine.api.internal.ServerAPI;\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\nimport org.bonitasoft.engine.service.ModelConvertor;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.ServiceAccessorSingleton;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.session.model.SSession;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class ConnectorAPIAccessorImpl implements APIAccessor {\n\n    @Serial\n    private static final long serialVersionUID = 3365911149008207537L;\n\n    private APISession apiSession;\n\n    public ConnectorAPIAccessorImpl() {\n        super();\n    }\n\n    protected APISession getAPISession() {\n        if (apiSession == null) {\n            final ServiceAccessor serviceAccessor = ServiceAccessorSingleton.getInstance();\n            final SessionAccessor sessionAccessor = serviceAccessor.getSessionAccessor();\n            final SessionService sessionService = serviceAccessor.getSessionService();\n            try {\n                final SSession session = sessionService.createSession(ConnectorAPIAccessorImpl.class.getSimpleName());// FIXME get the\n                sessionAccessor.setSessionId(session.getId());\n                apiSession = ModelConvertor.toAPISession(session);\n            } catch (final BonitaRuntimeException e) {\n                throw e;\n            } catch (final Exception e) {\n                throw new BonitaRuntimeException(e);\n            }\n        }\n        return apiSession;\n    }\n\n    @Override\n    public IdentityAPI getIdentityAPI() {\n        return getAPI(IdentityAPI.class, getAPISession());\n    }\n\n    @Override\n    public ProcessAPI getProcessAPI() {\n        return getAPI(ProcessAPI.class, getAPISession());\n    }\n\n    @Override\n    public CommandAPI getCommandAPI() {\n        return getAPI(CommandAPI.class, getAPISession());\n    }\n\n    @Override\n    public ProfileAPI getProfileAPI() {\n        return getAPI(ProfileAPI.class, getAPISession());\n    }\n\n    @Override\n    public PermissionAPI getPermissionAPI() {\n        return getAPI(PermissionAPI.class, getAPISession());\n    }\n\n    @Override\n    public PageAPI getCustomPageAPI() {\n        return getAPI(PageAPI.class, getAPISession());\n    }\n\n    @Override\n    public ApplicationAPI getLivingApplicationAPI() {\n        return getAPI(ApplicationAPI.class, getAPISession());\n    }\n\n    @Override\n    public BusinessDataAPI getBusinessDataAPI() {\n        return getAPI(BusinessDataAPI.class, getAPISession());\n    }\n\n    private static ServerAPI getServerAPI() {\n        return ServerAPIFactory.getServerAPI(false);\n    }\n\n    private static <T> T getAPI(final Class<T> clazz, final APISession session) {\n        final ServerAPI serverAPI = getServerAPI();\n        final ClientInterceptor sessionInterceptor = new ClientInterceptor(clazz.getName(), serverAPI, session);\n        return (T) Proxy.newProxyInstance(APIAccessor.class.getClassLoader(), new Class[] { clazz },\n                sessionInterceptor);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/connector/ConnectorServiceDecorator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connector;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.CompletableFuture;\n\nimport org.bonitasoft.engine.core.connector.ConnectorResult;\nimport org.bonitasoft.engine.core.connector.ConnectorService;\nimport org.bonitasoft.engine.core.connector.exception.SConnectorException;\nimport org.bonitasoft.engine.core.connector.exception.SInvalidConnectorImplementationException;\nimport org.bonitasoft.engine.core.connector.parser.SConnectorImplementationDescriptor;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstance;\nimport org.bonitasoft.engine.expression.EngineConstantExpressionBuilder;\nimport org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.resources.SBARResource;\n\n/**\n * This service wraps the connector service and add engine variables like apiAccessor, engineExecutionContext.\n *\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class ConnectorServiceDecorator implements ConnectorService {\n\n    private final ConnectorService connectorService;\n\n    public ConnectorServiceDecorator(final ConnectorService connectorService) {\n        super();\n        this.connectorService = connectorService;\n    }\n\n    @Override\n    public ConnectorResult executeMultipleEvaluation(final long processDefinitionId, final String connectorDefinitionId,\n            final String connectorDefinitionVersion, final Map<String, SExpression> connectorInputParameters,\n            final Map<String, Map<String, Serializable>> inputValues, final ClassLoader classLoader,\n            final SExpressionContext sexpContext)\n            throws SConnectorException {\n        final Map<String, SExpression> parameters = new HashMap<String, SExpression>(connectorInputParameters);\n        parameters.put(\"connectorApiAccessor\", EngineConstantExpressionBuilder.getConnectorAPIAccessorExpression());\n        parameters.put(\"engineExecutionContext\", EngineConstantExpressionBuilder.getEngineExecutionContext());\n        return connectorService.executeMultipleEvaluation(processDefinitionId, connectorDefinitionId,\n                connectorDefinitionVersion, parameters, inputValues,\n                classLoader, sexpContext);\n    }\n\n    @Override\n    public boolean loadConnectors(final SProcessDefinition sDefinition) throws SConnectorException {\n        return connectorService.loadConnectors(sDefinition);\n    }\n\n    @Override\n    public void setConnectorImplementation(final SProcessDefinition sProcessDefinition, final String connectorId,\n            final String connectorVersion, final byte[] connectorImplementationArchive)\n            throws SConnectorException, SInvalidConnectorImplementationException {\n        connectorService.setConnectorImplementation(sProcessDefinition, connectorId, connectorVersion,\n                connectorImplementationArchive);\n    }\n\n    @Override\n    public List<SConnectorImplementationDescriptor> getConnectorImplementations(final long processDefinitionId,\n            final int fromIndex,\n            final int numberPerPage, final String field, final OrderByType order) throws SConnectorException {\n        return connectorService.getConnectorImplementations(processDefinitionId, fromIndex, numberPerPage, field,\n                order);\n    }\n\n    @Override\n    public SConnectorImplementationDescriptor getConnectorImplementation(final long processDefinitionId,\n            final String connectorId,\n            final String connectorVersion) throws SConnectorException {\n        return connectorService.getConnectorImplementation(processDefinitionId, connectorId, connectorVersion);\n    }\n\n    @Override\n    public Map<String, Object> evaluateInputParameters(final String connectorId,\n            final Map<String, SExpression> parameters,\n            final SExpressionContext sExpressionContext,\n            final Map<String, Map<String, Serializable>> inputValues)\n            throws SExpressionTypeUnknownException, SExpressionEvaluationException,\n            SExpressionDependencyMissingException, SInvalidExpressionException {\n        SExpression apiAccessorExpression = EngineConstantExpressionBuilder.getConnectorAPIAccessorExpression();\n        SExpression engineExecutionContext = EngineConstantExpressionBuilder.getEngineExecutionContext();\n        final Map<String, SExpression> newParameters = new HashMap<String, SExpression>(parameters);\n        newParameters.put(\"connectorApiAccessor\", apiAccessorExpression);\n        newParameters.put(\"engineExecutionContext\", engineExecutionContext);\n        return connectorService.evaluateInputParameters(connectorId, newParameters, sExpressionContext, inputValues);\n    }\n\n    @Override\n    public void executeOutputOperation(final List<SOperation> outputs, final SExpressionContext expressionContext,\n            final ConnectorResult connectorOutput)\n            throws SConnectorException {\n        connectorService.executeOutputOperation(outputs, expressionContext, connectorOutput);\n    }\n\n    @Override\n    public CompletableFuture<ConnectorResult> executeConnector(final long processDefinitionId,\n            final SConnectorInstance sConnectorInstance,\n            SConnectorImplementationDescriptor connectorImplementationDescriptor, final ClassLoader classLoader,\n            final Map<String, Object> inputParameters) throws SConnectorException {\n        return connectorService.executeConnector(processDefinitionId, sConnectorInstance,\n                connectorImplementationDescriptor, classLoader, inputParameters);\n    }\n\n    @Override\n    public void disconnect(final ConnectorResult result) throws SConnectorException {\n        connectorService.disconnect(result);\n    }\n\n    @Override\n    public Long getNumberOfConnectorImplementations(final long processDefinitionId) throws SConnectorException {\n        return connectorService.getNumberOfConnectorImplementations(processDefinitionId);\n    }\n\n    @Override\n    public List<SBARResource> getConnectorImplementations(long processDefinitionId, int from, int numberOfElements)\n            throws SBonitaReadException {\n        return connectorService.getConnectorImplementations(processDefinitionId, from, numberOfElements);\n    }\n\n    @Override\n    public void addConnectorImplementation(Long processDefinitionId, String name, byte[] content)\n            throws SRecorderException {\n        connectorService.addConnectorImplementation(processDefinitionId, name, content);\n    }\n\n    @Override\n    public void removeConnectorImplementations(long processDefinitionId)\n            throws SBonitaReadException, SRecorderException {\n        connectorService.removeConnectorImplementations(processDefinitionId);\n    }\n\n    @Override\n    public SConnectorImplementationDescriptor getConnectorImplementationDescriptor(long processDefinitionId,\n            String connectorId, String version) throws SConnectorException {\n        return connectorService.getConnectorImplementation(processDefinitionId, connectorId, version);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/core/document/api/impl/DocumentHelper.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.document.api.impl;\n\nimport java.io.File;\nimport java.nio.file.Files;\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.contract.FileInputValue;\nimport org.bonitasoft.engine.bpm.document.Document;\nimport org.bonitasoft.engine.bpm.document.DocumentValue;\nimport org.bonitasoft.engine.commons.exceptions.SObjectAlreadyExistsException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectCreationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.commons.io.IOUtil;\nimport org.bonitasoft.engine.core.document.api.DocumentService;\nimport org.bonitasoft.engine.core.document.model.AbstractSMappedDocument;\nimport org.bonitasoft.engine.core.document.model.SDocument;\nimport org.bonitasoft.engine.core.document.model.SMappedDocument;\nimport org.bonitasoft.engine.core.document.model.builder.SDocumentBuilder;\nimport org.bonitasoft.engine.core.document.model.builder.SDocumentBuilderFactory;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.core.process.definition.model.SDocumentListDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\n/**\n * Helper class that set/get/update document on API level (it uses the process definition)\n *\n * @author Baptiste Mesta\n */\npublic class DocumentHelper {\n\n    private final DocumentService documentService;\n    private final ProcessDefinitionService processDefinitionService;\n    private final ProcessInstanceService processInstanceService;\n\n    public DocumentHelper(final DocumentService documentService,\n            final ProcessDefinitionService processDefinitionService,\n            final ProcessInstanceService processInstanceService) {\n        this.documentService = documentService;\n        this.processDefinitionService = processDefinitionService;\n        this.processInstanceService = processInstanceService;\n    }\n\n    public List<AbstractSMappedDocument> getAllDocumentOfTheList(final long processInstanceId, final String name)\n            throws SBonitaReadException {\n        QueryOptions queryOptions = new QueryOptions(0, 100);\n        List<SMappedDocument> mappedDocuments;\n        final List<AbstractSMappedDocument> result = new ArrayList<>();\n        do {\n            mappedDocuments = documentService.getDocumentList(name, processInstanceId, queryOptions.getFromIndex(),\n                    queryOptions.getNumberOfResults());\n            result.addAll(mappedDocuments);\n            queryOptions = QueryOptions.getNextPage(queryOptions);\n        } while (mappedDocuments.size() == 100);\n        return result;\n    }\n\n    public boolean isListDefinedInDefinition(final String documentName, final long processInstanceId)\n            throws org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException, SBonitaReadException {\n        try {\n            final SProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);\n            final SProcessDefinition processDefinition = processDefinitionService\n                    .getProcessDefinition(processInstance.getProcessDefinitionId());\n            final List<SDocumentListDefinition> documentDefinitions = processDefinition.getProcessContainer()\n                    .getDocumentListDefinitions();\n            for (final SDocumentListDefinition documentDefinition : documentDefinitions) {\n                if (documentName.equals(documentDefinition.getName())) {\n                    return true;\n                }\n            }\n        } catch (final SProcessInstanceNotFoundException e) {\n            throw new SObjectNotFoundException(\n                    \"Unable to find the list \" + documentName + \", nothing in database and the process instance \"\n                            + processInstanceId + \" is not found\",\n                    e);\n        } catch (final SProcessInstanceReadException e) {\n            throw new SBonitaReadException(e);\n        } catch (final SProcessDefinitionNotFoundException e) {\n            throw new SObjectNotFoundException(\n                    \"Unable to find the list \" + documentName + \" on process instance \" + processInstanceId\n                            + \", nothing in database and the process definition is not found\",\n                    e);\n        }\n        return false;\n    }\n\n    public SDocument createDocumentObject(final DocumentValue documentValue, final long authorId) {\n        final SDocumentBuilder processDocumentBuilder = new SDocumentBuilderFactory().createNewInstance(\n                documentValue.getFileName(),\n                getMimeTypeOrGuessIt(documentValue), authorId);\n        processDocumentBuilder.setHasContent(documentValue.hasContent());\n        processDocumentBuilder.setURL(documentValue.getUrl());\n        processDocumentBuilder.setContent(documentValue.getContent());\n        return processDocumentBuilder.done();\n    }\n\n    String getMimeTypeOrGuessIt(DocumentValue documentValue) {\n        final String mimeType = documentValue.getMimeType();\n        final byte[] content = documentValue.getContent();\n        final String fileName = documentValue.getFileName();\n        if (mimeType != null && !mimeType.isEmpty() || content == null || fileName == null || fileName.isEmpty()) {\n            return mimeType;\n        }\n        try {\n            final File tempFile = File.createTempFile(\"tmp\", fileName);\n            IOUtil.write(tempFile, content);\n            final String s = Files.probeContentType(tempFile.toPath());\n            tempFile.delete();\n            return s;\n        } catch (Throwable e) {\n            return mimeType;\n        }\n    }\n\n    public void deleteDocument(final String documentName, final long processInstanceId)\n            throws SObjectModificationException {\n        try {\n            documentService.removeCurrentVersion(processInstanceId, documentName);\n        } catch (final SObjectNotFoundException e) {\n            // nothing to do\n        }\n    }\n\n    /**\n     * @param newValue the new value\n     * @param documentName the name of the document\n     * @param processInstanceId the id of the process instance\n     * @param authorId the author id\n     * @param description used only when creating a document\n     * @throws SBonitaReadException\n     * @throws SObjectCreationException\n     * @throws SObjectModificationException\n     */\n    public void createOrUpdateDocument(final DocumentValue newValue, final String documentName,\n            final long processInstanceId, final long authorId,\n            String description)\n            throws SBonitaReadException, SObjectCreationException, SObjectModificationException {\n        final SDocument document = createDocumentObject(newValue, authorId);\n        try {\n            // Let's check if the document already exists:\n            final SMappedDocument mappedDocument = documentService.getMappedDocument(processInstanceId, documentName);\n            // a document exist, update it with the new values\n            documentService.updateDocument(mappedDocument, document);\n        } catch (final SObjectNotFoundException e) {\n            documentService.attachDocumentToProcessInstance(document, processInstanceId, documentName, description);\n        }\n    }\n\n    public void setDocumentList(final List<DocumentValue> documentList, final String documentName,\n            final long processInstanceId, final long authorId)\n            throws SBonitaReadException, SObjectCreationException, SObjectNotFoundException,\n            SObjectModificationException, SObjectAlreadyExistsException {\n        // get the list having the name\n        final List<AbstractSMappedDocument> currentList = getExistingDocumentList(documentName, processInstanceId);\n        // iterate on elements\n        int index;\n        for (index = 0; index < documentList.size(); index++) {\n            processDocumentOnIndex(documentList.get(index), documentName, processInstanceId, currentList, index,\n                    authorId);\n        }\n\n        // when no more elements in documentList remove elements above\n        removeOthersDocuments(currentList);\n    }\n\n    void updateExistingDocument(final AbstractSMappedDocument documentToUpdate, final int index,\n            final DocumentValue documentValue, final long authorId)\n            throws SObjectModificationException {\n        if (documentValue.hasChanged()) {\n            documentService.updateDocumentOfList(documentToUpdate, createDocumentObject(documentValue, authorId),\n                    index);\n        } else {\n            //  update the index if needed\n            if (documentToUpdate.getIndex() != index) {\n                documentService.updateDocumentIndex(documentToUpdate, index);\n            }\n        }\n    }\n\n    AbstractSMappedDocument getDocumentHavingDocumentIdAndRemoveFromList(\n            final List<AbstractSMappedDocument> currentList,\n            final Long documentId, final String documentName,\n            final Long processInstanceId) throws SObjectNotFoundException {\n        final Iterator<AbstractSMappedDocument> iterator = currentList.iterator();\n        while (iterator.hasNext()) {\n            final AbstractSMappedDocument next = iterator.next();\n            if (next.getId() == documentId) {\n                iterator.remove();\n                return next;\n            }\n        }\n        throw new SObjectNotFoundException(\n                \"The document with id \" + documentId + \" was not in the list \" + documentName + \" of process instance \"\n                        + processInstanceId);\n    }\n\n    List<AbstractSMappedDocument> getExistingDocumentList(final String documentName, final long processInstanceId)\n            throws SBonitaReadException,\n            SObjectNotFoundException {\n        List<AbstractSMappedDocument> currentList;\n        currentList = getAllDocumentOfTheList(processInstanceId, documentName);\n        // if it's not a list it throws an exception\n        if (currentList.isEmpty() && !isListDefinedInDefinition(documentName, processInstanceId)) {\n            throw new SObjectNotFoundException(\n                    \"Unable to find the list \" + documentName + \" on process instance \" + processInstanceId\n                            + \", nothing in database and nothing declared in the definition\");\n        }\n        return currentList;\n    }\n\n    void removeOthersDocuments(final List<AbstractSMappedDocument> currentList) throws SObjectModificationException {\n        for (final AbstractSMappedDocument mappedDocument : currentList) {\n            documentService.removeCurrentVersion(mappedDocument);\n        }\n\n    }\n\n    void processDocumentOnIndex(final DocumentValue documentValue, final String documentName,\n            final long processInstanceId,\n            final List<AbstractSMappedDocument> currentList, final int index, final long authorId)\n            throws SObjectCreationException, SObjectAlreadyExistsException,\n            SObjectNotFoundException, SObjectModificationException {\n        if (documentValue.getDocumentId() != null) {\n            // if hasChanged update\n            final AbstractSMappedDocument documentToUpdate = getDocumentHavingDocumentIdAndRemoveFromList(currentList,\n                    documentValue.getDocumentId(), documentName,\n                    processInstanceId);\n            updateExistingDocument(documentToUpdate, index, documentValue, authorId);\n        } else {\n            // create new element\n            documentService.attachDocumentToProcessInstance(createDocumentObject(documentValue, authorId),\n                    processInstanceId, documentName, null, index);\n        }\n    }\n\n    public DocumentValue toCheckedDocumentValue(final Object newValue) throws SOperationExecutionException {\n        if (newValue != null) {\n            final boolean isFileInput = newValue instanceof FileInputValue;\n            if (isFileInput) {\n                FileInputValue fileInput = ((FileInputValue) newValue);\n                return toDocumentValue(fileInput);\n            }\n            final boolean isDocumentWithContent = newValue instanceof DocumentValue;\n            if (!isDocumentWithContent) {\n                throw new SOperationExecutionException(\n                        \"Document operation only accepts an expression returning a DocumentValue and not \"\n                                + newValue.getClass().getName());\n            }\n        }\n        return (DocumentValue) newValue;\n    }\n\n    public DocumentValue toDocumentValue(FileInputValue fileInput) {\n        if (fileInput.getId() == null) {\n            return new DocumentValue(fileInput.getContent(), fileInput.getContentType(), fileInput.getFileName());\n        } else if (fileInput.getContent() != null) {\n            return new DocumentValue(Long.valueOf(fileInput.getId()), fileInput.getContent(),\n                    fileInput.getContentType(), fileInput.getFileName());\n        } else {\n            return new DocumentValue(Long.valueOf(fileInput.getId()));\n        }\n    }\n\n    public DocumentValue toDocumentValue(Document document) throws SOperationExecutionException {\n        DocumentValue documentValue;\n        if (document.hasContent()) {\n            try {\n                byte[] documentContent = documentService.getDocumentContent(document.getContentStorageId());\n                documentValue = new DocumentValue(documentContent, document.getContentMimeType(),\n                        document.getContentFileName());\n            } catch (SObjectNotFoundException e) {\n                throw new SOperationExecutionException(\n                        \"Unable to execute set document operation because the content of the document to use is not found\",\n                        e);\n            }\n        } else {\n            documentValue = new DocumentValue(document.getUrl());\n        }\n        return documentValue;\n    }\n\n    public List<DocumentValue> toCheckedList(final Object newValue) throws SOperationExecutionException {\n        if (!(newValue instanceof List)) {\n            throw new SOperationExecutionException(\n                    \"Document operation only accepts an expression returning a list of DocumentValue\");\n        }\n        List<DocumentValue> newList = new ArrayList<>(((List) newValue).size());\n        for (final Object item : (List<?>) newValue) {\n            if (item instanceof FileInputValue) {\n                newList.add(toDocumentValue((FileInputValue) item));\n                continue;\n            }\n            if (item instanceof Document) {\n                newList.add(toDocumentValue((Document) item));\n                continue;\n            }\n            if (item instanceof DocumentValue) {\n                newList.add((DocumentValue) item);\n                continue;\n\n            }\n            if (item == null) {\n                //ignore the item\n                continue;\n            }\n            throw new SOperationExecutionException(\n                    \"Document operation only accepts an expression returning a list of DocumentValue\");\n\n        }\n        return newList;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/core/form/ExternalURLAdapter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.form;\n\nimport java.io.Serializable;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.bonitasoft.engine.commons.exceptions.SExecutionException;\nimport org.bonitasoft.engine.page.URLAdapter;\nimport org.bonitasoft.engine.page.URLAdapterConstants;\n\n/**\n * @author Baptiste Mesta, Anthony Birembaut\n */\npublic class ExternalURLAdapter implements URLAdapter {\n\n    private static final String PARAM_TAG = \"?\";\n\n    private static final String HASH_TAG = \"#\";\n\n    @Override\n    public String adapt(String url, String key, Map<String, Serializable> context) throws SExecutionException {\n        @SuppressWarnings(\"unchecked\")\n        final Map<String, String[]> queryParameters = (Map<String, String[]>) context\n                .get(URLAdapterConstants.QUERY_PARAMETERS);\n        final String[] hash = url.split(HASH_TAG);\n        StringBuffer newURL = new StringBuffer(hash[0]);\n\n        appendParametersToURL(newURL, queryParameters);\n\n        if (url.contains(HASH_TAG)) {\n            newURL.append(HASH_TAG);\n            if (hash.length > 1) {\n                newURL.append(hash[1]);\n            }\n        }\n\n        return newURL.toString();\n    }\n\n    protected void appendParametersToURL(StringBuffer url, final Map<String, String[]> parameters)\n            throws SExecutionException {\n        if (parameters != null) {\n            for (Entry<String, String[]> parameterEntry : parameters.entrySet()) {\n                appendParameterToURL(url, parameterEntry.getKey(), parameterEntry.getValue());\n            }\n        }\n    }\n\n    protected void appendParameterToURL(final StringBuffer newURL, final String key, final String[] value) {\n        appendSeparator(newURL, PARAM_TAG);\n        newURL.append(key).append(\"=\");\n        for (int i = 0; i < value.length; i++) {\n            if (i > 0) {\n                newURL.append(\",\");\n            }\n            newURL.append(value[i]);\n        }\n    }\n\n    protected void appendSeparator(final StringBuffer buffer, final String initialSeparator) {\n        if (buffer.indexOf(initialSeparator) != -1) {\n            buffer.append(\"&\");\n        } else {\n            buffer.append(initialSeparator);\n        }\n    }\n\n    @Override\n    public String getId() {\n        return URLAdapterConstants.EXTERNAL_URL_ADAPTER;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/core/form/LegacyURLAdapter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.form;\n\nimport java.io.Serializable;\nimport java.io.UnsupportedEncodingException;\nimport java.net.URLEncoder;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SExecutionException;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\nimport org.bonitasoft.engine.form.FormMappingType;\nimport org.bonitasoft.engine.page.URLAdapter;\nimport org.bonitasoft.engine.page.URLAdapterConstants;\n\n/**\n * @author Baptiste Mesta, Anthony Birembaut\n */\npublic class LegacyURLAdapter implements URLAdapter {\n\n    private static final String UUID_SEPERATOR = \"--\";\n\n    private static final String DEFAULT_FORM_MODE = \"form\";\n\n    ProcessDefinitionService processDefinitionService;\n\n    FormMappingService formMappingService;\n\n    public LegacyURLAdapter(final ProcessDefinitionService processDefinitionService,\n            final FormMappingService formMappingService) {\n        this.processDefinitionService = processDefinitionService;\n        this.formMappingService = formMappingService;\n    }\n\n    @Override\n    public String adapt(final String url, final String key, final Map<String, Serializable> context)\n            throws SExecutionException {\n        @SuppressWarnings(\"unchecked\")\n        final Map<String, String[]> queryParameters = (Map<String, String[]>) context\n                .get(URLAdapterConstants.QUERY_PARAMETERS);\n\n        String[] idParamValue = new String[0];\n        if (queryParameters != null) {\n            idParamValue = queryParameters.get(URLAdapterConstants.ID_QUERY_PARAM);\n        }\n        String bpmId;\n        if (idParamValue == null || idParamValue.length == 0) {\n            throw new IllegalArgumentException(\"The parameter \\\"id\\\" is missing from the original URL\");\n        } else {\n            bpmId = idParamValue[0];\n            try {\n                final SFormMapping formMapping = formMappingService.get(key);\n                final SProcessDefinition processDefinition = processDefinitionService\n                        .getProcessDefinition(formMapping.getProcessDefinitionId());\n                final String locale = (String) context.get(URLAdapterConstants.LOCALE);\n                final String contextPath = (String) context.get(URLAdapterConstants.CONTEXT_PATH);\n                boolean assignTask = false;\n                final String[] assignTaskValue = queryParameters.get(URLAdapterConstants.ASSIGN_TASK_QUERY_PARAM);\n                if (assignTaskValue != null && assignTaskValue.length > 0 && \"true\".equals(assignTaskValue[0])) {\n                    assignTask = true;\n                }\n                String user = null;\n                final String[] userParamValue = queryParameters.get(URLAdapterConstants.USER_QUERY_PARAM);\n                if (userParamValue != null && userParamValue.length > 0) {\n                    user = userParamValue[0];\n                }\n                String mode = DEFAULT_FORM_MODE;\n                final String[] modeParamValue = queryParameters.get(URLAdapterConstants.MODE_QUERY_PARAM);\n                if (modeParamValue != null && modeParamValue.length > 0) {\n                    mode = modeParamValue[0];\n                }\n                boolean autoInstantiate = true;\n                final String[] autoInstantiateValue = queryParameters\n                        .get(URLAdapterConstants.AUTO_INSTANTIATE_QUERY_PARAM);\n                if (autoInstantiateValue != null && autoInstantiateValue.length > 0\n                        && \"false\".equals(autoInstantiateValue[0])) {\n                    autoInstantiate = false;\n                }\n                return generateLegacyURL(contextPath, locale, bpmId, formMapping, processDefinition, user, assignTask,\n                        mode, autoInstantiate);\n            } catch (final SBonitaException e) {\n                throw new SExecutionException(\n                        \"Unable to generate the legacy form URL for key \" + key + \"(id: \" + bpmId + \")\", e);\n            }\n        }\n    }\n\n    protected String generateLegacyURL(final String contextPath, final String locale, final String bpmId,\n            final SFormMapping formMapping, final SProcessDefinition processDefinition, final String user,\n            final boolean assignTask, final String mode, boolean autoInstantiate) {\n        final StringBuilder legacyFormURL = new StringBuilder(contextPath);\n        legacyFormURL.append(\"/portal/homepage?ui=form&locale=\")\n                .append(locale)\n                .append(\"&theme=\")\n                .append(formMapping.getProcessDefinitionId())\n                .append(\"#mode=\")\n                .append(mode)\n                .append(\"&form=\")\n                .append(urlEncode(processDefinition.getName()))\n                .append(UUID_SEPERATOR)\n                .append(urlEncode(processDefinition.getVersion()));\n        if (FormMappingType.TASK.getId().equals(formMapping.getType())) {\n            legacyFormURL.append(UUID_SEPERATOR).append(urlEncode(formMapping.getTask() + \"$\"))\n                    .append(\"entry&task=\")\n                    .append(bpmId);\n            if (assignTask) {\n                legacyFormURL.append(\"&assignTask=true\");\n            }\n        } else if (FormMappingType.PROCESS_OVERVIEW.getId().equals(formMapping.getType())) {\n            legacyFormURL.append(urlEncode(\"$\"))\n                    .append(\"recap&instance=\").append(bpmId)\n                    .append(\"&recap=true\");\n        } else {\n            legacyFormURL.append(urlEncode(\"$\"))\n                    .append(\"entry&process=\").append(bpmId);\n            if (!autoInstantiate) {\n                legacyFormURL.append(\"&autoInstantiate=false\");\n            }\n        }\n        if (user != null) {\n            legacyFormURL.append(\"&userId=\").append(user);\n        }\n        return legacyFormURL.toString();\n    }\n\n    protected String urlEncode(final String stringToEncode) {\n        try {\n            return URLEncoder.encode(stringToEncode, \"UTF-8\");\n        } catch (final UnsupportedEncodingException e) {\n            throw new BonitaRuntimeException(e);\n        }\n    }\n\n    @Override\n    public String getId() {\n        return URLAdapterConstants.LEGACY_URL_ADAPTER;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/data/ParentContainerResolverImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectReadException;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance;\nimport org.bonitasoft.engine.data.instance.api.DataContainer;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.data.instance.api.ParentContainerResolver;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\npublic class ParentContainerResolverImpl implements ParentContainerResolver {\n\n    private final FlowNodeInstanceService flowNodeInstanceService;\n    private final ProcessInstanceService processInstanceService;\n    private boolean allowUnknownContainer;\n\n    public ParentContainerResolverImpl(final FlowNodeInstanceService flowNodeInstanceService,\n            final ProcessInstanceService processInstanceService) {\n        super();\n        this.flowNodeInstanceService = flowNodeInstanceService;\n        this.processInstanceService = processInstanceService;\n    }\n\n    public boolean getAllowUnknownContainer() {\n        return allowUnknownContainer;\n    }\n\n    public void setAllowUnknownContainer(boolean allowUnknownContainer) {\n        this.allowUnknownContainer = allowUnknownContainer;\n    }\n\n    @Override\n    public List<DataContainer> getContainerHierarchy(final DataContainer currentContainer)\n            throws SObjectNotFoundException, SObjectReadException {\n        return getContainerHierarchy(currentContainer, false);\n    }\n\n    @Override\n    public List<DataContainer> getArchivedContainerHierarchy(final DataContainer currentContainer)\n            throws SObjectNotFoundException, SObjectReadException {\n        try {\n            return getContainerHierarchy(currentContainer, true);\n        } catch (SObjectNotFoundException e) {\n            return Collections.singletonList(currentContainer);\n        }\n    }\n\n    private List<DataContainer> getContainerHierarchy(DataContainer currentContainer, boolean isArchived)\n            throws SObjectNotFoundException, SObjectReadException {\n        DataContainer container = new DataContainer(currentContainer.getId(), currentContainer.getType());\n        final List<DataContainer> containerHierarchy = new ArrayList<>();\n        containerHierarchy.add(container);\n        try {\n            do {\n                container = getNextContainer(isArchived, container, containerHierarchy);\n            } while (container != null);\n            return containerHierarchy;\n        } catch (SProcessInstanceNotFoundException | SFlowNodeNotFoundException e) {\n            throw new SObjectNotFoundException(e);\n        } catch (SProcessInstanceReadException | SBonitaReadException | SFlowNodeReadException e) {\n            throw new SObjectReadException(e);\n        }\n    }\n\n    private DataContainer getNextContainer(boolean isArchived, DataContainer container,\n            List<DataContainer> containerHierarchy)\n            throws SFlowNodeReadException, SFlowNodeNotFoundException, SBonitaReadException,\n            SProcessInstanceNotFoundException, SProcessInstanceReadException, SObjectNotFoundException {\n        if (DataInstanceContainer.ACTIVITY_INSTANCE.name().equals(container.getType())) {\n            container = handleActivityContainer(containerHierarchy,\n                    getsFlowNodeInstance(container.getId(), isArchived));\n        } else if (DataInstanceContainer.MESSAGE_INSTANCE.name().equals(container.getType())) {\n            container = null;\n        } else if (DataInstanceContainer.PROCESS_INSTANCE.name().equals(container.getType())) {\n            container = handleProcessContainer(container.getId(), containerHierarchy, isArchived);\n        } else {\n            if (allowUnknownContainer) {\n                return null;\n            } else {\n                throw new SObjectNotFoundException(\"Unknown container type: \" + container.getType());\n            }\n        }\n        return container;\n    }\n\n    private DataContainer handleActivityContainer(List<DataContainer> containerHierarchy,\n            ActivityContainer flowNodeInstance) {\n        String containerType;\n        if (flowNodeInstance.parentActivityInstanceId > 0) {\n            containerType = DataInstanceContainer.ACTIVITY_INSTANCE.name();\n        } else {\n            containerType = DataInstanceContainer.PROCESS_INSTANCE.name();\n        }\n        DataContainer container = new DataContainer(flowNodeInstance.parentContainerId, containerType);\n        containerHierarchy.add(container);\n        if (flowNodeInstance.parentActivityInstanceId <= 0\n                && flowNodeInstance.parentContainerId == flowNodeInstance.rootContainerId) {\n            container = null;\n        }\n        return container;\n    }\n\n    private DataContainer handleProcessContainer(long id, List<DataContainer> containerHierarchy, boolean isArchived)\n            throws SProcessInstanceNotFoundException, SProcessInstanceReadException, SFlowNodeNotFoundException,\n            SBonitaReadException, SFlowNodeReadException {\n        final long callerId = getCallerId(id, isArchived);\n        if (callerId >= 0) {\n            ActivityContainer callerFlowNodeInstance = getsFlowNodeInstance(callerId, isArchived);\n            final SFlowNodeType callerType = callerFlowNodeInstance.type;\n            if (callerType != null && callerType.equals(SFlowNodeType.SUB_PROCESS)) {\n                final long callerProcessInstanceId = callerFlowNodeInstance.parentProcessInstanceId;\n                DataContainer container = new DataContainer(callerProcessInstanceId,\n                        DataInstanceContainer.PROCESS_INSTANCE.name());\n                containerHierarchy.add(container);\n                return container;\n            } else {\n                return null;\n            }\n        } else {\n            return null;\n        }\n    }\n\n    private ActivityContainer getSaFlowNodeInstance(long id) throws SBonitaReadException, SFlowNodeNotFoundException {\n        SAFlowNodeInstance flowNodeInstance;\n        flowNodeInstance = flowNodeInstanceService.getLastArchivedFlowNodeInstance(SAFlowNodeInstance.class, id);\n        if (flowNodeInstance == null) {\n            throw new SFlowNodeNotFoundException(id);\n        }\n        return new ActivityContainer(flowNodeInstance);\n    }\n\n    private ActivityContainer getsFlowNodeInstance(long id) throws SFlowNodeReadException, SFlowNodeNotFoundException {\n        SFlowNodeInstance flowNodeInstance;\n        flowNodeInstance = flowNodeInstanceService.getFlowNodeInstance(id);\n        return new ActivityContainer(flowNodeInstance);\n    }\n\n    private long getCallerId(long id, boolean isArchived)\n            throws SProcessInstanceNotFoundException, SProcessInstanceReadException, SBonitaReadException {\n        if (isArchived) {\n            return getArchivedCallerId(id);\n        } else {\n            return getCallerId(id);\n        }\n    }\n\n    private ActivityContainer getsFlowNodeInstance(long callerId, boolean isArchived)\n            throws SFlowNodeReadException, SFlowNodeNotFoundException, SBonitaReadException {\n        if (isArchived) {\n            return getSaFlowNodeInstance(callerId);\n        } else {\n            return getsFlowNodeInstance(callerId);\n        }\n    }\n\n    private long getCallerId(Long processInstanceId)\n            throws SProcessInstanceNotFoundException, SProcessInstanceReadException {\n        SProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);\n        return processInstance.getCallerId();\n    }\n\n    private long getArchivedCallerId(Long processInstanceId)\n            throws SProcessInstanceNotFoundException, SProcessInstanceReadException, SBonitaReadException {\n        SAProcessInstance processInstance = processInstanceService.getLastArchivedProcessInstance(processInstanceId);\n        if (processInstance == null) {\n            throw new SProcessInstanceNotFoundException(processInstanceId);\n        }\n        return processInstance.getCallerId();\n    }\n\n    private class ActivityContainer {\n\n        private long parentContainerId;\n        private long parentActivityInstanceId;\n        private long rootContainerId;\n        public SFlowNodeType type;\n        public long parentProcessInstanceId;\n\n        public ActivityContainer(SFlowNodeInstance flowNodeInstance) {\n            parentContainerId = flowNodeInstance.getParentContainerId();\n            parentActivityInstanceId = flowNodeInstance.getParentActivityInstanceId();\n            rootContainerId = flowNodeInstance.getRootContainerId();\n            type = flowNodeInstance.getType();\n            parentProcessInstanceId = flowNodeInstance.getParentProcessInstanceId();\n        }\n\n        public ActivityContainer(SAFlowNodeInstance flowNodeInstance) {\n            parentContainerId = flowNodeInstance.getParentContainerId();\n            parentActivityInstanceId = flowNodeInstance.getParentActivityInstanceId();\n            rootContainerId = flowNodeInstance.getRootContainerId();\n            type = flowNodeInstance.getType();\n            parentProcessInstanceId = flowNodeInstance.getParentProcessInstanceId();\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/event/PlatformStartedEvent.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.event;\n\nimport lombok.Data;\n\n/**\n * Spring event published when the Bonita Platform has finished to start.\n * <p>This event can be listened using Spring standards, for instance:\n *\n * <pre>\n * &#64;EventListener\n * public void handlePlatformStarted(PlatformStartedEvent event) { ... }\n * </pre>\n */\n@Data\npublic class PlatformStartedEvent {\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/AdvancedStartProcessValidator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.contract.validation.ContractValidator;\nimport org.bonitasoft.engine.bpm.contract.validation.ContractValidatorFactory;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SContractDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SContractViolationException;\nimport org.bonitasoft.engine.expression.ExpressionService;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class AdvancedStartProcessValidator {\n\n    private final ProcessDefinitionService processDefinitionService;\n    private final long processDefinitionId;\n\n    private ExpressionService expressionService;\n\n    public AdvancedStartProcessValidator(ProcessDefinitionService processDefinitionService, long processDefinitionId,\n            ExpressionService expressionService) {\n        this.processDefinitionService = processDefinitionService;\n        this.processDefinitionId = processDefinitionId;\n        this.expressionService = expressionService;\n    }\n\n    public List<String> validate(List<String> flowNodeNames, Map<String, Serializable> processContractInputs)\n            throws SBonitaException {\n        List<String> problems = new ArrayList<>();\n        if (flowNodeNames.isEmpty()) {\n            problems.add(\"The list of activity names to start cannot be empty!\");\n        }\n        List<String> foundFlowNodes = new ArrayList<>(flowNodeNames.size());\n        SProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(processDefinitionId);\n        problems.addAll(checkFlowNodesAreSupported(flowNodeNames, foundFlowNodes, processDefinition));\n        problems.addAll(checkForNotFoundFlowNodes(flowNodeNames, foundFlowNodes, processDefinition));\n        if (!problems.isEmpty()) {\n            //check contract only if flow nodes are ok\n            return problems;\n        }\n        problems.addAll(checkProcessContract(processContractInputs, processDefinition));\n        return problems;\n    }\n\n    private List<String> checkProcessContract(Map<String, Serializable> processContractInputs,\n            SProcessDefinition processDefinition) {\n        return validateContract(processContractInputs, processDefinition.getContract(), processDefinition.getName());\n    }\n\n    private List<String> validateContract(Map<String, Serializable> inputs, SContractDefinition contract,\n            String element) {\n        if (contract == null) {\n            return Collections.emptyList();\n        }\n        final ContractValidator validator = new ContractValidatorFactory().createContractValidator(expressionService);\n        try {\n            validator.validate(processDefinitionId, contract, inputs);\n        } catch (SContractViolationException e) {\n            return e.getExplanations().isEmpty() ? Collections.singletonList(e.getSimpleMessage() + \" on \" + element)\n                    : appendElement(e, element);\n        }\n        return Collections.emptyList();\n    }\n\n    private List<String> appendElement(SContractViolationException e, String element) {\n        ArrayList<String> strings = new ArrayList<>();\n        for (String explanation : e.getExplanations()) {\n            strings.add(explanation + \" on \" + element);\n        }\n        return strings;\n    }\n\n    private List<String> checkFlowNodesAreSupported(List<String> flowNodeNames, List<String> foundFlowNodes,\n            SProcessDefinition processDefinition) {\n        List<String> problems = new ArrayList<>();\n        for (SFlowNodeDefinition flowNode : processDefinition.getProcessContainer().getFlowNodes()) {\n            boolean invalidType = SFlowNodeType.BOUNDARY_EVENT.equals(flowNode.getType())\n                    || SFlowNodeType.SUB_PROCESS.equals(flowNode.getType())\n                    || SFlowNodeType.GATEWAY.equals(flowNode.getType());\n            if (flowNodeNames.contains(flowNode.getName())) {\n                foundFlowNodes.add(flowNode.getName());\n                if (invalidType) {\n                    problems.add(buildInvalidTypeErrorMessage(processDefinition, flowNode));\n                }\n            }\n        }\n        return problems;\n    }\n\n    private List<String> checkForNotFoundFlowNodes(List<String> flowNodeNames, List<String> foundFlowNodes,\n            SProcessDefinition processDefinition) {\n        List<String> problems = new ArrayList<>();\n        for (String flowNodeName : flowNodeNames) {\n            if (!foundFlowNodes.contains(flowNodeName)) {\n                problems.add(buildFlowNodeNotFoundErroMessage(processDefinition, flowNodeName));\n            }\n        }\n        return problems;\n    }\n\n    private String buildInvalidTypeErrorMessage(SProcessDefinition processDefinition, SFlowNodeDefinition flowNode) {\n        return \"'\" +\n                flowNode.getName() +\n                \"' is not a valid start point for the process \" +\n                buildProcessContext(processDefinition) +\n                \" You cannot start a process from a gateway, a boundary event or an event sub-process\";\n    }\n\n    private String buildFlowNodeNotFoundErroMessage(SProcessDefinition processDefinition, String flowNodeName) {\n        return \"No flownode named '\" +\n                flowNodeName +\n                \"' was found in the process\" +\n                buildProcessContext(processDefinition);\n    }\n\n    private String buildProcessContext(SProcessDefinition processDefinition) {\n        return \"<id: \" +\n                processDefinitionId +\n                \", name: \" +\n                processDefinition.getName() +\n                \", version: \" +\n                processDefinition.getVersion() +\n                \">.\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/ContainerExecutor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic interface ContainerExecutor {\n\n    /**\n     * Method called to notify this container executor that a child reached the given state\n     *\n     * @param processDefinitionId\n     *        The identifier of the process definition\n     * @param parentId\n     *        The identifier of the parent of the flow node\n     * @param childFlowNode\n     * @throws SBonitaException\n     */\n    void childFinished(long processDefinitionId, long parentId, SFlowNodeInstance childFlowNode)\n            throws SBonitaException;\n\n    /**\n     * Execute a flow node in the context of this container executor\n     *\n     * @param flowNodeInstance\n     *        The flow node instance\n     * @param executerId\n     *        The identifier of the user which execute the flow node\n     * @param executerSubstituteId\n     *        The identifier of the delegated user which execute the flow node\n     * @return The new state of the flow node after execution\n     * @throws SFlowNodeReadException\n     * @throws SFlowNodeExecutionException\n     *         Throw if there is an error when execute the flow node\n     */\n    FlowNodeState executeFlowNode(SFlowNodeInstance flowNodeInstance,\n            final Long executerId, final Long executerSubstituteId)\n            throws SFlowNodeReadException, SFlowNodeExecutionException;\n\n    /**\n     * @return The handled type\n     */\n    String getHandledType();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/ContainerRegistry.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.execution.work.BPMWorkFactory;\nimport org.bonitasoft.engine.work.SWorkRegisterException;\nimport org.bonitasoft.engine.work.WorkService;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n * @author Matthieu Chaffotte\n */\npublic class ContainerRegistry {\n\n    private final Map<String, ContainerExecutor> executors = new HashMap<String, ContainerExecutor>(2);\n\n    private final WorkService workService;\n    private final BPMWorkFactory workFactory;\n\n    public ContainerRegistry(final WorkService workService, BPMWorkFactory workFactory) {\n        super();\n        this.workService = workService;\n        this.workFactory = workFactory;\n    }\n\n    public void addContainerExecutor(final ContainerExecutor containerExecutor) {\n        executors.put(containerExecutor.getHandledType(), containerExecutor);\n    }\n\n    public void notifyChildFinished(SFlowNodeInstance flowNodeInstance) throws SWorkRegisterException {\n        workService.registerWork(workFactory.createNotifyChildFinishedWorkDescriptor(flowNodeInstance));\n    }\n\n    public void nodeReachedState(SFlowNodeInstance flowNodeInstance)\n            throws SBonitaException {\n        final ContainerExecutor containerExecutor = executors.get(flowNodeInstance.getParentContainerType().name());\n        if (containerExecutor != null) {\n            containerExecutor.childFinished(flowNodeInstance.getProcessDefinitionId(),\n                    flowNodeInstance.getParentContainerId(), flowNodeInstance);\n        } else {\n            throw new SActivityExecutionException(\n                    \"There is no container executor for the container \" + flowNodeInstance.getParentContainerId()\n                            + \" having the type \" + flowNodeInstance.getParentContainerType());\n        }\n    }\n\n    private ContainerExecutor getContainerExecutor(final String containerType) {\n        return executors.get(containerType);\n    }\n\n    public void executeFlowNode(SFlowNodeInstance flowNodeInstance) throws SWorkRegisterException {\n        workService.registerWork(workFactory.createExecuteFlowNodeWorkDescriptor(flowNodeInstance));\n    }\n\n    // FIXME, we should never execute a flow node directly, all call to this method should be replaced by a work\n    @Deprecated\n    public void executeFlowNodeInSameThread(final SFlowNodeInstance flowNodeInstance,\n            final String containerType) throws SFlowNodeReadException, SFlowNodeExecutionException {\n        final ContainerExecutor containerExecutor = getContainerExecutor(containerType);\n        containerExecutor.executeFlowNode(flowNodeInstance, null, null);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/EvaluateExpression.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport java.io.Serializable;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Baptiste Mesta\n */\nfinal class EvaluateExpression implements TransactionContentWithResult<Serializable> {\n\n    private final SExpressionContext contextDependency;\n\n    private final SExpression expression;\n\n    private Serializable result;\n\n    private final ExpressionResolverService expressionResolverService;\n\n    public EvaluateExpression(final ExpressionResolverService expressionResolverService,\n            final SExpressionContext contextDependency,\n            final SExpression expression) {\n        this.expressionResolverService = expressionResolverService;\n        this.contextDependency = contextDependency;\n        this.expression = expression;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        result = (Serializable) expressionResolverService.evaluate(expression, contextDependency);\n    }\n\n    @Override\n    public Serializable getResult() {\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/Filter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport java.io.Serializable;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface Filter<E> extends Serializable {\n\n    boolean mustSelect(E element);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/FlowElementExecutor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface FlowElementExecutor {\n\n    void executeActivity(SProcessDefinition sDefinition, SFlowNodeInstance flowNodeInstance);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/FlowNodeExecutor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.api.states.StateCode;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\n\n/**\n * @author Baptiste Mesta\n * @author Yanyan Liu\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic interface FlowNodeExecutor extends ContainerExecutor {\n\n    /**\n     * @param flowNodeInstance\n     * @param executerId\n     * @param executerSubstituteId\n     * @return\n     * @throws SFlowNodeExecutionException\n     */\n    FlowNodeState stepForward(SFlowNodeInstance flowNodeInstance,\n            Long executerId, Long executerSubstituteId) throws SFlowNodeExecutionException;\n\n    /**\n     * force the state of a flow node toa particular state\n     *\n     * @param flowNodeInstanceId\n     * @param stateId\n     */\n    void setStateByStateId(long flowNodeInstanceId, int stateId) throws SActivityStateExecutionException;\n\n    /**\n     * @param childProcInst\n     * @param childState\n     * @param hasActionsToExecute\n     * @throws SBonitaException\n     * @since 6.1\n     */\n    void childReachedState(SProcessInstance childProcInst, ProcessInstanceState childState, boolean hasActionsToExecute)\n            throws SBonitaException;\n\n    /**\n     * @param processDefinition\n     *        the process definition of the SFlowNodeInstance on which to execute the state.\n     * @param flowNodeInstance\n     *        the <code>SFlowNodeInstance</code> whose state has to be executed\n     * @param state\n     *        the <code>FlowNodeState</code> to execute\n     * @return the next <code>FlowNodeState</code>, after executing current state\n     * @throws SActivityStateExecutionException\n     *         if an exception occurs when executing current state\n     * @throws SActivityExecutionException\n     *         if an exception occurs when retrieving next state\n     * @since 6.0\n     */\n    StateCode executeState(SProcessDefinition processDefinition, SFlowNodeInstance flowNodeInstance,\n            FlowNodeState state)\n            throws SActivityStateExecutionException, SActivityExecutionException;\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/FlowNodeExecutorImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier;\nimport static org.bonitasoft.engine.core.process.instance.model.SStateCategory.ABORTING;\n\nimport java.util.Optional;\nimport java.util.function.Supplier;\n\nimport org.bonitasoft.engine.SArchivingException;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.comment.api.SCommentAddException;\nimport org.bonitasoft.engine.core.process.comment.api.SCommentService;\nimport org.bonitasoft.engine.core.process.comment.api.SystemCommentType;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeModificationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.api.states.StateCode;\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowElementsContainerType;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SUserTaskInstanceBuilderFactory;\nimport org.bonitasoft.engine.dependency.model.ScopeType;\nimport org.bonitasoft.engine.execution.archive.BPMArchiverService;\nimport org.bonitasoft.engine.execution.state.FlowNodeStateManager;\nimport org.bonitasoft.engine.execution.work.BPMWorkFactory;\nimport org.bonitasoft.engine.mdc.FlowNodeInstanceMDC;\nimport org.bonitasoft.engine.mdc.MDCHelper;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.work.SWorkRegisterException;\nimport org.bonitasoft.engine.work.WorkService;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Baptiste Mesta\n * @author Yanyan Liu\n * @author Elias Ricken de Medeiros\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n * @author Matthieu Chaffotte\n */\n@Component(\"flowNodeExecutor\")\npublic class FlowNodeExecutorImpl implements FlowNodeExecutor {\n\n    private static final Logger LOG = LoggerFactory.getLogger(FlowNodeExecutorImpl.class);\n\n    private final FlowNodeStateManager flowNodeStateManager;\n    private final ActivityInstanceService activityInstanceService;\n    private final ContainerRegistry containerRegistry;\n    private final ProcessDefinitionService processDefinitionService;\n    private final SCommentService commentService;\n    private final ClassLoaderService classLoaderService;\n    private final WorkService workService;\n    private final BPMWorkFactory workFactory;\n    private final ProcessInstanceInterruptor processInstanceInterruptor;\n    private final BPMArchiverService bpmArchiverService;\n\n    public FlowNodeExecutorImpl(final FlowNodeStateManager flowNodeStateManager,\n            final ActivityInstanceService activityInstanceManager,\n            final ContainerRegistry containerRegistry,\n            final ProcessDefinitionService processDefinitionService,\n            final SCommentService commentService,\n            final ClassLoaderService classLoaderService,\n            final WorkService workService, BPMWorkFactory workFactory,\n            final ProcessInstanceInterruptor processInstanceInterruptor,\n            final BPMArchiverService bpmArchiverService) {\n        this.flowNodeStateManager = flowNodeStateManager;\n        activityInstanceService = activityInstanceManager;\n        this.containerRegistry = containerRegistry;\n        this.classLoaderService = classLoaderService;\n        this.workService = workService;\n        this.workFactory = workFactory;\n        this.processInstanceInterruptor = processInstanceInterruptor;\n        containerRegistry.addContainerExecutor(this);\n        this.processDefinitionService = processDefinitionService;\n        this.commentService = commentService;\n        this.bpmArchiverService = bpmArchiverService;\n\n    }\n\n    @Override\n    public StateCode executeState(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance,\n            final FlowNodeState state)\n            throws SActivityStateExecutionException, SActivityExecutionException {\n        try {\n            // if state is part of normal state and the flowNode state category is aborting or canceling it's not necessary to execute the state\n            StateCode stateCode = StateCode.DONE;\n            if (state.getStateCategory().equals(flowNodeInstance.getStateCategory())) {\n                stateCode = state.execute(processDefinition, flowNodeInstance);\n                // Add a system comment for Human task only\n                addSystemComment(flowNodeInstance, state);\n            }\n            return stateCode;\n        } catch (final SCommentAddException e) {\n            throw new SActivityExecutionException(e);\n        }\n    }\n\n    private void addSystemComment(final SFlowNodeInstance flowNodeInstance, final FlowNodeState state)\n            throws SCommentAddException {\n        if (commentService.isCommentEnabled(SystemCommentType.STATE_CHANGE)\n                && state.mustAddSystemComment(flowNodeInstance)) {\n            commentService.addSystemComment(flowNodeInstance.getRootContainerId(),\n                    state.getSystemComment(flowNodeInstance));\n        }\n    }\n\n    @Override\n    public FlowNodeState stepForward(SFlowNodeInstance flowNodeInstance, Long executerId, Long executerSubstituteId)\n            throws SFlowNodeExecutionException {\n        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        Supplier<FlowNodeInstanceMDC> mdc = () -> new FlowNodeInstanceMDC(flowNodeInstance.getId(),\n                Optional.ofNullable(executerId),\n                Optional.ofNullable(executerSubstituteId),\n                flowNodeInstance.getProcessDefinitionId(),\n                flowNodeInstance.getParentProcessInstanceId(),\n                flowNodeInstance.getRootProcessInstanceId());\n        return MDCHelper.tryWithMDC(mdc, () -> {\n            try {\n                final long processDefinitionId = flowNodeInstance\n                        .getLogicalGroup(BuilderFactory.get(SUserTaskInstanceBuilderFactory.class)\n                                .getProcessDefinitionIndex());\n                final ClassLoader localClassLoader = classLoaderService.getClassLoader(\n                        identifier(ScopeType.PROCESS, processDefinitionId));\n                Thread.currentThread().setContextClassLoader(localClassLoader);\n\n                if (!flowNodeInstance.isStateExecuting()) {\n                    bpmArchiverService.archiveFlowNodeInstance(flowNodeInstance);\n                    setExecutedBy(executerId, flowNodeInstance);\n                    setExecutedBySubstitute(executerSubstituteId, flowNodeInstance);\n                }\n\n                final SProcessDefinition processDefinition = processDefinitionService\n                        .getProcessDefinition(processDefinitionId);\n                final FlowNodeState nextState = executeStateAndReturnNextState(flowNodeInstance, processDefinition);\n                registerWorkIfUnstableOrTerminal(nextState, flowNodeInstance);\n                return nextState;\n            } catch (final SFlowNodeExecutionException e) {\n                throw e;\n            } catch (final SBonitaException e) {\n                throw new SFlowNodeExecutionException(e);\n            } finally {\n                Thread.currentThread().setContextClassLoader(contextClassLoader);\n            }\n        });\n    }\n\n    /**\n     * Executes the current state, and returns the next step, if applicable.\n     * In a normal case, the normal next state is returned.\n     * If execution of the current state returns StateCode.EXECUTING (meaning the execution of the state is not\n     * finished)\n     * then null is returned (meaning we stay on the same state, some background work will trigger the next state\n     * later).\n     */\n    private FlowNodeState executeStateAndReturnNextState(final SFlowNodeInstance sFlowNodeInstance,\n            final SProcessDefinition processDefinition)\n            throws SActivityStateExecutionException, SActivityExecutionException, SFlowNodeModificationException {\n        final StateCode stateCode = executeState(processDefinition, sFlowNodeInstance,\n                flowNodeStateManager.getState(sFlowNodeInstance.getStateId()));\n        switch (stateCode) {\n            case DONE:\n                return getNextNormalState(sFlowNodeInstance, processDefinition);\n            case EXECUTING:\n            default:\n                // the state is still executing set the executing flag\n                activityInstanceService.setExecuting(sFlowNodeInstance);\n                return null;\n        }\n    }\n\n    private FlowNodeState getNextNormalState(final SFlowNodeInstance sFlowNodeInstance,\n            final SProcessDefinition processDefinition)\n            throws SActivityExecutionException,\n            SFlowNodeModificationException {\n        final FlowNodeState state = flowNodeStateManager.getNextState(processDefinition, sFlowNodeInstance,\n                sFlowNodeInstance.getStateId());\n        if (sFlowNodeInstance.getStateId() != state.getId()) {\n            // this also unset the executing flag\n            activityInstanceService.setState(sFlowNodeInstance, state);\n        }\n        return state;\n    }\n\n    private void registerWorkIfUnstableOrTerminal(FlowNodeState nextState, SFlowNodeInstance flowNodeInstance)\n            throws SWorkRegisterException {\n        if (flowNodeInstance.isStateExecuting() || nextState == null) {\n            LOG.debug(\"No work to register on state {} for flowNode {}\", nextState, flowNodeInstance);\n            return;\n        }\n        if (!nextState.isStable()) {\n            registerExecuteFlowNodeWork(flowNodeInstance);\n            return;\n        }\n        if (nextState.isTerminal()) {\n            registerNotifyFinishWork(flowNodeInstance);\n            return;\n        }\n        // State is stable, nothing to do.\n        LOG.debug(\"No work to register on state {} for flowNode {}\", nextState, flowNodeInstance);\n    }\n\n    private void registerExecuteFlowNodeWork(SFlowNodeInstance sFlowNodeInstance) throws SWorkRegisterException {\n        workService.registerWork(workFactory.createExecuteFlowNodeWorkDescriptor(sFlowNodeInstance));\n    }\n\n    private void setExecutedBySubstitute(final Long executerSubstituteId, final SFlowNodeInstance sFlowNodeInstance)\n            throws SFlowNodeModificationException {\n        if (isNotNullOrEmptyAndDifferentOf(sFlowNodeInstance.getExecutedBySubstitute(), executerSubstituteId)) {\n            activityInstanceService.setExecutedBySubstitute(sFlowNodeInstance, executerSubstituteId);\n        }\n    }\n\n    private void setExecutedBy(final Long executerId, final SFlowNodeInstance sFlowNodeInstance)\n            throws SFlowNodeModificationException {\n        if (isNotNullOrEmptyAndDifferentOf(sFlowNodeInstance.getExecutedBy(), executerId)) {\n            activityInstanceService.setExecutedBy(sFlowNodeInstance, executerId);\n        }\n    }\n\n    private boolean isNotNullOrEmptyAndDifferentOf(final long executerId, final Long newExecuterId) {\n        return newExecuterId != null && newExecuterId > 0 && executerId != newExecuterId;\n    }\n\n    @Override\n    public void setStateByStateId(long flowNodeInstanceId, int stateId) throws SActivityStateExecutionException {\n        final FlowNodeState state = flowNodeStateManager.getState(stateId);\n        try {\n            final SFlowNodeInstance sFlowNodeInstance = activityInstanceService.getFlowNodeInstance(flowNodeInstanceId);\n            bpmArchiverService.archiveFlowNodeInstance(sFlowNodeInstance);\n            activityInstanceService.setState(sFlowNodeInstance, state);\n            if (state.isTerminal()) {\n                if (hasChildren(sFlowNodeInstance)) {\n                    processInstanceInterruptor.interruptChildrenOfFlowNodeInstance(sFlowNodeInstance, ABORTING);\n                } else {\n                    registerNotifyFinishWork(sFlowNodeInstance);\n                }\n                if (sFlowNodeInstance instanceof SActivityInstance) {\n                    flowNodeStateManager.getStateBehaviors().interruptAttachedBoundaryEvent(\n                            processDefinitionService.getProcessDefinition(sFlowNodeInstance.getProcessDefinitionId()),\n                            ((SActivityInstance) sFlowNodeInstance), ABORTING);\n                }\n            }\n        } catch (final SBonitaException e) {\n            throw new SActivityStateExecutionException(e);\n        }\n    }\n\n    private boolean hasChildren(SFlowNodeInstance sFlowNodeInstance) {\n        return sFlowNodeInstance.getTokenCount() > 0;\n    }\n\n    private void registerNotifyFinishWork(SFlowNodeInstance sFlowNodeInstance) throws SWorkRegisterException {\n        workService.registerWork(\n                workFactory.createNotifyChildFinishedWorkDescriptor(sFlowNodeInstance));\n    }\n\n    @Override\n    public void childFinished(final long processDefinitionId, final long parentId, SFlowNodeInstance childFlowNode)\n            throws SFlowNodeNotFoundException, SFlowNodeReadException, SProcessDefinitionNotFoundException,\n            SBonitaReadException, SArchivingException, SFlowNodeModificationException, SFlowNodeExecutionException,\n            SWorkRegisterException {\n        bpmArchiverService.archiveAndDeleteFlowNodeInstance(childFlowNode, processDefinitionId);\n        final SActivityInstance activityInstanceParent = (SActivityInstance) activityInstanceService\n                .getFlowNodeInstance(parentId);\n        decrementToken(activityInstanceParent);\n        final SProcessDefinition sProcessDefinition = processDefinitionService\n                .getProcessDefinition(processDefinitionId);\n        final FlowNodeState state = flowNodeStateManager.getState(activityInstanceParent.getStateId());\n        final boolean shouldContinueParent = state.notifyChildFlowNodeHasFinished(sProcessDefinition,\n                activityInstanceParent, childFlowNode);\n        if (shouldContinueParent) {\n            // it should never happen, because the terminal state never waits children to finish\n            if (activityInstanceParent.isTerminal()) {\n                LOG.warn(\"Parent '{}' (id={}, processInstance={}, processDefinition={}) is already terminal \"\n                        + \"when child '{}' (id={}) finished — this should never happen\",\n                        activityInstanceParent.getName(), activityInstanceParent.getId(),\n                        childFlowNode.getRootProcessInstanceId(), processDefinitionId,\n                        childFlowNode.getName(), childFlowNode.getId());\n                registerNotifyFinishWork(activityInstanceParent);\n            } else {\n                stepForward(activityInstanceParent, null, null);\n            }\n        } else {\n            LOG.debug(\"the child flownode {} of parent flownode {} finished, but there are other children remaining\",\n                    childFlowNode, activityInstanceParent);\n        }\n    }\n\n    private void decrementToken(final SActivityInstance sActivityInstance) throws SFlowNodeModificationException {\n        final int tokenCount = sActivityInstance.getTokenCount() - 1;\n        activityInstanceService.setTokenCount(sActivityInstance, tokenCount);\n    }\n\n    @Override\n    public void childReachedState(final SProcessInstance childProcInst, final ProcessInstanceState childState,\n            final boolean hasActionsToExecute) throws SBonitaException {\n        if (isTerminalState(childState) && childProcInst.getCallerId() > 0) {\n            final SActivityInstance callActivityInstance = activityInstanceService\n                    .getActivityInstance(childProcInst.getCallerId());\n            decrementToken(callActivityInstance);\n            executeFlowNodeIfHasActionsToExecute(hasActionsToExecute, callActivityInstance);\n        }\n\n    }\n\n    private void executeFlowNodeIfHasActionsToExecute(final boolean hasActionsToExecute,\n            final SActivityInstance callActivityInstance)\n            throws SWorkRegisterException {\n        if (!hasActionsToExecute) {\n            containerRegistry.executeFlowNode(callActivityInstance);\n        }\n    }\n\n    private boolean isTerminalState(final ProcessInstanceState childState) {\n        return ProcessInstanceState.COMPLETED.equals(childState) || ProcessInstanceState.CANCELLED.equals(childState)\n                || ProcessInstanceState.ABORTED.equals(childState);\n    }\n\n    @Override\n    public String getHandledType() {\n        return SFlowElementsContainerType.FLOWNODE.name();\n    }\n\n    @Override\n    public FlowNodeState executeFlowNode(SFlowNodeInstance flowNodeInstance, Long executerId, Long executerSubstituteId)\n            throws SFlowNodeExecutionException {\n        return stepForward(flowNodeInstance, executerId, executerSubstituteId);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/FlowNodeIdFilter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition;\n\n/**\n * Created by Vincent Elcrin\n * Date: 17/12/13\n * Time: 17:34\n */\npublic class FlowNodeIdFilter implements Filter<SFlowNodeDefinition> {\n\n    private final List<Long> ids;\n\n    public FlowNodeIdFilter(long id) {\n        this.ids = Collections.singletonList(id);\n    }\n\n    public FlowNodeIdFilter(List<Long> ids) {\n        this.ids = ids;\n    }\n\n    @Override\n    public boolean mustSelect(SFlowNodeDefinition flowNode) {\n        return ids.contains(flowNode.getId());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/FlowNodeNameFilter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class FlowNodeNameFilter implements Filter<SFlowNodeDefinition> {\n\n    private final List<String> flowNodeNames;\n\n    public FlowNodeNameFilter(List<String> flowNodeNames) {\n        this.flowNodeNames = flowNodeNames;\n    }\n\n    @Override\n    public boolean mustSelect(SFlowNodeDefinition element) {\n        return flowNodeNames.contains(element.getName());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/FlowNodeSelector.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SSubProcessDefinition;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class FlowNodeSelector implements Serializable {\n\n    private final Filter<SFlowNodeDefinition> selector;\n    private final SProcessDefinition definition;\n    private long subProcessDefinitionId = -1;\n\n    public boolean isEventSubProcess() {\n        return subProcessDefinitionId > 0;\n    }\n\n    public FlowNodeSelector(SProcessDefinition definition, Filter<SFlowNodeDefinition> filter) {\n        this.definition = definition;\n        this.selector = filter;\n    }\n\n    public FlowNodeSelector(SProcessDefinition definition, Filter<SFlowNodeDefinition> filter,\n            final long subProcessDefinitionId) {\n        this(definition, filter);\n        this.subProcessDefinitionId = subProcessDefinitionId;\n    }\n\n    public List<SFlowNodeDefinition> getFilteredElements() {\n        SFlowElementContainerDefinition container = getContainer();\n        ArrayList<SFlowNodeDefinition> selectedFlowNodes = new ArrayList<>();\n        for (SFlowNodeDefinition flowNodeDefinition : container.getFlowNodes()) {\n            if (selector.mustSelect(flowNodeDefinition)) {\n                selectedFlowNodes.add(flowNodeDefinition);\n            }\n        }\n        return selectedFlowNodes;\n    }\n\n    public SFlowElementContainerDefinition getContainer() {\n        if (subProcessDefinitionId == -1) {\n            return definition.getProcessContainer();\n        }\n        final SSubProcessDefinition subProcDef = (SSubProcessDefinition) definition.getProcessContainer()\n                .getFlowNode(subProcessDefinitionId);\n        return subProcDef.getSubProcessContainer();\n    }\n\n    public SProcessDefinition getProcessDefinition() {\n        return definition;\n    }\n\n    public long getSubProcessDefinitionId() {\n        return subProcessDefinitionId;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/FlowNodeStateManagerImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport static org.bonitasoft.engine.core.process.instance.model.SStateCategory.NORMAL;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport org.bonitasoft.engine.bpm.model.impl.BPMInstancesCreator;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.execution.state.FlowNodeStateManager;\nimport org.bonitasoft.engine.execution.transition.FlowNodeStateSequences;\nimport org.springframework.stereotype.Component;\n\n/**\n * Default implementation of the activity state manager.\n *\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Yanyan Liu\n * @author Zhang Bole\n * @author Celine Souchet\n */\n\n@Component(\"flowNodeStateManager\")\npublic class FlowNodeStateManagerImpl implements FlowNodeStateManager {\n\n    protected final Map<Integer, FlowNodeState> allStates;\n    protected StateBehaviors stateBehaviors;\n    private final Map<SFlowNodeType, FlowNodeStateSequences> flowNodeStateSequences;\n\n    public FlowNodeStateManagerImpl(\n            BPMInstancesCreator bpmInstancesCreator,\n            StateBehaviors stateBehaviors,\n            List<FlowNodeStateSequences> flowNodeStateSequences,\n            List<FlowNodeState> allStates) {\n        this.stateBehaviors = stateBehaviors;\n        this.flowNodeStateSequences = flowNodeStateSequences.stream()\n                .collect(Collectors.toMap(FlowNodeStateSequences::getFlowNodeType, e -> e));\n        this.allStates = allStates.stream().collect(Collectors.toMap(FlowNodeState::getId, s -> s));\n        bpmInstancesCreator.setStateManager(this);\n    }\n\n    @Override\n    public StateBehaviors getStateBehaviors() {\n        return stateBehaviors;\n    }\n\n    @Override\n    public FlowNodeState getNextState(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance, final int currentStateId)\n            throws SActivityExecutionException {\n        FlowNodeState currentState = getState(currentStateId);\n        do {\n            currentState = getNextStateToHandle(flowNodeInstance, currentState);\n        } while (!currentState.shouldExecuteState(processDefinition, flowNodeInstance));\n        return currentState;\n    }\n\n    private FlowNodeState getNextStateToHandle(final SFlowNodeInstance flowNodeInstance,\n            final FlowNodeState currentState) throws SActivityExecutionException {\n        FlowNodeStateSequences stateSequence = this.flowNodeStateSequences.get(flowNodeInstance.getType());\n        SStateCategory stateCategory = flowNodeInstance.getStateCategory();\n        if (currentState.getStateCategory() != stateCategory) {\n            // the state category changed (flow node was aborted or cancelled), get the first state of the corresponding state category\n            return stateSequence.getFirstState(stateCategory);\n        } else {\n            FlowNodeState nextState = stateSequence.getStateAfter(stateCategory, currentState.getId());\n            if (nextState == null) {\n                throw new SActivityExecutionException(\n                        \"no state found after \" + allStates.get(currentState.getId()).getClass().getSimpleName()\n                                + \" for \" + flowNodeInstance.getClass().getSimpleName() + \" in state category \"\n                                + flowNodeInstance.getStateCategory() + \" activity id=\" + flowNodeInstance.getId());\n            }\n            return nextState;\n        }\n    }\n\n    @Override\n    public FlowNodeState getState(final int stateId) {\n        return allStates.get(stateId);\n    }\n\n    @Override\n    public Set<String> getSupportedState(final SFlowNodeType nodeType) {\n        return flowNodeStateSequences.get(nodeType).getSupportedStates();\n    }\n\n    public FlowNodeState getFirstState(SFlowNodeType nodeType) {\n        return flowNodeStateSequences.get(nodeType).getFirstState(NORMAL);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/ProcessExecutor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorDefinitionWithInputValues;\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SContractViolationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceCreationException;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\n\n/**\n * @author Baptiste Mesta\n * @author Yanyan Liu\n * @author Elias Ricken de Medeiros\n * @author Hongwen Zang\n * @author Celine Souchet\n * @author Matthieu Chaffotte\n */\npublic interface ProcessExecutor extends ContainerExecutor {\n\n    SProcessInstance start(long processDefinitionId, long targetSFlowNodeDefinitionId, long starterId,\n            long starterSubstituteId,\n            SExpressionContext expressionContext, List<SOperation> operations,\n            long callerId, long subProcessDefinitionId, Map<String, Serializable> processInputs)\n            throws SProcessInstanceCreationException, SContractViolationException;\n\n    SProcessInstance start(long starterId, long starterSubstituteId, List<SOperation> operations,\n            Map<String, Object> context, List<ConnectorDefinitionWithInputValues> connectorsWithInput,\n            FlowNodeSelector selector,\n            Map<String, Serializable> processInputs)\n            throws SProcessInstanceCreationException, SContractViolationException;\n\n    boolean registerConnectorsToExecute(SProcessDefinition processDefinition, SProcessInstance sInstance,\n            ConnectorEvent activationEvent,\n            FlowNodeSelector selector) throws SBonitaException;\n\n    SProcessInstance startElements(final SProcessInstance sProcessInstance, FlowNodeSelector selector)\n            throws SProcessInstanceCreationException,\n            SFlowNodeExecutionException, SFlowNodeReadException;\n\n    void handleProcessCompletion(final SProcessDefinition sProcessDefinition, final SProcessInstance sProcessInstance,\n            final boolean hasActionsToExecute)\n            throws SBonitaException;\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/ProcessExecutorImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Set;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.SArchivingException;\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.bonitasoft.engine.bpm.connector.ConnectorDefinition;\nimport org.bonitasoft.engine.bpm.connector.ConnectorDefinitionWithInputValues;\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.bpm.contract.validation.ContractValidator;\nimport org.bonitasoft.engine.bpm.contract.validation.ContractValidatorFactory;\nimport org.bonitasoft.engine.bpm.document.DocumentValue;\nimport org.bonitasoft.engine.bpm.model.impl.BPMInstancesCreator;\nimport org.bonitasoft.engine.bpm.process.ActivationState;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.business.data.BusinessDataRepository;\nimport org.bonitasoft.engine.business.data.proxy.ServerProxyfier;\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.classloader.SClassLoaderException;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectCreationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.commons.exceptions.ScopedException;\nimport org.bonitasoft.engine.core.connector.ConnectorInstanceService;\nimport org.bonitasoft.engine.core.connector.ConnectorResult;\nimport org.bonitasoft.engine.core.connector.ConnectorService;\nimport org.bonitasoft.engine.core.connector.exception.SConnectorException;\nimport org.bonitasoft.engine.core.connector.exception.SConnectorInstanceReadException;\nimport org.bonitasoft.engine.core.contract.data.ContractDataService;\nimport org.bonitasoft.engine.core.contract.data.SContractDataCreationException;\nimport org.bonitasoft.engine.core.document.api.DocumentService;\nimport org.bonitasoft.engine.core.document.api.impl.DocumentHelper;\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.OperationService;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionException;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.core.process.definition.model.SBusinessDataDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SConnectorDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SContractDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SDocumentDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SDocumentListDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.definition.model.SGatewayDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SGatewayType;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SEndEventDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.GatewayInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.RefBusinessDataService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SContractViolationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceCreationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceModificationException;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowElementsContainerType;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SGatewayInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.business.data.SRefBusinessDataInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SThrowEventInstance;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.dependency.model.ScopeType;\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.events.model.HandlerRegistrationException;\nimport org.bonitasoft.engine.events.model.SEvent;\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\nimport org.bonitasoft.engine.execution.archive.BPMArchiverService;\nimport org.bonitasoft.engine.execution.event.EventsHandler;\nimport org.bonitasoft.engine.execution.flowmerger.FlowNodeTransitionsWrapper;\nimport org.bonitasoft.engine.execution.handler.SProcessInstanceHandler;\nimport org.bonitasoft.engine.execution.work.BPMWorkFactory;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionService;\nimport org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.mdc.MDCConstants;\nimport org.bonitasoft.engine.operation.Operation;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.resources.BARResourceType;\nimport org.bonitasoft.engine.resources.ProcessResourcesService;\nimport org.bonitasoft.engine.service.ModelConvertor;\nimport org.bonitasoft.engine.work.SWorkRegisterException;\nimport org.bonitasoft.engine.work.WorkService;\nimport org.slf4j.MDC;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Yanyan Liu\n * @author Elias Ricken de Medeiros\n * @author Hongwen Zang\n * @author Celine Souchet\n */\n@Slf4j\npublic class ProcessExecutorImpl implements ProcessExecutor {\n\n    protected final ActivityInstanceService activityInstanceService;\n    protected final ProcessInstanceService processInstanceService;\n    protected final ClassLoaderService classLoaderService;\n    protected final ExpressionResolverService expressionResolverService;\n    protected final ExpressionService expressionService;\n    protected final ConnectorService connectorService;\n    protected final BPMInstancesCreator bpmInstancesCreator;\n    protected final EventsHandler eventsHandler;\n    private final FlowNodeExecutor flowNodeExecutor;\n    private final WorkService workService;\n    private final ProcessDefinitionService processDefinitionService;\n    private final GatewayInstanceService gatewayInstanceService;\n    private final OperationService operationService;\n    private final ProcessResourcesService processResourcesService;\n    private final ConnectorInstanceService connectorInstanceService;\n    private final TransitionEvaluator transitionEvaluator;\n    private final ContractDataService contractDataService;\n    private final BusinessDataRepository businessDataRepository;\n    private final RefBusinessDataService refBusinessDataService;\n    private final DocumentHelper documentHelper;\n    private final BPMWorkFactory workFactory;\n    private final BPMArchiverService bpmArchiverService;\n    private final ProcessStarterVerifier processStarterVerifier;\n\n    public ProcessExecutorImpl(final ActivityInstanceService activityInstanceService,\n            final ProcessInstanceService processInstanceService, final FlowNodeExecutor flowNodeExecutor,\n            final WorkService workService,\n            final ProcessDefinitionService processDefinitionService,\n            final GatewayInstanceService gatewayInstanceService,\n            final ProcessResourcesService processResourcesService, final ConnectorService connectorService,\n            final ConnectorInstanceService connectorInstanceService, final ClassLoaderService classLoaderService,\n            final OperationService operationService,\n            final ExpressionResolverService expressionResolverService, final ExpressionService expressionService,\n            final EventService eventService,\n            final Map<String, SProcessInstanceHandler<SEvent>> handlers, final DocumentService documentService,\n            final ContainerRegistry containerRegistry, final BPMInstancesCreator bpmInstancesCreator,\n            final EventsHandler eventsHandler,\n            final BusinessDataRepository businessDataRepository,\n            final RefBusinessDataService refBusinessDataService, final TransitionEvaluator transitionEvaluator,\n            final ContractDataService contractDataService, BPMWorkFactory workFactory,\n            BPMArchiverService bpmArchiverService, final ProcessStarterVerifier processStarterVerifier) {\n        super();\n        this.activityInstanceService = activityInstanceService;\n        this.processInstanceService = processInstanceService;\n        this.processResourcesService = processResourcesService;\n        this.connectorInstanceService = connectorInstanceService;\n        this.flowNodeExecutor = flowNodeExecutor;\n        this.workService = workService;\n        this.processDefinitionService = processDefinitionService;\n        this.gatewayInstanceService = gatewayInstanceService;\n        this.connectorService = connectorService;\n        this.classLoaderService = classLoaderService;\n        this.operationService = operationService;\n        this.expressionResolverService = expressionResolverService;\n        this.expressionService = expressionService;\n        this.bpmInstancesCreator = bpmInstancesCreator;\n        this.eventsHandler = eventsHandler;\n        this.transitionEvaluator = transitionEvaluator;\n        this.businessDataRepository = businessDataRepository;\n        this.refBusinessDataService = refBusinessDataService;\n        this.contractDataService = contractDataService;\n        this.workFactory = workFactory;\n        this.bpmArchiverService = bpmArchiverService;\n        this.processStarterVerifier = processStarterVerifier;\n        documentHelper = new DocumentHelper(documentService, processDefinitionService, processInstanceService);\n        //FIXME There is responsibility issue the circular dependencies must be fixed next time.\n        eventsHandler.setProcessExecutor(this);\n        for (final Entry<String, SProcessInstanceHandler<SEvent>> handler : handlers.entrySet()) {\n            try {\n                eventService.addHandler(handler.getKey(), handler.getValue());\n            } catch (final HandlerRegistrationException e) {\n                log.warn(e.getMessage());\n                log.debug(\"\", e);\n            }\n        }\n        containerRegistry.addContainerExecutor(this);\n    }\n\n    @Override\n    public FlowNodeState executeFlowNode(SFlowNodeInstance flowNodeInstance, Long executerId, Long executerSubstituteId)\n            throws SFlowNodeExecutionException {\n        return flowNodeExecutor.stepForward(flowNodeInstance, executerId, executerSubstituteId);\n    }\n\n    private SConnectorInstance getNextConnectorInstance(final SProcessInstance processInstance,\n            final ConnectorEvent event)\n            throws SConnectorInstanceReadException {\n        final List<SConnectorInstance> connectorInstances = connectorInstanceService.getConnectorInstances(\n                processInstance.getId(),\n                SConnectorInstance.PROCESS_TYPE, event, 0, 1, ConnectorService.TO_BE_EXECUTED);\n        return connectorInstances.size() == 1 ? connectorInstances.get(0) : null;\n    }\n\n    @Override\n    public boolean registerConnectorsToExecute(final SProcessDefinition processDefinition,\n            final SProcessInstance sProcessInstance, final ConnectorEvent activationEvent,\n            final FlowNodeSelector selectorForConnectorOnEnter) throws SBonitaException {\n        final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer();\n        final long processDefinitionId = processDefinition.getId();\n        final List<SConnectorDefinition> connectors = processContainer.getConnectors(activationEvent);\n        if (connectors.size() > 0) {\n            SConnectorInstance nextConnectorInstance;\n            nextConnectorInstance = getNextConnectorInstance(sProcessInstance, activationEvent);\n            if (nextConnectorInstance != null) {\n                // TODO: extract this search algorithm in a dedicated method:\n                for (final SConnectorDefinition sConnectorDefinition : connectors) {\n                    if (sConnectorDefinition.getName().equals(nextConnectorInstance.getName())) {\n                        workService.registerWork(workFactory.createExecuteConnectorOfProcessDescriptor(\n                                processDefinitionId, sProcessInstance.getId(),\n                                sProcessInstance.getRootProcessInstanceId(), nextConnectorInstance.getId(),\n                                sConnectorDefinition.getConnectorId(),\n                                sConnectorDefinition.getName(),\n                                activationEvent,\n                                selectorForConnectorOnEnter));\n                        return true;\n                    }\n                }\n            }\n        }\n        return false;\n    }\n\n    private List<SFlowNodeInstance> initializeFirstExecutableElements(final SProcessInstance sProcessInstance,\n            final FlowNodeSelector selector) {\n        try {\n            final List<SFlowNodeDefinition> flownNodeDefinitions = selector.getFilteredElements();\n            long rootProcessInstanceId = sProcessInstance.getRootProcessInstanceId();\n            if (rootProcessInstanceId <= 0) {\n                rootProcessInstanceId = sProcessInstance.getId();\n            }\n            return bpmInstancesCreator.createFlowNodeInstances(selector.getProcessDefinition().getId(),\n                    rootProcessInstanceId, sProcessInstance.getId(),\n                    flownNodeDefinitions, rootProcessInstanceId, sProcessInstance.getId(), SStateCategory.NORMAL);\n        } catch (final SBonitaException e) {\n            setExceptionContext(selector.getProcessDefinition(), sProcessInstance, e);\n            log.error(\"\", e);\n            throw new BonitaRuntimeException(e);\n        }\n    }\n\n    private SProcessInstance createProcessInstance(final SProcessDefinition sDefinition, final long starterId,\n            final long starterSubstituteId,\n            final long callerId, final SFlowNodeType callerType, final long rootProcessInstanceId)\n            throws SProcessInstanceCreationException {\n\n        SProcessInstance sProcessInstance = SProcessInstance.builder().name(sDefinition.getName())\n                .processDefinitionId(sDefinition.getId()).description(sDefinition.getDescription())\n                .startedBy(starterId).startedBySubstitute(starterSubstituteId).callerId(callerId).callerType(callerType)\n                .rootProcessInstanceId(rootProcessInstanceId).build();\n        processInstanceService.createProcessInstance(sProcessInstance);\n\n        // Context will be auto clear by parent method (call hierarchy is always using ProcessInstanceMDC)\n        MDC.put(MDCConstants.PROCESS_INSTANCE_ID, String.valueOf(sProcessInstance.getId()));\n        MDC.put(MDCConstants.ROOT_PROCESS_INSTANCE_ID, String.valueOf(sProcessInstance.getRootProcessInstanceId()));\n        return sProcessInstance;\n    }\n\n    protected SProcessInstance createProcessInstance(final SProcessDefinition processDefinition, final long starterId,\n            final long starterSubstituteId,\n            final long callerId) throws SProcessInstanceCreationException {\n        SActivityInstance callerInstance;\n        try {\n            callerInstance = getCaller(callerId);\n        } catch (final SBonitaException e) {\n            throw new SProcessInstanceCreationException(\"Unable to get caller.\", e);\n        }\n\n        if (callerInstance != null) {\n            return createProcessInstance(processDefinition, starterId, starterSubstituteId, callerId,\n                    callerInstance.getType(),\n                    callerInstance.getRootContainerId());\n        }\n        return createProcessInstance(processDefinition, starterId, starterSubstituteId, callerId, null, -1);\n    }\n\n    private SActivityInstance getCaller(final long callerId)\n            throws SActivityReadException, SActivityInstanceNotFoundException {\n        if (callerId > 0) {\n            return activityInstanceService.getActivityInstance(callerId);\n        }\n        return null;\n    }\n\n    /*\n     * this method is called when a flow node having a transition that goes to a gateway is finished\n     * it get the active gateway pointed by this transition, update the tokens of this gateway and execute it if merged\n     */\n    private void executeGateway(final SProcessDefinition sProcessDefinition,\n            final STransitionDefinition sTransitionDefinition,\n            final SFlowNodeInstance flowNodeThatTriggeredTheTransition) throws SBonitaException {\n        final long parentProcessInstanceId = flowNodeThatTriggeredTheTransition.getParentProcessInstanceId();\n        final long rootProcessInstanceId = flowNodeThatTriggeredTheTransition.getRootProcessInstanceId();\n        final SFlowNodeDefinition sFlowNodeDefinition = processDefinitionService.getNextFlowNode(sProcessDefinition,\n                String.valueOf(sTransitionDefinition.getId()));\n        try {\n            List<SGatewayInstance> gatewaysToExecute = new ArrayList<>(1);\n            final SProcessInstance parentProcessInstance = processInstanceService\n                    .getProcessInstance(parentProcessInstanceId);\n            final SStateCategory stateCategory = parentProcessInstance.getStateCategory();\n            final SGatewayInstance gatewayInstance = getActiveGatewayOrCreateIt(sProcessDefinition, sFlowNodeDefinition,\n                    stateCategory,\n                    parentProcessInstanceId,\n                    rootProcessInstanceId);\n            gatewayInstanceService.hitTransition(gatewayInstance,\n                    sFlowNodeDefinition.getTransitionIndex(sTransitionDefinition.getId()));\n            if (gatewayInstanceService.checkMergingCondition(sProcessDefinition, gatewayInstance)) {\n                gatewaysToExecute.add(gatewayInstance);\n                gatewaysToExecute.addAll(gatewayInstanceService\n                        .setFinishAndCreateNewGatewayForRemainingToken(sProcessDefinition, gatewayInstance));\n            }\n            for (final SGatewayInstance gatewayToExecute : gatewaysToExecute) {\n                registerExecuteFlowNodeWork(gatewayToExecute);\n            }\n        } catch (final SBonitaException e) {\n            setExceptionContext(sProcessDefinition, flowNodeThatTriggeredTheTransition, e);\n            log.error(\"\", e);\n            throw e;\n        }\n    }\n\n    /*\n     * try to gate active gateway.\n     * if the gateway is already hit by this transition or by the same token, we create a new gateway\n     */\n    SGatewayInstance getActiveGatewayOrCreateIt(final SProcessDefinition sProcessDefinition,\n            final SFlowNodeDefinition flowNodeDefinition,\n            final SStateCategory stateCategory, final long parentProcessInstanceId, final long rootProcessInstanceId)\n            throws SBonitaException {\n        SGatewayInstance gatewayInstance = gatewayInstanceService\n                .getActiveGatewayInstanceOfTheProcess(parentProcessInstanceId, flowNodeDefinition.getName());\n        if (gatewayInstance == null) {\n            // no gateway found we create one\n            gatewayInstance = createGateway(sProcessDefinition.getId(), flowNodeDefinition, stateCategory,\n                    parentProcessInstanceId, rootProcessInstanceId);\n        }\n        return gatewayInstance;\n    }\n\n    private SGatewayInstance createGateway(final Long processDefinitionId, final SFlowNodeDefinition flowNodeDefinition,\n            final SStateCategory stateCategory,\n            final long parentProcessInstanceId, final long rootProcessInstanceId) throws SBonitaException {\n        return (SGatewayInstance) bpmInstancesCreator\n                .createFlowNodeInstance(processDefinitionId, rootProcessInstanceId, parentProcessInstanceId,\n                        SFlowElementsContainerType.PROCESS,\n                        flowNodeDefinition, rootProcessInstanceId, parentProcessInstanceId, false, 0, stateCategory,\n                        -1);\n    }\n\n    protected void executeOperations(final List<SOperation> operations, final Map<String, Object> context,\n            SExpressionContext expressionContext,\n            final SExpressionContext expressionContextToEvaluateOperations,\n            final SProcessInstance sProcessInstance) throws SBonitaException {\n        if (operations != null && !operations.isEmpty()) {\n            SExpressionContext currentExpressionContext = expressionContextToEvaluateOperations != null\n                    ? expressionContextToEvaluateOperations\n                    : expressionContext;\n            currentExpressionContext.setInputValues(context);\n            if (currentExpressionContext.getContainerId() == null) {\n                currentExpressionContext.setContainerId(sProcessInstance.getId());\n                currentExpressionContext.setContainerType(DataInstanceContainer.PROCESS_INSTANCE.name());\n            }\n            operationService.execute(new ArrayList<>(operations), sProcessInstance.getId(),\n                    DataInstanceContainer.PROCESS_INSTANCE.name(),\n                    currentExpressionContext);\n        }\n    }\n\n    protected boolean initialize(final long userId, final SProcessDefinition sProcessDefinition,\n            final SProcessInstance sProcessInstance,\n            SExpressionContext expressionContextToEvaluateOperations, List<SOperation> operations,\n            final Map<String, Object> context,\n            final SFlowElementContainerDefinition processContainer,\n            final List<ConnectorDefinitionWithInputValues> connectors,\n            final FlowNodeSelector selectorForConnectorOnEnter, final Map<String, Serializable> processInputs)\n            throws SBonitaException {\n        SExpressionContext expressionContext = createExpressionsContextForProcessInstance(sProcessDefinition,\n                sProcessInstance);\n\n        operations = operations != null ? new ArrayList<>(operations) : Collections.emptyList();\n        try {\n            storeProcessInstantiationInputs(sProcessInstance.getId(), processInputs);\n\n            // Create SDataInstances\n            bpmInstancesCreator.createDataInstances(sProcessInstance, processContainer, sProcessDefinition,\n                    expressionContext, operations, context,\n                    expressionContextToEvaluateOperations);\n\n            initializeBusinessData(processContainer, sProcessInstance, expressionContext);\n            initializeStringIndexes(sProcessInstance, sProcessDefinition, processContainer);\n\n            createDocuments(sProcessDefinition, processContainer, sProcessInstance, userId, expressionContext, context);\n            createDocumentLists(processContainer, sProcessInstance, userId, expressionContext, context);\n        } catch (SBonitaException e) {\n            e.setScope(ScopedException.DATA);\n            throw e;\n        }\n        if (connectors != null) {\n            //these are set only when start process through the command ExecuteActionsAndStartInstanceExt\n            try {\n                executeConnectors(sProcessDefinition, sProcessInstance, connectors);\n            } catch (SBonitaException e) {\n                e.setScope(ScopedException.CONNECTOR);\n                throw e;\n            }\n        }\n        // operations given to the startProcess method of the API or by command, not operations of the process definition\n        try {\n            executeOperations(operations, context, expressionContext, expressionContextToEvaluateOperations,\n                    sProcessInstance);\n        } catch (SBonitaException e) {\n            // Data mapping of call activity\n            e.setScope(ScopedException.DATA);\n            throw e;\n        }\n\n        // Create connectors\n        bpmInstancesCreator.createConnectorInstances(sProcessInstance, processContainer.getConnectors(),\n                SConnectorInstance.PROCESS_TYPE);\n\n        return registerConnectorsToExecute(sProcessDefinition, sProcessInstance, ConnectorEvent.ON_ENTER,\n                selectorForConnectorOnEnter);\n    }\n\n    private SExpressionContext createExpressionsContextForProcessInstance(SProcessDefinition sProcessDefinition,\n            SProcessInstance sProcessInstance) {\n        SExpressionContext expressionContext = new SExpressionContext();\n        expressionContext.setProcessDefinitionId(sProcessDefinition.getId());\n        expressionContext.setContainerId(sProcessInstance.getId());\n        expressionContext.setContainerType(DataInstanceContainer.PROCESS_INSTANCE.name());\n        return expressionContext;\n    }\n\n    private void storeProcessInstantiationInputs(final long processInstanceId,\n            final Map<String, Serializable> processInputs)\n            throws SContractDataCreationException {\n        contractDataService.addProcessData(processInstanceId, processInputs);\n    }\n\n    protected void initializeBusinessData(SFlowElementContainerDefinition processContainer, SProcessInstance sInstance,\n            SExpressionContext expressionContext)\n            throws SBonitaException {\n        final List<SBusinessDataDefinition> businessDataDefinitions = processContainer.getBusinessDataDefinitions();\n        for (final SBusinessDataDefinition bdd : businessDataDefinitions) {\n            final SExpression expression = bdd.getDefaultValueExpression();\n            if (bdd.isMultiple()) {\n                final List<Long> dataIds = initializeMultipleBusinessDataIds(expressionContext, expression);\n                final SRefBusinessDataInstanceBuilderFactory instanceFactory = BuilderFactory\n                        .get(SRefBusinessDataInstanceBuilderFactory.class);\n                final SRefBusinessDataInstance instance = instanceFactory\n                        .createNewInstance(bdd.getName(), sInstance.getId(), dataIds, bdd.getClassName())\n                        .done();\n                refBusinessDataService.addRefBusinessDataInstance(instance);\n            } else {\n                final Long primaryKey = initializeSingleBusinessData(expressionContext, expression);\n                final SRefBusinessDataInstanceBuilderFactory instanceFactory = BuilderFactory\n                        .get(SRefBusinessDataInstanceBuilderFactory.class);\n                final SRefBusinessDataInstance instance = instanceFactory\n                        .createNewInstance(bdd.getName(), sInstance.getId(), primaryKey,\n                                bdd.getClassName())\n                        .done();\n                refBusinessDataService.addRefBusinessDataInstance(instance);\n            }\n        }\n    }\n\n    private Long initializeSingleBusinessData(final SExpressionContext expressionContext, final SExpression expression)\n            throws SBonitaException {\n        Long primaryKey = null;\n        if (expression != null) {\n            final Entity businessData = (Entity) expressionResolverService.evaluate(expression, expressionContext);\n            primaryKey = saveBusinessData(businessData);\n        }\n        return primaryKey;\n    }\n\n    private List<Long> initializeMultipleBusinessDataIds(final SExpressionContext expressionContext,\n            final SExpression expression) throws SBonitaException {\n        final List<Long> dataIds = new ArrayList<>();\n        if (expression != null) {\n            final List<Entity> businessData = (List<Entity>) expressionResolverService.evaluate(expression,\n                    expressionContext);\n            if (businessData != null) {\n                for (final Entity entity : businessData) {\n                    dataIds.add(saveBusinessData(entity));\n                }\n            }\n        }\n        return dataIds;\n    }\n\n    private Long saveBusinessData(final Entity entity) throws SObjectCreationException {\n        try {\n            final Entity mergedBusinessData = businessDataRepository.merge(ServerProxyfier.unProxifyIfNeeded(entity));\n            if (mergedBusinessData == null) {\n                return null;\n            }\n            return mergedBusinessData.getPersistenceId();\n        } catch (IllegalArgumentException e) {\n            throw new SObjectCreationException(\"Unable to save the business data\", e);\n        }\n    }\n\n    private void createDocuments(final SProcessDefinition sDefinition, SFlowElementContainerDefinition processContainer,\n            final SProcessInstance sProcessInstance, final long authorId,\n            final SExpressionContext expressionContext, final Map<String, Object> context)\n            throws SObjectCreationException, SBonitaReadException, SObjectModificationException,\n            SExpressionTypeUnknownException, SExpressionDependencyMissingException, SExpressionEvaluationException,\n            SInvalidExpressionException, SOperationExecutionException {\n        final List<SDocumentDefinition> documentDefinitions = processContainer.getDocumentDefinitions();\n        final Map<SExpression, DocumentValue> evaluatedDocumentValues = evaluateInitialExpressionsOfDocument(\n                sProcessInstance, expressionContext, context,\n                documentDefinitions);\n        if (!documentDefinitions.isEmpty()) {\n            for (final SDocumentDefinition document : documentDefinitions) {\n                final DocumentValue documentValue = getInitialDocumentValue(sDefinition, evaluatedDocumentValues,\n                        document);\n                if (documentValue != null) {\n                    documentHelper.createOrUpdateDocument(documentValue,\n                            document.getName(), sProcessInstance.getId(), authorId, document.getDescription());\n                }\n            }\n        }\n    }\n\n    protected DocumentValue getInitialDocumentValue(final SProcessDefinition sDefinition,\n            final Map<SExpression, DocumentValue> evaluatedDocumentValues,\n            final SDocumentDefinition document) throws SBonitaReadException {\n        DocumentValue documentValue = null;\n        if (document.getInitialValue() != null) {\n            documentValue = evaluatedDocumentValues.get(document.getInitialValue());\n        } else if (document.getFile() != null) {\n            final byte[] content = getProcessDocumentContent(sDefinition, document);\n            documentValue = new DocumentValue(content, document.getMimeType(), document.getFileName());\n        } else if (document.getUrl() != null) {\n            documentValue = new DocumentValue(document.getUrl());\n            documentValue.setFileName(document.getFileName());\n            documentValue.setMimeType(document.getMimeType());\n        }\n        return documentValue;\n    }\n\n    byte[] getProcessDocumentContent(final SProcessDefinition sDefinition, final SDocumentDefinition document)\n            throws SBonitaReadException {\n        final String file = document.getFile();// should always exists...validation on BusinessArchive\n        return processResourcesService.get(sDefinition.getId(), BARResourceType.DOCUMENT, file).getContent();\n    }\n\n    private Map<SExpression, DocumentValue> evaluateInitialExpressionsOfDocument(final SProcessInstance processInstance,\n            final SExpressionContext expressionContext,\n            final Map<String, Object> context, final List<SDocumentDefinition> documentDefinitions)\n            throws SExpressionTypeUnknownException,\n            SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException,\n            SOperationExecutionException {\n        final List<SExpression> initialValuesExpressions = new ArrayList<>(documentDefinitions.size());\n        final Map<SExpression, DocumentValue> evaluatedDocumentValue = new HashMap<>();\n        for (final SDocumentDefinition documentDefinition : documentDefinitions) {\n            if (documentDefinition.getInitialValue() != null) {\n                initialValuesExpressions.add(documentDefinition.getInitialValue());\n            }\n        }\n        final List<Object> evaluate = expressionResolverService.evaluate(initialValuesExpressions,\n                getsExpressionContext(processInstance, expressionContext, context));\n        for (int i = 0; i < initialValuesExpressions.size(); i++) {\n            evaluatedDocumentValue.put(initialValuesExpressions.get(i),\n                    documentHelper.toCheckedDocumentValue(evaluate.get(i)));\n        }\n        return evaluatedDocumentValue;\n    }\n\n    private void createDocumentLists(SFlowElementContainerDefinition processContainer,\n            final SProcessInstance processInstance, final long authorId,\n            final SExpressionContext expressionContext, final Map<String, Object> context)\n            throws SBonitaException {\n        final List<SDocumentListDefinition> documentListDefinitions = processContainer.getDocumentListDefinitions();\n        if (!documentListDefinitions.isEmpty()) {\n            final List<Object> initialValues = evaluateInitialExpressionsOfDocumentLists(processInstance,\n                    expressionContext, context, documentListDefinitions);\n            for (int i = 0; i < documentListDefinitions.size(); i++) {\n                final Object newValue = initialValues.get(i);\n                if (newValue == null) {\n                    continue;\n                }\n                documentHelper.setDocumentList(\n                        documentHelper.toCheckedList(newValue), documentListDefinitions.get(i).getName(),\n                        processInstance.getId(), authorId);\n            }\n        }\n    }\n\n    private List<Object> evaluateInitialExpressionsOfDocumentLists(final SProcessInstance processInstance,\n            final SExpressionContext expressionContext,\n            final Map<String, Object> context, final List<SDocumentListDefinition> documentListDefinitions)\n            throws SExpressionTypeUnknownException,\n            SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException {\n        final List<SExpression> initialValuesExpressions = new ArrayList<>(documentListDefinitions.size());\n        for (final SDocumentListDefinition documentList : documentListDefinitions) {\n            initialValuesExpressions.add(documentList.getExpression());\n        }\n        final SExpressionContext currentExpressionContext = getsExpressionContext(processInstance, expressionContext,\n                context);\n        return expressionResolverService.evaluate(initialValuesExpressions, currentExpressionContext);\n    }\n\n    private SExpressionContext getsExpressionContext(final SProcessInstance processInstance,\n            final SExpressionContext expressionContext,\n            final Map<String, Object> context) {\n        SExpressionContext currentExpressionContext;\n        if (expressionContext != null) {\n            expressionContext.setInputValues(context);\n            currentExpressionContext = expressionContext;\n        } else {\n            currentExpressionContext = new SExpressionContext(processInstance.getId(),\n                    DataInstanceContainer.PROCESS_INSTANCE.name(),\n                    processInstance.getProcessDefinitionId());\n            currentExpressionContext.setInputValues(context);\n        }\n        return currentExpressionContext;\n    }\n\n    @Override\n    public void childFinished(long processDefinitionId, long parentId, SFlowNodeInstance childFlowNode)\n            throws SBonitaException {\n        final SProcessDefinition sProcessDefinition = processDefinitionService\n                .getProcessDefinition(processDefinitionId);\n        final long processInstanceId = childFlowNode.getParentProcessInstanceId();\n\n        SProcessInstance sProcessInstance = processInstanceService.getProcessInstance(processInstanceId);\n\n        // this also deletes the event (unless the process was interrupted by event)\n        final boolean wasTheLastFlowNodeToExecute = executeValidOutgoingTransitionsAndUpdateTokens(sProcessDefinition,\n                childFlowNode,\n                sProcessInstance);\n        log.debug(\"The flow node <{}> with id<{}> of process instance <{}> finished\",\n                childFlowNode.getName(), childFlowNode.getId(), processInstanceId);\n        if (wasTheLastFlowNodeToExecute) {\n            // flow node has finished, now we want to log only process information in the context\n            MDC.remove(MDCConstants.FLOW_NODE_INSTANCE_ID);\n            int numberOfFlowNode = activityInstanceService.getNumberOfFlowNodes(sProcessInstance.getId());\n            if (sProcessInstance.getInterruptingEventId() > 0) {\n                //if it's interrupted by an event (error event), the flow node is kept to be executed last and deleted in triggerErrorEvents()\n                numberOfFlowNode -= 1;\n            }\n            if (numberOfFlowNode > 0) {\n                if (log.isDebugEnabled()) {\n                    log.debug(\"The process instance <{}> from definition <{}:{}> executed a branch \" +\n                            \"that is finished but there is still <{}> to execute\",\n                            processInstanceId, sProcessDefinition.getName(), sProcessDefinition.getVersion(),\n                            numberOfFlowNode);\n                    log.debug(activityInstanceService.getDirectChildrenOfProcessInstance(processInstanceId,\n                            0, numberOfFlowNode).toString());\n                }\n                return;\n            }\n            log.debug(\"The process instance <{}> from definition <{}:{}> finished\",\n                    processInstanceId, sProcessDefinition.getName(), sProcessDefinition.getVersion());\n            boolean hasTriggeredErrorEvents = false;\n            // in case of interruption by error event:\n            // * the first time the last element (except the error event itself)  goes here, it put the process in aborting\n            // * the error event in thrown\n            //     * the catching flow node is executed IN THE SAME THREAD (I don't know why...)\n            //     * OR the catching event sub process is executed IN THE SAME THREAD and the process having this event sub process is interrupted (not locked!)\n            // * the throw error event is deleted\n            // * the waiting error event is deleted\n            // * the process is put in:\n            //     * ABORTING: if the process was in state category ABORTING (I don't know why...)\n            //         I'm not sure this case really happens because this would require that a last flow node trigger this method again and it's never the case\n            //     * COMPLETED: in 'normal' case\n            //         In that case if the process is called by a call activity, the calling activity have its token count decremented but is not executed (hasTriggeredErrorEvents==true)\n            //\n            if (ProcessInstanceState.ABORTING.getId() != sProcessInstance.getStateId()) {\n                if (sProcessInstance.getStateCategory() != SStateCategory.CANCELLING\n                        && sProcessInstance.hasBeenInterruptedByEvent()) {\n                    // trigger error events only if process instance has been aborted by an event\n                    // and no-one cancelled the process instance in the meantime:\n                    hasTriggeredErrorEvents = triggerErrorEvents(sProcessDefinition, sProcessInstance,\n                            childFlowNode);\n                }\n                // the process instance has maybe changed\n                log.debug(\"has action to execute\");\n                if (hasTriggeredErrorEvents) {\n                    sProcessInstance = processInstanceService.getProcessInstance(processInstanceId);\n                }\n                eventsHandler.unregisterEventSubProcess(sProcessDefinition, sProcessInstance);\n            }\n            handleProcessCompletion(sProcessDefinition, sProcessInstance, hasTriggeredErrorEvents);\n        }\n    }\n\n    @Override\n    public void handleProcessCompletion(final SProcessDefinition sProcessDefinition,\n            final SProcessInstance sProcessInstance, final boolean hasActionsToExecute)\n            throws SBonitaException {\n        ProcessInstanceState processInstanceState;\n        switch (sProcessInstance.getStateCategory()) {\n            case ABORTING:\n                if (ProcessInstanceState.ABORTING.getId() == sProcessInstance.getStateId()) {\n                    processInstanceState = ProcessInstanceState.ABORTED;\n                } else {\n                    if (hasActionsToExecute) {\n                        processInstanceState = ProcessInstanceState.ABORTING;\n                    } else {\n                        processInstanceState = ProcessInstanceState.ABORTED;\n                    }\n                }\n                break;\n            case CANCELLING:\n                processInstanceState = ProcessInstanceState.CANCELLED;\n                break;\n            default:\n                if (ProcessInstanceState.COMPLETING.getId() == sProcessInstance.getStateId()) {\n                    processInstanceState = ProcessInstanceState.COMPLETED;\n                } else {\n                    if (registerConnectorsToExecute(sProcessDefinition, sProcessInstance, ConnectorEvent.ON_FINISH,\n                            null)) {\n                        // some connectors were trigger\n                        processInstanceState = ProcessInstanceState.COMPLETING;\n                    } else {\n                        processInstanceState = ProcessInstanceState.COMPLETED;\n                    }\n                }\n                break;\n        }\n        processInstanceService.setState(sProcessInstance, processInstanceState);\n        flowNodeExecutor.childReachedState(sProcessInstance, processInstanceState, hasActionsToExecute);\n\n    }\n\n    private boolean triggerErrorEvents(final SProcessDefinition sProcessDefinition,\n            final SProcessInstance sProcessInstance,\n            final SFlowNodeInstance child) throws SBonitaException {\n        final SFlowNodeInstance endEventInstance = activityInstanceService\n                .getFlowNodeInstance(sProcessInstance.getInterruptingEventId());\n        final SEndEventDefinition endEventDefinition = (SEndEventDefinition) sProcessDefinition\n                .getProcessContainer().getFlowNode(\n                        endEventInstance.getFlowNodeDefinitionId());\n        boolean hasTriggeredErrorEvents = eventsHandler.handlePostThrowEvent(sProcessDefinition, endEventDefinition,\n                (SThrowEventInstance) endEventInstance, child);\n        bpmArchiverService.archiveAndDeleteFlowNodeInstance(endEventInstance, sProcessDefinition.getId());\n        return hasTriggeredErrorEvents;\n    }\n\n    /**\n     * Evaluate the split of the element\n     * The element contains the current token it received\n     *\n     * @return number of token of the process\n     */\n    private boolean executeValidOutgoingTransitionsAndUpdateTokens(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance child, final SProcessInstance sProcessInstance) throws SBonitaException {\n        // token we merged\n        final SFlowNodeDefinition sFlowNodeDefinition = processDefinition.getProcessContainer()\n                .getFlowNode(child.getFlowNodeDefinitionId());\n        final FlowNodeTransitionsWrapper transitionsDescriptor = transitionEvaluator\n                .buildTransitionsWrapper(sFlowNodeDefinition, processDefinition, child);\n\n        List<STransitionDefinition> chosenGatewaysTransitions = new ArrayList<>(transitionsDescriptor\n                .getValidOutgoingTransitionDefinitions().size());\n        final List<SFlowNodeDefinition> chosenFlowNode = new ArrayList<>(\n                transitionsDescriptor.getValidOutgoingTransitionDefinitions().size());\n        for (final STransitionDefinition sTransitionDefinition : transitionsDescriptor\n                .getValidOutgoingTransitionDefinitions()) {\n            final SFlowNodeDefinition flowNodeDefinition = processDefinitionService.getNextFlowNode(processDefinition,\n                    String.valueOf(sTransitionDefinition.getId()));\n            if (flowNodeDefinition instanceof SGatewayDefinition) {\n                chosenGatewaysTransitions.add(sTransitionDefinition);\n            } else {\n                // Shortcut: event or activity, we execute them directly\n                chosenFlowNode.add(flowNodeDefinition);\n            }\n        }\n\n        archiveFlowNodeInstance(processDefinition, child, sProcessInstance);\n\n        final long processInstanceId = sProcessInstance.getId();\n        createAndExecuteActivities(processDefinition.getId(), child, processInstanceId, chosenFlowNode,\n                child.getRootProcessInstanceId());\n\n        //test to check the particular case of an inclusive gateway receiving transitions from the same flownode.\n        //if that's the case only one should be executed.\n        removeDuplicatedInclusiveGatewayTransitions(processDefinition, chosenGatewaysTransitions);\n\n        // execute transition/activities\n        for (final STransitionDefinition sTransitionDefinition : chosenGatewaysTransitions) {\n            executeGateway(processDefinition, sTransitionDefinition, child);\n        }\n\n        if (processDefinition.getProcessContainer().containsInclusiveGateway()\n                && needToReevaluateInclusiveGateways(transitionsDescriptor)) {\n            reevaluateGateways(processDefinition, processInstanceId);\n        }\n        return transitionsDescriptor.isLastFlowNode();\n    }\n\n    private void reevaluateGateways(SProcessDefinition processDefinition, long processInstanceId)\n            throws SBonitaException {\n        log.debug(\"some branches died, will check again all inclusive gateways\");\n        final List<SGatewayInstance> inclusiveGatewaysOfProcessInstance = gatewayInstanceService\n                .getInclusiveGatewaysOfProcessInstanceThatShouldFire(\n                        processDefinition, processInstanceId);\n        List<SGatewayInstance> gatewaysToExecute = new ArrayList<>(inclusiveGatewaysOfProcessInstance);\n        for (final SGatewayInstance gatewayInstance : inclusiveGatewaysOfProcessInstance) {\n            gatewaysToExecute\n                    .addAll(gatewayInstanceService.setFinishAndCreateNewGatewayForRemainingToken(processDefinition,\n                            gatewayInstance));\n        }\n        for (final SGatewayInstance gatewayToExecute : gatewaysToExecute) {\n            registerExecuteFlowNodeWork(gatewayToExecute);\n        }\n    }\n\n    protected void removeDuplicatedInclusiveGatewayTransitions(SProcessDefinition processDefinition,\n            List<STransitionDefinition> chosenGatewaysTransitions) {\n        List<STransitionDefinition> transitionToRemove = new ArrayList<>();\n        Set<SGatewayDefinition> gateways = new HashSet<>();\n        for (STransitionDefinition gatewaysTransition : chosenGatewaysTransitions) {\n            SGatewayDefinition gateway = getGateway(gatewaysTransition, processDefinition);\n            if (isInclusiveGateway(gateway)) {\n                boolean alreadyExists = !gateways.add(gateway);\n                if (alreadyExists) {\n                    transitionToRemove.add(gatewaysTransition);\n                }\n            }\n        }\n        chosenGatewaysTransitions.removeAll(transitionToRemove);\n    }\n\n    private boolean isInclusiveGateway(SGatewayDefinition gateway) {\n        return gateway.getGatewayType() == SGatewayType.INCLUSIVE;\n    }\n\n    private SGatewayDefinition getGateway(STransitionDefinition gatewaysTransition,\n            SProcessDefinition processDefinition) {\n        return (SGatewayDefinition) processDefinition.getProcessContainer().getFlowNode(gatewaysTransition.getTarget());\n    }\n\n    private void archiveFlowNodeInstance(final SProcessDefinition sProcessDefinition, final SFlowNodeInstance child,\n            final SProcessInstance sProcessInstance)\n            throws SArchivingException {\n        //FIXME we archive the flow node instance here because it was not archived before because the flow node was interrupting the parent.. we should change that because it's not very easy to see how it works\n        // * the flow node is archived only if its not the error event that triggered the interruption (unless if its in a sub process????)\n        if (child.getId() != sProcessInstance.getInterruptingEventId()\n                || SFlowNodeType.SUB_PROCESS.equals(sProcessInstance.getCallerType())) {\n            // Let's archive the final state of the child:\n            bpmArchiverService.archiveAndDeleteFlowNodeInstance(child, sProcessDefinition.getId());\n        }\n    }\n\n    private boolean needToReevaluateInclusiveGateways(final FlowNodeTransitionsWrapper transitionsDescriptor) {\n        final int allOutgoingTransitions = transitionsDescriptor.getNonDefaultOutgoingTransitionDefinitions().size()\n                + (transitionsDescriptor.getDefaultTransition() != null ? 1 : 0);\n        final int takenTransition = transitionsDescriptor.getValidOutgoingTransitionDefinitions().size();\n        /*\n         * Why this condition?\n         * If a gateway was blocked because it was waiting for a token to come it will not be unblock when all\n         * transitions\n         * are taken but only if some transitions are not.\n         * In conclusion if all declared transition are not taken it means that a 'branch died' and that some inclusive\n         * gateways might be triggered so the\n         * reevaluation is needed\n         */\n        return takenTransition < allOutgoingTransitions;\n    }\n\n    @Override\n    public SProcessInstance start(final long starterId, final long starterSubstituteId,\n            final List<SOperation> operations, final Map<String, Object> context,\n            final List<ConnectorDefinitionWithInputValues> connectorsWithInput, final FlowNodeSelector selector,\n            final Map<String, Serializable> processInputs)\n            throws SProcessInstanceCreationException, SContractViolationException {\n        return start(starterId, starterSubstituteId, null, operations, context, connectorsWithInput, -1, selector,\n                processInputs);\n    }\n\n    @Override\n    /* Started by call activity and events, operations must be evaluated using the given context */\n    public SProcessInstance start(final long processDefinitionId, final long targetSFlowNodeDefinitionId,\n            final long starterId, final long starterSubstituteId,\n            final SExpressionContext expressionContextToEvaluateOperations, final List<SOperation> operations,\n            final long callerId, final long subProcessDefinitionId,\n            final Map<String, Serializable> processInputs)\n            throws SProcessInstanceCreationException, SContractViolationException {\n        try {\n            final SProcessDefinition sProcessDefinition = processDefinitionService\n                    .getProcessDefinition(processDefinitionId);\n            final FlowNodeSelector selector = new FlowNodeSelector(sProcessDefinition,\n                    getFilter(targetSFlowNodeDefinitionId), subProcessDefinitionId);\n            return start(starterId, starterSubstituteId, expressionContextToEvaluateOperations, operations, null, null,\n                    callerId, selector, processInputs);\n        } catch (final SProcessDefinitionNotFoundException | SBonitaReadException e) {\n            throw new SProcessInstanceCreationException(e);\n        }\n    }\n\n    private Filter<SFlowNodeDefinition> getFilter(final long targetSFlowNodeDefinitionId) {\n        if (targetSFlowNodeDefinitionId == -1) {\n            return new StartFlowNodeFilter();\n        }\n        return new FlowNodeIdFilter(targetSFlowNodeDefinitionId);\n    }\n\n    protected void initializeStringIndexes(final SProcessInstance sInstance, SProcessDefinition sProcessDefinition,\n            final SFlowElementContainerDefinition processContainer) throws SExpressionTypeUnknownException,\n            SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException,\n            SProcessInstanceModificationException {\n        if (!sProcessDefinition.getProcessContainer().equals(processContainer)) {\n            //we are not instantiating the process, we are starting an event subprocess\n            return;\n        }\n        final SExpressionContext contextDependency = new SExpressionContext(sInstance.getId(),\n                DataInstanceContainer.PROCESS_INSTANCE.name(),\n                sProcessDefinition.getId());\n\n        boolean update = false;\n        EntityUpdateDescriptor entityUpdateDescriptor = new EntityUpdateDescriptor();\n        for (int i = 1; i <= 5; i++) {\n            final SExpression value = sProcessDefinition.getStringIndexValue(i);\n            if (value != null) {\n                update = true;\n                entityUpdateDescriptor.addField(SProcessInstance.STRING_INDEX_KEY + i,\n                        String.valueOf(expressionResolverService.evaluate(value, contextDependency)));\n            }\n        }\n        if (update) {\n            processInstanceService.updateProcess(sInstance, entityUpdateDescriptor);\n        }\n    }\n\n    protected SProcessInstance start(final long starterId, final long starterSubstituteId,\n            final SExpressionContext expressionContextToEvaluateOperations,\n            final List<SOperation> operations, final Map<String, Object> context,\n            final List<ConnectorDefinitionWithInputValues> connectors,\n            final long callerId, final FlowNodeSelector selector, final Map<String, Serializable> processInputs)\n            throws SProcessInstanceCreationException, SContractViolationException {\n\n        final SProcessDefinition sProcessDefinition = selector.getProcessDefinition();\n\n        // Validate start process contract inputs:\n        if (!selector.isEventSubProcess()) {\n            validateContractInputs(processInputs, sProcessDefinition);\n        }\n\n        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        try {\n            // so that event sub-process can trigger even if containing process definition is disabled:\n            if (!selector.isEventSubProcess()) {\n                ensureProcessIsEnabled(sProcessDefinition);\n            }\n            setProcessClassloader(sProcessDefinition);\n            final SProcessInstance sProcessInstance = createProcessInstance(sProcessDefinition, starterId,\n                    starterSubstituteId, callerId);\n            processStarterVerifier.verify(sProcessInstance);\n            final boolean isInitializing = initialize(starterId, sProcessDefinition, sProcessInstance,\n                    expressionContextToEvaluateOperations,\n                    operations, context, selector.getContainer(), connectors,\n                    selector, processInputs);\n            handleEventSubProcess(sProcessDefinition, sProcessInstance, selector.getSubProcessDefinitionId());\n\n            if (isInitializing) {\n                // some connectors were trigger\n                processInstanceService.setState(sProcessInstance, ProcessInstanceState.INITIALIZING);\n                // we stop execution here\n                return sProcessInstance;\n            }\n            return startElements(sProcessInstance, selector);\n        } catch (final SProcessInstanceCreationException e) {\n            throw e;\n        } catch (final SBonitaException e) {\n            throw new SProcessInstanceCreationException(e);\n        } finally {\n            Thread.currentThread().setContextClassLoader(contextClassLoader);\n        }\n    }\n\n    private void ensureProcessIsEnabled(final SProcessDefinition sProcessDefinition)\n            throws SBonitaReadException, SProcessDefinitionException {\n        final SProcessDefinitionDeployInfo deployInfo = processDefinitionService\n                .getProcessDeploymentInfo(sProcessDefinition.getId());\n        if (ActivationState.DISABLED.name().equals(deployInfo.getActivationState())) {\n            throw new SProcessDefinitionException(\n                    \"The process \" + deployInfo.getName() + \" \" + deployInfo.getVersion() + \" is not enabled.\",\n                    deployInfo.getProcessId(), deployInfo.getName(), deployInfo.getVersion());\n        }\n    }\n\n    private void setProcessClassloader(SProcessDefinition sProcessDefinition) throws SClassLoaderException {\n        final ClassLoader localClassLoader = classLoaderService.getClassLoader(\n                identifier(ScopeType.PROCESS, sProcessDefinition.getId()));\n        Thread.currentThread().setContextClassLoader(localClassLoader);\n        // initialize the process classloader by getting it one time\n        try {\n            localClassLoader.loadClass(this.getClass().getName());\n        } catch (final ClassNotFoundException e) {\n            // ignore, just to load\n        }\n    }\n\n    protected void validateContractInputs(final Map<String, Serializable> processInputs,\n            final SProcessDefinition sProcessDefinition)\n            throws SContractViolationException {\n        final SContractDefinition contractDefinition = sProcessDefinition.getContract();\n        if (contractDefinition != null) {\n            final ContractValidator validator = new ContractValidatorFactory()\n                    .createContractValidator(expressionService);\n            validator.validate(sProcessDefinition.getId(), contractDefinition, processInputs);\n        }\n    }\n\n    /*\n     * Execute connectors then execute output operation and disconnect connectors\n     */\n    protected void executeConnectors(final SProcessDefinition processDefinition,\n            final SProcessInstance sProcessInstance,\n            final List<ConnectorDefinitionWithInputValues> connectorsList)\n            throws SConnectorException {\n        final SExpressionContext expcontext = new SExpressionContext();\n        expcontext.setProcessDefinitionId(processDefinition.getId());\n        expcontext.setProcessDefinition(processDefinition);\n        expcontext.setContainerId(sProcessInstance.getId());\n        expcontext.setContainerType(DataInstanceContainer.PROCESS_INSTANCE.name());\n        for (final ConnectorDefinitionWithInputValues connectorWithInput : connectorsList) {\n            final ConnectorDefinition connectorDefinition = connectorWithInput.getConnectorDefinition();\n            final Map<String, Map<String, Serializable>> contextInputValues = connectorWithInput.getInputValues();\n            final String connectorId = connectorDefinition.getConnectorId();\n            final String version = connectorDefinition.getVersion();\n            final Map<String, Expression> inputs = connectorDefinition.getInputs();\n            if (contextInputValues.size() != inputs.size()) {\n                throw new SConnectorException(\"Invalid number of input parameters (expected \" + inputs.size() + \", got \"\n                        + contextInputValues.size() + \")\");\n            }\n            final Map<String, SExpression> connectorsExps = ModelConvertor.constructExpressions(inputs);\n\n            // we use the context classloader because the process classloader is already set\n            final ConnectorResult result = connectorService.executeMultipleEvaluation(processDefinition.getId(),\n                    connectorId, version, connectorsExps,\n                    contextInputValues, Thread.currentThread().getContextClassLoader(), expcontext);\n            final List<Operation> outputs = connectorDefinition.getOutputs();\n            connectorService.executeOutputOperation(ModelConvertor.convertOperations(outputs), expcontext, result);\n        }\n    }\n\n    protected void handleEventSubProcess(final SProcessDefinition sProcessDefinition,\n            final SProcessInstance sProcessInstance,\n            final long subProcessDefinitionId)\n            throws SBonitaException {\n        if (subProcessDefinitionId == -1) {\n            // modify that to support event sub-processes within sub-processes\n            try {\n                eventsHandler.handleEventSubProcess(sProcessDefinition, sProcessInstance);\n            } catch (final SProcessInstanceCreationException e) {\n                throw e;\n            } catch (final SBonitaException e) {\n                setExceptionContext(sProcessDefinition, sProcessInstance, e);\n                throw new SProcessInstanceCreationException(\n                        \"Unable to register events for event sub process in process.\", e);\n            }\n        }\n    }\n\n    @Override\n    public SProcessInstance startElements(final SProcessInstance sProcessInstance, final FlowNodeSelector selector)\n            throws SProcessInstanceCreationException,\n            SFlowNodeExecutionException {\n        try {\n            contractDataService.archiveAndDeleteProcessData(sProcessInstance.getId(), System.currentTimeMillis());\n        } catch (final SObjectModificationException e) {\n            throw new SProcessInstanceCreationException(e);\n        }\n        final List<SFlowNodeInstance> flowNodeInstances = initializeFirstExecutableElements(sProcessInstance, selector);\n        // process is initialized and now the engine trigger jobs to execute other activities, give the hand back\n        ProcessInstanceState state;\n        final int size = flowNodeInstances.size();\n        if (size == 0) {\n            state = ProcessInstanceState.COMPLETED;\n\n        } else {\n            state = ProcessInstanceState.STARTED;\n        }\n        try {\n            processInstanceService.setState(sProcessInstance, state);\n        } catch (final SBonitaException e) {\n            throw new SProcessInstanceCreationException(\"Unable to set the state on the process.\", e);\n        }\n        for (final SFlowNodeInstance sFlowNodeInstance : flowNodeInstances) {\n            try {\n                registerExecuteFlowNodeWork(sFlowNodeInstance);\n            } catch (final SWorkRegisterException e) {\n                setExceptionContext(sProcessInstance, sFlowNodeInstance, e);\n                throw new SFlowNodeExecutionException(\"Unable to trigger execution of the flow node.\", e);\n            }\n        }\n        return sProcessInstance;\n    }\n\n    @Override\n    public String getHandledType() {\n        return SFlowElementsContainerType.PROCESS.name();\n    }\n\n    private void createAndExecuteActivities(final Long processDefinitionId, final SFlowNodeInstance flowNodeInstance,\n            final long parentProcessInstanceId,\n            final List<SFlowNodeDefinition> choosenActivityDefinitions, final long rootProcessInstanceId)\n            throws SBonitaException {\n        final SProcessInstance parentProcessInstance = processInstanceService\n                .getProcessInstance(parentProcessInstanceId);\n        final SStateCategory stateCategory = parentProcessInstance.getStateCategory();\n\n        // Create Activities\n        final List<SFlowNodeInstance> sFlowNodeInstances = bpmInstancesCreator.createFlowNodeInstances(\n                processDefinitionId,\n                flowNodeInstance.getRootContainerId(), flowNodeInstance.getParentContainerId(),\n                choosenActivityDefinitions, rootProcessInstanceId,\n                parentProcessInstanceId, stateCategory);\n\n        // Execute Activities\n        for (final SFlowNodeInstance sFlowNodeInstance : sFlowNodeInstances) {\n            registerExecuteFlowNodeWork(sFlowNodeInstance);\n        }\n    }\n\n    private void registerExecuteFlowNodeWork(SFlowNodeInstance sFlowNodeInstance) throws SWorkRegisterException {\n        workService\n                .registerWork(workFactory.createExecuteFlowNodeWorkDescriptor(sFlowNodeInstance));\n    }\n\n    private void setExceptionContext(final SProcessDefinition sProcessDefinition,\n            final SFlowNodeInstance sFlowNodeInstance, final SBonitaException e) {\n        setExceptionContext(sProcessDefinition, e);\n        e.setProcessInstanceIdOnContext(sFlowNodeInstance.getParentProcessInstanceId());\n        e.setRootProcessInstanceIdOnContext(sFlowNodeInstance.getRootProcessInstanceId());\n        setExceptionContext(sFlowNodeInstance, e);\n    }\n\n    private void setExceptionContext(final SProcessDefinition sProcessDefinition,\n            final SProcessInstance sProcessInstance, final SBonitaException e) {\n        setExceptionContext(sProcessDefinition, e);\n        setExceptionContext(sProcessInstance, e);\n    }\n\n    private void setExceptionContext(final SProcessInstance sProcessInstance, final SFlowNodeInstance sFlowNodeInstance,\n            final SBonitaException e) {\n        e.setProcessDefinitionIdOnContext(sProcessInstance.getProcessDefinitionId());\n        e.setProcessDefinitionNameOnContext(sProcessInstance.getName());\n        setExceptionContext(sProcessInstance, e);\n        setExceptionContext(sFlowNodeInstance, e);\n    }\n\n    private void setExceptionContext(final SProcessDefinition sProcessDefinition, final SBonitaException e) {\n        e.setProcessDefinitionIdOnContext(sProcessDefinition.getId());\n        e.setProcessDefinitionNameOnContext(sProcessDefinition.getName());\n        e.setProcessDefinitionVersionOnContext(sProcessDefinition.getVersion());\n    }\n\n    private void setExceptionContext(final SProcessInstance sProcessInstance, final SBonitaException e) {\n        e.setProcessInstanceIdOnContext(sProcessInstance.getId());\n        e.setRootProcessInstanceIdOnContext(sProcessInstance.getRootProcessInstanceId());\n    }\n\n    private void setExceptionContext(final SFlowNodeInstance sFlowNodeInstance, final SBonitaException e) {\n        e.setFlowNodeDefinitionIdOnContext(sFlowNodeInstance.getFlowNodeDefinitionId());\n        e.setFlowNodeInstanceIdOnContext(sFlowNodeInstance.getId());\n        e.setFlowNodeNameOnContext(sFlowNodeInstance.getName());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/ProcessInstanceInterruptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.work.SWorkRegisterException;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\n@Slf4j\npublic class ProcessInstanceInterruptor {\n\n    private final ProcessInstanceService processInstanceService;\n    private final FlowNodeInstanceService flowNodeInstanceService;\n    private final ContainerRegistry containerRegistry;\n\n    public ProcessInstanceInterruptor(ProcessInstanceService processInstanceService,\n            FlowNodeInstanceService flowNodeInstanceService,\n            ContainerRegistry containerRegistry) {\n        this.processInstanceService = processInstanceService;\n        this.flowNodeInstanceService = flowNodeInstanceService;\n        this.containerRegistry = containerRegistry;\n    }\n\n    /**\n     * Interrupt the given process instance AND its children\n     *\n     * @param processInstanceId the process instance\n     * @param stateCategory the state category\n     * @return true if some children were interrupted\n     */\n    public boolean interruptProcessInstance(final long processInstanceId, final SStateCategory stateCategory)\n            throws SBonitaException {\n        processInstanceService.setStateCategory(processInstanceService.getProcessInstance(processInstanceId),\n                stateCategory);\n        List<SFlowNodeInstance> flowNodeInstances = flowNodeInstanceService\n                .getDirectChildrenOfProcessInstance(processInstanceId, 0, Integer.MAX_VALUE);\n        if (flowNodeInstances.isEmpty()) {\n            log.info(\"Process instance {} with no children was {}\", processInstanceId,\n                    getInterruptionType(stateCategory));\n            return false;\n        }\n        interruptFlowNodeInstances(flowNodeInstances, stateCategory);\n\n        log.info(\"Process instance {} and its children were {}\", processInstanceId,\n                getInterruptionType(stateCategory));\n        return true;\n\n    }\n\n    private String getInterruptionType(SStateCategory stateCategory) {\n        return stateCategory.equals(SStateCategory.ABORTING) ? \"aborted\" : \"cancelled\";\n    }\n\n    private void executeFlowNode(SFlowNodeInstance child) throws SWorkRegisterException {\n        if (child.isTerminal()) {\n            containerRegistry.notifyChildFinished(child);\n        } else {\n            //should not try to execute these because its the children that should be aborted\n            if (child.getType() != SFlowNodeType.MULTI_INSTANCE_ACTIVITY\n                    || child.getType() != SFlowNodeType.LOOP_ACTIVITY) {\n                containerRegistry.executeFlowNode(child);\n            }\n        }\n    }\n\n    /**\n     * Interrupt the given process instant AND its children, excluding the exceptionChildId\n     *\n     * @param processInstanceId the process instance\n     * @param stateCategory the state category\n     * @param exceptionChildId the element to exclude\n     */\n    public void interruptProcessInstance(final long processInstanceId, final SStateCategory stateCategory,\n            final long exceptionChildId)\n            throws SBonitaException {\n        processInstanceService.setStateCategory(processInstanceService.getProcessInstance(processInstanceId),\n                stateCategory);\n        interruptChildrenOfProcessInstance(processInstanceId, stateCategory, exceptionChildId);\n    }\n\n    /**\n     * Interrupt children of given process instance excluding notToInterruptFlownodeId\n     *\n     * @param processInstanceId the process instance\n     * @param stateCategory the state category in which children must be set\n     * @param notToInterruptFlownodeId the element to exclude\n     */\n    public void interruptChildrenOfProcessInstance(final long processInstanceId, final SStateCategory stateCategory,\n            final long notToInterruptFlownodeId)\n            throws SBonitaException {\n        List<SFlowNodeInstance> flowNodeInstances = flowNodeInstanceService\n                .getDirectChildrenOfProcessInstance(processInstanceId, 0, Integer.MAX_VALUE).stream()\n                .filter(f -> f.getId() != notToInterruptFlownodeId)\n                .collect(Collectors.toList());\n        if (flowNodeInstances.isEmpty()) {\n            log.warn(\"Process instance {} with no children was {} by flownode {}\", processInstanceId,\n                    getInterruptionType(stateCategory), notToInterruptFlownodeId);\n            return;\n        }\n        interruptFlowNodeInstances(flowNodeInstances, stateCategory);\n        log.info(\"Process instance {} and its children were {} by flownode {}\", processInstanceId,\n                getInterruptionType(stateCategory), notToInterruptFlownodeId);\n\n    }\n\n    /**\n     * Interrupt children of given flow node instance\n     *\n     * @param flowNodeInstance the flow node instance\n     * @param stateCategory the state category in which children must be set\n     */\n    public void interruptChildrenOfFlowNodeInstance(SFlowNodeInstance flowNodeInstance, SStateCategory stateCategory)\n            throws SBonitaException {\n        List<SFlowNodeInstance> flowNodeInstances = flowNodeInstanceService\n                .getDirectChildrenOfActivityInstance(flowNodeInstance.getId(), 0, Integer.MAX_VALUE);\n        if (flowNodeInstances.isEmpty()) {\n            log.warn(\"No children of flownode {} to {} found\", flowNodeInstance.getId(),\n                    getInterruptionType(stateCategory));\n            return;\n        }\n\n        interruptFlowNodeInstances(flowNodeInstances, stateCategory);\n    }\n\n    private void interruptFlowNodeInstances(final List<SFlowNodeInstance> children, final SStateCategory stateCategory)\n            throws SBonitaException {\n        for (final SFlowNodeInstance child : children) {\n            log.debug(\"Put element in {}, element:  {}, {}, {}\", stateCategory, child.getId(), child.getStateName(),\n                    child.getType());\n            flowNodeInstanceService.setStateCategory(child, stateCategory);\n            log.debug(\"Resume child in stateCategory {}: {}, {}, {}\", stateCategory, child.getId(),\n                    child.getStateName(), child.getStateCategory());\n            executeFlowNode(child);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/ProcessStarterVerifier.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceCreationException;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\n\n/**\n * Define rules to be executed before the start of a process, right after its creation.\n */\npublic interface ProcessStarterVerifier {\n\n    /**\n     * Verify that a process is ready to be started right after its creation.\n     *\n     * @param processInstance the process instance that is going to be started\n     * @throws SProcessInstanceCreationException if the process is not in a valid state to start\n     */\n    void verify(SProcessInstance processInstance) throws SProcessInstanceCreationException;\n\n    /**\n     * Get the current number of started process instances.\n     *\n     * @return -1 if non relevant in this context\n     */\n    default long getCurrentNumberOfStartedProcessInstances() {\n        return -1;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/ProcessStarterVerifierImpl.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport static java.lang.String.format;\nimport static java.lang.System.currentTimeMillis;\n\nimport java.io.IOException;\nimport java.security.GeneralSecurityException;\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.List;\n\nimport com.fasterxml.jackson.core.type.TypeReference;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceCreationException;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.platform.PlatformRetriever;\nimport org.bonitasoft.engine.platform.exception.SPlatformNotFoundException;\nimport org.bonitasoft.engine.platform.exception.SPlatformUpdateException;\nimport org.bonitasoft.engine.service.platform.PlatformInformationService;\nimport org.bonitasoft.engine.transaction.TransactionService;\nimport org.bonitasoft.platform.setup.SimpleEncryptor;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;\nimport org.springframework.stereotype.Component;\n\n@Component\n@ConditionalOnSingleCandidate(ProcessStarterVerifier.class)\n@Slf4j\npublic class ProcessStarterVerifierImpl implements ProcessStarterVerifier {\n\n    public static final int LIMIT = 150;\n    protected static final int PERIOD_IN_DAYS = 30;\n    protected static final long PERIOD_IN_MILLIS = PERIOD_IN_DAYS * 24L * 60L * 60L * 1000L;\n    protected static final List<Integer> THRESHOLDS_IN_PERCENT = List.of(80, 90);\n    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();\n\n    private final PlatformRetriever platformRetriever;\n    private final PlatformInformationService platformInformationService;\n    private final TransactionService transactionService;\n    private final ProcessInstanceService processInstanceService;\n\n    private final List<Long> counters = Collections.synchronizedList(new ArrayList<>());\n\n    @Autowired\n    public ProcessStarterVerifierImpl(PlatformRetriever platformRetriever,\n            PlatformInformationService platformInformationService,\n            TransactionService transactionService,\n            ProcessInstanceService processInstanceService) throws Exception {\n        this.platformRetriever = platformRetriever;\n        this.platformInformationService = platformInformationService;\n        this.transactionService = transactionService;\n        this.processInstanceService = processInstanceService;\n        counters.addAll(setupCounters(transactionService));\n        // clean up old values:\n        final long oldestValidDate = currentTimeMillis() - PERIOD_IN_MILLIS;\n        cleanupOldValues(oldestValidDate);\n        // then check database integrity:\n        verifyCountersCoherence(counters, oldestValidDate);\n    }\n\n    List<Long> setupCounters(TransactionService transactionService) throws Exception {\n        return transactionService.executeInTransaction(this::readCounters);\n    }\n\n    protected List<Long> getCounters() {\n        return Collections.unmodifiableList(counters);\n    }\n\n    protected void addCounter(long counter) {\n        synchronized (counters) {\n            counters.add(counter);\n        }\n    }\n\n    @Override\n    public void verify(SProcessInstance processInstance) throws SProcessInstanceCreationException {\n        log.debug(\"Verifying the possibility to create process instance {}\", processInstance.getId());\n        final long processStartDate = processInstance.getStartDate();\n        cleanupOldValues(processStartDate - PERIOD_IN_MILLIS);\n        log.debug(\"Found {} cases already started in the last {} days\", counters.size(), PERIOD_IN_DAYS);\n\n        if (counters.size() >= LIMIT) {\n            var nextResetTimestamp = getNextResetTimestamp(counters);\n            final String nextValidTime = getStringRepresentation(nextResetTimestamp);\n            throw new SProcessInstanceCreationException(\n                    format(\"Process start limit (%s cases during last %s days) reached. You are not allowed to start a new process until %s.\",\n                            LIMIT, PERIOD_IN_DAYS, nextValidTime),\n                    nextResetTimestamp);\n        }\n        try {\n            synchronized (counters) {\n                counters.add(processStartDate);\n            }\n            final String information = encryptDataBeforeSendingToDatabase(counters);\n            // store in database:\n            storeNewValueInDatabase(information);\n            logCaseLimitProgressIfThresholdReached();\n        } catch (IOException | SPlatformNotFoundException | SPlatformUpdateException e) {\n            log.trace(e.getMessage(), e);\n            throw new SProcessInstanceCreationException(\n                    format(\"Unable to start the process instance %s\", processInstance.getId()), e);\n        }\n    }\n\n    void cleanupOldValues(long olderThanInMilliseconds) {\n        log.trace(\"Cleaning up old values for the last {} days\", PERIOD_IN_DAYS);\n        synchronized (counters) {\n            counters.removeIf(timestamp -> timestamp < olderThanInMilliseconds);\n        }\n    }\n\n    void storeNewValueInDatabase(String information) throws SPlatformUpdateException, SPlatformNotFoundException {\n        platformInformationService.updatePlatformInfo(platformRetriever.getPlatform(), information);\n    }\n\n    List<Long> readCounters() {\n        try {\n            String information = platformRetriever.getPlatform().getInformation();\n            if (information == null || information.isBlank()) {\n                throw new IllegalStateException(\"Invalid database. Please reset it and restart.\");\n            }\n            return decryptDataFromDatabase(information);\n        } catch (SPlatformNotFoundException | IOException e) {\n            throw new IllegalStateException(\"Cannot read from database table 'platform'\", e);\n        }\n    }\n\n    @Override\n    public long getCurrentNumberOfStartedProcessInstances() {\n        return counters.size();\n    }\n\n    String encryptDataBeforeSendingToDatabase(List<Long> counters) throws IOException {\n        return encrypt(OBJECT_MAPPER.writeValueAsBytes(counters));\n    }\n\n    List<Long> decryptDataFromDatabase(String information) throws IOException {\n        return OBJECT_MAPPER.readValue(decrypt(information), new TypeReference<>() {\n        });\n    }\n\n    private static String encrypt(byte[] data) {\n        try {\n            return SimpleEncryptor.encrypt(data);\n        } catch (GeneralSecurityException e) {\n            throw new IllegalStateException(\"Cannot cipher information\", e);\n        }\n    }\n\n    private static byte[] decrypt(String information) {\n        try {\n            return SimpleEncryptor.decrypt(information);\n        } catch (GeneralSecurityException e) {\n            throw new IllegalStateException(\"Cannot decipher information\", e);\n        }\n    }\n\n    List<Long> fetchLastArchivedProcessInstanceStartDates(Long oldestValidDate) throws Exception {\n        final List<Long> startDates = transactionService.executeInTransaction(\n                () -> processInstanceService.getLastArchivedProcessInstanceStartDates(oldestValidDate));\n        // print the start dates to the log:\n        log.debug(\"Last archived process instance start dates: {}\", startDates);\n        return startDates;\n    }\n\n    public void verifyCountersCoherence(List<Long> counters, Long oldestValidDate) throws Exception {\n        final List<Long> lastArchivedProcessInstanceStartDates = fetchLastArchivedProcessInstanceStartDates(\n                oldestValidDate);\n        for (Long startDate : lastArchivedProcessInstanceStartDates) {\n            if (!counters.contains(startDate)) {\n                throw new IllegalStateException(\"Invalid database. Please reset it and restart.\");\n            }\n        }\n    }\n\n    void logCaseLimitProgressIfThresholdReached() {\n        var percentBeforeThisNewCase = (float) ((getCounters().size() - 1) * 100) / LIMIT;\n        var percentWithThisNewCase = (float) ((getCounters().size()) * 100) / LIMIT;\n        for (Integer threshold : THRESHOLDS_IN_PERCENT) {\n            if (percentBeforeThisNewCase < threshold && percentWithThisNewCase >= threshold) {\n                log.warn(\"You have started {}% of your allowed cases.\"\n                        + \"If you need more volume, please consider subscribing to an Enterprise edition.\",\n                        threshold);\n            }\n        }\n    }\n\n    /**\n     * Returns a timestamp to a human-readable format\n     */\n    private String getStringRepresentation(long timestamp) {\n        return new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\").format(new Date(timestamp));\n    }\n\n    private long getNextResetTimestamp(List<Long> timestamps) {\n        return Collections.min(timestamps) + PERIOD_IN_MILLIS;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/SIllegalStateTransition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityExecutionException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SIllegalStateTransition extends SActivityExecutionException {\n\n    private static final long serialVersionUID = 6940283544247417112L;\n\n    public SIllegalStateTransition(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n    public SIllegalStateTransition(String message) {\n        super(message);\n    }\n\n    public SIllegalStateTransition(Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/SNotSerializableException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SNotSerializableException extends SBonitaException {\n\n    private static final long serialVersionUID = -229226043369898514L;\n\n    public SNotSerializableException(final String connectorDefinitionId, final String connectorDefinitionVersion,\n            final String key, final Object value) {\n        super(createMessage(connectorDefinitionId, connectorDefinitionVersion, key, value));\n    }\n\n    private static String createMessage(final String connectorDefinitionId, final String connectorDefinitionVersion,\n            final String key, final Object value) {\n        final StringBuilder stringBuilder = new StringBuilder(\"the connector \");\n        stringBuilder.append(connectorDefinitionId);\n        stringBuilder.append(' ');\n        stringBuilder.append(connectorDefinitionVersion);\n        stringBuilder.append(\" have an unserializable output and was called directly from the api. name=\");\n        stringBuilder.append(key);\n        stringBuilder.append(\" value=\");\n        stringBuilder.append(value.toString());\n        return stringBuilder.toString();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/SUnreleasableTaskException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SUnreleasableTaskException extends SBonitaException {\n\n    private static final long serialVersionUID = 2845399803318977578L;\n\n    public SUnreleasableTaskException(final String string) {\n        super(string);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/StartFlowNodeFilter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Vincent Elcrin\n */\npublic class StartFlowNodeFilter implements Filter<SFlowNodeDefinition> {\n\n    @Override\n    public boolean mustSelect(final SFlowNodeDefinition flowNode) {\n        return flowNode.isStartable();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/StateBehaviors.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.TreeSet;\n\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.actor.mapping.SActorNotFoundException;\nimport org.bonitasoft.engine.actor.mapping.model.SActor;\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.bpm.connector.ConnectorState;\nimport org.bonitasoft.engine.bpm.model.impl.BPMInstancesCreator;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.business.data.RefBusinessDataRetriever;\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.classloader.SClassLoaderException;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.ScopedException;\nimport org.bonitasoft.engine.core.connector.ConnectorInstanceService;\nimport org.bonitasoft.engine.core.connector.exception.SConnectorInstanceModificationException;\nimport org.bonitasoft.engine.core.connector.exception.SConnectorInstanceReadException;\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.filter.FilterResult;\nimport org.bonitasoft.engine.core.filter.UserFilterService;\nimport org.bonitasoft.engine.core.filter.exception.SUserFilterExecutionException;\nimport org.bonitasoft.engine.core.operation.OperationService;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.core.process.comment.api.SCommentAddException;\nimport org.bonitasoft.engine.core.process.comment.api.SCommentService;\nimport org.bonitasoft.engine.core.process.comment.api.SystemCommentType;\nimport org.bonitasoft.engine.core.process.definition.model.SActivityDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SBusinessDataDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SConnectorDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.definition.model.SHumanTaskDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SLoopCharacteristics;\nimport org.bonitasoft.engine.core.process.definition.model.SMultiInstanceLoopCharacteristics;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SReceiveTaskDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SSendTaskDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SUserFilterDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SBoundaryEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SCatchEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SIntermediateCatchEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SThrowEventDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.RefBusinessDataService;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.*;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceModificationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowElementsContainerType;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping;\nimport org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SMultiInstanceActivityInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SBoundaryEventInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SFlowNodeSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SIntermediateCatchEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SThrowEventInstance;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceService;\nimport org.bonitasoft.engine.data.instance.api.ParentContainerResolver;\nimport org.bonitasoft.engine.data.instance.exception.SDataInstanceException;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\nimport org.bonitasoft.engine.dependency.model.ScopeType;\nimport org.bonitasoft.engine.execution.event.EventsHandler;\nimport org.bonitasoft.engine.execution.work.BPMWorkFactory;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.SUserNotFoundException;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.work.SWorkRegisterException;\nimport org.bonitasoft.engine.work.WorkService;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\n@Component\npublic class StateBehaviors {\n\n    private static final int BATCH_SIZE = 20;\n    protected final ParentContainerResolver parentContainerResolver;\n    private final BPMInstancesCreator bpmInstancesCreator;\n    private final EventsHandler eventsHandler;\n    private final ActivityInstanceService activityInstanceService;\n    private final UserFilterService userFilterService;\n    private final ClassLoaderService classLoaderService;\n    private final ActorMappingService actorMappingService;\n    private final ExpressionResolverService expressionResolverService;\n    private final DataInstanceService dataInstanceService;\n    private final OperationService operationService;\n    private final WorkService workService;\n    private final ContainerRegistry containerRegistry;\n    private final EventInstanceService eventInstanceService;\n    private final ConnectorInstanceService connectorInstanceService;\n    private final SCommentService commentService;\n\n    private final IdentityService identityService;\n    private final WaitingEventsInterrupter waitingEventsInterrupter;\n    private final RefBusinessDataService refBusinessDataService;\n    private final RefBusinessDataRetriever refBusinessDataRetriever;\n    private final BPMWorkFactory workFactory;\n    private ProcessInstanceInterruptor processInstanceInterruptor;\n\n    public StateBehaviors(final BPMInstancesCreator bpmInstancesCreator, final EventsHandler eventsHandler,\n            final ActivityInstanceService activityInstanceService, final UserFilterService userFilterService,\n            final ClassLoaderService classLoaderService,\n            final ActorMappingService actorMappingService, final ConnectorInstanceService connectorInstanceService,\n            final ExpressionResolverService expressionResolverService,\n            final DataInstanceService dataInstanceService, final OperationService operationService,\n            final WorkService workService,\n            final ContainerRegistry containerRegistry, final EventInstanceService eventInstanceService,\n            final SCommentService commentService,\n            final IdentityService identityService, final ParentContainerResolver parentContainerResolver,\n            final WaitingEventsInterrupter waitingEventsInterrupter,\n            final RefBusinessDataService refBusinessDataService,\n            final RefBusinessDataRetriever refBusinessDataRetriever,\n            final BPMWorkFactory workFactory, final ProcessInstanceInterruptor processInstanceInterruptor) {\n        super();\n        this.bpmInstancesCreator = bpmInstancesCreator;\n        this.eventsHandler = eventsHandler;\n        this.activityInstanceService = activityInstanceService;\n        this.userFilterService = userFilterService;\n        this.classLoaderService = classLoaderService;\n        this.actorMappingService = actorMappingService;\n        this.connectorInstanceService = connectorInstanceService;\n        this.expressionResolverService = expressionResolverService;\n        this.dataInstanceService = dataInstanceService;\n        this.operationService = operationService;\n        this.workService = workService;\n        this.containerRegistry = containerRegistry;\n        this.eventInstanceService = eventInstanceService;\n        this.commentService = commentService;\n        this.identityService = identityService;\n        this.parentContainerResolver = parentContainerResolver;\n        this.refBusinessDataService = refBusinessDataService;\n        this.refBusinessDataRetriever = refBusinessDataRetriever;\n        this.waitingEventsInterrupter = waitingEventsInterrupter;\n        this.workFactory = workFactory;\n        this.processInstanceInterruptor = processInstanceInterruptor;\n    }\n\n    public DataInstanceContainer getParentContainerType(final SFlowNodeInstance flowNodeInstance) {\n        DataInstanceContainer parentContainerType;\n        if (flowNodeInstance.getLogicalGroup(2) <= 0) {\n            parentContainerType = DataInstanceContainer.PROCESS_INSTANCE;\n        } else {\n            parentContainerType = DataInstanceContainer.ACTIVITY_INSTANCE;\n        }\n        return parentContainerType;\n    }\n\n    public DataInstanceService getDataInstanceService() {\n        return dataInstanceService;\n    }\n\n    public void mapDataOutputOfMultiInstance(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException {\n        if (flowNodeInstance instanceof SActivityInstance\n                && !SFlowNodeType.MULTI_INSTANCE_ACTIVITY.equals(flowNodeInstance.getType())) {\n            final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer();\n            final SActivityDefinition activityDefinition = (SActivityDefinition) processContainer\n                    .getFlowNode(flowNodeInstance.getFlowNodeDefinitionId());\n            if (activityDefinition != null) {// can be null if the activity was added in runtime\n                try {\n                    final SLoopCharacteristics loopCharacteristics = activityDefinition.getLoopCharacteristics();\n                    if (loopCharacteristics instanceof SMultiInstanceLoopCharacteristics miLoop\n                            && ((SMultiInstanceLoopCharacteristics) loopCharacteristics)\n                                    .getDataOutputItemRef() != null) {\n                        final SBusinessDataDefinition businessData = processContainer\n                                .getBusinessDataDefinition(miLoop.getLoopDataOutputRef());\n                        if (businessData == null) {\n                            mapDataOutputOfMultiInstance(flowNodeInstance, miLoop);\n                        } else {\n                            mapMultiInstanceBusinessDataOutput(flowNodeInstance, miLoop);\n                        }\n                    }\n                } catch (final SBonitaException sbe) {\n                    throw new SActivityStateExecutionException(\n                            \"Error while mapping multi instance output of \" + flowNodeInstance,\n                            ScopedException.ITERATION, sbe);\n                }\n            }\n        }\n    }\n\n    private void mapMultiInstanceBusinessDataOutput(final SFlowNodeInstance flowNodeInstance,\n            final SMultiInstanceLoopCharacteristics miLoop)\n            throws SRefBusinessDataInstanceNotFoundException, SBonitaReadException,\n            SRefBusinessDataInstanceModificationException {\n        final SRefBusinessDataInstance outputMIRef = refBusinessDataService.getFlowNodeRefBusinessDataInstance(\n                miLoop.getDataOutputItemRef(), flowNodeInstance.getId());\n        final SRefBusinessDataInstance outputMILoopRef = refBusinessDataService.getRefBusinessDataInstance(\n                miLoop.getLoopDataOutputRef(), flowNodeInstance.getParentProcessInstanceId());\n        final SProcessMultiRefBusinessDataInstance multiRefBusinessDataInstance = (SProcessMultiRefBusinessDataInstance) outputMILoopRef;\n        List<Long> dataIds = multiRefBusinessDataInstance.getDataIds();\n        if (dataIds == null) {\n            dataIds = new ArrayList<>();\n        }\n        final Long dataId = ((SFlowNodeSimpleRefBusinessDataInstance) outputMIRef).getDataId();\n        dataIds.add(dataId);\n        refBusinessDataService.updateRefBusinessDataInstance(multiRefBusinessDataInstance, dataIds);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public void mapDataOutputOfMultiInstance(final SFlowNodeInstance flowNodeInstance,\n            final SMultiInstanceLoopCharacteristics miLoop)\n            throws SBonitaException {\n        final SDataInstance outputData = dataInstanceService.getDataInstance(miLoop.getDataOutputItemRef(),\n                flowNodeInstance.getId(),\n                DataInstanceContainer.ACTIVITY_INSTANCE.name(), parentContainerResolver);\n        final SDataInstance loopData = dataInstanceService.getDataInstance(miLoop.getLoopDataOutputRef(),\n                flowNodeInstance.getId(),\n                DataInstanceContainer.ACTIVITY_INSTANCE.name(), parentContainerResolver);\n        if (outputData != null && loopData != null) {\n            final Serializable value = loopData.getValue();\n            final int index = flowNodeInstance.getLoopCounter();\n            if (value instanceof List<?>) {\n                ((List<Serializable>) value).set(index, outputData.getValue());\n            } else {\n                throw new SActivityExecutionException(\"unable to map the ouput of the multi instanciated activity \"\n                        + flowNodeInstance.getName() + \" the output loop data named \" + loopData.getName()\n                        + \" is not a list but \"\n                        + loopData.getClassName());\n            }\n            final EntityUpdateDescriptor entityUpdateDescriptor = new EntityUpdateDescriptor();\n            entityUpdateDescriptor.addField(\"value\", value);\n            dataInstanceService.updateDataInstance(loopData, entityUpdateDescriptor);\n        }\n    }\n\n    public void mapActors(final SFlowNodeInstance flowNodeInstance,\n            final SFlowElementContainerDefinition processContainer)\n            throws SActivityStateExecutionException {\n        if (SFlowNodeType.USER_TASK.equals(flowNodeInstance.getType())\n                || SFlowNodeType.MANUAL_TASK.equals(flowNodeInstance.getType())) {\n            final SHumanTaskDefinition humanTaskDefinition = (SHumanTaskDefinition) processContainer\n                    .getFlowNode(flowNodeInstance.getFlowNodeDefinitionId());\n            if (humanTaskDefinition != null) {\n                final String actorName = humanTaskDefinition.getActorName();\n                final long processDefinitionId = flowNodeInstance.getLogicalGroup(0);\n                final SUserFilterDefinition sUserFilterDefinition = humanTaskDefinition.getSUserFilterDefinition();\n                if (sUserFilterDefinition != null) {\n                    try {\n                        mapUsingUserFilters(flowNodeInstance, humanTaskDefinition, actorName, processDefinitionId,\n                                sUserFilterDefinition);\n                    } catch (SActivityStateExecutionException e) {\n                        throw e;\n                    } catch (SBonitaException e) {\n                        throw new SActivityStateExecutionException(\n                                \"Error while mapping actor '\" + actorName + \"' with filter \"\n                                        + sUserFilterDefinition.getUserFilterId() + \" for \" + flowNodeInstance,\n                                ScopedException.ACTOR_MAPPING, e);\n                    }\n                } else {\n                    try {\n                        mapUsingActors(flowNodeInstance, actorName, processDefinitionId);\n                    } catch (SBonitaException e) {\n                        throw new SActivityStateExecutionException(\"Error while mapping actors for \" + flowNodeInstance,\n                                ScopedException.ACTOR_MAPPING, e);\n                    }\n                }\n            }\n\n        }\n    }\n\n    private void mapUsingActors(final SFlowNodeInstance flowNodeInstance, final String actorName,\n            final long processDefinitionId)\n            throws SActorNotFoundException, SActivityCreationException {\n        final SActor actor = actorMappingService.getActor(actorName, processDefinitionId);\n        final SPendingActivityMapping mapping = SPendingActivityMapping.builder().activityId(flowNodeInstance.getId())\n                .actorId(actor.getId()).build();\n        activityInstanceService.addPendingActivityMappings(mapping);\n    }\n\n    void mapUsingUserFilters(final SFlowNodeInstance flowNodeInstance, final SHumanTaskDefinition humanTaskDefinition,\n            final String actorName,\n            final long processDefinitionId, final SUserFilterDefinition sUserFilterDefinition)\n            throws SClassLoaderException, SUserFilterExecutionException,\n            SActivityStateExecutionException, SActivityCreationException, SFlowNodeNotFoundException,\n            SFlowNodeReadException,\n            SActivityModificationException {\n        final ClassLoader processClassloader = classLoaderService.getClassLoader(\n                identifier(ScopeType.PROCESS, processDefinitionId));\n        final SExpressionContext expressionContext = new SExpressionContext(flowNodeInstance.getId(),\n                DataInstanceContainer.ACTIVITY_INSTANCE.name(),\n                flowNodeInstance.getLogicalGroup(0));\n        final FilterResult result = userFilterService.executeFilter(processDefinitionId, sUserFilterDefinition,\n                sUserFilterDefinition.getInputs(),\n                processClassloader, expressionContext, actorName);\n        final List<Long> userIds = result.getResult();\n        if (userIds == null || userIds.isEmpty() || userIds.contains(0L) || userIds.contains(-1L)) {\n            throw new SActivityStateExecutionException(\n                    \"No user id returned by the user filter \" + sUserFilterDefinition + \" on activity \"\n                            + humanTaskDefinition.getName(),\n                    ScopedException.ACTOR_MAPPING);\n        }\n        for (final Long userId : new TreeSet<>(userIds)) {\n            final SPendingActivityMapping mapping = SPendingActivityMapping.builder()\n                    .activityId(flowNodeInstance.getId()).userId(userId).build();\n            activityInstanceService.addPendingActivityMappings(mapping);\n        }\n        if (userIds.size() == 1 && result.shouldAutoAssignTaskIfSingleResult()) {\n            final Long userId = userIds.get(0);\n            activityInstanceService.assignHumanTask(flowNodeInstance.getId(), userId);\n            //system comment is added after the evaluation of the display name\n        }\n    }\n\n    public void registerWaitingEvent(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException {\n        // handle catch event\n        if (flowNodeInstance instanceof SIntermediateCatchEventInstance intermediateCatchEventInstance) {\n            // handleEventTriggerInstances(processDefinition, intermediateCatchEventInstance);\n            final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer();\n            final SIntermediateCatchEventDefinition intermediateCatchEventDefinition = (SIntermediateCatchEventDefinition) processContainer\n                    .getFlowNode(intermediateCatchEventInstance.getFlowNodeDefinitionId());\n            try {\n                eventsHandler.handleCatchEvent(processDefinition, intermediateCatchEventDefinition,\n                        intermediateCatchEventInstance);\n            } catch (final SBonitaException e) {\n                throw new SActivityStateExecutionException(\"unable to handle catch event \" + flowNodeInstance,\n                        ScopedException.EVENT, e);\n            }\n        } else if (flowNodeInstance instanceof SReceiveTaskInstance receiveTaskInstance) {\n            final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer();\n            final SReceiveTaskDefinition receiveTaskIDefinition = (SReceiveTaskDefinition) processContainer\n                    .getFlowNode(receiveTaskInstance\n                            .getFlowNodeDefinitionId());\n            try {\n                eventsHandler.handleCatchMessage(processDefinition, receiveTaskIDefinition, receiveTaskInstance);\n            } catch (final SBonitaException e) {\n                throw new SActivityStateExecutionException(\"unable to handle catch event \" + flowNodeInstance,\n                        ScopedException.EVENT, e);\n            }\n        }\n    }\n\n    public void handleBoundaryEvent(final SProcessDefinition processDefinition,\n            final SBoundaryEventInstance boundaryInstance)\n            throws SActivityStateExecutionException {\n        final long activityInstanceId = boundaryInstance.getActivityInstanceId();\n        // FIXME: add activity name in SBoundaryEventInstance to avoid the getActivityInstance below\n        try {\n            final SActivityInstance activityInstance = activityInstanceService.getActivityInstance(activityInstanceId);\n\n            final SActivityDefinition activityDefinition = (SActivityDefinition) processDefinition.getProcessContainer()\n                    .getFlowNode(\n                            activityInstance.getFlowNodeDefinitionId());\n            final SBoundaryEventDefinition boundaryEventDefinition = activityDefinition\n                    .getBoundaryEventDefinition(boundaryInstance.getName());\n            eventsHandler.handleCatchEvent(processDefinition, boundaryEventDefinition, boundaryInstance);\n        } catch (final SBonitaException e) {\n            throw new SActivityStateExecutionException(\"Unable to handle catch event \" + boundaryInstance,\n                    ScopedException.EVENT,\n                    e);\n        }\n    }\n\n    public void createData(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException {\n        if (flowNodeInstance instanceof SActivityInstance) {\n            bpmInstancesCreator.createDataInstances(processDefinition, flowNodeInstance);\n        }\n    }\n\n    public void updateDisplayNameAndDescription(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException {\n        final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer();\n        final SFlowNodeDefinition flowNode = processContainer\n                .getFlowNode(flowNodeInstance.getFlowNodeDefinitionId());\n        if (flowNode != null) {\n            final SExpression displayNameExpression = flowNode.getDisplayName();\n            final SExpression displayDescriptionExpression = flowNode.getDisplayDescription();\n            final SExpressionContext sExpressionContext = new SExpressionContext(flowNodeInstance.getId(),\n                    DataInstanceContainer.ACTIVITY_INSTANCE.name(),\n                    processDefinition.getId());\n            try {\n                String displayName = flowNode.getName();\n                if (displayNameExpression != null) {\n                    displayName = (String) expressionResolverService.evaluate(displayNameExpression,\n                            sExpressionContext);\n                }\n                activityInstanceService.updateDisplayName(flowNodeInstance, displayName);\n            } catch (SBonitaException e) {\n                throw new SActivityStateExecutionException(\"Error while updating display name\",\n                        ScopedException.GENERAL_INFORMATION, e);\n            }\n            try {\n                String displayDescription = flowNode.getDescription();\n                if (displayDescriptionExpression != null) {\n                    displayDescription = (String) expressionResolverService.evaluate(displayDescriptionExpression,\n                            sExpressionContext);\n                }\n                activityInstanceService.updateDisplayDescription(flowNodeInstance, displayDescription);\n            } catch (SBonitaException e) {\n                throw new SActivityStateExecutionException(\"Error while updating display description\",\n                        ScopedException.GENERAL_INFORMATION, e);\n            }\n        }\n    }\n\n    public void updateExpectedDuration(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException {\n        final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer();\n        final SFlowNodeDefinition flowNodeDefinition = processContainer\n                .getFlowNode(flowNodeInstance.getFlowNodeDefinitionId());\n        if (!(flowNodeDefinition instanceof SHumanTaskDefinition humanTaskDefinition)) {\n            return;\n        }\n        final SExpression expectedDurationExpression = humanTaskDefinition.getExpectedDuration();\n        if (expectedDurationExpression == null) {\n            return;\n        }\n        final SExpressionContext sExpressionContext = new SExpressionContext(flowNodeInstance.getId(),\n                DataInstanceContainer.ACTIVITY_INSTANCE.name(),\n                processDefinition.getId());\n        try {\n            final Long duration = (Long) expressionResolverService.evaluate(expectedDurationExpression,\n                    sExpressionContext);\n            if (duration == null) {\n                activityInstanceService.setExpectedEndDate(flowNodeInstance, null);\n            } else {\n                activityInstanceService.setExpectedEndDate(flowNodeInstance, System.currentTimeMillis() + duration);\n            }\n        } catch (final SBonitaException e) {\n            throw new SActivityStateExecutionException(\"Error while updating expected end date\",\n                    ScopedException.GENERAL_INFORMATION, e);\n        }\n    }\n\n    public void updateDisplayDescriptionAfterCompletion(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException {\n        final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer();\n        final SFlowNodeDefinition flowNode = processContainer\n                .getFlowNode(flowNodeInstance.getFlowNodeDefinitionId());\n        if (flowNode != null) {\n            final SExpression displayDescriptionAfterCompletionExpression = flowNode\n                    .getDisplayDescriptionAfterCompletion();\n            final SExpressionContext sExpressionContext = new SExpressionContext(flowNodeInstance.getId(),\n                    DataInstanceContainer.ACTIVITY_INSTANCE.name(),\n                    processDefinition.getId());\n            try {\n                final String displayDescriptionAfterCompletion;\n                if (displayDescriptionAfterCompletionExpression != null) {\n                    displayDescriptionAfterCompletion = (String) expressionResolverService.evaluate(\n                            displayDescriptionAfterCompletionExpression,\n                            sExpressionContext);\n                    activityInstanceService.updateDisplayDescription(flowNodeInstance,\n                            displayDescriptionAfterCompletion);\n                }\n            } catch (final SBonitaException e) {\n                throw new SActivityStateExecutionException(\"Error while updating display description\",\n                        ScopedException.GENERAL_INFORMATION, e);\n            }\n        }\n    }\n\n    public void executeOperations(final SProcessDefinition processDefinition, final SActivityInstance activityInstance)\n            throws SActivityStateExecutionException {\n        try {\n            final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer();\n            final SFlowNodeDefinition flowNode = processContainer\n                    .getFlowNode(activityInstance.getFlowNodeDefinitionId());\n            if (flowNode instanceof SActivityDefinition activityDefinition) {\n                final List<SOperation> sOperations = activityDefinition.getSOperations();\n                final SExpressionContext sExpressionContext = new SExpressionContext(activityInstance.getId(),\n                        DataInstanceContainer.ACTIVITY_INSTANCE.name(),\n                        processDefinition.getId());\n                operationService.execute(sOperations, sExpressionContext);\n            }\n        } catch (final SOperationExecutionException e) {\n            throw new SActivityStateExecutionException(\"Error while executing operations\",\n                    ScopedException.OPERATION, e);\n        }\n    }\n\n    public void handleThrowEvent(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException {\n        if (flowNodeInstance instanceof SThrowEventInstance throwEventInstance) {\n            final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer();\n            final SThrowEventDefinition eventDefinition = (SThrowEventDefinition) processContainer\n                    .getFlowNode(throwEventInstance.getFlowNodeDefinitionId());\n            try {\n                eventsHandler.handleThrowEvent(processDefinition, eventDefinition, throwEventInstance);\n            } catch (final SBonitaException e) {\n                throw new SActivityStateExecutionException(\"Unable to handle throw event \" + flowNodeInstance,\n                        ScopedException.EVENT, e);\n            }\n        } else if (SFlowNodeType.SEND_TASK.equals(flowNodeInstance.getType())) {\n            final SSendTaskInstance sendTaskInstance = (SSendTaskInstance) flowNodeInstance;\n            final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer();\n            final SSendTaskDefinition sendTaskDefinition = (SSendTaskDefinition) processContainer\n                    .getFlowNode(sendTaskInstance.getFlowNodeDefinitionId());\n            try {\n                eventsHandler.handleThrowMessage(processDefinition, sendTaskDefinition, sendTaskInstance);\n            } catch (final SBonitaException e) {\n                throw new SActivityStateExecutionException(\"Unable to handle throw message \" + flowNodeInstance,\n                        ScopedException.EVENT, e);\n            }\n        }\n    }\n\n    public void executeChildrenActivities(final SFlowNodeInstance flowNodeInstance) throws SActivityExecutionException {\n        try {\n            int i = 0;\n            List<SActivityInstance> childrenOfAnActivity;\n            do {\n                childrenOfAnActivity = activityInstanceService.getChildrenOfAnActivity(flowNodeInstance.getId(), i,\n                        BATCH_SIZE);\n                for (final SActivityInstance sActivityInstance : childrenOfAnActivity) {\n                    containerRegistry.executeFlowNode(sActivityInstance);\n                }\n                i += BATCH_SIZE;\n            } while (childrenOfAnActivity.size() == BATCH_SIZE);\n        } catch (final SBonitaException e) {\n            throw new SActivityExecutionException(e);\n        }\n    }\n\n    public void interruptSubActivities(SFlowNodeInstance flowNodeInstance, final SStateCategory stateCategory)\n            throws SBonitaException {\n        processInstanceInterruptor.interruptChildrenOfFlowNodeInstance(flowNodeInstance, stateCategory);\n    }\n\n    public void executeConnectorInWork(final Long processDefinitionId, final long processInstanceId,\n            final long rootProcessInstanceId,\n            final long flowNodeDefinitionId,\n            final long flowNodeInstanceId, final SConnectorInstance connector,\n            final SConnectorDefinition sConnectorDefinition)\n            throws SActivityStateExecutionException {\n        final long connectorInstanceId = connector.getId();\n        // final Long connectorDefinitionId = sConnectorDefinition.getId();// FIXME: Uncomment when generate id\n        final String connectorDefinitionName = sConnectorDefinition.getName();\n        try {\n            connectorInstanceService.setState(connector, ConnectorState.EXECUTING.name());\n            workService.registerWork(workFactory.createExecuteConnectorOfActivityDescriptor(processDefinitionId,\n                    processInstanceId, rootProcessInstanceId, flowNodeDefinitionId,\n                    flowNodeInstanceId, connectorInstanceId,\n                    sConnectorDefinition.getConnectorId(),\n                    connectorDefinitionName,\n                    sConnectorDefinition.getActivationEvent().name()));\n        } catch (final SConnectorInstanceModificationException e) {\n            throw new SActivityStateExecutionException(\"Unable to set ConnectorState to EXECUTING\",\n                    ScopedException.CONNECTOR, e);\n        } catch (final SWorkRegisterException e) {\n            throw new SActivityStateExecutionException(\n                    \"Unable to register the work that execute the connector \" + connector + \" on \" + flowNodeInstanceId,\n                    ScopedException.CONNECTOR,\n                    e);\n        }\n    }\n\n    public void createAttachedBoundaryEvents(final SProcessDefinition processDefinition,\n            final SActivityInstance activityInstance)\n            throws SActivityStateExecutionException {\n        final SActivityDefinition activityDefinition = (SActivityDefinition) processDefinition.getProcessContainer()\n                .getFlowNode(\n                        activityInstance.getFlowNodeDefinitionId());\n        if (mustAddBoundaryEvents(activityInstance, activityDefinition)) {\n            createAttachedBoundaryEvents(processDefinition, activityInstance, activityDefinition);\n        }\n    }\n\n    private void createAttachedBoundaryEvents(final SProcessDefinition processDefinition,\n            final SActivityInstance activityInstance,\n            final SActivityDefinition activityDefinition) throws SActivityStateExecutionException {\n        final List<SBoundaryEventDefinition> boundaryEventDefinitions = activityDefinition\n                .getBoundaryEventDefinitions();\n        try {\n            final SBoundaryEventInstanceBuilderFactory boundaryEventInstanceBuilder = BuilderFactory\n                    .get(SBoundaryEventInstanceBuilderFactory.class);\n            final long rootProcessInstanceId = activityInstance\n                    .getLogicalGroup(boundaryEventInstanceBuilder.getRootProcessInstanceIndex());\n            final long parentProcessInstanceId = activityInstance\n                    .getLogicalGroup(boundaryEventInstanceBuilder.getParentProcessInstanceIndex());\n            final SFlowElementsContainerType containerType = getContainerType(activityInstance,\n                    boundaryEventInstanceBuilder);\n\n            for (final SBoundaryEventDefinition boundaryEventDefinition : boundaryEventDefinitions) {\n                createBoundaryEvent(processDefinition, activityInstance, rootProcessInstanceId, parentProcessInstanceId,\n                        containerType,\n                        boundaryEventDefinition);\n            }\n        } catch (final SBonitaException e) {\n            throw new SActivityStateExecutionException(\n                    \"Unable to create boundary events attached to activity \" + activityInstance.getName(),\n                    ScopedException.EVENT, e);\n        }\n    }\n\n    private void createBoundaryEvent(final SProcessDefinition processDefinition,\n            final SActivityInstance activityInstance, final long rootProcessInstanceId,\n            final long parentProcessInstanceId, final SFlowElementsContainerType containerType,\n            final SBoundaryEventDefinition boundaryEventDefinition)\n            throws SBonitaException {\n        final SBoundaryEventInstance boundaryEventInstance = (SBoundaryEventInstance) bpmInstancesCreator\n                .createFlowNodeInstance(processDefinition.getId(),\n                        rootProcessInstanceId, activityInstance.getParentContainerId(), containerType,\n                        boundaryEventDefinition,\n                        rootProcessInstanceId, parentProcessInstanceId, false, -1, SStateCategory.NORMAL,\n                        activityInstance.getId());\n\n        // no need to handle failed state, creation is in the same tx\n        containerRegistry.executeFlowNodeInSameThread(boundaryEventInstance, containerType.name());\n    }\n\n    private SFlowElementsContainerType getContainerType(final SActivityInstance activityInstance,\n            final SBoundaryEventInstanceBuilderFactory boundaryEventInstanceBuilder) {\n        SFlowElementsContainerType containerType = SFlowElementsContainerType.PROCESS;\n        final long parentActivityInstanceId = activityInstance\n                .getLogicalGroup(boundaryEventInstanceBuilder.getParentActivityInstanceIndex());\n        if (parentActivityInstanceId > 0) {\n            containerType = SFlowElementsContainerType.FLOWNODE;\n        }\n        return containerType;\n    }\n\n    private boolean mustAddBoundaryEvents(final SActivityInstance activityInstance,\n            final SActivityDefinition activityDefinition) {\n        // avoid to add boundary events in children of multi instance\n        return activityDefinition != null && !activityDefinition.getBoundaryEventDefinitions().isEmpty()\n                && !isChildOfLoopOrMultiInstance(activityInstance, activityDefinition);\n    }\n\n    private boolean isChildOfLoopOrMultiInstance(final SActivityInstance activityInstance,\n            final SActivityDefinition activityDefinition) {\n        return activityDefinition.getLoopCharacteristics() != null\n                && !(SFlowNodeType.MULTI_INSTANCE_ACTIVITY.equals(activityInstance.getType())\n                        || SFlowNodeType.LOOP_ACTIVITY.equals(activityInstance.getType()));\n    }\n\n    public void interruptAttachedBoundaryEvent(final SProcessDefinition processDefinition,\n            final SActivityInstance activityInstance,\n            final SStateCategory categoryState) throws SActivityStateExecutionException {\n        try {\n            final List<SBoundaryEventInstance> boundaryEventInstances = eventInstanceService\n                    .getActivityBoundaryEventInstances(activityInstance.getId(), 0,\n                            QueryOptions.UNLIMITED_NUMBER_OF_RESULTS);\n            for (final SBoundaryEventInstance boundaryEventInstance : boundaryEventInstances) {\n                // don't abort boundary event that put this activity in aborting state\n                if (activityInstance.getAbortedByBoundary() != boundaryEventInstance.getId()) {\n                    final SCatchEventDefinition catchEventDef = processDefinition.getProcessContainer()\n                            .getBoundaryEvent(boundaryEventInstance.getName());\n                    waitingEventsInterrupter.interruptWaitingEvents(processDefinition, boundaryEventInstance,\n                            catchEventDef);\n                    activityInstanceService.setStateCategory(boundaryEventInstance, categoryState);\n                    containerRegistry.executeFlowNode(boundaryEventInstance);\n                }\n            }\n        } catch (final SBonitaException e) {\n            throw new SActivityStateExecutionException(\n                    \"Unable to cancel boundary events attached to activity \" + activityInstance.getName(),\n                    ScopedException.EVENT, e);\n        }\n    }\n\n    public void addAssignmentSystemCommentIfTaskWasAutoAssign(final SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException {\n        if (SFlowNodeType.USER_TASK.equals(flowNodeInstance.getType())\n                || SFlowNodeType.MANUAL_TASK.equals(flowNodeInstance.getType())) {\n            final long userId = ((SHumanTaskInstance) flowNodeInstance).getAssigneeId();\n            if (userId > 0) {\n                try {\n                    addAssignmentSystemComment(flowNodeInstance, userId);\n                } catch (final SBonitaException e) {\n                    throw new SActivityStateExecutionException(\n                            \"Error while adding a comment on task \" + flowNodeInstance,\n                            ScopedException.GENERAL_INFORMATION, e);\n                }\n            }\n        }\n\n    }\n\n    public void addAssignmentSystemComment(final SFlowNodeInstance flowNodeInstance, final long userId)\n            throws SUserNotFoundException, SCommentAddException {\n        final SUser user = identityService.getUser(userId);\n        if (commentService.isCommentEnabled(SystemCommentType.STATE_CHANGE)) {\n            commentService.addSystemComment(flowNodeInstance.getRootContainerId(),\n                    \"The task \\\"\" + flowNodeInstance.getDisplayName() + \"\\\" is now assigned to \"\n                            + user.getUserName());\n        }\n    }\n\n    public List<SFlowNodeInstance> createInnerInstances(final long processDefinitionId,\n            final SActivityDefinition activity,\n            final SMultiInstanceActivityInstance flowNodeInstance, final int numberOfInstanceToCreate)\n            throws SBonitaException {\n        final SMultiInstanceActivityInstanceBuilderFactory keyProvider = BuilderFactory\n                .get(SMultiInstanceActivityInstanceBuilderFactory.class);\n        final long rootProcessInstanceId = flowNodeInstance.getLogicalGroup(keyProvider.getRootProcessInstanceIndex());\n        final long parentProcessInstanceId = flowNodeInstance\n                .getLogicalGroup(keyProvider.getParentProcessInstanceIndex());\n        int nbOfcreatedInstances = 0;\n        final int nbOfInstances = flowNodeInstance.getNumberOfInstances();\n        final List<SFlowNodeInstance> createdInstances = new ArrayList<>();\n        for (int i = nbOfInstances; i < nbOfInstances + numberOfInstanceToCreate; i++) {\n            createdInstances.add(bpmInstancesCreator.createFlowNodeInstance(processDefinitionId,\n                    flowNodeInstance.getRootContainerId(),\n                    flowNodeInstance.getId(), SFlowElementsContainerType.FLOWNODE, activity, rootProcessInstanceId,\n                    parentProcessInstanceId, true, i,\n                    SStateCategory.NORMAL, -1));\n            nbOfcreatedInstances++;\n        }\n        activityInstanceService.addMultiInstanceNumberOfActiveActivities(flowNodeInstance, nbOfcreatedInstances);\n        final int tokenCount = flowNodeInstance.getTokenCount() + nbOfcreatedInstances;\n        activityInstanceService.setTokenCount(flowNodeInstance, tokenCount);\n        return createdInstances;\n    }\n\n    public int getNumberOfInstancesToCreateFromInputRef(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance,\n            final SMultiInstanceLoopCharacteristics miLoop, final int numberOfInstanceMax)\n            throws SDataInstanceException, SActivityStateExecutionException {\n        final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer();\n        final SBusinessDataDefinition businessData = processContainer\n                .getBusinessDataDefinition(miLoop.getLoopDataInputRef());\n        if (businessData == null) {\n            return getNumberOfInstanceToCreateFromSimpleData(processDefinition, flowNodeInstance, miLoop,\n                    numberOfInstanceMax);\n        }\n        try {\n            return refBusinessDataService.getNumberOfDataOfMultiRefBusinessData(businessData.getName(),\n                    refBusinessDataRetriever.getProcessInstanceIdThatCanContainBusinessData(\n                            flowNodeInstance.getParentProcessInstanceId()));\n        } catch (final SBonitaReadException | SProcessInstanceReadException | SProcessInstanceNotFoundException\n                | SFlowNodeReadException | SFlowNodeNotFoundException sbre) {\n            throw new SActivityStateExecutionException(\"Error while counting number of multi instances to create\",\n                    ScopedException.ITERATION, sbre);\n        }\n\n    }\n\n    private int getNumberOfInstanceToCreateFromSimpleData(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance,\n            final SMultiInstanceLoopCharacteristics miLoop, final int numberOfInstanceMax)\n            throws SDataInstanceException, SActivityStateExecutionException {\n        final SDataInstance loopDataInput = dataInstanceService.getDataInstance(miLoop.getLoopDataInputRef(),\n                flowNodeInstance.getId(),\n                DataInstanceContainer.ACTIVITY_INSTANCE.name(), parentContainerResolver);\n        if (loopDataInput != null) {\n            final Serializable value = loopDataInput.getValue();\n            if (value instanceof List<?> loopDataInputCollection) {\n                return loopDataInputCollection.size();\n            }\n            throw new SActivityStateExecutionException(\n                    \"The multi instance on activity \" + flowNodeInstance.getName() + \" of process \"\n                            + processDefinition.getName() + \" \" + processDefinition.getVersion()\n                            + \" have a loop data input which is not a java.util.List\",\n                    ScopedException.ITERATION);\n        }\n        return numberOfInstanceMax;\n    }\n\n    public boolean shouldCreateANewInstance(final SMultiInstanceLoopCharacteristics loopCharacteristics,\n            final int numberOfInstances,\n            final SMultiInstanceActivityInstance miActivityInstance) throws SDataInstanceException {\n        if (loopCharacteristics.getLoopCardinality() != null) {\n            return miActivityInstance.getLoopCardinality() > numberOfInstances;\n        }\n        List<?> possibleValues;\n        try {\n            //FIXME find if a business data is used if instead of try catch\n            final SProcessMultiRefBusinessDataInstance multiRef = (SProcessMultiRefBusinessDataInstance) refBusinessDataService\n                    .getRefBusinessDataInstance(\n                            loopCharacteristics.getLoopDataInputRef(), miActivityInstance.getParentProcessInstanceId());\n            possibleValues = multiRef.getDataIds();\n        } catch (final SBonitaException sbe) {\n            final SDataInstance dataInstance = getDataInstanceService().getDataInstance(\n                    loopCharacteristics.getLoopDataInputRef(),\n                    miActivityInstance.getId(), DataInstanceContainer.ACTIVITY_INSTANCE.name(),\n                    parentContainerResolver);\n            possibleValues = (List<?>) dataInstance.getValue();\n        }\n        return possibleValues != null && numberOfInstances < possibleValues.size();\n    }\n\n    public void updateOutputData(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance,\n            final SMultiInstanceLoopCharacteristics miLoop, final int numberOfInstanceMax)\n            throws SDataInstanceException, SActivityStateExecutionException {\n        if (!isBusinessData(processDefinition, miLoop)) {\n            final String loopDataOutputRef = miLoop.getLoopDataOutputRef();\n            if (loopDataOutputRef != null) {\n                final SDataInstance loopDataOutput = dataInstanceService.getDataInstance(loopDataOutputRef,\n                        flowNodeInstance.getId(),\n                        DataInstanceContainer.ACTIVITY_INSTANCE.name(), parentContainerResolver);\n                if (loopDataOutput != null) {\n                    final Serializable outValue = loopDataOutput.getValue();\n                    if (outValue instanceof List) {\n                        updateLoopDataOutputWithListContent((List<?>) outValue, loopDataOutput, numberOfInstanceMax);\n                    } else if (outValue == null) {\n                        updateLoopDataOutputWithNull(loopDataOutput, numberOfInstanceMax);\n                    } else {\n                        throw new SActivityStateExecutionException(\"The multi instance on activity \"\n                                + flowNodeInstance.getName()\n                                + \" of process \" + processDefinition.getName() + \" \" + processDefinition.getVersion()\n                                + \" have a loop data output which is not a java.util.List\", ScopedException.ITERATION);\n                    }\n                }\n            }\n        }\n    }\n\n    boolean isBusinessData(final SProcessDefinition processDefinition, final SMultiInstanceLoopCharacteristics miLoop) {\n        final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer();\n        final SBusinessDataDefinition businessData = processContainer\n                .getBusinessDataDefinition(miLoop.getLoopDataOutputRef());\n        return businessData != null;\n    }\n\n    private void updateLoopDataOutputWithNull(final SDataInstance loopDataOutput, final int numberOfInstanceMax)\n            throws SDataInstanceException {\n        final ArrayList<Object> newOutputList = new ArrayList<>(numberOfInstanceMax);\n        for (int i = 0; i < numberOfInstanceMax; i++) {\n            newOutputList.add(null);\n        }\n        updateLoopDataOutputDataInstance(loopDataOutput, newOutputList);\n    }\n\n    private void updateLoopDataOutputWithListContent(final List<?> outValue, final SDataInstance loopDataOutput,\n            final int numberOfInstanceMax)\n            throws SDataInstanceException {\n        if (outValue.size() < numberOfInstanceMax) {\n            // output data is too small\n            final ArrayList<Object> newOutputList = new ArrayList<>(numberOfInstanceMax);\n            newOutputList.addAll(outValue);\n            for (int i = outValue.size(); i < numberOfInstanceMax; i++) {\n                newOutputList.add(null);\n            }\n            updateLoopDataOutputDataInstance(loopDataOutput, newOutputList);\n        }\n    }\n\n    private void updateLoopDataOutputDataInstance(final SDataInstance loopDataOutput,\n            final ArrayList<Object> newOutputList) throws SDataInstanceException {\n        final EntityUpdateDescriptor updateDescriptor = new EntityUpdateDescriptor();\n        updateDescriptor.addField(SDataInstance.VALUE, newOutputList);\n        dataInstanceService.updateDataInstance(loopDataOutput, updateDescriptor);\n    }\n\n    public List<SConnectorDefinition> getConnectorDefinitions(SProcessDefinition processDefinition,\n            SFlowNodeInstance flowNodeInstance,\n            ConnectorEvent connectorEvent) {\n        final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer();\n        final SFlowNodeDefinition flowNodeDefinition = processContainer\n                .getFlowNode(flowNodeInstance.getFlowNodeDefinitionId());\n        if (flowNodeDefinition == null) {\n            return Collections.emptyList();\n        }\n        return flowNodeDefinition.getConnectors(connectorEvent);\n    }\n\n    public SConnectorInstance getNextConnectorInstance(List<SConnectorDefinition> connectorDefinitions,\n            SFlowNodeInstance flowNodeInstance,\n            ConnectorEvent connectorEvent) throws SConnectorInstanceReadException {\n        if (connectorDefinitions.isEmpty()) {\n            return null;\n        }\n        return connectorInstanceService.getNextExecutableConnectorInstance(flowNodeInstance.getId(),\n                SConnectorInstance.FLOWNODE_TYPE, connectorEvent);\n    }\n\n    public boolean isFirst(List<SConnectorDefinition> connectorsOnEnter,\n            SConnectorInstance nextConnectorInstanceToExecute) {\n        return connectorsOnEnter.get(0).getName().equals(nextConnectorInstanceToExecute.getName());\n    }\n\n    public boolean isNotExecutedYet(SConnectorInstance nextConnectorInstanceToExecute) {\n        return ConnectorState.TO_BE_EXECUTED.name().equals(nextConnectorInstanceToExecute.getState());\n    }\n\n    public SConnectorDefinition getConnectorDefinition(SConnectorInstance connectorInstance,\n            List<SConnectorDefinition> connectorDefinitions) {\n        for (final SConnectorDefinition sConnectorDefinition : connectorDefinitions) {\n            if (sConnectorDefinition.getName().equals(connectorInstance.getName())) {\n                return sConnectorDefinition;\n            }\n        }\n        throw new IllegalStateException(\"No connector definition found for connector instance \" + connectorInstance);\n    }\n\n    public void executeConnector(SProcessDefinition processDefinition, SFlowNodeInstance flowNodeInstance,\n            List<SConnectorDefinition> connectorsOnEnter,\n            SConnectorInstance connectorInstance) throws SActivityStateExecutionException {\n        executeConnectorInWork(processDefinition.getId(),\n                flowNodeInstance.getParentProcessInstanceId(),\n                flowNodeInstance.getRootProcessInstanceId(),\n                flowNodeInstance.getFlowNodeDefinitionId(),\n                flowNodeInstance.getId(), connectorInstance,\n                getConnectorDefinition(connectorInstance, connectorsOnEnter));\n    }\n\n    public boolean noConnectorHasStartedInCurrentList(List<SConnectorDefinition> connectorDefinitions,\n            SConnectorInstance connectorInstance) throws SBonitaReadException {\n        return /* there is no connector defined, it means that no connector could have been started here */\n        connectorDefinitions.isEmpty() ||\n        /*\n         * if there is some connector defined the only way to have not started the phase is that there is a connector to\n         * execute and it is the first\n         * connector of the list and it was never executed\n         */\n                connectorInstance != null && isFirst(connectorDefinitions, connectorInstance)\n                        && isNotExecutedYet(connectorInstance);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/TransitionEvaluator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SGatewayInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.execution.flowmerger.FlowNodeTransitionsWrapper;\nimport org.bonitasoft.engine.execution.transition.ImplicitGatewayTransitionEvaluator;\nimport org.bonitasoft.engine.execution.transition.InclusiveExclusiveTransitionEvaluator;\nimport org.bonitasoft.engine.execution.transition.ParallelGatewayTransitionEvaluator;\n\npublic class TransitionEvaluator {\n\n    private final ImplicitGatewayTransitionEvaluator implicitGatewayTransitionEvaluator;\n    private final ParallelGatewayTransitionEvaluator parallelGatewayTransitionEvaluator;\n    private final InclusiveExclusiveTransitionEvaluator inclusiveTransitionEvaluator;\n    private final InclusiveExclusiveTransitionEvaluator exclusiveTransitionEvaluator;\n\n    public TransitionEvaluator(ImplicitGatewayTransitionEvaluator implicitGatewayTransitionEvaluator,\n            ParallelGatewayTransitionEvaluator parallelGatewayTransitionEvaluator,\n            InclusiveExclusiveTransitionEvaluator inclusiveTransitionEvaluator,\n            InclusiveExclusiveTransitionEvaluator exclusiveTransitionEvaluator) {\n        this.implicitGatewayTransitionEvaluator = implicitGatewayTransitionEvaluator;\n        this.parallelGatewayTransitionEvaluator = parallelGatewayTransitionEvaluator;\n        this.inclusiveTransitionEvaluator = inclusiveTransitionEvaluator;\n        this.exclusiveTransitionEvaluator = exclusiveTransitionEvaluator;\n    }\n\n    protected List<STransitionDefinition> evaluateOutgoingTransitions(FlowNodeTransitionsWrapper transitions,\n            final SProcessDefinition sDefinition, final SFlowNodeInstance flowNodeInstance) throws SBonitaException {\n        // int nbOfTokenToMerge = 1;// may be > 1 in case of gateway\n        // if is not a normal state don't create new elements\n        if (!SStateCategory.NORMAL.equals(flowNodeInstance.getStateCategory())) {\n            return Collections.emptyList();\n        }\n        final SExpressionContext sExpressionContext = new SExpressionContext(flowNodeInstance.getId(),\n                DataInstanceContainer.ACTIVITY_INSTANCE.name(),\n                sDefinition.getId());\n        sExpressionContext.setProcessDefinition(sDefinition);\n        if (SFlowNodeType.GATEWAY.equals(flowNodeInstance.getType())) {\n            return evaluateOutgoingTransitionsForGateways(transitions, sDefinition, flowNodeInstance,\n                    sExpressionContext);\n        } else if (SFlowNodeType.BOUNDARY_EVENT.equals(flowNodeInstance.getType())) {\n            return new ArrayList<>(transitions.getNonDefaultOutgoingTransitionDefinitions());\n        } else {\n            return evaluateOutgoingTransitionsForActivity(transitions, sDefinition, flowNodeInstance,\n                    sExpressionContext);\n        }\n    }\n\n    List<STransitionDefinition> evaluateOutgoingTransitionsForActivity(final FlowNodeTransitionsWrapper transitions,\n            final SProcessDefinition sDefinition,\n            final SFlowNodeInstance flowNodeInstance, final SExpressionContext sExpressionContext)\n            throws SBonitaException {\n        if (transitions.getNonDefaultOutgoingTransitionDefinitions().isEmpty()) {\n            STransitionDefinition defaultTransition;\n            if ((defaultTransition = getDefaultTransition(sDefinition, flowNodeInstance)) == null) {\n                return Collections.emptyList();\n            }\n            return Collections.singletonList(defaultTransition);\n        }\n        return implicitGatewayTransitionEvaluator.evaluateTransitions(sDefinition, flowNodeInstance, transitions,\n                sExpressionContext);\n    }\n\n    List<STransitionDefinition> evaluateOutgoingTransitionsForGateways(final FlowNodeTransitionsWrapper transitions,\n            final SProcessDefinition sDefinition, final SFlowNodeInstance flowNodeInstance,\n            final SExpressionContext sExpressionContext)\n            throws SBonitaException {\n        List<STransitionDefinition> chosenTransitionDefinitions;\n        final SGatewayInstance gatewayInstance = (SGatewayInstance) flowNodeInstance;\n        switch (gatewayInstance.getGatewayType()) {\n            case EXCLUSIVE:\n                chosenTransitionDefinitions = exclusiveTransitionEvaluator.evaluateTransitions(sDefinition,\n                        flowNodeInstance, transitions, sExpressionContext);\n                break;\n            case INCLUSIVE:\n                chosenTransitionDefinitions = inclusiveTransitionEvaluator.evaluateTransitions(sDefinition,\n                        flowNodeInstance, transitions, sExpressionContext);\n                break;\n            case PARALLEL:\n                chosenTransitionDefinitions = parallelGatewayTransitionEvaluator.evaluateTransitions(transitions);\n                break;\n            default:\n                throw new UnsupportedOperationException(\n                        \"Unsupported gateway type: \" + gatewayInstance.getGatewayType());\n        }\n        return chosenTransitionDefinitions;\n    }\n\n    protected STransitionDefinition getDefaultTransition(final SProcessDefinition sDefinition,\n            final SFlowNodeInstance flowNodeInstance) {\n        final SFlowElementContainerDefinition processContainer = sDefinition.getProcessContainer();\n        final SFlowNodeDefinition flowNode = processContainer.getFlowNode(flowNodeInstance.getFlowNodeDefinitionId());\n        return flowNode.getDefaultTransition();\n    }\n\n    FlowNodeTransitionsWrapper buildTransitionsWrapper(final SFlowNodeDefinition flowNode,\n            final SProcessDefinition sProcessDefinition,\n            final SFlowNodeInstance child) throws SBonitaException {\n        final FlowNodeTransitionsWrapper transitionsDescriptor = new FlowNodeTransitionsWrapper();\n        // Retrieve all outgoing transitions\n        if (flowNode == null) {\n            // not in definition\n            transitionsDescriptor.setInputTransitionsSize(0);\n            transitionsDescriptor.setAllOutgoingTransitionDefinitions(Collections.<STransitionDefinition> emptyList());\n        } else {\n            transitionsDescriptor.setInputTransitionsSize(flowNode.getIncomingTransitions().size());\n            transitionsDescriptor\n                    .setAllOutgoingTransitionDefinitions(new ArrayList<>(flowNode.getOutgoingTransitions()));\n            transitionsDescriptor.setDefaultTransition(flowNode.getDefaultTransition());\n        }\n\n        // Evaluate all outgoing transitions, and retrieve valid outgoing transitions\n        transitionsDescriptor.setValidOutgoingTransitionDefinitions(evaluateOutgoingTransitions(transitionsDescriptor,\n                sProcessDefinition, child));\n        return transitionsDescriptor;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/WaitingEventsInterrupter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SCatchEventDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceDeletionException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventModificationException;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingEventKeyProviderBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SCatchEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SIntermediateCatchEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent;\nimport org.bonitasoft.engine.execution.job.JobNameBuilder;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.scheduler.SchedulerService;\nimport org.bonitasoft.engine.scheduler.exception.SSchedulerException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Slf4j\npublic class WaitingEventsInterrupter {\n\n    private final EventInstanceService eventInstanceService;\n    private final SchedulerService schedulerService;\n\n    private static final int MAX_NUMBER_OF_RESULTS = 100;\n\n    public WaitingEventsInterrupter(final EventInstanceService eventInstanceService,\n            final SchedulerService schedulerService) {\n        this.eventInstanceService = eventInstanceService;\n        this.schedulerService = schedulerService;\n    }\n\n    public void interruptWaitingEvents(final SProcessDefinition processDefinition,\n            final SCatchEventInstance catchEventInstance,\n            final SCatchEventDefinition catchEventDef) throws SBonitaException {\n        interruptTimerEvent(processDefinition, catchEventInstance, catchEventDef);\n        // message, signal and error\n        interruptWaitingEvents(catchEventInstance.getId(), catchEventDef);\n    }\n\n    private void interruptWaitingEvents(final long instanceId, final SCatchEventDefinition catchEventDef)\n            throws SBonitaReadException, SWaitingEventModificationException {\n        if (!catchEventDef.getEventTriggers().isEmpty()) {\n            interruptWaitingEvents(instanceId, SWaitingEvent.class);\n        }\n    }\n\n    public void interruptWaitingEvents(final SFlowNodeInstance flowNodeInstance) throws SBonitaException {\n        if (flowNodeInstance instanceof SReceiveTaskInstance\n                || flowNodeInstance instanceof SIntermediateCatchEventInstance\n                || flowNodeInstance instanceof SBoundaryEventInstance) {\n            interruptWaitingEvents(flowNodeInstance.getId(), SWaitingEvent.class);\n        }\n    }\n\n    private <T extends SWaitingEvent> void interruptWaitingEvents(final long instanceId,\n            final Class<T> waitingEventClass)\n            throws SBonitaReadException, SWaitingEventModificationException {\n        final QueryOptions queryOptions = getWaitingEventsQueryOptions(instanceId, waitingEventClass);\n        final QueryOptions countOptions = getWaitingEventsCountOptions(instanceId, waitingEventClass);\n        long count = 0;\n        List<T> waitingEvents;\n        do {\n            waitingEvents = eventInstanceService.searchWaitingEvents(waitingEventClass, queryOptions);\n            count = eventInstanceService.getNumberOfWaitingEvents(waitingEventClass, countOptions);\n            deleteWaitingEvents(waitingEvents);\n        } while (count > waitingEvents.size());\n    }\n\n    private void deleteWaitingEvents(final List<? extends SWaitingEvent> waitingEvents)\n            throws SWaitingEventModificationException {\n        for (final SWaitingEvent sWaitingEvent : waitingEvents) {\n            eventInstanceService.deleteWaitingEvent(sWaitingEvent);\n        }\n    }\n\n    private QueryOptions getWaitingEventsCountOptions(final long instanceId,\n            final Class<? extends SWaitingEvent> waitingEventClass) {\n        final List<FilterOption> filters = getFilterForWaitingEventsToInterrupt(instanceId, waitingEventClass);\n        return new QueryOptions(filters, null);\n    }\n\n    private QueryOptions getWaitingEventsQueryOptions(final long instanceId,\n            final Class<? extends SWaitingEvent> waitingEventClass) {\n        final OrderByOption orderByOption = new OrderByOption(waitingEventClass,\n                BuilderFactory.get(SWaitingEventKeyProviderBuilderFactory.class).getIdKey(),\n                OrderByType.ASC);\n        final List<FilterOption> filters = getFilterForWaitingEventsToInterrupt(instanceId, waitingEventClass);\n        return new QueryOptions(0, MAX_NUMBER_OF_RESULTS, Collections.singletonList(orderByOption), filters, null);\n    }\n\n    private List<FilterOption> getFilterForWaitingEventsToInterrupt(final long instanceId,\n            final Class<? extends SWaitingEvent> waitingEventClass) {\n        final SWaitingEventKeyProviderBuilderFactory waitingEventKeyProvider = BuilderFactory\n                .get(SWaitingEventKeyProviderBuilderFactory.class);\n        final List<FilterOption> filters = new ArrayList<FilterOption>(2);\n        filters.add(\n                new FilterOption(waitingEventClass, waitingEventKeyProvider.getFlowNodeInstanceIdKey(), instanceId));\n        filters.add(new FilterOption(waitingEventClass, waitingEventKeyProvider.getActiveKey(), true));\n        return filters;\n    }\n\n    private void interruptTimerEvent(final SProcessDefinition processDefinition,\n            final SCatchEventInstance catchEventInstance,\n            final SCatchEventDefinition catchEventDef) throws SSchedulerException, SBonitaReadException {\n        // FIXME to support multiple events change this code\n        if (!catchEventDef.getTimerEventTriggerDefinitions().isEmpty()) {\n            final String jobName = JobNameBuilder.getTimerEventJobName(processDefinition.getId(), catchEventDef,\n                    catchEventInstance);\n            final boolean delete = schedulerService.delete(jobName);\n            try {\n                eventInstanceService.deleteEventTriggerInstanceOfFlowNode(catchEventInstance.getId());\n            } catch (SEventTriggerInstanceDeletionException e) {\n                log.warn(\"Unable to delete event trigger of flow node instance {} because: {}\", catchEventInstance,\n                        e.getMessage());\n            }\n            if (!delete) {\n                log.warn(\"No job found with name '{}'  when interrupting timer catch event named '{}' and id '{}'.\" +\n                        \" It was probably already triggered.\", jobName, catchEventDef.getName(),\n                        catchEventDef.getName());\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/archive/BPMArchiverService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.archive;\n\nimport static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier;\n\nimport java.util.List;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.SArchivingException;\nimport org.bonitasoft.engine.archive.ArchiveInsertRecord;\nimport org.bonitasoft.engine.archive.ArchiveService;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.business.data.DataRetentionBdmTrackingService;\nimport org.bonitasoft.engine.business.data.SDataRetentionBdmTrackingException;\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.classloader.SClassLoaderException;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.connector.ConnectorInstanceService;\nimport org.bonitasoft.engine.core.contract.data.ContractDataService;\nimport org.bonitasoft.engine.core.document.api.DocumentService;\nimport org.bonitasoft.engine.core.document.model.SMappedDocument;\nimport org.bonitasoft.engine.core.process.comment.api.SCommentService;\nimport org.bonitasoft.engine.core.process.comment.model.SComment;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SActivityDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.BPMFailureService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.RefBusinessDataService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceModificationException;\nimport org.bonitasoft.engine.core.process.instance.model.SAbstractConnectorInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SGatewayInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAAutomaticTaskInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SACallActivityInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAGatewayInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SALoopActivityInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAManualTaskInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAMultiInstanceActivityInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAProcessInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAReceiveTaskInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SASendTaskInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SASubProcessActivityInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAUserTaskInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceService;\nimport org.bonitasoft.engine.data.instance.exception.SDataInstanceException;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\nimport org.bonitasoft.engine.dependency.model.ScopeType;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.recorder.SRecorderException;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\n@Slf4j\npublic class BPMArchiverService {\n\n    private final ArchiveService archiveService;\n    private final ProcessInstanceService processInstanceService;\n    private final DocumentService documentService;\n    private final SCommentService commentService;\n    private final ProcessDefinitionService processDefinitionService;\n    private final ConnectorInstanceService connectorInstanceService;\n    private final ClassLoaderService classLoaderService;\n    private final RefBusinessDataService refBusinessDataService;\n    private final ContractDataService contractDataService;\n    private final DataInstanceService dataInstanceService;\n    private final ActivityInstanceService activityInstanceService;\n    private final BPMFailureService bpmFailureService;\n    private final DataRetentionBdmTrackingService dataRetentionBdmTrackingService;\n\n    private static final int BATCH_SIZE = 100;\n\n    public BPMArchiverService(ArchiveService archiveService,\n            ProcessInstanceService processInstanceService,\n            DocumentService documentService,\n            SCommentService commentService,\n            ProcessDefinitionService processDefinitionService,\n            ConnectorInstanceService connectorInstanceService,\n            ClassLoaderService classLoaderService,\n            RefBusinessDataService refBusinessDataService,\n            ContractDataService contractDataService,\n            DataInstanceService dataInstanceService,\n            ActivityInstanceService activityInstanceService,\n            BPMFailureService bpmFailureService,\n            DataRetentionBdmTrackingService dataRetentionBdmTrackingService) {\n        this.archiveService = archiveService;\n        this.processInstanceService = processInstanceService;\n        this.documentService = documentService;\n        this.commentService = commentService;\n        this.processDefinitionService = processDefinitionService;\n        this.connectorInstanceService = connectorInstanceService;\n        this.classLoaderService = classLoaderService;\n        this.refBusinessDataService = refBusinessDataService;\n        this.contractDataService = contractDataService;\n        this.dataInstanceService = dataInstanceService;\n        this.activityInstanceService = activityInstanceService;\n        this.bpmFailureService = bpmFailureService;\n        this.dataRetentionBdmTrackingService = dataRetentionBdmTrackingService;\n    }\n\n    public void archiveAndDeleteProcessInstance(final SProcessInstance processInstance) throws SArchivingException {\n\n        //set the classloader to this process because we need it e.g. to archive data instance\n        ClassLoader processClassLoader;\n        try {\n            processClassLoader = classLoaderService.getClassLoader(\n                    identifier(ScopeType.PROCESS, processInstance.getProcessDefinitionId()));\n\n        } catch (SClassLoaderException e) {\n            throw new SArchivingException(e);\n        }\n        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        Thread.currentThread().setContextClassLoader(processClassLoader);\n        try {\n\n            final SAProcessInstance saProcessInstance = buildArchiveProcessInstance(processInstance);\n\n            SProcessDefinition processDefinition;\n            try {\n                processDefinition = processDefinitionService\n                        .getProcessDefinition(processInstance.getProcessDefinitionId());\n            } catch (final SBonitaException e) {\n                throw new SArchivingException(e);\n            }\n\n            final long archiveDate = saProcessInstance.getEndDate();\n\n            // The archive of data instance is not done because it is done on creation + when updating.\n            // Archive SComment\n            archiveComments(processDefinition, processInstance, archiveDate);\n\n            // archive document mappings\n            archiveDocumentMappings(processDefinition, processInstance, archiveDate);\n\n            archiveConnectorInstancesIfAny(processInstance, processDefinition, archiveDate);\n\n            archiveRefBusinessDataInstances(processInstance.getId());\n\n            //process instance failures\n            archiveProcessInstanceFailures(processInstance, archiveDate);\n\n            // Archive\n            archiveAndDeleteProcessInstanceObject(processDefinition, processInstance, saProcessInstance, archiveDate);\n        } finally {\n            Thread.currentThread().setContextClassLoader(contextClassLoader);\n        }\n\n    }\n\n    protected void archiveConnectorInstancesIfAny(SProcessInstance processInstance,\n            SProcessDefinition processDefinition, long archiveDate) throws SArchivingException {\n        if (!processDefinition.getProcessContainer().getConnectors().isEmpty()) {\n            archiveConnectors(archiveDate, processInstance.getId(), SConnectorInstance.PROCESS_TYPE);\n        }\n    }\n\n    protected SAProcessInstance buildArchiveProcessInstance(SProcessInstance processInstance) {\n        return BuilderFactory.get(SAProcessInstanceBuilderFactory.class).createNewInstance(processInstance).done();\n    }\n\n    private void archiveRefBusinessDataInstances(long processInstanceId) throws SArchivingException {\n        try {\n            List<SRefBusinessDataInstance> refBusinessDataInstances;\n            int i = 0;\n            do {\n                refBusinessDataInstances = refBusinessDataService.getRefBusinessDataInstances(processInstanceId, i,\n                        i + BATCH_SIZE);\n                i += BATCH_SIZE;\n                for (final SRefBusinessDataInstance sRefBusinessDataInstance : refBusinessDataInstances) {\n                    refBusinessDataService.archiveRefBusinessDataInstance(sRefBusinessDataInstance);\n                    upsertTrackingForRefBusinessData(sRefBusinessDataInstance);\n                }\n            } while (refBusinessDataInstances.size() == BATCH_SIZE);\n        } catch (final SBonitaException e) {\n            throw new SArchivingException(\"Unable to archive RefBusinessDataInstance\", e);\n        }\n    }\n\n    /**\n     * Upserts the {@code last_modified_at} timestamp in the {@code data_retention_bdm_tracking}\n     * table for every BDM entity referenced by the given business data instance.\n     * <p>Handles both single-valued references ({@link SSimpleRefBusinessDataInstance})\n     * and multivalued references ({@link SProcessMultiRefBusinessDataInstance}).\n     * References with a {@code null} data ID (unassigned BDM variable) are skipped.\n     */\n    private void upsertTrackingForRefBusinessData(SRefBusinessDataInstance refBusinessDataInstance)\n            throws SDataRetentionBdmTrackingException {\n        String dataClassname = refBusinessDataInstance.getDataClassName();\n        if (refBusinessDataInstance instanceof SSimpleRefBusinessDataInstance simpleRef) {\n            Long dataId = simpleRef.getDataId();\n            if (dataId != null) {\n                dataRetentionBdmTrackingService.upsert(dataId, dataClassname);\n            } else {\n                log.warn(\"Data id is null for SSimpleRefBusinessDataInstance with name {}, \" +\n                        \"cannot update tracking for business data with class name {}\",\n                        simpleRef.getName(), dataClassname);\n            }\n        } else if (refBusinessDataInstance instanceof SProcessMultiRefBusinessDataInstance multiRef) {\n            for (Long dataId : multiRef.getDataIds()) {\n                if (dataId != null) {\n                    dataRetentionBdmTrackingService.upsert(dataId, dataClassname);\n                } else {\n                    log.warn(\"Data id is null for SProcessMultiRefBusinessDataInstance with name {}, \" +\n                            \"cannot update tracking for business data with class name {}\",\n                            multiRef.getName(), dataClassname);\n                }\n            }\n        } else {\n            // Should never happen\n            log.warn(\"Unknown type of SRefBusinessDataInstance {}, \" +\n                    \"cannot update tracking for business data with id {} and class name {}\",\n                    refBusinessDataInstance.getClass().getName(), refBusinessDataInstance.getId(), dataClassname);\n        }\n    }\n\n    private void archiveConnectors(final long archiveDate,\n            final long containerId,\n            final String containerType) throws SArchivingException {\n        try {\n            List<SConnectorInstance> connectorInstances;\n            int i = 0;\n            do {\n                connectorInstances = connectorInstanceService.getConnectorInstances(containerId, containerType, i,\n                        i + BATCH_SIZE, \"id\", OrderByType.ASC);\n                i += BATCH_SIZE;\n                for (final SConnectorInstance sConnectorInstance : connectorInstances) {\n                    connectorInstanceService.archiveConnectorInstance(sConnectorInstance, archiveDate);\n                }\n            } while (connectorInstances.size() == BATCH_SIZE);\n        } catch (final SBonitaException e) {\n            throw new SArchivingException(\"Unable to archive the container instance with id \" + containerId, e);\n        }\n    }\n\n    private void archiveAndDeleteProcessInstanceObject(final SProcessDefinition processDefinition,\n            final SProcessInstance processInstance,\n            final SAProcessInstance saProcessInstance, final long archiveDate)\n            throws SArchivingException {\n        try {\n            final ArchiveInsertRecord insertRecord = new ArchiveInsertRecord(saProcessInstance);\n            archiveService.recordInsert(archiveDate, insertRecord);\n\n            if (log.isDebugEnabled()) {\n                log.debug(\"Archiving process instance with id = <{}> and state {}\", processInstance.getId(),\n                        processInstance.getStateId());\n            }\n            try {\n                processInstanceService.deleteProcessInstance(processInstance.getId());\n            } catch (final SBonitaException e) {\n                throw new SArchivingException(\"Unable to delete the process instance during the archiving.\", e);\n            }\n        } catch (final SRecorderException e) {\n            setExceptionContext(processDefinition, processInstance, e);\n            throw new SArchivingException(\"Unable to archive the process instance.\", e);\n        }\n    }\n\n    private void archiveDocumentMappings(final SProcessDefinition processDefinition,\n            final SProcessInstance processInstance,\n            final long archiveDate) throws SArchivingException {\n        try {\n            List<SMappedDocument> mappedDocuments;\n            int startIndex = 0;\n            do {\n                mappedDocuments = documentService.getDocumentsOfProcessInstance(processInstance.getId(), startIndex,\n                        BATCH_SIZE, null, null);\n                for (final SMappedDocument mappedDocument : mappedDocuments) {\n                    documentService.archive(mappedDocument, archiveDate);\n                }\n                startIndex += BATCH_SIZE;\n            } while (mappedDocuments.size() == BATCH_SIZE);\n        } catch (final SBonitaException e) {\n            setExceptionContext(processDefinition, processInstance, e);\n            throw new SArchivingException(\"Unable to archive the process instance.\", e);\n        }\n    }\n\n    private void archiveComments(final SProcessDefinition processDefinition, final SProcessInstance processInstance,\n            final long archiveDate) throws SArchivingException {\n        try {\n            List<SComment> sComments;\n            int startIndex = 0;\n            do {\n                sComments = commentService\n                        .getComments(processInstance.getId(),\n                                new QueryOptions(startIndex, BATCH_SIZE, SComment.class, \"id\", OrderByType.ASC));\n                for (final SComment sComment : sComments) {\n                    commentService.archive(archiveDate, sComment);\n                }\n                startIndex += BATCH_SIZE;\n            } while (!sComments.isEmpty());\n        } catch (final SBonitaException e) {\n            setExceptionContext(processDefinition, processInstance, e);\n            throw new SArchivingException(\"Unable to archive the process instance comments.\", e);\n        }\n    }\n\n    public void archiveAndDeleteFlowNodeInstance(final SFlowNodeInstance flowNodeInstance,\n            final long processDefinitionId) throws SArchivingException {\n        try {\n            final SProcessDefinition processDefinition = processDefinitionService\n                    .getProcessDefinition(processDefinitionId);\n            // Remove data instance + data visibility mapping\n            archiveAndDeleteFlownodeInstance(flowNodeInstance, processDefinition, System.currentTimeMillis());\n        } catch (final SArchivingException e) {\n            throw e;\n        } catch (final SBonitaException e) {\n            throw new SArchivingException(e);\n        }\n\n    }\n\n    public void archiveFlowNodeInstance(final SFlowNodeInstance flowNodeInstance) throws SArchivingException {\n        archiveFlowNodeInstance(flowNodeInstance, System.currentTimeMillis());\n    }\n\n    private void archiveAndDeleteFlownodeInstance(SFlowNodeInstance flowNodeInstance,\n            SProcessDefinition processDefinition, long archiveDate) throws SDataInstanceException, SArchivingException,\n            SFlowNodeNotFoundException, SFlowNodeReadException, SProcessInstanceModificationException {\n        if (flowNodeInstance instanceof SActivityInstance) {\n            final SActivityDefinition activityDef = (SActivityDefinition) processDefinition\n                    .getProcessContainer().getFlowNode(\n                            flowNodeInstance.getFlowNodeDefinitionId());\n            // only do search for data instances with there are data definitions. Can be null if it's a manual data add at runtime\n            if (activityDef != null && !activityDef.getSDataDefinitions().isEmpty()) {\n                /*\n                 * Delete data instances defined at activity level:\n                 * We do not archive because it's done after update not before update\n                 */\n                deleteLocalDataInstancesFromActivityInstance(flowNodeInstance);\n            }\n\n            if (activityDef != null && !activityDef.getConnectors().isEmpty()) {\n                archiveConnectors(archiveDate, flowNodeInstance.getId(),\n                        SAbstractConnectorInstance.FLOWNODE_TYPE);\n            }\n        }\n        if (flowNodeInstance instanceof SUserTaskInstance) {\n            archiveContractData(archiveDate, flowNodeInstance.getId());\n        }\n        archiveFlowNodeFailures(flowNodeInstance, archiveDate);\n\n        // then archive the flow node instance:\n        archiveFlowNodeInstance(flowNodeInstance, archiveDate);\n\n        // Reconnect the persisted object before deleting it:\n        final SFlowNodeInstance flowNodeInstance2 = activityInstanceService\n                .getFlowNodeInstance(flowNodeInstance.getId());\n        processInstanceService.deleteFlowNodeInstance(flowNodeInstance2, processDefinition);\n    }\n\n    private void archiveProcessInstanceFailures(SProcessInstance processInstance, long archiveDate)\n            throws SArchivingException {\n        try {\n            bpmFailureService.archiveProcessInstanceFailures(processInstance.getId(), archiveDate);\n        } catch (SBonitaException e) {\n            throw new SArchivingException(e);\n        }\n    }\n\n    private void archiveFlowNodeFailures(SFlowNodeInstance flowNodeInstance, long archiveDate)\n            throws SArchivingException {\n        try {\n            bpmFailureService.archiveFlowNodeFailures(flowNodeInstance.getId(), archiveDate);\n        } catch (SBonitaException e) {\n            throw new SArchivingException(e);\n        }\n    }\n\n    private void setExceptionContext(final SProcessDefinition processDefinition, final SProcessInstance processInstance,\n            final SBonitaException e) {\n        e.setProcessInstanceIdOnContext(processInstance.getId());\n        e.setRootProcessInstanceIdOnContext(processInstance.getRootProcessInstanceId());\n        e.setProcessDefinitionIdOnContext(processInstance.getProcessDefinitionId());\n        e.setProcessDefinitionNameOnContext(processDefinition.getName());\n        e.setProcessDefinitionVersionOnContext(processDefinition.getVersion());\n    }\n\n    private void archiveFlowNodeInstance(final SFlowNodeInstance flowNodeInstance,\n            final long archiveDate)\n            throws SArchivingException {\n        try {\n            final SAFlowNodeInstance saFlowNodeInstance = getArchivedObject(flowNodeInstance);\n            if (saFlowNodeInstance != null) {\n                final ArchiveInsertRecord insertRecord = new ArchiveInsertRecord(saFlowNodeInstance);\n                archiveService.recordInsert(archiveDate, insertRecord);\n            }\n        } catch (final SBonitaException e) {\n            throw new SArchivingException(e);\n        }\n    }\n\n    private SAFlowNodeInstance getArchivedObject(final SFlowNodeInstance flowNodeInstance) {\n        SAFlowNodeInstance saFlowNodeInstance = null;\n        switch (flowNodeInstance.getType()) {// TODO archive other flow node\n            case AUTOMATIC_TASK:\n                saFlowNodeInstance = BuilderFactory.get(SAAutomaticTaskInstanceBuilderFactory.class)\n                        .createNewAutomaticTaskInstance((SAutomaticTaskInstance) flowNodeInstance).done();\n                break;\n            case GATEWAY:\n                saFlowNodeInstance = BuilderFactory.get(SAGatewayInstanceBuilderFactory.class)\n                        .createNewGatewayInstance((SGatewayInstance) flowNodeInstance).done();\n                break;\n            case MANUAL_TASK:\n                saFlowNodeInstance = BuilderFactory.get(SAManualTaskInstanceBuilderFactory.class)\n                        .createNewManualTaskInstance((SManualTaskInstance) flowNodeInstance).done();\n                break;\n            case USER_TASK:\n                saFlowNodeInstance = BuilderFactory.get(SAUserTaskInstanceBuilderFactory.class)\n                        .createNewUserTaskInstance((SUserTaskInstance) flowNodeInstance)\n                        .done();\n                break;\n            case RECEIVE_TASK:\n                saFlowNodeInstance = BuilderFactory.get(SAReceiveTaskInstanceBuilderFactory.class)\n                        .createNewReceiveTaskInstance((SReceiveTaskInstance) flowNodeInstance).done();\n                break;\n            case SEND_TASK:\n                saFlowNodeInstance = BuilderFactory.get(SASendTaskInstanceBuilderFactory.class)\n                        .createNewSendTaskInstance((SSendTaskInstance) flowNodeInstance)\n                        .done();\n                break;\n            case LOOP_ACTIVITY:\n                saFlowNodeInstance = BuilderFactory.get(SALoopActivityInstanceBuilderFactory.class)\n                        .createNewLoopActivityInstance((SLoopActivityInstance) flowNodeInstance).done();\n                break;\n            case CALL_ACTIVITY:\n                saFlowNodeInstance = BuilderFactory.get(SACallActivityInstanceBuilderFactory.class)\n                        .createNewArchivedCallActivityInstance((SCallActivityInstance) flowNodeInstance).done();\n                break;\n            case MULTI_INSTANCE_ACTIVITY:\n                saFlowNodeInstance = BuilderFactory.get(SAMultiInstanceActivityInstanceBuilderFactory.class)\n                        .createNewMultiInstanceActivityInstance((SMultiInstanceActivityInstance) flowNodeInstance)\n                        .done();\n                break;\n            case SUB_PROCESS:\n                saFlowNodeInstance = BuilderFactory.get(SASubProcessActivityInstanceBuilderFactory.class)\n                        .createNewArchivedSubProcessActivityInstance((SSubProcessActivityInstance) flowNodeInstance)\n                        .done();\n                break;\n            case END_EVENT:\n            case START_EVENT:\n            case BOUNDARY_EVENT:\n            case INTERMEDIATE_CATCH_EVENT:\n            case INTERMEDIATE_THROW_EVENT:\n            default:\n                break;\n        }\n        return saFlowNodeInstance;\n    }\n\n    private void deleteLocalDataInstancesFromActivityInstance(final SFlowNodeInstance flowNodeInstance)\n            throws SDataInstanceException {\n        List<SDataInstance> dataInstances;\n        do {\n            dataInstances = dataInstanceService.getLocalDataInstances(flowNodeInstance.getId(),\n                    DataInstanceContainer.ACTIVITY_INSTANCE.toString(), 0, 100);\n            for (final SDataInstance sDataInstance : dataInstances) {\n                dataInstanceService.deleteDataInstance(sDataInstance);\n            }\n        } while (dataInstances.size() > 0);\n    }\n\n    private void archiveContractData(final long archiveDate,\n            final long userTaskId)\n            throws SArchivingException {\n        try {\n            contractDataService.archiveAndDeleteUserTaskData(userTaskId, archiveDate);\n        } catch (final SBonitaException e) {\n            throw new SArchivingException(\"Unable to archive contract data of container instance with id \" + userTaskId,\n                    e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/event/CoupleEventHandlerStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.event;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventModificationException;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingEventKeyProviderBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n * @author Matthieu Chaffotte\n */\npublic abstract class CoupleEventHandlerStrategy extends EventHandlerStrategy {\n\n    private final EventInstanceService eventInstanceService;\n\n    private static final int MAX_NUMBER_OF_RESULTS = 100;\n\n    public CoupleEventHandlerStrategy(final EventInstanceService eventInstanceService) {\n        this.eventInstanceService = eventInstanceService;\n    }\n\n    @Override\n    public void unregisterCatchEvent(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition,\n            final SEventTriggerDefinition sEventTriggerDefinition, final long subProcessId,\n            final SProcessInstance parentProcessInstance)\n            throws SBonitaException {\n        if (!eventDefinition.getEventTriggers().isEmpty()) {\n            unregisterWaitingEvents(SWaitingEvent.class, subProcessId, parentProcessInstance);\n        }\n    }\n\n    private QueryOptions getWaitingEventsQueryOptions(final Class<? extends SWaitingEvent> waitingEventClass,\n            final long subProcessId,\n            final SProcessInstance parentProcessInstance) {\n        final OrderByOption orderByOption = new OrderByOption(waitingEventClass,\n                BuilderFactory.get(SWaitingEventKeyProviderBuilderFactory.class).getIdKey(),\n                OrderByType.ASC);\n        final List<FilterOption> filters = getFilterForWaitingEventsToUnregister(waitingEventClass, subProcessId,\n                parentProcessInstance);\n        return new QueryOptions(0, MAX_NUMBER_OF_RESULTS, Collections.singletonList(orderByOption), filters, null);\n    }\n\n    private List<FilterOption> getFilterForWaitingEventsToUnregister(\n            final Class<? extends SWaitingEvent> waitingEventClass, final long subProcessId,\n            final SProcessInstance parentProcessInstance) {\n        final SWaitingEventKeyProviderBuilderFactory waitingEventKeyProvider = BuilderFactory\n                .get(SWaitingEventKeyProviderBuilderFactory.class);\n        final List<FilterOption> filters = new ArrayList<FilterOption>(3);\n        filters.add(new FilterOption(waitingEventClass, waitingEventKeyProvider.getSubProcessIdKey(), subProcessId));\n        filters.add(new FilterOption(waitingEventClass, waitingEventKeyProvider.getParentProcessInstanceIdKey(),\n                parentProcessInstance.getId()));\n        filters.add(new FilterOption(waitingEventClass, waitingEventKeyProvider.getActiveKey(), true));\n        return filters;\n    }\n\n    private <T extends SWaitingEvent> void unregisterWaitingEvents(final Class<T> waitingEventClass,\n            final long subProcessId, final SProcessInstance parentProcessInstance)\n            throws SBonitaReadException, SWaitingEventModificationException {\n        final QueryOptions queryOptions = getWaitingEventsQueryOptions(waitingEventClass, subProcessId,\n                parentProcessInstance);\n        List<T> waitingEvents;\n        do {\n            waitingEvents = eventInstanceService.searchWaitingEvents(waitingEventClass, queryOptions);\n            deleteWaitingEvents(waitingEvents);\n        } while (waitingEvents.size() > 0);\n    }\n\n    private void deleteWaitingEvents(final List<? extends SWaitingEvent> waitingEvents)\n            throws SWaitingEventModificationException {\n        for (final SWaitingEvent sWaitingEvent : waitingEvents) {\n            eventInstanceService.deleteWaitingEvent(sWaitingEvent);\n        }\n    }\n\n    protected EventInstanceService getEventInstanceService() {\n        return eventInstanceService;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/event/ErrorEventHandlerStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.event;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.List;\n\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SActivityDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SCallActivityDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SSubProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SBoundaryEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SEndEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SStartEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SCatchErrorEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SErrorEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventCreationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventReadException;\nimport org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SCallActivityInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SFlowNodeInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SBoundaryEventInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SEventInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SIntermediateCatchEventInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SIntermediateThrowEventInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingErrorEventBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingErrorEventBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SCatchEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SThrowEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SBPMEventType;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingErrorEvent;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent;\nimport org.bonitasoft.engine.execution.ProcessInstanceInterruptor;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n * @author Matthieu Chaffotte\n */\npublic class ErrorEventHandlerStrategy extends CoupleEventHandlerStrategy {\n\n    private static final OperationsWithContext EMPTY = new OperationsWithContext(null, null);\n\n    private final ProcessInstanceService processInstanceService;\n\n    private final FlowNodeInstanceService flowNodeInstanceService;\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    private final EventsHandler eventsHandler;\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(ErrorEventHandlerStrategy.class);\n\n    private ProcessInstanceInterruptor processInstanceInterruptor;\n\n    public ErrorEventHandlerStrategy(final EventInstanceService eventInstanceService,\n            final ProcessInstanceService processInstanceService,\n            final FlowNodeInstanceService flowNodeInstanceService,\n            final ProcessDefinitionService processDefinitionService, final EventsHandler eventsHandler,\n            ProcessInstanceInterruptor processInstanceInterruptor) {\n        super(eventInstanceService);\n        this.processInstanceService = processInstanceService;\n        this.flowNodeInstanceService = flowNodeInstanceService;\n        this.processDefinitionService = processDefinitionService;\n        this.eventsHandler = eventsHandler;\n        this.processInstanceInterruptor = processInstanceInterruptor;\n    }\n\n    @Override\n    public void handleThrowEvent(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition,\n            final SThrowEventInstance eventInstance,\n            final SEventTriggerDefinition sEventTriggerDefinition) throws SBonitaException {\n        LOGGER.debug(\"Error event is thrown, error code = {} process instance = {}\",\n                ((SErrorEventTriggerDefinition) sEventTriggerDefinition).getErrorCode(),\n                eventInstance.getRootContainerId());\n        processInstanceService.setInterruptingEventId(eventInstance.getParentProcessInstanceId(),\n                eventInstance.getId());\n        processInstanceInterruptor.interruptChildrenOfProcessInstance(eventInstance.getParentContainerId(),\n                SStateCategory.ABORTING, eventInstance.getId());\n    }\n\n    @Override\n    public boolean handlePostThrowEvent(final SProcessDefinition processDefinition,\n            final SEndEventDefinition sEventDefinition,\n            final SThrowEventInstance sThrowEventInstance, final SEventTriggerDefinition sEventTriggerDefinition,\n            final SFlowNodeInstance sFlowNodeInstance)\n            throws SBonitaException {\n        boolean hasActionToExecute = false;\n        final SFlowNodeInstanceBuilderFactory flowNodeKeyProvider = BuilderFactory\n                .get(SIntermediateThrowEventInstanceBuilderFactory.class);\n        final long parentProcessInstanceId = sThrowEventInstance\n                .getLogicalGroup(flowNodeKeyProvider.getParentProcessInstanceIndex());\n        final SErrorEventTriggerDefinition errorTrigger = (SErrorEventTriggerDefinition) sEventTriggerDefinition;\n        final SWaitingErrorEvent waitingErrorEvent = getWaitingErrorEvent(processDefinition.getProcessContainer(),\n                parentProcessInstanceId, errorTrigger,\n                sThrowEventInstance, sFlowNodeInstance);\n\n        if (waitingErrorEvent != null) {\n            eventsHandler.triggerCatchEvent(waitingErrorEvent, sThrowEventInstance.getId());\n            hasActionToExecute = true;\n        } else {\n            LOGGER.warn(\n                    \"No catch error event was defined to handle the error code {} defined in the process [name: {}, version: {}], throw event: {}. This throw error event will act as a Terminate Event.\",\n                    errorTrigger.getErrorCode(), processDefinition.getName(), processDefinition.getVersion(),\n                    sEventDefinition == null ? null : sEventDefinition.getName());\n        }\n        return hasActionToExecute;\n    }\n\n    private SWaitingErrorEvent getWaitingErrorEvent(final SFlowElementContainerDefinition container,\n            final long parentProcessInstanceId,\n            final SErrorEventTriggerDefinition errorTrigger, final SThrowEventInstance eventInstance,\n            final SFlowNodeInstance flowNodeInstance)\n            throws SBonitaException {\n        final SProcessInstance processInstance = processInstanceService.getProcessInstance(parentProcessInstanceId);\n        final String errorCode = errorTrigger.getErrorCode();\n        SWaitingErrorEvent waitingErrorEvent;\n\n        // check on direct boundary\n        waitingErrorEvent = getWaitingErrorEventFromBoundary(eventInstance, errorCode, flowNodeInstance);\n        // check on event sub-process\n        if (waitingErrorEvent == null) {\n            waitingErrorEvent = getWaitingErrorEventSubProcess(container, parentProcessInstanceId, errorCode);\n        }\n        // check on call activities (recursive)\n        if (waitingErrorEvent == null && processInstance.getCallerId() != -1\n                && SFlowNodeType.CALL_ACTIVITY.equals(processInstance.getCallerType())) {\n            // check on call activities\n            waitingErrorEvent = getWaitingErrorEventFromCallActivity(errorTrigger, processInstance, eventInstance,\n                    errorCode,\n                    flowNodeInstance);\n        }\n        return waitingErrorEvent;\n    }\n\n    protected SWaitingErrorEvent getWaitingErrorEventFromBoundary(final SThrowEventInstance eventInstance,\n            final String errorCode, final SFlowNodeInstance flowNodeInstance) throws SBonitaException {\n        final SFlowNodeInstanceBuilderFactory flowNodeKeyProvider = BuilderFactory\n                .get(SBoundaryEventInstanceBuilderFactory.class);\n        // get the parent activity of the boundary\n        final long logicalGroup = eventInstance.getLogicalGroup(flowNodeKeyProvider.getParentActivityInstanceIndex());\n        if (logicalGroup <= 0) {\n            // not in an activity = no boundary\n            return null;\n        }\n        final long processDefinitionId = flowNodeInstance\n                .getLogicalGroup(flowNodeKeyProvider.getProcessDefinitionIndex());\n        final SProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(processDefinitionId);\n        final SActivityDefinition flowNode = (SActivityDefinition) processDefinition.getProcessContainer().getFlowNode(\n                flowNodeInstance.getFlowNodeDefinitionId());\n        final List<SBoundaryEventDefinition> boundaryEventDefinitions = flowNode.getBoundaryEventDefinitions();\n        SWaitingErrorEvent waitingErrorEvent;\n        if (flowNode.getLoopCharacteristics() == null) {\n            waitingErrorEvent = getWaitingErrorEventFromBoundary(errorCode, flowNodeInstance, boundaryEventDefinitions);\n        } else {\n            final long multipleInstanceActivityId = flowNodeInstance\n                    .getLogicalGroup(flowNodeKeyProvider.getParentActivityInstanceIndex());\n            final SFlowNodeInstance miActivityInstance = flowNodeInstanceService\n                    .getFlowNodeInstance(multipleInstanceActivityId);\n            waitingErrorEvent = getWaitingErrorEventFromBoundary(errorCode, miActivityInstance,\n                    boundaryEventDefinitions);\n        }\n        return waitingErrorEvent;\n    }\n\n    private SWaitingErrorEvent getWaitingErrorEventFromCallActivity(final SErrorEventTriggerDefinition errorTrigger,\n            final SProcessInstance processInstance, final SThrowEventInstance eventInstance,\n            final String errorCode, final SFlowNodeInstance flowNodeInstance) throws SBonitaException {\n        final SFlowNodeInstanceBuilderFactory flowNodeKeyProvider = BuilderFactory\n                .get(SCallActivityInstanceBuilderFactory.class);\n        final SCallActivityInstance callActivityInstance = (SCallActivityInstance) flowNodeInstanceService\n                .getFlowNodeInstance(processInstance.getCallerId());\n        final long processDefinitionId = callActivityInstance\n                .getLogicalGroup(flowNodeKeyProvider.getProcessDefinitionIndex());\n        final SProcessDefinition callActivityContainer = processDefinitionService\n                .getProcessDefinition(processDefinitionId);\n        final SCallActivityDefinition callActivityDef = (SCallActivityDefinition) callActivityContainer\n                .getProcessContainer().getFlowNode(\n                        callActivityInstance.getFlowNodeDefinitionId());\n        final List<SBoundaryEventDefinition> boundaryEventDefinitions = callActivityDef.getBoundaryEventDefinitions();\n        SWaitingErrorEvent waitingErrorEvent;\n        if (callActivityDef.getLoopCharacteristics() != null) {\n            final long multipleInstanceActivityId = callActivityInstance\n                    .getLogicalGroup(flowNodeKeyProvider.getParentActivityInstanceIndex());\n            final SFlowNodeInstance miActivityInstance = flowNodeInstanceService\n                    .getFlowNodeInstance(multipleInstanceActivityId);\n            waitingErrorEvent = getWaitingErrorEventFromBoundary(errorCode, miActivityInstance,\n                    boundaryEventDefinitions);\n        } else {\n            waitingErrorEvent = getWaitingErrorEventFromBoundary(errorCode, callActivityInstance,\n                    boundaryEventDefinitions);\n        }\n        if (waitingErrorEvent == null) {\n            final long callActivityParentProcInstId = callActivityInstance\n                    .getLogicalGroup(flowNodeKeyProvider.getParentProcessInstanceIndex());\n            waitingErrorEvent = getWaitingErrorEvent(callActivityContainer.getProcessContainer(),\n                    callActivityParentProcInstId, errorTrigger, eventInstance,\n                    flowNodeInstance);\n        }\n        return waitingErrorEvent;\n    }\n\n    protected SWaitingErrorEvent getWaitingErrorEventFromBoundary(final String errorCode,\n            final SFlowNodeInstance flowNodeInstance,\n            final List<SBoundaryEventDefinition> boundaryEventDefinitions) throws SWaitingEventReadException {\n        boolean canHandleError;\n        String catchingErrorCode = errorCode;\n        canHandleError = containsHandler(boundaryEventDefinitions, catchingErrorCode);\n        if (!canHandleError) {\n            catchingErrorCode = null; // catch all errors\n            canHandleError = containsHandler(boundaryEventDefinitions, catchingErrorCode); // check for a handler that is able to catch all error codes\n        }\n        if (canHandleError) {\n            return getEventInstanceService().getBoundaryWaitingErrorEvent(flowNodeInstance.getId(), catchingErrorCode);\n        }\n        return null;\n    }\n\n    private SWaitingErrorEvent getWaitingErrorEventSubProcess(final SFlowElementContainerDefinition container,\n            final long parentProcessInstanceId,\n            final String errorCode) throws SBonitaReadException, SBPMEventHandlerException {\n        String catchingErrorCode = errorCode;\n        boolean canHandleError = hasEventSubProcessCatchingError(container, catchingErrorCode);\n        if (!canHandleError) {\n            // if there is no event sub-process catching that particular error,  we search for an event sub process that catch all kind of error\n            catchingErrorCode = null;\n            canHandleError = hasEventSubProcessCatchingError(container, catchingErrorCode);\n        }\n        SWaitingErrorEvent waitingErrorEvent = null;\n        if (canHandleError) {\n            final SWaitingErrorEventBuilderFactory waitingErrorEventKeyProvider = BuilderFactory\n                    .get(SWaitingErrorEventBuilderFactory.class);\n            final OrderByOption orderByOption = new OrderByOption(SWaitingEvent.class,\n                    waitingErrorEventKeyProvider.getFlowNodeNameKey(), OrderByType.ASC);\n\n            final List<FilterOption> filters = new ArrayList<>(3);\n            filters.add(new FilterOption(SWaitingErrorEvent.class, waitingErrorEventKeyProvider.getErrorCodeKey(),\n                    catchingErrorCode));\n            filters.add(new FilterOption(SWaitingErrorEvent.class, waitingErrorEventKeyProvider.getEventTypeKey(),\n                    SBPMEventType.EVENT_SUB_PROCESS.name()));\n            filters.add(new FilterOption(SWaitingErrorEvent.class,\n                    waitingErrorEventKeyProvider.getParentProcessInstanceIdKey(), parentProcessInstanceId));\n            final QueryOptions queryOptions = new QueryOptions(0, 2, Collections.singletonList(orderByOption), filters,\n                    null);\n            final List<SWaitingErrorEvent> waitingEvents = getEventInstanceService()\n                    .searchWaitingEvents(SWaitingErrorEvent.class, queryOptions);\n            if (waitingEvents.size() != 1) {\n                final StringBuilder stb = new StringBuilder();\n                stb.append(\"One and only one error start event sub-process was expected for the process instance \");\n                stb.append(parentProcessInstanceId);\n                stb.append(\" and error code \");\n                stb.append(catchingErrorCode);\n                stb.append(\", but \");\n                stb.append(waitingEvents.size());\n                stb.append(\" was found.\");\n                throw new SBPMEventHandlerException(stb.toString());\n            }\n            waitingErrorEvent = waitingEvents.get(0);\n        }\n        return waitingErrorEvent;\n    }\n\n    private boolean containsHandler(final List<SBoundaryEventDefinition> boundaryEventDefinitions,\n            final String errorCode) {\n        boolean found = false;\n        final Iterator<SBoundaryEventDefinition> iterator = boundaryEventDefinitions.iterator();\n        while (iterator.hasNext() && !found) {\n            final SBoundaryEventDefinition boundaryEventDefinition = iterator.next();\n            final SCatchErrorEventTriggerDefinition currentErrorTrigger = boundaryEventDefinition\n                    .getErrorEventTriggerDefinition(errorCode);\n            if (currentErrorTrigger != null) {\n                found = true;\n            }\n        }\n        return found;\n    }\n\n    private boolean hasEventSubProcessCatchingError(final SFlowElementContainerDefinition container,\n            final String errorCode) {\n        boolean found = false;\n        final Iterator<SActivityDefinition> iterator = container.getActivities().iterator();\n        while (iterator.hasNext() && !found) {\n            final SActivityDefinition activity = iterator.next();\n            if (SFlowNodeType.SUB_PROCESS.equals(activity.getType())\n                    && ((SSubProcessDefinition) activity).isTriggeredByEvent()) {\n                final SSubProcessDefinition eventSubProcess = (SSubProcessDefinition) activity;\n                final SStartEventDefinition startEventDefinition = eventSubProcess.getSubProcessContainer()\n                        .getStartEvents().get(0);\n                if (startEventDefinition.getErrorEventTriggerDefinition(errorCode) != null) {\n                    found = true;\n                }\n            }\n\n        }\n        return found;\n    }\n\n    @Override\n    public void handleCatchEvent(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition,\n            final SCatchEventInstance eventInstance,\n            final SEventTriggerDefinition sEventTriggerDefinition) throws SBonitaException {\n        final SWaitingErrorEventBuilderFactory builderFact = BuilderFactory.get(SWaitingErrorEventBuilderFactory.class);\n        final SErrorEventTriggerDefinition errorEventTriggerDefinition = (SErrorEventTriggerDefinition) sEventTriggerDefinition;\n        final SEventInstanceBuilderFactory eventInstanceKeyProvider = BuilderFactory\n                .get(SIntermediateCatchEventInstanceBuilderFactory.class);\n        switch (eventDefinition.getType()) {\n            case BOUNDARY_EVENT:\n                final SBoundaryEventInstance boundary = (SBoundaryEventInstance) eventInstance;\n                final long rootProcessInstanceId = eventInstance\n                        .getLogicalGroup(eventInstanceKeyProvider.getRootProcessInstanceIndex());\n                final long parentProcessInstanceId = eventInstance\n                        .getLogicalGroup(eventInstanceKeyProvider.getParentProcessInstanceIndex());\n                final SWaitingErrorEventBuilder builder = builderFact.createNewWaitingErrorBoundaryEventInstance(\n                        processDefinition.getId(),\n                        rootProcessInstanceId, parentProcessInstanceId, eventInstance.getId(),\n                        errorEventTriggerDefinition.getErrorCode(),\n                        processDefinition.getName(), eventInstance.getFlowNodeDefinitionId(), eventInstance.getName(),\n                        boundary.getActivityInstanceId());\n                final SWaitingErrorEvent errorEvent = builder.done();\n                getEventInstanceService().createWaitingEvent(errorEvent);\n                break;\n            case INTERMEDIATE_CATCH_EVENT:\n            case START_EVENT:\n                throw new SWaitingEventCreationException(\n                        \"Catch error event cannot be put in \" + eventDefinition.getType()\n                                + \". They must be used as boundary events or start event subprocess.\");\n            default:\n                throw new SWaitingEventCreationException(eventDefinition.getType() + \" is not a catch event.\");\n        }\n    }\n\n    @Override\n    public OperationsWithContext getOperations(final SWaitingEvent waitingEvent, final Long triggeringElementID) {\n        return EMPTY;\n    }\n\n    @Override\n    public void handleEventSubProcess(final SProcessDefinition processDefinition,\n            final SEventDefinition eventDefinition,\n            final SEventTriggerDefinition sEventTriggerDefinition, final long subProcessId,\n            final SProcessInstance parentProcessInstance)\n            throws SBonitaException {\n        final SWaitingErrorEventBuilderFactory builderFact = BuilderFactory.get(SWaitingErrorEventBuilderFactory.class);\n        final SErrorEventTriggerDefinition trigger = (SErrorEventTriggerDefinition) sEventTriggerDefinition;\n        final SWaitingErrorEventBuilder builder = builderFact.createNewWaitingErrorEventSubProcInstance(\n                processDefinition.getId(),\n                parentProcessInstance.getId(), parentProcessInstance.getRootProcessInstanceId(), trigger.getErrorCode(),\n                processDefinition.getName(),\n                eventDefinition.getId(), eventDefinition.getName(), subProcessId);\n\n        final SWaitingErrorEvent event = builder.done();\n        getEventInstanceService().createWaitingEvent(event);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/event/EventHandlerStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.event;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SEndEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowElementsContainerType;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SCatchEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SThrowEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\n\n/**\n * Strategy to handle one kind of event: TIMER, ERROR, SIGNAL or MESSAGE\n *\n * @author Baptiste Mesta\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic abstract class EventHandlerStrategy {\n\n    public EventHandlerStrategy() {\n        super();\n    }\n\n    public abstract void handleThrowEvent(SProcessDefinition processDefinition, SEventDefinition eventDefinition,\n            SThrowEventInstance eventInstance,\n            SEventTriggerDefinition sEventTriggerDefinition) throws SBonitaException;\n\n    public abstract void handleCatchEvent(SProcessDefinition processDefinition, SEventDefinition eventDefinition,\n            SCatchEventInstance eventInstance,\n            SEventTriggerDefinition sEventTriggerDefinition) throws SBonitaException;\n\n    public abstract void handleEventSubProcess(SProcessDefinition processDefinition, SEventDefinition eventDefinition,\n            SEventTriggerDefinition sEventTriggerDefinition, long subProcessId, SProcessInstance parentProcessInstance)\n            throws SBonitaException;\n\n    public abstract void unregisterCatchEvent(SProcessDefinition processDefinition, SEventDefinition eventDefinition,\n            SEventTriggerDefinition sEventTriggerDefinition, long subProcessId, SProcessInstance parentProcessIsnstance)\n            throws SBonitaException;\n\n    public abstract OperationsWithContext getOperations(SWaitingEvent waitingEvent, Long triggeringElementID)\n            throws SBonitaException;\n\n    public void handleThrowEvent(final SEventTriggerDefinition sEventTriggerDefinition) throws SBonitaException {\n    }\n\n    public boolean handlePostThrowEvent(final SProcessDefinition processDefinition,\n            final SEndEventDefinition sEventDefinition,\n            final SThrowEventInstance sThrowEventInstance,\n            final SEventTriggerDefinition sEventTriggerDefinition, final SFlowNodeInstance sFlowNodeInstance)\n            throws SBonitaException {\n        return false;\n    }\n\n    protected DataInstanceContainer getParentContainerType(final SFlowNodeInstance flowNodeInstance) {\n        DataInstanceContainer parentContainerType;\n        if (SFlowElementsContainerType.PROCESS.equals(flowNodeInstance.getParentContainerType())) {\n            parentContainerType = DataInstanceContainer.PROCESS_INSTANCE;\n        } else {\n            parentContainerType = DataInstanceContainer.ACTIVITY_INSTANCE;\n        }\n        return parentContainerType;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/event/EventsHandler.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.event;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Supplier;\n\nimport org.bonitasoft.engine.bpm.model.impl.BPMInstancesCreator;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContent;\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.operation.OperationService;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SActivityDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SReceiveTaskDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SSendTaskDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SSubProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SEndEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SStartEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerType;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowMessageEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SContractViolationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceCreationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageInstanceCreationException;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowElementsContainerType;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.core.process.instance.model.event.SCatchEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SThrowEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SBPMEventType;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.data.instance.exception.SDataInstanceException;\nimport org.bonitasoft.engine.execution.ContainerRegistry;\nimport org.bonitasoft.engine.execution.ProcessExecutor;\nimport org.bonitasoft.engine.execution.ProcessInstanceInterruptor;\nimport org.bonitasoft.engine.execution.work.BPMWorkFactory;\nimport org.bonitasoft.engine.expression.exception.SExpressionException;\nimport org.bonitasoft.engine.mdc.MDCHelper;\nimport org.bonitasoft.engine.mdc.MDCHelper.CheckedRunnable2;\nimport org.bonitasoft.engine.mdc.ProcessInstanceMDC;\nimport org.bonitasoft.engine.message.MessagesHandlingService;\nimport org.bonitasoft.engine.scheduler.SchedulerService;\nimport org.bonitasoft.engine.transaction.STransactionNotFoundException;\nimport org.bonitasoft.engine.work.WorkService;\n\n/**\n * Handle event depending on its type\n * TODO\n * * Move all code that instantiate process/execute flow node after a event was triggered here + make it call\n * reachedCatchEvent\n * * For event sub process: the instantiate process must cancel all activities then instantiate the process\n * * the instantiate event subprocess method is like the start of the process executor but with less things: factorise\n * it\n * * check that there is no execution issues with event sub process (add more test)\n * * add test for each kind of start event in event sub process\n * * try to trigger event subprocess with multiple events\n *\n * @author Baptiste Mesta\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class EventsHandler {\n\n    private final Map<SEventTriggerType, EventHandlerStrategy> handlers;\n\n    private final ContainerRegistry containerRegistry;\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    private final EventInstanceService eventInstanceService;\n\n    private final BPMInstancesCreator bpmInstancesCreator;\n\n    private final ProcessInstanceService processInstanceService;\n\n    private final OperationService operationService;\n\n    private ProcessExecutor processExecutor;\n    private ProcessInstanceInterruptor processInstanceInterruptor;\n\n    private FlowNodeInstanceService flowNodeInstanceService;\n\n    public EventsHandler(final SchedulerService schedulerService,\n            final ExpressionResolverService expressionResolverService,\n            final EventInstanceService eventInstanceService, final BPMInstancesCreator bpmInstancesCreator,\n            final ProcessDefinitionService processDefinitionService, final ContainerRegistry containerRegistry,\n            final ProcessInstanceService processInstanceService, final FlowNodeInstanceService flowNodeInstanceService,\n            OperationService operationService,\n            MessagesHandlingService messagesHandlingService, WorkService workService, BPMWorkFactory workFactory,\n            ProcessInstanceInterruptor processInstanceInterruptor) {\n        this.eventInstanceService = eventInstanceService;\n        this.processDefinitionService = processDefinitionService;\n        this.containerRegistry = containerRegistry;\n        this.bpmInstancesCreator = bpmInstancesCreator;\n        this.processInstanceService = processInstanceService;\n        this.operationService = operationService;\n        this.flowNodeInstanceService = flowNodeInstanceService;\n        this.processInstanceInterruptor = processInstanceInterruptor;\n        handlers = new HashMap<>(4);\n        handlers.put(SEventTriggerType.TIMER, new TimerEventHandlerStrategy(expressionResolverService, schedulerService,\n                eventInstanceService));\n        handlers.put(SEventTriggerType.MESSAGE,\n                new MessageEventHandlerStrategy(expressionResolverService, eventInstanceService,\n                        bpmInstancesCreator, processDefinitionService, messagesHandlingService));\n        handlers.put(SEventTriggerType.SIGNAL,\n                new SignalEventHandlerStrategy(eventInstanceService, workService, workFactory));\n        handlers.put(SEventTriggerType.TERMINATE, new TerminateEventHandlerStrategy(processInstanceInterruptor));\n        handlers.put(SEventTriggerType.ERROR,\n                new ErrorEventHandlerStrategy(eventInstanceService, processInstanceService, flowNodeInstanceService,\n                        processDefinitionService, this, processInstanceInterruptor));\n    }\n\n    public void setProcessExecutor(final ProcessExecutor processExecutor) {\n        this.processExecutor = processExecutor;\n\n    }\n\n    /**\n     * called when a catchEvent is reached\n     * e.g. we are going on a catch event in the flow of a process\n     * This is different of trigger catch event:\n     * e.g. for a message handleCatchEvent will create the waiting event and triggerCatchEvent is called when the\n     * message is received\n     *\n     * @param processDefinition\n     * @param eventDefinition\n     * @param eventInstance\n     * @throws SBonitaException\n     */\n    public void handleCatchEvent(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition,\n            final SEventInstance eventInstance)\n            throws SBonitaException {\n        final List<SEventTriggerDefinition> eventTriggers = eventDefinition.getEventTriggers();\n        for (final SEventTriggerDefinition sEventTriggerDefinition : eventTriggers) {\n            final EventHandlerStrategy eventHandlerStrategy = handlers\n                    .get(sEventTriggerDefinition.getEventTriggerType());\n            eventHandlerStrategy.handleCatchEvent(processDefinition, eventDefinition,\n                    (SCatchEventInstance) eventInstance,\n                    sEventTriggerDefinition);\n        }\n    }\n\n    public void handleCatchMessage(final SProcessDefinition processDefinition,\n            final SReceiveTaskDefinition receiveTaskDefinition,\n            final SReceiveTaskInstance receiveTaskInstance) throws SBonitaException {\n        final SEventTriggerDefinition eventTrigger = receiveTaskDefinition.getTrigger();\n        final MessageEventHandlerStrategy messageEventHandlerStrategy = (MessageEventHandlerStrategy) handlers\n                .get(SEventTriggerType.MESSAGE);\n        messageEventHandlerStrategy.handleCatchEvent(processDefinition, receiveTaskInstance, eventTrigger);\n    }\n\n    /**\n     * called when a star subprocess is reached\n     * e.g. we are going on a catch event in the flow of a process\n     * This is different of trigger catch event:\n     * e.g. for a message handleCatchEvent will create the waiting event and triggerCatchEvent is called when the\n     * message is received\n     *\n     * @param processDefinition\n     * @param eventDefinition\n     * @param parentProcessInstance\n     * @throws SBonitaException\n     */\n    private void handleEventSubProcess(final SProcessDefinition processDefinition,\n            final SEventDefinition eventDefinition,\n            final long subProcessId, final SProcessInstance parentProcessInstance) throws SBonitaException {\n        final List<SEventTriggerDefinition> eventTriggers = eventDefinition.getEventTriggers();\n        for (final SEventTriggerDefinition sEventTriggerDefinition : eventTriggers) {\n            final EventHandlerStrategy eventHandlerStrategy = handlers\n                    .get(sEventTriggerDefinition.getEventTriggerType());\n            eventHandlerStrategy.handleEventSubProcess(processDefinition, eventDefinition, sEventTriggerDefinition,\n                    subProcessId,\n                    parentProcessInstance);\n        }\n    }\n\n    public void handleEventSubProcess(final SProcessDefinition sDefinition,\n            final SProcessInstance parentProcessInstance) throws SBonitaException {\n        final Set<SActivityDefinition> activities = sDefinition.getProcessContainer().getActivities();\n        for (final SActivityDefinition activity : activities) {\n            if (SFlowNodeType.SUB_PROCESS.equals(activity.getType())\n                    && ((SSubProcessDefinition) activity).isTriggeredByEvent()) {\n                final SSubProcessDefinition eventSubProcess = (SSubProcessDefinition) activity;\n                final SStartEventDefinition sStartEventDefinition = eventSubProcess.getSubProcessContainer()\n                        .getStartEvents().get(0);\n                handleEventSubProcess(sDefinition, sStartEventDefinition, activity.getId(), parentProcessInstance);\n            }\n        }\n    }\n\n    private void unregisterEventSubProcess(final SProcessDefinition processDefinition,\n            final SEventDefinition eventDefinition, final long subProcessId,\n            final SProcessInstance parentProcessInstance) throws SBonitaException {\n        final List<SEventTriggerDefinition> eventTriggers = eventDefinition.getEventTriggers();\n        for (final SEventTriggerDefinition sEventTriggerDefinition : eventTriggers) {\n            final EventHandlerStrategy eventHandlerStrategy = handlers\n                    .get(sEventTriggerDefinition.getEventTriggerType());\n            eventHandlerStrategy.unregisterCatchEvent(processDefinition, eventDefinition, sEventTriggerDefinition,\n                    subProcessId, parentProcessInstance);\n        }\n    }\n\n    public void unregisterEventSubProcess(final SProcessDefinition sDefinition,\n            final SProcessInstance parentProcessInstance) throws SBonitaException {\n        final Set<SActivityDefinition> activities = sDefinition.getProcessContainer().getActivities();\n        for (final SActivityDefinition activity : activities) {\n            if (SFlowNodeType.SUB_PROCESS.equals(activity.getType())\n                    && ((SSubProcessDefinition) activity).isTriggeredByEvent()) {\n                final SSubProcessDefinition eventSubProcess = (SSubProcessDefinition) activity;\n                final SStartEventDefinition sStartEventDefinition = eventSubProcess.getSubProcessContainer()\n                        .getStartEvents().get(0);\n                unregisterEventSubProcess(sDefinition, sStartEventDefinition, activity.getId(), parentProcessInstance);\n            }\n        }\n    }\n\n    /**\n     * called when we reach a throw event in the flow of a process\n     *\n     * @param processDefinition\n     * @param eventDefinition\n     * @param eventInstance\n     * @throws SBonitaException\n     */\n    public void handleThrowEvent(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition,\n            final SEventInstance eventInstance)\n            throws SBonitaException {\n        final List<SEventTriggerDefinition> eventTriggers = eventDefinition.getEventTriggers();\n        for (final SEventTriggerDefinition sEventTriggerDefinition : eventTriggers) {\n            final EventHandlerStrategy eventHandlerStrategy = handlers\n                    .get(sEventTriggerDefinition.getEventTriggerType());\n            if (eventHandlerStrategy != null) {\n                eventHandlerStrategy.handleThrowEvent(processDefinition, eventDefinition,\n                        (SThrowEventInstance) eventInstance, sEventTriggerDefinition);\n            }\n        }\n    }\n\n    public void handleThrowMessage(final SProcessDefinition processDefinition,\n            final SSendTaskDefinition sendTaskDefinition,\n            final SSendTaskInstance sendTaskInstance)\n            throws SMessageInstanceCreationException, SDataInstanceException,\n            SExpressionException, STransactionNotFoundException {\n        final SThrowMessageEventTriggerDefinition eventTrigger = sendTaskDefinition.getMessageTrigger();\n        final MessageEventHandlerStrategy messageEventHandlerStrategy = (MessageEventHandlerStrategy) handlers\n                .get(SEventTriggerType.MESSAGE);\n        messageEventHandlerStrategy.handleThrowEvent(processDefinition, sendTaskInstance, eventTrigger);\n    }\n\n    public boolean handlePostThrowEvent(final SProcessDefinition sProcessDefinition,\n            final SEndEventDefinition sEndEventDefinition, final SThrowEventInstance sThrowEventInstance,\n            final SFlowNodeInstance sFlowNodeInstance) throws SBonitaException {\n        boolean hasActionsToExecute = false;\n        final List<SEventTriggerDefinition> eventTriggers = sEndEventDefinition.getEventTriggers();\n        for (final SEventTriggerDefinition sEventTriggerDefinition : eventTriggers) {\n            final EventHandlerStrategy eventHandlerStrategy = handlers\n                    .get(sEventTriggerDefinition.getEventTriggerType());\n            if (eventHandlerStrategy != null) {\n                hasActionsToExecute = hasActionsToExecute\n                        || eventHandlerStrategy.handlePostThrowEvent(sProcessDefinition, sEndEventDefinition,\n                                sThrowEventInstance, sEventTriggerDefinition, sFlowNodeInstance);\n            }\n        }\n        return hasActionsToExecute;\n    }\n\n    /**\n     * called when a BPM event is triggered by the API\n     *\n     * @param sEventTriggerDefinition\n     * @throws SBonitaException\n     */\n    public void handleThrowEvent(final SEventTriggerDefinition sEventTriggerDefinition) throws SBonitaException {\n        final EventHandlerStrategy eventHandlerStrategy = handlers.get(sEventTriggerDefinition.getEventTriggerType());\n        if (eventHandlerStrategy != null) {\n            eventHandlerStrategy.handleThrowEvent(sEventTriggerDefinition);\n        }\n    }\n\n    /**\n     * When a trigger is 'launched' the catch event is reached and is waken up/created using its waiting event\n     * Depending on the type it will execute the catch event of instantiate the process/subprocess\n     *\n     * @param waitingEvent\n     * @param triggeringElementID\n     * @throws SBonitaException\n     */\n    public void triggerCatchEvent(final SWaitingEvent waitingEvent, final Long triggeringElementID)\n            throws SBonitaException {\n        final SBPMEventType eventType = waitingEvent.getEventType();\n        final long processDefinitionId = waitingEvent.getProcessDefinitionId();\n        final long targetSFlowNodeDefinitionId = waitingEvent.getFlowNodeDefinitionId();\n        final long flowNodeInstanceId = waitingEvent.getFlowNodeInstanceId();\n        final OperationsWithContext operations = handlers.get(waitingEvent.getEventTriggerType())\n                .getOperations(waitingEvent, triggeringElementID);\n        if (SBPMEventType.EVENT_SUB_PROCESS.equals(waitingEvent.getEventType())) {\n            final SProcessDefinition processDefinition = processDefinitionService\n                    .getProcessDefinition(processDefinitionId);\n            final SStartEventDefinition startEvent = (SStartEventDefinition) processDefinition.getProcessContainer()\n                    .getFlowNode(\n                            waitingEvent.getFlowNodeDefinitionId());\n            triggerCatchStartEventSubProcess(waitingEvent.getEventTriggerType(), processDefinitionId,\n                    targetSFlowNodeDefinitionId, operations,\n                    waitingEvent.getSubProcessId(), waitingEvent.getParentProcessInstanceId(),\n                    waitingEvent.getRootProcessInstanceId(),\n                    startEvent.isInterrupting());\n        } else {\n            triggerCatchEvent(eventType, processDefinitionId, targetSFlowNodeDefinitionId, waitingEvent,\n                    flowNodeInstanceId, operations);\n        }\n    }\n\n    private void triggerInTransaction(final SBPMEventType eventType, final Long processDefinitionId,\n            final Long targetSFlowNodeDefinitionId,\n            final SWaitingEvent waitingEvent, final Long flowNodeInstanceId, final OperationsWithContext operations)\n            throws SBonitaException {\n        final TransactionContent transactionContent = new TransactionContent() {\n\n            @Override\n            public void execute() throws SBonitaException {\n                triggerCatchEvent(eventType, processDefinitionId, targetSFlowNodeDefinitionId, waitingEvent,\n                        flowNodeInstanceId, operations);\n            }\n        };\n        transactionContent.execute();\n    }\n\n    private void triggerInTransaction(final SEventTriggerType eventTriggerType, final Long processDefinitionId,\n            final Long targetSFlowNodeDefinitionId,\n            final OperationsWithContext operations, final long subProcessId, final Long parentProcessInstanceId,\n            final Long rootProcessInstanceId,\n            final Boolean isInterrupting) throws SBonitaException {\n        final TransactionContent transactionContent = new TransactionContent() {\n\n            @Override\n            public void execute() throws SBonitaException {\n                triggerCatchStartEventSubProcess(eventTriggerType, processDefinitionId, targetSFlowNodeDefinitionId,\n                        operations, subProcessId,\n                        parentProcessInstanceId, rootProcessInstanceId, isInterrupting);\n            }\n        };\n        transactionContent.execute();\n    }\n\n    private void triggerCatchEvent(final SBPMEventType eventType, final Long processDefinitionId,\n            final Long targetSFlowNodeDefinitionId,\n            final SWaitingEvent waitingEvent, final Long flowNodeInstanceId, final OperationsWithContext operations)\n            throws SBonitaException {\n        switch (eventType) {\n            case START_EVENT:\n                instantiateProcess(processDefinitionId, targetSFlowNodeDefinitionId, operations);\n                break;\n            default:\n                if (waitingEvent != null) { // is null if it's a timer\n                    eventInstanceService.deleteWaitingEvent(waitingEvent);\n                    executeFlowNode(flowNodeInstanceId, operations);\n                } else {\n                    executeFlowNode(flowNodeInstanceId, operations);\n                }\n                break;\n        }\n    }\n\n    private void triggerCatchStartEventSubProcess(final SEventTriggerType triggerType, final Long processDefinitionId,\n            final Long targetSFlowNodeDefinitionId,\n            final OperationsWithContext operations, final long subProcessId, final long parentProcessInstanceId,\n            final Long rootProcessInstanceId,\n            final Boolean isInterrupting) throws SBonitaException {\n        final SProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(processDefinitionId);\n        final SFlowNodeDefinition sFlowNodeDefinition = processDefinition.getProcessContainer()\n                .getFlowNode(subProcessId);\n        final SFlowNodeInstance subProcflowNodeInstance = bpmInstancesCreator.createFlowNodeInstance(\n                processDefinitionId, rootProcessInstanceId,\n                parentProcessInstanceId, SFlowElementsContainerType.PROCESS, sFlowNodeDefinition, rootProcessInstanceId,\n                parentProcessInstanceId, false, 0,\n                SStateCategory.NORMAL, -1);\n        final SProcessInstance parentProcessInstance = processInstanceService\n                .getProcessInstance(parentProcessInstanceId);\n        if (triggerType.equals(SEventTriggerType.ERROR) || isInterrupting) {\n            processInstanceInterruptor.interruptProcessInstance(parentProcessInstanceId, SStateCategory.ABORTING,\n                    subProcflowNodeInstance.getId());\n        }\n\n        Supplier<ProcessInstanceMDC> processInstanceMDC = () -> new ProcessInstanceMDC(0, Optional.empty(),\n                Optional.empty(), processDefinitionId, rootProcessInstanceId);\n        MDCHelper.tryWithMDC(processInstanceMDC, () -> {\n\n            processExecutor.start(processDefinitionId, targetSFlowNodeDefinitionId, 0, 0, operations.getContext(),\n                    operations.getOperations(),\n                    subProcflowNodeInstance.getId(), subProcessId, null); // Process contract inputs on EventSubProcess are not supported.\n\n        });\n\n        unregisterEventSubProcess(processDefinition, parentProcessInstance);\n    }\n\n    public void triggerCatchEvent(final String eventType, final Long processDefinitionId,\n            final Long targetSFlowNodeDefinitionId,\n            final Long flowNodeInstanceId, final String containerType) throws SBonitaException {\n        final SBPMEventType type = SBPMEventType.valueOf(eventType);\n        triggerInTransaction(type, processDefinitionId, targetSFlowNodeDefinitionId, null, flowNodeInstanceId,\n                new OperationsWithContext(null, null,\n                        containerType));\n    }\n\n    public void triggerCatchEvent(final SEventTriggerType eventTriggerType, final Long processDefinitionId,\n            final Long targetSFlowNodeDefinitionId,\n            final String containerType, final long subProcessId, final Long parentProcessInstanceId,\n            final Long rootProcessInstanceId,\n            final Boolean isInterrupting) throws SBonitaException {\n        triggerInTransaction(eventTriggerType, processDefinitionId, targetSFlowNodeDefinitionId,\n                new OperationsWithContext(null, null, containerType),\n                subProcessId, parentProcessInstanceId, rootProcessInstanceId, isInterrupting);\n    }\n\n    private void executeFlowNode(final long flowNodeInstanceId, final OperationsWithContext operations)\n            throws SFlowNodeReadException, SFlowNodeExecutionException, SFlowNodeNotFoundException {\n        // in same thread because we delete the message instance after triggering the catch event. The data is of the message\n        // is deleted so we will be unable to execute the flow node instance\n        if (operations.getOperations() != null && !operations.getOperations().isEmpty()) {\n            try {\n                operationService.execute(operations.getOperations(), flowNodeInstanceId,\n                        DataInstanceContainer.ACTIVITY_INSTANCE.name(),\n                        operations.getContext());\n            } catch (SOperationExecutionException e) {\n                throw new SFlowNodeExecutionException(\n                        \"Unable to execute operation before executing flow node \" + flowNodeInstanceId, e);\n            }\n        }\n        containerRegistry.executeFlowNodeInSameThread(flowNodeInstanceService.getFlowNodeInstance(flowNodeInstanceId),\n                operations.getContainerType());\n    }\n\n    private void instantiateProcess(final long processDefinitionId, final long targetSFlowNodeDefinitionId,\n            final OperationsWithContext operations)\n            throws SProcessInstanceCreationException, SContractViolationException {\n        Supplier<ProcessInstanceMDC> processInstanceMDC = () -> new ProcessInstanceMDC(0, Optional.empty(),\n                Optional.empty(), processDefinitionId, 0);\n        CheckedRunnable2<SProcessInstanceCreationException, SContractViolationException> run = () -> {\n            processExecutor.start(processDefinitionId, targetSFlowNodeDefinitionId, 0, 0, operations.getContext(),\n                    operations.getOperations(), -1,\n                    -1, null);\n        };\n\n        MDCHelper.tryWithMDC(processInstanceMDC, run);\n    }\n\n    public EventHandlerStrategy getHandler(final SEventTriggerType triggerType) {\n        return handlers.get(triggerType);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/event/MessageEventHandlerStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.event;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.bpm.model.impl.BPMInstancesCreator;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SReceiveTaskDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SCatchEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SCatchMessageEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SCorrelationDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SMessageEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowMessageEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageInstanceCreationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventCreationException;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SCorrelationContainerBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SMessageInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingMessageEventBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingMessageEventBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.event.SCatchEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SThrowEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SBPMEventType;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.data.instance.exception.SDataInstanceException;\nimport org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.exception.SExpressionException;\nimport org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.message.MessagesHandlingService;\nimport org.bonitasoft.engine.transaction.STransactionNotFoundException;\n\n/**\n * @author Baptiste Mesta\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\n@Slf4j\npublic class MessageEventHandlerStrategy extends CoupleEventHandlerStrategy {\n\n    private static final String CORRELATION_NO_VALUE = \"NONE\";\n\n    private final ExpressionResolverService expressionResolverService;\n\n    private final BPMInstancesCreator bpmInstancesCreator;\n\n    private final ProcessDefinitionService processDefinitionService;\n    private final MessagesHandlingService messagesHandlingService;\n\n    public MessageEventHandlerStrategy(final ExpressionResolverService expressionResolverService,\n            final EventInstanceService eventInstanceService, final BPMInstancesCreator bpmInstancesCreator,\n            final ProcessDefinitionService processDefinitionService, MessagesHandlingService messagesHandlingService) {\n        super(eventInstanceService);\n        this.expressionResolverService = expressionResolverService;\n        this.bpmInstancesCreator = bpmInstancesCreator;\n        this.processDefinitionService = processDefinitionService;\n        this.messagesHandlingService = messagesHandlingService;\n    }\n\n    @Override\n    public void handleCatchEvent(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition,\n            final SCatchEventInstance eventInstance,\n            final SEventTriggerDefinition sEventTriggerDefinition) throws SBonitaException {\n        final SCatchMessageEventTriggerDefinition messageTrigger = (SCatchMessageEventTriggerDefinition) sEventTriggerDefinition;\n        final String messageName = messageTrigger.getMessageName();\n        final String processName = processDefinition.getName();\n        final SWaitingMessageEventBuilder builder;\n        SExpressionContext expressionContext;\n        switch (eventDefinition.getType()) {\n            case BOUNDARY_EVENT:\n                builder = BuilderFactory.get(SWaitingMessageEventBuilderFactory.class)\n                        .createNewWaitingMessageBoundaryEventInstance(processDefinition.getId(),\n                                eventInstance.getRootContainerId(), eventInstance.getParentProcessInstanceId(),\n                                eventInstance.getId(), messageName, processName,\n                                eventInstance.getFlowNodeDefinitionId(),\n                                eventInstance.getName());\n                expressionContext = new SExpressionContext(eventInstance.getParentContainerId(),\n                        getParentContainerType(eventInstance).name(),\n                        processDefinition.getId());\n                break;\n            case INTERMEDIATE_CATCH_EVENT:\n                builder = BuilderFactory.get(SWaitingMessageEventBuilderFactory.class)\n                        .createNewWaitingMessageIntermediateEventInstance(\n                                processDefinition.getId(),\n                                eventInstance.getRootContainerId(), eventInstance.getParentProcessInstanceId(),\n                                eventInstance.getId(), messageName, processName,\n                                eventInstance.getFlowNodeDefinitionId(),\n                                eventInstance.getName());\n                expressionContext = new SExpressionContext(eventInstance.getParentContainerId(),\n                        getParentContainerType(eventInstance).name(),\n                        processDefinition.getId());\n                break;\n            case START_EVENT:\n                builder = BuilderFactory.get(SWaitingMessageEventBuilderFactory.class)\n                        .createNewWaitingMessageStartEventInstance(processDefinition.getId(),\n                                messageTrigger.getMessageName(), processDefinition.getName(), eventDefinition.getId(),\n                                eventDefinition.getName());\n                expressionContext = new SExpressionContext();\n                expressionContext.setProcessDefinitionId(processDefinition.getId());\n                break;\n            default:\n                throw new SWaitingEventCreationException(eventDefinition.getType() + \" is not a catch event.\");\n        }\n        fillCorrelation(builder, messageTrigger.getCorrelations(), expressionContext);\n        getEventInstanceService().createWaitingEvent(builder.done());\n        messagesHandlingService.triggerMatchingOfMessages();\n    }\n\n    public void handleCatchEvent(final SProcessDefinition processDefinition,\n            final SReceiveTaskInstance receiveTaskInstance,\n            final SEventTriggerDefinition sEventTriggerDefinition) throws SBonitaException {\n        final SCatchMessageEventTriggerDefinition messageTrigger = (SCatchMessageEventTriggerDefinition) sEventTriggerDefinition;\n        final String messageName = messageTrigger.getMessageName();\n        final String processName = processDefinition.getName();\n        final SWaitingMessageEventBuilder builder;\n        SExpressionContext expressionContext;\n\n        builder = BuilderFactory.get(SWaitingMessageEventBuilderFactory.class)\n                .createNewWaitingMessageIntermediateEventInstance(processDefinition.getId(),\n                        receiveTaskInstance.getRootContainerId(), receiveTaskInstance.getParentProcessInstanceId(),\n                        receiveTaskInstance.getId(), messageName,\n                        processName, receiveTaskInstance.getFlowNodeDefinitionId(),\n                        receiveTaskInstance.getName());\n        expressionContext = new SExpressionContext(receiveTaskInstance.getParentContainerId(),\n                getParentContainerType(receiveTaskInstance).name(),\n                processDefinition.getId());\n\n        try {\n            fillCorrelation(builder, messageTrigger.getCorrelations(), expressionContext);\n            getEventInstanceService().createWaitingEvent(builder.done());\n            messagesHandlingService.triggerMatchingOfMessages();\n        } catch (SBonitaException e) {\n            e.setMessageInstanceNameOnContext(messageTrigger.getMessageName());\n            throw e;\n        }\n    }\n\n    @Override\n    public void handleThrowEvent(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition,\n            final SThrowEventInstance eventInstance,\n            final SEventTriggerDefinition sEventTriggerDefinition) throws SBonitaException {\n        final Long processDefinitionId = processDefinition.getId();\n        final SExpressionContext expressionContext = new SExpressionContext(eventInstance.getParentContainerId(),\n                getParentContainerType(eventInstance).name(),\n                processDefinitionId);\n        try {\n            handleThrowMessage(sEventTriggerDefinition, eventInstance.getName(), processDefinitionId,\n                    expressionContext);\n        } catch (SBonitaException e) {\n            e.setMessageInstanceNameOnContext(\n                    ((SThrowMessageEventTriggerDefinition) sEventTriggerDefinition).getMessageName());\n            throw e;\n        }\n    }\n\n    public void handleThrowEvent(final SProcessDefinition processDefinition, final SSendTaskInstance sendTaskInstance,\n            final SThrowMessageEventTriggerDefinition messageTrigger) throws SMessageInstanceCreationException,\n            SDataInstanceException, SExpressionException, STransactionNotFoundException {\n        final SExpressionContext expressionContext = new SExpressionContext(sendTaskInstance.getId(),\n                DataInstanceContainer.ACTIVITY_INSTANCE.name(),\n                processDefinition.getId());\n        try {\n            handleThrowMessage(messageTrigger, sendTaskInstance.getName(), processDefinition.getId(),\n                    expressionContext);\n        } catch (SBonitaException e) {\n            e.setMessageInstanceNameOnContext(messageTrigger.getMessageName());\n            throw e;\n        }\n    }\n\n    private void handleThrowMessage(final SEventTriggerDefinition sEventTriggerDefinition,\n            final String eventInstanceName,\n            final Long processDefinitionId, final SExpressionContext expressionContext)\n            throws SMessageInstanceCreationException, SDataInstanceException, SExpressionException,\n            STransactionNotFoundException {\n        final SThrowMessageEventTriggerDefinition messageTrigger = (SThrowMessageEventTriggerDefinition) sEventTriggerDefinition;\n        final String messageName = messageTrigger.getMessageName();\n        final SExpression targetProcess = messageTrigger.getTargetProcess();\n        final SExpression targetFlowNode = messageTrigger.getTargetFlowNode();\n        // evaluate expression\n        final String stringTargetProcess = (String) expressionResolverService.evaluate(targetProcess,\n                expressionContext);\n        String stringTargetFlowNode = null;\n        if (targetFlowNode != null) {\n            stringTargetFlowNode = (String) expressionResolverService.evaluate(targetFlowNode, expressionContext);\n        }\n        final SMessageInstanceBuilder builder = SMessageInstanceBuilder.create(messageName, stringTargetProcess,\n                stringTargetFlowNode, processDefinitionId, eventInstanceName);\n        final List<SCorrelationDefinition> correlations = messageTrigger.getCorrelations();\n\n        fillCorrelation(builder, correlations, expressionContext);\n        final SMessageInstance messageInstance = builder.done();\n        // evaluate and add correlations\n        getEventInstanceService().createMessageInstance(messageInstance);\n        messagesHandlingService.triggerMatchingOfMessages();\n\n        // create data\n        if (!messageTrigger.getDataDefinitions().isEmpty()) {\n            bpmInstancesCreator.createDataInstances(messageTrigger.getDataDefinitions(), messageInstance.getId(),\n                    DataInstanceContainer.MESSAGE_INSTANCE,\n                    expressionContext);\n            if (log.isDebugEnabled()) {\n                log.debug(\"Initialized variables for message instance [name: <{}>, id: <{}>, flow node: <{}>,\" +\n                        \" target flow node: <{}>, target process: <{}>, process definition: <{}>]\",\n                        messageInstance.getMessageName(), messageInstance.getId(), messageInstance.getFlowNodeName(),\n                        messageInstance.getTargetFlowNode(), messageInstance.getTargetProcess(),\n                        messageInstance.getProcessDefinitionId());\n            }\n\n        }\n    }\n\n    private void fillCorrelation(final SCorrelationContainerBuilder builder,\n            final List<SCorrelationDefinition> correlations,\n            final SExpressionContext expressionContext)\n            throws SExpressionTypeUnknownException, SExpressionEvaluationException,\n            SExpressionDependencyMissingException, SInvalidExpressionException {\n        final int size = Math.min(5, correlations.size());\n        final List<SExpression> toEval = new ArrayList<>(size * 2);\n        if (size > 0) {\n            for (int i = 0; i < size; i++) {\n                final SCorrelationDefinition sCorrelationDefinition = correlations.get(i);\n                toEval.add(sCorrelationDefinition.getKey());\n                toEval.add(sCorrelationDefinition.getValue());\n            }\n            final List<Object> res = expressionResolverService.evaluate(toEval, expressionContext);\n\n            final List<String> keys = new ArrayList<>(size);\n            final List<String> values = new ArrayList<>(size);\n            for (int i = 0; i < size; i++) {\n                keys.add(String.valueOf(res.get(i * 2)));\n                values.add(String.valueOf(res.get(i * 2 + 1)));\n            }\n            final List<String> sortedKeys = new ArrayList<>(keys);\n            Collections.sort(sortedKeys);\n            for (int i = 0; i < size; i++) {\n                final String key = sortedKeys.get(i);\n                builder.setCorrelation(i + 1, key + \"-$-\" + values.get(keys.indexOf(key)));\n            }\n\n        }\n        for (int i = size; i < 5; i++) {\n            builder.setCorrelation(i + 1, CORRELATION_NO_VALUE);\n        }\n    }\n\n    @Override\n    public OperationsWithContext getOperations(final SWaitingEvent waitingEvent, final Long triggeringElementID)\n            throws SBonitaException {\n        final SMessageInstance messageInstance = getEventInstanceService().getMessageInstance(triggeringElementID);\n        final SProcessDefinition processDefinition = processDefinitionService\n                .getProcessDefinition(waitingEvent.getProcessDefinitionId());\n        final SExpressionContext context = new SExpressionContext(messageInstance.getId(),\n                DataInstanceContainer.MESSAGE_INSTANCE.name(),\n                processDefinition.getId());\n        final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer();\n        final SFlowNodeDefinition targetFlowNode = processContainer.getFlowNode(waitingEvent.getFlowNodeName());\n        SCatchMessageEventTriggerDefinition messageEventTrigger;\n        if (targetFlowNode instanceof SReceiveTaskDefinition) {\n            messageEventTrigger = ((SReceiveTaskDefinition) targetFlowNode).getTrigger();\n        } else {\n            SCatchEventDefinition catchEvent;\n            if (SBPMEventType.BOUNDARY_EVENT.equals(waitingEvent.getEventType())) {\n                catchEvent = processContainer.getBoundaryEvent(messageInstance.getTargetFlowNode());\n            } else {\n                catchEvent = (SCatchEventDefinition) targetFlowNode;\n            }\n            messageEventTrigger = catchEvent.getMessageEventTriggerDefinition(messageInstance.getMessageName());\n        }\n        final List<SOperation> operations = messageEventTrigger.getOperations();\n        return new OperationsWithContext(context, operations);\n    }\n\n    @Override\n    public void handleThrowEvent(final SEventTriggerDefinition sEventTriggerDefinition) throws SBonitaException {\n        handleThrowMessage(sEventTriggerDefinition, \"\", -1L, null);\n    }\n\n    @Override\n    public void handleEventSubProcess(final SProcessDefinition processDefinition,\n            final SEventDefinition eventDefinition,\n            final SEventTriggerDefinition sEventTriggerDefinition, final long subProcessId,\n            final SProcessInstance parentProcessInstance)\n            throws SBonitaException {\n        final SWaitingMessageEventBuilderFactory builderFact = BuilderFactory\n                .get(SWaitingMessageEventBuilderFactory.class);\n        final SMessageEventTriggerDefinition messageEventTriggerDefinition = (SMessageEventTriggerDefinition) sEventTriggerDefinition;\n        final SWaitingMessageEventBuilder builder = builderFact.createNewWaitingMessageEventSubProcInstance(\n                processDefinition.getId(),\n                parentProcessInstance.getId(), parentProcessInstance.getRootProcessInstanceId(),\n                messageEventTriggerDefinition.getMessageName(),\n                processDefinition.getName(), eventDefinition.getId(), eventDefinition.getName(), subProcessId);\n        final SExpressionContext expressionContext = new SExpressionContext(parentProcessInstance.getId(),\n                DataInstanceContainer.PROCESS_INSTANCE.name(),\n                processDefinition.getId());\n        fillCorrelation(builder, messageEventTriggerDefinition.getCorrelations(), expressionContext);\n        getEventInstanceService().createWaitingEvent(builder.done());\n        messagesHandlingService.triggerMatchingOfMessages();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/event/OperationsWithContext.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.event;\n\nimport java.io.Serializable;\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowElementsContainerType;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class OperationsWithContext implements Serializable {\n\n    private static final long serialVersionUID = 6034976719148086546L;\n\n    private final SExpressionContext context;\n\n    private final List<SOperation> operations;\n\n    private final String containerType;\n\n    /**\n     * @param context the list of operations\n     * @param operations\n     */\n    public OperationsWithContext(final SExpressionContext context, final List<SOperation> operations) {\n        this.context = context;\n        this.operations = operations;\n        containerType = SFlowElementsContainerType.PROCESS.name();\n    }\n\n    /**\n     * @param context\n     * @param operations the list of operations\n     * @param containerType the type of container on which to execute the operations\n     */\n    public OperationsWithContext(final SExpressionContext context, final List<SOperation> operations,\n            final String containerType) {\n        this.context = context;\n        this.operations = operations;\n        this.containerType = containerType;\n    }\n\n    /**\n     * @return the context\n     */\n    public SExpressionContext getContext() {\n        return context;\n    }\n\n    /**\n     * @return the operations\n     */\n    public List<SOperation> getOperations() {\n        return operations;\n    }\n\n    /**\n     * @return\n     */\n    public String getContainerType() {\n        return containerType;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/event/SBPMEventHandlerException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.event;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SBPMEventHandlerException extends SBonitaException {\n\n    private static final long serialVersionUID = 1509958636645119957L;\n\n    public SBPMEventHandlerException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/event/SignalEventHandlerStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.event;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SSignalEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventCreationException;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingSignalEventBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingSignalEventBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.event.SCatchEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SThrowEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent;\nimport org.bonitasoft.engine.execution.work.BPMWorkFactory;\nimport org.bonitasoft.engine.work.WorkService;\n\n/**\n * @author Baptiste Mesta\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class SignalEventHandlerStrategy extends CoupleEventHandlerStrategy {\n\n    private static final OperationsWithContext EMPTY = new OperationsWithContext(null, null);\n\n    private final WorkService workService;\n    private final BPMWorkFactory workFactory;\n\n    public SignalEventHandlerStrategy(final EventInstanceService eventInstanceService, WorkService workService,\n            BPMWorkFactory workFactory) {\n        super(eventInstanceService);\n        this.workService = workService;\n        this.workFactory = workFactory;\n    }\n\n    @Override\n    public void handleCatchEvent(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition,\n            final SCatchEventInstance eventInstance,\n            final SEventTriggerDefinition sEventTriggerDefinition) throws SBonitaException {\n        final SWaitingSignalEventBuilderFactory builderFact = BuilderFactory\n                .get(SWaitingSignalEventBuilderFactory.class);\n        final SSignalEventTriggerDefinition sSignalEventTriggerDefinition = (SSignalEventTriggerDefinition) sEventTriggerDefinition;\n        SWaitingSignalEventBuilder builder = null;\n        switch (eventDefinition.getType()) {\n            case BOUNDARY_EVENT:\n                builder = builderFact.createNewWaitingSignalBoundaryEventInstance(processDefinition.getId(),\n                        eventInstance.getRootProcessInstanceId(),\n                        eventInstance.getParentContainerId(), eventInstance.getId(),\n                        sSignalEventTriggerDefinition.getSignalName(), processDefinition.getName(),\n                        eventDefinition.getId(), eventInstance.getName());\n                break;\n            case INTERMEDIATE_CATCH_EVENT:\n                builder = builderFact.createNewWaitingSignalIntermediateEventInstance(processDefinition.getId(),\n                        eventInstance.getRootProcessInstanceId(),\n                        eventInstance.getParentContainerId(), eventInstance.getId(),\n                        sSignalEventTriggerDefinition.getSignalName(), processDefinition.getName(),\n                        eventDefinition.getId(), eventInstance.getName());\n                break;\n            case START_EVENT:\n                builder = builderFact.createNewWaitingSignalStartEventInstance(processDefinition.getId(),\n                        sSignalEventTriggerDefinition.getSignalName(),\n                        processDefinition.getName(), eventDefinition.getId(), eventDefinition.getName());\n                break;\n            default:\n                throw new SWaitingEventCreationException(eventDefinition.getType() + \" is not a catch event.\");\n        }\n\n        final SWaitingSignalEvent signalEvent = builder.done();\n        getEventInstanceService().createWaitingEvent(signalEvent);\n    }\n\n    @Override\n    public void handleThrowEvent(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition,\n            final SThrowEventInstance eventInstance,\n            final SEventTriggerDefinition sEventTriggerDefinition) throws SBonitaException {\n        handleThrowSignal(sEventTriggerDefinition);\n    }\n\n    private void handleThrowSignal(final SEventTriggerDefinition sEventTriggerDefinition) throws SBonitaException {\n        final SSignalEventTriggerDefinition signalTrigger = (SSignalEventTriggerDefinition) sEventTriggerDefinition;\n        int index = 0;\n        List<SWaitingSignalEvent> listeningSignals = get100WaitingSignalEvents(signalTrigger, index);\n        while (!listeningSignals.isEmpty()) {\n            for (final SWaitingSignalEvent listeningSignal : listeningSignals) {\n                workService.registerWork(workFactory.createTriggerSignalWorkDescriptor(listeningSignal));\n            }\n            index++;\n            listeningSignals = get100WaitingSignalEvents(signalTrigger, index);\n        }\n    }\n\n    private List<SWaitingSignalEvent> get100WaitingSignalEvents(final SSignalEventTriggerDefinition signalTrigger,\n            int index)\n            throws SEventTriggerInstanceReadException {\n        return getEventInstanceService().getWaitingSignalEvents(signalTrigger.getSignalName(), 100 * index, 100);\n    }\n\n    @Override\n    public OperationsWithContext getOperations(final SWaitingEvent waitingEvent, final Long triggeringElementID) {\n        return EMPTY;\n    }\n\n    @Override\n    public void handleThrowEvent(final SEventTriggerDefinition sEventTriggerDefinition) throws SBonitaException {\n        handleThrowSignal(sEventTriggerDefinition);\n    }\n\n    @Override\n    public void handleEventSubProcess(final SProcessDefinition processDefinition,\n            final SEventDefinition eventDefinition,\n            final SEventTriggerDefinition sEventTriggerDefinition, final long subProcessId,\n            final SProcessInstance parentProcessInstance)\n            throws SBonitaException {\n        final SWaitingSignalEventBuilderFactory builderFact = BuilderFactory\n                .get(SWaitingSignalEventBuilderFactory.class);\n        final SSignalEventTriggerDefinition sSignalEventTriggerDefinition = (SSignalEventTriggerDefinition) sEventTriggerDefinition;\n        final SWaitingSignalEventBuilder builder = builderFact.createNewWaitingSignalEventSubProcInstance(\n                processDefinition.getId(),\n                parentProcessInstance.getId(), parentProcessInstance.getRootProcessInstanceId(),\n                sSignalEventTriggerDefinition.getSignalName(),\n                processDefinition.getName(),\n                eventDefinition.getId(), eventDefinition.getName(), subProcessId);\n\n        final SWaitingSignalEvent signalEvent = builder.done();\n        getEventInstanceService().createWaitingEvent(signalEvent);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/event/TerminateEventHandlerStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.event;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.core.process.instance.model.event.SCatchEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SThrowEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent;\nimport org.bonitasoft.engine.execution.ProcessInstanceInterruptor;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class TerminateEventHandlerStrategy extends EventHandlerStrategy {\n\n    private static final OperationsWithContext EMPTY = new OperationsWithContext(null, null);\n\n    private ProcessInstanceInterruptor processInstanceInterruptor;\n\n    public TerminateEventHandlerStrategy(ProcessInstanceInterruptor processInstanceInterruptor) {\n        super();\n        this.processInstanceInterruptor = processInstanceInterruptor;\n    }\n\n    @Override\n    public void handleThrowEvent(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition,\n            final SThrowEventInstance eventInstance,\n            final SEventTriggerDefinition sEventTriggerDefinition) throws SBonitaException {\n        processInstanceInterruptor.interruptChildrenOfProcessInstance(eventInstance.getParentContainerId(),\n                SStateCategory.ABORTING, eventInstance.getId());\n        // Parent should always be process for event (but must change that id it's not the case anymore\n    }\n\n    @Override\n    public void handleCatchEvent(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition,\n            final SCatchEventInstance eventInstance,\n            final SEventTriggerDefinition sEventTriggerDefinition) {\n        // No catch of terminate\n\n    }\n\n    @Override\n    public OperationsWithContext getOperations(final SWaitingEvent waitingEvent, final Long triggeringElementID) {\n        return EMPTY;\n    }\n\n    @Override\n    public void handleEventSubProcess(final SProcessDefinition processDefinition,\n            final SEventDefinition eventDefinition,\n            final SEventTriggerDefinition sEventTriggerDefinition, final long subProcessId,\n            final SProcessInstance parentProcessInstance) {\n        // TODO Auto-generated method stub\n\n    }\n\n    @Override\n    public void unregisterCatchEvent(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition,\n            final SEventTriggerDefinition sEventTriggerDefinition, final long subProcessId,\n            final SProcessInstance parentProcessIsnstance) {\n        // TODO Auto-generated method stub\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/event/TimerEventHandlerStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.event;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.UUID;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SStartEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.STimerEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.STimerType;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowElementsContainerType;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SCatchEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SThrowEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent;\nimport org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.execution.job.JobNameBuilder;\nimport org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.jobs.TriggerTimerEventJob;\nimport org.bonitasoft.engine.scheduler.SchedulerService;\nimport org.bonitasoft.engine.scheduler.model.SJobDescriptor;\nimport org.bonitasoft.engine.scheduler.model.SJobParameter;\nimport org.bonitasoft.engine.scheduler.trigger.OneShotTrigger;\nimport org.bonitasoft.engine.scheduler.trigger.Trigger;\nimport org.bonitasoft.engine.scheduler.trigger.Trigger.MisfireRestartPolicy;\nimport org.bonitasoft.engine.scheduler.trigger.UnixCronTrigger;\n\n/**\n * @author Baptiste Mesta\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\n@Slf4j\npublic class TimerEventHandlerStrategy extends EventHandlerStrategy {\n\n    private static final OperationsWithContext EMPTY = new OperationsWithContext(null, null);\n\n    private final SchedulerService schedulerService;\n\n    private final ExpressionResolverService expressionResolverService;\n\n    private final EventInstanceService eventInstanceService;\n\n    public TimerEventHandlerStrategy(final ExpressionResolverService expressionResolverService,\n            final SchedulerService schedulerService,\n            final EventInstanceService eventInstanceService) {\n        this.schedulerService = schedulerService;\n        this.expressionResolverService = expressionResolverService;\n        this.eventInstanceService = eventInstanceService;\n    }\n\n    @Override\n    public void handleCatchEvent(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition,\n            final SCatchEventInstance eventInstance,\n            final SEventTriggerDefinition sEventTriggerDefinition) throws SBonitaException {\n        final String jobName = JobNameBuilder.getTimerEventJobName(processDefinition.getId(), eventDefinition,\n                eventInstance);\n        final SJobDescriptor jobDescriptor = getJobDescriptor(jobName);\n        final List<SJobParameter> jobParameters = getJobParameters(processDefinition, eventDefinition, eventInstance);\n        STimerEventTriggerDefinition timerEventTriggerDefinition = (STimerEventTriggerDefinition) sEventTriggerDefinition;\n        final Object timerCondition = evaluateTimerCondition(timerEventTriggerDefinition, processDefinition.getId(),\n                eventInstance != null ? eventInstance.getParentProcessInstanceId() : null);\n        Trigger trigger = scheduleJob(timerEventTriggerDefinition, jobDescriptor, jobParameters, timerCondition);\n        if (timerEventTriggerDefinition.getTimerType() != STimerType.CYCLE && eventInstance != null) {\n            final STimerEventTriggerInstance sEventTriggerInstance = new STimerEventTriggerInstance(\n                    eventInstance.getId(), eventInstance.getName(), trigger.getStartDate().getTime(),\n                    trigger.getName());\n            eventInstanceService.createTimerEventTriggerInstance(sEventTriggerInstance);\n        }\n    }\n\n    protected Trigger getTrigger(final STimerEventTriggerDefinition timerTrigger, Object timerCondition)\n            throws SBonitaException {\n        Date startDate;\n        Trigger trigger;\n        switch (timerTrigger.getTimerType()) {\n            case DURATION:\n                startDate = new Date(System.currentTimeMillis() + (Long) timerCondition);\n                trigger = new OneShotTrigger(\"OneShotTrigger\" + UUID.randomUUID().getLeastSignificantBits(), startDate);\n                break;\n            case DATE:\n                startDate = (Date) timerCondition;\n                trigger = new OneShotTrigger(\"OneShotTrigger\" + UUID.randomUUID().getLeastSignificantBits(), startDate);\n                break;\n            case CYCLE:\n                startDate = new Date();\n                trigger = new UnixCronTrigger(\"UnixCronTrigger\" + UUID.randomUUID().getLeastSignificantBits(),\n                        startDate, (String) timerCondition,\n                        MisfireRestartPolicy.ALL);\n                break;\n            default:\n                throw new IllegalStateException();\n        }\n        return trigger;\n    }\n\n    private Object evaluateTimerCondition(STimerEventTriggerDefinition timerTrigger, long processDefinitionId,\n            Long processInstanceId)\n            throws SExpressionTypeUnknownException, SExpressionEvaluationException,\n            SExpressionDependencyMissingException, SInvalidExpressionException {\n        final SExpressionContext expressionContext = getEvaluationContext(processDefinitionId, processInstanceId);\n        final SExpression timerExpression = timerTrigger.getTimerExpression();\n        final Object result = expressionResolverService.evaluate(timerExpression, expressionContext);\n        if (result == null) {\n            throw new SInvalidExpressionException(\"The duration cannot be null.\", timerExpression.getName());\n        }\n        return result;\n    }\n\n    private SExpressionContext getEvaluationContext(long processDefinitionId, Long processInstanceId) {\n        final SExpressionContext expressionContext;\n        if (processInstanceId != null) {\n            expressionContext = new SExpressionContext(processInstanceId, DataInstanceContainer.PROCESS_INSTANCE.name(),\n                    processDefinitionId);\n        } else {\n            expressionContext = new SExpressionContext();\n            expressionContext.setProcessDefinitionId(processDefinitionId);\n        }\n        return expressionContext;\n    }\n\n    @Override\n    public void handleThrowEvent(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition,\n            final SThrowEventInstance eventInstance,\n            final SEventTriggerDefinition sEventTriggerDefinition) {\n    }\n\n    @Override\n    public OperationsWithContext getOperations(final SWaitingEvent waitingEvent, final Long triggeringElementID) {\n        return EMPTY;\n    }\n\n    @Override\n    public void handleEventSubProcess(final SProcessDefinition processDefinition,\n            final SEventDefinition eventDefinition,\n            final SEventTriggerDefinition sEventTriggerDefinition, final long subProcessId,\n            final SProcessInstance parentProcessInstance)\n            throws SBonitaException {\n        final String jobName = JobNameBuilder.getTimerEventJobName(processDefinition.getId(), eventDefinition,\n                parentProcessInstance.getId(), subProcessId);\n        final SJobDescriptor jobDescriptor = getJobDescriptor(jobName);\n        final List<SJobParameter> jobParameters = getJobParameters(processDefinition, eventDefinition, null,\n                subProcessId, parentProcessInstance);\n\n        STimerEventTriggerDefinition timerEventTriggerDefinition = (STimerEventTriggerDefinition) sEventTriggerDefinition;\n        final Object timerCondition = evaluateTimerCondition(timerEventTriggerDefinition, processDefinition.getId(),\n                parentProcessInstance.getId());\n        scheduleJob(timerEventTriggerDefinition, jobDescriptor, jobParameters, timerCondition);\n    }\n\n    private Trigger scheduleJob(final STimerEventTriggerDefinition sEventTriggerDefinition,\n            final SJobDescriptor jobDescriptor,\n            final List<SJobParameter> jobParameters, Object timerCondition)\n            throws SBonitaException {\n        final Trigger trigger = getTrigger(sEventTriggerDefinition, timerCondition);\n        schedulerService.schedule(jobDescriptor, jobParameters, trigger);\n        return trigger;\n    }\n\n    private List<SJobParameter> getJobParameters(final SProcessDefinition processDefinition,\n            final SEventDefinition eventDefinition,\n            final SCatchEventInstance eventInstance) {\n        final List<SJobParameter> jobParameters = new ArrayList<SJobParameter>();\n        jobParameters.add(SJobParameter.builder()\n                .key(\"processDefinitionId\")\n                .value(processDefinition.getId()).build());\n        jobParameters.add(SJobParameter.builder()\n                .key(\"containerType\")\n                .value(SFlowElementsContainerType.PROCESS.name())\n                .build());\n        jobParameters.add(SJobParameter.builder()\n                .key(\"eventType\")\n                .value(eventDefinition.getType().name()).build());\n        jobParameters.add(SJobParameter.builder()\n                .key(\"targetSFlowNodeDefinitionId\")\n                .value(eventDefinition.getId())\n                .build());\n        if (SFlowNodeType.START_EVENT.equals(eventDefinition.getType())) {\n            final SStartEventDefinition startEvent = (SStartEventDefinition) eventDefinition;\n            jobParameters.add(SJobParameter.builder()\n                    .key(\"isInterrupting\")\n                    .value(startEvent.isInterrupting()).build());\n        }\n        if (eventInstance != null) {\n            jobParameters.add(SJobParameter.builder()\n                    .key(\"flowNodeInstanceId\")\n                    .value(eventInstance.getId()).build());\n        }\n        return jobParameters;\n    }\n\n    private List<SJobParameter> getJobParameters(final SProcessDefinition processDefinition,\n            final SEventDefinition eventDefinition,\n            final SCatchEventInstance eventInstance, final long subProcessId,\n            final SProcessInstance parentProcessInstance) {\n        final List<SJobParameter> jobParameters = new ArrayList<SJobParameter>();\n        jobParameters.addAll(getJobParameters(processDefinition, eventDefinition, eventInstance));\n        jobParameters.add(SJobParameter.builder()\n                .key(\"subProcessId\")\n                .value(subProcessId).build());\n        jobParameters.add(SJobParameter.builder()\n                .key(\"processInstanceId\")\n                .value(parentProcessInstance.getId()).build());\n        jobParameters.add(SJobParameter.builder()\n                .key(\"rootProcessInstanceId\")\n                .value(parentProcessInstance.getRootProcessInstanceId()).build());\n        return jobParameters;\n    }\n\n    private SJobDescriptor getJobDescriptor(final String jobName) {\n        return SJobDescriptor.builder()\n                .jobClassName(TriggerTimerEventJob.class.getName())\n                .jobName(jobName)\n                .build();\n    }\n\n    @Override\n    public void unregisterCatchEvent(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition,\n            final SEventTriggerDefinition sEventTriggerDefinition, final long subProcessId,\n            final SProcessInstance parentProcessInstance)\n            throws SBonitaException {\n        final String jobName = JobNameBuilder.getTimerEventJobName(processDefinition.getId(), eventDefinition,\n                parentProcessInstance.getId(), subProcessId);\n        final boolean delete = schedulerService.delete(jobName);\n        if (!delete) {\n            if (log.isDebugEnabled()) {\n                final StringBuilder stb = new StringBuilder();\n                stb.append(\"No job found with name '\");\n                stb.append(jobName);\n                stb.append(\"' when interrupting timer catch event named '\");\n                stb.append(eventDefinition.getName());\n                stb.append(\". In process definition [name = <\");\n                stb.append(processDefinition.getName());\n                stb.append(\">, version = <\");\n                stb.append(processDefinition.getVersion());\n                stb.append(\">]\");\n                stb.append(\"'. It was probably already triggered.\");\n                final String message = stb.toString();\n                log.debug(message);\n            }\n        }\n\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/flowmerger/FlowNodeTransitionsWrapper.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.flowmerger;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class FlowNodeTransitionsWrapper {\n\n    //TODO: calculate the value of this three attributes in this class\n\n    private int inputTransitionsSize;\n\n    private List<STransitionDefinition> allOutgoingTransitionDefinitions = new ArrayList<>();\n\n    private List<STransitionDefinition> validOutgoingTransitionDefinitions = new ArrayList<>();\n\n    private STransitionDefinition defaultTransition;\n\n    public int getInputTransitionsSize() {\n        return inputTransitionsSize;\n    }\n\n    public void setInputTransitionsSize(final int inputTransitionsSize) {\n        this.inputTransitionsSize = inputTransitionsSize;\n    }\n\n    public List<STransitionDefinition> getNonDefaultOutgoingTransitionDefinitions() {\n        return Collections.unmodifiableList(allOutgoingTransitionDefinitions);\n    }\n\n    public void setAllOutgoingTransitionDefinitions(\n            final List<STransitionDefinition> allOutgoingTransitionDefinitions) {\n        if (allOutgoingTransitionDefinitions != null) {\n            this.allOutgoingTransitionDefinitions = allOutgoingTransitionDefinitions;\n        } else {\n            this.allOutgoingTransitionDefinitions = new ArrayList<>();\n        }\n    }\n\n    public List<STransitionDefinition> getValidOutgoingTransitionDefinitions() {\n        return Collections.unmodifiableList(validOutgoingTransitionDefinitions);\n    }\n\n    public void setValidOutgoingTransitionDefinitions(\n            final List<STransitionDefinition> validOutgoingTransitionDefinitions) {\n        if (validOutgoingTransitionDefinitions != null) {\n            this.validOutgoingTransitionDefinitions = validOutgoingTransitionDefinitions;\n        } else {\n            this.validOutgoingTransitionDefinitions = new ArrayList<>();\n        }\n    }\n\n    public boolean isLastFlowNode() {\n        return validOutgoingTransitionDefinitions == null || validOutgoingTransitionDefinitions.isEmpty();\n    }\n\n    public boolean hasMultipleOutgoingTransitions() {\n        return hasMultipleElements(allOutgoingTransitionDefinitions);\n    }\n\n    private boolean hasMultipleElements(final Collection<?> collection) {\n        return collection != null && collection.size() > 1;\n    }\n\n    public boolean hasMultipleIncomingTransitions() {\n        return inputTransitionsSize > 1;\n    }\n\n    public boolean isManyToMany() {\n        return hasMultipleIncomingTransitions() && hasMultipleOutgoingTransitions();\n    }\n\n    /**\n     * from 0 or 1 input transition to one outgoing transition\n     *\n     * @return true for flow node with 0 or 1 input transition and one outgoing transitions\n     * @since 6.2\n     */\n    public boolean isSimpleMerge() {\n        return !hasMultipleIncomingTransitions() && hasOneElement();\n    }\n\n    private boolean hasOneElement() {\n        return allOutgoingTransitionDefinitions.size() == 1\n                || allOutgoingTransitionDefinitions.isEmpty() && validOutgoingTransitionDefinitions.size() == 1;\n    }\n\n    /**\n     * from 0 or 1 input transition to more than one outgoing transitions\n     *\n     * @return true for flow node with 0 or 1 input transition and more than one outgoing transitions\n     * @since 6.2\n     */\n    public boolean isSimpleToMany() {\n        return !hasMultipleIncomingTransitions() && hasMultipleOutgoingTransitions();\n    }\n\n    /**\n     * from more than 1 input transition to one outgoing transition\n     *\n     * @return true for flow node with more than 1 input transition and one outgoing transitions\n     * @since 6.2\n     */\n    public boolean isManyToOne() {\n        return hasMultipleIncomingTransitions() && hasOneElement();\n    }\n\n    public void setDefaultTransition(final STransitionDefinition defaultTransition) {\n        this.defaultTransition = defaultTransition;\n    }\n\n    public STransitionDefinition getDefaultTransition() {\n        return defaultTransition;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/handler/ArchiveProcessInstancesHandler.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.handler;\n\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.events.model.SHandlerExecutionException;\nimport org.bonitasoft.engine.events.model.SUpdateEvent;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.impl.ServiceAccessorFactory;\n\n/**\n * @author Baptiste Mesta\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class ArchiveProcessInstancesHandler implements SProcessInstanceHandler<SUpdateEvent> {\n\n    private static final long serialVersionUID = 1L;\n\n    private final String identifier;\n\n    public ArchiveProcessInstancesHandler() {\n        this.identifier = ArchiveProcessInstancesHandler.class.getName();\n    }\n\n    @Override\n    public void execute(final SUpdateEvent event) throws SHandlerExecutionException {\n        final SProcessInstance processInstance = (SProcessInstance) event.getObject();\n        try {\n            getServiceAccessor().getBPMArchiverService().archiveAndDeleteProcessInstance(processInstance);\n        } catch (SBonitaException e) {\n            throw new SHandlerExecutionException(e);\n        }\n    }\n\n    /**\n     * @return ServiceAccessor\n     */\n    private ServiceAccessor getServiceAccessor() throws SHandlerExecutionException {\n        try {\n            return ServiceAccessorFactory.getInstance().createServiceAccessor();\n        } catch (Exception e) {\n            throw new SHandlerExecutionException(e.getMessage(), null);\n        }\n    }\n\n    @Override\n    public boolean isInterested(final SUpdateEvent event) {\n        boolean isInterested = ProcessInstanceService.PROCESSINSTANCE_STATE_UPDATED.equals(event.getType())\n                && event.getObject() instanceof SProcessInstance;\n        if (isInterested) {\n            final SProcessInstance processInstance = (SProcessInstance) event.getObject();\n            // TODO add a method isInTerminalState in SProcessInstance\n            final boolean isTerminal = ProcessInstanceState.COMPLETED.getId() == processInstance.getStateId()\n                    || ProcessInstanceState.ABORTED.getId() == processInstance.getStateId()\n                    || ProcessInstanceState.CANCELLED.getId() == processInstance.getStateId();\n            // process instances called by an call activity are archive in the state CompletingCallActivity (wait data transfer from called process to caller).\n            // Sub-process can be archived directly\n            isInterested = isTerminal && (processInstance.getCallerId() <= 0\n                    || SFlowNodeType.SUB_PROCESS.equals(processInstance.getCallerType()));\n        }\n        return isInterested;\n    }\n\n    @Override\n    public String getIdentifier() {\n        return identifier;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/handler/SProcessInstanceHandler.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.handler;\n\nimport org.bonitasoft.engine.events.model.SEvent;\nimport org.bonitasoft.engine.events.model.SHandler;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface SProcessInstanceHandler<T extends SEvent> extends SHandler<T> {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/job/JobNameBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.job;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.definition.model.event.SEventDefinition;\nimport org.bonitasoft.engine.core.process.instance.model.event.SCatchEventInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n * @author Matthieu Chaffotte\n */\npublic class JobNameBuilder {\n\n    private static final String PREFIX = \"Timer_Ev_\";\n\n    public static String getTimerEventJobName(final Long processDefinitionId, final SEventDefinition eventDefinition,\n            final SCatchEventInstance flowNodeInstance) {\n        if (SFlowNodeType.START_EVENT.equals(eventDefinition.getType())) {\n            return PREFIX + processDefinitionId + eventDefinition.getName();\n        }\n        return PREFIX + flowNodeInstance.getId();\n    }\n\n    public static String getTimerEventJobName(final Long processDefinitionId, final SEventDefinition eventDefinition,\n            final long parentProcessInstanceId,\n            final long subProcessId) {\n        return PREFIX + processDefinitionId + eventDefinition.getName() + parentProcessInstanceId + subProcessId;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/AbortedFlowNodeState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.api.states.StateCode;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class AbortedFlowNodeState implements FlowNodeState {\n\n    @Override\n    public boolean shouldExecuteState(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance) {\n        return true;\n    }\n\n    @Override\n    public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance instance) {\n        return StateCode.DONE;\n    }\n\n    @Override\n    public boolean notifyChildFlowNodeHasFinished(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance parentInstance,\n            final SFlowNodeInstance childInstance) {\n        return true;\n    }\n\n    @Override\n    public int getId() {\n        return 16;\n    }\n\n    @Override\n    public String getName() {\n        return \"aborted\";\n    }\n\n    @Override\n    public boolean isStable() {\n        return true;\n    }\n\n    @Override\n    public boolean isTerminal() {\n        return true;\n    }\n\n    @Override\n    public SStateCategory getStateCategory() {\n        return SStateCategory.ABORTING;\n    }\n\n    @Override\n    public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return false;\n    }\n\n    @Override\n    public String getSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return \"\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/AbortingActivityWithBoundaryState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.api.states.StateCode;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class AbortingActivityWithBoundaryState implements FlowNodeState {\n\n    public AbortingActivityWithBoundaryState() {\n    }\n\n    @Override\n    public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return false;\n    }\n\n    @Override\n    public String getSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return \"\";\n    }\n\n    @Override\n    public int getId() {\n        return 35;\n    }\n\n    @Override\n    public String getName() {\n        return \"aborting activity with boundary\";\n    }\n\n    @Override\n    public SStateCategory getStateCategory() {\n        return SStateCategory.ABORTING;\n    }\n\n    public SStateCategory getBoundaryCategoryState() {\n        return SStateCategory.ABORTING;\n    }\n\n    @Override\n    public boolean shouldExecuteState(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance) {\n        return false;\n    }\n\n    @Override\n    public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance instance)\n            throws SActivityStateExecutionException {\n        return StateCode.DONE;\n    }\n\n    @Override\n    public boolean notifyChildFlowNodeHasFinished(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance parentInstance, final SFlowNodeInstance childInstance) {\n        return true;\n    }\n\n    @Override\n    public boolean isStable() {\n        return false;\n    }\n\n    @Override\n    public boolean isTerminal() {\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/AbortingBoundaryAndIntermediateCatchEventState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.execution.WaitingEventsInterrupter;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class AbortingBoundaryAndIntermediateCatchEventState extends InterruptingBoundaryAndIntermediateCatchEventState {\n\n    public AbortingBoundaryAndIntermediateCatchEventState(WaitingEventsInterrupter waitingEventsInterrupter) {\n        super(waitingEventsInterrupter);\n    }\n\n    @Override\n    public int getId() {\n        return 60;\n    }\n\n    @Override\n    public String getName() {\n        return \"aborting\";\n    }\n\n    @Override\n    public SStateCategory getStateCategory() {\n        return SStateCategory.ABORTING;\n    }\n\n    @Override\n    public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return false;\n    }\n\n    @Override\n    public String getSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return \"Aborting boundary or intermediate catch event\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/AbortingBoundaryEventsOnCompletingActivityState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport org.bonitasoft.engine.core.process.definition.model.SActivityDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.api.states.StateCode;\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.execution.StateBehaviors;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class AbortingBoundaryEventsOnCompletingActivityState implements FlowNodeState {\n\n    private final StateBehaviors stateBehaviors;\n\n    public AbortingBoundaryEventsOnCompletingActivityState(final StateBehaviors stateBehaviors) {\n        this.stateBehaviors = stateBehaviors;\n    }\n\n    @Override\n    public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return false;\n    }\n\n    @Override\n    public String getSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return \"\";\n    }\n\n    @Override\n    public int getId() {\n        return 34;\n    }\n\n    @Override\n    public String getName() {\n        // TODO: should be changed but has impacts client-side, as it is exposed client-side.\n        return \"completing activity with boundary\";\n    }\n\n    @Override\n    public SStateCategory getStateCategory() {\n        return SStateCategory.NORMAL;\n    }\n\n    @Override\n    public boolean shouldExecuteState(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance) {\n        final SActivityDefinition activityDef = (SActivityDefinition) processDefinition.getProcessContainer()\n                .getFlowNode(flowNodeInstance.getFlowNodeDefinitionId());\n        return !activityDef.getBoundaryEventDefinitions().isEmpty();\n    }\n\n    @Override\n    public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance instance)\n            throws SActivityStateExecutionException {\n        final SActivityDefinition activityDef = (SActivityDefinition) processDefinition.getProcessContainer()\n                .getFlowNode(instance.getFlowNodeDefinitionId());\n        if (!activityDef.getBoundaryEventDefinitions().isEmpty()) {\n            final SActivityInstance activityInstance = (SActivityInstance) instance;\n            stateBehaviors.interruptAttachedBoundaryEvent(processDefinition, activityInstance,\n                    SStateCategory.ABORTING);\n        }\n        return StateCode.DONE;\n    }\n\n    @Override\n    public boolean notifyChildFlowNodeHasFinished(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance parentInstance,\n            final SFlowNodeInstance childInstance) {\n        return true;\n    }\n\n    @Override\n    public boolean isStable() {\n        return false;\n    }\n\n    @Override\n    public boolean isTerminal() {\n        return false;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/AbortingCallActivityState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.execution.ProcessInstanceInterruptor;\nimport org.bonitasoft.engine.execution.archive.BPMArchiverService;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class AbortingCallActivityState extends EndingCallActivityExceptionState {\n\n    public AbortingCallActivityState(ProcessInstanceService processInstanceService,\n            ProcessInstanceInterruptor processInstanceInterruptor,\n            BPMArchiverService bpmArchiverService) {\n        super(processInstanceService, processInstanceInterruptor, bpmArchiverService);\n    }\n\n    @Override\n    public int getId() {\n        return 20;\n    }\n\n    @Override\n    public String getName() {\n        return \"aborting\";\n    }\n\n    @Override\n    public SStateCategory getStateCategory() {\n        return SStateCategory.ABORTING;\n    }\n\n    @Override\n    public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return false;\n    }\n\n    @Override\n    public String getSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return \"aborting call activity\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/AbortingFlowNodeContainerState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.execution.StateBehaviors;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class AbortingFlowNodeContainerState extends EndingFlowNodeContainerExceptionState {\n\n    public AbortingFlowNodeContainerState(final StateBehaviors stateBehaviors) {\n        super(stateBehaviors);\n    }\n\n    @Override\n    public int getId() {\n        return 15;\n    }\n\n    @Override\n    public String getName() {\n        return \"aborting\";\n    }\n\n    @Override\n    public SStateCategory getStateCategory() {\n        return SStateCategory.ABORTING;\n    }\n\n    @Override\n    public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return false;\n    }\n\n    @Override\n    public String getSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return \"\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/AbortingFlowNodeState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.api.states.StateCode;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class AbortingFlowNodeState implements FlowNodeState {\n\n    @Override\n    public boolean shouldExecuteState(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance) {\n        return false;\n    }\n\n    @Override\n    public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance instance) {\n        return StateCode.DONE;\n    }\n\n    @Override\n    public boolean notifyChildFlowNodeHasFinished(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance parentInstance,\n            final SFlowNodeInstance childInstance) {\n        return true;\n    }\n\n    @Override\n    public int getId() {\n        return 21;\n    }\n\n    @Override\n    public String getName() {\n        return \"aborting\";\n    }\n\n    @Override\n    public boolean isStable() {\n        return true;\n    }\n\n    @Override\n    public boolean isTerminal() {\n        return false;\n    }\n\n    @Override\n    public SStateCategory getStateCategory() {\n        return SStateCategory.ABORTING;\n    }\n\n    @Override\n    public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return false;\n    }\n\n    @Override\n    public String getSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return \"aborting flow node\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/AbortingReceiveTaskState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.states.StateCode;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance;\nimport org.bonitasoft.engine.execution.StateBehaviors;\nimport org.bonitasoft.engine.execution.WaitingEventsInterrupter;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class AbortingReceiveTaskState extends AbortingFlowNodeContainerState {\n\n    private final WaitingEventsInterrupter waitingEventsInterrupter;\n\n    public AbortingReceiveTaskState(final StateBehaviors stateBehaviors,\n            WaitingEventsInterrupter waitingEventsInterrupter) {\n        super(stateBehaviors);\n        this.waitingEventsInterrupter = waitingEventsInterrupter;\n    }\n\n    @Override\n    public int getId() {\n        return 38;\n    }\n\n    @Override\n    public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance instance)\n            throws SActivityStateExecutionException {\n        try {\n            final SReceiveTaskInstance receiveTaskInstance = (SReceiveTaskInstance) instance;\n            waitingEventsInterrupter.interruptWaitingEvents(receiveTaskInstance);\n        } catch (final SBonitaException e) {\n            throw new SActivityStateExecutionException(e);\n        }\n        return super.execute(processDefinition, instance);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/AbortingSubTaskState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.api.states.StateCode;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.execution.StateBehaviors;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class AbortingSubTaskState implements FlowNodeState {\n\n    private final StateBehaviors stateBehaviors;\n\n    public AbortingSubTaskState(final StateBehaviors stateBehaviors) {\n        super();\n        this.stateBehaviors = stateBehaviors;\n    }\n\n    @Override\n    public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) {\n        return StateCode.DONE;\n    }\n\n    @Override\n    public int getId() {\n        return 13;\n    }\n\n    @Override\n    public boolean isStable() {\n        return true;\n    }\n\n    @Override\n    public boolean isTerminal() {\n        return false;\n    }\n\n    @Override\n    public String getName() {\n        // TODO: should be changed but has impacts client-side, as it is exposed in\n        // org.bonitasoft.engine.bpm.flownode.ActivityStates.CANCELLING_SUBTASKS_STATE\n        return \"cancelling subtasks\";\n    }\n\n    @Override\n    public boolean notifyChildFlowNodeHasFinished(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance parentInstance,\n            final SFlowNodeInstance childInstance) {\n        final SHumanTaskInstance sHumanTaskInstance = (SHumanTaskInstance) parentInstance;\n        return sHumanTaskInstance.getTokenCount() == 0;\n    }\n\n    @Override\n    public boolean shouldExecuteState(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance) throws SActivityExecutionException {\n        final SHumanTaskInstance sHumanTaskInstance = (SHumanTaskInstance) flowNodeInstance;\n        final boolean hasTokens = sHumanTaskInstance.getTokenCount() > 0;\n        if (hasTokens) {\n            try {\n                stateBehaviors.interruptSubActivities(flowNodeInstance, SStateCategory.ABORTING);\n            } catch (final SBonitaException e) {\n                throw new SActivityExecutionException(e);\n            }\n        }\n        return hasTokens;\n    }\n\n    @Override\n    public SStateCategory getStateCategory() {\n        return SStateCategory.NORMAL;\n    }\n\n    @Override\n    public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return false;\n    }\n\n    @Override\n    public String getSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return \"\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/CancelledFlowNodeState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.api.states.StateCode;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class CancelledFlowNodeState implements FlowNodeState {\n\n    public CancelledFlowNodeState() {\n    }\n\n    @Override\n    public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance instance) {\n        return StateCode.DONE;\n    }\n\n    @Override\n    public int getId() {\n        return 14;\n    }\n\n    @Override\n    public String getName() {\n        return \"cancelled\";\n    }\n\n    @Override\n    public boolean isStable() {\n        return true;\n    }\n\n    @Override\n    public boolean isTerminal() {\n        return true;\n    }\n\n    @Override\n    public boolean shouldExecuteState(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance) {\n        return true;\n    }\n\n    @Override\n    public SStateCategory getStateCategory() {\n        return SStateCategory.CANCELLING;\n    }\n\n    @Override\n    public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return false;\n    }\n\n    @Override\n    public String getSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return \"\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/CancellingActivityWithBoundaryState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.api.states.StateCode;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class CancellingActivityWithBoundaryState implements FlowNodeState {\n\n    public CancellingActivityWithBoundaryState() {\n    }\n\n    @Override\n    public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return false;\n    }\n\n    @Override\n    public String getSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return \"\";\n    }\n\n    @Override\n    public int getId() {\n        return 36;\n    }\n\n    @Override\n    public String getName() {\n        return \"completing activity with boundary\";\n    }\n\n    @Override\n    public SStateCategory getStateCategory() {\n        return SStateCategory.CANCELLING;\n    }\n\n    public SStateCategory getBoundaryCategoryState() {\n        return SStateCategory.CANCELLING;\n    }\n\n    @Override\n    public boolean shouldExecuteState(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance) {\n        return false;\n    }\n\n    @Override\n    public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance instance)\n            throws SActivityStateExecutionException {\n        return StateCode.DONE;\n    }\n\n    @Override\n    public boolean notifyChildFlowNodeHasFinished(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance parentInstance, final SFlowNodeInstance childInstance) {\n        return true;\n    }\n\n    @Override\n    public boolean isStable() {\n        return false;\n    }\n\n    @Override\n    public boolean isTerminal() {\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/CancellingBoundaryAndIntermediateCatchEventState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.execution.WaitingEventsInterrupter;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class CancellingBoundaryAndIntermediateCatchEventState\n        extends InterruptingBoundaryAndIntermediateCatchEventState {\n\n    public CancellingBoundaryAndIntermediateCatchEventState(WaitingEventsInterrupter waitingEventsInterrupter) {\n        super(waitingEventsInterrupter);\n    }\n\n    @Override\n    public int getId() {\n        return 22;\n    }\n\n    @Override\n    public String getName() {\n        return \"cancelling\";\n    }\n\n    @Override\n    public SStateCategory getStateCategory() {\n        return SStateCategory.CANCELLING;\n    }\n\n    @Override\n    public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return false;\n    }\n\n    @Override\n    public String getSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return \"Canceling boundary or intermediate catch event\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/CancellingCallActivityState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.execution.ProcessInstanceInterruptor;\nimport org.bonitasoft.engine.execution.archive.BPMArchiverService;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class CancellingCallActivityState extends EndingCallActivityExceptionState {\n\n    public CancellingCallActivityState(ProcessInstanceService processInstanceService,\n            ProcessInstanceInterruptor processInstanceInterruptor,\n            BPMArchiverService bpmArchiverService) {\n        super(processInstanceService, processInstanceInterruptor, bpmArchiverService);\n    }\n\n    @Override\n    public int getId() {\n        return 19;\n    }\n\n    @Override\n    public String getName() {\n        return \"cancelling\";\n    }\n\n    @Override\n    public SStateCategory getStateCategory() {\n        return SStateCategory.CANCELLING;\n    }\n\n    @Override\n    public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return false;\n    }\n\n    @Override\n    public String getSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return \"canceling call activity\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/CancellingFlowNodeContainerChildrenState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.execution.StateBehaviors;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class CancellingFlowNodeContainerChildrenState extends EndingFlowNodeContainerExceptionState {\n\n    public CancellingFlowNodeContainerChildrenState(final StateBehaviors stateBehaviors) {\n        super(stateBehaviors);\n    }\n\n    @Override\n    public int getId() {\n        return 17;\n    }\n\n    @Override\n    public String getName() {\n        return \"cancelling\";\n    }\n\n    @Override\n    public SStateCategory getStateCategory() {\n        return SStateCategory.CANCELLING;\n    }\n\n    @Override\n    public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return false;\n    }\n\n    @Override\n    public String getSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return \"\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/CancellingFlowNodeState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.api.states.StateCode;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class CancellingFlowNodeState implements FlowNodeState {\n\n    public CancellingFlowNodeState() {\n    }\n\n    @Override\n    public boolean shouldExecuteState(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance) {\n        return false;\n    }\n\n    @Override\n    public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance instance) {\n        return StateCode.DONE;\n\n    }\n\n    @Override\n    public boolean notifyChildFlowNodeHasFinished(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance parentInstance,\n            final SFlowNodeInstance childInstance) {\n        return true;\n    }\n\n    @Override\n    public int getId() {\n        return 18;\n    }\n\n    @Override\n    public String getName() {\n        return \"cancelling\";\n    }\n\n    @Override\n    public boolean isStable() {\n        return true;\n    }\n\n    @Override\n    public boolean isTerminal() {\n        return false;\n    }\n\n    @Override\n    public SStateCategory getStateCategory() {\n        return SStateCategory.CANCELLING;\n    }\n\n    @Override\n    public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return false;\n    }\n\n    @Override\n    public String getSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return \"\";\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/CancellingReceiveTaskState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.states.StateCode;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance;\nimport org.bonitasoft.engine.execution.WaitingEventsInterrupter;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class CancellingReceiveTaskState extends CancellingActivityWithBoundaryState {\n\n    private final WaitingEventsInterrupter waitingEventsInterrupter;\n\n    public CancellingReceiveTaskState(WaitingEventsInterrupter waitingEventsInterrupter) {\n        this.waitingEventsInterrupter = waitingEventsInterrupter;\n    }\n\n    @Override\n    public int getId() {\n        return 39;\n    }\n\n    @Override\n    public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance instance)\n            throws SActivityStateExecutionException {\n        try {\n            final SReceiveTaskInstance receiveTaskInstance = (SReceiveTaskInstance) instance;\n            waitingEventsInterrupter.interruptWaitingEvents(receiveTaskInstance);\n        } catch (final SBonitaException e) {\n            throw new SActivityStateExecutionException(e);\n        }\n        return super.execute(processDefinition, instance);\n    }\n\n    @Override\n    public boolean shouldExecuteState(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance) {\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/CompletedActivityState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.api.states.StateCode;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class CompletedActivityState implements FlowNodeState {\n\n    public static final int ID = 2;\n\n    @Override\n    public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance activityInstance) {\n        return StateCode.DONE;\n        // this method is never called, use \"completing\" state\n    }\n\n    @Override\n    public int getId() {\n        return ID;\n    }\n\n    @Override\n    public boolean isStable() {\n        return true;\n    }\n\n    @Override\n    public boolean isTerminal() {\n        return true;\n    }\n\n    @Override\n    public String getName() {\n        return \"completed\";\n    }\n\n    @Override\n    public boolean shouldExecuteState(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance) {\n        return true;\n    }\n\n    @Override\n    public SStateCategory getStateCategory() {\n        return SStateCategory.NORMAL;\n    }\n\n    @Override\n    public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return flowNodeInstance instanceof SHumanTaskInstance;\n    }\n\n    @Override\n    public String getSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return \" User \" + flowNodeInstance.getExecutedBy() + \" has \" + getName() + \" task \"\n                + flowNodeInstance.getName();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/CompletingActivityState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.execution.StateBehaviors;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class CompletingActivityState extends OnFinishConnectorState {\n\n    protected final StateBehaviors stateBehaviors;\n\n    public CompletingActivityState(final StateBehaviors stateBehaviors) {\n        super(stateBehaviors);\n        this.stateBehaviors = stateBehaviors;\n    }\n\n    @Override\n    public int getId() {\n        return 9;\n    }\n\n    @Override\n    public String getName() {\n        return \"completing\";\n    }\n\n    @Override\n    public boolean isStable() {\n        return false;\n    }\n\n    @Override\n    public boolean isTerminal() {\n        return false;\n    }\n\n    @Override\n    public boolean shouldExecuteState(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance) {\n        return true;\n    }\n\n    @Override\n    public SStateCategory getStateCategory() {\n        return SStateCategory.NORMAL;\n    }\n\n    @Override\n    public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return false;\n    }\n\n    @Override\n    public String getSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return \"\";\n    }\n\n    @Override\n    protected void afterConnectors(SProcessDefinition processDefinition, SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException {\n        stateBehaviors.mapDataOutputOfMultiInstance(processDefinition, flowNodeInstance);\n        stateBehaviors.updateDisplayDescriptionAfterCompletion(processDefinition, flowNodeInstance);\n    }\n\n    @Override\n    protected void beforeConnectors(SProcessDefinition processDefinition, SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException {\n\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/CompletingCallActivityState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.ScopedException;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.OperationService;\nimport org.bonitasoft.engine.core.process.definition.model.SCallActivityDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException;\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.execution.StateBehaviors;\nimport org.bonitasoft.engine.execution.archive.BPMArchiverService;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class CompletingCallActivityState extends CompletingActivityState {\n\n    private final OperationService operationService;\n    private final ProcessInstanceService processInstanceService;\n    private final BPMArchiverService bpmArchiverService;\n\n    public CompletingCallActivityState(StateBehaviors stateBehaviors, OperationService operationService,\n            ProcessInstanceService processInstanceService,\n            BPMArchiverService bpmArchiverService) {\n        super(stateBehaviors);\n        this.operationService = operationService;\n        this.processInstanceService = processInstanceService;\n        this.bpmArchiverService = bpmArchiverService;\n    }\n\n    @Override\n    protected void beforeConnectors(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException {\n        executeDataOutputOperations(processDefinition, flowNodeInstance);\n        stateBehaviors.executeOperations(processDefinition, (SActivityInstance) flowNodeInstance);\n    }\n\n    private void executeDataOutputOperations(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance instance)\n            throws SActivityStateExecutionException {\n        final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer();\n        final SCallActivityDefinition callActivityDef = (SCallActivityDefinition) processContainer\n                .getFlowNode(instance.getFlowNodeDefinitionId());\n        try {\n            final SProcessInstance childProcInst = processInstanceService.getChildOfActivity(instance.getId());\n            final SExpressionContext expressionContext = new SExpressionContext(childProcInst.getId(),\n                    DataInstanceContainer.PROCESS_INSTANCE.name(),\n                    childProcInst.getProcessDefinitionId());\n            expressionContext.setParentProcessDefinitionId(instance.getProcessDefinitionId());\n            operationService.execute(callActivityDef.getDataOutputOperations(), instance.getId(),\n                    DataInstanceContainer.ACTIVITY_INSTANCE.name(),\n                    expressionContext);\n            // archive child process instance\n            bpmArchiverService.archiveAndDeleteProcessInstance(childProcInst);\n        } catch (final SBonitaException e) {\n            throw new SActivityStateExecutionException(\"Unable to map call activity output data\",\n                    ScopedException.OPERATION, e);\n        }\n    }\n\n    @Override\n    public int getId() {\n        return 30;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/EndingCallActivityExceptionState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.SArchivingException;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.api.states.StateCode;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.execution.ProcessInstanceInterruptor;\nimport org.bonitasoft.engine.execution.archive.BPMArchiverService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.springframework.stereotype.Component;\n\n@Slf4j\n@Component\npublic abstract class EndingCallActivityExceptionState implements FlowNodeState {\n\n    private final ProcessInstanceService processInstanceService;\n    private final ProcessInstanceInterruptor processInstanceInterruptor;\n    private final BPMArchiverService bpmArchiverService;\n\n    public EndingCallActivityExceptionState(ProcessInstanceService processInstanceService,\n            ProcessInstanceInterruptor processInstanceInterruptor,\n            BPMArchiverService bpmArchiverService) {\n        this.processInstanceService = processInstanceService;\n        this.processInstanceInterruptor = processInstanceInterruptor;\n        this.bpmArchiverService = bpmArchiverService;\n    }\n\n    @Override\n    public boolean shouldExecuteState(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance) throws SActivityExecutionException {\n        try {\n            final SProcessInstance targetProcessInstance = processInstanceService\n                    .getChildOfActivity(flowNodeInstance.getId());\n\n            final boolean hasActiveChild = processInstanceInterruptor\n                    .interruptProcessInstance(targetProcessInstance.getId(), getStateCategory());\n            log.debug(\"{} activity id {}, name {}   with active process : {} \", getStateCategory(),\n                    flowNodeInstance.getId(),\n                    flowNodeInstance.getName(),\n                    hasActiveChild);\n            if (!hasActiveChild) {\n                archiveChildProcessInstance(flowNodeInstance);\n            }\n            return hasActiveChild;\n        } catch (SProcessInstanceNotFoundException e) {\n            return false;\n        } catch (final SBonitaException e) {\n            throw new SActivityExecutionException(e);\n        }\n    }\n\n    @Override\n    public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance instance)\n            throws SActivityStateExecutionException {\n        // archive process target process instance\n        try {\n            archiveChildProcessInstance(instance);\n        } catch (final SBonitaException e) {\n            throw new SActivityStateExecutionException(\n                    \"Unable to found the process instance called by call activity with id \" + instance.getId(), e);\n        }\n        return StateCode.DONE;\n    }\n\n    private void archiveChildProcessInstance(final SFlowNodeInstance instance)\n            throws SArchivingException, SBonitaReadException {\n        try {\n            final SProcessInstance childProcInst = processInstanceService.getChildOfActivity(instance.getId());\n            bpmArchiverService.archiveAndDeleteProcessInstance(childProcInst);\n        } catch (SProcessInstanceNotFoundException ignored) {\n            log.warn(\"No target process instance found when archiving the call activity {}, in state {}\",\n                    instance.getId(), getName());\n        }\n    }\n\n    @Override\n    public boolean notifyChildFlowNodeHasFinished(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance parentInstance,\n            final SFlowNodeInstance childInstance) {\n        return true;\n    }\n\n    @Override\n    public boolean isStable() {\n        return true;\n    }\n\n    @Override\n    public boolean isTerminal() {\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/EndingFlowNodeContainerExceptionState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.api.states.StateCode;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.execution.StateBehaviors;\n\npublic abstract class EndingFlowNodeContainerExceptionState implements FlowNodeState {\n\n    private final StateBehaviors stateBehaviors;\n\n    public EndingFlowNodeContainerExceptionState(final StateBehaviors stateBehaviors) {\n        super();\n        this.stateBehaviors = stateBehaviors;\n    }\n\n    @Override\n    public boolean shouldExecuteState(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance) throws SActivityExecutionException {\n        if (flowNodeInstance.getTokenCount() > 0) {\n            try {\n                stateBehaviors.interruptSubActivities(flowNodeInstance, getStateCategory());\n            } catch (final SBonitaException e) {\n                throw new SActivityExecutionException(e);\n            }\n        }\n\n        return flowNodeInstance.getTokenCount() > 0;\n    }\n\n    @Override\n    public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance instance)\n            throws SActivityStateExecutionException {\n        return StateCode.DONE;\n    }\n\n    @Override\n    public boolean notifyChildFlowNodeHasFinished(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance parentInstance,\n            final SFlowNodeInstance childInstance) {\n        return parentInstance.getTokenCount() == 0;\n    }\n\n    @Override\n    public boolean isStable() {\n        return true;\n    }\n\n    @Override\n    public boolean isTerminal() {\n        return false;\n    }\n\n    public StateBehaviors getStateBehaviors() {\n        return stateBehaviors;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/ExecutingAutomaticActivityState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException;\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.execution.StateBehaviors;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class ExecutingAutomaticActivityState extends OnEnterAndFinishConnectorState {\n\n    private final StateBehaviors stateBehaviors;\n\n    public ExecutingAutomaticActivityState(final StateBehaviors stateBehaviors) {\n        super(stateBehaviors);\n        this.stateBehaviors = stateBehaviors;\n    }\n\n    @Override\n    protected void afterOnFinish(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException {\n        stateBehaviors.mapDataOutputOfMultiInstance(processDefinition, flowNodeInstance);\n        stateBehaviors.updateDisplayDescriptionAfterCompletion(processDefinition, flowNodeInstance);\n    }\n\n    @Override\n    protected void onEnterToOnFinish(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException {\n        final SActivityInstance activityInstance = (SActivityInstance) flowNodeInstance;\n        stateBehaviors.updateDisplayNameAndDescription(processDefinition, flowNodeInstance);\n        stateBehaviors.executeOperations(processDefinition, activityInstance);\n        stateBehaviors.handleThrowEvent(processDefinition, flowNodeInstance);\n    }\n\n    @Override\n    protected void beforeOnEnter(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException {\n        stateBehaviors.createData(processDefinition, flowNodeInstance);\n        stateBehaviors.createAttachedBoundaryEvents(processDefinition, (SActivityInstance) flowNodeInstance);\n    }\n\n    @Override\n    public int getId() {\n        return 37;\n    }\n\n    @Override\n    public boolean isStable() {\n        return false;\n    }\n\n    @Override\n    public boolean isTerminal() {\n        return false;\n    }\n\n    @Override\n    public String getName() {\n        return \"executing\";\n    }\n\n    @Override\n    public boolean shouldExecuteState(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance) {\n        return true;\n    }\n\n    @Override\n    public SStateCategory getStateCategory() {\n        return SStateCategory.NORMAL;\n    }\n\n    @Override\n    public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return false;\n    }\n\n    @Override\n    public String getSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return \"\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/ExecutingBoundaryEventState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.api.states.StateCode;\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance;\nimport org.bonitasoft.engine.execution.ContainerRegistry;\nimport org.bonitasoft.engine.execution.StateBehaviors;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class ExecutingBoundaryEventState implements FlowNodeState {\n\n    private final ActivityInstanceService activityInstanceService;\n\n    private final ContainerRegistry containerRegistry;\n\n    private final StateBehaviors stateBehaviors;\n\n    public ExecutingBoundaryEventState(ActivityInstanceService activityInstanceService,\n            ContainerRegistry containerRegistry,\n            StateBehaviors stateBehaviors) {\n        this.activityInstanceService = activityInstanceService;\n        this.containerRegistry = containerRegistry;\n        this.stateBehaviors = stateBehaviors;\n    }\n\n    @Override\n    public boolean shouldExecuteState(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance) {\n        return true;\n    }\n\n    @Override\n    public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return false;\n    }\n\n    @Override\n    public String getSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return \"executing boundary event\";\n    }\n\n    @Override\n    public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance boundary)\n            throws SActivityStateExecutionException {\n        final SBoundaryEventInstance boundaryEventInstance = (SBoundaryEventInstance) boundary;\n        if (boundaryEventInstance.isInterrupting()) {\n            abortRelatedActivity(boundaryEventInstance);\n            // Also abort the other boundary events on the same flow node instance:\n            try {\n                stateBehaviors.interruptAttachedBoundaryEvent(processDefinition,\n                        activityInstanceService.getActivityInstance(boundaryEventInstance.getActivityInstanceId()),\n                        SStateCategory.ABORTING);\n            } catch (SActivityInstanceNotFoundException | SActivityReadException e) {\n                throw new SActivityStateExecutionException(e);\n            }\n        }\n\n        return StateCode.DONE;\n    }\n\n    private void abortRelatedActivity(final SBoundaryEventInstance boundaryEventInstance)\n            throws SActivityStateExecutionException {\n        if (SStateCategory.NORMAL.equals(boundaryEventInstance.getStateCategory())) {\n            try {\n                final SActivityInstance relatedActivityInst = activityInstanceService\n                        .getActivityInstance(boundaryEventInstance.getActivityInstanceId());\n\n                activityInstanceService.setStateCategory(relatedActivityInst, SStateCategory.ABORTING);\n                activityInstanceService.setAbortedByBoundaryEvent(relatedActivityInst, boundaryEventInstance.getId());\n                containerRegistry\n                        .executeFlowNode(relatedActivityInst);\n            } catch (final SBonitaException e) {\n                throw new SActivityStateExecutionException(e);\n            }\n        }\n    }\n\n    @Override\n    public boolean notifyChildFlowNodeHasFinished(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance parentInstance,\n            final SFlowNodeInstance childInstance) {\n        return true;\n    }\n\n    @Override\n    public int getId() {\n        return 65;\n    }\n\n    @Override\n    public String getName() {\n        return \"executing\";\n    }\n\n    @Override\n    public boolean isStable() {\n        return false;\n    }\n\n    @Override\n    public boolean isTerminal() {\n        return false;\n    }\n\n    @Override\n    public SStateCategory getStateCategory() {\n        return SStateCategory.NORMAL;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/ExecutingCallActivityState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.api.states.StateCode;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.execution.StateBehaviors;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class ExecutingCallActivityState implements FlowNodeState {\n\n    private final StateBehaviors stateBehaviors;\n\n    public ExecutingCallActivityState(final StateBehaviors stateBehaviors) {\n        this.stateBehaviors = stateBehaviors;\n    }\n\n    @Override\n    public int getId() {\n        return 31;\n    }\n\n    @Override\n    public boolean isStable() {\n        return true;\n    }\n\n    @Override\n    public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance instance)\n            throws SActivityStateExecutionException {\n        stateBehaviors.mapDataOutputOfMultiInstance(processDefinition, instance);\n        stateBehaviors.updateDisplayDescriptionAfterCompletion(processDefinition, instance);\n        return StateCode.DONE;\n    }\n\n    @Override\n    public boolean isTerminal() {\n        return false;\n    }\n\n    @Override\n    public String getName() {\n        return \"executing\";\n    }\n\n    @Override\n    public boolean shouldExecuteState(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance) {\n        return true;\n    }\n\n    @Override\n    public SStateCategory getStateCategory() {\n        return SStateCategory.NORMAL;\n    }\n\n    @Override\n    public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return false;\n    }\n\n    @Override\n    public String getSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return \"\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/ExecutingFlowNodeState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.execution.StateBehaviors;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class ExecutingFlowNodeState extends OnFinishConnectorState {\n\n    private StateBehaviors stateBehaviors;\n\n    public ExecutingFlowNodeState(final StateBehaviors stateBehaviors) {\n        super(stateBehaviors);\n        this.stateBehaviors = stateBehaviors;\n    }\n\n    @Override\n    public int getId() {\n        return ID_ACTIVITY_EXECUTING;\n    }\n\n    @Override\n    public boolean isStable() {\n        return false;\n    }\n\n    @Override\n    public boolean isTerminal() {\n        return false;\n    }\n\n    @Override\n    public String getName() {\n        return \"executing\";\n    }\n\n    @Override\n    public boolean shouldExecuteState(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance) {\n        return true;\n    }\n\n    @Override\n    public SStateCategory getStateCategory() {\n        return SStateCategory.NORMAL;\n    }\n\n    @Override\n    public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return false;\n    }\n\n    @Override\n    public String getSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return \"\";\n    }\n\n    @Override\n    protected void beforeConnectors(SProcessDefinition processDefinition, SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException {\n\n    }\n\n    @Override\n    protected void afterConnectors(SProcessDefinition processDefinition, SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException {\n        stateBehaviors.updateDisplayDescriptionAfterCompletion(processDefinition, flowNodeInstance);\n\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/ExecutingLoopActivityState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.model.impl.BPMInstancesCreator;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.ScopedException;\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.process.definition.model.SActivityDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SStandardLoopCharacteristics;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.api.states.StateCode;\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowElementsContainerType;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SLoopActivityInstanceBuilderFactory;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.execution.ContainerRegistry;\nimport org.bonitasoft.engine.expression.ExpressionConstants;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class ExecutingLoopActivityState implements FlowNodeState {\n\n    private final ExpressionResolverService expressionResolverService;\n\n    private final BPMInstancesCreator bpmInstancesCreator;\n\n    private final ContainerRegistry containerRegistry;\n\n    private final ActivityInstanceService activityInstanceService;\n\n    public ExecutingLoopActivityState(final ExpressionResolverService expressionResolverService,\n            final BPMInstancesCreator bpmInstancesCreator,\n            final ContainerRegistry containerRegistry, final ActivityInstanceService activityInstanceService) {\n        this.expressionResolverService = expressionResolverService;\n        this.bpmInstancesCreator = bpmInstancesCreator;\n        this.containerRegistry = containerRegistry;\n        this.activityInstanceService = activityInstanceService;\n    }\n\n    @Override\n    public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) {\n        return StateCode.DONE;\n    }\n\n    @Override\n    public int getId() {\n        return 24;\n    }\n\n    @Override\n    public boolean isStable() {\n        return true;\n    }\n\n    @Override\n    public boolean isTerminal() {\n        return false;\n    }\n\n    @Override\n    public String getName() {\n        return \"executing\";\n    }\n\n    @Override\n    public boolean notifyChildFlowNodeHasFinished(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance parentInstance,\n            final SFlowNodeInstance childInstance)\n            throws SActivityStateExecutionException {\n        try {\n            final SLoopActivityInstance loopActivity = (SLoopActivityInstance) activityInstanceService\n                    .getFlowNodeInstance(parentInstance.getId());// get it\n            if (loopActivity.getStateCategory() != SStateCategory.NORMAL) {\n                // if is not a normal state (aborting / canceling), return true to change state from executing to aborting / cancelling (ChildReadstate),\n                // without create a new child task\n                return true;\n            }\n            final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer();\n            final SActivityDefinition activity = (SActivityDefinition) processContainer\n                    .getFlowNode(parentInstance.getFlowNodeDefinitionId());\n            final SStandardLoopCharacteristics loopCharacteristics = (SStandardLoopCharacteristics) activity\n                    .getLoopCharacteristics();\n            boolean loop = false;\n            final int loopCounter = loopActivity.getLoopCounter();\n            if (loopActivity.getLoopMax() > 0 && loopCounter >= loopActivity.getLoopMax()) {\n                return true;\n            }\n\n            final SStandardLoopCharacteristics standardLoop = loopCharacteristics;\n            final Map<String, Object> input = new HashMap<>(1);\n            input.put(ExpressionConstants.LOOP_COUNTER.getEngineConstantName(), loopActivity.getLoopCounter());\n            final SExpressionContext sExpressionContext = new SExpressionContext(loopActivity.getId(),\n                    DataInstanceContainer.ACTIVITY_INSTANCE.name(),\n                    loopActivity.getProcessDefinitionId(), input);\n            loop = (Boolean) expressionResolverService.evaluate(standardLoop.getLoopCondition(), sExpressionContext);\n            if (loop) {\n                final SLoopActivityInstanceBuilderFactory keyProvider = BuilderFactory\n                        .get(SLoopActivityInstanceBuilderFactory.class);\n                final long rootProcessInstanceId = parentInstance\n                        .getLogicalGroup(keyProvider.getRootProcessInstanceIndex());\n                final long parentProcessInstanceId = parentInstance\n                        .getLogicalGroup(keyProvider.getParentProcessInstanceIndex());\n                final SFlowNodeInstance child = bpmInstancesCreator.createFlowNodeInstance(processDefinition.getId(),\n                        parentInstance.getRootContainerId(),\n                        parentInstance.getId(), SFlowElementsContainerType.FLOWNODE, activity, rootProcessInstanceId,\n                        parentProcessInstanceId, true,\n                        loopCounter + 1, SStateCategory.NORMAL, -1);\n                activityInstanceService.incrementLoopCounter(loopActivity);\n                activityInstanceService.setTokenCount(loopActivity, loopActivity.getTokenCount() + 1);\n                containerRegistry.executeFlowNode(child);\n            }\n            return !loop;\n        } catch (final SBonitaException e) {\n            throw new SActivityStateExecutionException(\"Unable to handle loop activity\", ScopedException.ITERATION, e);\n        }\n    }\n\n    @Override\n    public boolean shouldExecuteState(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance) throws SActivityExecutionException {\n        List<SActivityInstance> childrenOfAnActivity;\n        try {\n            childrenOfAnActivity = activityInstanceService.getChildrenOfAnActivity(flowNodeInstance.getId(), 0, 1);\n            if (!childrenOfAnActivity.isEmpty()) {\n                containerRegistry.executeFlowNode(childrenOfAnActivity.get(0));\n            }\n            return !childrenOfAnActivity.isEmpty();\n        } catch (final SBonitaException e) {\n            throw new SActivityExecutionException(e);\n        }\n    }\n\n    @Override\n    public SStateCategory getStateCategory() {\n        return SStateCategory.NORMAL;\n    }\n\n    @Override\n    public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return false;\n    }\n\n    @Override\n    public String getSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return \"\";\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/ExecutingMultiInstanceActivityState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport static org.bonitasoft.engine.core.process.instance.model.SStateCategory.ABORTING;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.process.definition.model.SActivityDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SMultiInstanceLoopCharacteristics;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.api.states.StateCode;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.execution.ContainerRegistry;\nimport org.bonitasoft.engine.execution.StateBehaviors;\nimport org.bonitasoft.engine.expression.ExpressionConstants;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.springframework.stereotype.Component;\n\n@Slf4j\n@Component\npublic class ExecutingMultiInstanceActivityState implements FlowNodeState {\n\n    private final ExpressionResolverService expressionResolverService;\n\n    private final ContainerRegistry containerRegistry;\n\n    private final ActivityInstanceService activityInstanceService;\n\n    private final StateBehaviors stateBehaviors;\n\n    public ExecutingMultiInstanceActivityState(final ExpressionResolverService expressionResolverService,\n            final ContainerRegistry containerRegistry,\n            final ActivityInstanceService activityInstanceService, final StateBehaviors stateBehaviors) {\n        this.expressionResolverService = expressionResolverService;\n        this.containerRegistry = containerRegistry;\n        this.activityInstanceService = activityInstanceService;\n        this.stateBehaviors = stateBehaviors;\n    }\n\n    @Override\n    public int getId() {\n        return 28;\n    }\n\n    @Override\n    public boolean isStable() {\n        return true;\n    }\n\n    @Override\n    public boolean isTerminal() {\n        return false;\n    }\n\n    @Override\n    public String getName() {\n        return \"executing\";\n    }\n\n    @Override\n    public boolean notifyChildFlowNodeHasFinished(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance parentInstance,\n            final SFlowNodeInstance childInstance)\n            throws SActivityStateExecutionException {\n        final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer();\n        final SActivityDefinition activityDefinition = (SActivityDefinition) processContainer\n                .getFlowNode(parentInstance.getFlowNodeDefinitionId());\n        final SMultiInstanceLoopCharacteristics loopCharacteristics = (SMultiInstanceLoopCharacteristics) activityDefinition\n                .getLoopCharacteristics();\n\n        try {\n            if (parentInstance.getStateCategory() != SStateCategory.NORMAL) {\n                // if is not a normal state (aborting / canceling), return true to change state from executing to aborting / cancelling (ChildReachstate),\n                // without create a new child task\n                return true;\n            }\n\n            final SMultiInstanceActivityInstance parentMultiInstance = (SMultiInstanceActivityInstance) parentInstance;\n            final String stateCategory;\n            if (childInstance.isAborting() || childInstance.isCanceling()) {\n                activityInstanceService.addMultiInstanceNumberOfTerminatedActivities(parentMultiInstance, 1);\n                stateCategory = \"terminated\";\n            } else {\n                activityInstanceService.addMultiInstanceNumberOfCompletedActivities(parentMultiInstance, 1);\n                stateCategory = \"completed\";\n                // check the completionCondition\n                final SExpression completionCondition = loopCharacteristics.getCompletionCondition();\n                final Map<String, Object> input = new HashMap<>(1);\n                input.put(ExpressionConstants.NUMBER_OF_ACTIVE_INSTANCES.getEngineConstantName(),\n                        parentMultiInstance.getNumberOfActiveInstances());\n                input.put(ExpressionConstants.NUMBER_OF_TERMINATED_INSTANCES.getEngineConstantName(),\n                        parentMultiInstance.getNumberOfTerminatedInstances());\n                input.put(ExpressionConstants.NUMBER_OF_COMPLETED_INSTANCES.getEngineConstantName(),\n                        parentMultiInstance.getNumberOfCompletedInstances());\n                final int numberOfInstances = parentMultiInstance.getNumberOfInstances();\n                input.put(ExpressionConstants.NUMBER_OF_INSTANCES.getEngineConstantName(), numberOfInstances);\n                final SExpressionContext sExpressionContext = new SExpressionContext(parentMultiInstance.getId(),\n                        DataInstanceContainer.ACTIVITY_INSTANCE.name(),\n                        processDefinition.getId(), input);\n                sExpressionContext.setProcessDefinitionId(parentMultiInstance.getProcessDefinitionId());\n                if (completionCondition != null) {\n                    final boolean complete = (Boolean) expressionResolverService.evaluate(completionCondition,\n                            sExpressionContext);\n                    if (complete) {\n                        stateBehaviors.interruptSubActivities(parentMultiInstance, ABORTING);\n                        if (parentMultiInstance.isSequential()) {\n                            return true;\n                        }\n                    }\n                }\n            }\n            final int numberOfActiveInstances = parentMultiInstance.getNumberOfActiveInstances();\n            final int numberOfCompletedInstances = parentMultiInstance.getNumberOfCompletedInstances();\n            final int numberOfTerminatedInstances = parentMultiInstance.getNumberOfTerminatedInstances();\n            final int numberOfInstances = parentMultiInstance.getNumberOfInstances();\n            log.debug(\"Multi-instance {} after child '{}' (id={}) notification ({}): \"\n                    + \"active={}, completed+terminated={}/{}\",\n                    parentMultiInstance, childInstance.getName(), childInstance.getId(),\n                    stateCategory, numberOfActiveInstances,\n                    numberOfCompletedInstances + numberOfTerminatedInstances, numberOfInstances);\n            if (parentMultiInstance.isSequential()) {\n                // only instantiate when we are in sequence\n                List<SFlowNodeInstance> createInnerInstances = null;\n                if (stateBehaviors.shouldCreateANewInstance(loopCharacteristics, numberOfInstances,\n                        parentMultiInstance)) {\n                    createInnerInstances = stateBehaviors.createInnerInstances(processDefinition.getId(),\n                            activityDefinition, parentMultiInstance, 1);\n                    for (final SFlowNodeInstance sFlowNodeInstance : createInnerInstances) {\n                        containerRegistry.executeFlowNode(sFlowNodeInstance);\n                    }\n                }\n                return numberOfActiveInstances == 0\n                        && (createInnerInstances == null || createInnerInstances.isEmpty());\n            }\n            return numberOfActiveInstances == 0\n                    || numberOfInstances == numberOfCompletedInstances + numberOfTerminatedInstances;\n        } catch (final SBonitaException e) {\n            throw new SActivityStateExecutionException(e);\n        }\n    }\n\n    @Override\n    public boolean shouldExecuteState(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance) throws SActivityExecutionException {\n        final int numberOfActiveInstances = ((SMultiInstanceActivityInstance) flowNodeInstance)\n                .getNumberOfActiveInstances();\n        if (numberOfActiveInstances > 0) {\n            stateBehaviors.executeChildrenActivities(flowNodeInstance);\n        }\n        return numberOfActiveInstances > 0;\n    }\n\n    @Override\n    public final StateCode execute(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance) {\n        return StateCode.DONE;\n    }\n\n    @Override\n    public SStateCategory getStateCategory() {\n        return SStateCategory.NORMAL;\n    }\n\n    @Override\n    public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return false;\n    }\n\n    @Override\n    public String getSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return \"\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/ExecutingThrowEventState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.execution.StateBehaviors;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class ExecutingThrowEventState extends OnEnterAndFinishConnectorState {\n\n    private final StateBehaviors stateBehaviors;\n\n    public ExecutingThrowEventState(final StateBehaviors stateBehaviors) {\n        super(stateBehaviors);\n        this.stateBehaviors = stateBehaviors;\n    }\n\n    @Override\n    public int getId() {\n        return 26;\n    }\n\n    @Override\n    public boolean isStable() {\n        return false;\n    }\n\n    @Override\n    public boolean isTerminal() {\n        return false;\n    }\n\n    @Override\n    public String getName() {\n        return \"executing\";\n    }\n\n    @Override\n    public boolean shouldExecuteState(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance) {\n        return true;\n    }\n\n    @Override\n    public SStateCategory getStateCategory() {\n        return SStateCategory.NORMAL;\n    }\n\n    @Override\n    public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return false;\n    }\n\n    @Override\n    public String getSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return \"\";\n    }\n\n    @Override\n    protected void beforeOnEnter(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException {\n        stateBehaviors.createData(processDefinition, flowNodeInstance);\n    }\n\n    @Override\n    protected void onEnterToOnFinish(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException {\n        final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer();\n        stateBehaviors.updateDisplayNameAndDescription(processDefinition, flowNodeInstance);\n        stateBehaviors.mapActors(flowNodeInstance, processContainer);\n        stateBehaviors.handleThrowEvent(processDefinition, flowNodeInstance);\n    }\n\n    @Override\n    protected void afterOnFinish(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException {\n        stateBehaviors.updateDisplayDescriptionAfterCompletion(processDefinition, flowNodeInstance);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/FailedActivityState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.api.states.StateCode;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class FailedActivityState implements FlowNodeState {\n\n    @Override\n    public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance instance)\n            throws SActivityStateExecutionException {\n        return StateCode.DONE;\n    }\n\n    @Override\n    public int getId() {\n        return ID_ACTIVITY_FAILED;\n    }\n\n    @Override\n    public boolean isStable() {\n        return true;\n    }\n\n    @Override\n    public boolean isTerminal() {\n        return false;\n    }\n\n    @Override\n    public String getName() {\n        return \"failed\";\n    }\n\n    @Override\n    public boolean shouldExecuteState(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance) {\n        return true;\n    }\n\n    @Override\n    public SStateCategory getStateCategory() {\n        // FIXME: create failing category?\n        return SStateCategory.NORMAL;\n    }\n\n    @Override\n    public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return false;\n    }\n\n    @Override\n    public String getSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return \"\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/FlowNodeStateManager.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport java.util.Set;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.execution.StateBehaviors;\n\n/**\n * @author Baptiste Mesta\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic interface FlowNodeStateManager {\n\n    FlowNodeState getNextState(SProcessDefinition processDefinition, SFlowNodeInstance flowNodeInstance,\n            int currentStateId)\n            throws SActivityExecutionException;\n\n    FlowNodeState getState(int stateId);\n\n    Set<String> getSupportedState(SFlowNodeType nodeType);\n\n    StateBehaviors getStateBehaviors();\n\n    public FlowNodeState getFirstState(SFlowNodeType nodeType);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/InitializingActivityState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.execution.StateBehaviors;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class InitializingActivityState extends OnEnterConnectorState {\n\n    private final StateBehaviors stateBehaviors;\n\n    public InitializingActivityState(final StateBehaviors stateBehaviors) {\n        super(stateBehaviors);\n        this.stateBehaviors = stateBehaviors;\n    }\n\n    @Override\n    public int getId() {\n        return 0;\n    }\n\n    @Override\n    public boolean isStable() {\n        return false;\n    }\n\n    @Override\n    public boolean isTerminal() {\n        return false;\n    }\n\n    @Override\n    public String getName() {\n        return \"initializing\";\n    }\n\n    @Override\n    public boolean shouldExecuteState(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance) {\n        return true;\n    }\n\n    @Override\n    public SStateCategory getStateCategory() {\n        return SStateCategory.NORMAL;\n    }\n\n    @Override\n    public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return false;\n    }\n\n    @Override\n    public String getSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return \"\";\n    }\n\n    @Override\n    protected void beforeConnectors(SProcessDefinition processDefinition, SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException {\n        stateBehaviors.createData(processDefinition, flowNodeInstance);\n        stateBehaviors.mapActors(flowNodeInstance, processDefinition.getProcessContainer());\n    }\n\n    @Override\n    protected void afterConnectors(SProcessDefinition processDefinition, SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException {\n        stateBehaviors.updateDisplayNameAndDescription(processDefinition, flowNodeInstance);\n        stateBehaviors.updateExpectedDuration(processDefinition, flowNodeInstance);\n        stateBehaviors.registerWaitingEvent(processDefinition, flowNodeInstance);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/InitializingActivityWithBoundaryEventsState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.function.Supplier;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.ScopedException;\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SCallActivityDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException;\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\nimport org.bonitasoft.engine.execution.ProcessExecutor;\nimport org.bonitasoft.engine.execution.StateBehaviors;\nimport org.bonitasoft.engine.execution.work.BPMWorkFactory;\nimport org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.mdc.MDCConstants;\nimport org.bonitasoft.engine.mdc.MDCHelper;\nimport org.bonitasoft.engine.mdc.ProcessInstanceMDC;\nimport org.bonitasoft.engine.transaction.BonitaTransactionSynchronization;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.bonitasoft.engine.work.SWorkRegisterException;\nimport org.bonitasoft.engine.work.WorkService;\nimport org.slf4j.MDC;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Component;\n\n@Component\n@Slf4j\npublic class InitializingActivityWithBoundaryEventsState extends OnEnterConnectorState {\n\n    private final StateBehaviors stateBehaviors;\n    private final ExpressionResolverService expressionResolverService;\n    private ProcessExecutor processExecutor;\n    private final ActivityInstanceService activityInstanceService;\n    private final ProcessDefinitionService processDefinitionService;\n    private final UserTransactionService userTransactionService;\n    private final WorkService workService;\n    private final BPMWorkFactory workFactory;\n\n    public InitializingActivityWithBoundaryEventsState(final StateBehaviors stateBehaviors,\n            ExpressionResolverService expressionResolverService,\n            ActivityInstanceService activityInstanceService,\n            ProcessDefinitionService processDefinitionService,\n            UserTransactionService userTransactionService,\n            WorkService workService,\n            BPMWorkFactory workFactory) {\n        super(stateBehaviors);\n        this.stateBehaviors = stateBehaviors;\n        this.expressionResolverService = expressionResolverService;\n        this.activityInstanceService = activityInstanceService;\n        this.processDefinitionService = processDefinitionService;\n        this.userTransactionService = userTransactionService;\n        this.workService = workService;\n        this.workFactory = workFactory;\n    }\n\n    //FIXME There is responsibility issue the circular dependencies must be fixed next time.\n    @Autowired\n    public void setProcessExecutor(@Lazy @Qualifier(\"processExecutor\") ProcessExecutor processExecutor) {\n        this.processExecutor = processExecutor;\n    }\n\n    public ProcessExecutor getProcessExecutor() {\n        return processExecutor;\n    }\n\n    @Override\n    public int getId() {\n        return 32;\n    }\n\n    @Override\n    public boolean isStable() {\n        return false;\n    }\n\n    @Override\n    public boolean isTerminal() {\n        return false;\n    }\n\n    @Override\n    public String getName() {\n        return \"initializing\";\n    }\n\n    @Override\n    public boolean shouldExecuteState(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance) {\n        return true;\n    }\n\n    @Override\n    public SStateCategory getStateCategory() {\n        return SStateCategory.NORMAL;\n    }\n\n    @Override\n    public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return false;\n    }\n\n    @Override\n    public String getSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return \"\";\n    }\n\n    @Override\n    protected void beforeConnectors(SProcessDefinition processDefinition, SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException {\n        stateBehaviors.createData(processDefinition, flowNodeInstance);\n        stateBehaviors.createAttachedBoundaryEvents(processDefinition, (SActivityInstance) flowNodeInstance);\n        stateBehaviors.mapActors(flowNodeInstance, processDefinition.getProcessContainer());\n    }\n\n    @Override\n    protected void afterConnectors(SProcessDefinition processDefinition, SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException {\n        stateBehaviors.updateDisplayNameAndDescription(processDefinition, flowNodeInstance);\n        stateBehaviors.updateExpectedDuration(processDefinition, flowNodeInstance);\n        stateBehaviors.addAssignmentSystemCommentIfTaskWasAutoAssign(flowNodeInstance);\n        handleCallActivity(processDefinition, flowNodeInstance);\n        stateBehaviors.registerWaitingEvent(processDefinition, flowNodeInstance);\n    }\n\n    private void handleCallActivity(SProcessDefinition processDefinition, SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException {\n        if (SFlowNodeType.CALL_ACTIVITY.equals(flowNodeInstance.getType())) {\n            final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer();\n            try {\n                final SCallActivityDefinition callActivity = (SCallActivityDefinition) processContainer\n                        .getFlowNode(flowNodeInstance.getFlowNodeDefinitionId());\n                if (callActivity == null) {\n                    throw new SActivityStateExecutionException(\"Unable to find call activity definition with name \"\n                            + flowNodeInstance.getName() + \" in process definition \" + processDefinition.getId());\n                }\n\n                final SExpressionContext expressionContext = new SExpressionContext(flowNodeInstance.getId(),\n                        DataInstanceContainer.ACTIVITY_INSTANCE.name(), processDefinition.getId());\n                final String callableElement = (String) expressionResolverService\n                        .evaluate(callActivity.getCallableElement(), expressionContext);\n                String callableElementVersion = null;\n                if (callActivity.getCallableElementVersion() != null) {\n                    callableElementVersion = (String) expressionResolverService\n                            .evaluate(callActivity.getCallableElementVersion(), expressionContext);\n                }\n\n                final long targetProcessDefinitionId = getTargetProcessDefinitionId(callableElement,\n                        callableElementVersion);\n                SProcessInstance sProcessInstance = instantiateProcess(processDefinition, callActivity,\n                        flowNodeInstance, targetProcessDefinitionId);\n\n                final SCallActivityInstance callActivityInstance = (SCallActivityInstance) flowNodeInstance;\n                // update token count\n                if (sProcessInstance.getStateId() != ProcessInstanceState.COMPLETED.getId()) {\n                    activityInstanceService.setTokenCount(callActivityInstance,\n                            callActivityInstance.getTokenCount() + 1);\n                } else {\n                    log.warn(\"Call activity {} with id {} started the process {} that is already completed. \" +\n                            \"This might due to an empty process or a process without 'NONE' start event.\",\n                            callActivityInstance.getName(),\n                            callActivityInstance.getId(),\n                            sProcessInstance.getId());\n                    // This need to be done \"later\" we register a work to execute this call activity because it is in fact finish\n                    // we can't do it right now because its state is not yet changed and the work would fail.\n                    // FIXME: it 's not normal to handle this here , it was added to fix the bug https://bonitasoft.atlassian.net/browse/BS-11654.\n                    // It's happens because the start process do too much things, we need to provide a new way that handle start process with work, like flow node handling\n                    // this is covered by integration CallActivityIT should_complete_call_activity_when_target_is_empty\n                    // this situation happens when the user do an error of designing process and we should almost to log a warning message to inform the user.\n                    userTransactionService.registerBonitaSynchronization(new BonitaTransactionSynchronization() {\n\n                        @Override\n                        public void beforeCompletion() {\n                            //the called process is finished, next step is stable so we trigger execution of this flownode\n                            try {\n                                workService.registerWork(\n                                        workFactory.createExecuteFlowNodeWorkDescriptor(flowNodeInstance));\n                            } catch (SWorkRegisterException e) {\n                                throw new BonitaRuntimeException(e);\n                            }\n                        }\n\n                        @Override\n                        public void afterCompletion(final int txState) {\n                        }\n                    });\n                }\n            } catch (final SBonitaException e) {\n                throw new SActivityStateExecutionException(\"Unable to handle call activity\", ScopedException.DATA, e);\n            }\n        }\n    }\n\n    protected SProcessInstance instantiateProcess(final SProcessDefinition callerProcessDefinition,\n            final SCallActivityDefinition callActivityDefinition,\n            final SFlowNodeInstance callActivityInstance, final long targetProcessDefinitionId)\n            throws SBonitaException {\n        final long callerProcessDefinitionId = callerProcessDefinition.getId();\n        final long callerId = callActivityInstance.getId();\n        final SExpressionContext context = new SExpressionContext(callerId,\n                DataInstanceContainer.ACTIVITY_INSTANCE.name(), callerProcessDefinitionId);\n\n        /*\n         * We are already in the context of the call activity, so the new MDC will conflict with current one.\n         */\n        var parentContext = MDC.getCopyOfContextMap();\n        long rootProcessInstanceId = Optional.ofNullable(parentContext)\n                .map(c -> c.get(MDCConstants.ROOT_PROCESS_INSTANCE_ID)).filter(Objects::nonNull).map(Long::valueOf)\n                .orElse(0L);\n        // AbstractMDC will temporarily erase conflicting values\n        Supplier<ProcessInstanceMDC> processInstanceMDC = () -> new ProcessInstanceMDC(0, Optional.empty(),\n                Optional.empty(), targetProcessDefinitionId, Long.valueOf(rootProcessInstanceId));\n        return MDCHelper.tryWithMDC(processInstanceMDC, () -> {\n            final Map<String, Serializable> processInputs = evaluateContractInputExpression(\n                    callActivityDefinition.getProcessStartContractInputs(), context);\n\n            return processExecutor\n                    .start(targetProcessDefinitionId, -1, 0, 0, context,\n                            callActivityDefinition.getDataInputOperations(),\n                            callerId, -1, processInputs);\n        });\n    }\n\n    protected Map<String, Serializable> evaluateContractInputExpression(Map<String, SExpression> contractInputs,\n            SExpressionContext context)\n            throws SBonitaException {\n        final Map<SExpression, Serializable> evaluationResults = evaluateExpressions(contractInputs, context);\n        final Map<String, Serializable> inputExpressionsMap = new HashMap<>(contractInputs.size());\n        for (Map.Entry<String, SExpression> entry : contractInputs.entrySet()) {\n            inputExpressionsMap.put(entry.getKey(), evaluationResults.get(entry.getValue()));\n        }\n        return inputExpressionsMap;\n    }\n\n    private Map<SExpression, Serializable> evaluateExpressions(Map<String, SExpression> contractInputs,\n            SExpressionContext context) throws SExpressionTypeUnknownException, SExpressionEvaluationException,\n            SExpressionDependencyMissingException, SInvalidExpressionException {\n        List<SExpression> expressionList = new ArrayList<>(contractInputs.values());\n        final List<Object> exprResults = expressionResolverService.evaluate(expressionList, context);\n        final Map<SExpression, Serializable> evaluationResults = new HashMap<>(contractInputs.size());\n        for (int i = 0; i < expressionList.size(); i++) {\n            evaluationResults.put(expressionList.get(i), (Serializable) exprResults.get(i));\n        }\n        return evaluationResults;\n    }\n\n    private long getTargetProcessDefinitionId(final String callableElement, final String callableElementVersion)\n            throws SBonitaException {\n        if (callableElementVersion != null) {\n            return processDefinitionService.getProcessDefinitionId(callableElement, callableElementVersion);\n        }\n        return processDefinitionService.getLatestProcessDefinitionId(callableElement);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/InitializingAndExecutingFlowNodeState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.execution.StateBehaviors;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class InitializingAndExecutingFlowNodeState extends OnEnterAndFinishConnectorState {\n\n    public static final int ID = 61;\n    private final StateBehaviors stateBehaviors;\n\n    public InitializingAndExecutingFlowNodeState(final StateBehaviors stateBehaviors) {\n        super(stateBehaviors);\n        this.stateBehaviors = stateBehaviors;\n    }\n\n    @Override\n    public int getId() {\n        return ID;\n    }\n\n    @Override\n    public boolean isStable() {\n        return false;\n    }\n\n    @Override\n    public boolean isTerminal() {\n        return false;\n    }\n\n    @Override\n    public String getName() {\n        return \"executing\";\n    }\n\n    @Override\n    public boolean shouldExecuteState(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance) {\n        return true;\n    }\n\n    @Override\n    public SStateCategory getStateCategory() {\n        return SStateCategory.NORMAL;\n    }\n\n    @Override\n    public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return false;\n    }\n\n    @Override\n    public String getSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return \"\";\n    }\n\n    @Override\n    protected void beforeOnEnter(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException {\n\n        stateBehaviors.createData(processDefinition, flowNodeInstance);\n        stateBehaviors.mapActors(flowNodeInstance, processDefinition.getProcessContainer());\n    }\n\n    @Override\n    protected void onEnterToOnFinish(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException {\n        stateBehaviors.updateDisplayNameAndDescription(processDefinition, flowNodeInstance);\n    }\n\n    @Override\n    protected void afterOnFinish(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException {\n        stateBehaviors.updateDisplayDescriptionAfterCompletion(processDefinition, flowNodeInstance);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/InitializingBoundaryEventState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance;\nimport org.bonitasoft.engine.execution.StateBehaviors;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class InitializingBoundaryEventState extends OnEnterConnectorState {\n\n    private final StateBehaviors stateBehaviors;\n\n    public InitializingBoundaryEventState(final StateBehaviors stateBehaviors) {\n        super(stateBehaviors);\n        this.stateBehaviors = stateBehaviors;\n    }\n\n    @Override\n    public int getId() {\n        return 33;\n    }\n\n    @Override\n    public boolean isStable() {\n        return false;\n    }\n\n    @Override\n    public boolean isTerminal() {\n        return false;\n    }\n\n    @Override\n    public String getName() {\n        return \"initializing\";\n    }\n\n    @Override\n    public boolean shouldExecuteState(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance) {\n        return true;\n    }\n\n    @Override\n    public SStateCategory getStateCategory() {\n        return SStateCategory.NORMAL;\n    }\n\n    @Override\n    public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return false;\n    }\n\n    @Override\n    public String getSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return \"\";\n    }\n\n    @Override\n    protected void beforeConnectors(SProcessDefinition processDefinition, SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException {\n        stateBehaviors.handleBoundaryEvent(processDefinition, (SBoundaryEventInstance) flowNodeInstance);\n    }\n\n    @Override\n    protected void afterConnectors(SProcessDefinition processDefinition, SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException {\n        stateBehaviors.updateDisplayNameAndDescription(processDefinition, flowNodeInstance);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/InitializingLoopActivityState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.model.impl.BPMInstancesCreator;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.process.definition.model.SActivityDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SLoopCharacteristics;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SStandardLoopCharacteristics;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.api.states.StateCode;\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowElementsContainerType;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SLoopActivityInstanceBuilderFactory;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.execution.StateBehaviors;\nimport org.bonitasoft.engine.expression.ExpressionConstants;\nimport org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class InitializingLoopActivityState implements FlowNodeState {\n\n    private final ExpressionResolverService expressionResolverService;\n\n    private final BPMInstancesCreator bpmInstancesCreator;\n\n    private final ActivityInstanceService activityInstanceService;\n\n    private final StateBehaviors stateBehaviors;\n\n    public InitializingLoopActivityState(final ExpressionResolverService expressionResolverService,\n            final BPMInstancesCreator bpmInstancesCreator,\n            final ActivityInstanceService activityInstanceService, final StateBehaviors stateBehaviors) {\n        this.expressionResolverService = expressionResolverService;\n        this.bpmInstancesCreator = bpmInstancesCreator;\n        this.activityInstanceService = activityInstanceService;\n        this.stateBehaviors = stateBehaviors;\n    }\n\n    @Override\n    public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException {\n        stateBehaviors.createAttachedBoundaryEvents(processDefinition, (SActivityInstance) flowNodeInstance);\n        final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer();\n        final Long processDefinitionId = processDefinition.getId();\n        final SActivityDefinition activity = (SActivityDefinition) processContainer\n                .getFlowNode(flowNodeInstance.getFlowNodeDefinitionId());\n        try {\n            final SLoopActivityInstance loopActivity = (SLoopActivityInstance) activityInstanceService\n                    .getFlowNodeInstance(flowNodeInstance.getId());\n            final SLoopCharacteristics loopCharacteristics = activity.getLoopCharacteristics();\n            if (loopCharacteristics instanceof SStandardLoopCharacteristics standardLoop) {\n                final SExpression loopMax = ((SStandardLoopCharacteristics) loopCharacteristics).getLoopMax();\n                Integer intLoopMax;\n\n                if (loopMax != null) {\n                    intLoopMax = (Integer) expressionResolverService.evaluate(loopMax,\n                            new SExpressionContext(loopActivity.getId(),\n                                    DataInstanceContainer.ACTIVITY_INSTANCE.name(),\n                                    processDefinitionId));\n                    activityInstanceService.setLoopMax(loopActivity, intLoopMax);\n                }\n                final boolean loop = !standardLoop.isTestBefore() || evaluateLoop(standardLoop, loopActivity);\n                if (loop) {\n                    final SLoopActivityInstanceBuilderFactory keyProvider = BuilderFactory\n                            .get(SLoopActivityInstanceBuilderFactory.class);\n                    final long rootProcessInstanceId = flowNodeInstance\n                            .getLogicalGroup(keyProvider.getRootProcessInstanceIndex());\n                    final long parentProcessInstanceId = flowNodeInstance\n                            .getLogicalGroup(keyProvider.getParentProcessInstanceIndex());\n                    bpmInstancesCreator.createFlowNodeInstance(processDefinitionId,\n                            flowNodeInstance.getRootContainerId(), flowNodeInstance.getId(),\n                            SFlowElementsContainerType.FLOWNODE, activity, rootProcessInstanceId,\n                            parentProcessInstanceId, true, 1, SStateCategory.NORMAL, -1);\n                    activityInstanceService.incrementLoopCounter(loopActivity);\n                    activityInstanceService.setTokenCount(loopActivity, loopActivity.getTokenCount() + 1);\n                }\n            }\n        } catch (final SBonitaException e) {\n            throw new SActivityStateExecutionException(e);\n        }\n        return StateCode.DONE;\n    }\n\n    private boolean evaluateLoop(final SStandardLoopCharacteristics standardLoop,\n            final SLoopActivityInstance loopActivity)\n            throws SExpressionTypeUnknownException, SExpressionEvaluationException,\n            SExpressionDependencyMissingException, SInvalidExpressionException {\n        final Map<String, Object> input = new HashMap<>(1);\n        input.put(ExpressionConstants.LOOP_COUNTER.getEngineConstantName(), loopActivity.getLoopCounter());\n        final SExpressionContext sExpressionContext = new SExpressionContext(loopActivity.getId(),\n                DataInstanceContainer.ACTIVITY_INSTANCE.name(),\n                loopActivity.getProcessDefinitionId(), input);\n        return ((Boolean) expressionResolverService.evaluate(standardLoop.getLoopCondition(), sExpressionContext))\n                .booleanValue();\n    }\n\n    @Override\n    public int getId() {\n        return 23;\n    }\n\n    @Override\n    public boolean isStable() {\n        return false;\n    }\n\n    @Override\n    public boolean isTerminal() {\n        return false;\n    }\n\n    @Override\n    public String getName() {\n        return \"initializing\";\n    }\n\n    @Override\n    public boolean shouldExecuteState(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance) {\n        return true;\n    }\n\n    @Override\n    public SStateCategory getStateCategory() {\n        return SStateCategory.NORMAL;\n    }\n\n    @Override\n    public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return false;\n    }\n\n    @Override\n    public String getSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return \"\";\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/InitializingMultiInstanceActivityState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.ScopedException;\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.process.definition.model.SActivityDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SLoopCharacteristics;\nimport org.bonitasoft.engine.core.process.definition.model.SMultiInstanceLoopCharacteristics;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.api.states.StateCode;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.execution.StateBehaviors;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class InitializingMultiInstanceActivityState implements FlowNodeState {\n\n    private final ExpressionResolverService expressionResolverService;\n\n    private final ActivityInstanceService activityInstanceService;\n\n    private final StateBehaviors stateBehaviors;\n\n    public InitializingMultiInstanceActivityState(final ExpressionResolverService expressionResolverService,\n            final ActivityInstanceService activityInstanceService, final StateBehaviors stateBehaviors) {\n        this.expressionResolverService = expressionResolverService;\n        this.activityInstanceService = activityInstanceService;\n        this.stateBehaviors = stateBehaviors;\n    }\n\n    @Override\n    public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException {\n        try {\n            final SMultiInstanceActivityInstance multiInstanceActivityInstance = (SMultiInstanceActivityInstance) flowNodeInstance;\n            stateBehaviors.createAttachedBoundaryEvents(processDefinition, multiInstanceActivityInstance);\n            final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer();\n            final SActivityDefinition activity = (SActivityDefinition) processContainer\n                    .getFlowNode(flowNodeInstance.getFlowNodeDefinitionId());\n            final SLoopCharacteristics loopCharacteristics = activity.getLoopCharacteristics();\n            if (loopCharacteristics instanceof SMultiInstanceLoopCharacteristics miLoop) {\n                final SExpression loopCardinality = miLoop.getLoopCardinality();\n                int numberOfInstanceMax = -1;\n                if (loopCardinality != null) {\n                    numberOfInstanceMax = (Integer) expressionResolverService.evaluate(loopCardinality,\n                            new SExpressionContext(multiInstanceActivityInstance.getId(),\n                                    DataInstanceContainer.ACTIVITY_INSTANCE.name(), processDefinition\n                                            .getId()));\n                    activityInstanceService.setLoopCardinality(multiInstanceActivityInstance, numberOfInstanceMax);\n                } else if (miLoop.getLoopDataInputRef() != null) {\n                    numberOfInstanceMax = stateBehaviors.getNumberOfInstancesToCreateFromInputRef(processDefinition,\n                            multiInstanceActivityInstance, miLoop,\n                            numberOfInstanceMax);\n                }\n                if (numberOfInstanceMax < 0) {\n                    throw new SActivityStateExecutionException(\n                            \"The multi instance on activity \" + flowNodeInstance.getName() + \" of process \"\n                                    + processDefinition.getName() + \" \" + processDefinition.getVersion()\n                                    + \" did not have loop cardinality nor loop data input ref set\",\n                            ScopedException.ITERATION);\n                }\n                stateBehaviors.updateOutputData(processDefinition, multiInstanceActivityInstance, miLoop,\n                        numberOfInstanceMax);\n                if (numberOfInstanceMax > 0) {\n                    stateBehaviors.createInnerInstances(processDefinition.getId(), activity,\n                            multiInstanceActivityInstance, miLoop.isSequential() ? 1\n                                    : numberOfInstanceMax);\n                }\n            }\n        } catch (final SActivityStateExecutionException e) {\n            throw e;\n        } catch (final SBonitaException e) {\n            throw new SActivityStateExecutionException(\"Failed to execute multi instance activity \" + flowNodeInstance,\n                    ScopedException.ITERATION, e);\n        }\n        return StateCode.DONE;\n    }\n\n    @Override\n    public int getId() {\n        return 27;\n    }\n\n    @Override\n    public boolean isStable() {\n        return false;\n    }\n\n    @Override\n    public boolean isTerminal() {\n        return false;\n    }\n\n    @Override\n    public String getName() {\n        return \"initializing\";\n    }\n\n    @Override\n    public boolean notifyChildFlowNodeHasFinished(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance parentInstance,\n            final SFlowNodeInstance childInstance) {\n        return true;\n    }\n\n    @Override\n    public boolean shouldExecuteState(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance) {\n        return true;\n    }\n\n    @Override\n    public SStateCategory getStateCategory() {\n        return SStateCategory.NORMAL;\n    }\n\n    @Override\n    public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return false;\n    }\n\n    @Override\n    public String getSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return \"\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/InterruptingBoundaryAndIntermediateCatchEventState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SCatchEventDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.api.states.StateCode;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SCatchEventInstance;\nimport org.bonitasoft.engine.execution.WaitingEventsInterrupter;\n\npublic abstract class InterruptingBoundaryAndIntermediateCatchEventState implements FlowNodeState {\n\n    private final WaitingEventsInterrupter waitingEventsInterrupter;\n\n    public InterruptingBoundaryAndIntermediateCatchEventState(WaitingEventsInterrupter waitingEventsInterrupter) {\n        super();\n        this.waitingEventsInterrupter = waitingEventsInterrupter;\n    }\n\n    @Override\n    public boolean shouldExecuteState(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance) {\n        return true;\n    }\n\n    @Override\n    public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance instance)\n            throws SActivityStateExecutionException {\n        final SCatchEventDefinition catchEventDef = (SCatchEventDefinition) processDefinition.getProcessContainer()\n                .getFlowNode(\n                        instance.getFlowNodeDefinitionId());\n        try {\n            final SCatchEventInstance catchEventInstance = (SCatchEventInstance) instance;\n            waitingEventsInterrupter.interruptWaitingEvents(processDefinition, catchEventInstance,\n                    catchEventDef);\n        } catch (final SBonitaException e) {\n            throw new SActivityStateExecutionException(e);\n        }\n        return StateCode.DONE;\n    }\n\n    @Override\n    public boolean notifyChildFlowNodeHasFinished(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance parentInstance,\n            final SFlowNodeInstance childInstance) {\n        return true;\n    }\n\n    @Override\n    public boolean isStable() {\n        return false;\n    }\n\n    @Override\n    public boolean isTerminal() {\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/OnEnterAndFinishConnectorState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.core.connector.exception.SConnectorInstanceReadException;\nimport org.bonitasoft.engine.core.process.definition.model.SConnectorDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.api.states.StateCode;\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.execution.StateBehaviors;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\n/**\n * @author Baptiste Mesta\n */\npublic abstract class OnEnterAndFinishConnectorState implements FlowNodeState {\n\n    private final StateBehaviors stateBehaviors;\n\n    public OnEnterAndFinishConnectorState(StateBehaviors stateBehaviors) {\n        this.stateBehaviors = stateBehaviors;\n    }\n\n    public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException {\n        // Retrieve the phase to execute depending on which connectors to execute and when to execute them:\n        try {\n            //get all element to check where we are in the state execution\n            List<SConnectorDefinition> onEnterConnectors = stateBehaviors.getConnectorDefinitions(processDefinition,\n                    flowNodeInstance, ConnectorEvent.ON_ENTER);\n            final SConnectorInstance onEnterConnectorToExecute = stateBehaviors.getNextConnectorInstance(\n                    onEnterConnectors, flowNodeInstance,\n                    ConnectorEvent.ON_ENTER);\n            boolean noConnectorStartedOnEnter = stateBehaviors.noConnectorHasStartedInCurrentList(onEnterConnectors,\n                    onEnterConnectorToExecute);\n\n            List<SConnectorDefinition> onFinishConnectors = stateBehaviors.getConnectorDefinitions(processDefinition,\n                    flowNodeInstance,\n                    ConnectorEvent.ON_FINISH);\n            final SConnectorInstance onFinishConnectorToExecute = stateBehaviors.getNextConnectorInstance(\n                    onFinishConnectors, flowNodeInstance,\n                    ConnectorEvent.ON_FINISH);\n            boolean noConnectorStartedOnFinish = stateBehaviors.noConnectorHasStartedInCurrentList(onFinishConnectors,\n                    onFinishConnectorToExecute);\n\n            //if no connector has started neither on enter or on finish we can do the beforeOnEnter phase\n            if (noConnectorStartedOnEnter && noConnectorStartedOnFinish) {\n                beforeOnEnter(processDefinition, flowNodeInstance);\n            }\n            if (onEnterConnectorToExecute != null) {\n                stateBehaviors.executeConnector(processDefinition, flowNodeInstance, onEnterConnectors,\n                        onEnterConnectorToExecute);\n                return StateCode.EXECUTING;\n            }\n\n            if (noConnectorStartedOnFinish) {\n                onEnterToOnFinish(processDefinition, flowNodeInstance);\n            }\n            if (onFinishConnectorToExecute != null) {\n                stateBehaviors.executeConnector(processDefinition, flowNodeInstance, onFinishConnectors,\n                        onFinishConnectorToExecute);\n                return StateCode.EXECUTING;\n            }\n            afterOnFinish(processDefinition, flowNodeInstance);\n            return StateCode.DONE;\n        } catch (SBonitaReadException | SConnectorInstanceReadException e) {\n            throw new SActivityStateExecutionException(e);\n        }\n    }\n\n    protected abstract void beforeOnEnter(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException;\n\n    protected abstract void onEnterToOnFinish(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException;\n\n    protected abstract void afterOnFinish(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException;\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/OnEnterConnectorState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.execution.StateBehaviors;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Baptiste Mesta\n */\n@Component\npublic abstract class OnEnterConnectorState extends OnEnterOrOnFinishConnectorState {\n\n    public OnEnterConnectorState(StateBehaviors stateBehaviors) {\n        super(stateBehaviors);\n    }\n\n    @Override\n    protected ConnectorEvent getConnectorEvent() {\n        return ConnectorEvent.ON_ENTER;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/OnEnterOrOnFinishConnectorState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.core.connector.exception.SConnectorInstanceReadException;\nimport org.bonitasoft.engine.core.process.definition.model.SConnectorDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.api.states.StateCode;\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.execution.StateBehaviors;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Baptiste Mesta\n */\n@Component\npublic abstract class OnEnterOrOnFinishConnectorState implements FlowNodeState {\n\n    protected final StateBehaviors stateBehaviors;\n\n    public OnEnterOrOnFinishConnectorState(StateBehaviors stateBehaviors) {\n        this.stateBehaviors = stateBehaviors;\n    }\n\n    protected abstract void beforeConnectors(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException;\n\n    protected abstract void afterConnectors(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException;\n\n    public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException {\n        // Retrieve the phase to execute depending on which connectors to execute and when to execute them:\n        try {\n            ConnectorEvent connectorEvent = getConnectorEvent();\n            //get all element to check where we are in the state execution\n            List<SConnectorDefinition> connectorDefinitions = stateBehaviors.getConnectorDefinitions(processDefinition,\n                    flowNodeInstance, connectorEvent);\n            final SConnectorInstance connectorInstance = stateBehaviors.getNextConnectorInstance(connectorDefinitions,\n                    flowNodeInstance, connectorEvent);\n            boolean noConnectorStartedOnEnter = stateBehaviors.noConnectorHasStartedInCurrentList(connectorDefinitions,\n                    connectorInstance);\n            if (noConnectorStartedOnEnter) {\n                //TODO unit test condition on this\n                beforeConnectors(processDefinition, flowNodeInstance);\n            }\n            if (connectorInstance != null) {\n                stateBehaviors.executeConnector(processDefinition, flowNodeInstance, connectorDefinitions,\n                        connectorInstance);\n                return StateCode.EXECUTING;\n            }\n            afterConnectors(processDefinition, flowNodeInstance);\n            return StateCode.DONE;\n        } catch (SBonitaReadException | SConnectorInstanceReadException e) {\n            throw new SActivityStateExecutionException(e);\n        }\n    }\n\n    protected abstract ConnectorEvent getConnectorEvent();\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/OnFinishConnectorState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\n/**\n * @author Baptiste Mesta\n */\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.execution.StateBehaviors;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic abstract class OnFinishConnectorState extends OnEnterOrOnFinishConnectorState {\n\n    public OnFinishConnectorState(StateBehaviors stateBehaviors) {\n        super(stateBehaviors);\n    }\n\n    @Override\n    protected ConnectorEvent getConnectorEvent() {\n        return ConnectorEvent.ON_FINISH;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/ReadyActivityState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException;\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.execution.StateBehaviors;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class ReadyActivityState extends OnEnterAndFinishConnectorState {\n\n    public static final int ID = 4;\n    private final StateBehaviors stateBehaviors;\n\n    public ReadyActivityState(final StateBehaviors stateBehaviors) {\n        super(stateBehaviors);\n        this.stateBehaviors = stateBehaviors;\n    }\n\n    @Override\n    protected void beforeOnEnter(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException {\n        if (((SHumanTaskInstance) flowNodeInstance).getAssigneeId() <= 0) {\n            throw new SActivityStateExecutionException(\"The activity is not yet assigned, unable to execute it\");\n        }\n    }\n\n    @Override\n    protected void onEnterToOnFinish(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException {\n        stateBehaviors.executeOperations(processDefinition, (SActivityInstance) flowNodeInstance);\n    }\n\n    @Override\n    protected void afterOnFinish(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance)\n            throws SActivityStateExecutionException {\n        stateBehaviors.mapDataOutputOfMultiInstance(processDefinition, flowNodeInstance);\n        stateBehaviors.updateDisplayDescriptionAfterCompletion(processDefinition, flowNodeInstance);\n    }\n\n    @Override\n    public int getId() {\n        return ID;\n    }\n\n    @Override\n    public boolean isStable() {\n        return true;\n    }\n\n    @Override\n    public boolean isTerminal() {\n        return false;\n    }\n\n    @Override\n    public String getName() {\n        return \"ready\";\n    }\n\n    @Override\n    public boolean shouldExecuteState(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance) {\n        return true;\n    }\n\n    @Override\n    public SStateCategory getStateCategory() {\n        return SStateCategory.NORMAL;\n    }\n\n    @Override\n    public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return false;\n    }\n\n    @Override\n    public String getSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return \"\";\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/SkippedFlowNodeState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.api.states.StateCode;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class SkippedFlowNodeState implements FlowNodeState {\n\n    public static final int ID = 12;\n\n    public SkippedFlowNodeState() {\n    }\n\n    @Override\n    public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance instance) {\n        return StateCode.DONE;\n    }\n\n    @Override\n    public int getId() {\n        return ID;\n    }\n\n    @Override\n    public String getName() {\n        return \"skipped\";\n    }\n\n    @Override\n    public boolean isStable() {\n        return true;\n    }\n\n    @Override\n    public boolean isTerminal() {\n        return true;\n    }\n\n    @Override\n    public boolean notifyChildFlowNodeHasFinished(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance parentInstance,\n            final SFlowNodeInstance childInstance) {\n        return parentInstance.getTokenCount() == 0;\n    }\n\n    @Override\n    public boolean shouldExecuteState(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance) {\n        return true;\n    }\n\n    @Override\n    public SStateCategory getStateCategory() {\n        return SStateCategory.NORMAL;\n    }\n\n    @Override\n    public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return flowNodeInstance instanceof SHumanTaskInstance;\n    }\n\n    @Override\n    public String getSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return \" User \" + flowNodeInstance.getExecutedBy() + \" has \" + getName() + \" task \"\n                + flowNodeInstance.getName();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/WaitingFlowNodeState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.api.states.StateCode;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class WaitingFlowNodeState implements FlowNodeState {\n\n    public WaitingFlowNodeState() {\n    }\n\n    @Override\n    public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance instance) {\n        return StateCode.DONE;\n    }\n\n    @Override\n    public int getId() {\n        return 10;\n    }\n\n    @Override\n    public String getName() {\n        return \"waiting\";\n    }\n\n    @Override\n    public boolean isStable() {\n        return true;\n    }\n\n    @Override\n    public boolean isTerminal() {\n        return false;\n    }\n\n    @Override\n    public boolean notifyChildFlowNodeHasFinished(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance parentInstance,\n            final SFlowNodeInstance childInstance) {\n        return true;\n    }\n\n    @Override\n    public boolean shouldExecuteState(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance) {\n        return true;\n    }\n\n    @Override\n    public SStateCategory getStateCategory() {\n        return SStateCategory.NORMAL;\n    }\n\n    @Override\n    public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return false;\n    }\n\n    @Override\n    public String getSystemComment(final SFlowNodeInstance flowNodeInstance) {\n        return \"\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/AutomaticTaskStates.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.transition;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.execution.state.AbortedFlowNodeState;\nimport org.bonitasoft.engine.execution.state.AbortingActivityWithBoundaryState;\nimport org.bonitasoft.engine.execution.state.AbortingBoundaryEventsOnCompletingActivityState;\nimport org.bonitasoft.engine.execution.state.AbortingFlowNodeContainerState;\nimport org.bonitasoft.engine.execution.state.CancelledFlowNodeState;\nimport org.bonitasoft.engine.execution.state.CancellingActivityWithBoundaryState;\nimport org.bonitasoft.engine.execution.state.CancellingFlowNodeContainerChildrenState;\nimport org.bonitasoft.engine.execution.state.CompletedActivityState;\nimport org.bonitasoft.engine.execution.state.ExecutingAutomaticActivityState;\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class AutomaticTaskStates extends FlowNodeStateSequences {\n\n    public SFlowNodeType getFlowNodeType() {\n        return SFlowNodeType.AUTOMATIC_TASK;\n    }\n\n    public AutomaticTaskStates(\n            AbortingBoundaryEventsOnCompletingActivityState abortingBoundaryEventsOnCompletingActivityState,\n            CompletedActivityState completed,\n            AbortingActivityWithBoundaryState abortingActivityWithBoundary,\n            AbortedFlowNodeState aborted,\n            @Qualifier(\"cancellingActivityWithBoundaryState\") CancellingActivityWithBoundaryState cancellingActivityWithBoundary,\n            CancelledFlowNodeState cancelled,\n            ExecutingAutomaticActivityState executingAutomaticActivity,\n            CancellingFlowNodeContainerChildrenState cancellingContainer,\n            @Qualifier(\"abortingFlowNodeContainerState\") AbortingFlowNodeContainerState abortingContainer) {\n\n        defineNormalSequence(executingAutomaticActivity, abortingBoundaryEventsOnCompletingActivityState,\n                completed);\n        defineAbortSequence(abortingActivityWithBoundary, abortingContainer, aborted);\n        defineCancelSequence(cancellingActivityWithBoundary, cancellingContainer, cancelled);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/BoundaryEventStates.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.transition;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.execution.state.*;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class BoundaryEventStates extends FlowNodeStateSequences {\n\n    public SFlowNodeType getFlowNodeType() {\n        return SFlowNodeType.BOUNDARY_EVENT;\n    }\n\n    public BoundaryEventStates(CompletedActivityState completed,\n            AbortingBoundaryAndIntermediateCatchEventState aborting,\n            AbortedFlowNodeState aborted,\n            CancellingBoundaryAndIntermediateCatchEventState cancelling,\n            CancelledFlowNodeState cancelled,\n            WaitingFlowNodeState waiting,\n            InitializingBoundaryEventState initializingBoundaryEvent,\n            ExecutingBoundaryEventState executingBoundaryEvent) {\n\n        defineNormalSequence(initializingBoundaryEvent, waiting, executingBoundaryEvent, completed);\n        defineAbortSequence(aborting, aborted);\n        defineCancelSequence(cancelling, cancelled);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/CallActivityTaskStates.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.transition;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.execution.state.AbortedFlowNodeState;\nimport org.bonitasoft.engine.execution.state.AbortingActivityWithBoundaryState;\nimport org.bonitasoft.engine.execution.state.AbortingBoundaryEventsOnCompletingActivityState;\nimport org.bonitasoft.engine.execution.state.AbortingCallActivityState;\nimport org.bonitasoft.engine.execution.state.CancelledFlowNodeState;\nimport org.bonitasoft.engine.execution.state.CancellingActivityWithBoundaryState;\nimport org.bonitasoft.engine.execution.state.CancellingCallActivityState;\nimport org.bonitasoft.engine.execution.state.CompletedActivityState;\nimport org.bonitasoft.engine.execution.state.CompletingCallActivityState;\nimport org.bonitasoft.engine.execution.state.ExecutingCallActivityState;\nimport org.bonitasoft.engine.execution.state.InitializingActivityWithBoundaryEventsState;\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class CallActivityTaskStates extends FlowNodeStateSequences {\n\n    public SFlowNodeType getFlowNodeType() {\n        return SFlowNodeType.CALL_ACTIVITY;\n    }\n\n    public CallActivityTaskStates(InitializingActivityWithBoundaryEventsState initializingActivityWithBoundary,\n            AbortingBoundaryEventsOnCompletingActivityState abortingBoundaryEventsOnCompletingActivityState,\n            CompletedActivityState completed,\n            AbortingActivityWithBoundaryState abortingActivityWithBoundary,\n            AbortedFlowNodeState aborted,\n            @Qualifier(\"cancellingActivityWithBoundaryState\") CancellingActivityWithBoundaryState cancellingActivityWithBoundary,\n            CancelledFlowNodeState cancelled,\n            ExecutingCallActivityState executingCallActivity,\n            CompletingCallActivityState completingCallActivity,\n            AbortingCallActivityState abortingCallActivity,\n            CancellingCallActivityState cancellingCallActivity) {\n\n        defineNormalSequence(initializingActivityWithBoundary, executingCallActivity,\n                abortingBoundaryEventsOnCompletingActivityState, completingCallActivity, completed);\n        defineAbortSequence(abortingActivityWithBoundary, abortingCallActivity, aborted);\n        defineCancelSequence(cancellingActivityWithBoundary, cancellingCallActivity, cancelled);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/DefaultTransitionGetter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.transition;\n\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityExecutionException;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.execution.flowmerger.FlowNodeTransitionsWrapper;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class DefaultTransitionGetter {\n\n    STransitionDefinition getDefaultTransition(FlowNodeTransitionsWrapper transitions,\n            SProcessDefinition processDefinition, SFlowNodeInstance flowNodeInstance)\n            throws SActivityExecutionException {\n        STransitionDefinition defaultTransition = transitions.getDefaultTransition();\n        if (defaultTransition == null) {\n            final SActivityExecutionException exception = new SActivityExecutionException(\n                    \"There is no default transition on \" + flowNodeInstance.getName()\n                            + \", but no outgoing transition had a valid condition.\");\n            exception.setProcessDefinitionNameOnContext(processDefinition.getName());\n            exception.setProcessDefinitionVersionOnContext(processDefinition.getVersion());\n            exception.setProcessInstanceIdOnContext(flowNodeInstance.getParentProcessInstanceId());\n            throw exception;\n        }\n        return defaultTransition;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/EndEventStates.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.transition;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.execution.state.AbortedFlowNodeState;\nimport org.bonitasoft.engine.execution.state.AbortingFlowNodeState;\nimport org.bonitasoft.engine.execution.state.CancelledFlowNodeState;\nimport org.bonitasoft.engine.execution.state.CancellingFlowNodeState;\nimport org.bonitasoft.engine.execution.state.CompletedActivityState;\nimport org.bonitasoft.engine.execution.state.ExecutingThrowEventState;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class EndEventStates extends FlowNodeStateSequences {\n\n    public SFlowNodeType getFlowNodeType() {\n        return SFlowNodeType.END_EVENT;\n    }\n\n    @Autowired\n    public EndEventStates(CompletedActivityState completed,\n            AbortedFlowNodeState aborted,\n            CancelledFlowNodeState cancelled,\n            AbortingFlowNodeState abortingFlowNode,\n            CancellingFlowNodeState cancellingFlowNode,\n            ExecutingThrowEventState executingThrowEvent) {\n\n        defineNormalSequence(executingThrowEvent, completed);\n        defineAbortSequence(abortingFlowNode, aborted);\n        defineCancelSequence(cancellingFlowNode, cancelled);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/EvaluatedTransitions.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.transition;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class EvaluatedTransitions {\n\n    private List<STransitionDefinition> unconditionalTransitions = new ArrayList<STransitionDefinition>();\n    private List<STransitionDefinition> trueTransitions = new ArrayList<STransitionDefinition>();\n    private List<STransitionDefinition> falseTransitions = new ArrayList<STransitionDefinition>();\n\n    /**\n     * Add a transition to unconditional transitions\n     *\n     * @param transitionDefinition unconditional transition\n     */\n    void addUnconditionalTransition(STransitionDefinition transitionDefinition) {\n        unconditionalTransitions.add(transitionDefinition);\n    }\n\n    /**\n     * Add a transition to transitions having conditions evaluated to true\n     *\n     * @param transitionDefinition the transition which condition was evaluated to true\n     */\n    void addTrueTransition(STransitionDefinition transitionDefinition) {\n        trueTransitions.add(transitionDefinition);\n    }\n\n    /**\n     * Add a transition to transitions having conditions evaluated to false\n     *\n     * @param transitionDefinition the transition which condition was evaluated to false\n     */\n    void addFalseTransition(STransitionDefinition transitionDefinition) {\n        falseTransitions.add(transitionDefinition);\n    }\n\n    /**\n     * @return list of unconditional transitions\n     */\n    public List<STransitionDefinition> getUnconditionalTransitions() {\n        return unconditionalTransitions;\n    }\n\n    /**\n     * @return list of transitions which conditions were evaluated to true\n     */\n    public List<STransitionDefinition> getTrueTransitions() {\n        return trueTransitions;\n    }\n\n    /**\n     * @return true if there is at least one unconditional transition; false otherwise\n     */\n    public boolean hasUnconditionalTransitions() {\n        return !unconditionalTransitions.isEmpty();\n    }\n\n    /**\n     * @return true if there is at least one transition which condition was evaluated to true; false otherwise\n     */\n    public boolean hasTrueTransitions() {\n        return !trueTransitions.isEmpty();\n    }\n\n    /**\n     * @return true if there is at least one transition which condition was evaluated to false; false otherwise\n     */\n    public boolean hasFalseTransitons() {\n        return !falseTransitions.isEmpty();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/ExclusiveGatewayTransitionEvaluationStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.transition;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ExclusiveGatewayTransitionEvaluationStrategy implements TransitionEvaluationStrategy {\n\n    @Override\n    public boolean shouldContinue(final boolean alreadyFound) {\n        return !alreadyFound;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/FlowNodeStateSequences.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.transition;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.springframework.stereotype.Component;\n\n/**\n * For a given flow node type, this class maintains the supported flow node states and the sequence\n * and transitions from one state to another.\n * Each <code>FlowNodeStatesAndTransitions</code> class defines 3 state sequences:\n * <ul>\n * <li>one for the normal flow</li>\n * <li>one for the aborting flow</li>\n * <li>one for the cancelling flow</li>\n * </ul>\n */\n@Component\npublic abstract class FlowNodeStateSequences {\n\n    /*\n     * store all states of a given state category, and their order.\n     */\n    private static class StateSequence {\n\n        Map<Integer, FlowNodeState> mapOfCurrentStateIdToNextState = new HashMap<>();\n        FlowNodeState firstState;\n\n        public StateSequence(FlowNodeState... flowNodeStates) {\n            firstState = flowNodeStates[0];\n            for (int i = 0; i < flowNodeStates.length - 1; i++) {\n                // key = id of the current state\n                // value = next state\n                mapOfCurrentStateIdToNextState.put(flowNodeStates[i].getId(), flowNodeStates[i + 1]);\n            }\n        }\n\n        public FlowNodeState getFirstState() {\n            return firstState;\n        }\n\n        public FlowNodeState getStateAfter(int previousStateId) {\n            return mapOfCurrentStateIdToNextState.get(previousStateId);\n        }\n    }\n\n    private StateSequence normalSequence;\n    private StateSequence cancelSequence;\n    private StateSequence abortSequence;\n\n    protected void defineNormalSequence(FlowNodeState... flowNodeStates) {\n        normalSequence = new StateSequence(flowNodeStates);\n    }\n\n    protected void defineCancelSequence(FlowNodeState... flowNodeStates) {\n        cancelSequence = new StateSequence(flowNodeStates);\n    }\n\n    protected void defineAbortSequence(FlowNodeState... flowNodeStates) {\n        abortSequence = new StateSequence(flowNodeStates);\n    }\n\n    public abstract SFlowNodeType getFlowNodeType();\n\n    public FlowNodeState getFirstState(SStateCategory category) {\n        return getSequence(category).getFirstState();\n    }\n\n    public FlowNodeState getStateAfter(SStateCategory category, int currentStateId) {\n        return getSequence(category).getStateAfter(currentStateId);\n    }\n\n    private StateSequence getSequence(SStateCategory category) {\n        switch (category) {\n            case NORMAL:\n                return normalSequence;\n            case ABORTING:\n                return abortSequence;\n            case CANCELLING:\n                return cancelSequence;\n            default:\n                throw new IllegalStateException(\"Unexpected value: \" + category);\n        }\n    }\n\n    public Set<String> getSupportedStates() {\n        Set<String> names = new HashSet<>();\n        names.add(normalSequence.firstState.getName());\n        normalSequence.mapOfCurrentStateIdToNextState.values().forEach(s -> names.add(s.getName()));\n        return names;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/GatewaysStates.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.transition;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.execution.state.AbortedFlowNodeState;\nimport org.bonitasoft.engine.execution.state.AbortingFlowNodeState;\nimport org.bonitasoft.engine.execution.state.CancelledFlowNodeState;\nimport org.bonitasoft.engine.execution.state.CancellingFlowNodeState;\nimport org.bonitasoft.engine.execution.state.CompletedActivityState;\nimport org.bonitasoft.engine.execution.state.InitializingAndExecutingFlowNodeState;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class GatewaysStates extends FlowNodeStateSequences {\n\n    public SFlowNodeType getFlowNodeType() {\n        return SFlowNodeType.GATEWAY;\n    }\n\n    public GatewaysStates(CompletedActivityState completed,\n            AbortedFlowNodeState aborted,\n            CancelledFlowNodeState cancelled,\n            AbortingFlowNodeState abortingFlowNode,\n            CancellingFlowNodeState cancellingFlowNode,\n            InitializingAndExecutingFlowNodeState initializingAndExecuting) {\n\n        defineNormalSequence(initializingAndExecuting, completed);\n        defineAbortSequence(abortingFlowNode, aborted);\n        defineCancelSequence(cancellingFlowNode, cancelled);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/ImplicitGatewayTransitionEvaluator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.transition;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityExecutionException;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.execution.flowmerger.FlowNodeTransitionsWrapper;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ImplicitGatewayTransitionEvaluator {\n\n    private final TransitionConditionEvaluator conditionEvaluator;\n    private final DefaultTransitionGetter defaultTransitionGetter;\n\n    public ImplicitGatewayTransitionEvaluator(TransitionConditionEvaluator conditionEvaluator,\n            DefaultTransitionGetter defaultTransitionGetter) {\n        this.conditionEvaluator = conditionEvaluator;\n        this.defaultTransitionGetter = defaultTransitionGetter;\n    }\n\n    public List<STransitionDefinition> evaluateTransitions(final SProcessDefinition sDefinition,\n            final SFlowNodeInstance flowNodeInstance,\n            FlowNodeTransitionsWrapper transitions, final SExpressionContext sExpressionContext)\n            throws SBonitaException {\n        EvaluatedTransitions evaluatedTransitions = evaluatedTransitions(sExpressionContext,\n                transitions.getNonDefaultOutgoingTransitionDefinitions());\n        return buildChosenTransitions(evaluatedTransitions, transitions, sDefinition, flowNodeInstance);\n    }\n\n    private List<STransitionDefinition> buildChosenTransitions(final EvaluatedTransitions evaluatedTransitions,\n            final FlowNodeTransitionsWrapper transitions,\n            final SProcessDefinition sDefinition, final SFlowNodeInstance flowNodeInstance)\n            throws SActivityExecutionException {\n        final List<STransitionDefinition> chosenTransitions = new ArrayList<>(\n                evaluatedTransitions.getUnconditionalTransitions().size()\n                        + evaluatedTransitions.getTrueTransitions().size());\n        if (evaluatedTransitions.hasUnconditionalTransitions()) {\n            chosenTransitions.addAll(evaluatedTransitions.getUnconditionalTransitions());\n        }\n        if (evaluatedTransitions.hasTrueTransitions()) {\n            chosenTransitions.addAll(evaluatedTransitions.getTrueTransitions());\n        } else if (evaluatedTransitions.hasFalseTransitons()) {\n            final STransitionDefinition defaultTransition = defaultTransitionGetter.getDefaultTransition(transitions,\n                    sDefinition, flowNodeInstance);\n            chosenTransitions.add(defaultTransition);\n        }\n        return chosenTransitions;\n    }\n\n    private EvaluatedTransitions evaluatedTransitions(final SExpressionContext sExpressionContext,\n            final List<STransitionDefinition> outgoingTransitionDefinitions) throws SBonitaException {\n        EvaluatedTransitions evaluatedTransitions = new EvaluatedTransitions();\n        for (final STransitionDefinition sTransitionDefinition : outgoingTransitionDefinitions) {\n            evaluateTransition(evaluatedTransitions, sTransitionDefinition, sExpressionContext);\n        }\n        return evaluatedTransitions;\n    }\n\n    private void evaluateTransition(final EvaluatedTransitions evaluatedTransitions,\n            final STransitionDefinition sTransitionDefinition,\n            final SExpressionContext sExpressionContext) throws SBonitaException {\n        final Boolean condition = conditionEvaluator.evaluateCondition(sTransitionDefinition, sExpressionContext);\n        if (!sTransitionDefinition.hasCondition()) {\n            evaluatedTransitions.addUnconditionalTransition(sTransitionDefinition);\n        } else {\n            if (condition != null && condition) {\n                evaluatedTransitions.addTrueTransition(sTransitionDefinition);\n            } else {\n                evaluatedTransitions.addFalseTransition(sTransitionDefinition);\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/InclusiveExclusiveTransitionEvaluator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.transition;\n\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.execution.flowmerger.FlowNodeTransitionsWrapper;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class InclusiveExclusiveTransitionEvaluator {\n\n    private final TransitionEvaluationStrategy strategy;\n\n    private final TransitionConditionEvaluator evaluator;\n    private final DefaultTransitionGetter defaultTransitionGetter;\n\n    public InclusiveExclusiveTransitionEvaluator(TransitionEvaluationStrategy strategy,\n            TransitionConditionEvaluator evaluator, DefaultTransitionGetter defaultTransitionGetter) {\n        this.strategy = strategy;\n        this.evaluator = evaluator;\n        this.defaultTransitionGetter = defaultTransitionGetter;\n    }\n\n    public List<STransitionDefinition> evaluateTransitions(final SProcessDefinition sDefinition,\n            final SFlowNodeInstance flowNodeInstance,\n            FlowNodeTransitionsWrapper transitions, final SExpressionContext sExpressionContext)\n            throws SBonitaException {\n        List<STransitionDefinition> outgoingTransitionDefinitions = transitions\n                .getNonDefaultOutgoingTransitionDefinitions();\n        final List<STransitionDefinition> chosenTransitions = evaluateNonDefaultTransitions(sExpressionContext,\n                outgoingTransitionDefinitions);\n\n        if (chosenTransitions.isEmpty()) {\n            STransitionDefinition defaultTransition = defaultTransitionGetter.getDefaultTransition(transitions,\n                    sDefinition, flowNodeInstance);\n            chosenTransitions.add(defaultTransition);\n        }\n\n        return chosenTransitions;\n    }\n\n    private List<STransitionDefinition> evaluateNonDefaultTransitions(final SExpressionContext sExpressionContext,\n            final List<STransitionDefinition> outgoingTransitionDefinitions) throws SBonitaException {\n        final List<STransitionDefinition> chosenTransitions = new ArrayList<>(outgoingTransitionDefinitions.size());\n        boolean found = false;\n        Iterator<STransitionDefinition> iterator = outgoingTransitionDefinitions.iterator();\n        while (iterator.hasNext() && strategy.shouldContinue(found)) {\n            STransitionDefinition transitionDefinition = iterator.next();\n            Boolean shouldTakeTransition = evaluator.evaluateCondition(transitionDefinition, sExpressionContext);\n            if (!transitionDefinition.hasCondition() || (shouldTakeTransition != null && shouldTakeTransition)) {\n                chosenTransitions.add(transitionDefinition);\n                found = true;\n            }\n        }\n        return chosenTransitions;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/InclusiveGatewayTransitionEvaluationStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.transition;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class InclusiveGatewayTransitionEvaluationStrategy implements TransitionEvaluationStrategy {\n\n    @Override\n    public boolean shouldContinue(final boolean alreadyFound) {\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/IntermediateCatchEventStates.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.transition;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.execution.state.AbortedFlowNodeState;\nimport org.bonitasoft.engine.execution.state.AbortingBoundaryAndIntermediateCatchEventState;\nimport org.bonitasoft.engine.execution.state.CancelledFlowNodeState;\nimport org.bonitasoft.engine.execution.state.CancellingBoundaryAndIntermediateCatchEventState;\nimport org.bonitasoft.engine.execution.state.CompletedActivityState;\nimport org.bonitasoft.engine.execution.state.ExecutingFlowNodeState;\nimport org.bonitasoft.engine.execution.state.InitializingActivityState;\nimport org.bonitasoft.engine.execution.state.WaitingFlowNodeState;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class IntermediateCatchEventStates extends FlowNodeStateSequences {\n\n    public SFlowNodeType getFlowNodeType() {\n        return SFlowNodeType.INTERMEDIATE_CATCH_EVENT;\n    }\n\n    public IntermediateCatchEventStates(CompletedActivityState completed, AbortedFlowNodeState aborted,\n            CancelledFlowNodeState cancelled,\n            CancellingBoundaryAndIntermediateCatchEventState cancelingBoundaryAndIntermediateCatchEvent,\n            ExecutingFlowNodeState executing, InitializingActivityState initializing,\n            WaitingFlowNodeState waiting,\n            AbortingBoundaryAndIntermediateCatchEventState abortingBoundaryAndIntermediateCatchEvent) {\n\n        defineNormalSequence(initializing, waiting, executing, completed);\n        defineAbortSequence(abortingBoundaryAndIntermediateCatchEvent, aborted);\n        defineCancelSequence(cancelingBoundaryAndIntermediateCatchEvent, cancelled);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/IntermediateThrowEventStates.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.transition;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.execution.state.AbortedFlowNodeState;\nimport org.bonitasoft.engine.execution.state.AbortingFlowNodeState;\nimport org.bonitasoft.engine.execution.state.CancelledFlowNodeState;\nimport org.bonitasoft.engine.execution.state.CancellingFlowNodeState;\nimport org.bonitasoft.engine.execution.state.CompletedActivityState;\nimport org.bonitasoft.engine.execution.state.ExecutingThrowEventState;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class IntermediateThrowEventStates extends FlowNodeStateSequences {\n\n    public SFlowNodeType getFlowNodeType() {\n        return SFlowNodeType.INTERMEDIATE_THROW_EVENT;\n    }\n\n    public IntermediateThrowEventStates(\n            CompletedActivityState completed,\n            AbortedFlowNodeState aborted,\n            CancelledFlowNodeState cancelled,\n            AbortingFlowNodeState abortingFlowNode,\n            CancellingFlowNodeState cancellingFlowNode,\n            ExecutingThrowEventState executingThrowEvent) {\n\n        defineNormalSequence(executingThrowEvent, completed);\n        defineAbortSequence(abortingFlowNode, aborted);\n        defineCancelSequence(cancellingFlowNode, cancelled);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/LoopActivityStates.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.transition;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.execution.state.AbortedFlowNodeState;\nimport org.bonitasoft.engine.execution.state.AbortingActivityWithBoundaryState;\nimport org.bonitasoft.engine.execution.state.AbortingBoundaryEventsOnCompletingActivityState;\nimport org.bonitasoft.engine.execution.state.AbortingFlowNodeContainerState;\nimport org.bonitasoft.engine.execution.state.CancelledFlowNodeState;\nimport org.bonitasoft.engine.execution.state.CancellingActivityWithBoundaryState;\nimport org.bonitasoft.engine.execution.state.CancellingFlowNodeContainerChildrenState;\nimport org.bonitasoft.engine.execution.state.CompletedActivityState;\nimport org.bonitasoft.engine.execution.state.ExecutingLoopActivityState;\nimport org.bonitasoft.engine.execution.state.InitializingLoopActivityState;\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class LoopActivityStates extends FlowNodeStateSequences {\n\n    public SFlowNodeType getFlowNodeType() {\n        return SFlowNodeType.LOOP_ACTIVITY;\n    }\n\n    public LoopActivityStates(\n            AbortingBoundaryEventsOnCompletingActivityState abortingBoundaryEventsOnCompletingActivityState,\n            CompletedActivityState completed,\n            AbortingActivityWithBoundaryState abortingActivityWithBoundary,\n            AbortedFlowNodeState aborted,\n            @Qualifier(\"cancellingActivityWithBoundaryState\") CancellingActivityWithBoundaryState cancellingActivityWithBoundary,\n            CancelledFlowNodeState cancelled,\n            CancellingFlowNodeContainerChildrenState cancellingContainer,\n            @Qualifier(\"abortingFlowNodeContainerState\") AbortingFlowNodeContainerState abortingContainer,\n            InitializingLoopActivityState initializingLoop,\n            ExecutingLoopActivityState executingLoop) {\n\n        defineNormalSequence(initializingLoop, executingLoop,\n                abortingBoundaryEventsOnCompletingActivityState, completed);\n        defineAbortSequence(abortingActivityWithBoundary, abortingContainer, aborted);\n        defineCancelSequence(cancellingActivityWithBoundary, cancellingContainer, cancelled);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/ManualTaskStates.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.transition;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.execution.state.AbortedFlowNodeState;\nimport org.bonitasoft.engine.execution.state.AbortingFlowNodeContainerState;\nimport org.bonitasoft.engine.execution.state.AbortingSubTaskState;\nimport org.bonitasoft.engine.execution.state.CancelledFlowNodeState;\nimport org.bonitasoft.engine.execution.state.CancellingFlowNodeContainerChildrenState;\nimport org.bonitasoft.engine.execution.state.CompletedActivityState;\nimport org.bonitasoft.engine.execution.state.InitializingActivityState;\nimport org.bonitasoft.engine.execution.state.ReadyActivityState;\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class ManualTaskStates extends FlowNodeStateSequences {\n\n    public SFlowNodeType getFlowNodeType() {\n        return SFlowNodeType.MANUAL_TASK;\n    }\n\n    public ManualTaskStates(InitializingActivityState initializing,\n            ReadyActivityState ready,\n            AbortingSubTaskState abortingSubTaskState,\n            CompletedActivityState completed,\n            @Qualifier(\"abortingFlowNodeContainerState\") AbortingFlowNodeContainerState abortingContainer,\n            AbortedFlowNodeState aborted,\n            CancellingFlowNodeContainerChildrenState cancellingContainer,\n            CancelledFlowNodeState cancelled) {\n\n        defineNormalSequence(initializing, ready, abortingSubTaskState, completed);\n        defineAbortSequence(abortingContainer, aborted);\n        defineCancelSequence(cancellingContainer, cancelled);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/MultiInstanceActivityStates.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.transition;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.execution.state.AbortedFlowNodeState;\nimport org.bonitasoft.engine.execution.state.AbortingActivityWithBoundaryState;\nimport org.bonitasoft.engine.execution.state.AbortingBoundaryEventsOnCompletingActivityState;\nimport org.bonitasoft.engine.execution.state.AbortingFlowNodeContainerState;\nimport org.bonitasoft.engine.execution.state.CancelledFlowNodeState;\nimport org.bonitasoft.engine.execution.state.CancellingActivityWithBoundaryState;\nimport org.bonitasoft.engine.execution.state.CancellingFlowNodeContainerChildrenState;\nimport org.bonitasoft.engine.execution.state.CompletedActivityState;\nimport org.bonitasoft.engine.execution.state.ExecutingMultiInstanceActivityState;\nimport org.bonitasoft.engine.execution.state.InitializingMultiInstanceActivityState;\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class MultiInstanceActivityStates extends FlowNodeStateSequences {\n\n    public SFlowNodeType getFlowNodeType() {\n        return SFlowNodeType.MULTI_INSTANCE_ACTIVITY;\n    }\n\n    public MultiInstanceActivityStates(\n            AbortingBoundaryEventsOnCompletingActivityState abortingBoundaryEventsOnCompletingActivityState,\n            CompletedActivityState completed,\n            AbortingActivityWithBoundaryState abortingActivityWithBoundary,\n            AbortedFlowNodeState aborted,\n            @Qualifier(\"cancellingActivityWithBoundaryState\") CancellingActivityWithBoundaryState cancellingActivityWithBoundary,\n            CancelledFlowNodeState cancelled,\n            CancellingFlowNodeContainerChildrenState cancellingContainer,\n            @Qualifier(\"abortingFlowNodeContainerState\") AbortingFlowNodeContainerState abortingContainer,\n            InitializingMultiInstanceActivityState initializingMultiInstance,\n            ExecutingMultiInstanceActivityState executingMultiInstance) {\n\n        defineNormalSequence(initializingMultiInstance, executingMultiInstance,\n                abortingBoundaryEventsOnCompletingActivityState, completed);\n        defineAbortSequence(abortingActivityWithBoundary, abortingContainer, aborted);\n        defineCancelSequence(cancellingActivityWithBoundary, cancellingContainer, cancelled);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/ParallelGatewayTransitionEvaluator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.transition;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\nimport org.bonitasoft.engine.execution.flowmerger.FlowNodeTransitionsWrapper;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ParallelGatewayTransitionEvaluator {\n\n    public List<STransitionDefinition> evaluateTransitions(FlowNodeTransitionsWrapper transitions) {\n        return new ArrayList<>(transitions.getNonDefaultOutgoingTransitionDefinitions());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/ReceiveTaskStates.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.transition;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.execution.state.AbortedFlowNodeState;\nimport org.bonitasoft.engine.execution.state.AbortingActivityWithBoundaryState;\nimport org.bonitasoft.engine.execution.state.AbortingBoundaryEventsOnCompletingActivityState;\nimport org.bonitasoft.engine.execution.state.AbortingReceiveTaskState;\nimport org.bonitasoft.engine.execution.state.CancelledFlowNodeState;\nimport org.bonitasoft.engine.execution.state.CancellingActivityWithBoundaryState;\nimport org.bonitasoft.engine.execution.state.CancellingReceiveTaskState;\nimport org.bonitasoft.engine.execution.state.CompletedActivityState;\nimport org.bonitasoft.engine.execution.state.ExecutingFlowNodeState;\nimport org.bonitasoft.engine.execution.state.InitializingActivityWithBoundaryEventsState;\nimport org.bonitasoft.engine.execution.state.WaitingFlowNodeState;\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class ReceiveTaskStates extends FlowNodeStateSequences {\n\n    public SFlowNodeType getFlowNodeType() {\n        return SFlowNodeType.RECEIVE_TASK;\n    }\n\n    public ReceiveTaskStates(InitializingActivityWithBoundaryEventsState initializingActivityWithBoundary,\n            AbortingBoundaryEventsOnCompletingActivityState abortingBoundaryEventsOnCompletingActivityState,\n            CompletedActivityState completed,\n            AbortingActivityWithBoundaryState abortingActivityWithBoundary,\n            AbortedFlowNodeState aborted,\n            @Qualifier(\"cancellingActivityWithBoundaryState\") CancellingActivityWithBoundaryState cancellingActivityWithBoundary,\n            ExecutingFlowNodeState executing,\n            AbortingReceiveTaskState abortingReceiveTask,\n            @Qualifier(\"cancellingReceiveTaskState\") CancellingReceiveTaskState cancellingReceiveTask,\n            CancelledFlowNodeState cancelled,\n            WaitingFlowNodeState waiting) {\n\n        defineNormalSequence(initializingActivityWithBoundary, waiting, executing,\n                abortingBoundaryEventsOnCompletingActivityState, completed);\n        defineAbortSequence(abortingActivityWithBoundary, abortingReceiveTask, aborted);\n        defineCancelSequence(cancellingActivityWithBoundary, cancellingReceiveTask, cancelled);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/STransitionConditionEvaluationException.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.transition;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SExceptionContext;\nimport org.bonitasoft.engine.commons.exceptions.ScopedException;\n\npublic class STransitionConditionEvaluationException extends SBonitaException {\n\n    public STransitionConditionEvaluationException(String message, String transitionName, String targetFlowNode,\n            Throwable cause) {\n        super(message, ScopedException.OUTGOING_TRANSITION, cause);\n        if (transitionName != null) {\n            getContext().put(SExceptionContext.TRANSITION_NAME, transitionName);\n        }\n        if (targetFlowNode != null) {\n            getContext().put(SExceptionContext.TRANSITION_TARGET_FLOWNODE_NAME, targetFlowNode);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/SendTaskStates.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.transition;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.execution.state.AbortedFlowNodeState;\nimport org.bonitasoft.engine.execution.state.AbortingActivityWithBoundaryState;\nimport org.bonitasoft.engine.execution.state.AbortingBoundaryEventsOnCompletingActivityState;\nimport org.bonitasoft.engine.execution.state.AbortingReceiveTaskState;\nimport org.bonitasoft.engine.execution.state.CancelledFlowNodeState;\nimport org.bonitasoft.engine.execution.state.CancellingActivityWithBoundaryState;\nimport org.bonitasoft.engine.execution.state.CancellingReceiveTaskState;\nimport org.bonitasoft.engine.execution.state.CompletedActivityState;\nimport org.bonitasoft.engine.execution.state.ExecutingAutomaticActivityState;\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class SendTaskStates extends FlowNodeStateSequences {\n\n    public SFlowNodeType getFlowNodeType() {\n        return SFlowNodeType.SEND_TASK;\n    }\n\n    public SendTaskStates(\n            AbortingBoundaryEventsOnCompletingActivityState abortingBoundaryEventsOnCompletingActivityState,\n            CompletedActivityState completed,\n            AbortingActivityWithBoundaryState abortingActivityWithBoundary,\n            AbortedFlowNodeState aborted,\n            @Qualifier(\"cancellingActivityWithBoundaryState\") CancellingActivityWithBoundaryState cancellingActivityWithBoundary,\n            CancelledFlowNodeState cancelled,\n            ExecutingAutomaticActivityState executingAutomaticActivity,\n            @Qualifier(\"cancellingReceiveTaskState\") CancellingReceiveTaskState cancellingReceiveTask,\n            AbortingReceiveTaskState abortingReceiveTask) {\n\n        defineNormalSequence(executingAutomaticActivity, abortingBoundaryEventsOnCompletingActivityState,\n                completed);\n        defineAbortSequence(abortingActivityWithBoundary, abortingReceiveTask, aborted);\n        defineCancelSequence(cancellingActivityWithBoundary, cancellingReceiveTask, cancelled);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/StartEventStates.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.transition;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.execution.state.AbortedFlowNodeState;\nimport org.bonitasoft.engine.execution.state.AbortingFlowNodeState;\nimport org.bonitasoft.engine.execution.state.CancelledFlowNodeState;\nimport org.bonitasoft.engine.execution.state.CancellingFlowNodeState;\nimport org.bonitasoft.engine.execution.state.CompletedActivityState;\nimport org.bonitasoft.engine.execution.state.InitializingAndExecutingFlowNodeState;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class StartEventStates extends FlowNodeStateSequences {\n\n    public SFlowNodeType getFlowNodeType() {\n        return SFlowNodeType.START_EVENT;\n    }\n\n    public StartEventStates(CompletedActivityState completed,\n            AbortedFlowNodeState aborted,\n            CancelledFlowNodeState cancelled,\n            AbortingFlowNodeState abortingFlowNode,\n            CancellingFlowNodeState cancellingFlowNode,\n            InitializingAndExecutingFlowNodeState initializingAndExecuting) {\n\n        defineNormalSequence(initializingAndExecuting, completed);\n        defineAbortSequence(abortingFlowNode, aborted);\n        defineCancelSequence(cancellingFlowNode, cancelled);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/SubProcessActivityTaskStates.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.transition;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.execution.state.AbortedFlowNodeState;\nimport org.bonitasoft.engine.execution.state.AbortingCallActivityState;\nimport org.bonitasoft.engine.execution.state.CancelledFlowNodeState;\nimport org.bonitasoft.engine.execution.state.CancellingCallActivityState;\nimport org.bonitasoft.engine.execution.state.CompletedActivityState;\nimport org.bonitasoft.engine.execution.state.ExecutingCallActivityState;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class SubProcessActivityTaskStates extends FlowNodeStateSequences {\n\n    public SFlowNodeType getFlowNodeType() {\n        return SFlowNodeType.SUB_PROCESS;\n    }\n\n    public SubProcessActivityTaskStates(CompletedActivityState completed,\n            AbortedFlowNodeState aborted,\n            CancelledFlowNodeState cancelled,\n            ExecutingCallActivityState executingCallActivity,\n            AbortingCallActivityState abortingCallActivity,\n            CancellingCallActivityState cancellingCallActivity) {\n\n        defineNormalSequence(executingCallActivity, completed);\n        defineAbortSequence(abortingCallActivity, aborted);\n        defineCancelSequence(cancellingCallActivity, cancelled);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/TransitionConditionEvaluator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.transition;\n\nimport java.util.Objects;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class TransitionConditionEvaluator {\n\n    private final ExpressionResolverService resolverService;\n\n    public TransitionConditionEvaluator(ExpressionResolverService resolverService) {\n        this.resolverService = resolverService;\n    }\n\n    public Boolean evaluateCondition(final STransitionDefinition sTransitionDefinition,\n            final SExpressionContext contextDependency) throws SBonitaException {\n        SExpression condition = sTransitionDefinition.getCondition();\n        if (condition == null) {// no condition == true but return null to say it was a transition without condition --\n            return null;\n        }\n        if (!Boolean.class.getName().equals(condition.getReturnType())) {\n            throw new STransitionConditionEvaluationException(\n                    \"Condition expression must return a boolean\",\n                    getTransitionName(sTransitionDefinition),\n                    getTargetFlowNode(sTransitionDefinition, contextDependency),\n                    new SExpressionEvaluationException(\"Invalid expression return type\", condition.getName()));\n        }\n        try {\n            return (Boolean) resolverService.evaluate(condition, contextDependency);\n        } catch (SBonitaException e) {\n            throw new STransitionConditionEvaluationException(\n                    \"Unable to evaluate transition condition\",\n                    getTransitionName(sTransitionDefinition),\n                    getTargetFlowNode(sTransitionDefinition, contextDependency), e);\n        }\n    }\n\n    private static String getTransitionName(STransitionDefinition transition) {\n        if (Objects.equals(transition.getName(), transition.getSource() + \"_->_\" + transition.getTarget())) {\n            return null;\n        }\n        return transition.getName();\n    }\n\n    private static String getTargetFlowNode(STransitionDefinition sTransitionDefinition,\n            SExpressionContext contextDependency) {\n        if (contextDependency.getProcessDefinition() != null\n                && contextDependency.getProcessDefinition().getProcessContainer() != null) {\n            var targetFlowNode = contextDependency.getProcessDefinition().getProcessContainer()\n                    .getFlowNode(sTransitionDefinition.getTarget());\n            if (targetFlowNode == null) {\n                return null;\n            }\n            return targetFlowNode.getType().name().toLowerCase() + \"::\" + targetFlowNode.getName();\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/TransitionEvaluationStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.transition;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface TransitionEvaluationStrategy {\n\n    boolean shouldContinue(boolean alreadyFound);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/UserTaskStates.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.transition;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.execution.state.AbortedFlowNodeState;\nimport org.bonitasoft.engine.execution.state.AbortingActivityWithBoundaryState;\nimport org.bonitasoft.engine.execution.state.AbortingBoundaryEventsOnCompletingActivityState;\nimport org.bonitasoft.engine.execution.state.AbortingFlowNodeContainerState;\nimport org.bonitasoft.engine.execution.state.AbortingSubTaskState;\nimport org.bonitasoft.engine.execution.state.CancelledFlowNodeState;\nimport org.bonitasoft.engine.execution.state.CancellingActivityWithBoundaryState;\nimport org.bonitasoft.engine.execution.state.CancellingFlowNodeContainerChildrenState;\nimport org.bonitasoft.engine.execution.state.CompletedActivityState;\nimport org.bonitasoft.engine.execution.state.InitializingActivityWithBoundaryEventsState;\nimport org.bonitasoft.engine.execution.state.ReadyActivityState;\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class UserTaskStates extends FlowNodeStateSequences {\n\n    public SFlowNodeType getFlowNodeType() {\n        return SFlowNodeType.USER_TASK;\n    }\n\n    public UserTaskStates(InitializingActivityWithBoundaryEventsState initializingActivityWithBoundary,\n            ReadyActivityState ready,\n            AbortingBoundaryEventsOnCompletingActivityState abortingBoundaryEventsOnCompletingActivityState,\n            AbortingSubTaskState abortingSubTaskState,\n            CompletedActivityState completed,\n            AbortingActivityWithBoundaryState abortingActivityWithBoundary,\n            @Qualifier(\"abortingFlowNodeContainerState\") AbortingFlowNodeContainerState abortingContainer,\n            AbortedFlowNodeState aborted,\n            @Qualifier(\"cancellingActivityWithBoundaryState\") CancellingActivityWithBoundaryState cancellingActivityWithBoundary,\n            CancellingFlowNodeContainerChildrenState cancellingContainer,\n            CancelledFlowNodeState cancelled) {\n\n        defineNormalSequence(initializingActivityWithBoundary, ready,\n                abortingBoundaryEventsOnCompletingActivityState, abortingSubTaskState, completed);\n        defineAbortSequence(abortingActivityWithBoundary, abortingContainer, aborted);\n        defineCancelSequence(cancellingActivityWithBoundary, cancellingContainer, cancelled);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/BPMWorkFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work;\n\nimport static java.util.Arrays.stream;\nimport static java.util.Collections.emptyList;\nimport static java.util.stream.Collectors.toList;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent;\nimport org.bonitasoft.engine.execution.FlowNodeSelector;\nimport org.bonitasoft.engine.execution.work.failurewrapping.ConnectorDefinitionAndInstanceContextWork;\nimport org.bonitasoft.engine.execution.work.failurewrapping.FlowNodeDefinitionAndInstanceContextWork;\nimport org.bonitasoft.engine.execution.work.failurewrapping.MessageInstanceContextWork;\nimport org.bonitasoft.engine.execution.work.failurewrapping.ProcessDefinitionContextWork;\nimport org.bonitasoft.engine.execution.work.failurewrapping.ProcessInstanceContextWork;\nimport org.bonitasoft.engine.execution.work.failurewrapping.TriggerSignalWork;\nimport org.bonitasoft.engine.work.BonitaWork;\nimport org.bonitasoft.engine.work.WorkDescriptor;\nimport org.bonitasoft.engine.work.WorkFactory;\n\n/**\n * Factory to construct works\n *\n * @author Baptiste Mesta\n * @author Celine Souchet\n * @author Matthieu Chaffotte\n */\npublic class BPMWorkFactory implements WorkFactory {\n\n    private static final String EXECUTE_ACTIVITY_CONNECTOR = \"EXECUTE_ACTIVITY_CONNECTOR\";\n    private static final String EXECUTE_PROCESS_CONNECTOR = \"EXECUTE_PROCESS_CONNECTOR\";\n    private static final String EXECUTE_FLOWNODE = \"EXECUTE_FLOWNODE\";\n    private static final String FINISH_FLOWNODE = \"FINISH_FLOWNODE\";\n    private static final String EXECUTE_MESSAGE = \"EXECUTE_MESSAGE\";\n    private static final String TRIGGER_SIGNAL = \"TRIGGER_SIGNAL\";\n    private static final String PROCESS_DEFINITION_ID = \"processDefinitionId\";\n    private static final String PROCESS_INSTANCE_ID = \"processInstanceId\";\n    private static final String FLOW_NODE_DEFINITION_ID = \"flowNodeDefinitionId\";\n    private static final String FLOW_NODE_INSTANCE_ID = \"flowNodeInstanceId\";\n    private static final String CONNECTOR_INSTANCE_ID = \"connectorInstanceId\";\n    private static final String CONNECTOR_DEFINITION_NAME = \"connectorDefinitionName\";\n    private static final String CONNECTOR_DEFINITION_ID = \"connectorDefinitionId\";\n    private static final String ROOT_PROCESS_INSTANCE_ID = \"rootProcessInstanceId\";\n    public static final String STATE_ID = \"stateId\";\n    private static final String STATE_EXECUTING = \"stateExecuting\";\n    private static final String STATE_ABORTING = \"stateAborting\";\n    private static final String STATE_CANCELING = \"stateCanceling\";\n    private static final String ACTIVATION_EVENT = \"activationEvent\";\n    private static final String SUB_PROCESS_DEFINITION_ID = \"subProcessDefinitionId\";\n    private static final String FLOW_NODE_DEFINITIONS_FILTER = \"flowNodeDefinitionsFilter\";\n    private static final String MESSAGE_INSTANCE_ID = \"messageInstanceId\";\n    private static final String MESSAGE_INSTANCE_NAME = \"messageInstanceName\";\n    private static final String MESSAGE_INSTANCE_TARGET_PROCESS = \"messageInstanceTargetProcess\";\n    private static final String MESSAGE_INSTANCE_TARGET_FLOW_NODE = \"messageInstanceTargetFlowNode\";\n    private static final String WAITING_MESSAGE_ID = \"waitingMessageId\";\n    private static final String WAITING_MESSAGE_EVENT_TYPE = \"waitingMessageEventType\";\n    private static final String PARENT_TYPE = \"parentType\";\n    private static final String PARENT_ID = \"parentId\";\n    private static final String LISTENING_SIGNAL_ID = \"listeningSignalId\";\n    private static final String LISTENING_SIGNAL_NAME = \"listeningSignalName\";\n\n    private Map<String, Function<WorkDescriptor, BonitaWork>> extensions = new HashMap<>();\n\n    private BonitaWork createExecuteConnectorOfActivity(WorkDescriptor workDescriptor) {\n        final long processDefinitionId = workDescriptor.getLong(PROCESS_DEFINITION_ID);\n        final long processInstanceId = workDescriptor.getLong(PROCESS_INSTANCE_ID);\n        final long rootProcessInstanceId = workDescriptor.getLong(ROOT_PROCESS_INSTANCE_ID);\n        final long flowNodeInstanceId = workDescriptor.getLong(FLOW_NODE_INSTANCE_ID);\n        final long connectorInstanceId = workDescriptor.getLong(CONNECTOR_INSTANCE_ID);\n        final String connectorDefinitionId = workDescriptor.getString(CONNECTOR_DEFINITION_ID);\n        final String connectorDefinitionName = workDescriptor.getString(CONNECTOR_DEFINITION_NAME);\n        final ConnectorEvent activationEvent = ConnectorEvent.valueOf(workDescriptor.getString(ACTIVATION_EVENT));\n        BonitaWork wrappedWork = new ExecuteConnectorOfActivity(processDefinitionId, processInstanceId,\n                workDescriptor.getLong(FLOW_NODE_DEFINITION_ID), flowNodeInstanceId,\n                connectorInstanceId, connectorDefinitionName);\n        wrappedWork = new ConnectorDefinitionAndInstanceContextWork(wrappedWork, connectorDefinitionId,\n                connectorDefinitionName,\n                connectorInstanceId, activationEvent);\n        wrappedWork = withFlowNodeContext(processDefinitionId, processInstanceId, rootProcessInstanceId,\n                flowNodeInstanceId, wrappedWork);\n        return withSession(wrappedWork);\n    }\n\n    public WorkDescriptor createExecuteConnectorOfActivityDescriptor(final long processDefinitionId,\n            final long processInstanceId, final long rootProcessInstanceId,\n            final long flowNodeDefinitionId,\n            final long flowNodeInstanceId, final long connectorInstanceId,\n            final String connectorDefinitionId,\n            final String connectorDefinitionName, final String activationEvent) {\n        return WorkDescriptor.create(EXECUTE_ACTIVITY_CONNECTOR)\n                .withParameter(PROCESS_DEFINITION_ID, processDefinitionId)\n                .withParameter(PROCESS_INSTANCE_ID, processInstanceId)\n                .withParameter(ROOT_PROCESS_INSTANCE_ID, rootProcessInstanceId)\n                .withParameter(FLOW_NODE_DEFINITION_ID, flowNodeDefinitionId)\n                .withParameter(FLOW_NODE_INSTANCE_ID, flowNodeInstanceId)\n                .withParameter(CONNECTOR_INSTANCE_ID, connectorInstanceId)\n                .withParameter(CONNECTOR_DEFINITION_ID, connectorDefinitionId)\n                .withParameter(CONNECTOR_DEFINITION_NAME, connectorDefinitionName)\n                .withParameter(ACTIVATION_EVENT, activationEvent);\n    }\n\n    private BonitaWork createExecuteConnectorOfProcess(WorkDescriptor workDescriptor) {\n        long processDefinitionId = workDescriptor.getLong(PROCESS_DEFINITION_ID);\n        long processInstanceId = workDescriptor.getLong(PROCESS_INSTANCE_ID);\n        long rootProcessInstanceId = workDescriptor.getLong(ROOT_PROCESS_INSTANCE_ID);\n        long connectorInstanceId = workDescriptor.getLong(CONNECTOR_INSTANCE_ID);\n        String connectorDefinitionId = workDescriptor.getString(CONNECTOR_DEFINITION_ID);\n        String connectorDefinitionName = workDescriptor.getString(CONNECTOR_DEFINITION_NAME);\n        ConnectorEvent activationEvent = (ConnectorEvent.valueOf(workDescriptor.getString(ACTIVATION_EVENT)));\n        String flowNodeIds = workDescriptor.getString(FLOW_NODE_DEFINITIONS_FILTER);\n        List<Long> flowNodeDefinitionsFilter = getListOfFlowNodeDefinitionsToStart(flowNodeIds);\n        Long subProcessDefinitionId = workDescriptor.getLong(SUB_PROCESS_DEFINITION_ID);\n\n        BonitaWork wrappedWork = withConnectorContext(connectorInstanceId, connectorDefinitionId,\n                connectorDefinitionName, activationEvent,\n                withProcessContext(processDefinitionId, processInstanceId,\n                        rootProcessInstanceId,\n                        new ExecuteConnectorOfProcess(processDefinitionId, connectorInstanceId, connectorDefinitionName,\n                                processInstanceId,\n                                rootProcessInstanceId, activationEvent, flowNodeDefinitionsFilter,\n                                subProcessDefinitionId)));\n        return withSession(wrappedWork);\n    }\n\n    private List<Long> getListOfFlowNodeDefinitionsToStart(String flowNodeIds) {\n        if (flowNodeIds == null) {\n            //no flow node selector\n            return null;\n        }\n        if (flowNodeIds.isEmpty()) {\n            //a flow node selector that selects no elements\n            return emptyList();\n        }\n        return stream(flowNodeIds.split(\",\")).map(Long::valueOf).collect(toList());\n    }\n\n    public WorkDescriptor createExecuteConnectorOfProcessDescriptor(final long processDefinitionId,\n            final long processInstanceId, final long rootProcessInstanceId,\n            final long connectorInstanceId, final String connectorDefinitionId, final String connectorDefinitionName,\n            final ConnectorEvent activationEvent,\n            final FlowNodeSelector flowNodeSelector) {\n        return WorkDescriptor.create(EXECUTE_PROCESS_CONNECTOR)\n                .withParameter(PROCESS_DEFINITION_ID, processDefinitionId)\n                .withParameter(PROCESS_INSTANCE_ID, processInstanceId)\n                .withParameter(ROOT_PROCESS_INSTANCE_ID, rootProcessInstanceId)\n                .withParameter(CONNECTOR_INSTANCE_ID, connectorInstanceId)\n                .withParameter(CONNECTOR_DEFINITION_ID, connectorDefinitionId)\n                .withParameter(CONNECTOR_DEFINITION_NAME, connectorDefinitionName)\n                .withParameter(ACTIVATION_EVENT, activationEvent.name())\n                .withParameter(FLOW_NODE_DEFINITIONS_FILTER,\n                        flowNodeSelector != null ? flowNodeSelector.getFilteredElements().stream()\n                                .map(f -> f.getId().toString()).collect(Collectors.joining(\",\")) : null)\n                .withParameter(SUB_PROCESS_DEFINITION_ID,\n                        flowNodeSelector != null ? flowNodeSelector.getSubProcessDefinitionId() : null);\n    }\n\n    private InSessionBonitaWork withSession(BonitaWork wrappedWork) {\n        return new InSessionBonitaWork(wrappedWork);\n    }\n\n    private BonitaWork withConnectorContext(long connectorInstanceId,\n            String connectorDefinitionId,\n            String connectorDefinitionName,\n            ConnectorEvent activationEvent,\n            ProcessInstanceContextWork processInstanceContextWork) {\n        return new ConnectorDefinitionAndInstanceContextWork(processInstanceContextWork, connectorDefinitionId,\n                connectorDefinitionName,\n                connectorInstanceId,\n                activationEvent);\n    }\n\n    public WorkDescriptor createExecuteFlowNodeWorkDescriptor(SFlowNodeInstance flowNodeInstance) {\n        return WorkDescriptor.create(EXECUTE_FLOWNODE)\n                .withParameter(PROCESS_DEFINITION_ID, flowNodeInstance.getProcessDefinitionId())\n                .withParameter(PROCESS_INSTANCE_ID, flowNodeInstance.getParentProcessInstanceId())\n                .withParameter(ROOT_PROCESS_INSTANCE_ID, flowNodeInstance.getRootProcessInstanceId())\n                .withParameter(FLOW_NODE_INSTANCE_ID, flowNodeInstance.getId())\n                .withParameter(STATE_ID, flowNodeInstance.getStateId())\n                .withParameter(STATE_EXECUTING, flowNodeInstance.isStateExecuting())\n                .withParameter(STATE_ABORTING, flowNodeInstance.isAborting())\n                .withParameter(STATE_CANCELING, flowNodeInstance.isCanceling());\n    }\n\n    private BonitaWork createExecuteFlowNodeWork(WorkDescriptor workDescriptor) {\n        final long processInstanceId = workDescriptor.getLong(PROCESS_INSTANCE_ID);\n        final long rootProcessInstanceId = workDescriptor.getLong(ROOT_PROCESS_INSTANCE_ID);\n        final long flowNodeInstanceId = workDescriptor.getLong(FLOW_NODE_INSTANCE_ID);\n        if (processInstanceId <= 0) {\n            throw new RuntimeException(\n                    \"It is forbidden to create a ExecuteFlowNodeWork with a processInstanceId equals to \"\n                            + processInstanceId);\n        }\n        BonitaWork wrappedWork = new ExecuteFlowNodeWork(flowNodeInstanceId,\n                workDescriptor.getInteger(STATE_ID),\n                workDescriptor.getBoolean(STATE_EXECUTING),\n                workDescriptor.getBoolean(STATE_ABORTING),\n                workDescriptor.getBoolean(STATE_CANCELING));\n        wrappedWork = withLock(processInstanceId, withTx(wrappedWork));\n        wrappedWork = withFlowNodeContext(workDescriptor.getLong(PROCESS_DEFINITION_ID), processInstanceId,\n                rootProcessInstanceId,\n                flowNodeInstanceId, wrappedWork);\n        return withSession(wrappedWork);\n    }\n\n    public WorkDescriptor createExecuteMessageCoupleWorkDescriptor(final SMessageInstance messageInstance,\n            final SWaitingMessageEvent waitingMessage) {\n        return WorkDescriptor.create(EXECUTE_MESSAGE)\n                .withParameter(MESSAGE_INSTANCE_ID, messageInstance.getId())\n                .withParameter(MESSAGE_INSTANCE_NAME, messageInstance.getMessageName())\n                .withParameter(MESSAGE_INSTANCE_TARGET_PROCESS, messageInstance.getTargetProcess())\n                .withParameter(MESSAGE_INSTANCE_TARGET_FLOW_NODE, messageInstance.getTargetFlowNode())\n                .withParameter(WAITING_MESSAGE_ID, waitingMessage.getId())\n                .withParameter(WAITING_MESSAGE_EVENT_TYPE, waitingMessage.getEventType().name())\n                .withParameter(PROCESS_INSTANCE_ID, waitingMessage.getParentProcessInstanceId())\n                .withParameter(PROCESS_DEFINITION_ID, waitingMessage.getProcessDefinitionId())\n                .withParameter(FLOW_NODE_INSTANCE_ID, waitingMessage.getFlowNodeInstanceId())\n                .withParameter(ROOT_PROCESS_INSTANCE_ID, waitingMessage.getRootProcessInstanceId());\n    }\n\n    private BonitaWork createExecuteMessageCoupleWork(WorkDescriptor workDescriptor) {\n        // no target process: we do not wrap in a LockProcessInstanceWork\n        long messageId = workDescriptor.getLong(MESSAGE_INSTANCE_ID);\n        String messageName = workDescriptor.getString(MESSAGE_INSTANCE_NAME);\n        String targetProcess = workDescriptor.getString(MESSAGE_INSTANCE_TARGET_PROCESS);\n        String targetFlowNode = workDescriptor.getString(MESSAGE_INSTANCE_TARGET_FLOW_NODE);\n\n        long waitingMessageId = workDescriptor.getLong(WAITING_MESSAGE_ID);\n        long parentProcessInstanceId = workDescriptor.getLong(PROCESS_INSTANCE_ID);\n        long processDefinitionId = workDescriptor.getLong(PROCESS_DEFINITION_ID);\n        long flowNodeInstanceId = workDescriptor.getLong(FLOW_NODE_INSTANCE_ID);\n        long rootProcessInstanceId = workDescriptor.getLong(ROOT_PROCESS_INSTANCE_ID);\n        String eventType = workDescriptor.getString(WAITING_MESSAGE_EVENT_TYPE);\n\n        BonitaWork wrappedWork = withTx(new ExecuteMessageCoupleWork(messageId, waitingMessageId));\n        if (parentProcessInstanceId > 0) {\n            wrappedWork = withLock(parentProcessInstanceId, wrappedWork);\n        }\n        wrappedWork = new MessageInstanceContextWork(wrappedWork, messageName, targetProcess,\n                targetFlowNode, eventType);\n        wrappedWork = withProcessContext(processDefinitionId, parentProcessInstanceId,\n                rootProcessInstanceId, flowNodeInstanceId, wrappedWork);\n        return withSession(wrappedWork);\n    }\n\n    private BonitaWork createNotifyChildFinishedWork(WorkDescriptor workDescriptor) {\n        final long processDefinitionId = workDescriptor.getLong(PROCESS_DEFINITION_ID);\n        final long processInstanceId = workDescriptor.getLong(PROCESS_INSTANCE_ID);\n        final long rootProcessInstanceId = workDescriptor.getLong(ROOT_PROCESS_INSTANCE_ID);\n        final long flowNodeInstanceId = workDescriptor.getLong(FLOW_NODE_INSTANCE_ID);\n        BonitaWork wrappedWork = new NotifyChildFinishedWork(processDefinitionId, flowNodeInstanceId,\n                workDescriptor.getInteger(STATE_ID),\n                workDescriptor.getBoolean(STATE_EXECUTING),\n                workDescriptor.getBoolean(STATE_ABORTING),\n                workDescriptor.getBoolean(STATE_CANCELING));\n        wrappedWork = withLock(processInstanceId, withTx(wrappedWork));\n        wrappedWork = withFlowNodeContext(processDefinitionId, processInstanceId, rootProcessInstanceId,\n                flowNodeInstanceId, wrappedWork);\n        return withSession(wrappedWork);\n    }\n\n    public WorkDescriptor createNotifyChildFinishedWorkDescriptor(SFlowNodeInstance sFlowNodeInstance) {\n        return WorkDescriptor.create(FINISH_FLOWNODE)\n                .withParameter(PROCESS_DEFINITION_ID, sFlowNodeInstance.getProcessDefinitionId())\n                .withParameter(PROCESS_INSTANCE_ID, sFlowNodeInstance.getParentProcessInstanceId())\n                .withParameter(ROOT_PROCESS_INSTANCE_ID, sFlowNodeInstance.getRootProcessInstanceId())\n                .withParameter(FLOW_NODE_INSTANCE_ID, sFlowNodeInstance.getId())\n                .withParameter(STATE_ID, sFlowNodeInstance.getStateId())\n                .withParameter(STATE_EXECUTING, sFlowNodeInstance.isStateExecuting())\n                .withParameter(STATE_ABORTING, sFlowNodeInstance.isAborting())\n                .withParameter(STATE_CANCELING, sFlowNodeInstance.isCanceling());\n    }\n\n    private BonitaWork withLock(long processInstanceId, BonitaWork work) {\n        return new LockProcessInstanceWork(work, processInstanceId);\n    }\n\n    private BonitaWork withTx(BonitaWork wrappedWork) {\n        return new TxBonitaWork(wrappedWork);\n    }\n\n    private BonitaWork withFlowNodeContext(final long processDefinitionId, final long processInstanceId,\n            final long rootProcessInstanceId,\n            final long flowNodeInstanceId, final BonitaWork wrappedWork) {\n        final ProcessDefinitionContextWork processDefinitionContextWork = new ProcessDefinitionContextWork(wrappedWork,\n                processDefinitionId);\n        final ProcessInstanceContextWork processInstanceContextWork = new ProcessInstanceContextWork(\n                processDefinitionContextWork, processInstanceId, rootProcessInstanceId);\n        return new FlowNodeDefinitionAndInstanceContextWork(processInstanceContextWork, flowNodeInstanceId);\n    }\n\n    private BonitaWork withProcessContext(final long processDefinitionId, final long processInstanceId,\n            final long rootProcessInstanceId, final long flowNodeInstanceId, final BonitaWork wrappedWork) {\n        final ProcessInstanceContextWork processInstanceContextWork = withProcessContext(processDefinitionId,\n                processInstanceId,\n                rootProcessInstanceId, wrappedWork);\n        return new FlowNodeDefinitionAndInstanceContextWork(processInstanceContextWork, flowNodeInstanceId);\n    }\n\n    private ProcessInstanceContextWork withProcessContext(final long processDefinitionId, final long processInstanceId,\n            final long rootProcessInstanceId, final BonitaWork wrappedWork) {\n        final ProcessDefinitionContextWork processDefinitionContextWork = new ProcessDefinitionContextWork(wrappedWork,\n                processDefinitionId);\n        return new ProcessInstanceContextWork(processDefinitionContextWork, processInstanceId, rootProcessInstanceId);\n    }\n\n    public WorkDescriptor createTriggerSignalWorkDescriptor(SWaitingSignalEvent listeningSignal) {\n        return WorkDescriptor.create(TRIGGER_SIGNAL)\n                .withParameter(LISTENING_SIGNAL_ID, listeningSignal.getId())\n                .withParameter(LISTENING_SIGNAL_NAME, listeningSignal.getSignalName())\n                .withParameter(PROCESS_INSTANCE_ID, listeningSignal.getParentProcessInstanceId());\n    }\n\n    private BonitaWork createTriggerSignalWork(WorkDescriptor workDescriptor) {\n        BonitaWork triggerSignalWork = new TriggerSignalWork(workDescriptor.getLong(LISTENING_SIGNAL_ID),\n                workDescriptor.getString(LISTENING_SIGNAL_NAME));\n        triggerSignalWork = withTx(triggerSignalWork);\n        long parentProcessInstanceId = workDescriptor.getLong(PROCESS_INSTANCE_ID);\n        if (parentProcessInstanceId > 0) {\n            triggerSignalWork = withLock(parentProcessInstanceId, triggerSignalWork);\n        }\n        return withSession(triggerSignalWork);\n    }\n\n    @Override\n    public BonitaWork create(WorkDescriptor workDescriptor) {\n        return switch (workDescriptor.getType()) {\n            case EXECUTE_ACTIVITY_CONNECTOR -> createExecuteConnectorOfActivity(workDescriptor);\n            case EXECUTE_PROCESS_CONNECTOR -> createExecuteConnectorOfProcess(workDescriptor);\n            case EXECUTE_FLOWNODE -> createExecuteFlowNodeWork(workDescriptor);\n            case FINISH_FLOWNODE -> createNotifyChildFinishedWork(workDescriptor);\n            case TRIGGER_SIGNAL -> createTriggerSignalWork(workDescriptor);\n            case EXECUTE_MESSAGE -> createExecuteMessageCoupleWork(workDescriptor);\n            default -> createFromExtension(workDescriptor);\n        };\n    }\n\n    private BonitaWork createFromExtension(WorkDescriptor workDescriptor) {\n        if (!extensions.containsKey(workDescriptor.getType())) {\n            throw new IllegalArgumentException(\"Unknown type of work:\" + workDescriptor.getType());\n        }\n        return extensions.get(workDescriptor.getType()).apply(workDescriptor);\n    }\n\n    public void addExtension(String workType, Function<WorkDescriptor, BonitaWork> workFactoryOfType) {\n        extensions.put(workType, workFactoryOfType);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/ExecuteConnectorOfActivity.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.ScopedException;\nimport org.bonitasoft.engine.core.connector.ConnectorResult;\nimport org.bonitasoft.engine.core.connector.exception.SConnectorDefinitionNotFoundException;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SConnectorDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SEndEventDefinitionBuilderFactory;\nimport org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SThrowErrorEventTriggerDefinitionBuilderFactory;\nimport org.bonitasoft.engine.core.process.definition.model.event.SEndEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerType;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowErrorEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.BPMFailureService;\nimport org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SEndEventInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SEndEventInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.event.SThrowEventInstance;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.execution.WaitingEventsInterrupter;\nimport org.bonitasoft.engine.execution.event.EventsHandler;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.work.WorkDescriptor;\nimport org.bonitasoft.engine.work.WorkService;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n * @author Matthieu Chaffotte\n */\npublic class ExecuteConnectorOfActivity extends ExecuteConnectorWork {\n\n    private final long flowNodeInstanceId;\n\n    private final long flowNodeDefinitionId;\n\n    ExecuteConnectorOfActivity(final long processDefinitionId, final long processInstanceId,\n            final long flowNodeDefinitionId, final long flowNodeInstanceId,\n            final long connectorInstanceId,\n            final String connectorDefinitionName) {\n        super(processDefinitionId, connectorInstanceId, connectorDefinitionName,\n                new SExpressionContext(flowNodeInstanceId,\n                        DataInstanceContainer.ACTIVITY_INSTANCE.name(), processDefinitionId),\n                processInstanceId);\n        this.flowNodeDefinitionId = flowNodeDefinitionId;\n        this.flowNodeInstanceId = flowNodeInstanceId;\n    }\n\n    @Override\n    protected void evaluateOutput(final Map<String, Object> context, final ConnectorResult result,\n            final SConnectorDefinition sConnectorDefinition)\n            throws SBonitaException {\n        evaluateOutput(context, result, sConnectorDefinition, flowNodeInstanceId,\n                DataInstanceContainer.ACTIVITY_INSTANCE.name());\n    }\n\n    @Override\n    protected void continueFlow(final Map<String, Object> context) throws SBonitaException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor(context);\n        final FlowNodeInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final WorkService workService = serviceAccessor.getWorkService();\n        final SFlowNodeInstance sFlowNodeInstance = activityInstanceService.getFlowNodeInstance(flowNodeInstanceId);\n        final WorkDescriptor executeFlowNodeWork = serviceAccessor.getBPMWorkFactory()\n                .createExecuteFlowNodeWorkDescriptor(sFlowNodeInstance);\n        workService.registerWork(executeFlowNodeWork);\n    }\n\n    @Override\n    protected void setContainerInFail(final Map<String, Object> context, Throwable t) throws SBonitaException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor(context);\n        WaitingEventsInterrupter waitingEventsInterrupter = new WaitingEventsInterrupter(\n                serviceAccessor.getEventInstanceService(),\n                serviceAccessor.getSchedulerService());\n        var activityInstanceService = serviceAccessor.getActivityInstanceService();\n        FailedStateSetter failedStateSetter = new FailedStateSetter(waitingEventsInterrupter,\n                activityInstanceService,\n                serviceAccessor.getFlowNodeStateManager());\n        failedStateSetter.setAsFailed(flowNodeInstanceId);\n        var failureService = serviceAccessor.getBpmFailureService();\n        failureService.createFlowNodeFailure(activityInstanceService.getFlowNodeInstance(flowNodeInstanceId),\n                new BPMFailureService.Failure(ScopedException.CONNECTOR, t));\n    }\n\n    @Override\n    protected SThrowEventInstance createThrowErrorEventInstance(final Map<String, Object> context,\n            final SEndEventDefinition eventDefinition)\n            throws SBonitaException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor(context);\n        final EventInstanceService eventInstanceService = serviceAccessor.getEventInstanceService();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n\n        final SFlowNodeInstance sFlowNodeInstance = activityInstanceService.getFlowNodeInstance(flowNodeInstanceId);\n        final SEndEventInstanceBuilder builder = BuilderFactory.get(SEndEventInstanceBuilderFactory.class)\n                .createNewEndEventInstance(eventDefinition.getName(),\n                        eventDefinition.getId(), sFlowNodeInstance.getRootContainerId(),\n                        sFlowNodeInstance.getParentContainerId(), processDefinitionId,\n                        sFlowNodeInstance.getRootContainerId(), sFlowNodeInstance.getParentProcessInstanceId());\n        builder.setParentActivityInstanceId(flowNodeInstanceId);\n        final SThrowEventInstance done = (SThrowEventInstance) builder.done();\n        eventInstanceService.createEventInstance(done);\n        return done;\n    }\n\n    @Override\n    protected void errorEventOnFail(final Map<String, Object> context, final SConnectorDefinition sConnectorDefinition,\n            final Throwable exception)\n            throws SBonitaException {\n        setConnectorAndContainerToFailed(context, exception);\n        final ServiceAccessor serviceAccessor = getServiceAccessor(context);\n        final EventsHandler eventsHandler = serviceAccessor.getEventsHandler();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n\n        final SFlowNodeInstance sFlowNodeInstance = activityInstanceService.getFlowNodeInstance(flowNodeInstanceId);\n\n        // create a fake definition\n        final String errorCode = sConnectorDefinition.getErrorCode();\n        final SThrowErrorEventTriggerDefinition errorEventTriggerDefinition = BuilderFactory\n                .get(SThrowErrorEventTriggerDefinitionBuilderFactory.class)\n                .createNewInstance(errorCode).done();\n        // event definition as the error code as name, this way we don't need to find the connector that throw this error\n        final SEndEventDefinition eventDefinition = BuilderFactory.get(SEndEventDefinitionBuilderFactory.class)\n                .createNewInstance(errorCode)\n                .addErrorEventTriggerDefinition(errorEventTriggerDefinition).done();\n        // create an instance using this definition\n        final SThrowEventInstance throwEventInstance = createThrowErrorEventInstance(context, eventDefinition);\n        final SProcessDefinition sProcessDefinition = processDefinitionService\n                .getProcessDefinition(processDefinitionId);\n        eventsHandler.getHandler(SEventTriggerType.ERROR).handlePostThrowEvent(sProcessDefinition, eventDefinition,\n                throwEventInstance,\n                errorEventTriggerDefinition, sFlowNodeInstance);\n        serviceAccessor.getBPMArchiverService().archiveAndDeleteFlowNodeInstance(throwEventInstance,\n                sProcessDefinition.getId());\n    }\n\n    @Override\n    public String getDescription() {\n        return getClass().getSimpleName() + \": flowNodeInstanceId = \" + flowNodeInstanceId\n                + \", connectorDefinitionName = \" + connectorDefinitionName;\n    }\n\n    @Override\n    protected SConnectorDefinition getSConnectorDefinition(final ProcessDefinitionService processDefinitionService)\n            throws SBonitaException {\n        final SProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(processDefinitionId);\n        final SFlowNodeDefinition flowNodeDefinition = processDefinition.getProcessContainer()\n                .getFlowNode(flowNodeDefinitionId);\n\n        final SConnectorDefinition sConnectorDefinition = flowNodeDefinition\n                .getConnectorDefinition(connectorDefinitionName);\n        if (sConnectorDefinition == null) {\n            throw new SConnectorDefinitionNotFoundException(\n                    \"Couldn't find the connector definition [\" + connectorDefinitionName + \"]\");\n        }\n        return sConnectorDefinition;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/ExecuteConnectorOfProcess.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.bpm.model.impl.BPMInstancesCreator;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.ScopedException;\nimport org.bonitasoft.engine.core.connector.ConnectorResult;\nimport org.bonitasoft.engine.core.connector.exception.SConnectorDefinitionNotFoundException;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.core.process.definition.model.SConnectorDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SEndEventDefinitionBuilderFactory;\nimport org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SThrowErrorEventTriggerDefinitionBuilderFactory;\nimport org.bonitasoft.engine.core.process.definition.model.event.SEndEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerType;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowErrorEventTriggerDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.BPMFailureService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowElementsContainerType;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.core.process.instance.model.event.SThrowEventInstance;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.execution.Filter;\nimport org.bonitasoft.engine.execution.FlowNodeIdFilter;\nimport org.bonitasoft.engine.execution.FlowNodeSelector;\nimport org.bonitasoft.engine.execution.ProcessExecutor;\nimport org.bonitasoft.engine.execution.StartFlowNodeFilter;\nimport org.bonitasoft.engine.execution.event.EventsHandler;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.service.ServiceAccessor;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class ExecuteConnectorOfProcess extends ExecuteConnectorWork {\n\n    private final long processInstanceId;\n\n    private final long rootProcessInstanceId;\n\n    private final ConnectorEvent activationEvent;\n\n    final Filter<SFlowNodeDefinition> filterFlowNodeDefinitions;\n\n    private final long subProcessDefinitionId;\n\n    ExecuteConnectorOfProcess(final long processDefinitionId, final long connectorInstanceId,\n            final String connectorDefinitionName,\n            final long processInstanceId, final long rootProcessInstanceId, final ConnectorEvent activationEvent,\n            List<Long> flowNodeDefinitionsFilter, Long subProcessDefinitionId) {\n        super(processDefinitionId, connectorInstanceId, connectorDefinitionName,\n                new SExpressionContext(processInstanceId,\n                        DataInstanceContainer.PROCESS_INSTANCE.name(), processDefinitionId),\n                processInstanceId);\n        this.processInstanceId = processInstanceId;\n        this.rootProcessInstanceId = rootProcessInstanceId;\n        this.activationEvent = activationEvent;\n        this.filterFlowNodeDefinitions = flowNodeDefinitionsFilter != null\n                ? new FlowNodeIdFilter(flowNodeDefinitionsFilter) : new StartFlowNodeFilter();\n        this.subProcessDefinitionId = subProcessDefinitionId != null ? subProcessDefinitionId : -1;\n    }\n\n    @Override\n    protected void evaluateOutput(final Map<String, Object> context, final ConnectorResult result,\n            final SConnectorDefinition sConnectorDefinition)\n            throws SBonitaException {\n        evaluateOutput(context, result, sConnectorDefinition, processInstanceId,\n                DataInstanceContainer.PROCESS_INSTANCE.name());\n    }\n\n    @Override\n    protected void continueFlow(final Map<String, Object> context) throws SBonitaException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor(context);\n        final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        final ProcessExecutor processExecutor = serviceAccessor.getProcessExecutor();\n\n        final SProcessDefinition sProcessDefinition = processDefinitionService\n                .getProcessDefinition(processDefinitionId);\n        final SProcessInstance intTxProcessInstance = processInstanceService.getProcessInstance(processInstanceId);\n        FlowNodeSelector flowNodeSelector = new FlowNodeSelector(sProcessDefinition, filterFlowNodeDefinitions,\n                subProcessDefinitionId);\n        final boolean connectorTriggered = processExecutor.registerConnectorsToExecute(sProcessDefinition,\n                intTxProcessInstance,\n                activationEvent,\n                flowNodeSelector);\n        if (!connectorTriggered) {\n            if (activationEvent == ConnectorEvent.ON_ENTER) {\n                processExecutor.startElements(intTxProcessInstance, flowNodeSelector);\n            } else {\n                processExecutor.handleProcessCompletion(sProcessDefinition, intTxProcessInstance, false);\n            }\n        }\n    }\n\n    @Override\n    protected void setContainerInFail(final Map<String, Object> context, Throwable t) throws SBonitaException {\n        final ProcessInstanceService processInstanceService = getServiceAccessor(context).getProcessInstanceService();\n        final SProcessInstance intTxProcessInstance = processInstanceService.getProcessInstance(processInstanceId);\n        processInstanceService.setState(intTxProcessInstance, ProcessInstanceState.ERROR);\n        var failureService = getServiceAccessor(context).getBpmFailureService();\n        failureService.createProcessInstanceFailure(intTxProcessInstance,\n                new BPMFailureService.Failure(ScopedException.CONNECTOR, t));\n    }\n\n    @Override\n    protected SThrowEventInstance createThrowErrorEventInstance(final Map<String, Object> context,\n            final SEndEventDefinition eventDefinition)\n            throws SBonitaException {\n        final BPMInstancesCreator bpmInstancesCreator = getServiceAccessor(context).getBPMInstancesCreator();\n        final SFlowNodeInstance createFlowNodeInstance = bpmInstancesCreator.createFlowNodeInstance(processDefinitionId,\n                rootProcessInstanceId,\n                processInstanceId, SFlowElementsContainerType.PROCESS, eventDefinition, rootProcessInstanceId,\n                processInstanceId, false, -1,\n                SStateCategory.NORMAL, -1);\n        return (SThrowEventInstance) createFlowNodeInstance;\n    }\n\n    @Override\n    protected void errorEventOnFail(final Map<String, Object> context, final SConnectorDefinition sConnectorDefinition,\n            final Throwable exception)\n            throws SBonitaException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor(context);\n        final EventsHandler eventsHandler = serviceAccessor.getEventsHandler();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n\n        setConnectorOnlyToFailed(context, exception);\n        // create a fake definition\n        final String errorCode = sConnectorDefinition.getErrorCode();\n        final SThrowErrorEventTriggerDefinition errorEventTriggerDefinition = BuilderFactory\n                .get(SThrowErrorEventTriggerDefinitionBuilderFactory.class)\n                .createNewInstance(errorCode).done();\n        // event definition as the error code as name, this way we don't need to find the connector that throw this error\n        final SEndEventDefinition eventDefinition = BuilderFactory.get(SEndEventDefinitionBuilderFactory.class)\n                .createNewInstance(errorCode)\n                .addErrorEventTriggerDefinition(errorEventTriggerDefinition).done();\n        // create an instance using this definition\n        final SThrowEventInstance throwEventInstance = createThrowErrorEventInstance(context, eventDefinition);\n\n        final SProcessDefinition sProcessDefinition = processDefinitionService\n                .getProcessDefinition(processDefinitionId);\n        final boolean hasActionToExecute = eventsHandler.getHandler(SEventTriggerType.ERROR).handlePostThrowEvent(\n                sProcessDefinition, eventDefinition,\n                throwEventInstance, errorEventTriggerDefinition, throwEventInstance);\n\n        serviceAccessor.getBPMArchiverService().archiveAndDeleteFlowNodeInstance(throwEventInstance,\n                sProcessDefinition.getId());\n        if (!hasActionToExecute) {\n            setConnectorAndContainerToFailed(context, exception);\n        }\n    }\n\n    @Override\n    public String getDescription() {\n        return getClass().getSimpleName() + \": processInstanceId = \" + processInstanceId\n                + \", connectorDefinitionName = \" + connectorDefinitionName;\n    }\n\n    @Override\n    protected SConnectorDefinition getSConnectorDefinition(final ProcessDefinitionService processDefinitionService)\n            throws SProcessDefinitionNotFoundException, SBonitaReadException, SConnectorDefinitionNotFoundException {\n        final SProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(processDefinitionId);\n        final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer();\n        // final SConnectorDefinition sConnectorDefinition = processContainer.getConnectorDefinition(connectorDefinitionId);// FIXME: Uncomment when generate id\n        final SConnectorDefinition sConnectorDefinition = processContainer\n                .getConnectorDefinition(connectorDefinitionName);\n        if (sConnectorDefinition == null) {\n            throw new SConnectorDefinitionNotFoundException(connectorDefinitionName);\n        }\n        return sConnectorDefinition;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/ExecuteConnectorWork.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work;\n\nimport static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.CompletionException;\n\nimport org.bonitasoft.engine.bpm.connector.FailAction;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.connector.exception.SConnectorException;\nimport org.bonitasoft.engine.core.connector.ConnectorInstanceService;\nimport org.bonitasoft.engine.core.connector.ConnectorResult;\nimport org.bonitasoft.engine.core.connector.ConnectorService;\nimport org.bonitasoft.engine.core.connector.parser.SConnectorImplementationDescriptor;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SConnectorDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SEndEventDefinition;\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstanceWithFailureInfo;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowElementsContainerType;\nimport org.bonitasoft.engine.core.process.instance.model.event.SThrowEventInstance;\nimport org.bonitasoft.engine.dependency.model.ScopeType;\nimport org.bonitasoft.engine.lock.BonitaLock;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.tracking.TimeTracker;\nimport org.bonitasoft.engine.tracking.TimeTrackerRecords;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\n@lombok.extern.slf4j.Slf4j\npublic abstract class ExecuteConnectorWork extends TenantAwareBonitaWork {\n\n    protected final long processDefinitionId;\n\n    protected final long connectorInstanceId;\n\n    protected final String connectorDefinitionName;\n\n    private final SExpressionContext inputParametersContext;\n    private final long processInstanceId;\n\n    protected ExecuteConnectorWork(final long processDefinitionId, final long connectorInstanceId,\n            final String connectorDefinitionName, final SExpressionContext inputParametersContext,\n            long processInstanceId) {\n        this(processDefinitionId, connectorInstanceId, connectorDefinitionName, inputParametersContext, null,\n                processInstanceId);\n    }\n\n    protected ExecuteConnectorWork(final long processDefinitionId, final long connectorInstanceId,\n            final String connectorDefinitionName,\n            final SExpressionContext inputParametersContext, final Map<String, Object> inputs, long processInstanceId) {\n        super();\n        this.processDefinitionId = processDefinitionId;\n        this.connectorInstanceId = connectorInstanceId;\n        this.connectorDefinitionName = connectorDefinitionName;\n        this.inputParametersContext = inputParametersContext;\n        this.processInstanceId = processInstanceId;\n        this.inputParametersContext.setInputValues(inputs);\n    }\n\n    protected abstract void errorEventOnFail(Map<String, Object> context, SConnectorDefinition sConnectorDefinition,\n            Throwable t) throws SBonitaException;\n\n    protected abstract SThrowEventInstance createThrowErrorEventInstance(Map<String, Object> context,\n            final SEndEventDefinition eventDefinition)\n            throws SBonitaException;\n\n    protected abstract SConnectorDefinition getSConnectorDefinition(\n            final ProcessDefinitionService processDefinitionService) throws SBonitaException;\n\n    protected abstract void setContainerInFail(Map<String, Object> context, Throwable t) throws SBonitaException;\n\n    protected abstract void continueFlow(Map<String, Object> context) throws SBonitaException;\n\n    protected abstract void evaluateOutput(Map<String, Object> context, final ConnectorResult result,\n            SConnectorDefinition sConnectorDefinition)\n            throws SBonitaException;\n\n    protected ClassLoader getClassLoader(final Map<String, Object> context) throws SBonitaException {\n        return getServiceAccessor(context).getClassLoaderService().getClassLoader(\n                identifier(ScopeType.PROCESS, processDefinitionId));\n    }\n\n    protected void setConnectorAndContainerToFailed(final Map<String, Object> context, final Throwable t)\n            throws SBonitaException {\n        setConnectorOnlyToFailed(context, t);\n        setContainerInFail(context, t);\n    }\n\n    protected void setConnectorOnlyToFailed(final Map<String, Object> context, final Throwable t)\n            throws SBonitaException {\n        final ConnectorInstanceService connectorInstanceService = getServiceAccessor(context)\n                .getConnectorInstanceService();\n        final SConnectorInstanceWithFailureInfo connectorInstanceWithFailure = connectorInstanceService\n                .getConnectorInstanceWithFailureInfo(connectorInstanceId);\n        connectorInstanceService.setState(connectorInstanceWithFailure, ConnectorService.FAILED);\n        connectorInstanceService.setConnectorInstanceFailureException(connectorInstanceWithFailure, t);\n    }\n\n    protected void evaluateOutput(final Map<String, Object> context, final ConnectorResult result,\n            final SConnectorDefinition sConnectorDefinition, final Long id, final String containerType)\n            throws SBonitaException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor(context);\n        final ConnectorInstanceService connectorInstanceService = serviceAccessor.getConnectorInstanceService();\n        final ConnectorService connectorService = serviceAccessor.getConnectorService();\n        final List<SOperation> outputs = sConnectorDefinition.getOutputs();\n        final SExpressionContext sExpressionContext = new SExpressionContext(id, containerType,\n                processDefinitionId);\n        connectorService.executeOutputOperation(outputs, sExpressionContext, result);\n        connectorInstanceService.setState(connectorInstanceService.getConnectorInstance(connectorInstanceId),\n                ConnectorService.DONE);\n    }\n\n    @Override\n    public CompletableFuture<Void> work(final Map<String, Object> context) throws Exception {\n        final long startTime = System.currentTimeMillis();\n        final ServiceAccessor serviceAccessor = getServiceAccessor(context);\n        final ConnectorService connectorService = serviceAccessor.getConnectorService();\n        final ConnectorInstanceService connectorInstanceService = serviceAccessor.getConnectorInstanceService();\n        final UserTransactionService userTransactionService = serviceAccessor.getUserTransactionService();\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        final TimeTracker timeTracker = serviceAccessor.getTimeTracker();\n        final ClassLoader processClassloader = getClassLoader(context);\n        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        try {\n            Thread.currentThread().setContextClassLoader(processClassloader);\n            final EvaluateParameterAndGetConnectorInstance callable = new EvaluateParameterAndGetConnectorInstance(\n                    connectorService, processDefinitionService,\n                    connectorInstanceService);\n            userTransactionService.executeInTransaction(callable);\n            final SConnectorDefinition sConnectorDefinition = callable.getsConnectorDefinition();\n            final SConnectorInstance connectorInstance = callable.getConnectorInstance();\n            SConnectorImplementationDescriptor connectorImplementationDescriptor = callable\n                    .getConnectorImplementationDescriptor();\n            return connectorService.executeConnector(processDefinitionId, connectorInstance,\n                    connectorImplementationDescriptor, processClassloader,\n                    callable.getInputParameters())\n                    .thenAccept(r -> {\n                        try {\n                            executeOutputOperationsAndContinue(context, serviceAccessor, userTransactionService,\n                                    sConnectorDefinition, r);\n                        } catch (Exception e) {\n                            log.error(\"Unable to evaluate output operations and continue flow\"\n                                    + \" for connector '{}' (connectorInstanceId={},\"\n                                    + \" processDefinitionId={}, processInstanceId={})\",\n                                    connectorDefinitionName, connectorInstanceId,\n                                    processDefinitionId, processInstanceId, e);\n                            throw new CompletionException(\n                                    new SConnectorException(\n                                            \"Unable to evaluate output operations and continue flow for connector '\"\n                                                    + connectorDefinitionName + \"'\",\n                                            e));\n                        }\n                    });\n        } finally {\n            if (timeTracker.isTrackable(TimeTrackerRecords.EXECUTE_CONNECTOR_WORK)) {\n                final long endTime = System.currentTimeMillis();\n                final StringBuilder desc = new StringBuilder();\n                desc.append(\"processDefinitionId: \");\n                desc.append(processDefinitionId);\n                desc.append(\" - \");\n                desc.append(\"connectorDefinitionName: \");\n                desc.append(connectorDefinitionName);\n                desc.append(\" - \");\n                desc.append(\"connectorInstanceId: \");\n                desc.append(connectorInstanceId);\n                timeTracker.track(TimeTrackerRecords.EXECUTE_CONNECTOR_WORK, desc.toString(), endTime - startTime);\n            }\n            Thread.currentThread().setContextClassLoader(contextClassLoader);\n        }\n    }\n\n    private void executeOutputOperationsAndContinue(Map<String, Object> context, ServiceAccessor serviceAccessor,\n            UserTransactionService userTransactionService, SConnectorDefinition sConnectorDefinition, ConnectorResult r)\n            throws Exception {\n        // evaluate output and trigger the execution of the flow node\n        BonitaLock lock = serviceAccessor.getLockService().lock(processInstanceId,\n                SFlowElementsContainerType.PROCESS.name());\n        try {\n            userTransactionService.executeInTransaction(() -> {\n                evaluateOutput(context, r, sConnectorDefinition);\n                continueFlow(context);\n                return null;\n            });\n        } finally {\n            serviceAccessor.getLockService().unlock(lock);\n        }\n    }\n\n    @Override\n    public void handleFailure(final Throwable e, final Map<String, Object> context) throws Exception {\n        final UserTransactionService userTransactionService = getServiceAccessor(context).getUserTransactionService();\n        final ProcessDefinitionService processDefinitionService = getServiceAccessor(context)\n                .getProcessDefinitionService();\n\n        final HandleConnectorOnFailEventTxContent callable = new HandleConnectorOnFailEventTxContent(e,\n                processDefinitionService, context);\n        final SConnectorDefinition sConnectorDefinition = userTransactionService.executeInTransaction(callable);\n\n        if (shouldContinueFlow(sConnectorDefinition)) {\n            userTransactionService.executeInTransaction(new ContinueFlowTxContent(context));\n        }\n    }\n\n    private boolean shouldContinueFlow(SConnectorDefinition sConnectorDefinition) {\n        return sConnectorDefinition.getFailAction() == FailAction.IGNORE;\n    }\n\n    private final class EvaluateParameterAndGetConnectorInstance implements Callable<Void> {\n\n        private final ConnectorService connectorService;\n\n        private final ConnectorInstanceService connectorInstanceService;\n\n        private Map<String, Object> inputParameters;\n\n        private SConnectorInstance connectorInstance;\n\n        private final ProcessDefinitionService processDefinitionService;\n\n        private SConnectorDefinition sConnectorDefinition;\n        private SConnectorImplementationDescriptor connectorImplementationDescriptor;\n\n        private EvaluateParameterAndGetConnectorInstance(final ConnectorService connectorService,\n                final ProcessDefinitionService processDefinitionService,\n                final ConnectorInstanceService connectorInstanceService) {\n            this.connectorService = connectorService;\n            this.processDefinitionService = processDefinitionService;\n            this.connectorInstanceService = connectorInstanceService;\n        }\n\n        public Map<String, Object> getInputParameters() {\n            return inputParameters;\n        }\n\n        public SConnectorInstance getConnectorInstance() {\n            return connectorInstance;\n        }\n\n        public SConnectorDefinition getsConnectorDefinition() {\n            return sConnectorDefinition;\n        }\n\n        public SConnectorImplementationDescriptor getConnectorImplementationDescriptor() {\n            return connectorImplementationDescriptor;\n        }\n\n        @Override\n        public Void call() throws Exception {\n            sConnectorDefinition = getSConnectorDefinition(processDefinitionService);\n            inputParameters = connectorService.evaluateInputParameters(sConnectorDefinition.getConnectorId(),\n                    sConnectorDefinition.getInputs(),\n                    inputParametersContext, null);\n            connectorInstance = connectorInstanceService.getConnectorInstance(connectorInstanceId);\n            connectorImplementationDescriptor = connectorService.getConnectorImplementationDescriptor(\n                    processDefinitionId, sConnectorDefinition.getConnectorId(), sConnectorDefinition.getVersion());\n            return null;\n        }\n    }\n\n    /**\n     * Handle the error according to failure policy.\n     *\n     * @author Emmanuel Duchastenier\n     */\n    private final class HandleConnectorOnFailEventTxContent implements Callable<SConnectorDefinition> {\n\n        private final Throwable e;\n\n        private final ProcessDefinitionService processDefinitionService;\n\n        private final Map<String, Object> context;\n\n        private HandleConnectorOnFailEventTxContent(final Throwable e,\n                final ProcessDefinitionService processDefinitionService,\n                final Map<String, Object> context) {\n            this.e = e;\n            this.processDefinitionService = processDefinitionService;\n            this.context = context;\n\n        }\n\n        @Override\n        public SConnectorDefinition call() throws Exception {\n            final SConnectorDefinition sConnectorDefinition = getSConnectorDefinition(processDefinitionService);\n            switch (sConnectorDefinition.getFailAction()) {\n                case ERROR_EVENT:\n                    errorEventOnFail(context, sConnectorDefinition, e);\n                    break;\n                case FAIL:\n                    setConnectorAndContainerToFailed(context, e);\n                    break;\n                case IGNORE:\n                    setConnectorOnlyToFailed(context, e);\n                    break;\n                default:\n                    throw new Exception(\"No action defined for \" + sConnectorDefinition.getFailAction());\n            }\n            return sConnectorDefinition;\n        }\n    }\n\n    /**\n     * @author Emmanuel Duchastenier\n     */\n    final class ContinueFlowTxContent implements Callable<Void> {\n\n        private final Map<String, Object> context;\n\n        public ContinueFlowTxContent(final Map<String, Object> context) {\n            this.context = context;\n        }\n\n        @Override\n        public Void call() throws Exception {\n            continueFlow(context);\n            return null;\n        }\n    }\n\n    @Override\n    public boolean canBeRecoveredByTheRecoveryMechanism() {\n        return true;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/ExecuteFlowNodeWork.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work;\n\nimport java.util.Map;\nimport java.util.concurrent.CompletableFuture;\n\nimport org.bonitasoft.engine.commons.exceptions.ScopedException;\nimport org.bonitasoft.engine.core.process.instance.api.BPMFailureService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.execution.WaitingEventsInterrupter;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.bonitasoft.engine.work.SWorkPreconditionException;\n\n/**\n * Work that is responsible of executing a flow node.\n * If the execution fails it will put the flow node in failed state\n *\n * @author Baptiste Mesta\n * @author Celine Souchet\n * @author Matthieu Chaffotte\n */\npublic class ExecuteFlowNodeWork extends TenantAwareBonitaWork {\n\n    private final long flowNodeInstanceId;\n    private final Integer stateId;\n    private final Boolean executing;\n    private final Boolean aborting;\n    private final Boolean canceling;\n\n    ExecuteFlowNodeWork(final long flowNodeInstanceId, Integer stateId, Boolean executing, Boolean aborting,\n            Boolean canceling) {\n        this.flowNodeInstanceId = flowNodeInstanceId;\n        this.stateId = stateId;\n        this.executing = executing;\n        this.aborting = aborting;\n        this.canceling = canceling;\n    }\n\n    @Override\n    public String getDescription() {\n        return getClass().getSimpleName() + \": flowNodeInstanceId: \" + flowNodeInstanceId + \" (\" + stateId + \", \"\n                + executing + \", \" + aborting + \", \" + canceling + \")\";\n    }\n\n    @Override\n    public CompletableFuture<Void> work(final Map<String, Object> context) throws Exception {\n        final ServiceAccessor serviceAccessor = getServiceAccessor(context);\n        SFlowNodeInstance flowNodeInstance = retrieveAndVerifyFlowNodeInstance(serviceAccessor);\n        context.put(\"flowNodeInstance\", flowNodeInstance);\n        serviceAccessor.getFlowNodeExecutor().executeFlowNode(flowNodeInstance, null, null);\n        return CompletableFuture.completedFuture(null);\n    }\n\n    private SFlowNodeInstance retrieveAndVerifyFlowNodeInstance(ServiceAccessor serviceAccessor)\n            throws SFlowNodeReadException, SWorkPreconditionException {\n        SFlowNodeInstance flowNodeInstance;\n        try {\n            flowNodeInstance = serviceAccessor.getActivityInstanceService().getFlowNodeInstance(flowNodeInstanceId);\n        } catch (SFlowNodeNotFoundException e) {\n            throw new SWorkPreconditionException(String.format(\"Flow node %d does not exists\", flowNodeInstanceId), e);\n        }\n        if (stateId != flowNodeInstance.getStateId()\n                || executing != flowNodeInstance.isStateExecuting()\n                || aborting != flowNodeInstance.isAborting()\n                || canceling != flowNodeInstance.isCanceling()) {\n            throw new SWorkPreconditionException(\n                    String.format(\"Unable to execute flow node %d because it is not in the expected state \" +\n                            \"( expected state: %d, transitioning: %s, aborting: %s, canceling: %s, but got  state: %d, transitioning: %s, aborting: %s, canceling: %s).\"\n                            +\n                            \" Someone probably already called execute on it.\",\n                            flowNodeInstance.getId(), stateId, executing, aborting, canceling,\n                            flowNodeInstance.getStateId(),\n                            flowNodeInstance.isStateExecuting(), flowNodeInstance.isAborting(),\n                            flowNodeInstance.isCanceling()));\n        }\n        return flowNodeInstance;\n    }\n\n    @Override\n    public void handleFailure(final Throwable e, final Map<String, Object> context) throws Exception {\n        ServiceAccessor serviceAccessor = getServiceAccessor(context);\n        final UserTransactionService userTransactionService = serviceAccessor.getUserTransactionService();\n        WaitingEventsInterrupter waitingEventsInterrupter = new WaitingEventsInterrupter(\n                serviceAccessor.getEventInstanceService(),\n                serviceAccessor.getSchedulerService());\n        FailedStateSetter failedStateSetter = new FailedStateSetter(waitingEventsInterrupter,\n                serviceAccessor.getActivityInstanceService(),\n                serviceAccessor.getFlowNodeStateManager());\n        var flowNodeInstance = (SFlowNodeInstance) context.get(\"flowNodeInstance\");\n        userTransactionService.executeInTransaction(new SetInFailCallable(failedStateSetter,\n                flowNodeInstance,\n                serviceAccessor.getBpmFailureService(),\n                createFailure(e)));\n    }\n\n    private BPMFailureService.Failure createFailure(Throwable e) {\n        if (e instanceof ScopedException scopedException) {\n            return new BPMFailureService.Failure(scopedException.getScope(), e);\n        }\n        return new BPMFailureService.Failure(ScopedException.UNKNOWN_SCOPE, e);\n    }\n\n    @Override\n    public String toString() {\n        return \"Work[\" + getDescription() + \"]\";\n    }\n\n    @Override\n    public boolean canBeRecoveredByTheRecoveryMechanism() {\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/ExecuteMessageCoupleWork.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work;\n\nimport java.util.Map;\nimport java.util.concurrent.CompletableFuture;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventModificationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventReadException;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingMessageEventBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceService;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.service.ServiceAccessor;\n\n/**\n * @author Emmanuel Duchastenier\n * @author Matthieu Chaffotte\n */\n@Slf4j\npublic class ExecuteMessageCoupleWork extends TenantAwareBonitaWork {\n\n    private final long messageInstanceId;\n\n    private final long waitingMessageId;\n\n    ExecuteMessageCoupleWork(final long messageInstanceId, final long waitingMessageId) {\n        this.messageInstanceId = messageInstanceId;\n        this.waitingMessageId = waitingMessageId;\n    }\n\n    @Override\n    public String getDescription() {\n        return getClass().getSimpleName() + \": messageInstanceId: \" + messageInstanceId + \", waitingMessageId: \"\n                + waitingMessageId;\n    }\n\n    private void resetWaitingMessage(final long waitingMessageId, final EventInstanceService eventInstanceService)\n            throws SWaitingEventModificationException,\n            SWaitingEventReadException {\n        final SWaitingMessageEvent waitingMsg = eventInstanceService.getWaitingMessage(waitingMessageId);\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        descriptor\n                .addField(BuilderFactory.get(SWaitingMessageEventBuilderFactory.class).getProgressKey(),\n                        SWaitingMessageEventBuilderFactory.PROGRESS_FREE_KEY);\n        eventInstanceService.updateWaitingMessage(waitingMsg, descriptor);\n    }\n\n    @Override\n    public String getRecoveryProcedure() {\n        return \"call processApi.executeMessageCouple(\" + messageInstanceId + \", \" + waitingMessageId\n                + \"); to re-launch the execution of the message.\";\n    }\n\n    @Override\n    public CompletableFuture<Void> work(final Map<String, Object> context) throws Exception {\n        final ServiceAccessor serviceAccessor = getServiceAccessor(context);\n        final EventInstanceService eventInstanceService = serviceAccessor.getEventInstanceService();\n        final DataInstanceService dataInstanceService = serviceAccessor.getDataInstanceService();\n        final SWaitingMessageEvent waitingMessage = eventInstanceService.getWaitingMessage(waitingMessageId);\n        final SMessageInstance messageInstance = eventInstanceService.getMessageInstance(messageInstanceId);\n        if (waitingMessage != null) {\n            serviceAccessor.getEventsHandler().triggerCatchEvent(waitingMessage, messageInstanceId);\n            eventInstanceService.deleteMessageInstance(messageInstance);\n            dataInstanceService.deleteLocalDataInstances(messageInstanceId,\n                    DataInstanceContainer.MESSAGE_INSTANCE.name(), true);\n        }\n        return CompletableFuture.completedFuture(null);\n    }\n\n    @Override\n    public void handleFailure(final Throwable e, final Map<String, Object> context) throws Exception {\n        final ServiceAccessor serviceAccessor = getServiceAccessor(context);\n        serviceAccessor.getUserTransactionService().executeInTransaction(() -> {\n            resetWaitingMessage(waitingMessageId, serviceAccessor.getEventInstanceService());\n            return null;\n        });\n        log.warn(\n                String.format(\n                        \"Unable to execute message couple with sent message %s and waiting message %s, the waiting message was reset\"\n                                +\n                                \" to allow other message to trigger it. This failure might come from a design issue, cause is: %s\",\n                        messageInstanceId, waitingMessageId, getRootCause(e)));\n        log.debug(\"Cause of the issue while executing message couple: sent message {} and waiting message {} error {}\",\n                messageInstanceId, waitingMessageId, e);\n    }\n\n    private String getRootCause(Throwable e) {\n        String message = null;\n        while (e != null) {\n            message = e.getMessage();\n            e = e.getCause();\n        }\n        return message;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/FailedStateSetter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.execution.WaitingEventsInterrupter;\nimport org.bonitasoft.engine.execution.state.FlowNodeStateManager;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Slf4j\npublic class FailedStateSetter {\n\n    private final WaitingEventsInterrupter waitingEventsInterrupter;\n\n    private final ActivityInstanceService activityInstanceService;\n\n    private final FlowNodeStateManager flowNodeStateManager;\n\n    public FailedStateSetter(WaitingEventsInterrupter waitingEventsInterrupter,\n            final ActivityInstanceService activityInstanceService,\n            final FlowNodeStateManager flowNodeStateManager) {\n        this.waitingEventsInterrupter = waitingEventsInterrupter;\n        this.activityInstanceService = activityInstanceService;\n        this.flowNodeStateManager = flowNodeStateManager;\n    }\n\n    public void setAsFailed(long flowNodeInstanceId) throws SBonitaException {\n        final SFlowNodeInstance flowNodeInstance;\n        try {\n            flowNodeInstance = activityInstanceService.getFlowNodeInstance(flowNodeInstanceId);\n\n            //nothing to do if the flownode is already in failed state\n            if (flowNodeInstance.getStateId() != FlowNodeState.ID_ACTIVITY_FAILED) {\n                activityInstanceService.setState(flowNodeInstance,\n                        flowNodeStateManager.getState(FlowNodeState.ID_ACTIVITY_FAILED));\n                waitingEventsInterrupter.interruptWaitingEvents(flowNodeInstance);\n            }\n        } catch (SFlowNodeNotFoundException e) {\n            log.debug(\n                    \"Impossible to put flow node instance in failed state: flow node instance with id '{}' not found.\",\n                    flowNodeInstanceId);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/InSessionBonitaWork.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work;\n\nimport java.util.Map;\nimport java.util.concurrent.CompletableFuture;\n\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.ServiceAccessorSingleton;\nimport org.bonitasoft.engine.work.BonitaWork;\n\n/**\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n */\npublic class InSessionBonitaWork extends WrappingBonitaWork {\n\n    public InSessionBonitaWork(final BonitaWork work) {\n        super(work);\n    }\n\n    ServiceAccessor getServiceAccessor() {\n        try {\n            return ServiceAccessorSingleton.getInstance();\n        } catch (final Exception e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    @Override\n    public CompletableFuture<Void> work(final Map<String, Object> context) throws Exception {\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        context.put(SERVICE_ACCESSOR, serviceAccessor);\n        return getWrappedWork().work(context);\n    }\n\n    @Override\n    public void handleFailure(final Throwable e, final Map<String, Object> context) throws Exception {\n        getWrappedWork().handleFailure(e, context);\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/LockProcessInstanceWork.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work;\n\nimport java.util.Map;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.TimeUnit;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowElementsContainerType;\nimport org.bonitasoft.engine.lock.BonitaLock;\nimport org.bonitasoft.engine.lock.LockService;\nimport org.bonitasoft.engine.lock.SLockException;\nimport org.bonitasoft.engine.work.BonitaWork;\nimport org.bonitasoft.engine.work.LockException;\nimport org.bonitasoft.engine.work.LockTimeoutException;\n\n/**\n * Transactional work that lock the process instance\n * This work try to lock a process instance, if it can't be locked before the end of the TIMEOUT, we reschedule the fill\n * stack of work on the work service.\n *\n * @author Charles Souillard\n * @author Baptiste Mesta\n */\n@Slf4j\npublic class LockProcessInstanceWork extends WrappingBonitaWork {\n\n    protected final long processInstanceId;\n\n    private static final long TIMEOUT = 20;\n\n    private final TimeUnit timeUnit = TimeUnit.MILLISECONDS;\n\n    public LockProcessInstanceWork(final BonitaWork wrappedWork, final long processInstanceId) {\n        super(wrappedWork);\n        this.processInstanceId = processInstanceId;\n    }\n\n    @Override\n    public CompletableFuture<Void> work(final Map<String, Object> context) throws Exception {\n        final LockService lockService = getServiceAccessor(context).getLockService();\n        final String objectType = SFlowElementsContainerType.PROCESS.name();\n\n        BonitaLock lock = null;\n        try {\n            log.debug(\"{} trying to get lock for instance {}: {}\", Thread.currentThread().getName(),\n                    processInstanceId, getDescription());\n            lock = lockService.tryLock(processInstanceId, objectType, TIMEOUT, timeUnit);\n            if (lock == null) {\n                throw new LockTimeoutException(\n                        \"Unable to lock process instance \" + processInstanceId + \": \" + getDescription());\n            }\n            log.debug(\"{} obtained lock for instance {}: {}\", Thread.currentThread().getName(), processInstanceId,\n                    getDescription());\n            return getWrappedWork().work(context);\n        } catch (SLockException e) {\n            throw new LockException(\"Unexpected exception happened while trying to lock process instance \"\n                    + processInstanceId + \": \" + getDescription(), e);\n        } finally {\n            if (lock != null) {\n                lockService.unlock(lock);\n                log.debug(\"{} has unlocked lock for instance {}: {}\", Thread.currentThread().getName(),\n                        processInstanceId, getDescription());\n            }\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/NotifyChildFinishedWork.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work;\n\nimport static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier;\n\nimport java.util.Map;\nimport java.util.concurrent.CompletableFuture;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.ScopedException;\nimport org.bonitasoft.engine.core.process.instance.api.BPMFailureService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.dependency.model.ScopeType;\nimport org.bonitasoft.engine.execution.ContainerRegistry;\nimport org.bonitasoft.engine.execution.WaitingEventsInterrupter;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.bonitasoft.engine.work.SWorkPreconditionException;\n\n/**\n * Work that notify a container that a flow node is in completed state\n * e.g. when a flow node of a process finish we evaluate the outgoing transitions of this flow node.\n *\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\n@Slf4j\npublic class NotifyChildFinishedWork extends TenantAwareBonitaWork {\n\n    private final long processDefinitionId;\n    private final long flowNodeInstanceId;\n    private final Integer stateId;\n    private final Boolean executing;\n    private final Boolean aborting;\n    private final Boolean canceling;\n\n    NotifyChildFinishedWork(long processDefinitionId, long flowNodeInstanceId, Integer stateId, Boolean executing,\n            Boolean aborting, Boolean canceling) {\n        this.processDefinitionId = processDefinitionId;\n        this.flowNodeInstanceId = flowNodeInstanceId;\n        this.stateId = stateId;\n        this.executing = executing;\n        this.aborting = aborting;\n        this.canceling = canceling;\n    }\n\n    protected ClassLoader getClassLoader(final Map<String, Object> context) throws SBonitaException {\n        return getServiceAccessor(context).getClassLoaderService().getClassLoader(\n                identifier(ScopeType.PROCESS, processDefinitionId));\n    }\n\n    @Override\n    public CompletableFuture<Void> work(final Map<String, Object> context) throws Exception {\n        final ClassLoader processClassloader = getClassLoader(context);\n        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        try {\n            Thread.currentThread().setContextClassLoader(processClassloader);\n            ServiceAccessor serviceAccessor = getServiceAccessor(context);\n            SFlowNodeInstance flowNodeInstance = retrieveAndVerifyFlowNodeInstance(serviceAccessor);\n            context.put(\"flowNodeInstance\", flowNodeInstance);\n            log.debug(\"Processing completion of flowNode '{}' (id={}, state='{}', \"\n                    + \"processInstance={}, processDefinition={})\",\n                    flowNodeInstance.getName(), flowNodeInstance.getId(), flowNodeInstance.getStateName(),\n                    flowNodeInstance.getRootProcessInstanceId(), processDefinitionId);\n            final ContainerRegistry containerRegistry = serviceAccessor.getContainerRegistry();\n            containerRegistry.nodeReachedState(flowNodeInstance);\n        } finally {\n            Thread.currentThread().setContextClassLoader(contextClassLoader);\n        }\n        return CompletableFuture.completedFuture(null);\n    }\n\n    private SFlowNodeInstance retrieveAndVerifyFlowNodeInstance(ServiceAccessor serviceAccessor)\n            throws SWorkPreconditionException, SFlowNodeReadException {\n        SFlowNodeInstance flowNodeInstance;\n        try {\n            flowNodeInstance = serviceAccessor.getActivityInstanceService().getFlowNodeInstance(flowNodeInstanceId);\n        } catch (SFlowNodeNotFoundException e) {\n            throw new SWorkPreconditionException(\n                    \"Flow node \" + flowNodeInstanceId + \" is already completed ( not found )\");\n        }\n        if (stateId != flowNodeInstance.getStateId()\n                || executing != flowNodeInstance.isStateExecuting()\n                || aborting != flowNodeInstance.isAborting()\n                || canceling != flowNodeInstance.isCanceling()) {\n            throw new SWorkPreconditionException(\n                    String.format(\"Unable to execute flow node %d because it is not in the expected state \" +\n                            \"( expected state: %d, transitioning: %s, aborting: %s, canceling: %s, but got  state: %d, transitioning: %s, aborting: %s, canceling: %s).\"\n                            +\n                            \" Someone probably already called execute on it.\",\n                            flowNodeInstanceId, stateId, executing, aborting, canceling, flowNodeInstance.getStateId(),\n                            flowNodeInstance.isStateExecuting(), flowNodeInstance.isAborting(),\n                            flowNodeInstance.isCanceling()));\n        }\n        if (!flowNodeInstance.isTerminal()) {\n            throw new SWorkPreconditionException(\"Flow node \" + flowNodeInstanceId + \" is not yet completed\");\n        }\n        return flowNodeInstance;\n    }\n\n    @Override\n    public String getDescription() {\n        return getClass().getSimpleName() + \" flowNodeInstanceId: \" + flowNodeInstanceId;\n    }\n\n    @Override\n    public void handleFailure(final Throwable e, final Map<String, Object> context) throws Exception {\n        ServiceAccessor serviceAccessor = getServiceAccessor(context);\n        final UserTransactionService userTransactionService = serviceAccessor.getUserTransactionService();\n        WaitingEventsInterrupter waitingEventsInterrupter = new WaitingEventsInterrupter(\n                serviceAccessor.getEventInstanceService(),\n                serviceAccessor.getSchedulerService());\n        FailedStateSetter failedStateSetter = new FailedStateSetter(waitingEventsInterrupter,\n                serviceAccessor.getActivityInstanceService(),\n                serviceAccessor.getFlowNodeStateManager());\n        SFlowNodeInstance flowNodeInstance = (SFlowNodeInstance) context.get(\"flowNodeInstance\");\n        userTransactionService.executeInTransaction(new SetInFailCallable(failedStateSetter, flowNodeInstance,\n                serviceAccessor.getBpmFailureService(), createFailure(e)));\n    }\n\n    private BPMFailureService.Failure createFailure(Throwable e) {\n        if (e instanceof ScopedException scopedException) {\n            return new BPMFailureService.Failure(scopedException.getScope(), e);\n        }\n        return new BPMFailureService.Failure(ScopedException.UNKNOWN_SCOPE, e);\n    }\n\n    @Override\n    public String toString() {\n        return \"Work[\" + getDescription() + \"]\";\n    }\n\n    @Override\n    public boolean canBeRecoveredByTheRecoveryMechanism() {\n        return true;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/RestartException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work;\n\nimport org.bonitasoft.engine.exception.BonitaException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class RestartException extends BonitaException {\n\n    private static final long serialVersionUID = 6724312254693245291L;\n\n    public RestartException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/SetInFailCallable.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work;\n\nimport java.util.concurrent.Callable;\n\nimport org.bonitasoft.engine.core.process.instance.api.BPMFailureService;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SetInFailCallable implements Callable<Void> {\n\n    private final FailedStateSetter failedStateSetter;\n    private final SFlowNodeInstance flowNodeInstance;\n    private final BPMFailureService bpmFailureService;\n    private final BPMFailureService.Failure failure;\n\n    SetInFailCallable(FailedStateSetter failedStateSetter, final SFlowNodeInstance flowNodeInstance,\n            BPMFailureService bpmFailureService, BPMFailureService.Failure failure) {\n        this.failedStateSetter = failedStateSetter;\n        this.flowNodeInstance = flowNodeInstance;\n        this.bpmFailureService = bpmFailureService;\n        this.failure = failure;\n    }\n\n    @Override\n    public Void call() throws Exception {\n        failedStateSetter.setAsFailed(flowNodeInstance.getId());\n        bpmFailureService.createFlowNodeFailure(flowNodeInstance, failure);\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/TenantAwareBonitaWork.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.work.BonitaWork;\n\n/**\n * @author Baptiste Mesta\n */\npublic abstract class TenantAwareBonitaWork extends BonitaWork {\n\n    public static final String SERVICE_ACCESSOR = \"serviceAccessor\";\n\n    public TenantAwareBonitaWork() {\n        super();\n    }\n\n    protected ServiceAccessor getServiceAccessor(final Map<String, Object> context) {\n        return (ServiceAccessor) context.get(TenantAwareBonitaWork.SERVICE_ACCESSOR);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/TxBonitaWork.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work;\n\nimport java.util.Map;\nimport java.util.concurrent.CompletableFuture;\n\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.bonitasoft.engine.work.BonitaWork;\n\n/**\n * A work that wrap an other work in a transaction\n *\n * @author Baptiste Mesta\n * @author Emmanuel Duchastenier\n * @author Charles Souillard\n * @author Celine Souchet\n */\npublic class TxBonitaWork extends WrappingBonitaWork {\n\n    public TxBonitaWork(final BonitaWork wrappedWork) {\n        super(wrappedWork);\n    }\n\n    @Override\n    public CompletableFuture<Void> work(final Map<String, Object> context) throws Exception {\n        final ServiceAccessor serviceAccessor = getServiceAccessor(context);\n        final UserTransactionService userTransactionService = serviceAccessor.getUserTransactionService();\n        return userTransactionService.executeInTransaction(() -> getWrappedWork().work(context));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/WrappingBonitaWork.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.work.BonitaWork;\n\n/**\n * @author Baptiste Mesta\n */\npublic abstract class WrappingBonitaWork extends TenantAwareBonitaWork {\n\n    private static final long serialVersionUID = 1L;\n\n    private final BonitaWork wrappedWork;\n\n    public WrappingBonitaWork(final BonitaWork wrappedWork) {\n        this.wrappedWork = wrappedWork;\n        wrappedWork.setParent(this);\n    }\n\n    @Override\n    public String getDescription() {\n        return wrappedWork.getDescription();\n    }\n\n    @Override\n    public String getRecoveryProcedure() {\n        return wrappedWork.getRecoveryProcedure();\n    }\n\n    public BonitaWork getWrappedWork() {\n        return wrappedWork;\n    }\n\n    @Override\n    public String toString() {\n        return wrappedWork.toString();\n    }\n\n    @Override\n    public void handleFailure(final Throwable e, final Map<String, Object> context) throws Exception {\n        wrappedWork.handleFailure(e, context);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/ConnectorDefinitionAndInstanceContextWork.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work.failurewrapping;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.commons.exceptions.ExceptionContext;\nimport org.bonitasoft.engine.work.BonitaWork;\n\n/**\n * Adding context information about Connector definition and instance to exception for better logging\n *\n * @author Celine Souchet\n */\npublic class ConnectorDefinitionAndInstanceContextWork extends TxInHandleFailureWrappingWork {\n\n    private static final long serialVersionUID = 6958842321501639910L;\n\n    private final String connectorDefinitionId;\n\n    private final String connectorName;\n\n    private final ConnectorEvent activationEvent;\n\n    private final long connectorInstanceId;\n\n    /**\n     * @param wrappedWork\n     *        The work to wrap\n     * @param connectorDefinitionId\n     *        The name of the connector definition\n     * @param connectorName\n     *        The name of the connector\n     * @param connectorInstanceId\n     *        The identifier of the connector instance\n     * @param activationEvent\n     *        The event to activate the connector\n     */\n    public ConnectorDefinitionAndInstanceContextWork(final BonitaWork wrappedWork,\n            final String connectorDefinitionId,\n            final String connectorName,\n            long connectorInstanceId, final ConnectorEvent activationEvent) {\n        super(wrappedWork);\n        this.connectorDefinitionId = connectorDefinitionId;\n        this.connectorName = connectorName;\n        this.activationEvent = activationEvent;\n        this.connectorInstanceId = connectorInstanceId;\n    }\n\n    @Override\n    protected void setExceptionContext(final ExceptionContext e, final Map<String, Object> context) {\n        e.setConnectorImplementationClassNameOnContext(connectorName);\n        e.setConnectorNameOnContext(connectorName);\n        e.setConnectorDefinitionIdOnContext(connectorDefinitionId);\n        e.setConnectorInstanceIdOnContext(connectorInstanceId);\n        if (activationEvent != null) {\n            e.setConnectorActivationEventOnContext(activationEvent.name());\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/FlowNodeDefinitionAndInstanceContextWork.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work.failurewrapping;\n\nimport java.util.Map;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.function.Supplier;\n\nimport org.bonitasoft.engine.commons.exceptions.ExceptionContext;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.execution.work.WrappingBonitaWork;\nimport org.bonitasoft.engine.mdc.AbstractMDC;\nimport org.bonitasoft.engine.mdc.MDCConstants;\nimport org.bonitasoft.engine.mdc.MDCHelper;\nimport org.bonitasoft.engine.service.ServiceAccessor;\n\n/**\n * Adding context information about Flow Node & Process definition and instance to exception for better logging\n *\n * @author Aurelien Pupier\n * @author Celine Souchet\n */\npublic class FlowNodeDefinitionAndInstanceContextWork extends TxInHandleFailureWrappingWork {\n\n    private static final long serialVersionUID = -8192129441020811731L;\n\n    private final long flowNodeInstanceId;\n\n    /**\n     * @param wrappedWork\n     *        The work to wrap\n     * @param flowNodeInstanceId\n     *        The identifier of the flow node instance\n     */\n    public FlowNodeDefinitionAndInstanceContextWork(final WrappingBonitaWork wrappedWork,\n            final long flowNodeInstanceId) {\n        super(wrappedWork);\n        this.flowNodeInstanceId = flowNodeInstanceId;\n    }\n\n    @Override\n    public CompletableFuture<Void> work(Map<String, Object> context) throws Exception {\n        // the corresponding wrapping work will take care of adding other information...\n        Supplier<AbstractMDC> mdc = () -> new AbstractMDC(\n                Map.of(MDCConstants.FLOW_NODE_INSTANCE_ID, Long.toString(flowNodeInstanceId))) {\n        };\n        return MDCHelper.tryWithMDC(mdc, () -> super.work(context));\n    }\n\n    @Override\n    protected void setExceptionContext(final ExceptionContext sBonitaException, final Map<String, Object> context)\n            throws SBonitaException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor(context);\n        final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService();\n        final SFlowNodeInstance flowNodeInstance = activityInstanceService.getFlowNodeInstance(flowNodeInstanceId);\n        sBonitaException.setFlowNodeDefinitionIdOnContext(flowNodeInstance.getFlowNodeDefinitionId());\n        sBonitaException.setFlowNodeInstanceIdOnContext(flowNodeInstanceId);\n        sBonitaException.setFlowNodeNameOnContext(flowNodeInstance.getName());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/MessageInstanceContextWork.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work.failurewrapping;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.commons.exceptions.ExceptionContext;\nimport org.bonitasoft.engine.work.BonitaWork;\n\n/**\n * @author Aurelien Pupier\n * @author Celine Souchet\n */\npublic class MessageInstanceContextWork extends TxInHandleFailureWrappingWork {\n\n    private String messageName;\n    private String targetProcess;\n    private String targetFlowNode;\n    private String eventType;\n\n    public MessageInstanceContextWork(BonitaWork work, String messageName, String targetProcess, String targetFlowNode,\n            String eventType) {\n        super(work);\n        this.messageName = messageName;\n        this.targetProcess = targetProcess;\n        this.targetFlowNode = targetFlowNode;\n        this.eventType = eventType;\n    }\n\n    @Override\n    protected void setExceptionContext(final ExceptionContext sBonitaException, final Map<String, Object> context) {\n        sBonitaException.setMessageInstanceNameOnContext(messageName);\n        sBonitaException.setMessageInstanceTargetProcessOnContext(targetProcess);\n        sBonitaException.setMessageInstanceTargetFlowNodeOnContext(targetFlowNode);\n        sBonitaException.setWaitingMessageEventTypeOnContext(eventType);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/ProcessDefinitionContextWork.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work.failurewrapping;\n\nimport java.util.Map;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.function.Supplier;\n\nimport org.bonitasoft.engine.commons.exceptions.ExceptionContext;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.mdc.AbstractMDC;\nimport org.bonitasoft.engine.mdc.MDCConstants;\nimport org.bonitasoft.engine.mdc.MDCHelper;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.work.BonitaWork;\n\n/**\n * Adding context information about Process definition to exception for better logging\n *\n * @author Celine Souchet\n */\npublic class ProcessDefinitionContextWork extends TxInHandleFailureWrappingWork {\n\n    private static final long serialVersionUID = 6958842321501639910L;\n\n    private long processDefinitionId;\n\n    /**\n     * @param wrappedWork\n     *        The work to wrap\n     * @param processDefinitionId\n     *        The identifier of the process definition\n     */\n    public ProcessDefinitionContextWork(final BonitaWork wrappedWork, final long processDefinitionId) {\n        super(wrappedWork);\n        this.processDefinitionId = processDefinitionId;\n    }\n\n    @Override\n    public CompletableFuture<Void> work(Map<String, Object> context) throws Exception {\n        // the corresponding wrapping work will take care of adding other information...\n        Supplier<AbstractMDC> mdc = () -> new AbstractMDC(Map.of(\n                MDCConstants.PROCESS_DEFINITION_ID, Long.toString(processDefinitionId))) {\n        };\n        return MDCHelper.tryWithMDC(mdc, () -> super.work(context));\n    }\n\n    @Override\n    protected void setExceptionContext(final ExceptionContext sBonitaException, final Map<String, Object> context)\n            throws SBonitaException {\n        final ServiceAccessor serviceAccessor = getServiceAccessor(context);\n        final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService();\n        final SProcessDefinitionDeployInfo processDeploymentInfo = processDefinitionService\n                .getProcessDeploymentInfo(processDefinitionId);\n\n        sBonitaException.setProcessDefinitionIdOnContext(processDefinitionId);\n        sBonitaException.setProcessDefinitionNameOnContext(processDeploymentInfo.getName());\n        sBonitaException.setProcessDefinitionVersionOnContext(processDeploymentInfo.getVersion());\n    }\n\n    /**\n     * @return The identifier of the process definition\n     * @since 6.3\n     */\n    public long getProcessDefinitionId() {\n        return processDefinitionId;\n    }\n\n    /**\n     * @param processDefinitionId\n     *        The identifier of the process definition\n     * @since 6.3\n     */\n    protected void setProcessDefinitionId(long processDefinitionId) {\n        this.processDefinitionId = processDefinitionId;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/ProcessInstanceContextWork.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work.failurewrapping;\n\nimport java.util.Map;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.function.Supplier;\n\nimport org.bonitasoft.engine.commons.exceptions.ExceptionContext;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.execution.work.WrappingBonitaWork;\nimport org.bonitasoft.engine.mdc.AbstractMDC;\nimport org.bonitasoft.engine.mdc.MDCConstants;\nimport org.bonitasoft.engine.mdc.MDCHelper;\nimport org.bonitasoft.engine.service.ServiceAccessor;\n\n/**\n * Adding context information about Process definition and instance to exception for better logging\n *\n * @author Celine Souchet\n */\npublic class ProcessInstanceContextWork extends TxInHandleFailureWrappingWork {\n\n    private static final long serialVersionUID = 6958842321501639910L;\n\n    private long processInstanceId;\n\n    private long rootProcessInstanceId;\n\n    /**\n     * @param wrappedWork\n     *        The work to wrap\n     * @param processInstanceId\n     *        The identifier of the process instance\n     */\n    public ProcessInstanceContextWork(final WrappingBonitaWork wrappedWork, final long processInstanceId) {\n        this(wrappedWork, processInstanceId, -1);\n    }\n\n    /**\n     * @param wrappedWork\n     *        The work to wrap\n     * @param processDefinitionId\n     *        The identifier of the process definition\n     * @param processInstanceId\n     *        The identifier of the process instance\n     * @param rootProcessInstanceId\n     *        The identifier of the root process instance\n     */\n    public ProcessInstanceContextWork(final WrappingBonitaWork wrappedWork, final long processInstanceId,\n            final long rootProcessInstanceId) {\n        super(wrappedWork);\n        this.processInstanceId = processInstanceId;\n        this.rootProcessInstanceId = rootProcessInstanceId;\n    }\n\n    @Override\n    public CompletableFuture<Void> work(Map<String, Object> context) throws Exception {\n        // the corresponding wrapping work will take care of adding other information...\n        Supplier<AbstractMDC> mdc = () -> new AbstractMDC(Map.of(\n                MDCConstants.PROCESS_INSTANCE_ID, Long.toString(processInstanceId),\n                MDCConstants.ROOT_PROCESS_INSTANCE_ID,\n                Long.toString(rootProcessInstanceId == -1 ? processInstanceId : rootProcessInstanceId))) {\n        };\n        return MDCHelper.tryWithMDC(mdc, () -> super.work(context));\n    }\n\n    protected void setProcessInstanceId(final long id) {\n        processInstanceId = id;\n    }\n\n    protected void setRootProcessInstanceId(final long id) {\n        rootProcessInstanceId = id;\n    }\n\n    @Override\n    protected void setExceptionContext(final ExceptionContext sBonitaException, final Map<String, Object> context)\n            throws SBonitaException {\n        if (rootProcessInstanceId < 0) {\n            final ServiceAccessor serviceAccessor = getServiceAccessor(context);\n            final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService();\n            final SProcessInstance sProcessInstance = processInstanceService.getProcessInstance(processInstanceId);\n            rootProcessInstanceId = sProcessInstance.getRootProcessInstanceId();\n        }\n        sBonitaException.setProcessInstanceIdOnContext(processInstanceId);\n        sBonitaException.setRootProcessInstanceIdOnContext(rootProcessInstanceId);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/TriggerSignalWork.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work.failurewrapping;\n\nimport java.util.Map;\nimport java.util.concurrent.CompletableFuture;\n\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent;\nimport org.bonitasoft.engine.execution.work.TenantAwareBonitaWork;\nimport org.bonitasoft.engine.service.ServiceAccessor;\n\n/**\n * @author Baptiste Mesta\n */\npublic class TriggerSignalWork extends TenantAwareBonitaWork {\n\n    private long signalId;\n    private String signalName;\n\n    public TriggerSignalWork(long signalId, String signalName) {\n        this.signalId = signalId;\n        this.signalName = signalName;\n    }\n\n    @Override\n    public String getDescription() {\n        return getClass().getSimpleName() + \" waitingSignalEvent: \" + signalId;\n    }\n\n    @Override\n    public CompletableFuture<Void> work(Map<String, Object> context) throws Exception {\n        ServiceAccessor serviceAccessor = getServiceAccessor(context);\n        SWaitingSignalEvent listeningSignal = serviceAccessor.getEventInstanceService()\n                .getWaitingSignalEvent(signalId);\n        serviceAccessor.getEventsHandler().triggerCatchEvent(listeningSignal, null);\n        return CompletableFuture.completedFuture(null);\n    }\n\n    @Override\n    public void handleFailure(Throwable e, Map<String, Object> context) throws Exception {\n        throw new UnsupportedOperationException(\"No automatic failure handling for signals. See recovery procedure.\");\n    }\n\n    @Override\n    public String getRecoveryProcedure() {\n        return \"send the signal \" + signalName + \" again\";\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/TxInHandleFailureWrappingWork.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work.failurewrapping;\n\nimport java.util.Map;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.CompletableFuture;\n\nimport org.bonitasoft.engine.commons.exceptions.ExceptionContext;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.execution.work.WrappingBonitaWork;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.bonitasoft.engine.work.BonitaWork;\n\n/**\n * This work manages the transaction in its handleFailure method.\n * Don't use this work with {@link org.bonitasoft.engine.execution.work.TxBonitaWork}\n *\n * @author Celine Souchet\n */\npublic abstract class TxInHandleFailureWrappingWork extends WrappingBonitaWork {\n\n    protected TxInHandleFailureWrappingWork(final BonitaWork work) {\n        super(work);\n    }\n\n    @Override\n    public CompletableFuture<Void> work(final Map<String, Object> context) throws Exception {\n        return getWrappedWork().work(context);\n    }\n\n    @Override\n    public void handleFailure(final Throwable e, final Map<String, Object> context) throws Exception {\n        // Enrich the exception before log it.\n        if (e instanceof ExceptionContext exceptionContext) {\n            final ServiceAccessor serviceAccessor = getServiceAccessor(context);\n            final UserTransactionService transactionService = serviceAccessor.getUserTransactionService();\n            transactionService.executeInTransaction((Callable<Void>) () -> {\n                setExceptionContext(exceptionContext, context);\n                return null;\n            });\n        }\n        getWrappedWork().handleFailure(e, context);\n    }\n\n    protected abstract void setExceptionContext(final ExceptionContext exceptionContext,\n            final Map<String, Object> context) throws SBonitaException;\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/expression/BusinessDataExpressionExecutorStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.business.data.BusinessDataRetriever;\nimport org.bonitasoft.engine.business.data.RefBusinessDataRetriever;\nimport org.bonitasoft.engine.commons.Container;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.model.ExpressionKind;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.operation.BusinessDataContext;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\n/**\n * @author Colin Puy\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n * @author Matthieu Chaffotte\n */\npublic class BusinessDataExpressionExecutorStrategy extends CommonBusinessDataExpressionExecutorStrategy {\n\n    private final RefBusinessDataRetriever refBusinessDataRetriever;\n\n    private final BusinessDataRetriever businessDataRetriever;\n\n    public BusinessDataExpressionExecutorStrategy(final RefBusinessDataRetriever refBusinessDataRetriever,\n            BusinessDataRetriever businessDataRetriever) {\n        super();\n        this.refBusinessDataRetriever = refBusinessDataRetriever;\n        this.businessDataRetriever = businessDataRetriever;\n    }\n\n    @Override\n    public ExpressionKind getExpressionKind() {\n        return KIND_BUSINESS_DATA;\n    }\n\n    @Override\n    public Object evaluate(final SExpression expression, final Map<String, Object> context,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState) throws SExpressionEvaluationException {\n        final String businessDataName = expression.getContent();\n        if (context.containsKey(businessDataName)) {\n            return context.get(businessDataName);\n        }\n        final Long containerId = (Long) context.get(SExpressionContext.CONTAINER_ID_KEY);\n        final String containerType = (String) context.get(SExpressionContext.CONTAINER_TYPE_KEY);\n        try {\n            final SRefBusinessDataInstance refBusinessDataInstance = refBusinessDataRetriever\n                    .getRefBusinessDataInstance(new BusinessDataContext(\n                            businessDataName, new Container(containerId, containerType)));\n            return businessDataRetriever.getBusinessData(refBusinessDataInstance);\n        } catch (final SBonitaReadException | SFlowNodeReadException e) {\n            throw new SExpressionEvaluationException(\n                    \"Unable to retrieve business data instance with name \" + businessDataName, e, businessDataName);\n        } catch (final SBonitaException e) {\n            setProcessInstanceId(containerId, containerType, e);\n            throw new SExpressionEvaluationException(e, expression.getName());\n        }\n    }\n\n    @Override\n    public List<Object> evaluate(final List<SExpression> expressions, final Map<String, Object> context,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState) throws SExpressionEvaluationException {\n        final List<Object> bizData = new ArrayList<>(expressions.size());\n        final List<String> alreadyEvaluatedExpressionContent = new ArrayList<>();\n        for (final SExpression expression : expressions) {\n            if (!alreadyEvaluatedExpressionContent.contains(expression.getContent())) {\n                bizData.add(evaluate(expression, context, resolvedExpressions, containerState));\n                alreadyEvaluatedExpressionContent.add(expression.getContent());\n            }\n        }\n        return bizData;\n    }\n\n    @Override\n    public boolean mustPutEvaluatedExpressionInContext() {\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/expression/BusinessDataReferenceExpressionExecutorStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.business.data.RefBusinessDataRetriever;\nimport org.bonitasoft.engine.commons.Container;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.model.ExpressionKind;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.operation.BusinessDataContext;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Colin Puy\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n * @author Matthieu Chaffotte\n */\npublic class BusinessDataReferenceExpressionExecutorStrategy extends CommonBusinessDataExpressionExecutorStrategy {\n\n    private final RefBusinessDataRetriever refBusinessDataRetriever;\n\n    public BusinessDataReferenceExpressionExecutorStrategy(final RefBusinessDataRetriever refBusinessDataRetriever) {\n        this.refBusinessDataRetriever = refBusinessDataRetriever;\n    }\n\n    @Override\n    public ExpressionKind getExpressionKind() {\n        return KIND_BUSINESS_DATA_REFERENCE;\n    }\n\n    @Override\n    public Object evaluate(final SExpression expression, final Map<String, Object> context,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState) throws SExpressionEvaluationException {\n        final String businessDataName = expression.getContent();\n        final Long containerId = (Long) context.get(SExpressionContext.CONTAINER_ID_KEY);\n        final String containerType = (String) context.get(SExpressionContext.CONTAINER_TYPE_KEY);\n        try {\n            final SRefBusinessDataInstance refBusinessDataInstance = refBusinessDataRetriever\n                    .getRefBusinessDataInstance(new BusinessDataContext(\n                            businessDataName, new Container(containerId, containerType)));\n            return ModelConvertor.toBusinessDataReference(refBusinessDataInstance);\n        } catch (final SBonitaReadException e) {\n            throw new SExpressionEvaluationException(e,\n                    \"Unable to retrieve business data instance with name \" + businessDataName);\n        } catch (final SBonitaException e) {\n            setProcessInstanceId(containerId, containerType, e);\n            throw new SExpressionEvaluationException(e, expression.getName());\n        }\n    }\n\n    @Override\n    public List<Object> evaluate(final List<SExpression> expressions, final Map<String, Object> context,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState) throws SExpressionEvaluationException {\n        final List<Object> bizData = new ArrayList<>(expressions.size());\n        for (final SExpression expression : expressions) {\n            bizData.add(evaluate(expression, context, resolvedExpressions, containerState));\n        }\n        return bizData;\n    }\n\n    @Override\n    public boolean mustPutEvaluatedExpressionInContext() {\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/expression/BusinessObjectDAOExpressionStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression;\n\nimport java.lang.reflect.Constructor;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.business.data.BusinessDataRepository;\nimport org.bonitasoft.engine.business.data.SBusinessDataRepositoryException;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.model.ExpressionKind;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class BusinessObjectDAOExpressionStrategy extends NonEmptyContentExpressionExecutorStrategy {\n\n    private final BusinessDataRepository businessDataRepository;\n\n    public BusinessObjectDAOExpressionStrategy(final BusinessDataRepository businessDataRepository) {\n        this.businessDataRepository = businessDataRepository;\n    }\n\n    @Override\n    public ExpressionKind getExpressionKind() {\n        return KIND_BUSINESS_OBJECT_DAO;\n    }\n\n    @Override\n    public Object evaluate(final SExpression expression, final Map<String, Object> context,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState) throws SExpressionEvaluationException {\n        final String daoName = expression.getContent();\n        if (context.containsKey(daoName)) {\n            return context.get(daoName);\n        }\n        final String daoServerImplementation = getDAOServerImplementationFromInterface(expression.getReturnType());\n        try {\n            return instantiateDAO(daoServerImplementation);\n        } catch (Exception e) {\n            throw new SExpressionEvaluationException(\n                    \"Unable to instantiate Business Object Server DAO implementation\" + daoServerImplementation, e,\n                    expression.getName());\n        }\n    }\n\n    protected Object instantiateDAO(final String daoClassName) throws Exception {\n        Class daoImplClass = Thread.currentThread().getContextClassLoader().loadClass(daoClassName);\n        if (daoImplClass != null) {\n            Constructor constructor = daoImplClass.getConstructor(BusinessDataRepository.class);\n            return constructor.newInstance(businessDataRepository);\n        }\n        throw new SBusinessDataRepositoryException(\"Cannot load class \" + daoClassName);\n    }\n\n    protected String getDAOServerImplementationFromInterface(final String daoClassName) {\n        int pointIdx = daoClassName.lastIndexOf('.');\n        return daoClassName.substring(0, pointIdx + 1) + \"server.\" + daoClassName.substring(pointIdx + 1) + \"Impl\";\n    }\n\n    @Override\n    public List<Object> evaluate(final List<SExpression> expressions, final Map<String, Object> context,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState) throws SExpressionEvaluationException {\n        List<Object> daos = new ArrayList<>(expressions.size());\n        for (SExpression expression : expressions) {\n            daos.add(evaluate(expression, context, resolvedExpressions, containerState));\n        }\n        return daos;\n    }\n\n    @Override\n    public boolean mustPutEvaluatedExpressionInContext() {\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/expression/CommonBusinessDataExpressionExecutorStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Baptiste Mesta\n */\npublic abstract class CommonBusinessDataExpressionExecutorStrategy extends NonEmptyContentExpressionExecutorStrategy {\n\n    void setProcessInstanceId(final Long containerId, final String containerType, final SBonitaException e) {\n        if (\"PROCESS_INSTANCE\".equals(containerType)) {\n            e.setProcessInstanceIdOnContext(containerId);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/expression/ContractInputExpressionExecutorStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.core.contract.data.ContractDataService;\nimport org.bonitasoft.engine.core.contract.data.SContractDataNotFoundException;\nimport org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.ExpressionKind;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class ContractInputExpressionExecutorStrategy implements ExpressionExecutorStrategy {\n\n    private final ContractDataService contractDataService;\n\n    public ContractInputExpressionExecutorStrategy(final ContractDataService contractDataService) {\n        super();\n        this.contractDataService = contractDataService;\n    }\n\n    @Override\n    public Object evaluate(final SExpression expression, final Map<String, Object> context,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState)\n            throws SExpressionEvaluationException, SExpressionDependencyMissingException {\n        final Long containerId = (Long) context.get(CONTAINER_ID_KEY);\n        final String containerType = (String) context.get(CONTAINER_TYPE_KEY);\n        if (containerId == null) {\n            throw new SExpressionEvaluationException(\"Unable to evaluate contract input without container id\",\n                    expression.getName());\n        }\n        try {\n            if (\"PROCESS_INSTANCE\".equals(containerType)) {\n                return contractDataService.getProcessDataValue(containerId, expression.getContent());\n            } else {\n                return contractDataService.getUserTaskDataValue(containerId, expression.getContent());\n            }\n        } catch (final SContractDataNotFoundException | SBonitaReadException e) {\n            throw new SExpressionEvaluationException(e, expression.getName());\n        }\n    }\n\n    @Override\n    public void validate(final SExpression expression) throws SInvalidExpressionException {\n        // No validation because the type validation can only be done after evaluation\n    }\n\n    @Override\n    public ExpressionKind getExpressionKind() {\n        return KIND_CONTRACT_INPUT;\n    }\n\n    @Override\n    public List<Object> evaluate(final List<SExpression> expressions, final Map<String, Object> context,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState)\n            throws SExpressionEvaluationException, SExpressionDependencyMissingException {\n        final List<Object> results = new ArrayList<Object>();\n        for (final SExpression expression : expressions) {\n            results.add(evaluate(expression, context, resolvedExpressions, containerState));\n        }\n        return results;\n    }\n\n    @Override\n    public boolean mustPutEvaluatedExpressionInContext() {\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/expression/DataExpressionExecutorStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.lang.model.SourceVersion;\n\nimport org.bonitasoft.engine.data.instance.api.DataInstanceService;\nimport org.bonitasoft.engine.data.instance.api.ParentContainerResolver;\nimport org.bonitasoft.engine.data.instance.exception.SDataInstanceException;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.SADataInstance;\nimport org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.ExpressionKind;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Zhao Na\n * @author Celine Souchet\n */\npublic class DataExpressionExecutorStrategy extends NonEmptyContentExpressionExecutorStrategy {\n\n    public static final String TIME = \"time\";\n\n    private final DataInstanceService dataService;\n\n    private ParentContainerResolver parentContainerResolver;\n\n    public DataExpressionExecutorStrategy(final DataInstanceService dataService,\n            final ParentContainerResolver parentContainerResolver) {\n        this.dataService = dataService;\n        this.parentContainerResolver = parentContainerResolver;\n    }\n\n    @Override\n    public Object evaluate(final SExpression expression, final Map<String, Object> context,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState)\n            throws SExpressionDependencyMissingException, SExpressionEvaluationException {\n        return evaluate(Collections.singletonList(expression), context, resolvedExpressions, containerState).get(0);\n    }\n\n    @Override\n    public void validate(final SExpression expression) throws SInvalidExpressionException {\n        // $ can be part of variable name\n        super.validate(expression);\n        if (!SourceVersion.isIdentifier(expression.getContent()) || SourceVersion.isKeyword(expression.getContent())) {\n            throw new SInvalidExpressionException(\n                    expression.getContent() + \" is not a valid data name in expression : \" + expression,\n                    expression.getName());\n        }\n    }\n\n    @Override\n    public ExpressionKind getExpressionKind() {\n        return KIND_VARIABLE;\n    }\n\n    @Override\n    public List<Object> evaluate(final List<SExpression> expressions, final Map<String, Object> context,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState)\n            throws SExpressionDependencyMissingException, SExpressionEvaluationException {\n        final int maxExpressionSize = expressions.size();\n        final List<String> dataNames = new ArrayList<>(maxExpressionSize);\n        final Map<String, Serializable> results = new HashMap<>(maxExpressionSize);\n        for (final SExpression sExpression : expressions) {\n            final String dataName = sExpression.getContent();\n            if (context.containsKey(dataName)) {\n                final Serializable value = (Serializable) context.get(dataName);\n                results.put(dataName, value);\n            } else {\n                dataNames.add(dataName);\n            }\n        }\n\n        if (dataNames.isEmpty()) {\n            return buildExpressionResultSameOrderAsInputList(expressions, results);\n        }\n        if (context == null || !context.containsKey(CONTAINER_ID_KEY) || !context.containsKey(CONTAINER_TYPE_KEY)) {\n            throw new SExpressionDependencyMissingException(\n                    \"The context to evaluate the data '\" + dataNames + \"' was not set\");\n        }\n        try {\n            final long containerId = (Long) context.get(CONTAINER_ID_KEY);\n            final String containerType = (String) context.get(CONTAINER_TYPE_KEY);\n            final Long time = (Long) context.get(TIME);\n            if (time != null) {\n                final List<SADataInstance> dataInstances = dataService.getSADataInstances(containerId, containerType,\n                        parentContainerResolver, dataNames, time);\n                for (final SADataInstance dataInstance : dataInstances) {\n                    dataNames.remove(dataInstance.getName());\n                    results.put(dataInstance.getName(), dataInstance.getValue());\n                }\n            }\n            final List<SDataInstance> dataInstances = dataService.getDataInstances(dataNames, containerId,\n                    containerType, parentContainerResolver);\n            for (final SDataInstance dataInstance : dataInstances) {\n                dataNames.remove(dataInstance.getName());\n                results.put(dataInstance.getName(), dataInstance.getValue());\n            }\n            if (!dataNames.isEmpty()) {\n                throw new SExpressionEvaluationException(\"Some data were not found \" + dataNames, dataNames.get(0));\n            }\n            return buildExpressionResultSameOrderAsInputList(expressions, results);\n        } catch (final SDataInstanceException e) {\n            throw new SExpressionEvaluationException(e, null);\n        }\n    }\n\n    private List<Object> buildExpressionResultSameOrderAsInputList(final List<SExpression> expressions,\n            final Map<String, Serializable> results) {\n        final List<Object> list = new ArrayList<>(expressions.size());\n        for (final SExpression expression : expressions) {\n            list.add(results.get(expression.getContent()));\n        }\n        return list;\n    }\n\n    @Override\n    public boolean mustPutEvaluatedExpressionInContext() {\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/expression/DocumentListReferenceExpressionExecutorStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.document.Document;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.core.document.api.DocumentService;\nimport org.bonitasoft.engine.core.document.api.impl.DocumentHelper;\nimport org.bonitasoft.engine.core.document.model.AbstractSMappedDocument;\nimport org.bonitasoft.engine.core.document.model.SMappedDocument;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.model.ExpressionKind;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class DocumentListReferenceExpressionExecutorStrategy extends NonEmptyContentExpressionExecutorStrategy {\n\n    private final DocumentService documentService;\n\n    private final ActivityInstanceService activityInstanceService;\n\n    private final DocumentHelper documentHelper;\n\n    public DocumentListReferenceExpressionExecutorStrategy(final DocumentService documentService,\n            final ActivityInstanceService activityInstanceService,\n            final ProcessDefinitionService processDefinitionService,\n            final ProcessInstanceService processInstanceService) {\n        this.documentService = documentService;\n        this.activityInstanceService = activityInstanceService;\n        documentHelper = new DocumentHelper(documentService, processDefinitionService, processInstanceService);\n    }\n\n    @Override\n    public Object evaluate(final SExpression expression, final Map<String, Object> context,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState)\n            throws SExpressionEvaluationException, SExpressionDependencyMissingException {\n        return evaluate(Collections.singletonList(expression), context, resolvedExpressions, containerState).get(0);\n    }\n\n    @Override\n    public ExpressionKind getExpressionKind() {\n        return KIND_DOCUMENT_LIST;\n    }\n\n    @Override\n    public List<Object> evaluate(final List<SExpression> expressions, final Map<String, Object> context,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState)\n            throws SExpressionEvaluationException, SExpressionDependencyMissingException {\n        final Long containerId = (Long) context.get(CONTAINER_ID_KEY);\n        final String containerType = (String) context.get(CONTAINER_TYPE_KEY);\n\n        try {\n            final Long time = (Long) context.get(\"time\");\n            final long processInstanceId = getProcessInstance(containerId, containerType, time != null);\n            final List<Object> results = new ArrayList<>(expressions.size());\n            for (final SExpression expression : expressions) {\n                final String docListName = expression.getContent();\n                if (context.containsKey(docListName)) {\n                    results.add(context.get(docListName));\n                } else {\n                    results.add(getDocumentList(processInstanceId, docListName, time));\n                }\n            }\n            return results;\n        } catch (final SExpressionDependencyMissingException e) {\n            throw e;\n        } catch (final SBonitaException e) {\n            throw new SExpressionEvaluationException(e, null);\n        }\n    }\n\n    // Visible for Testing\n    List<Document> getDocumentList(final long processInstanceId, final String name, final Long time)\n            throws SBonitaReadException {\n        final List<AbstractSMappedDocument> documentList = getAllDocumentOfTheList(processInstanceId, name, time);\n        try {\n            if (documentList.isEmpty()\n                    && !documentHelper.isListDefinedInDefinition(name, processInstanceId)) {\n                return null;\n            }\n        } catch (final SObjectNotFoundException e) {\n            return null;\n        }\n        return ModelConvertor.toDocuments(documentList, documentService);\n    }\n\n    private List<AbstractSMappedDocument> getAllDocumentOfTheList(final long processInstanceId, final String name,\n            final Long time) throws SBonitaReadException {\n        if (time != null) {\n            return documentService.getDocumentList(name, processInstanceId, time);\n        }\n        QueryOptions queryOptions = new QueryOptions(0, 100);\n        List<SMappedDocument> mappedDocuments;\n        final List<AbstractSMappedDocument> result = new ArrayList<>();\n        do {\n            mappedDocuments = documentService.getDocumentList(name, processInstanceId, queryOptions.getFromIndex(),\n                    queryOptions.getNumberOfResults());\n            result.addAll(mappedDocuments);\n            queryOptions = QueryOptions.getNextPage(queryOptions);\n        } while (mappedDocuments.size() == 100);\n        return result;\n    }\n\n    private long getProcessInstance(final Long containerId, final String containerType,\n            final boolean flowNodeIsArchived) throws SBonitaException {\n        if (containerId == null || containerType == null) {\n            throw new SExpressionDependencyMissingException(\"The context to retrieve the document is not set.\");\n        }\n        if (DataInstanceContainer.PROCESS_INSTANCE.name().equals(containerType)) {\n            return containerId;\n        }\n        if (flowNodeIsArchived) {\n            return activityInstanceService.getMostRecentArchivedActivityInstance(containerId)\n                    .getParentProcessInstanceId();\n        }\n        return activityInstanceService.getFlowNodeInstance(containerId).getParentProcessInstanceId();\n    }\n\n    @Override\n    public boolean mustPutEvaluatedExpressionInContext() {\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/expression/DocumentReferenceExpressionExecutorStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.document.Document;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.core.document.api.DocumentService;\nimport org.bonitasoft.engine.core.document.model.AbstractSMappedDocument;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.model.ExpressionKind;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class DocumentReferenceExpressionExecutorStrategy extends NonEmptyContentExpressionExecutorStrategy {\n\n    private final DocumentService documentService;\n\n    private final ActivityInstanceService activityInstanceService;\n\n    public DocumentReferenceExpressionExecutorStrategy(final DocumentService documentService,\n            final ActivityInstanceService activityInstanceService) {\n        this.documentService = documentService;\n        this.activityInstanceService = activityInstanceService;\n    }\n\n    @Override\n    public Object evaluate(final SExpression expression, final Map<String, Object> context,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState)\n            throws SExpressionEvaluationException, SExpressionDependencyMissingException {\n        return evaluate(Collections.singletonList(expression), context, resolvedExpressions, containerState).get(0);\n    }\n\n    @Override\n    public ExpressionKind getExpressionKind() {\n        return KIND_DOCUMENT;\n    }\n\n    @Override\n    public List<Object> evaluate(final List<SExpression> expressions, final Map<String, Object> context,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState)\n            throws SExpressionEvaluationException, SExpressionDependencyMissingException {\n        final Long containerId = (Long) context.get(CONTAINER_ID_KEY);\n        final String containerType = (String) context.get(CONTAINER_TYPE_KEY);\n        final Long time = (Long) context.get(\"time\");\n\n        try {\n            final long processInstanceId = getProcessInstance(containerId, containerType, time);\n            final List<Object> results = new ArrayList<>(expressions.size());\n            for (final SExpression expression : expressions) {\n                final String docName = expression.getContent();\n                if (context.containsKey(docName)) {\n                    results.add(context.get(docName));\n                } else {\n                    results.add(getDocument(processInstanceId, expression, time));\n                }\n            }\n            return results;\n        } catch (final SExpressionDependencyMissingException e) {\n            throw e;\n        } catch (final SBonitaException e) {\n            throw new SExpressionEvaluationException(e, null);\n        }\n    }\n\n    private Document getDocument(final long processInstanceId, final SExpression expression, final Long time)\n            throws SBonitaReadException {\n        try {\n            AbstractSMappedDocument document;\n            if (time != null) {\n                document = documentService.getMappedDocument(processInstanceId, expression.getContent(), time);\n            } else {\n                document = documentService.getMappedDocument(processInstanceId, expression.getContent());\n            }\n            return ModelConvertor.toDocument(document, documentService);\n        } catch (final SObjectNotFoundException e) {\n            return null;\n        }\n    }\n\n    // visible for testing\n    long getProcessInstance(final Long containerId, final String containerType, Long time)\n            throws SFlowNodeNotFoundException, SFlowNodeReadException,\n            SExpressionDependencyMissingException, SActivityInstanceNotFoundException {\n        if (containerId == null || containerType == null) {\n            throw new SExpressionDependencyMissingException(\"The context to retrieve the document is not set.\");\n        }\n        if (DataInstanceContainer.PROCESS_INSTANCE.name().equals(containerType)) {\n            return containerId;\n        }\n        // on a flownode instance, containerId is always the original id:\n        if (time != null) {\n            return activityInstanceService.getMostRecentArchivedActivityInstance(containerId)\n                    .getParentProcessInstanceId();\n        } else {\n            return activityInstanceService.getFlowNodeInstance(containerId).getParentProcessInstanceId();\n        }\n    }\n\n    @Override\n    public boolean mustPutEvaluatedExpressionInContext() {\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/expression/EngineConstantExpressionBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression;\n\nimport org.bonitasoft.engine.api.APIAccessor;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException;\nimport org.bonitasoft.engine.connector.EngineExecutionContext;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.expression.model.builder.SExpressionBuilder;\nimport org.bonitasoft.engine.expression.model.builder.SExpressionBuilderFactory;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic final class EngineConstantExpressionBuilder {\n\n    public static SExpression getConnectorAPIAccessorExpression() {\n        final SExpressionBuilder builder = BuilderFactory.get(SExpressionBuilderFactory.class).createNewInstance();\n        builder.setContent(\"connectorApiAccessor\").setExpressionType(ExpressionType.TYPE_ENGINE_CONSTANT.name())\n                .setReturnType(APIAccessor.class.getName());\n        try {\n            return builder.done();\n        } catch (final SInvalidExpressionException e) {\n            // Never happens !!\n            throw new SBonitaRuntimeException(e);\n        }\n    }\n\n    public static SExpression getEngineExecutionContext() {\n        final SExpressionBuilder builder = BuilderFactory.get(SExpressionBuilderFactory.class).createNewInstance();\n        builder.setContent(\"engineExecutionContext\").setExpressionType(ExpressionType.TYPE_ENGINE_CONSTANT.name())\n                .setReturnType(EngineExecutionContext.class.getName());\n        try {\n            return builder.done();\n        } catch (final SInvalidExpressionException e) {\n            // Never happens !!\n            throw new SBonitaRuntimeException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/expression/EngineConstantExpressionExecutorStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.APIAccessor;\nimport org.bonitasoft.engine.api.impl.APIAccessorImpl;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.connector.ConnectorAPIAccessorImpl;\nimport org.bonitasoft.engine.connector.EngineExecutionContext;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.ExpressionKind;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\n\n/**\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class EngineConstantExpressionExecutorStrategy implements ExpressionExecutorStrategy {\n\n    private final ActivityInstanceService activityInstanceService;\n\n    private final ProcessInstanceService processInstanceService;\n\n    private final SessionAccessor sessionAccessor;\n\n    private final SessionService sessionService;\n\n    public EngineConstantExpressionExecutorStrategy(final ActivityInstanceService activityInstanceService,\n            final ProcessInstanceService processInstanceService,\n            final SessionService sessionService, final SessionAccessor sessionAccessor) {\n        this.activityInstanceService = activityInstanceService;\n        this.processInstanceService = processInstanceService;\n        this.sessionService = sessionService;\n        this.sessionAccessor = sessionAccessor;\n    }\n\n    @Override\n    public Serializable evaluate(final SExpression expression, final Map<String, Object> context,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState) throws SExpressionEvaluationException {\n        ExpressionConstants expressionConstant = ExpressionConstantsResolver\n                .getExpressionConstantsFromName(expression.getContent());\n        if (expressionConstant == null) {\n            expressionConstant = ExpressionConstants.API_ACCESSOR;// just to make the expressionConstantsResolver load constants\n            expressionConstant = ExpressionConstantsResolver.getExpressionConstantsFromName(expression.getContent());\n        }\n        final String expressionName = expression.getName();\n        if (expressionConstant == null) {\n            throw new SExpressionEvaluationException(\n                    expression.getContent() + \" is not a valid Engine-provided variable\", expressionName);\n        }\n        return evaluate(context, containerState, expressionConstant, expressionName);\n    }\n\n    public Serializable evaluate(Map<String, Object> context, ContainerState containerState,\n            ExpressionConstants expressionConstant, String expressionName) throws SExpressionEvaluationException {\n        try {\n            switch (expressionConstant) {\n                case API_ACCESSOR:\n                    return getApiAccessor();\n                case CONNECTOR_API_ACCESSOR:\n                    return getConnectorApiAccessor();\n                case ENGINE_EXECUTION_CONTEXT:\n                case ACTIVITY_INSTANCE_ID:\n                case PROCESS_INSTANCE_ID:\n                case ROOT_PROCESS_INSTANCE_ID:\n                case PROCESS_DEFINITION_ID:\n                case TASK_ASSIGNEE_ID:\n                    return getFromContextOrEngineExecutionContext(expressionConstant, context, containerState);\n                case LOGGED_USER_ID:\n                    return getLoggedUserFromSession(context, containerState);\n                case LOOP_COUNTER:\n                    return getLoopCounter(context);\n                case NUMBER_OF_COMPLETED_INSTANCES:\n                case NUMBER_OF_INSTANCES:\n                case NUMBER_OF_ACTIVE_INSTANCES:\n                case NUMBER_OF_TERMINATED_INSTANCES:\n                    return getInstanceNumberForTheEngineConstant(context, expressionConstant);\n                default:\n                    final Object object = context.get(expressionConstant.getEngineConstantName());\n                    if (object == null) {\n                        throw new SExpressionEvaluationException(\n                                \"EngineConstantExpression not supported for: \"\n                                        + expressionConstant.getEngineConstantName(),\n                                expressionName);\n                    }\n                    return (Serializable) object;\n            }\n        } catch (final SProcessInstanceNotFoundException | SProcessInstanceReadException e) {\n            throw new SExpressionEvaluationException(\n                    \"Error retrieving process instance while building EngineExecutionContext as EngineConstantExpression\",\n                    e,\n                    expressionName);\n        } catch (final SFlowNodeReadException | SFlowNodeNotFoundException e) {\n            throw new SExpressionEvaluationException(\n                    \"Error retrieving flow node instance while building EngineExecutionContext as EngineConstantExpression\",\n                    e, expressionName);\n        } catch (final SActivityInstanceNotFoundException e) {\n            throw new SExpressionEvaluationException(\n                    \"Error retrieving activity instance while building EngineExecutionContext as EngineConstantExpression\",\n                    e,\n                    expressionName);\n        } catch (final SBonitaReadException e) {\n            throw new SExpressionEvaluationException(\n                    \"Error while building EngineExecutionContext as EngineConstantExpression\", e, expressionName);\n        } catch (final SBonitaException e) {\n            throw new SExpressionEvaluationException(e, expressionName);\n        }\n    }\n\n    private Serializable getInstanceNumberForTheEngineConstant(Map<String, Object> context,\n            ExpressionConstants expressionConstants)\n            throws SFlowNodeReadException, SFlowNodeNotFoundException, SExpressionEvaluationException {\n        final String containerType = (String) context.get(SExpressionContext.CONTAINER_TYPE_KEY);\n        final long containerId = (Long) context.get(SExpressionContext.CONTAINER_ID_KEY);\n        String engineConstantName = expressionConstants.getEngineConstantName();\n        if (DataInstanceContainer.ACTIVITY_INSTANCE.toString().equals(containerType)) {\n            SFlowNodeInstance flowNodeInstance = activityInstanceService.getFlowNodeInstance(containerId);\n            if (flowNodeInstance instanceof SMultiInstanceActivityInstance) {\n                switch (expressionConstants) {\n                    case NUMBER_OF_TERMINATED_INSTANCES:\n                        return ((SMultiInstanceActivityInstance) flowNodeInstance).getNumberOfTerminatedInstances();\n                    case NUMBER_OF_ACTIVE_INSTANCES:\n                        return ((SMultiInstanceActivityInstance) flowNodeInstance).getNumberOfActiveInstances();\n                    case NUMBER_OF_INSTANCES:\n                        return ((SMultiInstanceActivityInstance) flowNodeInstance).getNumberOfInstances();\n                    case NUMBER_OF_COMPLETED_INSTANCES:\n                        return ((SMultiInstanceActivityInstance) flowNodeInstance).getNumberOfCompletedInstances();\n                    default:\n                        throw new SExpressionEvaluationException(\n                                engineConstantName + \" is not available in this context\",\n                                engineConstantName);\n                }\n            }\n            SMultiInstanceActivityInstance multiInstanceActivityInstance = (SMultiInstanceActivityInstance) activityInstanceService\n                    .getFlowNodeInstance(flowNodeInstance.getParentActivityInstanceId());\n            switch (expressionConstants) {\n                case NUMBER_OF_TERMINATED_INSTANCES:\n                    return multiInstanceActivityInstance.getNumberOfTerminatedInstances();\n                case NUMBER_OF_ACTIVE_INSTANCES:\n                    return multiInstanceActivityInstance.getNumberOfActiveInstances();\n                case NUMBER_OF_INSTANCES:\n                    return multiInstanceActivityInstance.getNumberOfInstances();\n                case NUMBER_OF_COMPLETED_INSTANCES:\n                    return multiInstanceActivityInstance.getNumberOfCompletedInstances();\n                default:\n                    throw new SExpressionEvaluationException(engineConstantName + \" is not available in this context\",\n                            engineConstantName);\n            }\n        }\n        throw new SExpressionEvaluationException(engineConstantName + \" is not available in this context\",\n                engineConstantName);\n    }\n\n    private Serializable getLoopCounter(Map<String, Object> context)\n            throws SExpressionEvaluationException, SFlowNodeReadException, SFlowNodeNotFoundException {\n        final String containerType = (String) context.get(SExpressionContext.CONTAINER_TYPE_KEY);\n        final long containerId = (Long) context.get(SExpressionContext.CONTAINER_ID_KEY);\n        if (DataInstanceContainer.ACTIVITY_INSTANCE.toString().equals(containerType)) {\n            SFlowNodeInstance flowNodeInstance = activityInstanceService.getFlowNodeInstance(containerId);\n            if (flowNodeInstance instanceof SLoopActivityInstance) {\n                return flowNodeInstance.getLoopCounter();\n            }\n            SLoopActivityInstance loopActivityInstance = (SLoopActivityInstance) activityInstanceService\n                    .getFlowNodeInstance(flowNodeInstance.getParentActivityInstanceId());\n            return loopActivityInstance.getLoopCounter();\n        }\n        throw new SExpressionEvaluationException(\"loopCounter is not available in this context\", \"loopCounter\");\n    }\n\n    protected APIAccessor getApiAccessor() {\n        return new APIAccessorImpl();\n    }\n\n    protected APIAccessor getConnectorApiAccessor() {\n        return new ConnectorAPIAccessorImpl();\n    }\n\n    long getLoggedUserFromSession(Map<String, Object> context, ContainerState containerState) {\n        long loggedUserFromSession = sessionService.getLoggedUserFromSession(sessionAccessor);\n        if (loggedUserFromSession <= 0) {\n            //try to get it from the user task assignee if applicable\n            try {\n                return (long) getFromContextOrEngineExecutionContext(ExpressionConstants.TASK_ASSIGNEE_ID, context,\n                        containerState);\n            } catch (SBonitaException e) {\n                return -1L;\n            }\n        }\n        return loggedUserFromSession;\n    }\n\n    public Serializable getFromContextOrEngineExecutionContext(final ExpressionConstants expressionConstant,\n            final Map<String, Object> context,\n            final ContainerState containerState) throws SBonitaException {\n        final Object object = context.get(expressionConstant.getEngineConstantName());\n        if (object == null) {\n            // try to get it from an already evaluated context\n            final EngineExecutionContext engineContext = (EngineExecutionContext) context\n                    .get(ExpressionConstants.ENGINE_EXECUTION_CONTEXT\n                            .getEngineConstantName());\n            if (engineContext != null) {\n                return engineContext.getExpressionConstant(expressionConstant);\n            }\n            return evaluate(expressionConstant, context, containerState);\n        }\n        // we have it already evaluated\n        return (Serializable) object;\n    }\n\n    private Serializable evaluate(final ExpressionConstants expressionConstant, final Map<String, Object> context,\n            final ContainerState containerState) throws SBonitaException {\n        // guess it\n        if (ExpressionConstants.ENGINE_EXECUTION_CONTEXT.equals(expressionConstant)) {\n            return createContext(context, containerState);\n        } else if (context.containsKey(SExpressionContext.CONTAINER_TYPE_KEY)\n                && context.containsKey(SExpressionContext.CONTAINER_ID_KEY)) {\n            final String containerType = (String) context.get(SExpressionContext.CONTAINER_TYPE_KEY);\n            final long containerId = (Long) context.get(SExpressionContext.CONTAINER_ID_KEY);\n            if (ExpressionConstants.PROCESS_DEFINITION_ID.equals(expressionConstant)) {\n                return (Serializable) context.get(SExpressionContext.PROCESS_DEFINITION_ID_KEY);\n            } else if (DataInstanceContainer.ACTIVITY_INSTANCE.toString().equals(containerType)) {\n                return evaluateUsingActivityInstanceContainer(expressionConstant, context, containerId);\n            } else {\n                return evaluateUsingProcessInstanceContainer(expressionConstant, context, containerId);\n            }\n        } else {\n            return -1L;// no container id and not processDefinition\n        }\n    }\n\n    private Serializable evaluateUsingProcessInstanceContainer(final ExpressionConstants expressionConstant,\n            final Map<String, Object> context,\n            final long containerId) throws SProcessInstanceNotFoundException, SProcessInstanceReadException {\n        if (ExpressionConstants.PROCESS_INSTANCE_ID.equals(expressionConstant)) {\n            return containerId;\n        } else if (ExpressionConstants.TASK_ASSIGNEE_ID.equals(expressionConstant)) {\n            return -1L; // the assignee is related to an user task\n        } else {\n            // get the process and fill the others elements\n            fillDependenciesFromProcessInstance(context, containerId);\n            return getNonNullLong(expressionConstant, context);\n        }\n    }\n\n    private Serializable evaluateUsingActivityInstanceContainer(final ExpressionConstants expressionConstant,\n            final Map<String, Object> context,\n            final long containerId) throws SBonitaException {\n        if (ExpressionConstants.ACTIVITY_INSTANCE_ID.equals(expressionConstant)) {\n            return containerId;\n        }\n        // get the activity and fill the others elements\n        fillDependenciesFromFlowNodeInstance(context, containerId);\n        return getNonNullLong(expressionConstant, context);\n    }\n\n    private Serializable getNonNullLong(final ExpressionConstants expressionConstant,\n            final Map<String, Object> context) {\n        final Serializable serializable = (Serializable) context.get(expressionConstant.getEngineConstantName());\n        return serializable == null ? -1L : serializable;\n    }\n\n    private void fillDependenciesFromProcessInstance(final Map<String, Object> context, final long processInstanceId)\n            throws SProcessInstanceNotFoundException,\n            SProcessInstanceReadException {\n        final SProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);\n        context.put(ExpressionConstants.PROCESS_INSTANCE_ID.getEngineConstantName(), processInstance.getId());\n        context.put(ExpressionConstants.ROOT_PROCESS_INSTANCE_ID.getEngineConstantName(),\n                processInstance.getRootProcessInstanceId());\n    }\n\n    void fillDependenciesFromFlowNodeInstance(final Map<String, Object> context, final long flowNodeInstanceId)\n            throws SBonitaException {\n        if (context.get(\"time\") != null) {\n            final SAActivityInstance aActivityInstance = activityInstanceService\n                    .getMostRecentArchivedActivityInstance(flowNodeInstanceId);\n            context.put(ExpressionConstants.PROCESS_INSTANCE_ID.getEngineConstantName(),\n                    aActivityInstance.getLogicalGroup(3));\n            context.put(ExpressionConstants.ROOT_PROCESS_INSTANCE_ID.getEngineConstantName(),\n                    aActivityInstance.getLogicalGroup(1));\n            if (isHumanTask(aActivityInstance)) {\n                final SAHumanTaskInstance saHumanTask = (SAHumanTaskInstance) aActivityInstance;\n                context.put(ExpressionConstants.TASK_ASSIGNEE_ID.getEngineConstantName(), saHumanTask.getAssigneeId());\n            }\n        } else {\n            final SFlowNodeInstance flowNodeInstance = activityInstanceService.getFlowNodeInstance(flowNodeInstanceId);\n            context.put(ExpressionConstants.PROCESS_INSTANCE_ID.getEngineConstantName(),\n                    flowNodeInstance.getLogicalGroup(3));\n            context.put(ExpressionConstants.ROOT_PROCESS_INSTANCE_ID.getEngineConstantName(),\n                    flowNodeInstance.getLogicalGroup(1));\n            if (isHumanTask(flowNodeInstance)) {\n                final SHumanTaskInstance taskInstance = (SHumanTaskInstance) flowNodeInstance;\n                context.put(ExpressionConstants.TASK_ASSIGNEE_ID.getEngineConstantName(), taskInstance.getAssigneeId());\n            }\n        }\n    }\n\n    private boolean isHumanTask(final SAFlowNodeInstance aFlowNodeInstance) {\n        return SFlowNodeType.USER_TASK.equals(aFlowNodeInstance.getType())\n                || SFlowNodeType.MANUAL_TASK.equals(aFlowNodeInstance.getType());\n    }\n\n    private boolean isHumanTask(final SFlowNodeInstance flowNodeInstance) {\n        return SFlowNodeType.USER_TASK.equals(flowNodeInstance.getType())\n                || SFlowNodeType.MANUAL_TASK.equals(flowNodeInstance.getType());\n    }\n\n    private Serializable createContext(final Map<String, Object> context, final ContainerState containerState)\n            throws SProcessInstanceNotFoundException,\n            SProcessInstanceReadException, SActivityInstanceNotFoundException, SFlowNodeReadException,\n            SBonitaReadException {\n        final EngineExecutionContext ctx = new EngineExecutionContext();\n        if (context.containsKey(SExpressionContext.CONTAINER_TYPE_KEY)\n                && context.containsKey(SExpressionContext.CONTAINER_ID_KEY)) {\n            final String containerType = (String) context.get(SExpressionContext.CONTAINER_TYPE_KEY);\n            final long containerId = (Long) context.get(SExpressionContext.CONTAINER_ID_KEY);\n            if (ContainerState.ARCHIVED.equals(containerState)) {\n                if (DataInstanceContainer.ACTIVITY_INSTANCE.toString().equals(containerType)) {\n                    updateContextFromArchivedActivityInstance(ctx, containerId);\n                } else if (DataInstanceContainer.PROCESS_INSTANCE.toString().equals(containerType)) {\n                    updateContextFromArchivedProcessInstance(ctx, containerId);\n                }\n            } else {\n                if (DataInstanceContainer.ACTIVITY_INSTANCE.toString().equals(containerType)) {\n                    updateContextFromActivityInstance(ctx, containerId);\n                } else if (DataInstanceContainer.PROCESS_INSTANCE.toString().equals(containerType)) {\n                    updateContextFromProcessInstance(ctx, containerId);\n                }\n            }\n        }\n        if (context.containsKey(SExpressionContext.PROCESS_DEFINITION_ID_KEY)) {\n            ctx.setProcessDefinitionId((Long) context.get(SExpressionContext.PROCESS_DEFINITION_ID_KEY));\n        }\n        return ctx;\n    }\n\n    private void updateContextFromArchivedProcessInstance(final EngineExecutionContext ctx,\n            final long processInstanceId)\n            throws SBonitaReadException {\n        final SAProcessInstance processInstance = processInstanceService\n                .getLastArchivedProcessInstance(processInstanceId);\n        if (processInstance != null) {\n            ctx.setProcessInstanceId(processInstance.getSourceObjectId());\n            ctx.setRootProcessInstanceId(processInstance.getRootProcessInstanceId());\n        }\n    }\n\n    private void updateContextFromArchivedActivityInstance(final EngineExecutionContext ctx,\n            final long activityInstanceId) throws SBonitaReadException {\n        final SAActivityInstance activityInstance = activityInstanceService\n                .getLastArchivedFlowNodeInstance(SAActivityInstance.class, activityInstanceId);\n        if (activityInstance != null) {\n            ctx.setActivityInstanceId(activityInstance.getSourceObjectId());\n            ctx.setProcessInstanceId(activityInstance.getParentProcessInstanceId());\n            ctx.setRootProcessInstanceId(activityInstance.getRootProcessInstanceId());\n            if (isHumanTask(activityInstance)) {\n                ctx.setTaskAssigneeId(((SAHumanTaskInstance) activityInstance).getAssigneeId());\n            }\n        }\n    }\n\n    private void updateContextFromProcessInstance(final EngineExecutionContext ctx, final long processInstanceId)\n            throws SProcessInstanceNotFoundException,\n            SProcessInstanceReadException {\n        final SProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);\n        ctx.setProcessInstanceId(processInstance.getId());\n        ctx.setRootProcessInstanceId(processInstance.getRootProcessInstanceId());\n    }\n\n    private void updateContextFromActivityInstance(final EngineExecutionContext ctx, final long activityInstanceId)\n            throws SActivityReadException,\n            SActivityInstanceNotFoundException {\n        ctx.setActivityInstanceId(activityInstanceId);\n        final SActivityInstance activityInstance = activityInstanceService.getActivityInstance(activityInstanceId);\n        ctx.setProcessInstanceId(activityInstance.getParentProcessInstanceId());\n        ctx.setRootProcessInstanceId(activityInstance.getRootProcessInstanceId());\n        if (isHumanTask(activityInstance)) {\n            ctx.setTaskAssigneeId(((SHumanTaskInstance) activityInstance).getAssigneeId());\n        }\n    }\n\n    @Override\n    public void validate(final SExpression expression) throws SInvalidExpressionException {\n        if (ExpressionConstantsResolver.getExpressionConstantsFromName(expression.getContent()) == null) {\n            throw new SInvalidExpressionException(\n                    \"Unable to get Engine Constant '\" + expression.getContent() + \"' in expression: \" + expression,\n                    expression.getName());\n        }\n    }\n\n    @Override\n    public ExpressionKind getExpressionKind() {\n        return KIND_ENGINE_CONSTANT;\n    }\n\n    @Override\n    public List<Object> evaluate(final List<SExpression> expressions, final Map<String, Object> context,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState) throws SExpressionEvaluationException {\n        final List<Object> results = new ArrayList<Object>();\n        for (final SExpression sExpression : expressions) {\n            results.add(evaluate(sExpression, context, resolvedExpressions, containerState));\n        }\n        return results;\n    }\n\n    @Override\n    public boolean mustPutEvaluatedExpressionInContext() {\n        return true;\n    }\n\n    public SessionAccessor getSessionAccessor() {\n        return sessionAccessor;\n    }\n\n    public SessionService getSessionService() {\n        return sessionService;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/expression/ParameterExpressionExecutorStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.lang.model.SourceVersion;\n\nimport org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.ExpressionKind;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.parameter.ParameterService;\nimport org.bonitasoft.engine.parameter.SParameter;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\n/**\n * Retrieve a String parameter from the ParameterService. The content of the expression must be the parameter name, as a\n * String.\n * The dependency map must contain a value for 'processDefinitionId' key to identify the process definition context to\n * evaluate the parameter.\n *\n * @see {@link ParameterService}\n * @author Zhao Na\n * @author Celine Souchet\n */\npublic class ParameterExpressionExecutorStrategy extends NonEmptyContentExpressionExecutorStrategy {\n\n    public static final String PROCESS_DEFINITION_ID = \"processDefinitionId\";\n\n    private final ParameterService parameterService;\n\n    public ParameterExpressionExecutorStrategy(final ParameterService parameterService) {\n        this.parameterService = parameterService;\n    }\n\n    @Override\n    public Object evaluate(final SExpression expression, final Map<String, Object> context,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState)\n            throws SExpressionDependencyMissingException, SExpressionEvaluationException {\n        final String expressionContent = expression.getContent();\n        try {\n            if (context != null && !context.isEmpty()) {\n                if (context.containsKey(PROCESS_DEFINITION_ID)) {\n                    final long processDefinitionId = (Long) context.get(PROCESS_DEFINITION_ID);\n                    final SParameter parameter = parameterService.get(processDefinitionId, expressionContent);\n                    if (parameter == null) {\n                        throw new SExpressionEvaluationException(\n                                \"Referenced parameter '\" + expressionContent + \"' does not exist\",\n                                expression.getName());\n                    }\n                    try {\n                        final String returnType = expression.getReturnType();\n                        if (Boolean.class.getName().equals(returnType)) {\n                            return Boolean.parseBoolean(parameter.getValue());\n                        } else if (Double.class.getName().equals(returnType)) {\n                            return Double.parseDouble(parameter.getValue());\n                        } else if (Integer.class.getName().equals(returnType)) {\n                            return Integer.parseInt(parameter.getValue());\n                        } else if (String.class.getName().equals(returnType)) {\n                            return parameter.getValue();\n                        }\n                    } catch (final NumberFormatException e) {\n                        throw new SExpressionEvaluationException(\n                                \"Can't convert value = \" + parameter.getValue() + \" in type = returnType\", e,\n                                expression.getName());\n                    }\n                } else {\n                    throw new SExpressionDependencyMissingException(\n                            \"Mandatory dependency processDefinitionId is missing.\");\n                }\n            }\n        } catch (SBonitaReadException e) {\n            throw new SExpressionEvaluationException(\"Unable to read references parameter '\" + expressionContent + \"' \",\n                    e, expression.getName());\n        }\n        return null;\n    }\n\n    @Override\n    public void validate(final SExpression expression) throws SInvalidExpressionException {\n        super.validate(expression);\n        if (!SourceVersion.isIdentifier(expression.getContent()) || SourceVersion.isKeyword(expression.getContent())) {\n            throw new SInvalidExpressionException(\n                    expression.getContent() + \" is not a valid parameter name in expression : \" + expression,\n                    expression.getName());\n        }\n    }\n\n    @Override\n    public ExpressionKind getExpressionKind() {\n        return KIND_PARAMETER;\n    }\n\n    @Override\n    public List<Object> evaluate(final List<SExpression> expressions, final Map<String, Object> context,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState)\n            throws SExpressionEvaluationException, SExpressionDependencyMissingException {\n        final List<Object> list = new ArrayList<Object>(expressions.size());\n        for (final SExpression expression : expressions) {\n            list.add(evaluate(expression, context, resolvedExpressions, containerState));\n        }\n        return list;\n    }\n\n    @Override\n    public boolean mustPutEvaluatedExpressionInContext() {\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/expression/QueryBusinessDataExpressionExecutorStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.bonitasoft.engine.business.data.BusinessDataRepository;\nimport org.bonitasoft.engine.business.data.NonUniqueResultException;\nimport org.bonitasoft.engine.business.data.proxy.ServerLazyLoader;\nimport org.bonitasoft.engine.business.data.proxy.ServerProxyfier;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.model.ExpressionKind;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class QueryBusinessDataExpressionExecutorStrategy extends NonEmptyContentExpressionExecutorStrategy {\n\n    private final BusinessDataRepository businessDataRepository;\n\n    public QueryBusinessDataExpressionExecutorStrategy(final BusinessDataRepository businessDataRepository) {\n        this.businessDataRepository = businessDataRepository;\n    }\n\n    @Override\n    public Object evaluate(final SExpression expression, final Map<String, Object> context,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState) throws SExpressionEvaluationException {\n        final String queryName = expression.getContent();\n        final String returnType = expression.getReturnType();\n        final Map<String, Serializable> parameters = new HashMap<>();\n        for (final SExpression dependency : expression.getDependencies()) {\n            parameters.put(dependency.getName(), (Serializable) resolvedExpressions.get(dependency.getDiscriminant()));\n        }\n        try {\n            if (isNumber(returnType)) {\n                Class<? extends Serializable> numberClass;\n                try {\n                    numberClass = (Class<? extends Serializable>) Class.forName(returnType);\n                } catch (ClassNotFoundException e) {\n                    throw new SExpressionEvaluationException(e, expression.getName());\n                }\n                return businessDataRepository.findByNamedQuery(queryName, numberClass, parameters);\n            } else if (List.class.getName().equals(returnType)) {\n                List<Entity> entities = businessDataRepository.findListByNamedQuery(queryName, Entity.class, parameters,\n                        getStartIndexParameter(expression.getDependencies(), resolvedExpressions, expression.getName(),\n                                parameters),\n                        getMaxResultParameter(expression.getDependencies(), resolvedExpressions, expression.getName(),\n                                parameters));\n\n                List<Entity> e = new ArrayList<>();\n                for (Entity entity : entities) {\n                    ServerProxyfier proxyfier = new ServerProxyfier(new ServerLazyLoader(businessDataRepository));\n                    Entity proxify = proxyfier.proxify(entity);\n                    e.add(proxify);\n                }\n\n                return e;\n            } else {\n                Entity findByNamedQuery = businessDataRepository.findByNamedQuery(queryName, Entity.class, parameters);\n                ServerProxyfier proxyfier = new ServerProxyfier(new ServerLazyLoader(businessDataRepository));\n                return proxyfier.proxify(findByNamedQuery);\n            }\n        } catch (final NonUniqueResultException nure) {\n            throw new SExpressionEvaluationException(nure, expression.getName());\n        }\n    }\n\n    private int getStartIndexParameter(final List<SExpression> dependencies,\n            final Map<Integer, Object> resolvedExpressions, final String expressionName,\n            final Map<String, Serializable> parameters) throws SExpressionEvaluationException {\n        for (SExpression dependency : dependencies) {\n            if (\"startIndex\".equals(dependency.getName())) {\n                parameters.remove(dependency.getName());\n                return (Integer) resolvedExpressions.get(dependency.getDiscriminant());\n            }\n        }\n        throw new SExpressionEvaluationException(\n                \"Pagination parameter 'startIndex' is mandatory when calling 'find*()' methods returning a List of Business Data\",\n                expressionName);\n    }\n\n    private int getMaxResultParameter(final List<SExpression> dependencies,\n            final Map<Integer, Object> resolvedExpressions, final String expressionName,\n            final Map<String, Serializable> parameters) throws SExpressionEvaluationException {\n        for (SExpression dependency : dependencies) {\n            if (\"maxResults\".equals(dependency.getName())) {\n                parameters.remove(dependency.getName());\n                return (Integer) resolvedExpressions.get(dependency.getDiscriminant());\n            }\n        }\n        throw new SExpressionEvaluationException(\n                \"Pagination parameter 'maxResults' is mandatory when calling 'find*()' methods returning a List of Business Data\",\n                expressionName);\n    }\n\n    private boolean isNumber(final String returnType) {\n        return Long.class.getName().equals(returnType) || Integer.class.getName().equals(returnType)\n                || Double.class.getName().equals(returnType)\n                || Float.class.getName().equals(returnType);\n    }\n\n    @Override\n    public ExpressionKind getExpressionKind() {\n        return KIND_QUERY_BUSINESS_DATA;\n    }\n\n    @Override\n    public List<Object> evaluate(final List<SExpression> expressions, final Map<String, Object> context,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState) throws SExpressionEvaluationException {\n        final List<Object> list = new ArrayList<>(expressions.size());\n        for (final SExpression expression : expressions) {\n            list.add(evaluate(expression, context, resolvedExpressions, containerState));\n        }\n        return list;\n    }\n\n    @Override\n    public boolean mustPutEvaluatedExpressionInContext() {\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/handler/SchedulerServiceRestartHandler.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.handler;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.commons.PlatformRestartHandler;\nimport org.bonitasoft.engine.scheduler.SchedulerService;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\n\n/**\n * @author Matthieu Chaffotte\n */\n@Slf4j\npublic class SchedulerServiceRestartHandler implements PlatformRestartHandler {\n\n    private final SchedulerService schedulerService;\n    private UserTransactionService userTransactionService;\n\n    public SchedulerServiceRestartHandler(SchedulerService schedulerService,\n            UserTransactionService userTransactionService) {\n        super();\n        this.schedulerService = schedulerService;\n        this.userTransactionService = userTransactionService;\n    }\n\n    @Override\n    public void execute() {\n        log.info(\"Rescheduling all scheduler Triggers in ERROR state\");\n        try {\n            userTransactionService.executeInTransaction(() -> {\n                schedulerService.rescheduleErroneousTriggers();\n                return null;\n            });\n        } catch (Exception e) {\n            log.warn(\n                    \"Unable to reschedule all erroneous triggers, call PlatformAPI.rescheduleErroneousTriggers to retry. Cause is {}\",\n                    e.getMessage());\n            log.debug(\"Cause: \", e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/identity/CustomUserInfoDefinitionImporter.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.xml.ExportedCustomUserInfoDefinition;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class CustomUserInfoDefinitionImporter {\n\n    private final IdentityService identityService;\n    private final ImportOrganizationStrategy strategy;\n\n    public CustomUserInfoDefinitionImporter(IdentityService identityService,\n            final ImportOrganizationStrategy strategy) {\n        this.strategy = strategy;\n        this.identityService = identityService;\n    }\n\n    public Map<String, SCustomUserInfoDefinition> importCustomUserInfoDefinitions(\n            List<ExportedCustomUserInfoDefinition> userInfoDefinitionCreators)\n            throws SIdentityException, ImportDuplicateInOrganizationException {\n        Map<String, SCustomUserInfoDefinition> nameToDefinition = new HashMap<>(userInfoDefinitionCreators.size());\n        for (ExportedCustomUserInfoDefinition creator : userInfoDefinitionCreators) {\n            String name = creator.getName();\n            SCustomUserInfoDefinition userInfoDef;\n            if (identityService.hasCustomUserInfoDefinition(name)) {\n                userInfoDef = identityService.getCustomUserInfoDefinitionByName(name);\n                strategy.foundExistingCustomUserInfoDefinition(userInfoDef, creator);\n            } else {\n                userInfoDef = addCustomUserInfoDefinition(creator);\n            }\n            nameToDefinition.put(name, userInfoDef);\n        }\n        return nameToDefinition;\n    }\n\n    private SCustomUserInfoDefinition addCustomUserInfoDefinition(ExportedCustomUserInfoDefinition creator)\n            throws SCustomUserInfoDefinitionAlreadyExistsException, SCustomUserInfoDefinitionCreationException {\n        final SCustomUserInfoDefinition.SCustomUserInfoDefinitionBuilder userInfoBuilder = SCustomUserInfoDefinition\n                .builder();\n        userInfoBuilder.name(creator.getName());\n        userInfoBuilder.description(creator.getDescription());\n        return identityService.createCustomUserInfoDefinition(userInfoBuilder.build());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/identity/CustomUserInfoValueImporter.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.impl.SCustomUserInfoValueAPI;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.xml.ExportedCustomUserInfoValue;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class CustomUserInfoValueImporter {\n\n    private final SCustomUserInfoValueAPI userInfoAPI;\n    private final Map<String, SCustomUserInfoDefinition> customUserInfoDefinitions;\n\n    public CustomUserInfoValueImporter(SCustomUserInfoValueAPI userInfoAPI,\n            Map<String, SCustomUserInfoDefinition> customUserInfoDefinitions) {\n        this.userInfoAPI = userInfoAPI;\n        this.customUserInfoDefinitions = customUserInfoDefinitions;\n    }\n\n    public void imporCustomUserInfoValues(List<ExportedCustomUserInfoValue> customUserInfoValues, long persistedUserId)\n            throws SBonitaException {\n        for (ExportedCustomUserInfoValue infoValue : customUserInfoValues) {\n            SCustomUserInfoDefinition infoDefinition = customUserInfoDefinitions.get(infoValue.getName());\n            if (infoDefinition == null) {\n                String message = \"The XML file is inconsistent. A custom user info value is refenced with name '\"\n                        + infoValue.getName()\n                        + \"', but no custom user info definition is defined with this name.\";\n                throw new SImportOrganizationException(message);\n            }\n            userInfoAPI.set(infoDefinition.getId(), persistedUserId, infoValue.getValue());\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/identity/ExportOrganization.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.xml.bind.JAXBException;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.identity.model.SContactInfo;\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoValue;\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.identity.model.SRole;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.identity.model.SUserMembership;\nimport org.bonitasoft.engine.identity.xml.ExportedCustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.xml.ExportedCustomUserInfoValue;\nimport org.bonitasoft.engine.identity.xml.ExportedGroup;\nimport org.bonitasoft.engine.identity.xml.ExportedRole;\nimport org.bonitasoft.engine.identity.xml.ExportedUser;\nimport org.bonitasoft.engine.identity.xml.ExportedUserMembership;\nimport org.bonitasoft.engine.identity.xml.Organization;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n */\npublic class ExportOrganization implements TransactionContentWithResult<String> {\n\n    private final IdentityService identityService;\n\n    private Map<Long, String> userNames;\n\n    private String xmlOrganization;\n\n    private final int maxResults;\n\n    public ExportOrganization(final IdentityService identityService, final int maxResults) {\n        this.identityService = identityService;\n        this.maxResults = maxResults;\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        userNames = new HashMap<>(20);\n        final List<SCustomUserInfoDefinition> customUserInfoDefinitions = getAllCustomUserInfoDefinitions();\n        final List<ExportedUser> users = getAllUsers(getUserInfoDefinitionNames(customUserInfoDefinitions));\n\n        // improvement: user server object to avoid useless conversion;\n        final List<ExportedRole> roles = getAllRoles();\n\n        final List<SGroup> groups = getAllGroups();\n        final Map<Long, String> groupIdParentPath = new HashMap<>(groups.size());\n        for (final SGroup group : groups) {\n            groupIdParentPath.put(group.getId(), group.getParentPath());\n        }\n        final List<SUserMembership> userMemberships = getAllUserMemberships();\n\n        // improvement: user server object to avoid useless conversion;\n        final List<ExportedGroup> clientGroups = ModelConvertor.toExportedGroups(groups);\n\n        // improvement: user server object to avoid useless conversion;\n        final List<ExportedUserMembership> clientUserMemberships = ModelConvertor\n                .toExportedUserMembership(userMemberships, userNames, groupIdParentPath);\n        List<ExportedCustomUserInfoDefinition> customUserInfoDefinitionCreators = ModelConvertor\n                .toExportedCustomUserInfoDefinition(customUserInfoDefinitions);\n        final Organization organization = new Organization(users, roles, clientGroups, clientUserMemberships,\n                customUserInfoDefinitionCreators);\n        try {\n            xmlOrganization = new OrganizationParser().convert(organization);\n        } catch (JAXBException e) {\n            throw new SBonitaReadException(e);\n        }\n    }\n\n    private static Map<Long, String> getUserInfoDefinitionNames(\n            final List<SCustomUserInfoDefinition> userInfoDefinitions) {\n        final Map<Long, String> names = new HashMap<>(userInfoDefinitions.size());\n        for (final SCustomUserInfoDefinition userInfoDefinition : userInfoDefinitions) {\n            names.put(userInfoDefinition.getId(), userInfoDefinition.getName());\n        }\n        return names;\n    }\n\n    @Override\n    public String getResult() {\n        return xmlOrganization;\n    }\n\n    protected List<SCustomUserInfoDefinition> getAllCustomUserInfoDefinitions() throws SIdentityException {\n        final List<SCustomUserInfoDefinition> allCustomUserInfoDefinitions = new ArrayList<>(5);\n        List<SCustomUserInfoDefinition> currentPage = null;\n        int startIndex = 0;\n        do {\n            currentPage = identityService.getCustomUserInfoDefinitions(startIndex, maxResults);\n            allCustomUserInfoDefinitions.addAll(currentPage);\n            startIndex += maxResults;\n        } while (currentPage.size() == maxResults);\n        return allCustomUserInfoDefinitions;\n    }\n\n    List<SUserMembership> getAllUserMemberships() throws SIdentityException {\n        final long numberOfUserMemberships = identityService.getNumberOfUserMemberships();\n        final List<SUserMembership> sUserMemberships = new ArrayList<>();\n        for (int startIndex = 0; startIndex < numberOfUserMemberships; startIndex = startIndex + maxResults) {\n            OrderByOption orderByOption = new OrderByOption(SUserMembership.class, SUserMembership.ID, OrderByType.ASC);\n            sUserMemberships.addAll(identityService.getUserMemberships(startIndex, maxResults, orderByOption));\n        }\n        return sUserMemberships;\n    }\n\n    List<SGroup> getAllGroups() throws SIdentityException {\n        List<SGroup> groups;\n        final long groupNumber = identityService.getNumberOfGroups();\n        groups = new ArrayList<>();\n        for (int startIndex = 0; startIndex < groupNumber; startIndex = startIndex + maxResults) {\n            groups.addAll(identityService.getGroups(startIndex, maxResults, \"id\", OrderByType.ASC));\n        }\n        return groups;\n    }\n\n    List<ExportedRole> getAllRoles() throws SIdentityException {\n        final long roleNumber = identityService.getNumberOfRoles();\n        final List<ExportedRole> roles = new ArrayList<>();\n        for (int startIndex = 0; startIndex < roleNumber; startIndex = startIndex + maxResults) {\n            final List<SRole> sRoles = identityService.getRoles(startIndex, maxResults, \"id\", OrderByType.ASC);\n            roles.addAll(ModelConvertor.toExportedRoles(sRoles));\n        }\n        return roles;\n    }\n\n    private List<ExportedUser> getAllUsers(final Map<Long, String> userInfoDefinitionNames) throws SBonitaException {\n        final long userNumber = identityService.getNumberOfUsers();\n        final List<ExportedUser> users = new ArrayList<>();\n        for (int startIndex = 0; startIndex <= userNumber; startIndex = startIndex + maxResults) {\n            users.addAll(getNextUsersPage(startIndex, maxResults, userInfoDefinitionNames));\n        }\n        return users;\n    }\n\n    List<ExportedUser> getNextUsersPage(final int startIndex, final int numberPerPage,\n            final Map<Long, String> userInfoDefinitionNames)\n            throws SBonitaException {\n        final List<ExportedUser> currentUsersPage = new ArrayList<>(numberPerPage);\n        final List<SUser> sUsers = identityService.getUsers(startIndex, numberPerPage, \"id\", OrderByType.ASC);\n        for (final SUser sUser : sUsers) {\n            userNames.put(sUser.getId(), sUser.getUserName());\n            currentUsersPage.add(toExportedUser(sUser, userInfoDefinitionNames));\n        }\n        return currentUsersPage;\n    }\n\n    protected ExportedUser toExportedUser(final SUser sUser, final Map<Long, String> userInfoDefinitionNames)\n            throws SBonitaException {\n        final String managerUserName = getManagerUsername(sUser);\n\n        final ExportedUserBuilder clientUserbuilder = ExportedUserBuilderFactory.createNewInstance(sUser.getUserName(),\n                sUser.getPassword());\n        // Do not export dates and id\n        clientUserbuilder.setPasswordEncrypted(true);\n        clientUserbuilder.setFirstName(sUser.getFirstName());\n        clientUserbuilder.setLastName(sUser.getLastName());\n        clientUserbuilder.setTitle(sUser.getTitle());\n        clientUserbuilder.setJobTitle(sUser.getJobTitle());\n        clientUserbuilder.setEnabled(sUser.isEnabled());\n\n        setManagerInfo(managerUserName, clientUserbuilder);\n        setPersonalContactInfo(sUser, clientUserbuilder);\n        setProfessionalContactInfo(sUser, clientUserbuilder);\n        addCustomUserInfoValues(sUser.getId(), clientUserbuilder, userInfoDefinitionNames);\n        return clientUserbuilder.done();\n    }\n\n    protected void addCustomUserInfoValues(final long userId, final ExportedUserBuilder clientUserbuilder,\n            final Map<Long, String> userInfoDefinitionNames)\n            throws SBonitaReadException {\n        final List<SCustomUserInfoValue> userInfoValues = getAllCustomUserInfoForUser(userId);\n        for (final SCustomUserInfoValue infoValue : userInfoValues) {\n            final String definitionName = userInfoDefinitionNames.get(infoValue.getDefinitionId());\n            clientUserbuilder\n                    .addCustomUserInfoValue(new ExportedCustomUserInfoValue(definitionName, infoValue.getValue()));\n        }\n    }\n\n    protected List<SCustomUserInfoValue> getAllCustomUserInfoForUser(final long userId) throws SBonitaReadException {\n        final List<SCustomUserInfoValue> allValues = new ArrayList<>(5);\n        int fromIndex = 0;\n        List<SCustomUserInfoValue> currentPage;\n        do {\n            final QueryOptions options = getQueryOptions(userId, fromIndex);\n            currentPage = identityService.searchCustomUserInfoValue(options);\n            allValues.addAll(currentPage);\n            fromIndex += maxResults;\n        } while (currentPage.size() == maxResults);\n        return allValues;\n    }\n\n    private QueryOptions getQueryOptions(final long userId, final int fromIndex) {\n        final OrderByOption orderByOption = new OrderByOption(SCustomUserInfoValue.class, SCustomUserInfoValue.ID,\n                OrderByType.ASC);\n        final FilterOption filterOption = new FilterOption(SCustomUserInfoValue.class, SCustomUserInfoValue.USER_ID,\n                userId);\n        return new QueryOptions(fromIndex, maxResults, Collections.singletonList(orderByOption),\n                Collections.singletonList(filterOption), null);\n    }\n\n    private void setPersonalContactInfo(final SUser sUser, final ExportedUserBuilder clientUserbuilder)\n            throws SIdentityException {\n        final SContactInfo persoInfo = identityService.getUserContactInfo(sUser.getId(), true);\n        if (persoInfo != null) {\n            clientUserbuilder.setPersonalData(ModelConvertor.toUserContactData(persoInfo));\n        }\n    }\n\n    private void setProfessionalContactInfo(final SUser sUser, final ExportedUserBuilder clientUserbuilder)\n            throws SIdentityException {\n        final SContactInfo proInfo = identityService.getUserContactInfo(sUser.getId(), false);\n        if (proInfo != null) {\n            clientUserbuilder.setProfessionalData(ModelConvertor.toUserContactData(proInfo));\n        }\n    }\n\n    private void setManagerInfo(final String managerUserName, final ExportedUserBuilder clientUserbuilder) {\n        clientUserbuilder.setManagerUserName(managerUserName);\n    }\n\n    private String getManagerUsername(final SUser sUser) throws SUserNotFoundException {\n        final long managerUserId = sUser.getManagerUserId();\n        String managerUserName = null;\n        if (managerUserId > 0) {\n            managerUserName = userNames.get(managerUserId);\n            if (managerUserName == null) {\n                final SUser manager = identityService.getUser(sUser.getManagerUserId());\n                userNames.put(manager.getId(), manager.getUserName());\n                managerUserName = manager.getUserName();\n            }\n        }\n        return managerUserName;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/identity/ImportDuplicateInOrganizationException.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Emmanuel Duchastenier\n * @author Matthieu Chaffotte\n */\npublic class ImportDuplicateInOrganizationException extends SBonitaException {\n\n    private static final long serialVersionUID = -2325217966573197811L;\n\n    public ImportDuplicateInOrganizationException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/identity/ImportOrganization.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.xml.bind.JAXBException;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.StringUtils;\nimport org.bonitasoft.engine.api.impl.SCustomUserInfoValueAPI;\nimport org.bonitasoft.engine.api.impl.SessionInfos;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.identity.model.SRole;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.identity.model.SUserMembership;\nimport org.bonitasoft.engine.identity.model.builder.SUserUpdateBuilderFactory;\nimport org.bonitasoft.engine.identity.xml.ExportedCustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.xml.ExportedGroup;\nimport org.bonitasoft.engine.identity.xml.ExportedRole;\nimport org.bonitasoft.engine.identity.xml.ExportedUser;\nimport org.bonitasoft.engine.identity.xml.ExportedUserMembership;\nimport org.bonitasoft.engine.identity.xml.Organization;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Matthieu Chaffotte\n */\n@Slf4j\npublic class ImportOrganization {\n\n    private static final String LEGACY_NS = \"xmlns:organization=\\\"http://documentation.bonitasoft.com/organization-xml-schema\\\"\";\n\n    private static final String VERSIONED_NS = \"xmlns:organization=\\\"http://documentation.bonitasoft.com/organization-xml-schema/1.1\\\"\";\n\n    final IdentityService identityService;\n    private final String organizationContent;\n\n    private final List<String> warnings;\n\n    private final ImportOrganizationStrategy strategy;\n\n    private final SCustomUserInfoValueAPI userInfoValueAPI;\n\n    public ImportOrganization(IdentityService identityService, final String organizationContent,\n            final ImportPolicy policy, final SCustomUserInfoValueAPI userInfoValueAPI)\n            throws OrganizationImportException {\n        this.userInfoValueAPI = userInfoValueAPI;\n        this.identityService = identityService;\n        this.organizationContent = updateNamespace(organizationContent);\n        warnings = new ArrayList<>();\n        switch (policy) {\n            case FAIL_ON_DUPLICATES:\n                strategy = new ImportOrganizationFailOnDuplicatesStrategy();\n                break;\n            case IGNORE_DUPLICATES:\n                strategy = new ImportOrganizationIgnoreDuplicatesStrategy();\n                break;\n            case MERGE_DUPLICATES:\n                strategy = new ImportOrganizationMergeDuplicatesStrategy(identityService, userInfoValueAPI);\n                break;\n            default:\n                throw new OrganizationImportException(\"No import strategy found for \" + policy);\n        }\n\n    }\n\n    private void excludeGroupsWithInvalidCharactersInName(List<ExportedGroup> groups)\n            throws OrganizationImportException {\n        for (ExportedGroup group : groups) {\n            String groupName = group.getName();\n            if (groupName.contains(\"/\")) {\n                groups.remove(group);\n                warnings.add(\"The group name \" + groupName\n                        + \" contains the character '/' which is not supported. The group has not been imported\");\n            }\n        }\n    }\n\n    private String updateNamespace(String content) {\n        if (content != null) {\n            if (content.contains(LEGACY_NS)) {\n                content = content.replace(LEGACY_NS, VERSIONED_NS);\n            }\n        }\n        return content;\n    }\n\n    public List<String> execute() throws SBonitaException, JAXBException {\n        try {\n            final Organization organization = new OrganizationParser().convert(organizationContent);\n            final List<ExportedUser> users = organization.getUsers();\n            final List<ExportedRole> roles = organization.getRoles();\n            final List<ExportedGroup> groups = organization.getGroups();\n            //checking if the Group names contain illegal characters\n            excludeGroupsWithInvalidCharactersInName(groups);\n            final List<ExportedUserMembership> memberships = organization.getMemberships();\n\n            long importDate = System.currentTimeMillis();\n            // custom user info definitions\n            final Map<String, SCustomUserInfoDefinition> customUserInfoDefinitions = importCustomUserInfoDefinitions(\n                    organization.getCustomUserInfoDefinition());\n            // Users\n            final Map<String, SUser> userNameToSUsers = importUsers(users, customUserInfoDefinitions);\n            updateManagerId(users, userNameToSUsers, importDate);\n            // Roles\n            final Map<String, Long> roleNameToIdMap = importRoles(roles);\n            // Groups\n            final Map<String, Long> groupPathToIdMap = importGroups(groups);\n            // UserMemberships\n            importMemberships(memberships, userNameToSUsers, roleNameToIdMap, groupPathToIdMap);\n            return warnings;\n        } catch (final SBonitaException | JAXBException e) {\n            throw e;\n        } catch (final Exception e) {\n            throw new SImportOrganizationException(e);\n        }\n    }\n\n    private Map<String, SCustomUserInfoDefinition> importCustomUserInfoDefinitions(\n            final List<ExportedCustomUserInfoDefinition> customUserInfoDefinitionCreators)\n            throws SIdentityException, ImportDuplicateInOrganizationException {\n        final CustomUserInfoDefinitionImporter importer = new CustomUserInfoDefinitionImporter(identityService,\n                strategy);\n        return importer.importCustomUserInfoDefinitions(customUserInfoDefinitionCreators);\n    }\n\n    private void importMemberships(final List<ExportedUserMembership> memberships,\n            final Map<String, SUser> userNameToSUsers,\n            final Map<String, Long> roleNameToIdMap,\n            final Map<String, Long> groupPathToIdMap)\n            throws SIdentityException, ImportDuplicateInOrganizationException {\n        for (final ExportedUserMembership newMembership : memberships) {\n            final Long userId = getUserId(userNameToSUsers, newMembership);\n            final Long groupId = getGroupId(groupPathToIdMap, newMembership);\n            final Long roleId = getRoleId(roleNameToIdMap, newMembership);\n            if (userId != null && groupId != null && roleId != null) {\n                try {\n                    final SUserMembership sUserMembership = identityService.getUserMembership(userId, groupId, roleId);\n                    strategy.foundExistingMembership(sUserMembership);\n                } catch (final SIdentityException e) {\n                    final Long assignedBy = getAssignedBy(userNameToSUsers, newMembership);\n                    addMembership(newMembership, userId, groupId, roleId, assignedBy);\n                }\n            } else {\n                log.warn(\"The membership \" + newMembership\n                        + \" could not be imported because the user, group or role can't be found\\n userId=\" +\n                        userId + \" groupId=\" + groupId + \" roleId=\" + roleId);\n            }\n        }\n    }\n\n    private Long getUserId(final Map<String, SUser> userNameToSUsers, final ExportedUserMembership newMembership) {\n        final String username = newMembership.getUserName();\n        if (username == null || username.isEmpty()) {\n            return -1L;\n        }\n        final SUser sUser = userNameToSUsers.get(username);\n        if (sUser != null) {\n            return sUser.getId();\n        }\n        return null;\n\n    }\n\n    private Long getGroupId(final Map<String, Long> groupPathToIdMap, final ExportedUserMembership newMembership) {\n        final String groupParentPath = newMembership.getGroupParentPath();\n        final String groupFullPath = (groupParentPath == null ? '/' : groupParentPath + '/')\n                + newMembership.getGroupName();\n        return groupPathToIdMap.get(groupFullPath);\n    }\n\n    private Long getRoleId(final Map<String, Long> roleNameToIdMap, final ExportedUserMembership newMembership) {\n        final String roleName = newMembership.getRoleName();\n        if (roleName == null || roleName.isEmpty()) {\n            return -1L;\n        }\n        final Long roleId = roleNameToIdMap.get(roleName);\n        if (roleId != null) {\n            return roleId;\n        }\n        return null;\n    }\n\n    private Long getAssignedBy(final Map<String, SUser> userNameToSUsers, final ExportedUserMembership newMembership) {\n        final String assignedByName = newMembership.getAssignedBy();\n        if (assignedByName == null || assignedByName.isEmpty()) {\n            return -1L;\n        }\n        final SUser sUserAssigned = userNameToSUsers.get(assignedByName);\n        if (sUserAssigned != null) {\n            return sUserAssigned.getId();\n        }\n        return -1L;\n    }\n\n    private Map<String, Long> importGroups(final List<ExportedGroup> groupCreators)\n            throws ImportDuplicateInOrganizationException, SIdentityException {\n        final Map<String, Long> groupPathToIdMap = new HashMap<>(groupCreators.size());\n        for (final ExportedGroup groupCreator : groupCreators) {\n            SGroup sGroup;\n            try {\n                final String groupPath = getGroupPath(groupCreator);\n                sGroup = identityService.getGroupByPath(groupPath);\n                strategy.foundExistingGroup(sGroup, groupCreator);\n            } catch (final SGroupNotFoundException e) {\n                sGroup = addGroup(groupCreator);\n            }\n            groupPathToIdMap.put(sGroup.getPath(), sGroup.getId());\n        }\n        return groupPathToIdMap;\n    }\n\n    private String getGroupPath(final ExportedGroup exportedGroup) {\n        final String name = exportedGroup.getName();\n        final String parentPath = exportedGroup.getParentPath();\n        if (parentPath == null) {\n            return \"/\" + name;\n        }\n        return parentPath + \"/\" + name;\n    }\n\n    private Map<String, Long> importRoles(final List<ExportedRole> roleCreators)\n            throws ImportDuplicateInOrganizationException, SIdentityException {\n        final Map<String, Long> roleNameToIdMap = new HashMap<>(roleCreators.size());\n        for (final ExportedRole roleCreator : roleCreators) {\n            SRole sRole;\n            try {\n                sRole = identityService.getRoleByName(roleCreator.getName());\n                strategy.foundExistingRole(sRole, roleCreator);\n            } catch (final SRoleNotFoundException e) {\n                sRole = addRole(roleCreator);\n            }\n            roleNameToIdMap.put(sRole.getName(), sRole.getId());\n        }\n        return roleNameToIdMap;\n    }\n\n    private void updateManagerId(final List<ExportedUser> users, final Map<String, SUser> userNameToSUsers,\n            long importDate)\n            throws SUserUpdateException {\n        for (final ExportedUser user : users) {\n            //Skip the update of the manager if user was already present before the import\n            // and the strategy says to skip it in that case\n            if (strategy.shouldSkipUpdateManagerOfExistingUser()\n                    && wasUserAlreadyPresent(userNameToSUsers, importDate, user)) {\n                continue;\n            }\n\n            if (StringUtils.isNotBlank(user.getManagerUserName())) {\n                final String managerUserName = user.getManagerUserName().trim();\n                SUser manager = userNameToSUsers.get(managerUserName);\n                if (manager != null) {\n                    identityService.updateUser(userNameToSUsers.get(user.getUserName()),\n                            BuilderFactory.get(SUserUpdateBuilderFactory.class).createNewInstance()\n                                    .updateManagerUserId(manager.getId()).done());\n                } else {\n                    log.warn(\"The user \" + user.getUserName() + \" has a manager with username \"\n                            + managerUserName + \", but this one does not exist. Please set it manually.\");\n                }\n            }\n        }\n    }\n\n    private boolean wasUserAlreadyPresent(Map<String, SUser> userNameToSUsers, long importDate, ExportedUser user) {\n        return userNameToSUsers.get(user.getUserName()).getCreationDate() < importDate;\n    }\n\n    private Map<String, SUser> importUsers(final List<ExportedUser> users,\n            final Map<String, SCustomUserInfoDefinition> customUserInfoDefinitions)\n            throws SBonitaException {\n        final CustomUserInfoValueImporter userInfoValueImporter = new CustomUserInfoValueImporter(userInfoValueAPI,\n                customUserInfoDefinitions);\n        final UserImporter userImporter = new UserImporter(identityService, strategy,\n                SessionInfos.getUserIdFromSession(), userInfoValueImporter);\n        return userImporter.importUsers(users);\n    }\n\n    private void addMembership(final ExportedUserMembership newMembership, final Long userId, final Long groupId,\n            final Long roleId, final Long assignedBy)\n            throws SUserMembershipCreationException {\n        final long assignedDateAsLong = getAssignedDate(newMembership);\n        final SUserMembership sUserMembership = SUserMembership.builder().userId(userId).groupId(groupId).roleId(roleId)\n                .assignedBy(assignedBy).assignedDate(assignedDateAsLong).build();\n        identityService.createUserMembership(sUserMembership);\n    }\n\n    private long getAssignedDate(final ExportedUserMembership newMembership) {\n        final Long assignedDate = newMembership.getAssignedDate();\n        if (assignedDate != null) {\n            return assignedDate;\n        }\n        return 0;\n    }\n\n    private SGroup addGroup(final ExportedGroup exportedGroup) throws SGroupCreationException {\n        final SGroup sGroup = ModelConvertor.constructSGroup(exportedGroup);\n        identityService.createGroup(sGroup, null, null);\n        return sGroup;\n    }\n\n    private SRole addRole(final ExportedRole exportedRole) throws SIdentityException {\n        final SRole sRole = ModelConvertor.constructSRole(exportedRole);\n        identityService.createRole(sRole, null, null);\n        return sRole;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/identity/ImportOrganizationFailOnDuplicatesStrategy.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.identity.model.SRole;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.identity.model.SUserMembership;\nimport org.bonitasoft.engine.identity.xml.ExportedCustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.xml.ExportedGroup;\nimport org.bonitasoft.engine.identity.xml.ExportedRole;\nimport org.bonitasoft.engine.identity.xml.ExportedUser;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic class ImportOrganizationFailOnDuplicatesStrategy implements ImportOrganizationStrategy {\n\n    public ImportOrganizationFailOnDuplicatesStrategy() {\n    }\n\n    @Override\n    public void foundExistingGroup(final SGroup existingGroup, final ExportedGroup newGroup)\n            throws ImportDuplicateInOrganizationException {\n        throw new ImportDuplicateInOrganizationException(\n                \"There's already a group with the path : \" + existingGroup.getPath());\n    }\n\n    @Override\n    public void foundExistingUser(final SUser existingUser, final ExportedUser user) throws SBonitaException {\n        throw new ImportDuplicateInOrganizationException(\n                \"There's already a user with the name : \" + existingUser.getUserName());\n    }\n\n    @Override\n    public void foundExistingRole(final SRole existingRole, final ExportedRole newRole)\n            throws ImportDuplicateInOrganizationException {\n        throw new ImportDuplicateInOrganizationException(\n                \"There's already a role with the name : \" + existingRole.getName());\n\n    }\n\n    @Override\n    public void foundExistingMembership(final SUserMembership existingMembership)\n            throws ImportDuplicateInOrganizationException {\n        throw new ImportDuplicateInOrganizationException(\n                \"There's already a user membership with the name : \" + existingMembership.getUsername()\n                        + \", the role : \" + existingMembership.getRoleName() + \"and the group : \"\n                        + existingMembership.getGroupName());\n    }\n\n    @Override\n    public void foundExistingCustomUserInfoDefinition(SCustomUserInfoDefinition existingUserInfoDefinition,\n            ExportedCustomUserInfoDefinition newUserInfoDefinition) throws ImportDuplicateInOrganizationException {\n        throw new ImportDuplicateInOrganizationException(\n                \"There's already a custom user info definition with the name : '\" + newUserInfoDefinition.getName()\n                        + \"'\");\n    }\n\n    @Override\n    public boolean shouldSkipUpdateManagerOfExistingUser() {\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/identity/ImportOrganizationIgnoreDuplicatesStrategy.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.identity.model.SRole;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.identity.model.SUserMembership;\nimport org.bonitasoft.engine.identity.xml.ExportedCustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.xml.ExportedGroup;\nimport org.bonitasoft.engine.identity.xml.ExportedRole;\nimport org.bonitasoft.engine.identity.xml.ExportedUser;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic class ImportOrganizationIgnoreDuplicatesStrategy implements ImportOrganizationStrategy {\n\n    public ImportOrganizationIgnoreDuplicatesStrategy() {\n    }\n\n    @Override\n    public void foundExistingGroup(final SGroup existingGroup, final ExportedGroup newGroup) {\n    }\n\n    @Override\n    public void foundExistingUser(final SUser existingUser, final ExportedUser user) {\n    }\n\n    @Override\n    public void foundExistingRole(final SRole existingRole, final ExportedRole newRole) {\n    }\n\n    @Override\n    public void foundExistingMembership(final SUserMembership existingMembership) {\n    }\n\n    @Override\n    public void foundExistingCustomUserInfoDefinition(final SCustomUserInfoDefinition existingUserInfoDefinition,\n            final ExportedCustomUserInfoDefinition newUserInfoDefinition) {\n    }\n\n    @Override\n    public boolean shouldSkipUpdateManagerOfExistingUser() {\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/identity/ImportOrganizationMergeDuplicatesStrategy.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport org.bonitasoft.engine.api.impl.SCustomUserInfoValueAPI;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.identity.model.SContactInfo;\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.identity.model.SRole;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.identity.model.SUserMembership;\nimport org.bonitasoft.engine.identity.model.builder.SContactInfoUpdateBuilder;\nimport org.bonitasoft.engine.identity.model.builder.SContactInfoUpdateBuilderFactory;\nimport org.bonitasoft.engine.identity.model.builder.SCustomUserInfoDefinitionUpdateBuilder;\nimport org.bonitasoft.engine.identity.model.builder.SCustomUserInfoDefinitionUpdateBuilderFactory;\nimport org.bonitasoft.engine.identity.model.builder.SGroupUpdateBuilder;\nimport org.bonitasoft.engine.identity.model.builder.SGroupUpdateBuilderFactory;\nimport org.bonitasoft.engine.identity.model.builder.SRoleUpdateBuilder;\nimport org.bonitasoft.engine.identity.model.builder.SRoleUpdateBuilderFactory;\nimport org.bonitasoft.engine.identity.model.builder.SUserUpdateBuilder;\nimport org.bonitasoft.engine.identity.model.builder.SUserUpdateBuilderFactory;\nimport org.bonitasoft.engine.identity.xml.ExportedCustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.xml.ExportedCustomUserInfoValue;\nimport org.bonitasoft.engine.identity.xml.ExportedGroup;\nimport org.bonitasoft.engine.identity.xml.ExportedRole;\nimport org.bonitasoft.engine.identity.xml.ExportedUser;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic class ImportOrganizationMergeDuplicatesStrategy implements ImportOrganizationStrategy {\n\n    private final IdentityService identityService;\n\n    private final SCustomUserInfoValueAPI userInfoValueAPI;\n\n    public ImportOrganizationMergeDuplicatesStrategy(final IdentityService identityService,\n            final SCustomUserInfoValueAPI userInfoValueAPI) {\n        this.identityService = identityService;\n        this.userInfoValueAPI = userInfoValueAPI;\n    }\n\n    @Override\n    public void foundExistingGroup(final SGroup existingGroup, final ExportedGroup newGroup) throws SIdentityException {\n        final EntityUpdateDescriptor descriptor = getGroupDescriptor(existingGroup, newGroup);\n        if (!descriptor.getFields().isEmpty()) {\n            identityService.updateGroup(existingGroup, descriptor, null);\n        }\n    }\n\n    @Override\n    public void foundExistingUser(final SUser existingUser, final ExportedUser userToImport) throws SBonitaException {\n        final long existingUserId = existingUser.getId();\n        final EntityUpdateDescriptor descriptor = getUserDescriptor(userToImport);\n        identityService.updateUser(existingUser, descriptor, userToImport.isPasswordEncrypted());\n\n        createOrUpdatePersonalContactInfo(userToImport, existingUserId);\n        createOrUpdateProfessionalContactInfo(userToImport, existingUserId);\n        updateCustomUserInfoValues(userToImport, existingUserId);\n    }\n\n    private void updateCustomUserInfoValues(final ExportedUser userToImport, final long existingUserId)\n            throws SBonitaException {\n        for (final ExportedCustomUserInfoValue infoValue : userToImport.getCustomUserInfoValues()) {\n            final SCustomUserInfoDefinition customUserInfoDefinition = identityService\n                    .getCustomUserInfoDefinitionByName(infoValue.getName());\n            userInfoValueAPI.set(customUserInfoDefinition.getId(), existingUserId, infoValue.getValue());\n        }\n    }\n\n    private void createOrUpdateProfessionalContactInfo(final ExportedUser user, final long userId)\n            throws SIdentityException, SUserCreationException {\n        SContactInfo professContactInfo = identityService.getUserContactInfo(userId, false);\n        if (professContactInfo == null) {\n            professContactInfo = SContactInfo.builder().userId(userId).personal(false).build();\n            identityService.createUserContactInfo(professContactInfo);\n        }\n        final EntityUpdateDescriptor professionalDataDesc = getUserContactInfoDescriptor(user, false);\n        identityService.updateUserContactInfo(professContactInfo, professionalDataDesc);\n    }\n\n    private void createOrUpdatePersonalContactInfo(final ExportedUser user, final long userId)\n            throws SIdentityException, SUserCreationException {\n        SContactInfo persoContactInfo = identityService.getUserContactInfo(userId, true);\n        if (persoContactInfo == null) {\n            persoContactInfo = SContactInfo.builder().userId(userId).personal(true).build();\n            identityService.createUserContactInfo(persoContactInfo);\n        }\n        final EntityUpdateDescriptor personalDataDesc = getUserContactInfoDescriptor(user, true);\n        identityService.updateUserContactInfo(persoContactInfo, personalDataDesc);\n    }\n\n    @Override\n    public void foundExistingRole(final SRole existingRole, final ExportedRole newRole) throws SIdentityException {\n        final EntityUpdateDescriptor descriptor = getRoleDescriptor(existingRole, newRole);\n        if (!descriptor.getFields().isEmpty()) {\n            identityService.updateRole(existingRole, descriptor, null);\n        }\n    }\n\n    @Override\n    public void foundExistingMembership(final SUserMembership existingMembership) {\n\n    }\n\n    protected EntityUpdateDescriptor getRoleDescriptor(final SRole existingRole, final ExportedRole exportedRole) {\n        final SRoleUpdateBuilder roleUpdateBuilder = BuilderFactory.get(SRoleUpdateBuilderFactory.class)\n                .createNewInstance();\n        final String name = exportedRole.getName();\n        if (name != null && !name.equals(existingRole.getName())) {\n            roleUpdateBuilder.updateName(name);\n        }\n        final String description = exportedRole.getDescription();\n        if (description != null && !description.equals(existingRole.getDescription())) {\n            roleUpdateBuilder.updateDescription(description);\n        }\n        final String displayName = exportedRole.getDisplayName();\n        if (displayName != null && !displayName.equals(existingRole.getDisplayName())) {\n            roleUpdateBuilder.updateDisplayName(displayName);\n        }\n        roleUpdateBuilder.updateLastUpdate(System.currentTimeMillis());\n        return roleUpdateBuilder.done();\n    }\n\n    protected EntityUpdateDescriptor getGroupDescriptor(final SGroup existingGroup, final ExportedGroup exportedGroup) {\n        final SGroupUpdateBuilder groupUpdateBuilder = BuilderFactory.get(SGroupUpdateBuilderFactory.class)\n                .createNewInstance();\n        final String name = exportedGroup.getName();\n        if (name != null && !name.equals(existingGroup.getName())) {\n            groupUpdateBuilder.updateName(name);\n        }\n        final String parentPath = exportedGroup.getParentPath();\n        if (parentPath != null && !parentPath.equals(existingGroup.getParentPath())) {\n            groupUpdateBuilder.updateName(parentPath);\n        }\n        final String description = exportedGroup.getDescription();\n        if (description != null && !description.equals(existingGroup.getDescription())) {\n            groupUpdateBuilder.updateDescription(description);\n        }\n        final String displayName = exportedGroup.getDisplayName();\n        if (displayName != null && !displayName.equals(existingGroup.getDisplayName())) {\n            groupUpdateBuilder.updateDisplayName(displayName);\n        }\n        groupUpdateBuilder.updateLastUpdate(System.currentTimeMillis());\n        return groupUpdateBuilder.done();\n    }\n\n    protected EntityUpdateDescriptor getUserContactInfoDescriptor(final ExportedUser user, final boolean isPersonal) {\n        final SContactInfoUpdateBuilder updateBuilder = BuilderFactory.get(SContactInfoUpdateBuilderFactory.class)\n                .createNewInstance();\n        if (isPersonal) {\n            updateBuilder.updateAddress(user.getPersonalAddress());\n            updateBuilder.updateBuilding(user.getPersonalBuilding());\n            updateBuilder.updateCity(user.getPersonalCity());\n            updateBuilder.updateCountry(user.getPersonalCountry());\n            updateBuilder.updateEmail(user.getPersonalEmail());\n            updateBuilder.updateFaxNumber(user.getPersonalFaxNumber());\n            updateBuilder.updateMobileNumber(user.getPersonalMobileNumber());\n            updateBuilder.updatePhoneNumber(user.getPersonalPhoneNumber());\n            updateBuilder.updateRoom(user.getPersonalRoom());\n            updateBuilder.updateState(user.getPersonalState());\n            updateBuilder.updateWebsite(user.getPersonalWebsite());\n            updateBuilder.updateZipCode(user.getPersonalZipCode());\n        } else {\n            updateBuilder.updateAddress(user.getProfessionalAddress());\n            updateBuilder.updateBuilding(user.getProfessionalBuilding());\n            updateBuilder.updateCity(user.getProfessionalCity());\n            updateBuilder.updateCountry(user.getProfessionalCountry());\n            updateBuilder.updateEmail(user.getProfessionalEmail());\n            updateBuilder.updateFaxNumber(user.getProfessionalFaxNumber());\n            updateBuilder.updateMobileNumber(user.getProfessionalMobileNumber());\n            updateBuilder.updatePhoneNumber(user.getProfessionalPhoneNumber());\n            updateBuilder.updateRoom(user.getProfessionalRoom());\n            updateBuilder.updateState(user.getProfessionalState());\n            updateBuilder.updateWebsite(user.getProfessionalWebsite());\n            updateBuilder.updateZipCode(user.getProfessionalZipCode());\n        }\n        return updateBuilder.done();\n    }\n\n    protected EntityUpdateDescriptor getUserDescriptor(final ExportedUser user) {\n        final SUserUpdateBuilder userUpdateBuilder = BuilderFactory.get(SUserUpdateBuilderFactory.class)\n                .createNewInstance();\n        userUpdateBuilder.updateFirstName(user.getFirstName());\n        userUpdateBuilder.updateJobTitle(user.getJobTitle());\n        userUpdateBuilder.updateLastName(user.getLastName());\n        userUpdateBuilder.updatePassword(user.getPassword());\n        userUpdateBuilder.updateTitle(user.getTitle());\n        userUpdateBuilder.updateUserName(user.getUserName());\n        userUpdateBuilder.updateEnabled(user.isEnabled());\n        userUpdateBuilder.updateLastUpdate(System.currentTimeMillis());\n        return userUpdateBuilder.done();\n    }\n\n    @Override\n    public void foundExistingCustomUserInfoDefinition(final SCustomUserInfoDefinition existingUserInfoDefinition,\n            final ExportedCustomUserInfoDefinition newUserInfoDefinition) throws SIdentityException {\n        // only description is updated as it only matches if they have the same name\n        final EntityUpdateDescriptor updateDescriptor = getUpdateDescriptor(newUserInfoDefinition.getDescription());\n        identityService.updateCustomUserInfoDefinition(existingUserInfoDefinition, updateDescriptor);\n    }\n\n    @Override\n    public boolean shouldSkipUpdateManagerOfExistingUser() {\n        return false;\n    }\n\n    private EntityUpdateDescriptor getUpdateDescriptor(final String newDescription) {\n        final SCustomUserInfoDefinitionUpdateBuilder builder = BuilderFactory\n                .get(SCustomUserInfoDefinitionUpdateBuilderFactory.class).createNewInstance();\n        builder.updateDescription(newDescription);\n        return builder.done();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/identity/ImportOrganizationStrategy.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.identity.model.SRole;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.identity.model.SUserMembership;\nimport org.bonitasoft.engine.identity.xml.ExportedCustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.xml.ExportedGroup;\nimport org.bonitasoft.engine.identity.xml.ExportedRole;\nimport org.bonitasoft.engine.identity.xml.ExportedUser;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n */\npublic interface ImportOrganizationStrategy {\n\n    void foundExistingMembership(final SUserMembership existingMembership)\n            throws ImportDuplicateInOrganizationException;\n\n    void foundExistingRole(final SRole existingRole, final ExportedRole newRole)\n            throws ImportDuplicateInOrganizationException, SIdentityException;\n\n    void foundExistingUser(final SUser existingUser, final ExportedUser user) throws SBonitaException;\n\n    void foundExistingGroup(final SGroup existingGroup, final ExportedGroup newGroup)\n            throws ImportDuplicateInOrganizationException, SIdentityException;\n\n    void foundExistingCustomUserInfoDefinition(SCustomUserInfoDefinition existingUserInfoDefinition,\n            ExportedCustomUserInfoDefinition newUserInfoDefinition)\n            throws ImportDuplicateInOrganizationException, SIdentityException;\n\n    /**\n     * @return true if users that were already existing should have their manager set\n     */\n    boolean shouldSkipUpdateManagerOfExistingUser();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/identity/SImportOrganizationException.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SImportOrganizationException extends SBonitaException {\n\n    private static final long serialVersionUID = 3972373903843407422L;\n\n    public SImportOrganizationException(final String message) {\n        super(message);\n    }\n\n    public SImportOrganizationException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/identity/UserImporter.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.identity.model.SContactInfo;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.identity.xml.ExportedUser;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class UserImporter {\n\n    private final IdentityService identityService;\n\n    private final ImportOrganizationStrategy strategy;\n\n    private final long userIdFromSession;\n\n    private final CustomUserInfoValueImporter infoValueImporter;\n\n    public UserImporter(IdentityService identityService, final ImportOrganizationStrategy strategy,\n            long userIdFromSession,\n            CustomUserInfoValueImporter infoValueImporter) {\n        this.strategy = strategy;\n        this.userIdFromSession = userIdFromSession;\n        this.infoValueImporter = infoValueImporter;\n        this.identityService = identityService;\n    }\n\n    public Map<String, SUser> importUsers(final List<ExportedUser> usersToImport) throws SBonitaException {\n        final Map<String, SUser> userNameToSUsers = new HashMap<>(\n                (int) Math.min(Integer.MAX_VALUE, identityService.getNumberOfUsers()));\n        for (final ExportedUser userToImport : usersToImport) {\n            SUser sUser;\n            if (hasUserWithUserName(userToImport.getUserName())) {\n                sUser = identityService.getUserByUserName(userToImport.getUserName());\n                strategy.foundExistingUser(sUser, userToImport);\n            } else {\n                sUser = addAllUserInfo(userToImport);\n            }\n            userNameToSUsers.put(sUser.getUserName(), sUser);\n        }\n        return userNameToSUsers;\n    }\n\n    private boolean hasUserWithUserName(String userName) throws SBonitaReadException {\n        final FilterOption filter = new FilterOption(SUser.class, SUser.USER_NAME, userName);\n        final QueryOptions queryOptions = new QueryOptions(Collections.singletonList(filter), null);\n        final long numberOfUsers = identityService.getNumberOfUsers(queryOptions);\n        return numberOfUsers > 0;\n    }\n\n    private SUser addAllUserInfo(final ExportedUser userToImport) throws SBonitaException {\n        final SUser persistedUser = addUser(userToImport);\n        addContactInfo(userToImport, persistedUser);\n        infoValueImporter.imporCustomUserInfoValues(userToImport.getCustomUserInfoValues(), persistedUser.getId());\n        return persistedUser;\n    }\n\n    private void addContactInfo(final ExportedUser userToImport, SUser persistedUser) throws SUserCreationException {\n        final SContactInfo persoSContactInfo = ModelConvertor.constructSUserContactInfo(userToImport, true,\n                persistedUser.getId());\n        identityService.createUserContactInfo(persoSContactInfo);\n        final SContactInfo professSContactInfo = ModelConvertor.constructSUserContactInfo(userToImport, false,\n                persistedUser.getId());\n        identityService.createUserContactInfo(professSContactInfo);\n    }\n\n    private SUser addUser(final ExportedUser user) throws SUserCreationException {\n        SUser sUser;\n        if (user.isPasswordEncrypted()) {\n            sUser = identityService.createUserWithoutEncryptingPassword(constructSUser(user));\n        } else {\n            sUser = identityService.createUser(constructSUser(user));\n        }\n        return sUser;\n    }\n\n    private SUser constructSUser(final ExportedUser exportedUser) {\n        final SUser.SUserBuilder userBuilder = SUser.builder();\n        final long now = System.currentTimeMillis();\n        userBuilder.creationDate(now);\n        userBuilder.lastUpdate(now);\n\n        userBuilder.userName(exportedUser.getUserName());\n        userBuilder.password(exportedUser.getPassword());\n        userBuilder.firstName(exportedUser.getFirstName());\n        userBuilder.lastName(exportedUser.getLastName());\n        userBuilder.jobTitle(exportedUser.getJobTitle());\n        userBuilder.title(exportedUser.getTitle());\n        userBuilder.createdBy(userIdFromSession);\n        userBuilder.enabled(exportedUser.isEnabled());\n        return userBuilder.build();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/jobs/InternalJob.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.jobs;\n\nimport org.bonitasoft.engine.scheduler.StatelessJob;\nimport org.bonitasoft.engine.scheduler.exception.SJobConfigurationException;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.ServiceAccessorSingleton;\n\n/**\n * @author Baptiste Mesta\n */\npublic abstract class InternalJob implements StatelessJob {\n\n    private static final long serialVersionUID = 5627886991070497312L;\n\n    protected ServiceAccessor getServiceAccessor() throws SJobConfigurationException {\n        try {\n            return ServiceAccessorSingleton.getInstance();\n        } catch (final Exception e) {\n            throw new SJobConfigurationException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/jobs/TriggerTimerEventJob.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.jobs;\n\nimport java.io.Serializable;\nimport java.util.Map;\nimport java.util.Optional;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SRetryableException;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerType;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance;\nimport org.bonitasoft.engine.execution.event.EventsHandler;\nimport org.bonitasoft.engine.scheduler.exception.SJobConfigurationException;\nimport org.bonitasoft.engine.scheduler.exception.SJobExecutionException;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.tenant.TenantServicesManager;\n\n/**\n * @author Baptiste Mesta\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class TriggerTimerEventJob extends InternalJob {\n\n    private static final long serialVersionUID = 8727861254645155327L;\n\n    private transient EventsHandler eventsHandler;\n\n    private Long processDefinitionId;\n\n    private long targetSFlowNodeDefinitionId;\n\n    private Long flowNodeInstanceId;\n\n    private Long parentProcessInstanceId;\n\n    private Long rootProcessInstanceId;\n\n    private String containerType;\n\n    private String eventType;\n\n    private Boolean isInterrupting;\n\n    private Long subProcessId;\n\n    private transient EventInstanceService eventInstanceService;\n    private Long jobDescriptorId;\n    private TenantServicesManager tenantServicesManager;\n\n    @Override\n    public String getName() {\n        return null;\n    }\n\n    @Override\n    public String getDescription() {\n        return null;\n    }\n\n    @Override\n    public void execute() throws SJobExecutionException, SRetryableException {\n        try {\n            if (!tenantServicesManager.isStarted()) {\n                throw new SRetryableException(\"Unable to execute timer linked with the job  \" + jobDescriptorId\n                        + \" because the tenant is not started\");\n            }\n            if (subProcessId == null) {\n                eventsHandler.triggerCatchEvent(eventType, processDefinitionId, targetSFlowNodeDefinitionId,\n                        flowNodeInstanceId, containerType);\n            } else {\n                eventsHandler.triggerCatchEvent(SEventTriggerType.TIMER, processDefinitionId,\n                        targetSFlowNodeDefinitionId, containerType, subProcessId,\n                        parentProcessInstanceId, rootProcessInstanceId, isInterrupting);\n            }\n            if (flowNodeInstanceId != null) {\n                Optional<STimerEventTriggerInstance> triggerInstance = eventInstanceService\n                        .getTimerEventTriggerInstanceOfFlowNode(flowNodeInstanceId);\n                if (triggerInstance.isPresent()) {\n                    eventInstanceService.deleteEventTriggerInstance(triggerInstance.get());\n                }\n\n            }\n\n        } catch (final SBonitaException e) {\n            throw new SJobExecutionException(e);\n        }\n    }\n\n    @Override\n    public void setAttributes(final Map<String, Serializable> attributes) throws SJobConfigurationException {\n        processDefinitionId = (Long) attributes.get(\"processDefinitionId\");\n        targetSFlowNodeDefinitionId = (Long) attributes.get(\"targetSFlowNodeDefinitionId\");\n        flowNodeInstanceId = (Long) attributes.get(\"flowNodeInstanceId\");\n        containerType = (String) attributes.get(\"containerType\");\n        eventType = (String) attributes.get(\"eventType\");\n        subProcessId = (Long) attributes.get(\"subProcessId\");\n        parentProcessInstanceId = (Long) attributes.get(\"processInstanceId\");\n        rootProcessInstanceId = (Long) attributes.get(\"rootProcessInstanceId\");\n        isInterrupting = (Boolean) attributes.get(\"isInterrupting\");\n        final ServiceAccessor serviceAccessor = getServiceAccessor();\n        eventsHandler = serviceAccessor.getEventsHandler();\n        tenantServicesManager = serviceAccessor.getTenantServicesManager();\n        eventInstanceService = serviceAccessor.getEventInstanceService();\n        jobDescriptorId = (Long) attributes.get(JOB_DESCRIPTOR_ID);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/log/LogMessageBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.log;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.session.model.SSession;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class LogMessageBuilder {\n\n    /**\n     * Build the log message using the flow node's context (display name, id, parent activity id, parent process\n     * instance id, root process instance id, and\n     * process definition id)\n     *\n     * @param flowNodeInstance\n     * @return the message log built using the flow node's context.\n     */\n    public static String buildFlowNodeContextMessage(final SFlowNodeInstance flowNodeInstance) {\n        StringBuilder stb = new StringBuilder();\n        stb.append(\" [name = <\");\n        stb.append(flowNodeInstance.getName());\n        stb.append(\">, display name = <\");\n        stb.append(flowNodeInstance.getDisplayName());\n        stb.append(\">, id = <\");\n        stb.append(flowNodeInstance.getId());\n        if (flowNodeInstance.getParentActivityInstanceId() > 0) {\n            stb.append(\">, parent activity instance = <\");\n            stb.append(flowNodeInstance.getParentActivityInstanceId());\n        }\n        stb.append(\">, parent process instance = <\");\n        stb.append(flowNodeInstance.getParentProcessInstanceId());\n        stb.append(\">, root process instance = <\");\n        stb.append(flowNodeInstance.getRootProcessInstanceId());\n        stb.append(\">, process definition = <\");\n        stb.append(flowNodeInstance.getProcessDefinitionId());\n        stb.append(\">]\");\n        return stb.toString();\n    }\n\n    public static String buildExecuteTaskContextMessage(final SFlowNodeInstance flowNodeInstance, final String username,\n            final long executerUserId,\n            final long executerSubstituteId, Map<String, Serializable> inputs) {\n        final StringBuilder stb = new StringBuilder();\n        stb.append(\"The user <\" + username + \"> \");\n        if (executerUserId != executerSubstituteId) {\n            stb.append(\"acting as delegate of the user with id = <\" + executerUserId + \"> \");\n        }\n        stb.append(\"has executed the task\");\n        stb.append(LogMessageBuilder.buildFlowNodeContextMessage(flowNodeInstance));\n        if (inputs != null && !inputs.isEmpty()) {\n            stb.append(\" with task inputs: \" + inputs);\n        }\n        return stb.toString();\n    }\n\n    /**\n     * Build message \"The user <session.getUsername> (acting as delegate of user with id <starterId>)\"\n     *\n     * @param session\n     * @param starterId\n     * @return\n     */\n    public static String builUserActionPrefix(final SSession session, final long starterId) {\n        final StringBuilder stb = new StringBuilder();\n        stb.append(\"The user <\");\n        stb.append(session.getUserName());\n        stb.append(\"> \");\n        if (starterId != session.getUserId()) {\n            stb.append(\"acting as delegate of the user with id = <\");\n            stb.append(starterId);\n            stb.append(\"> \");\n        }\n        return stb.toString();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/message/MessagesHandlingService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.message;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.ArrayBlockingQueue;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\n\nimport io.micrometer.core.instrument.Counter;\nimport io.micrometer.core.instrument.MeterRegistry;\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.api.utils.VisibleForTesting;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.TenantLifecycleService;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageModificationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventModificationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventReadException;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SMessageInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingMessageEventBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SBPMEventType;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageEventCouple;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent;\nimport org.bonitasoft.engine.execution.work.BPMWorkFactory;\nimport org.bonitasoft.engine.lock.BonitaLock;\nimport org.bonitasoft.engine.lock.LockService;\nimport org.bonitasoft.engine.mdc.MDCTransmitingThreadPoolExecutor;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.transaction.BonitaTransactionSynchronization;\nimport org.bonitasoft.engine.transaction.STransactionNotFoundException;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.bonitasoft.engine.work.SWorkRegisterException;\nimport org.bonitasoft.engine.work.WorkService;\n\n/**\n * @author Baptiste Mesta\n */\n@Slf4j\npublic class MessagesHandlingService implements TenantLifecycleService {\n\n    private static final int MAX_COUPLES = 100;\n    private static final String LOCK_TYPE = \"EVENTS\";\n    public static final String NUMBER_OF_MESSAGES_EXECUTED = \"bonita.bpmengine.message.executed\";\n    public static final String NUMBER_OF_MESSAGES_POTENTIAL_MATCHED = \"bonita.bpmengine.message.potential\";\n    public static final String NUMBER_OF_MESSAGES_MATCHING_RETRIGGERED_TASKS = \"bonita.bpmengine.message.retriggeredtasks\";\n    private ThreadPoolExecutor threadPoolExecutor;\n    private final EventInstanceService eventInstanceService;\n    private final WorkService workService;\n    private final LockService lockService;\n    private final UserTransactionService userTransactionService;\n    private final BPMWorkFactory workFactory;\n\n    private final Counter executedMessagesCounter;\n    private final Counter matchedPotentialMessagesCounter;\n    private final Counter retriggeredMatchingTasksCounter;\n\n    public MessagesHandlingService(EventInstanceService eventInstanceService, WorkService workService,\n            LockService lockService, UserTransactionService userTransactionService, BPMWorkFactory workFactory,\n            MeterRegistry meterRegistry) {\n        this.eventInstanceService = eventInstanceService;\n        this.workService = workService;\n        this.lockService = lockService;\n        this.userTransactionService = userTransactionService;\n        this.workFactory = workFactory;\n        executedMessagesCounter = Counter.builder(NUMBER_OF_MESSAGES_EXECUTED)\n                .baseUnit(\"messages\")\n                .description(\"BPMN message couples executed\")\n                .register(meterRegistry);\n        matchedPotentialMessagesCounter = Counter.builder(NUMBER_OF_MESSAGES_POTENTIAL_MATCHED)\n                .baseUnit(\"messages\")\n                .description(\"BPMN message couples potentially matched\")\n                .register(meterRegistry);\n        retriggeredMatchingTasksCounter = Counter.builder(NUMBER_OF_MESSAGES_MATCHING_RETRIGGERED_TASKS)\n                .baseUnit(\"messages matching tasks\")\n                .description(\"BPMN message matching tasks retriggered\")\n                .register(meterRegistry);\n    }\n\n    @Override\n    public void start() {\n        log.info(\"Starting BPMN messages matcher thread\");\n        threadPoolExecutor = new MDCTransmitingThreadPoolExecutor(1, 1, 1L, TimeUnit.HOURS,\n                new ArrayBlockingQueue<>(5),\n                r -> new Thread(r, \"Bonita-Message-Matching\"),\n                (r, executor) -> log.debug(\"Message matching queue capacity reached\"));\n        log.debug(\"Thread pool that handle messages matching successfully started\");\n    }\n\n    @Override\n    public void stop() {\n        log.info(\"Stopping BPMN messages matcher thread\");\n        if (threadPoolExecutor == null) {\n            log.info(\"BPMN messages matcher thread is already stopped\");\n            return;\n        }\n        threadPoolExecutor.shutdown();\n        try {\n            boolean termination = threadPoolExecutor.awaitTermination(5000, TimeUnit.MILLISECONDS);\n            if (!termination) {\n                log.warn(\"Failed to terminate the BPMN messages matcher thread.\" +\n                        \" This will not have functional impacts but it might produce warnings on server shutdown\");\n            }\n        } catch (InterruptedException ignored) {\n        }\n        threadPoolExecutor = null;\n        log.info(\"BPMN messages matcher thread successfully stopped\");\n    }\n\n    @Override\n    public void pause() {\n        stop();\n    }\n\n    @Override\n    public void resume() {\n        start();\n    }\n\n    public void triggerMatchingOfMessages() throws STransactionNotFoundException {\n        if (threadPoolExecutor == null) {\n            log.warn(\"Cannot match messages when service is stopped. Maybe the engine is not yet started\");\n            return;\n        }\n        userTransactionService.registerBonitaSynchronization(new RegisterMessagesMatchingSynchronization());\n    }\n\n    @VisibleForTesting\n    void matchEventCoupleAndTriggerExecution() throws Exception {\n        userTransactionService.executeInTransaction(() -> {\n            final List<SMessageEventCouple> potentialMessageCouples = eventInstanceService.getMessageEventCouples(0,\n                    MAX_COUPLES);\n            final int potentialMessagesCount = potentialMessageCouples.size();\n            log.info(\"Found {} potential message/event couples\", potentialMessagesCount);\n            matchedPotentialMessagesCounter.increment(potentialMessagesCount);\n            final List<SMessageEventCouple> uniqueCouples = getMessageUniqueCouples(potentialMessageCouples);\n            if (!uniqueCouples.isEmpty()) {\n                log.info(\"Triggering execution of unique {} message/event couples\", uniqueCouples.size());\n                executeUniqueMessageCouplesWork(uniqueCouples);\n                log.info(\"Execution of message/event couples triggered\");\n            } else {\n                log.debug(\"No message/event couples to be executed\");\n            }\n            if (potentialMessagesCount == MAX_COUPLES) {\n                log.debug(\"There are more than {} message/event couples to match. \" +\n                        \"Will trigger the execution again now, to match more couples\", MAX_COUPLES);\n                triggerMatchingOfMessages();\n                retriggeredMatchingTasksCounter.increment();\n            }\n            return null;\n        });\n    }\n\n    private void executeUniqueMessageCouplesWork(final List<SMessageEventCouple> uniqueCouples)\n            throws SBonitaException {\n        for (final SMessageEventCouple couple : uniqueCouples) {\n            executeMessageCouple(couple.getMessageInstanceId(), couple.getWaitingMessageId());\n        }\n    }\n\n    @VisibleForTesting\n    void executeMessageCouple(long messageInstanceId, long waitingMessageId)\n            throws SWaitingEventReadException, SMessageInstanceReadException,\n            SMessageModificationException, SWaitingEventModificationException, SWorkRegisterException {\n        log.debug(\"Registering message/event couple execution: message {} / event {}\", messageInstanceId,\n                waitingMessageId);\n\n        // Mark messages that will be treated as \"treatment in progress\":\n        final SWaitingMessageEvent waitingMsg = eventInstanceService.getWaitingMessage(waitingMessageId);\n        final SMessageInstance messageInstance = eventInstanceService.getMessageInstance(messageInstanceId);\n        markMessageAsInProgress(messageInstance);\n\n        // EVENT_SUB_PROCESS of type non-interrupted should be considered as well, as soon as we support them\n        if (!SBPMEventType.START_EVENT.equals(waitingMsg.getEventType())) {\n            markWaitingMessageAsInProgress(waitingMsg);\n        }\n        executedMessagesCounter.increment();\n        workService.registerWork(workFactory.createExecuteMessageCoupleWorkDescriptor(messageInstance, waitingMsg));\n    }\n\n    /**\n     * From a list of couples that may contain duplicate waiting message candidates, select only one waiting message for\n     * each message instance: the first\n     * matching waiting message is arbitrary chosen.\n     * In the case of <code>SWaitingMessageEvent</code> of types {@link SBPMEventType#START_EVENT} or\n     * {@link SBPMEventType#EVENT_SUB_PROCESS}, it can be\n     * selected several times to trigger multiple instances.\n     *\n     * @param potentialMessageCouples all the possible couples that match the potential correlation.\n     * @return the reduced list of couple, where we insure that a unique message instance is associated with a unique\n     *         waiting message.\n     */\n    List<SMessageEventCouple> getMessageUniqueCouples(List<SMessageEventCouple> potentialMessageCouples) {\n        final List<Long> takenMessages = new ArrayList<>();\n        final List<Long> takenWaitings = new ArrayList<>();\n        final List<SMessageEventCouple> uniqueMessageCouples = new ArrayList<>();\n        for (final SMessageEventCouple couple : potentialMessageCouples) {\n            final long messageInstanceId = couple.getMessageInstanceId();\n            final long waitingMessageId = couple.getWaitingMessageId();\n            final SBPMEventType waitingMessageEventType = couple.getWaitingMessageEventType();\n            final boolean isMessageAlreadyTaken = takenMessages.contains(messageInstanceId);\n            if (!isMessageAlreadyTaken && !takenWaitings.contains(waitingMessageId)) {\n                takenMessages.add(messageInstanceId);\n                // Starting events and Starting event sub-processes must not be considered as taken if they appear several times\n                // EVENT_SUB_PROCESS of type non-interrupted should be considered as well, as soon as we support them\n                if (!SBPMEventType.START_EVENT.equals(waitingMessageEventType)) {\n                    takenWaitings.add(waitingMessageId);\n                }\n                uniqueMessageCouples.add(couple);\n            } else if (log.isTraceEnabled()) {\n                log.trace(\"Ignoring couple: message {} / event {}.\" +\n                        \" Duplication cause: message? {} / event? {}\", couple.getMessageInstanceId(),\n                        couple.getWaitingMessageId(), isMessageAlreadyTaken, takenWaitings.contains(waitingMessageId));\n            }\n        }\n        return uniqueMessageCouples;\n    }\n\n    private void markMessageAsInProgress(final SMessageInstance messageInstance) throws SMessageModificationException {\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        descriptor.addField(SMessageInstanceBuilder.HANDLED, true);\n        eventInstanceService.updateMessageInstance(messageInstance, descriptor);\n    }\n\n    private void markWaitingMessageAsInProgress(final SWaitingMessageEvent waitingMsg)\n            throws SWaitingEventModificationException {\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        descriptor.addField(BuilderFactory.get(SWaitingMessageEventBuilderFactory.class).getProgressKey(),\n                SWaitingMessageEventBuilderFactory.PROGRESS_IN_TREATMENT_KEY);\n        eventInstanceService.updateWaitingMessage(waitingMsg, descriptor);\n    }\n\n    public void resetMessageCouple(long messageInstanceId, long waitingMessageId)\n            throws SWaitingEventReadException, SWaitingEventModificationException, SMessageModificationException,\n            SMessageInstanceReadException {\n        resetWaitingMessage(waitingMessageId);\n        resetMessageInstance(messageInstanceId);\n    }\n\n    private void resetMessageInstance(final long messageInstanceId)\n            throws SMessageModificationException, SMessageInstanceReadException {\n        final SMessageInstance messageInstance = eventInstanceService.getMessageInstance(messageInstanceId);\n        if (messageInstance == null) {\n            log.warn(\"Unable to reset message instance {} because it is not found\", messageInstanceId);\n            return;\n        }\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        descriptor.addField(SMessageInstanceBuilder.HANDLED, false);\n        eventInstanceService.updateMessageInstance(messageInstance, descriptor);\n    }\n\n    private void resetWaitingMessage(final long waitingMessageId)\n            throws SWaitingEventModificationException, SWaitingEventReadException {\n        final SWaitingMessageEvent waitingMsg = eventInstanceService.getWaitingMessage(waitingMessageId);\n        if (waitingMsg == null) {\n            log.warn(\"Unable to reset waiting event because it is not found: {}\", waitingMessageId);\n            return;\n        }\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        descriptor.addField(BuilderFactory.get(SWaitingMessageEventBuilderFactory.class).getProgressKey(),\n                SWaitingMessageEventBuilderFactory.PROGRESS_FREE_KEY);\n        eventInstanceService.updateWaitingMessage(waitingMsg, descriptor);\n    }\n\n    private class MessagesMatchingTask implements Callable<Void> {\n\n        @Override\n        public Void call() throws Exception {\n            try {\n                log.debug(\"Starting messages matching\");\n                // we use a lock in order to have only one execution at a time even in cluster\n                BonitaLock eventLock = lockService.tryLock(1L, LOCK_TYPE, 1L, TimeUnit.MILLISECONDS);\n                if (eventLock == null) {\n                    // It could happen that some messages were still not triggered because the work that is currently\n                    // executing was started after the last message execution\n                    log.debug(\n                            \"The task that matches BPMN messages is already running, this execution will be ignored\");\n                    return null;\n                }\n                try {\n                    matchEventCoupleAndTriggerExecution();\n                } finally {\n                    lockService.unlock(eventLock);\n                }\n                log.debug(\"Messages matching completed\");\n            } catch (Exception e) {\n                log.error(\"Error while matching messages\", e);\n                throw e;\n            }\n            return null;\n        }\n    }\n\n    private class RegisterMessagesMatchingSynchronization implements BonitaTransactionSynchronization {\n\n        @Override\n        public void afterCompletion(final int txState) {\n            threadPoolExecutor.submit(new MessagesMatchingTask());\n            log.debug(\"Messages matching task registered\");\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/operation/AbstractDocumentLeftOperandHandler.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.operation;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.LeftOperandHandler;\nimport org.bonitasoft.engine.core.operation.model.SLeftOperand;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException;\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\n\n/**\n * @author Baptiste Mesta\n */\npublic abstract class AbstractDocumentLeftOperandHandler implements LeftOperandHandler {\n\n    private final ActivityInstanceService activityInstanceService;\n    private final SessionAccessor sessionAccessor;\n    private final SessionService sessionService;\n\n    public AbstractDocumentLeftOperandHandler(final ActivityInstanceService activityInstanceService,\n            final SessionAccessor sessionAccessor,\n            final SessionService sessionService) {\n        this.activityInstanceService = activityInstanceService;\n        this.sessionAccessor = sessionAccessor;\n        this.sessionService = sessionService;\n    }\n\n    protected long getProcessInstanceId(final long containerId, final String containerType)\n            throws SFlowNodeNotFoundException, SFlowNodeReadException {\n        long processInstanceId;\n        if (DataInstanceContainer.PROCESS_INSTANCE.name().equals(containerType)) {\n            processInstanceId = containerId;\n        } else {\n            final SFlowNodeInstance flowNodeInstance = activityInstanceService.getFlowNodeInstance(containerId);\n            processInstanceId = flowNodeInstance.getParentProcessInstanceId();\n        }\n        return processInstanceId;\n    }\n\n    protected long getAuthorId(long containerId, String containerType) {\n        long loggedUserFromSession = sessionService.getLoggedUserFromSession(sessionAccessor);\n        try {\n            if (loggedUserFromSession <= 0 && DataInstanceContainer.ACTIVITY_INSTANCE.name().equals(containerType)) {\n                SActivityInstance activityInstance = activityInstanceService.getActivityInstance(containerId);\n                if (activityInstance instanceof SHumanTaskInstance instance) {\n                    return instance.getAssigneeId();\n                }\n            }\n        } catch (SActivityInstanceNotFoundException | SActivityReadException ignored) {\n        }\n        return loggedUserFromSession;\n    }\n\n    @Override\n    public void loadLeftOperandInContext(final SLeftOperand sLeftOperand, final long leftOperandContainerId,\n            final String leftOperandContainerType, final SExpressionContext expressionContext) {\n        //do nothing\n    }\n\n    @Override\n    public void loadLeftOperandInContext(final List<SLeftOperand> sLeftOperand, final long leftOperandContainerId,\n            final String leftOperandContainerType, final SExpressionContext expressionContext)\n            throws SBonitaReadException {\n        //do nothing\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/operation/BusinessDataAssignmentStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.operation;\n\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.OperationExecutorStrategy;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.operation.model.SLeftOperand;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.core.operation.model.SOperatorType;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Component\npublic class BusinessDataAssignmentStrategy implements OperationExecutorStrategy {\n\n    private final EntitiesActionsExecutor actionsExecutor;\n    private final MergeEntityAction mergeEntityAction;\n\n    public BusinessDataAssignmentStrategy(EntitiesActionsExecutor actionsExecutor,\n            MergeEntityAction mergeEntityAction) {\n        this.actionsExecutor = actionsExecutor;\n        this.mergeEntityAction = mergeEntityAction;\n    }\n\n    @Override\n    public Object computeNewValueForLeftOperand(final SOperation operation, final Object value,\n            final SExpressionContext expressionContext,\n            final boolean shouldPersistValue)\n            throws SOperationExecutionException {\n        if (!shouldPersistValue) {\n            return value;\n        }\n        try {\n            return actionsExecutor.executeAction(value, null, mergeEntityAction);\n        } catch (SEntityActionExecutionException e) {\n            throw new SOperationExecutionException(e);\n        }\n    }\n\n    @Override\n    public String getOperationType() {\n        return SOperatorType.ASSIGNMENT.name() + \"_\" + SLeftOperand.TYPE_BUSINESS_DATA;\n    }\n\n    @Override\n    public boolean shouldPersistOnNullValue() {\n        return false;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/operation/BusinessDataContext.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.operation;\n\nimport org.apache.commons.lang3.builder.EqualsBuilder;\nimport org.apache.commons.lang3.builder.HashCodeBuilder;\nimport org.bonitasoft.engine.commons.Container;\n\n/**\n * Represents a Evaluation Context to reference / retrieve Business Data.\n *\n * @author Elias Ricken de Medeiros\n */\npublic class BusinessDataContext {\n\n    /**\n     * name of the business data to retrieve\n     */\n    private String name;\n\n    /**\n     * Container on which to look for the business data (PROCESS of FLOWNODE)\n     */\n    private Container container;\n\n    public BusinessDataContext(final String name, final Container container) {\n        this.name = name;\n        this.container = container;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public Container getContainer() {\n        return container;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (o == null || getClass() != o.getClass())\n            return false;\n        BusinessDataContext that = (BusinessDataContext) o;\n        return new EqualsBuilder()\n                .append(name, that.name)\n                .append(container, that.container)\n                .isEquals();\n    }\n\n    @Override\n    public int hashCode() {\n        return new HashCodeBuilder(17, 37)\n                .append(name)\n                .append(container)\n                .toHashCode();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/operation/BusinessDataJavaMethodOperationExecutorStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.operation;\n\nimport org.bonitasoft.engine.business.data.BusinessDataService;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.operation.impl.JavaMethodOperationExecutorStrategy;\nimport org.bonitasoft.engine.core.operation.model.SLeftOperand;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Laurent Leseigneur\n * @author Matthieu Chaffotte\n */\n@Component\npublic class BusinessDataJavaMethodOperationExecutorStrategy extends JavaMethodOperationExecutorStrategy {\n\n    private final BusinessDataService businessDataService;\n    private final EntitiesActionsExecutor entitiesActionsExecutor;\n    private final MergeEntityAction mergeEntityAction;\n\n    public BusinessDataJavaMethodOperationExecutorStrategy(final BusinessDataService businessDataService,\n            final EntitiesActionsExecutor entitiesActionsExecutor,\n            final MergeEntityAction mergeEntityAction) {\n        this.businessDataService = businessDataService;\n        this.entitiesActionsExecutor = entitiesActionsExecutor;\n        this.mergeEntityAction = mergeEntityAction;\n    }\n\n    @Override\n    public Object computeNewValueForLeftOperand(final SOperation operation, final Object valueToSetObjectWith,\n            final SExpressionContext expressionContext,\n            final boolean shouldPersistValue)\n            throws SOperationExecutionException {\n        if (isBusinessData(operation)) {\n            return delegateBusinessValueForLeftOperand(operation, valueToSetObjectWith, expressionContext,\n                    shouldPersistValue);\n        }\n        return computeJavaOperation(operation, valueToSetObjectWith, expressionContext, shouldPersistValue);\n    }\n\n    protected Object computeJavaOperation(final SOperation operation, final Object valueToSetObjectWith,\n            final SExpressionContext expressionContext,\n            final boolean shouldPersistValue) throws SOperationExecutionException {\n        return super.computeNewValueForLeftOperand(operation, valueToSetObjectWith, expressionContext,\n                shouldPersistValue);\n    }\n\n    private Object delegateBusinessValueForLeftOperand(final SOperation operation, final Object valueToSetObjectWith,\n            final SExpressionContext expressionContext, final boolean shouldPersistValue)\n            throws SOperationExecutionException {\n        final Object businessObject = extractObjectToInvokeFromContext(operation, expressionContext);\n        final String methodName = extractMethodName(operation);\n        final String parameterType = extractParameterType(operation);\n        try {\n            Object newValue = businessDataService.callJavaOperation(businessObject, valueToSetObjectWith, methodName,\n                    parameterType);\n            if (shouldPersistValue) {\n                newValue = entitiesActionsExecutor.executeAction(newValue, null, mergeEntityAction);\n            }\n            return newValue;\n        } catch (final SBonitaException e) {\n            throw new SOperationExecutionException(e);\n        }\n    }\n\n    private boolean isBusinessData(final SOperation operation) {\n        return SLeftOperand.TYPE_BUSINESS_DATA.equals(operation.getLeftOperand().getType());\n    }\n\n    @Override\n    public boolean shouldPersistOnNullValue() {\n        // should persist because the null value will be used to set an object field, but the object itself is not null\n        return true;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/operation/BusinessDataLeftOperandHandler.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.operation;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.bonitasoft.engine.business.data.BusinessDataRepository;\nimport org.bonitasoft.engine.business.data.RefBusinessDataRetriever;\nimport org.bonitasoft.engine.business.data.SBusinessDataNotFoundException;\nimport org.bonitasoft.engine.commons.Container;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.LeftOperandHandler;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.operation.model.SLeftOperand;\nimport org.bonitasoft.engine.core.process.instance.api.RefBusinessDataService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceModificationException;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\n@Component\npublic class BusinessDataLeftOperandHandler implements LeftOperandHandler {\n\n    protected final RefBusinessDataService refBusinessDataService;\n\n    private final EntitiesActionsExecutor entitiesActionsExecutor;\n    private final UpdateDataRefAction updateDataRefAction;\n    private final RefBusinessDataRetriever refBusinessDataRetriever;\n\n    private final BusinessDataRepository businessDataRepository;\n\n    protected BusinessDataLeftOperandHandler(final BusinessDataRepository businessDataRepository,\n            final RefBusinessDataService refBusinessDataService,\n            RefBusinessDataRetriever refBusinessDataRetriever, EntitiesActionsExecutor entitiesActionsExecutor,\n            UpdateDataRefAction updateDataRefAction) {\n        super();\n        this.businessDataRepository = businessDataRepository;\n        this.refBusinessDataService = refBusinessDataService;\n        this.entitiesActionsExecutor = entitiesActionsExecutor;\n        this.updateDataRefAction = updateDataRefAction;\n        this.refBusinessDataRetriever = refBusinessDataRetriever;\n    }\n\n    @Override\n    public String getType() {\n        return SLeftOperand.TYPE_BUSINESS_DATA;\n    }\n\n    @Override\n    public Object update(final SLeftOperand sLeftOperand, Map<String, Object> inputValues, final Object newValue,\n            final long containerId,\n            final String containerType)\n            throws SOperationExecutionException {\n        try {\n            return entitiesActionsExecutor.executeAction(newValue,\n                    new BusinessDataContext(sLeftOperand.getName(), new Container(containerId, containerType)),\n                    updateDataRefAction);\n        } catch (SEntityActionExecutionException e) {\n            throw new SOperationExecutionException(e);\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected Object getBusinessData(final String businessDataName, final long containerId, final String containerType)\n            throws SBonitaReadException {\n        try {\n            final SRefBusinessDataInstance reference = refBusinessDataRetriever\n                    .getRefBusinessDataInstance(new BusinessDataContext(businessDataName,\n                            new Container(containerId, containerType)));\n            final Class<Entity> dataClass = (Class<Entity>) Thread.currentThread().getContextClassLoader()\n                    .loadClass(reference.getDataClassName());\n            if (reference instanceof SSimpleRefBusinessDataInstance simpleRef) {\n                final Long dataId = simpleRef.getDataId();\n                if (dataId != null) {\n                    return businessDataRepository.findById(dataClass, dataId);\n                }\n                return dataClass.newInstance();\n            }\n            final SProcessMultiRefBusinessDataInstance multiRef = (SProcessMultiRefBusinessDataInstance) reference;\n            final List<Long> dataIds = multiRef.getDataIds();\n            if (!dataIds.isEmpty()) {\n                return businessDataRepository.findByIds(dataClass, dataIds);\n            }\n            return new ArrayList<Entity>();\n        } catch (final Exception e) {\n            throw new SBonitaReadException(e);\n        }\n    }\n\n    @Override\n    public void delete(final SLeftOperand sLeftOperand, final long containerId, final String containerType)\n            throws SOperationExecutionException {\n        try {\n            final SRefBusinessDataInstance refBusinessDataInstance = refBusinessDataRetriever\n                    .getRefBusinessDataInstance(new BusinessDataContext(sLeftOperand\n                            .getName(), new Container(containerId, containerType)));\n            removeBusinessData(refBusinessDataInstance);\n            dereferenceBusinessData(refBusinessDataInstance);\n        } catch (final Exception e) {\n            throw new SOperationExecutionException(e);\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected void removeBusinessData(final SRefBusinessDataInstance reference)\n            throws ClassNotFoundException, SBusinessDataNotFoundException {\n        final Class<Entity> dataClass = (Class<Entity>) Thread.currentThread().getContextClassLoader()\n                .loadClass(reference.getDataClassName());\n        if (reference instanceof SSimpleRefBusinessDataInstance simpleRef) {\n            final Entity entity = businessDataRepository.findById(dataClass, simpleRef.getDataId());\n            businessDataRepository.remove(entity);\n        } else {\n            final SProcessMultiRefBusinessDataInstance multiRef = (SProcessMultiRefBusinessDataInstance) reference;\n            for (final Long dataId : multiRef.getDataIds()) {\n                final Entity entity = businessDataRepository.findById(dataClass, dataId);\n                businessDataRepository.remove(entity);\n            }\n        }\n    }\n\n    protected void dereferenceBusinessData(final SRefBusinessDataInstance reference)\n            throws SRefBusinessDataInstanceModificationException {\n        if (reference instanceof SSimpleRefBusinessDataInstance) {\n            refBusinessDataService.updateRefBusinessDataInstance((SSimpleRefBusinessDataInstance) reference, null);\n        } else {\n            refBusinessDataService.updateRefBusinessDataInstance((SProcessMultiRefBusinessDataInstance) reference,\n                    new ArrayList<>());\n        }\n    }\n\n    @Override\n    public void loadLeftOperandInContext(final SLeftOperand sLeftOperand, final long leftOperandContainerId,\n            final String leftOperandContainerType,\n            final SExpressionContext expressionContext)\n            throws SBonitaReadException {\n        final Map<String, Object> inputValues = expressionContext.getInputValues();\n        final String businessDataName = sLeftOperand.getName();\n        if (!inputValues.containsKey(businessDataName)) {\n            inputValues.put(businessDataName,\n                    getBusinessData(businessDataName, leftOperandContainerId, leftOperandContainerType));\n        }\n    }\n\n    @Override\n    public void loadLeftOperandInContext(final List<SLeftOperand> sLeftOperand, final long leftOperandContainerId,\n            final String leftOperandContainerType,\n            final SExpressionContext expressionContext)\n            throws SBonitaReadException {\n        for (SLeftOperand leftOperand : sLeftOperand) {\n            loadLeftOperandInContext(leftOperand, leftOperandContainerId, leftOperandContainerType, expressionContext);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/operation/DataLeftOperandHandler.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.operation;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.LeftOperandHandler;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.operation.model.SLeftOperand;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceService;\nimport org.bonitasoft.engine.data.instance.api.ParentContainerResolver;\nimport org.bonitasoft.engine.data.instance.exception.SDataInstanceException;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\n@Component\npublic class DataLeftOperandHandler implements LeftOperandHandler {\n\n    private static final String DATA_INSTANCE = \"%DATA_INSTANCE%_\";\n    private final DataInstanceService dataInstanceService;\n\n    private final ParentContainerResolver parentContainerResolver;\n\n    public DataLeftOperandHandler(final DataInstanceService dataInstanceService,\n            final ParentContainerResolver parentContainerResolver) {\n        this.dataInstanceService = dataInstanceService;\n        this.parentContainerResolver = parentContainerResolver;\n    }\n\n    @Override\n    public String getType() {\n        return \"DATA\";\n    }\n\n    @Override\n    public Object update(final SLeftOperand leftOperand, Map<String, Object> inputValues, final Object newValue,\n            final long containerId, final String containerType)\n            throws SOperationExecutionException {\n        updateDataInstance(leftOperand, containerId, containerType, inputValues, newValue);\n        return newValue;\n    }\n\n    protected void update(final SDataInstance sDataInstance, final Object content) throws SDataInstanceException {\n        final EntityUpdateDescriptor updateDescriptor = new EntityUpdateDescriptor();\n        updateDescriptor.addField(SDataInstance.VALUE, content);\n\n        dataInstanceService.updateDataInstance(sDataInstance, updateDescriptor);\n    }\n\n    private void checkReturnType(final Object value, final SDataInstance sDataInstance)\n            throws SOperationExecutionException {\n        if (value != null) {\n            final Object dataValue = sDataInstance.getValue();\n            /*\n             * if the object is null (data is not initialized) the return type is not checked\n             * but the data instance service should throw an exception\n             */\n            if (dataValue != null) {\n                final Class<?> dataEffectiveType = dataValue.getClass();\n                final Class<?> evaluatedReturnedType = value.getClass();\n                if (!(dataEffectiveType.isAssignableFrom(evaluatedReturnedType)\n                        || dataEffectiveType.equals(evaluatedReturnedType))) {\n                    throw new SOperationExecutionException(\n                            \"Incompatible assignment operation type: Left operand \" + dataEffectiveType\n                                    + \" is not compatible with right operand \" + evaluatedReturnedType\n                                    + \" for expression with name '\" + sDataInstance.getName() + \"'\");\n                }\n            }\n        }\n    }\n\n    private void updateDataInstance(final SLeftOperand leftOperand, final long containerId, final String containerType,\n            Map<String, Object> inputValues, final Object expressionResult)\n            throws SOperationExecutionException {\n        final String dataInstanceName = leftOperand.getName();\n        SDataInstance dataInstance;\n        try {\n            dataInstance = (SDataInstance) inputValues.get(DATA_INSTANCE + dataInstanceName);\n            if (dataInstance == null) {\n                dataInstance = getDataInstance(dataInstanceName, containerId, containerType);\n            }\n            // Specific return type check for Data:\n            checkReturnType(expressionResult, dataInstance);\n            update(dataInstance, expressionResult);\n        } catch (final SDataInstanceException e) {\n            throw new SOperationExecutionException(e);\n        }\n    }\n\n    @Override\n    public void delete(final SLeftOperand leftOperand, final long containerId, final String containerType)\n            throws SOperationExecutionException {\n        throw new SOperationExecutionException(\"Deleting a data is not supported\");\n    }\n\n    @Override\n    public void loadLeftOperandInContext(final SLeftOperand sLeftOperand, final long leftOperandContainerId,\n            final String leftOperandContainerType, final SExpressionContext expressionContext)\n            throws SBonitaReadException {\n        try {\n            putInContext(sLeftOperand, leftOperandContainerId, leftOperandContainerType, expressionContext);\n        } catch (final SDataInstanceException e) {\n            throw new SBonitaReadException(\"Unable to retrieve the data\", e);\n        }\n    }\n\n    private void putInContext(SLeftOperand sLeftOperand, final long leftOperandContainerId,\n            final String leftOperandContainerType, SExpressionContext expressionContext) throws SDataInstanceException {\n        SDataInstance dataInstance = getDataInstance(sLeftOperand.getName(), leftOperandContainerId,\n                leftOperandContainerType);\n        putDataInContext(expressionContext.getInputValues(), dataInstance);\n    }\n\n    private void putDataInContext(Map<String, Object> contextToSet, SDataInstance dataInstance) {\n        String name = dataInstance.getName();\n        contextToSet.put(DATA_INSTANCE + name, dataInstance);\n        if (!contextToSet.containsKey(name)) {\n            contextToSet.put(name, dataInstance.getValue());\n        }\n    }\n\n    @Override\n    public void loadLeftOperandInContext(final List<SLeftOperand> sLeftOperand, final long leftOperandContainerId,\n            final String leftOperandContainerType, final SExpressionContext expressionContext)\n            throws SBonitaReadException {\n        try {\n            ArrayList<String> names = new ArrayList<>(sLeftOperand.size());\n            for (SLeftOperand leftOperand : sLeftOperand) {\n                names.add(leftOperand.getName());\n            }\n            List<SDataInstance> dataInstances = dataInstanceService.getDataInstances(names, leftOperandContainerId,\n                    leftOperandContainerType, parentContainerResolver);\n            for (SDataInstance dataInstance : dataInstances) {\n                putDataInContext(expressionContext.getInputValues(), dataInstance);\n            }\n        } catch (final SDataInstanceException e) {\n            throw new SBonitaReadException(\"Unable to retrieve the data\", e);\n        }\n    }\n\n    protected SDataInstance getDataInstance(final String dataInstanceName, final long containerId,\n            final String containerType) throws SDataInstanceException {\n        return dataInstanceService.getDataInstance(dataInstanceName, containerId, containerType,\n                parentContainerResolver);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/operation/DocumentLeftOperandHandler.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.operation;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.document.DocumentValue;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.document.api.DocumentService;\nimport org.bonitasoft.engine.core.document.api.impl.DocumentHelper;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.operation.model.SLeftOperand;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.springframework.stereotype.Component;\n\n/**\n * Updates of creates a document of the process.\n * this operation accepts only {@link DocumentValue} {@link DocumentValue} provides filename, mimetype and content\n * The document that will be update/created have the name given to the leftOperand (leftOperand.getName())\n * If the document already exists on the process instance (document with same name), it is update.\n * If there is no document with this name, it is created.\n *\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\n@Component\npublic class DocumentLeftOperandHandler extends AbstractDocumentLeftOperandHandler {\n\n    private final DocumentHelper documentHelper;\n    final DocumentService documentService;\n\n    public DocumentLeftOperandHandler(final DocumentService documentService,\n            final ActivityInstanceService activityInstanceService,\n            final SessionAccessor sessionAccessor, final SessionService sessionService) {\n        super(activityInstanceService, sessionAccessor, sessionService);\n        this.documentService = documentService;\n        documentHelper = new DocumentHelper(documentService, null, null);\n    }\n\n    @Override\n    public Object update(final SLeftOperand sLeftOperand, Map<String, Object> inputValues, final Object newValue,\n            final long containerId,\n            final String containerType)\n            throws SOperationExecutionException {\n        final DocumentValue documentValue = documentHelper.toCheckedDocumentValue(newValue);\n        final String documentName = sLeftOperand.getName();\n        long processInstanceId;\n        try {\n            processInstanceId = getProcessInstanceId(containerId, containerType);\n            if (newValue == null) {\n                // we just delete the current version\n                documentHelper.deleteDocument(documentName, processInstanceId);\n            } else {\n                if (documentValue.getDocumentId() != null && !documentValue.hasChanged()) {\n                    //do not update if the document value say it did not changed\n                    return newValue;\n                }\n                documentHelper.createOrUpdateDocument(documentValue, documentName, processInstanceId,\n                        getAuthorId(containerId, containerType), null);\n            }\n            return newValue;\n        } catch (final SBonitaException e) {\n            throw new SOperationExecutionException(e);\n        }\n\n    }\n\n    @Override\n    public String getType() {\n        return LeftOperand.TYPE_DOCUMENT;\n    }\n\n    @Override\n    public void delete(final SLeftOperand leftOperand, final long containerId, final String containerType)\n            throws SOperationExecutionException {\n        throw new SOperationExecutionException(\"Deleting a document is not supported\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/operation/DocumentListLeftOperandHandler.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.operation;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.document.DocumentValue;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.document.api.impl.DocumentHelper;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.operation.model.SLeftOperand;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.springframework.stereotype.Component;\n\n/**\n * Handles document lists\n * this operation accepts only a List of {@link org.bonitasoft.engine.bpm.document.DocumentValue}\n *\n * @author Baptiste Mesta\n */\n@Component\npublic class DocumentListLeftOperandHandler extends AbstractDocumentLeftOperandHandler {\n\n    public final DocumentHelper documentHelper;\n\n    public DocumentListLeftOperandHandler(final ActivityInstanceService activityInstanceService,\n            final SessionAccessor sessionAccessor,\n            final SessionService sessionService,\n            final DocumentHelper documentHelper) {\n        super(activityInstanceService, sessionAccessor, sessionService);\n        this.documentHelper = documentHelper;\n    }\n\n    @Override\n    public Object update(final SLeftOperand sLeftOperand, Map<String, Object> inputValues, final Object newValue,\n            final long containerId,\n            final String containerType)\n            throws SOperationExecutionException {\n        final List<DocumentValue> documentList = documentHelper.toCheckedList(newValue);\n        final String documentName = sLeftOperand.getName();\n        try {\n            final long processInstanceId = getProcessInstanceId(containerId, containerType);\n            documentHelper.setDocumentList(documentList, documentName, processInstanceId,\n                    getAuthorId(containerId, containerType));\n            return documentList;\n        } catch (final SBonitaException e) {\n            throw new SOperationExecutionException(e.getMessage(), e);\n        }\n\n    }\n\n    @Override\n    public String getType() {\n        return LeftOperand.TYPE_DOCUMENT_LIST;\n    }\n\n    @Override\n    public void delete(final SLeftOperand leftOperand, final long containerId, final String containerType)\n            throws SOperationExecutionException {\n        throw new SOperationExecutionException(\"Deleting a document is not supported\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/operation/EntitiesActionsExecutor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.operation;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bdm.Entity;\n\n/**\n * Allows the execution of an action against an Object that is instance of Entity or List<Entity>\n *\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic class EntitiesActionsExecutor {\n\n    /**\n     * Executes of an action against an Object that is instance of Entity or List<Entity>.\n     *\n     * @param value an Entity or List<Entity>\n     * @param businessDataContext the business data context\n     * @param action the action to be executed\n     * @return the initial object after executing the action.\n     */\n    public Object executeAction(final Object value, final BusinessDataContext businessDataContext,\n            final EntityAction action) throws SEntityActionExecutionException {\n        if (value == null) {\n            action.handleNull(businessDataContext);\n            return null;\n        }\n        if (value instanceof Entity) {\n            return action.execute((Entity) value, businessDataContext);\n        }\n        if (value instanceof List<?>) {\n            return action.execute((List<Entity>) value, businessDataContext);\n        }\n        throw new SEntityActionExecutionException(\n                value.getClass().getName() + \" is not a valid type. Expected an Entity or a List<Entity>\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/operation/EntityAction.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.operation;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bdm.Entity;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic interface EntityAction {\n\n    /**\n     * Executes an action against a null entity.\n     *\n     * @param businessDataContext the business data context\n     * @throws SEntityActionExecutionException\n     */\n    void handleNull(final BusinessDataContext businessDataContext) throws SEntityActionExecutionException;\n\n    /**\n     * Executes an action against an entity.\n     *\n     * @param entity the entity\n     * @param businessDataContext the business data context\n     * @return the entity after the action execution.\n     */\n    Entity execute(Entity entity, final BusinessDataContext businessDataContext) throws SEntityActionExecutionException;\n\n    /**\n     * Executes an action against a a list of entities.\n     *\n     * @param entities the list of entities\n     * @param businessDataContext the business data context\n     * @return the list of entities after the action execution.\n     */\n    List<Entity> execute(List<Entity> entities, final BusinessDataContext businessDataContext)\n            throws SEntityActionExecutionException;\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/operation/MergeEntityAction.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.operation;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.bonitasoft.engine.business.data.BusinessDataRepository;\nimport org.bonitasoft.engine.business.data.proxy.ServerProxyfier;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic class MergeEntityAction implements EntityAction {\n\n    BusinessDataRepository repository;\n\n    public MergeEntityAction(final BusinessDataRepository repository) {\n        this.repository = repository;\n    }\n\n    @Override\n    public Entity execute(final Entity entity, final BusinessDataContext businessDataContext)\n            throws SEntityActionExecutionException {\n        if (entity == null) {\n            throw new SEntityActionExecutionException(\"Unable to insert/update a null business object instance\");\n        }\n        try {\n            return repository.merge(ServerProxyfier.unProxifyIfNeeded(entity));\n        } catch (final IllegalArgumentException iae) {\n            throw new SEntityActionExecutionException(iae);\n        }\n    }\n\n    @Override\n    public List<Entity> execute(final List<Entity> entities, final BusinessDataContext businessDataContext)\n            throws SEntityActionExecutionException {\n        final List<Entity> mergedEntities = new ArrayList<>();\n        for (final Entity entity : entities) {\n            if (entity != null) {\n                mergedEntities.add(execute(entity, businessDataContext));\n            }\n        }\n        return mergedEntities;\n    }\n\n    @Override\n    public void handleNull(final BusinessDataContext businessDataContext) throws SEntityActionExecutionException {\n        throw new SEntityActionExecutionException(\"Cannot save a null entity\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/operation/SEntityActionExecutionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.operation;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SEntityActionExecutionException extends SBonitaException {\n\n    public SEntityActionExecutionException(final String message) {\n        super(message);\n    }\n\n    public SEntityActionExecutionException(final Throwable cause) {\n        super(cause);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/operation/StringIndexLeftOperandHandler.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.operation;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.LeftOperandHandler;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.operation.model.SLeftOperand;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\n@Component\npublic class StringIndexLeftOperandHandler implements LeftOperandHandler {\n\n    private final ProcessInstanceService processInstanceService;\n\n    private final ActivityInstanceService activityInstanceService;\n\n    public StringIndexLeftOperandHandler(final ProcessInstanceService processInstanceService,\n            final ActivityInstanceService activityInstanceService) {\n        this.processInstanceService = processInstanceService;\n        this.activityInstanceService = activityInstanceService;\n    }\n\n    @Override\n    public Object update(final SLeftOperand sLeftOperand, Map<String, Object> inputValues, final Object newValue,\n            final long containerId, final String containerType)\n            throws SOperationExecutionException {\n        final String name = sLeftOperand.getName();\n        int index;\n        try {\n            index = Integer.parseInt(name);\n        } catch (final NumberFormatException e) {\n            throw new SOperationExecutionException(\n                    \"name of left operand for search key operation must be 1,2,3,4 or 5 but was \" + name);\n        }\n        if (index < 1 || index > 5) {\n            throw new SOperationExecutionException(\n                    \"name of left operand for search key operation must be 1,2,3,4 or 5 but was \" + name);\n        }\n        if (newValue != null && !(newValue instanceof String)) {\n            throw new SOperationExecutionException(\n                    \"expression of search key operation must return a string, was:\" + newValue.getClass().getName());\n        }\n        try {\n            SProcessInstance processInstance;\n            if (DataInstanceContainer.PROCESS_INSTANCE.name().equals(containerType)) {\n                processInstance = processInstanceService.getProcessInstance(containerId);\n            } else {\n                final SFlowNodeInstance flowNodeInstance = activityInstanceService.getFlowNodeInstance(containerId);\n                processInstance = processInstanceService\n                        .getProcessInstance(flowNodeInstance.getParentProcessInstanceId());\n            }\n            if (processInstance.getCallerType() == SFlowNodeType.SUB_PROCESS) {\n                SFlowNodeInstance subProcessActivity = activityInstanceService\n                        .getFlowNodeInstance(processInstance.getCallerId());\n                processInstance = processInstanceService\n                        .getProcessInstance(subProcessActivity.getParentProcessInstanceId());\n            }\n            processInstanceService.updateProcess(processInstance,\n                    new EntityUpdateDescriptor().addField(SProcessInstance.STRING_INDEX_KEY + index, newValue));\n            return newValue;\n        } catch (final SBonitaException e) {\n            throw new SOperationExecutionException(e);\n        }\n    }\n\n    @Override\n    public String getType() {\n        return SLeftOperand.TYPE_SEARCH_INDEX;\n    }\n\n    @Override\n    public void delete(final SLeftOperand leftOperand, final long containerId, final String containerType)\n            throws SOperationExecutionException {\n        throw new SOperationExecutionException(\"Deleting a search key is not supported\");\n    }\n\n    @Override\n    public void loadLeftOperandInContext(SLeftOperand sLeftOperand, long leftOperandContainerId,\n            String leftOperandContainerType, SExpressionContext contextToSet) throws SBonitaReadException {\n        //do nothing\n    }\n\n    @Override\n    public void loadLeftOperandInContext(List<SLeftOperand> sLeftOperandList, long leftOperandContainerId,\n            String leftOperandContainerType, SExpressionContext contextToSet) throws SBonitaReadException {\n        //do nothing\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/operation/TransientDataLeftOperandHandler.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.operation;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.core.data.instance.TransientDataService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.LeftOperandHandler;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.operation.model.SLeftOperand;\nimport org.bonitasoft.engine.data.instance.exception.SDataInstanceException;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\n\n@Slf4j\n@Component\npublic class TransientDataLeftOperandHandler implements LeftOperandHandler {\n\n    private static final String TRANSIENT_DATA = \"%TRANSIENT_DATA%_\";\n\n    private final TransientDataService transientDataService;\n\n    public TransientDataLeftOperandHandler(TransientDataService transientDataService) {\n        this.transientDataService = transientDataService;\n    }\n\n    @Override\n    public String getType() {\n        return LeftOperand.TYPE_TRANSIENT_DATA;\n    }\n\n    @Override\n    public Object update(final SLeftOperand sLeftOperand, Map<String, Object> inputValues, final Object newValue,\n            final long containerId, final String containerType)\n            throws SOperationExecutionException {\n        SDataInstance dataInstance;\n        try {\n\n            dataInstance = (SDataInstance) inputValues.get(TRANSIENT_DATA + sLeftOperand.getName());\n            if (dataInstance == null) {\n                dataInstance = retrieve(sLeftOperand, containerId, containerType);\n            }\n            final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n            descriptor.addField(\"value\", newValue);\n            log.warn(\n                    \"The value of the transient data \"\n                            + sLeftOperand.getName()\n                            + \" of \"\n                            + containerId\n                            + \" \"\n                            + containerType\n                            + \" is being updated, be carefull if the application server is restarted this new value will be lost and the data will be reset to its initial value. \"\n                            + \"We advise you to change the design of your process. If you understand the risks and want to hide this warning, change the logging level of this class to error.\");\n            transientDataService.updateDataInstance(dataInstance, descriptor);\n            return newValue;\n        } catch (final SDataInstanceException | SBonitaReadException e) {\n            throw new SOperationExecutionException(\"Unable to update the transient data\", e);\n        }\n    }\n\n    @Override\n    public void delete(final SLeftOperand leftOperand, final long containerId, final String containerType)\n            throws SOperationExecutionException {\n        throw new SOperationExecutionException(\"Deleting a transient data is not supported\");\n    }\n\n    @Override\n    public void loadLeftOperandInContext(final SLeftOperand sLeftOperand, final long leftOperandContainerId,\n            final String leftOperandContainerType, final SExpressionContext expressionContext)\n            throws SBonitaReadException {\n        String name = sLeftOperand.getName();\n        SDataInstance dataInstance = retrieve(sLeftOperand, leftOperandContainerId, leftOperandContainerType);\n        expressionContext.getInputValues().put(TRANSIENT_DATA + name, dataInstance);\n        if (!expressionContext.getInputValues().containsKey(name)) {\n            expressionContext.getInputValues().put(name, dataInstance.getValue());\n        }\n    }\n\n    private SDataInstance retrieve(final SLeftOperand sLeftOperand, final Long containerId, final String containerType)\n            throws SBonitaReadException {\n        try {\n            return transientDataService.getDataInstance(sLeftOperand.getName(), containerId, containerType);\n        } catch (final SDataInstanceException e) {\n            throw new SBonitaReadException(\"Unable to read the transient data\", e);\n        }\n    }\n\n    @Override\n    public void loadLeftOperandInContext(final List<SLeftOperand> sLeftOperand, final long leftOperandContainerId,\n            final String leftOperandContainerType, final SExpressionContext expressionContext)\n            throws SBonitaReadException {\n        for (SLeftOperand leftOperand : sLeftOperand) {\n            loadLeftOperandInContext(leftOperand, leftOperandContainerId, leftOperandContainerType, expressionContext);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/operation/UpdateDataRefAction.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.operation;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.bonitasoft.engine.business.data.RefBusinessDataRetriever;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.instance.api.RefBusinessDataService;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SSimpleRefBusinessDataInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic class UpdateDataRefAction implements EntityAction {\n\n    private final RefBusinessDataService refBusinessDataService;\n    private final RefBusinessDataRetriever refBusinessDataRetriever;\n\n    public UpdateDataRefAction(final RefBusinessDataService refBusinessDataService,\n            final RefBusinessDataRetriever refBusinessDataRetriever) {\n        this.refBusinessDataService = refBusinessDataService;\n        this.refBusinessDataRetriever = refBusinessDataRetriever;\n    }\n\n    @Override\n    public Entity execute(final Entity entity, final BusinessDataContext businessDataContext)\n            throws SEntityActionExecutionException {\n        try {\n            final SRefBusinessDataInstance reference = refBusinessDataRetriever\n                    .getRefBusinessDataInstance(businessDataContext);\n            checkThatIsSimpleRef(reference);\n            final SSimpleRefBusinessDataInstance simpleRef = (SSimpleRefBusinessDataInstance) reference;\n            if (!entity.getPersistenceId().equals(simpleRef.getDataId())) {\n                refBusinessDataService.updateRefBusinessDataInstance(simpleRef, entity.getPersistenceId());\n            }\n        } catch (final SBonitaException e) {\n            throw new SEntityActionExecutionException(e);\n        }\n        return entity;\n    }\n\n    private void checkThatIsSimpleRef(final SRefBusinessDataInstance reference) throws SEntityActionExecutionException {\n        if (!(reference instanceof SSimpleRefBusinessDataInstance)) {\n            throw new SEntityActionExecutionException(\"Incompatible types: the business data '\" + reference.getName()\n                    + \"' is marked as multiple, but its new value is not a list. Either mark the business data as simple or use a list as new value.'\");\n        }\n    }\n\n    @Override\n    public List<Entity> execute(final List<Entity> entities, final BusinessDataContext businessDataContext)\n            throws SEntityActionExecutionException {\n        try {\n            final SRefBusinessDataInstance reference = refBusinessDataRetriever\n                    .getRefBusinessDataInstance(businessDataContext);\n            checkThatIsMultiRef(reference);\n            final SProcessMultiRefBusinessDataInstance multiRef = (SProcessMultiRefBusinessDataInstance) reference;\n            final ArrayList<Long> dataIds = buildDataIdsList(entities);\n            if (!multiRef.getDataIds().containsAll(dataIds) || multiRef.getDataIds().size() != dataIds.size()) {\n                refBusinessDataService.updateRefBusinessDataInstance(multiRef, dataIds);\n            }\n        } catch (final SBonitaException e) {\n            throw new SEntityActionExecutionException(e);\n        }\n        return entities;\n    }\n\n    private void checkThatIsMultiRef(final SRefBusinessDataInstance reference) throws SEntityActionExecutionException {\n        if (!(reference instanceof SProcessMultiRefBusinessDataInstance)) {\n            throw new SEntityActionExecutionException(\n                    \"Incompatible types: the business data '\"\n                            + reference.getName()\n                            + \"' is not marked as multiple, but its new value is a list. Either mark the business data as multiple or use a single Entity as new value.'\");\n        }\n    }\n\n    private ArrayList<Long> buildDataIdsList(final List<Entity> entities) throws SEntityActionExecutionException {\n        final ArrayList<Long> businessDataIds = new ArrayList<>(entities.size());\n        for (final Entity entity : entities) {\n            checkNotNull(entity);\n            businessDataIds.add(entity.getPersistenceId());\n        }\n        return businessDataIds;\n    }\n\n    private void checkNotNull(final Entity entity) throws SEntityActionExecutionException {\n        if (entity == null) {\n            throw new SEntityActionExecutionException(\n                    \"The list of entities contains some null elements. Unable to execute action against null entity.\");\n        }\n    }\n\n    @Override\n    public void handleNull(final BusinessDataContext businessDataContext) throws SEntityActionExecutionException {\n        try {\n            final SRefBusinessDataInstance reference = refBusinessDataRetriever\n                    .getRefBusinessDataInstance(businessDataContext);\n            if (reference instanceof SSimpleRefBusinessDataInstance simpleReference) {\n                refBusinessDataService.updateRefBusinessDataInstance(simpleReference, null);\n            } else {\n                final SProcessMultiRefBusinessDataInstance multiReference = (SProcessMultiRefBusinessDataInstance) reference;\n                refBusinessDataService.updateRefBusinessDataInstance(multiReference, new ArrayList<Long>());\n            }\n        } catch (final SBonitaException sbe) {\n            throw new SEntityActionExecutionException(sbe);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/page/AuthorizationRuleWithParameters.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.commons.exceptions.SExecutionException;\nimport org.bonitasoft.engine.session.SSessionNotFoundException;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.bonitasoft.engine.sessionaccessor.SessionIdNotSetException;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic abstract class AuthorizationRuleWithParameters {\n\n    protected Long getLongParameter(Map<String, Serializable> context, String parameterKey) {\n        final Map<String, String[]> queryParameters = (Map<String, String[]>) context\n                .get(URLAdapterConstants.QUERY_PARAMETERS);\n        if (queryParameters != null) {\n            String[] idParamValue = queryParameters.get(parameterKey);\n            if (idParamValue != null && idParamValue.length > 0) {\n                return Long.parseLong(idParamValue[0]);\n            }\n        }\n        return null;\n    }\n\n    protected long getLoggedUserId(SessionAccessor sessionAccessor, SessionService sessionService)\n            throws SExecutionException {\n        try {\n            return sessionService.getSession(sessionAccessor.getSessionId()).getUserId();\n        } catch (SSessionNotFoundException e) {\n            throw new SExecutionException(e);\n        } catch (SessionIdNotSetException e) {\n            throw new SExecutionException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/page/IsActorInitiatorRule.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SExecutionException;\nimport org.bonitasoft.engine.core.form.FormMappingService;\nimport org.bonitasoft.engine.core.form.SFormMapping;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\n\n/**\n * @author Anthony Birembaut\n */\npublic class IsActorInitiatorRule implements AuthorizationRule {\n\n    ActorMappingService actorMappingService;\n\n    SessionAccessor sessionAccessor;\n\n    SessionService sessionService;\n\n    FormMappingService formMappingService;\n\n    public IsActorInitiatorRule(ActorMappingService actorMappingService, SessionAccessor sessionAccessor,\n            SessionService sessionService,\n            FormMappingService formMappingService) {\n        this.actorMappingService = actorMappingService;\n        this.sessionAccessor = sessionAccessor;\n        this.sessionService = sessionService;\n        this.formMappingService = formMappingService;\n    }\n\n    @Override\n    public boolean isAllowed(final String key, final Map<String, Serializable> context) throws SExecutionException {\n        try {\n            SFormMapping formMapping = formMappingService.get(key);\n            long processDefinitionId = formMapping.getProcessDefinitionId();\n            final long userId = sessionService.getSession(sessionAccessor.getSessionId()).getUserId();\n            return actorMappingService.canUserStartProcessDefinition(userId, processDefinitionId);\n        } catch (final SBonitaException e) {\n            throw new SExecutionException(\n                    \"Unable to figure out if the logged user is an actor initiator for the process.\", e);\n        }\n    }\n\n    @Override\n    public String getId() {\n        return AuthorizationRuleConstants.IS_ACTOR_INITIATOR;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/page/IsAdminRule.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.commons.exceptions.SExecutionException;\nimport org.bonitasoft.engine.session.SSessionNotFoundException;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.session.model.SSession;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.bonitasoft.engine.sessionaccessor.SessionIdNotSetException;\n\n/**\n * @author Emmanuel Duchastenier, Anthony Birembaut\n */\npublic class IsAdminRule implements AuthorizationRule {\n\n    SessionAccessor sessionAccessor;\n\n    SessionService sessionService;\n\n    protected static final String PROCESS_DEPLOY_PERMISSION = \"process_deploy\";\n\n    public IsAdminRule(SessionAccessor sessionAccessor, SessionService sessionService) {\n        this.sessionAccessor = sessionAccessor;\n        this.sessionService = sessionService;\n    }\n\n    @Override\n    public boolean isAllowed(final String key, final Map<String, Serializable> context) throws SExecutionException {\n        try {\n            return getSession().getUserPermissions().contains(PROCESS_DEPLOY_PERMISSION);\n        } catch (SSessionNotFoundException | SessionIdNotSetException e) {\n            throw new SExecutionException(e);\n        }\n    }\n\n    protected SSession getSession() throws SSessionNotFoundException, SessionIdNotSetException {\n        return sessionService.getSession(sessionAccessor.getSessionId());\n    }\n\n    @Override\n    public String getId() {\n        return AuthorizationRuleConstants.IS_ADMIN;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/page/IsInvolvedInProcessInstanceRule.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.impl.TaskInvolvementDelegate;\nimport org.bonitasoft.engine.commons.exceptions.SExecutionException;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class IsInvolvedInProcessInstanceRule extends AuthorizationRuleWithParameters implements AuthorizationRule {\n\n    private SessionAccessor sessionAccessor;\n\n    private SessionService sessionService;\n\n    private final TaskInvolvementDelegate taskInvolvementDelegate;\n\n    public IsInvolvedInProcessInstanceRule(SessionService sessionService, SessionAccessor sessionAccessor,\n            TaskInvolvementDelegate taskInvolvementDelegate) {\n        this.sessionAccessor = sessionAccessor;\n        this.sessionService = sessionService;\n        this.taskInvolvementDelegate = taskInvolvementDelegate;\n    }\n\n    @Override\n    public boolean isAllowed(final String key, final Map<String, Serializable> context) throws SExecutionException {\n        long userId = getLoggedUserId(sessionAccessor, sessionService);\n        Long processInstanceId = getLongParameter(context, URLAdapterConstants.ID_QUERY_PARAM);\n        if (processInstanceId == null) {\n            throw new IllegalArgumentException(\n                    \"Parameter 'id' is mandatory to execute Page Authorization rule 'IsInvolvedInProcessInstanceRule'\");\n        }\n        return hasUserPendingOrAssignedTasks(userId, processInstanceId);\n\n    }\n\n    private boolean hasUserPendingOrAssignedTasks(long userId, Long processInstanceId) throws SExecutionException {\n        return taskInvolvementDelegate.hasUserPendingOrAssignedTasks(userId, processInstanceId);\n    }\n\n    @Override\n    public String getId() {\n        return AuthorizationRuleConstants.IS_INVOLVED_IN_PROCESS_INSTANCE;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/page/IsManagerOfUserInvolvedInProcessInstanceRule.java",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.ProcessRuntimeAPI;\nimport org.bonitasoft.engine.api.impl.ProcessInvolvementDelegate;\nimport org.bonitasoft.engine.commons.exceptions.SExecutionException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\n\n/**\n * This Rule authorizes a user, if this user is the manager of another user involved in the given process instance.\n * It has the same behavior as {@link ProcessRuntimeAPI#isManagerOfUserInvolvedInProcessInstance(long, long)}\n *\n * @author Emmanuel Duchastenier\n */\npublic class IsManagerOfUserInvolvedInProcessInstanceRule extends AuthorizationRuleWithParameters\n        implements AuthorizationRule {\n\n    private SessionAccessor sessionAccessor;\n\n    private SessionService sessionService;\n\n    private final ProcessInvolvementDelegate processInvolvementDelegate;\n\n    public IsManagerOfUserInvolvedInProcessInstanceRule(SessionService sessionService, SessionAccessor sessionAccessor,\n            ProcessInvolvementDelegate processInvolvementDelegate) {\n        this.sessionAccessor = sessionAccessor;\n        this.sessionService = sessionService;\n        this.processInvolvementDelegate = processInvolvementDelegate;\n    }\n\n    @Override\n    public boolean isAllowed(final String key, final Map<String, Serializable> context) throws SExecutionException {\n        Long processInstanceId = getLongParameter(context, URLAdapterConstants.ID_QUERY_PARAM);\n        if (processInstanceId == null) {\n            throw new IllegalArgumentException(\n                    \"Parameter 'id' is mandatory to execute Page Authorization rule 'isManagerOfUserInvolvedInProcessInstance'\");\n        }\n        try {\n            long userId = getLoggedUserId(sessionAccessor, sessionService);\n            return processInvolvementDelegate.isManagerOfUserInvolvedInProcessInstance(userId, processInstanceId);\n        } catch (BonitaException e) {\n            throw new SExecutionException(e);\n        }\n\n    }\n\n    @Override\n    public String getId() {\n        return AuthorizationRuleConstants.IS_MANAGER_OF_USER_INVOLVED_IN_PROCESS_INSTANCE;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/page/IsProcessInitiatorRule.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.impl.ProcessInvolvementDelegate;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.commons.exceptions.SExecutionException;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\n\n/**\n * @author Anthony Birembaut\n */\npublic class IsProcessInitiatorRule extends AuthorizationRuleWithParameters implements AuthorizationRule {\n\n    private SessionService sessionService;\n\n    private SessionAccessor sessionAccessor;\n\n    private final ProcessInvolvementDelegate processInvolvementDelegate;\n\n    public IsProcessInitiatorRule(SessionService sessionService, SessionAccessor sessionAccessor,\n            ProcessInvolvementDelegate processInvolvementDelegate) {\n        this.sessionAccessor = sessionAccessor;\n        this.sessionService = sessionService;\n        this.processInvolvementDelegate = processInvolvementDelegate;\n    }\n\n    @Override\n    public boolean isAllowed(final String key, final Map<String, Serializable> context) throws SExecutionException {\n        long userId = getLoggedUserId(sessionAccessor, sessionService);\n        Long processInstanceId = getLongParameter(context, URLAdapterConstants.ID_QUERY_PARAM);\n        if (processInstanceId == null) {\n            throw new IllegalArgumentException(\n                    \"Parameter 'id' is mandatory to execute Page Authorization rule 'IsProcessInitiatorRule'\");\n        }\n        try {\n            return processInvolvementDelegate.isProcessOrArchivedProcessInitiator(userId, processInstanceId);\n        } catch (ProcessInstanceNotFoundException e) {\n            throw new SExecutionException(e);\n        }\n    }\n\n    @Override\n    public String getId() {\n        return AuthorizationRuleConstants.IS_PROCESS_INITIATOR;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/page/IsProcessOwnerRule.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SExecutionException;\nimport org.bonitasoft.engine.core.form.FormMappingService;\nimport org.bonitasoft.engine.core.form.SFormMapping;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.bonitasoft.engine.supervisor.mapping.SupervisorMappingService;\n\n/**\n * @author Emmanuel Duchastenier, Anthony Birembaut\n */\npublic class IsProcessOwnerRule implements AuthorizationRule {\n\n    SupervisorMappingService supervisorMappingService;\n\n    SessionAccessor sessionAccessor;\n\n    SessionService sessionService;\n\n    FormMappingService formMappingService;\n\n    public IsProcessOwnerRule(SupervisorMappingService supervisorMappingService, SessionAccessor sessionAccessor,\n            SessionService sessionService,\n            FormMappingService formMappingService) {\n        this.supervisorMappingService = supervisorMappingService;\n        this.sessionAccessor = sessionAccessor;\n        this.sessionService = sessionService;\n        this.formMappingService = formMappingService;\n    }\n\n    @Override\n    public boolean isAllowed(final String key, final Map<String, Serializable> context) throws SExecutionException {\n        try {\n            SFormMapping formMapping = formMappingService.get(key);\n            long processDefinitionId = formMapping.getProcessDefinitionId();\n            final long userId = sessionService.getSession(sessionAccessor.getSessionId()).getUserId();\n            return supervisorMappingService.isProcessSupervisor(processDefinitionId, userId);\n        } catch (final SBonitaException e) {\n            throw new SExecutionException(\"Unable to figure out if the logged user is a process owner.\", e);\n        }\n    }\n\n    @Override\n    public String getId() {\n        return AuthorizationRuleConstants.IS_PROCESS_OWNER;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/page/IsTaskAvailableForUserRule.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityReadException;\nimport org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\n\n/**\n * @author Emmanuel Duchastenier, Anthony Birembaut\n */\npublic class IsTaskAvailableForUserRule implements AuthorizationRule {\n\n    ActivityInstanceService activityInstanceService;\n\n    SessionService sessionService;\n\n    SessionAccessor sessionAccessor;\n\n    public IsTaskAvailableForUserRule(ActivityInstanceService activityInstanceService, SessionService sessionService,\n            SessionAccessor sessionAccessor) {\n        this.activityInstanceService = activityInstanceService;\n        this.sessionService = sessionService;\n        this.sessionAccessor = sessionAccessor;\n    }\n\n    @Override\n    public boolean isAllowed(final String key, final Map<String, Serializable> context) throws SExecutionException {\n        @SuppressWarnings(\"unchecked\")\n        final Map<String, String[]> queryParameters = (Map<String, String[]>) context\n                .get(URLAdapterConstants.QUERY_PARAMETERS);\n        String[] idParamValue = new String[0];\n        if (queryParameters != null) {\n            idParamValue = queryParameters.get(URLAdapterConstants.ID_QUERY_PARAM);\n        }\n        long taskInstanceId;\n        if (idParamValue == null || idParamValue.length == 0) {\n            throw new IllegalArgumentException(\"The parameter \\\"id\\\" is missing from the original URL\");\n        } else {\n            taskInstanceId = Long.parseLong(idParamValue[0]);\n            try {\n                long userId = sessionService.getSession(sessionAccessor.getSessionId()).getUserId();\n                return isTaskAvailableForOrExecutedByUser(taskInstanceId, userId);\n            } catch (final SBonitaException e) {\n                throw new SExecutionException(\n                        \"Unable to figure out if the task \" + taskInstanceId + \" is available for the user.\", e);\n            }\n        }\n    }\n\n    protected boolean isTaskAvailableForOrExecutedByUser(long taskInstanceId, long userId)\n            throws SActivityReadException, SBonitaReadException, SActivityInstanceNotFoundException {\n        try {\n            final SHumanTaskInstance humanTaskInstance = activityInstanceService.getHumanTaskInstance(taskInstanceId);\n            long assigneeId = humanTaskInstance.getAssigneeId();\n            if (assigneeId > 0) {\n                return userId == assigneeId;\n            } else {\n                return activityInstanceService.isTaskPendingForUser(taskInstanceId, userId);\n            }\n        } catch (SActivityInstanceNotFoundException e) {\n            final SAHumanTaskInstance archivedHumanTaskInstance = activityInstanceService\n                    .getLastArchivedFlowNodeInstance(SAHumanTaskInstance.class, taskInstanceId);\n            if (archivedHumanTaskInstance != null) {\n                return userId == archivedHumanTaskInstance.getExecutedBy();\n            } else {\n                throw e;\n            }\n        }\n    }\n\n    @Override\n    public String getId() {\n        return AuthorizationRuleConstants.IS_TASK_AVAILABLE_FOR_USER;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/page/IsTaskPerformerRule.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.impl.TaskInvolvementDelegate;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SExecutionException;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class IsTaskPerformerRule extends AuthorizationRuleWithParameters implements AuthorizationRule {\n\n    private final SessionService sessionService;\n\n    private final SessionAccessor sessionAccessor;\n\n    private final TaskInvolvementDelegate taskInvolvementDelegate;\n\n    public IsTaskPerformerRule(SessionService sessionService, SessionAccessor sessionAccessor,\n            TaskInvolvementDelegate taskInvolvementDelegate) {\n        this.sessionAccessor = sessionAccessor;\n        this.sessionService = sessionService;\n        this.taskInvolvementDelegate = taskInvolvementDelegate;\n    }\n\n    @Override\n    public boolean isAllowed(String key, Map<String, Serializable> context) throws SExecutionException {\n        try {\n            long userId = getLoggedUserId(sessionAccessor, sessionService);\n            Long processInstanceId = getLongParameter(context, URLAdapterConstants.ID_QUERY_PARAM);\n            if (processInstanceId == null) {\n                throw new IllegalArgumentException(\n                        \"Parameter 'id' is mandatory to execute Page Authorization rule 'IsProcessInitiatorRule'\");\n            }\n            return taskInvolvementDelegate.isExecutorOfArchivedTaskOfProcess(userId, processInstanceId);\n        } catch (final SBonitaException e) {\n            throw new SExecutionException(e);\n        }\n    }\n\n    @Override\n    public String getId() {\n        return AuthorizationRuleConstants.IS_TASK_PERFORMER;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/platform/BroadcastServiceLocal.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform;\n\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.Future;\n\nimport org.bonitasoft.engine.service.BroadcastService;\nimport org.bonitasoft.engine.service.TaskResult;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;\nimport org.springframework.stereotype.Component;\n\n/**\n * This implementation does nothing, to be used in a single node environment.\n * When cluster feature is enabled, an other implementation of the BroadcastService dispatch calls to other nodes.\n *\n * @author Baptiste Mesta\n */\n@Component\n@ConditionalOnSingleCandidate(BroadcastService.class)\npublic class BroadcastServiceLocal implements BroadcastService {\n\n    @Override\n    public <T> Future<Map<String, TaskResult<T>>> executeOnOthers(Callable<T> callable) {\n        return CompletableFuture.completedFuture(Collections.emptyMap());\n    }\n\n    @Override\n    public <T> Map<String, TaskResult<T>> executeOnOthersAndWait(Callable<T> callable) {\n        return Collections.emptyMap();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/platform/PlatformManager.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.PlatformLifecycleService;\nimport org.bonitasoft.engine.commons.PlatformRestartHandler;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.platform.configuration.NodeConfiguration;\nimport org.bonitasoft.engine.service.BonitaTaskExecutor;\nimport org.bonitasoft.engine.service.ServiceAccessorSingleton;\nimport org.bonitasoft.engine.tenant.TenantStateManager;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\n\n/**\n * Manages the lifecycle of the Bonita Platform node, coordinating the start and stop of all platform services.\n * <p>\n * The PlatformManager is responsible for orchestrating the startup and shutdown sequences of the Bonita Engine.\n * It manages the state transitions of the platform node (the current JVM instance) and coordinates all\n * {@link PlatformLifecycleService} implementations to ensure proper initialization and cleanup.\n * <p>\n * <b>Key Responsibilities:</b>\n * <ul>\n * <li><b>Platform State Management</b>: Tracks and manages platform state (STARTED, STOPPED, STARTING, STOPPING)\n * via {@link PlatformStateProvider}</li>\n * <li><b>Service Lifecycle Coordination</b>: Starts and stops all {@link PlatformLifecycleService} instances\n * in the correct order (SchedulerService, WorkService, ConnectorExecutor, ClassLoaderService, etc.)</li>\n * <li><b>Tenant Management</b>: Coordinates with {@link TenantStateManager} to start/stop tenant services</li>\n * <li><b>Version Validation</b>: Verifies platform binaries version matches database schema via\n * {@link PlatformVersionChecker}</li>\n * <li><b>Restart Handlers</b>: Executes {@link PlatformRestartHandler} instances after successful startup\n * (e.g., resume interrupted work, reschedule jobs)</li>\n * </ul>\n * <p>\n * <b>Startup Sequence:</b>\n * <ol>\n * <li>Validates platform state allows starting (via {@link PlatformStateProvider#initializeStart()})</li>\n * <li>Checks platform version compatibility via {@link PlatformVersionChecker}</li>\n * <li>Starts all {@link PlatformLifecycleService} instances in order</li>\n * <li>Updates state to STARTED</li>\n * <li>Starts tenant services via {@link TenantStateManager}</li>\n * <li>Executes platform restart handlers asynchronously</li>\n * </ol>\n * <p>\n * <b>Shutdown Sequence:</b>\n * <ol>\n * <li>Validates platform state allows stopping (via {@link PlatformStateProvider#initializeStop()})</li>\n * <li>Stops tenant services via {@link TenantStateManager}</li>\n * <li>Stops all {@link PlatformLifecycleService} instances in order</li>\n * <li>Updates state to STOPPED</li>\n * </ol>\n * <p>\n * <b>Thread Safety:</b> All start/stop methods are synchronized to prevent concurrent state modifications.\n * <p>\n * <b>Typical Platform Services Managed:</b>\n * <ul>\n * <li>SchedulerService - Quartz-based job scheduling</li>\n * <li>WorkService - Asynchronous work execution via thread pools</li>\n * <li>ConnectorExecutorService - Connector execution management</li>\n * <li>ClassLoaderService - Process-specific classloader management</li>\n * <li>EventService - Event publishing and subscription</li>\n * <li>CacheService - Data caching infrastructure</li>\n * </ul>\n *\n * @see PlatformLifecycleService\n * @see PlatformStateProvider\n * @see TenantStateManager\n * @see PlatformVersionChecker\n * @see PlatformRestartHandler\n */\n@Component\npublic class PlatformManager {\n\n    private static final Logger logger = LoggerFactory.getLogger(PlatformManager.class);\n    private final BonitaTaskExecutor bonitaTaskExecutor;\n    private final NodeConfiguration nodeConfiguration;\n    private final List<PlatformLifecycleService> platformServices;\n    private final PlatformStateProvider platformStateProvider;\n    private final PlatformVersionChecker platformVersionChecker;\n\n    public PlatformManager(NodeConfiguration nodeConfiguration, List<PlatformLifecycleService> platformServices,\n            PlatformStateProvider platformStateProvider, BonitaTaskExecutor bonitaTaskExecutor,\n            PlatformVersionChecker platformVersionChecker) {\n        this.nodeConfiguration = nodeConfiguration;\n        this.platformServices = platformServices;\n        this.platformStateProvider = platformStateProvider;\n        this.bonitaTaskExecutor = bonitaTaskExecutor;\n        this.platformVersionChecker = platformVersionChecker;\n    }\n\n    /**\n     * @return the current state of the platform\n     */\n    public PlatformState getState() {\n        return platformStateProvider.getState();\n    }\n\n    /**\n     * Stop the platform\n     *\n     * @return true if the node was stopped, false if it was not stoppable (already stopped, starting or stopping)\n     */\n    public synchronized boolean stop() throws Exception {\n        logger.info(\"Stopping platform:\");\n        if (!platformStateProvider.initializeStop()) {\n            return false;\n        }\n        getTenantStateManager().stop();\n        for (final PlatformLifecycleService platformService : platformServices) {\n            logger.info(\"Stop service of platform: {}\", platformService);\n            platformService.stop();\n        }\n        platformStateProvider.setStopped();\n        logger.info(\"Platform stopped.\");\n        return true;\n    }\n\n    /**\n     * Start the platform and default tenant\n     *\n     * @return true if the node was started, false if it was not startable (already started, starting or stopping)\n     */\n    public synchronized boolean start() throws Exception {\n        logger.info(\"Starting platform:\");\n        if (!platformStateProvider.initializeStart()) {\n            logger.info(\"Platform cannot be started, it is: {}\", platformStateProvider.getState());\n            return false;\n        }\n        checkPlatformVersion();\n        startPlatformServices();\n        platformStateProvider.setStarted();\n\n        getTenantStateManager().start();\n\n        restartHandlersOfPlatform();\n        logger.info(\"Platform started.\");\n        return true;\n    }\n\n    TenantStateManager getTenantStateManager() {\n        return ServiceAccessorSingleton.getInstance().getTenantStateManager();\n    }\n\n    private void restartHandlersOfPlatform() {\n        for (final PlatformRestartHandler platformRestartHandler : nodeConfiguration.getPlatformRestartHandlers()) {\n            bonitaTaskExecutor.execute(platformRestartHandler::execute);\n        }\n    }\n\n    private void checkPlatformVersion() throws Exception {\n        if (!platformVersionChecker.verifyPlatformVersion()) {\n            throw new StartNodeException(platformVersionChecker.getErrorMessage());\n        }\n    }\n\n    private void startPlatformServices() throws SBonitaException {\n        for (final PlatformLifecycleService platformService : platformServices) {\n            logger.info(\"Start service of platform : {}\", platformService);\n            platformService.start();\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/platform/PlatformStateProvider.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform;\n\nimport lombok.Getter;\nimport org.springframework.stereotype.Component;\n\n@Getter\n@Component\npublic class PlatformStateProvider {\n\n    private PlatformState state = PlatformState.STOPPED;\n\n    public PlatformStateProvider() {\n    }\n\n    PlatformStateProvider(PlatformState state) {\n        this.state = state;\n    }\n\n    /**\n     * Transition the Platform state to STARTED ( state put in STARTING )\n     *\n     * @return true only if the state was changed\n     */\n    boolean initializeStart() {\n        if (state != PlatformState.STOPPED) {\n            return false;\n        }\n        state = PlatformState.STARTING;\n        return true;\n    }\n\n    void setStarted() {\n        state = PlatformState.STARTED;\n    }\n\n    /**\n     * Transition the Platform state to STOPPED ( state put in STOPPING )\n     *\n     * @return true only if the state was changed\n     */\n    boolean initializeStop() {\n        if (state != PlatformState.STARTED) {\n            return false;\n        }\n        state = PlatformState.STOPPING;\n        return true;\n    }\n\n    void setStopped() {\n        state = PlatformState.STOPPED;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/platform/PlatformVersionChecker.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform;\n\nimport java.io.Serializable;\nimport java.text.MessageFormat;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ExecutionException;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport org.bonitasoft.engine.EngineInitializer;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.service.BroadcastService;\nimport org.bonitasoft.engine.service.TaskResult;\nimport org.bonitasoft.engine.service.impl.ServiceAccessorFactory;\nimport org.bonitasoft.engine.transaction.TransactionService;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Matthieu Chaffotte\n * @author Baptiste Mesta\n * @author Emmanuel Duchastenier\n */\n@Component\npublic class PlatformVersionChecker {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(EngineInitializer.class);\n\n    private final PlatformService platformService;\n    private final BroadcastService broadcastService;\n    private final TransactionService transactionService;\n\n    private String errorMessage;\n\n    public PlatformVersionChecker(final PlatformService platformService, BroadcastService broadcastService,\n            TransactionService transactionService) {\n        this.platformService = platformService;\n        this.broadcastService = broadcastService;\n        this.transactionService = transactionService;\n    }\n\n    public boolean verifyPlatformVersion() throws Exception {\n        return transactionService.executeInTransaction(this::execute);\n    }\n\n    boolean execute() throws SBonitaException {\n        // the database  schema version:\n        String databaseSchemaVersion = platformService.getPlatform().getDbSchemaVersion();\n        // the version in jars\n        final String platformVersionFromBinaries = platformService.getSPlatformProperties().getPlatformVersion();\n        String supportedDatabaseSchemaVersion = extractMinorVersion(platformVersionFromBinaries);\n        LOGGER.info(\"Bonita platform version (binaries): {}\", platformVersionFromBinaries);\n        LOGGER.info(\"Bonita database schema version: {}\", databaseSchemaVersion);\n\n        boolean isDatabaseSchemaSupported = databaseSchemaVersion.equals(supportedDatabaseSchemaVersion);\n        if (!isDatabaseSchemaSupported) {\n            errorMessage = MessageFormat.format(\"The version of the platform in database is not the same as expected:\" +\n                    \" Supported database schema version is <{0}> and current database schema version is <{1}>\",\n                    supportedDatabaseSchemaVersion, databaseSchemaVersion);\n            return false;\n        }\n\n        final Optional<String> versionFromOtherNodes = getVersionFromOtherNodes();\n        if (versionFromOtherNodes.isPresent() && !platformVersionFromBinaries.equals(versionFromOtherNodes.get())) {\n            errorMessage = MessageFormat.format(\n                    \"Node cannot be started as it is in version {0} whereas other nodes are in version {1}\\n\"\n                            + \"All nodes in the same cluster must execute the same Bonita runtime version\",\n                    platformVersionFromBinaries, versionFromOtherNodes.get());\n            LOGGER.error(errorMessage);\n            return false;\n        }\n\n        return true;\n    }\n\n    /**\n     * Search for the first version found amongst other nodes.\n     *\n     * @return the first version encountered, if there are other nodes,\n     *         or empty optional if there are no other nodes in the cluster,\n     *         or throw exception if no version can be retrieved.\n     */\n    protected Optional<String> getVersionFromOtherNodes() {\n        try {\n            final Map<String, TaskResult<String>> nodeToVersionMap = broadcastService\n                    .executeOnOthers(new GetPlatformVersionFromNode()).get();\n            if (nodeToVersionMap.isEmpty()) {\n                // There are no other nodes in the cluster, so ok:\n                return Optional.empty();\n            }\n            for (Map.Entry<String, TaskResult<String>> nodeToVersion : nodeToVersionMap.entrySet()) {\n                final TaskResult<String> result = nodeToVersion.getValue();\n                if (result.isOk()) {\n                    LOGGER.info(\"Found that Bonita node '{}' is in version {}\", nodeToVersion.getKey(),\n                            result.getResult());\n                    return Optional.of(result.getResult());\n                }\n                // otherwise we check the next node, until finding a valid version\n            }\n        } catch (InterruptedException | ExecutionException ignored) {\n        }\n        final String msg = \"Cannot access other node version in the cluster\";\n        LOGGER.error(msg);\n        throw new RuntimeException(msg);\n    }\n\n    /**\n     * This method is duplicate in class VersionServiceImpl.\n     * This is accepted to limit over-engineering just to extract an util method.\n     */\n    private String extractMinorVersion(String version) {\n        final Matcher matcher = Pattern.compile(\"(\\\\d+\\\\.\\\\d+).*\").matcher(version);\n        if (matcher.matches()) {\n            return matcher.group(1);\n        } else {\n            throw new IllegalArgumentException(version + \" does not respect Semantic Versioning\");\n        }\n    }\n\n    public String getErrorMessage() {\n        return errorMessage;\n    }\n\n    static class GetPlatformVersionFromNode implements Callable<String>, Serializable {\n\n        @Override\n        public String call() throws Exception {\n            return ServiceAccessorFactory.getInstance().createServiceAccessor()\n                    .getPlatformService().getSPlatformProperties().getPlatformVersion();\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/platform/configuration/NodeConfiguration.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.configuration;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.PlatformRestartHandler;\n\n/**\n * This class allow to provide a configuration for the current node\n * We should be able to have one different per node\n *\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic interface NodeConfiguration {\n\n    /**\n     * Handlers called on restart of the platform\n     */\n    List<PlatformRestartHandler> getPlatformRestartHandlers();\n\n    /**\n     * @return\n     *         true if the sessions should be cleaned when the node is stopped\n     */\n    boolean shouldClearSessions();\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/platform/configuration/NodeConfigurationImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.configuration;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.CollectionUtil;\nimport org.bonitasoft.engine.commons.PlatformRestartHandler;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Laurent Vaills\n * @author Celine Souchet\n */\n@Component\n@ConditionalOnSingleCandidate(NodeConfiguration.class)\npublic class NodeConfigurationImpl implements NodeConfiguration {\n\n    private List<PlatformRestartHandler> platformRestartHandlers;\n\n    @Override\n    public List<PlatformRestartHandler> getPlatformRestartHandlers() {\n        return CollectionUtil.emptyOrUnmodifiable(platformRestartHandlers);\n    }\n\n    @Autowired(required = false)\n    public void setPlatformRestartHandlers(final List<PlatformRestartHandler> platformRestartHandlers) {\n        this.platformRestartHandlers = platformRestartHandlers;\n    }\n\n    @Override\n    public boolean shouldClearSessions() {\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/platform/configuration/datasource/QuartzConnectionProvider.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.configuration.datasource;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\n\nimport javax.sql.DataSource;\n\nimport org.quartz.utils.ConnectionProvider;\n\n/**\n * This is a hack to let Quartz access datasource beans from SprigContext\n * Quartz support custom connection providers but not non primitive parameters for them\n */\npublic class QuartzConnectionProvider implements ConnectionProvider {\n\n    private Boolean isXaDataSource;\n    private DataSource dataSource;\n\n    /**\n     * this is called by quartz (given by the configuration)\n     *\n     * @param isXaDataSource true if the datasource returned by this configuration provider should be the xa datasource\n     */\n    public void setXaDataSource(boolean isXaDataSource) {\n        this.isXaDataSource = isXaDataSource;\n    }\n\n    @Override\n    public Connection getConnection() throws SQLException {\n        return dataSource.getConnection();\n    }\n\n    @Override\n    public void shutdown() throws SQLException {\n\n    }\n\n    @Override\n    public void initialize() throws SQLException {\n        if (isXaDataSource == null) {\n            throw new IllegalStateException(\"Quartz datasource is not set\");\n        }\n        if (isXaDataSource) {\n            dataSource = QuartzDataSourceAccessorProvider.getInstance().getBonitaDataSource();\n        } else {\n            dataSource = QuartzDataSourceAccessorProvider.getInstance().getBonitaNonXaDataSource();\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/platform/configuration/datasource/QuartzDataSourceAccessor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.configuration.datasource;\n\nimport javax.sql.DataSource;\n\n/**\n * This is a hack to let Quartz access datasource beans from SprigContext\n * Quartz support custom connection providers but not non primitive parameters for them\n */\npublic class QuartzDataSourceAccessor {\n\n    private DataSource bonitaDataSource;\n    private DataSource bonitaNonXaDataSource;\n\n    public QuartzDataSourceAccessor(DataSource bonitaDataSource, DataSource bonitaNonXaDataSource) {\n        this.bonitaDataSource = bonitaDataSource;\n        this.bonitaNonXaDataSource = bonitaNonXaDataSource;\n    }\n\n    public DataSource getBonitaDataSource() {\n        return bonitaDataSource;\n    }\n\n    public DataSource getBonitaNonXaDataSource() {\n        return bonitaNonXaDataSource;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/platform/configuration/datasource/QuartzDataSourceAccessorProvider.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.configuration.datasource;\n\nimport javax.sql.DataSource;\n\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.stereotype.Component;\n\n/**\n * This is a hack to let Quartz access datasource beans from SprigContext\n * Quartz support custom connection providers but not non primitive parameters for them\n */\n@Component\npublic class QuartzDataSourceAccessorProvider {\n\n    private static QuartzDataSourceAccessor INSTANCE;\n\n    public QuartzDataSourceAccessorProvider(@Qualifier(\"bonitaDataSource\") DataSource bonitaDataSource,\n            @Qualifier(\"bonitaNonXaDataSource\") DataSource bonitaNonXaDataSource) {\n        INSTANCE = new QuartzDataSourceAccessor(bonitaDataSource, bonitaNonXaDataSource);\n    }\n\n    public static QuartzDataSourceAccessor getInstance() {\n        return INSTANCE;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/profile/DefaultProfilesUpdater.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.List;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.api.ImportStatus;\nimport org.bonitasoft.engine.commons.TenantLifecycleService;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.io.IOUtil;\nimport org.bonitasoft.engine.exception.ExecutionException;\nimport org.bonitasoft.engine.profile.xml.ProfilesNode;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.stereotype.Component;\n\n/**\n * Update provided profiles from profiles.xml from classpath\n */\n@Slf4j\n@Component\n//must be initialized before page and application import to ensure applications are mapped to profile\n@Order(3)\npublic class DefaultProfilesUpdater implements TenantLifecycleService {\n\n    private final ProfilesImporter profilesImporter;\n\n    public DefaultProfilesUpdater(ProfilesImporter profilesImporter) {\n        this.profilesImporter = profilesImporter;\n    }\n\n    @Override\n    public void init() throws SBonitaException {\n        execute();\n    }\n\n    /**\n     * Executes a default profile update\n     *\n     * @return whether the default profiles where updated\n     * @throws RuntimeException if execution fails\n     */\n    public boolean execute() {\n        try {\n            final File md5File = getProfilesMD5File();\n            final String defaultProfilesXml = getDefaultProfilesXml();\n            if (shouldUpdateProfiles(md5File, defaultProfilesXml)) {\n                // Default profiles do not exist or are different\n                log.info(\n                        \"Default profiles not up to date, updating them...\");\n                final ProfilesNode defaultProfiles = getProfilesFromXML(defaultProfilesXml);\n                doUpdateProfiles(defaultProfiles, md5File, defaultProfilesXml);\n                return true;\n            } else {\n                // No update required\n                log.info(\n                        \"Default profiles are up to date\");\n                return false;\n            }\n\n        } catch (IOException e) {\n            log.error(\n                    \"Unable to read the read the default profile file to update them\", e);\n        } catch (Exception e) {\n            log.error(\n                    \"Unable to update default profiles\", e);\n        }\n        return false;\n    }\n\n    void doUpdateProfiles(final ProfilesNode defaultProfiles, final File md5File, final String defaultProfilesXml)\n            throws NoSuchAlgorithmException, IOException {\n        try {\n            final List<ImportStatus> importStatuses = profilesImporter.importProfiles(defaultProfiles,\n                    ImportPolicy.UPDATE_DEFAULTS, SessionService.SYSTEM_ID);\n            log.info(\"Updated default profiles {}\", importStatuses);\n            if (md5File != null) { // but may not exist\n                IOUtil.writeMD5(md5File, defaultProfilesXml.getBytes());\n            }\n        } catch (ExecutionException e) {\n            log.error(\"Unable to update default profiles\", e);\n        }\n    }\n\n    File getProfilesMD5File() throws IOException {\n        return ProfilesImporter.getFileContainingMD5();\n    }\n\n    /**\n     * Checks if profiles should be updated: if MD5 file differs from MD5 of XML file\n     *\n     * @return true if profiles should be updated\n     */\n    boolean shouldUpdateProfiles(final File md5File, final String defaultProfilesXml) throws NoSuchAlgorithmException {\n        return md5File == null || !IOUtil.checkMD5(md5File, defaultProfilesXml.getBytes());\n    }\n\n    /**\n     * @return content of the XML file that contains default profiles\n     * @throws IOException in case of problem reading profiles file\n     */\n    String getDefaultProfilesXml() throws IOException {\n        String profiles = IOUtil.readResource(\"profiles-sp.xml\");\n        if (profiles != null) {\n            log.info(\"Loading profiles from file profiles-sp.xml\");\n        } else {\n            profiles = IOUtil.readResource(\"profiles.xml\");\n            log.info(\"Loading profiles from file profiles.xml\");\n        }\n        return profiles;\n    }\n\n    ProfilesNode getProfilesFromXML(final String defaultProfilesXml) throws IOException {\n        return profilesImporter.convertFromXml(defaultProfilesXml);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/profile/DeleteExistingImportStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.exception.ExecutionException;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.profile.model.SProfile;\nimport org.bonitasoft.engine.profile.xml.ProfileNode;\n\n/**\n * @author Baptiste Mesta\n */\npublic class DeleteExistingImportStrategy extends ProfileImportStrategy {\n\n    public DeleteExistingImportStrategy(final ProfileService profileService) {\n        super(profileService);\n    }\n\n    @Override\n    public void beforeImport() throws ExecutionException {\n        final QueryOptions queryOptions = new QueryOptions(0, 100,\n                Collections.singletonList(new OrderByOption(SProfile.class,\n                        SProfile.NAME, OrderByType.ASC)),\n                Collections.<FilterOption> emptyList(), null);\n        // delete all profiles\n        try {\n            List<SProfile> profiles;\n            do {\n                profiles = getProfileService().searchProfiles(queryOptions);\n\n                for (final SProfile sProfile : profiles) {\n                    getProfileService().deleteProfile(sProfile);\n                }\n            } while (!profiles.isEmpty());\n        } catch (final SBonitaException e) {\n            throw new ExecutionException(e);\n        }\n    }\n\n    @Override\n    public SProfile whenProfileExists(final long importerId, final ProfileNode profile,\n            final SProfile existingProfile) {\n        // nothing to do because we deleted all profiles\n        return null;\n    }\n\n    @Override\n    public boolean canCreateProfileIfNotExists(final ProfileNode profile) {\n        return true;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/profile/FailOnDuplicateImportStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile;\n\nimport org.bonitasoft.engine.exception.ExecutionException;\nimport org.bonitasoft.engine.profile.model.SProfile;\nimport org.bonitasoft.engine.profile.xml.ProfileNode;\n\n/**\n * @author Baptiste Mesta\n */\npublic class FailOnDuplicateImportStrategy extends ProfileImportStrategy {\n\n    public FailOnDuplicateImportStrategy(final ProfileService profileService) {\n        super(profileService);\n    }\n\n    @Override\n    public void beforeImport() {\n\n    }\n\n    @Override\n    public SProfile whenProfileExists(final long importerId, final ProfileNode profile, final SProfile existingProfile)\n            throws ExecutionException {\n        throw new ExecutionException(\"There's a same name profile when import a profile named \" + profile.getName());\n    }\n\n    @Override\n    public boolean canCreateProfileIfNotExists(final ProfileNode profile) {\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/profile/IgnoreDuplicateImportStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile;\n\nimport org.bonitasoft.engine.profile.model.SProfile;\nimport org.bonitasoft.engine.profile.xml.ProfileNode;\n\n/**\n * @author Baptiste Mesta\n */\npublic class IgnoreDuplicateImportStrategy extends ProfileImportStrategy {\n\n    public IgnoreDuplicateImportStrategy(final ProfileService profileService) {\n        super(profileService);\n    }\n\n    @Override\n    public void beforeImport() {\n\n    }\n\n    @Override\n    public SProfile whenProfileExists(final long importerId, final ProfileNode profile,\n            final SProfile existingProfile) {\n        // will be skipped\n        return null;\n    }\n\n    @Override\n    public boolean canCreateProfileIfNotExists(final ProfileNode profile) {\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/profile/ImportPolicy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile;\n\n/**\n * @author Zhao Na\n * @author Matthieu Chaffotte\n * @author Emmanuel Duchastenier\n */\npublic enum ImportPolicy {\n\n    DELETE_EXISTING, REPLACE_DUPLICATES, FAIL_ON_DUPLICATES, IGNORE_DUPLICATES, UPDATE_DEFAULTS, UPDATE_DEFAULTS_AND_CREATE_NEW\n    // , MERGE_DUPLICATES (Merge should retain existing IDs)\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/profile/ProfileImportStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile;\n\nimport org.bonitasoft.engine.exception.ExecutionException;\nimport org.bonitasoft.engine.profile.exception.profile.SProfileUpdateException;\nimport org.bonitasoft.engine.profile.exception.profilemember.SProfileMemberDeletionException;\nimport org.bonitasoft.engine.profile.model.SProfile;\nimport org.bonitasoft.engine.profile.xml.ProfileNode;\n\n/**\n * @author Baptiste Mesta\n */\npublic abstract class ProfileImportStrategy {\n\n    private final ProfileService profileService;\n\n    public ProfileImportStrategy(final ProfileService profileService) {\n        this.profileService = profileService;\n    }\n\n    /**\n     * what to do before the import\n     */\n    public abstract void beforeImport() throws ExecutionException;\n\n    /**\n     * return the imported version of the profile\n     */\n    public abstract SProfile whenProfileExists(final long importerId,\n            final ProfileNode profile,\n            final SProfile existingProfile)\n            throws ExecutionException, SProfileMemberDeletionException, SProfileUpdateException;\n\n    /**\n     * return whether the profile can be created if it does not exist\n     */\n    public abstract boolean canCreateProfileIfNotExists(ProfileNode profile);\n\n    /**\n     * convert {@link ProfileNode} to {@link SProfile}\n     *\n     * @return the profile service\n     */\n\n    protected ProfileService getProfileService() {\n        return profileService;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/profile/ProfilesExporter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport javax.xml.bind.JAXBException;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.exception.ExecutionException;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.SGroupNotFoundException;\nimport org.bonitasoft.engine.identity.SRoleNotFoundException;\nimport org.bonitasoft.engine.identity.SUserNotFoundException;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.profile.model.SProfile;\nimport org.bonitasoft.engine.profile.model.SProfileMember;\nimport org.bonitasoft.engine.profile.xml.MembershipNode;\nimport org.bonitasoft.engine.profile.xml.ProfileMappingNode;\nimport org.bonitasoft.engine.profile.xml.ProfileNode;\nimport org.bonitasoft.engine.profile.xml.ProfilesNode;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Baptiste Mesta\n */\n@Component\npublic class ProfilesExporter {\n\n    private final static String USER_SUFFIX = \"ForUser\";\n    private final static String ROLE_SUFFIX = \"ForRole\";\n    private final static String GROUP_SUFFIX = \"ForGroup\";\n    private final static String ROLE_AND_GROUP_SUFFIX = \"ForRoleAndGroup\";\n    private static final int NUMBER_OF_RESULTS = 100;\n    private IdentityService identityService;\n    private ProfileService profileService;\n    private ProfilesParser profilesParser;\n\n    public ProfilesExporter(IdentityService identityService, ProfileService profileService,\n            ProfilesParser profilesParser) {\n        this.identityService = identityService;\n        this.profileService = profileService;\n        this.profilesParser = profilesParser;\n    }\n\n    public String exportAllProfiles() throws ExecutionException {\n        try {\n            ArrayList<SProfile> sProfiles = getAllProfiles();\n            ProfilesNode profiles = toProfiles(sProfiles);\n            return profilesParser.convert(profiles);\n        } catch (SBonitaException | JAXBException e) {\n            throw new ExecutionException(e);\n        }\n    }\n\n    private ArrayList<SProfile> getAllProfiles() throws SBonitaReadException {\n        ArrayList<SProfile> profiles = new ArrayList<>();\n        int from = 0;\n        List<SProfile> sProfiles;\n        do {\n            final QueryOptions queryOptions = new QueryOptions(from, 100, Collections.singletonList(new OrderByOption(\n                    SProfile.class, SProfile.NAME, OrderByType.ASC)), Collections.<FilterOption> emptyList(), null);\n            sProfiles = profileService.searchProfiles(queryOptions);\n            from += 100;\n            profiles.addAll(sProfiles);\n        } while (sProfiles.size() == 100);\n        return profiles;\n    }\n\n    public String exportProfiles(List<Long> longs) throws ExecutionException {\n        try {\n            List<SProfile> sProfile = profileService.getProfiles(longs);\n            ProfilesNode profiles = toProfiles(sProfile);\n            return profilesParser.convert(profiles);\n        } catch (SBonitaException | JAXBException e) {\n            throw new ExecutionException(e);\n        }\n    }\n\n    ProfilesNode toProfiles(List<SProfile> sProfile)\n            throws SBonitaReadException, SUserNotFoundException, SGroupNotFoundException, SRoleNotFoundException {\n        ArrayList<ProfileNode> profiles = new ArrayList<>();\n        for (SProfile profile : sProfile) {\n            profiles.add(toProfile(profile));\n        }\n        return new ProfilesNode(profiles);\n    }\n\n    private ProfileNode toProfile(SProfile sProfile)\n            throws SBonitaReadException, SUserNotFoundException, SGroupNotFoundException, SRoleNotFoundException {\n        ProfileNode profile = new ProfileNode(sProfile.getName(), sProfile.isDefault());\n        profile.setDescription(sProfile.getDescription());\n        profile.setProfileMapping(getProfileMapping(sProfile));\n        return profile;\n    }\n\n    private ProfileMappingNode getProfileMapping(SProfile sProfile)\n            throws SUserNotFoundException, SBonitaReadException, SGroupNotFoundException, SRoleNotFoundException {\n        ProfileMappingNode profileMapping = new ProfileMappingNode();\n        profileMapping.setUsers(getUsers(sProfile));\n        profileMapping.setGroups(getGroups(sProfile));\n        profileMapping.setRoles(getRoles(sProfile));\n        profileMapping.setMemberships(getMemberships(sProfile));\n        return profileMapping;\n    }\n\n    private List<String> getUsers(SProfile profile) throws SBonitaReadException, SUserNotFoundException {\n        ArrayList<String> users = new ArrayList<>();\n        int pageIndex = 0;\n        List<SProfileMember> sProfileMembers;\n        do {\n            sProfileMembers = searchProfileMembers(pageIndex, profile.getId(), USER_SUFFIX);\n            for (final SProfileMember sProfileMember : sProfileMembers) {\n                users.add(identityService.getUser(sProfileMember.getUserId()).getUserName());\n            }\n            pageIndex++;\n        } while (sProfileMembers.size() > 0);\n        return users;\n    }\n\n    private List<String> getGroups(SProfile profile) throws SBonitaReadException, SGroupNotFoundException {\n        ArrayList<String> groups = new ArrayList<>();\n        int pageIndex = 0;\n        List<SProfileMember> sProfileMembers;\n        do {\n            sProfileMembers = searchProfileMembers(pageIndex, profile.getId(), GROUP_SUFFIX);\n            for (final SProfileMember sProfileMember : sProfileMembers) {\n                groups.add(identityService.getGroup(sProfileMember.getGroupId()).getPath());\n            }\n            pageIndex++;\n        } while (sProfileMembers.size() > 0);\n        return groups;\n    }\n\n    private List<String> getRoles(SProfile profile) throws SBonitaReadException, SRoleNotFoundException {\n        ArrayList<String> roles = new ArrayList<>();\n        int pageIndex = 0;\n        List<SProfileMember> sProfileMembers;\n        do {\n            sProfileMembers = searchProfileMembers(pageIndex, profile.getId(), ROLE_SUFFIX);\n            for (final SProfileMember sProfileMember : sProfileMembers) {\n                roles.add(identityService.getRole(sProfileMember.getRoleId()).getName());\n            }\n            pageIndex++;\n        } while (sProfileMembers.size() > 0);\n        return roles;\n    }\n\n    private List<MembershipNode> getMemberships(SProfile profile)\n            throws SBonitaReadException, SRoleNotFoundException, SGroupNotFoundException {\n        ArrayList<MembershipNode> memberships = new ArrayList<>();\n        int pageIndex = 0;\n        List<SProfileMember> sProfileMembers;\n        do {\n            sProfileMembers = searchProfileMembers(pageIndex, profile.getId(), ROLE_AND_GROUP_SUFFIX);\n            for (final SProfileMember sProfileMember : sProfileMembers) {\n                memberships.add(new MembershipNode(identityService.getGroup(sProfileMember.getGroupId()).getPath(),\n                        identityService.getRole(sProfileMember.getRoleId()).getName()));\n            }\n            pageIndex++;\n        } while (sProfileMembers.size() > 0);\n        return memberships;\n    }\n\n    private List<SProfileMember> searchProfileMembers(final int fromIndex, final long profileId,\n            final String querySuffix) throws SBonitaReadException {\n        final QueryOptions queryOptions = new QueryOptions(fromIndex * NUMBER_OF_RESULTS, NUMBER_OF_RESULTS,\n                Collections.singletonList(new OrderByOption(\n                        SProfileMember.class, SProfileMember.ID, OrderByType.ASC)),\n                Collections.singletonList(new FilterOption(SProfileMember.class,\n                        SProfileMember.PROFILE_ID, profileId)),\n                null);\n        return profileService.searchProfileMembers(querySuffix, queryOptions);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/profile/ProfilesImporter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport javax.xml.bind.JAXBException;\n\nimport org.bonitasoft.engine.api.ImportError;\nimport org.bonitasoft.engine.api.ImportError.Type;\nimport org.bonitasoft.engine.api.ImportStatus;\nimport org.bonitasoft.engine.api.ImportStatus.Status;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.exception.ExecutionException;\nimport org.bonitasoft.engine.home.BonitaHomeServer;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.SGroupNotFoundException;\nimport org.bonitasoft.engine.identity.SRoleNotFoundException;\nimport org.bonitasoft.engine.identity.SUserNotFoundException;\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.identity.model.SRole;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.profile.exception.profile.SProfileCreationException;\nimport org.bonitasoft.engine.profile.exception.profile.SProfileNotFoundException;\nimport org.bonitasoft.engine.profile.exception.profile.SProfileUpdateException;\nimport org.bonitasoft.engine.profile.exception.profilemember.SProfileMemberCreationException;\nimport org.bonitasoft.engine.profile.exception.profilemember.SProfileMemberDeletionException;\nimport org.bonitasoft.engine.profile.model.SProfile;\nimport org.bonitasoft.engine.profile.xml.MembershipNode;\nimport org.bonitasoft.engine.profile.xml.ProfileMappingNode;\nimport org.bonitasoft.engine.profile.xml.ProfileNode;\nimport org.bonitasoft.engine.profile.xml.ProfilesNode;\nimport org.springframework.stereotype.Component;\n\n/**\n * Import profiles with mapping using Policy\n *\n * @author Baptiste Mesta\n */\n@Component\npublic class ProfilesImporter {\n\n    private final ProfileService profileService;\n    private final IdentityService identityService;\n    private final ProfilesParser profilesParser;\n\n    public ProfilesImporter(final ProfileService profileService, final IdentityService identityService,\n            ProfilesParser profilesParser) {\n        this.profileService = profileService;\n        this.identityService = identityService;\n        this.profilesParser = profilesParser;\n    }\n\n    private static ProfileImportStrategy getStrategy(final ProfileService profileService, final ImportPolicy policy) {\n        return switch (policy) {\n            case DELETE_EXISTING -> new DeleteExistingImportStrategy(profileService);\n            case FAIL_ON_DUPLICATES -> new FailOnDuplicateImportStrategy(profileService);\n            case IGNORE_DUPLICATES -> new IgnoreDuplicateImportStrategy(profileService);\n            case REPLACE_DUPLICATES -> new ReplaceDuplicateImportStrategy(profileService);\n            case UPDATE_DEFAULTS -> new UpdateDefaultsImportStrategy(profileService);\n            case UPDATE_DEFAULTS_AND_CREATE_NEW -> new UpdateDefaultsAndCreateNewImportStrategy(profileService);\n        };\n    }\n\n    public List<ImportStatus> importProfiles(ProfilesNode profiles, ImportPolicy policy, final long importerId)\n            throws ExecutionException {\n        ProfileImportStrategy importStrategy = getStrategy(profileService, policy);\n        importStrategy.beforeImport();\n        try {\n            final List<ImportStatus> importStatus = new ArrayList<>(profiles.getProfiles().size());\n            for (final ProfileNode profile : profiles.getProfiles()) {\n                if (profile.getName() == null || profile.getName().isEmpty()) {\n                    continue;\n                }\n                final ImportStatus currentStatus = new ImportStatus(profile.getName());\n                importStatus.add(currentStatus);\n                SProfile existingProfile = null;\n\n                try {\n                    existingProfile = profileService.getProfileByName(profile.getName());\n                    currentStatus.setStatus(Status.REPLACED);\n                } catch (final SProfileNotFoundException e1) {\n                    // profile does not exists\n                }\n                final SProfile newProfile = importTheProfile(importerId, profile, existingProfile, importStrategy);\n                if (newProfile == null) {\n                    // in case of skip\n                    currentStatus.setStatus(Status.SKIPPED);\n                    continue;\n                }\n\n                final long profileId = newProfile.getId();\n\n                /*\n                 * Import mapping with organization\n                 */\n                ProfileMappingNode profileMapping = profile.getProfileMapping();\n                if (profileMapping != null) {\n                    currentStatus.getErrors()\n                            .addAll(importProfileMapping(profileService, identityService, profileId, profileMapping));\n                }\n            }\n            return importStatus;\n\n        } catch (final SBonitaException e) {\n            throw new ExecutionException(e);\n        }\n    }\n\n    List<ImportError> importProfileMapping(final ProfileService profileService, final IdentityService identityService,\n            final long profileId,\n            final ProfileMappingNode profileMapping) throws SProfileMemberCreationException {\n        final ArrayList<ImportError> errors = new ArrayList<>();\n\n        for (final String userName : profileMapping.getUsers()) {\n            SUser user = null;\n            try {\n                user = identityService.getUserByUserName(userName);\n            } catch (final SUserNotFoundException e) {\n                errors.add(new ImportError(userName, Type.USER));\n                continue;\n            }\n            profileService.addUserToProfile(profileId, user.getId(), user.getFirstName(), user.getLastName(),\n                    user.getUserName());\n        }\n        for (final String groupPath : profileMapping.getGroups()) {\n            SGroup group = null;\n            try {\n                group = identityService.getGroupByPath(groupPath);\n            } catch (final SGroupNotFoundException e) {\n                errors.add(new ImportError(groupPath, Type.GROUP));\n                continue;\n            }\n            profileService.addGroupToProfile(profileId, group.getId(), group.getName(), group.getParentPath());\n        }\n        for (final String roleName : profileMapping.getRoles()) {\n            SRole role = null;\n            try {\n                role = identityService.getRoleByName(roleName);\n            } catch (final SRoleNotFoundException e) {\n                errors.add(new ImportError(roleName, Type.ROLE));\n                continue;\n            }\n            profileService.addRoleToProfile(profileId, role.getId(), role.getName());\n        }\n\n        for (final MembershipNode membership : profileMapping.getMemberships()) {\n            SGroup group = null;\n            try {\n                group = identityService.getGroupByPath(membership.getGroup());\n            } catch (final SGroupNotFoundException e) {\n                errors.add(new ImportError(membership.getGroup(), Type.GROUP));\n            }\n            SRole role = null;\n            try {\n                role = identityService.getRoleByName(membership.getRole());\n            } catch (final SRoleNotFoundException e) {\n                errors.add(new ImportError(membership.getRole(), Type.ROLE));\n            }\n            if (group == null || role == null) {\n                continue;\n            }\n            profileService.addRoleAndGroupToProfile(profileId, role.getId(), group.getId(), role.getName(),\n                    group.getName(), group.getParentPath());\n        }\n        return errors;\n    }\n\n    protected SProfile importTheProfile(final long importerId, final ProfileNode profile,\n            final SProfile existingProfile, ProfileImportStrategy importStrategy)\n            throws ExecutionException, SProfileMemberDeletionException,\n            SProfileUpdateException, SProfileCreationException {\n        final SProfile newProfile;\n        if (existingProfile != null) {\n            newProfile = importStrategy.whenProfileExists(importerId, profile, existingProfile);\n        } else if (importStrategy.canCreateProfileIfNotExists(profile)) {\n            newProfile = profileService.createProfile(createSProfile(profile, importerId));\n        } else {\n            newProfile = null;\n        }\n        return newProfile;\n    }\n\n    SProfile createSProfile(final ProfileNode profileNode, final long importerId) {\n        final boolean isDefault = profileNode.isDefault();\n        final long creationDate = System.currentTimeMillis();\n        return SProfile.builder()\n                .name(profileNode.getName())\n                .isDefault(isDefault)\n                .creationDate(creationDate)\n                .createdBy(importerId)\n                .lastUpdateDate(creationDate)\n                .lastUpdatedBy(importerId).description(profileNode.getDescription()).build();\n    }\n\n    public ProfilesNode convertFromXml(final String xmlContent) throws IOException {\n        try {\n            return profilesParser.convert(xmlContent);\n        } catch (JAXBException e) {\n            throw new IOException(e);\n        }\n    }\n\n    static File getFileContainingMD5() throws IOException {\n        return BonitaHomeServer.getInstance().getProfileStorage().getProfileMD5();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/profile/ReplaceDuplicateImportStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile;\n\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.profile.builder.SProfileUpdateBuilder;\nimport org.bonitasoft.engine.profile.builder.SProfileUpdateBuilderFactory;\nimport org.bonitasoft.engine.profile.exception.profile.SProfileUpdateException;\nimport org.bonitasoft.engine.profile.exception.profilemember.SProfileMemberDeletionException;\nimport org.bonitasoft.engine.profile.model.SProfile;\nimport org.bonitasoft.engine.profile.xml.ProfileNode;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Baptiste Mesta\n */\npublic class ReplaceDuplicateImportStrategy extends ProfileImportStrategy {\n\n    public ReplaceDuplicateImportStrategy(final ProfileService profileService) {\n        super(profileService);\n    }\n\n    @Override\n    public void beforeImport() {\n\n    }\n\n    @Override\n    public SProfile whenProfileExists(final long importerId, final ProfileNode profile, final SProfile existingProfile)\n            throws SProfileMemberDeletionException, SProfileUpdateException {\n        getProfileService().deleteAllProfileMembersOfProfile(existingProfile);\n        // update profile\n        if (profile.isDefault() || existingProfile.isDefault()) {\n            // only update LastUpdatedBy and LastUpdateDate\n            return getProfileService().updateProfile(existingProfile,\n                    getProfileUpdateDescriptor(profile, importerId, false));\n        } else {\n            return getProfileService().updateProfile(existingProfile,\n                    getProfileUpdateDescriptor(profile, importerId, true));\n        }\n    }\n\n    @Override\n    public boolean canCreateProfileIfNotExists(final ProfileNode profile) {\n        return !profile.isDefault();\n    }\n\n    EntityUpdateDescriptor getProfileUpdateDescriptor(final ProfileNode profile, final long importerId,\n            final boolean updateAllProfile) {\n        final SProfileUpdateBuilder updateBuilder = BuilderFactory.get(SProfileUpdateBuilderFactory.class)\n                .createNewInstance();\n        updateBuilder.setLastUpdateDate(System.currentTimeMillis()).setLastUpdatedBy(importerId);\n        if (updateAllProfile) {\n            updateBuilder.setDescription(profile.getDescription());\n        }\n        return updateBuilder.done();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/profile/UpdateDefaultsAndCreateNewImportStrategy.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile;\n\nimport org.bonitasoft.engine.profile.exception.profile.SProfileUpdateException;\nimport org.bonitasoft.engine.profile.model.SProfile;\nimport org.bonitasoft.engine.profile.xml.ProfileNode;\n\npublic class UpdateDefaultsAndCreateNewImportStrategy extends UpdateDefaultsImportStrategy {\n\n    public UpdateDefaultsAndCreateNewImportStrategy(final ProfileService profileService) {\n        super(profileService);\n    }\n\n    @Override\n    public SProfile whenProfileExists(final long importerId, final ProfileNode profile, final SProfile existingProfile)\n            throws SProfileUpdateException {\n        if (profile.isDefault() || existingProfile.isDefault()) {\n            // only update LastUpdatedBy and LastUpdateDate\n            return getProfileService().updateProfile(existingProfile,\n                    getProfileUpdateDescriptor(profile, importerId, true));\n        } else {\n            throw new SProfileUpdateException(\"A profile already exists with name: \" + profile.getName());\n        }\n    }\n\n    @Override\n    public boolean canCreateProfileIfNotExists(final ProfileNode profile) {\n        return true;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/profile/UpdateDefaultsImportStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile;\n\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.profile.builder.SProfileUpdateBuilder;\nimport org.bonitasoft.engine.profile.builder.SProfileUpdateBuilderFactory;\nimport org.bonitasoft.engine.profile.exception.profile.SProfileUpdateException;\nimport org.bonitasoft.engine.profile.model.SProfile;\nimport org.bonitasoft.engine.profile.xml.ProfileNode;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Baptiste Mesta\n */\npublic class UpdateDefaultsImportStrategy extends ProfileImportStrategy {\n\n    public UpdateDefaultsImportStrategy(final ProfileService profileService) {\n        super(profileService);\n    }\n\n    @Override\n    public void beforeImport() {\n\n    }\n\n    @Override\n    public SProfile whenProfileExists(final long importerId, final ProfileNode profile, final SProfile existingProfile)\n            throws SProfileUpdateException {\n        // update profile\n        if (profile.isDefault() || existingProfile.isDefault()) {\n            // only update LastUpdatedBy and LastUpdateDate\n            return getProfileService().updateProfile(existingProfile,\n                    getProfileUpdateDescriptor(profile, importerId, true));\n        } else {\n            return null;\n        }\n    }\n\n    @Override\n    public boolean canCreateProfileIfNotExists(final ProfileNode profile) {\n        return profile.isDefault();\n    }\n\n    EntityUpdateDescriptor getProfileUpdateDescriptor(final ProfileNode profile, final long importerId,\n            final boolean updateAllProfile) {\n        final SProfileUpdateBuilder updateBuilder = BuilderFactory.get(SProfileUpdateBuilderFactory.class)\n                .createNewInstance();\n        updateBuilder.setLastUpdateDate(System.currentTimeMillis()).setLastUpdatedBy(importerId);\n        if (updateAllProfile) {\n            updateBuilder.setDescription(profile.getDescription());\n        }\n        return updateBuilder.done();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractActivityInstanceSearchEntity.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance;\nimport org.bonitasoft.engine.execution.state.FlowNodeStateManager;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\nimport org.bonitasoft.engine.search.impl.SearchFilter;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Yanyan Liu\n * @author Celine Souchet\n */\npublic abstract class AbstractActivityInstanceSearchEntity\n        extends AbstractSearchEntity<ActivityInstance, SActivityInstance> {\n\n    private final FlowNodeStateManager flowNodeStateManager;\n\n    private final Class<? extends PersistentObject> entityClass;\n\n    public AbstractActivityInstanceSearchEntity(final SearchEntityDescriptor searchDescriptor,\n            final SearchOptions options,\n            final FlowNodeStateManager flowNodeStateManager) throws SBonitaReadException {\n        super(searchDescriptor, options);\n        this.flowNodeStateManager = flowNodeStateManager;\n        entityClass = getEntityClass(options);\n    }\n\n    @Override\n    public List<ActivityInstance> convertToClientObjects(final List<SActivityInstance> serverObjects) {\n        // invoke this method to get to different typed client object according to the server type\n        return ModelConvertor.toActivityInstances(serverObjects, flowNodeStateManager);\n    }\n\n    private Class<? extends PersistentObject> getEntityClass(final SearchOptions searchOptions)\n            throws SBonitaReadException {\n        Class<? extends PersistentObject> entityClass = SActivityInstance.class;\n        final SearchFilter searchFilter = getSearchFilter(searchOptions,\n                ActivityInstanceSearchDescriptor.ACTIVITY_TYPE);\n        if (searchFilter != null) {\n            final FlowNodeType activityType = (FlowNodeType) searchFilter.getValue();\n            if (activityType != null) {\n                switch (activityType) {\n                    case AUTOMATIC_TASK:\n                        entityClass = SAutomaticTaskInstance.class;\n                        break;\n                    case MANUAL_TASK:\n                        entityClass = SManualTaskInstance.class;\n                        break;\n                    case USER_TASK:\n                        entityClass = SUserTaskInstance.class;\n                        break;\n                    case HUMAN_TASK:\n                        entityClass = SHumanTaskInstance.class;\n                        break;\n                    case RECEIVE_TASK:\n                        entityClass = SReceiveTaskInstance.class;\n                        break;\n                    case SEND_TASK:\n                        entityClass = SSendTaskInstance.class;\n                        break;\n                    case CALL_ACTIVITY:\n                        entityClass = SCallActivityInstance.class;\n                        break;\n                    case MULTI_INSTANCE_ACTIVITY:\n                        entityClass = SMultiInstanceActivityInstance.class;\n                        break;\n                    case SUB_PROCESS:\n                        entityClass = SSubProcessActivityInstance.class;\n                        break;\n                    case LOOP_ACTIVITY:\n                        entityClass = SLoopActivityInstance.class;\n                        break;\n                    default:\n                        throw new SBonitaReadException(\"You're searching for a \" + activityType.name()\n                                + \" which is not an activity type, using the ActivityInstanceSearch feature. Ensure you are using the correct search method for your needs.\");\n                }\n                searchOptions.getFilters().remove(searchFilter);\n            }\n        }\n        return entityClass;\n    }\n\n    @Override\n    protected void validateQuery(final SearchOptions searchOptions) throws SBonitaReadException {\n        validateActivityTypeFilterUnicity(searchOptions);\n    }\n\n    private void validateActivityTypeFilterUnicity(SearchOptions searchOptions) throws SBonitaReadException {\n        for (final SearchFilter searchFilter : searchOptions.getFilters()) {\n            if (ActivityInstanceSearchDescriptor.ACTIVITY_TYPE.equals(searchFilter.getField())) {\n                throw new SBonitaReadException(\n                        \"Invalid query, filtering several times on 'ActivityInstanceSearchDescriptor.ACTIVITY_TYPE' is not supported.\");\n            }\n        }\n    }\n\n    protected Class<? extends PersistentObject> getEntityClass() {\n        return entityClass;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractArchiveActivityInstanceSearchEntity.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAAutomaticTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SACallActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SALoopActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAManualTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAMultiInstanceActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAReceiveTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SASendTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SASubProcessActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance;\nimport org.bonitasoft.engine.execution.state.FlowNodeStateManager;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.search.descriptor.SearchArchivedActivityInstanceDescriptor;\nimport org.bonitasoft.engine.search.impl.SearchFilter;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Yanyan Liu\n * @author Celine Souchet\n */\npublic abstract class AbstractArchiveActivityInstanceSearchEntity\n        extends AbstractSearchEntity<ArchivedActivityInstance, SAActivityInstance> {\n\n    private final FlowNodeStateManager flowNodeStateManager;\n\n    private final Class<? extends PersistentObject> entityClass;\n\n    public AbstractArchiveActivityInstanceSearchEntity(final SearchArchivedActivityInstanceDescriptor searchDescriptor,\n            final SearchOptions searchOptions,\n            final FlowNodeStateManager flowNodeStateManager) {\n        super(searchDescriptor, searchOptions);\n        this.flowNodeStateManager = flowNodeStateManager;\n        entityClass = getEntityClass(searchOptions);\n    }\n\n    @Override\n    public List<ArchivedActivityInstance> convertToClientObjects(final List<SAActivityInstance> serverObjects) {\n        return ModelConvertor.toArchivedActivityInstances(serverObjects, flowNodeStateManager);\n    }\n\n    protected Class<? extends PersistentObject> getEntityClass(final SearchOptions searchOptions) {\n        Class<? extends PersistentObject> entityClass = SAActivityInstance.class;\n        final SearchFilter searchFilter = getSearchFilter(searchOptions,\n                ArchivedActivityInstanceSearchDescriptor.ACTIVITY_TYPE);\n        if (searchFilter != null) {\n            final FlowNodeType activityType = (FlowNodeType) searchFilter.getValue();\n            if (activityType != null) {\n                switch (activityType) {\n                    case AUTOMATIC_TASK:\n                        entityClass = SAAutomaticTaskInstance.class;\n                        break;\n                    case MANUAL_TASK:\n                        entityClass = SAManualTaskInstance.class;\n                        break;\n                    case USER_TASK:\n                        entityClass = SAUserTaskInstance.class;\n                        break;\n                    case HUMAN_TASK:\n                        entityClass = SAHumanTaskInstance.class;\n                        break;\n                    case RECEIVE_TASK:\n                        entityClass = SAReceiveTaskInstance.class;\n                        break;\n                    case SEND_TASK:\n                        entityClass = SASendTaskInstance.class;\n                        break;\n                    case CALL_ACTIVITY:\n                        entityClass = SACallActivityInstance.class;\n                        break;\n                    case LOOP_ACTIVITY:\n                        entityClass = SALoopActivityInstance.class;\n                        break;\n                    case MULTI_INSTANCE_ACTIVITY:\n                        entityClass = SAMultiInstanceActivityInstance.class;\n                        break;\n                    case SUB_PROCESS:\n                        entityClass = SASubProcessActivityInstance.class;\n                        break;\n                    default:\n                        entityClass = SAActivityInstance.class;\n                        break;\n                }\n                searchOptions.getFilters().remove(searchFilter);\n            }\n        }\n        return entityClass;\n    }\n\n    protected Class<? extends PersistentObject> getEntityClass() {\n        return entityClass;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractArchivedCommentsSearchEntity.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.comment.ArchivedComment;\nimport org.bonitasoft.engine.core.process.comment.model.archive.SAComment;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Hongwen Zang\n */\npublic abstract class AbstractArchivedCommentsSearchEntity extends AbstractSearchEntity<ArchivedComment, SAComment> {\n\n    public AbstractArchivedCommentsSearchEntity(final SearchEntityDescriptor searchDescriptor,\n            final SearchOptions options) {\n        super(searchDescriptor, options);\n    }\n\n    @Override\n    public List<ArchivedComment> convertToClientObjects(final List<SAComment> serverObjects) {\n        return ModelConvertor.toArchivedComments(serverObjects);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractArchivedConnectorInstanceSearchEntity.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.connector.ArchivedConnectorInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAConnectorInstance;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Baptiste Mesta\n */\npublic abstract class AbstractArchivedConnectorInstanceSearchEntity\n        extends AbstractSearchEntity<ArchivedConnectorInstance, SAConnectorInstance> {\n\n    public AbstractArchivedConnectorInstanceSearchEntity(final SearchEntityDescriptor searchDescriptor,\n            final SearchOptions options) {\n        super(searchDescriptor, options);\n    }\n\n    @Override\n    public List<ArchivedConnectorInstance> convertToClientObjects(final List<SAConnectorInstance> serverObjects) {\n        return ModelConvertor.toArchivedConnectorInstances(serverObjects);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractArchivedDocumentSearchEntity.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.document.ArchivedDocument;\nimport org.bonitasoft.engine.core.document.api.DocumentService;\nimport org.bonitasoft.engine.core.document.model.archive.SAMappedDocument;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Zhang Bole\n * @author Baptiste Mesta\n */\npublic abstract class AbstractArchivedDocumentSearchEntity\n        extends AbstractSearchEntity<ArchivedDocument, SAMappedDocument> {\n\n    private final DocumentService documentService;\n\n    public AbstractArchivedDocumentSearchEntity(final SearchEntityDescriptor searchDescriptor,\n            final SearchOptions options, DocumentService documentService) {\n        super(searchDescriptor, options);\n        this.documentService = documentService;\n    }\n\n    @Override\n    public List<ArchivedDocument> convertToClientObjects(final List<SAMappedDocument> serverObjects) {\n        return ModelConvertor.toArchivedDocuments(serverObjects, documentService);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractArchivedHumanTaskInstanceSearchEntity.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance;\nimport org.bonitasoft.engine.execution.state.FlowNodeStateManager;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Baptiste Mesta\n */\npublic abstract class AbstractArchivedHumanTaskInstanceSearchEntity\n        extends AbstractSearchEntity<ArchivedHumanTaskInstance, SAHumanTaskInstance> {\n\n    private final FlowNodeStateManager flowNodeStateManager;\n\n    public AbstractArchivedHumanTaskInstanceSearchEntity(final SearchEntityDescriptor searchDescriptor,\n            final SearchOptions options,\n            final FlowNodeStateManager flowNodeStateManager) {\n        super(searchDescriptor, options);\n        this.flowNodeStateManager = flowNodeStateManager;\n    }\n\n    @Override\n    public List<ArchivedHumanTaskInstance> convertToClientObjects(final List<SAHumanTaskInstance> serverObjects) {\n        return ModelConvertor.toArchivedHumanTaskInstances(serverObjects, flowNodeStateManager);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractArchivedProcessInstanceSearchEntity.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstance;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic abstract class AbstractArchivedProcessInstanceSearchEntity\n        extends AbstractSearchEntity<ArchivedProcessInstance, SAProcessInstance> {\n\n    private SProcessDefinition sProcessDefinition;\n\n    private ProcessDefinitionService processDefinitionService;\n\n    public AbstractArchivedProcessInstanceSearchEntity(final SearchEntityDescriptor searchDescriptor,\n            final SearchOptions options,\n            final SProcessDefinition sProcessDefinition) {\n        super(searchDescriptor, options);\n        this.sProcessDefinition = sProcessDefinition;\n    }\n\n    public AbstractArchivedProcessInstanceSearchEntity(final SearchEntityDescriptor searchDescriptor,\n            final SearchOptions options,\n            final ProcessDefinitionService processDefinitionService) {\n        super(searchDescriptor, options);\n        this.processDefinitionService = processDefinitionService;\n    }\n\n    @Override\n    public List<ArchivedProcessInstance> convertToClientObjects(final List<SAProcessInstance> serverObjects) {\n        if (sProcessDefinition != null) {\n            return ModelConvertor.toArchivedProcessInstances(serverObjects, sProcessDefinition);\n        }\n        return ModelConvertor.toArchivedProcessInstances(serverObjects, processDefinitionService);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractCommandSearchEntity.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.command.CommandDescriptor;\nimport org.bonitasoft.engine.command.model.SCommand;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Yanyan Liu\n */\npublic abstract class AbstractCommandSearchEntity extends AbstractSearchEntity<CommandDescriptor, SCommand> {\n\n    public AbstractCommandSearchEntity(final SearchEntityDescriptor searchDescriptor, final SearchOptions options) {\n        super(searchDescriptor, options);\n    }\n\n    @Override\n    public List<CommandDescriptor> convertToClientObjects(final List<SCommand> serverObjects) {\n        return ModelConvertor.toCommandDescriptors(serverObjects);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractCommentSearchEntity.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.comment.Comment;\nimport org.bonitasoft.engine.core.process.comment.model.SComment;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Hongwen Zang\n */\npublic abstract class AbstractCommentSearchEntity extends AbstractSearchEntity<Comment, SComment> {\n\n    public AbstractCommentSearchEntity(final SearchEntityDescriptor searchDescriptor, final SearchOptions options) {\n        super(searchDescriptor, options);\n    }\n\n    @Override\n    public List<Comment> convertToClientObjects(final List<SComment> serverObjects) {\n        return ModelConvertor.toComments(serverObjects);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractDocumentSearchEntity.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.document.Document;\nimport org.bonitasoft.engine.core.document.api.DocumentService;\nimport org.bonitasoft.engine.core.document.model.SMappedDocument;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Zhang Bole\n */\npublic abstract class AbstractDocumentSearchEntity extends AbstractSearchEntity<Document, SMappedDocument> {\n\n    private final DocumentService documentService;\n\n    public AbstractDocumentSearchEntity(final SearchEntityDescriptor searchDescriptor, final SearchOptions options,\n            DocumentService documentService) {\n        super(searchDescriptor, options);\n        this.documentService = documentService;\n    }\n\n    @Override\n    public List<Document> convertToClientObjects(final List<SMappedDocument> serverObjects) {\n        return ModelConvertor.toDocuments(new ArrayList<>(serverObjects), documentService);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractGroupSearchEntity.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.identity.Group;\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Baptiste Mesta\n */\npublic abstract class AbstractGroupSearchEntity extends AbstractSearchEntity<Group, SGroup> {\n\n    public AbstractGroupSearchEntity(final SearchEntityDescriptor searchDescriptor, final SearchOptions options) {\n        super(searchDescriptor, options);\n    }\n\n    @Override\n    public List<Group> convertToClientObjects(final List<SGroup> serverObjects) {\n        return ModelConvertor.toGroups(serverObjects);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractHumanTaskInstanceSearchEntity.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance;\nimport org.bonitasoft.engine.execution.state.FlowNodeStateManager;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Baptiste Mesta\n */\npublic abstract class AbstractHumanTaskInstanceSearchEntity\n        extends AbstractSearchEntity<HumanTaskInstance, SHumanTaskInstance> {\n\n    private final FlowNodeStateManager flowNodeStateManager;\n\n    public AbstractHumanTaskInstanceSearchEntity(final SearchEntityDescriptor searchDescriptor,\n            final SearchOptions options,\n            final FlowNodeStateManager flowNodeStateManager) {\n        super(searchDescriptor, options);\n        this.flowNodeStateManager = flowNodeStateManager;\n    }\n\n    @Override\n    public List<HumanTaskInstance> convertToClientObjects(final List<SHumanTaskInstance> serverObjects) {\n        return ModelConvertor.toHumanTaskInstances(serverObjects, flowNodeStateManager);\n    }\n\n    /**\n     * factory to create a search human task descriptor\n     */\n    public static AbstractHumanTaskInstanceSearchEntity searchHumanTaskInstance(SearchEntityDescriptor searchDescriptor,\n            SearchOptions options,\n            FlowNodeStateManager flowNodeStateManager,\n            BonitaReadFunction<QueryOptions, Long> count,\n            BonitaReadFunction<QueryOptions, List<SHumanTaskInstance>> search) {\n        return new HumanTaskInstanceSearchEntity(searchDescriptor, options, flowNodeStateManager, count, search);\n    }\n\n    private static class HumanTaskInstanceSearchEntity extends AbstractHumanTaskInstanceSearchEntity {\n\n        private final BonitaReadFunction<QueryOptions, Long> count;\n        private final BonitaReadFunction<QueryOptions, List<SHumanTaskInstance>> search;\n\n        HumanTaskInstanceSearchEntity(SearchEntityDescriptor searchDescriptor, SearchOptions options,\n                FlowNodeStateManager flowNodeStateManager, BonitaReadFunction<QueryOptions, Long> count,\n                BonitaReadFunction<QueryOptions, List<SHumanTaskInstance>> search) {\n            super(searchDescriptor, options, flowNodeStateManager);\n            this.count = count;\n            this.search = search;\n        }\n\n        @Override\n        public long executeCount(QueryOptions queryOptions) throws SBonitaReadException {\n            return count.apply(queryOptions);\n        }\n\n        @Override\n        public List<SHumanTaskInstance> executeSearch(QueryOptions queryOptions) throws SBonitaReadException {\n            return search.apply(queryOptions);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractProcessDeploymentInfoSearchEntity.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Baptiste Mesta\n */\npublic abstract class AbstractProcessDeploymentInfoSearchEntity\n        extends AbstractSearchEntity<ProcessDeploymentInfo, SProcessDefinitionDeployInfo> {\n\n    public AbstractProcessDeploymentInfoSearchEntity(final SearchEntityDescriptor searchDescriptor,\n            final SearchOptions options) {\n        super(searchDescriptor, options);\n    }\n\n    @Override\n    public List<ProcessDeploymentInfo> convertToClientObjects(final List<SProcessDefinitionDeployInfo> serverObjects) {\n        return ModelConvertor.toProcessDeploymentInfo(serverObjects);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractProcessInstanceSearchEntity.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Baptiste Mesta\n */\npublic abstract class AbstractProcessInstanceSearchEntity\n        extends AbstractSearchEntity<ProcessInstance, SProcessInstance> {\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    public AbstractProcessInstanceSearchEntity(final SearchEntityDescriptor searchDescriptor,\n            final SearchOptions options,\n            final ProcessDefinitionService processDefinitionService) {\n        super(searchDescriptor, options);\n        this.processDefinitionService = processDefinitionService;\n    }\n\n    @Override\n    public List<ProcessInstance> convertToClientObjects(final List<SProcessInstance> serverObjects) {\n        return ModelConvertor.toProcessInstances(serverObjects, processDefinitionService);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractProfileSearchEntity.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.profile.Profile;\nimport org.bonitasoft.engine.profile.model.SProfile;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Celine Souchet\n */\npublic abstract class AbstractProfileSearchEntity extends AbstractSearchEntity<Profile, SProfile> {\n\n    public AbstractProfileSearchEntity(final SearchEntityDescriptor searchDescriptor, final SearchOptions options) {\n        super(searchDescriptor, options);\n    }\n\n    @Override\n    public List<Profile> convertToClientObjects(final List<SProfile> serverObjects) {\n        return ModelConvertor.toProfiles(serverObjects);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractRoleSearchEntity.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.identity.Role;\nimport org.bonitasoft.engine.identity.model.SRole;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Baptiste Mesta\n */\npublic abstract class AbstractRoleSearchEntity extends AbstractSearchEntity<Role, SRole> {\n\n    public AbstractRoleSearchEntity(final SearchEntityDescriptor searchDescriptor, final SearchOptions options) {\n        super(searchDescriptor, options);\n    }\n\n    @Override\n    public List<Role> convertToClientObjects(final List<SRole> serverObjects) {\n        return ModelConvertor.toRoles(serverObjects);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractSearchEntity.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.transaction.TransactionContentWithResult;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SearchFields;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\nimport org.bonitasoft.engine.search.impl.SearchFilter;\nimport org.bonitasoft.engine.search.impl.SearchResultImpl;\n\n/**\n * Abstract class to allow to search server object and convert them to client object\n *\n * @author Matthieu Chaffotte\n * @author Baptiste Mesta\n * @author Celine Souchet\n * @param <C>\n *        The client object\n * @param <S>\n *        The server object\n */\n@Slf4j\npublic abstract class AbstractSearchEntity<C extends Serializable, S extends PersistentObject>\n        implements TransactionContentWithResult<SearchResult<C>> {\n\n    private final SearchOptions options;\n\n    private final SearchEntityDescriptor searchDescriptor;\n\n    private long count;\n\n    private List<C> clientObjects;\n\n    /**\n     * @param searchDescriptor\n     *        The search descriptor of the searched entity\n     * @param options\n     *        The options of the search\n     */\n    public AbstractSearchEntity(final SearchEntityDescriptor searchDescriptor, final SearchOptions options) {\n        this.searchDescriptor = searchDescriptor;\n        this.options = options;\n    }\n\n    protected void validateQuery(SearchOptions options) throws SBonitaException {\n        /*\n         * Used to validate the query before execution,\n         * in order to throw exceptions with meaningful message.\n         */\n    }\n\n    @Override\n    public void execute() throws SBonitaException {\n        validateQuery(options);\n        List<S> serverObjects;\n        if (options == null) {\n            throw new SBonitaReadException(\"SearchOptions cannot be null\");\n        }\n        final int numberOfResults = options.getMaxResults();\n        final int fromIndex = options.getStartIndex();\n        final List<SearchFilter> filters = options.getFilters();\n        final List<FilterOption> filterOptions = new ArrayList<>(filters.size());\n        for (final SearchFilter filter : filters) {\n            final FilterOption option = searchDescriptor.getEntityFilter(filter);\n            if (option != null) {// in case of a unknown filter on state\n                filterOptions.add(option);\n            }\n        }\n        final String searchTerm = options.getSearchTerm();\n        SearchFields userSearchTerm = null;\n        if (searchTerm != null) {\n            userSearchTerm = searchDescriptor.getEntitySearchTerm(searchTerm);\n        }\n        final List<OrderByOption> orderOptions = new ArrayList<>();\n        final List<Sort> sorts = options.getSorts();\n        for (final Sort sort : sorts) {\n            final OrderByOption order = searchDescriptor.getEntityOrder(sort);\n            orderOptions.add(order);\n        }\n        final QueryOptions countOptions = new QueryOptions(0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS, null,\n                filterOptions, userSearchTerm);\n        count = executeCount(countOptions);\n        if (count > 0 && numberOfResults != 0) {\n            final QueryOptions searchOptions = new QueryOptions(fromIndex, numberOfResults, orderOptions, filterOptions,\n                    userSearchTerm);\n            serverObjects = executeSearch(searchOptions);\n            detectPotentialTransactionIsolationIssue(serverObjects, numberOfResults, countOptions);\n        } else {\n            serverObjects = Collections.emptyList();\n        }\n        clientObjects = convertToClientObjects(serverObjects);\n    }\n\n    private void detectPotentialTransactionIsolationIssue(List<S> serverObjects, int numberOfResults,\n            QueryOptions countOptions) throws SBonitaReadException {\n        // If there are at most 1 page of result AND the count does not detect as many objects as the search:\n        if (count <= numberOfResults && count != serverObjects.size()) {\n            long doubleCheck = executeCount(countOptions);\n            if (count != doubleCheck) {\n                log.error(\"Double checking the same query within the same transaction did NOT bring the same\"\n                        + \" result. You DO have a database transaction isolation problem. Please fix it ASAP. See\" +\n                        \" https://documentation.ofelia.com/bonita/latest/runtime/database-configuration#customize-rdbms\"\n                        + \" for details.\");\n            } else {\n                log.warn(\"Within the same transaction, the Search count & page results are not consistent.\" +\n                        \" Please see https://documentation.ofelia.com/bonita/latest/runtime/performance-troubleshooting#monitor-transaction-isolation\");\n            }\n        }\n    }\n\n    /**\n     * execute this search and return the result\n     *\n     * @return the result of the search\n     */\n    public SearchResult<C> search() throws SearchException {\n        try {\n            execute();\n        } catch (SBonitaException e) {\n            throw new SearchException(e);\n        }\n        return getResult();\n    }\n\n    /**\n     * Execute the count here\n     *\n     * @param queryOptions\n     *        The query options to execute the count with\n     * @return The number of result on the server\n     * @throws SBonitaReadException when the search failed to retrieve the count number\n     */\n    public abstract long executeCount(QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * Execute the search here\n     *\n     * @param queryOptions\n     *        The query options to execute the search with\n     * @return The list of searched server objects\n     * @throws SBonitaReadException when the search failed to retrieve the results\n     */\n    public abstract List<S> executeSearch(QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * Must convert server objects in client objects here\n     *\n     * @param serverObjects\n     *        The server object to convert\n     * @return The list of the client objects corresponding to the server objects\n     */\n    public abstract List<C> convertToClientObjects(List<S> serverObjects) throws SBonitaException;\n\n    @Override\n    public SearchResult<C> getResult() {\n        return new SearchResultImpl<>(count, clientObjects);\n    }\n\n    protected SearchFilter getSearchFilter(final SearchOptions searchOptions, final String searchedKey) {\n        return searchOptions.getFilters().stream().filter(searchFilter -> searchedKey.equals(searchFilter.getField()))\n                .findFirst().orElse(null);\n    }\n\n    public static <C extends Serializable, S extends PersistentObject> SearchResult<C> search(\n            SearchEntityDescriptor searchDescriptor,\n            SearchOptions options,\n            BonitaReadFunction<List<S>, List<C>> converter,\n            BonitaReadFunction<QueryOptions, Long> count,\n            BonitaReadFunction<QueryOptions, List<S>> search) throws SearchException {\n        return new AbstractSearchEntity<C, S>(searchDescriptor, options) {\n\n            @Override\n            public long executeCount(QueryOptions queryOptions) throws SBonitaReadException {\n                return count.apply(queryOptions);\n            }\n\n            @Override\n            public List<S> executeSearch(QueryOptions queryOptions) throws SBonitaReadException {\n                return search.apply(queryOptions);\n            }\n\n            @Override\n            public List<C> convertToClientObjects(List<S> serverObjects) throws SBonitaException {\n                return converter.apply(serverObjects);\n            }\n        }.search();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractSupervisorSearchEntity.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.supervisor.ProcessSupervisor;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\nimport org.bonitasoft.engine.service.ModelConvertor;\nimport org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor;\n\n/**\n * @author Baptiste Mesta\n */\npublic abstract class AbstractSupervisorSearchEntity\n        extends AbstractSearchEntity<ProcessSupervisor, SProcessSupervisor> {\n\n    public AbstractSupervisorSearchEntity(final SearchEntityDescriptor searchDescriptor, final SearchOptions options) {\n        super(searchDescriptor, options);\n    }\n\n    @Override\n    public List<ProcessSupervisor> convertToClientObjects(final List<SProcessSupervisor> serverObjects) {\n        return ModelConvertor.toProcessSupervisors(serverObjects);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractUserSearchEntity.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Baptiste Mesta\n */\npublic abstract class AbstractUserSearchEntity extends AbstractSearchEntity<User, SUser> {\n\n    public AbstractUserSearchEntity(final SearchEntityDescriptor searchDescriptor, final SearchOptions options) {\n        super(searchDescriptor, options);\n    }\n\n    @Override\n    public List<User> convertToClientObjects(final List<SUser> serverObjects) {\n        return ModelConvertor.toUsers(serverObjects);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/BonitaReadFunction.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\n@FunctionalInterface\npublic interface BonitaReadFunction<T, R> {\n\n    R apply(T t) throws SBonitaReadException;\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/SPageOutOfRangeException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Yanyan Liu\n */\npublic class SPageOutOfRangeException extends SBonitaException {\n\n    private static final long serialVersionUID = 7565283595822464464L;\n\n    public SPageOutOfRangeException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SPageOutOfRangeException(final String message) {\n        super(message);\n    }\n\n    public SPageOutOfRangeException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/SSearchException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class SSearchException extends SBonitaException {\n\n    private static final long serialVersionUID = -938225343187657385L;\n\n    public SSearchException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SSearchException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/SearchCommands.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.command.CommandService;\nimport org.bonitasoft.engine.command.model.SCommand;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.descriptor.SearchCommandDescriptor;\n\n/**\n * @author Yanyan Liu\n */\npublic class SearchCommands extends AbstractCommandSearchEntity {\n\n    private final CommandService commandService;\n\n    public SearchCommands(final CommandService commandService, final SearchCommandDescriptor searchDescriptor,\n            final SearchOptions options) {\n        super(searchDescriptor, options);\n        this.commandService = commandService;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return commandService.getNumberOfCommands(searchOptions);\n    }\n\n    @Override\n    public List<SCommand> executeSearch(final QueryOptions searchOptions) throws SBonitaReadException {\n        return commandService.searchCommands(searchOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/activity/SearchActivityInstances.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.activity;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\nimport org.bonitasoft.engine.execution.state.FlowNodeStateManager;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractActivityInstanceSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchActivityInstanceDescriptor;\n\n/**\n * @author Yanyan Liu\n * @author Celine Souchet\n */\npublic class SearchActivityInstances extends AbstractActivityInstanceSearchEntity {\n\n    private final ActivityInstanceService activityInstanceService;\n\n    public SearchActivityInstances(final ActivityInstanceService activityInstanceService,\n            final FlowNodeStateManager flowNodeStateManager,\n            final SearchActivityInstanceDescriptor searchDescriptor, final SearchOptions searchOptions)\n            throws SBonitaReadException {\n        super(searchDescriptor, searchOptions, flowNodeStateManager);\n        this.activityInstanceService = activityInstanceService;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return activityInstanceService.getNumberOfActivityInstances(getEntityClass(), searchOptions);\n    }\n\n    @Override\n    public List<SActivityInstance> executeSearch(final QueryOptions searchOptions) throws SBonitaReadException {\n        return activityInstanceService.searchActivityInstances(getEntityClass(), searchOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/activity/SearchArchivedActivityInstances.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.activity;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance;\nimport org.bonitasoft.engine.execution.state.FlowNodeStateManager;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractArchiveActivityInstanceSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchArchivedActivityInstanceDescriptor;\n\n/**\n * @author Yanyan Liu\n * @author Celine Souchet\n */\npublic class SearchArchivedActivityInstances extends AbstractArchiveActivityInstanceSearchEntity {\n\n    private final ActivityInstanceService activityInstanceService;\n\n    public SearchArchivedActivityInstances(final ActivityInstanceService activityInstanceService,\n            final FlowNodeStateManager flowNodeStateManager,\n            final SearchArchivedActivityInstanceDescriptor searchDescriptor, final SearchOptions searchOptions) {\n        super(searchDescriptor, searchOptions, flowNodeStateManager);\n        this.activityInstanceService = activityInstanceService;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return activityInstanceService.getNumberOfArchivedActivityInstances(getEntityClass(), searchOptions);\n    }\n\n    @Override\n    public List<SAActivityInstance> executeSearch(final QueryOptions searchOptions) throws SBonitaReadException {\n        return activityInstanceService.searchArchivedActivityInstances(getEntityClass(), searchOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/comment/SearchArchivedComments.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.comment;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.comment.api.SCommentService;\nimport org.bonitasoft.engine.core.process.comment.model.archive.SAComment;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractArchivedCommentsSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\n\n/**\n * @author Hongwen Zang\n * @author Celine Souchet\n */\npublic class SearchArchivedComments extends AbstractArchivedCommentsSearchEntity {\n\n    private final SCommentService sCommentService;\n\n    public SearchArchivedComments(final SCommentService sCommentService, final SearchEntityDescriptor searchDescriptor,\n            final SearchOptions options) {\n        super(searchDescriptor, options);\n        this.sCommentService = sCommentService;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return sCommentService.getNumberOfArchivedComments(searchOptions);\n    }\n\n    @Override\n    public List<SAComment> executeSearch(final QueryOptions searchOptions) throws SBonitaReadException {\n        return sCommentService.searchArchivedComments(searchOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/comment/SearchComments.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.comment;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.comment.api.SCommentService;\nimport org.bonitasoft.engine.core.process.comment.model.SComment;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractCommentSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchCommentDescriptor;\n\n/**\n * @author Hongwen Zang\n */\npublic class SearchComments extends AbstractCommentSearchEntity {\n\n    private final SCommentService commentService;\n\n    public SearchComments(SearchCommentDescriptor searchDescriptor, SearchOptions options,\n            SCommentService commentService) {\n        super(searchDescriptor, options);\n        this.commentService = commentService;\n    }\n\n    @Override\n    public long executeCount(QueryOptions searchOptions) throws SBonitaReadException {\n        return commentService.getNumberOfComments(searchOptions);\n    }\n\n    @Override\n    public List<SComment> executeSearch(QueryOptions searchOptions) throws SBonitaReadException {\n        return commentService.searchComments(searchOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/comment/SearchCommentsInvolvingUser.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.comment;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.comment.api.SCommentService;\nimport org.bonitasoft.engine.core.process.comment.model.SComment;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractCommentSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\n\n/**\n * @author Hongwen Zang\n */\npublic class SearchCommentsInvolvingUser extends AbstractCommentSearchEntity {\n\n    private final SCommentService commentService;\n\n    private final long userId;\n\n    public SearchCommentsInvolvingUser(SearchEntityDescriptor searchDescriptor, SearchOptions options,\n            SCommentService commentService, long userId) {\n        super(searchDescriptor, options);\n        this.commentService = commentService;\n        this.userId = userId;\n    }\n\n    @Override\n    public long executeCount(QueryOptions searchOptions) throws SBonitaReadException {\n        return commentService.getNumberOfCommentsInvolvingUser(userId, searchOptions);\n    }\n\n    @Override\n    public List<SComment> executeSearch(QueryOptions searchOptions) throws SBonitaReadException {\n        return commentService.searchCommentsInvolvingUser(userId, searchOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/comment/SearchCommentsManagedBy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.comment;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.comment.api.SCommentService;\nimport org.bonitasoft.engine.core.process.comment.model.SComment;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractCommentSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\n\n/**\n * @author Hongwen Zang\n */\npublic class SearchCommentsManagedBy extends AbstractCommentSearchEntity {\n\n    private final SCommentService commentService;\n\n    private final long managerUserId;\n\n    public SearchCommentsManagedBy(SearchEntityDescriptor searchDescriptor, SearchOptions options,\n            SCommentService commentService, long managerUserId) {\n        super(searchDescriptor, options);\n        this.commentService = commentService;\n        this.managerUserId = managerUserId;\n    }\n\n    @Override\n    public long executeCount(QueryOptions searchOptions) throws SBonitaReadException {\n        return commentService.getNumberOfCommentsManagedBy(managerUserId, searchOptions);\n    }\n\n    @Override\n    public List<SComment> executeSearch(QueryOptions searchOptions) throws SBonitaReadException {\n        return commentService.searchCommentsManagedBy(managerUserId, searchOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/connector/SearchArchivedConnectorInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.connector;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.connector.ConnectorInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAConnectorInstance;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractArchivedConnectorInstanceSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SearchArchivedConnectorInstance extends AbstractArchivedConnectorInstanceSearchEntity {\n\n    private final ReadPersistenceService persistenceService;\n\n    private final ConnectorInstanceService connectorInstanceService;\n\n    public SearchArchivedConnectorInstance(final ConnectorInstanceService connectorInstanceService,\n            final SearchEntityDescriptor searchDescriptor,\n            final SearchOptions options, final ReadPersistenceService persistenceService) {\n        super(searchDescriptor, options);\n        this.connectorInstanceService = connectorInstanceService;\n        this.persistenceService = persistenceService;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return connectorInstanceService.getNumberArchivedConnectorInstance(searchOptions, persistenceService);\n    }\n\n    @Override\n    public List<SAConnectorInstance> executeSearch(final QueryOptions searchOptions) throws SBonitaReadException {\n        return connectorInstanceService.searchArchivedConnectorInstance(searchOptions, persistenceService);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/FieldDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class FieldDescriptor {\n\n    private final Class<? extends PersistentObject> persitentClass;\n\n    private final String value;\n\n    public FieldDescriptor(final Class<? extends PersistentObject> persitentClass, final String value) {\n        super();\n        this.persitentClass = persitentClass;\n        this.value = value;\n    }\n\n    public Class<? extends PersistentObject> getPersistentClass() {\n        return persitentClass;\n    }\n\n    public String getValue() {\n        return value;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchActivityInstanceDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SUserTaskInstanceBuilderFactory;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor;\n\n/**\n * @author Yanyan Liu\n * @author Elias Ricken de Medeiros\n */\npublic class SearchActivityInstanceDescriptor extends SearchEntityDescriptor {\n\n    private final Map<String, FieldDescriptor> activityInstanceDescriptorKeys;\n\n    private final Map<Class<? extends PersistentObject>, Set<String>> activityInstanceDescriptorAllFields;\n\n    public SearchActivityInstanceDescriptor() {\n        final SUserTaskInstanceBuilderFactory keyProvider = BuilderFactory.get(SUserTaskInstanceBuilderFactory.class);\n        activityInstanceDescriptorKeys = new HashMap<>();\n        activityInstanceDescriptorKeys.put(ActivityInstanceSearchDescriptor.NAME,\n                new FieldDescriptor(SActivityInstance.class, keyProvider.getNameKey()));\n        activityInstanceDescriptorKeys.put(ActivityInstanceSearchDescriptor.STATE_NAME,\n                new FieldDescriptor(SActivityInstance.class, keyProvider.getStateNameKey()));\n        activityInstanceDescriptorKeys.put(ActivityInstanceSearchDescriptor.PROCESS_DEFINITION_ID,\n                new FieldDescriptor(SActivityInstance.class,\n                        keyProvider.getProcessDefinitionKey()));\n        activityInstanceDescriptorKeys.put(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID,\n                new FieldDescriptor(SActivityInstance.class,\n                        keyProvider.getRootProcessInstanceKey()));\n        activityInstanceDescriptorKeys.put(ActivityInstanceSearchDescriptor.PARENT_ACTIVITY_INSTANCE_ID,\n                new FieldDescriptor(SActivityInstance.class,\n                        keyProvider.getParentActivityInstanceKey()));\n        activityInstanceDescriptorKeys.put(ActivityInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID,\n                new FieldDescriptor(SActivityInstance.class,\n                        keyProvider.getParentProcessInstanceKey()));\n        activityInstanceDescriptorKeys.put(ActivityInstanceSearchDescriptor.PARENT_CONTAINER_ID,\n                new FieldDescriptor(SActivityInstance.class,\n                        keyProvider.getParentContainerIdKey()));\n        activityInstanceDescriptorKeys.put(ActivityInstanceSearchDescriptor.LAST_MODIFICATION_DATE,\n                new FieldDescriptor(SActivityInstance.class,\n                        keyProvider.getLastUpdateDateKey()));\n        activityInstanceDescriptorKeys.put(ActivityInstanceSearchDescriptor.DISPLAY_NAME,\n                new FieldDescriptor(SActivityInstance.class, keyProvider.getDisplayNameKey()));\n        activityInstanceDescriptorKeys.put(ActivityInstanceSearchDescriptor.USER_ID,\n                new FieldDescriptor(SProcessSupervisor.class, SProcessSupervisor.USER_ID_KEY));\n        activityInstanceDescriptorKeys.put(ActivityInstanceSearchDescriptor.GROUP_ID,\n                new FieldDescriptor(SProcessSupervisor.class, SProcessSupervisor.GROUP_ID_KEY));\n        activityInstanceDescriptorKeys.put(ActivityInstanceSearchDescriptor.ROLE_ID,\n                new FieldDescriptor(SProcessSupervisor.class, SProcessSupervisor.ROLE_ID_KEY));\n\n        activityInstanceDescriptorAllFields = new HashMap<>();\n        final Set<String> tasksInstanceFields = new HashSet<>();\n        tasksInstanceFields.add(keyProvider.getNameKey());\n        tasksInstanceFields.add(keyProvider.getDisplayNameKey());\n        activityInstanceDescriptorAllFields.put(SActivityInstance.class, tasksInstanceFields);\n    }\n\n    @Override\n    protected Map<String, FieldDescriptor> getEntityKeys() {\n        return activityInstanceDescriptorKeys;\n    }\n\n    @Override\n    protected Map<Class<? extends PersistentObject>, Set<String>> getAllFields() {\n        return activityInstanceDescriptorAllFields;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchApplicationDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.business.application.ApplicationSearchDescriptor;\nimport org.bonitasoft.engine.business.application.model.AbstractSApplication;\nimport org.bonitasoft.engine.business.application.model.SApplication;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SearchApplicationDescriptor extends SearchEntityDescriptor {\n\n    public static final String APPLICATION_VISIBILITY = \"visibility\";\n    private final Map<String, FieldDescriptor> keys;\n\n    private final Map<Class<? extends PersistentObject>, Set<String>> allFields;\n\n    protected SearchApplicationDescriptor() {\n        keys = new HashMap<>(13);\n        keys.put(ApplicationSearchDescriptor.ID,\n                new FieldDescriptor(SApplication.class, AbstractSApplication.ID));\n        keys.put(ApplicationSearchDescriptor.TOKEN,\n                new FieldDescriptor(SApplication.class, AbstractSApplication.TOKEN));\n        keys.put(ApplicationSearchDescriptor.DISPLAY_NAME,\n                new FieldDescriptor(SApplication.class, AbstractSApplication.DISPLAY_NAME));\n        keys.put(ApplicationSearchDescriptor.VERSION,\n                new FieldDescriptor(SApplication.class, AbstractSApplication.VERSION));\n        keys.put(ApplicationSearchDescriptor.ICON_PATH,\n                new FieldDescriptor(SApplication.class, AbstractSApplication.ICON_PATH));\n        keys.put(ApplicationSearchDescriptor.CREATION_DATE,\n                new FieldDescriptor(SApplication.class, AbstractSApplication.CREATION_DATE));\n        keys.put(ApplicationSearchDescriptor.CREATED_BY,\n                new FieldDescriptor(SApplication.class, AbstractSApplication.CREATED_BY));\n        keys.put(ApplicationSearchDescriptor.LAST_UPDATE_DATE,\n                new FieldDescriptor(SApplication.class, AbstractSApplication.LAST_UPDATE_DATE));\n        keys.put(ApplicationSearchDescriptor.UPDATED_BY,\n                new FieldDescriptor(SApplication.class, AbstractSApplication.UPDATED_BY));\n        keys.put(ApplicationSearchDescriptor.STATE,\n                new FieldDescriptor(SApplication.class, AbstractSApplication.STATE));\n        keys.put(ApplicationSearchDescriptor.PROFILE_ID,\n                new FieldDescriptor(SApplication.class, AbstractSApplication.PROFILE_ID));\n        keys.put(ApplicationSearchDescriptor.LAYOUT_ID,\n                new FieldDescriptor(SApplication.class, AbstractSApplication.LAYOUT_ID));\n        keys.put(ApplicationSearchDescriptor.THEME_ID,\n                new FieldDescriptor(SApplication.class, AbstractSApplication.THEME_ID));\n        // internal usage only for now (as it would require a conversion of the Visibility enum):\n        keys.put(APPLICATION_VISIBILITY,\n                new FieldDescriptor(SApplication.class, AbstractSApplication.INTERNAL_PROFILE));\n\n        allFields = new HashMap<>(1);\n\n        final Set<String> pageFields = new HashSet<>(5);\n        pageFields.add(AbstractSApplication.TOKEN);\n        pageFields.add(AbstractSApplication.DISPLAY_NAME);\n        pageFields.add(AbstractSApplication.VERSION);\n        pageFields.add(AbstractSApplication.ICON_PATH);\n        pageFields.add(AbstractSApplication.STATE);\n        allFields.put(SApplication.class, pageFields);\n    }\n\n    @Override\n    protected Map<String, FieldDescriptor> getEntityKeys() {\n        return keys;\n    }\n\n    @Override\n    protected Map<Class<? extends PersistentObject>, Set<String>> getAllFields() {\n        return allFields;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchApplicationMenuDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.business.application.ApplicationMenuSearchDescriptor;\nimport org.bonitasoft.engine.business.application.model.SApplicationMenu;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SearchApplicationMenuDescriptor extends SearchEntityDescriptor {\n\n    private final Map<String, FieldDescriptor> keys;\n\n    private final Map<Class<? extends PersistentObject>, Set<String>> allFields;\n\n    SearchApplicationMenuDescriptor() {\n        keys = new HashMap<>(6);\n        keys.put(ApplicationMenuSearchDescriptor.ID, new FieldDescriptor(SApplicationMenu.class, SApplicationMenu.ID));\n        keys.put(ApplicationMenuSearchDescriptor.APPLICATION_PAGE_ID,\n                new FieldDescriptor(SApplicationMenu.class, SApplicationMenu.APPLICATION_PAGE_ID));\n        keys.put(ApplicationMenuSearchDescriptor.DISPLAY_NAME,\n                new FieldDescriptor(SApplicationMenu.class, SApplicationMenu.DISPLAY_NAME));\n        keys.put(ApplicationMenuSearchDescriptor.INDEX,\n                new FieldDescriptor(SApplicationMenu.class, SApplicationMenu.INDEX));\n        keys.put(ApplicationMenuSearchDescriptor.APPLICATION_ID,\n                new FieldDescriptor(SApplicationMenu.class, SApplicationMenu.APPLICAITON_ID));\n        keys.put(ApplicationMenuSearchDescriptor.PARENT_ID,\n                new FieldDescriptor(SApplicationMenu.class, SApplicationMenu.PARENT_ID));\n\n        allFields = new HashMap<>(1);\n\n        final Set<String> pageFields = new HashSet<>(1);\n        pageFields.add(SApplicationMenu.DISPLAY_NAME);\n        allFields.put(SApplicationMenu.class, pageFields);\n    }\n\n    @Override\n    protected Map<String, FieldDescriptor> getEntityKeys() {\n        return keys;\n    }\n\n    @Override\n    protected Map<Class<? extends PersistentObject>, Set<String>> getAllFields() {\n        return allFields;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchApplicationPageDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.business.application.ApplicationPageSearchDescriptor;\nimport org.bonitasoft.engine.business.application.model.SApplicationPage;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SearchApplicationPageDescriptor extends SearchEntityDescriptor {\n\n    private final Map<String, FieldDescriptor> keys;\n\n    private final Map<Class<? extends PersistentObject>, Set<String>> allFields;\n\n    SearchApplicationPageDescriptor() {\n        keys = new HashMap<>(4);\n        keys.put(ApplicationPageSearchDescriptor.ID, new FieldDescriptor(SApplicationPage.class, SApplicationPage.ID));\n        keys.put(ApplicationPageSearchDescriptor.TOKEN,\n                new FieldDescriptor(SApplicationPage.class, SApplicationPage.TOKEN));\n        keys.put(ApplicationPageSearchDescriptor.APPLICATION_ID,\n                new FieldDescriptor(SApplicationPage.class, SApplicationPage.APPLICATION_ID));\n        keys.put(ApplicationPageSearchDescriptor.PAGE_ID,\n                new FieldDescriptor(SApplicationPage.class, SApplicationPage.PAGE_ID));\n\n        allFields = new HashMap<>(1);\n\n        final Set<String> pageFields = new HashSet<>(1);\n        pageFields.add(SApplicationPage.TOKEN);\n        allFields.put(SApplicationPage.class, pageFields);\n    }\n\n    @Override\n    protected Map<String, FieldDescriptor> getEntityKeys() {\n        return keys;\n    }\n\n    @Override\n    protected Map<Class<? extends PersistentObject>, Set<String>> getAllFields() {\n        return allFields;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchArchivedActivityInstanceDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstanceSearchDescriptor;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAUserTaskInstanceBuilderFactory;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Yanyan Liu\n * @author Celine Souchet\n */\npublic class SearchArchivedActivityInstanceDescriptor extends SearchEntityDescriptor {\n\n    private final Map<String, FieldDescriptor> archivedActivityInstanceDescriptorKeys;\n\n    private final Map<Class<? extends PersistentObject>, Set<String>> archivedActivityInstanceDescriptorAllFields;\n\n    public SearchArchivedActivityInstanceDescriptor() {\n        final SAUserTaskInstanceBuilderFactory keyProvider = BuilderFactory.get(SAUserTaskInstanceBuilderFactory.class);\n        archivedActivityInstanceDescriptorKeys = new HashMap<>(10);\n        archivedActivityInstanceDescriptorKeys.put(ArchivedActivityInstanceSearchDescriptor.NAME,\n                new FieldDescriptor(SAActivityInstance.class, keyProvider.getNameKey()));\n        archivedActivityInstanceDescriptorKeys.put(ArchivedActivityInstanceSearchDescriptor.PRIORITY,\n                new FieldDescriptor(SAActivityInstance.class,\n                        keyProvider.getPriorityKey()));\n        archivedActivityInstanceDescriptorKeys.put(ArchivedActivityInstanceSearchDescriptor.PROCESS_DEFINITION_ID,\n                new FieldDescriptor(\n                        SAActivityInstance.class, keyProvider.getProcessDefinitionKey()));\n        archivedActivityInstanceDescriptorKeys.put(ArchivedActivityInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID,\n                new FieldDescriptor(\n                        SAActivityInstance.class, keyProvider.getRootProcessInstanceKey()));\n        archivedActivityInstanceDescriptorKeys.put(ArchivedActivityInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID,\n                new FieldDescriptor(\n                        SAActivityInstance.class, keyProvider.getParentProcessInstanceKey()));\n        archivedActivityInstanceDescriptorKeys.put(ArchivedActivityInstanceSearchDescriptor.PARENT_ACTIVITY_INSTANCE_ID,\n                new FieldDescriptor(\n                        SAActivityInstance.class, keyProvider.getParentActivityInstanceKey()));\n        archivedActivityInstanceDescriptorKeys.put(ArchivedActivityInstanceSearchDescriptor.STATE_NAME,\n                new FieldDescriptor(SAActivityInstance.class,\n                        keyProvider.getStateNameKey()));\n        archivedActivityInstanceDescriptorKeys.put(ArchivedActivityInstanceSearchDescriptor.ASSIGNEE_ID,\n                new FieldDescriptor(SAActivityInstance.class,\n                        keyProvider.getAssigneeIdKey()));\n        archivedActivityInstanceDescriptorKeys.put(ArchivedActivityInstanceSearchDescriptor.DISPLAY_NAME,\n                new FieldDescriptor(SAActivityInstance.class,\n                        keyProvider.getDisplayNameKey()));\n        archivedActivityInstanceDescriptorKeys.put(ArchivedActivityInstanceSearchDescriptor.REACHED_STATE_DATE,\n                new FieldDescriptor(SAActivityInstance.class,\n                        keyProvider.getReachedStateDateKey()));\n        archivedActivityInstanceDescriptorKeys.put(ArchivedActivityInstanceSearchDescriptor.SOURCE_OBJECT_ID,\n                new FieldDescriptor(SAActivityInstance.class,\n                        keyProvider.getSourceObjectIdKey()));\n        archivedActivityInstanceDescriptorKeys.put(ArchivedActivityInstanceSearchDescriptor.ARCHIVE_DATE,\n                new FieldDescriptor(SAActivityInstance.class, keyProvider.getArchivedDateKey()));\n\n        archivedActivityInstanceDescriptorAllFields = new HashMap<>(1);\n        final Set<String> humanFields = new HashSet<String>(2);\n        humanFields.add(keyProvider.getNameKey());\n        humanFields.add(keyProvider.getDisplayNameKey());\n        archivedActivityInstanceDescriptorAllFields.put(SAActivityInstance.class, humanFields);\n    }\n\n    @Override\n    protected Map<String, FieldDescriptor> getEntityKeys() {\n        return archivedActivityInstanceDescriptorKeys;\n    }\n\n    @Override\n    protected Map<Class<? extends PersistentObject>, Set<String>> getAllFields() {\n        return archivedActivityInstanceDescriptorAllFields;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchArchivedCommentsDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.bpm.comment.ArchivedCommentsSearchDescriptor;\nimport org.bonitasoft.engine.core.process.comment.model.archive.SAComment;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Hongwen Zang\n * @author Celine Souchet\n */\npublic class SearchArchivedCommentsDescriptor extends SearchEntityDescriptor {\n\n    private final Map<String, FieldDescriptor> searchEntityKeys;\n\n    private final Map<Class<? extends PersistentObject>, Set<String>> archivedCommentsAllFields;\n\n    SearchArchivedCommentsDescriptor() {\n        searchEntityKeys = new HashMap<>(7);\n        searchEntityKeys.put(ArchivedCommentsSearchDescriptor.PROCESS_INSTANCE_ID,\n                new FieldDescriptor(SAComment.class, SAComment.PROCESSINSTANCEID_KEY));\n        searchEntityKeys.put(ArchivedCommentsSearchDescriptor.POSTED_BY_ID,\n                new FieldDescriptor(SAComment.class, SAComment.USERID_KEY));\n        searchEntityKeys.put(ArchivedCommentsSearchDescriptor.ID,\n                new FieldDescriptor(SAComment.class, SAComment.ID_KEY));\n        searchEntityKeys.put(ArchivedCommentsSearchDescriptor.POSTDATE,\n                new FieldDescriptor(SAComment.class, SAComment.POSTDATE_KEY));\n        searchEntityKeys.put(ArchivedCommentsSearchDescriptor.SOURCE_OBJECT_ID,\n                new FieldDescriptor(SAComment.class, SAComment.SOURCEOBJECTID_KEY));\n        searchEntityKeys.put(ArchivedCommentsSearchDescriptor.CONTENT,\n                new FieldDescriptor(SAComment.class, SAComment.CONTENT_KEY));\n        searchEntityKeys.put(ArchivedCommentsSearchDescriptor.USER_NAME,\n                new FieldDescriptor(SUser.class, SUser.USER_NAME));\n\n        archivedCommentsAllFields = new HashMap<>(1);\n        final Set<String> archivedCommentFields = new HashSet<>(1);\n        archivedCommentFields.add(SAComment.CONTENT_KEY);\n        archivedCommentsAllFields.put(SAComment.class, archivedCommentFields);\n    }\n\n    @Override\n    protected Map<String, FieldDescriptor> getEntityKeys() {\n        return searchEntityKeys;\n    }\n\n    @Override\n    protected Map<Class<? extends PersistentObject>, Set<String>> getAllFields() {\n        return archivedCommentsAllFields;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchArchivedConnectorInstanceDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.bpm.connector.ArchiveConnectorInstancesSearchDescriptor;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAConnectorInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAConnectorInstanceBuilderFactory;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SearchArchivedConnectorInstanceDescriptor extends SearchEntityDescriptor {\n\n    private final Map<String, FieldDescriptor> searchEntityKeys;\n\n    private final Map<Class<? extends PersistentObject>, Set<String>> archivedConnectorssAllFields;\n\n    SearchArchivedConnectorInstanceDescriptor() {\n        final SAConnectorInstanceBuilderFactory keyProvider = BuilderFactory\n                .get(SAConnectorInstanceBuilderFactory.class);\n        searchEntityKeys = new HashMap<String, FieldDescriptor>(7);\n        searchEntityKeys.put(ArchiveConnectorInstancesSearchDescriptor.NAME,\n                new FieldDescriptor(SAConnectorInstance.class, keyProvider.getNameKey()));\n        searchEntityKeys.put(ArchiveConnectorInstancesSearchDescriptor.ACTIVATION_EVENT,\n                new FieldDescriptor(SAConnectorInstance.class, keyProvider.getActivationEventKey()));\n        searchEntityKeys.put(ArchiveConnectorInstancesSearchDescriptor.CONNECTOR_DEFINITION_ID,\n                new FieldDescriptor(SAConnectorInstance.class, keyProvider.getConnectorIdKey()));\n        searchEntityKeys.put(ArchiveConnectorInstancesSearchDescriptor.CONNECTOR_DEFINITION_VERSION,\n                new FieldDescriptor(SAConnectorInstance.class, keyProvider.getVersionKey()));\n        searchEntityKeys.put(ArchiveConnectorInstancesSearchDescriptor.CONTAINER_ID,\n                new FieldDescriptor(SAConnectorInstance.class, keyProvider.getContainerIdKey()));\n        searchEntityKeys.put(ArchiveConnectorInstancesSearchDescriptor.CONTAINER_TYPE,\n                new FieldDescriptor(SAConnectorInstance.class, keyProvider.getContainerTypeKey()));\n        searchEntityKeys.put(ArchiveConnectorInstancesSearchDescriptor.STATE,\n                new FieldDescriptor(SAConnectorInstance.class, keyProvider.getStateKey()));\n        searchEntityKeys.put(ArchiveConnectorInstancesSearchDescriptor.SOURCE_OBJECT_ID,\n                new FieldDescriptor(SAConnectorInstance.class, keyProvider.getSourceObjectIdKey()));\n        archivedConnectorssAllFields = new HashMap<Class<? extends PersistentObject>, Set<String>>(1);\n        final Set<String> connectorFields = new HashSet<String>(2);\n        connectorFields.add(keyProvider.getNameKey());\n        connectorFields.add(keyProvider.getConnectorIdKey());\n\n        archivedConnectorssAllFields.put(SAConnectorInstance.class, connectorFields);\n    }\n\n    @Override\n    protected Map<String, FieldDescriptor> getEntityKeys() {\n        return searchEntityKeys;\n    }\n\n    @Override\n    protected Map<Class<? extends PersistentObject>, Set<String>> getAllFields() {\n        return archivedConnectorssAllFields;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchArchivedDocumentDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.bpm.document.ArchivedDocumentsSearchDescriptor;\nimport org.bonitasoft.engine.core.document.model.archive.SADocumentMapping;\nimport org.bonitasoft.engine.core.document.model.archive.SAMappedDocument;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Zhang Bole\n * @author Matthieu Chaffotte\n */\npublic class SearchArchivedDocumentDescriptor extends SearchEntityDescriptor {\n\n    private final Map<String, FieldDescriptor> searchEntityKeys;\n\n    private final Map<Class<? extends PersistentObject>, Set<String>> documentAllFields;\n\n    SearchArchivedDocumentDescriptor() {\n        searchEntityKeys = new HashMap<>(12);\n        searchEntityKeys.put(ArchivedDocumentsSearchDescriptor.ARCHIVE_DATE,\n                new FieldDescriptor(SAMappedDocument.class, SADocumentMapping.ARCHIVE_DATE));\n        searchEntityKeys.put(ArchivedDocumentsSearchDescriptor.DOCUMENT_AUTHOR,\n                new FieldDescriptor(SAMappedDocument.class, \"document.\" + SADocumentMapping.AUTHOR));\n        searchEntityKeys.put(ArchivedDocumentsSearchDescriptor.DOCUMENT_CONTENT_FILENAME,\n                new FieldDescriptor(SAMappedDocument.class, \"document.\" + SADocumentMapping.FILE_NAME));\n        searchEntityKeys.put(ArchivedDocumentsSearchDescriptor.DOCUMENT_CONTENT_MIMETYPE,\n                new FieldDescriptor(SAMappedDocument.class, \"document.\" + SADocumentMapping.MIME_TYPE));\n        searchEntityKeys.put(ArchivedDocumentsSearchDescriptor.DOCUMENT_CREATIONDATE,\n                new FieldDescriptor(SAMappedDocument.class, \"document.\" + SADocumentMapping.CREATION_DATE));\n        searchEntityKeys.put(ArchivedDocumentsSearchDescriptor.DOCUMENT_HAS_CONTENT,\n                new FieldDescriptor(SAMappedDocument.class, \"document.\" + SADocumentMapping.HAS_CONTENT));\n        searchEntityKeys.put(ArchivedDocumentsSearchDescriptor.DOCUMENT_NAME,\n                new FieldDescriptor(SAMappedDocument.class, SADocumentMapping.NAME));\n        searchEntityKeys.put(ArchivedDocumentsSearchDescriptor.DOCUMENT_DESCRIPTION,\n                new FieldDescriptor(SAMappedDocument.class, SADocumentMapping.DESCRIPTION));\n        searchEntityKeys.put(ArchivedDocumentsSearchDescriptor.DOCUMENT_VERSION,\n                new FieldDescriptor(SAMappedDocument.class, SADocumentMapping.VERSION));\n        searchEntityKeys.put(ArchivedDocumentsSearchDescriptor.LIST_INDEX,\n                new FieldDescriptor(SAMappedDocument.class, SADocumentMapping.INDEX));\n        searchEntityKeys.put(ArchivedDocumentsSearchDescriptor.DOCUMENT_URL,\n                new FieldDescriptor(SAMappedDocument.class, \"document.\" + SADocumentMapping.URL));\n        searchEntityKeys.put(ArchivedDocumentsSearchDescriptor.PROCESSINSTANCE_ID,\n                new FieldDescriptor(SAMappedDocument.class, SADocumentMapping.PROCESS_INSTANCE_ID));\n        searchEntityKeys.put(ArchivedDocumentsSearchDescriptor.SOURCEOBJECT_ID,\n                new FieldDescriptor(SAMappedDocument.class, SADocumentMapping.SOURCE_OBJECT_ID));\n        searchEntityKeys.put(ArchivedDocumentsSearchDescriptor.CONTENT_STORAGE_ID,\n                new FieldDescriptor(SAMappedDocument.class, SADocumentMapping.DOCUMENT_ID));\n\n        documentAllFields = new HashMap<>(1);\n        final Set<String> documentFields = new HashSet<String>(7);\n        documentFields.add(SADocumentMapping.NAME);\n        documentFields.add(SADocumentMapping.DESCRIPTION);\n        documentFields.add(\"document.\" + SADocumentMapping.FILE_NAME);\n        documentFields.add(\"document.\" + SADocumentMapping.MIME_TYPE);\n        documentFields.add(\"document.\" + SADocumentMapping.URL);\n        documentAllFields.put(SAMappedDocument.class, documentFields);\n    }\n\n    @Override\n    protected Map<String, FieldDescriptor> getEntityKeys() {\n        return searchEntityKeys;\n    }\n\n    @Override\n    protected Map<Class<? extends PersistentObject>, Set<String>> getAllFields() {\n        return documentAllFields;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchArchivedFlowNodeInstanceDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstanceSearchDescriptor;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAFlowNodeInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAUserTaskInstanceBuilderFactory;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n */\npublic class SearchArchivedFlowNodeInstanceDescriptor extends SearchEntityDescriptor {\n\n    private final Map<String, FieldDescriptor> archFlowNodeDescriptorKeys;\n\n    private final Map<Class<? extends PersistentObject>, Set<String>> flowNodeInstanceDescriptorAllFields;\n\n    public SearchArchivedFlowNodeInstanceDescriptor() {\n        final SAFlowNodeInstanceBuilderFactory keyProvider = BuilderFactory.get(SAUserTaskInstanceBuilderFactory.class);\n        archFlowNodeDescriptorKeys = new HashMap<String, FieldDescriptor>(13);\n        archFlowNodeDescriptorKeys.put(ArchivedFlowNodeInstanceSearchDescriptor.NAME,\n                new FieldDescriptor(SAFlowNodeInstance.class, keyProvider.getNameKey()));\n        archFlowNodeDescriptorKeys.put(ArchivedFlowNodeInstanceSearchDescriptor.STATE_NAME,\n                new FieldDescriptor(SAFlowNodeInstance.class, keyProvider.getStateNameKey()));\n        archFlowNodeDescriptorKeys.put(ArchivedFlowNodeInstanceSearchDescriptor.PROCESS_DEFINITION_ID,\n                new FieldDescriptor(SAFlowNodeInstance.class,\n                        keyProvider.getProcessDefinitionKey()));\n        archFlowNodeDescriptorKeys.put(ArchivedFlowNodeInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID,\n                new FieldDescriptor(SAFlowNodeInstance.class,\n                        keyProvider.getParentProcessInstanceKey()));\n        archFlowNodeDescriptorKeys.put(ArchivedFlowNodeInstanceSearchDescriptor.PARENT_ACTIVITY_INSTANCE_ID,\n                new FieldDescriptor(SAFlowNodeInstance.class,\n                        keyProvider.getParentActivityInstanceKey()));\n        archFlowNodeDescriptorKeys.put(ArchivedFlowNodeInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID,\n                new FieldDescriptor(SAFlowNodeInstance.class,\n                        keyProvider.getRootProcessInstanceKey()));\n        archFlowNodeDescriptorKeys.put(ArchivedFlowNodeInstanceSearchDescriptor.DISPLAY_NAME,\n                new FieldDescriptor(SAFlowNodeInstance.class, keyProvider.getDisplayNameKey()));\n        archFlowNodeDescriptorKeys.put(ArchivedFlowNodeInstanceSearchDescriptor.FLOW_NODE_TYPE,\n                new FieldDescriptor(SAFlowNodeInstance.class,\n                        keyProvider.getKindKey()));\n        archFlowNodeDescriptorKeys.put(ArchivedFlowNodeInstanceSearchDescriptor.ORIGINAL_FLOW_NODE_ID,\n                new FieldDescriptor(SAFlowNodeInstance.class,\n                        keyProvider.getSourceObjectIdKey()));\n        archFlowNodeDescriptorKeys.put(ArchivedFlowNodeInstanceSearchDescriptor.TERMINAL,\n                new FieldDescriptor(SAFlowNodeInstance.class, keyProvider.getTerminalKey()));\n        archFlowNodeDescriptorKeys.put(ArchivedFlowNodeInstanceSearchDescriptor.REACHED_STATE_DATE,\n                new FieldDescriptor(SAFlowNodeInstance.class, keyProvider.getReachedStateDateKey()));\n        archFlowNodeDescriptorKeys.put(ArchivedFlowNodeInstanceSearchDescriptor.ARCHIVE_DATE,\n                new FieldDescriptor(SAFlowNodeInstance.class, keyProvider.getArchivedDateKey()));\n        archFlowNodeDescriptorKeys.put(ArchivedFlowNodeInstanceSearchDescriptor.STATE_ID,\n                new FieldDescriptor(SAFlowNodeInstance.class, keyProvider.getStateIdKey()));\n\n        final Set<String> tasksInstanceFields = new HashSet<String>(2);\n        tasksInstanceFields.add(keyProvider.getNameKey());\n        tasksInstanceFields.add(keyProvider.getDisplayNameKey());\n        flowNodeInstanceDescriptorAllFields = new HashMap<Class<? extends PersistentObject>, Set<String>>(1);\n        flowNodeInstanceDescriptorAllFields.put(SAFlowNodeInstance.class, tasksInstanceFields);\n    }\n\n    @Override\n    protected Map<String, FieldDescriptor> getEntityKeys() {\n        return archFlowNodeDescriptorKeys;\n    }\n\n    @Override\n    protected Map<Class<? extends PersistentObject>, Set<String>> getAllFields() {\n        return flowNodeInstanceDescriptorAllFields;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchArchivedHumanTaskInstanceDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstanceSearchDescriptor;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAUserTaskInstanceBuilderFactory;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Julien Mege\n * @author Matthieu Chaffotte\n */\npublic class SearchArchivedHumanTaskInstanceDescriptor extends SearchEntityDescriptor {\n\n    private final Map<String, FieldDescriptor> entityKeys;\n\n    private final Map<Class<? extends PersistentObject>, Set<String>> humanTaskInstanceAllFields;\n\n    public SearchArchivedHumanTaskInstanceDescriptor() {\n        final SAUserTaskInstanceBuilderFactory keyProvider = BuilderFactory.get(SAUserTaskInstanceBuilderFactory.class);\n        entityKeys = new HashMap<String, FieldDescriptor>(10);\n        entityKeys.put(ArchivedHumanTaskInstanceSearchDescriptor.NAME,\n                new FieldDescriptor(SAHumanTaskInstance.class, keyProvider.getNameKey()));\n        entityKeys.put(ArchivedHumanTaskInstanceSearchDescriptor.PRIORITY,\n                new FieldDescriptor(SAHumanTaskInstance.class, keyProvider.getPriorityKey()));\n        entityKeys.put(ArchivedHumanTaskInstanceSearchDescriptor.PROCESS_DEFINITION_ID,\n                new FieldDescriptor(SAHumanTaskInstance.class, keyProvider.getProcessDefinitionKey()));\n        entityKeys.put(ArchivedHumanTaskInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID,\n                new FieldDescriptor(SAHumanTaskInstance.class, keyProvider.getRootProcessInstanceKey()));\n        entityKeys.put(ArchivedHumanTaskInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID,\n                new FieldDescriptor(SAHumanTaskInstance.class, keyProvider.getParentProcessInstanceKey()));\n        entityKeys.put(ArchivedHumanTaskInstanceSearchDescriptor.ORIGINAL_HUMAN_TASK_ID,\n                new FieldDescriptor(SAHumanTaskInstance.class, keyProvider.getSourceObjectIdKey()));\n        entityKeys.put(ArchivedHumanTaskInstanceSearchDescriptor.PARENT_ACTIVITY_INSTANCE_ID,\n                new FieldDescriptor(SAHumanTaskInstance.class, keyProvider.getParentActivityInstanceKey()));\n        entityKeys.put(ArchivedHumanTaskInstanceSearchDescriptor.STATE_NAME,\n                new FieldDescriptor(SAHumanTaskInstance.class, keyProvider.getStateNameKey()));\n        entityKeys.put(ArchivedHumanTaskInstanceSearchDescriptor.ASSIGNEE_ID,\n                new FieldDescriptor(SAHumanTaskInstance.class, keyProvider.getAssigneeIdKey()));\n        entityKeys.put(ArchivedHumanTaskInstanceSearchDescriptor.DISPLAY_NAME,\n                new FieldDescriptor(SAHumanTaskInstance.class, keyProvider.getDisplayNameKey()));\n        entityKeys.put(ArchivedHumanTaskInstanceSearchDescriptor.REACHED_STATE_DATE,\n                new FieldDescriptor(SAHumanTaskInstance.class, keyProvider.getReachedStateDateKey()));\n        entityKeys.put(ArchivedHumanTaskInstanceSearchDescriptor.TERMINAL,\n                new FieldDescriptor(SAHumanTaskInstance.class, keyProvider.getTerminalKey()));\n        entityKeys.put(ArchivedHumanTaskInstanceSearchDescriptor.ARCHIVE_DATE,\n                new FieldDescriptor(SAHumanTaskInstance.class, keyProvider.getArchivedDateKey()));\n\n        humanTaskInstanceAllFields = new HashMap<Class<? extends PersistentObject>, Set<String>>(1);\n        final Set<String> humanFields = new HashSet<String>(2);\n        humanFields.add(keyProvider.getNameKey());\n        humanFields.add(keyProvider.getDisplayNameKey());\n        humanTaskInstanceAllFields.put(SAHumanTaskInstance.class, humanFields);\n    }\n\n    @Override\n    protected Map<String, FieldDescriptor> getEntityKeys() {\n        return entityKeys;\n    }\n\n    @Override\n    protected Map<Class<? extends PersistentObject>, Set<String>> getAllFields() {\n        return humanTaskInstanceAllFields;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchArchivedProcessInstancesDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport static org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor.*;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAProcessInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SUserTaskInstanceBuilderFactory;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor;\n\n/**\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class SearchArchivedProcessInstancesDescriptor extends SearchEntityDescriptor {\n\n    protected final Map<String, FieldDescriptor> searchEntityKeys;\n\n    protected final Map<Class<? extends PersistentObject>, Set<String>> archivedProcessInstanceAllFields;\n\n    protected final Set<String> processInstanceFields;\n\n    public SearchArchivedProcessInstancesDescriptor() {\n        final SAProcessInstanceBuilderFactory instanceBuilder = BuilderFactory\n                .get(SAProcessInstanceBuilderFactory.class);\n        final SUserTaskInstanceBuilderFactory sUserTaskInstanceBuilder = BuilderFactory\n                .get(SUserTaskInstanceBuilderFactory.class);\n\n        searchEntityKeys = new HashMap<>();\n        searchEntityKeys.put(NAME, new FieldDescriptor(SAProcessInstance.class, instanceBuilder.getNameKey()));\n        searchEntityKeys.put(PROCESS_DEFINITION_ID,\n                new FieldDescriptor(SAProcessInstance.class, instanceBuilder.getProcessDefinitionIdKey()));\n        searchEntityKeys.put(ID, new FieldDescriptor(SAProcessInstance.class, instanceBuilder.getIdKey()));\n        searchEntityKeys.put(ROOT_PROCESS_INSTANCE_ID,\n                new FieldDescriptor(SAProcessInstance.class, instanceBuilder.getRootProcessInstanceIdKey()));\n        searchEntityKeys.put(STARTED_BY,\n                new FieldDescriptor(SAProcessInstance.class, instanceBuilder.getStartedByKey()));\n        searchEntityKeys.put(STARTED_BY_SUBSTITUTE,\n                new FieldDescriptor(SAProcessInstance.class, instanceBuilder.getStartedBySubstituteKey()));\n        searchEntityKeys.put(START_DATE,\n                new FieldDescriptor(SAProcessInstance.class, instanceBuilder.getStartDateKey()));\n        searchEntityKeys.put(END_DATE, new FieldDescriptor(SAProcessInstance.class, instanceBuilder.getEndDateKey()));\n        searchEntityKeys.put(STATE_ID, new FieldDescriptor(SAProcessInstance.class, instanceBuilder.getStateIdKey()));\n        searchEntityKeys.put(SOURCE_OBJECT_ID,\n                new FieldDescriptor(SAProcessInstance.class, instanceBuilder.getSourceObjectIdKey()));\n        searchEntityKeys.put(LAST_UPDATE,\n                new FieldDescriptor(SAProcessInstance.class, instanceBuilder.getLastUpdateKey()));\n        searchEntityKeys.put(ARCHIVE_DATE,\n                new FieldDescriptor(SAProcessInstance.class, instanceBuilder.getArchiveDateKey()));\n        searchEntityKeys.put(CALLER_ID,\n                new FieldDescriptor(SAProcessInstance.class, instanceBuilder.getCallerIdKey()));\n        searchEntityKeys\n                .put(USER_ID, new FieldDescriptor(SProcessSupervisor.class,\n                        SProcessSupervisor.USER_ID_KEY));\n        searchEntityKeys.put(GROUP_ID,\n                new FieldDescriptor(SProcessSupervisor.class,\n                        SProcessSupervisor.GROUP_ID_KEY));\n        searchEntityKeys\n                .put(ROLE_ID, new FieldDescriptor(SProcessSupervisor.class,\n                        SProcessSupervisor.ROLE_ID_KEY));\n        searchEntityKeys.put(ASSIGNEE_ID,\n                new FieldDescriptor(SUserTaskInstance.class, sUserTaskInstanceBuilder.getAssigneeIdKey()));\n\n        searchEntityKeys.put(STRING_INDEX_1,\n                new FieldDescriptor(SAProcessInstance.class, STRING_INDEX_1));\n        searchEntityKeys.put(STRING_INDEX_2,\n                new FieldDescriptor(SAProcessInstance.class, STRING_INDEX_2));\n        searchEntityKeys.put(STRING_INDEX_3,\n                new FieldDescriptor(SAProcessInstance.class, STRING_INDEX_3));\n        searchEntityKeys.put(STRING_INDEX_4,\n                new FieldDescriptor(SAProcessInstance.class, STRING_INDEX_4));\n        searchEntityKeys.put(STRING_INDEX_5,\n                new FieldDescriptor(SAProcessInstance.class, STRING_INDEX_5));\n\n        archivedProcessInstanceAllFields = new HashMap<>();\n        processInstanceFields = new HashSet<>();\n        processInstanceFields.add(instanceBuilder.getNameKey());\n        processInstanceFields.add(STRING_INDEX_1);\n        processInstanceFields.add(STRING_INDEX_2);\n        processInstanceFields.add(STRING_INDEX_3);\n        processInstanceFields.add(STRING_INDEX_4);\n        processInstanceFields.add(STRING_INDEX_5);\n        archivedProcessInstanceAllFields.put(SAProcessInstance.class, processInstanceFields);\n    }\n\n    @Override\n    protected Map<String, FieldDescriptor> getEntityKeys() {\n        return searchEntityKeys;\n    }\n\n    @Override\n    protected Map<Class<? extends PersistentObject>, Set<String>> getAllFields() {\n        return archivedProcessInstanceAllFields;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchCommandDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.command.CommandSearchDescriptor;\nimport org.bonitasoft.engine.command.model.SCommand;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n */\npublic class SearchCommandDescriptor extends SearchEntityDescriptor {\n\n    private final Map<String, FieldDescriptor> commandKeys;\n\n    private final Map<Class<? extends PersistentObject>, Set<String>> commandAllFields;\n\n    SearchCommandDescriptor() {\n        commandKeys = new HashMap<String, FieldDescriptor>(5);\n        commandKeys.put(CommandSearchDescriptor.ID, new FieldDescriptor(SCommand.class, SCommand.ID));\n        commandKeys.put(CommandSearchDescriptor.NAME, new FieldDescriptor(SCommand.class, SCommand.NAME));\n        commandKeys.put(CommandSearchDescriptor.DESCRIPTION, new FieldDescriptor(SCommand.class, SCommand.DESCRIPTION));\n        commandKeys.put(CommandSearchDescriptor.IMPLEMENTATION,\n                new FieldDescriptor(SCommand.class, SCommand.IMPLEMENTATION));\n        commandKeys.put(CommandSearchDescriptor.SYSTEM, new FieldDescriptor(SCommand.class, SCommand.SYSTEM));\n\n        commandAllFields = new HashMap<Class<? extends PersistentObject>, Set<String>>(1);\n        final Set<String> commandFields = new HashSet<String>(5);\n        commandFields.add(SCommand.NAME);\n        commandFields.add(SCommand.DESCRIPTION);\n        commandFields.add(SCommand.IMPLEMENTATION);\n        commandAllFields.put(SCommand.class, commandFields);\n    }\n\n    @Override\n    protected Map<String, FieldDescriptor> getEntityKeys() {\n        return commandKeys;\n    }\n\n    @Override\n    protected Map<Class<? extends PersistentObject>, Set<String>> getAllFields() {\n        return commandAllFields;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchCommentDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.bpm.comment.SearchCommentsDescriptor;\nimport org.bonitasoft.engine.core.process.comment.model.SComment;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Hongwen Zang\n * @author Matthieu Chaffotte\n */\npublic class SearchCommentDescriptor extends SearchEntityDescriptor {\n\n    private final Map<String, FieldDescriptor> commentKeys;\n\n    private final Map<Class<? extends PersistentObject>, Set<String>> commentAllFields;\n\n    public SearchCommentDescriptor() {\n        commentKeys = new HashMap<>();\n        commentKeys.put(SearchCommentsDescriptor.PROCESS_INSTANCE_ID,\n                new FieldDescriptor(SComment.class, SComment.PROCESSINSTANCEID_KEY));\n        commentKeys.put(SearchCommentsDescriptor.POSTED_BY_ID,\n                new FieldDescriptor(SComment.class, SComment.USERID_KEY));\n        commentKeys.put(SearchCommentsDescriptor.ID, new FieldDescriptor(SComment.class, SComment.ID_KEY));\n        commentKeys.put(SearchCommentsDescriptor.POSTDATE, new FieldDescriptor(SComment.class, SComment.POSTDATE_KEY));\n        commentKeys.put(SearchCommentsDescriptor.CONTENT, new FieldDescriptor(SComment.class, SComment.CONTENT_KEY));\n        commentKeys.put(SearchCommentsDescriptor.USER_NAME, new FieldDescriptor(SUser.class, SUser.USER_NAME));\n        commentAllFields = new HashMap<>();\n        final Set<String> commentFields = new HashSet<>();\n        commentFields.add(SComment.CONTENT_KEY);\n        commentAllFields.put(SComment.class, commentFields);\n    }\n\n    @Override\n    protected Map<String, FieldDescriptor> getEntityKeys() {\n        return commentKeys;\n    }\n\n    @Override\n    protected Map<Class<? extends PersistentObject>, Set<String>> getAllFields() {\n        return commentAllFields;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchConnectorInstanceDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorInstancesSearchDescriptor;\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstance;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SearchConnectorInstanceDescriptor extends SearchEntityDescriptor {\n\n    private final Map<String, FieldDescriptor> searchEntityKeys;\n\n    private final Map<Class<? extends PersistentObject>, Set<String>> connectorInstanceAllFields;\n\n    SearchConnectorInstanceDescriptor() {\n        searchEntityKeys = new HashMap<>(7);\n        searchEntityKeys.put(ConnectorInstancesSearchDescriptor.NAME,\n                new FieldDescriptor(SConnectorInstance.class, SConnectorInstance.NAME_KEY));\n        searchEntityKeys.put(ConnectorInstancesSearchDescriptor.ACTIVATION_EVENT,\n                new FieldDescriptor(SConnectorInstance.class, SConnectorInstance.ACTIVATION_EVENT_KEY));\n        searchEntityKeys.put(ConnectorInstancesSearchDescriptor.CONNECTOR_DEFINITION_ID,\n                new FieldDescriptor(SConnectorInstance.class,\n                        SConnectorInstance.CONNECTOR_ID_KEY));\n        searchEntityKeys.put(ConnectorInstancesSearchDescriptor.CONNECTOR_DEFINITION_VERSION,\n                new FieldDescriptor(SConnectorInstance.class,\n                        SConnectorInstance.VERSION_KEY));\n        searchEntityKeys.put(ConnectorInstancesSearchDescriptor.CONTAINER_ID,\n                new FieldDescriptor(SConnectorInstance.class, SConnectorInstance.CONTAINER_ID_KEY));\n        searchEntityKeys.put(ConnectorInstancesSearchDescriptor.CONTAINER_TYPE,\n                new FieldDescriptor(SConnectorInstance.class, SConnectorInstance.CONTAINER_TYPE_KEY));\n        searchEntityKeys.put(ConnectorInstancesSearchDescriptor.STATE,\n                new FieldDescriptor(SConnectorInstance.class, SConnectorInstance.STATE_KEY));\n        searchEntityKeys.put(ConnectorInstancesSearchDescriptor.EXECUTION_ORDER,\n                new FieldDescriptor(SConnectorInstance.class, SConnectorInstance.EXECUTION_ORDER));\n\n        connectorInstanceAllFields = new HashMap<>(1);\n        final Set<String> connectorFields = new HashSet<String>(2);\n        connectorFields.add(SConnectorInstance.NAME_KEY);\n        connectorFields.add(SConnectorInstance.CONNECTOR_ID_KEY);\n\n        connectorInstanceAllFields.put(SConnectorInstance.class, connectorFields);\n    }\n\n    @Override\n    protected Map<String, FieldDescriptor> getEntityKeys() {\n        return searchEntityKeys;\n    }\n\n    @Override\n    protected Map<Class<? extends PersistentObject>, Set<String>> getAllFields() {\n        return connectorInstanceAllFields;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchCustomUserInfoValueDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstance;\nimport org.bonitasoft.engine.identity.CustomUserInfoValueSearchDescriptor;\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoValue;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Vincent Elcrin\n */\npublic class SearchCustomUserInfoValueDescriptor extends SearchEntityDescriptor {\n\n    private final Map<String, FieldDescriptor> searchableKeys;\n\n    private final Map<Class<? extends PersistentObject>, Set<String>> allFields;\n\n    public SearchCustomUserInfoValueDescriptor() {\n        searchableKeys = new HashMap<String, FieldDescriptor>(3);\n        searchableKeys.put(CustomUserInfoValueSearchDescriptor.DEFINITION_ID,\n                new FieldDescriptor(SCustomUserInfoValue.class, SCustomUserInfoValue.DEFINITION_ID));\n        searchableKeys.put(CustomUserInfoValueSearchDescriptor.USER_ID,\n                new FieldDescriptor(SCustomUserInfoValue.class, SCustomUserInfoValue.USER_ID));\n        searchableKeys.put(CustomUserInfoValueSearchDescriptor.VALUE,\n                new FieldDescriptor(SCustomUserInfoValue.class, SCustomUserInfoValue.VALUE));\n\n        allFields = new HashMap<Class<? extends PersistentObject>, Set<String>>(1);\n        final Set<String> fields = new HashSet<String>(2);\n        fields.add(SCustomUserInfoValue.DEFINITION_ID);\n        fields.add(SCustomUserInfoValue.USER_ID);\n        fields.add(SCustomUserInfoValue.VALUE);\n\n        allFields.put(SConnectorInstance.class, fields);\n    }\n\n    @Override\n    protected Map<String, FieldDescriptor> getEntityKeys() {\n        return searchableKeys;\n    }\n\n    @Override\n    protected Map<Class<? extends PersistentObject>, Set<String>> getAllFields() {\n        return allFields;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchDocumentDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.bpm.document.DocumentsSearchDescriptor;\nimport org.bonitasoft.engine.core.document.model.SDocument;\nimport org.bonitasoft.engine.core.document.model.SMappedDocument;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Zhang Bole\n * @author Baptiste Mesta\n */\npublic class SearchDocumentDescriptor extends SearchEntityDescriptor {\n\n    public static final String DOCUMENT_PREFIX = \"document.\";\n    private final Map<String, FieldDescriptor> searchEntityKeys;\n\n    private final Map<Class<? extends PersistentObject>, Set<String>> documentAllFields;\n\n    SearchDocumentDescriptor() {\n        searchEntityKeys = new HashMap<>(9);\n\n        searchEntityKeys.put(DocumentsSearchDescriptor.CONTENT_STORAGE_ID,\n                new FieldDescriptor(SMappedDocument.class, \"documentId\"));\n        searchEntityKeys.put(DocumentsSearchDescriptor.DOCUMENT_AUTHOR,\n                new FieldDescriptor(SMappedDocument.class, DOCUMENT_PREFIX + SDocument.AUTHOR));\n        searchEntityKeys.put(DocumentsSearchDescriptor.DOCUMENT_CONTENT_FILENAME,\n                new FieldDescriptor(SMappedDocument.class, DOCUMENT_PREFIX + SDocument.FILENAME));\n        searchEntityKeys.put(DocumentsSearchDescriptor.DOCUMENT_CONTENT_MIMETYPE,\n                new FieldDescriptor(SMappedDocument.class, DOCUMENT_PREFIX + SDocument.MIMETYPE));\n        searchEntityKeys.put(DocumentsSearchDescriptor.DOCUMENT_CREATIONDATE,\n                new FieldDescriptor(SMappedDocument.class, DOCUMENT_PREFIX + SDocument.CREATION_DATE));\n        searchEntityKeys.put(DocumentsSearchDescriptor.DOCUMENT_HAS_CONTENT,\n                new FieldDescriptor(SMappedDocument.class, DOCUMENT_PREFIX + SDocument.HAS_CONTENT));\n        searchEntityKeys.put(DocumentsSearchDescriptor.DOCUMENT_NAME,\n                new FieldDescriptor(SMappedDocument.class, SDocument.NAME));\n        searchEntityKeys.put(DocumentsSearchDescriptor.DOCUMENT_DESCRIPTION,\n                new FieldDescriptor(SMappedDocument.class, SDocument.DESCRIPTION));\n        searchEntityKeys.put(DocumentsSearchDescriptor.DOCUMENT_VERSION,\n                new FieldDescriptor(SMappedDocument.class, SDocument.VERSION));\n        searchEntityKeys.put(DocumentsSearchDescriptor.LIST_INDEX,\n                new FieldDescriptor(SMappedDocument.class, SDocument.INDEX));\n        searchEntityKeys.put(DocumentsSearchDescriptor.DOCUMENT_URL,\n                new FieldDescriptor(SMappedDocument.class, DOCUMENT_PREFIX + SDocument.URL));\n        searchEntityKeys.put(DocumentsSearchDescriptor.PROCESSINSTANCE_ID,\n                new FieldDescriptor(SMappedDocument.class, \"processInstanceId\"));\n\n        documentAllFields = new HashMap<>(1);\n        final Set<String> documentFields = new HashSet<>(8);\n        documentFields.add(DOCUMENT_PREFIX + SDocument.FILENAME);\n        documentFields.add(DOCUMENT_PREFIX + SDocument.MIMETYPE);\n        documentFields.add(SDocument.NAME);\n        documentFields.add(SDocument.DESCRIPTION);\n        documentFields.add(DOCUMENT_PREFIX + SDocument.URL);\n        documentAllFields.put(SMappedDocument.class, documentFields);\n    }\n\n    @Override\n    protected Map<String, FieldDescriptor> getEntityKeys() {\n        return searchEntityKeys;\n    }\n\n    @Override\n    protected Map<Class<? extends PersistentObject>, Set<String>> getAllFields() {\n        return documentAllFields;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchEntitiesDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\n/**\n * @author Matthieu Chaffotte\n * @author Zhang Bole\n * @author Yanyan Liu\n * @author Celine Souchet\n */\npublic class SearchEntitiesDescriptor {\n\n    private final SearchUserDescriptor searchUserDescriptor;\n\n    private final SearchRoleDescriptor searchRoleDescriptor;\n\n    private final SearchGroupDescriptor searchGroupDescriptor;\n\n    private final SearchCustomUserInfoValueDescriptor searchCustomUserInfoValueDescriptor;\n\n    private final SearchProcessInstanceDescriptor searchProcessInstanceDescriptor;\n\n    private final SearchArchivedProcessInstancesDescriptor searchArchivedProcessInstanceDescriptor;\n\n    private final SearchHumanTaskInstanceDescriptor searchHumanTaskInstanceDescriptor;\n\n    private final SearchArchivedHumanTaskInstanceDescriptor searchArchivedHumanTaskInstanceDescriptor;\n\n    private final SearchProcessDefinitionsDescriptor searchProcessDefinitionsDescriptor;\n\n    private final SearchCommentDescriptor searchCommentDescriptor;\n\n    private final SearchDocumentDescriptor searchDocumentDescriptor;\n\n    private final SearchArchivedDocumentDescriptor searchArchivedDocumentDescriptor;\n\n    private final SearchActivityInstanceDescriptor searchActivityInstanceDescriptor;\n\n    private final SearchFlowNodeInstanceDescriptor searchFlowNodeInstanceDescriptor;\n\n    private final SearchArchivedActivityInstanceDescriptor searchArchivedActivityInstanceDescriptor;\n\n    private final SearchArchivedCommentsDescriptor searchArchivedCommentsDescriptor;\n\n    private final SearchArchivedConnectorInstanceDescriptor searchArchivedConnectorInstanceDescriptor;\n\n    private final SearchCommandDescriptor searchCommandDescriptor;\n\n    private final SearchArchivedFlowNodeInstanceDescriptor searchArchivedFlowNodeInstanceDescriptor;\n\n    private final SearchConnectorInstanceDescriptor searchConnectorInstanceDescriptor;\n\n    private final SearchProfileDescriptor searchProfileDescriptor;\n\n    private final SearchFormMappingDescriptor searchFormMappingDescriptor;\n\n    private final SearchProfileMemberUserDescriptor searchProfileMemberUserDescriptor;\n\n    private final SearchProfileMemberGroupDescriptor searchProfileMemberGroupDescriptor;\n\n    private final SearchProfileMemberRoleDescriptor searchProfileMemberRoleDescriptor;\n\n    private final SearchProfileMemberRoleAndGroupDescriptor searchProfileMemberRoleAndGroupDescriptor;\n\n    private final SearchEventTriggerInstanceDescriptor searchEventTriggerInstanceDescriptor;\n\n    private final SearchPageDescriptor searchPageDescriptor;\n\n    private final SearchApplicationDescriptor searchApplicationDescriptor;\n\n    private final SearchApplicationMenuDescriptor searchApplicationMenuDescriptor;\n\n    private final SearchApplicationPageDescriptor searchApplicationPageDescriptor;\n\n    private final SearchMessageInstanceDescriptor searchMessageInstanceDescriptor;\n\n    public SearchEntitiesDescriptor() {\n        searchUserDescriptor = new SearchUserDescriptor();\n        searchRoleDescriptor = new SearchRoleDescriptor();\n        searchGroupDescriptor = new SearchGroupDescriptor();\n        searchCustomUserInfoValueDescriptor = new SearchCustomUserInfoValueDescriptor();\n        searchProcessInstanceDescriptor = new SearchProcessInstanceDescriptor();\n        searchArchivedProcessInstanceDescriptor = new SearchArchivedProcessInstancesDescriptor();\n        searchHumanTaskInstanceDescriptor = new SearchHumanTaskInstanceDescriptor();\n        searchArchivedHumanTaskInstanceDescriptor = new SearchArchivedHumanTaskInstanceDescriptor();\n        searchProcessDefinitionsDescriptor = new SearchProcessDefinitionsDescriptor();\n        searchCommentDescriptor = new SearchCommentDescriptor();\n        searchConnectorInstanceDescriptor = new SearchConnectorInstanceDescriptor();\n        searchDocumentDescriptor = new SearchDocumentDescriptor();\n        searchArchivedDocumentDescriptor = new SearchArchivedDocumentDescriptor();\n        searchActivityInstanceDescriptor = new SearchActivityInstanceDescriptor();\n        searchArchivedActivityInstanceDescriptor = new SearchArchivedActivityInstanceDescriptor();\n        searchArchivedCommentsDescriptor = new SearchArchivedCommentsDescriptor();\n        searchArchivedConnectorInstanceDescriptor = new SearchArchivedConnectorInstanceDescriptor();\n        searchFlowNodeInstanceDescriptor = new SearchFlowNodeInstanceDescriptor();\n        searchCommandDescriptor = new SearchCommandDescriptor();\n        searchArchivedFlowNodeInstanceDescriptor = new SearchArchivedFlowNodeInstanceDescriptor();\n        searchProfileDescriptor = new SearchProfileDescriptor();\n        searchProfileMemberUserDescriptor = new SearchProfileMemberUserDescriptor();\n        searchProfileMemberGroupDescriptor = new SearchProfileMemberGroupDescriptor();\n        searchProfileMemberRoleDescriptor = new SearchProfileMemberRoleDescriptor();\n        searchProfileMemberRoleAndGroupDescriptor = new SearchProfileMemberRoleAndGroupDescriptor();\n        searchEventTriggerInstanceDescriptor = new SearchEventTriggerInstanceDescriptor();\n        searchPageDescriptor = new SearchPageDescriptor();\n        searchApplicationDescriptor = new SearchApplicationDescriptor();\n        searchApplicationMenuDescriptor = new SearchApplicationMenuDescriptor();\n        searchApplicationPageDescriptor = new SearchApplicationPageDescriptor();\n        searchFormMappingDescriptor = new SearchFormMappingDescriptor();\n        searchMessageInstanceDescriptor = new SearchMessageInstanceDescriptor();\n    }\n\n    public SearchUserDescriptor getSearchUserDescriptor() {\n        return searchUserDescriptor;\n    }\n\n    public SearchRoleDescriptor getSearchRoleDescriptor() {\n        return searchRoleDescriptor;\n    }\n\n    public SearchGroupDescriptor getSearchGroupDescriptor() {\n        return searchGroupDescriptor;\n    }\n\n    public SearchCustomUserInfoValueDescriptor getSearchCustomUserInfoValueDescriptor() {\n        return searchCustomUserInfoValueDescriptor;\n    }\n\n    public SearchProcessInstanceDescriptor getSearchProcessInstanceDescriptor() {\n        return searchProcessInstanceDescriptor;\n    }\n\n    public SearchArchivedProcessInstancesDescriptor getSearchArchivedProcessInstanceDescriptor() {\n        return searchArchivedProcessInstanceDescriptor;\n    }\n\n    public SearchHumanTaskInstanceDescriptor getSearchHumanTaskInstanceDescriptor() {\n        return searchHumanTaskInstanceDescriptor;\n    }\n\n    public SearchArchivedHumanTaskInstanceDescriptor getSearchArchivedHumanTaskInstanceDescriptor() {\n        return searchArchivedHumanTaskInstanceDescriptor;\n    }\n\n    public SearchProcessDefinitionsDescriptor getSearchProcessDefinitionsDescriptor() {\n        return searchProcessDefinitionsDescriptor;\n    }\n\n    public SearchCommentDescriptor getSearchCommentDescriptor() {\n        return searchCommentDescriptor;\n    }\n\n    public SearchDocumentDescriptor getSearchDocumentDescriptor() {\n        return searchDocumentDescriptor;\n    }\n\n    public SearchArchivedDocumentDescriptor getSearchArchivedDocumentDescriptor() {\n        return searchArchivedDocumentDescriptor;\n    }\n\n    public SearchActivityInstanceDescriptor getSearchActivityInstanceDescriptor() {\n        return searchActivityInstanceDescriptor;\n    }\n\n    public SearchFlowNodeInstanceDescriptor getSearchFlowNodeInstanceDescriptor() {\n        return searchFlowNodeInstanceDescriptor;\n    }\n\n    public SearchArchivedActivityInstanceDescriptor getSearchArchivedActivityInstanceDescriptor() {\n        return searchArchivedActivityInstanceDescriptor;\n    }\n\n    public SearchArchivedCommentsDescriptor getSearchArchivedCommentsDescriptor() {\n        return searchArchivedCommentsDescriptor;\n    }\n\n    public SearchArchivedConnectorInstanceDescriptor getSearchArchivedConnectorInstanceDescriptor() {\n        return searchArchivedConnectorInstanceDescriptor;\n    }\n\n    public SearchCommandDescriptor getSearchCommandDescriptor() {\n        return searchCommandDescriptor;\n    }\n\n    public SearchArchivedFlowNodeInstanceDescriptor getSearchArchivedFlowNodeInstanceDescriptor() {\n        return searchArchivedFlowNodeInstanceDescriptor;\n    }\n\n    public SearchConnectorInstanceDescriptor getSearchConnectorInstanceDescriptor() {\n        return searchConnectorInstanceDescriptor;\n    }\n\n    public SearchProfileDescriptor getSearchProfileDescriptor() {\n        return searchProfileDescriptor;\n    }\n\n    public SearchFormMappingDescriptor getSearchFormMappingDescriptor() {\n        return searchFormMappingDescriptor;\n    }\n\n    public SearchProfileMemberUserDescriptor getSearchProfileMemberUserDescriptor() {\n        return searchProfileMemberUserDescriptor;\n    }\n\n    public SearchProfileMemberGroupDescriptor getSearchProfileMemberGroupDescriptor() {\n        return searchProfileMemberGroupDescriptor;\n    }\n\n    public SearchProfileMemberRoleDescriptor getSearchProfileMemberRoleDescriptor() {\n        return searchProfileMemberRoleDescriptor;\n    }\n\n    public SearchProfileMemberRoleAndGroupDescriptor getSearchProfileMemberRoleAndGroupDescriptor() {\n        return searchProfileMemberRoleAndGroupDescriptor;\n    }\n\n    public SearchEventTriggerInstanceDescriptor getSearchEventTriggerInstanceDescriptor() {\n        return searchEventTriggerInstanceDescriptor;\n    }\n\n    public SearchPageDescriptor getSearchPageDescriptor() {\n        return searchPageDescriptor;\n    }\n\n    public SearchApplicationDescriptor getSearchApplicationDescriptor() {\n        return searchApplicationDescriptor;\n    }\n\n    public SearchApplicationMenuDescriptor getSearchApplicationMenuDescriptor() {\n        return searchApplicationMenuDescriptor;\n    }\n\n    public SearchApplicationPageDescriptor getSearchApplicationPageDescriptor() {\n        return searchApplicationPageDescriptor;\n    }\n\n    public SearchMessageInstanceDescriptor getSearchMessageInstanceDescriptor() {\n        return searchMessageInstanceDescriptor;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchEntityDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.StringTokenizer;\n\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SearchFields;\nimport org.bonitasoft.engine.persistence.search.FilterOperationType;\nimport org.bonitasoft.engine.search.Sort;\nimport org.bonitasoft.engine.search.impl.SearchFilter;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic abstract class SearchEntityDescriptor {\n\n    public FilterOption getEntityFilter(final SearchFilter filter) {\n        final String key = filter.getField();\n        final FieldDescriptor fieldDescriptor = getEntityKeys().get(key);\n        if (fieldDescriptor == null && !filter.isUndefinedFieldNameAuthorized()) {\n            throw new IllegalArgumentException(\"the field '\" + key + \"' is unknown for the entity searched using \"\n                    + this.getClass().getSimpleName());\n        }\n        return constructFilterOption(filter, fieldDescriptor);\n    }\n\n    public OrderByOption getEntityOrder(final Sort sort) throws SBonitaReadException {\n        final FieldDescriptor fieldDescriptor = getEntityKeys().get(sort.getField());\n        if (fieldDescriptor == null) {\n            throw new SBonitaReadException(\"Invalid sort key: \" + sort.getField());\n        }\n        final OrderByType type = OrderByType.valueOf(sort.getOrder().name());\n        return new OrderByOption(fieldDescriptor.getPersistentClass(), fieldDescriptor.getValue(), type);\n    }\n\n    public SearchFields getEntitySearchTerm(final String searchString) {\n        final StringTokenizer tokens = new StringTokenizer(searchString, \" \");\n        final ArrayList<String> terms = new ArrayList<>(tokens.countTokens());\n        while (tokens.hasMoreTokens()) {\n            final String term = tokens.nextToken();\n            terms.add(term);\n        }\n\n        return new SearchFields(terms, getAllFields());\n    }\n\n    protected abstract Map<String, FieldDescriptor> getEntityKeys();\n\n    protected abstract Map<Class<? extends PersistentObject>, Set<String>> getAllFields();\n\n    /**\n     * Override this method to have specific conversion behavior from client filter value to server filter value .\n     *\n     * @param filterField\n     *        The field to filter\n     * @param filterValue\n     *        The initial value\n     * @return the converted filter value\n     * @since 6.4.0\n     */\n    protected Serializable convertFilterValue(final String filterField, final Serializable filterValue) {\n        return filterValue;\n    }\n\n    public FilterOption constructFilterOption(final SearchFilter filter, final FieldDescriptor fieldDescriptor) {\n        final Class<? extends PersistentObject> clazz = fieldDescriptor != null\n                ? fieldDescriptor.getPersistentClass()\n                : null;\n        final String fieldName = fieldDescriptor != null ? fieldDescriptor.getValue() : null;\n        final Serializable value = convertFilterValue(filter.getField(), filter.getValue());\n        switch (filter.getOperation()) {\n            case BETWEEN:\n                return new FilterOption(clazz, fieldName, convertFilterValue(filter.getField(), filter.getFrom()),\n                        convertFilterValue(filter.getField(),\n                                filter.getTo()));\n            case DIFFERENT:\n                return new FilterOption(clazz, fieldName, value, FilterOperationType.DIFFERENT);\n            case EQUALS:\n                return new FilterOption(clazz, fieldName, value, FilterOperationType.EQUALS);\n            case GREATER_OR_EQUAL:\n                return new FilterOption(clazz, fieldName, value, FilterOperationType.GREATER_OR_EQUALS);\n            case GREATER_THAN:\n                return new FilterOption(clazz, fieldName, value, FilterOperationType.GREATER);\n            case LESS_OR_EQUAL:\n                return new FilterOption(clazz, fieldName, value, FilterOperationType.LESS_OR_EQUALS);\n            case LESS_THAN:\n                return new FilterOption(clazz, fieldName, value, FilterOperationType.LESS);\n            case AND:\n                return new FilterOption(FilterOperationType.AND);\n            case OR:\n                return new FilterOption(FilterOperationType.OR);\n            case L_PARENTHESIS:\n                return new FilterOption(FilterOperationType.L_PARENTHESIS);\n            case R_PARENTHESIS:\n                return new FilterOption(FilterOperationType.R_PARENTHESIS);\n            default:\n                return null;\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchEventTriggerInstanceDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.bpm.flownode.EventTriggerInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.TimerEventTriggerInstanceSearchDescriptor;\nimport org.bonitasoft.engine.core.process.instance.model.event.SEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Celine Souchet\n * @version 6.4.0\n * @since 6.4.0\n */\npublic class SearchEventTriggerInstanceDescriptor extends SearchEntityDescriptor {\n\n    private final Map<String, FieldDescriptor> eventTriggerInstanceDescriptorKeys;\n\n    private final Map<Class<? extends PersistentObject>, Set<String>> eventTriggerInstanceDescriptorAllFields;\n\n    public SearchEventTriggerInstanceDescriptor() {\n        eventTriggerInstanceDescriptorKeys = new HashMap<String, FieldDescriptor>(6);\n        eventTriggerInstanceDescriptorKeys.put(EventTriggerInstanceSearchDescriptor.EVENT_INSTANCE_ID,\n                new FieldDescriptor(STimerEventTriggerInstance.class,\n                        \"eventInstanceId\"));\n        eventTriggerInstanceDescriptorKeys.put(TimerEventTriggerInstanceSearchDescriptor.EVENT_INSTANCE_NAME,\n                new FieldDescriptor(SEventInstance.class,\n                        \"name\"));\n        eventTriggerInstanceDescriptorKeys.put(TimerEventTriggerInstanceSearchDescriptor.EXECUTION_DATE,\n                new FieldDescriptor(STimerEventTriggerInstance.class,\n                        \"executionDate\"));\n\n        eventTriggerInstanceDescriptorAllFields = new HashMap<Class<? extends PersistentObject>, Set<String>>(\n                1);\n        eventTriggerInstanceDescriptorAllFields.put(SEventInstance.class, Collections.singleton(\"name\"));\n    }\n\n    @Override\n    protected Map<String, FieldDescriptor> getEntityKeys() {\n        return eventTriggerInstanceDescriptorKeys;\n    }\n\n    @Override\n    protected Map<Class<? extends PersistentObject>, Set<String>> getAllFields() {\n        return eventTriggerInstanceDescriptorAllFields;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchFlowNodeInstanceDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstanceSearchDescriptor;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SFlowNodeInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SUserTaskInstanceBuilderFactory;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SearchFlowNodeInstanceDescriptor extends SearchEntityDescriptor {\n\n    private final Map<String, FieldDescriptor> flowNodeInstanceDescriptorKeys;\n\n    private final Map<Class<? extends PersistentObject>, Set<String>> flowNodeInstanceDescriptorAllFields;\n\n    public SearchFlowNodeInstanceDescriptor() {\n        final SFlowNodeInstanceBuilderFactory keyProvider = BuilderFactory.get(SUserTaskInstanceBuilderFactory.class);\n        flowNodeInstanceDescriptorKeys = new HashMap<String, FieldDescriptor>(8);\n        flowNodeInstanceDescriptorKeys.put(FlowNodeInstanceSearchDescriptor.NAME,\n                new FieldDescriptor(SFlowNodeInstance.class, keyProvider.getNameKey()));\n        flowNodeInstanceDescriptorKeys.put(FlowNodeInstanceSearchDescriptor.STATE_NAME,\n                new FieldDescriptor(SFlowNodeInstance.class, keyProvider.getStateNameKey()));\n        flowNodeInstanceDescriptorKeys.put(FlowNodeInstanceSearchDescriptor.PROCESS_DEFINITION_ID,\n                new FieldDescriptor(SFlowNodeInstance.class,\n                        keyProvider.getProcessDefinitionKey()));\n        flowNodeInstanceDescriptorKeys.put(FlowNodeInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID,\n                new FieldDescriptor(SFlowNodeInstance.class,\n                        keyProvider.getParentProcessInstanceKey()));\n        flowNodeInstanceDescriptorKeys.put(FlowNodeInstanceSearchDescriptor.PARENT_ACTIVITY_INSTANCE_ID,\n                new FieldDescriptor(SFlowNodeInstance.class,\n                        keyProvider.getParentActivityInstanceKey()));\n        flowNodeInstanceDescriptorKeys.put(FlowNodeInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID,\n                new FieldDescriptor(SFlowNodeInstance.class,\n                        keyProvider.getRootProcessInstanceKey()));\n        flowNodeInstanceDescriptorKeys.put(FlowNodeInstanceSearchDescriptor.DISPLAY_NAME,\n                new FieldDescriptor(SFlowNodeInstance.class, keyProvider.getDisplayNameKey()));\n        flowNodeInstanceDescriptorKeys.put(FlowNodeInstanceSearchDescriptor.STATE_CATEGORY,\n                new FieldDescriptor(SFlowNodeInstance.class, keyProvider.getStateCategoryKey()));\n        flowNodeInstanceDescriptorKeys.put(FlowNodeInstanceSearchDescriptor.LAST_UPDATE_DATE,\n                new FieldDescriptor(SFlowNodeInstance.class, keyProvider.getLastUpdateDateKey()));\n\n        final Set<String> tasksInstanceFields = new HashSet<String>(2);\n        tasksInstanceFields.add(keyProvider.getNameKey());\n        tasksInstanceFields.add(keyProvider.getDisplayNameKey());\n        flowNodeInstanceDescriptorAllFields = new HashMap<Class<? extends PersistentObject>, Set<String>>(1);\n        flowNodeInstanceDescriptorAllFields.put(SFlowNodeInstance.class, tasksInstanceFields);\n    }\n\n    @Override\n    protected Map<String, FieldDescriptor> getEntityKeys() {\n        return flowNodeInstanceDescriptorKeys;\n    }\n\n    @Override\n    protected Map<Class<? extends PersistentObject>, Set<String>> getAllFields() {\n        return flowNodeInstanceDescriptorAllFields;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchFormMappingDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.core.form.SFormMapping;\nimport org.bonitasoft.engine.form.FormMappingSearchDescriptor;\nimport org.bonitasoft.engine.form.FormMappingType;\nimport org.bonitasoft.engine.page.SPageMapping;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.profile.model.SProfile;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SearchFormMappingDescriptor extends SearchEntityDescriptor {\n\n    private final Map<String, FieldDescriptor> searchEntityKeys;\n\n    private final Map<Class<? extends PersistentObject>, Set<String>> profileAllFields;\n\n    public SearchFormMappingDescriptor() {\n        searchEntityKeys = new HashMap<>(6);\n        searchEntityKeys.put(FormMappingSearchDescriptor.ID, new FieldDescriptor(SFormMapping.class, \"id\"));\n        searchEntityKeys.put(FormMappingSearchDescriptor.PROCESS_DEFINITION_ID,\n                new FieldDescriptor(SFormMapping.class, \"processDefinitionId\"));\n        searchEntityKeys.put(FormMappingSearchDescriptor.TYPE, new FieldDescriptor(SFormMapping.class, \"type\"));\n        searchEntityKeys.put(FormMappingSearchDescriptor.TASK, new FieldDescriptor(SFormMapping.class, \"task\"));\n        searchEntityKeys.put(FormMappingSearchDescriptor.PAGE_ID, new FieldDescriptor(SPageMapping.class, \"pageId\"));\n\n        profileAllFields = new HashMap<>(1);\n        profileAllFields.put(SProfile.class, new HashSet<String>(0));\n    }\n\n    @Override\n    protected Map<String, FieldDescriptor> getEntityKeys() {\n        return searchEntityKeys;\n    }\n\n    @Override\n    protected Map<Class<? extends PersistentObject>, Set<String>> getAllFields() {\n        return profileAllFields;\n    }\n\n    @Override\n    protected Serializable convertFilterValue(String filterField, Serializable filterValue) {\n        if (filterValue instanceof FormMappingType) {\n            return ((FormMappingType) filterValue).getId();\n        }\n        return super.convertFilterValue(filterField, filterValue);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchGroupDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.identity.GroupSearchDescriptor;\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SearchGroupDescriptor extends SearchEntityDescriptor {\n\n    private final Map<String, FieldDescriptor> groupKeys;\n\n    private final Map<Class<? extends PersistentObject>, Set<String>> groupAllFields;\n\n    SearchGroupDescriptor() {\n        groupKeys = new HashMap<>(4);\n        groupKeys.put(GroupSearchDescriptor.ID, new FieldDescriptor(SGroup.class, SGroup.ID));\n        groupKeys.put(GroupSearchDescriptor.NAME, new FieldDescriptor(SGroup.class, SGroup.NAME));\n        groupKeys.put(GroupSearchDescriptor.PARENT_PATH, new FieldDescriptor(SGroup.class, SGroup.PARENT_PATH));\n        groupKeys.put(GroupSearchDescriptor.DISPLAY_NAME,\n                new FieldDescriptor(SGroup.class, SGroup.DISPLAY_NAME));\n\n        groupAllFields = new HashMap<>(1);\n        final Set<String> groupFields = new HashSet<>(3);\n        groupFields.add(SGroup.NAME);\n        groupFields.add(SGroup.DISPLAY_NAME);\n        groupFields.add(SGroup.DESCRIPTION);\n        groupAllFields.put(SGroup.class, groupFields);\n    }\n\n    @Override\n    protected Map<String, FieldDescriptor> getEntityKeys() {\n        return groupKeys;\n    }\n\n    @Override\n    protected Map<Class<? extends PersistentObject>, Set<String>> getAllFields() {\n        return groupAllFields;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchHumanTaskInstanceDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.TaskPriority;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.STaskPriority;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SUserTaskInstanceBuilderFactory;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor;\n\n/**\n * @author Julien Mege\n * @author Zhang Bole\n * @author Matthieu Chaffotte\n */\npublic class SearchHumanTaskInstanceDescriptor extends SearchEntityDescriptor {\n\n    private final Map<String, FieldDescriptor> humanTaskInstanceDescriptorKeys;\n\n    private final Map<Class<? extends PersistentObject>, Set<String>> humanTaskInstanceDescriptorAllFields;\n\n    public SearchHumanTaskInstanceDescriptor() {\n        final SUserTaskInstanceBuilderFactory keyProvider = BuilderFactory.get(SUserTaskInstanceBuilderFactory.class);\n        humanTaskInstanceDescriptorKeys = new HashMap<>(13);\n        humanTaskInstanceDescriptorKeys.put(HumanTaskInstanceSearchDescriptor.NAME,\n                new FieldDescriptor(SHumanTaskInstance.class, keyProvider.getNameKey()));\n        humanTaskInstanceDescriptorKeys.put(HumanTaskInstanceSearchDescriptor.PRIORITY,\n                new FieldDescriptor(SHumanTaskInstance.class, keyProvider.getPriorityKey()));\n        humanTaskInstanceDescriptorKeys.put(HumanTaskInstanceSearchDescriptor.DUE_DATE,\n                new FieldDescriptor(SHumanTaskInstance.class, keyProvider.getExpectedEndDateKey()));\n        humanTaskInstanceDescriptorKeys.put(HumanTaskInstanceSearchDescriptor.STATE_NAME,\n                new FieldDescriptor(SHumanTaskInstance.class,\n                        keyProvider.getStateNameKey()));\n        humanTaskInstanceDescriptorKeys.put(HumanTaskInstanceSearchDescriptor.ASSIGNEE_ID,\n                new FieldDescriptor(SHumanTaskInstance.class,\n                        keyProvider.getAssigneeIdKey()));\n        humanTaskInstanceDescriptorKeys.put(HumanTaskInstanceSearchDescriptor.PROCESS_DEFINITION_ID,\n                new FieldDescriptor(SHumanTaskInstance.class,\n                        keyProvider.getProcessDefinitionKey()));\n        humanTaskInstanceDescriptorKeys.put(HumanTaskInstanceSearchDescriptor.PROCESS_INSTANCE_ID,\n                new FieldDescriptor(SHumanTaskInstance.class,\n                        keyProvider.getRootProcessInstanceKey()));\n        humanTaskInstanceDescriptorKeys.put(HumanTaskInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID,\n                new FieldDescriptor(SHumanTaskInstance.class, keyProvider.getRootProcessInstanceKey()));\n        humanTaskInstanceDescriptorKeys.put(HumanTaskInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID,\n                new FieldDescriptor(SHumanTaskInstance.class, keyProvider.getParentProcessInstanceKey()));\n        humanTaskInstanceDescriptorKeys.put(HumanTaskInstanceSearchDescriptor.PARENT_ACTIVITY_INSTANCE_ID,\n                new FieldDescriptor(SHumanTaskInstance.class,\n                        keyProvider.getParentActivityInstanceKey()));\n        humanTaskInstanceDescriptorKeys.put(HumanTaskInstanceSearchDescriptor.PARENT_CONTAINER_ID,\n                new FieldDescriptor(SHumanTaskInstance.class,\n                        keyProvider.getParentContainerIdKey()));\n        humanTaskInstanceDescriptorKeys.put(HumanTaskInstanceSearchDescriptor.DISPLAY_NAME,\n                new FieldDescriptor(SHumanTaskInstance.class,\n                        keyProvider.getDisplayNameKey()));\n        humanTaskInstanceDescriptorKeys.put(HumanTaskInstanceSearchDescriptor.REACHED_STATE_DATE,\n                new FieldDescriptor(SHumanTaskInstance.class,\n                        keyProvider.getReachStateDateKey()));\n        humanTaskInstanceDescriptorKeys.put(HumanTaskInstanceSearchDescriptor.USER_ID,\n                new FieldDescriptor(SProcessSupervisor.class, SProcessSupervisor.USER_ID_KEY));\n        humanTaskInstanceDescriptorKeys.put(HumanTaskInstanceSearchDescriptor.GROUP_ID,\n                new FieldDescriptor(SProcessSupervisor.class, SProcessSupervisor.GROUP_ID_KEY));\n        humanTaskInstanceDescriptorKeys.put(HumanTaskInstanceSearchDescriptor.ROLE_ID,\n                new FieldDescriptor(SProcessSupervisor.class, SProcessSupervisor.ROLE_ID_KEY));\n\n        humanTaskInstanceDescriptorAllFields = new HashMap<>(1);\n        final Set<String> tasksInstanceFields = new HashSet<>(3);\n        tasksInstanceFields.add(keyProvider.getNameKey());\n        tasksInstanceFields.add(keyProvider.getDisplayNameKey());\n        humanTaskInstanceDescriptorAllFields.put(SHumanTaskInstance.class, tasksInstanceFields);\n    }\n\n    @Override\n    protected Map<String, FieldDescriptor> getEntityKeys() {\n        return humanTaskInstanceDescriptorKeys;\n    }\n\n    @Override\n    protected Map<Class<? extends PersistentObject>, Set<String>> getAllFields() {\n        return humanTaskInstanceDescriptorAllFields;\n    }\n\n    @Override\n    protected Serializable convertFilterValue(final String filterField, final Serializable filterValue) {\n        // Convert value of the filter \"priority\" to the server STaskPriority, hiberate will handle that as a normal enum\n        if (HumanTaskInstanceSearchDescriptor.PRIORITY.equals(filterField)) {\n            if (filterValue instanceof Integer) {\n                return STaskPriority.fromOrdinal((Integer) filterValue);\n            } else if (filterValue instanceof String) {\n                return STaskPriority.valueOf((String) filterValue);\n            } else if (filterValue instanceof TaskPriority) {\n                return STaskPriority.valueOf(((TaskPriority) filterValue).name());\n            } else {\n                throw new IllegalArgumentException(\n                        \"Invalid value '\" + filterValue + \"' for filter on field 'priority'\");\n            }\n        }\n        return filterValue;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchMessageInstanceDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance;\nimport org.bonitasoft.engine.form.FormMappingType;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.profile.model.SProfile;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SearchMessageInstanceDescriptor extends SearchEntityDescriptor {\n\n    private final Map<String, FieldDescriptor> searchEntityKeys;\n\n    private final Map<Class<? extends PersistentObject>, Set<String>> profileAllFields;\n\n    public SearchMessageInstanceDescriptor() {\n        searchEntityKeys = new HashMap<>(1);\n        searchEntityKeys.put(\"messageName\", new FieldDescriptor(SMessageInstance.class, \"messageName\"));\n\n        profileAllFields = new HashMap<>(1);\n        profileAllFields.put(SProfile.class, new HashSet<String>(0));\n    }\n\n    @Override\n    protected Map<String, FieldDescriptor> getEntityKeys() {\n        return searchEntityKeys;\n    }\n\n    @Override\n    protected Map<Class<? extends PersistentObject>, Set<String>> getAllFields() {\n        return profileAllFields;\n    }\n\n    @Override\n    protected Serializable convertFilterValue(String filterField, Serializable filterValue) {\n        if (filterValue instanceof FormMappingType) {\n            return ((FormMappingType) filterValue).getId();\n        }\n        return super.convertFilterValue(filterField, filterValue);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchPageDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.page.PageSearchDescriptor;\nimport org.bonitasoft.engine.page.SPage;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SearchPageDescriptor extends SearchEntityDescriptor {\n\n    private final Map<String, FieldDescriptor> pageKeys;\n\n    private final Map<Class<? extends PersistentObject>, Set<String>> pageAllFields;\n\n    SearchPageDescriptor() {\n        pageKeys = new HashMap<>();\n        pageKeys.put(PageSearchDescriptor.ID, new FieldDescriptor(SPage.class, SPage.ID));\n        pageKeys.put(PageSearchDescriptor.NAME, new FieldDescriptor(SPage.class, SPage.NAME));\n        pageKeys.put(PageSearchDescriptor.PROVIDED, new FieldDescriptor(SPage.class, SPage.PROVIDED));\n        pageKeys.put(PageSearchDescriptor.INSTALLATION_DATE,\n                new FieldDescriptor(SPage.class, SPage.INSTALLATION_DATE));\n        pageKeys.put(PageSearchDescriptor.LAST_MODIFICATION_DATE,\n                new FieldDescriptor(SPage.class, SPage.LAST_MODIFICATION_DATE));\n        pageKeys.put(PageSearchDescriptor.INSTALLED_BY,\n                new FieldDescriptor(SPage.class, SPage.INSTALLED_BY));\n        pageKeys.put(PageSearchDescriptor.DISPLAY_NAME,\n                new FieldDescriptor(SPage.class, SPage.DISPLAY_NAME));\n        pageKeys.put(PageSearchDescriptor.CONTENT_TYPE,\n                new FieldDescriptor(SPage.class, SPage.CONTENT_TYPE));\n        pageKeys.put(PageSearchDescriptor.PROCESS_DEFINITION_ID,\n                new FieldDescriptor(SPage.class, SPage.PROCESS_DEFINITION_ID));\n\n        pageAllFields = new HashMap<>();\n\n        final Set<String> pageFields = new HashSet<>();\n        pageFields.add(SPage.NAME);\n        pageFields.add(SPage.DISPLAY_NAME);\n        pageAllFields.put(SPage.class, pageFields);\n    }\n\n    @Override\n    protected Serializable convertFilterValue(String filterField, Serializable filterValue) {\n        if (PageSearchDescriptor.PROCESS_DEFINITION_ID.equals(filterField) && filterValue == null) {\n            return 0;\n        }\n        return super.convertFilterValue(filterField, filterValue);\n    }\n\n    @Override\n    protected Map<String, FieldDescriptor> getEntityKeys() {\n        return pageKeys;\n    }\n\n    @Override\n    protected Map<Class<? extends PersistentObject>, Set<String>> getAllFields() {\n        return pageAllFields;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchProcessDefinitionsDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoSearchDescriptor;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.core.category.model.SProcessCategoryMapping;\nimport org.bonitasoft.engine.core.category.model.builder.SProcessCategoryMappingBuilderFactory;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Zhao Na\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class SearchProcessDefinitionsDescriptor extends SearchEntityDescriptor {\n\n    private final Map<String, FieldDescriptor> searchEntityKeys;\n\n    private final Map<Class<? extends PersistentObject>, Set<String>> processDefDeployInfos;\n\n    public SearchProcessDefinitionsDescriptor() {\n        searchEntityKeys = new HashMap<String, FieldDescriptor>(12);\n        searchEntityKeys.put(ProcessDeploymentInfoSearchDescriptor.ACTIVATION_STATE,\n                new FieldDescriptor(SProcessDefinitionDeployInfo.class,\n                        SProcessDefinitionDeployInfo.ACTIVATION_STATE_KEY));\n        searchEntityKeys.put(ProcessDeploymentInfoSearchDescriptor.CONFIGURATION_STATE,\n                new FieldDescriptor(SProcessDefinitionDeployInfo.class,\n                        SProcessDefinitionDeployInfo.CONFIGURATION_STATE_KEY));\n        searchEntityKeys\n                .put(ProcessDeploymentInfoSearchDescriptor.ID,\n                        new FieldDescriptor(SProcessDefinitionDeployInfo.class, SProcessDefinitionDeployInfo.ID_KEY));\n        searchEntityKeys.put(ProcessDeploymentInfoSearchDescriptor.NAME,\n                new FieldDescriptor(SProcessDefinitionDeployInfo.class, SProcessDefinitionDeployInfo.NAME_KEY));\n\n        searchEntityKeys.put(ProcessDeploymentInfoSearchDescriptor.VERSION,\n                new FieldDescriptor(SProcessDefinitionDeployInfo.class, SProcessDefinitionDeployInfo.VERSION_KEY));\n\n        searchEntityKeys.put(ProcessDeploymentInfoSearchDescriptor.DEPLOYMENT_DATE,\n                new FieldDescriptor(SProcessDefinitionDeployInfo.class,\n                        SProcessDefinitionDeployInfo.DEPLOYMENT_DATE_KEY));\n\n        searchEntityKeys.put(ProcessDeploymentInfoSearchDescriptor.DEPLOYED_BY,\n                new FieldDescriptor(SProcessDefinitionDeployInfo.class, SProcessDefinitionDeployInfo.DEPLOYED_BY_KEY));\n\n        searchEntityKeys.put(ProcessDeploymentInfoSearchDescriptor.PROCESS_ID,\n                new FieldDescriptor(SProcessDefinitionDeployInfo.class, SProcessDefinitionDeployInfo.PROCESS_ID_KEY));\n\n        searchEntityKeys.put(ProcessDeploymentInfoSearchDescriptor.DISPLAY_NAME,\n                new FieldDescriptor(SProcessDefinitionDeployInfo.class, SProcessDefinitionDeployInfo.DISPLAY_NAME_KEY));\n\n        searchEntityKeys.put(ProcessDeploymentInfoSearchDescriptor.LAST_UPDATE_DATE,\n                new FieldDescriptor(SProcessDefinitionDeployInfo.class,\n                        SProcessDefinitionDeployInfo.LAST_UPDATE_DATE_KEY));\n\n        final SProcessCategoryMappingBuilderFactory processCategoryMappingBuilderFactory = BuilderFactory\n                .get(SProcessCategoryMappingBuilderFactory.class);\n        searchEntityKeys.put(ProcessDeploymentInfoSearchDescriptor.CATEGORY_ID,\n                new FieldDescriptor(SProcessCategoryMapping.class,\n                        processCategoryMappingBuilderFactory.getCategoryIdKey()));\n\n        processDefDeployInfos = new HashMap<Class<? extends PersistentObject>, Set<String>>(1);\n        final Set<String> processFields = new HashSet<String>(3);\n        processFields.add(SProcessDefinitionDeployInfo.NAME_KEY);\n        processFields.add(SProcessDefinitionDeployInfo.DISPLAY_NAME_KEY);\n        processFields.add(SProcessDefinitionDeployInfo.VERSION_KEY);\n        processDefDeployInfos.put(SProcessDefinitionDeployInfo.class, processFields);\n    }\n\n    @Override\n    protected Map<String, FieldDescriptor> getEntityKeys() {\n        return searchEntityKeys;\n    }\n\n    @Override\n    protected Map<Class<? extends PersistentObject>, Set<String>> getAllFields() {\n        return processDefDeployInfos;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchProcessInstanceDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport static org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor.*;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SUserTaskInstanceBuilderFactory;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor;\n\n/**\n * @author Yanyan Liu\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class SearchProcessInstanceDescriptor extends SearchEntityDescriptor {\n\n    protected final Map<String, FieldDescriptor> searchEntityKeys;\n\n    private final Map<Class<? extends PersistentObject>, Set<String>> processInstanceAllFields;\n\n    protected final Set<String> processFields;\n\n    public SearchProcessInstanceDescriptor() {\n        final SUserTaskInstanceBuilderFactory sUserTaskInstanceBuilder = BuilderFactory\n                .get(SUserTaskInstanceBuilderFactory.class);\n\n        searchEntityKeys = new HashMap<>();\n        searchEntityKeys.put(NAME, new FieldDescriptor(SProcessInstance.class, SProcessInstance.NAME_KEY));\n        searchEntityKeys.put(PROCESS_DEFINITION_ID,\n                new FieldDescriptor(SProcessInstance.class, SProcessInstance.PROCESSDEF_ID_KEY));\n        searchEntityKeys.put(LAST_UPDATE,\n                new FieldDescriptor(SProcessInstance.class, SProcessInstance.LAST_UPDATE_KEY));\n        searchEntityKeys.put(START_DATE,\n                new FieldDescriptor(SProcessInstance.class, SProcessInstance.START_DATE_KEY));\n        searchEntityKeys.put(END_DATE, new FieldDescriptor(SProcessInstance.class, SProcessInstance.END_DATE_KEY));\n        searchEntityKeys.put(STATE_ID, new FieldDescriptor(SProcessInstance.class, SProcessInstance.STATE_ID_KEY));\n        searchEntityKeys.put(STATE_NAME, new FieldDescriptor(SProcessInstance.class, SProcessInstance.STATE_ID_KEY));\n        searchEntityKeys.put(ID, new FieldDescriptor(SProcessInstance.class, SProcessInstance.ID_KEY));\n        searchEntityKeys.put(ROOT_PROCESS_INSTANCE_ID, new FieldDescriptor(SProcessInstance.class,\n                SProcessInstance.ROOT_PROCESS_INSTANCE_ID_KEY));\n        searchEntityKeys.put(STARTED_BY,\n                new FieldDescriptor(SProcessInstance.class, SProcessInstance.STARTED_BY_KEY));\n        searchEntityKeys.put(CALLER_ID, new FieldDescriptor(SProcessInstance.class, SProcessInstance.CALLER_ID));\n        searchEntityKeys.put(PROCESS_SUPERVISOR_USER_ID, new FieldDescriptor(SProcessSupervisor.class,\n                SProcessSupervisor.USER_ID_KEY));\n        searchEntityKeys.put(PROCESS_SUPERVISOR_GROUP_ID, new FieldDescriptor(SProcessSupervisor.class,\n                SProcessSupervisor.GROUP_ID_KEY));\n        searchEntityKeys.put(PROCESS_SUPERVISOR_ROLE_ID, new FieldDescriptor(SProcessSupervisor.class,\n                SProcessSupervisor.ROLE_ID_KEY));\n        searchEntityKeys.put(ASSIGNEE_ID,\n                new FieldDescriptor(SUserTaskInstance.class, sUserTaskInstanceBuilder.getAssigneeIdKey()));\n\n        processInstanceAllFields = new HashMap<>();\n        processFields = new HashSet<>();\n        processFields.add(SProcessInstance.NAME_KEY);\n        processInstanceAllFields.put(SProcessInstance.class, processFields);\n\n        searchEntityKeys.put(ProcessInstanceSearchDescriptor.STRING_INDEX_1,\n                new FieldDescriptor(SProcessInstance.class, SProcessInstance.STRING_INDEX_1_KEY));\n        searchEntityKeys.put(ProcessInstanceSearchDescriptor.STRING_INDEX_2,\n                new FieldDescriptor(SProcessInstance.class, SProcessInstance.STRING_INDEX_2_KEY));\n        searchEntityKeys.put(ProcessInstanceSearchDescriptor.STRING_INDEX_3,\n                new FieldDescriptor(SProcessInstance.class, SProcessInstance.STRING_INDEX_3_KEY));\n        searchEntityKeys.put(ProcessInstanceSearchDescriptor.STRING_INDEX_4,\n                new FieldDescriptor(SProcessInstance.class, SProcessInstance.STRING_INDEX_4_KEY));\n        searchEntityKeys.put(ProcessInstanceSearchDescriptor.STRING_INDEX_5,\n                new FieldDescriptor(SProcessInstance.class, SProcessInstance.STRING_INDEX_5_KEY));\n\n        processFields.add(SProcessInstance.STRING_INDEX_1_KEY);\n        processFields.add(SProcessInstance.STRING_INDEX_2_KEY);\n        processFields.add(SProcessInstance.STRING_INDEX_3_KEY);\n        processFields.add(SProcessInstance.STRING_INDEX_4_KEY);\n        processFields.add(SProcessInstance.STRING_INDEX_5_KEY);\n    }\n\n    @Override\n    protected Map<String, FieldDescriptor> getEntityKeys() {\n        return searchEntityKeys;\n    }\n\n    @Override\n    protected Map<Class<? extends PersistentObject>, Set<String>> getAllFields() {\n        return processInstanceAllFields;\n    }\n\n    @Override\n    protected Serializable convertFilterValue(final String filterField, final Serializable filterValue) {\n        if (STATE_NAME.equals(filterField)) {\n            if (filterValue instanceof String) {\n                return ProcessInstanceState.valueOf((String) filterValue).getId();\n            } else if (filterValue instanceof ProcessInstanceState) {\n                return ((ProcessInstanceState) filterValue).getId();\n            } else {\n                throw new IllegalArgumentException(\"The state name must be a String or a ProcessInstanceState !!\");\n            }\n        }\n        return filterValue;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchProcessSupervisorDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.bpm.supervisor.ProcessSupervisorSearchDescriptor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor;\n\n/**\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n */\npublic class SearchProcessSupervisorDescriptor extends SearchEntityDescriptor {\n\n    private final Map<String, FieldDescriptor> fieldDescriptorMap;\n\n    private final Map<Class<? extends PersistentObject>, Set<String>> supervisorAllFields;\n\n    public SearchProcessSupervisorDescriptor() {\n        // final SUserBuilder userBuilder = identityModelBuilder.getUserBuilder();\n        // final GroupBuilder groupBuilder = identityModelBuilder.getGroupBuilder();\n        // final RoleBuilder roleBuilder = identityModelBuilder.getRoleBuilder();\n        fieldDescriptorMap = new HashMap<String, FieldDescriptor>(5);\n        fieldDescriptorMap.put(ProcessSupervisorSearchDescriptor.ID,\n                new FieldDescriptor(SProcessSupervisor.class, SProcessSupervisor.ID_KEY));\n        fieldDescriptorMap.put(ProcessSupervisorSearchDescriptor.PROCESS_DEFINITION_ID,\n                new FieldDescriptor(SProcessSupervisor.class, SProcessSupervisor.PROCESS_DEF_ID_KEY));\n        fieldDescriptorMap.put(ProcessSupervisorSearchDescriptor.USER_ID,\n                new FieldDescriptor(SProcessSupervisor.class, SProcessSupervisor.USER_ID_KEY));\n        fieldDescriptorMap.put(ProcessSupervisorSearchDescriptor.GROUP_ID,\n                new FieldDescriptor(SProcessSupervisor.class, SProcessSupervisor.GROUP_ID_KEY));\n        fieldDescriptorMap.put(ProcessSupervisorSearchDescriptor.ROLE_ID,\n                new FieldDescriptor(SProcessSupervisor.class, SProcessSupervisor.ROLE_ID_KEY));\n        // fieldDescriptorMap.put(ProcessSupervisorSearchDescriptor.USER_FISRT_NAME, new FieldDescriptor(SUser.class, userBuilder.getFirstNameKey()));\n        // fieldDescriptorMap.put(ProcessSupervisorSearchDescriptor.USER_LAST_NAME, new FieldDescriptor(SUser.class, userBuilder.getLastNameKey()));\n        // fieldDescriptorMap.put(ProcessSupervisorSearchDescriptor.USERNAME, new FieldDescriptor(SUser.class, userBuilder.getUserNameKey()));\n        // fieldDescriptorMap.put(ProcessSupervisorSearchDescriptor.GROUP_NAME, new FieldDescriptor(SGroup.class, groupBuilder.getNameKey()));\n        // fieldDescriptorMap.put(ProcessSupervisorSearchDescriptor.GROUP_PARENT_PATH, new FieldDescriptor(SGroup.class, groupBuilder.getParentPathKey()));\n        // fieldDescriptorMap.put(ProcessSupervisorSearchDescriptor.ROLE_NAME, new FieldDescriptor(SRole.class, roleBuilder.getNameKey()));\n        //\n        supervisorAllFields = new HashMap<Class<? extends PersistentObject>, Set<String>>(3);\n        // final Set<String> userFields = new HashSet<String>(3);\n        // userFields.add(userBuilder.getFirstNameKey());\n        // userFields.add(userBuilder.getLastNameKey());\n        // userFields.add(userBuilder.getUserNameKey());\n        // supervisorAllFields.put(SUser.class, userFields);\n        //\n        // final Set<String> groupFields = new HashSet<String>(2);\n        // groupFields.add(groupBuilder.getNameKey());\n        // groupFields.add(groupBuilder.getParentPathKey());\n        // supervisorAllFields.put(SGroup.class, groupFields);\n        //\n        // final Set<String> roleFields = new HashSet<String>(1);\n        // roleFields.add(roleBuilder.getNameKey());\n        // supervisorAllFields.put(SRole.class, roleFields);\n    }\n\n    @Override\n    protected Map<String, FieldDescriptor> getEntityKeys() {\n        return fieldDescriptorMap;\n    }\n\n    @Override\n    protected Map<Class<? extends PersistentObject>, Set<String>> getAllFields() {\n        return supervisorAllFields;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchProfileDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.profile.ProfileSearchDescriptor;\nimport org.bonitasoft.engine.profile.model.SProfile;\n\n/**\n * @author Zhang Bole\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class SearchProfileDescriptor extends SearchEntityDescriptor {\n\n    private final Map<String, FieldDescriptor> searchEntityKeys = new HashMap<>();\n\n    private final Map<Class<? extends PersistentObject>, Set<String>> profileAllFields = new HashMap<>();\n\n    public SearchProfileDescriptor() {\n        searchEntityKeys.put(ProfileSearchDescriptor.ID, new FieldDescriptor(SProfile.class, SProfile.ID));\n        searchEntityKeys.put(ProfileSearchDescriptor.NAME, new FieldDescriptor(SProfile.class, SProfile.NAME));\n\n        final Set<String> fields = new HashSet<>();\n        fields.add(SProfile.NAME);\n        profileAllFields.put(SProfile.class, fields);\n    }\n\n    @Override\n    protected Map<String, FieldDescriptor> getEntityKeys() {\n        return searchEntityKeys;\n    }\n\n    @Override\n    protected Map<Class<? extends PersistentObject>, Set<String>> getAllFields() {\n        return profileAllFields;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchProfileMemberGroupDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.profile.ProfileMemberSearchDescriptor;\nimport org.bonitasoft.engine.profile.model.SProfileMember;\n\n/**\n * @author Zhang Bole\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class SearchProfileMemberGroupDescriptor extends SearchEntityDescriptor {\n\n    private final Map<String, FieldDescriptor> searchEntityKeys;\n\n    private final Map<Class<? extends PersistentObject>, Set<String>> profileMemberAllFields;\n\n    public SearchProfileMemberGroupDescriptor() {\n        searchEntityKeys = new HashMap<String, FieldDescriptor>(7);\n        searchEntityKeys.put(ProfileMemberSearchDescriptor.ID,\n                new FieldDescriptor(SProfileMember.class, SProfileMember.ID));\n        searchEntityKeys.put(ProfileMemberSearchDescriptor.PROFILE_ID,\n                new FieldDescriptor(SProfileMember.class, SProfileMember.PROFILE_ID));\n        searchEntityKeys.put(ProfileMemberSearchDescriptor.ROLE_ID,\n                new FieldDescriptor(SProfileMember.class, SProfileMember.ROLE_ID));\n        searchEntityKeys.put(ProfileMemberSearchDescriptor.USER_ID,\n                new FieldDescriptor(SProfileMember.class, SProfileMember.USER_ID));\n        searchEntityKeys.put(ProfileMemberSearchDescriptor.GROUP_ID,\n                new FieldDescriptor(SProfileMember.class, SProfileMember.GROUP_ID));\n        searchEntityKeys.put(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART1,\n                new FieldDescriptor(SGroup.class, SGroup.NAME));\n        searchEntityKeys.put(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART2,\n                new FieldDescriptor(SGroup.class, SGroup.PARENT_PATH));\n\n        profileMemberAllFields = new HashMap<Class<? extends PersistentObject>, Set<String>>(1);\n        final Set<String> groupFields = new HashSet<String>(2);\n        groupFields.add(SGroup.NAME);\n        groupFields.add(SGroup.PARENT_PATH);\n        profileMemberAllFields.put(SGroup.class, groupFields);\n    }\n\n    @Override\n    protected Map<String, FieldDescriptor> getEntityKeys() {\n        return searchEntityKeys;\n    }\n\n    @Override\n    protected Map<Class<? extends PersistentObject>, Set<String>> getAllFields() {\n        return profileMemberAllFields;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchProfileMemberRoleAndGroupDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.identity.model.SRole;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.profile.ProfileMemberSearchDescriptor;\nimport org.bonitasoft.engine.profile.model.SProfileMember;\n\n/**\n * @author Zhang Bole\n * @author Celine Souchet\n */\npublic class SearchProfileMemberRoleAndGroupDescriptor extends SearchEntityDescriptor {\n\n    private final Map<String, FieldDescriptor> searchEntityKeys;\n\n    private final Map<Class<? extends PersistentObject>, Set<String>> profileMemberAllFields;\n\n    public SearchProfileMemberRoleAndGroupDescriptor() {\n        searchEntityKeys = new HashMap<String, FieldDescriptor>(7);\n        searchEntityKeys.put(ProfileMemberSearchDescriptor.ID,\n                new FieldDescriptor(SProfileMember.class, SProfileMember.ID));\n        searchEntityKeys.put(ProfileMemberSearchDescriptor.PROFILE_ID,\n                new FieldDescriptor(SProfileMember.class, SProfileMember.PROFILE_ID));\n        searchEntityKeys.put(ProfileMemberSearchDescriptor.ROLE_ID,\n                new FieldDescriptor(SProfileMember.class, SProfileMember.ROLE_ID));\n        searchEntityKeys.put(ProfileMemberSearchDescriptor.USER_ID,\n                new FieldDescriptor(SProfileMember.class, SProfileMember.USER_ID));\n        searchEntityKeys.put(ProfileMemberSearchDescriptor.GROUP_ID,\n                new FieldDescriptor(SProfileMember.class, SProfileMember.GROUP_ID));\n        searchEntityKeys.put(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART1,\n                new FieldDescriptor(SRole.class, SRole.NAME));\n        searchEntityKeys.put(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART2,\n                new FieldDescriptor(SGroup.class, SGroup.NAME));\n        searchEntityKeys.put(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART3,\n                new FieldDescriptor(SGroup.class, SGroup.PARENT_PATH));\n\n        profileMemberAllFields = new HashMap<Class<? extends PersistentObject>, Set<String>>(2);\n        final Set<String> roleFields = new HashSet<String>(1);\n        roleFields.add(SRole.NAME);\n        profileMemberAllFields.put(SRole.class, roleFields);\n        final Set<String> groupFields = new HashSet<String>(2);\n        groupFields.add(SGroup.NAME);\n        groupFields.add(SGroup.PARENT_PATH);\n        profileMemberAllFields.put(SGroup.class, groupFields);\n    }\n\n    @Override\n    protected Map<String, FieldDescriptor> getEntityKeys() {\n        return searchEntityKeys;\n    }\n\n    @Override\n    protected Map<Class<? extends PersistentObject>, Set<String>> getAllFields() {\n        return profileMemberAllFields;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchProfileMemberRoleDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.identity.model.SRole;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.profile.ProfileMemberSearchDescriptor;\nimport org.bonitasoft.engine.profile.model.SProfileMember;\n\n/**\n * @author Zhang Bole\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class SearchProfileMemberRoleDescriptor extends SearchEntityDescriptor {\n\n    private final Map<String, FieldDescriptor> searchEntityKeys;\n\n    private final Map<Class<? extends PersistentObject>, Set<String>> profileMemberAllFields;\n\n    public SearchProfileMemberRoleDescriptor() {\n        searchEntityKeys = new HashMap<String, FieldDescriptor>(6);\n        searchEntityKeys.put(ProfileMemberSearchDescriptor.ID,\n                new FieldDescriptor(SProfileMember.class, SProfileMember.ID));\n        searchEntityKeys.put(ProfileMemberSearchDescriptor.PROFILE_ID,\n                new FieldDescriptor(SProfileMember.class, SProfileMember.PROFILE_ID));\n        searchEntityKeys.put(ProfileMemberSearchDescriptor.ROLE_ID,\n                new FieldDescriptor(SProfileMember.class, SProfileMember.ROLE_ID));\n        searchEntityKeys.put(ProfileMemberSearchDescriptor.USER_ID,\n                new FieldDescriptor(SProfileMember.class, SProfileMember.USER_ID));\n        searchEntityKeys.put(ProfileMemberSearchDescriptor.GROUP_ID,\n                new FieldDescriptor(SProfileMember.class, SProfileMember.GROUP_ID));\n        searchEntityKeys.put(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART1,\n                new FieldDescriptor(SRole.class, SRole.NAME));\n\n        profileMemberAllFields = new HashMap<Class<? extends PersistentObject>, Set<String>>(1);\n        final Set<String> roleFields = new HashSet<String>(1);\n        roleFields.add(SRole.NAME);\n        profileMemberAllFields.put(SRole.class, roleFields);\n    }\n\n    @Override\n    protected Map<String, FieldDescriptor> getEntityKeys() {\n        return searchEntityKeys;\n    }\n\n    @Override\n    protected Map<Class<? extends PersistentObject>, Set<String>> getAllFields() {\n        return profileMemberAllFields;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchProfileMemberUserDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.profile.ProfileMemberSearchDescriptor;\nimport org.bonitasoft.engine.profile.model.SProfileMember;\n\n/**\n * @author Zhang Bole\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class SearchProfileMemberUserDescriptor extends SearchEntityDescriptor {\n\n    private final Map<String, FieldDescriptor> searchEntityKeys;\n\n    private final Map<Class<? extends PersistentObject>, Set<String>> profileMemberAllFields;\n\n    public SearchProfileMemberUserDescriptor() {\n        searchEntityKeys = new HashMap<String, FieldDescriptor>(5);\n        searchEntityKeys.put(ProfileMemberSearchDescriptor.ID,\n                new FieldDescriptor(SProfileMember.class, SProfileMember.ID));\n        searchEntityKeys.put(ProfileMemberSearchDescriptor.PROFILE_ID,\n                new FieldDescriptor(SProfileMember.class, SProfileMember.PROFILE_ID));\n        searchEntityKeys.put(ProfileMemberSearchDescriptor.ROLE_ID,\n                new FieldDescriptor(SProfileMember.class, SProfileMember.ROLE_ID));\n        searchEntityKeys.put(ProfileMemberSearchDescriptor.USER_ID,\n                new FieldDescriptor(SProfileMember.class, SProfileMember.USER_ID));\n        searchEntityKeys.put(ProfileMemberSearchDescriptor.GROUP_ID,\n                new FieldDescriptor(SProfileMember.class, SProfileMember.GROUP_ID));\n        searchEntityKeys.put(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART1,\n                new FieldDescriptor(SUser.class, SUser.FIRST_NAME));\n        searchEntityKeys.put(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART2,\n                new FieldDescriptor(SUser.class, SUser.LAST_NAME));\n        searchEntityKeys.put(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART3,\n                new FieldDescriptor(SUser.class, SUser.USER_NAME));\n\n        profileMemberAllFields = new HashMap<Class<? extends PersistentObject>, Set<String>>(1);\n        final Set<String> userFields = new HashSet<String>(3);\n        userFields.add(SUser.FIRST_NAME);\n        userFields.add(SUser.LAST_NAME);\n        userFields.add(SUser.USER_NAME);\n        profileMemberAllFields.put(SUser.class, userFields);\n    }\n\n    @Override\n    protected Map<String, FieldDescriptor> getEntityKeys() {\n        return searchEntityKeys;\n    }\n\n    @Override\n    protected Map<Class<? extends PersistentObject>, Set<String>> getAllFields() {\n        return profileMemberAllFields;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchRoleDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.identity.RoleSearchDescriptor;\nimport org.bonitasoft.engine.identity.model.SRole;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SearchRoleDescriptor extends SearchEntityDescriptor {\n\n    private final Map<String, FieldDescriptor> roleKeys;\n\n    private final Map<Class<? extends PersistentObject>, Set<String>> roleAllFields;\n\n    SearchRoleDescriptor() {\n        roleKeys = new HashMap<>(5);\n        roleKeys.put(RoleSearchDescriptor.ID, new FieldDescriptor(SRole.class, SRole.ID));\n        roleKeys.put(RoleSearchDescriptor.NAME, new FieldDescriptor(SRole.class, SRole.NAME));\n        roleKeys.put(RoleSearchDescriptor.DISPLAY_NAME, new FieldDescriptor(SRole.class, SRole.DISPLAY_NAME));\n\n        roleAllFields = new HashMap<>(1);\n        final Set<String> roleFields = new HashSet<>(3);\n        roleFields.add(SRole.NAME);\n        roleFields.add(SRole.DISPLAY_NAME);\n        roleFields.add(SRole.DESCRIPTION);\n        roleAllFields.put(SRole.class, roleFields);\n    }\n\n    @Override\n    protected Map<String, FieldDescriptor> getEntityKeys() {\n        return roleKeys;\n    }\n\n    @Override\n    protected Map<Class<? extends PersistentObject>, Set<String>> getAllFields() {\n        return roleAllFields;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchUserDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.identity.UserSearchDescriptor;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.identity.model.SUserMembership;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SearchUserDescriptor extends SearchEntityDescriptor {\n\n    private final Map<String, FieldDescriptor> userKeys;\n\n    private final Map<Class<? extends PersistentObject>, Set<String>> userAllFields;\n\n    SearchUserDescriptor() {\n        userKeys = new HashMap<String, FieldDescriptor>(8);\n        userKeys.put(UserSearchDescriptor.ID, new FieldDescriptor(SUser.class, SUser.ID));\n        userKeys.put(UserSearchDescriptor.USER_NAME, new FieldDescriptor(SUser.class, SUser.USER_NAME));\n        userKeys.put(UserSearchDescriptor.FIRST_NAME, new FieldDescriptor(SUser.class, SUser.FIRST_NAME));\n        userKeys.put(UserSearchDescriptor.LAST_NAME, new FieldDescriptor(SUser.class, SUser.LAST_NAME));\n        userKeys.put(UserSearchDescriptor.ENABLED, new FieldDescriptor(SUser.class, SUser.ENABLED));\n        userKeys.put(UserSearchDescriptor.LAST_CONNECTION, new FieldDescriptor(SUser.class, SUser.LAST_CONNECTION));\n        userKeys.put(UserSearchDescriptor.MANAGER_USER_ID, new FieldDescriptor(SUser.class, SUser.MANAGER_USER_ID));\n        userKeys.put(UserSearchDescriptor.ROLE_ID, new FieldDescriptor(SUserMembership.class, SUserMembership.ROLE_ID));\n        userKeys.put(UserSearchDescriptor.GROUP_ID,\n                new FieldDescriptor(SUserMembership.class, SUserMembership.GROUP_ID));\n\n        userAllFields = new HashMap<Class<? extends PersistentObject>, Set<String>>(1);\n        final Set<String> userFields = new HashSet<String>(4);\n        userFields.add(SUser.USER_NAME);\n        userFields.add(SUser.FIRST_NAME);\n        userFields.add(SUser.LAST_NAME);\n        userFields.add(SUser.JOB_TITLE);\n        userAllFields.put(SUser.class, userFields);\n    }\n\n    @Override\n    protected Map<String, FieldDescriptor> getEntityKeys() {\n        return userKeys;\n    }\n\n    @Override\n    protected Map<Class<? extends PersistentObject>, Set<String>> getAllFields() {\n        return userAllFields;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchWaitingEventSerchDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.bpm.flownode.WaitingEventSearchDescriptor;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingEventKeyProviderBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SearchWaitingEventSerchDescriptor extends SearchEntityDescriptor {\n\n    private final Map<String, FieldDescriptor> activityInstanceDescriptorKeys;\n\n    public SearchWaitingEventSerchDescriptor() {\n        final SWaitingEventKeyProviderBuilderFactory keyProvider = BuilderFactory\n                .get(SWaitingEventKeyProviderBuilderFactory.class);\n        activityInstanceDescriptorKeys = new HashMap<String, FieldDescriptor>(6);\n        activityInstanceDescriptorKeys.put(WaitingEventSearchDescriptor.BPM_EVENT_TYPE,\n                new FieldDescriptor(SWaitingEvent.class, keyProvider.getEventTypeKey()));\n        activityInstanceDescriptorKeys.put(WaitingEventSearchDescriptor.FLOW_NODE_NAME,\n                new FieldDescriptor(SWaitingEvent.class, keyProvider.getFlowNodeNameKey()));\n        activityInstanceDescriptorKeys.put(WaitingEventSearchDescriptor.PARENT_PROCESS_INSTANCE_ID,\n                new FieldDescriptor(SWaitingEvent.class,\n                        keyProvider.getParentProcessInstanceIdKey()));\n        activityInstanceDescriptorKeys.put(WaitingEventSearchDescriptor.PROCESS_DEFINITION_ID,\n                new FieldDescriptor(SWaitingEvent.class, keyProvider.getProcessDefinitionIdKey()));\n        activityInstanceDescriptorKeys.put(WaitingEventSearchDescriptor.PROCESS_NAME,\n                new FieldDescriptor(SWaitingEvent.class, keyProvider.getProcessNameKey()));\n        activityInstanceDescriptorKeys.put(WaitingEventSearchDescriptor.ROOT_PROCESS_INSTANCE_ID,\n                new FieldDescriptor(SWaitingEvent.class,\n                        keyProvider.getRootProcessInstanceIdKey()));\n    }\n\n    @Override\n    protected Map<String, FieldDescriptor> getEntityKeys() {\n        return activityInstanceDescriptorKeys;\n    }\n\n    @Override\n    protected Map<Class<? extends PersistentObject>, Set<String>> getAllFields() {\n        return Collections.emptyMap();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/document/SearchArchivedDocuments.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.document;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.document.api.DocumentService;\nimport org.bonitasoft.engine.core.document.model.archive.SAMappedDocument;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractArchivedDocumentSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchArchivedDocumentDescriptor;\n\n/**\n * @author Zhang Bole\n * @author Celine Souchet\n */\npublic class SearchArchivedDocuments extends AbstractArchivedDocumentSearchEntity {\n\n    private final DocumentService documentService;\n\n    public SearchArchivedDocuments(final DocumentService documentService,\n            final SearchArchivedDocumentDescriptor searchDescriptor,\n            final SearchOptions options) {\n        super(searchDescriptor, options, documentService);\n        this.documentService = documentService;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return documentService.getNumberOfArchivedDocuments(searchOptions);\n    }\n\n    @Override\n    public List<SAMappedDocument> executeSearch(final QueryOptions searchOptions) throws SBonitaReadException {\n        return documentService.searchArchivedDocuments(searchOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/document/SearchArchivedDocumentsSupervisedBy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.document;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.document.api.DocumentService;\nimport org.bonitasoft.engine.core.document.model.archive.SAMappedDocument;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractArchivedDocumentSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchArchivedDocumentDescriptor;\n\n/**\n * @author Zhang Bole\n * @author Celine Souchet\n */\npublic class SearchArchivedDocumentsSupervisedBy extends AbstractArchivedDocumentSearchEntity {\n\n    private final long userId;\n\n    private final DocumentService documentService;\n\n    public SearchArchivedDocumentsSupervisedBy(final long userId, final DocumentService documentService,\n            final SearchArchivedDocumentDescriptor searchDescriptor, final SearchOptions options) {\n        super(searchDescriptor, options, documentService);\n        this.userId = userId;\n        this.documentService = documentService;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return documentService.getNumberOfArchivedDocumentsSupervisedBy(userId, searchOptions);\n    }\n\n    @Override\n    public List<SAMappedDocument> executeSearch(final QueryOptions searchOptions) throws SBonitaReadException {\n        return documentService.searchArchivedDocumentsSupervisedBy(userId, searchOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/document/SearchDocuments.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.document;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.document.api.DocumentService;\nimport org.bonitasoft.engine.core.document.model.SMappedDocument;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractDocumentSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchDocumentDescriptor;\n\n/**\n * @author Zhang Bole\n */\npublic class SearchDocuments extends AbstractDocumentSearchEntity {\n\n    private final DocumentService documentService;\n\n    public SearchDocuments(final DocumentService documentService, final SearchDocumentDescriptor searchDescriptor,\n            final SearchOptions options) {\n        super(searchDescriptor, options, documentService);\n        this.documentService = documentService;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return documentService.getNumberOfDocuments(searchOptions);\n    }\n\n    @Override\n    public List<SMappedDocument> executeSearch(final QueryOptions searchOptions) throws SBonitaReadException {\n        return documentService.searchDocuments(searchOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/document/SearchDocumentsSupervisedBy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.document;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.document.api.DocumentService;\nimport org.bonitasoft.engine.core.document.model.SMappedDocument;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractDocumentSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchDocumentDescriptor;\n\n/**\n * @author Zhang Bole\n */\npublic class SearchDocumentsSupervisedBy extends AbstractDocumentSearchEntity {\n\n    private final DocumentService documentService;\n\n    private final long userId;\n\n    public SearchDocumentsSupervisedBy(final DocumentService documentService,\n            final SearchDocumentDescriptor searchDescriptor,\n            final SearchOptions options, final long userId) {\n        super(searchDescriptor, options, documentService);\n        this.documentService = documentService;\n        this.userId = userId;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return documentService.getNumberOfDocumentsSupervisedBy(userId, searchOptions);\n    }\n\n    @Override\n    public List<SMappedDocument> executeSearch(final QueryOptions searchOptions) throws SBonitaReadException {\n        return documentService.searchDocumentsSupervisedBy(userId, searchOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/events/trigger/SearchTimerEventTriggerInstances.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.events.trigger;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.flownode.TimerEventTriggerInstance;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchEventTriggerInstanceDescriptor;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Celine Souchet\n * @version 6.4.0\n * @since 6.4.0\n */\npublic class SearchTimerEventTriggerInstances\n        extends AbstractSearchEntity<TimerEventTriggerInstance, STimerEventTriggerInstance> {\n\n    private final EventInstanceService eventInstanceService;\n\n    private final long processInstanceId;\n\n    public SearchTimerEventTriggerInstances(final EventInstanceService eventInstanceService,\n            final SearchEventTriggerInstanceDescriptor searchEventTriggerInstanceDescriptor,\n            final long processInstanceId, final SearchOptions searchOptions) {\n        super(searchEventTriggerInstanceDescriptor, searchOptions);\n        this.eventInstanceService = eventInstanceService;\n        this.processInstanceId = processInstanceId;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return eventInstanceService.getNumberOfTimerEventTriggerInstances(processInstanceId, searchOptions);\n    }\n\n    @Override\n    public List<STimerEventTriggerInstance> executeSearch(final QueryOptions searchOptions)\n            throws SBonitaReadException {\n        return eventInstanceService.searchTimerEventTriggerInstances(processInstanceId, searchOptions);\n    }\n\n    @Override\n    public List<TimerEventTriggerInstance> convertToClientObjects(\n            final List<STimerEventTriggerInstance> serverObjects) {\n        return ModelConvertor.toTimerEventTriggerInstances(serverObjects);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/events/trigger/SearchWaitingEvents.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.events.trigger;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.flownode.WaitingEvent;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic class SearchWaitingEvents extends AbstractSearchEntity<WaitingEvent, SWaitingEvent> {\n\n    private final EventInstanceService eventInstanceService;\n\n    public SearchWaitingEvents(final SearchEntityDescriptor searchDescriptor, final SearchOptions options,\n            final EventInstanceService eventInstanceService) {\n        super(searchDescriptor, options);\n        this.eventInstanceService = eventInstanceService;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return eventInstanceService.getNumberOfWaitingEvents(SWaitingEvent.class, searchOptions);\n    }\n\n    @Override\n    public List<SWaitingEvent> executeSearch(final QueryOptions searchOptions) throws SBonitaReadException {\n        return eventInstanceService.searchWaitingEvents(SWaitingEvent.class, searchOptions);\n    }\n\n    @Override\n    public List<WaitingEvent> convertToClientObjects(final List<SWaitingEvent> serverObjects) {\n        return ModelConvertor.toWaitingEvents(serverObjects);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/flownode/SearchArchivedFlowNodeInstances.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.flownode;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance;\nimport org.bonitasoft.engine.execution.state.FlowNodeStateManager;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchArchivedFlowNodeInstanceDescriptor;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class SearchArchivedFlowNodeInstances\n        extends AbstractSearchEntity<ArchivedFlowNodeInstance, SAFlowNodeInstance> {\n\n    private final FlowNodeInstanceService flowNodeInstanceService;\n\n    private final FlowNodeStateManager flowNodeStateManager;\n\n    public SearchArchivedFlowNodeInstances(final FlowNodeInstanceService flowNodeInstanceService,\n            final FlowNodeStateManager flowNodeStateManager,\n            final SearchArchivedFlowNodeInstanceDescriptor searchDescriptor, final SearchOptions searchOptions) {\n        super(searchDescriptor, searchOptions);\n        this.flowNodeInstanceService = flowNodeInstanceService;\n        this.flowNodeStateManager = flowNodeStateManager;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return flowNodeInstanceService.getNumberOfArchivedFlowNodeInstances(SAFlowNodeInstance.class, searchOptions);\n    }\n\n    @Override\n    public List<SAFlowNodeInstance> executeSearch(final QueryOptions searchOptions) throws SBonitaReadException {\n        return flowNodeInstanceService.searchArchivedFlowNodeInstances(SAFlowNodeInstance.class, searchOptions);\n    }\n\n    @Override\n    public List<ArchivedFlowNodeInstance> convertToClientObjects(final List<SAFlowNodeInstance> serverObjects) {\n        return ModelConvertor.toArchivedFlowNodeInstances(serverObjects, flowNodeStateManager);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/flownode/SearchFlowNodeInstances.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.flownode;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.execution.state.FlowNodeStateManager;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchFlowNodeInstanceDescriptor;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SearchFlowNodeInstances extends AbstractSearchEntity<FlowNodeInstance, SFlowNodeInstance> {\n\n    private final FlowNodeInstanceService flowNodeInstanceService;\n\n    private final FlowNodeStateManager flowNodeStateManager;\n\n    public SearchFlowNodeInstances(final FlowNodeInstanceService flowNodeInstanceService,\n            final FlowNodeStateManager flowNodeStateManager,\n            final SearchFlowNodeInstanceDescriptor searchDescriptor, final SearchOptions searchOptions) {\n        super(searchDescriptor, searchOptions);\n        this.flowNodeInstanceService = flowNodeInstanceService;\n        this.flowNodeStateManager = flowNodeStateManager;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return flowNodeInstanceService.getNumberOfFlowNodeInstances(SFlowNodeInstance.class, searchOptions);\n    }\n\n    @Override\n    public List<SFlowNodeInstance> executeSearch(final QueryOptions searchOptions) throws SBonitaReadException {\n        return flowNodeInstanceService.searchFlowNodeInstances(SFlowNodeInstance.class, searchOptions);\n    }\n\n    @Override\n    public List<FlowNodeInstance> convertToClientObjects(final List<SFlowNodeInstance> serverObjects) {\n        return ModelConvertor.toFlowNodeInstances(serverObjects, flowNodeStateManager);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/form/SearchFormMappings.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.form;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.form.FormMappingService;\nimport org.bonitasoft.engine.core.form.SFormMapping;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.form.FormMapping;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchFormMappingDescriptor;\nimport org.bonitasoft.engine.service.FormRequiredAnalyzer;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SearchFormMappings extends AbstractSearchEntity<FormMapping, SFormMapping> {\n\n    private final FormMappingService formMappingService;\n    private final ProcessDefinitionService processDefinitionService;\n\n    public SearchFormMappings(final FormMappingService formMappingService,\n            final ProcessDefinitionService processDefinitionService,\n            final SearchFormMappingDescriptor searchProfileDescriptor, final SearchOptions options) {\n        super(searchProfileDescriptor, options);\n        this.formMappingService = formMappingService;\n        this.processDefinitionService = processDefinitionService;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions queryOptions) throws SBonitaReadException {\n        return formMappingService.getNumberOfFormMappings(queryOptions);\n    }\n\n    @Override\n    public List<SFormMapping> executeSearch(final QueryOptions queryOptions) throws SBonitaReadException {\n        return formMappingService.searchFormMappings(queryOptions);\n    }\n\n    @Override\n    public List<FormMapping> convertToClientObjects(final List<SFormMapping> serverObjects) {\n        return ModelConvertor.toFormMappings(serverObjects, new FormRequiredAnalyzer(processDefinitionService));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/identity/SearchCustomUserInfoValues.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.identity;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.identity.CustomUserInfoValue;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoValue;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Vincent Elcrin\n */\npublic class SearchCustomUserInfoValues extends AbstractSearchEntity<CustomUserInfoValue, SCustomUserInfoValue> {\n\n    private final IdentityService service;\n\n    public SearchCustomUserInfoValues(IdentityService service, SearchEntityDescriptor searchDescriptor,\n            SearchOptions options) {\n        super(searchDescriptor, options);\n        this.service = service;\n    }\n\n    @Override\n    public long executeCount(QueryOptions options) throws SBonitaReadException {\n        return service.getNumberOfCustomUserInfoValue(options);\n    }\n\n    @Override\n    public List<SCustomUserInfoValue> executeSearch(QueryOptions options) throws SBonitaReadException {\n        return service.searchCustomUserInfoValue(options);\n    }\n\n    @Override\n    public List<CustomUserInfoValue> convertToClientObjects(List<SCustomUserInfoValue> sValues) {\n        List<CustomUserInfoValue> values = new ArrayList<CustomUserInfoValue>(sValues.size());\n        for (SCustomUserInfoValue value : sValues) {\n            values.add(ModelConvertor.convert(value));\n        }\n        return values;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/identity/SearchGroups.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.identity;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractGroupSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchGroupDescriptor;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SearchGroups extends AbstractGroupSearchEntity {\n\n    private final IdentityService identityService;\n\n    public SearchGroups(final IdentityService identityService, final SearchGroupDescriptor searchDescriptor,\n            final SearchOptions options) {\n        super(searchDescriptor, options);\n        this.identityService = identityService;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return identityService.getNumberOfGroups(searchOptions);\n    }\n\n    @Override\n    public List<SGroup> executeSearch(final QueryOptions searchOptions) throws SBonitaReadException {\n        return identityService.searchGroups(searchOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/identity/SearchRoles.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.identity;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.model.SRole;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractRoleSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchRoleDescriptor;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SearchRoles extends AbstractRoleSearchEntity {\n\n    private final IdentityService identityService;\n\n    public SearchRoles(final IdentityService identityService, final SearchRoleDescriptor searchDescriptor,\n            final SearchOptions options) {\n        super(searchDescriptor, options);\n        this.identityService = identityService;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return identityService.getNumberOfRoles(searchOptions);\n    }\n\n    @Override\n    public List<SRole> executeSearch(final QueryOptions searchOptions) throws SBonitaReadException {\n        return identityService.searchRoles(searchOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/identity/SearchUsers.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.identity;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractUserSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchUserDescriptor;\n\n/**\n * @author Matthieu Chaffotte\n * @author Baptiste Mesta\n */\npublic class SearchUsers extends AbstractUserSearchEntity {\n\n    private final IdentityService identityService;\n\n    public SearchUsers(final IdentityService identityService, final SearchUserDescriptor searchDescriptor,\n            final SearchOptions options) {\n        super(searchDescriptor, options);\n        this.identityService = identityService;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return identityService.getNumberOfUsers(searchOptions);\n    }\n\n    @Override\n    public List<SUser> executeSearch(final QueryOptions searchOptions) throws SBonitaReadException {\n        return identityService.searchUsers(searchOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/identity/SearchUsersWhoCanExecutePendingHumanTaskDeploymentInfo.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.identity;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractUserSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchUserDescriptor;\n\n/**\n * @author Julien Reboul\n */\npublic class SearchUsersWhoCanExecutePendingHumanTaskDeploymentInfo extends AbstractUserSearchEntity {\n\n    private final ActivityInstanceService activityInstanceService;\n\n    private final long humanTaskInstanceId;\n\n    public SearchUsersWhoCanExecutePendingHumanTaskDeploymentInfo(long humanTaskInstanceId,\n            final ActivityInstanceService activityInstanceService,\n            final SearchUserDescriptor searchDescriptor, final SearchOptions options) {\n        super(searchDescriptor, options);\n        this.activityInstanceService = activityInstanceService;\n        this.humanTaskInstanceId = humanTaskInstanceId;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return activityInstanceService.getNumberOfUsersWhoCanExecutePendingHumanTaskDeploymentInfo(humanTaskInstanceId,\n                searchOptions);\n    }\n\n    @Override\n    public List<SUser> executeSearch(final QueryOptions searchOptions) throws SBonitaReadException {\n        return activityInstanceService.searchUsersWhoCanExecutePendingHumanTaskDeploymentInfo(humanTaskInstanceId,\n                searchOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/identity/SearchUsersWhoCanStartProcessDeploymentInfo.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.identity;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractUserSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchUserDescriptor;\n\n/**\n * @author Celine Souchet\n */\npublic class SearchUsersWhoCanStartProcessDeploymentInfo extends AbstractUserSearchEntity {\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    private final long processDefinitionId;\n\n    public SearchUsersWhoCanStartProcessDeploymentInfo(final ProcessDefinitionService processDefinitionService,\n            final SearchUserDescriptor searchDescriptor,\n            final long processDefinitionId, final SearchOptions options) {\n        super(searchDescriptor, options);\n        this.processDefinitionService = processDefinitionService;\n        this.processDefinitionId = processDefinitionId;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return processDefinitionService.getNumberOfUsersWhoCanStartProcessDeploymentInfo(processDefinitionId,\n                searchOptions);\n    }\n\n    @Override\n    public List<SUser> executeSearch(final QueryOptions searchOptions) throws SBonitaReadException {\n        return processDefinitionService.searchUsersWhoCanStartProcessDeploymentInfo(processDefinitionId, searchOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchArchivedProcessInstances.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.process;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractArchivedProcessInstanceSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchArchivedProcessInstancesDescriptor;\n\n/**\n * @author Celine Souchet\n */\npublic class SearchArchivedProcessInstances extends AbstractArchivedProcessInstanceSearchEntity {\n\n    private final ProcessInstanceService processInstanceService;\n\n    public SearchArchivedProcessInstances(final ProcessInstanceService processInstanceService,\n            final ProcessDefinitionService processDefinitionService,\n            final SearchArchivedProcessInstancesDescriptor archivedProcessInstancesDescriptor,\n            final SearchOptions options) {\n        super(archivedProcessInstancesDescriptor, options, processDefinitionService);\n        this.processInstanceService = processInstanceService;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return processInstanceService.getNumberOfArchivedProcessInstances(searchOptions);\n    }\n\n    @Override\n    public List<SAProcessInstance> executeSearch(final QueryOptions searchOptions) throws SBonitaReadException {\n        return processInstanceService.searchArchivedProcessInstances(searchOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchArchivedProcessInstancesInvolvingUser.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.process;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractArchivedProcessInstanceSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\n\n/**\n * @author Yanyan Liu\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n */\npublic class SearchArchivedProcessInstancesInvolvingUser extends AbstractArchivedProcessInstanceSearchEntity {\n\n    private final ProcessInstanceService processInstanceService;\n\n    private final long userId;\n\n    public SearchArchivedProcessInstancesInvolvingUser(final long userId,\n            final ProcessInstanceService processInstanceService,\n            final ProcessDefinitionService processDefinitionService, final SearchEntityDescriptor searchDescriptor,\n            final SearchOptions searchOptions) {\n        super(searchDescriptor, searchOptions, processDefinitionService);\n        this.userId = userId;\n        this.processInstanceService = processInstanceService;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return processInstanceService.getNumberOfArchivedProcessInstancesInvolvingUser(userId, searchOptions);\n    }\n\n    @Override\n    public List<SAProcessInstance> executeSearch(final QueryOptions searchOptions) throws SBonitaReadException {\n        return processInstanceService.searchArchivedProcessInstancesInvolvingUser(userId, searchOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchArchivedProcessInstancesSupervisedBy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.process;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractArchivedProcessInstanceSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\n\n/**\n * @author Yanyan Liu\n * @author Celine Souchet\n */\npublic class SearchArchivedProcessInstancesSupervisedBy extends AbstractArchivedProcessInstanceSearchEntity {\n\n    private final long userId;\n\n    private final ProcessInstanceService processInstanceService;\n\n    public SearchArchivedProcessInstancesSupervisedBy(final long userId,\n            final ProcessInstanceService processInstanceService,\n            final ProcessDefinitionService processDefinitionService, final SearchEntityDescriptor searchDescriptor,\n            final SearchOptions options) {\n        super(searchDescriptor, options, processDefinitionService);\n        this.userId = userId;\n        this.processInstanceService = processInstanceService;\n\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return processInstanceService.getNumberOfArchivedProcessInstancesSupervisedBy(userId, searchOptions);\n    }\n\n    @Override\n    public List<SAProcessInstance> executeSearch(final QueryOptions searchOptions) throws SBonitaReadException {\n        return processInstanceService.searchArchivedProcessInstancesSupervisedBy(userId, searchOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchArchivedProcessInstancesWithoutSubProcess.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.process;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractArchivedProcessInstanceSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\n\n/**\n * @author Yanyan Liu\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class SearchArchivedProcessInstancesWithoutSubProcess extends AbstractArchivedProcessInstanceSearchEntity {\n\n    private final ProcessInstanceService processInstanceService;\n\n    public SearchArchivedProcessInstancesWithoutSubProcess(final ProcessInstanceService processInstanceService,\n            final ProcessDefinitionService processDefinitionService, final SearchEntityDescriptor searchDescriptor,\n            final SearchOptions options) {\n        super(searchDescriptor, options, processDefinitionService);\n        this.processInstanceService = processInstanceService;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return processInstanceService.getNumberOfArchivedProcessInstancesWithoutSubProcess(searchOptions);\n    }\n\n    @Override\n    public List<SAProcessInstance> executeSearch(final QueryOptions searchOptions) throws SBonitaReadException {\n        return processInstanceService.searchArchivedProcessInstancesWithoutSubProcess(searchOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchFailedProcessInstances.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.process;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractProcessInstanceSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor;\n\n/**\n * @author Celine Souchet\n * @version 6.4.0\n * @since 6.4.0\n */\npublic class SearchFailedProcessInstances extends AbstractProcessInstanceSearchEntity {\n\n    private final ProcessInstanceService processInstanceService;\n\n    public SearchFailedProcessInstances(final ProcessInstanceService processInstanceService,\n            final SearchProcessInstanceDescriptor searchEntitiesDescriptor,\n            final SearchOptions options, final ProcessDefinitionService processDefinitionService) {\n        super(searchEntitiesDescriptor, options, processDefinitionService);\n        this.processInstanceService = processInstanceService;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions queryOptions) throws SBonitaReadException {\n        return processInstanceService.getNumberOfFailedProcessInstances(queryOptions);\n    }\n\n    @Override\n    public List<SProcessInstance> executeSearch(final QueryOptions queryOptions) throws SBonitaReadException {\n        return processInstanceService.searchFailedProcessInstances(queryOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchFailedProcessInstancesSupervisedBy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.process;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractProcessInstanceSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor;\n\n/**\n * @author Yanyan Liu\n * @author Baptiste Mesta\n */\npublic class SearchFailedProcessInstancesSupervisedBy extends AbstractProcessInstanceSearchEntity {\n\n    private final ProcessInstanceService processInstanceService;\n\n    private final long userId;\n\n    public SearchFailedProcessInstancesSupervisedBy(final ProcessInstanceService processInstanceService,\n            final SearchProcessInstanceDescriptor searchEntitiesDescriptor, final long userId,\n            final SearchOptions options,\n            final ProcessDefinitionService processDefinitionService) {\n        super(searchEntitiesDescriptor, options, processDefinitionService);\n        this.processInstanceService = processInstanceService;\n        this.userId = userId;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return processInstanceService.getNumberOfFailedProcessInstancesSupervisedBy(userId, searchOptions);\n    }\n\n    @Override\n    public List<SProcessInstance> executeSearch(final QueryOptions searchOptions) throws SBonitaReadException {\n        return processInstanceService.searchFailedProcessInstancesSupervisedBy(userId, searchOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchOpenProcessInstancesInvolvingUser.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.process;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractProcessInstanceSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor;\n\n/**\n * @author Yanyan Liu\n */\npublic class SearchOpenProcessInstancesInvolvingUser extends AbstractProcessInstanceSearchEntity {\n\n    private final ProcessInstanceService processInstanceService;\n\n    private final long userId;\n\n    public SearchOpenProcessInstancesInvolvingUser(final ProcessInstanceService processInstanceService,\n            final SearchProcessInstanceDescriptor searchEntitiesDescriptor, final long userId,\n            final SearchOptions options,\n            final ProcessDefinitionService processDefinitionService) {\n        super(searchEntitiesDescriptor, options, processDefinitionService);\n        this.processInstanceService = processInstanceService;\n        this.userId = userId;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return processInstanceService.getNumberOfOpenProcessInstancesInvolvingUser(userId, searchOptions);\n    }\n\n    @Override\n    public List<SProcessInstance> executeSearch(final QueryOptions searchOptions) throws SBonitaReadException {\n        return processInstanceService.searchOpenProcessInstancesInvolvingUser(userId, searchOptions);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchOpenProcessInstancesInvolvingUsersManagedBy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.process;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractProcessInstanceSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor;\n\n/**\n * @author Celine Souchet\n */\npublic class SearchOpenProcessInstancesInvolvingUsersManagedBy extends AbstractProcessInstanceSearchEntity {\n\n    private final ProcessInstanceService processInstanceService;\n\n    private final long managerUserId;\n\n    public SearchOpenProcessInstancesInvolvingUsersManagedBy(final ProcessInstanceService processInstanceService,\n            final SearchProcessInstanceDescriptor searchEntitiesDescriptor, final long managerUserId,\n            final SearchOptions options,\n            final ProcessDefinitionService processDefinitionService) {\n        super(searchEntitiesDescriptor, options, processDefinitionService);\n        this.processInstanceService = processInstanceService;\n        this.managerUserId = managerUserId;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return processInstanceService.getNumberOfOpenProcessInstancesInvolvingUsersManagedBy(managerUserId,\n                searchOptions);\n    }\n\n    @Override\n    public List<SProcessInstance> executeSearch(final QueryOptions searchOptions) throws SBonitaReadException {\n        return processInstanceService.searchOpenProcessInstancesInvolvingUsersManagedBy(managerUserId, searchOptions);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchOpenProcessInstancesSupervisedBy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.process;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractProcessInstanceSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor;\n\n/**\n * @author Yanyan Liu\n * @author Baptiste Mesta\n */\npublic class SearchOpenProcessInstancesSupervisedBy extends AbstractProcessInstanceSearchEntity {\n\n    private final ProcessInstanceService processInstanceService;\n\n    private final long userId;\n\n    public SearchOpenProcessInstancesSupervisedBy(final ProcessInstanceService processInstanceService,\n            final SearchProcessInstanceDescriptor searchEntitiesDescriptor, final long userId,\n            final SearchOptions options,\n            final ProcessDefinitionService processDefinitionService) {\n        super(searchEntitiesDescriptor, options, processDefinitionService);\n        this.processInstanceService = processInstanceService;\n        this.userId = userId;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return processInstanceService.getNumberOfOpenProcessInstancesSupervisedBy(userId, searchOptions);\n    }\n\n    @Override\n    public List<SProcessInstance> executeSearch(final QueryOptions searchOptions) throws SBonitaReadException {\n        return processInstanceService.searchOpenProcessInstancesSupervisedBy(userId, searchOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchProcessDeploymentInfos.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.process;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractProcessDeploymentInfoSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchProcessDefinitionsDescriptor;\n\n/**\n * @author Zhao Na\n */\npublic class SearchProcessDeploymentInfos extends AbstractProcessDeploymentInfoSearchEntity {\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    public SearchProcessDeploymentInfos(final ProcessDefinitionService processDefinitionService,\n            final SearchProcessDefinitionsDescriptor searchEntitiesDescriptor,\n            final SearchOptions options) {\n        super(searchEntitiesDescriptor, options);\n        this.processDefinitionService = processDefinitionService;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return processDefinitionService.getNumberOfProcessDeploymentInfos(searchOptions);\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> executeSearch(final QueryOptions searchOptions)\n            throws SBonitaReadException {\n        return processDefinitionService.searchProcessDeploymentInfos(searchOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchProcessDeploymentInfosCanBeStartedBy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.process;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractProcessDeploymentInfoSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchProcessDefinitionsDescriptor;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SearchProcessDeploymentInfosCanBeStartedBy extends AbstractProcessDeploymentInfoSearchEntity {\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    private final long userId;\n\n    public SearchProcessDeploymentInfosCanBeStartedBy(final ProcessDefinitionService processDefinitionService,\n            final SearchProcessDefinitionsDescriptor searchEntitiesDescriptor, final SearchOptions options,\n            final long userId) {\n        super(searchEntitiesDescriptor, options);\n        this.processDefinitionService = processDefinitionService;\n        this.userId = userId;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return processDefinitionService.getNumberOfProcessDeploymentInfosCanBeStartedBy(userId, searchOptions);\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> executeSearch(final QueryOptions searchOptions)\n            throws SBonitaReadException {\n        return processDefinitionService.searchProcessDeploymentInfosCanBeStartedBy(userId, searchOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchProcessDeploymentInfosCanBeStartedByUsersManagedBy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.process;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractProcessDeploymentInfoSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchProcessDefinitionsDescriptor;\n\n/**\n * @author Celine Souchet\n */\npublic class SearchProcessDeploymentInfosCanBeStartedByUsersManagedBy\n        extends AbstractProcessDeploymentInfoSearchEntity {\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    private final long managerUserId;\n\n    public SearchProcessDeploymentInfosCanBeStartedByUsersManagedBy(\n            final ProcessDefinitionService processDefinitionService,\n            final SearchProcessDefinitionsDescriptor searchEntitiesDescriptor, final SearchOptions options,\n            final long managerUserId) {\n        super(searchEntitiesDescriptor, options);\n        this.processDefinitionService = processDefinitionService;\n        this.managerUserId = managerUserId;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return processDefinitionService.getNumberOfProcessDeploymentInfosCanBeStartedByUsersManagedBy(managerUserId,\n                searchOptions);\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> executeSearch(final QueryOptions searchOptions)\n            throws SBonitaReadException {\n        return processDefinitionService.searchProcessDeploymentInfosCanBeStartedByUsersManagedBy(managerUserId,\n                searchOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchProcessDeploymentInfosStartedBy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.process;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractProcessDeploymentInfoSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchProcessDefinitionsDescriptor;\n\n/**\n * @author Zhao Na\n */\npublic class SearchProcessDeploymentInfosStartedBy extends AbstractProcessDeploymentInfoSearchEntity {\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    private final long userId;\n\n    public SearchProcessDeploymentInfosStartedBy(final ProcessDefinitionService processDefinitionService,\n            final SearchProcessDefinitionsDescriptor searchEntitiesDescriptor, final long userId,\n            final SearchOptions options) {\n        super(searchEntitiesDescriptor, options);\n        this.processDefinitionService = processDefinitionService;\n        this.userId = userId;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return processDefinitionService.getNumberOfProcessDeploymentInfosStartedBy(userId, searchOptions);\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> executeSearch(final QueryOptions searchOptions)\n            throws SBonitaReadException {\n        return processDefinitionService.searchProcessDeploymentInfosStartedBy(userId, searchOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasks.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.process;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractProcessDeploymentInfoSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchProcessDefinitionsDescriptor;\n\n/**\n * @author Celine Souchet\n */\npublic class SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasks\n        extends AbstractProcessDeploymentInfoSearchEntity {\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    public SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasks(\n            final ProcessDefinitionService processDefinitionService,\n            final SearchProcessDefinitionsDescriptor searchEntitiesDescriptor, final SearchOptions options) {\n        super(searchEntitiesDescriptor, options);\n        this.processDefinitionService = processDefinitionService;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions queryOptions) throws SBonitaReadException {\n        return processDefinitionService.getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasks(queryOptions);\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> executeSearch(final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        return processDefinitionService.searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks(queryOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.process;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractProcessDeploymentInfoSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchProcessDefinitionsDescriptor;\n\n/**\n * @author Celine Souchet\n */\npublic class SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor\n        extends AbstractProcessDeploymentInfoSearchEntity {\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    private final long userId;\n\n    public SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(\n            final ProcessDefinitionService processDefinitionService,\n            final SearchProcessDefinitionsDescriptor searchEntitiesDescriptor, final SearchOptions options,\n            final long userId) {\n        super(searchEntitiesDescriptor, options);\n        this.processDefinitionService = processDefinitionService;\n        this.userId = userId;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions queryOptions) throws SBonitaReadException {\n        return processDefinitionService.getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(userId,\n                queryOptions);\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> executeSearch(final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        return processDefinitionService.searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(userId,\n                queryOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.process;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractProcessDeploymentInfoSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchProcessDefinitionsDescriptor;\n\n/**\n * @author Celine Souchet\n */\npublic class SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy\n        extends AbstractProcessDeploymentInfoSearchEntity {\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    private final long userId;\n\n    public SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(\n            final ProcessDefinitionService processDefinitionService,\n            final SearchProcessDefinitionsDescriptor searchEntitiesDescriptor, final SearchOptions options,\n            final long userId) {\n        super(searchEntitiesDescriptor, options);\n        this.processDefinitionService = processDefinitionService;\n        this.userId = userId;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions queryOptions) throws SBonitaReadException {\n        return processDefinitionService\n                .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(userId, queryOptions);\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> executeSearch(final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        return processDefinitionService.searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(userId,\n                queryOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchProcessInstances.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.process;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractProcessInstanceSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor;\n\n/**\n * @author Celine Souchet\n */\npublic class SearchProcessInstances extends AbstractProcessInstanceSearchEntity {\n\n    private final ProcessInstanceService processInstanceService;\n\n    public SearchProcessInstances(final ProcessInstanceService processInstanceService,\n            final SearchProcessInstanceDescriptor searchEntitiesDescriptor,\n            final SearchOptions options, final ProcessDefinitionService processDefinitionService) {\n        super(searchEntitiesDescriptor, options, processDefinitionService);\n        this.processInstanceService = processInstanceService;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions queryOptions) throws SBonitaReadException {\n        return processInstanceService.getNumberOfProcessInstances(queryOptions);\n    }\n\n    @Override\n    public List<SProcessInstance> executeSearch(final QueryOptions queryOptions) throws SBonitaReadException {\n        return processInstanceService.searchProcessInstances(queryOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchUncategorizedProcessDeploymentInfos.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.process;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractProcessDeploymentInfoSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchProcessDefinitionsDescriptor;\n\n/**\n * @author Zhang Bole\n */\npublic class SearchUncategorizedProcessDeploymentInfos extends AbstractProcessDeploymentInfoSearchEntity {\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    public SearchUncategorizedProcessDeploymentInfos(final ProcessDefinitionService processDefinitionService,\n            final SearchProcessDefinitionsDescriptor searchEntitiesDescriptor, final SearchOptions options) {\n        super(searchEntitiesDescriptor, options);\n        this.processDefinitionService = processDefinitionService;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return processDefinitionService.getNumberOfUncategorizedProcessDeploymentInfos(searchOptions);\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> executeSearch(final QueryOptions searchOptions)\n            throws SBonitaReadException {\n        return processDefinitionService.searchUncategorizedProcessDeploymentInfos(searchOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchUncategorizedProcessDeploymentInfosCanBeStartedBy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.process;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractProcessDeploymentInfoSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchProcessDefinitionsDescriptor;\n\n/**\n * @author Zhang Bole\n */\npublic class SearchUncategorizedProcessDeploymentInfosCanBeStartedBy extends AbstractProcessDeploymentInfoSearchEntity {\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    private final long userId;\n\n    public SearchUncategorizedProcessDeploymentInfosCanBeStartedBy(\n            final ProcessDefinitionService processDefinitionService,\n            final SearchProcessDefinitionsDescriptor searchEntitiesDescriptor, final SearchOptions options,\n            final long userId) {\n        super(searchEntitiesDescriptor, options);\n        this.processDefinitionService = processDefinitionService;\n        this.userId = userId;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return processDefinitionService.getNumberOfUncategorizedProcessDeploymentInfosCanBeStartedBy(userId,\n                searchOptions);\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> executeSearch(final QueryOptions searchOptions)\n            throws SBonitaReadException {\n        return processDefinitionService.searchUncategorizedProcessDeploymentInfosCanBeStartedBy(userId, searchOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchUncategorizedProcessDeploymentInfosSupervisedBy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.process;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractProcessDeploymentInfoSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchProcessDefinitionsDescriptor;\n\n/**\n * @author Zhang Bole\n */\npublic class SearchUncategorizedProcessDeploymentInfosSupervisedBy extends AbstractProcessDeploymentInfoSearchEntity {\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    private final long userId;\n\n    public SearchUncategorizedProcessDeploymentInfosSupervisedBy(\n            final ProcessDefinitionService processDefinitionService,\n            final SearchProcessDefinitionsDescriptor searchEntitiesDescriptor, final SearchOptions options,\n            final long userId) {\n        super(searchEntitiesDescriptor, options);\n        this.processDefinitionService = processDefinitionService;\n        this.userId = userId;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return processDefinitionService.getNumberOfUncategorizedProcessDeploymentInfosSupervisedBy(userId,\n                searchOptions);\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> executeSearch(final QueryOptions searchOptions)\n            throws SBonitaReadException {\n        return processDefinitionService.searchUncategorizedProcessDeploymentInfosSupervisedBy(userId, searchOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/profile/SearchProfileMembersForProfile.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.profile;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.profile.ProfileMember;\nimport org.bonitasoft.engine.profile.ProfileService;\nimport org.bonitasoft.engine.profile.model.SProfileMember;\nimport org.bonitasoft.engine.search.AbstractSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Zhao Na\n * @author Celine Souchet\n */\npublic class SearchProfileMembersForProfile extends AbstractSearchEntity<ProfileMember, SProfileMember> {\n\n    private final ProfileService profileService;\n\n    private final String querySuffix;\n\n    public SearchProfileMembersForProfile(final String querySuffix, final ProfileService profileService,\n            final SearchEntityDescriptor searchDescriptor,\n            final SearchOptions searchOptions) {\n        super(searchDescriptor, searchOptions);\n        this.querySuffix = querySuffix;\n        this.profileService = profileService;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return profileService.getNumberOfProfileMembers(querySuffix, searchOptions);\n    }\n\n    @Override\n    public List<SProfileMember> executeSearch(final QueryOptions searchOptions) throws SBonitaReadException {\n        return profileService.searchProfileMembers(querySuffix, searchOptions);\n    }\n\n    @Override\n    public List<ProfileMember> convertToClientObjects(final List<SProfileMember> serverObjects) {\n        return ModelConvertor.toProfileMembers(serverObjects);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/profile/SearchProfiles.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.profile;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.profile.Profile;\nimport org.bonitasoft.engine.profile.ProfileService;\nimport org.bonitasoft.engine.profile.model.SProfile;\nimport org.bonitasoft.engine.search.AbstractSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchProfileDescriptor;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Celine Souchet\n */\npublic class SearchProfiles extends AbstractSearchEntity<Profile, SProfile> {\n\n    private final ProfileService profileService;\n\n    public SearchProfiles(final ProfileService profileService, final SearchProfileDescriptor searchProfileDescriptor,\n            final SearchOptions options) {\n        super(searchProfileDescriptor, options);\n        this.profileService = profileService;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions queryOptions) throws SBonitaReadException {\n        return profileService.getNumberOfProfiles(queryOptions);\n    }\n\n    @Override\n    public List<SProfile> executeSearch(final QueryOptions queryOptions) throws SBonitaReadException {\n        return profileService.searchProfiles(queryOptions);\n    }\n\n    @Override\n    public List<Profile> convertToClientObjects(final List<SProfile> serverObjects) {\n        return ModelConvertor.toProfiles(serverObjects);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/supervisor/SearchArchivedActivityInstanceSupervisedBy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.supervisor;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance;\nimport org.bonitasoft.engine.execution.state.FlowNodeStateManager;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Celine Souchet\n */\npublic class SearchArchivedActivityInstanceSupervisedBy\n        extends AbstractSearchEntity<ArchivedActivityInstance, SAActivityInstance> {\n\n    private final ActivityInstanceService activityInstanceService;\n\n    private final FlowNodeStateManager flowNodeStateManager;\n\n    private final Long supervisorId;\n\n    public SearchArchivedActivityInstanceSupervisedBy(final Long supervisorId,\n            final ActivityInstanceService activityInstanceService,\n            final FlowNodeStateManager flowNodeStateManager, final SearchEntityDescriptor searchDescriptor,\n            final SearchOptions searchOptions) {\n        super(searchDescriptor, searchOptions);\n        this.supervisorId = supervisorId;\n        this.activityInstanceService = activityInstanceService;\n        this.flowNodeStateManager = flowNodeStateManager;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return activityInstanceService.getNumberOfArchivedFlowNodeInstancesSupervisedBy(supervisorId,\n                SAActivityInstance.class, searchOptions);\n    }\n\n    @Override\n    public List<SAActivityInstance> executeSearch(final QueryOptions searchOptions) throws SBonitaReadException {\n        return activityInstanceService.searchArchivedFlowNodeInstancesSupervisedBy(supervisorId,\n                SAActivityInstance.class, searchOptions);\n    }\n\n    @Override\n    public List<ArchivedActivityInstance> convertToClientObjects(final List<SAActivityInstance> serverObjects) {\n        return ModelConvertor.toArchivedActivityInstances(serverObjects, flowNodeStateManager);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/supervisor/SearchArchivedFlowNodeInstanceSupervisedBy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.supervisor;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance;\nimport org.bonitasoft.engine.execution.state.FlowNodeStateManager;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Celine Souchet\n */\npublic class SearchArchivedFlowNodeInstanceSupervisedBy\n        extends AbstractSearchEntity<ArchivedFlowNodeInstance, SAFlowNodeInstance> {\n\n    private final FlowNodeInstanceService flowNodeInstanceService;\n\n    private final FlowNodeStateManager flowNodeStateManager;\n\n    private final Long supervisorId;\n\n    public SearchArchivedFlowNodeInstanceSupervisedBy(final Long supervisorId,\n            final FlowNodeInstanceService flowNodeInstanceService,\n            final FlowNodeStateManager flowNodeStateManager, final SearchEntityDescriptor searchDescriptor,\n            final SearchOptions searchOptions) {\n        super(searchDescriptor, searchOptions);\n        this.supervisorId = supervisorId;\n        this.flowNodeInstanceService = flowNodeInstanceService;\n        this.flowNodeStateManager = flowNodeStateManager;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return flowNodeInstanceService.getNumberOfArchivedFlowNodeInstancesSupervisedBy(supervisorId,\n                SAFlowNodeInstance.class, searchOptions);\n    }\n\n    @Override\n    public List<SAFlowNodeInstance> executeSearch(final QueryOptions searchOptions) throws SBonitaReadException {\n        return flowNodeInstanceService.searchArchivedFlowNodeInstancesSupervisedBy(supervisorId,\n                SAFlowNodeInstance.class, searchOptions);\n    }\n\n    @Override\n    public List<ArchivedFlowNodeInstance> convertToClientObjects(final List<SAFlowNodeInstance> serverObjects) {\n        return ModelConvertor.toArchivedFlowNodeInstances(serverObjects, flowNodeStateManager);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/supervisor/SearchArchivedHumanTasksSupervisedBy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.supervisor;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance;\nimport org.bonitasoft.engine.execution.state.FlowNodeStateManager;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractArchivedHumanTaskInstanceSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\n\npublic class SearchArchivedHumanTasksSupervisedBy extends AbstractArchivedHumanTaskInstanceSearchEntity {\n\n    private final ActivityInstanceService activityInstanceService;\n\n    private final Long supervisorId;\n\n    public SearchArchivedHumanTasksSupervisedBy(final Long supervisorId,\n            final ActivityInstanceService activityInstanceService,\n            final FlowNodeStateManager flowNodeStateManager, final SearchEntityDescriptor searchDescriptor,\n            final SearchOptions searchOptions) {\n        super(searchDescriptor, searchOptions, flowNodeStateManager);\n        this.supervisorId = supervisorId;\n        this.activityInstanceService = activityInstanceService;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return activityInstanceService.getNumberOfArchivedHumanTasksSupervisedBy(supervisorId, searchOptions);\n    }\n\n    @Override\n    public List<SAHumanTaskInstance> executeSearch(final QueryOptions searchOptions) throws SBonitaReadException {\n        return activityInstanceService.searchArchivedHumanTasksSupervisedBy(supervisorId, searchOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/supervisor/SearchFlowNodeInstanceSupervisedBy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.supervisor;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.execution.state.FlowNodeStateManager;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\nimport org.bonitasoft.engine.service.ModelConvertor;\n\n/**\n * @author Celine Souchet\n */\npublic class SearchFlowNodeInstanceSupervisedBy extends AbstractSearchEntity<FlowNodeInstance, SFlowNodeInstance> {\n\n    private final FlowNodeInstanceService flowNodeInstanceService;\n\n    private final FlowNodeStateManager flowNodeStateManager;\n\n    private final Long supervisorId;\n\n    public SearchFlowNodeInstanceSupervisedBy(final Long supervisorId,\n            final FlowNodeInstanceService flowNodeInstanceService,\n            final FlowNodeStateManager flowNodeStateManager, final SearchEntityDescriptor searchDescriptor,\n            final SearchOptions searchOptions) {\n        super(searchDescriptor, searchOptions);\n        this.supervisorId = supervisorId;\n        this.flowNodeInstanceService = flowNodeInstanceService;\n        this.flowNodeStateManager = flowNodeStateManager;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return flowNodeInstanceService.getNumberOfFlowNodeInstancesSupervisedBy(supervisorId, SFlowNodeInstance.class,\n                searchOptions);\n    }\n\n    @Override\n    public List<SFlowNodeInstance> executeSearch(final QueryOptions searchOptions) throws SBonitaReadException {\n        return flowNodeInstanceService.searchFlowNodeInstancesSupervisedBy(supervisorId, SFlowNodeInstance.class,\n                searchOptions);\n    }\n\n    @Override\n    public List<FlowNodeInstance> convertToClientObjects(final List<SFlowNodeInstance> serverObjects) {\n        return ModelConvertor.toFlowNodeInstances(serverObjects, flowNodeStateManager);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/supervisor/SearchProcessDeploymentInfosSupervised.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.supervisor;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractProcessDeploymentInfoSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchProcessDefinitionsDescriptor;\n\npublic class SearchProcessDeploymentInfosSupervised extends AbstractProcessDeploymentInfoSearchEntity {\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    private final long userId;\n\n    public SearchProcessDeploymentInfosSupervised(final ProcessDefinitionService processDefinitionService,\n            final SearchProcessDefinitionsDescriptor searchEntitiesDescriptor, final SearchOptions options,\n            final long userId) {\n        super(searchEntitiesDescriptor, options);\n        this.processDefinitionService = processDefinitionService;\n        this.userId = userId;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return processDefinitionService.getNumberOfProcessDeploymentInfos(userId, searchOptions, \"UserSupervised\");\n    }\n\n    @Override\n    public List<SProcessDefinitionDeployInfo> executeSearch(final QueryOptions searchOptions)\n            throws SBonitaReadException {\n        return processDefinitionService.searchProcessDeploymentInfos(userId, searchOptions, \"UserSupervised\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/supervisor/SearchSupervisors.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.supervisor;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractSupervisorSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\nimport org.bonitasoft.engine.supervisor.mapping.SupervisorMappingService;\nimport org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class SearchSupervisors extends AbstractSupervisorSearchEntity {\n\n    private final SupervisorMappingService supervisorService;\n\n    public SearchSupervisors(final SupervisorMappingService supervisorService,\n            final SearchEntityDescriptor searchDescriptor,\n            final SearchOptions options) {\n        super(searchDescriptor, options);\n        this.supervisorService = supervisorService;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return supervisorService.getNumberOfProcessSupervisors(searchOptions);\n    }\n\n    @Override\n    public List<SProcessSupervisor> executeSearch(final QueryOptions searchOptions) throws SBonitaReadException {\n        return supervisorService.searchProcessSupervisors(searchOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/task/SearchArchivedTasks.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.task;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance;\nimport org.bonitasoft.engine.execution.state.FlowNodeStateManager;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractArchivedHumanTaskInstanceSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchArchivedHumanTaskInstanceDescriptor;\n\n/**\n * @author Zhang Bole\n * @author Celine Souchet\n */\npublic class SearchArchivedTasks extends AbstractArchivedHumanTaskInstanceSearchEntity {\n\n    private final ActivityInstanceService activityInstanceService;\n\n    public SearchArchivedTasks(final ActivityInstanceService activityInstanceService,\n            final FlowNodeStateManager flowNodeStateManager,\n            final SearchArchivedHumanTaskInstanceDescriptor searchArchivedTasksDescriptor,\n            final SearchOptions options) {\n        super(searchArchivedTasksDescriptor, options, flowNodeStateManager);\n        this.activityInstanceService = activityInstanceService;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return activityInstanceService.getNumberOfArchivedTasks(searchOptions);\n    }\n\n    @Override\n    public List<SAHumanTaskInstance> executeSearch(final QueryOptions searchOptions) throws SBonitaReadException {\n        return activityInstanceService.searchArchivedTasks(searchOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/task/SearchArchivedTasksManagedBy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.task;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance;\nimport org.bonitasoft.engine.execution.state.FlowNodeStateManager;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractArchivedHumanTaskInstanceSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchArchivedHumanTaskInstanceDescriptor;\n\n/**\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n */\npublic class SearchArchivedTasksManagedBy extends AbstractArchivedHumanTaskInstanceSearchEntity {\n\n    private final ActivityInstanceService activityInstanceService;\n\n    private final long managerUserId;\n\n    public SearchArchivedTasksManagedBy(final long managerUserId, final SearchOptions options,\n            final ActivityInstanceService activityInstanceService,\n            final FlowNodeStateManager flowNodeStateManager,\n            final SearchArchivedHumanTaskInstanceDescriptor searchArchivedHumanTaskInstanceDescriptor) {\n        super(searchArchivedHumanTaskInstanceDescriptor, options, flowNodeStateManager);\n        this.managerUserId = managerUserId;\n        this.activityInstanceService = activityInstanceService;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return activityInstanceService.getNumberOfArchivedTasksManagedBy(managerUserId, searchOptions);\n    }\n\n    @Override\n    public List<SAHumanTaskInstance> executeSearch(final QueryOptions searchOptions) throws SBonitaReadException {\n        return activityInstanceService.searchArchivedTasksManagedBy(managerUserId, searchOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/APIAccessResolver.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service;\n\nimport org.bonitasoft.engine.exception.APIImplementationNotFoundException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface APIAccessResolver {\n\n    <T> T getAPIImplementation(Class<T> apiInterface) throws APIImplementationNotFoundException;\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/FormRequiredAnalyzer.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.flownode.ActivityDefinition;\nimport org.bonitasoft.engine.bpm.flownode.UserTaskDefinition;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.form.SFormMapping;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\n\npublic class FormRequiredAnalyzer {\n\n    private ProcessDefinitionService processDefinitionService;\n\n    public FormRequiredAnalyzer(ProcessDefinitionService processDefinitionService) {\n        this.processDefinitionService = processDefinitionService;\n    }\n\n    /**\n     * Is a form required to start case / execute task: if there is a contract, a form is required (by the web part\n     * only, the Engine never needs a form mapping\n     * to start a case / execute a task)\n     *\n     * @param sFormMapping\n     * @return true if a form is required for this form mapping, false otherwise.\n     */\n    public boolean isFormRequired(SFormMapping sFormMapping) {\n        if (sFormMapping.getType() == SFormMapping.TYPE_PROCESS_OVERVIEW) {\n            return false;\n        }\n        try {\n            final DesignProcessDefinition designProcessDefinition = processDefinitionService\n                    .getDesignProcessDefinition(sFormMapping.getProcessDefinitionId());\n            if (designProcessDefinition != null) {\n                if (sFormMapping.getType() == SFormMapping.TYPE_PROCESS_START) {\n                    return designProcessDefinition.getContract() != null\n                            && designProcessDefinition.getContract().getInputs() != null\n                            && !designProcessDefinition.getContract().getInputs().isEmpty();\n                } else // if (sFormMapping.getType() == TYPE_TASK)\n                {\n                    final List<ActivityDefinition> activities = designProcessDefinition.getFlowElementContainer()\n                            .getActivities();\n                    final UserTaskDefinition userTaskDefinition = findActivityWithName(activities,\n                            sFormMapping.getTask());\n                    return userTaskDefinition != null && userTaskDefinition.getContract() != null\n                            && userTaskDefinition.getContract().getInputs() != null\n                            && !userTaskDefinition.getContract().getInputs().isEmpty();\n                }\n            }\n        } catch (SBonitaException e) {\n        }\n        return false;\n    }\n\n    protected UserTaskDefinition findActivityWithName(List<ActivityDefinition> activities, String task) {\n        if (task != null && activities != null) {\n            for (ActivityDefinition activity : activities) {\n                if (task.equals(activity.getName()) && activity instanceof UserTaskDefinition) {\n                    return (UserTaskDefinition) activity;\n                }\n            }\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/InstallationFailedException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service;\n\npublic class InstallationFailedException extends Exception {\n\n    private static final long serialVersionUID = 1L;\n\n    public InstallationFailedException(String message, Exception e) {\n        super(message, e);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/InstallationService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service;\n\npublic interface InstallationService {\n\n    /**\n     * Install binaries archive content\n     * If a configuration archive is provided, it also update the configuration\n     */\n    void install(byte[] binaries, byte[] configuration) throws InstallationFailedException;\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/ModelConvertor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Set;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.actor.mapping.model.SActor;\nimport org.bonitasoft.engine.actor.mapping.model.SActorMember;\nimport org.bonitasoft.engine.api.impl.SessionInfos;\nimport org.bonitasoft.engine.bpm.actor.ActorInstance;\nimport org.bonitasoft.engine.bpm.actor.ActorMember;\nimport org.bonitasoft.engine.bpm.actor.impl.ActorInstanceImpl;\nimport org.bonitasoft.engine.bpm.actor.impl.ActorMemberImpl;\nimport org.bonitasoft.engine.bpm.category.Category;\nimport org.bonitasoft.engine.bpm.category.impl.CategoryImpl;\nimport org.bonitasoft.engine.bpm.comment.ArchivedComment;\nimport org.bonitasoft.engine.bpm.comment.Comment;\nimport org.bonitasoft.engine.bpm.comment.impl.ArchivedCommentImpl;\nimport org.bonitasoft.engine.bpm.comment.impl.CommentImpl;\nimport org.bonitasoft.engine.bpm.connector.ArchivedConnectorInstance;\nimport org.bonitasoft.engine.bpm.connector.ConnectorImplementationDescriptor;\nimport org.bonitasoft.engine.bpm.connector.ConnectorInstance;\nimport org.bonitasoft.engine.bpm.connector.ConnectorInstanceWithFailureInfo;\nimport org.bonitasoft.engine.bpm.connector.ConnectorState;\nimport org.bonitasoft.engine.bpm.connector.impl.ArchivedConnectorInstanceImpl;\nimport org.bonitasoft.engine.bpm.connector.impl.ConnectorInstanceImpl;\nimport org.bonitasoft.engine.bpm.connector.impl.ConnectorInstanceWithFailureInfoImpl;\nimport org.bonitasoft.engine.bpm.contract.ContractDefinition;\nimport org.bonitasoft.engine.bpm.contract.InputDefinition;\nimport org.bonitasoft.engine.bpm.contract.Type;\nimport org.bonitasoft.engine.bpm.contract.impl.ConstraintDefinitionImpl;\nimport org.bonitasoft.engine.bpm.contract.impl.ContractDefinitionImpl;\nimport org.bonitasoft.engine.bpm.contract.impl.InputDefinitionImpl;\nimport org.bonitasoft.engine.bpm.data.ArchivedDataInstance;\nimport org.bonitasoft.engine.bpm.data.DataDefinition;\nimport org.bonitasoft.engine.bpm.data.DataInstance;\nimport org.bonitasoft.engine.bpm.data.impl.ArchivedDataInstanceImpl;\nimport org.bonitasoft.engine.bpm.data.impl.BlobDataInstanceImpl;\nimport org.bonitasoft.engine.bpm.data.impl.BooleanDataInstanceImpl;\nimport org.bonitasoft.engine.bpm.data.impl.DataDefinitionImpl;\nimport org.bonitasoft.engine.bpm.data.impl.DataInstanceImpl;\nimport org.bonitasoft.engine.bpm.data.impl.DateDataInstanceImpl;\nimport org.bonitasoft.engine.bpm.data.impl.DoubleDataInstanceImpl;\nimport org.bonitasoft.engine.bpm.data.impl.FloatDataInstanceImpl;\nimport org.bonitasoft.engine.bpm.data.impl.IntegerDataInstanceImpl;\nimport org.bonitasoft.engine.bpm.data.impl.LongDataInstanceImpl;\nimport org.bonitasoft.engine.bpm.data.impl.ShortTextDataInstanceImpl;\nimport org.bonitasoft.engine.bpm.document.ArchivedDocument;\nimport org.bonitasoft.engine.bpm.document.Document;\nimport org.bonitasoft.engine.bpm.document.impl.ArchivedDocumentImpl;\nimport org.bonitasoft.engine.bpm.document.impl.DocumentImpl;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ActivityStates;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedAutomaticTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedCallActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedGatewayInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedLoopActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedManualTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedReceiveTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedSendTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedSubProcessActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedUserTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.BPMEventType;\nimport org.bonitasoft.engine.bpm.flownode.EventInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstance;\nimport org.bonitasoft.engine.bpm.flownode.GatewayInstance;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.ManualTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.StateCategory;\nimport org.bonitasoft.engine.bpm.flownode.TaskPriority;\nimport org.bonitasoft.engine.bpm.flownode.TimerEventTriggerInstance;\nimport org.bonitasoft.engine.bpm.flownode.UserTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.WaitingEvent;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.ActivityInstanceImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.ArchivedAutomaticTaskInstanceImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.ArchivedCallActivityInstanceImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.ArchivedFlowNodeInstanceImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.ArchivedGatewayInstanceImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.ArchivedHumanTaskInstanceImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.ArchivedLoopActivityInstanceImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.ArchivedManualTaskInstanceImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.ArchivedMultiInstanceActivityInstanceImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.ArchivedReceiveTaskInstanceImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.ArchivedSendTaskInstanceImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.ArchivedSubProcessActivityInstanceImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.ArchivedUserTaskInstanceImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.AutomaticTaskInstanceImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.BoundaryEventInstanceImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.CallActivityInstanceImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.EndEventInstanceImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.EventInstanceImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.FlowNodeInstanceImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.GatewayInstanceImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.HumanTaskInstanceImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.IntermediateCatchEventInstanceImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.IntermediateThrowEventInstanceImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.LoopActivityInstanceImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.ManualTaskInstanceImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.MultiInstanceActivityInstanceImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.ReceiveTaskInstanceImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.SendTaskInstanceImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.StartEventInstanceImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.SubProcessActivityInstanceImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.TimerEventTriggerInstanceImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.UserTaskInstanceImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.WaitingErrorEventImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.WaitingEventImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.WaitingMessageEventImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.WaitingSignalEventImpl;\nimport org.bonitasoft.engine.bpm.process.ActivationState;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ConfigurationState;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessInstanceBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.internal.ArchivedProcessInstanceImpl;\nimport org.bonitasoft.engine.bpm.process.impl.internal.ProcessDefinitionImpl;\nimport org.bonitasoft.engine.bpm.process.impl.internal.ProcessDeploymentInfoImpl;\nimport org.bonitasoft.engine.bpm.supervisor.ProcessSupervisor;\nimport org.bonitasoft.engine.bpm.supervisor.impl.ProcessSupervisorImpl;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.business.data.BusinessDataReference;\nimport org.bonitasoft.engine.business.data.impl.MultipleBusinessDataReferenceImpl;\nimport org.bonitasoft.engine.business.data.impl.SimpleBusinessDataReferenceImpl;\nimport org.bonitasoft.engine.command.CommandDescriptor;\nimport org.bonitasoft.engine.command.CommandDescriptorImpl;\nimport org.bonitasoft.engine.command.model.SCommand;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException;\nimport org.bonitasoft.engine.core.category.model.SCategory;\nimport org.bonitasoft.engine.core.connector.parser.SConnectorImplementationDescriptor;\nimport org.bonitasoft.engine.core.document.api.DocumentService;\nimport org.bonitasoft.engine.core.document.model.AbstractSMappedDocument;\nimport org.bonitasoft.engine.core.document.model.archive.SAMappedDocument;\nimport org.bonitasoft.engine.core.form.SFormMapping;\nimport org.bonitasoft.engine.core.operation.model.SLeftOperand;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.core.operation.model.SOperatorType;\nimport org.bonitasoft.engine.core.operation.model.builder.SLeftOperandBuilderFactory;\nimport org.bonitasoft.engine.core.operation.model.builder.SOperationBuilderFactory;\nimport org.bonitasoft.engine.core.process.comment.model.SComment;\nimport org.bonitasoft.engine.core.process.comment.model.archive.SAComment;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.core.process.definition.model.SConstraintDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SContractDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SInputDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.core.process.definition.model.SType;\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstanceWithFailureInfo;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SGatewayInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAAutomaticTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SACallActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAConnectorInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAGatewayInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SALoopActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAManualTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAMultiInstanceActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAReceiveTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SASendTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SASubProcessActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingErrorEvent;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent;\nimport org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance;\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.SADataInstance;\nimport org.bonitasoft.engine.exception.UnknownElementType;\nimport org.bonitasoft.engine.execution.state.FlowNodeStateManager;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.impl.ExpressionImpl;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.expression.model.builder.SExpressionBuilder;\nimport org.bonitasoft.engine.expression.model.builder.SExpressionBuilderFactory;\nimport org.bonitasoft.engine.form.FormMapping;\nimport org.bonitasoft.engine.form.FormMappingTarget;\nimport org.bonitasoft.engine.form.FormMappingType;\nimport org.bonitasoft.engine.identity.ContactData;\nimport org.bonitasoft.engine.identity.ContactDataCreator.ContactDataField;\nimport org.bonitasoft.engine.identity.Group;\nimport org.bonitasoft.engine.identity.GroupCreator;\nimport org.bonitasoft.engine.identity.GroupCreator.GroupField;\nimport org.bonitasoft.engine.identity.Icon;\nimport org.bonitasoft.engine.identity.Role;\nimport org.bonitasoft.engine.identity.RoleCreator;\nimport org.bonitasoft.engine.identity.RoleCreator.RoleField;\nimport org.bonitasoft.engine.identity.SIcon;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.identity.UserCreator;\nimport org.bonitasoft.engine.identity.UserCreator.UserField;\nimport org.bonitasoft.engine.identity.UserMembership;\nimport org.bonitasoft.engine.identity.impl.ContactDataImpl;\nimport org.bonitasoft.engine.identity.impl.CustomUserInfoDefinitionImpl;\nimport org.bonitasoft.engine.identity.impl.CustomUserInfoValueImpl;\nimport org.bonitasoft.engine.identity.impl.GroupImpl;\nimport org.bonitasoft.engine.identity.impl.IconImpl;\nimport org.bonitasoft.engine.identity.impl.RoleImpl;\nimport org.bonitasoft.engine.identity.impl.UserImpl;\nimport org.bonitasoft.engine.identity.impl.UserMembershipImpl;\nimport org.bonitasoft.engine.identity.model.SContactInfo;\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoValue;\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.identity.model.SRole;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.identity.model.SUserLogin;\nimport org.bonitasoft.engine.identity.model.SUserMembership;\nimport org.bonitasoft.engine.identity.xml.ExportedCustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.xml.ExportedGroup;\nimport org.bonitasoft.engine.identity.xml.ExportedRole;\nimport org.bonitasoft.engine.identity.xml.ExportedUser;\nimport org.bonitasoft.engine.identity.xml.ExportedUserMembership;\nimport org.bonitasoft.engine.job.FailedJob;\nimport org.bonitasoft.engine.job.impl.FailedJobImpl;\nimport org.bonitasoft.engine.operation.Operation;\nimport org.bonitasoft.engine.operation.OperatorType;\nimport org.bonitasoft.engine.operation.impl.LeftOperandImpl;\nimport org.bonitasoft.engine.operation.impl.OperationImpl;\nimport org.bonitasoft.engine.page.PageURL;\nimport org.bonitasoft.engine.page.SPageMapping;\nimport org.bonitasoft.engine.page.SPageURL;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.platform.Platform;\nimport org.bonitasoft.engine.platform.command.model.SPlatformCommand;\nimport org.bonitasoft.engine.platform.impl.PlatformImpl;\nimport org.bonitasoft.engine.platform.model.SPlatform;\nimport org.bonitasoft.engine.platform.model.SPlatformProperties;\nimport org.bonitasoft.engine.profile.Profile;\nimport org.bonitasoft.engine.profile.ProfileMember;\nimport org.bonitasoft.engine.profile.ProfileMemberCreator;\nimport org.bonitasoft.engine.profile.ProfileMemberCreator.ProfileMemberField;\nimport org.bonitasoft.engine.profile.impl.ProfileImpl;\nimport org.bonitasoft.engine.profile.impl.ProfileMemberImpl;\nimport org.bonitasoft.engine.profile.model.SProfile;\nimport org.bonitasoft.engine.profile.model.SProfileMember;\nimport org.bonitasoft.engine.resources.STenantResourceLight;\nimport org.bonitasoft.engine.scheduler.model.SFailedJob;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.impl.APISessionImpl;\nimport org.bonitasoft.engine.session.model.SSession;\nimport org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor;\nimport org.bonitasoft.engine.tenant.TenantResource;\nimport org.bonitasoft.engine.tenant.TenantResourceState;\n\n/**\n * @author Matthieu Chaffotte\n * @author Yanyan Liu\n * @author Celine Souchet\n */\n@Slf4j\npublic class ModelConvertor {\n\n    protected ModelConvertor() {\n        // private constructor\n    }\n\n    public static APISession toAPISession(final SSession session) {\n        final long id = session.getId();\n        final long userId = session.getUserId();\n        final String userName = session.getUserName();\n        final Date creationDate = session.getCreationDate();\n        final long duration = session.getDuration();\n        final boolean technicalUser = session.isTechnicalUser();\n        final APISessionImpl apiSession = new APISessionImpl(id, creationDate, duration, userName, userId);\n        apiSession.setTechnicalUser(technicalUser);\n        apiSession.setProfiles(session.getProfiles());\n        return apiSession;\n    }\n\n    public static Platform toPlatform(final SPlatform sPlatform, SPlatformProperties sPlatformProperties) {\n        return new PlatformImpl(sPlatformProperties.getPlatformVersion(), sPlatform.getInitialBonitaVersion(),\n                sPlatform.getCreatedBy(), sPlatform.getCreated(), sPlatform.isMaintenanceEnabled());\n    }\n\n    public static List<ActivityInstance> toActivityInstances(final List<SActivityInstance> sActivities,\n            final FlowNodeStateManager flowNodeStateManager) {\n        final List<ActivityInstance> activityInstances = new ArrayList<>();\n        for (final SActivityInstance sActivity : sActivities) {\n            final ActivityInstance activityInstance = toActivityInstance(sActivity, flowNodeStateManager);\n            activityInstances.add(activityInstance);\n        }\n        return activityInstances;\n    }\n\n    public static List<FlowNodeInstance> toFlowNodeInstances(final List<SFlowNodeInstance> sFlowNodes,\n            final FlowNodeStateManager flowNodeStateManager) {\n        final List<FlowNodeInstance> flowNodeInstances = new ArrayList<>();\n        for (final SFlowNodeInstance sFlowNode : sFlowNodes) {\n            final FlowNodeInstance flowNodeInstance = toFlowNodeInstance(sFlowNode, flowNodeStateManager);\n            flowNodeInstances.add(flowNodeInstance);\n        }\n        return flowNodeInstances;\n    }\n\n    private static void updateFlowNode(final FlowNodeInstanceImpl flowNode, final SFlowNodeInstance sflowNode,\n            final String state) {\n        flowNode.setId(sflowNode.getId());\n        flowNode.setState(state);\n        flowNode.setParentContainerId(sflowNode.getParentContainerId());\n        flowNode.setRootContainerId(sflowNode.getRootContainerId());\n        flowNode.setProcessDefinitionId(sflowNode.getLogicalGroup(0));\n        flowNode.setParentProcessInstanceId(sflowNode.getLogicalGroup(3));\n        flowNode.setDisplayName(sflowNode.getDisplayName());\n        flowNode.setDisplayDescription(sflowNode.getDisplayDescription());\n        flowNode.setDescription(sflowNode.getDescription());\n        flowNode.setExecutedBy(sflowNode.getExecutedBy());\n        flowNode.setExecutedBySubstitute(sflowNode.getExecutedBySubstitute());\n        flowNode.setStateCategory(StateCategory.valueOf(sflowNode.getStateCategory().name()));\n        flowNode.setReachedSateDate(new Date(sflowNode.getReachedStateDate()));\n        flowNode.setLastUpdateDate(new Date(sflowNode.getLastUpdateDate()));\n    }\n\n    public static ActivityInstance toActivityInstance(final SActivityInstance sActivity,\n            final FlowNodeStateManager flowNodeStateManager) {\n        switch (sActivity.getType()) {\n            case AUTOMATIC_TASK:\n                return toAutomaticTask((SAutomaticTaskInstance) sActivity, flowNodeStateManager);\n            case MANUAL_TASK:\n                return toManualTask((SManualTaskInstance) sActivity, flowNodeStateManager);\n            case USER_TASK:\n                return toUserTaskInstance((SUserTaskInstance) sActivity, flowNodeStateManager);\n            case RECEIVE_TASK:\n                return toReceiveTaskInstance((SReceiveTaskInstance) sActivity, flowNodeStateManager);\n            case SEND_TASK:\n                return toSendTaskInstance((SSendTaskInstance) sActivity, flowNodeStateManager);\n            case CALL_ACTIVITY:\n                return toCallActivityInstance((SCallActivityInstance) sActivity, flowNodeStateManager);\n            case SUB_PROCESS:\n                return toSubProcessActivityInstance((SSubProcessActivityInstance) sActivity, flowNodeStateManager);\n            case LOOP_ACTIVITY:\n                return toLoopActivityInstance((SLoopActivityInstance) sActivity, flowNodeStateManager);\n            case MULTI_INSTANCE_ACTIVITY:\n                return toMultiInstanceActivityInstance((SMultiInstanceActivityInstance) sActivity,\n                        flowNodeStateManager);\n            default:\n                throw new UnknownElementType(sActivity.getType().name());\n        }\n    }\n\n    public static FlowNodeInstance toFlowNodeInstance(final SFlowNodeInstance sFlowNode,\n            final FlowNodeStateManager flowNodeStateManager) {\n        return switch (sFlowNode.getType()) {\n            case START_EVENT, INTERMEDIATE_CATCH_EVENT, BOUNDARY_EVENT, INTERMEDIATE_THROW_EVENT, END_EVENT ->\n                toEventInstance((SEventInstance) sFlowNode, flowNodeStateManager);\n            case GATEWAY -> toGatewayInstance((SGatewayInstance) sFlowNode, flowNodeStateManager);\n            default -> {\n                if (sFlowNode instanceof SActivityInstance) {\n                    yield toActivityInstance((SActivityInstance) sFlowNode, flowNodeStateManager);\n                }\n                throw new UnknownElementType(sFlowNode.getType().name());\n            }\n        };\n    }\n\n    public static ActivityInstance toAutomaticTask(final SAutomaticTaskInstance sActivity,\n            final FlowNodeStateManager flowNodeStateManager) {\n        final AutomaticTaskInstanceImpl automaticTaskInstance = new AutomaticTaskInstanceImpl(sActivity.getName(),\n                sActivity.getFlowNodeDefinitionId());\n        updateActivityInstance(sActivity, flowNodeStateManager, automaticTaskInstance);\n        return automaticTaskInstance;\n    }\n\n    public static ActivityInstance toCallActivityInstance(final SCallActivityInstance sActivity,\n            final FlowNodeStateManager flowNodeStateManager) {\n        final CallActivityInstanceImpl callActivityInstance = new CallActivityInstanceImpl(sActivity.getName(),\n                sActivity.getFlowNodeDefinitionId());\n        updateActivityInstance(sActivity, flowNodeStateManager, callActivityInstance);\n        return callActivityInstance;\n    }\n\n    public static ActivityInstance toCallActivityInstance(final SSubProcessActivityInstance sActivity,\n            final FlowNodeStateManager flowNodeStateManager) {\n        final SubProcessActivityInstanceImpl subProcActivityInstance = new SubProcessActivityInstanceImpl(\n                sActivity.getName(),\n                sActivity.getFlowNodeDefinitionId(), sActivity.isTriggeredByEvent());\n        updateActivityInstance(sActivity, flowNodeStateManager, subProcActivityInstance);\n        return subProcActivityInstance;\n    }\n\n    public static ActivityInstance toSubProcessActivityInstance(final SSubProcessActivityInstance sActivity,\n            final FlowNodeStateManager flowNodeStateManager) {\n        final SubProcessActivityInstanceImpl subProcessActivityInstance = new SubProcessActivityInstanceImpl(\n                sActivity.getName(),\n                sActivity.getFlowNodeDefinitionId(), sActivity.isTriggeredByEvent());\n        updateActivityInstance(sActivity, flowNodeStateManager, subProcessActivityInstance);\n        return subProcessActivityInstance;\n    }\n\n    public static ActivityInstance toLoopActivityInstance(final SLoopActivityInstance sActivity,\n            final FlowNodeStateManager flowNodeStateManager) {\n        final LoopActivityInstanceImpl loopActivityInstance = new LoopActivityInstanceImpl(sActivity.getName(),\n                sActivity.getFlowNodeDefinitionId(),\n                sActivity.getLoopCounter());\n        updateActivityInstance(sActivity, flowNodeStateManager, loopActivityInstance);\n        return loopActivityInstance;\n    }\n\n    public static ActivityInstance toMultiInstanceActivityInstance(final SMultiInstanceActivityInstance sActivity,\n            final FlowNodeStateManager flowNodeStateManager) {\n        final MultiInstanceActivityInstanceImpl loopActivityInstance = new MultiInstanceActivityInstanceImpl(\n                sActivity.getName(),\n                sActivity.getFlowNodeDefinitionId(), sActivity.isSequential(), sActivity.getLoopDataInputRef(),\n                sActivity.getLoopDataOutputRef(),\n                sActivity.getDataInputItemRef(), sActivity.getDataOutputItemRef(),\n                sActivity.getNumberOfActiveInstances(),\n                sActivity.getNumberOfCompletedInstances(), sActivity.getNumberOfTerminatedInstances(),\n                sActivity.getLoopCardinality());\n        updateActivityInstance(sActivity, flowNodeStateManager, loopActivityInstance);\n        return loopActivityInstance;\n    }\n\n    public static GatewayInstance toGatewayInstance(final SGatewayInstance sGatewayInstance,\n            final FlowNodeStateManager flowNodeStateManager) {\n        final GatewayInstanceImpl gatewayInstance = new GatewayInstanceImpl(sGatewayInstance.getName(),\n                sGatewayInstance.getFlowNodeDefinitionId());\n        final String state = flowNodeStateManager.getState(sGatewayInstance.getStateId()).getName();\n        updateFlowNode(gatewayInstance, sGatewayInstance, state);\n        return gatewayInstance;\n    }\n\n    public static ArchivedGatewayInstance toArchivedGatewayInstance(final SAGatewayInstance saGatewayInstance,\n            final FlowNodeStateManager flowNodeStateManager) {\n        final String name = saGatewayInstance.getName();\n        final ArchivedGatewayInstanceImpl aGatewayInstance = new ArchivedGatewayInstanceImpl(name);\n        updateArchivedFlowNodeInstance(aGatewayInstance, saGatewayInstance,\n                flowNodeStateManager.getState(saGatewayInstance.getStateId()).getName());\n        return aGatewayInstance;\n    }\n\n    private static void updateActivityInstance(final SActivityInstance sActivity,\n            final FlowNodeStateManager flowNodeStateManager,\n            final ActivityInstanceImpl activity) {\n        final String state = flowNodeStateManager.getState(sActivity.getStateId()).getName();\n        updateFlowNode(activity, sActivity, state);\n    }\n\n    public static List<UserTaskInstance> toUserTaskInstances(final List<SUserTaskInstance> sUserTasks,\n            final FlowNodeStateManager flowNodeStateManager) {\n        final List<UserTaskInstance> userTaskInstances = new ArrayList<>();\n        for (final SUserTaskInstance sUserTask : sUserTasks) {\n            final UserTaskInstance userTask = toUserTaskInstance(sUserTask, flowNodeStateManager);\n            userTaskInstances.add(userTask);\n        }\n        return userTaskInstances;\n    }\n\n    public static UserTaskInstance toUserTaskInstance(final SUserTaskInstance sUserTask,\n            final FlowNodeStateManager flowNodeStateManager) {\n        final UserTaskInstanceImpl userTaskInstance = new UserTaskInstanceImpl(sUserTask.getName(),\n                sUserTask.getFlowNodeDefinitionId(),\n                sUserTask.getActorId());\n        updateHumanTaskInstance(sUserTask, flowNodeStateManager, userTaskInstance);\n        return userTaskInstance;\n    }\n\n    public static ActivityInstance toReceiveTaskInstance(final SReceiveTaskInstance sReceiveTask,\n            final FlowNodeStateManager flowNodeStateManager) {\n        final ReceiveTaskInstanceImpl receiveTaskInstance = new ReceiveTaskInstanceImpl(sReceiveTask.getName(),\n                sReceiveTask.getFlowNodeDefinitionId());\n        updateActivityInstance(sReceiveTask, flowNodeStateManager, receiveTaskInstance);\n        return receiveTaskInstance;\n    }\n\n    public static ActivityInstance toSendTaskInstance(final SSendTaskInstance sSendTask,\n            final FlowNodeStateManager flowNodeStateManager) {\n        final SendTaskInstanceImpl sendTask = new SendTaskInstanceImpl(sSendTask.getName(),\n                sSendTask.getFlowNodeDefinitionId());\n        updateActivityInstance(sSendTask, flowNodeStateManager, sendTask);\n        return sendTask;\n    }\n\n    private static void updateHumanTaskInstance(final SHumanTaskInstance sHumanTask,\n            final FlowNodeStateManager flowNodeStateManager,\n            final HumanTaskInstanceImpl humanTaskInstance) {\n        updateActivityInstance(sHumanTask, flowNodeStateManager, humanTaskInstance);\n        humanTaskInstance.setAssigneeId(sHumanTask.getAssigneeId());\n        final long claimedDate = sHumanTask.getClaimedDate();\n        if (claimedDate > 0) {\n            humanTaskInstance.setClaimedDate(new Date(claimedDate));\n        }\n        humanTaskInstance.setPriority(TaskPriority.valueOf(sHumanTask.getPriority().name()));\n        final Long expectedEndDate = sHumanTask.getExpectedEndDate();\n        if (expectedEndDate != null) {\n            humanTaskInstance.setExpectedEndDate(new Date(expectedEndDate));\n        }\n    }\n\n    public static List<HumanTaskInstance> toHumanTaskInstances(final List<? extends SHumanTaskInstance> sHumanTasks,\n            final FlowNodeStateManager flowNodeStateManager) {\n        final List<HumanTaskInstance> humanTaskInstances = new ArrayList<>(sHumanTasks.size());\n        for (final SHumanTaskInstance sUserTask : sHumanTasks) {\n            final HumanTaskInstance userTask = toHumanTaskInstance(sUserTask, flowNodeStateManager);\n            humanTaskInstances.add(userTask);\n        }\n        return humanTaskInstances;\n    }\n\n    public static HumanTaskInstance toHumanTaskInstance(final SHumanTaskInstance sHumanTask,\n            final FlowNodeStateManager flowNodeStateManager) {\n        switch (sHumanTask.getType()) {\n            case USER_TASK:\n                return toUserTaskInstance((SUserTaskInstance) sHumanTask, flowNodeStateManager);\n            case MANUAL_TASK:\n                return toManualTask((SManualTaskInstance) sHumanTask, flowNodeStateManager);\n            default:\n                throw new UnknownElementType(sHumanTask.getType().name());\n        }\n    }\n\n    public static ManualTaskInstance toManualTask(final SManualTaskInstance sHumanTask,\n            final FlowNodeStateManager flowNodeStateManager) {\n        final ManualTaskInstanceImpl manualTaskInstance = new ManualTaskInstanceImpl(sHumanTask.getName(),\n                sHumanTask.getFlowNodeDefinitionId(),\n                sHumanTask.getActorId());\n        updateHumanTaskInstance(sHumanTask, flowNodeStateManager, manualTaskInstance);\n        return manualTaskInstance;\n    }\n\n    public static ProcessDefinition toProcessDefinition(final SProcessDefinition sDefinition) {\n        final ProcessDefinitionImpl processDefinitionImpl = new ProcessDefinitionImpl(sDefinition.getName(),\n                sDefinition.getVersion());\n        processDefinitionImpl.setId(sDefinition.getId());\n        processDefinitionImpl.setDescription(sDefinition.getDescription());\n        return processDefinitionImpl;\n    }\n\n    public static List<ProcessInstance> toProcessInstances(final List<SProcessInstance> sProcessInstances,\n            final ProcessDefinitionService processDefinitionService) {\n        final List<ProcessInstance> clientProcessInstances = new ArrayList<>();\n        if (sProcessInstances != null) {\n            final Map<Long, SProcessDefinition> processDefinitions = new HashMap<>();\n            for (final SProcessInstance sProcessInstance : sProcessInstances) {\n                SProcessDefinition sProcessDefinition = processDefinitions.computeIfAbsent(\n                        sProcessInstance.getProcessDefinitionId(),\n                        processDefinitionId -> getProcessDefinition(processDefinitionService, processDefinitionId));\n                clientProcessInstances.add(toProcessInstance(sProcessDefinition, sProcessInstance));\n            }\n        }\n        return Collections.unmodifiableList(clientProcessInstances);\n    }\n\n    /**\n     * @param processDefinitionService the {@link ProcessDefinitionService}\n     * @param processDefinitionId the process definition id\n     * @return the {@link SProcessDefinition} for the processDefinitionId or null if no {@link SProcessDefinition} is\n     *         found.\n     */\n    private static SProcessDefinition getProcessDefinition(final ProcessDefinitionService processDefinitionService,\n            final long processDefinitionId) {\n        try {\n            return processDefinitionService.getProcessDefinition(processDefinitionId);\n        } catch (SProcessDefinitionNotFoundException pdnfe) {\n            // Due to RUNTIME-1828 sProcessDefinition can be null. Do not rethrow exception\n            log.warn(\"No process definition found for process definition id {}.\",\n                    processDefinitionId);\n            return null;\n        } catch (SBonitaReadException e) {\n            throw new SBonitaRuntimeException(e);\n        }\n    }\n\n    public static ProcessInstance toProcessInstance(final SProcessDefinition definition,\n            final SProcessInstance sInstance) {\n        final ProcessInstanceBuilder clientProcessInstanceBuilder = ProcessInstanceBuilder.getInstance()\n                .createNewInstance(sInstance.getName());\n        clientProcessInstanceBuilder.setId(sInstance.getId());\n\n        clientProcessInstanceBuilder\n                .setState(ProcessInstanceState.getFromId(sInstance.getStateId()).name().toLowerCase());\n        if (sInstance.getStartDate() > 0) {\n            clientProcessInstanceBuilder.setStartDate(sInstance.getStartDate());\n        }\n        clientProcessInstanceBuilder.setStartedBy(sInstance.getStartedBy());\n        clientProcessInstanceBuilder.setStartedBySubstitute(sInstance.getStartedBySubstitute());\n        if (sInstance.getEndDate() > 0) {\n            clientProcessInstanceBuilder.setEndDate(sInstance.getEndDate());\n        }\n        clientProcessInstanceBuilder.setLastUpdate(sInstance.getLastUpdate());\n        clientProcessInstanceBuilder.setProcessDefinitionId(sInstance.getProcessDefinitionId());\n        clientProcessInstanceBuilder.setDescription(sInstance.getDescription());\n        clientProcessInstanceBuilder.setRootProcessInstanceId(sInstance.getRootProcessInstanceId());\n        clientProcessInstanceBuilder.setCallerId(sInstance.getCallerId());\n\n        if (definition != null) {\n            for (int i = 1; i <= 5; i++) {\n                clientProcessInstanceBuilder.setStringIndexLabel(i, definition.getStringIndexLabel(i));\n            }\n        }\n        clientProcessInstanceBuilder.setStringIndex1(sInstance.getStringIndex1());\n        clientProcessInstanceBuilder.setStringIndex2(sInstance.getStringIndex2());\n        clientProcessInstanceBuilder.setStringIndex3(sInstance.getStringIndex3());\n        clientProcessInstanceBuilder.setStringIndex4(sInstance.getStringIndex4());\n        clientProcessInstanceBuilder.setStringIndex5(sInstance.getStringIndex5());\n        return clientProcessInstanceBuilder.done();\n    }\n\n    public static List<ProcessDeploymentInfo> toProcessDeploymentInfo(\n            final List<SProcessDefinitionDeployInfo> processDefinitionDIs) {\n        final List<ProcessDeploymentInfo> deploymentInfos = new ArrayList<>();\n        for (final SProcessDefinitionDeployInfo processDefinitionDI : processDefinitionDIs) {\n            final ProcessDeploymentInfo deploymentInfo = toProcessDeploymentInfo(processDefinitionDI);\n            deploymentInfos.add(deploymentInfo);\n        }\n        return deploymentInfos;\n    }\n\n    public static ProcessDeploymentInfo toProcessDeploymentInfo(\n            final SProcessDefinitionDeployInfo processDefinitionDI) {\n        return new ProcessDeploymentInfoImpl(processDefinitionDI.getId(), processDefinitionDI.getProcessId(),\n                processDefinitionDI.getName(),\n                processDefinitionDI.getVersion(), processDefinitionDI.getDescription(),\n                new Date(processDefinitionDI.getDeploymentDate()),\n                processDefinitionDI.getDeployedBy(), ActivationState.valueOf(processDefinitionDI.getActivationState()),\n                ConfigurationState.valueOf(processDefinitionDI.getConfigurationState()),\n                processDefinitionDI.getDisplayName(), new Date(\n                        processDefinitionDI.getLastUpdateDate()),\n                processDefinitionDI.getIconPath(), processDefinitionDI.getDisplayDescription());\n    }\n\n    public static Map<Long, ProcessDeploymentInfo> toProcessDeploymentInfos(\n            final Map<Long, SProcessDefinitionDeployInfo> sProcessDeploymentInfos) {\n        if (sProcessDeploymentInfos != null && !sProcessDeploymentInfos.isEmpty()) {\n            final Map<Long, ProcessDeploymentInfo> processDeploymentInfos = new HashMap<>();\n            final Set<Entry<Long, SProcessDefinitionDeployInfo>> entries = sProcessDeploymentInfos.entrySet();\n            for (final Entry<Long, SProcessDefinitionDeployInfo> entry : entries) {\n                processDeploymentInfos.put(entry.getKey(), toProcessDeploymentInfo(entry.getValue()));\n            }\n            return processDeploymentInfos;\n        }\n        return Collections.emptyMap();\n    }\n\n    public static ArchivedUserTaskInstance toArchivedUserTaskInstance(final SAUserTaskInstance sInstance,\n            final FlowNodeStateManager flowNodeStateManager) {\n        final ArchivedUserTaskInstanceImpl archivedUserTaskInstanceImpl = new ArchivedUserTaskInstanceImpl(\n                sInstance.getName());\n        updateArchivedFlowNodeInstance(archivedUserTaskInstanceImpl, sInstance,\n                flowNodeStateManager.getState(sInstance.getStateId()).getName());\n        updateArchivedHumanTaskInstance(archivedUserTaskInstanceImpl, sInstance);\n        return archivedUserTaskInstanceImpl;\n    }\n\n    public static ArchivedReceiveTaskInstance toArchivedReceiveTaskInstance(final SAReceiveTaskInstance sInstance,\n            final FlowNodeStateManager flowNodeStateManager) {\n        final ArchivedReceiveTaskInstanceImpl archivedReceiveTaskInstanceImpl = new ArchivedReceiveTaskInstanceImpl(\n                sInstance.getName());\n        updateArchivedFlowNodeInstance(archivedReceiveTaskInstanceImpl, sInstance,\n                flowNodeStateManager.getState(sInstance.getStateId()).getName());\n        return archivedReceiveTaskInstanceImpl;\n    }\n\n    public static ArchivedSendTaskInstance toArchivedSendTaskInstance(final SASendTaskInstance sInstance,\n            final FlowNodeStateManager flowNodeStateManager) {\n        final ArchivedSendTaskInstanceImpl archivedSendTaskInstanceImpl = new ArchivedSendTaskInstanceImpl(\n                sInstance.getName());\n        updateArchivedFlowNodeInstance(archivedSendTaskInstanceImpl, sInstance,\n                flowNodeStateManager.getState(sInstance.getStateId()).getName());\n        return archivedSendTaskInstanceImpl;\n    }\n\n    /**\n     * Update the fields of ArchivedHumanTaskInstance from a SAHumanTaskInstance\n     */\n    private static void updateArchivedHumanTaskInstance(final ArchivedHumanTaskInstanceImpl activity,\n            final SAHumanTaskInstance saHumanTask) {\n        activity.setAssigneeId(saHumanTask.getAssigneeId());\n        activity.setPriority(TaskPriority.valueOf(saHumanTask.getPriority().name()));\n        activity.setActorId(saHumanTask.getActorId());\n        if (saHumanTask.getExpectedEndDate() != null) {\n            activity.setExpectedEndDate(new Date(saHumanTask.getExpectedEndDate()));\n        }\n        if (saHumanTask.getClaimedDate() > 0) {\n            activity.setClaimedDate(new Date(saHumanTask.getClaimedDate()));\n        }\n    }\n\n    private static void updateArchivedFlowNodeInstance(final ArchivedFlowNodeInstanceImpl aFlowNode,\n            final SAFlowNodeInstance saFlowNode, final String state) {\n        aFlowNode.setId(saFlowNode.getId());\n        aFlowNode.setState(state);\n        aFlowNode.setParentContainerId(saFlowNode.getParentContainerId());\n        aFlowNode.setRootContainerId(saFlowNode.getRootContainerId());\n        aFlowNode.setSourceObjectId(saFlowNode.getSourceObjectId());\n        aFlowNode.setProcessDefinitionId(saFlowNode.getProcessDefinitionId());\n        aFlowNode.setProcessInstanceId(saFlowNode.getParentProcessInstanceId());\n        aFlowNode.setParentActivityInstanceId(saFlowNode.getParentActivityInstanceId());\n        aFlowNode.setDescription(saFlowNode.getDescription());\n        aFlowNode.setDisplayName(saFlowNode.getDisplayName());\n        aFlowNode.setDisplayDescription(saFlowNode.getDisplayDescription());\n        if (saFlowNode.getArchiveDate() > 0) {\n            aFlowNode.setArchiveDate(new Date(saFlowNode.getArchiveDate()));\n        }\n        aFlowNode.setExecutedBy(saFlowNode.getExecutedBy());\n        aFlowNode.setExecutedBySubstitute(saFlowNode.getExecutedBySubstitute());\n        aFlowNode.setFlownodeDefinitionId(saFlowNode.getFlowNodeDefinitionId());\n        aFlowNode.setTerminal(saFlowNode.isTerminal());\n        aFlowNode.setReachedStateDate(new Date(saFlowNode.getReachedStateDate()));\n        aFlowNode.setLastUpdateDate(new Date(saFlowNode.getLastUpdateDate()));\n    }\n\n    public static List<ArchivedUserTaskInstance> toArchivedUserTaskInstances(final List<SAUserTaskInstance> sInstances,\n            final FlowNodeStateManager flowNodeStateManager) {\n        final List<ArchivedUserTaskInstance> archivedUserTaskInstances = new ArrayList<>();\n        for (final SAUserTaskInstance sAUserTaskInstance : sInstances) {\n            final ArchivedUserTaskInstance archivedUserTaskInstance = toArchivedUserTaskInstance(sAUserTaskInstance,\n                    flowNodeStateManager);\n            archivedUserTaskInstances.add(archivedUserTaskInstance);\n        }\n        return archivedUserTaskInstances;\n    }\n\n    public static List<ArchivedReceiveTaskInstance> toArchivedReceiveTaskInstances(\n            final List<SAReceiveTaskInstance> sInstances,\n            final FlowNodeStateManager flowNodeStateManager) {\n        final List<ArchivedReceiveTaskInstance> archivedReceiveTaskInstances = new ArrayList<>();\n        for (final SAReceiveTaskInstance sAReceiveTaskInstance : sInstances) {\n            final ArchivedReceiveTaskInstance archivedReceiveTaskInstance = toArchivedReceiveTaskInstance(\n                    sAReceiveTaskInstance, flowNodeStateManager);\n            archivedReceiveTaskInstances.add(archivedReceiveTaskInstance);\n        }\n        return archivedReceiveTaskInstances;\n    }\n\n    public static List<ArchivedHumanTaskInstance> toArchivedHumanTaskInstances(\n            final List<? extends SAHumanTaskInstance> sInstances,\n            final FlowNodeStateManager flowNodeStateManager) {\n        final List<ArchivedHumanTaskInstance> archivedUserTaskInstances = new ArrayList<>();\n        for (final SAHumanTaskInstance sInstance : sInstances) {\n            final ArchivedHumanTaskInstance archivedUserTaskInstance = toArchivedHumanTaskInstance(sInstance,\n                    flowNodeStateManager);\n            archivedUserTaskInstances.add(archivedUserTaskInstance);\n        }\n        return archivedUserTaskInstances;\n    }\n\n    public static ArchivedHumanTaskInstance toArchivedHumanTaskInstance(final SAHumanTaskInstance sInstance,\n            final FlowNodeStateManager flowNodeStateManager) {\n        switch (sInstance.getType()) {\n            case MANUAL_TASK:\n                return toArchivedManualTaskInstance((SAManualTaskInstance) sInstance, flowNodeStateManager);\n            case USER_TASK:\n                return toArchivedUserTaskInstance((SAUserTaskInstance) sInstance, flowNodeStateManager);\n            default:\n                throw new UnknownElementType(sInstance.getType().name());\n        }\n    }\n\n    public static ArchivedActivityInstance toArchivedActivityInstance(final SAActivityInstance sInstance,\n            final FlowNodeStateManager flowNodeStateManager) {\n        switch (sInstance.getType()) {\n            case AUTOMATIC_TASK:\n                return toArchivedAutomaticTaskInstance(sInstance, flowNodeStateManager);\n            case MANUAL_TASK:\n                return toArchivedManualTaskInstance((SAManualTaskInstance) sInstance, flowNodeStateManager);\n            case USER_TASK:\n                return toArchivedUserTaskInstance((SAUserTaskInstance) sInstance, flowNodeStateManager);\n            case RECEIVE_TASK:\n                return toArchivedReceiveTaskInstance((SAReceiveTaskInstance) sInstance, flowNodeStateManager);\n            case SEND_TASK:\n                return toArchivedSendTaskInstance((SASendTaskInstance) sInstance, flowNodeStateManager);\n            case LOOP_ACTIVITY:\n                return toArchivedLoopActivityInstance((SALoopActivityInstance) sInstance, flowNodeStateManager);\n            case CALL_ACTIVITY:\n                return toArchivedCallActivityInstance((SACallActivityInstance) sInstance, flowNodeStateManager);\n            case SUB_PROCESS:\n                return toArchivedSubProcessActivityInstance((SASubProcessActivityInstance) sInstance,\n                        flowNodeStateManager);\n            case MULTI_INSTANCE_ACTIVITY:\n                return toArchivedMultiInstanceActivityInstance((SAMultiInstanceActivityInstance) sInstance,\n                        flowNodeStateManager);\n            case BOUNDARY_EVENT:\n            case END_EVENT:\n            case GATEWAY:\n            case INTERMEDIATE_CATCH_EVENT:\n            case INTERMEDIATE_THROW_EVENT:\n            case START_EVENT:\n                throw new UnknownElementType(\"Events are not yet archived\");\n            default:\n                throw new UnknownElementType(sInstance.getType().name());\n        }\n    }\n\n    private static ArchivedLoopActivityInstance toArchivedLoopActivityInstance(final SALoopActivityInstance sInstance,\n            final FlowNodeStateManager flowNodeStateManager) {\n        final ArchivedLoopActivityInstanceImpl archivedloopActivityInstanceImpl = new ArchivedLoopActivityInstanceImpl(\n                sInstance.getName());\n        updateArchivedFlowNodeInstance(archivedloopActivityInstanceImpl, sInstance,\n                flowNodeStateManager.getState(sInstance.getStateId()).getName());\n        archivedloopActivityInstanceImpl.setLoopCounter(sInstance.getLoopCounter());\n        archivedloopActivityInstanceImpl.setLoopMax(sInstance.getLoopMax());\n        return archivedloopActivityInstanceImpl;\n    }\n\n    private static ArchivedMultiInstanceActivityInstanceImpl toArchivedMultiInstanceActivityInstance(\n            final SAMultiInstanceActivityInstance sInstance,\n            final FlowNodeStateManager flowNodeStateManager) {\n        final ArchivedMultiInstanceActivityInstanceImpl archivedMultiInstanceActivityInstanceImpl = new ArchivedMultiInstanceActivityInstanceImpl(\n                sInstance.getName(), sInstance.getFlowNodeDefinitionId(), sInstance.isSequential(),\n                sInstance.getLoopDataInputRef(),\n                sInstance.getLoopDataOutputRef(), sInstance.getDataInputItemRef(), sInstance.getDataOutputItemRef(),\n                sInstance.getNumberOfActiveInstances(),\n                sInstance.getNumberOfCompletedInstances(), sInstance.getNumberOfTerminatedInstances(),\n                sInstance.getLoopCardinality());\n        updateArchivedFlowNodeInstance(archivedMultiInstanceActivityInstanceImpl, sInstance,\n                flowNodeStateManager.getState(sInstance.getStateId()).getName());\n        return archivedMultiInstanceActivityInstanceImpl;\n    }\n\n    public static ArchivedManualTaskInstance toArchivedManualTaskInstance(final SAManualTaskInstance sInstance,\n            final FlowNodeStateManager flowNodeStateManager) {\n        final ArchivedManualTaskInstanceImpl archivedManualTaskInstance = new ArchivedManualTaskInstanceImpl(\n                sInstance.getName());\n        updateArchivedFlowNodeInstance(archivedManualTaskInstance, sInstance,\n                flowNodeStateManager.getState(sInstance.getStateId()).getName());\n        updateArchivedHumanTaskInstance(archivedManualTaskInstance, sInstance);\n        return archivedManualTaskInstance;\n    }\n\n    public static ArchivedCallActivityInstance toArchivedCallActivityInstance(final SACallActivityInstance sInstance,\n            final FlowNodeStateManager flowNodeStateManager) {\n        final ArchivedCallActivityInstanceImpl archivedCallActivityInstanceImpl = new ArchivedCallActivityInstanceImpl(\n                sInstance.getName());\n        updateArchivedFlowNodeInstance(archivedCallActivityInstanceImpl, sInstance,\n                flowNodeStateManager.getState(sInstance.getStateId()).getName());\n        return archivedCallActivityInstanceImpl;\n    }\n\n    public static ArchivedSubProcessActivityInstance toArchivedSubProcessActivityInstance(\n            final SASubProcessActivityInstance sInstance,\n            final FlowNodeStateManager flowNodeStateManager) {\n        final ArchivedSubProcessActivityInstanceImpl archivedSubProcActivityInstanceImpl = new ArchivedSubProcessActivityInstanceImpl(\n                sInstance.getName(),\n                sInstance.isTriggeredByEvent());\n        updateArchivedFlowNodeInstance(archivedSubProcActivityInstanceImpl, sInstance,\n                flowNodeStateManager.getState(sInstance.getStateId()).getName());\n        return archivedSubProcActivityInstanceImpl;\n    }\n\n    public static ArchivedAutomaticTaskInstance toArchivedAutomaticTaskInstance(final SAActivityInstance sInstance,\n            final FlowNodeStateManager flowNodeStateManager) {\n        final ArchivedAutomaticTaskInstanceImpl archivedUserTaskInstanceImpl = new ArchivedAutomaticTaskInstanceImpl(\n                sInstance.getName());\n        updateArchivedFlowNodeInstance(archivedUserTaskInstanceImpl, sInstance,\n                flowNodeStateManager.getState(sInstance.getStateId()).getName());\n        return archivedUserTaskInstanceImpl;\n    }\n\n    public static ArchivedGatewayInstance toArchivedGatewayInstance(final SAActivityInstance sInstance,\n            final FlowNodeStateManager flowNodeStateManager) {\n        final ArchivedGatewayInstanceImpl archGatewayInstanceImpl = new ArchivedGatewayInstanceImpl(\n                sInstance.getName());\n        updateArchivedFlowNodeInstance(archGatewayInstanceImpl, sInstance,\n                flowNodeStateManager.getState(sInstance.getStateId()).getName());\n        return archGatewayInstanceImpl;\n    }\n\n    public static List<ArchivedActivityInstance> toArchivedActivityInstances(\n            final List<SAActivityInstance> saActivityInstances,\n            final FlowNodeStateManager flowNodeStateManager) {\n        final List<ArchivedActivityInstance> archivedActivityInstances = new ArrayList<>();\n        for (final SAActivityInstance saActivityInstance : saActivityInstances) {\n            final ArchivedActivityInstance archivedActivityInstance = toArchivedActivityInstance(saActivityInstance,\n                    flowNodeStateManager);\n            archivedActivityInstances.add(archivedActivityInstance);\n        }\n        return archivedActivityInstances;\n    }\n\n    public static List<ArchivedProcessInstance> toArchivedProcessInstances(\n            final List<SAProcessInstance> saProcessInstances,\n            final ProcessDefinitionService processDefinitionService) {\n        if (saProcessInstances != null) {\n            final List<ArchivedProcessInstance> clientProcessInstances = new ArrayList<>(saProcessInstances.size());\n            final Map<Long, SProcessDefinition> processDefinitions = new HashMap<>(saProcessInstances.size());\n            for (final SAProcessInstance saProcessInstance : saProcessInstances) {\n                SProcessDefinition sProcessDefinition = processDefinitions.computeIfAbsent(\n                        saProcessInstance.getProcessDefinitionId(),\n                        processDefinitionId -> getProcessDefinition(processDefinitionService, processDefinitionId));\n                clientProcessInstances.add(toArchivedProcessInstance(saProcessInstance, sProcessDefinition));\n            }\n            return Collections.unmodifiableList(clientProcessInstances);\n        }\n        return Collections.unmodifiableList(new ArrayList<ArchivedProcessInstance>(1));\n    }\n\n    public static List<ArchivedProcessInstance> toArchivedProcessInstances(\n            final List<SAProcessInstance> sProcessInstances,\n            final SProcessDefinition sProcessDefinition) {\n        final List<ArchivedProcessInstance> clientProcessInstances = new ArrayList<>(sProcessInstances.size());\n        for (final SAProcessInstance sProcessInstance : sProcessInstances) {\n            clientProcessInstances.add(toArchivedProcessInstance(sProcessInstance, sProcessDefinition));\n        }\n        return Collections.unmodifiableList(clientProcessInstances);\n    }\n\n    public static ArchivedProcessInstance toArchivedProcessInstance(final SAProcessInstance sInstance,\n            final SProcessDefinition sProcessDefinition) {\n        final ArchivedProcessInstanceImpl archivedInstance = new ArchivedProcessInstanceImpl(sInstance.getName());\n        archivedInstance.setId(sInstance.getId());\n        final int stateId = sInstance.getStateId();\n        archivedInstance.setStateId(stateId);\n        archivedInstance.setState(ProcessInstanceState.getFromId(stateId).name().toLowerCase());\n        if (sInstance.getStartDate() > 0) {\n            archivedInstance.setStartDate(new Date(sInstance.getStartDate()));\n        }\n        archivedInstance.setStartedBy(sInstance.getStartedBy());\n        archivedInstance.setStartedBySubstitute(sInstance.getStartedBySubstitute());\n        if (sInstance.getEndDate() > 0) {\n            archivedInstance.setEndDate(new Date(sInstance.getEndDate()));\n        }\n        if (sInstance.getArchiveDate() > 0) {\n            archivedInstance.setArchiveDate(new Date(sInstance.getArchiveDate()));\n        }\n        if (sInstance.getLastUpdate() > 0) {\n            archivedInstance.setLastUpdate(new Date(sInstance.getLastUpdate()));\n        }\n        archivedInstance.setProcessDefinitionId(sInstance.getProcessDefinitionId());\n        archivedInstance.setDescription(sInstance.getDescription());\n        archivedInstance.setSourceObjectId(sInstance.getSourceObjectId());\n        archivedInstance.setRootProcessInstanceId(sInstance.getRootProcessInstanceId());\n        archivedInstance.setCallerId(sInstance.getCallerId());\n\n        if (sProcessDefinition != null) {\n            for (int i = 1; i <= 5; i++) {\n                archivedInstance.setStringIndexLabel(i, sProcessDefinition.getStringIndexLabel(i));\n            }\n        }\n        archivedInstance.setStringIndexValue(1, sInstance.getStringIndex1());\n        archivedInstance.setStringIndexValue(2, sInstance.getStringIndex2());\n        archivedInstance.setStringIndexValue(3, sInstance.getStringIndex3());\n        archivedInstance.setStringIndexValue(4, sInstance.getStringIndex4());\n        archivedInstance.setStringIndexValue(5, sInstance.getStringIndex5());\n        return archivedInstance;\n    }\n\n    public static List<Group> toGroups(final List<SGroup> sGroups) {\n        final List<Group> clientGroups = new ArrayList<>();\n        if (sGroups != null) {\n            for (final SGroup sGroup : sGroups) {\n                clientGroups.add(toGroup(sGroup));\n            }\n        }\n        return Collections.unmodifiableList(clientGroups);\n    }\n\n    public static List<ExportedGroup> toExportedGroups(final List<SGroup> sGroups) {\n        final List<ExportedGroup> clientGroups = new ArrayList<>();\n        if (sGroups != null) {\n            for (final SGroup sGroup : sGroups) {\n                clientGroups.add(toExportedGroup(sGroup));\n            }\n        }\n        return Collections.unmodifiableList(clientGroups);\n    }\n\n    public static Group toGroup(final SGroup sGroup) {\n        final GroupImpl group = new GroupImpl(sGroup.getId(), sGroup.getName());\n        group.setParentPath(sGroup.getParentPath());\n        group.setCreatedBy(sGroup.getCreatedBy());\n        group.setCreationDate(new Date(sGroup.getCreationDate()));\n        group.setDescription(sGroup.getDescription());\n        group.setDisplayName(sGroup.getDisplayName());\n        group.setIconId(sGroup.getIconId());\n        group.setLastUpdate(new Date(sGroup.getLastUpdate()));\n        return group;\n    }\n\n    private static ExportedGroup toExportedGroup(final SGroup sGroup) {\n        final ExportedGroup group = new ExportedGroup();\n        group.setName(sGroup.getName());\n        group.setParentPath(sGroup.getParentPath());\n        group.setDescription(sGroup.getDescription());\n        group.setDisplayName(sGroup.getDisplayName());\n        return group;\n    }\n\n    public static User toUser(final SUser sUser) {\n        final UserImpl user = new UserImpl(sUser.getId(), sUser.getUserName());\n        user.setFirstName(sUser.getFirstName());\n        user.setLastName(sUser.getLastName());\n        user.setTitle(sUser.getTitle());\n        user.setJobTitle(sUser.getJobTitle());\n        user.setCreatedBy(sUser.getCreatedBy());\n        user.setCreationDate(new Date(sUser.getCreationDate()));\n        user.setIconId(sUser.getIconId());\n        user.setLastUpdate(new Date(sUser.getLastUpdate()));\n        user.setEnabled(sUser.isEnabled());\n        final long managerUserId = sUser.getManagerUserId();\n        user.setManagerUserId(managerUserId);\n        final SUserLogin sUserLogin = sUser.getSUserLogin();\n        if (sUserLogin != null && sUserLogin.getLastConnection() != null) {\n            user.setLastConnection(new Date(sUserLogin.getLastConnection()));\n        }\n        return user;\n    }\n\n    public static ContactData toUserContactData(final SContactInfo sContactData) {\n        final ContactDataImpl contactData = new ContactDataImpl(sContactData.getUserId());\n        contactData.setAddress(sContactData.getAddress());\n        contactData.setBuilding(sContactData.getBuilding());\n        contactData.setCity(sContactData.getCity());\n        contactData.setCountry(sContactData.getCountry());\n        contactData.setEmail(sContactData.getEmail());\n        contactData.setFaxNumber(sContactData.getFaxNumber());\n        contactData.setMobileNumber(sContactData.getMobileNumber());\n        contactData.setPersonal(sContactData.isPersonal());\n        contactData.setPhoneNumber(sContactData.getPhoneNumber());\n        contactData.setRoom(sContactData.getRoom());\n        contactData.setState(sContactData.getState());\n        contactData.setWebsite(sContactData.getWebsite());\n        contactData.setZipCode(sContactData.getZipCode());\n        return contactData;\n    }\n\n    public static List<User> toUsers(final List<SUser> sUsers) {\n        final List<User> users = new ArrayList<>();\n        if (sUsers != null) {\n            for (final SUser sUser : sUsers) {\n                final User user = ModelConvertor.toUser(sUser);\n                users.add(user);\n            }\n        }\n        return Collections.unmodifiableList(users);\n    }\n\n    public static Role toRole(final SRole sRole) {\n        final RoleImpl role = new RoleImpl(sRole.getId(), sRole.getName());\n        role.setDisplayName(sRole.getDisplayName());\n        role.setDescription(sRole.getDescription());\n        role.setIconId(sRole.getIconId());\n        role.setCreatedBy(sRole.getCreatedBy());\n        role.setCreationDate(new Date(sRole.getCreationDate()));\n        role.setLastUpdate(new Date(sRole.getLastUpdate()));\n        return role;\n    }\n\n    private static ExportedRole toExportedRole(final SRole sRole) {\n        final ExportedRole role = new ExportedRole(sRole.getName());\n        role.setDisplayName(sRole.getDisplayName());\n        role.setDescription(sRole.getDescription());\n        return role;\n    }\n\n    public static List<Role> toRoles(final List<SRole> sRoles) {\n        final List<Role> lightRoles = new ArrayList<>();\n        if (sRoles != null) {\n            for (final SRole sRole : sRoles) {\n                final Role role = toRole(sRole);\n                lightRoles.add(role);\n            }\n        }\n        return Collections.unmodifiableList(lightRoles);\n    }\n\n    public static List<ExportedRole> toExportedRoles(final List<SRole> sRoles) {\n        final List<ExportedRole> lightRoles = new ArrayList<>();\n        if (sRoles != null) {\n            for (final SRole sRole : sRoles) {\n                final ExportedRole role = toExportedRole(sRole);\n                lightRoles.add(role);\n            }\n        }\n        return Collections.unmodifiableList(lightRoles);\n    }\n\n    public static UserMembership toUserMembership(final SUserMembership sUserMembership) {\n        final UserMembershipImpl userMembership = new UserMembershipImpl(sUserMembership.getId(),\n                sUserMembership.getUserId(), sUserMembership.getGroupId(),\n                sUserMembership.getRoleId());\n        userMembership.setAssignedBy(sUserMembership.getAssignedBy());\n        userMembership.setAssignedDate(new Date(sUserMembership.getAssignedDate()));\n        userMembership.setGroupName(sUserMembership.getGroupName());\n        userMembership.setRoleName(sUserMembership.getRoleName());\n        userMembership.setUsername(sUserMembership.getUsername());\n        userMembership.setGroupParentPath(sUserMembership.getGroupParentPath());\n        return userMembership;\n    }\n\n    public static List<UserMembership> toUserMembership(final List<SUserMembership> sUserMemberships) {\n        final List<UserMembership> userMemberships = new ArrayList<>();\n        if (sUserMemberships != null) {\n            for (final SUserMembership sMembership : sUserMemberships) {\n                final UserMembership userMembership = toUserMembership(sMembership);\n                userMemberships.add(userMembership);\n            }\n        }\n        return Collections.unmodifiableList(userMemberships);\n    }\n\n    public static List<UserMembership> toUserMembership(final List<SUserMembership> sUserMemberships,\n            final Map<Long, String> userNames,\n            final Map<Long, String> groupIdToGroup) {\n        final List<UserMembership> userMemberships = new ArrayList<>();\n        if (sUserMemberships != null) {\n            for (final SUserMembership sMembership : sUserMemberships) {\n                final UserMembership userMembership = toUserMembership(sMembership, userNames, groupIdToGroup);\n                userMemberships.add(userMembership);\n            }\n        }\n        return Collections.unmodifiableList(userMemberships);\n    }\n\n    public static List<ExportedUserMembership> toExportedUserMembership(final List<SUserMembership> sUserMemberships,\n            final Map<Long, String> userNames,\n            final Map<Long, String> groupIdToGroup) {\n        final List<ExportedUserMembership> userMemberships = new ArrayList<>();\n        if (sUserMemberships != null) {\n            for (final SUserMembership sMembership : sUserMemberships) {\n                final ExportedUserMembership userMembership = toExportedUserMembership(sMembership, userNames,\n                        groupIdToGroup);\n                userMemberships.add(userMembership);\n            }\n        }\n        return Collections.unmodifiableList(userMemberships);\n    }\n\n    private static UserMembership toUserMembership(final SUserMembership sUserMembership,\n            final Map<Long, String> userNames,\n            final Map<Long, String> groupIdToGroup) {\n        final UserMembershipImpl userMembership = new UserMembershipImpl(sUserMembership.getId(),\n                sUserMembership.getUserId(), sUserMembership.getGroupId(),\n                sUserMembership.getRoleId());\n        userMembership.setGroupName(sUserMembership.getGroupName());\n        userMembership.setGroupParentPath(groupIdToGroup.get(sUserMembership.getGroupId()));\n        userMembership.setRoleName(sUserMembership.getRoleName());\n        userMembership.setUsername(sUserMembership.getUsername());\n        final long assignedBy = sUserMembership.getAssignedBy();\n        userMembership.setAssignedBy(assignedBy);\n        if (assignedBy > 0) {\n            userMembership.setAssignedByName(userNames.get(assignedBy));\n        }\n        userMembership.setAssignedDate(new Date(sUserMembership.getAssignedDate()));\n        return userMembership;\n    }\n\n    private static ExportedUserMembership toExportedUserMembership(final SUserMembership sUserMembership,\n            final Map<Long, String> userNames,\n            final Map<Long, String> groupIdToGroup) {\n        final ExportedUserMembership userMembership = new ExportedUserMembership();\n        userMembership.setGroupName(sUserMembership.getGroupName());\n        userMembership.setGroupParentPath(groupIdToGroup.get(sUserMembership.getGroupId()));\n        userMembership.setRoleName(sUserMembership.getRoleName());\n        userMembership.setUserName(sUserMembership.getUsername());\n        final long assignedBy = sUserMembership.getAssignedBy();\n        userMembership.setAssignedBy(userNames.get(assignedBy));\n        userMembership.setAssignedDate(sUserMembership.getAssignedDate());\n        return userMembership;\n    }\n\n    public static Category toCategory(final SCategory sCategory) {\n        final CategoryImpl category = new CategoryImpl(sCategory.getId(), sCategory.getName());\n        category.setDescription(sCategory.getDescription());\n        category.setCreator(sCategory.getCreator());\n        category.setCreationDate(new Date(sCategory.getCreationDate()));\n        category.setLastUpdate(new Date(sCategory.getLastUpdateDate()));\n        return category;\n    }\n\n    public static CommandDescriptor toCommandDescriptor(final SCommand command) {\n        final CommandDescriptorImpl commandDescriptor = new CommandDescriptorImpl(command.getName(),\n                command.getDescription(), command.getImplementation());\n        commandDescriptor.setId(command.getId());\n        commandDescriptor.setSystem(command.isSystem());\n        return commandDescriptor;\n    }\n\n    public static CommandDescriptor toCommandDescriptor(final SPlatformCommand platformCommand) {\n        final CommandDescriptorImpl commandDescriptor = new CommandDescriptorImpl(platformCommand.getName(),\n                platformCommand.getDescription(),\n                platformCommand.getImplementation());\n        commandDescriptor.setId(platformCommand.getId());\n        return commandDescriptor;\n    }\n\n    public static List<CommandDescriptor> toCommandDescriptors(final List<SCommand> sCommands) {\n        if (sCommands != null) {\n            final List<CommandDescriptor> commandList = new ArrayList<>();\n            for (final SCommand sCommand : sCommands) {\n                commandList.add(toCommandDescriptor(sCommand));\n            }\n            return Collections.unmodifiableList(commandList);\n        }\n        return Collections.emptyList();\n    }\n\n    public static List<CommandDescriptor> toPlatformCommandDescriptors(final List<SPlatformCommand> sPlatformCommands) {\n        if (sPlatformCommands != null) {\n            final List<CommandDescriptor> platformCommandList = new ArrayList<>();\n            for (final SPlatformCommand sCommand : sPlatformCommands) {\n                platformCommandList.add(toCommandDescriptor(sCommand));\n            }\n            return Collections.unmodifiableList(platformCommandList);\n        }\n        return Collections.emptyList();\n    }\n\n    public static List<Category> toCategories(final List<SCategory> sCategories) {\n        if (sCategories != null) {\n            final List<Category> categoryList = new ArrayList<>();\n            for (final SCategory sCategory : sCategories) {\n                categoryList.add(toCategory(sCategory));\n            }\n            return Collections.unmodifiableList(categoryList);\n        }\n        return Collections.emptyList();\n    }\n\n    public static List<EventInstance> toEventInstances(final Collection<SEventInstance> sEvents,\n            final FlowNodeStateManager flowNodeStateManager) {\n        final List<EventInstance> eventInstances = new ArrayList<>();\n        for (final SEventInstance sEvent : sEvents) {\n            final EventInstance eventInstance = toEventInstance(sEvent, flowNodeStateManager);\n            eventInstances.add(eventInstance);\n        }\n        return eventInstances;\n    }\n\n    public static EventInstance toEventInstance(final SEventInstance sEvent,\n            final FlowNodeStateManager flowNodeStateManager) {\n        final EventInstanceImpl eventInstance = getEventInstance(sEvent);\n        updateFlowNode(eventInstance, sEvent, flowNodeStateManager.getState(sEvent.getStateId()).getName());\n        return eventInstance;\n    }\n\n    public static List<TimerEventTriggerInstance> toTimerEventTriggerInstances(\n            final List<STimerEventTriggerInstance> sEventTriggerInstances) {\n        final List<TimerEventTriggerInstance> eventTriggerInstances = new ArrayList<>();\n        for (final STimerEventTriggerInstance sEventTriggerInstance : sEventTriggerInstances) {\n            final TimerEventTriggerInstance eventTriggerInstance = toTimerEventTriggerInstance(sEventTriggerInstance);\n            eventTriggerInstances.add(eventTriggerInstance);\n        }\n        return eventTriggerInstances;\n    }\n\n    public static TimerEventTriggerInstance toTimerEventTriggerInstance(\n            final STimerEventTriggerInstance sTimerEventTriggerInstance) {\n        return new TimerEventTriggerInstanceImpl(sTimerEventTriggerInstance.getId(),\n                sTimerEventTriggerInstance.getEventInstanceId(),\n                sTimerEventTriggerInstance.getEventInstanceName(),\n                new Date(sTimerEventTriggerInstance.getExecutionDate()));\n    }\n\n    public static WaitingEvent toWaitingEvent(final SWaitingEvent sWaitingEvent) {\n        WaitingEventImpl waitingEvent;\n        final BPMEventType bpmEventType = BPMEventType.valueOf(sWaitingEvent.getEventType().name());\n        final long processDefinitionId = sWaitingEvent.getProcessDefinitionId();\n        final String processName = sWaitingEvent.getProcessName();\n        final long flowNodeDefinitionId = sWaitingEvent.getFlowNodeDefinitionId();\n        switch (sWaitingEvent.getEventTriggerType()) {\n            case ERROR:\n                final SWaitingErrorEvent sWaitingErrorEvent = (SWaitingErrorEvent) sWaitingEvent;\n                waitingEvent = new WaitingErrorEventImpl(bpmEventType, processDefinitionId, processName,\n                        flowNodeDefinitionId,\n                        sWaitingErrorEvent.getErrorCode());\n                break;\n            case MESSAGE:\n                final SWaitingMessageEvent sWaitingMessageEvent = (SWaitingMessageEvent) sWaitingEvent;\n                waitingEvent = new WaitingMessageEventImpl(bpmEventType, processDefinitionId, processName,\n                        flowNodeDefinitionId,\n                        sWaitingMessageEvent.getMessageName());\n                break;\n            case SIGNAL:\n                final SWaitingSignalEvent sWaitingSignalEvent = (SWaitingSignalEvent) sWaitingEvent;\n                waitingEvent = new WaitingSignalEventImpl(bpmEventType, processDefinitionId, processName,\n                        flowNodeDefinitionId,\n                        sWaitingSignalEvent.getSignalName());\n                break;\n            default:\n                throw new UnknownElementType(sWaitingEvent.getClass().getName());\n        }\n        waitingEvent.setRootProcessInstanceId(sWaitingEvent.getRootProcessInstanceId());\n        waitingEvent.setParentProcessInstanceId(sWaitingEvent.getParentProcessInstanceId());\n        return waitingEvent;\n    }\n\n    public static List<WaitingEvent> toWaitingEvents(final List<SWaitingEvent> sWaitingEvents) {\n        final List<WaitingEvent> waitingEvents = new ArrayList<>(sWaitingEvents.size());\n        for (final SWaitingEvent sWaitingEvent : sWaitingEvents) {\n            waitingEvents.add(toWaitingEvent(sWaitingEvent));\n        }\n        return Collections.unmodifiableList(waitingEvents);\n    }\n\n    private static EventInstanceImpl getEventInstance(final SEventInstance sEvent) {\n        switch (sEvent.getType()) {\n            case END_EVENT:\n                return new EndEventInstanceImpl(sEvent.getName(), sEvent.getFlowNodeDefinitionId());\n            case INTERMEDIATE_CATCH_EVENT:\n                return new IntermediateCatchEventInstanceImpl(sEvent.getName(), sEvent.getFlowNodeDefinitionId());\n            case INTERMEDIATE_THROW_EVENT:\n                return new IntermediateThrowEventInstanceImpl(sEvent.getName(), sEvent.getFlowNodeDefinitionId());\n            case BOUNDARY_EVENT:\n                return new BoundaryEventInstanceImpl(sEvent.getName(), sEvent.getFlowNodeDefinitionId(),\n                        ((SBoundaryEventInstance) sEvent).getActivityInstanceId());\n            case START_EVENT:\n                return new StartEventInstanceImpl(sEvent.getName(), sEvent.getFlowNodeDefinitionId());\n            default:\n                throw new UnknownElementType(sEvent.getType().name());\n        }\n    }\n\n    public static List<DataInstance> toDataInstances(final List<SDataInstance> sDataInstances) {\n        if (sDataInstances != null) {\n            final List<DataInstance> dataInstanceList = new ArrayList<>();\n            for (final SDataInstance sDataInstance : sDataInstances) {\n                dataInstanceList.add(toDataInstance(sDataInstance));\n            }\n            return Collections.unmodifiableList(dataInstanceList);\n        }\n        return Collections.emptyList();\n    }\n\n    public static List<DataDefinition> toDataDefinitions(final List<SDataDefinition> sDataDefinitions) {\n        if (sDataDefinitions != null) {\n            final List<DataDefinition> dataDefinitionList = new ArrayList<>();\n            for (final SDataDefinition sDataDefinition : sDataDefinitions) {\n                dataDefinitionList.add(toDataDefinition(sDataDefinition));\n            }\n            return Collections.unmodifiableList(dataDefinitionList);\n        }\n        return Collections.emptyList();\n    }\n\n    public static DataDefinition toDataDefinition(final SDataDefinition sDataDefinition) {\n        DataDefinitionImpl dataDefinitionImpl = null;\n        if (sDataDefinition != null) {\n            dataDefinitionImpl = new DataDefinitionImpl(sDataDefinition.getName(),\n                    toExpression(sDataDefinition.getDefaultValueExpression()));\n            dataDefinitionImpl.setClassName(sDataDefinition.getClassName());\n            dataDefinitionImpl.setDescription(sDataDefinition.getDescription());\n            dataDefinitionImpl.setTransientData(sDataDefinition.isTransientData());\n        }\n        return dataDefinitionImpl;\n    }\n\n    public static List<Expression> toExpressions(final List<SExpression> sExpressions) {\n        if (sExpressions != null && !sExpressions.isEmpty()) {\n            final List<Expression> expList = new ArrayList<>(sExpressions.size());\n            for (final SExpression sexp : sExpressions) {\n                expList.add(toExpression(sexp));\n            }\n            return expList;\n        }\n        return Collections.emptyList();\n    }\n\n    public static Expression toExpression(final SExpression sexp) {\n        final ExpressionImpl exp = new ExpressionImpl();\n        if (sexp != null) {\n            exp.setContent(sexp.getContent());\n            exp.setExpressionType(sexp.getExpressionType());\n            exp.setInterpreter(sexp.getInterpreter());\n            exp.setName(sexp.getName());\n            exp.setReturnType(sexp.getReturnType());\n            exp.setDependencies(toExpressions(sexp.getDependencies()));\n        }\n        return exp;\n    }\n\n    public static DataInstance toDataInstance(final SDataInstance sDataInstance) {\n        DataInstanceImpl dataInstance;\n        if (sDataInstance.getClassName().equals(Integer.class.getName())) {\n            dataInstance = new IntegerDataInstanceImpl();\n        } else if (sDataInstance.getClassName().equals(Long.class.getName())) {\n            dataInstance = new LongDataInstanceImpl();\n        } else if (sDataInstance.getClassName().equals(Boolean.class.getName())) {\n            dataInstance = new BooleanDataInstanceImpl();\n        } else if (sDataInstance.getClassName().equals(Date.class.getName())) {\n            dataInstance = new DateDataInstanceImpl();\n        } else if (sDataInstance.getClassName().equals(Double.class.getName())) {\n            dataInstance = new DoubleDataInstanceImpl();\n        } else if (sDataInstance.getClassName().equals(Float.class.getName())) {\n            dataInstance = new FloatDataInstanceImpl();\n        } else if (sDataInstance.getClassName().equals(String.class.getName())) {\n            dataInstance = new ShortTextDataInstanceImpl();\n        } else {\n            dataInstance = new BlobDataInstanceImpl();\n        }\n        dataInstance.setTransientData(sDataInstance.isTransientData());\n        dataInstance.setClassName(sDataInstance.getClassName());\n        dataInstance.setContainerId(sDataInstance.getContainerId());\n        dataInstance.setContainerType(sDataInstance.getContainerType());\n        dataInstance.setDataTypeClassName(sDataInstance.getClassName());\n        dataInstance.setDescription(sDataInstance.getDescription());\n        dataInstance.setId(sDataInstance.getId());\n        dataInstance.setName(sDataInstance.getName());\n        dataInstance.setValue(sDataInstance.getValue());\n        return dataInstance;\n    }\n\n    public static List<ArchivedDataInstance> toArchivedDataInstances(final List<SADataInstance> sADataInstances) {\n        final List<ArchivedDataInstance> dataInstances = new ArrayList<>();\n        for (final SADataInstance sADataInstance : sADataInstances) {\n            final ArchivedDataInstance dataInstance = toArchivedDataInstance(sADataInstance);\n            dataInstances.add(dataInstance);\n        }\n        return dataInstances;\n    }\n\n    public static ArchivedDataInstance toArchivedDataInstance(final SADataInstance sDataInstance) {\n        final ArchivedDataInstanceImpl dataInstance = new ArchivedDataInstanceImpl();\n        dataInstance.setClassName(sDataInstance.getClassName());\n        dataInstance.setContainerId(sDataInstance.getContainerId());\n        dataInstance.setContainerType(sDataInstance.getContainerType());\n        dataInstance.setDataTypeClassName(sDataInstance.getClassName());\n        dataInstance.setDescription(sDataInstance.getDescription());\n        dataInstance.setId(sDataInstance.getId());\n        dataInstance.setName(sDataInstance.getName());\n        dataInstance.setValue(sDataInstance.getValue());\n        dataInstance.setArchiveDate(new Date(sDataInstance.getArchiveDate()));\n        dataInstance.setSourceObjectId(sDataInstance.getSourceObjectId());\n        return dataInstance;\n    }\n\n    public static ActorMember toActorMember(final SActorMember sActorMember) {\n        return new ActorMemberImpl(sActorMember.getId(), sActorMember.getUserId(), sActorMember.getGroupId(),\n                sActorMember.getRoleId());\n    }\n\n    public static List<ActorMember> toActorMembers(final List<SActorMember> sActorMembers) {\n        final List<ActorMember> actorMembers = new ArrayList<>();\n        for (final SActorMember sActorMember : sActorMembers) {\n            final ActorMember actorMember = toActorMember(sActorMember);\n            actorMembers.add(actorMember);\n        }\n        return actorMembers;\n    }\n\n    public static ActorInstance toActorInstance(final SActor actor) {\n        final String name = actor.getName();\n        final String description = actor.getDescription();\n        final long scopeId = actor.getScopeId();\n        final String displayName = actor.getDisplayName();\n        final boolean initiator = actor.isInitiator();\n        final ActorInstanceImpl actorInstance = new ActorInstanceImpl(name, description, displayName, scopeId,\n                initiator);\n        actorInstance.setId(actor.getId());\n        return actorInstance;\n    }\n\n    public static SUser constructSUser(final UserCreator creator) {\n        final long now = System.currentTimeMillis();\n        final SUser.SUserBuilder userBuilder = SUser.builder();\n        final Map<UserField, Serializable> fields = creator.getFields();\n        userBuilder.userName((String) fields.get(UserField.NAME));\n        userBuilder.password((String) fields.get(UserField.PASSWORD));\n        final String firstName = (String) fields.get(UserField.FIRST_NAME);\n        if (firstName != null) {\n            userBuilder.firstName(firstName);\n        }\n        final String lastName = (String) fields.get(UserField.LAST_NAME);\n        if (lastName != null) {\n            userBuilder.lastName(lastName);\n        }\n        final String jobTitle = (String) fields.get(UserField.JOB_TITLE);\n        if (jobTitle != null) {\n            userBuilder.jobTitle(jobTitle);\n        }\n        final String title = (String) fields.get(UserField.TITLE);\n        if (title != null) {\n            userBuilder.title(title);\n        }\n        userBuilder.createdBy(SessionInfos.getUserIdFromSession());\n\n        final Long managerUserId = (Long) fields.get(UserField.MANAGER_ID);\n        if (managerUserId != null) {\n            userBuilder.managerUserId(managerUserId);\n        }\n\n        final Boolean enabled = (Boolean) fields.get(UserField.ENABLED);\n        if (enabled != null) {\n            userBuilder.enabled(enabled);\n        } else {\n            userBuilder.enabled(Boolean.TRUE);\n        }\n        userBuilder.creationDate(now);\n        userBuilder.lastUpdate(now);\n        return userBuilder.build();\n    }\n\n    public static SContactInfo constructSUserContactInfo(final UserCreator creator, final long userId,\n            final boolean personal) {\n        Map<ContactDataField, Serializable> fields;\n        if (personal) {\n            fields = creator.getPersoFields();\n        } else {\n            fields = creator.getProFields();\n        }\n        if (fields != null && !fields.isEmpty()) {\n            final SContactInfo.SContactInfoBuilder contactInfoBuilder = SContactInfo.builder().userId(userId)\n                    .personal(personal);\n            final String address = (String) fields.get(ContactDataField.ADDRESS);\n            if (address != null) {\n                contactInfoBuilder.address(address);\n            }\n            final String email = (String) fields.get(ContactDataField.EMAIL);\n            if (email != null) {\n                contactInfoBuilder.email(email);\n            }\n            final String building = (String) fields.get(ContactDataField.BUILDING);\n            if (building != null) {\n                contactInfoBuilder.building(building);\n            }\n            final String city = (String) fields.get(ContactDataField.CITY);\n            if (city != null) {\n                contactInfoBuilder.city(city);\n            }\n            final String country = (String) fields.get(ContactDataField.COUNTRY);\n            if (country != null) {\n                contactInfoBuilder.country(country);\n            }\n            final String fax = (String) fields.get(ContactDataField.FAX);\n            if (fax != null) {\n                contactInfoBuilder.faxNumber(fax);\n            }\n            final String mobile = (String) fields.get(ContactDataField.MOBILE);\n            if (mobile != null) {\n                contactInfoBuilder.mobileNumber(mobile);\n            }\n            final String phone = (String) fields.get(ContactDataField.PHONE);\n            if (phone != null) {\n                contactInfoBuilder.phoneNumber(phone);\n            }\n            final String room = (String) fields.get(ContactDataField.ROOM);\n            if (room != null) {\n                contactInfoBuilder.room(room);\n            }\n            final String state = (String) fields.get(ContactDataField.STATE);\n            if (state != null) {\n                contactInfoBuilder.state(state);\n            }\n            final String website = (String) fields.get(ContactDataField.WEBSITE);\n            if (website != null) {\n                contactInfoBuilder.website(website);\n            }\n            final String zipCode = (String) fields.get(ContactDataField.ZIP_CODE);\n            if (zipCode != null) {\n                contactInfoBuilder.zipCode(zipCode);\n            }\n            return contactInfoBuilder.build();\n        }\n        return null;\n    }\n\n    public static SContactInfo constructSUserContactInfo(final ExportedUser user, final boolean isPersonal,\n            final long userId) {\n        final SContactInfo.SContactInfoBuilder contactInfoBuilder = SContactInfo.builder().userId(userId)\n                .personal(isPersonal);\n        if (isPersonal) {\n            contactInfoBuilder.address(user.getPersonalAddress());\n            contactInfoBuilder.building(user.getPersonalBuilding());\n            contactInfoBuilder.city(user.getPersonalCity());\n            contactInfoBuilder.country(user.getPersonalCountry());\n            contactInfoBuilder.email(user.getPersonalEmail());\n            contactInfoBuilder.faxNumber(user.getPersonalFaxNumber());\n            contactInfoBuilder.mobileNumber(user.getPersonalMobileNumber());\n            contactInfoBuilder.phoneNumber(user.getPersonalPhoneNumber());\n            contactInfoBuilder.room(user.getPersonalRoom());\n            contactInfoBuilder.state(user.getPersonalState());\n            contactInfoBuilder.website(user.getPersonalWebsite());\n            contactInfoBuilder.zipCode(user.getPersonalZipCode());\n        } else {\n            contactInfoBuilder.address(user.getProfessionalAddress());\n            contactInfoBuilder.building(user.getProfessionalBuilding());\n            contactInfoBuilder.city(user.getProfessionalCity());\n            contactInfoBuilder.country(user.getProfessionalCountry());\n            contactInfoBuilder.email(user.getProfessionalEmail());\n            contactInfoBuilder.faxNumber(user.getProfessionalFaxNumber());\n            contactInfoBuilder.mobileNumber(user.getProfessionalMobileNumber());\n            contactInfoBuilder.phoneNumber(user.getProfessionalPhoneNumber());\n            contactInfoBuilder.room(user.getProfessionalRoom());\n            contactInfoBuilder.state(user.getProfessionalState());\n            contactInfoBuilder.website(user.getProfessionalWebsite());\n            contactInfoBuilder.zipCode(user.getProfessionalZipCode());\n        }\n        return contactInfoBuilder.build();\n    }\n\n    public static SRole constructSRole(final RoleCreator creator) {\n        final long now = System.currentTimeMillis();\n        final SRole.SRoleBuilder roleBuilder = SRole.builder();\n        roleBuilder.createdBy(SessionInfos.getUserIdFromSession());\n        roleBuilder.creationDate(now).lastUpdate(now);\n        final Map<RoleField, Serializable> fields = creator.getFields();\n        roleBuilder.name((String) fields.get(RoleField.NAME));\n        final String displayName = (String) fields.get(RoleField.DISPLAY_NAME);\n        if (displayName != null) {\n            roleBuilder.displayName(displayName);\n        }\n        final String description = (String) fields.get(RoleField.DESCRIPTION);\n        if (description != null) {\n            roleBuilder.description(description);\n        }\n        return roleBuilder.build();\n    }\n\n    public static SRole constructSRole(final ExportedRole exportedRole) {\n        final long now = System.currentTimeMillis();\n        final SRole.SRoleBuilder roleBuilder = SRole.builder();\n        roleBuilder.createdBy(SessionInfos.getUserIdFromSession());\n        roleBuilder.creationDate(now).lastUpdate(now);\n        roleBuilder.name(exportedRole.getName());\n        roleBuilder.displayName(exportedRole.getDisplayName());\n        roleBuilder.description(exportedRole.getDescription());\n        return roleBuilder.build();\n    }\n\n    public static SGroup constructSGroup(final GroupCreator creator) {\n        final long now = System.currentTimeMillis();\n        final SGroup.SGroupBuilder groupBuilder = SGroup.builder();\n        groupBuilder.createdBy(SessionInfos.getUserIdFromSession());\n        groupBuilder.creationDate(now).lastUpdate(now);\n        final Map<GroupField, Serializable> fields = creator.getFields();\n        groupBuilder.name((String) fields.get(GroupField.NAME));\n        final String parentPath = (String) fields.get(GroupField.PARENT_PATH);\n        if (parentPath != null && !parentPath.isEmpty()) {\n            groupBuilder.parentPath(parentPath);\n        }\n        final String displayName = (String) fields.get(GroupField.DISPLAY_NAME);\n        if (displayName != null) {\n            groupBuilder.displayName(displayName);\n        }\n        final String description = (String) fields.get(GroupField.DESCRIPTION);\n        if (description != null) {\n            groupBuilder.description(description);\n        }\n        return groupBuilder.build();\n    }\n\n    public static SGroup constructSGroup(final ExportedGroup exportedGroup) {\n        final long now = System.currentTimeMillis();\n        final SGroup.SGroupBuilder groupBuilder = SGroup.builder();\n        groupBuilder.createdBy(SessionInfos.getUserIdFromSession());\n        groupBuilder.creationDate(now).lastUpdate(now);\n        groupBuilder.name(exportedGroup.getName());\n        groupBuilder.parentPath(exportedGroup.getParentPath());\n        groupBuilder.displayName(exportedGroup.getDisplayName());\n        groupBuilder.description(exportedGroup.getDescription());\n        return groupBuilder.build();\n    }\n\n    public static List<ProcessSupervisor> toProcessSupervisors(final List<SProcessSupervisor> sSupervisors) {\n        final List<ProcessSupervisor> processSupervisors = new ArrayList<>();\n        if (sSupervisors != null) {\n            for (final SProcessSupervisor sSupervisor : sSupervisors) {\n                processSupervisors.add(toProcessSupervisor(sSupervisor));\n            }\n        }\n        return processSupervisors;\n    }\n\n    public static ProcessSupervisor toProcessSupervisor(final SProcessSupervisor sSupervisor) {\n        final ProcessSupervisorImpl supervisor = new ProcessSupervisorImpl();\n        supervisor.setId(sSupervisor.getId());\n        supervisor.setProcessDefinitionId(sSupervisor.getProcessDefId());\n        supervisor.setUserId(sSupervisor.getUserId());\n        supervisor.setGroupId(sSupervisor.getGroupId());\n        supervisor.setRoleId(sSupervisor.getRoleId());\n        return supervisor;\n    }\n\n    public static List<Document> toDocuments(final Collection<AbstractSMappedDocument> mappedDocuments,\n            final DocumentService documentService) {\n        final List<Document> documents = new ArrayList<>();\n        for (final AbstractSMappedDocument mappedDocument : mappedDocuments) {\n            final Document document = toDocument(mappedDocument, documentService);\n            documents.add(document);\n        }\n        return documents;\n    }\n\n    public static Document toDocument(final AbstractSMappedDocument mappedDocument,\n            final DocumentService documentService) {\n\n        final DocumentImpl documentImpl = new DocumentImpl();\n        if (mappedDocument instanceof SAMappedDocument) {\n            documentImpl.setId(((SAMappedDocument) mappedDocument).getSourceObjectId());\n        } else {\n            documentImpl.setId(mappedDocument.getId());\n        }\n        setDocumentFields(mappedDocument, documentService, documentImpl);\n        return documentImpl;\n    }\n\n    private static void setDocumentFields(final AbstractSMappedDocument mappedDocument,\n            final DocumentService documentService,\n            final DocumentImpl documentImpl) {\n        documentImpl.setProcessInstanceId(mappedDocument.getProcessInstanceId());\n        documentImpl.setName(mappedDocument.getName());\n        documentImpl.setDescription(mappedDocument.getDescription());\n        documentImpl.setVersion(mappedDocument.getVersion());\n        documentImpl.setAuthor(mappedDocument.getAuthor());\n        documentImpl.setCreationDate(new Date(mappedDocument.getCreationDate()));\n        documentImpl.setHasContent(mappedDocument.hasContent());\n        documentImpl.setContentMimeType(mappedDocument.getMimeType());\n        documentImpl.setFileName(mappedDocument.getFileName());\n        documentImpl.setContentStorageId(String.valueOf(mappedDocument.getDocumentId()));\n        documentImpl.setIndex(mappedDocument.getIndex());\n        if (mappedDocument.hasContent()) {\n            documentImpl.setUrl(documentService.generateDocumentURL(mappedDocument.getFileName(),\n                    String.valueOf(mappedDocument.getDocumentId())));\n        } else {\n            documentImpl.setUrl(mappedDocument.getUrl());\n        }\n    }\n\n    public static List<ArchivedDocument> toArchivedDocuments(final Collection<SAMappedDocument> mappedDocuments,\n            final DocumentService documentService) {\n        final List<ArchivedDocument> documents = new ArrayList<>();\n        for (final SAMappedDocument mappedDocument : mappedDocuments) {\n            final ArchivedDocument document = toArchivedDocument(mappedDocument, documentService);\n            documents.add(document);\n        }\n        return documents;\n    }\n\n    public static ArchivedDocument toArchivedDocument(final SAMappedDocument mappedDocument,\n            final DocumentService documentService) {\n        final ArchivedDocumentImpl documentImpl = new ArchivedDocumentImpl(mappedDocument.getName());\n        documentImpl.setId(mappedDocument.getId());\n        setDocumentFields(mappedDocument, documentService, documentImpl);\n        documentImpl.setArchiveDate(new Date(mappedDocument.getArchiveDate()));\n        documentImpl.setSourceObjectId(mappedDocument.getSourceObjectId());\n        return documentImpl;\n    }\n\n    public static int getServerActivityStateId(final String state) {\n        int stateId;\n        if (state.equalsIgnoreCase(ActivityStates.READY_STATE)) {\n            stateId = 4;\n        } else if (state.equalsIgnoreCase(ActivityStates.COMPLETING_STATE)) {\n            stateId = 9;\n        } else if (state.equalsIgnoreCase(ActivityStates.COMPLETED_STATE)) {\n            stateId = 2;\n        } else if (state.equalsIgnoreCase(ActivityStates.EXECUTING_STATE)) {\n            stateId = 1;\n        } else if (state.equalsIgnoreCase(ActivityStates.INITIALIZING_STATE)) {\n            stateId = 0;\n        } else if (state.equalsIgnoreCase(ActivityStates.SKIPPED_STATE)) {\n            stateId = 12;\n        } else if (state.equalsIgnoreCase(ActivityStates.CANCELLING_SUBTASKS_STATE)) {\n            stateId = 13;\n        } else if (state.equalsIgnoreCase(ActivityStates.CANCELLED_STATE)) {\n            stateId = 14;\n        } else if (state.equalsIgnoreCase(ActivityStates.FAILED_STATE)) {\n            stateId = 3;\n        } else {\n            throw new IllegalArgumentException(\"Unknown activity state \" + state);\n        }\n        return stateId;\n    }\n\n    public static ProcessInstanceState getProcessInstanceState(final String state) {\n        if (state != null) {\n            if (state.equalsIgnoreCase(ProcessInstanceState.ABORTED.toString())) {\n                return ProcessInstanceState.ABORTED;\n            } else if (state.equalsIgnoreCase(ProcessInstanceState.CANCELLED.toString())) {\n                return ProcessInstanceState.CANCELLED;\n            } else if (state.equalsIgnoreCase(ProcessInstanceState.COMPLETED.toString())) {\n                return ProcessInstanceState.COMPLETED;\n            } else if (state.equalsIgnoreCase(ProcessInstanceState.COMPLETING.toString())) {\n                return ProcessInstanceState.COMPLETING;\n            } else if (state.equalsIgnoreCase(ProcessInstanceState.ERROR.toString())) {\n                return ProcessInstanceState.ERROR;\n            } else if (state.equalsIgnoreCase(ProcessInstanceState.INITIALIZING.toString())) {\n                return ProcessInstanceState.INITIALIZING;\n            } else if (state.equalsIgnoreCase(ProcessInstanceState.STARTED.toString())) {\n                return ProcessInstanceState.STARTED;\n            } else if (state.equalsIgnoreCase(ProcessInstanceState.SUSPENDED.toString())) {\n                return ProcessInstanceState.SUSPENDED;\n            }\n        }\n        throw new IllegalArgumentException(\"Invalid process instance state: \" + state);\n    }\n\n    public static Comment toComment(final SComment sComment) {\n        final CommentImpl commentImpl = new CommentImpl();\n        commentImpl.setId(sComment.getId());\n        commentImpl.setUserId(sComment.getUserId());\n        commentImpl.setProcessInstanceId(sComment.getProcessInstanceId());\n        commentImpl.setPostDate(sComment.getPostDate());\n        commentImpl.setContent(sComment.getContent());\n        return commentImpl;\n    }\n\n    public static List<Comment> toComments(final List<SComment> sComments) {\n        final List<Comment> comments = new ArrayList<>();\n        for (final SComment sComment : sComments) {\n            comments.add(toComment(sComment));\n        }\n        return comments;\n    }\n\n    public static Map<String, SExpression> constructExpressions(final Map<String, Expression> inputs) {\n\n        final Map<String, SExpression> result = new HashMap<>(inputs.size());\n        for (final Entry<String, Expression> expression : inputs.entrySet()) {\n            result.put(expression.getKey(), constructSExpression(expression.getValue()));\n        }\n        return result;\n    }\n\n    public static SExpression constructSExpression(final Expression model) {\n        final ArrayList<SExpression> dependencies = new ArrayList<>();\n        for (final Expression dep : model.getDependencies()) {\n            dependencies.add(constructSExpression(dep));\n        }\n        final SExpressionBuilder expressionBuilder = BuilderFactory.get(SExpressionBuilderFactory.class)\n                .createNewInstance();\n        expressionBuilder.setName(model.getName());\n        expressionBuilder.setContent(model.getContent());\n        expressionBuilder.setExpressionType(model.getExpressionType());\n        expressionBuilder.setInterpreter(model.getInterpreter());\n        expressionBuilder.setReturnType(model.getReturnType());\n        expressionBuilder.setDependencies(dependencies);\n        try {\n            return expressionBuilder.done();\n        } catch (final SInvalidExpressionException e) {\n            throw new IllegalArgumentException(\"Error constructing SExpression\");\n        }\n    }\n\n    public static SOperation convertOperation(final Operation operation) {\n        if (operation == null) {\n            return null;\n        }\n        return BuilderFactory\n                .get(SOperationBuilderFactory.class)\n                .createNewInstance()\n                .setOperator(operation.getOperator())\n                .setType(SOperatorType.valueOf(operation.getType().name()))\n                .setRightOperand(ModelConvertor.constructSExpression(operation.getRightOperand()))\n                .setLeftOperand(\n                        BuilderFactory.get(SLeftOperandBuilderFactory.class).createNewInstance()\n                                .setName(operation.getLeftOperand().getName())\n                                .setType(operation.getLeftOperand().getType()).done())\n                .done();\n    }\n\n    public static List<SOperation> convertOperations(final List<Operation> operations) {\n        if (operations == null) {\n            return Collections.emptyList();\n        }\n        final List<SOperation> sOperations = new ArrayList<>(operations.size());\n        for (final Operation operation : operations) {\n            sOperations.add(convertOperation(operation));\n        }\n        return sOperations;\n    }\n\n    public static List<ConnectorImplementationDescriptor> toConnectorImplementationDescriptors(\n            final List<SConnectorImplementationDescriptor> sConnectorImplementationDescriptors) {\n        if (sConnectorImplementationDescriptors != null) {\n            final List<ConnectorImplementationDescriptor> connectorImplementationDescriptors = new ArrayList<>(\n                    sConnectorImplementationDescriptors.size());\n            for (final SConnectorImplementationDescriptor sConnectorImplementationDescriptor : sConnectorImplementationDescriptors) {\n                connectorImplementationDescriptors\n                        .add(toConnectorImplementationDescriptor(sConnectorImplementationDescriptor));\n            }\n            return connectorImplementationDescriptors;\n        }\n        return Collections.emptyList();\n    }\n\n    public static ConnectorImplementationDescriptor toConnectorImplementationDescriptor(\n            final SConnectorImplementationDescriptor sConnectorImplementationDescriptor) {\n        return new ConnectorImplementationDescriptor(sConnectorImplementationDescriptor.getImplementationClassName(),\n                sConnectorImplementationDescriptor.getId(), sConnectorImplementationDescriptor.getVersion(),\n                sConnectorImplementationDescriptor.getDefinitionId(),\n                sConnectorImplementationDescriptor.getDefinitionVersion(),\n                sConnectorImplementationDescriptor.getJarDependencies());\n    }\n\n    public static List<ArchivedComment> toArchivedComments(final List<SAComment> serverObjects) {\n        final List<ArchivedComment> comments = new ArrayList<>();\n        for (final SAComment saComment : serverObjects) {\n            final ArchivedComment comment = toArchivedComment(saComment);\n            comments.add(comment);\n        }\n        return comments;\n    }\n\n    public static ArchivedComment toArchivedComment(final SAComment saComment) {\n        final ArchivedCommentImpl commentImpl = new ArchivedCommentImpl(saComment.getContent());\n        commentImpl.setId(saComment.getId());\n        commentImpl.setProcessInstanceId(saComment.getProcessInstanceId());\n        commentImpl.setArchiveDate(new Date(saComment.getArchiveDate()));\n        commentImpl.setContent(saComment.getContent());\n        commentImpl.setSourceObjectId(saComment.getSourceObjectId());\n        commentImpl.setUserId(saComment.getUserId());\n        commentImpl.setPostDate(new Date(saComment.getPostDate()));\n        return commentImpl;\n    }\n\n    public static Operation toOperation(final SOperation operation) {\n        final OperationImpl operationImpl = new OperationImpl();\n        operationImpl.setRightOperand(toExpression(operation.getRightOperand()));\n        operationImpl.setOperator(operation.getOperator());\n        operationImpl.setType(toOperatorType(operation.getType()));\n        final LeftOperandImpl leftOperand = new LeftOperandImpl();\n        final SLeftOperand sLeftOperand = operation.getLeftOperand();\n        leftOperand.setName(sLeftOperand.getName());\n        leftOperand.setType(sLeftOperand.getType());\n        operationImpl.setLeftOperand(leftOperand);\n        return operationImpl;\n    }\n\n    private static OperatorType toOperatorType(final SOperatorType type) {\n        OperatorType operatorType = null;\n        if (SOperatorType.ASSIGNMENT.equals(type)) {\n            operatorType = OperatorType.ASSIGNMENT;\n        } else if (SOperatorType.JAVA_METHOD.equals(type)) {\n            operatorType = OperatorType.JAVA_METHOD;\n        } else if (SOperatorType.XPATH_UPDATE_QUERY.equals(type)) {\n            operatorType = OperatorType.XPATH_UPDATE_QUERY;\n        }\n        return operatorType;\n    }\n\n    public static List<ActorInstance> toActors(final List<SActor> sActors) {\n        final List<ActorInstance> actors = new ArrayList<>();\n        for (final SActor sActor : sActors) {\n            final ActorInstance actor = toActorInstance(sActor);\n            actors.add(actor);\n        }\n        return actors;\n    }\n\n    public static List<ArchivedFlowNodeInstance> toArchivedFlowNodeInstances(final List<SAFlowNodeInstance> saFlowNodes,\n            final FlowNodeStateManager flowNodeStateManager) {\n        final List<ArchivedFlowNodeInstance> flowNodeInstances = new ArrayList<>();\n        for (final SAFlowNodeInstance saFlowNode : saFlowNodes) {\n            final ArchivedFlowNodeInstance flowNodeInstance = toArchivedFlowNodeInstance(saFlowNode,\n                    flowNodeStateManager);\n            flowNodeInstances.add(flowNodeInstance);\n        }\n        return flowNodeInstances;\n    }\n\n    public static ArchivedFlowNodeInstance toArchivedFlowNodeInstance(final SAFlowNodeInstance saFlowNode,\n            final FlowNodeStateManager flowNodeStateManager) {\n        ArchivedFlowNodeInstance archiveFlowNodeInstance = null;\n        switch (saFlowNode.getType()) {\n            case AUTOMATIC_TASK:\n                archiveFlowNodeInstance = toArchivedAutomaticTaskInstance((SAAutomaticTaskInstance) saFlowNode,\n                        flowNodeStateManager);\n                break;\n            case MANUAL_TASK:\n                archiveFlowNodeInstance = toArchivedManualTaskInstance((SAManualTaskInstance) saFlowNode,\n                        flowNodeStateManager);\n                break;\n            case USER_TASK:\n                archiveFlowNodeInstance = toArchivedUserTaskInstance((SAUserTaskInstance) saFlowNode,\n                        flowNodeStateManager);\n                break;\n            case RECEIVE_TASK:\n                archiveFlowNodeInstance = toArchivedReceiveTaskInstance((SAReceiveTaskInstance) saFlowNode,\n                        flowNodeStateManager);\n                break;\n            case SEND_TASK:\n                archiveFlowNodeInstance = toArchivedSendTaskInstance((SASendTaskInstance) saFlowNode,\n                        flowNodeStateManager);\n                break;\n            case CALL_ACTIVITY:\n                archiveFlowNodeInstance = toArchivedCallActivityInstance((SACallActivityInstance) saFlowNode,\n                        flowNodeStateManager);\n                break;\n            case LOOP_ACTIVITY:\n                archiveFlowNodeInstance = toArchivedLoopActivityInstance((SALoopActivityInstance) saFlowNode,\n                        flowNodeStateManager);\n                break;\n            case SUB_PROCESS:\n                archiveFlowNodeInstance = toArchivedSubProcessActivityInstance(\n                        (SASubProcessActivityInstance) saFlowNode, flowNodeStateManager);\n                break;\n            case GATEWAY:\n                archiveFlowNodeInstance = toArchivedGatewayInstance((SAGatewayInstance) saFlowNode,\n                        flowNodeStateManager);\n                break;\n            case MULTI_INSTANCE_ACTIVITY:\n                archiveFlowNodeInstance = toArchivedMultiInstanceActivityInstance(\n                        (SAMultiInstanceActivityInstance) saFlowNode, flowNodeStateManager);\n                break;\n            case BOUNDARY_EVENT:\n            case START_EVENT:\n            case INTERMEDIATE_CATCH_EVENT:\n            case INTERMEDIATE_THROW_EVENT:\n            case END_EVENT:\n                // archiveFlowNodeInstance = toArchivedEventInstance((SAEventInstance) saFlowNode, flowNodeStateManager);\n                break;\n            default:\n                throw new UnknownElementType(saFlowNode.getType().name());\n        }\n        return archiveFlowNodeInstance;\n    }\n\n    public static List<ConnectorInstance> toConnectorInstances(final List<SConnectorInstance> sConnectorInstances) {\n        final ArrayList<ConnectorInstance> connectorInstances = new ArrayList<>(sConnectorInstances.size());\n        for (final SConnectorInstance sConnectorInstance : sConnectorInstances) {\n            connectorInstances.add(toConnectorInstance(sConnectorInstance));\n        }\n        return connectorInstances;\n    }\n\n    private static ConnectorInstance toConnectorInstance(final SConnectorInstance sConnectorInstance) {\n        final ConnectorInstanceImpl connectorInstanceImpl = new ConnectorInstanceImpl(sConnectorInstance.getName(),\n                sConnectorInstance.getContainerId(),\n                sConnectorInstance.getContainerType(), sConnectorInstance.getConnectorId(),\n                sConnectorInstance.getVersion(),\n                ConnectorState.valueOf(sConnectorInstance.getState()), sConnectorInstance.getActivationEvent());\n        connectorInstanceImpl.setId(sConnectorInstance.getId());\n        return connectorInstanceImpl;\n    }\n\n    public static ConnectorInstanceWithFailureInfo toConnectorInstanceWithFailureInfo(\n            final SConnectorInstanceWithFailureInfo sConnectorInstanceWithFailureInfo) {\n        final ConnectorInstanceWithFailureInfoImpl connectorInstanceImpl = new ConnectorInstanceWithFailureInfoImpl(\n                sConnectorInstanceWithFailureInfo.getName(), sConnectorInstanceWithFailureInfo.getContainerId(),\n                sConnectorInstanceWithFailureInfo.getContainerType(),\n                sConnectorInstanceWithFailureInfo.getConnectorId(),\n                sConnectorInstanceWithFailureInfo.getVersion(),\n                ConnectorState.valueOf(sConnectorInstanceWithFailureInfo.getState()),\n                sConnectorInstanceWithFailureInfo.getActivationEvent(),\n                sConnectorInstanceWithFailureInfo.getExceptionMessage(),\n                sConnectorInstanceWithFailureInfo.getStackTrace());\n        connectorInstanceImpl.setId(sConnectorInstanceWithFailureInfo.getId());\n        return connectorInstanceImpl;\n    }\n\n    public static ArchivedConnectorInstance toArchivedConnectorInstance(final SAConnectorInstance sAConnectorInstance) {\n        final ArchivedConnectorInstanceImpl connectorInstanceImpl = new ArchivedConnectorInstanceImpl(\n                sAConnectorInstance.getName(), new Date(\n                        sAConnectorInstance.getArchiveDate()),\n                sAConnectorInstance.getContainerId(), sAConnectorInstance.getContainerType(),\n                sAConnectorInstance.getConnectorId(), sAConnectorInstance.getVersion(),\n                sAConnectorInstance.getActivationEvent(),\n                ConnectorState.valueOf(sAConnectorInstance.getState()), sAConnectorInstance.getSourceObjectId());\n        connectorInstanceImpl.setId(sAConnectorInstance.getId());\n        return connectorInstanceImpl;\n    }\n\n    public static List<ArchivedConnectorInstance> toArchivedConnectorInstances(\n            final List<SAConnectorInstance> serverObjects) {\n        final List<ArchivedConnectorInstance> commments = new ArrayList<>();\n        for (final SAConnectorInstance saConnectorInstance : serverObjects) {\n            final ArchivedConnectorInstance archivedConnectorInstance = toArchivedConnectorInstance(\n                    saConnectorInstance);\n            commments.add(archivedConnectorInstance);\n        }\n        return commments;\n    }\n\n    public static List<Profile> toProfiles(final List<SProfile> sProfiles) {\n        final List<Profile> profiles = new ArrayList<>(sProfiles.size());\n        for (final SProfile sProfile : sProfiles) {\n            final Profile profile = toProfile(sProfile);\n            profiles.add(profile);\n        }\n        return profiles;\n    }\n\n    public static Profile toProfile(final SProfile sProfile) {\n        final ProfileImpl profileImpl = new ProfileImpl(sProfile.getName());\n        profileImpl.setId(sProfile.getId());\n        profileImpl.setDefault(sProfile.isDefault());\n        profileImpl.setDescription(sProfile.getDescription());\n        profileImpl.setCreationDate(new Date(sProfile.getCreationDate()));\n        profileImpl.setCreatedBy(sProfile.getCreatedBy());\n        profileImpl.setLastUpdateDate(new Date(sProfile.getLastUpdateDate()));\n        profileImpl.setLastUpdatedBy(sProfile.getLastUpdatedBy());\n        return profileImpl;\n    }\n\n    public static List<ProfileMember> toProfileMembers(final List<SProfileMember> sProfileMembers) {\n        final List<ProfileMember> profiles = new ArrayList<>(sProfileMembers.size());\n        for (final SProfileMember sProfileMember : sProfileMembers) {\n            final ProfileMember profile = toProfileMember(sProfileMember);\n            profiles.add(profile);\n        }\n        return profiles;\n    }\n\n    public static ProfileMember toProfileMember(final SProfileMember sProfileMember) {\n        final ProfileMemberImpl profileMemberImpl = new ProfileMemberImpl();\n        profileMemberImpl.setId(sProfileMember.getId());\n        profileMemberImpl.setDisplayNamePart1(sProfileMember.getDisplayNamePart1());\n        profileMemberImpl.setDisplayNamePart2(sProfileMember.getDisplayNamePart2());\n        profileMemberImpl.setDisplayNamePart3(sProfileMember.getDisplayNamePart3());\n        profileMemberImpl.setGroupId(sProfileMember.getGroupId());\n        profileMemberImpl.setProfileId(sProfileMember.getProfileId());\n        profileMemberImpl.setRoleId(sProfileMember.getRoleId());\n        profileMemberImpl.setUserId(sProfileMember.getUserId());\n        return profileMemberImpl;\n    }\n\n    public static SProfileMember constructSProfileMember(final ProfileMemberCreator creator) {\n        final Map<ProfileMemberField, Serializable> fields = creator.getFields();\n        final SProfileMember.SProfileMemberBuilder newSProfileMemberBuilder = SProfileMember.builder()\n                .profileId((Long) fields.get(ProfileMemberField.PROFILE_ID));\n        final Long groupeId = (Long) fields.get(ProfileMemberField.GROUP_ID);\n        if (groupeId != null) {\n            newSProfileMemberBuilder.groupId(groupeId);\n        }\n        final Long roleId = (Long) fields.get(ProfileMemberField.ROLE_ID);\n        if (roleId != null) {\n            newSProfileMemberBuilder.roleId(roleId);\n        }\n        final Long userId = (Long) fields.get(ProfileMemberField.USER_ID);\n        if (userId != null) {\n            newSProfileMemberBuilder.userId(userId);\n        }\n        return newSProfileMemberBuilder.build();\n    }\n\n    public static List<FailedJob> toFailedJobs(final List<SFailedJob> sFailedJobs) {\n        final List<FailedJob> failedJobs = new ArrayList<>(sFailedJobs.size());\n        for (final SFailedJob sFailedJob : sFailedJobs) {\n            failedJobs.add(toFailedJob(sFailedJob));\n        }\n        return failedJobs;\n    }\n\n    public static FailedJob toFailedJob(final SFailedJob sFailedJob) {\n        final FailedJobImpl failedJob = new FailedJobImpl(sFailedJob.getJobDescriptorId(), sFailedJob.getJobName());\n        failedJob.setDescription(sFailedJob.getDescription());\n        failedJob.setLastMessage(sFailedJob.getLastMessage());\n        failedJob.setNumberOfFailures(sFailedJob.getNumberOfFailures());\n        failedJob.setLastUpdateDate(new Date(sFailedJob.getLastUpdateDate()));\n        return failedJob;\n    }\n\n    public static CustomUserInfoDefinitionImpl convert(final SCustomUserInfoDefinition sDefinition) {\n        final CustomUserInfoDefinitionImpl definition = new CustomUserInfoDefinitionImpl();\n        definition.setId(sDefinition.getId());\n        definition.setName(sDefinition.getName());\n        definition.setDescription(sDefinition.getDescription());\n        return definition;\n    }\n\n    public static CustomUserInfoValueImpl convert(final SCustomUserInfoValue sValue) {\n        if (sValue == null) {\n            return null;\n        }\n        final CustomUserInfoValueImpl value = new CustomUserInfoValueImpl();\n        value.setDefinitionId(sValue.getDefinitionId());\n        value.setUserId(sValue.getUserId());\n        value.setValue(sValue.getValue());\n        return value;\n    }\n\n    public static FormMapping toFormMapping(final SFormMapping sFormMapping,\n            final FormRequiredAnalyzer formRequiredAnalyzer) {\n        if (sFormMapping == null) {\n            return null;\n        }\n        final FormMapping formMapping = new FormMapping();\n        formMapping.setId(sFormMapping.getId());\n        formMapping.setTask(sFormMapping.getTask());\n        final SPageMapping pageMapping = sFormMapping.getPageMapping();\n        if (pageMapping != null) {\n            formMapping.setPageMappingKey(pageMapping.getKey());\n            formMapping.setPageId(pageMapping.getPageId());\n            formMapping.setPageURL(pageMapping.getUrl());\n        }\n        formMapping.setType(FormMappingType.getTypeFromId(sFormMapping.getType()));\n        formMapping.setTarget(\n                sFormMapping.getTarget() == null ? null : FormMappingTarget.valueOf(sFormMapping.getTarget()));\n        formMapping.setProcessDefinitionId(sFormMapping.getProcessDefinitionId());\n        final long lastUpdateDate = sFormMapping.getLastUpdateDate();\n        formMapping.setLastUpdateDate(lastUpdateDate > 0 ? new Date(lastUpdateDate) : null);\n        formMapping.setLastUpdatedBy(sFormMapping.getLastUpdatedBy());\n\n        formMapping.setFormRequired(formRequiredAnalyzer.isFormRequired(sFormMapping));\n\n        return formMapping;\n    }\n\n    public static List<FormMapping> toFormMappings(final List<SFormMapping> serverObjects,\n            final FormRequiredAnalyzer formRequiredAnalyzer) {\n        final List<FormMapping> clientObjects = new ArrayList<>(serverObjects.size());\n        for (final SFormMapping serverObject : serverObjects) {\n            clientObjects.add(toFormMapping(serverObject, formRequiredAnalyzer));\n        }\n        return clientObjects;\n    }\n\n    public static BusinessDataReference toBusinessDataReference(\n            final SRefBusinessDataInstance sRefBusinessDataInstance) {\n        if (sRefBusinessDataInstance == null) {\n            return null;\n        }\n        if (sRefBusinessDataInstance instanceof SProcessMultiRefBusinessDataInstance multi) {\n            return new MultipleBusinessDataReferenceImpl(multi.getName(), multi.getDataClassName(), multi.getDataIds());\n        }\n        final SSimpleRefBusinessDataInstance simple = (SSimpleRefBusinessDataInstance) sRefBusinessDataInstance;\n        return new SimpleBusinessDataReferenceImpl(simple.getName(), simple.getDataClassName(), simple.getDataId());\n\n    }\n\n    public static ContractDefinition toContract(final SContractDefinition sContract) {\n        if (sContract == null) {\n            return null;\n        }\n        final ContractDefinitionImpl contract = new ContractDefinitionImpl();\n        for (final SInputDefinition input : sContract.getInputDefinitions()) {\n            contract.addInput(toInput(input));\n        }\n        for (final SConstraintDefinition sConstraintDefinition : sContract.getConstraints()) {\n            final ConstraintDefinitionImpl constraint = new ConstraintDefinitionImpl(sConstraintDefinition.getName(),\n                    sConstraintDefinition.getExpression(),\n                    sConstraintDefinition.getExplanation());\n            for (final String inputName : sConstraintDefinition.getInputNames()) {\n                constraint.addInputName(inputName);\n            }\n            contract.addConstraint(constraint);\n        }\n        return contract;\n    }\n\n    private static InputDefinition toInput(final SInputDefinition input) {\n        final List<InputDefinition> inputDefinitions = new ArrayList<>();\n        for (final SInputDefinition sInputDefinition : input.getInputDefinitions()) {\n            inputDefinitions.add(toInput(sInputDefinition));\n        }\n        final SType type = input.getType();\n        final InputDefinitionImpl inputDefinition = new InputDefinitionImpl(input.getName(),\n                type == null ? null : Type.valueOf(type.toString()),\n                input.getDescription(), input.isMultiple());\n        inputDefinition.getInputs().addAll(inputDefinitions);\n        return inputDefinition;\n\n    }\n\n    public static PageURL toPageURL(final SPageURL sPageURL) {\n        return new PageURL(sPageURL.getUrl(), sPageURL.getPageId());\n    }\n\n    public static List<ExportedCustomUserInfoDefinition> toExportedCustomUserInfoDefinition(\n            List<SCustomUserInfoDefinition> customUserInfoDefinitions) {\n        ArrayList<ExportedCustomUserInfoDefinition> customUserInfoDefinitionCreators = new ArrayList<>(\n                customUserInfoDefinitions.size());\n        for (SCustomUserInfoDefinition customUserInfoDefinition : customUserInfoDefinitions) {\n            customUserInfoDefinitionCreators\n                    .add(new ExportedCustomUserInfoDefinition(customUserInfoDefinition.getName(),\n                            customUserInfoDefinition.getDescription()));\n        }\n        return customUserInfoDefinitionCreators;\n    }\n\n    public static Icon toIcon(SIcon icon) {\n        return new IconImpl(icon.getId(), icon.getMimeType(), icon.getContent());\n    }\n\n    public static TenantResource toTenantResource(STenantResourceLight r) {\n        if (r == null) {\n            return TenantResource.NONE;\n        }\n        return new TenantResource(r.getId(), r.getName(),\n                org.bonitasoft.engine.tenant.TenantResourceType.valueOf(r.getType().name()), r.getLastUpdateDate(),\n                r.getLastUpdatedBy(), TenantResourceState.valueOf(r.getState().name()));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/ProcessEngineServicesResolver.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service;\n\nimport org.bonitasoft.engine.service.impl.ServiceAccessorFactory;\n\n/**\n * @author Baptiste Mesta\n */\npublic class ProcessEngineServicesResolver implements ServicesLookup {\n\n    @Override\n    public <T> T lookupService(String serviceName) {\n        try {\n            return ServiceAccessorFactory.getInstance().createServiceAccessor().lookup(serviceName);\n        } catch (Exception e) {\n            throw new IllegalStateException(\"Unable to find the service \" + serviceName, e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/ServiceAccessor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service;\n\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.api.impl.resolver.BusinessArchiveArtifactsManager;\nimport org.bonitasoft.engine.archive.ArchiveService;\nimport org.bonitasoft.engine.authentication.GenericAuthenticationService;\nimport org.bonitasoft.engine.authorization.PermissionService;\nimport org.bonitasoft.engine.bar.BusinessArchiveService;\nimport org.bonitasoft.engine.bpm.model.impl.BPMInstancesCreator;\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.importer.ApplicationImporter;\nimport org.bonitasoft.engine.business.data.BusinessDataModelRepository;\nimport org.bonitasoft.engine.business.data.BusinessDataRepository;\nimport org.bonitasoft.engine.business.data.BusinessDataService;\nimport org.bonitasoft.engine.cache.CacheService;\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.command.CommandService;\nimport org.bonitasoft.engine.connector.ConnectorExecutor;\nimport org.bonitasoft.engine.core.category.CategoryService;\nimport org.bonitasoft.engine.core.connector.ConnectorInstanceService;\nimport org.bonitasoft.engine.core.connector.ConnectorService;\nimport org.bonitasoft.engine.core.contract.data.ContractDataService;\nimport org.bonitasoft.engine.core.data.instance.TransientDataService;\nimport org.bonitasoft.engine.core.document.api.DocumentService;\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.filter.UserFilterService;\nimport org.bonitasoft.engine.core.form.FormMappingService;\nimport org.bonitasoft.engine.core.login.LoginService;\nimport org.bonitasoft.engine.core.login.TechnicalUser;\nimport org.bonitasoft.engine.core.operation.OperationService;\nimport org.bonitasoft.engine.core.platform.login.PlatformLoginService;\nimport org.bonitasoft.engine.core.process.comment.api.SCommentService;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.BPMFailureService;\nimport org.bonitasoft.engine.core.process.instance.api.GatewayInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.RefBusinessDataService;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceRepository;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceService;\nimport org.bonitasoft.engine.data.instance.api.ParentContainerResolver;\nimport org.bonitasoft.engine.dependency.DependencyService;\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.exception.NotFoundException;\nimport org.bonitasoft.engine.execution.ContainerRegistry;\nimport org.bonitasoft.engine.execution.FlowNodeExecutor;\nimport org.bonitasoft.engine.execution.ProcessExecutor;\nimport org.bonitasoft.engine.execution.ProcessInstanceInterruptor;\nimport org.bonitasoft.engine.execution.ProcessStarterVerifier;\nimport org.bonitasoft.engine.execution.archive.BPMArchiverService;\nimport org.bonitasoft.engine.execution.event.EventsHandler;\nimport org.bonitasoft.engine.execution.state.FlowNodeStateManager;\nimport org.bonitasoft.engine.execution.work.BPMWorkFactory;\nimport org.bonitasoft.engine.expression.ExpressionService;\nimport org.bonitasoft.engine.identity.IconService;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.incident.IncidentService;\nimport org.bonitasoft.engine.lock.LockService;\nimport org.bonitasoft.engine.message.MessagesHandlingService;\nimport org.bonitasoft.engine.page.PageMappingService;\nimport org.bonitasoft.engine.page.PageService;\nimport org.bonitasoft.engine.parameter.ParameterService;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.platform.PlatformManager;\nimport org.bonitasoft.engine.platform.PlatformRetriever;\nimport org.bonitasoft.engine.platform.PlatformService;\nimport org.bonitasoft.engine.platform.authentication.PlatformAuthenticationService;\nimport org.bonitasoft.engine.platform.command.PlatformCommandService;\nimport org.bonitasoft.engine.platform.configuration.NodeConfiguration;\nimport org.bonitasoft.engine.platform.session.PlatformSessionService;\nimport org.bonitasoft.engine.profile.ProfileService;\nimport org.bonitasoft.engine.profile.ProfilesExporter;\nimport org.bonitasoft.engine.profile.ProfilesImporter;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.resources.ProcessResourcesService;\nimport org.bonitasoft.engine.resources.TenantResourcesService;\nimport org.bonitasoft.engine.scheduler.JobService;\nimport org.bonitasoft.engine.scheduler.SchedulerService;\nimport org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.bonitasoft.engine.supervisor.mapping.SupervisorMappingService;\nimport org.bonitasoft.engine.synchro.SynchroService;\nimport org.bonitasoft.engine.temporary.content.TemporaryContentService;\nimport org.bonitasoft.engine.tenant.TenantServicesManager;\nimport org.bonitasoft.engine.tenant.TenantStateManager;\nimport org.bonitasoft.engine.tracking.TimeTracker;\nimport org.bonitasoft.engine.transaction.TransactionService;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.bonitasoft.engine.work.WorkExecutorService;\nimport org.bonitasoft.engine.work.WorkService;\nimport org.springframework.context.ApplicationContext;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface ServiceAccessor {\n\n    ParentContainerResolver getParentContainerResolver();\n\n    SessionService getSessionService();\n\n    IdentityService getIdentityService();\n\n    IconService getIconService();\n\n    LoginService getLoginService();\n\n    QueriableLoggerService getQueriableLoggerService();\n\n    UserTransactionService getUserTransactionService();\n\n    ProcessDefinitionService getProcessDefinitionService();\n\n    ProcessInstanceService getProcessInstanceService();\n\n    ActivityInstanceService getActivityInstanceService();\n\n    BPMFailureService getBpmFailureService();\n\n    BPMInstancesCreator getBPMInstancesCreator();\n\n    FlowNodeExecutor getFlowNodeExecutor();\n\n    ProcessExecutor getProcessExecutor();\n\n    FlowNodeStateManager getFlowNodeStateManager();\n\n    ActorMappingService getActorMappingService();\n\n    ArchiveService getArchiveService();\n\n    CategoryService getCategoryService();\n\n    ExpressionService getExpressionService();\n\n    CommandService getCommandService();\n\n    ClassLoaderService getClassLoaderService();\n\n    DependencyService getDependencyService();\n\n    DependencyService getPlatformDependencyService();\n\n    EventInstanceService getEventInstanceService();\n\n    EventInstanceRepository getEventInstanceRepository();\n\n    ConnectorService getConnectorService();\n\n    ConnectorInstanceService getConnectorInstanceService();\n\n    DocumentService getDocumentService();\n\n    ProfileService getProfileService();\n\n    ProfilesImporter getProfilesImporter();\n\n    ProfilesExporter getProfilesExporter();\n\n    DataInstanceService getDataInstanceService();\n\n    TransientDataService getTransientDataService();\n\n    ExpressionResolverService getExpressionResolverService();\n\n    OperationService getOperationService();\n\n    SupervisorMappingService getSupervisorService();\n\n    UserFilterService getUserFilterService();\n\n    SearchEntitiesDescriptor getSearchEntitiesDescriptor();\n\n    SCommentService getCommentService();\n\n    ContainerRegistry getContainerRegistry();\n\n    LockService getLockService();\n\n    EventsHandler getEventsHandler();\n\n    EventService getEventService();\n\n    ConnectorExecutor getConnectorExecutor();\n\n    CacheService getCacheService();\n\n    BusinessArchiveArtifactsManager getBusinessArchiveArtifactsManager();\n\n    WorkService getWorkService();\n\n    WorkExecutorService getWorkExecutorService();\n\n    SessionAccessor getSessionAccessor();\n\n    SynchroService getSynchroService();\n\n    IncidentService getIncidentService();\n\n    SchedulerService getSchedulerService();\n\n    JobService getJobService();\n\n    <T> T lookup(String serviceName) throws NotFoundException;\n\n    <T> T lookup(Class<T> beanClass) throws NotFoundException;\n\n    GatewayInstanceService getGatewayInstanceService();\n\n    void destroy();\n\n    TimeTracker getTimeTracker();\n\n    PermissionService getPermissionService();\n\n    ContractDataService getContractDataService();\n\n    ParameterService getParameterService();\n\n    PageService getPageService();\n\n    ApplicationService getApplicationService();\n\n    FormMappingService getFormMappingService();\n\n    BusinessDataRepository getBusinessDataRepository();\n\n    BusinessDataService getBusinessDataService();\n\n    BusinessDataModelRepository getBusinessDataModelRepository();\n\n    RefBusinessDataService getRefBusinessDataService();\n\n    PageMappingService getPageMappingService();\n\n    GenericAuthenticationService getAuthenticationService();\n\n    ReadPersistenceService getReadPersistenceService();\n\n    Recorder getRecorder();\n\n    BusinessArchiveService getBusinessArchiveService();\n\n    ProcessResourcesService getProcessResourcesService();\n\n    TenantResourcesService getTenantResourcesService();\n\n    MessagesHandlingService getMessagesHandlingService();\n\n    ProcessInstanceInterruptor getProcessInstanceInterruptor();\n\n    BPMWorkFactory getBPMWorkFactory();\n\n    TechnicalUser getTechnicalUser();\n\n    TenantStateManager getTenantStateManager();\n\n    TenantServicesManager getTenantServicesManager();\n\n    BPMArchiverService getBPMArchiverService();\n\n    ApplicationImporter getApplicationImporter();\n\n    PlatformService getPlatformService();\n\n    PlatformLoginService getPlatformLoginService();\n\n    TransactionService getTransactionService();\n\n    PlatformSessionService getPlatformSessionService();\n\n    PlatformCommandService getPlatformCommandService();\n\n    NodeConfiguration getPlatformConfiguration();\n\n    PlatformManager getPlatformManager();\n\n    CacheService getPlatformCacheService();\n\n    TemporaryContentService getTemporaryContentService();\n\n    BroadcastService getBroadcastService();\n\n    PlatformAuthenticationService getPlatformAuthenticationService();\n\n    ServicesResolver getServicesResolver();\n\n    void publishEvent(Object event);\n\n    ApplicationContext getContext();\n\n    PlatformRetriever getPlatformRetriever();\n\n    InstallationService getInstallationService();\n\n    ProcessStarterVerifier getProcessStarterVerifier();\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/ServiceAccessorSingleton.java",
    "content": "/**\n * Copyright (C) 2019-2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service;\n\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\nimport org.bonitasoft.engine.service.impl.ServiceAccessorFactory;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic final class ServiceAccessorSingleton {\n\n    private static ServiceAccessor instance = null;\n\n    private ServiceAccessorSingleton() {\n        super();\n    }\n\n    private static synchronized ServiceAccessor createNewInstance() {\n        try {\n            return ServiceAccessorFactory.getInstance().createServiceAccessor();\n        } catch (final Exception e) {\n            throw new BonitaRuntimeException(e);\n        }\n    }\n\n    public static ServiceAccessor getInstance() {\n        if (instance == null) {\n            instance = createNewInstance();\n        }\n        return instance;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/impl/APIAccessResolverImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service.impl;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.*;\nimport org.bonitasoft.engine.api.impl.*;\nimport org.bonitasoft.engine.api.impl.platform.PlatformInformationAPIImpl;\nimport org.bonitasoft.engine.api.platform.PlatformInformationAPI;\nimport org.bonitasoft.engine.exception.APIImplementationNotFoundException;\nimport org.bonitasoft.engine.service.APIAccessResolver;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class APIAccessResolverImpl implements APIAccessResolver {\n\n    private static final Map<String, Object> apis = new HashMap<>();\n\n    static {\n        apis.put(PlatformAPI.class.getName(), new PlatformAPIImpl());\n        apis.put(PlatformLoginAPI.class.getName(), new PlatformLoginAPIImpl());\n        apis.put(PlatformCommandAPI.class.getName(), new PlatformCommandAPIImpl());\n        apis.put(LoginAPI.class.getName(), new LoginAPIImpl());\n        apis.put(IdentityAPI.class.getName(), new IdentityAPIImpl());\n        apis.put(ProcessAPI.class.getName(), new ProcessAPIImpl());\n        apis.put(CommandAPI.class.getName(), new CommandAPIImpl());\n        apis.put(ProfileAPI.class.getName(), new ProfileAPIImpl());\n        apis.put(PermissionAPI.class.getName(), new PermissionAPIImpl());\n        apis.put(PageAPI.class.getName(), new PageAPIImpl());\n        apis.put(ApplicationAPI.class.getName(), new ApplicationAPIImpl());\n        apis.put(TenantAdministrationAPI.class.getName(), new TenantAdministrationAPIImpl());\n        apis.put(BusinessDataAPI.class.getName(), new BusinessDataAPIImpl());\n        apis.put(TemporaryContentAPI.class.getName(), new TemporaryContentAPIImpl());\n        apis.put(MaintenanceAPI.class.getName(), new MaintenanceAPIImpl());\n        apis.put(PlatformInformationAPI.class.getName(), new PlatformInformationAPIImpl());\n    }\n\n    @Override\n    public <T> T getAPIImplementation(Class<T> apiInterface) throws APIImplementationNotFoundException {\n        final Object api = getApiImplementation(apiInterface);\n        if (api == null) {\n            throw new APIImplementationNotFoundException(\n                    \"No API implementation was found for: \" + apiInterface.getName());\n        }\n        return apiInterface.cast(api);\n    }\n\n    protected Object getApiImplementation(Class<?> apiInterface) {\n        return apis.get(apiInterface.getName());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/impl/BonitaSpringContext.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service.impl;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.platform.configuration.model.BonitaConfiguration;\nimport org.springframework.beans.BeansException;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.support.AbstractXmlApplicationContext;\nimport org.springframework.core.io.ByteArrayResource;\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.core.io.Resource;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class BonitaSpringContext extends AbstractXmlApplicationContext {\n\n    private String name;\n    private List<Resource> resources = new ArrayList<>();\n\n    /**\n     * Create a new XmlApplicationContext with the given parent,\n     * loading the definitions from the given XML files and automatically\n     * refreshing the context.\n     *\n     * @param parent the parent context\n     * @throws BeansException if context creation failed\n     */\n    public BonitaSpringContext(ApplicationContext parent, String name) throws BeansException {\n        super(parent);\n        this.name = name;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    @Override\n    protected Resource[] getConfigResources() {\n        return resources.toArray(new Resource[resources.size()]);\n    }\n\n    public void addClassPathResource(String location) {\n        ClassPathResource classPathResource = new ClassPathResource(location);\n        if (classPathResource.exists()) {\n            resources.add(classPathResource);\n        }\n    }\n\n    public void addByteArrayResource(BonitaConfiguration configuration) {\n        resources.add(new ByteArrayResource(configuration.getResourceContent(), configuration.getResourceName()));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/impl/CustomPropertySource.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service.impl;\n\nimport java.util.Properties;\n\nimport org.springframework.core.env.PropertySource;\n\n/**\n * @author Charles Souillard\n */\npublic class CustomPropertySource extends PropertySource<String> {\n\n    private final Properties properties;\n\n    public CustomPropertySource(final String name, final Properties properties) {\n        super(name);\n        this.properties = properties;\n        //System.err.println(\"----- CustomPropertySource(\" + name + \") Thread: \" + Thread.currentThread().getId() + \"-----\");\n        //Thread.dumpStack();\n        //System.err.println(\"Loading properties: \" + properties);\n        //System.err.println(\"----- END CustomPropertySource(\" + name + \") Thread: \" + Thread.currentThread().getId() + \"-----\");\n    }\n\n    @Override\n    public Object getProperty(String key) {\n        final Object value = properties.get(key);\n        System.err.println(\"--- (\" + name + \" --- Retrieving \" + key + \"=\" + value);\n        return value;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/impl/MapToPropertiesFactoryBean.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service.impl;\n\nimport java.util.Map;\nimport java.util.Properties;\n\nimport org.springframework.beans.factory.config.AbstractFactoryBean;\n\n/**\n * @author Baptiste Mesta\n */\npublic class MapToPropertiesFactoryBean extends AbstractFactoryBean<Properties> {\n\n    private Map<String, Object> map;\n\n    @Override\n    public Class<?> getObjectType() {\n        return Properties.class;\n    }\n\n    @Override\n    protected Properties createInstance() throws Exception {\n        Properties properties = new Properties();\n        properties.putAll(map);\n        return properties;\n    }\n\n    public void setMap(final Map<String, Object> map) {\n        this.map = map;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/impl/PlatformAuthenticationChecker.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service.impl;\n\nimport java.io.IOException;\nimport java.util.Properties;\n\nimport org.bonitasoft.engine.home.BonitaHomeServer;\nimport org.bonitasoft.engine.platform.authentication.PlatformAuthenticationService;\nimport org.bonitasoft.engine.platform.authentication.SInvalidPasswordException;\nimport org.bonitasoft.engine.platform.authentication.SInvalidUserException;\n\n/**\n * @author Lu Kai\n * @author Matthieu Chaffotte\n * @author Frederic Bouquet\n */\npublic class PlatformAuthenticationChecker implements PlatformAuthenticationService {\n\n    @Override\n    public void checkUserCredentials(final String userName, final String password)\n            throws SInvalidUserException, SInvalidPasswordException {\n        try {\n            final Properties properties = BonitaHomeServer.getInstance().getPlatformProperties();\n            final String userProperty = properties.getProperty(\"platformAdminUsername\");\n            if (userProperty == null || !userProperty.equals(userName)) {\n                throw new SInvalidUserException(\"Invalid user: \" + userName);\n            }\n            final String passProperty = properties.getProperty(\"platformAdminPassword\");\n            if (passProperty == null || !passProperty.equals(password)) {\n                throw new SInvalidPasswordException(\"Invalid password\");\n            }\n        } catch (final IOException ioe) {\n            throw new SInvalidUserException(ioe);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/impl/ServerLoggerWrapper.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service.impl;\n\nimport org.bonitasoft.engine.api.Logger;\n\n/**\n * Wrap the technical logger service to be available to client extensions\n *\n * @author Baptiste Mesta\n */\npublic class ServerLoggerWrapper implements Logger {\n\n    private Class<?> clazz;\n    private org.slf4j.Logger logger;\n\n    public ServerLoggerWrapper(Class<?> clazz, org.slf4j.Logger logger) {\n        this.clazz = clazz;\n        this.logger = logger;\n    }\n\n    @Override\n    public void trace(String message, Throwable t) {\n        logger.trace(message, t);\n    }\n\n    @Override\n    public void trace(String message) {\n        logger.trace(message);\n    }\n\n    @Override\n    public void debug(String message, Throwable t) {\n        logger.debug(message, t);\n    }\n\n    @Override\n    public void debug(String message) {\n        logger.debug(message);\n    }\n\n    @Override\n    public void info(String message, Throwable t) {\n        logger.info(message, t);\n    }\n\n    @Override\n    public void info(String message) {\n        logger.info(message);\n    }\n\n    @Override\n    public void warning(String message, Throwable t) {\n        logger.warn(message, t);\n    }\n\n    @Override\n    public void warning(String message) {\n        logger.warn(message);\n    }\n\n    @Override\n    public void error(String message, Throwable t) {\n        logger.error(message, t);\n    }\n\n    @Override\n    public void error(String message) {\n        logger.error(message);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/impl/ServiceAccessorFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service.impl;\n\nimport java.io.IOException;\n\nimport org.bonitasoft.engine.exception.BonitaHomeConfigurationException;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.home.BonitaHomeServer;\nimport org.bonitasoft.engine.service.APIAccessResolver;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\n\n/**\n * Singleton factory that creates and manages the main {@link ServiceAccessor} instance for the Bonita Engine.\n * <p>\n * This factory is the main entry point to access all engine services and API implementations. It manages\n * the lifecycle of the Spring application context that contains all engine services (100+ beans) and\n * provides access to core infrastructure components.\n * <p>\n * <b>Key Responsibilities:</b>\n * <ul>\n * <li><b>Service Accessor Creation</b>: Creates and caches the main {@link ServiceAccessor} instance,\n * which wraps the engine's Spring application context</li>\n * <li><b>Session Accessor Creation</b>: Provides {@link SessionAccessor} for thread-local session management</li>\n * <li><b>API Access Resolver</b>: Creates {@link APIAccessResolver} for API implementation resolution</li>\n * <li><b>Lifecycle Management</b>: Handles cleanup via {@link #destroyAccessors()} during engine shutdown</li>\n * <li><b>Configuration Loading</b>: Loads class names from bonita-platform-private-community.properties</li>\n * </ul>\n * <p>\n * <b>Configuration Properties:</b>\n * The factory reads the following properties from {@code bonita-platform-private-community.properties}:\n * <ul>\n * <li>{@code serviceAccessors}: Class name of {@link ServiceAccessors} implementation (default:\n * ServiceAccessorsImpl)</li>\n * <li>{@code apiAccessResolver}: Class name of {@link APIAccessResolver} implementation</li>\n * </ul>\n * <p>\n * <b>Usage Pattern:</b>\n *\n * <pre>\n *\n * ServiceAccessor accessor = ServiceAccessorFactory.getInstance().createServiceAccessor();\n * PlatformService platformService = accessor.getPlatformService();\n * </pre>\n *\n * <p>\n * <b>Spring Context Hierarchy:</b>\n * The {@link ServiceAccessor} created by this factory contains the root Spring context for the engine,\n * which serves as the parent context for the web application context.\n * <p>\n * <b>Thread Safety:</b> All factory methods are synchronized to ensure thread-safe singleton creation.\n *\n * @see ServiceAccessor\n * @see SessionAccessor\n * @see APIAccessResolver\n */\npublic class ServiceAccessorFactory {\n\n    private static final ServiceAccessorFactory INSTANCE = new ServiceAccessorFactory();\n    private static final String API_ACCESS_RESOLVER_CLASS_NAME = \"apiAccessResolver\";\n    private static final String SERVICE_ACCESSORS = \"serviceAccessors\";\n\n    private APIAccessResolver apiAccessResolver;\n    private ServiceAccessors serviceAccessors;\n\n    protected ServiceAccessorFactory() {\n        super();\n    }\n\n    public static ServiceAccessorFactory getInstance() {\n        return INSTANCE;\n    }\n\n    public synchronized ServiceAccessor createServiceAccessor() throws BonitaHomeConfigurationException, IOException,\n            ReflectiveOperationException {\n        return getServiceAccessors().getServiceAccessor();\n    }\n\n    private synchronized ServiceAccessors getServiceAccessors() throws BonitaHomeConfigurationException, IOException,\n            ReflectiveOperationException {\n        if (serviceAccessors == null) {\n            serviceAccessors = (ServiceAccessors) loadClassFromPropertyName(SERVICE_ACCESSORS).getDeclaredConstructor()\n                    .newInstance();\n        }\n        return serviceAccessors;\n    }\n\n    public SessionAccessor createSessionAccessor()\n            throws BonitaHomeNotSetException, IOException, BonitaHomeConfigurationException,\n            ReflectiveOperationException {\n        return createServiceAccessor().getSessionAccessor();\n    }\n\n    public synchronized APIAccessResolver createAPIAccessResolver()\n            throws IOException, BonitaHomeConfigurationException, ReflectiveOperationException {\n        if (apiAccessResolver == null) {\n            apiAccessResolver = (APIAccessResolver) loadClassFromPropertyName(API_ACCESS_RESOLVER_CLASS_NAME)\n                    .getDeclaredConstructor().newInstance();\n        }\n        return apiAccessResolver;\n    }\n\n    private Class<?> loadClassFromPropertyName(String propertyName)\n            throws IOException, BonitaHomeConfigurationException, ClassNotFoundException {\n        final String sessionAccessorStr = BonitaHomeServer.getInstance().getPlatformProperties()\n                .getProperty(propertyName);\n        if (sessionAccessorStr == null) {\n            throw new BonitaHomeConfigurationException(\n                    propertyName + \" not set in bonita-platform-private-community.properties\");\n        }\n        return Class.forName(sessionAccessorStr);\n    }\n\n    public synchronized void destroyAccessors() {\n        serviceAccessors.destroy();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/impl/ServiceAccessors.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service.impl;\n\nimport org.bonitasoft.engine.service.ServiceAccessor;\n\n/**\n * This is the main entry point to the engine services\n *\n * @author Baptiste Mesta.\n */\npublic interface ServiceAccessors {\n\n    ServiceAccessor getServiceAccessor();\n\n    void destroy();\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/impl/SpringBeanAccessor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service.impl;\n\nimport static org.bonitasoft.engine.Profiles.CLUSTER;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Properties;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\nimport org.bonitasoft.engine.home.BonitaHomeServer;\nimport org.bonitasoft.engine.io.IOUtil;\nimport org.bonitasoft.platform.configuration.model.BonitaConfiguration;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.core.env.MutablePropertySources;\nimport org.springframework.core.env.PropertiesPropertySource;\nimport org.springframework.core.env.StandardEnvironment;\n\n/**\n * Spring bean accessor that get its configuration from configuration file in classpath and in database\n *\n * @author Charles Souillard\n */\n@Slf4j\npublic class SpringBeanAccessor {\n\n    private static final String HAZELCAST_CONFIG_FILENAME = \"hazelcast.xml\";\n\n    static final BonitaHomeServer BONITA_HOME_SERVER = BonitaHomeServer.getInstance();\n\n    private static final String WORK_CORE_POOL_SIZE = \"bonita.tenant.work.corePoolSize\";\n    private static final String WORK_MAX_POOL_SIZE = \"bonita.tenant.work.maximumPoolSize\";\n    private static final String WORK_KEEP_ALIVE_IN_SECONDS = \"bonita.tenant.work.keepAliveTimeSeconds\";\n    private static final String WORK_SQLSERVER_DELAY_ON_MULTIPLE_XA_RESOURCE = \"bonita.tenant.work.sqlserver.delayOnMultipleXAResource\";\n    private static final String WORK_MYSQL_DELAY_ON_MULTIPLE_XA_RESOURCE = \"bonita.tenant.work.mysql.delayOnMultipleXAResource\";\n    private static final String WORK_ORACLE_DELAY_ON_MULTIPLE_XA_RESOURCE = \"bonita.tenant.work.oracle.delayOnMultipleXAResource\";\n    private static final String CONNECTOR_CORE_POOL_SIZE = \"bonita.tenant.connector.corePoolSize\";\n    private static final String CONNECTOR_MAX_POOL_SIZE = \"bonita.tenant.connector.maximumPoolSize\";\n    private static final String CONNECTOR_KEEP_ALIVE_IN_SECONDS = \"bonita.tenant.connector.keepAliveTimeSeconds\";\n    private static final String PROMOTION_MESSAGES_ENABLED = \"bonita.runtime.promotion.messages.enabled\";\n\n    private BonitaSpringContext context;\n\n    private boolean contextFinishedInitialized = false;\n\n    public <T> T getService(final Class<T> serviceClass) {\n        return getContext().getBean(serviceClass);\n    }\n\n    <T> T getService(String name, Class<T> clazz) {\n        return getContext().getBean(name, clazz);\n    }\n\n    <T> T getService(String serviceName) {\n        return (T) getContext().getBean(serviceName);\n    }\n\n    public ApplicationContext getContext() {\n        if (!contextFinishedInitialized) {\n            initializeContext();\n        }\n        return context;\n    }\n\n    private synchronized void initializeContext() {\n        if (contextFinishedInitialized) {\n            return;\n        }\n        try {\n            context = createContext();\n            configureContext(context);\n            context.refresh();\n            contextFinishedInitialized = true;\n        } catch (IOException e) {\n            throw new BonitaRuntimeException(e);\n        }\n    }\n\n    private void configureContext(BonitaSpringContext context) throws IOException {\n        boolean isCluster = isCluster();\n        for (String classPathResource : getSpringFileFromClassPath(isCluster)) {\n            context.addClassPathResource(classPathResource);\n        }\n        if (isCluster) {\n            context.getEnvironment().setActiveProfiles(CLUSTER);\n        }\n        for (BonitaConfiguration bonitaConfiguration : getConfigurationFromDatabase()) {\n            context.addByteArrayResource(bonitaConfiguration);\n        }\n\n        MutablePropertySources propertySources = context.getEnvironment().getPropertySources();\n        final boolean legacyMode = context.getEnvironment().getProperty(\"bonita.runtime.properties.order.legacy-mode\",\n                boolean.class, false);\n        if (legacyMode) {\n            // continue to have properties files from database with the higher priority order.\n            propertySources.addFirst(new PropertiesPropertySource(\"contextProperties\", getProperties()));\n        } else {\n            // Make values from database be easily overridable with default Spring mechanism.\n            // This is achieved by adding Bonita properties from database with a priority just AFTER\n            // OS environment variables and Java System properties.\n            // For default Spring property order, see\n            // https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.external-config\n            propertySources.addAfter(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,\n                    new PropertiesPropertySource(\"contextProperties\", getProperties()));\n        }\n        warnDeprecatedProperties(propertySources);\n    }\n\n    protected void warnDeprecatedProperties(MutablePropertySources propertySources) {\n        warnIfPropertyIsDeprecated(propertySources, WORK_CORE_POOL_SIZE);\n        warnIfPropertyIsDeprecated(propertySources, WORK_MAX_POOL_SIZE);\n        warnIfPropertyIsDeprecated(propertySources, WORK_KEEP_ALIVE_IN_SECONDS);\n        warnIfPropertyIsDeprecated(propertySources, WORK_SQLSERVER_DELAY_ON_MULTIPLE_XA_RESOURCE);\n        warnIfPropertyIsDeprecated(propertySources, WORK_MYSQL_DELAY_ON_MULTIPLE_XA_RESOURCE);\n        warnIfPropertyIsDeprecated(propertySources, WORK_ORACLE_DELAY_ON_MULTIPLE_XA_RESOURCE);\n        warnIfPropertyIsDeprecated(propertySources, CONNECTOR_CORE_POOL_SIZE);\n        warnIfPropertyIsDeprecated(propertySources, CONNECTOR_MAX_POOL_SIZE);\n        warnIfPropertyIsDeprecated(propertySources, CONNECTOR_KEEP_ALIVE_IN_SECONDS);\n        warnIfPropertyIsDeprecated(propertySources, PROMOTION_MESSAGES_ENABLED);\n    }\n\n    private void warnIfPropertyIsDeprecated(MutablePropertySources propertySources, String property) {\n        propertySources.stream()\n                .filter(ps -> ps.containsProperty(property))\n                .map(ps -> ps.getProperty(property))\n                .filter(Objects::nonNull)\n                .findFirst()\n                .ifPresent(value -> log.warn(\n                        \"{} property is not supported in community edition anymore. It will be ignored.\",\n                        property));\n    }\n\n    protected BonitaSpringContext createContext() {\n        return new BonitaSpringContext(null, \"Platform\");\n    }\n\n    public void destroy() {\n        if (context != null) {\n            context.close();\n            context = null;\n        }\n        contextFinishedInitialized = false;\n    }\n\n    protected Properties getProperties() throws IOException {\n        Properties platformProperties = BONITA_HOME_SERVER.getPlatformProperties();\n        platformProperties.putAll(BONITA_HOME_SERVER.getTenantProperties());\n        return platformProperties;\n    }\n\n    protected List<BonitaConfiguration> getConfigurationFromDatabase() throws IOException {\n        List<BonitaConfiguration> bonitaConfigurations = new ArrayList<>();\n\n        List<BonitaConfiguration> platformConfiguration = BONITA_HOME_SERVER.getPlatformConfiguration();\n\n        extractHazelcastConfigurationFile(platformConfiguration);\n\n        bonitaConfigurations.addAll(platformConfiguration);\n        bonitaConfigurations.addAll(BONITA_HOME_SERVER.getTenantConfiguration());\n        return bonitaConfigurations;\n    }\n\n    private static void extractHazelcastConfigurationFile(List<BonitaConfiguration> platformConfiguration)\n            throws IOException {\n        // handle special case for Hazelcast configuration file:\n        Iterator<BonitaConfiguration> iterator = platformConfiguration.iterator();\n        while (iterator.hasNext()) {\n            BonitaConfiguration bonitaConfiguration = iterator.next();\n            if (HAZELCAST_CONFIG_FILENAME.equals(bonitaConfiguration.getResourceName())) {\n                iterator.remove();\n                final File hzConfigFile = new File(IOUtil.TMP_DIRECTORY, HAZELCAST_CONFIG_FILENAME);\n                if (!hzConfigFile.exists()) {\n                    Files.write(hzConfigFile.toPath(), bonitaConfiguration.getResourceContent());\n                    hzConfigFile.deleteOnExit();\n                }\n                String hazelcastConfigFile = hzConfigFile.getAbsolutePath();\n\n                // Allow to preserve \"hazelcast.config\" if already passed as System property:\n                if (!System.getProperties().containsKey(\"hazelcast.config\")) {\n                    log.info(\"Setting sysprop 'hazelcast.config' to {}\", hazelcastConfigFile);\n                    System.setProperty(\"hazelcast.config\", hazelcastConfigFile);\n                    System.setProperty(\"hibernate.javax.cache.uri\", new File(hazelcastConfigFile).toURI().toString());\n                } else {\n                    log.info(\"Sysprop 'hazelcast.config' already set to '{}'. Preserving this value.\",\n                            System.getProperty(\"hazelcast.config\"));\n                }\n                return; // found, no need to go further\n            }\n        }\n    }\n\n    protected List<String> getSpringFileFromClassPath(boolean cluster) {\n        return List.of(\"bonita-community.xml\", \"bonita-subscription.xml\");\n    }\n\n    String getPropertyWithPlaceholder(Properties properties, String key, String defaultValue) {\n        String property = properties.getProperty(key, defaultValue);\n        if (property.startsWith(\"${\") && property.endsWith(\"}\")) {\n            property = property.substring(2, property.length() - 1);\n            String sysPropertyKey = property.substring(0, property.indexOf(':'));\n            String sysPropertyDefaultValue = property.substring(property.indexOf(':') + 1);\n            return System.getProperty(sysPropertyKey, sysPropertyDefaultValue);\n        }\n        return property;\n    }\n\n    protected boolean isCluster() throws IOException {\n        return Boolean.parseBoolean(\n                getPropertyWithPlaceholder(BONITA_HOME_SERVER.getPlatformProperties(), \"bonita.cluster\", \"false\"));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/impl/SpringServiceAccessor.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service.impl;\n\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.api.impl.resolver.BusinessArchiveArtifactsManager;\nimport org.bonitasoft.engine.archive.ArchiveService;\nimport org.bonitasoft.engine.authentication.GenericAuthenticationService;\nimport org.bonitasoft.engine.authentication.GenericAuthenticationServiceAccessor;\nimport org.bonitasoft.engine.authorization.PermissionService;\nimport org.bonitasoft.engine.bar.BusinessArchiveService;\nimport org.bonitasoft.engine.bpm.model.impl.BPMInstancesCreator;\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.importer.ApplicationImporter;\nimport org.bonitasoft.engine.business.data.BusinessDataModelRepository;\nimport org.bonitasoft.engine.business.data.BusinessDataRepository;\nimport org.bonitasoft.engine.business.data.BusinessDataService;\nimport org.bonitasoft.engine.cache.CacheService;\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.command.CommandService;\nimport org.bonitasoft.engine.connector.ConnectorExecutor;\nimport org.bonitasoft.engine.core.category.CategoryService;\nimport org.bonitasoft.engine.core.connector.ConnectorInstanceService;\nimport org.bonitasoft.engine.core.connector.ConnectorService;\nimport org.bonitasoft.engine.core.contract.data.ContractDataService;\nimport org.bonitasoft.engine.core.data.instance.TransientDataService;\nimport org.bonitasoft.engine.core.document.api.DocumentService;\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.filter.UserFilterService;\nimport org.bonitasoft.engine.core.form.FormMappingService;\nimport org.bonitasoft.engine.core.login.LoginService;\nimport org.bonitasoft.engine.core.login.TechnicalUser;\nimport org.bonitasoft.engine.core.operation.OperationService;\nimport org.bonitasoft.engine.core.platform.login.PlatformLoginService;\nimport org.bonitasoft.engine.core.process.comment.api.SCommentService;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.BPMFailureService;\nimport org.bonitasoft.engine.core.process.instance.api.GatewayInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.RefBusinessDataService;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceRepository;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceService;\nimport org.bonitasoft.engine.data.instance.api.ParentContainerResolver;\nimport org.bonitasoft.engine.dependency.DependencyService;\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.exception.NotFoundException;\nimport org.bonitasoft.engine.execution.ContainerRegistry;\nimport org.bonitasoft.engine.execution.FlowNodeExecutor;\nimport org.bonitasoft.engine.execution.ProcessExecutor;\nimport org.bonitasoft.engine.execution.ProcessInstanceInterruptor;\nimport org.bonitasoft.engine.execution.ProcessStarterVerifier;\nimport org.bonitasoft.engine.execution.archive.BPMArchiverService;\nimport org.bonitasoft.engine.execution.event.EventsHandler;\nimport org.bonitasoft.engine.execution.state.FlowNodeStateManager;\nimport org.bonitasoft.engine.execution.work.BPMWorkFactory;\nimport org.bonitasoft.engine.expression.ExpressionService;\nimport org.bonitasoft.engine.identity.IconService;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.incident.IncidentService;\nimport org.bonitasoft.engine.lock.LockService;\nimport org.bonitasoft.engine.message.MessagesHandlingService;\nimport org.bonitasoft.engine.page.PageMappingService;\nimport org.bonitasoft.engine.page.PageService;\nimport org.bonitasoft.engine.parameter.ParameterService;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.platform.PlatformManager;\nimport org.bonitasoft.engine.platform.PlatformRetriever;\nimport org.bonitasoft.engine.platform.PlatformService;\nimport org.bonitasoft.engine.platform.authentication.PlatformAuthenticationService;\nimport org.bonitasoft.engine.platform.command.PlatformCommandService;\nimport org.bonitasoft.engine.platform.configuration.NodeConfiguration;\nimport org.bonitasoft.engine.platform.session.PlatformSessionService;\nimport org.bonitasoft.engine.profile.ProfileService;\nimport org.bonitasoft.engine.profile.ProfilesExporter;\nimport org.bonitasoft.engine.profile.ProfilesImporter;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.resources.ProcessResourcesService;\nimport org.bonitasoft.engine.resources.TenantResourcesService;\nimport org.bonitasoft.engine.scheduler.JobService;\nimport org.bonitasoft.engine.scheduler.SchedulerService;\nimport org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor;\nimport org.bonitasoft.engine.service.BroadcastService;\nimport org.bonitasoft.engine.service.InstallationService;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.ServicesResolver;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.bonitasoft.engine.supervisor.mapping.SupervisorMappingService;\nimport org.bonitasoft.engine.synchro.SynchroService;\nimport org.bonitasoft.engine.temporary.content.TemporaryContentService;\nimport org.bonitasoft.engine.tenant.TenantServicesManager;\nimport org.bonitasoft.engine.tenant.TenantStateManager;\nimport org.bonitasoft.engine.tracking.TimeTracker;\nimport org.bonitasoft.engine.transaction.TransactionService;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.bonitasoft.engine.work.WorkExecutorService;\nimport org.bonitasoft.engine.work.WorkService;\nimport org.springframework.beans.factory.NoSuchBeanDefinitionException;\nimport org.springframework.context.ApplicationContext;\n\npublic class SpringServiceAccessor implements ServiceAccessor {\n\n    protected final SpringBeanAccessor beanAccessor;\n\n    public SpringServiceAccessor(final SpringBeanAccessor beanAccessor) {\n        this.beanAccessor = beanAccessor;\n    }\n\n    @Override\n    public ParentContainerResolver getParentContainerResolver() {\n        return beanAccessor.getService(ParentContainerResolver.class);\n    }\n\n    @Override\n    public TimeTracker getTimeTracker() {\n        return beanAccessor.getService(TimeTracker.class);\n    }\n\n    @Override\n    public SessionAccessor getSessionAccessor() {\n        return beanAccessor.getService(SessionAccessor.class);\n    }\n\n    @Override\n    public SessionService getSessionService() {\n        return beanAccessor.getService(SessionService.class);\n    }\n\n    @Override\n    public IdentityService getIdentityService() {\n        return beanAccessor.getService(IdentityService.class);\n    }\n\n    @Override\n    public IconService getIconService() {\n        return beanAccessor.getService(IconService.class);\n    }\n\n    @Override\n    public LoginService getLoginService() {\n        return beanAccessor.getService(LoginService.class);\n    }\n\n    @Override\n    public QueriableLoggerService getQueriableLoggerService() {\n        return beanAccessor.getService(\"queriableLoggerService\", QueriableLoggerService.class);\n    }\n\n    @Override\n    public UserTransactionService getUserTransactionService() {\n        return getTransactionService();\n    }\n\n    @Override\n    public ProcessDefinitionService getProcessDefinitionService() {\n        return beanAccessor.getService(ProcessDefinitionService.class);\n    }\n\n    @Override\n    public ProcessInstanceService getProcessInstanceService() {\n        return beanAccessor.getService(ProcessInstanceService.class);\n    }\n\n    @Override\n    public ActivityInstanceService getActivityInstanceService() {\n        return beanAccessor.getService(ActivityInstanceService.class);\n    }\n\n    @Override\n    public BPMFailureService getBpmFailureService() {\n        return beanAccessor.getService(BPMFailureService.class);\n    }\n\n    @Override\n    public BPMInstancesCreator getBPMInstancesCreator() {\n        return beanAccessor.getService(BPMInstancesCreator.class);\n    }\n\n    @Override\n    public FlowNodeExecutor getFlowNodeExecutor() {\n        return beanAccessor.getService(FlowNodeExecutor.class);\n    }\n\n    @Override\n    public ProcessExecutor getProcessExecutor() {\n        return beanAccessor.getService(ProcessExecutor.class);\n    }\n\n    @Override\n    public FlowNodeStateManager getFlowNodeStateManager() {\n        return beanAccessor.getService(FlowNodeStateManager.class);\n    }\n\n    @Override\n    public ActorMappingService getActorMappingService() {\n        return beanAccessor.getService(ActorMappingService.class);\n    }\n\n    @Override\n    public ArchiveService getArchiveService() {\n        return beanAccessor.getService(ArchiveService.class);\n    }\n\n    @Override\n    public CategoryService getCategoryService() {\n        return beanAccessor.getService(CategoryService.class);\n    }\n\n    @Override\n    public CommandService getCommandService() {\n        return beanAccessor.getService(CommandService.class);\n    }\n\n    @Override\n    public ClassLoaderService getClassLoaderService() {\n        return beanAccessor.getService(ClassLoaderService.class);\n    }\n\n    @Override\n    public DependencyService getDependencyService() {\n        // default implementation taken from SpringTenantServiceAccessor\n        return beanAccessor.getService(\"dependencyService\", DependencyService.class);\n    }\n\n    @Override\n    public DependencyService getPlatformDependencyService() {\n        return beanAccessor.getService(\"platformDependencyService\", DependencyService.class);\n    }\n\n    @Override\n    public EventInstanceService getEventInstanceService() {\n        return beanAccessor.getService(EventInstanceService.class);\n    }\n\n    @Override\n    public EventInstanceRepository getEventInstanceRepository() {\n        return beanAccessor.getService(EventInstanceRepository.class);\n    }\n\n    @Override\n    public ConnectorService getConnectorService() {\n        return beanAccessor.getService(\"connectorService\", ConnectorService.class);\n    }\n\n    @Override\n    public ConnectorInstanceService getConnectorInstanceService() {\n        return beanAccessor.getService(ConnectorInstanceService.class);\n    }\n\n    @Override\n    public ConnectorExecutor getConnectorExecutor() {\n        return beanAccessor.getService(ConnectorExecutor.class);\n    }\n\n    @Override\n    public ExpressionService getExpressionService() {\n        return beanAccessor.getService(ExpressionService.class);\n    }\n\n    @Override\n    public DocumentService getDocumentService() {\n        return beanAccessor.getService(DocumentService.class);\n    }\n\n    @Override\n    public ProfileService getProfileService() {\n        return beanAccessor.getService(ProfileService.class);\n    }\n\n    @Override\n    public ProfilesImporter getProfilesImporter() {\n        return beanAccessor.getService(ProfilesImporter.class);\n    }\n\n    @Override\n    public ProfilesExporter getProfilesExporter() {\n        return beanAccessor.getService(ProfilesExporter.class);\n    }\n\n    @Override\n    public DataInstanceService getDataInstanceService() {\n        return beanAccessor.getService(DataInstanceService.class);\n    }\n\n    @Override\n    public OperationService getOperationService() {\n        return beanAccessor.getService(OperationService.class);\n    }\n\n    @Override\n    public ExpressionResolverService getExpressionResolverService() {\n        return beanAccessor.getService(ExpressionResolverService.class);\n    }\n\n    @Override\n    public SupervisorMappingService getSupervisorService() {\n        return beanAccessor.getService(SupervisorMappingService.class);\n    }\n\n    @Override\n    public UserFilterService getUserFilterService() {\n        return beanAccessor.getService(\"userFilterService\", UserFilterService.class);\n    }\n\n    @Override\n    public SearchEntitiesDescriptor getSearchEntitiesDescriptor() {\n        return beanAccessor.getService(SearchEntitiesDescriptor.class);\n    }\n\n    @Override\n    public SCommentService getCommentService() {\n        return beanAccessor.getService(SCommentService.class);\n    }\n\n    @Override\n    public ContainerRegistry getContainerRegistry() {\n        return beanAccessor.getService(ContainerRegistry.class);\n    }\n\n    @Override\n    public LockService getLockService() {\n        return beanAccessor.getService(LockService.class);\n    }\n\n    @Override\n    public EventsHandler getEventsHandler() {\n        return beanAccessor.getService(EventsHandler.class);\n    }\n\n    @Override\n    public EventService getEventService() {\n        return beanAccessor.getService(\"platformEventService\", EventService.class);\n    }\n\n    public SpringBeanAccessor getBeanAccessor() {\n        return beanAccessor;\n    }\n\n    @Override\n    public CacheService getCacheService() {\n        return beanAccessor.getService(CacheService.class);\n    }\n\n    @Override\n    public BusinessArchiveArtifactsManager getBusinessArchiveArtifactsManager() {\n        return beanAccessor.getService(BusinessArchiveArtifactsManager.class);\n    }\n\n    @Override\n    public WorkService getWorkService() {\n        return beanAccessor.getService(WorkService.class);\n    }\n\n    @Override\n    public WorkExecutorService getWorkExecutorService() {\n        return beanAccessor.getService(WorkExecutorService.class);\n    }\n\n    @Override\n    public SynchroService getSynchroService() {\n        return beanAccessor.getService(SynchroService.class);\n    }\n\n    @Override\n    public IncidentService getIncidentService() {\n        return beanAccessor.getService(IncidentService.class);\n    }\n\n    @Override\n    public SchedulerService getSchedulerService() {\n        return beanAccessor.getService(SchedulerService.class);\n    }\n\n    @Override\n    public JobService getJobService() {\n        return beanAccessor.getService(JobService.class);\n    }\n\n    @Override\n    public TransientDataService getTransientDataService() {\n        return beanAccessor.getService(TransientDataService.class);\n    }\n\n    @Override\n    public GatewayInstanceService getGatewayInstanceService() {\n        return beanAccessor.getService(GatewayInstanceService.class);\n    }\n\n    @Override\n    public void destroy() {\n        beanAccessor.destroy();\n    }\n\n    @Override\n    public <T> T lookup(final String serviceName) throws NotFoundException {\n        try {\n\n            return beanAccessor.getService(serviceName);\n        } catch (NoSuchBeanDefinitionException e) {\n            throw new NotFoundException(e);\n        }\n    }\n\n    @Override\n    public <T> T lookup(final Class<T> beanClass) throws NotFoundException {\n        try {\n\n            return beanAccessor.getService(beanClass);\n        } catch (NoSuchBeanDefinitionException e) {\n            throw new NotFoundException(e);\n        }\n    }\n\n    @Override\n    public PermissionService getPermissionService() {\n        return beanAccessor.getService(PermissionService.class);\n    }\n\n    @Override\n    public ContractDataService getContractDataService() {\n        return beanAccessor.getService(ContractDataService.class);\n    }\n\n    @Override\n    public ParameterService getParameterService() {\n        return beanAccessor.getService(ParameterService.class);\n    }\n\n    /**\n     * might not be an available service\n     */\n    @Override\n    public PageService getPageService() {\n        return beanAccessor.getService(PageService.class);\n    }\n\n    @Override\n    public ApplicationService getApplicationService() {\n        return beanAccessor.getService(ApplicationService.class);\n    }\n\n    @Override\n    public BusinessDataRepository getBusinessDataRepository() {\n        return beanAccessor.getService(BusinessDataRepository.class);\n    }\n\n    @Override\n    public BusinessDataModelRepository getBusinessDataModelRepository() {\n        return beanAccessor.getService(BusinessDataModelRepository.class);\n    }\n\n    @Override\n    public RefBusinessDataService getRefBusinessDataService() {\n        return beanAccessor.getService(RefBusinessDataService.class);\n    }\n\n    @Override\n    public GenericAuthenticationService getAuthenticationService() {\n        return beanAccessor.getService(GenericAuthenticationServiceAccessor.class).getAuthenticationService();\n    }\n\n    @Override\n    public ReadPersistenceService getReadPersistenceService() {\n        return beanAccessor.getService(\"persistenceService\");\n    }\n\n    @Override\n    public Recorder getRecorder() {\n        return beanAccessor.getService(Recorder.class);\n    }\n\n    @Override\n    public BusinessArchiveService getBusinessArchiveService() {\n        return beanAccessor.getService(BusinessArchiveService.class);\n    }\n\n    @Override\n    public BusinessDataService getBusinessDataService() {\n        return beanAccessor.getService(BusinessDataService.class);\n    }\n\n    @Override\n    public FormMappingService getFormMappingService() {\n        return beanAccessor.getService(FormMappingService.class);\n    }\n\n    @Override\n    public PageMappingService getPageMappingService() {\n        return beanAccessor.getService(PageMappingService.class);\n    }\n\n    public ProcessResourcesService getProcessResourcesService() {\n        return beanAccessor.getService(ProcessResourcesService.class);\n    }\n\n    public TenantResourcesService getTenantResourcesService() {\n        return beanAccessor.getService(TenantResourcesService.class);\n    }\n\n    public MessagesHandlingService getMessagesHandlingService() {\n        return beanAccessor.getService(MessagesHandlingService.class);\n    }\n\n    @Override\n    public ProcessInstanceInterruptor getProcessInstanceInterruptor() {\n        return beanAccessor.getService(ProcessInstanceInterruptor.class);\n    }\n\n    public BPMWorkFactory getBPMWorkFactory() {\n        return beanAccessor.getService(BPMWorkFactory.class);\n    }\n\n    public TechnicalUser getTechnicalUser() {\n        return beanAccessor.getService(TechnicalUser.class);\n    }\n\n    @Override\n    public TenantStateManager getTenantStateManager() {\n        return beanAccessor.getService(TenantStateManager.class);\n    }\n\n    @Override\n    public TenantServicesManager getTenantServicesManager() {\n        return beanAccessor.getService(TenantServicesManager.class);\n    }\n\n    @Override\n    public BPMArchiverService getBPMArchiverService() {\n        return beanAccessor.getService(BPMArchiverService.class);\n    }\n\n    @Override\n    public ApplicationImporter getApplicationImporter() {\n        return beanAccessor.getService(ApplicationImporter.class);\n    }\n\n    @Override\n    public TransactionService getTransactionService() {\n        return beanAccessor.getService(TransactionService.class);\n    }\n\n    @Override\n    public PlatformLoginService getPlatformLoginService() {\n        return beanAccessor.getService(PlatformLoginService.class);\n    }\n\n    @Override\n    public PlatformService getPlatformService() {\n        return beanAccessor.getService(PlatformService.class);\n    }\n\n    @Override\n    public PlatformCommandService getPlatformCommandService() {\n        return beanAccessor.getService(\"platformCommandService\", PlatformCommandService.class);\n    }\n\n    @Override\n    public PlatformSessionService getPlatformSessionService() {\n        return beanAccessor.getService(PlatformSessionService.class);\n    }\n\n    @Override\n    public NodeConfiguration getPlatformConfiguration() {\n        return beanAccessor.getService(NodeConfiguration.class);\n    }\n\n    @Override\n    public PlatformManager getPlatformManager() {\n        return beanAccessor.getService(PlatformManager.class);\n    }\n\n    @Override\n    public CacheService getPlatformCacheService() {\n        return beanAccessor.getService(CacheService.class);\n    }\n\n    @Override\n    public TemporaryContentService getTemporaryContentService() {\n        return beanAccessor.getService(TemporaryContentService.class);\n    }\n\n    @Override\n    public BroadcastService getBroadcastService() {\n        return beanAccessor.getService(BroadcastService.class);\n    }\n\n    @Override\n    public PlatformAuthenticationService getPlatformAuthenticationService() {\n        return beanAccessor.getService(PlatformAuthenticationService.class);\n    }\n\n    @Override\n    public ServicesResolver getServicesResolver() {\n        return beanAccessor.getService(ServicesResolver.class);\n    }\n\n    @Override\n    public void publishEvent(final Object event) {\n        beanAccessor.getContext().publishEvent(event);\n    }\n\n    @Override\n    public ApplicationContext getContext() {\n        return beanAccessor.getContext();\n    }\n\n    @Override\n    public PlatformRetriever getPlatformRetriever() {\n        return beanAccessor.getService(PlatformRetriever.class);\n    }\n\n    @Override\n    public InstallationService getInstallationService() {\n        return beanAccessor.getService(InstallationService.class);\n    }\n\n    @Override\n    public ProcessStarterVerifier getProcessStarterVerifier() {\n        return beanAccessor.getService(ProcessStarterVerifier.class);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/impl/SpringServiceAccessors.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service.impl;\n\nimport org.bonitasoft.engine.service.ServiceAccessor;\n\n/**\n * @author Baptiste Mesta.\n */\npublic class SpringServiceAccessors implements ServiceAccessors {\n\n    private SpringBeanAccessor springBeanAccessor;\n\n    //----  Initialize spring contexts\n    protected synchronized SpringBeanAccessor getBeanAccessor() {\n        if (springBeanAccessor == null) {\n            springBeanAccessor = createBeanAccessor();\n        }\n        return springBeanAccessor;\n    }\n\n    protected SpringBeanAccessor createBeanAccessor() {\n        return new SpringBeanAccessor();\n    }\n\n    @Override\n    public ServiceAccessor getServiceAccessor() {\n        return new SpringServiceAccessor(getBeanAccessor());\n    }\n\n    @Override\n    public void destroy() {\n        if (springBeanAccessor != null) {\n            springBeanAccessor.destroy();\n            springBeanAccessor = null;\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/impl/installation/ConfigurationArchive.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service.impl.installation;\n\nimport static java.util.stream.Collectors.toMap;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.file.Files;\nimport java.nio.file.StandardCopyOption;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Properties;\nimport java.util.Set;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipFile;\n\nimport lombok.extern.slf4j.Slf4j;\n\n@Slf4j\npublic class ConfigurationArchive implements AutoCloseable {\n\n    private static final String PARAMETERS_FILENAME = \"parameters.properties\";\n    private final File tmpFile;\n    private String builderVersion;\n    private String targetEnvironment;\n    private final List<ProcessConfiguration> processConfigurations = new ArrayList<>();\n\n    public ConfigurationArchive(byte[] content) throws IOException {\n        tmpFile = File.createTempFile(\"configuration\", \".zip\");\n        try (ByteArrayInputStream is = new ByteArrayInputStream(content)) {\n            Files.copy(is, tmpFile.toPath(), StandardCopyOption.REPLACE_EXISTING);\n        }\n        try (ZipFile zipFile = new ZipFile(tmpFile)) {\n            readManifest(zipFile);\n            loadProcessConfigurations(zipFile);\n            loadProcessConfigurationsParameters(zipFile);\n        }\n    }\n\n    private void loadProcessConfigurationsParameters(ZipFile zipFile) throws IOException {\n        for (ProcessConfiguration processConf : processConfigurations) {\n            ZipEntry parametersEntry = zipFile\n                    .getEntry(String.format(\"%s/%s/%s\", processConf.getName(), processConf.getVersion(),\n                            PARAMETERS_FILENAME));\n            if (parametersEntry != null) {\n                try (InputStream is = zipFile.getInputStream(parametersEntry)) {\n                    Properties parameters = new Properties();\n                    parameters.load(is);\n                    processConf.setParameters(asMap(parameters.entrySet()));\n                }\n            }\n        }\n\n    }\n\n    private Map<String, String> asMap(Set<Entry<Object, Object>> entrySet) {\n        return entrySet.stream()\n                .collect(toMap(entry -> (String) entry.getKey(), entry -> (String) entry.getValue(), (a, b) -> b));\n    }\n\n    private void loadProcessConfigurations(ZipFile zipFile) {\n        zipFile.stream().forEach(entry -> {\n            String name = entry.getName();\n            String[] path = name.split(\"/\");\n            if (path.length > 2) {\n                String processName = path[0];\n                String processVersion = path[1];\n                ProcessConfiguration processConfiguration = new ProcessConfiguration(processName, processVersion);\n                if (!processConfigurations.contains(processConfiguration)) {\n                    processConfigurations.add(processConfiguration);\n                }\n            }\n        });\n\n    }\n\n    private void readManifest(ZipFile zipFile) throws IOException {\n        ZipEntry manifestEntry = zipFile.getEntry(\"MANIFEST\");\n        if (manifestEntry == null) {\n            throw new IOException(\"Invalid archive format (missing MANIFEST).\");\n        }\n        try (InputStream is = zipFile.getInputStream(manifestEntry)) {\n            Properties manifest = new Properties();\n            manifest.load(is);\n            builderVersion = manifest.getProperty(\"builder.version\");\n            targetEnvironment = manifest.getProperty(\"target.environment\");\n        }\n    }\n\n    public String getBuilderVersion() {\n        return builderVersion;\n    }\n\n    public String getTargetEnvironment() {\n        return targetEnvironment;\n    }\n\n    public List<ProcessConfiguration> getProcessConfigurations() {\n        return processConfigurations;\n    }\n\n    @Override\n    public void close() throws Exception {\n        if (tmpFile != null) {\n            log.debug(\"deleting temp file for application configuration file read\");\n            final boolean done = Files.deleteIfExists(tmpFile.toPath());\n            log.debug(\"deleting temp file {}\", done ? \"successful\" : \"unsuccessful!\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/impl/installation/InstallationServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service.impl.installation;\n\nimport static org.bonitasoft.engine.commons.ExceptionUtils.printLightWeightStacktrace;\n\nimport java.util.List;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.api.impl.resolver.BusinessArchiveArtifactsManager;\nimport org.bonitasoft.engine.bpm.process.ActivationState;\nimport org.bonitasoft.engine.bpm.process.Problem;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDisablementException;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.core.process.definition.model.event.SStartEventDefinition;\nimport org.bonitasoft.engine.execution.event.EventsHandler;\nimport org.bonitasoft.engine.parameter.ParameterService;\nimport org.bonitasoft.engine.service.InstallationFailedException;\nimport org.bonitasoft.engine.service.InstallationService;\nimport org.springframework.stereotype.Service;\n\n@Slf4j\n@Service\npublic class InstallationServiceImpl implements InstallationService {\n\n    private final ProcessDefinitionService processDefinitionService;\n    private final ParameterService parameterService;\n    private final BusinessArchiveArtifactsManager businessArchiveArtifactsManager;\n    private final EventsHandler eventsHandler;\n\n    public InstallationServiceImpl(ProcessDefinitionService processDefinitionService, ParameterService parameterService,\n            BusinessArchiveArtifactsManager dependencyResolver, EventsHandler eventsHandler) {\n        this.processDefinitionService = processDefinitionService;\n        this.parameterService = parameterService;\n        this.businessArchiveArtifactsManager = dependencyResolver;\n        this.eventsHandler = eventsHandler;\n    }\n\n    @Override\n    public void install(byte[] binaries, byte[] configuration) throws InstallationFailedException {\n        if (binaries != null) {\n            throw new IllegalStateException(\"binaries archive is not yet implemented\");\n        }\n        if (configuration != null) {\n            installConfiguration(configuration);\n        }\n    }\n\n    private void installConfiguration(byte[] configuration) throws InstallationFailedException {\n        try (ConfigurationArchive confArchive = new ConfigurationArchive(configuration)) {\n            for (ProcessConfiguration processConfiguration : confArchive.getProcessConfigurations()) {\n                String processName = processConfiguration.getName();\n                String processVersion = processConfiguration.getVersion();\n                try {\n                    long pDefId = processDefinitionService.getProcessDefinitionId(processName, processVersion);\n                    parameterService.merge(pDefId, processConfiguration.getParameters());\n                    final SProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(pDefId);\n                    final List<Problem> problems = businessArchiveArtifactsManager\n                            .getProcessResolutionProblems(processDefinition);\n                    final SProcessDefinitionDeployInfo pInfo = processDefinitionService\n                            .getProcessDeploymentInfo(pDefId);\n                    if (problems.isEmpty()) {\n                        businessArchiveArtifactsManager.changeResolutionStatus(pDefId, processDefinitionService,\n                                true);\n                        if (ActivationState.DISABLED.name().equals(pInfo.getActivationState())) {\n                            log.info(\"Configuration of process {}-{} is now complete. Enabling it.\",\n                                    processName, processVersion);\n                            enableProcess(pDefId, processName, processVersion);\n                        }\n                    } else {\n                        businessArchiveArtifactsManager.changeResolutionStatus(pDefId, processDefinitionService,\n                                false);\n                        if (ActivationState.ENABLED.name().equals(pInfo.getActivationState())) {\n                            disableProcess(pDefId, processName, processVersion);\n                        }\n                    }\n                } catch (SProcessDefinitionNotFoundException e) {\n                    // Process configuration may be present in bconf file for non deployed processes filtered by a deploy.json file at deploy time (see la-deployer)\n                    // don't rethrow ex to avoid breaking ongoing configuration deployment.\n                    log.warn(\"Configuration parameter found in BCONF file for non existing process \" + processName + \"-\"\n                            + processVersion + \". Skipping those parameters.\");\n                }\n            }\n        } catch (Exception e) {\n            log.error(\"Failed to apply configuration.\", e);\n            throw new InstallationFailedException(\"Failed to apply configuration.\", e);\n        }\n    }\n\n    private void enableProcess(long processDefinitionId, String processName, String processVersion) {\n        try {\n            processDefinitionService.enableProcess(processDefinitionId, false);\n            handleStartEvents(processDefinitionId);\n        } catch (SBonitaException e) {\n            // It's not mandatory to enable the process here, simply log the action\n            log.warn(\"Failed to enable the process \" + processName + \" in version \" + processVersion\n                    + \" after deploying the configuration\",\n                    printLightWeightStacktrace(e));\n        }\n    }\n\n    private void handleStartEvents(long processDefinitionId) throws SBonitaException {\n        final SProcessDefinition sProcessDefinition = processDefinitionService\n                .getProcessDefinition(processDefinitionId);\n        final SFlowElementContainerDefinition processContainer = sProcessDefinition.getProcessContainer();\n        for (final SStartEventDefinition sStartEventDefinition : processContainer.getStartEvents()) {\n            eventsHandler.handleCatchEvent(sProcessDefinition, sStartEventDefinition, null);\n        }\n    }\n\n    private void disableProcess(long processDefinitionId, String processName, String processVersion) {\n        try {\n            processDefinitionService.disableProcess(processDefinitionId, false);\n        } catch (SProcessDefinitionNotFoundException | SProcessDisablementException e) {\n            // It's not mandatory to disable the process here, simply log the action\n            log.warn(\"Failed to disable the process \" + processName + \" in version \" + processVersion\n                    + \" after deploying the configuration\",\n                    printLightWeightStacktrace(e));\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/impl/installation/ProcessConfiguration.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service.impl.installation;\n\nimport java.util.Map;\n\nimport lombok.EqualsAndHashCode;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\nimport lombok.Setter;\n\n@RequiredArgsConstructor\n@EqualsAndHashCode(exclude = \"parameters\")\npublic class ProcessConfiguration {\n\n    @Getter\n    private final String name;\n    @Getter\n    private final String version;\n    @Getter\n    @Setter\n    private Map<String, String> parameters;\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/platform/PlatformInformationService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service.platform;\n\nimport org.bonitasoft.engine.platform.exception.SPlatformUpdateException;\nimport org.bonitasoft.engine.platform.model.SPlatform;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface PlatformInformationService {\n\n    /**\n     * Updates the platform information\n     *\n     * @param platform the platform to be updated\n     * @param platformInfo the new platform information\n     * @throws SPlatformUpdateException\n     * @since 7.1.0\n     */\n    void updatePlatformInfo(SPlatform platform, String platformInfo) throws SPlatformUpdateException;\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/platform/PlatformInformationServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service.platform;\n\nimport org.bonitasoft.engine.platform.exception.SPlatformUpdateException;\nimport org.bonitasoft.engine.platform.model.SPlatform;\nimport org.bonitasoft.engine.services.PersistenceService;\nimport org.bonitasoft.engine.services.SPersistenceException;\nimport org.bonitasoft.engine.services.UpdateDescriptor;\nimport org.springframework.stereotype.Service;\n\n/**\n * Updates the platform information using directly the {@link PersistenceService}\n *\n * @author Elias Ricken de Medeiros\n */\n@Service(\"platformInformationService\")\npublic class PlatformInformationServiceImpl implements PlatformInformationService {\n\n    private final PersistenceService persistenceService;\n\n    public PlatformInformationServiceImpl(final PersistenceService persistenceService) {\n        this.persistenceService = persistenceService;\n    }\n\n    @Override\n    public void updatePlatformInfo(final SPlatform platform, final String platformInfo)\n            throws SPlatformUpdateException {\n\n        UpdateDescriptor desc = new UpdateDescriptor(platform);\n        desc.addField(SPlatform.INFORMATION, platformInfo);\n\n        try {\n            persistenceService.update(desc);\n        } catch (SPersistenceException e) {\n            throw new SPlatformUpdateException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/tenant/ChangesServicesStateCallable.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tenant;\n\nimport java.io.Serializable;\nimport java.util.concurrent.Callable;\n\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.ServiceAccessorSingleton;\n\n/**\n * @author Emmanuel Duchastenier\n */\nclass ChangesServicesStateCallable implements Callable<Void>, Serializable {\n\n    private final TenantServicesManager.ServiceAction action;\n\n    public ChangesServicesStateCallable(TenantServicesManager.ServiceAction action) {\n        this.action = action;\n    }\n\n    @Override\n    public Void call() throws Exception {\n        ServiceAccessor serviceAccessor = ServiceAccessorSingleton.getInstance();\n        TenantServicesManager tenantServicesManager = serviceAccessor.getTenantServicesManager();\n        TenantStateManager tenantStateManager = serviceAccessor.getTenantStateManager();\n        return tenantStateManager.executeManagementOperation(\n                \"Executing received \" + action.name().toLowerCase() + \" operation\", () -> {\n                    switch (action) {\n                        case START:\n                            tenantServicesManager.start();\n                            break;\n                        case STOP:\n                            tenantServicesManager.stop();\n                            break;\n                        case PAUSE:\n                            tenantServicesManager.pause();\n                            break;\n                        case RESUME:\n                            tenantServicesManager.resume();\n                            break;\n                    }\n                    return null;\n                });\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/tenant/SingleNodeTaskCoordinator.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tenant;\n\n/**\n * Determines whether the current node is responsible for executing a given named task.\n * <p>\n * In single-node deployments, always returns {@code true}.\n * In cluster deployments, uses Hazelcast partition ownership to ensure\n * exactly one node is responsible for each task at any given time.\n * <p>\n * Each task name maps to a distinct Hazelcast partition, so different tasks\n * may be owned by different cluster nodes, distributing the load.\n *\n * @see SingleNodeTaskCoordinatorLocal\n */\npublic interface SingleNodeTaskCoordinator {\n\n    /** Task name used by the {@link org.bonitasoft.engine.tenant.restart.RecoveryScheduler}. */\n    String TASK_RECOVERY = \"RECOVER_NODE\";\n\n    /** Task name used by the (Subscription-specific) {@link com.bonitasoft.engine.retention.DataRetentionScheduler}. */\n    String TASK_CLEANUP_OBSOLETE_DATA = \"CLEANUP_OBSOLETE_DATA\";\n\n    /**\n     * Check whether the current node is responsible for executing the given task.\n     *\n     * @param taskName a stable identifier for the task (used as Hazelcast partition key in cluster mode)\n     * @return {@code true} if this node should execute the task, {@code false} otherwise\n     */\n    boolean isResponsibleForTask(String taskName);\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/tenant/SingleNodeTaskCoordinatorLocal.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tenant;\n\nimport static org.bonitasoft.engine.Profiles.NOT_IN_CLUSTER;\n\nimport org.springframework.context.annotation.Profile;\nimport org.springframework.stereotype.Component;\n\n/**\n * Local (single-node) implementation of {@link SingleNodeTaskCoordinator}.\n * Always returns {@code true} since there is only one node to run any task.\n */\n@Component\n@Profile(NOT_IN_CLUSTER)\npublic class SingleNodeTaskCoordinatorLocal implements SingleNodeTaskCoordinator {\n\n    @Override\n    public boolean isResponsibleForTask(String taskName) {\n        return true;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/tenant/TenantElementsRestartSupervisor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tenant;\n\npublic interface TenantElementsRestartSupervisor {\n\n    boolean shouldRestartElements();\n\n    boolean willRestartElements();\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/tenant/TenantElementsRestartSupervisorLocal.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tenant;\n\nimport org.bonitasoft.engine.commons.TenantLifecycleService;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;\nimport org.springframework.stereotype.Component;\n\n@Component\n@ConditionalOnSingleCandidate(TenantElementsRestartSupervisor.class)\npublic class TenantElementsRestartSupervisorLocal implements TenantLifecycleService, TenantElementsRestartSupervisor {\n\n    private boolean areTenantsElementsAlreadyRestarted;\n\n    @Override\n    public void start() throws SBonitaException {\n    }\n\n    @Override\n    public void stop() {\n        areTenantsElementsAlreadyRestarted = false;\n    }\n\n    @Override\n    public void pause() {\n        areTenantsElementsAlreadyRestarted = false;\n    }\n\n    @Override\n    public boolean shouldRestartElements() {\n        return !areTenantsElementsAlreadyRestarted;\n    }\n\n    @Override\n    public boolean willRestartElements() {\n        if (!areTenantsElementsAlreadyRestarted) {\n            areTenantsElementsAlreadyRestarted = true;\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/tenant/TenantElementsRestarter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tenant;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\n/**\n * Handles the restart of elements when the tenant is started (strategy is different in cluster)\n */\n@Component\n@Slf4j\npublic class TenantElementsRestarter {\n\n    private final TenantRestarter tenantRestarter;\n    private final TenantElementsRestartSupervisor tenantElementsRestartSupervisor;\n\n    public TenantElementsRestarter(TenantRestarter tenantRestarter,\n            TenantElementsRestartSupervisor tenantElementsRestartSupervisor) {\n        this.tenantRestarter = tenantRestarter;\n        this.tenantElementsRestartSupervisor = tenantElementsRestartSupervisor;\n    }\n\n    void prepareRestartOfElements() throws Exception {\n        if (tenantElementsRestartSupervisor.shouldRestartElements()) {\n            // Here get all elements that are not \"finished\"\n            // * FlowNodes that have flag: stateExecuting to true: call execute on them (connectors were executing)\n            // * Process instances with token count == 0 (either not started again or finishing) -> same thing connectors were executing\n            // * transitions that are in state created: call execute on them\n            // * flow node that are completed and not deleted : call execute to make it create transitions and so on\n            // * all element that are in not stable state\n            log.info(\"Preparing restart of elements\");\n            tenantRestarter.executeBeforeServicesStart();\n        } else {\n            log.info(\"Not preparing restart of elements, as another node already did it\");\n        }\n    }\n\n    // Here get all elements that are not \"finished\"\n    // * FlowNodes that have flag: stateExecuting to true: call execute on them (connectors were executing)\n    // * Process instances with token count == 0 (either not started again or finishing) -> same thing connectors were executing\n    // * transitions that are in state created: call execute on them\n    // * flow node that are completed and not deleted : call execute to make it create transitions and so on\n    // * all element that are in not stable state\n    void restartElements() throws Exception {\n        if (tenantElementsRestartSupervisor.willRestartElements()) {\n            log.info(\"Restarting unfinished elements\");\n            tenantRestarter.executeAfterServicesStart();\n        } else {\n            log.info(\"Not restarting elements of tenant, as another node already did it\");\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/tenant/TenantRestarter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tenant;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.impl.StarterThread;\nimport org.bonitasoft.engine.platform.PlatformService;\nimport org.bonitasoft.engine.tenant.restart.TenantRestartHandler;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Baptiste Mesta\n */\n@Component\npublic class TenantRestarter {\n\n    private final UserTransactionService transactionService;\n    private final List<TenantRestartHandler> tenantRestartHandlers;\n    private final PlatformService platformService;\n\n    public TenantRestarter(UserTransactionService transactionService, PlatformService platformService,\n            List<TenantRestartHandler> tenantRestartHandlers) {\n        this.transactionService = transactionService;\n        this.platformService = platformService;\n        this.tenantRestartHandlers = tenantRestartHandlers;\n    }\n\n    public void executeBeforeServicesStart() throws Exception {\n        transactionService.executeInTransaction(() -> {\n            for (TenantRestartHandler tenantRestartHandler : tenantRestartHandlers) {\n                tenantRestartHandler.beforeServicesStart();\n            }\n            return null;\n        });\n    }\n\n    public void executeAfterServicesStart() {\n        new StarterThread(transactionService, platformService,\n                tenantRestartHandlers).start();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/tenant/TenantServicesManager.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tenant;\n\nimport static java.text.MessageFormat.format;\nimport static org.bonitasoft.engine.tenant.TenantServicesManager.ServiceAction.*;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.concurrent.Callable;\n\nimport org.bonitasoft.engine.classloader.ClassLoaderIdentifier;\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.commons.TenantLifecycleService;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SLifecycleException;\nimport org.bonitasoft.engine.service.RunnableWithException;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.bonitasoft.engine.sessionaccessor.SessionIdNotSetException;\nimport org.bonitasoft.engine.transaction.TransactionService;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\n\n/**\n * Handles the lifecycle of tenant services: start, stop, (pause, resume -> will be removed)\n * Does not handle state of the tenant in database\n */\n@Component\npublic class TenantServicesManager {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(TenantServicesManager.class);\n\n    public enum ServiceAction {\n        START, STOP, PAUSE, RESUME\n    }\n\n    public enum TenantServiceState {\n        STOPPED, STARTING, STARTED, STOPPING, ABORTING_START\n    }\n\n    private final SessionAccessor sessionAccessor;\n    private final SessionService sessionService;\n    private final TransactionService transactionService;\n    private final ClassLoaderService classLoaderService;\n    private final List<TenantLifecycleService> services;\n    private final TenantElementsRestarter tenantElementsRestarter;\n    private TenantServiceState tenantServiceState = TenantServiceState.STOPPED;\n\n    public TenantServicesManager(SessionAccessor sessionAccessor, SessionService sessionService,\n            TransactionService transactionService, ClassLoaderService classLoaderService,\n            List<TenantLifecycleService> services, TenantElementsRestarter tenantElementsRestarter) {\n        this.sessionAccessor = sessionAccessor;\n        this.sessionService = sessionService;\n        this.transactionService = transactionService;\n        this.classLoaderService = classLoaderService;\n        this.services = services;\n        this.tenantElementsRestarter = tenantElementsRestarter;\n    }\n\n    public boolean isStarted() {\n        return tenantServiceState == TenantServiceState.STARTED;\n    }\n\n    private void updateState(TenantServiceState tenantServiceState) {\n        LOGGER.debug(\"Services state updated to {}\", tenantServiceState);\n        this.tenantServiceState = tenantServiceState;\n    }\n\n    public void start() throws Exception {\n        doStart(START);\n    }\n\n    public void resume() throws Exception {\n        doStart(RESUME);\n    }\n\n    public void stop() throws Exception {\n        // stop the services:\n        doStop(STOP);\n    }\n\n    public void pause() throws Exception {\n        doStop(PAUSE);\n    }\n\n    public void initServices() throws Exception {\n        inSessionTransaction(() -> {\n            for (TenantLifecycleService tenantService : services) {\n                LOGGER.info(\"Initializing service {}\", tenantService.getClass().getName());\n                tenantService.init();\n            }\n            return null;\n        });\n    }\n\n    private void doStart(ServiceAction startAction) throws Exception {\n        LOGGER.debug(\"Starting services\");\n        if (tenantServiceState != TenantServiceState.STOPPED) {\n            LOGGER.debug(\"Services cannot be started, they are {}\", tenantServiceState);\n            return;\n        }\n        updateState(TenantServiceState.STARTING);\n        try {\n            inSession(() -> {\n                tenantElementsRestarter.prepareRestartOfElements();\n                transactionService.executeInTransaction((Callable<Void>) () -> {\n                    executeInClassloader(() -> startServices(startAction));\n                    return null;\n                });\n            });\n        } catch (Exception e) {\n            abortStart(startAction, e);\n            throw new SLifecycleException(\n                    \"Unable to \" + startAction + \" a service. All services are kept STOPPED. Error: \" + e.getMessage(),\n                    e);\n        }\n        updateState(TenantServiceState.STARTED);\n        inSession(tenantElementsRestarter::restartElements);\n        LOGGER.debug(\"Services are started.\");\n    }\n\n    private void startServices(ServiceAction startAction) throws SLifecycleException {\n\n        for (TenantLifecycleService tenantService : services) {\n            try {\n                LOGGER.info(\"{} service {}\", startAction, tenantService.getClass().getName());\n                if (startAction == RESUME) {\n                    tenantService.resume();\n                } else {\n                    tenantService.start();\n                }\n            } catch (Exception e) {\n                LOGGER.error(\"Error while executing the {} of the service {}\", startAction,\n                        tenantService.getClass().getName());\n                throw new SLifecycleException(\n                        format(\"Error while executing the {0} of the service {1}: {2}\", startAction,\n                                transactionService.getClass().getName(), e.getMessage()),\n                        e);\n            }\n        }\n    }\n\n    private void abortStart(ServiceAction startAction, Exception e) {\n        updateState(TenantServiceState.ABORTING_START);\n        ServiceAction stopAction = startAction == START ? STOP : PAUSE;\n        try {\n            LOGGER.info(\"Stopping services after a failed {}...\", startAction);\n            doStop(stopAction);\n        } catch (Exception exceptionOnStop) {\n            LOGGER.warn(\"Unable to {} services to recover from exception when executing {} because {}: {}\",\n                    stopAction, startAction, e.getClass().getName(), e.getMessage());\n            LOGGER.debug(\"Caused by: \", exceptionOnStop);\n        }\n    }\n\n    private void doStop(ServiceAction stopAction) throws Exception {\n        LOGGER.debug(\"Stopping services\");\n        if (tenantServiceState != TenantServiceState.STARTED\n                && tenantServiceState != TenantServiceState.ABORTING_START) {\n            LOGGER.debug(\"Services cannot be stopped, they are {}\", tenantServiceState);\n            return;\n        }\n        updateState(TenantServiceState.STOPPING);\n        List<TenantLifecycleService> list = new ArrayList<>(services);\n        Collections.reverse(list);\n        Optional<Exception> firstIssue = transactionService\n                .executeInTransaction(\n                        () -> list.stream()\n                                .map(tenantService -> {\n                                    LOGGER.info(\"{} service {}\", stopAction, tenantService.getClass().getName());\n                                    try {\n                                        if (stopAction == PAUSE) {\n                                            tenantService.pause();\n                                        } else {\n                                            tenantService.stop();\n                                        }\n                                    } catch (final Exception e) {\n                                        LOGGER.error(\"Error executing the {} of the service {} because: {} {}\",\n                                                stopAction, tenantService.getClass().getName(), e.getClass().getName(),\n                                                e.getMessage());\n                                        LOGGER.debug(\"Cause\", e);\n                                        return e;\n                                    }\n                                    return null;\n                                }).filter(Objects::nonNull).findFirst());\n        updateState(TenantServiceState.STOPPED);\n        LOGGER.debug(\"Services are stopped.\");\n        if (firstIssue.isPresent()) {\n            throw new SLifecycleException(\"Unable to stop some services\", firstIssue.get());\n        }\n    }\n\n    private void executeInClassloader(RunnableWithException runnable) throws Exception {\n        final ClassLoader baseClassLoader = Thread.currentThread().getContextClassLoader();\n        try {\n            // Set the right classloader only on start and resume because we destroy it on stop and pause anyway\n            final ClassLoader serverClassLoader = classLoaderService.getClassLoader(\n                    ClassLoaderIdentifier.TENANT);\n            Thread.currentThread().setContextClassLoader(serverClassLoader);\n            runnable.run();\n        } finally {\n            // reset previous class loader:\n            Thread.currentThread().setContextClassLoader(baseClassLoader);\n        }\n    }\n\n    protected Long createSession(final SessionService sessionService) throws SBonitaException {\n        return sessionService.createSession(SessionService.SYSTEM).getId();\n    }\n\n    private void inSession(RunnableWithException runnable) throws Exception {\n        long currentSessionId;\n        try {\n            currentSessionId = sessionAccessor.getSessionId();\n        } catch (SessionIdNotSetException e) {\n            runnable.run();\n            return;\n        }\n        try {\n            final long sessionId = createSession(sessionService);\n            sessionAccessor.deleteSessionId();\n            sessionAccessor.setSessionId(sessionId);\n            runnable.run();\n            sessionService.deleteSession(sessionId);\n        } finally {\n            sessionAccessor.setSessionId(currentSessionId);\n        }\n    }\n\n    public <T> void inSessionTransaction(final Callable<T> callable) throws Exception {\n        inSession(() -> transactionService.executeInTransaction(callable));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/tenant/TenantStateManager.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tenant;\n\nimport static org.bonitasoft.engine.tenant.TenantServicesManager.ServiceAction.PAUSE;\nimport static org.bonitasoft.engine.tenant.TenantServicesManager.ServiceAction.RESUME;\n\nimport java.io.IOException;\nimport java.util.Map;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.TimeoutException;\n\nimport org.bonitasoft.engine.exception.BonitaHomeConfigurationException;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.platform.PlatformService;\nimport org.bonitasoft.engine.platform.configuration.NodeConfiguration;\nimport org.bonitasoft.engine.platform.model.SPlatform;\nimport org.bonitasoft.engine.scheduler.SchedulerService;\nimport org.bonitasoft.engine.service.BroadcastService;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.TaskResult;\nimport org.bonitasoft.engine.service.impl.ServiceAccessorFactory;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.tenant.TenantServicesManager.ServiceAction;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class TenantStateManager {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(TenantStateManager.class);\n\n    private final UserTransactionService transactionService;\n    private final PlatformService platformService;\n    private final NodeConfiguration nodeConfiguration;\n    private final SessionService sessionService;\n    private final SchedulerService schedulerService;\n    private final BroadcastService broadcastService;\n    private final TenantServicesManager tenantServicesManager;\n\n    public TenantStateManager(UserTransactionService transactionService, PlatformService platformService,\n            NodeConfiguration nodeConfiguration, SessionService sessionService,\n            SchedulerService schedulerService, BroadcastService broadcastService,\n            TenantServicesManager tenantServicesManager) {\n        this.transactionService = transactionService;\n        this.platformService = platformService;\n        this.nodeConfiguration = nodeConfiguration;\n        this.sessionService = sessionService;\n        this.schedulerService = schedulerService;\n        this.broadcastService = broadcastService;\n        this.tenantServicesManager = tenantServicesManager;\n    }\n\n    /**\n     * Stop the platform:\n     * - stop services\n     * - delete session if its the only node\n     * **Called outside of a transaction in a platform-level session**\n     */\n    public synchronized void stop() throws Exception {\n        if (nodeConfiguration.shouldClearSessions()) {\n            sessionService.deleteSessions();\n        }\n        tenantServicesManager.stop();\n    }\n\n    /**\n     * Start the platform:\n     * - start services\n     * - resume elements if its the only node\n     * **Called outside of a transaction in a platform-level session**\n     */\n    public synchronized void start() throws Exception {\n        tenantServicesManager.initServices();\n        final SPlatform platform = getPlatformInTransaction();\n        if (platform.isMaintenanceEnabled()) {\n            LOGGER.debug(\"Not starting platform. It is {}\", platform.getPausedStatus());\n            return;\n        }\n        tenantServicesManager.start();\n    }\n\n    protected ServiceAccessor getServiceAccessor()\n            throws BonitaHomeNotSetException, IOException, BonitaHomeConfigurationException,\n            ReflectiveOperationException {\n        return ServiceAccessorFactory.getInstance().createServiceAccessor();\n    }\n\n    private SPlatform getPlatformInTransaction() throws Exception {\n        return transactionService.executeInTransaction(platformService::getPlatform);\n    }\n\n    /**\n     * Pause the platform:\n     * - pause services\n     * - platform has the status PAUSED in database\n     * - other nodes pause the services\n     * **Called outside a transaction with a platform-level session**\n     */\n    public synchronized void pause() throws Exception {\n        LOGGER.info(\"Pausing platform\");\n        SPlatform platform = getPlatformInTransaction();\n        if (platform.isMaintenanceEnabled()) {\n            throw new UpdateException(\"Can't pause platform in state \" + platform.getPausedStatus());\n        }\n        pauseServicesInTransaction();\n        pauseSchedulerJobsInTransaction();\n        tenantServicesManager.pause();\n        pauseServicesOnOtherNodes();\n        LOGGER.info(\"Paused platform\");\n    }\n\n    /**\n     * Resume the platform:\n     * - resume services\n     * - platform has the status ACTIVATED in database\n     * - other nodes resume the services\n     * **Called outside a transaction with a platform-level session**\n     */\n    public synchronized void resume() throws Exception {\n        LOGGER.info(\"Resuming platform\");\n        SPlatform platform = getPlatformInTransaction();\n        if (!platform.isMaintenanceEnabled()) {\n            throw new UpdateException(\"Can't resume platform in state \" + platform.getPausedStatus());\n        }\n        resumeServicesInTransaction();\n        try {\n            tenantServicesManager.resume();\n        } catch (Exception e) {\n            pauseServicesInTransaction();\n            throw e;\n        }\n        resumeServicesOnOtherNodes();\n        resumeSchedulerJobsInTransaction();\n\n        LOGGER.info(\"Resumed platform\");\n    }\n\n    private void resumeSchedulerJobsInTransaction() throws Exception {\n        transactionService.executeInTransaction(() -> {\n            schedulerService.resumeJobs();\n            return null;\n        });\n    }\n\n    private void pauseSchedulerJobsInTransaction() throws Exception {\n        transactionService.executeInTransaction(() -> {\n            schedulerService.pauseJobs();\n            return null;\n        });\n    }\n\n    private void pauseServicesInTransaction() throws Exception {\n        transactionService.executeInTransaction(() -> {\n            platformService.pauseServices();\n            return null;\n        });\n    }\n\n    private void resumeServicesInTransaction() throws Exception {\n        transactionService.executeInTransaction(() -> {\n            platformService.resumeServices();\n            return null;\n        });\n    }\n\n    private void pauseServicesOnOtherNodes() {\n        executeOnOtherNodes(PAUSE);\n    }\n\n    private void resumeServicesOnOtherNodes() {\n        executeOnOtherNodes(RESUME);\n    }\n\n    private void executeOnOtherNodes(ServiceAction action) {\n        Map<String, TaskResult<Void>> execute;\n        try {\n            execute = broadcastService.executeOnOthersAndWait(new ChangesServicesStateCallable(action));\n        } catch (InterruptedException | ExecutionException | TimeoutException e) {\n            throw new IllegalStateException(\"Unable to update services on other nodes\", e);\n        }\n        for (Map.Entry<String, TaskResult<Void>> resultEntry : execute.entrySet()) {\n            if (resultEntry.getValue().isError()) {\n                throw new IllegalStateException(resultEntry.getValue().getThrowable());\n            }\n        }\n    }\n\n    public synchronized <T> T executeManagementOperation(String operationName, Callable<T> operation)\n            throws Exception {\n        LOGGER.info(\"Executing synchronized maintenance operation {}\", operationName);\n        T operationReturn = operation.call();\n        LOGGER.info(\"Successful synchronized maintenance operation {}\", operationName);\n        return operationReturn;\n    }\n\n    public synchronized boolean isPaused() throws Exception {\n        return getPlatformInTransaction().isMaintenanceEnabled();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/tenant/restart/ElementToRecover.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tenant.restart;\n\nimport lombok.Builder;\nimport lombok.Data;\n\n@Data\n@Builder\npublic class ElementToRecover {\n\n    enum Type {\n        PROCESS, FLOWNODE\n    }\n\n    private Type type;\n    private Long id;\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/tenant/restart/FlowNodesRecover.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tenant.restart;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.api.utils.VisibleForTesting;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SGatewayInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.execution.state.FlowNodeStateManager;\nimport org.bonitasoft.engine.execution.work.BPMWorkFactory;\nimport org.bonitasoft.engine.work.WorkService;\nimport org.springframework.stereotype.Component;\n\n/**\n * Recover flow nodes\n * <p>\n * This class is called after the start of the engine or periodically and will recover elements given their ids.\n */\n@Slf4j\n@Component\npublic class FlowNodesRecover {\n\n    private final WorkService workService;\n    private final BPMWorkFactory workFactory;\n    private final ActivityInstanceService activityInstanceService;\n    private final FlowNodeStateManager flowNodeStateManager;\n\n    public FlowNodesRecover(WorkService workService,\n            ActivityInstanceService activityInstanceService,\n            FlowNodeStateManager flowNodeStateManager, BPMWorkFactory workFactory) {\n        this.workService = workService;\n        this.workFactory = workFactory;\n        this.activityInstanceService = activityInstanceService;\n        this.flowNodeStateManager = flowNodeStateManager;\n    }\n\n    void execute(RecoveryMonitor recoveryMonitor, List<Long> flowNodeIds) throws SBonitaException {\n        List<Long> unprocessed = new ArrayList<>(flowNodeIds);\n        List<SFlowNodeInstance> flowNodeInstances = activityInstanceService.getFlowNodeInstancesByIds(flowNodeIds);\n        for (SFlowNodeInstance flowNodeInstance : flowNodeInstances) {\n            unprocessed.remove(flowNodeInstance.getId());\n            if (flowNodeInstance.isTerminal()) {\n                recoveryMonitor.incrementFinishing();\n                log.debug(\"Restarting flow node (Notify finished...) with name = <\" + flowNodeInstance.getName()\n                        + \">, and id = <\" + flowNodeInstance.getId()\n                        + \" in state = <\" + flowNodeInstance.getStateName() + \">\");\n                workService.registerWork(workFactory.createNotifyChildFinishedWorkDescriptor(flowNodeInstance));\n            } else {\n                if (shouldBeRecovered(flowNodeInstance)) {\n                    recoveryMonitor.incrementExecuting();\n                    log.debug(\"Recovering flow node (Execute ...) with name = <\" + flowNodeInstance.getName()\n                            + \">, and id = <\" + flowNodeInstance.getId()\n                            + \"> in state = <\" + flowNodeInstance.getStateName() + \">\");\n                    workService.registerWork(workFactory.createExecuteFlowNodeWorkDescriptor(flowNodeInstance));\n                } else {\n                    recoveryMonitor.incrementNotExecutable();\n                    log.debug(\n                            \"Flownode with name = <{}>, and id = <{}> in state = <{}> does not fulfill the recovered conditions.\",\n                            flowNodeInstance.getName(), flowNodeInstance.getId(), flowNodeInstance.getStateName());\n                }\n            }\n        }\n        recoveryMonitor.incrementNotFound(unprocessed.size());\n    }\n\n    @VisibleForTesting\n    boolean shouldBeRecovered(final SFlowNodeInstance sFlowNodeInstance) {\n        //when state category is cancelling but the state is 'stable' (e.g. boundary event in waiting but that has been cancelled)\n        if ((sFlowNodeInstance.getStateCategory().equals(SStateCategory.CANCELLING)\n                || sFlowNodeInstance.getStateCategory().equals(SStateCategory.ABORTING))\n                && !sFlowNodeInstance.isTerminal()\n                && sFlowNodeInstance.isStable()) {\n            FlowNodeState state = flowNodeStateManager.getState(sFlowNodeInstance.getStateId());\n            //in this case we restart it only if the state is not in this cancelling of aborting category\n            //this can happen when we abort a process with a call activity:\n            //the call activity is put in aborting state and wait for its children to finish, in that case we do not call execute on it\n            return !state.getStateCategory().equals(sFlowNodeInstance.getStateCategory());\n        }\n        // Gateway is a special case :\n        //   Initial state is not stable but it should probably be stable because it waits for other flowNode to be completed.\n        if (SFlowNodeType.GATEWAY.equals(sFlowNodeInstance.getType())) {\n            return sFlowNodeInstance.isAborting() || sFlowNodeInstance.isCanceling()\n                    || ((SGatewayInstance) sFlowNodeInstance).isFinished();\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/tenant/restart/MessagesRestartHandler.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tenant.restart;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceRepository;\nimport org.bonitasoft.engine.execution.work.ExecuteMessageCoupleWork;\nimport org.bonitasoft.engine.execution.work.RestartException;\nimport org.bonitasoft.engine.message.MessagesHandlingService;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.springframework.stereotype.Component;\n\n/**\n * Resets all \"In Progress\" BPMN Message couples so that they can be triggered again on next cron.\n * Restart work {@link ExecuteMessageCoupleWork}\n *\n * @author Emmanuel Duchastenier\n */\n@Slf4j\n@Component\npublic class MessagesRestartHandler implements TenantRestartHandler {\n\n    private EventInstanceRepository eventInstanceRepository;\n    private UserTransactionService userTransactionService;\n    private MessagesHandlingService messagesHandlingService;\n\n    public MessagesRestartHandler(\n            EventInstanceRepository eventInstanceRepository, UserTransactionService userTransactionService,\n            MessagesHandlingService messagesHandlingService) {\n        this.eventInstanceRepository = eventInstanceRepository;\n        this.userTransactionService = userTransactionService;\n        this.messagesHandlingService = messagesHandlingService;\n    }\n\n    @Override\n    public void beforeServicesStart()\n            throws RestartException {\n\n        try {\n            // Reset of all SMessageInstance:\n            log.info(\n                    \"Reinitializing message instances in non-stable state to make them reworked by MessagesHandlingService\");\n            final int nbMessagesReset = eventInstanceRepository.resetProgressMessageInstances();\n            log.info(nbMessagesReset + \" message instances found and reset.\");\n\n            // Reset of all SWaitingMessageEvent:\n            log.info(\n                    \"Reinitializing waiting message events in non-stable state to make them reworked by MessagesHandlingService\");\n            final int nbWaitingEventsReset = eventInstanceRepository.resetInProgressWaitingEvents();\n            log.info(nbWaitingEventsReset + \" waiting message events found and reset.\");\n\n        } catch (final SBonitaException e) {\n            throw new RestartException(\n                    \"Unable to reset MessageInstances / WaitingMessageEvents that were 'In Progress' when the node stopped\",\n                    e);\n        }\n    }\n\n    @Override\n    public void afterServicesStart() {\n        try {\n            userTransactionService.executeInTransaction(() -> {\n                messagesHandlingService.triggerMatchingOfMessages();\n                return null;\n            });\n        } catch (Exception e) {\n            log.error(\n                    \"Unable to register work to handle message events on startup, work will be triggered on next message event update\",\n                    e);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/tenant/restart/ProcessesRecover.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tenant.restart;\n\nimport java.util.List;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.execution.ProcessExecutor;\nimport org.bonitasoft.engine.execution.work.BPMWorkFactory;\nimport org.bonitasoft.engine.work.WorkService;\nimport org.springframework.stereotype.Component;\n\n/**\n * This class handles the continuation of unfinished process instances\n * passed as parameter in {@link #execute(RecoveryMonitor, List)} method.\n * The logic ensures that if an instance fails to continue its execution,\n * the error is logged and other instances are continued anyways.\n */\n@Slf4j\n@Component\npublic class ProcessesRecover {\n\n    private final WorkService workService;\n    private final ActivityInstanceService activityInstanceService;\n    private final ProcessDefinitionService processDefinitionService;\n    private final ProcessInstanceService processInstanceService;\n    private final ProcessExecutor processExecutor;\n    private final BPMWorkFactory workFactory;\n\n    public ProcessesRecover(WorkService workService,\n            ActivityInstanceService activityInstanceService,\n            ProcessDefinitionService processDefinitionService,\n            ProcessInstanceService processInstanceService,\n            ProcessExecutor processExecutor,\n            BPMWorkFactory workFactory) {\n        this.workService = workService;\n        this.activityInstanceService = activityInstanceService;\n        this.processDefinitionService = processDefinitionService;\n        this.processInstanceService = processInstanceService;\n        this.processExecutor = processExecutor;\n        this.workFactory = workFactory;\n    }\n\n    void execute(RecoveryMonitor recoveryMonitor, List<Long> ids) {\n        for (Long processId : ids) {\n            try {\n                final SProcessInstance processInstance = processInstanceService.getProcessInstance(processId);\n                final SProcessDefinition processDefinition = processDefinitionService\n                        .getProcessDefinition(processInstance.getProcessDefinitionId());\n                switch (ProcessInstanceState.getFromId(processInstance.getStateId())) {\n                    case ABORTED:\n                    case CANCELLED:\n                    case COMPLETED:\n                        recoveryMonitor.incrementFinishing();\n                        handleCompletion(processInstance);\n                        break;\n                    case COMPLETING:\n                        recoveryMonitor.incrementFinishing();\n                        processExecutor.registerConnectorsToExecute(processDefinition, processInstance,\n                                ConnectorEvent.ON_FINISH,\n                                null);\n                        break;\n                    case INITIALIZING:\n                        recoveryMonitor.incrementExecuting();\n                        processExecutor.registerConnectorsToExecute(processDefinition, processInstance,\n                                ConnectorEvent.ON_ENTER,\n                                null);\n                        break;\n                    default:\n                        recoveryMonitor.incrementNotExecutable();\n                        break;\n                }\n            } catch (final SProcessInstanceNotFoundException e) {\n                recoveryMonitor.incrementNotFound();\n                log.debug(\"Unable to recover the process instance {}, it is not found (probably already completed).\",\n                        processId);\n            } catch (final Exception e) {\n                recoveryMonitor.incrementInError();\n                log.warn(\n                        \"Unable to recover the process instance {}, it will be retry in next recovery. Because : {} \",\n                        processId, e.getMessage());\n                log.debug(\"Cause\", e);\n            }\n        }\n    }\n\n    private void handleCompletion(final SProcessInstance processInstance)\n            throws SBonitaException {\n        // Only Error events set interruptedByEvent on SProcessInstance:\n        if (!processInstance.hasBeenInterruptedByEvent()) {\n\n            final long callerId = processInstance.getCallerId();\n            // Should always be in a CallActivity:\n            if (callerId > 0) {\n                final SActivityInstance callActivityInstance = activityInstanceService\n                        .getActivityInstance(processInstance.getCallerId());\n                if (callActivityInstance.getStateId() != FlowNodeState.ID_ACTIVITY_FAILED) {\n                    workService.registerWork(workFactory.createExecuteFlowNodeWorkDescriptor(callActivityInstance));\n                    log.info(\"Restarting notification of finished process '{}' with id {} in state {}\",\n                            processInstance.getName(), processInstance.getId(),\n                            ProcessInstanceState.getFromId(processInstance.getStateId()));\n                }\n            }\n            // No need to handle completion of process instance in state COMPLETED here,\n            // as it can never happen, because when a process instance goes into COMPLETED state, it is archived\n            // directly in the same transaction (in ArchiveProcessInstanceHandler)\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/tenant/restart/RecoveryHandler.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tenant.restart;\n\nimport java.time.Duration;\nimport java.util.List;\n\nimport org.springframework.stereotype.Component;\n\n/**\n * The Recovery handler is responsible for recovering all candidate elements\n * at Engine startup.\n * It is called only once in a cluster startup (handled by TenantElementsRestartSupervisor)\n */\n@Component\npublic class RecoveryHandler implements TenantRestartHandler {\n\n    private final RecoveryService recoveryService;\n    private List<ElementToRecover> allElementsToRecover;\n\n    public RecoveryHandler(RecoveryService recoveryService) {\n        this.recoveryService = recoveryService;\n    }\n\n    @Override\n    public void beforeServicesStart() {\n        allElementsToRecover = recoveryService.getAllElementsToRecover(Duration.ZERO);\n    }\n\n    @Override\n    public void afterServicesStart() {\n        recoveryService.recover(allElementsToRecover);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/tenant/restart/RecoveryMonitor.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tenant.restart;\n\nimport static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;\n\nimport java.time.Duration;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.stereotype.Component;\n\n/**\n * Responsible for monitoring the recovery mechanism.\n * It measures some recovery metrics and prints them in standard logger when the recovery triggers.\n */\n@Slf4j\n@Component\n@Scope(SCOPE_PROTOTYPE)\nclass RecoveryMonitor {\n\n    private long finishing;\n    private long executing;\n    private long notExecutable;\n    private long notFound;\n    private long inError;\n    private long startTime;\n    private int numberOfElementsToProcess;\n\n    public void startNow(int numberOfElementsToProcess) {\n        if (startTime > 0) {\n            throw new UnsupportedOperationException(\"Can't start the Recovery Monitor, it is already started\");\n        }\n        this.numberOfElementsToProcess = numberOfElementsToProcess;\n        startTime = System.currentTimeMillis();\n    }\n\n    public long getFinishing() {\n        return finishing;\n    }\n\n    public long getExecuting() {\n        return executing;\n    }\n\n    public long getNumberOfElementRecovered() {\n        return executing + finishing;\n    }\n\n    public long getNotExecutable() {\n        return notExecutable;\n    }\n\n    public long getNotFound() {\n        return notFound;\n    }\n\n    public long getInError() {\n        return inError;\n    }\n\n    public void incrementFinishing() {\n        this.finishing++;\n    }\n\n    public void incrementExecuting() {\n        this.executing++;\n    }\n\n    public void incrementNotExecutable() {\n        this.notExecutable++;\n    }\n\n    public void incrementInError() {\n        this.inError++;\n    }\n\n    public void incrementNotFound() {\n        this.notFound++;\n    }\n\n    public void incrementNotFound(int add) {\n        this.notFound += add;\n    }\n\n    public void printProgress() {\n        //This will be called only when more than one \"page\" of element to restart are present\n        log.info(\"Restarting elements...Handled \"\n                + (getFinishing() + getExecuting() + getNotExecutable() + getNotFound() + getInError()) + \" of \"\n                + numberOfElementsToProcess +\n                \" elements candidates to be recovered in \" + Duration.ofMillis(System.currentTimeMillis() - startTime));\n    }\n\n    public void printSummary() {\n        // only print a single status line for that\n        long numberOfElementRecovered = getNumberOfElementRecovered();\n        if (numberOfElementRecovered == 0) {\n            log.info(\"Recovery of elements executed. Nothing detected that needs recovery.\");\n        } else {\n            log.info(\"Recovery of elements executed, {} elements recovered.\", numberOfElementRecovered);\n        }\n        // details in debug\n        log.debug(\"Handled {} elements candidates to be recovered in {}\",\n                (getFinishing() + getExecuting() + getNotExecutable() + getNotFound() + getInError()),\n                Duration.ofMillis(System.currentTimeMillis() - startTime));\n        log.debug(\"Found {} elements recovered (Executing)\", getExecuting());\n        log.debug(\"Found {} elements recovered (Finishing)\", getFinishing());\n        log.debug(\"Found {} elements that were not executable (e.g. unmerged gateway)\", getNotExecutable());\n        log.debug(getNotFound() + \" elements were not found (might have been manually executed)\");\n        log.debug(\"Found {} elements in error (see stacktrace for reason)\", getInError());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/tenant/restart/RecoveryScheduler.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tenant.restart;\n\nimport static org.bonitasoft.engine.tenant.SingleNodeTaskCoordinator.TASK_RECOVERY;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.tenant.SingleNodeTaskCoordinator;\nimport org.springframework.scheduling.annotation.Scheduled;\nimport org.springframework.stereotype.Component;\n\n/**\n * The RecoveryScheduler is responsible to trigger the recovery mechanism periodically.\n * The scheduling values can be changed using engine properties files.\n */\n@Component\n@Slf4j\npublic class RecoveryScheduler {\n\n    private final SingleNodeTaskCoordinator singleNodeTaskCoordinator;\n    private final RecoveryService recoveryService;\n\n    RecoveryScheduler(SingleNodeTaskCoordinator singleNodeTaskCoordinator,\n            RecoveryService recoveryService) {\n        this.singleNodeTaskCoordinator = singleNodeTaskCoordinator;\n        this.recoveryService = recoveryService;\n    }\n\n    @Scheduled(fixedDelayString = \"${bonita.tenant.recover.delay_between_recovery:PT2H}\", initialDelayString = \"${bonita.tenant.recover.delay_between_recovery:PT2H}\")\n    public void triggerRecoveryOfAllElements() {\n        try {\n            if (singleNodeTaskCoordinator.isResponsibleForTask(TASK_RECOVERY)) {\n                log.debug(\"Starting periodic recovery of elements...\");\n                recoveryService.recoverAllElements();\n                log.debug(\"Completed periodic recovery of elements.\");\n            } else {\n                log.debug(\"Periodic recovery of elements not executed, an other node is responsible for it.\");\n            }\n        } catch (Exception e) {\n            log.warn(\"Recovery of elements failed because of {} - {}, it will be re-executed soon\",\n                    e.getClass().getName(), e.getMessage());\n            log.debug(\"Cause by \", e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/tenant/restart/RecoveryService.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tenant.restart;\n\nimport static org.bonitasoft.engine.commons.CollectionUtil.split;\nimport static org.bonitasoft.engine.tenant.restart.ElementToRecover.Type.FLOWNODE;\nimport static org.bonitasoft.engine.tenant.restart.ElementToRecover.Type.PROCESS;\n\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicLong;\nimport java.util.stream.Collectors;\n\nimport javax.annotation.PostConstruct;\n\nimport io.micrometer.core.instrument.Counter;\nimport io.micrometer.core.instrument.Gauge;\nimport io.micrometer.core.instrument.LongTaskTimer;\nimport io.micrometer.core.instrument.MeterRegistry;\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.api.utils.VisibleForTesting;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.springframework.beans.factory.ObjectFactory;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.stereotype.Component;\n\n/**\n * Responsible to recover from incidents like database or network outage.\n * It scans the database (on-demand) and reschedules the elements to recover.\n * It will recover these elements using multiple transaction using a batch size configured by the property\n * `bonita.tenant.work.batch_restart_size`\n */\n@Component\n@Slf4j\npublic class RecoveryService {\n\n    public static final String DURATION_OF_RECOVERY_TASK = \"bonita.bpmengine.recovery.duration\";\n    public static final String NUMBER_OF_RECOVERY = \"bonita.bpmengine.recovery.execution\";\n    public static final String NUMBER_OF_ELEMENTS_RECOVERED_LAST_RECOVERY = \"bonita.bpmengine.recovery.recovered.last\";\n    public static final String NUMBER_OF_ELEMENTS_RECOVERED_TOTAL = \"bonita.bpmengine.recovery.recovered.total\";\n\n    private final FlowNodeInstanceService flowNodeInstanceService;\n    private final ProcessInstanceService processInstanceService;\n    private final UserTransactionService userTransactionService;\n    private final FlowNodesRecover flowNodesRecover;\n    private final ProcessesRecover processesRecover;\n    private final ObjectFactory<RecoveryMonitor> recoveryMonitorProvider;\n    private final MeterRegistry meterRegistry;\n    private int readBatchSize;\n    private int batchRestartSize;\n    private Duration considerElementsOlderThan;\n    private LongTaskTimer longTaskTimer;\n    private Counter numberOfElementsRecoveredTotal;\n    private Counter numberOfRecoverExecuted;\n    private final AtomicLong numberOfElementsRecoveredDuringTheLastRecover = new AtomicLong();\n\n    public RecoveryService(FlowNodeInstanceService flowNodeInstanceService,\n            ProcessInstanceService processInstanceService,\n            UserTransactionService userTransactionService,\n            FlowNodesRecover flowNodesRecover,\n            ProcessesRecover processesRecover,\n            ObjectFactory<RecoveryMonitor> recoveryMonitorProvider,\n            MeterRegistry meterRegistry) {\n        this.flowNodeInstanceService = flowNodeInstanceService;\n        this.processInstanceService = processInstanceService;\n        this.userTransactionService = userTransactionService;\n        this.flowNodesRecover = flowNodesRecover;\n        this.processesRecover = processesRecover;\n        this.recoveryMonitorProvider = recoveryMonitorProvider;\n        this.meterRegistry = meterRegistry;\n    }\n\n    @PostConstruct\n    protected void initMetrics() {\n        this.longTaskTimer = LongTaskTimer\n                .builder(DURATION_OF_RECOVERY_TASK)\n                .description(\"duration of recovery task\")\n                .register(meterRegistry);\n        Gauge.builder(NUMBER_OF_ELEMENTS_RECOVERED_LAST_RECOVERY, numberOfElementsRecoveredDuringTheLastRecover,\n                AtomicLong::doubleValue)\n                .description(\"number of elements recovered\").baseUnit(\"elements\")\n                .register(meterRegistry);\n        numberOfElementsRecoveredTotal = Counter.builder(NUMBER_OF_ELEMENTS_RECOVERED_TOTAL)\n                .baseUnit(\"elements\").description(\"Total number of elements recovered\")\n                .register(meterRegistry);\n        numberOfRecoverExecuted = Counter.builder(NUMBER_OF_RECOVERY)\n                .baseUnit(\"executions\").description(\"Number of recovery executed\")\n                .register(meterRegistry);\n    }\n\n    @Value(\"${bonita.tenant.recover.read_batch_size:5000}\")\n    public void setReadBatchSize(int readBatchSize) {\n        this.readBatchSize = readBatchSize;\n    }\n\n    @Value(\"${bonita.tenant.recover.consider_elements_older_than:PT1H}\")\n    public void setConsiderElementsOlderThan(String considerElementsOlderThan) {\n        setConsiderElementsOlderThan(Duration.parse(considerElementsOlderThan));\n    }\n\n    @Value(\"${bonita.tenant.work.batch_restart_size:1000}\")\n    public void setBatchRestartSize(int batchRestartSize) {\n        this.batchRestartSize = batchRestartSize;\n    }\n\n    @VisibleForTesting\n    void setConsiderElementsOlderThan(Duration considerElementsOlderThan) {\n        this.considerElementsOlderThan = considerElementsOlderThan;\n    }\n\n    /**\n     * Retrieve elements ( ProcessInstance and Flow Nodes ) that needs to be recovered and that are older than the given\n     * duration.\n     *\n     * @param considerElementsOlderThan consider elements older than that duration\n     * @return elements to be recovered\n     */\n    public List<ElementToRecover> getAllElementsToRecover(Duration considerElementsOlderThan) {\n        List<ElementToRecover> elementsToRecover = new ArrayList<>();\n        try {\n            elementsToRecover.addAll(getAllElementsToRecover(PROCESS,\n                    (q) -> processInstanceService.getProcessInstanceIdsToRecover(considerElementsOlderThan, q)));\n            elementsToRecover.addAll(getAllElementsToRecover(FLOWNODE,\n                    (q) -> flowNodeInstanceService.getFlowNodeInstanceIdsToRecover(considerElementsOlderThan, q)));\n            elementsToRecover.addAll(getAllElementsToRecover(FLOWNODE,\n                    (q) -> flowNodeInstanceService.getGatewayInstanceIdsToRecover(considerElementsOlderThan, q)));\n            return elementsToRecover;\n        } catch (SBonitaException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    /**\n     * Trigger works to execute elements ( ProcessInstance and Flow Nodes ) that needs to be recovered\n     *\n     * @param elementsToRecover elements needs to be recovered\n     */\n    public void recover(List<ElementToRecover> elementsToRecover) {\n        RecoveryMonitor recoveryMonitor = recoveryMonitorProvider.getObject();\n        recoveryMonitor.startNow(elementsToRecover.size());\n        executeInBatch(recoveryMonitor, elementsToRecover.stream()\n                .filter(e1 -> e1.getType() == FLOWNODE)\n                .collect(Collectors.toList()), ids -> flowNodesRecover.execute(recoveryMonitor, ids));\n        executeInBatch(recoveryMonitor, elementsToRecover.stream()\n                .filter(e -> e.getType() == PROCESS)\n                .collect(Collectors.toList()), ids -> processesRecover.execute(recoveryMonitor, ids));\n\n        recoveryMonitor.printSummary();\n        long numberOfElementRecovered = recoveryMonitor.getNumberOfElementRecovered();\n        numberOfElementsRecoveredTotal.increment(numberOfElementRecovered);\n        numberOfElementsRecoveredDuringTheLastRecover.set(numberOfElementRecovered);\n        numberOfRecoverExecuted.increment();\n    }\n\n    protected void executeInBatch(RecoveryMonitor recoveryMonitor, List<ElementToRecover> elements,\n            BatchExecution execution) {\n        for (List<ElementToRecover> batchElementsIds : split(elements, batchRestartSize)) {\n            try {\n                userTransactionService.executeInTransaction(() -> {\n                    execution.execute(\n                            batchElementsIds.stream().map(ElementToRecover::getId).collect(Collectors.toList()));\n                    return null;\n                });\n            } catch (Exception e) {\n                log.warn(\n                        \"Error processing batch of elements to recover, they will be recovered next time: {}, Cause: {}: {}\",\n                        batchElementsIds, e.getClass().getName(), e.getMessage());\n                log.debug(\"Cause\", e);\n            }\n            if (batchElementsIds.size() == batchRestartSize) {\n                // only print progress when there is more than one page\n                recoveryMonitor.printProgress();\n            }\n        }\n    }\n\n    /**\n     * Recover all elements considered as \"stuck\".\n     * Only recover elements older than a duration configured with {@link #setConsiderElementsOlderThan(String)}.\n     */\n    public void recoverAllElements() {\n        longTaskTimer.record(() -> {\n            try {\n                List<ElementToRecover> allElementsToRecover = userTransactionService.executeInTransaction(\n                        () -> RecoveryService.this.getAllElementsToRecover(considerElementsOlderThan));\n                log.debug(\"Found {} that can potentially be recovered\", allElementsToRecover.size());\n                recover(allElementsToRecover);\n            } catch (Exception e) {\n                throw new RuntimeException(e);\n            }\n        });\n    }\n\n    private List<ElementToRecover> getAllElementsToRecover(ElementToRecover.Type type, IdsRetriever idsRetriever)\n            throws SBonitaException {\n        // using a too low page size (100) causes too many access to the database and causes timeout exception if there are lot of elements.\n        // As we retrieve only the id we can use a greater page size\n        QueryOptions queryOptions = new QueryOptions(0, readBatchSize);\n        final List<Long> ids = new ArrayList<>();\n        List<Long> elementsIds;\n        log.debug(\"Start detecting {} to recover...\", type);\n        do {\n            elementsIds = idsRetriever.getIds(queryOptions);\n            queryOptions = QueryOptions.getNextPage(queryOptions);\n            ids.addAll(elementsIds);\n        } while (elementsIds.size() == queryOptions.getNumberOfResults());\n        log.debug(\"Found {} {} to recover\", elementsIds.size(), type);\n        return ids\n                .stream().map(id -> ElementToRecover.builder().id(id).type(type).build())\n                .collect(Collectors.toList());\n    }\n\n    private interface BatchExecution {\n\n        void execute(List<Long> ids) throws Exception;\n    }\n\n    private interface IdsRetriever {\n\n        List<Long> getIds(QueryOptions queryOptions) throws SBonitaException;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/tenant/restart/TenantRestartHandler.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tenant.restart;\n\nimport org.bonitasoft.engine.api.PlatformAPI;\nimport org.bonitasoft.engine.execution.work.RestartException;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface TenantRestartHandler {\n\n    /**\n     * Called in a transaction during {@link PlatformAPI#startNode()}.\n     */\n    void beforeServicesStart() throws RestartException;\n\n    /**\n     * Called outside a transaction after {@link PlatformAPI#startNode()} in a separate thread from the api call.\n     */\n    void afterServicesStart();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/userfilter/UserFilterServiceDecorator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.userfilter;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.APIAccessor;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.filter.FilterResult;\nimport org.bonitasoft.engine.core.filter.UserFilterService;\nimport org.bonitasoft.engine.core.filter.exception.SUserFilterExecutionException;\nimport org.bonitasoft.engine.core.filter.exception.SUserFilterLoadingException;\nimport org.bonitasoft.engine.core.process.definition.model.SUserFilterDefinition;\nimport org.bonitasoft.engine.expression.EngineConstantExpressionBuilder;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.SRecorderException;\n\n/**\n * This {@link UserFilterService} implementation injects, in method\n * {@link #executeFilter(long, SUserFilterDefinition, Map, ClassLoader)} a new expression to\n * access the {@link APIAccessor} for User filters.\n * This new expression is referenced under the name 'apiAccessor'.\n *\n * @author Emmanuel Duchastenier\n * @author Matthieu Chaffotte\n */\npublic class UserFilterServiceDecorator implements UserFilterService {\n\n    private final UserFilterService userFilterService;\n\n    /**\n     * @param userFilterService\n     *        the UserFilterService class that this class is decorating.\n     */\n    public UserFilterServiceDecorator(final UserFilterService userFilterService) {\n        super();\n        this.userFilterService = userFilterService;\n    }\n\n    /**\n     * {@inheritDoc}. This implementation injects a new expression to access the {@link APIAccessor} for User filters.\n     * This new expression is referenced under the name 'apiAccessor'.\n     */\n    @Override\n    public FilterResult executeFilter(final long processDefinitionId, final SUserFilterDefinition sUserFilterDefinition,\n            final Map<String, SExpression> inputs,\n            final ClassLoader classLoader, final SExpressionContext expressionContext, final String actorName)\n            throws SUserFilterExecutionException {\n        SExpression apiAccessorExpression;\n        SExpression engineExecutionContext;\n        apiAccessorExpression = EngineConstantExpressionBuilder.getConnectorAPIAccessorExpression();\n        engineExecutionContext = EngineConstantExpressionBuilder.getEngineExecutionContext();\n        final Map<String, SExpression> enrichedInputs = new HashMap<String, SExpression>(inputs);\n        enrichedInputs.put(\"connectorApiAccessor\", apiAccessorExpression);\n        enrichedInputs.put(\"engineExecutionContext\", engineExecutionContext);\n        return userFilterService.executeFilter(processDefinitionId, sUserFilterDefinition, enrichedInputs, classLoader,\n                expressionContext, actorName);\n    }\n\n    @Override\n    public boolean loadUserFilters(final long processDefinitionId) throws SUserFilterLoadingException {\n        return userFilterService.loadUserFilters(processDefinitionId);\n    }\n\n    @Override\n    public void removeUserFilters(long processDefinitionId) throws SBonitaReadException, SRecorderException {\n        userFilterService.removeUserFilters(processDefinitionId);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/resources/actorMapping.xsd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<xsd:schema xmlns=\"http://www.bonitasoft.org/ns/actormapping/6.0\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" targetNamespace=\"http://www.bonitasoft.org/ns/actormapping/6.0\">\n\t<xsd:annotation>\n\t\t<xsd:documentation xml:lang=\"en\">\n\t\t\tActor Mapping Schema 1.0\n\t\t\tfor Bonita Open Solution.\n\t\t\tCopyright (C) 2011 BonitaSoft S.A.\n\t\t</xsd:documentation>\n\t</xsd:annotation>\n\n\t<xsd:element name=\"actorMappings\">\n\t\t<xsd:complexType>\n\t\t\t<xsd:sequence>\n\t\t\t\t<xsd:element name=\"actorMapping\" maxOccurs=\"unbounded\" type=\"ActorMapping\" />\n\t\t\t</xsd:sequence>\n\t\t</xsd:complexType>\n\t</xsd:element>\n\n\t<xsd:complexType name=\"ActorMapping\">\n\t\t<xsd:all>\n\t\t\t<xsd:element name=\"users\" type=\"Users\" minOccurs=\"0\" />\n\t\t\t<xsd:element name=\"groups\" type=\"Groups\" minOccurs=\"0\" />\n\t\t\t<xsd:element name=\"roles\" type=\"Roles\" minOccurs=\"0\" />\n\t\t\t<xsd:element name=\"memberships\" type=\"Membership\" minOccurs=\"0\" />\n\t\t</xsd:all>\n\t\t<xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n\t</xsd:complexType>\n\n\n\t<xsd:complexType name=\"Users\">\n\t\t<xsd:sequence>\n\t\t\t<xsd:element name=\"user\" type=\"xsd:string\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n\t\t</xsd:sequence>\n\t</xsd:complexType>\n\n\t<xsd:complexType name=\"Groups\">\n\t\t<xsd:sequence>\n\t\t\t<xsd:element name=\"group\" type=\"xsd:string\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n\t\t</xsd:sequence>\n\t</xsd:complexType>\n\n\t<xsd:complexType name=\"Roles\">\n\t\t<xsd:sequence>\n\t\t\t<xsd:element name=\"role\" type=\"xsd:string\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n\t\t</xsd:sequence>\n\t</xsd:complexType>\n\n\t<xsd:complexType name=\"Membership\">\n\t\t<xsd:sequence>\n\t\t\t<xsd:element name=\"membership\" minOccurs=\"0\" maxOccurs=\"unbounded\">\n\t\t\t\t<xsd:complexType>\n\t\t\t\t\t<xsd:all>\n\t\t\t\t\t\t<xsd:element name=\"group\" type=\"xsd:string\" />\n\t\t\t\t\t\t<xsd:element name=\"role\" type=\"xsd:string\" />\n\t\t\t\t\t</xsd:all>\n\t\t\t\t</xsd:complexType>\n\t\t\t</xsd:element>\n\t\t</xsd:sequence>\n\t</xsd:complexType>\n\n</xsd:schema>"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/resources/bonita-community.xml",
    "content": "<beans\n        xmlns=\"http://www.springframework.org/schema/beans\"\n        xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n        xmlns:util=\"http://www.springframework.org/schema/util\"\n        xmlns:context=\"http://www.springframework.org/schema/context\"\n        xmlns:task=\"http://www.springframework.org/schema/task\"\n        xsi:schemaLocation=\"\n            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd\n            http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.3.xsd\n            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd\n            http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.3.xsd\">\n\n    <context:annotation-config />\n\n    <context:property-placeholder />\n\n    <bean class=\"org.bonitasoft.engine.EngineConfiguration\" />\n\n    <bean id=\"synchroService\" class=\"org.bonitasoft.engine.synchro.SynchroServiceImpl\">\n        <constructor-arg name=\"initialCapacity\" value=\"${bonita.platform.synchro.initialcapacity}\" />\n    </bean>\n\n    <bean id=\"quartzProperties\" class=\"org.springframework.beans.factory.config.PropertiesFactoryBean\">\n        <property name=\"properties\">\n            <props>\n                <prop key=\"org.quartz.scheduler.skipUpdateCheck\">${org.quartz.scheduler.skipUpdateCheck:true}</prop>\n                <prop key=\"org.quartz.threadPool.class\">org.quartz.simpl.SimpleThreadPool</prop>\n                <prop key=\"org.quartz.threadPool.threadCount\">${bonita.platform.scheduler.quartz.threadpool.size}</prop>\n                <prop key=\"org.quartz.jobStore.class\">org.bonitasoft.engine.scheduler.impl.BonitaJobStoreCMT</prop>\n                <prop key=\"org.quartz.jobStore.driverDelegateClass\">${${db.vendor}.quartz.connection.jobstoredriver}</prop>\n                <prop key=\"org.quartz.jobStore.dataSource\">managedDS</prop>\n                <prop key=\"org.quartz.jobStore.useProperties\">true</prop>\n                <prop key=\"org.quartz.jobStore.nonManagedTXDataSource\">notManagedDS</prop>\n                <prop key=\"org.quartz.dataSource.managedDS.connectionProvider.class\">org.bonitasoft.engine.platform.configuration.datasource.QuartzConnectionProvider</prop>\n                <prop key=\"org.quartz.dataSource.managedDS.xaDataSource\">true</prop>\n                <prop key=\"org.quartz.dataSource.notManagedDS.connectionProvider.class\">org.bonitasoft.engine.platform.configuration.datasource.QuartzConnectionProvider</prop>\n                <prop key=\"org.quartz.dataSource.notManagedDS.xaDataSource\">false</prop>\n                <prop key=\"org.quartz.jobStore.dontSetAutoCommitFalse\">true</prop>\n                <prop key=\"org.quartz.scheduler.wrapJobExecutionInUserTransaction\">true</prop>\n                <prop key=\"org.quartz.scheduler.userTransactionURL\">${userTransaction}</prop>\n                <prop key=\"org.quartz.plugin.shutdownhook.class\">org.quartz.plugins.management.ShutdownHookPlugin</prop>\n                <prop key=\"org.quartz.plugin.shutdownhook.cleanShutdown\">false</prop>\n                <prop key=\"org.quartz.jobStore.txIsolationLevelReadCommitted\">${org.quartz.jobStore.txIsolationLevelReadCommitted}</prop>\n                <prop key=\"org.quartz.jobStore.misfireThreshold\">${org.quartz.jobStore.misfireThreshold}</prop>\n                <prop key=\"org.quartz.jobStore.maxMisfiresToHandleAtATime\">${org.quartz.jobStore.maxMisfiresToHandleAtATime}</prop>\n                <prop key=\"org.quartz.jobStore.acquireTriggersWithinLock\">${org.quartz.jobStore.acquireTriggersWithinLock}</prop>\n                <prop key=\"org.quartz.scheduler.batchTriggerAcquisitionMaxCount\">${org.quartz.scheduler.batchTriggerAcquisitionMaxCount}</prop>\n                <prop key=\"org.quartz.scheduler.batchTriggerAcquisitionFireAheadTimeWindow\">${org.quartz.scheduler.batchTriggerAcquisitionFireAheadTimeWindow}</prop>\n            </props>\n        </property>\n    </bean>\n\n    <bean id=\"schedulerExecutor\" class=\"org.bonitasoft.engine.scheduler.impl.QuartzSchedulerExecutor\" autowire=\"byType\">\n        <constructor-arg name=\"transactionService\" ref=\"transactionService\" />\n        <constructor-arg name=\"useOptimization\" value=\"true\" />\n    </bean>\n\n    <bean id=\"jobService\" class=\"org.bonitasoft.engine.scheduler.impl.JobServiceImpl\">\n        <constructor-arg name=\"eventService\" ref=\"platformEventService\" />\n        <constructor-arg name=\"recorder\" ref=\"tenantRecorderSyncForPlatform\" />\n        <constructor-arg name=\"readPersistenceService\" ref=\"persistenceService\" />\n    </bean>\n\n    <bean id=\"scheduler\" class=\"org.bonitasoft.engine.scheduler.impl.SchedulerServiceImpl\">\n        <constructor-arg name=\"schedulerExecutor\" ref=\"schedulerExecutor\" />\n        <constructor-arg name=\"jobService\" ref=\"jobService\" />\n        <constructor-arg name=\"eventService\" ref=\"platformEventService\" />\n        <constructor-arg name=\"transactionService\" ref=\"transactionService\" />\n        <constructor-arg name=\"servicesResolver\" ref=\"servicesResolver\" />\n        <constructor-arg name=\"persistenceService\" ref=\"persistenceService\" />\n    </bean>\n\n    <bean id=\"servicesResolver\" class=\"org.bonitasoft.engine.service.ServicesResolver\">\n        <constructor-arg name=\"servicesLookup\" ref=\"servicesLookup\" />\n    </bean>\n\n    <bean id=\"servicesLookup\" class=\"org.bonitasoft.engine.service.ProcessEngineServicesResolver\" />\n\n    <bean id=\"platformLoginService\" class=\"org.bonitasoft.engine.core.platform.login.impl.PlatformLoginServiceImpl\">\n        <constructor-arg name=\"platformAuthenticationService\" ref=\"platformAuthenticationService\" />\n    </bean>\n\n    <bean id=\"platformCommandService\" class=\"org.bonitasoft.engine.platform.command.impl.PlatformCommandServiceImpl\">\n        <constructor-arg name=\"platformPersistenceService\" ref=\"persistenceService\" />\n    </bean>\n\n    <bean id=\"platformAuthenticationService\" class=\"org.bonitasoft.engine.service.impl.PlatformAuthenticationChecker\" />\n\n\n    <bean id=\"platformRetriever\" class=\"org.bonitasoft.engine.platform.impl.PlatformRetrieverImpl\">\n        <constructor-arg name=\"platformPersistenceService\" ref=\"persistenceService\" />\n    </bean>\n\n    <bean id=\"platformService\" class=\"org.bonitasoft.engine.platform.impl.PlatformServiceImpl\">\n        <constructor-arg ref=\"persistenceService\" />\n        <constructor-arg name=\"platformRetriever\" ref=\"platformRetriever\" />\n        <constructor-arg ref=\"platformProperties\" />\n        <constructor-arg name=\"recorder\" ref=\"platformRecorderSync\" />\n    </bean>\n\n    <bean id=\"platformProperties\" class=\"org.bonitasoft.engine.platform.model.impl.SPlatformPropertiesImpl\" />\n\n    <bean id=\"jdbcJobListener\" class=\"org.bonitasoft.engine.scheduler.impl.JDBCJobListener\">\n        <constructor-arg name=\"jobService\" ref=\"jobService\" />\n        <constructor-arg name=\"schedulerService\" ref=\"scheduler\" />\n    </bean>\n\n    <bean id=\"monitoringJobListener\" class=\"org.bonitasoft.engine.scheduler.impl.MonitoringJobListener\">\n        <constructor-arg name=\"meterRegistry\" ref=\"meterRegistry\" />\n    </bean>\n\n    <bean id=\"schedulerServiceRestartHandler\" class=\"org.bonitasoft.engine.handler.SchedulerServiceRestartHandler\">\n        <constructor-arg name=\"schedulerService\" ref=\"scheduler\" />\n        <constructor-arg name=\"userTransactionService\" ref=\"transactionService\" />\n    </bean>\n\n    <bean id=\"incidentService\" class=\"org.bonitasoft.engine.incident.IncidentServiceImpl\">\n        <constructor-arg name=\"handlers\">\n            <list>\n                <bean class=\"org.bonitasoft.engine.incident.FileLoggerIncidentHandler\" />\n            </list>\n        </constructor-arg>\n    </bean>\n\n    <bean id=\"bonitaDataSource\" class=\"org.springframework.jndi.JndiObjectFactoryBean\">\n        <property name=\"jndiName\" value=\"${database.journal.datasource.name}\" />\n    </bean>\n\n    <bean id=\"bonitaNonXaDataSource\" class=\"org.springframework.jndi.JndiObjectFactoryBean\">\n        <property name=\"jndiName\" value=\"${database.sequence.manager.datasource.name}\" />\n    </bean>\n\n    <bean id=\"transactionManager\" class=\"org.springframework.jndi.JndiObjectFactoryBean\">\n        <property name=\"jndiName\" value=\"${transaction.manager}\" />\n    </bean>\n\n    <bean id=\"platformDependencyService\" class=\"org.bonitasoft.engine.dependency.impl.PlatformDependencyService\">\n        <constructor-arg name=\"platformPersistenceService\" ref=\"persistenceService\" />\n    </bean>\n\n\n    <bean id=\"bonitaTaskExecutor\" class=\"org.bonitasoft.engine.service.BonitaTaskExecutor\" />\n\n    <bean id=\"platformRecorderSync\" class=\"org.bonitasoft.engine.recorder.impl.RecorderImpl\">\n        <constructor-arg name=\"persistenceService\" ref=\"persistenceService\" />\n        <constructor-arg name=\"eventService\" ref=\"platformEventService\" />\n    </bean>\n\n    <bean id=\"tenantRecorderSyncForPlatform\" class=\"org.bonitasoft.engine.recorder.impl.RecorderImpl\">\n        <constructor-arg name=\"persistenceService\" ref=\"persistenceService\" />\n        <constructor-arg name=\"eventService\" ref=\"platformEventService\" />\n    </bean>\n\n    <bean id=\"sequenceMappingProvider\" class=\"org.bonitasoft.engine.sequence.SequenceMappingProvider\" autowire=\"byType\" />\n\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.dependency.model.SPlatformDependency\" />\n        <constructor-arg name=\"sequenceId\" value=\"2\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.2:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.dependency.model.SPlatformDependencyMapping\" />\n        <constructor-arg name=\"sequenceId\" value=\"3\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.3:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.platform.command.model.SPlatformCommand\" />\n        <constructor-arg name=\"sequenceId\" value=\"4\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.4:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.dependency.model.SDependency\" />\n        <constructor-arg name=\"sequenceId\" value=\"10\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.10:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.dependency.model.SDependencyMapping\" />\n        <constructor-arg name=\"sequenceId\" value=\"11\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.11:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.identity.model.SUser\" />\n        <constructor-arg name=\"sequenceId\" value=\"20\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.20:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.identity.model.SRole\" />\n        <constructor-arg name=\"sequenceId\" value=\"21\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.21:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.identity.model.SGroup\" />\n        <constructor-arg name=\"sequenceId\" value=\"22\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.22:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.identity.model.SUserMembership\" />\n        <constructor-arg name=\"sequenceId\" value=\"23\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.23:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition\" />\n        <constructor-arg name=\"sequenceId\" value=\"24\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.24:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.identity.model.SCustomUserInfoValue\" />\n        <constructor-arg name=\"sequenceId\" value=\"25\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.25:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.identity.model.SContactInfo\" />\n        <constructor-arg name=\"sequenceId\" value=\"26\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.26:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.identity.SIcon\" />\n        <constructor-arg name=\"sequenceId\" value=\"27\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.27:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.queriablelogger.model.SQueriableLog\" />\n        <constructor-arg name=\"sequenceId\" value=\"30\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.30:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.scheduler.model.SJobDescriptor\" />\n        <constructor-arg name=\"sequenceId\" value=\"70\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.70:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.scheduler.model.SJobParameter\" />\n        <constructor-arg name=\"sequenceId\" value=\"71\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.71:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.scheduler.model.SJobLog\" />\n        <constructor-arg name=\"sequenceId\" value=\"72\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.72:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.command.model.SCommand\" />\n        <constructor-arg name=\"sequenceId\" value=\"90\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.90:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.profile.model.SProfile\" />\n        <constructor-arg name=\"sequenceId\" value=\"9990\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.9990:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.profile.model.SProfileMember\" />\n        <constructor-arg name=\"sequenceId\" value=\"9992\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.9992:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.actor.mapping.model.SActor\" />\n        <constructor-arg name=\"sequenceId\" value=\"10000\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.10000:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.actor.mapping.model.SActorMember\" />\n        <constructor-arg name=\"sequenceId\" value=\"10001\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.10001:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.core.process.instance.model.SProcessInstance\" />\n        <constructor-arg name=\"sequenceId\" value=\"10010\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.10010:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"classNames\">\n            <set>\n                <value>org.bonitasoft.engine.core.process.instance.model.SActivityInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SGatewayInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.SStartEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.SIntermediateCatchEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.SIntermediateThrowEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.SEndEventInstance</value>\n            </set>\n        </constructor-arg>\n        <constructor-arg name=\"sequenceId\" value=\"10011\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.10011:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo\" />\n        <constructor-arg name=\"sequenceId\" value=\"10012\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.10012:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.core.process.definition.model.impl.SActorImpl\" />\n        <constructor-arg name=\"sequenceId\" value=\"10013\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.10013:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"classNames\">\n            <set>\n                <value>org.bonitasoft.engine.core.process.instance.model.SConnectorInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SConnectorInstanceWithFailureInfo</value>\n            </set>\n        </constructor-arg>\n        <constructor-arg name=\"sequenceId\" value=\"10014\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.10014:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"classNames\">\n            <set>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.trigger.impl.SThrowMessageEventTriggerInstanceImpl</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.trigger.impl.SThrowSignalEventTriggerInstanceImpl</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.trigger.impl.SThrowErrorEventTriggerInstanceImpl</value>\n            </set>\n        </constructor-arg>\n        <constructor-arg name=\"sequenceId\" value=\"10015\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.10015:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"classNames\">\n            <set>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingErrorEvent</value>\n            </set>\n        </constructor-arg>\n        <constructor-arg name=\"sequenceId\" value=\"10016\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.10016:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance\" />\n        <constructor-arg name=\"sequenceId\" value=\"10017\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.10017:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping\" />\n        <constructor-arg name=\"sequenceId\" value=\"10018\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.10018:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"classNames\">\n            <set>\n                <value>org.bonitasoft.engine.data.instance.model.SDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.SBooleanDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.SIntegerDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.SDoubleDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.SFloatDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.SShortTextDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.SLongDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.SDateDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.SLongTextDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.SXMLDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.SXMLObjectDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.SBlobDataInstance</value>\n            </set>\n        </constructor-arg>\n        <constructor-arg name=\"sequenceId\" value=\"10020\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.10020:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.core.category.model.SCategory\" />\n        <constructor-arg name=\"sequenceId\" value=\"10030\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.10030:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.core.category.model.SProcessCategoryMapping\" />\n        <constructor-arg name=\"sequenceId\" value=\"10031\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.10031:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.core.document.model.SDocumentMapping\" />\n        <constructor-arg name=\"sequenceId\" value=\"10040\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.10040:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor\" />\n        <constructor-arg name=\"sequenceId\" value=\"10050\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.10050:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"classNames\">\n            <set>\n                <value>org.bonitasoft.engine.core.process.comment.model.SHumanComment</value>\n                <value>org.bonitasoft.engine.core.process.comment.model.SSystemComment</value>\n            </set>\n        </constructor-arg>\n        <constructor-arg name=\"sequenceId\" value=\"10060\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.10060:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.core.process.comment.model.archive.SAComment\" />\n        <constructor-arg name=\"sequenceId\" value=\"10080\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.bonitasoft:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"classNames\">\n            <set>\n                <value>org.bonitasoft.engine.core.document.model.SDocument</value>\n                <value>org.bonitasoft.engine.core.document.model.SLightDocument</value>\n            </set>\n        </constructor-arg>\n        <constructor-arg name=\"sequenceId\" value=\"10090\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.10090:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"classNames\">\n            <set>\n                <value>org.bonitasoft.engine.page.SPageWithContent</value>\n                <value>org.bonitasoft.engine.page.SPage</value>\n            </set>\n        </constructor-arg>\n        <constructor-arg name=\"sequenceId\" value=\"10120\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.10120:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"classNames\">\n            <set>\n                <value>org.bonitasoft.engine.parameter.SParameter</value>\n            </set>\n        </constructor-arg>\n        <constructor-arg name=\"sequenceId\" value=\"10400\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.10400:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"classNames\">\n            <set>\n                <value>org.bonitasoft.engine.resources.SBARResource</value>\n            </set>\n        </constructor-arg>\n        <constructor-arg name=\"sequenceId\" value=\"10500\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.10500:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"classNames\">\n            <set>\n                <value>org.bonitasoft.engine.resources.STenantResource</value>\n            </set>\n        </constructor-arg>\n        <constructor-arg name=\"sequenceId\" value=\"10501\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.10501:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.page.SPageMapping\" />\n        <constructor-arg name=\"sequenceId\" value=\"10121\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.10121:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"classNames\">\n            <set>\n                <value>org.bonitasoft.engine.business.application.model.SApplication</value>\n                <value>org.bonitasoft.engine.business.application.model.SApplicationWithIcon</value>\n            </set>\n        </constructor-arg>\n        <constructor-arg name=\"sequenceId\" value=\"10200\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.10200:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.business.application.model.SApplicationPage\" />\n        <constructor-arg name=\"sequenceId\" value=\"10201\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.10201:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.business.application.model.SApplicationMenu\" />\n        <constructor-arg name=\"sequenceId\" value=\"10202\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.10202:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"classNames\">\n            <set>\n                <value>org.bonitasoft.engine.core.contract.data.STaskContractData</value>\n                <value>org.bonitasoft.engine.core.contract.data.SProcessContractData</value>\n            </set>\n        </constructor-arg>\n        <constructor-arg name=\"sequenceId\" value=\"10210\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.10210:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.core.form.SFormMapping\" />\n        <constructor-arg name=\"sequenceId\" value=\"10300\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.10300:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDesignContent\" />\n        <constructor-arg name=\"sequenceId\" value=\"10310\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.10310:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"classNames\">\n            <set>\n                <value>org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.business.data.SProcessSimpleRefBusinessDataInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.business.data.SFlowNodeSimpleRefBusinessDataInstance</value>\n            </set>\n        </constructor-arg>\n        <constructor-arg name=\"sequenceId\" value=\"10096\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.10096:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"classNames\">\n            <set>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.business.data.SAProcessMultiRefBusinessDataInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.business.data.SAProcessSimpleRefBusinessDataInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.business.data.SAFlowNodeSimpleRefBusinessDataInstance</value>\n            </set>\n        </constructor-arg>\n        <constructor-arg name=\"sequenceId\" value=\"20096\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.20096:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance\" />\n        <constructor-arg name=\"sequenceId\" value=\"20010\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.20010:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"classNames\">\n            <set>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SAAutomaticTaskInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SACallActivityInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SASubProcessActivityInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SAManualTaskInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SAReceiveTaskInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SASendTaskInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SALoopActivityInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SAMultiInstanceActivityInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.event.SAStartEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.event.SAEndEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SAGatewayInstance</value>\n            </set>\n        </constructor-arg>\n        <constructor-arg name=\"sequenceId\" value=\"20011\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.20011:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.core.process.instance.model.archive.SAConnectorInstance\" />\n        <constructor-arg name=\"sequenceId\" value=\"20013\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.20013:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"classNames\">\n            <set>\n                <value>org.bonitasoft.engine.data.instance.model.archive.SADataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.archive.SABooleanDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.archive.SAIntegerDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.archive.SADoubleDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.archive.SAFloatDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.archive.SAShortTextDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.archive.SALongDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.archive.SADateDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.archive.SALongTextDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.archive.SAXMLObjectDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.archive.SABlobDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.archive.SAXMLDataInstance</value>\n            </set>\n        </constructor-arg>\n        <constructor-arg name=\"sequenceId\" value=\"20050\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.20050:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.core.document.model.archive.SADocumentMapping\" />\n        <constructor-arg name=\"sequenceId\" value=\"20040\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.20040:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"classNames\">\n            <set>\n                <value>org.bonitasoft.engine.core.contract.data.SATaskContractData</value>\n                <value>org.bonitasoft.engine.core.contract.data.SAProcessContractData</value>\n            </set>\n        </constructor-arg>\n        <constructor-arg name=\"sequenceId\" value=\"20210\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.20210:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"classNames\">\n            <set>\n                <value>org.bonitasoft.engine.temporary.content.STemporaryContent</value>\n            </set>\n        </constructor-arg>\n        <constructor-arg name=\"sequenceId\" value=\"5\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.5:${bonita.platform.sequence.defaultRangeSize}}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"classNames\">\n            <set>\n                <value>org.bonitasoft.engine.core.process.instance.model.SBPMFailure</value>\n            </set>\n        </constructor-arg>\n        <constructor-arg name=\"sequenceId\" value=\"6\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.defaultRangeSize}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"classNames\">\n            <set>\n                <value>org.bonitasoft.engine.core.process.instance.model.SABPMFailure</value>\n            </set>\n        </constructor-arg>\n        <constructor-arg name=\"sequenceId\" value=\"7\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.defaultRangeSize}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.business.data.model.SDataRetentionConfig\" />\n        <constructor-arg name=\"sequenceId\" value=\"8\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.defaultRangeSize}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.business.data.model.SDataRetentionBdmTracking\" />\n        <constructor-arg name=\"sequenceId\" value=\"9\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.defaultRangeSize}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.core.delegation.model.SDelegationRule\" />\n        <constructor-arg name=\"sequenceId\" value=\"20097\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.defaultRangeSize}\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.sequence.SequenceMapping\">\n        <constructor-arg name=\"className\" value=\"org.bonitasoft.engine.core.delegation.model.SDelegationRuleProcess\" />\n        <constructor-arg name=\"sequenceId\" value=\"20098\" />\n        <constructor-arg name=\"rangeSize\" value=\"${bonita.platform.sequence.defaultRangeSize}\" />\n    </bean>\n\n    <bean id=\"transactionService\" class=\"org.bonitasoft.engine.transaction.JTATransactionServiceImpl\" scope=\"singleton\">\n        <constructor-arg name=\"txManager\" ref=\"transactionManager\" />\n    </bean>\n\n    <bean id=\"communityHbmResourcesProvider\" class=\"org.bonitasoft.engine.persistence.HibernateResourcesProvider\">\n        <property name=\"resources\">\n            <set>\n                <!-- actor -->\n                <value>org/bonitasoft/engine/actor/mapping/model/impl/hibernate/actor.queries.hbm.xml</value>\n                <!-- bar/tenant resources -->\n                <value>org/bonitasoft/engine/resources/hibernate/resources.queries.hbm.xml</value>\n                <!-- uploads -->\n                <value>org/bonitasoft/engine/temporary/content/hibernate/temporary.content.queries.hbm.xml</value>\n                <!-- supervisor -->\n                <value>org/bonitasoft/engine/supervisor/mapping/model/impl/hibernate/supervisor.queries.hbm.xml</value>\n                <!-- scheduler -->\n                <value>org/bonitasoft/engine/scheduler/impl/hibernate/schedulerimpl.queries.hbm.xml</value>\n                <!-- profile -->\n                <value>org/bonitasoft/engine/profile/model/impl/hibernate/profile.queries.hbm.xml</value>\n                <!-- process instance -->\n                <value>org/bonitasoft/engine/core/process/instance/model/impl/hibernate/process.instance.queries.hbm.xml</value>\n                <!-- archived process instance -->\n                <value>org/bonitasoft/engine/core/process/instance/model/impl/hibernate/archived.process.instance.queries.hbm.xml</value>\n                <!-- process definition -->\n                <value>org/bonitasoft/engine/core/process/definition/model/impl/hibernate/process.definition.queries.hbm.xml</value>\n                <!-- comment -->\n                <value>org/bonitasoft/engine/core/process/comment/model/impl/hibernate/comment.queries.hbm.xml</value>\n                <!-- archived comment -->\n                <value>org/bonitasoft/engine/core/process/comment/model/impl/hibernate/archive.comment.queries.hbm.xml</value>\n                <!-- platform -->\n                <value>org/bonitasoft/engine/platform/model/impl/hibernate/platform.queries.hbm.xml</value>\n                <!-- platform command -->\n                <value>org/bonitasoft/engine/platform/command/model/impl/hibernate/platformCommand.queries.hbm.xml</value>\n                <!-- page -->\n                <value>org/bonitasoft/engine/page/impl/hibernate/page.queries.hbm.xml</value>\n                <!-- queriable log -->\n                <value>org/bonitasoft/engine/queriablelogger/model/impl/hibernate/queriablelogger.queries.hbm.xml</value>\n                <!-- identity -->\n                <value>org/bonitasoft/engine/identity/model/impl/hibernate/identity.queries.hbm.xml</value>\n                <!-- document -->\n                <value>org/bonitasoft/engine/core/document/model/impl/hibernate/document.queries.hbm.xml</value>\n                <!-- archived document -->\n                <value>org/bonitasoft/engine/core/document/model/impl/hibernate/archive.document.queries.hbm.xml</value>\n                <!-- platform dependency -->\n                <value>org/bonitasoft/engine/dependency/model/impl/hibernate/platform-dependency.queries.hbm.xml</value>\n                <!-- dependency -->\n                <value>org/bonitasoft/engine/dependency/model/impl/hibernate/dependency.queries.hbm.xml</value>\n                <!-- data instance -->\n                <value>org/bonitasoft/engine/data/instance/model/impl/hibernate/data.instance.queries.hbm.xml</value>\n                <!-- archived data instance -->\n                <value>org/bonitasoft/engine/data/instance/model/impl/hibernate/archived.data.instance.queries.hbm.xml</value>\n                <!-- command -->\n                <value>org/bonitasoft/engine/command/model/impl/hibernate/command.queries.hbm.xml</value>\n                <!-- category -->\n                <value>org/bonitasoft/engine/core/category/model/impl/hibernate/category.queries.hbm.xml</value>\n                <!-- application -->\n                <value>org/bonitasoft/engine/business/application/impl/hibernate/application.queries.hbm.xml</value>\n                <!-- form mapping -->\n                <value>org/bonitasoft/engine/core/form/impl/form-mapping.queries.hbm.xml</value>\n                <!-- contract -->\n                <value>org/bonitasoft/engine/core/contract/data/model/impl/hibernate/contract.queries.hbm.xml</value>\n                <!-- parameters -->\n                <value>org/bonitasoft/engine/parameter/parameter.queries.hbm.xml</value>\n                <!-- BPM Failures -->\n                <value>org/bonitasoft/engine/core/failure/model/impl/hibernate/failure.queries.hbm.xml</value>\n                <!-- data retention bdm tracking -->\n                <value>org/bonitasoft/engine/business/data/model/impl/hibernate/data-retention-bdm-tracking.queries.hbm.xml</value>\n            </set>\n        </property>\n        <property name=\"classAliasMappings\">\n            <map>\n                <!-- actor -->\n                <entry key=\"org.bonitasoft.engine.actor.mapping.model.SActor\" value=\"actor\" />\n                <entry key=\"org.bonitasoft.engine.actor.mapping.model.SActorMember\" value=\"actormember\" />\n                <!-- supervisor -->\n                <entry key=\"org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor\" value=\"processsupervisor\" />\n                <!-- scheduler -->\n                <entry key=\"org.bonitasoft.engine.scheduler.model.SJobDescriptor\" value=\"jd\" />\n                <entry key=\"org.bonitasoft.engine.scheduler.model.SJobParameter\" value=\"jp\" />\n                <entry key=\"org.bonitasoft.engine.scheduler.model.SJobLog\" value=\"jl\" />\n                <!-- profile -->\n                <entry key=\"org.bonitasoft.engine.profile.model.SProfile\" value=\"profile\" />\n                <entry key=\"org.bonitasoft.engine.profile.model.SProfileMember\" value=\"pm\" />\n                <!-- process instance -->\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.SProcessInstance\" value=\"p\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.SActivityInstance\" value=\"a\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance\" value=\"f\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.SGatewayInstance\" value=\"g\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.SConnectorInstance\" value=\"c\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.SConnectorInstanceWithFailureInfo\" value=\"cfi\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.event.SStartEventInstance\" value=\"e\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance\" value=\"e\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.event.SIntermediateCatchEventInstance\" value=\"e\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.event.SIntermediateThrowEventInstance\" value=\"e\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.event.SEndEventInstance\" value=\"e\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.event.SEventInstance\" value=\"e\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.event.SCatchEventInstance\" value=\"e\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance\" value=\"t\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance\" value=\"t\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent\" value=\"s\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent\" value=\"s\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent\" value=\"s\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingErrorEvent\" value=\"s\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance\" value=\"m\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping\" value=\"mapping\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.SActivityInstance\" value=\"a\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance\" value=\"a\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance\" value=\"a\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance\" value=\"a\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance\" value=\"a\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance\" value=\"a\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance\" value=\"a\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance\" value=\"la\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance\" value=\"a\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance\" value=\"a\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance\" value=\"a\" />\n                <!-- archived process instance -->\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance\" value=\"ap\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance\" value=\"aa\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance\" value=\"aa\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.archive.SAAutomaticTaskInstance\" value=\"aa\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.archive.SAReceiveTaskInstance\" value=\"aa\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.archive.SASendTaskInstance\" value=\"aa\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.archive.SACallActivityInstance\" value=\"aa\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.archive.SASubProcessActivityInstance\" value=\"aa\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance\" value=\"aa\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance\" value=\"aa\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.archive.SALoopActivityInstance\" value=\"aa\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.archive.SAGatewayInstance\" value=\"aa\" />\n                <entry key=\"org.bonitasoft.engine.core.process.instance.model.archive.SAConnectorInstance\" value=\"ac\" />\n                <!-- process definition -->\n                <entry key=\"org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo\" value=\"process_definition\" />\n                <entry key=\"org.bonitasoft.engine.core.process.definition.model.SActor\" value=\"actor\" />\n                <entry key=\"org.bonitasoft.engine.actor.mapping.model.SActorMember\" value=\"actormember\" />\n                <entry key=\"org.bonitasoft.engine.identity.model.SUser\" value=\"user\" />\n                <!-- comment -->\n                <entry key=\"org.bonitasoft.engine.core.process.comment.model.SComment\" value=\"comment\" />\n                <entry key=\"org.bonitasoft.engine.core.process.comment.model.SHumanComment\" value=\"comment\" />\n                <entry key=\"org.bonitasoft.engine.core.process.comment.model.SSystemComment\" value=\"comment\" />\n                <!-- archived comment -->\n                <entry key=\"org.bonitasoft.engine.core.process.comment.model.archive.SAComment\" value=\"saComment\" />\n                <!-- platform -->\n                <entry key=\"org.bonitasoft.engine.platform.model.SPlatform\" value=\"platform\" />\n                <!-- data retention config -->\n                <entry key=\"org.bonitasoft.engine.business.data.model.SDataRetentionConfig\" value=\"dataRetentionConfig\" />\n                <!-- data retention bdm tracking -->\n                <entry key=\"org.bonitasoft.engine.business.data.model.SDataRetentionBdmTracking\" value=\"dataRetentionBdmTracking\" />\n                <!-- delegation -->\n                <entry key=\"org.bonitasoft.engine.core.delegation.model.SDelegationRule\" value=\"delegationRule\" />\n                <entry key=\"org.bonitasoft.engine.core.delegation.model.SDelegationRuleProcess\" value=\"delegationRuleProcess\" />\n                <!-- platform command -->\n                <entry key=\"org.bonitasoft.engine.platform.command.model.SPlatformCommand\" value=\"platformCommand\" />\n                <!-- page -->\n                <entry key=\"org.bonitasoft.engine.page.SPage\" value=\"page\" />\n                <entry key=\"org.bonitasoft.engine.page.SPageWithContent\" value=\"pagecontent\" />\n                <!-- page -->\n                <entry key=\"org.bonitasoft.engine.parameter.SParameter\" value=\"param\" />\n                <!-- queriable log -->\n                <entry key=\"org.bonitasoft.engine.queriablelogger.model.SQueriableLog\" value=\"log\" />\n                <!-- identity -->\n                <entry key=\"org.bonitasoft.engine.identity.model.SUser\" value=\"user\" />\n                <entry key=\"org.bonitasoft.engine.identity.model.SRole\" value=\"role\" />\n                <entry key=\"org.bonitasoft.engine.identity.model.SGroup\" value=\"group_\" />\n                <entry key=\"org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition\" value=\"custom_usr_inf_def\" />\n                <entry key=\"org.bonitasoft.engine.identity.model.SCustomUserInfoValue\" value=\"custom_usr_inf_val\" />\n                <entry key=\"org.bonitasoft.engine.identity.model.SUserMembership\" value=\"user_membership\" />\n                <entry key=\"org.bonitasoft.engine.identity.model.SContactInfo\" value=\"contactinfo\" />\n                <entry key=\"org.bonitasoft.engine.identity.SIcon\" value=\"icon\" />\n                <!-- document -->\n                <entry key=\"org.bonitasoft.engine.core.document.model.SDocument\" value=\"doc\" />\n                <entry key=\"org.bonitasoft.engine.core.document.model.SLightDocument\" value=\"doc\" />\n                <entry key=\"org.bonitasoft.engine.core.document.model.SDocumentMapping\" value=\"docmapping\" />\n                <entry key=\"org.bonitasoft.engine.core.document.model.SMappedDocument\" value=\"mappeddoc\" />\n                <!-- archived document -->\n                <entry key=\"org.bonitasoft.engine.core.document.model.archive.SADocumentMapping\" value=\"docmapping\" />\n                <entry key=\"org.bonitasoft.engine.core.document.model.archive.SAMappedDocument\" value=\"mappeddoc\" />\n                <!-- platform dependency -->\n                <entry key=\"org.bonitasoft.engine.dependency.model.SPlatformDependency\" value=\"pdependency\" />\n                <entry key=\"org.bonitasoft.engine.dependency.model.SPlatformDependencyMapping\" value=\"pdependencymapping\" />\n                <!-- dependency -->\n                <entry key=\"org.bonitasoft.engine.dependency.model.SDependency\" value=\"dependency\" />\n                <entry key=\"org.bonitasoft.engine.dependency.model.SDependencyMapping\" value=\"dependencymapping\" />\n                <!-- data instance -->\n                <entry key=\"org.bonitasoft.engine.data.instance.model.SDataInstance\" value=\"dataInstance\" />\n                <!-- archived data instance -->\n                <entry key=\"org.bonitasoft.engine.data.instance.model.archive.SADataInstance\" value=\"saDataInstance\" />\n                <!-- command -->\n                <entry key=\"org.bonitasoft.engine.command.model.SCommand\" value=\"command\" />\n                <!-- category -->\n                <entry key=\"org.bonitasoft.engine.core.category.model.SCategory\" value=\"category\" />\n                <entry key=\"org.bonitasoft.engine.core.category.model.SProcessCategoryMapping\" value=\"categorymapping\" />\n                <!-- application -->\n                <entry key=\"org.bonitasoft.engine.business.application.model.SApplicationWithIcon\" value=\"application\" />\n                <entry key=\"org.bonitasoft.engine.business.application.model.SApplication\" value=\"application\" />\n                <entry key=\"org.bonitasoft.engine.business.application.model.SApplicationPage\" value=\"applicationPage\" />\n                <entry key=\"org.bonitasoft.engine.business.application.model.SApplicationMenu\" value=\"applicationMenu\" />\n                <!-- form mapping -->\n                <entry key=\"org.bonitasoft.engine.core.form.SFormMapping\" value=\"formmap\" />\n                <entry key=\"org.bonitasoft.engine.page.SPageMapping\" value=\"pagemap\" />\n                <!-- contract -->\n                <entry key=\"org.bonitasoft.engine.core.contract.data.STaskContractData\" value=\"tcd\" />\n                <entry key=\"org.bonitasoft.engine.core.contract.data.SATaskContractData\" value=\"atcd\" />\n                <entry key=\"org.bonitasoft.engine.core.contract.data.SProcessContractData\" value=\"pcd\" />\n                <entry key=\"org.bonitasoft.engine.core.contract.data.SAProcessContractData\" value=\"apcd\" />\n            </map>\n        </property>\n        <property name=\"entities\">\n            <set>\n                <value>org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo</value>\n                <value>org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDesignContent</value>\n                <value>org.bonitasoft.engine.identity.model.SContactInfo</value>\n                <value>org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition</value>\n                <value>org.bonitasoft.engine.identity.model.SCustomUserInfoValue</value>\n                <value>org.bonitasoft.engine.identity.model.SGroup</value>\n                <value>org.bonitasoft.engine.identity.model.SHavingIcon</value>\n                <value>org.bonitasoft.engine.identity.SIcon</value>\n                <value>org.bonitasoft.engine.identity.model.SRole</value>\n                <value>org.bonitasoft.engine.identity.model.SUser</value>\n                <value>org.bonitasoft.engine.identity.model.SUserLogin</value>\n                <value>org.bonitasoft.engine.identity.model.SUserMembership</value>\n                <value>org.bonitasoft.engine.actor.mapping.model.SActor</value>\n                <value>org.bonitasoft.engine.actor.mapping.model.SActorMember</value>\n                <value>org.bonitasoft.engine.business.application.model.SApplicationWithIcon</value>\n                <value>org.bonitasoft.engine.business.application.model.SApplication</value>\n                <value>org.bonitasoft.engine.business.application.model.SApplicationPage</value>\n                <value>org.bonitasoft.engine.business.application.model.SApplicationMenu</value>\n                <value>org.bonitasoft.engine.platform.model.SPlatform</value>\n                <value>org.bonitasoft.engine.business.data.model.SDataRetentionConfig</value>\n                <value>org.bonitasoft.engine.business.data.model.SDataRetentionBdmTracking</value>\n                <value>org.bonitasoft.engine.core.delegation.model.SDelegationRule</value>\n                <value>org.bonitasoft.engine.core.delegation.model.SDelegationRuleProcess</value>\n                <value>org.bonitasoft.engine.platform.command.model.SPlatformCommand</value>\n                <value>org.bonitasoft.engine.core.category.model.SCategory</value>\n                <value>org.bonitasoft.engine.core.category.model.SProcessCategoryMapping</value>\n                <value>org.bonitasoft.engine.scheduler.model.SJobDescriptor</value>\n                <value>org.bonitasoft.engine.scheduler.model.SJobParameter</value>\n                <value>org.bonitasoft.engine.scheduler.model.SJobLog</value>\n                <value>org.bonitasoft.engine.command.model.SCommand</value>\n                <value>org.bonitasoft.engine.core.process.comment.model.SComment</value>\n                <value>org.bonitasoft.engine.core.process.comment.model.SHumanComment</value>\n                <value>org.bonitasoft.engine.core.process.comment.model.SSystemComment</value>\n                <value>org.bonitasoft.engine.core.process.comment.model.archive.SAComment</value>\n                <value>org.bonitasoft.engine.dependency.model.SDependency</value>\n                <value>org.bonitasoft.engine.dependency.model.SDependencyMapping</value>\n                <value>org.bonitasoft.engine.dependency.model.SPlatformDependency</value>\n                <value>org.bonitasoft.engine.dependency.model.SPlatformDependencyMapping</value>\n                <value>org.bonitasoft.engine.core.contract.data.SContractData</value>\n                <value>org.bonitasoft.engine.core.contract.data.SProcessContractData</value>\n                <value>org.bonitasoft.engine.core.contract.data.STaskContractData</value>\n                <value>org.bonitasoft.engine.core.contract.data.SAContractData</value>\n                <value>org.bonitasoft.engine.core.contract.data.SAProcessContractData</value>\n                <value>org.bonitasoft.engine.core.contract.data.SATaskContractData</value>\n                <value>org.bonitasoft.engine.core.document.model.SLightDocument</value>\n                <value>org.bonitasoft.engine.core.document.model.SDocument</value>\n                <value>org.bonitasoft.engine.core.document.model.SDocumentMapping</value>\n                <value>org.bonitasoft.engine.core.document.model.SMappedDocument</value>\n                <value>org.bonitasoft.engine.core.document.model.archive.SADocumentMapping</value>\n                <value>org.bonitasoft.engine.core.document.model.archive.SAMappedDocument</value>\n                <value>org.bonitasoft.engine.data.instance.model.archive.SABlobDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.archive.SABooleanDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.archive.SADataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.archive.SADateDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.archive.SADoubleDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.archive.SAFloatDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.archive.SAIntegerDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.archive.SALongDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.archive.SALongTextDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.archive.SAShortTextDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.archive.SAXMLDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.archive.SAXMLObjectDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.SBlobDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.SBooleanDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.SDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.SDateDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.SDoubleDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.SFloatDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.SIntegerDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.SLongDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.SLongTextDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.SShortTextDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.SXMLDataInstance</value>\n                <value>org.bonitasoft.engine.data.instance.model.SXMLObjectDataInstance</value>\n                <value>org.bonitasoft.engine.page.SPage</value>\n                <value>org.bonitasoft.engine.page.SPageWithContent</value>\n                <value>org.bonitasoft.engine.page.SPageMapping</value>\n                <value>org.bonitasoft.engine.profile.model.SProfile</value>\n                <value>org.bonitasoft.engine.profile.model.SProfileMember</value>\n                <value>org.bonitasoft.engine.parameter.SParameter</value>\n                <value>org.bonitasoft.engine.resources.SBARResourceLight</value>\n                <value>org.bonitasoft.engine.resources.SBARResource</value>\n                <value>org.bonitasoft.engine.resources.STenantResource</value>\n                <value>org.bonitasoft.engine.resources.STenantResourceLight</value>\n                <value>org.bonitasoft.engine.temporary.content.STemporaryContent</value>\n                <value>org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor</value>\n                <value>org.bonitasoft.engine.core.form.SFormMapping</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SProcessInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SActivityInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SGatewayInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SConnectorInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SConnectorInstanceWithFailureInfo</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.SEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.SCatchEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.SStartEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.SIntermediateCatchEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.SThrowEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.SIntermediateThrowEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.SEndEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingErrorEvent</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.business.data.SSimpleRefBusinessDataInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.business.data.SProcessSimpleRefBusinessDataInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.business.data.SFlowNodeSimpleRefBusinessDataInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SAManualTaskInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SAAutomaticTaskInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SAReceiveTaskInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SASendTaskInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SACallActivityInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SASubProcessActivityInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SALoopActivityInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SAMultiInstanceActivityInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SAGatewayInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.SAConnectorInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.event.SAEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.event.SACatchEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.event.SAStartEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.event.SAIntermediateCatchEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.event.SABoundaryEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.event.SAThrowEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.event.SAIntermediateThrowEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.event.SAEndEventInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.business.data.SARefBusinessDataInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.business.data.SASimpleRefBusinessDataInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.business.data.SAProcessSimpleRefBusinessDataInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.business.data.SAFlowNodeSimpleRefBusinessDataInstance</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.archive.business.data.SAProcessMultiRefBusinessDataInstance</value>\n                <value>org.bonitasoft.engine.queriablelogger.model.SQueriableLog</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SBPMFailure</value>\n                <value>org.bonitasoft.engine.core.process.instance.model.SABPMFailure</value>\n            </set>\n        </property>\n    </bean>\n\n    <bean id=\"extraHibernateProperties\" class=\"org.bonitasoft.engine.service.impl.MapToPropertiesFactoryBean\">\n        <property name=\"map\">\n            <map>\n            </map>\n        </property>\n    </bean>\n\n    <bean id=\"queryBuilderFactory\" class=\"org.bonitasoft.engine.persistence.QueryBuilderFactory\">\n        <constructor-arg name=\"orderByCheckingMode\" value=\"${sysprop.bonita.orderby.checking.mode:NONE}\" />\n        <constructor-arg name=\"classAliasMappings\">\n            <bean factory-bean=\"hbmConfigurationProvider\" factory-method=\"getClassAliasMappings\" />\n        </constructor-arg>\n        <constructor-arg name=\"likeEscapeCharacter\" value=\"${bonita.platform.persistence.tenant.likeEscapeCharacter}\" />\n    </bean>\n\n    <bean id=\"hbmConfigurationProviderProperties\" parent=\"communityHbmConfigurationProviderProperties\" class=\"org.bonitasoft.engine.service.impl.MapToPropertiesFactoryBean\">\n        <property name=\"map\">\n            <map merge=\"true\">\n            </map>\n        </property>\n    </bean>\n\n    <bean id=\"communityHbmConfigurationProviderProperties\" class=\"org.bonitasoft.engine.service.impl.MapToPropertiesFactoryBean\">\n        <property name=\"map\">\n            <map>\n                <entry key=\"hibernate.session.events.log.LOG_QUERIES_SLOWER_THAN_MS\" value=\"${bonita.platform.persistence.dbquery.warnWhenLongerThanMillis}\" />\n                <entry key=\"hibernate.connection.datasource\" value-ref=\"bonitaDataSource\" />\n                <entry key=\"hibernate.current_session_context_class\" value=\"jta\" />\n                <entry key=\"hibernate.transaction.coordinator_class\" value=\"jta\" />\n                <entry key=\"hibernate.transaction.jta.platform\" value=\"${hibernate.transaction.jta_platform}\" />\n                <entry key=\"hibernate.dialect\" value=\"${${db.vendor}.hibernate.dialect}\" />\n                <entry key=\"hibernate.interceptor\" value=\"${${db.vendor}.hibernate.journal.interceptor}\" />\n                <entry key=\"hibernate.show_sql\" value=\"${hibernate.journal.show_sql}\" />\n                <entry key=\"hibernate.format_sql\" value=\"${hibernate.journal.format_sql}\" />\n                <entry key=\"hibernate.use_sql_comments\" value=\"${hibernate.journal.use_sql_comments}\" />\n                <entry key=\"hibernate.generate_statistics\" value=\"${bonita.platform.persistence.generate_statistics}\" />\n                <entry key=\"hibernate.connection.shutdown\" value=\"${bonita.platform.persistence.connection.shutdown}\" />\n                <entry key=\"hibernate.validator.autoregister_listeners\" value=\"${bonita.platform.persistence.validator.autoregister_listeners}\" />\n                <entry key=\"hibernate.validator.apply_to_ddl\" value=\"${bonita.platform.persistence.validator.apply_to_ddl}\" />\n                <entry key=\"javax.persistence.validation.mode\" value=\"${bonita.platform.persistence.javax.persistence.validation.mode}\" />\n                <entry key=\"hibernate.query.plan_cache_max_size\" value=\"${bonita.platform.persistence.query.cache.max_size}\" />\n            </map>\n        </property>\n    </bean>\n\n    <bean id=\"mappingExclusions\" class=\"org.springframework.beans.factory.config.ListFactoryBean\">\n        <property name=\"sourceList\">\n            <list>\n                <value>org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageEventCouple</value>\n            </list>\n        </property>\n    </bean>\n    <bean id=\"hbmConfigurationProvider\" class=\"org.bonitasoft.engine.persistence.HibernateConfigurationProviderImpl\">\n        <constructor-arg name=\"properties\" ref=\"hbmConfigurationProviderProperties\" />\n        <constructor-arg name=\"mappingExclusions\" ref=\"mappingExclusions\" />\n        <constructor-arg ref=\"hbmResourcesConfigurationProvider\" />\n    </bean>\n\n    <bean id=\"hbmResourcesConfigurationProvider\" class=\"org.bonitasoft.engine.persistence.HibernateResourcesConfigurationProviderImpl\" autowire=\"byType\" />\n\n\n\n    <bean id=\"sessionService\" class=\"org.bonitasoft.engine.session.impl.SessionServiceImpl\">\n        <constructor-arg name=\"applicationName\" value=\"BPM\" />\n        <!-- set Engine session duration (in ms) : -->\n        <property name=\"sessionDuration\" value=\"${bonita.runtime.session.duration:${bonita.tenant.session.duration:3600000}}\" />\n    </bean>\n\n    <bean id=\"queriableLogSessionProvider\" class=\"org.bonitasoft.engine.services.impl.QueriableLogSessionProviderImpl\">\n        <constructor-arg name=\"sessionService\" ref=\"sessionService\" />\n    </bean>\n\n    <bean id=\"tenantRecorderSync\" class=\"org.bonitasoft.engine.recorder.impl.RecorderImpl\" primary=\"true\">\n        <constructor-arg name=\"persistenceService\" ref=\"persistenceService\" />\n        <constructor-arg name=\"eventService\" ref=\"platformEventService\" />\n    </bean>\n\n    <bean id=\"searchEntitiesDescriptor\" class=\"org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor\">\n    </bean>\n\n    <bean id=\"containerRegistry\" class=\"org.bonitasoft.engine.execution.ContainerRegistry\">\n        <constructor-arg name=\"workService\" ref=\"workService\" />\n        <constructor-arg name=\"workFactory\" ref=\"workFactory\" />\n    </bean>\n\n    <bean id=\"parentContainerResolver\" class=\"org.bonitasoft.engine.data.ParentContainerResolverImpl\">\n        <constructor-arg name=\"flowNodeInstanceService\" ref=\"activityInstanceService\" />\n        <constructor-arg name=\"processInstanceService\" ref=\"processInstanceService\" />\n    </bean>\n\n    <bean id=\"bpmInstancesCreator\" class=\"org.bonitasoft.engine.bpm.model.impl.BPMInstancesCreator\">\n        <constructor-arg name=\"activityInstanceService\" ref=\"activityInstanceService\" />\n        <constructor-arg name=\"actorMappingService\" ref=\"actorMappingService\" />\n        <constructor-arg name=\"gatewayInstanceService\" ref=\"gatewayInstanceService\" />\n        <constructor-arg name=\"eventInstanceService\" ref=\"eventInstanceService\" />\n        <constructor-arg name=\"connectorInstanceService\" ref=\"connectorInstanceService\" />\n        <constructor-arg name=\"expressionResolverService\" ref=\"expressionResolverService\" />\n        <constructor-arg name=\"dataInstanceService\" ref=\"dataInstanceService\" />\n        <constructor-arg name=\"transientDataService\" ref=\"transientDataService\" />\n        <constructor-arg name=\"parentContainerResolver\" ref=\"parentContainerResolver\" />\n        <constructor-arg name=\"refBusinessDataService\" ref=\"refBusinessDataService\" />\n\n    </bean>\n\n    <bean id=\"waitingEventsInterrupter\" class=\"org.bonitasoft.engine.execution.WaitingEventsInterrupter\">\n        <constructor-arg name=\"eventInstanceService\" ref=\"eventInstanceService\" />\n        <constructor-arg name=\"schedulerService\" ref=\"scheduler\" />\n    </bean>\n\n    <bean id=\"processInstanceInterruptor\" class=\"org.bonitasoft.engine.execution.ProcessInstanceInterruptor\">\n        <constructor-arg name=\"processInstanceService\" ref=\"processInstanceService\" />\n        <constructor-arg name=\"flowNodeInstanceService\" ref=\"activityInstanceService\" />\n        <constructor-arg name=\"containerRegistry\" ref=\"containerRegistry\" />\n    </bean>\n\n    <bean id=\"eventsHandler\" class=\"org.bonitasoft.engine.execution.event.EventsHandler\">\n        <constructor-arg name=\"schedulerService\" ref=\"scheduler\" />\n        <constructor-arg name=\"expressionResolverService\" ref=\"expressionResolverService\" />\n        <constructor-arg name=\"eventInstanceService\" ref=\"eventInstanceService\" />\n        <constructor-arg name=\"bpmInstancesCreator\" ref=\"bpmInstancesCreator\" />\n        <constructor-arg name=\"processDefinitionService\" ref=\"processDefinitionService\" />\n        <constructor-arg name=\"containerRegistry\" ref=\"containerRegistry\" />\n        <constructor-arg name=\"processInstanceService\" ref=\"processInstanceService\" />\n        <constructor-arg name=\"flowNodeInstanceService\" ref=\"activityInstanceService\" />\n        <constructor-arg name=\"operationService\" ref=\"operationService\" />\n        <constructor-arg name=\"messagesHandlingService\" ref=\"messagesHandlingService\" />\n        <constructor-arg name=\"workService\" ref=\"workService\" />\n        <constructor-arg name=\"workFactory\" ref=\"workFactory\" />\n        <constructor-arg name=\"processInstanceInterruptor\" ref=\"processInstanceInterruptor\" />\n    </bean>\n\n\n    <bean id=\"messagesHandlingService\" class=\"org.bonitasoft.engine.message.MessagesHandlingService\">\n        <constructor-arg name=\"eventInstanceService\" ref=\"eventInstanceService\" />\n        <constructor-arg name=\"workService\" ref=\"workService\" />\n        <constructor-arg name=\"userTransactionService\" ref=\"transactionService\" />\n        <constructor-arg name=\"workFactory\" ref=\"workFactory\" />\n        <constructor-arg name=\"meterRegistry\" ref=\"meterRegistry\" />\n    </bean>\n\n    <bean id=\"transitionConditionEvaluator\"\n          class=\"org.bonitasoft.engine.execution.transition.TransitionConditionEvaluator\">\n        <constructor-arg name=\"resolverService\" ref=\"expressionResolverService\" />\n    </bean>\n\n    <bean id=\"defaultTransitionGetter\" class=\"org.bonitasoft.engine.execution.transition.DefaultTransitionGetter\" />\n\n    <bean id=\"implicitGatewayTransitionEvaluator\"\n          class=\"org.bonitasoft.engine.execution.transition.ImplicitGatewayTransitionEvaluator\">\n        <constructor-arg name=\"conditionEvaluator\" ref=\"transitionConditionEvaluator\" />\n        <constructor-arg name=\"defaultTransitionGetter\" ref=\"defaultTransitionGetter\" />\n    </bean>\n\n\n    <bean id=\"inclusiveGatewayTransitionEvaluationStrategy\"\n          class=\"org.bonitasoft.engine.execution.transition.InclusiveGatewayTransitionEvaluationStrategy\" />\n    <bean id=\"exclusiveGatewayTransitionEvaluationStrategy\"\n          class=\"org.bonitasoft.engine.execution.transition.ExclusiveGatewayTransitionEvaluationStrategy\" />\n\n    <bean id=\"inclusiveTransitionEvaluator\"\n          class=\"org.bonitasoft.engine.execution.transition.InclusiveExclusiveTransitionEvaluator\">\n        <constructor-arg name=\"strategy\" ref=\"inclusiveGatewayTransitionEvaluationStrategy\" />\n        <constructor-arg name=\"evaluator\" ref=\"transitionConditionEvaluator\" />\n        <constructor-arg name=\"defaultTransitionGetter\" ref=\"defaultTransitionGetter\" />\n\n    </bean>\n\n    <bean id=\"exclusiveTransitionEvaluator\"\n          class=\"org.bonitasoft.engine.execution.transition.InclusiveExclusiveTransitionEvaluator\">\n        <constructor-arg name=\"strategy\" ref=\"exclusiveGatewayTransitionEvaluationStrategy\" />\n        <constructor-arg name=\"evaluator\" ref=\"transitionConditionEvaluator\" />\n        <constructor-arg name=\"defaultTransitionGetter\" ref=\"defaultTransitionGetter\" />\n    </bean>\n\n    <bean id=\"parallelGatewayTransitionEvaluator\"\n          class=\"org.bonitasoft.engine.execution.transition.ParallelGatewayTransitionEvaluator\" />\n\n    <bean id=\"transitionEvaluator\" class=\"org.bonitasoft.engine.execution.TransitionEvaluator\">\n        <constructor-arg name=\"implicitGatewayTransitionEvaluator\" ref=\"implicitGatewayTransitionEvaluator\" />\n        <constructor-arg name=\"parallelGatewayTransitionEvaluator\" ref=\"parallelGatewayTransitionEvaluator\" />\n        <constructor-arg name=\"inclusiveTransitionEvaluator\" ref=\"inclusiveTransitionEvaluator\" />\n        <constructor-arg name=\"exclusiveTransitionEvaluator\" ref=\"exclusiveTransitionEvaluator\" />\n    </bean>\n\n    <bean id=\"processExecutor\" class=\"org.bonitasoft.engine.execution.ProcessExecutorImpl\">\n        <constructor-arg name=\"activityInstanceService\" ref=\"activityInstanceService\" />\n        <constructor-arg name=\"processInstanceService\" ref=\"processInstanceService\" />\n        <constructor-arg name=\"flowNodeExecutor\" ref=\"flowNodeExecutor\" />\n        <constructor-arg name=\"workService\" ref=\"workService\" />\n        <constructor-arg name=\"processDefinitionService\" ref=\"processDefinitionService\" />\n        <constructor-arg name=\"gatewayInstanceService\" ref=\"gatewayInstanceService\" />\n        <constructor-arg name=\"processResourcesService\" ref=\"processResourcesService\" />\n        <constructor-arg name=\"connectorService\" ref=\"connectorService\" />\n        <constructor-arg name=\"connectorInstanceService\" ref=\"connectorInstanceService\" />\n        <constructor-arg name=\"operationService\" ref=\"operationService\" />\n        <constructor-arg name=\"expressionResolverService\" ref=\"expressionResolverService\" />\n        <constructor-arg name=\"expressionService\" ref=\"expressionService\" />\n        <constructor-arg name=\"eventService\" ref=\"platformEventService\" />\n        <constructor-arg name=\"handlers\">\n            <map>\n                <entry key=\"PROCESSINSTANCE_STATE_UPDATED\">\n                    <bean class=\"org.bonitasoft.engine.execution.handler.ArchiveProcessInstancesHandler\" />\n                </entry>\n            </map>\n        </constructor-arg>\n        <constructor-arg name=\"documentService\" ref=\"documentService\" />\n        <constructor-arg name=\"containerRegistry\" ref=\"containerRegistry\" />\n        <constructor-arg name=\"bpmInstancesCreator\" ref=\"bpmInstancesCreator\" />\n        <constructor-arg name=\"eventsHandler\" ref=\"eventsHandler\" />\n        <constructor-arg name=\"businessDataRepository\" ref=\"businessDataRepository\" />\n        <constructor-arg name=\"refBusinessDataService\" ref=\"refBusinessDataService\" />\n        <constructor-arg name=\"transitionEvaluator\" ref=\"transitionEvaluator\" />\n        <constructor-arg name=\"contractDataService\" ref=\"contractDataService\" />\n        <constructor-arg name=\"bpmArchiverService\" ref=\"bpmArchiverService\" />\n        <constructor-arg name=\"processStarterVerifier\" ref=\"processStarterVerifierImpl\" />\n    </bean>\n\n    <bean id=\"documentReferenceExpressionExecutorStrategy\"\n          class=\"org.bonitasoft.engine.expression.DocumentReferenceExpressionExecutorStrategy\">\n        <constructor-arg name=\"documentService\" ref=\"documentService\" />\n        <constructor-arg name=\"activityInstanceService\" ref=\"activityInstanceService\" />\n    </bean>\n\n    <bean id=\"documentListReferenceExpressionExecutorStrategy\"\n          class=\"org.bonitasoft.engine.expression.DocumentListReferenceExpressionExecutorStrategy\">\n        <constructor-arg name=\"documentService\" ref=\"documentService\" />\n        <constructor-arg name=\"activityInstanceService\" ref=\"activityInstanceService\" />\n        <constructor-arg name=\"processDefinitionService\" ref=\"processDefinitionService\" />\n        <constructor-arg name=\"processInstanceService\" ref=\"processInstanceService\" />\n    </bean>\n\n    <bean id=\"dependencyResolver\" class=\"org.bonitasoft.engine.api.impl.resolver.BusinessArchiveArtifactsManager\">\n        <constructor-arg name=\"dependencyResolvers\">\n            <list>\n                <bean class=\"org.bonitasoft.engine.api.impl.resolver.ClasspathArtifactManager\">\n                    <constructor-arg name=\"dependencyService\" ref=\"dependencyService\" />\n                </bean>\n                <bean class=\"org.bonitasoft.engine.api.impl.resolver.ActorBusinessArchiveArtifactManager\">\n                    <constructor-arg name=\"actorMappingService\" ref=\"actorMappingService\" />\n                    <constructor-arg name=\"identityService\" ref=\"identityService\" />\n                </bean>\n                <bean class=\"org.bonitasoft.engine.api.impl.resolver.ConnectorBusinessArchiveArtifactManager\">\n                    <constructor-arg name=\"connectorService\" ref=\"connectorService\" />\n                </bean>\n                <bean class=\"org.bonitasoft.engine.api.impl.resolver.UserFilterBusinessArchiveArtifactManager\">\n                    <constructor-arg name=\"userFilterService\" ref=\"userFilterService\" />\n                    <constructor-arg name=\"processResourcesService\" ref=\"processResourcesService\" />\n                </bean>\n                <bean class=\"org.bonitasoft.engine.api.impl.resolver.ParameterBusinessArchiveArtifactManager\">\n                    <constructor-arg name=\"parameterService\" ref=\"parameterService\" />\n                </bean>\n                <bean class=\"org.bonitasoft.engine.api.impl.resolver.FormMappingAndPageArtifactManager\">\n                    <constructor-arg name=\"sessionService\" ref=\"sessionService\" />\n                    <constructor-arg name=\"pageService\" ref=\"pageService\" />\n                    <constructor-arg name=\"formMappingService\" ref=\"formMappingService\" />\n                    <constructor-arg name=\"processDefinitionService\" ref=\"processDefinitionService\" />\n                </bean>\n                <bean class=\"org.bonitasoft.engine.api.impl.resolver.BusinessDataBusinessArchiveArtifactManager\">\n                    <constructor-arg name=\"businessDataRepository\" ref=\"businessDataRepository\" />\n                </bean>\n                <bean class=\"org.bonitasoft.engine.api.impl.resolver.DocumentInitialValueArtifactManager\">\n                    <constructor-arg name=\"processResourcesService\" ref=\"processResourcesService\" />\n                </bean>\n                <bean class=\"org.bonitasoft.engine.api.impl.resolver.ExternalResourceArtifactManager\">\n                    <constructor-arg name=\"processResourcesService\" ref=\"processResourcesService\" />\n                </bean>\n            </list>\n        </constructor-arg>\n    </bean>\n\n    <bean id=\"processResourcesService\" class=\"org.bonitasoft.engine.resources.ProcessResourcesServiceImpl\">\n        <constructor-arg name=\"persistenceService\" ref=\"persistenceService\" />\n        <constructor-arg name=\"recorder\" ref=\"tenantRecorderSync\" />\n    </bean>\n\n    <bean id=\"tenantResourcesService\" class=\"org.bonitasoft.engine.resources.TenantResourcesServiceImpl\">\n        <constructor-arg name=\"persistenceService\" ref=\"persistenceService\" />\n        <constructor-arg name=\"recorder\" ref=\"tenantRecorderSync\" />\n    </bean>\n\n    <bean id=\"businessArchiveService\" class=\"org.bonitasoft.engine.bar.BusinessArchiveServiceImpl\">\n        <constructor-arg name=\"processDefinitionService\" ref=\"processDefinitionService\" />\n        <constructor-arg name=\"businessArchiveArtifactsManager\" ref=\"dependencyResolver\" />\n    </bean>\n\n    <bean id=\"commandService\" class=\"org.bonitasoft.engine.command.api.impl.CommandServiceImpl\">\n        <constructor-arg name=\"eventService\" ref=\"platformEventService\" />\n        <constructor-arg name=\"persistenceService\" ref=\"persistenceService\" />\n        <constructor-arg name=\"recorder\" ref=\"tenantRecorderSync\" />\n        <constructor-arg name=\"queriableLoggerService\" ref=\"queriableLoggerService\" />\n        <constructor-arg name=\"defaultCommandProvider\" ref=\"defaultCommandProvider\" />\n    </bean>\n\n    <bean id=\"communityDefaultCommands\" class=\"org.springframework.beans.factory.config.ListFactoryBean\">\n        <property name=\"sourceList\">\n            <list>\n                <ref bean=\"searchSCommentSupervisedBy\" />\n                <ref bean=\"searchWaitingEventsCommand\" />\n                <ref bean=\"advancedStartProcessCommand\" />\n                <ref bean=\"executeBDMQuery\" />\n                <ref bean=\"getBusinessDataByIdCommand\" />\n                <ref bean=\"getBusinessDataByIdsCommand\" />\n                <ref bean=\"getBusinessDataByQueryCommand\" />\n                <ref bean=\"multipleStartPointsProcessCommand\" />\n            </list>\n        </property>\n    </bean>\n\n    <bean id=\"commandsToDeployAtStartup\" parent=\"communityDefaultCommands\"\n          class=\"org.springframework.beans.factory.config.ListFactoryBean\">\n        <property name=\"sourceList\">\n            <list merge=\"true\">\n            </list>\n        </property>\n    </bean>\n\n\n    <bean id=\"defaultCommandProvider\" class=\"org.bonitasoft.engine.command.api.impl.CommandProvider\">\n        <property name=\"defaultCommands\" ref=\"commandsToDeployAtStartup\" />\n    </bean>\n\n    <bean id=\"executeBDMQuery\" class=\"org.bonitasoft.engine.command.api.impl.CommandDeployment\">\n        <constructor-arg name=\"name\">\n            <value>executeBDMQuery</value>\n        </constructor-arg>\n        <constructor-arg name=\"description\">\n            <value>Execute a named query in the BDM. Use parameter keys : \"queryName\" the name of the query in the bdm,\n                \"returnType\" the query expected return type, \"returnsList\" if result is a List or a single value,\n                \"queryParameters\" a Map to value query parameters\n            </value>\n        </constructor-arg>\n        <constructor-arg name=\"implementation\">\n            <value>org.bonitasoft.engine.command.ExecuteBDMQueryCommand</value>\n        </constructor-arg>\n    </bean>\n\n    <!-- Used in bonita-web org.bonitasoft.web.rest.server.datastore.bpm.cases.CommentDatastore#runSupervisorSearch -->\n    <bean id=\"searchSCommentSupervisedBy\" class=\"org.bonitasoft.engine.command.api.impl.CommandDeployment\">\n        <constructor-arg name=\"name\">\n            <value>searchSCommentSupervisedBy</value>\n        </constructor-arg>\n        <constructor-arg name=\"description\">\n            <value>Returns the list of comments for processes supervised by a user. One mandatory parameter:\n                supervisorId: ID of the user supervisor of the processes on\n                which comments are.\n            </value>\n        </constructor-arg>\n        <constructor-arg name=\"implementation\">\n            <value>org.bonitasoft.engine.external.comment.SearchCommentsSupervisedBy</value>\n        </constructor-arg>\n    </bean>\n\n    <!-- Used in tests only, TODO: Remove it-->\n    <bean id=\"searchWaitingEventsCommand\" class=\"org.bonitasoft.engine.command.api.impl.CommandDeployment\">\n        <constructor-arg name=\"name\">\n            <value>searchWaitingEventsCommand</value>\n        </constructor-arg>\n        <constructor-arg name=\"description\">\n            <value>Search waiting events. Parameter keys: searchOptions.</value>\n        </constructor-arg>\n        <constructor-arg name=\"implementation\">\n            <value>org.bonitasoft.engine.command.system.SearchWaitingEventsCommand</value>\n        </constructor-arg>\n    </bean>\n\n    <bean id=\"advancedStartProcessCommand\" class=\"org.bonitasoft.engine.command.api.impl.CommandDeployment\">\n        <constructor-arg name=\"name\">\n            <value>advancedStartProcessCommand</value>\n        </constructor-arg>\n        <constructor-arg name=\"description\">\n            <value>Advanced start process.</value>\n        </constructor-arg>\n        <constructor-arg name=\"implementation\">\n            <value>org.bonitasoft.engine.command.AdvancedStartProcessCommand</value>\n        </constructor-arg>\n    </bean>\n\n    <bean id=\"getBusinessDataByIdCommand\" class=\"org.bonitasoft.engine.command.api.impl.CommandDeployment\">\n        <constructor-arg name=\"name\">\n            <value>getBusinessDataById</value>\n        </constructor-arg>\n        <constructor-arg name=\"description\">\n            <value>Get the business data via its identifier and class name, and returns its Json representation.</value>\n        </constructor-arg>\n        <constructor-arg name=\"implementation\">\n            <value>org.bonitasoft.engine.command.GetBusinessDataByIdCommand</value>\n        </constructor-arg>\n    </bean>\n\n    <bean id=\"getBusinessDataByIdsCommand\" class=\"org.bonitasoft.engine.command.api.impl.CommandDeployment\">\n        <constructor-arg name=\"name\">\n            <value>getBusinessDataByIds</value>\n        </constructor-arg>\n        <constructor-arg name=\"description\">\n            <value>Get the business data via its identifiers and class name, and returns its Json representation.\n            </value>\n        </constructor-arg>\n        <constructor-arg name=\"implementation\">\n            <value>org.bonitasoft.engine.command.GetBusinessDataByIdsCommand</value>\n        </constructor-arg>\n    </bean>\n\n    <bean id=\"getBusinessDataByQueryCommand\" class=\"org.bonitasoft.engine.command.api.impl.CommandDeployment\">\n        <constructor-arg name=\"name\">\n            <value>getBusinessDataByQueryCommand</value>\n        </constructor-arg>\n        <constructor-arg name=\"description\">\n            <value>Execute a named query in the BDM, and returns its Json representation. Use parameter keys :\n                \"queryName\" the name of the query in the bdm, \"returnType\" the query expected return type, \"returnsList\"\n                if result is a List or a single value, \"queryParameters\" a Map to value query parameters\n            </value>\n        </constructor-arg>\n        <constructor-arg name=\"implementation\">\n            <value>org.bonitasoft.engine.command.GetBusinessDataByQueryCommand</value>\n        </constructor-arg>\n    </bean>\n\n\n    <bean id=\"multipleStartPointsProcessCommand\" class=\"org.bonitasoft.engine.command.api.impl.CommandDeployment\">\n        <constructor-arg name=\"name\">\n            <value>multipleStartPointsProcessCommand</value>\n        </constructor-arg>\n        <constructor-arg name=\"description\">\n            <value>Advanced start process using multiple start points.</value>\n        </constructor-arg>\n        <constructor-arg name=\"implementation\">\n            <value>org.bonitasoft.engine.command.MultipleStartPointsProcessCommand</value>\n        </constructor-arg>\n    </bean>\n\n    <bean id=\"expressionService\" class=\"org.bonitasoft.engine.expression.impl.ExpressionServiceImpl\">\n        <constructor-arg name=\"checkExpressionReturnType\" value=\"true\" />\n        <constructor-arg name=\"timeTracker\" ref=\"timeTracker\" />\n    </bean>\n\n    <bean id=\"expressionExecutorStrategyProvider\"\n          class=\"org.bonitasoft.engine.expression.ExpressionExecutorStrategyProvider\" autowire=\"byType\">\n    </bean>\n\n    <bean id=\"serverLazyLoader\" class=\"org.bonitasoft.engine.business.data.proxy.ServerLazyLoader\">\n        <constructor-arg name=\"bdBusinessDataRepository\" ref=\"businessDataRepository\" />\n    </bean>\n\n    <bean id=\"serverProxyfier\" class=\"org.bonitasoft.engine.business.data.proxy.ServerProxyfier\">\n        <constructor-arg name=\"lazyLoader\" ref=\"serverLazyLoader\" />\n    </bean>\n\n    <bean id=\"businessDataRetriever\" class=\"org.bonitasoft.engine.business.data.BusinessDataRetriever\">\n        <constructor-arg name=\"businessDataRepository\" ref=\"businessDataRepository\" />\n        <constructor-arg name=\"proxyfier\" ref=\"serverProxyfier\" />\n    </bean>\n\n    <bean id=\"businessDataExpressionExecutorStrategy\"\n          class=\"org.bonitasoft.engine.expression.BusinessDataExpressionExecutorStrategy\">\n        <constructor-arg name=\"refBusinessDataRetriever\" ref=\"refBusinessDataRetriever\" />\n        <constructor-arg name=\"businessDataRetriever\" ref=\"businessDataRetriever\" />\n    </bean>\n\n    <bean id=\"businessObjectDAOExpressionStrategy\"\n          class=\"org.bonitasoft.engine.expression.BusinessObjectDAOExpressionStrategy\">\n        <constructor-arg name=\"businessDataRepository\" ref=\"businessDataRepository\" />\n    </bean>\n\n    <bean id=\"queryBusinessDataExpressionExecutorStrategy\"\n          class=\"org.bonitasoft.engine.expression.QueryBusinessDataExpressionExecutorStrategy\">\n        <constructor-arg name=\"businessDataRepository\" ref=\"businessDataRepository\" />\n    </bean>\n\n    <bean id=\"constantExpressionExecutorStrategy\"\n          class=\"org.bonitasoft.engine.expression.impl.ConstantExpressionExecutorStrategy\" />\n\n    <bean id=\"groovyScriptExpressionExecutorStrategy\"\n          class=\"org.bonitasoft.engine.expression.impl.GroovyScriptExpressionExecutorCacheStrategy\">\n    </bean>\n\n    <bean id=\"groovyScriptConditionExpressionExecutorStrategy\"\n          class=\"org.bonitasoft.engine.expression.impl.GroovyScriptConditionExpressionExecutorStrategy\">\n    </bean>\n\n    <bean id=\"inputExpressionExecutorStrategy\"\n          class=\"org.bonitasoft.engine.expression.impl.InputExpressionExecutorStrategy\" />\n\n    <bean id=\"patternExpressionExecutorStrategy\"\n          class=\"org.bonitasoft.engine.expression.impl.PatternExpressionExecutorStrategy\" />\n\n    <bean id=\"xPathReadExpressionExecutorStrategy\"\n          class=\"org.bonitasoft.engine.expression.impl.XPathReadExpressionExecutorStrategy\" />\n\n    <bean id=\"engineConstantExpressionExecutorStrategy\"\n          class=\"org.bonitasoft.engine.expression.EngineConstantExpressionExecutorStrategy\">\n        <constructor-arg name=\"processInstanceService\" ref=\"processInstanceService\" />\n        <constructor-arg name=\"activityInstanceService\" ref=\"activityInstanceService\" />\n        <constructor-arg name=\"sessionService\" ref=\"sessionService\" />\n    </bean>\n\n    <bean id=\"contractInputExpressionExecutorStrategy\"\n          class=\"org.bonitasoft.engine.expression.ContractInputExpressionExecutorStrategy\">\n        <constructor-arg name=\"contractDataService\" ref=\"contractDataService\" />\n    </bean>\n\n    <bean id=\"listExpressionExecutorStrategy\"\n          class=\"org.bonitasoft.engine.expression.impl.ListExpressionExecutorStrategy\" />\n\n\n    <bean id=\"businessDataExpressionReferenceExecutorStrategy\"\n          class=\"org.bonitasoft.engine.expression.BusinessDataReferenceExpressionExecutorStrategy\">\n        <constructor-arg name=\"refBusinessDataRetriever\" ref=\"refBusinessDataRetriever\" />\n    </bean>\n\n    <bean id=\"equalityComparator\" class=\"org.bonitasoft.engine.expression.impl.condition.EqualityComparator\" />\n    <bean id=\"inequalityComparator\" class=\"org.bonitasoft.engine.expression.impl.condition.InequalityComparator\" />\n    <bean id=\"binaryComparatorMapper\" class=\"org.bonitasoft.engine.expression.impl.condition.BinaryComparatorMapper\">\n        <constructor-arg name=\"equalityComparator\" ref=\"equalityComparator\" />\n        <constructor-arg name=\"inequalityComparator\" ref=\"inequalityComparator\" />\n    </bean>\n\n    <bean id=\"binaryComparatorExecutor\"\n          class=\"org.bonitasoft.engine.expression.impl.condition.BinaryComparatorExecutor\">\n        <constructor-arg name=\"mapper\" ref=\"binaryComparatorMapper\" />\n    </bean>\n\n    <bean id=\"logicalComplementExecutor\"\n          class=\"org.bonitasoft.engine.expression.impl.condition.LogicalComplementExecutor\" />\n\n    <bean id=\"conditionExpressionExecutorStrategy\"\n          class=\"org.bonitasoft.engine.expression.impl.ConditionExpressionExecutorStrategy\">\n        <constructor-arg name=\"logicalComplementExecutor\" ref=\"logicalComplementExecutor\" />\n        <constructor-arg name=\"binaryComparatorExecutor\" ref=\"binaryComparatorExecutor\" />\n    </bean>\n\n    <bean id=\"javaMethodCallExpressionExecutorStrategy\"\n          class=\"org.bonitasoft.engine.expression.impl.JavaMethodCallExpressionExecutorStrategy\" />\n\n    <bean id=\"parameterExpressionExecutorStrategy\"\n          class=\"org.bonitasoft.engine.expression.ParameterExpressionExecutorStrategy\">\n        <constructor-arg name=\"parameterService\" ref=\"parameterService\" />\n    </bean>\n\n    <bean id=\"parameterService\" class=\"org.bonitasoft.engine.parameter.ParameterServiceImpl\">\n        <constructor-arg name=\"persistenceService\" ref=\"persistenceService\" />\n        <constructor-arg name=\"recorder\" ref=\"tenantRecorderSync\" />\n    </bean>\n\n    <bean id=\"queriableLoggerStrategy\" class=\"org.bonitasoft.engine.services.impl.SimpleQueriableLoggerStrategy\" />\n\n    <bean id=\"workFactory\" class=\"org.bonitasoft.engine.execution.work.BPMWorkFactory\" />\n\n\n    <bean id=\"exceptionRetryabilityEvaluator\" class=\"org.bonitasoft.engine.work.DefaultExceptionRetryabilityEvaluator\">\n        <constructor-arg name=\"exceptionClassesToRetry\">\n            <list>\n                <value>org.bonitasoft.engine.commons.exceptions.SRetryableException</value>\n\n                <!-- Related to concurrent updates -->\n                <value>org.hibernate.StaleStateException</value>\n                <value>org.hibernate.exception.LockAcquisitionException</value>\n                <value>javax.persistence.LockTimeoutException</value>\n                <value>javax.persistence.PessimisticLockException</value>\n                <value>javax.persistence.OptimisticLockException</value>\n                <value>org.hibernate.dialect.lock.LockingStrategyException</value>\n                <value>java.sql.SQLRecoverableException</value>\n                <value>java.sql.SQLTransientException</value>\n                <value>org.springframework.dao.TransientDataAccessException</value>\n                <value>org.springframework.dao.RecoverableDataAccessException</value>\n                <value>org.hibernate.exception.JDBCConnectionException</value>\n\n                <!-- Exceptions below might be useful on Microsoft Sql Server when data are not available directly after write -->\n                <!--<value>org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityInstanceNotFoundException</value>-->\n                <!--<value>org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException</value>-->\n                <!--<value>org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceNotFoundException</value>-->\n                <!--<value>org.bonitasoft.engine.business.data.SBusinessDataNotFoundException</value>-->\n                <!--<value>org.bonitasoft.engine.core.connector.exception.SConnectorInstanceNotFoundException</value>-->\n            </list>\n        </constructor-arg>\n        <constructor-arg name=\"exceptionClassesToNotRetry\">\n            <list>\n                <!-- Will not retry issues coming from extensions like connectors -->\n                <value>org.bonitasoft.engine.connector.exception.SConnectorException</value>\n            </list>\n        </constructor-arg>\n    </bean>\n\n\n    <bean id=\"refBusinessDataRetriever\" class=\"org.bonitasoft.engine.business.data.RefBusinessDataRetriever\">\n        <constructor-arg name=\"refBusinessDataService\" ref=\"refBusinessDataService\" />\n        <constructor-arg name=\"flowNodeInstanceService\" ref=\"activityInstanceService\" />\n        <constructor-arg name=\"processInstanceService\" ref=\"processInstanceService\" />\n    </bean>\n\n    <bean id=\"updateDataRefAction\" class=\"org.bonitasoft.engine.operation.UpdateDataRefAction\">\n        <constructor-arg name=\"refBusinessDataService\" ref=\"refBusinessDataService\" />\n        <constructor-arg name=\"refBusinessDataRetriever\" ref=\"refBusinessDataRetriever\" />\n    </bean>\n\n\n    <bean id=\"mergeEntityAction\" class=\"org.bonitasoft.engine.operation.MergeEntityAction\">\n        <constructor-arg name=\"repository\" ref=\"businessDataRepository\" />\n\n    </bean>\n\n    <bean id=\"entitiesActionsExecutor\" class=\"org.bonitasoft.engine.operation.EntitiesActionsExecutor\" />\n\n\n    <bean id=\"actorMappingService\" class=\"org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl\">\n        <constructor-arg name=\"persistenceService\" ref=\"persistenceService\" />\n        <constructor-arg name=\"recorder\" ref=\"tenantRecorderSync\" />\n        <constructor-arg name=\"eventService\" ref=\"platformEventService\" />\n        <constructor-arg name=\"queriableLoggerService\" ref=\"queriableLoggerService\" />\n        <constructor-arg name=\"identityService\" ref=\"identityService\" />\n    </bean>\n\n\n    <bean id=\"userFilterService\" class=\"org.bonitasoft.engine.userfilter.UserFilterServiceDecorator\">\n        <constructor-arg name=\"userFilterService\" ref=\"userFilterServiceSub\" />\n    </bean>\n\n    <bean id=\"userFilterServiceSub\" class=\"org.bonitasoft.engine.core.filter.impl.UserFilterServiceImpl\">\n        <constructor-arg name=\"processResourcesService\" ref=\"processResourcesService\" />\n        <constructor-arg name=\"expressionResolverService\" ref=\"expressionResolverService\" />\n    </bean>\n\n\n    <bean id=\"defaultFlushEventListeners\" class=\"org.springframework.beans.factory.config.ListFactoryBean\">\n        <property name=\"sourceList\">\n            <list>\n                <ref bean=\"csvFlushEventListener\" />\n                <ref bean=\"memoryFlushEventListener\" />\n            </list>\n        </property>\n    </bean>\n\n    <bean id=\"flushEventListeners\" parent=\"defaultFlushEventListeners\"\n          class=\"org.springframework.beans.factory.config.ListFactoryBean\">\n        <property name=\"sourceList\">\n            <list merge=\"true\">\n            </list>\n        </property>\n    </bean>\n\n    <bean id=\"defaultActivatedRecords\" class=\"org.springframework.beans.factory.config.ListFactoryBean\">\n        <property name=\"sourceList\">\n            <list>\n                <value>EXECUTE_CONNECTOR_INCLUDING_POOL_SUBMIT</value>\n                <value>EXECUTE_CONNECTOR_CALLABLE</value>\n                <value>EXECUTE_CONNECTOR_OUTPUT_OPERATIONS</value>\n                <value>EXECUTE_CONNECTOR_INPUT_EXPRESSIONS</value>\n                <value>EXECUTE_CONNECTOR_DISCONNECT</value>\n                <value>EXECUTE_CONNECTOR_WORK</value>\n                <value>EVALUATE_EXPRESSION_INCLUDING_CONTEXT</value>\n                <value>EVALUATE_EXPRESSION</value>\n                <value>EVALUATE_EXPRESSIONS</value>\n            </list>\n        </property>\n    </bean>\n\n    <bean id=\"activatedRecords\" parent=\"defaultActivatedRecords\"\n          class=\"org.springframework.beans.factory.config.ListFactoryBean\">\n        <property name=\"sourceList\">\n            <list merge=\"true\">\n            </list>\n        </property>\n    </bean>\n\n    <bean id=\"timeTracker\" class=\"org.bonitasoft.engine.tracking.TimeTracker\">\n        <constructor-arg name=\"startTracking\" value=\"${bonita.tenant.timetracker.startTracking}\" />\n        <constructor-arg name=\"flushEventListeners\" ref=\"flushEventListeners\" />\n        <constructor-arg name=\"maxSize\" value=\"${bonita.tenant.timetracker.maxSize}\" />\n        <constructor-arg name=\"flushIntervalInSeconds\"\n                         value=\"${bonita.tenant.timetracker.flushIntervalInSeconds}\" />\n        <constructor-arg name=\"activatedRecords\" ref=\"activatedRecords\" />\n    </bean>\n\n    <bean id=\"csvFlushEventListener\" class=\"org.bonitasoft.engine.tracking.csv.CSVFlushEventListener\">\n        <constructor-arg name=\"activateAtStart\" value=\"${bonita.tenant.timetracker.csv.activateAtStart}\" />\n        <constructor-arg name=\"outputFolder\" value=\"${bonita.tenant.timetracker.csv.folder}\" />\n        <constructor-arg name=\"csvSeparator\" value=\"${bonita.tenant.timetracker.csv.separator}\" />\n    </bean>\n\n    <bean id=\"memoryFlushEventListener\" class=\"org.bonitasoft.engine.tracking.memory.MemoryFlushEventListener\">\n        <constructor-arg name=\"activateAtStart\" value=\"${bonita.tenant.timetracker.memory.activateAtStart}\" />\n        <constructor-arg name=\"maxSize\" value=\"${bonita.tenant.timetracker.memory.maxSize}\" />\n    </bean>\n\n    <task:scheduled-tasks scheduler=\"internalTasksScheduler\">\n        <task:scheduled ref=\"sessionService\" method=\"cleanInvalidSessions\" cron=\"${org.bonitasoft.engine.clean.invalid.sessions.cron:0 0 */2 * * ?}\" />\n    </task:scheduled-tasks>\n    <task:annotation-driven />\n\n    <task:scheduler id=\"internalTasksScheduler\" />\n\n\n    <bean id=\"supervisorMappingService\"\n          class=\"org.bonitasoft.engine.supervisor.mapping.impl.SupervisorMappingServiceImpl\">\n        <constructor-arg name=\"persistenceService\" ref=\"persistenceService\" />\n        <constructor-arg name=\"recorder\" ref=\"tenantRecorderSync\" />\n        <constructor-arg name=\"eventService\" ref=\"platformEventService\" />\n        <constructor-arg name=\"queriableLoggerService\" ref=\"queriableLoggerService\" />\n    </bean>\n\n\n    <bean id=\"profileService\" class=\"org.bonitasoft.engine.profile.impl.ProfileServiceImpl\">\n        <constructor-arg name=\"persistenceService\" ref=\"persistenceService\" />\n        <constructor-arg name=\"recorder\" ref=\"tenantRecorderSync\" />\n        <constructor-arg name=\"queriableLoggerService\" ref=\"queriableLoggerService\" />\n        <constructor-arg name=\"sessionService\" ref=\"sessionService\" />\n    </bean>\n\n    <bean id=\"profilesParser\" class=\"org.bonitasoft.engine.profile.ProfilesParser\">\n    </bean>\n\n\n    <bean id=\"activityInstanceService\"\n          class=\"org.bonitasoft.engine.core.process.instance.impl.ActivityInstanceServiceImpl\">\n        <constructor-arg name=\"recorder\" ref=\"tenantRecorderSync\" />\n        <constructor-arg name=\"persistenceService\" ref=\"persistenceService\" />\n        <constructor-arg name=\"archiveService\" ref=\"archiveService\" />\n    </bean>\n\n    <bean id=\"processInstanceService\"\n          class=\"org.bonitasoft.engine.core.process.instance.impl.ProcessInstanceServiceImpl\">\n        <constructor-arg name=\"recorder\" ref=\"tenantRecorderSync\" />\n        <constructor-arg name=\"persistenceRead\" ref=\"persistenceService\" />\n        <constructor-arg name=\"activityService\" ref=\"activityInstanceService\" />\n        <constructor-arg name=\"bpmEventInstanceService\" ref=\"eventInstanceService\" />\n        <constructor-arg name=\"dataInstanceService\" ref=\"dataInstanceService\" />\n        <constructor-arg name=\"archiveService\" ref=\"archiveService\" />\n        <constructor-arg name=\"processDefinitionService\" ref=\"processDefinitionService\" />\n        <constructor-arg name=\"connectorInstanceService\" ref=\"connectorInstanceService\" />\n        <constructor-arg name=\"documentService\" ref=\"documentService\" />\n        <constructor-arg name=\"commentService\" ref=\"commentService\" />\n        <constructor-arg name=\"refBusinessDataService\" ref=\"refBusinessDataService\" />\n        <constructor-arg name=\"contractDataService\" ref=\"contractDataService\" />\n    </bean>\n    <bean id=\"gatewayInstanceService\"\n          class=\"org.bonitasoft.engine.core.process.instance.impl.GatewayInstanceServiceImpl\">\n        <constructor-arg name=\"recorder\" ref=\"tenantRecorderSync\" />\n        <constructor-arg name=\"persistenceRead\" ref=\"persistenceService\" />\n        <constructor-arg name=\"flowNodeInstanceService\" ref=\"activityInstanceService\" />\n    </bean>\n\n    <bean id=\"eventInstanceRepository\"\n          class=\"org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl\">\n        <constructor-arg name=\"recorder\" ref=\"tenantRecorderSync\" />\n        <constructor-arg name=\"eventService\" ref=\"platformEventService\" />\n        <constructor-arg name=\"persistenceService\" ref=\"persistenceService\" />\n        <constructor-arg name=\"archiveService\" ref=\"archiveService\" />\n    </bean>\n\n    <bean id=\"eventInstanceService\"\n          class=\"org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceServiceImpl\">\n        <constructor-arg name=\"eventInstanceRepository\" ref=\"eventInstanceRepository\" />\n        <constructor-arg name=\"meterRegistry\" ref=\"meterRegistry\" />\n    </bean>\n\n\n    <bean id=\"refBusinessDataService\"\n          class=\"org.bonitasoft.engine.core.process.instance.impl.RefBusinessDataServiceImpl\">\n        <constructor-arg name=\"recorder\" ref=\"tenantRecorderSync\" />\n        <constructor-arg name=\"persistenceRead\" ref=\"persistenceService\" />\n        <constructor-arg name=\"eventService\" ref=\"platformEventService\" />\n        <constructor-arg name=\"queriableLoggerService\" ref=\"queriableLoggerService\" />\n        <constructor-arg name=\"archiveService\" ref=\"archiveService\" />\n    </bean>\n\n    <bean id=\"processDefinitionService\"\n          class=\"org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl\">\n        <constructor-arg name=\"persistenceService\" ref=\"persistenceService\" />\n        <constructor-arg name=\"recorder\" ref=\"tenantRecorderSync\" />\n        <constructor-arg name=\"sessionService\" ref=\"sessionService\" />\n        <constructor-arg name=\"queriableLoggerService\" ref=\"queriableLoggerService\" />\n    </bean>\n\n\n    <bean id=\"commentService\" class=\"org.bonitasoft.engine.core.process.comment.api.impl.SCommentServiceImpl\">\n        <constructor-arg name=\"recorder\" ref=\"tenantRecorderSync\" />\n        <constructor-arg name=\"sessionService\" ref=\"sessionService\" />\n        <constructor-arg name=\"persistenceService\" ref=\"persistenceService\" />\n        <constructor-arg name=\"archiveService\" ref=\"archiveService\" />\n        <constructor-arg name=\"systemCommentType\">\n            <map>\n                <entry key=\"STATE_CHANGE\" value=\"true\" />\n            </map>\n        </constructor-arg>\n    </bean>\n\n    <bean id=\"apiExtensionPageServiceListener\"\n          class=\"org.bonitasoft.engine.page.impl.ApiExtensionPageServiceListenerImpl\">\n        <constructor-arg name=\"pageMappingService\" ref=\"pageMappingService\" />\n    </bean>\n\n    <bean id=\"genericAuthenticationServiceAccessor\"\n          class=\"org.bonitasoft.engine.authentication.GenericAuthenticationServiceAccessor\">\n        <constructor-arg ref=\"${authentication.service.ref.name:authenticationService}\" />\n    </bean>\n\n    <!--  Used to inject the bean referenced by property 'authentication.service.ref.name' in SecuredLoginServiceImpl: -->\n    <alias name=\"${authentication.service.ref.name:authenticationService}\" alias=\"entryPointAuthenticationService\"  />\n\n    <bean id=\"expressionResolverService\"\n          class=\"org.bonitasoft.engine.core.expression.control.api.impl.ExpressionResolverServiceImpl\">\n        <constructor-arg name=\"expressionService\" ref=\"expressionService\" />\n        <constructor-arg name=\"processDefinitionService\" ref=\"processDefinitionService\" />\n        <constructor-arg name=\"timeTracker\" ref=\"timeTracker\" />\n    </bean>\n\n    <bean id=\"documentService\" class=\"org.bonitasoft.engine.core.document.api.impl.DocumentServiceImpl\">\n        <constructor-arg name=\"recorder\" ref=\"tenantRecorderSync\" />\n        <constructor-arg name=\"persistenceService\" ref=\"persistenceService\" />\n        <constructor-arg name=\"urlProvider\" ref=\"documentURLProvider\" />\n        <constructor-arg name=\"archiveService\" ref=\"archiveService\" />\n    </bean>\n\n    <bean id=\"documentHelper\" class=\"org.bonitasoft.engine.core.document.api.impl.DocumentHelper\">\n        <constructor-arg name=\"documentService\" ref=\"documentService\" />\n        <constructor-arg name=\"processDefinitionService\" ref=\"processDefinitionService\" />\n        <constructor-arg name=\"processInstanceService\" ref=\"processInstanceService\" />\n    </bean>\n\n    <bean name=\"documentURLProvider\"\n          class=\"org.bonitasoft.engine.core.document.api.impl.SDocumentDownloadURLProviderImpl\">\n        <constructor-arg name=\"servletUrl\" value=\"${document.servlet.url}\" />\n    </bean>\n\n    <bean id=\"dependencyService\" class=\"org.bonitasoft.engine.dependency.impl.TenantDependencyService\">\n        <constructor-arg name=\"persistenceService\" ref=\"persistenceService\" />\n        <constructor-arg name=\"recorder\" ref=\"tenantRecorderSync\" />\n        <constructor-arg name=\"queriableLoggerService\" ref=\"queriableLoggerService\" />\n    </bean>\n\n    <bean id=\"transientDataService\" class=\"org.bonitasoft.engine.core.data.instance.impl.TransientDataServiceImpl\">\n        <constructor-arg name=\"expressionResolverService\" ref=\"expressionResolverService\" />\n        <constructor-arg name=\"flowNodeInstanceService\" ref=\"activityInstanceService\" />\n        <constructor-arg name=\"processDefinitionService\" ref=\"processDefinitionService\" />\n    </bean>\n\n    <bean id=\"transientDataExpressionExecutorStrategy\"\n          class=\"org.bonitasoft.engine.core.data.instance.impl.TransientDataExpressionExecutorStrategy\"\n          autowire=\"byType\">\n        <constructor-arg name=\"transientDataService\" ref=\"transientDataService\" />\n    </bean>\n\n    <bean id=\"dataInstanceService\" class=\"org.bonitasoft.engine.data.instance.api.impl.DataInstanceServiceImpl\">\n        <constructor-arg name=\"recorder\" ref=\"tenantRecorderSync\" />\n        <constructor-arg name=\"persistenceService\" ref=\"persistenceService\" />\n        <constructor-arg name=\"archiveService\" ref=\"archiveService\" />\n    </bean>\n\n    <bean id=\"dataExpressionExecutorStrategy\" class=\"org.bonitasoft.engine.expression.DataExpressionExecutorStrategy\">\n        <constructor-arg name=\"dataService\" ref=\"dataInstanceService\" />\n        <constructor-arg name=\"parentContainerResolver\" ref=\"parentContainerResolver\" />\n    </bean>\n\n    <bean id=\"connectorServiceSub\" class=\"org.bonitasoft.engine.core.connector.impl.ConnectorServiceImpl\">\n        <constructor-arg name=\"expressionResolverService\" ref=\"expressionResolverService\" />\n        <constructor-arg name=\"operationService\" ref=\"operationService\" />\n        <constructor-arg name=\"dependencyService\" ref=\"dependencyService\" />\n        <constructor-arg name=\"timeTracker\" ref=\"timeTracker\" />\n        <constructor-arg name=\"processResourcesService\" ref=\"processResourcesService\" />\n    </bean>\n\n    <bean id=\"connectorService\" class=\"org.bonitasoft.engine.connector.ConnectorServiceDecorator\">\n        <constructor-arg name=\"connectorService\" ref=\"connectorServiceSub\" />\n    </bean>\n\n    <bean id=\"connectorTimeExecutionLogger\" class=\"org.bonitasoft.engine.core.connector.impl.ConnectorExecutionTimeLogger\">\n        <constructor-arg name=\"warnWhenLongerThanMillis\" value=\"${bonita.tenant.connector.warnWhenLongerThanMillis}\" />\n    </bean>\n\n    <bean id=\"connectorInstanceService\" class=\"org.bonitasoft.engine.core.connector.impl.ConnectorInstanceServiceImpl\">\n        <constructor-arg name=\"persistenceService\" ref=\"persistenceService\" />\n        <constructor-arg name=\"recorder\" ref=\"tenantRecorderSync\" />\n        <constructor-arg name=\"archiveService\" ref=\"archiveService\" />\n    </bean>\n\n    <bean id=\"categoryService\" class=\"org.bonitasoft.engine.core.category.impl.CategoryServiceImpl\">\n        <constructor-arg name=\"persistenceService\" ref=\"persistenceService\" />\n        <constructor-arg name=\"recorder\" ref=\"tenantRecorderSync\" />\n        <constructor-arg name=\"eventService\" ref=\"platformEventService\" />\n        <constructor-arg name=\"sessionService\" ref=\"sessionService\" />\n        <constructor-arg name=\"queriableLoggerService\" ref=\"queriableLoggerService\" />\n    </bean>\n\n\n    <bean id=\"bdmDatasource\" class=\"org.springframework.jndi.JndiObjectFactoryBean\">\n        <property name=\"jndiName\" value=\"${bdm.datasource.jndi.path}\" />\n    </bean>\n\n    <bean id=\"bdmNonXaDatasource\" class=\"org.springframework.jndi.JndiObjectFactoryBean\">\n        <property name=\"jndiName\" value=\"${bdm.notmanageddatasource.jndi.path}\" />\n    </bean>\n\n    <bean id=\"businessDataRepository\" class=\"org.bonitasoft.engine.business.data.impl.JPABusinessDataRepositoryImpl\">\n        <constructor-arg name=\"transactionService\" ref=\"transactionService\" />\n        <constructor-arg name=\"businessDataModelRepository\" ref=\"businessDataModelRepository\" />\n        <constructor-arg name=\"configuration\">\n            <map>\n                <!-- to prevent Hibernate 5 from changing its way of managing ID generation: -->\n                <!-- WARNING: do not remove this parameter, as it allows to preserve compatibility with BDMs generated\n                               with Bonita versions pre-7.12. Otherwise Hibernate 5 would interpret the generated\n                                BDM code with some logics that make it incompatible on MySQL and SQL Server -->\n                <entry key=\"hibernate.id.new_generator_mappings\" value=\"false\" />\n\n                <entry key=\"hibernate.dialect\" value=\"${${bdm.db.vendor}.hibernate.dialect}\" />\n                <entry key=\"hibernate.connection.datasource\" value-ref=\"bdmDatasource\" />\n                <entry key=\"hibernate.show_sql\" value=\"${bonita.tenant.bdm.repository.show_sql}\" />\n                <entry key=\"hibernate.format_sql\" value=\"${bonita.tenant.bdm.repository.format_sql}\" />\n                <entry key=\"hibernate.transaction.jta.platform\"\n                       value=\"${bdm.hibernate.transaction.jta_platform}\" />\n                <entry key=\"hibernate.validator.autoregister_listeners\"\n                       value=\"${bonita.tenant.bdm.repository.validator.autoregister_listeners}\" />\n                <entry key=\"hibernate.validator.apply_to_ddl\"\n                       value=\"${bonita.tenant.bdm.repository.validator.apply_to_ddl}\" />\n                <entry key=\"javax.persistence.validation.mode\"\n                       value=\"${bonita.tenant.bdm.repository.javax.persistence.validation.mode}\" />\n                <entry key=\"hibernate.current_session_context_class\" value=\"thread\" />\n                <entry key=\"hibernate.cache.use_second_level_cache\" value=\"false\" />\n                <entry key=\"hibernate.cache.use_query_cache\" value=\"false\" />\n            </map>\n        </constructor-arg>\n        <constructor-arg name=\"dataRetentionBdmTrackingService\" ref=\"dataRetentionBdmTrackingService\" />\n    </bean>\n\n\n    <bean id=\"schemaManager\" class=\"org.bonitasoft.engine.business.data.impl.SchemaManagerUpdate\">\n        <constructor-arg name=\"configuration\">\n            <map>\n                <!-- to prevent Hibernate 5 from changing its way of managing ID generation: -->\n                <!-- WARNING: do not remove this parameter, as it allows to preserve compatibility with BDMs generated\n                               with Bonita versions pre-7.12. Otherwise Hibernate 5 would interpret the generated\n                                BDM code with some logics that make it incompatible on MySQL and SQL Server -->\n                <entry key=\"hibernate.id.new_generator_mappings\" value=\"false\" />\n\n                <entry key=\"hibernate.dialect\" value=\"${${bdm.db.vendor}.hibernate.dialect}\" />\n                <entry key=\"hibernate.connection.datasource\" value-ref=\"bdmNonXaDatasource\" />\n                <entry key=\"hibernate.show_sql\" value=\"${bonita.tenant.bdm.schemamanager.show_sql}\" />\n                <entry key=\"hibernate.format_sql\" value=\"${bonita.tenant.bdm.schemamanager.format_sql}\" />\n                <entry key=\"hibernate.transaction.jta.platform\"\n                       value=\"${bdm.hibernate.transaction.jta_platform}\" />\n                <entry key=\"hibernate.validator.autoregister_listeners\"\n                       value=\"${bonita.tenant.bdm.schemamanager.validator.autoregister_listeners}\" />\n                <entry key=\"hibernate.validator.apply_to_ddl\"\n                       value=\"${bonita.tenant.bdm.schemamanager.validator.apply_to_ddl}\" />\n                <entry key=\"javax.persistence.validation.mode\"\n                       value=\"${bonita.tenant.bdm.schemamanager.javax.persistence.validation.mode}\" />\n            </map>\n        </constructor-arg>\n    </bean>\n\n    <bean id=\"typeConverterUtil\" class=\"org.bonitasoft.engine.commons.TypeConverterUtil\">\n        <constructor-arg name=\"datePatterns\" type=\"java.lang.String[]\">\n            <list>\n                <value>yyyy-MM-dd</value>\n                <value>HH:mm:ss</value>\n                <value>yyyy-MM-dd HH:mm:ss</value>\n                <value>yyyy-MM-dd'T'HH:mm:ss</value>\n                <value>yyyy-MM-dd'T'HH:mm:ss.SSS</value>\n            </list>\n        </constructor-arg>\n    </bean>\n\n    <bean id=\"businessDataReloader\" class=\"org.bonitasoft.engine.business.data.impl.BusinessDataReloader\">\n        <constructor-arg name=\"businessDataRepository\" ref=\"businessDataRepository\" />\n    </bean>\n\n    <bean id=\"countQueryProvider\" class=\"org.bonitasoft.engine.business.data.impl.CountQueryProvider\" />\n\n    <bean id=\"archivingStrategy\" class=\"org.bonitasoft.engine.archive.impl.DefaultArchivingStrategy\">\n        <constructor-arg name=\"additionalConfiguration\" ref=\"archivingStrategyAdditionalConfiguration\" />\n    </bean>\n\n    <util:map id=\"archivingStrategyAdditionalConfiguration\">\n    </util:map>\n\n    <bean id=\"archiveService\" class=\"org.bonitasoft.engine.archive.impl.ArchiveServiceImpl\">\n        <constructor-arg name=\"definitiveArchivePersistenceService\" ref=\"persistenceService\" />\n        <constructor-arg name=\"archivingStrategy\" ref=\"archivingStrategy\" />\n        <constructor-arg ref=\"transactionService\" />\n    </bean>\n\n    <bean id=\"contractDataService\" class=\"org.bonitasoft.engine.core.contract.data.ContractDataServiceImpl\">\n        <constructor-arg name=\"persistenceService\" ref=\"persistenceService\" />\n        <constructor-arg name=\"recorder\" ref=\"tenantRecorderSync\" />\n        <constructor-arg name=\"queriableLoggerService\" ref=\"queriableLoggerService\" />\n        <constructor-arg name=\"archiveService\" ref=\"archiveService\" />\n    </bean>\n\n\n    <bean id=\"formMappingService\" class=\"org.bonitasoft.engine.core.form.impl.FormMappingServiceImpl\">\n        <constructor-arg name=\"persistenceService\" ref=\"persistenceService\" />\n        <constructor-arg name=\"recorder\" ref=\"tenantRecorderSync\" />\n        <constructor-arg name=\"sessionService\" ref=\"sessionService\" />\n        <constructor-arg name=\"pageMappingService\" ref=\"pageMappingService\" />\n        <constructor-arg name=\"pageService\" ref=\"pageService\" />\n        <constructor-arg name=\"formMappingKeyGenerator\" ref=\"formMappingKeyGenerator\" />\n        <constructor-arg name=\"externalUrlAdapter\">\n            <util:constant static-field=\"org.bonitasoft.engine.page.URLAdapterConstants.EXTERNAL_URL_ADAPTER\" />\n        </constructor-arg>\n        <constructor-arg name=\"legacyUrlAdapter\">\n            <util:constant static-field=\"org.bonitasoft.engine.page.URLAdapterConstants.LEGACY_URL_ADAPTER\" />\n        </constructor-arg>\n        <constructor-arg name=\"queriableLoggerService\" ref=\"queriableLoggerService\" />\n        <constructor-arg name=\"authorizationRuleMapping\" ref=\"${bonita.tenant.authorization.rule.mapping}\" />\n    </bean>\n\n    <bean id=\"defaultAuthorizationRuleMapping\" class=\"org.bonitasoft.engine.core.form.impl.AuthorizationRuleMappingImpl\" />\n\n    <bean id=\"queriableLoggerService\" class=\"org.bonitasoft.engine.services.impl.QueriableLoggerImpl\">\n        <constructor-arg name=\"persistenceService\" ref=\"persistenceService\" />\n        <constructor-arg name=\"loggerStrategy\" ref=\"queriableLoggerStrategy\" />\n        <constructor-arg name=\"sessionProvider\" ref=\"queriableLogSessionProvider\" />\n        <constructor-arg name=\"platformService\" ref=\"platformService\" />\n        <constructor-arg name=\"transactionService\" ref=\"transactionService\" />\n    </bean>\n\n    <bean id=\"pageMappingService\" class=\"org.bonitasoft.engine.page.impl.PageMappingServiceImpl\">\n        <constructor-arg name=\"persistenceService\" ref=\"persistenceService\" />\n        <constructor-arg name=\"recorder\" ref=\"tenantRecorderSync\" />\n        <constructor-arg name=\"sessionService\" ref=\"sessionService\" />\n    </bean>\n\n    <bean id=\"urlAdapterInjector\" class=\"org.bonitasoft.engine.page.impl.UrlAdapterInjector\" autowire=\"byType\"\n          init-method=\"injectUrlAdapters\">\n        <constructor-arg name=\"pageMappingService\" ref=\"pageMappingService\" />\n    </bean>\n\n    <bean id=\"authorizationRulesInjector\" class=\"org.bonitasoft.engine.page.impl.AuthorizationRulesInjector\"\n          autowire=\"byType\" init-method=\"injectAuthorizationRules\">\n        <constructor-arg name=\"pageMappingService\" ref=\"pageMappingService\" />\n    </bean>\n\n    <bean id=\"taskInvolvementDelegate\" class=\"org.bonitasoft.engine.api.impl.TaskInvolvementDelegate\" />\n\n    <bean id=\"processInvolvementDelegate\" class=\"org.bonitasoft.engine.api.impl.ProcessInvolvementDelegate\" />\n\n    <!-- Authorization rules to register -->\n    <bean id=\"isAdmin\" class=\"org.bonitasoft.engine.page.IsAdminRule\">\n        <constructor-arg name=\"sessionService\" ref=\"sessionService\" />\n    </bean>\n\n    <bean id=\"isProcessOwner\" class=\"org.bonitasoft.engine.page.IsProcessOwnerRule\">\n        <constructor-arg name=\"sessionService\" ref=\"sessionService\" />\n        <constructor-arg name=\"formMappingService\" ref=\"formMappingService\" />\n        <constructor-arg name=\"supervisorMappingService\" ref=\"supervisorMappingService\" />\n    </bean>\n    <bean id=\"isTaskAvailableForUser\" class=\"org.bonitasoft.engine.page.IsTaskAvailableForUserRule\">\n        <constructor-arg name=\"sessionService\" ref=\"sessionService\" />\n        <constructor-arg name=\"activityInstanceService\" ref=\"activityInstanceService\" />\n    </bean>\n    <bean id=\"isActorInitiatorRule\" class=\"org.bonitasoft.engine.page.IsActorInitiatorRule\">\n        <constructor-arg name=\"sessionService\" ref=\"sessionService\" />\n        <constructor-arg name=\"formMappingService\" ref=\"formMappingService\" />\n        <constructor-arg name=\"actorMappingService\" ref=\"actorMappingService\" />\n    </bean>\n    <bean id=\"isProcessInitiator\" class=\"org.bonitasoft.engine.page.IsProcessInitiatorRule\">\n        <constructor-arg name=\"sessionService\" ref=\"sessionService\" />\n        <constructor-arg name=\"processInvolvementDelegate\" ref=\"processInvolvementDelegate\" />\n    </bean>\n    <bean id=\"isTaskPerformer\" class=\"org.bonitasoft.engine.page.IsTaskPerformerRule\">\n        <constructor-arg name=\"sessionService\" ref=\"sessionService\" />\n        <constructor-arg name=\"taskInvolvementDelegate\" ref=\"taskInvolvementDelegate\" />\n    </bean>\n    <bean id=\"isInvolvedInProcessInstance\" class=\"org.bonitasoft.engine.page.IsInvolvedInProcessInstanceRule\">\n        <constructor-arg name=\"sessionService\" ref=\"sessionService\" />\n        <constructor-arg name=\"taskInvolvementDelegate\" ref=\"taskInvolvementDelegate\" />\n    </bean>\n\n\n    <bean id=\"formMappingKeyGenerator\"\n          class=\"org.bonitasoft.engine.core.form.impl.FormMappingKeyGeneratorImpl\">\n        <constructor-arg name=\"processDefinitionService\"\n                         ref=\"processDefinitionService\" />\n    </bean>\n    <bean class=\"org.bonitasoft.engine.core.form.LegacyURLAdapter\">\n        <constructor-arg name=\"processDefinitionService\" ref=\"processDefinitionService\" />\n        <constructor-arg name=\"formMappingService\" ref=\"formMappingService\" />\n    </bean>\n\n    <bean id=\"externalUrlAdapter\" class=\"org.bonitasoft.engine.core.form.ExternalURLAdapter\" />\n\n    <bean id=\"engineClock\" class=\"org.bonitasoft.engine.commons.time.DefaultEngineClock\" />\n\n\n    <bean id=\"bpmArchiverService\" class=\"org.bonitasoft.engine.execution.archive.BPMArchiverService\">\n        <constructor-arg name=\"archiveService\" ref=\"archiveService\" />\n        <constructor-arg name=\"processInstanceService\" ref=\"processInstanceService\" />\n        <constructor-arg name=\"documentService\" ref=\"documentService\" />\n        <constructor-arg name=\"commentService\" ref=\"commentService\" />\n        <constructor-arg name=\"processDefinitionService\" ref=\"processDefinitionService\" />\n        <constructor-arg name=\"connectorInstanceService\" ref=\"connectorInstanceService\" />\n        <constructor-arg name=\"refBusinessDataService\" ref=\"refBusinessDataService\" />\n        <constructor-arg name=\"contractDataService\" ref=\"contractDataService\" />\n        <constructor-arg name=\"dataInstanceService\" ref=\"dataInstanceService\" />\n        <constructor-arg name=\"activityInstanceService\" ref=\"activityInstanceService\" />\n        <constructor-arg name=\"dataRetentionBdmTrackingService\" ref=\"dataRetentionBdmTrackingService\" />\n    </bean>\n\n</beans>\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/resources/bonita-platform-community.properties",
    "content": "#Bonita platform core configuration\n\n# Platform administrator\nplatformAdminUsername=platformAdmin\nplatformAdminPassword=platform\n\n# this datasource name is used by the engine to get connected to the database\ndatabase.journal.datasource.name=${sysprop.bonita.database.journal.datasource.name:java:comp/env/bonitaDS}\ndatabase.sequence.manager.datasource.name=${sysprop.bonita.database.sequence.manager.datasource.name:java:comp/env/bonitaSequenceManagerDS}\n\n# By default, get DB vendor type from JVM System Property. If not set, fallback on value after semi-colon (e.g. h2)\ndb.vendor=${sysprop.bonita.db.vendor:h2}\n\n# Hibernate specific configurations\nhibernate.journal.show_sql=false\nhibernate.journal.format_sql=false\nhibernate.journal.use_sql_comments=false\nhibernate.transaction.jta_platform=${sysprop.bonita.hibernate.transaction.jta_platform:org.bonitasoft.engine.persistence.Narayana5HibernateJtaPlatform}\n\n# Transaction Service properties\ntransaction.manager=${sysprop.bonita.transaction.manager:java:comp/env/TransactionManager}\nuserTransaction=${sysprop.bonita.userTransaction:java:comp/UserTransaction}\n\n# Synchro service\n# Initial capacity of the waiters map\nbonita.platform.synchro.initialcapacity=50\n\n# Scheduler\n# Number of threads in Quartz scheduler Thread Pool\nbonita.platform.scheduler.quartz.threadpool.size=5\n\n# Lock Service\n# Number of seconds to wait for a lock\nbonita.platform.lock.memory.timeout=60\n\n# Default platform cache: used if no specific cache is defined\nbonita.platform.cache.default.maxElementsInMemory=1000\nbonita.platform.cache.default.eternal=true\nbonita.platform.cache.default.evictionPolicy=LRU\nbonita.platform.cache.default.timeToLiveSeconds=3600\nbonita.platform.cache.default.readIntensive=false\nbonita.platform.cache.default.offHeapSizeMB=0\n\n# Synchro service cache configuration\nbonita.platform.cache.synchro.maxElementsInMemory=10000\nbonita.platform.cache.synchro.eternal=false\nbonita.platform.cache.synchro.evictionPolicy=LRU\nbonita.platform.cache.synchro.timeToLiveSeconds=120\nbonita.platform.cache.synchro.readIntensive=false\nbonita.platform.cache.synchro.offHeapSizeMB=0\n\n# Configuration files cache configuration\nbonita.platform.cache.configfiles.maxElementsInMemory=10000\nbonita.platform.cache.configfiles.eternal=true\nbonita.platform.cache.configfiles.evictionPolicy=LRU\nbonita.platform.cache.configfiles.timeToLiveSeconds=3600\nbonita.platform.cache.configfiles.readIntensive=true\nbonita.platform.cache.configfiles.offHeapSizeMB=0\n\n# Sequence manager configuration\nbonita.platform.sequence.retries=9\nbonita.platform.sequence.delay=10\nbonita.platform.sequence.delayFactor=3\n# if the sequence range size is not overridden, this value will be taken\nbonita.platform.sequence.defaultRangeSize=100\n# you can override the range size of any sequenceId following the pattern bonita.platform.sequence.<sequenceId>=<new range size>\n# Most used objects: range size depends on process design\nbonita.platform.sequence.70=2000\n# Job description\nbonita.platform.sequence.30=10000\n# queriable log\nbonita.platform.sequence.10010=1000\n# ProcessInstance\nbonita.platform.sequence.10011=20000\n# ActivityInstance<!-- 20 step by process average\nbonita.platform.sequence.10013=20000\n# TransitionInstance<!-- same as activity instance\nbonita.platform.sequence.10014=20000\n# ConnectorInstance<!-- same as activity instance\nbonita.platform.sequence.20010=3000\n# archived ProcessInstance<!-- Process instance * nb state\nbonita.platform.sequence.20011=80000\n# EventTriggerInstance<!-- 1/5 of activity\nbonita.platform.sequence.10016=4000\n# WaitingEvent<!-- 1/5 of activity\nbonita.platform.sequence.10017=4000\n# MessageInstance<!-- 1/5 of activity\nbonita.platform.sequence.10018=4000\n# PendingActivityMapping<!-- 1/5 of activity\nbonita.platform.sequence.10020=5000\n# data\nbonita.platform.sequence.20050=25000\n# archived data<!-- 5 change by data\nbonita.platform.sequence.10060=20000\n# comment\nbonita.platform.sequence.10080=20000\n\n\n# Platform persistence service\nbonita.platform.persistence.platform.likeEscapeCharacter=#\n\n# Tenant persistence service\nbonita.platform.persistence.tenant.likeEscapeCharacter=#\n\n# Hibernate persistence configuration:\nbonita.platform.persistence.generate_statistics=false\nbonita.platform.persistence.connection.shutdown=true\nbonita.platform.persistence.validator.autoregister_listeners=false\nbonita.platform.persistence.validator.apply_to_ddl=false\nbonita.platform.persistence.javax.persistence.validation.mode=NONE\n# log in `INFO` using logger `org.hibernate.SQL_SLOW` when sql queries are longer than this value\nbonita.platform.persistence.dbquery.warnWhenLongerThanMillis=1000\n# Max number of queries in the cache hibernate uses to optimize similar queries\nbonita.platform.persistence.query.cache.max_size=2048\n\n\n# Brute-force login protection\nbonita.runtime.security.bruteforce.enabled=true\nbonita.runtime.security.bruteforce.max.attempts=5\nbonita.runtime.security.bruteforce.lockout.duration.seconds=600\n\n# QUARTZ\n# quartz non managed connections transaction isolation level\n# database default if false, read committed if true\norg.quartz.jobStore.txIsolationLevelReadCommitted=false\n# time in ms after which a job is considered misfired\norg.quartz.jobStore.misfireThreshold=60000\n# number of jobs to update at the same time (more means less locking)\norg.quartz.jobStore.maxMisfiresToHandleAtATime=20\n# must be set to true when 'batchTriggerAcquisitionMaxCount' is more than 1\norg.quartz.jobStore.acquireTriggersWithinLock=false\n# number of trigger to get at the same time, useful with high number of jobs\norg.quartz.scheduler.batchTriggerAcquisitionMaxCount=1\n# consider that a job can fire this amount of ms before its actual fire time\norg.quartz.scheduler.batchTriggerAcquisitionFireAheadTimeWindow=0\n\n# Controls whether BDM JSON serialization follows standard REST shapes or legacy array-based formats.\n# When enabled (true, default):\n#   - Count queries return { \"value\": n } instead of [n]\n#   - Single-entity queries return { ? } instead of [ { ? } ]\n# When disabled (false): legacy array format is used for backward compatibility.\nbonita.runtime.business-data.serialization.standard-shape.enabled=true\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/resources/bonita-platform-private-community.properties",
    "content": "#Bonita platform core configuration\n\n# Default service accessors\nserviceAccessors=org.bonitasoft.engine.service.impl.SpringServiceAccessors\napiAccessResolver=org.bonitasoft.engine.service.impl.APIAccessResolverImpl\nserverApi=org.bonitasoft.engine.api.impl.ServerAPIImpl\n\n# Hibernate Dialects\nh2.hibernate.dialect=org.hibernate.dialect.H2Dialect\npostgres.hibernate.dialect=org.hibernate.dialect.PostgreSQL10Dialect\n\n# Hibernate Interceptors for journal (runtime database)\nh2.hibernate.journal.interceptor=\npostgres.hibernate.journal.interceptor=org.bonitasoft.engine.persistence.PostgresInterceptor\n\n# Quartz properties\nh2.quartz.connection.jobstoredriver=org.quartz.impl.jdbcjobstore.StdJDBCDelegate\npostgres.quartz.connection.jobstoredriver=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/resources/bonita-tenant-community.properties",
    "content": "# Bonita Tenant server core configuration\nbonita.runtime.admin.username=install\nbonita.runtime.admin.password=install\n\n# Business data configuration\nbdm.db.vendor=${sysprop.bonita.bdm.db.vendor:h2}\nbdm.datasource.jndi.path=${sysprop.bonita.businessdata.datasource.jndi:java:comp/env/BusinessDataDS}\nbdm.notmanageddatasource.jndi.path=${sysprop.bonita.businessdata.notmanageddatasource.jndi:java:comp/env/NotManagedBizDataDS}\nbdm.hibernate.transaction.jta_platform=${sysprop.bonita.hibernate.transaction.jta_platform:org.bonitasoft.engine.persistence.Narayana5HibernateJtaPlatform}\n\n#Cron jobs configuration\n#Seconds Minutes Hours Day-of-Month Month Day-of-Week Year (optional field)\norg.bonitasoft.engine.clean.invalid.sessions.cron=0 0 */2 * * ?\n\n# name of the servlet used to download content of document\ndocument.servlet.url=documentDownload\n\n# Session service\n# session duration in ms\nbonita.runtime.session.duration=3600000\n# previously named: bonita.tenant.session.duration=3600000\n\n# Connector executor\nbonita.tenant.connector.queueCapacity=10000\n\n# Produce a warning log when connector took longer to execute than this value\nbonita.tenant.connector.warnWhenLongerThanMillis=10000\n\n# Work service\n# Time to wait in seconds for all work to terminate when the service is paused or stopped\nbonita.tenant.work.terminationTimeout=30\nbonita.tenant.work.queueCapacity=500000\n\n# When a work fails, log only a limited number of frames from the stacktrace\nbonita.tenant.work.exceptionsNumberOfFrameToLog=3\n\nbonita.tenant.work.batch_restart_size=1000\n\n# Retry mechanism: retry work when they fails because of an error that is transient\n# maximum number of times a work will be retried before setting it as failed\nbonita.tenant.work.maxRetry=10\n# delay in millis before retrying the work\nbonita.tenant.work.retry.delay=1000\n# factor to multiply the delay with, between two subsequent retries\nbonita.tenant.work.retry.factor=2\n\n# Recovery Mechanism: recreate works when they are lost due to incidents\n# All following configuration should work for everyone, it can be changed only to do performance tuning in limit-cases\n# Avoid verifying elements recently modified, by default no elements updated during the last hour is considered (ISO-8601 duration format).\nbonita.tenant.recover.consider_elements_older_than=PT1H\n# Duration after the end of the previous execution before a new one is started. By default, recovery runs every 2 hours (ISO-8601 duration format)\nbonita.tenant.recover.delay_between_recovery=PT2H\n\n# Time tracker\nbonita.tenant.timetracker.startTracking=false\nbonita.tenant.timetracker.maxSize=1000\nbonita.tenant.timetracker.flushIntervalInSeconds=30\nbonita.tenant.timetracker.csv.activateAtStart=true\nbonita.tenant.timetracker.csv.folder=${java.io.tmpdir}\nbonita.tenant.timetracker.csv.separator=;\nbonita.tenant.timetracker.memory.activateAtStart=false\nbonita.tenant.timetracker.memory.maxSize=1000000\n\n# Connector service cache configuration\nbonita.tenant.cache.connector.maxElementsInMemory=10000\nbonita.tenant.cache.connector.eternal=false\nbonita.tenant.cache.connector.evictionPolicy=LRU\nbonita.tenant.cache.connector.timeToLiveSeconds=3600\nbonita.tenant.cache.connector.readIntensive=true\n# Off-heap storage size in MB (0 = no off-heap, heap-only)\n# Off-heap provides overflow capacity without disk I/O, reducing GC pressure\n# Example: bonita.tenant.cache.connector.offHeapSizeMB=64\n#bonita.tenant.cache.connector.offHeapSizeMB=0\n\n# Parameter service cache configuration\nbonita.tenant.cache.parameter.maxElementsInMemory=10000\nbonita.tenant.cache.parameter.eternal=false\nbonita.tenant.cache.parameter.evictionPolicy=LRU\nbonita.tenant.cache.parameter.timeToLiveSeconds=3600\nbonita.tenant.cache.parameter.readIntensive=false\nbonita.tenant.cache.parameter.offHeapSizeMB=0\n\n# Process Definition service cache configuration\nbonita.tenant.cache.processdef.maxElementsInMemory=10000\nbonita.tenant.cache.processdef.eternal=false\nbonita.tenant.cache.processdef.evictionPolicy=LRU\nbonita.tenant.cache.processdef.timeToLiveSeconds=3600\nbonita.tenant.cache.processdef.readIntensive=false\n# Off-heap storage size in MB (0 = no off-heap, heap-only)\n# Recommended for large process definitions to reduce GC pressure\n# Example: bonita.tenant.cache.processdef.offHeapSizeMB=128\n#bonita.tenant.cache.processdef.offHeapSizeMB=0\n\n# User Filter service cache configuration\nbonita.tenant.cache.userfilter.maxElementsInMemory=10000\nbonita.tenant.cache.userfilter.eternal=false\nbonita.tenant.cache.userfilter.evictionPolicy=LRU\nbonita.tenant.cache.userfilter.timeToLiveSeconds=3600\nbonita.tenant.cache.userfilter.readIntensive=false\nbonita.tenant.cache.userfilter.offHeapSizeMB=0\n\n# Groovy script service cache configuration\nbonita.tenant.cache.groovy.maxElementsInMemory=10000\nbonita.tenant.cache.groovy.eternal=true\nbonita.tenant.cache.groovy.evictionPolicy=LRU\nbonita.tenant.cache.groovy.timeToLiveSeconds=3600\nbonita.tenant.cache.groovy.readIntensive=false\n# Off-heap storage size in MB (0 = no off-heap, heap-only)\n# Recommended for compiled Groovy scripts to reduce GC pressure\n# Example: bonita.tenant.cache.groovy.offHeapSizeMB=1024\n#bonita.tenant.cache.groovy.offHeapSizeMB=0\n\n# Transient Data service cache configuration\nbonita.tenant.cache.transientdata.maxElementsInMemory=10000\nbonita.tenant.cache.transientdata.eternal=true\nbonita.tenant.cache.transientdata.evictionPolicy=LRU\nbonita.tenant.cache.transientdata.timeToLiveSeconds=3600\nbonita.tenant.cache.transientdata.readIntensive=false\nbonita.tenant.cache.transientdata.offHeapSizeMB=0\n\n# Business Data Repository hibernate configuration\nbonita.tenant.bdm.repository.show_sql=false\nbonita.tenant.bdm.repository.format_sql=false\nbonita.tenant.bdm.repository.validator.autoregister_listeners=false\nbonita.tenant.bdm.repository.validator.apply_to_ddl=false\nbonita.tenant.bdm.repository.javax.persistence.validation.mode=NONE\n\n# Business Data Schema manager\nbonita.tenant.bdm.schemamanager.show_sql=false\nbonita.tenant.bdm.schemamanager.format_sql=true\nbonita.tenant.bdm.schemamanager.validator.autoregister_listeners=false\nbonita.tenant.bdm.schemamanager.validator.apply_to_ddl=false\nbonita.tenant.bdm.schemamanager.javax.persistence.validation.mode=NONE\n\n# Page and form mapping authorization rules\n# you can customize this mapping by defining your own bean\n# that implement org.bonitasoft.engine.core.form.AuthorizationRuleMapping\n# and a set of org.bonitasoft.engine.page.AuthorizationRule\n# to be declared in bonita-tenant-custom.xml\n#bonita.tenant.authorization.rule.mapping=customAuthorizationRuleMapping\nbonita.tenant.authorization.rule.mapping=defaultAuthorizationRuleMapping\n# to restore pre-7.3.0 behavior (where manager of user involved in process instance could access Case Overview), use this implementation below instead:\n#bonita.tenant.authorization.rule.mapping=managerInvolvedAuthorizationRuleMappingImpl\n\n\n# Work execution audit\n# Durations must be ChronoUnit enum values\n#bonita.tenant.work.audit.activated=true\n# Too many executions threshold\n#bonita.tenant.work.audit.abnormal.execution.threshold.execution_count=10\n#bonita.tenant.work.audit.abnormal.execution.threshold.execution_count_duration_amount=10\n#bonita.tenant.work.audit.abnormal.execution.threshold.execution_count_duration_unit=MINUTES\n# Too much time elapsed since registration\n#bonita.tenant.work.audit.abnormal.execution.threshold.elapsed_duration_since_registration_amount=30\n#bonita.tenant.work.audit.abnormal.execution.threshold.elapsed_duration_since_registration_unit=MINUTES\n\n# Set this value to false to disable any dynamic permissions totally\nbonita.runtime.authorization.dynamic-check.enabled=true\n\n# Default timeout in minutes for class loaders initialization and refresh\nbonita.runtime.classloader.initialization.timeout-minutes=5\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/main/resources/profiles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<profiles:profiles xmlns:profiles=\"http://documentation.bonitasoft.com/profile-xml-schema/1.0\">\n\t<profile name=\"User\" isDefault=\"true\">\n\t\t<description>The user can view and perform tasks and can start a new case of a process.</description>\n\t</profile>\n\t<profile name=\"Administrator\" isDefault=\"true\">\n\t\t<description>The administrator can install a process, manage the organization, and handle some errors (for example, by replaying a task).</description>\n\t</profile>\n</profiles:profiles>\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/groovy/org/bonitasoft/engine/execution/transition/FlowNodeStateSequencesTest.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.transition;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.core.process.instance.model.SStateCategory.*;\nimport static org.bonitasoft.engine.execution.TestFlowNodeState.*;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class FlowNodeStateSequencesTest {\n\n    public static final int NORMAL_STATE_1 = 1;\n    public static final int NORMAL_STATE_2 = 2;\n    public static final int CANCEL_STATE_1 = 3;\n    public static final int CANCEL_STATE_2 = 4;\n    public static final int ABORT_STATE_1 = 5;\n    public static final int ABORT_STATE_2 = 6;\n    private final FlowNodeStateSequences flowNodeStateSequences = new FlowNodeStateSequences() {\n\n        @Override\n        public SFlowNodeType getFlowNodeType() {\n            return SFlowNodeType.AUTOMATIC_TASK;\n        }\n    };\n\n    @Before\n    public void before() {\n        flowNodeStateSequences.defineNormalSequence(normalState(NORMAL_STATE_1, NORMAL),\n                terminalState(NORMAL_STATE_2, NORMAL));\n        flowNodeStateSequences.defineCancelSequence(normalState(CANCEL_STATE_1, CANCELLING),\n                terminalState(CANCEL_STATE_2, CANCELLING));\n        flowNodeStateSequences.defineAbortSequence(normalState(ABORT_STATE_1, ABORTING),\n                terminalState(ABORT_STATE_2, ABORTING));\n    }\n\n    @Test\n    public void should_give_the_first_state_of_normal_branch() {\n        assertThat(flowNodeStateSequences.getFirstState(NORMAL).getId()).isEqualTo(NORMAL_STATE_1);\n    }\n\n    @Test\n    public void should_give_the_first_state_of_abort_branch() {\n        assertThat(flowNodeStateSequences.getFirstState(CANCELLING).getId()).isEqualTo(CANCEL_STATE_1);\n    }\n\n    @Test\n    public void should_give_the_first_state_of_cancel_branch() {\n        assertThat(flowNodeStateSequences.getFirstState(ABORTING).getId()).isEqualTo(ABORT_STATE_1);\n    }\n\n    @Test\n    public void should_give_the_next_of_normal_branch() {\n        assertThat(flowNodeStateSequences.getStateAfter(NORMAL, NORMAL_STATE_1).getId())\n                .isEqualTo(NORMAL_STATE_2);\n    }\n\n    @Test\n    public void should_give_the_next_of_abort_branch() {\n        assertThat(flowNodeStateSequences.getStateAfter(CANCELLING, CANCEL_STATE_1).getId())\n                .isEqualTo(CANCEL_STATE_2);\n    }\n\n    @Test\n    public void should_give_the_next_of_cancel_branch() {\n        assertThat(flowNodeStateSequences.getStateAfter(ABORTING, ABORT_STATE_1).getId())\n                .isEqualTo(ABORT_STATE_2);\n    }\n\n    @Test\n    public void should_give_null_after_the_last_state() {\n        assertThat(flowNodeStateSequences.getStateAfter(NORMAL, NORMAL_STATE_2)).isNull();\n    }\n\n    @Test\n    public void should_give_null_when_state_does_not_exists() {\n        assertThat(flowNodeStateSequences.getStateAfter(NORMAL, 9999)).isNull();\n    }\n\n    @Test\n    public void should_give_null_if_state_does_not_exists_in_current_state_category() {\n        assertThat(flowNodeStateSequences.getStateAfter(ABORTING, NORMAL_STATE_1)).isNull();\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/groovy/org/bonitasoft/permissions/ActorMemberPermissionRuleTest.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.permissions\n\nimport static org.assertj.core.api.Assertions.assertThat\nimport static org.mockito.Mockito.doReturn\nimport static org.mockito.Mockito.mock\n\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.IdentityAPI\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.ProcessAPI\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.api.permission.PermissionRule\nimport org.bonitasoft.engine.bpm.actor.ActorInstance\nimport org.bonitasoft.engine.identity.User\nimport org.bonitasoft.engine.session.APISession\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.mockito.Mock\nimport org.mockito.junit.MockitoJUnitRunner\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ActorMemberPermissionRuleTest {\n\n    @Mock\n    def APISession apiSession\n    @Mock\n    def APICallContext apiCallContext\n    @Mock\n    def APIAccessor apiAccessor\n    @Mock\n    def Logger logger\n    def PermissionRule rule = new ActorMemberPermissionRule()\n    @Mock\n    def ProcessAPI processAPI\n    @Mock\n    def IdentityAPI identityAPI\n    @Mock\n    def User user\n    def long currentUserId = 16l\n\n    @Before\n    public void before() {\n        doReturn(processAPI).when(apiAccessor).getProcessAPI()\n        doReturn(currentUserId).when(apiSession).getUserId()\n    }\n\n    @Test\n    public void should_check_verify_post_is_true_when_process_owner() {\n        doReturn(true).when(apiCallContext).isPOST()\n        doReturn('''\n            [{\n                \"actor_id\":\"547\",\n                \"other\":\"sample\"\n            }]\n        ''').when(apiCallContext).getBody()\n        def actor = mock(ActorInstance.class)\n        doReturn(154l).when(actor).getProcessDefinitionId()\n        doReturn(actor).when(processAPI).getActor(547l)\n        doReturn(true).when(processAPI).isUserProcessSupervisor(154l, currentUserId)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_post_is_false_when_not_process_owner() {\n        doReturn(true).when(apiCallContext).isPOST()\n        doReturn('''\n            [{\n                \"actor_id\":\"547\",\n                \"other\":\"sample\"\n            }]\n        ''').when(apiCallContext).getBody()\n\n        def actor = mock(ActorInstance.class)\n        doReturn(154l).when(actor).getProcessDefinitionId()\n        doReturn(actor).when(processAPI).getActor(547l)\n        doReturn(false).when(processAPI).isUserProcessSupervisor(154l, currentUserId)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_verify_get_is_true_when_process_owner() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn([\n            \"actor_id\": \"547\",\n            \"other\"   : \"sample\"\n        ]).when(apiCallContext).getFilters()\n        def actor = mock(ActorInstance.class)\n        doReturn(154l).when(actor).getProcessDefinitionId()\n        doReturn(actor).when(processAPI).getActor(547l)\n        doReturn(true).when(processAPI).isUserProcessSupervisor(154l, currentUserId)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_get_is_false_when_not_process_owner() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn([\n            \"actor_id\": \"547\",\n            \"other\"   : \"sample\"\n        ]).when(apiCallContext).getFilters()\n        def actor = mock(ActorInstance.class)\n        doReturn(154l).when(actor).getProcessDefinitionId()\n        doReturn(actor).when(processAPI).getActor(547l)\n        doReturn(false).when(processAPI).isUserProcessSupervisor(154l, currentUserId)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_return_false_on_delete() {\n        doReturn(true).when(apiCallContext).isDELETE()\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/groovy/org/bonitasoft/permissions/ActorPermissionRuleTest.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\nimport static org.assertj.core.api.Assertions.assertThat\nimport static org.mockito.Mockito.doReturn\n\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.ProcessAPI\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.api.permission.PermissionRule\nimport org.bonitasoft.engine.bpm.actor.ActorInstance\nimport org.bonitasoft.engine.identity.User\nimport org.bonitasoft.engine.session.APISession\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.mockito.Mock\nimport org.mockito.junit.MockitoJUnitRunner\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ActorPermissionRuleTest {\n\n    @Mock\n    def APISession apiSession\n    @Mock\n    def APICallContext apiCallContext\n    @Mock\n    def APIAccessor apiAccessor\n    @Mock\n    def Logger logger\n    def PermissionRule rule = new ActorPermissionRule()\n    @Mock\n    def ProcessAPI processAPI\n    @Mock\n    def ActorInstance actor\n    @Mock\n    def User user\n    def long currentUserId = 16l\n\n    @Before\n    public void before() {\n        doReturn(processAPI).when(apiAccessor).getProcessAPI()\n        doReturn(currentUserId).when(apiSession).getUserId()\n    }\n\n    @Test\n    public void should_check_verify_get_multiple_is_true_when_process_owner() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(\n                [\n                    \"process_id\": \"1\"\n                ]\n                ).when(apiCallContext).getFilters()\n        doReturn(true).when(processAPI).isUserProcessSupervisor(1l, currentUserId)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_get_multiple_is_false_when_not_process_owner() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(\n                [\n                    \"process_id\": \"1\"\n                ]\n                ).when(apiCallContext).getFilters()\n        doReturn(false).when(processAPI).isUserProcessSupervisor(1l, currentUserId)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_verify_get_is_true_when_process_owner() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(\"2\").when(apiCallContext).getResourceId()\n        doReturn(actor).when(processAPI).getActor(2l)\n        doReturn(1l).when(actor).getProcessDefinitionId()\n        doReturn(true).when(processAPI).isUserProcessSupervisor(1l, currentUserId)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_get_is_false_when_not_process_owner() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(\"2\").when(apiCallContext).getResourceId()\n        doReturn(actor).when(processAPI).getActor(2l)\n        doReturn(1l).when(actor).getProcessDefinitionId()\n        doReturn(false).when(processAPI).isUserProcessSupervisor(1l, currentUserId)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_verify_put_when_not_process_owner() {\n        doReturn(true).when(apiCallContext).isPUT()\n        doReturn(\"2\").when(apiCallContext).getResourceId()\n        doReturn(actor).when(processAPI).getActor(2l)\n        doReturn(1l).when(actor).getProcessDefinitionId()\n        doReturn(false).when(processAPI).isUserProcessSupervisor(1l, currentUserId)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_verify_put_when_process_owner() {\n        doReturn(true).when(apiCallContext).isPUT()\n        doReturn(\"2\").when(apiCallContext).getResourceId()\n        doReturn(actor).when(processAPI).getActor(2l)\n        doReturn(1l).when(actor).getProcessDefinitionId()\n        doReturn(true).when(processAPI).isUserProcessSupervisor(1l, currentUserId)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/groovy/org/bonitasoft/permissions/ApplicationMenuPermissionRuleTest.groovy",
    "content": "/**\n * Copyright (C) 2021 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\nimport org.bonitasoft.engine.business.application.ApplicationVisibility\nimport org.bonitasoft.engine.business.application.impl.ApplicationImpl\n\nimport static org.mockito.Mockito.doReturn\nimport static org.mockito.Mockito.mock\n\nimport org.assertj.core.api.Assertions\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.ProfileAPI\nimport org.bonitasoft.engine.api.ApplicationAPI\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.business.application.Application\nimport org.bonitasoft.engine.business.application.ApplicationMenu\nimport org.bonitasoft.engine.identity.User\nimport org.bonitasoft.engine.profile.ProfileCriterion\nimport org.bonitasoft.engine.profile.impl.ProfileImpl\nimport org.bonitasoft.engine.session.APISession\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.mockito.Mock\nimport org.mockito.junit.MockitoJUnitRunner\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ApplicationMenuPermissionRuleTest {\n\n    @Mock\n    def APISession apiSession\n    @Mock\n    def APICallContext apiCallContext\n    @Mock\n    def APIAccessor apiAccessor\n    @Mock\n    def Logger logger\n    def ApplicationMenuPermissionRule rule = new ApplicationMenuPermissionRule()\n    @Mock\n    def ProfileAPI profileAPI\n    @Mock\n    def ApplicationAPI applicationAPI\n    @Mock\n    def User user\n    def long currentUserId = 16l\n\n    @Before\n    public void before() {\n        doReturn(profileAPI).when(apiAccessor).getProfileAPI()\n        doReturn(applicationAPI).when(apiAccessor).getLivingApplicationAPI()\n        doReturn(currentUserId).when(apiSession).getUserId()\n    }\n\n    @Test\n    void should_return_true_when_internal_profile_ALL() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(\"2\").when(apiCallContext).getResourceId()\n        def applicationMenu = mock(ApplicationMenu.class)\n        doReturn(4l).when(applicationMenu).getApplicationId()\n        doReturn(applicationMenu).when(applicationAPI).getApplicationMenu(2l)\n        def application = new ApplicationImpl(\"appToken\", \"1.0\", \"dtc\")\n        application.setProfileId(-1L)\n        application.setVisibility(ApplicationVisibility.ALL)\n        doReturn(application).when(applicationAPI).getApplication(4l)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    void should_return_false_when_internal_profile_SuperAdmin_and_user_not_technical_user() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(\"2\").when(apiCallContext).getResourceId()\n        def applicationMenu = mock(ApplicationMenu.class)\n        doReturn(4l).when(applicationMenu).getApplicationId()\n        doReturn(applicationMenu).when(applicationAPI).getApplicationMenu(2L)\n        def application = new ApplicationImpl(\"appToken\", \"1.0\", \"dtc\")\n        application.setProfileId(-1L)\n        application.setVisibility(ApplicationVisibility.TECHNICAL_USER)\n        doReturn(application).when(applicationAPI).getApplication(4L)\n        doReturn(false).when(apiSession).isTechnicalUser()\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    void should_return_true_when_internal_profile_SuperAdmin_and_user_technical_user() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(\"2\").when(apiCallContext).getResourceId()\n        def applicationMenu = mock(ApplicationMenu.class)\n        doReturn(4l).when(applicationMenu).getApplicationId()\n        doReturn(applicationMenu).when(applicationAPI).getApplicationMenu(2l)\n        def application = new ApplicationImpl(\"appToken\", \"1.0\", \"dtc\")\n        application.setProfileId(-1L)\n        application.setVisibility(ApplicationVisibility.TECHNICAL_USER)\n        doReturn(application).when(applicationAPI).getApplication(4l)\n        doReturn(true).when(apiSession).isTechnicalUser()\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_get_with_resource_user_is_in_profile() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(\"2\").when(apiCallContext).getResourceId()\n        def applicationMenu = mock(ApplicationMenu.class)\n        doReturn(4l).when(applicationMenu).getApplicationId()\n        doReturn(applicationMenu).when(applicationAPI).getApplicationMenu(2l)\n        def application = mock(Application.class)\n        doReturn(3l).when(application).getProfileId()\n        doReturn(application).when(applicationAPI).getApplication(4l)\n        doReturn([profile(1), profile(2), profile(3)]).when(profileAPI).getProfilesForUser(currentUserId, 0, 100, ProfileCriterion.ID_ASC)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_get_with_resource_user_is_in_profile_with_more_than_100_elements() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(\"2\").when(apiCallContext).getResourceId()\n        def applicationMenu = mock(ApplicationMenu.class)\n        doReturn(4l).when(applicationMenu).getApplicationId()\n        doReturn(applicationMenu).when(applicationAPI).getApplicationMenu(2l)\n        def application = mock(Application.class)\n        doReturn(110l).when(application).getProfileId()\n        doReturn(application).when(applicationAPI).getApplication(4l)\n        doReturn((1l..100l).collect { profile(it) }).when(profileAPI).getProfilesForUser(currentUserId, 0, 100, ProfileCriterion.ID_ASC)\n        doReturn((101l..110l).collect { profile(it) }).when(profileAPI).getProfilesForUser(currentUserId, 100, 100, ProfileCriterion.ID_ASC)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_get_with_resource_user_is_not_in_profile() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(\"2\").when(apiCallContext).getResourceId()\n        def applicationMenu = mock(ApplicationMenu.class)\n        doReturn(4l).when(applicationMenu).getApplicationId()\n        doReturn(applicationMenu).when(applicationAPI).getApplicationMenu(2l)\n        def application = mock(Application.class)\n        doReturn(10l).when(application).getProfileId()\n        doReturn(application).when(applicationAPI).getApplication(4l)\n        doReturn([profile(1), profile(3)]).when(profileAPI).getProfilesForUser(currentUserId, 0, 100, ProfileCriterion.ID_ASC)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_verify_not_get_return_false() {\n        doReturn(false).when(apiCallContext).isGET()\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isFalse()\n    }\n\n    private ProfileImpl profile(long id) {\n        def profile = new ProfileImpl(\"profilename\")\n        profile.setId(id)\n        return profile\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/groovy/org/bonitasoft/permissions/ApplicationPermissionRuleTest.groovy",
    "content": "/**\n * Copyright (C) 2021 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\nimport org.bonitasoft.engine.business.application.ApplicationVisibility\nimport org.bonitasoft.engine.business.application.impl.ApplicationImpl\n\nimport static org.mockito.Mockito.doReturn\nimport static org.mockito.Mockito.mock\n\nimport org.assertj.core.api.Assertions\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.ProfileAPI\nimport org.bonitasoft.engine.api.ApplicationAPI\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.business.application.Application\nimport org.bonitasoft.engine.identity.User\nimport org.bonitasoft.engine.profile.ProfileCriterion\nimport org.bonitasoft.engine.profile.impl.ProfileImpl\nimport org.bonitasoft.engine.session.APISession\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.mockito.Mock\nimport org.mockito.junit.MockitoJUnitRunner\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ApplicationPermissionRuleTest {\n\n    @Mock\n    def APISession apiSession\n    @Mock\n    def APICallContext apiCallContext\n    @Mock\n    def APIAccessor apiAccessor\n    @Mock\n    def Logger logger\n    def ApplicationPermissionRule rule = new ApplicationPermissionRule()\n    @Mock\n    def ProfileAPI profileAPI\n    @Mock\n    def ApplicationAPI applicationAPI\n    @Mock\n    def User user\n    def long currentUserId = 16l\n\n    @Before\n    public void before() {\n        doReturn(profileAPI).when(apiAccessor).getProfileAPI()\n        doReturn(applicationAPI).when(apiAccessor).getLivingApplicationAPI()\n        doReturn(currentUserId).when(apiSession).getUserId()\n    }\n\n    @Test\n    void should_return_true_when_internal_profile_ALL(){\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(null).when(apiCallContext).getResourceId()\n        doReturn([token: \"appToken\"]).when(apiCallContext).getFilters()\n        def application = new ApplicationImpl(\"appToken\", \"1.0\", \"dtc\")\n        application.setProfileId(-1L)\n        application.setVisibility(ApplicationVisibility.ALL)\n        doReturn(application).when(applicationAPI).getApplicationByToken(\"appToken\")\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    void should_return_false_when_internal_profile_SuperAdmin_and_user_not_technical_user(){\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(null).when(apiCallContext).getResourceId()\n        doReturn([token: \"appToken\"]).when(apiCallContext).getFilters()\n        def application = new ApplicationImpl(\"appToken\", \"1.0\", \"dtc\")\n        application.setProfileId(-1L)\n        application.setVisibility(ApplicationVisibility.TECHNICAL_USER)\n        doReturn(application).when(applicationAPI).getApplicationByToken(\"appToken\")\n        doReturn(false).when(apiSession).isTechnicalUser()\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    void should_return_true_when_internal_profile_SuperAdmin_and_user_technical_user(){\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(null).when(apiCallContext).getResourceId()\n        doReturn([token: \"appToken\"]).when(apiCallContext).getFilters()\n        def application = new ApplicationImpl(\"appToken\", \"1.0\", \"dtc\")\n        application.setProfileId(-1L)\n        application.setVisibility(ApplicationVisibility.TECHNICAL_USER)\n        doReturn(application).when(applicationAPI).getApplicationByToken(\"appToken\")\n        doReturn(true).when(apiSession).isTechnicalUser()\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_get_is_true_when_user_id_in_filters() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn([userId: currentUserId as String]).when(apiCallContext).getFilters()\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_get_with_resource_user_is_in_profile() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(\"2\").when(apiCallContext).getResourceId()\n        def application = mock(Application.class)\n        doReturn(4l).when(application).getProfileId()\n        doReturn(application).when(applicationAPI).getApplication(2l)\n        doReturn([profile(1), profile(2), profile(3), profile(4)]).when(profileAPI).getProfilesForUser(currentUserId, 0, 100, ProfileCriterion.ID_ASC)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n\n    @Test\n    public void should_check_verify_get_with_token_filter_user_is_in_profile() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(null).when(apiCallContext).getResourceId()\n        doReturn([token: \"appToken\"]).when(apiCallContext).getFilters()\n        def application = mock(Application.class)\n        doReturn(4l).when(application).getProfileId()\n        doReturn(application).when(applicationAPI).getApplicationByToken(\"appToken\")\n        doReturn([profile(1), profile(2), profile(3), profile(4)]).when(profileAPI).getProfilesForUser(currentUserId, 0, 100, ProfileCriterion.ID_ASC)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_get_with_resource_user_is_in_profile_with_more_than_100_elements() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(\"10\").when(apiCallContext).getResourceId()\n        def application = mock(Application.class)\n        doReturn(110l).when(application).getProfileId()\n        doReturn(application).when(applicationAPI).getApplication(10l)\n        doReturn((1l..100l).collect { profile(it) }).when(profileAPI).getProfilesForUser(currentUserId, 0, 100, ProfileCriterion.ID_ASC)\n        doReturn((101l..110l).collect { profile(it) }).when(profileAPI).getProfilesForUser(currentUserId, 100, 100, ProfileCriterion.ID_ASC)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_get_with_resource_user_is_not_in_profile() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(\"2\").when(apiCallContext).getResourceId()\n        def application = mock(Application.class)\n        doReturn(4l).when(application).getProfileId()\n        doReturn(application).when(applicationAPI).getApplication(2l)\n        doReturn([profile(1), profile(3)]).when(profileAPI).getProfilesForUser(currentUserId, 0, 100, ProfileCriterion.ID_ASC)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_verify_not_get_return_false() {\n        doReturn(false).when(apiCallContext).isGET()\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isFalse()\n    }\n\n    private ProfileImpl profile(long id) {\n        def profile = new ProfileImpl(\"profilename\")\n        profile.setId(id)\n        return profile\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/groovy/org/bonitasoft/permissions/CaseContextPermissionRuleTest.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\nimport static org.mockito.Mockito.*\n\nimport org.assertj.core.api.Assertions\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.IdentityAPI\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.ProcessAPI\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstance\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstanceNotFoundException\nimport org.bonitasoft.engine.bpm.process.ProcessInstance\nimport org.bonitasoft.engine.exception.SearchException\nimport org.bonitasoft.engine.identity.User\nimport org.bonitasoft.engine.session.APISession\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.mockito.Mock\nimport org.mockito.junit.MockitoJUnitRunner\n\n@RunWith(MockitoJUnitRunner.class)\npublic class CaseContextPermissionRuleTest {\n\n    @Mock\n    def APISession apiSession\n    @Mock\n    def APICallContext apiCallContext\n    @Mock\n    def APIAccessor apiAccessor\n    @Mock\n    def Logger logger\n    @Mock\n    def ProcessAPI processAPI\n    @Mock\n    def IdentityAPI identityAPI\n    @Mock\n    def User user\n    def long currentUserId = 16l\n    def CaseContextPermissionRule rule = new CaseContextPermissionRule()\n\n    @Before\n    public void before() {\n        doReturn(processAPI).when(apiAccessor).getProcessAPI()\n        doReturn(currentUserId).when(apiSession).getUserId()\n    }\n\n    @Test\n    public void should_check_verify_resourceId_isInvolved_on_GET() {\n        //given\n        havingResourceId(true, false, false)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_resourceId_isInvolved_as_manager_on_GET() {\n        //given\n        havingResourceId(false, true, false)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_resourceId_not_isInvolved_on_GET() {\n        //given\n        havingResourceId(false, false, false)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isFalse()\n    }\n\n\n    @Test\n    public void should_check_verify_resourceId_not_isInvolved_on_GET_but_supervisor() {\n        //given\n        havingResourceId(false, false, true)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_resourceId_archived_isInvolved_on_GET() {\n        //given\n        havingArchivedResourceId(true, false, false)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_resourceId_archived_not_isInvolved_on_GET_and_not_supervisor() {\n        //given\n        havingArchivedResourceId(false, false, false)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_verify_resourceId_archived_not_isInvolved_on_GET_but_supervisor() {\n        //given\n        havingArchivedResourceId(false, false, true)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_resourceId_archived_isInvolved_as_manager_on_GET() {\n        //given\n        havingArchivedResourceId(false, true, false)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_resourceId_archived_not_isInvolved_on_GET() {\n        //given\n        havingArchivedResourceId(false, false, false)\n        doThrow(new ArchivedProcessInstanceNotFoundException(new Exception())).when(processAPI).getArchivedProcessInstance(45l)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_allow_when_isManager_throws_Exception_on_GET() {\n        //given\n        havingArchivedResourceId(false, false, false)\n        doThrow(new SearchException(new Exception())).when(processAPI).isManagerOfUserInvolvedInProcessInstance(currentUserId, 34l)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    def havingResourceId(boolean isInvolvedIn, boolean isManagerOfInvolvedIn, boolean isProcessSupervisor) {\n        doReturn(currentUserId).when(apiSession).getUserId()\n        doReturn(\"case\").when(apiCallContext).getResourceName()\n        doReturn(Arrays.asList(\"45\", \"context\")).when(apiCallContext).getCompoundResourceId()\n        doReturn(isInvolvedIn).when(processAPI).isInvolvedInProcessInstance(currentUserId, 45l)\n        doReturn(isManagerOfInvolvedIn)when(processAPI).isManagerOfUserInvolvedInProcessInstance(currentUserId, 45l)\n        def processInstance = mock(ProcessInstance.class)\n        doReturn(processInstance).when(processAPI).getProcessInstance(45l)\n        doReturn(1024l).when(processInstance).getProcessDefinitionId()\n        doReturn(isProcessSupervisor).when(processAPI).isUserProcessSupervisor(1024l, currentUserId)\n    }\n\n    def havingArchivedResourceId(boolean isInvolvedIn, boolean isManagerOfInvolvedIn, boolean isProcessSupervisor) {\n        doReturn(currentUserId).when(apiSession).getUserId()\n        doReturn(\"archivedCase\").when(apiCallContext).getResourceName()\n        doReturn(Arrays.asList(\"45\", \"context\")).when(apiCallContext).getCompoundResourceId()\n        def archivedInstance = mock(ArchivedProcessInstance.class)\n        doReturn(archivedInstance).when(processAPI).getArchivedProcessInstance(45l)\n        doReturn(34l).when(archivedInstance).getSourceObjectId()\n        doReturn(isInvolvedIn).when(processAPI).isInvolvedInProcessInstance(currentUserId, 34l)\n        doReturn(isManagerOfInvolvedIn)when(processAPI).isManagerOfUserInvolvedInProcessInstance(currentUserId, 34l)\n        doReturn(1024l).when(archivedInstance).getProcessDefinitionId()\n        doReturn(isProcessSupervisor).when(processAPI).isUserProcessSupervisor(1024l, currentUserId)\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/groovy/org/bonitasoft/permissions/CasePermissionRuleTest.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\nimport static org.mockito.ArgumentMatchers.any\nimport static org.mockito.ArgumentMatchers.eq\nimport static org.mockito.Mockito.*\n\nimport org.assertj.core.api.Assertions\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.IdentityAPI\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.ProcessAPI\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstance\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstanceNotFoundException\nimport org.bonitasoft.engine.bpm.process.ProcessInstance\nimport org.bonitasoft.engine.exception.SearchException\nimport org.bonitasoft.engine.identity.User\nimport org.bonitasoft.engine.search.SearchOptions\nimport org.bonitasoft.engine.search.impl.SearchResultImpl\nimport org.bonitasoft.engine.session.APISession\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.mockito.Mock\nimport org.mockito.junit.MockitoJUnitRunner\n\n@RunWith(MockitoJUnitRunner.class)\npublic class CasePermissionRuleTest {\n\n    @Mock\n    def APISession apiSession\n    @Mock\n    def APICallContext apiCallContext\n    @Mock\n    def APIAccessor apiAccessor\n    @Mock\n    def Logger logger\n    def CasePermissionRule rule = new CasePermissionRule()\n    @Mock\n    def ProcessAPI processAPI\n    @Mock\n    def IdentityAPI identityAPI\n    @Mock\n    def User user\n    def long currentUserId = 16l\n\n    @Before\n    public void before() {\n\n        doReturn(processAPI).when(apiAccessor).getProcessAPI()\n        doReturn(identityAPI).when(apiAccessor).getIdentityAPI()\n        doReturn(user).when(identityAPI).getUser(currentUserId)\n        doReturn(currentUserId).when(apiSession).getUserId()\n    }\n\n    @Test\n    public void should_check_verify_filters_on_GET_with_different_user_involved() {\n        //given\n        havingFilters([user_id: \"15\"])\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isFalse()\n    }\n\n    def havingFilters(Map filters) {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(filters).when(apiCallContext).getFilters()\n    }\n\n    @Test\n    public void should_check_verify_filters_on_GET_with_different_user_started() {\n        //given\n        havingFilters([started_by: \"15\"])\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_verify_filters_on_GET_with_same_user_involved() {\n        //given\n        havingFilters([user_id: \"16\"])\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_filters_on_GET_with_same_user_started() {\n        //given\n        havingFilters([started_by: \"16\"])\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_filters_on_GET_nofilter_on_user() {\n        //given\n        havingFilters([plop: \"16\"])\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_verify_resourceId_isInvolved_on_GET() {\n        //given\n        havingResourceId(true)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_resourceId_isInvolved_as_manager_on_GET() {\n        //given\n        havingResourceId(false)\n        doReturn(true).when(processAPI).isManagerOfUserInvolvedInProcessInstance(currentUserId, 45l)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_resourceId_not_isInvolved_on_GET() {\n        //given\n        havingResourceId(false)\n\n        def processInstance = mock(ProcessInstance.class)\n        doReturn(processInstance).when(processAPI).getProcessInstance(45l)\n        doReturn(1024l).when(processInstance).getProcessDefinitionId()\n        doReturn(false).when(processAPI).isUserProcessSupervisor(1024l, currentUserId)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isFalse()\n    }\n\n\n    @Test\n    public void should_check_verify_resourceId_not_isInvolved_on_GET_but_supervisor() {\n        //given\n        havingResourceId(false)\n\n        def processInstance = mock(ProcessInstance.class)\n        doReturn(processInstance).when(processAPI).getProcessInstance(45l)\n        doReturn(1024l).when(processInstance).getProcessDefinitionId()\n        doReturn(true).when(processAPI).isUserProcessSupervisor(1024l, currentUserId)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_resourceId_archived_isInvolved_on_GET() {\n        //given\n        havingArchivedResourceId(true)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_resourceId_archived_not_isInvolved_on_GET_and_not_supervisor() {\n        //given\n        havingArchivedResourceId(false)\n        doReturn(false).when(processAPI).isManagerOfUserInvolvedInProcessInstance(currentUserId, 45l)\n        def instance = mock(ArchivedProcessInstance.class)\n        doReturn(instance).when(processAPI).getArchivedProcessInstance(45l)\n        doReturn(1024l).when(instance).getProcessDefinitionId()\n        doReturn(false).when(processAPI).isUserProcessSupervisor(1024l, currentUserId)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_verify_resourceId_archived_not_isInvolved_on_GET_but_supervisor() {\n        //given\n        havingArchivedResourceId(false)\n        def instance = mock(ArchivedProcessInstance.class)\n        doReturn(instance).when(processAPI).getArchivedProcessInstance(45l)\n        doReturn(1024l).when(instance).getProcessDefinitionId()\n        doReturn(true).when(processAPI).isUserProcessSupervisor(1024l, currentUserId)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_resourceId_archived_not_isInvolved_on_GET() {\n        //given\n        havingArchivedResourceId(false)\n        doThrow(new ArchivedProcessInstanceNotFoundException(new Exception())).when(processAPI).getArchivedProcessInstance(45l)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_archived_filters_sourceObjectId_on_GET_not_involved_but_supervisor() {\n        //given\n        havingFilters([sourceObjectId: \"123\"])\n        doReturn(\"archivedCase\").when(apiCallContext).getResourceName()\n        doReturn(new SearchResultImpl<ArchivedProcessInstance>(0, [])).when(processAPI).searchArchivedProcessInstancesInvolvingUser(eq(currentUserId), any(SearchOptions.class))\n        def archivedInstance = mock(ArchivedProcessInstance.class)\n        doReturn(archivedInstance).when(processAPI).getFinalArchivedProcessInstance(123l)\n        doReturn(1024l).when(archivedInstance).getProcessDefinitionId()\n        doReturn(true).when(processAPI).isUserProcessSupervisor(1024l, currentUserId)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_allow_when_isManager_throws_Exception_on_GET() {\n        //given\n        havingResourceId(false)\n        doThrow(new SearchException(new Exception())).when(processAPI).isManagerOfUserInvolvedInProcessInstance(currentUserId, 45l)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    def havingResourceId(boolean isInvolvedIn) {\n        doReturn(currentUserId).when(apiSession).getUserId()\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(\"case\").when(apiCallContext).getResourceName()\n        doReturn(\"45\").when(apiCallContext).getResourceId()\n        doReturn(isInvolvedIn).when(processAPI).isInvolvedInProcessInstance(currentUserId, 45l)\n    }\n\n    def havingArchivedResourceId(boolean isInvolvedIn) {\n        doReturn(currentUserId).when(apiSession).getUserId()\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(\"archivedCase\").when(apiCallContext).getResourceName()\n        doReturn(\"45\").when(apiCallContext).getResourceId()\n        def instance = mock(ArchivedProcessInstance.class)\n        doReturn(instance).when(processAPI).getArchivedProcessInstance(45l)\n        doReturn(42l).when(instance).getSourceObjectId()\n        doReturn(isInvolvedIn).when(processAPI).isInvolvedInProcessInstance(currentUserId, 42l)\n    }\n\n    @Test\n    public void should_check_verify_can_start_on_post_is_true() {\n        doReturn(true).when(apiCallContext).isPOST()\n        doReturn('''\n            {\n                \"processDefinitionId\":\"154\",\n                \"other\":\"sample\"\n            }\n        ''').when(apiCallContext).getBody()\n        doReturn(new SearchResultImpl<User>(1, [user])).when(processAPI).searchUsersWhoCanStartProcessDefinition(eq(154l), any(SearchOptions.class))\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_can_start_on_post_is_false() {\n        doReturn(true).when(apiCallContext).isPOST()\n        doReturn('''\n            {\n                \"processDefinitionId\":\"154\",\n                \"other\":\"sample\"\n            }\n        ''').when(apiCallContext).getBody()\n        doReturn(new SearchResultImpl<User>(0, [])).when(processAPI).searchUsersWhoCanStartProcessDefinition(eq(154l), any(SearchOptions.class))\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_verify_can_start_on_post_with_bad_body_is_true() {\n        doReturn(true).when(apiCallContext).isPOST()\n        doReturn('''\n            {\n                \"unknown\":\"154\",\n                \"other\":\"sample\"\n            }\n        ''').when(apiCallContext).getBody()\n        doReturn(new SearchResultImpl<User>(1, [user])).when(processAPI).searchUsersWhoCanStartProcessDefinition(eq(154l), any(SearchOptions.class))\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_allow_on_PUT_when_user_is_process_supervisor() {\n        // given\n        doReturn(true).when(apiCallContext).isPUT()\n        doReturn(\"case\").when(apiCallContext).getResourceName()\n        doReturn(\"45\").when(apiCallContext).getResourceId()\n\n        def processInstance = mock(ProcessInstance.class)\n        doReturn(processInstance).when(processAPI).getProcessInstance(45l)\n        doReturn(1024l).when(processInstance).getProcessDefinitionId()\n        doReturn(true).when(processAPI).isUserProcessSupervisor(1024l, currentUserId)\n\n        // when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n\n        // then\n        Assertions.assertThat(isAuthorized).isTrue()\n        verify(processAPI).getProcessInstance(45l)\n        verify(processAPI).isUserProcessSupervisor(1024l, currentUserId)\n    }\n\n    @Test\n    public void should_deny_on_PUT_when_user_is_not_process_supervisor() {\n        // given\n        doReturn(true).when(apiCallContext).isPUT()\n        doReturn(\"case\").when(apiCallContext).getResourceName()\n        doReturn(\"45\").when(apiCallContext).getResourceId()\n\n        def processInstance = mock(ProcessInstance.class)\n        doReturn(processInstance).when(processAPI).getProcessInstance(45l)\n        doReturn(1024l).when(processInstance).getProcessDefinitionId()\n        doReturn(false).when(processAPI).isUserProcessSupervisor(1024l, currentUserId)\n\n        // when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n\n        // then\n        Assertions.assertThat(isAuthorized).isFalse()\n        verify(processAPI).getProcessInstance(45l)\n        verify(processAPI).isUserProcessSupervisor(1024l, currentUserId)\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/groovy/org/bonitasoft/permissions/CaseVariablePermissionRuleTest.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\nimport static org.assertj.core.api.Assertions.assertThat\nimport static org.mockito.Mockito.doReturn\nimport static org.mockito.Mockito.mock\n\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.IdentityAPI\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.ProcessAPI\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.bpm.process.ProcessInstance\nimport org.bonitasoft.engine.identity.User\nimport org.bonitasoft.engine.session.APISession\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.mockito.Mock\nimport org.mockito.junit.MockitoJUnitRunner\n\n@RunWith(MockitoJUnitRunner.class)\nclass CaseVariablePermissionRuleTest {\n\n    @Mock\n    def APISession apiSession\n    @Mock\n    def APICallContext apiCallContext\n    @Mock\n    def APIAccessor apiAccessor\n    @Mock\n    def Logger logger\n\n    def CaseVariablePermissionRule rule = new CaseVariablePermissionRule()\n\n    @Mock\n    def ProcessAPI processAPI\n    @Mock\n    def IdentityAPI identityAPI\n    @Mock\n    def User user\n    def long currentUserId = 16l\n\n    @Before\n    void before() {\n        doReturn(processAPI).when(apiAccessor).getProcessAPI()\n        doReturn(currentUserId).when(apiSession).getUserId()\n    }\n\n    def havingProcessInstance(boolean isSupervisor) {\n        def instance = mock(ProcessInstance.class)\n        doReturn(425l).when(instance).getProcessDefinitionId()\n        doReturn(instance).when(processAPI).getProcessInstance(158l)\n        doReturn(isSupervisor).when(processAPI).isUserProcessSupervisor(425l, currentUserId)\n    }\n\n    def havingFilters(Map filters) {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(filters).when(apiCallContext).getFilters()\n    }\n\n    @Test\n    void should_check_return_false_on_delete() {\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    void should_check_return_true_on_get() {\n        //given\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(\"158/myData\").when(apiCallContext).getResourceId()\n        havingProcessInstance(true)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    void should_check_return_false_on_get_if_not_process_owner() {\n        //given\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(\"158/myData\").when(apiCallContext).getResourceId()\n        havingProcessInstance(false)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    void should_check_return_true_on_put() {\n        //given\n        doReturn(true).when(apiCallContext).isPUT()\n        doReturn(\"158/myData\").when(apiCallContext).getResourceId()\n        havingProcessInstance(true)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    void should_check_return_true_on_search_if_supervisor() {\n        havingFilters([case_id: \"158\"])\n        havingProcessInstance(true)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    void should_check_return_false_on_search_if_not_supervisor() {\n        havingFilters([case_id: \"158\"])\n        havingProcessInstance(false)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/groovy/org/bonitasoft/permissions/CommentPermissionRuleTest.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\nimport static org.mockito.Mockito.*\n\nimport org.assertj.core.api.Assertions\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.IdentityAPI\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.ProcessAPI\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.bpm.process.ProcessInstance\nimport org.bonitasoft.engine.exception.SearchException\nimport org.bonitasoft.engine.identity.User\nimport org.bonitasoft.engine.session.APISession\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.mockito.Mock\nimport org.mockito.junit.MockitoJUnitRunner\n\n@RunWith(MockitoJUnitRunner.class)\npublic class CommentPermissionRuleTest {\n\n    @Mock\n    def APISession apiSession\n    @Mock\n    def APICallContext apiCallContext\n    @Mock\n    def APIAccessor apiAccessor\n    @Mock\n    def Logger logger\n    def CommentPermissionRule rule = new CommentPermissionRule()\n    @Mock\n    def ProcessAPI processAPI\n    @Mock\n    def IdentityAPI identityAPI\n    @Mock\n    def User user\n    def long currentUserId = 16l\n\n    @Before\n    public void before() {\n        doReturn(processAPI).when(apiAccessor).getProcessAPI()\n        doReturn(currentUserId).when(apiSession).getUserId()\n    }\n\n    @Test\n    public void should_check_verify_filters_on_GET_with_different_user_involved() {\n        //given\n        havingFilters([user_id: \"15\"])\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isFalse()\n    }\n\n    def havingFilters(Map filters) {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(filters).when(apiCallContext).getFilters()\n    }\n\n    @Test\n    public void should_check_verify_filters_on_GET_with_different_user_started() {\n        //given\n        havingFilters([team_manager_id: \"15\"])\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_verify_filters_on_GET_with_same_user_involved() {\n        //given\n        havingFilters([user_id: \"16\"])\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_filters_on_GET_with_same_user_started() {\n        //given\n        havingFilters([team_manager_id: \"16\"])\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_filters_on_GET_nofilter_on_user() {\n        //given\n        havingFilters([plop: \"16\"])\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_allow_if_isInvolved_as_manager_on_POST() {\n        // given\n        doReturn(true).when(apiCallContext).isPOST()\n        doReturn('''{ \"processInstanceId\":\"154\" }''').when(apiCallContext).getBody()\n        def processInstance = mock(ProcessInstance.class)\n        doReturn(1024l).when(processInstance).getProcessDefinitionId()\n        doReturn(processInstance).when(processAPI).getProcessInstance(154l)\n        doReturn(false).when(processAPI).isUserProcessSupervisor(1024l, currentUserId)\n        doReturn(false).when(processAPI).isInvolvedInProcessInstance(currentUserId, 154l)\n        doReturn(true).when(processAPI).isManagerOfUserInvolvedInProcessInstance(currentUserId, 154l)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    def havingResourceId(boolean isInvolvedIn, boolean isInvolvedAsManager) {\n        doReturn(currentUserId).when(apiSession).getUserId()\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn([\"processInstanceId\": \"45\"]).when(apiCallContext).getFilters()\n        def instance = mock(ProcessInstance.class)\n        doReturn(instance).when(processAPI).getProcessInstance(45l)\n        doReturn(1024l).when(instance).getProcessDefinitionId()\n        doReturn(false).when(processAPI).isUserProcessSupervisor(1024l, currentUserId)\n        doReturn(isInvolvedIn).when(processAPI).isInvolvedInProcessInstance(currentUserId, 45l)\n        doReturn(isInvolvedAsManager).when(processAPI).isManagerOfUserInvolvedInProcessInstance(currentUserId, 45l)\n    }\n\n    @Test\n    public void should_allow_if_isInvolved_himself_on_get() {\n        // given\n        havingResourceId(true, false)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_allow_if_isInvolved_as_manager_on_get() {\n        // given\n        havingResourceId(false, true)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_not_allow_if_isNotInvolved_on_get() {\n        // given\n        havingResourceId(false, false)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_allow_if_isManager_throws_exception_on_get() {\n        // given\n        havingResourceId(false, false)\n        doThrow(new SearchException(new Exception())).when(processAPI).isManagerOfUserInvolvedInProcessInstance(currentUserId, 45l)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_can_start_on_post_is_true() {\n        doReturn(true).when(apiCallContext).isPOST()\n        doReturn('''\n            {\n                \"processInstanceId\":\"154\",\n                \"other\":\"sample\"\n            }\n        ''').when(apiCallContext).getBody()\n        doReturn(true).when(processAPI).isInvolvedInProcessInstance(currentUserId, 154l)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_can_start_on_post_is_false() {\n        doReturn(true).when(apiCallContext).isPOST()\n        doReturn('''\n            {\n                \"processInstanceId\":\"154\",\n                \"other\":\"sample\"\n            }\n        ''').when(apiCallContext).getBody()\n        def processInstance = mock(ProcessInstance.class)\n        doReturn(1024l).when(processInstance).getProcessDefinitionId()\n        doReturn(processInstance).when(processAPI).getProcessInstance(154l)\n        doReturn(false).when(processAPI).isUserProcessSupervisor(1024l, currentUserId)\n        doReturn(false).when(processAPI).isInvolvedInProcessInstance(currentUserId, 154l)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_verify_can_start_on_post_is_false_but_is_supervisor() {\n        doReturn(true).when(apiCallContext).isPOST()\n        doReturn('''\n            {\n                \"processInstanceId\":\"154\",\n                \"other\":\"sample\"\n            }\n        ''').when(apiCallContext).getBody()\n        def processInstance = mock(ProcessInstance.class)\n        doReturn(1024l).when(processInstance).getProcessDefinitionId()\n        doReturn(processInstance).when(processAPI).getProcessInstance(154l)\n        doReturn(true).when(processAPI).isUserProcessSupervisor(1024l, currentUserId)\n        doReturn(false).when(processAPI).isInvolvedInProcessInstance(currentUserId, 154l)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_can_start_on_post_with_bad_body_is_false() {\n        doReturn(true).when(apiCallContext).isPOST()\n        doReturn('''\n            {\n                \"unknown\":\"154\",\n                \"other\":\"sample\"\n            }\n        ''').when(apiCallContext).getBody()\n        doReturn(true).when(processAPI).isInvolvedInProcessInstance(currentUserId, 154l)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/groovy/org/bonitasoft/permissions/ConnectorInstancePermissionRuleTest.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\nimport static org.assertj.core.api.Assertions.assertThat\nimport static org.mockito.ArgumentMatchers.any\nimport static org.mockito.Mockito.doReturn\n\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.ProcessAPI\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.api.permission.PermissionRule\nimport org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstance\nimport org.bonitasoft.engine.identity.User\nimport org.bonitasoft.engine.search.SearchOptions\nimport org.bonitasoft.engine.search.SearchResult\nimport org.bonitasoft.engine.session.APISession\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.mockito.Mock\nimport org.mockito.junit.MockitoJUnitRunner\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ConnectorInstancePermissionRuleTest {\n\n    @Mock\n    def APISession apiSession\n    @Mock\n    def APICallContext apiCallContext\n    @Mock\n    def APIAccessor apiAccessor\n    @Mock\n    def Logger logger\n    def PermissionRule rule = new ConnectorInstancePermissionRule()\n    @Mock\n    def ProcessAPI processAPI\n    @Mock\n    def FlowNodeInstance flownodeInstance\n    @Mock\n    def SearchResult<ArchivedFlowNodeInstance> archivedFlowNodeInstanceSearchResult\n    @Mock\n    def ArchivedFlowNodeInstance archivedFlowNodeInstance\n    @Mock\n    def User user\n    def long currentUserId = 16l\n\n    @Before\n    public void before() {\n        doReturn(processAPI).when(apiAccessor).getProcessAPI()\n        doReturn(currentUserId).when(apiSession).getUserId()\n    }\n\n    @Test\n    public void should_check_verify_get_is_true_when_process_owner() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(\n                [\n                    \"containerId\": \"2\"\n                ]\n                ).when(apiCallContext).getFilters()\n        doReturn(\"connectorInstance\").when(apiCallContext).getResourceName()\n        doReturn(flownodeInstance).when(processAPI).getFlowNodeInstance(2l)\n        doReturn(1l).when(flownodeInstance).getProcessDefinitionId()\n        doReturn(true).when(processAPI).isUserProcessSupervisor(1l, currentUserId)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_get_is_false_when_not_process_owner() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(\n                [\n                    \"containerId\": \"2\"\n                ]\n                ).when(apiCallContext).getFilters()\n        doReturn(\"connectorInstance\").when(apiCallContext).getResourceName()\n        doReturn(flownodeInstance).when(processAPI).getFlowNodeInstance(2l)\n        doReturn(1l).when(flownodeInstance).getProcessDefinitionId()\n        doReturn(false).when(processAPI).isUserProcessSupervisor(1l, currentUserId)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_verify_get_for_archived_is_true_when_process_owner() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(\n                [\n                    \"containerId\": \"2\"\n                ]\n                ).when(apiCallContext).getFilters()\n        doReturn(\"archivedConnectorInstance\").when(apiCallContext).getResourceName()\n        doReturn(archivedFlowNodeInstanceSearchResult).when(processAPI).searchArchivedFlowNodeInstances(any(SearchOptions.class))\n        doReturn([archivedFlowNodeInstance]).when(archivedFlowNodeInstanceSearchResult).getResult()\n        doReturn(1l).when(archivedFlowNodeInstance).getProcessDefinitionId()\n        doReturn(true).when(processAPI).isUserProcessSupervisor(1l, currentUserId)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_get_for_archived_is_false_when_not_process_owner() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(\n                [\n                    \"containerId\": \"2\"\n                ]\n                ).when(apiCallContext).getFilters()\n        doReturn(\"archivedConnectorInstance\").when(apiCallContext).getResourceName()\n        doReturn(archivedFlowNodeInstanceSearchResult).when(processAPI).searchArchivedFlowNodeInstances(any(SearchOptions.class))\n        doReturn([archivedFlowNodeInstance]).when(archivedFlowNodeInstanceSearchResult).getResult()\n        doReturn(1l).when(archivedFlowNodeInstance).getProcessDefinitionId()\n        doReturn(false).when(processAPI).isUserProcessSupervisor(1l, currentUserId)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_verify_get_is_false_when_no_process_id() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(\n                [\n                    \"other\": \"sample\"\n                ]\n                ).when(apiCallContext).getFilters()\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/groovy/org/bonitasoft/permissions/DocumentPermissionRuleTest.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\nimport static org.assertj.core.api.Assertions.assertThat\nimport static org.mockito.Mockito.*\n\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.IdentityAPI\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.ProcessAPI\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.api.permission.PermissionRule\nimport org.bonitasoft.engine.bpm.document.Document\nimport org.bonitasoft.engine.bpm.process.ProcessInstance\nimport org.bonitasoft.engine.exception.BonitaException\nimport org.bonitasoft.engine.identity.User\nimport org.bonitasoft.engine.search.SearchOptions\nimport org.bonitasoft.engine.search.SearchResult\nimport org.bonitasoft.engine.session.APISession\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.mockito.Mock\nimport org.mockito.junit.MockitoJUnitRunner\n\n@RunWith(MockitoJUnitRunner.class)\npublic class DocumentPermissionRuleTest {\n\n    @Mock\n    def APISession apiSession\n    @Mock\n    def APICallContext apiCallContext\n    @Mock\n    def APIAccessor apiAccessor\n    @Mock\n    def Logger logger\n    def PermissionRule rule = new DocumentPermissionRule()\n    @Mock\n    def ProcessAPI processAPI\n    @Mock\n    def IdentityAPI identityAPI\n    @Mock\n    def User user\n    def long currentUserId = 16l\n\n    @Before\n    public void before() {\n        doReturn(processAPI).when(apiAccessor).getProcessAPI()\n        doReturn(currentUserId).when(apiSession).getUserId()\n    }\n\n    @Test\n    public void should_check_verify_filters_on_GET_with_user_not_involved_nor_supervisor() {\n        //given\n        havingFilters([caseId: \"46\"])\n        def processInstance = mock(ProcessInstance.class)\n        doReturn(1024l).when(processInstance).getProcessDefinitionId()\n        doReturn(processInstance).when(processAPI).getProcessInstance(46l)\n        doReturn(false).when(processAPI).isUserProcessSupervisor(1024l, currentUserId)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_verify_filters_on_GET_with_user_not_involved_but_supervisor() {\n        //given\n        havingFilters([caseId: \"46\"])\n        def processInstance = mock(ProcessInstance.class)\n        doReturn(1024l).when(processInstance).getProcessDefinitionId()\n        doReturn(processInstance).when(processAPI).getProcessInstance(46l)\n        doReturn(true).when(processAPI).isUserProcessSupervisor(1024l, currentUserId)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    def havingFilters(Map filters) {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(filters).when(apiCallContext).getFilters()\n        doReturn(true).when(processAPI).isInvolvedInProcessInstance(currentUserId, 45l)\n        def SearchResult = mock(SearchResult.class)\n        doReturn(SearchResult).when(processAPI).searchMyAvailableHumanTasks(eq(currentUserId), any(SearchOptions.class))\n    }\n\n    def havingResourceId(boolean isInvolvedIn, boolean isInvolvedAsManager) {\n        doReturn(currentUserId).when(apiSession).getUserId()\n        doReturn(\"77\").when(apiCallContext).getResourceId()\n        def document = mock(Document.class)\n        doReturn(document).when(processAPI).getDocument(77L)\n        doReturn(123L).when(document).getProcessInstanceId()\n        def instance = mock(ProcessInstance.class)\n        doReturn(instance).when(processAPI).getProcessInstance(123L)\n        doReturn(2048L).when(instance).getProcessDefinitionId()\n        doReturn(false).when(processAPI).isUserProcessSupervisor(2048L, currentUserId)\n        doReturn(isInvolvedIn).when(processAPI).isInvolvedInProcessInstance(currentUserId, 123L)\n        doReturn(isInvolvedAsManager).when(processAPI).isManagerOfUserInvolvedInProcessInstance(currentUserId, 123L)\n        def SearchResult = mock(SearchResult.class)\n        doReturn(SearchResult).when(processAPI).searchMyAvailableHumanTasks(eq(currentUserId), any(SearchOptions.class))\n    }\n\n    @Test\n    public void should_check_verify_filters_on_GET_with_user_involved() {\n        //given\n        havingFilters([caseId: \"45\"])\n        def instance = mock(ProcessInstance.class)\n        doReturn(instance).when(processAPI).getProcessInstance(45L)\n        doReturn(1024L).when(instance).getProcessDefinitionId()\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_allow_user_involved_for_himself_on_GET() {\n        //given\n        havingResourceId(true, false)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_allow_user_involved_as_manager_on_GET() {\n        //given\n        havingResourceId(false, true)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_not_allow_user_not_involved_and_not_supervisor() {\n        //given\n        havingResourceId(false, false)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_not_allow_user_when_isManager_throws_exception() {\n        //given\n        havingResourceId(false, false)\n        doThrow(new BonitaException(\"cause\")).when(processAPI).isManagerOfUserInvolvedInProcessInstance(currentUserId, 123L)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_verify_filters_on_GET_no_filter() {\n        //given\n        havingFilters([plop: \"16\"])\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_verify_can_start_on_post_is_true() {\n        doReturn(true).when(apiCallContext).isPOST()\n        doReturn('''\n            {\n                \"caseId\":\"154\",\n                \"other\":\"sample\"\n            }\n        ''').when(apiCallContext).getBody()\n        doReturn(true).when(processAPI).isInvolvedInProcessInstance(currentUserId, 154l)\n        def instance = mock(ProcessInstance.class)\n        doReturn(instance).when(processAPI).getProcessInstance(154L)\n        doReturn(1024L).when(instance).getProcessDefinitionId()\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_can_start_on_post_is_false() {\n        doReturn(true).when(apiCallContext).isPOST()\n        doReturn('''\n            {\n                \"caseId\":\"154\",\n                \"other\":\"sample\"\n            }\n        ''').when(apiCallContext).getBody()\n        def instance = mock(ProcessInstance.class)\n        doReturn(instance).when(processAPI).getProcessInstance(154L)\n        doReturn(1024L).when(instance).getProcessDefinitionId()\n        doReturn(false).when(processAPI).isInvolvedInProcessInstance(currentUserId, 154l)\n        def SearchResult = mock(SearchResult.class)\n        doReturn(SearchResult).when(processAPI).searchMyAvailableHumanTasks(eq(currentUserId), any(SearchOptions.class))\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_verify_can_start_on_post_with_bad_body_is_true() {\n        doReturn(true).when(apiCallContext).isPOST()\n        doReturn('''\n            {\n                \"unknown\":\"154\",\n                \"other\":\"sample\"\n            }\n        ''').when(apiCallContext).getBody()\n        doReturn(true).when(processAPI).isInvolvedInProcessInstance(currentUserId, 154l)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/groovy/org/bonitasoft/permissions/DownloadDocumentPermissionRuleTest.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.ProcessAPI\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.api.permission.PermissionRule\nimport org.bonitasoft.engine.bpm.document.Document\nimport org.bonitasoft.engine.bpm.document.ArchivedDocument\nimport org.bonitasoft.engine.bpm.process.ProcessInstance\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstance\nimport org.bonitasoft.engine.identity.User\nimport org.bonitasoft.engine.search.SearchOptions\nimport org.bonitasoft.engine.search.SearchResult\nimport org.bonitasoft.engine.search.impl.SearchResultImpl\nimport org.bonitasoft.engine.session.APISession\nimport org.bonitasoft.engine.bpm.document.DocumentNotFoundException\nimport org.bonitasoft.engine.bpm.document.ArchivedDocumentNotFoundException\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceNotFoundException\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.mockito.Mock\nimport org.mockito.junit.MockitoJUnitRunner\n\nimport static org.assertj.core.api.Assertions.assertThat\nimport static org.mockito.ArgumentMatchers.any\nimport static org.mockito.ArgumentMatchers.eq\nimport static org.mockito.Mockito.doReturn\nimport static org.mockito.Mockito.doThrow\nimport static org.mockito.Mockito.mock\n\n@RunWith(MockitoJUnitRunner.class)\npublic class DownloadDocumentPermissionRuleTest {\n\n    @Mock\n    def APISession apiSession\n    @Mock\n    def APICallContext apiCallContext\n    @Mock\n    def APIAccessor apiAccessor\n    @Mock\n    def Logger logger\n    def PermissionRule rule = new DownloadDocumentPermissionRule()\n    @Mock\n    def ProcessAPI processAPI\n    @Mock\n    def User user\n    def long currentUserId = 16l\n\n    @Before\n    public void before() {\n\n        doReturn(processAPI).when(apiAccessor).getProcessAPI()\n        doReturn(currentUserId).when(apiSession).getUserId()\n    }\n\n    @Test\n    public void should_allow_on_GET_document_not_found() {\n        //given\n        def documentId = \"45\"\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn([document: [documentId] as String[]]).when(apiCallContext).getParameters()\n        doThrow(new DocumentNotFoundException(\"\")).when(processAPI).getDocument(Long.valueOf(documentId))\n        doThrow(new ArchivedDocumentNotFoundException(new Exception(\"\"))).when(processAPI).getArchivedVersionOfProcessDocument(Long.valueOf(documentId))\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        //let the download servlet handle the 404\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_deny_on_GET_with_user_not_involved_nor_supervisor() {\n        //given\n        havingDocumentParameter()\n        havingInvolvementInProcessInstance(false, false, false, false)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_deny_on_GET_with_user_not_involved_nor_supervisor_on_archived_case() {\n        //given\n        havingDocumentParameter()\n        havingInvolvementInArchivedProcessInstance(false, false, false)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_deny_on_GET_content_storage_id_with_user_not_involved_nor_supervisor() {\n        //given\n        havingContentStorageIdParameter()\n        havingInvolvementInProcessInstance(false, false, false, false)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_deny_on_GET_content_storage_id_with_user_not_involved_nor_supervisor_on_archived_case() {\n        //given\n        havingContentStorageIdParameterForArchivedDocument()\n        havingInvolvementInArchivedProcessInstance(false, false, false)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_allow_on_GET_with_user_not_involved_but_supervisor() {\n        //given\n        havingDocumentParameter()\n        havingInvolvementInProcessInstance(true, false, false, false)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_allow_on_GET_with_user_involved() {\n        //given\n        havingDocumentParameter()\n        havingInvolvementInProcessInstance(false, true, false, false)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_allow_on_GET_with_user_involved_on_archived_case() {\n        //given\n        havingDocumentParameter()\n        havingInvolvementInArchivedProcessInstance(false, true, false)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_allow_on_GET_with_manager_of_involved_user() {\n        //given\n        havingDocumentParameter()\n        havingInvolvementInProcessInstance(false, false, true, false)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_allow_on_GET_with_user_involved_and_supervisor() {\n        //given\n        havingDocumentParameter()\n        havingInvolvementInProcessInstance(true, true, true, false)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_allow_on_GET_with_user_involved_in_process_instance_as_subprocess() {\n        //given\n        havingDocumentParameter()\n        havingInvolvementInProcessInstance(false, false, false, true)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    def havingDocumentParameter() {\n        def documentId = \"46\"\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn([document: [documentId] as String[]]).when(apiCallContext).getParameters()\n        def document = mock(Document.class)\n        doReturn(document).when(processAPI).getDocument(Long.valueOf(documentId))\n        doReturn(123L).when(document).getProcessInstanceId()\n    }\n\n    def havingDocumentParameterForArchivedDocument() {\n        def documentId = \"46\"\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn([document: [documentId] as String[]]).when(apiCallContext).getParameters()\n        doThrow(new DocumentNotFoundException(\"\")).when(processAPI).getDocument(Long.valueOf(documentId))\n        def archivedDocument = mock(ArchivedDocument.class)\n        doReturn(archivedDocument).when(processAPI).getArchivedVersionOfProcessDocument(Long.valueOf(documentId))\n        doReturn(123L).when(archivedDocument).getProcessInstanceId()\n    }\n\n    def havingContentStorageIdParameter() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn([contentStorageId: [\"45\"] as String[], fileName: [\"test.txt\"] as String[]]).when(apiCallContext).getParameters()\n        def document = mock(Document.class)\n        doReturn(new SearchResultImpl(1, [document])).when(processAPI).searchDocuments(any(SearchOptions.class))\n        doReturn(123L).when(document).getProcessInstanceId()\n    }\n\n    def havingContentStorageIdParameterForArchivedDocument() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn([contentStorageId: [\"45\"] as String[], fileName: [\"test.txt\"] as String[]]).when(apiCallContext).getParameters()\n        doReturn(new SearchResultImpl(0, [])).when(processAPI).searchDocuments(any(SearchOptions.class))\n        def document = mock(ArchivedDocument.class)\n        doReturn(new SearchResultImpl(1, [document])).when(processAPI).searchArchivedDocuments(any(SearchOptions.class))\n        doReturn(123L).when(document).getProcessInstanceId()\n    }\n\n    def havingInvolvementInProcessInstance(boolean isSupervisor, boolean isInvolvedIn, boolean isInvolvedAsManager, boolean isInvolvedInForSubprocesses) {\n        def instance = mock(ProcessInstance.class)\n        doReturn(instance).when(processAPI).getProcessInstance(123L)\n        doReturn(2048L).when(instance).getProcessDefinitionId()\n        doReturn(isSupervisor).when(processAPI).isUserProcessSupervisor(2048L, currentUserId)\n        doReturn(isInvolvedIn).when(processAPI).isInvolvedInProcessInstance(currentUserId, 123L)\n        doReturn(isInvolvedAsManager).when(processAPI).isManagerOfUserInvolvedInProcessInstance(currentUserId, 123L)\n        def searchResult = mock(SearchResult.class)\n        if (isInvolvedInForSubprocesses) {\n            doReturn(1L).when(searchResult).getCount()\n        }\n        doReturn(searchResult).when(processAPI).searchMyAvailableHumanTasks(eq(currentUserId), any(SearchOptions.class))\n    }\n\n    def havingInvolvementInArchivedProcessInstance(boolean isSupervisor, boolean isInvolvedIn, boolean isInvolvedAsManager) {\n        def archivedInstance = mock(ArchivedProcessInstance.class)\n        doThrow(new ProcessInstanceNotFoundException(\"\")).when(processAPI).getProcessInstance(123L)\n        doReturn(archivedInstance).when(processAPI).getFinalArchivedProcessInstance(123L)\n        doReturn(2048L).when(archivedInstance).getProcessDefinitionId()\n        doReturn(isSupervisor).when(processAPI).isUserProcessSupervisor(2048L, currentUserId)\n        doReturn(isInvolvedIn).when(processAPI).isInvolvedInProcessInstance(currentUserId, 123L)\n        doReturn(isInvolvedAsManager).when(processAPI).isManagerOfUserInvolvedInProcessInstance(currentUserId, 123L)\n        def searchResult = mock(SearchResult.class)\n        doReturn(searchResult).when(processAPI).searchMyAvailableHumanTasks(eq(currentUserId), any(SearchOptions.class))\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/groovy/org/bonitasoft/permissions/ProcessConfigurationPermissionRuleTest.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\nimport static org.assertj.core.api.Assertions.assertThat\nimport static org.mockito.Mockito.doReturn\n\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.ProcessAPI\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.api.permission.PermissionRule\nimport org.bonitasoft.engine.identity.User\nimport org.bonitasoft.engine.session.APISession\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.mockito.Mock\nimport org.mockito.junit.MockitoJUnitRunner\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ProcessConfigurationPermissionRuleTest {\n\n    @Mock\n    def APISession apiSession\n    @Mock\n    def APICallContext apiCallContext\n    @Mock\n    def APIAccessor apiAccessor\n    @Mock\n    def Logger logger\n    def PermissionRule rule = new ProcessConfigurationPermissionRule()\n    @Mock\n    def ProcessAPI processAPI\n    @Mock\n    def User user\n    def long currentUserId = 16l\n\n    @Before\n    public void before() {\n        doReturn(processAPI).when(apiAccessor).getProcessAPI()\n        doReturn(currentUserId).when(apiSession).getUserId()\n    }\n\n    @Test\n    public void should_check_verify_get_multiple_is_true_when_process_owner() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(\n                [\n                    \"process_id\": \"1\"\n                ]\n                ).when(apiCallContext).getFilters()\n        doReturn(true).when(processAPI).isUserProcessSupervisor(1l, currentUserId)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_get_multiple_is_false_when_not_process_owner() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(\n                [\n                    \"process_id\": \"1\"\n                ]\n                ).when(apiCallContext).getFilters()\n        doReturn(false).when(processAPI).isUserProcessSupervisor(1l, currentUserId)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_verify_get_is_true_when_process_owner() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn([\"1\", \"connectorName\", \"1.0\"]).when(apiCallContext).getCompoundResourceId()\n        doReturn(true).when(processAPI).isUserProcessSupervisor(1l, currentUserId)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_get_is_false_when_not_process_owner() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn([\"1\", \"connectorName\", \"1.0\"]).when(apiCallContext).getCompoundResourceId()\n        doReturn(false).when(processAPI).isUserProcessSupervisor(1l, currentUserId)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_verify_put_when_not_process_owner() {\n        doReturn(true).when(apiCallContext).isPUT()\n        doReturn([\"1\", \"connectorName\", \"1.0\"]).when(apiCallContext).getCompoundResourceId()\n        doReturn(false).when(processAPI).isUserProcessSupervisor(1l, currentUserId)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_verify_put_when_process_owner() {\n        doReturn(true).when(apiCallContext).isPUT()\n        doReturn([\"1\", \"connectorName\", \"1.0\"]).when(apiCallContext).getCompoundResourceId()\n        doReturn(true).when(processAPI).isUserProcessSupervisor(1l, currentUserId)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_get_is_false_when_no_process_id() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(\n                [\n                    \"other\": \"sample\"\n                ]\n                ).when(apiCallContext).getFilters()\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/groovy/org/bonitasoft/permissions/ProcessConnectorDependencyPermissionRuleTest.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\nimport static org.assertj.core.api.Assertions.assertThat\nimport static org.mockito.Mockito.doReturn\n\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.ProcessAPI\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.api.permission.PermissionRule\nimport org.bonitasoft.engine.identity.User\nimport org.bonitasoft.engine.session.APISession\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.mockito.Mock\nimport org.mockito.junit.MockitoJUnitRunner\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ProcessConnectorDependencyPermissionRuleTest {\n\n    @Mock\n    def APISession apiSession\n    @Mock\n    def APICallContext apiCallContext\n    @Mock\n    def APIAccessor apiAccessor\n    @Mock\n    def Logger logger\n    def PermissionRule rule = new ProcessConnectorDependencyPermissionRule()\n    @Mock\n    def ProcessAPI processAPI\n    @Mock\n    def User user\n    def long currentUserId = 16l\n\n    @Before\n    public void before() {\n        doReturn(processAPI).when(apiAccessor).getProcessAPI()\n        doReturn(currentUserId).when(apiSession).getUserId()\n    }\n\n    @Test\n    public void should_check_verify_get_is_true_when_process_owner() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(\n                [\n                    \"connector_process_id\": \"1\"\n                ]\n                ).when(apiCallContext).getFilters()\n        doReturn(true).when(processAPI).isUserProcessSupervisor(1l, currentUserId)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_get_is_false_when_not_process_owner() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(\n                [\n                    \"connector_process_id\": \"1\"\n                ]\n                ).when(apiCallContext).getFilters()\n        doReturn(false).when(processAPI).isUserProcessSupervisor(1l, currentUserId)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_verify_get_is_false_when_no_process_id() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(\n                [\n                    \"other\": \"sample\"\n                ]\n                ).when(apiCallContext).getFilters()\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/groovy/org/bonitasoft/permissions/ProcessInstantiationPermissionRuleTest.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\nimport static org.mockito.ArgumentMatchers.any\nimport static org.mockito.ArgumentMatchers.eq\nimport static org.mockito.Mockito.doReturn\n\nimport org.assertj.core.api.Assertions\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.IdentityAPI\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.ProcessAPI\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.identity.User\nimport org.bonitasoft.engine.search.SearchOptions\nimport org.bonitasoft.engine.search.impl.SearchResultImpl\nimport org.bonitasoft.engine.session.APISession\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.mockito.Mock\nimport org.mockito.junit.MockitoJUnitRunner\n\n@RunWith(MockitoJUnitRunner.Silent.class)\npublic class ProcessInstantiationPermissionRuleTest {\n\n    @Mock\n    def APISession apiSession\n    @Mock\n    def APICallContext apiCallContext\n    @Mock\n    def APIAccessor apiAccessor\n    @Mock\n    def Logger logger\n    @Mock\n    def ProcessAPI processAPI\n    @Mock\n    def IdentityAPI identityAPI\n    @Mock\n    def User user\n    def long currentUserId = 16l\n    def ProcessInstantiationPermissionRule rule = new ProcessInstantiationPermissionRule()\n\n    @Before\n    public void before() {\n\n        doReturn(processAPI).when(apiAccessor).getProcessAPI()\n        doReturn(identityAPI).when(apiAccessor).getIdentityAPI()\n        doReturn(user).when(identityAPI).getUser(currentUserId)\n        doReturn(currentUserId).when(apiSession).getUserId()\n    }\n\n    def havingResourceId() {\n        doReturn(currentUserId).when(apiSession).getUserId()\n        doReturn(true).when(apiCallContext).isPOST()\n        doReturn(\"process\").when(apiCallContext).getResourceName()\n        doReturn(\"45/instantiation\").when(apiCallContext).getResourceId()\n        doReturn(Arrays.asList(\"45\", \"instantiation\")).when(apiCallContext).getCompoundResourceId()\n    }\n\n    def beingProcessSupervisor(isProcessSupervisor) {\n        doReturn(isProcessSupervisor).when(processAPI).isUserProcessSupervisor(45l, currentUserId)\n    }\n\n    def startingForUser(userId) {\n        String[] startingForUserId = [userId]\n        def parametersMap = [:]\n        parametersMap[\"user\"] = startingForUserId\n        doReturn(parametersMap).when(apiCallContext).getParameters()\n    }\n\n    @Test\n    public void should_check_verify_can_start_is_true() {\n        havingResourceId()\n        beingProcessSupervisor(false)\n        doReturn(new SearchResultImpl<User>(1, [user])).when(processAPI).searchUsersWhoCanStartProcessDefinition(eq(45l), any(SearchOptions.class))\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_can_start_is_false() {\n        havingResourceId()\n        beingProcessSupervisor(false)\n        doReturn(new SearchResultImpl<User>(0, [])).when(processAPI).searchUsersWhoCanStartProcessDefinition(eq(45l), any(SearchOptions.class))\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_verify_can_start_is_false_with_user_param() {\n        havingResourceId()\n        beingProcessSupervisor(false)\n        startingForUser(\"3\")\n        doReturn(new SearchResultImpl<User>(1, [user])).when(processAPI).searchUsersWhoCanStartProcessDefinition(eq(45l), any(SearchOptions.class))\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_verify_can_start_is_true_for_process_supervisor() {\n        havingResourceId()\n        beingProcessSupervisor(true)\n        startingForUser(\"3\")\n        doReturn(new SearchResultImpl<User>(1, [user])).when(processAPI).searchUsersWhoCanStartProcessDefinition(eq(45l), any(SearchOptions.class))\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_can_start_is_false_for_process_supervisor() {\n        havingResourceId()\n        beingProcessSupervisor(true)\n        startingForUser(\"3\")\n        doReturn(new SearchResultImpl<User>(0, [])).when(processAPI).searchUsersWhoCanStartProcessDefinition(eq(45l), any(SearchOptions.class))\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isFalse()\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/groovy/org/bonitasoft/permissions/ProcessPermissionRuleTest.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\nimport static org.mockito.ArgumentMatchers.eq\nimport static org.mockito.Mockito.doReturn\nimport static org.mockito.Mockito.mock\n\nimport org.assertj.core.api.Assertions\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.IdentityAPI\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.ProcessAPI\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo\nimport org.bonitasoft.engine.identity.User\nimport org.bonitasoft.engine.search.SearchOptions\nimport org.bonitasoft.engine.search.impl.SearchResultImpl\nimport org.bonitasoft.engine.session.APISession\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.mockito.Mock\nimport org.mockito.Mockito\nimport org.mockito.junit.MockitoJUnitRunner\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ProcessPermissionRuleTest {\n\n    @Mock\n    def APISession apiSession\n    @Mock\n    def APICallContext apiCallContext\n    @Mock\n    def APIAccessor apiAccessor\n    @Mock\n    def Logger logger\n    def ProcessPermissionRule rule = new ProcessPermissionRule()\n    @Mock\n    def ProcessAPI processAPI\n    @Mock\n    def IdentityAPI identityAPI\n    @Mock\n    def User user\n    def long currentUserId = 16l\n\n    @Before\n    public void before() {\n        doReturn(processAPI).when(apiAccessor).getProcessAPI()\n        doReturn(currentUserId).when(apiSession).getUserId()\n        doReturn([]).when(apiCallContext).getCompoundResourceId()\n    }\n\n    @Test\n    public void should_check_verify_filters_on_GET_with_different_user() {\n        //given\n        havingFilters([user_id: \"15\"])\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_verify_filters_on_GET_with_diff_team_manager_id() {\n        //given\n        havingFilters([team_manager_id: \"15\"])\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_verify_resourceid_on_PUT_when_is_supervisor() {\n        //given\n        doReturn(true).when(apiCallContext).isPUT()\n        doReturn([\"56\"]).when(apiCallContext).getCompoundResourceId()\n        doReturn(true).when(processAPI).isUserProcessSupervisor(56l, currentUserId)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_resourceid_on_PUT_when_is_not_supervisor() {\n        //given\n        doReturn(true).when(apiCallContext).isPUT()\n        doReturn([\"56\"]).when(apiCallContext).getCompoundResourceId()\n        doReturn(false).when(processAPI).isUserProcessSupervisor(56l, currentUserId)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_verify_filters_on_GET_with_diff_supervisor_id() {\n        //given\n        havingFilters([supervisor_id: \"15\"])\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_verify_filters_on_GET_with_same_user() {\n        //given\n        havingFilters([user_id: \"16\"])\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_filters_on_GET_with_same_team_manager_id() {\n        //given\n        havingFilters([team_manager_id: \"16\"])\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_filters_on_GET_with_same_supervisor_id() {\n        //given\n        havingFilters([supervisor_id: \"16\"])\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    def havingFilters(Map filters) {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(filters).when(apiCallContext).getFilters()\n    }\n\n\n    @Test\n    public void should_check_verify_resourceId_isDeployer_on_GET() {\n        //given\n        havingResourceId(currentUserId)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_resourceId_initiator_on_GET() {\n        //given\n        havingResourceId(15)\n        doReturn(new SearchResultImpl(1, [])).when(processAPI).searchProcessDeploymentInfosCanBeStartedBy(eq(currentUserId), Mockito.any(SearchOptions.class))\n        doReturn(new SearchResultImpl(0, [])).when(processAPI).searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(eq(currentUserId), Mockito.any(SearchOptions.class))\n        doReturn(new SearchResultImpl(0, [])).when(processAPI).searchMyAvailableHumanTasks(eq(currentUserId), Mockito.any(SearchOptions.class))\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_resourceId_not_initiator_nor_involved_on_GET() {\n        //given\n        havingResourceId(15)\n        doReturn(new SearchResultImpl(0, [])).when(processAPI).searchProcessDeploymentInfosCanBeStartedBy(eq(currentUserId), Mockito.any(SearchOptions.class))\n        doReturn(new SearchResultImpl(0, [])).when(processAPI).searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(eq(currentUserId), Mockito.any(SearchOptions.class))\n        doReturn(new SearchResultImpl(0, [])).when(processAPI).searchMyAvailableHumanTasks(eq(currentUserId), Mockito.any(SearchOptions.class))\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_verify_resourceId_isInvolved_on_GET() {\n        //given\n        havingResourceId(15)\n        doReturn(new SearchResultImpl(0, [])).when(processAPI).searchProcessDeploymentInfosCanBeStartedBy(eq(currentUserId), Mockito.any(SearchOptions.class))\n        doReturn(new SearchResultImpl(0, [])).when(processAPI).searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(eq(currentUserId), Mockito.any(SearchOptions.class))\n        doReturn(new SearchResultImpl(1, [])).when(processAPI).searchMyAvailableHumanTasks(eq(currentUserId), Mockito.any(SearchOptions.class))\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_resourceId_isInvolved_in_subprocesses_on_GET() {\n        //given\n        havingResourceId(15)\n        doReturn(new SearchResultImpl(0, [])).when(processAPI).searchProcessDeploymentInfosCanBeStartedBy(eq(currentUserId), Mockito.any(SearchOptions.class))\n        doReturn(new SearchResultImpl(1, [])).when(processAPI).searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(eq(currentUserId), Mockito.any(SearchOptions.class))\n        doReturn(new SearchResultImpl(0, [])).when(processAPI).searchMyAvailableHumanTasks(eq(currentUserId), Mockito.any(SearchOptions.class))\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    def havingResourceId(long deployedBy) {\n        doReturn(currentUserId).when(apiSession).getUserId()\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn([\"45\"]).when(apiCallContext).getCompoundResourceId()\n\n        def info = mock(ProcessDeploymentInfo.class)\n        doReturn(deployedBy).when(info).getDeployedBy()\n        doReturn(info).when(processAPI).getProcessDeploymentInfo(45l)\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/groovy/org/bonitasoft/permissions/ProcessResolutionProblemPermissionRuleTest.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\nimport static org.assertj.core.api.Assertions.assertThat\nimport static org.mockito.Mockito.doReturn\n\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.ProcessAPI\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.api.permission.PermissionRule\nimport org.bonitasoft.engine.identity.User\nimport org.bonitasoft.engine.session.APISession\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.mockito.Mock\nimport org.mockito.junit.MockitoJUnitRunner\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ProcessResolutionProblemPermissionRuleTest {\n\n    @Mock\n    def APISession apiSession\n    @Mock\n    def APICallContext apiCallContext\n    @Mock\n    def APIAccessor apiAccessor\n    @Mock\n    def Logger logger\n    def PermissionRule rule = new ProcessResolutionProblemPermissionRule()\n    @Mock\n    def ProcessAPI processAPI\n    @Mock\n    def User user\n    def long currentUserId = 16l\n\n    @Before\n    public void before() {\n        doReturn(processAPI).when(apiAccessor).getProcessAPI()\n        doReturn(currentUserId).when(apiSession).getUserId()\n    }\n\n    @Test\n    public void should_check_verify_get_is_true_when_process_owner() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(\n                [\n                    \"process_id\": \"1\"\n                ]\n                ).when(apiCallContext).getFilters()\n        doReturn(true).when(processAPI).isUserProcessSupervisor(1l, currentUserId)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_get_is_false_when_not_process_owner() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(\n                [\n                    \"process_id\": \"1\"\n                ]\n                ).when(apiCallContext).getFilters()\n        doReturn(false).when(processAPI).isUserProcessSupervisor(1l, currentUserId)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_verify_get_is_false_when_no_process_id() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(\n                [\n                    \"other\": \"sample\"\n                ]\n                ).when(apiCallContext).getFilters()\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/groovy/org/bonitasoft/permissions/ProcessSupervisorPermissionRuleTest.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\nimport static org.assertj.core.api.Assertions.assertThat\nimport static org.mockito.Mockito.doReturn\n\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.IdentityAPI\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.ProcessAPI\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.api.permission.PermissionRule\nimport org.bonitasoft.engine.identity.User\nimport org.bonitasoft.engine.session.APISession\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.mockito.Mock\nimport org.mockito.junit.MockitoJUnitRunner\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ProcessSupervisorPermissionRuleTest {\n\n    @Mock\n    def APISession apiSession\n    @Mock\n    def APICallContext apiCallContext\n    @Mock\n    def APIAccessor apiAccessor\n    @Mock\n    def Logger logger\n    def PermissionRule rule = new ProcessSupervisorPermissionRule()\n    @Mock\n    def ProcessAPI processAPI\n    @Mock\n    def IdentityAPI identityAPI\n    @Mock\n    def User user\n    def long currentUserId = 16l\n\n    @Before\n    public void before() {\n        doReturn(processAPI).when(apiAccessor).getProcessAPI()\n        doReturn(currentUserId).when(apiSession).getUserId()\n    }\n\n    @Test\n    public void should_check_verify_post_is_true_when_process_owner() {\n        doReturn(true).when(apiCallContext).isPOST()\n        doReturn('''\n            {\n                \"process_id\":\"154\",\n                \"other\":\"sample\"\n            }\n        ''').when(apiCallContext).getBody()\n        doReturn(true).when(processAPI).isUserProcessSupervisor(154l, currentUserId)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_post_is_false_when_not_process_owner() {\n        doReturn(true).when(apiCallContext).isPOST()\n        doReturn('''\n            {\n                \"process_id\":\"154\",\n                \"other\":\"sample\"\n            }\n        ''').when(apiCallContext).getBody()\n        doReturn(false).when(processAPI).isUserProcessSupervisor(154l, currentUserId)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_verify_get_is_true_when_process_owner() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn([\n            \"process_id\": \"154\",\n            \"other\"     : \"sample\"\n        ]).when(apiCallContext).getFilters()\n        doReturn(true).when(processAPI).isUserProcessSupervisor(154l, currentUserId)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_get_is_false_when_not_process_owner() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn([\n            \"process_id\": \"154\",\n            \"other\"     : \"sample\"\n        ]).when(apiCallContext).getFilters()\n        doReturn(false).when(processAPI).isUserProcessSupervisor(154l, currentUserId)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_verify_delete_return_false_when_not_process_owner() {\n        doReturn(true).when(apiCallContext).isDELETE()\n        doReturn([\"154\", \"1\", \"2\", \"3\"]).when(apiCallContext).getCompoundResourceId()\n        doReturn(false).when(processAPI).isUserProcessSupervisor(154l, currentUserId)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_verify_delete_return_true_when_process_owner() {\n        doReturn(true).when(apiCallContext).isDELETE()\n        doReturn([\"154\", \"1\", \"2\", \"3\"]).when(apiCallContext).getCompoundResourceId()\n        doReturn(true).when(processAPI).isUserProcessSupervisor(154l, currentUserId)\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/groovy/org/bonitasoft/permissions/ProfilePermissionRuleTest.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\nimport static org.mockito.Mockito.doReturn\n\nimport org.assertj.core.api.Assertions\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.ProfileAPI\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.identity.User\nimport org.bonitasoft.engine.profile.ProfileCriterion\nimport org.bonitasoft.engine.profile.impl.ProfileImpl\nimport org.bonitasoft.engine.session.APISession\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.mockito.Mock\nimport org.mockito.junit.MockitoJUnitRunner\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ProfilePermissionRuleTest {\n\n    @Mock\n    def APISession apiSession\n    @Mock\n    def APICallContext apiCallContext\n    @Mock\n    def APIAccessor apiAccessor\n    @Mock\n    def Logger logger\n    def ProfilePermissionRule rule = new ProfilePermissionRule()\n    @Mock\n    def ProfileAPI profileAPI\n    @Mock\n    def User user\n    def long currentUserId = 16l\n\n    @Before\n    public void before() {\n        doReturn(profileAPI).when(apiAccessor).getProfileAPI()\n        doReturn(currentUserId).when(apiSession).getUserId()\n    }\n\n    @Test\n    public void should_check_verify_get_is_true_when_user_id_in_filters() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn([user_id: \"16\"]).when(apiCallContext).getFilters()\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_get_with_resource_user_is_in_profile() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(\"2\").when(apiCallContext).getResourceId()\n        doReturn([user_id: \"16\"]).when(apiCallContext).getFilters()\n        doReturn([profile(1), profile(2), profile(3)]).when(profileAPI).getProfilesForUser(currentUserId, 0, 100, ProfileCriterion.ID_ASC)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_get_with_resource_user_is_in_profile_with_more_than_100_elements() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(\"110\").when(apiCallContext).getResourceId()\n\n        doReturn((1l..100l).collect { profile(it) }).when(profileAPI).getProfilesForUser(currentUserId, 0, 100, ProfileCriterion.ID_ASC)\n        doReturn((101l..110l).collect { profile(it) }).when(profileAPI).getProfilesForUser(currentUserId, 100, 100, ProfileCriterion.ID_ASC)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_get_with_resource_user_is_not_in_profile() {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(\"2\").when(apiCallContext).getResourceId()\n        doReturn([profile(1), profile(3)]).when(profileAPI).getProfilesForUser(currentUserId, 0, 100, ProfileCriterion.ID_ASC)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_verify_not_get_return_false() {\n        doReturn(false).when(apiCallContext).isGET()\n\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        Assertions.assertThat(isAuthorized).isFalse()\n    }\n\n    private ProfileImpl profile(long id) {\n        def profile = new ProfileImpl(\"profilename\")\n        profile.setId(id)\n        return profile\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/groovy/org/bonitasoft/permissions/TaskExecutionPermissionRuleTest.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\nimport static org.assertj.core.api.Assertions.assertThat\nimport static org.mockito.ArgumentMatchers.any\nimport static org.mockito.ArgumentMatchers.eq\nimport static org.mockito.Mockito.*\n\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.IdentityAPI\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.ProcessAPI\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.bpm.flownode.*\nimport org.bonitasoft.engine.identity.User\nimport org.bonitasoft.engine.search.SearchOptions\nimport org.bonitasoft.engine.search.impl.SearchResultImpl\nimport org.bonitasoft.engine.session.APISession\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.mockito.Mock\nimport org.mockito.junit.MockitoJUnitRunner\n\n@RunWith(MockitoJUnitRunner.class)\npublic class TaskExecutionPermissionRuleTest {\n\n    @Mock\n    def APISession apiSession\n    @Mock\n    def APICallContext apiCallContext\n    @Mock\n    def APIAccessor apiAccessor\n    @Mock\n    def Logger logger\n    @Mock\n    def ProcessAPI processAPI\n    @Mock\n    def IdentityAPI identityAPI\n    @Mock\n    def User user\n    def long currentUserId = 16l\n    def TaskExecutionPermissionRule rule = new TaskExecutionPermissionRule()\n\n    @Before\n    public void before() {\n        doReturn(processAPI).when(apiAccessor).getProcessAPI()\n        doReturn(identityAPI).when(apiAccessor).getIdentityAPI()\n        doReturn(currentUserId).when(apiSession).getUserId()\n    }\n\n    @Test\n    public void should_get_on_a_archived_task_is_ok() {\n        //given\n        havingResource(\"archivedUserTask\")\n        def archivedTask = mock(ArchivedFlowNodeInstance.class)\n        doReturn(FlowNodeType.USER_TASK).when(archivedTask).getType()\n        doReturn(currentUserId).when(archivedTask).getExecutedBy()\n        doReturn(archivedTask).when(processAPI).getArchivedFlowNodeInstance(458)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_get_on_a_archived_task_is_not_the_assignee() {\n        //given\n        havingResource(\"archivedUserTask\")\n        def archivedTask = mock(ArchivedFlowNodeInstance.class)\n        doReturn(FlowNodeType.USER_TASK).when(archivedTask).getType()\n        doReturn(58l).when(archivedTask).getExecutedBy()\n        doReturn(archivedTask).when(processAPI).getArchivedFlowNodeInstance(458)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_get_on_a_archived_task_is_not_found() {\n        //given\n        havingResource(\"archivedUserTask\")\n        doThrow(new ArchivedFlowNodeInstanceNotFoundException(458)).when(processAPI).getArchivedFlowNodeInstance(458)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n\n    @Test\n    public void should_get_on_a_assigned_human_task_is_ok() {\n        //given\n        havingResource(\"userTask\")\n        def instance = mock(UserTaskInstance.class)\n        doReturn(FlowNodeType.USER_TASK).when(instance).getType()\n        doReturn(currentUserId).when(instance).getAssigneeId()\n        doReturn(instance).when(processAPI).getFlowNodeInstance(458)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_get_on_a_assigned_human_task_is_not_ok() {\n        //given\n        havingResource(\"userTask\")\n        def instance = mock(UserTaskInstance.class)\n        doReturn(FlowNodeType.USER_TASK).when(instance).getType()\n        doReturn(59l).when(instance).getAssigneeId()\n        doReturn(instance).when(processAPI).getFlowNodeInstance(458)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_get_on_an_unexisting_flow_node() {\n        //given\n        havingResource(\"userTask\")\n        doThrow(new FlowNodeInstanceNotFoundException(new Exception())).when(processAPI).getFlowNodeInstance(458)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n\n    @Test\n    public void should_get_on_a_pending_task_is_ok() {\n        //given\n        havingResource(\"userTask\")\n        def instance = mock(UserTaskInstance.class)\n        doReturn(FlowNodeType.USER_TASK).when(instance).getType()\n        doReturn(-1l).when(instance).getAssigneeId()\n        doReturn(instance).when(processAPI).getFlowNodeInstance(458)\n        doReturn(new SearchResultImpl(1, [])).when(processAPI).searchUsersWhoCanExecutePendingHumanTask(eq(458l), any(SearchOptions.class))\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_get_on_a_pending_task_is_not_ok() {\n        //given\n        havingResource(\"userTask\")\n        def instance = mock(UserTaskInstance.class)\n        doReturn(FlowNodeType.USER_TASK).when(instance).getType()\n        doReturn(-1l).when(instance).getAssigneeId()\n        doReturn(instance).when(processAPI).getFlowNodeInstance(458)\n        doReturn(new SearchResultImpl(0, [])).when(processAPI).searchUsersWhoCanExecutePendingHumanTask(eq(458l), any(SearchOptions.class))\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_POST_on_a_pending_task_if_supervisor() {\n        //given\n        havingResource(\"userTask\")\n        def instance = mock(UserTaskInstance.class)\n        doReturn(10l).when(instance).getProcessDefinitionId()\n        doReturn(FlowNodeType.USER_TASK).when(instance).getType()\n        doReturn(4l).when(instance).getAssigneeId()\n        doReturn(instance).when(processAPI).getFlowNodeInstance(458l)\n        doReturn(true).when(processAPI).isUserProcessSupervisor(10l, currentUserId)\n        doReturn([user: [\"4\"] as String[]]).when(apiCallContext).getParameters()\n        def assigneUser = mock(User.class)\n        doReturn(assigneUser).when(identityAPI).getUser(4l)\n        doReturn(4l).when(assigneUser).getId()\n        doReturn(new SearchResultImpl(0, [])).when(processAPI).searchUsersWhoCanExecutePendingHumanTask(eq(458l), any(SearchOptions.class))\n        doReturn(false).when(processAPI).isUserProcessSupervisor(10l, 4l)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_not_POST_on_a_pending_task_if_supervisor_but_assigned_id_no_actor() {\n        //given\n        havingResource(\"userTask\")\n        def instance = mock(UserTaskInstance.class)\n        doReturn(10l).when(instance).getProcessDefinitionId()\n        doReturn(FlowNodeType.USER_TASK).when(instance).getType()\n        doReturn(-1l).when(instance).getAssigneeId()\n        doReturn(instance).when(processAPI).getFlowNodeInstance(458l)\n        doReturn(true).when(processAPI).isUserProcessSupervisor(10l, currentUserId)\n        doReturn([user: [\"4\"] as String[]]).when(apiCallContext).getParameters()\n        def assigneUser = mock(User.class)\n        doReturn(assigneUser).when(identityAPI).getUser(4l)\n        doReturn(4l).when(assigneUser).getId()\n        doReturn(new SearchResultImpl(0, [])).when(processAPI).searchUsersWhoCanExecutePendingHumanTask(eq(458l), any(SearchOptions.class))\n        doReturn(false).when(processAPI).isUserProcessSupervisor(10l, 4l)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    def havingResource(String resourceName) {\n        doReturn(Arrays.asList(\"458\", \"execution\")).when(apiCallContext).getCompoundResourceId()\n        doReturn(resourceName).when(apiCallContext).getResourceName()\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/groovy/org/bonitasoft/permissions/TaskPermissionRuleTest.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage org.bonitasoft.permissions\n\nimport static org.assertj.core.api.Assertions.assertThat\nimport static org.mockito.ArgumentMatchers.any\nimport static org.mockito.ArgumentMatchers.eq\nimport static org.mockito.Mockito.*\n\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.IdentityAPI\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.ProcessAPI\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.api.permission.PermissionRule\nimport org.bonitasoft.engine.bpm.flownode.*\nimport org.bonitasoft.engine.bpm.process.ProcessInstance\nimport org.bonitasoft.engine.exception.NotFoundException\nimport org.bonitasoft.engine.identity.User\nimport org.bonitasoft.engine.search.SearchOptions\nimport org.bonitasoft.engine.search.impl.SearchResultImpl\nimport org.bonitasoft.engine.session.APISession\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.mockito.Mock\nimport org.mockito.junit.MockitoJUnitRunner\n\n@RunWith(MockitoJUnitRunner.class)\npublic class TaskPermissionRuleTest {\n\n    @Mock\n    def APISession apiSession\n    @Mock\n    def APICallContext apiCallContext\n    @Mock\n    def APIAccessor apiAccessor\n    @Mock\n    def Logger logger\n    def PermissionRule rule = new TaskPermissionRule()\n    @Mock\n    def ProcessAPI processAPI\n    @Mock\n    def IdentityAPI identityAPI\n    @Mock\n    def User user\n    def long currentUserId = 16l\n\n    @Before\n    public void before() {\n        doReturn(processAPI).when(apiAccessor).getProcessAPI()\n        doReturn(identityAPI).when(apiAccessor).getIdentityAPI()\n        doReturn(currentUserId).when(apiSession).getUserId()\n    }\n\n\n    def havingFilters(Map filters) {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(filters).when(apiCallContext).getFilters()\n    }\n\n    @Test\n    public void should_check_verify_filters_on_GET_with_same_user_involved() {\n        //given\n        havingFilters([user_id: \"16\"])\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_filters_on_GET_with_same_hidden_user_involved() {\n        //given\n        havingFilters([hidden_user_id: \"16\"])\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_filters_on_GET_with_same_assigned_user_involved() {\n        //given\n        havingFilters([assigned_id: \"16\"])\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_check_verify_filters_on_GET_with_diff_user_involved() {\n        //given\n        havingFilters([user_id: \"17\"])\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_check_verify_filters_on_GET_with_diff_hidden_user_involved() {\n        //given\n        havingFilters([hidden_user_id: \"17\"])\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_get_on_a_archived_task_is_ok() {\n        //given\n        havingResource(\"archivedElement\")\n        def archivedTask = mock(ArchivedFlowNodeInstance.class)\n        doReturn(FlowNodeType.USER_TASK).when(archivedTask).getType()\n        doReturn(currentUserId).when(archivedTask).getExecutedBy()\n        doReturn(archivedTask).when(processAPI).getArchivedFlowNodeInstance(458)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_get_on_a_archived_task_is_not_the_assignee() {\n        //given\n        havingResource(\"archivedElement\")\n        def archivedTask = mock(ArchivedFlowNodeInstance.class)\n        doReturn(FlowNodeType.USER_TASK).when(archivedTask).getType()\n        doReturn(58l).when(archivedTask).getExecutedBy()\n        doReturn(archivedTask).when(processAPI).getArchivedFlowNodeInstance(458)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_get_on_a_archived_task_is_not_found() {\n        //given\n        havingResource(\"archivedElement\")\n        doThrow(new ArchivedFlowNodeInstanceNotFoundException(458)).when(processAPI).getArchivedFlowNodeInstance(458)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n\n    @Test\n    public void should_get_on_a_assigned_human_task_is_ok() {\n        //given\n        havingResource(\"humanTask\")\n        def instance = mock(UserTaskInstance.class)\n        doReturn(currentUserId).when(instance).getAssigneeId()\n        doReturn(instance).when(processAPI).getFlowNodeInstance(458)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_get_on_a_assigned_human_task_is_not_ok() {\n        //given\n        havingResource(\"humanTask\")\n        def instance = mock(UserTaskInstance.class)\n        doReturn(59l).when(instance).getAssigneeId()\n        doReturn(instance).when(processAPI).getFlowNodeInstance(458)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_get_on_an_unexisting_flow_node() {\n        //given\n        havingResource(\"humanTask\")\n        doThrow(new FlowNodeInstanceNotFoundException(new Exception())).when(processAPI).getFlowNodeInstance(458)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n\n    @Test\n    public void should_get_on_a_pending_task_is_ok() {\n        //given\n        havingResource(\"humanTask\")\n        def instance = mock(UserTaskInstance.class)\n        doReturn(-1l).when(instance).getAssigneeId()\n        doReturn(instance).when(processAPI).getFlowNodeInstance(458)\n        doReturn(new SearchResultImpl(1, [])).when(processAPI).searchUsersWhoCanExecutePendingHumanTask(eq(458l), any(SearchOptions.class))\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_get_on_a_pending_task_is_not_ok() {\n        //given\n        havingResource(\"humanTask\")\n        def instance = mock(UserTaskInstance.class)\n        doReturn(-1l).when(instance).getAssigneeId()\n        doReturn(instance).when(processAPI).getFlowNodeInstance(458)\n        doReturn(new SearchResultImpl(0, [])).when(processAPI).searchUsersWhoCanExecutePendingHumanTask(eq(458l), any(SearchOptions.class))\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_PUT_on_assigned_human_task_is_ok() {\n        //given\n        doReturn(true).when(apiCallContext).isPUT()\n        doReturn(\"458\").when(apiCallContext).getResourceId()\n        doReturn(\"humanTask\").when(apiCallContext).getResourceName()\n        def instance = mock(UserTaskInstance.class)\n        doReturn(currentUserId).when(instance).getAssigneeId()\n        doReturn(instance).when(processAPI).getFlowNodeInstance(458)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_GET_an_archivedHumanTask_providing_a_parentTaskId() {\n        def archivedTask = mock(ArchivedFlowNodeInstance.class)\n        doReturn(FlowNodeType.MANUAL_TASK).when(archivedTask).getType()\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(null).when(apiCallContext).getResourceName()\n        havingFilters([parentTaskId: \"4\"])\n        doThrow(FlowNodeInstanceNotFoundException.class).when(processAPI).getFlowNodeInstance(4)\n        doReturn(archivedTask).when(processAPI).getArchivedFlowNodeInstance(4)\n        doReturn(currentUserId).when(archivedTask).getExecutedBy()\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_GET_humanTasks_providing_a_parentCaseId() {\n        //given\n        havingFilters([parentCaseId: \"4\"])\n        doReturn(\"humanTask\").when(apiCallContext).getResourceName()\n\n        def processInstance = mock(ProcessInstance.class)\n        doReturn(processInstance).when(processAPI).getProcessInstance(4l)\n        doReturn(10l).when(processInstance).getProcessDefinitionId()\n        doReturn(true).when(processAPI).isUserProcessSupervisor(10l, currentUserId)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_GET_humanTasks_providing_a_caseId_if_supervisor() {\n        //given\n        havingFilters([caseId: \"5\"])\n        doReturn(\"humanTask\").when(apiCallContext).getResourceName()\n\n        def processInstance = mock(ProcessInstance.class)\n        doReturn(processInstance).when(processAPI).getProcessInstance(5l)\n        doReturn(10l).when(processInstance).getProcessDefinitionId()\n        doReturn(true).when(processAPI).isUserProcessSupervisor(10l, currentUserId)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_PUT_on_a_human_task_if_supervisor() {\n        //given\n        doReturn(true).when(apiCallContext).isPUT()\n        doReturn(\"458\").when(apiCallContext).getResourceId()\n        doReturn(\"humanTask\").when(apiCallContext).getResourceName()\n        def instance = mock(UserTaskInstance.class)\n        doReturn(instance).when(processAPI).getFlowNodeInstance(458)\n        doReturn(10l).when(instance).getProcessDefinitionId()\n        doReturn(true).when(processAPI).isUserProcessSupervisor(10l, currentUserId)\n        doReturn('''{\"assigned_id\":\"4\"}''').when(apiCallContext).getBody()\n        def assigneUser = mock(User.class)\n        doReturn(assigneUser).when(identityAPI).getUser(4l)\n        doReturn(4l).when(assigneUser).getId()\n        doReturn(new SearchResultImpl(0, [])).when(processAPI).searchUsersWhoCanExecutePendingHumanTask(eq(458l), any(SearchOptions.class))\n        doReturn(4l).when(instance).getAssigneeId()\n        doReturn(false).when(processAPI).isUserProcessSupervisor(10l, 4l)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void should_not_PUT_on_a_human_task_if_supervisor_but_assigned_id_no_actor() {\n        //given\n        doReturn(true).when(apiCallContext).isPUT()\n        doReturn(\"458\").when(apiCallContext).getResourceId()\n        doReturn(\"humanTask\").when(apiCallContext).getResourceName()\n        def instance = mock(UserTaskInstance.class)\n        doReturn(instance).when(processAPI).getFlowNodeInstance(458)\n        doReturn(10l).when(instance).getProcessDefinitionId()\n        doReturn(true).when(processAPI).isUserProcessSupervisor(10l, currentUserId)\n        doReturn('''{\"assigned_id\":\"4\"}''').when(apiCallContext).getBody()\n        def assigneUser = mock(User.class)\n        doReturn(assigneUser).when(identityAPI).getUser(4l)\n        doReturn(4l).when(assigneUser).getId()\n        doReturn(new SearchResultImpl(0, [])).when(processAPI).searchUsersWhoCanExecutePendingHumanTask(eq(458l), any(SearchOptions.class))\n        doReturn(99l).when(instance).getAssigneeId()\n        doReturn(false).when(processAPI).isUserProcessSupervisor(10l, 4l)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void should_not_GET_humanTasks_providing_a_caseId_if_not_supervisor() {\n        //given\n        havingFilters([caseId: \"5\"])\n        doReturn(\"humanTask\").when(apiCallContext).getResourceName()\n\n        def processInstance = mock(ProcessInstance.class)\n        doReturn(processInstance).when(processAPI).getProcessInstance(5l)\n        doReturn(10l).when(processInstance).getProcessDefinitionId()\n\n        doReturn(false).when(processAPI).isUserProcessSupervisor(10l, currentUserId)\n        //when\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n        //then\n        assertThat(isAuthorized).isFalse()\n    }\n\n    def havingResource(String resourceName) {\n        doReturn(true).when(apiCallContext).isGET()\n        doReturn(\"458\").when(apiCallContext).getResourceId()\n        doReturn(resourceName).when(apiCallContext).getResourceName()\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/groovy/org/bonitasoft/permissions/UserPermissionRuleTest.groovy",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.permissions\n\nimport static org.assertj.core.api.Assertions.assertThat\nimport static org.mockito.Mockito.doReturn\n\nimport org.bonitasoft.engine.api.APIAccessor\nimport org.bonitasoft.engine.api.Logger\nimport org.bonitasoft.engine.api.permission.APICallContext\nimport org.bonitasoft.engine.api.permission.PermissionRule\nimport org.bonitasoft.engine.session.APISession\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.mockito.Mock\nimport org.mockito.junit.MockitoJUnitRunner\n\n@RunWith(MockitoJUnitRunner.class)\npublic class UserPermissionRuleTest {\n    @Mock\n    def APISession apiSession\n    @Mock\n    def APICallContext apiCallContext\n    @Mock\n    def APIAccessor apiAccessor\n    @Mock\n    def Logger logger\n    def PermissionRule rule = new UserPermissionRule()\n\n    @Test\n    public void checkOnResourceWithCurrentUser() throws Exception {\n        doReturn(15l).when(apiSession).getUserId()\n        doReturn(\"15\").when(apiCallContext).getResourceId()\n\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n\n        assertThat(isAuthorized).isTrue()\n    }\n\n    @Test\n    public void checkOnResourceWithOtherCurrentUser() throws Exception {\n        doReturn(15l).when(apiSession).getUserId()\n        doReturn(\"16\").when(apiCallContext).getResourceId()\n\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void checkOnResourceWithDeployOnPersonnalData() throws Exception {\n        doReturn(15l).when(apiSession).getUserId()\n        doReturn(\"16\").when(apiCallContext).getResourceId()\n        doReturn(\"?d=personnal_data&otherParam=2\").when(apiCallContext).getQueryString()\n\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void checkOnResourceWithDeployOnProfessionalData() throws Exception {\n        doReturn(15l).when(apiSession).getUserId()\n        doReturn(\"16\").when(apiCallContext).getResourceId()\n        doReturn(\"?d=professional_data&otherParam=2\").when(apiCallContext).getQueryString()\n\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n\n        assertThat(isAuthorized).isFalse()\n    }\n\n    @Test\n    public void checkOnResourceWithNoResource() throws Exception {\n        doReturn(15l).when(apiSession).getUserId()\n        doReturn(\"?otherParam=2\").when(apiCallContext).getQueryString()\n\n        def isAuthorized = rule.isAllowed(apiSession, apiCallContext, apiAccessor, logger)\n\n        assertThat(isAuthorized).isFalse()\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/EngineInitializerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.catchThrowable;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.Mockito.*;\n\nimport org.bonitasoft.engine.api.PlatformAPI;\nimport org.bonitasoft.engine.event.PlatformStartedEvent;\nimport org.bonitasoft.engine.platform.PlatformNotFoundException;\nimport org.bonitasoft.engine.platform.session.PlatformSessionService;\nimport org.bonitasoft.engine.platform.session.model.SPlatformSession;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.impl.ServiceAccessorFactory;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.SystemOutRule;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class EngineInitializerTest {\n\n    @Spy\n    @InjectMocks\n    private EngineInitializer engineInitializer;\n    @Mock\n    private PlatformAPI platformAPI;\n    @Mock\n    private SessionAccessor sessionAccessor;\n    @Mock\n    private ServiceAccessorFactory serviceAccessorFactory;\n    @Mock\n    private ServiceAccessor platformService;\n    @Mock\n    private PlatformSessionService platformSessionService;\n    @Mock\n    private SPlatformSession sPlatformSession;\n\n    @Rule\n    public final SystemOutRule systemOutRule = new SystemOutRule().enableLog().muteForSuccessfulTests();\n\n    @Before\n    public void before() throws Exception {\n        doReturn(platformAPI).when(engineInitializer).getPlatformAPI();\n        doReturn(sessionAccessor).when(engineInitializer).getSessionAccessor();\n        doReturn(serviceAccessorFactory).when(engineInitializer).getServiceAccessorFactory();\n        doReturn(platformService).when(engineInitializer).getPlatformService();\n        doReturn(platformSessionService).when(platformService).getPlatformSessionService();\n        doReturn(sPlatformSession).when(platformSessionService).createSession(anyString());\n    }\n\n    @Test\n    public void initializeEngine_should_start_node() throws Exception {\n        // given\n        doReturn(true).when(platformAPI).isPlatformCreated();\n        systemOutRule.clearLog();\n        final PlatformStartedEvent platformStartEvent = new PlatformStartedEvent();\n\n        // when\n        engineInitializer.initializeEngine();\n\n        //then\n        verify(platformAPI).startNode();\n        assertThat(systemOutRule.getLog()).contains(\"Initialization of Bonita Engine done!\");\n        verify(platformService).publishEvent(platformStartEvent);\n    }\n\n    @Test\n    public void should_not_start_node_when_platform_is_not_created() throws Exception {\n        //given\n        doReturn(false).when(platformAPI).isPlatformCreated();\n        //when\n        final Throwable throwable = catchThrowable(() -> engineInitializer.initializeEngine());\n        //then\n        assertThat(throwable).isInstanceOf(PlatformNotFoundException.class);\n        verify(platformAPI, never()).startNode();\n    }\n\n    @Test\n    public void testUnloadEngine() throws Exception {\n        doReturn(true).when(platformAPI).isNodeStarted();\n\n        engineInitializer.unloadEngine();\n\n        verify(platformAPI).stopNode();\n        verify(serviceAccessorFactory).destroyAccessors();\n    }\n\n    @Test\n    public void should_not_stop_node_if_node_is_not_started() throws Exception {\n        //given\n        doReturn(false).when(platformAPI).isNodeStarted();\n        //when\n        engineInitializer.unloadEngine();\n        //then\n        verify(platformAPI, never()).stopNode();\n    }\n\n    @Test\n    public void should_destroy_accessor_if_exception() throws Exception {\n        engineInitializer.unloadEngine();\n\n        verify(serviceAccessorFactory).destroyAccessors();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/APIAccessorImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.api.*;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class APIAccessorImplTest {\n\n    private APIAccessorImpl apiAccessor;\n\n    private void initAPIAccessor() {\n        apiAccessor = new APIAccessorImpl();\n    }\n\n    @Test\n    public void getIdentityAPI_should_return_the_default_implementation() {\n        initAPIAccessor();\n        final IdentityAPI identityAPI = apiAccessor.getIdentityAPI();\n\n        assertThat(identityAPI).isNotNull().isExactlyInstanceOf(IdentityAPIImpl.class);\n    }\n\n    @Test\n    public void getProcessAPI_should_return_the_default_implementation() {\n        initAPIAccessor();\n        final ProcessAPI processAPI = apiAccessor.getProcessAPI();\n\n        assertThat(processAPI).isNotNull().isExactlyInstanceOf(ProcessAPIImpl.class);\n    }\n\n    @Test\n    public void getCommandAPI_should_return_the_default_implementation() {\n        initAPIAccessor();\n        final CommandAPI commandAPI = apiAccessor.getCommandAPI();\n\n        assertThat(commandAPI).isNotNull().isExactlyInstanceOf(CommandAPIImpl.class);\n    }\n\n    @Test\n    public void getProfileAPI_should_return_the_default_implementation() {\n        initAPIAccessor();\n        final ProfileAPI profileAPI = apiAccessor.getProfileAPI();\n\n        assertThat(profileAPI).isNotNull().isExactlyInstanceOf(ProfileAPIImpl.class);\n    }\n\n    @Test\n    public void getPageAPI_should_return_the_default_implementation() throws SBonitaException {\n        initAPIAccessor();\n        final org.bonitasoft.engine.api.PageAPI pageAPI = apiAccessor.getCustomPageAPI();\n\n        assertThat(pageAPI).isNotNull().isExactlyInstanceOf(PageAPIImpl.class);\n    }\n\n    @Test\n    public void getApplicationAPI_should_return_the_default_implementation() throws SBonitaException {\n        initAPIAccessor();\n        final ApplicationAPI applicationAPI = apiAccessor.getLivingApplicationAPI();\n\n        assertThat(applicationAPI).isNotNull().isExactlyInstanceOf(ApplicationAPIImpl.class);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/BusinessDataAPIImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.when;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.data.DataNotFoundException;\nimport org.bonitasoft.engine.business.data.BusinessDataReference;\nimport org.bonitasoft.engine.business.data.MultipleBusinessDataReference;\nimport org.bonitasoft.engine.business.data.SimpleBusinessDataReference;\nimport org.bonitasoft.engine.core.process.instance.api.RefBusinessDataService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.exception.RetrieveException;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class BusinessDataAPIImplTest {\n\n    @Mock\n    private ServiceAccessor serviceAccessor;\n\n    @Mock\n    private RefBusinessDataService refBusinessDataService;\n\n    @Spy\n    private BusinessDataAPIImpl businessDataAPI;\n\n    @Before\n    public void setUp() {\n        doReturn(serviceAccessor).when(businessDataAPI).getServiceAccessor();\n        when(serviceAccessor.getRefBusinessDataService()).thenReturn(refBusinessDataService);\n    }\n\n    private SProcessSimpleRefBusinessDataInstance buildSimpleRefBusinessData(final String name,\n            final String dataClassName, final Long dataId) {\n        final SProcessSimpleRefBusinessDataInstance reference = new SProcessSimpleRefBusinessDataInstance();\n        reference.setId(64645L);\n        reference.setName(name);\n        reference.setDataClassName(dataClassName);\n        reference.setDataId(dataId);\n        return reference;\n    }\n\n    private SProcessMultiRefBusinessDataInstance buildMultiRefBusinessData(final String name,\n            final String dataClassName, final List<Long> dataIds) {\n        final SProcessMultiRefBusinessDataInstance reference = new SProcessMultiRefBusinessDataInstance();\n        reference.setId(64645L);\n        reference.setName(name);\n        reference.setDataClassName(dataClassName);\n        reference.setDataIds(dataIds);\n        return reference;\n    }\n\n    @Test\n    public void getProcessBusinessDataShouldReturnASimpleBusinessDataReference() throws Exception {\n        final String name = \"address\";\n        final String dataClassName = \"com.bonitasoft.Address\";\n        final Long dataId = 6873654654L;\n        final SProcessSimpleRefBusinessDataInstance reference = buildSimpleRefBusinessData(name, dataClassName, dataId);\n        when(refBusinessDataService.getRefBusinessDataInstance(name, 487654L)).thenReturn(reference);\n\n        final BusinessDataReference businessData = businessDataAPI.getProcessBusinessDataReference(name, 487654L);\n\n        assertThat(businessData).isInstanceOf(SimpleBusinessDataReference.class);\n        assertThat(businessData.getName()).isEqualTo(name);\n        assertThat(businessData.getType()).isEqualTo(dataClassName);\n        assertThat(((SimpleBusinessDataReference) businessData).getStorageId()).isEqualTo(dataId);\n    }\n\n    @Test\n    public void getProcessBusinessDataShouldReturnAMultipleBusinessDataReference() throws Exception {\n        final String name = \"myEmployees\";\n        final String dataClassName = \"com.bonitasoft.Employee\";\n        final List<Long> dataIds = Arrays.asList(8735468463748L, 87634386731L);\n        final SProcessMultiRefBusinessDataInstance reference = buildMultiRefBusinessData(name, dataClassName, dataIds);\n        when(refBusinessDataService.getRefBusinessDataInstance(name, 487654L)).thenReturn(reference);\n\n        final BusinessDataReference businessData = businessDataAPI.getProcessBusinessDataReference(name, 487654L);\n\n        assertThat(businessData).isInstanceOf(MultipleBusinessDataReference.class);\n        assertThat(businessData.getName()).isEqualTo(name);\n        assertThat(businessData.getType()).isEqualTo(dataClassName);\n        assertThat(((MultipleBusinessDataReference) businessData).getStorageIds()).isEqualTo(dataIds);\n    }\n\n    @Test(expected = RetrieveException.class)\n    public void getProcessBusinessDataShouldThrowARuntimeException() throws Exception {\n        final String name = \"myEmployees\";\n        when(refBusinessDataService.getRefBusinessDataInstance(name, 487654L))\n                .thenThrow(new SBonitaReadException(\"exception\"));\n\n        businessDataAPI.getProcessBusinessDataReference(name, 487654L);\n    }\n\n    @Test(expected = DataNotFoundException.class)\n    public void getProcessBusinessDataShouldThrowADataNotFoundException() throws Exception {\n        final String name = \"myEmployees\";\n        when(refBusinessDataService.getRefBusinessDataInstance(name, 487654L))\n                .thenThrow(new SRefBusinessDataInstanceNotFoundException(487654L, name));\n\n        businessDataAPI.getProcessBusinessDataReference(name, 487654L);\n    }\n\n    @Test\n    public void getProcessBusinessDataReferencesShouldReturnAListOfBusinessDataReferences() throws Exception {\n        final String name = \"address\";\n        final String dataClassName = \"com.bonitasoft.Address\";\n        final Long dataId = 6873654654L;\n        final List<Long> dataIds = Arrays.asList(8735468463748L, 87634386731L);\n        final SProcessSimpleRefBusinessDataInstance sReference1 = buildSimpleRefBusinessData(name, dataClassName,\n                dataId);\n        final SProcessMultiRefBusinessDataInstance sReference2 = buildMultiRefBusinessData(name, dataClassName,\n                dataIds);\n        when(refBusinessDataService.getRefBusinessDataInstances(487654L, 0, 10))\n                .thenReturn(Arrays.asList(sReference1, sReference2));\n\n        final List<BusinessDataReference> references = businessDataAPI.getProcessBusinessDataReferences(487654L, 0, 10);\n\n        assertThat(references).hasSize(2);\n        final BusinessDataReference reference1 = references.get(0);\n        assertThat(reference1).isInstanceOf(SimpleBusinessDataReference.class);\n        assertThat(reference1.getName()).isEqualTo(name);\n        assertThat(reference1.getType()).isEqualTo(dataClassName);\n        assertThat(((SimpleBusinessDataReference) reference1).getStorageId()).isEqualTo(dataId);\n        final BusinessDataReference reference2 = references.get(1);\n        assertThat(reference2).isInstanceOf(MultipleBusinessDataReference.class);\n        assertThat(reference2.getName()).isEqualTo(name);\n        assertThat(reference2.getType()).isEqualTo(dataClassName);\n        assertThat(((MultipleBusinessDataReference) reference2).getStorageIds()).isEqualTo(dataIds);\n    }\n\n    @Test(expected = RetrieveException.class)\n    public void getProcessBusinessDataReferencesShouldThrowAnException() throws Exception {\n        when(refBusinessDataService.getRefBusinessDataInstances(487654L, 0, 10))\n                .thenThrow(new SBonitaReadException(\"exception\"));\n\n        businessDataAPI.getProcessBusinessDataReferences(487654L, 0, 10);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/CustomUserInfoAPIDelegateTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.doReturn;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.identity.CustomUserInfo;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoValue;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Vincent Elcrin\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class CustomUserInfoAPIDelegateTest {\n\n    private CustomUserInfoAPIDelegate api;\n\n    @Mock\n    private IdentityService service;\n\n    @Before\n    public void setUp() {\n        api = new CustomUserInfoAPIDelegate(service);\n    }\n\n    @Test\n    public void list_should_retrieve_CustomUserItems_for_a_given_user() throws Exception {\n        given(service.getCustomUserInfoDefinitions(0, 2)).willReturn(\n                Arrays.asList(\n                        SCustomUserInfoDefinition.builder().id(1).build(),\n                        SCustomUserInfoDefinition.builder().id(2).build()));\n\n        List<CustomUserInfo> result = api.list(1L, 0, 2);\n\n        assertThat(result.get(0).getDefinition().getId()).isEqualTo(1L);\n        assertThat(result.get(1).getDefinition().getId()).isEqualTo(2L);\n    }\n\n    @Test\n    public void list_should_return_an_empty_when_there_is_no_definitions() throws Exception {\n        given(service.getCustomUserInfoDefinitions(0, 2))\n                .willReturn(Collections.<SCustomUserInfoDefinition> emptyList());\n\n        List<CustomUserInfo> result = api.list(1L, 0, 2);\n\n        assertThat(result).isEmpty();\n    }\n\n    @Test\n    public void list_should_retrieve_values_associated_to_definitions_for_a_given_user() throws Exception {\n        List<SCustomUserInfoDefinition> list1 = Arrays.asList(\n                SCustomUserInfoDefinition.builder().id(1).name(\"definition 1\").build(),\n                SCustomUserInfoDefinition.builder().id(2).name(\"definition 2\").build());\n        List<SCustomUserInfoValue> list2 = Arrays.asList(\n                SCustomUserInfoValue.builder().definitionId(1).value(\"value 1\").build(),\n                SCustomUserInfoValue.builder().definitionId(2).value(\"value 2\").build());\n        doReturn(list1).when(service).getCustomUserInfoDefinitions(0, 2);\n        doReturn(list2).when(service).getCustomUserInfoValueOfUserAndDefinitions(eq(1L), any());\n\n        List<CustomUserInfo> result = api.list(1L, 0, 2);\n\n        assertThat(result.get(0).getValue()).isEqualTo(\"value 1\");\n        assertThat(result.get(1).getValue()).isEqualTo(\"value 2\");\n    }\n\n    @Test\n    public void list_should_return_a_null_value_for_a_not_found_definition_matching_value() throws Exception {\n        given(service.getCustomUserInfoDefinitions(0, 2)).willReturn(Collections.singletonList(\n                SCustomUserInfoDefinition.builder().id(1).name(\"definition\").build()));\n\n        List<CustomUserInfo> result = api.list(2L, 0, 2);\n\n        assertThat(result.get(0).getValue()).isEqualTo(null);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/CustomUserInfoDefinitionAPIDelegateTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.fail;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.verify;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.identity.CustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.CustomUserInfoDefinitionCreator;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.SCustomUserInfoDefinitionAlreadyExistsException;\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Vincent Elcrin\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class CustomUserInfoDefinitionAPIDelegateTest {\n\n    @Mock\n    private IdentityService service;\n\n    private CustomUserInfoDefinitionAPIDelegate api;\n\n    @Before\n    public void setUp() {\n        api = new CustomUserInfoDefinitionAPIDelegate(service);\n    }\n\n    @Test\n    public void create_should_call_service_to_retrieve_the_item_and_return_result_as_a_CustomUserDefinition()\n            throws Exception {\n        given(service.createCustomUserInfoDefinition(any(SCustomUserInfoDefinition.class)))\n                .willReturn(SCustomUserInfoDefinition.builder().id(1).build());\n\n        CustomUserInfoDefinition definition = api.create(new CustomUserInfoDefinitionCreator(\"skill\"));\n\n        assertThat(definition.getId()).isEqualTo(1L);\n    }\n\n    @Test\n    public void create_should_throws_AlreadyExistException_when_service_throws_SCustomUserInfoDefinitionAlreadyExistsException()\n            throws Exception {\n        // given\n        String name = \"skill\";\n        SCustomUserInfoDefinitionAlreadyExistsException serverException = new SCustomUserInfoDefinitionAlreadyExistsException(\n                name);\n        given(service.createCustomUserInfoDefinition(any(SCustomUserInfoDefinition.class))).willThrow(serverException);\n\n        try {\n            // when\n            api.create(new CustomUserInfoDefinitionCreator(name));\n            fail(\"Expected AlreadyExistsException\");\n        } catch (AlreadyExistsException e) {\n            // then\n            assertThat(e.getMessage())\n                    .isEqualTo(\"A custom user info definition already exists with name '\" + name + \"'\");\n        }\n    }\n\n    @Test(expected = CreationException.class)\n    public void create_should_throws_CreationException_if_name_is_null() throws Exception {\n\n        api.create(new CustomUserInfoDefinitionCreator(null));\n    }\n\n    @Test\n    public void create_should_throws_CreationException_if_name_is_empty() {\n        // given\n        String name = \"\";\n\n        try {\n            // when\n            api.create(new CustomUserInfoDefinitionCreator(name));\n            fail(\"Expected CreationException\");\n        } catch (CreationException e) {\n            // then\n            assertThat(e.getMessage()).isEqualTo(\"The definition name cannot be null or empty.\");\n        }\n    }\n\n    @Test\n    public void create_should_throws_CreationException_if_name_is_longer_then_75() {\n        // given\n        String name = \"123456789:123456789:123456789:123456789:123456789:123456789:123456789:123456\";\n        try {\n            // when\n            api.create(new CustomUserInfoDefinitionCreator(name));\n            fail(\"Expected CreationException\");\n        } catch (CreationException e) {\n            // then\n            assertThat(e.getMessage()).isEqualTo(\"The definition name cannot be longer then 75 characters.\");\n        }\n    }\n\n    @Test\n    public void list_call_service_to_retrieve_items_and_return_result_as_a_list_of_CustomUserDefinition()\n            throws Exception {\n        given(service.getCustomUserInfoDefinitions(0, 3)).willReturn(\n                Arrays.asList(\n                        SCustomUserInfoDefinition.builder().id(1).build(),\n                        SCustomUserInfoDefinition.builder().id(2).build(),\n                        SCustomUserInfoDefinition.builder().id(3).build()));\n\n        List<CustomUserInfoDefinition> definitions = api.list(0, 3);\n\n        assertThat(definitions.get(0).getId()).isEqualTo(1L);\n        assertThat(definitions.get(1).getId()).isEqualTo(2L);\n        assertThat(definitions.get(2).getId()).isEqualTo(3L);\n    }\n\n    @Test\n    public void list_call_service_to_retrieve_items_and_return_empty_list_when_service_returns_empty_list()\n            throws Exception {\n        given(service.getCustomUserInfoDefinitions(0, 3)).willReturn(\n                Collections.<SCustomUserInfoDefinition> emptyList());\n\n        List<CustomUserInfoDefinition> definitions = api.list(0, 3);\n\n        assertThat(definitions).isEmpty();\n    }\n\n    @Test\n    public void delete_should_call_server_to_delete_the_item() throws Exception {\n        api.delete(1L);\n\n        verify(service, atLeastOnce()).deleteCustomUserInfoDefinition(1L);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/IdentityAPIImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.doReturn;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.exception.RetrieveException;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.SIdentityException;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class IdentityAPIImplTest {\n\n    @Mock\n    private ServiceAccessor serviceAccessor;\n\n    @Mock\n    private IdentityService identityService;\n\n    @Spy\n    private IdentityAPIImpl identityAPI;\n\n    @Before\n    public void setUp() {\n        doReturn(serviceAccessor).when(identityAPI).getServiceAccessor();\n        given(serviceAccessor.getIdentityService()).willReturn(identityService);\n    }\n\n    @Test\n    public void getUserIdsWithCustomUserInfo_returns_the_value_returned_by_service() throws Exception {\n        //given\n        given(identityService.getUserIdsWithCustomUserInfo(\"skills\", \"Java\", false, 0, 10))\n                .willReturn(Arrays.asList(25L, 40L));\n\n        //when\n        final List<Long> userIds = identityAPI.getUserIdsWithCustomUserInfo(\"skills\", \"Java\", false, 0, 10);\n\n        //then\n        assertThat(userIds).containsExactly(25L, 40L);\n    }\n\n    @Test(expected = RetrieveException.class)\n    //then\n    public void getUserIdsWithCustomUserInfo_throws_RetriveException_when_service_throws_SBonitaException()\n            throws Exception {\n        //given\n        given(identityService.getUserIdsWithCustomUserInfo(anyString(), anyString(), anyBoolean(), anyInt(), anyInt()))\n                .willThrow(new SIdentityException(\"\"));\n\n        //when\n        identityAPI.getUserIdsWithCustomUserInfo(\"skills\", \"Java\", true, 0, 10);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/MaintenanceAPIImplTest.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.*;\n\nimport org.bonitasoft.engine.maintenance.MaintenanceDetails;\nimport org.bonitasoft.engine.platform.PlatformService;\nimport org.bonitasoft.engine.platform.model.SPlatform;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.tenant.TenantStateManager;\nimport org.bonitasoft.engine.transaction.TransactionService;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class MaintenanceAPIImplTest {\n\n    public static final long TENANT_ID = 56423L;\n\n    @Mock\n    private ServiceAccessor serviceAccessor;\n    @Mock\n    private PlatformService platformService;\n    @Mock\n    private TenantStateManager tenantStateManager;\n    @Mock\n    private TransactionService transactionService;\n    @Spy\n    @InjectMocks\n    private MaintenanceAPIImpl maintenanceAPI;\n\n    @Before\n    public void setup() throws Exception {\n        doReturn(platformService).when(serviceAccessor).getPlatformService();\n        doReturn(tenantStateManager).when(serviceAccessor).getTenantStateManager();\n        doReturn(transactionService).when(serviceAccessor).getTransactionService();\n        doReturn(serviceAccessor).when(maintenanceAPI).getServiceAccessor();\n    }\n\n    @Test\n    public void get_maintenance_info_should_retrieve_from_platform_service() throws Exception {\n        //given\n        SPlatform platform = SPlatform.builder()\n                .maintenanceMessage(\"maintenance msg\")\n                .maintenanceMessageActive(true)\n                .maintenanceEnabled(true)\n                .build();\n\n        doReturn(platform).when(platformService).getPlatform();\n        //when\n        MaintenanceDetails info = maintenanceAPI.getMaintenanceDetails();\n        //then\n        assertThat(info.getMaintenanceState()).isEqualTo(MaintenanceDetails.State.ENABLED);\n        assertThat(info.getMaintenanceMessage()).isEqualTo(platform.getMaintenanceMessage());\n        assertThat(info.isMaintenanceMessageActive()).isEqualTo(platform.isMaintenanceMessageActive());\n    }\n\n    @Test\n    public void enable_maintenance_mode_should_update_state_manager() throws Exception {\n        //given\n        doNothing().when(tenantStateManager).pause();\n        //when\n        maintenanceAPI.enableMaintenanceMode();\n        //then\n        verify(tenantStateManager).pause();\n    }\n\n    @Test\n    public void disable_maintenance_mode_should_update_state_manager() throws Exception {\n        //given\n        doNothing().when(tenantStateManager).resume();\n        //when\n        maintenanceAPI.disableMaintenanceMode();\n        //then\n        verify(tenantStateManager).resume();\n    }\n\n    @Test\n    public void update_maintenance_msg_should_pass_message_to_platform_service() throws Exception {\n        //given\n        doNothing().when(platformService).updatePlatform(any());\n        //when\n        String msg = \"maintenance msg\";\n        maintenanceAPI.updateMaintenanceMessage(msg);\n        //then\n        ArgumentCaptor<EntityUpdateDescriptor> captor = ArgumentCaptor.forClass(EntityUpdateDescriptor.class);;\n        verify(platformService).updatePlatform(captor.capture());\n        assertThat(captor.getValue().getFields()).containsValue(msg);\n    }\n\n    @Test\n    public void enable_maintenance_mode_msg_should_update_platform_service() throws Exception {\n        //given\n        doNothing().when(platformService).updatePlatform(any());\n        //when\n        maintenanceAPI.enableMaintenanceMessage();\n        //then\n        ArgumentCaptor<EntityUpdateDescriptor> captor = ArgumentCaptor.forClass(EntityUpdateDescriptor.class);;\n        verify(platformService).updatePlatform(captor.capture());\n        assertThat(captor.getValue().getFields()).containsValue(true);\n    }\n\n    @Test\n    public void disable_maintenance_mode_msg_should_update_platform_service() throws Exception {\n        //given\n        doNothing().when(platformService).updatePlatform(any());\n        //when\n        maintenanceAPI.disableMaintenanceMessage();\n        //then\n        ArgumentCaptor<EntityUpdateDescriptor> captor = ArgumentCaptor.forClass(EntityUpdateDescriptor.class);;\n        verify(platformService).updatePlatform(captor.capture());\n        assertThat(captor.getValue().getFields()).containsValue(false);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/OrganizationAPIImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\nimport java.util.Collections;\n\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.core.process.comment.api.SCommentService;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition;\nimport org.bonitasoft.engine.profile.ProfileService;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.supervisor.mapping.SupervisorMappingService;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class OrganizationAPIImplTest {\n\n    private static final int PAGE_SIZE = 1;\n\n    private static final long CUSTOM_USER_INFO_DEF_ID1 = 11;\n\n    private static final long CUSTOM_USER_INFO_DEF_ID2 = 12;\n\n    @Mock\n    private ServiceAccessor serviceAccessor;\n\n    @Mock\n    private ProcessInstanceService processInstanceService;\n\n    @Mock\n    private SCommentService commentService;\n\n    @Mock\n    private ActivityInstanceService activityInstanceService;\n\n    @Mock\n    private IdentityService identityService;\n\n    @Mock\n    private ActorMappingService actorMappingService;\n\n    @Mock\n    private ProfileService profileService;\n\n    @Mock\n    private SupervisorMappingService supervisorService;\n\n    @Mock\n    private ProcessDefinitionService processDefinitionService;\n\n    @Mock\n    private SCustomUserInfoDefinition userInfoDef1;\n\n    @Mock\n    private SCustomUserInfoDefinition userInfoDef2;\n\n    private OrganizationAPIImpl organizationAPIImpl;\n\n    @Before\n    public void setUp() {\n        organizationAPIImpl = new OrganizationAPIImpl(serviceAccessor, PAGE_SIZE);\n\n        given(serviceAccessor.getProcessInstanceService()).willReturn(processInstanceService);\n        given(serviceAccessor.getCommentService()).willReturn(commentService);\n        given(serviceAccessor.getActivityInstanceService()).willReturn(activityInstanceService);\n        given(serviceAccessor.getIdentityService()).willReturn(identityService);\n        given(serviceAccessor.getActorMappingService()).willReturn(actorMappingService);\n        given(serviceAccessor.getProfileService()).willReturn(profileService);\n        given(serviceAccessor.getSupervisorService()).willReturn(supervisorService);\n        given(serviceAccessor.getProcessDefinitionService()).willReturn(processDefinitionService);\n\n        given(userInfoDef1.getId()).willReturn(CUSTOM_USER_INFO_DEF_ID1);\n        given(userInfoDef2.getId()).willReturn(CUSTOM_USER_INFO_DEF_ID2);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Test\n    public void deleteOrganization_call_services_to_delete_all_organizationInfo() throws Exception {\n        // given\n        given(identityService.getCustomUserInfoDefinitions(0, PAGE_SIZE)).willReturn(\n                Collections.singletonList(userInfoDef1),\n                Collections.singletonList(userInfoDef2), Collections.emptyList());\n\n        // when\n        organizationAPIImpl.deleteOrganization();\n\n        // then\n        verify(identityService, times(1)).deleteCustomUserInfoDefinition(CUSTOM_USER_INFO_DEF_ID1);\n        verify(identityService, times(1)).deleteCustomUserInfoDefinition(CUSTOM_USER_INFO_DEF_ID2);\n        verify(actorMappingService, times(1)).deleteAllActorMembers();\n        verify(profileService, times(1)).deleteAllProfileMembers();\n        verify(activityInstanceService, times(1)).deleteAllPendingMappings();\n        verify(supervisorService, times(1)).deleteAllProcessSupervisors();\n        verify(identityService, times(1)).deleteAllUserMemberships();\n        verify(identityService, times(1)).deleteAllGroups();\n        verify(identityService, times(1)).deleteAllRoles();\n        verify(identityService, times(1)).deleteAllUsers();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/PageAPIImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport static org.mockito.Mockito.*;\n\nimport org.bonitasoft.engine.exception.UnauthorizedAccessException;\nimport org.bonitasoft.engine.page.PageMappingService;\nimport org.bonitasoft.engine.page.SAuthorizationException;\nimport org.bonitasoft.engine.page.SPageMapping;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Baptiste Mesta\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class PageAPIImplTest {\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n    @Mock\n    PageMappingService pageMappingService;\n\n    @Test\n    public void resolvePageOrURLShouldThrowUnauthorizedAccessExceptionIfSAuthorizRaised() throws Exception {\n        final String pageKey = \"pageKey\";\n        final SPageMapping pageMapping = mock(SPageMapping.class);\n        doReturn(pageMapping).when(pageMappingService).get(pageKey);\n        doThrow(SAuthorizationException.class).when(pageMappingService).resolvePageURL(pageMapping, null, true);\n\n        final PageAPIImpl processConfigurationAPI = spy(new PageAPIImpl());\n        doReturn(pageMappingService).when(processConfigurationAPI).retrievePageMappingService();\n\n        expectedException.expect(UnauthorizedAccessException.class);\n\n        processConfigurationAPI.resolvePageOrURL(pageKey, null, true);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/PermissionAPIImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport static org.junit.Assert.assertThrows;\nimport static org.mockito.Mockito.*;\n\nimport org.bonitasoft.engine.api.permission.APICallContext;\nimport org.bonitasoft.engine.authorization.PermissionService;\nimport org.bonitasoft.engine.commons.exceptions.SExecutionException;\nimport org.bonitasoft.engine.exception.ExecutionException;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class PermissionAPIImplTest {\n\n    @Mock\n    private ServiceAccessor serviceAccessor;\n    @Mock\n    private PermissionService permissionService;\n    private PermissionAPIImpl permissionAPI;\n    private APICallContext apiCallContext;\n\n    @Before\n    public void before() {\n        permissionAPI = spy(new PermissionAPIImpl());\n        doReturn(serviceAccessor).when(permissionAPI).getServiceAccessor();\n        doReturn(permissionService).when(serviceAccessor).getPermissionService();\n        apiCallContext = new APICallContext(\"GET\", \"identity\", \"user\", \"1\", \"query\", \"{\\\"body\\\":\\\"value\\\"}\");\n    }\n\n    @Test\n    public void should_isAuthorized_throw_execution_exception() throws Exception {\n        //given\n        final APICallContext apiCallContext = new APICallContext(\"GET\", \"bpm\", \"case\", null, \"\", \"\");\n        doThrow(SExecutionException.class).when(permissionService).isAuthorized(apiCallContext);\n\n        assertThrows(ExecutionException.class,\n                () -> permissionAPI.isAuthorized(apiCallContext));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/PlatformAPIImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.*;\n\nimport java.io.IOException;\n\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.home.BonitaHomeServer;\nimport org.bonitasoft.engine.platform.PlatformManager;\nimport org.bonitasoft.engine.platform.StartNodeException;\nimport org.bonitasoft.engine.scheduler.SchedulerService;\nimport org.bonitasoft.engine.scheduler.exception.SSchedulerException;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class PlatformAPIImplTest {\n\n    public static final long TENANT_ID = 56423L;\n    @Mock\n    private ServiceAccessor serviceAccessor;\n    @Mock\n    private SchedulerService schedulerService;\n    @Mock\n    private BonitaHomeServer bonitaHomeServer;\n    @Mock\n    private PlatformManager platformManager;\n    @Spy\n    @InjectMocks\n    private PlatformAPIImpl platformAPI;\n\n    @Before\n    public void setup() throws Exception {\n        doReturn(schedulerService).when(serviceAccessor).getSchedulerService();\n        doReturn(platformManager).when(serviceAccessor).getPlatformManager();\n        doReturn(serviceAccessor).when(platformAPI).getServiceAccessor();\n        doReturn(bonitaHomeServer).when(platformAPI).getBonitaHomeServer();\n    }\n\n    @Test\n    public void rescheduleErroneousTriggers_should_call_rescheduleErroneousTriggers() throws Exception {\n        platformAPI.rescheduleErroneousTriggers();\n\n        verify(schedulerService).rescheduleErroneousTriggers();\n    }\n\n    @Test(expected = UpdateException.class)\n    public void rescheduleErroneousTriggers_should_throw_exception_when_rescheduleErroneousTriggers_failed()\n            throws Exception {\n        doThrow(new SSchedulerException(\"failed\")).when(schedulerService).rescheduleErroneousTriggers();\n\n        platformAPI.rescheduleErroneousTriggers();\n    }\n\n    @Test(expected = UpdateException.class)\n    public void rescheduleErroneousTriggers_should_throw_exception_when_cant_getPlatformAccessor() throws Exception {\n        doThrow(new IOException()).when(platformAPI).getServiceAccessor();\n\n        platformAPI.rescheduleErroneousTriggers();\n    }\n\n    @Test\n    public void should_updateTenantPortalConfigurationFile_call_bonitaHomeServer() throws Exception {\n        //when\n        platformAPI.updateClientTenantConfigurationFile(\"myProps.properties\", \"updated content\".getBytes());\n        //then\n        verify(bonitaHomeServer).updateTenantPortalConfigurationFile(\"myProps.properties\",\n                \"updated content\".getBytes());\n    }\n\n    @Test\n    public void should_getTenantPortalConfigurationFile_call_bonitaHomeServer() {\n        //given\n        final String configurationFile = \"a file\";\n        doReturn(\"content\".getBytes()).when(bonitaHomeServer).getTenantPortalConfiguration(\n                configurationFile);\n\n        //when\n        final byte[] configuration = platformAPI.getClientTenantConfiguration(configurationFile);\n\n        //then\n        assertThat(configuration).as(\"should return file content\").isEqualTo(\"content\".getBytes());\n        verify(bonitaHomeServer).getTenantPortalConfiguration(configurationFile);\n    }\n\n    @Test(expected = StartNodeException.class)\n    public void startNode_should_throw_exception_when_platform_is_not_in_state_stopped() throws Exception {\n        doReturn(false).when(platformManager).start();\n\n        platformAPI.startNode();\n    }\n\n    @Test\n    public void startNode_should_call_start_on_platformManager() throws Exception {\n        doReturn(true).when(platformManager).start();\n\n        platformAPI.startNode();\n\n        verify(platformManager).start();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/ProcessAPIImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport static java.util.Arrays.asList;\nimport static java.util.Collections.singletonList;\nimport static java.util.Collections.singletonMap;\nimport static org.assertj.core.api.Assertions.*;\nimport static org.mockito.ArgumentMatchers.argThat;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.*;\n\nimport java.io.FileNotFoundException;\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.concurrent.Callable;\n\nimport javax.transaction.Synchronization;\n\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.actor.mapping.SActorNotFoundException;\nimport org.bonitasoft.engine.actor.mapping.model.SActor;\nimport org.bonitasoft.engine.api.DocumentAPI;\nimport org.bonitasoft.engine.api.impl.transaction.identity.GetSUser;\nimport org.bonitasoft.engine.bar.BusinessArchiveService;\nimport org.bonitasoft.engine.bpm.connector.ConnectorCriterion;\nimport org.bonitasoft.engine.bpm.connector.ConnectorImplementationDescriptor;\nimport org.bonitasoft.engine.bpm.contract.ContractDefinition;\nimport org.bonitasoft.engine.bpm.data.DataInstance;\nimport org.bonitasoft.engine.bpm.data.impl.IntegerDataInstanceImpl;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.TimerEventTriggerInstanceNotFoundException;\nimport org.bonitasoft.engine.bpm.flownode.UserTaskNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstance;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ProcessResourceNotFoundException;\nimport org.bonitasoft.engine.bpm.process.impl.internal.ProcessInstanceImpl;\nimport org.bonitasoft.engine.bpm.userfilter.impl.UserFilterDefinitionImpl;\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SDeletionException;\nimport org.bonitasoft.engine.core.connector.ConnectorResult;\nimport org.bonitasoft.engine.core.connector.ConnectorService;\nimport org.bonitasoft.engine.core.connector.exception.SConnectorException;\nimport org.bonitasoft.engine.core.connector.parser.SConnectorImplementationDescriptor;\nimport org.bonitasoft.engine.core.contract.data.ContractDataService;\nimport org.bonitasoft.engine.core.contract.data.SContractDataNotFoundException;\nimport org.bonitasoft.engine.core.data.instance.TransientDataService;\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.filter.UserFilterService;\nimport org.bonitasoft.engine.core.filter.impl.FilterResultImpl;\nimport org.bonitasoft.engine.core.operation.OperationService;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SContextEntryImpl;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SFlowElementContainerDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SProcessDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SUserFilterDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SUserTaskDefinitionImpl;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceModificationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageModificationException;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowElementsContainerType;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstanceStateCounter;\nimport org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.core.process.instance.model.STaskPriority;\nimport org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceService;\nimport org.bonitasoft.engine.data.instance.api.ParentContainerResolver;\nimport org.bonitasoft.engine.data.instance.exception.SDataInstanceException;\nimport org.bonitasoft.engine.data.instance.exception.SDataInstanceReadException;\nimport org.bonitasoft.engine.data.instance.model.SBlobDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\nimport org.bonitasoft.engine.dependency.model.ScopeType;\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\nimport org.bonitasoft.engine.exception.ContractDataNotFoundException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.ExceptionContext;\nimport org.bonitasoft.engine.exception.ExecutionException;\nimport org.bonitasoft.engine.exception.RetrieveException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.execution.FlowNodeExecutor;\nimport org.bonitasoft.engine.execution.ProcessInstanceInterruptor;\nimport org.bonitasoft.engine.execution.archive.BPMArchiverService;\nimport org.bonitasoft.engine.execution.state.FlowNodeStateManager;\nimport org.bonitasoft.engine.execution.work.BPMWorkFactory;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.expression.ExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.model.impl.SExpressionImpl;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.lock.BonitaLock;\nimport org.bonitasoft.engine.lock.LockService;\nimport org.bonitasoft.engine.message.MessagesHandlingService;\nimport org.bonitasoft.engine.operation.LeftOperand;\nimport org.bonitasoft.engine.operation.LeftOperandBuilder;\nimport org.bonitasoft.engine.operation.Operation;\nimport org.bonitasoft.engine.operation.OperationBuilder;\nimport org.bonitasoft.engine.operation.OperatorType;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.OrderAndField;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.resources.BARResourceType;\nimport org.bonitasoft.engine.resources.ProcessResourcesService;\nimport org.bonitasoft.engine.resources.SBARResource;\nimport org.bonitasoft.engine.scheduler.SchedulerService;\nimport org.bonitasoft.engine.scheduler.exception.SSchedulerException;\nimport org.bonitasoft.engine.scheduler.model.SJobParameter;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor;\nimport org.bonitasoft.engine.search.descriptor.SearchHumanTaskInstanceDescriptor;\nimport org.bonitasoft.engine.search.descriptor.SearchMessageInstanceDescriptor;\nimport org.bonitasoft.engine.search.impl.SearchOptionsImpl;\nimport org.bonitasoft.engine.search.process.SearchFailedProcessInstancesSupervisedBy;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.session.model.SSession;\nimport org.bonitasoft.engine.transaction.STransactionCommitException;\nimport org.bonitasoft.engine.transaction.STransactionNotFoundException;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.bonitasoft.engine.work.WorkDescriptor;\nimport org.bonitasoft.engine.work.WorkService;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ProcessAPIImplTest {\n\n    private static final ConnectorCriterion CONNECTOR_CRITERION_DEFINITION_ID_ASC = ConnectorCriterion.DEFINITION_ID_ASC;\n\n    private static final int MAX_RESULT = 10;\n    private static final int START_INDEX = 0;\n    private static final long ACTOR_ID = 100;\n    private static final long PROCESS_DEFINITION_ID = 110;\n    private static final long PROCESS_INSTANCE_ID = 45;\n    private static final long ARCHIVED_PROCESS_INSTANCE_ID = 45;\n    private static final long FLOW_NODE_INSTANCE_ID = 1674;\n    private static final long ARCHIVED_FLOW_NODE_INSTANCE_ID = 1674;\n    private static final long FLOW_NODE_DEFINITION_ID = 1664;\n    private static final String ACTOR_NAME = \"employee\";\n    @Rule\n    public ExpectedException expectedEx = ExpectedException.none();\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n    @Mock\n    private ProcessManagementAPIImplDelegate managementAPIImplDelegate;\n    @Mock\n    private ServiceAccessor serviceAccessor;\n    @Mock\n    private TransientDataService transientDataService;\n    @Mock\n    private OperationService operationService;\n    @Mock\n    private ActivityInstanceService activityInstanceService;\n    @Mock\n    private DataInstanceService dataInstanceService;\n    @Mock\n    private ParentContainerResolver parentContainerResolver;\n    @Mock\n    private ProcessDefinitionService processDefinitionService;\n    @Mock\n    private ProcessInstanceService processInstanceService;\n    @Mock\n    private ClassLoaderService classLoaderService;\n    @Mock\n    private ActorMappingService actorMappingService;\n    @Mock\n    private SchedulerService schedulerService;\n    @Mock\n    private SearchEntitiesDescriptor searchEntitiesDescriptor;\n\n    @Mock\n    private EventInstanceService eventInstanceService;\n    @Mock\n    private FlowNodeStateManager flowNodeStateManager;\n    @Mock\n    private DocumentAPI documentAPI;\n    @Mock\n    private ConnectorService connectorService;\n    @Mock\n    private ContractDataService contractDataService;\n    @Mock\n    private MessagesHandlingService messageHandlingService;\n    @Mock\n    private ExpressionResolverService expressionResolverService;\n    @Mock\n    private FlowNodeExecutor flowNodeExecutor;\n    @Mock\n    private WorkService workService;\n    @Mock\n    private BPMWorkFactory workFactory;\n    @Mock\n    private BusinessArchiveService businessArchiveService;\n    @Mock\n    private UserFilterService userFilterService;\n    @Mock\n    private BPMArchiverService bpmArchiverService;\n    @Mock\n    private ProcessResourcesService processResourcesService;\n    private SProcessDefinitionImpl processDefinition;\n    private SUserTaskDefinitionImpl userTaskDefinition;\n    @Captor\n    private ArgumentCaptor<List<String>> argument;\n    @Captor\n    private ArgumentCaptor<SPendingActivityMapping> pendingMappingArgumentCaptor;\n    @Captor\n    private ArgumentCaptor<QueryOptions> deleteOldMessageArgumentCaptor;\n\n    @Spy\n    @InjectMocks\n    private ProcessAPIImpl processAPI;\n    private SUserTaskInstance sUserTaskInstance;\n\n    private TestUserTransactionService userTransactionService;\n\n    @Before\n    public void setup() throws Exception {\n        doReturn(serviceAccessor).when(processAPI).getServiceAccessor();\n        when(serviceAccessor.getDataInstanceService()).thenReturn(dataInstanceService);\n        when(serviceAccessor.getOperationService()).thenReturn(operationService);\n        when(serviceAccessor.getActorMappingService()).thenReturn(actorMappingService);\n        when(serviceAccessor.getConnectorService()).thenReturn(connectorService);\n        when(serviceAccessor.getSchedulerService()).thenReturn(schedulerService);\n        when(serviceAccessor.getSearchEntitiesDescriptor()).thenReturn(searchEntitiesDescriptor);\n        when(serviceAccessor.getEventInstanceService()).thenReturn(eventInstanceService);\n        when(serviceAccessor.getFlowNodeStateManager()).thenReturn(flowNodeStateManager);\n        when(serviceAccessor.getParentContainerResolver()).thenReturn(parentContainerResolver);\n        when(serviceAccessor.getContractDataService()).thenReturn(contractDataService);\n        when(serviceAccessor.getTransientDataService()).thenReturn(transientDataService);\n        when(serviceAccessor.getExpressionResolverService()).thenReturn(expressionResolverService);\n        when(serviceAccessor.getActivityInstanceService()).thenReturn(activityInstanceService);\n        when(serviceAccessor.getClassLoaderService()).thenReturn(classLoaderService);\n        when(serviceAccessor.getProcessDefinitionService()).thenReturn(processDefinitionService);\n        when(serviceAccessor.getProcessInstanceService()).thenReturn(processInstanceService);\n        when(serviceAccessor.getWorkService()).thenReturn(workService);\n        when(serviceAccessor.getBPMWorkFactory()).thenReturn(workFactory);\n        when(serviceAccessor.getUserFilterService()).thenReturn(userFilterService);\n        when(serviceAccessor.getProcessResourcesService()).thenReturn(processResourcesService);\n        when(serviceAccessor.getBPMArchiverService()).thenReturn(bpmArchiverService);\n        userTransactionService = new TestUserTransactionService();\n        when(serviceAccessor.getUserTransactionService()).thenReturn(userTransactionService);\n\n        sUserTaskInstance = new SUserTaskInstance(\"userTaskName\", FLOW_NODE_DEFINITION_ID, PROCESS_INSTANCE_ID,\n                PROCESS_INSTANCE_ID,\n                ACTOR_ID, STaskPriority.ABOVE_NORMAL, PROCESS_DEFINITION_ID, PROCESS_INSTANCE_ID);\n        sUserTaskInstance.setLogicalGroup(3, PROCESS_INSTANCE_ID);\n        sUserTaskInstance.setId(FLOW_NODE_INSTANCE_ID);\n        when(activityInstanceService.getFlowNodeInstance(FLOW_NODE_INSTANCE_ID)).thenReturn(sUserTaskInstance);\n        SAUserTaskInstance value = new SAUserTaskInstance(sUserTaskInstance);\n        value.setId(ARCHIVED_FLOW_NODE_INSTANCE_ID);\n        when(activityInstanceService.getArchivedFlowNodeInstance(ARCHIVED_FLOW_NODE_INSTANCE_ID)).thenReturn(value);\n        processDefinition = new SProcessDefinitionImpl(\"myProcess\", \"1.0\");\n        SFlowElementContainerDefinitionImpl processContainer = new SFlowElementContainerDefinitionImpl();\n        processDefinition.setProcessContainer(processContainer);\n        userTaskDefinition = new SUserTaskDefinitionImpl(FLOW_NODE_DEFINITION_ID, \"userTask\", \"actor\");\n        processContainer.addActivity(userTaskDefinition);\n\n        doReturn(processDefinition).when(processDefinitionService).getProcessDefinition(PROCESS_DEFINITION_ID);\n\n        SProcessInstance sProcessInstance = new SProcessInstance(\"processName\", PROCESS_DEFINITION_ID);\n        when(processInstanceService.getProcessInstance(PROCESS_INSTANCE_ID)).thenReturn(sProcessInstance);\n        SAProcessInstance value1 = new SAProcessInstance(sProcessInstance);\n        value1.setId(ARCHIVED_PROCESS_INSTANCE_ID);\n        when(processInstanceService.getArchivedProcessInstance(PROCESS_INSTANCE_ID)).thenReturn(value1);\n        doReturn(SSession.builder().id(54L).userName(\"john\").userId(12).build()).when(processAPI)\n                .getSession();\n        doReturn(\"john\").when(processAPI).getUserNameFromSession();\n\n        when(serviceAccessor.getMessagesHandlingService()).thenReturn(messageHandlingService);\n    }\n\n    @Test\n    public void getFlownodeStateCountersForProcessDefinition_should_build_proper_journal_counters() throws Exception {\n        final long processDefinitionId = 58L;\n        final List<SFlowNodeInstanceStateCounter> flownodes = new ArrayList<>(4);\n        flownodes.add(new SFlowNodeInstanceStateCounter(\"step1\", \"ready\", 2L));\n        flownodes.add(new SFlowNodeInstanceStateCounter(\"step2\", \"ready\", 4L));\n        flownodes.add(new SFlowNodeInstanceStateCounter(\"step1\", \"executing\", 1L));\n        flownodes.add(new SFlowNodeInstanceStateCounter(\"step3\", \"failed\", 8L));\n        when(activityInstanceService.getNumberOfFlownodesOfProcessDefinitionInAllStates(processDefinitionId))\n                .thenReturn(flownodes);\n\n        final Map<String, Map<String, Long>> flownodeStateCounters = processAPI\n                .getActiveFlownodeStateCountersForProcessDefinition(processDefinitionId);\n        assertThat(flownodeStateCounters.size()).isEqualTo(3);\n\n        final Map<String, Long> step1 = flownodeStateCounters.get(\"step1\");\n        assertThat(step1.size()).isEqualTo(2);\n        assertThat(step1.get(\"ready\")).isEqualTo(2L);\n        assertThat(step1.get(\"executing\")).isEqualTo(1L);\n\n        final Map<String, Long> step2 = flownodeStateCounters.get(\"step2\");\n        assertThat(step2.size()).isEqualTo(1);\n        assertThat(step2.get(\"ready\")).isEqualTo(4L);\n\n        final Map<String, Long> step3 = flownodeStateCounters.get(\"step3\");\n        assertThat(step3.size()).isEqualTo(1);\n        assertThat(step3.get(\"failed\")).isEqualTo(8L);\n    }\n\n    @Test(expected = ProcessDefinitionNotFoundException.class)\n    public void getFlownodeStateCountersForProcessDefinition_should_throw_exception_when_process_is_not_found()\n            throws Exception {\n        final long processDefinitionId = 35L;\n\n        when(processDefinitionService.getProcessDeploymentInfo(processDefinitionId))\n                .thenThrow(new SProcessDefinitionNotFoundException(\"process not found\", processDefinitionId));\n\n        processAPI.getActiveFlownodeStateCountersForProcessDefinition(processDefinitionId);\n    }\n\n    @Test\n    public void getFlownodeStateCounters_should_build_proper_journal_and_archived_counters() throws Exception {\n        final long processInstanceId = 9811L;\n\n        when(processInstanceService.getLastArchivedProcessInstance(processInstanceId))\n                .thenReturn(mock(SAProcessInstance.class));\n\n        final List<SFlowNodeInstanceStateCounter> flownodes = new ArrayList<>(4);\n        flownodes.add(new SFlowNodeInstanceStateCounter(\"step1\", \"completed\", 2L));\n        flownodes.add(new SFlowNodeInstanceStateCounter(\"step2\", \"completed\", 4L));\n        flownodes.add(new SFlowNodeInstanceStateCounter(\"step1\", \"ready\", 1L));\n        flownodes.add(new SFlowNodeInstanceStateCounter(\"step3\", \"failed\", 8L));\n        when(activityInstanceService.getNumberOfFlownodesInAllStates(processInstanceId)).thenReturn(flownodes);\n\n        final List<SFlowNodeInstanceStateCounter> archivedFlownodes = new ArrayList<>(1);\n        archivedFlownodes.add(new SFlowNodeInstanceStateCounter(\"step2\", \"aborted\", 3L));\n        when(activityInstanceService.getNumberOfArchivedFlownodesInAllStates(processInstanceId))\n                .thenReturn(archivedFlownodes);\n\n        final Map<String, Map<String, Long>> flownodeStateCounters = processAPI\n                .getFlownodeStateCounters(processInstanceId);\n        assertThat(flownodeStateCounters.size()).isEqualTo(3);\n\n        final Map<String, Long> step1 = flownodeStateCounters.get(\"step1\");\n        assertThat(step1.size()).isEqualTo(2);\n        assertThat(step1.get(\"completed\")).isEqualTo(2L);\n        assertThat(step1.get(\"ready\")).isEqualTo(1L);\n\n        final Map<String, Long> step2 = flownodeStateCounters.get(\"step2\");\n        assertThat(step2.size()).isEqualTo(2);\n        assertThat(step2.get(\"aborted\")).isEqualTo(3L);\n        assertThat(step2.get(\"completed\")).isEqualTo(4L);\n\n        final Map<String, Long> step3 = flownodeStateCounters.get(\"step3\");\n        assertThat(step3.size()).isEqualTo(1);\n        assertThat(step3.get(\"failed\")).isEqualTo(8L);\n    }\n\n    @Test(expected = RetrieveException.class)\n    public void getFlownodeStateCounters_should_throw_exception_when_process_instance_is_not_found_anywhere()\n            throws Exception {\n        final long processInstanceId = 11L;\n        when(processInstanceService.getLastArchivedProcessInstance(processInstanceId)).thenReturn(null);\n        when(processInstanceService.getProcessInstance(processInstanceId)).thenReturn(null);\n\n        processAPI.getFlownodeStateCounters(processInstanceId);\n    }\n\n    @Test\n    public void getFlownodeStateCounters_should_not_throw_exception_when_process_instance_is_found_in_active_instances()\n            throws Exception {\n        final long processInstanceId = 11L;\n        when(processInstanceService.getLastArchivedProcessInstance(processInstanceId)).thenReturn(null);\n        when(processInstanceService.getProcessInstance(processInstanceId)).thenReturn(mock(SProcessInstance.class));\n\n        processAPI.getFlownodeStateCounters(processInstanceId);\n    }\n\n    @Test\n    public void cancelAnUnknownProcessInstanceThrowsANotFoundException() throws Exception {\n        final LockService lockService = mock(LockService.class);\n        final ProcessInstanceInterruptor interruptor = mock(ProcessInstanceInterruptor.class);\n\n        when(serviceAccessor.getLockService()).thenReturn(lockService);\n        doReturn(interruptor).when(serviceAccessor).getProcessInstanceInterruptor();\n        doThrow(new SProcessInstanceNotFoundException(PROCESS_INSTANCE_ID)).when(interruptor).interruptProcessInstance(\n                PROCESS_INSTANCE_ID,\n                SStateCategory.CANCELLING);\n\n        try {\n            processAPI.cancelProcessInstance(PROCESS_INSTANCE_ID);\n            fail(\"The process instance does not exists\");\n        } catch (final ProcessInstanceNotFoundException pinfe) {\n            verify(lockService).lock(PROCESS_INSTANCE_ID, SFlowElementsContainerType.PROCESS.name());\n            verify(lockService).unlock(nullable(BonitaLock.class));\n        }\n    }\n\n    @Test\n    public void updateProcessDataInstance_should_call_updateProcessDataInstances() throws Exception {\n        // Given\n        doNothing().when(processAPI).updateProcessDataInstances(eq(PROCESS_INSTANCE_ID), anyMap());\n\n        // When\n        processAPI.updateProcessDataInstance(\"foo\", PROCESS_INSTANCE_ID, \"go\");\n\n        // Then\n        verify(processAPI).updateProcessDataInstances(eq(PROCESS_INSTANCE_ID),\n                eq(Collections.singletonMap(\"foo\", \"go\")));\n    }\n\n    @Test(expected = UpdateException.class)\n    public void updateProcessDataInstance_should_throw_exception_when_updateProcessDataInstances_failed()\n            throws Exception {\n        // Given\n        doThrow(new UpdateException()).when(processAPI).updateProcessDataInstances(eq(PROCESS_INSTANCE_ID), anyMap());\n        // When\n        processAPI.updateProcessDataInstance(\"foo\", PROCESS_INSTANCE_ID, \"go\");\n    }\n\n    @Test\n    public void updateProcessDataInstances_should_update_data_instances_when_new_value_is_instance_of_data_type()\n            throws Exception {\n        // Given\n        ClassLoader processClassLoader = mock(ClassLoader.class);\n        when(processClassLoader.loadClass(String.class.getName())).thenReturn((Class) String.class);\n        doReturn(processClassLoader).when(processAPI).getProcessInstanceClassloader(any(ServiceAccessor.class),\n                anyLong());\n\n        final SBlobDataInstance sDataFoo = new SBlobDataInstance();\n        sDataFoo.setClassName(String.class.getName());\n        sDataFoo.setName(\"foo\");\n\n        final SBlobDataInstance sDataBar = new SBlobDataInstance();\n        sDataBar.setClassName(String.class.getName());\n        sDataBar.setName(\"bar\");\n\n        doReturn(asList(sDataFoo, sDataBar)).when(dataInstanceService).getDataInstances(argument.capture(), anyLong(),\n                anyString(),\n                any(ParentContainerResolver.class));\n\n        // Then update the data instances\n        final Map<String, Serializable> dataNameValues = new HashMap<>();\n        dataNameValues.put(\"foo\", \"go\");\n        dataNameValues.put(\"bar\", \"go\");\n\n        // When\n        processAPI.updateProcessDataInstances(PROCESS_INSTANCE_ID, dataNameValues);\n\n        // Then\n        // Check that we called DataInstanceService for each pair data/value\n        verify(dataInstanceService, times(2)).updateDataInstance(any(SDataInstance.class),\n                any(EntityUpdateDescriptor.class));\n        assertThat(argument.getValue()).containsOnly(\"foo\", \"bar\");\n        verify(dataInstanceService).updateDataInstance(eq(sDataFoo), any(EntityUpdateDescriptor.class));\n        verify(dataInstanceService).updateDataInstance(eq(sDataBar), any(EntityUpdateDescriptor.class));\n    }\n\n    @Test\n    public void updateProcessDataInstances_should_throw_exception_when_new_value_is_not_instance_of_data_type()\n            throws Exception {\n        // Given\n        final String dataName = \"dataName\";\n        ClassLoader processClassLoader = mock(ClassLoader.class);\n        when(processClassLoader.loadClass(List.class.getName())).thenReturn((Class) List.class);\n        doReturn(processClassLoader).when(processAPI).getProcessInstanceClassloader(any(ServiceAccessor.class),\n                anyLong());\n\n        final Map<String, Serializable> dataNameValues = singletonMap(dataName, \"dataValue\");\n\n        final SBlobDataInstance dataInstance = new SBlobDataInstance();\n        dataInstance.setClassName(List.class.getName());\n        dataInstance.setName(dataName);\n        doReturn(singletonList(dataInstance)).when(dataInstanceService).getDataInstances(\n                singletonList(dataName),\n                PROCESS_INSTANCE_ID, DataInstanceContainer.PROCESS_INSTANCE.toString(), parentContainerResolver);\n\n        // When\n        try {\n            processAPI.updateProcessDataInstances(PROCESS_INSTANCE_ID, dataNameValues);\n            fail(\"An exception should have been thrown.\");\n        } catch (final UpdateException e) {\n            // Then\n            assertThat(e.getMessage()).isEqualTo(\n                    \"DATA_NAME=\" + dataName + \" | DATA_CLASS_NAME=java.util.List | The type of new value [\"\n                            + String.class.getName()\n                            + \"] is not compatible with the type of the data.\");\n            final Map<ExceptionContext, Serializable> exceptionContext = e.getContext();\n            assertThat(List.class.getName()).isEqualTo(exceptionContext.get(ExceptionContext.DATA_CLASS_NAME));\n            assertThat(dataName).isEqualTo(exceptionContext.get(ExceptionContext.DATA_NAME));\n        }\n    }\n\n    @Test\n    public void should_updateProcessDataInstances_call_DataInstance_on_non_existing_data_throw_UpdateException()\n            throws Exception {\n        final long processInstanceId = 42L;\n        doReturn(null).when(processAPI).getProcessInstanceClassloader(any(ServiceAccessor.class), anyLong());\n        doThrow(new SDataInstanceReadException(\"Mocked\")).when(dataInstanceService).getDataInstances(argument.capture(),\n                anyLong(), anyString(),\n                any(ParentContainerResolver.class));\n\n        // Then update the data instances\n        final Map<String, Serializable> dataNameValues = new HashMap<>();\n        dataNameValues.put(\"foo\", \"go\");\n        dataNameValues.put(\"bar\", \"go\");\n        try {\n            processAPI.updateProcessDataInstances(processInstanceId, dataNameValues);\n            fail(\"An exception should have been thrown.\");\n        } catch (final UpdateException e) {\n            // Ok\n        }\n\n        // Check that we called DataInstanceService for each pair data/value\n        verify(dataInstanceService, never()).updateDataInstance(any(SDataInstance.class),\n                any(EntityUpdateDescriptor.class));\n        assertThat(argument.getValue()).containsOnly(\"foo\", \"bar\");\n    }\n\n    @Test\n    @SuppressWarnings(\"unchecked\")\n    public void replayingAFailedJobNoParamShouldExecuteAgainSchedulerServiceWithNoParameters() throws Exception {\n        final long jobDescriptorId = 25L;\n        processAPI.replayFailedJob(jobDescriptorId, null);\n        processAPI.replayFailedJob(jobDescriptorId, Collections.EMPTY_MAP);\n\n        verify(schedulerService, times(2)).retryJobThatFailed(jobDescriptorId);\n    }\n\n    @Test\n    @SuppressWarnings({ \"rawtypes\" })\n    public void replayingAFailedJobShouldExecuteAgainSchedulerServiceWithSomeParameters() throws Exception {\n        final Map<String, Serializable> parameters = Collections.singletonMap(\"anyparam\", Boolean.FALSE);\n        final long jobDescriptorId = 544L;\n        doNothing().when(schedulerService).retryJobThatFailed(anyLong(), anyList());\n        doReturn(new ArrayList()).when(processAPI).buildJobParametersFromMap(parameters);\n\n        processAPI.replayFailedJob(jobDescriptorId, parameters);\n\n        verify(schedulerService).retryJobThatFailed(eq(jobDescriptorId), anyList());\n    }\n\n    @Test\n    public void replayingAFailedJobWithNoParamShouldCallWithNullParams() throws Exception {\n        final long jobDescriptorId = 544L;\n\n        doNothing().when(processAPI).replayFailedJob(jobDescriptorId, null);\n\n        processAPI.replayFailedJob(jobDescriptorId);\n\n        verify(processAPI).replayFailedJob(jobDescriptorId, null);\n    }\n\n    @Test\n    public void getJobParametersShouldConvertMapIntoList() {\n        // given:\n        final Map<String, Serializable> parameters = new HashMap<>(2);\n        final String key1 = \"mon param 1\";\n        final String key2 = \"my second param\";\n        final SJobParameter expectedValue1 = mockSJobParameter(key1);\n        parameters.put(expectedValue1.getKey(), expectedValue1.getValue());\n\n        final SJobParameter expectedValue2 = mockSJobParameter(key2);\n        parameters.put(expectedValue2.getKey(), expectedValue2.getValue());\n\n        doReturn(expectedValue1).when(processAPI).buildSJobParameter(eq(key1), any(Serializable.class));\n        doReturn(expectedValue2).when(processAPI).buildSJobParameter(eq(key2), any(Serializable.class));\n\n        // when:\n        final List<SJobParameter> jobParameters = processAPI.buildJobParametersFromMap(parameters);\n\n        // then:\n        assertThat(jobParameters).containsOnly(expectedValue1, expectedValue2);\n    }\n\n    private SJobParameter mockSJobParameter(final String key) {\n        final SJobParameter jobParam = mock(SJobParameter.class);\n        when(jobParam.getKey()).thenReturn(key);\n        when(jobParam.getValue()).thenReturn(Integer.MAX_VALUE);\n        return jobParam;\n    }\n\n    @Test\n    public void getActivityTransientDataInstances() throws Exception {\n        final int nbResults = 100;\n        final int startIndex = 0;\n        final SDataInstance sDataInstance = mock(SDataInstance.class);\n        final List<SDataInstance> sDataInstances = singletonList(sDataInstance);\n        when(transientDataService.getDataInstances(FLOW_NODE_INSTANCE_ID,\n                DataInstanceContainer.ACTIVITY_INSTANCE.name(), startIndex, nbResults))\n                .thenReturn(sDataInstances);\n        final IntegerDataInstanceImpl dataInstance = mock(IntegerDataInstanceImpl.class);\n        doReturn(singletonList(dataInstance)).when(processAPI).convertModelToDataInstances(sDataInstances);\n\n        final List<DataInstance> dis = processAPI.getActivityTransientDataInstances(FLOW_NODE_INSTANCE_ID, startIndex,\n                nbResults);\n\n        assertThat(dis).contains(dataInstance);\n\n        verify(processAPI).convertModelToDataInstances(sDataInstances);\n        verify(transientDataService).getDataInstances(FLOW_NODE_INSTANCE_ID,\n                DataInstanceContainer.ACTIVITY_INSTANCE.name(), startIndex, nbResults);\n        verify(serviceAccessor).getTransientDataService();\n        verify(serviceAccessor).getClassLoaderService();\n        verify(serviceAccessor).getActivityInstanceService();\n        verify(activityInstanceService).getFlowNodeInstance(FLOW_NODE_INSTANCE_ID);\n        verify(classLoaderService)\n                .getClassLoader(argThat(id -> id.getType().equals(ScopeType.PROCESS)));\n    }\n\n    @Test\n    public void getActivityTransientDataInstance() throws Exception {\n        final String dataName = \"TransientName\";\n\n        final SDataInstance sDataInstance = mock(SDataInstance.class);\n        when(transientDataService.getDataInstance(dataName, FLOW_NODE_INSTANCE_ID,\n                DataInstanceContainer.ACTIVITY_INSTANCE.name())).thenReturn(sDataInstance);\n        final IntegerDataInstanceImpl dataInstance = mock(IntegerDataInstanceImpl.class);\n        doReturn(dataInstance).when(processAPI).convertModeltoDataInstance(sDataInstance);\n\n        final DataInstance di = processAPI.getActivityTransientDataInstance(dataName, FLOW_NODE_INSTANCE_ID);\n\n        assertThat(di).isEqualTo(dataInstance);\n\n        verify(processAPI).convertModeltoDataInstance(sDataInstance);\n        verify(transientDataService).getDataInstance(dataName, FLOW_NODE_INSTANCE_ID,\n                DataInstanceContainer.ACTIVITY_INSTANCE.name());\n        verify(serviceAccessor).getTransientDataService();\n        verify(serviceAccessor).getClassLoaderService();\n        verify(serviceAccessor).getActivityInstanceService();\n        verify(activityInstanceService).getFlowNodeInstance(FLOW_NODE_INSTANCE_ID);\n        verify(classLoaderService)\n                .getClassLoader(argThat(id -> id.getType().equals(ScopeType.PROCESS)));\n    }\n\n    @Test\n    public void updateActivityDataInstance_should_throw_exception_when_new_value_is_not_instance_of_data_type()\n            throws Exception {\n        // Given\n        final String dataName = \"dataName\";\n        ClassLoader processClassLoader = mock(ClassLoader.class);\n        when(processClassLoader.loadClass(List.class.getName())).thenReturn((Class) List.class);\n        when(classLoaderService.getClassLoader(any())).thenReturn(processClassLoader);\n\n        final SBlobDataInstance dataInstance = new SBlobDataInstance();\n        dataInstance.setClassName(List.class.getName());\n        dataInstance.setName(dataName);\n        doReturn(dataInstance).when(dataInstanceService).getDataInstance(dataName, FLOW_NODE_INSTANCE_ID,\n                DataInstanceContainer.ACTIVITY_INSTANCE.toString(),\n                parentContainerResolver);\n\n        // When\n        try {\n\n            processAPI.updateActivityDataInstance(dataName, FLOW_NODE_INSTANCE_ID, \"dataValue\");\n            fail(\"An exception should have been thrown.\");\n        } catch (final UpdateException e) {\n            // Then\n            assertThat(\"DATA_NAME=\" + dataName + \" | DATA_CLASS_NAME=java.util.List | The type of new value [\"\n                    + String.class.getName()\n                    + \"] is not compatible with the type of the data.\").isEqualTo(e.getMessage());\n            final Map<ExceptionContext, Serializable> exceptionContext = e.getContext();\n            assertThat(List.class.getName()).isEqualTo(exceptionContext.get(ExceptionContext.DATA_CLASS_NAME));\n            assertThat(dataName).isEqualTo(exceptionContext.get(ExceptionContext.DATA_NAME));\n        }\n    }\n\n    @Test\n    public void updateActivityTransientDataInstance_should_throw_exception_when_new_value_is_not_instance_of_data_type()\n            throws Exception {\n        // Given\n        final String dataName = \"dataName\";\n        ClassLoader processClassLoader = mock(ClassLoader.class);\n        when(processClassLoader.loadClass(List.class.getName())).thenReturn((Class) List.class);\n        when(classLoaderService.getClassLoader(any()))\n                .thenReturn(processClassLoader);\n\n        final SBlobDataInstance dataInstance = new SBlobDataInstance();\n        dataInstance.setClassName(List.class.getName());\n        dataInstance.setName(dataName);\n        doReturn(dataInstance).when(transientDataService).getDataInstance(dataName, FLOW_NODE_INSTANCE_ID,\n                DataInstanceContainer.ACTIVITY_INSTANCE.toString());\n\n        // When\n        try {\n\n            processAPI.updateActivityTransientDataInstance(dataName, FLOW_NODE_INSTANCE_ID, \"dataValue\");\n            fail(\"An exception should have been thrown.\");\n        } catch (final UpdateException e) {\n            // Then\n            assertThat(\"DATA_NAME=\" + dataName + \" | DATA_CLASS_NAME=java.util.List | The type of new value [\"\n                    + String.class.getName()\n                    + \"] is not compatible with the type of the data.\").isEqualTo(e.getMessage());\n            final Map<ExceptionContext, Serializable> exceptionContext = e.getContext();\n            assertThat(List.class.getName()).isEqualTo(exceptionContext.get(ExceptionContext.DATA_CLASS_NAME));\n            assertThat(dataName).isEqualTo(exceptionContext.get(ExceptionContext.DATA_NAME));\n        }\n    }\n\n    @Test\n    public void updateActivityTransientDataInstance_should_call_updateTransientData_when_new_value_is_instance_of_data_type()\n            throws Exception {\n        // Given\n        final String dataValue = \"TestOfCourse\";\n        final String dataName = \"TransientName\";\n        ClassLoader contextClassLoader = mock(ClassLoader.class);\n        when(classLoaderService.getClassLoader(any())).thenReturn(contextClassLoader);\n        doNothing().when(processAPI).updateTransientData(dataName, FLOW_NODE_INSTANCE_ID, dataValue,\n                transientDataService, contextClassLoader);\n\n        // When\n        processAPI.updateActivityTransientDataInstance(dataName, FLOW_NODE_INSTANCE_ID, dataValue);\n\n        // Then\n\n        verify(processAPI).updateTransientData(dataName, FLOW_NODE_INSTANCE_ID, dataValue, transientDataService,\n                contextClassLoader);\n        verify(serviceAccessor).getTransientDataService();\n        verify(serviceAccessor).getClassLoaderService();\n        verify(serviceAccessor).getActivityInstanceService();\n        verify(activityInstanceService).getFlowNodeInstance(FLOW_NODE_INSTANCE_ID);\n        verify(classLoaderService)\n                .getClassLoader(argThat(id -> id.getType().equals(ScopeType.PROCESS)));\n    }\n\n    @Test(expected = UpdateException.class)\n    public void updateActivityTransientDataInstance_should_throw_exception_when_new_value_is_instance_of_data_type_and_updateTransientData_failed()\n            throws Exception {\n        // Given\n        final String dataValue = \"TestOfCourse\";\n        final String dataName = \"TransientName\";\n        doThrow(new SDataInstanceException(\"\")).when(processAPI).updateTransientData(eq(dataName),\n                eq(FLOW_NODE_INSTANCE_ID), eq(dataValue),\n                eq(transientDataService), nullable(ClassLoader.class));\n\n        // When\n        processAPI.updateActivityTransientDataInstance(dataName, FLOW_NODE_INSTANCE_ID, dataValue);\n    }\n\n    @Test\n    public void updateTransientData() throws Exception {\n        // Given\n        final String dataValue = \"TestOfCourse\";\n        final String dataName = \"TransientName\";\n        final SBlobDataInstance dataInstance = new SBlobDataInstance();\n        dataInstance.setClassName(String.class.getName());\n        dataInstance.setName(dataName);\n        when(transientDataService.getDataInstance(dataName, FLOW_NODE_INSTANCE_ID,\n                DataInstanceContainer.ACTIVITY_INSTANCE.toString())).thenReturn(dataInstance);\n\n        // When\n        processAPI.updateTransientData(dataName, FLOW_NODE_INSTANCE_ID, dataValue, transientDataService,\n                this.getClass().getClassLoader());\n\n        // Then\n        verify(transientDataService).updateDataInstance(eq(dataInstance), any(EntityUpdateDescriptor.class));\n        verify(transientDataService).getDataInstance(dataName, FLOW_NODE_INSTANCE_ID,\n                DataInstanceContainer.ACTIVITY_INSTANCE.toString());\n    }\n\n    @Test\n    public void getUserIdsForActor_returns_result_of_actor_mapping_service() throws Exception {\n        // given\n        final SActor actor = mock(SActor.class);\n        when(actor.getId()).thenReturn(ACTOR_ID);\n\n        when(actorMappingService.getPossibleUserIdsOfActorId(ACTOR_ID, 0, 10)).thenReturn(Arrays.asList(1L, 10L));\n        when(actorMappingService.getActor(ACTOR_NAME, PROCESS_DEFINITION_ID)).thenReturn(actor);\n\n        // when\n        final List<Long> userIdsForActor = processAPI.getUserIdsForActor(PROCESS_DEFINITION_ID, ACTOR_NAME, 0, 10);\n\n        // then\n        assertThat(userIdsForActor).containsExactly(1L, 10L);\n    }\n\n    @Test\n    public void getUserIdsForActor_throws_RetrieveException_when_actorMappingService_throws_SBonitaException()\n            throws Exception {\n        when(actorMappingService.getActor(ACTOR_NAME, PROCESS_DEFINITION_ID))\n                .thenThrow(new SActorNotFoundException(\"\"));\n\n        try {\n            processAPI.getUserIdsForActor(PROCESS_DEFINITION_ID, ACTOR_NAME, 0, 10);\n            fail(\"Exception expected\");\n        } catch (final RetrieveException e) {\n            // then ok\n        }\n\n    }\n\n    @Test\n    public void updateActivityInstanceVariables_should_load_processDef_classes() throws Exception {\n        final String dataInstanceName = \"acase\";\n\n        final LeftOperand leftOperand = new LeftOperandBuilder().createNewInstance().setName(dataInstanceName)\n                .setType(LeftOperand.TYPE_DATA).done();\n        final String customDataTypeName = \"com.bonitasoft.support.Case\";\n        final Expression expression = new ExpressionBuilder().createGroovyScriptExpression(\"updateDataCaseTest\",\n                \"new com.bonitasoft.support.Case(\\\"title\\\", \\\"description\\\")\",\n                customDataTypeName);\n        final Operation operation = new OperationBuilder().createNewInstance().setOperator(\"=\")\n                .setLeftOperand(leftOperand).setType(OperatorType.ASSIGNMENT)\n                .setRightOperand(expression).done();\n        final ClassLoader contextClassLoader = mock(ClassLoader.class);\n        when(classLoaderService.getClassLoader(any())).thenReturn(contextClassLoader);\n        final SActivityInstance activityInstance = mock(SActivityInstance.class);\n        when(activityInstanceService.getActivityInstance(anyLong())).thenReturn(activityInstance);\n\n        final List<Operation> operations = new ArrayList<>();\n        operations.add(operation);\n        doReturn(singletonList(mock(SOperation.class))).when(processAPI).convertOperations(operations);\n\n        processAPI.updateActivityInstanceVariables(operations, 2, null);\n\n        verify(classLoaderService).getClassLoader(any());\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void deleteArchivedProcessInstances_by_ids_should_throw_exception_when_list_is_empty() throws Exception {\n        processAPI.deleteArchivedProcessInstancesInAllStates(Collections.emptyList());\n    }\n\n    @Test\n    public void searchFailedProcessInstances_should_return_failed_process_instances() throws Exception {\n        // Given\n        final SearchOptions searchOptions = new SearchOptionsBuilder(0, 20).done();\n        final long numberOfFailedProcessInstances = 2L;\n        final List<ProcessInstance> failedProcessInstances = List.of(new ProcessInstanceImpl(\"name\"));\n        final long processDefinitionId = 9L;\n        final List<SProcessInstance> sFailedProcessInstances = List\n                .of(new SProcessInstance(\"name\", processDefinitionId));\n        doReturn(numberOfFailedProcessInstances).when(processInstanceService)\n                .getNumberOfFailedProcessInstances(any(QueryOptions.class));\n        doReturn(sFailedProcessInstances).when(processInstanceService)\n                .searchFailedProcessInstances(any(QueryOptions.class));\n        doReturn(mock(SProcessDefinition.class)).when(processDefinitionService)\n                .getProcessDefinition(processDefinitionId);\n\n        // When\n        final SearchResult<ProcessInstance> searchFailedProcessInstances = processAPI\n                .searchFailedProcessInstances(searchOptions);\n\n        // Then\n        assertThat(numberOfFailedProcessInstances).isEqualTo(searchFailedProcessInstances.getCount());\n        assertThat(failedProcessInstances).isEqualTo(searchFailedProcessInstances.getResult());\n    }\n\n    @Test(expected = RetrieveException.class)\n    public void getConnectorsImplementations_should_throw__exception() throws Exception {\n        //given\n        final SConnectorException sConnectorException = new SConnectorException(\"message\");\n        doThrow(sConnectorException).when(connectorService).getConnectorImplementations(anyLong(),\n                anyInt(), anyInt(), anyString(),\n                any(OrderByType.class));\n\n        //when then exception\n        processAPI.getConnectorImplementations(PROCESS_DEFINITION_ID, START_INDEX, MAX_RESULT,\n                CONNECTOR_CRITERION_DEFINITION_ID_ASC);\n\n    }\n\n    @Test(expected = RetrieveException.class)\n    public void getNumberOfConnectorImplementations_should_throw__exception() throws Exception {\n        //given\n        final SConnectorException sConnectorException = new SConnectorException(\"message\");\n        doThrow(sConnectorException).when(connectorService).getNumberOfConnectorImplementations(anyLong());\n\n        //when then exception\n        processAPI.getNumberOfConnectorImplementations(PROCESS_DEFINITION_ID);\n\n    }\n\n    @Test(expected = SearchException.class)\n    public void searchFailedProcessInstances_should_throw_exception_when_transaction_content_failed() throws Exception {\n        // Given\n        final SearchOptions searchOptions = new SearchOptionsBuilder(0, 20).done();\n        doThrow(new SBonitaReadException(new Exception(\"plop\"))).when(processInstanceService)\n                .getNumberOfFailedProcessInstances(any(QueryOptions.class));\n\n        // When\n        processAPI.searchFailedProcessInstances(searchOptions);\n    }\n\n    @Test\n    public void getConnectorsImplementations_should_return_list() throws Exception {\n        //given\n        final List<SConnectorImplementationDescriptor> sConnectorImplementationDescriptors = createConnectorList();\n\n        doReturn(sConnectorImplementationDescriptors).when(connectorService).getConnectorImplementations(anyLong(),\n                anyInt(), anyInt(), anyString(),\n                any(OrderByType.class));\n\n        //when\n        final List<ConnectorImplementationDescriptor> connectorImplementations = processAPI.getConnectorImplementations(\n                PROCESS_DEFINITION_ID, START_INDEX,\n                MAX_RESULT, CONNECTOR_CRITERION_DEFINITION_ID_ASC);\n\n        //then\n        assertThat(connectorImplementations).as(\"should return connectore implementation\")\n                .hasSameSizeAs(sConnectorImplementationDescriptors);\n    }\n\n    @Test\n    public void getNumberOfConnectorImplementations_should_return_count() throws Exception {\n        //given\n        final List<SConnectorImplementationDescriptor> sConnectorImplementationDescriptors = createConnectorList();\n\n        doReturn((long) sConnectorImplementationDescriptors.size()).when(connectorService)\n                .getNumberOfConnectorImplementations(PROCESS_DEFINITION_ID);\n\n        //when\n        final long numberOfConnectorImplementations = processAPI\n                .getNumberOfConnectorImplementations(PROCESS_DEFINITION_ID);\n\n        //then\n        assertThat(numberOfConnectorImplementations).as(\"should return count\")\n                .isEqualTo(sConnectorImplementationDescriptors.size());\n    }\n\n    private List<SConnectorImplementationDescriptor> createConnectorList() {\n        final List<SConnectorImplementationDescriptor> sConnectorImplementationDescriptors = new ArrayList<>();\n        final SConnectorImplementationDescriptor sConnectorImplementationDescriptor = new SConnectorImplementationDescriptor(\n                \"className\", \"id\", \"version\",\n                \"definitionId\", \"definitionVersion\", new ArrayList<>(Arrays.asList(\"dep1\", \"dep2\")));\n        sConnectorImplementationDescriptors.add(sConnectorImplementationDescriptor);\n        sConnectorImplementationDescriptors.add(sConnectorImplementationDescriptor);\n        sConnectorImplementationDescriptors.add(sConnectorImplementationDescriptor);\n        return sConnectorImplementationDescriptors;\n    }\n\n    @Test\n    public void evaluateExpressionsOnCompletedActivityInstance_should_call_getLastArchivedProcessInstance_using_parentProcessInstanceId()\n            throws Exception {\n        //given\n        final long processInstanceId = 21L;\n        final long activityInstanceId = 5L;\n        final ArchivedActivityInstance activityInstance = mock(ArchivedActivityInstance.class);\n        given(activityInstance.getProcessInstanceId()).willReturn(processInstanceId);\n        given(activityInstance.getArchiveDate()).willReturn(new Date());\n        doReturn(activityInstance).when(processAPI).getArchivedActivityInstance(activityInstanceId);\n\n        final ArchivedProcessInstance procInst = mock(ArchivedProcessInstance.class);\n        given(procInst.getProcessDefinitionId()).willReturn(1000L);\n        doReturn(procInst).when(processAPI).getLastArchivedProcessInstance(anyLong());\n\n        //when\n        processAPI.evaluateExpressionsOnCompletedActivityInstance(activityInstanceId,\n                new HashMap<>());\n\n        //then\n        verify(processAPI).getLastArchivedProcessInstance(processInstanceId);\n        verify(activityInstance, never()).getParentContainerId();\n        verify(activityInstance, never()).getParentActivityInstanceId();\n        verify(activityInstance, never()).getRootContainerId();\n    }\n\n    @Test\n    public void purgeClassLoader_should_call_delegate() throws Exception {\n        processAPI.purgeClassLoader(45L);\n\n        verify(managementAPIImplDelegate).purgeClassLoader(45L);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void deleteArchivedProcessInstances_by_ids_should_throw_exception_when_null_argument() throws Exception {\n        processAPI.deleteArchivedProcessInstancesInAllStates(null);\n    }\n\n    @Test\n    public void deleteArchivedProcessInstances_by_ids_should_return_0_when_no_archived_process_instance_for_ids()\n            throws Exception {\n        // Given\n        final long archivedProcessInstanceId = 42L;\n\n        // When\n        final long deleteArchivedProcessInstances = processAPI\n                .deleteArchivedProcessInstancesInAllStates(archivedProcessInstanceId);\n\n        // Then\n        assertThat(deleteArchivedProcessInstances)\n                .as(\"Must to return 0, when there are no archived process instance to delete.\")\n                .isEqualTo(0L);\n    }\n\n    @Test\n    public void deleteArchivedProcessInstances_by_ids_should_return_number_of_deleted_archived_process_instance_when_exist()\n            throws Exception {\n        // Given\n        final long archivedProcessInstanceId = 42L;\n        doReturn(1).when(processInstanceService).deleteArchivedProcessInstances(anyList());\n\n        // When\n        final long deleteArchivedProcessInstances = processAPI\n                .deleteArchivedProcessInstancesInAllStates(archivedProcessInstanceId);\n\n        // Then\n        assertThat(1L).as(\"Must to return 1 deleted archived process instance.\")\n                .isEqualTo(deleteArchivedProcessInstances);\n    }\n\n    @Test\n    public void deleteArchivedProcessInstance_by_id_should_delete_archived_process_instance_when_exist()\n            throws Exception {\n        // Given\n        final long processInstanceId = 42L;\n\n        // When\n        processAPI.deleteArchivedProcessInstancesInAllStates(processInstanceId);\n\n        // Then\n        verify(processInstanceService).deleteArchivedProcessInstances(List.of(processInstanceId));\n    }\n\n    @Test(expected = DeletionException.class)\n    public void deleteArchivedProcessInstance_by_id_should_throw_exception_when_getArchivedProcessInstance_throws_exception()\n            throws Exception {\n        // Given\n        final long archivedProcessInstanceId = 42L;\n        doThrow(new SProcessInstanceReadException(new Exception())).when(processInstanceService)\n                .deleteArchivedProcessInstances(anyList());\n\n        // When\n        processAPI.deleteArchivedProcessInstancesInAllStates(archivedProcessInstanceId);\n    }\n\n    @Test(expected = DeletionException.class)\n    public void deleteArchivedProcessInstance_by_id_should_throw_exception_when_deleteParentArchivedProcessInstanceAndElements_throws_exception()\n            throws Exception {\n        // Given\n        final long archivedProcessInstanceId = 42L;\n        doThrow(new SDeletionException(\"\")).when(processInstanceService).deleteArchivedProcessInstances(anyList());\n\n        // When\n        processAPI.deleteArchivedProcessInstancesInAllStates(archivedProcessInstanceId);\n    }\n\n    @Test(expected = SearchException.class)\n    public void searchEventTriggerInstances_should_throw_exception_when_transaction_throws_exception()\n            throws Exception {\n        // Given\n        final long processInstanceId = 42L;\n        final SearchOptions searchOptions = new SearchOptionsBuilder(0, 10).done();\n\n        doThrow(new SBonitaReadException(new Exception())).when(eventInstanceService)\n                .getNumberOfTimerEventTriggerInstances(eq(processInstanceId),\n                        any(QueryOptions.class));\n\n        // When\n        processAPI.searchTimerEventTriggerInstances(processInstanceId, searchOptions);\n    }\n\n    @Test(expected = UpdateException.class)\n    public void updateTimerEventTriggerInstance_should_throw_exception_when_new_execution_date_is_null()\n            throws Exception {\n        processAPI.updateExecutionDateOfTimerEventTriggerInstance(6, null);\n    }\n\n    @Test(expected = TimerEventTriggerInstanceNotFoundException.class)\n    public void updateTimerEventTriggerInstance_should_throw_exception_when_timer_event_trigger_not_exist()\n            throws Exception {\n        processAPI.updateExecutionDateOfTimerEventTriggerInstance(6, new Date());\n    }\n\n    @Test(expected = UpdateException.class)\n    public void updateTimerEventTriggerInstance_should_throw_exception_when_cant_get_timer_event_trigger()\n            throws Exception {\n        // Given\n        final int timerEventTriggerInstanceId = 6;\n        doThrow(new SEventTriggerInstanceReadException(new Exception(\"\"))).when(eventInstanceService)\n                .getEventTriggerInstance(STimerEventTriggerInstance.class,\n                        timerEventTriggerInstanceId);\n\n        // When\n        processAPI.updateExecutionDateOfTimerEventTriggerInstance(timerEventTriggerInstanceId, new Date());\n    }\n\n    @Test(expected = UpdateException.class)\n    public void updateTimerEventTriggerInstance_should_throw_exception_when_cant_update_timer_event_trigger()\n            throws Exception {\n        // Given\n        final int timerEventTriggerInstanceId = 6;\n        final STimerEventTriggerInstance sTimerEventTriggerInstance = mock(STimerEventTriggerInstance.class);\n        doReturn(sTimerEventTriggerInstance).when(eventInstanceService)\n                .getEventTriggerInstance(STimerEventTriggerInstance.class, timerEventTriggerInstanceId);\n        doThrow(new SEventTriggerInstanceModificationException(new Exception(\"\"))).when(eventInstanceService)\n                .updateEventTriggerInstance(\n                        eq(sTimerEventTriggerInstance), any(EntityUpdateDescriptor.class));\n\n        // When\n        processAPI.updateExecutionDateOfTimerEventTriggerInstance(timerEventTriggerInstanceId, new Date());\n    }\n\n    @Test(expected = UpdateException.class)\n    public void updateTimerEventTriggerInstance_should_throw_exception_when_cant_reschedule_job() throws Exception {\n        // Given\n        final int timerEventTriggerInstanceId = 6;\n        final Date date = new Date();\n        final STimerEventTriggerInstance sTimerEventTriggerInstance = mock(STimerEventTriggerInstance.class);\n        doReturn(sTimerEventTriggerInstance).when(eventInstanceService)\n                .getEventTriggerInstance(STimerEventTriggerInstance.class, timerEventTriggerInstanceId);\n\n        doThrow(new SSchedulerException(new Exception(\"\"))).when(schedulerService)\n                .rescheduleJob(nullable(String.class), eq(date));\n\n        // When\n        processAPI.updateExecutionDateOfTimerEventTriggerInstance(timerEventTriggerInstanceId, date);\n    }\n\n    @Test(expected = SearchException.class)\n    public void searchHumanTaskInstancesWithSearchException() throws Exception {\n        // Given\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        doReturn(new SearchHumanTaskInstanceDescriptor()).when(searchEntitiesDescriptor)\n                .getSearchHumanTaskInstanceDescriptor();\n\n        final SearchResult<HumanTaskInstance> humanTasksSearch = processAPI\n                .searchHumanTaskInstances(searchOptionsBuilder.sort(\"tyefv\", Order.ASC).done());\n        assertThat(0).isEqualTo(humanTasksSearch.getCount());\n    }\n\n    @Test\n    public void getPendingHumanTaskInstances_should_return_user_tasks_of_enabled_and_disabled_processes()\n            throws Exception {\n        final long userId = 1983L;\n        final List<Long> processDefinitionIds = new ArrayList<>();\n        processDefinitionIds.add(7897987L);\n        when(processDefinitionService.getProcessDefinitionIds(0, Integer.MAX_VALUE)).thenReturn(processDefinitionIds);\n        final List<SActor> actors = new ArrayList<>();\n        final SActor actor = mock(SActor.class);\n        actors.add(actor);\n        when(actor.getId()).thenReturn(454545L);\n        when(actorMappingService.getActors(new HashSet<>(processDefinitionIds), userId)).thenReturn(actors);\n        final OrderAndField orderAndField = OrderAndFields\n                .getOrderAndFieldForActivityInstance(ActivityInstanceCriterion.NAME_DESC);\n\n        processAPI.getPendingHumanTaskInstances(userId, 0, 100, ActivityInstanceCriterion.NAME_DESC);\n\n        verify(processDefinitionService).getProcessDefinitionIds(0, Integer.MAX_VALUE);\n        verify(actorMappingService).getActors(anySet(), eq(userId));\n        verify(activityInstanceService).getPendingTasks(eq(userId), anySet(), eq(0), eq(100),\n                eq(orderAndField.getField()),\n                eq(orderAndField.getOrder()));\n    }\n\n    @Test\n    public void getUserTaskContractVariableValue_should_return_archived_value() throws Exception {\n        when(contractDataService.getArchivedUserTaskDataValue(1983L, \"id\")).thenReturn(10L);\n\n        final Long id = (Long) processAPI.getUserTaskContractVariableValue(1983L, \"id\");\n\n        assertThat(id).isEqualTo(10L);\n    }\n\n    @Test(expected = BonitaRuntimeException.class)\n    public void getUserTaskContractVariableValue_should_throw_an_exception_if_an_exception_occurs_when_reading_data()\n            throws Exception {\n        //given\n        when(contractDataService.getArchivedUserTaskDataValue(1983L, \"id\"))\n                .thenThrow(new SBonitaReadException(\"exception\"));\n\n        //when then exception\n        processAPI.getUserTaskContractVariableValue(1983L, \"id\");\n    }\n\n    @Test(expected = ContractDataNotFoundException.class)\n    public void getUserTaskContractVariableValue_should_throw_an_exception_if_an_exception_occurs_when_getting_data()\n            throws Exception {\n        when(contractDataService.getArchivedUserTaskDataValue(1983L, \"id\"))\n                .thenThrow(new SContractDataNotFoundException(\"exception\"));\n\n        processAPI.getUserTaskContractVariableValue(1983L, \"id\");\n    }\n\n    @Test\n    public void getProcessContract_should_return_null_when_no_contract() throws ProcessDefinitionNotFoundException {\n        final ContractDefinition processContract = processAPI.getProcessContract(PROCESS_DEFINITION_ID);\n\n        assertThat(processContract).as(\"process contract\").isNull();\n    }\n\n    @Test\n    public void getArchivedProcessInstance_Should_Return_Instance_Of_Disabled_Process() throws Exception {\n        final long processInstanceId = PROCESS_INSTANCE_ID;\n\n        final SAProcessInstance saProcessInstance = mock(SAProcessInstance.class);\n        when(processInstanceService.getArchivedProcessInstance(processInstanceId)).thenReturn(saProcessInstance);\n        when(saProcessInstance.getProcessDefinitionId()).thenReturn(PROCESS_DEFINITION_ID);\n        final SProcessDefinition sProcessDefinition = mock(SProcessDefinition.class);\n        when(processDefinitionService.getProcessDefinition(PROCESS_DEFINITION_ID)).thenReturn(sProcessDefinition);\n\n        final ArchivedProcessInstance archivedProcessInstanceMocked = mock(ArchivedProcessInstance.class);\n        doReturn(archivedProcessInstanceMocked).when(processAPI).toArchivedProcessInstance(saProcessInstance,\n                sProcessDefinition);\n\n        final ArchivedProcessInstance archivedProcessInstance = processAPI\n                .getArchivedProcessInstance(processInstanceId);\n        assertThat(archivedProcessInstance).isEqualTo(archivedProcessInstanceMocked);\n    }\n\n    @Test\n    public void searchFailedProcessInstancesSupervisedBy_should_Return_ProcessInstances_And_Call_ProcessInstanceService()\n            throws Exception {\n        final long userId = 0;\n        final IdentityService identityService = mock(IdentityService.class);\n        when(serviceAccessor.getIdentityService()).thenReturn(identityService);\n        doReturn(mock(GetSUser.class)).when(processAPI).createTxUserGetter(userId, identityService);\n\n        final SearchOptions searchOptions = mock(SearchOptions.class);\n        final SearchFailedProcessInstancesSupervisedBy searchFailedProcessInstancesSupervisedBy = mock(\n                SearchFailedProcessInstancesSupervisedBy.class);\n        doReturn(searchFailedProcessInstancesSupervisedBy).when(processAPI)\n                .createSearchFailedProcessInstancesSupervisedBy(userId, searchOptions,\n                        processInstanceService, searchEntitiesDescriptor, processDefinitionService);\n\n        final SearchResult<ProcessInstance> searchResult = mock(SearchResult.class);\n        when(searchFailedProcessInstancesSupervisedBy.getResult()).thenReturn(searchResult);\n\n        final SearchResult<ProcessInstance> failedProcessInstancesSupervisedBy = processAPI\n                .searchFailedProcessInstancesSupervisedBy(userId,\n                        searchOptions);\n        assertThat(failedProcessInstancesSupervisedBy).isEqualTo(searchResult);\n    }\n\n    @Test\n    public void searchFailedProcessInstancesSupervisedBy_should_Return_Empty_Result_When_User_Does_Not_Exist()\n            throws Exception {\n        final long userId = 0;\n        final IdentityService identityService = mock(IdentityService.class);\n        when(serviceAccessor.getIdentityService()).thenReturn(identityService);\n        final GetSUser getSUser = mock(GetSUser.class);\n        doReturn(getSUser).when(processAPI).createTxUserGetter(userId, identityService);\n        doThrow(new SBonitaException() {\n        }).when(getSUser).execute();\n\n        final SearchResult<ProcessInstance> failedProcessInstancesSupervisedBy = processAPI\n                .searchFailedProcessInstancesSupervisedBy(userId,\n                        mock(SearchOptions.class));\n        assertThat(failedProcessInstancesSupervisedBy.getCount()).isEqualTo(0);\n        assertThat(failedProcessInstancesSupervisedBy.getResult()).hasSize(0);\n    }\n\n    @Test\n    public void should_getUserTaskExecutionContext_evaluate_context_of_activity() throws Exception {\n        SExpressionImpl e1 = createExpression(\"e1\");\n        userTaskDefinition.getContext().add(new SContextEntryImpl(\"key1\", e1));\n        SExpressionImpl e2 = createExpression(\"e2\");\n        userTaskDefinition.getContext().add(new SContextEntryImpl(\"key2\", e2));\n        doReturn(Arrays.asList(\"e1\", \"e2\")).when(expressionResolverService).evaluate(eq(Arrays.asList(e1, e2)),\n                any(SExpressionContext.class));\n\n        Map<String, Serializable> userTaskExecutionContext = processAPI\n                .getUserTaskExecutionContext(FLOW_NODE_INSTANCE_ID);\n\n        assertThat(userTaskExecutionContext).containsOnly(entry(\"key1\", \"e1\"), entry(\"key2\", \"e2\"));\n    }\n\n    @Test\n    public void should_getProcessInstanceExecutionContext_evaluate_context_of_process() throws Exception {\n        SExpressionImpl e1 = createExpression(\"e1\");\n        processDefinition.getContext().add(new SContextEntryImpl(\"key1\", e1));\n        SExpressionImpl e2 = createExpression(\"e2\");\n        processDefinition.getContext().add(new SContextEntryImpl(\"key2\", e2));\n        doReturn(Arrays.asList(\"e1\", \"e2\")).when(expressionResolverService).evaluate(eq(Arrays.asList(e1, e2)),\n                any(SExpressionContext.class));\n\n        Map<String, Serializable> userTaskExecutionContext = processAPI\n                .getProcessInstanceExecutionContext(PROCESS_INSTANCE_ID);\n\n        assertThat(userTaskExecutionContext).containsOnly(entry(\"key1\", \"e1\"), entry(\"key2\", \"e2\"));\n    }\n\n    @Test\n    public void should_getArchivedPExecutionContext_evaluate_context_of_activity() throws Exception {\n        SExpressionImpl e1 = createExpression(\"e1\");\n        userTaskDefinition.getContext().add(new SContextEntryImpl(\"key1\", e1));\n        SExpressionImpl e2 = createExpression(\"e2\");\n        userTaskDefinition.getContext().add(new SContextEntryImpl(\"key2\", e2));\n        doReturn(Arrays.asList(\"e1\", \"e2\")).when(expressionResolverService).evaluate(eq(Arrays.asList(e1, e2)),\n                any(SExpressionContext.class));\n\n        Map<String, Serializable> userTaskExecutionContext = processAPI\n                .getArchivedUserTaskExecutionContext(ARCHIVED_FLOW_NODE_INSTANCE_ID);\n\n        assertThat(userTaskExecutionContext).containsOnly(entry(\"key1\", \"e1\"), entry(\"key2\", \"e2\"));\n    }\n\n    @Test\n    public void should_getArchivedProcessInstanceExecutionContext_evaluate_context_of_process() throws Exception {\n        SExpressionImpl e1 = createExpression(\"e1\");\n        processDefinition.getContext().add(new SContextEntryImpl(\"key1\", e1));\n        SExpressionImpl e2 = createExpression(\"e2\");\n        processDefinition.getContext().add(new SContextEntryImpl(\"key2\", e2));\n        doReturn(Arrays.asList(\"e1\", \"e2\")).when(expressionResolverService).evaluate(eq(Arrays.asList(e1, e2)),\n                any(SExpressionContext.class));\n\n        Map<String, Serializable> userTaskExecutionContext = processAPI\n                .getArchivedProcessInstanceExecutionContext(ARCHIVED_PROCESS_INSTANCE_ID);\n\n        assertThat(userTaskExecutionContext).containsOnly(entry(\"key1\", \"e1\"), entry(\"key2\", \"e2\"));\n    }\n\n    SExpressionImpl createExpression(String name) {\n        SExpressionImpl sExpression = new SExpressionImpl();\n        sExpression.setName(name);\n        return sExpression;\n    }\n\n    @Test\n    public void getDesignProcessDefinition_Should_Return_Design() throws Exception {\n        //given\n        int processDefinitionId = 123;\n        doReturn(processDefinitionService).when(serviceAccessor).getProcessDefinitionService();\n        DesignProcessDefinition designProcessDefinition = mock(DesignProcessDefinition.class);\n        when(processDefinitionService.getDesignProcessDefinition(processDefinitionId))\n                .thenReturn(designProcessDefinition);\n        //when\n        DesignProcessDefinition designProcessDefinitionResult = processAPI\n                .getDesignProcessDefinition(processDefinitionId);\n        //then\n        assertThat(designProcessDefinitionResult).isSameAs(designProcessDefinition);\n        verify(processDefinitionService).getDesignProcessDefinition(processDefinitionId);\n    }\n\n    @Test(expected = ProcessDefinitionNotFoundException.class)\n    public void getDesignProcessDefinition_Should_ThrowException_When_No_ProcessDefinition_Exists() throws Exception {\n        int processDefinitionId = 123;\n        doReturn(processDefinitionService).when(serviceAccessor).getProcessDefinitionService();\n        when(processDefinitionService.getDesignProcessDefinition(processDefinitionId)).thenThrow(\n                new SProcessDefinitionNotFoundException(\"impossible to found given process definition\"));\n        processAPI.getDesignProcessDefinition(processDefinitionId);\n    }\n\n    @Test\n    public void executeUserTask_should_register_contract_data_and_trigger_work() throws Exception {\n        //given\n        Map<String, Serializable> inputValues = new HashMap<>();\n        inputValues.put(\"input1\", 456);\n        inputValues.put(\"input2\", \"value\");\n        sUserTaskInstance.setStateId(FlowNodeState.ID_ACTIVITY_READY);\n        sUserTaskInstance.setAssigneeId(543L);\n        sUserTaskInstance.setExecutedBySubstitute(15);\n        WorkDescriptor workDescriptor = WorkDescriptor.create(\"flownode\");\n        doReturn(workDescriptor).when(workFactory).createExecuteFlowNodeWorkDescriptor(sUserTaskInstance);\n        //when\n        processAPI.executeUserTask(FLOW_NODE_INSTANCE_ID, inputValues);\n        //then\n        verify(contractDataService).addUserTaskData(1674, inputValues);\n        verify(workService).registerWork(workDescriptor);\n    }\n\n    @Test\n    public void executeUserTask_should_throw_exception_if_user_task_not_in_ready() throws Exception {\n        //given\n        sUserTaskInstance.setStateId(FlowNodeState.ID_ACTIVITY_EXECUTING);\n        sUserTaskInstance.setStateName(\"executing\");\n        sUserTaskInstance.setAssigneeId(543L);\n        //when\n        expectedEx.expect(UserTaskNotFoundException.class);\n        expectedEx.expectMessage(\n                \"User task is not executable (currently in state 'executing'), this might be because someone else already executed it.\");\n        processAPI.executeUserTask(FLOW_NODE_INSTANCE_ID, Collections.emptyMap());\n        //then exception\n    }\n\n    @Test\n    public void should_verify_if_the_task_was_in_fact_in_a_wrong_state_when_executing_it() throws Exception {\n        //with some concurrency, we might have some commit exceptions when executing a task.\n        // in that case if there is a commit exception we then open an other transaction to verify if the state of the task was ok\n        sUserTaskInstance.setStateId(FlowNodeState.ID_ACTIVITY_READY);\n        sUserTaskInstance.setStateName(\"ready\");\n        sUserTaskInstance.setStateExecuting(true);\n        sUserTaskInstance.setAssigneeId(543L);\n        userTransactionService.failFirstTx();\n        //when\n        expectedEx.expect(UserTaskNotFoundException.class);\n        expectedEx.expectMessage(\n                \"User task is not executable (currently in state 'executing ready'), this might be because someone else already executed it.\");\n        processAPI.executeUserTask(FLOW_NODE_INSTANCE_ID, Collections.emptyMap());\n    }\n\n    @Test\n    public void executeUserTask_should_throw_exception_if_user_task_is_ready_but_executing() throws Exception {\n        //given\n        sUserTaskInstance.setStateId(FlowNodeState.ID_ACTIVITY_READY);\n        sUserTaskInstance.setStateName(\"ready\");\n        sUserTaskInstance.setStateExecuting(true);\n        sUserTaskInstance.setAssigneeId(543L);\n        //when\n        expectedEx.expect(UserTaskNotFoundException.class);\n        expectedEx.expectMessage(\n                \"User task is not executable (currently in state 'executing ready'), this might be because someone else already executed it.\");\n        processAPI.executeUserTask(FLOW_NODE_INSTANCE_ID, Collections.emptyMap());\n        //then exception\n    }\n\n    @Test\n    public void should_not_be_able_to_update_mapping_when_task_is_not_ready() throws Exception {\n        //given)\n        sUserTaskInstance.setStateId(FlowNodeState.ID_ACTIVITY_FAILED);\n        //when\n        expectedEx.expect(UpdateException.class);\n        expectedEx.expectMessage(\"Unable to update actors of the task 1674 because it is not in ready state\");\n        processAPI.updateActorsOfUserTask(FLOW_NODE_INSTANCE_ID);\n    }\n\n    @Test\n    public void should_not_be_able_to_update_mapping_when_task_is_ready_but_with_executing_flag() throws Exception {\n        //given\n        sUserTaskInstance.setStateId(FlowNodeState.ID_ACTIVITY_READY);\n        sUserTaskInstance.setStateExecuting(true);\n        //when\n        expectedEx.expect(UpdateException.class);\n        expectedEx.expectMessage(\"Unable to update actors of the task 1674 because it is not in ready state\");\n        processAPI.updateActorsOfUserTask(FLOW_NODE_INSTANCE_ID);\n    }\n\n    @Test\n    public void should_update_mapping_when_task_is_ready() throws Exception {\n        //given\n        sUserTaskInstance.setStateId(FlowNodeState.ID_ACTIVITY_READY);\n        sUserTaskInstance.setStateExecuting(false);\n        SUserFilterDefinitionImpl sUserFilterDefinition = new SUserFilterDefinitionImpl(\n                new UserFilterDefinitionImpl(\"myUserFilter\", \"def\", \"version\"));\n        userTaskDefinition.setUserFilter(sUserFilterDefinition);\n        doReturn(new FilterResultImpl(Arrays.asList(4L, 5L), true)).when(userFilterService).executeFilter(anyLong(),\n                eq(sUserFilterDefinition),\n                anyMap(),\n                nullable(ClassLoader.class),\n                nullable(SExpressionContext.class), nullable(String.class));\n        //when\n        processAPI.updateActorsOfUserTask(FLOW_NODE_INSTANCE_ID);\n        //then\n        verify(activityInstanceService).deletePendingMappings(FLOW_NODE_INSTANCE_ID);\n        verify(activityInstanceService, times(2)).addPendingActivityMappings(pendingMappingArgumentCaptor.capture());\n        assertThat(pendingMappingArgumentCaptor.getAllValues()).hasSize(2).extracting(\"activityId\", \"userId\")\n                .containsOnly(tuple(FLOW_NODE_INSTANCE_ID, 4L),\n                        tuple(FLOW_NODE_INSTANCE_ID, 5L));\n    }\n\n    @Test\n    public void should_update_mapping_auto_assign_if_flag_is_set() throws Exception {\n        //given\n        sUserTaskInstance.setStateId(FlowNodeState.ID_ACTIVITY_READY);\n        sUserTaskInstance.setStateExecuting(false);\n        SUserFilterDefinitionImpl sUserFilterDefinition = new SUserFilterDefinitionImpl(\n                new UserFilterDefinitionImpl(\"myUserFilter\", \"def\", \"version\"));\n        userTaskDefinition.setUserFilter(sUserFilterDefinition);\n        doReturn(new FilterResultImpl(singletonList(4L), true)).when(userFilterService).executeFilter(\n                anyLong(), eq(sUserFilterDefinition),\n                anyMap(),\n                nullable(ClassLoader.class),\n                nullable(SExpressionContext.class), nullable(String.class));\n        //when\n        processAPI.updateActorsOfUserTask(FLOW_NODE_INSTANCE_ID);\n        //then\n        verify(activityInstanceService).assignHumanTask(FLOW_NODE_INSTANCE_ID, 4L);\n    }\n\n    @Test\n    public void should_update_mapping_not_auto_assign_if_flag_is_not_set() throws Exception {\n        //given\n        sUserTaskInstance.setStateId(FlowNodeState.ID_ACTIVITY_READY);\n        sUserTaskInstance.setStateExecuting(false);\n        SUserFilterDefinitionImpl sUserFilterDefinition = new SUserFilterDefinitionImpl(\n                new UserFilterDefinitionImpl(\"myUserFilter\", \"def\", \"version\"));\n        userTaskDefinition.setUserFilter(sUserFilterDefinition);\n        doReturn(new FilterResultImpl(singletonList(4L), false)).when(userFilterService).executeFilter(\n                anyLong(), eq(sUserFilterDefinition),\n                anyMap(),\n                nullable(ClassLoader.class),\n                nullable(SExpressionContext.class), nullable(String.class));\n        //when\n        processAPI.updateActorsOfUserTask(FLOW_NODE_INSTANCE_ID);\n        //then\n        verify(activityInstanceService, never()).assignHumanTask(eq(FLOW_NODE_INSTANCE_ID), anyLong());\n    }\n\n    @Test\n    public void should_throw_Illegal_Arg_Exception_when_setState_with_unknown_state() throws UpdateException {\n\n        expectedException.expect(IllegalArgumentException.class);\n        processAPI.setActivityStateByName(25L, \"garbage\");\n    }\n\n    @Test\n    public void executeMessageCouple_should_reset_couple() throws Exception {\n        // given:\n        final long messageInstanceId = 123L;\n        final long waitingMessageId = 999L;\n\n        // when:\n        processAPI.executeMessageCouple(messageInstanceId, waitingMessageId);\n\n        // then:\n        verify(messageHandlingService).resetMessageCouple(messageInstanceId, waitingMessageId);\n    }\n\n    @Test\n    public void executeMessageCouple_should_execute_couple_on_messageHandlingService() throws Exception {\n        // given:\n        final long messageInstanceId = 456L;\n        final long waitingMessageId = 888L;\n\n        // when:\n        processAPI.executeMessageCouple(messageInstanceId, waitingMessageId);\n\n        // then:\n        verify(messageHandlingService).triggerMatchingOfMessages();\n    }\n\n    @Test\n    public void executeMessageCouple_should_throw_ExecutionException_with_details_in_case_of_error() throws Exception {\n        // given:\n        final long messageInstanceId = 456L;\n        final long waitingMessageId = 888L;\n        doThrow(STransactionNotFoundException.class).when(messageHandlingService).triggerMatchingOfMessages();\n\n        // then:\n        expectedException.expect(ExecutionException.class);\n        expectedException.expectMessage(\"messageInstanceId=\" + messageInstanceId);\n        expectedException.expectMessage(\"waitingMessageId=\" + waitingMessageId);\n\n        // when:\n        processAPI.executeMessageCouple(456L, 888L);\n    }\n\n    @Test(expected = ProcessInstanceNotFoundException.class)\n    public void getArchivedProcessInstanceExecutionContext_should_throw_the_correct_exception_when_given_a_missing_process_ID()\n            throws ProcessInstanceNotFoundException, ExpressionEvaluationException {\n        processAPI.getArchivedProcessInstanceExecutionContext(935);\n    }\n\n    @Test(expected = ProcessInstanceNotFoundException.class)\n    public void getProcessInstanceExecutionContext_should_throw_the_correct_exception_when_given_a_missing_process_ID()\n            throws ProcessInstanceNotFoundException, ExpressionEvaluationException {\n        processAPI.getProcessInstanceExecutionContext(935);\n    }\n\n    private static class TestUserTransactionService implements UserTransactionService {\n\n        private boolean failOnce = false;\n\n        @Override\n        public <T> T executeInTransaction(Callable<T> callable) throws Exception {\n            if (failOnce) {\n                failOnce = false;\n                throw new STransactionCommitException(\"commit exception\");\n            }\n            return callable.call();\n        }\n\n        @Override\n        public void registerBonitaSynchronization(Synchronization txSync) {\n        }\n\n        @Override\n        public void registerBeforeCommitCallable(Callable<Void> callable) {\n        }\n\n        @Override\n        public boolean isTransactionActive() {\n            return false;\n        }\n\n        @Override\n        public Optional<Boolean> hasMultipleResources() {\n            return Optional.empty();\n        }\n\n        public void failFirstTx() {\n            failOnce = true;\n        }\n    }\n\n    @Test\n    public void should_get_external_resources_from_process() throws Exception {\n        doReturn(\n                new SBARResource(\"myResource\", BARResourceType.EXTERNAL, PROCESS_DEFINITION_ID, new byte[] { 1, 2, 3 }))\n                .when(processResourcesService)\n                .get(PROCESS_DEFINITION_ID, BARResourceType.EXTERNAL, \"myResource\");\n\n        byte[] myResource = processAPI.getExternalProcessResource(PROCESS_DEFINITION_ID, \"myResource\");\n\n        assertThat(myResource).isEqualTo(new byte[] { 1, 2, 3 });\n    }\n\n    @Test\n    public void should_throw_FileNotFoundException_when_getting_unexisting_external_resource() throws Exception {\n        expectedException.expect(FileNotFoundException.class);\n\n        processAPI.getExternalProcessResource(PROCESS_DEFINITION_ID, \"myResource2\");\n    }\n\n    @Test\n    public void should_throw_RetrieveException_when_cant_read_database() throws Exception {\n        doThrow(new SBonitaReadException(\"\"))\n                .when(processResourcesService)\n                .get(anyLong(), any(), anyString());\n\n        expectedException.expect(RetrieveException.class);\n\n        processAPI.getExternalProcessResource(PROCESS_DEFINITION_ID, \"myResource2\");\n    }\n\n    @Test\n    public void should_get_document_resource_from_process() throws Exception {\n        doReturn(\n                new SBARResource(\"myDoc\", BARResourceType.DOCUMENT, PROCESS_DEFINITION_ID, new byte[] { 4, 5, 6 }))\n                .when(processResourcesService)\n                .get(PROCESS_DEFINITION_ID, BARResourceType.DOCUMENT, \"myDoc\");\n\n        byte[] myDoc = processAPI.getDocumentProcessResource(PROCESS_DEFINITION_ID, \"myDoc\");\n\n        assertThat(myDoc).isEqualTo(new byte[] { 4, 5, 6 });\n    }\n\n    @Test\n    public void should_throw_ProcessResourceNotFoundException_when_getting_nonexistent_document_resource()\n            throws Exception {\n        expectedException.expect(ProcessResourceNotFoundException.class);\n\n        processAPI.getDocumentProcessResource(PROCESS_DEFINITION_ID, \"nonexistent\");\n    }\n\n    @Test\n    public void should_throw_RetrieveException_when_cant_read_database_for_document_resource() throws Exception {\n        doThrow(new SBonitaReadException(\"\"))\n                .when(processResourcesService)\n                .get(PROCESS_DEFINITION_ID, BARResourceType.DOCUMENT, \"myDoc\");\n\n        expectedException.expect(RetrieveException.class);\n\n        processAPI.getDocumentProcessResource(PROCESS_DEFINITION_ID, \"myDoc\");\n    }\n\n    @Test\n    public void should_invoke_deleteMessageAndDataInstanceOlderCreationDate_with_good_fields() throws Exception {\n        long creationDate = System.currentTimeMillis();\n\n        SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 1000);\n        searchOptionsBuilder.filter(\"messageName\", \"test\");\n\n        when(searchEntitiesDescriptor.getSearchMessageInstanceDescriptor())\n                .thenReturn(new SearchMessageInstanceDescriptor());\n\n        when(eventInstanceService.deleteMessageAndDataInstanceOlderThanCreationDate(anyLong(), any(QueryOptions.class)))\n                .thenReturn(2);\n        int numberMessageDeleted = processAPI.deleteMessageByCreationDate(creationDate, searchOptionsBuilder.done());\n\n        assertThat(numberMessageDeleted).isEqualTo(2);\n        verify(eventInstanceService).deleteMessageAndDataInstanceOlderThanCreationDate(eq(creationDate),\n                deleteOldMessageArgumentCaptor.capture());\n        assertThat(deleteOldMessageArgumentCaptor.getValue().getFromIndex()).isEqualTo(0);\n        assertThat(deleteOldMessageArgumentCaptor.getValue().getNumberOfResults()).isEqualTo(1000);\n        assertThat(deleteOldMessageArgumentCaptor.getValue().getFilters())\n                .containsExactly(new FilterOption(SMessageInstance.class, \"messageName\", \"test\"));\n    }\n\n    @Test\n    public void should_throw_ExecutionException_when_wrong_fields_used() throws Exception {\n\n        when(searchEntitiesDescriptor.getSearchMessageInstanceDescriptor())\n                .thenReturn(new SearchMessageInstanceDescriptor());\n        expectedException.expect(IllegalArgumentException.class);\n        expectedException.expectMessage(\n                \"the field 'test' is unknown for the entity searched using SearchMessageInstanceDescriptor\");\n\n        SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10000);\n        searchOptionsBuilder.filter(\"test\", \"test\");\n\n        processAPI.deleteMessageByCreationDate(1000L, searchOptionsBuilder.done());\n    }\n\n    @Test\n    public void should_throw_ExecutionException_when_service_throw_an_exception() throws Exception {\n        doThrow(new SMessageModificationException(\"\"))\n                .when(eventInstanceService)\n                .deleteMessageAndDataInstanceOlderThanCreationDate(anyLong(), any(QueryOptions.class));\n        when(searchEntitiesDescriptor.getSearchMessageInstanceDescriptor())\n                .thenReturn(new SearchMessageInstanceDescriptor());\n        expectedException.expect(ExecutionException.class);\n\n        processAPI.deleteMessageByCreationDate(1000L, new SearchOptionsImpl(0, 1000));\n    }\n\n    @Test\n    public void should_disconnect_connector_execution_when_output_operation_throw_an_exception() throws Exception {\n        // Given\n        doThrow(SOperationExecutionException.class).when(operationService).execute(anyList(), anyLong(),\n                nullable(String.class), any(SExpressionContext.class));\n        ConnectorResult connectorResult = new ConnectorResult(null, new HashMap<>(), 0);\n\n        // When\n        assertThatThrownBy(() -> processAPI.executeOperations(connectorResult, new ArrayList<>(),\n                new HashMap<>(), new SExpressionContext(), ProcessAPIImplTest.class.getClassLoader(), serviceAccessor))\n                .isInstanceOf(SOperationExecutionException.class);\n\n        // Then\n        verify(connectorService).disconnect(connectorResult);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/ProcessDeploymentAPIDelegateTest.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport java.io.InputStream;\n\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveFactory;\nimport org.bonitasoft.engine.bpm.process.ProcessDeployException;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class ProcessDeploymentAPIDelegateTest {\n\n    @Spy\n    ProcessDeploymentAPIDelegate processDeploymentAPIDelegate;\n\n    @Test\n    public void validateBusinessArchive_should_throw_exception_if_empty_file_detected() throws Exception {\n        try (final InputStream resourceAsStream = this.getClass().getResourceAsStream(\"EmptyDocument--1.0.bar\")) {\n            final BusinessArchive businessArchive = BusinessArchiveFactory.readBusinessArchive(resourceAsStream);\n\n            assertThatThrownBy(() -> processDeploymentAPIDelegate.validateBusinessArchive(businessArchive))\n                    .isInstanceOf(ProcessDeployException.class).hasMessage(\n                            \"The BAR file you are trying to deploy contains an empty file: resources/forms/resources/emptyDocument2.pdf. The process cannot be deployed. Fix it or remove it from the BAR.\");\n        }\n    }\n\n    @Test\n    public void deploy_should_not_call_service_deploy_if_bar_validation_failed() throws Exception {\n        // given:\n        doThrow(ProcessDeployException.class).when(processDeploymentAPIDelegate)\n                .validateBusinessArchive(any(BusinessArchive.class));\n        final BusinessArchive businessArchive = mock(BusinessArchive.class);\n\n        // when:\n        assertThatThrownBy(() -> processDeploymentAPIDelegate.deploy(businessArchive))\n                .isInstanceOf(ProcessDeployException.class);\n\n        // then:\n        verifyNoMoreInteractions(businessArchive);\n        verify(processDeploymentAPIDelegate, times(0)).getServiceAccessor();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/ProcessInvolvementDelegateTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.doThrow;\n\nimport java.util.Arrays;\n\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ProcessInvolvementDelegateTest {\n\n    private static final long PROCESS_INSTANCE_ID = 987564L;\n    public static final long PROCESS_INITIATOR_USER_ID = 55L;\n    public static final long WRONG_USER_ID = -2l;\n\n    @Mock\n    SProcessInstance sProcessInstance;\n\n    @Mock\n    ProcessInstanceService processInstanceService;\n\n    @Mock\n    SessionService sessionService;\n\n    @Mock\n    SessionAccessor sessionAccessor;\n\n    @Mock\n    ActivityInstanceService activityInstanceService;\n\n    @Mock\n    SAProcessInstance saProcessInstance;\n\n    @Mock\n    ServiceAccessor serviceAccessor;\n\n    @Spy\n    @InjectMocks\n    public ProcessInvolvementDelegate processInvolvementDelegate;\n\n    @Before\n    public void before() {\n        doReturn(serviceAccessor).when(processInvolvementDelegate).getServiceAccessor();\n        doReturn(processInstanceService).when(serviceAccessor).getProcessInstanceService();\n    }\n\n    @Test\n    public final void should_isProcessOrArchivedProcessInitiator_return_false_if_user_do_not_exists() throws Exception {\n        //given\n        doReturn(sProcessInstance).when(processInstanceService).getProcessInstance(PROCESS_INSTANCE_ID);\n        doReturn(PROCESS_INITIATOR_USER_ID).when(sProcessInstance).getStartedBy();\n\n        // When\n        boolean involvedInProcessInstance = processInvolvementDelegate\n                .isProcessOrArchivedProcessInitiator(WRONG_USER_ID, PROCESS_INSTANCE_ID);\n\n        //then\n        assertThat(involvedInProcessInstance).as(\"user should not be involved involved in human task\").isFalse();\n    }\n\n    @Test\n    public final void should_isProcessOrArchivedProcessInitiator_return_true_if_user_started_process()\n            throws Exception {\n        //given\n        doReturn(sProcessInstance).when(processInstanceService).getProcessInstance(PROCESS_INSTANCE_ID);\n        doReturn(PROCESS_INITIATOR_USER_ID).when(sProcessInstance).getStartedBy();\n\n        // When\n        boolean involvedInProcessInstance = processInvolvementDelegate\n                .isProcessOrArchivedProcessInitiator(PROCESS_INITIATOR_USER_ID, PROCESS_INSTANCE_ID);\n\n        //then\n        assertThat(involvedInProcessInstance).as(\"user should not be involved involved in human task\").isTrue();\n    }\n\n    @Test\n    public final void should_isProcessOrArchivedProcessInitiator_return_true_when_process_is_archived()\n            throws Exception {\n        //given\n        doThrow(SProcessInstanceNotFoundException.class).when(processInstanceService)\n                .getProcessInstance(PROCESS_INSTANCE_ID);\n        doReturn(Arrays.asList(saProcessInstance)).when(processInstanceService)\n                .searchArchivedProcessInstances(any(QueryOptions.class));\n        doReturn(PROCESS_INITIATOR_USER_ID).when(saProcessInstance).getStartedBy();\n        // When\n        boolean involvedInProcessInstance = processInvolvementDelegate\n                .isProcessOrArchivedProcessInitiator(PROCESS_INITIATOR_USER_ID, PROCESS_INSTANCE_ID);\n\n        //then\n        assertThat(involvedInProcessInstance).as(\"should be involved in archived case\").isTrue();\n    }\n\n    @Test\n    public final void should_isProcessOrArchivedProcessInitiator_return_false_when_process_is_archived()\n            throws Exception {\n        //given\n        doThrow(SProcessInstanceNotFoundException.class).when(processInstanceService)\n                .getProcessInstance(PROCESS_INSTANCE_ID);\n        doReturn(Arrays.asList(saProcessInstance)).when(processInstanceService)\n                .searchArchivedProcessInstances(any(QueryOptions.class));\n        doReturn(PROCESS_INITIATOR_USER_ID).when(saProcessInstance).getStartedBy();\n        // When\n        boolean involvedInProcessInstance = processInvolvementDelegate\n                .isProcessOrArchivedProcessInitiator(WRONG_USER_ID, PROCESS_INSTANCE_ID);\n\n        //then\n        assertThat(involvedInProcessInstance).as(\"should be involved in archived case\").isFalse();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/ProcessManagementAPIImplDelegateTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier;\nimport static org.bonitasoft.engine.dependency.model.ScopeType.PROCESS;\nimport static org.mockito.Mockito.*;\n\nimport org.bonitasoft.engine.bpm.process.ActivationState;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.exception.RetrieveException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ProcessManagementAPIImplDelegateTest {\n\n    @Mock\n    private ProcessDefinitionService processDefinitionService;\n\n    @Mock\n    private ProcessInstanceService processInstanceService;\n\n    @Mock\n    private ServiceAccessor serviceAccessor;\n\n    @Spy\n    private ProcessManagementAPIImplDelegate delegate;\n\n    @Mock\n    private SProcessDefinitionDeployInfo processInfo;\n\n    @Mock\n    private ClassLoaderService classLoaderService;\n\n    @Before\n    public void setUp() {\n        doReturn(serviceAccessor).when(delegate).getServiceAccessor();\n        when(serviceAccessor.getProcessDefinitionService()).thenReturn(processDefinitionService);\n        when(serviceAccessor.getProcessInstanceService()).thenReturn(processInstanceService);\n        when(serviceAccessor.getClassLoaderService()).thenReturn(classLoaderService);\n    }\n\n    @Test(expected = ProcessDefinitionNotFoundException.class)\n    public void purgeClassLoader_should_throw_an_exception_if_process_definition_does_not_exist() throws Exception {\n        when(processDefinitionService.getProcessDeploymentInfo(45L))\n                .thenThrow(new SProcessDefinitionNotFoundException(\"proc\"));\n\n        delegate.purgeClassLoader(45L);\n    }\n\n    @Test(expected = UpdateException.class)\n    public void purgeClassLoader_should_throw_an_exception_if_process_definition_is_not_disable() throws Exception {\n        when(processDefinitionService.getProcessDeploymentInfo(45L)).thenReturn(processInfo);\n        when(processInfo.getActivationState()).thenReturn(ActivationState.ENABLED.toString());\n\n        delegate.purgeClassLoader(45L);\n    }\n\n    @Test(expected = UpdateException.class)\n    public void purgeClassLoader_should_throw_an_exception_if_process_instances_are_still_running() throws Exception {\n        when(processDefinitionService.getProcessDeploymentInfo(45L)).thenReturn(processInfo);\n        when(processInfo.getActivationState()).thenReturn(ActivationState.DISABLED.toString());\n        when(processInstanceService.getNumberOfProcessInstances(45L)).thenReturn(3L);\n\n        delegate.purgeClassLoader(45L);\n    }\n\n    @Test(expected = RetrieveException.class)\n    public void purgeClassLoader_should_throw_an_exception_if_a_read_exception_occurs_when_retrieving_process_info()\n            throws Exception {\n        when(processDefinitionService.getProcessDeploymentInfo(45L)).thenThrow(new SBonitaReadException(\"error\"));\n\n        delegate.purgeClassLoader(45L);\n    }\n\n    @Test(expected = RetrieveException.class)\n    public void purgeClassLoader_should_throw_an_exception_if_a_read_exception_occurs_when_retrieving_the_number_of_running_process_instances()\n            throws Exception {\n        when(processDefinitionService.getProcessDeploymentInfo(45L)).thenReturn(processInfo);\n        when(processInfo.getActivationState()).thenReturn(ActivationState.DISABLED.toString());\n        when(processInstanceService.getNumberOfProcessInstances(45L)).thenThrow(new SBonitaReadException(\"error\"));\n\n        delegate.purgeClassLoader(45L);\n    }\n\n    @Test\n    public void purgeClassLoader_should_call_the_classLoader_service() throws Exception {\n        final long processDefinitionId = 45L;\n        when(processDefinitionService.getProcessDeploymentInfo(processDefinitionId)).thenReturn(processInfo);\n        when(processInfo.getActivationState()).thenReturn(ActivationState.DISABLED.toString());\n        when(processInstanceService.getNumberOfProcessInstances(processDefinitionId)).thenReturn(0L);\n\n        delegate.purgeClassLoader(processDefinitionId);\n\n        verify(classLoaderService).removeLocalClassloader(identifier(PROCESS, processDefinitionId));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/ProfileAPIImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.*;\n\nimport org.bonitasoft.engine.api.impl.profile.ProfileAPIDelegate;\nimport org.bonitasoft.engine.profile.ProfileCriterion;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ProfileAPIImplTest {\n\n    private static final long USER_ID = 1L;\n\n    @Mock\n    ProfileAPIDelegate profileApiDelegate;\n\n    @Spy\n    private ProfileAPIImpl profileAPIImpl;\n\n    @Before\n    public void before() throws Exception {\n        doReturn(profileApiDelegate).when(profileAPIImpl).getProfileAPIDelegate();\n    }\n\n    @Test\n    public void should_invoke_getProfilesForUser_from_delegate() {\n        // when:\n        profileAPIImpl.getProfilesForUser(-1, 0, 10, ProfileCriterion.ID_ASC);\n\n        // then:\n        verify(profileApiDelegate, times(1)).getProfilesForUser(-1, 0, 10, ProfileCriterion.ID_ASC);\n    }\n\n    @Test\n    public void should_invoke_deleteProfileMember_from_delegate() throws Exception {\n        // when\n        profileAPIImpl.deleteProfileMember(1L);\n\n        // then\n        verify(profileApiDelegate, times(1)).deleteProfileMember(1L);\n    }\n\n    @Test\n    public void should_invoke_createProfileMember_from_delegate() throws Exception {\n        final Long profileId = 1L;\n        final Long userId = 2L;\n        final Long groupId = 3L;\n        final Long roleId = 4L;\n\n        // when\n        profileAPIImpl.createProfileMember(profileId, userId, groupId, roleId);\n\n        // then\n        verify(profileApiDelegate, times(1)).createProfileMember(profileId, userId, groupId, roleId);\n\n    }\n\n    @Test\n    public void should_invoke_getMemberType_from_delegate() throws Exception {\n        profileAPIImpl.getMemberType(USER_ID, null, null);\n        verify(profileApiDelegate, times(1)).getMemberType(USER_ID, null, null);\n\n    }\n\n    @Test\n    public void should_profile_api_be_available_when_tenant_is_paused() throws Exception {\n        assertThat(ProfileAPIImpl.class.isAnnotationPresent(AvailableInMaintenanceMode.class))\n                .as(\"should profile api be available when tenant is paused\")\n                .isTrue();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/SCustomUserInfoValueAPITest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verify;\n\nimport java.util.Arrays;\nimport java.util.Collections;\n\nimport org.bonitasoft.engine.identity.CustomUserInfoValueUpdater;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoValue;\nimport org.bonitasoft.engine.identity.model.builder.SCustomUserInfoValueUpdateBuilderFactory;\nimport org.bonitasoft.engine.identity.model.builder.impl.SCustomUserInfoValueUpdateBuilderImpl;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Answers;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Vincent Elcrin\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class SCustomUserInfoValueAPITest {\n\n    @Mock(answer = Answers.RETURNS_MOCKS)\n    private SCustomUserInfoValueUpdateBuilderFactory updateFactory;\n\n    @Mock(answer = Answers.RETURNS_MOCKS)\n    private IdentityService service;\n\n    @InjectMocks\n    private SCustomUserInfoValueAPI api;\n\n    @Test\n    public void update_should_call_service_to_update_custom_user_info_when_exist() throws Exception {\n        final SCustomUserInfoValue value = SCustomUserInfoValue.builder().id(1).build();\n        final SCustomUserInfoValueUpdateBuilderImpl builder = new SCustomUserInfoValueUpdateBuilderImpl(\n                new EntityUpdateDescriptor());\n        given(updateFactory.createNewInstance()).willReturn(builder);\n\n        api.update(value, new CustomUserInfoValueUpdater(\"value\"));\n\n        verify(service).updateCustomUserInfoValue(value, builder.done());\n    }\n\n    @Test\n    public void update_should_return_updated_value() throws Exception {\n        given(service.getCustomUserInfoValue(2L)).willReturn(SCustomUserInfoValue.builder().value(\"updated\").build());\n\n        final SCustomUserInfoValue result = api.update(SCustomUserInfoValue.builder().id(2).build(),\n                new CustomUserInfoValueUpdater(\"value\"));\n\n        assertThat(result.getValue()).isEqualTo(\"updated\");\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void update_should_throw_update_exception_when_value_is_null() throws Exception {\n        api.update(null, new CustomUserInfoValueUpdater(\"value\"));\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void update_should_throw_update_exception_when_updater_is_null() throws Exception {\n        api.update(new SCustomUserInfoValue(), null);\n    }\n\n    @Test\n    public void set_should_delete_CustomUserInfoValue_when_value_is_null() throws Exception {\n        final SCustomUserInfoValue value = SCustomUserInfoValue.builder().id(254).build();\n        given(service.getCustomUserInfoValueOfUserAndDefinitions(2L, Collections.singletonList(1L)))\n                .willReturn(Arrays.asList(value));\n\n        api.set(1L, 2L, null);\n\n        verify(service).deleteCustomUserInfoValue(value);\n    }\n\n    @Test\n    public void set_should_delete_SCustomUserInfoValue_when_value_is_empty() throws Exception {\n        final SCustomUserInfoValue value = SCustomUserInfoValue.builder().id(254).build();\n        given(service.getCustomUserInfoValueOfUserAndDefinitions(2L, Collections.singletonList(1L)))\n                .willReturn(Arrays.asList(value));\n\n        api.set(1L, 2L, \"\");\n\n        verify(service).deleteCustomUserInfoValue(value);\n    }\n\n    @Test\n    public void set_should_return_updated_value_when_value_is_deleted() throws Exception {\n        final SCustomUserInfoValue value = api.set(1L, 2L, \"\");\n\n        assertThat(value.getDefinitionId()).isEqualTo(1L);\n        assertThat(value.getUserId()).isEqualTo(2L);\n        assertThat(value.getValue()).isEqualTo(\"\");\n    }\n\n    @Test\n    public void set_should_update_SCustomUserInfoValue_when_value_exist() throws Exception {\n        final SCustomUserInfoValue value = SCustomUserInfoValue.builder().id(254).build();\n        given(service.getCustomUserInfoValueOfUserAndDefinitions(2L, Collections.singletonList(1L)))\n                .willReturn(Arrays.asList(value));\n\n        api.set(1L, 2L, \"update\");\n\n        verify(service).updateCustomUserInfoValue(eq(value), any(EntityUpdateDescriptor.class));\n    }\n\n    @Test\n    public void set_should_create_SCustomUserInfoValue_when_value_doesnt_exist() throws Exception {\n\n        api.set(5L, 3L, \"update\");\n\n        verify(service).createCustomUserInfoValue(any(SCustomUserInfoValue.class));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/ServerAPIFactoryTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\nimport static org.mockito.Mockito.*;\n\nimport org.bonitasoft.engine.api.internal.ServerAPI;\nimport org.bonitasoft.engine.home.BonitaHomeServer;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ServerAPIFactoryTest {\n\n    @Mock\n    private BonitaHomeServer bonitaHomeServer;\n\n    @InjectMocks\n    private ServerAPIFactory serverAPIFactory;\n\n    @Test\n    public void getServerAPIImplemShouldReturnTestImplemOfServerAPI() throws Exception {\n        // given:\n        when(bonitaHomeServer.getServerAPIImplementation())\n                .thenReturn(\"org.bonitasoft.engine.api.impl.TestImplemOfServerAPI\");\n        // when:\n        ServerAPI serverAPIimplementation = serverAPIFactory.getServerAPIImplementation();\n        // then:\n        assertNotNull(serverAPIimplementation);\n        assertEquals(TestImplemOfServerAPI.class, serverAPIimplementation.getClass());\n\n    }\n\n    @Test\n    public void should_cache_the_serverapi_class() throws Exception {\n        //given\n        when(bonitaHomeServer.getServerAPIImplementation())\n                .thenReturn(\"org.bonitasoft.engine.api.impl.TestImplemOfServerAPI\");\n        serverAPIFactory.getServerAPIImplementation();\n        //when\n        serverAPIFactory.getServerAPIImplementation();\n\n        //then\n        verify(bonitaHomeServer, times(1)).getServerAPIImplementation();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/ServerAPIImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport static java.util.Collections.emptyList;\nimport static java.util.Collections.emptyMap;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.hamcrest.CoreMatchers.instanceOf;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport java.io.IOException;\nimport java.io.Serializable;\nimport java.lang.reflect.Method;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.Callable;\n\nimport org.bonitasoft.engine.api.MaintenanceAPI;\nimport org.bonitasoft.engine.api.NoSessionRequired;\nimport org.bonitasoft.engine.api.PlatformAPI;\nimport org.bonitasoft.engine.api.impl.transaction.CustomTransactions;\nimport org.bonitasoft.engine.api.internal.ServerWrappedException;\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.core.login.LoginService;\nimport org.bonitasoft.engine.core.platform.login.PlatformLoginService;\nimport org.bonitasoft.engine.exception.APIImplementationNotFoundException;\nimport org.bonitasoft.engine.exception.BonitaHomeConfigurationException;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\nimport org.bonitasoft.engine.exception.TenantStatusException;\nimport org.bonitasoft.engine.maintenance.MaintenanceDetails;\nimport org.bonitasoft.engine.platform.PlatformManager;\nimport org.bonitasoft.engine.platform.PlatformState;\nimport org.bonitasoft.engine.platform.session.PlatformSessionService;\nimport org.bonitasoft.engine.scheduler.SchedulerService;\nimport org.bonitasoft.engine.service.APIAccessResolver;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.impl.ServiceAccessorFactory;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.bonitasoft.engine.session.PlatformSession;\nimport org.bonitasoft.engine.session.Session;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.session.impl.APISessionImpl;\nimport org.bonitasoft.engine.session.impl.PlatformSessionImpl;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.hamcrest.CustomTypeSafeMatcher;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.SystemOutRule;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Celine Souchet\n * @author Aurelien Pupier\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class ServerAPIImplTest {\n\n    private static final long TENANT_SESSION_ID = 54335453241L;\n    private static final long PLATFORM_SESSION_ID = 54335453241L;\n    private final APISession tenantSession = new APISessionImpl(TENANT_SESSION_ID, new Date(), 10000, \"john\", 14L);\n    private final PlatformSession platformSession = new PlatformSessionImpl(PLATFORM_SESSION_ID, new Date(), 10000,\n            \"john\",\n            14L);\n    @Mock\n    private SessionAccessor sessionAccessor;\n    @Mock\n    private UserTransactionService userTransactionService;\n    @Mock\n    private ServiceAccessorFactory serviceAccessorFactory;\n    @Mock\n    private ServiceAccessor serviceAccessor;\n    @Mock\n    private LoginService tenantLoginService;\n    @Mock\n    private SchedulerService schedulerService;\n    @Mock\n    private SessionService sessionService;\n    @Mock\n    private ClassLoaderService classLoaderService;\n    @Mock\n    private PlatformLoginService platformLoginService;\n    @Mock\n    private PlatformSessionService platformSessionService;\n    @Mock\n    private PlatformManager platformManager;\n\n    @Rule\n    public final SystemOutRule systemOutRule = new SystemOutRule().enableLog();\n\n    private ServerAPIImpl serverAPIImpl;\n    private final MyApiImplementation myApi = new MyApiImplementation();\n    private final MyApiWithNoSessionImpl myApiWithNoSession = new MyApiWithNoSessionImpl();\n    private final ApiFullyAccessibleWhenMaintenanceModeIsEnabledImpl myApiFullyAccessibleWhenMaintenanceModeIsEnabled = new ApiFullyAccessibleWhenMaintenanceModeIsEnabledImpl();\n    private APIAccessResolver accessResolver;\n    @Mock\n    private PlatformAPI platformApi;\n    @Mock\n    private MaintenanceAPI maintenanceAPI;\n    @Mock\n    MaintenanceDetails maintenanceDetails;\n\n    private boolean isMaintenanceEnabled = false;\n\n    @Before\n    public void createServerAPI() throws Exception {\n        doReturn(true).when(platformApi).isNodeStarted();\n        when(userTransactionService.executeInTransaction(any()))\n                .thenAnswer(invocation -> ((Callable<?>) invocation.getArgument(0)).call());\n        doReturn(serviceAccessor).when(serviceAccessorFactory).createServiceAccessor();\n        doReturn(sessionAccessor).when(serviceAccessorFactory).createSessionAccessor();\n        doReturn(schedulerService).when(serviceAccessor).getSchedulerService();\n        doReturn(platformLoginService).when(serviceAccessor).getPlatformLoginService();\n        doReturn(platformSessionService).when(serviceAccessor).getPlatformSessionService();\n        doReturn(platformManager).when(serviceAccessor).getPlatformManager();\n        doReturn(tenantLoginService).when(serviceAccessor).getLoginService();\n        doReturn(sessionService).when(serviceAccessor).getSessionService();\n        doReturn(classLoaderService).when(serviceAccessor).getClassLoaderService();\n\n        doReturn(true).when(tenantLoginService).isValid(TENANT_SESSION_ID);\n        doReturn(true).when(platformLoginService).isValid(PLATFORM_SESSION_ID);\n        doAnswer(invocation -> (isMaintenanceEnabled ? MaintenanceDetails.State.ENABLED\n                : MaintenanceDetails.State.DISABLED))\n                .when(maintenanceDetails).getMaintenanceState();\n        doAnswer(invocation -> maintenanceDetails).when(maintenanceAPI).getMaintenanceDetails();\n\n        accessResolver = new APIAccessResolver() {\n\n            @Override\n            public <T> T getAPIImplementation(Class<T> apiInterface) throws APIImplementationNotFoundException {\n                if (apiInterface.isAssignableFrom(MyApi.class)) {\n                    return (T) myApi;\n                } else if (apiInterface.equals(MyApiWithNoSession.class)) {\n                    return (T) myApiWithNoSession;\n                } else if (apiInterface.equals(ApiFullyAccessibleWhenMaintenanceModeIsEnabled.class)) {\n                    return (T) myApiFullyAccessibleWhenMaintenanceModeIsEnabled;\n                } else if (apiInterface.equals(PlatformAPI.class)) {\n                    return (T) platformApi;\n                } else if (apiInterface.equals(MaintenanceAPI.class)) {\n                    return (T) maintenanceAPI;\n                } else {\n                    throw new APIImplementationNotFoundException(\"not the FakeApi\");\n                }\n\n            }\n        };\n        serverAPIImpl = new ServerAPIImpl(accessResolver) {\n\n            @Override\n            UserTransactionService selectUserTransactionService(Session session, SessionType sessionType)\n                    throws BonitaHomeNotSetException, InstantiationException, IllegalAccessException,\n                    ClassNotFoundException, IOException, BonitaHomeConfigurationException {\n                return userTransactionService;\n            }\n\n            @Override\n            ServiceAccessorFactory getServiceAccessorFactoryInstance() {\n                return serviceAccessorFactory;\n            }\n        };\n    }\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void should_throw_ClassNotFoundException_when_api_is_not_known() throws Throwable {\n\n        expectedException.expect(ServerWrappedException.class);\n        expectedException.expectCause(\n                new CustomTypeSafeMatcher<Throwable>(\"should be a runtime caused by a ClassNotFoundException\") {\n\n                    @Override\n                    protected boolean matchesSafely(Throwable throwable) {\n                        return throwable instanceof BonitaRuntimeException\n                                && throwable.getCause() instanceof ClassNotFoundException;\n                    }\n                });\n\n        serverAPIImpl.invokeMethod(options(tenantSession), \"UnknownApi\", \"someMethod\", emptyList(), null);\n    }\n\n    @Test\n    public void should_not_open_transaction_on_CustomTransaction_annotated_methods() throws Throwable {\n\n        serverAPIImpl.invokeMethod(options(tenantSession), MyApi.class.getName(), \"customTxAPIMethod\", emptyList(),\n                null);\n\n        assertThat(myApi.customTxAPIMethodCalled).isTrue();\n        //only one call: \"maintenanceAPI.getMaintenanceDetails()\"\n        verify(userTransactionService, only()).executeInTransaction(any());\n    }\n\n    @Test\n    public void should_be_able_to_call_methods_that_dont_require_sessions_without_session() throws Throwable {\n\n        serverAPIImpl.invokeMethod(emptyMap(), MyApiWithNoSession.class.getName(), \"someMethod\", emptyList(), null);\n\n        assertThat(myApiWithNoSession.someMethodCalled).isTrue();\n    }\n\n    @Test\n    public void should_call_normal_method_in_transaction() throws Throwable {\n\n        serverAPIImpl.invokeMethod(options(tenantSession), MyApi.class.getName(), \"notAnnotatedMethod\", emptyList(),\n                null);\n\n        assertThat(myApi.notAnnotatedMethodCalled).isTrue();\n        verify(userTransactionService).executeInTransaction(any());\n    }\n\n    @Test\n    public void should_fail_when_calling_normal_method_without_session() throws Throwable {\n\n        expectedException.expectCause(instanceOf(InvalidSessionException.class));\n        serverAPIImpl.invokeMethod(options(null), MyApi.class.getName(), \"notAnnotatedMethod\", emptyList(), null);\n    }\n\n    @Test\n    public void should_not_be_able_to_call_normal_method_when_maintenance_mode_is_enabled() throws Exception {\n        isMaintenanceEnabled = true;\n\n        expectedException.expectCause(instanceOf(TenantStatusException.class));\n        serverAPIImpl.invokeMethod(options(tenantSession), MyApi.class.getName(), \"notAnnotatedMethod\", emptyList(),\n                null);\n    }\n\n    @Test\n    public void should_be_able_to_call_method_with_AvailableInMaintenanceMode_when_maintenance_mode_is_enabled()\n            throws Exception {\n        isMaintenanceEnabled = true;\n\n        serverAPIImpl.invokeMethod(options(tenantSession), MyApi.class.getName(), \"availableInMaintenanceMode\",\n                emptyList(), null);\n\n        assertThat(myApi.availableInMaintenanceModeCalled).isTrue();\n    }\n\n    @Test\n    public void should_be_able_to_call_method_with_AvailableInMaintenanceMode_when_maintenance_mode_is_disabled()\n            throws Exception {\n        isMaintenanceEnabled = false;\n\n        serverAPIImpl.invokeMethod(options(tenantSession), MyApi.class.getName(), \"availableInMaintenanceMode\",\n                emptyList(), null);\n\n        assertThat(myApi.availableInMaintenanceModeCalled).isTrue();\n    }\n\n    @Test\n    public void should_not_be_able_to_call_method_with_OnlyAvailableInMaintenanceMode_when_maintenance_mode_is_disabled()\n            throws Exception {\n        isMaintenanceEnabled = false;\n\n        expectedException.expectCause(instanceOf(TenantStatusException.class));\n\n        serverAPIImpl.invokeMethod(options(tenantSession), MyApi.class.getName(), \"onlyAvailableInMaintenanceMode\",\n                emptyList(), null);\n    }\n\n    @Test\n    public void should_be_able_to_call_method_with_OnlyAvailableInMaintenanceMode_when_maintenance_mode_is_enabled()\n            throws Exception {\n        isMaintenanceEnabled = true;\n\n        serverAPIImpl.invokeMethod(options(tenantSession), MyApi.class.getName(), \"onlyAvailableInMaintenanceMode\",\n                emptyList(), null);\n\n        assertThat(myApi.onlyAvailableInMaintenanceModeCalled).isTrue();\n    }\n\n    @Test\n    public void should_be_able_to_call_method_of_api_fully_available_when_maintenance_mode_is_enabled()\n            throws Exception {\n        isMaintenanceEnabled = true;\n\n        //There is no real check in server API whether it is a tenant or a platform API, we only check the type of session\n        serverAPIImpl.invokeMethod(options(tenantSession),\n                ApiFullyAccessibleWhenMaintenanceModeIsEnabled.class.getName(),\n                \"aMethod\", emptyList(), null);\n\n        assertThat(myApiFullyAccessibleWhenMaintenanceModeIsEnabled.aMethodCalled).isTrue();\n    }\n\n    @Test\n    public void should_be_able_to_call_platform_apis_when_maintenance_mode_is_enabled() throws Exception {\n        isMaintenanceEnabled = true;\n\n        //There is no real check in server API whether it is a tenant or a platform API, we only check the type of session\n        serverAPIImpl.invokeMethod(options(platformSession), MyApi.class.getName(), \"notAnnotatedMethod\", emptyList(),\n                null);\n\n        assertThat(myApi.notAnnotatedMethodCalled).isTrue();\n    }\n\n    @Test\n    public void should_fail_if_tenant_session_is_not_valid() throws Exception {\n        doReturn(false).when(tenantLoginService).isValid(TENANT_SESSION_ID);\n\n        expectedException.expectCause(instanceOf(InvalidSessionException.class));\n        serverAPIImpl.invokeMethod(options(tenantSession), MyApi.class.getName(), \"notAnnotatedMethod\", emptyList(),\n                null);\n    }\n\n    @Test\n    public void should_fail_if_platform_session_is_not_valid() throws Exception {\n        doReturn(false).when(platformLoginService).isValid(PLATFORM_SESSION_ID);\n\n        expectedException.expectCause(instanceOf(InvalidSessionException.class));\n        serverAPIImpl.invokeMethod(options(platformSession), MyApi.class.getName(), \"notAnnotatedMethod\", emptyList(),\n                null);\n    }\n\n    @Test\n    public void should_renew_tenant_session_when_call_is_ok() throws Exception {\n\n        serverAPIImpl.invokeMethod(options(tenantSession), MyApi.class.getName(), \"notAnnotatedMethod\", emptyList(),\n                null);\n\n        verify(sessionService).renewSession(TENANT_SESSION_ID);\n    }\n\n    @Test\n    public void should_renew_platform_session_when_call_is_ok() throws Exception {\n        doReturn(PlatformState.STARTING).when(platformManager).getState();\n        serverAPIImpl.invokeMethod(options(platformSession), MyApi.class.getName(), \"notAnnotatedMethod\", emptyList(),\n                null);\n\n        verify(platformSessionService).renewSession(PLATFORM_SESSION_ID);\n    }\n\n    @Test\n    public void should_checkMethodAccessibility_do_not_warn_user_when_method_is_not_deprecated() throws Throwable {\n        //given\n        final Method callMe = MyApiImplementation.class.getDeclaredMethod(\"callMeNew\");\n        systemOutRule.clearLog();\n        //when\n        serverAPIImpl.checkMethodAccessibility(new MyApiImplementation(), MyApi.class.getName(), callMe, null, true);\n\n        //then\n        assertThat(systemOutRule.getLog()).doesNotContain(\"is deprecated\");\n    }\n\n    @Test\n    public void should_checkMethodAccessibility_warn_user_when_method_is_deprecated() throws Throwable {\n        //given\n        final Method callMe = MyApiImplementation.class.getDeclaredMethod(\"callMeOld\");\n        systemOutRule.clearLog();\n\n        //when\n        serverAPIImpl.checkMethodAccessibility(new MyApiImplementation(), MyApi.class.getName(), callMe, null, true);\n\n        //then\n\n        assertThat(systemOutRule.getLog())\n                .contains(\"The API method \" + this.getClass().getName() + \"$MyApi.callMeOld is deprecated.\");\n    }\n\n    @Test\n    public void should_throw_ServerWrappedException_when_some_exception_is_thrown() throws Throwable {\n        expectedException.expect(ServerWrappedException.class);\n        expectedException.expectCause(instanceOf(BonitaRuntimeException.class));\n\n        serverAPIImpl.invokeMethod(options(tenantSession), MyApi.class.getName(), \"methodThatThrowRuntimeException\",\n                emptyList(), null);\n    }\n\n    private Map<String, Serializable> options(Session session) {\n        final Map<String, Serializable> options = new HashMap<>();\n        options.put(\"session\", session);\n        return options;\n    }\n\n    // ----\n    // APIS for tests\n    // ----\n\n    interface ApiFullyAccessibleWhenMaintenanceModeIsEnabled {\n\n        void aMethod();\n    }\n\n    @AvailableInMaintenanceMode\n    static class ApiFullyAccessibleWhenMaintenanceModeIsEnabledImpl\n            implements ApiFullyAccessibleWhenMaintenanceModeIsEnabled {\n\n        boolean aMethodCalled;\n\n        @Override\n        public void aMethod() {\n            aMethodCalled = true;\n        }\n    }\n\n    @NoSessionRequired\n    interface MyApiWithNoSession {\n\n        void someMethod();\n    }\n\n    static class MyApiWithNoSessionImpl implements MyApiWithNoSession {\n\n        boolean someMethodCalled;\n\n        @Override\n        public void someMethod() {\n            someMethodCalled = true;\n        }\n    }\n\n    interface MyApi {\n\n        @Deprecated\n        @AvailableOnStoppedNode\n        void callMeOld();\n\n        @AvailableOnStoppedNode\n        void callMeNew();\n\n        void availableInMaintenanceMode();\n\n        void onlyAvailableInMaintenanceMode();\n\n        void customTxAPIMethod();\n\n        void notAnnotatedMethod();\n\n        void methodThatThrowRuntimeException();\n    }\n\n    static class MyApiImplementation implements MyApi {\n\n        boolean customTxAPIMethodCalled;\n        boolean notAnnotatedMethodCalled;\n        boolean availableInMaintenanceModeCalled;\n        boolean onlyAvailableInMaintenanceModeCalled;\n\n        @Deprecated\n        @AvailableOnStoppedNode\n        public void callMeOld() {\n\n        }\n\n        @AvailableOnStoppedNode\n        public void callMeNew() {\n\n        }\n\n        @AvailableInMaintenanceMode\n        public void availableInMaintenanceMode() {\n            availableInMaintenanceModeCalled = true;\n        }\n\n        @AvailableInMaintenanceMode(onlyAvailableInMaintenanceMode = true)\n        public void onlyAvailableInMaintenanceMode() {\n            onlyAvailableInMaintenanceModeCalled = true;\n        }\n\n        @CustomTransactions\n        public void customTxAPIMethod() {\n            customTxAPIMethodCalled = true;\n        }\n\n        public void notAnnotatedMethod() {\n            notAnnotatedMethodCalled = true;\n\n        }\n\n        @Override\n        public void methodThatThrowRuntimeException() {\n            throw new BonitaRuntimeException(\"some exception\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/StarterThreadTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Arrays;\nimport java.util.concurrent.Callable;\n\nimport org.bonitasoft.engine.platform.PlatformService;\nimport org.bonitasoft.engine.platform.model.SPlatform;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.bonitasoft.engine.tenant.restart.TenantRestartHandler;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.SystemOutRule;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Baptiste Mesta\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class StarterThreadTest {\n\n    @Rule\n    public SystemOutRule systemOutRule = new SystemOutRule().enableLog();\n\n    private final SPlatform platform = createPlatform();\n    @Mock\n    private UserTransactionService transactionService;\n    @Mock\n    private PlatformService platformService;\n\n    @Mock\n    private TenantRestartHandler tenantRestartHandler1;\n\n    @Mock\n    private TenantRestartHandler tenantRestartHandler2;\n    @Mock\n    private SessionAccessor sessionAccessor;\n    private StarterThread starterThread;\n\n    @Before\n    public void before() throws Exception {\n        doAnswer(invocation -> ((Callable) invocation.getArgument(0)).call()).when(transactionService)\n                .executeInTransaction(any());\n        starterThread = new StarterThread(transactionService,\n                platformService, Arrays.asList(tenantRestartHandler1, tenantRestartHandler2));\n        doReturn(platform).when(platformService).getPlatform();\n    }\n\n    private SPlatform createPlatform() {\n        return new SPlatform(\"10.3\", \"10.3.0\", \"0.0.0\", null, false, \"system\", 123455, false);\n    }\n\n    @Test\n    public void should_call_all_restart_handlers() {\n        //given\n        platform.setMaintenanceEnabled(false);\n        //when\n        starterThread.run();\n        //then\n        verify(tenantRestartHandler1).afterServicesStart();\n        verify(tenantRestartHandler2).afterServicesStart();\n    }\n\n    @Test\n    public void should_not_call_restart_handlers_on_paused_tenant() {\n        //given\n        platform.setMaintenanceEnabled(true);\n        //when\n        starterThread.run();\n        //then\n        verify(tenantRestartHandler1, never()).afterServicesStart();\n        verify(tenantRestartHandler2, never()).afterServicesStart();\n    }\n\n    @Test\n    public void should_call_all_restart_handlers_even_when_one_handler_fails() {\n        //given\n        platform.setMaintenanceEnabled(false);\n        doThrow(new RuntimeException(\"test\")).when(tenantRestartHandler1).afterServicesStart();\n        //when\n        starterThread.run();\n        //then\n        verify(tenantRestartHandler1).afterServicesStart();\n        verify(tenantRestartHandler2).afterServicesStart();\n\n        assertThat(systemOutRule.getLog())\n                .contains(\"The Restart Handler \" + tenantRestartHandler1.getClass().getName() + \" failed\");\n        assertThat(systemOutRule.getLog()).contains(\"java.lang.RuntimeException: test\");\n    }\n\n    @Test\n    public void should_call_all_restart_handlers_even_when_one_handler_fails_with_a_runtime_exception() {\n        //given\n        platform.setMaintenanceEnabled(false);\n        doThrow(new RuntimeException(\"test\", new Exception())).when(tenantRestartHandler1).afterServicesStart();\n        //when\n        starterThread.run();\n        //then\n        verify(tenantRestartHandler1).afterServicesStart();\n        verify(tenantRestartHandler2).afterServicesStart();\n\n        assertThat(systemOutRule.getLog())\n                .contains(\"The Restart Handler \" + tenantRestartHandler1.getClass().getName() + \" failed\");\n        assertThat(systemOutRule.getLog()).contains(\"RuntimeException\", \"test\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/TaskInvolvementDelegateTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.doThrow;\n\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceNotFoundException;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class TaskInvolvementDelegateTest {\n\n    private static final long EXISTING_TASK = 56789465L;\n    private static final long ASSIGNED_TASK = 54789465L;\n    private static final long ASSIGNED_USER = 1234595L;\n    private static final long USER_PENDING = 4545666L;\n    private static final long PROCESS_DEFINITION_ID = 123456789L;\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Mock\n    private ActivityInstanceService activityInstanceService;\n\n    @Mock\n    ServiceAccessor serviceAccessor;\n\n    @Mock\n    public ProcessAPIImpl processAPI;\n\n    @Mock\n    private SUserTaskInstance humanTaskInstance;\n\n    private SUserTaskInstance assignedHumanTaskInstance;\n\n    @InjectMocks\n    @Spy\n    public TaskInvolvementDelegate taskInvolvementDelegate;\n\n    @Before\n    public void before() throws SBonitaException {\n        doReturn(serviceAccessor).when(taskInvolvementDelegate).getServiceAccessor();\n        doReturn(activityInstanceService).when(serviceAccessor).getActivityInstanceService();\n\n        humanTaskInstance = new SUserTaskInstance();\n        humanTaskInstance.setId(EXISTING_TASK);\n        humanTaskInstance.setLogicalGroup(1, PROCESS_DEFINITION_ID);\n\n        assignedHumanTaskInstance = new SUserTaskInstance();\n        assignedHumanTaskInstance.setId(ASSIGNED_TASK);\n        assignedHumanTaskInstance.setLogicalGroup(1, PROCESS_DEFINITION_ID);\n        assignedHumanTaskInstance.setAssigneeId(ASSIGNED_USER);\n\n        doThrow(SActivityInstanceNotFoundException.class).when(activityInstanceService).getHumanTaskInstance(anyLong());\n        doReturn(assignedHumanTaskInstance).when(activityInstanceService).getHumanTaskInstance(ASSIGNED_TASK);\n        doReturn(humanTaskInstance).when(activityInstanceService).getHumanTaskInstance(EXISTING_TASK);\n        doReturn(true).when(activityInstanceService).isTaskPendingForUser(EXISTING_TASK, USER_PENDING);\n    }\n\n    @Test\n    public final void should_isInvolvedInHumanTaskInstance_return_false_if_user_do_not_exists() throws Exception {\n        // When\n        boolean involvedInHumanTaskInstance = taskInvolvementDelegate.isInvolvedInHumanTaskInstance(-2l, EXISTING_TASK);\n\n        //then\n        assertThat(involvedInHumanTaskInstance).as(\"Is involved in human task\").isFalse();\n    }\n\n    @Test\n    public final void should_isInvolvedInHumanTaskInstance_return_true_if_user_assigned() throws Exception {\n        // When\n        boolean involvedInHumanTaskInstance = taskInvolvementDelegate.isInvolvedInHumanTaskInstance(ASSIGNED_USER,\n                ASSIGNED_TASK);\n\n        //then\n        assertThat(involvedInHumanTaskInstance).as(\"Is involved in human task\").isTrue();\n\n    }\n\n    @Test\n    public final void should_isInvolvedInHumanTaskInstance_return_true_if_user_in_actors() throws Exception {\n        //given\n        // When\n        boolean involvedInHumanTaskInstance = taskInvolvementDelegate.isInvolvedInHumanTaskInstance(USER_PENDING,\n                EXISTING_TASK);\n\n        //then\n        assertThat(involvedInHumanTaskInstance).as(\"Is involved in human task\").isTrue();\n    }\n\n    @Test\n    public final void should_isInvolvedInHumanTaskInstance_return_false_if_user_not_the_assignee() throws Exception {\n        // When\n        boolean involvedInHumanTaskInstance = taskInvolvementDelegate.isInvolvedInHumanTaskInstance(USER_PENDING,\n                ASSIGNED_TASK);\n\n        //then\n        assertThat(involvedInHumanTaskInstance).as(\"Is involved in human task\").isFalse();\n    }\n\n    @Test\n    public final void should_isInvolvedInHumanTaskInstance_throw_exception_if_task_do_not_exists() throws Exception {\n        expectedException.expect(ActivityInstanceNotFoundException.class);\n        // When\n        taskInvolvementDelegate.isInvolvedInHumanTaskInstance(ASSIGNED_USER, 45621l);\n    }\n\n    @Test\n    public final void should_hasUserPendingOrAssignedTasks_return_true_when_user_has_tasks() throws Exception {\n        //given\n        doReturn(5L).when(activityInstanceService).getNumberOfPendingOrAssignedTasks(eq(ASSIGNED_USER),\n                any(QueryOptions.class));\n\n        // When\n        final boolean involvedInHumanTaskInstance = taskInvolvementDelegate.hasUserPendingOrAssignedTasks(ASSIGNED_USER,\n                45621L);\n\n        //then\n        assertThat(involvedInHumanTaskInstance).as(\"should return true\").isTrue();\n    }\n\n    @Test\n    public final void should_hasUserPendingOrAssignedTasks_return_false_when_user_has_no_tasks() throws Exception {\n        //given\n        doReturn(0L).when(activityInstanceService).getNumberOfPendingOrAssignedTasks(eq(ASSIGNED_USER),\n                any(QueryOptions.class));\n\n        // When\n        final boolean involvedInHumanTaskInstance = taskInvolvementDelegate.hasUserPendingOrAssignedTasks(ASSIGNED_USER,\n                45621L);\n\n        //then\n        assertThat(involvedInHumanTaskInstance).as(\"should return false\").isFalse();\n    }\n\n    @Test\n    public final void should_hasUserPendingOrAssignedTasks_fail_when_read_exception() throws Exception {\n        //given\n        doThrow(SBonitaReadException.class).when(activityInstanceService)\n                .getNumberOfPendingOrAssignedTasks(eq(ASSIGNED_USER), any(QueryOptions.class));\n\n        //expect\n        expectedException.expect(SExecutionException.class);\n\n        // When\n        taskInvolvementDelegate.hasUserPendingOrAssignedTasks(ASSIGNED_USER, 45621L);\n\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/TenantAdministrationAPIImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.tenant.TenantResourceType.BDM;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport java.lang.reflect.Method;\nimport java.util.concurrent.Callable;\n\nimport org.bonitasoft.engine.api.impl.resolver.BusinessArchiveArtifactsManager;\nimport org.bonitasoft.engine.business.data.BusinessDataModelRepository;\nimport org.bonitasoft.engine.business.data.BusinessDataRepositoryDeploymentException;\nimport org.bonitasoft.engine.business.data.BusinessDataRepositoryException;\nimport org.bonitasoft.engine.business.data.InvalidBusinessDataModelException;\nimport org.bonitasoft.engine.business.data.SBusinessDataRepositoryException;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.resources.STenantResourceLight;\nimport org.bonitasoft.engine.resources.STenantResourceState;\nimport org.bonitasoft.engine.resources.TenantResourceType;\nimport org.bonitasoft.engine.resources.TenantResourcesService;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.tenant.TenantResource;\nimport org.bonitasoft.engine.tenant.TenantStateManager;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.SystemOutRule;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Celine Souchet\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class TenantAdministrationAPIImplTest {\n\n    @Rule\n    public SystemOutRule systemOutRule = new SystemOutRule().enableLog().muteForSuccessfulTests();\n    @Mock\n    private TenantResourcesService tenantResourcesService;\n    @Mock\n    private ServiceAccessor serviceAccessor;\n    @Mock\n    private BusinessArchiveArtifactsManager businessArchiveArtifactsManager;\n    @Mock\n    private TenantStateManager tenantStateManager;\n    @Mock\n    private UserTransactionService userTransactionService;\n\n    @Spy\n    @InjectMocks\n    private TenantAdministrationAPIImpl tenantManagementAPI;\n\n    @Before\n    public void before() throws Exception {\n        doReturn(serviceAccessor).when(tenantManagementAPI).getServiceAccessorNoException();\n        doReturn(serviceAccessor).when(tenantManagementAPI).getServiceAccessor();\n        doReturn(tenantResourcesService).when(serviceAccessor).getTenantResourcesService();\n        doReturn(userTransactionService).when(serviceAccessor).getUserTransactionService();\n\n        doAnswer(invocation -> ((Callable) invocation.getArgument(0)).call()).when(userTransactionService)\n                .executeInTransaction(any());\n\n        when(serviceAccessor.getBusinessArchiveArtifactsManager()).thenReturn(businessArchiveArtifactsManager);\n        when(serviceAccessor.getTenantStateManager()).thenReturn(tenantStateManager);\n    }\n\n    @Test\n    public void resume_should_have_annotation_available_when_tenant_is_paused() throws Exception {\n        final Method method = TenantAdministrationAPIImpl.class.getMethod(\"resume\");\n\n        final boolean present = method.isAnnotationPresent(AvailableInMaintenanceMode.class)\n                || TenantAdministrationAPIImpl.class.isAnnotationPresent(AvailableInMaintenanceMode.class);\n\n        assertThat(present).as(\n                \"Annotation @AvailableWhenTenantIsPaused should be present on API method 'resume' or directly on class TenantManagementAPIExt\")\n                .isTrue();\n    }\n\n    @Test\n    public void pause_should_have_annotation_available_when_tenant_is_paused() throws Exception {\n        final Method method = TenantAdministrationAPIImpl.class.getMethod(\"pause\");\n\n        final boolean present = method.isAnnotationPresent(AvailableInMaintenanceMode.class)\n                || TenantAdministrationAPIImpl.class.isAnnotationPresent(AvailableInMaintenanceMode.class);\n\n        assertThat(present).as(\n                \"Annotation @AvailableWhenTenantIsPaused should be present on API method 'pause' or directly on class TenantManagementAPIExt\")\n                .isTrue();\n    }\n\n    @Test\n    public void pageApi_should_be_available_in_maintenance_mode() {\n        // given:\n        final Class<PageAPIImpl> classPageApiExt = PageAPIImpl.class;\n\n        // then:\n        assertThat(classPageApiExt.isAnnotationPresent(AvailableInMaintenanceMode.class)).as(\n                \"Annotation @AvailableOnMaintenanceTenant should be present on PageAPIIml\");\n    }\n\n    @Test\n    public void resume_should_resolve_dependencies_for_deployed_processes() throws Exception {\n        tenantManagementAPI.resume();\n\n        verify(businessArchiveArtifactsManager).resolveDependenciesForAllProcesses(serviceAccessor);\n    }\n\n    @Test\n    public void installBDR_should_be_available_when_tenant_is_paused_ONLY() throws Exception {\n        final Method method = TenantAdministrationAPIImpl.class.getMethod(\"updateBusinessDataModel\", byte[].class);\n        final AvailableInMaintenanceMode annotation = method.getAnnotation(AvailableInMaintenanceMode.class);\n\n        final boolean present = annotation != null && annotation.onlyAvailableInMaintenanceMode();\n        assertThat(present).as(\n                \"Annotation @AvailableWhenTenantIsPaused(only=true) should be present on API method 'installBusinessDataModel(byte[])'\")\n                .isTrue();\n    }\n\n    @Test\n    public void uninstallBDR_should_be_available_when_tenant_is_paused_ONLY() throws Exception {\n        final Method method = TenantAdministrationAPIImpl.class.getMethod(\"uninstallBusinessDataModel\");\n        final AvailableInMaintenanceMode annotation = method.getAnnotation(AvailableInMaintenanceMode.class);\n\n        final boolean present = annotation != null && annotation.onlyAvailableInMaintenanceMode();\n        assertThat(present).as(\n                \"Annotation @AvailableWhenTenantIsPaused(only=true) should be present on API method 'uninstallBusinessDataModel()'\")\n                .isTrue();\n    }\n\n    @Test\n    public void uninstallBusinessDataModel_should_work() throws Exception {\n        // Given\n        final BusinessDataModelRepository repository = mock(BusinessDataModelRepository.class);\n        when(serviceAccessor.getBusinessDataModelRepository()).thenReturn(repository);\n        when(tenantStateManager.executeManagementOperation(anyString(), any(Callable.class)))\n                .thenAnswer(invocation -> {\n                    Object[] args = invocation.getArguments();\n                    return ((Callable) args[1]).call();\n                });\n\n        // When\n        tenantManagementAPI.uninstallBusinessDataModel();\n\n        // Then\n        verify(repository).uninstall();\n    }\n\n    @Test(expected = BusinessDataRepositoryException.class)\n    public void uninstallBusinessDataModel_should_throw_BusinessDataRepositoryException() throws Exception {\n        // Given\n        final BusinessDataModelRepository repository = mock(BusinessDataModelRepository.class);\n        when(serviceAccessor.getBusinessDataModelRepository()).thenReturn(repository);\n        when(tenantStateManager.executeManagementOperation(anyString(), any(Callable.class)))\n                .thenAnswer(invocation -> {\n                    Object[] args = invocation.getArguments();\n                    return ((Callable) args[1]).call();\n                });\n        doThrow(new SBusinessDataRepositoryException(\"error\")).when(repository).uninstall();\n\n        // When\n        tenantManagementAPI.uninstallBusinessDataModel();\n    }\n\n    @Test\n    public void getTenantResource_should_retrieve_resource_from_tenantResourceService() throws Exception {\n        // Given\n        final STenantResourceLight toBeReturned = new STenantResourceLight(\"some name\",\n                TenantResourceType.BDM, 111L, 222L, STenantResourceState.INSTALLED);\n        doReturn(toBeReturned).when(tenantResourcesService).getSingleLightResource(TenantResourceType.BDM);\n\n        // When\n        tenantManagementAPI.getTenantResource(BDM);\n\n        // Then\n        verify(tenantResourcesService).getSingleLightResource(TenantResourceType.BDM);\n    }\n\n    @Test\n    public void getTenantResource_should_return_NONE_if_exception() throws Exception {\n        // Given\n        doThrow(SBonitaReadException.class).when(tenantResourcesService)\n                .getSingleLightResource(any(TenantResourceType.class));\n\n        // When\n        final TenantResource tenantResource = tenantManagementAPI.getTenantResource(BDM);\n\n        // Then\n        assertThat(tenantResource).isEqualTo(TenantResource.NONE);\n    }\n\n    @Test\n    public void getBusinessDataModelResource_should_get_resource_for_BDM_type() {\n        // given:\n        doReturn(mock(TenantResource.class)).when(tenantManagementAPI)\n                .getTenantResource(any(org.bonitasoft.engine.tenant.TenantResourceType.class));\n\n        // when:\n        tenantManagementAPI.getBusinessDataModelResource();\n\n        // then:\n        verify(tenantManagementAPI).getTenantResource(BDM);\n    }\n\n    @Test\n    public void updateBDM_should_warn_when_update_fails()\n            throws BusinessDataRepositoryDeploymentException, InvalidBusinessDataModelException {\n        //given\n        doThrow(new BusinessDataRepositoryDeploymentException(\" an exception \")).when(tenantManagementAPI)\n                .uninstallBusinessDataModel();\n        systemOutRule.clearLog();\n\n        //when\n        try {\n            tenantManagementAPI.updateBusinessDataModel(\"toto\".getBytes());\n        } catch (Exception e) {\n            // normal\n        }\n        //then\n        assertThat(systemOutRule.getLog()).contains(\n                \"Caught an error when installing/updating the BDM, the transaction will be reverted and the previous BDM restored\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/TestImplemOfServerAPI.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.internal.ServerAPI;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class TestImplemOfServerAPI implements ServerAPI {\n\n    private static final long serialVersionUID = 1L;\n\n    @Override\n    public Object invokeMethod(final Map<String, Serializable> options, final String apiInterfaceName,\n            final String methodName,\n            final List<String> classNameParameters, final Object[] parametersValues) {\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/application/installer/ApplicationArchiveReaderTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.application.installer;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.io.FileAndContentUtils.file;\nimport static org.bonitasoft.engine.io.FileAndContentUtils.zip;\nimport static org.bonitasoft.engine.io.FileOperations.asInputStream;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.bonitasoft.engine.api.impl.application.installer.detector.*;\nimport org.junit.Test;\n\n/**\n * @author Baptiste Mesta.\n */\npublic class ApplicationArchiveReaderTest {\n\n    ArtifactTypeDetector artifactTypeDetector = new ArtifactTypeDetector(new BdmDetector(),\n            new LivingApplicationDetector(), new OrganizationDetector(), new CustomPageDetector(),\n            new ProcessDetector(), new ThemeDetector(), new PageAndFormDetector(), new LayoutDetector(),\n            new IconDetector());\n    private final ApplicationArchiveReader applicationArchiveReader = new ApplicationArchiveReader(\n            artifactTypeDetector);\n\n    @Test\n    public void should_read_application_archive_with_a_live_application() throws Exception {\n        byte[] zip = zip(\n                file(\"apps/MyApp.xml\",\n                        \"<applications xmlns=\\\"http://documentation.bonitasoft.com/application-xml-schema/1.1\\\"></applications>\"));\n\n        ApplicationArchive applicationArchive = applicationArchiveReader.read(asInputStream(zip));\n\n        assertThat(applicationArchive.getApplications().size()).isEqualTo(1);\n        assertThat(applicationArchive.getApplications().get(0).getName()).isEqualTo(\"MyApp.xml\");\n        assertThat(new String(Files.readAllBytes(applicationArchive.getApplications().get(0).toPath()))).contains(\n                \"<applications xmlns=\\\"http://documentation.bonitasoft.com/application-xml-schema/1.1\\\"></applications>\");\n\n    }\n\n    @Test\n    public void should_read_application_archive_with_pages() throws IOException {\n        byte[] zip = zip(\n                file(\"pages/myCustomPage1.zip\",\n                        zip(file(\"page.properties\", \"name=custompage_test1\\ncontentType=page\"),\n                                file(\"resources/index.html\", \"someContent\"))),\n                file(\"pages/myCustomPage2.zip\",\n                        zip(file(\"page.properties\", \"name=custompage_test2\\ncontentType=page\"),\n                                file(\"resources/Index.groovy\", \"someContent\"))),\n                file(\"pages/myCustomPage3.zip\", zip(file(\"page.properties\", \"name=custompage_test3\\ncontentType=page\"))) //ignored, no index\n        );\n\n        ApplicationArchive applicationArchive = applicationArchiveReader.read(zip);\n\n        assertThat(applicationArchive.getPages()).hasSize(2);\n        List<java.io.File> pageList = applicationArchive.getPages().stream().sorted().collect(Collectors.toList());\n        File firstPage = pageList.get(0);\n        File secondPage = pageList.get(1);\n\n        assertThat(firstPage.getName()).contains(\"myCustomPage1.zip\");\n        assertThat(Files.readAllBytes(firstPage.toPath()))\n                .containsExactly(zip(file(\"page.properties\", \"name=custompage_test1\\ncontentType=page\"),\n                        file(\"resources/index.html\", \"someContent\")));\n        assertThat(secondPage.getName()).contains(\"myCustomPage2.zip\");\n        assertThat(Files.readAllBytes(secondPage.toPath()))\n                .containsExactly(zip(file(\"page.properties\", \"name=custompage_test2\\ncontentType=page\"),\n                        file(\"resources/Index.groovy\", \"someContent\")));\n    }\n\n    @Test\n    public void should_read_application_archive_with_layout() throws IOException {\n        byte[] layout = zip(file(\"page.properties\", \"name=custompage_test1\\ncontentType=layout\"),\n                file(\"resources/index.html\", \"someContent\"));\n        byte[] zip = zip(\n                file(\"layout.zip\", layout));\n\n        ApplicationArchive applicationArchive = applicationArchiveReader.read(asInputStream(zip));\n\n        List<java.io.File> layouts = applicationArchive.getLayouts();\n        assertThat(layouts.size()).isEqualTo(1);\n        assertThat(layouts.get(0).getName()).contains(\"layout.zip\");\n        assertThat(Files.readAllBytes(layouts.get(0).toPath())).containsExactly(layout);\n    }\n\n    @Test\n    public void should_read_application_archive_with_theme() throws IOException {\n        byte[] themeContent = zip(file(\"page.properties\", \"name=custompage_test1\\ncontentType=theme\"),\n                file(\"resources/theme.css\", \"someContent\"));\n        byte[] zip = zip(\n                file(\"theme.zip\", themeContent));\n\n        ApplicationArchive applicationArchive = applicationArchiveReader.read(asInputStream(zip));\n\n        List<java.io.File> themes = applicationArchive.getThemes();\n        assertThat(themes.size()).isEqualTo(1);\n        assertThat(themes.get(0).getName()).contains(\"theme.zip\");\n        assertThat(Files.readAllBytes(themes.get(0).toPath())).containsExactly(themeContent);\n\n    }\n\n    @Test\n    public void should_read_application_archive_with_apiExtension() throws IOException {\n        byte[] apiExtensionContent = zip(file(\"page.properties\", \"name=custompage_test1\\ncontentType=apiExtension\"));\n        byte[] zip = zip(\n                file(\"apiExtension.zip\", apiExtensionContent));\n\n        ApplicationArchive applicationArchive = applicationArchiveReader.read(asInputStream(zip));\n\n        List<java.io.File> restAPIExtensions = applicationArchive.getRestAPIExtensions();\n        assertThat(restAPIExtensions.size()).isEqualTo(1);\n        assertThat(restAPIExtensions.get(0).getName()).contains(\"apiExtension.zip\");\n        assertThat(Files.readAllBytes(restAPIExtensions.get(0).toPath())).containsExactly(apiExtensionContent);\n    }\n\n    @Test\n    public void should_read_application_archive_with_process() throws IOException {\n        byte[] barContent = zip(file(\"process-design.xml\",\n                \"<process xmlns=\\\"http://www.bonitasoft.org/ns/process/client/\\\"></process>\"));\n        byte[] zip = zip(\n                file(\"myprocess.bar\", barContent));\n\n        ApplicationArchive applicationArchive = applicationArchiveReader.read(asInputStream(zip));\n\n        List<java.io.File> processes = applicationArchive.getProcesses();\n        assertThat(processes.size()).isEqualTo(1);\n        assertThat(processes.get(0).getName()).contains(\"myprocess.bar\");\n        assertThat(Files.readAllBytes(processes.get(0).toPath())).containsExactly(barContent);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/application/installer/ApplicationArchiveTest.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.application.installer;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.assertThrows;\n\nimport com.vdurmont.semver4j.SemverException;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\n\nclass ApplicationArchiveTest {\n\n    @ParameterizedTest\n    @ValueSource(strings = { \"SNAPSHOT\", \"\", \".0\" })\n    void unsupportedApplicationVersions(String version)\n            throws Exception {\n        try (var applicationArchive = new ApplicationArchive()) {\n            assertThrows(SemverException.class, () -> applicationArchive.setVersion(version));\n        }\n    }\n\n    @ParameterizedTest\n    @ValueSource(strings = { \"9-SNAPSHOT\", \"5\", \"2.0\", \"1.0.0\", \"2.1-alpha\", \"3.3.2.beta1\" })\n    void supportedApplicationVersions(String version)\n            throws Exception {\n        try (var applicationArchive = new ApplicationArchive()) {\n            applicationArchive.setVersion(version);\n            assertThat(applicationArchive.hasVersion()).isTrue();\n        }\n    }\n\n    @Test\n    void hasGreaterVersionThan() throws Exception {\n        try (var applicationArchive = new ApplicationArchive()) {\n            applicationArchive.setVersion(\"2.0\");\n            assertThat(applicationArchive.hasVersionGreaterThan(\"1.0.0\")).isTrue();\n\n            applicationArchive.setVersion(\"0.0.2\");\n            assertThat(applicationArchive.hasVersionGreaterThan(\"1.0.0\")).isFalse();\n\n            applicationArchive.setVersion(null);\n            assertThat(applicationArchive.hasVersionGreaterThan(\"1.0.0\")).isFalse();\n        }\n    }\n\n    @Test\n    void hasEquivalentVersionTo() throws Exception {\n        try (var applicationArchive = new ApplicationArchive()) {\n            applicationArchive.setVersion(\"2.0\");\n            assertThat(applicationArchive.hasVersionEquivalentTo(\"2.0\")).isTrue();\n\n            applicationArchive.setVersion(\"0.0.2\");\n            assertThat(applicationArchive.hasVersionEquivalentTo(\"1.0.0\")).isFalse();\n\n            applicationArchive.setVersion(null);\n            assertThat(applicationArchive.hasVersionEquivalentTo(\"1.0.0\")).isFalse();\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/application/installer/ApplicationInstallerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.application.installer;\n\nimport static java.util.Collections.emptyList;\nimport static org.assertj.core.api.Assertions.*;\nimport static org.bonitasoft.engine.api.result.Status.Level.ERROR;\nimport static org.bonitasoft.engine.api.result.Status.Level.INFO;\nimport static org.bonitasoft.engine.api.result.StatusCode.*;\nimport static org.bonitasoft.engine.api.result.StatusContext.PROCESS_NAME_KEY;\nimport static org.bonitasoft.engine.api.result.StatusContext.PROCESS_VERSION_KEY;\nimport static org.bonitasoft.engine.business.application.ApplicationImportPolicy.FAIL_ON_DUPLICATES;\nimport static org.bonitasoft.engine.io.FileAndContentUtils.file;\nimport static org.bonitasoft.engine.io.FileAndContentUtils.zip;\nimport static org.bonitasoft.engine.io.IOUtils.createTempFile;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.*;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.*;\nimport java.util.concurrent.Callable;\n\nimport javax.xml.bind.JAXBException;\n\nimport org.bonitasoft.engine.api.ImportError;\nimport org.bonitasoft.engine.api.ImportStatus;\nimport org.bonitasoft.engine.api.impl.ProcessDeploymentAPIDelegate;\nimport org.bonitasoft.engine.api.result.ExecutionResult;\nimport org.bonitasoft.engine.api.result.Status;\nimport org.bonitasoft.engine.bdm.BusinessObjectModelConverter;\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.model.field.FieldType;\nimport org.bonitasoft.engine.bdm.model.field.SimpleField;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveFactory;\nimport org.bonitasoft.engine.bpm.bar.InvalidBusinessArchiveFormatException;\nimport org.bonitasoft.engine.bpm.process.*;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.internal.ProcessDefinitionImpl;\nimport org.bonitasoft.engine.business.application.exporter.ApplicationNodeContainerConverter;\nimport org.bonitasoft.engine.business.application.xml.ApplicationNode;\nimport org.bonitasoft.engine.business.application.xml.ApplicationNodeBuilder.ApplicationBuilder;\nimport org.bonitasoft.engine.business.application.xml.ApplicationNodeContainer;\nimport org.bonitasoft.engine.business.data.BusinessDataRepositoryDeploymentException;\nimport org.bonitasoft.engine.exception.ApplicationInstallationException;\nimport org.bonitasoft.engine.identity.ImportPolicy;\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.engine.service.InstallationService;\nimport org.bonitasoft.engine.tenant.TenantStateManager;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.mockito.*;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport org.mockito.junit.jupiter.MockitoSettings;\nimport org.mockito.quality.Strictness;\nimport org.xml.sax.SAXException;\n\n/**\n * @author Baptiste Mesta.\n */\n@ExtendWith(MockitoExtension.class)\n@MockitoSettings(strictness = Strictness.LENIENT)\nclass ApplicationInstallerTest {\n\n    @Mock\n    private UserTransactionService transactionService;\n\n    @Mock\n    private InstallationService installationService;\n\n    @Mock\n    private TenantStateManager tenantStateManager;\n\n    @Captor\n    ArgumentCaptor<Callable<Object>> callableCaptor;\n\n    @InjectMocks\n    @Spy\n    private ApplicationInstallerImpl applicationInstaller;\n\n    @BeforeEach\n    void before() throws Exception {\n        // to bypass the transaction-wrapping part:\n        doAnswer(inv -> callableCaptor.getValue().call()).when(transactionService)\n                .executeInTransaction(callableCaptor.capture());\n\n        // to bypass the session-wrapping part:\n        doAnswer(inv -> callableCaptor.getValue().call()).when(applicationInstaller)\n                .inSession(callableCaptor.capture());\n\n        // bypass update version\n        doNothing().when(applicationInstaller).updateApplicationVersion(\"1.0.0\");\n    }\n\n    @Test\n    void should_install_application_containing_all_kind_of_custom_pages() throws Exception {\n        File page = createTempFile(\"page\", \"zip\", zip(file(\"page.properties\", \"name=MyPage\\ncontentType=page\")));\n        File layout = createTempFile(\"layout\", \"zip\",\n                zip(file(\"page.properties\", \"name=MyLayout\\ncontentType=layout\")));\n        File theme = createTempFile(\"theme\", \"zip\", zip(file(\"page.properties\", \"name=MyTheme\\ncontentType=theme\")));\n        File restAPI = createTempFile(\"restApiExtension\", \"zip\",\n                zip(file(\"page.properties\", \"name=MyApi\\ncontentType=apiExtension\")));\n        try (var applicationArchive = new ApplicationArchive()) {\n            applicationArchive.addPage(page)\n                    .addLayout(layout)\n                    .addTheme(theme)\n                    .addRestAPIExtension(restAPI);\n            applicationArchive.setVersion(\"1.0.0\");\n\n            doNothing().when(applicationInstaller).enableResolvedProcesses(any(), any());\n            doNothing().when(applicationInstaller).installOrganization(any(), any());\n            doReturn(mock(Page.class)).when(applicationInstaller).createPage(any(), any());\n            doReturn(null).when(applicationInstaller).getPageIfExist(any());\n\n            applicationInstaller.install(applicationArchive);\n        }\n        var captor = ArgumentCaptor.forClass(Properties.class);\n        verify(applicationInstaller, times(4)).createPage(any(), captor.capture());\n\n        assertThat(captor.getAllValues()).extracting(\"name\", \"contentType\").contains(tuple(\"MyPage\", \"page\"),\n                tuple(\"MyLayout\", \"layout\"),\n                tuple(\"MyTheme\", \"theme\"),\n                tuple(\"MyApi\", \"apiExtension\"));\n    }\n\n    @Test\n    void should_install_application_containing_living_applications() throws Exception {\n        File application = createTempFile(\"application\", \"xml\",\n                applicationContent(new ApplicationBuilder(\"myApp\", \"My App\", \"1.0\").create()));\n        doNothing().when(applicationInstaller).installOrganization(any(), any());\n        doReturn(new ImportStatus(\"application\")).when(applicationInstaller).importApplication(any(), any(),\n                eq(FAIL_ON_DUPLICATES));\n        doNothing().when(applicationInstaller).enableResolvedProcesses(any(), any());\n        try (var applicationArchive = new ApplicationArchive()) {\n            applicationArchive.addApplication(application);\n            applicationArchive.setVersion(\"1.0.0\");\n            applicationInstaller.install(applicationArchive);\n        }\n\n        verify(applicationInstaller).importApplication(any(), any(), eq(FAIL_ON_DUPLICATES));\n    }\n\n    @Test\n    void should_not_install_living_applications_if_page_missing() throws Exception {\n\n        final ExecutionResult result = new ExecutionResult();\n        ImportStatus importStatus = new ImportStatus(\"application\");\n        importStatus.addError(new ImportError(\"page\", ImportError.Type.PAGE));\n        importStatus.addError(new ImportError(\"test\", ImportError.Type.PAGE));\n        File applicationFile = createTempFile(\"application\", \"xml\",\n                applicationContent(new ApplicationBuilder(\"myApp\", \"My App\", \"1.0\").create()));\n\n        doReturn(importStatus).when(applicationInstaller).importApplication(any(), any(), eq(FAIL_ON_DUPLICATES));\n\n        try (var applicationArchive = new ApplicationArchive()) {\n            applicationArchive.addApplication(applicationFile);\n            assertThatExceptionOfType(ApplicationInstallationException.class)\n                    .isThrownBy(() -> applicationInstaller.installLivingApplications(applicationArchive, result,\n                            FAIL_ON_DUPLICATES))\n                    .withMessage(\"At least one application failed to be installed. Canceling installation.\");\n        }\n        verify(applicationInstaller).importApplication(any(), any(), eq(FAIL_ON_DUPLICATES));\n\n        assertThat(result.getAllStatus()).hasSize(2).extracting(\"code\")\n                .containsOnly(LIVING_APP_REFERENCES_UNKNOWN_PAGE);\n        assertThat(result.getAllStatus()).extracting(\"message\")\n                .containsExactly(\"Unknown PAGE named 'page'\", \"Unknown PAGE named 'test'\");\n        assertThat(result.getAllStatus()).extracting(\"level\")\n                .containsExactly(ERROR, ERROR);\n    }\n\n    private byte[] applicationContent(ApplicationNode application) throws JAXBException, IOException, SAXException {\n        var container = new ApplicationNodeContainer();\n        container.addApplication(application);\n        return new ApplicationNodeContainerConverter().marshallToXML(container);\n    }\n\n    @Test\n    void should_install_and_enable_resolved_process(@TempDir Path tmpFolder) throws Exception {\n        // given\n        byte[] barContent = createValidBusinessArchive(tmpFolder);\n        File process = createTempFile(\"process\", \"bar\", barContent);\n        doNothing().when(applicationInstaller).installOrganization(any(), any());\n\n        ProcessDeploymentAPIDelegate processDeploymentAPIDelegate = mock(ProcessDeploymentAPIDelegate.class);\n        doReturn(processDeploymentAPIDelegate).when(applicationInstaller).getProcessDeploymentAPIDelegate();\n        doReturn(Optional.empty()).when(applicationInstaller).getDeployedProcessId(any(), eq(\"1.0\"));\n\n        ProcessDefinition myProcess = aProcessDefinition(123L);\n        doReturn(myProcess).when(processDeploymentAPIDelegate).deploy(any());\n\n        // when\n        try (var applicationArchive = new ApplicationArchive()) {\n            applicationArchive.addProcess(process);\n            applicationArchive.setVersion(\"1.0.0\");\n            applicationInstaller.install(applicationArchive);\n        }\n        // then\n        verify(applicationInstaller).deployProcess(any(), any());\n        verify(processDeploymentAPIDelegate).deploy(any());\n        verify(processDeploymentAPIDelegate, never()).enableProcess(123L);\n\n    }\n\n    @Test\n    void should_install_bdm_in_maintenance_mode() throws Exception {\n        byte[] bdmZipContent = createValidBDMZipFile();\n        File bdm = createTempFile(\"bdm\", \"zip\", bdmZipContent);\n        try (var applicationArchive = new ApplicationArchive()) {\n            applicationArchive.setBdm(bdm);\n            doNothing().when(applicationInstaller).installOrganization(any(), any());\n            doReturn(\"1.0\").when(applicationInstaller).updateBusinessDataModel(applicationArchive);\n            doReturn(false).when(applicationInstaller).sameBdmContentDeployed(any());\n            doReturn(Collections.emptyList()).when(applicationInstaller).installProcesses(any(), any());\n            doNothing().when(applicationInstaller).enableResolvedProcesses(any(), any());\n            doNothing().when(applicationInstaller).pauseTenantInSession();\n            doNothing().when(applicationInstaller).resumeTenantInSession();\n            applicationArchive.setVersion(\"1.0.0\");\n            applicationInstaller.install(applicationArchive);\n\n            verify(applicationInstaller).pauseTenantInSession();\n            verify(applicationInstaller).updateBusinessDataModel(applicationArchive);\n            verify(applicationInstaller).resumeTenantInSession();\n        }\n\n    }\n\n    @Test\n    void should_keep_maintenance_mode_when_bdm_update_fails() throws Exception {\n        byte[] bdmZipContent = createValidBDMZipFile();\n        File bdm = createTempFile(\"bdm\", \"zip\", bdmZipContent);\n        try (var applicationArchive = new ApplicationArchive()) {\n            applicationArchive.setBdm(bdm);\n            doNothing().when(applicationInstaller).installOrganization(any(), any());\n            doReturn(\"1.0\").when(applicationInstaller).updateBusinessDataModel(applicationArchive);\n            doReturn(false).when(applicationInstaller).sameBdmContentDeployed(any());\n            doReturn(Collections.emptyList()).when(applicationInstaller).installProcesses(any(), any());\n            doNothing().when(applicationInstaller).enableResolvedProcesses(any(), any());\n            doNothing().when(applicationInstaller).pauseTenantInSession();\n            doNothing().when(applicationInstaller).resumeTenantInSession();\n            applicationArchive.setVersion(\"1.0.0\");\n            doThrow(BusinessDataRepositoryDeploymentException.class).when(applicationInstaller)\n                    .updateBusinessDataModel(any());\n\n            Assertions.assertThrows(ApplicationInstallationException.class,\n                    () -> applicationInstaller.install(applicationArchive));\n\n            verify(applicationInstaller).pauseTenantInSession();\n            verify(applicationInstaller).updateBusinessDataModel(applicationArchive);\n            verify(applicationInstaller, never()).resumeTenantInSession();\n        }\n\n    }\n\n    @Test\n    void should_call_enable_resolved_processes(@TempDir Path tmpFolder) throws Exception {\n        byte[] barContent = createValidBusinessArchive(tmpFolder);\n        File process = createTempFile(\"process\", \"bar\", barContent);\n        doNothing().when(applicationInstaller).installOrganization(any(), any());\n        ProcessDeploymentAPIDelegate processDeploymentAPIDelegate = mock(ProcessDeploymentAPIDelegate.class);\n\n        doReturn(processDeploymentAPIDelegate).when(applicationInstaller).getProcessDeploymentAPIDelegate();\n        doReturn(Optional.empty()).when(applicationInstaller).getDeployedProcessId(any(), eq(\"1.0\"));\n\n        ProcessDefinition myProcess = aProcessDefinition(123L);\n        doReturn(myProcess).when(processDeploymentAPIDelegate).deploy(any());\n\n        try (var applicationArchive = new ApplicationArchive()) {\n            applicationArchive.addProcess(process);\n            applicationArchive.setVersion(\"1.0.0\");\n            applicationInstaller.install(applicationArchive);\n        }\n\n        verify(applicationInstaller).deployProcess(any(), any());\n        verify(processDeploymentAPIDelegate).deploy(any());\n        verify(applicationInstaller).enableResolvedProcesses(eq(Collections.singletonList(123L)), any());\n    }\n\n    @Test\n    void enableResolvedProcesses_should_enable_processes_resolved_and_not_already_enabled() throws Exception {\n        // given:\n        final ExecutionResult result = new ExecutionResult();\n        ProcessDeploymentAPIDelegate processDeploymentAPIDelegate = mock(ProcessDeploymentAPIDelegate.class);\n        doReturn(processDeploymentAPIDelegate).when(applicationInstaller).getProcessDeploymentAPIDelegate();\n        final ProcessDeploymentInfo info = mock(ProcessDeploymentInfo.class);\n        final long processDefId = 234L;\n        doReturn(Map.of(processDefId, info)).when(processDeploymentAPIDelegate)\n                .getProcessDeploymentInfosFromIds(List.of(processDefId));\n        doReturn(processDefId).when(info).getProcessId();\n        doReturn(ConfigurationState.RESOLVED).when(info).getConfigurationState();\n        doReturn(ActivationState.DISABLED).when(info).getActivationState();\n\n        // when:\n        applicationInstaller.enableResolvedProcesses(List.of(processDefId), result);\n\n        // then:\n        verify(processDeploymentAPIDelegate).enableProcess(processDefId);\n    }\n\n    @Test\n    void enableResolvedProcesses_should_throw_exception_on_unresolved_process() throws Exception {\n        // given:\n        final ExecutionResult result = new ExecutionResult();\n        ProcessDeploymentAPIDelegate processDeploymentAPIDelegate = mock(ProcessDeploymentAPIDelegate.class);\n        doReturn(processDeploymentAPIDelegate).when(applicationInstaller).getProcessDeploymentAPIDelegate();\n        final ProcessDeploymentInfo info = mock(ProcessDeploymentInfo.class);\n        final long processDefId = 234L;\n        doReturn(Map.of(processDefId, info)).when(processDeploymentAPIDelegate)\n                .getProcessDeploymentInfosFromIds(List.of(processDefId));\n        doReturn(processDefId).when(info).getProcessId();\n        doReturn(ConfigurationState.UNRESOLVED).when(info).getConfigurationState();\n\n        // when:\n        assertThatExceptionOfType(ProcessDeployException.class)\n                .isThrownBy(() -> applicationInstaller.enableResolvedProcesses(List.of(processDefId), result))\n                .withMessage(\"At least one process failed to deploy / enable. Canceling installation.\");\n\n        // then:\n        verify(processDeploymentAPIDelegate, never()).enableProcess(anyLong());\n        assertThat(result.getAllStatus()).hasSize(1).extracting(\"code\")\n                .containsExactly(PROCESS_DEPLOYMENT_ENABLEMENT_KO);\n    }\n\n    @Test\n    void should_skip_install_process_if_already_existing(@TempDir Path tmpFolder) throws Exception {\n        final ExecutionResult executionResult = new ExecutionResult();\n        byte[] barContent = createValidBusinessArchive(tmpFolder);\n        File process = createTempFile(\"process\", \"bar\", barContent);\n        doReturn(Optional.of(1L)).when(applicationInstaller).getDeployedProcessId(\"myProcess\", \"1.0\");\n\n        // when\n        try (ApplicationArchive applicationArchive = new ApplicationArchive()) {\n            applicationArchive.addProcess(process);\n            applicationInstaller.installProcesses(applicationArchive, executionResult);\n        }\n\n        // verify executionResult\n        assertThat(executionResult.hasInfo()).isTrue();\n        assertThat(executionResult.getAllStatus().get(0))\n                .returns(INFO, from(Status::getLevel))\n                .returns(PROCESS_DEPLOYMENT_SKIP_INSTALL, from(Status::getCode))\n                .returns(\"myProcess\", from(status -> status.getContext().get(PROCESS_NAME_KEY)))\n                .returns(\"1.0\", from(status -> status.getContext().get(PROCESS_VERSION_KEY)));\n\n        // verify deploy never called\n        verify(applicationInstaller, never()).deployProcess(\n                ArgumentMatchers.argThat(b -> b.getProcessDefinition().getName().equals(\"myProcess\")\n                        && b.getProcessDefinition().getVersion().equals(\"1.0\")),\n                any());\n    }\n\n    @Test\n    void should_throw_exception_if_application_archive_is_empty() throws Exception {\n        try (ApplicationArchive applicationArchive = new ApplicationArchive()) {\n            applicationArchive.setVersion(\"1.0.0\");\n            assertThatExceptionOfType(ApplicationInstallationException.class)\n                    .isThrownBy(() -> applicationInstaller.install(applicationArchive))\n                    .withMessage(\"The Application Archive contains no valid artifact to install\");\n        }\n    }\n\n    @Test\n    void should_install_organization() throws Exception {\n        File organization = createTempFile(\"org\", \"xml\", \"content\".getBytes());\n        doReturn(emptyList()).when(applicationInstaller).importOrganization(any(), any());\n        doNothing().when(applicationInstaller).enableResolvedProcesses(any(), any());\n        try (var applicationArchive = new ApplicationArchive()) {\n            applicationArchive.setOrganization(organization);\n            applicationArchive.setVersion(\"1.0.0\");\n            applicationInstaller.install(applicationArchive);\n        }\n\n        verify(applicationInstaller).importOrganization(organization, ImportPolicy.FAIL_ON_DUPLICATES);\n    }\n\n    @Test\n    void should_not_fail_when_no_organization() throws Exception {\n        var executionResult = new ExecutionResult();\n        try (var applicationArchive = new ApplicationArchive()) {\n            applicationInstaller.installOrganization(applicationArchive, executionResult);\n        }\n\n        assertThat(executionResult.getInfo()).extracting(\"message\")\n                .containsOnly(\"No organization found. Use the technical user to configure the organization.\");\n    }\n\n    @Test\n    void install_should_call_install_configuration_file_on_installation_service() throws Exception {\n        // given:\n        final ExecutionResult executionResult = new ExecutionResult();\n        doNothing().when(installationService).install(eq(null), any());\n\n        // when:\n        applicationInstaller.installConfiguration(\n                new File(ApplicationInstallerImpl.class.getResource(\"/RequestLoan_conf_with_null_params.bconf\")\n                        .getFile()),\n                executionResult);\n\n        // then:\n        verify(installationService).install(eq(null), any());\n    }\n\n    private ProcessDefinitionImpl aProcessDefinition(long id) {\n        ProcessDefinitionImpl myProcess = new ProcessDefinitionImpl(\"myProcess\", \"1.0\");\n        myProcess.setId(id);\n        return myProcess;\n    }\n\n    private byte[] createValidBusinessArchive(Path tmpFolder)\n            throws InvalidBusinessArchiveFormatException, InvalidProcessDefinitionException, IOException {\n        BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(new ProcessDefinitionBuilder().createNewInstance(\"myProcess\", \"1.0\").done())\n                .done();\n        Path businessArchiveFile = Files.createFile(tmpFolder.resolve(\"tmpBar.bar\"));\n        assert businessArchiveFile.toFile().delete();\n        BusinessArchiveFactory.writeBusinessArchiveToFile(businessArchive, businessArchiveFile.toFile());\n        return Files.readAllBytes(businessArchiveFile);\n    }\n\n    private byte[] createValidBDMZipFile() throws IOException, JAXBException, SAXException {\n        BusinessObjectModel bom = new BusinessObjectModel();\n        BusinessObject businessObject = new BusinessObject(\"org.bonita.Employee\");\n        SimpleField field = new SimpleField();\n        field.setName(\"name\");\n        field.setType(FieldType.STRING);\n        businessObject.addField(field);\n        bom.addBusinessObject(businessObject);\n        return new BusinessObjectModelConverter().marshall(bom);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/application/installer/ApplicationInstallerUpdateTest.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.application.installer;\n\nimport static java.util.Collections.emptyList;\nimport static org.assertj.core.api.Assertions.*;\nimport static org.bonitasoft.engine.business.application.ApplicationImportPolicy.REPLACE_DUPLICATES;\nimport static org.bonitasoft.engine.io.FileAndContentUtils.file;\nimport static org.bonitasoft.engine.io.FileAndContentUtils.zip;\nimport static org.bonitasoft.engine.io.IOUtils.createTempFile;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.*;\nimport static org.mockito.Mockito.mock;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.*;\nimport java.util.concurrent.Callable;\nimport java.util.stream.Stream;\n\nimport javax.xml.bind.JAXBException;\n\nimport org.assertj.core.util.Lists;\nimport org.bonitasoft.engine.api.ImportStatus;\nimport org.bonitasoft.engine.api.impl.ProcessDeploymentAPIDelegate;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveFactory;\nimport org.bonitasoft.engine.bpm.bar.InvalidBusinessArchiveFormatException;\nimport org.bonitasoft.engine.bpm.process.*;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.internal.ProcessDefinitionImpl;\nimport org.bonitasoft.engine.business.application.exporter.ApplicationNodeContainerConverter;\nimport org.bonitasoft.engine.business.application.xml.ApplicationNode;\nimport org.bonitasoft.engine.business.application.xml.ApplicationNodeBuilder.ApplicationBuilder;\nimport org.bonitasoft.engine.business.application.xml.ApplicationNodeContainer;\nimport org.bonitasoft.engine.business.data.BusinessDataModelRepository;\nimport org.bonitasoft.engine.exception.ApplicationInstallationException;\nimport org.bonitasoft.engine.identity.ImportPolicy;\nimport org.bonitasoft.engine.io.FileOperations;\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.TemporaryFolder;\nimport org.junit.runner.RunWith;\nimport org.mockito.*;\nimport org.mockito.junit.MockitoJUnitRunner;\nimport org.xml.sax.SAXException;\n\n/**\n * @author Danila Mazour\n * @author Haroun El Alami\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class ApplicationInstallerUpdateTest {\n\n    @Rule\n    public TemporaryFolder temporaryFolder = new TemporaryFolder();\n\n    @Mock\n    private UserTransactionService transactionService;\n\n    @Captor\n    ArgumentCaptor<Callable<Object>> callableCaptor;\n\n    @Mock\n    private BusinessDataModelRepository bdmRepository;\n\n    @InjectMocks\n    @Spy\n    private ApplicationInstallerImpl applicationInstaller;\n\n    @Before\n    public void before() throws Exception {\n        // to bypass the transaction-wrapping part:\n        doAnswer(inv -> callableCaptor.getValue().call()).when(transactionService)\n                .executeInTransaction(callableCaptor.capture());\n\n        // to bypass the session-wrapping part:\n        doAnswer(inv -> callableCaptor.getValue().call()).when(applicationInstaller)\n                .inSession(callableCaptor.capture());\n\n        // trigger update behaviour\n        Page samplePage = spy(Page.class);\n        doReturn(1L).when(samplePage).getId();\n\n        doReturn(samplePage).when(applicationInstaller).getPageIfExist(anyString());\n        doNothing().when(applicationInstaller).updatePageContent(any(), eq(1L));\n\n        // bypass update version\n        doNothing().when(applicationInstaller).updateApplicationVersion(\"1.0.1\");\n        doNothing().when(applicationInstaller).pauseTenantInSession();\n        doNothing().when(applicationInstaller).resumeTenantInSession();\n    }\n\n    @Test\n    public void should_update_application_containing_all_kind_of_custom_pages() throws Exception {\n        File page = createTempFile(\"page\", \"zip\", zip(file(\"page.properties\", \"name=MyPage\\ncontentType=page\")));\n        File layout = createTempFile(\"layout\", \"zip\",\n                zip(file(\"page.properties\", \"name=MyLayout\\ncontentType=layout\")));\n        File theme = createTempFile(\"theme\", \"zip\", zip(file(\"page.properties\", \"name=MyTheme\\ncontentType=theme\")));\n        File restAPI = createTempFile(\"restApiExtension\", \"zip\",\n                zip(file(\"page.properties\", \"name=MyApi\\ncontentType=apiExtension\")));\n        try (var applicationArchive = new ApplicationArchive()) {\n            applicationArchive.addPage(page)\n                    .addLayout(layout)\n                    .addTheme(theme)\n                    .addRestAPIExtension(restAPI);\n            applicationArchive.setVersion(\"1.0.1\");\n            doNothing().when(applicationInstaller).disableOldProcesses(any(), any());\n            doNothing().when(applicationInstaller).enableResolvedProcesses(any(), any());\n\n            applicationInstaller.update(applicationArchive);\n        }\n        var captor = ArgumentCaptor.forClass(File.class);\n        verify(applicationInstaller, times(4)).updatePageContent(captor.capture(), eq(1L));\n\n        assertThat(\n                captor.getAllValues().stream().allMatch(file -> Stream.of(\"page\", \"layout\", \"theme\", \"restApiExtension\")\n                        .anyMatch(name -> file.getName().startsWith(name))))\n                .isTrue();\n    }\n\n    @Test\n    public void should_update_application_containing_living_applications() throws Exception {\n        File application = createTempFile(\"application\", \"xml\",\n                applicationContent(new ApplicationBuilder(\"myApp\", \"My App\", \"1.0\").create()));\n        doReturn(new ImportStatus(\"application\")).when(applicationInstaller).importApplication(any(), any(),\n                eq(REPLACE_DUPLICATES));\n        doNothing().when(applicationInstaller).disableOldProcesses(any(), any());\n        doNothing().when(applicationInstaller).enableResolvedProcesses(any(), any());\n        try (var applicationArchive = new ApplicationArchive()) {\n            applicationArchive.setVersion(\"1.0.1\");\n            applicationArchive.addApplication(application);\n            applicationInstaller.update(applicationArchive);\n        }\n\n        verify(applicationInstaller).importApplication(any(), any(), eq(REPLACE_DUPLICATES));\n    }\n\n    private byte[] applicationContent(ApplicationNode application) throws JAXBException, IOException, SAXException {\n        var container = new ApplicationNodeContainer();\n        container.addApplication(application);\n        return new ApplicationNodeContainerConverter().marshallToXML(container);\n    }\n\n    @Test\n    public void should_install_and_enable_resolved_process() throws Exception {\n        // given\n        byte[] barContent = createValidBusinessArchive();\n        File process = createTempFile(\"process\", \"bar\", barContent);\n        doReturn(Optional.empty()).when(applicationInstaller).getDeployedProcessId(\"myProcess\", \"1.0\");\n        long newProcessId = 123L;\n        doReturn(Lists.newArrayList(newProcessId)).when(applicationInstaller).getDeployedProcessIds();\n\n        ProcessDeploymentAPIDelegate processDeploymentAPIDelegate = mock(ProcessDeploymentAPIDelegate.class);\n        doReturn(processDeploymentAPIDelegate).when(applicationInstaller).getProcessDeploymentAPIDelegate();\n\n        final ProcessDeploymentInfo info = mock(ProcessDeploymentInfo.class);\n        doReturn(newProcessId).when(info).getProcessId();\n        doReturn(ConfigurationState.RESOLVED).when(info).getConfigurationState();\n        doReturn(ActivationState.DISABLED).when(info).getActivationState();\n        doReturn(Map.of(newProcessId, info)).when(processDeploymentAPIDelegate)\n                .getProcessDeploymentInfosFromIds(List.of(newProcessId));\n\n        ProcessDefinition myProcess = aProcessDefinition(newProcessId);\n        doReturn(myProcess).when(processDeploymentAPIDelegate).deploy(any());\n\n        // when\n        try (var applicationArchive = new ApplicationArchive()) {\n            applicationArchive.addProcess(process);\n            applicationArchive.setVersion(\"1.0.1\");\n            applicationInstaller.update(applicationArchive);\n        }\n        // then\n        verify(applicationInstaller).deployProcess(\n                ArgumentMatchers.argThat(b -> b.getProcessDefinition().getName().equals(\"myProcess\")\n                        && b.getProcessDefinition().getVersion().equals(\"1.0\")),\n                any());\n        verify(processDeploymentAPIDelegate).enableProcess(newProcessId);\n    }\n\n    @Test\n    public void should_not_install_process_if_process_with_same_version_exists() throws Exception {\n        // given\n        byte[] barContent = createValidBusinessArchive();\n        File process = createTempFile(\"process\", \"bar\", barContent);\n\n        doReturn(Optional.of(1L)).when(applicationInstaller).getDeployedProcessId(anyString(), eq(\"1.0\"));\n        doReturn(Lists.newArrayList(1L)).when(applicationInstaller).getDeployedProcessIds();\n\n        ProcessDeploymentAPIDelegate processDeploymentAPIDelegate = mock(ProcessDeploymentAPIDelegate.class);\n        doReturn(processDeploymentAPIDelegate).when(applicationInstaller).getProcessDeploymentAPIDelegate();\n\n        // when\n        try (var applicationArchive = new ApplicationArchive()) {\n            applicationArchive.addProcess(process);\n            applicationArchive.setVersion(\"1.0.1\");\n            applicationInstaller.update(applicationArchive);\n        }\n        // then\n        verify(applicationInstaller, never()).deployProcess(any(), any());\n        verify(applicationInstaller, never()).disableProcess(anyLong());\n    }\n\n    @Test\n    public void should_install_and_enable_resolved_process_and_disable_previous_version_existing() throws Exception {\n        // given\n        byte[] barContent = createValidBusinessArchive();\n        File process = createTempFile(\"process\", \"bar\", barContent);\n\n        doReturn(Lists.newArrayList(1L, 2L, 123L)).when(applicationInstaller).getDeployedProcessIds();\n        doNothing().when(applicationInstaller).disableProcess(anyLong());\n\n        ProcessDeploymentInfo deployedProcess1 = spy(ProcessDeploymentInfo.class);\n        doReturn(\"1.1\").when(deployedProcess1).getVersion();\n        doReturn(ActivationState.ENABLED).when(deployedProcess1).getActivationState();\n\n        ProcessDeploymentInfo deployedProcess2 = spy(ProcessDeploymentInfo.class);\n        doReturn(\"1.2\").when(deployedProcess2).getVersion();\n        doReturn(ActivationState.ENABLED).when(deployedProcess2).getActivationState();\n\n        doReturn(deployedProcess1).when(applicationInstaller).getProcessDeploymentInfo(1L);\n        doReturn(deployedProcess2).when(applicationInstaller).getProcessDeploymentInfo(2L);\n\n        doReturn(Optional.empty()).when(applicationInstaller)\n                .getDeployedProcessId(anyString(), any());\n\n        ProcessDeploymentAPIDelegate processDeploymentAPIDelegate = mock(ProcessDeploymentAPIDelegate.class);\n        doReturn(processDeploymentAPIDelegate).when(applicationInstaller).getProcessDeploymentAPIDelegate();\n\n        ProcessDefinition myProcess = aProcessDefinition(123L);\n        doReturn(myProcess).when(processDeploymentAPIDelegate).deploy(any());\n\n        // when\n        try (var applicationArchive = new ApplicationArchive()) {\n            applicationArchive.addProcess(process);\n            applicationArchive.setVersion(\"1.0.1\");\n            applicationInstaller.update(applicationArchive);\n        }\n        // then\n        verify(applicationInstaller).deployProcess(any(), any());\n        verify(applicationInstaller, times(2)).disableProcess(anyLong());\n        verify(applicationInstaller, times(1)).enableResolvedProcesses(eq(List.of(myProcess.getId())), any());\n    }\n\n    @Test\n    public void should_update_bdm() throws Exception {\n        byte[] bdmZipContent = createValidBDMZipFile();\n        File bdm = createTempFile(\"bdm\", \"zip\", bdmZipContent);\n        try (var applicationArchive = new ApplicationArchive()) {\n            applicationArchive.setBdm(bdm);\n            doReturn(\"1.0\").when(applicationInstaller).updateBusinessDataModel(applicationArchive);\n            doNothing().when(applicationInstaller).enableResolvedProcesses(any(), any());\n            doReturn(false).when(bdmRepository).isDeployed(any(byte[].class));\n            doNothing().when(applicationInstaller).disableOldProcesses(any(), any());\n            applicationArchive.setVersion(\"1.0.1\");\n\n            applicationInstaller.update(applicationArchive);\n\n            verify(applicationInstaller).updateBusinessDataModel(applicationArchive);\n        }\n    }\n\n    @Test\n    public void should_call_enable_resolved_processes() throws Exception {\n        byte[] barContent = createValidBusinessArchive();\n        File process = createTempFile(\"process\", \"bar\", barContent);\n\n        doReturn(Lists.newArrayList(123L)).when(applicationInstaller).getDeployedProcessIds();\n        doReturn(Optional.empty()).when(applicationInstaller).getDeployedProcessId(anyString(), eq(\"1.0\"));\n\n        ProcessDeploymentAPIDelegate processDeploymentAPIDelegate = mock(ProcessDeploymentAPIDelegate.class);\n        doReturn(processDeploymentAPIDelegate).when(applicationInstaller).getProcessDeploymentAPIDelegate();\n\n        ProcessDefinition myProcess = aProcessDefinition(123L);\n        doReturn(myProcess).when(processDeploymentAPIDelegate).deploy(any());\n\n        try (var applicationArchive = new ApplicationArchive()) {\n            applicationArchive.addProcess(process);\n            applicationArchive.setVersion(\"1.0.1\");\n            applicationInstaller.update(applicationArchive);\n        }\n\n        verify(applicationInstaller).deployProcess(any(), any());\n        verify(processDeploymentAPIDelegate).deploy(any());\n        verify(applicationInstaller).enableResolvedProcesses(eq(Collections.singletonList(123L)), any());\n    }\n\n    @Test\n    public void should_throw_exception_if_application_archive_is_empty() throws Exception {\n        try (ApplicationArchive applicationArchive = new ApplicationArchive()) {\n            applicationArchive.setVersion(\"1.0.1\");\n            assertThatExceptionOfType(ApplicationInstallationException.class)\n                    .isThrownBy(() -> applicationInstaller.update(applicationArchive))\n                    .withMessage(\"The Application Archive contains no valid artifact to install\");\n        }\n    }\n\n    @Test\n    public void should_update_organisation() throws Exception {\n        File organization = createTempFile(\"org\", \"xml\", \"content\".getBytes());\n        doReturn(emptyList()).when(applicationInstaller).importOrganization(any(), any());\n        doNothing().when(applicationInstaller).enableResolvedProcesses(any(), any());\n        doNothing().when(applicationInstaller).disableOldProcesses(any(), any());\n        try (var applicationArchive = new ApplicationArchive()) {\n            applicationArchive.setOrganization(organization);\n            applicationArchive.setVersion(\"1.0.1\");\n            applicationInstaller.update(applicationArchive);\n        }\n\n        verify(applicationInstaller).importOrganization(organization, ImportPolicy.IGNORE_DUPLICATES);\n    }\n\n    private ProcessDefinitionImpl aProcessDefinition(long id) {\n        ProcessDefinitionImpl myProcess = new ProcessDefinitionImpl(\"myProcess\", \"1.0\");\n        myProcess.setId(id);\n        return myProcess;\n    }\n\n    private byte[] createValidBusinessArchive()\n            throws InvalidBusinessArchiveFormatException, InvalidProcessDefinitionException, IOException {\n        BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(new ProcessDefinitionBuilder().createNewInstance(\"myProcess\", \"1.0\").done())\n                .done();\n        File businessArchiveFile = temporaryFolder.newFile();\n        assert businessArchiveFile.delete();\n        BusinessArchiveFactory.writeBusinessArchiveToFile(businessArchive, businessArchiveFile);\n        return FileOperations.readFully(businessArchiveFile);\n    }\n\n    private byte[] createValidBDMZipFile() throws IOException {\n        return zip(file(\"bom.xml\", (\"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\" standalone=\\\"yes\\\"?>\\n\" +\n                \"<businessObjectModel xmlns=\\\"http://documentation.bonitasoft.com/bdm-xml-schema/1.0\\\" modelVersion=\\\"1.0\\\" productVersion=\\\"8.0.0\\\" />\")\n                .getBytes()));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/application/installer/CustomOrDefaultApplicationInstallerConfigTest.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.application.installer;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\n\nimport org.bonitasoft.engine.business.application.importer.DefaultLivingApplicationImporter;\nimport org.bonitasoft.engine.business.application.importer.MandatoryLivingApplicationImporter;\nimport org.bonitasoft.engine.platform.PlatformService;\nimport org.bonitasoft.engine.tenant.TenantServicesManager;\nimport org.junit.Test;\nimport org.junit.experimental.runners.Enclosed;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.TestPropertySource;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(Enclosed.class)\npublic class CustomOrDefaultApplicationInstallerConfigTest {\n\n    @RunWith(SpringRunner.class)\n    @ContextConfiguration(classes = AbstractConfigTest.TextConfiguration.class)\n    abstract static class AbstractConfigTest {\n\n        @Autowired\n        protected CustomOrDefaultApplicationInstaller installer;\n\n        @Configuration\n        static class TextConfiguration {\n\n            @Bean\n            public CustomOrDefaultApplicationInstaller installer() {\n                return new CustomOrDefaultApplicationInstaller(mock(ApplicationInstallerImpl.class),\n                        mock(DefaultLivingApplicationImporter.class), mock(MandatoryLivingApplicationImporter.class),\n                        mock(TenantServicesManager.class), mock(ApplicationArchiveReader.class),\n                        mock(PlatformService.class));\n            }\n        }\n    }\n\n    public static class CustomOrDefaultApplicationInstallerDefaultConfigTest extends AbstractConfigTest {\n\n        @Test\n        public void should_application_install_folder_have_default_value() {\n            assertThat(installer.getApplicationInstallFolder()).isEqualTo(\"my-application\");\n        }\n    }\n\n    @TestPropertySource(properties = {\n            \"bonita.runtime.custom-application.install-folder=my-carpeta-personalizada\",\n    })\n    public static class CustomOrDefaultApplicationInstallerOverwrittenConfigTest extends AbstractConfigTest {\n\n        @Test\n        public void should_support_application_install_folder_overwrite() {\n            assertThat(installer.getApplicationInstallFolder()).isEqualTo(\"my-carpeta-personalizada\");\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/application/installer/CustomOrDefaultApplicationInstallerTest.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.application.installer;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.Optional;\nimport java.util.concurrent.Callable;\nimport java.util.stream.Stream;\n\nimport org.bonitasoft.engine.business.application.importer.DefaultLivingApplicationImporter;\nimport org.bonitasoft.engine.exception.ApplicationInstallationException;\nimport org.bonitasoft.engine.tenant.TenantServicesManager;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.MethodSource;\nimport org.mockito.*;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport org.mockito.junit.jupiter.MockitoSettings;\nimport org.mockito.quality.Strictness;\nimport org.springframework.core.io.FileSystemResource;\nimport org.springframework.core.io.Resource;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@ExtendWith(MockitoExtension.class)\n@MockitoSettings(strictness = Strictness.LENIENT)\nclass CustomOrDefaultApplicationInstallerTest {\n\n    @Captor\n    ArgumentCaptor<Callable<Object>> callableCaptor;\n\n    @Mock\n    private ApplicationInstallerImpl applicationInstaller;\n    @Mock\n    DefaultLivingApplicationImporter defaultLivingApplicationImporter;\n    @Mock\n    TenantServicesManager tenantServicesManager;\n\n    @Mock\n    private ApplicationArchiveReader applicationArchiveReader;\n    @InjectMocks\n    @Spy\n    private CustomOrDefaultApplicationInstaller listener;\n\n    @BeforeEach\n    void before() throws Exception {\n        doAnswer(inv -> callableCaptor.getValue().call()).when(tenantServicesManager)\n                .inSessionTransaction(callableCaptor.capture());\n    }\n\n    @Test\n    void should_detect_one_custom_application() throws Exception {\n        //given\n        Resource resource1 = mockResource(\"resource1\", true, true, 1L);\n\n        doReturn(new Resource[] { resource1 })\n                .when(listener)\n                .getCustomAppResourcesFromClasspath();\n\n        //when\n        Resource result = listener.detectCustomApplication();\n\n        //then\n        assertThat(result).isNotNull();\n        assertThat(result.getFilename()).isEqualTo(\"resource1\");\n    }\n\n    @Test\n    void should_raise_exception_if_more_than_one_application() throws Exception {\n        //given\n        Resource resource1 = mockResource(\"resource1\", true, true, 1L);\n        Resource resource2 = mockResource(\"resource2\", true, true, 1L);\n\n        doReturn(new Resource[] { resource1, resource2 })\n                .when(listener)\n                .getCustomAppResourcesFromClasspath();\n\n        //then\n        assertThatExceptionOfType(ApplicationInstallationException.class)\n                .isThrownBy(listener::detectCustomApplication)\n                .withMessage(\"More than one resource of type application zip detected. Abort startup.\");\n    }\n\n    @ParameterizedTest\n    @MethodSource(\"ignoredApplicationResources\")\n    void ignoreDetectedCustomApplication(boolean exists, boolean readable, long contentSize) throws Exception {\n        //given\n        Resource resource1 = mockResource(\"resource1\", exists, readable, contentSize);\n\n        doReturn(new Resource[] { resource1 })\n                .when(listener)\n                .getCustomAppResourcesFromClasspath();\n\n        //when\n        Resource result = listener.detectCustomApplication();\n\n        //then\n        assertThat(result).isNull();\n    }\n\n    private static Stream<Arguments> ignoredApplicationResources() {\n        return Stream.of(\n                Arguments.of(false, false, 0L), // resource does not exist\n                Arguments.of(true, false, 1L), // resource is not readable\n                Arguments.of(true, true, 0L) // resource has no content\n        );\n    }\n\n    @Test\n    void should_install_custom_application_if_detected_and_platform_first_init()\n            throws Exception {\n        //given\n        Resource resource1 = mockResource(\"resource1\", true, true, 0L);\n        InputStream resourceStream1 = mock(InputStream.class);\n\n        doReturn(resource1).when(listener).detectCustomApplication();\n        doReturn(resourceStream1).when(resource1).getInputStream();\n        final ApplicationArchive applicationArchive = new ApplicationArchive();\n        applicationArchive.setVersion(\"1.0.0\");\n        doReturn(applicationArchive).when(applicationArchiveReader).read(resourceStream1);\n        doReturn(true).when(listener).isPlatformFirstInitialization();\n        //when\n        listener.autoDeployDetectedCustomApplication(any());\n\n        //then\n        verify(applicationInstaller).install(applicationArchive);\n        verify(defaultLivingApplicationImporter, never()).execute();\n    }\n\n    @Test\n    void should_update_custom_application_if_detected_version_superior_to_deployed_version()\n            throws Exception {\n        //given\n        Resource resource1 = mockResource(\"resource1\", true, true, 0L);\n        InputStream resourceStream1 = mock(InputStream.class);\n\n        doReturn(resource1).when(listener).detectCustomApplication();\n        doReturn(resourceStream1).when(resource1).getInputStream();\n        final ApplicationArchive applicationArchive = new ApplicationArchive();\n        applicationArchive.setVersion(\"1.0.1\");\n        doReturn(applicationArchive).when(applicationArchiveReader).read(resourceStream1);\n        doReturn(false).when(listener).isPlatformFirstInitialization();\n        doReturn(\"1.0.0\").when(listener).getInstalledApplicationVersion();\n        //when\n        listener.autoDeployDetectedCustomApplication(any());\n\n        //then\n        InOrder inOrder = inOrder(defaultLivingApplicationImporter, applicationInstaller);\n        inOrder.verify(applicationInstaller).update(applicationArchive);\n        verify(defaultLivingApplicationImporter, never()).execute();\n    }\n\n    @Test\n    void should_update_conf_if_detected_version_equal_to_deployed_version()\n            throws Exception {\n        //given\n        Resource resource1 = mockResource(\"resource1\", true, true, 0L);\n        doReturn(resource1).when(listener).detectCustomApplication();\n        var applicationArchive = new ApplicationArchive();\n        applicationArchive.setVersion(\"1.0.0\");\n        doReturn(applicationArchive).when(listener).createApplicationArchive(resource1);\n        doReturn(false).when(listener).isPlatformFirstInitialization();\n        doReturn(\"1.0.0\").when(listener).getInstalledApplicationVersion();\n        //when\n        listener.autoDeployDetectedCustomApplication(any());\n\n        //then\n        InOrder inOrder = inOrder(defaultLivingApplicationImporter, applicationInstaller);\n        inOrder.verify(applicationInstaller, never()).update(any());\n        verify(listener, times(1)).findAndUpdateConfiguration();\n        verify(defaultLivingApplicationImporter, never()).execute();\n    }\n\n    @Test\n    void should_throw_an_exception_if_detected_version_inferior_to_deployed_version()\n            throws Exception {\n        //given\n        Resource resource1 = mockResource(\"resource1\", true, true, 0L);\n\n        doReturn(resource1).when(listener).detectCustomApplication();\n        var applicationArchive = new ApplicationArchive();\n        applicationArchive.setVersion(\"0.0.9-SNAPSHOT\");\n        doReturn(applicationArchive).when(listener).createApplicationArchive(resource1);\n        doReturn(false).when(listener).isPlatformFirstInitialization();\n        doReturn(\"1.0.0\").when(listener).getInstalledApplicationVersion();\n        //when\n        String exceptionMessage = Assertions.assertThrows(ApplicationInstallationException.class,\n                () -> listener.autoDeployDetectedCustomApplication(any())).getMessage();\n\n        //then\n        assertThat(exceptionMessage).contains(\n                \"An application has been detected, but its newVersion 0.0.9-SNAPSHOT is inferior to the one deployed: 1.0.0. Nothing will be updated, and the Bonita engine startup has been aborted.\");\n    }\n\n    @Test\n    void should_install_default_applications_if_no_custom_app_detected()\n            throws Exception {\n        //given\n        doReturn(null).when(listener).detectCustomApplication();\n        doReturn(true).when(listener).isPlatformFirstInitialization();\n\n        //when\n        listener.autoDeployDetectedCustomApplication(any());\n\n        //then\n        verify(applicationInstaller, never()).install(any());\n        verify(defaultLivingApplicationImporter).execute();\n    }\n\n    @Test\n    void should_raise_exception_if_more_than_one_configuration_file() throws Exception {\n        //given\n        Resource resource1 = mockResource(\"resource1\", true, true, 1L);\n        Resource resource2 = mockResource(\"resource2\", true, true, 1L);\n\n        doReturn(new Resource[] { resource1, resource2 })\n                .when(listener)\n                .getConfigurationFileResourcesFromClasspath();\n\n        //then\n        assertThatExceptionOfType(ApplicationInstallationException.class)\n                .isThrownBy(listener::detectConfigurationFile)\n                .withMessage(\"More than one resource of type configuration file .bconf detected. Abort startup.\");\n    }\n\n    @Test\n    void should_return_empty_optional_if_no_configuration_file_found() throws Exception {\n        doReturn(null)\n                .when(listener)\n                .getConfigurationFileResourcesFromClasspath();\n\n        listener.detectConfigurationFile();\n\n        //then\n        assertThat(Optional.empty()).isEqualTo(listener.detectConfigurationFile());\n\n    }\n\n    private Resource mockResource(String filename, boolean exists, boolean isReadable, long contentLength)\n            throws IOException {\n        Resource resource = spy(new FileSystemResource(mock(File.class)));\n        doReturn(exists).when(resource).exists();\n        doReturn(isReadable).when(resource).isReadable();\n        doReturn(contentLength).when(resource).contentLength();\n        doReturn(filename).when(resource).getFilename();\n        return resource;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/application/installer/detector/ArtifactTypeDetectorTest.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.application.installer.detector;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.mockito.InjectMocks;\nimport org.mockito.Spy;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\n@ExtendWith(MockitoExtension.class)\nclass ArtifactTypeDetectorTest {\n\n    @Spy\n    @InjectMocks\n    private ArtifactTypeDetector detector;\n\n    @Test\n    void readApplicationPropertiesVersion(@TempDir Path tempFolder) throws IOException {\n        var applicationPropertyFile = tempFolder.resolve(\"application.properties\");\n        Files.writeString(applicationPropertyFile, \"version=1.0.0\");\n\n        var version = detector.readVersion(applicationPropertyFile.toFile());\n\n        assertThat(version).isEqualTo(\"1.0.0\");\n    }\n\n    @Test\n    void readMissingApplicationPropertiesVersion(@TempDir Path tempFolder) throws IOException {\n        var applicationPropertyFile = tempFolder.resolve(\"application.properties\");\n        Files.writeString(applicationPropertyFile, \"\");\n\n        var version = detector.readVersion(applicationPropertyFile.toFile());\n\n        assertThat(version).isNull();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/application/installer/detector/IconDetectorTest.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.application.installer.detector;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.io.File;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nclass IconDetectorTest {\n\n    private IconDetector iconDetector;\n\n    @BeforeEach\n    void setup() throws Exception {\n        iconDetector = new IconDetector();\n    }\n\n    @Test\n    void isCompliant() throws Exception {\n        var pngIcon = new File(IconDetectorTest.class.getResource(\"/icon.png\").getFile());\n        var jpegIcon = new File(IconDetectorTest.class.getResource(\"/icon.JPEG\").getFile());\n\n        assertThat(iconDetector.isCompliant(pngIcon)).isTrue();\n        assertThat(iconDetector.isCompliant(jpegIcon)).isTrue();\n    }\n\n    @Test\n    void isNotCompliant() throws Exception {\n        var icon = new File(IconDetectorTest.class.getResource(\"/icon.docx\").getFile());\n\n        assertThat(iconDetector.isCompliant(icon)).isFalse();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/application/installer/detector/LayoutDetectorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.application.installer.detector;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.io.FileAndContentUtils.file;\nimport static org.bonitasoft.engine.io.FileAndContentUtils.zip;\nimport static org.bonitasoft.engine.io.IOUtils.createTempFile;\n\nimport java.io.File;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.TemporaryFolder;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class LayoutDetectorTest {\n\n    @Rule\n    public TemporaryFolder tempFolder = new TemporaryFolder();\n\n    @Test\n    public void isCompliant_should_reject_layout_if_contentType_is_not_layout() throws Exception {\n        // given:\n        final LayoutDetector detector = new LayoutDetector();\n        byte[] zip = zip(\n                file(\"page.properties\", \"contentType=toto\"),\n                file(\"resources/index.html\", \"anything\"));\n        File tempFile = createTempFile(\"myLayout\", \"zip\", zip);\n\n        // when:\n        final boolean compliant = detector.isCompliant(tempFile);\n\n        // then:\n        assertThat(compliant).isFalse();\n\n        //cleanup\n        tempFile.delete();\n    }\n\n    @Test\n    public void isCompliant_should_reject_layout_if_index_page_not_present() throws Exception {\n        // given:\n        final LayoutDetector detector = new LayoutDetector();\n        byte[] zip = zip(\n                file(\"page.properties\", \"contentType=layout\"),\n                file(\"resources/toto.html\", \"anything\"));\n        File tempFile = createTempFile(\"myLayout\", \"zip\", zip);\n\n        // when:\n        final boolean compliant = detector.isCompliant(tempFile);\n\n        // then:\n        assertThat(compliant).isFalse();\n\n        //cleanup\n        tempFile.delete();\n    }\n\n    @Test\n    public void isCompliant_should_accept_valid_layout() throws Exception {\n        // given:\n        final byte[] zip = zip(file(\"page.properties\", \"name=custompage_test1\\ncontentType=layout\"),\n                file(\"resources/index.html\", \"someContent\"));\n        File tempFile = createTempFile(\"layout\", \"zip\", zip);\n\n        // when:\n        final boolean compliant = new LayoutDetector().isCompliant(tempFile);\n\n        // then:\n        assertThat(compliant).isTrue();\n\n        //cleanup\n        tempFile.delete();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/application/installer/detector/PageAndFormDetectorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.application.installer.detector;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.io.FileAndContentUtils.file;\nimport static org.bonitasoft.engine.io.FileAndContentUtils.zip;\nimport static org.bonitasoft.engine.io.IOUtils.createTempFile;\n\nimport java.io.File;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.TemporaryFolder;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class PageAndFormDetectorTest {\n\n    @Rule\n    public TemporaryFolder tempFolder = new TemporaryFolder();\n\n    @Test\n    public void isCompliant_should_reject_page_if_contentType_is_not_layout() throws Exception {\n        // given:\n        final PageAndFormDetector detector = new PageAndFormDetector();\n        byte[] zip = zip(\n                file(\"page.properties\", \"contentType=toto\"),\n                file(\"resources/index.html\", \"anything\"));\n        File tempFile = createTempFile(\"myPage\", \"zip\", zip);\n\n        // when:\n        final boolean compliant = detector.isCompliant(tempFile);\n\n        // then:\n        assertThat(compliant).isFalse();\n\n        //cleanup\n        tempFile.delete();\n    }\n\n    @Test\n    public void isCompliant_should_reject_page_if_index_page_not_present() throws Exception {\n        // given:\n        final PageAndFormDetector detector = new PageAndFormDetector();\n        byte[] zip = zip(file(\"page.properties\", \"contentType=layout\"));\n        File tempFile = createTempFile(\"myPage\", \"zip\", zip);\n\n        // when:\n        final boolean compliant = detector.isCompliant(tempFile);\n\n        // then:\n        assertThat(compliant).isFalse();\n\n        //cleanup\n        tempFile.delete();\n    }\n\n    @Test\n    public void isCompliant_should_accept_valid_page() throws Exception {\n        // given:\n        byte[] zip = zip(\n                file(\"page.properties\", \"contentType=page\"),\n                file(\"resources/index.html\", \"some HTML content\"));\n        File tempFile = createTempFile(\"myPage\", \"zip\", zip);\n\n        // when:\n        final boolean compliant = new PageAndFormDetector().isCompliant(tempFile);\n\n        // then:\n        assertThat(compliant).isTrue();\n\n        //cleanup\n        tempFile.delete();\n    }\n\n    @Test\n    public void isCompliant_should_accept_valid_form() throws Exception {\n        // given:\n        byte[] zip = zip(\n                file(\"page.properties\", \"contentType=form\"),\n                file(\"resources/Index.groovy\", \"some Groovy content\"));\n        File tempFile = createTempFile(\"myGroovyForm\", \"zip\", zip);\n        // when:\n        final boolean compliant = new PageAndFormDetector().isCompliant(tempFile);\n\n        // then:\n        assertThat(compliant).isTrue();\n\n        //cleanup\n        tempFile.delete();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/application/installer/detector/ThemeDetectorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.application.installer.detector;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.io.FileAndContentUtils.file;\nimport static org.bonitasoft.engine.io.FileAndContentUtils.zip;\nimport static org.bonitasoft.engine.io.IOUtils.createTempFile;\n\nimport java.io.File;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.TemporaryFolder;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class ThemeDetectorTest {\n\n    @Rule\n    public TemporaryFolder tempFolder = new TemporaryFolder();\n\n    @Test\n    public void isCompliant_should_accept_if_theme_css_file_exists() throws Exception {\n        // given:\n        final byte[] content = zip(file(\"page.properties\", \"name=custompage_test1\\ncontentType=theme\"),\n                file(\"resources/theme.css\", \"someContent\"));\n        File tempFile = createTempFile(\"custom-theme\", \"zip\", content);\n\n        // when:\n        final boolean compliant = new ThemeDetector().isCompliant(tempFile);\n\n        // then:\n        assertThat(compliant).isTrue();\n\n        //cleanup\n        tempFile.delete();\n    }\n\n    @Test\n    public void isCompliant_should_reject_if_page_is_not_a_theme() throws Exception {\n        // given:\n        byte[] zip = zip(file(\"page.properties\", \"name=custompage_test1\\ncontentType=layout\"),\n                file(\"resources/index.html\", \"someContent\"));\n        File tempFile = createTempFile(\"layout\", \"zip\", zip);\n\n        // when:\n        final boolean compliant = new ThemeDetector().isCompliant(tempFile);\n\n        // then:\n        assertThat(compliant).isFalse();\n\n        //cleanup\n        tempFile.delete();\n    }\n\n    @Test\n    public void isCompliant_should_reject_theme_css_file_absence() throws Exception {\n        // given:\n        byte[] zip = zip(file(\"page.properties\", \"contentType=theme\"));\n        File tempFile = createTempFile(\"invalid-theme\", \"zip\", zip);\n\n        // when:\n        final boolean compliant = new ThemeDetector().isCompliant(tempFile);\n\n        // then:\n        assertThat(compliant).isFalse();\n\n        //cleanup\n        tempFile.delete();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/connector/ConnectorReseterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.connector;\n\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.*;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorStateReset;\nimport org.bonitasoft.engine.bpm.flownode.ActivityExecutionException;\nimport org.bonitasoft.engine.core.connector.ConnectorInstanceService;\nimport org.bonitasoft.engine.core.connector.exception.SConnectorInstanceModificationException;\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstanceWithFailureInfo;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ConnectorReseterTest {\n\n    @Mock\n    private ConnectorInstanceService connectorInstanceService;\n\n    private ConnectorReseter connectorReseter;\n\n    @Mock\n    private SConnectorInstanceWithFailureInfo connectorInstance;\n\n    @Before\n    public void setUp() throws Exception {\n        connectorReseter = new ConnectorReseter(connectorInstanceService);\n    }\n\n    @Test\n    public void resetState_should_change_the_connector_state_and_clean_stack_trace() throws Exception {\n        //given\n        given(connectorInstance.getStackTrace()).willReturn(\"A stack trace...\");\n\n        //when\n        connectorReseter.resetState(connectorInstance, ConnectorStateReset.SKIPPED);\n\n        //then\n        verify(connectorInstanceService).setState(connectorInstance, ConnectorStateReset.SKIPPED.name());\n        verify(connectorInstanceService).setConnectorInstanceFailureException(connectorInstance, null);\n    }\n\n    @Test\n    public void resetState_should_not_clean_stack_trace_when_current_stack_trace_is_null() throws Exception {\n        //given\n        given(connectorInstance.getStackTrace()).willReturn(null);\n\n        //when\n        connectorReseter.resetState(connectorInstance, ConnectorStateReset.SKIPPED);\n\n        //then\n        verify(connectorInstanceService, never()).setConnectorInstanceFailureException(connectorInstance, null);\n    }\n\n    @Test(expected = ActivityExecutionException.class)\n    public void resetState_should_throw_ActivityExecutionException_when_connectorInstance_service_throws_exception()\n            throws Exception {\n        //given\n        doThrow(new SConnectorInstanceModificationException(new Exception())).when(connectorInstanceService).setState(\n                connectorInstance,\n                ConnectorStateReset.SKIPPED.name());\n\n        //when\n        connectorReseter.resetState(connectorInstance, ConnectorStateReset.SKIPPED);\n\n        //then exception\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/connector/ResetAllFailedConnectorStrategyTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.connector;\n\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\nimport java.util.Arrays;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorState;\nimport org.bonitasoft.engine.bpm.connector.ConnectorStateReset;\nimport org.bonitasoft.engine.bpm.flownode.ActivityExecutionException;\nimport org.bonitasoft.engine.core.connector.ConnectorInstanceService;\nimport org.bonitasoft.engine.core.connector.exception.SConnectorInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstanceWithFailureInfo;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ResetAllFailedConnectorStrategyTest {\n\n    public static final int MAX_RESULTS = 2;\n    @Mock\n    private ConnectorInstanceService connectorInstanceService;\n\n    @Mock\n    private ConnectorReseter connectorReseter;\n\n    private ResetAllFailedConnectorStrategy strategy;\n\n    @Before\n    public void setUp() throws Exception {\n        strategy = new ResetAllFailedConnectorStrategy(connectorInstanceService, connectorReseter, MAX_RESULTS);\n    }\n\n    @Test\n    public void resetConnectorsOf_should_put_all_failed_connectors_in_the_state_TO_RE_EXECUTE() throws Exception {\n        //given\n        SConnectorInstanceWithFailureInfo instance1 = mock(SConnectorInstanceWithFailureInfo.class);\n        SConnectorInstanceWithFailureInfo instance2 = mock(SConnectorInstanceWithFailureInfo.class);\n        SConnectorInstanceWithFailureInfo instance3 = mock(SConnectorInstanceWithFailureInfo.class);\n        given(\n                connectorInstanceService.getConnectorInstancesWithFailureInfo(20L, SConnectorInstance.FLOWNODE_TYPE,\n                        ConnectorState.FAILED.name(), 0,\n                        MAX_RESULTS))\n                .willReturn(Arrays.asList(instance1, instance2));\n        given(\n                connectorInstanceService.getConnectorInstancesWithFailureInfo(20L, SConnectorInstance.FLOWNODE_TYPE,\n                        ConnectorState.FAILED.name(), MAX_RESULTS,\n                        MAX_RESULTS))\n                .willReturn(Arrays.asList(instance3));\n\n        //when\n        strategy.resetConnectorsOf(20L);\n\n        //then\n        verify(connectorReseter).resetState(instance1, ConnectorStateReset.TO_RE_EXECUTE);\n        verify(connectorReseter).resetState(instance2, ConnectorStateReset.TO_RE_EXECUTE);\n        verify(connectorReseter).resetState(instance3, ConnectorStateReset.TO_RE_EXECUTE);\n    }\n\n    @Test(expected = ActivityExecutionException.class)\n    public void resetConnectorsOf_should_throw_ActivityExecution_Exception_when_connectorService_throws_exception()\n            throws Exception {\n        //given\n        given(\n                connectorInstanceService.getConnectorInstancesWithFailureInfo(20L, SConnectorInstance.FLOWNODE_TYPE,\n                        ConnectorState.FAILED.name(), 0,\n                        MAX_RESULTS))\n                .willThrow(new SConnectorInstanceReadException(new Exception()));\n\n        //when\n        strategy.resetConnectorsOf(20L);\n\n        //then exception\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/converter/ApplicationMenuModelConverterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.converter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.business.application.ApplicationMenu;\nimport org.bonitasoft.engine.business.application.ApplicationMenuCreator;\nimport org.bonitasoft.engine.business.application.ApplicationMenuUpdater;\nimport org.bonitasoft.engine.business.application.model.SApplicationMenu;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ApplicationMenuModelConverterTest {\n\n    private ApplicationMenuModelConverter convertor;\n\n    @Before\n    public void setUp() throws Exception {\n        convertor = new ApplicationMenuModelConverter();\n    }\n\n    @Test\n    public void buildSApplicationMenu_should_map_all_creator_fields_and_add_index() throws Exception {\n        //given\n        final ApplicationMenuCreator creator = new ApplicationMenuCreator(4L, \"main\", 20L);\n        creator.setParentId(11);\n\n        //when\n        final SApplicationMenu menu = convertor.buildSApplicationMenu(creator, 1);\n\n        //then\n        assertThat(menu).isNotNull();\n        assertThat(menu.getDisplayName()).isEqualTo(\"main\");\n        assertThat(menu.getApplicationId()).isEqualTo(4);\n        assertThat(menu.getApplicationPageId()).isEqualTo(20);\n        assertThat(menu.getIndex()).isEqualTo(1);\n        assertThat(menu.getParentId()).isEqualTo(11);\n    }\n\n    @Test\n    public void buildSApplicationMenu_should_have_null_parentId_when_not_set_on_creator() throws Exception {\n        //given\n        final ApplicationMenuCreator creator = new ApplicationMenuCreator(4L, \"main\", 20L);\n\n        //when\n        final SApplicationMenu menu = convertor.buildSApplicationMenu(creator, 1);\n\n        //then\n        assertThat(menu).isNotNull();\n        assertThat(menu.getParentId()).isNull();\n    }\n\n    @Test\n    public void toApplicationMenu_should_map_all_server_object_fields_and_set_application_id() throws Exception {\n        //given\n        final long appPageId = 15;\n        final long applicationId = 18;\n        final SApplicationMenu sMenu = new SApplicationMenu(\"main\", applicationId, appPageId, 1);\n        sMenu.setId(3);\n        sMenu.setParentId(21L);\n\n        //when\n        final ApplicationMenu menu = convertor.toApplicationMenu(sMenu);\n\n        //then\n        assertThat(menu).isNotNull();\n        assertThat(menu.getId()).isEqualTo(3);\n        assertThat(menu.getDisplayName()).isEqualTo(\"main\");\n        assertThat(menu.getApplicationPageId()).isEqualTo(15);\n        assertThat(menu.getIndex()).isEqualTo(1);\n        assertThat(menu.getParentId()).isEqualTo(21);\n        assertThat(menu.getApplicationId()).isEqualTo(applicationId);\n    }\n\n    @Test\n    public void toApplicationMenu_list_should_call_toApplitionMenu_for_each_element_in_the_list_and_return_the_list_of_converted_values()\n            throws Exception {\n        //given\n        final SApplicationMenu sMenu1 = mock(SApplicationMenu.class);\n        final SApplicationMenu sMenu2 = mock(SApplicationMenu.class);\n        final ApplicationMenu menu1 = mock(ApplicationMenu.class);\n        final ApplicationMenu menu2 = mock(ApplicationMenu.class);\n        final ApplicationMenuModelConverter convertorMock = spy(convertor);\n        doReturn(menu1).when(convertorMock).toApplicationMenu(sMenu1);\n        doReturn(menu2).when(convertorMock).toApplicationMenu(sMenu2);\n\n        //when\n        final List<ApplicationMenu> applicationMenus = convertorMock.toApplicationMenu(Arrays.asList(sMenu1, sMenu2));\n\n        //then\n        assertThat(applicationMenus).containsExactly(menu1, menu2);\n    }\n\n    @Test\n    public void toApplicationMenuUpdateDescriptor_should_map_all_fields() throws Exception {\n        ApplicationMenuUpdater updater = new ApplicationMenuUpdater();\n        updater.setApplicationPageId(1L);\n        updater.setDisplayName(\"disp\");\n        updater.setIndex(2);\n        updater.setParentId(3L);\n\n        EntityUpdateDescriptor updateDescriptor = convertor.toApplicationMenuUpdateDescriptor(updater);\n        assertThat(updateDescriptor).isNotNull();\n        assertThat(updateDescriptor.getFields()).hasSize(4);\n        assertThat(updateDescriptor.getFields().get(SApplicationMenu.APPLICATION_PAGE_ID)).isEqualTo(1L);\n        assertThat(updateDescriptor.getFields().get(SApplicationMenu.DISPLAY_NAME)).isEqualTo(\"disp\");\n        assertThat(updateDescriptor.getFields().get(SApplicationMenu.INDEX)).isEqualTo(2);\n        assertThat(updateDescriptor.getFields().get(SApplicationMenu.PARENT_ID)).isEqualTo(3L);\n\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/converter/ApplicationModelConverterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.converter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\nimport java.nio.charset.StandardCharsets;\nimport java.util.Arrays;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.business.application.*;\nimport org.bonitasoft.engine.business.application.model.AbstractSApplication;\nimport org.bonitasoft.engine.business.application.model.SApplication;\nimport org.bonitasoft.engine.business.application.model.SApplicationState;\nimport org.bonitasoft.engine.business.application.model.SApplicationWithIcon;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.page.PageService;\nimport org.bonitasoft.engine.page.SPage;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ApplicationModelConverterTest {\n\n    private static final String ICON_PATH = \"/icon.jpg\";\n    private static final long TENANT_ID = 1;\n    private static final long ID = 11;\n    private static final long CREATOR_ID = 16;\n    private static final long HOME_PAGE_ID = 130;\n    private static final long PROFILE_ID = 40;\n    private static final String APP_DESC = \"app desc\";\n    private static final String APP_VERSION = \"1.0\";\n    private static final String APP_NAME = \"app\";\n    private static final String APP_DISPLAY_NAME = \"My application\";\n    private static final long LOGGED_USER_ID = 10;\n    public static final long LAYOUT_ID = 55L;\n    public static final long THEME_ID = 56L;\n    public static final String APP_NAME2 = \"app2\";\n    private static final String ICON_MIME_TYPE = \"app mime_type\";\n    private static final byte[] ICON_CONTENT = \"app_icon_content\".getBytes(StandardCharsets.UTF_8);\n\n    @Mock\n    private PageService pageService;\n    @Mock\n    private SPage defaultLayout;\n    @Mock\n    private SPage defaultTheme;\n\n    @InjectMocks\n    private ApplicationModelConverter converter;\n\n    @Before\n    public void before() throws Exception {\n        given(defaultLayout.getId()).willReturn(LAYOUT_ID);\n        given(pageService.getPageByName(ApplicationService.DEFAULT_LAYOUT_NAME)).willReturn(defaultLayout);\n        given(defaultTheme.getId()).willReturn(THEME_ID);\n        given(pageService.getPageByName(ApplicationService.DEFAULT_THEME_NAME)).willReturn(defaultTheme);\n    }\n\n    @Test\n    public void buildSApplication_should_map_all_information_from_creator_and_initialize_mandatory_fields()\n            throws Exception {\n        //given\n        final ApplicationCreator creator = new ApplicationCreator(APP_NAME, APP_DISPLAY_NAME, APP_VERSION);\n        creator.setDescription(APP_DESC);\n        creator.setIconPath(ICON_PATH);\n        creator.setProfileId(PROFILE_ID);\n        creator.setIcon(\"myIcon.jpg\", ICON_CONTENT);\n        final long userId = 10;\n        final long before = System.currentTimeMillis();\n\n        //when\n        final SApplicationWithIcon application = converter.buildSApplication(creator, userId);\n\n        //then\n        assertThat(application).isNotNull();\n        assertThat(application.getToken()).isEqualTo(APP_NAME);\n        assertThat(application.getDisplayName()).isEqualTo(APP_DISPLAY_NAME);\n        assertThat(application.getVersion()).isEqualTo(APP_VERSION);\n        assertThat(application.getDescription()).isEqualTo(APP_DESC);\n        assertThat(application.getIconPath()).isEqualTo(ICON_PATH);\n        assertThat(application.getCreatedBy()).isEqualTo(userId);\n        assertThat(application.getCreationDate()).isGreaterThanOrEqualTo(before);\n        assertThat(application.getUpdatedBy()).isEqualTo(userId);\n        assertThat(application.getLastUpdateDate()).isEqualTo(application.getCreationDate());\n        assertThat(application.getState()).isEqualTo(SApplicationState.ACTIVATED.name());\n        assertThat(application.getProfileId()).isEqualTo(PROFILE_ID);\n        assertThat(application.getLayoutId()).isEqualTo(LAYOUT_ID);\n        assertThat(application.getThemeId()).isEqualTo(THEME_ID);\n        assertThat(application.getIconContent()).isEqualTo(ICON_CONTENT);\n        assertThat(application.getIconMimeType()).isEqualTo(\"image/jpeg\");\n        assertThat(application.hasIcon()).isTrue();\n    }\n\n    @Test\n    public void should_not_have_icon_when_filename_is_empty() throws Exception {\n        ApplicationCreator creator = new ApplicationCreator(APP_NAME, APP_DISPLAY_NAME, APP_VERSION);\n        creator.setIcon(\"\", ICON_CONTENT);\n\n        SApplicationWithIcon application = converter.buildSApplication(creator, 10);\n\n        assertThat(application.getIconContent()).isNull();\n        assertThat(application.getIconMimeType()).isNull();\n        assertThat(application.hasIcon()).isFalse();\n    }\n\n    @Test\n    public void should_not_have_icon_when_filename_is_null_but_content_is_set() throws Exception {\n        ApplicationCreator creator = new ApplicationCreator(APP_NAME, APP_DISPLAY_NAME, APP_VERSION);\n        creator.setIcon(null, ICON_CONTENT);\n\n        SApplicationWithIcon application = converter.buildSApplication(creator, 10);\n\n        assertThat(application.getIconContent()).isNull();\n        assertThat(application.getIconMimeType()).isNull();\n        assertThat(application.hasIcon()).isFalse();\n    }\n\n    @Test\n    public void should_not_have_icon_when_content_is_empty() throws Exception {\n        ApplicationCreator creator = new ApplicationCreator(APP_NAME, APP_DISPLAY_NAME, APP_VERSION);\n        creator.setIcon(\"someIcon.png\", new byte[] {});\n\n        SApplicationWithIcon application = converter.buildSApplication(creator, 10);\n\n        assertThat(application.getIconContent()).isNull();\n        assertThat(application.getIconMimeType()).isNull();\n        assertThat(application.hasIcon()).isFalse();\n    }\n\n    @Test\n    public void should_have_correct_mime_type_when_filename_is_invalid() throws Exception {\n        final ApplicationCreator creator = new ApplicationCreator(APP_NAME, APP_DISPLAY_NAME, APP_VERSION);\n        creator.setIcon(\"nodotfile\", ICON_CONTENT);\n\n        assertThatThrownBy(() -> converter.buildSApplication(creator, 10)).isInstanceOf(IllegalArgumentException.class)\n                .hasMessage(\"An icon can't have mimetype application/octet-stream\");\n\n    }\n\n    @Test(expected = CreationException.class)\n    public void buildSApplication_should_throw_CreationException_when_the_default_page_layout_is_not_available()\n            throws Exception {\n        //given\n        final ApplicationCreator creator = new ApplicationCreator(APP_NAME, APP_DISPLAY_NAME, APP_VERSION);\n        final long userId = 10;\n\n        given(pageService.getPageByName(ApplicationService.DEFAULT_LAYOUT_NAME)).willReturn(null);\n\n        //when\n        converter.buildSApplication(creator, userId);\n\n        //then exception\n    }\n\n    @Test(expected = CreationException.class)\n    public void buildSApplication_should_throw_CreationException_when_the_default_theme_is_not_available()\n            throws Exception {\n        //given\n        final ApplicationCreator creator = new ApplicationCreator(APP_NAME, APP_DISPLAY_NAME, APP_VERSION);\n        final long userId = 10;\n\n        SPage layout = mock(SPage.class);\n        given(layout.getId()).willReturn(LAYOUT_ID);\n        given(pageService.getPageByName(ApplicationService.DEFAULT_LAYOUT_NAME)).willReturn(layout);\n\n        given(pageService.getPageByName(ApplicationService.DEFAULT_THEME_NAME)).willReturn(null);\n\n        //when\n        converter.buildSApplication(creator, userId);\n\n        //then exception\n    }\n\n    @Test\n    public void toApplication_must_map_all_server_fields() throws Exception {\n        //given\n        final long currentDate = System.currentTimeMillis();\n        final String state = SApplicationState.DEACTIVATED.name();\n        final SApplication sApp = new SApplication(APP_NAME, APP_DISPLAY_NAME, APP_VERSION,\n                currentDate, CREATOR_ID, state);\n        sApp.setDescription(APP_DESC);\n        sApp.setId(ID);\n        sApp.setIconPath(ICON_PATH);\n        sApp.setHomePageId(HOME_PAGE_ID);\n        sApp.setProfileId(PROFILE_ID);\n        sApp.setThemeId(THEME_ID);\n        sApp.setLayoutId(LAYOUT_ID);\n        sApp.setIconMimeType(ICON_MIME_TYPE);\n        sApp.setEditable(false);\n        InternalProfiles internalProfileAll = InternalProfiles.INTERNAL_PROFILE_ALL;\n        sApp.setInternalProfile(internalProfileAll.getProfileName());\n\n        //when\n        final Application application = (Application) converter.toApplication(sApp);\n\n        //then\n        assertThat(application).isNotNull();\n        assertThat(application.getId()).isEqualTo(ID);\n        assertThat(application.getToken()).isEqualTo(APP_NAME);\n        assertThat(application.getDisplayName()).isEqualTo(APP_DISPLAY_NAME);\n        assertThat(application.getVersion()).isEqualTo(APP_VERSION);\n        assertThat(application.getDescription()).isEqualTo(APP_DESC);\n        assertThat(application.getIconPath()).isEqualTo(ICON_PATH);\n        assertThat(application.getCreatedBy()).isEqualTo(CREATOR_ID);\n        assertThat(application.getCreationDate()).isEqualTo(new Date(currentDate));\n        assertThat(application.getUpdatedBy()).isEqualTo(CREATOR_ID);\n        assertThat(application.getLastUpdateDate()).isEqualTo(new Date(currentDate));\n        assertThat(application.getState()).isEqualTo(state);\n        assertThat(application.getHomePageId()).isEqualTo(HOME_PAGE_ID);\n        assertThat(application.getProfileId()).isEqualTo(PROFILE_ID);\n        assertThat(application.getLayoutId()).isEqualTo(LAYOUT_ID);\n        assertThat(application.getThemeId()).isEqualTo(THEME_ID);\n        assertThat(application.isEditable()).isFalse();\n        assertThat(application.getVisibility()).isEqualTo(internalProfileAll.getApplicationVisibility());\n        assertThat(application.hasIcon()).isTrue();\n\n    }\n\n    @Test\n    public void toApplication_must_set_false_when_application_icon_mime_type_is_null() throws Exception {\n        //given\n        final long currentDate = System.currentTimeMillis();\n        final String state = SApplicationState.DEACTIVATED.name();\n        final SApplication sApp = new SApplication(APP_NAME, APP_DISPLAY_NAME, APP_VERSION, currentDate, CREATOR_ID,\n                state);\n\n        //when\n        final Application application = (Application) converter.toApplication(sApp);\n\n        //then\n        assertThat(application.hasIcon()).isFalse();\n\n    }\n\n    @Test\n    public void toApplicationList_should_call_toApplication_for_each_element_in_the_list_and_return_the_list_of_converted_values()\n            throws Exception {\n        //given\n        final SApplication sApp1 = new SApplication(APP_NAME, APP_DISPLAY_NAME, APP_VERSION, System.currentTimeMillis(),\n                CREATOR_ID, SApplicationState.DEACTIVATED.name());\n        final SApplication sApp2 = new SApplication(APP_NAME2, \" my app2\", APP_VERSION, System.currentTimeMillis(),\n                CREATOR_ID, SApplicationState.DEACTIVATED.name());\n\n        //when\n        final List<IApplication> applications = converter.toApplication(Arrays.<SApplication> asList(sApp1, sApp2));\n\n        //then\n        assertThat(applications).extracting(\"token\").containsExactly(APP_NAME, APP_NAME2);\n    }\n\n    @Test\n    public void toApplicationUpdateDescriptor_should_map_all_fields() throws Exception {\n        //given\n        final ApplicationUpdater updater = new ApplicationUpdater();\n        updater.setToken(\"My-updated-app\");\n        updater.setDisplayName(\"Updated display name\");\n        updater.setVersion(\"1.1\");\n        updater.setDescription(\"Up description\");\n        updater.setIconPath(\"/newIcon.jpg\");\n        updater.setProfileId(10L);\n        updater.setState(ApplicationState.ACTIVATED.name());\n        updater.setHomePageId(11L);\n\n        //when\n        final EntityUpdateDescriptor updateDescriptor = converter.toApplicationUpdateDescriptor(updater,\n                LOGGED_USER_ID);\n\n        //then\n        assertThat(updateDescriptor).isNotNull();\n        final Map<String, Object> fields = updateDescriptor.getFields();\n        assertThat(fields).hasSize(10); // field lastUpdateDate cannot be checked:\n        assertThat(fields).contains(\n                entry(AbstractSApplication.TOKEN, \"My-updated-app\"),\n                entry(AbstractSApplication.DISPLAY_NAME, \"Updated display name\"),\n                entry(AbstractSApplication.VERSION, \"1.1\"),\n                entry(AbstractSApplication.DESCRIPTION, \"Up description\"),\n                entry(AbstractSApplication.ICON_PATH, \"/newIcon.jpg\"),\n                entry(AbstractSApplication.PROFILE_ID, 10L),\n                entry(AbstractSApplication.STATE, ApplicationState.ACTIVATED.name()),\n                entry(AbstractSApplication.UPDATED_BY, LOGGED_USER_ID),\n                entry(AbstractSApplication.HOME_PAGE_ID, 11L));\n    }\n\n    @Test\n    public void toApplicationUpdateDescriptor_should_set_iconFileName_to_null_when_empty() throws Exception {\n        ApplicationUpdater updater = new ApplicationUpdater();\n        updater.setIcon(\"\", null);\n\n        EntityUpdateDescriptor updateDescriptor = converter.toApplicationUpdateDescriptor(updater,\n                LOGGED_USER_ID);\n\n        assertThat(updateDescriptor.getFields()).contains(entry(\"iconMimeType\", null));\n    }\n\n    @Test\n    public void toApplicationUpdateDescriptor_should_fail_to_update_icon_filename_when_invalid() throws Exception {\n        ApplicationUpdater updater = new ApplicationUpdater();\n        updater.setIcon(\"someFile.exe\", \"content\".getBytes(StandardCharsets.UTF_8));\n\n        assertThatThrownBy(() -> converter.toApplicationUpdateDescriptor(updater,\n                LOGGED_USER_ID)).isInstanceOf(IllegalArgumentException.class)\n                .hasMessageContaining(\"An icon can't have mimetype\");\n    }\n\n    @Test\n    public void toApplicationUpdateDescriptor_should_return_empty_map_if_no_field_is_updated() throws Exception {\n        //given\n        final ApplicationUpdater updater = new ApplicationUpdater();\n\n        //when\n        final EntityUpdateDescriptor updateDescriptor = converter.toApplicationUpdateDescriptor(updater,\n                LOGGED_USER_ID);\n\n        //then\n        assertThat(updateDescriptor).isNotNull();\n        final Map<String, Object> fields = updateDescriptor.getFields();\n        assertThat(fields).hasSize(2);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/converter/ApplicationPageModelConverterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.converter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.spy;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.business.application.ApplicationPage;\nimport org.bonitasoft.engine.business.application.impl.ApplicationPageImpl;\nimport org.bonitasoft.engine.business.application.model.SApplicationPage;\nimport org.junit.Test;\n\npublic class ApplicationPageModelConverterTest {\n\n    private static final long ID = 11;\n    private static final long APPLICATION_ID = 20;\n    private static final long PAGE_ID = 30;\n    private static final String APP_PAGE_NAME = \"firstPage\";\n    private final ApplicationPageModelConverter convertor = new ApplicationPageModelConverter();\n\n    @Test\n    public void toApplicationPage_should_map_all_server_fields() throws Exception {\n        //given\n        final SApplicationPage sAppPage = new SApplicationPage(APPLICATION_ID, PAGE_ID, APP_PAGE_NAME);\n        sAppPage.setId(ID);\n\n        //when\n        final ApplicationPage appPage = convertor.toApplicationPage(sAppPage);\n\n        //then\n        assertThat(appPage).isNotNull();\n        assertThat(appPage.getId()).isEqualTo(ID);\n        assertThat(appPage.getApplicationId()).isEqualTo(APPLICATION_ID);\n        assertThat(appPage.getPageId()).isEqualTo(PAGE_ID);\n        assertThat(appPage.getToken()).isEqualTo(APP_PAGE_NAME);\n    }\n\n    @Test\n    public void toApplicationPageList_should_call_toApplitionPage_for_each_element_and_return_the_list_of_converted_values()\n            throws Exception {\n        //given\n        final SApplicationPage sAppPage1 = new SApplicationPage(10, 21, \"appPage1\");\n        final SApplicationPage sAppPage2 = new SApplicationPage(10, 21, \"appPage2\");\n        final ApplicationPageImpl appPage1 = new ApplicationPageImpl(10, 21, \"appPage1\");\n        final ApplicationPageImpl appPage2 = new ApplicationPageImpl(10, 21, \"appPage2\");\n        final ApplicationPageModelConverter convertorMock = spy(convertor);\n        doReturn(appPage1).when(convertorMock).toApplicationPage(sAppPage1);\n        doReturn(appPage2).when(convertorMock).toApplicationPage(sAppPage2);\n\n        //when\n        final List<ApplicationPage> applicationPages = convertorMock\n                .toApplicationPage(Arrays.<SApplicationPage> asList(sAppPage1, sAppPage2));\n\n        //then\n        assertThat(applicationPages).containsExactly(appPage1, appPage2);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/converter/PageAssert.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.converter;\n\nimport static java.lang.String.format;\n\nimport java.util.Date;\n\nimport org.assertj.core.api.AbstractAssert;\nimport org.bonitasoft.engine.page.Page;\n\n/**\n * {@link org.bonitasoft.engine.page.Page} specific assertions - Generated by CustomAssertionGenerator.\n */\npublic class PageAssert extends AbstractAssert<PageAssert, Page> {\n\n    /**\n     * Creates a new </code>{@link PageAssert}</code> to make assertions on actual Page.\n     *\n     * @param actual the Page we want to make assertions on.\n     */\n    public PageAssert(Page actual) {\n        super(actual, PageAssert.class);\n    }\n\n    /**\n     * An entry point for PageAssert to follow AssertJ standard <code>assertThat()</code> statements.<br>\n     * With a static import, one's can write directly : <code>assertThat(myPage)</code> and get specific assertion with\n     * code completion.\n     *\n     * @param actual the Page we want to make assertions on.\n     * @return a new </code>{@link PageAssert}</code>\n     */\n    public static PageAssert assertThat(Page actual) {\n        return new PageAssert(actual);\n    }\n\n    /**\n     * Verifies that the actual Page's contentName is equal to the given one.\n     *\n     * @param contentName the given contentName to compare the actual Page's contentName to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual Page's contentName is not equal to the given one.\n     */\n    public PageAssert hasContentName(String contentName) {\n        // check that actual Page we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> contentName to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                contentName, actual.getContentName());\n\n        // check\n        if (!actual.getContentName().equals(contentName)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual Page's contentType is equal to the given one.\n     *\n     * @param contentType the given contentType to compare the actual Page's contentType to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual Page's contentType is not equal to the given one.\n     */\n    public PageAssert hasContentType(String contentType) {\n        // check that actual Page we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> contentType to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                contentType, actual.getContentType());\n\n        // check\n        if (!actual.getContentType().equals(contentType)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual Page's description is equal to the given one.\n     *\n     * @param description the given description to compare the actual Page's description to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual Page's description is not equal to the given one.\n     */\n    public PageAssert hasDescription(String description) {\n        // check that actual Page we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> description to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                description, actual.getDescription());\n\n        // check\n        if (!actual.getDescription().equals(description)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual Page's displayName is equal to the given one.\n     *\n     * @param displayName the given displayName to compare the actual Page's displayName to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual Page's displayName is not equal to the given one.\n     */\n    public PageAssert hasDisplayName(String displayName) {\n        // check that actual Page we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> displayName to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                displayName, actual.getDisplayName());\n\n        // check\n        if (!actual.getDisplayName().equals(displayName)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual Page's installationDate is equal to the given one.\n     *\n     * @param installationDate the given installationDate to compare the actual Page's installationDate to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual Page's installationDate is not equal to the given one.\n     */\n    public PageAssert hasInstallationDate(Date installationDate) {\n        // check that actual Page we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> installationDate to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                installationDate, actual.getInstallationDate());\n\n        // check\n        if (!actual.getInstallationDate().equals(installationDate)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual Page's installedBy is equal to the given one.\n     *\n     * @param installedBy the given installedBy to compare the actual Page's installedBy to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual Page's installedBy is not equal to the given one.\n     */\n    public PageAssert hasInstalledBy(long installedBy) {\n        // check that actual Page we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> installedBy to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                installedBy, actual.getInstalledBy());\n\n        // check\n        if (actual.getInstalledBy() != installedBy) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual Page's lastModificationDate is equal to the given one.\n     *\n     * @param lastModificationDate the given lastModificationDate to compare the actual Page's lastModificationDate to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual Page's lastModificationDate is not equal to the given one.\n     */\n    public PageAssert hasLastModificationDate(Date lastModificationDate) {\n        // check that actual Page we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> lastModificationDate to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                lastModificationDate, actual.getLastModificationDate());\n\n        // check\n        if (!actual.getLastModificationDate().equals(lastModificationDate)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual Page's lastUpdatedBy is equal to the given one.\n     *\n     * @param lastUpdatedBy the given lastUpdatedBy to compare the actual Page's lastUpdatedBy to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual Page's lastUpdatedBy is not equal to the given one.\n     */\n    public PageAssert hasLastUpdatedBy(long lastUpdatedBy) {\n        // check that actual Page we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> lastUpdatedBy to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                lastUpdatedBy, actual.getLastUpdatedBy());\n\n        // check\n        if (actual.getLastUpdatedBy() != lastUpdatedBy) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual Page's name is equal to the given one.\n     *\n     * @param name the given name to compare the actual Page's name to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual Page's name is not equal to the given one.\n     */\n    public PageAssert hasName(String name) {\n        // check that actual Page we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> name to be:\\n  <%s>\\n but was:\\n  <%s>\", actual, name,\n                actual.getName());\n\n        // check\n        if (!actual.getName().equals(name)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual Page's processDefinitionId is equal to the given one.\n     *\n     * @param processDefinitionId the given processDefinitionId to compare the actual Page's processDefinitionId to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual Page's processDefinitionId is not equal to the given one.\n     */\n    public PageAssert hasProcessDefinitionId(Long processDefinitionId) {\n        // check that actual Page we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> processDefinitionId to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                processDefinitionId, actual.getProcessDefinitionId());\n\n        // check\n        if (!actual.getProcessDefinitionId().equals(processDefinitionId)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual Page is provided.\n     *\n     * @return this assertion object.\n     * @throws AssertionError - if the actual Page is not provided.\n     */\n    public PageAssert isProvided() {\n        // check that actual Page we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"Expected actual Page to be provided but was not.\", actual);\n\n        // check\n        if (!actual.isProvided())\n            throw new AssertionError(errorMessage);\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual Page is not provided.\n     *\n     * @return this assertion object.\n     * @throws AssertionError - if the actual Page is provided.\n     */\n    public PageAssert isNotProvided() {\n        // check that actual Page we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"Expected actual Page not to be provided but was.\", actual);\n\n        // check\n        if (actual.isProvided())\n            throw new AssertionError(errorMessage);\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/converter/PageModelConverterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.converter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.page.ContentType;\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.engine.page.PageCreator;\nimport org.bonitasoft.engine.page.PageUpdater;\nimport org.bonitasoft.engine.page.SContentType;\nimport org.bonitasoft.engine.page.SPage;\nimport org.junit.Test;\n\npublic class PageModelConverterTest {\n\n    private static final String ZIP_FILE_NAME = \"content.zip\";\n\n    private static final String NAME = \"page name\";\n\n    private static final String DESCRIPTION = \"description\";\n\n    private static final boolean PROVIDED = true;\n\n    private static final String DISPLAY_NAME = \"display name\";\n\n    public static final long PROCESS_DEFINITION_ID = 456789L;\n    public static final long INSTALLATION_DATE = 1L;\n    public static final long LAST_MODIFICATION_DATE = 2L;\n    public static final String CONTENT_ZIP = \"content.zip\";\n    private static final long LAST_UPDATED_BY = 3L;\n    public static final long INSTALLED_BY = 4L;\n    public static final long TENANT_ID = 5L;\n    public static final long ID = 6L;\n\n    @Test\n    public void should_construct_SPage_with_pageCreator() {\n        //given\n        PageModelConverter pageModelConverter = new PageModelConverter();\n        final PageCreator pageCreator = new PageCreator(NAME, ZIP_FILE_NAME, ContentType.FORM, PROCESS_DEFINITION_ID)\n                .setDisplayName(DISPLAY_NAME)\n                .setDescription(DESCRIPTION);\n\n        //when\n        final SPage sPage = pageModelConverter.constructSPage(pageCreator, LAST_UPDATED_BY);\n\n        //then\n        SPageAssert.assertThat(sPage)\n                .hasName(NAME)\n                .hasDisplayName(DISPLAY_NAME)\n                .hasDescription(DESCRIPTION)\n                .hasContentName(ZIP_FILE_NAME)\n                .hasContentType(SContentType.FORM)\n                .hasProcessDefinitionId(PROCESS_DEFINITION_ID)\n                .isNotProvided()\n                .hasLastUpdatedBy(LAST_UPDATED_BY);\n    }\n\n    @Test\n    public void should_construct_SPage_with_pageUpdater() {\n        //given\n        PageModelConverter pageModelConverter = new PageModelConverter();\n        final PageUpdater pageUpdater = new PageUpdater().setName(NAME).setContentName(ZIP_FILE_NAME)\n                .setDisplayName(DISPLAY_NAME).setDescription(DESCRIPTION)\n                .setContentType(ContentType.PAGE).setProcessDefinitionId(PROCESS_DEFINITION_ID);\n\n        //when\n        final SPage sPage = pageModelConverter.constructSPage(pageUpdater, LAST_UPDATED_BY);\n\n        //then\n        SPageAssert.assertThat(sPage)\n                .hasName(NAME)\n                .hasDisplayName(DISPLAY_NAME)\n                .hasDescription(DESCRIPTION)\n                .hasContentName(ZIP_FILE_NAME)\n                .hasContentType(SContentType.PAGE)\n                .hasProcessDefinitionId(PROCESS_DEFINITION_ID)\n                .isNotProvided()\n                .hasLastUpdatedBy(LAST_UPDATED_BY);\n\n    }\n\n    @Test\n    public void should_constructSPage_with_null_processDefinitionId_set_it_to_0() {\n        //given\n        PageModelConverter pageModelConverter = new PageModelConverter();\n        final PageUpdater pageUpdater = new PageUpdater().setName(NAME).setContentName(ZIP_FILE_NAME)\n                .setDisplayName(DISPLAY_NAME).setDescription(DESCRIPTION)\n                .setContentType(ContentType.PAGE).setProcessDefinitionId(null);\n\n        //when\n        final SPage sPage = pageModelConverter.constructSPage(pageUpdater, LAST_UPDATED_BY);\n\n        //then\n        SPageAssert.assertThat(sPage).hasName(NAME).hasProcessDefinitionId(0);\n    }\n\n    @Test\n    public void should_construct_Page_with_SPage() {\n        //given\n        PageModelConverter pageModelConverter = new PageModelConverter();\n        final SPage sPage = new SPage(NAME, DESCRIPTION, DISPLAY_NAME, INSTALLATION_DATE, INSTALLED_BY, PROVIDED,\n                LAST_MODIFICATION_DATE,\n                LAST_UPDATED_BY,\n                CONTENT_ZIP);\n        sPage.setContentType(SContentType.FORM);\n        sPage.setProcessDefinitionId(PROCESS_DEFINITION_ID);\n\n        //when\n        final Page page = pageModelConverter.toPage(sPage);\n\n        //then\n        PageAssert.assertThat(page)\n                .hasName(NAME)\n                .hasDisplayName(DISPLAY_NAME)\n                .hasDescription(DESCRIPTION)\n                .hasContentName(ZIP_FILE_NAME)\n                .hasContentType(ContentType.FORM)\n                .hasProcessDefinitionId(PROCESS_DEFINITION_ID)\n                .isProvided()\n                .hasLastUpdatedBy(LAST_UPDATED_BY);\n\n    }\n\n    @Test\n    public void should_construct_Page_with_SPage_with_no_processDefinitionId() {\n        //given\n        PageModelConverter pageModelConverter = new PageModelConverter();\n        final SPage sPage = new SPage(NAME, DESCRIPTION, DISPLAY_NAME, INSTALLATION_DATE, INSTALLED_BY, PROVIDED,\n                LAST_MODIFICATION_DATE,\n                LAST_UPDATED_BY,\n                CONTENT_ZIP);\n        //when\n        final Page page = pageModelConverter.toPage(sPage);\n        //then\n        assertThat(page.getProcessDefinitionId()).isNull();\n    }\n\n    @Test\n    public void should_construct_Page_with_SPage_list() {\n        //given\n        PageModelConverter pageModelConverter = new PageModelConverter();\n        final SPage sPage = new SPage(NAME, DESCRIPTION, DISPLAY_NAME, INSTALLATION_DATE, INSTALLED_BY, PROVIDED,\n                LAST_MODIFICATION_DATE,\n                LAST_UPDATED_BY,\n                CONTENT_ZIP);\n        List<SPage> sPages = new ArrayList<>();\n        sPages.add(sPage);\n\n        //when\n        final Page page = pageModelConverter.toPage(sPage);\n        final List<Page> pages = pageModelConverter.toPages(sPages);\n\n        //then\n        assertThat(pages).hasSize(1);\n        assertThat(pages.get(0)).isEqualToComparingFieldByField(page);\n\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/converter/SPageAssert.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.converter;\n\nimport static java.lang.String.format;\n\nimport org.assertj.core.api.AbstractAssert;\nimport org.bonitasoft.engine.page.SPage;\n\n/**\n * {@link SPage} specific assertions - Generated by CustomAssertionGenerator.\n */\npublic class SPageAssert extends AbstractAssert<SPageAssert, SPage> {\n\n    /**\n     * Creates a new </code>{@link SPageAssert}</code> to make assertions on actual SPage.\n     *\n     * @param actual the SPage we want to make assertions on.\n     */\n    public SPageAssert(SPage actual) {\n        super(actual, SPageAssert.class);\n    }\n\n    /**\n     * An entry point for SPageAssert to follow AssertJ standard <code>assertThat()</code> statements.<br>\n     * With a static import, one's can write directly : <code>assertThat(mySPage)</code> and get specific assertion with\n     * code completion.\n     *\n     * @param actual the SPage we want to make assertions on.\n     * @return a new </code>{@link SPageAssert}</code>\n     */\n    public static SPageAssert assertThat(SPage actual) {\n        return new SPageAssert(actual);\n    }\n\n    /**\n     * Verifies that the actual SPage's contentName is equal to the given one.\n     *\n     * @param contentName the given contentName to compare the actual SPage's contentName to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPage's contentName is not equal to the given one.\n     */\n    public SPageAssert hasContentName(String contentName) {\n        // check that actual SPage we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> contentName to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                contentName, actual.getContentName());\n\n        // check\n        if (!actual.getContentName().equals(contentName)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual SPage's contentType is equal to the given one.\n     *\n     * @param contentType the given contentType to compare the actual SPage's contentType to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPage's contentType is not equal to the given one.\n     */\n    public SPageAssert hasContentType(String contentType) {\n        // check that actual SPage we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> contentType to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                contentType, actual.getContentType());\n\n        // check\n        if (!actual.getContentType().equals(contentType)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual SPage's description is equal to the given one.\n     *\n     * @param description the given description to compare the actual SPage's description to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPage's description is not equal to the given one.\n     */\n    public SPageAssert hasDescription(String description) {\n        // check that actual SPage we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> description to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                description, actual.getDescription());\n\n        // check\n        if (!actual.getDescription().equals(description)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual SPage's displayName is equal to the given one.\n     *\n     * @param displayName the given displayName to compare the actual SPage's displayName to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPage's displayName is not equal to the given one.\n     */\n    public SPageAssert hasDisplayName(String displayName) {\n        // check that actual SPage we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> displayName to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                displayName, actual.getDisplayName());\n\n        // check\n        if (!actual.getDisplayName().equals(displayName)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual SPage's installationDate is equal to the given one.\n     *\n     * @param installationDate the given installationDate to compare the actual SPage's installationDate to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPage's installationDate is not equal to the given one.\n     */\n    public SPageAssert hasInstallationDate(long installationDate) {\n        // check that actual SPage we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> installationDate to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                installationDate, actual.getInstallationDate());\n\n        // check\n        if (actual.getInstallationDate() != installationDate) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual SPage's installedBy is equal to the given one.\n     *\n     * @param installedBy the given installedBy to compare the actual SPage's installedBy to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPage's installedBy is not equal to the given one.\n     */\n    public SPageAssert hasInstalledBy(long installedBy) {\n        // check that actual SPage we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> installedBy to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                installedBy, actual.getInstalledBy());\n\n        // check\n        if (actual.getInstalledBy() != installedBy) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual SPage's lastModificationDate is equal to the given one.\n     *\n     * @param lastModificationDate the given lastModificationDate to compare the actual SPage's lastModificationDate to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPage's lastModificationDate is not equal to the given one.\n     */\n    public SPageAssert hasLastModificationDate(long lastModificationDate) {\n        // check that actual SPage we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> lastModificationDate to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                lastModificationDate, actual.getLastModificationDate());\n\n        // check\n        if (actual.getLastModificationDate() != lastModificationDate) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual SPage's lastUpdatedBy is equal to the given one.\n     *\n     * @param lastUpdatedBy the given lastUpdatedBy to compare the actual SPage's lastUpdatedBy to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPage's lastUpdatedBy is not equal to the given one.\n     */\n    public SPageAssert hasLastUpdatedBy(long lastUpdatedBy) {\n        // check that actual SPage we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> lastUpdatedBy to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                lastUpdatedBy, actual.getLastUpdatedBy());\n\n        // check\n        if (actual.getLastUpdatedBy() != lastUpdatedBy) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual SPage's name is equal to the given one.\n     *\n     * @param name the given name to compare the actual SPage's name to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPage's name is not equal to the given one.\n     */\n    public SPageAssert hasName(String name) {\n        // check that actual SPage we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> name to be:\\n  <%s>\\n but was:\\n  <%s>\", actual, name,\n                actual.getName());\n\n        // check\n        if (!actual.getName().equals(name)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual SPage's processDefinitionId is equal to the given one.\n     *\n     * @param processDefinitionId the given processDefinitionId to compare the actual SPage's processDefinitionId to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPage's processDefinitionId is not equal to the given one.\n     */\n    public SPageAssert hasProcessDefinitionId(long processDefinitionId) {\n        // check that actual SPage we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> processDefinitionId to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                processDefinitionId, actual.getProcessDefinitionId());\n\n        // check\n        if (actual.getProcessDefinitionId() != processDefinitionId) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual SPage is provided.\n     *\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPage is not provided.\n     */\n    public SPageAssert isProvided() {\n        // check that actual SPage we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"Expected actual SPage to be provided but was not.\", actual);\n\n        // check\n        if (!actual.isProvided())\n            throw new AssertionError(errorMessage);\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual SPage is not provided.\n     *\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPage is provided.\n     */\n    public SPageAssert isNotProvided() {\n        // check that actual SPage we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"Expected actual SPage not to be provided but was.\", actual);\n\n        // check\n        if (actual.isProvided())\n            throw new AssertionError(errorMessage);\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/flownode/FlowNodeRetrierTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.flownode;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.fail;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.*;\n\nimport org.bonitasoft.engine.api.impl.connector.ConnectorResetStrategy;\nimport org.bonitasoft.engine.bpm.flownode.ActivityExecutionException;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceNotFoundException;\nimport org.bonitasoft.engine.bpm.flownode.ActivityStates;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.execution.ContainerRegistry;\nimport org.bonitasoft.engine.execution.FlowNodeExecutor;\nimport org.bonitasoft.engine.execution.StateBehaviors;\nimport org.bonitasoft.engine.execution.state.CompletedActivityState;\nimport org.bonitasoft.engine.execution.state.FailedActivityState;\nimport org.bonitasoft.engine.execution.state.FlowNodeStateManager;\nimport org.bonitasoft.engine.execution.state.InitializingActivityState;\nimport org.bonitasoft.engine.execution.state.ReadyActivityState;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class FlowNodeRetrierTest {\n\n    public static final String FLOW_NODE_NAME = \"step1\";\n    @Mock\n    private FlowNodeExecutor flowNodeExecutor;\n\n    @Mock\n    private ContainerRegistry registry;\n\n    @Mock\n    private ActivityInstanceService activityInstanceService;\n\n    @Mock\n    private FlowNodeStateManager flowNodeStateManager;\n\n    @Mock\n    private SFlowNodeInstance flowNodeInstance;\n\n    @Mock\n    ConnectorResetStrategy strategy;\n\n    private static long FLOW_NODE_INSTANCE_ID = 10;\n\n    private static int STATE_ID = 29;\n\n    private static int PREVIOUS_STATE_ID = 30;\n\n    private FlowNodeRetrier retrier;\n\n    @Before\n    public void setUp() throws Exception {\n        retrier = new FlowNodeRetrier(registry, flowNodeExecutor, activityInstanceService, flowNodeStateManager,\n                strategy);\n\n        given(flowNodeInstance.getId()).willReturn(FLOW_NODE_INSTANCE_ID);\n        given(flowNodeInstance.getStateId()).willReturn(STATE_ID);\n        given(flowNodeInstance.getPreviousStateId()).willReturn(PREVIOUS_STATE_ID);\n        given(flowNodeInstance.getName()).willReturn(FLOW_NODE_NAME);\n\n    }\n\n    @Test\n    public void retry_should_reset_connectors_set_the_previous_state_and_execute_flow_node() throws Exception {\n        //given\n        given(activityInstanceService.getFlowNodeInstance(FLOW_NODE_INSTANCE_ID)).willReturn(flowNodeInstance);\n\n        given(flowNodeStateManager.getState(STATE_ID)).willReturn(new FailedActivityState());\n        given(flowNodeStateManager.getState(PREVIOUS_STATE_ID))\n                .willReturn(new InitializingActivityState(mock(StateBehaviors.class)));\n\n        //when\n        retrier.retry(FLOW_NODE_INSTANCE_ID);\n\n        //then\n        verify(strategy).resetConnectorsOf(FLOW_NODE_INSTANCE_ID);\n        verify(flowNodeExecutor).setStateByStateId(FLOW_NODE_INSTANCE_ID, PREVIOUS_STATE_ID);\n        verify(registry).executeFlowNode(flowNodeInstance);\n    }\n\n    @Test\n    public void retry_should_not_execute_flow_node_when_previous_state_is_terminal() throws Exception {\n        //given\n        given(activityInstanceService.getFlowNodeInstance(FLOW_NODE_INSTANCE_ID)).willReturn(flowNodeInstance);\n\n        given(flowNodeStateManager.getState(STATE_ID)).willReturn(new FailedActivityState());\n        given(flowNodeStateManager.getState(PREVIOUS_STATE_ID)).willReturn(new CompletedActivityState());\n\n        //when\n        retrier.retry(FLOW_NODE_INSTANCE_ID);\n\n        //then\n        verify(registry, never()).executeFlowNode(any());\n        verify(flowNodeExecutor).setStateByStateId(FLOW_NODE_INSTANCE_ID, PREVIOUS_STATE_ID);\n    }\n\n    @Test(expected = ActivityExecutionException.class)\n    public void retryTask_should_throw_ActivityExecutionException_when_activityInstanceService_throws_exception()\n            throws Exception {\n        //given\n        given(activityInstanceService.getFlowNodeInstance(FLOW_NODE_INSTANCE_ID))\n                .willThrow(new SFlowNodeReadException(\"\"));\n\n        //when\n        retrier.retry(FLOW_NODE_INSTANCE_ID);\n\n        //then exception\n    }\n\n    @Test(expected = ActivityInstanceNotFoundException.class)\n    public void retryTask_should_throw_ActivityInstanceNotFoundException_when_activityInstanceService_throws_SFlowNodeNotFoundException()\n            throws Exception {\n        //given\n        given(activityInstanceService.getFlowNodeInstance(FLOW_NODE_INSTANCE_ID))\n                .willThrow(new SFlowNodeNotFoundException(FLOW_NODE_INSTANCE_ID));\n\n        //when\n        retrier.retry(FLOW_NODE_INSTANCE_ID);\n\n        //then exception\n    }\n\n    @Test\n    public void retry_should_throw_exception_when_flowNode_is_not_in_failed_state() throws Exception {\n        //given\n        given(activityInstanceService.getFlowNodeInstance(FLOW_NODE_INSTANCE_ID)).willReturn(flowNodeInstance);\n        given(flowNodeStateManager.getState(STATE_ID))\n                .willReturn(new ReadyActivityState(mock(StateBehaviors.class)));\n\n        try {\n            //when\n            retrier.retry(FLOW_NODE_INSTANCE_ID);\n            fail(\"Exception expected\");\n        } catch (ActivityExecutionException e) {\n            //then\n            assertThat(e.getMessage()).isEqualTo(\n                    \"Unable to retry the flow node instance [name=\" + FLOW_NODE_NAME + \", id=\"\n                            + FLOW_NODE_INSTANCE_ID\n                            + \"] because it is not in failed state. The current state for this flow node instance is '\"\n                            + ActivityStates.READY_STATE + \"'\");\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/livingapplication/LivingApplicationAPIDelegateTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.livingapplication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.*;\n\nimport java.nio.charset.StandardCharsets;\n\nimport org.bonitasoft.engine.api.impl.converter.ApplicationModelConverter;\nimport org.bonitasoft.engine.api.impl.transaction.application.SearchApplications;\nimport org.bonitasoft.engine.business.application.Application;\nimport org.bonitasoft.engine.business.application.ApplicationCreator;\nimport org.bonitasoft.engine.business.application.ApplicationLink;\nimport org.bonitasoft.engine.business.application.ApplicationLinkCreator;\nimport org.bonitasoft.engine.business.application.ApplicationLinkUpdater;\nimport org.bonitasoft.engine.business.application.ApplicationNotFoundException;\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.ApplicationUpdater;\nimport org.bonitasoft.engine.business.application.IApplication;\nimport org.bonitasoft.engine.business.application.impl.ApplicationImpl;\nimport org.bonitasoft.engine.business.application.impl.ApplicationLinkImpl;\nimport org.bonitasoft.engine.business.application.importer.validator.ApplicationTokenValidator;\nimport org.bonitasoft.engine.business.application.importer.validator.ValidationStatus;\nimport org.bonitasoft.engine.business.application.model.SApplication;\nimport org.bonitasoft.engine.business.application.model.SApplicationState;\nimport org.bonitasoft.engine.business.application.model.SApplicationWithIcon;\nimport org.bonitasoft.engine.commons.exceptions.SObjectAlreadyExistsException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectCreationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.RetrieveException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class LivingApplicationAPIDelegateTest {\n\n    @Mock\n    private ServiceAccessor accessor;\n\n    @Mock\n    private ApplicationModelConverter converter;\n\n    @Mock\n    private SearchApplications<IApplication> searchApplications;\n\n    @Mock\n    private ApplicationService applicationService;\n\n    @Mock\n    private SearchResult<IApplication> appSearchResult;\n\n    @Mock\n    ApplicationTokenValidator validator;\n\n    private LivingApplicationAPIDelegate delegate;\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    private static final long APPLICATION_ID = 15;\n\n    private static final long LOGGED_USER_ID = 10;\n\n    private static final long LAYOUT_ID = 60;\n\n    private static final long THEME_ID = 61;\n\n    private static final String APP_TOKEN = \"app\";\n\n    private static final String APP_DISP_NAME = \"My app\";\n\n    private static final String VERSION = \"1.0\";\n\n    private static final String DESCRIPTION = \"app desc\";\n\n    private static final String ICON_MIME_TYPE = \"app mime_type\";\n\n    private static final byte[] ICON_CONTENT = \"app_icon_content\".getBytes(StandardCharsets.UTF_8);\n\n    @Before\n    public void setUp() throws Exception {\n        given(accessor.getApplicationService()).willReturn(applicationService);\n        delegate = new LivingApplicationAPIDelegate(accessor, converter, LOGGED_USER_ID, validator);\n        given(validator.validate(anyString())).willReturn(new ValidationStatus(true));\n    }\n\n    @Test\n    public void createApplication_should_call_applicationService_createApplication_and_return_created_application()\n            throws Exception {\n        //given\n        final ApplicationCreator creator = new ApplicationCreator(APP_TOKEN, APP_DISP_NAME, VERSION);\n        creator.setDescription(DESCRIPTION);\n        final SApplicationWithIcon sApp = buildDefaultApplicationWithMetadata();\n        sApp.setDescription(DESCRIPTION);\n        final ApplicationImpl application = new ApplicationImpl(APP_TOKEN, VERSION, DESCRIPTION);\n        given(converter.buildSApplication(creator, LOGGED_USER_ID)).willReturn(sApp);\n        given(converter.toApplication(sApp)).willReturn(application);\n        given(applicationService.createApplication(sApp)).willReturn(sApp);\n\n        //when\n        final Application createdApplication = delegate.createApplication(creator);\n\n        //then\n        assertThat(createdApplication).isEqualTo(application);\n    }\n\n    @Test\n    public void createApplicationLink_should_call_applicationService_createApplication_and_return_created_application()\n            throws Exception {\n        //given\n        final ApplicationLinkCreator creator = new ApplicationLinkCreator(APP_TOKEN, APP_DISP_NAME, VERSION);\n        creator.setDescription(DESCRIPTION);\n        final SApplicationWithIcon sApp = buildDefaultApplicationWithMetadata();\n        sApp.setDescription(DESCRIPTION);\n        final ApplicationLinkImpl applicationLink = new ApplicationLinkImpl(APP_TOKEN, VERSION, DESCRIPTION);\n        given(converter.buildSApplication(creator, LOGGED_USER_ID)).willReturn(sApp);\n        given(converter.toApplication(sApp)).willReturn(applicationLink);\n        given(applicationService.createApplication(sApp)).willReturn(sApp);\n\n        //when\n        final ApplicationLink createdApplication = delegate.createApplicationLink(creator);\n\n        //then\n        assertThat(createdApplication).isEqualTo(applicationLink);\n    }\n\n    private SApplication buildDefaultApplication() {\n        return new SApplication(APP_TOKEN, APP_DISP_NAME, VERSION, System.currentTimeMillis(), LOGGED_USER_ID,\n                SApplicationState.DEACTIVATED.name());\n    }\n\n    private SApplicationWithIcon buildDefaultApplicationWithMetadata() {\n        return new SApplicationWithIcon(APP_TOKEN, APP_DISP_NAME, VERSION, System.currentTimeMillis(),\n                LOGGED_USER_ID, SApplicationState.DEACTIVATED.name());\n    }\n\n    @Test(expected = AlreadyExistsException.class)\n    public void createApplication_should_throw_AlreadyExistsException_when_applicationService_throws_SObjectAlreadyExistsException()\n            throws Exception {\n        //given\n        final ApplicationCreator creator = new ApplicationCreator(APP_TOKEN, APP_DISP_NAME, VERSION);\n        final SApplicationWithIcon sApp = buildDefaultApplicationWithMetadata();\n        given(converter.buildSApplication(creator, LOGGED_USER_ID)).willReturn(sApp);\n        given(applicationService.createApplication(sApp))\n                .willThrow(new SObjectAlreadyExistsException(\"\"));\n\n        //when\n        delegate.createApplication(creator);\n\n        //then exception\n    }\n\n    @Test(expected = CreationException.class)\n    public void createApplication_should_throw_CreationException_when_applicationService_throws_SObjectCreationException()\n            throws Exception {\n        //given\n        final ApplicationCreator creator = new ApplicationCreator(APP_TOKEN, APP_DISP_NAME, VERSION);\n        final SApplicationWithIcon sApp = buildDefaultApplicationWithMetadata();\n        given(converter.buildSApplication(creator, LOGGED_USER_ID)).willReturn(sApp);\n        given(applicationService.createApplication(sApp))\n                .willThrow(new SObjectCreationException(\"\"));\n\n        //when\n        delegate.createApplication(creator);\n\n        //then exception\n    }\n\n    @Test\n    public void createApplication_should_throw_InvalidCharacterException_when_token_contains_invalid_character()\n            throws Exception {\n        //given\n        final ApplicationCreator creator = new ApplicationCreator(\"invalid token\", APP_DISP_NAME, VERSION);\n        given(validator.validate(\"invalid token\")).willReturn(new ValidationStatus(false, \"invalid character\"));\n\n        //then\n        expectedException.expect(CreationException.class);\n        expectedException.expectMessage(\"invalid character\");\n\n        //when\n        delegate.createApplication(creator);\n\n    }\n\n    @Test(expected = CreationException.class)\n    public void createApplication_should_throw_CreationException_when_display_name_is_null() throws Exception {\n        //given\n        final ApplicationCreator creator = new ApplicationCreator(APP_TOKEN, null, VERSION);\n\n        //when\n        delegate.createApplication(creator);\n\n        //then exception\n    }\n\n    @Test(expected = CreationException.class)\n    public void createApplication_should_throw_CreationException_when_display_name_is_empty() throws Exception {\n        //given\n        final ApplicationCreator creator = new ApplicationCreator(APP_TOKEN, \"\", VERSION);\n\n        //when\n        delegate.createApplication(creator);\n\n        //then exception\n    }\n\n    @Test\n    public void delete_Application_should_call_applicationService_delete() throws Exception {\n        //when\n        delegate.deleteApplication(APPLICATION_ID);\n\n        //then\n        verify(applicationService, times(1)).deleteApplication(APPLICATION_ID);\n    }\n\n    @Test(expected = DeletionException.class)\n    public void delete_Application_should_throw_DeletionException_when_applicationService_throws_SObjectModificationException()\n            throws Exception {\n        doThrow(new SObjectModificationException()).when(applicationService).deleteApplication(APPLICATION_ID);\n\n        //when\n        delegate.deleteApplication(APPLICATION_ID);\n\n        //then exception\n    }\n\n    @Test(expected = DeletionException.class)\n    public void delete_Application_should_throw_DeletionException_when_applicationService_throws_SObjectNotFoundException()\n            throws Exception {\n        doThrow(new SObjectNotFoundException()).when(applicationService).deleteApplication(APPLICATION_ID);\n\n        //when\n        delegate.deleteApplication(APPLICATION_ID);\n\n        //then exception\n    }\n\n    @Test\n    public void getApplication_should_return_the_application_returned_by_applicationService_converted()\n            throws Exception {\n        SApplication sApp = buildDefaultApplication();\n        final ApplicationImpl application = new ApplicationImpl(APP_TOKEN, VERSION, null);\n        given(applicationService.getApplication(APPLICATION_ID)).willReturn(sApp);\n        given(converter.toApplication(sApp)).willReturn(application);\n\n        //when\n        final IApplication retriedApp = delegate.getIApplication(APPLICATION_ID);\n\n        //then\n        assertThat(retriedApp).isEqualTo(application);\n\n    }\n\n    @Test(expected = RetrieveException.class)\n    public void getApplication_should_throw_RetrieveException_when_applicationService_throws_SBonitaReadException()\n            throws Exception {\n        //given\n        given(applicationService.getApplication(APPLICATION_ID)).willThrow(new SBonitaReadException(\"\"));\n\n        //when\n        delegate.getIApplication(APPLICATION_ID);\n\n        //then exception\n    }\n\n    @Test(expected = ApplicationNotFoundException.class)\n    public void getApplication_should_throw_ApplicationNotFoundException_when_applicationService_throws_SObjectNotFoundException()\n            throws Exception {\n        //given\n        given(applicationService.getApplication(APPLICATION_ID)).willThrow(new SObjectNotFoundException());\n\n        //when\n        delegate.getIApplication(APPLICATION_ID);\n\n        //then exception\n    }\n\n    @Test\n    public void getApplicationByToken_should_return_the_application_returned_by_applicationService_converted()\n            throws Exception {\n        SApplication sApp = buildDefaultApplication();\n        final ApplicationImpl application = new ApplicationImpl(APP_TOKEN, VERSION, null);\n        given(applicationService.getApplicationByToken(APP_TOKEN)).willReturn(sApp);\n        given(converter.toApplication(sApp)).willReturn(application);\n\n        //when\n        final IApplication retriedApp = delegate.getIApplicationByToken(APP_TOKEN);\n\n        //then\n        assertThat(retriedApp).isEqualTo(application);\n\n    }\n\n    @Test(expected = RetrieveException.class)\n    public void getApplicationByToken_should_throw_RetrieveException_when_applicationService_throws_SBonitaReadException()\n            throws Exception {\n        //given\n        given(applicationService.getApplicationByToken(APP_TOKEN)).willThrow(new SBonitaReadException(\"\"));\n\n        //when\n        delegate.getIApplicationByToken(APP_TOKEN);\n\n        //then exception\n    }\n\n    @Test(expected = ApplicationNotFoundException.class)\n    public void getApplicationByToken_should_throw_ApplicationNotFoundException_when_applicationService_returns_null()\n            throws Exception {\n        //given\n        given(applicationService.getApplicationByToken(APP_TOKEN)).willReturn(null);\n\n        //when\n        delegate.getIApplicationByToken(APP_TOKEN);\n\n        //then exception\n    }\n\n    @Test\n    public void updateApplication_should_return_result_of_applicationservice_updateApplication() throws Exception {\n        //given\n        final SApplicationWithIcon sApplicationWithIcon = mock(SApplicationWithIcon.class);\n        final Application application = mock(Application.class);\n        final ApplicationUpdater updater = new ApplicationUpdater();\n        updater.setToken(\"newToken\");\n        final EntityUpdateDescriptor updateDescriptor = new EntityUpdateDescriptor();\n        given(converter.toApplicationUpdateDescriptor(updater, LOGGED_USER_ID)).willReturn(updateDescriptor);\n        given(applicationService.updateApplication(APPLICATION_ID, updateDescriptor))\n                .willReturn(sApplicationWithIcon);\n        given(converter.toApplication(sApplicationWithIcon)).willReturn(application);\n\n        //when\n        final Application updatedApplication = delegate.updateApplication(APPLICATION_ID, updater);\n\n        //then\n        assertThat(updatedApplication).isEqualTo(application);\n    }\n\n    @Test\n    public void updateApplicationLink_should_return_result_of_applicationservice_updateApplication() throws Exception {\n        //given\n        final SApplicationWithIcon sApplicationWithIcon = mock(SApplicationWithIcon.class);\n        final ApplicationLink applicationLink = mock(ApplicationLink.class);\n        final ApplicationLinkUpdater updater = new ApplicationLinkUpdater();\n        updater.setToken(\"newToken\");\n        final EntityUpdateDescriptor updateDescriptor = new EntityUpdateDescriptor();\n        given(converter.toApplicationUpdateDescriptor(updater, LOGGED_USER_ID)).willReturn(updateDescriptor);\n        given(applicationService.updateApplication(APPLICATION_ID, updateDescriptor))\n                .willReturn(sApplicationWithIcon);\n        given(converter.toApplication(sApplicationWithIcon)).willReturn(applicationLink);\n\n        //when\n        final ApplicationLink updatedApplication = delegate.updateApplicationLink(APPLICATION_ID, updater);\n\n        //then\n        assertThat(updatedApplication).isEqualTo(applicationLink);\n    }\n\n    @Test\n    public void updateApplicationLink_should_not_return_when_using_ApplicationUpdater() throws Exception {\n        //given\n        final SApplicationWithIcon sApplicationWithIcon = mock(SApplicationWithIcon.class);\n        final ApplicationLink applicationLink = mock(ApplicationLink.class);\n        final ApplicationUpdater updater = new ApplicationUpdater();\n        updater.setToken(\"newToken\");\n        final EntityUpdateDescriptor updateDescriptor = new EntityUpdateDescriptor();\n        given(converter.toApplicationUpdateDescriptor(updater, LOGGED_USER_ID)).willReturn(updateDescriptor);\n        given(applicationService.updateApplication(APPLICATION_ID, updateDescriptor))\n                .willReturn(sApplicationWithIcon);\n        given(converter.toApplication(sApplicationWithIcon)).willReturn(applicationLink);\n\n        //when/ then\n        assertThrows(UpdateException.class, () -> delegate.updateApplication(APPLICATION_ID, updater));\n    }\n\n    @Test\n    public void updateApplication_should_return_result_of_applicationservice_getApplication_when_updater_is_empty()\n            throws Exception {\n        //given\n        final SApplication sApplication = mock(SApplication.class);\n        final Application application = mock(Application.class);\n        final ApplicationUpdater updater = new ApplicationUpdater();\n        final EntityUpdateDescriptor updateDescriptor = new EntityUpdateDescriptor();\n        given(applicationService.getApplication(APPLICATION_ID)).willReturn(sApplication);\n        given(converter.toApplication(sApplication)).willReturn(application);\n\n        //when\n        final Application updatedApplication = delegate.updateApplication(APPLICATION_ID, updater);\n\n        //then\n        assertThat(updatedApplication).isEqualTo(application);\n    }\n\n    @Test(expected = UpdateException.class)\n    public void updateApplication_should_throw_UpdateException_when_applicationService_throws_SObjectModificationException()\n            throws Exception {\n        //given\n        final ApplicationUpdater updater = new ApplicationUpdater();\n        updater.setToken(\"newToken\");\n        final EntityUpdateDescriptor updateDescriptor = new EntityUpdateDescriptor();\n        given(converter.toApplicationUpdateDescriptor(updater, LOGGED_USER_ID)).willReturn(updateDescriptor);\n        given(applicationService.updateApplication(APPLICATION_ID, updateDescriptor))\n                .willThrow(new SObjectModificationException());\n\n        //when\n        delegate.updateApplication(APPLICATION_ID, updater);\n\n        //then exception\n    }\n\n    @Test(expected = AlreadyExistsException.class)\n    public void updateApplication_should_throw_UpdateException_when_applicationService_throws_SObjectAlreadyExistsException()\n            throws Exception {\n        //given\n        final ApplicationUpdater updater = new ApplicationUpdater();\n        updater.setToken(\"newToken\");\n        final EntityUpdateDescriptor updateDescriptor = new EntityUpdateDescriptor();\n        given(converter.toApplicationUpdateDescriptor(updater, LOGGED_USER_ID)).willReturn(updateDescriptor);\n        given(applicationService.updateApplication(APPLICATION_ID, updateDescriptor))\n                .willThrow(new SObjectAlreadyExistsException());\n\n        //when\n        delegate.updateApplication(APPLICATION_ID, updater);\n\n        //then exception\n    }\n\n    @Test(expected = ApplicationNotFoundException.class)\n    public void updateApplication_should_throw_ApplicationNotFoundException_when_applicationservice_throws_SObjectNotFoundException()\n            throws Exception {\n        //given\n        final ApplicationUpdater updater = new ApplicationUpdater();\n        updater.setToken(\"newToken\");\n        final EntityUpdateDescriptor updateDescriptor = new EntityUpdateDescriptor();\n        given(converter.toApplicationUpdateDescriptor(updater, LOGGED_USER_ID)).willReturn(updateDescriptor);\n        given(applicationService.updateApplication(APPLICATION_ID, updateDescriptor))\n                .willThrow(new SObjectNotFoundException());\n\n        //when\n        delegate.updateApplication(APPLICATION_ID, updater);\n\n        //then exception\n    }\n\n    @Test\n    public void updateApplication_should_throw_UpdateException_when_applicationService_token_is_invalid()\n            throws Exception {\n        //given\n        final ApplicationUpdater updater = new ApplicationUpdater();\n        updater.setToken(\"invalid token\");\n        given(validator.validate(\"invalid token\")).willReturn(new ValidationStatus(false, \"invalid token\"));\n\n        //then exception\n        expectedException.expect(UpdateException.class);\n        expectedException.expectMessage(\"invalid token\");\n\n        //when\n        delegate.updateApplication(APPLICATION_ID, updater);\n\n    }\n\n    @Test(expected = UpdateException.class)\n    public void updateApplication_should_throw_UpdateException_when_applicationService_displayName_is_empty()\n            throws Exception {\n        //given\n        final ApplicationUpdater updater = new ApplicationUpdater();\n        updater.setDisplayName(\"\");\n\n        //when\n        delegate.updateApplication(APPLICATION_ID, updater);\n\n        //then exception\n    }\n\n    @Test(expected = UpdateException.class)\n    public void updateApplication_should_throw_UpdateException_when_applicationService_displayName_is_null()\n            throws Exception {\n        //given\n        final ApplicationUpdater updater = new ApplicationUpdater();\n        updater.setDisplayName(null);\n\n        //when\n        delegate.updateApplication(APPLICATION_ID, updater);\n\n        //then exception\n    }\n\n    @Test\n    public void searchApplications_should_return_the_result_of_searchApplications_getResult() throws Exception {\n        //given\n        given(searchApplications.getResult()).willReturn(appSearchResult);\n\n        //when\n        final SearchResult<IApplication> retrievedSearchResult = delegate.searchIApplications(searchApplications);\n\n        //then\n        assertThat(retrievedSearchResult).isEqualTo(appSearchResult);\n        verify(searchApplications, times(1)).execute();\n    }\n\n    @Test(expected = SearchException.class)\n    public void searchApplications_should_throw_SearchException_when_searchApplications_throws_SBonitaException()\n            throws Exception {\n        //given\n        doThrow(new SBonitaReadException(\"\")).when(searchApplications).execute();\n\n        //when\n        delegate.searchIApplications(searchApplications);\n\n        //then exception\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/livingapplication/LivingApplicationExporterDelegateTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.livingapplication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.anyList;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.exporter.ApplicationExporter;\nimport org.bonitasoft.engine.business.application.model.SApplication;\nimport org.bonitasoft.engine.exception.ExportException;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class LivingApplicationExporterDelegateTest {\n\n    @Mock\n    private ApplicationExporter exporter;\n\n    @Mock\n    private ApplicationService applicationService;\n\n    @InjectMocks\n    private LivingApplicationExporterDelegate delegate;\n\n    @Test\n    public void exportApplications_should_return_result_of_ApplicationsExporter() throws Exception {\n        //given\n        given(applicationService.getApplication(5L)).willReturn(mock(SApplication.class));\n        given(exporter.export(anyList())).willReturn(\"<applications/>\".getBytes());\n\n        //when\n        byte[] exportApplications = delegate.exportApplications(5);\n\n        //then\n        assertThat(new String(exportApplications)).isEqualTo(\"<applications/>\");\n    }\n\n    @Test(expected = ExportException.class)\n    public void exportApplications_should_throw_SBonitaExportException_when_applicationService_throwsSBonitaReadException()\n            throws Exception {\n        //given\n        given(applicationService.getApplication(5L)).willThrow(new SBonitaReadException(\"\"));\n\n        //when\n        delegate.exportApplications(5);\n\n        //then exception\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/livingapplication/LivingApplicationMenuAPIDelegateTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.livingapplication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Collections;\n\nimport org.bonitasoft.engine.api.impl.converter.ApplicationMenuModelConverter;\nimport org.bonitasoft.engine.api.impl.transaction.application.SearchApplicationMenus;\nimport org.bonitasoft.engine.business.application.ApplicationMenu;\nimport org.bonitasoft.engine.business.application.ApplicationMenuCreator;\nimport org.bonitasoft.engine.business.application.ApplicationMenuNotFoundException;\nimport org.bonitasoft.engine.business.application.ApplicationMenuUpdater;\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.impl.ApplicationMenuImpl;\nimport org.bonitasoft.engine.business.application.importer.validator.ApplicationMenuCreatorValidator;\nimport org.bonitasoft.engine.business.application.model.SApplicationMenu;\nimport org.bonitasoft.engine.commons.exceptions.SObjectCreationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.RetrieveException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class LivingApplicationMenuAPIDelegateTest {\n\n    @Mock\n    private ServiceAccessor accessor;\n\n    @Mock\n    private ApplicationMenuModelConverter convertor;\n\n    @Mock\n    private ApplicationMenuCreatorValidator creatorValidator;\n\n    @Mock\n    private SearchApplicationMenus searchApplicatonMenus;\n\n    @Mock\n    private ApplicationService applicationService;\n\n    @Mock\n    private SearchResult<ApplicationMenu> appMenuSearchResult;\n\n    private LivingApplicationMenuAPIDelegate delegate;\n\n    private static final long APPLICATION_ID = 34;\n\n    private static final long APPLICATION_PAGE_ID = 35;\n\n    @Before\n    public void setUp() throws Exception {\n        given(accessor.getApplicationService()).willReturn(applicationService);\n        delegate = new LivingApplicationMenuAPIDelegate(accessor, convertor, creatorValidator, 911L);\n        given(creatorValidator.isValid(any(ApplicationMenuCreator.class))).willReturn(Collections.emptyList());\n    }\n\n    @Test\n    public void createApplicationMenu_should_update_application() throws Exception {\n        // given\n        final ApplicationMenuCreator creator = new ApplicationMenuCreator(APPLICATION_ID, \"Main\", APPLICATION_PAGE_ID);\n        final SApplicationMenu sAppMenu = mock(SApplicationMenu.class);\n        given(sAppMenu.getApplicationId()).willReturn(APPLICATION_ID);\n        given(applicationService.getNextAvailableIndex(null)).willReturn(5);\n        given(convertor.buildSApplicationMenu(creator, 5)).willReturn(sAppMenu);\n        given(applicationService.createApplicationMenu(sAppMenu)).willReturn(sAppMenu);\n\n        //when\n        delegate.createApplicationMenu(creator);\n\n        //then\n        verify(applicationService).updateApplication(eq(APPLICATION_ID), any(EntityUpdateDescriptor.class));\n    }\n\n    @Test\n    public void updateApplicationMenu_should_update_application() throws Exception {\n        // given\n        final SApplicationMenu sAppMenu = mock(SApplicationMenu.class);\n        given(sAppMenu.getApplicationId()).willReturn(APPLICATION_ID);\n        given(sAppMenu.getId()).willReturn(9811L);\n        given(applicationService.updateApplicationMenu(eq(sAppMenu.getId()), nullable(EntityUpdateDescriptor.class)))\n                .willReturn(sAppMenu);\n\n        //when\n        delegate.updateApplicationMenu(sAppMenu.getId(), mock(ApplicationMenuUpdater.class));\n\n        //then\n        verify(applicationService).updateApplication(eq(APPLICATION_ID), any(EntityUpdateDescriptor.class));\n    }\n\n    @Test\n    public void deleteApplicationMenu_should_update_application() throws Exception {\n        // given\n        final SApplicationMenu sAppMenu = mock(SApplicationMenu.class);\n        given(sAppMenu.getApplicationId()).willReturn(APPLICATION_ID);\n        given(sAppMenu.getId()).willReturn(9811L);\n        given(applicationService.deleteApplicationMenu(sAppMenu.getId())).willReturn(sAppMenu);\n\n        //when\n        delegate.deleteApplicationMenu(sAppMenu.getId());\n\n        //then\n        verify(applicationService).updateApplication(eq(APPLICATION_ID), any(EntityUpdateDescriptor.class));\n    }\n\n    @Test\n    public void createApplicationMenu_with_no_parent_should_call_applicationService_createApplicationMenu_with_nexIndex_and_return_created_applicationMenu()\n            throws Exception {\n        //given\n        final ApplicationMenuCreator creator = new ApplicationMenuCreator(APPLICATION_ID, \"Main\", APPLICATION_PAGE_ID);\n        final SApplicationMenu sAppMenu = new SApplicationMenu(\"Main\", APPLICATION_ID, APPLICATION_PAGE_ID, 1);\n        final ApplicationMenuImpl appMenu = new ApplicationMenuImpl(\"Main\", APPLICATION_ID, APPLICATION_PAGE_ID, 1);\n        given(applicationService.getNextAvailableIndex(null)).willReturn(5);\n        given(convertor.buildSApplicationMenu(creator, 5)).willReturn(sAppMenu);\n        given(convertor.toApplicationMenu(sAppMenu)).willReturn(appMenu);\n        given(applicationService.createApplicationMenu(sAppMenu)).willReturn(sAppMenu);\n\n        //when\n        final ApplicationMenu createdAppMenu = delegate.createApplicationMenu(creator);\n\n        //then\n        assertThat(createdAppMenu).isNotNull();\n        assertThat(createdAppMenu).isEqualTo(appMenu);\n    }\n\n    @Test(expected = CreationException.class)\n    public void createApplicationMenu_should_throw_CreationException_when_applicationService_throws_SObjectCreationException()\n            throws Exception {\n        //given\n        final ApplicationMenuCreator creator = new ApplicationMenuCreator(APPLICATION_ID, \"Main\", APPLICATION_PAGE_ID);\n        final SApplicationMenu sAppMenu = new SApplicationMenu(\"Main\", APPLICATION_ID, APPLICATION_PAGE_ID, 1);\n        given(applicationService.getNextAvailableIndex(null)).willReturn(1);\n        given(convertor.buildSApplicationMenu(creator, 1)).willReturn(sAppMenu);\n        given(applicationService.createApplicationMenu(sAppMenu)).willThrow(new SObjectCreationException());\n\n        //when\n        delegate.createApplicationMenu(creator);\n\n        //then exception\n    }\n\n    @Test(expected = CreationException.class)\n    public void createApplicationMenu_should_throw_CreationException_when_creator_is_not_valid() throws Exception {\n        //given\n        final ApplicationMenuCreator creator = new ApplicationMenuCreator(APPLICATION_ID, \"Main\", APPLICATION_PAGE_ID);\n        given(creatorValidator.isValid(creator)).willReturn(Collections.singletonList(\"An error\"));\n\n        //when\n        delegate.createApplicationMenu(creator);\n\n        //then exception\n    }\n\n    @Test\n    public void updateApplicationMenu_should_return_result_of_applicationService_updateApplicationMenu_converted_to_client_object()\n            throws Exception {\n        //given\n        final ApplicationMenuUpdater updater = mock(ApplicationMenuUpdater.class);\n        final EntityUpdateDescriptor updateDescriptor = mock(EntityUpdateDescriptor.class);\n        final SApplicationMenu sApplicationMenu = mock(SApplicationMenu.class);\n        final ApplicationMenu applicationMenu = mock(ApplicationMenu.class);\n\n        given(convertor.toApplicationMenuUpdateDescriptor(updater)).willReturn(updateDescriptor);\n        given(applicationService.updateApplicationMenu(4, updateDescriptor)).willReturn(sApplicationMenu);\n        given(convertor.toApplicationMenu(sApplicationMenu)).willReturn(applicationMenu);\n\n        //when\n        final ApplicationMenu updatedMenu = delegate.updateApplicationMenu(4, updater);\n\n        //then\n        assertThat(updatedMenu).isEqualTo(applicationMenu);\n\n    }\n\n    @Test(expected = ApplicationMenuNotFoundException.class)\n    public void updateApplicationMenu_should_throw_ApplicationNotFoundException_when_applicationService_throws_SObjectNotFoundException()\n            throws Exception {\n        //given\n        final ApplicationMenuUpdater updater = mock(ApplicationMenuUpdater.class);\n        final EntityUpdateDescriptor updateDescriptor = mock(EntityUpdateDescriptor.class);\n\n        given(convertor.toApplicationMenuUpdateDescriptor(updater)).willReturn(updateDescriptor);\n        given(applicationService.updateApplicationMenu(4, updateDescriptor)).willThrow(new SObjectNotFoundException());\n\n        //when\n        delegate.updateApplicationMenu(4, updater);\n\n        //then exception\n\n    }\n\n    @Test(expected = UpdateException.class)\n    public void updateApplicationMenu_should_throw_UpdateException_when_applicationService_throws_SObjectModificationException()\n            throws Exception {\n        //given\n        final ApplicationMenuUpdater updater = mock(ApplicationMenuUpdater.class);\n        final EntityUpdateDescriptor updateDescriptor = mock(EntityUpdateDescriptor.class);\n\n        given(convertor.toApplicationMenuUpdateDescriptor(updater)).willReturn(updateDescriptor);\n        given(applicationService.updateApplicationMenu(4, updateDescriptor))\n                .willThrow(new SObjectModificationException());\n\n        //when\n        delegate.updateApplicationMenu(4, updater);\n\n        //then exception\n\n    }\n\n    @Test\n    public void getApplicationMenu_should_return_result_of_applicationService() throws Exception {\n        //given\n        final SApplicationMenu sAppMenu = new SApplicationMenu(\"Main\", APPLICATION_ID, APPLICATION_PAGE_ID, 1);\n        final ApplicationMenuImpl appMenu = new ApplicationMenuImpl(\"Main\", APPLICATION_ID, APPLICATION_PAGE_ID, 1);\n        given(applicationService.getApplicationMenu(10)).willReturn(sAppMenu);\n        given(convertor.toApplicationMenu(sAppMenu)).willReturn(appMenu);\n\n        //when\n        final ApplicationMenu retrievedMenu = delegate.getApplicationMenu(10);\n\n        //then\n        assertThat(retrievedMenu).isNotNull();\n        assertThat(retrievedMenu).isEqualTo(appMenu);\n    }\n\n    @Test(expected = ApplicationMenuNotFoundException.class)\n    public void getApplicationMenu_should_throw_ApplicationMenuNotFoundException_when_applicationService_throws_SObjectNotFoundException()\n            throws Exception {\n        //given\n        given(applicationService.getApplicationMenu(10)).willThrow(new SObjectNotFoundException());\n\n        //when\n        delegate.getApplicationMenu(10);\n\n        //then exception\n    }\n\n    @Test(expected = RetrieveException.class)\n    public void getApplicationMenu_should_throw_RetriveException_when_applicationService_throws_SBonitaReadException()\n            throws Exception {\n        //given\n        given(applicationService.getApplicationMenu(10)).willThrow(new SBonitaReadException(\"\"));\n\n        //when\n        delegate.getApplicationMenu(10);\n\n        //then exception\n    }\n\n    @Test\n    public void deleteApplicationMenu_should_call_applicationService_deleteApplicationMenu() throws Exception {\n        // given\n        final long appMenuId = 9811L;\n        final SApplicationMenu sAppMenu = mock(SApplicationMenu.class);\n        given(sAppMenu.getApplicationId()).willReturn(APPLICATION_ID);\n        given(sAppMenu.getId()).willReturn(appMenuId);\n        given(applicationService.deleteApplicationMenu(sAppMenu.getId())).willReturn(sAppMenu);\n\n        //when\n        delegate.deleteApplicationMenu(sAppMenu.getId());\n\n        //then\n        verify(applicationService, times(1)).deleteApplicationMenu(appMenuId);\n    }\n\n    @Test(expected = DeletionException.class)\n    public void deleteApplicationMenu_should_throw_DeletionException_when_applicationService_throws_SObjectModificationException()\n            throws Exception {\n        //given\n        doThrow(new SObjectModificationException()).when(applicationService).deleteApplicationMenu(15);\n\n        //when\n        delegate.deleteApplicationMenu(15);\n\n        //then exception\n    }\n\n    @Test(expected = DeletionException.class)\n    public void deleteApplicationMenu_should_throw_DeletionException_when_applicationService_throws_SObjectNotFoundException()\n            throws Exception {\n        //given\n        doThrow(new SObjectNotFoundException()).when(applicationService).deleteApplicationMenu(15);\n\n        //when\n        delegate.deleteApplicationMenu(15);\n\n        //then exception\n    }\n\n    @Test\n    public void searchApplicationMenus_should_return_result_of_searchApplicationMenus_getResult() throws Exception {\n        //given\n        given(searchApplicatonMenus.getResult()).willReturn(appMenuSearchResult);\n\n        //when\n        final SearchResult<ApplicationMenu> retrievedResult = delegate.searchApplicationMenus(searchApplicatonMenus);\n\n        //then\n        assertThat(retrievedResult).isEqualTo(appMenuSearchResult);\n        verify(searchApplicatonMenus, times(1)).execute();\n    }\n\n    @Test(expected = SearchException.class)\n    public void searchApplicationMenus_should_throw_SearchException_when_searchApplicationMenus_execute_throws_exception()\n            throws Exception {\n        //given\n        doThrow(new SBonitaReadException(\"\")).when(searchApplicatonMenus).execute();\n\n        //when\n        delegate.searchApplicationMenus(searchApplicatonMenus);\n\n        //then exception\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/livingapplication/LivingApplicationPageAPIDelegateTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.livingapplication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.*;\n\nimport org.bonitasoft.engine.api.impl.converter.ApplicationPageModelConverter;\nimport org.bonitasoft.engine.api.impl.transaction.application.SearchApplicationPages;\nimport org.bonitasoft.engine.business.application.ApplicationPage;\nimport org.bonitasoft.engine.business.application.ApplicationPageNotFoundException;\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.impl.ApplicationPageImpl;\nimport org.bonitasoft.engine.business.application.importer.validator.ApplicationTokenValidator;\nimport org.bonitasoft.engine.business.application.importer.validator.ValidationStatus;\nimport org.bonitasoft.engine.business.application.model.SApplicationPage;\nimport org.bonitasoft.engine.commons.exceptions.SObjectAlreadyExistsException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectCreationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.RetrieveException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class LivingApplicationPageAPIDelegateTest {\n\n    @Mock\n    private ServiceAccessor accessor;\n\n    @Mock\n    private ApplicationPageModelConverter converter;\n\n    @Mock\n    private SearchApplicationPages searchApplicationPages;\n\n    @Mock\n    private ApplicationService applicationService;\n\n    @Mock\n    private SearchResult<ApplicationPage> appPageSearchResult;\n\n    @Mock\n    ApplicationTokenValidator validator;\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    private LivingApplicationPageAPIDelegate delegate;\n\n    private static final long APPLICATION_ID = 15;\n\n    private static final long APPLICATION_PAGE_ID = 35;\n\n    private static final long PAGE_ID = 20;\n\n    private static final String APP_NAME = \"app\";\n\n    private static final String APP_PAGE_TOKEN = \"firstPage\";\n\n    @Before\n    public void setUp() throws Exception {\n        given(accessor.getApplicationService()).willReturn(applicationService);\n        delegate = new LivingApplicationPageAPIDelegate(accessor, converter, 9999L, validator);\n        given(validator.validate(anyString())).willReturn(new ValidationStatus(true));\n    }\n\n    @Test\n    public void setApplicationHomePage_should_call_applicationService_update_application_with_homePageId_key()\n            throws Exception {\n        //when\n        delegate.setApplicationHomePage(APPLICATION_ID, APPLICATION_PAGE_ID);\n\n        //then\n        verify(applicationService, times(1)).updateApplication(eq(APPLICATION_ID), any(EntityUpdateDescriptor.class));\n    }\n\n    @Test(expected = UpdateException.class)\n    public void setApplicationHomePage_should_throw_UpdateException_when_applicationService_throws_SObjectModificationException()\n            throws Exception {\n        //given\n        given(applicationService.updateApplication(anyLong(), any(EntityUpdateDescriptor.class)))\n                .willThrow(new SObjectModificationException(\"\"));\n\n        //when\n        delegate.setApplicationHomePage(APPLICATION_ID, APPLICATION_PAGE_ID);\n\n        //then exception\n    }\n\n    @Test\n    public void createApplicationPage_should_call_applicationService_createApplicationPage_and_return_created_applicationPage()\n            throws Exception {\n        //given\n        final ApplicationPageImpl appPage = new ApplicationPageImpl(APPLICATION_ID, PAGE_ID, APP_PAGE_TOKEN);\n        final SApplicationPage sAppPage = new SApplicationPage(APPLICATION_ID, PAGE_ID, APP_PAGE_TOKEN);\n        given(converter.toApplicationPage(sAppPage)).willReturn(appPage);\n        given(applicationService.createApplicationPage(sAppPage)).willReturn(sAppPage);\n\n        //when\n        final ApplicationPage createdAppPage = delegate.createApplicationPage(APPLICATION_ID, PAGE_ID, APP_PAGE_TOKEN);\n\n        //then\n        assertThat(createdAppPage).isEqualTo(appPage);\n    }\n\n    @Test\n    public void createApplicationPage_should_update_application() throws Exception {\n        //when\n        delegate.createApplicationPage(APPLICATION_ID, PAGE_ID, APP_PAGE_TOKEN);\n\n        //then\n        verify(applicationService).updateApplication(eq(APPLICATION_ID), any(EntityUpdateDescriptor.class));\n    }\n\n    @Test\n    public void deleteApplicationPage_should_update_application() throws Exception {\n        // given\n        final SApplicationPage appPage = mock(SApplicationPage.class);\n        given(appPage.getApplicationId()).willReturn(APPLICATION_ID);\n        given(applicationService.deleteApplicationPage(APPLICATION_PAGE_ID)).willReturn(appPage);\n\n        //when\n        delegate.deleteApplicationPage(APPLICATION_PAGE_ID);\n\n        //then\n        verify(applicationService).updateApplication(eq(APPLICATION_ID), any(EntityUpdateDescriptor.class));\n    }\n\n    @Test(expected = CreationException.class)\n    public void createApplicationPage_should_throw_CreationException_when_applicationService_throws_SObjectCreationException()\n            throws Exception {\n        //given\n        final SApplicationPage sAppPage = new SApplicationPage(APPLICATION_ID, PAGE_ID, APP_PAGE_TOKEN);\n        given(applicationService.createApplicationPage(sAppPage)).willThrow(new SObjectCreationException());\n\n        //when\n        delegate.createApplicationPage(APPLICATION_ID, PAGE_ID, APP_PAGE_TOKEN);\n\n        //then exception\n    }\n\n    @Test(expected = AlreadyExistsException.class)\n    public void createApplicationPage_should_throw_AlreadyExistsException_when_applicationService_throws_SObjectAlreadyExistsException()\n            throws Exception {\n        //given\n        final SApplicationPage sAppPage = new SApplicationPage(APPLICATION_ID, PAGE_ID, APP_PAGE_TOKEN);\n        given(applicationService.createApplicationPage(sAppPage)).willThrow(new SObjectAlreadyExistsException());\n\n        //when\n        delegate.createApplicationPage(APPLICATION_ID, PAGE_ID, APP_PAGE_TOKEN);\n\n        //then exception\n    }\n\n    @Test\n    public void createApplicationPage_should_throw_CreationException_when_token_is_invalid() throws Exception {\n        //given\n        given(validator.validate(\"invalid token\")).willReturn(new ValidationStatus(false, \"Invalid\"));\n\n        //then\n        expectedException.expect(CreationException.class);\n        expectedException.expectMessage(\"Invalid\");\n\n        //when\n        delegate.createApplicationPage(APPLICATION_ID, PAGE_ID, \"invalid token\");\n    }\n\n    @Test\n    public void getApplicationPage_byNameAndAppName_should_return_the_result_of_applicationService_getApplicationPage()\n            throws Exception {\n        //given\n        final ApplicationPageImpl appPage = new ApplicationPageImpl(APPLICATION_ID, PAGE_ID, APP_PAGE_TOKEN);\n        final SApplicationPage sAppPage = new SApplicationPage(APPLICATION_ID, PAGE_ID, APP_PAGE_TOKEN);\n        given(applicationService.getApplicationPage(APP_NAME, APP_PAGE_TOKEN)).willReturn(sAppPage);\n        given(converter.toApplicationPage(sAppPage)).willReturn(appPage);\n\n        //when\n        final ApplicationPage retrievedAppPage = delegate.getApplicationPage(APP_NAME, APP_PAGE_TOKEN);\n\n        //then\n        assertThat(retrievedAppPage).isEqualTo(appPage);\n    }\n\n    @Test(expected = RetrieveException.class)\n    public void getApplicationPage_byNameAndAppName_should_throw_RetrieveException_when_applicationService_throws_SBonitaReadException()\n            throws Exception {\n        //given\n        given(applicationService.getApplicationPage(APP_NAME, APP_PAGE_TOKEN)).willThrow(new SBonitaReadException(\"\"));\n\n        //when\n        delegate.getApplicationPage(APP_NAME, APP_PAGE_TOKEN);\n\n        //then exception\n    }\n\n    @Test(expected = ApplicationPageNotFoundException.class)\n    public void getApplicationPage_byNameAndAppName_should_throw_SObjectNotFoundException_when_applicationService_throws_SObjectNotFoundException()\n            throws Exception {\n        //given\n        given(applicationService.getApplicationPage(APP_NAME, APP_PAGE_TOKEN))\n                .willThrow(new SObjectNotFoundException());\n\n        //when\n        delegate.getApplicationPage(APP_NAME, APP_PAGE_TOKEN);\n\n        //then exception\n    }\n\n    @Test\n    public void getApplicationPage_byId_should_return_the_result_of_applicationService_getApplicationPage_byId()\n            throws Exception {\n        //given\n        final ApplicationPageImpl appPage = new ApplicationPageImpl(APPLICATION_ID, PAGE_ID, APP_PAGE_TOKEN);\n        final SApplicationPage sAppPage = new SApplicationPage(APPLICATION_ID, PAGE_ID, APP_PAGE_TOKEN);\n        given(applicationService.getApplicationPage(APPLICATION_PAGE_ID)).willReturn(sAppPage);\n        given(converter.toApplicationPage(sAppPage)).willReturn(appPage);\n\n        //when\n        final ApplicationPage retrievedAppPage = delegate.getApplicationPage(APPLICATION_PAGE_ID);\n\n        //then\n        assertThat(retrievedAppPage).isEqualTo(appPage);\n    }\n\n    @Test(expected = RetrieveException.class)\n    public void getApplicationPage_byId_should_throw_RetrieveException_when_applicationService_throws_SBonitaReadException()\n            throws Exception {\n        //given\n        given(applicationService.getApplicationPage(APPLICATION_PAGE_ID)).willThrow(new SBonitaReadException(\"\"));\n\n        //when\n        delegate.getApplicationPage(APPLICATION_PAGE_ID);\n\n        //then exception\n    }\n\n    @Test(expected = ApplicationPageNotFoundException.class)\n    public void getApplicationPage_byId_should_throw_SObjectNotFoundException_when_applicationService_throws_SObjectNotFoundException()\n            throws Exception {\n        //given\n        given(applicationService.getApplicationPage(APPLICATION_PAGE_ID)).willThrow(new SObjectNotFoundException());\n\n        //when\n        delegate.getApplicationPage(APPLICATION_PAGE_ID);\n\n        //then exception\n    }\n\n    @Test\n    public void deleteApplicationPage_should_call_applicationService_deleteApplicationPage() throws Exception {\n        // given\n        final SApplicationPage appPage = mock(SApplicationPage.class);\n        given(appPage.getApplicationId()).willReturn(APPLICATION_ID);\n        given(applicationService.deleteApplicationPage(APPLICATION_PAGE_ID)).willReturn(appPage);\n\n        //when\n        delegate.deleteApplicationPage(APPLICATION_PAGE_ID);\n\n        //then\n        verify(applicationService, times(1)).deleteApplicationPage(APPLICATION_PAGE_ID);\n    }\n\n    @Test(expected = DeletionException.class)\n    public void deleteApplicationPage_should_throw_DeletionException_when_applicationService_throws_SObjectModificationException()\n            throws Exception {\n        doThrow(new SObjectModificationException()).when(applicationService).deleteApplicationPage(APPLICATION_PAGE_ID);\n\n        //when\n        delegate.deleteApplicationPage(APPLICATION_PAGE_ID);\n\n        //then exception\n    }\n\n    @Test(expected = DeletionException.class)\n    public void deleteApplicationPage_should_throw_DeletionException_when_applicationService_throws_SObjectNotFoundException()\n            throws Exception {\n        doThrow(new SObjectNotFoundException()).when(applicationService).deleteApplicationPage(APPLICATION_PAGE_ID);\n\n        //when\n        delegate.deleteApplicationPage(APPLICATION_PAGE_ID);\n\n        //then exception\n    }\n\n    @Test\n    public void getApplicationHomePage_should_return_the_result_of_applicationService_getApplicationHomePage()\n            throws Exception {\n        //given\n        final ApplicationPageImpl appPage = new ApplicationPageImpl(APPLICATION_ID, PAGE_ID, APP_PAGE_TOKEN);\n        final SApplicationPage sAppPage = new SApplicationPage(APPLICATION_ID, PAGE_ID, APP_PAGE_TOKEN);\n        given(applicationService.getApplicationHomePage(APPLICATION_ID)).willReturn(sAppPage);\n        given(converter.toApplicationPage(sAppPage)).willReturn(appPage);\n\n        //when\n        final ApplicationPage retrievedAppPage = delegate.getApplicationHomePage(APPLICATION_ID);\n\n        //then\n        assertThat(retrievedAppPage).isEqualTo(appPage);\n    }\n\n    @Test(expected = RetrieveException.class)\n    public void getApplicationHomePage_should_throw_RetrieveException_when_applicationService_throws_SBonitaReadException()\n            throws Exception {\n        //given\n        given(applicationService.getApplicationHomePage(APPLICATION_ID)).willThrow(new SBonitaReadException(\"\"));\n\n        //when\n        delegate.getApplicationHomePage(APPLICATION_ID);\n\n        //then exception\n    }\n\n    @Test(expected = ApplicationPageNotFoundException.class)\n    public void getApplicationHomePage_should_throw_ApplicationPageNotFoundException_when_applicationService_throws_SObjectNotFoundException()\n            throws Exception {\n        //given\n        given(applicationService.getApplicationHomePage(APPLICATION_ID)).willThrow(new SObjectNotFoundException(\"\"));\n\n        //when\n        delegate.getApplicationHomePage(APPLICATION_ID);\n\n        //then exception\n    }\n\n    @Test\n    public void searchApplicationPages_should_return_the_result_of_searchApplicationPages_getResult() throws Exception {\n        //given\n        given(searchApplicationPages.getResult()).willReturn(appPageSearchResult);\n\n        //when\n        final SearchResult<ApplicationPage> retrievedSearchResult = delegate\n                .searchApplicationPages(searchApplicationPages);\n\n        //then\n        assertThat(retrievedSearchResult).isEqualTo(appPageSearchResult);\n        verify(searchApplicationPages, times(1)).execute();\n    }\n\n    @Test(expected = SearchException.class)\n    public void searchApplicationPages_should_throw_SearchException_when_searchApplicationPages_throws_SBonitaException()\n            throws Exception {\n        //given\n        doThrow(new SBonitaReadException(\"\")).when(searchApplicationPages).execute();\n\n        //when\n        delegate.searchApplicationPages(searchApplicationPages);\n\n        //then exception\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/page/PageAPIDelegateTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.page;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.commons.Pair.pair;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.*;\n\nimport java.io.IOException;\nimport java.util.*;\n\nimport org.bonitasoft.engine.api.impl.resolver.BusinessArchiveArtifactsManager;\nimport org.bonitasoft.engine.api.impl.transaction.page.SearchPages;\nimport org.bonitasoft.engine.commons.exceptions.SObjectAlreadyExistsException;\nimport org.bonitasoft.engine.commons.io.IOUtil;\nimport org.bonitasoft.engine.core.form.FormMappingService;\nimport org.bonitasoft.engine.core.form.SFormMapping;\nimport org.bonitasoft.engine.exception.*;\nimport org.bonitasoft.engine.page.*;\nimport org.bonitasoft.engine.page.PageUpdater.PageUpdateField;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class PageAPIDelegateTest {\n\n    private static final long PAGE_ID = 4578;\n    private static final Long PROCESS_ID_1 = 566446515L;\n    private static final Long PROCESS_ID_2 = 566746515L;\n    private final long userId = 123L;\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n    @Mock\n    SearchPages searchPages;\n    @Mock\n    ServiceAccessor serviceAccessor;\n    @Mock\n    SPage sPage;\n    @Mock\n    Page page;\n    @Mock\n    private PageUpdater pageUpdater;\n    @Mock\n    private SPageUpdateBuilder sPageUpdateBuilder;\n\n    @Mock\n    private PageService pageService;\n    @Mock\n    private PageMappingService pageMappingService;\n    @Mock\n    private FormMappingService formMappingService;\n    @Mock\n    private BusinessArchiveArtifactsManager businessArchiveArtifactsManager;\n\n    @Spy\n    @InjectMocks\n    private PageAPIDelegate pageAPIDelegate;\n\n    @Test\n    public void testSearchPages() throws Exception {\n        doReturn(searchPages).when(pageAPIDelegate).getSearchPages(any(SearchOptions.class));\n\n        // when\n        final SearchOptions searchOptions = new SearchOptionsBuilder(0, 5).searchTerm(\"search\").done();\n        pageAPIDelegate.searchPages(searchOptions);\n\n        // then\n        verify(searchPages, times(1)).execute();\n    }\n\n    @Test\n    public void createPageShouldCallAddPageOnPageService() throws Exception {\n        // given\n        final PageCreator pageCreator = new PageCreator(\"name\", \"content.zip\");\n        final byte[] content = \"content\".getBytes();\n\n        doReturn(sPage).when(pageAPIDelegate).constructPage(pageCreator, userId);\n        doReturn(page).when(pageAPIDelegate).convertToPage(nullable(SPage.class));\n\n        // when\n        pageAPIDelegate.createPage(pageCreator, content, userId);\n\n        // then\n        verify(pageService, times(1)).addPage(sPage, content);\n    }\n\n    @Test\n    public void createPageWithContentShouldCallAddPageOnPageService() throws Exception {\n        // given\n        final byte[] content = \"content\".getBytes();\n        final String contentName = \"contentName\";\n\n        doReturn(page).when(pageAPIDelegate).convertToPage(nullable(SPage.class));\n\n        // when\n        pageAPIDelegate.createPage(contentName, content, userId);\n\n        // then\n        verify(pageService, times(1)).addPage(content, contentName, userId);\n    }\n\n    @Test\n    public void testDeletePage() throws Exception {\n        final long pageId = 123;\n        final SFormMapping formMapping = new SFormMapping();\n        formMapping.setPageMapping(new SPageMapping());\n        doReturn(Collections.singletonList(formMapping)).when(formMappingService)\n                .searchFormMappings(any(QueryOptions.class));\n\n        pageAPIDelegate.deletePage(pageId);\n\n        verify(pageService).deletePage(pageId);\n        verify(pageMappingService).update(any(SPageMapping.class), isNull()); // As the page has just been deleted\n        verify(formMappingService, times(1)).searchFormMappings(any(QueryOptions.class));\n    }\n\n    @Test\n    public void updatePageMapping_return_affected_processes() throws Exception {\n        doReturn(Arrays.asList(formMapping(PROCESS_ID_1), formMapping(PROCESS_ID_2), formMapping(PROCESS_ID_1)))\n                .when(formMappingService).searchFormMappings(\n                        any(QueryOptions.class));\n\n        final Set<Long> processDefinitionIds = pageAPIDelegate.updatePageMappings(PAGE_ID);\n\n        assertThat(processDefinitionIds).containsOnly(PROCESS_ID_1, PROCESS_ID_2);\n    }\n\n    @Test\n    public void deletePage_update_resolution_of_related_processes() throws Exception {\n        doReturn(Arrays.asList(formMapping(PROCESS_ID_1), formMapping(PROCESS_ID_2), formMapping(PROCESS_ID_1)))\n                .when(formMappingService).searchFormMappings(\n                        any(QueryOptions.class));\n\n        pageAPIDelegate.deletePage(PAGE_ID);\n\n        verify(businessArchiveArtifactsManager).resolveDependencies(PROCESS_ID_1, serviceAccessor);\n        verify(businessArchiveArtifactsManager).resolveDependencies(PROCESS_ID_2, serviceAccessor);\n    }\n\n    private SFormMapping formMapping(Long processId) {\n        final SFormMapping sFormMapping = new SFormMapping();\n        sFormMapping.setProcessDefinitionId(processId);\n        return sFormMapping;\n    }\n\n    @Test\n    public void testDeletePages() throws Exception {\n        // given\n        final List<Long> pageIds = new ArrayList<>();\n        for (long pageId = 0; pageId < 10; pageId++) {\n            pageIds.add(pageId);\n        }\n        doNothing().when(pageAPIDelegate).deletePage(anyLong());\n\n        // when\n        pageAPIDelegate.deletePages(pageIds);\n\n        // then\n        verify(pageAPIDelegate, times(pageIds.size())).deletePage(anyLong());\n    }\n\n    @Test\n    public void testGetPage() throws Exception {\n        // given\n        final long pageId = 123;\n        doReturn(sPage).when(pageService).getPage(pageId);\n\n        // when\n        pageAPIDelegate.getPage(pageId);\n\n        // then\n        verify(pageService, times(1)).getPage(pageId);\n    }\n\n    @Test\n    public void testGetPageByName() throws Exception {\n        // given\n        final String pageName = \"name\";\n        doReturn(sPage).when(pageService).getPageByName(pageName);\n\n        // when\n        pageAPIDelegate.getPageByName(pageName);\n\n        // then\n        verify(pageService, times(1)).getPageByName(pageName);\n    }\n\n    @Test(expected = PageNotFoundException.class)\n    public void testGetPageByNameNotFound() throws Exception {\n        // when\n        pageAPIDelegate.getPageByName(\"unknown\");\n        // then: exception\n    }\n\n    @Test(expected = UpdateException.class)\n    public void testUpdatePageWithEmptyUpdateFileShouldThrowExceptions() throws Exception {\n        // given\n        final Map<PageUpdateField, String> map = new HashMap<>();\n        doReturn(map).when(pageUpdater).getFields();\n\n        // when\n        pageAPIDelegate.updatePage(1, pageUpdater, userId);\n\n        // then\n        verify(pageService, times(1)).updatePage(anyLong(), any(EntityUpdateDescriptor.class));\n    }\n\n    @Test\n    public void testUpdatePageContent() throws Exception {\n        doReturn(sPageUpdateBuilder).when(pageAPIDelegate).getPageUpdateBuilder();\n\n        // given\n        @SuppressWarnings(\"unchecked\")\n        final byte[] content = IOUtil.zip(pair(\"Index.groovy\", \"content of the groovy\".getBytes()),\n                pair(\"page.properties\",\n                        \"name=mypage\\ndisplayName=mypage display name\\ndescription=mypage description\\n\".getBytes()));\n        final long pageId = 1;\n\n        // when\n        pageAPIDelegate.updatePageContent(pageId, content, userId);\n\n        // then\n        verify(pageService, times(1)).updatePageContent(anyLong(), eq(content), nullable(String.class),\n                eq(sPageUpdateBuilder));\n    }\n\n    @Test\n    public void testUpdatePage() throws Exception {\n        final Map<PageUpdateField, String> map = new HashMap<>();\n        doReturn(sPage).when(pageAPIDelegate).constructPage(any(PageUpdater.class), anyLong());\n        doReturn(page).when(pageAPIDelegate).convertToPage(nullable(SPage.class));\n        doReturn(sPageUpdateBuilder).when(pageAPIDelegate).getPageUpdateBuilder();\n        doReturn(map).when(pageUpdater).getFields();\n\n        // given\n        map.put(PageUpdateField.DISPLAY_NAME, \"displayname\");\n        map.put(PageUpdateField.DESCRIPTION, \"description\");\n        map.put(PageUpdateField.CONTENT_NAME, \"content.zip\");\n\n        // when\n        pageAPIDelegate.updatePage(1, pageUpdater, userId);\n\n        // then\n        verify(pageService, times(1)).updatePage(anyLong(), nullable(EntityUpdateDescriptor.class));\n    }\n\n    @Test\n    public void testGetPageContent() throws Exception {\n        // given\n        final long pageId = 123;\n\n        // when\n        pageAPIDelegate.getPageContent(pageId);\n\n        // then\n        verify(pageService, times(1)).getPageContent(pageId);\n    }\n\n    @Test(expected = AlreadyExistsException.class)\n    public void testCheckPageAlreadyExists() throws Exception {\n        // given\n        final PageCreator pageCreator = new PageCreator(\"name\", \"content.zip\");\n        doReturn(sPage).when(pageAPIDelegate).constructPage(pageCreator, userId);\n        final byte[] content = IOUtil.zip(Collections.singletonMap(\"Index.groovy\", \"content of the groovy\".getBytes()));\n        doThrow(SObjectAlreadyExistsException.class).when(pageService).addPage(sPage, content);\n\n        // when\n        pageAPIDelegate.createPage(pageCreator, content, userId);\n\n        // then\n        // AlreadyExistsException\n\n    }\n\n    @Test\n    public void should_getPageProperties_check_already_exists_if_check_to_true() throws Exception {\n        // given\n        final byte[] content = new byte[] { 1, 2, 3 };\n        final Properties properties = new Properties();\n        properties.setProperty(PageService.PROPERTIES_NAME, \"MyPage\");\n        doReturn(properties).when(pageService).readPageZip(content);\n        doReturn(null).when(pageService).getPageByName(\"MyPage\");\n        // when\n        final Properties pageProperties = pageAPIDelegate.getPageProperties(content, true);\n\n        // then\n        assertThat(pageProperties).isEqualTo(properties);\n    }\n\n    @Test\n    public void should_getPageProperties_throw_already_exists_if_check_to_true() throws Exception {\n        // given\n        final byte[] content = new byte[] { 1, 2, 3 };\n        final Properties properties = new Properties();\n        properties.setProperty(PageService.PROPERTIES_NAME, \"MyPage\");\n        doReturn(properties).when(pageService).readPageZip(content);\n        doReturn(mock(SPage.class)).when(pageService).getPageByName(\"MyPage\");\n        // when\n        expectedException.expect(AlreadyExistsException.class);\n        pageAPIDelegate.getPageProperties(content, true);\n\n        // then\n        // AlreadyExistsException\n    }\n\n    @Test\n    public void should_getPageProperties_throw_retrieve_exception() throws Exception {\n        // given\n        final byte[] content = new byte[] { 1, 2, 3 };\n        final Properties properties = new Properties();\n        properties.setProperty(PageService.PROPERTIES_NAME, \"MyPage\");\n        doReturn(properties).when(pageService).readPageZip(content);\n        doThrow(new SBonitaReadException(new IOException(\"IO issue\"))).when(pageService).getPageByName(\"MyPage\");\n        // when\n        expectedException.expect(RetrieveException.class);\n        pageAPIDelegate.getPageProperties(content, true);\n\n        // then\n        // AlreadyExistsException\n    }\n\n    @Test\n    public void should_getPageProperties_not_throw_already_exists_if_check_to_false() throws Exception {\n        // given\n        final byte[] content = new byte[] { 1, 2, 3 };\n        final Properties properties = new Properties();\n        properties.setProperty(PageService.PROPERTIES_NAME, \"MyPage\");\n        doReturn(properties).when(pageService).readPageZip(content);\n        // when\n        final Properties pageProperties = pageAPIDelegate.getPageProperties(content, false);\n\n        // then\n        assertThat(pageProperties).isEqualTo(properties);\n    }\n\n    @Test\n    public void should_getPageProperties_throw_Invalid_missing_index() throws Exception {\n        // given\n        final byte[] content = new byte[] { 1, 2, 3 };\n        doThrow(SInvalidPageZipMissingIndexException.class).when(pageService).readPageZip(content);\n        // when\n        expectedException.expect(InvalidPageZipMissingIndexException.class);\n        pageAPIDelegate.getPageProperties(content, false);\n    }\n\n    @Test\n    public void should_getPageProperties_throw_Invalid_missing_properties() throws Exception {\n        // given\n        final byte[] content = new byte[] { 1, 2, 3 };\n        doThrow(SInvalidPageZipMissingPropertiesException.class).when(pageService).readPageZip(content);\n        // when\n        expectedException.expect(InvalidPageZipMissingPropertiesException.class);\n        pageAPIDelegate.getPageProperties(content, false);\n    }\n\n    @Test\n    public void should_getPageProperties_throw_Invalid_missing_a_property() throws Exception {\n        // given\n        final byte[] content = new byte[] { 1, 2, 3 };\n        doThrow(SInvalidPageZipMissingAPropertyException.class).when(pageService).readPageZip(content);\n        // when\n        expectedException.expect(InvalidPageZipMissingAPropertyException.class);\n        pageAPIDelegate.getPageProperties(content, false);\n    }\n\n    @Test\n    public void should_getPageProperties_throw_Invalid_inconsistent() throws Exception {\n        // given\n        final byte[] content = new byte[] { 1, 2, 3 };\n        doThrow(SInvalidPageZipInconsistentException.class).when(pageService).readPageZip(content);\n        // when\n        expectedException.expect(InvalidPageZipInconsistentException.class);\n        pageAPIDelegate.getPageProperties(content, false);\n    }\n\n    @Test\n    public void should_getPageProperties_throw_Invalid_token() throws Exception {\n        // given\n        final byte[] content = new byte[] { 1, 2, 3 };\n        doThrow(SInvalidPageTokenException.class).when(pageService).readPageZip(content);\n        // when\n        expectedException.expect(InvalidPageTokenException.class);\n        pageAPIDelegate.getPageProperties(content, false);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/platform/PlatformInformationAPIImplTest.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.platform;\n\nimport static java.lang.String.valueOf;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.execution.ProcessStarterVerifierImpl.LIMIT;\nimport static org.mockito.Mockito.doReturn;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.execution.ProcessStarterVerifier;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\n@ExtendWith(MockitoExtension.class)\nclass PlatformInformationAPIImplTest {\n\n    @Mock\n    private ProcessStarterVerifier processStarterVerifier;\n\n    @InjectMocks\n    private PlatformInformationAPIImpl platformInformationAPI;\n\n    @Test\n    void platformInformationAPI_should_return_case_counters_info() {\n        //given\n        doReturn(120L).when(processStarterVerifier).getCurrentNumberOfStartedProcessInstances();\n\n        //when\n        final Map<String, String> platformInformation = platformInformationAPI.getPlatformInformation();\n\n        //then\n        assertThat(platformInformation).containsAllEntriesOf(Map.of(\n                \"edition\", \"Community\",\n                \"caseCounter\", \"120\",\n                \"caseCounterLimit\", valueOf(LIMIT)));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/profile/ProfileAPIIDelegateTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.profile;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyLong;\nimport static org.mockito.Mockito.*;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.impl.AvailableInMaintenanceMode;\nimport org.bonitasoft.engine.api.impl.ProfileAPIImpl;\nimport org.bonitasoft.engine.api.impl.transaction.profile.CreateProfileMember;\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.MemberType;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.profile.Profile;\nimport org.bonitasoft.engine.profile.ProfileCriterion;\nimport org.bonitasoft.engine.profile.ProfileMember;\nimport org.bonitasoft.engine.profile.ProfileService;\nimport org.bonitasoft.engine.profile.model.SProfileMember;\nimport org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ProfileAPIIDelegateTest {\n\n    private static final long USER_ID = 1L;\n\n    private static final long GROUP_ID = 1L;\n\n    private static final long ROLE_ID = 1L;\n\n    @Mock\n    ProfileService profileService;\n\n    @Mock\n    private IdentityService identityService;\n\n    @Mock\n    SProfileMember sProfileMember;\n\n    @Mock\n    private SUser sUser;\n\n    @Mock\n    private ProfileMember profileMember;\n\n    @Mock\n    private ApplicationService applicationService;\n\n    @Mock\n    private SearchEntitiesDescriptor searchEntitiesDescriptor;\n\n    private ProfileAPIDelegate profileApiDelegate;\n\n    @Before\n    public void before() throws Exception {\n        profileApiDelegate = spy(\n                new ProfileAPIDelegate(profileService, identityService, searchEntitiesDescriptor, applicationService));\n        doReturn(sUser).when(identityService).getUser(anyLong());\n        doReturn(profileMember).when(profileApiDelegate).convertToProfileMember(any(CreateProfileMember.class));\n        doReturn(sProfileMember).when(profileService).getProfileMemberWithoutDisplayName(anyLong());\n    }\n\n    @Test\n    public void getProfilesForUser_should_return_empty_list_for_system_user() throws SBonitaReadException {\n        // when:\n        final List<Profile> profilesForUser = profileApiDelegate.getProfilesForUser(-1, 0, 10, ProfileCriterion.ID_ASC);\n\n        // then:\n        assertThat(profilesForUser).isEmpty();\n        verify(profileService, never()).searchProfilesOfUser(anyLong(), anyInt(), anyInt(), anyString(),\n                any(OrderByType.class));\n    }\n\n    @Test\n    public void should_deleteProfileMember_update_profile_metadata() throws Exception {\n        // when\n        profileApiDelegate.deleteProfileMember(1L);\n\n        // then\n        verify(profileService, times(1)).updateProfileMetaData(anyLong());\n    }\n\n    @Test\n    public void should_updateProfileMetaData_update_profilemetadata() throws Exception {\n        final Long profileId = 1L;\n        final Long userId = 2L;\n        final Long groupId = 3L;\n        final Long roleId = 4L;\n\n        doNothing().when(profileApiDelegate).checkIfProfileMemberExists(\n                any(Long.class), any(Long.class), any(Long.class), any(Long.class),\n                any(MemberType.class));\n\n        // when\n        profileApiDelegate.createProfileMember(profileId, userId, groupId, roleId);\n\n        // then\n        verify(profileService, times(1)).updateProfileMetaData(anyLong());\n\n    }\n\n    @Test\n    public void memberType_User() throws Exception {\n        assertThat(profileApiDelegate.getMemberType(USER_ID, null, null)).isEqualTo(MemberType.USER);\n        assertThat(profileApiDelegate.getMemberType(USER_ID, -1L, null)).isEqualTo(MemberType.USER);\n        assertThat(profileApiDelegate.getMemberType(USER_ID, -1L, -1L)).isEqualTo(MemberType.USER);\n\n    }\n\n    @Test\n    public void memberType_Group() throws Exception {\n        assertThat(profileApiDelegate.getMemberType(null, GROUP_ID, null)).isEqualTo(MemberType.GROUP);\n        assertThat(profileApiDelegate.getMemberType(-1L, GROUP_ID, null)).isEqualTo(MemberType.GROUP);\n        assertThat(profileApiDelegate.getMemberType(null, GROUP_ID, -1L)).isEqualTo(MemberType.GROUP);\n    }\n\n    @Test\n    public void memberType_Role() throws Exception {\n        assertThat(profileApiDelegate.getMemberType(null, null, ROLE_ID)).isEqualTo(MemberType.ROLE);\n        assertThat(profileApiDelegate.getMemberType(-1L, null, ROLE_ID)).isEqualTo(MemberType.ROLE);\n        assertThat(profileApiDelegate.getMemberType(null, -1L, ROLE_ID)).isEqualTo(MemberType.ROLE);\n\n    }\n\n    @Test\n    public void memberType_MemberShip() throws Exception {\n        assertThat(profileApiDelegate.getMemberType(null, GROUP_ID, ROLE_ID)).isEqualTo(MemberType.MEMBERSHIP);\n        assertThat(profileApiDelegate.getMemberType(-1L, GROUP_ID, ROLE_ID)).isEqualTo(MemberType.MEMBERSHIP);\n\n    }\n\n    @Test\n    public void should_profile_api_be_available_when_tenant_is_paused() throws Exception {\n        assertThat(ProfileAPIImpl.class.isAnnotationPresent(AvailableInMaintenanceMode.class))\n                .as(\"should profile api be available when tenant is paused\")\n                .isTrue();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/resolver/BusinessDataBusinessArchiveArtifactManagerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.resolver;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.when;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.bpm.process.Problem;\nimport org.bonitasoft.engine.business.data.BusinessDataRepository;\nimport org.bonitasoft.engine.core.process.definition.model.SBusinessDataDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SBusinessDataDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SFlowElementContainerDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SProcessDefinitionImpl;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class BusinessDataBusinessArchiveArtifactManagerTest {\n\n    @Mock\n    private BusinessDataRepository businessDataRepository;\n\n    private BusinessDataBusinessArchiveArtifactManager resolver;\n\n    @Before\n    public void setUp() {\n        resolver = new BusinessDataBusinessArchiveArtifactManager(businessDataRepository);\n        final Set<String> entityClassNames = new HashSet<>();\n        entityClassNames.add(\"com.bonitasoft.Employee\");\n        entityClassNames.add(\"com.bonitasoft.LeaveRequest\");\n\n        when(businessDataRepository.getEntityClassNames()).thenReturn(entityClassNames);\n    }\n\n    private SProcessDefinition buildProcessDefinition(final SBusinessDataDefinition... businessDataDefinitions) {\n        final SFlowElementContainerDefinitionImpl processContainer = new SFlowElementContainerDefinitionImpl();\n        final SProcessDefinitionImpl processDefinition = new SProcessDefinitionImpl(\"process\", \"1.0\");\n        processDefinition.setProcessContainer(processContainer);\n        for (final SBusinessDataDefinition sBusinessDataDefinition : businessDataDefinitions) {\n            processContainer.addBusinessDataDefinition(sBusinessDataDefinition);\n        }\n        return processDefinition;\n    }\n\n    private SBusinessDataDefinition buildBusinessDataDefinition(final String name, final String className) {\n        final SBusinessDataDefinitionImpl businessDataDefinition = new SBusinessDataDefinitionImpl();\n        businessDataDefinition.setName(name);\n        businessDataDefinition.setClassName(className);\n        return businessDataDefinition;\n    }\n\n    @Test\n    public void checkResolution_returns_no_problem_with_no_business_data() {\n        final SProcessDefinition processDefinition = buildProcessDefinition();\n\n        final List<Problem> problems = resolver.checkResolution(processDefinition);\n\n        assertThat(problems).isEmpty();\n    }\n\n    @Test\n    public void checkResolution_returns_no_problem_with_a_valid_business_data() {\n        final SProcessDefinition processDefinition = buildProcessDefinition(\n                buildBusinessDataDefinition(\"bizData\", \"com.bonitasoft.Employee\"));\n\n        final List<Problem> problems = resolver.checkResolution(processDefinition);\n\n        assertThat(problems).isEmpty();\n    }\n\n    @Test\n    public void checkResolution_returns_a_problem_with_invalid_business_data() {\n        final SProcessDefinition processDefinition = buildProcessDefinition(\n                buildBusinessDataDefinition(\"bizData1\", \"com.bonitasoft.Address\"),\n                buildBusinessDataDefinition(\"bizData2\", Long.class.getName()));\n\n        final List<Problem> problems = resolver.checkResolution(processDefinition);\n\n        assertThat(problems).areExactly(2, new ProblemCondition());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/resolver/DocumentInitialValueArtifactManagerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.resolver;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.verify;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.bar.BarResource;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SProcessDefinitionImpl;\nimport org.bonitasoft.engine.resources.BARResourceType;\nimport org.bonitasoft.engine.resources.ProcessResourcesService;\nimport org.bonitasoft.engine.resources.SBARResource;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Baptiste Mesta\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class DocumentInitialValueArtifactManagerTest {\n\n    public static final long PROCESS_ID = 456L;\n    @Mock\n    private ProcessResourcesService processResourcesService;\n    @InjectMocks\n    private DocumentInitialValueArtifactManager documentInitialValueDependencyManager;\n\n    @Test\n    public void deploy_should_call_resourceService_add_on_each_resource() throws Exception {\n        final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive();\n        businessArchiveBuilder.addDocumentResource(new BarResource(\"myDoc.pdf\", new byte[] { 0 }));\n        businessArchiveBuilder.addDocumentResource(new BarResource(\"myDoc2.pdf\", new byte[] { 1 }));\n        businessArchiveBuilder\n                .setProcessDefinition(new ProcessDefinitionBuilder().createNewInstance(\"tata\", \"toto\").done());\n        final SProcessDefinitionImpl processDefinition = new SProcessDefinitionImpl(\"tata\", \"toto\");\n        processDefinition.setId(PROCESS_ID);\n\n        documentInitialValueDependencyManager.deploy(businessArchiveBuilder.done(), processDefinition);\n\n        verify(processResourcesService).add(PROCESS_ID, \"myDoc.pdf\", BARResourceType.DOCUMENT, new byte[] { 0 });\n        verify(processResourcesService).add(PROCESS_ID, \"myDoc2.pdf\", BARResourceType.DOCUMENT, new byte[] { 1 });\n    }\n\n    @Test\n    public void exportBusinessArchive_should_export_only_initial_documents() throws Exception {\n        final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive();\n        businessArchiveBuilder\n                .setProcessDefinition(new ProcessDefinitionBuilder().createNewInstance(\"tata\", \"toto\").done());\n        doReturn(getBarResources()).when(processResourcesService).get(PROCESS_ID, BARResourceType.DOCUMENT, 0, 10);\n        doReturn(Collections\n                .singletonList(new SBARResource(\"10.pdf\", BARResourceType.DOCUMENT, PROCESS_ID, new byte[] { 10 })))\n                .when(\n                        processResourcesService)\n                .get(PROCESS_ID, BARResourceType.DOCUMENT, 10, 10);\n\n        documentInitialValueDependencyManager.exportToBusinessArchive(PROCESS_ID, businessArchiveBuilder);\n\n        final BusinessArchive done = businessArchiveBuilder.done();\n        final Map<String, byte[]> resources = done.getResources(\"^documents/.*\");\n        final ArrayList<String> strings = new ArrayList<>();\n        for (int i = 0; i < 11; i++) {\n            strings.add(\"documents/\" + i + \".pdf\");\n        }\n        assertThat(resources.keySet()).containsOnly(strings.toArray(new String[strings.size()]));\n\n    }\n\n    List<SBARResource> getBarResources() {\n        final List<SBARResource> sbarResources = new ArrayList<>();\n        for (int i = 0; i < 10; i++) {\n            final SBARResource sbarResource = new SBARResource(i + \".pdf\", BARResourceType.DOCUMENT, PROCESS_ID,\n                    new byte[] { (byte) i });\n            sbarResources.add(sbarResource);\n        }\n        return sbarResources;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/resolver/FormMappingAndPageArtifactManagerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.resolver;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.tuple;\nimport static org.bonitasoft.engine.bpm.form.FormMappingDefinitionBuilder.buildFormMapping;\nimport static org.bonitasoft.engine.bpm.form.FormMappingModelBuilder.buildFormMappingModel;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.*;\nimport static org.mockito.Mockito.nullable;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.UUID;\n\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;\nimport org.bonitasoft.engine.bpm.bar.form.model.FormMappingDefinition;\nimport org.bonitasoft.engine.bpm.bar.form.model.FormMappingModel;\nimport org.bonitasoft.engine.bpm.form.FormMappingModelBuilder;\nimport org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException;\nimport org.bonitasoft.engine.bpm.process.Problem;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.core.form.FormMappingService;\nimport org.bonitasoft.engine.core.form.SFormMapping;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.form.FormMappingTarget;\nimport org.bonitasoft.engine.form.FormMappingType;\nimport org.bonitasoft.engine.page.PageService;\nimport org.bonitasoft.engine.page.SPage;\nimport org.bonitasoft.engine.page.SPageMapping;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class FormMappingAndPageArtifactManagerTest {\n\n    public static final String PAGE = \"myPage\";\n    public static final byte[] CONTENT1 = \"content1\".getBytes();\n    public static final byte[] CONTENT2 = \"content2\".getBytes();\n    public static final long PROCESS_DEFINITION_ID = 15L;\n    public static final long USER_ID = 12L;\n    public static final long PAGE_ID = 45L;\n    public static final String CUSTOMPAGE_STEP1_ZIP = \"custompage_step1.zip\";\n    public static final String CUSTOMPAGE_STEP2_ZIP = \"custompage_step2.zip\";\n    public static final String DISPLAY_NAME = \"display name\";\n    public static final String DESCRIPTION = \"description\";\n    private java.util.Map<java.lang.String, byte[]> resources;\n    @Mock\n    private Properties properties;\n    private FormMappingAndPageArtifactManager formMappingAndPageArtifactManager;\n    @Mock\n    private BusinessArchive businessArchive;\n    @Mock\n    private SProcessDefinition sDefinition;\n    @Mock\n    private PageService pageService;\n    @Mock\n    private FormMappingService formMappingService;\n    @Mock\n    private SPage sPage;\n    private List<SFormMapping> formMappings;\n    @Mock\n    private SPageMapping sPageMapping;\n    @Mock\n    private SessionService sessionService;\n    @Mock\n    private SessionAccessor sessionAccessor;\n    @Mock\n    private ProcessDefinitionService processDefinitionService;\n\n    private BusinessArchiveBuilder barBuilder;\n\n    @Before\n    public void before() {\n        formMappingAndPageArtifactManager = spy(\n                new FormMappingAndPageArtifactManager(sessionService, sessionAccessor, pageService, formMappingService,\n                        processDefinitionService));\n        formMappings = new ArrayList<>();\n\n        doReturn(PROCESS_DEFINITION_ID).when(sDefinition).getId();\n\n        resources = new HashMap<>();\n        resources.put(\"resources/customPages/custompage_step1.zip\", CONTENT1);\n        resources.put(\"resources/customPages/custompage_step2.zip\", CONTENT2);\n        resources.put(\"resources/otherResource/data.txt\", \"data\".getBytes());\n\n        doReturn(resources).when(businessArchive).getResources();\n        doReturn(14L).when(sessionService).getLoggedUserFromSession(sessionAccessor);\n        when(businessArchive.getResources(anyString())).thenCallRealMethod();\n    }\n\n    @Test\n    public void deployPageFailure_Should_Not_Interrupt_Deployment() throws Exception {\n        //given\n        formMappings.add(new SFormMapping(PROCESS_DEFINITION_ID, FormMappingType.PROCESS_OVERVIEW.getId(), \"task\",\n                SFormMapping.TARGET_INTERNAL));\n        BusinessArchive bar = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setFormMappings(buildFormMappingModel().build())\n                .setProcessDefinition(new ProcessDefinitionBuilder().createNewInstance(\"mockProcess\", \"1.0\").done())\n                .done();\n        doReturn(new ArrayList<Problem>()).when(formMappingAndPageArtifactManager)\n                .checkResolution(any(SProcessDefinition.class));\n        //when then\n        formMappingAndPageArtifactManager.deploy(bar, sDefinition);\n        verify(formMappingAndPageArtifactManager).checkResolution(any(SProcessDefinition.class));\n    }\n\n    @Test\n    public void should_check_resolution_report_problem_when_page_is_missing() throws Exception {\n        //given\n        doReturn(1L).when(sPageMapping).getPageId();\n        final SFormMapping sFormMapping = new SFormMapping(PROCESS_DEFINITION_ID, FormMappingType.TASK.getId(), null,\n                SFormMapping.TARGET_INTERNAL);\n        sFormMapping.setPageMapping(sPageMapping);\n        formMappings.add(sFormMapping);\n        doReturn(formMappings).when(formMappingService).list(eq(PROCESS_DEFINITION_ID), anyInt(), anyInt());\n        doReturn(null).when(pageService).getPage(anyLong());\n\n        //when\n        final List<Problem> problems = formMappingAndPageArtifactManager.checkResolution(sDefinition);\n\n        // then\n        assertThat(problems).as(\"should return a problem\").hasSize(1);\n        ProblemAssert.assertThat(problems.get(0))\n                .hasDescription(String.format(FormMappingAndPageArtifactManager.ERROR_MESSAGE_FORM_NOT_FOUND,\n                        sPageMapping.getKey(), 1L))\n                .hasLevel(Problem.Level.ERROR);\n\n    }\n\n    @Test\n    public void should_check_resolution_report_problem_when_formMapping_on_page_has_no_pageMapping() throws Exception {\n        //given\n        doReturn(1L).when(sPageMapping).getPageId();\n        final SFormMapping sFormMapping = new SFormMapping(PROCESS_DEFINITION_ID, FormMappingType.TASK.getId(), null,\n                SFormMapping.TARGET_INTERNAL);\n        sFormMapping.setPageMapping(sPageMapping);\n        formMappings.add(sFormMapping);\n        doReturn(formMappings).when(formMappingService).list(eq(PROCESS_DEFINITION_ID), anyInt(), anyInt());\n        doReturn(null).when(pageService).getPage(anyLong());\n\n        //when\n        final List<Problem> problems = formMappingAndPageArtifactManager.checkResolution(sDefinition);\n\n        // then\n        assertThat(problems).as(\"should return a problem\").hasSize(1);\n        ProblemAssert.assertThat(problems.get(0))\n                .hasDescription(String.format(FormMappingAndPageArtifactManager.ERROR_MESSAGE_FORM_NOT_FOUND,\n                        sPageMapping.getKey(), sPageMapping.getPageId()))\n                .hasLevel(Problem.Level.ERROR);\n\n    }\n\n    @Test\n    public void should_check_resolution_report_problems_when_mapping_has_no_pages() throws Exception {\n        //given\n        doReturn(null).when(sPageMapping).getPageId();\n        final SFormMapping sFormMapping = new SFormMapping(PROCESS_DEFINITION_ID, FormMappingType.TASK.getId(), null,\n                SFormMapping.TARGET_INTERNAL);\n        sFormMapping.setPageMapping(sPageMapping);\n        formMappings.add(sFormMapping);\n        doReturn(formMappings).when(formMappingService).list(eq(PROCESS_DEFINITION_ID), anyInt(), anyInt());\n\n        //when\n        final List<Problem> problems = formMappingAndPageArtifactManager.checkResolution(sDefinition);\n\n        // then\n        assertThat(problems).as(\"should not return a problem\").isNotEmpty();\n\n    }\n\n    @Test\n    public void should_format_message_when_form_mapping_page_is_null() throws Exception {\n        //given\n        final SFormMapping sFormMapping = new SFormMapping(PROCESS_DEFINITION_ID,\n                FormMappingType.PROCESS_OVERVIEW.getId(), null,\n                SFormMapping.TARGET_INTERNAL);\n        sFormMapping.setPageMapping(sPageMapping);\n        formMappings.add(sFormMapping);\n        doReturn(formMappings).when(formMappingService).list(eq(PROCESS_DEFINITION_ID), anyInt(), anyInt());\n        doReturn(null).when(sPageMapping).getPageId();\n\n        //when\n        final List<Problem> problems = formMappingAndPageArtifactManager.checkResolution(sDefinition);\n\n        // then\n        assertThat(problems).as(\"should return a problem\").hasSize(1);\n        ProblemAssert.assertThat(problems.get(0))\n                .hasDescription(String.format(FormMappingAndPageArtifactManager.ERROR_MESSAGE_FORM_NOT_FOUND,\n                        sPageMapping.getKey(), null))\n                .hasLevel(Problem.Level.ERROR);\n\n    }\n\n    @Test\n    public void should_check_resolution_throw_exception() throws Exception {\n        //given\n        final SFormMapping sFormMapping = new SFormMapping(PROCESS_DEFINITION_ID,\n                FormMappingType.PROCESS_OVERVIEW.getId(), \"task\",\n                SFormMapping.TARGET_INTERNAL);\n        sFormMapping.setPageMapping(sPageMapping);\n        formMappings.add(sFormMapping);\n        doReturn(formMappings).when(formMappingService).list(eq(PROCESS_DEFINITION_ID), anyInt(), anyInt());\n        doThrow(SBonitaReadException.class).when(pageService).getPage(anyLong());\n\n        //when\n        final List<Problem> problems = formMappingAndPageArtifactManager.checkResolution(sDefinition);\n\n        // then\n        assertThat(problems).as(\"should return a problem\").hasSize(1);\n\n    }\n\n    @Test\n    public void should_deploy_process_insert_pages() throws Exception {\n        //given\n        doReturn(null).when(pageService).getPageByNameAndProcessDefinitionId(anyString(), anyLong());\n        doReturn(properties).when(pageService).readPageZip(any(byte[].class));\n\n        doReturn(DISPLAY_NAME).when(properties).getProperty(PageService.PROPERTIES_DISPLAY_NAME);\n        doReturn(DESCRIPTION).when(properties).getProperty(PageService.PROPERTIES_DESCRIPTION);\n\n        //when\n        formMappingAndPageArtifactManager.deployProcessPages(businessArchive, PROCESS_DEFINITION_ID, USER_ID);\n\n        //then\n        verify(pageService, times(2)).addPage(any(SPage.class), any(byte[].class));\n        verify(pageService, never()).updatePageContent(anyLong(), any(byte[].class), anyString());\n    }\n\n    @Test\n    public void should_deploy_process_update_pages() throws Exception {\n        //given\n        doReturn(PAGE_ID).when(sPage).getId();\n        doReturn(sPage).when(pageService).getPageByNameAndProcessDefinitionId(anyString(), anyLong());\n\n        //when\n        formMappingAndPageArtifactManager.deployProcessPages(businessArchive, PROCESS_DEFINITION_ID, USER_ID);\n\n        //then\n        verify(pageService, never()).addPage(any(SPage.class), any(byte[].class));\n        verify(pageService).updatePageContent(PAGE_ID, CONTENT1, CUSTOMPAGE_STEP1_ZIP);\n        verify(pageService).updatePageContent(PAGE_ID, CONTENT2, CUSTOMPAGE_STEP2_ZIP);\n    }\n\n    @Test\n    public void should_getPageResources_filter_resources() throws Exception {\n        //given\n\n        //when\n        final Map<String, byte[]> pageResources = formMappingAndPageArtifactManager.getPageResources(businessArchive);\n\n        //then\n        assertThat(pageResources).hasSize(2).containsKeys(\"resources/customPages/custompage_step1.zip\",\n                \"resources/customPages/custompage_step2.zip\");\n    }\n\n    @Before\n    public void init() throws InvalidProcessDefinitionException {\n        barBuilder = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(new ProcessDefinitionBuilder().createNewInstance(\"p\", \"1\").done());\n    }\n\n    @Test\n    public void deployShouldCreateDeclaredMappingsForProcessForms() throws Exception {\n        // when:\n        final long processDefinitionId = 3L;\n        final String startForm = \"startPage\";\n        final String overviewForm = \"overviewPage\";\n        formMappingAndPageArtifactManager.deployFormMappings(\n                barBuilder.setFormMappings(\n                        FormMappingModelBuilder.buildFormMappingModel()\n                                .addProcessStartForm(startForm, FormMappingTarget.INTERNAL)\n                                .addProcessOverviewForm(overviewForm, FormMappingTarget.INTERNAL)\n                                .build())\n                        .done(),\n                processDefinitionId);\n\n        // then:\n        verify(formMappingService, times(1))\n                .create(processDefinitionId, null, FormMappingType.PROCESS_START.getId(),\n                        FormMappingTarget.INTERNAL.name(), startForm);\n        verify(formMappingService, times(1))\n                .create(processDefinitionId, null, FormMappingType.PROCESS_OVERVIEW.getId(),\n                        FormMappingTarget.INTERNAL.name(), overviewForm);\n    }\n\n    @Test\n    public void deployShouldCreateNonDeclaredMappingsForProcessForms() throws Exception {\n        // given:\n        final long processDefinitionId = 3L;\n\n        // when:\n        formMappingAndPageArtifactManager.deployFormMappings(barBuilder.done(), processDefinitionId);\n\n        // then:\n        verify(formMappingService, times(1)).create(processDefinitionId, null, FormMappingType.PROCESS_OVERVIEW.getId(),\n                FormMappingTarget.NONE.name(),\n                null);\n        verify(formMappingService, times(1)).create(processDefinitionId, null, FormMappingType.PROCESS_START.getId(),\n                FormMappingTarget.NONE.name(), null);\n    }\n\n    @Test\n    public void deployShouldCreateDeclaredMappingsForDeclaredTaskForms() throws Exception {\n        // when:\n        final String taskname = \"taskname\";\n        final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder().createNewInstance(\"proc\",\n                \"5\");\n        processDefBuilder.addUserTask(taskname, null);\n\n        final long processDefinitionId = 3L;\n        final String form = \"pagename\";\n        final FormMappingType type = FormMappingType.TASK;\n        final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(\n                        processDefBuilder.done());\n        businessArchiveBuilder.setFormMappings(buildFormMappingModel().withFormMapping(\n                buildFormMapping(form, type, FormMappingTarget.INTERNAL).withTaskname(taskname).build()).build());\n        formMappingAndPageArtifactManager.deployFormMappings(businessArchiveBuilder.done(), processDefinitionId);\n\n        // then:\n        verify(formMappingService, times(1)).create(processDefinitionId, taskname, type.getId(),\n                FormMappingTarget.INTERNAL.name(), form);\n    }\n\n    @Test\n    public void deployShouldNotCreateDeclaredMappingsForDeclaredAutomaticTaskForms() throws Exception {\n        // when:\n        final String taskname = \"taskname\";\n        final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder().createNewInstance(\"proc\",\n                \"5\");\n        processDefBuilder.addAutomaticTask(taskname);\n\n        final long processDefinitionId = 3L;\n        final String form = \"pagename\";\n        final String type = FormMappingType.TASK.name();\n        final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(\n                        processDefBuilder.done());\n        businessArchiveBuilder.setFormMappings(buildFormMappingModel().withFormMapping(\n                buildFormMapping(form, FormMappingType.valueOf(type), FormMappingTarget.INTERNAL).withTaskname(taskname)\n                        .build())\n                .build());\n        formMappingAndPageArtifactManager.deployFormMappings(businessArchiveBuilder.done(), processDefinitionId);\n\n        // then:\n        verify(formMappingService, times(1)).create(processDefinitionId, null, FormMappingType.PROCESS_START.getId(),\n                FormMappingTarget.NONE.name(), null);\n        verify(formMappingService, times(1)).create(processDefinitionId, null, FormMappingType.PROCESS_OVERVIEW.getId(),\n                FormMappingTarget.NONE.name(),\n                null);\n        verifyNoMoreInteractions(formMappingService);\n    }\n\n    @Test\n    public void deployShouldCreateEmptyMappingsForNonDeclaredTasks() throws Exception {\n        // Given:\n        final long processDefinitionId = 5L;\n        final String taskName = \"taskname\";\n        final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder().createNewInstance(\"proc\",\n                \"5\");\n        processDefBuilder.addUserTask(taskName, null);\n        final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(processDefBuilder.done()).done();\n\n        // when:\n        formMappingAndPageArtifactManager.deployFormMappings(businessArchive, processDefinitionId);\n\n        // then:\n        verify(formMappingService, times(1)).create(processDefinitionId, taskName, FormMappingType.TASK.getId(),\n                FormMappingTarget.NONE.name(), null);\n    }\n\n    @Test\n    public void deployShouldIgnoreDeclaredMappingsForUndeclaredTasks() throws Exception {\n        // Given:\n        final long processDefinitionId = 5L;\n        final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder().createNewInstance(\"proc\",\n                \"5\");\n        final String declaredTaskName = \"declaredtaskName\";\n        processDefBuilder.addUserTask(declaredTaskName, null);\n        final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive()\n                .setProcessDefinition(\n                        processDefBuilder.done());\n        final String unknownTaskName = \"Unknown__Task__Name\";\n        businessArchiveBuilder.setFormMappings(FormMappingModelBuilder.buildFormMappingModel()\n                .addTaskForm(\"someForm\", FormMappingTarget.INTERNAL, unknownTaskName).build());\n        final BusinessArchive businessArchive = businessArchiveBuilder.done();\n\n        // when:\n        formMappingAndPageArtifactManager.deployFormMappings(businessArchive, processDefinitionId);\n\n        // then:\n        verify(formMappingService, times(1)).create(eq(processDefinitionId), eq(declaredTaskName),\n                eq(FormMappingType.TASK.getId()), anyString(),\n                nullable(String.class));\n        verify(formMappingService, times(0)).create(eq(processDefinitionId), eq(unknownTaskName),\n                eq(FormMappingType.TASK.getId()), anyString(),\n                nullable(String.class));\n    }\n\n    @Test\n    public void checkFormMappingResolutionShouldAddProblemsIfPageIdIsNull() throws Exception {\n        ArrayList<Problem> problems = new ArrayList<>();\n\n        SFormMapping sFormMappingTask = new SFormMapping(321324, FormMappingType.TASK.getId(), \"Step1\",\n                SFormMapping.TARGET_INTERNAL);\n        SFormMapping sFormMappingProcessOverview = new SFormMapping(321324, FormMappingType.PROCESS_OVERVIEW.getId(),\n                null,\n                SFormMapping.TARGET_INTERNAL);\n        SFormMapping sFormMappingProcessStart = new SFormMapping(321324, FormMappingType.PROCESS_START.getId(), null,\n                SFormMapping.TARGET_INTERNAL);\n\n        SPageMapping pageMapping = new SPageMapping();\n        sFormMappingTask.setPageMapping(pageMapping);\n        sFormMappingProcessOverview.setPageMapping(pageMapping);\n        sFormMappingProcessStart.setPageMapping(pageMapping);\n\n        formMappingAndPageArtifactManager.checkFormMappingResolution(sFormMappingTask, problems);\n        formMappingAndPageArtifactManager.checkFormMappingResolution(sFormMappingProcessOverview, problems);\n        formMappingAndPageArtifactManager.checkFormMappingResolution(sFormMappingProcessStart, problems);\n        assertThat(problems).as(\"the problem list should contain three mapping problems\").hasSize(3)\n                .extracting(\"resource\", \"resourceId\")\n                .contains(tuple(\"form mapping\", \"Step1\"),\n                        tuple(\"form mapping\", FormMappingType.PROCESS_OVERVIEW.name()),\n                        tuple(\"form mapping\", FormMappingType.PROCESS_START.name()));\n    }\n\n    @Test\n    public void checkFormMappingResolutionShouldNotAddProblemsIfThereIsNONE() throws Exception {\n        ArrayList<Problem> problems = new ArrayList<>();\n\n        SFormMapping sFormMappingTask = new SFormMapping(321324, FormMappingType.TASK.getId(), \"Step1\",\n                SFormMapping.TARGET_NONE);\n        SFormMapping sFormMappingProcessOverview = new SFormMapping(321324, FormMappingType.PROCESS_OVERVIEW.getId(),\n                null, SFormMapping.TARGET_NONE);\n        SFormMapping sFormMappingProcessStart = new SFormMapping(321324, FormMappingType.PROCESS_START.getId(), null,\n                SFormMapping.TARGET_NONE);\n\n        formMappingAndPageArtifactManager.checkFormMappingResolution(sFormMappingTask, problems);\n        formMappingAndPageArtifactManager.checkFormMappingResolution(sFormMappingProcessOverview, problems);\n        formMappingAndPageArtifactManager.checkFormMappingResolution(sFormMappingProcessStart, problems);\n        assertThat(problems).as(\"the problem list should not contain any mapping problem\").hasSize(0);\n    }\n\n    @Test\n    public void checkFormMappingResolutionShouldAddProblemsIfThereIsUNDEFINED() throws Exception {\n        ArrayList<Problem> problems = new ArrayList<>();\n\n        SFormMapping sFormMappingTask = new SFormMapping(321324, FormMappingType.TASK.getId(), \"Step1\",\n                SFormMapping.TARGET_UNDEFINED);\n        SFormMapping sFormMappingProcessOverview = new SFormMapping(321324, FormMappingType.PROCESS_OVERVIEW.getId(),\n                null,\n                SFormMapping.TARGET_UNDEFINED);\n        SFormMapping sFormMappingProcessStart = new SFormMapping(321324, FormMappingType.PROCESS_START.getId(), null,\n                SFormMapping.TARGET_UNDEFINED);\n\n        formMappingAndPageArtifactManager.checkFormMappingResolution(sFormMappingTask, problems);\n        formMappingAndPageArtifactManager.checkFormMappingResolution(sFormMappingProcessOverview, problems);\n        formMappingAndPageArtifactManager.checkFormMappingResolution(sFormMappingProcessStart, problems);\n\n        assertThat(problems).as(\"the problem list should contain three mapping problems\").hasSize(3)\n                .extracting(\"resource\", \"resourceId\").contains(\n                        tuple(\"form mapping\", \"Step1\"),\n                        tuple(\"form mapping\", FormMappingType.PROCESS_OVERVIEW.name()),\n                        tuple(\"form mapping\", FormMappingType.PROCESS_START.name()));\n    }\n\n    @Test\n    public void checkFormMappingResolutionShouldNotContainAnyProblem() throws Exception {\n        ArrayList<Problem> problems = new ArrayList<>();\n        SFormMapping sFormMapping = new SFormMapping(321324, 1, \"Step1\", SFormMapping.TARGET_INTERNAL);\n        SPageMapping pageMapping = new SPageMapping();\n        long pageId = 1322L;\n        pageMapping.setPageId(pageId);\n        when(pageService.getPage(pageId)).thenReturn(mock(SPage.class));\n\n        sFormMapping.setPageMapping(pageMapping);\n\n        formMappingAndPageArtifactManager.checkFormMappingResolution(sFormMapping, problems);\n        assertThat(problems).as(\"the problem list should not contain any mapping problems\").hasSize(0);\n    }\n\n    @Test\n    public void should_export_form_mapping_with_existing_page() throws Exception {\n        //given\n        BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive();\n        businessArchiveBuilder.setProcessDefinition(\n                new ProcessDefinitionBuilder().createNewInstance(\"myProcess\", \"1.0\").addActor(\"actor\")\n                        .addUserTask(\"task1\", \"actor\").addUserTask(\"task2\", \"actor\").getProcess());\n        doReturn(Arrays.asList(\n                withFormMapping(\"myPage1\", PROCESS_DEFINITION_ID, FormMappingType.PROCESS_OVERVIEW,\n                        FormMappingTarget.INTERNAL, null),\n                withFormMapping(\"myPage2\", PROCESS_DEFINITION_ID, FormMappingType.PROCESS_START,\n                        FormMappingTarget.INTERNAL, null),\n                withFormMapping(\"myPage3\", PROCESS_DEFINITION_ID, FormMappingType.TASK, FormMappingTarget.INTERNAL,\n                        \"task1\"),\n                withFormMapping(\"myPage4\", PROCESS_DEFINITION_ID, FormMappingType.TASK, FormMappingTarget.INTERNAL,\n                        \"task2\")))\n                .when(formMappingService).list(PROCESS_DEFINITION_ID, 0, Integer.MAX_VALUE);\n        //when\n        formMappingAndPageArtifactManager.exportToBusinessArchive(PROCESS_DEFINITION_ID, businessArchiveBuilder);\n        //then\n        FormMappingModel formMappingModel = businessArchiveBuilder.done().getFormMappingModel();\n        assertThat(formMappingModel.getFormMappings())\n                .containsOnly(\n                        new FormMappingDefinition(\"myPage1\", FormMappingType.PROCESS_OVERVIEW,\n                                FormMappingTarget.INTERNAL),\n                        new FormMappingDefinition(\"myPage2\", FormMappingType.PROCESS_START, FormMappingTarget.INTERNAL),\n                        new FormMappingDefinition(\"myPage3\", FormMappingType.TASK, FormMappingTarget.INTERNAL, \"task1\"),\n                        new FormMappingDefinition(\"myPage4\", FormMappingType.TASK, FormMappingTarget.INTERNAL,\n                                \"task2\"));\n    }\n\n    @Test\n    public void should_export_form_mapping_with_unexisting_page() throws Exception {\n        //given\n        BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive();\n        businessArchiveBuilder.setProcessDefinition(\n                new ProcessDefinitionBuilder().createNewInstance(\"myProcess\", \"1.0\").addActor(\"actor\")\n                        .addUserTask(\"task1\", \"actor\").addUserTask(\"task2\", \"actor\").getProcess());\n        doReturn(Collections.singletonList(withFormMapping(null, PROCESS_DEFINITION_ID, FormMappingType.TASK,\n                FormMappingTarget.INTERNAL, \"task2\")))\n                .when(formMappingService).list(PROCESS_DEFINITION_ID, 0, Integer.MAX_VALUE);\n        //when\n        formMappingAndPageArtifactManager.exportToBusinessArchive(PROCESS_DEFINITION_ID, businessArchiveBuilder);\n        //then\n        FormMappingModel formMappingModel = businessArchiveBuilder.done().getFormMappingModel();\n        assertThat(formMappingModel.getFormMappings())\n                .containsOnly(\n                        new FormMappingDefinition(null, FormMappingType.TASK, FormMappingTarget.INTERNAL, null));\n    }\n\n    private SFormMapping withFormMapping(String pageName, long processDefinitionId, FormMappingType type,\n            FormMappingTarget target, String task)\n            throws SBonitaReadException, SObjectNotFoundException {\n        SFormMapping sFormMapping = new SFormMapping(processDefinitionId, type.getId(), task, target.name());\n        SPageMapping pageMapping = new SPageMapping();\n        if (pageName != null) {\n            long pageId = UUID.randomUUID().getMostSignificantBits();\n            pageMapping.setPageId(pageId);\n            sFormMapping.setPageMapping(pageMapping);\n            doReturn(new SPage(pageName, 0, 0, false, \"page.zip\")).when(pageService).getPage(pageId);\n        }\n        return sFormMapping;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/resolver/ParameterBusinessArchiveArtifactManagerTest.java",
    "content": "/**\n * Copyright (C) 2018 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.resolver;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.core.process.definition.model.SParameterDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.parameter.ParameterService;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class ParameterBusinessArchiveArtifactManagerTest {\n\n    @Mock\n    ParameterService parameterService;\n\n    @InjectMocks\n    ParameterBusinessArchiveArtifactManager parameterArtifactManager;\n\n    @Test\n    public void should_resolve_process_if_all_existing_parameters_have_value() throws Exception {\n        // given:\n        final BusinessArchive businessArchive = mock(BusinessArchive.class);\n        final Map<String, String> providedParametersWithValues = Collections.singletonMap(\"pName\", \"someDefaultValue\");\n        doReturn(providedParametersWithValues).when(businessArchive).getParameters();\n\n        final SProcessDefinition processDefinition = mock(SProcessDefinition.class);\n        final SParameterDefinition sParameterDefinition = mock(SParameterDefinition.class);\n        doReturn(\"pName\").when(sParameterDefinition).getName();\n        doReturn(Collections.singleton(sParameterDefinition)).when(processDefinition).getParameters();\n\n        // when:\n        final boolean deployed = parameterArtifactManager.deploy(businessArchive, processDefinition);\n\n        // then:\n        assertThat(deployed).isTrue();\n    }\n\n    @Test\n    public void resolution_should_ignore_undefined_parameters() throws Exception {\n        // given:\n        final BusinessArchive businessArchive = mock(BusinessArchive.class);\n        final Map<String, String> providedParametersWithValues = new HashMap<>();\n        providedParametersWithValues.put(\"pName\", \"someDefaultValue\");\n        providedParametersWithValues.put(\"ignoredParameter\", null);\n        doReturn(providedParametersWithValues).when(businessArchive).getParameters();\n\n        final SProcessDefinition processDefinition = mock(SProcessDefinition.class);\n        final SParameterDefinition sParameterDefinition = mock(SParameterDefinition.class);\n        doReturn(\"pName\").when(sParameterDefinition).getName();\n        doReturn(Collections.singleton(sParameterDefinition)).when(processDefinition).getParameters();\n\n        // when:\n        final boolean deployed = parameterArtifactManager.deploy(businessArchive, processDefinition);\n\n        // then:\n        assertThat(deployed).isTrue();\n    }\n\n    @Test\n    public void should_resolve_process_if_no_defined_parameters_in_process() throws Exception {\n        // given:\n        final SProcessDefinition processDefinition = mock(SProcessDefinition.class);\n        doReturn(Collections.emptySet()).when(processDefinition).getParameters();\n\n        // when:\n        final boolean deployed = parameterArtifactManager.deploy(mock(BusinessArchive.class), processDefinition);\n\n        // then:\n        assertThat(deployed).isTrue();\n        verifyNoInteractions(parameterService);\n    }\n\n    @Test\n    public void should_not_resolve_a_null_declared_parameter_value() throws Exception {\n        // given:\n        final BusinessArchive businessArchive = mock(BusinessArchive.class);\n        final Map<String, String> providedParametersWithValues = new HashMap<>();\n        providedParametersWithValues.put(\"pName\", null);\n        doReturn(providedParametersWithValues).when(businessArchive).getParameters();\n\n        final SProcessDefinition processDefinition = mock(SProcessDefinition.class);\n        final SParameterDefinition sParameterDefinition = mock(SParameterDefinition.class);\n        doReturn(\"pName\").when(sParameterDefinition).getName();\n        doReturn(Collections.singleton(sParameterDefinition)).when(processDefinition).getParameters();\n\n        // when:\n        final boolean deployed = parameterArtifactManager.deploy(businessArchive, processDefinition);\n\n        // then:\n        assertThat(deployed).isFalse();\n    }\n\n    @Test\n    public void should_not_resolve_a_not_provided_declared_parameter_value() throws Exception {\n        // given:\n        final BusinessArchive businessArchive = mock(BusinessArchive.class);\n        doReturn(Collections.emptyMap()).when(businessArchive).getParameters();\n\n        final SProcessDefinition processDefinition = mock(SProcessDefinition.class);\n        final SParameterDefinition sParameterDefinition = mock(SParameterDefinition.class);\n        doReturn(\"pName\").when(sParameterDefinition).getName();\n        doReturn(Collections.singleton(sParameterDefinition)).when(processDefinition).getParameters();\n\n        // when:\n        final boolean deployed = parameterArtifactManager.deploy(businessArchive, processDefinition);\n\n        // then:\n        assertThat(deployed).isFalse();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/resolver/ProblemAssert.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.resolver;\n\nimport org.assertj.core.api.AbstractAssert;\nimport org.assertj.core.util.Objects;\nimport org.bonitasoft.engine.bpm.process.Problem;\n\n/**\n * {@link Problem} specific assertions - Generated by CustomAssertionGenerator.\n */\npublic class ProblemAssert extends AbstractAssert<ProblemAssert, Problem> {\n\n    /**\n     * Creates a new <code>{@link ProblemAssert}</code> to make assertions on actual Problem.\n     *\n     * @param actual the Problem we want to make assertions on.\n     */\n    public ProblemAssert(Problem actual) {\n        super(actual, ProblemAssert.class);\n    }\n\n    /**\n     * An entry point for ProblemAssert to follow AssertJ standard <code>assertThat()</code> statements.<br>\n     * With a static import, one can write directly: <code>assertThat(myProblem)</code> and get specific assertion with\n     * code completion.\n     *\n     * @param actual the Problem we want to make assertions on.\n     * @return a new <code>{@link ProblemAssert}</code>\n     */\n    public static ProblemAssert assertThat(Problem actual) {\n        return new ProblemAssert(actual);\n    }\n\n    /**\n     * Verifies that the actual Problem's description is equal to the given one.\n     *\n     * @param description the given description to compare the actual Problem's description to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual Problem's description is not equal to the given one.\n     */\n    public ProblemAssert hasDescription(String description) {\n        // check that actual Problem we want to make assertions on is not null.\n        isNotNull();\n\n        // overrides the default error message with a more explicit one\n        String assertjErrorMessage = \"\\nExpected description of:\\n  <%s>\\nto be:\\n  <%s>\\nbut was:\\n  <%s>\";\n\n        // null safe check\n        String actualDescription = actual.getDescription();\n        if (!Objects.areEqual(actualDescription, description)) {\n            failWithMessage(assertjErrorMessage, actual, description, actualDescription);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual Problem's level is equal to the given one.\n     *\n     * @param level the given level to compare the actual Problem's level to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual Problem's level is not equal to the given one.\n     */\n    public ProblemAssert hasLevel(Problem.Level level) {\n        // check that actual Problem we want to make assertions on is not null.\n        isNotNull();\n\n        // overrides the default error message with a more explicit one\n        String assertjErrorMessage = \"\\nExpected level of:\\n  <%s>\\nto be:\\n  <%s>\\nbut was:\\n  <%s>\";\n\n        // null safe check\n        Problem.Level actualLevel = actual.getLevel();\n        if (!Objects.areEqual(actualLevel, level)) {\n            failWithMessage(assertjErrorMessage, actual, level, actualLevel);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual Problem's resource is equal to the given one.\n     *\n     * @param resource the given resource to compare the actual Problem's resource to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual Problem's resource is not equal to the given one.\n     */\n    public ProblemAssert hasResource(String resource) {\n        // check that actual Problem we want to make assertions on is not null.\n        isNotNull();\n\n        // overrides the default error message with a more explicit one\n        String assertjErrorMessage = \"\\nExpected resource of:\\n  <%s>\\nto be:\\n  <%s>\\nbut was:\\n  <%s>\";\n\n        // null safe check\n        String actualResource = actual.getResource();\n        if (!Objects.areEqual(actualResource, resource)) {\n            failWithMessage(assertjErrorMessage, actual, resource, actualResource);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual Problem's resourceId is equal to the given one.\n     *\n     * @param resourceId the given resourceId to compare the actual Problem's resourceId to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual Problem's resourceId is not equal to the given one.\n     */\n    public ProblemAssert hasResourceId(String resourceId) {\n        // check that actual Problem we want to make assertions on is not null.\n        isNotNull();\n\n        // overrides the default error message with a more explicit one\n        String assertjErrorMessage = \"\\nExpected resourceId of:\\n  <%s>\\nto be:\\n  <%s>\\nbut was:\\n  <%s>\";\n\n        // null safe check\n        String actualResourceId = actual.getResourceId();\n        if (!Objects.areEqual(actualResourceId, resourceId)) {\n            failWithMessage(assertjErrorMessage, actual, resourceId, actualResourceId);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/resolver/ProblemCondition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.resolver;\n\nimport org.assertj.core.api.Condition;\nimport org.bonitasoft.engine.bpm.process.Problem;\nimport org.bonitasoft.engine.bpm.process.Problem.Level;\n\npublic class ProblemCondition extends Condition<Problem> {\n\n    @Override\n    public boolean matches(final Problem value) {\n        return Level.ERROR.equals(value.getLevel()) && \"business data\".equals(value.getResource());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/transaction/GetNumberOfActorsTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction;\n\nimport static org.hamcrest.core.Is.is;\nimport static org.junit.Assert.assertThat;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.util.Collections;\n\nimport org.bonitasoft.engine.api.impl.transaction.actor.GetNumberOfActors;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SActorDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\n\npublic class GetNumberOfActorsTest {\n\n    private static ProcessDefinitionService processDefinitionService;\n    private static SProcessDefinition definition;\n    private static GetNumberOfActors getNumberOfActors;\n\n    @BeforeClass\n    public static void setUpClass() throws Exception {\n        processDefinitionService = mock(ProcessDefinitionService.class);\n        definition = mock(SProcessDefinition.class);\n        getNumberOfActors = new GetNumberOfActors(processDefinitionService, 1);\n        when(processDefinitionService.getProcessDefinition(1)).thenReturn(definition);\n    }\n\n    @Test\n    public void initiatorShouldBeCountOnlyOnceAsActor() throws Exception {\n        SActorDefinition initiator = mock(SActorDefinition.class);\n        when(definition.getActorInitiator()).thenReturn(initiator);\n        when(definition.getActors()).thenReturn(Collections.singleton(initiator));\n\n        getNumberOfActors.execute();\n        assertThat(getNumberOfActors.getResult(), is(1));\n    }\n\n    @Test\n    public void resultShouldBeOneWhenOnlyOneActorAndNoInitiator() throws Exception {\n        SActorDefinition initiator = mock(SActorDefinition.class);\n        when(definition.getActorInitiator()).thenReturn(null);\n        when(definition.getActors()).thenReturn(Collections.singleton(initiator));\n\n        getNumberOfActors.execute();\n        assertThat(getNumberOfActors.getResult(), is(1));\n    }\n\n    @Test\n    public void numberOfActorsShouldBeOneMoreIfInitiatorIsNotAnActor() throws Exception {\n        SActorDefinition initiator = mock(SActorDefinition.class);\n        SActorDefinition actor = mock(SActorDefinition.class);\n        when(definition.getActorInitiator()).thenReturn(initiator);\n        when(definition.getActors()).thenReturn(Collections.singleton(actor));\n\n        getNumberOfActors.execute();\n        assertThat(getNumberOfActors.getResult(), is(2));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/transaction/actor/ActorMappingMarshallerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.actor;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.io.InputStream;\n\nimport org.apache.commons.io.IOUtils;\nimport org.bonitasoft.engine.bpm.bar.ActorMappingMarshaller;\nimport org.bonitasoft.engine.bpm.bar.XmlMarshallException;\nimport org.bonitasoft.engine.bpm.bar.actorMapping.Actor;\nimport org.bonitasoft.engine.bpm.bar.actorMapping.ActorMapping;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author mazourd\n */\npublic class ActorMappingMarshallerTest {\n\n    ActorMappingMarshaller marshaller;\n\n    @Before\n    public void initialization() {\n        this.marshaller = new ActorMappingMarshaller();\n    }\n\n    @Test\n    public void should_correctly_read_an_old_actor_mapping_xml() throws Exception {\n        InputStream xmlStream = ActorMappingMarshallerTest.class.getResourceAsStream(\"/complexActorMapping.xml\");\n        ActorMapping actorMappingFromXML = marshaller.deserializeFromXML(IOUtils.toByteArray(xmlStream));\n        Actor actorTest = new Actor(\"Employee actor\");\n        actorTest.addUser(\"john\");\n        actorTest.addRole(\"dev\");\n        actorTest.addGroup(\"/RD\");\n        actorTest.addMembership(\"/RD\", \"dev\");\n        assertThat(actorMappingFromXML.getActors()).contains(actorTest);\n        xmlStream.close();\n    }\n\n    @Test(expected = XmlMarshallException.class)\n    public void should_throw_exception_on_non_valid_xml_reading() throws Exception {\n        InputStream xmlStream = ActorMappingMarshallerTest.class.getResourceAsStream(\"/testThatShouldFail.xml\");\n        String xmlContent = IOUtils.toString(xmlStream);\n        marshaller.deserializeFromXML(xmlContent.getBytes());\n    }\n\n    @Test\n    public void should_correctly_read_an_empty_actor_mapping() throws Exception {\n        ActorMapping employeeActor = new ActorMapping();\n        byte[] bytes = marshaller.serializeToXML(employeeActor);\n        Object actors = marshaller.deserializeFromXML(bytes);\n        assertThat(employeeActor).isEqualTo(actors);\n    }\n\n    @Test\n    public void should_read_correctly_an_actor_mapping_with_an_empty_actor() throws Exception {\n        ActorMapping employeeActor = new ActorMapping();\n        Actor actor = new Actor(\"lulu\");\n        employeeActor.addActor(actor);\n        byte[] result = marshaller.serializeToXML(employeeActor);\n        ActorMapping result2 = marshaller.deserializeFromXML(result);\n        assertThat(employeeActor).isEqualTo(result2);\n    }\n\n    @Test\n    public void should_read_and_write_correctly_a_complete_actor_mapping() throws Exception {\n        ActorMapping employeeActor = new ActorMapping();\n        employeeActor.addActor(new Actor(\"EmployeeMembership1\"));\n        employeeActor.addActor(new Actor(\"EmployeeMembership2\"));\n        final Actor actor = employeeActor.getActors().get(0);\n        actor.addUser(\"william.jobs\");\n        actor.addGroup(\"RD\");\n        actor.addRole(\"dev\");\n        actor.setDescription(\"Just here for the tests\");\n        final Actor actor1 = employeeActor.getActors().get(1);\n        actor1.addUser(\"lala.ru\");\n        actor1.addMembership(\"group1\", \"role1\");\n        byte[] result = marshaller.serializeToXML(employeeActor);\n        Object actors = marshaller.deserializeFromXML(result);\n        assertThat(employeeActor).isEqualToComparingFieldByField((ActorMapping) actors);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/transaction/actor/ImportActorMappingTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.actor;\n\nimport static org.mockito.ArgumentMatchers.anyLong;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.actor.mapping.model.SActor;\nimport org.bonitasoft.engine.bpm.bar.actorMapping.Actor;\nimport org.bonitasoft.engine.bpm.bar.actorMapping.ActorMapping;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.identity.model.SRole;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author mazourd\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class ImportActorMappingTest {\n\n    @Mock\n    private ActorMappingService actorMappingService;\n\n    @Mock\n    private IdentityService identityService;\n\n    @InjectMocks\n    private ImportActorMapping importActorMapping;\n\n    @Test\n    public void execute_method_should_create_SActors_for_all_actors_and_correctly_add_users_roles_and_groups()\n            throws SBonitaException {\n        //given\n        ArrayList<String> mocklist = new ArrayList<>();\n        mocklist.add(\"mock\");\n        Actor actor1 = new Actor(\"Lulu\");\n        Actor actor2 = new Actor(\"Lala\");\n        Actor actor3 = new Actor(\"Sisi\");\n        List<Actor> actors = new ArrayList<>();\n        actors.add(actor1);\n        actors.add(actor2);\n        actors.add(actor3);\n        ActorMapping actorMapping = new ActorMapping();\n        actorMapping.setActors(actors);\n        long ACTOR_ID = 12;\n        SActor sActor = SActor.builder().name(\"Lulu\").scopeId(1458714L).initiator(true).build();\n        sActor.setId(ACTOR_ID);\n\n        SUser sUser = new SUser();\n        final long userId = 111L;\n        sUser.setId(userId);\n\n        SRole sRole = new SRole();\n        final long roleId = 222L;\n        sRole.setId(roleId);\n\n        SGroup sGroup = new SGroup();\n        final long groupId = 333L;\n        sGroup.setId(groupId);\n\n        int cpt = 0;\n        for (final Actor actor : actors) {\n            actor.addUser(\"mockUser\" + cpt++);\n            actor.addRole(\"mockRole\");\n            actor.addGroup(\"mockGroup\");\n            actor.addMembership(\"mockRole\", \"mockGroup\");\n        }\n        when(actorMappingService.getActor(anyString(), anyLong())).thenReturn(sActor);\n        when(identityService.getUserByUserName(anyString())).thenReturn(sUser);\n        when(identityService.getRoleByName(anyString())).thenReturn(sRole);\n        when(identityService.getGroupByPath(anyString())).thenReturn(sGroup);\n\n        //when\n        importActorMapping.execute(actorMapping, ACTOR_ID);\n\n        //then\n        verify(identityService).getUserByUserName(\"mockUser0\");\n        verify(identityService).getUserByUserName(\"mockUser1\");\n        verify(identityService).getUserByUserName(\"mockUser2\");\n        verify(actorMappingService, times(3)).addRoleToActor(ACTOR_ID, roleId);\n        verify(actorMappingService, times(3)).addGroupToActor(ACTOR_ID, groupId);\n        verify(actorMappingService, times(3)).addRoleAndGroupToActor(ACTOR_ID, roleId, groupId);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/transaction/application/SearchApplicationMenusTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.application;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.impl.converter.ApplicationMenuModelConverter;\nimport org.bonitasoft.engine.business.application.ApplicationMenu;\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.model.SApplicationMenu;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class SearchApplicationMenusTest {\n\n    private static final int START_INDEX = 0;\n\n    private static final int MAX_RESULTS = 10;\n\n    @Mock\n    private ApplicationService applicationService;\n\n    @Mock\n    private ApplicationMenuModelConverter convertor;\n\n    @InjectMocks\n    private SearchApplicationMenus search;\n\n    @Test\n    public void executeCount_should_return_result_of_applicationService_getNumberOfApplicationMenus() throws Exception {\n        //given\n        final QueryOptions options = new QueryOptions(START_INDEX, MAX_RESULTS);\n        given(applicationService.getNumberOfApplicationMenus(options)).willReturn(5L);\n\n        //when\n        final long count = search.executeCount(options);\n\n        //then\n        assertThat(count).isEqualTo(5);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void executeCount_should_throw_SearchException_when_applicationService_throws_SBonitaReadException()\n            throws Exception {\n        //given\n        final QueryOptions options = new QueryOptions(START_INDEX, MAX_RESULTS);\n        given(applicationService.getNumberOfApplicationMenus(options)).willThrow(new SBonitaReadException(\"\"));\n\n        //when\n        search.executeCount(options);\n\n        //then exception\n    }\n\n    @Test\n    public void executeSearch_should_return_result_of_applicationService_searchApplicationMenus() throws Exception {\n        //given\n        final QueryOptions options = new QueryOptions(START_INDEX, MAX_RESULTS);\n        final List<SApplicationMenu> menus = new ArrayList<SApplicationMenu>(1);\n        menus.add(mock(SApplicationMenu.class));\n        given(applicationService.searchApplicationMenus(options)).willReturn(menus);\n\n        //when\n        final List<SApplicationMenu> retrievedMenus = search.executeSearch(options);\n\n        //then\n        assertThat(retrievedMenus).isEqualTo(menus);\n    }\n\n    @Test\n    public void convertToClientObjects_should_return_result_of_convertor() throws Exception {\n        //given\n        final List<SApplicationMenu> sMenus = Arrays.asList(mock(SApplicationMenu.class));\n        final List<ApplicationMenu> menus = Arrays.asList(mock(ApplicationMenu.class));\n        given(convertor.toApplicationMenu(sMenus)).willReturn(menus);\n\n        //when\n        final List<ApplicationMenu> clientObjects = search.convertToClientObjects(sMenus);\n\n        //then\n        assertThat(clientObjects).isNotNull();\n        assertThat(clientObjects).isEqualTo(menus);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/transaction/application/SearchApplicationPagesTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.application;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.impl.converter.ApplicationPageModelConverter;\nimport org.bonitasoft.engine.business.application.ApplicationPage;\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.model.SApplicationPage;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class SearchApplicationPagesTest {\n\n    private static final int START_INDEX = 0;\n\n    private static final int MAX_RESULTS = 10;\n\n    @Mock\n    private ApplicationService applicationService;\n\n    @Mock\n    private ApplicationPageModelConverter convertor;\n\n    @InjectMocks\n    private SearchApplicationPages search;\n\n    @Test\n    public void executeCount_should_return_the_result_of_applicationService_getNumberOfApplicationPages()\n            throws Exception {\n        //given\n        final QueryOptions options = new QueryOptions(START_INDEX, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS);\n        given(applicationService.getNumberOfApplicationPages(options)).willReturn(8L);\n\n        //when\n        final long count = search.executeCount(options);\n\n        //then\n        assertThat(count).isEqualTo(8L);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void executeCount_should_throw_SBonitaSeachException_when_applicationService_throws_SBonitaReadException()\n            throws Exception {\n        //given\n        final QueryOptions options = new QueryOptions(START_INDEX, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS);\n        given(applicationService.getNumberOfApplicationPages(options)).willThrow(new SBonitaReadException(\"\"));\n\n        //when\n        search.executeCount(options);\n\n        //then exception\n    }\n\n    @Test\n    public void executeSearch_should_return_the_result_of_applicationService_searchApplicationPages() throws Exception {\n        //given\n        final QueryOptions queryOptions = new QueryOptions(START_INDEX, MAX_RESULTS);\n        final List<SApplicationPage> appPages = new ArrayList<SApplicationPage>(1);\n        appPages.add(mock(SApplicationPage.class));\n        given(applicationService.searchApplicationPages(queryOptions)).willReturn(appPages);\n\n        //when\n        final List<SApplicationPage> retrievedAppPages = search.executeSearch(queryOptions);\n\n        //then\n        assertThat(retrievedAppPages).isEqualTo(appPages);\n    }\n\n    @Test\n    public void convetToClientObjects_should_return_result_of_convertor_toApplicationPage() throws Exception {\n        //given\n        final SApplicationPage sAppPage = mock(SApplicationPage.class);\n        final ApplicationPage appPage = mock(ApplicationPage.class);\n        final List<SApplicationPage> sApplicationPages = Arrays.asList(sAppPage);\n        given(convertor.toApplicationPage(sApplicationPages)).willReturn(Arrays.asList(appPage));\n\n        //when\n        final List<ApplicationPage> applicationPages = search.convertToClientObjects(sApplicationPages);\n\n        //then\n        assertThat(applicationPages).containsExactly(appPage);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/transaction/application/SearchApplicationsOfUsersTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.application;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.impl.converter.ApplicationModelConverter;\nimport org.bonitasoft.engine.business.application.Application;\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.IApplication;\nimport org.bonitasoft.engine.business.application.model.SApplication;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class SearchApplicationsOfUsersTest {\n\n    private static final int START_INDEX = 0;\n\n    private static final int MAX_RESULTS = 10;\n\n    private static final long userId = 12;\n\n    @Mock\n    private ApplicationService applicationService;\n\n    @Mock\n    private SearchEntityDescriptor descriptor;\n\n    @Mock\n    private SearchOptions options;\n\n    @Mock\n    private ApplicationModelConverter convertor;\n\n    private SearchApplicationsOfUser<IApplication> searchApplicationsOfUser;\n\n    @Before\n    public void before() {\n        searchApplicationsOfUser = SearchApplicationsOfUser.defaultSearchApplicationsOfUser(userId, applicationService,\n                descriptor, options, convertor);\n    }\n\n    @Test\n    public void executeCount_should_return_result_of_applicationService_getNumberOfApplications() throws Exception {\n        //given\n        final QueryOptions options = new QueryOptions(START_INDEX, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS);\n        given(applicationService.getNumberOfApplicationsOfUser(userId, options)).willReturn(10L);\n\n        //when\n        final long count = searchApplicationsOfUser.executeCount(options);\n\n        //then\n        assertThat(count).isEqualTo(10L);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void executeCount_should_throw_SBonitaSeachException_when_applicationService_throws_SBonitaReadException()\n            throws Exception {\n        //given\n        final QueryOptions options = new QueryOptions(START_INDEX, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS);\n        given(applicationService.getNumberOfApplicationsOfUser(userId, options))\n                .willThrow(new SBonitaReadException(\"\"));\n\n        //when\n        searchApplicationsOfUser.executeCount(options);\n\n        //then exception\n    }\n\n    @Test\n    public void executeSearch_should_return_the_result_of_applicationService_searchApplications() throws Exception {\n        //given\n        final QueryOptions queryOptions = new QueryOptions(START_INDEX, MAX_RESULTS);\n        final List<SApplication> applications = new ArrayList<SApplication>(1);\n        applications.add(mock(SApplication.class));\n        given(applicationService.searchApplicationsOfUser(userId, queryOptions)).willReturn(applications);\n\n        //when\n        final List<SApplication> retrievedApplications = searchApplicationsOfUser.executeSearch(queryOptions);\n\n        //then\n        assertThat(retrievedApplications).isEqualTo(applications);\n    }\n\n    @Test\n    public void convetToClientObjects_should_return_result_of_convertor_toApplication() throws Exception {\n        //given\n        final SApplication sApp = mock(SApplication.class);\n        final Application app = mock(Application.class);\n        final List<SApplication> sApplications = Arrays.asList(sApp);\n        given(convertor.toApplication(sApplications)).willReturn(Arrays.asList(app));\n\n        //when\n        final List<IApplication> applications = searchApplicationsOfUser.convertToClientObjects(sApplications);\n\n        //then\n        assertThat(applications).containsExactly(app);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/transaction/application/SearchApplicationsTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.application;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.impl.converter.ApplicationModelConverter;\nimport org.bonitasoft.engine.business.application.Application;\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.IApplication;\nimport org.bonitasoft.engine.business.application.model.SApplication;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class SearchApplicationsTest {\n\n    private static final int START_INDEX = 0;\n\n    private static final int MAX_RESULTS = 10;\n\n    @Mock\n    private ApplicationService applicationService;\n\n    @Mock\n    private SearchEntityDescriptor descriptor;\n\n    @Mock\n    private SearchOptions options;\n\n    @Mock\n    private ApplicationModelConverter convertor;\n\n    private SearchApplications<IApplication> searchApplications;\n\n    @Before\n    public void initSearchApplications() {\n        searchApplications = SearchApplications.defaultSearchApplications(applicationService, descriptor, options,\n                convertor);\n    }\n\n    @Test\n    public void executeCount_should_return_result_of_applicationService_getNumberOfApplications() throws Exception {\n        //given\n        final QueryOptions options = new QueryOptions(START_INDEX, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS);\n        given(applicationService.getNumberOfApplications(options)).willReturn(10L);\n\n        //when\n        final long count = searchApplications.executeCount(options);\n\n        //then\n        assertThat(count).isEqualTo(10L);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void executeCount_should_throw_SBonitaSeachException_when_applicationService_throws_SBonitaReadException()\n            throws Exception {\n        //given\n        final QueryOptions options = new QueryOptions(START_INDEX, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS);\n        given(applicationService.getNumberOfApplications(options)).willThrow(new SBonitaReadException(\"\"));\n\n        //when\n        searchApplications.executeCount(options);\n\n        //then exception\n    }\n\n    @Test\n    public void executeSearch_should_return_the_result_of_applicationService_searchApplications() throws Exception {\n        //given\n        final QueryOptions queryOptions = new QueryOptions(START_INDEX, MAX_RESULTS);\n        final List<SApplication> applications = new ArrayList<SApplication>(1);\n        applications.add(mock(SApplication.class));\n        given(applicationService.searchApplications(queryOptions)).willReturn(applications);\n\n        //when\n        final List<SApplication> retrievedApplications = searchApplications.executeSearch(queryOptions);\n\n        //then\n        assertThat(retrievedApplications).isEqualTo(applications);\n    }\n\n    @Test\n    public void convetToClientObjects_should_return_result_of_convertor_toApplication() throws Exception {\n        //given\n        final SApplication sApp = mock(SApplication.class);\n        final Application app = mock(Application.class);\n        final List<SApplication> sApplications = Arrays.asList(sApp);\n        given(convertor.toApplication(sApplications)).willReturn(Arrays.asList(app));\n\n        //when\n        final List<IApplication> applications = searchApplications.convertToClientObjects(sApplications);\n\n        //then\n        assertThat(applications).containsExactly(app);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/transaction/expression/EvaluateExpressionsDefinitionLevelTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.expression;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.fail;\nimport static org.mockito.ArgumentMatchers.nullable;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport org.bonitasoft.engine.commons.exceptions.SExceptionContext;\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class EvaluateExpressionsDefinitionLevelTest {\n\n    @Mock\n    private ExpressionResolverService expressionResolverService;\n\n    @Mock\n    private ProcessDefinitionService processDefinitionService;\n\n    @Test\n    public void executeShouldEnrichExceptionContextWithProcessDefinitionId() throws Exception {\n        // given:\n        SProcessDefinition processDef = mock(SProcessDefinition.class);\n        Long processDefId = 15935777L;\n        when(processDef.getId()).thenReturn(processDefId);\n        when(expressionResolverService.evaluate(nullable(SExpression.class), nullable(SExpressionContext.class)))\n                .thenThrow(\n                        new SExpressionEvaluationException(\"some msg\", \"some expressionName\"));\n\n        try {\n            // when:\n            new EvaluateExpressionsDefinitionLevel(null, processDefId, expressionResolverService,\n                    processDefinitionService, null).evaluateExpression(null, null,\n                            processDef);\n            fail(\"There should be a exception raised\");\n        } catch (SExpressionEvaluationException e) {\n            // then:\n            assertThat(e.getContext().get(SExceptionContext.PROCESS_DEFINITION_ID)).isEqualTo(processDefId);\n        }\n    }\n\n    @Test\n    public void executeShouldEnrichExceptionContextWithProcessDefinitionName() throws Exception {\n        // given:\n        SProcessDefinition processDef = mock(SProcessDefinition.class);\n        String processName = \"my_process\";\n        when(processDef.getName()).thenReturn(processName);\n        when(expressionResolverService.evaluate(nullable(SExpression.class), nullable(SExpressionContext.class)))\n                .thenThrow(\n                        new SExpressionEvaluationException(\"some msg\", \"some expressionName\"));\n\n        try {\n            // when:\n            new EvaluateExpressionsDefinitionLevel(null, 196584L, expressionResolverService, processDefinitionService,\n                    null).evaluateExpression(null, null,\n                            processDef);\n            fail(\"There should be a exception raised\");\n        } catch (SExpressionEvaluationException e) {\n            // then:\n            assertThat(e.getContext().get(SExceptionContext.PROCESS_NAME)).isEqualTo(processName);\n        }\n    }\n\n    @Test\n    public void executeShouldEnrichExceptionContextWithProcessDefinitionVersion() throws Exception {\n        // given:\n        SProcessDefinition processDef = mock(SProcessDefinition.class);\n        String processVersion = \"7.3.1\";\n        when(processDef.getVersion()).thenReturn(processVersion);\n        when(expressionResolverService.evaluate(nullable(SExpression.class), nullable(SExpressionContext.class)))\n                .thenThrow(\n                        new SExpressionEvaluationException(\"some msg\", \"some expressionName\"));\n\n        try {\n            // when:\n            new EvaluateExpressionsDefinitionLevel(null, 6453241L, expressionResolverService, processDefinitionService,\n                    null).evaluateExpression(null, null,\n                            processDef);\n            fail(\"There should be a exception raised\");\n        } catch (SExpressionEvaluationException e) {\n            // then:\n            assertThat(e.getContext().get(SExceptionContext.PROCESS_VERSION)).isEqualTo(processVersion);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/transaction/expression/bdm/internal/EntityMergerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.expression.bdm.internal;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.*;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.impl.transaction.expression.EntityMerger;\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.bonitasoft.engine.business.data.BusinessDataRepository;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Romain Bioteau\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class EntityMergerTest {\n\n    @Mock\n    private BusinessDataRepository bdrService;\n\n    @Mock\n    private Entity testEntity;\n\n    private EntityMerger entityMerger;\n\n    @Before\n    public void setUp() {\n        entityMerger = new EntityMerger(bdrService);\n    }\n\n    @Test\n    public void merge_an_single_entity_should_not_call_merge_on_bdrService() {\n        entityMerger.merge(testEntity);\n        verify(bdrService, never()).merge(testEntity);\n    }\n\n    @Test\n    public void merge_a_collection_of_entity_should_not_call_merge_on_bdrService_for_each_entity() {\n        final List<Entity> listOfEntities = new ArrayList<Entity>();\n        listOfEntities.add(testEntity);\n        listOfEntities.add(testEntity);\n        listOfEntities.add(testEntity);\n        entityMerger.merge((Serializable) listOfEntities);\n        verify(bdrService, never()).merge(testEntity);\n    }\n\n    @Test\n    public void merge_a_simple_serializable_should_not_call_merge_on_bdrService() {\n        final String hello = \"Hello\";\n        assertThat(entityMerger.merge(\"Hello\")).isEqualTo(hello);\n        verifyNoInteractions(bdrService);\n    }\n\n    @Test\n    public void merge_a_collection_of_simple_serializable_should_not_call_merge_on_bdrService() {\n        final List<String> listOfEntities = new ArrayList<String>();\n        listOfEntities.add(\"Hello\");\n        listOfEntities.add(\"Goodbye\");\n        listOfEntities.add(\"Have a nice day\");\n        entityMerger.merge((Serializable) listOfEntities);\n        verifyNoInteractions(bdrService);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/transaction/flownode/SetExpectedEndDateTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.flownode;\n\nimport static org.mockito.Mockito.doReturn;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.Mockito;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Laurent Leseigneur\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class SetExpectedEndDateTest {\n\n    @Mock\n    private ActivityInstanceService activityInstanceService;\n\n    @Mock\n    private SFlowNodeInstance flowNodeInstance;\n\n    @Test\n    public void should_set_valid_due_date() throws Exception {\n        //given\n        final long dateAsLong = 45L;\n        Date date = new Date(dateAsLong);\n        final long instanceId = 1L;\n        SetExpectedEndDate setExpectedEndDate = new SetExpectedEndDate(activityInstanceService, instanceId, date);\n        doReturn(flowNodeInstance).when(activityInstanceService).getFlowNodeInstance(instanceId);\n        //when\n        setExpectedEndDate.execute();\n\n        //then\n        Mockito.verify(activityInstanceService).setExpectedEndDate(flowNodeInstance, dateAsLong);\n\n    }\n\n    @Test\n    public void should_set_null_due_date() throws Exception {\n        //given\n        final long dateAsLong = 45L;\n        Date date = new Date(dateAsLong);\n        final long instanceId = 1L;\n        SetExpectedEndDate setExpectedEndDate = new SetExpectedEndDate(activityInstanceService, instanceId, null);\n        doReturn(flowNodeInstance).when(activityInstanceService).getFlowNodeInstance(instanceId);\n        //when\n        setExpectedEndDate.execute();\n\n        //then\n        Mockito.verify(activityInstanceService).setExpectedEndDate(flowNodeInstance, null);\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/transaction/process/DisableProcessTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.process;\n\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.mockito.Mockito.when;\n\nimport java.util.ArrayList;\n\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.scheduler.SchedulerService;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class DisableProcessTest {\n\n    @Mock\n    private ClassLoaderService classLoaderService;\n    @Mock\n    private ProcessDefinitionService processDefinitionService;\n    @Mock\n    private EventInstanceService eventInstanceService;\n    @Mock\n    private SchedulerService scheduler;\n    @Mock\n    private SProcessDefinition processDefinition;\n    @Mock\n    private SFlowElementContainerDefinition flowElementCOntainerDefintion;\n\n    @Test\n    public void execute_should_not_clean_the_classLoader_due_to_its_use_for_running_instances()\n            throws SBonitaException {\n        final long processDefinitionId = 1;\n        when(processDefinitionService.getProcessDefinition(processDefinitionId)).thenReturn(processDefinition);\n        when(processDefinition.getProcessContainer()).thenReturn(flowElementCOntainerDefintion);\n        when(flowElementCOntainerDefintion.getStartEvents()).thenReturn(new ArrayList<>());\n        final DisableProcess disableProcess = new DisableProcess(processDefinitionService, processDefinitionId,\n                eventInstanceService,\n                scheduler, \"matti\");\n\n        disableProcess.execute();\n\n        verifyNoInteractions(classLoaderService);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/transaction/profile/CreateProfileMemberTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.api.impl.transaction.profile;\n\nimport static org.junit.Assert.assertNull;\nimport static org.mockito.ArgumentMatchers.anyLong;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.ArgumentMatchers.nullable;\nimport static org.mockito.Mockito.*;\n\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.MemberType;\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.identity.model.SRole;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.profile.ProfileService;\nimport org.bonitasoft.engine.profile.builder.SProfileUpdateBuilder;\nimport org.bonitasoft.engine.profile.model.SProfileMember;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class CreateProfileMemberTest {\n\n    private static final long USER_ID = 1L;\n\n    private static final long ROLE_ID = 2L;\n\n    private static final long GROUP_ID = 3L;\n\n    private static final long PROFILE_ID = 1l;\n\n    @Mock\n    private ProfileService profileService;\n\n    @Mock\n    private IdentityService identityService;\n\n    @Mock\n    private SProfileMember addGroupToProfile;\n\n    @Mock\n    SProfileUpdateBuilder sProfileUpdateBuilder;\n\n    @Mock\n    private SGroup sGroup;\n\n    @Mock\n    private SUser sUser;\n\n    @Mock\n    private SRole sRole;\n\n    @Before\n    public void before() throws Exception {\n        doReturn(sGroup).when(identityService).getGroup(anyLong());\n        doReturn(sUser).when(identityService).getUser(anyLong());\n        doReturn(sRole).when(identityService).getRole(anyLong());\n\n        doReturn(null).when(profileService).addGroupToProfile(anyLong(), anyLong(), anyString(), anyString());\n        doReturn(null).when(profileService).addRoleToProfile(anyLong(), anyLong(), anyString());\n        doReturn(null).when(profileService).addRoleAndGroupToProfile(anyLong(), anyLong(), anyLong(), anyString(),\n                anyString(), anyString());\n\n        doReturn(\"group\").when(sGroup).getName();\n        doReturn(\"/parent\").when(sGroup).getParentPath();\n\n        doReturn(\"role\").when(sRole).getName();\n    }\n\n    @Test\n    public void should_updateProfileMetaData_and_addUserToProfile_when_userId_is_not_empty_and_member_is_user()\n            throws Exception {\n        // Given\n        final CreateProfileMember createProfileMember = spy(\n                new CreateProfileMember(profileService, identityService, PROFILE_ID, USER_ID, null, null,\n                        MemberType.USER));\n\n        // When\n        createProfileMember.execute();\n\n        // Then\n        verify(profileService).addUserToProfile(eq(PROFILE_ID), eq(USER_ID), nullable(String.class),\n                nullable(String.class), nullable(String.class));\n        verify(profileService, times(1)).updateProfileMetaData(anyLong());\n    }\n\n    @Test\n    public void should_just_updateProfileMetaData_when_no_user_and_member_is_user() throws Exception {\n        // Given\n        final CreateProfileMember createProfileMember = spy(\n                new CreateProfileMember(profileService, identityService, PROFILE_ID, USER_ID, null, null,\n                        MemberType.USER));\n        doReturn(null).when(identityService).getUser(USER_ID);\n\n        // When\n        createProfileMember.execute();\n\n        // Then\n        verify(profileService, never()).addUserToProfile(eq(PROFILE_ID), eq(USER_ID), anyString(), anyString(),\n                anyString());\n        verify(profileService, times(1)).updateProfileMetaData(anyLong());\n    }\n\n    @Test\n    public void should_just_updateProfileMetaData_when_userId_is_null_and_member_is_user() throws Exception {\n        // Given\n        final CreateProfileMember createProfileMember = spy(\n                new CreateProfileMember(profileService, identityService, PROFILE_ID, null, null, null,\n                        MemberType.USER));\n\n        // When\n        createProfileMember.execute();\n\n        // Then\n        verify(profileService, never()).addUserToProfile(eq(PROFILE_ID), eq(USER_ID), anyString(), anyString(),\n                anyString());\n        verify(profileService, times(1)).updateProfileMetaData(anyLong());\n    }\n\n    @Test\n    public void should_just_updateProfileMetaData_when_userId_equals_0_and_member_is_user() throws Exception {\n        // Given\n        final CreateProfileMember createProfileMember = spy(\n                new CreateProfileMember(profileService, identityService, PROFILE_ID, 0L, null, null,\n                        MemberType.USER));\n\n        // When\n        createProfileMember.execute();\n\n        // Then\n        verify(profileService, never()).addUserToProfile(eq(PROFILE_ID), eq(USER_ID), anyString(), anyString(),\n                anyString());\n        verify(profileService, times(1)).updateProfileMetaData(anyLong());\n    }\n\n    @Test\n    public void should_addGroupToProfile_when_member_is_group() throws Exception {\n        // Given\n        final CreateProfileMember createProfileMember = spy(\n                new CreateProfileMember(profileService, identityService, PROFILE_ID, null, GROUP_ID, null,\n                        MemberType.GROUP));\n\n        // When\n        createProfileMember.execute();\n\n        // Then\n        verify(profileService, times(1)).addGroupToProfile(eq(PROFILE_ID), eq(GROUP_ID), anyString(), anyString());\n        verify(profileService, times(1)).updateProfileMetaData(anyLong());\n    }\n\n    @Test\n    public void should_just_updateProfileMetaData_when_no_group_and_member_is_group() throws Exception {\n        // Given\n        final CreateProfileMember createProfileMember = spy(\n                new CreateProfileMember(profileService, identityService, PROFILE_ID, null, GROUP_ID, null,\n                        MemberType.GROUP));\n        doReturn(null).when(identityService).getGroup(GROUP_ID);\n\n        // When\n        createProfileMember.execute();\n\n        // Then\n        verify(profileService, never()).addGroupToProfile(eq(PROFILE_ID), eq(GROUP_ID), anyString(), anyString());\n        verify(profileService, times(1)).updateProfileMetaData(anyLong());\n    }\n\n    @Test\n    public void should_just_updateProfileMetaData_when_groupId_is_null_and_member_is_group() throws Exception {\n        // Given\n        final CreateProfileMember createProfileMember = spy(\n                new CreateProfileMember(profileService, identityService, PROFILE_ID, null, null, null,\n                        MemberType.GROUP));\n\n        // When\n        createProfileMember.execute();\n\n        // Then\n        verify(profileService, never()).addGroupToProfile(eq(PROFILE_ID), eq(GROUP_ID), anyString(), anyString());\n        verify(profileService, times(1)).updateProfileMetaData(anyLong());\n    }\n\n    @Test\n    public void should_just_updateProfileMetaData_when_groupId_equals_0_and_member_is_group() throws Exception {\n        // Given\n        final CreateProfileMember createProfileMember = spy(\n                new CreateProfileMember(profileService, identityService, PROFILE_ID, null, 0L, null,\n                        MemberType.GROUP));\n\n        // When\n        createProfileMember.execute();\n\n        // Then\n        verify(profileService, never()).addGroupToProfile(eq(PROFILE_ID), eq(GROUP_ID), anyString(), anyString());\n        verify(profileService, times(1)).updateProfileMetaData(anyLong());\n    }\n\n    @Test\n    public void should_addRoleAndGroupToProfile_when_member_is_membership() throws Exception {\n        // Given\n        final CreateProfileMember createProfileMember = spy(\n                new CreateProfileMember(profileService, identityService, PROFILE_ID, null, GROUP_ID, ROLE_ID,\n                        MemberType.MEMBERSHIP));\n\n        // When\n        createProfileMember.execute();\n\n        // Then\n        verify(profileService, times(1)).addRoleAndGroupToProfile(eq(PROFILE_ID), eq(ROLE_ID), eq(GROUP_ID),\n                anyString(), anyString(), anyString());\n    }\n\n    @Test\n    public void should_just_updateProfileMetaData_when_no_group_and_member_is_membership() throws Exception {\n        // Given\n        final CreateProfileMember createProfileMember = spy(\n                new CreateProfileMember(profileService, identityService, PROFILE_ID, null, GROUP_ID, ROLE_ID,\n                        MemberType.MEMBERSHIP));\n        doReturn(null).when(identityService).getGroup(GROUP_ID);\n\n        // When\n        createProfileMember.execute();\n\n        // Then\n        verify(profileService, never()).addRoleAndGroupToProfile(eq(PROFILE_ID), eq(ROLE_ID), eq(GROUP_ID), anyString(),\n                anyString(), anyString());\n        verify(profileService, times(1)).updateProfileMetaData(anyLong());\n    }\n\n    @Test\n    public void should_just_updateProfileMetaData_when_no_role_and_member_is_membership() throws Exception {\n        // Given\n        final CreateProfileMember createProfileMember = spy(\n                new CreateProfileMember(profileService, identityService, PROFILE_ID, null, GROUP_ID, ROLE_ID,\n                        MemberType.MEMBERSHIP));\n        doReturn(null).when(identityService).getRole(ROLE_ID);\n\n        // When\n        createProfileMember.execute();\n\n        // Then\n        verify(profileService, never()).addRoleAndGroupToProfile(eq(PROFILE_ID), eq(ROLE_ID), eq(GROUP_ID), anyString(),\n                anyString(), anyString());\n        verify(profileService, times(1)).updateProfileMetaData(anyLong());\n    }\n\n    @Test\n    public void should_just_updateProfileMetaData_when_groupId_is_null_and_member_is_membership() throws Exception {\n        // Given\n        final CreateProfileMember createProfileMember = spy(\n                new CreateProfileMember(profileService, identityService, PROFILE_ID, null, null, ROLE_ID,\n                        MemberType.MEMBERSHIP));\n\n        // When\n        createProfileMember.execute();\n\n        // Then\n        verify(profileService, never()).addRoleAndGroupToProfile(eq(PROFILE_ID), eq(ROLE_ID), eq(GROUP_ID), anyString(),\n                anyString(), anyString());\n        verify(profileService, times(1)).updateProfileMetaData(anyLong());\n    }\n\n    @Test\n    public void should_just_updateProfileMetaData_when_groupId_equals_0_and_member_is_membership() throws Exception {\n        // Given\n        final CreateProfileMember createProfileMember = spy(\n                new CreateProfileMember(profileService, identityService, PROFILE_ID, null, 0L, ROLE_ID,\n                        MemberType.MEMBERSHIP));\n\n        // When\n        createProfileMember.execute();\n\n        // Then\n        verify(profileService, never()).addRoleAndGroupToProfile(eq(PROFILE_ID), eq(ROLE_ID), eq(GROUP_ID), anyString(),\n                anyString(), anyString());\n        verify(profileService, times(1)).updateProfileMetaData(anyLong());\n    }\n\n    @Test\n    public void should_just_updateProfileMetaData_when_roleId_is_null_and_member_is_membership() throws Exception {\n        // Given\n        final CreateProfileMember createProfileMember = spy(\n                new CreateProfileMember(profileService, identityService, PROFILE_ID, null, GROUP_ID, null,\n                        MemberType.MEMBERSHIP));\n\n        // When\n        createProfileMember.execute();\n\n        // Then\n        verify(profileService, never()).addRoleAndGroupToProfile(eq(PROFILE_ID), eq(ROLE_ID), eq(GROUP_ID), anyString(),\n                anyString(), anyString());\n        verify(profileService, times(1)).updateProfileMetaData(anyLong());\n    }\n\n    @Test\n    public void should_just_updateProfileMetaData_when_roleId_equals_0_and_member_is_membership() throws Exception {\n        // Given\n        final CreateProfileMember createProfileMember = spy(\n                new CreateProfileMember(profileService, identityService, PROFILE_ID, null, GROUP_ID, 0L,\n                        MemberType.MEMBERSHIP));\n\n        // When\n        createProfileMember.execute();\n\n        // Then\n        verify(profileService, never()).addRoleAndGroupToProfile(eq(PROFILE_ID), eq(ROLE_ID), eq(GROUP_ID), anyString(),\n                anyString(), anyString());\n        verify(profileService, times(1)).updateProfileMetaData(anyLong());\n    }\n\n    @Test\n    public void should_addRoleToProfile_when_member_is_role() throws Exception {\n        // Given\n        final CreateProfileMember createProfileMember = spy(\n                new CreateProfileMember(profileService, identityService, PROFILE_ID, null, null, ROLE_ID,\n                        MemberType.ROLE));\n\n        // When\n        createProfileMember.execute();\n\n        // Then\n        verify(profileService, times(1)).addRoleToProfile(eq(PROFILE_ID), eq(ROLE_ID), anyString());\n    }\n\n    @Test\n    public void should_just_updateProfileMetaData_when_no_role_and_member_is_role() throws Exception {\n        // Given\n        final CreateProfileMember createProfileMember = spy(\n                new CreateProfileMember(profileService, identityService, PROFILE_ID, null, null, ROLE_ID,\n                        MemberType.ROLE));\n        doReturn(null).when(identityService).getRole(ROLE_ID);\n\n        // When\n        createProfileMember.execute();\n\n        // Then\n        verify(profileService, never()).addRoleToProfile(eq(PROFILE_ID), eq(ROLE_ID), anyString());\n        verify(profileService, times(1)).updateProfileMetaData(anyLong());\n    }\n\n    @Test\n    public void should_just_updateProfileMetaData_when_roleId_is_null_and_member_is_role() throws Exception {\n        // Given\n        final CreateProfileMember createProfileMember = spy(\n                new CreateProfileMember(profileService, identityService, PROFILE_ID, null, null, null,\n                        MemberType.ROLE));\n\n        // When\n        createProfileMember.execute();\n\n        // Then\n        verify(profileService, never()).addRoleToProfile(eq(PROFILE_ID), eq(ROLE_ID), anyString());\n        verify(profileService, times(1)).updateProfileMetaData(anyLong());\n    }\n\n    @Test\n    public void should_just_updateProfileMetaData_when_roleId_equals_0_and_member_is_role() throws Exception {\n        // Given\n        final CreateProfileMember createProfileMember = spy(\n                new CreateProfileMember(profileService, identityService, PROFILE_ID, null, null, 0L,\n                        MemberType.ROLE));\n\n        // When\n        createProfileMember.execute();\n\n        // Then\n        verify(profileService, never()).addRoleToProfile(eq(PROFILE_ID), eq(ROLE_ID), anyString());\n        verify(profileService, times(1)).updateProfileMetaData(anyLong());\n    }\n\n    @Test\n    public void getResult() {\n        // Given\n        final CreateProfileMember createProfileMember = new CreateProfileMember(profileService, identityService,\n                PROFILE_ID, null, null, null, MemberType.ROLE);\n\n        // When\n        final SProfileMember result = createProfileMember.getResult();\n\n        // Then\n        assertNull(result);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/authorization/PermissionServiceConfigurationTest.java",
    "content": "/**\n * Copyright (C) 2021 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.authorization;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\n\nimport org.bonitasoft.engine.authorization.properties.CompoundPermissionsMapping;\nimport org.bonitasoft.engine.authorization.properties.CustomPermissionsMapping;\nimport org.bonitasoft.engine.authorization.properties.DynamicPermissionsChecks;\nimport org.bonitasoft.engine.authorization.properties.ResourcesPermissionsMapping;\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.TestPropertySource;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@ContextConfiguration(classes = { PermissionServiceConfigurationTest.TestConfiguration.class })\n@TestPropertySource(properties = { \"bonita.runtime.authorization.dynamic-check.enabled=false\" })\npublic class PermissionServiceConfigurationTest {\n\n    @Autowired\n    protected PermissionServiceImpl permissionService;\n\n    @Test\n    public void should_dynamic_check_property_use_specific_property_value_if_overwritten() {\n        assertThat(permissionService.dynamicPermissionCheck.isEnabled()).isFalse();\n    }\n\n    @ComponentScan({\n            \"org.bonitasoft.engine.authorization\" // for Spring to find PermissionService\n    })\n    @Configuration\n    public static class TestConfiguration {\n\n        @Bean\n        ClassLoaderService classLoaderService() {\n            return mock(ClassLoaderService.class);\n        }\n\n        @Bean\n        SessionAccessor sessionAccessor() {\n            return mock(SessionAccessor.class);\n        }\n\n        @Bean\n        SessionService sessionService() {\n            return mock(SessionService.class);\n        }\n\n        @Bean\n        PermissionsBuilder permissionsBuilder() {\n            return mock(PermissionsBuilder.class);\n        }\n\n        @Bean\n        CompoundPermissionsMapping compoundPermissionsMapping() {\n            return mock(CompoundPermissionsMapping.class);\n        }\n\n        @Bean\n        ResourcesPermissionsMapping resourcesPermissionsMapping() {\n            return mock(ResourcesPermissionsMapping.class);\n        }\n\n        @Bean\n        CustomPermissionsMapping customPermissionsMapping() {\n            return mock(CustomPermissionsMapping.class);\n        }\n\n        @Bean\n        DynamicPermissionsChecks dynamicPermissionsChecks() {\n            return mock(DynamicPermissionsChecks.class);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/authorization/PermissionServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.authorization;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.assertThrows;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.*;\nimport static org.mockito.Mockito.anySet;\nimport static org.mockito.Mockito.anyString;\n\nimport java.io.IOException;\nimport java.util.*;\n\nimport org.bonitasoft.engine.api.permission.APICallContext;\nimport org.bonitasoft.engine.authorization.properties.CompoundPermissionsMapping;\nimport org.bonitasoft.engine.authorization.properties.CustomPermissionsMapping;\nimport org.bonitasoft.engine.authorization.properties.DynamicPermissionsChecks;\nimport org.bonitasoft.engine.authorization.properties.ResourcesPermissionsMapping;\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.classloader.SClassLoaderException;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SExecutionException;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.page.ContentType;\nimport org.bonitasoft.engine.session.SSessionNotFoundException;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.session.model.SSession;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.EnvironmentVariables;\nimport org.junit.contrib.java.lang.system.RestoreSystemProperties;\nimport org.junit.contrib.java.lang.system.SystemOutRule;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class PermissionServiceImplTest {\n\n    public static final long TENANT_ID = 12L;\n\n    @Rule\n    public final SystemOutRule systemOutRule = new SystemOutRule().enableLog().muteForSuccessfulTests();\n\n    @Mock\n    private ClassLoaderService classLoaderService;\n\n    @Mock\n    private SessionAccessor sessionAccessor;\n\n    @Mock\n    private SessionService sessionService;\n\n    private PermissionServiceImpl permissionService;\n\n    @Mock\n    private SSession session;\n\n    @Mock\n    private ResourcesPermissionsMapping resourcesPermissionsMapping;\n\n    @Mock\n    private CompoundPermissionsMapping compoundPermissionsMapping;\n\n    @Mock\n    private CustomPermissionsMapping customPermissionsMapping;\n\n    @Mock\n    private DynamicPermissionsChecks dynamicPermissionsChecks;\n\n    @Before\n    public void before()\n            throws IOException, SClassLoaderException, SSessionNotFoundException, BonitaHomeNotSetException {\n\n        doReturn(Thread.currentThread().getContextClassLoader()).when(classLoaderService)\n                .getClassLoader(any());\n        permissionService = spy(\n                new PermissionServiceImpl(classLoaderService, sessionAccessor, sessionService,\n                        compoundPermissionsMapping, resourcesPermissionsMapping, customPermissionsMapping,\n                        dynamicPermissionsChecks, true));\n        doReturn(session).when(sessionService).getSession(anyLong());\n    }\n\n    @Test\n    public void should_start_then_stop_forbid_to_check_api() throws SBonitaException, ClassNotFoundException {\n        //given service not started\n        permissionService.start();\n        permissionService.stop();\n\n        //when\n        Throwable throwable = assertThrows(SExecutionException.class,\n                () -> permissionService.checkAPICallWithScript(\"plop\", new APICallContext()));\n        assertThat(throwable.getMessage()).contains(\"not started\");\n    }\n\n    @Test\n    public void should_pause_call_stop_tow_times() {\n        //when\n        permissionService.stop();\n        permissionService.stop();\n    }\n\n    @Test\n    public void should_checkAPICallWithScript_throw_exception_if_not_started()\n            throws SExecutionException, ClassNotFoundException {\n        //given service not started\n\n        //when\n        Throwable throwable = assertThrows(SExecutionException.class,\n                () -> permissionService.checkAPICallWithScript(\"plop\", new APICallContext()));\n        assertThat(throwable.getMessage()).contains(\"not started\");\n    }\n\n    @Test\n    public void should_checkAPICallWithScript_with_wrong_class() throws SBonitaException, ClassNotFoundException {\n        //given\n        permissionService.start();\n\n        //when\n        Throwable throwable = assertThrows(SExecutionException.class,\n                () -> permissionService.checkAPICallWithScript(String.class.getName(), new APICallContext()));\n        assertThat(throwable.getMessage())\n                .contains(\"does not implements org.bonitasoft.engine.api.permission.PermissionRule\");\n    }\n\n    @Test\n    public void should_checkAPICallWithScript_with_unknown_class() throws SBonitaException, ClassNotFoundException {\n        //given\n        permissionService.start();\n\n        //when\n        assertThrows(ClassNotFoundException.class,\n                () -> permissionService.checkAPICallWithScript(\"plop\", new APICallContext()));\n    }\n\n    @Test\n    public void should_isAllowed_return_true_if_static_authorized() throws Exception {\n        //given\n        final List<String> resourcePermissions = List.of(\"CasePermission\", \"AnOtherPermission\");\n        returnPermissionsFor(\"GET\", \"bpm\", \"case\", null, resourcePermissions);\n        returnUserPermissionsFromSession(\"MyPermission\", \"AnOtherPermission\");\n\n        //when\n        final boolean isAuthorized = permissionService.isAuthorized(new APICallContext(\"GET\", \"bpm\", \"case\", null));\n\n        //then\n        assertThat(isAuthorized).isTrue();\n    }\n\n    private void returnUserPermissionsFromSession(String... sessionUserPermissions) {\n        doReturn(Set.of(sessionUserPermissions)).when(session).getUserPermissions();\n    }\n\n    @Test\n    public void should_isAllowed_return_false_if_static_not_authorized() throws Exception {\n        //given\n        final List<String> resourcePermissions = List.of(\"CasePermission\", \"SecondPermission\");\n        returnPermissionsFor(\"GET\", \"bpm\", \"case\", null, resourcePermissions);\n        returnUserPermissionsFromSession(\"MyPermission\", \"AnOtherPermission\");\n\n        //when\n        final boolean isAuthorized = permissionService.isAuthorized(new APICallContext(\"GET\", \"bpm\", \"case\", null));\n\n        //then\n        assertThat(isAuthorized).isFalse();\n    }\n\n    @Test\n    public void should_isAllowed_return_false_on_resource_with_id_even_if_permission_in_general_is_there()\n            throws Exception {\n        //given\n        returnPermissionsFor(\"GET\", \"bpm\", \"case\", null, List.of(\"CasePermission\", \"AnOtherPermission\"));\n        returnPermissionsFor(\"GET\", \"bpm\", \"case\", List.of(\"12\"), List.of(\"CasePermission\", \"SecondPermission\"));\n        returnUserPermissionsFromSession(\"MyPermission\", \"AnOtherPermission\");\n\n        //when\n        final boolean isAuthorized = permissionService.isAuthorized(new APICallContext(\"GET\", \"bpm\", \"case\", \"12\"));\n\n        //then\n        assertThat(isAuthorized).isFalse();\n    }\n\n    @Test\n    public void should_isAllowed_return_true_on_resource_with_id() throws Exception {\n        //given\n        returnPermissionsFor(\"GET\", \"bpm\", \"case\", List.of(\"12\"), List.of(\"CasePermission\", \"MyPermission\"));\n        returnUserPermissionsFromSession(\"MyPermission\", \"AnOtherPermission\");\n\n        //when\n        final boolean isAuthorized = permissionService.isAuthorized(new APICallContext(\"GET\", \"bpm\", \"case\", \"12\"));\n\n        //then\n        assertThat(isAuthorized).isTrue();\n    }\n\n    @Test\n    public void should_isAllowed_for_resource_with_id_check_parent_if_no_rule() throws Exception {\n        //given\n        final List<String> resourcePermissions = List.of(\"CasePermission\", \"MyPermission\");\n        returnPermissionsFor(\"GET\", \"bpm\", \"case\", null, resourcePermissions);\n        returnUserPermissionsFromSession(\"MyPermission\", \"AnOtherPermission\");\n\n        //when\n        final boolean isAuthorized = permissionService.isAuthorized(new APICallContext(\"GET\", \"bpm\", \"case\", \"12\"));\n\n        //then\n        assertThat(isAuthorized).isTrue();\n    }\n\n    @Test\n    public void should_isAllowed_work_on_resource_with_wildcard() throws Exception {\n        //given\n        final List<String> resourcePermissions = List.of(\"CasePermission\", \"MyPermission\");\n        doReturn(new HashSet<>(resourcePermissions)).when(resourcesPermissionsMapping)\n                .getResourcePermissionsWithWildCard(\"GET\", \"bpm\", \"case\",\n                        List.of(\"12\", \"instantiation\"));\n        returnUserPermissionsFromSession(\"MyPermission\", \"AnOtherPermission\");\n\n        //when\n        final boolean isAuthorized = permissionService\n                .isAuthorized(new APICallContext(\"GET\", \"bpm\", \"case\", \"12/instantiation\"));\n\n        //then\n        assertThat(isAuthorized).isTrue();\n    }\n\n    @Test\n    public void isAuthorized_should_call_script_when_declared() throws Exception {\n        returnDynamicPermissionsFor(\"GET\", \"bpm\", \"case\", null, List.of(\"check|className\"));\n        final APICallContext apiCallContext = new APICallContext(\"GET\", \"bpm\", \"case\", null, \"\", \"\");\n        doReturn(true).when(permissionService).checkAPICallWithScript(\"className\", apiCallContext);\n\n        final boolean isAuthorized = permissionService.isAuthorized(apiCallContext);\n\n        assertThat(isAuthorized).isTrue();\n        verify(permissionService).checkAPICallWithScript(\"className\", apiCallContext);\n    }\n\n    @Test\n    public void isAuthorized_should_return_false_when_script_returns_false() throws Exception {\n        returnDynamicPermissionsFor(\"GET\", \"bpm\", \"case\", null, List.of(\"check|className\"));\n        final APICallContext apiCallContext = new APICallContext(\"GET\", \"bpm\", \"case\", null, \"\", \"\");\n        doReturn(false).when(permissionService).checkAPICallWithScript(\"className\", apiCallContext);\n\n        final boolean isAuthorized = permissionService.isAuthorized(apiCallContext);\n\n        assertThat(isAuthorized).isFalse();\n        verify(permissionService).checkAPICallWithScript(\"className\", apiCallContext);\n    }\n\n    @Test\n    public void isAuthorized_should_return_false_when_script_execution_fails() throws Exception {\n        returnDynamicPermissionsFor(\"GET\", \"bpm\", \"case\", null, List.of(\"check|className\"));\n        final APICallContext apiCallContext = new APICallContext(\"GET\", \"bpm\", \"case\", null, \"\", \"\");\n        doThrow(ClassNotFoundException.class).when(permissionService).checkAPICallWithScript(\"className\",\n                apiCallContext);\n\n        assertThrows(SExecutionException.class,\n                () -> permissionService.isAuthorized(apiCallContext));\n    }\n\n    @Test\n    public void isAuthorized_should_check_static_permissions_if_no_script_declared() throws Exception {\n        //given\n        doReturn(false).when(permissionService).isAuthorizedByStaticPermissions(any(APICallContext.class));\n        final APICallContext apiCallContext = new APICallContext(\"GET\", \"bpm\", \"case\", null, \"\", \"\");\n\n        // when:\n        final boolean authorized = permissionService.isAuthorized(apiCallContext);\n\n        // then:\n        assertThat(authorized).isFalse();\n        verify(permissionService).isAuthorizedByStaticPermissions(eq(apiCallContext));\n    }\n\n    @Test\n    public void isAuthorized_should_return_true_with_profile_check() throws Exception {\n        final String sessionUserPermissions = \"profile|admin\";\n        returnPermissionsFor(\"GET\", \"bpm\", \"case\", null, List.of(sessionUserPermissions, \"check|className\"));\n        final APICallContext apiCallContext = new APICallContext(\"GET\", \"bpm\", \"case\", null, \"\", \"\");\n        returnUserPermissionsFromSession(sessionUserPermissions);\n\n        final boolean isAuthorized = permissionService.isAuthorized(apiCallContext);\n\n        assertThat(isAuthorized).isTrue();\n        verify(permissionService, never()).checkDynamicPermissionsWithScript(any(), any());\n    }\n\n    @Test\n    public void isAuthorized_should_return_true_with_user_check() throws Exception {\n        // given\n        final String userPermissions = \"user|Juan-Carlos\";\n        returnPermissionsFor(\"GET\", \"bpm\", \"case\", null, List.of(userPermissions, \"check|className\"));\n        returnUserPermissionsFromSession(userPermissions);\n\n        // when\n        final boolean isAuthorized = permissionService.isAuthorized(\n                new APICallContext(\"GET\", \"bpm\", \"case\", null, \"\", \"\"));\n\n        // then\n        assertThat(isAuthorized).isTrue();\n        verify(permissionService, never()).checkDynamicPermissionsWithScript(any(), anyString());\n    }\n\n    @Test\n    public void isAuthorized_should_work_with_default_rule() throws Exception {\n        // given\n        long userId = 10L;\n        doReturn(userId).when(session).getUserId();\n        final List<String> dynamicAuthorizations = List.of(\"check|org.bonitasoft.permissions.UserPermissionRule\");\n        returnDynamicPermissionsFor(\"GET\", \"identity\", \"user\", null, dynamicAuthorizations);\n\n        // when\n        permissionService.start();\n        final boolean isAuthorizedOnAllUsers = permissionService.isAuthorized(\n                new APICallContext(\"GET\", \"identity\", \"user\", null, \"\", \"\"));\n\n        // then\n        assertThat(isAuthorizedOnAllUsers).isFalse();\n\n        // when\n        final boolean isAuthorizedOnCurrentUser = permissionService.isAuthorized(\n                new APICallContext(\"GET\", \"identity\", \"user\", String.valueOf(userId), \"\", \"\"));\n\n        // then\n        assertThat(isAuthorizedOnCurrentUser).isTrue();\n    }\n\n    @Test\n    public void isAuthorized_should_throw_exception_if_the_script_is_not_found() throws Exception {\n        returnDynamicPermissionsFor(\"GET\", \"bpm\", \"case\", null, List.of(\"check|className\"));\n        final APICallContext apiCallContext = new APICallContext(\"GET\", \"bpm\", \"case\", null, \"\", \"\");\n        doThrow(ClassNotFoundException.class).when(permissionService).checkAPICallWithScript(\"className\",\n                apiCallContext);\n\n        assertThrows(SExecutionException.class,\n                () -> permissionService.isAuthorized(apiCallContext));\n    }\n\n    @Test\n    public void isAuthorized_should_throw_exception_if_syntax_is_invalid() {\n        returnDynamicPermissionsFor(\"GET\", \"bpm\", \"case\", null, List.of(\"anyText\"));\n        final APICallContext apiCallContext = new APICallContext(\"GET\", \"bpm\", \"case\", null, \"\", \"\");\n\n        assertThrows(IllegalArgumentException.class,\n                () -> permissionService.isAuthorized(apiCallContext));\n    }\n\n    @Test\n    public void checkResourceAuthorizationsSyntax_should_throw_exception_if_syntax_is_invalid() {\n        final Set<String> resourceAuthorizations = new HashSet<>();\n        resourceAuthorizations.add(\"any string\");\n\n        assertThrows(IllegalArgumentException.class,\n                () -> permissionService.checkResourceAuthorizationsSyntax(resourceAuthorizations));\n    }\n\n    @Test\n    public void checkResourceAuthorizationsSyntax_should_not_throw_exception_if_syntax_is_valid() {\n        final Set<String> resourceAuthorizations = Set.of(\"user|any.username\", \"profile|any.profile\",\n                \"check|className\");\n\n        permissionService.checkResourceAuthorizationsSyntax(resourceAuthorizations);\n\n        // no exception\n    }\n\n    @Test\n    public void should_checkPermissions_return_true_if_dynamic_authorized() throws Exception {\n        //given\n        final List<String> dynamicAuthorizations = List.of(\"check|className\");\n        returnDynamicPermissionsFor(\"GET\", \"bpm\", \"case\", null, dynamicAuthorizations);\n        final APICallContext apiCallContext = new APICallContext(\"GET\", \"bpm\", \"case\", null, \"\", \"\");\n        doReturn(true).when(permissionService).isAuthorizedByDynamicPermissions(eq(apiCallContext),\n                anySet(), anySet());\n\n        //when\n        final boolean isAuthorized = permissionService.isAuthorized(apiCallContext);\n\n        //then\n        assertThat(isAuthorized).isTrue();\n    }\n\n    @Test\n    public void should_checkPermissions_return_false_if_dynamic_not_authorized() throws Exception {\n        //given\n        final List<String> dynamicAuthorizations = List.of(\"check|className\");\n        returnDynamicPermissionsFor(\"GET\", \"bpm\", \"case\", null, dynamicAuthorizations);\n        final APICallContext apiCallContext = new APICallContext(\"GET\", \"bpm\", \"case\", null, \"\", \"\");\n        doReturn(false).when(permissionService).isAuthorizedByDynamicPermissions(eq(apiCallContext),\n                anySet(), anySet());\n\n        //when\n        final boolean isAuthorized = permissionService.isAuthorized(apiCallContext);\n\n        //then\n        assertThat(isAuthorized).isFalse();\n    }\n\n    @Test\n    public void should_not_return_resource_when_unknown_resource_is_declared_in_PageProperties() {\n        // Given\n        doReturn(Collections.emptySet()).when(resourcesPermissionsMapping).getPropertyAsSet(\"GET|unknown/resource\");\n        doReturn(Set.of(\"Organization Visualization\", \"Organization Management\"))\n                .when(resourcesPermissionsMapping).getPropertyAsSet(\"PUT|identity/user\");\n\n        // When\n        final Set<String> customPagePermissions = permissionService.getCustomPagePermissions(\n                \"[GET|unknown/resource, PUT|identity/user]\", resourcesPermissionsMapping);\n\n        // Then\n        assertThat(customPagePermissions).containsOnly(\"Organization Visualization\", \"Organization Management\");\n    }\n\n    @Test\n    public void should_add_api_extension_permissions() {\n        //given\n        Properties properties = new Properties();\n        properties.put(PermissionServiceImpl.PROPERTY_CONTENT_TYPE, ContentType.API_EXTENSION);\n        properties.put(PermissionServiceImpl.PROPERTY_API_EXTENSIONS, \"restResource1,restResource2\");\n        properties.put(\"restResource1.method\", \"GET\");\n        properties.put(\"restResource1.pathTemplate\", \"restApiGet\");\n        properties.put(\"restResource1.permissions\", \"permission1\");\n        properties.put(\"restResource2.method\", \"POST\");\n        properties.put(\"restResource2.pathTemplate\", \"restApiPost\");\n        properties.put(\"restResource2.permissions\", \"permission2,permission3\");\n\n        //when\n        permissionService.addRestApiExtensionPermissions(resourcesPermissionsMapping, properties);\n\n        //then\n        verify(resourcesPermissionsMapping).setInternalProperty(\"GET|extension/restApiGet\", \"[permission1]\");\n        verify(resourcesPermissionsMapping).setInternalProperty(\"POST|extension/restApiPost\",\n                \"[permission2,permission3]\");\n    }\n\n    @Test\n    public void should_add_api_extension_permissions_for_absolute_pathTemplate() {\n        //given\n        Properties properties = new Properties();\n        properties.put(PermissionServiceImpl.PROPERTY_CONTENT_TYPE, ContentType.API_EXTENSION);\n        properties.put(PermissionServiceImpl.PROPERTY_API_EXTENSIONS, \"restResource1,restResource2\");\n        properties.put(\"restResource1.method\", \"GET\");\n        properties.put(\"restResource1.pathTemplate\", \"/restApiGet\");\n        properties.put(\"restResource1.permissions\", \"permission1\");\n\n        //when\n        permissionService.addRestApiExtensionPermissions(resourcesPermissionsMapping, properties);\n\n        //then\n        verify(resourcesPermissionsMapping).setInternalProperty(\"GET|extension/restApiGet\", \"[permission1]\");\n    }\n\n    @Test\n    public void should_not_add_non_api_extension_permissions_to_resource_permission_mapping() {\n        //given\n        Properties properties = new Properties();\n        properties.put(PermissionServiceImpl.PROPERTY_CONTENT_TYPE, \"page\");\n        properties.put(PermissionServiceImpl.PROPERTY_API_EXTENSIONS, \"restResource1\");\n        properties.put(\"restResource1.method\", \"GET\");\n        properties.put(\"restResource1.pathTemplate\", \"restApiGet\");\n        properties.put(\"restResource1.permissions\", \"permission1\");\n\n        //when\n        permissionService.addRestApiExtensionPermissions(resourcesPermissionsMapping, properties);\n\n        //then\n        verifyNoInteractions(resourcesPermissionsMapping);\n    }\n\n    @Test\n    public void removePermissions_should_remove_all_rest_api_keys_from_properties() {\n        // given:\n        Properties properties = new Properties();\n        properties.put(PermissionServiceImpl.PROPERTY_CONTENT_TYPE, ContentType.API_EXTENSION);\n        properties.put(PermissionServiceImpl.PROPERTY_API_EXTENSIONS, \"restResource\");\n        properties.put(\"restResource.method\", \"GET\");\n        properties.put(\"restResource.pathTemplate\", \"restApiGet\");\n        properties.put(\"restResource.permissions\", \"permission\");\n\n        // when:\n        permissionService.removePermissions(properties);\n\n        // then:\n        verify(resourcesPermissionsMapping).removeInternalProperty(\"GET|extension/restApiGet\");\n    }\n\n    @Test\n    public void removePermissions_should_remove_key_from_compound_permission_mappings() {\n        // given:\n        Properties properties = new Properties();\n        properties.put(\"name\", \"custompage_page44\");\n        properties.put(PermissionServiceImpl.PROPERTY_CONTENT_TYPE, ContentType.API_EXTENSION);\n        properties.put(PermissionServiceImpl.PROPERTY_API_EXTENSIONS, \"restResource\");\n        properties.put(\"restResource.method\", \"GET\");\n        properties.put(\"restResource.pathTemplate\", \"restApiGet\");\n        properties.put(\"restResource.permissions\", \"permission\");\n\n        // when:\n        permissionService.removePermissions(properties);\n\n        // then:\n        verify(compoundPermissionsMapping).removeInternalProperty(\"custompage_page44\");\n    }\n\n    @Test\n    public void addPermissions_should_update_compound_permissions_for_pages() {\n        // given:\n        Properties properties = new Properties();\n        properties.put(\"name\", \"myPage\");\n        properties.put(PermissionServiceImpl.PROPERTY_CONTENT_TYPE, ContentType.PAGE);\n        properties.put(PermissionServiceImpl.RESOURCES_PROPERTY, \"[]\");\n\n        // when:\n        permissionService.addPermissions(\"myPage\", properties);\n\n        // then:\n        verify(compoundPermissionsMapping).setInternalPropertyAsSet(eq(\"myPage\"), anySet());\n    }\n\n    @Test\n    public void addPermissions_should_update_compound_permissions_for_layouts() {\n        // given:\n        Properties properties = new Properties();\n        properties.put(\"name\", \"myLayout\");\n        properties.put(PermissionServiceImpl.PROPERTY_CONTENT_TYPE, ContentType.LAYOUT);\n        properties.put(PermissionServiceImpl.RESOURCES_PROPERTY, \"[]\");\n\n        // when:\n        permissionService.addPermissions(\"myLayout\", properties);\n\n        // then:\n        verify(compoundPermissionsMapping).setInternalPropertyAsSet(eq(\"myLayout\"), anySet());\n    }\n\n    @Test\n    public void addPermissions_should_not_update_compound_permissions_for_forms() {\n        // given:\n        Properties properties = new Properties();\n        properties.put(\"name\", \"myForm\");\n        properties.put(PermissionServiceImpl.PROPERTY_CONTENT_TYPE, ContentType.FORM);\n        properties.put(PermissionServiceImpl.RESOURCES_PROPERTY, \"[]\");\n\n        // when:\n        permissionService.addPermissions(\"myForm\", properties);\n\n        // then:\n        verify(compoundPermissionsMapping, never()).setPropertyAsSet(eq(\"myForm\"), anySet());\n    }\n\n    @Test\n    public void should_return_permissions_associated_with_the_resource() {\n        //given\n        final List<String> resourcePermissions = List.of(\"CasePermission\", \"MyPermission\");\n        doReturn(new HashSet<>(resourcePermissions)).when(resourcesPermissionsMapping)\n                .getPropertyAsSet(\"GET|bpm/case\");\n\n        //then\n        assertThat(permissionService.getResourcePermissions(\"GET|bpm/case\")).containsExactlyInAnyOrder(\"CasePermission\",\n                \"MyPermission\");\n    }\n\n    @Rule\n    public RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties();\n\n    @Rule\n    public EnvironmentVariables envVar = new EnvironmentVariables();\n\n    @Test\n    public void initDynamicPermissionsEnabledProperty_should_take_System_property_if_set() {\n        // given:\n        System.setProperty(\"bonita.runtime.authorization.dynamic-check.enabled\", \"false\");\n        envVar.set(\"bonita.runtime.authorization.dynamic-check.enabled\", \"true\");\n\n        // when:\n        final boolean property = permissionService.initDynamicPermissionsEnabledProperty(true).isEnabled();\n\n        // then:\n        assertThat(property).isFalse();\n    }\n\n    @Test\n    public void initDynamicPermissionsEnabledProperty_should_take_envVar_if_no_System_property_if_set() {\n        // given:\n        envVar.set(\"BONITA_RUNTIME_AUTHORIZATION_DYNAMICCHECK_ENABLED\", \"false\");\n\n        // when:\n        final boolean property = permissionService.initDynamicPermissionsEnabledProperty(true).isEnabled();\n\n        // then:\n        assertThat(property).isFalse();\n    }\n\n    @Test\n    public void initDynamicPermissionsEnabledProperty_should_take_file_property_if_no_System__nor_env_property_if_set() {\n        // when:\n        final boolean property = permissionService.initDynamicPermissionsEnabledProperty(false).isEnabled();\n\n        // then:\n        assertThat(property).isFalse();\n    }\n\n    private void returnPermissionsFor(final String method, final String apiName, final String resourceName,\n            final List<String> resourceQualifiers,\n            final List<String> toBeReturned) {\n        returnPermissionsFor(method, apiName, resourceName, resourceQualifiers, toBeReturned,\n                resourcesPermissionsMapping);\n    }\n\n    private void returnDynamicPermissionsFor(final String method, final String apiName, final String resourceName,\n            final List<String> resourceQualifiers,\n            final List<String> toBeReturned) {\n        returnPermissionsFor(method, apiName, resourceName, resourceQualifiers, toBeReturned, dynamicPermissionsChecks);\n    }\n\n    private void returnPermissionsFor(final String method, final String apiName, final String resourceName,\n            final List<String> resourceQualifiers,\n            final List<String> toBeReturned, final ResourcesPermissionsMapping resourcesPermissionsMapping) {\n        if (resourceQualifiers != null) {\n            doReturn(new HashSet<>(toBeReturned)).when(resourcesPermissionsMapping).getResourcePermissions(method,\n                    apiName, resourceName,\n                    resourceQualifiers);\n        } else {\n            doReturn(new HashSet<>(toBeReturned)).when(resourcesPermissionsMapping).getResourcePermissions(method,\n                    apiName, resourceName);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/bpm/contract/validation/ConstraintsDefinitionHelperTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.contract.validation;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.bpm.contract.validation.builder.SComplexInputDefinitionBuilder.aComplexInput;\nimport static org.bonitasoft.engine.bpm.contract.validation.builder.SContractDefinitionBuilder.aContract;\nimport static org.bonitasoft.engine.bpm.contract.validation.builder.SSimpleInputDefinitionBuilder.aSimpleInput;\n\nimport org.bonitasoft.engine.core.process.definition.model.SContractDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SInputDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SType;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class ConstraintsDefinitionHelperTest {\n\n    private ConstraintsDefinitionHelper constraintsDefinitionHelper;\n\n    @Before\n    public void before() throws Exception {\n        constraintsDefinitionHelper = new ConstraintsDefinitionHelper();\n\n    }\n\n    @Test\n    public void should_find_simple_definition() throws Exception {\n        //given\n        final SContractDefinition contract = buildContract();\n\n        //when\n        final String[] inputNames = { \"user\", \"firstName\", \"lastName\", \"userId\", \"nature\", \"simple\", \"expenseLine\",\n                \"date\" };\n        for (final String inputName : inputNames) {\n            //then\n            checkDefinitionIsFound(contract, inputName);\n        }\n    }\n\n    @Test\n    public void should_not_find_simple_definition() throws Exception {\n        //given\n        final SContractDefinition contract = buildContract();\n\n        //when then\n        assertThat(constraintsDefinitionHelper.getInputDefinition(contract, \"not an input\")).isNull();\n    }\n\n    private SContractDefinition buildContract() {\n        final SContractDefinition contract = aContract()\n                .withInput(\n                        aComplexInput().withName(\"user\")\n                                .withInput(aSimpleInput(SType.TEXT).withName(\"firstName\").build())\n                                .withInput(aSimpleInput(SType.TEXT).withName(\"lastName\").build())\n                                .withInput(aSimpleInput(SType.LONG).withName(\"userId\").build()))\n                .withInput(\n                        aComplexInput()\n                                .withName(\"expenseReport\")\n                                .withInput(\n                                        aComplexInput().withName(\"expenseLine\").withMultiple(true)\n                                                .withInput(aSimpleInput(SType.TEXT).withName(\"nature\").build())\n                                                .withInput(aSimpleInput(SType.DECIMAL).withName(\"amount\").build())\n                                                .withInput(aSimpleInput(SType.DATE).withName(\"date\").build())\n                                                .withInput(aSimpleInput(SType.TEXT).withName(\"comment\").build())\n                                                .build())\n                                .build())\n                .withInput(aSimpleInput(SType.TEXT).withMultiple(true).withName(\"simple\").build())\n                .build();\n        return contract;\n    }\n\n    private void checkDefinitionIsFound(final SContractDefinition contract, final String inputName) {\n\n        //when\n        final SInputDefinition inputDefinition = constraintsDefinitionHelper.getInputDefinition(contract, inputName);\n\n        //then\n        assertThat(inputDefinition).as(\"should find definition for \" + inputName).isNotNull();\n        assertThat(inputDefinition.getName()).as(\"should find definition\").isEqualTo(inputName);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/bpm/contract/validation/ContractConstraintsValidatorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.contract.validation;\n\nimport static org.assertj.core.api.Assertions.*;\nimport static org.bonitasoft.engine.bpm.contract.validation.builder.MapBuilder.contractInputMap;\nimport static org.bonitasoft.engine.bpm.contract.validation.builder.SConstraintDefinitionBuilder.aRuleFor;\nimport static org.bonitasoft.engine.bpm.contract.validation.builder.SContractDefinitionBuilder.aContract;\nimport static org.bonitasoft.engine.bpm.contract.validation.builder.SSimpleInputDefinitionBuilder.aSimpleInput;\nimport static org.bonitasoft.engine.core.process.definition.model.SType.BOOLEAN;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.argThat;\nimport static org.mockito.Mockito.*;\n\nimport java.util.regex.Pattern;\n\nimport org.bonitasoft.engine.core.process.definition.model.SContractDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SConstraintDefinitionImpl;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SContractViolationException;\nimport org.bonitasoft.engine.expression.ContainerState;\nimport org.bonitasoft.engine.expression.ExpressionService;\nimport org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.SystemOutRule;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ContractConstraintsValidatorTest {\n\n    private static final String NICE_COMMENT = \"no way!\";\n    private static final String COMMENT = \"comment\";\n    private static final String IS_VALID = \"isValid\";\n    private static final long PROCESS_DEFINITION_ID = 154L;\n\n    @Rule\n    public final SystemOutRule systemOutRule = new SystemOutRule().enableLog().muteForSuccessfulTests();\n    @Mock\n    private ExpressionService expressionService;\n\n    private ContractConstraintsValidator validator;\n\n    @Before\n    public void setUp() throws SExpressionTypeUnknownException, SExpressionDependencyMissingException,\n            SExpressionEvaluationException,\n            SInvalidExpressionException {\n        doReturn(false).when(expressionService).evaluate(any(SExpression.class), anyMap(), anyMap(),\n                any(ContainerState.class));\n        returnTrueForExpressionWithContent(\"isValid != null\");\n        returnTrueForExpressionWithContent(\"isValid || !isValid && comment != null\");\n        validator = new ContractConstraintsValidator(expressionService);\n    }\n\n    private void returnTrueForExpressionWithContent(String content)\n            throws SExpressionTypeUnknownException, SExpressionEvaluationException,\n            SExpressionDependencyMissingException, SInvalidExpressionException {\n        doReturn(true).when(expressionService).evaluate(expressionHavingContent(content), anyMap(),\n                anyMap(), any(ContainerState.class));\n    }\n\n    private SExpression expressionHavingContent(String content) {\n        return argThat(expr -> expr.getContent().equals(content));\n    }\n\n    @Test\n    public void should_log_all_rules_in_debug_mode() throws Exception {\n        final SContractDefinition contract = buildContractWithInputsAndConstraints();\n\n        systemOutRule.clearLog();\n        validator.validate(PROCESS_DEFINITION_ID, contract,\n                contractInputMap(entry(IS_VALID, false), entry(COMMENT, NICE_COMMENT)));\n\n        //then\n        String[] log = systemOutRule.getLog().split(\"\\n\");\n        // String.matches() has a different behaviour on windows so use Pattern and Matcher instead\n        assertThat(log).anyMatch(l -> Pattern.compile(\".*DEBUG.*Evaluating constraint.*\").matcher(l).find());\n        assertThat(systemOutRule.getLog()).doesNotContain(\"WARN\");\n    }\n\n    @Test\n    public void should_not_log_all_rules_in_info_mode() throws Exception {\n        final SContractDefinition contract = buildContractWithInputsAndConstraints();\n\n        //\n        systemOutRule.clearLog();\n        validator.validate(PROCESS_DEFINITION_ID, contract,\n                contractInputMap(entry(IS_VALID, false), entry(COMMENT, NICE_COMMENT)));\n\n        //then\n        String[] log = systemOutRule.getLog().split(\"\\n\");\n        assertThat(log).noneMatch(l -> l.matches(\".*INFO.*Evaluating constraint.*\"));\n        assertThat(systemOutRule.getLog()).doesNotContain(\"WARN\");\n    }\n\n    @Test\n    public void isValid_should_log_invalid_constraints_in_warning_mode() throws Exception {\n        //given\n        final SContractDefinition contract = aContract()\n                .withInput(aSimpleInput(BOOLEAN).withName(IS_VALID).build())\n                .withConstraint(aRuleFor(IS_VALID).name(\"false constraint\").expression(\"false\")\n                        .explanation(\"should re implement\").build())\n                .build();\n\n        try {\n            systemOutRule.clearLog();\n            validator.validate(PROCESS_DEFINITION_ID, contract,\n                    contractInputMap(entry(IS_VALID, false), entry(COMMENT, null)));\n            fail(\"validation should fail\");\n        } catch (final SContractViolationException e) {\n            assertThat(systemOutRule.getLog()).contains(\n                    \"Constraint [false constraint] on input(s) [isValid] is not valid\");\n        }\n    }\n\n    @Test\n    public void validate_should_throw_ContractViolation_with_explanations_when_rule_fails_to_evaluate()\n            throws Exception {\n        //given\n        final SContractDefinition contract = buildContractWithInputsAndConstraints();\n        final SConstraintDefinitionImpl badRule = new SConstraintDefinitionImpl(\"bad rule\", \"a == b\", \"failing rule\");\n        contract.getConstraints().add(badRule);\n\n        //when\n        try {\n            validator.validate(PROCESS_DEFINITION_ID, contract,\n                    contractInputMap(entry(IS_VALID, false), entry(COMMENT, NICE_COMMENT)));\n            fail(\"validation should fail\");\n        } catch (final SContractViolationException e) {\n            assertThat(e.getExplanations()).hasSize(1).containsExactly(badRule.getExplanation());\n        }\n\n    }\n\n    @Test\n    public void exception_during_evaluation_report_it() throws Exception {\n        //given\n        final SContractDefinition contract = buildContractWithInputsAndConstraints();\n        doThrow(SExpressionEvaluationException.class).when(expressionService).evaluate(any(SExpression.class), anyMap(),\n                anyMap(), any(ContainerState.class));\n\n        //when\n        try {\n            validator.validate(PROCESS_DEFINITION_ID, contract,\n                    contractInputMap(entry(IS_VALID, false), entry(COMMENT, NICE_COMMENT)));\n            fail(\"validation should fail\");\n        } catch (final SContractViolationException e) {\n            assertThat(e.getMessage()).contains(\"Exception while\");\n            assertThat(e.getCause()).isNotNull().isInstanceOf(SExpressionEvaluationException.class);\n        }\n    }\n\n    private SContractDefinition buildContractWithInputsAndConstraints() {\n        return aContract()\n                .withInput(aSimpleInput(BOOLEAN).withName(IS_VALID).build())\n                .withInput(aSimpleInput(BOOLEAN).withName(IS_VALID).build())\n                .withConstraint(aRuleFor(IS_VALID).name(\"Mandatory\").expression(\"isValid != null\")\n                        .explanation(\"isValid must be set\").build())\n                .withConstraint(aRuleFor(IS_VALID, COMMENT).name(\"Comment_Needed_If_Not_Valid\")\n                        .expression(\"isValid || !isValid && comment != null\")\n                        .explanation(\"A comment is required when no validation\").build())\n                .build();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/bpm/contract/validation/ContractStructureValidatorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.contract.validation;\n\nimport static org.assertj.core.api.Assertions.*;\nimport static org.bonitasoft.engine.bpm.contract.validation.builder.MapBuilder.aMap;\nimport static org.bonitasoft.engine.bpm.contract.validation.builder.MapBuilder.contractInputMap;\nimport static org.bonitasoft.engine.bpm.contract.validation.builder.SComplexInputDefinitionBuilder.aComplexInput;\nimport static org.bonitasoft.engine.bpm.contract.validation.builder.SContractDefinitionBuilder.aContract;\nimport static org.bonitasoft.engine.bpm.contract.validation.builder.SSimpleInputDefinitionBuilder.aSimpleInput;\nimport static org.bonitasoft.engine.core.process.definition.model.SType.BOOLEAN;\nimport static org.bonitasoft.engine.core.process.definition.model.SType.INTEGER;\nimport static org.bonitasoft.engine.core.process.definition.model.SType.TEXT;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.*;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.core.process.definition.model.SContractDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SInputDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SType;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SContractViolationException;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.SystemOutRule;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ContractStructureValidatorTest {\n\n    @Rule\n    public SystemOutRule systemOutRule = new SystemOutRule().enableLog();\n    @Mock\n    ContractTypeValidator typeValidator;\n    @InjectMocks\n    private ContractStructureValidator validator;\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Before\n    public void setUp() {\n        doReturn(true).when(typeValidator).validate(any(SInputDefinition.class), any(), any(ErrorReporter.class));\n    }\n\n    @Test\n    public void should_log_inputs_provided_but_not_in_defined_in_contract() throws Exception {\n        final SContractDefinition contract = aContract().withInput(aSimpleInput(TEXT).withName(\"aText\")).build();\n        final Map<String, Serializable> taskInputs = aMap()\n                .put(\"aText\", \"should be provided\")\n                .put(\"someFieldNotDefinedInContract\", true)\n                .put(\"someOtherFieldNotDefinedInContract\", \"42\").build();\n\n        systemOutRule.clearLog();\n        validator.validate(contract, taskInputs);\n\n        assertThat(systemOutRule.getLog()).contains(\"Unexpected input [someFieldNotDefinedInContract] provided\");\n    }\n\n    @Test\n    public void should_pass_when_inputs_are_provided_and_valid() throws Exception {\n        final SContractDefinition contract = aContract()\n                .withInput(aSimpleInput(TEXT).withName(\"aText\"))\n                .withInput(aSimpleInput(BOOLEAN).withName(\"aBoolean\")).build();\n        final Map<String, Serializable> taskInputs = aMap()\n                .put(\"aText\", \"hello\")\n                .put(\"aBoolean\", true).build();\n\n        validator.validate(contract, taskInputs);\n\n        // No exception expected\n    }\n\n    @Test\n    public void should_pass_when_multiple_inputs_are_provided_and_valid() throws Exception {\n        final SContractDefinition contract = aContract()\n                .withInput(aSimpleInput(TEXT).withName(\"aText\").withMultiple(true)).build();\n\n        final List<String> values = new ArrayList<>();\n        values.add(\"hello\");\n        values.add(\"world\");\n\n        final Map<String, Serializable> taskInputs = aMap()\n                .put(\"aText\", (Serializable) values).build();\n\n        validator.validate(contract, taskInputs);\n\n        // No exception expected\n    }\n\n    @Test\n    public void should_pass_when_multiple_complex_inputs_are_provided_and_valid() throws Exception {\n        //given\n        final SContractDefinition contract = aContract()\n                .withInput(\n                        aComplexInput().withName(\"complex\").withMultiple(true)\n                                .withInput(aSimpleInput(TEXT).withName(\"name\").build(),\n                                        aSimpleInput(SType.INTEGER).withName(\"value\").build()))\n                .build();\n\n        final List<Map<String, Serializable>> complexList = new ArrayList<>();\n        complexList.add(aMap().put(\"name\", \"value1\").put(\"value\", 5).build());\n        complexList.add(aMap().put(\"name\", \"value2\").put(\"value\", 8).build());\n\n        final Map<String, Serializable> taskInputs = aMap()\n                .put(\"complex\", (Serializable) complexList).build();\n\n        //then no exception expected\n        try {\n            validator.validate(contract, taskInputs);\n        } catch (final SContractViolationException e) {\n            fail(e.getExplanations().toString());\n        }\n    }\n\n    @Test\n    public void should_pass_when_complex_inputs_are_provided_and_valid() throws Exception {\n        final SContractDefinition contract = aContract()\n                .withInput(aSimpleInput(TEXT).withName(\"aText\"))\n                .withInput(aSimpleInput(BOOLEAN).withName(\"aBoolean\"))\n                .withInput(aComplexInput().withName(\"complex\").withInput(aSimpleInput(SType.TEXT).withName(\"embedded\")))\n                .build();\n        final Map<String, Serializable> taskInputs = aMap()\n                .put(\"aText\", \"hello\")\n                .put(\"aBoolean\", true)\n                .put(\"complex\", (Serializable) aMap().put(\"embedded\", \"aValue\").build())\n                .build();\n\n        validator.validate(contract, taskInputs);\n    }\n\n    @Test\n    public void should_throw_exception_with_explanations_when_inputs_are_missing() throws Exception {\n        final SContractDefinition contract = aContract()\n                .withInput(aSimpleInput(TEXT).withName(\"aText\").build())\n                .withInput(aSimpleInput(TEXT).withName(\"anotherText\").build()).build();\n\n        try {\n            validator.validate(contract, new HashMap<String, Serializable>());\n            fail(\"Expected SContractViolationException\");\n        } catch (final SContractViolationException e) {\n            assertThat(e.getExplanations())\n                    .containsOnly(\"Expected input [aText] is missing\", \"Expected input [anotherText] is missing\");\n        }\n    }\n\n    @Test\n    public void should_throw_exception_with_explanations_when_complex_inputs_are_missing() throws Exception {\n        final SContractDefinition contract = aContract()\n                .withInput(aComplexInput().withName(\"complex\").withInput(aSimpleInput(SType.TEXT).withName(\"embedded\")))\n                .build();\n\n        try {\n            validator.validate(contract, new HashMap<String, Serializable>());\n            fail(\"Expected SContractViolationException\");\n        } catch (final SContractViolationException e) {\n            assertThat(e.getExplanations())\n                    .containsOnly(\"Expected input [complex] is missing\");\n        }\n    }\n\n    @Test\n    public void should_throw_exception_with_explanations_when_complex_inputs_leaves_are_missing() throws Exception {\n        final SContractDefinition contract = aContract()\n                .withInput(aComplexInput().withName(\"complex\").withInput(aSimpleInput(SType.TEXT).withName(\"embedded\")))\n                .build();\n        final Map<String, Serializable> map = aMap().put(\"complex\", (Serializable) aMap().build())\n                .build();\n        try {\n            validator.validate(contract, map);\n            fail(\"expected exception has not been thrown\");\n        } catch (final SContractViolationException e) {\n            assertThat(e.getExplanations()).containsOnly(\"Expected input [embedded] is missing\");\n        }\n    }\n\n    @Test\n    public void should_not_check_children_when_complex_input_is_null() throws Exception {\n        // Given\n        final SInputDefinition complex = aComplexInput().withName(\"complex\")\n                .withInput(aSimpleInput(SType.TEXT).withName(\"embedded\")).build();\n        final SContractDefinition contract = aContract().withInput(complex).build();\n        final Map<String, Serializable> inputs = Collections.singletonMap(\"complex\", null);\n        final ContractStructureValidator spy = spy(this.validator);\n        // when\n        spy.validate(contract, inputs);\n        // then\n        verify(spy, times(0)).validateInputContainer(eq(complex), eq(inputs), any(ErrorReporter.class));\n    }\n\n    @Test\n    public void should_not_check_type_validation_when_input_is_null() throws Exception {\n        final SContractDefinition contract = aContract().withInput(aSimpleInput(TEXT).withName(\"someNullText\")).build();\n        validator.validate(contract, Collections.<String, Serializable> singletonMap(\"someNullText\", null));\n        verifyNoInteractions(typeValidator);\n    }\n\n    @Test\n    public void should_call_type_validator_when_types_are_not_valid() throws Exception {\n        final SContractDefinition contract = aContract()\n                .withInput(aSimpleInput(INTEGER).withName(\"anInteger\"))\n                .withInput(aComplexInput().withName(\"complex\").withInput(aSimpleInput(SType.TEXT).withName(\"embedded\")))\n                .build();\n        doReturn(false).when(typeValidator).validate(any(SInputDefinition.class), any(Object.class),\n                any(ErrorReporter.class));\n        final Map<String, Serializable> taskInputs = aMap().put(\"anInteger\", \"thisIsNotAnInteger\")\n                .put(\"complex\", \"thisIsNotAComplex\").build();\n\n        validator.validate(contract, taskInputs);\n\n        verify(typeValidator).validate(any(SInputDefinition.class), eq(\"thisIsNotAComplex\"), any(ErrorReporter.class));\n    }\n\n    @Test\n    public void validate_should_handle_null_inputs_map_argument_as_empty_map() throws Exception {\n        final SContractDefinition contract = aContract()\n                .withInput(aSimpleInput(INTEGER).withName(\"anInteger\"))\n                .withInput(aComplexInput().withName(\"complex\").withInput(aSimpleInput(SType.TEXT).withName(\"embedded\")))\n                .build();\n        final ErrorReporter errorReporter = new ErrorReporter();\n        errorReporter.addError(\"plop\");\n        try {\n            validator.validate(contract, null);\n            fail(\"expected exception has not been thrown\");\n        } catch (final SContractViolationException e) {\n            assertThat(e.getExplanations()).containsOnly(\"Expected input [anInteger] is missing\",\n                    \"Expected input [complex] is missing\");\n        }\n    }\n\n    @Test\n    public void should_not_throw_exception_for_present_simple_input_with_null_value() throws Exception {\n        final SContractDefinition contract = aContract().withInput(aSimpleInput(TEXT).withName(\"firstName\")).build();\n\n        validator.validate(contract, contractInputMap(entry(\"firstName\", null)));\n    }\n\n    @Test\n    public void should_not_throw_exception_for_present_complex_input_with_null_value() throws Exception {\n        final SContractDefinition contract = aContract().withInput(aComplexInput().withName(\"employee\")).build();\n\n        validator.validate(contract, contractInputMap(entry(\"employee\", null)));\n    }\n\n    @Test\n    public void should_not_throw_exception_for_present_multiple_complex_input_with_empty_list() throws Exception {\n        final SContractDefinition contract = aContract()\n                .withInput(aComplexInput().withName(\"complex\").withMultiple(true)\n                        .withInput(aSimpleInput(TEXT).withName(\"name\").build()))\n                .build();\n\n        final Map<String, Serializable> taskInputs = aMap()\n                .put(\"complex\", (Serializable) Collections.emptyList())\n                .build();\n\n        validator.validate(contract, taskInputs);\n    }\n\n    @Test\n    public void should_ignore_null_values_for_multiple_complex_input() throws Exception {\n        final SContractDefinition contract = aContract()\n                .withInput(aComplexInput().withName(\"complex\").withMultiple(true).withInput(\n                        aSimpleInput(TEXT).withName(\"plic\").build()))\n                .build();\n\n        final List<Map<String, Serializable>> complexList = new ArrayList<>();\n        complexList.add(null);\n\n        final Map<String, Serializable> taskInputs = aMap().put(\"complex\", (Serializable) complexList)\n                .build();\n\n        validator.validate(contract, taskInputs);\n    }\n\n    @Test\n    public void should_ignore_null_values_for_multiple_complex_input_and_validate_next_element() throws Exception {\n        final SContractDefinition contract = aContract()\n                .withInput(aComplexInput().withName(\"complex\").withMultiple(true).withInput(\n                        aSimpleInput(TEXT).withName(\"plic\").build(),\n                        aSimpleInput(INTEGER).withName(\"plac\").build()))\n                .build();\n\n        final List<Map<String, Serializable>> complexList = new ArrayList<>();\n        complexList.add(null);\n        complexList.add(aMap().put(\"plic\", \"value2\").build());\n\n        final Map<String, Serializable> taskInputs = aMap().put(\"complex\", (Serializable) complexList)\n                .build();\n\n        try {\n            validator.validate(contract, taskInputs);\n            fail(\"Should throw validation exception\");\n        } catch (final SContractViolationException e) {\n            assertThat(e.getExplanations()).containsOnly(\"Expected input [plac] is missing\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/bpm/contract/validation/ContractTypeValidatorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.contract.validation;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.bonitasoft.engine.bpm.contract.validation.builder.MapBuilder.contractInputMap;\nimport static org.bonitasoft.engine.bpm.contract.validation.builder.SComplexInputDefinitionBuilder.aComplexInput;\nimport static org.bonitasoft.engine.bpm.contract.validation.builder.SSimpleInputDefinitionBuilder.aSimpleInput;\nimport static org.bonitasoft.engine.core.process.definition.model.SType.BOOLEAN;\nimport static org.bonitasoft.engine.core.process.definition.model.SType.DECIMAL;\nimport static org.bonitasoft.engine.core.process.definition.model.SType.TEXT;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.contract.FileInputValue;\nimport org.bonitasoft.engine.core.process.definition.model.SInputDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SType;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SInputDefinitionImpl;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class ContractTypeValidatorTest {\n\n    private ContractTypeValidator contractTypeValidator;\n    private ErrorReporter errorReporter;\n\n    @Before\n    public void setUp() {\n        contractTypeValidator = new ContractTypeValidator();\n        errorReporter = new ErrorReporter();\n    }\n\n    @Test\n    public void should_delegate_simple_type_validation_to_associated_enum() throws Exception {\n        final SInputDefinition definition = aSimpleInput(BOOLEAN).build();\n\n        contractTypeValidator.validate(definition, \"not a boolean\", errorReporter);\n\n        // then\n        assertThat(errorReporter.hasError()).isTrue();\n        assertThat(errorReporter.getErrors()).containsExactly(\"not a boolean cannot be assigned to BOOLEAN\");\n    }\n\n    @Test\n    public void should_decimal_validation_accept_integer() throws Exception {\n        //given\n        final SInputDefinition definition = aSimpleInput(DECIMAL).build();\n\n        //when\n        contractTypeValidator.validate(definition, 2, errorReporter);\n\n        // then\n        assertThat(errorReporter.hasError()).isFalse();\n    }\n\n    @Test\n    public void should_not_validate_non_map_object_for_complex_type() throws Exception {\n        final SInputDefinitionImpl definition = new SInputDefinitionImpl(\"a complex definition\", \"\");\n\n        contractTypeValidator.validate(definition, \"this is not a map\", errorReporter);\n\n        assertThat(errorReporter.hasError()).isTrue();\n    }\n\n    @Test\n    public void should_validate_map_object_for_complex_type() throws Exception {\n        //given\n        final SInputDefinitionImpl definition = new SInputDefinitionImpl(\"a complex definition\", \"description\", true,\n                Collections.<SInputDefinition> singletonList(\n                        new SInputDefinitionImpl(\"a simple multiple definition\", SType.TEXT, \"description\")));\n\n        definition.getInputDefinitions().add(\n                new SInputDefinitionImpl(\"a simple multiple definition\", SType.TEXT, \"description\"));\n\n        //when\n        contractTypeValidator.validate(definition, new ArrayList<String>(), errorReporter);\n\n        // then\n        assertThat(errorReporter.hasError()).isFalse();\n    }\n\n    @Test\n    public void should_validate_multiple_simple_type_empty_list() throws Exception {\n        //given\n        final SInputDefinitionImpl definition = new SInputDefinitionImpl(\"a simple multiple definition\", SType.TEXT,\n                \"description\", true);\n\n        //when\n        contractTypeValidator.validate(definition, new ArrayList<String>(), errorReporter);\n\n        // then\n        assertThat(errorReporter.hasError()).isFalse();\n\n    }\n\n    @Test\n    public void should_validate_multiple_complex_type_empty_list() throws Exception {\n        //given\n        final SInputDefinitionImpl definition = new SInputDefinitionImpl(\"a simple multiple definition\", SType.TEXT,\n                \"description\", true);\n\n        //when\n        contractTypeValidator.validate(definition, new ArrayList<String>(), errorReporter);\n\n        // then\n        assertThat(errorReporter.hasError()).isFalse();\n\n    }\n\n    @Test\n    public void should_validate_multiple_simple_type() throws Exception {\n        //given\n        final SInputDefinitionImpl definition = new SInputDefinitionImpl(\"a simple multiple definition\", SType.TEXT,\n                \"description\", true);\n\n        //when\n        contractTypeValidator.validate(definition, Arrays.asList(\"input1\", \"input2\"), errorReporter);\n\n        // then\n        assertThat(errorReporter.hasError()).isFalse();\n\n    }\n\n    @Test\n    public void should_validate_file_input() throws Exception {\n        //given\n        final SInputDefinitionImpl definition = new SInputDefinitionImpl(\"theFile\", SType.FILE, \"description\", false,\n                Collections.<SInputDefinition> singletonList(new SInputDefinitionImpl(\"\", \"\")));\n\n        //when\n        contractTypeValidator.validate(definition, new FileInputValue(\"theFile\", \"\", new byte[] { 0, 1 }),\n                errorReporter);\n\n        // then\n        assertThat(errorReporter.hasError()).isFalse();\n\n    }\n\n    @Test\n    public void should_not_validate_multiple_simple_type_with_bad_values() throws Exception {\n        //given\n        final SInputDefinitionImpl definition = new SInputDefinitionImpl(\"a simple multiple definition\", SType.DECIMAL,\n                \"description\", true);\n\n        //when\n        contractTypeValidator.validate(definition, Collections.singletonList(\"not a number\"), errorReporter);\n\n        assertThat(errorReporter.hasError()).isTrue();\n        assertThat(errorReporter.getErrors()).containsExactly(\"[not a number] cannot be assigned to multiple DECIMAL\");\n    }\n\n    @Test\n    public void should_not_validate_multiple_simple_type() throws Exception {\n        //given\n        final SInputDefinitionImpl definition = new SInputDefinitionImpl(\"a simple multiple definition\", SType.TEXT,\n                \"description\", true);\n\n        //when\n        contractTypeValidator.validate(definition, \"i am not a list\", errorReporter);\n\n        // then\n        assertThat(errorReporter.hasError()).isTrue();\n        assertThat(errorReporter.getErrors()).containsExactly(\"i am not a list cannot be assigned to multiple TEXT\");\n    }\n\n    @Test\n    public void should_not_validate_multiple_complex_type_when_no_list() throws Exception {\n        //given\n\n        final SInputDefinition simpleDefinition = new SInputDefinitionImpl(\"simpleInput\", SType.TEXT, \"description\");\n        final SInputDefinition complexDefinition = aComplexInput().withName(\"complexName\")\n                .withDescription(\"complex multiple input\").withMultiple(true)\n                .withInput(simpleDefinition).build();\n\n        //when\n        contractTypeValidator.validate(complexDefinition, \"i am not a list\", errorReporter);\n\n        // then\n        assertThat(errorReporter.hasError()).isTrue();\n        assertThat(errorReporter.getErrors())\n                .containsExactly(\"i am not a list cannot be assigned to multiple COMPLEX type\");\n    }\n\n    @Test\n    public void should_validate_multiple_complex_type() throws Exception {\n        //given\n        final SInputDefinition simpleDefinition = new SInputDefinitionImpl(\"simpleInput\", SType.TEXT, \"description\");\n        final SInputDefinition complexDefinition = aComplexInput().withName(\"complexName\")\n                .withDescription(\"complex multiple input\").withMultiple(true)\n                .withInput(simpleDefinition).build();\n\n        //when\n        final Map<String, Object> complexInput = new HashMap<>();\n        complexInput.put(\"simpleInput\", \"text value\");\n        final List<Map<String, Object>> complexList = new ArrayList<>();\n        complexList.add(complexInput);\n        complexList.add(complexInput);\n        complexList.add(complexInput);\n\n        //then no exception\n        contractTypeValidator.validate(complexDefinition, complexList, errorReporter);\n    }\n\n    @Test\n    public void should_validate_multiple_complex_with_multiple_complex_type() throws Exception {\n        //given\n        final SInputDefinition simpleDefinition = new SInputDefinitionImpl(\"simpleInput\", SType.INTEGER, \"description\");\n        final SInputDefinition complexListDefinition = aComplexInput().withName(\"complexInComplex\")\n                .withDescription(\"complex multiple input\")\n                .withMultiple(true)\n                .withInput(simpleDefinition).build();\n        final SInputDefinition complexDefinition = aComplexInput().withName(\"complexName\")\n                .withDescription(\"complex multiple input\")\n                .withInput(complexListDefinition).build();\n\n        //when\n        final Map<String, Object> complexInput = new HashMap<>();\n        complexInput.put(\"simpleInput\", 123);\n        final List<Map<String, Object>> complexList = new ArrayList<>();\n        complexList.add(complexInput);\n        complexList.add(complexInput);\n        complexList.add(complexInput);\n\n        final Map<String, Object> taskInput = new HashMap<>();\n        taskInput.put(\"complexInComplex\", complexList);\n\n        contractTypeValidator.validate(complexDefinition, taskInput, errorReporter);\n\n        // then\n        assertThat(errorReporter.hasError()).isFalse();\n\n    }\n\n    @Test\n    public void should_not_validate_complex_with_multiple_complex_type() throws Exception {\n        //given\n        final SInputDefinition simpleDefinition = new SInputDefinitionImpl(\"simpleInput\", SType.DATE, \"description\");\n        final SInputDefinition complexListDefinition = aComplexInput().withName(\"complexInComplex\")\n                .withDescription(\"complex multiple input\")\n                .withMultiple(true)\n                .withInput(simpleDefinition).build();\n        final SInputDefinition complexDefinition = aComplexInput().withName(\"complexName\")\n                .withDescription(\"complex multiple input\")\n                .withInput(complexListDefinition).withMultiple(true).build();\n\n        //when\n        final Map<String, Object> complexInput = new HashMap<>();\n        complexInput.put(\"simpleInput\", \"not a date\");\n        final List<Map<String, Object>> complexList = new ArrayList<>();\n        complexList.add(complexInput);\n        complexList.add(complexInput);\n        complexList.add(complexInput);\n\n        final Map<String, Object> taskInput = new HashMap<>();\n        taskInput.put(\"complexInComplex\", complexList);\n\n        contractTypeValidator.validate(complexDefinition, taskInput, errorReporter);\n\n        // then\n        assertThat(errorReporter.hasError()).isTrue();\n        assertThat(errorReporter.getErrors()).containsExactly(\n                \"{complexInComplex=[{simpleInput=not a date}, {simpleInput=not a date}, {simpleInput=not a date}]} cannot be assigned to multiple COMPLEX type\");\n\n    }\n\n    @Test\n    public void should_not_validate_complex_that_has_not_a_map_as_value() throws Exception {\n        //given\n        final SInputDefinition simpleDefinition = new SInputDefinitionImpl(\"simpleInput\", SType.TEXT, \"description\");\n        final SInputDefinition complexDefinition = aComplexInput().withName(\"complexInComplex\")\n                .withDescription(\"complex multiple input\")\n                .withInput(simpleDefinition).build();\n\n        //when\n        contractTypeValidator.validate(complexDefinition, \"not a complex\", errorReporter);\n\n        // then\n        assertThat(errorReporter.hasError()).isTrue();\n        assertThat(errorReporter.getErrors()).containsExactly(\"not a complex cannot be assigned to COMPLEX type\");\n\n    }\n\n    @Test\n    public void should_not_validate_multiple_complex_type() throws Exception {\n        //given\n        final SInputDefinition simpleDefinition = new SInputDefinitionImpl(\"simpleInput\", SType.INTEGER, \"description\");\n        final SInputDefinition complexDefinition = aComplexInput().withName(\"complexName\")\n                .withDescription(\"complex multiple input\").withMultiple(true)\n                .withInput(simpleDefinition).build();\n\n        //when\n        final List<Map<String, Object>> complexList = new ArrayList<>();\n        complexList.add(Collections.<String, Object> singletonMap(\"simpleInput\", \"aa\"));\n        complexList.add(Collections.<String, Object> singletonMap(\"simpleInput\", \"bb\"));\n        complexList.add(Collections.<String, Object> singletonMap(\"simpleInput\", \"cc\"));\n\n        contractTypeValidator.validate(complexDefinition, complexList, errorReporter);\n\n        // then\n        assertThat(errorReporter.hasError()).isTrue();\n        assertThat(errorReporter.getErrors()).containsExactly(\"aa cannot be assigned to INTEGER\",\n                \"bb cannot be assigned to INTEGER\",\n                \"cc cannot be assigned to INTEGER\");\n\n    }\n\n    @Test\n    public void should_validate_for_multiple_complex_type_with_missing_value_for_subType() throws Exception {\n        //given\n        final SInputDefinition complexDefinition = aComplexInput().withName(\"invoice\")\n                .withDescription(\"complex multiple input\")\n                .withInput(aComplexInput().withName(\"lines\").withDescription(\"my lines\").withMultiple(true)\n                        .withInput(aSimpleInput().withName(\"productName\").build()).build())\n                .build();\n\n        final List<Map<String, Object>> lines = new ArrayList<>();\n        lines.add(Collections.<String, Object> singletonMap(\"productName\", \"product value\"));\n        lines.add(null);\n        // JSON equivalent:\n        // {\n        //        \"invoice\": {\n        //            \"lines\": [\n        //            {\n        //                \"productName\": \"product value\"\n        //            },\n        //            null\n        //            ]\n        //        }\n        //    }\n\n        final Map<String, Object> inputs = Collections.<String, Object> singletonMap(\"lines\", lines);\n\n        //when\n        contractTypeValidator.validate(complexDefinition, inputs, errorReporter);\n\n        // then\n        assertThat(errorReporter.hasError()).isFalse();\n    }\n\n    @Test\n    public void should_long_validate_accept_integer() throws Exception {\n        //given\n        final SInputDefinition definition = aSimpleInput(SType.LONG).build();\n\n        //when\n        contractTypeValidator.validate(definition, 2, errorReporter);\n\n        // then\n        assertThat(errorReporter.hasError()).overridingErrorMessage(errorReporter.getErrors().toString()).isFalse();\n    }\n\n    @Test\n    public void should_validate_accept_long() throws Exception {\n        //given\n        final SInputDefinition definition = aSimpleInput(SType.LONG).build();\n\n        //when\n        contractTypeValidator.validate(definition, 10L, errorReporter);\n\n        // then\n        assertThat(errorReporter.hasError()).overridingErrorMessage(errorReporter.getErrors().toString()).isFalse();\n    }\n\n    @Test\n    public void should_validate_deep_complex_input_with_null_value_in_the_middle() throws Exception {\n        // given a three layer complex input def with a multiple on the third layer\n        SInputDefinition leafNameInputDef = aSimpleInput().withName(\"leafName\").withType(TEXT).build();\n        SInputDefinition nodeNameInputDef = aSimpleInput().withName(\"nodeName\").withType(TEXT).build();\n        SInputDefinition rootNameInputDef = aSimpleInput().withName(\"rootName\").withType(TEXT).build();\n        SInputDefinition leafInputDef = aComplexInput().withName(\"leaf\").withInput(leafNameInputDef).withMultiple(true)\n                .build();\n        SInputDefinition nodeInputDef = aComplexInput().withName(\"node\").withInput(nodeNameInputDef, leafInputDef)\n                .build();\n        SInputDefinition rootInputDef = aComplexInput().withName(\"root\").withInput(rootNameInputDef, nodeInputDef)\n                .build();\n\n        Serializable rootInputs = (Serializable) contractInputMap(\n                entry(\"rootName\", \"rootNameValue\"),\n                entry(\"node\", null));\n\n        // when validate an input with null value for the second layer\n        contractTypeValidator.validate(rootInputDef, rootInputs, errorReporter);\n\n        // then\n        assertThat(errorReporter.hasError()).overridingErrorMessage(errorReporter.getErrors().toString()).isFalse();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/bpm/contract/validation/ContractValidatorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.contract.validation;\n\nimport static java.util.Arrays.asList;\nimport static org.bonitasoft.engine.bpm.contract.validation.builder.MapBuilder.aMap;\nimport static org.bonitasoft.engine.bpm.contract.validation.builder.SContractDefinitionBuilder.aContract;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyLong;\nimport static org.mockito.Mockito.*;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.core.process.definition.model.SContractDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SContractViolationException;\nimport org.hamcrest.BaseMatcher;\nimport org.hamcrest.Description;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ContractValidatorTest {\n\n    private static final long PROCESS_DEFINITION_ID = 1245L;\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n    @Mock\n    private ContractStructureValidator structureValidator;\n    @Mock\n    private ContractConstraintsValidator constraintValidator;\n    @InjectMocks\n    private ContractValidator contractValidator;\n\n    private Map<String, Serializable> anyVariables() {\n        return anyMap();\n    }\n\n    @Test\n    public void should_not_validate_rules_if_structure_validation_fail() throws Exception {\n        final SContractDefinition contract = aContract().build();\n        final Map<String, Serializable> inputs = aMap().build();\n        doThrow(new SContractViolationException(\"bad structure\", new ArrayList<String>())).when(structureValidator)\n                .validate(contract, inputs);\n\n        expectedException.expect(SContractViolationException.class);\n        try {\n            contractValidator.validate(PROCESS_DEFINITION_ID, contract, inputs);\n        } finally {\n            verify(constraintValidator, never()).validate(anyLong(), any(SContractDefinition.class), anyVariables());\n        }\n\n    }\n\n    @Test\n    public void should_return_false_if_structure_validation_fail() throws Exception {\n        final SContractDefinition contract = aContract().build();\n        final Map<String, Serializable> variables = aMap().build();\n        doThrow(new SContractViolationException(\"bad structure\", new ArrayList<String>())).when(structureValidator)\n                .validate(contract, variables);\n\n        expectedException.expect(SContractViolationException.class);\n        contractValidator.validate(PROCESS_DEFINITION_ID, contract, variables);\n    }\n\n    @Test\n    public void should_populate_comments_with_validation_problems_when_structure_validation_fail() throws Exception {\n        final SContractDefinition contract = aContract().build();\n        final Map<String, Serializable> variables = aMap().build();\n        final List<String> problems = Arrays.asList(\"There is problems with structure\",\n                \"Might have issue with types too\");\n        doThrow(new SContractViolationException(\"bad structure\", problems)).when(structureValidator).validate(contract,\n                variables);\n\n        expectedException.expect(new ExceptionHavingExplanations(problems));\n        contractValidator.validate(PROCESS_DEFINITION_ID, contract, variables);\n    }\n\n    @Test\n    public void should_return_false_if_rule_validation_fail() throws Exception {\n        final SContractDefinition contract = aContract().build();\n        final Map<String, Serializable> variables = aMap().build();\n        doThrow(new SContractViolationException(\"rule failure\", new ArrayList<String>())).when(constraintValidator)\n                .validate(PROCESS_DEFINITION_ID, contract,\n                        variables);\n\n        expectedException.expect(SContractViolationException.class);\n        contractValidator.validate(PROCESS_DEFINITION_ID, contract, variables);\n    }\n\n    @Test\n    public void should_populate_comments_with_validation_problems_when_rule_validation_fail() throws Exception {\n        final SContractDefinition contract = aContract().build();\n        final Map<String, Serializable> variables = aMap().build();\n        final List<String> problems = asList(\"There is problems with a rule\", \"Might have issue with other rule too\");\n        doThrow(new SContractViolationException(\"rule failure\", problems)).when(constraintValidator)\n                .validate(PROCESS_DEFINITION_ID, contract, variables);\n\n        expectedException.expect(new ExceptionHavingExplanations(problems));\n        contractValidator.validate(PROCESS_DEFINITION_ID, contract, variables);\n    }\n\n    private static class ExceptionHavingExplanations extends BaseMatcher<Exception> {\n\n        private final List<String> problems;\n\n        public ExceptionHavingExplanations(List<String> problems) {\n            this.problems = problems;\n        }\n\n        @Override\n        public boolean matches(Object item) {\n            return (item instanceof SContractViolationException)\n                    && ((SContractViolationException) item).getExplanations().equals(problems);\n        }\n\n        @Override\n        public void describeTo(Description description) {\n\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/bpm/contract/validation/ContractVariableHelperTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.contract.validation;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.bpm.contract.validation.builder.MapBuilder.aMap;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.core.process.definition.model.SConstraintDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SConstraintDefinitionImpl;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class ContractVariableHelperTest {\n\n    private ContractVariableHelper contractVariableHelper;\n\n    @Before\n    public void before() {\n        contractVariableHelper = new ContractVariableHelper();\n    }\n\n    @Test\n    public void should_find_multiple_contract_variables() throws Exception {\n        final Map<String, Serializable> variables = buildMap();\n\n        final SConstraintDefinition constraintDefinition = getConstraintDefinitionWithName(\"nature\");\n\n        final List<Map<String, Serializable>> buildMandatoryMultipleInputVariables = contractVariableHelper\n                .buildMandatoryMultipleInputVariables(\n                        constraintDefinition, variables);\n        assertThat(buildMandatoryMultipleInputVariables)\n                .as(\"should not find variable for constraint \" + constraintDefinition.getName()).hasSize(8);\n\n    }\n\n    @Test\n    public void should_find_multiple_contract_variables_with_complex() throws Exception {\n        final Map<String, Serializable> variables = buildMapWithComplexAndSimpleMultiple();\n\n        final SConstraintDefinition constraintDefinition = getConstraintDefinitionWithName(\"nature\");\n\n        final List<Map<String, Serializable>> buildMandatoryMultipleInputVariables = contractVariableHelper\n                .buildMandatoryMultipleInputVariables(\n                        constraintDefinition, variables);\n        assertThat(buildMandatoryMultipleInputVariables)\n                .as(\"should not find variable for constraint \" + constraintDefinition.getName()).hasSize(8);\n\n    }\n\n    @Test\n    public void should_not_find_multiple_contract_variables() throws Exception {\n        final Map<String, Serializable> variables = buildMap();\n\n        final SConstraintDefinition constraintDefinition = getConstraintDefinitionWithName(\"not in payload\");\n\n        final List<Map<String, Serializable>> buildMandatoryMultipleInputVariables = contractVariableHelper\n                .buildMandatoryMultipleInputVariables(\n                        constraintDefinition, variables);\n        assertThat(buildMandatoryMultipleInputVariables)\n                .as(\"should not find variable for constraint \" + constraintDefinition.getName()).isEmpty();\n\n    }\n\n    @Test\n    public void should_convert_multiple_variable() throws Exception {\n        //given\n        final List<String> values = Arrays.asList(\"item1\", \"item2\", \"item3\");\n        final Map<String, Serializable> variable = aMap().put(\"key\", (Serializable) values).build();\n\n        //when\n        final List<Map<String, Serializable>> convertMultipleToList = contractVariableHelper\n                .convertMultipleToList(variable);\n\n        //then\n        assertThat(convertMultipleToList).as(\"should split multiple item\").hasSize(3);\n        for (int i = 0; i < convertMultipleToList.size(); i++) {\n            final Map<String, Serializable> map = convertMultipleToList.get(i);\n            assertThat(map).as(\"should contain key\").hasSize(1);\n            assertThat(map.get(\"key\").toString()).as(\"should contain item\" + i).isEqualTo(\"item\" + (i + 1));\n        }\n    }\n\n    @Test\n    public void should_not_convert_multiple_variable_and_return_empty_list() throws Exception {\n        //given\n        final Map<String, Serializable> variable = aMap().put(\"key\", \"not a list\").build();\n\n        //when\n        final List<Map<String, Serializable>> convertMultipleToList = contractVariableHelper\n                .convertMultipleToList(variable);\n\n        //then\n        assertThat(convertMultipleToList).as(\"should not split multiple item\").isNotNull().isEmpty();\n    }\n\n    private Map<String, Serializable> buildMap() {\n        final Map<String, Serializable> variables;\n        final Map<String, Serializable> user = aMap().put(\"firstName\", \"john\").put(\"lastName\", \"doe\").build();\n        final Map<String, Serializable> taxiExpenseLine = aMap().put(\"nature\", \"taxi\").put(\"amount\", 30)\n                .put(\"date\", \"2014-10-16\").put(\"comment\", \"slow\")\n                .build();\n        final Map<String, Serializable> hotelExpenseLine = aMap().put(\"nature\", \"hotel\").put(\"amount\", 1000)\n                .put(\"date\", \"2014-10-16\")\n                .put(\"comment\", \"expensive\")\n                .build();\n        final List<Map<String, Serializable>> expenseLines = new ArrayList<Map<String, Serializable>>();\n        expenseLines.add(taxiExpenseLine);\n        expenseLines.add(hotelExpenseLine);\n\n        final Map<String, Serializable> expenseReport = aMap()\n                .put(\"expenseReport\", (Serializable) expenseLines)\n                .build();\n\n        final List<Map<String, Serializable>> expenseReports = new ArrayList<Map<String, Serializable>>();\n        expenseReports.add(expenseReport);\n        expenseReports.add(expenseReport);\n        expenseReports.add(expenseReport);\n        expenseReports.add(expenseReport);\n\n        variables = aMap().put(\"user\", (Serializable) user)\n                .put(\"expenseReport\", (Serializable) expenseReports).build();\n        return variables;\n    }\n\n    private Map<String, Serializable> buildMapWithComplexAndSimpleMultiple() {\n        final Map<String, Serializable> map = new HashMap<>(buildMap());\n        map.put(\"comments\", new ArrayList<>(Arrays.asList(\"good trip\", \"hello\")));\n        //        map.put(\"hotel\",);\n        return map;\n    }\n\n    private SConstraintDefinition getConstraintDefinitionWithName(final String inputName) {\n        final SConstraintDefinition constraintDefinition = new SConstraintDefinitionImpl(inputName, \"fake\", \"fake\");\n        constraintDefinition.getInputNames().add(inputName);\n        return constraintDefinition;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/bpm/contract/validation/builder/MapBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.contract.validation.builder;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.assertj.core.data.MapEntry;\n\npublic class MapBuilder {\n\n    HashMap<String, Serializable> theMap = new HashMap<>();\n\n    public MapBuilder put(String k, Serializable v) {\n        theMap.put(k, v);\n        return this;\n    }\n\n    public static MapBuilder aMap() {\n        return new MapBuilder();\n    }\n\n    public Map<String, Serializable> build() {\n        return theMap;\n    }\n\n    public static Map<String, Serializable> contractInputMap(final MapEntry... entries) {\n        final Map<String, Serializable> result = new HashMap<>();\n        for (final MapEntry entry : entries) {\n            result.put((String) entry.key, (Serializable) entry.value);\n        }\n        return result;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/bpm/contract/validation/builder/SComplexInputDefinitionBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.contract.validation.builder;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.model.SInputDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SInputDefinitionImpl;\n\npublic class SComplexInputDefinitionBuilder {\n\n    private final List<SInputDefinition> inputDefinitions = new ArrayList<>();\n    private String name = \"aName\";\n    private String description = \"a description\";\n    private boolean multiple = false;\n\n    public static SComplexInputDefinitionBuilder aComplexInput() {\n        return new SComplexInputDefinitionBuilder();\n    }\n\n    public SInputDefinition build() {\n        return new SInputDefinitionImpl(name, description, multiple, inputDefinitions);\n    }\n\n    public SComplexInputDefinitionBuilder withInput(final SSimpleInputDefinitionBuilder... definitions) {\n        for (final SSimpleInputDefinitionBuilder definition : definitions) {\n            inputDefinitions.add(definition.build());\n        }\n        return this;\n    }\n\n    public SComplexInputDefinitionBuilder withInput(final SInputDefinition... definitions) {\n        Collections.addAll(inputDefinitions, definitions);\n        return this;\n    }\n\n    public SComplexInputDefinitionBuilder withName(final String name) {\n        this.name = name;\n        return this;\n    }\n\n    public SComplexInputDefinitionBuilder withDescription(final String description) {\n        this.description = description;\n        return this;\n    }\n\n    public SComplexInputDefinitionBuilder withMultiple(final boolean multiple) {\n        this.multiple = multiple;\n        return this;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/bpm/contract/validation/builder/SConstraintDefinitionBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.contract.validation.builder;\n\nimport static java.util.Arrays.asList;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.model.SConstraintDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SConstraintDefinitionImpl;\n\npublic class SConstraintDefinitionBuilder {\n\n    private final List<String> inputNames;\n    private String expression;\n    private String explanation;\n    private String name;\n\n    public SConstraintDefinitionBuilder(final String... inputNames) {\n        this.inputNames = asList(inputNames);\n    }\n\n    public static SConstraintDefinitionBuilder aRuleFor(final String... inputName) {\n        return new SConstraintDefinitionBuilder(inputName);\n    }\n\n    public SConstraintDefinitionBuilder expression(final String expression) {\n        this.expression = expression;\n        return this;\n    }\n\n    public SConstraintDefinitionBuilder explanation(final String explanation) {\n        this.explanation = explanation;\n        return this;\n    }\n\n    public SConstraintDefinitionBuilder name(final String name) {\n        this.name = name;\n        return this;\n    }\n\n    public SConstraintDefinition build() {\n        final SConstraintDefinitionImpl rule = new SConstraintDefinitionImpl(name, expression, explanation);\n        for (final String inputName : inputNames) {\n            rule.addInputName(inputName);\n        }\n        return rule;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/bpm/contract/validation/builder/SContractDefinitionBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.contract.validation.builder;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.model.SConstraintDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SContractDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SInputDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SContractDefinitionImpl;\n\npublic class SContractDefinitionBuilder {\n\n    private final List<SInputDefinition> inputDefinitions = new ArrayList<>();\n    private final List<SConstraintDefinition> constraints = new ArrayList<>();\n\n    public static SContractDefinitionBuilder aContract() {\n        return new SContractDefinitionBuilder();\n    }\n\n    public SContractDefinitionBuilder withInput(final SSimpleInputDefinitionBuilder builder) {\n        return withInput(builder.build());\n    }\n\n    public SContractDefinitionBuilder withInput(final SComplexInputDefinitionBuilder builder) {\n        return withInput(builder.build());\n    }\n\n    public SContractDefinitionBuilder withInput(final SInputDefinition input) {\n        inputDefinitions.add(input);\n        return this;\n    }\n\n    public SContractDefinitionBuilder withConstraint(final SConstraintDefinition constraint) {\n        constraints.add(constraint);\n        return this;\n    }\n\n    public SContractDefinition build() {\n        final SContractDefinitionImpl contract = new SContractDefinitionImpl();\n        for (final SInputDefinition input : inputDefinitions) {\n            contract.addInput(input);\n        }\n        for (final SConstraintDefinition constraint : constraints) {\n            contract.addConstraint(constraint);\n        }\n        return contract;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/bpm/contract/validation/builder/SSimpleInputDefinitionBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.contract.validation.builder;\n\nimport org.bonitasoft.engine.core.process.definition.model.SInputDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SType;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SInputDefinitionImpl;\n\npublic class SSimpleInputDefinitionBuilder {\n\n    private String name = \"aName\";\n    private String description = \"a description\";\n    private boolean multiple = false;\n    private SType type;\n\n    public SSimpleInputDefinitionBuilder(final SType type) {\n        this.type = type;\n    }\n\n    public static SSimpleInputDefinitionBuilder aSimpleInput(final SType type) {\n        return new SSimpleInputDefinitionBuilder(type);\n    }\n\n    public static SSimpleInputDefinitionBuilder aSimpleInput() {\n        return new SSimpleInputDefinitionBuilder(SType.TEXT);\n    }\n\n    public SInputDefinition build() {\n        return new SInputDefinitionImpl(name, type, description, multiple);\n    }\n\n    public SSimpleInputDefinitionBuilder withName(final String name) {\n        this.name = name;\n        return this;\n    }\n\n    public SSimpleInputDefinitionBuilder withDescription(final String description) {\n        this.description = description;\n        return this;\n    }\n\n    public SSimpleInputDefinitionBuilder withMultiple(final boolean multiple) {\n        this.multiple = multiple;\n        return this;\n    }\n\n    public SSimpleInputDefinitionBuilder withType(final SType type) {\n        this.type = type;\n        return this;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/bpm/model/impl/BPMInstancesCreatorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bpm.model.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.core.process.definition.model.SFlowNodeType.USER_TASK;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyLong;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.*;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.actor.mapping.model.SActor;\nimport org.bonitasoft.engine.core.connector.ConnectorInstanceService;\nimport org.bonitasoft.engine.core.operation.model.SLeftOperand;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.core.operation.model.SOperatorType;\nimport org.bonitasoft.engine.core.operation.model.impl.SLeftOperandImpl;\nimport org.bonitasoft.engine.core.operation.model.impl.SOperationImpl;\nimport org.bonitasoft.engine.core.process.definition.model.SConnectorDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.definition.model.SGatewayType;\nimport org.bonitasoft.engine.core.process.definition.model.SHumanTaskDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SGatewayDefinitionImpl;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.GatewayInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowElementsContainerType;\nimport org.bonitasoft.engine.core.process.instance.model.SGatewayInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.core.process.instance.model.STaskPriority;\nimport org.bonitasoft.engine.execution.state.FlowNodeStateManager;\nimport org.bonitasoft.engine.execution.state.InitializingActivityState;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class BPMInstancesCreatorTest {\n\n    @Mock\n    private ConnectorInstanceService connectorInstanceService;\n\n    @Mock\n    private GatewayInstanceService gatewayInstanceService;\n\n    @Mock\n    private ActivityInstanceService activityInstanceService;\n\n    @Mock\n    private FlowNodeStateManager flowNodeStateManager;\n\n    @Mock\n    ActorMappingService actorMappingService;\n\n    @Spy\n    @InjectMocks\n    private BPMInstancesCreator bpmInstancesCreator;\n\n    @Mock\n    private SHumanTaskDefinition sHumanTaskDefinition;\n\n    @Mock\n    private SActor actor;\n\n    @Before\n    public void setupClass() {\n        bpmInstancesCreator.setStateManager(flowNodeStateManager);\n    }\n\n    @Test\n    public void testExecutionOrder() throws Exception {\n        final PersistentObject container = mock(PersistentObject.class);\n        final List<SConnectorDefinition> connectors = getConnectorList();\n\n        bpmInstancesCreator.createConnectorInstances(container, connectors, SConnectorInstance.FLOWNODE_TYPE);\n\n        verify(bpmInstancesCreator).createConnectorInstanceObject(any(PersistentObject.class), anyString(),\n                any(SConnectorDefinition.class), eq(0));\n        verify(bpmInstancesCreator).createConnectorInstanceObject(any(PersistentObject.class), anyString(),\n                any(SConnectorDefinition.class), eq(1));\n    }\n\n    @Test\n    public void should_getOperationToSetData_return_the_operation_for_the_data() {\n        // given\n        SLeftOperandImpl leftOp1 = new SLeftOperandImpl();\n        leftOp1.setName(\"Plop1\");\n        leftOp1.setType(SLeftOperand.TYPE_DATA);\n        SOperationImpl op1 = new SOperationImpl();\n        op1.setLeftOperand(leftOp1);\n        op1.setType(SOperatorType.ASSIGNMENT);\n        SLeftOperandImpl leftOp2 = new SLeftOperandImpl();\n        leftOp2.setName(\"Plop2\");\n        leftOp2.setType(SLeftOperand.TYPE_DATA);\n        SOperationImpl op2 = new SOperationImpl();\n        op2.setType(SOperatorType.ASSIGNMENT);\n        op2.setLeftOperand(leftOp2);\n        // when\n        SOperation operationToSetData = bpmInstancesCreator.getOperationToSetData(\"Plop2\",\n                Arrays.<SOperation> asList(op1, op2));\n\n        // then\n        assertThat(operationToSetData).isEqualTo(op2);\n    }\n\n    private List<SConnectorDefinition> getConnectorList() {\n        final SConnectorDefinition connector1 = mock(SConnectorDefinition.class);\n        final SConnectorDefinition connector2 = mock(SConnectorDefinition.class);\n        final List<SConnectorDefinition> connectors = new ArrayList<>(2);\n        connectors.add(connector1);\n        connectors.add(connector2);\n        return connectors;\n    }\n\n    @Test\n    public void createManualTaskInstanceShouldSetReachedStateDateAndLastUpdateDate() throws Exception {\n        doReturn(mock(SManualTaskInstance.class)).when(activityInstanceService).getFlowNodeInstance(anyLong());\n        doReturn(new InitializingActivityState(null)).when(flowNodeStateManager)\n                .getFirstState(SFlowNodeType.MANUAL_TASK);\n\n        final SManualTaskInstance manualTaskInstance = bpmInstancesCreator.createManualTaskInstance(2345L, \"task\",\n                964854854L, \"disp\", 78L, \"desc\",\n                987968744446L, STaskPriority.HIGHEST);\n\n        assertThat(manualTaskInstance.getReachedStateDate()).isNotEqualTo(0L);\n        assertThat(manualTaskInstance.getLastUpdateDate()).isNotEqualTo(0L);\n    }\n\n    @Test\n    public void createFlownodeInstanceShouldSetReachedStateDateAndLastUpdateDate() throws Exception {\n        doReturn(new InitializingActivityState(null)).when(flowNodeStateManager)\n                .getFirstState(SFlowNodeType.GATEWAY);\n\n        final SGatewayInstance gatewayInstance = (SGatewayInstance) bpmInstancesCreator.createFlowNodeInstance(2345L,\n                999L, 888L,\n                SFlowElementsContainerType.FLOWNODE, new SGatewayDefinitionImpl(222L, \"myGate\", SGatewayType.EXCLUSIVE),\n                964854854L, 78L, true, 0,\n                SStateCategory.NORMAL, 987968744446L);\n\n        assertThat(gatewayInstance.getReachedStateDate()).isNotEqualTo(0L);\n        assertThat(gatewayInstance.getLastUpdateDate()).isNotEqualTo(0L);\n    }\n\n    @Test\n    public void create_user_task_should_not_evaluate_setExpectedEndDate_expression() throws Exception {\n        //given\n        doReturn(new InitializingActivityState(null)).when(flowNodeStateManager)\n                .getFirstState(SFlowNodeType.USER_TASK);\n        doReturn(USER_TASK).when(sHumanTaskDefinition).getType();\n        doReturn(\"actor\").when(sHumanTaskDefinition).getActorName();\n        doReturn(actor).when(actorMappingService).getActor(anyString(), anyLong());\n\n        ExpressionBuilder expressionBuilder = new ExpressionBuilder();\n        expressionBuilder.createConstantLongExpression(3600L);\n\n        //when\n        final SHumanTaskInstance sHumanTaskInstance = (SHumanTaskInstance) bpmInstancesCreator.createFlowNodeInstance(\n                2345L, 456L, 987L,\n                SFlowElementsContainerType.FLOWNODE,\n                sHumanTaskDefinition, 964854854L, 547, false, 0, SStateCategory.NORMAL, 0);\n\n        //then\n        assertThat(sHumanTaskInstance.getExpectedEndDate()).as(\"should not compute end date on creation\").isNull();\n\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/business/application/converter/ApplicationMenuToNodeConverterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.converter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.doReturn;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.model.SApplicationMenu;\nimport org.bonitasoft.engine.business.application.model.SApplicationPage;\nimport org.bonitasoft.engine.business.application.xml.ApplicationMenuNode;\nimport org.bonitasoft.engine.business.application.xml.ApplicationNode;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ApplicationMenuToNodeConverterTest {\n\n    @Mock\n    ApplicationService applicationService;\n\n    @InjectMocks\n    private ApplicationMenuToNodeConverter converter;\n\n    @Test(expected = IllegalArgumentException.class)\n    public void convertNullMenuShouldThrowIllegalArgument() throws Exception {\n        converter.toMenu(null);\n    }\n\n    @Test\n    public void convertMenuShouldConvertAllFields() throws Exception {\n        // given:\n        final Long applicationPageId = 888L;\n        final String displayName = \"Readable menu name for display\";\n        final String token = \"application-page-token\";\n        doReturn(new SApplicationPage(91L, applicationPageId, token)).when(applicationService)\n                .getApplicationPage(applicationPageId);\n\n        // when:\n        final ApplicationMenuNode convertedMenu = converter\n                .toMenu(new SApplicationMenu(displayName, 147L, applicationPageId, 14));\n\n        // then:\n        assertThat(convertedMenu.getApplicationPage()).isEqualTo(token);\n        assertThat(convertedMenu.getDisplayName()).isEqualTo(displayName);\n    }\n\n    @Test\n    public void addMenusToApplicationNodeShouldBeRecursive() throws Exception {\n        // given:\n        final long applicationId = 333L;\n        final Long parentMenuId = null;\n        final Long applicationPageId1 = 44L;\n\n        final String displayName1 = \"HR\";\n        final SApplicationMenu sApplicationMenu1 = new SApplicationMenu(displayName1, applicationId, applicationPageId1,\n                1);\n        final List<SApplicationMenu> level1Menus = new ArrayList<SApplicationMenu>();\n        level1Menus.add(sApplicationMenu1);\n\n        final List<SApplicationMenu> level2Menus = new ArrayList<SApplicationMenu>();\n        final String displayName11 = \"Legal HR procedures\";\n        final SApplicationMenu sApplicationMenu11 = new SApplicationMenu(displayName11, applicationId, null, 1);\n        level2Menus.add(sApplicationMenu11);\n        final String displayName12 = \"HR collective agreement\";\n        final Long applicationPageId12 = 577L;\n        final SApplicationMenu sApplicationMenu12 = new SApplicationMenu(displayName12, applicationId,\n                applicationPageId12, 2);\n        level2Menus.add(sApplicationMenu12);\n\n        given(applicationService.searchApplicationMenus(any(QueryOptions.class))).willReturn(level1Menus)\n                .willReturn(level2Menus)\n                .willReturn(Collections.<SApplicationMenu> emptyList());\n\n        final String token1 = \"mytoken-level-1\";\n        given(applicationService.getApplicationPage(applicationPageId1))\n                .willReturn(new SApplicationPage(applicationId, applicationPageId1, token1));\n        final String token12 = \"mytoken-level-12\";\n        given(applicationService.getApplicationPage(applicationPageId12))\n                .willReturn(new SApplicationPage(applicationId, applicationPageId12, token12));\n\n        // when:\n        final ApplicationNode node = new ApplicationNode();\n        converter.addMenusToApplicationNode(applicationId, parentMenuId, node, null);\n\n        // then:\n        assertThat(node.getApplicationMenus().size()).isEqualTo(1);\n        final ApplicationMenuNode menuNode1 = node.getApplicationMenus().get(0);\n        assertThat(menuNode1.getApplicationPage()).isEqualTo(token1);\n        assertThat(menuNode1.getDisplayName()).isEqualTo(displayName1);\n        assertThat(menuNode1.getApplicationMenus().size()).isEqualTo(2);\n        final ApplicationMenuNode menuNode11 = menuNode1.getApplicationMenus().get(0);\n        assertThat(menuNode11.getApplicationPage()).isNull(); // no page linked to that menu\n        assertThat(menuNode11.getDisplayName()).isEqualTo(displayName11);\n        final ApplicationMenuNode menuNode12 = menuNode1.getApplicationMenus().get(1);\n        assertThat(menuNode12.getApplicationPage()).isEqualTo(token12);\n        assertThat(menuNode12.getDisplayName()).isEqualTo(displayName12);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/business/application/converter/ApplicationPageToNodeConverterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.converter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.mock;\n\nimport org.bonitasoft.engine.business.application.model.SApplicationPage;\nimport org.bonitasoft.engine.business.application.xml.ApplicationPageNode;\nimport org.bonitasoft.engine.page.PageService;\nimport org.bonitasoft.engine.page.SPage;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ApplicationPageToNodeConverterTest {\n\n    @Mock\n    PageService pageService;\n\n    @InjectMocks\n    private ApplicationPageToNodeConverter converter;\n\n    @Test(expected = IllegalArgumentException.class)\n    public void convertNullPageShouldThrowIllegalArgument() throws Exception {\n        converter.toPage(null);\n    }\n\n    @Test\n    public void convertPageShouldConvertAllFields() throws Exception {\n        // given:\n        final String customPage = \"customPage\";\n        final String token = \"tekken\";\n        final long applicationId = 38L;\n        final long pageId = 154L;\n        final ApplicationPageNode node = new ApplicationPageNode();\n        node.setCustomPage(customPage);\n        node.setToken(token);\n        final SPage sPage = mock(SPage.class);\n        doReturn(sPage).when(pageService).getPage(pageId);\n        doReturn(customPage).when(sPage).getName();\n\n        // when:\n        final ApplicationPageNode convertedPage = converter.toPage(new SApplicationPage(applicationId, pageId, token));\n\n        // then:\n        assertThat(convertedPage.getCustomPage()).isEqualTo(customPage);\n        assertThat(convertedPage.getToken()).isEqualTo(token);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/business/application/converter/ApplicationToNodeConverterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.converter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.ArgumentMatchers.isNull;\nimport static org.mockito.ArgumentMatchers.nullable;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.*;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.model.SApplication;\nimport org.bonitasoft.engine.business.application.model.SApplicationPage;\nimport org.bonitasoft.engine.business.application.xml.AbstractApplicationNode;\nimport org.bonitasoft.engine.business.application.xml.ApplicationLinkNode;\nimport org.bonitasoft.engine.business.application.xml.ApplicationMenuNode;\nimport org.bonitasoft.engine.business.application.xml.ApplicationNode;\nimport org.bonitasoft.engine.business.application.xml.ApplicationPageNode;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.exception.ExportException;\nimport org.bonitasoft.engine.page.PageService;\nimport org.bonitasoft.engine.page.SPage;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.profile.ProfileService;\nimport org.bonitasoft.engine.profile.exception.profile.SProfileNotFoundException;\nimport org.bonitasoft.engine.profile.model.SProfile;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ApplicationToNodeConverterTest {\n\n    @Mock\n    private ProfileService profileService;\n    @Mock\n    private ApplicationService applicationService;\n    @Mock\n    private ApplicationPageToNodeConverter pageConverter;\n    @Mock\n    private ApplicationMenuToNodeConverter menuConverter;\n    @Mock\n    private PageService pageService;\n    @Mock\n    private SPage defaultLayout;\n    @Mock\n    private SPage defaultTheme;\n\n    @InjectMocks\n    private ApplicationToNodeConverter converter;\n\n    @Test\n    public void toNode_should_return_application_link() throws Exception {\n        //given\n        final SApplication application = new SApplication(\"app\", \"my app\", \"1.0\", new Date().getTime(), 10L,\n                \"enabled\");\n        application.setLink(true);\n\n        //when\n        final AbstractApplicationNode applicationNode = converter.toNode(application);\n\n        //then\n        assertThat(applicationNode).isNotNull();\n        assertThat(applicationNode).isInstanceOf(ApplicationLinkNode.class);\n\n    }\n\n    @Test\n    public void toNode_should_return_convert_all_string_fields() throws Exception {\n        //given\n        long createdBy = 11L;\n        final SApplication application = new SApplication(\"app\", \"my app\", \"1.0\", new Date().getTime(), createdBy,\n                \"enabled\");\n        application.setDescription(\"this is my app\");\n        application.setIconPath(\"/icon.jpg\");\n\n        //when\n        final ApplicationNode applicationNode = (ApplicationNode) converter.toNode(application);\n\n        //then\n        assertThat(applicationNode).isNotNull();\n        assertThat(applicationNode.getToken()).isEqualTo(\"app\");\n        assertThat(applicationNode.getDisplayName()).isEqualTo(\"my app\");\n        assertThat(applicationNode.getVersion()).isEqualTo(\"1.0\");\n        assertThat(applicationNode.getDescription()).isEqualTo(\"this is my app\");\n        assertThat(applicationNode.getIconPath()).isEqualTo(\"/icon.jpg\");\n        assertThat(applicationNode.getState()).isEqualTo(\"enabled\");\n        assertThat(applicationNode.getProfile()).isNull();\n        assertThat(applicationNode.getHomePage()).isNull();\n\n    }\n\n    @Test\n    public void toNode_should_replace_profile_id_by_profile_name() throws Exception {\n        //given\n        final SApplication application = new SApplication(\"app\", \"my app\", \"1.0\", new Date().getTime(), 10, \"enabled\");\n        application.setProfileId(7L);\n        final SProfile profile = mock(SProfile.class);\n        given(profile.getName()).willReturn(\"admin\");\n\n        given(profileService.getProfile(7L)).willReturn(profile);\n\n        //when\n        final ApplicationNode applicationNode = (ApplicationNode) converter.toNode(application);\n\n        //then\n        assertThat(applicationNode).isNotNull();\n        assertThat(applicationNode.getProfile()).isEqualTo(\"admin\");\n    }\n\n    @Test(expected = ExportException.class)\n    public void toNode_should_throw_ExportException_when_profileService_throws_exception() throws Exception {\n        //given\n        final SApplication application = new SApplication(\"app\", \"my app\", \"1.0\", new Date().getTime(), 10L, \"enabled\");\n        application.setProfileId(7L);\n\n        given(profileService.getProfile(7L)).willThrow(new SProfileNotFoundException(\"\"));\n\n        //when\n        converter.toNode(application);\n\n        //then exception\n    }\n\n    @Test\n    public void toNode_should_replaceHomePageId_by_application_page_token() throws Exception {\n        //given\n        final SApplication application = new SApplication(\"app\", \"my app\", \"1.0\", new Date().getTime(), 10L, \"enabled\");\n        application.setHomePageId(8L);\n        final SApplicationPage homePage = mock(SApplicationPage.class);\n        given(homePage.getToken()).willReturn(\"home\");\n\n        given(applicationService.getApplicationPage(8L)).willReturn(homePage);\n\n        //when\n        final ApplicationNode applicationNode = (ApplicationNode) converter.toNode(application);\n\n        //then\n        assertThat(applicationNode).isNotNull();\n        assertThat(applicationNode.getHomePage()).isEqualTo(\"home\");\n    }\n\n    @Test(expected = ExportException.class)\n    public void toNode_should_throw_ExportException_when_applicationService_throws_exception() throws Exception {\n        //given\n        final SApplication application = new SApplication(\"app\", \"my app\", \"1.0\", new Date().getTime(), 10L, \"enabled\");\n        application.setHomePageId(8L);\n\n        given(applicationService.getApplicationPage(8L)).willThrow(new SObjectNotFoundException());\n\n        //when\n        converter.toNode(application);\n\n        //then exception\n    }\n\n    @Test\n    public void toNode_should_replaceLayoutId_by_page_name() throws Exception {\n        //given\n        final SApplication application = new SApplication(\"app\", \"my app\", \"1.0\", new Date().getTime(), 10L, \"enabled\");\n        application.setLayoutId(9L);\n        final SPage layout = mock(SPage.class);\n        given(layout.getName()).willReturn(\"mainLayout\");\n\n        given(pageService.getPage(9L)).willReturn(layout);\n\n        //when\n        final ApplicationNode applicationNode = (ApplicationNode) converter.toNode(application);\n\n        //then\n        assertThat(applicationNode).isNotNull();\n        assertThat(applicationNode.getLayout()).isEqualTo(\"mainLayout\");\n    }\n\n    @Test\n    public void toNode_should_replaceThemeId_by_page_name() throws Exception {\n        //given\n        final SApplication application = new SApplication(\"app\", \"my app\", \"1.0\", new Date().getTime(), 10L, \"enabled\");\n        application.setThemeId(20L);\n        final SPage layout = mock(SPage.class);\n        given(layout.getName()).willReturn(\"mainTheme\");\n\n        given(pageService.getPage(20L)).willReturn(layout);\n\n        //when\n        final ApplicationNode applicationNode = (ApplicationNode) converter.toNode(application);\n\n        //then\n        assertThat(applicationNode).isNotNull();\n        assertThat(applicationNode.getTheme()).isEqualTo(\"mainTheme\");\n    }\n\n    @Test(expected = ExportException.class)\n    public void toNodeShouldThrowExceptionAtMenuConversion() throws Exception {\n        //given\n        final SApplication application = new SApplication(\"app\", \"my app\", \"1.0\", new Date().getTime(), 10L, \"enabled\");\n\n        doThrow(new SBonitaReadException(\"\")).when(menuConverter).addMenusToApplicationNode(nullable(Long.class),\n                nullable(Long.class), nullable(ApplicationNode.class),\n                nullable(ApplicationMenuNode.class));\n\n        converter.toNode(application);\n    }\n\n    @Test\n    public void toNodeShouldAddConvertedMenus() throws Exception {\n        //given\n        final long applicationId = 1191L;\n        final SApplication application = new SApplication(\"app\", \"my app\", \"1.0\", new Date().getTime(), 10L, \"enabled\");\n        application.setId(applicationId);\n\n        doNothing().when(menuConverter).addMenusToApplicationNode(eq(applicationId), isNull(), any(), isNull());\n\n        //when\n        converter.toNode(application);\n\n        //then\n        verify(menuConverter).addMenusToApplicationNode(eq(applicationId), isNull(), any(), isNull());\n    }\n\n    @Test(expected = ExportException.class)\n    public void toNodeShouldAddThrowExceptionAtPageConversion() throws Exception {\n        //given\n        final SApplication application = new SApplication(\"app\", \"my app\", \"1.0\", new Date().getTime(), 10L, \"enabled\");\n\n        given(applicationService.searchApplicationPages(any(QueryOptions.class)))\n                .willThrow(new SBonitaReadException(\"\"));\n\n        converter.toNode(application);\n    }\n\n    @Test\n    public void toNodeShouldAddConvertedPages() throws Exception {\n        //given\n        final SApplication application = new SApplication(\"app\", \"my app\", \"1.0\", new Date().getTime(), 10L, \"enabled\");\n        final List<SApplicationPage> pages = new ArrayList<>(1);\n        final SApplicationPage page = mock(SApplicationPage.class);\n        pages.add(page);\n\n        final ApplicationPageNode pageNode = mock(ApplicationPageNode.class);\n        given(pageConverter.toPage(page)).willReturn(pageNode);\n\n        given(applicationService.searchApplicationPages(any(QueryOptions.class))).willReturn(pages)\n                .willReturn(Collections.emptyList());\n\n        //when\n        final ApplicationNode applicationNode = (ApplicationNode) converter.toNode(application);\n\n        //then\n        assertThat(applicationNode.getApplicationPages().size()).isEqualTo(1);\n        assertThat(applicationNode.getApplicationPages().get(0)).isEqualTo(pageNode);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/business/application/converter/NodeToApplicationConverterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.converter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.bonitasoft.engine.business.application.InternalProfiles.INTERNAL_PROFILE_ALL;\nimport static org.bonitasoft.engine.business.application.InternalProfiles.INTERNAL_PROFILE_SUPER_ADMIN;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.doThrow;\nimport static org.mockito.Mockito.mock;\n\nimport java.nio.charset.StandardCharsets;\n\nimport org.bonitasoft.engine.api.ImportError;\nimport org.bonitasoft.engine.api.ImportStatus;\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.importer.ImportResult;\nimport org.bonitasoft.engine.business.application.importer.validator.ApplicationImportValidator;\nimport org.bonitasoft.engine.business.application.model.SApplicationWithIcon;\nimport org.bonitasoft.engine.business.application.xml.AbstractApplicationNode;\nimport org.bonitasoft.engine.business.application.xml.ApplicationLinkNode;\nimport org.bonitasoft.engine.business.application.xml.ApplicationNode;\nimport org.bonitasoft.engine.exception.ImportException;\nimport org.bonitasoft.engine.page.PageService;\nimport org.bonitasoft.engine.page.SPage;\nimport org.bonitasoft.engine.profile.ProfileService;\nimport org.bonitasoft.engine.profile.exception.profile.SProfileNotFoundException;\nimport org.bonitasoft.engine.profile.model.SProfile;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class NodeToApplicationConverterTest {\n\n    @Mock\n    private ProfileService profileService;\n\n    @Mock\n    private PageService pageService;\n\n    @Mock\n    private SPage defaultLayout;\n\n    @Mock\n    private SPage defaultTheme;\n\n    public static final long DEFAULT_LAYOUT_ID = 101;\n\n    public static final long DEFAULT_THEME_ID = 102;\n\n    private static final String ICON_MIME_TYPE = \"iconMimeType\";\n\n    private static final byte[] ICON_CONTENT = \"iconContent\".getBytes(StandardCharsets.UTF_8);\n\n    @Mock\n    ApplicationImportValidator validator;\n\n    @InjectMocks\n    private NodeToApplicationConverter converter;\n\n    @Before\n    public void setUp() throws Exception {\n        given(defaultLayout.getId()).willReturn(DEFAULT_LAYOUT_ID);\n        given(pageService.getPageByName(ApplicationService.DEFAULT_LAYOUT_NAME)).willReturn(defaultLayout);\n\n        given(defaultTheme.getId()).willReturn(DEFAULT_THEME_ID);\n        given(pageService.getPageByName(ApplicationService.DEFAULT_THEME_NAME)).willReturn(defaultTheme);\n    }\n\n    private ImportResult convertToSApplication(final AbstractApplicationNode applicationNode, final long createdBy)\n            throws Exception {\n        return converter.toSApplication(applicationNode, null, null, createdBy, true);\n    }\n\n    @Test\n    public void toSApplication_should_set_internalField_when_applicable() throws Exception {\n        //given\n        final ApplicationNode node1 = new ApplicationNode();\n        final ApplicationNode node2 = new ApplicationNode();\n        node1.setProfile(INTERNAL_PROFILE_ALL.getProfileName());\n        node2.setProfile(INTERNAL_PROFILE_SUPER_ADMIN.getProfileName());\n        long createdBy = 1L;\n\n        //when\n        final ImportResult importResult1 = convertToSApplication(node1, createdBy);\n        final ImportResult importResult2 = convertToSApplication(node2, createdBy);\n\n        final SApplicationWithIcon application1 = importResult1.getApplication();\n        final SApplicationWithIcon application2 = importResult2.getApplication();\n\n        assertThat(application1.getInternalProfile()).isEqualTo(INTERNAL_PROFILE_ALL.getProfileName());\n        assertThat(application2.getInternalProfile()).isEqualTo(INTERNAL_PROFILE_SUPER_ADMIN.getProfileName());\n        assertThat(application1.getProfileId()).isNull();\n        assertThat(application2.getProfileId()).isNull();\n    }\n\n    @Test\n    public void toSApplication_should_return_ImportResult_with_no_errors_and_application_with_all_fields_except_home_page()\n            throws Exception {\n        //given\n        final ApplicationNode node = new ApplicationNode();\n        node.setDisplayName(\"My app\");\n        node.setDescription(\"This is my app\");\n        node.setHomePage(\"home\");\n        node.setVersion(\"1.0\");\n        node.setToken(\"app\");\n        node.setIconPath(\"/icon.jpg\");\n        node.setProfile(\"admin\");\n        node.setState(\"ENABLED\");\n\n        long profileId = 8L;\n        final SProfile profile = mock(SProfile.class);\n        given(profile.getId()).willReturn(profileId);\n        given(profileService.getProfileByName(\"admin\")).willReturn(profile);\n        final long before = System.currentTimeMillis();\n\n        //when\n        long createdBy = 1L;\n        boolean editable = false;\n        final ImportResult importResult = converter.toSApplication(node, ICON_CONTENT, ICON_MIME_TYPE, createdBy,\n                editable);\n\n        //then\n        assertThat(importResult).isNotNull();\n\n        final SApplicationWithIcon application = importResult.getApplication();\n        assertThat(application.getDisplayName()).isEqualTo(\"My app\");\n        assertThat(application.getDescription()).isEqualTo(\"This is my app\");\n        assertThat(application.getHomePageId()).isNull();\n        assertThat(application.getVersion()).isEqualTo(\"1.0\");\n        assertThat(application.getToken()).isEqualTo(\"app\");\n        assertThat(application.getIconPath()).isEqualTo(\"/icon.jpg\");\n        assertThat(application.getProfileId()).isEqualTo(profileId);\n        assertThat(application.getState()).isEqualTo(\"ENABLED\");\n        assertThat(application.getCreatedBy()).isEqualTo(createdBy);\n        assertThat(application.getIconContent()).isEqualTo(ICON_CONTENT);\n        assertThat(application.getIconMimeType()).isEqualTo(ICON_MIME_TYPE);\n        assertThat(application.getInternalProfile()).isNull();\n        assertThat(application.isEditable()).isEqualTo(editable);\n        assertThat(application.isLink()).isFalse();\n\n        // also check auto fields\n        final long after = System.currentTimeMillis();\n        assertThat(application.getUpdatedBy()).isEqualTo(createdBy);\n        assertThat(application.getCreationDate()).isBetween(before, after);\n        assertThat(application.getLastUpdateDate()).isEqualTo(application.getCreationDate());\n\n        final ImportStatus importStatus = importResult.getImportStatus();\n        assertThat(importStatus.getName()).isEqualTo(\"app\");\n        assertThat(importStatus.getStatus()).isEqualTo(ImportStatus.Status.ADDED);\n        assertThat(importStatus.getErrors()).isEmpty();\n    }\n\n    @Test\n    public void toSApplication_should_return_ImportResult_with_no_errors_and_application_link_with_correct_fields()\n            throws Exception {\n        //given\n        final ApplicationLinkNode node = new ApplicationLinkNode();\n        node.setDisplayName(\"My app\");\n        node.setDescription(\"This is my app\");\n        node.setVersion(\"1.0\");\n        node.setToken(\"app\");\n        node.setIconPath(\"/icon.jpg\");\n        node.setProfile(\"admin\");\n        node.setState(\"ENABLED\");\n\n        long profileId = 8L;\n        final SProfile profile = mock(SProfile.class);\n        given(profile.getId()).willReturn(profileId);\n        given(profileService.getProfileByName(\"admin\")).willReturn(profile);\n\n        //when\n        long createdBy = 1L;\n        final ImportResult importResult = convertToSApplication(node, createdBy);\n\n        //then\n        assertThat(importResult).isNotNull();\n\n        final SApplicationWithIcon application = importResult.getApplication();\n        assertThat(application.getDisplayName()).isEqualTo(\"My app\");\n        assertThat(application.getDescription()).isEqualTo(\"This is my app\");\n        assertThat(application.getHomePageId()).isNull();\n        assertThat(application.getVersion()).isEqualTo(\"1.0\");\n        assertThat(application.getToken()).isEqualTo(\"app\");\n        assertThat(application.getIconPath()).isEqualTo(\"/icon.jpg\");\n        assertThat(application.getProfileId()).isEqualTo(profileId);\n        assertThat(application.getState()).isEqualTo(\"ENABLED\");\n        assertThat(application.getCreatedBy()).isEqualTo(createdBy);\n        assertThat(application.getIconContent()).isNull();\n        assertThat(application.getIconMimeType()).isNull();\n        assertThat(application.getInternalProfile()).isNull();\n        assertThat(application.isEditable()).isTrue();\n        assertThat(application.isLink()).isTrue();\n\n        final ImportStatus importStatus = importResult.getImportStatus();\n        assertThat(importStatus.getName()).isEqualTo(\"app\");\n        assertThat(importStatus.getStatus()).isEqualTo(ImportStatus.Status.ADDED);\n        assertThat(importStatus.getErrors()).isEmpty();\n    }\n\n    @Test\n    public void toSApplication_should_use_layout_defined_in_ApplicationNode() throws Exception {\n        //given\n        String layoutName = \"custompage_mainLayout\";\n        final ApplicationNode node = new ApplicationNode();\n        node.setToken(\"app\");\n        node.setLayout(layoutName);\n\n        long layoutId = 15L;\n        SPage layout = buildMockPage(layoutId);\n        given(pageService.getPageByName(layoutName)).willReturn(layout);\n\n        //when\n        long createdBy = 1L;\n        final ImportResult importResult = convertToSApplication(node, createdBy);\n\n        //then\n        assertThat(importResult).isNotNull();\n\n        final SApplicationWithIcon application = importResult.getApplication();\n        assertThat(application.getLayoutId()).isEqualTo(layoutId);\n\n        final ImportStatus importStatus = importResult.getImportStatus();\n        assertThat(importStatus.getName()).isEqualTo(\"app\");\n        assertThat(importStatus.getStatus()).isEqualTo(ImportStatus.Status.ADDED);\n        assertThat(importStatus.getErrors()).isEmpty();\n    }\n\n    private SPage buildMockPage(final long layoutId) {\n        SPage layout = mock(SPage.class);\n        given(layout.getId()).willReturn(layoutId);\n        return layout;\n    }\n\n    @Test\n    public void toSApplication_should_use_default_layout_when_layout_is_not_defined_in_ApplicationNode()\n            throws Exception {\n        //given\n        final ApplicationNode node = new ApplicationNode();\n        node.setToken(\"app\");\n\n        //when\n        long createdBy = 1L;\n        final ImportResult importResult = convertToSApplication(node, createdBy);\n\n        //then\n        assertThat(importResult).isNotNull();\n\n        final SApplicationWithIcon application = importResult.getApplication();\n        assertThat(application.getLayoutId()).isEqualTo(DEFAULT_LAYOUT_ID);\n\n        final ImportStatus importStatus = importResult.getImportStatus();\n        assertThat(importStatus.getName()).isEqualTo(\"app\");\n        assertThat(importStatus.getStatus()).isEqualTo(ImportStatus.Status.ADDED);\n        assertThat(importStatus.getErrors()).isEmpty();\n    }\n\n    @Test\n    public void toSApplication_should_throw_importException_when_neither_specified_layout_neither_default_layout_is_found()\n            throws Exception {\n        //given\n        final ApplicationNode node = new ApplicationNode();\n        String notAvailableLayout = \"notAvailableLayout\";\n        node.setLayout(notAvailableLayout);\n        String token = \"app\";\n        node.setToken(token);\n\n        given(pageService.getPageByName(notAvailableLayout)).willReturn(null);\n\n        //when - then exception\n        assertThatExceptionOfType(ImportException.class).isThrownBy(() -> convertToSApplication(node, 1L))\n                .withMessage(\"Unable to import application with token '%s' because the layout '%s' was not found.\",\n                        token, notAvailableLayout);\n    }\n\n    @Test\n    public void toSApplication_should_use_theme_defined_in_ApplicationNode() throws Exception {\n        //given\n        String themeName = \"custompage_mainTheme\";\n        final ApplicationNode node = new ApplicationNode();\n        node.setToken(\"app\");\n        node.setTheme(themeName);\n\n        long themeId = 15L;\n        SPage theme = buildMockPage(themeId);\n        given(pageService.getPageByName(themeName)).willReturn(theme);\n\n        //when\n        long createdBy = 1L;\n        final ImportResult importResult = convertToSApplication(node, createdBy);\n\n        //then\n        assertThat(importResult).isNotNull();\n\n        final SApplicationWithIcon application = importResult.getApplication();\n        assertThat(application.getThemeId()).isEqualTo(themeId);\n\n        final ImportStatus importStatus = importResult.getImportStatus();\n        assertThat(importStatus.getName()).isEqualTo(\"app\");\n        assertThat(importStatus.getStatus()).isEqualTo(ImportStatus.Status.ADDED);\n        assertThat(importStatus.getErrors()).isEmpty();\n    }\n\n    @Test\n    public void toSApplication_should_use_default_theme_when_layout_is_not_defined_in_ApplicationNode()\n            throws Exception {\n        //given\n        final ApplicationNode node = new ApplicationNode();\n        node.setToken(\"app\");\n\n        long createdBy = 1L;\n        final ImportResult importResult = convertToSApplication(node, createdBy);\n\n        //then\n        assertThat(importResult).isNotNull();\n\n        final SApplicationWithIcon application = importResult.getApplication();\n        assertThat(application.getThemeId()).isEqualTo(DEFAULT_THEME_ID);\n\n        final ImportStatus importStatus = importResult.getImportStatus();\n        assertThat(importStatus.getName()).isEqualTo(\"app\");\n        assertThat(importStatus.getStatus()).isEqualTo(ImportStatus.Status.ADDED);\n        assertThat(importStatus.getErrors()).isEmpty();\n    }\n\n    @Test\n    public void toSApplication_should_throw_ImportException_when_neither_specified_theme_neither_default_theme_is_found()\n            throws Exception {\n        //given\n        final ApplicationNode node = new ApplicationNode();\n        node.setTheme(\"notAvailable\");\n        node.setToken(\"app\");\n\n        given(pageService.getPageByName(\"notAvailable\")).willReturn(null);\n\n        //when - then exception\n        assertThatExceptionOfType(ImportException.class).isThrownBy(() -> convertToSApplication(node, 1L))\n                .withMessage(\"Unable to import application with token '%s' because the theme '%s' was not found.\",\n                        \"app\", \"notAvailable\");\n    }\n\n    @Test\n    public void toSApplication_should_return_application_with_null_profile_id_when_node_has_no_profile()\n            throws Exception {\n        //given\n        final ApplicationNode node = new ApplicationNode();\n        node.setProfile(null);\n        node.setToken(\"TokenName\"); // token can never be null in the XML\n\n        //when\n        final ImportResult importResult = convertToSApplication(node, 1L);\n\n        //then\n        assertThat(importResult).isNotNull();\n        assertThat(importResult.getApplication().getProfileId()).isNull();\n    }\n\n    @Test\n    public void toSApplication_should_return_Import_result_with_errors_when_profile_is_not_found() throws Exception {\n        //given\n        final ApplicationNode node = new ApplicationNode();\n        node.setProfile(\"admin\");\n        node.setVersion(\"1.0\");\n        node.setToken(\"app\");\n        node.setState(\"ENABLED\");\n\n        given(profileService.getProfileByName(\"admin\")).willThrow(new SProfileNotFoundException(\"\"));\n\n        //when\n        final ImportResult importResult = convertToSApplication(node, 1L);\n\n        //then\n        assertThat(importResult.getApplication().getProfileId()).isNull();\n\n        final ImportStatus importStatus = importResult.getImportStatus();\n        assertThat(importStatus.getName()).isEqualTo(\"app\");\n        assertThat(importStatus.getStatus()).isEqualTo(ImportStatus.Status.ADDED);\n        assertThat(importStatus.getErrors()).containsExactly(new ImportError(\"admin\", ImportError.Type.PROFILE));\n    }\n\n    @Test\n    public void toSApplication_should_throw_ImportException_when_layout_is_not_found() throws Exception {\n        //given\n        final ApplicationNode node = buildApplicationNode(\"app\", \"1.0\");\n\n        given(pageService.getPageByName(ApplicationService.DEFAULT_LAYOUT_NAME)).willReturn(null);\n\n        //when - then exception\n        assertThatExceptionOfType(ImportException.class).isThrownBy(() -> convertToSApplication(node, 1L))\n                .withMessage(\"Unable to import application with token '%s' because the layout '%s' was not found.\",\n                        \"app\", ApplicationService.DEFAULT_LAYOUT_NAME);\n    }\n\n    @Test\n    public void toSApplication_should_throw_ImportException_when_theme_is_not_found() throws Exception {\n        //given\n        final ApplicationNode node = buildApplicationNode(\"app\", \"1.0\");\n\n        given(pageService.getPageByName(ApplicationService.DEFAULT_THEME_NAME)).willReturn(null);\n\n        assertThatExceptionOfType(ImportException.class).isThrownBy(() -> convertToSApplication(node, 1L))\n                .withMessage(\"Unable to import application with token '%s' because the theme '%s' was not found.\",\n                        \"app\", ApplicationService.DEFAULT_THEME_NAME);\n    }\n\n    private ApplicationNode buildApplicationNode(final String token, final String version) {\n        final ApplicationNode node = new ApplicationNode();\n        node.setVersion(version);\n        node.setToken(token);\n        node.setState(\"ENABLED\");\n        return node;\n    }\n\n    @Test\n    public void toSApplication_should_throw_ImportException_when_application_token_is_invalid() throws Exception {\n        //given\n        doThrow(new ImportException(\"invalid token\")).when(validator).validate(\"invalid\");\n        ApplicationNode applicationNode = buildApplicationNode(\"invalid\", \"1.0\");\n\n        //when - then exception\n        assertThatExceptionOfType(ImportException.class).isThrownBy(() -> convertToSApplication(applicationNode, 1L))\n                .withMessage(\"invalid token\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/business/application/converter/NodeToApplicationPageConverterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.converter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.doThrow;\nimport static org.mockito.Mockito.mock;\n\nimport org.bonitasoft.engine.api.ImportError;\nimport org.bonitasoft.engine.business.application.importer.ApplicationPageImportResult;\nimport org.bonitasoft.engine.business.application.importer.validator.ApplicationImportValidator;\nimport org.bonitasoft.engine.business.application.model.SApplicationPage;\nimport org.bonitasoft.engine.business.application.model.SApplicationWithIcon;\nimport org.bonitasoft.engine.business.application.xml.ApplicationPageNode;\nimport org.bonitasoft.engine.exception.ImportException;\nimport org.bonitasoft.engine.page.PageService;\nimport org.bonitasoft.engine.page.SPage;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class NodeToApplicationPageConverterTest {\n\n    @Mock\n    PageService pageService;\n\n    @Mock\n    ApplicationImportValidator validator;\n\n    @InjectMocks\n    private NodeToApplicationPageConverter converter;\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    private static long APPLICATION_ID = 11L;\n\n    private SApplicationWithIcon application;\n\n    @Before\n    public void setUp() throws Exception {\n        application = new SApplicationWithIcon();\n        application.setId(APPLICATION_ID);\n    }\n\n    @Test\n    public void toSApplication_page_should_convert_all_fields_set_applicationId_and_return_no_errors_when_custom_page_is_found()\n            throws Exception {\n        //given\n        final ApplicationPageNode node = buildAppPageNode(\"home\", \"customPage\");\n\n        final long pageId = 3L;\n        final SPage page = mock(SPage.class);\n        given(page.getId()).willReturn(pageId);\n        given(pageService.getPageByName(\"customPage\")).willReturn(page);\n\n        //when\n        final ApplicationPageImportResult importResult = converter.toSApplicationPage(node, application);\n\n        //then\n        assertThat(importResult).isNotNull();\n        final SApplicationPage applicationPage = importResult.getApplicationPage();\n        assertThat(applicationPage.getToken()).isEqualTo(\"home\");\n        assertThat(applicationPage.getApplicationId()).isEqualTo(APPLICATION_ID);\n        assertThat(applicationPage.getPageId()).isEqualTo(pageId);\n\n        assertThat(importResult.getError()).isNull();\n    }\n\n    @Test\n    public void toSApplication_page_should_convert_available_fields_set_applicationId_and_return_errors_when_custom_page_is_not_found()\n            throws Exception {\n        //given\n        final ApplicationPageNode node = buildAppPageNode(\"home\", \"customPage\");\n\n        given(pageService.getPageByName(\"customPage\")).willReturn(null);\n\n        //when\n        final ApplicationPageImportResult importResult = converter.toSApplicationPage(node, application);\n\n        //then\n        assertThat(importResult).isNotNull();\n        final SApplicationPage applicationPage = importResult.getApplicationPage();\n        assertThat(applicationPage.getToken()).isEqualTo(\"home\");\n        assertThat(applicationPage.getApplicationId()).isEqualTo(APPLICATION_ID);\n        assertThat(applicationPage.getPageId()).isEqualTo(0);\n\n        assertThat(importResult.getError()).isEqualTo(new ImportError(\"customPage\", ImportError.Type.PAGE));\n    }\n\n    private ApplicationPageNode buildAppPageNode(final String token, final String customPageName) {\n        final ApplicationPageNode node = new ApplicationPageNode();\n        node.setToken(token);\n        node.setCustomPage(customPageName);\n        return node;\n    }\n\n    @Test\n    public void toSApplicationPage_should_throw_ImportException_when_page_has_invalid_token() throws Exception {\n        //given\n        ApplicationPageNode pageNode = buildAppPageNode(\"invalid\", \"page\");\n        doThrow(new ImportException(\"invalid token\")).when(validator).validate(\"invalid\");\n\n        //then\n        expectedException.expect(ImportException.class);\n        expectedException.expectMessage(\"invalid token\");\n\n        //when\n        converter.toSApplicationPage(pageNode, application);\n\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/business/application/converter/NodetoApplicationMenuConverterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.converter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.*;\n\nimport org.bonitasoft.engine.api.ImportError;\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.importer.ApplicationMenuImportResult;\nimport org.bonitasoft.engine.business.application.model.SApplicationMenu;\nimport org.bonitasoft.engine.business.application.model.SApplicationPage;\nimport org.bonitasoft.engine.business.application.model.SApplicationWithIcon;\nimport org.bonitasoft.engine.business.application.xml.ApplicationMenuNode;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class NodetoApplicationMenuConverterTest {\n\n    @Mock\n    ApplicationService applicationService;\n\n    @InjectMocks\n    private NodeToApplicationMenuConverter converter;\n\n    @Test\n    public void toSApplicationMenu_should_convert_all_fields() throws Exception {\n        //given\n        long applicationId = 3L;\n        SApplicationWithIcon application = mock(SApplicationWithIcon.class);\n        given(application.getId()).willReturn(applicationId);\n        given(application.getToken()).willReturn(\"app\");\n\n        long parentMenuId = 100L;\n        SApplicationMenu parentMenu = buildMenu(parentMenuId);\n\n        ApplicationMenuNode menuNode = new ApplicationMenuNode();\n        menuNode.setApplicationPage(\"home\");\n        menuNode.setDisplayName(\"Sub\");\n\n        long appPageId = 12L;\n        SApplicationPage applicationPage = mock(SApplicationPage.class);\n        given(applicationPage.getId()).willReturn(appPageId);\n        given(applicationService.getApplicationPage(\"app\", \"home\")).willReturn(applicationPage);\n\n        int index = 4;\n        given(applicationService.getNextAvailableIndex(parentMenuId)).willReturn(index);\n\n        //when\n        ApplicationMenuImportResult importResult = converter.toSApplicationMenu(menuNode, application, parentMenu);\n\n        //then\n        assertThat(importResult).isNotNull();\n        SApplicationMenu applicationMenu = importResult.getApplicationMenu();\n        assertThat(applicationMenu.getApplicationId()).isEqualTo(applicationId);\n        assertThat(applicationMenu.getApplicationPageId()).isEqualTo(appPageId);\n        assertThat(applicationMenu.getDisplayName()).isEqualTo(\"Sub\");\n        assertThat(applicationMenu.getIndex()).isEqualTo(index);\n        assertThat(applicationMenu.getParentId()).isEqualTo(parentMenuId);\n        assertThat(importResult.getError()).isNull();\n    }\n\n    @Test\n    public void toSApplicationMenu_should_not_set_parent_id_when_parent_is_null() throws Exception {\n        //given\n        long applicationId = 3L;\n        SApplicationWithIcon application = mock(SApplicationWithIcon.class);\n        given(application.getId()).willReturn(applicationId);\n        given(application.getToken()).willReturn(\"app\");\n\n        ApplicationMenuNode menuNode = new ApplicationMenuNode();\n        menuNode.setApplicationPage(\"home\");\n        menuNode.setDisplayName(\"Sub\");\n\n        long appPageId = 12L;\n        SApplicationPage applicationPage = mock(SApplicationPage.class);\n        given(applicationPage.getId()).willReturn(appPageId);\n        given(applicationService.getApplicationPage(\"app\", \"home\")).willReturn(applicationPage);\n\n        int index = 4;\n        given(applicationService.getNextAvailableIndex(null)).willReturn(index);\n\n        //when\n        ApplicationMenuImportResult importResult = converter.toSApplicationMenu(menuNode, application, null);\n\n        //then\n        assertThat(importResult).isNotNull();\n        SApplicationMenu applicationMenu = importResult.getApplicationMenu();\n        assertThat(applicationMenu.getApplicationId()).isEqualTo(applicationId);\n        assertThat(applicationMenu.getApplicationPageId()).isEqualTo(appPageId);\n        assertThat(applicationMenu.getDisplayName()).isEqualTo(\"Sub\");\n        assertThat(applicationMenu.getIndex()).isEqualTo(index);\n        assertThat(applicationMenu.getParentId()).isNull();\n        assertThat(importResult.getError()).isNull();\n    }\n\n    private SApplicationMenu buildMenu(final long id) {\n        SApplicationMenu parentMenu = mock(SApplicationMenu.class);\n        given(parentMenu.getId()).willReturn(id);\n        return parentMenu;\n    }\n\n    @Test\n    public void toSApplicationMenu_should_have_null_applicationPageId_when_applicationPage_is_null_in_xml()\n            throws Exception {\n        //given\n        long applicationId = 3L;\n        SApplicationWithIcon application = mock(SApplicationWithIcon.class);\n        given(application.getId()).willReturn(applicationId);\n\n        ApplicationMenuNode menuNode = new ApplicationMenuNode();\n        menuNode.setDisplayName(\"Sub\");\n\n        long parentMenuId = 100L;\n        SApplicationMenu parentMenu = buildMenu(parentMenuId);\n\n        int index = 4;\n        given(applicationService.getNextAvailableIndex(parentMenuId)).willReturn(index);\n\n        //when\n        ApplicationMenuImportResult importResult = converter.toSApplicationMenu(menuNode, application, parentMenu);\n\n        //then\n        assertThat(importResult).isNotNull();\n        SApplicationMenu applicationMenu = importResult.getApplicationMenu();\n        assertThat(applicationMenu.getApplicationId()).isEqualTo(applicationId);\n        assertThat(applicationMenu.getApplicationPageId()).isNull();\n        assertThat(applicationMenu.getDisplayName()).isEqualTo(\"Sub\");\n        assertThat(applicationMenu.getIndex()).isEqualTo(index);\n        assertThat(applicationMenu.getParentId()).isEqualTo(parentMenuId);\n        assertThat(importResult.getError()).isNull();\n\n        verify(applicationService, never()).getApplicationPage(anyString(), anyString());\n    }\n\n    @Test\n    public void toSApplicationMenu_return_error_when_ApplicationPage_is_not_found() throws Exception {\n        //given\n        long applicationId = 3L;\n        SApplicationWithIcon application = mock(SApplicationWithIcon.class);\n        given(application.getId()).willReturn(applicationId);\n        given(application.getToken()).willReturn(\"app\");\n\n        ApplicationMenuNode menuNode = new ApplicationMenuNode();\n        menuNode.setApplicationPage(\"home\");\n        menuNode.setDisplayName(\"Sub\");\n\n        given(applicationService.getApplicationPage(\"app\", \"home\")).willThrow(new SObjectNotFoundException());\n\n        long parentMenuId = 100L;\n        int index = 4;\n        given(applicationService.getNextAvailableIndex(parentMenuId)).willReturn(index);\n\n        SApplicationMenu parentMenu = buildMenu(parentMenuId);\n\n        //when\n        ApplicationMenuImportResult importResult = converter.toSApplicationMenu(menuNode, application, parentMenu);\n\n        //then\n        assertThat(importResult).isNotNull();\n        SApplicationMenu applicationMenu = importResult.getApplicationMenu();\n        assertThat(applicationMenu.getApplicationId()).isEqualTo(applicationId);\n        assertThat(applicationMenu.getApplicationPageId()).isNull();\n        assertThat(applicationMenu.getDisplayName()).isEqualTo(\"Sub\");\n        assertThat(applicationMenu.getIndex()).isEqualTo(index);\n        assertThat(applicationMenu.getParentId()).isEqualTo(parentMenuId);\n        assertThat(importResult.getError()).isEqualTo(new ImportError(\"home\", ImportError.Type.APPLICATION_PAGE));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/business/application/exporter/ApplicationContainerExporterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.exporter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.business.application.xml.ApplicationNodeContainer;\nimport org.junit.Test;\n\npublic class ApplicationContainerExporterTest {\n\n    @Test\n    public void exportApplications_should_call_marshall() throws Exception {\n        final ApplicationContainerExporter exporter = new ApplicationContainerExporter();\n\n        //when\n        final byte[] bytes = exporter.export(new ApplicationNodeContainer());\n\n        //then\n        assertThat(new String(bytes)).isEqualTo(\"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\" standalone=\\\"yes\\\"?>\\n\" +\n                \"<applications xmlns=\\\"http://documentation.bonitasoft.com/application-xml-schema/1.1\\\"/>\\n\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/business/application/exporter/ApplicationExporterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.exporter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.business.application.converter.ApplicationsToNodeContainerConverter;\nimport org.bonitasoft.engine.business.application.model.SApplication;\nimport org.bonitasoft.engine.business.application.xml.ApplicationNodeContainer;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ApplicationExporterTest {\n\n    @Mock\n    private ApplicationsToNodeContainerConverter converter;\n\n    @Mock\n    private ApplicationContainerExporter containerExporter;\n\n    @InjectMocks\n    private ApplicationExporter applicationExporter;\n\n    @Test\n    public void export_should_return_result_of_ApplicationContainerExporter() throws Exception {\n        //given\n        List<SApplication> applications = Arrays.asList(mock(SApplication.class));\n        ApplicationNodeContainer container = mock(ApplicationNodeContainer.class);\n\n        given(converter.toNode(applications)).willReturn(container);\n        given(containerExporter.export(container)).willReturn(\"<applications/>\".getBytes());\n\n        //when\n        byte[] exportedApplications = applicationExporter.export(applications);\n\n        //then\n        assertThat(new String(exportedApplications)).isEqualTo(\"<applications/>\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/business/application/importer/ApplicationImporterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.importer;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.*;\n\nimport java.nio.charset.StandardCharsets;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.ImportError;\nimport org.bonitasoft.engine.api.ImportStatus;\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.converter.NodeToApplicationConverter;\nimport org.bonitasoft.engine.business.application.model.AbstractSApplication;\nimport org.bonitasoft.engine.business.application.model.SApplication;\nimport org.bonitasoft.engine.business.application.model.SApplicationPage;\nimport org.bonitasoft.engine.business.application.model.SApplicationWithIcon;\nimport org.bonitasoft.engine.business.application.xml.ApplicationLinkNode;\nimport org.bonitasoft.engine.business.application.xml.ApplicationMenuNode;\nimport org.bonitasoft.engine.business.application.xml.ApplicationNode;\nimport org.bonitasoft.engine.business.application.xml.ApplicationNodeBuilder;\nimport org.bonitasoft.engine.business.application.xml.ApplicationPageNode;\nimport org.bonitasoft.engine.commons.exceptions.SObjectCreationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.ImportException;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ApplicationImporterTest {\n\n    private static final String UNUSED_PAGE = \"\";\n\n    private static final String ICON_MIME_TYPE = \"iconMimeType\";\n\n    private static final byte[] ICON_CONTENT = \"iconContent\".getBytes(StandardCharsets.UTF_8);\n\n    @Mock\n    private ApplicationService applicationService;\n    @Mock\n    private ApplicationImportStrategy strategy;\n    @Mock\n    private NodeToApplicationConverter nodeToApplicationConverter;\n    @Mock\n    private ApplicationPageImporter applicationPageImporter;\n    @Mock\n    private ApplicationMenuImporter applicationMenuImporter;\n    @Mock\n    private ImportResult importResult;\n\n    private ApplicationImporter applicationImporter;\n\n    @Before\n    public void before() {\n        applicationImporter = spy(new ApplicationImporter(applicationService, nodeToApplicationConverter,\n                applicationPageImporter, applicationMenuImporter));\n    }\n\n    @Test\n    public void importApplication_should_create_application_import_pages_and_menus_and_return_status_when_no_application_existing_and_add_if_missing_is_true()\n            throws Exception {\n        //given\n        long createdBy = 5L;\n        boolean editable = false;\n        SApplicationWithIcon app = new SApplicationWithIcon(\"app\", \"app\", \"1.0\", 1L, createdBy, \"state\", editable);\n        app.setId(1);\n        given(importResult.getApplication()).willReturn(app);\n        given(importResult.getImportStatus()).willReturn(new ImportStatus(app.getToken()));\n\n        ApplicationPageNode homePage = ApplicationNodeBuilder.newApplicationPage(\"home\", \"home\").create();\n        ApplicationPageNode pageNode2 = ApplicationNodeBuilder.newApplicationPage(\"page\", \"page\").create();\n\n        ApplicationMenuNode menu1 = ApplicationNodeBuilder.newMenu(\"menu1\", UNUSED_PAGE).create();\n        ApplicationMenuNode menu2 = ApplicationNodeBuilder.newMenu(\"menu2\", UNUSED_PAGE).create();\n\n        ApplicationNode applicationNode = new ApplicationNode();\n        applicationNode.addApplicationPage(homePage);\n        applicationNode.addApplicationPage(pageNode2);\n        applicationNode.addApplicationMenu(menu1);\n        applicationNode.addApplicationMenu(menu2);\n        applicationNode.setHomePage(homePage.getToken());\n        applicationNode.setToken(\"app\");\n\n        given(nodeToApplicationConverter.toSApplication(applicationNode, ICON_CONTENT, ICON_MIME_TYPE, createdBy,\n                editable))\n                .willReturn(importResult);\n\n        long homePageId = 222L;\n        SApplicationPage applicationPage = new SApplicationPage(app.getId(), homePageId, \"home\");\n        applicationPage.setId(homePageId);\n\n        given(applicationService.getApplicationPage(\"app\", \"home\")).willReturn(applicationPage);\n\n        ImportError errorMenu = new ImportError(\"errorMenu\", ImportError.Type.APPLICATION_PAGE);\n        ImportError errorPage = new ImportError(\"errorPage\", ImportError.Type.APPLICATION_PAGE);\n\n        given(applicationPageImporter.importApplicationPages(any(), eq(app)))\n                .willReturn(List.of(errorPage));\n\n        given(applicationMenuImporter.importApplicationMenus(any(), eq(app)))\n                .willReturn(Arrays.asList(errorMenu, errorMenu));\n\n        given(applicationService.createApplication(app)).willReturn(app);\n\n        //when\n        ImportStatus retrievedStatus = applicationImporter.importApplication(applicationNode, editable, createdBy,\n                ICON_CONTENT, ICON_MIME_TYPE, true, strategy);\n\n        //then\n        //create application\n        assertThat(retrievedStatus.getStatus()).isEqualTo(ImportStatus.Status.ADDED);\n        verify(applicationService).createApplication(app);\n        assertThat(app.isEditable()).isFalse();\n        verifyNoInteractions(strategy);\n\n        //add pages\n        verify(applicationPageImporter).importApplicationPages(any(), eq(app));\n        assertThat(retrievedStatus.getErrors()).containsExactlyInAnyOrder(errorPage, errorMenu);\n        //add menus\n        verify(applicationMenuImporter).importApplicationMenus(any(), eq(app));\n\n        //set home page\n        verify(applicationService).updateApplication(eq(app),\n                argThat(desc -> desc.getFields().get(AbstractSApplication.HOME_PAGE_ID).equals(homePageId)));\n    }\n\n    @Test\n    public void importApplication_should_not_create_application_when_no_application_existing_and_add_if_missing_is_false()\n            throws Exception {\n        //given\n        long createdBy = 5L;\n        boolean editable = false;\n        SApplicationWithIcon app = new SApplicationWithIcon(\"app\", \"app\", \"1.0\", 1L, createdBy, \"state\", editable);\n        app.setId(1);\n        given(importResult.getApplication()).willReturn(app);\n        given(importResult.getImportStatus()).willReturn(new ImportStatus(app.getToken()));\n\n        ApplicationPageNode homePage = ApplicationNodeBuilder.newApplicationPage(\"home\", \"home\").create();\n        ApplicationPageNode pageNode2 = ApplicationNodeBuilder.newApplicationPage(\"page\", \"page\").create();\n\n        ApplicationMenuNode menu1 = ApplicationNodeBuilder.newMenu(\"menu1\", UNUSED_PAGE).create();\n        ApplicationMenuNode menu2 = ApplicationNodeBuilder.newMenu(\"menu2\", UNUSED_PAGE).create();\n\n        ApplicationNode applicationNode = new ApplicationNode();\n        applicationNode.addApplicationPage(homePage);\n        applicationNode.addApplicationPage(pageNode2);\n        applicationNode.addApplicationMenu(menu1);\n        applicationNode.addApplicationMenu(menu2);\n        applicationNode.setHomePage(homePage.getToken());\n        applicationNode.setToken(\"app\");\n\n        given(nodeToApplicationConverter.toSApplication(applicationNode, ICON_CONTENT, ICON_MIME_TYPE, createdBy,\n                editable))\n                .willReturn(importResult);\n\n        long homePageId = 222L;\n\n        //when\n        ImportStatus retrievedStatus = applicationImporter.importApplication(applicationNode, editable, createdBy,\n                ICON_CONTENT, ICON_MIME_TYPE, false, strategy);\n\n        //then\n        //create application\n        assertThat(retrievedStatus.getStatus()).isEqualTo(ImportStatus.Status.SKIPPED);\n        verify(applicationService, never()).createApplication(app);\n        assertThat(app.isEditable()).isFalse();\n        verifyNoInteractions(strategy);\n\n        //add pages\n        verify(applicationPageImporter, never()).importApplicationPages(any(), eq(app));\n        //add menus\n        verify(applicationMenuImporter, never()).importApplicationMenus(any(), eq(app));\n\n        //set home page\n        verify(applicationService, never()).updateApplication(eq(app),\n                argThat(desc -> desc.getFields().get(AbstractSApplication.HOME_PAGE_ID).equals(homePageId)));\n    }\n\n    @Test\n    public void importApplication_should_not_add_error_when_error_already_exists() throws Exception {\n        //given\n        long createdBy = 5L;\n        boolean editable = true;\n        SApplicationWithIcon app = mock(SApplicationWithIcon.class);\n\n        ImportResult importResult = mock(ImportResult.class);\n        given(importResult.getApplication()).willReturn(app);\n        ImportStatus importStatus = new ImportStatus(\"app\");\n        ImportError errorPage = new ImportError(\"home\", ImportError.Type.PAGE);\n        importStatus.addError(errorPage);\n\n        given(importResult.getImportStatus()).willReturn(importStatus);\n\n        ApplicationPageNode pageNode1 = mock(ApplicationPageNode.class);\n\n        ApplicationNode applicationNode = new ApplicationNode();\n        applicationNode.addApplicationPage(pageNode1);\n        given(nodeToApplicationConverter.toSApplication(applicationNode, ICON_CONTENT, ICON_MIME_TYPE, createdBy,\n                editable))\n                .willReturn(importResult);\n\n        given(applicationService.createApplication(app)).willReturn(app);\n\n        //when\n        ImportStatus retrievedStatus = applicationImporter.importApplication(applicationNode, editable, createdBy,\n                ICON_CONTENT, ICON_MIME_TYPE, true, strategy);\n\n        //then\n        assertThat(retrievedStatus).isEqualTo(importResult.getImportStatus());\n        assertThat(retrievedStatus.getErrors()).containsExactly(errorPage);\n    }\n\n    @Test\n    public void importApplication_should_not_set_home_page_when_applicationNode_does_not_have_home_page()\n            throws Exception {\n        //given\n        long createdBy = 5L;\n        boolean editable = true;\n        SApplicationWithIcon app = mock(SApplicationWithIcon.class);\n\n        ImportResult importResult = mock(ImportResult.class);\n        given(importResult.getApplication()).willReturn(app);\n        ImportStatus importStatus = mock(ImportStatus.class);\n        given(importResult.getImportStatus()).willReturn(importStatus);\n\n        ApplicationNode applicationNode = new ApplicationNode();\n        applicationNode.setToken(\"app\");\n        given(nodeToApplicationConverter.toSApplication(applicationNode, ICON_CONTENT, ICON_MIME_TYPE, createdBy,\n                editable))\n                .willReturn(importResult);\n        given(applicationService.createApplication(app)).willReturn(app);\n\n        //when\n        applicationImporter.importApplication(applicationNode, editable, createdBy, ICON_CONTENT,\n                ICON_MIME_TYPE, true, strategy);\n\n        //then\n        //set home page\n        verify(applicationService, never()).updateApplication(any(SApplicationWithIcon.class),\n                any(EntityUpdateDescriptor.class));\n    }\n\n    @Test\n    public void importApplication_should_add_error_when_home_page_is_not_found() throws Exception {\n        //given\n        long createdBy = 5L;\n        boolean editable = true;\n        SApplicationWithIcon app = mock(SApplicationWithIcon.class);\n\n        ImportResult importResult = mock(ImportResult.class);\n        given(importResult.getApplication()).willReturn(app);\n        ImportStatus importStatus = mock(ImportStatus.class);\n        given(importResult.getImportStatus()).willReturn(importStatus);\n\n        ApplicationNode applicationNode = new ApplicationNode();\n        applicationNode.setToken(\"app\");\n        applicationNode.setHomePage(\"home\");\n\n        given(nodeToApplicationConverter.toSApplication(applicationNode, ICON_CONTENT, ICON_MIME_TYPE, createdBy,\n                editable))\n                .willReturn(importResult);\n        given(applicationService.createApplication(app)).willReturn(app);\n\n        given(applicationService.getApplicationPage(\"app\", \"home\")).willThrow(new SObjectNotFoundException(\"\"));\n\n        //when\n        applicationImporter.importApplication(applicationNode, editable, createdBy, ICON_CONTENT,\n                ICON_MIME_TYPE, true, strategy);\n\n        //then\n        //set home page\n        verify(applicationService, never()).updateApplication(any(SApplicationWithIcon.class),\n                any(EntityUpdateDescriptor.class));\n        verify(importStatus)\n                .addErrorsIfNotExists(List.of(new ImportError(\"home\", ImportError.Type.APPLICATION_PAGE)));\n    }\n\n    @Test\n    public void importApplication_should_skip_when_strategy_return_skip() throws Exception {\n        //given\n        long createdBy = 5L;\n        boolean editable = true;\n        SApplicationWithIcon appToBeImported = mock(SApplicationWithIcon.class);\n        given(appToBeImported.getToken()).willReturn(\"application\");\n\n        ImportResult importResult = new ImportResult(appToBeImported, new ImportStatus(\"name\"));\n\n        SApplication appInConflict = mock(SApplication.class);\n\n        ApplicationNode applicationNode = mock(ApplicationNode.class);\n        given(nodeToApplicationConverter.toSApplication(applicationNode, ICON_CONTENT, ICON_MIME_TYPE, createdBy,\n                editable))\n                .willReturn(importResult);\n        given(applicationService.getApplicationByToken(\"application\")).willReturn(appInConflict);\n        given(strategy.whenApplicationExists(any(), any())).willReturn(ApplicationImportStrategy.ImportStrategy.SKIP);\n        //when\n        ImportStatus importStatus = applicationImporter.importApplication(applicationNode, editable, createdBy,\n                ICON_CONTENT, ICON_MIME_TYPE, true, strategy);\n\n        //then\n        verify(applicationService, never()).forceDeleteApplication(any());\n        verify(applicationService, never()).createApplication(any());\n        assertThat(importStatus.getStatus()).isEqualTo(ImportStatus.Status.SKIPPED);\n    }\n\n    @Test\n    public void importApplication_replace_application_when_strategy_is_replace_duplicate() throws Exception {\n        //given\n        long createdBy = 5L;\n        boolean editable = true;\n        SApplicationWithIcon appToBeImported = mock(SApplicationWithIcon.class);\n        given(appToBeImported.getToken()).willReturn(\"application\");\n\n        ImportResult importResult = new ImportResult(appToBeImported, new ImportStatus(\"name\"));\n\n        SApplication appInConflict = mock(SApplication.class);\n\n        ApplicationNode applicationNode = mock(ApplicationNode.class);\n        given(nodeToApplicationConverter.toSApplication(applicationNode, ICON_CONTENT, ICON_MIME_TYPE, createdBy,\n                editable))\n                .willReturn(importResult);\n        given(applicationService.getApplicationByToken(\"application\")).willReturn(appInConflict);\n\n        //when\n        ImportStatus importStatus = applicationImporter.importApplication(applicationNode, editable, createdBy,\n                ICON_CONTENT, ICON_MIME_TYPE, true, new ReplaceDuplicateApplicationImportStrategy());\n\n        //then\n        verify(applicationService).forceDeleteApplication(appInConflict);\n        verify(applicationService).createApplication(appToBeImported);\n        verify(applicationService).createApplication(appToBeImported);\n        verify(applicationService).createApplication(appToBeImported);\n        assertThat(importStatus.getStatus()).isEqualTo(ImportStatus.Status.REPLACED);\n    }\n\n    @Test\n    public void importApplication_should_throw_alreadyExistsException_when_strategy_FailOnDuplicateApplicationImportStrategy()\n            throws Exception {\n\n        //given\n        long createdBy = 5L;\n        boolean editable = true;\n        SApplicationWithIcon appToBeImported = mock(SApplicationWithIcon.class);\n        given(appToBeImported.getToken()).willReturn(\"application\");\n\n        ImportResult importResult = mock(ImportResult.class);\n        given(importResult.getApplication()).willReturn(appToBeImported);\n        ImportStatus importStatus = mock(ImportStatus.class);\n        given(importResult.getImportStatus()).willReturn(importStatus);\n\n        SApplication appInConflict = mock(SApplication.class);\n\n        ApplicationNode applicationNode = mock(ApplicationNode.class);\n        given(nodeToApplicationConverter.toSApplication(applicationNode, ICON_CONTENT, ICON_MIME_TYPE, createdBy,\n                editable))\n                .willReturn(importResult);\n        given(applicationService.getApplicationByToken(\"application\")).willReturn(appInConflict);\n\n        //when - then exception\n        assertThatExceptionOfType(AlreadyExistsException.class)\n                .isThrownBy(() -> applicationImporter\n                        .importApplication(applicationNode, editable, createdBy, ICON_CONTENT, ICON_MIME_TYPE, true,\n                                new FailOnDuplicateApplicationImportStrategy()));\n    }\n\n    @Test\n    public void importApplication_should_throw_ExecutionException_when_application_service_throws_exception()\n            throws Exception {\n        //given\n        SApplicationWithIcon app1 = mock(SApplicationWithIcon.class);\n        ImportResult importResult = mock(ImportResult.class);\n        given(importResult.getApplication()).willReturn(app1);\n        ImportStatus importStatus = mock(ImportStatus.class);\n        given(importResult.getImportStatus()).willReturn(importStatus);\n\n        long createdBy = 5L;\n        boolean editable = true;\n\n        given(importResult.getApplication()).willReturn(app1);\n\n        ApplicationNode applicationNode = mock(ApplicationNode.class);\n        given(nodeToApplicationConverter.toSApplication(applicationNode, ICON_CONTENT, ICON_MIME_TYPE, createdBy,\n                editable))\n                .willReturn(importResult);\n\n        given(applicationService.createApplication(app1)).willThrow(new SObjectCreationException(\"\"));\n\n        //when - then exception\n        assertThatExceptionOfType(ImportException.class).isThrownBy(() -> applicationImporter\n                .importApplication(applicationNode, editable, createdBy, ICON_CONTENT, ICON_MIME_TYPE, true, strategy));\n    }\n\n    @Test\n    public void importApplicationLink_should_create_application_link_with_no_menus_and_no_pages_and_no_home_page()\n            throws Exception {\n        //given\n        long createdBy = SessionService.SYSTEM_ID;\n        boolean editable = true;\n        SApplicationWithIcon app = new SApplicationWithIcon(\"app\", \"app\", \"1.0\", 1L, createdBy, \"state\", editable);\n        app.setId(1);\n        app.setLink(true);\n        given(importResult.getApplication()).willReturn(app);\n        given(importResult.getImportStatus()).willReturn(new ImportStatus(app.getToken()));\n\n        ApplicationLinkNode applicationLinkNode = new ApplicationLinkNode();\n        applicationLinkNode.setToken(\"app\");\n\n        given(nodeToApplicationConverter.toSApplication(applicationLinkNode, null, null, createdBy, editable))\n                .willReturn(importResult);\n\n        given(applicationService.createApplication(app)).willReturn(app);\n\n        //when\n        ImportStatus retrievedStatus = applicationImporter.importApplication(applicationLinkNode, editable,\n                createdBy, null, null, true, strategy);\n\n        //then\n        //create application\n        assertThat(retrievedStatus.getStatus()).isEqualTo(ImportStatus.Status.ADDED);\n        assertThat(retrievedStatus.getErrors()).isEmpty();\n        verify(applicationService).createApplication(app);\n        verifyNoInteractions(strategy);\n\n        //no pages added\n        verify(applicationPageImporter, never()).importApplicationPages(any(), any());\n        //no menus added\n        verify(applicationMenuImporter, never()).importApplicationMenus(any(), any());\n        //no home page set\n        verify(applicationService, never()).updateApplication(any(), any());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/business/application/importer/ApplicationMenuImporterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.importer;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.ImportError;\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.converter.NodeToApplicationMenuConverter;\nimport org.bonitasoft.engine.business.application.model.SApplicationMenu;\nimport org.bonitasoft.engine.business.application.model.SApplicationWithIcon;\nimport org.bonitasoft.engine.business.application.xml.ApplicationMenuNode;\nimport org.bonitasoft.engine.business.application.xml.ApplicationNodeBuilder;\nimport org.bonitasoft.engine.commons.exceptions.SObjectCreationException;\nimport org.bonitasoft.engine.exception.ImportException;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ApplicationMenuImporterTest {\n\n    @Mock\n    ApplicationService applicationService;\n\n    @Mock\n    NodeToApplicationMenuConverter converter;\n\n    @InjectMocks\n    ApplicationMenuImporter importer;\n\n    @Test\n    public void importApplicationMenu_should_create_applicationMenu_and_sub_menus_when_no_error() throws Exception {\n        //given\n        SApplicationWithIcon application = mock(SApplicationWithIcon.class);\n        ApplicationMenuNode subMenuNode = ApplicationNodeBuilder.newMenu(\"subMenuNode\", \"\").create();\n\n        ApplicationMenuNode menuNode = ApplicationNodeBuilder.newMenu(\"menuNode\", \"\").create();\n        menuNode.addApplicationMenu(subMenuNode);\n\n        SApplicationMenu mainMenu = mock(SApplicationMenu.class);\n        ApplicationMenuImportResult mainMenuImportResult = mock(ApplicationMenuImportResult.class);\n        given(mainMenuImportResult.getApplicationMenu()).willReturn(mainMenu);\n\n        SApplicationMenu subMenu = mock(SApplicationMenu.class);\n        ApplicationMenuImportResult subMenuImportResult = mock(ApplicationMenuImportResult.class);\n        given(subMenuImportResult.getApplicationMenu()).willReturn(subMenu);\n\n        given(converter.toSApplicationMenu(menuNode, application, null)).willReturn(mainMenuImportResult);\n        given(converter.toSApplicationMenu(subMenuNode, application, null)).willReturn(subMenuImportResult);\n\n        //when\n        List<ImportError> importErrors = importer.importApplicationMenu(menuNode, application, null);\n\n        //then\n        assertThat(importErrors).isEmpty();\n        verify(applicationService).createApplicationMenu(mainMenu);\n        verify(applicationService).createApplicationMenu(subMenu);\n\n    }\n\n    @Test\n    public void importApplicationMenu_should_not_create_applicationMenu_when_there_is_error() throws Exception {\n        //given\n        SApplicationWithIcon application = mock(SApplicationWithIcon.class);\n        ApplicationMenuNode menuNode = new ApplicationMenuNode();\n\n        ImportError error = new ImportError(\"page\", ImportError.Type.APPLICATION_PAGE);\n        ApplicationMenuImportResult importResult = mock(ApplicationMenuImportResult.class);\n        given(importResult.getError()).willReturn(error);\n\n        given(converter.toSApplicationMenu(menuNode, application, null)).willReturn(importResult);\n\n        //when\n        List<ImportError> importErrors = importer.importApplicationMenu(menuNode, application, null);\n\n        //then\n        assertThat(importErrors).containsExactly(error);\n        verify(applicationService, never()).createApplicationMenu(any(SApplicationMenu.class));\n\n    }\n\n    @Test(expected = ImportException.class)\n    public void importApplicationMenu_should_throw_Exception_when_menu_creation_throws_exception() throws Exception {\n        //given\n        SApplicationWithIcon application = mock(SApplicationWithIcon.class);\n        ApplicationMenuNode menuNode = new ApplicationMenuNode();\n\n        SApplicationMenu mainMenu = mock(SApplicationMenu.class);\n        ApplicationMenuImportResult mainMenuImportResult = mock(ApplicationMenuImportResult.class);\n        given(mainMenuImportResult.getApplicationMenu()).willReturn(mainMenu);\n\n        given(converter.toSApplicationMenu(menuNode, application, null)).willReturn(mainMenuImportResult);\n        given(applicationService.createApplicationMenu(mainMenu)).willThrow(new SObjectCreationException());\n\n        //when\n        importer.importApplicationMenu(menuNode, application, null);\n\n        //then exception\n\n    }\n\n    @Test\n    public void importApplicationMenu_should_return_errors_for_sub_menus() throws Exception {\n        //given\n        SApplicationWithIcon application = mock(SApplicationWithIcon.class);\n        ApplicationMenuNode subMenuNode1 = ApplicationNodeBuilder.newMenu(\"subMenuNode1\", \"\").create();\n        ApplicationMenuNode subMenuNode2 = ApplicationNodeBuilder.newMenu(\"subMenuNode2\", \"\").create();\n\n        ApplicationMenuNode menuNode = new ApplicationMenuNode();\n        menuNode.addApplicationMenu(subMenuNode1);\n        menuNode.addApplicationMenu(subMenuNode2);\n\n        SApplicationMenu mainMenu = mock(SApplicationMenu.class);\n        ApplicationMenuImportResult mainMenuImportResult = mock(ApplicationMenuImportResult.class);\n        given(mainMenuImportResult.getApplicationMenu()).willReturn(mainMenu);\n\n        ImportError error1 = new ImportError(\"page1\", ImportError.Type.APPLICATION_PAGE);\n        SApplicationMenu subMenu1 = mock(SApplicationMenu.class);\n        ApplicationMenuImportResult subMenuImportResult1 = mock(ApplicationMenuImportResult.class);\n        given(subMenuImportResult1.getError()).willReturn(error1);\n\n        ImportError error2 = new ImportError(\"page2\", ImportError.Type.APPLICATION_PAGE);\n        SApplicationMenu subMenu2 = mock(SApplicationMenu.class);\n        ApplicationMenuImportResult subMenuImportResult2 = mock(ApplicationMenuImportResult.class);\n        given(subMenuImportResult2.getError()).willReturn(error2);\n\n        given(converter.toSApplicationMenu(menuNode, application, null)).willReturn(mainMenuImportResult);\n        given(converter.toSApplicationMenu(subMenuNode1, application, null)).willReturn(subMenuImportResult1);\n        given(converter.toSApplicationMenu(subMenuNode2, application, null)).willReturn(subMenuImportResult2);\n\n        //when\n        List<ImportError> importErrors = importer.importApplicationMenu(menuNode, application, null);\n\n        //then\n        assertThat(importErrors).containsExactly(error1, error2);\n        verify(applicationService).createApplicationMenu(mainMenu);\n        verify(applicationService, never()).createApplicationMenu(subMenu1);\n        verify(applicationService, never()).createApplicationMenu(subMenu2);\n\n    }\n\n    @Test\n    public void importApplicationMenus_should_invoke_application_menu() throws Exception {\n\n        SApplicationWithIcon application = mock(SApplicationWithIcon.class);\n        ApplicationMenuNode subMenuNode1 = ApplicationNodeBuilder.newMenu(\"subMenuNode1\", \"\").create();\n        ApplicationMenuNode subMenuNode2 = ApplicationNodeBuilder.newMenu(\"subMenuNode2\", \"\").create();\n\n        ApplicationMenuNode subMenuNode3 = ApplicationNodeBuilder.newMenu(\"subMenuNode3\", \"\").create();\n        ApplicationMenuNode subMenuNode4 = ApplicationNodeBuilder.newMenu(\"subMenuNode4\", \"\").create();\n\n        ApplicationMenuNode menuNode = new ApplicationMenuNode();\n        menuNode.addApplicationMenu(subMenuNode1);\n        menuNode.addApplicationMenu(subMenuNode2);\n\n        ImportError error1 = new ImportError(\"page1\", ImportError.Type.APPLICATION_PAGE);\n        ImportError error2 = new ImportError(\"page2\", ImportError.Type.APPLICATION_PAGE);\n\n        ApplicationMenuNode menuNode2 = new ApplicationMenuNode();\n        menuNode2.addApplicationMenu(subMenuNode3);\n        menuNode2.addApplicationMenu(subMenuNode4);\n\n        importer = spy(new ApplicationMenuImporter(applicationService, converter));\n\n        doReturn(Arrays.asList(error1, error1)).when(importer).importApplicationMenu(eq(menuNode), any(), any());\n        doReturn(Arrays.asList(error2, error2)).when(importer).importApplicationMenu(eq(menuNode2), any(), any());\n\n        //when\n        List<ImportError> importErrors = importer.importApplicationMenus(Arrays.asList(menuNode, menuNode2),\n                application);\n\n        //then\n        verify(importer, times(1)).importApplicationMenu(eq(menuNode), any(), eq(null));\n        verify(importer, times(1)).importApplicationMenu(eq(menuNode2), any(), eq(null));\n        assertThat(importErrors).containsExactlyInAnyOrder(error1, error1, error2, error2);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/business/application/importer/ApplicationPageImporterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.importer;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.ImportError;\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.converter.NodeToApplicationPageConverter;\nimport org.bonitasoft.engine.business.application.model.SApplicationPage;\nimport org.bonitasoft.engine.business.application.model.SApplicationWithIcon;\nimport org.bonitasoft.engine.business.application.xml.ApplicationPageNode;\nimport org.bonitasoft.engine.commons.exceptions.SObjectCreationException;\nimport org.bonitasoft.engine.exception.ImportException;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ApplicationPageImporterTest {\n\n    @Mock\n    private ApplicationService applicationService;\n\n    @Mock\n    private NodeToApplicationPageConverter converter;\n\n    @InjectMocks\n    private ApplicationPageImporter importer;\n\n    @Test\n    public void importApplicationPage_should_create_applicationPage_when_there_is_no_error() throws Exception {\n        //given\n        SApplicationWithIcon application = mock(SApplicationWithIcon.class);\n        ApplicationPageNode applicationPageNode = mock(ApplicationPageNode.class);\n        SApplicationPage applicationPage = mock(SApplicationPage.class);\n        ApplicationPageImportResult importResult = new ApplicationPageImportResult(applicationPage, null);\n\n        given(converter.toSApplicationPage(applicationPageNode, application)).willReturn(importResult);\n\n        //when\n        ImportError importError = importer.importApplicationPage(applicationPageNode, application);\n\n        //then\n        verify(applicationService, times(1)).createApplicationPage(applicationPage);\n        assertThat(importError).isNull();\n    }\n\n    @Test\n    public void importApplicationPage_should_not_create_applicationPage_when_there_is_an_error() throws Exception {\n        //given\n        SApplicationWithIcon application = mock(SApplicationWithIcon.class);\n        ApplicationPageNode applicationPageNode = mock(ApplicationPageNode.class);\n        SApplicationPage applicationPage = mock(SApplicationPage.class);\n        ImportError error = mock(ImportError.class);\n        ApplicationPageImportResult importResult = new ApplicationPageImportResult(applicationPage, error);\n\n        given(converter.toSApplicationPage(applicationPageNode, application)).willReturn(importResult);\n\n        //when\n        ImportError importError = importer.importApplicationPage(applicationPageNode, application);\n\n        //then\n        verify(applicationService, never()).createApplicationPage(any(SApplicationPage.class));\n        assertThat(importError).isEqualTo(error);\n    }\n\n    @Test(expected = ImportException.class)\n    public void importApplicationPage_should_throw_Exception_when_create_applicationPage_throws_exception()\n            throws Exception {\n        //given\n        SApplicationWithIcon application = mock(SApplicationWithIcon.class);\n        ApplicationPageNode applicationPageNode = mock(ApplicationPageNode.class);\n        SApplicationPage applicationPage = mock(SApplicationPage.class);\n        ApplicationPageImportResult importResult = new ApplicationPageImportResult(applicationPage, null);\n\n        given(converter.toSApplicationPage(applicationPageNode, application)).willReturn(importResult);\n        given(applicationService.createApplicationPage(applicationPage)).willThrow(new SObjectCreationException(\"\"));\n\n        //when\n        importer.importApplicationPage(applicationPageNode, application);\n\n        //then exception\n    }\n\n    @Test\n    public void importApplicationPages_should_invoke_import_application_page() throws Exception {\n        importer = spy(new ApplicationPageImporter(applicationService, converter));\n        //given\n        SApplicationWithIcon application = mock(SApplicationWithIcon.class);\n\n        ApplicationPageNode applicationPageNode = new ApplicationPageNode();\n        applicationPageNode.setToken(\"appNode1\");\n        ApplicationPageNode applicationPageNode1 = new ApplicationPageNode();\n        applicationPageNode1.setToken(\"appNode2\");\n        ImportError error1 = new ImportError(\"page1\", ImportError.Type.APPLICATION_PAGE);\n        ImportError error2 = new ImportError(\"page2\", ImportError.Type.APPLICATION_PAGE);\n\n        doReturn(error1).when(importer).importApplicationPage(eq(applicationPageNode), any());\n        doReturn(error2).when(importer).importApplicationPage(eq(applicationPageNode1), any());\n\n        //when\n        List<ImportError> importErrors = importer\n                .importApplicationPages(Arrays.asList(applicationPageNode, applicationPageNode1), application);\n\n        //then\n        verify(importer, times(1)).importApplicationPage(eq(applicationPageNode), any());\n        verify(importer, times(1)).importApplicationPage(eq(applicationPageNode1), any());\n        assertThat(importErrors).containsExactlyInAnyOrder(error1, error2);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/business/application/importer/DefaultLivingApplicationImporterTest.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.importer;\n\nimport static java.nio.charset.StandardCharsets.UTF_8;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.catchThrowable;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.*;\n\nimport java.io.InputStream;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.ImportStatus;\nimport org.bonitasoft.engine.authorization.PermissionService;\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.converter.NodeToApplicationConverter;\nimport org.bonitasoft.engine.business.application.model.SApplicationWithIcon;\nimport org.bonitasoft.engine.commons.Pair;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.io.IOUtil;\nimport org.bonitasoft.engine.page.ContentType;\nimport org.bonitasoft.engine.page.PageServiceListener;\nimport org.bonitasoft.engine.page.SPage;\nimport org.bonitasoft.engine.page.impl.PageServiceImpl;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\nimport org.springframework.util.DigestUtils;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class DefaultLivingApplicationImporterTest {\n\n    private static final String DEFAULT_THEME_NAME = \"custompage_themeBonita\";\n    private static final String DEFAULT_LAYOUT_NAME = \"custompage_defaultlayout\";\n    private static final String HTML_EXAMPLE_PAGE_NAME = \"custompage_htmlexample\";\n    private static final String GROOVY_EXAMPLE_PAGE_NAME = \"custompage_groovyexample\";\n    private static final String HOME_PAGE_NAME = \"custompage_home\";\n    private static final String TENANT_STATUS_BONITA_PAGE_NAME = \"custompage_tenantStatusBonita\";\n    private static final String MY_PAGE_NAME = \"custompage_mypage\";\n    private static final String INDEX_GROOVY = \"Index.groovy\";\n    private static final String PAGE_PROPERTIES = \"page.properties\";\n    private static final String DEFAULT_APP_3_TOKEN = \"default_app_3\";\n\n    private static final Map<String, String> TEST_PAGES = Map.of(\n            DEFAULT_LAYOUT_NAME, \"/org/bonitasoft/web/page/bonita-default-layout.zip\",\n            HTML_EXAMPLE_PAGE_NAME, \"/org/bonitasoft/web/page/bonita-html-page-example.zip\",\n            GROOVY_EXAMPLE_PAGE_NAME, \"/org/bonitasoft/web/page/bonita-groovy-page-example.zip\",\n            HOME_PAGE_NAME, \"/org/bonitasoft/web/page/bonita-home-page.zip\",\n            TENANT_STATUS_BONITA_PAGE_NAME, \"/org/bonitasoft/web/page/page-tenant-status.zip\",\n            DEFAULT_THEME_NAME, \"/org/bonitasoft/web/page/bonita-theme.zip\");\n\n    @Mock\n    private ReadPersistenceService readPersistenceService;\n    @Mock\n    private Recorder recorder;\n    @Mock\n    private QueriableLoggerService queriableLoggerService;\n    @Mock\n    private ReadSessionAccessor sessionAccessor;\n    @Mock\n    private SessionService sessionService;\n    @Mock\n    private PermissionService permissionService;\n    @Mock\n    private PageServiceListener apiExtensionPageServiceListener;\n    @Mock\n    private ApplicationService applicationService;\n    @Mock\n    private NodeToApplicationConverter nodeToApplicationConverter;\n    @Mock\n    private ApplicationPageImporter applicationPageImporter;\n    @Mock\n    private ApplicationMenuImporter applicationMenuImporter;\n\n    @Captor\n    private ArgumentCaptor<SPage> pageArgumentCaptor;\n\n    private PageServiceImpl pageServiceImpl;\n    private ApplicationImporter applicationImporter;\n    private DefaultLivingApplicationImporter defaultLivingApplicationImporter;\n\n    @Before\n    public void before() {\n        pageServiceImpl = spy(\n                new PageServiceImpl(readPersistenceService, recorder, queriableLoggerService, sessionAccessor,\n                        sessionService, permissionService));\n        pageServiceImpl.setPageServiceListeners(Collections.singletonList(apiExtensionPageServiceListener));\n\n        applicationImporter = spy(\n                new ApplicationImporter(applicationService, nodeToApplicationConverter, applicationPageImporter,\n                        applicationMenuImporter));\n\n        defaultLivingApplicationImporter = spy(\n                new DefaultLivingApplicationImporter(pageServiceImpl, applicationImporter));\n    }\n\n    @Test\n    public void init_should_import_all_editable_provided_pages_if_is_the_first_run() throws SBonitaException {\n        // given\n        // resource in the classpath bonita-groovy-example-page.zip\n        final Map<String, String> map = new HashMap<>();\n        map.put(DEFAULT_LAYOUT_NAME, ContentType.LAYOUT);\n        map.put(HTML_EXAMPLE_PAGE_NAME, ContentType.PAGE);\n        map.put(GROOVY_EXAMPLE_PAGE_NAME, ContentType.PAGE);\n        map.put(HOME_PAGE_NAME, ContentType.PAGE);\n        map.put(TENANT_STATUS_BONITA_PAGE_NAME, ContentType.PAGE);\n        map.put(DEFAULT_THEME_NAME, ContentType.THEME);\n        doAnswer(invocation -> invocation.getArguments()[0]).when(pageServiceImpl)\n                .insertPage(pageArgumentCaptor.capture(), any());\n\n        // to simulate a first run:\n        defaultLivingApplicationImporter.setAddRemovablePagesIfMissing(true);\n\n        // when\n        defaultLivingApplicationImporter.execute();\n\n        // then\n        List<SPage> insertedPages = pageArgumentCaptor.getAllValues();\n        assertThat(insertedPages)\n                .hasSize(6)\n                .allSatisfy(insertedPage -> {\n                    String name = insertedPage.getName();\n                    assertThat(name).isIn(map.keySet());\n                    assertThat(insertedPage.getContentType()).isEqualTo(map.get(name));\n                    assertThat(insertedPage.getPageHash()).isEqualTo(getHashOfContent(name));\n                    assertThat(insertedPage.isProvided()).isTrue();\n                    assertThat(insertedPage.isEditable()).isTrue();\n                    assertThat(insertedPage.isRemovable()).isTrue();\n                    assertThat(insertedPage.getContentType()).isEqualTo(map.get(name));\n                    assertThat(insertedPage.getPageHash()).isEqualTo(getHashOfContent(name));\n                });\n\n        verify(pageServiceImpl, never()).updatePage(anyLong(), any(EntityUpdateDescriptor.class));\n        verify(pageServiceImpl, never()).updatePageContent(anyLong(), any(byte[].class), anyString());\n    }\n\n    @Test\n    public void init_should_not_insert_not_removable_provided_page_if_is_not_first_run() throws Exception {\n        // given\n        final Map<String, String> removablePage = new HashMap<>();\n        removablePage.put(DEFAULT_LAYOUT_NAME, ContentType.LAYOUT);\n        removablePage.put(HTML_EXAMPLE_PAGE_NAME, ContentType.PAGE);\n        removablePage.put(GROOVY_EXAMPLE_PAGE_NAME, ContentType.PAGE);\n        removablePage.put(HOME_PAGE_NAME, ContentType.PAGE);\n        removablePage.put(TENANT_STATUS_BONITA_PAGE_NAME, ContentType.PAGE);\n        removablePage.put(DEFAULT_THEME_NAME, ContentType.THEME);\n\n        // to simulate a subsequent run:\n        defaultLivingApplicationImporter.setAddRemovablePagesIfMissing(false);\n\n        // when\n        defaultLivingApplicationImporter.execute();\n\n        // then\n        verify(defaultLivingApplicationImporter, times(6)).importProvidedPage(any(), any(byte[].class), eq(true),\n                eq(true), eq(false));\n        verify(pageServiceImpl, never()).insertPage(argThat(page -> !removablePage.containsKey(page.getName())),\n                any(byte[].class));\n    }\n\n    @Test\n    public void import_provided_page_should_return_import_status_added_if_page_is_missing_equal_true()\n            throws Exception {\n        // given: a zip without properties\n        @SuppressWarnings(\"unchecked\")\n        final byte[] content = IOUtil.zip(getIndexGroovyContentPair(), getPagePropertiesContentPair());\n\n        doReturn(null).when(pageServiceImpl).getPageByName(MY_PAGE_NAME);\n\n        ImportStatus importStatus = defaultLivingApplicationImporter.importProvidedPage(MY_PAGE_NAME, content, true,\n                true, true);\n\n        assertThat(importStatus.getStatus()).isEqualTo(ImportStatus.Status.ADDED);\n        verify(pageServiceImpl, never()).updatePageContent(anyLong(), any(byte[].class), any());\n    }\n\n    @Test\n    public void should_return_skipped_when_page_does_not_exist_and_addIfMissing_equals_false() throws Exception {\n        // given: a zip without properties\n        @SuppressWarnings(\"unchecked\")\n        final byte[] content = IOUtil.zip(getIndexGroovyContentPair(), getPagePropertiesContentPair());\n\n        doReturn(null).when(pageServiceImpl).getPageByName(MY_PAGE_NAME);\n\n        ImportStatus importStatus = defaultLivingApplicationImporter.importProvidedPage(MY_PAGE_NAME, content, true,\n                true, false);\n\n        assertThat(importStatus.getStatus()).isEqualTo(ImportStatus.Status.SKIPPED);\n        verify(pageServiceImpl, never()).insertPage(any(), any(byte[].class));\n        verify(pageServiceImpl, never()).updatePageContent(anyLong(), any(byte[].class), any());\n    }\n\n    @Test\n    public void should_replaced_an_existing_provided_page_when_content_is_different_and_still_provided()\n            throws Exception {\n\n        final SPage pageInDb = new SPage();\n        pageInDb.setName(MY_PAGE_NAME);\n        pageInDb.setDescription(\"mypage description\");\n        pageInDb.setDisplayName(\"mypage display name\");\n        pageInDb.setId(12);\n        pageInDb.setProvided(true);\n        pageInDb.setPageHash(DigestUtils.md5DigestAsHex(\"some hash\".getBytes(UTF_8)));\n\n        doNothing().when(pageServiceImpl).updatePageContent(anyLong(), any(byte[].class), anyString());\n\n        doReturn(pageInDb).when(pageServiceImpl).getPageByName(MY_PAGE_NAME);\n\n        @SuppressWarnings(\"unchecked\")\n        final byte[] content = IOUtil.zip(getIndexGroovyContentPair(), getPagePropertiesContentPair());\n\n        // final page\n        ImportStatus importStatus = defaultLivingApplicationImporter.importProvidedPage(MY_PAGE_NAME, content, false,\n                false, true);\n        assertThat(importStatus.getStatus()).isEqualTo(ImportStatus.Status.REPLACED);\n        importStatus = defaultLivingApplicationImporter.importProvidedPage(MY_PAGE_NAME, content, false, true, true);\n        assertThat(importStatus.getStatus()).isEqualTo(ImportStatus.Status.REPLACED);\n        importStatus = defaultLivingApplicationImporter.importProvidedPage(MY_PAGE_NAME, content, true, true, true);\n        assertThat(importStatus.getStatus()).isEqualTo(ImportStatus.Status.REPLACED);\n\n        verify(pageServiceImpl, times(3)).updatePageContent(eq(12L), eq(content), any());\n    }\n\n    @Test\n    public void should_not_replaced_an_existing_page_when_content_is_different_and_no_more_provided()\n            throws Exception {\n\n        final SPage page = new SPage();\n        page.setName(MY_PAGE_NAME);\n        page.setDescription(\"mypage description\");\n        page.setDisplayName(\"mypage display name\");\n        page.setId(12);\n        page.setProvided(false);\n\n        page.setPageHash(DigestUtils.md5DigestAsHex(\"some hash\".getBytes(UTF_8)));\n\n        doReturn(page).when(pageServiceImpl).getPageByName(MY_PAGE_NAME);\n\n        @SuppressWarnings(\"unchecked\")\n        final byte[] content = IOUtil.zip(getIndexGroovyContentPair(), getPagePropertiesContentPair());\n\n        // final page\n        ImportStatus importStatus = defaultLivingApplicationImporter.importProvidedPage(MY_PAGE_NAME, content, false,\n                false, true);\n\n        assertThat(importStatus.getStatus()).isEqualTo(ImportStatus.Status.SKIPPED);\n\n        // edit only page\n        importStatus = defaultLivingApplicationImporter.importProvidedPage(MY_PAGE_NAME, content, false, true, true);\n        assertThat(importStatus.getStatus()).isEqualTo(ImportStatus.Status.SKIPPED);\n\n        // removable page\n        importStatus = defaultLivingApplicationImporter.importProvidedPage(MY_PAGE_NAME, content, true, true, true);\n        assertThat(importStatus.getStatus()).isEqualTo(ImportStatus.Status.SKIPPED);\n\n        verify(pageServiceImpl, never()).updatePageContent(anyLong(), any(byte[].class), any());\n        verify(pageServiceImpl, never()).insertPage(any(), any(byte[].class));\n    }\n\n    @Test\n    public void import_provided_page_should_do_nothing_and_throw_exception_when_exception_happened_on_get_page_by_name()\n            throws Exception {\n\n        final SPage page = new SPage();\n        page.setName(MY_PAGE_NAME);\n        page.setDescription(\"mypage description\");\n        page.setDisplayName(\"mypage display name\");\n        page.setId(12);\n        page.setEditable(true);\n        page.setRemovable(true);\n        @SuppressWarnings(\"unchecked\")\n        final byte[] content = IOUtil.zip(getIndexGroovyContentPair(), getPagePropertiesContentPair());\n        page.setPageHash(DigestUtils.md5DigestAsHex(content));\n\n        doThrow(SBonitaReadException.class).when(pageServiceImpl).getPageByName(MY_PAGE_NAME);\n\n        Throwable expected = catchThrowable(\n                () -> defaultLivingApplicationImporter.importProvidedPage(MY_PAGE_NAME, content, true, true, true));\n\n        assertThat(expected).isInstanceOf(SBonitaException.class);\n        verify(pageServiceImpl, never()).updatePageContent(anyLong(), any(byte[].class), any());\n        verify(pageServiceImpl, never()).insertPage(any(), any(byte[].class));\n    }\n\n    private String getHashOfContent(String name) {\n        try (InputStream resourceAsStream = this.getClass().getResourceAsStream(TEST_PAGES.get(name))) {\n            if (resourceAsStream == null) {\n                throw new AssertionError(\n                        \"No content found for page \" + name + \" in classpath: \" + TEST_PAGES.get(name));\n            }\n            return DigestUtils.md5DigestAsHex(resourceAsStream.readAllBytes());\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    private Pair<String, byte[]> getIndexGroovyContentPair() {\n        return Pair.pair(INDEX_GROOVY, \"content of the groovy\".getBytes());\n    }\n\n    private Pair<String, byte[]> getPagePropertiesContentPair(final String... otherProperties) {\n        final StringBuilder stringBuilder = new StringBuilder()\n                .append(\"name=custompage_mypage\\ndisplayName=mypage display name\\ndescription=mypage description\\n\");\n        for (final String property : otherProperties) {\n            stringBuilder.append(property).append(\"\\n\");\n        }\n\n        return Pair.pair(PAGE_PROPERTIES, stringBuilder.toString().getBytes(UTF_8));\n    }\n\n    @Test\n    public void should_import_editable_default_applications_on_first_run() throws Exception {\n        //given\n        SApplicationWithIcon editableApp = new SApplicationWithIcon();\n        editableApp.setId(3);\n        editableApp.setToken(DEFAULT_APP_3_TOKEN);\n\n        // to simulate a first run:\n        defaultLivingApplicationImporter.setAddEditableApplicationsIfMissing(true);\n\n        //when\n        defaultLivingApplicationImporter.execute();\n\n        //then\n        verify(applicationImporter).importApplication(argThat(node -> node.getToken().equals(editableApp.getToken())),\n                eq(true), anyLong(), any(byte[].class), any(), eq(true), any());\n    }\n\n    @Test\n    public void should_not_import_editable_default_applications_if_not_first_run() throws Exception {\n        //given\n        SApplicationWithIcon editableApp = new SApplicationWithIcon();\n        editableApp.setId(3);\n        editableApp.setToken(DEFAULT_APP_3_TOKEN);\n\n        // to simulate a subsequent run:\n        defaultLivingApplicationImporter.setAddEditableApplicationsIfMissing(false);\n\n        //when\n        defaultLivingApplicationImporter.execute();\n\n        //then\n        verify(applicationImporter).importApplication(argThat(node -> node.getToken().equals(editableApp.getToken())),\n                eq(true), anyLong(), any(byte[].class), any(), eq(false), any());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/business/application/importer/FailOnDuplicateApplicationImportStrategyTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.importer;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\n\nimport org.bonitasoft.engine.business.application.model.SApplication;\nimport org.bonitasoft.engine.business.application.model.SApplicationWithIcon;\nimport org.junit.Test;\n\npublic class FailOnDuplicateApplicationImportStrategyTest {\n\n    private FailOnDuplicateApplicationImportStrategy strategy = new FailOnDuplicateApplicationImportStrategy();\n\n    @Test\n    public void whenApplicationExists_should_return_fail() throws Exception {\n        //given\n        SApplication existingApplication = mock(SApplication.class);\n        SApplicationWithIcon toBeImported = mock(SApplicationWithIcon.class);\n\n        //when\n        ApplicationImportStrategy.ImportStrategy importStrategy = strategy.whenApplicationExists(existingApplication,\n                toBeImported);\n\n        assertThat(importStrategy).isEqualTo(ApplicationImportStrategy.ImportStrategy.FAIL);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/business/application/importer/ImportResultTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.importer;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\n\nimport org.bonitasoft.engine.api.ImportStatus;\nimport org.bonitasoft.engine.business.application.model.SApplicationWithIcon;\nimport org.junit.Test;\n\npublic class ImportResultTest {\n\n    @Test\n    public void getImportedObject_should_return_object_passed_by_constructor() throws Exception {\n        //given\n        final SApplicationWithIcon application = mock(SApplicationWithIcon.class);\n        final ImportResult importResult = new ImportResult(application, mock(ImportStatus.class));\n\n        // when\n        final SApplicationWithIcon retrievedApp = importResult.getApplication();\n\n        //then\n        assertThat(retrievedApp).isEqualTo(application);\n    }\n\n    @Test\n    public void getImportStatus() throws Exception {\n        //given\n        final ImportStatus status = mock(ImportStatus.class);\n        final ImportResult importResult = new ImportResult(mock(SApplicationWithIcon.class), status);\n\n        //when\n        final ImportStatus retrievedStatus = importResult.getImportStatus();\n\n        //then\n        assertThat(retrievedStatus).isEqualTo(status);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/business/application/importer/MandatoryLivingApplicationImporterTest.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.importer;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.fail;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.*;\n\nimport java.io.InputStream;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.ImportStatus;\nimport org.bonitasoft.engine.authorization.PermissionService;\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.converter.NodeToApplicationConverter;\nimport org.bonitasoft.engine.business.application.model.SApplicationWithIcon;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.exception.ImportException;\nimport org.bonitasoft.engine.page.ContentType;\nimport org.bonitasoft.engine.page.PageServiceListener;\nimport org.bonitasoft.engine.page.SPage;\nimport org.bonitasoft.engine.page.impl.PageServiceImpl;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\nimport org.springframework.util.DigestUtils;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class MandatoryLivingApplicationImporterTest {\n\n    private static final String CONTENT_NAME = \"content.zip\";\n    private static final String HTML_EXAMPLE_EDITONLY_PAGE_NAME = \"custompage_htmlexample_editonly\";\n    private static final String HTML_EXAMPLE_EDITONLY_ZIP_NAME = \"bonita-html-page-example-editonly.zip\";\n    private static final String HTML_EXAMPLE_FINAL_PAGE_NAME = \"custompage_htmlexample_final\";\n    private static final String HTML_EXAMPLE_FINAL_ZIP_NAME = \"bonita-html-page-example-final.zip\";\n    private static final String DEFAULT_APP_1_TOKEN = \"default_app_1\";\n    private static final String DEFAULT_APP_2_TOKEN = \"default_app_2\";\n\n    private static final Map<String, String> TEST_PAGES = Map.of(\n            HTML_EXAMPLE_EDITONLY_PAGE_NAME, \"/org/bonitasoft/web/page/editonly/\" + HTML_EXAMPLE_EDITONLY_ZIP_NAME,\n            HTML_EXAMPLE_FINAL_PAGE_NAME, \"/org/bonitasoft/web/page/final/\" + HTML_EXAMPLE_FINAL_ZIP_NAME);\n\n    @Mock\n    private Recorder recorder;\n    @Mock\n    private ReadPersistenceService readPersistenceService;\n    @Mock\n    private QueriableLoggerService queriableLoggerService;\n    @Mock\n    private ReadSessionAccessor sessionAccessor;\n    @Mock\n    private SessionService sessionService;\n    @Mock\n    private PermissionService permissionService;\n    @Mock\n    private PageServiceListener apiExtensionPageServiceListener;\n\n    @Mock\n    private ApplicationService applicationService;\n    @Mock\n    private NodeToApplicationConverter nodeToApplicationConverter;\n    @Mock\n    private ApplicationPageImporter applicationPageImporter;\n    @Mock\n    private ApplicationMenuImporter applicationMenuImporter;\n\n    @Captor\n    private ArgumentCaptor<SPage> pageArgumentCaptor;\n\n    private PageServiceImpl pageServiceImpl;\n    private ApplicationImporter applicationImporter;\n    private MandatoryLivingApplicationImporter mandatoryLivingApplicationImporter;\n    @Mock\n    private DefaultLivingApplicationImporter defaultLivingApplicationImporter;\n\n    @Before\n    public void before() {\n        pageServiceImpl = spy(\n                new PageServiceImpl(readPersistenceService, recorder, queriableLoggerService, sessionAccessor,\n                        sessionService, permissionService));\n        pageServiceImpl.setPageServiceListeners(Collections.singletonList(apiExtensionPageServiceListener));\n\n        applicationImporter = spy(\n                new ApplicationImporter(applicationService, nodeToApplicationConverter, applicationPageImporter,\n                        applicationMenuImporter));\n\n        mandatoryLivingApplicationImporter = spy(\n                new MandatoryLivingApplicationImporter(pageServiceImpl, applicationImporter,\n                        defaultLivingApplicationImporter));\n    }\n\n    private String getHashOfContent(String name) {\n        try (InputStream resourceAsStream = this.getClass().getResourceAsStream(TEST_PAGES.get(name))) {\n            if (resourceAsStream == null) {\n                throw new AssertionError(\n                        \"No content found for page \" + name + \" in classpath: \" + TEST_PAGES.get(name));\n            }\n            return DigestUtils.md5DigestAsHex(resourceAsStream.readAllBytes());\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    @Test\n    public void init_should_import_all_mandatory_provided_pages() throws SBonitaException {\n        // given\n        final Map<String, String> map = new HashMap<>();\n        map.put(HTML_EXAMPLE_EDITONLY_PAGE_NAME, ContentType.PAGE);\n        map.put(HTML_EXAMPLE_FINAL_PAGE_NAME, ContentType.PAGE);\n        doAnswer(invocation -> invocation.getArguments()[0]).when(pageServiceImpl)\n                .insertPage(pageArgumentCaptor.capture(), any());\n\n        // when\n        mandatoryLivingApplicationImporter.init();\n\n        // then\n        List<SPage> insertedPages = pageArgumentCaptor.getAllValues();\n        assertThat(insertedPages)\n                .hasSize(2)\n                .allSatisfy(insertedPage -> {\n                    String name = insertedPage.getName();\n                    assertThat(name).isIn(map.keySet());\n                    assertThat(insertedPage.getContentType()).isEqualTo(map.get(name));\n                    assertThat(insertedPage.getPageHash()).isEqualTo(getHashOfContent(name));\n                    if (name.equals(HTML_EXAMPLE_EDITONLY_PAGE_NAME)) {\n                        assertThat(insertedPage.isEditable()).isTrue();\n                        assertThat(insertedPage.isProvided()).isTrue();\n                        assertThat(insertedPage.isRemovable()).isFalse();\n                    } else if (name.equals(HTML_EXAMPLE_FINAL_PAGE_NAME)) {\n                        assertThat(insertedPage.isEditable()).isFalse();\n                        assertThat(insertedPage.isProvided()).isTrue();\n                        assertThat(insertedPage.isRemovable()).isFalse();\n                    } else {\n                        fail(\"Unknown page name: {}\", name);\n                    }\n                });\n\n        verify(defaultLivingApplicationImporter).setAddRemovablePagesIfMissing(eq(true));\n    }\n\n    @Test\n    public void init_should_not_add_removable_pages_if_missing() throws SBonitaException {\n        // given\n        ImportStatus importStatus = new ImportStatus(HTML_EXAMPLE_FINAL_PAGE_NAME);\n        importStatus.setStatus(ImportStatus.Status.SKIPPED);\n        doReturn(importStatus).when(mandatoryLivingApplicationImporter).importProvidedPage(\n                eq(HTML_EXAMPLE_FINAL_ZIP_NAME),\n                any(byte[].class), eq(false), eq(false), eq(true));\n        doReturn(importStatus).when(mandatoryLivingApplicationImporter).importProvidedPage(\n                eq(HTML_EXAMPLE_EDITONLY_ZIP_NAME),\n                any(byte[].class), eq(false), eq(true), eq(true));\n\n        // when\n        mandatoryLivingApplicationImporter.init();\n\n        // then\n        verify(defaultLivingApplicationImporter).setAddRemovablePagesIfMissing(eq(false));\n    }\n\n    @Test\n    public void init_should_not_insert_anything_if_exception_happened_on_import_final_page() throws Exception {\n        // given\n        final Map<String, String> notImportedPages = new HashMap<>();\n        notImportedPages.put(HTML_EXAMPLE_EDITONLY_ZIP_NAME, ContentType.THEME);\n\n        doThrow(new SBonitaReadException(\"Mock exception\")).when(mandatoryLivingApplicationImporter)\n                .importProvidedPage(eq(HTML_EXAMPLE_FINAL_ZIP_NAME), any(byte[].class), eq(false), eq(false),\n                        eq(true));\n\n        // when\n        mandatoryLivingApplicationImporter.init();\n\n        // then\n        verify(mandatoryLivingApplicationImporter, never()).importProvidedPage(\n                argThat(notImportedPages::containsKey),\n                any(byte[].class), eq(false), eq(true), anyBoolean());\n        verify(defaultLivingApplicationImporter, never()).setAddRemovablePagesIfMissing(eq(true));\n    }\n\n    @Test\n    public void init_should_update_final_page_if_is_different() throws SBonitaException {\n        // given\n        // resource in the classpath provided-page.properties and provided-page.zip\n        final SPage currentGroovyPage = new SPage(HTML_EXAMPLE_FINAL_PAGE_NAME, \"example\", \"example\",\n                System.currentTimeMillis(), SessionService.SYSTEM_ID, true, System.currentTimeMillis(),\n                SessionService.SYSTEM_ID, CONTENT_NAME);\n        currentGroovyPage.setId(12L);\n        currentGroovyPage.setRemovable(false);\n        currentGroovyPage.setEditable(false);\n\n        doReturn(currentGroovyPage).when(pageServiceImpl).getPageByName(HTML_EXAMPLE_FINAL_PAGE_NAME);\n        doNothing().when(pageServiceImpl).updatePageContent(anyLong(), any(byte[].class), anyString());\n\n        // when\n        mandatoryLivingApplicationImporter.init();\n\n        // then\n        verify(pageServiceImpl).updatePageContent(eq(12L), any(byte[].class),\n                eq(HTML_EXAMPLE_FINAL_ZIP_NAME));\n    }\n\n    @Test\n    public void init_should_update_edit_only_page_if_is_different() throws SBonitaException {\n        // given\n        // resource in the classpath provided-page.properties and provided-page.zip\n        final SPage currentGroovyPage = new SPage(HTML_EXAMPLE_EDITONLY_PAGE_NAME, \"example\", \"example\",\n                System.currentTimeMillis(), SessionService.SYSTEM_ID, true, System.currentTimeMillis(),\n                SessionService.SYSTEM_ID, CONTENT_NAME);\n        currentGroovyPage.setId(12L);\n        currentGroovyPage.setRemovable(false);\n        currentGroovyPage.setEditable(true);\n\n        doReturn(currentGroovyPage).when(pageServiceImpl).getPageByName(HTML_EXAMPLE_EDITONLY_PAGE_NAME);\n        doNothing().when(pageServiceImpl).updatePageContent(anyLong(), any(byte[].class), anyString());\n\n        // when\n        mandatoryLivingApplicationImporter.init();\n\n        // then\n        verify(pageServiceImpl).updatePageContent(eq(12L), any(byte[].class),\n                eq(HTML_EXAMPLE_EDITONLY_ZIP_NAME));\n    }\n\n    @Test\n    public void init_should_do_nothing_if_non_editable_non_removable_already_here_and_the_same()\n            throws SBonitaException {\n        final SPage currentHomePage = new SPage(HTML_EXAMPLE_EDITONLY_PAGE_NAME, \"example\", \"example\",\n                System.currentTimeMillis(), SessionService.SYSTEM_ID, true, System.currentTimeMillis(),\n                SessionService.SYSTEM_ID, CONTENT_NAME);\n        currentHomePage.setId(14);\n        currentHomePage.setEditable(false);\n        currentHomePage.setRemovable(false);\n        currentHomePage.setPageHash(getHashOfContent(HTML_EXAMPLE_EDITONLY_PAGE_NAME));\n        doReturn(currentHomePage).when(pageServiceImpl).getPageByName(HTML_EXAMPLE_EDITONLY_PAGE_NAME);\n\n        // when\n        mandatoryLivingApplicationImporter.init();\n\n        // then\n        verify(pageServiceImpl, never()).insertPage(\n                argThat(sPage -> sPage.getName().equals(HTML_EXAMPLE_EDITONLY_PAGE_NAME)),\n                any(byte[].class));\n        verify(pageServiceImpl, never()).updatePage(eq(14), any(EntityUpdateDescriptor.class));\n        verify(pageServiceImpl, never()).updatePageContent(eq(14), any(byte[].class), anyString());\n    }\n\n    @Test\n    public void init_should_import_all_default_applications_on_first_run() throws Exception {\n        //given\n        SApplicationWithIcon finalApp1 = new SApplicationWithIcon();\n        finalApp1.setId(1);\n        finalApp1.setToken(DEFAULT_APP_1_TOKEN);\n        SApplicationWithIcon finalApp2 = new SApplicationWithIcon();\n        finalApp2.setId(2);\n        finalApp2.setToken(DEFAULT_APP_2_TOKEN);\n\n        ImportStatus app1ImportStatus = new ImportStatus(finalApp1.getToken());\n        app1ImportStatus.setStatus(ImportStatus.Status.ADDED);\n        ImportStatus app2ImportStatus = new ImportStatus(finalApp2.getToken());\n        app2ImportStatus.setStatus(ImportStatus.Status.ADDED);\n\n        doReturn(app1ImportStatus).when(applicationImporter)\n                .importApplication(argThat(node -> node.getToken().equals(finalApp1.getToken())), eq(false), anyLong(),\n                        any(byte[].class), any(), eq(true), any());\n        doReturn(app2ImportStatus).when(applicationImporter)\n                .importApplication(argThat(node -> node.getToken().equals(finalApp2.getToken())), eq(false), anyLong(),\n                        any(byte[].class), any(), eq(true), any());\n\n        // when\n        mandatoryLivingApplicationImporter.init();\n\n        // then\n        verify(applicationImporter).importApplication(argThat(node -> node.getToken().equals(finalApp1.getToken())),\n                eq(false), anyLong(), any(byte[].class), any(), eq(true), any());\n        verify(applicationImporter).importApplication(argThat(node -> node.getToken().equals(finalApp2.getToken())),\n                eq(false), anyLong(), any(byte[].class), any(), eq(true), any());\n        verify(defaultLivingApplicationImporter).setAddEditableApplicationsIfMissing(eq(true));\n    }\n\n    @Test\n    public void init_should_not_import_editable_default_applications_if_not_first_run() throws Exception {\n        // given\n        SApplicationWithIcon finalApp1 = new SApplicationWithIcon();\n        finalApp1.setId(1);\n        finalApp1.setToken(DEFAULT_APP_1_TOKEN);\n        SApplicationWithIcon finalApp2 = new SApplicationWithIcon();\n        finalApp2.setId(2);\n        finalApp2.setToken(DEFAULT_APP_2_TOKEN);\n\n        ImportStatus app1ImportStatus = new ImportStatus(finalApp1.getToken());\n        app1ImportStatus.setStatus(ImportStatus.Status.SKIPPED);\n        ImportStatus app2ImportStatus = new ImportStatus(finalApp2.getToken());\n        app2ImportStatus.setStatus(ImportStatus.Status.REPLACED);\n\n        doReturn(app1ImportStatus).when(applicationImporter)\n                .importApplication(argThat(node -> node.getToken().equals(finalApp1.getToken())), eq(false), anyLong(),\n                        any(byte[].class), any(), eq(true), any());\n        doReturn(app2ImportStatus).when(applicationImporter)\n                .importApplication(argThat(node -> node.getToken().equals(finalApp2.getToken())), eq(false), anyLong(),\n                        any(byte[].class), any(), eq(true), any());\n\n        // when\n        mandatoryLivingApplicationImporter.init();\n\n        // then\n        verify(applicationImporter).importApplication(argThat(node -> node.getToken().equals(finalApp1.getToken())),\n                eq(false), anyLong(), any(byte[].class), any(), eq(true), any());\n        verify(applicationImporter).importApplication(argThat(node -> node.getToken().equals(finalApp2.getToken())),\n                eq(false), anyLong(), any(byte[].class), any(), eq(true), any());\n        verify(defaultLivingApplicationImporter).setAddEditableApplicationsIfMissing(eq(false));\n    }\n\n    @Test\n    public void init_should_not_import_editable_default_applications_if_all_final_are_skipped() throws Exception {\n        // given\n        SApplicationWithIcon finalApp1 = new SApplicationWithIcon();\n        finalApp1.setId(1);\n        finalApp1.setToken(DEFAULT_APP_1_TOKEN);\n        SApplicationWithIcon finalApp2 = new SApplicationWithIcon();\n        finalApp2.setId(2);\n        finalApp2.setToken(DEFAULT_APP_2_TOKEN);\n\n        ImportStatus app1ImportStatus = new ImportStatus(finalApp1.getToken());\n        app1ImportStatus.setStatus(ImportStatus.Status.SKIPPED);\n        ImportStatus app2ImportStatus = new ImportStatus(finalApp2.getToken());\n        app2ImportStatus.setStatus(ImportStatus.Status.SKIPPED);\n\n        doReturn(app1ImportStatus).when(applicationImporter)\n                .importApplication(argThat(node -> node.getToken().equals(finalApp1.getToken())), eq(false), anyLong(),\n                        any(byte[].class), any(), eq(true), any());\n        doReturn(app2ImportStatus).when(applicationImporter)\n                .importApplication(argThat(node -> node.getToken().equals(finalApp2.getToken())), eq(false), anyLong(),\n                        any(byte[].class), any(), eq(true), any());\n\n        // when\n        mandatoryLivingApplicationImporter.init();\n\n        // then\n        verify(applicationImporter).importApplication(argThat(node -> node.getToken().equals(finalApp1.getToken())),\n                eq(false), anyLong(), any(byte[].class), any(), eq(true), any());\n        verify(applicationImporter).importApplication(argThat(node -> node.getToken().equals(finalApp2.getToken())),\n                eq(false), anyLong(), any(byte[].class), any(), eq(true), any());\n        verify(defaultLivingApplicationImporter).setAddEditableApplicationsIfMissing(eq(false));\n    }\n\n    @Test\n    public void init_should_import_editable_default_applications_if_final_apps_are_added_or_updated() throws Exception {\n        // given\n        SApplicationWithIcon finalApp1 = new SApplicationWithIcon();\n        finalApp1.setId(1);\n        finalApp1.setToken(DEFAULT_APP_1_TOKEN);\n        SApplicationWithIcon finalApp2 = new SApplicationWithIcon();\n        finalApp2.setId(2);\n        finalApp2.setToken(DEFAULT_APP_2_TOKEN);\n\n        ImportStatus app1ImportStatus = new ImportStatus(finalApp1.getToken());\n        app1ImportStatus.setStatus(ImportStatus.Status.ADDED);\n        ImportStatus app2ImportStatus = new ImportStatus(finalApp2.getToken());\n        app2ImportStatus.setStatus(ImportStatus.Status.REPLACED);\n\n        doReturn(app1ImportStatus).when(applicationImporter)\n                .importApplication(argThat(node -> node.getToken().equals(finalApp1.getToken())), eq(false), anyLong(),\n                        any(byte[].class), any(), eq(true), any());\n        doReturn(app2ImportStatus).when(applicationImporter)\n                .importApplication(argThat(node -> node.getToken().equals(finalApp2.getToken())), eq(false), anyLong(),\n                        any(byte[].class), any(), eq(true), any());\n\n        // when\n        mandatoryLivingApplicationImporter.init();\n\n        // then\n        verify(applicationImporter).importApplication(argThat(node -> node.getToken().equals(finalApp1.getToken())),\n                eq(false), anyLong(), any(byte[].class), any(), eq(true), any());\n        verify(applicationImporter).importApplication(argThat(node -> node.getToken().equals(finalApp2.getToken())),\n                eq(false), anyLong(), any(byte[].class), any(), eq(true), any());\n        verify(defaultLivingApplicationImporter).setAddEditableApplicationsIfMissing(eq(true));\n    }\n\n    @Test\n    public void init_should_not_import_default_app_when_importApplication_throw_exception() throws Exception {\n        // given\n        SApplicationWithIcon finalApp1 = new SApplicationWithIcon();\n        finalApp1.setId(1);\n        finalApp1.setToken(DEFAULT_APP_1_TOKEN);\n        SApplicationWithIcon finalApp2 = new SApplicationWithIcon();\n        finalApp2.setId(2);\n        finalApp2.setToken(DEFAULT_APP_2_TOKEN);\n\n        ImportStatus app1ImportStatus = new ImportStatus(finalApp1.getToken());\n        app1ImportStatus.setStatus(ImportStatus.Status.ADDED);\n\n        doReturn(app1ImportStatus).when(applicationImporter)\n                .importApplication(argThat(node -> node.getToken().equals(finalApp1.getToken())), eq(false), anyLong(),\n                        any(byte[].class), any(), eq(true), any());\n        doThrow(new ImportException(\"Mock exception\")).when(applicationImporter).importApplication(\n                argThat(node -> node.getToken().equals(finalApp2.getToken())), eq(false), anyLong(), any(byte[].class),\n                any(), eq(true), any());\n\n        // when\n        mandatoryLivingApplicationImporter.init();\n\n        // then\n        verify(applicationImporter).importApplication(argThat(node -> node.getToken().equals(finalApp1.getToken())),\n                eq(false), anyLong(), any(byte[].class), any(), eq(true), any());\n        verify(applicationImporter).importApplication(argThat(node -> node.getToken().equals(finalApp2.getToken())),\n                eq(false), anyLong(), any(byte[].class), any(), eq(true), any());\n        verify(defaultLivingApplicationImporter, never()).setAddEditableApplicationsIfMissing(eq(true));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/business/application/importer/ReplaceDuplicateApplicationImportStrategyTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.importer;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.business.application.model.SApplication;\nimport org.bonitasoft.engine.business.application.model.SApplicationWithIcon;\nimport org.junit.Test;\n\n/**\n * @author Emmanuel Duchastenier\n */\n\npublic class ReplaceDuplicateApplicationImportStrategyTest {\n\n    private ReplaceDuplicateApplicationImportStrategy strategy = new ReplaceDuplicateApplicationImportStrategy();\n\n    @Test\n    public void whenApplicationExists_should_return_replace() throws Exception {\n        // given:\n        final long applicationId = 123L;\n        final SApplication existingApplication = new SApplication();\n        existingApplication.setId(applicationId);\n\n        final SApplicationWithIcon applicationToImport = new SApplicationWithIcon();\n        applicationToImport.setId(98745L);\n\n        // when:\n        ApplicationImportStrategy.ImportStrategy importStrategy = strategy.whenApplicationExists(existingApplication,\n                applicationToImport);\n\n        // then:\n        assertThat(importStrategy).isEqualTo(ApplicationImportStrategy.ImportStrategy.REPLACE);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/business/application/importer/StrategySelectorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.importer;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.business.application.ApplicationImportPolicy;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class StrategySelectorTest {\n\n    @InjectMocks\n    private StrategySelector selector;\n\n    @Test\n    public void selectStrategy_should_return_instance_of_FailOnDuplicate_when_policy_is_FailOnDuplicate() {\n        //when\n        ApplicationImportStrategy strategy = selector.selectStrategy(ApplicationImportPolicy.FAIL_ON_DUPLICATES);\n\n        //then\n        assertThat(strategy).isInstanceOf(FailOnDuplicateApplicationImportStrategy.class);\n    }\n\n    @Test\n    public void selectStrategy_should_return_instance_of_ReplaceDuplicates_when_policy_is_ReplaceDuplicates() {\n        //when\n        ApplicationImportStrategy strategy = selector.selectStrategy(ApplicationImportPolicy.REPLACE_DUPLICATES);\n\n        //then\n        assertThat(strategy).isInstanceOf(ReplaceDuplicateApplicationImportStrategy.class);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/business/application/importer/UpdateNewerNonEditableApplicationStrategyTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.importer;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.business.application.model.SApplication;\nimport org.bonitasoft.engine.business.application.model.SApplicationWithIcon;\nimport org.junit.Test;\n\n/**\n * @author Emmanuel Duchastenier\n */\n\npublic class UpdateNewerNonEditableApplicationStrategyTest {\n\n    private UpdateNewerNonEditableApplicationStrategy strategy = new UpdateNewerNonEditableApplicationStrategy();\n\n    @Test\n    public void whenApplicationExists_should_return_Replace_when_existing_application_is_not_editable_and_version_different()\n            throws Exception {\n        // given:\n        final long applicationId = 123L;\n        final SApplication existingApplication = new SApplication();\n        existingApplication.setId(applicationId);\n        existingApplication.setEditable(false);\n        existingApplication.setVersion(\"v1\");\n\n        final SApplicationWithIcon applicationToImport = new SApplicationWithIcon();\n        applicationToImport.setId(98745L);\n        applicationToImport.setEditable(false);\n        applicationToImport.setVersion(\"v2\");\n        // when:\n        ApplicationImportStrategy.ImportStrategy importStrategy = strategy.whenApplicationExists(existingApplication,\n                applicationToImport);\n\n        // then:\n        assertThat(importStrategy).isEqualTo(ApplicationImportStrategy.ImportStrategy.REPLACE);\n    }\n\n    @Test\n    public void whenApplicationExists_should_return_skip_when_existing_application_is_not_editable_and_version_equals()\n            throws Exception {\n        // given:\n        final long applicationId = 123L;\n        final SApplication existingApplication = new SApplication();\n        existingApplication.setId(applicationId);\n        existingApplication.setEditable(false);\n        existingApplication.setVersion(\"v1\");\n\n        final SApplicationWithIcon applicationToImport = new SApplicationWithIcon();\n        applicationToImport.setId(98745L);\n        applicationToImport.setEditable(false);\n        applicationToImport.setVersion(\"v1\");\n        // when:\n        ApplicationImportStrategy.ImportStrategy importStrategy = strategy.whenApplicationExists(existingApplication,\n                applicationToImport);\n\n        // then:\n        assertThat(importStrategy).isEqualTo(ApplicationImportStrategy.ImportStrategy.SKIP);\n    }\n\n    @Test\n    public void whenApplicationExists_should_return_skip_when_existing_application_is_editable() throws Exception {\n        // given:\n        final long applicationId = 123L;\n        final SApplication existingApplication = new SApplication();\n        existingApplication.setId(applicationId);\n        existingApplication.setEditable(true);\n        existingApplication.setVersion(\"v1\");\n\n        final SApplicationWithIcon applicationToImport = new SApplicationWithIcon();\n        applicationToImport.setId(98745L);\n        applicationToImport.setEditable(true);\n        applicationToImport.setVersion(\"v2\");\n        // when:\n        ApplicationImportStrategy.ImportStrategy importStrategy = strategy.whenApplicationExists(existingApplication,\n                applicationToImport);\n\n        // then:\n        assertThat(importStrategy).isEqualTo(ApplicationImportStrategy.ImportStrategy.SKIP);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/business/application/importer/validator/ApplicationImportValidatorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.importer.validator;\n\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verify;\n\nimport org.bonitasoft.engine.exception.ImportException;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ApplicationImportValidatorTest {\n\n    @Mock\n    private ApplicationTokenValidator tokenValidator;\n\n    @InjectMocks\n    private ApplicationImportValidator importValidator;\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void validate_should_do_nothing_when_token_is_valid() throws Exception {\n        //given\n        given(tokenValidator.validate(\"aToken\")).willReturn(new ValidationStatus(true));\n\n        //when\n        importValidator.validate(\"aToken\");\n\n        //then\n        verify(tokenValidator).validate(\"aToken\");\n    }\n\n    @Test\n    public void validate_should_throw_ImportException_when_token_is_invalid() throws Exception {\n        //given\n        given(tokenValidator.validate(\"aToken\")).willReturn(new ValidationStatus(false, \"Invalid\"));\n\n        //then\n        expectedException.expect(ImportException.class);\n        expectedException.expectMessage(\"Invalid\");\n\n        //when\n        importValidator.validate(\"aToken\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/business/application/importer/validator/ApplicationMenuCreatorValidatorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.importer.validator;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.business.application.ApplicationMenuCreator;\nimport org.junit.Test;\n\npublic class ApplicationMenuCreatorValidatorTest {\n\n    private final ApplicationMenuCreatorValidator validator = new ApplicationMenuCreatorValidator();\n\n    @Test\n    public void isValid_should_return_empty_list_if_all_mandatory_fields_are_filled() throws Exception {\n        //given\n        final ApplicationMenuCreator creator = new ApplicationMenuCreator(5L, \"main\");\n\n        //when\n        assertThat(validator.isValid(creator)).isEmpty();\n    }\n\n    @Test\n    public void isValid_should_return_errors_if_applicationId_is_null() throws Exception {\n        //given\n        final ApplicationMenuCreator creator = new ApplicationMenuCreator(null, \"main\");\n\n        //when\n        List<String> errors = validator.isValid(creator);\n\n        //then\n        assertThat(errors).isNotEmpty();\n        assertThat(errors).containsExactly(\"The applicationId cannot be null\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/business/application/importer/validator/ApplicationTokenValidatorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.importer.validator;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.Test;\n\npublic class ApplicationTokenValidatorTest {\n\n    private ApplicationTokenValidator validator = new ApplicationTokenValidator();\n\n    @Test\n    public void isValid_should_return_true_if_value_is_alphanumeric() throws Exception {\n        //when\n        final ValidationStatus valid = validator.validate(\"123name\");\n\n        //then\n        assertThat(valid.isValid()).isTrue();\n    }\n\n    @Test\n    public void isValid_should_return_false_if_value_contains_special_characters() throws Exception {\n        //when\n        final ValidationStatus valid = validator.validate(\"123/name\");\n\n        //then\n        assertThat(valid.isValid()).isFalse();\n        assertThat(valid.getMessage()).isEqualTo(getMessage(\"123/name\"));\n    }\n\n    @Test\n    public void isValid_should_return_false_if_value_contains_space() throws Exception {\n        //when\n        final ValidationStatus valid = validator.validate(\"my name\");\n\n        //then\n        assertThat(valid.isValid()).isFalse();\n    }\n\n    @Test\n    public void value_can_contains_hyphen() throws Exception {\n        //when\n        final ValidationStatus valid = validator.validate(\"my-name\");\n\n        //then\n        assertThat(valid.isValid()).isTrue();\n    }\n\n    @Test\n    public void value_can_contains_dot() throws Exception {\n        //when\n        final ValidationStatus valid = validator.validate(\"my.name\");\n\n        //then\n        assertThat(valid.isValid()).isTrue();\n    }\n\n    @Test\n    public void value_can_contains_underscore() throws Exception {\n        //when\n        final ValidationStatus valid = validator.validate(\"my_name\");\n\n        //then\n        assertThat(valid.isValid()).isTrue();\n    }\n\n    @Test\n    public void value_can_contains_tilde() throws Exception {\n        //when\n        final ValidationStatus valid = validator.validate(\"my~name\");\n\n        //then\n        assertThat(valid.isValid()).isTrue();\n    }\n\n    @Test\n    public void value_can_contains_all_authorized_symbols() throws Exception {\n        //when\n        final ValidationStatus valid = validator.validate(\"m-y.n_a~-m.e\");\n\n        //then\n        assertThat(valid.isValid()).isTrue();\n    }\n\n    @Test\n    public void value_cannot_be_empty() throws Exception {\n        //when\n        final ValidationStatus valid = validator.validate(\"\");\n\n        //then\n        ValidationStatusAssert.assertThat(valid).isNotValid().hasMessage(getMessage(\"\"));\n\n    }\n\n    @Test\n    public void value_cannot_contain_only_white_spaces() throws Exception {\n        //when\n        final ValidationStatus valid = validator.validate(\"  \");\n\n        //then\n        ValidationStatusAssert.assertThat(valid).isNotValid().hasMessage(getMessage(\"  \"));\n\n    }\n\n    @Test\n    public void value_cannot_be_null() throws Exception {\n        //when\n        final ValidationStatus valid = validator.validate(null);\n\n        //then\n        ValidationStatusAssert.assertThat(valid).isNotValid().hasMessage(getMessage(null));\n    }\n\n    @Test\n    public void should_be_invalid_if_token_contains_keyword_content_lower_case() throws Exception {\n        //when\n        ValidationStatus status = validator.validate(\"content\");\n\n        //then\n        ValidationStatusAssert.assertThat(status).isNotValid()\n                .hasMessage(getMessage(\"content\"));\n    }\n\n    @Test\n    public void should_be_invalid_if_token_contains_keyword_content_up_case() throws Exception {\n        //when\n        String token = \"CONTENT\";\n        ValidationStatus status = validator.validate(token);\n\n        //then\n        ValidationStatusAssert.assertThat(status).isNotValid()\n                .hasMessage(getMessage(\"CONTENT\"));\n    }\n\n    @Test\n    public void should_be_invalid_if_token_contains_keyword_api_lower_case() throws Exception {\n        //when\n        ValidationStatus status = validator.validate(\"api\");\n\n        //then\n        ValidationStatusAssert.assertThat(status).isNotValid()\n                .hasMessage(getMessage(\"api\"));\n    }\n\n    public String getMessage(String token) {\n        StringBuilder stb = new StringBuilder(\"The token '\");\n        stb.append(token);\n        stb.append(\n                \"' is invalid: the token can not be null or empty and should contain only alpha numeric characters and the following \");\n        stb.append(\n                \"special characters '-', '.', '_' or '~'. In addition, the following words are reserved keywords and cannot be used as token: 'api', 'content', 'theme'.\");\n        return stb.toString();\n    }\n\n    @Test\n    public void should_be_invalid_if_token_contains_keyword_api_up_case() throws Exception {\n        //when\n        ValidationStatus status = validator.validate(\"API\");\n\n        //then\n        ValidationStatusAssert.assertThat(status).isNotValid()\n                .hasMessage(getMessage(\"API\"));\n    }\n\n    @Test\n    public void should_be_invalid_if_token_contains_keyword_theme_lower_case() throws Exception {\n        //when\n        ValidationStatus status = validator.validate(\"theme\");\n\n        //then\n        ValidationStatusAssert.assertThat(status).isNotValid()\n                .hasMessage(getMessage(\"theme\"));\n    }\n\n    @Test\n    public void should_be_invalid_if_token_contains_keyword_theme_up_case() throws Exception {\n        //when\n        ValidationStatus status = validator.validate(\"THEME\");\n\n        //then\n        ValidationStatusAssert.assertThat(status).isNotValid()\n                .hasMessage(getMessage(\"THEME\"));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/business/application/importer/validator/ValidationStatusAssert.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.importer.validator;\n\nimport org.assertj.core.api.AbstractAssert;\nimport org.assertj.core.util.Objects;\n\n/**\n * {@link ValidationStatus} specific assertions - Generated by CustomAssertionGenerator.\n */\npublic class ValidationStatusAssert extends AbstractAssert<ValidationStatusAssert, ValidationStatus> {\n\n    /**\n     * Creates a new <code>{@link ValidationStatusAssert}</code> to make assertions on actual ValidationStatus.\n     *\n     * @param actual the ValidationStatus we want to make assertions on.\n     */\n    public ValidationStatusAssert(ValidationStatus actual) {\n        super(actual, ValidationStatusAssert.class);\n    }\n\n    /**\n     * An entry point for ValidationStatusAssert to follow AssertJ standard <code>assertThat()</code> statements.<br>\n     * With a static import, one can write directly: <code>assertThat(myValidationStatus)</code> and get specific\n     * assertion with code completion.\n     *\n     * @param actual the ValidationStatus we want to make assertions on.\n     * @return a new <code>{@link ValidationStatusAssert}</code>\n     */\n    public static ValidationStatusAssert assertThat(ValidationStatus actual) {\n        return new ValidationStatusAssert(actual);\n    }\n\n    /**\n     * Verifies that the actual ValidationStatus's message is equal to the given one.\n     *\n     * @param message the given message to compare the actual ValidationStatus's message to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual ValidationStatus's message is not equal to the given one.\n     */\n    public ValidationStatusAssert hasMessage(String message) {\n        // check that actual ValidationStatus we want to make assertions on is not null.\n        isNotNull();\n\n        // overrides the default error message with a more explicit one\n        String assertjErrorMessage = \"\\nExpected message of:\\n  <%s>\\nto be:\\n  <%s>\\nbut was:\\n  <%s>\";\n\n        // null safe check\n        String actualMessage = actual.getMessage();\n        if (!Objects.areEqual(actualMessage, message)) {\n            failWithMessage(assertjErrorMessage, actual, message, actualMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual ValidationStatus is valid.\n     *\n     * @return this assertion object.\n     * @throws AssertionError - if the actual ValidationStatus is not valid.\n     */\n    public ValidationStatusAssert isValid() {\n        // check that actual ValidationStatus we want to make assertions on is not null.\n        isNotNull();\n\n        // check\n        if (!actual.isValid()) {\n            failWithMessage(\"\\nExpected actual ValidationStatus to be valid but was not.\");\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual ValidationStatus is not valid.\n     *\n     * @return this assertion object.\n     * @throws AssertionError - if the actual ValidationStatus is valid.\n     */\n    public ValidationStatusAssert isNotValid() {\n        // check that actual ValidationStatus we want to make assertions on is not null.\n        isNotNull();\n\n        // check\n        if (actual.isValid()) {\n            failWithMessage(\"\\nExpected actual ValidationStatus not to be valid but was.\");\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/business/application/importer/validator/ValidationStatusTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.importer.validator;\n\nimport static org.bonitasoft.engine.business.application.importer.validator.ValidationStatusAssert.assertThat;\n\nimport org.junit.Test;\n\npublic class ValidationStatusTest {\n\n    @Test\n    public void should_create_valid_status_with_no_message() throws Exception {\n        //given\n        ValidationStatus status = new ValidationStatus(true);\n\n        //then\n        assertThat(status).isValid().hasMessage(null);\n    }\n\n    @Test\n    public void should_create_invalid_status_with_no_message() throws Exception {\n        //given\n        ValidationStatus status = new ValidationStatus(false);\n\n        //then\n        assertThat(status).isNotValid().hasMessage(null);\n    }\n\n    @Test\n    public void should_create_invalid_status_with_message() throws Exception {\n        //given\n        ValidationStatus status = new ValidationStatus(false, \"'content' is a reserved keyword\");\n\n        //then\n        assertThat(status).isNotValid().hasMessage(\"'content' is a reserved keyword\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/business/data/BusinessDataRetrieverTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.business.data.DummyBusinessDataRefBuilder.buildMultiRefBusinessData;\nimport static org.bonitasoft.engine.business.data.DummyBusinessDataRefBuilder.buildSimpleRefBusinessData;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.bonitasoft.engine.business.data.proxy.ServerProxyfier;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.operation.pojo.Employee;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class BusinessDataRetrieverTest {\n\n    @Mock\n    private ServerProxyfier proxyfier;\n\n    @Mock\n    private BusinessDataRepository repository;\n\n    @InjectMocks\n    private BusinessDataRetriever retriever;\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void getSimpleBusinessData_should_return_proxyfied_entity() throws Exception {\n        //given\n        long id = 11L;\n        Employee employee = new Employee(id, 1L, \"John\", \"Doe\");\n        Employee proxy = new Employee(id, 1L, \"John\", \"Proxy\");\n        given(repository.findById(Employee.class, id)).willReturn(employee);\n        given(proxyfier.proxify(employee)).willReturn(proxy);\n\n        //when\n        Entity retrievedEntity = retriever.getSimpleBusinessData(buildSimpleRefBusinessData(id), Employee.class);\n\n        //then\n        assertThat(retrievedEntity).isEqualTo(proxy);\n    }\n\n    @Test\n    public void getSimpleBusinessData_should_return_null_when_ref_business_data_does_not_reference_any_data()\n            throws Exception {\n        //when\n        Entity retrievedEntity = retriever.getSimpleBusinessData(buildSimpleRefBusinessData((Long) null),\n                Employee.class);\n\n        //then\n        assertThat(retrievedEntity).isNull();\n        verifyNoInteractions(repository);\n        verifyNoInteractions(proxyfier);\n    }\n\n    @Test\n    public void getMultiBusinessData_should_return_list_of_proxyfied_entities() throws Exception {\n        //given\n        List<Long> ids = Arrays.asList(11L, 12L);\n        List<Employee> employees = Arrays.asList(new Employee(ids.get(0), 1L, \"John\", \"Doe\"),\n                new Employee(ids.get(1), 1L, \"Jack\", \"Doe\"));\n        List<Employee> proxies = Arrays.asList(new Employee(ids.get(0), 1L, \"John\", \"Proxy\"),\n                new Employee(ids.get(1), 1L, \"Jack\", \"Proxy\"));\n        given(repository.findByIds(Employee.class, ids)).willReturn(employees);\n        given(proxyfier.proxify(employees.get(0))).willReturn(proxies.get(0));\n        given(proxyfier.proxify(employees.get(1))).willReturn(proxies.get(1));\n\n        //when\n        List<Entity> retrievedEntities = retriever.getMultiBusinessData(buildMultiRefBusinessData(ids), Employee.class);\n\n        //then\n        assertThat(retrievedEntities).containsExactly(proxies.get(0), proxies.get(1));\n    }\n\n    @Test\n    public void getMultiBusinessData_should_return_empty_list_when_multi_ref_data_does_not_reference_any_data()\n            throws Exception {\n        //when\n        List<Entity> retrievedEntities = retriever.getMultiBusinessData(buildMultiRefBusinessData((List<Long>) null),\n                Employee.class);\n\n        //then\n        assertThat(retrievedEntities).isEmpty();\n        verifyNoInteractions(repository);\n        verifyNoInteractions(proxyfier);\n    }\n\n    @Test\n    public void getMultiBusinessData_should_return_empty_Array_list_when_multi_ref_data_does_not_reference_any_data()\n            throws Exception {\n\n        List<Entity> retrievedEntities = retriever.getMultiBusinessData(buildMultiRefBusinessData(new ArrayList<>()),\n                Employee.class);\n\n        assertThat(retrievedEntities).isEmpty();\n\n        //when\n        retrievedEntities.add(new Employee());\n        // no exceptions\n\n        verifyNoInteractions(repository);\n        verifyNoInteractions(proxyfier);\n    }\n\n    @Test\n    public void getBusinessData_should_use_getSimpleBusinessData_for_SSimpleBusinessDataRef() throws Exception {\n        //given\n        BusinessDataRetriever retrieverSpy = spy(retriever);\n        SSimpleRefBusinessDataInstance dataRef = buildSimpleRefBusinessData(\"employee\", Employee.class.getName());\n        Employee employee = new Employee(5L, 1L, \"John\", \"Doe\");\n        given(retrieverSpy.getSimpleBusinessData(dataRef, Employee.class)).willReturn(employee);\n\n        //when\n        Object businessData = retrieverSpy.getBusinessData(dataRef);\n\n        //then\n        assertThat(businessData).isEqualTo(employee);\n    }\n\n    @Test\n    public void getBusinessData_should_use_getMultiBusinessData_for_SMultiBusinessDataRef() throws Exception {\n        //given\n        BusinessDataRetriever retrieverSpy = spy(retriever);\n        SProcessMultiRefBusinessDataInstance dataRef = buildMultiRefBusinessData(Employee.class.getName());\n        Employee employee = new Employee(5L, 1L, \"John\", \"Doe\");\n        given(retrieverSpy.getMultiBusinessData(dataRef, Employee.class))\n                .willReturn(Collections.<Entity> singletonList(employee));\n\n        //when\n        List<Entity> businessData = (List<Entity>) retrieverSpy.getBusinessData(dataRef);\n\n        //then\n        assertThat(businessData).containsExactly(employee);\n    }\n\n    @Test\n    public void getBusinessData_should_throw_exception_if_class_is_unknown() throws Exception {\n        //given\n        String dataName = \"employee\";\n        SSimpleRefBusinessDataInstance dataRef = buildSimpleRefBusinessData(dataName, \"org.any.not.exists.Pojo\");\n\n        //then\n        expectedException.expect(SExpressionEvaluationException.class);\n        expectedException\n                .expectMessage(\"Unable to load class for the business data having reference '\" + dataName + \"'\");\n\n        //when\n        retriever.getBusinessData(dataRef);\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/business/data/DummyBusinessDataRefBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.business.data.SAProcessSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.business.data.SASimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SSimpleRefBusinessDataInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class DummyBusinessDataRefBuilder {\n\n    public static SSimpleRefBusinessDataInstance buildSimpleRefBusinessData(final Long dataId) {\n        final SSimpleRefBusinessDataInstance sRefBusinessDataInstance = new SProcessSimpleRefBusinessDataInstance();\n        sRefBusinessDataInstance.setDataId(dataId);\n        return sRefBusinessDataInstance;\n    }\n\n    public static SSimpleRefBusinessDataInstance buildSimpleRefBusinessData(final String dataName,\n            final String dataClassName) {\n        final SSimpleRefBusinessDataInstance sRefBusinessDataInstance = new SProcessSimpleRefBusinessDataInstance();\n        sRefBusinessDataInstance.setName(dataName);\n        sRefBusinessDataInstance.setDataClassName(dataClassName);\n        return sRefBusinessDataInstance;\n    }\n\n    public static SProcessMultiRefBusinessDataInstance buildMultiRefBusinessData(final List<Long> dataIds) {\n        final SProcessMultiRefBusinessDataInstance reference = new SProcessMultiRefBusinessDataInstance();\n        reference.setDataIds(dataIds);\n        return reference;\n    }\n\n    public static SProcessMultiRefBusinessDataInstance buildMultiRefBusinessData(final String dataClassName) {\n        final SProcessMultiRefBusinessDataInstance reference = new SProcessMultiRefBusinessDataInstance();\n        reference.setDataClassName(dataClassName);\n        return reference;\n    }\n\n    public static SASimpleRefBusinessDataInstance buildArchivedSimpleRefBusinessData(final Long dataId) {\n        final SASimpleRefBusinessDataInstance saProcessSimpleRefBusinessDataInstance = new SAProcessSimpleRefBusinessDataInstance();\n        saProcessSimpleRefBusinessDataInstance.setDataId(dataId);\n        return saProcessSimpleRefBusinessDataInstance;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/business/data/RefBusinessDataRetrieverTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.business.data.DummyBusinessDataRefBuilder.buildArchivedSimpleRefBusinessData;\nimport static org.bonitasoft.engine.business.data.DummyBusinessDataRefBuilder.buildSimpleRefBusinessData;\nimport static org.mockito.BDDMockito.given;\n\nimport org.bonitasoft.engine.commons.Container;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.RefBusinessDataService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAAutomaticTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.business.data.SASimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.operation.BusinessDataContext;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class RefBusinessDataRetrieverTest {\n\n    @Mock\n    private ProcessInstanceService processInstanceService;\n    @Mock\n    private RefBusinessDataService refBusinessDataService;\n    @Mock\n    private FlowNodeInstanceService flowNodeInstanceService;\n    @InjectMocks\n    private RefBusinessDataRetriever retriever;\n    private static long PROCESS_INSTANCE_ID = 50L;\n    private static long EVENT_SUBPROCESS_ID = 5452220L;\n    private static long FLOW_NODE_INSTANCE_OF_EVENT_SUBPROCESS = 213215L;\n    private static long FLOW_NODE_INSTANCE_ID = 100L;\n    private SAFlowNodeInstance archivedFlowNodeInstance;\n    private SFlowNodeInstance flowNodeInstance;\n    private SProcessInstance processInstance;\n    private SAProcessInstance archivedProcessInstance;\n    private SProcessInstance eventSubProcessInstance;\n    private SFlowNodeInstance eventSubProcessFlowNode;\n\n    @Before\n    public void before() throws Exception {\n        flowNodeInstance = new SAutomaticTaskInstance(\"auto1\", 312490L, PROCESS_INSTANCE_ID, PROCESS_INSTANCE_ID, 0L,\n                0L);\n        flowNodeInstance.setLogicalGroup(1, PROCESS_INSTANCE_ID);\n        flowNodeInstance.setLogicalGroup(3, PROCESS_INSTANCE_ID);\n        archivedFlowNodeInstance = new SAAutomaticTaskInstance((SAutomaticTaskInstance) flowNodeInstance);\n        processInstance = new SProcessInstance(\"MyProcess\", 809234L);\n        processInstance.setId(PROCESS_INSTANCE_ID);\n        archivedProcessInstance = new SAProcessInstance(processInstance);\n        eventSubProcessInstance = new SProcessInstance();\n        eventSubProcessInstance.setId(EVENT_SUBPROCESS_ID);\n        eventSubProcessInstance.setCallerType(SFlowNodeType.SUB_PROCESS);\n        eventSubProcessInstance.setCallerId(FLOW_NODE_INSTANCE_ID);\n        eventSubProcessFlowNode = new SAutomaticTaskInstance(\"flownodeInEventSubProcess\", 352523L, PROCESS_INSTANCE_ID,\n                EVENT_SUBPROCESS_ID,\n                0L, PROCESS_INSTANCE_ID);\n        eventSubProcessFlowNode.setLogicalGroup(1, PROCESS_INSTANCE_ID);\n        eventSubProcessFlowNode.setLogicalGroup(3, EVENT_SUBPROCESS_ID);\n\n        given(flowNodeInstanceService.getFlowNodeInstance(FLOW_NODE_INSTANCE_OF_EVENT_SUBPROCESS))\n                .willReturn(eventSubProcessFlowNode);\n        given(flowNodeInstanceService.getFlowNodeInstance(FLOW_NODE_INSTANCE_ID)).willReturn(flowNodeInstance);\n        given(processInstanceService.getProcessInstance(PROCESS_INSTANCE_ID)).willReturn(processInstance);\n        given(processInstanceService.getProcessInstance(EVENT_SUBPROCESS_ID)).willReturn(eventSubProcessInstance);\n        given(flowNodeInstanceService.getLastArchivedFlowNodeInstance(SAFlowNodeInstance.class, FLOW_NODE_INSTANCE_ID))\n                .willReturn(archivedFlowNodeInstance);\n    }\n\n    @Test\n    public void getRefBusinessData_should_retrieve_data_using_process_context_when_container_is_a_process()\n            throws Exception {\n        //given\n        SSimpleRefBusinessDataInstance refBusinessDataInstance = buildSimpleRefBusinessData(4L);\n        given(refBusinessDataService.getRefBusinessDataInstance(\"data\", PROCESS_INSTANCE_ID))\n                .willReturn(refBusinessDataInstance);\n\n        //when\n        SRefBusinessDataInstance retrievedData = retriever\n                .getRefBusinessDataInstance(new BusinessDataContext(\"data\",\n                        new Container(PROCESS_INSTANCE_ID, DataInstanceContainer.PROCESS_INSTANCE.name())));\n\n        //then\n        assertThat(retrievedData).isEqualTo(refBusinessDataInstance);\n    }\n\n    @Test\n    public void getRefBusinessData_should_retrieve_archived_data_when_container_is_a_process_and_data_not_found_in_journal()\n            throws Exception {\n        //given\n        final SASimpleRefBusinessDataInstance refBusinessDataInstance = buildArchivedSimpleRefBusinessData(30L);\n        given(refBusinessDataService.getRefBusinessDataInstance(\"data\", PROCESS_INSTANCE_ID))\n                .willThrow(new SRefBusinessDataInstanceNotFoundException(PROCESS_INSTANCE_ID, \"data\"));\n        given(refBusinessDataService.getSARefBusinessDataInstance(\"data\", PROCESS_INSTANCE_ID))\n                .willReturn(refBusinessDataInstance);\n\n        //when\n        SRefBusinessDataInstance retrievedData = retriever\n                .getRefBusinessDataInstance(new BusinessDataContext(\"data\",\n                        new Container(PROCESS_INSTANCE_ID, DataInstanceContainer.PROCESS_INSTANCE.name())));\n\n        //then\n        assertThat(retrievedData).isEqualToComparingFieldByField(refBusinessDataInstance);\n    }\n\n    @Test\n    public void getRefBusinessData_should_retrieve_data_using_flow_node_context_when_container_is_a_flow_node()\n            throws Exception {\n        //given\n        SSimpleRefBusinessDataInstance refBusinessDataInstance = buildSimpleRefBusinessData(4L);\n        given(refBusinessDataService.getFlowNodeRefBusinessDataInstance(\"data\", FLOW_NODE_INSTANCE_ID))\n                .willReturn(refBusinessDataInstance);\n\n        //when\n        SRefBusinessDataInstance retrievedData = retriever.getRefBusinessDataInstance(\n                new BusinessDataContext(\"data\",\n                        new Container(FLOW_NODE_INSTANCE_ID, DataInstanceContainer.ACTIVITY_INSTANCE.name())));\n\n        //then\n        assertThat(retrievedData).isEqualTo(refBusinessDataInstance);\n    }\n\n    @Test\n    public void getRefBusinessData_should_retry_to_retrieve_data_using_process_context_when_retrieving_data_using_flow_node_fails()\n            throws Exception {\n        //given\n        SSimpleRefBusinessDataInstance refBusinessDataInstance = buildSimpleRefBusinessData(4L);\n        given(refBusinessDataService.getFlowNodeRefBusinessDataInstance(\"data\", FLOW_NODE_INSTANCE_ID))\n                .willThrow(new SRefBusinessDataInstanceNotFoundException(PROCESS_INSTANCE_ID, \"data\"));\n        given(refBusinessDataService.getRefBusinessDataInstance(\"data\", PROCESS_INSTANCE_ID))\n                .willReturn(refBusinessDataInstance);\n\n        //when\n        SRefBusinessDataInstance retrievedData = retriever.getRefBusinessDataInstance(\n                new BusinessDataContext(\"data\",\n                        new Container(FLOW_NODE_INSTANCE_ID, DataInstanceContainer.ACTIVITY_INSTANCE.name())));\n\n        //then\n        assertThat(retrievedData).isEqualTo(refBusinessDataInstance);\n    }\n\n    @Test\n    public void getRefBusinessData_should_retry_when_not_found_in_journal_on_flow_node() throws Exception {\n        //given\n        final SASimpleRefBusinessDataInstance archRefBusinessDataInstance = buildArchivedSimpleRefBusinessData(531L);\n        given(refBusinessDataService.getFlowNodeRefBusinessDataInstance(\"data\", FLOW_NODE_INSTANCE_ID))\n                .willThrow(new SRefBusinessDataInstanceNotFoundException(PROCESS_INSTANCE_ID, \"data\"));\n        given(refBusinessDataService.getSAFlowNodeRefBusinessDataInstance(\"data\", FLOW_NODE_INSTANCE_ID))\n                .willReturn(archRefBusinessDataInstance);\n\n        //when\n        SRefBusinessDataInstance retrievedData = retriever.getRefBusinessDataInstance(\n                new BusinessDataContext(\"data\",\n                        new Container(FLOW_NODE_INSTANCE_ID, DataInstanceContainer.ACTIVITY_INSTANCE.name())));\n\n        //then\n        assertThat(retrievedData).isEqualToComparingFieldByField(archRefBusinessDataInstance);\n    }\n\n    @Test\n    public void getRefBusinessData_should_retry_up_to_archived_process_scope_when_not_found_in_journal_on_flow_node()\n            throws Exception {\n        //given\n        final SASimpleRefBusinessDataInstance archRefBusinessDataInstance = buildArchivedSimpleRefBusinessData(531L);\n        final SRefBusinessDataInstanceNotFoundException notFoundException = new SRefBusinessDataInstanceNotFoundException(\n                PROCESS_INSTANCE_ID, \"data\");\n        given(refBusinessDataService.getFlowNodeRefBusinessDataInstance(\"data\", FLOW_NODE_INSTANCE_ID))\n                .willThrow(notFoundException);\n        given(refBusinessDataService.getRefBusinessDataInstance(\"data\", PROCESS_INSTANCE_ID))\n                .willThrow(notFoundException);\n        given(refBusinessDataService.getSAFlowNodeRefBusinessDataInstance(\"data\", FLOW_NODE_INSTANCE_ID))\n                .willThrow(notFoundException);\n        given(refBusinessDataService.getSARefBusinessDataInstance(\"data\", PROCESS_INSTANCE_ID))\n                .willReturn(archRefBusinessDataInstance);\n\n        //when\n        SRefBusinessDataInstance retrievedData = retriever.getRefBusinessDataInstance(\n                new BusinessDataContext(\"data\",\n                        new Container(FLOW_NODE_INSTANCE_ID, DataInstanceContainer.ACTIVITY_INSTANCE.name())));\n\n        //then\n        assertThat(retrievedData).isEqualToComparingFieldByField(archRefBusinessDataInstance);\n    }\n\n    @Test\n    public void getRefBusinessData_should_look_up_to_archived_process_scope_when_not_found_in_journal_on_flow_node_when_flow_node_and_process_is_archived()\n            throws Exception {\n        //given\n        final SASimpleRefBusinessDataInstance archRefBusinessDataInstance = buildArchivedSimpleRefBusinessData(531L);\n        final SRefBusinessDataInstanceNotFoundException notFoundException = new SRefBusinessDataInstanceNotFoundException(\n                PROCESS_INSTANCE_ID, \"data\");\n        given(refBusinessDataService.getFlowNodeRefBusinessDataInstance(\"data\", FLOW_NODE_INSTANCE_ID))\n                .willThrow(notFoundException);\n        given(flowNodeInstanceService.getFlowNodeInstance(FLOW_NODE_INSTANCE_ID))\n                .willThrow(new SFlowNodeNotFoundException(FLOW_NODE_INSTANCE_ID));\n        given(refBusinessDataService.getRefBusinessDataInstance(\"data\", PROCESS_INSTANCE_ID))\n                .willThrow(notFoundException);\n        given(refBusinessDataService.getSAFlowNodeRefBusinessDataInstance(\"data\", FLOW_NODE_INSTANCE_ID))\n                .willThrow(notFoundException);\n        given(refBusinessDataService.getSARefBusinessDataInstance(\"data\", PROCESS_INSTANCE_ID))\n                .willReturn(archRefBusinessDataInstance);\n\n        //when\n        SRefBusinessDataInstance retrievedData = retriever.getRefBusinessDataInstance(\n                new BusinessDataContext(\"data\",\n                        new Container(FLOW_NODE_INSTANCE_ID, DataInstanceContainer.ACTIVITY_INSTANCE.name())));\n\n        //then\n        assertThat(retrievedData).isEqualToComparingFieldByField(archRefBusinessDataInstance);\n    }\n\n    @Test\n    public void getRefBusinessData_should_get_from_root_process_for_event_subprocess() throws Exception {\n        //given\n        SSimpleRefBusinessDataInstance refBusinessDataInstance = buildSimpleRefBusinessData(4L);\n        given(refBusinessDataService.getRefBusinessDataInstance(\"data\", PROCESS_INSTANCE_ID))\n                .willReturn(refBusinessDataInstance);\n\n        //when\n        SRefBusinessDataInstance retrievedData = retriever\n                .getRefBusinessDataInstance(new BusinessDataContext(\"data\",\n                        new Container(EVENT_SUBPROCESS_ID, DataInstanceContainer.PROCESS_INSTANCE.name())));\n\n        //then\n        assertThat(retrievedData).isEqualTo(refBusinessDataInstance);\n    }\n\n    @Test\n    public void getRefBusinessData_should_get_from_root_process_for_flow_node_in_event_subprocess() throws Exception {\n        //given\n        SSimpleRefBusinessDataInstance refBusinessDataInstance = buildSimpleRefBusinessData(4L);\n\n        given(refBusinessDataService.getFlowNodeRefBusinessDataInstance(\"data\", FLOW_NODE_INSTANCE_OF_EVENT_SUBPROCESS))\n                .willThrow(new SRefBusinessDataInstanceNotFoundException(PROCESS_INSTANCE_ID, \"data\"));\n        given(refBusinessDataService.getRefBusinessDataInstance(\"data\", PROCESS_INSTANCE_ID))\n                .willReturn(refBusinessDataInstance);\n\n        //when\n        SRefBusinessDataInstance retrievedData = retriever.getRefBusinessDataInstance(\n                new BusinessDataContext(\"data\", new Container(FLOW_NODE_INSTANCE_OF_EVENT_SUBPROCESS,\n                        DataInstanceContainer.ACTIVITY_INSTANCE.name())));\n\n        //then\n        assertThat(retrievedData).isEqualTo(refBusinessDataInstance);\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/business/data/converter/BusinessDataModelConverterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.converter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.Arrays;\nimport java.util.LinkedList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.business.data.MultipleBusinessDataReference;\nimport org.bonitasoft.engine.business.data.SimpleBusinessDataReference;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SSimpleRefBusinessDataInstance;\nimport org.junit.Test;\n\npublic class BusinessDataModelConverterTest {\n\n    @Test\n    public void convertSSimpleBusinessDataReferencetoClientObject() throws Exception {\n        final SSimpleRefBusinessDataInstance sReference = new SProcessSimpleRefBusinessDataInstance();\n        sReference.setName(\"employee\");\n        sReference.setDataClassName(\"com.bonitasoft.Employee\");\n        sReference.setId(465L);\n        sReference.setDataId(87997L);\n\n        final SimpleBusinessDataReference reference = BusinessDataModelConverter\n                .toSimpleBusinessDataReference(sReference);\n        assertThat(reference.getStorageId()).isEqualTo(87997L);\n        assertThat(reference.getStorageIdAsString()).isEqualTo(\"87997\");\n        assertThat(reference.getName()).isEqualTo(\"employee\");\n        assertThat(reference.getType()).isEqualTo(\"com.bonitasoft.Employee\");\n    }\n\n    @Test\n    public void convertSMultiBusinessDataReferencetoClientObject_should_Create_a_correct_list_from_a_list_with_a_null() {\n        //given\n        List dataIDs = new LinkedList();\n        dataIDs.add(25L);\n        dataIDs.add(null);\n        dataIDs.add(3L);\n        final SProcessMultiRefBusinessDataInstance sReference = new SProcessMultiRefBusinessDataInstance();\n        sReference.setDataIds(dataIDs);\n\n        //when\n        final MultipleBusinessDataReference reference = BusinessDataModelConverter\n                .toMultipleBusinessDataReference(sReference);\n\n        //then\n        assertThat(reference).isNotNull();\n\n    }\n\n    @Test\n    public void convertSMultiBusinessDataReferencetoClientObject() throws Exception {\n        final SProcessMultiRefBusinessDataInstance sReference = new SProcessMultiRefBusinessDataInstance();\n        sReference.setName(\"employees\");\n        sReference.setDataClassName(\"com.bonitasoft.Employee\");\n        sReference.setId(465L);\n        sReference.setDataIds(Arrays.asList(87997L, 654312354L, 4786454L));\n\n        final MultipleBusinessDataReference reference = BusinessDataModelConverter\n                .toMultipleBusinessDataReference(sReference);\n        assertThat(reference.getStorageIds()).isEqualTo(Arrays.asList(87997L, 654312354L, 4786454L));\n        assertThat(reference.getStorageIdsAsString()).isEqualTo(Arrays.asList(\"87997\", \"654312354\", \"4786454\"));\n        assertThat(reference.getName()).isEqualTo(\"employees\");\n        assertThat(reference.getType()).isEqualTo(\"com.bonitasoft.Employee\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/command/AdvancedStartProcessCommandTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.fail;\nimport static org.mockito.Mockito.doReturn;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SUserTaskDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SBoundaryEventDefinition;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class AdvancedStartProcessCommandTest {\n\n    private static final long PROCESS_DEFINITION_ID = 1234L;\n\n    @Mock\n    private ServiceAccessor serviceAccessor;\n\n    @Mock\n    private ProcessDefinitionService processDefinitionService;\n\n    @Mock\n    private SProcessDefinition procDef;\n\n    @Mock\n    private SFlowElementContainerDefinition container;\n\n    @Mock\n    private SBoundaryEventDefinition boundary;\n\n    @Mock\n    private SUserTaskDefinition userTask;\n\n    private Map<String, Serializable> parameters;\n\n    @Before\n    public void setUp() throws Exception {\n        parameters = new HashMap<String, Serializable>(2);\n        parameters.put(AdvancedStartProcessCommand.PROCESS_DEFINITION_ID, PROCESS_DEFINITION_ID);\n        parameters.put(AdvancedStartProcessCommand.STARTED_BY, 123L);\n        parameters.put(AdvancedStartProcessCommand.ACTIVITY_NAME, \"\");\n\n        Set<SFlowNodeDefinition> flowNodes = new HashSet<SFlowNodeDefinition>();\n        flowNodes.add(userTask);\n        flowNodes.add(boundary);\n\n        doReturn(processDefinitionService).when(serviceAccessor).getProcessDefinitionService();\n        doReturn(procDef).when(processDefinitionService).getProcessDefinition(PROCESS_DEFINITION_ID);\n        doReturn(container).when(procDef).getProcessContainer();\n        doReturn(flowNodes).when(container).getFlowNodes();\n    }\n\n    @Test\n    public void execute_command_throws_SCommandExecutionException_if_validation_returns_problems() throws Exception {\n        AdvancedStartProcessCommand command = new AdvancedStartProcessCommand();\n        try {\n            command.execute(parameters, serviceAccessor);\n            fail(\"As the activity names is empty and exception must be thrown during the validation\");\n        } catch (SCommandExecutionException e) {\n            assertThat(e.getMessage()).contains(\"No flownode named '' was found in the process\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/command/ExecuteBDMQueryCommandTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport static java.util.Arrays.asList;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.catchThrowable;\nimport static org.bonitasoft.engine.command.ExecuteBDMQueryCommandTest.ParametersBuilder.parameters;\nimport static org.mockito.Mockito.when;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.business.data.BusinessDataRepository;\nimport org.bonitasoft.engine.operation.pojo.Employee;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Romain Bioteau\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class ExecuteBDMQueryCommandTest {\n\n    @Spy\n    private ExecuteBDMQueryCommand command;\n\n    @Mock\n    private BusinessDataRepository bdmRepository;\n\n    @Mock\n    private ServiceAccessor serviceAccessor;\n\n    @Before\n    public void setUp() {\n        when(serviceAccessor.getBusinessDataRepository()).thenReturn(bdmRepository);\n    }\n\n    @Test\n    public void execute_should_return_an_instance() throws Exception {\n        // given\n        Map<String, Serializable> queryParameters = new HashMap<>();\n        when(bdmRepository.findByNamedQuery(\"a_query\", Employee.class, queryParameters))\n                .thenReturn(new Employee());\n        Map<String, Serializable> parameters = parameters().withQueryName(\"a_query\")\n                .withReturnType(Employee.class.getName())\n                .withQueryParameters(queryParameters)\n                .withIsReturnList(false)\n                .build();\n\n        // when\n        byte[] result = (byte[]) command.execute(parameters, serviceAccessor);\n\n        // then\n        assertThat(new String(result)).isEqualTo(\n                \"{\\\"persistenceId\\\":null,\\\"persistenceVersion\\\":null,\\\"firstName\\\":null,\\\"lastName\\\":null}\");\n    }\n\n    @Test\n    public void execute_should_return_an_instance_when_the_returnsList_parameter_is_not_set() throws Exception {\n        // given\n        Map<String, Serializable> queryParameters = new HashMap<>();\n        when(bdmRepository.findByNamedQuery(\"a_query\", Employee.class, queryParameters))\n                .thenReturn(new Employee());\n        Map<String, Serializable> parameters = parameters().withQueryName(\"a_query\")\n                .withReturnType(Employee.class.getName())\n                .withQueryParameters(queryParameters)\n                .build();\n\n        // when\n        byte[] result = (byte[]) command.execute(parameters, serviceAccessor);\n\n        // then\n        assertThat(new String(result)).isEqualTo(\n                \"{\\\"persistenceId\\\":null,\\\"persistenceVersion\\\":null,\\\"firstName\\\":null,\\\"lastName\\\":null}\");\n    }\n\n    @Test\n    public void execute_should_return_an_list() throws Exception {\n        // given\n        Map<String, Serializable> queryParameters = new HashMap<>();\n        when(bdmRepository.findListByNamedQuery(\"a_query\", Employee.class, queryParameters, 0, 10))\n                .thenReturn(asList(new Employee(1L, 0L, \"Walter\", \"Bates\"), new Employee(2L, 0L, \"Helen\", \"Kelly\")));\n        Map<String, Serializable> parameters = parameters().withQueryName(\"a_query\")\n                .withReturnType(Employee.class.getName())\n                .withQueryParameters(queryParameters)\n                .withIsReturnList(true)\n                .withStartIndex(0)\n                .withMaxResults(10)\n                .build();\n\n        // when\n        byte[] result = (byte[]) command.execute(parameters, serviceAccessor);\n\n        // then\n        assertThat(new String(result)).as(\"json result\").isEqualTo(\n                \"[{\\\"persistenceId\\\":1,\\\"persistenceVersion\\\":0,\\\"firstName\\\":\\\"Walter\\\",\\\"lastName\\\":\\\"Bates\\\"}\"\n                        + \",{\\\"persistenceId\\\":2,\\\"persistenceVersion\\\":0,\\\"firstName\\\":\\\"Helen\\\",\\\"lastName\\\":\\\"Kelly\\\"}]\");\n    }\n\n    @Test\n    public void execute_should_fail_if_the_query_name_is_null() throws Exception {\n        // given\n        Map<String, Serializable> parameters = parameters().withQueryName(null)\n                .build();\n\n        // when\n        Throwable thrown = catchThrowable(() -> command.execute(parameters, serviceAccessor));\n\n        // then\n        assertThat(thrown).isInstanceOf(SCommandParameterizationException.class)\n                .hasMessageContaining(\"queryName\");\n    }\n\n    @Test\n    public void execute_should_fail_if_the_return_type_is_empty() throws Exception {\n        // given\n        Map<String, Serializable> parameters = parameters().withQueryName(\"a_query\")\n                .withReturnType(null)\n                .build();\n\n        // when\n        Throwable thrown = catchThrowable(() -> command.execute(parameters, serviceAccessor));\n\n        // then\n        assertThat(thrown).isInstanceOf(SCommandParameterizationException.class)\n                .hasMessageContaining(\"returnType\");\n    }\n\n    // =================================================================================================================\n    // UTILS\n    // =================================================================================================================\n\n    static class ParametersBuilder {\n\n        private Map<String, Serializable> parameters = new HashMap<>();\n\n        private ParametersBuilder() {\n            // hidden\n        }\n\n        public static ParametersBuilder parameters() {\n            return new ParametersBuilder();\n        }\n\n        private ParametersBuilder withParameter(String name, Serializable value) {\n            parameters.put(name, value);\n            return this;\n        }\n\n        public ParametersBuilder withQueryName(String name) {\n            return withParameter(ExecuteBDMQueryCommand.QUERY_NAME, name);\n        }\n\n        public ParametersBuilder withReturnType(String className) {\n            return withParameter(ExecuteBDMQueryCommand.RETURN_TYPE, className);\n        }\n\n        public ParametersBuilder withQueryParameters(Map<String, Serializable> queryParameters) {\n            return withParameter(ExecuteBDMQueryCommand.QUERY_PARAMETERS, (Serializable) queryParameters);\n        }\n\n        public ParametersBuilder withIsReturnList(boolean isReturnList) {\n            return withParameter(ExecuteBDMQueryCommand.RETURNS_LIST, isReturnList);\n        }\n\n        public ParametersBuilder withStartIndex(int startIndex) {\n            return withParameter(ExecuteBDMQueryCommand.START_INDEX, startIndex);\n        }\n\n        public ParametersBuilder withMaxResults(int maxResults) {\n            return withParameter(ExecuteBDMQueryCommand.MAX_RESULTS, maxResults);\n        }\n\n        private Map<String, Serializable> build() {\n            return new HashMap<>(parameters);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/command/GetBusinessDataByIdCommandTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\nimport static org.mockito.Mockito.*;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.business.data.*;\nimport org.bonitasoft.engine.operation.pojo.Travel;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class GetBusinessDataByIdCommandTest {\n\n    private static final String PARAMETER_CHILDNAME = \"lines\";\n\n    private static final long PARAMETER_IDENTIFIER = 1983L;\n\n    private static final String PARAMETER_CLASS_NAME = Travel.class.getName();\n\n    private static final String PARAMETER_BUSINESSDATA_CLASS_URI_VALUE = \"/businessdata/{className}/{id}/{field}\";\n\n    private GetBusinessDataByIdCommand command;\n\n    @Mock\n    private ServiceAccessor serviceAccessor;\n\n    @Mock\n    private BusinessDataService businessDataService;\n\n    private Map<String, Serializable> parameters;\n\n    @Before\n    public void setUp() throws Exception {\n        command = new GetBusinessDataByIdCommand();\n        parameters = new HashMap<String, Serializable>();\n        parameters.put(GetBusinessDataByIdCommand.BUSINESS_DATA_ID, PARAMETER_IDENTIFIER);\n        parameters.put(GetBusinessDataByIdCommand.ENTITY_CLASS_NAME, PARAMETER_CLASS_NAME);\n        parameters.put(BusinessDataCommandField.BUSINESS_DATA_URI_PATTERN, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n        when(serviceAccessor.getBusinessDataService()).thenReturn(businessDataService);\n    }\n\n    @Test\n    public void executeCommandWithEntity() throws Exception {\n        //when\n        command.execute(parameters, serviceAccessor);\n\n        //then\n        verify(businessDataService).getJsonEntity(PARAMETER_CLASS_NAME, PARAMETER_IDENTIFIER,\n                PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n    }\n\n    @Test\n    public void executeCommandWithEntityShouldThrowException() throws Exception {\n        //given\n        doThrow(new SBusinessDataNotFoundException(\"BO not found\")).when(businessDataService).getJsonEntity(\n                PARAMETER_CLASS_NAME,\n                PARAMETER_IDENTIFIER,\n                PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n        //when then exception\n        assertThatThrownBy(() -> command.execute(parameters, serviceAccessor))\n                .isInstanceOf(SCommandExecutionException.class)\n                .hasRootCauseInstanceOf(BusinessDataNotFoundException.class)\n                .hasRootCauseMessage(\"BO not found\");\n    }\n\n    @Test\n    public void executeCommandWithChildEntityShouldThrowException() throws Exception {\n        //given\n        parameters.put(GetBusinessDataByIdCommand.BUSINESS_DATA_CHILD_NAME, PARAMETER_CHILDNAME);\n        doThrow(new SBusinessDataRepositoryException(\"Constraint violation\")).when(businessDataService)\n                .getJsonChildEntity(\n                        PARAMETER_CLASS_NAME, PARAMETER_IDENTIFIER,\n                        PARAMETER_CHILDNAME,\n                        PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n        //when then exception\n        assertThatThrownBy(() -> command.execute(parameters, serviceAccessor))\n                .isInstanceOf(SCommandExecutionException.class)\n                .hasRootCauseInstanceOf(BusinessDataRepositoryException.class)\n                .hasRootCauseMessage(\"Constraint violation\");\n    }\n\n    @Test\n    public void executeCommandWithEmptyChildName() throws Exception {\n        //given\n        parameters.put(GetBusinessDataByIdCommand.BUSINESS_DATA_CHILD_NAME, \"\");\n\n        //when\n        command.execute(parameters, serviceAccessor);\n\n        //then\n        verify(businessDataService).getJsonEntity(PARAMETER_CLASS_NAME, PARAMETER_IDENTIFIER,\n                PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n    }\n\n    @Test\n    public void executeCommandWithChildEntity() throws Exception {\n        //given\n        parameters.put(GetBusinessDataByIdCommand.BUSINESS_DATA_CHILD_NAME, PARAMETER_CHILDNAME);\n\n        //when\n        command.execute(parameters, serviceAccessor);\n\n        //then\n        verify(businessDataService).getJsonChildEntity(PARAMETER_CLASS_NAME, PARAMETER_IDENTIFIER, PARAMETER_CHILDNAME,\n                PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/command/GetBusinessDataByIdsCommandTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\nimport static org.mockito.Mockito.*;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.business.data.BusinessDataRepositoryException;\nimport org.bonitasoft.engine.business.data.BusinessDataService;\nimport org.bonitasoft.engine.business.data.SBusinessDataRepositoryException;\nimport org.bonitasoft.engine.operation.pojo.Travel;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class GetBusinessDataByIdsCommandTest {\n\n    private static final List<Long> identifiers;\n\n    static {\n        identifiers = new ArrayList<>();\n        identifiers.add(1983L);\n        identifiers.add(1990L);\n    }\n\n    private static final String PARAMETER_CLASS_NAME = Travel.class.getName();\n\n    private static final String PARAMETER_BUSINESSDATA_CLASS_URI_VALUE = \"/businessdata/{className}/{id}/{field}\";\n\n    private GetBusinessDataByIdsCommand command;\n\n    @Mock\n    private ServiceAccessor serviceAccessor;\n\n    @Mock\n    private BusinessDataService businessDataService;\n\n    private Map<String, Serializable> parameters;\n\n    @Before\n    public void setUp() throws Exception {\n        command = new GetBusinessDataByIdsCommand();\n        parameters = new HashMap<>();\n        parameters.put(GetBusinessDataByIdsCommand.BUSINESS_DATA_IDS, (Serializable) identifiers);\n        parameters.put(GetBusinessDataByIdsCommand.ENTITY_CLASS_NAME, PARAMETER_CLASS_NAME);\n        parameters.put(BusinessDataCommandField.BUSINESS_DATA_URI_PATTERN, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n        when(serviceAccessor.getBusinessDataService()).thenReturn(businessDataService);\n    }\n\n    @Test\n    public void executeCommandWithEntities() throws Exception {\n        //when\n        command.execute(parameters, serviceAccessor);\n\n        //then\n        verify(businessDataService).getJsonEntities(PARAMETER_CLASS_NAME, identifiers,\n                PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n    }\n\n    @Test\n    public void executeCommandWithEntitiesShouldThrowException() throws Exception {\n        //given\n        doThrow(new SBusinessDataRepositoryException(\"Constraint violation\")).when(businessDataService).getJsonEntities(\n                PARAMETER_CLASS_NAME,\n                identifiers,\n                PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n        //when then exception\n        assertThatThrownBy(() -> command.execute(parameters, serviceAccessor))\n                .isInstanceOf(SCommandExecutionException.class)\n                .hasRootCauseInstanceOf(BusinessDataRepositoryException.class)\n                .hasRootCauseMessage(\"Constraint violation\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/command/GetBusinessDataByQueryCommandTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\nimport static org.junit.Assert.fail;\nimport static org.mockito.Mockito.*;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.business.data.BusinessDataRepositoryException;\nimport org.bonitasoft.engine.business.data.BusinessDataService;\nimport org.bonitasoft.engine.business.data.SBusinessDataRepositoryException;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class GetBusinessDataByQueryCommandTest {\n\n    private static final String PARAMETER_BUSINESS_DATA_CLASS_URI_VALUE = \"/businessdata/{className}/{id}/{field}\";\n    private static final String PARAMETER_RETURN_TYPE = \"com.company.model.Employee\";\n    public static final String PARAMETER_QUERY_NAME = \"myQuery\";\n    public static final int PARAMETER_MAX_RESULTS = 10;\n    public static final int PARAMETER_START_INDEX = 3;\n\n    private GetBusinessDataByQueryCommand command;\n\n    @Mock\n    private ServiceAccessor serviceAccessor;\n\n    @Mock\n    private BusinessDataService businessDataService;\n\n    private Map<String, Serializable> commandParameters;\n    private Map<String, Serializable> queryParameters;\n\n    @Before\n    public void setUp() throws Exception {\n        command = new GetBusinessDataByQueryCommand();\n\n        queryParameters = new HashMap<>();\n        queryParameters.put(\"param\", \"value\");\n\n        commandParameters = new HashMap<>();\n        commandParameters.put(GetBusinessDataByQueryCommand.QUERY_NAME, PARAMETER_QUERY_NAME);\n        commandParameters.put(GetBusinessDataByQueryCommand.ENTITY_CLASS_NAME, PARAMETER_RETURN_TYPE);\n        commandParameters.put(GetBusinessDataByQueryCommand.MAX_RESULTS, PARAMETER_MAX_RESULTS);\n        commandParameters.put(GetBusinessDataByQueryCommand.START_INDEX, PARAMETER_START_INDEX);\n        commandParameters.put(GetBusinessDataByQueryCommand.QUERY_PARAMETERS, (Serializable) queryParameters);\n        commandParameters.put(BusinessDataCommandField.BUSINESS_DATA_URI_PATTERN,\n                PARAMETER_BUSINESS_DATA_CLASS_URI_VALUE);\n        when(serviceAccessor.getBusinessDataService()).thenReturn(businessDataService);\n    }\n\n    @Test\n    public void executeCommand_should_call_service() throws Exception {\n        //when\n        command.execute(commandParameters, serviceAccessor);\n\n        //then\n        verify(businessDataService).getJsonQueryEntities(PARAMETER_RETURN_TYPE, PARAMETER_QUERY_NAME, queryParameters,\n                PARAMETER_START_INDEX,\n                PARAMETER_MAX_RESULTS,\n                PARAMETER_BUSINESS_DATA_CLASS_URI_VALUE);\n    }\n\n    @Test\n    public void executeCommand_should_throw_exception() throws Exception {\n        //given\n        doThrow(new SBusinessDataRepositoryException(\"Constraint violation\")).when(businessDataService)\n                .getJsonQueryEntities(\n                        PARAMETER_RETURN_TYPE, PARAMETER_QUERY_NAME,\n                        queryParameters, PARAMETER_START_INDEX, PARAMETER_MAX_RESULTS,\n                        PARAMETER_BUSINESS_DATA_CLASS_URI_VALUE);\n\n        //when then exception\n        assertThatThrownBy(() -> command.execute(commandParameters, serviceAccessor))\n                .isInstanceOf(SCommandExecutionException.class)\n                .hasRootCauseInstanceOf(BusinessDataRepositoryException.class)\n                .hasRootCauseMessage(\"Constraint violation\");\n    }\n\n    @Test\n    public void executeCommand_should_check_return_type() throws Exception {\n        String[] mandatoryParameters = { GetBusinessDataByQueryCommand.START_INDEX,\n                GetBusinessDataByQueryCommand.MAX_RESULTS,\n                GetBusinessDataByQueryCommand.QUERY_NAME, GetBusinessDataByQueryCommand.QUERY_PARAMETERS,\n                GetBusinessDataByQueryCommand.ENTITY_CLASS_NAME };\n\n        for (String mandatoryParameter : mandatoryParameters) {\n            try {\n                verify_command_checks_parameter(mandatoryParameter);\n                fail(\"should throw exception\");\n            } catch (SCommandParameterizationException e) {\n                //ok\n            }\n        }\n    }\n\n    private void verify_command_checks_parameter(String mandatoryParameter) throws Exception {\n        //given\n        commandParameters.remove(mandatoryParameter);\n\n        //when then exception\n        command.execute(commandParameters, serviceAccessor);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/configuration/BonitaPropertyAnnotationProcessorTest.java",
    "content": "/**\n * Copyright (C) 2025 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.configuration;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.mockito.Mockito.when;\n\nimport java.lang.reflect.Field;\n\nimport org.bonitasoft.engine.properties.BonitaProperty;\nimport org.bonitasoft.engine.properties.BonitaPropertyAnnotationProcessor;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport org.springframework.core.env.Environment;\n\n@ExtendWith(MockitoExtension.class)\nclass BonitaPropertyAnnotationProcessorTest {\n\n    @Mock\n    private Environment environment;\n\n    @InjectMocks\n    private BonitaPropertyAnnotationProcessor processor;\n\n    @Test\n    void should_PostProcessBeforeInitialization_set_BonitaProperty_value() throws Exception {\n        // Create a test bean with a field annotated with @BonitaProperty\n        TestBean testBean = new TestBean();\n\n        // Mock the environment to return a value for the property\n        when(environment.getProperty(\"test.property\")).thenReturn(\"testValue\");\n\n        // Process the bean\n        processor.postProcessBeforeInitialization(testBean, \"testBean\");\n\n        // Verify that the field value was set correctly\n        Field field = TestBean.class.getDeclaredField(\"property\");\n        field.setAccessible(true);\n        assertEquals(\"testValue\", field.get(testBean));\n    }\n\n    static class TestBean {\n\n        @BonitaProperty(\"test.property\")\n        private String property;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/connector/ConnectorServiceDecoratorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connector;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.Mockito.doReturn;\n\nimport java.io.Serializable;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.core.connector.ConnectorResult;\nimport org.bonitasoft.engine.core.connector.ConnectorService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.expression.EngineConstantExpressionBuilder;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Celine Souchet\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class ConnectorServiceDecoratorTest {\n\n    @Mock\n    private ConnectorService connectorService;\n\n    @InjectMocks\n    private ConnectorServiceDecorator connectorServiceDecorator;\n\n    @Test\n    public final void checkIfEngineExecutionContextIsPutOnParametersWhenExecuteMutipleEvaluation() throws Exception {\n        final long processDefinitionId = 6L;\n        final String connectorDefinitionId = \"connectorDefinitionId\";\n        final String connectorDefinitionVersion = \"connectorDefinitionVersion\";\n        final Map<String, SExpression> connectorInputParameters = Collections.emptyMap();\n        final Map<String, Map<String, Serializable>> inputValues = null;\n        final ClassLoader classLoader = null;\n        final SExpressionContext sexpContext = null;\n        final Map<String, SExpression> parameters = new HashMap<String, SExpression>(connectorInputParameters);\n        parameters.put(\"connectorApiAccessor\", EngineConstantExpressionBuilder.getConnectorAPIAccessorExpression());\n        parameters.put(\"engineExecutionContext\", EngineConstantExpressionBuilder.getEngineExecutionContext());\n\n        final Map<String, Object> result = new HashMap<String, Object>(connectorInputParameters);\n        parameters.put(\"connectorApiAccessor\", EngineConstantExpressionBuilder.getConnectorAPIAccessorExpression());\n        parameters.put(\"engineExecutionContext\", EngineConstantExpressionBuilder.getEngineExecutionContext());\n        final ConnectorResult toBeReturned = new ConnectorResult(null, result, 100);\n        doReturn(toBeReturned).when(connectorService).executeMultipleEvaluation(processDefinitionId,\n                connectorDefinitionId,\n                connectorDefinitionVersion, parameters, inputValues, classLoader, sexpContext);\n\n        final ConnectorResult connectorResult = connectorServiceDecorator.executeMultipleEvaluation(processDefinitionId,\n                connectorDefinitionId,\n                connectorDefinitionVersion, connectorInputParameters, inputValues, classLoader, sexpContext);\n        assertEquals(toBeReturned, connectorResult);\n    }\n\n    @Test\n    public final void checkIfEngineExecutionContextIsPutOnParametersWhenEvaluateInputParameters() throws Exception {\n        final Map<String, SExpression> parameters = Collections.emptyMap();\n        final SExpressionContext sExpressionContext = null;\n        final Map<String, Map<String, Serializable>> inputValues = Collections.emptyMap();\n        final Map<String, SExpression> newParameters = new HashMap<String, SExpression>(parameters);\n        newParameters.put(\"connectorApiAccessor\", EngineConstantExpressionBuilder.getConnectorAPIAccessorExpression());\n        newParameters.put(\"engineExecutionContext\", EngineConstantExpressionBuilder.getEngineExecutionContext());\n\n        final Map<String, Object> toBeReturned = new HashMap<String, Object>(parameters);\n        toBeReturned.put(\"connectorApiAccessor\", EngineConstantExpressionBuilder.getConnectorAPIAccessorExpression());\n        toBeReturned.put(\"engineExecutionContext\", EngineConstantExpressionBuilder.getEngineExecutionContext());\n        doReturn(toBeReturned).when(connectorService).evaluateInputParameters(\"connectorId\", newParameters,\n                sExpressionContext, inputValues);\n\n        final Map<String, Object> evaluateInputParameters = connectorServiceDecorator.evaluateInputParameters(\n                \"connectorId\", parameters, sExpressionContext,\n                inputValues);\n        assertEquals(toBeReturned, evaluateInputParameters);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/core/document/api/impl/DocumentHelperTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.document.api.impl;\n\nimport static java.util.Arrays.asList;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.nullable;\nimport static org.mockito.Mockito.*;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.contract.FileInputValue;\nimport org.bonitasoft.engine.bpm.document.DocumentValue;\nimport org.bonitasoft.engine.bpm.document.impl.DocumentImpl;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.core.document.api.DocumentService;\nimport org.bonitasoft.engine.core.document.model.AbstractSMappedDocument;\nimport org.bonitasoft.engine.core.document.model.SDocument;\nimport org.bonitasoft.engine.core.document.model.SMappedDocument;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.core.process.definition.model.SDocumentListDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SDocumentListDefinitionImpl;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class DocumentHelperTest {\n\n    public static final long AUTHOR_ID = 12L;\n    public static final long PROCESS_INSTANCE_ID = 45L;\n    @Rule\n    public ExpectedException exception = ExpectedException.none();\n    @Mock\n    private ProcessDefinitionService processDefinitionService;\n    @Mock\n    private ProcessInstanceService processInstanceService;\n    @Mock\n    private SProcessInstance processInstance;\n    @Mock\n    private SProcessDefinition processDefinition;\n    @Mock\n    private SFlowElementContainerDefinition flowElementContainerDefinition;\n    @Mock\n    private DocumentService documentService;\n    @InjectMocks\n    private DocumentHelper documentHelper;\n\n    @Test\n    public void should_isDefinedInDefinition_return_false_id_not_in_def() throws Exception {\n        //given\n        initDefinition(\"list1\", \"list2\");\n        //when then\n        assertThat(documentHelper.isListDefinedInDefinition(\"theList\", PROCESS_INSTANCE_ID)).isFalse();\n    }\n\n    @Test\n    public void should_isDefinedInDefinition_throw_not_found_when_not_existing_instance() throws Exception {\n        //given\n        initDefinition(\"list1\", \"list2\");\n        doThrow(SProcessInstanceNotFoundException.class).when(processInstanceService)\n                .getProcessInstance(PROCESS_INSTANCE_ID);\n        exception.expect(org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException.class);\n        exception.expectMessage(\n                \"Unable to find the list theList, nothing in database and the process instance 45 is not found\");\n        //when\n        documentHelper.isListDefinedInDefinition(\"theList\", PROCESS_INSTANCE_ID);\n        //then exception\n    }\n\n    @Test\n    public void should_isDefinedInDefinition_throw_not_found_when_not_existing_definition() throws Exception {\n        initDefinition(\"list1\", \"list2\");\n        doThrow(SProcessDefinitionNotFoundException.class).when(processDefinitionService).getProcessDefinition(154L);\n        exception.expect(org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException.class);\n        exception.expectMessage(\n                \"Unable to find the list theList on process instance 45, nothing in database and the process definition is not found\");\n        //when\n        documentHelper.isListDefinedInDefinition(\"theList\", PROCESS_INSTANCE_ID);\n    }\n\n    @Test\n    public void should_isDefinedInDefinition_throw_read_ex_when_not_existing_instance() throws Exception {\n        initDefinition(\"list1\", \"list2\");\n        doThrow(SProcessInstanceReadException.class).when(processInstanceService)\n                .getProcessInstance(PROCESS_INSTANCE_ID);\n        exception.expect(SBonitaReadException.class);\n        documentHelper.isListDefinedInDefinition(\"theList\", PROCESS_INSTANCE_ID);\n    }\n\n    @Test\n    public void should_isDefinedInDefinition_throw_read_ex_when_not_existing_definition() throws Exception {\n        initDefinition(\"list1\", \"list2\");\n        doThrow(SBonitaReadException.class).when(processDefinitionService).getProcessDefinition(154L);\n        exception.expect(SBonitaReadException.class);\n        documentHelper.isListDefinedInDefinition(\"theList\", 45);\n    }\n\n    @Test\n    public void should_isDefinedInDefinition_return_true_if_in_def() throws Exception {\n        //given\n        initDefinition(\"list1\", \"list2\", \"theList\");\n        //when then\n        assertThat(documentHelper.isListDefinedInDefinition(\"theList\", PROCESS_INSTANCE_ID)).isTrue();\n    }\n\n    private void initDefinition(String... names) throws SProcessInstanceNotFoundException,\n            SProcessInstanceReadException, SProcessDefinitionNotFoundException,\n            SBonitaReadException {\n        doReturn(processInstance).when(processInstanceService).getProcessInstance(PROCESS_INSTANCE_ID);\n        doReturn(154L).when(processInstance).getProcessDefinitionId();\n        doReturn(processDefinition).when(processDefinitionService).getProcessDefinition(154L);\n        doReturn(flowElementContainerDefinition).when(processDefinition).getProcessContainer();\n        doReturn(createListOfDocumentListDefinition(names)).when(flowElementContainerDefinition)\n                .getDocumentListDefinitions();\n    }\n\n    private List<SDocumentListDefinition> createListOfDocumentListDefinition(String... names) {\n        List<SDocumentListDefinition> list = new ArrayList<>();\n        for (String name : names) {\n            list.add(new SDocumentListDefinitionImpl(name));\n        }\n        return list;\n    }\n\n    @Test\n    public void should_getAllDocumentOfTheList_return_all_the_elements() throws Exception {\n        //given\n        List<AbstractSMappedDocument> list = createList(100);\n        doReturn(list).when(documentService).getDocumentList(\"theList\", PROCESS_INSTANCE_ID, 0, 100);\n        List<AbstractSMappedDocument> list2 = createList(50);\n        doReturn(list2).when(documentService).getDocumentList(\"theList\", PROCESS_INSTANCE_ID, 100, 100);\n        //when\n        List<AbstractSMappedDocument> theList = documentHelper.getAllDocumentOfTheList(PROCESS_INSTANCE_ID, \"theList\");\n\n        //then\n        ArrayList<AbstractSMappedDocument> expected = new ArrayList<>(list);\n        expected.addAll(list2);\n        assertThat(theList).hasSize(150);\n        assertThat(theList).isEqualTo(expected);\n    }\n\n    private List<AbstractSMappedDocument> createList(int size) {\n        List<AbstractSMappedDocument> sMappedDocuments = new ArrayList<>(size);\n        for (int i = 0; i < size; i++) {\n            SMappedDocument sMappedDocument = new SMappedDocument();\n            sMappedDocument.setId(size + (i * 1000));\n            sMappedDocuments.add(sMappedDocument);\n        }\n        return sMappedDocuments;\n    }\n\n    @Test\n    public void should_createDocumentObject_have_all_fields() {\n        DocumentValue documentValue = new DocumentValue(\"plop\".getBytes(), \"mime\", \"filename\");\n        SDocument documentObject = documentHelper.createDocumentObject(documentValue, AUTHOR_ID);\n        assertThat(documentObject.getContent()).isEqualTo(\"plop\".getBytes());\n        assertThat(documentObject.getMimeType()).isEqualTo(\"mime\");\n        assertThat(documentObject.getFileName()).isEqualTo(\"filename\");\n        assertThat(documentObject.hasContent()).isTrue();\n        assertThat(documentObject.getAuthor()).isEqualTo(AUTHOR_ID);\n    }\n\n    @Test\n    public void should_delete_document_removeCurrentVersion()\n            throws SObjectModificationException, SObjectNotFoundException {\n        //when\n        documentHelper.deleteDocument(\"myDoc\", PROCESS_INSTANCE_ID);\n        //then\n        verify(documentService).removeCurrentVersion(PROCESS_INSTANCE_ID, \"myDoc\");\n    }\n\n    @Test\n    public void should_delete_document_be_quiet_when_not_found()\n            throws SObjectModificationException, SObjectNotFoundException {\n        //given\n        doThrow(SObjectNotFoundException.class).when(documentService).removeCurrentVersion(PROCESS_INSTANCE_ID,\n                \"myDoc\");\n        //when\n        documentHelper.deleteDocument(\"myDoc\", PROCESS_INSTANCE_ID);\n        //no exception\n    }\n\n    @Test(expected = SObjectModificationException.class)\n    public void should_delete_document_throw_SObjectModificationException()\n            throws SObjectModificationException, SObjectNotFoundException {\n        //given\n        doThrow(SObjectModificationException.class).when(documentService).removeCurrentVersion(PROCESS_INSTANCE_ID,\n                \"myDoc\");\n        //when\n        documentHelper.deleteDocument(\"myDoc\", PROCESS_INSTANCE_ID);\n    }\n\n    @Test\n    public void should_createOrUpdate_update_if_found() throws Exception {\n        //given\n        SMappedDocument docToUpdate = new SMappedDocument();\n        docToUpdate.setId(145);\n        doReturn(docToUpdate).when(documentService).getMappedDocument(PROCESS_INSTANCE_ID, \"myDoc\");\n        //when\n        DocumentValue docValue = new DocumentValue(\"myUrl\");\n        documentHelper.createOrUpdateDocument(docValue, \"myDoc\", PROCESS_INSTANCE_ID, AUTHOR_ID, null);\n        //then\n        verify(documentService).updateDocument(eq(docToUpdate), any(SDocument.class));\n    }\n\n    @Test\n    public void should_createOrUpdate_create_if_not_found_if_found() throws Exception {\n        //given\n        doThrow(SObjectNotFoundException.class).when(documentService).getMappedDocument(PROCESS_INSTANCE_ID, \"myDoc\");\n        //when\n        DocumentValue docValue = new DocumentValue(\"myUrl\");\n        documentHelper.createOrUpdateDocument(docValue, \"myDoc\", PROCESS_INSTANCE_ID, AUTHOR_ID, null);\n        //then\n        verify(documentService).attachDocumentToProcessInstance(any(SDocument.class), eq(PROCESS_INSTANCE_ID),\n                eq(\"myDoc\"), nullable(String.class));\n    }\n\n    @Test\n    public void should_setDocumentList_set_the_list() throws Exception {\n        DocumentHelper documentHelperSpy = spy(documentHelper);\n        List<AbstractSMappedDocument> existingList = Collections.singletonList(new SMappedDocument());\n        doReturn(existingList).when(documentHelperSpy).getExistingDocumentList(\"theList\", PROCESS_INSTANCE_ID);\n\n        DocumentValue docValue1 = new DocumentValue(\"url1\");\n        DocumentValue docValue2 = new DocumentValue(\"url2\");\n        documentHelperSpy.setDocumentList(asList(docValue1, docValue2), \"theList\", PROCESS_INSTANCE_ID, AUTHOR_ID);\n\n        verify(documentHelperSpy).processDocumentOnIndex(docValue1, \"theList\", PROCESS_INSTANCE_ID, existingList, 0,\n                AUTHOR_ID);\n        verify(documentHelperSpy).processDocumentOnIndex(docValue2, \"theList\", PROCESS_INSTANCE_ID, existingList, 1,\n                AUTHOR_ID);\n        verify(documentHelperSpy).removeOthersDocuments(existingList);\n    }\n\n    @Test\n    public void should_getExistingDocumentList_return_the_list() throws Exception {\n        //given\n        DocumentHelper documentHelperSpy = spy(documentHelper);\n        List<AbstractSMappedDocument> existingList = createList(5);\n        doReturn(existingList).when(documentHelperSpy).getAllDocumentOfTheList(PROCESS_INSTANCE_ID, \"theList\");\n        //when\n        List<AbstractSMappedDocument> theList = documentHelperSpy.getExistingDocumentList(\"theList\",\n                PROCESS_INSTANCE_ID);\n        //then\n        assertThat(theList).isEqualTo(existingList);\n    }\n\n    @Test\n    public void should_getExistingDocumentList_return_empty_if_in_def() throws Exception {\n        //given\n        DocumentHelper documentHelperSpy = spy(documentHelper);\n        doReturn(Collections.emptyList()).when(documentHelperSpy).getAllDocumentOfTheList(PROCESS_INSTANCE_ID,\n                \"theList\");\n        doReturn(true).when(documentHelperSpy).isListDefinedInDefinition(\"theList\", PROCESS_INSTANCE_ID);\n        //when\n        List<AbstractSMappedDocument> theList = documentHelperSpy.getExistingDocumentList(\"theList\",\n                PROCESS_INSTANCE_ID);\n        //then\n        assertThat(theList).isEmpty();\n    }\n\n    @Test(expected = SObjectNotFoundException.class)\n    public void should_getExistingDocumentList_return_throw_exception_if_not_in_def() throws Exception {\n        //given\n        DocumentHelper documentHelperSpy = spy(documentHelper);\n        doReturn(Collections.emptyList()).when(documentHelperSpy).getAllDocumentOfTheList(PROCESS_INSTANCE_ID,\n                \"theList\");\n        doReturn(false).when(documentHelperSpy).isListDefinedInDefinition(\"theList\", PROCESS_INSTANCE_ID);\n        //when\n        documentHelperSpy.getExistingDocumentList(\"theList\", PROCESS_INSTANCE_ID);\n        //then exception\n    }\n\n    @Test\n    public void should_processDocumentOnIndex_create_new() throws Exception {\n        //given\n        List<AbstractSMappedDocument> list = createList(5);\n        DocumentValue documentValue = new DocumentValue(\"new url\");\n        //when\n        documentHelper.processDocumentOnIndex(documentValue, \"theList\", PROCESS_INSTANCE_ID, list, 3, AUTHOR_ID);\n        //then\n        verify(documentService).attachDocumentToProcessInstance(any(SDocument.class), eq(PROCESS_INSTANCE_ID),\n                eq(\"theList\"), nullable(String.class), eq(3));\n    }\n\n    @Test\n    public void should_processDocumentOnIndex_update_index() throws Exception {\n        //given\n        DocumentHelper documentHelperSpy = spy(documentHelper);\n        List<AbstractSMappedDocument> list = createList(5);\n        AbstractSMappedDocument documentToUpdate = list.get(list.size() - 1);\n        DocumentValue documentValue = new DocumentValue(documentToUpdate.getId(), \"new url\");\n        //when\n        documentHelperSpy.processDocumentOnIndex(documentValue, \"theList\", PROCESS_INSTANCE_ID, list, 3, AUTHOR_ID);\n        //then\n        verify(documentHelperSpy).updateExistingDocument(documentToUpdate, 3, documentValue, AUTHOR_ID);\n    }\n\n    @Test(expected = SObjectNotFoundException.class)\n    public void should_processDocumentOnIndex_update_index_of_non_existing_doc() throws Exception {\n        //given\n        DocumentHelper documentHelperSpy = spy(documentHelper);\n        List<AbstractSMappedDocument> list = createList(5);\n        DocumentValue documentValue = new DocumentValue(125L, \"new url\");\n        //when\n        documentHelperSpy.processDocumentOnIndex(documentValue, \"theList\", PROCESS_INSTANCE_ID, list, 3, AUTHOR_ID);\n        //then exception\n    }\n\n    @Test\n    public void should_updateExistingDocument_with_unmodified_content_update_only_index() throws Exception {\n        //given\n        DocumentValue documentValue = new DocumentValue(125L);\n        SMappedDocument documentToUpdate = new SMappedDocument();\n        documentToUpdate.setIndex(1);\n        //when\n        documentHelper.updateExistingDocument(documentToUpdate, 2, documentValue, AUTHOR_ID);\n        //then\n        verify(documentService).updateDocumentIndex(documentToUpdate, 2);\n    }\n\n    @Test\n    public void should_updateExistingDocument_with_unmodified_content_and_index_do_nothing() throws Exception {\n        //given\n        DocumentValue documentValue = new DocumentValue(125L);\n        SMappedDocument documentToUpdate = new SMappedDocument();\n        documentToUpdate.setIndex(2);\n        //when\n        documentHelper.updateExistingDocument(documentToUpdate, 2, documentValue, AUTHOR_ID);\n        //then\n        verify(documentService, times(0)).updateDocumentIndex(documentToUpdate, 2);\n    }\n\n    @Test\n    public void should_updateExistingDocument_with_modified_content_update_everything() throws Exception {\n        //given\n        DocumentValue documentValue = new DocumentValue(125L, \"the new url\");\n        SMappedDocument documentToUpdate = new SMappedDocument();\n        documentToUpdate.setIndex(1);\n        //when\n        documentHelper.updateExistingDocument(documentToUpdate, 2, documentValue, AUTHOR_ID);\n        //then\n        verify(documentService).updateDocumentOfList(eq(documentToUpdate), any(SDocument.class), eq(2));\n    }\n\n    @Test\n    public void should_getMimeTypeOrGuessIt_return_the_original_mime_type_if_not_null() {\n        //given\n        final DocumentValue documentValue = new DocumentValue(new byte[] { 1, 2 }, \"myMimeType\", \"theFile.bin\");\n        //when\n        final String mimeTypeOrGuessIt = documentHelper.getMimeTypeOrGuessIt(documentValue);\n        //then\n        assertThat(mimeTypeOrGuessIt).isEqualTo(\"myMimeType\");\n\n    }\n\n    @Test\n    public void should_getMimeTypeOrGuessIt_guess_plain_text_mime_type() {\n        //given\n        final DocumentValue documentValue = new DocumentValue(\"content\".getBytes(), null, \"theFile.txt\");\n        //when\n        final String mimeTypeOrGuessIt = documentHelper.getMimeTypeOrGuessIt(documentValue);\n        //then\n        assertThat(mimeTypeOrGuessIt == null || mimeTypeOrGuessIt.equals(\"text/plain\"))\n                .as(\"mimetype should be text/plain or null on mac\").isTrue();\n    }\n\n    @Test\n    public void should_getMimeTypeOrGuessIt_guess_plain_text_mime_type_when_empty() {\n        //given\n        final DocumentValue documentValue = new DocumentValue(\"content\".getBytes(), \"\", \"theFile.txt\");\n        //when\n        final String mimeTypeOrGuessIt = documentHelper.getMimeTypeOrGuessIt(documentValue);\n        //then\n        assertThat(mimeTypeOrGuessIt == null || mimeTypeOrGuessIt.equals(\"text/plain\"))\n                .as(\"mimetype should be text/plain or null on mac\").isTrue();\n    }\n\n    @Test\n    public void should_getMimeTypeOrGuessIt_guess_xml_mime_type() {\n        //given\n        final String xmlFileContent = \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>\\n\" +\n                \"<note>\\n\" +\n                \"\\t<to>Tove</to>\\n\" +\n                \"\\t<from>Jani</from>\\n\" +\n                \"\\t<heading>Reminder</heading>\\n\" +\n                \"\\t<body>Don't forget me this weekend!</body>\\n\" +\n                \"</note>\";\n        final DocumentValue documentValue = new DocumentValue(xmlFileContent.getBytes(), null, \"theFile.xml\");\n        //when\n        final String mimeTypeOrGuessIt = documentHelper.getMimeTypeOrGuessIt(documentValue);\n        //then\n        assertThat(mimeTypeOrGuessIt).as(\"mimetype\").isIn( //\n                null, \"application/xml\" // Mac/Linux\n                , \"text/xml\" // Windows\n        );\n    }\n\n    @Test\n    public void should_getMimeTypeOrGuessIt_do_not_fail_with_bad_filename() {\n        //given\n        final DocumentValue documentValue = new DocumentValue(new byte[] { 1, 2 }, null, \"the\\0File.bin\");\n        //when\n        final String mimeTypeOrGuessIt = documentHelper.getMimeTypeOrGuessIt(documentValue);\n        //then\n        assertThat(mimeTypeOrGuessIt).isNull();\n    }\n\n    @Test\n    public void should_toCheckedDocumentValue_return_new_DocumentValue_for_FileInput() throws Exception {\n        final FileInputValue fileInputValue = new FileInputValue(\"theFile.txt\", \"contentType\",\n                \"It's my file\".getBytes());\n\n        final DocumentValue documentValue = documentHelper.toCheckedDocumentValue(fileInputValue);\n\n        assertThat(documentValue).isEqualToIgnoringGivenFields(new DocumentValue(null, \"contentType\", \"theFile.txt\"),\n                \"content\");\n        assertThat(documentValue.getContent()).isEqualTo(\"It's my file\".getBytes());\n    }\n\n    @Test\n    public void should_toCheckedList_check_null() throws Exception {\n        exception.expect(SOperationExecutionException.class);\n        exception.expectMessage(\"Document operation only accepts an expression returning a list of DocumentValue\");\n        documentHelper.toCheckedList(null);\n    }\n\n    @Test\n    public void should_toCheckedList_check_not_list() throws Exception {\n        exception.expect(SOperationExecutionException.class);\n        exception.expectMessage(\"Document operation only accepts an expression returning a list of DocumentValue\");\n        documentHelper.toCheckedList(new Object());\n    }\n\n    @Test\n    public void should_toCheckedList_check_not_all_doc() throws Exception {\n        exception.expect(SOperationExecutionException.class);\n        exception.expectMessage(\"Document operation only accepts an expression returning a list of DocumentValue\");\n        documentHelper.toCheckedList(asList(new DocumentValue(\"theUrl\"), new Object()));\n    }\n\n    @Test\n    public void should_toCheckedList_returns_the_list_if_contains_FileInputValue() throws Exception {\n        final List<FileInputValue> inputList = Collections\n                .singletonList(new FileInputValue(\"report.pdf\", \"contentType\", \"The report content\".getBytes()));\n        final List<DocumentValue> result = documentHelper.toCheckedList(inputList);\n        assertThat(result.get(0).getContent()).isEqualTo(\"The report content\".getBytes());\n        assertThat(result.get(0).getFileName()).isEqualTo(\"report.pdf\");\n        assertThat(result.get(0).getMimeType()).isEqualTo(\"contentType\");\n    }\n\n    @Test\n    public void should_convert_FileInputValue_to_DocumentValue() {\n        FileInputValue fileInputValue = new FileInputValue(\"report.pdf\", \"contentType\",\n                \"The report content\".getBytes());\n\n        DocumentValue result = documentHelper.toDocumentValue(fileInputValue);\n\n        assertThat(result).isEqualToComparingFieldByField(\n                new DocumentValue(\"The report content\".getBytes(), \"contentType\", \"report.pdf\"));\n    }\n\n    @Test\n    public void should_convert_FileInputValue_having_id_and_updated_content_to_DocumentValue() {\n        FileInputValue fileInputValue = new FileInputValue(\"report.pdf\", \"contentType\", \"The report content\".getBytes(),\n                \"55\");\n\n        DocumentValue result = documentHelper.toDocumentValue(fileInputValue);\n\n        DocumentValue expected = new DocumentValue(55L, \"The report content\".getBytes(), \"contentType\", \"report.pdf\");\n        assertThat(result).isEqualToComparingFieldByField(expected);\n    }\n\n    @Test\n    public void should_convert_FileInputValue_having_id_and_unchanged_content_to_DocumentValue() {\n        FileInputValue fileInputValue = new FileInputValue(\"report.pdf\", \"contentType\", null, \"55\");\n\n        DocumentValue result = documentHelper.toDocumentValue(fileInputValue);\n\n        DocumentValue expected = new DocumentValue(55L);\n        assertThat(result).isEqualToComparingFieldByField(expected);\n    }\n\n    @Test\n    public void should_toCheckedList_returns_the_list_if_contains_DocumentImpl() throws Exception {\n        DocumentImpl documentWithContent = new DocumentImpl();\n        documentWithContent.setHasContent(true);\n        documentWithContent.setContentMimeType(\"application/pdf\");\n        documentWithContent.setContentStorageId(\"the storage ID\");\n        documentWithContent.setFileName(\"myPdf.pdf\");\n        final List<DocumentImpl> inputList = Collections.singletonList(documentWithContent);\n        doReturn(\"the pdf content\".getBytes()).when(documentService).getDocumentContent(\"the storage ID\");\n\n        final List<DocumentValue> result = documentHelper.toCheckedList(inputList);\n\n        assertThat(result.get(0).getContent()).isEqualTo(\"the pdf content\".getBytes());\n        assertThat(result.get(0).getFileName()).isEqualTo(\"myPdf.pdf\");\n        assertThat(result.get(0).getMimeType()).isEqualTo(\"application/pdf\");\n    }\n\n    @Test\n    public void should_toCheckedList_returns_the_list_if_contains_external_DocumentImpl() throws Exception {\n        DocumentImpl documentWithContent = new DocumentImpl();\n        documentWithContent.setHasContent(false);\n        documentWithContent.setUrl(\"the url\");\n        final List<DocumentImpl> inputList = Collections.singletonList(documentWithContent);\n\n        final List<DocumentValue> result = documentHelper.toCheckedList(inputList);\n\n        assertThat(result.get(0).getUrl()).isEqualTo(\"the url\");\n    }\n\n    @Test\n    public void should_toCheckedList_throw_exception_with_document_having_content_not_found() throws Exception {\n        DocumentImpl documentWithContent = new DocumentImpl();\n        documentWithContent.setHasContent(true);\n        documentWithContent.setContentMimeType(\"application/pdf\");\n        documentWithContent.setContentStorageId(\"the storage ID\");\n        documentWithContent.setFileName(\"myPdf.pdf\");\n        final List<DocumentImpl> inputList = Collections.singletonList(documentWithContent);\n        doThrow(SObjectNotFoundException.class).when(documentService).getDocumentContent(\"the storage ID\");\n\n        exception.expect(SOperationExecutionException.class);\n        exception.expectMessage(\n                \"Unable to execute set document operation because the content of the document to use is not found\");\n\n        documentHelper.toCheckedList(inputList);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/core/form/ExternalURLAdapterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.form;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.page.URLAdapterConstants;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * author Emmanuel Duchastenier\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class ExternalURLAdapterTest {\n\n    ExternalURLAdapter urlAdapter = new ExternalURLAdapter();\n\n    @Test\n    public void adaptShouldAppendParameterNameValuePairs() throws Exception {\n        Map<String, String[]> queryParametersMap = new HashMap<>();\n        queryParametersMap.put(\"someKey\", new String[] { \"true\" });\n        queryParametersMap.put(\"someParam\", new String[] { \"17\", \"641\" });\n\n        Map<String, Serializable> context = new HashMap<>();\n        context.put(URLAdapterConstants.QUERY_PARAMETERS, (Serializable) queryParametersMap);\n\n        String url = \"http://internal.subdomain.company.net/myFormProviderApp/local\";\n\n        String newUrl = urlAdapter.adapt(url, null, context);\n\n        assertThat(newUrl).isEqualTo(url + \"?someKey=true&someParam=17,641\");\n    }\n\n    @Test\n    public void adaptShouldAppendParametersBeforeHashtagIfPresent() throws Exception {\n        Map<String, String[]> queryParametersMap = new HashMap<>();\n        queryParametersMap.put(\"someKey\", new String[] { \"true\" });\n\n        Map<String, Serializable> context = new HashMap<>();\n        context.put(URLAdapterConstants.QUERY_PARAMETERS, (Serializable) queryParametersMap);\n\n        String newUrl = urlAdapter.adapt(\"http://internal.subdomain.company.net/myFormProviderApp/local#myAnchor\", null,\n                context);\n\n        assertThat(newUrl)\n                .isEqualTo(\"http://internal.subdomain.company.net/myFormProviderApp/local?someKey=true#myAnchor\");\n    }\n\n    @Test\n    public void adaptShouldKeepExistingParametersIfPresent() throws Exception {\n        Map<String, String[]> queryParametersMap = new HashMap<>();\n        queryParametersMap.put(\"boniparam\", new String[] { \"bonitabpm\" });\n\n        Map<String, Serializable> context = new HashMap<>();\n        context.put(URLAdapterConstants.QUERY_PARAMETERS, (Serializable) queryParametersMap);\n\n        String newUrl = urlAdapter.adapt(\"http://internal.subdomain.company.net?redirect=false#title17\", null, context);\n\n        assertThat(newUrl)\n                .isEqualTo(\"http://internal.subdomain.company.net?redirect=false&boniparam=bonitabpm#title17\");\n    }\n\n    @Test\n    public void adaptShouldSupportEmptyHashtag() throws Exception {\n        Map<String, String[]> queryParametersMap = new HashMap<>();\n        queryParametersMap.put(\"p\", new String[] { \"value\" });\n\n        Map<String, Serializable> context = new HashMap<>();\n        context.put(URLAdapterConstants.QUERY_PARAMETERS, (Serializable) queryParametersMap);\n\n        String newUrl = urlAdapter.adapt(\"http://internal.subdomain.company.net/page#\", null, context);\n\n        assertThat(newUrl).isEqualTo(\"http://internal.subdomain.company.net/page?p=value#\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/core/form/LegacyURLAdapterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.form;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.hamcrest.CoreMatchers.containsString;\nimport static org.mockito.Mockito.when;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.commons.exceptions.SExecutionException;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.form.FormMappingType;\nimport org.bonitasoft.engine.page.URLAdapterConstants;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class LegacyURLAdapterTest {\n\n    @Mock\n    FormMappingService formMappingService;\n\n    @Mock\n    ProcessDefinitionService processDefinitionService;\n\n    @Mock\n    SProcessDefinition processDefinition;\n\n    @InjectMocks\n    LegacyURLAdapter legacyURLAdapter = new LegacyURLAdapter(processDefinitionService, formMappingService);\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void should_generate_legacy_URL_for_process_start_when_mapping_on_legacy() throws Exception {\n        String mappingKey = \"process/processName/processVersion\";\n        SFormMapping formMapping = new SFormMapping(1L, FormMappingType.PROCESS_START.getId(), null,\n                SFormMapping.TARGET_INTERNAL);\n        when(formMappingService.get(mappingKey)).thenReturn(formMapping);\n        when(processDefinition.getName()).thenReturn(\"processName\");\n        when(processDefinition.getVersion()).thenReturn(\"processVersion\");\n        when(processDefinitionService.getProcessDefinition(1L)).thenReturn(processDefinition);\n\n        Map<String, Serializable> context = new HashMap<>();\n        context.put(URLAdapterConstants.CONTEXT_PATH, \"/bonita\");\n        context.put(URLAdapterConstants.LOCALE, \"en\");\n        Map<String, String[]> queryParametersMap = new HashMap<>();\n        queryParametersMap.put(URLAdapterConstants.ID_QUERY_PARAM, new String[] { \"1\" });\n        context.put(URLAdapterConstants.QUERY_PARAMETERS, (Serializable) queryParametersMap);\n\n        String legacyURL = legacyURLAdapter.adapt(null, mappingKey, context);\n\n        assertThat(legacyURL).isEqualTo(\n                \"/bonita/portal/homepage?ui=form&locale=en&theme=1#mode=form&form=processName--processVersion%24entry&process=1\");\n    }\n\n    @Test\n    public void should_generate_legacy_URL_for_instance_when_mapping_on_legacy() throws Exception {\n        String mappingKey = \"processInstance/processName/processVersion\";\n        SFormMapping formMapping = new SFormMapping(1L, FormMappingType.PROCESS_OVERVIEW.getId(), null,\n                SFormMapping.TARGET_INTERNAL);\n        when(formMappingService.get(mappingKey)).thenReturn(formMapping);\n        when(processDefinition.getName()).thenReturn(\"processName\");\n        when(processDefinition.getVersion()).thenReturn(\"processVersion\");\n        when(processDefinitionService.getProcessDefinition(1L)).thenReturn(processDefinition);\n\n        Map<String, Serializable> context = new HashMap<String, Serializable>();\n        context.put(URLAdapterConstants.CONTEXT_PATH, \"/bonita\");\n        context.put(URLAdapterConstants.LOCALE, \"en\");\n        Map<String, String[]> queryParametersMap = new HashMap<String, String[]>();\n        queryParametersMap.put(URLAdapterConstants.ID_QUERY_PARAM, new String[] { \"42\" });\n        context.put(URLAdapterConstants.QUERY_PARAMETERS, (Serializable) queryParametersMap);\n\n        String legacyURL = legacyURLAdapter.adapt(null, mappingKey, context);\n\n        assertThat(legacyURL).isEqualTo(\n                \"/bonita/portal/homepage?ui=form&locale=en&theme=1#mode=form&form=processName--processVersion%24recap&instance=42&recap=true\");\n    }\n\n    @Test\n    public void should_generate_legacy_URL_for_task_when_mapping_on_legacy() throws Exception {\n        String mappingKey = \"process/processName/processVersion\";\n        SFormMapping formMapping = new SFormMapping(1L, FormMappingType.TASK.getId(), \"taskName\",\n                SFormMapping.TARGET_INTERNAL);\n        when(formMappingService.get(mappingKey)).thenReturn(formMapping);\n        when(processDefinition.getName()).thenReturn(\"processName\");\n        when(processDefinition.getVersion()).thenReturn(\"processVersion\");\n        when(processDefinitionService.getProcessDefinition(1L)).thenReturn(processDefinition);\n\n        Map<String, Serializable> context = new HashMap<>();\n        context.put(URLAdapterConstants.CONTEXT_PATH, \"/bonita\");\n        context.put(URLAdapterConstants.LOCALE, \"en\");\n        Map<String, String[]> queryParametersMap = new HashMap<>();\n        queryParametersMap.put(URLAdapterConstants.ID_QUERY_PARAM, new String[] { \"42\" });\n        queryParametersMap.put(URLAdapterConstants.USER_QUERY_PARAM, new String[] { \"2\" });\n        queryParametersMap.put(URLAdapterConstants.ASSIGN_TASK_QUERY_PARAM, new String[] { \"true\" });\n        context.put(URLAdapterConstants.QUERY_PARAMETERS, (Serializable) queryParametersMap);\n\n        String legacyURL = legacyURLAdapter.adapt(null, mappingKey, context);\n\n        assertThat(legacyURL)\n                .isEqualTo(\n                        \"/bonita/portal/homepage?ui=form&locale=en&theme=1#mode=form&form=processName--processVersion--taskName%24entry&task=42&assignTask=true&userId=2\");\n    }\n\n    @Test\n    public void should_generate_legacy_URL_for_process_start_when_mapping_on_legacy_with_autInstantiate_param()\n            throws Exception {\n        String mappingKey = \"process/processName/processVersion\";\n        SFormMapping formMapping = new SFormMapping(1L, FormMappingType.PROCESS_START.getId(), null,\n                SFormMapping.TARGET_INTERNAL);\n        when(formMappingService.get(mappingKey)).thenReturn(formMapping);\n        when(processDefinition.getName()).thenReturn(\"processName\");\n        when(processDefinition.getVersion()).thenReturn(\"processVersion\");\n        when(processDefinitionService.getProcessDefinition(1L)).thenReturn(processDefinition);\n\n        Map<String, Serializable> context = new HashMap<>();\n        context.put(URLAdapterConstants.CONTEXT_PATH, \"/bonita\");\n        context.put(URLAdapterConstants.LOCALE, \"en\");\n        Map<String, String[]> queryParametersMap = new HashMap<>();\n        queryParametersMap.put(URLAdapterConstants.ID_QUERY_PARAM, new String[] { \"1\" });\n        queryParametersMap.put(URLAdapterConstants.AUTO_INSTANTIATE_QUERY_PARAM, new String[] { \"false\" });\n        context.put(URLAdapterConstants.QUERY_PARAMETERS, (Serializable) queryParametersMap);\n\n        String legacyURL = legacyURLAdapter.adapt(null, mappingKey, context);\n\n        assertThat(legacyURL).isEqualTo(\n                \"/bonita/portal/homepage?ui=form&locale=en&theme=1#mode=form&form=processName--processVersion%24entry&process=1&autoInstantiate=false\");\n    }\n\n    @Test\n    public void should_generate_legacy_URL_when_mapping_on_legacy_with_specific_mode() throws Exception {\n        String mappingKey = \"process/processName/processVersion\";\n        SFormMapping formMapping = new SFormMapping(1L, FormMappingType.PROCESS_START.getId(), null,\n                SFormMapping.TARGET_INTERNAL);\n        when(formMappingService.get(mappingKey)).thenReturn(formMapping);\n        when(processDefinition.getName()).thenReturn(\"processName\");\n        when(processDefinition.getVersion()).thenReturn(\"processVersion\");\n        when(processDefinitionService.getProcessDefinition(1L)).thenReturn(processDefinition);\n\n        Map<String, Serializable> context = new HashMap<>();\n        context.put(URLAdapterConstants.CONTEXT_PATH, \"/bonita\");\n        context.put(URLAdapterConstants.LOCALE, \"en\");\n        Map<String, String[]> queryParametersMap = new HashMap<>();\n        queryParametersMap.put(URLAdapterConstants.ID_QUERY_PARAM, new String[] { \"1\" });\n        queryParametersMap.put(URLAdapterConstants.MODE_QUERY_PARAM, new String[] { \"app\" });\n        context.put(URLAdapterConstants.QUERY_PARAMETERS, (Serializable) queryParametersMap);\n\n        String legacyURL = legacyURLAdapter.adapt(null, mappingKey, context);\n\n        assertThat(legacyURL).isEqualTo(\n                \"/bonita/portal/homepage?ui=form&locale=en&theme=1#mode=app&form=processName--processVersion%24entry&process=1\");\n    }\n\n    @Test\n    public void adaptShouldThrowIllegalArgumentIf_ID_parameterIsNotPresent() throws SExecutionException {\n        Map<String, Serializable> context = new HashMap<>();\n        context.put(URLAdapterConstants.QUERY_PARAMETERS, new HashMap<>());\n\n        expectedException.expect(IllegalArgumentException.class);\n        expectedException.expectMessage(containsString(\"parameter \\\"id\\\"\"));\n\n        legacyURLAdapter.adapt(null, null, context);\n    }\n\n    @Test\n    public void adaptShouldThrowIllegalArgumentIf_ID_parameterHasNoValue() throws SExecutionException {\n        Map<String, String[]> queryParametersMap = new HashMap<>();\n        queryParametersMap.put(URLAdapterConstants.ID_QUERY_PARAM, new String[] {});\n        Map<String, Serializable> context = new HashMap<>();\n        context.put(URLAdapterConstants.QUERY_PARAMETERS, (Serializable) queryParametersMap);\n\n        expectedException.expect(IllegalArgumentException.class);\n        expectedException.expectMessage(containsString(\"parameter \\\"id\\\"\"));\n\n        legacyURLAdapter.adapt(null, null, context);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/data/ParentContainerResolverImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.doReturn;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance;\nimport org.bonitasoft.engine.data.instance.api.DataContainer;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ParentContainerResolverImplTest {\n\n    private static final long PROCESS_INSTANCE_ID = 5l;\n    private static final long CALLER_ID = 12l;\n    private static final long ACTIVITY_INSTANCE_ID = 59l;\n    private static final long SUB_ACTIVITY_ID = 60l;\n    @Mock\n    private ProcessInstanceService processInstanceService;\n    @Mock\n    private FlowNodeInstanceService flowNodeInstanceService;\n    @InjectMocks\n    private ParentContainerResolverImpl parentContainerResolver;\n\n    @Test\n    public void getContainerHierarchy_on_process_with_no_caller() throws Exception {\n        //given\n        doReturn(SProcessInstance.builder().build()).when(processInstanceService).getProcessInstance(5L);\n        //when\n        List<DataContainer> containerHierarchy = parentContainerResolver\n                .getContainerHierarchy(new DataContainer(5L, DataInstanceContainer.PROCESS_INSTANCE.name()));\n        //then\n        assertThat(containerHierarchy).hasSize(1);\n        assertThat(containerHierarchy.get(0))\n                .isEqualTo(new DataContainer(5L, DataInstanceContainer.PROCESS_INSTANCE.name()));\n    }\n\n    @Test\n    public void getContainerHierarchy_on_message() throws Exception {\n        //given\n        //when\n        List<DataContainer> containerHierarchy = parentContainerResolver\n                .getContainerHierarchy(new DataContainer(5l, DataInstanceContainer.MESSAGE_INSTANCE.name()));\n        //then\n        assertThat(containerHierarchy).hasSize(1);\n        assertThat(containerHierarchy.get(0))\n                .isEqualTo(new DataContainer(5l, DataInstanceContainer.MESSAGE_INSTANCE.name()));\n    }\n\n    @Test\n    public void getContainerHierarchy_on_process_with_caller_is_a_call_activity() throws Exception {\n        //given\n        processWithCaller(PROCESS_INSTANCE_ID, CALLER_ID);\n        activityWithType(CALLER_ID, SFlowNodeType.CALL_ACTIVITY, 0, 0, 0, 0);\n        //when\n        List<DataContainer> containerHierarchy = parentContainerResolver\n                .getContainerHierarchy(\n                        new DataContainer(PROCESS_INSTANCE_ID, DataInstanceContainer.PROCESS_INSTANCE.name()));\n        //then\n        assertThat(containerHierarchy).hasSize(1);\n        assertThat(containerHierarchy.get(0))\n                .isEqualTo(new DataContainer(PROCESS_INSTANCE_ID, DataInstanceContainer.PROCESS_INSTANCE.name()));\n    }\n\n    private void activityWithType(long id, final SFlowNodeType type, final long parentProcessInstanceId,\n            final long parentActivityInstanceId,\n            final long parentContainerId, final long rootContainerId)\n            throws SFlowNodeReadException, SFlowNodeNotFoundException {\n        SActivityInstance activity = new SUserTaskInstance() {\n\n            @Override\n            public SFlowNodeType getType() {\n                return type;\n            }\n        };\n        activity.setId(id);\n        doReturn(activity).when(flowNodeInstanceService).getFlowNodeInstance(id);\n        activity.setLogicalGroup(3, parentProcessInstanceId);\n        activity.setLogicalGroup(2, parentActivityInstanceId);\n        activity.setParentContainerId(parentContainerId);\n        activity.setRootContainerId(rootContainerId);\n    }\n\n    private void processWithCaller(long processInstanceId, long callerId)\n            throws SProcessInstanceNotFoundException, SProcessInstanceReadException {\n        SProcessInstance processInstance = new SProcessInstance();\n        processInstance.setCallerId(callerId);\n        doReturn(processInstance).when(processInstanceService).getProcessInstance(processInstanceId);\n    }\n\n    @Test\n    public void getContainerHierarchy_on_process_with_caller_is_a_sub_process() throws Exception {\n        //given\n        processWithCaller(PROCESS_INSTANCE_ID, CALLER_ID);\n        processWithCaller(45l, -1);\n        activityWithType(CALLER_ID, SFlowNodeType.SUB_PROCESS, 45l, -1, 45l, 45l);\n        //when\n        List<DataContainer> containerHierarchy = parentContainerResolver\n                .getContainerHierarchy(\n                        new DataContainer(PROCESS_INSTANCE_ID, DataInstanceContainer.PROCESS_INSTANCE.name()));\n        //then\n        assertThat(containerHierarchy).hasSize(2);\n        assertThat(containerHierarchy.get(0))\n                .isEqualTo(new DataContainer(PROCESS_INSTANCE_ID, DataInstanceContainer.PROCESS_INSTANCE.name()));\n    }\n\n    @Test\n    public void getContainerHierarchy_on_activity() throws Exception {\n        //given\n        processWithCaller(PROCESS_INSTANCE_ID, -1);\n        activityWithType(ACTIVITY_INSTANCE_ID, SFlowNodeType.USER_TASK, PROCESS_INSTANCE_ID, -1, PROCESS_INSTANCE_ID,\n                PROCESS_INSTANCE_ID);\n        //when\n        List<DataContainer> containerHierarchy = parentContainerResolver\n                .getContainerHierarchy(\n                        new DataContainer(ACTIVITY_INSTANCE_ID, DataInstanceContainer.ACTIVITY_INSTANCE.name()));\n        //then\n        assertThat(containerHierarchy).hasSize(2);\n        assertThat(containerHierarchy.get(0))\n                .isEqualTo(new DataContainer(ACTIVITY_INSTANCE_ID, DataInstanceContainer.ACTIVITY_INSTANCE.name()));\n        assertThat(containerHierarchy.get(1))\n                .isEqualTo(new DataContainer(PROCESS_INSTANCE_ID, DataInstanceContainer.PROCESS_INSTANCE.name()));\n    }\n\n    @Test\n    public void getContainerHierarchy_on_sub_activity() throws Exception {\n        //given\n        processWithCaller(PROCESS_INSTANCE_ID, -1);\n        activityWithType(ACTIVITY_INSTANCE_ID, SFlowNodeType.USER_TASK, PROCESS_INSTANCE_ID, -1, PROCESS_INSTANCE_ID,\n                PROCESS_INSTANCE_ID);\n        activityWithType(SUB_ACTIVITY_ID, SFlowNodeType.USER_TASK, PROCESS_INSTANCE_ID, ACTIVITY_INSTANCE_ID,\n                ACTIVITY_INSTANCE_ID, PROCESS_INSTANCE_ID);\n        //when\n        List<DataContainer> containerHierarchy = parentContainerResolver\n                .getContainerHierarchy(\n                        new DataContainer(SUB_ACTIVITY_ID, DataInstanceContainer.ACTIVITY_INSTANCE.name()));\n        //then\n        assertThat(containerHierarchy).hasSize(3);\n        assertThat(containerHierarchy.get(0))\n                .isEqualTo(new DataContainer(SUB_ACTIVITY_ID, DataInstanceContainer.ACTIVITY_INSTANCE.name()));\n        assertThat(containerHierarchy.get(1))\n                .isEqualTo(new DataContainer(ACTIVITY_INSTANCE_ID, DataInstanceContainer.ACTIVITY_INSTANCE.name()));\n        assertThat(containerHierarchy.get(2))\n                .isEqualTo(new DataContainer(PROCESS_INSTANCE_ID, DataInstanceContainer.PROCESS_INSTANCE.name()));\n    }\n\n    //// archived\n\n    @Test\n    public void getArchivedContainerHierarchy_on_process_with_no_caller() throws Exception {\n        //when\n        List<DataContainer> containerHierarchy = parentContainerResolver\n                .getArchivedContainerHierarchy(new DataContainer(5l, DataInstanceContainer.PROCESS_INSTANCE.name()));\n        //then\n        assertThat(containerHierarchy).hasSize(1);\n        assertThat(containerHierarchy.get(0))\n                .isEqualTo(new DataContainer(5l, DataInstanceContainer.PROCESS_INSTANCE.name()));\n    }\n\n    @Test\n    public void getArchivedContainerHierarchy_on_message() throws Exception {\n        //given\n        //when\n        List<DataContainer> containerHierarchy = parentContainerResolver\n                .getArchivedContainerHierarchy(new DataContainer(5l, DataInstanceContainer.MESSAGE_INSTANCE.name()));\n        //then\n        assertThat(containerHierarchy).hasSize(1);\n        assertThat(containerHierarchy.get(0))\n                .isEqualTo(new DataContainer(5l, DataInstanceContainer.MESSAGE_INSTANCE.name()));\n    }\n\n    @Test\n    public void getArchivedContainerHierarchy_on_process_with_caller_is_a_call_activity() throws Exception {\n        //given\n        processWithCaller(PROCESS_INSTANCE_ID, CALLER_ID);\n        activityWithType(CALLER_ID, SFlowNodeType.CALL_ACTIVITY, 0, 0, 0, 0);\n        //when\n        List<DataContainer> containerHierarchy = parentContainerResolver\n                .getArchivedContainerHierarchy(\n                        new DataContainer(PROCESS_INSTANCE_ID, DataInstanceContainer.PROCESS_INSTANCE.name()));\n        //then\n        assertThat(containerHierarchy).hasSize(1);\n        assertThat(containerHierarchy.get(0))\n                .isEqualTo(new DataContainer(PROCESS_INSTANCE_ID, DataInstanceContainer.PROCESS_INSTANCE.name()));\n    }\n\n    private void archivedActivityWithType(long id, final SFlowNodeType type, final long parentProcessInstanceId,\n            final long parentActivityInstanceId,\n            final long parentContainerId, final long rootContainerId)\n            throws SFlowNodeReadException, SFlowNodeNotFoundException, SBonitaReadException {\n        SAActivityInstance activity = new SAUserTaskInstance() {\n\n            @Override\n            public SFlowNodeType getType() {\n                return type;\n            }\n        };\n        activity.setId(id);\n        doReturn(activity).when(flowNodeInstanceService).getLastArchivedFlowNodeInstance(SAFlowNodeInstance.class, id);\n        activity.setLogicalGroup(3, parentProcessInstanceId);\n        activity.setLogicalGroup(2, parentActivityInstanceId);\n        activity.setParentContainerId(parentContainerId);\n        activity.setRootContainerId(rootContainerId);\n    }\n\n    private void archivedProcessWithCaller(long processInstanceId, long callerId)\n            throws SProcessInstanceNotFoundException, SProcessInstanceReadException, SBonitaReadException {\n        SAProcessInstance processInstance = new SAProcessInstance();\n        processInstance.setCallerId(callerId);\n        doReturn(processInstance).when(processInstanceService).getLastArchivedProcessInstance(processInstanceId);\n    }\n\n    @Test\n    public void getArchivedContainerHierarchy_on_process_with_caller_is_a_sub_process() throws Exception {\n        //given\n        archivedProcessWithCaller(PROCESS_INSTANCE_ID, CALLER_ID);\n        archivedProcessWithCaller(45l, -1);\n        archivedActivityWithType(CALLER_ID, SFlowNodeType.SUB_PROCESS, 45l, -1, 45l, 45l);\n        //when\n        List<DataContainer> containerHierarchy = parentContainerResolver\n                .getArchivedContainerHierarchy(\n                        new DataContainer(PROCESS_INSTANCE_ID, DataInstanceContainer.PROCESS_INSTANCE.name()));\n        //then\n        assertThat(containerHierarchy).hasSize(2);\n        assertThat(containerHierarchy.get(0))\n                .isEqualTo(new DataContainer(PROCESS_INSTANCE_ID, DataInstanceContainer.PROCESS_INSTANCE.name()));\n    }\n\n    @Test\n    public void getArchivedContainerHierarchy_on_activity() throws Exception {\n        //given\n        archivedProcessWithCaller(PROCESS_INSTANCE_ID, -1);\n        archivedActivityWithType(ACTIVITY_INSTANCE_ID, SFlowNodeType.USER_TASK, PROCESS_INSTANCE_ID, -1,\n                PROCESS_INSTANCE_ID, PROCESS_INSTANCE_ID);\n        //when\n        List<DataContainer> containerHierarchy = parentContainerResolver\n                .getArchivedContainerHierarchy(\n                        new DataContainer(ACTIVITY_INSTANCE_ID, DataInstanceContainer.ACTIVITY_INSTANCE.name()));\n        //then\n        assertThat(containerHierarchy).hasSize(2);\n        assertThat(containerHierarchy.get(0))\n                .isEqualTo(new DataContainer(ACTIVITY_INSTANCE_ID, DataInstanceContainer.ACTIVITY_INSTANCE.name()));\n        assertThat(containerHierarchy.get(1))\n                .isEqualTo(new DataContainer(PROCESS_INSTANCE_ID, DataInstanceContainer.PROCESS_INSTANCE.name()));\n    }\n\n    @Test\n    public void getArchivedContainerHierarchy_on_sub_activity() throws Exception {\n        //given\n        archivedProcessWithCaller(PROCESS_INSTANCE_ID, -1);\n        archivedActivityWithType(ACTIVITY_INSTANCE_ID, SFlowNodeType.USER_TASK, PROCESS_INSTANCE_ID, -1,\n                PROCESS_INSTANCE_ID, PROCESS_INSTANCE_ID);\n        archivedActivityWithType(SUB_ACTIVITY_ID, SFlowNodeType.USER_TASK, PROCESS_INSTANCE_ID, ACTIVITY_INSTANCE_ID,\n                ACTIVITY_INSTANCE_ID,\n                PROCESS_INSTANCE_ID);\n        //when\n        List<DataContainer> containerHierarchy = parentContainerResolver\n                .getArchivedContainerHierarchy(\n                        new DataContainer(SUB_ACTIVITY_ID, DataInstanceContainer.ACTIVITY_INSTANCE.name()));\n        //then\n        assertThat(containerHierarchy).hasSize(3);\n        assertThat(containerHierarchy.get(0))\n                .isEqualTo(new DataContainer(SUB_ACTIVITY_ID, DataInstanceContainer.ACTIVITY_INSTANCE.name()));\n        assertThat(containerHierarchy.get(1))\n                .isEqualTo(new DataContainer(ACTIVITY_INSTANCE_ID, DataInstanceContainer.ACTIVITY_INSTANCE.name()));\n        assertThat(containerHierarchy.get(2))\n                .isEqualTo(new DataContainer(PROCESS_INSTANCE_ID, DataInstanceContainer.PROCESS_INSTANCE.name()));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/AdvancedStartProcessValidatorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.Mockito.doReturn;\n\nimport java.io.Serializable;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.core.process.definition.model.SGatewayType;\nimport org.bonitasoft.engine.core.process.definition.model.SType;\nimport org.bonitasoft.engine.core.process.definition.model.event.impl.SBoundaryEventDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.impl.*;\nimport org.bonitasoft.engine.expression.ExpressionService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class AdvancedStartProcessValidatorTest {\n\n    private static final String GATEWAY_NAME = \"gateway\";\n\n    private static final String SUBPROCESS_NAME = \"subprocess\";\n\n    private static final String BOUNDARY_NAME = \"boundary\";\n\n    private static final String USERTASK_NAME = \"usertask\";\n\n    private final long PROCESS_DEFINITION_ID = 1234L;\n    private final long PROCESS_DEFINITION_WITH_CONTRACT_INPUT_ID = 6543654223L;\n\n    @Mock\n    private ProcessDefinitionService processDefinitionService;\n    @Mock\n    private ExpressionService expressionService;\n\n    @Before\n    public void setUp() throws Exception {\n        //process with all kind of elements\n        createCompleteProcess();\n\n        //process with contract inputs\n        createProcessWithContract();\n\n    }\n\n    private void createProcessWithContract() throws SProcessDefinitionNotFoundException, SBonitaReadException {\n        SProcessDefinitionImpl processDefinition = new SProcessDefinitionImpl(\"My process1\", \"1.0\");\n        SFlowElementContainerDefinitionImpl container = new SFlowElementContainerDefinitionImpl();\n        processDefinition.setProcessContainer(container);\n        SGatewayDefinitionImpl gateway = new SGatewayDefinitionImpl(6546543L, GATEWAY_NAME, SGatewayType.EXCLUSIVE);\n        SSubProcessDefinitionImpl subProcess = new SSubProcessDefinitionImpl(64236543L, SUBPROCESS_NAME, true);\n        SBoundaryEventDefinitionImpl boundary = new SBoundaryEventDefinitionImpl(67523L, BOUNDARY_NAME);\n        SUserTaskDefinitionImpl userTask = new SUserTaskDefinitionImpl(23425L, USERTASK_NAME, \"actor\");\n        container.addGateway(gateway);\n        container.addSubProcess(subProcess);\n        container.addActivity(userTask);\n        userTask.addBoundaryEventDefinition(boundary);\n        doReturn(processDefinition).when(processDefinitionService).getProcessDefinition(PROCESS_DEFINITION_ID);\n    }\n\n    private void createCompleteProcess() throws SProcessDefinitionNotFoundException, SBonitaReadException {\n        SProcessDefinitionImpl procDef = new SProcessDefinitionImpl(\"My process2\", \"1.0\");\n        SFlowElementContainerDefinitionImpl container = new SFlowElementContainerDefinitionImpl();\n        procDef.setProcessContainer(container);\n        procDef.setContract(ContractBuilder.contract().input(\"input1\", SType.TEXT).build());\n        SUserTaskDefinitionImpl userTask1 = new SUserTaskDefinitionImpl(23425L, \"userTask1WithContract\", \"actor\");\n        userTask1.setContract(ContractBuilder.contract().input(\"u1Input\", SType.TEXT).build());\n        SUserTaskDefinitionImpl userTask2 = new SUserTaskDefinitionImpl(23425L, \"userTask2WithContract\", \"actor\");\n        userTask2.setContract(ContractBuilder.contract().input(\"u2Input\", SType.TEXT).build());\n        SUserTaskDefinitionImpl userTask3 = new SUserTaskDefinitionImpl(23425L, \"userTask3WithNoContract\", \"actor\");\n        SUserTaskDefinitionImpl userTask4 = new SUserTaskDefinitionImpl(23425L, \"userTask4WithMultipleContract\",\n                \"actor\");\n        userTask4.setContract(ContractBuilder.contract().input(\"u4Input1\", SType.TEXT).build());\n        userTask4.setContract(ContractBuilder.contract().input(\"u4Input2\", SType.INTEGER).build());\n        container.addActivity(userTask1);\n        container.addActivity(userTask2);\n        container.addActivity(userTask3);\n        container.addActivity(userTask4);\n        doReturn(procDef).when(processDefinitionService)\n                .getProcessDefinition(PROCESS_DEFINITION_WITH_CONTRACT_INPUT_ID);\n    }\n\n    private AdvancedStartProcessValidator createValidatorFor(long process_definition_id) {\n        return new AdvancedStartProcessValidator(processDefinitionService, process_definition_id, expressionService);\n    }\n\n    @Test\n    public void validate_return_problem_if_one_of_chosen_flownode_is_a_boundary() throws Exception {\n        AdvancedStartProcessValidator starterValidator = createValidatorFor(PROCESS_DEFINITION_ID);\n\n        List<String> problems = starterValidator.validate(Collections.singletonList(BOUNDARY_NAME), null);\n        assertEquals(1, problems.size());\n        assertTrue(problems.get(0).contains(BOUNDARY_NAME));\n    }\n\n    @Test\n    public void validate_return_problem_if_one_of_chosen_flownode_is_a_subprocess() throws Exception {\n        AdvancedStartProcessValidator starterValidator = createValidatorFor(PROCESS_DEFINITION_ID);\n        List<String> problems = starterValidator.validate(Collections.singletonList(SUBPROCESS_NAME), null);\n        assertEquals(1, problems.size());\n        assertTrue(problems.get(0).contains(SUBPROCESS_NAME));\n    }\n\n    @Test\n    public void validate_return_problem_if_one_of_chosen_flownode_is_a_gateway() throws Exception {\n        AdvancedStartProcessValidator starterValidator = createValidatorFor(PROCESS_DEFINITION_ID);\n        List<String> problems = starterValidator.validate(Collections.singletonList(GATEWAY_NAME), null);\n        assertEquals(1, problems.size());\n        assertTrue(problems.get(0).contains(GATEWAY_NAME));\n    }\n\n    @Test\n    public void validate_return_empty_list_if_is_valid() throws Exception {\n        AdvancedStartProcessValidator starterValidator = createValidatorFor(PROCESS_DEFINITION_ID);\n        List<String> problems = starterValidator.validate(Collections.singletonList(USERTASK_NAME), null);\n        assertEquals(0, problems.size());\n    }\n\n    @Test\n    public void validate_return_problem_if_one_element_is_not_found() throws Exception {\n        AdvancedStartProcessValidator starterValidator = createValidatorFor(PROCESS_DEFINITION_ID);\n        List<String> problems = starterValidator.validate(Collections.singletonList(\"unknowntask\"), null);\n        assertEquals(1, problems.size());\n        assertTrue(problems.get(0).contains(\"unknowntask\"));\n    }\n\n    @Test\n    public void validate_return_problem_if_the_flowNode_name_list_is_empty() throws Exception {\n        AdvancedStartProcessValidator starterValidator = createValidatorFor(PROCESS_DEFINITION_ID);\n        List<String> problems = starterValidator.validate(Collections.<String> emptyList(), null);\n        assertEquals(1, problems.size());\n        assertTrue(problems.get(0).contains(\"empty\"));\n    }\n\n    @Test\n    public void should_detect_no_problem_when_process_contract_input_are_present_and_required() throws Exception {\n        //given\n        AdvancedStartProcessValidator starterValidator = createValidatorFor(PROCESS_DEFINITION_WITH_CONTRACT_INPUT_ID);\n        //when\n        List<String> problems = starterValidator.validate(Collections.singletonList(\"userTask3WithNoContract\"),\n                inputs(\"input1\"));\n        //then\n        assertThat(problems).isEmpty();\n    }\n\n    private Map<String, Serializable> inputs(String input1) {\n        return Collections.singletonMap(input1, (Serializable) \"value\");\n    }\n\n    @Test\n    public void should_detect_problem_when_when_process_contract_input_are_required_and_not_given() throws Exception {\n        //given\n        AdvancedStartProcessValidator starterValidator = createValidatorFor(PROCESS_DEFINITION_WITH_CONTRACT_INPUT_ID);\n        //when\n        List<String> problems = starterValidator.validate(Collections.singletonList(\"userTask3WithNoContract\"), null);\n        //then\n        assertThat(problems).containsOnly(\"Expected input [input1] is missing on My process2\");\n    }\n\n    @Test\n    public void should_detect_problem_when_when_process_contract_input_are_required_and_not_all_given()\n            throws Exception {\n        //given\n        AdvancedStartProcessValidator starterValidator = createValidatorFor(PROCESS_DEFINITION_WITH_CONTRACT_INPUT_ID);\n        //when\n        List<String> problems = starterValidator.validate(Collections.singletonList(\"userTask3WithNoContract\"),\n                inputs(\"unknown\"));\n        //then\n        assertThat(problems).containsOnly(\"Expected input [input1] is missing on My process2\");\n    }\n\n    private static class ContractBuilder {\n\n        private SContractDefinitionImpl sContractDefinition = new SContractDefinitionImpl();\n\n        static ContractBuilder contract() {\n            return new ContractBuilder();\n        }\n\n        public SContractDefinitionImpl build() {\n            return sContractDefinition;\n        }\n\n        public ContractBuilder input(String name, SType type) {\n            sContractDefinition.addInput(new SInputDefinitionImpl(name, type, \"\"));\n            return this;\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/CountAnswer.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport org.mockito.invocation.InvocationOnMock;\nimport org.mockito.stubbing.Answer;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class CountAnswer implements Answer<Void> {\n\n    private int count;\n\n    public int getCount() {\n        return count;\n    }\n\n    @Override\n    public Void answer(final InvocationOnMock invocation) {\n        count++;\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/FlowNodeExecutorImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.core.process.instance.model.SStateCategory.ABORTING;\nimport static org.mockito.Mockito.*;\n\nimport org.bonitasoft.engine.archive.ArchiveService;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException;\nimport org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance;\nimport org.bonitasoft.engine.execution.archive.BPMArchiverService;\nimport org.bonitasoft.engine.execution.state.FlowNodeStateManager;\nimport org.bonitasoft.engine.execution.state.SkippedFlowNodeState;\nimport org.bonitasoft.engine.execution.work.BPMWorkFactory;\nimport org.bonitasoft.engine.work.WorkDescriptor;\nimport org.bonitasoft.engine.work.WorkService;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Baptiste Mesta.\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class FlowNodeExecutorImplTest {\n\n    private static final long PROCESS_INSTANCE_ID = 343332L;\n    @Mock\n    private WorkService workService;\n    private BPMWorkFactory workFactory = new BPMWorkFactory();\n    @Mock\n    private ActivityInstanceService activityInstanceService;\n    @Mock\n    private ContainerRegistry containerRegistry;\n    @Mock\n    private FlowNodeStateManager flowNodeStateManager;\n    @Mock\n    private ProcessDefinitionService processDefinitionService;\n    @Mock\n    private ProcessInstanceInterruptor processInstanceInterruptor;\n    @Mock\n    private ArchiveService archiveService;\n    @Mock\n    private StateBehaviors stateBehaviors;\n    @Mock\n    BPMArchiverService bpmArchiverService;\n    @Captor\n    private ArgumentCaptor<WorkDescriptor> workDescriptorArgumentCaptor;\n    private FlowNodeExecutorImpl flowNodeExecutor;\n    private SkippedFlowNodeState skippedFlowNodeState;\n\n    @Before\n    public void before() throws Exception {\n        flowNodeExecutor = new FlowNodeExecutorImpl(flowNodeStateManager, activityInstanceService,\n                containerRegistry, processDefinitionService, null, null, workService, workFactory,\n                processInstanceInterruptor, bpmArchiverService);\n        skippedFlowNodeState = new SkippedFlowNodeState();\n        doReturn(skippedFlowNodeState).when(flowNodeStateManager).getState(SkippedFlowNodeState.ID);\n        doReturn(stateBehaviors).when(flowNodeStateManager).getStateBehaviors();\n    }\n\n    @Test\n    public void should_abort_children_when_setting_activity_to_a_terminal_state() throws Exception {\n        SUserTaskInstance flowNodeInstance = aTask(1L, true);\n        flowNodeInstance.setTokenCount(2);\n\n        flowNodeExecutor.setStateByStateId(1L, SkippedFlowNodeState.ID);\n\n        verify(processInstanceInterruptor).interruptChildrenOfFlowNodeInstance(flowNodeInstance, ABORTING);\n    }\n\n    @Test\n    public void should_interrupt_boundary_when_setting_activity_to_a_terminal_state() throws Exception {\n        SUserTaskInstance sUserTaskInstance = aTask(1L, true);\n\n        flowNodeExecutor.setStateByStateId(1L, SkippedFlowNodeState.ID);\n\n        verify(stateBehaviors).interruptAttachedBoundaryEvent(any(), eq(sUserTaskInstance), eq(ABORTING));\n    }\n\n    @Test\n    public void should_register_notifyFinish_when_setting_activity_to_a_terminal_state_with_no_children()\n            throws Exception {\n        aTask(1L, true);\n\n        flowNodeExecutor.setStateByStateId(1L, SkippedFlowNodeState.ID);\n\n        verify(workService).registerWork(workDescriptorArgumentCaptor.capture());\n        assertThat(workDescriptorArgumentCaptor.getValue().getType())\n                .isEqualTo(\"FINISH_FLOWNODE\");\n    }\n\n    @Test\n    public void should_set_the_state_on_the_activity() throws Exception {\n        SUserTaskInstance aTask = aTask(1L, true);\n\n        flowNodeExecutor.setStateByStateId(1L, SkippedFlowNodeState.ID);\n\n        verify(activityInstanceService).setState(aTask, skippedFlowNodeState);\n    }\n\n    private SUserTaskInstance aTask(long id, boolean stable) throws SFlowNodeReadException, SFlowNodeNotFoundException {\n        SUserTaskInstance sUserTaskInstance = new SUserTaskInstance();\n        sUserTaskInstance.setId(id);\n        sUserTaskInstance.setParentContainerId(PROCESS_INSTANCE_ID);\n        sUserTaskInstance.setLogicalGroup(3, PROCESS_INSTANCE_ID);\n        sUserTaskInstance.setStable(stable);\n        doReturn(sUserTaskInstance).when(activityInstanceService).getFlowNodeInstance(id);\n        return sUserTaskInstance;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/FlowNodeIdFilterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport static java.util.Arrays.asList;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.Mockito.doReturn;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * Created by Vincent Elcrin\n * Date: 17/12/13\n * Time: 17:36\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class FlowNodeIdFilterTest {\n\n    @Mock\n    private SFlowNodeDefinition flownode;\n\n    @Test\n    public void select_should_return_false_if_flow_node_id_is_not_the_expected_one() {\n        doReturn(5L).when(flownode).getId();\n\n        boolean result = new FlowNodeIdFilter(3L).mustSelect(flownode);\n\n        assertFalse(result);\n    }\n\n    @Test\n    public void select_should_return_true_if_flow_node_id_is_the_expected_one() {\n        doReturn(3L).when(flownode).getId();\n\n        boolean result = new FlowNodeIdFilter(3L).mustSelect(flownode);\n\n        assertTrue(result);\n    }\n\n    @Test\n    public void should_return_true_if_flow_node_is_in_the_expected_list() {\n        doReturn(3L).when(flownode).getId();\n\n        boolean result = new FlowNodeIdFilter(asList(4L, 3L)).mustSelect(flownode);\n\n        assertTrue(result);\n    }\n\n    @Test\n    public void should_return_false_if_flow_node_is_not_in_the_expected_list() {\n        doReturn(3L).when(flownode).getId();\n\n        boolean result = new FlowNodeIdFilter(asList(4L, 5L)).mustSelect(flownode);\n\n        assertFalse(result);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/FlowNodeNameFilterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.Mockito.doReturn;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class FlowNodeNameFilterTest {\n\n    @Mock\n    private SFlowNodeDefinition flowNode1;\n\n    @Mock\n    private SFlowNodeDefinition flowNode2;\n\n    private final List<String> names = Arrays.asList(\"step1\", \"step3\");\n\n    @Before\n    public void setUp() {\n        doReturn(\"step1\").when(flowNode1).getName();\n        doReturn(\"step2\").when(flowNode2).getName();\n    }\n\n    @Test\n    public void select_return_true_if_name_is_contained_in_the_list() {\n        assertTrue(new FlowNodeNameFilter(names).mustSelect(flowNode1));\n    }\n\n    @Test\n    public void select_return_false_if_name_is_contained_in_the_list() {\n        assertFalse(new FlowNodeNameFilter(names).mustSelect(flowNode2));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/FlowNodeSelectorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.mock;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SSubProcessDefinition;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class FlowNodeSelectorTest {\n\n    /**\n     *\n     */\n    private static final long SUB_PROCESS_DEFINITION_ID = 10L;\n\n    @Mock\n    private SFlowElementContainerDefinition rootContainer;\n\n    @Mock\n    private SFlowElementContainerDefinition subProcessContainer;\n\n    @Mock\n    private SProcessDefinition definition;\n\n    @Mock\n    private SSubProcessDefinition subProcessDefinition;\n\n    @Before\n    public void setUp() {\n        doReturn(rootContainer).when(definition).getProcessContainer();\n        doReturn(subProcessDefinition).when(rootContainer).getFlowNode(SUB_PROCESS_DEFINITION_ID);\n        doReturn(subProcessContainer).when(subProcessDefinition).getSubProcessContainer();\n\n        Set<SFlowNodeDefinition> flowNodes = new HashSet<>(\n                Arrays.asList(creatFlowNode(\"step1\"), creatFlowNode(\"step2\"),\n                        creatFlowNode(\"step3\")));\n        doReturn(flowNodes).when(rootContainer).getFlowNodes();\n    }\n\n    @Test\n    public void getContainer_return_root_container_if_subprocess_id_is_not_set() {\n        FlowNodeSelector selector = new FlowNodeSelector(definition, null);\n        assertEquals(rootContainer, selector.getContainer());\n    }\n\n    @Test\n    public void getContainer_return_subprocess_container_if_subprocess_id_is_set() {\n        FlowNodeSelector selector = new FlowNodeSelector(definition, null, SUB_PROCESS_DEFINITION_ID);\n        assertEquals(subProcessContainer, selector.getContainer());\n    }\n\n    private SFlowNodeDefinition creatFlowNode(String name) {\n        SFlowNodeDefinition flowNodeDefinition = mock(SFlowNodeDefinition.class);\n        doReturn(name).when(flowNodeDefinition).getName();\n        return flowNodeDefinition;\n    }\n\n    @Test\n    public void getStartNodes_return_all_selected_elements() {\n        FlowNodeSelector flowNodeSelector = new FlowNodeSelector(definition,\n                new FlowNodeNameFilter(Arrays.asList(\"step1\", \"step3\")));\n        assertEquals(\"[step1, step3]\", stringify(flowNodeSelector.getFilteredElements()));\n    }\n\n    private String stringify(List<SFlowNodeDefinition> elements) {\n        List<String> elementNames = new ArrayList<>();\n        for (SFlowNodeDefinition sFlowNodeDefinition : elements) {\n            elementNames.add(sFlowNodeDefinition.getName());\n        }\n        Collections.sort(elementNames);\n        return elementNames.toString();\n    }\n\n    @Test\n    public void get_process_definition_returns_process_definition_given_in_constructor() {\n        FlowNodeSelector selector = new FlowNodeSelector(definition, null);\n        assertEquals(definition, selector.getProcessDefinition());\n    }\n\n    @Test\n    public void get_subProcess_definition_returns_the_id_given_in_constructor() {\n        FlowNodeSelector selector = new FlowNodeSelector(null, null, SUB_PROCESS_DEFINITION_ID);\n        assertEquals(SUB_PROCESS_DEFINITION_ID, selector.getSubProcessDefinitionId());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/FlowNodeStateManagerImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport static java.util.Arrays.asList;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.core.process.instance.model.SStateCategory.*;\nimport static org.bonitasoft.engine.execution.TestFlowNodeState.*;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.bpm.model.impl.BPMInstancesCreator;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.STaskPriority;\nimport org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance;\nimport org.bonitasoft.engine.execution.transition.FlowNodeStateSequences;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class FlowNodeStateManagerImplTest {\n\n    private FlowNodeStateManagerImpl stateManager;\n\n    @Mock\n    BPMInstancesCreator bpmInstancesCreator;\n    List<FlowNodeState> states = new ArrayList<>();\n\n    @Before\n    public void before() {\n        stateManager = new FlowNodeStateManagerImpl(bpmInstancesCreator, null,\n                asList(\n                        new TestFlowNodeStateSequences(SFlowNodeType.AUTOMATIC_TASK,\n                                asList(state(normalState(10, NORMAL)), state(normalState(11, NORMAL)),\n                                        state(terminalState(12, NORMAL))),\n                                asList(state(normalState(120, ABORTING)), state(terminalState(121, ABORTING))),\n                                asList(state(normalState(122, CANCELLING)), state(terminalState(123, CANCELLING)))),\n                        new TestFlowNodeStateSequences(SFlowNodeType.USER_TASK,\n                                asList(state(normalState(20, NORMAL)), state(normalState(21, NORMAL)),\n                                        state(skippedState(22, NORMAL)), state(terminalState(23, NORMAL))),\n                                asList(state(normalState(130, ABORTING)), state(terminalState(131, ABORTING))),\n                                asList(state(normalState(132, CANCELLING)), state(terminalState(133, CANCELLING))))),\n                states);\n    }\n\n    private FlowNodeState state(FlowNodeState state) {\n        states.add(state);\n        return state;\n    }\n\n    @Test\n    public void should_return_first_normal_state_of_AUTOMATIC_TASK() {\n        FlowNodeState firstState = stateManager.getFirstState(SFlowNodeType.AUTOMATIC_TASK);\n\n        assertThat(firstState.getId()).isEqualTo(10);\n    }\n\n    @Test\n    public void should_return_first_normal_state_of_USER_TASK() {\n        FlowNodeState firstState = stateManager.getFirstState(SFlowNodeType.USER_TASK);\n\n        assertThat(firstState.getId()).isEqualTo(20);\n    }\n\n    @Test\n    public void should_return_next_state_of_a_normal_state() throws Exception {\n        SAutomaticTaskInstance automaticTaskInstance = new SAutomaticTaskInstance(\"toto\", 1, 2, 3, 4, 5);\n\n        FlowNodeState nextState = stateManager.getNextState(null, automaticTaskInstance, 11);\n\n        assertThat(nextState.getId()).isEqualTo(12);\n    }\n\n    @Test\n    public void should_skip_state_that_should_not_be_executed() throws Exception {\n        SUserTaskInstance userTaskInstance = new SUserTaskInstance(\"toto\", 1, 2, 3, 4, STaskPriority.ABOVE_NORMAL, 5,\n                6);\n\n        FlowNodeState nextState = stateManager.getNextState(null, userTaskInstance, 21);\n\n        // then: the state \"21\" is skipped because \"shouldExecuteState\" returned false\n        assertThat(nextState.getId()).isEqualTo(23);\n    }\n\n    @Test\n    public void should_return_first_state_of_aborting_branch_when_flownode_was_just_aborted() throws Exception {\n        SAutomaticTaskInstance automaticTaskInstance = new SAutomaticTaskInstance(\"toto\", 1, 2, 3, 4, 5);\n        automaticTaskInstance.setStateCategory(ABORTING);\n\n        FlowNodeState nextState = stateManager.getNextState(null, automaticTaskInstance, 11);\n\n        //then: task should be in the first \"ABORTING\" state\n        assertThat(nextState.getId()).isEqualTo(120);\n    }\n\n    @Test\n    public void should_return_next_state_of_aborting_branch() throws Exception {\n        SAutomaticTaskInstance automaticTaskInstance = new SAutomaticTaskInstance(\"toto\", 1, 2, 3, 4, 5);\n        automaticTaskInstance.setStateCategory(ABORTING);\n\n        FlowNodeState nextState = stateManager.getNextState(null, automaticTaskInstance, 120);\n\n        assertThat(nextState.getId()).isEqualTo(121);\n    }\n\n    @Test(expected = SActivityExecutionException.class)\n    public void should_throw_exception_when_there_is_no_state_after_current() throws Exception {\n        SAutomaticTaskInstance automaticTaskInstance = new SAutomaticTaskInstance(\"toto\", 1, 2, 3, 4, 5);\n\n        stateManager.getNextState(null, automaticTaskInstance, 12);\n    }\n\n    @Test\n    public void should_return_supported_states() {\n        Set<String> supportedState = stateManager.getSupportedState(SFlowNodeType.USER_TASK);\n\n        assertThat(supportedState).containsExactlyInAnyOrder(\"test-state-20\", \"test-state-21\", \"test-state-22\",\n                \"test-state-23\");\n    }\n\n    static class TestFlowNodeStateSequences extends FlowNodeStateSequences {\n\n        private final SFlowNodeType type;\n\n        @Override\n        public SFlowNodeType getFlowNodeType() {\n            return type;\n        }\n\n        TestFlowNodeStateSequences(SFlowNodeType type,\n                List<FlowNodeState> normalTransition,\n                List<FlowNodeState> abortTransition,\n                List<FlowNodeState> cancelTransition) {\n            this.type = type;\n\n            defineNormalSequence(toVarArgs(normalTransition));\n            defineAbortSequence(toVarArgs(abortTransition));\n            defineCancelSequence(toVarArgs(cancelTransition));\n        }\n\n        private FlowNodeState[] toVarArgs(List<FlowNodeState> states) {\n            return states.toArray(new FlowNodeState[0]);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/ProcessExecutorImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.*;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.archive.ArchiveService;\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.bonitasoft.engine.bpm.connector.ConnectorDefinitionWithInputValues;\nimport org.bonitasoft.engine.bpm.document.DocumentValue;\nimport org.bonitasoft.engine.bpm.model.impl.BPMInstancesCreator;\nimport org.bonitasoft.engine.bpm.process.ActivationState;\nimport org.bonitasoft.engine.business.data.BusinessDataRepository;\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.core.connector.ConnectorInstanceService;\nimport org.bonitasoft.engine.core.connector.ConnectorService;\nimport org.bonitasoft.engine.core.document.api.DocumentService;\nimport org.bonitasoft.engine.core.document.model.builder.SDocumentBuilder;\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.OperationService;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionException;\nimport org.bonitasoft.engine.core.process.definition.model.SContractDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SGatewayType;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.core.process.definition.model.SSubProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SBusinessDataDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SDocumentDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SFlowElementContainerDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SGatewayDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SProcessDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SSubProcessDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.impl.STransitionDefinitionImpl;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.GatewayInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.RefBusinessDataService;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceCreationException;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowElementsContainerType;\nimport org.bonitasoft.engine.core.process.instance.model.SGatewayInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.events.model.SEvent;\nimport org.bonitasoft.engine.execution.event.EventsHandler;\nimport org.bonitasoft.engine.execution.handler.SProcessInstanceHandler;\nimport org.bonitasoft.engine.execution.state.FlowNodeStateManager;\nimport org.bonitasoft.engine.expression.ExpressionService;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.expression.model.impl.SExpressionImpl;\nimport org.bonitasoft.engine.lock.LockService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor;\nimport org.bonitasoft.engine.work.WorkService;\nimport org.hamcrest.Description;\nimport org.hamcrest.TypeSafeMatcher;\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.Answers;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ProcessExecutorImplTest {\n\n    @Rule\n    public ExpectedException exception = ExpectedException.none();\n\n    @Mock\n    private ActivityInstanceService activityInstanceService;\n\n    @Mock\n    private ArchiveService archiveService;\n\n    @Mock\n    private BPMInstancesCreator bpmInstancesCreator;\n\n    @Mock(answer = Answers.RETURNS_MOCKS)\n    private ClassLoaderService classLoaderService;\n\n    @Mock\n    private ConnectorInstanceService connectorInstanceService;\n\n    @Mock\n    private ConnectorService connectorService;\n\n    @Mock\n    private ContainerRegistry containerRegistry;\n\n    @Mock\n    private SDocumentBuilder documentBuilder;\n\n    @Mock\n    private EventInstanceService eventInstanceService;\n\n    @Mock\n    private EventService eventService;\n\n    @Mock\n    private EventsHandler eventsHandler;\n\n    @Mock\n    private ExpressionResolverService expressionResolverService;\n\n    @Mock\n    private ExpressionService expressionService;\n\n    @Mock\n    private FlowNodeExecutor flowNodeExecutor;\n\n    @Mock\n    private FlowNodeStateManager flowNodeStateManager;\n\n    @Mock\n    private GatewayInstanceService gatewayInstanceService;\n\n    @Mock\n    private Map<String, SProcessInstanceHandler<SEvent>> handlers;\n\n    @Mock\n    private LockService lockService;\n\n    @Mock\n    private OperationService operationService;\n\n    @Mock\n    private ProcessDefinitionService processDefinitionService;\n\n    @Mock\n    private DocumentService documentService;\n\n    @Mock\n    private ProcessInstanceService processInstanceService;\n\n    @Mock\n    private ReadSessionAccessor sessionAccessor;\n\n    @Mock\n    private WorkService workService;\n\n    @Mock\n    private BusinessDataRepository businessDataRepository;\n\n    @Mock\n    private RefBusinessDataService refBusinessDataService;\n\n    @Mock\n    private SProcessDefinition processDefinition;\n\n    @Mock\n    private SFlowNodeDefinition flowNodeDefinition;\n\n    @Mock\n    private SFlowElementContainerDefinition processContainer;\n\n    @Mock\n    private ProcessStarterVerifier processStarterVerifier;\n\n    @InjectMocks\n    private ProcessExecutorImpl processExecutorImpl;\n\n    @Test\n    public void should_not_start_disabled_process() throws Exception {\n        final long starterId = 1L;\n        final long starterSubstituteId = 9L;\n        final List<SOperation> operations = new ArrayList<>();\n        final Map<String, Object> context = new HashMap<>();\n\n        final ProcessExecutorImpl mockedProcessExecutorImpl = mock(ProcessExecutorImpl.class,\n                withSettings().spiedInstance(processExecutorImpl));\n        final SProcessDefinition sProcessDefinition = mock(SProcessDefinition.class);\n        final FlowNodeSelector selector = new FlowNodeSelector(sProcessDefinition, null);\n        final SProcessDefinitionDeployInfo sProcessDefinitionDeployInfo = mock(SProcessDefinitionDeployInfo.class);\n        long processId = 42L;\n        String processName = \"processName\";\n        String processVersion = \"processVersion\";\n        doReturn(processId).when(sProcessDefinitionDeployInfo).getProcessId();\n        doReturn(processName).when(sProcessDefinitionDeployInfo).getName();\n        doReturn(processVersion).when(sProcessDefinitionDeployInfo).getVersion();\n        doReturn(ActivationState.DISABLED.name()).when(sProcessDefinitionDeployInfo).getActivationState();\n        doReturn(sProcessDefinitionDeployInfo).when(processDefinitionService).getProcessDeploymentInfo(anyLong());\n\n        // Let's call it for real:\n        doCallRealMethod().when(mockedProcessExecutorImpl).start(starterId, starterSubstituteId, operations, context,\n                null, selector, null);\n        doCallRealMethod().when(mockedProcessExecutorImpl).start(starterId, starterSubstituteId, null, operations,\n                context, null, -1, selector, null);\n\n        exception.expect(SProcessInstanceCreationException.class);\n        final Throwable expectedCause = new SProcessDefinitionException(\n                \"The process processName processVersion is not enabled.\", processId, processName, processVersion);\n        exception.expectCause(new TypeSafeMatcher<Throwable>() {\n\n            @Override\n            protected boolean matchesSafely(Throwable item) {\n                return expectedCause.getClass().getName().equals(item.getClass().getName())\n                        && expectedCause.getMessage().equals(item.getMessage());\n            }\n\n            @Override\n            public void describeTo(Description description) {\n                description.appendValue(expectedCause);\n\n            }\n        });\n\n        mockedProcessExecutorImpl.start(starterId, starterSubstituteId, operations, context, null, selector, null);\n    }\n\n    @Test\n    public void startProcessWithOperationsAndContext() throws Exception {\n        final long starterId = 1L;\n        final long starterSubstituteId = 9L;\n        final List<SOperation> operations = new ArrayList<>(1);\n        operations.add(mock(SOperation.class));\n        final Map<String, Object> context = new HashMap<>(1);\n        context.put(\"input\", \"value\");\n\n        final ProcessExecutorImpl mockedProcessExecutorImpl = mock(ProcessExecutorImpl.class,\n                withSettings().spiedInstance(processExecutorImpl));\n        final SProcessDefinition sProcessDefinition = mock(SProcessDefinition.class);\n        final SProcessInstance sProcessInstance = mock(SProcessInstance.class);\n        final FlowNodeSelector selector = new FlowNodeSelector(sProcessDefinition, null);\n        final SProcessDefinitionDeployInfo sProcessDefinitionDeployInfo = mock(SProcessDefinitionDeployInfo.class);\n        doReturn(ActivationState.ENABLED.name()).when(sProcessDefinitionDeployInfo).getActivationState();\n        doReturn(sProcessDefinitionDeployInfo).when(processDefinitionService).getProcessDeploymentInfo(anyLong());\n        when(mockedProcessExecutorImpl.createProcessInstance(sProcessDefinition, starterId, starterSubstituteId, -1))\n                .thenReturn(sProcessInstance);\n        when(mockedProcessExecutorImpl.startElements(eq(sProcessInstance), eq(selector))).thenReturn(sProcessInstance);\n        doNothing().when(processStarterVerifier).verify(sProcessInstance);\n\n        // Let's call it for real:\n        doCallRealMethod().when(mockedProcessExecutorImpl).start(starterId, starterSubstituteId, operations, context,\n                null, selector, null);\n        doCallRealMethod().when(mockedProcessExecutorImpl).start(starterId, starterSubstituteId, null, operations,\n                context, null, -1, selector, null);\n        final SProcessInstance result = mockedProcessExecutorImpl.start(starterId, starterSubstituteId, operations,\n                context, null, selector, null);\n\n        Assert.assertNotNull(result);\n        Assert.assertEquals(sProcessInstance, result);\n    }\n\n    @Test\n    public void startProcessWithOperationsAndContextAndExpressionContextAndConnectors_on_EventSubProcess()\n            throws Exception {\n        final long starterId = 1L;\n        final long starterSubstituteId = 9L;\n        final List<SOperation> operations = new ArrayList<>(1);\n        operations.add(mock(SOperation.class));\n        final Map<String, Object> context = new HashMap<>(1);\n        context.put(\"input\", \"value\");\n        final SExpressionContext expressionContext = mock(SExpressionContext.class);\n        final List<ConnectorDefinitionWithInputValues> connectors = new ArrayList<>();\n        connectors.add(mock(ConnectorDefinitionWithInputValues.class));\n        final long callerId = 1L;\n        final long subProcessDefinitionId = 1L;\n\n        final ProcessExecutorImpl mockedProcessExecutorImpl = mock(ProcessExecutorImpl.class,\n                withSettings().spiedInstance(processExecutorImpl));\n        final SProcessDefinition sProcessDefinition = mock(SProcessDefinition.class);\n        final SSubProcessDefinition subProcessDef = mock(SSubProcessDefinition.class);\n        final SFlowElementContainerDefinition rootContainerDefinition = mock(SFlowElementContainerDefinition.class);\n        doReturn(rootContainerDefinition).when(sProcessDefinition).getProcessContainer();\n        doReturn(subProcessDef).when(rootContainerDefinition).getFlowNode(subProcessDefinitionId);\n        final SProcessInstance sProcessInstance = mock(SProcessInstance.class);\n        final FlowNodeSelector selector = new FlowNodeSelector(sProcessDefinition, null, subProcessDefinitionId);\n        final SProcessDefinitionDeployInfo sProcessDefinitionDeployInfo = mock(SProcessDefinitionDeployInfo.class);\n        when(mockedProcessExecutorImpl.startElements(sProcessInstance, selector)).thenReturn(sProcessInstance);\n        when(mockedProcessExecutorImpl.createProcessInstance(sProcessDefinition, starterId, starterSubstituteId,\n                subProcessDefinitionId)).thenReturn(sProcessInstance);\n        doNothing().when(processStarterVerifier).verify(sProcessInstance);\n\n        final Map<String, Serializable> processInputs = new HashMap<>(0);\n\n        // Let's call it for real:\n        doCallRealMethod().when(mockedProcessExecutorImpl).start(starterId, starterSubstituteId, expressionContext,\n                operations, context,\n                connectors, callerId, selector, processInputs);\n        final SProcessInstance result = mockedProcessExecutorImpl.start(starterId, starterSubstituteId,\n                expressionContext, operations,\n                context, connectors, callerId, selector, processInputs);\n\n        // and check methods are called:\n        verify(mockedProcessExecutorImpl, times(1)).startElements(any(SProcessInstance.class),\n                any(FlowNodeSelector.class));\n        verify(mockedProcessExecutorImpl).createProcessInstance(sProcessDefinition, starterId, starterSubstituteId,\n                subProcessDefinitionId);\n        verify(mockedProcessExecutorImpl, never()).validateContractInputs(processInputs, sProcessDefinition);\n\n        Assert.assertNotNull(result);\n        Assert.assertEquals(sProcessInstance, result);\n    }\n\n    @Test\n    public void startProcessWithOperationsAndContextAndExpressionContextAndConnectors() throws Exception {\n        final long starterId = 1L;\n        final long starterSubstituteId = 9L;\n        final List<SOperation> operations = new ArrayList<>(1);\n        operations.add(mock(SOperation.class));\n        final Map<String, Object> context = new HashMap<>(1);\n        context.put(\"input\", \"value\");\n        final SExpressionContext expressionContext = mock(SExpressionContext.class);\n        final List<ConnectorDefinitionWithInputValues> connectors = new ArrayList<>();\n        connectors.add(mock(ConnectorDefinitionWithInputValues.class));\n        final long callerId = 1L;\n        final long subProcessDefinitionId = 1L;\n\n        final ProcessExecutorImpl mockedProcessExecutorImpl = mock(ProcessExecutorImpl.class,\n                withSettings().spiedInstance(processExecutorImpl));\n        final SProcessDefinition sProcessDefinition = mock(SProcessDefinition.class);\n        final SFlowElementContainerDefinition rootContainerDefinition = mock(SFlowElementContainerDefinition.class);\n        doReturn(rootContainerDefinition).when(sProcessDefinition).getProcessContainer();\n        final SProcessInstance sProcessInstance = mock(SProcessInstance.class);\n        final FlowNodeSelector selector = new FlowNodeSelector(sProcessDefinition, null);\n        final SProcessDefinitionDeployInfo sProcessDefinitionDeployInfo = mock(SProcessDefinitionDeployInfo.class);\n        doReturn(ActivationState.ENABLED.name()).when(sProcessDefinitionDeployInfo).getActivationState();\n        doReturn(sProcessDefinitionDeployInfo).when(processDefinitionService).getProcessDeploymentInfo(anyLong());\n        when(mockedProcessExecutorImpl.startElements(eq(sProcessInstance), eq(selector))).thenReturn(sProcessInstance);\n        when(mockedProcessExecutorImpl.createProcessInstance(sProcessDefinition, starterId, starterSubstituteId, 1L))\n                .thenReturn(sProcessInstance);\n        doNothing().when(processStarterVerifier).verify(sProcessInstance);\n\n        final Map<String, Serializable> processInputs = new HashMap<>(0);\n        doNothing().when(mockedProcessExecutorImpl).validateContractInputs(processInputs, sProcessDefinition);\n\n        // Let's call it for real:\n        doCallRealMethod().when(mockedProcessExecutorImpl).start(starterId, starterSubstituteId, expressionContext,\n                operations, context,\n                connectors, callerId, selector, processInputs);\n        final SProcessInstance result = mockedProcessExecutorImpl.start(starterId, starterSubstituteId,\n                expressionContext, operations,\n                context, connectors, callerId, selector, processInputs);\n\n        // and check methods are called:\n        verify(mockedProcessExecutorImpl, times(1)).startElements(any(SProcessInstance.class),\n                any(FlowNodeSelector.class));\n        verify(mockedProcessExecutorImpl).createProcessInstance(sProcessDefinition, starterId, starterSubstituteId,\n                subProcessDefinitionId);\n        verify(mockedProcessExecutorImpl, times(1)).validateContractInputs(processInputs, sProcessDefinition);\n\n        Assert.assertNotNull(result);\n        Assert.assertEquals(sProcessInstance, result);\n    }\n\n    private SFlowElementContainerDefinitionImpl createProcessWithBusinessData(\n            SBusinessDataDefinitionImpl businessDataDefinition) {\n        SFlowElementContainerDefinitionImpl processContainer = new SFlowElementContainerDefinitionImpl();\n        processContainer.addBusinessDataDefinition(businessDataDefinition);\n        return processContainer;\n    }\n\n    @Test\n    public void should_not_fail_when_initialize_multiple_businessData_even_when_expression_returns_null()\n            throws Exception {\n        SBusinessDataDefinitionImpl businessDataDefinition = new SBusinessDataDefinitionImpl();\n        businessDataDefinition.setMultiple(true);\n        businessDataDefinition.setDefaultValueExpression(new SExpressionImpl());\n        SFlowElementContainerDefinitionImpl processContainer = createProcessWithBusinessData(businessDataDefinition);\n\n        processExecutorImpl.initializeBusinessData(processContainer, new SProcessInstance(), new SExpressionContext());\n\n        verify(refBusinessDataService).addRefBusinessDataInstance(any(SProcessMultiRefBusinessDataInstance.class));\n    }\n\n    @Test\n    public void should_initialize_multiple_business_data_on_process_instance() throws Exception {\n        SBusinessDataDefinitionImpl businessDataDefinition = new SBusinessDataDefinitionImpl();\n        businessDataDefinition.setMultiple(true);\n        businessDataDefinition.setDefaultValueExpression(new SExpressionImpl());\n        SFlowElementContainerDefinitionImpl processContainer = createProcessWithBusinessData(businessDataDefinition);\n        doReturn(Arrays.asList(mock(Entity.class), mock(Entity.class))).when(expressionResolverService)\n                .evaluate(any(SExpression.class), any());\n\n        processExecutorImpl.initializeBusinessData(processContainer, new SProcessInstance(), new SExpressionContext());\n\n        verify(refBusinessDataService).addRefBusinessDataInstance(any(SProcessMultiRefBusinessDataInstance.class));\n    }\n\n    @Test\n    public void should_not_fail_when_initialize_simple_businessData_even_when_expression_returns_null()\n            throws Exception {\n        SBusinessDataDefinitionImpl businessDataDefinition = new SBusinessDataDefinitionImpl();\n        businessDataDefinition.setMultiple(false);\n        businessDataDefinition.setDefaultValueExpression(new SExpressionImpl());\n        SFlowElementContainerDefinitionImpl processContainer = createProcessWithBusinessData(businessDataDefinition);\n\n        processExecutorImpl.initializeBusinessData(processContainer, new SProcessInstance(), new SExpressionContext());\n\n        verify(refBusinessDataService).addRefBusinessDataInstance(any(SSimpleRefBusinessDataInstance.class));\n    }\n\n    @Test\n    public void should_initialize_simple_business_data_on_process_instance() throws Exception {\n        SBusinessDataDefinitionImpl businessDataDefinition = new SBusinessDataDefinitionImpl();\n        businessDataDefinition.setMultiple(false);\n        businessDataDefinition.setDefaultValueExpression(new SExpressionImpl());\n        doReturn(mock(Entity.class)).when(expressionResolverService).evaluate(any(SExpression.class), any());\n        SFlowElementContainerDefinitionImpl processContainer = createProcessWithBusinessData(businessDataDefinition);\n\n        processExecutorImpl.initializeBusinessData(processContainer, new SProcessInstance(), new SExpressionContext());\n\n        verify(refBusinessDataService).addRefBusinessDataInstance(any(SSimpleRefBusinessDataInstance.class));\n    }\n\n    @Test\n    public void should_getInitialDocumentValue_return_the_document_value_from_expression()\n            throws SBonitaReadException {\n        //given\n        final SProcessDefinition sProcessDefinition = mock(SProcessDefinition.class);\n        final SExpression initialValueExpression = new SExpressionImpl();\n        final DocumentValue theUrl = new DocumentValue(\"theUrl\");\n        Map<SExpression, DocumentValue> evaluatedDocumentValues = Collections.singletonMap(initialValueExpression,\n                theUrl);\n        SDocumentDefinitionImpl documentDefinition = new SDocumentDefinitionImpl(\"myDoc\");\n        documentDefinition.setUrl(\"toto\");//check it overrides it\n        documentDefinition.setInitialValue(initialValueExpression);\n        //when\n        final DocumentValue initialDocumentValue = processExecutorImpl.getInitialDocumentValue(sProcessDefinition,\n                evaluatedDocumentValues, documentDefinition);\n        //then\n        assertThat(initialDocumentValue).isEqualTo(theUrl);\n    }\n\n    @Test\n    public void should_getInitialDocumentValue_return_the_document_value_from_url() throws SBonitaReadException {\n        //given\n        final SProcessDefinition sProcessDefinition = mock(SProcessDefinition.class);\n        SDocumentDefinitionImpl documentDefinition = new SDocumentDefinitionImpl(\"myDoc\");\n        documentDefinition.setUrl(\"url for the file\");\n        //when\n        final DocumentValue initialDocumentValue = processExecutorImpl.getInitialDocumentValue(sProcessDefinition,\n                Collections.<SExpression, DocumentValue> emptyMap(), documentDefinition);\n        //then\n        assertThat(initialDocumentValue).isEqualTo(new DocumentValue(\"url for the file\"));\n    }\n\n    @Test\n    public void should_getInitialDocumentValue_return_the_document_value_from_file() throws SBonitaReadException {\n        //given\n        ProcessExecutorImpl processExecutor = spy(processExecutorImpl);\n        final SProcessDefinition sProcessDefinition = mock(SProcessDefinition.class);\n        SDocumentDefinitionImpl documentDefinition = new SDocumentDefinitionImpl(\"myDoc\");\n        documentDefinition.setFile(\"toto.txt\");\n        documentDefinition.setFileName(\"myFile.txt\");\n        final byte[] content = { 1, 2, 3 };\n        doReturn(content).when(processExecutor).getProcessDocumentContent(sProcessDefinition, documentDefinition);\n        //when\n        final DocumentValue initialDocumentValue = processExecutor.getInitialDocumentValue(sProcessDefinition,\n                Collections.<SExpression, DocumentValue> emptyMap(), documentDefinition);\n        //then\n        assertThat(initialDocumentValue).isEqualTo(new DocumentValue(content, null, \"myFile.txt\"));\n    }\n\n    @Test\n    public void validateContractInputsShouldDoNothingIfcontractDefinitionIsNull() throws Exception {\n        final SProcessDefinition mock = mock(SProcessDefinition.class);\n        doReturn(null).when(mock).getContract();\n        processExecutorImpl.validateContractInputs(new HashMap<>(), mock);\n    }\n\n    @Test\n    public void validateContractInputsShouldValidateContractWithDefinition() throws Exception {\n        final SProcessDefinition processDef = mock(SProcessDefinition.class);\n        doReturn(mock(SContractDefinition.class)).when(processDef).getContract();\n        processExecutorImpl.validateContractInputs(new HashMap<>(), processDef);\n\n        // then, we call the validator:\n        verify(processDef).getId();\n    }\n\n    @Test\n    public void should_getActivateGatewayOrCreateIt_create_it_if_not_found() throws Exception {\n        final SProcessDefinition processDef = mock(SProcessDefinition.class);\n        final SFlowNodeDefinition gatewayDefinition = new SGatewayDefinitionImpl(12, \"myGate\", SGatewayType.INCLUSIVE);\n        final SGatewayInstance gatewayInstanceToBeReturned = mock(SGatewayInstance.class);\n        doReturn(gatewayInstanceToBeReturned).when(bpmInstancesCreator).createFlowNodeInstance(anyLong(), anyLong(),\n                anyLong(), eq(SFlowElementsContainerType.PROCESS),\n                eq(gatewayDefinition), anyLong(), anyLong(), eq(false), eq(0), eq(SStateCategory.NORMAL), anyLong());\n\n        final SGatewayInstance gatewayInstance = processExecutorImpl.getActiveGatewayOrCreateIt(processDef,\n                gatewayDefinition, SStateCategory.NORMAL, 45l, 46l);\n\n        assertThat(gatewayInstance).isEqualTo(gatewayInstanceToBeReturned);\n    }\n\n    @Test\n    public void should_getActivateGatewayOrCreateIt_return_the_existing_gateway() throws Exception {\n        final SProcessDefinition processDef = mock(SProcessDefinition.class);\n        final SFlowNodeDefinition gatewayDefinition = new SGatewayDefinitionImpl(12, \"myGate\", SGatewayType.INCLUSIVE);\n        final SGatewayInstance gatewayInstanceToBeReturned = mock(SGatewayInstance.class);\n        doReturn(gatewayInstanceToBeReturned).when(gatewayInstanceService).getActiveGatewayInstanceOfTheProcess(45l,\n                \"myGate\");\n\n        final SGatewayInstance gatewayInstance = processExecutorImpl.getActiveGatewayOrCreateIt(processDef,\n                gatewayDefinition, SStateCategory.NORMAL, 45l, 46l);\n\n        assertThat(gatewayInstance).isEqualTo(gatewayInstanceToBeReturned);\n    }\n\n    private STransitionDefinitionImpl create_Transition_and_set_Id(String name, long source, long target, long id) {\n        STransitionDefinitionImpl transition = new STransitionDefinitionImpl(name, source, target);\n        transition.setId(id);\n        return transition;\n    }\n\n    @Test\n    public void removeDuplicatedInclusiveGatewayTransitions_should_remove_transitions_going_into_the_same_inclusive_gateway() {\n        List<STransitionDefinition> transitionList = new LinkedList<>();\n        SFlowNodeDefinition localFlowNodeDefinitionInclusive = new SGatewayDefinitionImpl(1, \"The Inclusive Gateway\",\n                SGatewayType.INCLUSIVE);\n        SFlowNodeDefinition localFlowNodeDefinitionParallel = new SGatewayDefinitionImpl(2, \"The Parallel Gateway\",\n                SGatewayType.PARALLEL);\n        STransitionDefinitionImpl transition1 = create_Transition_and_set_Id(\"The identical one\", 0, 1, 11L);\n        STransitionDefinitionImpl transition2 = create_Transition_and_set_Id(\"The identical one\", 0, 1, 22L);\n        STransitionDefinitionImpl transition3 = create_Transition_and_set_Id(\"The different transition\", 0, 2, 33L);\n        transitionList.add(transition1);\n        transitionList.add(transition2);\n        transitionList.add(transition3);\n        doReturn(processContainer).when(processDefinition).getProcessContainer();\n        doReturn(localFlowNodeDefinitionInclusive).when(processContainer).getFlowNode(1);\n        doReturn(localFlowNodeDefinitionParallel).when(processContainer).getFlowNode(2);\n\n        processExecutorImpl.removeDuplicatedInclusiveGatewayTransitions(processDefinition, transitionList);\n\n        assertThat(transitionList).hasSize(2);\n        assertThat(transitionList).contains(transition1);\n        assertThat(transitionList).contains(transition3);\n        assertThat(transitionList).doesNotContain(transition2);\n    }\n\n    @Test\n    public void removeDuplicatedInclusiveGatewayTransitions_should_keep_transitions_going_to_different_gateways() {\n\n        SFlowNodeDefinition localFlowNodeDefinitionInclusive = new SGatewayDefinitionImpl(1, \"The Inclusive Gateway\",\n                SGatewayType.INCLUSIVE);\n        SFlowNodeDefinition localFlowNodeDefinitionInclusive2 = new SGatewayDefinitionImpl(3,\n                \"Another Inclusive Gateway\", SGatewayType.INCLUSIVE);\n        SFlowNodeDefinition localFlowNodeDefinitionParallel = new SGatewayDefinitionImpl(2, \"The Parallel Gateway\",\n                SGatewayType.PARALLEL);\n        STransitionDefinitionImpl transition1 = create_Transition_and_set_Id(\"The identical one going into Inclusive\",\n                0, 1, 11L);\n        STransitionDefinitionImpl transition2 = create_Transition_and_set_Id(\"The identical one going into Inclusive\",\n                0, 1, 22L);\n        STransitionDefinitionImpl transition3 = create_Transition_and_set_Id(\"The identical one going into Parallel\", 0,\n                2, 33L);\n        STransitionDefinitionImpl transition4 = create_Transition_and_set_Id(\"The identical one going into Parallel\", 0,\n                2, 44L);\n        STransitionDefinitionImpl transition5 = create_Transition_and_set_Id(\n                \"The identical one going into another Inclusive\", 0, 3, 55L);\n        List<STransitionDefinition> transitionList = new LinkedList<>();\n        transitionList.add(transition1);\n        transitionList.add(transition2);\n        transitionList.add(transition3);\n        transitionList.add(transition4);\n        transitionList.add(transition5);\n        doReturn(processContainer).when(processDefinition).getProcessContainer();\n        doReturn(localFlowNodeDefinitionInclusive).when(processContainer).getFlowNode(1);\n        doReturn(localFlowNodeDefinitionParallel).when(processContainer).getFlowNode(2);\n        doReturn(localFlowNodeDefinitionInclusive2).when(processContainer).getFlowNode(3);\n\n        processExecutorImpl.removeDuplicatedInclusiveGatewayTransitions(processDefinition, transitionList);\n\n        assertThat(transitionList).hasSize(4);\n        assertThat(transitionList).contains(transition1);\n        assertThat(transitionList).contains(transition3);\n        assertThat(transitionList).contains(transition4);\n        assertThat(transitionList).contains(transition5);\n    }\n\n    @Test\n    public void should_initializeStringIndex_should_update_process_if_string_index_defined() throws Exception {\n        //given\n        SProcessInstance processInstance = new SProcessInstance(\"p1\", 15234632L);\n        SProcessDefinitionImpl processDefinition = new SProcessDefinitionImpl(\"p1\", \"1.0\");\n        processDefinition.setStringIndex(1, \"stringIndex1\", new SExpressionImpl());\n        SFlowElementContainerDefinitionImpl processContainer = new SFlowElementContainerDefinitionImpl();\n        processDefinition.setProcessContainer(processContainer);\n        //when\n        processExecutorImpl.initializeStringIndexes(processInstance, processDefinition, processContainer);\n        //then\n        verify(processInstanceService).updateProcess(eq(processInstance), any(EntityUpdateDescriptor.class));\n    }\n\n    @Test\n    public void should_initializeStringIndex_do_not_set_indexes_for_sub_processes() throws Exception {\n        //given\n        SProcessInstance processInstance = new SProcessInstance(\"p1\", 15234632L);\n        SProcessDefinitionImpl processDefinition = new SProcessDefinitionImpl(\"p1\", \"1.0\");\n        processDefinition.setStringIndex(1, \"stringIndex1\", new SExpressionImpl());\n        SFlowElementContainerDefinitionImpl processContainer = new SFlowElementContainerDefinitionImpl();\n        SSubProcessDefinitionImpl subProcess = new SSubProcessDefinitionImpl(489372L, \"subProcess\", true);\n        SFlowElementContainerDefinitionImpl subProcessContainer = new SFlowElementContainerDefinitionImpl();\n        subProcess.setSubProcessContainer(subProcessContainer);\n        processContainer.addSubProcess(subProcess);\n        processDefinition.setProcessContainer(processContainer);\n        //when\n        processExecutorImpl.initializeStringIndexes(processInstance, processDefinition, subProcessContainer);\n        //then\n        verify(processInstanceService, never()).updateProcess(eq(processInstance), any(EntityUpdateDescriptor.class));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/ProcessInstanceInterruptorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.core.process.instance.model.SStateCategory.ABORTING;\nimport static org.bonitasoft.engine.core.process.instance.model.SStateCategory.CANCELLING;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Arrays;\nimport java.util.Collections;\n\nimport org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SGatewayInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.STaskPriority;\nimport org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Baptiste Mesta\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class ProcessInstanceInterruptorTest {\n\n    public static final long PROCESS_DEFINITION_ID = 1231L;\n    @Mock\n    private ProcessInstanceService processInstanceService;\n    @Mock\n    private FlowNodeInstanceService flowNodeInstanceService;\n    @Mock\n    private ContainerRegistry containerRegistry;\n    private ProcessInstanceInterruptor processInstanceInterruptor;\n\n    private long PROCESS_INSTANCE_ID = 5348927512390L;\n    private SUserTaskInstance flownode1_stable;\n    private SUserTaskInstance flownode2_unstable;\n    private SUserTaskInstance flownode3_stable;\n\n    @Before\n    public void before() throws Exception {\n        processInstanceInterruptor = new ProcessInstanceInterruptor(processInstanceService, flowNodeInstanceService,\n                containerRegistry);\n        flownode1_stable = new SUserTaskInstance(\"user1\", 532654L, 54336L, 5643456L, 897523454L,\n                STaskPriority.ABOVE_NORMAL, PROCESS_DEFINITION_ID, 67547L);\n        flownode1_stable.setId(9846769L);\n        flownode1_stable.setLogicalGroup(3, PROCESS_INSTANCE_ID);\n        flownode1_stable.setStable(true);\n        flownode2_unstable = new SUserTaskInstance(\"user2\", 532654L, 54336L, 5643456L, 897523454L,\n                STaskPriority.ABOVE_NORMAL, PROCESS_DEFINITION_ID, 67547L);\n        flownode2_unstable.setId(432950L);\n        flownode2_unstable.setLogicalGroup(3, PROCESS_INSTANCE_ID);\n        flownode2_unstable.setStable(false);\n        flownode3_stable = new SUserTaskInstance(\"user3\", 532654L, 54336L, 5643456L, 897523454L,\n                STaskPriority.ABOVE_NORMAL, PROCESS_DEFINITION_ID, 67547L);\n        flownode3_stable.setId(543522L);\n        flownode3_stable.setLogicalGroup(3, PROCESS_INSTANCE_ID);\n        flownode3_stable.setStable(true);\n        doReturn(Arrays.asList(flownode1_stable, flownode2_unstable, flownode3_stable)).when(flowNodeInstanceService)\n                .getDirectChildrenOfProcessInstance(PROCESS_INSTANCE_ID, 0, Integer.MAX_VALUE);\n    }\n\n    @Test\n    public void should_set_state_category_to_ABORTING_on_all_children() throws Exception {\n        //when\n        processInstanceInterruptor.interruptProcessInstance(PROCESS_INSTANCE_ID, ABORTING);\n        //then\n        verify(flowNodeInstanceService).setStateCategory(flownode1_stable, ABORTING);\n        verify(flowNodeInstanceService).setStateCategory(flownode2_unstable, ABORTING);\n        verify(flowNodeInstanceService).setStateCategory(flownode3_stable, ABORTING);\n    }\n\n    @Test\n    public void should_set_state_category_to_CANCELLING_on_all_children() throws Exception {\n        //when\n        processInstanceInterruptor.interruptProcessInstance(PROCESS_INSTANCE_ID, CANCELLING);\n        //then\n        verify(flowNodeInstanceService).setStateCategory(flownode1_stable, CANCELLING);\n        verify(flowNodeInstanceService).setStateCategory(flownode2_unstable, CANCELLING);\n        verify(flowNodeInstanceService).setStateCategory(flownode3_stable, CANCELLING);\n    }\n\n    @Test\n    public void should_not_interrupt_excluded_flownode() throws Exception {\n        //given\n        doReturn(Arrays.asList(flownode1_stable, flownode2_unstable)).when(flowNodeInstanceService)\n                .getDirectChildrenOfProcessInstance(PROCESS_INSTANCE_ID, 0, Integer.MAX_VALUE);\n        //when\n        processInstanceInterruptor.interruptProcessInstance(PROCESS_INSTANCE_ID, ABORTING, flownode3_stable.getId());\n        //then\n        verify(containerRegistry, never()).executeFlowNode(flownode3_stable);\n    }\n\n    @Test\n    public void should_return_true_were_children_were_interrupted() throws Exception {\n        //given\n        doReturn(Arrays.asList(flownode1_stable, flownode2_unstable)).when(flowNodeInstanceService)\n                .getDirectChildrenOfProcessInstance(PROCESS_INSTANCE_ID, 0, Integer.MAX_VALUE);\n        //when\n        boolean actual = processInstanceInterruptor.interruptProcessInstance(PROCESS_INSTANCE_ID, ABORTING);\n        //then\n        verify(containerRegistry).executeFlowNode(flownode1_stable);\n        verify(containerRegistry).executeFlowNode(flownode2_unstable);\n        assertThat(actual).isTrue();\n    }\n\n    @Test\n    public void should_return_false_were_no_children() throws Exception {\n        //given\n        doReturn(Arrays.asList()).when(flowNodeInstanceService)\n                .getDirectChildrenOfProcessInstance(PROCESS_INSTANCE_ID, 0, Integer.MAX_VALUE);\n        //when\n        boolean actual = processInstanceInterruptor.interruptProcessInstance(PROCESS_INSTANCE_ID, ABORTING);\n        //then\n        verify(containerRegistry, never()).executeFlowNode(any());\n        assertThat(actual).isFalse();\n    }\n\n    @Test\n    public void should_call_execute_on_all_elements_only() throws Exception {\n        //when\n        processInstanceInterruptor.interruptProcessInstance(PROCESS_INSTANCE_ID, ABORTING);\n        //then\n        verify(containerRegistry).executeFlowNode(flownode1_stable);\n        verify(containerRegistry).executeFlowNode(flownode2_unstable);\n        verify(containerRegistry).executeFlowNode(flownode3_stable);\n    }\n\n    @Test\n    public void should_call_execute_on_terminal_state() throws Exception {\n        //given\n        flownode1_stable.setTerminal(true);\n        flownode3_stable.setTerminal(false);\n        //when\n        processInstanceInterruptor.interruptProcessInstance(PROCESS_INSTANCE_ID, ABORTING);\n        //then\n        verify(containerRegistry).notifyChildFinished(flownode1_stable);\n        verify(containerRegistry).executeFlowNode(flownode3_stable);\n    }\n\n    @Test\n    public void should_call_execute_on_gateway() throws Exception {\n        //given\n        // whatever the stable or terminal flag are, gateways are always executed\n        SGatewayInstance sGatewayInstance = new SGatewayInstance();\n        sGatewayInstance.setId(53121234L);\n        sGatewayInstance.setLogicalGroup(0, PROCESS_DEFINITION_ID);\n        sGatewayInstance.setLogicalGroup(3, PROCESS_INSTANCE_ID);\n        sGatewayInstance.setStable(false);\n        sGatewayInstance.setTerminal(false);\n        doReturn(Collections.singletonList(sGatewayInstance)).when(flowNodeInstanceService)\n                .getDirectChildrenOfProcessInstance(PROCESS_INSTANCE_ID, 0, Integer.MAX_VALUE);\n        //when\n        processInstanceInterruptor.interruptProcessInstance(PROCESS_INSTANCE_ID, ABORTING);\n        //then\n        verify(containerRegistry).executeFlowNode(sGatewayInstance);\n    }\n\n    @Test\n    public void should_set_process_instance_to_ABORTING() throws Exception {\n        //given\n        SProcessInstance processInstance = new SProcessInstance(\"proc\", PROCESS_INSTANCE_ID);\n        doReturn(processInstance).when(processInstanceService).getProcessInstance(PROCESS_INSTANCE_ID);\n        //when\n        processInstanceInterruptor.interruptProcessInstance(PROCESS_INSTANCE_ID, ABORTING);\n        //then\n        verify(processInstanceService).setStateCategory(processInstance, ABORTING);\n    }\n\n    @Test\n    public void should_set_process_instance_to_CANCELLING() throws Exception {\n        //given\n        SProcessInstance processInstance = new SProcessInstance(\"proc\", PROCESS_INSTANCE_ID);\n        doReturn(processInstance).when(processInstanceService).getProcessInstance(PROCESS_INSTANCE_ID);\n        //when\n        processInstanceInterruptor.interruptProcessInstance(PROCESS_INSTANCE_ID, CANCELLING);\n        //then\n        verify(processInstanceService).setStateCategory(processInstance, CANCELLING);\n    }\n\n    @Test\n    public void should_abort_children_of_flow_node_instance() throws Exception {\n        SUserTaskInstance sUserTaskInstance = new SUserTaskInstance();\n        sUserTaskInstance.setId(42L);\n        sUserTaskInstance.setTokenCount(2);\n        doReturn(Arrays.asList(flownode1_stable, flownode2_unstable)).when(flowNodeInstanceService)\n                .getDirectChildrenOfActivityInstance(sUserTaskInstance.getId(), 0, Integer.MAX_VALUE);\n\n        processInstanceInterruptor.interruptChildrenOfFlowNodeInstance(sUserTaskInstance, ABORTING);\n\n        verify(flowNodeInstanceService).setStateCategory(flownode1_stable, ABORTING);\n        verify(flowNodeInstanceService).setStateCategory(flownode2_unstable, ABORTING);\n        verify(containerRegistry).executeFlowNode(flownode1_stable);\n        verify(containerRegistry).executeFlowNode(flownode2_unstable);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/ProcessStarterVerifierImplTest.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport static com.github.stefanbirkner.systemlambda.SystemLambda.tapSystemOut;\nimport static java.lang.System.currentTimeMillis;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.bonitasoft.engine.execution.ProcessStarterVerifierImpl.LIMIT;\nimport static org.bonitasoft.engine.execution.ProcessStarterVerifierImpl.PERIOD_IN_MILLIS;\nimport static org.junit.jupiter.api.Assertions.assertDoesNotThrow;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.Mockito.*;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceCreationException;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.platform.PlatformRetriever;\nimport org.bonitasoft.engine.platform.model.SPlatform;\nimport org.bonitasoft.engine.service.platform.PlatformInformationService;\nimport org.bonitasoft.engine.transaction.TransactionService;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\n@ExtendWith(MockitoExtension.class)\nclass ProcessStarterVerifierImplTest {\n\n    @Mock\n    private PlatformRetriever platformRetriever;\n    @Mock\n    private PlatformInformationService platformInformationService;\n    @Mock\n    private TransactionService transactionService;\n    @Mock\n    private ProcessInstanceService processInstanceService;\n\n    private ProcessStarterVerifierImpl processStarterVerifier;\n\n    @BeforeEach\n    public void initMocks() throws Exception {\n        doReturn(new ArrayList<>()).when(transactionService).executeInTransaction(any());\n        processStarterVerifier = spy(new ProcessStarterVerifierImpl(platformRetriever, platformInformationService,\n                transactionService, processInstanceService));\n    }\n\n    @Test\n    void should_be_able_to_decrypt_encrypted_value() throws Exception {\n        //given\n        final long currentTime = currentTimeMillis();\n        final var originalValue = List.of(currentTime, currentTime + 1000L, currentTime + 2000L);\n\n        //when\n        final List<Long> computedValue = processStarterVerifier\n                .decryptDataFromDatabase(processStarterVerifier.encryptDataBeforeSendingToDatabase(originalValue));\n\n        //then\n        assertThat(computedValue).isEqualTo(originalValue);\n    }\n\n    @Test\n    void verify_should_not_remove_still_valid_values_from_counters() throws Exception {\n        //given\n        final long validTimestamp = currentTimeMillis() - PERIOD_IN_MILLIS + 86400000L; // plus 1 day\n        processStarterVerifier.addCounter(validTimestamp);\n        doNothing().when(processStarterVerifier).storeNewValueInDatabase(anyString());\n\n        //when\n        processStarterVerifier.verify(new SProcessInstance());\n\n        //then\n        assertThat(processStarterVerifier.getCounters()).size().isEqualTo(2);\n        assertThat(processStarterVerifier.getCounters()).contains(validTimestamp);\n    }\n\n    @Test\n    void verify_should_remove_old_values_from_counters() throws Exception {\n        //given\n        final long obsoleteValue = currentTimeMillis() - PERIOD_IN_MILLIS - 86400000L; // minus 1 day\n        processStarterVerifier.addCounter(obsoleteValue);\n        doNothing().when(processStarterVerifier).storeNewValueInDatabase(anyString());\n\n        //when\n        processStarterVerifier.verify(new SProcessInstance());\n\n        //then\n        assertThat(processStarterVerifier.getCounters()).size().isEqualTo(1);\n        assertThat(processStarterVerifier.getCounters()).doesNotContain(obsoleteValue);\n    }\n\n    @Test\n    void verify_should_throw_exception_if_limit_is_reached() throws Exception {\n        //given\n        for (int i = 0; i < LIMIT; i++) {\n            processStarterVerifier.addCounter(currentTimeMillis());\n        }\n        final SProcessInstance processInstance = mock(SProcessInstance.class);\n        doReturn(currentTimeMillis() + 1000L).when(processInstance).getStartDate();\n\n        //when - then\n        assertThatExceptionOfType(SProcessInstanceCreationException.class)\n                .isThrownBy(() -> processStarterVerifier.verify(processInstance))\n                .withMessageContaining(\"Process start limit\");\n        assertThat(processStarterVerifier.getCounters()).size().isEqualTo(LIMIT);\n        verify(processStarterVerifier, never()).storeNewValueInDatabase(anyString());\n    }\n\n    @Test\n    void should_log_when_80_percent_is_reached() throws Exception {\n        var counters = mock(List.class);\n        doReturn(LIMIT * 80 / 100).when(counters).size();\n        doReturn(counters).when(processStarterVerifier).getCounters();\n\n        // when\n        final String log = tapSystemOut(() -> processStarterVerifier.logCaseLimitProgressIfThresholdReached());\n\n        // then\n        assertThat(log).containsPattern(\"WARN.*80%\");\n    }\n\n    @Test\n    void should_log_when_90_percent_is_reached() throws Exception {\n        var counters = mock(List.class);\n        doReturn(LIMIT * 90 / 100).when(counters).size();\n        doReturn(counters).when(processStarterVerifier).getCounters();\n\n        // when\n        final String log = tapSystemOut(() -> processStarterVerifier.logCaseLimitProgressIfThresholdReached());\n\n        // then\n        assertThat(log)\n                .containsPattern(\"WARN.*90%\")\n                .doesNotContain(\"80%\");\n    }\n\n    @Test\n    void should_not_log_when_80_percent_has_already_been_reached() throws Exception {\n        var counters = mock(List.class);\n        doReturn(121).when(counters).size();\n        doReturn(counters).when(processStarterVerifier).getCounters();\n\n        // when\n        final String log = tapSystemOut(() -> processStarterVerifier.logCaseLimitProgressIfThresholdReached());\n\n        // then\n        assertThat(log).isBlank();\n    }\n\n    @Test\n    void readCounters_should_fail_if_counters_are_not_set() throws Exception {\n        // given\n        final SPlatform platform = new SPlatform();\n        platform.setInformation(\"\");\n        doReturn(platform).when(platformRetriever).getPlatform();\n\n        // when - then\n        assertThatExceptionOfType(IllegalStateException.class)\n                .isThrownBy(() -> processStarterVerifier.readCounters());\n    }\n\n    @Test\n    void readCounters_should_fail_if_counters_cannot_be_decrypted_from_database() throws Exception {\n        // given\n        final SPlatform platform = new SPlatform();\n        final String encryptedValue = \"encrypted value\";\n        platform.setInformation(encryptedValue);\n        doReturn(platform).when(platformRetriever).getPlatform();\n        doThrow(new IOException(\"Cannot decipher information\")).when(processStarterVerifier)\n                .decryptDataFromDatabase(encryptedValue);\n\n        // when - then\n        assertThatExceptionOfType(IllegalStateException.class)\n                .isThrownBy(() -> processStarterVerifier.readCounters());\n    }\n\n    @Test\n    void readCounters_should_succeed_if_counters_can_be_decrypted_from_database() throws Exception {\n        // given\n        final SPlatform platform = new SPlatform();\n        final String encryptedValue = \"encrypted value\";\n        platform.setInformation(encryptedValue);\n        doReturn(platform).when(platformRetriever).getPlatform();\n        final List<Long> countersFromDatabase = List.of(currentTimeMillis());\n        doReturn(countersFromDatabase).when(processStarterVerifier).decryptDataFromDatabase(encryptedValue);\n\n        // when\n        final List<Long> counters = processStarterVerifier.readCounters();\n\n        // then\n        assertThat(counters).isEqualTo(countersFromDatabase);\n    }\n\n    @Test\n    void verify_should_pass_if_all_counters_found_in_archives_are_found_in_counters() throws Exception {\n        // given\n        final long shouldBeThereTimestamp = currentTimeMillis();\n        final long oldestValidDate = 1212000002999L;\n        doReturn(List.of(shouldBeThereTimestamp)).when(processStarterVerifier)\n                .fetchLastArchivedProcessInstanceStartDates(oldestValidDate);\n\n        // when - then\n        assertDoesNotThrow(() -> processStarterVerifier\n                .verifyCountersCoherence(List.of(shouldBeThereTimestamp, 1111252531354L), oldestValidDate));\n\n    }\n\n    @Test\n    void verify_should_fail_if_counters_do_not_contains_data_found_in_archives() throws Exception {\n        // given\n        final Long oldestValidDate = currentTimeMillis();\n        doReturn(List.of(currentTimeMillis())).when(processStarterVerifier)\n                .fetchLastArchivedProcessInstanceStartDates(oldestValidDate);\n\n        // when - then\n        assertThrows(IllegalStateException.class,\n                () -> processStarterVerifier.verifyCountersCoherence(List.of(), oldestValidDate));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/StartFlowNodeFilterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.Mockito.doReturn;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Vincent Elcrin\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class StartFlowNodeFilterTest {\n\n    @Mock\n    SFlowNodeDefinition flowNode;\n\n    final StartFlowNodeFilter filter = new StartFlowNodeFilter();\n\n    @Test\n    public void select_should_return_false_if_flow_node_is_not_startable() {\n        doReturn(false).when(flowNode).isStartable();\n\n        assertFalse(filter.mustSelect(flowNode));\n    }\n\n    @Test\n    public void select_should_return_true_if_flow_node_is_startable() {\n        doReturn(true).when(flowNode).isStartable();\n\n        assertTrue(filter.mustSelect(flowNode));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/StateBehaviorsTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.bpm.connector.ConnectorState.TO_BE_EXECUTED;\nimport static org.bonitasoft.engine.bpm.connector.ConnectorState.TO_RE_EXECUTE;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.*;\nimport static org.mockito.Mockito.anyMap;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.core.connector.ConnectorInstanceService;\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.filter.FilterResult;\nimport org.bonitasoft.engine.core.filter.UserFilterService;\nimport org.bonitasoft.engine.core.process.definition.model.SConnectorDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SUserFilterDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SUserTaskDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SConnectorDefinitionImpl;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException;\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * Phase order is: BEFORE_ON_ENTER > DURING_ON_ENTER > BEFORE_ON_FINISH > DURING_ON_FINISH > AFTER_ON_FINISH.\n *\n * @author Emmanuel Duchastenier\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class StateBehaviorsTest {\n\n    final long flownodeInstanceId = 3541L;\n    final long processDefinitionId = 4567L;\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n    @Mock\n    private SFlowNodeInstance flowNodeInstance;\n    @Mock\n    private SProcessDefinition processDefinition;\n    @Mock\n    private SFlowElementContainerDefinition containerDefinition;\n    @Mock\n    private SUserTaskDefinition flowNodeDefinition;\n    @Mock\n    private ConnectorInstanceService connectorInstanceService;\n    @Mock\n    private ActivityInstanceService activityInstanceService;\n    @Mock\n    private UserFilterService userFilterService;\n    @Mock\n    private SUserFilterDefinition userFilterDefinition;\n    @Mock\n    private ClassLoaderService classLoaderService;\n    @Mock\n    private ExpressionResolverService expressionResolverService;\n    @InjectMocks\n    private StateBehaviors stateBehaviors;\n\n    @Before\n    public void setConstants() {\n        when(flowNodeInstance.getId()).thenReturn(flownodeInstanceId);\n        when(flowNodeDefinition.getName()).thenReturn(\"Step\");\n    }\n\n    @Test\n    public void should_mapUsingUserFilters_map_user_id_only_once() throws Exception {\n        //given\n        final FilterResult result = new FilterResult() {\n\n            @Override\n            public List<Long> getResult() {\n                return Arrays.asList(1L, 2L, 3L, 2L);\n            }\n\n            @Override\n            public boolean shouldAutoAssignTaskIfSingleResult() {\n                return true;\n            }\n        };\n        doReturn(result).when(userFilterService).executeFilter(eq(processDefinitionId), eq(userFilterDefinition),\n                anyMap(),\n                nullable(ClassLoader.class), nullable(SExpressionContext.class), eq(\"actor\"));\n        //when\n        stateBehaviors.mapUsingUserFilters(flowNodeInstance, flowNodeDefinition, \"actor\", processDefinitionId,\n                userFilterDefinition);\n        //then\n        ArgumentCaptor<SPendingActivityMapping> argumentCaptor = ArgumentCaptor.forClass(SPendingActivityMapping.class);\n        verify(activityInstanceService, times(3)).addPendingActivityMappings(argumentCaptor.capture());\n        verify(activityInstanceService, never()).assignHumanTask(anyLong(), anyLong());\n        List<SPendingActivityMapping> allValues = argumentCaptor.getAllValues();\n        assertThat(allValues).hasSize(3);\n        assertThat(allValues).extracting(\"userId\").containsOnly(1L, 2L, 3L);\n    }\n\n    @Test\n    public void should_mapUsingUserFilters_assign_if_only_one_and_auto_assign() throws Exception {\n        //given\n        final FilterResult result = new FilterResult() {\n\n            @Override\n            public List<Long> getResult() {\n                return Arrays.asList(1L);\n            }\n\n            @Override\n            public boolean shouldAutoAssignTaskIfSingleResult() {\n                return true;\n            }\n        };\n        doReturn(result).when(userFilterService).executeFilter(eq(processDefinitionId), eq(userFilterDefinition),\n                anyMap(),\n                nullable(ClassLoader.class), nullable(SExpressionContext.class), eq(\"actor\"));\n        //when\n        stateBehaviors.mapUsingUserFilters(flowNodeInstance, flowNodeDefinition, \"actor\", processDefinitionId,\n                userFilterDefinition);\n        //then\n        ArgumentCaptor<SPendingActivityMapping> argumentCaptor = ArgumentCaptor.forClass(SPendingActivityMapping.class);\n        verify(activityInstanceService, times(1)).addPendingActivityMappings(argumentCaptor.capture());\n        verify(activityInstanceService, times(1)).assignHumanTask(anyLong(), eq(1l));\n        List<SPendingActivityMapping> allValues = argumentCaptor.getAllValues();\n        assertThat(allValues).extracting(\"userId\").containsOnly(1L);\n    }\n\n    @Test\n    public void should_mapUsingUserFilters_do_not_assign_if_only_one_and_auto_assign_is_false() throws Exception {\n        //given\n        final FilterResult result = getFilterResult(Arrays.asList(1L));\n        doReturn(result).when(userFilterService).executeFilter(eq(processDefinitionId), eq(userFilterDefinition),\n                anyMap(),\n                nullable(ClassLoader.class), nullable(SExpressionContext.class), eq(\"actor\"));\n        //when\n        stateBehaviors.mapUsingUserFilters(flowNodeInstance, flowNodeDefinition, \"actor\", processDefinitionId,\n                userFilterDefinition);\n        //then\n        ArgumentCaptor<SPendingActivityMapping> argumentCaptor = ArgumentCaptor.forClass(SPendingActivityMapping.class);\n        verify(activityInstanceService, times(1)).addPendingActivityMappings(argumentCaptor.capture());\n        verify(activityInstanceService, never()).assignHumanTask(anyLong(), anyLong());\n        List<SPendingActivityMapping> allValues = argumentCaptor.getAllValues();\n        assertThat(allValues).extracting(\"userId\").containsOnly(1L);\n    }\n\n    @Test\n    public void should_mapUsingUserFilters_throw_exception_when_empty_return() throws Exception {\n        //given\n        final FilterResult result = getFilterResult(Collections.<Long> emptyList());\n        doReturn(result).when(userFilterService).executeFilter(eq(processDefinitionId), eq(userFilterDefinition),\n                anyMap(),\n                nullable(ClassLoader.class), nullable(SExpressionContext.class), eq(\"actor\"));\n        expectedException.expect(SActivityStateExecutionException.class);\n        expectedException.expectMessage(\"No user id returned by the user filter userFilterDefinition on activity Step\");\n        //when\n        stateBehaviors.mapUsingUserFilters(flowNodeInstance, flowNodeDefinition, \"actor\", processDefinitionId,\n                userFilterDefinition);\n    }\n\n    @Test\n    public void should_mapUsingUserFilters_throw_exception_when_null_return() throws Exception {\n        //given\n        final FilterResult result = getFilterResult(null);\n        doReturn(result).when(userFilterService).executeFilter(eq(processDefinitionId), eq(userFilterDefinition),\n                anyMap(),\n                nullable(ClassLoader.class), nullable(SExpressionContext.class), eq(\"actor\"));\n        expectedException.expect(SActivityStateExecutionException.class);\n        expectedException.expectMessage(\"No user id returned by the user filter userFilterDefinition on activity Step\");\n        //when\n        stateBehaviors.mapUsingUserFilters(flowNodeInstance, flowNodeDefinition, \"actor\", processDefinitionId,\n                userFilterDefinition);\n    }\n\n    @Test\n    public void should_mapUsingUserFilters_throw_exception_when_return_contains_minus_1() throws Exception {\n        //given\n        final FilterResult result = getFilterResult(Arrays.asList(-1L));\n        doReturn(result).when(userFilterService).executeFilter(eq(processDefinitionId), eq(userFilterDefinition),\n                anyMap(),\n                nullable(ClassLoader.class), nullable(SExpressionContext.class), eq(\"actor\"));\n        expectedException.expect(SActivityStateExecutionException.class);\n        expectedException.expectMessage(\"No user id returned by the user filter userFilterDefinition on activity Step\");\n        //when\n        stateBehaviors.mapUsingUserFilters(flowNodeInstance, flowNodeDefinition, \"actor\", processDefinitionId,\n                userFilterDefinition);\n    }\n\n    @Test\n    public void should_mapUsingUserFilters_throw_exception_when_return_contains_0() throws Exception {\n        //given\n        final FilterResult result = getFilterResult(Arrays.asList(0L));\n        doReturn(result).when(userFilterService).executeFilter(eq(processDefinitionId), eq(userFilterDefinition),\n                anyMap(),\n                nullable(ClassLoader.class), nullable(SExpressionContext.class), eq(\"actor\"));\n        expectedException.expect(SActivityStateExecutionException.class);\n        expectedException.expectMessage(\"No user id returned by the user filter userFilterDefinition on activity Step\");\n        //when\n        stateBehaviors.mapUsingUserFilters(flowNodeInstance, flowNodeDefinition, \"actor\", processDefinitionId,\n                userFilterDefinition);\n    }\n\n    FilterResult getFilterResult(final List<Long> theResult) {\n        return new FilterResult() {\n\n            @Override\n            public List<Long> getResult() {\n                return theResult;\n            }\n\n            @Override\n            public boolean shouldAutoAssignTaskIfSingleResult() {\n                return false;\n            }\n        };\n    }\n\n    @Test\n    public void should_noConnectorHasStartedInCurrentList_return_true_when_first_connector_must_be_executed()\n            throws Exception {\n        //given\n        List<SConnectorDefinition> sConnectorDefinitions = Arrays.<SConnectorDefinition> asList(\n                new SConnectorDefinitionImpl(\"connector1\", null, null, null),\n                new SConnectorDefinitionImpl(\"connector2\", null, null, null));\n        SConnectorInstance sConnectorInstance = new SConnectorInstance(\"connector1\", 12, null, null, null, null);\n        sConnectorInstance.setState(TO_BE_EXECUTED.name());\n        //when\n        boolean noConnectorStarted = stateBehaviors.noConnectorHasStartedInCurrentList(sConnectorDefinitions,\n                sConnectorInstance);\n        //then\n        assertThat(noConnectorStarted).isTrue();\n    }\n\n    @Test\n    public void should_noConnectorHasStartedInCurrentList_return_false_when_first_connector_must_be_RE_executed()\n            throws Exception {\n        //given\n        List<SConnectorDefinition> sConnectorDefinitions = Arrays.<SConnectorDefinition> asList(\n                new SConnectorDefinitionImpl(\"connector1\", null, null, null),\n                new SConnectorDefinitionImpl(\"connector2\", null, null, null));\n        SConnectorInstance sConnectorInstance = new SConnectorInstance(\"connector1\", 12, null, null, null, null);\n        sConnectorInstance.setState(TO_RE_EXECUTE.name());\n        //when\n        boolean noConnectorStarted = stateBehaviors.noConnectorHasStartedInCurrentList(sConnectorDefinitions,\n                sConnectorInstance);\n        //then\n        assertThat(noConnectorStarted).isFalse();\n    }\n\n    @Test\n    public void should_noConnectorHasStartedInCurrentList_return_false_when_connector_must_be_executed_but_is_not_the_first()\n            throws Exception {\n        //given\n        List<SConnectorDefinition> sConnectorDefinitions = Arrays.<SConnectorDefinition> asList(\n                new SConnectorDefinitionImpl(\"connector1\", null, null, null),\n                new SConnectorDefinitionImpl(\"connector2\", null, null, null));\n        SConnectorInstance sConnectorInstance = new SConnectorInstance(\"connector2\", 12, null, null, null, null);\n        sConnectorInstance.setState(TO_BE_EXECUTED.name());\n        //when\n        boolean noConnectorStarted = stateBehaviors.noConnectorHasStartedInCurrentList(sConnectorDefinitions,\n                sConnectorInstance);\n        //then\n        assertThat(noConnectorStarted).isFalse();\n    }\n\n    @Test\n    public void should_noConnectorHasStartedInCurrentList_return_false_when_no_connector_must_be_executed_but_some_were_executed()\n            throws Exception {\n        //given\n        List<SConnectorDefinition> sConnectorDefinitions = Arrays.<SConnectorDefinition> asList(\n                new SConnectorDefinitionImpl(\"connector1\", null, null, null),\n                new SConnectorDefinitionImpl(\"connector2\", null, null, null));\n        //when\n        boolean noConnectorStarted = stateBehaviors.noConnectorHasStartedInCurrentList(sConnectorDefinitions, null);\n        //then\n        assertThat(noConnectorStarted).isFalse();\n    }\n\n    @Test\n    public void should_noConnectorHasStartedInCurrentList_return_true_when_no_connector_must_be_executed()\n            throws Exception {\n        //given\n        List<SConnectorDefinition> sConnectorDefinitions = Collections.emptyList();\n        //when\n        boolean noConnectorStarted = stateBehaviors.noConnectorHasStartedInCurrentList(sConnectorDefinitions, null);\n        //then\n        assertThat(noConnectorStarted).isTrue();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/TestFlowNodeState.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.api.states.StateCode;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\n\npublic class TestFlowNodeState implements FlowNodeState {\n\n    private final SStateCategory stateCategory;\n    private final boolean stable;\n    private final boolean terminal;\n    private final int id;\n    private final boolean shouldBeExecuted;\n\n    private TestFlowNodeState(int id, SStateCategory stateCategory, boolean stable, boolean terminal,\n            boolean shouldBeExecuted) {\n        this.id = id;\n        this.stateCategory = stateCategory;\n        this.stable = stable;\n        this.terminal = terminal;\n        this.shouldBeExecuted = shouldBeExecuted;\n    }\n\n    public static TestFlowNodeState terminalState(int id, SStateCategory stateCategory) {\n        return new TestFlowNodeState(id, stateCategory, false, true, true);\n    }\n\n    public static TestFlowNodeState stableState(int id, SStateCategory stateCategory) {\n        return new TestFlowNodeState(id, stateCategory, true, false, true);\n    }\n\n    public static TestFlowNodeState normalState(int id, SStateCategory stateCategory) {\n        return new TestFlowNodeState(id, stateCategory, false, false, true);\n    }\n\n    public static TestFlowNodeState skippedState(int id, SStateCategory stateCategory) {\n        return new TestFlowNodeState(id, stateCategory, false, false, false);\n    }\n\n    @Override\n    public boolean shouldExecuteState(SProcessDefinition processDefinition, SFlowNodeInstance flowNodeInstance) {\n        return shouldBeExecuted;\n    }\n\n    @Override\n    public boolean mustAddSystemComment(SFlowNodeInstance flowNodeInstance) {\n        return false;\n    }\n\n    @Override\n    public String getSystemComment(SFlowNodeInstance flowNodeInstance) {\n        return null;\n    }\n\n    @Override\n    public StateCode execute(SProcessDefinition processDefinition, SFlowNodeInstance instance)\n            throws SActivityStateExecutionException {\n        return null;\n    }\n\n    @Override\n    public int getId() {\n        return id;\n    }\n\n    @Override\n    public String getName() {\n        return \"test-state-\" + id;\n    }\n\n    @Override\n    public boolean isStable() {\n        return stable;\n    }\n\n    @Override\n    public boolean isTerminal() {\n        return terminal;\n    }\n\n    @Override\n    public SStateCategory getStateCategory() {\n        return stateCategory;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/TransactionServiceMock.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport java.util.Optional;\nimport java.util.concurrent.Callable;\n\nimport javax.transaction.Synchronization;\n\nimport org.bonitasoft.engine.transaction.TransactionService;\n\n/**\n * @author Baptiste Mesta\n */\npublic class TransactionServiceMock implements TransactionService {\n\n    @Override\n    public void begin() {\n    }\n\n    @Override\n    public void complete() {\n    }\n\n    @Deprecated\n    @Override\n    public boolean isTransactionActive() {\n        return false;\n    }\n\n    @Override\n    public Optional<Boolean> hasMultipleResources() {\n        return Optional.empty();\n    }\n\n    @Override\n    public void setRollbackOnly() {\n    }\n\n    @Override\n    public boolean isRollbackOnly() {\n        return false;\n    }\n\n    @Override\n    public <T> T executeInTransaction(final Callable<T> callable) throws Exception {\n        begin();\n        try {\n            return callable.call();\n        } catch (final Exception e) {\n            setRollbackOnly();\n            throw e;\n        } finally {\n            complete();\n        }\n    }\n\n    @Override\n    public void registerBonitaSynchronization(final Synchronization txSync) {\n    }\n\n    @Override\n    public void registerBeforeCommitCallable(final Callable<Void> callable) {\n    }\n\n    @Override\n    public long getNumberOfActiveTransactions() {\n        return 0;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/TransitionEvaluatorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\n\nimport java.util.List;\n\nimport org.assertj.core.util.Lists;\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.execution.flowmerger.FlowNodeTransitionsWrapper;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class TransitionEvaluatorTest {\n\n    @Rule\n    public ExpectedException thrown = ExpectedException.none();\n\n    @Mock\n    private ExpressionResolverService expressionResolverService;\n\n    @InjectMocks\n    private TransitionEvaluator transitionEvaluator;\n\n    @Test(expected = NullPointerException.class)\n    public void testEvaluateOutgoingTransitions_should_throw_NPE() throws Exception {\n        transitionEvaluator.evaluateOutgoingTransitions(null, null, null);\n    }\n\n    @Test\n    public void testEvaluateOutgoingTransitions_with_empty_params_should_return_empty_list() throws Exception {\n        SProcessDefinition processDefinition = mock(SProcessDefinition.class);\n        SFlowNodeInstance flowNodeInstance = mock(SFlowNodeInstance.class);\n        FlowNodeTransitionsWrapper transitions = new FlowNodeTransitionsWrapper();\n        transitions.setAllOutgoingTransitionDefinitions(Lists.<STransitionDefinition> newArrayList());\n\n        List<STransitionDefinition> results = transitionEvaluator.evaluateOutgoingTransitions(transitions,\n                processDefinition, flowNodeInstance);\n        assertThat(results).isEmpty();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/WaitingEventsInterrupterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution;\n\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\nimport java.util.Optional;\n\nimport org.bonitasoft.engine.core.process.definition.model.event.impl.SIntermediateCatchEventDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.STimerType;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.STimerEventTriggerDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SProcessDefinitionImpl;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.event.SIntermediateCatchEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance;\nimport org.bonitasoft.engine.scheduler.SchedulerService;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnit;\nimport org.mockito.junit.MockitoRule;\n\npublic class WaitingEventsInterrupterTest {\n\n    public static final long CATCH_EVENT_INSTANCE_ID = 432987L;\n    @Rule\n    public MockitoRule mockitoRule = MockitoJUnit.rule();\n\n    @Mock\n    private EventInstanceService eventInstanceService;\n    @Mock\n    private SchedulerService schedulerService;\n\n    private WaitingEventsInterrupter waitingEventsInterrupter;\n    private SProcessDefinitionImpl processDefinition;\n    private SIntermediateCatchEventInstance catchEventInstance;\n    private SIntermediateCatchEventDefinitionImpl catchEventDefinition;\n\n    @Before\n    public void before() {\n        waitingEventsInterrupter = new WaitingEventsInterrupter(eventInstanceService, schedulerService);\n\n        processDefinition = new SProcessDefinitionImpl(\"myPocess\", \"1.0\");\n        catchEventInstance = new SIntermediateCatchEventInstance();\n        catchEventInstance.setId(CATCH_EVENT_INSTANCE_ID);\n        catchEventDefinition = new SIntermediateCatchEventDefinitionImpl(1234L, \"myCatchTimer\");\n    }\n\n    @Test\n    public void should_delete_timer_job_associated_with_the_catch_event() throws Exception {\n        catchEventDefinition.addTimerEventTrigger(new STimerEventTriggerDefinitionImpl(STimerType.DURATION, null));\n\n        waitingEventsInterrupter.interruptWaitingEvents(processDefinition, catchEventInstance, catchEventDefinition);\n\n        verify(schedulerService).delete(\"Timer_Ev_\" + CATCH_EVENT_INSTANCE_ID);\n    }\n\n    @Test\n    public void should_delete_event_trigger_instance_associated_with_the_catch_timer() throws Exception {\n        catchEventDefinition.addTimerEventTrigger(new STimerEventTriggerDefinitionImpl(STimerType.DURATION, null));\n        STimerEventTriggerInstance eventTriggerInstance = new STimerEventTriggerInstance(123, \"someEvent\", 123, \"job\");\n        doReturn(Optional.of(eventTriggerInstance)).when(eventInstanceService)\n                .getTimerEventTriggerInstanceOfFlowNode(CATCH_EVENT_INSTANCE_ID);\n\n        waitingEventsInterrupter.interruptWaitingEvents(processDefinition, catchEventInstance, catchEventDefinition);\n\n        verify(eventInstanceService).deleteEventTriggerInstanceOfFlowNode(CATCH_EVENT_INSTANCE_ID);\n    }\n\n    @Test\n    public void should_not_delete_timer_and_event_trigger_when_catch_is_not_a_timer() throws Exception {\n\n        waitingEventsInterrupter.interruptWaitingEvents(processDefinition, catchEventInstance, catchEventDefinition);\n\n        verifyNoInteractions(eventInstanceService);\n        verifyNoInteractions(schedulerService);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/archive/BPMArchiverServiceTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.archive;\n\nimport static org.mockito.ArgumentMatchers.anyInt;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.archive.ArchiveService;\nimport org.bonitasoft.engine.business.data.DataRetentionBdmTrackingService;\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.core.connector.ConnectorInstanceService;\nimport org.bonitasoft.engine.core.contract.data.ContractDataService;\nimport org.bonitasoft.engine.core.document.api.DocumentService;\nimport org.bonitasoft.engine.core.process.comment.api.SCommentService;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.BPMFailureService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.RefBusinessDataService;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceService;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * author Emmanuel Duchastenier\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class BPMArchiverServiceTest {\n\n    @Mock\n    private ArchiveService archiveService;\n    @Mock\n    private ProcessInstanceService processInstanceService;\n    @Mock\n    private DocumentService documentService;\n    @Mock\n    private SCommentService commentService;\n    @Mock\n    private ProcessDefinitionService processDefinitionService;\n    @Mock\n    private ConnectorInstanceService connectorInstanceService;\n    @Mock\n    private ClassLoaderService classLoaderService;\n    @Mock\n    private RefBusinessDataService refBusinessDataService;\n    @Mock\n    private ContractDataService contractDataService;\n    @Mock\n    private DataInstanceService dataInstanceService;\n    @Mock\n    private ActivityInstanceService activityInstanceService;\n    @Mock\n    private BPMFailureService failureService;\n    @Mock\n    private DataRetentionBdmTrackingService dataRetentionBdmTrackingService;\n    @Spy\n    @InjectMocks\n    private BPMArchiverService bpmArchiverService;\n\n    @Test\n    public void archiveProcessInstance_should_archive_SRefBusinessDataInstances() throws Exception {\n        final SProcessSimpleRefBusinessDataInstance ref1 = new SProcessSimpleRefBusinessDataInstance();\n        ref1.setId(1L); // so that those 3 objects are not considered the same (in the verify)\n        ref1.setDataId(100L);\n        ref1.setDataClassName(\"com.company.model.Invoice\");\n        final SProcessSimpleRefBusinessDataInstance ref2 = new SProcessSimpleRefBusinessDataInstance();\n        ref2.setId(2L);\n        ref2.setDataId(200L);\n        ref2.setDataClassName(\"com.company.model.Invoice\");\n        final SProcessMultiRefBusinessDataInstance ref3 = new SProcessMultiRefBusinessDataInstance();\n        ref3.setId(3L);\n        ref3.setDataIds(Arrays.asList(300L, 301L));\n        ref3.setDataClassName(\"com.company.model.LineItem\");\n        List<SRefBusinessDataInstance> sRefBusinessDataInstances = Arrays.asList(ref1, ref2, ref3);\n        SProcessInstance processInstance = new SProcessInstance();\n        processInstance.setId(451L);\n\n        doReturn(mock(SAProcessInstance.class)).when(bpmArchiverService).buildArchiveProcessInstance(processInstance);\n        doNothing().when(bpmArchiverService).archiveConnectorInstancesIfAny(eq(processInstance),\n                nullable(SProcessDefinition.class),\n                anyLong());\n\n        doReturn(sRefBusinessDataInstances).when(refBusinessDataService)\n                .getRefBusinessDataInstances(eq(processInstance.getId()), eq(0), anyInt());\n        doNothing().when(refBusinessDataService)\n                .archiveRefBusinessDataInstance(nullable(SRefBusinessDataInstance.class));\n\n        bpmArchiverService.archiveAndDeleteProcessInstance(processInstance);\n\n        verify(refBusinessDataService).archiveRefBusinessDataInstance(ref1);\n        verify(refBusinessDataService).archiveRefBusinessDataInstance(ref2);\n        verify(refBusinessDataService).archiveRefBusinessDataInstance(ref3);\n\n        // Verify tracking upsert for simple refs\n        verify(dataRetentionBdmTrackingService).upsert(100L, \"com.company.model.Invoice\");\n        verify(dataRetentionBdmTrackingService).upsert(200L, \"com.company.model.Invoice\");\n        // Verify tracking upsert for multi ref (each data ID)\n        verify(dataRetentionBdmTrackingService).upsert(300L, \"com.company.model.LineItem\");\n        verify(dataRetentionBdmTrackingService).upsert(301L, \"com.company.model.LineItem\");\n    }\n\n    @Test\n    public void archiveAndDeleteFlowNodeInstance_should_archive_failures() throws Exception {\n        var flowNodeInstance = mock(SFlowNodeInstance.class);\n        when(flowNodeInstance.getId()).thenReturn(123L);\n        when(flowNodeInstance.getType()).thenReturn(SFlowNodeType.END_EVENT);\n\n        bpmArchiverService.archiveAndDeleteFlowNodeInstance(flowNodeInstance, 1L);\n\n        verify(failureService).archiveFlowNodeFailures(eq(123L), any(long.class));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/event/ErrorEventHandlerStrategyTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.event;\n\nimport static org.mockito.ArgumentMatchers.anyList;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.*;\n\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.impl.SBoundaryEventDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.SCatchErrorEventTriggerDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SFlowElementContainerDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SProcessDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SStandardLoopCharacteristicsImpl;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SUserTaskDefinitionImpl;\nimport org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.STaskPriority;\nimport org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SEndEventInstance;\nimport org.bonitasoft.engine.execution.ContainerRegistry;\nimport org.bonitasoft.engine.execution.ProcessInstanceInterruptor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ErrorEventHandlerStrategyTest {\n\n    @Mock\n    private EventInstanceService eventInstanceService;\n\n    @Mock\n    private ProcessInstanceService processInstanceService;\n\n    @Mock\n    private FlowNodeInstanceService flowNodeInstanceService;\n\n    @Mock\n    private ContainerRegistry containerRegistry;\n\n    @Mock\n    private ProcessDefinitionService processDefinitionService;\n\n    @Mock\n    private EventsHandler eventsHandler;\n    @Mock\n    private ProcessInstanceInterruptor processInstanceInterruptor;\n\n    private ErrorEventHandlerStrategy spyStrategy;\n\n    @Before\n    public void setUp() {\n        spyStrategy = spy(new ErrorEventHandlerStrategy(eventInstanceService, processInstanceService,\n                flowNodeInstanceService,\n                processDefinitionService, eventsHandler, processInstanceInterruptor));\n    }\n\n    @Test\n    public void getWaitingErrorEventFromBoundary_should_find_matching_event_in_multiple_instance_activity()\n            throws Exception {\n        final long processDefinitionId = 7845475L;\n        final long flowNodeDefinitionId = 357L;\n        final long rootContainerId = 951L;\n        final long parentContainerId = 7887L;\n        final long logicalGroup2 = 78454646L;\n        final long parentActivityInstanceId = 7897L;\n        final String errorCode = \"mistake\";\n        final SProcessDefinition definition = buildProcessDefinitionWithAMultipleInstanceUserTask(flowNodeDefinitionId,\n                errorCode);\n        final SMultiInstanceActivityInstance multiInstanceInstance = buildMultiInstanceActivity(processDefinitionId,\n                flowNodeDefinitionId, rootContainerId,\n                parentContainerId);\n        final SEndEventInstance eventInstance = buildEventInstance(processDefinitionId, flowNodeDefinitionId,\n                rootContainerId, parentContainerId,\n                logicalGroup2);\n        final SUserTaskInstance instance = buildSUserTaskInstance(processDefinitionId, flowNodeDefinitionId,\n                rootContainerId, parentContainerId,\n                logicalGroup2, parentActivityInstanceId);\n        when(processDefinitionService.getProcessDefinition(processDefinitionId)).thenReturn(definition);\n        when(flowNodeInstanceService.getFlowNodeInstance(parentActivityInstanceId)).thenReturn(multiInstanceInstance);\n\n        spyStrategy.getWaitingErrorEventFromBoundary(eventInstance, errorCode, instance);\n\n        verify(spyStrategy).getWaitingErrorEventFromBoundary(eq(errorCode), eq(multiInstanceInstance), anyList());\n    }\n\n    private SMultiInstanceActivityInstance buildMultiInstanceActivity(final long processDefinitionId,\n            final long flowNodeDefinitionId,\n            final long rootContainerId,\n            final long parentContainerId) {\n        final SMultiInstanceActivityInstance multiInstanceInstance = new SMultiInstanceActivityInstance(\"MIActivity\",\n                flowNodeDefinitionId,\n                rootContainerId, parentContainerId, processDefinitionId, rootContainerId, false);\n        return multiInstanceInstance;\n    }\n\n    private SUserTaskInstance buildSUserTaskInstance(final long processDefinitionId, final long flowNodeDefinitionId,\n            final long rootContainerId,\n            final long parentContainerId, final long logicalGroup2, final long parentActivityInstanceId) {\n        final SUserTaskInstance instance = new SUserTaskInstance(\"usertTask\", flowNodeDefinitionId, rootContainerId,\n                parentContainerId, 457L,\n                STaskPriority.NORMAL, processDefinitionId, logicalGroup2);\n        instance.setLogicalGroup(2, parentActivityInstanceId);\n        return instance;\n    }\n\n    private SEndEventInstance buildEventInstance(final long processDefinitionId, final long flowNodeDefinitionId,\n            final long rootContainerId,\n            final long parentContainerId, final long logicalGroup2) {\n        final SEndEventInstance eventInstance = new SEndEventInstance(\"ErrorEvent\", flowNodeDefinitionId,\n                rootContainerId, parentContainerId,\n                processDefinitionId, logicalGroup2);\n        eventInstance.setLogicalGroup(2, 9L);\n        return eventInstance;\n    }\n\n    private SProcessDefinition buildProcessDefinitionWithAMultipleInstanceUserTask(final long flowNodeDefinitionId,\n            final String errorCode) {\n        final SProcessDefinitionImpl definition = new SProcessDefinitionImpl(\"test\", \"2.0\");\n        final SStandardLoopCharacteristicsImpl loopCharacteristics = new SStandardLoopCharacteristicsImpl(null, true);\n        final SUserTaskDefinitionImpl taskDefinition = new SUserTaskDefinitionImpl(flowNodeDefinitionId, \"usertTask\",\n                \"employee\");\n        taskDefinition.setLoopCharacteristics(loopCharacteristics);\n        final SBoundaryEventDefinitionImpl boundaryEventDefinition = new SBoundaryEventDefinitionImpl(786768768L,\n                \"error\");\n        final SCatchErrorEventTriggerDefinitionImpl errorEventTrigger = new SCatchErrorEventTriggerDefinitionImpl(\n                errorCode);\n        boundaryEventDefinition.addErrorEventTrigger(errorEventTrigger);\n        taskDefinition.addBoundaryEventDefinition(boundaryEventDefinition);\n        final SFlowElementContainerDefinitionImpl processContainer = new SFlowElementContainerDefinitionImpl();\n        processContainer.addActivity(taskDefinition);\n        definition.setProcessContainer(processContainer);\n        return definition;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/event/TimerEventHandlerStrategyTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.event;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.verify;\n\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.process.definition.model.event.impl.SEventDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.event.impl.SStartEventDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.STimerType;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.STimerEventTriggerDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SProcessDefinitionImpl;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.expression.model.impl.SExpressionImpl;\nimport org.bonitasoft.engine.scheduler.SchedulerService;\nimport org.bonitasoft.engine.scheduler.model.SJobDescriptor;\nimport org.bonitasoft.engine.scheduler.model.SJobParameter;\nimport org.bonitasoft.engine.scheduler.trigger.OneShotTrigger;\nimport org.bonitasoft.engine.scheduler.trigger.Trigger;\nimport org.bonitasoft.engine.scheduler.trigger.UnixCronTrigger;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Baptiste Mesta\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class TimerEventHandlerStrategyTest {\n\n    private static final long PROCESS_DEFINITION_ID = 5642367345L;\n    private static final long SUB_PROCESS_ID = 84233523563L;\n    private static final long PROCESS_INSTANCE_ID = 15908L;\n    @Mock\n    private SchedulerService schedulerService;\n    @Mock\n    private ExpressionResolverService expressionResolverService;\n    @Mock\n    private EventInstanceService eventInstanceService;\n    @InjectMocks\n    private TimerEventHandlerStrategy timerEventHandlerStrategy;\n    private SProcessDefinitionImpl processDefinition;\n    private SEventDefinitionImpl eventDefintion;\n    private STimerEventTriggerDefinitionImpl eventTriggerDefinition;\n    private SProcessInstance processInstance;\n    private SExpressionImpl timerExpression;\n    @Captor\n    private ArgumentCaptor<SJobDescriptor> jobDescriptorArgumentCaptor;\n    @Captor\n    private ArgumentCaptor<List<SJobParameter>> listArgumentCaptor;\n    @Captor\n    private ArgumentCaptor<Trigger> triggerArgumentCaptor;\n\n    @Before\n    public void before() throws Exception {\n        processDefinition = new SProcessDefinitionImpl(\"test\", \"1.0\");\n        processDefinition.setId(PROCESS_DEFINITION_ID);\n        eventDefintion = new SStartEventDefinitionImpl(6543721L, \"startevent\");\n        timerExpression = new SExpressionImpl(\"duration\", \"12342\", \"constant\", \"long\", \"\",\n                Collections.<SExpression> emptyList());\n\n        processInstance = new SProcessInstance(\"proceInst\", PROCESS_DEFINITION_ID);\n        processInstance.setId(PROCESS_INSTANCE_ID);\n\n    }\n\n    @Test\n    public void should_trigger_timer_for_event_subprocess_evaluate_trigger_using_process_instance_context()\n            throws Exception {\n        doReturn(5000L).when(expressionResolverService).evaluate(timerExpression,\n                new SExpressionContext(PROCESS_INSTANCE_ID, \"PROCESS_INSTANCE\", PROCESS_DEFINITION_ID));\n        eventTriggerDefinition = new STimerEventTriggerDefinitionImpl(STimerType.DURATION, timerExpression);\n        //when\n        timerEventHandlerStrategy.handleEventSubProcess(processDefinition, eventDefintion, eventTriggerDefinition,\n                SUB_PROCESS_ID, processInstance);\n        //then\n        verify(expressionResolverService).evaluate(timerExpression,\n                new SExpressionContext(PROCESS_INSTANCE_ID, \"PROCESS_INSTANCE\", PROCESS_DEFINITION_ID));\n    }\n\n    @Test\n    public void should_trigger_timer_with_expected_duration() throws Exception {\n        doReturn(5000L).when(expressionResolverService).evaluate(timerExpression,\n                new SExpressionContext(PROCESS_INSTANCE_ID, \"PROCESS_INSTANCE\", PROCESS_DEFINITION_ID));\n        eventTriggerDefinition = new STimerEventTriggerDefinitionImpl(STimerType.DURATION, timerExpression);\n        //when\n        timerEventHandlerStrategy.handleEventSubProcess(processDefinition, eventDefintion, eventTriggerDefinition,\n                SUB_PROCESS_ID, processInstance);\n        long now = System.currentTimeMillis();\n        //then\n        verify(schedulerService).schedule(jobDescriptorArgumentCaptor.capture(), listArgumentCaptor.capture(),\n                triggerArgumentCaptor.capture());\n        assertThat(triggerArgumentCaptor.getValue()).isInstanceOf(OneShotTrigger.class);\n        assertThat(triggerArgumentCaptor.getValue().getStartDate()).isBetween(new Date(now - 200),\n                new Date(now + 5200));\n    }\n\n    @Test\n    public void should_trigger_timer_with_expected_cycle() throws Exception {\n        doReturn(\"theCron\").when(expressionResolverService).evaluate(timerExpression,\n                new SExpressionContext(PROCESS_INSTANCE_ID, \"PROCESS_INSTANCE\", PROCESS_DEFINITION_ID));\n        eventTriggerDefinition = new STimerEventTriggerDefinitionImpl(STimerType.CYCLE, timerExpression);\n        //when\n        timerEventHandlerStrategy.handleEventSubProcess(processDefinition, eventDefintion, eventTriggerDefinition,\n                SUB_PROCESS_ID, processInstance);\n        long now = System.currentTimeMillis();\n        //then\n        verify(schedulerService).schedule(jobDescriptorArgumentCaptor.capture(), listArgumentCaptor.capture(),\n                triggerArgumentCaptor.capture());\n        assertThat(triggerArgumentCaptor.getValue()).isInstanceOf(UnixCronTrigger.class);\n        assertThat(((UnixCronTrigger) triggerArgumentCaptor.getValue()).getExpression()).isEqualTo(\"theCron\");\n    }\n\n    @Test\n    public void should_trigger_timer_with_expected_date() throws Exception {\n        Date startDate = new Date(4893705);\n        doReturn(startDate).when(expressionResolverService).evaluate(timerExpression,\n                new SExpressionContext(PROCESS_INSTANCE_ID, \"PROCESS_INSTANCE\", PROCESS_DEFINITION_ID));\n        eventTriggerDefinition = new STimerEventTriggerDefinitionImpl(STimerType.DATE, timerExpression);\n        //when\n        timerEventHandlerStrategy.handleEventSubProcess(processDefinition, eventDefintion, eventTriggerDefinition,\n                SUB_PROCESS_ID, processInstance);\n        long now = System.currentTimeMillis();\n        //then\n        verify(schedulerService).schedule(jobDescriptorArgumentCaptor.capture(), listArgumentCaptor.capture(),\n                triggerArgumentCaptor.capture());\n        assertThat(triggerArgumentCaptor.getValue()).isInstanceOf(OneShotTrigger.class);\n        assertThat(triggerArgumentCaptor.getValue().getStartDate()).isEqualTo(startDate);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/flowmerger/FlowNodeTransitionsWrapperTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.flowmerger;\n\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertTrue;\n\nimport java.util.Arrays;\nimport java.util.Collections;\n\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class FlowNodeTransitionsWrapperTest {\n\n    @Mock\n    private STransitionDefinition transition1;\n\n    @Mock\n    private STransitionDefinition transition2;\n\n    private FlowNodeTransitionsWrapper flowNodeTransitionsWrapper;\n\n    @Before\n    public void setUp() {\n        flowNodeTransitionsWrapper = new FlowNodeTransitionsWrapper();\n    }\n\n    @Test\n    public void is_last_flowNode_if_no_valid_outgoing_transitions() {\n        flowNodeTransitionsWrapper\n                .setValidOutgoingTransitionDefinitions(Collections.<STransitionDefinition> emptyList());\n        assertTrue(flowNodeTransitionsWrapper.isLastFlowNode());\n    }\n\n    @Test\n    public void is_not_last_flowNode_if_valid_outgoing_transitions() {\n        flowNodeTransitionsWrapper.setValidOutgoingTransitionDefinitions(Collections.singletonList(transition1));\n        assertFalse(flowNodeTransitionsWrapper.isLastFlowNode());\n    }\n\n    @Test\n    public void has_multiple_outgoing_transitions() {\n        flowNodeTransitionsWrapper.setAllOutgoingTransitionDefinitions(Arrays.asList(transition1, transition2));\n        assertTrue(flowNodeTransitionsWrapper.hasMultipleOutgoingTransitions());\n    }\n\n    @Test\n    public void doesnt_have_multiple_outgoing_transitions_empty_list() {\n        flowNodeTransitionsWrapper.setAllOutgoingTransitionDefinitions(Collections.<STransitionDefinition> emptyList());\n        assertFalse(flowNodeTransitionsWrapper.hasMultipleOutgoingTransitions());\n    }\n\n    @Test\n    public void doesnt_have_multiple_outgoing_transitions_one_transition() {\n        flowNodeTransitionsWrapper.setAllOutgoingTransitionDefinitions(Arrays.asList(transition1));\n        assertFalse(flowNodeTransitionsWrapper.hasMultipleOutgoingTransitions());\n    }\n\n    @Test\n    public void has_multiple_incoming_transitions() {\n        flowNodeTransitionsWrapper.setInputTransitionsSize(2);\n        assertTrue(flowNodeTransitionsWrapper.hasMultipleIncomingTransitions());\n    }\n\n    @Test\n    public void doesnt_have_multiple_incoming_transitions_zero() {\n        flowNodeTransitionsWrapper.setInputTransitionsSize(0);\n        assertFalse(flowNodeTransitionsWrapper.hasMultipleIncomingTransitions());\n    }\n\n    @Test\n    public void doesnt_have_multiple_incoming_transitions_one() {\n        flowNodeTransitionsWrapper.setInputTransitionsSize(1);\n        assertFalse(flowNodeTransitionsWrapper.hasMultipleIncomingTransitions());\n    }\n\n    @Test\n    public void isSimple_return_true_if_has_one_incoming_and_one_outgoing_transitions() {\n        flowNodeTransitionsWrapper.setInputTransitionsSize(1);\n        flowNodeTransitionsWrapper.setAllOutgoingTransitionDefinitions(Arrays.asList(transition1));\n        assertTrue(flowNodeTransitionsWrapper.isSimpleMerge());\n    }\n\n    @Test\n    public void isSimple_return_true_if_has_one_incoming_and_one_default_outgoing_transitions() {\n        flowNodeTransitionsWrapper.setInputTransitionsSize(1);\n        flowNodeTransitionsWrapper.setAllOutgoingTransitionDefinitions(Collections.<STransitionDefinition> emptyList());\n        flowNodeTransitionsWrapper.setValidOutgoingTransitionDefinitions(Arrays.asList(transition1));\n        assertTrue(flowNodeTransitionsWrapper.isSimpleMerge());\n    }\n\n    @Test\n    public void isSimple_return_false_if_has_one_incoming_and_no_default_outgoing_transitions() {\n        flowNodeTransitionsWrapper.setInputTransitionsSize(1);\n        flowNodeTransitionsWrapper.setAllOutgoingTransitionDefinitions(Collections.<STransitionDefinition> emptyList());\n        flowNodeTransitionsWrapper\n                .setValidOutgoingTransitionDefinitions(Collections.<STransitionDefinition> emptyList());\n        assertFalse(flowNodeTransitionsWrapper.isSimpleMerge());\n    }\n\n    @Test\n    public void isSimple_return_true_if_has_zero_incoming_and_one_outgoing_transitions() {\n        flowNodeTransitionsWrapper.setInputTransitionsSize(0);\n        flowNodeTransitionsWrapper.setAllOutgoingTransitionDefinitions(Arrays.asList(transition1));\n        assertTrue(flowNodeTransitionsWrapper.isSimpleMerge());\n    }\n\n    @Test\n    public void isSimple_return_false_if_has_zero_outgoing_transitions() {\n        flowNodeTransitionsWrapper.setInputTransitionsSize(1);\n        flowNodeTransitionsWrapper.setAllOutgoingTransitionDefinitions(Arrays.<STransitionDefinition> asList());\n        assertFalse(flowNodeTransitionsWrapper.isSimpleMerge());\n    }\n\n    @Test\n    public void isSimple_return_false_if_has_one_incoming_and_multiple_outgoing_transitions() {\n        flowNodeTransitionsWrapper.setInputTransitionsSize(1);\n        flowNodeTransitionsWrapper.setAllOutgoingTransitionDefinitions(Arrays.asList(transition1, transition2));\n        assertFalse(flowNodeTransitionsWrapper.isSimpleMerge());\n    }\n\n    @Test\n    public void isSimple_return_false_if_has_multiple_incoming_and_one_outgoing_transitions() {\n        flowNodeTransitionsWrapper.setInputTransitionsSize(2);\n        flowNodeTransitionsWrapper.setAllOutgoingTransitionDefinitions(Arrays.asList(transition1));\n        assertFalse(flowNodeTransitionsWrapper.isSimpleMerge());\n    }\n\n    @Test\n    public void isSimpleToMany_return_true_if_has_one_incoming_and_multiple_outgoing_transitions() {\n        flowNodeTransitionsWrapper.setInputTransitionsSize(1);\n        flowNodeTransitionsWrapper.setAllOutgoingTransitionDefinitions(Arrays.asList(transition1, transition2));\n        assertTrue(flowNodeTransitionsWrapper.isSimpleToMany());\n    }\n\n    @Test\n    public void isSimpleToMany_return_true_if_has_zero_incoming_and_multiple_outgoing_transitions() {\n        flowNodeTransitionsWrapper.setInputTransitionsSize(0);\n        flowNodeTransitionsWrapper.setAllOutgoingTransitionDefinitions(Arrays.asList(transition1, transition2));\n        assertTrue(flowNodeTransitionsWrapper.isSimpleToMany());\n    }\n\n    @Test\n    public void isSimpleToMany_return_false_if_has_one_incoming_and_one_outgoing_transitions() {\n        flowNodeTransitionsWrapper.setInputTransitionsSize(1);\n        flowNodeTransitionsWrapper.setAllOutgoingTransitionDefinitions(Arrays.asList(transition1));\n        assertFalse(flowNodeTransitionsWrapper.isSimpleToMany());\n    }\n\n    @Test\n    public void isManyToMany_return_true_if_has_multiple_incoming_and_outgoing_transitions() {\n        flowNodeTransitionsWrapper.setInputTransitionsSize(2);\n        flowNodeTransitionsWrapper.setAllOutgoingTransitionDefinitions(Arrays.asList(transition1, transition2));\n        assertTrue(flowNodeTransitionsWrapper.isManyToMany());\n    }\n\n    @Test\n    public void isManyToMany_return_false_if_has_multiple_incoming_and_one_outgoing_transition() {\n        flowNodeTransitionsWrapper.setInputTransitionsSize(2);\n        flowNodeTransitionsWrapper.setAllOutgoingTransitionDefinitions(Arrays.asList(transition1));\n        assertFalse(flowNodeTransitionsWrapper.isManyToMany());\n    }\n\n    @Test\n    public void isManyToMany_return_false_if_has_one_incoming_and_multiple_outgoing_transition() {\n        flowNodeTransitionsWrapper.setInputTransitionsSize(1);\n        flowNodeTransitionsWrapper.setAllOutgoingTransitionDefinitions(Arrays.asList(transition1, transition2));\n        assertFalse(flowNodeTransitionsWrapper.isManyToMany());\n    }\n\n    @Test\n    public void isManyToOne_return_True_if_has_multiple_incoming_and_one_outgoing_transition() {\n        flowNodeTransitionsWrapper.setInputTransitionsSize(2);\n        flowNodeTransitionsWrapper.setAllOutgoingTransitionDefinitions(Arrays.asList(transition1));\n        assertTrue(flowNodeTransitionsWrapper.isManyToOne());\n    }\n\n    @Test\n    public void isManyToOne_return_false_if_has_multiple_incoming_and_multiple_outgoing_transition() {\n        flowNodeTransitionsWrapper.setInputTransitionsSize(2);\n        flowNodeTransitionsWrapper.setAllOutgoingTransitionDefinitions(Arrays.asList(transition1, transition2));\n        assertFalse(flowNodeTransitionsWrapper.isManyToOne());\n    }\n\n    @Test\n    public void isManyToOne_return_false_if_has_one_incoming_and_multiple_one_transition() {\n        flowNodeTransitionsWrapper.setInputTransitionsSize(1);\n        flowNodeTransitionsWrapper.setAllOutgoingTransitionDefinitions(Arrays.asList(transition1));\n        assertFalse(flowNodeTransitionsWrapper.isManyToOne());\n    }\n\n    @Test\n    public void isManyToOne_return_false_if_has_multiple_incoming_and_multiple_zero_transition() {\n        flowNodeTransitionsWrapper.setInputTransitionsSize(12);\n        flowNodeTransitionsWrapper.setAllOutgoingTransitionDefinitions(Arrays.<STransitionDefinition> asList());\n        assertFalse(flowNodeTransitionsWrapper.isManyToOne());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/state/CancellingCallActivityStateTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.*;\n\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SProcessDefinitionImpl;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.execution.ProcessInstanceInterruptor;\nimport org.bonitasoft.engine.execution.archive.BPMArchiverService;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Baptiste Mesta\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class CancellingCallActivityStateTest {\n\n    private static final long CALL_ACTIVITY_ID = 665437L;\n    private static final long PROCESS_DEFINITION_ID = 4538938902L;\n    private static final long PROCESS_INSTANCE_ID = 3240213094L;\n\n    private SProcessDefinition processDefinition = new SProcessDefinitionImpl(\"myProcess\", \"1.0\");\n    private SProcessInstance processInstance = new SProcessInstance(\"myProcess\", PROCESS_DEFINITION_ID);\n    private SCallActivityInstance callActivity = new SCallActivityInstance(\"callACtivity\", 5342985348L, 4323264L,\n            65222L, 87686L, 2342L);\n    @Mock\n    private ProcessInstanceService processInstanceService;\n    @Mock\n    ProcessInstanceInterruptor processInstanceInterruptor;\n    @Mock\n    BPMArchiverService bpmArchiverService;\n\n    @InjectMocks\n    private CancellingCallActivityState cancellingCallActivityState;\n\n    @Before\n    public void before() throws Exception {\n        callActivity.setId(CALL_ACTIVITY_ID);\n        processInstance.setId(PROCESS_INSTANCE_ID);\n    }\n\n    @Test\n    public void should_not_execute_state_when_target_process_is_not_found() throws Exception {\n        doThrow(SProcessInstanceNotFoundException.class).when(processInstanceService)\n                .getChildOfActivity(CALL_ACTIVITY_ID);\n        //when\n        boolean actual = cancellingCallActivityState.shouldExecuteState(processDefinition, callActivity);\n\n        assertThat(actual).isFalse();\n    }\n\n    @Test\n    public void should_not_execute_state_when_process_interruptor_did_not_execute_anything() throws Exception {\n        doReturn(processInstance).when(processInstanceService).getChildOfActivity(CALL_ACTIVITY_ID);\n        doReturn(false).when(processInstanceInterruptor).interruptProcessInstance(PROCESS_INSTANCE_ID,\n                SStateCategory.CANCELLING);\n        //when\n        boolean actual = cancellingCallActivityState.shouldExecuteState(processDefinition, callActivity);\n\n        assertThat(actual).isFalse();\n        verify(bpmArchiverService).archiveAndDeleteProcessInstance(processInstance);\n    }\n\n    @Test\n    public void should_execute_state_when_process_interruptor_aborted_child_of_target_process() throws Exception {\n        doReturn(processInstance).when(processInstanceService).getChildOfActivity(CALL_ACTIVITY_ID);\n        doReturn(true).when(processInstanceInterruptor).interruptProcessInstance(PROCESS_INSTANCE_ID,\n                SStateCategory.CANCELLING);\n        //when\n        boolean actual = cancellingCallActivityState.shouldExecuteState(processDefinition, callActivity);\n\n        assertThat(actual).isTrue();\n        verify(bpmArchiverService, never()).archiveAndDeleteProcessInstance(processInstance);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/state/InitializingActivityStateTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport static org.mockito.Mockito.verify;\n\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.execution.StateBehaviors;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Laurent Leseigneur\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class InitializingActivityStateTest {\n\n    @Mock\n    private StateBehaviors stateBehaviors;\n\n    @Mock\n    private SProcessDefinition sProcessDefinition;\n\n    @Mock\n    private SFlowNodeInstance sFlowNodeInstance;\n\n    @Test\n    public void should_update_expected_duration() throws Exception {\n        //given\n        InitializingActivityState initializingActivityState = new InitializingActivityState(stateBehaviors);\n\n        //when\n        initializingActivityState.afterConnectors(sProcessDefinition, sFlowNodeInstance);\n\n        //then\n        verify(stateBehaviors).updateExpectedDuration(sProcessDefinition, sFlowNodeInstance);\n    }\n\n    @Test\n    public void should_register_waiting_event_after_connectors() throws Exception {\n        InitializingActivityState initializingActivityState = new InitializingActivityState(stateBehaviors);\n\n        initializingActivityState.afterConnectors(sProcessDefinition, sFlowNodeInstance);\n\n        verify(stateBehaviors).registerWaitingEvent(sProcessDefinition, sFlowNodeInstance);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/state/InitializingActivityWithBoundaryEventsStateTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.*;\nimport static org.mockito.Mockito.mock;\n\nimport java.io.Serializable;\nimport java.util.Arrays;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SCallActivityDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SCallActivityDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SProcessDefinitionImpl;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException;\nimport org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.execution.ProcessExecutor;\nimport org.bonitasoft.engine.execution.StateBehaviors;\nimport org.bonitasoft.engine.execution.work.BPMWorkFactory;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.expression.model.impl.SExpressionImpl;\nimport org.bonitasoft.engine.work.WorkService;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Laurent Leseigneur\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class InitializingActivityWithBoundaryEventsStateTest {\n\n    @Mock\n    private StateBehaviors stateBehaviors;\n    @Mock\n    private ExpressionResolverService expressionResolverService;\n    @Mock\n    private ProcessExecutor processExecutor;\n    @Mock\n    private ActivityInstanceService activityInstanceService;\n    @Mock\n    private ProcessDefinitionService processDefinitionService;\n    @Mock\n    private WorkService workService;\n    @Mock\n    private BPMWorkFactory workFactory;\n    @InjectMocks\n    InitializingActivityWithBoundaryEventsState initializingActivityWithBoundaryEventsState;\n\n    @Before\n    public void before() {\n        initializingActivityWithBoundaryEventsState.setProcessExecutor(processExecutor);\n    }\n\n    @Test(expected = SActivityStateExecutionException.class)\n    public void should_throw_SActivityStateExecutionException_when_callDefinition_not_exist() throws Exception {\n        final long callerId = 666L;\n\n        SProcessDefinition sProcessDefinition = mock(SProcessDefinitionImpl.class);\n        SFlowElementContainerDefinition processContainer = mock(SFlowElementContainerDefinition.class);\n        SCallActivityInstance sFlowNodeInstance = new SCallActivityInstance();\n        sFlowNodeInstance.setId(666L);\n        sFlowNodeInstance.setFlowNodeDefinitionId(555L);\n        sFlowNodeInstance.setStateId(ProcessInstanceState.INITIALIZING.getId());\n\n        when(sProcessDefinition.getProcessContainer()).thenReturn(processContainer);\n        doReturn(null).when(processContainer).getFlowNode(555L);\n\n        //when\n        initializingActivityWithBoundaryEventsState.afterConnectors(sProcessDefinition, sFlowNodeInstance);\n\n        //then\n        verify(stateBehaviors).updateDisplayNameAndDescription(sProcessDefinition, sFlowNodeInstance);\n        verify(stateBehaviors).updateExpectedDuration(sProcessDefinition, sFlowNodeInstance);\n        verify(stateBehaviors).addAssignmentSystemCommentIfTaskWasAutoAssign(sFlowNodeInstance);\n        verify(stateBehaviors, never()).registerWaitingEvent(sProcessDefinition, sFlowNodeInstance);\n        verify(processExecutor, never()).start(any(), any(), any(), any(), any(), any(), eq(callerId), any(), any());\n    }\n\n    @Test\n    public void should_start_target_process_with_version__and_invoke_methods() throws Exception {\n        final long callerId = 666L;\n        final long targProcessInstanceId = 667L;\n        final long targetDefinitionId = 554L;\n        SProcessDefinition sProcessDefinition = mock(SProcessDefinitionImpl.class);\n        SFlowElementContainerDefinition processContainer = mock(SFlowElementContainerDefinition.class);\n        SCallActivityInstance sFlowNodeInstance = new SCallActivityInstance();\n        sFlowNodeInstance.setId(666L);\n        sFlowNodeInstance.setFlowNodeDefinitionId(555L);\n        sFlowNodeInstance.setStateId(ProcessInstanceState.INITIALIZING.getId());\n        SCallActivityDefinition callActivityDefinition = spy(new SCallActivityDefinitionImpl(666, \"call1\"));\n        Map<String, SExpression> startConctractInputs = new LinkedHashMap<>();\n        startConctractInputs.put(\"input1\", new SExpressionImpl(\"expr1\", \"content1\", null, null, null, null));\n        startConctractInputs.put(\"input2\", new SExpressionImpl(\"expr2\", \"content2\", null, null, null, null));\n        callActivityDefinition.getProcessStartContractInputs().putAll(startConctractInputs);\n        SProcessInstance targetProcess = new SProcessInstance();\n        targetProcess.setId(targProcessInstanceId);\n        targetProcess.setStateId(ProcessInstanceState.INITIALIZING.getId());\n        final SExpressionContext context = new SExpressionContext(callerId,\n                DataInstanceContainer.ACTIVITY_INSTANCE.name(), callerId);\n        SExpression callableElement = mock(SExpression.class);\n        SExpression callableElementVersion = mock(SExpression.class);\n        when(sProcessDefinition.getProcessContainer()).thenReturn(processContainer);\n        doReturn(callActivityDefinition).when(processContainer).getFlowNode(555L);\n        doReturn(callableElement).when(callActivityDefinition).getCallableElement();\n        doReturn(callableElementVersion).when(callActivityDefinition).getCallableElementVersion();\n        doReturn(\"target\").when(expressionResolverService).evaluate(eq(callableElement), any());\n        doReturn(\"v1\").when(expressionResolverService).evaluate(eq(callableElementVersion), any());\n        doReturn(Arrays.asList(\"value1\", \"value2\")).when(expressionResolverService).evaluate(anyList(), any());\n        Map<String, Serializable> processInputs = new LinkedHashMap<>();\n        processInputs.put(\"input1\", \"value1\");\n        processInputs.put(\"input2\", \"value2\");\n\n        doReturn(targetDefinitionId).when(processDefinitionService).getProcessDefinitionId(eq(\"target\"), eq(\"v1\"));\n\n        doReturn(targetProcess).when(processExecutor).start(eq(targetDefinitionId), eq(-1L), eq(0L), eq(0L),\n                any(), any(), eq(callerId), eq(-1L), eq(processInputs));\n\n        //when\n        initializingActivityWithBoundaryEventsState.afterConnectors(sProcessDefinition, sFlowNodeInstance);\n\n        //then\n        verify(stateBehaviors).updateDisplayNameAndDescription(sProcessDefinition, sFlowNodeInstance);\n        verify(stateBehaviors).updateExpectedDuration(sProcessDefinition, sFlowNodeInstance);\n        verify(stateBehaviors).addAssignmentSystemCommentIfTaskWasAutoAssign(sFlowNodeInstance);\n        verify(stateBehaviors).registerWaitingEvent(sProcessDefinition, sFlowNodeInstance);\n    }\n\n    @Test\n    public void should_start_latest_target_process_and_invoke_methods() throws Exception {\n        final long callerId = 666L;\n        final long targProcessInstanceId = 667L;\n        final long targetDefinitionId = 554L;\n        SProcessDefinition sProcessDefinition = mock(SProcessDefinitionImpl.class);\n        SFlowElementContainerDefinition processContainer = mock(SFlowElementContainerDefinition.class);\n        SCallActivityInstance sFlowNodeInstance = new SCallActivityInstance();\n        sFlowNodeInstance.setId(666L);\n        sFlowNodeInstance.setFlowNodeDefinitionId(555L);\n        sFlowNodeInstance.setStateId(ProcessInstanceState.INITIALIZING.getId());\n        SCallActivityDefinition callActivityDefinition = spy(new SCallActivityDefinitionImpl(666, \"call1\"));\n        Map<String, SExpression> startConctractInputs = new LinkedHashMap<>();\n        startConctractInputs.put(\"input1\", new SExpressionImpl(\"expr1\", \"content1\", null, null, null, null));\n        startConctractInputs.put(\"input2\", new SExpressionImpl(\"expr2\", \"content2\", null, null, null, null));\n        callActivityDefinition.getProcessStartContractInputs().putAll(startConctractInputs);\n        SProcessInstance targetProcess = new SProcessInstance();\n        targetProcess.setId(targProcessInstanceId);\n        targetProcess.setStateId(ProcessInstanceState.INITIALIZING.getId());\n        final SExpressionContext context = new SExpressionContext(callerId,\n                DataInstanceContainer.ACTIVITY_INSTANCE.name(), callerId);\n        SExpression callableElement = mock(SExpression.class);\n\n        when(sProcessDefinition.getProcessContainer()).thenReturn(processContainer);\n        doReturn(callActivityDefinition).when(processContainer).getFlowNode(555L);\n        doReturn(callableElement).when(callActivityDefinition).getCallableElement();\n        doReturn(null).when(callActivityDefinition).getCallableElementVersion();\n        doReturn(\"target\").when(expressionResolverService).evaluate(eq(callableElement), any());\n        doReturn(Arrays.asList(\"value1\", \"value2\")).when(expressionResolverService).evaluate(anyList(), any());\n        Map<String, Serializable> processInputs = new LinkedHashMap<>();\n        processInputs.put(\"input1\", \"value1\");\n        processInputs.put(\"input2\", \"value2\");\n\n        doReturn(targetDefinitionId).when(processDefinitionService).getLatestProcessDefinitionId(eq(\"target\"));\n\n        doReturn(targetProcess).when(processExecutor).start(eq(targetDefinitionId), eq(-1L), eq(0L), eq(0L),\n                any(), any(), eq(callerId), eq(-1L), eq(processInputs));\n\n        //when\n        initializingActivityWithBoundaryEventsState.afterConnectors(sProcessDefinition, sFlowNodeInstance);\n\n        //then\n        verify(stateBehaviors).updateDisplayNameAndDescription(sProcessDefinition, sFlowNodeInstance);\n        verify(stateBehaviors).updateExpectedDuration(sProcessDefinition, sFlowNodeInstance);\n        verify(stateBehaviors).addAssignmentSystemCommentIfTaskWasAutoAssign(sFlowNodeInstance);\n        verify(stateBehaviors).registerWaitingEvent(sProcessDefinition, sFlowNodeInstance);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/state/InitializingAndExecutingFlowNodeStateTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport static org.mockito.Mockito.verify;\n\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.execution.StateBehaviors;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class InitializingAndExecutingFlowNodeStateTest {\n\n    @Mock\n    private StateBehaviors stateBehaviors;\n\n    @Mock\n    private SFlowNodeInstance flowNodeInstance;\n\n    @Mock\n    private SProcessDefinition processDefinition;\n\n    @InjectMocks\n    private InitializingAndExecutingFlowNodeState initializingAndExecutingFlowNodeState;\n\n    @Test\n    public void testBeforeOnEnter() throws Exception {\n\n        // when\n        initializingAndExecutingFlowNodeState.beforeOnEnter(processDefinition, flowNodeInstance);\n\n        // then\n        verify(stateBehaviors).createData(processDefinition, flowNodeInstance);\n        verify(stateBehaviors).mapActors(flowNodeInstance, processDefinition.getProcessContainer());\n    }\n\n    @Test\n    public void testOnEnterToOnFinish() throws Exception {\n\n        // when\n        initializingAndExecutingFlowNodeState.onEnterToOnFinish(processDefinition, flowNodeInstance);\n\n        // then\n        verify(stateBehaviors).updateDisplayNameAndDescription(processDefinition, flowNodeInstance);\n\n    }\n\n    @Test\n    public void testAfterOnFinish() throws Exception {\n\n        // when\n        initializingAndExecutingFlowNodeState.afterOnFinish(processDefinition, flowNodeInstance);\n\n        // then\n        verify(stateBehaviors).updateDisplayDescriptionAfterCompletion(processDefinition, flowNodeInstance);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/state/InitializingMultiInstanceActivityStateTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.state;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyInt;\nimport static org.mockito.ArgumentMatchers.anyLong;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\n\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.MultiInstanceLoopCharacteristicsImpl;\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.process.definition.model.SActivityDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SMultiInstanceLoopCharacteristicsImpl;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance;\nimport org.bonitasoft.engine.execution.StateBehaviors;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Baptiste Mesta\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class InitializingMultiInstanceActivityStateTest {\n\n    private static final long MULTI_INSTANCE_ACTIVITY_INSTANCE_ID = 789432L;\n    private static final long FLOW_NODE_DEFINITION_ID = 765311123L;\n    @Mock\n    private SProcessDefinition processDefinition;\n    @Mock\n    private SActivityDefinition activityDefinition;\n    private SMultiInstanceActivityInstance flowNodeInstance = new SMultiInstanceActivityInstance();\n    @Mock\n    ExpressionResolverService expressionResolverService;\n    @Mock\n    ActivityInstanceService activityInstanceService;\n    @Mock\n    StateBehaviors stateBehaviors;\n    @InjectMocks\n    private InitializingMultiInstanceActivityState state;\n\n    @Before\n    public void before() throws Exception {\n        flowNodeInstance.setId(MULTI_INSTANCE_ACTIVITY_INSTANCE_ID);\n        flowNodeInstance.setFlowNodeDefinitionId(FLOW_NODE_DEFINITION_ID);\n        SFlowElementContainerDefinition flowElementContainerDefinition = mock(SFlowElementContainerDefinition.class);\n        doReturn(flowElementContainerDefinition).when(processDefinition).getProcessContainer();\n        doReturn(activityDefinition).when(flowElementContainerDefinition).getFlowNode(FLOW_NODE_DEFINITION_ID);\n    }\n\n    @Test\n    public void should_not_create_instances_with_empty_list() throws Exception {\n        //given\n        SMultiInstanceLoopCharacteristicsImpl miLoop = new SMultiInstanceLoopCharacteristicsImpl(\n                new MultiInstanceLoopCharacteristicsImpl(false, \"data\"));\n        doReturn(miLoop).when(activityDefinition)\n                .getLoopCharacteristics();\n        doReturn(0).when(stateBehaviors).getNumberOfInstancesToCreateFromInputRef(processDefinition, flowNodeInstance,\n                miLoop,\n                -1);\n        //when\n        state.execute(processDefinition, flowNodeInstance);\n        //then\n        verify(stateBehaviors, never()).createInnerInstances(anyLong(), any(SActivityDefinition.class),\n                any(SMultiInstanceActivityInstance.class), anyInt());\n    }\n\n    @Test\n    public void should_create_instances_when_list_not_empty() throws Exception {\n        //given\n        SMultiInstanceLoopCharacteristicsImpl miLoop = new SMultiInstanceLoopCharacteristicsImpl(\n                new MultiInstanceLoopCharacteristicsImpl(false, \"data\"));\n        doReturn(miLoop).when(activityDefinition)\n                .getLoopCharacteristics();\n        doReturn(1).when(stateBehaviors).getNumberOfInstancesToCreateFromInputRef(processDefinition, flowNodeInstance,\n                miLoop,\n                -1);\n        //when\n        state.execute(processDefinition, flowNodeInstance);\n        //then\n        verify(stateBehaviors).createInnerInstances(anyLong(), any(SActivityDefinition.class),\n                any(SMultiInstanceActivityInstance.class), anyInt());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/transition/AbstractTransitionEvaluatorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.transition;\n\nimport static org.mockito.BDDMockito.given;\n\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.impl.STransitionDefinitionImpl;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.junit.Before;\nimport org.mockito.Mock;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class AbstractTransitionEvaluatorTest {\n\n    @Mock\n    protected TransitionConditionEvaluator conditionEvaluator;\n\n    @Mock\n    protected DefaultTransitionGetter defaultTransitionGetter;\n\n    @Mock\n    protected SExpression trueExpression;\n\n    @Mock\n    protected SExpression falseExpression;\n\n    @Mock\n    protected SProcessDefinition processDefinition;\n\n    @Mock\n    protected SFlowNodeInstance flowNodeInstance;\n\n    @Mock\n    protected SExpressionContext context;\n\n    protected STransitionDefinition unConditionalTransition;\n\n    protected STransitionDefinition defaultTransition;\n\n    protected STransitionDefinition trueTransition1;\n\n    protected STransitionDefinition trueTransition2;\n\n    protected STransitionDefinition falseTransition;\n\n    protected STransitionDefinition nullTransition;\n\n    @Before\n    public void setUp() throws Exception {\n        unConditionalTransition = new STransitionDefinitionImpl(\"nonConditional\");\n        defaultTransition = new STransitionDefinitionImpl(\"default\");\n        trueTransition1 = buildTransition(\"trueT1\", trueExpression);\n        trueTransition2 = buildTransition(\"truT2\", trueExpression);\n        falseTransition = buildTransition(\"falseT1\", falseExpression);\n        nullTransition = buildTransition(\"falseT2\", falseExpression);\n\n        given(conditionEvaluator.evaluateCondition(trueTransition1, context)).willReturn(true);\n        given(conditionEvaluator.evaluateCondition(trueTransition2, context)).willReturn(true);\n        given(conditionEvaluator.evaluateCondition(falseTransition, context)).willReturn(false);\n        given(conditionEvaluator.evaluateCondition(nullTransition, context)).willReturn(null);\n    }\n\n    STransitionDefinition buildTransition(String name, SExpression expression) {\n        STransitionDefinitionImpl transition = new STransitionDefinitionImpl(name);\n        transition.setCondition(expression);\n        return transition;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/transition/DefaultTransitionGetterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.transition;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.fail;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\nimport org.bonitasoft.engine.commons.exceptions.SExceptionContext;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityExecutionException;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.execution.flowmerger.FlowNodeTransitionsWrapper;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class DefaultTransitionGetterTest {\n\n    private DefaultTransitionGetter defaultTransitionGetter;\n\n    @Mock\n    private SProcessDefinition processDefinition;\n\n    @Mock\n    private SFlowNodeInstance flowNodeInstance;\n\n    public static final String PROCESS_NAME = \"my proc\";\n\n    public static final String FLOW_NODE_NAME = \"gateway\";\n\n    public static final String PROCESS_VERSION = \"4.1\";\n\n    public static final long PROCESS_INSTANCE_ID = 1;\n\n    @Before\n    public void setUp() throws Exception {\n        defaultTransitionGetter = new DefaultTransitionGetter();\n\n        given(processDefinition.getName()).willReturn(PROCESS_NAME);\n        given(processDefinition.getVersion()).willReturn(PROCESS_VERSION);\n        given(flowNodeInstance.getName()).willReturn(FLOW_NODE_NAME);\n        given(flowNodeInstance.getParentProcessInstanceId()).willReturn(PROCESS_INSTANCE_ID);\n\n    }\n\n    @Test\n    public void getDefaultTransition_should_return_default_transition_from_wrapper() throws Exception {\n        //given\n        FlowNodeTransitionsWrapper wrapper = new FlowNodeTransitionsWrapper();\n        STransitionDefinition transition = mock(STransitionDefinition.class);\n        wrapper.setDefaultTransition(transition);\n\n        //when\n        STransitionDefinition defaultTransition = defaultTransitionGetter.getDefaultTransition(wrapper,\n                processDefinition, flowNodeInstance);\n\n        //then\n        assertThat(defaultTransition).isEqualTo(transition);\n    }\n\n    @Test\n    public void getDefaultTransition_should_throw_SActivityExecutionException_when_default_transition_is_not_set()\n            throws Exception {\n        //given\n        FlowNodeTransitionsWrapper wrapper = new FlowNodeTransitionsWrapper();\n\n        try {\n            //when\n            defaultTransitionGetter.getDefaultTransition(wrapper, processDefinition, flowNodeInstance);\n            fail(\"Exception expected\");\n        } catch (SActivityExecutionException e) {\n            //then\n            assertThat(e.getMessage()).contains(\"There is no default transition on \" + FLOW_NODE_NAME\n                    + \", but no outgoing transition had a valid condition.\");\n            assertThat(e.getContext().get(SExceptionContext.PROCESS_NAME)).isEqualTo(PROCESS_NAME);\n            assertThat(e.getContext().get(SExceptionContext.PROCESS_VERSION)).isEqualTo(PROCESS_VERSION);\n            assertThat(e.getContext().get(SExceptionContext.PROCESS_INSTANCE_ID)).isEqualTo(PROCESS_INSTANCE_ID);\n\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/transition/ExclusiveGatewayTransitionEvaluationStrategyTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.transition;\n\nimport static org.assertj.core.api.Assertions.*;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class ExclusiveGatewayTransitionEvaluationStrategyTest {\n\n    private ExclusiveGatewayTransitionEvaluationStrategy strategy;\n\n    @Before\n    public void setUp() throws Exception {\n        strategy = new ExclusiveGatewayTransitionEvaluationStrategy();\n    }\n\n    @Test\n    public void shouldContinue_should_return_true_when_not_found() throws Exception {\n        //when\n        boolean shouldContinue = strategy.shouldContinue(false);\n\n        //then\n        assertThat(shouldContinue).isTrue();\n    }\n\n    @Test\n    public void shouldContinue_should_return_false_when_found() throws Exception {\n        //when\n        boolean shouldContinue = strategy.shouldContinue(true);\n\n        //then\n        assertThat(shouldContinue).isFalse();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/transition/ImplicitGatewayTransitionEvaluatorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.transition;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\nimport org.bonitasoft.engine.execution.flowmerger.FlowNodeTransitionsWrapper;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.Silent.class)\npublic class ImplicitGatewayTransitionEvaluatorTest extends AbstractTransitionEvaluatorTest {\n\n    @InjectMocks\n    private ImplicitGatewayTransitionEvaluator implicitGatewayTransitionEvaluator;\n\n    @Test\n    public void evaluateTransitions_should_return_all_unconditional_transitions_and_transitions_evaluated_to_true()\n            throws Exception {\n        //given\n        FlowNodeTransitionsWrapper transitions = new FlowNodeTransitionsWrapper();\n        transitions.setAllOutgoingTransitionDefinitions(\n                new ArrayList<STransitionDefinition>(Arrays.asList(unConditionalTransition, trueTransition1,\n                        trueTransition2, falseTransition, nullTransition)));\n\n        //when\n        List<STransitionDefinition> chosenTransitions = implicitGatewayTransitionEvaluator\n                .evaluateTransitions(processDefinition, flowNodeInstance, transitions, context);\n\n        //then\n        assertThat(chosenTransitions).containsExactly(unConditionalTransition, trueTransition1, trueTransition2);\n    }\n\n    @Test\n    public void evaluateTransitions_should_return_unconditional_transitions_and_default_transition_when_all_conditional_transitions_are_evaluated_to_false()\n            throws Exception {\n        //given\n        FlowNodeTransitionsWrapper transitions = new FlowNodeTransitionsWrapper();\n        transitions.setAllOutgoingTransitionDefinitions(new ArrayList<STransitionDefinition>(\n                Arrays.asList(unConditionalTransition, falseTransition, nullTransition)));\n        given(defaultTransitionGetter.getDefaultTransition(transitions, processDefinition, flowNodeInstance))\n                .willReturn(defaultTransition);\n\n        //when\n        List<STransitionDefinition> chosenTransitions = implicitGatewayTransitionEvaluator\n                .evaluateTransitions(processDefinition, flowNodeInstance, transitions, context);\n\n        //then\n        assertThat(chosenTransitions).containsExactly(unConditionalTransition, defaultTransition);\n    }\n\n    @Test\n    public void evaluateTransitions_should_not_return_default_transition_when_there_are_no_conditional_transitions()\n            throws Exception {\n        //given\n        FlowNodeTransitionsWrapper transitions = new FlowNodeTransitionsWrapper();\n        transitions.setAllOutgoingTransitionDefinitions(\n                new ArrayList<STransitionDefinition>(Arrays.asList(unConditionalTransition)));\n        given(defaultTransitionGetter.getDefaultTransition(transitions, processDefinition, flowNodeInstance))\n                .willReturn(defaultTransition);\n\n        //when\n        List<STransitionDefinition> chosenTransitions = implicitGatewayTransitionEvaluator\n                .evaluateTransitions(processDefinition, flowNodeInstance, transitions, context);\n\n        //then\n        assertThat(chosenTransitions).containsExactly(unConditionalTransition);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/transition/InclusiveExclusiveTransitionEvaluatorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.transition;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.impl.STransitionDefinitionImpl;\nimport org.bonitasoft.engine.execution.flowmerger.FlowNodeTransitionsWrapper;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class InclusiveExclusiveTransitionEvaluatorTest extends AbstractTransitionEvaluatorTest {\n\n    private InclusiveGatewayTransitionEvaluationStrategy inclusiveStrategy;\n\n    private ExclusiveGatewayTransitionEvaluationStrategy exclusiveStrategy;\n\n    private InclusiveExclusiveTransitionEvaluator inclusiveTransitionEvaluator;\n\n    private InclusiveExclusiveTransitionEvaluator exclusiveTransitionEvaluator;\n\n    @Before\n    public void setUp() throws Exception {\n        super.setUp();\n        inclusiveStrategy = new InclusiveGatewayTransitionEvaluationStrategy();\n        exclusiveStrategy = new ExclusiveGatewayTransitionEvaluationStrategy();\n        inclusiveTransitionEvaluator = new InclusiveExclusiveTransitionEvaluator(inclusiveStrategy, conditionEvaluator,\n                defaultTransitionGetter);\n        exclusiveTransitionEvaluator = new InclusiveExclusiveTransitionEvaluator(exclusiveStrategy, conditionEvaluator,\n                defaultTransitionGetter);\n    }\n\n    STransitionDefinition buildTransition(String name, SExpression expression) {\n        STransitionDefinitionImpl transition = new STransitionDefinitionImpl(name);\n        transition.setCondition(expression);\n        return transition;\n    }\n\n    @Test\n    public void evaluateTransitions_should_return_all_transitions_without_conditions_or_with_true_conditions_when_inclusive_strategy_is_used()\n            throws Exception {\n        //given\n        FlowNodeTransitionsWrapper transitions = new FlowNodeTransitionsWrapper();\n        transitions.setAllOutgoingTransitionDefinitions(\n                Arrays.asList(unConditionalTransition, trueTransition1, trueTransition2, falseTransition,\n                        nullTransition));\n\n        //when\n        List<STransitionDefinition> chosenTransitions = inclusiveTransitionEvaluator.evaluateTransitions(\n                processDefinition, flowNodeInstance,\n                transitions, context);\n\n        //then\n        assertThat(chosenTransitions).containsExactly(unConditionalTransition, trueTransition1, trueTransition2);\n    }\n\n    @Test\n    public void evaluateTransitions_should_return_only_the_first_transition_with_true_condition_when_exclusive_strategy_is_used()\n            throws Exception {\n        //given\n        FlowNodeTransitionsWrapper transitions = new FlowNodeTransitionsWrapper();\n        transitions.setAllOutgoingTransitionDefinitions(\n                Arrays.asList(trueTransition1, trueTransition2, falseTransition, nullTransition));\n\n        //when\n        List<STransitionDefinition> chosenTransitions = exclusiveTransitionEvaluator.evaluateTransitions(\n                processDefinition, flowNodeInstance,\n                transitions, context);\n\n        //then\n        assertThat(chosenTransitions).containsExactly(trueTransition1);\n    }\n\n    @Test\n    public void evaluateTransitions_should_return_default_transition_when_all_conditional_transitions_are_evaluated_to_false_when_inclusive_strategy_is_used()\n            throws Exception {\n        //given\n        FlowNodeTransitionsWrapper transitions = new FlowNodeTransitionsWrapper();\n        transitions\n                .setAllOutgoingTransitionDefinitions(new ArrayList<STransitionDefinition>(Arrays.asList(falseTransition,\n                        nullTransition)));\n        given(defaultTransitionGetter.getDefaultTransition(transitions, processDefinition, flowNodeInstance))\n                .willReturn(defaultTransition);\n\n        //when\n        List<STransitionDefinition> chosenTransitions = inclusiveTransitionEvaluator.evaluateTransitions(\n                processDefinition, flowNodeInstance,\n                transitions, context);\n\n        //then\n        assertThat(chosenTransitions).containsExactly(defaultTransition);\n    }\n\n    @Test\n    public void evaluateTransitions_should_return_default_transition_when_all_conditional_transitions_are_evaluated_to_false_when_exclusive_strategy_is_used()\n            throws Exception {\n        //given\n        FlowNodeTransitionsWrapper transitions = new FlowNodeTransitionsWrapper();\n        transitions.setAllOutgoingTransitionDefinitions(\n                new ArrayList<STransitionDefinition>(Arrays.asList(falseTransition, nullTransition)));\n        given(defaultTransitionGetter.getDefaultTransition(transitions, processDefinition, flowNodeInstance))\n                .willReturn(defaultTransition);\n\n        //when\n        List<STransitionDefinition> chosenTransitions = exclusiveTransitionEvaluator.evaluateTransitions(\n                processDefinition, flowNodeInstance,\n                transitions, context);\n\n        //then\n        assertThat(chosenTransitions).containsExactly(defaultTransition);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/transition/InclusiveGatewayTransitionEvaluationStrategyTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.transition;\n\nimport static org.assertj.core.api.Assertions.*;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class InclusiveGatewayTransitionEvaluationStrategyTest {\n\n    private InclusiveGatewayTransitionEvaluationStrategy strategy;\n\n    @Before\n    public void setUp() throws Exception {\n        strategy = new InclusiveGatewayTransitionEvaluationStrategy();\n    }\n\n    @Test\n    public void shouldContinue_should_return_true_when_not_found() throws Exception {\n        //when\n        boolean shouldContinue = strategy.shouldContinue(false);\n\n        //then\n        assertThat(shouldContinue).isTrue();\n    }\n\n    @Test\n    public void shouldContinue_should_return_true_when_found() throws Exception {\n        //when\n        boolean shouldContinue = strategy.shouldContinue(true);\n\n        //then\n        assertThat(shouldContinue).isTrue();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/transition/ParallelGatewayTransitionEvaluatorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.transition;\n\nimport static org.assertj.core.api.Assertions.*;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\nimport org.bonitasoft.engine.execution.flowmerger.FlowNodeTransitionsWrapper;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.Silent.class)\npublic class ParallelGatewayTransitionEvaluatorTest extends AbstractTransitionEvaluatorTest {\n\n    @InjectMocks\n    private ParallelGatewayTransitionEvaluator parallelGatewayTransitionEvaluator;\n\n    @Test // it's not possible to add conditions on transition coming from parallel gateway at design time\n    // so it's not necessary to evaluate them\n    public void evaluateTransitions_should_return_all_transitions() throws Exception {\n        //given\n        FlowNodeTransitionsWrapper transitions = new FlowNodeTransitionsWrapper();\n        transitions.setAllOutgoingTransitionDefinitions(new ArrayList<STransitionDefinition>(Arrays\n                .asList(unConditionalTransition, trueTransition1, trueTransition2, falseTransition, nullTransition)));\n\n        //when\n        List<STransitionDefinition> chosenTransitions = parallelGatewayTransitionEvaluator\n                .evaluateTransitions(transitions);\n\n        //then\n        assertThat(chosenTransitions).containsExactly(unConditionalTransition, trueTransition1, trueTransition2,\n                falseTransition, nullTransition);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/transition/TransitionConditionEvaluatorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.transition;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\nimport java.util.Collections;\n\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.process.definition.model.impl.STransitionDefinitionImpl;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.expression.model.impl.SExpressionImpl;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class TransitionConditionEvaluatorTest {\n\n    @Mock\n    private ExpressionResolverService expressionResolverService;\n\n    @InjectMocks\n    private TransitionConditionEvaluator evaluator;\n\n    @Mock\n    private SExpressionContext context;\n\n    @Test\n    public void evaluateCondition_should_return_result_of_expressionResolverService_evaluate() throws Exception {\n        //given\n        STransitionDefinitionImpl transition = new STransitionDefinitionImpl(\"t1\");\n        SExpression condition = buildBooleanExpression(true);\n        transition.setCondition(condition);\n        given(expressionResolverService.evaluate(condition, context)).willReturn(true);\n\n        //when\n        Boolean value = evaluator.evaluateCondition(transition, context);\n\n        //then\n        assertThat(value).isTrue();\n    }\n\n    @Test\n    public void evaluateCondition_should_return_null_when_there_is_no_condition() throws Exception {\n        //given\n        STransitionDefinitionImpl transition = new STransitionDefinitionImpl(\"t1\");\n\n        //when\n        Boolean value = evaluator.evaluateCondition(transition, context);\n\n        //then\n        assertThat(value).isNull();\n\n        verifyNoInteractions(expressionResolverService);\n    }\n\n    @Test\n    public void evaluateCondition_should_throw_STransitionConditionEvaluationException_when_expression_return_type_is_not_a_boolean()\n            throws Exception {\n        //given\n        SExpression condition = mock(SExpression.class);\n        given(condition.getReturnType()).willReturn(String.class.getName());\n        given(condition.getName()).willReturn(\"isTrue\");\n        STransitionDefinitionImpl transition = new STransitionDefinitionImpl(\"t1\");\n        transition.setCondition(condition);\n\n        var exception = assertThrows(STransitionConditionEvaluationException.class, () -> {\n            evaluator.evaluateCondition(transition, context);\n        });\n\n        //then\n        assertThat(exception).hasMessage(\"TRANSITION_NAME=t1 | Condition expression must return a boolean\");\n        assertThat(exception.getCause()).isInstanceOf(SExpressionEvaluationException.class);\n        var cause = (SExpressionEvaluationException) exception.getCause();\n        assertThat(cause.getExpressionName()).isEqualTo(\"isTrue\");\n\n        verifyNoInteractions(expressionResolverService);\n    }\n\n    private SExpression buildBooleanExpression(boolean value) {\n        final SExpressionImpl expression = new SExpressionImpl();\n        String strValue = String.valueOf(value);\n        expression.setName(strValue);\n        expression.setContent(strValue);\n        expression.setExpressionType(SExpression.TYPE_CONSTANT);\n        expression.setReturnType(Boolean.class.getName());\n        expression.setDependencies(Collections.<SExpression> emptyList());\n        return expression;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/work/BPMWorkFactoryTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work;\n\nimport static java.util.Collections.emptyList;\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.Arrays;\nimport java.util.Map;\nimport java.util.concurrent.CompletableFuture;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.core.process.definition.model.event.impl.SStartEventDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SFlowElementContainerDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SProcessDefinitionImpl;\nimport org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SBPMEventType;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent;\nimport org.bonitasoft.engine.execution.FlowNodeNameFilter;\nimport org.bonitasoft.engine.execution.FlowNodeSelector;\nimport org.bonitasoft.engine.execution.work.failurewrapping.FlowNodeDefinitionAndInstanceContextWork;\nimport org.bonitasoft.engine.execution.work.failurewrapping.MessageInstanceContextWork;\nimport org.bonitasoft.engine.execution.work.failurewrapping.ProcessDefinitionContextWork;\nimport org.bonitasoft.engine.execution.work.failurewrapping.ProcessInstanceContextWork;\nimport org.bonitasoft.engine.execution.work.failurewrapping.TriggerSignalWork;\nimport org.bonitasoft.engine.work.BonitaWork;\nimport org.bonitasoft.engine.work.WorkDescriptor;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Aurelien Pupier\n * @author Celine Souchet\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class BPMWorkFactoryTest {\n\n    @InjectMocks\n    private BPMWorkFactory workFactory;\n\n    @Test\n    public void createExecuteMessageCoupleWorkHasNoLockProcessInstanceWorkIfNoTargetProcess() {\n        SWaitingMessageEvent waitingMessageEvent = createWaitingMessage();\n        SMessageInstance messageInstance = createMessageInstance();\n        final WrappingBonitaWork work = (WrappingBonitaWork) workFactory\n                .create(workFactory.createExecuteMessageCoupleWorkDescriptor(messageInstance, waitingMessageEvent));\n        final boolean containsLockProcessInstance = containsLockProcessInstanceWork(work);\n        Assert.assertFalse(\"A lock Process Instance Work is used although there is no Target process\",\n                containsLockProcessInstance);\n    }\n\n    @Test\n    public void createExecuteMessageCoupleWorkWithLockProcessInstanceWork() {\n        SWaitingMessageEvent waitingMessageEvent = createWaitingMessage();\n        waitingMessageEvent.setParentProcessInstanceId(5L);\n        SMessageInstance messageInstance = createMessageInstance();\n        final WrappingBonitaWork work = (WrappingBonitaWork) workFactory\n                .create(workFactory.createExecuteMessageCoupleWorkDescriptor(messageInstance, waitingMessageEvent));\n        final boolean containsLockProcessInstance = containsLockProcessInstanceWork(work);\n        Assert.assertTrue(\"A lock Process Instance Work is missing although there is a Target process\",\n                containsLockProcessInstance);\n    }\n\n    @Test\n    public void createExecuteMessageCoupleWork() {\n        SWaitingMessageEvent waitingMessageEvent = createWaitingMessage();\n        SMessageInstance messageInstance = createMessageInstance();\n        final WrappingBonitaWork work = (WrappingBonitaWork) workFactory\n                .create(workFactory.createExecuteMessageCoupleWorkDescriptor(messageInstance, waitingMessageEvent));\n        Assert.assertTrue(\"A MessageInstanceContextWork is missing\", containsFailureHandlingMessageInstance(work));\n        Assert.assertTrue(\"A FailureHandlingProcessDefinitionCOntextWork is missing\",\n                containsFailureHandlingProcessDefinition(work));\n    }\n\n    private SMessageInstance createMessageInstance() {\n        SMessageInstance sMessageInstance = new SMessageInstance(\"message\", \"myProcess\", \"flowNode\", 1L,\n                \"throwFlowNode\");\n        sMessageInstance.setId(543L);\n        return sMessageInstance;\n    }\n\n    private SWaitingMessageEvent createWaitingMessage() {\n        SWaitingMessageEvent sWaitingMessageEvent = new SWaitingMessageEvent(SBPMEventType.INTERMEDIATE_CATCH_EVENT, 1L,\n                \"myProcess\", 2, \"flowNode\", \"message\");\n        sWaitingMessageEvent.setId(1234L);\n        return sWaitingMessageEvent;\n    }\n\n    @Test\n    public void createExecuteFlowNode() {\n        SAutomaticTaskInstance flowNodeInstance = new SAutomaticTaskInstance(\"task\", 5432L, 631L, 52311, 33L, 441L);\n        flowNodeInstance.setLogicalGroup4(3452L);\n        final WrappingBonitaWork work = (WrappingBonitaWork) workFactory\n                .create(workFactory.createExecuteFlowNodeWorkDescriptor(flowNodeInstance));\n        Assert.assertTrue(\"A ProcessDefinitionContextWork is missing\", containsFailureHandlingProcessDefinition(work));\n        Assert.assertTrue(\"A ProcessInstanceContextWork is missing\", containsFailureHandlingProcessInstance(work));\n        Assert.assertTrue(\"A ProcessInstanceContextWork is missing\", containsFailureHandlingFlowNodeInstance(work));\n    }\n\n    @Test\n    public void createExecuteConnectorOfProcess() {\n        final WrappingBonitaWork work = (WrappingBonitaWork) workFactory.create(workFactory\n                .createExecuteConnectorOfProcessDescriptor(1L, 2L, 4L, 3L, \"groovy\", \"connectorDefName\",\n                        ConnectorEvent.ON_ENTER,\n                        null));\n        Assert.assertTrue(\"A ProcessDefinitionContextWork is missing\", containsFailureHandlingProcessDefinition(work));\n    }\n\n    @Test\n    public void createExecuteConnectorOfProcessWithFlowNodeSelector() {\n        SProcessDefinitionImpl definition = new SProcessDefinitionImpl(\"name\", \"version\");\n        SFlowElementContainerDefinitionImpl processContainer = new SFlowElementContainerDefinitionImpl();\n        definition.setProcessContainer(processContainer);\n        SStartEventDefinitionImpl start1 = new SStartEventDefinitionImpl(1L, \"start1\");\n        SStartEventDefinitionImpl start2 = new SStartEventDefinitionImpl(2L, \"start2\");\n        SStartEventDefinitionImpl start3 = new SStartEventDefinitionImpl(3L, \"start3\");\n        processContainer.addEvent(start1);\n        processContainer.addEvent(start2);\n        processContainer.addEvent(start3);\n        FlowNodeSelector flowNodeSelector = new FlowNodeSelector(definition,\n                new FlowNodeNameFilter(Arrays.asList(\"start1\", \"start2\")));\n\n        final WrappingBonitaWork work = (WrappingBonitaWork) workFactory.create(workFactory\n                .createExecuteConnectorOfProcessDescriptor(1L, 2L, 4L, 3L, \"groovy\", \"connectorDefName\",\n                        ConnectorEvent.ON_ENTER,\n                        flowNodeSelector));\n\n        assertThat(getWorkOfClass(work, ExecuteConnectorOfProcess.class).filterFlowNodeDefinitions.mustSelect(start1))\n                .isTrue();\n        assertThat(getWorkOfClass(work, ExecuteConnectorOfProcess.class).filterFlowNodeDefinitions.mustSelect(start2))\n                .isTrue();\n        assertThat(getWorkOfClass(work, ExecuteConnectorOfProcess.class).filterFlowNodeDefinitions.mustSelect(start3))\n                .isFalse();\n    }\n\n    @Test\n    public void should_be_able_to_create_execute_connector_work_with_empty_flownode_selector() {\n        SProcessDefinitionImpl definition = new SProcessDefinitionImpl(\"name\", \"version\");\n        SFlowElementContainerDefinitionImpl processContainer = new SFlowElementContainerDefinitionImpl();\n        definition.setProcessContainer(processContainer);\n        SStartEventDefinitionImpl start1 = new SStartEventDefinitionImpl(1L, \"start1\");\n        processContainer.addEvent(start1);\n        FlowNodeSelector flowNodeSelector = new FlowNodeSelector(definition,\n                new FlowNodeNameFilter(emptyList()));\n\n        final WrappingBonitaWork work = (WrappingBonitaWork) workFactory.create(workFactory\n                .createExecuteConnectorOfProcessDescriptor(1L, 2L, 4L, 3L, \"groovy\", \"connectorDefName\",\n                        ConnectorEvent.ON_ENTER,\n                        flowNodeSelector));\n\n        assertThat(getWorkOfClass(work, ExecuteConnectorOfProcess.class).filterFlowNodeDefinitions.mustSelect(start1))\n                .isFalse();\n    }\n\n    @Test\n    public void createExecuteConnectorOfActivity() {\n        final WrappingBonitaWork work = (WrappingBonitaWork) workFactory\n                .create(workFactory.createExecuteConnectorOfActivityDescriptor(1L, 3L, 3L, 4L, 5L, 6,\n                        \"groovy\",\n                        \"connectorDefName\",\n                        \"ON_ENTER\"));\n\n        Assert.assertTrue(\"A ProcessDefinitionContextWork is missing\", containsFailureHandlingProcessDefinition(work));\n        Assert.assertTrue(\"A ProcessInstanceContextWork is missing\", containsFailureHandlingProcessInstance(work));\n    }\n\n    @Test(expected = IllegalStateException.class)\n    public void createExecuteConnectorOfActivity_with_missing_parameter() {\n        workFactory.create(WorkDescriptor.create(\"EXECUTE_ACTIVITY_CONNECTOR\"));\n    }\n\n    @Test\n    public void createNotifyChildFinishedWork() {\n        final WrappingBonitaWork work = (WrappingBonitaWork) workFactory\n                .create(workFactory.createNotifyChildFinishedWorkDescriptor(new SAutomaticTaskInstance()));\n        Assert.assertTrue(\"A ProcessDefinitionContextWork is missing\", containsFailureHandlingProcessDefinition(work));\n        Assert.assertTrue(\"A ProcessInstanceContextWork is missing\", containsFailureHandlingProcessInstance(work));\n        Assert.assertTrue(\"A ProcessInstanceContextWork is missing\", containsFailureHandlingFlowNodeInstance(work));\n    }\n\n    @Test\n    public void createTriggerSignalWork() {\n        SWaitingSignalEvent listeningSignal = new SWaitingSignalEvent(SBPMEventType.INTERMEDIATE_CATCH_EVENT, 1L,\n                \"myProcess\", 2L, \"myFlowNode\", \"mySignal\");\n        listeningSignal.setId(123L);\n        WrappingBonitaWork work = (WrappingBonitaWork) workFactory\n                .create(workFactory.createTriggerSignalWorkDescriptor(listeningSignal));\n\n        assertThat(getWorkOfClass(work, TriggerSignalWork.class).getDescription()).contains(\"waitingSignalEvent: 123\");\n        assertThat(containsLockProcessInstanceWork(work)).isFalse();\n    }\n\n    @Test\n    public void createTriggerSignalWork_in_a_process_instance() {\n        SWaitingSignalEvent listeningSignal = new SWaitingSignalEvent(SBPMEventType.INTERMEDIATE_CATCH_EVENT, 1L,\n                \"myProcess\", 2L, \"myFlowNode\", \"mySignal\");\n        listeningSignal.setId(123L);\n        listeningSignal.setParentProcessInstanceId(456L);\n        WrappingBonitaWork work = (WrappingBonitaWork) workFactory\n                .create(workFactory.createTriggerSignalWorkDescriptor(listeningSignal));\n\n        assertThat(getWorkOfClass(work, TriggerSignalWork.class).getDescription()).contains(\"waitingSignalEvent: 123\");\n        assertThat(getWorkOfClass(work, LockProcessInstanceWork.class).processInstanceId).isEqualTo(456L);\n    }\n\n    private boolean containsFailureHandlingFlowNodeInstance(BonitaWork work) {\n        return containsWorkOfClass(work, FlowNodeDefinitionAndInstanceContextWork.class);\n    }\n\n    private boolean containsFailureHandlingProcessInstance(BonitaWork work) {\n        return containsWorkOfClass(work, ProcessInstanceContextWork.class);\n    }\n\n    private boolean containsFailureHandlingProcessDefinition(BonitaWork work) {\n        return containsWorkOfClass(work, ProcessDefinitionContextWork.class);\n    }\n\n    private boolean containsFailureHandlingMessageInstance(BonitaWork work) {\n        return containsWorkOfClass(work, MessageInstanceContextWork.class);\n    }\n\n    private boolean containsLockProcessInstanceWork(BonitaWork work) {\n        return containsWorkOfClass(work, LockProcessInstanceWork.class);\n    }\n\n    private boolean containsWorkOfClass(BonitaWork work, final Class<?> clazz) {\n        return getWorkOfClass(work, clazz) != null;\n    }\n\n    private <T> T getWorkOfClass(BonitaWork work, Class<T> clazz) {\n        if (clazz.isAssignableFrom(work.getClass())) {\n            return ((T) work);\n        }\n        if (!(work instanceof WrappingBonitaWork wrappingBonitaWork)) {\n            return null;\n        }\n        return getWorkOfClass(wrappingBonitaWork.getWrappedWork(), clazz);\n    }\n\n    @Test\n    public void should_create_work_using_extensions() {\n        BonitaWork bonitaWork = new BonitaWork() {\n\n            @Override\n            public String getDescription() {\n                return null;\n            }\n\n            @Override\n            public CompletableFuture<Void> work(Map<String, Object> context) {\n                return CompletableFuture.completedFuture(null);\n            }\n\n            @Override\n            public void handleFailure(Throwable e, Map<String, Object> context) {\n            }\n        };\n        workFactory.addExtension(\"MyWork\", workDescriptor -> bonitaWork);\n\n        assertThat(workFactory.create(WorkDescriptor.create(\"MyWork\"))).isEqualTo(bonitaWork);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/work/ExecuteConnectorOfActivityTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.when;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.core.process.definition.model.event.SEndEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.impl.SEndEventDefinitionImpl;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.STaskPriority;\nimport org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SThrowEventInstance;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ExecuteConnectorOfActivityTest {\n\n    private Map<String, Object> context;\n\n    @Mock\n    private ServiceAccessor accessor;\n\n    @Mock\n    private ActivityInstanceService activityInstanceService;\n\n    @Mock\n    private EventInstanceService eventService;\n\n    @Before\n    public void setUp() {\n        context = new HashMap<String, Object>();\n        context.put(TenantAwareBonitaWork.SERVICE_ACCESSOR, accessor);\n\n        when(accessor.getActivityInstanceService()).thenReturn(activityInstanceService);\n        when(accessor.getEventInstanceService()).thenReturn(eventService);\n    }\n\n    @Test\n    public void createThrowErrorEventInstance_should_return_a_well_formed_instance() throws Exception {\n        final ExecuteConnectorOfActivity work = new ExecuteConnectorOfActivity(4L, 123L, 45L, 687L, 5357L,\n                \"myConnector\");\n        final SEndEventDefinition eventDefinition = new SEndEventDefinitionImpl(8687L, \"end\");\n        final SUserTaskInstance instanceImpl = new SUserTaskInstance(\"userTask1\", 1L, 2L, 3L, 4L, STaskPriority.NORMAL,\n                5L, 6L);\n        instanceImpl.setLogicalGroup(3, 4L);\n        when(activityInstanceService.getFlowNodeInstance(687L)).thenReturn(instanceImpl);\n\n        final SThrowEventInstance errorEventInstance = work.createThrowErrorEventInstance(context, eventDefinition);\n\n        assertThat(errorEventInstance.getParentProcessInstanceId()).isEqualTo(4L);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/work/ExecuteConnectorWorkTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work;\n\nimport static java.util.concurrent.CompletableFuture.completedFuture;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.CompletableFuture;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.connector.exception.SConnectorException;\nimport org.bonitasoft.engine.core.connector.ConnectorInstanceService;\nimport org.bonitasoft.engine.core.connector.ConnectorResult;\nimport org.bonitasoft.engine.core.connector.ConnectorService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SConnectorDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SEndEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SConnectorDefinitionImpl;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowElementsContainerType;\nimport org.bonitasoft.engine.core.process.instance.model.event.SThrowEventInstance;\nimport org.bonitasoft.engine.lock.BonitaLock;\nimport org.bonitasoft.engine.lock.LockService;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.tracking.TimeTracker;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InOrder;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Baptiste Mesta\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class ExecuteConnectorWorkTest {\n\n    private interface EvaluateOutputCallable {\n\n        void call() throws SBonitaException;\n    }\n\n    private static final long PROCESS_DEFINITION_ID = 154323L;\n    private static final long CONNECTOR_INSTANCE_ID = 95043L;\n    private static final String CONNECTOR_NAME = \"MyConnector\";\n    private static final long PROCESS_INSTANCE_ID = 92347844L;\n    private static final long TENANT_ID = 125L;\n\n    @Mock\n    private ServiceAccessor serviceAccessor;\n    @Mock\n    private LockService lockService;\n    @Mock\n    private UserTransactionService userTransactionService;\n    @Mock\n    private ConnectorInstanceService connectorInstanceService;\n    private final SConnectorDefinition sConnectorDefinition = new SConnectorDefinitionImpl(\"testDef\", \"connectorDef\",\n            \"1.0\",\n            ConnectorEvent.ON_ENTER);\n    private EvaluateOutputCallable evaluateOutput = () -> {\n    };\n    private final SExpressionContext expressionContext = new SExpressionContext(PROCESS_INSTANCE_ID, \"PROCESS\",\n            PROCESS_DEFINITION_ID);\n    private final Map<String, Object> workContext = new HashMap<>();\n    private final ExecuteConnectorWork executeConnectorWork = new ExecuteConnectorWork(PROCESS_DEFINITION_ID,\n            CONNECTOR_INSTANCE_ID, CONNECTOR_NAME, expressionContext, PROCESS_INSTANCE_ID) {\n\n        @Override\n        protected void errorEventOnFail(Map<String, Object> context, SConnectorDefinition sConnectorDefinition,\n                Throwable Exception) {\n        }\n\n        @Override\n        protected SThrowEventInstance createThrowErrorEventInstance(Map<String, Object> context,\n                SEndEventDefinition eventDefinition) {\n            return null;\n        }\n\n        @Override\n        protected SConnectorDefinition getSConnectorDefinition(ProcessDefinitionService processDefinitionService) {\n            return sConnectorDefinition;\n        }\n\n        @Override\n        protected void setContainerInFail(Map<String, Object> context, Throwable t) {\n        }\n\n        @Override\n        protected void continueFlow(Map<String, Object> context) {\n        }\n\n        @Override\n        protected void evaluateOutput(Map<String, Object> context, ConnectorResult result,\n                SConnectorDefinition sConnectorDefinition) throws SBonitaException {\n            evaluateOutput.call();\n        }\n\n        @Override\n        public String getDescription() {\n            return null;\n        }\n    };\n    @Mock\n    private ClassLoaderService classLoaderService;\n    @Mock\n    private TimeTracker timeTracker;\n    @Mock\n    private ConnectorService connectorService;\n\n    @Before\n    public void before() throws Exception {\n        workContext.put(TenantAwareBonitaWork.SERVICE_ACCESSOR, serviceAccessor);\n        doReturn(lockService).when(serviceAccessor).getLockService();\n        doReturn(userTransactionService).when(serviceAccessor).getUserTransactionService();\n        doAnswer(args -> ((Callable) args.getArgument(0)).call()).when(userTransactionService)\n                .executeInTransaction(any());\n        doReturn(classLoaderService).when(serviceAccessor).getClassLoaderService();\n        doReturn(this.getClass().getClassLoader()).when(classLoaderService).getClassLoader(any());\n        doReturn(timeTracker).when(serviceAccessor).getTimeTracker();\n        doReturn(connectorService).when(serviceAccessor).getConnectorService();\n        doReturn(connectorInstanceService).when(serviceAccessor).getConnectorInstanceService();\n    }\n\n    @Test\n    public void should_execute_do_operations_in_a_lock() throws Exception {\n        //given\n        CompletableFuture<ConnectorResult> toBeReturned = completedFuture(\n                new ConnectorResult(null, Collections.emptyMap(), 100));\n        when(connectorService.executeConnector(anyLong(), any(), any(), any(), any())).thenReturn(toBeReturned);\n\n        //when\n        executeConnectorWork.work(workContext);\n\n        //then\n        InOrder inOrder = inOrder(lockService, userTransactionService);\n        inOrder.verify(lockService).lock(eq(PROCESS_INSTANCE_ID), eq(SFlowElementsContainerType.PROCESS.name()));\n        inOrder.verify(userTransactionService).executeInTransaction(any());\n        inOrder.verify(lockService).unlock(nullable(BonitaLock.class));\n    }\n\n    @Test\n    public void should_throw_SConnectorException_when_an_error_occurs_while_evaluating_output_operations()\n            throws Exception {\n        //We need to have an SConnectorException thrown when evaluating output operation because we don't want connectors to be automatically retried\n        evaluateOutput = () -> {\n            throw new SOperationExecutionException(\"Unable to save some connector output ¯\\\\_(ツ)_/¯\");\n        };\n        CompletableFuture<ConnectorResult> toBeReturned = completedFuture(\n                new ConnectorResult(null, Collections.emptyMap(), 100));\n        when(connectorService.executeConnector(anyLong(), any(), any(), any(), any())).thenReturn(toBeReturned);\n\n        CompletableFuture<Void> work = executeConnectorWork.work(workContext);\n\n        assertThatThrownBy(work::get).satisfies(exception -> {\n            assertThat(exception).hasCauseInstanceOf(SConnectorException.class);\n            assertThat(exception.getCause())\n                    .hasMessageContaining(\"Unable to evaluate output operations and continue flow for connector\");\n            assertThat(exception.getCause()).hasCauseInstanceOf(SOperationExecutionException.class);\n        });\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/work/ExecuteFlowNodeWorkTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work;\n\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.verify;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance;\nimport org.bonitasoft.engine.execution.FlowNodeExecutor;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.work.SWorkPreconditionException;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Baptiste Mesta\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class ExecuteFlowNodeWorkTest {\n\n    public static final long FLOW_NODE_INSTANCE_ID = 564325L;\n    public static final long PROCESS_INSTANCE_ID = 34552L;\n    @Mock\n    private ServiceAccessor serviceAccessor;\n    @Mock\n    private ActivityInstanceService activityInstanceService;\n    @Mock\n    private FlowNodeExecutor flowNodeExecutor;\n    private SUserTaskInstance sHumanTaskInstance;\n    private Map<String, Object> context;\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Before\n    public void before() throws Exception {\n        context = new HashMap<>();\n        context.put(TenantAwareBonitaWork.SERVICE_ACCESSOR, serviceAccessor);\n        doReturn(activityInstanceService).when(serviceAccessor).getActivityInstanceService();\n        doReturn(flowNodeExecutor).when(serviceAccessor).getFlowNodeExecutor();\n        sHumanTaskInstance = new SUserTaskInstance();\n        sHumanTaskInstance.setId(FLOW_NODE_INSTANCE_ID);\n        doReturn(sHumanTaskInstance).when(activityInstanceService).getFlowNodeInstance(FLOW_NODE_INSTANCE_ID);\n    }\n\n    @Test\n    public void should_throw_exception_if_executing_human_task_not_ready_when_isReadyHumanTask_flag_is_true()\n            throws Exception {\n        //given\n        ExecuteFlowNodeWork executeFlowNodeWork = new ExecuteFlowNodeWork(FLOW_NODE_INSTANCE_ID, 4, false, false,\n                false);\n        sHumanTaskInstance.setStateId(2);\n        //when\n        expectedException.expect(SWorkPreconditionException.class);\n        expectedException.expectMessage(\"Unable to execute flow node \" + FLOW_NODE_INSTANCE_ID\n                + \" because it is not in the expected state \" +\n                \"( expected state: 4, transitioning: false, aborting: false, canceling: false, \" +\n                \"but got  state: 2, transitioning: false, aborting: false, canceling: false). Someone probably already called execute on it.\");\n        executeFlowNodeWork.work(context);\n        //then exception\n    }\n\n    @Test\n    public void should_throw_exception_if_executing_flow_node_not_in_expected_state() throws Exception {\n        //given\n        ExecuteFlowNodeWork executeFlowNodeWork = new ExecuteFlowNodeWork(FLOW_NODE_INSTANCE_ID, 4, false, false, true);\n        sHumanTaskInstance.setStateId(4);\n        sHumanTaskInstance.setStateExecuting(false);\n        //when\n        expectedException.expect(SWorkPreconditionException.class);\n        executeFlowNodeWork.work(context);\n        //then exception\n    }\n\n    @Test\n    public void should_execute_human_task_in_ready_and_executing_if_isReadyHumanTask_flag_is_true() throws Exception {\n        //given\n        ExecuteFlowNodeWork executeFlowNodeWork = new ExecuteFlowNodeWork(FLOW_NODE_INSTANCE_ID, 4, true, false, false);\n        sHumanTaskInstance.setStateId(4);\n        sHumanTaskInstance.setStateExecuting(true);\n        //when\n        executeFlowNodeWork.work(context);\n        //then\n        verify(flowNodeExecutor).executeFlowNode(sHumanTaskInstance, null, null);\n    }\n\n    @Test\n    public void should_execute_flow_node_in_any_state_if_isReadyHumanTask_flag_is_false() throws Exception {\n        //given\n        ExecuteFlowNodeWork executeFlowNodeWork = new ExecuteFlowNodeWork(FLOW_NODE_INSTANCE_ID, 2, true, false, false);\n        sHumanTaskInstance.setStateId(2);\n        sHumanTaskInstance.setStateExecuting(true);\n        //when\n        executeFlowNodeWork.work(context);\n        //then\n        verify(flowNodeExecutor).executeFlowNode(sHumanTaskInstance, null, null);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/work/ExecuteMessageCoupleWorkTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work;\n\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.*;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.Callable;\n\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SBPMEventType;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceService;\nimport org.bonitasoft.engine.execution.event.EventsHandler;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnit;\nimport org.mockito.junit.MockitoRule;\n\npublic class ExecuteMessageCoupleWorkTest {\n\n    private static final long MESSAGE_INSTANCE_ID = 543L;\n    private static final long WAITING_MESSAGE_ID = 2342L;\n    @Rule\n    public MockitoRule mockitoRule = MockitoJUnit.rule();\n    @Mock\n    private EventsHandler eventsHandler;\n    @Mock\n    private ServiceAccessor serviceAccessor;\n    @Mock\n    private EventInstanceService eventInstanceService;\n    @Mock\n    private DataInstanceService dataInstanceService;\n    @Mock\n    private UserTransactionService userTransactionService;\n    @Captor\n    private ArgumentCaptor<Callable<?>> callableArgumentCaptor;\n    private final Map<String, Object> context = new HashMap<>();\n\n    private final ExecuteMessageCoupleWork executeMessageCoupleWork = new ExecuteMessageCoupleWork(MESSAGE_INSTANCE_ID,\n            WAITING_MESSAGE_ID);\n    private final SWaitingMessageEvent waitingMessageEvent = new SWaitingMessageEvent(SBPMEventType.BOUNDARY_EVENT,\n            4243252L,\n            \"Process\", 5435312, \"flownode\", \"message\");\n    private final SMessageInstance messageInstance = new SMessageInstance(\"message\", \"Process\", \"flowNode\", 4243252L,\n            \"flownode\");\n\n    @Before\n    public void before() {\n        waitingMessageEvent.setId(WAITING_MESSAGE_ID);\n        messageInstance.setId(MESSAGE_INSTANCE_ID);\n        context.put(TenantAwareBonitaWork.SERVICE_ACCESSOR, serviceAccessor);\n        doReturn(eventsHandler).when(serviceAccessor).getEventsHandler();\n        doReturn(eventInstanceService).when(serviceAccessor).getEventInstanceService();\n        doReturn(dataInstanceService).when(serviceAccessor).getDataInstanceService();\n        doReturn(userTransactionService).when(serviceAccessor).getUserTransactionService();\n    }\n\n    @Test\n    public void should_execute_message_couple() throws Exception {\n        doReturn(waitingMessageEvent).when(eventInstanceService).getWaitingMessage(WAITING_MESSAGE_ID);\n\n        executeMessageCoupleWork.work(context);\n\n        verify(eventsHandler).triggerCatchEvent(waitingMessageEvent, MESSAGE_INSTANCE_ID);\n    }\n\n    @Test\n    public void should_delete_message_data_when_couple_is_handled() throws Exception {\n        doReturn(waitingMessageEvent).when(eventInstanceService).getWaitingMessage(WAITING_MESSAGE_ID);\n\n        executeMessageCoupleWork.work(context);\n\n        verify(dataInstanceService).deleteLocalDataInstances(MESSAGE_INSTANCE_ID,\n                DataInstanceContainer.MESSAGE_INSTANCE.name(), true);\n    }\n\n    @Test\n    public void should_delete_message_instance_when_couple_is_handled() throws Exception {\n        doReturn(waitingMessageEvent).when(eventInstanceService).getWaitingMessage(WAITING_MESSAGE_ID);\n        doReturn(messageInstance).when(eventInstanceService).getMessageInstance(MESSAGE_INSTANCE_ID);\n\n        executeMessageCoupleWork.work(context);\n\n        verify(eventInstanceService).deleteMessageInstance(messageInstance);\n    }\n\n    @Test\n    public void should_reset_message_instance_in_transaction_on_failure() throws Exception {\n        doReturn(messageInstance).when(eventInstanceService).getMessageInstance(MESSAGE_INSTANCE_ID);\n        doReturn(null).when(userTransactionService).executeInTransaction(callableArgumentCaptor.capture());\n\n        executeMessageCoupleWork.handleFailure(new Exception(\"something happened during the coupling\"), context);\n\n        callableArgumentCaptor.getValue().call();\n        verify(eventInstanceService, never()).updateMessageInstance(any(), any());\n    }\n\n    @Test\n    public void should_not_reset_waiting_message_instance_on_failure() throws Exception {\n        doReturn(messageInstance).when(eventInstanceService).getMessageInstance(MESSAGE_INSTANCE_ID);\n        doReturn(null).when(userTransactionService).executeInTransaction(callableArgumentCaptor.capture());\n\n        executeMessageCoupleWork.handleFailure(new Exception(\"something happened during the coupling\"), context);\n\n        callableArgumentCaptor.getValue().call();\n        verify(eventInstanceService, never()).updateWaitingMessage(eq(waitingMessageEvent),\n                argThat(arg -> arg.getFields().size() == 1 &&\n                        arg.getFields().get(\"progress\").equals(0)));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/work/FailedStateSetterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\n\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.execution.WaitingEventsInterrupter;\nimport org.bonitasoft.engine.execution.state.FlowNodeStateManager;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.SystemOutRule;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class FailedStateSetterTest {\n\n    @Rule\n    public SystemOutRule systemOutRule = new SystemOutRule().enableLog();\n    @Mock\n    private WaitingEventsInterrupter waitingEventsInterrupter;\n\n    @Mock\n    private ActivityInstanceService activityInstanceService;\n\n    @Mock\n    private FlowNodeStateManager flowNodeStateManager;\n\n    @Mock\n    private SFlowNodeInstance flowNodeInstance;\n\n    @Mock\n    private FlowNodeState failedState;\n\n    @InjectMocks\n    private FailedStateSetter failedStateSetter;\n\n    public static final long FLOW_NODE_INSTANCE_ID = 15;\n\n    public static final long PROCESS_DEFINITION_ID = 25;\n\n    public static final int STATE_ID = 10;\n\n    @Before\n    public void setUp() throws Exception {\n        given(flowNodeStateManager.getState(FlowNodeState.ID_ACTIVITY_FAILED)).willReturn(failedState);\n        given(flowNodeInstance.getStateId()).willReturn(STATE_ID);\n    }\n\n    @Test\n    public void setAsFailed_should_setState_to_failed_and_interrupt_waitingEvents() throws Exception {\n        //given\n        given(activityInstanceService.getFlowNodeInstance(FLOW_NODE_INSTANCE_ID)).willReturn(flowNodeInstance);\n\n        //when\n        failedStateSetter.setAsFailed(FLOW_NODE_INSTANCE_ID);\n\n        //then\n        verify(waitingEventsInterrupter).interruptWaitingEvents(flowNodeInstance);\n        verify(activityInstanceService).setState(flowNodeInstance, failedState);\n    }\n\n    @Test\n    public void setAsFailed_should_log_message_when_flowNodeInstance_is_not_found() throws Exception {\n        //given\n        given(activityInstanceService.getFlowNodeInstance(FLOW_NODE_INSTANCE_ID))\n                .willThrow(new SFlowNodeNotFoundException(FLOW_NODE_INSTANCE_ID));\n\n        //when\n        systemOutRule.clearLog();\n        failedStateSetter.setAsFailed(FLOW_NODE_INSTANCE_ID);\n        //then\n        assertThat(systemOutRule.getLog())\n                .contains(\"Impossible to put flow node instance in failed state: flow node instance with id '\"\n                        + FLOW_NODE_INSTANCE_ID + \"' not found.\");\n        verify(activityInstanceService, never()).setState(flowNodeInstance, failedState);\n    }\n\n    @Test\n    public void setAsFailed_should_do_nothing_when_flow_node_is_already_in_failed_state() throws Exception {\n        //given\n        given(activityInstanceService.getFlowNodeInstance(FLOW_NODE_INSTANCE_ID)).willReturn(flowNodeInstance);\n        given(flowNodeInstance.getStateId()).willReturn(FlowNodeState.ID_ACTIVITY_FAILED);\n\n        //when\n        failedStateSetter.setAsFailed(FLOW_NODE_INSTANCE_ID);\n\n        //then\n        verify(activityInstanceService, never()).setState(any(SFlowNodeInstance.class), any(FlowNodeState.class));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/work/InSessionBonitaWorkTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.Mockito.*;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.work.BonitaWork;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class InSessionBonitaWorkTest {\n\n    @Mock\n    private BonitaWork wrappedWork;\n    @Mock\n    private ServiceAccessor serviceAccessor;\n\n    private InSessionBonitaWork txBonitaWork;\n\n    @Before\n    public void before() {\n        txBonitaWork = spy(new InSessionBonitaWork(wrappedWork));\n\n        doReturn(serviceAccessor).when(txBonitaWork).getServiceAccessor();\n    }\n\n    @Test\n    public void testWork() throws Exception {\n        final Map<String, Object> singletonMap = new HashMap<>();\n        txBonitaWork.work(singletonMap);\n        verify(wrappedWork, times(1)).work(singletonMap);\n    }\n\n    @Test\n    public void testWorkFailureIsHandled() throws Throwable {\n        final Map<String, Object> singletonMap = new HashMap<>();\n        final Exception e = new Exception();\n        txBonitaWork.handleFailure(e, singletonMap);\n        verify(wrappedWork).handleFailure(e, singletonMap);\n    }\n\n    @Test(expected = Exception.class)\n    public void testFailureHandlingFail() throws Throwable {\n        final Map<String, Object> singletonMap = new HashMap<>();\n        final Exception e1 = new Exception();\n        final Exception e2 = new Exception();\n        doThrow(e2).when(wrappedWork).handleFailure(e1, singletonMap);\n        txBonitaWork.handleFailure(e1, singletonMap);\n        verify(wrappedWork).handleFailure(e1, singletonMap);\n    }\n\n    @Test\n    public void putInMap() throws Exception {\n        final Map<String, Object> singletonMap = new HashMap<>();\n        txBonitaWork.work(singletonMap);\n        assertEquals(serviceAccessor, singletonMap.get(\"serviceAccessor\"));\n    }\n\n    @Test\n    public void getDescription() {\n        when(wrappedWork.getDescription()).thenReturn(\"The description\");\n        assertEquals(\"The description\", txBonitaWork.getDescription());\n    }\n\n    @Test\n    public void getRecoveryProcedure() {\n        when(wrappedWork.getRecoveryProcedure()).thenReturn(\"recoveryProcedure\");\n        assertEquals(\"recoveryProcedure\", txBonitaWork.getRecoveryProcedure());\n    }\n\n    @Test\n    public void handleFailure() throws Throwable {\n        final Map<String, Object> context = Map.of(\"serviceAccessor\", serviceAccessor);\n        final SBonitaException e = new SBonitaException() {\n\n            private static final long serialVersionUID = -6748168976371554636L;\n        };\n        txBonitaWork.handleFailure(e, context);\n        verify(wrappedWork).handleFailure(e, context);\n    }\n\n    @Test\n    public void getWrappedWork() {\n        assertEquals(wrappedWork, txBonitaWork.getWrappedWork());\n    }\n\n    @Test\n    public void testToString() {\n        when(wrappedWork.toString()).thenReturn(\"the to string\");\n        assertEquals(\"the to string\", txBonitaWork.toString());\n    }\n\n    @Test(expected = Exception.class)\n    public void should_throw_the_exception_of_wrapped_work() throws Throwable {\n        final Map<String, Object> context = new HashMap<>();\n        final Exception e = new Exception();\n        doThrow(e).when(wrappedWork).work(context);\n        txBonitaWork.work(context);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/work/LockProcessInstanceWorkTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.concurrent.TimeUnit;\n\nimport org.bonitasoft.engine.lock.BonitaLock;\nimport org.bonitasoft.engine.lock.LockService;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.work.BonitaWork;\nimport org.bonitasoft.engine.work.LockTimeoutException;\nimport org.bonitasoft.engine.work.WorkExecutorService;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class LockProcessInstanceWorkTest {\n\n    private static final String PROCESS = \"PROCESS\";\n\n    private final long processInstanceId = 123L;\n\n    private final BonitaWork wrappedWork = mock(BonitaWork.class);\n\n    private LockProcessInstanceWork lockProcessInstanceWork;\n\n    private ServiceAccessor serviceAccessor;\n\n    private LockService lockService;\n\n    @Before\n    public void before() {\n        lockProcessInstanceWork = new LockProcessInstanceWork(wrappedWork, processInstanceId);\n        serviceAccessor = mock(ServiceAccessor.class);\n        lockService = mock(LockService.class);\n        WorkExecutorService workService = mock(WorkExecutorService.class);\n        when(serviceAccessor.getLockService()).thenReturn(lockService);\n        when(serviceAccessor.getWorkExecutorService()).thenReturn(workService);\n    }\n\n    @Test\n    public void testWork() throws Exception {\n        BonitaLock bonitaLock = new BonitaLock(PROCESS, processInstanceId);\n        when(lockService.tryLock(eq(processInstanceId), eq(PROCESS), eq(20L), eq(TimeUnit.MILLISECONDS)))\n                .thenReturn(\n                        bonitaLock);\n        Map<String, Object> singletonMap = Collections.singletonMap(\"serviceAccessor\", serviceAccessor);\n        lockProcessInstanceWork.work(singletonMap);\n        verify(lockService, times(1)).tryLock(eq(processInstanceId), eq(PROCESS), eq(20L), eq(TimeUnit.MILLISECONDS));\n        verify(lockService, times(1)).unlock(bonitaLock);\n        verify(wrappedWork, times(1)).work(singletonMap);\n    }\n\n    @Test\n    public void getDescription() {\n        when(wrappedWork.getDescription()).thenReturn(\"The description\");\n        assertEquals(\"The description\", lockProcessInstanceWork.getDescription());\n    }\n\n    @Test\n    public void getRecoveryProcedure() {\n        when(wrappedWork.getRecoveryProcedure()).thenReturn(\"recoveryProcedure\");\n        assertEquals(\"recoveryProcedure\", lockProcessInstanceWork.getRecoveryProcedure());\n    }\n\n    @Test\n    public void handleFailure() throws Throwable {\n        Map<String, Object> context = Collections.singletonMap(\"serviceAccessor\", serviceAccessor);\n        Exception e = new Exception();\n        lockProcessInstanceWork.handleFailure(e, context);\n        verify(wrappedWork).handleFailure(e, context);\n    }\n\n    @Test\n    public void getWrappedWork() {\n        assertEquals(wrappedWork, lockProcessInstanceWork.getWrappedWork());\n    }\n\n    @Test\n    public void testToString() {\n        when(wrappedWork.toString()).thenReturn(\"the to string\");\n        assertEquals(\"the to string\", lockProcessInstanceWork.toString());\n    }\n\n    @Test(expected = LockTimeoutException.class)\n    public void should_throw_exception_when_unable_to_lock() throws Exception {\n        // On first try to lock : exception to reschedule the work\n        // On the second try : return a correct lock\n        when(lockService.tryLock(eq(processInstanceId), eq(PROCESS), eq(20L), eq(TimeUnit.MILLISECONDS)))\n                .thenReturn(null);\n\n        lockProcessInstanceWork.work(Collections.singletonMap(\"serviceAccessor\", serviceAccessor));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/work/NotifyChildFinishedWorkTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.classloader.SClassLoaderException;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance;\nimport org.bonitasoft.engine.execution.ContainerRegistry;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.work.SWorkPreconditionException;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnit;\nimport org.mockito.junit.MockitoRule;\n\npublic class NotifyChildFinishedWorkTest {\n\n    private static final int PROCESS_DEFINITION_ID = 123;\n    private static final int FLOW_NODE_INSTANCE_ID = 345;\n    @Rule\n    public MockitoRule mockitoRule = MockitoJUnit.rule();\n    @Mock\n    private ServiceAccessor serviceAccessor;\n    @Mock\n    private ActivityInstanceService flowNodeInstanceService;\n    @Mock\n    private ContainerRegistry containerRegistry;\n    @Mock\n    private ClassLoaderService classLoaderService;\n    private Map<String, Object> context;\n    private NotifyChildFinishedWork notifyChildFinishedWork;\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Before\n    public void before() throws SClassLoaderException {\n        doReturn(this.getClass().getClassLoader()).when(classLoaderService).getClassLoader(any());\n        context = new HashMap<>();\n        context.put(TenantAwareBonitaWork.SERVICE_ACCESSOR, serviceAccessor);\n\n        doReturn(classLoaderService).when(serviceAccessor).getClassLoaderService();\n        doReturn(flowNodeInstanceService).when(serviceAccessor).getActivityInstanceService();\n    }\n\n    @Test\n    public void should_throw_precondition_exception_when_flownode_is_not_found() throws Exception {\n        notifyChildFinishedWork = new NotifyChildFinishedWork(PROCESS_DEFINITION_ID, FLOW_NODE_INSTANCE_ID, 4, false,\n                false, false);\n        doThrow(SFlowNodeNotFoundException.class).when(flowNodeInstanceService)\n                .getFlowNodeInstance(FLOW_NODE_INSTANCE_ID);\n\n        expectedException.expect(SWorkPreconditionException.class);\n        expectedException.expectMessage(\"Flow node \" + FLOW_NODE_INSTANCE_ID + \" is already completed ( not found )\");\n\n        notifyChildFinishedWork.work(context);\n    }\n\n    @Test\n    public void should_throw_precondition_exception_when_flownode_is_completed() throws Exception {\n        notifyChildFinishedWork = new NotifyChildFinishedWork(PROCESS_DEFINITION_ID, FLOW_NODE_INSTANCE_ID, 1, false,\n                false, false);\n        SAutomaticTaskInstance flowNodeInstance = new SAutomaticTaskInstance();\n        flowNodeInstance.setTerminal(false);\n        flowNodeInstance.setStateId(1);\n        doReturn(flowNodeInstance).when(flowNodeInstanceService).getFlowNodeInstance(FLOW_NODE_INSTANCE_ID);\n\n        expectedException.expect(SWorkPreconditionException.class);\n        expectedException.expectMessage(\"Flow node \" + FLOW_NODE_INSTANCE_ID + \" is not yet completed\");\n\n        notifyChildFinishedWork.work(context);\n    }\n\n    @Test\n    public void should_throw_precondition_exception_when_flownode_changed() throws Exception {\n        notifyChildFinishedWork = new NotifyChildFinishedWork(PROCESS_DEFINITION_ID, FLOW_NODE_INSTANCE_ID, 3, false,\n                false, false);\n        SAutomaticTaskInstance flowNodeInstance = new SAutomaticTaskInstance();\n        flowNodeInstance.setTerminal(true);\n        flowNodeInstance.setStateId(2);\n        doReturn(flowNodeInstance).when(flowNodeInstanceService).getFlowNodeInstance(FLOW_NODE_INSTANCE_ID);\n\n        expectedException.expect(SWorkPreconditionException.class);\n        expectedException.expectMessage(\"Unable to execute flow node \" + FLOW_NODE_INSTANCE_ID\n                + \" because it is not in the expected state \" +\n                \"( expected state: 3, transitioning: false, aborting: false, canceling: false, but got  state: 2, transitioning: false, aborting: false, canceling: false).\"\n                +\n                \" Someone probably already called execute on it.\");\n\n        notifyChildFinishedWork.work(context);\n    }\n\n    @Test\n    public void should_notify_child_finished_if_flow_node_is_completed() throws Exception {\n        notifyChildFinishedWork = new NotifyChildFinishedWork(PROCESS_DEFINITION_ID, FLOW_NODE_INSTANCE_ID, 2, false,\n                false, false);\n        doReturn(containerRegistry).when(serviceAccessor).getContainerRegistry();\n        SAutomaticTaskInstance flowNodeInstance = new SAutomaticTaskInstance();\n        flowNodeInstance.setTerminal(true);\n        flowNodeInstance.setStateId(2);\n        doReturn(flowNodeInstance).when(flowNodeInstanceService).getFlowNodeInstance(FLOW_NODE_INSTANCE_ID);\n\n        notifyChildFinishedWork.work(context);\n\n        verify(containerRegistry).nodeReachedState(flowNodeInstance);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/work/SetInFailCallableTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work;\n\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport org.bonitasoft.engine.commons.exceptions.ScopedException;\nimport org.bonitasoft.engine.core.process.instance.api.BPMFailureService;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class SetInFailCallableTest {\n\n    @Mock\n    private FailedStateSetter failedStateSetter;\n\n    @Mock\n    private SFlowNodeInstance flowNodeInstance;\n\n    @Mock\n    private BPMFailureService failureService;\n\n    public static final long PROCESS_DEFINITION_ID = 25L;\n    private static final long FLOW_NODE_INSTANCE_ID = 123L;\n\n    private SetInFailCallable setInFailCallable;\n    private BPMFailureService.Failure failure;\n\n    @Before\n    public void setUp() throws Exception {\n        when(flowNodeInstance.getId()).thenReturn(FLOW_NODE_INSTANCE_ID);\n        failure = new BPMFailureService.Failure(ScopedException.UNKNOWN_SCOPE, new RuntimeException());\n        setInFailCallable = new SetInFailCallable(failedStateSetter, flowNodeInstance, failureService, failure);\n    }\n\n    @Test\n    public void call_should_setState() throws Exception {\n\n        //when\n        setInFailCallable.call();\n\n        //then\n        verify(failedStateSetter).setAsFailed(FLOW_NODE_INSTANCE_ID);\n    }\n\n    @Test\n    public void call_should_create_failure() throws Exception {\n\n        //when\n        setInFailCallable.call();\n\n        //then\n        verify(failureService).createFlowNodeFailure(flowNodeInstance, failure);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/work/TenantRestarterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work;\n\nimport static java.util.Arrays.asList;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.doThrow;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport java.util.concurrent.Callable;\n\nimport org.bonitasoft.engine.platform.PlatformService;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.bonitasoft.engine.tenant.TenantRestarter;\nimport org.bonitasoft.engine.tenant.restart.TenantRestartHandler;\nimport org.bonitasoft.engine.transaction.TransactionService;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Baptiste Mesta\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class TenantRestarterTest {\n\n    @Mock\n    private TenantRestartHandler tenantRestartHandler1;\n    @Mock\n    private TenantRestartHandler tenantRestartHandler2;\n    @Mock\n    private TransactionService transactionService;\n    @Mock\n    private SessionAccessor sessionAccessor;\n    @Mock\n    private PlatformService platformService;\n    private TenantRestarter tenantRestarter;\n\n    @Before\n    public void before() throws Exception {\n        tenantRestarter = new TenantRestarter(transactionService, platformService,\n                asList(tenantRestartHandler1, tenantRestartHandler2));\n        when(transactionService.executeInTransaction(any()))\n                .then(invocationOnMock -> ((Callable) invocationOnMock.getArgument(0)).call());\n    }\n\n    @Test\n    public void should_execute_beforeServicesStart_on_handlers() throws Exception {\n\n        tenantRestarter.executeBeforeServicesStart();\n\n        verify(tenantRestartHandler1).beforeServicesStart();\n        verify(tenantRestartHandler2).beforeServicesStart();\n        verify(transactionService).executeInTransaction(any());\n    }\n\n    @Test(expected = RuntimeException.class)\n    public void should_stop_execution_when_exception_happens_on_tenantRestartHandler() throws Exception {\n        doThrow(new RuntimeException(\"ohoh...\")).when(tenantRestartHandler1).beforeServicesStart();\n\n        tenantRestarter.executeBeforeServicesStart();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/work/TransactionServiceForTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work;\n\nimport java.util.Optional;\nimport java.util.concurrent.Callable;\n\nimport javax.transaction.Synchronization;\n\nimport org.bonitasoft.engine.transaction.UserTransactionService;\n\npublic class TransactionServiceForTest implements UserTransactionService {\n\n    public TransactionServiceForTest() {\n    }\n\n    @Override\n    public void registerBonitaSynchronization(final Synchronization txSync) {\n    }\n\n    @Override\n    public <T> T executeInTransaction(final Callable<T> callable) throws Exception {\n        return callable.call();\n    }\n\n    @Override\n    public void registerBeforeCommitCallable(final Callable<Void> callable) {\n    }\n\n    @Override\n    public boolean isTransactionActive() {\n        return false;\n    }\n\n    @Override\n    public Optional<Boolean> hasMultipleResources() {\n        return Optional.empty();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/work/TxBonitaWorkTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.bonitasoft.engine.work.BonitaWork;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class TxBonitaWorkTest {\n\n    private final BonitaWork wrappedWork = mock(BonitaWork.class);\n\n    private TxBonitaWork txBonitawork;\n\n    private ServiceAccessor serviceAccessor;\n\n    private UserTransactionService userTransactionService;\n\n    @Before\n    public void before() {\n        txBonitawork = new TxBonitaWork(wrappedWork);\n        serviceAccessor = mock(ServiceAccessor.class);\n        userTransactionService = mock(UserTransactionService.class);\n        when(serviceAccessor.getUserTransactionService()).thenReturn(userTransactionService);\n    }\n\n    @Test\n    public void testWork() throws Exception {\n        Map<String, Object> singletonMap = Collections.singletonMap(\"serviceAccessor\", serviceAccessor);\n        txBonitawork.work(singletonMap);\n        verify(userTransactionService, times(1)).executeInTransaction(any());\n    }\n\n    @Test\n    public void getDescription() {\n        when(wrappedWork.getDescription()).thenReturn(\"The description\");\n        assertEquals(\"The description\", txBonitawork.getDescription());\n    }\n\n    @Test\n    public void getRecoveryProcedure() {\n        when(wrappedWork.getRecoveryProcedure()).thenReturn(\"recoveryProcedure\");\n        assertEquals(\"recoveryProcedure\", txBonitawork.getRecoveryProcedure());\n    }\n\n    @Test\n    public void handleFailure() throws Throwable {\n        Map<String, Object> context = Collections.singletonMap(\"serviceAccessor\", serviceAccessor);\n        Exception e = new Exception();\n        txBonitawork.handleFailure(e, context);\n        verify(wrappedWork).handleFailure(e, context);\n    }\n\n    @Test\n    public void getWrappedWork() {\n        assertEquals(wrappedWork, txBonitawork.getWrappedWork());\n    }\n\n    @Test\n    public void testToString() {\n        when(wrappedWork.toString()).thenReturn(\"the to string\");\n        assertEquals(\"the to string\", txBonitawork.toString());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/work/failurewrapping/AbstractContextWorkTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work.failurewrapping;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.Mockito.*;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.execution.work.TransactionServiceForTest;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\n\n/**\n * @author Aurelien Pupier\n */\npublic abstract class AbstractContextWorkTest {\n\n    @Mock\n    protected TxInHandleFailureWrappingWork wrappedWork;\n\n    @Mock\n    protected ServiceAccessor serviceAccessor;\n\n    @Spy\n    private TransactionServiceForTest transactionService;\n\n    protected TxInHandleFailureWrappingWork txBonitaWork;\n\n    @Before\n    public void before() throws SBonitaException {\n        doReturn(transactionService).when(serviceAccessor).getUserTransactionService();\n\n        doReturn(\"The description\").when(txBonitaWork).getDescription();\n\n    }\n\n    @Test\n    public void work() throws Exception {\n        final Map<String, Object> singletonMap = new HashMap<>();\n        txBonitaWork.work(singletonMap);\n        verify(wrappedWork, times(1)).work(singletonMap);\n    }\n\n    @Test\n    public void getDescription() {\n        assertEquals(\"The description\", txBonitaWork.getDescription());\n    }\n\n    @Test\n    public void getRecoveryProcedure() {\n        when(wrappedWork.getRecoveryProcedure()).thenReturn(\"recoveryProcedure\");\n        assertEquals(\"recoveryProcedure\", txBonitaWork.getRecoveryProcedure());\n    }\n\n    @Test\n    public void getWrappedWork() {\n        assertEquals(wrappedWork, txBonitaWork.getWrappedWork());\n    }\n\n    @Test\n    public void testToString() {\n        when(wrappedWork.toString()).thenReturn(\"the to string\");\n        assertEquals(\"the to string\", txBonitaWork.toString());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/work/failurewrapping/ConnectorDefinitionAndInstanceContextWorkTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work.failurewrapping;\n\nimport static java.util.Collections.singletonMap;\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Celine Souchet\n */\n@SuppressWarnings(\"javadoc\")\n@RunWith(MockitoJUnitRunner.class)\npublic class ConnectorDefinitionAndInstanceContextWorkTest extends AbstractContextWorkTest {\n\n    private static final String CONNECTOR_DEFINITION_NAME = \"connector_name\";\n\n    private static final ConnectorEvent ACTIVATION_EVENT = ConnectorEvent.ON_ENTER;\n\n    private static final long CONNECTOR_INSTANCE_ID = 10L;\n    private static final String CONNECTOR_DEFINITION_ID = \"groovy\";\n\n    @Override\n    @Before\n    public void before() throws SBonitaException {\n        txBonitaWork = spy(new ConnectorDefinitionAndInstanceContextWork(wrappedWork,\n                CONNECTOR_DEFINITION_ID,\n                CONNECTOR_DEFINITION_NAME,\n                CONNECTOR_INSTANCE_ID, ConnectorEvent.ON_ENTER));\n        super.before();\n    }\n\n    @Test\n    public void handleFailureWithNameAndId() throws Throwable {\n        final Map<String, Object> context = singletonMap(\"serviceAccessor\", serviceAccessor);\n        final SBonitaException e = new SBonitaException() {\n\n            private static final long serialVersionUID = -6748168976371554636L;\n        };\n\n        txBonitaWork.handleFailure(e, context);\n\n        assertTrue(e.getMessage().contains(\"CONNECTOR_IMPLEMENTATION_CLASS_NAME=\" + CONNECTOR_DEFINITION_NAME));\n        assertTrue(e.getMessage().contains(\"CONNECTOR_INSTANCE_ID=\" + CONNECTOR_INSTANCE_ID));\n        verify(wrappedWork).handleFailure(e, context);\n    }\n\n    @Test\n    public void handleFailureWithNameAndIdAndActivationEvent() throws Throwable {\n        txBonitaWork = spy(new ConnectorDefinitionAndInstanceContextWork(wrappedWork,\n                CONNECTOR_DEFINITION_ID,\n                CONNECTOR_DEFINITION_NAME,\n                CONNECTOR_INSTANCE_ID,\n                ACTIVATION_EVENT));\n        final Map<String, Object> context = singletonMap(\"serviceAccessor\", serviceAccessor);\n        final SBonitaException e = new SBonitaException() {\n\n            private static final long serialVersionUID = -6748168976371554636L;\n        };\n\n        txBonitaWork.handleFailure(e, context);\n\n        assertTrue(e.getMessage().contains(\"CONNECTOR_IMPLEMENTATION_CLASS_NAME=\" + CONNECTOR_DEFINITION_NAME));\n        assertTrue(e.getMessage().contains(\"CONNECTOR_INSTANCE_ID=\" + CONNECTOR_INSTANCE_ID));\n        assertTrue(e.getMessage().contains(\"CONNECTOR_ACTIVATION_EVENT=\" + ACTIVATION_EVENT));\n        verify(wrappedWork, times(1)).handleFailure(e, context);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/work/failurewrapping/FlowNodeDefinitionAndInstanceContextWorkTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work.failurewrapping;\n\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class FlowNodeDefinitionAndInstanceContextWorkTest extends AbstractContextWorkTest {\n\n    private static final long FLOW_NODE_DEFINITION_ID = 2;\n\n    private static final long FLOW_NODE_INSTANCE_ID = 3;\n\n    private static final String FLOW_NODE_NAME = \"name\";\n\n    @Mock\n    private ActivityInstanceService activityInstanceService;\n\n    @Mock\n    private SFlowNodeInstance flowNodeInstance;\n\n    @Override\n    @Before\n    public void before() throws SBonitaException {\n        when(serviceAccessor.getActivityInstanceService()).thenReturn(activityInstanceService);\n\n        txBonitaWork = spy(new FlowNodeDefinitionAndInstanceContextWork(wrappedWork, FLOW_NODE_INSTANCE_ID));\n\n        doReturn(flowNodeInstance).when(activityInstanceService).getFlowNodeInstance(FLOW_NODE_INSTANCE_ID);\n        doReturn(FLOW_NODE_DEFINITION_ID).when(flowNodeInstance).getFlowNodeDefinitionId();\n        doReturn(FLOW_NODE_NAME).when(flowNodeInstance).getName();\n        super.before();\n    }\n\n    @Test\n    public void handleFailure() throws Throwable {\n        final Map<String, Object> context = Collections.singletonMap(\"serviceAccessor\", serviceAccessor);\n        final SBonitaException e = new SBonitaException() {\n\n            private static final long serialVersionUID = -6748168976371554636L;\n        };\n\n        txBonitaWork.handleFailure(e, context);\n\n        assertTrue(e.getMessage().contains(\"FLOW_NODE_DEFINITION_ID=\" + FLOW_NODE_DEFINITION_ID));\n        assertTrue(e.getMessage().contains(\"FLOW_NODE_NAME=\" + FLOW_NODE_NAME));\n        assertTrue(e.getMessage().contains(\"FLOW_NODE_INSTANCE_ID=\" + FLOW_NODE_INSTANCE_ID));\n        verify(wrappedWork).handleFailure(e, context);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/work/failurewrapping/MessageInstanceContextWorkTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work.failurewrapping;\n\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SBPMEventType;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class MessageInstanceContextWorkTest extends AbstractContextWorkTest {\n\n    private final static String MESSAGE_INSTANCE_NAME = \"name\";\n\n    private final static String MESSAGE_INSTANCE_TARGET_PROCESS_NAME = \"process\";\n\n    private final static String MESSAGE_INSTANCE_TARGET_FLOW_NODE_NAME = \"flowNode name\";\n\n    private final static SBPMEventType WAITING_MESSAGE_EVENT_TYPE = SBPMEventType.INTERMEDIATE_THROW_EVENT;\n\n    @Override\n    @Before\n    public void before() throws SBonitaException {\n        txBonitaWork = spy(\n                new MessageInstanceContextWork(wrappedWork, MESSAGE_INSTANCE_NAME, MESSAGE_INSTANCE_TARGET_PROCESS_NAME,\n                        MESSAGE_INSTANCE_TARGET_FLOW_NODE_NAME, WAITING_MESSAGE_EVENT_TYPE.name()));\n        super.before();\n    }\n\n    @Test\n    public void handleFailure() throws Throwable {\n        final Map<String, Object> context = Collections.singletonMap(\"serviceAccessor\", serviceAccessor);\n        final SBonitaException e = new SBonitaException() {\n\n            private static final long serialVersionUID = -6748168976371554636L;\n        };\n\n        txBonitaWork.handleFailure(e, context);\n\n        assertTrue(e.getMessage().contains(\"MESSAGE_INSTANCE_NAME=\" + MESSAGE_INSTANCE_NAME));\n        assertTrue(e.getMessage()\n                .contains(\"MESSAGE_INSTANCE_TARGET_PROCESS_NAME=\" + MESSAGE_INSTANCE_TARGET_PROCESS_NAME));\n        assertTrue(e.getMessage()\n                .contains(\"MESSAGE_INSTANCE_TARGET_FLOW_NODE_NAME=\" + MESSAGE_INSTANCE_TARGET_FLOW_NODE_NAME));\n        assertTrue(e.getMessage().contains(\"WAITING_MESSAGE_INSTANCE_TYPE=\" + WAITING_MESSAGE_EVENT_TYPE.name()));\n        verify(wrappedWork).handleFailure(e, context);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/work/failurewrapping/ProcessDefinitionContextWorkTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work.failurewrapping;\n\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Celine Souchet\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class ProcessDefinitionContextWorkTest extends AbstractContextWorkTest {\n\n    private static final long PROCESS_DEFINITION_ID = 2;\n\n    private static final String VERSION = \"version\";\n\n    private static final String NAME = \"name\";\n\n    @Mock\n    private ProcessDefinitionService processDefinitionService;\n\n    @Mock\n    private SProcessDefinitionDeployInfo sProcessDefinitionDeployInfo;\n\n    @Override\n    @Before\n    public void before() throws SBonitaException {\n        doReturn(NAME).when(sProcessDefinitionDeployInfo).getName();\n        doReturn(VERSION).when(sProcessDefinitionDeployInfo).getVersion();\n\n        doReturn(sProcessDefinitionDeployInfo).when(processDefinitionService)\n                .getProcessDeploymentInfo(PROCESS_DEFINITION_ID);\n        doReturn(processDefinitionService).when(serviceAccessor).getProcessDefinitionService();\n\n        txBonitaWork = spy(new ProcessDefinitionContextWork(wrappedWork, PROCESS_DEFINITION_ID));\n\n        super.before();\n    }\n\n    @Test\n    public void handleFailure() throws Throwable {\n        final Map<String, Object> context = Collections.singletonMap(\"serviceAccessor\",\n                serviceAccessor);\n        final SBonitaException e = new SBonitaException() {\n\n            private static final long serialVersionUID = -6748168976371554636L;\n        };\n\n        txBonitaWork.handleFailure(e, context);\n\n        assertTrue(e.getMessage().contains(\"PROCESS_DEFINITION_ID=\" + PROCESS_DEFINITION_ID));\n        assertTrue(e.getMessage().contains(\"PROCESS_NAME=\" + NAME));\n        assertTrue(e.getMessage().contains(\"PROCESS_VERSION=\" + VERSION));\n        verify(wrappedWork).handleFailure(e, context);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/work/failurewrapping/ProcessInstanceContextWorkTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work.failurewrapping;\n\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Celine Souchet\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class ProcessInstanceContextWorkTest extends AbstractContextWorkTest {\n\n    static final long PROCESS_INSTANCE_ID = 2;\n\n    static final long ROOT_PROCESS_INSTANCE_ID = 3;\n\n    @Mock\n    ProcessInstanceService processInstanceService;\n\n    @Mock\n    SProcessInstance sProcessInstance;\n\n    @Override\n    @Before\n    public void before() throws SBonitaException {\n        doReturn(ROOT_PROCESS_INSTANCE_ID).when(sProcessInstance).getRootProcessInstanceId();\n        doReturn(sProcessInstance).when(processInstanceService).getProcessInstance(PROCESS_INSTANCE_ID);\n\n        doReturn(processInstanceService).when(serviceAccessor).getProcessInstanceService();\n        txBonitaWork = spy(new ProcessInstanceContextWork(wrappedWork, PROCESS_INSTANCE_ID));\n        super.before();\n    }\n\n    @Test\n    public void handleFailureWithProcessInstanceId() throws Throwable {\n        final Map<String, Object> context = Collections.singletonMap(\"serviceAccessor\", serviceAccessor);\n        final SBonitaException e = new SBonitaException() {\n\n            private static final long serialVersionUID = -6748168976371554636L;\n        };\n\n        txBonitaWork.handleFailure(e, context);\n\n        assertTrue(e.getMessage().contains(\"PROCESS_INSTANCE_ID=\" + PROCESS_INSTANCE_ID));\n        assertTrue(e.getMessage().contains(\"ROOT_PROCESS_INSTANCE_ID=\" + ROOT_PROCESS_INSTANCE_ID));\n        verify(wrappedWork).handleFailure(e, context);\n    }\n\n    @Test\n    public void handleFailureWithProcessInstanceAndRootIds() throws Throwable {\n        txBonitaWork = spy(new ProcessInstanceContextWork(wrappedWork, PROCESS_INSTANCE_ID, 5));\n        final Map<String, Object> context = Collections.singletonMap(\"serviceAccessor\", serviceAccessor);\n        final SBonitaException e = new SBonitaException() {\n\n            private static final long serialVersionUID = -6748168976371554636L;\n        };\n\n        txBonitaWork.handleFailure(e, context);\n\n        assertTrue(e.getMessage().contains(\"PROCESS_INSTANCE_ID=\" + PROCESS_INSTANCE_ID));\n        assertTrue(e.getMessage().contains(\"ROOT_PROCESS_INSTANCE_ID=\" + 5));\n        verify(wrappedWork, times(1)).handleFailure(e, context);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/work/failurewrapping/TriggerSignalWorkTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.execution.work.failurewrapping;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.*;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SBPMEventType;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent;\nimport org.bonitasoft.engine.execution.event.EventsHandler;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Baptiste Mesta\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class TriggerSignalWorkTest {\n\n    public static final long UNREADABLE_SIGNAL_ID = 58923749333L;\n    private TriggerSignalWork triggerSignalWork;\n    private final String SIGNAL_NAME = \"theSignal\";\n    private final long SIGNAL_ID = 5643261L;\n    private Map<String, Object> context;\n\n    @Mock\n    private ServiceAccessor serviceAccessor;\n    @Mock\n    private EventsHandler eventsHandler;\n    @Mock\n    private EventInstanceService eventInstanceService;\n    private SWaitingSignalEvent waitingSignalEvent;\n\n    @Before\n    public void before() throws Exception {\n        context = new HashMap<>();\n        context.put(\"serviceAccessor\", serviceAccessor);\n        doReturn(eventInstanceService).when(serviceAccessor).getEventInstanceService();\n        doReturn(eventsHandler).when(serviceAccessor).getEventsHandler();\n        waitingSignalEvent = new SWaitingSignalEvent(SBPMEventType.EVENT_SUB_PROCESS, 654223L, \"proc\", 54362L,\n                \"flownode\", SIGNAL_NAME);\n        doThrow(SEventTriggerInstanceNotFoundException.class).when(eventInstanceService)\n                .getWaitingSignalEvent(anyLong());\n        doReturn(waitingSignalEvent).when(eventInstanceService).getWaitingSignalEvent(SIGNAL_ID);\n    }\n\n    @Test\n    public void should_trigger_catch_signal() throws Exception {\n        triggerSignalWork = new TriggerSignalWork(SIGNAL_ID, SIGNAL_NAME);\n        //when\n        triggerSignalWork.work(context);\n        //then\n        verify(eventsHandler).triggerCatchEvent(waitingSignalEvent, null);\n    }\n\n    @Test(expected = SEventTriggerInstanceNotFoundException.class)\n    public void should_throw_exception_when_signal_not_found() throws Exception {\n        triggerSignalWork = new TriggerSignalWork(5348934579L, SIGNAL_NAME);\n        //when\n        triggerSignalWork.work(context);\n    }\n\n    @Test(expected = SEventTriggerInstanceReadException.class)\n    public void should_throw_exception_when_we_are_unable_to_read_the_signal() throws Exception {\n        doThrow(SEventTriggerInstanceReadException.class).when(eventInstanceService)\n                .getWaitingSignalEvent(UNREADABLE_SIGNAL_ID);\n        triggerSignalWork = new TriggerSignalWork(UNREADABLE_SIGNAL_ID, SIGNAL_NAME);\n        //when\n        triggerSignalWork.work(context);\n    }\n\n    @Test\n    public void should_give_signal_name_in_recovery_procedure() {\n        //given\n        triggerSignalWork = new TriggerSignalWork(SIGNAL_ID, SIGNAL_NAME);\n        //when\n        String recoveryProcedure = triggerSignalWork.getRecoveryProcedure();\n        //then\n        assertThat(recoveryProcedure).contains(SIGNAL_NAME);\n    }\n\n    @Test(expected = UnsupportedOperationException.class)\n    public void should_throw_exception_when_handling_failure() throws Exception {\n        //given\n        triggerSignalWork = new TriggerSignalWork(SIGNAL_ID, SIGNAL_NAME);\n        //when\n        triggerSignalWork.handleFailure(new Exception(\"unexpected\"), context);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/expression/BusinessDataExpressionExecutorStrategyTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression;\n\nimport static java.util.Arrays.asList;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.data.instance.api.DataInstanceContainer.PROCESS_INSTANCE;\nimport static org.junit.Assert.fail;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.argThat;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.doThrow;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.nullable;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.mockito.Mockito.when;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.bonitasoft.engine.business.data.BusinessDataRetriever;\nimport org.bonitasoft.engine.business.data.RefBusinessDataRetriever;\nimport org.bonitasoft.engine.business.data.proxy.ServerProxyfier;\nimport org.bonitasoft.engine.commons.Container;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SExceptionContext;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.expression.model.impl.SExpressionImpl;\nimport org.bonitasoft.engine.operation.BusinessDataContext;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class BusinessDataExpressionExecutorStrategyTest {\n\n    @Mock\n    ServerProxyfier proxyfier;\n\n    @Mock\n    private BusinessDataRetriever businessDataRetriever;\n\n    @Mock\n    private RefBusinessDataRetriever refBusinessDataRetriever;\n\n    @InjectMocks\n    private BusinessDataExpressionExecutorStrategy businessDataExpressionExecutorStrategy;\n\n    private SimpleBizData createAbizDataInRepository() throws Exception {\n        return createAbizDataInRepository(98L);\n    }\n\n    private SimpleBizData createAbizDataInRepository(final long bizDataId) throws Exception {\n        return new SimpleBizData(bizDataId);\n    }\n\n    private SRefBusinessDataInstance createARefBizDataInRepository(final SimpleBizData bizData,\n            final long processInstanceId) throws Exception {\n        return createARefBizDataInRepository(bizData, \"bizDataName\", processInstanceId);\n    }\n\n    private SRefBusinessDataInstance createARefBizDataInRepository(final SimpleBizData bizData,\n            final String bizDataName, final long processInstanceId)\n            throws Exception {\n        final SProcessSimpleRefBusinessDataInstance refBizData = new SProcessSimpleRefBusinessDataInstance();\n        refBizData.setDataClassName(bizData.getClass().getName());\n        refBizData.setDataId(bizData.getId());\n        refBizData.setProcessInstanceId(processInstanceId);\n        refBizData.setName(bizDataName);\n        doReturn(refBizData).when(refBusinessDataRetriever)\n                .getRefBusinessDataInstance(\n                        argThat(ctx -> ctx.getName().equals(bizDataName) && ctx.getContainer().equals(\n                                new Container(processInstanceId, PROCESS_INSTANCE.name()))));\n        doReturn(bizData).when(businessDataRetriever).getBusinessData(\n                argThat(ref -> Objects.equals(((SSimpleRefBusinessDataInstance) ref).getDataId(), bizData.getId())));\n        return refBizData;\n    }\n\n    private SRefBusinessDataInstance createAMultiRefBizDataInRepository(final SimpleBizData bizData,\n            final String bizDataName, final long processInstanceId)\n            throws Exception {\n        final SProcessMultiRefBusinessDataInstance refBizData = mock(SProcessMultiRefBusinessDataInstance.class);\n        when(refBizData.getName()).thenReturn(bizDataName);\n        when(refBusinessDataRetriever.getRefBusinessDataInstance(any())).thenReturn(refBizData);\n        given(businessDataRetriever.getBusinessData(refBizData)).willReturn(Arrays.asList(bizData));\n        return refBizData;\n    }\n\n    private HashMap<String, Object> buildBusinessDataExpressionContext(final long containerId,\n            final DataInstanceContainer containerType) {\n        final HashMap<String, Object> context = new HashMap<>();\n        context.put(SExpressionContext.CONTAINER_ID_KEY, containerId);\n        context.put(SExpressionContext.CONTAINER_TYPE_KEY, containerType.name());\n        return context;\n    }\n\n    private SExpressionImpl buildBusinessDataExpression(final String content) {\n        final SExpressionImpl expression = new SExpressionImpl();\n        expression.setContent(content);\n        expression.setReturnType(\n                \"com.bonitasoft.engine.expression.BusinessDataExpressionExecutorStrategyTest.LeaveRequest\");\n        expression.setExpressionType(ExpressionType.TYPE_BUSINESS_DATA.name());\n        return expression;\n    }\n\n    @Test\n    public void should_be_a_business_data_expression_kind_strategy() {\n        assertThat(businessDataExpressionExecutorStrategy.getExpressionKind())\n                .isEqualTo(ExpressionExecutorStrategy.KIND_BUSINESS_DATA);\n    }\n\n    @Test\n    public void evaluate_on_a_process_instance_should_return_biz_data_instance_corresponding_to_data_name_and_processInstance_id()\n            throws Exception {\n        final SimpleBizData expectedBizData = createAbizDataInRepository();\n        final long processInstanceId = 1L;\n        final SRefBusinessDataInstance refBizData = createARefBizDataInRepository(expectedBizData, processInstanceId);\n        final HashMap<String, Object> context = buildBusinessDataExpressionContext(processInstanceId, PROCESS_INSTANCE);\n        final SExpressionImpl buildBusinessDataExpression = buildBusinessDataExpression(refBizData.getName());\n\n        final Entity fetchedBizData = (Entity) businessDataExpressionExecutorStrategy.evaluate(\n                buildBusinessDataExpression, context, null,\n                ContainerState.ACTIVE);\n\n        assertThat(unProxyfyIdNeeded(fetchedBizData)).isEqualTo(expectedBizData);\n    }\n\n    @Test(expected = SExpressionEvaluationException.class)\n    public void evaluate_on_a_process_instance_should_throw_an_exception_when_a_read_exception_occurs()\n            throws Exception {\n        final SimpleBizData expectedBizData = createAbizDataInRepository();\n        final long processInstanceId = 1L;\n        final SRefBusinessDataInstance refBizData = createARefBizDataInRepository(expectedBizData, processInstanceId);\n        final HashMap<String, Object> context = buildBusinessDataExpressionContext(processInstanceId, PROCESS_INSTANCE);\n        final SExpressionImpl buildBusinessDataExpression = buildBusinessDataExpression(refBizData.getName());\n\n        doThrow(new SBonitaReadException(\"internal error\")).when(refBusinessDataRetriever)\n                .getRefBusinessDataInstance(any());\n\n        final Object fetchedBizData = businessDataExpressionExecutorStrategy.evaluate(buildBusinessDataExpression,\n                context, null, ContainerState.ACTIVE);\n\n        assertThat(fetchedBizData).isEqualTo(expectedBizData);\n    }\n\n    @Test\n    public void evaluate_on_a_process_instance_should_return_list_of_biz_data_instance_corresponding_to_data_name_and_processInstance()\n            throws Exception {\n        final SimpleBizData bizData = new SimpleBizData(457L);\n        final long proccessInstanceId = 1L;\n        final SRefBusinessDataInstance refBizData = createAMultiRefBizDataInRepository(bizData, \"bizData\",\n                proccessInstanceId);\n        final HashMap<String, Object> context = buildBusinessDataExpressionContext(proccessInstanceId,\n                PROCESS_INSTANCE);\n        final SExpressionImpl buildBusinessDataExpression = buildBusinessDataExpression(refBizData.getName());\n\n        @SuppressWarnings(\"unchecked\")\n        final List<Entity> fetchedBizDataList = (List<Entity>) businessDataExpressionExecutorStrategy.evaluate(\n                buildBusinessDataExpression, context, null,\n                ContainerState.ACTIVE);\n\n        assertThat(unProxyfyIdNeeded(fetchedBizDataList.get(0))).isEqualTo(bizData);\n    }\n\n    @Test\n    public void failingEvaluateShouldPutProcessInstanceIdInExceptionContext() throws Exception {\n        final SimpleBizData expectedBizData = createAbizDataInRepository();\n        final long proccessInstanceId = 1564L;\n        final SRefBusinessDataInstance refBizData = createARefBizDataInRepository(expectedBizData, proccessInstanceId);\n        final HashMap<String, Object> context = buildBusinessDataExpressionContext(proccessInstanceId,\n                PROCESS_INSTANCE);\n        final SExpressionImpl buildBusinessDataExpression = buildBusinessDataExpression(refBizData.getName());\n        doThrow(new SRefBusinessDataInstanceNotFoundException(444L, \"toto\")).when(refBusinessDataRetriever)\n                .getRefBusinessDataInstance(\n                        any(BusinessDataContext.class));\n\n        try {\n            businessDataExpressionExecutorStrategy.evaluate(buildBusinessDataExpression, context, null,\n                    ContainerState.ACTIVE);\n            fail(\"should throw Exception\");\n        } catch (final SBonitaException e) {\n            assertThat(((SBonitaException) e.getCause()).getContext().get(SExceptionContext.PROCESS_INSTANCE_ID))\n                    .isEqualTo(proccessInstanceId);\n        }\n    }\n\n    private SimpleBizData unProxyfyIdNeeded(final Entity fetchedBizData) {\n        final SimpleBizData unProxyfyIfNeeded;\n        unProxyfyIfNeeded = (SimpleBizData) ServerProxyfier.unProxifyIfNeeded(fetchedBizData);\n        return unProxyfyIfNeeded;\n    }\n\n    @Test\n    public void evaluate_should_first_check_if_expression_already_evaluated_and_available_in_context()\n            throws Exception {\n        final HashMap<String, Object> context = new HashMap<>();\n        final SimpleBizData expectedBizData = new SimpleBizData(12L);\n        final String bizDataName = \"businessDataName\";\n        context.put(bizDataName, expectedBizData);\n        final SExpressionImpl buildBusinessDataExpression = buildBusinessDataExpression(bizDataName);\n\n        final Object fetchedBizData = businessDataExpressionExecutorStrategy.evaluate(buildBusinessDataExpression,\n                context, null, ContainerState.ACTIVE);\n\n        assertThat(fetchedBizData).isEqualTo(expectedBizData);\n        verifyNoInteractions(businessDataRetriever);\n        verifyNoInteractions(refBusinessDataRetriever);\n    }\n\n    @Test\n    public void evaluate_should_resolve_multiple_expressions() throws Exception {\n        final SimpleBizData firstBizData = createAbizDataInRepository(1L);\n        final SimpleBizData secondBizData = createAbizDataInRepository(2L);\n        final long processInstanceId = 6L;\n        final SRefBusinessDataInstance firstRefBizData = createARefBizDataInRepository(firstBizData, \"dataOne\",\n                processInstanceId);\n        final SRefBusinessDataInstance secondRefBizData = createARefBizDataInRepository(secondBizData, \"dataTwo\",\n                processInstanceId);\n        final HashMap<String, Object> context = buildBusinessDataExpressionContext(processInstanceId, PROCESS_INSTANCE);\n        final SExpression firstbuildBusinessDataExpression = buildBusinessDataExpression(firstRefBizData.getName());\n        final SExpression secondbuildBusinessDataExpression = buildBusinessDataExpression(secondRefBizData.getName());\n\n        final List<Object> fetchedBizDatas = businessDataExpressionExecutorStrategy.evaluate(\n                Arrays.asList(firstbuildBusinessDataExpression, secondbuildBusinessDataExpression), context, null,\n                ContainerState.ACTIVE);\n\n        assertThat(fetchedBizDatas).contains(firstBizData, secondBizData);\n    }\n\n    @Test\n    @SuppressWarnings(\"unchecked\")\n    public void evaluate_should_not_resolve_expression_with_same_content_as_already_resolved_one() throws Exception {\n        final SExpression firstbuildBusinessDataExpression = buildBusinessDataExpression(\"sameName\");\n        final SExpression secondbuildBusinessDataExpression = buildBusinessDataExpression(\"sameName\");\n        final BusinessDataExpressionExecutorStrategy strategy = spy(\n                new BusinessDataExpressionExecutorStrategy(refBusinessDataRetriever, businessDataRetriever));\n        final ContainerState active = ContainerState.ACTIVE;\n        doReturn(new Object()).when(strategy).evaluate(any(SExpression.class), nullable(Map.class), nullable(Map.class),\n                eq(active));\n\n        strategy.evaluate(asList(firstbuildBusinessDataExpression, secondbuildBusinessDataExpression), null, null,\n                active);\n\n        verify(strategy).evaluate(eq(firstbuildBusinessDataExpression), nullable(Map.class), nullable(Map.class),\n                eq(active));\n    }\n\n    @Test\n    public void evaluation_result_should_be_pushed_in_context() {\n        assertThat(businessDataExpressionExecutorStrategy.mustPutEvaluatedExpressionInContext()).isEqualTo(true);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/expression/BusinessDataReferenceExpressionExecutorStrategyTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.doThrow;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.business.data.BusinessDataReference;\nimport org.bonitasoft.engine.business.data.RefBusinessDataRetriever;\nimport org.bonitasoft.engine.business.data.impl.SimpleBusinessDataReferenceImpl;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SFlowNodeSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.model.impl.SExpressionImpl;\nimport org.bonitasoft.engine.operation.BusinessDataContext;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class BusinessDataReferenceExpressionExecutorStrategyTest {\n\n    @Mock\n    private RefBusinessDataRetriever refBusinessDataRetriever;\n\n    @InjectMocks\n    private BusinessDataReferenceExpressionExecutorStrategy businessDataExpressionExecutorStrategy;\n\n    @Test\n    public void should_be_a_business_data_expression_kind_strategy() {\n        assertThat(businessDataExpressionExecutorStrategy.getExpressionKind())\n                .isEqualTo(ExpressionExecutorStrategy.KIND_BUSINESS_DATA_REFERENCE);\n    }\n\n    @Test\n    public void should_evaluate_return_a_simple_ref_on_process() throws Exception {\n        final String bizDataName = \"BizDataName\";\n        final Map<String, Object> context = createContext(112l, \"PROCESS_INSTANCE\");\n        doReturn(createProcessSimpleDataReference(\"BizDataName\", 112l, \"com.objects.MyObject\", 150l))\n                .when(refBusinessDataRetriever)\n                .getRefBusinessDataInstance(\n                        any());\n\n        final Object evaluate = businessDataExpressionExecutorStrategy.evaluate(createExpression(bizDataName), context,\n                null, null);\n\n        assertThat(evaluate)\n                .isEqualTo(new SimpleBusinessDataReferenceImpl(\"BizDataName\", \"com.objects.MyObject\", 150l));\n    }\n\n    @Test\n    public void should_evaluate_return_a_simple_ref_on_activity() throws Exception {\n        final String bizDataName = \"BizDataName\";\n        final Map<String, Object> context = createContext(112l, \"ACTIVITY_INSTANCE\");\n        doReturn(createFlowNodeSimpleDataReference(\"BizDataName\", 112l, \"com.objects.MyObject\", 150l))\n                .when(refBusinessDataRetriever)\n                .getRefBusinessDataInstance(\n                        any());\n        final Object evaluate = businessDataExpressionExecutorStrategy.evaluate(createExpression(bizDataName), context,\n                null, null);\n\n        assertThat(evaluate)\n                .isEqualTo(new SimpleBusinessDataReferenceImpl(\"BizDataName\", \"com.objects.MyObject\", 150l));\n    }\n\n    @Test(expected = SExpressionEvaluationException.class)\n    public void should_evaluate_throw_evaluation_exception_when_not_found() throws Exception {\n        final String bizDataName = \"BizDataName\";\n        final Map<String, Object> context = createContext(112l, \"ACTIVITY_INSTANCE\");\n        doThrow(new SRefBusinessDataInstanceNotFoundException(112l, \"exception\")).when(refBusinessDataRetriever)\n                .getRefBusinessDataInstance(\n                        any(BusinessDataContext.class));\n        businessDataExpressionExecutorStrategy.evaluate(createExpression(bizDataName), context, null, null);\n    }\n\n    SProcessSimpleRefBusinessDataInstance createProcessSimpleDataReference(final String name,\n            final long processInstanceId, final String type, final long businessDataId) {\n        final SProcessSimpleRefBusinessDataInstance sProcessSimpleRefBusinessDataInstance = new SProcessSimpleRefBusinessDataInstance();\n        sProcessSimpleRefBusinessDataInstance.setName(name);\n        sProcessSimpleRefBusinessDataInstance.setProcessInstanceId(processInstanceId);\n        sProcessSimpleRefBusinessDataInstance.setDataClassName(type);\n        sProcessSimpleRefBusinessDataInstance.setDataId(businessDataId);\n        return sProcessSimpleRefBusinessDataInstance;\n    }\n\n    SFlowNodeSimpleRefBusinessDataInstance createFlowNodeSimpleDataReference(final String name,\n            final long flowNodeInstanceId, final String type, final long businessDataId) {\n        final SFlowNodeSimpleRefBusinessDataInstance sProcessSimpleRefBusinessDataInstance = new SFlowNodeSimpleRefBusinessDataInstance();\n        sProcessSimpleRefBusinessDataInstance.setName(name);\n        sProcessSimpleRefBusinessDataInstance.setFlowNodeInstanceId(flowNodeInstanceId);\n        sProcessSimpleRefBusinessDataInstance.setDataClassName(type);\n        sProcessSimpleRefBusinessDataInstance.setDataId(businessDataId);\n        return sProcessSimpleRefBusinessDataInstance;\n    }\n\n    Map<String, Object> createContext(final long id, final String type) {\n        final Map<String, Object> context = new HashMap<>();\n        context.put(SExpressionContext.CONTAINER_ID_KEY, id);\n        context.put(SExpressionContext.CONTAINER_TYPE_KEY, type);\n        return context;\n    }\n\n    SExpressionImpl createExpression(final String bizDataName) {\n        return new SExpressionImpl(\"theData\", bizDataName, ExpressionExecutorStrategy.TYPE_BUSINESS_DATA_REFERENCE,\n                BusinessDataReference.class.getName(), null, null);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/expression/BusinessObjectDAOExpressionStrategyTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.business.data.BusinessDataRepository;\nimport org.bonitasoft.engine.expression.model.impl.SExpressionImpl;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class BusinessObjectDAOExpressionStrategyTest {\n\n    public static final String ORG_BONITASOFT_ENGINE_EXPRESSION_DUMMY_SERVER_DAO = \"org.bonitasoft.engine.expression.DummyServerDAO\";\n    @Mock\n    private BusinessDataRepository businessDataRepository;\n\n    @InjectMocks\n    private BusinessObjectDAOExpressionStrategy businessObjectDAOExpressionStrategy;\n\n    @Test\n    public void instantiateDAOShouldWorkAndInjectBizDataRepo() throws Exception {\n        DummyServerDAO daoInstance = (DummyServerDAO) businessObjectDAOExpressionStrategy\n                .instantiateDAO(ORG_BONITASOFT_ENGINE_EXPRESSION_DUMMY_SERVER_DAO);\n\n        assertThat(daoInstance).isInstanceOf(DummyServerDAO.class);\n        assertThat(daoInstance.getBusinessDataRepository()).isNotNull();\n    }\n\n    @Test\n    public void getDAOServerImplementationShouldReturnProperImplemNameFromInterface() {\n        String implemClassName = businessObjectDAOExpressionStrategy\n                .getDAOServerImplementationFromInterface(\"org.package.MyObjectDAO\");\n\n        assertThat(implemClassName).isEqualTo(\"org.package.server.MyObjectDAOImpl\");\n    }\n\n    @Test\n    public void getDAOServerImplementationShouldReturnProperImplemNameForDefaultPackageName() {\n        String implemClassName = businessObjectDAOExpressionStrategy\n                .getDAOServerImplementationFromInterface(\"BusinessDAO\");\n\n        assertThat(implemClassName).isEqualTo(\"server.BusinessDAOImpl\");\n    }\n\n    @Test\n    public void evaluateShouldComputeImplementationClassName() throws Exception {\n        // given:\n        String daoClassName = \"SomeDAOInterfaceName\";\n        SExpressionImpl expression = new SExpressionImpl(\"name\", \"myDAO\", \"dummyExpressionType\", daoClassName, null,\n                null);\n        Map<String, Object> context = Collections.emptyMap();\n        BusinessObjectDAOExpressionStrategy spy = spy(businessObjectDAOExpressionStrategy);\n        doReturn(ORG_BONITASOFT_ENGINE_EXPRESSION_DUMMY_SERVER_DAO).when(spy)\n                .getDAOServerImplementationFromInterface(daoClassName);\n\n        // when:\n        spy.evaluate(expression, context, null, null);\n\n        // then:\n        verify(spy).getDAOServerImplementationFromInterface(daoClassName);\n    }\n\n    @Test\n    public void evaluateShouldInstantiateDAOImplementationClassName() throws Exception {\n        // given:\n        String daoClassName = \"SomeDAOInterfaceName\";\n        SExpressionImpl expression = new SExpressionImpl(\"name\", \"myDAO\", \"dummyExpressionType\", daoClassName, null,\n                null);\n        Map<String, Object> context = Collections.emptyMap();\n        BusinessObjectDAOExpressionStrategy spy = spy(businessObjectDAOExpressionStrategy);\n        String daoImplClassName = ORG_BONITASOFT_ENGINE_EXPRESSION_DUMMY_SERVER_DAO;\n        doReturn(daoImplClassName).when(spy).getDAOServerImplementationFromInterface(daoClassName);\n\n        // when:\n        spy.evaluate(expression, context, null, null);\n\n        // then:\n        verify(spy).instantiateDAO(daoImplClassName);\n    }\n\n    @Test\n    public void getExpressionKindShouldReturn_BUSINESS_OBJECT_DAO_kind() {\n        assertThat(businessObjectDAOExpressionStrategy.getExpressionKind())\n                .isEqualTo(ExpressionExecutorStrategy.KIND_BUSINESS_OBJECT_DAO);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/expression/ContractInputExpressionExecutorStrategyTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.anyLong;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.core.contract.data.ContractDataService;\nimport org.bonitasoft.engine.core.contract.data.SContractDataNotFoundException;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.expression.model.impl.SExpressionImpl;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ContractInputExpressionExecutorStrategyTest {\n\n    @Mock\n    private ContractDataService contractDataService;\n\n    @InjectMocks\n    private ContractInputExpressionExecutorStrategy strategy;\n\n    private Map<String, Object> buildInitialContext(final Long containerId) {\n        final Map<String, Object> context = new HashMap<String, Object>(2);\n        context.put(SExpressionContext.CONTAINER_ID_KEY, containerId);\n        context.put(SExpressionContext.CONTAINER_TYPE_KEY, DataInstanceContainer.ACTIVITY_INSTANCE.name());\n        return context;\n    }\n\n    private SExpression buildInputContractExpression(final String name, final Class<?> returnType) {\n        return new SExpressionImpl(name, name, ExpressionExecutorStrategy.TYPE_CONTRACT_INPUT, returnType.getName(),\n                null, null);\n    }\n\n    @Test\n    public void evaluateShouldReturnTheContractInput() throws Exception {\n        final Map<String, Object> inputs = new HashMap<String, Object>();\n        inputs.put(\"comment\", \"No way!\");\n        final SExpression expression = buildInputContractExpression(\"comment\", String.class);\n        final Map<String, Object> context = buildInitialContext(465465L);\n        when(contractDataService.getUserTaskDataValue(465465L, \"comment\")).thenReturn(\"No way!\");\n\n        final String result = (String) strategy.evaluate(expression, context, null, null);\n        assertThat(result).isEqualTo(\"No way!\");\n    }\n\n    @Test(expected = SExpressionEvaluationException.class)\n    public void evaluateShouldThrowAnExceptionInputNotFound() throws Exception {\n        final SExpression expression = buildInputContractExpression(\"comment\", String.class);\n        final Map<String, Object> context = buildInitialContext(465465L);\n        when(contractDataService.getUserTaskDataValue(465465L, \"comment\"))\n                .thenThrow(new SContractDataNotFoundException(\"exception\"));\n\n        strategy.evaluate(expression, context, null, null);\n    }\n\n    @Test(expected = SExpressionEvaluationException.class)\n    public void should_evaluate_throw_exception_when_no_containerId() throws Exception {\n        final SExpression expression = buildInputContractExpression(\"comment\", String.class);\n        final Map<String, Object> context = buildInitialContext(null);\n\n        strategy.evaluate(expression, context, null, null);\n    }\n\n    @Test(expected = SExpressionEvaluationException.class)\n    public void evaluateShouldThrowAnExceptionDuetoServiceFailure() throws Exception {\n        final SExpression expression = buildInputContractExpression(\"comment\", String.class);\n        final Map<String, Object> context = buildInitialContext(465465L);\n        when(contractDataService.getUserTaskDataValue(465465L, \"comment\"))\n                .thenThrow(new SBonitaReadException(\"exception\"));\n\n        strategy.evaluate(expression, context, null, null);\n    }\n\n    @Test\n    public void getExpressionKindShouldReturnAContractInputKind() throws Exception {\n        assertThat(strategy.getExpressionKind()).isEqualTo(ExpressionExecutorStrategy.KIND_CONTRACT_INPUT);\n    }\n\n    @Test\n    public void validateShouldDoNothing() throws Exception {\n        final SExpression expression = buildInputContractExpression(\"comment\", String.class);\n        strategy.validate(expression);\n\n        verify(contractDataService, never()).getUserTaskDataValue(anyLong(), anyString());\n    }\n\n    @Test\n    public void mustPutEvaluatedExpressionInContext() throws Exception {\n        assertThat(strategy.mustPutEvaluatedExpressionInContext()).isTrue();\n    }\n\n    @Test\n    public void evaluateShouldReturnTheContractInputs() throws Exception {\n        final Map<String, Object> inputs = new HashMap<String, Object>();\n        inputs.put(\"comment\", \"No way!\");\n        inputs.put(\"isValid\", false);\n        final SExpression expression1 = buildInputContractExpression(\"comment\", String.class);\n        final SExpression expression2 = buildInputContractExpression(\"isValid\", Boolean.class);\n        final List<SExpression> expressions = Arrays.asList(expression1, expression2);\n        final Map<String, Object> context = buildInitialContext(465465L);\n        when(contractDataService.getUserTaskDataValue(465465L, \"comment\")).thenReturn(\"No way!\");\n        when(contractDataService.getUserTaskDataValue(465465L, \"isValid\")).thenReturn(false);\n\n        final List<Object> results = strategy.evaluate(expressions, context, null, null);\n        assertThat(results).hasSize(2);\n        assertThat(results.get(0)).isEqualTo(\"No way!\");\n        assertThat((Boolean) results.get(1)).isFalse();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/expression/DataExpressionExecutorStrategyTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression;\n\nimport static org.mockito.Mockito.when;\n\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\n\nimport org.bonitasoft.engine.data.instance.api.DataInstanceService;\nimport org.bonitasoft.engine.data.instance.api.ParentContainerResolver;\nimport org.bonitasoft.engine.data.instance.exception.SDataInstanceException;\nimport org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@RunWith(MockitoJUnitRunner.class)\n@SuppressWarnings(\"javadoc\")\npublic class DataExpressionExecutorStrategyTest {\n\n    @Mock\n    private DataInstanceService dataService;\n\n    @Mock\n    private List<SExpression> expressionList;\n\n    @Mock\n    private SExpression expression;\n\n    @Mock\n    private ParentContainerResolver parentContainerResolver;\n\n    @Mock\n    private Iterator<SExpression> exprIterator;\n\n    @InjectMocks\n    private DataExpressionExecutorStrategy dataExpressionExecutorStrategy;\n\n    @Test\n    public void evaluateListOfEmptyDataExpressionDoesNotThrowException()\n            throws SExpressionDependencyMissingException, SExpressionEvaluationException,\n            SDataInstanceException {\n        when(expressionList.size()).thenReturn(1);\n        when(expressionList.iterator()).thenReturn(exprIterator);\n        when(exprIterator.hasNext()).thenReturn(false);\n        final HashMap<String, Object> dependencyValues = new HashMap<>(2);\n        dependencyValues.put(\"containerId\", 17L);\n        dependencyValues.put(\"containerType\", \"process\");\n        dataExpressionExecutorStrategy.evaluate(expressionList, dependencyValues, new HashMap<Integer, Object>(0),\n                ContainerState.ACTIVE);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/expression/DocumentListReferenceExpressionExecutorStrategyTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression;\n\nimport static java.util.Arrays.asList;\nimport static java.util.Collections.emptyList;\nimport static java.util.Collections.singletonList;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.expression.ContainerState.ACTIVE;\nimport static org.mockito.Mockito.*;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.document.Document;\nimport org.bonitasoft.engine.bpm.document.impl.DocumentImpl;\nimport org.bonitasoft.engine.core.document.api.DocumentService;\nimport org.bonitasoft.engine.core.document.model.SMappedDocument;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.core.process.definition.model.SDocumentListDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SDocumentListDefinitionImpl;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.expression.model.impl.SExpressionImpl;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class DocumentListReferenceExpressionExecutorStrategyTest {\n\n    @Mock\n    private DocumentService documentService;\n    @Mock\n    private ActivityInstanceService activityInstanceService;\n    @Mock\n    private ProcessInstanceService processInstanceService;\n    @Mock\n    private ProcessDefinitionService processDefinitionService;\n\n    @InjectMocks\n    private DocumentListReferenceExpressionExecutorStrategy strategy;\n\n    @Test\n    public void evaluate_result_should_retrieve_documents_list_when_it_is_not_already_in_the_context()\n            throws Exception {\n        //given:\n        final Map<String, Object> context = new HashMap<>();\n        context.put(\"containerId\", 123L);\n        context.put(\"containerType\", \"PROCESS_INSTANCE\");\n\n        final DocumentListReferenceExpressionExecutorStrategy spiedStrategy = spy(strategy);\n        final List<DocumentImpl> expectedDocumentList = singletonList(new DocumentImpl());\n        doReturn(expectedDocumentList).when(spiedStrategy).getDocumentList(anyLong(), anyString(), eq(null));\n\n        //when:\n        final List<Object> result = spiedStrategy.evaluate(asList(expressionForDocumentList()), context, null, ACTIVE);\n\n        //then:\n        assertThat(result).hasSize(1).containsOnly(expectedDocumentList);\n        verifyNoInteractions(activityInstanceService);\n    }\n\n    @Test\n    public void evaluate_should_directly_return_the_documents_list_when_it_is_already_in_the_context()\n            throws Exception {\n        //given:\n        final Map<String, Object> context = new HashMap<>();\n        final List<DocumentImpl> documentListInContext = singletonList(new DocumentImpl());\n        final String docListName = \"docListPreviouslyPutInContext\";\n        context.put(docListName, documentListInContext);\n\n        context.put(\"containerId\", 365498L);\n        context.put(\"containerType\", \"PROCESS_INSTANCE\");\n\n        //when:\n        final Object result = strategy.evaluate(expressionForDocumentList(docListName), context, null, ACTIVE);\n\n        //then:\n        assertThat(result).isSameAs(documentListInContext);\n        verifyNoInteractions(documentService);\n    }\n\n    // =================================================================================================================\n    // implementation detail methods tests\n    // =================================================================================================================\n\n    @Test\n    public void should_getDocument_return_empty() throws Exception {\n        //given\n        doReturn(emptyList()).when(documentService).getDocumentList(\"theList\", 45l, 0, 100);\n        initDefinition(\"theList\");\n\n        //when\n        final List<Document> theList = strategy.getDocumentList(45l, \"theList\", null);\n\n        assertThat(theList).isEmpty();\n    }\n\n    @Test\n    public void should_getDocument_return_null() throws Exception {\n        //given\n        doReturn(emptyList()).when(documentService).getDocumentList(\"theList\", 45l, 0, 100);\n        initDefinition(\"notTheList\");\n\n        //when\n        final List<Document> theList = strategy.getDocumentList(45l, \"theList\", null);\n\n        assertThat(theList).isNull();\n    }\n\n    @Test\n    public void should_getDocument_return_theList() throws Exception {\n        //given\n        doReturn(singletonList(mock(SMappedDocument.class))).when(documentService).getDocumentList(\"theList\", 45l, 0,\n                100);\n\n        //when\n        final List<Document> theList = strategy.getDocumentList(45l, \"theList\", null);\n\n        assertThat(theList).hasSize(1);\n    }\n\n    // =================================================================================================================\n    // UTILS\n    // =================================================================================================================\n\n    private void initDefinition(final String... names)\n            throws SProcessInstanceNotFoundException, SProcessInstanceReadException,\n            SProcessDefinitionNotFoundException,\n            SBonitaReadException {\n        final SProcessInstance processInstance = mock(SProcessInstance.class);\n        doReturn(154l).when(processInstance).getProcessDefinitionId();\n        doReturn(processInstance).when(processInstanceService).getProcessInstance(45l);\n\n        final SProcessDefinition processDefinition = mock(SProcessDefinition.class);\n        final SFlowElementContainerDefinition flowElementContainerDefinition = mock(\n                SFlowElementContainerDefinition.class);\n        doReturn(flowElementContainerDefinition).when(processDefinition).getProcessContainer();\n        doReturn(createListOfDocumentListDefinition(names)).when(flowElementContainerDefinition)\n                .getDocumentListDefinitions();\n        doReturn(processDefinition).when(processDefinitionService).getProcessDefinition(154l);\n    }\n\n    private static List<SDocumentListDefinition> createListOfDocumentListDefinition(final String... names) {\n        final List<SDocumentListDefinition> list = new ArrayList<>();\n        for (final String name : names) {\n            list.add(new SDocumentListDefinitionImpl(name));\n        }\n        return list;\n    }\n\n    private static SExpressionImpl expressionForDocumentList(final String content) {\n        final SExpressionImpl expression = new SExpressionImpl();\n        expression.setContent(content);\n        expression.setReturnType(List.class.getName());\n        expression.setExpressionType(ExpressionType.TYPE_DOCUMENT_LIST.name());\n        return expression;\n    }\n\n    private static SExpressionImpl expressionForDocumentList() {\n        return expressionForDocumentList(\"a_name_i_dont_care_about\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/expression/DocumentReferenceExpressionExecutorStrategyTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression;\n\nimport static java.util.Arrays.asList;\nimport static java.util.Collections.*;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.expression.ContainerState.ACTIVE;\nimport static org.bonitasoft.engine.service.ModelConvertor.toDocument;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.document.Document;\nimport org.bonitasoft.engine.bpm.document.impl.DocumentImpl;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.core.document.api.DocumentService;\nimport org.bonitasoft.engine.core.document.model.SMappedDocument;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance;\nimport org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.expression.model.impl.SExpressionImpl;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Vincent Elcrin\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class DocumentReferenceExpressionExecutorStrategyTest {\n\n    public static final long PROCESS_INSTANCE_ID = 1L;\n    private static final long PARENT_PROCESS_INSTANCE_ID = 2L;\n    private static final long A_LONG_TIME_AGO = 1234L;\n\n    @Mock\n    private DocumentService documentService;\n    @Mock\n    private ActivityInstanceService activityInstanceService;\n    @InjectMocks\n    private DocumentReferenceExpressionExecutorStrategy strategy;\n\n    @Mock\n    private SMappedDocument document;\n    @Mock\n    private SMappedDocument parentDocument;\n    @Mock\n    private SMappedDocument archivedDocument;\n\n    @Before\n    public void setUp() throws Exception {\n        final SFlowNodeInstance flowNodeInstance = mock(SFlowNodeInstance.class);\n        doReturn(flowNodeInstance).when(activityInstanceService).getFlowNodeInstance(PROCESS_INSTANCE_ID);\n        doReturn(PARENT_PROCESS_INSTANCE_ID).when(flowNodeInstance).getParentProcessInstanceId();\n        doReturn(document).when(documentService).getMappedDocument(eq(PROCESS_INSTANCE_ID), nullable(String.class));\n        doReturn(parentDocument).when(documentService).getMappedDocument(eq(PARENT_PROCESS_INSTANCE_ID),\n                nullable(String.class));\n        doReturn(archivedDocument).when(documentService).getMappedDocument(eq(PROCESS_INSTANCE_ID),\n                nullable(String.class), eq(A_LONG_TIME_AGO));\n    }\n\n    @Test(expected = SExpressionDependencyMissingException.class)\n    public void evaluate_should_throw_an_exception_when_container_id_is_null() throws Exception {\n        strategy.evaluate(Collections.emptyList(), emptyMap(), null, ACTIVE);\n    }\n\n    @Test(expected = SExpressionDependencyMissingException.class)\n    public void evaluate_should_throw_an_exception_when_container_type_is_null() throws Exception {\n        strategy.evaluate(Collections.emptyList(), singletonMap(\"containerId\", PROCESS_INSTANCE_ID), null,\n                ACTIVE);\n    }\n\n    @Test\n    public void evaluate_result_should_contains_process_document_when_container_is_a_process_instance()\n            throws Exception {\n        final Map<String, Object> context = new HashMap<>();\n        context.put(\"containerId\", PROCESS_INSTANCE_ID);\n        context.put(\"containerType\", \"PROCESS_INSTANCE\");\n\n        final List<Object> result = strategy.evaluate(asList(mock(SExpression.class)), context, null, ACTIVE);\n\n        assertThat(result).hasSize(1).contains(toDocument(document, documentService));\n    }\n\n    @Test\n    public void evaluate_result_should_contains_parent_process_document_when_container_is_not_a_process_instance()\n            throws Exception {\n        final Map<String, Object> context = new HashMap<>();\n        context.put(\"containerId\", PROCESS_INSTANCE_ID);\n        context.put(\"containerType\", \"OTHER\");\n\n        final List<Object> result = strategy.evaluate(asList(mock(SExpression.class)), context, null, ACTIVE);\n\n        assertThat(result).hasSize(1).contains(toDocument(parentDocument, documentService));\n    }\n\n    @Test\n    public void evaluate_result_should_contains_null_when_document_can_not_be_found_for_a_process_instance()\n            throws Exception {\n        doThrow(SObjectNotFoundException.class).when(documentService).getMappedDocument(eq(PROCESS_INSTANCE_ID),\n                nullable(String.class));\n        final Map<String, Object> context = new HashMap<>();\n        context.put(\"containerId\", PROCESS_INSTANCE_ID);\n        context.put(\"containerType\", \"PROCESS_INSTANCE\");\n\n        final List<Object> result = strategy.evaluate(asList(mock(SExpression.class)), context, null, ACTIVE);\n\n        assertThat(result).hasSize(1).contains((Document) null);\n    }\n\n    @Test\n    public void evaluate_result_should_contains_null_when_document_can_not_be_found_for_a_parent_process_instance()\n            throws Exception {\n        doThrow(SObjectNotFoundException.class).when(documentService).getMappedDocument(eq(PARENT_PROCESS_INSTANCE_ID),\n                nullable(String.class));\n        final Map<String, Object> context = new HashMap<>();\n        context.put(\"containerId\", PROCESS_INSTANCE_ID);\n        context.put(\"containerType\", \"OTHER\");\n\n        final List<Object> result = strategy.evaluate(asList(mock(SExpression.class)), context, null, ACTIVE);\n\n        assertThat(result).hasSize(1).contains((Document) null);\n    }\n\n    @Test\n    public void evaluate_result_should_contains_archived_document_when_a_time_is_defined() throws Exception {\n        final Map<String, Object> context = new HashMap<>();\n        context.put(\"containerId\", PROCESS_INSTANCE_ID);\n        context.put(\"containerType\", \"PROCESS_INSTANCE\");\n        context.put(\"time\", A_LONG_TIME_AGO);\n\n        final List<Object> result = strategy.evaluate(singletonList(mock(SExpression.class)), context,\n                null, ACTIVE);\n\n        assertThat(result).hasSize(1).contains(toDocument(archivedDocument, documentService));\n    }\n\n    @Test\n    public void evaluate_should_directly_return_the_document_when_it_is_already_in_the_context() throws Exception {\n        //given:\n        final Map<String, Object> context = new HashMap<>();\n        final DocumentImpl docInContext = new DocumentImpl();\n        final String docName = \"docPreviouslyPutInContext\";\n        context.put(docName, docInContext);\n\n        context.put(\"containerId\", PROCESS_INSTANCE_ID);\n        context.put(\"containerType\", \"PROCESS_INSTANCE\");\n        context.put(\"time\", A_LONG_TIME_AGO);\n\n        //when:\n        final Object result = strategy.evaluate(expressionForDocument(docName), context, null,\n                ACTIVE);\n\n        //then:\n        assertThat(result).isSameAs(docInContext);\n        verifyNoInteractions(documentService);\n    }\n\n    @Test\n    public void getProcessInstance_should_query_archives_if_time_is_set() throws Exception {\n        final long containerId = 123456L;\n        final String containerType = \"ACTIVITY_INSTANCE\";\n        final SAActivityInstance activityInstance = mock(SAActivityInstance.class);\n        doReturn(activityInstance).when(activityInstanceService).getMostRecentArchivedActivityInstance(containerId);\n        final long processInstanceId = 99998888777L;\n        doReturn(processInstanceId).when(activityInstance).getParentProcessInstanceId();\n\n        final long retrievedPIId = strategy.getProcessInstance(containerId, containerType, A_LONG_TIME_AGO);\n\n        verify(activityInstanceService).getMostRecentArchivedActivityInstance(containerId);\n        assertThat(retrievedPIId).isEqualTo(processInstanceId);\n    }\n\n    // =================================================================================================================\n    // UTILS\n    // =================================================================================================================\n\n    private static SExpressionImpl expressionForDocument(final String content) {\n        final SExpressionImpl expression = new SExpressionImpl();\n        expression.setContent(content);\n        expression.setReturnType(DocumentImpl.class.getName());\n        expression.setExpressionType(ExpressionType.TYPE_DOCUMENT.name());\n        return expression;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/expression/DummyServerDAO.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression;\n\nimport org.bonitasoft.engine.business.data.BusinessDataRepository;\n\npublic class DummyServerDAO {\n\n    private final BusinessDataRepository businessDataRepository;\n\n    public DummyServerDAO(final BusinessDataRepository businessDataRepository) {\n        this.businessDataRepository = businessDataRepository;\n    }\n\n    public BusinessDataRepository getBusinessDataRepository() {\n        return businessDataRepository;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/expression/EngineConstantExpressionExecutorStrategyTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.expression.ExpressionConstants.*;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\nimport static org.mockito.ArgumentMatchers.anyInt;\nimport static org.mockito.ArgumentMatchers.anyLong;\nimport static org.mockito.Mockito.*;\n\nimport java.io.Serializable;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.connector.EngineExecutionContext;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException;\nimport org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.impl.SExpressionImpl;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class EngineConstantExpressionExecutorStrategyTest {\n\n    private static final long CONTAINER_ID = 156L;\n    private static final long USER_ID = 219047231L;\n\n    private Map<String, Object> dependencies;\n\n    @Mock\n    private ActivityInstanceService activityInstanceService;\n    @Mock\n    private SessionService sessionService;\n    @Mock\n    private ProcessInstanceService ProcessInstanceService;\n    @Mock\n    private SessionAccessor sessionAccessor;\n    private SUserTaskInstance taskInstance;\n    @InjectMocks\n    private EngineConstantExpressionExecutorStrategy strategy;\n    private SExpressionImpl expression = new SExpressionImpl();\n    public static final long PARENT_PROCESS_INSTANCE_ID = 123456L;\n\n    @Before\n    public void setUp() throws SFlowNodeReadException, SFlowNodeNotFoundException {\n        dependencies = new HashMap<>(2);\n        dependencies.put(SExpressionContext.CONTAINER_ID_KEY, CONTAINER_ID);\n        dependencies.put(SExpressionContext.CONTAINER_TYPE_KEY, DataInstanceContainer.ACTIVITY_INSTANCE.name());\n        taskInstance = new SUserTaskInstance();\n        when(activityInstanceService.getFlowNodeInstance(CONTAINER_ID)).thenReturn(taskInstance);\n        taskInstance.setId(CONTAINER_ID);\n        taskInstance.setParentContainerId(PARENT_PROCESS_INSTANCE_ID);\n        taskInstance.setParentContainerId(PARENT_PROCESS_INSTANCE_ID);\n    }\n\n    @Test\n    public void taskAssigneeUserTask() throws Exception {\n        taskAssigneeOnHumanTask(SFlowNodeType.USER_TASK, mock(SUserTaskInstance.class));\n\n    }\n\n    @Test\n    public void taskAssigneeManualTask() throws Exception {\n        taskAssigneeOnHumanTask(SFlowNodeType.MANUAL_TASK, mock(SManualTaskInstance.class));\n\n    }\n\n    private void taskAssigneeOnHumanTask(final SFlowNodeType flowNodeType, final SHumanTaskInstance humanTaskInstance)\n            throws SExpressionEvaluationException,\n            SFlowNodeNotFoundException, SFlowNodeReadException {\n        when(activityInstanceService.getFlowNodeInstance(CONTAINER_ID)).thenReturn(humanTaskInstance);\n        expression.setContent(TASK_ASSIGNEE_ID.getEngineConstantName());\n\n        final long taskAssigneeId = 10L;\n        when(humanTaskInstance.getAssigneeId()).thenReturn(taskAssigneeId);\n        when(humanTaskInstance.getType()).thenReturn(flowNodeType);\n\n        final long evaluatedTaskAssigneeId = (Long) strategy.evaluate(expression, dependencies,\n                Collections.<Integer, Object> emptyMap(),\n                ContainerState.ACTIVE);\n        assertEquals(taskAssigneeId, evaluatedTaskAssigneeId);\n    }\n\n    @Test\n    public void taskAssigneeAutomaticTask() throws Exception {\n\n        when(activityInstanceService.getFlowNodeInstance(CONTAINER_ID)).thenReturn(mock(SAutomaticTaskInstance.class));\n        expression.setContent(TASK_ASSIGNEE_ID.getEngineConstantName());\n\n        final long evaluatedTaskAssigneeId = (Long) strategy.evaluate(expression, dependencies,\n                Collections.<Integer, Object> emptyMap(),\n                ContainerState.ACTIVE);\n        assertEquals(-1L, evaluatedTaskAssigneeId);\n\n    }\n\n    @Test\n    public void taskAssigneeProcessInstance() throws Exception {\n        dependencies.put(SExpressionContext.CONTAINER_TYPE_KEY, DataInstanceContainer.PROCESS_INSTANCE.name());\n        expression.setContent(TASK_ASSIGNEE_ID.getEngineConstantName());\n\n        final Serializable evaluatedTaskAssigneeId = strategy.evaluate(expression, dependencies,\n                Collections.<Integer, Object> emptyMap(),\n                ContainerState.ACTIVE);\n        assertEquals(-1L, evaluatedTaskAssigneeId);\n\n    }\n\n    @Test\n    public void taskAssigneeIdInEngineExecutionContext() throws Exception {\n        final SUserTaskInstance taskInstance = mock(SUserTaskInstance.class);\n\n        when(activityInstanceService.getActivityInstance(CONTAINER_ID)).thenReturn(taskInstance);\n        expression.setContent(ENGINE_EXECUTION_CONTEXT.getEngineConstantName());\n\n        final long taskAssigneeId = 10L;\n        when(taskInstance.getAssigneeId()).thenReturn(taskAssigneeId);\n        when(taskInstance.getType()).thenReturn(SFlowNodeType.USER_TASK);\n\n        final EngineExecutionContext engineExecutionContext = (EngineExecutionContext) strategy.evaluate(expression,\n                dependencies,\n                Collections.<Integer, Object> emptyMap(), ContainerState.ACTIVE);\n        assertEquals(taskAssigneeId, engineExecutionContext.getTaskAssigneeId());\n\n    }\n\n    @Test\n    public void taskAssigneeIdInEngineExecutionContextAlreadyEvaluated() throws Exception {\n        expression.setContent(TASK_ASSIGNEE_ID.getEngineConstantName());\n\n        final Long taskAssigneeId = 10L;\n        final EngineExecutionContext engineExecutionContext = new EngineExecutionContext();\n        engineExecutionContext.setTaskAssigneeId(taskAssigneeId);\n        dependencies.put(ENGINE_EXECUTION_CONTEXT.getEngineConstantName(), engineExecutionContext);\n\n        final Long evaluatedTaskAssigneeId = (Long) strategy\n                .evaluate(expression, dependencies, Collections.<Integer, Object> emptyMap(), ContainerState.ACTIVE);\n        assertEquals(taskAssigneeId, evaluatedTaskAssigneeId);\n\n        verify(activityInstanceService, never()).getActivityInstance(anyLong());\n\n    }\n\n    @Test\n    public void processInstanceIdInEngineExecutionContextIsFound() throws Exception {\n        expression.setContent(PROCESS_INSTANCE_ID.getEngineConstantName());\n\n        final long processInstanceId = 187555L;\n        final EngineExecutionContext engineExecutionContext = new EngineExecutionContext();\n        engineExecutionContext.setProcessInstanceId(processInstanceId);\n        dependencies.put(ENGINE_EXECUTION_CONTEXT.getEngineConstantName(), engineExecutionContext);\n\n        final Long evaluatedProcessInstanceId = (Long) strategy.evaluate(expression, dependencies,\n                Collections.<Integer, Object> emptyMap(),\n                ContainerState.ACTIVE);\n        assertEquals(processInstanceId, evaluatedProcessInstanceId.longValue());\n\n    }\n\n    @Test\n    public void processInstanceIdNotFoundInEngineExecutionContext() throws Exception {\n        expression.setContent(PROCESS_INSTANCE_ID.getEngineConstantName());\n\n        final long processInstanceId = 799451L;\n\n        final Serializable noValue = strategy.evaluate(expression, new HashMap<String, Object>(0),\n                Collections.<Integer, Object> emptyMap(), ContainerState.ACTIVE);\n        assertFalse((\"\" + processInstanceId).equals(String.valueOf(noValue)));\n\n    }\n\n    @Test(expected = SExpressionEvaluationException.class)\n    public void engineConstantNotFound() throws Exception {\n        expression.setContent(\"unexisting_constant_value\");\n\n        strategy.evaluate(expression, dependencies, Collections.<Integer, Object> emptyMap(), ContainerState.ACTIVE);\n    }\n\n    @Test(expected = SInvalidExpressionException.class)\n    public void engineConstantWithValidationException() throws Exception {\n        expression.setContent(\"unexisting_constant_value\");\n\n        strategy.validate(expression);\n    }\n\n    @Test\n    public void fillDependenciesOnArchivedFlowNodeDoesNotThrowException() throws Exception {\n        final SAActivityInstance activityInstance = mock(SAActivityInstance.class);\n        when(activityInstanceService.getMostRecentArchivedActivityInstance(anyLong())).thenReturn(activityInstance);\n        when(activityInstance.getLogicalGroup(anyInt())).thenReturn(PARENT_PROCESS_INSTANCE_ID);\n        dependencies.put(\"time\", System.currentTimeMillis());\n\n        strategy.fillDependenciesFromFlowNodeInstance(dependencies, 54);\n        assertEquals(PARENT_PROCESS_INSTANCE_ID, dependencies.get(PROCESS_INSTANCE_ID.getEngineConstantName()));\n        assertEquals(PARENT_PROCESS_INSTANCE_ID, dependencies.get(ROOT_PROCESS_INSTANCE_ID.getEngineConstantName()));\n    }\n\n    @Test\n    public void should_evaluate_LOGGED_USER_ID_return_user_id_from_session() throws Exception {\n        expression.setContent(LOGGED_USER_ID.getEngineConstantName());\n        doReturn(USER_ID).when(sessionService).getLoggedUserFromSession(sessionAccessor);\n\n        Serializable loggerUserId = strategy.evaluate(expression, dependencies,\n                Collections.<Integer, Object> emptyMap(),\n                ContainerState.ACTIVE);\n        assertThat(loggerUserId).as(\"logged user id\").isEqualTo(USER_ID);\n    }\n\n    @Test\n    public void should_evaluate_LOGGED_USER_ID_return_user_id_from_task_assignee() throws Exception {\n        //given\n        expression.setContent(LOGGED_USER_ID.getEngineConstantName());\n        taskInstance.setAssigneeId(USER_ID);\n        //when\n        Serializable loggerUserId = strategy.evaluate(expression, dependencies,\n                Collections.<Integer, Object> emptyMap(),\n                ContainerState.ACTIVE);\n        //then\n        assertThat(loggerUserId).as(\"logged user id\").isEqualTo(USER_ID);\n    }\n\n    @Test\n    public void should_evaluate_LOGGED_USER_ID_return_minus1_when_exception() throws Exception {\n        //given\n        expression.setContent(LOGGED_USER_ID.getEngineConstantName());\n        doThrow(SFlowNodeReadException.class).when(activityInstanceService).getFlowNodeInstance(anyLong());\n        //when\n        Serializable loggerUserId = strategy.evaluate(expression, dependencies, Collections.emptyMap(),\n                ContainerState.ACTIVE);\n        //then\n        assertThat(loggerUserId).as(\"logged user id\").isEqualTo(-1L);\n    }\n\n    @Test\n    public void should_evaluate_LOGGED_USER_ID_return_what_is_in_context() throws Exception {\n        //given\n        expression.setContent(LOGGED_USER_ID.getEngineConstantName());\n        dependencies.put(TASK_ASSIGNEE_ID.getEngineConstantName(), USER_ID);\n        //when\n        Serializable loggerUserId = strategy.evaluate(expression, dependencies,\n                Collections.<Integer, Object> emptyMap(),\n                ContainerState.ACTIVE);\n        //then\n        assertThat(loggerUserId).as(\"logged user id\").isEqualTo(USER_ID);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/expression/ParameterExpressionExecutorStrategyTest.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression;\n\nimport static org.junit.jupiter.api.Assertions.*;\n\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.expression.model.builder.impl.SExpressionBuilderFactoryImpl;\nimport org.bonitasoft.engine.parameter.ParameterService;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.CsvSource;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\n@ExtendWith(MockitoExtension.class)\nclass ParameterExpressionExecutorStrategyTest {\n\n    @Mock\n    private ParameterService parameterService;\n\n    @ParameterizedTest\n    @CsvSource(value = { \"hello\", \"some_password\", \"Attention\", \"$hello1\" })\n    void validParameterExpressionContent(String content) throws SInvalidExpressionException {\n        var strategy = new ParameterExpressionExecutorStrategy(parameterService);\n\n        assertDoesNotThrow(() -> strategy.validate(parameterExpression(content)));\n    }\n\n    @ParameterizedTest\n    @CsvSource(value = { \"1hello\", \")hello\", \"(hello\", \"hel\\\\lo\", \"hel/lo\", \"switch\", \"for\" })\n    void invalidParameterExpressionContent(String content) {\n        var strategy = new ParameterExpressionExecutorStrategy(parameterService);\n\n        assertThrows(SInvalidExpressionException.class, () -> strategy.validate(parameterExpression(content)));\n    }\n\n    private static SExpression parameterExpression(String content) throws SInvalidExpressionException {\n        return new SExpressionBuilderFactoryImpl().createNewInstance()\n                .setExpressionType(ExpressionType.TYPE_PARAMETER.name())\n                .setName(content)\n                .setContent(content)\n                .setReturnType(String.class.getName())\n                .done();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/expression/QueryBusinessDataExpressionExecutorStrategyTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.bonitasoft.engine.business.data.BusinessDataRepository;\nimport org.bonitasoft.engine.business.data.NonUniqueResultException;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.expression.model.impl.SExpressionImpl;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class QueryBusinessDataExpressionExecutorStrategyTest {\n\n    @Mock\n    private BusinessDataRepository businessDataRepository;\n\n    @InjectMocks\n    private QueryBusinessDataExpressionExecutorStrategy strategy;\n\n    @Mock\n    private Entity entity;\n\n    private Map<String, Object> buildContext() {\n        final Map<String, Object> context = new HashMap<String, Object>();\n        context.put(\"containerId\", 784654l);\n        context.put(\"containerType\", \"activity\");\n        return context;\n    }\n\n    @Test\n    public void evaluation_result_should_not__be_pushed_in_context() {\n        assertThat(strategy.mustPutEvaluatedExpressionInContext()).isFalse();\n    }\n\n    @Test\n    public void evaluate_should_return_a_long_result_after_querying() throws Exception {\n        final SExpressionImpl expression = new SExpressionImpl(\"employees\", \"countEmployees\", null,\n                Long.class.getName(), null, null);\n        final Long result = Long.valueOf(45);\n        when(businessDataRepository.findByNamedQuery(\"countEmployees\", Long.class,\n                Collections.<String, Serializable> emptyMap())).thenReturn(result);\n\n        final Long count = (Long) strategy.evaluate(expression, buildContext(), null, ContainerState.ACTIVE);\n\n        assertThat(count).isEqualTo(result);\n    }\n\n    @Test\n    public void evaluate_should_return_a_double_result_after_querying() throws Exception {\n        final SExpressionImpl expression = new SExpressionImpl(\"employees\", \"avgEmployees\", null,\n                Double.class.getName(), null, null);\n        final Double result = Double.valueOf(2.5);\n        when(businessDataRepository.findByNamedQuery(\"avgEmployees\", Double.class,\n                Collections.<String, Serializable> emptyMap())).thenReturn(result);\n\n        final Double avg = (Double) strategy.evaluate(expression, buildContext(), null, ContainerState.ACTIVE);\n\n        assertThat(avg).isEqualTo(result);\n    }\n\n    @Test\n    public void evaluate_should_return_a_integer_result_after_querying() throws Exception {\n        final SExpressionImpl expression = new SExpressionImpl(\"employees\", \"maxEmployees\", null,\n                Integer.class.getName(), null, null);\n        final Integer result = Integer.valueOf(25);\n        when(businessDataRepository.findByNamedQuery(\"maxEmployees\", Integer.class,\n                Collections.<String, Serializable> emptyMap())).thenReturn(result);\n\n        final Integer max = (Integer) strategy.evaluate(expression, buildContext(), null, ContainerState.ACTIVE);\n\n        assertThat(max).isEqualTo(result);\n    }\n\n    @Test\n    public void evaluate_should_return_a_float_result_after_querying() throws Exception {\n        final SExpressionImpl expression = new SExpressionImpl(\"employees\", \"minEmployees\", null, Float.class.getName(),\n                null, null);\n        final Float result = Float.valueOf(1.5F);\n        when(businessDataRepository.findByNamedQuery(\"minEmployees\", Float.class,\n                Collections.<String, Serializable> emptyMap())).thenReturn(result);\n\n        final Float min = (Float) strategy.evaluate(expression, buildContext(), null, ContainerState.ACTIVE);\n\n        assertThat(min).isEqualTo(result);\n    }\n\n    @Test\n    public void evaluate_should_return_the_list_of_results_after_querying() throws Exception {\n        final SExpression dependencyStartIndex = new SExpressionImpl(\"startIndex\", \"startIndex\", null,\n                Integer.class.getName(), null, null);\n        final SExpression dependencyMaxResults = new SExpressionImpl(\"maxResults\", \"startIndex\", null,\n                Integer.class.getName(), null, null);\n        final SExpressionImpl expression = new SExpressionImpl(\"employees\", \"getEmployees\", null, List.class.getName(),\n                null, Arrays.asList(\n                        dependencyStartIndex, dependencyMaxResults));\n\n        final Map<Integer, Object> resolvedExpressions = new HashMap<Integer, Object>(2);\n        resolvedExpressions.put(dependencyStartIndex.getDiscriminant(), 0);\n        resolvedExpressions.put(dependencyMaxResults.getDiscriminant(), 10);\n        strategy.evaluate(expression, buildContext(), resolvedExpressions, ContainerState.ACTIVE);\n\n        verify(businessDataRepository).findListByNamedQuery(\"getEmployees\", Entity.class,\n                Collections.<String, Serializable> emptyMap(), 0, 10);\n    }\n\n    @Test\n    public void evaluate_should_return_a_result_after_querying_with_parameters() throws Exception {\n\n        final SExpressionImpl dependencyFirstNameExpression = new SExpressionImpl(\"firstName\", \"John\", null,\n                String.class.getName(), null, null);\n        final SExpressionImpl dependencyLastNameExpression = new SExpressionImpl(\"lastName\", \"Doe\", null,\n                String.class.getName(), null, null);\n        final List<SExpression> dependencies = new ArrayList<SExpression>();\n        dependencies.add(dependencyFirstNameExpression);\n        dependencies.add(dependencyLastNameExpression);\n        final SExpressionImpl expression = new SExpressionImpl(\"employees\", \"getEmployeeByFirstNameAndLastName\", \"\",\n                \"com.bonitasoft.Employee\", \"\",\n                dependencies);\n        final Map<Integer, Object> resolvedExpressions = new HashMap<Integer, Object>();\n        resolvedExpressions.put(dependencyFirstNameExpression.getDiscriminant(), \"John\");\n        resolvedExpressions.put(dependencyLastNameExpression.getDiscriminant(), \"Doe\");\n        final Map<String, Serializable> parameters = new HashMap<String, Serializable>();\n        parameters.put(\"firstName\", \"John\");\n        parameters.put(\"lastName\", \"Doe\");\n        when(businessDataRepository.findByNamedQuery(\"getEmployeeByFirstNameAndLastName\", Entity.class, parameters))\n                .thenReturn(entity);\n\n        strategy.evaluate(expression, buildContext(), resolvedExpressions, ContainerState.ACTIVE);\n\n        verify(businessDataRepository).findByNamedQuery(\"getEmployeeByFirstNameAndLastName\", Entity.class, parameters);\n    }\n\n    @Test(expected = SExpressionEvaluationException.class)\n    public void evaluate_should_throw_an_exception_after_querying() throws Exception {\n        final SExpressionImpl expression = new SExpressionImpl(\"employees\", \"getEmployees\", null,\n                Entity.class.getName(), null, null);\n        when(businessDataRepository.findByNamedQuery(\"getEmployees\", Entity.class,\n                Collections.<String, Serializable> emptyMap())).thenThrow(\n                        new NonUniqueResultException(new IllegalArgumentException(\"several\")));\n\n        strategy.evaluate(expression, buildContext(), null, ContainerState.ACTIVE);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/expression/SimpleBizData.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression;\n\nimport org.bonitasoft.engine.bdm.Entity;\n\n/**\n * Simple Business Data test class\n */\nclass SimpleBizData implements Entity {\n\n    private static final long serialVersionUID = 1L;\n\n    private final Long id;\n\n    public SimpleBizData() {\n        id = null;\n    }\n\n    public SimpleBizData(final Long id) {\n        this.id = id;\n    }\n\n    public Long getId() {\n        return id;\n    }\n\n    @Override\n    public Long getPersistenceId() {\n        return null;\n    }\n\n    @Override\n    public Long getPersistenceVersion() {\n        return null;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/expression/SimpleBizDataAssert.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression;\n\nimport static java.lang.String.format;\n\nimport org.assertj.core.api.AbstractAssert;\n\n/**\n * {@link SimpleBizData} specific assertions - Generated by CustomAssertionGenerator.\n */\npublic class SimpleBizDataAssert extends AbstractAssert<SimpleBizDataAssert, SimpleBizData> {\n\n    /**\n     * Creates a new </code>{@link SimpleBizDataAssert}</code> to make assertions on actual SimpleBizData.\n     *\n     * @param actual the SimpleBizData we want to make assertions on.\n     */\n    public SimpleBizDataAssert(SimpleBizData actual) {\n        super(actual, SimpleBizDataAssert.class);\n    }\n\n    /**\n     * An entry point for SimpleBizDataAssert to follow AssertJ standard <code>assertThat()</code> statements.<br>\n     * With a static import, one's can write directly : <code>assertThat(mySimpleBizData)</code> and get specific\n     * assertion with code completion.\n     *\n     * @param actual the SimpleBizData we want to make assertions on.\n     * @return a new </code>{@link SimpleBizDataAssert}</code>\n     */\n    public static SimpleBizDataAssert assertThat(SimpleBizData actual) {\n        return new SimpleBizDataAssert(actual);\n    }\n\n    /**\n     * Verifies that the actual SimpleBizData's id is equal to the given one.\n     *\n     * @param id the given id to compare the actual SimpleBizData's id to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SimpleBizData's id is not equal to the given one.\n     */\n    public SimpleBizDataAssert hasId(Long id) {\n        // check that actual SimpleBizData we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> id to be:\\n  <%s>\\n but was:\\n  <%s>\", actual, id,\n                actual.getId());\n\n        // check\n        if (!actual.getId().equals(id)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual SimpleBizData's persistenceId is equal to the given one.\n     *\n     * @param persistenceId the given persistenceId to compare the actual SimpleBizData's persistenceId to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SimpleBizData's persistenceId is not equal to the given one.\n     */\n    public SimpleBizDataAssert hasPersistenceId(Long persistenceId) {\n        // check that actual SimpleBizData we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> persistenceId to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                persistenceId, actual.getPersistenceId());\n\n        // check\n        if (!actual.getPersistenceId().equals(persistenceId)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual SimpleBizData's persistenceVersion is equal to the given one.\n     *\n     * @param persistenceVersion the given persistenceVersion to compare the actual SimpleBizData's persistenceVersion\n     *        to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SimpleBizData's persistenceVersion is not equal to the given one.\n     */\n    public SimpleBizDataAssert hasPersistenceVersion(Long persistenceVersion) {\n        // check that actual SimpleBizData we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> persistenceVersion to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                persistenceVersion, actual.getPersistenceVersion());\n\n        // check\n        if (!actual.getPersistenceVersion().equals(persistenceVersion)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/handler/SchedulerServiceRestartHandlerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.handler;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport java.util.concurrent.Callable;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.scheduler.SchedulerService;\nimport org.bonitasoft.engine.scheduler.exception.SSchedulerException;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.SystemOutRule;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class SchedulerServiceRestartHandlerTest {\n\n    @Mock\n    private SchedulerService schedulerService;\n    @Mock\n    private UserTransactionService userTransactionService;\n\n    private SchedulerServiceRestartHandler handler;\n\n    @Rule\n    public SystemOutRule systemOutRule = new SystemOutRule().enableLog();\n\n    @Before\n    public void before() throws Exception {\n        handler = new SchedulerServiceRestartHandler(schedulerService, userTransactionService);\n        when(userTransactionService.executeInTransaction(any()))\n                .thenAnswer(invocation -> ((Callable) invocation.getArgument(0)).call());\n\n    }\n\n    @Test\n    public void should_call_rescheduleErroneousTriggers_in_transaction() throws Exception {\n        handler.execute();\n\n        verify(schedulerService).rescheduleErroneousTriggers();\n        verify(userTransactionService).executeInTransaction(any());\n    }\n\n    @Test\n    public void should_log_when_rescheduleErroneousTriggers_fails() throws SBonitaException {\n        doThrow(new SSchedulerException(\"failed\")).when(schedulerService).rescheduleErroneousTriggers();\n\n        systemOutRule.clearLog();\n        handler.execute();\n\n        assertThat(systemOutRule.getLog()).contains(\n                \"Unable to reschedule all erroneous triggers, call PlatformAPI.rescheduleErroneousTriggers to retry.\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/identity/CustomUserInfoDefinitionImporterTest.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Arrays;\n\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.xml.ExportedCustomUserInfoDefinition;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class CustomUserInfoDefinitionImporterTest {\n\n    private static final String LOCATION_NAME = \"Office location\";\n\n    private static final String SKILLS_DESCRIPTION = \"The user skills\";\n\n    private static final String SKILLS_NAME = \"Skills\";\n\n    @Mock\n    private IdentityService identityService;\n\n    @Mock\n    private ImportOrganizationStrategy strategy;\n\n    private CustomUserInfoDefinitionImporter importer;\n\n    private final ExportedCustomUserInfoDefinition skillsUserInfoDefinition = new ExportedCustomUserInfoDefinition(\n            SKILLS_NAME, SKILLS_DESCRIPTION);\n\n    private final ExportedCustomUserInfoDefinition locationInfoUserDefinition = new ExportedCustomUserInfoDefinition(\n            LOCATION_NAME, null);\n\n    @Before\n    public void setUp() {\n        importer = new CustomUserInfoDefinitionImporter(identityService, strategy);\n    }\n\n    @Test\n    public void importCustomUserInfoDefinitions_call_service_to_create_elements_if_doesnt_exists() throws Exception {\n        // when\n        importer.importCustomUserInfoDefinitions(Arrays.asList(skillsUserInfoDefinition, locationInfoUserDefinition));\n\n        // then\n        ArgumentCaptor<SCustomUserInfoDefinition> userInfoCaptor = ArgumentCaptor\n                .forClass(SCustomUserInfoDefinition.class);\n        verify(identityService, times(2)).createCustomUserInfoDefinition(userInfoCaptor.capture());\n        SCustomUserInfoDefinition skills = userInfoCaptor.getAllValues().get(0);\n        SCustomUserInfoDefinition location = userInfoCaptor.getAllValues().get(1);\n\n        assertThat(skills.getName()).isEqualTo(SKILLS_NAME);\n        assertThat(skills.getDescription()).isEqualTo(SKILLS_DESCRIPTION);\n\n        assertThat(location.getName()).isEqualTo(LOCATION_NAME);\n        assertThat(location.getDescription()).isNull();\n    }\n\n    @Test\n    public void importCustomUserInfoDefinitions_dont_call_service_to_create_elements_and_call_import_stragy_if_already_exists()\n            throws Exception {\n        // given\n        SCustomUserInfoDefinition existingDefinition = mock(SCustomUserInfoDefinition.class);\n        given(identityService.hasCustomUserInfoDefinition(SKILLS_NAME)).willReturn(true);\n        given(identityService.getCustomUserInfoDefinitionByName(SKILLS_NAME)).willReturn(existingDefinition);\n\n        // when\n        importer.importCustomUserInfoDefinitions(Arrays.asList(skillsUserInfoDefinition));\n\n        // then\n        verify(identityService, never()).createCustomUserInfoDefinition(any(SCustomUserInfoDefinition.class));\n        verify(strategy, times(1)).foundExistingCustomUserInfoDefinition(existingDefinition, skillsUserInfoDefinition);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/identity/CustomUserInfoValueImporterTest.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.fail;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.impl.SCustomUserInfoValueAPI;\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.xml.ExportedCustomUserInfoValue;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class CustomUserInfoValueImporterTest {\n\n    private static final String SKILLS_NAME = \"Skills\";\n\n    private static final String SKILLS_VALUE = \"Java\";\n\n    private static final String LOCATION_NAME = \"Location\";\n\n    private static final String LOCATION_VALUE = \"Egineering\";\n\n    private static final long SKILLS_ID = 42L;\n\n    private static final long LOCATION_ID = 43L;\n\n    private static final long USER_ID = 50L;\n\n    @Mock\n    private SCustomUserInfoValueAPI infoAPI;\n\n    @Mock\n    private SCustomUserInfoDefinition skillsDef;\n\n    @Mock\n    private SCustomUserInfoDefinition locationDef;\n\n    private CustomUserInfoValueImporter importer;\n\n    @Before\n    public void setUp() {\n        given(skillsDef.getId()).willReturn(SKILLS_ID);\n        given(locationDef.getId()).willReturn(LOCATION_ID);\n\n        Map<String, SCustomUserInfoDefinition> infoDefinitions = new HashMap<>(1);\n        infoDefinitions.put(SKILLS_NAME, skillsDef);\n        infoDefinitions.put(LOCATION_NAME, locationDef);\n\n        importer = new CustomUserInfoValueImporter(infoAPI, infoDefinitions);\n    }\n\n    @Test\n    public void importCustomUserInfoValues_should_call_setCustomUserInfoValue() throws Exception {\n        // given\n        List<ExportedCustomUserInfoValue> customUserInfoValues = new ArrayList<>(2);\n        customUserInfoValues.add(new ExportedCustomUserInfoValue(SKILLS_NAME, SKILLS_VALUE));\n        customUserInfoValues.add(new ExportedCustomUserInfoValue(LOCATION_NAME, LOCATION_VALUE));\n\n        // when\n        importer.imporCustomUserInfoValues(customUserInfoValues, USER_ID);\n\n        // then\n        verify(infoAPI, times(1)).set(SKILLS_ID, USER_ID, SKILLS_VALUE);\n        verify(infoAPI, times(1)).set(LOCATION_ID, USER_ID, LOCATION_VALUE);\n    }\n\n    @Test\n    public void importUsers_should_throw_OrganizationImportException_if_value_name_isnt_conform_to_definition_name()\n            throws Exception {\n        // given\n        String notDefinedName = \"no exist in definition\";\n        ExportedCustomUserInfoValue invalidValue = new ExportedCustomUserInfoValue(notDefinedName, \"any value\");\n\n        try {\n            // when\n            importer.imporCustomUserInfoValues(Arrays.asList(invalidValue), USER_ID);\n            fail(\"exception expected\");\n        } catch (SImportOrganizationException e) {\n            // then\n            String expectedMessage = \"The XML file is inconsistent. A custom user info value is refenced with name '\"\n                    + notDefinedName\n                    + \"', but no custom user info definition is defined with this name.\";\n            assertThat(e.getMessage()).isEqualTo(expectedMessage);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/identity/ExportOrganizationTest.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport static java.util.Collections.emptyMap;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoValue;\nimport org.bonitasoft.engine.identity.model.SUserMembership;\nimport org.bonitasoft.engine.identity.xml.ExportedCustomUserInfoValue;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ExportOrganizationTest {\n\n    private static final long USER_ID = 15L;\n\n    private static final long USER_INFO_DEF1_ID = 25L;\n\n    private static final int MAX_RESULTS = 2;\n\n    private static final String SKILSS_NAME = \"Skills\";\n\n    private static final String SKILLS_VALUE = \"Java\";\n\n    @Mock\n    private IdentityService identityService;\n\n    @Mock\n    private SCustomUserInfoDefinition userInfoDef1;\n\n    @Mock\n    private SCustomUserInfoDefinition userInfoDef2;\n\n    @Mock\n    private SCustomUserInfoDefinition userInfoDef3;\n\n    @Mock\n    private SCustomUserInfoValue userInfoVal1;\n\n    @Mock\n    private SCustomUserInfoValue userInfoVal2;\n\n    @Mock\n    private SCustomUserInfoValue userInfoVal3;\n\n    private ExportOrganization exportOrganization;\n\n    @Before\n    public void setUp() {\n        exportOrganization = new ExportOrganization(identityService, MAX_RESULTS);\n        given(userInfoVal1.getDefinitionId()).willReturn(USER_INFO_DEF1_ID);\n        given(userInfoVal1.getValue()).willReturn(SKILLS_VALUE);\n    }\n\n    @Test\n    public void getAllCustomUserInfoDefinitions_should_return_all_custom_user_info_definition_from_service()\n            throws Exception {\n        // given\n        given(identityService.getCustomUserInfoDefinitions(0, MAX_RESULTS))\n                .willReturn(Arrays.asList(userInfoDef1, userInfoDef2));\n        given(identityService.getCustomUserInfoDefinitions(2, MAX_RESULTS)).willReturn(Arrays.asList(userInfoDef3));\n\n        // when\n        List<SCustomUserInfoDefinition> allCustomUserInfoDefinitions = exportOrganization\n                .getAllCustomUserInfoDefinitions();\n\n        // then\n        assertThat(allCustomUserInfoDefinitions).isEqualTo(Arrays.asList(userInfoDef1, userInfoDef2, userInfoDef3));\n    }\n\n    @Test\n    public void getAllCustomUserInfoForUser_should_return_elements_from_service() throws Exception {\n        // given\n        given(identityService.searchCustomUserInfoValue(any())).willReturn(\n                Arrays.asList(userInfoVal1, userInfoVal2)).willReturn(Collections.singletonList(userInfoVal3));\n\n        // when\n        List<SCustomUserInfoValue> allCustomUserInfoValues = exportOrganization.getAllCustomUserInfoForUser(USER_ID);\n\n        // then\n        assertThat(allCustomUserInfoValues).isEqualTo(Arrays.asList(userInfoVal1, userInfoVal2, userInfoVal3));\n    }\n\n    @Test\n    public void addCustomUserInfoValues_should_call_addCustomUserInfoValue_on_builder_for_all_elements()\n            throws Exception {\n        // given\n        given(identityService.searchCustomUserInfoValue(any())).willReturn(\n                Arrays.asList(userInfoVal1));\n        ExportedUserBuilder clientUserbuilder = mock(ExportedUserBuilder.class);\n\n        // when\n        exportOrganization.addCustomUserInfoValues(USER_ID, clientUserbuilder,\n                Collections.singletonMap(USER_INFO_DEF1_ID, SKILSS_NAME));\n\n        // then\n        verify(clientUserbuilder, times(1))\n                .addCustomUserInfoValue(new ExportedCustomUserInfoValue(SKILSS_NAME, SKILLS_VALUE));\n        verify(clientUserbuilder, times(1))\n                .addCustomUserInfoValue(new ExportedCustomUserInfoValue(SKILSS_NAME, SKILLS_VALUE));\n    }\n\n    @Test\n    public void getAllUserMemberships_should_call_getUserMemberships_with_order_by_option() throws SIdentityException {\n        //given\n        when(identityService.getNumberOfUserMemberships()).thenReturn(5L);\n        OrderByOption orderByOption = new OrderByOption(SUserMembership.class, SUserMembership.ID, OrderByType.ASC);\n\n        // when\n        exportOrganization.getAllUserMemberships();\n\n        // then\n        verify(identityService, times(3)).getUserMemberships(anyInt(), anyInt(), eq(orderByOption));\n    }\n\n    @Test\n    public void getAllGroups_should_call_getGroups_with_order_by_option() throws SIdentityException {\n        //given\n        when(identityService.getNumberOfGroups()).thenReturn(5L);\n\n        // when\n        exportOrganization.getAllGroups();\n\n        // then\n        verify(identityService, times(3)).getGroups(anyInt(), anyInt(), eq(\"id\"), eq(OrderByType.ASC));\n    }\n\n    @Test\n    public void getAllRoles_should_call_getRoles_with_order_by_option() throws SIdentityException {\n        //given\n        when(identityService.getNumberOfRoles()).thenReturn(5L);\n\n        // when\n        exportOrganization.getAllRoles();\n\n        // then\n        verify(identityService, times(3)).getRoles(anyInt(), anyInt(), eq(\"id\"), eq(OrderByType.ASC));\n    }\n\n    @Test\n    public void getNextUsersPage_should_call_getUsers_with_order_by_option() throws SBonitaException {\n        //given\n\n        // when\n        exportOrganization.getNextUsersPage(0, 10, emptyMap());\n\n        // then\n        verify(identityService, times(1)).getUsers(anyInt(), anyInt(), eq(\"id\"), eq(OrderByType.ASC));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/identity/ImportOrganizationFailOnDuplicatesStrategyTest.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.fail;\nimport static org.mockito.Mockito.mock;\n\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.xml.ExportedCustomUserInfoDefinition;\nimport org.junit.Test;\n\npublic class ImportOrganizationFailOnDuplicatesStrategyTest {\n\n    @Test\n    public void foundExistingCustomUserInfoDefinition_throws_ImportDuplicateInOrganizationException() {\n        // given\n        String name = \"duplicate\";\n        SCustomUserInfoDefinition existingUserInfoDefinition = mock(SCustomUserInfoDefinition.class);\n        ExportedCustomUserInfoDefinition newUserInfoDefinition = new ExportedCustomUserInfoDefinition(name, null);\n        ImportOrganizationFailOnDuplicatesStrategy strategy = new ImportOrganizationFailOnDuplicatesStrategy();\n\n        try {\n            // when\n            strategy.foundExistingCustomUserInfoDefinition(existingUserInfoDefinition, newUserInfoDefinition);\n            fail(\"exception expected\");\n        } catch (ImportDuplicateInOrganizationException e) {\n            // then\n            assertThat(e.getMessage())\n                    .isEqualTo(\"There's already a custom user info definition with the name : '\" + name + \"'\");\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/identity/ImportOrganizationMergeDuplicatesStrategyTest.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.*;\n\nimport org.bonitasoft.engine.api.impl.SCustomUserInfoValueAPI;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.identity.model.builder.SCustomUserInfoDefinitionUpdateBuilder;\nimport org.bonitasoft.engine.identity.model.builder.SCustomUserInfoDefinitionUpdateBuilderFactory;\nimport org.bonitasoft.engine.identity.xml.ExportedCustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.xml.ExportedCustomUserInfoValue;\nimport org.bonitasoft.engine.identity.xml.ExportedUser;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ImportOrganizationMergeDuplicatesStrategyTest {\n\n    private static final long USER_ID = 2L;\n    private static final long DEFINITION_ID = 1L;\n    private static final String SKILLS_VALUE = \"Java\";\n    private static final String SKILLS_NAME = \"Skills\";\n    private static final long MANAGER_ID = 1954238L;\n    @Mock\n    private IdentityService identityService;\n    @Mock\n    private SCustomUserInfoValueAPI userInfoValueAPI;\n    @InjectMocks\n    private ImportOrganizationMergeDuplicatesStrategy strategy;\n    @Mock\n    private SUser existingUser;\n    private ExportedUser userToImport;\n    @Mock\n    private SCustomUserInfoDefinition infoDef;\n    @Captor\n    private ArgumentCaptor<EntityUpdateDescriptor> captor;\n    private SUser manager;\n\n    @Before\n    public void setUp() {\n        given(existingUser.getId()).willReturn(USER_ID);\n        given(infoDef.getId()).willReturn(DEFINITION_ID);\n        manager = new SUser();\n        manager.setId(MANAGER_ID);\n        manager.setUserName(\"manager\");\n        userToImport = new ExportedUser();\n    }\n\n    @Test\n    public void foundExistingCustomUserInfoDefinition_should_call_service_to_update_element() throws Exception {\n        // given\n        String newDescription = \"updated description\";\n        SCustomUserInfoDefinition existingUserInfoDefinition = mock(SCustomUserInfoDefinition.class);\n        ExportedCustomUserInfoDefinition creator = new ExportedCustomUserInfoDefinition(\"name\", newDescription);\n        EntityUpdateDescriptor updateDescriptor = getUpdateDescriptor(newDescription);\n        userToImport.addCustomUserInfoValues(new ExportedCustomUserInfoValue(SKILLS_NAME, SKILLS_VALUE));\n\n        // when\n        strategy.foundExistingCustomUserInfoDefinition(existingUserInfoDefinition, creator);\n\n        // then\n        verify(identityService, times(1)).updateCustomUserInfoDefinition(existingUserInfoDefinition, updateDescriptor);\n    }\n\n    private EntityUpdateDescriptor getUpdateDescriptor(String newDescription) {\n        SCustomUserInfoDefinitionUpdateBuilder builder = BuilderFactory\n                .get(SCustomUserInfoDefinitionUpdateBuilderFactory.class).createNewInstance();\n        builder.updateDescription(newDescription);\n        return builder.done();\n    }\n\n    @Test\n    public void foundExistingUser_show_call_customUserInfoValueImporter() throws Exception {\n        // given\n        given(identityService.getCustomUserInfoDefinitionByName(SKILLS_NAME)).willReturn(infoDef);\n        userToImport.addCustomUserInfoValues(new ExportedCustomUserInfoValue(SKILLS_NAME, SKILLS_VALUE));\n\n        // when\n        strategy.foundExistingUser(existingUser, userToImport);\n\n        // then\n        verify(userInfoValueAPI, times(1)).set(DEFINITION_ID, USER_ID, SKILLS_VALUE);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/identity/UserImporterTest.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport static java.util.Collections.singletonList;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyLong;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.identity.xml.ExportedCustomUserInfoValue;\nimport org.bonitasoft.engine.identity.xml.ExportedUser;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentMatchers;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class UserImporterTest {\n\n    private static final long USER_ID = 10L;\n    private static final String LOCATION_VALUE = \"engineering\";\n    private static final String LOCATION_NAME = \"location\";\n    private static final String SKILLS_VALUE = \"Java\";\n    private static final String FIRST_USER = \"first.user\";\n    private static final String SKILLS_NAME = \"Skills\";\n    private static final long MANAGER_ID = 1954238L;\n    @Mock\n    private IdentityService identityService;\n    @Mock\n    private ImportOrganizationStrategy strategy;\n    @Mock\n    private CustomUserInfoValueImporter userInfoValueImporter;\n    private UserImporter importer;\n    @Mock\n    private SUser persistedUser;\n    private ExportedUser userToImport;\n    private ExportedCustomUserInfoValue skillsValue;\n    private ExportedCustomUserInfoValue locationValue;\n\n    public UserImporterTest() {\n    }\n\n    @Before\n    public void setUp() throws SUserCreationException {\n        importer = new UserImporter(identityService, strategy, 5, userInfoValueImporter);\n\n        given(persistedUser.getId()).willReturn(USER_ID);\n\n        skillsValue = new ExportedCustomUserInfoValue(SKILLS_NAME, SKILLS_VALUE);\n        locationValue = new ExportedCustomUserInfoValue(LOCATION_NAME, LOCATION_VALUE);\n        SUser manager = new SUser();\n        manager.setId(MANAGER_ID);\n        manager.setUserName(\"manager\");\n\n        userToImport = getUser(Arrays.asList(skillsValue, locationValue));\n    }\n\n    private ExportedUser getUser(List<ExportedCustomUserInfoValue> userInfoValues) {\n        ExportedUser userImpl = new ExportedUser();\n        userImpl.setUserName(UserImporterTest.FIRST_USER);\n        for (ExportedCustomUserInfoValue infoValue : userInfoValues) {\n            userImpl.addCustomUserInfoValues(infoValue);\n        }\n        return userImpl;\n    }\n\n    @Test\n    public void importUsers_should_call_customUserInfoValueImporter_if_the_user_does_not_exist() throws Exception {\n        // given\n        given(identityService.getNumberOfUsers(any(QueryOptions.class))).willReturn(0L);\n        given(identityService.createUser(any(SUser.class))).willReturn(persistedUser);\n\n        // when\n        importer.importUsers(singletonList(userToImport));\n\n        // then\n        verify(userInfoValueImporter, times(1)).imporCustomUserInfoValues(Arrays.asList(skillsValue, locationValue),\n                USER_ID);\n    }\n\n    @Test\n    public void importUsers_should_not_call_customUserInfoValueImporter_if_the_user_exists() throws Exception {\n        // given\n        given(identityService.getNumberOfUsers(any(QueryOptions.class))).willReturn(1L);\n        given(identityService.getUserByUserName(FIRST_USER)).willReturn(persistedUser);\n\n        // when\n        importer.importUsers(singletonList(userToImport));\n\n        // then\n        verify(userInfoValueImporter, never())\n                .imporCustomUserInfoValues(ArgumentMatchers.any(), anyLong());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/jobs/TriggerTimerEventJobTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.jobs;\n\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.verify;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Optional;\n\nimport org.bonitasoft.engine.commons.exceptions.SRetryableException;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerType;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SBPMEventType;\nimport org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance;\nimport org.bonitasoft.engine.execution.event.EventsHandler;\nimport org.bonitasoft.engine.scheduler.JobService;\nimport org.bonitasoft.engine.scheduler.SchedulerService;\nimport org.bonitasoft.engine.scheduler.StatelessJob;\nimport org.bonitasoft.engine.scheduler.exception.SJobConfigurationException;\nimport org.bonitasoft.engine.scheduler.model.SJobDescriptor;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.tenant.TenantServicesManager;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnit;\nimport org.mockito.junit.MockitoRule;\n\npublic class TriggerTimerEventJobTest {\n\n    @Rule\n    public MockitoRule mockitoRule = MockitoJUnit.rule();\n\n    private final TriggerTimerEventJob triggerTimerEventJob = new TriggerTimerEventJob() {\n\n        @Override\n        protected ServiceAccessor getServiceAccessor() {\n            return serviceAccessor;\n        }\n    };\n    @Mock\n    private ServiceAccessor serviceAccessor;\n    @Mock\n    private EventsHandler eventsHandler;\n    @Mock\n    private JobService jobService;\n    @Mock\n    private SchedulerService schedulerService;\n    @Mock\n    private EventInstanceService eventInstanceService;\n    @Mock\n    private TenantServicesManager tenantServicesManager;\n\n    private Long processDefinitionId = 1234L;\n    private Long targetSFlowNodeDefinitionId = 32497L;\n    private Long flowNodeInstanceId = 43298L;\n    private String containerType = \"containerType\";\n    private String eventType = SBPMEventType.INTERMEDIATE_CATCH_EVENT.name();\n    private Long subProcessId = null;\n    private Long processInstanceId = 646534L;\n    private Long rootProcessInstanceId = 3412L;\n    private Boolean isInterrupting = false;\n    private Long jobDescriptorId = 3209494L;\n\n    @Before\n    public void before() throws Exception {\n        doReturn(eventsHandler).when(serviceAccessor).getEventsHandler();\n        doReturn(jobService).when(serviceAccessor).getJobService();\n        doReturn(schedulerService).when(serviceAccessor).getSchedulerService();\n        doReturn(eventInstanceService).when(serviceAccessor).getEventInstanceService();\n        doReturn(tenantServicesManager).when(serviceAccessor).getTenantServicesManager();\n        doReturn(true).when(tenantServicesManager).isStarted();\n    }\n\n    private void setAttributes() throws SJobConfigurationException {\n        Map<String, Serializable> attributes = new HashMap<>();\n        attributes.put(\"processDefinitionId\", processDefinitionId);\n        attributes.put(\"targetSFlowNodeDefinitionId\", targetSFlowNodeDefinitionId);\n        attributes.put(\"flowNodeInstanceId\", flowNodeInstanceId);\n        attributes.put(\"containerType\", containerType);\n        attributes.put(\"eventType\", eventType);\n        attributes.put(\"subProcessId\", subProcessId);\n        attributes.put(\"processInstanceId\", processInstanceId);\n        attributes.put(\"rootProcessInstanceId\", rootProcessInstanceId);\n        attributes.put(\"isInterrupting\", isInterrupting);\n        attributes.put(StatelessJob.JOB_DESCRIPTOR_ID, jobDescriptorId);\n        triggerTimerEventJob.setAttributes(attributes);\n    }\n\n    @Test(expected = SRetryableException.class)\n    public void should_throw_retryableException_when_tenant_is_stopped() throws Exception {\n        SJobDescriptor jobDescriptor = SJobDescriptor.builder().id(jobDescriptorId).build();\n        doReturn(jobDescriptor).when(jobService).getJobDescriptor(jobDescriptorId);\n        setAttributes();\n        doReturn(false).when(tenantServicesManager).isStarted();\n\n        triggerTimerEventJob.execute();\n\n    }\n\n    @Test\n    public void should_trigger_timer_for_event_sub_process() throws Exception {\n        eventType = SBPMEventType.EVENT_SUB_PROCESS.name();\n        subProcessId = 31289032198L;\n        setAttributes();\n\n        triggerTimerEventJob.execute();\n\n        verify(eventsHandler).triggerCatchEvent(SEventTriggerType.TIMER, processDefinitionId,\n                targetSFlowNodeDefinitionId,\n                containerType, subProcessId, processInstanceId, rootProcessInstanceId, isInterrupting);\n    }\n\n    @Test\n    public void should_trigger_timer_for_start_process() throws Exception {\n        eventType = SBPMEventType.START_EVENT.name();\n        flowNodeInstanceId = null;\n        setAttributes();\n\n        triggerTimerEventJob.execute();\n\n        verify(eventsHandler).triggerCatchEvent(eventType, processDefinitionId, targetSFlowNodeDefinitionId,\n                null, containerType);\n    }\n\n    @Test\n    public void should_trigger_timer_for_catch_event() throws Exception {\n        setAttributes();\n\n        triggerTimerEventJob.execute();\n\n        verify(eventsHandler).triggerCatchEvent(eventType, processDefinitionId, targetSFlowNodeDefinitionId,\n                flowNodeInstanceId, containerType);\n    }\n\n    @Test\n    public void should_delete_EventTriggerInstance() throws Exception {\n        setAttributes();\n        STimerEventTriggerInstance timerEventTriggerInstance = new STimerEventTriggerInstance();\n        timerEventTriggerInstance.setId(32143L);\n        doReturn(Optional.of(timerEventTriggerInstance)).when(eventInstanceService)\n                .getTimerEventTriggerInstanceOfFlowNode(flowNodeInstanceId);\n\n        triggerTimerEventJob.execute();\n\n        verify(eventInstanceService).deleteEventTriggerInstance(timerEventTriggerInstance);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/message/MessagesHandlingServiceTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.message;\n\nimport static java.util.Arrays.asList;\nimport static java.util.stream.Collectors.toList;\nimport static java.util.stream.Stream.iterate;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.core.process.instance.model.event.handling.SBPMEventType.*;\nimport static org.bonitasoft.engine.message.MessagesHandlingService.*;\nimport static org.mockito.Mockito.*;\n\nimport java.time.Duration;\nimport java.util.List;\nimport java.util.concurrent.Callable;\n\nimport io.micrometer.core.instrument.Clock;\nimport io.micrometer.core.instrument.Counter;\nimport io.micrometer.core.instrument.MeterRegistry;\nimport io.micrometer.core.instrument.simple.SimpleMeterRegistry;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SBPMEventType;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageEventCouple;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent;\nimport org.bonitasoft.engine.execution.work.BPMWorkFactory;\nimport org.bonitasoft.engine.lock.LockService;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.bonitasoft.engine.work.WorkService;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Celine Souchet\n * @author Emmanuel Duchastenier\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class MessagesHandlingServiceTest {\n\n    public static final long TENANT_ID = 1L;\n    @Mock\n    private EventInstanceService eventInstanceService;\n    @Mock\n    private WorkService workService;\n    @Mock\n    private LockService lockService;\n    @Mock\n    private UserTransactionService userTransactionService;\n    @Mock\n    private SessionAccessor sessionAccessor;\n    @Mock\n    private BPMWorkFactory workFactory;\n\n    private MessagesHandlingService messagesHandlingService;\n    private MeterRegistry meterRegistry;\n\n    @Before\n    public void setup() throws Exception {\n        when(userTransactionService.executeInTransaction(any())).thenAnswer(a -> ((Callable) a.getArgument(0)).call());\n\n        meterRegistry = new SimpleMeterRegistry(\n                // So that micrometer updates its counters every 1 ms:\n                k -> k.equals(\"simple.step\") ? Duration.ofMillis(1).toString() : null,\n                Clock.SYSTEM);\n        messagesHandlingService = spy(new MessagesHandlingService(eventInstanceService, workService, lockService,\n                userTransactionService, workFactory, meterRegistry));\n    }\n\n    @Test\n    public void getMessageUniqueCouplesWithDuplicateMessage() {\n        // Given\n        final List<SMessageEventCouple> messageCouples = asList( //\n                msgEventCouple(1L, 10L), //\n                msgEventCouple(2L, 20L), //\n                msgEventCouple(1L, 30L));\n\n        // When\n        final List<SMessageEventCouple> uniqueCouples = messagesHandlingService.getMessageUniqueCouples(messageCouples);\n\n        // Then\n        assertThat(uniqueCouples).containsExactly(\n                msgEventCouple(1L, 10L), //\n                msgEventCouple(2L, 20L));\n    }\n\n    @Test\n    public void getMessageUniqueCouplesWithDuplicateWaitingEvent() {\n        // Given\n        final List<SMessageEventCouple> messageCouples = asList( //\n                msgEventCouple(1L, 10L), //\n                msgEventCouple(2L, 10L), //\n                msgEventCouple(3L, 30L));\n\n        // When\n        final List<SMessageEventCouple> uniqueCouples = messagesHandlingService.getMessageUniqueCouples(messageCouples);\n\n        // Then\n        assertThat(uniqueCouples).containsExactly(\n                msgEventCouple(1L, 10L), //\n                msgEventCouple(3L, 30L));\n    }\n\n    @Test\n    public void couplesWithDuplicateStartWaitingEventsAreConsideredTwice() {\n        // Given\n        final List<SMessageEventCouple> messageCouples = asList(\n                msgEventCouple(1L, 10L, START_EVENT), //\n                msgEventCouple(2L, 10L, START_EVENT));\n\n        // When\n        final List<SMessageEventCouple> uniqueCouples = messagesHandlingService.getMessageUniqueCouples(messageCouples);\n\n        // Then\n        assertThat(uniqueCouples).containsExactly(\n                msgEventCouple(1L, 10L, START_EVENT), //\n                msgEventCouple(2L, 10L, START_EVENT));\n    }\n\n    @Test\n    public void couplesWithDuplicateEventSubProcessesAreConsideredOnlyOnce() {\n        // Given\n        final List<SMessageEventCouple> messageCouples = asList(\n                msgEventCouple(1L, 10L, EVENT_SUB_PROCESS), //\n                msgEventCouple(2L, 10L, EVENT_SUB_PROCESS));\n\n        // When\n        final List<SMessageEventCouple> uniqueCouples = messagesHandlingService.getMessageUniqueCouples(messageCouples);\n\n        // Then\n        assertThat(uniqueCouples).containsExactly(msgEventCouple(1L, 10L, EVENT_SUB_PROCESS));\n    }\n\n    @Test\n    public void getMessageUniqueCouplesWithDuplicateMessagesAndWaitingEvent() {\n        // Given\n        final List<SMessageEventCouple> messageCouples = asList( //\n                msgEventCouple(1L, 10L), //\n                msgEventCouple(2L, 20L), //\n                msgEventCouple(2L, 10L), //\n                msgEventCouple(2L, 20L));\n\n        // When\n        final List<SMessageEventCouple> uniqueCouples = messagesHandlingService.getMessageUniqueCouples(messageCouples);\n\n        // Then\n        assertThat(uniqueCouples).containsExactly(\n                msgEventCouple(1L, 10L), //\n                msgEventCouple(2L, 20L));\n    }\n\n    @Test\n    public void executeMessageCouple_should_increment_executed_message_counter() throws Exception {\n        // given:\n        doReturn(new SWaitingMessageEvent()).when(eventInstanceService).getWaitingMessage(2L);\n        doReturn(new SMessageInstance()).when(eventInstanceService).getMessageInstance(1L);\n\n        // when:\n        messagesHandlingService.executeMessageCouple(1L, 2L);\n\n        // then:\n        assertThat(counterValue(NUMBER_OF_MESSAGES_EXECUTED)).isEqualTo(1);\n    }\n\n    @Test\n    public void should_counters_be_present() {\n        assertThat(counter(NUMBER_OF_MESSAGES_EXECUTED)).isNotNull();\n        assertThat(counter(NUMBER_OF_MESSAGES_POTENTIAL_MATCHED)).isNotNull();\n        assertThat(counter(NUMBER_OF_MESSAGES_MATCHING_RETRIGGERED_TASKS)).isNotNull();\n    }\n\n    @Test\n    public void should_increment_metrics_on_executed_and_potential_couples_when_matching_messages() throws Exception {\n        doReturn(new SWaitingMessageEvent()).when(eventInstanceService).getWaitingMessage(anyLong());\n        doReturn(new SMessageInstance()).when(eventInstanceService).getMessageInstance(anyLong());\n        doReturn(asList(\n                new SMessageEventCouple(51, INTERMEDIATE_CATCH_EVENT, 61),\n                new SMessageEventCouple(52, INTERMEDIATE_CATCH_EVENT, 62),\n                new SMessageEventCouple(53, INTERMEDIATE_CATCH_EVENT, 63),\n                new SMessageEventCouple(53, INTERMEDIATE_CATCH_EVENT, 64)// waiting event already matched\n        )).when(eventInstanceService).getMessageEventCouples(anyInt(), anyInt());\n\n        messagesHandlingService.matchEventCoupleAndTriggerExecution();\n\n        assertThat(counterValue(NUMBER_OF_MESSAGES_EXECUTED)).isEqualTo(3);\n        assertThat(counterValue(NUMBER_OF_MESSAGES_POTENTIAL_MATCHED)).isEqualTo(4);\n        assertThat(counterValue(NUMBER_OF_MESSAGES_MATCHING_RETRIGGERED_TASKS)).isEqualTo(0);\n    }\n\n    @Test\n    public void should_increment_metric_on_retriggered_tasks_when_matching_more_couples_than_the_maximum()\n            throws Exception {\n        doReturn(new SWaitingMessageEvent()).when(eventInstanceService).getWaitingMessage(anyLong());\n        doReturn(new SMessageInstance()).when(eventInstanceService).getMessageInstance(anyLong());\n        List<SMessageEventCouple> couples = iterate(1, i -> i + 1).limit(100) // 100 == MAX_COUPLES\n                .map(i -> msgEventCouple(i, i))\n                .collect(toList());\n        doReturn(couples).when(eventInstanceService).getMessageEventCouples(anyInt(), anyInt());\n\n        messagesHandlingService.matchEventCoupleAndTriggerExecution();\n\n        assertThat(counterValue(NUMBER_OF_MESSAGES_MATCHING_RETRIGGERED_TASKS)).isEqualTo(1);\n    }\n\n    // =================================================================================================================\n    // UTILS\n    // =================================================================================================================\n\n    private Counter counter(String counterName) {\n        return meterRegistry.find(counterName).counter();\n    }\n\n    private double counterValue(String counterName) {\n        return meterRegistry.find(counterName).counter().count();\n    }\n\n    private static SMessageEventCouple msgEventCouple(long msgId, long eventId) {\n        return new SMessageEventCouple(eventId, null, msgId);\n    }\n\n    private static SMessageEventCouple msgEventCouple(long msgId, long eventId, SBPMEventType eventType) {\n        return new SMessageEventCouple(eventId, eventType, msgId);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/operation/Address.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.operation;\n\nimport org.bonitasoft.engine.bdm.Entity;\n\npublic class Address implements Entity {\n\n    private static final long serialVersionUID = -7765232426654390190L;\n\n    private Long persistenceId;\n\n    public Address(final Long persistenceId) {\n        this.persistenceId = persistenceId;\n    }\n\n    @Override\n    public Long getPersistenceId() {\n        return persistenceId;\n    }\n\n    @Override\n    public Long getPersistenceVersion() {\n        return 4687634L;\n    }\n\n    @Override\n    public String toString() {\n        return \"Address{\" +\n                \"persistenceId=\" + persistenceId +\n                '}';\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/operation/BusinessDataAssignmentStrategyTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.operation;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\n\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.operation.model.SLeftOperand;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.core.operation.model.SOperatorType;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class BusinessDataAssignmentStrategyTest {\n\n    @Mock\n    private EntitiesActionsExecutor actionsExecutor;\n\n    @Mock\n    private MergeEntityAction action;\n\n    @InjectMocks\n    private BusinessDataAssignmentStrategy strategy;\n\n    @Mock\n    private Entity entity;\n\n    private Address mergedEntity = new Address(45L);\n\n    @Mock\n    SExpressionContext expressionContext;\n\n    @Mock\n    SOperation operation;\n\n    @Test\n    public void computeNewValueForLeftOperand_should_return_merged_entity_when_should_persist() throws Exception {\n        //given\n        given(actionsExecutor.executeAction(entity, null, action)).willReturn(mergedEntity);\n\n        //when\n        Object newValue = strategy.computeNewValueForLeftOperand(operation, entity, expressionContext, true);\n\n        //then\n        assertThat(newValue).isEqualTo(mergedEntity);\n    }\n\n    @Test\n    public void computeNewValueForLeftOperand_should_return_current_entity_when_should_not_persist() throws Exception {\n        //when\n        Object newValue = strategy.computeNewValueForLeftOperand(operation, entity, expressionContext, false);\n\n        //then\n        assertThat(newValue).isEqualTo(entity);\n    }\n\n    @Test(expected = SOperationExecutionException.class)\n    public void computeNewValueForLeftOperand_should_throws_operation_exception_when_merge_throws_exception()\n            throws Exception {\n        //given\n        given(actionsExecutor.executeAction(entity, null, action)).willThrow(new SEntityActionExecutionException(\"\"));\n\n        //when\n        strategy.computeNewValueForLeftOperand(operation, entity, expressionContext, true);\n\n        //then\n    }\n\n    @Test\n    public void getOperationType_should_return_business_data_assignment() throws Exception {\n        assertThat(strategy.getOperationType())\n                .isEqualTo(SOperatorType.ASSIGNMENT + \"_\" + SLeftOperand.TYPE_BUSINESS_DATA);\n    }\n\n    @Test\n    public void should_not_persist_on_null() throws Exception {\n        assertThat(strategy.shouldPersistOnNullValue()).isFalse();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/operation/BusinessDataContextTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.operation;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.commons.Container;\nimport org.junit.Test;\n\npublic class BusinessDataContextTest {\n\n    @Test\n    public void should_create_a_consistent_context() throws Exception {\n        //when\n        BusinessDataContext context = new BusinessDataContext(\"address\", new Container(2L, \"process\"));\n\n        //then\n        assertThat(context.getName()).isEqualTo(\"address\");\n        assertThat(context.getContainer().getId()).isEqualTo(2L);\n        assertThat(context.getContainer().getType()).isEqualTo(\"process\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/operation/BusinessDataJavaMethodOperationExecutorStrategyTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.operation;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.*;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.bonitasoft.engine.business.data.BusinessDataService;\nimport org.bonitasoft.engine.business.data.SBusinessDataNotFoundException;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.operation.impl.JavaMethodOperationExecutorStrategy;\nimport org.bonitasoft.engine.core.operation.model.SLeftOperand;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class BusinessDataJavaMethodOperationExecutorStrategyTest {\n\n    private static final String PARAMETER_TYPE = \"org.pojo.Address\";\n\n    private static final String METHOD_NAME = \"setAddress\";\n\n    private static final String DATA_TO_SET = \"employee\";\n\n    @Mock\n    private BusinessDataService businessDataService;\n\n    @Mock\n    private EntitiesActionsExecutor actionsExecutor;\n\n    @Mock\n    private MergeEntityAction mergeEntityAction;\n\n    @Mock\n    private JavaMethodOperationExecutorStrategy javaMethodOperationExecutorStrategy;\n\n    @InjectMocks\n    @Spy\n    private BusinessDataJavaMethodOperationExecutorStrategy strategy;\n\n    @Mock\n    private SExpressionContext context;\n\n    @Mock\n    private Object valuetoSetObjectWith;\n\n    @Mock\n    private SOperation operation;\n\n    @Mock\n    private SLeftOperand leftOperand;\n\n    @Mock\n    private Entity businessData;\n\n    @Before\n    public void before() throws Exception {\n        doReturn(leftOperand).when(operation).getLeftOperand();\n    }\n\n    @Test\n    public void getOperationType() throws Exception {\n        //when\n        final String operationType = strategy.getOperationType();\n\n        //then\n        assertThat(operationType).as(\"should get opration type\")\n                .isEqualTo(JavaMethodOperationExecutorStrategy.TYPE_JAVA_METHOD);\n\n    }\n\n    @Test\n    public void should_delegateToBusinessDataService_and_call_merge_action_when_should_persist() throws Exception {\n        //given\n        final String methodName = METHOD_NAME;\n        final String parameterType = PARAMETER_TYPE;\n        final Map<String, Object> map = new HashMap<>();\n        map.put(DATA_TO_SET, businessData);\n\n        doReturn(SLeftOperand.TYPE_BUSINESS_DATA).when(leftOperand).getType();\n        doReturn(DATA_TO_SET).when(leftOperand).getName();\n        doReturn(methodName + \":\" + parameterType).when(operation).getOperator();\n        doReturn(map).when(context).getInputValues();\n\n        Address valueAfterCallOperation = new Address(20L);\n        Address valueAfterMerge = new Address(21L);\n        given(businessDataService.callJavaOperation(businessData, valuetoSetObjectWith, methodName, parameterType))\n                .willReturn(valueAfterCallOperation);\n        given(actionsExecutor.executeAction(valueAfterCallOperation, null, mergeEntityAction))\n                .willReturn(valueAfterMerge);\n\n        //when\n        Object newValue = strategy.computeNewValueForLeftOperand(operation, valuetoSetObjectWith, context, true);\n\n        //then\n        assertThat(newValue).isEqualTo(valueAfterMerge);\n\n    }\n\n    @Test\n    public void should_delegate_To_BusinessDataService_without_calling_merge_action_when_should_not_persist()\n            throws Exception {\n        //given\n        final String methodName = METHOD_NAME;\n        final String parameterType = PARAMETER_TYPE;\n        final Map<String, Object> map = new HashMap<>();\n        map.put(DATA_TO_SET, businessData);\n\n        doReturn(SLeftOperand.TYPE_BUSINESS_DATA).when(leftOperand).getType();\n        doReturn(DATA_TO_SET).when(leftOperand).getName();\n        doReturn(methodName + \":\" + parameterType).when(operation).getOperator();\n        doReturn(map).when(context).getInputValues();\n\n        Address valueAfterCallOperation = new Address(20L);\n        given(businessDataService.callJavaOperation(businessData, valuetoSetObjectWith, methodName, parameterType))\n                .willReturn(valueAfterCallOperation);\n\n        //when\n        Object newValue = strategy.computeNewValueForLeftOperand(operation, valuetoSetObjectWith, context, false);\n\n        //then\n        assertThat(newValue).isEqualTo(valueAfterCallOperation);\n        verify(actionsExecutor, never()).executeAction(businessData, null, mergeEntityAction);\n\n    }\n\n    @Test(expected = SOperationExecutionException.class)\n    public void should_throw_exception_when_businessDataService_throws_exception() throws Exception {\n        //given\n        final String methodName = METHOD_NAME;\n        final String parameterType = PARAMETER_TYPE;\n        final Map<String, Object> map = new HashMap<>();\n        map.put(DATA_TO_SET, businessData);\n\n        doReturn(SLeftOperand.TYPE_BUSINESS_DATA).when(leftOperand).getType();\n        doReturn(DATA_TO_SET).when(leftOperand).getName();\n        doReturn(methodName + \":\" + parameterType).when(operation).getOperator();\n        doReturn(map).when(context).getInputValues();\n\n        given(businessDataService.callJavaOperation(businessData, valuetoSetObjectWith, methodName, parameterType))\n                .willThrow(\n                        new SBusinessDataNotFoundException(\"\"));\n\n        //when\n        strategy.computeNewValueForLeftOperand(operation, valuetoSetObjectWith, context, false);\n\n        //then exception\n    }\n\n    @Test\n    public void shouldNotDelegateToBusinessDataService() throws Exception {\n        //given\n        doReturn(\"not business data type\").when(leftOperand).getType();\n        doReturn(null).when(strategy).computeJavaOperation(operation, valuetoSetObjectWith, context, false);\n        verifyNoMoreInteractions(businessDataService);\n\n        //when\n        strategy.computeNewValueForLeftOperand(operation, valuetoSetObjectWith, context, false);\n\n        //then\n        verify(strategy).computeJavaOperation(operation, valuetoSetObjectWith, context, false);\n\n    }\n\n    @Test\n    public void shouldPersistOnNullValue_should_return_true() throws Exception {\n        assertThat(strategy.shouldPersistOnNullValue()).isTrue();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/operation/BusinessDataLeftOperandHandlerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.operation;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.*;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.business.data.BusinessDataRepository;\nimport org.bonitasoft.engine.business.data.RefBusinessDataRetriever;\nimport org.bonitasoft.engine.commons.Container;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.operation.model.SLeftOperand;\nimport org.bonitasoft.engine.core.operation.model.impl.SLeftOperandImpl;\nimport org.bonitasoft.engine.core.process.instance.api.RefBusinessDataService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.operation.pojo.Employee;\nimport org.bonitasoft.engine.operation.pojo.Travel;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class BusinessDataLeftOperandHandlerTest {\n\n    public static final String PROCESS_INSTANCE = DataInstanceContainer.PROCESS_INSTANCE.name();\n    @Rule\n    public final ExpectedException expectedException = ExpectedException.none();\n\n    @Mock\n    private RefBusinessDataRetriever refBusinessDataRetriever;\n\n    @Mock\n    private BusinessDataRepository repository;\n\n    @Mock\n    private RefBusinessDataService refBusinessDataService;\n\n    @Mock\n    private EntitiesActionsExecutor actionsExecutor;\n\n    @Mock\n    private UpdateDataRefAction action;\n\n    @InjectMocks\n    private BusinessDataLeftOperandHandler leftOperandHandler;\n\n    @Test\n    public void should_retrieve_retrieve_the_business_data() throws Exception {\n        // given:\n        final String bizDataName = \"myTravel\";\n        final Travel myTravel = new Travel();\n\n        final BusinessDataLeftOperandHandler spy = spy(leftOperandHandler);\n        doReturn(myTravel).when(spy).getBusinessData(anyString(), anyLong(), anyString());\n        final Map<String, Object> inputValues = new HashMap<>(1);\n        final SExpressionContext expressionContext = new SExpressionContext(-1L, \"unused\", 987L, inputValues);\n        final SLeftOperand leftOperand = createLeftOperand(bizDataName);\n        // when:\n        spy.loadLeftOperandInContext(leftOperand, expressionContext.getContainerId(),\n                expressionContext.getContainerType(), expressionContext);\n\n        // then:\n        assertThat(expressionContext.getInputValues()).containsOnly(entry(\"myTravel\", myTravel));\n    }\n\n    private SLeftOperand createLeftOperand(final String bizDataName) {\n        final SLeftOperandImpl leftOperand = new SLeftOperandImpl();\n        leftOperand.setName(bizDataName);\n        return leftOperand;\n    }\n\n    @Test\n    public void shouldGetOperatorType_Return_BUSINESS_DATA_JAVA_SETTER() {\n        assertThat(leftOperandHandler.getType()).isEqualTo(SLeftOperand.TYPE_BUSINESS_DATA);\n    }\n\n    @Test\n    public void getBusinessDataCreateAnInstanceIfNoReferenceExists() throws Exception {\n        final SSimpleRefBusinessDataInstance refInstance = mock(SSimpleRefBusinessDataInstance.class);\n        final String bizDataName = \"employee\";\n        final int processInstanceId = 457;\n        BusinessDataContext businessDataContext = new BusinessDataContext(bizDataName,\n                new Container(processInstanceId, PROCESS_INSTANCE));\n        when(refBusinessDataRetriever.getRefBusinessDataInstance(eq(businessDataContext))).thenReturn(refInstance);\n        when(refInstance.getDataId()).thenReturn(null);\n        when(refInstance.getDataClassName()).thenReturn(Employee.class.getName());\n\n        final Employee employee = (Employee) leftOperandHandler.getBusinessData(bizDataName, processInstanceId,\n                PROCESS_INSTANCE);\n\n        assertThat(employee).isNotNull();\n        assertThat(employee.getPersistenceId()).isNull();\n        assertThat(employee.getFirstName()).isNull();\n        assertThat(employee.getLastName()).isNull();\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getBusinessDataDoesNotCreateAnInstanceIfNoReferenceExistsWhenTheClassNameIsWrong() throws Exception {\n        final SSimpleRefBusinessDataInstance refInstance = mock(SSimpleRefBusinessDataInstance.class);\n        final String bizDataName = \"employee\";\n        final int processInstanceId = 457;\n\n        leftOperandHandler.getBusinessData(bizDataName, processInstanceId, PROCESS_INSTANCE);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getBusinessDataDoesNotCreateAnInstanceIfNoReferenceExistsWhenTheClassNameIsAnInterface()\n            throws Exception {\n        leftOperandHandler.getBusinessData(\"employee\", 457, PROCESS_INSTANCE);\n    }\n\n    @Test\n    public void removeAndDereferenceBusinessData() throws Exception {\n        final SLeftOperandImpl leftOperand = new SLeftOperandImpl();\n        leftOperand.setName(\"address\");\n        final SSimpleRefBusinessDataInstance instance = mock(SSimpleRefBusinessDataInstance.class);\n        when(\n                refBusinessDataRetriever.getRefBusinessDataInstance(any())).thenReturn(instance);\n        when(instance.getDataClassName()).thenReturn(Address.class.getName());\n\n        leftOperandHandler.delete(leftOperand, 45, PROCESS_INSTANCE);\n\n        verify(refBusinessDataService).updateRefBusinessDataInstance(instance, null);\n        verify(repository).remove(nullable(Address.class));\n    }\n\n    @Test\n    public void deleteThrowsExceptionIfAnInternalExceptionOccurs() throws Exception {\n        //given\n        final SLeftOperandImpl leftOperand = new SLeftOperandImpl();\n        leftOperand.setName(\"address\");\n\n        given(refBusinessDataRetriever.getRefBusinessDataInstance(\n                new BusinessDataContext(leftOperand.getName(), new Container(45, PROCESS_INSTANCE))))\n                .willThrow(new SFlowNodeNotFoundException(45));\n\n        //then\n        expectedException.expect(SOperationExecutionException.class);\n\n        //when\n        leftOperandHandler.delete(leftOperand, 45, PROCESS_INSTANCE);\n    }\n\n    @Test\n    public void removeAndDereferenceAMultiBusinessData() throws Exception {\n        final SLeftOperandImpl leftOperand = new SLeftOperandImpl();\n        leftOperand.setName(\"address\");\n        final SProcessMultiRefBusinessDataInstance ref = mock(SProcessMultiRefBusinessDataInstance.class);\n        doReturn(ref).when(refBusinessDataRetriever).getRefBusinessDataInstance(any());\n        when(ref.getDataClassName()).thenReturn(Address.class.getName());\n        when(ref.getDataIds()).thenReturn(Arrays.asList(486L));\n\n        leftOperandHandler.delete(leftOperand, 45, PROCESS_INSTANCE);\n\n        verify(refBusinessDataService).updateRefBusinessDataInstance(ref, new ArrayList<Long>());\n        verify(repository).remove(nullable(Address.class));\n    }\n\n    @Test\n    public void getMultiBusinessDataReturnTheBusinessData() throws Exception {\n        final SProcessMultiRefBusinessDataInstance refInstance = mock(SProcessMultiRefBusinessDataInstance.class);\n        final String bizDataName = \"employee\";\n        final int processInstanceId = 457;\n        BusinessDataContext businessDataContext = new BusinessDataContext(bizDataName,\n                new Container(processInstanceId, PROCESS_INSTANCE));\n        when(refBusinessDataRetriever.getRefBusinessDataInstance(eq(businessDataContext))).thenReturn(refInstance);\n        when(refInstance.getDataIds()).thenReturn(Arrays.asList(45l));\n        when(refInstance.getDataClassName()).thenReturn(Employee.class.getName());\n\n        leftOperandHandler.getBusinessData(bizDataName, processInstanceId, PROCESS_INSTANCE);\n\n        verify(repository).findByIds(Employee.class, Arrays.asList(45l));\n    }\n\n    @Test\n    public void getMultiBusinessDataCreateAnInstanceIfNoReferenceExists() throws Exception {\n        final SProcessMultiRefBusinessDataInstance refInstance = mock(SProcessMultiRefBusinessDataInstance.class);\n        final String bizDataName = \"employee\";\n        final long processInstanceId = 457;\n        BusinessDataContext businessDataContext = new BusinessDataContext(bizDataName, new Container(processInstanceId,\n                PROCESS_INSTANCE));\n        when(refBusinessDataRetriever.getRefBusinessDataInstance(eq(businessDataContext))).thenReturn(refInstance);\n        when(refInstance.getDataIds()).thenReturn(new ArrayList<>());\n        when(refInstance.getDataClassName()).thenReturn(Employee.class.getName());\n\n        final List<Employee> employees = (List<Employee>) leftOperandHandler.getBusinessData(bizDataName,\n                processInstanceId, PROCESS_INSTANCE);\n        assertThat(employees).isEmpty();\n    }\n\n    @Test\n    public void update_should_execute_UpdateDataRefAction() throws Exception {\n        //given\n        Address address = new Address(15L);\n        String businessDataName = \"address\";\n        long containerId = 1L;\n        String containerType = \"PROCESS\";\n        BusinessDataContext businessDataContext = new BusinessDataContext(businessDataName,\n                new Container(containerId, containerType));\n        given(actionsExecutor.executeAction(eq(address), eq(businessDataContext), eq(action)))\n                .willReturn(address);\n\n        //when\n        Object result = leftOperandHandler.update(createLeftOperand(businessDataName),\n                Collections.<String, Object> emptyMap(), address, containerId,\n                containerType);\n\n        //then\n        assertThat(result).isEqualTo(address);\n    }\n\n    @Test(expected = SOperationExecutionException.class)\n    public void update_should_throws_operation_exception_when_update_data_ref_action_fails() throws Exception {\n        //given\n        Address address = new Address(15L);\n        String businessDataName = \"address\";\n        long containerId = 1L;\n        String containerType = \"PROCESS\";\n        BusinessDataContext businessDataContext = new BusinessDataContext(businessDataName,\n                new Container(containerId, containerType));\n        given(actionsExecutor.executeAction(eq(address), eq(businessDataContext), eq(action))).willThrow(\n                new SEntityActionExecutionException(\"\"));\n\n        //when\n        leftOperandHandler.update(createLeftOperand(businessDataName), Collections.<String, Object> emptyMap(), address,\n                containerId, containerType);\n\n        //then exception\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/operation/DataLeftOperandHandlerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.operation;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\n\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.operation.model.SLeftOperand;\nimport org.bonitasoft.engine.core.operation.model.impl.SLeftOperandImpl;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceService;\nimport org.bonitasoft.engine.data.instance.api.ParentContainerResolver;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SShortTextDataInstance;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class DataLeftOperandHandlerTest {\n\n    private static final long CONTAINER_ID = 125l;\n    private static final String CONTAINER_TYPE = \"CONTAINER_TYPE\";\n    private static final long PROCESS_DEFINITION_ID = 123456789l;\n    @Mock\n    private DataInstanceService dataInstanceService;\n    @Mock\n    private ParentContainerResolver parentContainerResolver;\n    @InjectMocks\n    private DataLeftOperandHandler handler;\n\n    private SLeftOperandImpl createLeftOperand(final String name) {\n        final SLeftOperandImpl sLeftOperand = new SLeftOperandImpl();\n        sLeftOperand.setName(name);\n        return sLeftOperand;\n    }\n\n    @Test(expected = SOperationExecutionException.class)\n    public void deleteThrowsAnExceptionNotYetSupported() throws Exception {\n        handler.delete(createLeftOperand(\"myData\"), 45l, \"container\");\n    }\n\n    @Test\n    public void loadLeftOperandInContext_should_do_one_request() throws Exception {\n        //given\n        SExpressionContext sExpressionContext = createContext();\n        HashMap<String, Object> context = new HashMap<String, Object>();\n        SDataInstance data1 = data(\"data1\", \"value1\");\n        SDataInstance data2 = data(\"data2\", \"value2\");\n        doReturn(Arrays.asList(data1, data2)).when(dataInstanceService).getDataInstances(\n                Arrays.asList(\"data2\", \"data1\"), CONTAINER_ID, CONTAINER_TYPE, parentContainerResolver);\n        //when\n        handler.loadLeftOperandInContext(Arrays.asList(leftOperand(\"data2\"), leftOperand(\"data1\")),\n                sExpressionContext.getContainerId(), sExpressionContext.getContainerType(), sExpressionContext);\n        verify(dataInstanceService).getDataInstances(Arrays.asList(\"data2\", \"data1\"), CONTAINER_ID, CONTAINER_TYPE,\n                parentContainerResolver);\n        verifyNoMoreInteractions(dataInstanceService);\n        //then\n        assertThat(sExpressionContext.getInputValues()).containsOnly(entry(\"data1\", \"value1\"),\n                entry(\"%DATA_INSTANCE%_data1\", data1), entry(\"data2\", \"value2\"), entry(\"%DATA_INSTANCE%_data2\", data2));\n    }\n\n    @Test\n    public void loadLeftOperandInContext_on_single_left_operand() throws Exception {\n        //given\n        SExpressionContext sExpressionContext = createContext();\n        HashMap<String, Object> context = new HashMap<String, Object>();\n        SDataInstance data1 = data(\"data1\", \"value1\");\n        doReturn(data1).when(dataInstanceService).getDataInstance(\"data1\", CONTAINER_ID, CONTAINER_TYPE,\n                parentContainerResolver);\n        //when\n        handler.loadLeftOperandInContext(leftOperand(\"data1\"), sExpressionContext.getContainerId(),\n                sExpressionContext.getContainerType(), sExpressionContext);\n        verify(dataInstanceService).getDataInstance(\"data1\", CONTAINER_ID, CONTAINER_TYPE, parentContainerResolver);\n        verifyNoMoreInteractions(dataInstanceService);\n        //then\n        assertThat(sExpressionContext.getInputValues()).containsOnly(entry(\"data1\", \"value1\"),\n                entry(\"%DATA_INSTANCE%_data1\", data1));\n    }\n\n    @Test\n    public void update_should_get_from_context_data_instances() throws Exception {\n        //given\n        HashMap<String, Object> context = new HashMap<String, Object>();\n        SDataInstance data2 = data(\"data2\", \"value2\");\n        context.put(\"%DATA_INSTANCE%_data2\", data2);\n        //when\n        handler.update(leftOperand(\"data2\"), context, \"newValue\", CONTAINER_ID, CONTAINER_TYPE);\n\n        ArgumentCaptor<EntityUpdateDescriptor> captor = ArgumentCaptor.forClass(EntityUpdateDescriptor.class);\n        verify(dataInstanceService).updateDataInstance(eq(data2), captor.capture());\n        verifyNoMoreInteractions(dataInstanceService);\n        //then\n        assertThat(captor.getValue().getFields().get(\"value\")).isEqualTo(\"newValue\");\n    }\n\n    @Test\n    public void update_should_get_from_service_data_instances_when_not_in_context() throws Exception {\n        //given\n        HashMap<String, Object> context = new HashMap<String, Object>();\n        SDataInstance data2 = data(\"data2\", \"value2\");\n        doReturn(data2).when(dataInstanceService).getDataInstance(\"data2\", CONTAINER_ID, CONTAINER_TYPE,\n                parentContainerResolver);\n        //when\n        handler.update(leftOperand(\"data2\"), context, \"newValue\", CONTAINER_ID, CONTAINER_TYPE);\n\n        ArgumentCaptor<EntityUpdateDescriptor> captor = ArgumentCaptor.forClass(EntityUpdateDescriptor.class);\n        verify(dataInstanceService).updateDataInstance(eq(data2), captor.capture());\n        verify(dataInstanceService).getDataInstance(\"data2\", CONTAINER_ID, CONTAINER_TYPE, parentContainerResolver);\n        verifyNoMoreInteractions(dataInstanceService);\n        //then\n        assertThat(captor.getValue().getFields().get(\"value\")).isEqualTo(\"newValue\");\n    }\n\n    private SDataInstance data(String data1, String value1) {\n        SShortTextDataInstance sShortTextDataInstance = new SShortTextDataInstance();\n        sShortTextDataInstance.setName(data1);\n        sShortTextDataInstance.setValue(value1);\n        return sShortTextDataInstance;\n    }\n\n    private SExpressionContext createContext() {\n        return new SExpressionContext(CONTAINER_ID, CONTAINER_TYPE, PROCESS_DEFINITION_ID);\n    }\n\n    private SLeftOperand leftOperand(String name) {\n        SLeftOperandImpl sLeftOperand = new SLeftOperandImpl();\n        sLeftOperand.setName(name);\n        return sLeftOperand;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/operation/DocumentLeftOperandHandlerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.operation;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.ArgumentMatchers.isNull;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Collections;\nimport java.util.HashMap;\n\nimport org.bonitasoft.engine.bpm.document.DocumentValue;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstance;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.core.document.api.DocumentService;\nimport org.bonitasoft.engine.core.document.model.SDocument;\nimport org.bonitasoft.engine.core.document.model.SMappedDocument;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.operation.model.impl.SLeftOperandImpl;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class DocumentLeftOperandHandlerTest {\n\n    @Rule\n    public final ExpectedException exception = ExpectedException.none();\n\n    @Mock\n    private DocumentService documentService;\n\n    @Mock\n    private ActivityInstanceService activityInstanceService;\n\n    @Mock\n    private SessionAccessor sessionAccessor;\n\n    @Mock\n    private SessionService sessionService;\n\n    @InjectMocks\n    private DocumentLeftOperandHandler handler;\n\n    private SLeftOperandImpl createLeftOperand(final String name) {\n        final SLeftOperandImpl sLeftOperand = new SLeftOperandImpl();\n        sLeftOperand.setName(name);\n        return sLeftOperand;\n    }\n\n    @Test\n    public void deleteThrowsAnExceptionNotYetSupported() throws Exception {\n        exception.expect(SOperationExecutionException.class);\n        exception.expectMessage(\"Deleting a document is not supported\");\n        handler.delete(createLeftOperand(\"myData\"), 45l, \"container\");\n    }\n\n    @Test\n    public void should_update_check_the_type() throws Exception {\n        exception.expect(SOperationExecutionException.class);\n        exception.expectMessage(\n                \"Document operation only accepts an expression returning a DocumentValue and not java.util.HashMap\");\n        handler.update(new SLeftOperandImpl(), Collections.<String, Object> emptyMap(), new HashMap<Object, Object>(),\n                45l, \"container\");\n    }\n\n    @Test\n    public void should_update_delete_if_type_is_null() throws Exception {\n        handler.update(createLeftOperand(\"myDoc\"), Collections.<String, Object> emptyMap(), null, 45l,\n                \"PROCESS_INSTANCE\");\n        verify(documentService).removeCurrentVersion(45l, \"myDoc\");\n    }\n\n    @Test\n    public void should_update_create_doc_if_not_exists() throws Exception {\n        //given\n        doThrow(new SObjectNotFoundException(\"myDoc\")).when(documentService).getMappedDocument(45l, \"myDoc\");\n        //when\n        handler.update(createLeftOperand(\"myDoc\"), Collections.<String, Object> emptyMap(),\n                new DocumentValue(\"content\".getBytes(), \"plain/text\", \"file.txt\"), 45l, \"PROCESS_INSTANCE\");\n        //then\n        verify(documentService).attachDocumentToProcessInstance(any(SDocument.class), eq(45l), eq(\"myDoc\"),\n                isNull(String.class));\n    }\n\n    @Test\n    public void should_update_update_doc_if_exists() throws Exception {\n        //given\n        final SMappedDocument sMappedDocument = mock(SMappedDocument.class);\n        doReturn(sMappedDocument).when(documentService).getMappedDocument(45l, \"myDoc\");\n        //when\n        handler.update(createLeftOperand(\"myDoc\"), Collections.<String, Object> emptyMap(),\n                new DocumentValue(\"content\".getBytes(), \"plain/text\", \"file.txt\"), 45l, \"PROCESS_INSTANCE\");\n        //then\n        verify(documentService).updateDocument(eq(sMappedDocument), any(SDocument.class));\n    }\n\n    public void should_update_find_process_id() throws Exception {\n        //given\n        final FlowNodeInstance flowNodeInstance = mock(FlowNodeInstance.class);\n        doReturn(45l).when(flowNodeInstance).getParentProcessInstanceId();\n        doReturn(flowNodeInstance).when(activityInstanceService).getFlowNodeInstance(12l);\n        final SMappedDocument sMappedDocument = mock(SMappedDocument.class);\n        doReturn(sMappedDocument).when(documentService).getMappedDocument(45l, \"myDoc\");\n        //when\n        handler.update(createLeftOperand(\"myDoc\"), Collections.<String, Object> emptyMap(),\n                new DocumentValue(\"content\".getBytes(), \"plain/text\", \"file.txt\"), 12l, \"notProcess\");\n        //then\n        verify(documentService).updateDocument(eq(sMappedDocument), any(SDocument.class));\n\n    }\n\n    @Test\n    public void should_not_update_if_has_change_is_false() throws Exception {\n        //when\n        handler.update(createLeftOperand(\"myDoc\"), Collections.<String, Object> emptyMap(), new DocumentValue(123l),\n                45l, \"PROCESS_INSTANCE\");\n        //then\n        verify(documentService, times(0)).updateDocument(any(SMappedDocument.class), any(SDocument.class));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/operation/DocumentListLeftOperandHandlerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.operation;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyLong;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.Mockito.*;\nimport static org.mockito.Mockito.anyList;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.document.DocumentValue;\nimport org.bonitasoft.engine.core.document.api.impl.DocumentHelper;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.operation.model.impl.SLeftOperandImpl;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class DocumentListLeftOperandHandlerTest {\n\n    public static final long CONTAINER_ID = 45l;\n    public static final String CONTAINER_TYPE = \"container\";\n    public static final long PROCESS_INSTANCE_ID = 45l;\n    private static final long LOGGED_USER_ID = 125;\n    @Rule\n    public final ExpectedException exception = ExpectedException.none();\n\n    @Mock\n    private ActivityInstanceService activityInstanceService;\n\n    @Mock\n    private SessionAccessor sessionAccessor;\n\n    @Mock\n    private SessionService sessionService;\n    @Mock\n    private ProcessDefinitionService processDefinitionService;\n    @Mock\n    private ProcessInstanceService processInstanceService;\n    @Mock\n    private SProcessInstance processInstance;\n    @Mock\n    private SProcessDefinition processDefinition;\n    @Mock\n    private SFlowElementContainerDefinition flowElementContainerDefinition;\n    @Mock\n    private DocumentHelper documentHelper;\n\n    private DocumentListLeftOperandHandler handler;\n\n    @Before\n    public void setUp() throws SFlowNodeReadException, SFlowNodeNotFoundException {\n        handler = new DocumentListLeftOperandHandler(activityInstanceService, sessionAccessor, sessionService,\n                documentHelper);\n        doReturn(LOGGED_USER_ID).when(sessionService).getLoggedUserFromSession(sessionAccessor);\n\n        SFlowNodeInstance flowNodeInstance = mock(SFlowNodeInstance.class);\n        doReturn(flowNodeInstance).when(activityInstanceService).getFlowNodeInstance(CONTAINER_ID);\n        doReturn(PROCESS_INSTANCE_ID).when(flowNodeInstance).getParentProcessInstanceId();\n    }\n\n    private SLeftOperandImpl createLeftOperand(final String name) {\n        final SLeftOperandImpl sLeftOperand = new SLeftOperandImpl();\n        sLeftOperand.setName(name);\n        return sLeftOperand;\n    }\n\n    @Test\n    public void should_update_check_it_is_a_list() throws Exception {\n        doThrow(SOperationExecutionException.class).when(documentHelper).toCheckedList(any());\n        exception.expect(SOperationExecutionException.class);\n        handler.update(createLeftOperand(\"myDoc\"), Collections.<String, Object> emptyMap(), new HashMap<>(), 45l,\n                \"container\");\n    }\n\n    @Test\n    public void should_update_setDocumentList() throws Exception {\n        doNothing().when(documentHelper).setDocumentList(anyList(), anyString(), anyLong(), anyLong());\n        List<DocumentValue> newValue = Arrays.asList(documentValue(\"doc1\"), documentValue(\"doc2\"));\n        doReturn(newValue).when(documentHelper).toCheckedList(newValue);\n        handler.update(createLeftOperand(\"myDoc\"), Collections.<String, Object> emptyMap(), newValue, CONTAINER_ID,\n                CONTAINER_TYPE);\n        verify(documentHelper).setDocumentList(newValue, \"myDoc\", PROCESS_INSTANCE_ID, LOGGED_USER_ID);\n    }\n\n    private DocumentValue documentValue(String name) {\n        return new DocumentValue(name);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/operation/EntitiesActionsExecutorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.operation;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.bonitasoft.engine.commons.Container;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class EntitiesActionsExecutorTest {\n\n    @InjectMocks\n    private EntitiesActionsExecutor actionsExecutor;\n\n    @Mock\n    private EntityAction action;\n\n    private final BusinessDataContext businessDataContext = new BusinessDataContext(\"data\",\n            new Container(1L, \"process\"));\n\n    @Test\n    public void executeAction_should_execute_action_on_entity_if_its_a_simple_entity() throws Exception {\n        //given\n        final Entity entity = mock(Entity.class);\n        final Entity entityAfterAction = mock(Entity.class);\n        given(action.execute(entity, businessDataContext)).willReturn(entityAfterAction);\n\n        //when\n        final Object actionResult = actionsExecutor.executeAction(entity, businessDataContext, action);\n\n        //then\n        assertThat(actionResult).isEqualTo(entityAfterAction);\n        verify(action, never()).execute(anyList(), any(BusinessDataContext.class));\n    }\n\n    @Test\n    public void executeAction_should_execute_action_on_list_if_its_a_list_of_entities() throws Exception {\n        //given\n        final List<Entity> entities = Collections.singletonList(mock(Entity.class));\n        final List<Entity> entitiesAfterAction = Collections.singletonList(mock(Entity.class));\n        given(action.execute(entities, businessDataContext)).willReturn(entitiesAfterAction);\n\n        //when\n        final Object actionResult = actionsExecutor.executeAction(entities, businessDataContext, action);\n\n        //then\n        assertThat(actionResult).isEqualTo(entitiesAfterAction);\n        verify(action, never()).execute(any(Entity.class), any(BusinessDataContext.class));\n    }\n\n    @Test(expected = SEntityActionExecutionException.class)\n    public void execute_action_should_throw_IllegalStateException_when_value_is_not_valid() throws Exception {\n        //when\n        actionsExecutor.executeAction(\"not entity, neither list of entity\", businessDataContext, action);\n\n        //then exception\n    }\n\n    @Test\n    public void execute_should_handle_the_value_nullity() throws Exception {\n        //when\n        actionsExecutor.executeAction(null, businessDataContext, action);\n\n        //then exception\n        verify(action).handleNull(businessDataContext);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/operation/MergeEntityActionTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.operation;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.bonitasoft.engine.business.data.BusinessDataRepository;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class MergeEntityActionTest {\n\n    @Mock\n    private BusinessDataRepository repository;\n\n    @InjectMocks\n    private MergeEntityAction mergeEntityAction;\n\n    @Test\n    public void execute_should_return_merged_entity() throws Exception {\n        //given\n        final Entity entity = mock(Entity.class);\n        final Entity mergedEntity = mock(Entity.class);\n        given(repository.merge(entity)).willReturn(mergedEntity);\n\n        //when\n        final Entity actionResult = mergeEntityAction.execute(entity, null);\n\n        //then\n        assertThat(actionResult).isEqualTo(mergedEntity);\n    }\n\n    @Test(expected = SEntityActionExecutionException.class)\n    public void execute_should_throws_exception_if_entity_is_null() throws Exception {\n        //when\n        mergeEntityAction.execute((Entity) null, null);\n\n        //then exception\n    }\n\n    @Test\n    public void execute_should_return_list_of_merged_entities() throws Exception {\n        //given\n        final Entity entity1 = mock(Entity.class);\n        final Entity entity2 = mock(Entity.class);\n        final Entity mergedEntity1 = mock(Entity.class);\n        final Entity mergedEntity2 = mock(Entity.class);\n        given(repository.merge(entity1)).willReturn(mergedEntity1);\n        given(repository.merge(entity2)).willReturn(mergedEntity2);\n\n        //when\n        final List<Entity> actionResult = mergeEntityAction.execute(Arrays.asList(entity1, null, entity2), null);\n\n        //then\n        assertThat(actionResult).containsExactly(mergedEntity1, mergedEntity2);\n    }\n\n    @Test(expected = SEntityActionExecutionException.class)\n    public void handleNull_throw_an_exception_impossible_to_merge_a_null_entity() throws Exception {\n        mergeEntityAction.handleNull(null);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/operation/StringIndexLeftOperandHandlerTest.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.operation;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.verify;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.operation.model.impl.SLeftOperandImpl;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class StringIndexLeftOperandHandlerTest {\n\n    private static final long FLOWNODE_ID = 3248907432L;\n    private static final long CALLED_PROCESS_FLOW_NODE_ID = 654907432L;\n    private static final long SUB_PROCESS_FLOW_NODE_ID = 123327432L;\n    private static final long PROCESS_INSTANCE_ID = 9019873L;\n    private static final long SUB_PROCESS_INSTANCE_ID = 906234273L;\n    private static final long CALLED_PROCESS_INSTANCE_ID = 43273L;\n    @Mock\n    private ProcessInstanceService processInstanceService;\n\n    @Mock\n    private ActivityInstanceService activityInstanceService;\n\n    @InjectMocks\n    private StringIndexLeftOperandHandler handler;\n    @Captor\n    private ArgumentCaptor<EntityUpdateDescriptor> entityUpdateDescriptorArgumentCaptor;\n    private SUserTaskInstance userTaskInstance;\n    private SProcessInstance processInstance;\n    private SProcessInstance subProcessInstance;\n    private SProcessInstance calledProcessInstance;\n    private SUserTaskInstance taskOfCalledProcess;\n    private SUserTaskInstance taskOfSubProcess;\n\n    @Before\n    public void before() throws Exception {\n\n        userTaskInstance = aTaskInstance(FLOWNODE_ID);\n        taskOfCalledProcess = aTaskInstance(CALLED_PROCESS_FLOW_NODE_ID);\n        taskOfSubProcess = aTaskInstance(SUB_PROCESS_FLOW_NODE_ID);\n\n        processInstance = aProcessInstance(PROCESS_INSTANCE_ID);\n        calledProcessInstance = aProcessInstance(CALLED_PROCESS_INSTANCE_ID);\n        calledProcessInstance.setCallerType(SFlowNodeType.CALL_ACTIVITY);\n        calledProcessInstance.setCallerId(userTaskInstance.getId());\n        subProcessInstance = aProcessInstance(SUB_PROCESS_INSTANCE_ID);\n        subProcessInstance.setCallerType(SFlowNodeType.SUB_PROCESS);\n        subProcessInstance.setCallerId(taskOfCalledProcess.getId());\n\n        userTaskInstance.setLogicalGroup(3, processInstance.getId());\n        taskOfCalledProcess.setLogicalGroup(3, calledProcessInstance.getId());\n        taskOfSubProcess.setLogicalGroup(3, subProcessInstance.getId());\n    }\n\n    private SProcessInstance aProcessInstance(long id)\n            throws SProcessInstanceNotFoundException, SProcessInstanceReadException {\n        SProcessInstance processInstance = new SProcessInstance();\n        processInstance.setId(id);\n        doReturn(processInstance).when(processInstanceService).getProcessInstance(id);\n        return processInstance;\n    }\n\n    private SUserTaskInstance aTaskInstance(long id) throws SFlowNodeNotFoundException, SFlowNodeReadException {\n        SUserTaskInstance userTaskInstance = new SUserTaskInstance();\n        userTaskInstance.setId(id);\n        doReturn(userTaskInstance).when(activityInstanceService).getFlowNodeInstance(id);\n        return userTaskInstance;\n    }\n\n    @Test(expected = SOperationExecutionException.class)\n    public void deleteThrowsAnExceptionNotYetSupported() throws Exception {\n        handler.delete(stringIndex(\"2\"), 45l, \"container\");\n    }\n\n    @Test\n    public void should_update_string_index_of_process_when_on_flownode() throws Exception {\n        Map<String, Object> objectObjectMap = Collections.emptyMap();\n\n        handler.update(stringIndex(\"1\"), objectObjectMap, \"newValue\", FLOWNODE_ID,\n                DataInstanceContainer.ACTIVITY_INSTANCE.name());\n\n        verify(processInstanceService).updateProcess(eq(processInstance),\n                entityUpdateDescriptorArgumentCaptor.capture());\n        assertThat(entityUpdateDescriptorArgumentCaptor.getValue().getFields())\n                .containsOnly(entry(\"stringIndex1\", \"newValue\"));\n    }\n\n    @Test\n    public void should_update_string_index_of_process_when_on_flownode_in_called_process() throws Exception {\n        Map<String, Object> objectObjectMap = Collections.emptyMap();\n\n        handler.update(stringIndex(\"2\"), objectObjectMap, \"newValue\", CALLED_PROCESS_FLOW_NODE_ID,\n                DataInstanceContainer.ACTIVITY_INSTANCE.name());\n\n        verify(processInstanceService).updateProcess(eq(calledProcessInstance),\n                entityUpdateDescriptorArgumentCaptor.capture());\n        assertThat(entityUpdateDescriptorArgumentCaptor.getValue().getFields())\n                .containsOnly(entry(\"stringIndex2\", \"newValue\"));\n    }\n\n    @Test\n    public void should_update_string_index_of_parent_process_when_on_flownode_in_subProcess() throws Exception {\n        Map<String, Object> objectObjectMap = Collections.emptyMap();\n\n        handler.update(stringIndex(\"3\"), objectObjectMap, \"newValue\", SUB_PROCESS_FLOW_NODE_ID,\n                DataInstanceContainer.ACTIVITY_INSTANCE.name());\n\n        verify(processInstanceService).updateProcess(eq(calledProcessInstance),\n                entityUpdateDescriptorArgumentCaptor.capture());\n        assertThat(entityUpdateDescriptorArgumentCaptor.getValue().getFields())\n                .containsOnly(entry(\"stringIndex3\", \"newValue\"));\n    }\n\n    @Test\n    public void should_update_string_index_of_given_process() throws Exception {\n        Map<String, Object> objectObjectMap = Collections.emptyMap();\n\n        handler.update(stringIndex(\"4\"), objectObjectMap, \"newValue\", PROCESS_INSTANCE_ID,\n                DataInstanceContainer.PROCESS_INSTANCE.name());\n\n        verify(processInstanceService).updateProcess(eq(processInstance),\n                entityUpdateDescriptorArgumentCaptor.capture());\n        assertThat(entityUpdateDescriptorArgumentCaptor.getValue().getFields())\n                .containsOnly(entry(\"stringIndex4\", \"newValue\"));\n    }\n\n    @Test(expected = SOperationExecutionException.class)\n    public void should_fail_when_updating_string_index_with_wrong_index() throws Exception {\n        Map<String, Object> objectObjectMap = Collections.emptyMap();\n\n        handler.update(stringIndex(\"0\"), objectObjectMap, \"newValue\", PROCESS_INSTANCE_ID,\n                DataInstanceContainer.PROCESS_INSTANCE.name());\n    }\n\n    @Test(expected = SOperationExecutionException.class)\n    public void should_fail_when_updating_string_index_with_not_a_number_index() throws Exception {\n        Map<String, Object> objectObjectMap = Collections.emptyMap();\n\n        handler.update(stringIndex(\"toto\"), objectObjectMap, \"newValue\", PROCESS_INSTANCE_ID,\n                DataInstanceContainer.PROCESS_INSTANCE.name());\n    }\n\n    @Test(expected = SOperationExecutionException.class)\n    public void should_fail_when_updating_string_index_with_value_that_is_not_string() throws Exception {\n        Map<String, Object> objectObjectMap = Collections.emptyMap();\n\n        handler.update(stringIndex(\"1\"), objectObjectMap, 1231, PROCESS_INSTANCE_ID,\n                DataInstanceContainer.PROCESS_INSTANCE.name());\n    }\n\n    private SLeftOperandImpl stringIndex(String index) {\n        SLeftOperandImpl sLeftOperand = new SLeftOperandImpl();\n        sLeftOperand.setName(index);\n        return sLeftOperand;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/operation/TransientDataLeftOperandHandlerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.operation;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport java.util.Collections;\n\nimport org.bonitasoft.engine.core.data.instance.TransientDataService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.operation.model.impl.SLeftOperandImpl;\nimport org.bonitasoft.engine.data.instance.model.SShortTextDataInstance;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.SystemOutRule;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class TransientDataLeftOperandHandlerTest {\n\n    @Rule\n    public SystemOutRule systemOutRule = new SystemOutRule().enableLog();\n    @Mock\n    private TransientDataService transientDataService;\n\n    @InjectMocks\n    private TransientDataLeftOperandHandler transientDataLeftOperandHandler;\n\n    @Test\n    public void should_update_call_transient_data_service() throws Exception {\n        // given\n        final SShortTextDataInstance data = createData();\n        when(transientDataService.getDataInstance(\"myData\", 42, \"ctype\")).thenReturn(data);\n        systemOutRule.clearLog();\n        // when\n        transientDataLeftOperandHandler.update(createLeftOperand(\"myData\"), Collections.<String, Object> emptyMap(),\n                \"new Value\", 42, \"ctype\");\n\n        // then\n        final EntityUpdateDescriptor entityUpdateDescriptor = new EntityUpdateDescriptor();\n        entityUpdateDescriptor.addField(\"value\", \"new Value\");\n        verify(transientDataService, times(1)).updateDataInstance(eq(data), eq(entityUpdateDescriptor));\n        assertThat(systemOutRule.getLog()).containsPattern(\"WARN.*\");\n    }\n\n    private SLeftOperandImpl createLeftOperand(final String name) {\n        final SLeftOperandImpl sLeftOperand = new SLeftOperandImpl();\n        sLeftOperand.setName(name);\n        return sLeftOperand;\n    }\n\n    @Test\n    public void should_retrieve_get_data_from_transient_service() throws Exception {\n        // given\n        final SShortTextDataInstance data = createData();\n        when(transientDataService.getDataInstance(\"myData\", 42, \"ctype\")).thenReturn(data);\n        SExpressionContext sExpressionContext = new SExpressionContext(42l, \"ctype\", 12l);\n        // when\n        transientDataLeftOperandHandler.loadLeftOperandInContext(createLeftOperand(\"myData\"),\n                sExpressionContext.getContainerId(), sExpressionContext.getContainerType(), sExpressionContext);\n\n        // then\n        assertThat(sExpressionContext.getInputValues()).containsOnly(entry(\"myData\", data.getValue()),\n                entry(\"%TRANSIENT_DATA%_myData\", data));\n    }\n\n    private SShortTextDataInstance createData() {\n        final SShortTextDataInstance data = new SShortTextDataInstance();\n        data.setName(\"myData\");\n        data.setId(56);\n        data.setValue(\"The data value\");\n        return data;\n    }\n\n    @Test(expected = SOperationExecutionException.class)\n    public void deleteThrowsAnExceptionNotYetSupported() throws Exception {\n        transientDataLeftOperandHandler.delete(createLeftOperand(\"myData\"), 45l, \"container\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/operation/UpdateDataRefActionTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.operation;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyLong;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.doThrow;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\n\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.bonitasoft.engine.business.data.RefBusinessDataRetriever;\nimport org.bonitasoft.engine.commons.Container;\nimport org.bonitasoft.engine.core.process.instance.api.RefBusinessDataService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceModificationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class UpdateDataRefActionTest {\n\n    public static final long FLOW_NODE_INSTANCE_ID = 123L;\n    public static final long PROCESS_INSTANCE_ID = 1L;\n\n    @Mock\n    private RefBusinessDataService refBusinessDataService;\n\n    @Mock\n    private RefBusinessDataRetriever refBusinessDataRetriever;\n\n    @InjectMocks\n    private UpdateDataRefAction updateDataRefAction;\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    public static final String PROCESS_INSTANCE = DataInstanceContainer.PROCESS_INSTANCE.name();\n    public static final String ACTIVITY_INSTANCE = DataInstanceContainer.ACTIVITY_INSTANCE.name();\n\n    @Test\n    public void execute_should_update_simple_business_data_when_without_previous_refDataId() throws Exception {\n        //given\n        final String dataName = \"address\";\n        final BusinessDataContext businessDataContext = new BusinessDataContext(dataName,\n                new Container(FLOW_NODE_INSTANCE_ID, ACTIVITY_INSTANCE));\n        final UpdateDataRefAction updateDataRefAction = new UpdateDataRefAction(refBusinessDataService,\n                refBusinessDataRetriever);\n        final SSimpleRefBusinessDataInstance refBusinessDataInstance = createSimpleRefBusinessDataInstance(null);\n        given(refBusinessDataRetriever.getRefBusinessDataInstance(businessDataContext))\n                .willReturn(refBusinessDataInstance);\n\n        //when\n        final Address entity = new Address(45L);\n        updateDataRefAction.execute(entity, businessDataContext);\n\n        //then\n        verify(refBusinessDataService).updateRefBusinessDataInstance(refBusinessDataInstance,\n                entity.getPersistenceId());\n    }\n\n    @Test\n    public void execute_should_not_update_simple_business_data_when_refDataId_is_up_to_date() throws Exception {\n        //given\n        final long persistenceId = 45L;\n        final String dataName = \"address\";\n        final BusinessDataContext businessDataContext = buildContext(dataName);\n        final SSimpleRefBusinessDataInstance refBusinessDataInstance = createSimpleRefBusinessDataInstance(\n                persistenceId);\n        given(refBusinessDataRetriever.getRefBusinessDataInstance(businessDataContext))\n                .willReturn(refBusinessDataInstance);\n\n        //when\n        updateDataRefAction.execute(new Address(persistenceId), businessDataContext);\n\n        //then\n        verify(refBusinessDataService, never()).updateRefBusinessDataInstance(any(SSimpleRefBusinessDataInstance.class),\n                anyLong());\n    }\n\n    @Test\n    public void execute_should_update_simple_business_data_when_with_previous_refDataId() throws Exception {\n        //given\n        final long persistenceId = 45L;\n        final String dataName = \"address\";\n        final BusinessDataContext businessDataContext = buildContext(dataName);\n        final SSimpleRefBusinessDataInstance refBusinessDataInstance = createSimpleRefBusinessDataInstance(30L);\n        given(refBusinessDataRetriever.getRefBusinessDataInstance(businessDataContext))\n                .willReturn(refBusinessDataInstance);\n\n        //when\n        updateDataRefAction.execute(new Address(persistenceId), businessDataContext);\n\n        //then\n        verify(refBusinessDataService).updateRefBusinessDataInstance(refBusinessDataInstance, persistenceId);\n    }\n\n    private SSimpleRefBusinessDataInstance createSimpleRefBusinessDataInstance(final Long dataId) {\n        final SSimpleRefBusinessDataInstance sRefBusinessDataInstance = new SProcessSimpleRefBusinessDataInstance();\n        sRefBusinessDataInstance.setDataId(dataId);\n        return sRefBusinessDataInstance;\n    }\n\n    @Test\n    public void execute_should_throw_exception_when_refbusinessDataRetriever_throws_exception() throws Exception {\n        //given\n        final String dataName = \"address\";\n        final BusinessDataContext businessDataContext = buildContext(dataName);\n        given(refBusinessDataRetriever.getRefBusinessDataInstance(businessDataContext)).willThrow(\n                new SRefBusinessDataInstanceNotFoundException(PROCESS_INSTANCE_ID, dataName));\n\n        //then\n        expectedException.expect(SEntityActionExecutionException.class);\n\n        //when\n        final Address entity = new Address(45L);\n        updateDataRefAction.execute(entity, businessDataContext);\n\n        //then exception\n    }\n\n    @Test\n    public void execute_should_throws_exception_when_it_is_simple_entity_and_multi_ref_data() throws Exception {\n        //given\n        final String dataName = \"address\";\n        final BusinessDataContext businessDataContext = buildContext(dataName);\n        final SProcessMultiRefBusinessDataInstance refBusinessDataInstance = mock(\n                SProcessMultiRefBusinessDataInstance.class);\n        given(refBusinessDataRetriever.getRefBusinessDataInstance(businessDataContext))\n                .willReturn(refBusinessDataInstance);\n\n        //then\n        expectedException.expect(SEntityActionExecutionException.class);\n\n        //when\n        final Address entity = new Address(45L);\n        updateDataRefAction.execute(entity, businessDataContext);\n    }\n\n    @Test\n    public void execute_should_update_multiple_business_data_without_refDataIds_at_process_level() throws Exception {\n        //given\n        final String dataName = \"addresses\";\n        final BusinessDataContext businessDataContext = buildContext(dataName);\n        final SProcessMultiRefBusinessDataInstance refBusinessDataInstance = mock(\n                SProcessMultiRefBusinessDataInstance.class);\n        given(refBusinessDataRetriever.getRefBusinessDataInstance(businessDataContext))\n                .willReturn(refBusinessDataInstance);\n\n        //when\n        updateDataRefAction.execute(Arrays.<Entity> asList(new Address(45L), new Address(46L)), businessDataContext);\n\n        //then\n        verify(refBusinessDataService).updateRefBusinessDataInstance(refBusinessDataInstance, Arrays.asList(45L, 46L));\n    }\n\n    private SProcessMultiRefBusinessDataInstance createMultiRefBusinessDataInstance(final Long... dataIds) {\n        final SProcessMultiRefBusinessDataInstance sRefBusinessDataInstance = new SProcessMultiRefBusinessDataInstance();\n        sRefBusinessDataInstance.setDataIds(Arrays.asList(dataIds));\n        return sRefBusinessDataInstance;\n    }\n\n    @Test\n    public void execute_should_not_update_multiple_business_data_when_ref_dataIds_are_up_to_date() throws Exception {\n        //given\n        final String dataName = \"addresses\";\n        final BusinessDataContext businessDataContext = buildContext(dataName);\n\n        final long persistenceId1 = 45L;\n        final long persistenceId2 = 46L;\n        final SProcessMultiRefBusinessDataInstance refBusinessDataInstance = createMultiRefBusinessDataInstance(\n                persistenceId1,\n                persistenceId2);\n\n        given(refBusinessDataRetriever.getRefBusinessDataInstance(businessDataContext))\n                .willReturn(refBusinessDataInstance);\n\n        //when\n        updateDataRefAction.execute(Arrays.<Entity> asList(new Address(persistenceId1), new Address(persistenceId2)),\n                businessDataContext);\n\n        //then\n        verify(refBusinessDataService, never()).updateRefBusinessDataInstance(refBusinessDataInstance,\n                Arrays.asList(persistenceId1, persistenceId2));\n    }\n\n    @Test\n    public void execute_should_update_multiple_business_data_with_previous_refDataIds() throws Exception {\n        //given\n        final String dataName = \"addresses\";\n        final BusinessDataContext businessDataContext = buildContext(dataName);\n\n        final long persistenceId1 = 45L;\n        final long persistenceId2 = 46L;\n        final SProcessMultiRefBusinessDataInstance refBusinessDataInstance = createMultiRefBusinessDataInstance(\n                persistenceId1,\n                34L);\n        given(refBusinessDataRetriever.getRefBusinessDataInstance(businessDataContext))\n                .willReturn(refBusinessDataInstance);\n\n        //when\n        updateDataRefAction.execute(Arrays.<Entity> asList(new Address(persistenceId1), new Address(persistenceId2)),\n                businessDataContext);\n\n        //then\n        verify(refBusinessDataService).updateRefBusinessDataInstance(refBusinessDataInstance,\n                Arrays.asList(persistenceId1, persistenceId2));\n    }\n\n    @Test\n    public void execute_should_throws_exception_when_service_throws_exception() throws Exception {\n        //given\n        final String dataName = \"addresses\";\n        final SProcessMultiRefBusinessDataInstance refBusinessDataInstance = createMultiRefBusinessDataInstance(34L);\n\n        final BusinessDataContext businessDataContext = buildContext(dataName);\n        given(refBusinessDataRetriever.getRefBusinessDataInstance(businessDataContext))\n                .willReturn(refBusinessDataInstance);\n        doThrow(new SRefBusinessDataInstanceModificationException(new Exception())).when(refBusinessDataService)\n                .updateRefBusinessDataInstance(\n                        refBusinessDataInstance, Arrays.asList(45L, 46L));\n\n        //then\n        expectedException.expect(SEntityActionExecutionException.class);\n\n        //when\n        updateDataRefAction.execute(Arrays.<Entity> asList(new Address(45L), new Address(46L)), businessDataContext);\n\n    }\n\n    @Test\n    public void execute_should_throw_exception_when_is_a_list_of_entity_and_simple_data_ref() throws Exception {\n        //given\n        final String dataName = \"addresses\";\n        final BusinessDataContext businessDataContext = buildContext(dataName);\n        final SSimpleRefBusinessDataInstance refBusinessDataInstance = mock(SSimpleRefBusinessDataInstance.class);\n        given(refBusinessDataRetriever.getRefBusinessDataInstance(businessDataContext))\n                .willReturn(refBusinessDataInstance);\n\n        //then\n        expectedException.expect(SEntityActionExecutionException.class);\n\n        //when\n        updateDataRefAction.execute(Arrays.<Entity> asList(new Address(45L), new Address(46L)), businessDataContext);\n\n    }\n\n    @Test\n    public void execute_should_throws_exception_when_list_contains_null_entries() throws Exception {\n        //given\n        final String dataName = \"addresses\";\n        final BusinessDataContext businessDataContext = buildContext(dataName);\n        final SProcessMultiRefBusinessDataInstance refBusinessDataInstance = mock(\n                SProcessMultiRefBusinessDataInstance.class);\n        given(refBusinessDataRetriever.getRefBusinessDataInstance(businessDataContext))\n                .willReturn(refBusinessDataInstance);\n\n        //then\n        expectedException.expect(SEntityActionExecutionException.class);\n\n        //when\n        updateDataRefAction.execute(Arrays.<Entity> asList(new Address(45L), null), businessDataContext);\n    }\n\n    private BusinessDataContext buildContext(final String dataName) {\n        final BusinessDataContext businessDataContext = new BusinessDataContext(dataName,\n                new Container(PROCESS_INSTANCE_ID, PROCESS_INSTANCE));\n        return businessDataContext;\n    }\n\n    @Test\n    public void handleNull_delete_the_reference_to_the_business_data() throws Exception {\n        final SSimpleRefBusinessDataInstance refBusinessDataInstance = mock(SSimpleRefBusinessDataInstance.class);\n        final BusinessDataContext context = buildContext(\"employee\");\n        when(refBusinessDataRetriever.getRefBusinessDataInstance(context)).thenReturn(refBusinessDataInstance);\n\n        updateDataRefAction.handleNull(context);\n\n        verify(refBusinessDataService).updateRefBusinessDataInstance(refBusinessDataInstance, null);\n    }\n\n    @Test\n    public void handleNull_delete_references_to_the_business_data() throws Exception {\n        final SProcessMultiRefBusinessDataInstance refBusinessDataInstance = mock(\n                SProcessMultiRefBusinessDataInstance.class);\n        final BusinessDataContext context = buildContext(\"employee\");\n        when(refBusinessDataRetriever.getRefBusinessDataInstance(context)).thenReturn(refBusinessDataInstance);\n\n        updateDataRefAction.handleNull(context);\n\n        verify(refBusinessDataService).updateRefBusinessDataInstance(refBusinessDataInstance, new ArrayList<>());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/operation/pojo/Employee.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.operation.pojo;\n\nimport org.apache.commons.lang3.builder.EqualsBuilder;\nimport org.apache.commons.lang3.builder.HashCodeBuilder;\nimport org.bonitasoft.engine.bdm.Entity;\n\npublic class Employee implements Entity {\n\n    private static final long serialVersionUID = 4877386043381866907L;\n\n    private Long persistenceId;\n\n    private Long persistenceVersion;\n\n    private String firstName;\n\n    private String lastName;\n\n    public Employee() {\n        super();\n    }\n\n    public Employee(final Long id, final Long persistenceVersion, final String firstName, final String lastName) {\n        super();\n        persistenceId = id;\n        this.persistenceVersion = persistenceVersion;\n        this.firstName = firstName;\n        this.lastName = lastName;\n    }\n\n    @Override\n    public Long getPersistenceId() {\n        return persistenceId;\n    }\n\n    public void setPersistenceId(final Long id) {\n        persistenceId = id;\n    }\n\n    @Override\n    public Long getPersistenceVersion() {\n        return persistenceVersion;\n    }\n\n    public String getFirstName() {\n        return firstName;\n    }\n\n    public void setFirstName(final String firstName) {\n        this.firstName = firstName;\n    }\n\n    public String getLastName() {\n        return lastName;\n    }\n\n    public void setLastName(final String lastName) {\n        this.lastName = lastName;\n    }\n\n    @Override\n    public int hashCode() {\n        return new HashCodeBuilder().append(persistenceId).append(persistenceVersion).append(firstName).append(lastName)\n                .build();\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        final Employee other = (Employee) obj;\n        return new EqualsBuilder().append(persistenceId, other.persistenceId)\n                .append(persistenceVersion, other.persistenceVersion)\n                .append(firstName, other.firstName).append(lastName, other.lastName).build();\n    }\n\n    @Override\n    public String toString() {\n        return \"Employee [id=\" + persistenceId + \", version=\" + persistenceVersion + \", firstName=\" + firstName\n                + \", lastName=\" + lastName + \"]\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/operation/pojo/InvalidTravel.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.operation.pojo;\n\nimport java.io.Serializable;\n\npublic class InvalidTravel implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    private int nbDays;\n\n    public int getNbDays() {\n        return nbDays;\n    }\n\n    public void setNbDays(final int nbDays) {\n        this.nbDays = nbDays;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/operation/pojo/Travel.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.operation.pojo;\n\nimport org.bonitasoft.engine.bdm.Entity;\n\npublic class Travel implements Entity {\n\n    private static final long serialVersionUID = 1L;\n\n    private int nbDays;\n\n    private Long persistenceId;\n\n    private Long persistenceVersion;\n\n    public int getNbDays() {\n        return nbDays;\n    }\n\n    public void setNbDays(final int nbDays) {\n        this.nbDays = nbDays;\n    }\n\n    @Override\n    public Long getPersistenceVersion() {\n        return persistenceVersion;\n    }\n\n    @Override\n    public Long getPersistenceId() {\n        return persistenceId;\n    }\n\n    public void setPersistenceId(final Long id) {\n        persistenceId = id;\n    }\n\n    public void setPersistenceVersion(Long persistenceVersion) {\n        this.persistenceVersion = persistenceVersion;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/page/IsActorInitiatorRuleTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.actor.mapping.ActorMappingService;\nimport org.bonitasoft.engine.core.form.FormMappingService;\nimport org.bonitasoft.engine.core.form.SFormMapping;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.session.model.SSession;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class IsActorInitiatorRuleTest {\n\n    @Mock\n    ActorMappingService actorMappingService;\n\n    @Mock\n    SessionAccessor sessionAccessor;\n\n    @Mock\n    SessionService sessionService;\n\n    @Mock\n    FormMappingService formMappingService;\n\n    @Mock\n    SFormMapping formMapping;\n\n    String pageMappingKey = \"key\";\n\n    long processDefinitionId = 42L;\n\n    long userId = 2L;\n\n    @InjectMocks\n    IsActorInitiatorRule isActorInitiatorRule = new IsActorInitiatorRule(actorMappingService, sessionAccessor,\n            sessionService, formMappingService);\n\n    @Before\n    public void initMocks() throws Exception {\n        when(formMappingService.get(pageMappingKey)).thenReturn(formMapping);\n        when(formMapping.getProcessDefinitionId()).thenReturn(processDefinitionId);\n        when(sessionAccessor.getSessionId()).thenReturn(1L);\n        SSession session = mock(SSession.class);\n        when(session.getUserId()).thenReturn(userId);\n        when(sessionService.getSession(1L)).thenReturn(session);\n    }\n\n    @Test\n    public void isAllowed_should_return_true_if_actor_initiator() throws Exception {\n        Map<String, Serializable> context = new HashMap<String, Serializable>();\n        when(actorMappingService.canUserStartProcessDefinition(userId, processDefinitionId)).thenReturn(true);\n\n        assertThat(isActorInitiatorRule.isAllowed(pageMappingKey, context)).isTrue();\n    }\n\n    @Test\n    public void isAllowed_should_return_false_if_not_actor_initiator() throws Exception {\n        Map<String, Serializable> context = new HashMap<String, Serializable>();\n        when(actorMappingService.canUserStartProcessDefinition(userId, processDefinitionId)).thenReturn(false);\n\n        assertThat(isActorInitiatorRule.isAllowed(pageMappingKey, context)).isFalse();\n    }\n\n    @Test\n    public void getIdShouldReturnIsActorInitiator() throws Exception {\n        assertThat(isActorInitiatorRule.getId()).isEqualTo(\"IS_ACTOR_INITIATOR\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/page/IsAdminRuleTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport static java.util.Collections.emptySet;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.mock;\n\nimport java.util.HashMap;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.session.model.SSession;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class IsAdminRuleTest {\n\n    @Mock\n    private SessionAccessor sessionAccessor;\n\n    @Mock\n    private SessionService sessionService;\n\n    @Spy\n    @InjectMocks\n    IsAdminRule isAdminRule;\n\n    @Test\n    public void isAllowed_should_return_true_if_user_has_process_deploy_permission() throws Exception {\n        // given\n        final SSession session = mock(SSession.class);\n        doReturn(session).when(isAdminRule).getSession();\n        doReturn(Set.of(IsAdminRule.PROCESS_DEPLOY_PERMISSION)).when(session).getUserPermissions();\n\n        // when\n        final boolean allowed = isAdminRule.isAllowed(\"key\", new HashMap<>());\n\n        // then\n        assertThat(allowed).isTrue();\n    }\n\n    @Test\n    public void isAllowed_should_return_false_if_no_permissions_in_session() throws Exception {\n        // given\n        final SSession session = mock(SSession.class);\n        doReturn(session).when(isAdminRule).getSession();\n        doReturn(emptySet()).when(session).getUserPermissions();\n\n        // when\n        final boolean allowed = isAdminRule.isAllowed(\"key\", new HashMap<>());\n\n        // then\n        assertThat(allowed).isFalse();\n    }\n\n    @Test\n    public void getIdShouldReturnIsAdmin() {\n        assertThat(isAdminRule.getId()).isEqualTo(\"IS_ADMIN\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/page/IsInvolvedInProcessInstanceRuleTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.*;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.impl.TaskInvolvementDelegate;\nimport org.bonitasoft.engine.commons.exceptions.SExecutionException;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.session.model.SSession;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class IsInvolvedInProcessInstanceRuleTest extends RuleTest {\n\n    public static final long PROCESS_INSTANCE_ID = 189L;\n\n    public static final long LOGGED_USER_ID = 7L;\n\n    @Mock\n    TaskInvolvementDelegate taskInvolvementDelegate;\n\n    @Mock\n    SessionService sessionService;\n\n    @Mock\n    SessionAccessor sessionAccessor;\n\n    @InjectMocks\n    IsInvolvedInProcessInstanceRule rule;\n\n    @Before\n    public void initMocks() throws Exception {\n        when(sessionAccessor.getSessionId()).thenReturn(1L);\n        SSession session = mock(SSession.class);\n        when(session.getUserId()).thenReturn(LOGGED_USER_ID);\n        when(sessionService.getSession(1L)).thenReturn(session);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void shouldThrowIllegallArgumentIfProcessInstanceIdParamNotPresent() throws Exception {\n        Map<String, Serializable> context = buildContext(null, null);\n\n        rule.isAllowed(\"someKey\", context);\n    }\n\n    @Test\n    public void shouldNotAllowIfNotPendingOrAssignedTasks() throws Exception {\n        Map<String, Serializable> context = buildContext(189L, null);\n\n        final boolean allowed = rule.isAllowed(\"someKey\", context);\n\n        assertThat(allowed).isFalse();\n    }\n\n    @Test\n    public void shouldAllowIfAtLeastOnePendingOrAssignedTask() throws Exception {\n        Map<String, Serializable> context = buildContext(PROCESS_INSTANCE_ID, LOGGED_USER_ID);\n        doReturn(true).when(taskInvolvementDelegate).hasUserPendingOrAssignedTasks(LOGGED_USER_ID, PROCESS_INSTANCE_ID);\n\n        final boolean allowed = rule.isAllowed(\"someKey\", context);\n\n        assertThat(allowed).isTrue();\n    }\n\n    @Test(expected = SExecutionException.class)\n    public void shouldThrowExecutionExceptionIfReadException() throws Exception {\n        Map<String, Serializable> context = buildContext(PROCESS_INSTANCE_ID, LOGGED_USER_ID);\n        doThrow(SExecutionException.class).when(taskInvolvementDelegate).hasUserPendingOrAssignedTasks(LOGGED_USER_ID,\n                PROCESS_INSTANCE_ID);\n\n        rule.isAllowed(\"someKey\", context);\n    }\n\n    @Test\n    public void getIdShouldReturnIsInvolvedInProcessInstance() throws Exception {\n        assertThat(rule.getId()).isEqualTo(\"IS_INVOLVED_IN_PROCESS_INSTANCE\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/page/IsManagerOfUserInvolvedInProcessInstanceRuleTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.*;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.impl.ProcessInvolvementDelegate;\nimport org.bonitasoft.engine.commons.exceptions.SExecutionException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.session.model.SSession;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Emmanuel Duchastenier\n * @author Danila Mazour\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class IsManagerOfUserInvolvedInProcessInstanceRuleTest extends RuleTest {\n\n    private static final Long PROCESS_INSTANCE_ID = 1111L;\n    private static final Long LOGGED_USER_ID = 2222L;\n    private Map<String, Serializable> context;\n\n    @Mock\n    SessionService sessionService;\n\n    @Mock\n    SessionAccessor sessionAccessor;\n\n    @Mock\n    IdentityService identityService;\n\n    @Mock\n    ProcessInvolvementDelegate processInvolvementDelegate;\n    @InjectMocks\n    IsManagerOfUserInvolvedInProcessInstanceRule rule;\n\n    @Before\n    public void initMocks() throws Exception {\n        when(sessionAccessor.getSessionId()).thenReturn(1L);\n        SSession session = mock(SSession.class);\n        when(session.getUserId()).thenReturn(LOGGED_USER_ID);\n        when(sessionService.getSession(1L)).thenReturn(session);\n\n        context = buildContext(PROCESS_INSTANCE_ID, LOGGED_USER_ID);\n    }\n\n    @Test\n    public void should_return_the_right_rule_id() throws Exception {\n        // when:\n        final String ruleId = rule.getId();\n\n        // then:\n        assertThat(ruleId).isEqualTo(AuthorizationRuleConstants.IS_MANAGER_OF_USER_INVOLVED_IN_PROCESS_INSTANCE);\n    }\n\n    @Test\n    public void should_allow_manager_if_processInvolvementDelegate_returns_true() throws Exception {\n        // given:\n        doReturn(true).when(processInvolvementDelegate).isManagerOfUserInvolvedInProcessInstance(LOGGED_USER_ID,\n                PROCESS_INSTANCE_ID);\n\n        // when:\n        final boolean allowed = rule.isAllowed(null, context);\n\n        // then:\n        assertThat(allowed).isTrue();\n        verify(processInvolvementDelegate).isManagerOfUserInvolvedInProcessInstance(LOGGED_USER_ID,\n                PROCESS_INSTANCE_ID);\n    }\n\n    @Test\n    public void should_not_allow_manager_if_processInvolvementDelegate_returns_false() throws Exception {\n        // given:\n        doReturn(false).when(processInvolvementDelegate).isManagerOfUserInvolvedInProcessInstance(LOGGED_USER_ID,\n                PROCESS_INSTANCE_ID);\n\n        // when:\n        final boolean allowed = rule.isAllowed(null, context);\n\n        // then:\n        assertThat(allowed).isFalse();\n        verify(processInvolvementDelegate).isManagerOfUserInvolvedInProcessInstance(LOGGED_USER_ID,\n                PROCESS_INSTANCE_ID);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void should_throw_illegal_argument_if_processInstanceId_param_not_present() throws Exception {\n\n        rule.isAllowed(null, buildContext(null, null));\n    }\n\n    @Test(expected = SExecutionException.class)\n    public void should_throw_ExecutionException_if_processInvolvementDelegate_fails() throws Exception {\n        doThrow(new BonitaException(\"test\")).when(processInvolvementDelegate)\n                .isManagerOfUserInvolvedInProcessInstance(LOGGED_USER_ID, PROCESS_INSTANCE_ID);\n\n        rule.isAllowed(null, context);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/page/IsProcessInitiatorRuleTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.*;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.impl.ProcessInvolvementDelegate;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.commons.exceptions.SExecutionException;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.session.model.SSession;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class IsProcessInitiatorRuleTest extends RuleTest {\n\n    public static final long PROCESS_INSTANCE_ID = 541L;\n    public static final long LOGGED_USER_ID = 7L;\n\n    @Mock\n    ProcessInvolvementDelegate processInvolvementDelegate;\n\n    @Mock\n    SessionService sessionService;\n\n    @Mock\n    SessionAccessor sessionAccessor;\n\n    @InjectMocks\n    @Spy\n    IsProcessInitiatorRule rule;\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Before\n    public void initMocks() throws Exception {\n        when(sessionAccessor.getSessionId()).thenReturn(1L);\n        SSession session = mock(SSession.class);\n        when(session.getUserId()).thenReturn(LOGGED_USER_ID);\n        when(sessionService.getSession(1L)).thenReturn(session);\n    }\n\n    @Test\n    public void should_not_be_allowed_if_process_instance_initiator_is_not_given_user() throws Exception {\n\n        Map<String, Serializable> context = buildContext(PROCESS_INSTANCE_ID, null);\n        final SProcessInstance processInstance = mock(SProcessInstance.class);\n        doReturn(false).when(processInvolvementDelegate).isProcessOrArchivedProcessInitiator(LOGGED_USER_ID,\n                PROCESS_INSTANCE_ID);\n\n        final boolean allowed = rule.isAllowed(\"someKey\", context);\n\n        assertThat(allowed).isFalse();\n    }\n\n    @Test\n    public void should_allow_if_process_instance_initiator_is_precisely_the_given_user() throws Exception {\n\n        Map<String, Serializable> context = buildContext(PROCESS_INSTANCE_ID, null);\n        final SProcessInstance processInstance = mock(SProcessInstance.class);\n        doReturn(true).when(processInvolvementDelegate).isProcessOrArchivedProcessInitiator(LOGGED_USER_ID,\n                PROCESS_INSTANCE_ID);\n\n        final boolean allowed = rule.isAllowed(\"someKey\", context);\n\n        assertThat(allowed).isTrue();\n    }\n\n    @Test\n    public void should_throw_exception_if_service_problem_occurs() throws Exception {\n        //given\n        Map<String, Serializable> context = buildContext(PROCESS_INSTANCE_ID, LOGGED_USER_ID);\n\n        ProcessInstanceNotFoundException processInstanceNotFoundException = new ProcessInstanceNotFoundException(\n                \"message\");\n        doThrow(processInstanceNotFoundException).when(processInvolvementDelegate).isProcessOrArchivedProcessInitiator(\n                LOGGED_USER_ID,\n                PROCESS_INSTANCE_ID);\n\n        //expect\n        expectedException.expect(SExecutionException.class);\n\n        //when\n        rule.isAllowed(\"exception raised\", context);\n    }\n\n    @Test\n    public void should_throw_exception_if_process_instance_id_is_missing() throws Exception {\n        //given\n        Map<String, Serializable> context = new HashMap<>();\n\n        //expect\n        expectedException.expect(IllegalArgumentException.class);\n\n        //when\n        rule.isAllowed(\"exception raised\", context);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/page/IsProcessOwnerRuleTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.core.form.FormMappingService;\nimport org.bonitasoft.engine.core.form.SFormMapping;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.session.model.SSession;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.bonitasoft.engine.supervisor.mapping.SupervisorMappingService;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class IsProcessOwnerRuleTest {\n\n    @Mock\n    SupervisorMappingService supervisorMappingService;\n\n    @Mock\n    SessionAccessor sessionAccessor;\n\n    @Mock\n    SessionService sessionService;\n\n    @Mock\n    FormMappingService formMappingService;\n\n    @Mock\n    SFormMapping formMapping;\n\n    String pageMappingKey = \"key\";\n\n    long processDefinitionId = 42L;\n\n    long userId = 2L;\n\n    @InjectMocks\n    IsProcessOwnerRule isProcessOwnerRule = new IsProcessOwnerRule(supervisorMappingService, sessionAccessor,\n            sessionService, formMappingService);\n\n    @Before\n    public void initMocks() throws Exception {\n        when(formMappingService.get(pageMappingKey)).thenReturn(formMapping);\n        when(formMapping.getProcessDefinitionId()).thenReturn(processDefinitionId);\n        when(sessionAccessor.getSessionId()).thenReturn(1L);\n        SSession session = mock(SSession.class);\n        when(session.getUserId()).thenReturn(userId);\n        when(sessionService.getSession(1L)).thenReturn(session);\n    }\n\n    @Test\n    public void isAllowed_should_return_true_if_process_supervisor() throws Exception {\n        Map<String, Serializable> context = new HashMap<String, Serializable>();\n        when(supervisorMappingService.isProcessSupervisor(processDefinitionId, userId)).thenReturn(true);\n\n        assertThat(isProcessOwnerRule.isAllowed(pageMappingKey, context)).isTrue();\n    }\n\n    @Test\n    public void isAllowed_should_return_false_if_not_process_supervisor() throws Exception {\n        Map<String, Serializable> context = new HashMap<String, Serializable>();\n        when(supervisorMappingService.isProcessSupervisor(processDefinitionId, userId)).thenReturn(false);\n\n        assertThat(isProcessOwnerRule.isAllowed(pageMappingKey, context)).isFalse();\n    }\n\n    @Test\n    public void getIdShouldReturnIsProcessOwner() throws Exception {\n        assertThat(isProcessOwnerRule.getId()).isEqualTo(\"IS_PROCESS_OWNER\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/page/IsTaskAvailableForUserRuleTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.*;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.commons.exceptions.SExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.session.model.SSession;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class IsTaskAvailableForUserRuleTest {\n\n    @Mock\n    SessionAccessor sessionAccessor;\n\n    @Mock\n    SessionService sessionService;\n\n    @Mock\n    ActivityInstanceService activityInstanceService;\n\n    @Mock\n    SHumanTaskInstance humanTaskInstance;\n\n    @Mock\n    SAHumanTaskInstance archivedHumanTaskInstance;\n\n    long userId = 2L;\n\n    long taskInstanceId = 42L;\n\n    @InjectMocks\n    IsTaskAvailableForUserRule isTaskAvailableForUserRule = new IsTaskAvailableForUserRule(activityInstanceService,\n            sessionService, sessionAccessor);\n\n    @Before\n    public void initMocks() throws Exception {\n        when(sessionAccessor.getSessionId()).thenReturn(1L);\n        SSession session = mock(SSession.class);\n        when(session.getUserId()).thenReturn(userId);\n        when(sessionService.getSession(1L)).thenReturn(session);\n    }\n\n    @Test\n    public void isAllowed_should_return_true_if_is_assignee() throws Exception {\n        Map<String, Serializable> context = new HashMap<String, Serializable>();\n        final Map<String, String[]> queryParameters = new HashMap<String, String[]>();\n        queryParameters.put(URLAdapterConstants.ID_QUERY_PARAM, new String[] { Long.toString(taskInstanceId) });\n        context.put(URLAdapterConstants.QUERY_PARAMETERS, (Serializable) queryParameters);\n        when(humanTaskInstance.getAssigneeId()).thenReturn(userId);\n        when(activityInstanceService.getHumanTaskInstance(taskInstanceId)).thenReturn(humanTaskInstance);\n\n        assertThat(isTaskAvailableForUserRule.isAllowed(\"key\", context)).isTrue();\n    }\n\n    @Test\n    public void isAllowed_should_return_true_if_is_actor() throws Exception {\n        Map<String, Serializable> context = new HashMap<String, Serializable>();\n        final Map<String, String[]> queryParameters = new HashMap<String, String[]>();\n        queryParameters.put(URLAdapterConstants.ID_QUERY_PARAM, new String[] { Long.toString(taskInstanceId) });\n        context.put(URLAdapterConstants.QUERY_PARAMETERS, (Serializable) queryParameters);\n        when(humanTaskInstance.getAssigneeId()).thenReturn(0L);\n        when(activityInstanceService.getHumanTaskInstance(taskInstanceId)).thenReturn(humanTaskInstance);\n        when(activityInstanceService.isTaskPendingForUser(taskInstanceId, userId)).thenReturn(true);\n\n        assertThat(isTaskAvailableForUserRule.isAllowed(\"key\", context)).isTrue();\n    }\n\n    @Test\n    public void isAllowed_should_return_false_if_is_assigneed_to_someone_else() throws Exception {\n        Map<String, Serializable> context = new HashMap<String, Serializable>();\n        final Map<String, String[]> queryParameters = new HashMap<String, String[]>();\n        queryParameters.put(URLAdapterConstants.ID_QUERY_PARAM, new String[] { Long.toString(taskInstanceId) });\n        context.put(URLAdapterConstants.QUERY_PARAMETERS, (Serializable) queryParameters);\n        when(humanTaskInstance.getAssigneeId()).thenReturn(5L);\n        when(activityInstanceService.getHumanTaskInstance(taskInstanceId)).thenReturn(humanTaskInstance);\n\n        assertThat(isTaskAvailableForUserRule.isAllowed(\"key\", context)).isFalse();\n    }\n\n    @Test\n    public void isAllowed_should_return_false_if_is_not_actor() throws Exception {\n        Map<String, Serializable> context = new HashMap<String, Serializable>();\n        final Map<String, String[]> queryParameters = new HashMap<String, String[]>();\n        queryParameters.put(URLAdapterConstants.ID_QUERY_PARAM, new String[] { Long.toString(taskInstanceId) });\n        context.put(URLAdapterConstants.QUERY_PARAMETERS, (Serializable) queryParameters);\n        when(humanTaskInstance.getAssigneeId()).thenReturn(0L);\n        when(activityInstanceService.getHumanTaskInstance(taskInstanceId)).thenReturn(humanTaskInstance);\n        when(activityInstanceService.isTaskPendingForUser(taskInstanceId, userId)).thenReturn(false);\n\n        assertThat(isTaskAvailableForUserRule.isAllowed(\"key\", context)).isFalse();\n    }\n\n    @Test\n    public void isAllowed_should_return_false_if_archived_task_performed_by_someone_else() throws Exception {\n        Map<String, Serializable> context = new HashMap<String, Serializable>();\n        final Map<String, String[]> queryParameters = new HashMap<String, String[]>();\n        queryParameters.put(URLAdapterConstants.ID_QUERY_PARAM, new String[] { Long.toString(taskInstanceId) });\n        context.put(URLAdapterConstants.QUERY_PARAMETERS, (Serializable) queryParameters);\n        doThrow(SActivityInstanceNotFoundException.class).when(activityInstanceService)\n                .getHumanTaskInstance(taskInstanceId);\n        when(archivedHumanTaskInstance.getExecutedBy()).thenReturn(5L);\n        when(activityInstanceService.getLastArchivedFlowNodeInstance(SAHumanTaskInstance.class, taskInstanceId))\n                .thenReturn(archivedHumanTaskInstance);\n\n        assertThat(isTaskAvailableForUserRule.isAllowed(\"key\", context)).isFalse();\n    }\n\n    @Test\n    public void isAllowed_should_return_true_if_archived_task_performed_by_user() throws Exception {\n        Map<String, Serializable> context = new HashMap<String, Serializable>();\n        final Map<String, String[]> queryParameters = new HashMap<String, String[]>();\n        queryParameters.put(URLAdapterConstants.ID_QUERY_PARAM, new String[] { Long.toString(taskInstanceId) });\n        context.put(URLAdapterConstants.QUERY_PARAMETERS, (Serializable) queryParameters);\n        doThrow(SActivityInstanceNotFoundException.class).when(activityInstanceService)\n                .getHumanTaskInstance(taskInstanceId);\n        when(archivedHumanTaskInstance.getExecutedBy()).thenReturn(userId);\n        when(activityInstanceService.getLastArchivedFlowNodeInstance(SAHumanTaskInstance.class, taskInstanceId))\n                .thenReturn(archivedHumanTaskInstance);\n\n        assertThat(isTaskAvailableForUserRule.isAllowed(\"key\", context)).isTrue();\n    }\n\n    @Test(expected = SExecutionException.class)\n    public void isAllowed_should_throw_exception_when_task_not_found() throws Exception {\n        Map<String, Serializable> context = new HashMap<String, Serializable>();\n        final Map<String, String[]> queryParameters = new HashMap<String, String[]>();\n        queryParameters.put(URLAdapterConstants.ID_QUERY_PARAM, new String[] { Long.toString(taskInstanceId) });\n        context.put(URLAdapterConstants.QUERY_PARAMETERS, (Serializable) queryParameters);\n        doThrow(SActivityInstanceNotFoundException.class).when(activityInstanceService)\n                .getHumanTaskInstance(taskInstanceId);\n        when(activityInstanceService.getLastArchivedFlowNodeInstance(SAHumanTaskInstance.class, taskInstanceId))\n                .thenReturn(null);\n\n        isTaskAvailableForUserRule.isAllowed(\"key\", context);\n    }\n\n    @Test\n    public void getIdShouldReturnIsTaskAvailableForUser() throws Exception {\n        assertThat(isTaskAvailableForUserRule.getId()).isEqualTo(\"IS_TASK_AVAILABLE_FOR_USER\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/page/IsTaskPerformerRuleTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.*;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.impl.TaskInvolvementDelegate;\nimport org.bonitasoft.engine.commons.exceptions.SExecutionException;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.session.model.SSession;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class IsTaskPerformerRuleTest extends RuleTest {\n\n    public static final long USER_ID = 7L;\n    public static final long PROCESS_INSTANCE_ID = 541L;\n\n    @Mock\n    TaskInvolvementDelegate taskInvolvementDelegate;\n\n    @Mock\n    SessionService sessionService;\n\n    @Mock\n    SessionAccessor sessionAccessor;\n\n    @InjectMocks\n    IsTaskPerformerRule rule;\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Before\n    public void initMocks() throws Exception {\n        when(sessionAccessor.getSessionId()).thenReturn(1L);\n        SSession session = mock(SSession.class);\n        when(session.getUserId()).thenReturn(USER_ID);\n        when(sessionService.getSession(1L)).thenReturn(session);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void shouldThrowIllegalArgumentIfProcessInstanceIdParamNotPresent() throws Exception {\n        Map<String, Serializable> context = buildContext(null, 7L);\n\n        rule.isAllowed(\"betje\", context);\n    }\n\n    @Test\n    public void shouldNotBeAllowedIfNoArchivedTasks() throws Exception {\n        Map<String, Serializable> context = buildContext(PROCESS_INSTANCE_ID, USER_ID);\n        doReturn(false).when(taskInvolvementDelegate).isExecutorOfArchivedTaskOfProcess(USER_ID, PROCESS_INSTANCE_ID);\n\n        final boolean allowed = rule.isAllowed(\"someKey\", context);\n\n        assertThat(allowed).isFalse();\n    }\n\n    @Test\n    public void shouldBeAllowedIfAnArchivedTaskIsExecutedByGivenUser() throws Exception {\n        Map<String, Serializable> context = buildContext(PROCESS_INSTANCE_ID, USER_ID);\n        doReturn(true).when(taskInvolvementDelegate).isExecutorOfArchivedTaskOfProcess(USER_ID, PROCESS_INSTANCE_ID);\n\n        final boolean allowed = rule.isAllowed(\"someKey\", context);\n\n        assertThat(allowed).isTrue();\n    }\n\n    @Test\n    public void shouldThrowSExecutionExceptionIfExceptionOccurs() throws Exception {\n        Map<String, Serializable> context = buildContext(PROCESS_INSTANCE_ID, USER_ID);\n        final SBonitaReadException exception = new SBonitaReadException(\"message\");\n        doThrow(exception).when(taskInvolvementDelegate).isExecutorOfArchivedTaskOfProcess(USER_ID,\n                PROCESS_INSTANCE_ID);\n\n        //expect\n        expectedException.expect(SExecutionException.class);\n\n        //when\n        rule.isAllowed(\"exception raised\", context);\n    }\n\n    @Test\n    public void getIdShouldReturnIsTaskPerformer() throws Exception {\n        assertThat(rule.getId()).isEqualTo(\"IS_TASK_PERFORMER\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/page/RuleTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic abstract class RuleTest {\n\n    protected Map<String, Serializable> buildContext(Long processInstanceId, Long userId) {\n        Map<String, Serializable> context = new HashMap<>(1);\n        final Map<String, String[]> queryParameters = new HashMap<>(2);\n        if (processInstanceId != null) {\n            queryParameters.put(URLAdapterConstants.ID_QUERY_PARAM, new String[] { Long.toString(processInstanceId) });\n        }\n        if (userId != null) {\n            queryParameters.put(URLAdapterConstants.USER_QUERY_PARAM, new String[] { Long.toString(userId) });\n        }\n        context.put(URLAdapterConstants.QUERY_PARAMETERS, (Serializable) queryParameters);\n        return context;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/platform/PlatformManagerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform;\n\nimport static java.util.Arrays.asList;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport java.util.concurrent.Callable;\n\nimport org.bonitasoft.engine.commons.PlatformLifecycleService;\nimport org.bonitasoft.engine.commons.PlatformRestartHandler;\nimport org.bonitasoft.engine.platform.configuration.NodeConfiguration;\nimport org.bonitasoft.engine.platform.model.SPlatform;\nimport org.bonitasoft.engine.platform.model.impl.SPlatformPropertiesImpl;\nimport org.bonitasoft.engine.service.BonitaTaskExecutor;\nimport org.bonitasoft.engine.service.RunnableWithException;\nimport org.bonitasoft.engine.tenant.TenantStateManager;\nimport org.bonitasoft.engine.transaction.TransactionService;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.RestoreSystemProperties;\nimport org.junit.contrib.java.lang.system.SystemOutRule;\nimport org.mockito.InOrder;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnit;\nimport org.mockito.junit.MockitoRule;\n\npublic class PlatformManagerTest {\n\n    @Rule\n    public MockitoRule mockitoRule = MockitoJUnit.rule();\n    @Rule\n    public SystemOutRule systemOutRule = new SystemOutRule().enableLog();\n    @Rule\n    public RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties();\n\n    @Mock\n    private NodeConfiguration nodeConfiguration;\n    @Mock\n    private TransactionService transactionService;\n    @Mock\n    private PlatformService platformService;\n    @Mock\n    private TenantStateManager tenantManager;\n    @Mock\n    private PlatformLifecycleService platformLifecycleService1;\n    @Mock\n    private PlatformLifecycleService platformLifecycleService2;\n    @Mock\n    private PlatformStateProvider platformStateProvider;\n    @Mock\n    private BonitaTaskExecutor bonitaTaskExecutor;\n    @Mock\n    private PlatformVersionChecker platformVersionChecker;\n\n    private PlatformManager platformManager;\n    @Mock\n    private PlatformRestartHandler platformRestartHandler1;\n    @Mock\n    private PlatformRestartHandler platformRestartHandler2;\n\n    @Before\n    public void before() throws Exception {\n        doReturn(asList(platformRestartHandler1, platformRestartHandler2)).when(nodeConfiguration)\n                .getPlatformRestartHandlers();\n        platformManager = spy(new PlatformManager(nodeConfiguration,\n                asList(platformLifecycleService1, platformLifecycleService2), platformStateProvider,\n                bonitaTaskExecutor, platformVersionChecker));\n        when(transactionService.executeInTransaction(any()))\n                .thenAnswer(invocationOnMock -> ((Callable) invocationOnMock.getArgument(0)).call());\n        when(bonitaTaskExecutor.execute(any(RunnableWithException.class)))\n                .thenAnswer(invocationOnMock -> {\n                    ((RunnableWithException) invocationOnMock.getArgument(0)).run();\n                    return null;\n                });\n        doReturn(tenantManager).when(platformManager).getTenantStateManager();\n        doReturn(new SPlatform(\"1.3\", \"1.1.0\", \"0.0.0\", null, false, \"someUser\", 123455, false))\n                .when(platformService)\n                .getPlatform();\n        doReturn(new SPlatformPropertiesImpl(\"1.3.0\")).when(platformService).getSPlatformProperties();\n        doReturn(true).when(platformVersionChecker).verifyPlatformVersion();\n    }\n\n    @Test\n    public void should_start_scheduler_when_starting_node() throws Exception {\n        doReturn(true).when(platformStateProvider).initializeStart();\n\n        boolean started = platformManager.start();\n\n        verify(platformLifecycleService1).start();\n        verify(platformLifecycleService2).start();\n        verify(platformStateProvider).setStarted();\n        assertThat(started).isTrue();\n    }\n\n    @Test\n    public void should_start_platform_only_once() throws Exception {\n\n        boolean started = platformManager.start();\n\n        verifyNoInteractions(platformLifecycleService1);\n        verifyNoInteractions(platformLifecycleService2);\n        verify(platformStateProvider, never()).setStarted();\n        assertThat(started).isFalse();\n    }\n\n    @Test\n    public void should_stop_services_when_stopping_platform() throws Exception {\n        doReturn(true).when(platformStateProvider).initializeStop();\n\n        boolean stopped = platformManager.stop();\n\n        verify(platformLifecycleService1).stop();\n        verify(platformLifecycleService2).stop();\n        verify(platformStateProvider).setStopped();\n        assertThat(stopped).isTrue();\n    }\n\n    @Test\n    public void should_stop_platform_only_once() throws Exception {\n        //doReturn(false).when(platformStateProvider).initializeStop();\n\n        boolean stopped = platformManager.stop();\n\n        verifyNoInteractions(platformLifecycleService1);\n        verifyNoInteractions(platformLifecycleService2);\n        verify(platformStateProvider, never()).setStopped();\n        assertThat(stopped).isFalse();\n    }\n\n    @Test\n    public void start_should_execute_platform_restart_handlers_in_an_other_thread() throws Exception {\n        doReturn(true).when(platformStateProvider).initializeStart();\n\n        platformManager.start();\n\n        verify(platformRestartHandler1).execute();\n        verify(platformRestartHandler2).execute();\n        verify(bonitaTaskExecutor, times(2)).execute(any(RunnableWithException.class));\n    }\n\n    @Test\n    public void start_should_start_platform_and_tenant_services_in_the_right_order() throws Exception {\n        // given:\n        doReturn(true).when(platformStateProvider).initializeStart();\n\n        // when:\n        platformManager.start();\n\n        // then:\n        InOrder inOrder = inOrder(platformStateProvider, tenantManager, platformLifecycleService1,\n                platformLifecycleService2, platformRestartHandler1, platformRestartHandler2);\n        inOrder.verify(platformStateProvider).initializeStart();\n        inOrder.verify(platformLifecycleService1).start();\n        inOrder.verify(platformLifecycleService2).start();\n        inOrder.verify(platformStateProvider).setStarted();\n        inOrder.verify(tenantManager).start();\n        inOrder.verify(platformRestartHandler1).execute();\n        inOrder.verify(platformRestartHandler2).execute();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/platform/PlatformStateProviderTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.platform.PlatformState.STARTED;\nimport static org.bonitasoft.engine.platform.PlatformState.STARTING;\nimport static org.bonitasoft.engine.platform.PlatformState.STOPPED;\nimport static org.bonitasoft.engine.platform.PlatformState.STOPPING;\n\nimport org.junit.Test;\n\npublic class PlatformStateProviderTest {\n\n    @Test\n    public void should_return_false_when_initializingStart_on_STARTING_platform() {\n        PlatformStateProvider platformStateProvider = new PlatformStateProvider(STARTING);\n\n        boolean initializeStart = platformStateProvider.initializeStart();\n\n        assertThat(initializeStart).isFalse();\n        assertThat(platformStateProvider.getState()).isEqualTo(STARTING);\n    }\n\n    @Test\n    public void should_return_false_when_initializingStart_on_STOPPING_platform() {\n        PlatformStateProvider platformStateProvider = new PlatformStateProvider(STOPPING);\n\n        boolean initializeStart = platformStateProvider.initializeStart();\n\n        assertThat(initializeStart).isFalse();\n        assertThat(platformStateProvider.getState()).isEqualTo(STOPPING);\n    }\n\n    @Test\n    public void should_return_false_when_initializingStart_on_STARTED_platform() {\n        PlatformStateProvider platformStateProvider = new PlatformStateProvider(STARTED);\n\n        boolean initializeStart = platformStateProvider.initializeStart();\n\n        assertThat(initializeStart).isFalse();\n        assertThat(platformStateProvider.getState()).isEqualTo(STARTED);\n    }\n\n    @Test\n    public void should_return_true_when_initializingStart_on_STOPPED_platform() {\n        PlatformStateProvider platformStateProvider = new PlatformStateProvider(STOPPED);\n\n        boolean initializeStart = platformStateProvider.initializeStart();\n\n        assertThat(initializeStart).isTrue();\n        assertThat(platformStateProvider.getState()).isEqualTo(STARTING);\n    }\n\n    @Test\n    public void should_return_false_when_initializingStop_on_STARTING_platform() {\n        PlatformStateProvider platformStateProvider = new PlatformStateProvider(STARTING);\n\n        boolean initializeStop = platformStateProvider.initializeStop();\n\n        assertThat(initializeStop).isFalse();\n        assertThat(platformStateProvider.getState()).isEqualTo(STARTING);\n    }\n\n    @Test\n    public void should_return_false_when_initializingStop_on_STOPPING_platform() {\n        PlatformStateProvider platformStateProvider = new PlatformStateProvider(STOPPING);\n\n        boolean initializeStop = platformStateProvider.initializeStop();\n\n        assertThat(initializeStop).isFalse();\n        assertThat(platformStateProvider.getState()).isEqualTo(STOPPING);\n    }\n\n    @Test\n    public void should_return_true_when_initializingStop_on_STARTED_platform() {\n        PlatformStateProvider platformStateProvider = new PlatformStateProvider(STARTED);\n\n        boolean initializeStop = platformStateProvider.initializeStop();\n\n        assertThat(initializeStop).isTrue();\n        assertThat(platformStateProvider.getState()).isEqualTo(STOPPING);\n    }\n\n    @Test\n    public void should_return_false_when_initializingStop_on_STOPPED_platform() {\n        PlatformStateProvider platformStateProvider = new PlatformStateProvider(STOPPED);\n\n        boolean initializeStop = platformStateProvider.initializeStop();\n\n        assertThat(initializeStop).isFalse();\n        assertThat(platformStateProvider.getState()).isEqualTo(STOPPED);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/platform/PlatformVersionCheckerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.Future;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.platform.model.SPlatform;\nimport org.bonitasoft.engine.platform.model.SPlatformProperties;\nimport org.bonitasoft.engine.service.BroadcastService;\nimport org.bonitasoft.engine.service.TaskResult;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.SystemOutRule;\nimport org.junit.rules.ExpectedException;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\nimport org.mockito.Spy;\n\npublic class PlatformVersionCheckerTest {\n\n    @Rule\n    public final SystemOutRule systemOutRule = new SystemOutRule().enableLog().muteForSuccessfulTests();\n\n    @Mock\n    private PlatformService platformService;\n\n    @Mock\n    private SPlatform platform;\n\n    @Mock\n    private SPlatformProperties platformProperties;\n\n    @Mock\n    private BroadcastService broadcastService;\n\n    @Spy\n    @InjectMocks\n    private PlatformVersionChecker platformVersionChecker;\n\n    @Before\n    public void initialize() throws Exception {\n        MockitoAnnotations.initMocks(this);\n        when(platformService.getPlatform()).thenReturn(platform);\n        when(platformService.getSPlatformProperties()).thenReturn(platformProperties);\n        doReturn(CompletableFuture.completedFuture(Collections.emptyMap())).when(broadcastService)\n                .executeOnOthers(any());\n    }\n\n    @Test\n    public void check_db_and_jars_with_same_minor_version_is_accepted() throws Exception {\n        givenDatabaseSchemaVersion(\"7.11\");\n        givenBinaryVersion(\"7.11.0\");\n\n        boolean sameVersion = platformVersionChecker.execute();\n\n        assertThat(sameVersion).isTrue();\n    }\n\n    @Test\n    public void should_allow_to_run_snapshot_on_same_schema_version() throws Exception {\n        givenDatabaseSchemaVersion(\"6.1\");\n        givenBinaryVersion(\"6.1.1-SNAPSHOT\");\n\n        boolean sameVersion = platformVersionChecker.execute();\n\n        assertThat(sameVersion).isTrue();\n    }\n\n    @Test\n    public void should_not_allow_to_run_snapshot_on_different_schema_version() throws Exception {\n        givenDatabaseSchemaVersion(\"6.0.3\");\n        givenBinaryVersion(\"6.1.1-SNAPSHOT\");\n\n        boolean sameVersion = platformVersionChecker.execute();\n\n        assertThat(sameVersion).isFalse();\n        assertThat(platformVersionChecker.getErrorMessage())\n                .contains(\"Supported database schema version is <6.1> and current database schema version is <6.0.3>\");\n    }\n\n    @Test\n    public void should_not_allow_to_run_when_database_schema_is_in_old_format() throws Exception {\n        givenDatabaseSchemaVersion(\"7.10.5\");\n        givenBinaryVersion(\"7.11.0\");\n\n        boolean sameVersion = platformVersionChecker.execute();\n\n        assertThat(sameVersion).isFalse();\n        assertThat(platformVersionChecker.getErrorMessage()).contains(\n                \"Supported database schema version is <7.11> and current database schema version is <7.10.5>\");\n    }\n\n    @Test\n    public void should_not_allow_to_run_when_schema_is_in_a_different_version_but_starts_with_same_digit()\n            throws Exception {\n        givenDatabaseSchemaVersion(\"6.1\");\n        givenBinaryVersion(\"6.10.1\");\n\n        boolean sameVersion = platformVersionChecker.execute();\n\n        assertThat(sameVersion).isFalse();\n        assertThat(platformVersionChecker.getErrorMessage())\n                .contains(\"Supported database schema version is <6.10> and current database schema version is <6.1>\");\n    }\n\n    @Test\n    public void execute_should_log_bonita_and_database_versions() throws SBonitaException {\n        // given:\n        givenDatabaseSchemaVersion(\"7.12\");\n        givenBinaryVersion(\"7.12.3\");\n\n        systemOutRule.clearLog();\n\n        // when:\n        platformVersionChecker.execute();\n\n        // then:\n        assertThat(systemOutRule.getLog()).contains(\"Bonita platform version (binaries): 7.12.3\",\n                \"Bonita database schema version: 7.12\");\n    }\n\n    @Test\n    public void getVersionFromOtherNodes_should_return_this_node_version_if_no_other_node() {\n        // when:\n        final Optional<String> versionFromOtherNodes = platformVersionChecker.getVersionFromOtherNodes();\n\n        // then:\n        assertThat(versionFromOtherNodes).isEmpty();\n    }\n\n    @Test\n    public void getVersionFromOtherNodes_should_return_other_node_version_if_other_node_returns_ok() {\n        // given:\n        final HashMap<String, TaskResult<String>> hashMap = new HashMap<>();\n        Future<Map<String, TaskResult<String>>> nodeToVersionMap = CompletableFuture.completedFuture(hashMap);\n        hashMap.put(\"node2\", new TaskResult<>(\"7.11.0\"));\n        doReturn(nodeToVersionMap).when(broadcastService).executeOnOthers(any());\n\n        // when:\n        final Optional<String> versionFromOtherNodes = platformVersionChecker.getVersionFromOtherNodes();\n\n        // then:\n        assertThat(versionFromOtherNodes.isPresent()).isTrue();\n        assertThat(versionFromOtherNodes.get()).isEqualTo(\"7.11.0\");\n    }\n\n    @Rule\n    public ExpectedException exception = ExpectedException.none();\n\n    @Test\n    public void getVersionFromOtherNodes_should_throw_exception_if_no_node_can_be_accessed() {\n        // given:\n        final HashMap<String, TaskResult<String>> hashMap = new HashMap<>();\n        Future<Map<String, TaskResult<String>>> nodeToVersionMap = CompletableFuture.completedFuture(hashMap);\n        hashMap.put(\"node2\", new TaskResult<>(new RuntimeException()));\n        doReturn(nodeToVersionMap).when(broadcastService).executeOnOthers(any());\n\n        // then:\n        exception.expect(RuntimeException.class);\n        exception.expectMessage(\"Cannot access other node version in the cluster\");\n\n        // when:\n        platformVersionChecker.getVersionFromOtherNodes();\n    }\n\n    @Test\n    public void getVersionFromOtherNodes_should_throw_exception_if_exception_is_thrown_by_broadcastService()\n            throws Exception {\n        // given:\n        final Future<?> future = mock(Future.class);\n        doReturn(future).when(broadcastService).executeOnOthers(any());\n        doThrow(java.util.concurrent.ExecutionException.class).when(future).get();\n\n        // then:\n        exception.expect(RuntimeException.class);\n        exception.expectMessage(\"Cannot access other node version in the cluster\");\n\n        // when:\n        platformVersionChecker.getVersionFromOtherNodes();\n    }\n\n    @Test\n    public void should_return_false_if_other_node_version_is_different() throws SBonitaException {\n        // given:\n        givenDatabaseSchemaVersion(\"7.11\");\n        final String currentBinariesVersion = \"7.11.2\";\n        givenBinaryVersion(currentBinariesVersion);\n        doReturn(Optional.of(\"7.11.0\")).when(platformVersionChecker).getVersionFromOtherNodes();\n\n        systemOutRule.clearLog();\n\n        // when:\n        final boolean versionValid = platformVersionChecker.execute();\n\n        // then:\n        assertThat(versionValid).isFalse();\n        assertThat(systemOutRule.getLog()).contains(\n                \"Node cannot be started as it is in version 7.11.2 whereas other nodes are in version 7.11.0\");\n    }\n\n    private void givenBinaryVersion(String jarVersion) {\n        when(platformProperties.getPlatformVersion()).thenReturn(jarVersion);\n    }\n\n    private void givenDatabaseSchemaVersion(String dbVersion) {\n        when(platform.getDbSchemaVersion()).thenReturn(dbVersion);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/profile/DefaultProfilesUpdaterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.contentOf;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyLong;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.Mockito.*;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.security.NoSuchAlgorithmException;\n\nimport org.bonitasoft.engine.commons.io.IOUtil;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ExecutionException;\nimport org.bonitasoft.engine.profile.xml.ProfilesNode;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.TemporaryFolder;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class DefaultProfilesUpdaterTest {\n\n    @Rule\n    public TemporaryFolder temporaryFolder = new TemporaryFolder();\n    @Mock\n    public ProfilesImporter profilesImporter;\n    @Mock\n    public ProfilesNode defaultProfiles;\n    private DefaultProfilesUpdater defaultProfilesUpdater;\n    private File md5File;\n\n    @Before\n    public void before() throws IOException, BonitaHomeNotSetException, ExecutionException {\n        md5File = temporaryFolder.newFile();\n        defaultProfilesUpdater = spy(new DefaultProfilesUpdater(profilesImporter));\n        doReturn(md5File).when(defaultProfilesUpdater).getProfilesMD5File();\n        doReturn(\"xml content\").when(defaultProfilesUpdater).getDefaultProfilesXml();\n        doReturn(defaultProfiles).when(defaultProfilesUpdater).getProfilesFromXML(anyString());\n\n        doReturn(null).when(profilesImporter).importProfiles(any(ProfilesNode.class), any(ImportPolicy.class),\n                anyLong());\n    }\n\n    @Test\n    public void execute_should_not_update_when_shouldUpdateProfiles_returns_false() throws Exception {\n        // Given\n        doReturn(false).when(defaultProfilesUpdater).shouldUpdateProfiles(any(File.class), anyString());\n        // When\n        boolean hasUpdated = defaultProfilesUpdater.execute();\n        // Then\n        assertThat(hasUpdated).isFalse();\n    }\n\n    @Test\n    public void execute_should_update_when_shouldUpdateProfiles_returns_true() throws Exception {\n        // Given\n        doReturn(true).when(defaultProfilesUpdater).shouldUpdateProfiles(any(File.class), anyString());\n        doNothing().when(defaultProfilesUpdater).doUpdateProfiles(any(ProfilesNode.class), any(File.class),\n                anyString());\n        // When\n        boolean hasUpdated = defaultProfilesUpdater.execute();\n        // Then\n        assertThat(hasUpdated).isTrue();\n    }\n\n    @Test\n    public void execute_call_doUpdateProfiles() throws Exception {\n        // Given\n        doReturn(true).when(defaultProfilesUpdater).shouldUpdateProfiles(any(File.class), anyString());\n        doNothing().when(defaultProfilesUpdater).doUpdateProfiles(any(ProfilesNode.class), any(File.class),\n                anyString());\n        // When\n        defaultProfilesUpdater.execute();\n        // Then\n        verify(defaultProfilesUpdater).doUpdateProfiles(any(ProfilesNode.class), any(File.class), anyString());\n    }\n\n    @Test\n    public void doUpdateProfiles_should_write_MD5_if_import_succeeds() throws IOException, NoSuchAlgorithmException {\n        // Given\n        String oldHash = \"oldHash\";\n        IOUtil.writeFile(md5File, oldHash);\n        // When\n        defaultProfilesUpdater.doUpdateProfiles(defaultProfiles, md5File, \"content of profiles\");\n        // Then\n        assertThat(contentOf(md5File)).isNotEqualTo(oldHash);\n    }\n\n    @Test\n    public void doUpdateProfiles_should_not_write_MD5_if_import_fails()\n            throws IOException, ExecutionException, NoSuchAlgorithmException {\n        // Given\n        IOUtil.writeFile(md5File, \"oldHash\");\n        doThrow(new ExecutionException(\"\")).when(profilesImporter).importProfiles(any(ProfilesNode.class),\n                any(ImportPolicy.class), anyLong());\n        // When\n        defaultProfilesUpdater.doUpdateProfiles(defaultProfiles, md5File, \"content of profiles\");\n        // Then\n        assertThat(md5File).hasContent(\"oldHash\");\n    }\n\n    @Test\n    public void doUpdate_call_importer() throws IOException, NoSuchAlgorithmException, ExecutionException {\n        // Given\n        ProfilesNode profilesFromXML = new ProfilesNode();\n        // When\n        defaultProfilesUpdater.doUpdateProfiles(profilesFromXML, md5File, \"content of profiles\");\n        // Then\n        verify(profilesImporter).importProfiles(any(ProfilesNode.class), eq(ImportPolicy.UPDATE_DEFAULTS), eq(-1L));\n        assertThat(md5File).hasContent(IOUtil.md5(\"content of profiles\".getBytes()));\n    }\n\n    @Test\n    public void shouldUpdateProfiles_should_return_false_when_md5_matches() throws Exception {\n        // Given\n        String defaultProfilesXml = \"the content\";\n        IOUtil.writeMD5(md5File, defaultProfilesXml.getBytes());\n        // When\n        boolean shouldUpdateProfiles = defaultProfilesUpdater.shouldUpdateProfiles(md5File, defaultProfilesXml);\n        // Then\n        assertThat(shouldUpdateProfiles).isFalse();\n    }\n\n    @Test\n    public void shouldUpdateProfiles_should_return_true_when_md5_is_missing() throws Exception {\n        // Given\n        String defaultProfilesXml = \"the content\";\n        md5File.delete();\n        // When\n        boolean shouldUpdateProfiles = defaultProfilesUpdater.shouldUpdateProfiles(md5File, defaultProfilesXml);\n        // Then\n        assertThat(shouldUpdateProfiles).isTrue();\n    }\n\n    @Test\n    public void shouldUpdateProfiles_should_return_true_when_md5_differs() throws Exception {\n        // Given\n        String defaultProfilesXml = \"the content\";\n        IOUtil.writeMD5(md5File, \"other content\".getBytes());\n        // When\n        boolean shouldUpdateProfiles = defaultProfilesUpdater.shouldUpdateProfiles(md5File, defaultProfilesXml);\n        // Then\n        assertThat(shouldUpdateProfiles).isTrue();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/profile/ProfileImportStrategyTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.when;\n\nimport org.bonitasoft.engine.profile.xml.ProfileNode;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ProfileImportStrategyTest {\n\n    @Mock\n    private ProfileService profileService;\n\n    @Mock\n    private ProfileNode defaultExportedProfile;\n\n    @Mock\n    private ProfileNode customExportedProfile;\n\n    @Before\n    public void before() {\n        when(defaultExportedProfile.isDefault()).thenReturn(true);\n        when(customExportedProfile.isDefault()).thenReturn(false);\n    }\n\n    @Test\n    public void FailOnDuplicate_stategy_allows_to_create_defaults_profile() {\n        // given\n        final FailOnDuplicateImportStrategy strategy = new FailOnDuplicateImportStrategy(profileService);\n\n        // when then\n        assertThat(strategy.canCreateProfileIfNotExists(defaultExportedProfile)).isTrue();\n    }\n\n    @Test\n    public void FailOnDuplicate_stategy_allows_to_create_custom_profile() {\n        // given\n        final FailOnDuplicateImportStrategy strategy = new FailOnDuplicateImportStrategy(profileService);\n\n        // when then\n        assertThat(strategy.canCreateProfileIfNotExists(customExportedProfile)).isTrue();\n    }\n\n    @Test\n    public void IgnoreDuplicate_stategy_allows_to_create_defaults_profile() {\n        // given\n        final IgnoreDuplicateImportStrategy strategy = new IgnoreDuplicateImportStrategy(profileService);\n\n        // when then\n        assertThat(strategy.canCreateProfileIfNotExists(defaultExportedProfile)).isTrue();\n    }\n\n    @Test\n    public void IgnoreDuplicate_stategy_allows_to_create_custom_profile() {\n        // given\n        final IgnoreDuplicateImportStrategy strategy = new IgnoreDuplicateImportStrategy(profileService);\n\n        // when then\n        assertThat(strategy.canCreateProfileIfNotExists(customExportedProfile)).isTrue();\n    }\n\n    @Test\n    public void DeleteExisting_stategy_allows_to_create_defaults_profile() {\n        // given\n        final DeleteExistingImportStrategy strategy = new DeleteExistingImportStrategy(profileService);\n\n        // when then\n        assertThat(strategy.canCreateProfileIfNotExists(defaultExportedProfile)).isTrue();\n    }\n\n    @Test\n    public void DeleteExisting_stategy_allows_to_create_custom_profile() {\n        // given\n        final DeleteExistingImportStrategy strategy = new DeleteExistingImportStrategy(profileService);\n\n        // when then\n        assertThat(strategy.canCreateProfileIfNotExists(customExportedProfile)).isTrue();\n    }\n\n    @Test\n    public void ReplaceDuplicate_stategy_refuse_to_create_defaults_profile() {\n        // given\n        final ReplaceDuplicateImportStrategy strategy = new ReplaceDuplicateImportStrategy(profileService);\n\n        // when then\n        assertThat(strategy.canCreateProfileIfNotExists(defaultExportedProfile)).isFalse();\n    }\n\n    @Test\n    public void ReplaceDuplicate_stategy_allows_to_create_custom_profile() {\n        // given\n        final ReplaceDuplicateImportStrategy strategy = new ReplaceDuplicateImportStrategy(profileService);\n\n        // when then\n        assertThat(strategy.canCreateProfileIfNotExists(customExportedProfile)).isTrue();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/profile/ProfilesExporterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.Arrays;\n\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.profile.model.SProfile;\nimport org.bonitasoft.engine.profile.xml.ProfileNode;\nimport org.bonitasoft.engine.profile.xml.ProfilesNode;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Baptiste Mesta\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class ProfilesExporterTest {\n\n    @Mock\n    private IdentityService identityService;\n    @Mock\n    private ProfileService profileService;\n    @Mock\n    private ProfilesParser profilesParser;\n    @InjectMocks\n    private ProfilesExporter profilesExporter;\n\n    @Test\n    public void should_convert_profiles_to_exported_version() throws Exception {\n        //given\n        SProfile profile1 = SProfile.builder().build();\n        profile1.setName(\"MyProfile1\");\n        SProfile profile2 = SProfile.builder().build();\n        profile2.setName(\"MyProfile2\");\n        //when\n        ProfilesNode exportedProfiles = profilesExporter.toProfiles(Arrays.asList(profile1, profile2));\n        //then\n        assertThat(exportedProfiles.getProfiles()).containsOnly(new ProfileNode(\"MyProfile1\", false),\n                new ProfileNode(\"MyProfile2\", false));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/profile/ProfilesImporterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.ImportError;\nimport org.bonitasoft.engine.api.ImportError.Type;\nimport org.bonitasoft.engine.api.ImportStatus;\nimport org.bonitasoft.engine.api.ImportStatus.Status;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.SGroupNotFoundException;\nimport org.bonitasoft.engine.identity.SRoleNotFoundException;\nimport org.bonitasoft.engine.identity.SUserNotFoundException;\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.identity.model.SRole;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.profile.exception.profile.SProfileNotFoundException;\nimport org.bonitasoft.engine.profile.model.SProfile;\nimport org.bonitasoft.engine.profile.xml.MembershipNode;\nimport org.bonitasoft.engine.profile.xml.ProfileMappingNode;\nimport org.bonitasoft.engine.profile.xml.ProfileNode;\nimport org.bonitasoft.engine.profile.xml.ProfilesNode;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ProfilesImporterTest {\n\n    @Mock\n    private IdentityService identityService;\n\n    @Mock\n    private ProfileImportStrategy importStrategy;\n\n    @Mock\n    private ProfileService profileService;\n\n    @Mock\n    EntityUpdateDescriptor entityUpdateDescriptor;\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @InjectMocks\n    private ProfilesImporter profilesImporter;\n\n    @Before\n    public void before() throws Exception {\n        doReturn(mock(SProfile.class)).when(profileService).createProfile(any(SProfile.class));\n        doReturn(mock(SProfile.class)).when(profileService).updateProfile(any(SProfile.class),\n                any(EntityUpdateDescriptor.class));\n    }\n\n    @Test\n    public void should_importProfiles_replace_custom_profile() throws Exception {\n        // given\n        final ProfileNode exportedProfile = new ProfileNode(\"Mine\", false);\n\n        doReturn(mock(SProfile.class)).when(profileService).getProfileByName(exportedProfile.getName());\n\n        // when\n        final List<ImportStatus> importProfiles = profilesImporter.importProfiles(exportedProfiles(exportedProfile),\n                ImportPolicy.REPLACE_DUPLICATES, -1);\n\n        // then: all entries and mappings are replaced\n        verify(profileService, times(1)).deleteAllProfileMembersOfProfile(any(SProfile.class));\n        assertThat(importProfiles.get(0)).isEqualTo(importStatusWith(\"Mine\", Status.REPLACED));\n    }\n\n    @Test\n    public void should_importProfiles_replace_default_profile() throws Exception {\n        // given\n        final ProfileNode exportedProfile = new ProfileNode(\"Mine\", true);\n\n        doReturn(mock(SProfile.class)).when(profileService).getProfileByName(exportedProfile.getName());\n\n        // when\n        final List<ImportStatus> importProfiles = profilesImporter.importProfiles(exportedProfiles(exportedProfile),\n                ImportPolicy.REPLACE_DUPLICATES, -1);\n\n        // then: all entries and mappings are replaced\n        verify(profileService, times(1)).deleteAllProfileMembersOfProfile(any(SProfile.class));\n        assertThat(importProfiles.get(0)).isEqualTo(importStatusWith(\"Mine\", Status.REPLACED));\n    }\n\n    private ProfilesNode exportedProfiles(ProfileNode... exportedProfile) {\n        return new ProfilesNode(Arrays.asList(exportedProfile));\n    }\n\n    @Test\n    public void should_importProfiles_add_profile() throws Exception {\n        // given\n        final ProfileNode exportedProfile = new ProfileNode(\"MineNotDefault\", false);\n        // profile do not exists\n        doThrow(new SProfileNotFoundException(\"\")).when(profileService).getProfileByName(\"MineNotDefault\");\n        doThrow(new SProfileNotFoundException(\"\")).when(profileService).getProfileByName(\"MineDefault\");\n\n        // when\n        final List<ImportStatus> importProfiles = profilesImporter.importProfiles(\n                exportedProfiles(exportedProfile, new ProfileNode(\"MineDefault\", true)),\n                ImportPolicy.REPLACE_DUPLICATES, -1);\n\n        assertThat(importProfiles.get(0)).isEqualTo(importStatusWith(\"MineNotDefault\", Status.ADDED));\n        assertThat(importProfiles.get(1)).isEqualTo(importStatusWith(\"MineDefault\", Status.SKIPPED));\n    }\n\n    @Test\n    public void should_importProfiles_skip_profile_when_strategy_tells_it() throws Exception {\n        // given\n        final ProfileNode exportedProfile = new ProfileNode(\"Mine\", false);\n        // profile exists\n        doReturn(mock(SProfile.class)).when(profileService).getProfileByName(exportedProfile.getName());\n\n        // when\n        final List<ImportStatus> importProfiles = profilesImporter.importProfiles(exportedProfiles(exportedProfile),\n                ImportPolicy.IGNORE_DUPLICATES, -1);\n\n        assertThat(importProfiles.get(0)).isEqualTo(importStatusWith(\"Mine\", Status.SKIPPED));\n    }\n\n    @Test\n    public void should_importProfiles_import_nothing_when_profile_name_is_empty() throws Exception {\n        // when\n        final List<ImportStatus> importProfiles = profilesImporter.importProfiles(\n                exportedProfiles(new ProfileNode(\"\", false)),\n                ImportPolicy.UPDATE_DEFAULTS, -1);\n\n        // then\n        assertThat(importProfiles).isEmpty();\n    }\n\n    @Test\n    public void should_importProfiles_import_nothing_when_profile_name_is_null() throws Exception {\n        // when\n        final List<ImportStatus> importProfiles = profilesImporter.importProfiles(\n                exportedProfiles(new ProfileNode(null, false)),\n                ImportPolicy.UPDATE_DEFAULTS, -1);\n\n        // then\n        assertThat(importProfiles).isEmpty();\n    }\n\n    @Test\n    public void should_importProfiles_with_skip_strategy_return_skip_status() throws Exception {\n        // given\n        final ProfileNode exportedProfile = new ProfileNode(\"Mine\", true);\n\n        doReturn(mock(SProfile.class)).when(profileService).getProfileByName(exportedProfile.getName());\n        // when\n        final List<ImportStatus> importProfiles = profilesImporter.importProfiles(exportedProfiles(exportedProfile),\n                ImportPolicy.IGNORE_DUPLICATES, -1);\n\n        // then\n        verify(profileService, never()).deleteAllProfileMembersOfProfile(any(SProfile.class));\n        assertThat(importProfiles.get(0)).isEqualTo(importStatusWith(\"Mine\", Status.SKIPPED));\n    }\n\n    private ImportStatus importStatusWith(final String name, final Status status) {\n        final ImportStatus expected = new ImportStatus(name);\n        expected.setStatus(status);\n        return expected;\n    }\n\n    @Test\n    public void should_importProfileMapping_return_error_if_user_do_not_exists() throws Exception {\n        // given\n        final ProfileMappingNode exportedProfileMapping = new ProfileMappingNode();\n        exportedProfileMapping.setUsers(Collections.singletonList(\"john\"));\n        doThrow(new SUserNotFoundException(\"john\")).when(identityService).getUserByUserName(\"john\");\n        // when\n        final List<ImportError> importProfileMapping = profilesImporter.importProfileMapping(profileService,\n                identityService, 123, exportedProfileMapping);\n\n        // then\n        assertThat(importProfileMapping.get(0)).isEqualTo(new ImportError(\"john\", Type.USER));\n    }\n\n    @Test\n    public void should_importProfileMapping_return_no_error_if_user_exists() throws Exception {\n        // given\n        final ProfileMappingNode exportedProfileMapping = new ProfileMappingNode();\n        exportedProfileMapping.setUsers(Collections.singletonList(\"john\"));\n        final SUser user = mock(SUser.class);\n        when(user.getId()).thenReturn(456L);\n        when(user.getUserName()).thenReturn(\"john\");\n        doReturn(user).when(identityService).getUserByUserName(\"john\");\n\n        // when\n        final List<ImportError> importProfileMapping = profilesImporter.importProfileMapping(profileService,\n                identityService, 123, exportedProfileMapping);\n\n        // then\n        assertThat(importProfileMapping).isEmpty();\n        verify(profileService, times(1)).addUserToProfile(123L, 456L, null, null, \"john\");\n    }\n\n    @Test\n    public void should_importProfileMapping_return_error_if_role_do_not_exists() throws Exception {\n        // given\n        final ProfileMappingNode exportedProfileMapping = new ProfileMappingNode();\n        exportedProfileMapping.setRoles(Collections.singletonList(\"role\"));\n        doThrow(new SRoleNotFoundException(\"role\")).when(identityService).getRoleByName(\"role\");\n        // when\n        final List<ImportError> importProfileMapping = profilesImporter.importProfileMapping(profileService,\n                identityService, 123, exportedProfileMapping);\n\n        // then\n        assertThat(importProfileMapping.get(0)).isEqualTo(new ImportError(\"role\", Type.ROLE));\n    }\n\n    @Test\n    public void should_importProfileMapping_return_no_error_if_role_exists() throws Exception {\n        // given\n        final ProfileMappingNode exportedProfileMapping = new ProfileMappingNode();\n        exportedProfileMapping.setRoles(Collections.singletonList(\"role\"));\n        final SRole role = mock(SRole.class);\n        when(role.getId()).thenReturn(456L);\n        when(role.getName()).thenReturn(\"role\");\n        doReturn(role).when(identityService).getRoleByName(\"role\");\n\n        // when\n        final List<ImportError> importProfileMapping = profilesImporter.importProfileMapping(profileService,\n                identityService, 123, exportedProfileMapping);\n\n        // then\n        assertThat(importProfileMapping).isEmpty();\n\n        verify(profileService, times(1)).addRoleToProfile(123L, 456L, \"role\");\n    }\n\n    @Test\n    public void should_importProfileMapping_return_error_if_group_do_not_exists() throws Exception {\n        // given\n        final ProfileMappingNode exportedProfileMapping = new ProfileMappingNode();\n        exportedProfileMapping.setGroups(Collections.singletonList(\"group\"));\n        doThrow(new SGroupNotFoundException(\"group\")).when(identityService).getGroupByPath(\"group\");\n        // when\n        final List<ImportError> importProfileMapping = profilesImporter.importProfileMapping(profileService,\n                identityService, 123, exportedProfileMapping);\n\n        // then\n        assertThat(importProfileMapping.get(0)).isEqualTo(new ImportError(\"group\", Type.GROUP));\n    }\n\n    @Test\n    public void should_importProfileMapping_return_no_error_if_group_exists() throws Exception {\n        // given\n        final ProfileMappingNode exportedProfileMapping = new ProfileMappingNode();\n        exportedProfileMapping.setGroups(Collections.singletonList(\"group\"));\n        final SGroup group = mock(SGroup.class);\n        when(group.getId()).thenReturn(456L);\n        when(group.getName()).thenReturn(\"group\");\n        doReturn(group).when(identityService).getGroupByPath(\"group\");\n\n        // when\n        final List<ImportError> importProfileMapping = profilesImporter.importProfileMapping(profileService,\n                identityService, 123, exportedProfileMapping);\n\n        // then\n        assertThat(importProfileMapping).isEmpty();\n\n        verify(profileService, times(1)).addGroupToProfile(123L, 456L, \"group\", null);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Test\n    public void should_importProfileMapping_return_error_if_group_membership_do_not_exists() throws Exception {\n        // given\n        final ProfileMappingNode exportedProfileMapping = new ProfileMappingNode();\n        exportedProfileMapping.setMemberships(Collections.singletonList(new MembershipNode(\"group\", \"role\")));\n        doThrow(new SGroupNotFoundException(\"group\")).when(identityService).getGroupByPath(\"group\");\n        // when\n        final List<ImportError> importProfileMapping = profilesImporter.importProfileMapping(profileService,\n                identityService, 123, exportedProfileMapping);\n\n        // then\n        assertThat(importProfileMapping.get(0)).isEqualTo(new ImportError(\"group\", Type.GROUP));\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Test\n    public void should_importProfileMapping_return_error_if_role_membership_do_not_exists() throws Exception {\n        // given\n        final ProfileMappingNode exportedProfileMapping = new ProfileMappingNode();\n        exportedProfileMapping.setMemberships(Collections.singletonList(new MembershipNode(\"group\", \"role\")));\n        doThrow(new SRoleNotFoundException(\"role\")).when(identityService).getRoleByName(\"role\");\n        // when\n        final List<ImportError> importProfileMapping = profilesImporter.importProfileMapping(profileService,\n                identityService, 123, exportedProfileMapping);\n\n        // then\n        assertThat(importProfileMapping.get(0)).isEqualTo(new ImportError(\"role\", Type.ROLE));\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Test\n    public void should_importProfileMapping_return_error_if_role_and_group_membership_do_not_exists() throws Exception {\n        // given\n        final ProfileMappingNode exportedProfileMapping = new ProfileMappingNode();\n        exportedProfileMapping.setMemberships(Collections.singletonList(new MembershipNode(\"group\", \"role\")));\n        doThrow(new SRoleNotFoundException(\"role\")).when(identityService).getRoleByName(\"role\");\n        doThrow(new SGroupNotFoundException(\"group\")).when(identityService).getGroupByPath(\"group\");\n        // when\n        final List<ImportError> importProfileMapping = profilesImporter.importProfileMapping(profileService,\n                identityService, 123, exportedProfileMapping);\n\n        // then\n        assertThat(importProfileMapping.get(1)).isEqualTo(new ImportError(\"role\", Type.ROLE));\n        assertThat(importProfileMapping.get(0)).isEqualTo(new ImportError(\"group\", Type.GROUP));\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Test\n    public void should_importProfileMapping_return_no_error_if_membershi_exists() throws Exception {\n        // given\n        final ProfileMappingNode exportedProfileMapping = new ProfileMappingNode();\n        exportedProfileMapping.setMemberships(Collections.singletonList(new MembershipNode(\"group\", \"role\")));\n        final SRole role = mock(SRole.class);\n        when(role.getId()).thenReturn(456L);\n        when(role.getName()).thenReturn(\"role\");\n        doReturn(role).when(identityService).getRoleByName(\"role\");\n        final SGroup group = mock(SGroup.class);\n        when(group.getId()).thenReturn(789L);\n        when(group.getName()).thenReturn(\"group\");\n        doReturn(group).when(identityService).getGroupByPath(\"group\");\n\n        // when\n        final List<ImportError> importProfileMapping = profilesImporter.importProfileMapping(profileService,\n                identityService, 123, exportedProfileMapping);\n\n        // then\n        assertThat(importProfileMapping).isEmpty();\n\n        verify(profileService, times(1)).addRoleAndGroupToProfile(123L, 456L, 789L, \"role\", \"group\", null);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/profile/UpdateDefaultsAndCreateNewImportStrategyTest.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.verify;\n\nimport org.bonitasoft.engine.profile.exception.profile.SProfileUpdateException;\nimport org.bonitasoft.engine.profile.model.SProfile;\nimport org.bonitasoft.engine.profile.xml.ProfileNode;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class UpdateDefaultsAndCreateNewImportStrategyTest {\n\n    @Mock\n    private ProfileService profileService;\n    @InjectMocks\n    private UpdateDefaultsAndCreateNewImportStrategy strategy;\n\n    @Test\n    public void should_update_profile_if_new_default_profile() throws SProfileUpdateException {\n        //given\n        SProfile existingProfile = SProfile.builder().isDefault(false).build();\n        ProfileNode newProfile = new ProfileNode(\"foo\", true);\n\n        //when\n        strategy.whenProfileExists(-1, newProfile, existingProfile);\n\n        //then\n        verify(profileService).updateProfile(eq(existingProfile), any(EntityUpdateDescriptor.class));\n    }\n\n    @Test\n    public void should_update_profile_if_existing_default_profile() throws SProfileUpdateException {\n        //given\n        SProfile existingProfile = SProfile.builder().isDefault(true).build();\n        ProfileNode newProfile = new ProfileNode(\"foo\", false);\n\n        //when\n        strategy.whenProfileExists(-1, newProfile, existingProfile);\n\n        //then\n        verify(profileService).updateProfile(eq(existingProfile), any(EntityUpdateDescriptor.class));\n    }\n\n    @Test\n    public void should_update_profile_if_new_and_existing_default_profiles() throws SProfileUpdateException {\n        //given\n        SProfile existingProfile = SProfile.builder().isDefault(true).build();\n        ProfileNode newProfile = new ProfileNode(\"foo\", true);\n\n        //when\n        strategy.whenProfileExists(-1, newProfile, existingProfile);\n\n        //then\n        verify(profileService).updateProfile(eq(existingProfile), any(EntityUpdateDescriptor.class));\n    }\n\n    @Test\n    public void should_raise_exception_if_existing_non_default_profile() {\n        //given\n        SProfile existingProfile = SProfile.builder().isDefault(false).build();\n        ProfileNode newProfile = new ProfileNode(\"foo\", false);\n\n        //then\n        assertThatExceptionOfType(SProfileUpdateException.class)\n                .isThrownBy(() -> strategy.whenProfileExists(-1, newProfile, existingProfile))\n                .withMessage(\"A profile already exists with name: foo\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/profile/UpdateDefaultsImportStrategyTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\nimport org.bonitasoft.engine.profile.exception.profile.SProfileUpdateException;\nimport org.bonitasoft.engine.profile.model.SProfile;\nimport org.bonitasoft.engine.profile.xml.ProfileNode;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class UpdateDefaultsImportStrategyTest {\n\n    @Mock\n    public ProfileService profileService;\n    @InjectMocks\n    public UpdateDefaultsImportStrategy updateDefaultsImportStrategy;\n\n    @Test\n    public void should_whenProfileExists_update_if_default()\n            throws SProfileUpdateException {\n        //when\n        SProfile sProfile = SProfile.builder().build();\n        sProfile.setDefault(false);\n        updateDefaultsImportStrategy.whenProfileExists(-1, new ProfileNode(\"plop\", true), sProfile);\n\n        verify(profileService).updateProfile(eq(sProfile), any(EntityUpdateDescriptor.class));\n    }\n\n    @Test\n    public void should_whenProfileExists_update_if_default2()\n            throws SProfileUpdateException {\n        //when\n        SProfile sProfile = SProfile.builder().build();\n        sProfile.setDefault(true);\n        updateDefaultsImportStrategy.whenProfileExists(-1, new ProfileNode(\"plop\", false), sProfile);\n\n        verify(profileService).updateProfile(eq(sProfile), any(EntityUpdateDescriptor.class));\n    }\n\n    @Test\n    public void should_whenProfileExists_not_update_if_custom()\n            throws SProfileUpdateException {\n        //when\n        SProfile sProfile = SProfile.builder().build();\n        sProfile.setDefault(false);\n        updateDefaultsImportStrategy.whenProfileExists(-1, new ProfileNode(\"plop\", false), sProfile);\n\n        verify(profileService, times(0)).updateProfile(any(SProfile.class), any(EntityUpdateDescriptor.class));\n    }\n\n    @Test\n    public void should_canCreateProfileIfNotExists_return_true_if_default() {\n        boolean canCreateProfileIfNotExists = updateDefaultsImportStrategy\n                .canCreateProfileIfNotExists(new ProfileNode(\"plop\", true));\n\n        assertThat(canCreateProfileIfNotExists).isTrue();\n    }\n\n    @Test\n    public void should_canCreateProfileIfNotExists_return_false_if_custom() {\n        boolean canCreateProfileIfNotExists = updateDefaultsImportStrategy\n                .canCreateProfileIfNotExists(new ProfileNode(\"plop\", false));\n\n        assertThat(canCreateProfileIfNotExists).isFalse();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/search/AbstractActivityInstanceSearchEntityTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.impl.SearchOptionsImpl;\nimport org.junit.Test;\n\n/**\n * author Emmanuel Duchastenier\n */\npublic class AbstractActivityInstanceSearchEntityTest {\n\n    @Test\n    public void getEntityClassShouldHandleAutoTaskType() throws Exception {\n        final SearchOptionsImpl searchOptions = new SearchOptionsImpl(0, 10);\n        searchOptions.addFilter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.AUTOMATIC_TASK);\n        final AbstractActivityInstanceSearchEntity searcher = new MyAbstractActivityInstanceSearchEntity(searchOptions);\n\n        assertThat(searcher.getEntityClass()).isEqualTo(SAutomaticTaskInstance.class);\n    }\n\n    @Test\n    public void getEntityClassShouldHandleManualTaskType() throws Exception {\n        final SearchOptionsImpl searchOptions = new SearchOptionsImpl(0, 10);\n        searchOptions.addFilter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.MANUAL_TASK);\n        final AbstractActivityInstanceSearchEntity searcher = new MyAbstractActivityInstanceSearchEntity(searchOptions);\n\n        assertThat(searcher.getEntityClass()).isEqualTo(SManualTaskInstance.class);\n    }\n\n    @Test\n    public void getEntityClassShouldHandleUserTaskType() throws Exception {\n        final SearchOptionsImpl searchOptions = new SearchOptionsImpl(0, 10);\n        searchOptions.addFilter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.USER_TASK);\n        final AbstractActivityInstanceSearchEntity searcher = new MyAbstractActivityInstanceSearchEntity(searchOptions);\n\n        assertThat(searcher.getEntityClass()).isEqualTo(SUserTaskInstance.class);\n    }\n\n    @Test\n    public void getEntityClassShouldHandleHumanTaskType() throws Exception {\n        final SearchOptionsImpl searchOptions = new SearchOptionsImpl(0, 10);\n        searchOptions.addFilter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.HUMAN_TASK);\n        final AbstractActivityInstanceSearchEntity searcher = new MyAbstractActivityInstanceSearchEntity(searchOptions);\n\n        assertThat(searcher.getEntityClass()).isEqualTo(SHumanTaskInstance.class);\n    }\n\n    @Test\n    public void getEntityClassShouldHandleReceiveTaskType() throws Exception {\n        final SearchOptionsImpl searchOptions = new SearchOptionsImpl(0, 10);\n        searchOptions.addFilter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.RECEIVE_TASK);\n        final AbstractActivityInstanceSearchEntity searcher = new MyAbstractActivityInstanceSearchEntity(searchOptions);\n\n        assertThat(searcher.getEntityClass()).isEqualTo(SReceiveTaskInstance.class);\n    }\n\n    @Test\n    public void getEntityClassShouldHandleSendTaskType() throws Exception {\n        final SearchOptionsImpl searchOptions = new SearchOptionsImpl(0, 10);\n        searchOptions.addFilter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.SEND_TASK);\n        final AbstractActivityInstanceSearchEntity searcher = new MyAbstractActivityInstanceSearchEntity(searchOptions);\n\n        assertThat(searcher.getEntityClass()).isEqualTo(SSendTaskInstance.class);\n    }\n\n    @Test\n    public void getEntityClassShouldHandleCallActivityType() throws Exception {\n        final SearchOptionsImpl searchOptions = new SearchOptionsImpl(0, 10);\n        searchOptions.addFilter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.CALL_ACTIVITY);\n        final AbstractActivityInstanceSearchEntity searcher = new MyAbstractActivityInstanceSearchEntity(searchOptions);\n\n        assertThat(searcher.getEntityClass()).isEqualTo(SCallActivityInstance.class);\n    }\n\n    @Test\n    public void getEntityClassShouldHandleLoopTaskType() throws Exception {\n        final SearchOptionsImpl searchOptions = new SearchOptionsImpl(0, 10);\n        searchOptions.addFilter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.LOOP_ACTIVITY);\n        final AbstractActivityInstanceSearchEntity searcher = new MyAbstractActivityInstanceSearchEntity(searchOptions);\n\n        assertThat(searcher.getEntityClass()).isEqualTo(SLoopActivityInstance.class);\n    }\n\n    @Test\n    public void getEntityClassShouldHandleMultiInstanceTaskType() throws Exception {\n        final SearchOptionsImpl searchOptions = new SearchOptionsImpl(0, 10);\n        searchOptions.addFilter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.MULTI_INSTANCE_ACTIVITY);\n        final AbstractActivityInstanceSearchEntity searcher = new MyAbstractActivityInstanceSearchEntity(searchOptions);\n\n        assertThat(searcher.getEntityClass()).isEqualTo(SMultiInstanceActivityInstance.class);\n    }\n\n    @Test\n    public void getEntityClassShouldThrowExceptionOnUnknownType() {\n        final SearchOptionsImpl searchOptions = new SearchOptionsImpl(0, 10);\n        searchOptions.addFilter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.START_EVENT);\n\n        assertThrows(SBonitaReadException.class, () -> new MyAbstractActivityInstanceSearchEntity(searchOptions));\n    }\n\n    @Test\n    public void getEntityClassShouldHandleSuProcessTaskType() throws Exception {\n        final SearchOptionsImpl searchOptions = new SearchOptionsImpl(0, 10);\n        searchOptions.addFilter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.SUB_PROCESS);\n        final AbstractActivityInstanceSearchEntity searcher = new MyAbstractActivityInstanceSearchEntity(searchOptions);\n\n        assertThat(searcher.getEntityClass()).isEqualTo(SSubProcessActivityInstance.class);\n    }\n\n    private static class MyAbstractActivityInstanceSearchEntity extends AbstractActivityInstanceSearchEntity {\n\n        public MyAbstractActivityInstanceSearchEntity(SearchOptionsImpl searchOptions) throws SBonitaReadException {\n            super(null, searchOptions, null);\n        }\n\n        @Override\n        public long executeCount(QueryOptions queryOptions) {\n            return 0;\n        }\n\n        @Override\n        public List<SActivityInstance> executeSearch(QueryOptions queryOptions) {\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/search/AbstractArchiveActivityInstanceSearchEntityTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAAutomaticTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SACallActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SALoopActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAManualTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAMultiInstanceActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAReceiveTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SASendTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SASubProcessActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.search.impl.SearchOptionsImpl;\nimport org.junit.Test;\n\n/**\n * author Emmanuel Duchastenier\n */\npublic class AbstractArchiveActivityInstanceSearchEntityTest {\n\n    @Test\n    public void getEntityClassShouldHandleAutoTaskType() {\n        final SearchOptionsImpl searchOptions = new SearchOptionsImpl(0, 10);\n        searchOptions.addFilter(ArchivedActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.AUTOMATIC_TASK);\n        final AbstractArchiveActivityInstanceSearchEntity searcher = new MyAbstractArchiveActivityInstanceSearchEntity(\n                searchOptions);\n\n        assertThat(searcher.getEntityClass()).isEqualTo(SAAutomaticTaskInstance.class);\n    }\n\n    @Test\n    public void getEntityClassShouldHandleManualTaskType() {\n        final SearchOptionsImpl searchOptions = new SearchOptionsImpl(0, 10);\n        searchOptions.addFilter(ArchivedActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.MANUAL_TASK);\n        final AbstractArchiveActivityInstanceSearchEntity searcher = new MyAbstractArchiveActivityInstanceSearchEntity(\n                searchOptions);\n\n        assertThat(searcher.getEntityClass()).isEqualTo(SAManualTaskInstance.class);\n    }\n\n    @Test\n    public void getEntityClassShouldHandleUserTaskType() {\n        final SearchOptionsImpl searchOptions = new SearchOptionsImpl(0, 10);\n        searchOptions.addFilter(ArchivedActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.USER_TASK);\n        final AbstractArchiveActivityInstanceSearchEntity searcher = new MyAbstractArchiveActivityInstanceSearchEntity(\n                searchOptions);\n\n        assertThat(searcher.getEntityClass()).isEqualTo(SAUserTaskInstance.class);\n    }\n\n    @Test\n    public void getEntityClassShouldHandleHumanTaskType() {\n        final SearchOptionsImpl searchOptions = new SearchOptionsImpl(0, 10);\n        searchOptions.addFilter(ArchivedActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.HUMAN_TASK);\n        final AbstractArchiveActivityInstanceSearchEntity searcher = new MyAbstractArchiveActivityInstanceSearchEntity(\n                searchOptions);\n\n        assertThat(searcher.getEntityClass()).isEqualTo(SAHumanTaskInstance.class);\n    }\n\n    @Test\n    public void getEntityClassShouldHandleReceiveTaskType() {\n        final SearchOptionsImpl searchOptions = new SearchOptionsImpl(0, 10);\n        searchOptions.addFilter(ArchivedActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.RECEIVE_TASK);\n        final AbstractArchiveActivityInstanceSearchEntity searcher = new MyAbstractArchiveActivityInstanceSearchEntity(\n                searchOptions);\n\n        assertThat(searcher.getEntityClass()).isEqualTo(SAReceiveTaskInstance.class);\n    }\n\n    @Test\n    public void getEntityClassShouldHandleSendTaskType() {\n        final SearchOptionsImpl searchOptions = new SearchOptionsImpl(0, 10);\n        searchOptions.addFilter(ArchivedActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.SEND_TASK);\n        final AbstractArchiveActivityInstanceSearchEntity searcher = new MyAbstractArchiveActivityInstanceSearchEntity(\n                searchOptions);\n\n        assertThat(searcher.getEntityClass()).isEqualTo(SASendTaskInstance.class);\n    }\n\n    @Test\n    public void getEntityClassShouldHandleCallActivityType() {\n        final SearchOptionsImpl searchOptions = new SearchOptionsImpl(0, 10);\n        searchOptions.addFilter(ArchivedActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.CALL_ACTIVITY);\n        final AbstractArchiveActivityInstanceSearchEntity searcher = new MyAbstractArchiveActivityInstanceSearchEntity(\n                searchOptions);\n\n        assertThat(searcher.getEntityClass()).isEqualTo(SACallActivityInstance.class);\n    }\n\n    @Test\n    public void getEntityClassShouldHandleLoopTaskType() {\n        final SearchOptionsImpl searchOptions = new SearchOptionsImpl(0, 10);\n        searchOptions.addFilter(ArchivedActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.LOOP_ACTIVITY);\n        final AbstractArchiveActivityInstanceSearchEntity searcher = new MyAbstractArchiveActivityInstanceSearchEntity(\n                searchOptions);\n\n        assertThat(searcher.getEntityClass()).isEqualTo(SALoopActivityInstance.class);\n    }\n\n    @Test\n    public void getEntityClassShouldHandleMultiInstanceTaskType() {\n        final SearchOptionsImpl searchOptions = new SearchOptionsImpl(0, 10);\n        searchOptions.addFilter(ArchivedActivityInstanceSearchDescriptor.ACTIVITY_TYPE,\n                FlowNodeType.MULTI_INSTANCE_ACTIVITY);\n        final AbstractArchiveActivityInstanceSearchEntity searcher = new MyAbstractArchiveActivityInstanceSearchEntity(\n                searchOptions);\n\n        assertThat(searcher.getEntityClass()).isEqualTo(SAMultiInstanceActivityInstance.class);\n    }\n\n    @Test\n    public void getEntityClassShouldHandleSuProcessTaskType() {\n        final SearchOptionsImpl searchOptions = new SearchOptionsImpl(0, 10);\n        searchOptions.addFilter(ArchivedActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.SUB_PROCESS);\n        final AbstractArchiveActivityInstanceSearchEntity searcher = new MyAbstractArchiveActivityInstanceSearchEntity(\n                searchOptions);\n\n        assertThat(searcher.getEntityClass()).isEqualTo(SASubProcessActivityInstance.class);\n    }\n\n    private static class MyAbstractArchiveActivityInstanceSearchEntity\n            extends AbstractArchiveActivityInstanceSearchEntity {\n\n        public MyAbstractArchiveActivityInstanceSearchEntity(SearchOptionsImpl searchOptions) {\n            super(null, searchOptions, null);\n        }\n\n        @Override\n        public long executeCount(QueryOptions queryOptions) {\n            return 0;\n        }\n\n        @Override\n        public List<SAActivityInstance> executeSearch(QueryOptions queryOptions) {\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/search/AbstractSearchEntityTest.java",
    "content": "/**\n * Copyright (C) 2021 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search;\n\nimport static com.github.stefanbirkner.systemlambda.SystemLambda.tapSystemOut;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.search.impl.SearchOptionsImpl;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Spy;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@ExtendWith(MockitoExtension.class)\nclass AbstractSearchEntityTest {\n\n    @Spy\n    private final MySearchEntity mySearchEntity = new MySearchEntity(new SearchOptionsImpl(0, 10));\n\n    @Test\n    void should_detect_potential_query_synchronization_issue_between_count_and_search() throws Exception {\n        // given\n        when(mySearchEntity.executeCount(any())).thenReturn(4L).thenReturn(4L);\n\n        // when\n        final String log = tapSystemOut(mySearchEntity::execute);\n\n        // then\n        assertThat(log).containsPattern(\"WARN.*Within the same transaction\");\n        verify(mySearchEntity).executeSearch(any());\n        assertThat(log).doesNotContain(\"Double checking the same query\");\n        verify(mySearchEntity, times(2)).executeCount(any());\n    }\n\n    @Test\n    void should_detect_when_search_retrieves_more_results_than_count() throws Exception {\n        // given\n        when(mySearchEntity.executeCount(any())).thenReturn(2L).thenReturn(1L);\n\n        // when\n        final String log = tapSystemOut(mySearchEntity::execute);\n\n        // then\n        verify(mySearchEntity).executeSearch(any());\n        verify(mySearchEntity, times(2)).executeCount(any());\n        assertThat(log).containsPattern(\"ERROR.*Double checking the same query\");\n    }\n\n    @Test\n    void should_not_double_check_anything_if_count_and_result_page_match() throws Exception {\n        // when\n        final String log = tapSystemOut(mySearchEntity::execute);\n\n        // then\n        verify(mySearchEntity).executeSearch(any());\n        assertThat(log).doesNotContain(\"Within the same transaction\");\n        verify(mySearchEntity).executeCount(any()); // only one execution\n        assertThat(log).doesNotContain(\"Double checking the same query\");\n    }\n\n    static class MySearchEntity extends AbstractSearchEntity<Serializable, PersistentObject> {\n\n        public MySearchEntity(SearchOptions searchOptions) {\n            super(null, searchOptions);\n        }\n\n        @Override\n        public long executeCount(QueryOptions queryOptions) {\n            return 1L; // By default, count & search return one result. In some tests, this method is overwritten by mocking\n        }\n\n        @Override\n        public List<PersistentObject> executeSearch(QueryOptions queryOptions) {\n            final ArrayList<PersistentObject> list = new ArrayList<>();\n            list.add(new TestEntity());\n            return list;\n        }\n\n        @Override\n        public List<Serializable> convertToClientObjects(List<PersistentObject> serverObjects) {\n            return null;\n        }\n\n        private static class TestEntity implements PersistentObject {\n\n            @Override\n            public long getId() {\n                return 0;\n            }\n\n            @Override\n            public void setId(long id) {\n            }\n\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/search/activity/SearchActivityInstancesTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.activity;\n\nimport static org.mockito.Mockito.doReturn;\n\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.execution.state.FlowNodeStateManager;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.descriptor.SearchActivityInstanceDescriptor;\nimport org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Julien Mege\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class SearchActivityInstancesTest {\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Mock\n    SearchEntitiesDescriptor searchEntitiesDescriptor;\n\n    @Mock\n    ActivityInstanceService activityInstanceService;\n\n    @Mock\n    FlowNodeStateManager flowNodeStateManager;\n\n    @Mock\n    SearchActivityInstanceDescriptor searchActivityInstanceDescriptor;\n\n    @Before\n    public void setUp() throws Exception {\n        doReturn(searchActivityInstanceDescriptor).when(searchEntitiesDescriptor).getSearchActivityInstanceDescriptor();\n\n    }\n\n    @Test\n    public void execute_search_with_two_activityType_filter_should_throw_exception() throws Exception {\n        //given\n        SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.HUMAN_TASK);\n        searchOptionsBuilder.filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.AUTOMATIC_TASK);\n\n        final SearchActivityInstances searchActivityInstancesTransaction = new SearchActivityInstances(\n                activityInstanceService, flowNodeStateManager,\n                searchEntitiesDescriptor.getSearchActivityInstanceDescriptor(), searchOptionsBuilder.done());\n\n        //then\n        expectedException.expect(SBonitaReadException.class);\n        expectedException.expectMessage(\n                \"Invalid query, filtering several times on 'ActivityInstanceSearchDescriptor.ACTIVITY_TYPE' is not supported.\");\n\n        //when\n        searchActivityInstancesTransaction.execute();\n\n    }\n\n    @Test\n    public void execute_search_with_one_activityType_filter_should_not_throw_exception() throws Exception {\n        //given\n        SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        searchOptionsBuilder.filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.HUMAN_TASK);\n\n        final SearchActivityInstances searchActivityInstancesTransaction = new SearchActivityInstances(\n                activityInstanceService, flowNodeStateManager,\n                searchEntitiesDescriptor.getSearchActivityInstanceDescriptor(), searchOptionsBuilder.done());\n\n        //when\n        searchActivityInstancesTransaction.execute();\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/search/descriptor/SearchArchivedHumanTaskInstanceDescriptorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstanceSearchDescriptor;\nimport org.junit.Test;\n\npublic class SearchArchivedHumanTaskInstanceDescriptorTest {\n\n    @Test\n    public void getEntityKey_should_map_root_process_instance_to_the_logical_group_2_which_is_the_root_process_instance() {\n        final SearchArchivedHumanTaskInstanceDescriptor descriptor = new SearchArchivedHumanTaskInstanceDescriptor();\n\n        final FieldDescriptor fieldDescriptor = descriptor.getEntityKeys()\n                .get(ArchivedHumanTaskInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID);\n\n        assertThat(fieldDescriptor.getValue()).isEqualTo(\"logicalGroup2\");\n    }\n\n    @Test\n    public void getEntityKey_should_map_parent_process_instance_to_the_logical_group_4_which_is_the_parent_process_instance() {\n        final SearchArchivedHumanTaskInstanceDescriptor descriptor = new SearchArchivedHumanTaskInstanceDescriptor();\n\n        final FieldDescriptor fieldDescriptor = descriptor.getEntityKeys()\n                .get(ArchivedHumanTaskInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID);\n\n        assertThat(fieldDescriptor.getValue()).isEqualTo(\"logicalGroup4\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/search/descriptor/SearchHumanTaskInstanceDescriptorTest.java",
    "content": "/**\n * Copyright (C) 2018 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor;\nimport org.junit.Test;\n\npublic class SearchHumanTaskInstanceDescriptorTest {\n\n    @Test\n    public void getEntityKey_should_map_process_instance_to_the_logical_group_2_which_is_the_root_process_instance() {\n        final SearchHumanTaskInstanceDescriptor descriptor = new SearchHumanTaskInstanceDescriptor();\n\n        final FieldDescriptor fieldDescriptor = descriptor.getEntityKeys()\n                .get(HumanTaskInstanceSearchDescriptor.PROCESS_INSTANCE_ID);\n\n        assertThat(fieldDescriptor.getValue()).isEqualTo(\"logicalGroup2\");\n    }\n\n    @Test\n    public void getEntityKey_should_map_root_process_instance_to_the_logical_group_2_which_is_the_root_process_instance() {\n        final SearchHumanTaskInstanceDescriptor descriptor = new SearchHumanTaskInstanceDescriptor();\n\n        final FieldDescriptor fieldDescriptor = descriptor.getEntityKeys()\n                .get(HumanTaskInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID);\n\n        assertThat(fieldDescriptor.getValue()).isEqualTo(\"logicalGroup2\");\n    }\n\n    @Test\n    public void getEntityKey_should_map_parent_process_instance_to_the_logical_group_4_which_is_the_parent_process_instance() {\n        final SearchHumanTaskInstanceDescriptor descriptor = new SearchHumanTaskInstanceDescriptor();\n\n        final FieldDescriptor fieldDescriptor = descriptor.getEntityKeys()\n                .get(HumanTaskInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID);\n\n        assertThat(fieldDescriptor.getValue()).isEqualTo(\"logicalGroup4\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/search/descriptor/SearchPageDescriptorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.page.SPage;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.junit.Test;\n\npublic class SearchPageDescriptorTest {\n\n    @Test\n    public void should_get_all_page_fields() throws Exception {\n        //given\n        SearchPageDescriptor searchPageDescriptor = new SearchPageDescriptor();\n\n        //when\n        final Map<Class<? extends PersistentObject>, Set<String>> allFields = searchPageDescriptor\n                .getAllFields();\n\n        //then\n        final Set<String> fields = allFields.get(SPage.class);\n        assertThat(fields).contains(\"name\", \"displayName\");\n\n    }\n\n    @Test\n    public void should_get_all_page_keys() throws Exception {\n        //given\n        SearchPageDescriptor searchPageDescriptor = new SearchPageDescriptor();\n\n        //when\n        final Map<String, FieldDescriptor> entityKeys = searchPageDescriptor.getEntityKeys();\n\n        //then\n        assertThat(entityKeys).containsKeys(\"contentType\", \"processDefinitionId\");\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/search/descriptor/SearchProcessInstanceDescriptorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.descriptor;\n\nimport static org.junit.Assert.*;\n\nimport java.io.Serializable;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance;\nimport org.bonitasoft.engine.exception.IncorrectParameterException;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.persistence.search.FilterOperationType;\nimport org.bonitasoft.engine.search.SearchFilterOperation;\nimport org.bonitasoft.engine.search.impl.SearchFilter;\nimport org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Celine Souchet\n * @version 6.4.0\n * @since 6.4.0\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class SearchProcessInstanceDescriptorTest {\n\n    private SearchProcessInstanceDescriptor searchProcessInstanceDescriptor;\n\n    @Before\n    public void before() {\n        searchProcessInstanceDescriptor = new SearchProcessInstanceDescriptor();\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor#getEntityKeys()}.\n     */\n    @Test\n    public final void getEntityKeys_should_return_map_containing_process_instance_name() {\n        // When\n        final Map<String, FieldDescriptor> entityKeys = searchProcessInstanceDescriptor.getEntityKeys();\n\n        // Then\n        final FieldDescriptor fieldDescriptor = entityKeys.get(ProcessInstanceSearchDescriptor.NAME);\n        assertNotNull(fieldDescriptor);\n        assertEquals(SProcessInstance.class, fieldDescriptor.getPersistentClass());\n        assertEquals(\"name\", fieldDescriptor.getValue());\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor#getEntityKeys()}.\n     */\n    @Test\n    public final void getEntityKeys_should_return_map_containing_process_definition_id() {\n        // When\n        final Map<String, FieldDescriptor> entityKeys = searchProcessInstanceDescriptor.getEntityKeys();\n\n        // Then\n        final FieldDescriptor fieldDescriptor = entityKeys.get(ProcessInstanceSearchDescriptor.PROCESS_DEFINITION_ID);\n        assertNotNull(fieldDescriptor);\n        assertEquals(SProcessInstance.class, fieldDescriptor.getPersistentClass());\n        assertEquals(\"processDefinitionId\", fieldDescriptor.getValue());\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor#getEntityKeys()}.\n     */\n    @Test\n    public final void getEntityKeys_should_return_map_containing_last_update_date() {\n        // When\n        final Map<String, FieldDescriptor> entityKeys = searchProcessInstanceDescriptor.getEntityKeys();\n\n        // Then\n        final FieldDescriptor fieldDescriptor = entityKeys.get(ProcessInstanceSearchDescriptor.LAST_UPDATE);\n        assertNotNull(fieldDescriptor);\n        assertEquals(SProcessInstance.class, fieldDescriptor.getPersistentClass());\n        assertEquals(\"lastUpdate\", fieldDescriptor.getValue());\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor#getEntityKeys()}.\n     */\n    @Test\n    public final void getEntityKeys_should_return_map_containing_start_date() {\n        // When\n        final Map<String, FieldDescriptor> entityKeys = searchProcessInstanceDescriptor.getEntityKeys();\n\n        // Then\n        final FieldDescriptor fieldDescriptor = entityKeys.get(ProcessInstanceSearchDescriptor.START_DATE);\n        assertNotNull(fieldDescriptor);\n        assertEquals(SProcessInstance.class, fieldDescriptor.getPersistentClass());\n        assertEquals(\"startDate\", fieldDescriptor.getValue());\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor#getEntityKeys()}.\n     */\n    @Test\n    public final void getEntityKeys_should_return_map_containing_end_date() {\n        // When\n        final Map<String, FieldDescriptor> entityKeys = searchProcessInstanceDescriptor.getEntityKeys();\n\n        // Then\n        final FieldDescriptor fieldDescriptor = entityKeys.get(ProcessInstanceSearchDescriptor.END_DATE);\n        assertNotNull(fieldDescriptor);\n        assertEquals(SProcessInstance.class, fieldDescriptor.getPersistentClass());\n        assertEquals(\"endDate\", fieldDescriptor.getValue());\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor#getEntityKeys()}.\n     */\n    @Test\n    public final void getEntityKeys_should_return_map_containing_state_id() {\n        // When\n        final Map<String, FieldDescriptor> entityKeys = searchProcessInstanceDescriptor.getEntityKeys();\n\n        // Then\n        final FieldDescriptor fieldDescriptor = entityKeys.get(ProcessInstanceSearchDescriptor.STATE_ID);\n        assertNotNull(fieldDescriptor);\n        assertEquals(SProcessInstance.class, fieldDescriptor.getPersistentClass());\n        assertEquals(\"stateId\", fieldDescriptor.getValue());\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor#getEntityKeys()}.\n     */\n    @Test\n    public final void getEntityKeys_should_return_map_containing_state_name() {\n        // When\n        final Map<String, FieldDescriptor> entityKeys = searchProcessInstanceDescriptor.getEntityKeys();\n\n        // Then\n        final FieldDescriptor fieldDescriptor = entityKeys.get(ProcessInstanceSearchDescriptor.STATE_NAME);\n        assertNotNull(fieldDescriptor);\n        assertEquals(SProcessInstance.class, fieldDescriptor.getPersistentClass());\n        assertEquals(\"stateId\", fieldDescriptor.getValue());\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor#getEntityKeys()}.\n     */\n    @Test\n    public final void getEntityKeys_should_return_map_containing_process_instance_id() {\n        // When\n        final Map<String, FieldDescriptor> entityKeys = searchProcessInstanceDescriptor.getEntityKeys();\n\n        // Then\n        final FieldDescriptor fieldDescriptor = entityKeys.get(ProcessInstanceSearchDescriptor.ID);\n        assertNotNull(fieldDescriptor);\n        assertEquals(SProcessInstance.class, fieldDescriptor.getPersistentClass());\n        assertEquals(\"id\", fieldDescriptor.getValue());\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor#getEntityKeys()}.\n     */\n    @Test\n    public final void getEntityKeys_should_return_map_containing_started_by() {\n        // When\n        final Map<String, FieldDescriptor> entityKeys = searchProcessInstanceDescriptor.getEntityKeys();\n\n        // Then\n        final FieldDescriptor fieldDescriptor = entityKeys.get(ProcessInstanceSearchDescriptor.STARTED_BY);\n        assertNotNull(fieldDescriptor);\n        assertEquals(SProcessInstance.class, fieldDescriptor.getPersistentClass());\n        assertEquals(\"startedBy\", fieldDescriptor.getValue());\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor#getEntityKeys()}.\n     */\n    @Test\n    public final void getEntityKeys_should_return_map_containing_caller_id() {\n        // When\n        final Map<String, FieldDescriptor> entityKeys = searchProcessInstanceDescriptor.getEntityKeys();\n\n        // Then\n        final FieldDescriptor fieldDescriptor = entityKeys.get(ProcessInstanceSearchDescriptor.CALLER_ID);\n        assertNotNull(fieldDescriptor);\n        assertEquals(SProcessInstance.class, fieldDescriptor.getPersistentClass());\n        assertEquals(\"callerId\", fieldDescriptor.getValue());\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor#getEntityKeys()}.\n     */\n    @Test\n    public final void getEntityKeys_should_return_map_containing_user_id() {\n        // When\n        final Map<String, FieldDescriptor> entityKeys = searchProcessInstanceDescriptor.getEntityKeys();\n\n        // Then\n        final FieldDescriptor fieldDescriptor = entityKeys\n                .get(ProcessInstanceSearchDescriptor.PROCESS_SUPERVISOR_USER_ID);\n        assertNotNull(fieldDescriptor);\n        assertEquals(SProcessSupervisor.class, fieldDescriptor.getPersistentClass());\n        assertEquals(\"userId\", fieldDescriptor.getValue());\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor#getEntityKeys()}.\n     */\n    @Test\n    public final void getEntityKeys_should_return_map_containing_group_id() {\n        // When\n        final Map<String, FieldDescriptor> entityKeys = searchProcessInstanceDescriptor.getEntityKeys();\n\n        // Then\n        final FieldDescriptor fieldDescriptor = entityKeys\n                .get(ProcessInstanceSearchDescriptor.PROCESS_SUPERVISOR_GROUP_ID);\n        assertNotNull(fieldDescriptor);\n        assertEquals(SProcessSupervisor.class, fieldDescriptor.getPersistentClass());\n        assertEquals(\"groupId\", fieldDescriptor.getValue());\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor#getEntityKeys()}.\n     */\n    @Test\n    public final void getEntityKeys_should_return_map_containing_role_id() {\n        // When\n        final Map<String, FieldDescriptor> entityKeys = searchProcessInstanceDescriptor.getEntityKeys();\n\n        // Then\n        final FieldDescriptor fieldDescriptor = entityKeys\n                .get(ProcessInstanceSearchDescriptor.PROCESS_SUPERVISOR_ROLE_ID);\n        assertNotNull(fieldDescriptor);\n        assertEquals(SProcessSupervisor.class, fieldDescriptor.getPersistentClass());\n        assertEquals(\"roleId\", fieldDescriptor.getValue());\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor#getEntityKeys()}.\n     */\n    @Test\n    public final void getEntityKeys_should_return_map_containing_assignee_id() {\n        // When\n        final Map<String, FieldDescriptor> entityKeys = searchProcessInstanceDescriptor.getEntityKeys();\n\n        // Then\n        final FieldDescriptor fieldDescriptor = entityKeys.get(ProcessInstanceSearchDescriptor.ASSIGNEE_ID);\n        assertNotNull(fieldDescriptor);\n        assertEquals(SUserTaskInstance.class, fieldDescriptor.getPersistentClass());\n        assertEquals(\"assigneeId\", fieldDescriptor.getValue());\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor#getAllFields()}.\n     */\n    @Test\n    public final void getAllFields_should_return_map_containing_process_instance_name() {\n        // When\n        final Map<Class<? extends PersistentObject>, Set<String>> allFields = searchProcessInstanceDescriptor\n                .getAllFields();\n\n        // Then\n        final Set<String> fieldDescriptor = allFields.get(SProcessInstance.class);\n        assertNotNull(fieldDescriptor);\n        assertTrue(fieldDescriptor.contains(\"name\"));\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor#convertFilterValue(java.lang.String, java.io.Serializable)}.\n     */\n    @Test\n    public final void convertFilterValue_should_convert_state_name_to_state_id_if_state_name_is_ProcessInstanceState() {\n        // When\n        final Serializable convertFilterValue = searchProcessInstanceDescriptor.convertFilterValue(\n                ProcessInstanceSearchDescriptor.STATE_NAME,\n                ProcessInstanceState.ERROR);\n\n        // Then\n        assertEquals(7, convertFilterValue);\n    }\n\n    @Test\n    public final void convertFilterValue_should_convert_state_name_to_state_id_if_state_name_is_String() {\n        // When\n        final Serializable convertFilterValue = searchProcessInstanceDescriptor.convertFilterValue(\n                ProcessInstanceSearchDescriptor.STATE_NAME,\n                ProcessInstanceState.ABORTED.name());\n\n        // Then\n        assertEquals(4, convertFilterValue);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public final void convertFilterValue_should_throw_exception_if_state_name_is_another_type() {\n        // When\n        searchProcessInstanceDescriptor.convertFilterValue(ProcessInstanceSearchDescriptor.STATE_NAME, 6);\n    }\n\n    @Test\n    public final void convertFilterValue_should_not_convert_if_field_is_not_state_name() {\n        // When\n        final Serializable convertFilterValue = searchProcessInstanceDescriptor\n                .convertFilterValue(ProcessInstanceSearchDescriptor.ID, 6);\n\n        // Then\n        assertEquals(6, convertFilterValue);\n    }\n\n    /**\n     * Test method for\n     * {@link\n     * org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor#constructFilterOption(org.bonitasoft.engine.search.impl.SearchFilter,\n     * FieldDescriptor)\n     */\n    @Test\n    public final void constructFilterOption_should_convert_SearchFilter_to_FilterOption_when_SearchFilterOperation_is_EQUALS() {\n        //Given\n        final String fieldName = \"name\";\n        final SearchFilter filter = new SearchFilter(fieldName, SearchFilterOperation.EQUALS, \"plop\");\n        final FieldDescriptor fieldDescriptor = new FieldDescriptor(SProcessInstance.class, fieldName);\n\n        // When\n        final FilterOption filterOption = searchProcessInstanceDescriptor.constructFilterOption(filter,\n                fieldDescriptor);\n\n        // Then\n        assertEquals(FilterOperationType.EQUALS, filterOption.getFilterOperationType());\n        assertEquals(SProcessInstance.class, filterOption.getPersistentClass());\n        assertEquals(fieldName, filterOption.getFieldName());\n        assertEquals(\"plop\", filterOption.getValue());\n    }\n\n    /**\n     * Test method for\n     * {@link\n     * org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor#constructFilterOption(org.bonitasoft.engine.search.impl.SearchFilter,\n     * FieldDescriptor)\n     */\n    @Test\n    public final void constructFilterOption_should_convert_SearchFilter_to_FilterOption_when_SearchFilterOperation_is_DIFFERENT() {\n        //Given\n        final String fieldName = \"name\";\n        final SearchFilter filter = new SearchFilter(fieldName, SearchFilterOperation.DIFFERENT, \"plop\");\n        final FieldDescriptor fieldDescriptor = new FieldDescriptor(SProcessInstance.class, fieldName);\n\n        // When\n        final FilterOption filterOption = searchProcessInstanceDescriptor.constructFilterOption(filter,\n                fieldDescriptor);\n\n        // Then\n        assertEquals(FilterOperationType.DIFFERENT, filterOption.getFilterOperationType());\n        assertEquals(SProcessInstance.class, filterOption.getPersistentClass());\n        assertEquals(fieldName, filterOption.getFieldName());\n        assertEquals(\"plop\", filterOption.getValue());\n    }\n\n    /**\n     * Test method for\n     * {@link\n     * org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor#constructFilterOption(org.bonitasoft.engine.search.impl.SearchFilter,\n     * FieldDescriptor)\n     */\n    @Test\n    public final void constructFilterOption_should_convert_SearchFilter_to_FilterOption_when_SearchFilterOperation_is_GREATER_OR_EQUAL() {\n        //Given\n        final String fieldName = \"name\";\n        final SearchFilter filter = new SearchFilter(fieldName, SearchFilterOperation.GREATER_OR_EQUAL, \"plop\");\n        final FieldDescriptor fieldDescriptor = new FieldDescriptor(SProcessInstance.class, fieldName);\n\n        // When\n        final FilterOption filterOption = searchProcessInstanceDescriptor.constructFilterOption(filter,\n                fieldDescriptor);\n\n        // Then\n        assertEquals(FilterOperationType.GREATER_OR_EQUALS, filterOption.getFilterOperationType());\n        assertEquals(SProcessInstance.class, filterOption.getPersistentClass());\n        assertEquals(fieldName, filterOption.getFieldName());\n        assertEquals(\"plop\", filterOption.getValue());\n    }\n\n    /**\n     * Test method for\n     * {@link\n     * org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor#constructFilterOption(org.bonitasoft.engine.search.impl.SearchFilter,\n     * FieldDescriptor)\n     */\n    @Test\n    public final void constructFilterOption_should_convert_SearchFilter_to_FilterOption_when_SearchFilterOperation_is_GREATER_THAN() {\n        //Given\n        final String fieldName = \"name\";\n        final SearchFilter filter = new SearchFilter(fieldName, SearchFilterOperation.GREATER_THAN, \"plop\");\n        final FieldDescriptor fieldDescriptor = new FieldDescriptor(SProcessInstance.class, fieldName);\n\n        // When\n        final FilterOption filterOption = searchProcessInstanceDescriptor.constructFilterOption(filter,\n                fieldDescriptor);\n\n        // Then\n        assertEquals(FilterOperationType.GREATER, filterOption.getFilterOperationType());\n        assertEquals(SProcessInstance.class, filterOption.getPersistentClass());\n        assertEquals(fieldName, filterOption.getFieldName());\n        assertEquals(\"plop\", filterOption.getValue());\n    }\n\n    /**\n     * Test method for\n     * {@link\n     * org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor#constructFilterOption(org.bonitasoft.engine.search.impl.SearchFilter,\n     * FieldDescriptor)\n     */\n    @Test\n    public final void constructFilterOption_should_convert_SearchFilter_to_FilterOption_when_SearchFilterOperation_is_LESS_OR_EQUAL() {\n        //Given\n        final String fieldName = \"name\";\n        final SearchFilter filter = new SearchFilter(fieldName, SearchFilterOperation.LESS_OR_EQUAL, \"plop\");\n        final FieldDescriptor fieldDescriptor = new FieldDescriptor(SProcessInstance.class, fieldName);\n\n        // When\n        final FilterOption filterOption = searchProcessInstanceDescriptor.constructFilterOption(filter,\n                fieldDescriptor);\n\n        // Then\n        assertEquals(FilterOperationType.LESS_OR_EQUALS, filterOption.getFilterOperationType());\n        assertEquals(SProcessInstance.class, filterOption.getPersistentClass());\n        assertEquals(fieldName, filterOption.getFieldName());\n        assertEquals(\"plop\", filterOption.getValue());\n    }\n\n    /**\n     * Test method for\n     * {@link\n     * org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor#constructFilterOption(org.bonitasoft.engine.search.impl.SearchFilter,\n     * FieldDescriptor)\n     */\n    @Test\n    public final void constructFilterOption_should_convert_SearchFilter_to_FilterOption_when_SearchFilterOperation_is_LESS_THAN() {\n        //Given\n        final String fieldName = \"name\";\n        final SearchFilter filter = new SearchFilter(fieldName, SearchFilterOperation.LESS_THAN, \"plop\");\n        final FieldDescriptor fieldDescriptor = new FieldDescriptor(SProcessInstance.class, fieldName);\n\n        // When\n        final FilterOption filterOption = searchProcessInstanceDescriptor.constructFilterOption(filter,\n                fieldDescriptor);\n\n        // Then\n        assertEquals(FilterOperationType.LESS, filterOption.getFilterOperationType());\n        assertEquals(SProcessInstance.class, filterOption.getPersistentClass());\n        assertEquals(fieldName, filterOption.getFieldName());\n        assertEquals(\"plop\", filterOption.getValue());\n    }\n\n    /**\n     * Test method for\n     * {@link\n     * org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor#constructFilterOption(org.bonitasoft.engine.search.impl.SearchFilter,\n     * FieldDescriptor)\n     */\n    @Test\n    public final void constructFilterOption_should_convert_SearchFilter_to_FilterOption_when_SearchFilterOperation_is_BETWEEN() {\n        //Given\n        final String fieldName = \"id\";\n        final long from = 697L;\n        final long to = 9633L;\n        final SearchFilter filter = new SearchFilter(fieldName, from, to);\n        final FieldDescriptor fieldDescriptor = new FieldDescriptor(SProcessInstance.class, fieldName);\n\n        // When\n        final FilterOption filterOption = searchProcessInstanceDescriptor.constructFilterOption(filter,\n                fieldDescriptor);\n\n        // Then\n        assertEquals(FilterOperationType.BETWEEN, filterOption.getFilterOperationType());\n        assertEquals(SProcessInstance.class, filterOption.getPersistentClass());\n        assertEquals(fieldName, filterOption.getFieldName());\n        assertEquals(from, filterOption.getFrom());\n        assertEquals(to, filterOption.getTo());\n        assertNull(filterOption.getValue());\n    }\n\n    /**\n     * Test method for\n     * {@link\n     * org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor#constructFilterOption(org.bonitasoft.engine.search.impl.SearchFilter,\n     * FieldDescriptor)\n     */\n    @Test\n    public final void constructFilterOption_should_convert_SearchFilter_to_FilterOption_when_SearchFilterOperation_is_AND()\n            throws IncorrectParameterException {\n        //Given\n        final SearchFilter filter = new SearchFilter(SearchFilterOperation.AND);\n\n        // When\n        final FilterOption filterOption = searchProcessInstanceDescriptor.constructFilterOption(filter, null);\n\n        // Then\n        assertEquals(FilterOperationType.AND, filterOption.getFilterOperationType());\n        assertNull(filterOption.getPersistentClass());\n        assertNull(filterOption.getFieldName());\n        assertNull(filterOption.getValue());\n    }\n\n    /**\n     * Test method for\n     * {@link\n     * org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor#constructFilterOption(org.bonitasoft.engine.search.impl.SearchFilter,\n     * FieldDescriptor)\n     */\n    @Test\n    public final void constructFilterOption_should_convert_SearchFilter_to_FilterOption_when_SearchFilterOperation_is_OR()\n            throws IncorrectParameterException {\n        //Given\n        final SearchFilter filter = new SearchFilter(SearchFilterOperation.OR);\n\n        // When\n        final FilterOption filterOption = searchProcessInstanceDescriptor.constructFilterOption(filter, null);\n\n        // Then\n        assertEquals(FilterOperationType.OR, filterOption.getFilterOperationType());\n        assertNull(filterOption.getPersistentClass());\n        assertNull(filterOption.getFieldName());\n        assertNull(filterOption.getValue());\n    }\n\n    /**\n     * Test method for\n     * {@link\n     * org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor#constructFilterOption(org.bonitasoft.engine.search.impl.SearchFilter,\n     * FieldDescriptor)\n     */\n    @Test\n    public final void constructFilterOption_should_convert_SearchFilter_to_FilterOption_when_SearchFilterOperation_is_L_PARENTHESIS()\n            throws IncorrectParameterException {\n        //Given\n        final SearchFilter filter = new SearchFilter(SearchFilterOperation.L_PARENTHESIS);\n\n        // When\n        final FilterOption filterOption = searchProcessInstanceDescriptor.constructFilterOption(filter, null);\n\n        // Then\n        assertEquals(FilterOperationType.L_PARENTHESIS, filterOption.getFilterOperationType());\n        assertNull(filterOption.getPersistentClass());\n        assertNull(filterOption.getFieldName());\n        assertNull(filterOption.getValue());\n    }\n\n    /**\n     * Test method for\n     * {@link\n     * org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor#constructFilterOption(org.bonitasoft.engine.search.impl.SearchFilter,\n     * FieldDescriptor)\n     */\n    @Test\n    public final void constructFilterOption_should_convert_SearchFilter_to_FilterOption_when_SearchFilterOperation_is_R_PARENTHESIS()\n            throws IncorrectParameterException {\n        //Given\n        final SearchFilter filter = new SearchFilter(SearchFilterOperation.R_PARENTHESIS);\n\n        // When\n        final FilterOption filterOption = searchProcessInstanceDescriptor.constructFilterOption(filter, null);\n\n        // Then\n        assertEquals(FilterOperationType.R_PARENTHESIS, filterOption.getFilterOperationType());\n        assertNull(filterOption.getPersistentClass());\n        assertNull(filterOption.getFieldName());\n        assertNull(filterOption.getValue());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/search/identity/SearchCustomUserInfoValuesTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.search.identity;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.identity.CustomUserInfoValue;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoValue;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Vincent Elcrin\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class SearchCustomUserInfoValuesTest {\n\n    @Mock\n    private IdentityService service;\n\n    @Mock\n    private SearchEntityDescriptor descriptor;\n\n    @Mock\n    private SearchOptions options;\n\n    @Test\n    public void should_return_a_list_of_CustomUserInfoValues() {\n        SearchCustomUserInfoValues search = new SearchCustomUserInfoValues(service, descriptor, options);\n\n        List<CustomUserInfoValue> result = search.convertToClientObjects(Arrays.asList(\n                SCustomUserInfoValue.builder().definitionId(3).build(),\n                SCustomUserInfoValue.builder().definitionId(4).build()));\n\n        assertThat(result.get(0).getDefinitionId()).isEqualTo(3L);\n        assertThat(result.get(1).getDefinitionId()).isEqualTo(4L);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/service/FormRequiredAnalyzerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.anyList;\nimport static org.mockito.ArgumentMatchers.nullable;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Collections;\n\nimport org.bonitasoft.engine.bpm.contract.Type;\nimport org.bonitasoft.engine.bpm.contract.impl.ContractDefinitionImpl;\nimport org.bonitasoft.engine.bpm.contract.impl.InputDefinitionImpl;\nimport org.bonitasoft.engine.bpm.flownode.ActivityDefinition;\nimport org.bonitasoft.engine.bpm.flownode.FlowElementContainerDefinition;\nimport org.bonitasoft.engine.bpm.flownode.UserTaskDefinition;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.UserTaskDefinitionImpl;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.core.form.SFormMapping;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * author Emmanuel Duchastenier\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class FormRequiredAnalyzerTest {\n\n    @Mock\n    private ProcessDefinitionService processDefinitionService;\n\n    @InjectMocks\n    private FormRequiredAnalyzer formRequiredAnalyzer;\n\n    @Test\n    public void findActivityWithNameShouldReturnNullForNullTaskName() throws Exception {\n        final UserTaskDefinition userTaskDefinition = formRequiredAnalyzer.findActivityWithName(\n                Collections.<ActivityDefinition> singletonList(new UserTaskDefinitionImpl(\"tarea\", \"someActorName\")),\n                null);\n        assertThat(userTaskDefinition).isNull();\n    }\n\n    @Test\n    public void findActivityWithNameShouldReturnNullForNullActivityList() throws Exception {\n        final UserTaskDefinition userTaskDefinition = formRequiredAnalyzer.findActivityWithName(null, \"tarea\");\n        assertThat(userTaskDefinition).isNull();\n    }\n\n    @Test\n    public void findActivityWithNameShouldReturnNullForTaskNotFoundForGivenName() throws Exception {\n        final UserTaskDefinitionImpl expectedUserTaskDefinition = new UserTaskDefinitionImpl(\"tarea\", \"someActorName\");\n        final UserTaskDefinition userTaskDefinition = formRequiredAnalyzer.findActivityWithName(\n                Collections.<ActivityDefinition> singletonList(expectedUserTaskDefinition), \"some other name\");\n        assertThat(userTaskDefinition).isNull();\n    }\n\n    @Test\n    public void findActivityWithNameShouldReturnUserTaskDefinitionForFoundTask() throws Exception {\n        final UserTaskDefinitionImpl expectedUserTaskDefinition = new UserTaskDefinitionImpl(\"tarea\", \"someActorName\");\n        final UserTaskDefinition userTaskDefinition = formRequiredAnalyzer.findActivityWithName(\n                Collections.<ActivityDefinition> singletonList(expectedUserTaskDefinition), \"tarea\");\n        assertThat(userTaskDefinition).isEqualTo(expectedUserTaskDefinition);\n    }\n\n    @Test\n    public void isFormRequiredShouldBeFalseForOverview() throws Exception {\n        final boolean formRequired = formRequiredAnalyzer\n                .isFormRequired(new SFormMapping(1L, SFormMapping.TYPE_PROCESS_OVERVIEW, null, \"\"));\n        assertThat(formRequired).isFalse();\n    }\n\n    @Test\n    public void isFormRequiredShouldBeFalseIfExceptionOccurs() throws Exception {\n        doThrow(SProcessDefinitionNotFoundException.class).when(processDefinitionService)\n                .getDesignProcessDefinition(157L);\n        final boolean formRequired = formRequiredAnalyzer\n                .isFormRequired(new SFormMapping(157L, SFormMapping.TYPE_PROCESS_START, null, \"\"));\n        assertThat(formRequired).isFalse();\n    }\n\n    @Test\n    public void isFormRequiredShouldBeFalseIfNoContractOnProcessStart() throws Exception {\n        final DesignProcessDefinition definition = mock(DesignProcessDefinition.class);\n        doReturn(null).when(definition).getContract();\n        doReturn(definition).when(processDefinitionService).getDesignProcessDefinition(111L);\n        final boolean formRequired = formRequiredAnalyzer\n                .isFormRequired(new SFormMapping(111L, SFormMapping.TYPE_PROCESS_START, null, \"\"));\n        assertThat(formRequired).isFalse();\n    }\n\n    @Test\n    public void isFormRequiredShouldBeTrueIfNonEmptyContractFoundOnProcessStart() throws Exception {\n        final DesignProcessDefinition definition = mock(DesignProcessDefinition.class);\n        final ContractDefinitionImpl contractDefinition = new ContractDefinitionImpl();\n        contractDefinition.addInput(new InputDefinitionImpl(\"input1\", \"theInput\"));\n        doReturn(contractDefinition).when(definition).getContract();\n        doReturn(definition).when(processDefinitionService).getDesignProcessDefinition(111L);\n        final boolean formRequired = formRequiredAnalyzer\n                .isFormRequired(new SFormMapping(111L, SFormMapping.TYPE_PROCESS_START, null, \"\"));\n        assertThat(formRequired).isTrue();\n    }\n\n    @Test\n    public void isFormRequiredShouldBeFalseIfEmptyContractFoundOnProcessStart() throws Exception {\n        final DesignProcessDefinition definition = mock(DesignProcessDefinition.class);\n        final ContractDefinitionImpl contractDefinition = new ContractDefinitionImpl();\n        doReturn(contractDefinition).when(definition).getContract();\n        doReturn(definition).when(processDefinitionService).getDesignProcessDefinition(111L);\n        final boolean formRequired = formRequiredAnalyzer\n                .isFormRequired(new SFormMapping(111L, SFormMapping.TYPE_PROCESS_START, null, \"\"));\n        assertThat(formRequired).isFalse();\n    }\n\n    @Test\n    public void isFormRequiredShouldBeTrueIfNonEmptyContractFoundOnTask() throws Exception {\n        final DesignProcessDefinition definition = mock(DesignProcessDefinition.class);\n        doReturn(mock(FlowElementContainerDefinition.class)).when(definition).getFlowElementContainer();\n        doReturn(definition).when(processDefinitionService).getDesignProcessDefinition(111L);\n\n        final FormRequiredAnalyzer spy = spy(formRequiredAnalyzer);\n        final UserTaskDefinition userTaskDefinition = mock(UserTaskDefinition.class);\n        final ContractDefinitionImpl contractDefinition = new ContractDefinitionImpl();\n        contractDefinition.addInput(new InputDefinitionImpl(\"name\", Type.BOOLEAN, \"description\"));\n        doReturn(contractDefinition).when(userTaskDefinition).getContract();\n\n        doReturn(userTaskDefinition).when(spy).findActivityWithName(anyList(), nullable(String.class));\n\n        final boolean formRequired = spy.isFormRequired(new SFormMapping(111L, SFormMapping.TYPE_TASK, null, \"\"));\n        assertThat(formRequired).isTrue();\n    }\n\n    @Test\n    public void isFormRequiredShouldBeFalseIfEmptyContractFoundOnTask() throws Exception {\n        final DesignProcessDefinition definition = mock(DesignProcessDefinition.class);\n        doReturn(mock(FlowElementContainerDefinition.class)).when(definition).getFlowElementContainer();\n        doReturn(definition).when(processDefinitionService).getDesignProcessDefinition(111L);\n\n        final FormRequiredAnalyzer spy = spy(formRequiredAnalyzer);\n\n        final boolean formRequired = spy.isFormRequired(new SFormMapping(111L, SFormMapping.TYPE_TASK, null, \"\"));\n        assertThat(formRequired).isFalse();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/service/ModelConvertorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.service.ModelConvertor.toUserMembership;\nimport static org.bonitasoft.engine.tenant.TenantResourceState.INSTALLED;\nimport static org.bonitasoft.engine.tenant.TenantResourceType.BDM;\nimport static org.junit.jupiter.api.Assertions.*;\nimport static org.mockito.Mockito.*;\n\nimport java.time.OffsetDateTime;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.contract.ConstraintDefinition;\nimport org.bonitasoft.engine.bpm.contract.ContractDefinition;\nimport org.bonitasoft.engine.bpm.contract.InputDefinition;\nimport org.bonitasoft.engine.bpm.contract.Type;\nimport org.bonitasoft.engine.bpm.contract.impl.ConstraintDefinitionImpl;\nimport org.bonitasoft.engine.bpm.contract.impl.InputDefinitionImpl;\nimport org.bonitasoft.engine.bpm.data.DataInstance;\nimport org.bonitasoft.engine.bpm.document.Document;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedUserTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.TimerEventTriggerInstance;\nimport org.bonitasoft.engine.bpm.flownode.UserTaskInstance;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.business.data.BusinessDataReference;\nimport org.bonitasoft.engine.business.data.impl.MultipleBusinessDataReferenceImpl;\nimport org.bonitasoft.engine.business.data.impl.SimpleBusinessDataReferenceImpl;\nimport org.bonitasoft.engine.core.document.api.DocumentService;\nimport org.bonitasoft.engine.core.document.model.SMappedDocument;\nimport org.bonitasoft.engine.core.form.SFormMapping;\nimport org.bonitasoft.engine.core.process.definition.model.SConstraintDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SContractDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SInputDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SConstraintDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SContractDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SInputDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SProcessDefinitionImpl;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.model.SGatewayInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.STaskPriority;\nimport org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAGatewayInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAReceiveTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SASendTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\nimport org.bonitasoft.engine.execution.state.CompletedActivityState;\nimport org.bonitasoft.engine.execution.state.FlowNodeStateManager;\nimport org.bonitasoft.engine.form.FormMapping;\nimport org.bonitasoft.engine.form.FormMappingType;\nimport org.bonitasoft.engine.identity.CustomUserInfoValue;\nimport org.bonitasoft.engine.identity.impl.CustomUserInfoDefinitionImpl;\nimport org.bonitasoft.engine.identity.impl.UserMembershipImpl;\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoValue;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.identity.model.SUserMembership;\nimport org.bonitasoft.engine.page.SPageMapping;\nimport org.bonitasoft.engine.resources.STenantResourceLight;\nimport org.bonitasoft.engine.resources.STenantResourceState;\nimport org.bonitasoft.engine.resources.TenantResourceType;\nimport org.bonitasoft.engine.tenant.TenantResource;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\n@ExtendWith(MockitoExtension.class)\nclass ModelConvertorTest {\n\n    @Mock\n    private FlowNodeStateManager manager;\n\n    @Mock\n    private FormRequiredAnalyzer formRequiredAnalyzer;\n\n    @Test\n    void convertDataInstanceIsTransient() {\n        final SDataInstance sDataInstance = mock(SDataInstance.class);\n        when(sDataInstance.getClassName()).thenReturn(Integer.class.getName());\n        when(sDataInstance.isTransientData()).thenReturn(true);\n\n        final DataInstance dataInstance = ModelConvertor.toDataInstance(sDataInstance);\n        assertTrue(dataInstance.isTransientData());\n    }\n\n    @Test\n    void convertDataInstanceIsNotTransient() {\n        final SDataInstance sDataInstance = mock(SDataInstance.class);\n        when(sDataInstance.getClassName()).thenReturn(Integer.class.getName());\n        when(sDataInstance.isTransientData()).thenReturn(false);\n\n        final DataInstance dataInstance = ModelConvertor.toDataInstance(sDataInstance);\n        assertFalse(dataInstance.isTransientData());\n    }\n\n    @Test\n    void getProcessInstanceState_conversionOnUnknownStateShouldThrowException() {\n        assertThrows(IllegalArgumentException.class, () -> ModelConvertor.getProcessInstanceState(\"un_known_state\"));;\n    }\n\n    @Test\n    void getProcessInstanceState_conversionOnNullStateShouldThrowException() {\n        assertThrows(IllegalArgumentException.class, () -> ModelConvertor.getProcessInstanceState(null));\n    }\n\n    @Test\n    void convertSUserToUserDoesntShowPassword() {\n        final SUser sUser = mock(SUser.class);\n        ModelConvertor.toUser(sUser);\n        verify(sUser, never()).getPassword();\n    }\n\n    private DocumentService createdMockedDocumentService() {\n        final DocumentService documentService = mock(DocumentService.class);\n        lenient().doReturn(\"url?fileName=document&contentStorageId=123\").when(documentService).generateDocumentURL(\n                \"document\",\n                \"123\");\n        return documentService;\n    }\n\n    private SMappedDocument createMockedDocument() {\n        final SMappedDocument documentMapping = mock(SMappedDocument.class);\n        doReturn(\"document\").when(documentMapping).getFileName();\n        doReturn(123l).when(documentMapping).getDocumentId();\n        lenient().doReturn(\"whateverUrl\").when(documentMapping).getUrl();\n        return documentMapping;\n    }\n\n    @Test\n    void toArchivedUserTaskInstance_should_return_the_right_identifiers() {\n        final SAUserTaskInstance sInstance = new SAUserTaskInstance();\n        sInstance.setRootContainerId(1L);\n        sInstance.setParentContainerId(2L);\n        sInstance.setLogicalGroup(0, 456789456798L);\n        sInstance.setLogicalGroup(1, 1L);\n        sInstance.setLogicalGroup(2, 456L);\n        sInstance.setLogicalGroup(3, 2L);\n        sInstance.setStateId(5);\n        final long claimedDate = System.currentTimeMillis();\n        sInstance.setClaimedDate(claimedDate);\n        sInstance.setPriority(STaskPriority.NORMAL);\n\n        when(manager.getState(5)).thenReturn(new CompletedActivityState());\n\n        final ArchivedUserTaskInstance archivedUserTaskInstance = ModelConvertor.toArchivedUserTaskInstance(sInstance,\n                manager);\n        assertThat(archivedUserTaskInstance.getProcessDefinitionId()).isEqualTo(456789456798L);\n        assertThat(archivedUserTaskInstance.getRootContainerId()).isEqualTo(1L);\n        assertThat(archivedUserTaskInstance.getParentContainerId()).isEqualTo(2L);\n        assertThat(archivedUserTaskInstance.getProcessInstanceId()).isEqualTo(2L);\n        assertThat(archivedUserTaskInstance.getParentActivityInstanceId()).isEqualTo(456L);\n        assertThat(archivedUserTaskInstance.getClaimedDate().getTime()).isEqualTo(claimedDate);\n    }\n\n    @Test\n    void getDocument_from_process_instance_and_name_should_return_a_document_with_generated_url_when_it_has_content() {\n\n        final SMappedDocument documentMapping = createMockedDocument();\n        final DocumentService documentService = createdMockedDocumentService();\n        doReturn(true).when(documentMapping).hasContent();\n\n        final Document document = ModelConvertor.toDocument(documentMapping, documentService);\n\n        assertEquals(\"url?fileName=document&contentStorageId=123\", document.getUrl());\n    }\n\n    @Test\n    void getDocument_from_process_instance_and_name_should_return_a_document_url_when_is_external_url() {\n\n        final SMappedDocument documentMapping = createMockedDocument();\n        final DocumentService documentService = createdMockedDocumentService();\n        doReturn(false).when(documentMapping).hasContent();\n\n        final Document document = ModelConvertor.toDocument(documentMapping, documentService);\n\n        assertEquals(\"whateverUrl\", document.getUrl());\n    }\n\n    @Test\n    void should_convert_server_definition_into_client_definition() {\n        final CustomUserInfoDefinitionImpl definition = ModelConvertor.convert(\n                SCustomUserInfoDefinition.builder().name(\"name\").id(1).description(\"description\").build());\n\n        assertThat(definition.getId()).isEqualTo(1L);\n        assertThat(definition.getName()).isEqualTo(\"name\");\n        assertThat(definition.getDescription()).isEqualTo(\"description\");\n    }\n\n    @Test\n    void should_convert_server_value_into_client_value() {\n        final CustomUserInfoValue value = ModelConvertor.convert(\n                SCustomUserInfoValue.builder().definitionId(2).userId(1).value(\"value\").build());\n\n        assertThat(value.getDefinitionId()).isEqualTo(2L);\n        assertThat(value.getValue()).isEqualTo(\"value\");\n        assertThat(value.getUserId()).isEqualTo(1L);\n    }\n\n    @Test\n    void should_return_null_when_trying_to_convert_a_null_value() {\n        final CustomUserInfoValue value = ModelConvertor.convert((SCustomUserInfoValue) null);\n\n        assertThat(value).isNull();\n    }\n\n    @Test\n    void toEventTriggerInstance_can_convert_TIMER_Type() {\n        // Given\n        final STimerEventTriggerInstance sTimerEventTriggerInstance = new STimerEventTriggerInstance(2,\n                \"eventInstanceName\", 69, \"jobTriggerName\");\n\n        // Then\n        final TimerEventTriggerInstance eventTriggerInstance = ModelConvertor\n                .toTimerEventTriggerInstance(sTimerEventTriggerInstance);\n\n        // When\n        assertNotNull(eventTriggerInstance);\n        assertEquals(sTimerEventTriggerInstance.getEventInstanceId(), eventTriggerInstance.getEventInstanceId());\n        assertEquals(sTimerEventTriggerInstance.getId(), eventTriggerInstance.getId());\n        assertEquals(sTimerEventTriggerInstance.getEventInstanceName(), eventTriggerInstance.getEventInstanceName());\n        assertEquals(sTimerEventTriggerInstance.getExecutionDate(), eventTriggerInstance.getExecutionDate().getTime());\n    }\n\n    @Test\n    void toTimerEventTriggerInstance_can_convert() {\n        // Given\n        final STimerEventTriggerInstance sTimerEventTriggerInstance = new STimerEventTriggerInstance(2,\n                \"eventInstanceName\", 69, \"jobTriggerName\");\n        sTimerEventTriggerInstance.setId(9);\n\n        // Then\n        final TimerEventTriggerInstance eventTriggerInstance = ModelConvertor\n                .toTimerEventTriggerInstance(sTimerEventTriggerInstance);\n\n        // When\n        assertNotNull(eventTriggerInstance);\n        assertEquals(sTimerEventTriggerInstance.getEventInstanceId(), eventTriggerInstance.getEventInstanceId());\n        assertEquals(sTimerEventTriggerInstance.getId(), eventTriggerInstance.getId());\n        assertEquals(sTimerEventTriggerInstance.getEventInstanceName(), eventTriggerInstance.getEventInstanceName());\n        assertEquals(sTimerEventTriggerInstance.getExecutionDate(), eventTriggerInstance.getExecutionDate().getTime());\n    }\n\n    @Test\n    void toProcessInstance() {\n        // Given\n        final SProcessInstance sProcessInstance = new SProcessInstance();\n        sProcessInstance.setCallerId(-1L);\n        sProcessInstance.setDescription(\"description\");\n        sProcessInstance.setEndDate(1345646L);\n        sProcessInstance.setId(98L);\n        sProcessInstance.setLastUpdate(8L);\n        sProcessInstance.setName(\"name2\");\n        sProcessInstance.setProcessDefinitionId(9L);\n        sProcessInstance.setRootProcessInstanceId(9745L);\n        sProcessInstance.setStartDate(8864564156L);\n        sProcessInstance.setStartedBy(46L);\n        sProcessInstance.setStartedBySubstitute(962L);\n        sProcessInstance.setStateId(4);\n        sProcessInstance.setStringIndex1(\"stringIndex1\");\n        sProcessInstance.setStringIndex2(\"stringIndex2\");\n        sProcessInstance.setStringIndex3(\"stringIndex3\");\n        sProcessInstance.setStringIndex4(\"stringIndex4\");\n        sProcessInstance.setStringIndex5(\"stringIndex5\");\n\n        final SProcessDefinitionImpl sProcessDefinition = new SProcessDefinitionImpl(\"name\", \"version\");\n        sProcessDefinition.setStringIndex(1, \"label1\", null);\n        sProcessDefinition.setStringIndex(2, \"label2\", null);\n        sProcessDefinition.setStringIndex(3, \"label3\", null);\n        sProcessDefinition.setStringIndex(4, \"label4\", null);\n        sProcessDefinition.setStringIndex(5, \"label5\", null);\n\n        // Then\n        final ProcessInstance processInstance = ModelConvertor\n                .toProcessInstance(sProcessDefinition, sProcessInstance);\n\n        // When\n        assertNotNull(processInstance);\n        assertEquals(sProcessInstance.getCallerId(), processInstance.getCallerId());\n        assertEquals(sProcessInstance.getId(), processInstance.getId());\n        assertEquals(sProcessInstance.getDescription(), processInstance.getDescription());\n        assertEquals(sProcessInstance.getEndDate(), processInstance.getEndDate().getTime());\n        assertEquals(sProcessInstance.getLastUpdate(), processInstance.getLastUpdate().getTime());\n        assertEquals(sProcessInstance.getName(), processInstance.getName());\n        assertEquals(sProcessInstance.getProcessDefinitionId(), processInstance.getProcessDefinitionId());\n        assertEquals(sProcessInstance.getRootProcessInstanceId(), processInstance.getRootProcessInstanceId());\n        assertEquals(sProcessInstance.getStartDate(), processInstance.getStartDate().getTime());\n        assertEquals(sProcessInstance.getStartedBy(), processInstance.getStartedBy());\n        assertEquals(sProcessInstance.getStartedBySubstitute(), processInstance.getStartedBySubstitute());\n        assertEquals(sProcessDefinition.getStringIndexLabel(1), processInstance.getStringIndexLabel(1));\n        assertEquals(sProcessInstance.getStringIndex1(), processInstance.getStringIndex1());\n        assertEquals(sProcessDefinition.getStringIndexLabel(2), processInstance.getStringIndexLabel(2));\n        assertEquals(sProcessInstance.getStringIndex2(), processInstance.getStringIndex2());\n        assertEquals(sProcessDefinition.getStringIndexLabel(3), processInstance.getStringIndexLabel(3));\n        assertEquals(sProcessInstance.getStringIndex3(), processInstance.getStringIndex3());\n        assertEquals(sProcessDefinition.getStringIndexLabel(4), processInstance.getStringIndexLabel(4));\n        assertEquals(sProcessInstance.getStringIndex4(), processInstance.getStringIndex4());\n        assertEquals(sProcessDefinition.getStringIndexLabel(5), processInstance.getStringIndexLabel(5));\n        assertEquals(sProcessInstance.getStringIndex5(), processInstance.getStringIndex5());\n    }\n\n    @Test\n    void toProcessInstance_wit_missing_process_definition() {\n        // Given\n        final SProcessInstance sProcessInstance = new SProcessInstance();\n        sProcessInstance.setCallerId(-1L);\n        sProcessInstance.setDescription(\"description\");\n        sProcessInstance.setEndDate(1345646L);\n        sProcessInstance.setId(98L);\n        sProcessInstance.setLastUpdate(8L);\n        sProcessInstance.setName(\"name2\");\n        sProcessInstance.setProcessDefinitionId(9L);\n        sProcessInstance.setRootProcessInstanceId(9745L);\n        sProcessInstance.setStartDate(8864564156L);\n        sProcessInstance.setStartedBy(46L);\n        sProcessInstance.setStartedBySubstitute(962L);\n        sProcessInstance.setStateId(4);\n        sProcessInstance.setStringIndex1(\"stringIndex1\");\n        sProcessInstance.setStringIndex2(\"stringIndex2\");\n        sProcessInstance.setStringIndex3(\"stringIndex3\");\n        sProcessInstance.setStringIndex4(\"stringIndex4\");\n        sProcessInstance.setStringIndex5(\"stringIndex5\");\n\n        // Then\n        final ProcessInstance processInstance = ModelConvertor\n                .toProcessInstance(null, sProcessInstance);\n\n        // When\n        assertNotNull(processInstance);\n        assertEquals(sProcessInstance.getCallerId(), processInstance.getCallerId());\n        assertEquals(sProcessInstance.getId(), processInstance.getId());\n        assertEquals(sProcessInstance.getDescription(), processInstance.getDescription());\n        assertEquals(sProcessInstance.getEndDate(), processInstance.getEndDate().getTime());\n        assertEquals(sProcessInstance.getLastUpdate(), processInstance.getLastUpdate().getTime());\n        assertEquals(sProcessInstance.getName(), processInstance.getName());\n        assertEquals(sProcessInstance.getProcessDefinitionId(), processInstance.getProcessDefinitionId());\n        assertEquals(sProcessInstance.getRootProcessInstanceId(), processInstance.getRootProcessInstanceId());\n        assertEquals(sProcessInstance.getStartDate(), processInstance.getStartDate().getTime());\n        assertEquals(sProcessInstance.getStartedBy(), processInstance.getStartedBy());\n        assertEquals(sProcessInstance.getStartedBySubstitute(), processInstance.getStartedBySubstitute());\n        assertNull(processInstance.getStringIndexLabel(1));\n        assertEquals(sProcessInstance.getStringIndex1(), processInstance.getStringIndex1());\n        assertNull(processInstance.getStringIndexLabel(2));\n        assertEquals(sProcessInstance.getStringIndex2(), processInstance.getStringIndex2());\n        assertNull(processInstance.getStringIndexLabel(3));\n        assertEquals(sProcessInstance.getStringIndex3(), processInstance.getStringIndex3());\n        assertNull(processInstance.getStringIndexLabel(4));\n        assertEquals(sProcessInstance.getStringIndex4(), processInstance.getStringIndex4());\n        assertNull(processInstance.getStringIndexLabel(5));\n        assertEquals(sProcessInstance.getStringIndex5(), processInstance.getStringIndex5());\n    }\n\n    @Test\n    void toArchivedProcessInstance_can_convert() {\n        // Given\n        final SAProcessInstance saProcessInstance = new SAProcessInstance();\n        saProcessInstance.setCallerId(-1L);\n        saProcessInstance.setDescription(\"description\");\n        saProcessInstance.setEndDate(1345646L);\n        saProcessInstance.setId(98L);\n        saProcessInstance.setLastUpdate(8L);\n        saProcessInstance.setName(\"name2\");\n        saProcessInstance.setProcessDefinitionId(9L);\n        saProcessInstance.setRootProcessInstanceId(9745L);\n        saProcessInstance.setSourceObjectId(741L);\n        saProcessInstance.setStartDate(8864564156L);\n        saProcessInstance.setStartedBy(46L);\n        saProcessInstance.setStartedBySubstitute(962L);\n        saProcessInstance.setStateId(4);\n        saProcessInstance.setStringIndex1(\"stringIndex1\");\n        saProcessInstance.setStringIndex2(\"stringIndex2\");\n        saProcessInstance.setStringIndex3(\"stringIndex3\");\n        saProcessInstance.setStringIndex4(\"stringIndex4\");\n        saProcessInstance.setStringIndex5(\"stringIndex5\");\n\n        final SProcessDefinitionImpl sProcessDefinition = new SProcessDefinitionImpl(\"name\", \"version\");\n        sProcessDefinition.setStringIndex(1, \"label1\", null);\n        sProcessDefinition.setStringIndex(2, \"label2\", null);\n        sProcessDefinition.setStringIndex(3, \"label3\", null);\n        sProcessDefinition.setStringIndex(4, \"label4\", null);\n        sProcessDefinition.setStringIndex(5, \"label5\", null);\n\n        // Then\n        final ArchivedProcessInstance archivedProcessInstance = ModelConvertor\n                .toArchivedProcessInstance(saProcessInstance, sProcessDefinition);\n\n        // When\n        assertNotNull(archivedProcessInstance);\n        assertEquals(saProcessInstance.getCallerId(), archivedProcessInstance.getCallerId());\n        assertEquals(saProcessInstance.getId(), archivedProcessInstance.getId());\n        assertEquals(saProcessInstance.getDescription(), archivedProcessInstance.getDescription());\n        assertEquals(saProcessInstance.getEndDate(), archivedProcessInstance.getEndDate().getTime());\n        assertEquals(saProcessInstance.getLastUpdate(), archivedProcessInstance.getLastUpdate().getTime());\n        assertEquals(saProcessInstance.getName(), archivedProcessInstance.getName());\n        assertEquals(saProcessInstance.getProcessDefinitionId(), archivedProcessInstance.getProcessDefinitionId());\n        assertEquals(saProcessInstance.getRootProcessInstanceId(), archivedProcessInstance.getRootProcessInstanceId());\n        assertEquals(saProcessInstance.getSourceObjectId(), archivedProcessInstance.getSourceObjectId());\n        assertEquals(saProcessInstance.getStartDate(), archivedProcessInstance.getStartDate().getTime());\n        assertEquals(saProcessInstance.getStartedBy(), archivedProcessInstance.getStartedBy());\n        assertEquals(saProcessInstance.getStartedBySubstitute(), archivedProcessInstance.getStartedBySubstitute());\n        assertEquals(saProcessInstance.getStateId(), archivedProcessInstance.getStateId());\n        assertEquals(sProcessDefinition.getStringIndexLabel(1), archivedProcessInstance.getStringIndexLabel(1));\n        assertEquals(saProcessInstance.getStringIndex1(), archivedProcessInstance.getStringIndexValue(1));\n        assertEquals(sProcessDefinition.getStringIndexLabel(2), archivedProcessInstance.getStringIndexLabel(2));\n        assertEquals(saProcessInstance.getStringIndex2(), archivedProcessInstance.getStringIndexValue(2));\n        assertEquals(sProcessDefinition.getStringIndexLabel(3), archivedProcessInstance.getStringIndexLabel(3));\n        assertEquals(saProcessInstance.getStringIndex3(), archivedProcessInstance.getStringIndexValue(3));\n        assertEquals(sProcessDefinition.getStringIndexLabel(4), archivedProcessInstance.getStringIndexLabel(4));\n        assertEquals(saProcessInstance.getStringIndex4(), archivedProcessInstance.getStringIndexValue(4));\n        assertEquals(sProcessDefinition.getStringIndexLabel(5), archivedProcessInstance.getStringIndexLabel(5));\n        assertEquals(saProcessInstance.getStringIndex5(), archivedProcessInstance.getStringIndexValue(5));\n    }\n\n    @Test\n    void toArchivedProcessInstance_can_convert_when_process_definition_is_missing() {\n        // Given\n        final SAProcessInstance saProcessInstance = new SAProcessInstance();\n        saProcessInstance.setCallerId(-1L);\n        saProcessInstance.setDescription(\"description\");\n        saProcessInstance.setEndDate(1345646L);\n        saProcessInstance.setId(98L);\n        saProcessInstance.setLastUpdate(8L);\n        saProcessInstance.setName(\"name2\");\n        saProcessInstance.setProcessDefinitionId(9L);\n        saProcessInstance.setRootProcessInstanceId(9745L);\n        saProcessInstance.setSourceObjectId(741L);\n        saProcessInstance.setStartDate(8864564156L);\n        saProcessInstance.setStartedBy(46L);\n        saProcessInstance.setStartedBySubstitute(962L);\n        saProcessInstance.setStateId(4);\n        saProcessInstance.setStringIndex1(\"stringIndex1\");\n        saProcessInstance.setStringIndex2(\"stringIndex2\");\n        saProcessInstance.setStringIndex3(\"stringIndex3\");\n        saProcessInstance.setStringIndex4(\"stringIndex4\");\n        saProcessInstance.setStringIndex5(\"stringIndex5\");\n\n        // Then\n        final ArchivedProcessInstance archivedProcessInstance = ModelConvertor\n                .toArchivedProcessInstance(saProcessInstance, null);\n\n        // When\n        assertNotNull(archivedProcessInstance);\n        assertEquals(saProcessInstance.getCallerId(), archivedProcessInstance.getCallerId());\n        assertEquals(saProcessInstance.getId(), archivedProcessInstance.getId());\n        assertEquals(saProcessInstance.getDescription(), archivedProcessInstance.getDescription());\n        assertEquals(saProcessInstance.getEndDate(), archivedProcessInstance.getEndDate().getTime());\n        assertEquals(saProcessInstance.getLastUpdate(), archivedProcessInstance.getLastUpdate().getTime());\n        assertEquals(saProcessInstance.getName(), archivedProcessInstance.getName());\n        assertEquals(saProcessInstance.getProcessDefinitionId(), archivedProcessInstance.getProcessDefinitionId());\n        assertEquals(saProcessInstance.getRootProcessInstanceId(), archivedProcessInstance.getRootProcessInstanceId());\n        assertEquals(saProcessInstance.getSourceObjectId(), archivedProcessInstance.getSourceObjectId());\n        assertEquals(saProcessInstance.getStartDate(), archivedProcessInstance.getStartDate().getTime());\n        assertEquals(saProcessInstance.getStartedBy(), archivedProcessInstance.getStartedBy());\n        assertEquals(saProcessInstance.getStartedBySubstitute(), archivedProcessInstance.getStartedBySubstitute());\n        assertEquals(saProcessInstance.getStateId(), archivedProcessInstance.getStateId());\n        assertNull(archivedProcessInstance.getStringIndexLabel(1));\n        assertEquals(saProcessInstance.getStringIndex1(), archivedProcessInstance.getStringIndexValue(1));\n        assertNull(archivedProcessInstance.getStringIndexLabel(2));\n        assertEquals(saProcessInstance.getStringIndex2(), archivedProcessInstance.getStringIndexValue(2));\n        assertNull(archivedProcessInstance.getStringIndexLabel(3));\n        assertEquals(saProcessInstance.getStringIndex3(), archivedProcessInstance.getStringIndexValue(3));\n        assertNull(archivedProcessInstance.getStringIndexLabel(4));\n        assertEquals(saProcessInstance.getStringIndex4(), archivedProcessInstance.getStringIndexValue(4));\n        assertNull(archivedProcessInstance.getStringIndexLabel(5));\n        assertEquals(saProcessInstance.getStringIndex5(), archivedProcessInstance.getStringIndexValue(5));\n    }\n\n    @Test\n    void toFormMapping_can_convert() {\n        // Given\n        SFormMapping sFormMapping = new SFormMapping();\n        sFormMapping.setId(555l);\n        //        sFormMapping.setForm(\"myForm1\");\n        sFormMapping.setType(FormMappingType.TASK.getId());\n        sFormMapping.setTask(\"myTask\");\n        sFormMapping.setProcessDefinitionId(666l);\n        sFormMapping.setPageMapping(new SPageMapping());\n\n        // Then\n        FormMapping formMapping = ModelConvertor.toFormMapping(sFormMapping, formRequiredAnalyzer);\n\n        // When\n        assertThat(formMapping).isNotNull();\n        assertThat(formMapping.getId()).isEqualTo(555l);\n        assertThat(formMapping.getType()).isEqualTo(FormMappingType.TASK);\n        //        assertThat(formMapping.getTarget()).isEqualTo(FormMappingTarget.LEGACY);\n        //        assertThat(formMapping.getForm()).isEqualTo(\"myForm1\");\n        assertThat(formMapping.getTask()).isEqualTo(\"myTask\");\n        assertThat(formMapping.getProcessDefinitionId()).isEqualTo(666l);\n\n    }\n\n    @Test\n    void toFormMappings_can_convert() {\n        // Given\n        SFormMapping sFormMapping = new SFormMapping();\n        sFormMapping.setId(555l);\n        //        sFormMapping.setForm(\"myForm1\");\n        sFormMapping.setType(FormMappingType.TASK.getId());\n        sFormMapping.setTask(\"myTask\");\n        sFormMapping.setProcessDefinitionId(666l);\n        sFormMapping.setPageMapping(new SPageMapping());\n\n        // Then\n        List<FormMapping> formMapping = ModelConvertor.toFormMappings(Arrays.<SFormMapping> asList(sFormMapping),\n                formRequiredAnalyzer);\n\n        // When\n        assertThat(formMapping).hasSize(1);\n        assertThat(formMapping.get(0).getId()).isEqualTo(555l);\n        assertThat(formMapping.get(0).getType()).isEqualTo(FormMappingType.TASK);\n        //        assertThat(formMapping.get(0).getTarget()).isEqualTo(FormMappingTarget.URL);\n        //        assertThat(formMapping.get(0).getForm()).isEqualTo(\"myForm1\");\n        assertThat(formMapping.get(0).getTask()).isEqualTo(\"myTask\");\n        assertThat(formMapping.get(0).getProcessDefinitionId()).isEqualTo(666l);\n\n    }\n\n    @Test\n    void toFormMapping_can_convert_null() {\n        // Given\n\n        // Then\n        FormMapping formMapping = ModelConvertor.toFormMapping(null, formRequiredAnalyzer);\n\n        // When\n        assertThat(formMapping).isNull();\n\n    }\n\n    @Test\n    void convertSContractDefinition() {\n        //given\n        final InputDefinition expectedSimpleInput = new InputDefinitionImpl(\"name\", Type.TEXT, \"description\");\n        final InputDefinition expectedComplexInput = new InputDefinitionImpl(\"complex input\", \"complex description\",\n                Arrays.asList(expectedSimpleInput));\n        final ConstraintDefinition expectedRule = new ConstraintDefinitionImpl(\"name\", \"expression\", \"explanation\");\n        expectedRule.getInputNames().add(\"input1\");\n        expectedRule.getInputNames().add(\"input2\");\n\n        //when\n        final SContractDefinition contractDefinition = new SContractDefinitionImpl();\n        final SConstraintDefinition sRule = new SConstraintDefinitionImpl(expectedRule);\n        final SInputDefinition sSimpleInput = new SInputDefinitionImpl(expectedSimpleInput);\n        final SInputDefinition sComplexInput = new SInputDefinitionImpl(expectedComplexInput);\n\n        contractDefinition.getConstraints().add(sRule);\n        contractDefinition.getInputDefinitions().add(sSimpleInput);\n        contractDefinition.getInputDefinitions().add(sComplexInput);\n\n        final ContractDefinition contract = ModelConvertor.toContract(contractDefinition);\n\n        //then\n        assertThat(contract.getConstraints()).as(\"should convert rules\").containsExactly(expectedRule);\n        assertThat(contract.getInputs()).as(\"should convert inputs\").containsExactly(expectedSimpleInput,\n                expectedComplexInput);\n    }\n\n    @Test\n    void convertNullSContractDefinition() {\n        //when\n        final ContractDefinition contract = ModelConvertor.toContract(null);\n\n        //then\n        assertThat(contract).as(\"contract null\").isNull();\n    }\n\n    @Test\n    void convertMultipleSContractDefinition() {\n        //given\n        final InputDefinition expectedSimpleInput = new InputDefinitionImpl(\"name\", Type.TEXT, \"description\", true);\n        final InputDefinition expectedComplexInput = new InputDefinitionImpl(\"complex input\", \"complex description\",\n                true,\n                Collections.singletonList(expectedSimpleInput));\n        final InputDefinition expectedComplexWithComplexInput = new InputDefinitionImpl(\"complex in complex\",\n                \"complex description\", true,\n                null, Collections.singletonList(expectedComplexInput));\n\n        final ConstraintDefinition expectedRule = new ConstraintDefinitionImpl(\"name\", \"expression\", \"explanation\");\n        expectedRule.getInputNames().add(\"input1\");\n        expectedRule.getInputNames().add(\"input2\");\n\n        //when\n        final SContractDefinition contractDefinition = new SContractDefinitionImpl();\n        final SConstraintDefinition sRule = new SConstraintDefinitionImpl(expectedRule);\n        final SInputDefinition sSimpleInput = new SInputDefinitionImpl(expectedSimpleInput);\n        final SInputDefinition sComplexInput = new SInputDefinitionImpl(expectedComplexWithComplexInput);\n\n        contractDefinition.getConstraints().add(sRule);\n        contractDefinition.getInputDefinitions().add(sSimpleInput);\n        contractDefinition.getInputDefinitions().add(sComplexInput);\n\n        final ContractDefinition contract = ModelConvertor.toContract(contractDefinition);\n\n        //then\n        assertThat(contract.getConstraints()).as(\"should convert rules\").containsExactly(expectedRule);\n        assertThat(contract.getInputs()).as(\"should convert simple inputs\").containsExactly(expectedSimpleInput,\n                expectedComplexWithComplexInput);\n    }\n\n    SProcessSimpleRefBusinessDataInstance createProcessSimpleDataReference(String name, long processInstanceId,\n            String type, long businessDataId) {\n        SProcessSimpleRefBusinessDataInstance sProcessSimpleRefBusinessDataInstance = new SProcessSimpleRefBusinessDataInstance();\n        sProcessSimpleRefBusinessDataInstance.setName(name);\n        sProcessSimpleRefBusinessDataInstance.setProcessInstanceId(processInstanceId);\n        sProcessSimpleRefBusinessDataInstance.setDataClassName(type);\n        sProcessSimpleRefBusinessDataInstance.setDataId(businessDataId);\n        return sProcessSimpleRefBusinessDataInstance;\n    }\n\n    SProcessMultiRefBusinessDataInstance createProcessMultipleDataReference(String name, long processInstanceId,\n            String type, List<Long> dataIds) {\n        SProcessMultiRefBusinessDataInstance sProcessSimpleRefBusinessDataInstance = new SProcessMultiRefBusinessDataInstance();\n        sProcessSimpleRefBusinessDataInstance.setName(name);\n        sProcessSimpleRefBusinessDataInstance.setProcessInstanceId(processInstanceId);\n        sProcessSimpleRefBusinessDataInstance.setDataClassName(type);\n        sProcessSimpleRefBusinessDataInstance.setDataIds(dataIds);\n        return sProcessSimpleRefBusinessDataInstance;\n    }\n\n    @Test\n    void convert_multiple_business_data() {\n        BusinessDataReference businessDataReference = ModelConvertor\n                .toBusinessDataReference(createProcessSimpleDataReference(\"myBData\", 157l, \"theType\", 5555l));\n\n        assertThat(businessDataReference).isEqualTo(new SimpleBusinessDataReferenceImpl(\"myBData\", \"theType\", 5555l));\n    }\n\n    @Test\n    void convert_simple_business_data() {\n        BusinessDataReference businessDataReference = ModelConvertor\n                .toBusinessDataReference(createProcessMultipleDataReference(\"myBData\", 157l, \"theType\",\n                        Arrays.asList(5555l, 5556l, 5557l)));\n\n        assertThat(businessDataReference).isEqualTo(\n                new MultipleBusinessDataReferenceImpl(\"myBData\", \"theType\", Arrays.asList(5555l, 5556l, 5557l)));\n    }\n\n    @Test\n    void convert_null_business_data() {\n        BusinessDataReference businessDataReference = ModelConvertor.toBusinessDataReference(null);\n\n        assertThat(businessDataReference).isNull();\n    }\n\n    @Test\n    void toArchivedFlownodeInstance_should_convert_reachStateDate_for_gateways() {\n        final FlowNodeStateManager flowNodeStateManager = mock(FlowNodeStateManager.class);\n        doReturn(mock(FlowNodeState.class)).when(flowNodeStateManager).getState(anyInt());\n\n        final SAGatewayInstance saFlowNode = new SAGatewayInstance();\n        final long reachedStateDate = 4534311114L;\n        saFlowNode.setReachedStateDate(reachedStateDate);\n        assertThat(ModelConvertor.toArchivedFlowNodeInstance(saFlowNode, flowNodeStateManager).getReachedStateDate())\n                .isEqualTo(new Date(reachedStateDate));\n    }\n\n    @Test\n    void toArchivedFlownodeInstance_should_convert_reachStateDate_for_receivetask() {\n        final FlowNodeStateManager flowNodeStateManager = mock(FlowNodeStateManager.class);\n        doReturn(mock(FlowNodeState.class)).when(flowNodeStateManager).getState(anyInt());\n\n        final SAReceiveTaskInstance receiveTaskInstance = new SAReceiveTaskInstance();\n        assertThat(ModelConvertor.toArchivedFlowNodeInstance(receiveTaskInstance, flowNodeStateManager)\n                .getReachedStateDate()).isNotNull();\n    }\n\n    @Test\n    void toArchivedFlownodeInstance_should_convert_reachStateDate_for_sendtask() {\n        final FlowNodeStateManager flowNodeStateManager = mock(FlowNodeStateManager.class);\n        doReturn(mock(FlowNodeState.class)).when(flowNodeStateManager).getState(anyInt());\n\n        final SASendTaskInstance sendTaskInstance = new SASendTaskInstance();\n        assertThat(\n                ModelConvertor.toArchivedFlowNodeInstance(sendTaskInstance, flowNodeStateManager).getReachedStateDate())\n                .isNotNull();\n    }\n\n    @Test\n    void toArchivedFlownodeInstance_should_convert_reachStateDate_for_usertask() {\n        final FlowNodeStateManager flowNodeStateManager = mock(FlowNodeStateManager.class);\n        doReturn(mock(FlowNodeState.class)).when(flowNodeStateManager).getState(anyInt());\n\n        final SAUserTaskInstance saFlowNode = new SAUserTaskInstance();\n        saFlowNode.setPriority(STaskPriority.UNDER_NORMAL);\n        assertThat(ModelConvertor.toArchivedFlowNodeInstance(saFlowNode, flowNodeStateManager).getReachedStateDate())\n                .isNotNull();\n    }\n\n    @Test\n    void toFlownodeInstance_should_convert_reachStateDate_for_usertask() {\n        final FlowNodeStateManager flowNodeStateManager = mock(FlowNodeStateManager.class);\n        doReturn(mock(FlowNodeState.class)).when(flowNodeStateManager).getState(anyInt());\n\n        final SUserTaskInstance sFlowNode = new SUserTaskInstance();\n        sFlowNode.setPriority(STaskPriority.UNDER_NORMAL);\n        assertThat(ModelConvertor.toFlowNodeInstance(sFlowNode, flowNodeStateManager).getReachedStateDate())\n                .isNotNull();\n    }\n\n    @Test\n    void toArchivedFlownodeInstance_should_convert_lastUpdateDate_for_usertask() {\n        final FlowNodeStateManager flowNodeStateManager = mock(FlowNodeStateManager.class);\n        doReturn(mock(FlowNodeState.class)).when(flowNodeStateManager).getState(anyInt());\n\n        final SAUserTaskInstance saFlowNode = new SAUserTaskInstance();\n        saFlowNode.setPriority(STaskPriority.UNDER_NORMAL);\n        final long lastUpdateDate = 5746354125555L;\n        saFlowNode.setLastUpdateDate(lastUpdateDate);\n        assertThat(ModelConvertor.toArchivedFlowNodeInstance(saFlowNode, flowNodeStateManager).getLastUpdateDate())\n                .isEqualTo(new Date(lastUpdateDate));\n    }\n\n    @Test\n    void toFlownodeInstance_should_convert_lastUpdateDate_for_usertask() {\n        final FlowNodeStateManager flowNodeStateManager = mock(FlowNodeStateManager.class);\n        doReturn(mock(FlowNodeState.class)).when(flowNodeStateManager).getState(anyInt());\n\n        final SUserTaskInstance sFlowNode = new SUserTaskInstance();\n        sFlowNode.setPriority(STaskPriority.UNDER_NORMAL);\n        assertThat(ModelConvertor.toFlowNodeInstance(sFlowNode, flowNodeStateManager).getLastUpdateDate()).isNotNull();\n        assertThat(ModelConvertor.toFlowNodeInstance(new SGatewayInstance(), flowNodeStateManager).getLastUpdateDate())\n                .isNotNull();\n    }\n\n    @Test\n    void should_convert_expected_end_date() {\n        //given\n        final FlowNodeStateManager flowNodeStateManager = mock(FlowNodeStateManager.class);\n        doReturn(mock(FlowNodeState.class)).when(flowNodeStateManager).getState(anyInt());\n\n        final SUserTaskInstance urgentSTask = new SUserTaskInstance();\n        urgentSTask.setExpectedEndDate(15L);\n        urgentSTask.setPriority(STaskPriority.UNDER_NORMAL);\n\n        final SUserTaskInstance takeYourTimeSTask = new SUserTaskInstance();\n        takeYourTimeSTask.setPriority(STaskPriority.UNDER_NORMAL);\n\n        //when\n        final UserTaskInstance urgentTask = ModelConvertor.toUserTaskInstance(urgentSTask, flowNodeStateManager);\n        final UserTaskInstance takeYourTimeTask = ModelConvertor.toUserTaskInstance(takeYourTimeSTask,\n                flowNodeStateManager);\n\n        //then\n        assertThat(urgentTask.getExpectedEndDate()).isEqualTo(new Date(15L));\n        assertThat(takeYourTimeTask.getExpectedEndDate()).isNull();\n    }\n\n    @Test\n    void should_convert_archived_expected_end_date() {\n        //given\n        final FlowNodeStateManager flowNodeStateManager = mock(FlowNodeStateManager.class);\n        doReturn(mock(FlowNodeState.class)).when(flowNodeStateManager).getState(anyInt());\n\n        final SAUserTaskInstance urgentASTask = new SAUserTaskInstance();\n        urgentASTask.setExpectedEndDate(15L);\n        urgentASTask.setPriority(STaskPriority.UNDER_NORMAL);\n\n        final SAUserTaskInstance takeYourTimeASTask = new SAUserTaskInstance();\n        takeYourTimeASTask.setPriority(STaskPriority.UNDER_NORMAL);\n\n        //when\n        final ArchivedUserTaskInstance urgentATask = ModelConvertor.toArchivedUserTaskInstance(urgentASTask,\n                flowNodeStateManager);\n        final ArchivedUserTaskInstance takeYourTimeATask = ModelConvertor.toArchivedUserTaskInstance(takeYourTimeASTask,\n                flowNodeStateManager);\n\n        //then\n        assertThat(urgentATask.getExpectedEndDate()).isEqualTo(new Date(15L));\n        assertThat(takeYourTimeATask.getExpectedEndDate()).isNull();\n    }\n\n    @Test\n    void should_set_the_parentPath_when_creating_a_UserMembership() {\n        //given\n        SUserMembership sUserMembership = new SUserMembership(257L, 157L, 357L, 457L, 557L, 190119993L,\n                \"Bonita/dummy\", \"dummy rolename\", \"dummy groupname\", \"dummy username\");\n\n        //when\n        UserMembershipImpl userMembership = (UserMembershipImpl) toUserMembership(sUserMembership);\n\n        //then\n        assertThat(userMembership.getGroupParentPath()).isNotNull();\n        assertThat(userMembership.getGroupParentPath()).isEqualToIgnoringCase(\"Bonita/dummy\");\n    }\n\n    @Test\n    void toTenantResource_should_convert_all_fields() {\n        // given:\n        final OffsetDateTime offsetDateTime = OffsetDateTime.parse(\"1970-01-01T00:00:15Z\");\n        String resourceName = \"any\";\n        STenantResourceLight sTenantResource = new STenantResourceLight(resourceName,\n                TenantResourceType.BDM, 12L, offsetDateTime.toInstant().toEpochMilli(), STenantResourceState.INSTALLED);\n\n        // when:\n        TenantResource tenantResource = ModelConvertor.toTenantResource(sTenantResource);\n\n        // then:\n        assertThat(tenantResource.getName()).isEqualTo(resourceName);\n        assertThat(tenantResource.getLastUpdatedBy()).isEqualTo(12L);\n        assertThat(tenantResource.getLastUpdateDate()).isEqualTo(offsetDateTime);\n        assertThat(tenantResource.getState()).isEqualTo(INSTALLED);\n        assertThat(tenantResource.getType()).isEqualTo(BDM);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/service/impl/BonitaSpringContextTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.BeansException;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n/**\n * @author Baptiste Mesta.\n */\n@RunWith(SpringRunner.class)\n@ContextConfiguration(classes = { BonitaSpringContextTest.TestConfiguration.class })\npublic class BonitaSpringContextTest implements ApplicationContextAware {\n\n    private BonitaSpringContext bonitaSpringContext;\n    private ApplicationContext applicationContext;\n\n    @Before\n    public void before() throws Exception {\n        bonitaSpringContext = new BonitaSpringContext(applicationContext, \"test context\");\n        bonitaSpringContext.refresh();\n    }\n\n    @Test\n    public void should_cache_beans_resolution_when_getting_bean_by_class() throws Exception {\n        MyService firstCallToGetBean = bonitaSpringContext.getBean(MyService.class);\n        MyService secondCallToGetBean = bonitaSpringContext.getBean(MyService.class);\n\n        assertThat(firstCallToGetBean).isEqualTo(secondCallToGetBean);\n    }\n\n    @Test\n    public void should_cache_beans_resolution_when_getting_bean_by_name() throws Exception {\n        MyService firstCallToGetBean = bonitaSpringContext.getBean(\"ThisIsMyService\", MyService.class);\n        MyService secondCallToGetBean = bonitaSpringContext.getBean(\"ThisIsMyService\", MyService.class);\n\n        assertThat(firstCallToGetBean).isEqualTo(secondCallToGetBean);\n    }\n\n    @Test(expected = IllegalStateException.class)\n    public void should_clear_cache_on_destroy() throws Exception {\n        bonitaSpringContext.getBean(\"ThisIsMyService\", MyService.class);\n\n        bonitaSpringContext.destroy();\n\n        bonitaSpringContext.getBean(\"ThisIsMyService\", MyService.class);\n    }\n\n    @Override\n    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\n        this.applicationContext = applicationContext;\n    }\n\n    @Configuration\n    static class TestConfiguration {\n\n        @Bean(name = \"ThisIsMyService\")\n        MyService myService() {\n            return new MyService();\n        }\n    }\n\n    private static class MyService {\n\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/service/impl/ServerLoggerWrapperTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service.impl;\n\nimport static org.mockito.Mockito.verify;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\nimport org.slf4j.Logger;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ServerLoggerWrapperTest {\n\n    private final Class<?> aClass = ServerLoggerWrapperTest.class;\n    private final IllegalStateException anException = new IllegalStateException();\n    private ServerLoggerWrapper logger;\n    @Mock\n    private Logger loggerSL4J;\n\n    @Before\n    public void before() {\n        logger = new ServerLoggerWrapper(aClass, loggerSL4J);\n    }\n\n    @Test\n    public void testTrace() {\n        logger.trace(\"msg\");\n\n        verify(loggerSL4J).trace(\"msg\");\n    }\n\n    @Test\n    public void testTrace1() {\n        logger.trace(\"msg\", anException);\n\n        verify(loggerSL4J).trace(\"msg\", anException);\n    }\n\n    @Test\n    public void testDebug() {\n        logger.debug(\"msg\");\n\n        verify(loggerSL4J).debug(\"msg\");\n\n    }\n\n    @Test\n    public void testDebug1() {\n        logger.debug(\"msg\", anException);\n\n        verify(loggerSL4J).debug(\"msg\", anException);\n\n    }\n\n    @Test\n    public void testInfo() {\n        logger.info(\"msg\");\n\n        verify(loggerSL4J).info(\"msg\");\n\n    }\n\n    @Test\n    public void testInfo1() {\n        logger.info(\"msg\", anException);\n\n        verify(loggerSL4J).info(\"msg\", anException);\n\n    }\n\n    @Test\n    public void testWarning() {\n        logger.warning(\"msg\");\n\n        verify(loggerSL4J).warn(\"msg\");\n\n    }\n\n    @Test\n    public void testWarning1() {\n        logger.warning(\"msg\", anException);\n\n        verify(loggerSL4J).warn(\"msg\", anException);\n\n    }\n\n    @Test\n    public void testError() {\n        logger.error(\"msg\");\n\n        verify(loggerSL4J).error(\"msg\");\n\n    }\n\n    @Test\n    public void testError1() {\n        logger.error(\"msg\", anException);\n\n        verify(loggerSL4J).error(\"msg\", anException);\n\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/service/impl/SpringBeanAccessorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service.impl;\n\nimport static java.util.Arrays.asList;\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Properties;\n\nimport org.bonitasoft.platform.configuration.model.BonitaConfiguration;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.EnvironmentVariables;\nimport org.junit.contrib.java.lang.system.RestoreSystemProperties;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.support.FileSystemXmlApplicationContext;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SpringBeanAccessorTest {\n\n    private List<String> springFilesFromClasspath = new ArrayList<>();\n    private List<BonitaConfiguration> configurationFromDatabase = new ArrayList<>();\n    private SpringBeanAccessor springBeanAccessor;\n\n    private Properties contextProperties = new Properties();\n\n    @Rule\n    public EnvironmentVariables envVar = new EnvironmentVariables();\n    @Rule\n    public RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties();\n\n    private void createSpringContext() {\n        springBeanAccessor = createSpringBeanAccessor();\n    }\n\n    private SpringBeanAccessor createSpringBeanAccessor() {\n        FileSystemXmlApplicationContext parent = new FileSystemXmlApplicationContext();\n        parent.refresh();\n        return new SpringBeanAccessor() {\n\n            @Override\n            protected BonitaSpringContext createContext() {\n                return new BonitaSpringContext(parent, \"test\");\n            }\n\n            @Override\n            protected Properties getProperties() {\n                return contextProperties;\n            }\n\n            @Override\n            protected List<BonitaConfiguration> getConfigurationFromDatabase() {\n                return configurationFromDatabase;\n            }\n\n            @Override\n            protected boolean isCluster() {\n                return false;\n            }\n\n            @Override\n            protected List<String> getSpringFileFromClassPath(boolean cluster) {\n                return springFilesFromClasspath;\n            }\n        };\n    }\n\n    @Test\n    public void should_getContext_create_context_using_classpathResources() {\n        //given: these resources exists in the classpath (or else they are not added)\n        springFilesFromClasspath.add(\"classpathresource1\");\n        springFilesFromClasspath.add(\"classpathresource2\");\n        createSpringContext();\n        //when\n        BonitaSpringContext context = (BonitaSpringContext) springBeanAccessor.getContext();\n        //then: the resources in spring context are ClassPathResources with path as follow\n        assertThat(asList(context.getConfigResources())).extracting(\"path\").containsExactly(\"classpathresource1\",\n                \"classpathresource2\");\n    }\n\n    @Test\n    public void should_create_context_using_BonitaConfiguration_as_byte_array_resources() {\n        //given\n        String xmlFile = \"<beans xmlns=\\\"http://www.springframework.org/schema/beans\\\" xmlns:xsi=\\\"http://www.w3.org/2001/XMLSchema-instance\\\" \"\n                +\n                \"xsi:schemaLocation=\\\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd\\\"/>\";\n        BonitaConfiguration res1 = new BonitaConfiguration(\"res1\", xmlFile.getBytes());\n        BonitaConfiguration res2 = new BonitaConfiguration(\"res2\", xmlFile.getBytes());\n        configurationFromDatabase.add(res1);\n        configurationFromDatabase.add(res2);\n        createSpringContext();\n        //when\n        BonitaSpringContext context = (BonitaSpringContext) springBeanAccessor.getContext();\n        //then: the resources in spring context are ByteArrayResource with generated description as follow\n        assertThat(asList(context.getConfigResources())).extracting(\"description\")\n                .containsExactly(\"Byte array resource [res1]\", \"Byte array resource [res2]\");\n    }\n\n    @Test\n    public void should_populate_environment_with_properties() {\n        contextProperties.setProperty(\"a.property\", \"itsValue\");\n        createSpringContext();\n\n        ApplicationContext context = springBeanAccessor.getContext();\n\n        assertThat(context.getEnvironment().getProperty(\"a.property\")).isEqualTo(\"itsValue\");\n    }\n\n    @Test\n    public void should_create_context_with_properties_from_env_that_override_database() {\n        contextProperties.setProperty(\"myProperty\", \"databaseValue\");\n        envVar.set(\"myProperty\", \"envValue\");\n        createSpringContext();\n\n        ApplicationContext context = springBeanAccessor.getContext();\n\n        assertThat(context.getEnvironment().getProperty(\"myProperty\")).isEqualTo(\"envValue\");\n    }\n\n    @Test\n    public void should_create_context_with_properties_from_system_properties_that_override_database() {\n        contextProperties.setProperty(\"myProperty\", \"databaseValue\");\n        System.setProperty(\"myProperty\", \"sysPropValue\");\n        createSpringContext();\n\n        ApplicationContext context = springBeanAccessor.getContext();\n\n        assertThat(context.getEnvironment().getProperty(\"myProperty\")).isEqualTo(\"sysPropValue\");\n    }\n\n    @Test\n    public void should_property_from_database_take_precedence_on_env_if_legacy_mode() {\n        contextProperties.setProperty(\"myProperty\", \"databaseValue\");\n        envVar.set(\"myProperty\", \"envValue\");\n        envVar.set(\"BONITA_RUNTIME_PROPERTIES_ORDER_LEGACY_MODE\", \"true\");\n        createSpringContext();\n\n        ApplicationContext context = springBeanAccessor.getContext();\n\n        assertThat(context.getEnvironment().getProperty(\"myProperty\")).isEqualTo(\"databaseValue\");\n    }\n\n    @Test\n    public void should_property_from_database_take_precedence_on_sysProp_if_legacy_mode() {\n        contextProperties.setProperty(\"myProperty\", \"databaseValue\");\n        System.setProperty(\"myProperty\", \"sysPropValue\");\n        System.setProperty(\"bonita.runtime.properties.order.legacy-mode\", \"true\");\n        createSpringContext();\n\n        ApplicationContext context = springBeanAccessor.getContext();\n\n        assertThat(context.getEnvironment().getProperty(\"myProperty\")).isEqualTo(\"databaseValue\");\n    }\n\n    @Test\n    public void should_getPropertyWithPlaceholder_with_placeholder_return_system_property() {\n        //given\n        createSpringContext();\n        Properties properties = new Properties();\n        properties.setProperty(\"myTestProperty\",\n                \"${system.test.property:default value if not set in system properties}\");\n        System.setProperty(\"system.test.property\", \"the value set in syst properties\");\n        //when\n        String value = springBeanAccessor.getPropertyWithPlaceholder(properties, \"myTestProperty\", \"default\");\n\n        //then\n        assertThat(value).isEqualTo(\"the value set in syst properties\");\n    }\n\n    @Test\n    public void should_getPropertyWithPlaceholder_with_placeholder_return_default_value_from_property() {\n        //given\n        createSpringContext();\n        Properties properties = new Properties();\n        properties.setProperty(\"myTestProperty\",\n                \"${system.test.property2:default value if not set in system properties}\");\n        //when\n        String value = springBeanAccessor.getPropertyWithPlaceholder(properties, \"myTestProperty\", \"default\");\n\n        //then\n        assertThat(value).isEqualTo(\"default value if not set in system properties\");\n    }\n\n    @Test\n    public void should_getPropertyWithPlaceholder_without_placeholder_return_default_value() {\n        //given\n        createSpringContext();\n        Properties properties = new Properties();\n        //when\n        String value = springBeanAccessor.getPropertyWithPlaceholder(properties, \"myTestProperty\", \"default\");\n\n        //then\n        assertThat(value).isEqualTo(\"default\");\n    }\n\n    @Test\n    public void should_getPropertyWithPlaceholder_without_placeholder_return_value() {\n        //given\n        createSpringContext();\n        Properties properties = new Properties();\n        properties.setProperty(\"myTestProperty\", \"toto}\");\n        //when\n        String value = springBeanAccessor.getPropertyWithPlaceholder(properties, \"myTestProperty\", \"default\");\n\n        //then\n        assertThat(value).isEqualTo(\"toto}\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/service/impl/installation/ConfigurationArchiveTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service.impl.installation;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\n\nimport java.io.InputStream;\nimport java.util.List;\n\nimport org.junit.Test;\n\npublic class ConfigurationArchiveTest {\n\n    @Test\n    public void should_load_process_configurations_from_archive() throws Exception {\n        try (InputStream bConfStream = ConfigurationArchiveTest.class.getResourceAsStream(\"/test.bconf\");\n                ConfigurationArchive configurationArchive = new ConfigurationArchive(bConfStream.readAllBytes())) {\n            assertThat(configurationArchive.getBuilderVersion()).isEqualTo(\"7.8.0-SNAPSHOT\");\n            assertThat(configurationArchive.getTargetEnvironment()).isEqualTo(\"Production\");\n            List<ProcessConfiguration> processConfigurations = configurationArchive.getProcessConfigurations();\n            assertThat(processConfigurations)\n                    .contains(new ProcessConfiguration(\"RequestLoan\", \"1.3.1\"),\n                            new ProcessConfiguration(\"LoanRequestBot\", \"1.3.1\"));\n\n            ProcessConfiguration processConfiguration = configurationArchive.getProcessConfigurations().stream()\n                    .filter(new ProcessConfiguration(\"RequestLoan\", \"1.3.1\")::equals).findFirst().orElse(null);\n            assertThat(processConfiguration).isNotNull();\n            assertThat(processConfiguration.getParameters()).contains(entry(\"botActivated\", \"true\"),\n                    entry(\"smtpPassword\", \"dfghj65789)IU\"));\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/service/impl/installation/InstallationServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service.impl.installation;\n\nimport static org.assertj.core.api.Assertions.*;\nimport static org.mockito.ArgumentMatchers.anyLong;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.*;\n\nimport java.io.InputStream;\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.impl.resolver.BusinessArchiveArtifactsManager;\nimport org.bonitasoft.engine.bpm.process.Problem;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SFlowElementContainerDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SProcessDefinitionImpl;\nimport org.bonitasoft.engine.execution.event.EventsHandler;\nimport org.bonitasoft.engine.parameter.ParameterService;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.SystemOutRule;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class InstallationServiceImplTest {\n\n    @Rule\n    public SystemOutRule systemOutRule = new SystemOutRule().enableLog();\n    @Mock\n    private ProcessDefinitionService processDefinitionService;\n    @Mock\n    private ParameterService parameterService;\n    @Mock\n    private BusinessArchiveArtifactsManager businessArchiveArtifactsManager;\n    @Mock\n    private EventsHandler eventsHandler;\n\n    @Spy\n    @InjectMocks\n    private InstallationServiceImpl service;\n\n    @Test\n    public void should_merge_parameters_for_each_process_configuration_and_enable_the_process() throws Exception {\n        // Given\n        when(processDefinitionService.getProcessDefinitionId(\"RequestLoan\", \"1.3.1\")).thenReturn(123L);\n        when(processDefinitionService.getProcessDefinitionId(\"LoanRequestBot\", \"1.3.1\")).thenReturn(321L);\n        when(processDefinitionService.getProcessDefinition(123L))\n                .thenReturn(new SProcessDefinitionImpl(\"RequestLoan\", \"1.3.1\"));\n        when(processDefinitionService.getProcessDefinition(321L))\n                .thenReturn(new SProcessDefinitionImpl(\"LoanRequestBot\", \"1.3.1\"));\n        final SProcessDefinitionDeployInfo info = mock(SProcessDefinitionDeployInfo.class);\n        doReturn(info).when(processDefinitionService).getProcessDeploymentInfo(123L);\n        doReturn(info).when(processDefinitionService).getProcessDeploymentInfo(321L);\n        doReturn(\"DISABLED\").when(info).getActivationState();\n\n        // When\n        try (InputStream is = InstallationServiceImplTest.class.getResourceAsStream(\"/test.bconf\")) {\n            service.install(null, is.readAllBytes());\n        }\n\n        // Then\n        ArgumentCaptor<Map> parametersCaptor = ArgumentCaptor.forClass(Map.class);\n        verify(parameterService).merge(eq(123L), (java.util.Map<String, String>) parametersCaptor.capture());\n        assertThat(parametersCaptor.getValue()).contains(entry(\"botActivated\", \"true\"),\n                entry(\"smtpPassword\", \"dfghj65789)IU\"));\n        parametersCaptor = ArgumentCaptor.forClass(Map.class);\n        verify(parameterService).merge(eq(321L), (java.util.Map<String, String>) parametersCaptor.capture());\n        assertThat(parametersCaptor.getValue()).contains(entry(\"validateLoanMinTime\", \"36000000\"),\n                entry(\"completeLoanMinTime\", \"600000\"));\n    }\n\n    @Test\n    public void should_check_process_resolution_and_enable_process() throws Exception {\n        // Given\n        when(processDefinitionService.getProcessDefinitionId(\"RequestLoan\", \"1.3.1\")).thenReturn(123L);\n        when(processDefinitionService.getProcessDefinitionId(\"LoanRequestBot\", \"1.3.1\")).thenReturn(779L);\n        final SProcessDefinition processDefinition1 = mock(SProcessDefinition.class);\n        doReturn(new SFlowElementContainerDefinitionImpl()).when(processDefinition1).getProcessContainer();\n        doReturn(processDefinition1).when(processDefinitionService).getProcessDefinition(123L);\n        final SProcessDefinition processDefinition2 = mock(SProcessDefinition.class);\n        doReturn(new SFlowElementContainerDefinitionImpl()).when(processDefinition2).getProcessContainer();\n        doReturn(processDefinition2).when(processDefinitionService).getProcessDefinition(779L);\n        // no need to mock the return of checkResolution, as it is empty list by default.\n        final SProcessDefinitionDeployInfo info = mock(SProcessDefinitionDeployInfo.class);\n        doReturn(info).when(processDefinitionService).getProcessDeploymentInfo(123L);\n        doReturn(info).when(processDefinitionService).getProcessDeploymentInfo(779L);\n        doReturn(\"DISABLED\").when(info).getActivationState();\n\n        // When\n        try (InputStream is = InstallationServiceImplTest.class.getResourceAsStream(\"/test.bconf\")) {\n            service.install(null, is.readAllBytes());\n        }\n\n        // Then\n        verify(businessArchiveArtifactsManager).getProcessResolutionProblems(processDefinition1);\n        verify(businessArchiveArtifactsManager).getProcessResolutionProblems(processDefinition2);\n        verify(businessArchiveArtifactsManager).changeResolutionStatus(123L, processDefinitionService, true);\n        verify(processDefinitionService).enableProcess(123L, false);\n        verify(businessArchiveArtifactsManager).changeResolutionStatus(779L, processDefinitionService, true);\n        verify(processDefinitionService).enableProcess(779L, false);\n\n        verify(processDefinitionService, times(0)).disableProcess(anyLong(), anyBoolean());\n    }\n\n    @Test\n    public void should_check_process_resolution_and_disable_process_if_unresolved() throws Exception {\n        // Given\n        when(processDefinitionService.getProcessDefinitionId(\"RequestLoan\", \"1.3.1\")).thenReturn(123L);\n        final SProcessDefinition processDefinition = mock(SProcessDefinition.class);\n        doReturn(processDefinition).when(processDefinitionService).getProcessDefinition(123L);\n        doReturn(Collections.singletonList(mock(Problem.class))).when(businessArchiveArtifactsManager)\n                .getProcessResolutionProblems(processDefinition);\n        final SProcessDefinitionDeployInfo info = mock(SProcessDefinitionDeployInfo.class);\n        doReturn(info).when(processDefinitionService).getProcessDeploymentInfo(123L);\n        doReturn(\"ENABLED\").when(info).getActivationState();\n\n        // When\n        try (InputStream is = this.getClass().getResourceAsStream(\"/RequestLoan_conf_with_null_params.bconf\")) {\n            service.install(null, is.readAllBytes());\n        }\n\n        // Then\n        verify(businessArchiveArtifactsManager).getProcessResolutionProblems(processDefinition);\n        verify(businessArchiveArtifactsManager).changeResolutionStatus(123L, processDefinitionService, false);\n        verify(processDefinitionService).disableProcess(123L, false);\n\n        verify(processDefinitionService, times(0)).enableProcess(anyLong(), anyBoolean());\n    }\n\n    @Test\n    public void should_not_throw_an_InstallationFailedException_if_process_def_is_not_found() throws Exception {\n        // Given\n        doThrow(new SProcessDefinitionNotFoundException(\"RequestLoan\"))\n                .when(processDefinitionService).getProcessDefinitionId(\"RequestLoan\", \"1.3.1\");\n        final long processDefinitionId = 987L;\n        doReturn(processDefinitionId).when(processDefinitionService).getProcessDefinitionId(\"LoanRequestBot\", \"1.3.1\");\n        doReturn(new SProcessDefinitionImpl(\"LoanRequestBot\", \"1.3.1\")).when(processDefinitionService)\n                .getProcessDefinition(processDefinitionId);\n        final SProcessDefinitionDeployInfo info = mock(SProcessDefinitionDeployInfo.class);\n        doReturn(info).when(processDefinitionService).getProcessDeploymentInfo(processDefinitionId);\n        doReturn(\"ENABLED\").when(info).getActivationState();\n\n        systemOutRule.clearLog();\n        // When\n        try (InputStream is = InstallationServiceImplTest.class.getResourceAsStream(\"/test.bconf\")) {\n            service.install(null, is.readAllBytes());\n        }\n\n        assertThat(systemOutRule.getLog()).containsPattern(\"WARN.*.BCONF file for non existing process \");\n    }\n\n    @Test\n    public void should_throw_an_IllegalStateException_if_a_binaries_archive_is_given() throws Exception {\n        try (InputStream is = InstallationServiceImplTest.class.getResourceAsStream(\"/test.bconf\")) {\n            assertThatExceptionOfType(IllegalStateException.class)\n                    .isThrownBy(() -> service.install(is.readAllBytes(), null));\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/service/platform/PlatformInformationServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service.platform;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport org.bonitasoft.engine.platform.exception.SPlatformUpdateException;\nimport org.bonitasoft.engine.platform.model.SPlatform;\nimport org.bonitasoft.engine.services.PersistenceService;\nimport org.bonitasoft.engine.services.SPersistenceException;\nimport org.bonitasoft.engine.services.UpdateDescriptor;\nimport org.hamcrest.core.IsEqual;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class PlatformInformationServiceImplTest {\n\n    @Mock\n    private PersistenceService persistenceService;\n\n    @InjectMocks\n    private PlatformInformationServiceImpl platformInformationService;\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void updatePlatformInfo() throws Exception {\n        //given\n        SPlatform platform = mock(SPlatform.class);\n\n        //when\n        platformInformationService.updatePlatformInfo(platform, \"info\");\n\n        //then\n        ArgumentCaptor<UpdateDescriptor> upDescCaptor = ArgumentCaptor.forClass(UpdateDescriptor.class);\n        verify(persistenceService).update(upDescCaptor.capture());\n        UpdateDescriptor descriptor = upDescCaptor.getValue();\n        assertThat(descriptor.getFields()).containsEntry(SPlatform.INFORMATION, \"info\");\n        assertThat(descriptor.getEntity()).isEqualTo(platform);\n    }\n\n    @Test\n    public void updatePlatformInfo_should_throw_exception_when_persistence_service_throws_exception() throws Exception {\n        //given\n        SPlatform platform = mock(SPlatform.class);\n        SPersistenceException toBeThrown = new SPersistenceException(\"exception\");\n        doThrow(toBeThrown).when(persistenceService).update(any(UpdateDescriptor.class));\n\n        //then\n        expectedException.expect(SPlatformUpdateException.class);\n        expectedException.expectCause(new IsEqual<Throwable>(toBeThrown));\n\n        //when\n        platformInformationService.updatePlatformInfo(platform, \"info\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/tenant/SingleNodeTaskCoordinatorLocalTest.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tenant;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.tenant.SingleNodeTaskCoordinator.TASK_CLEANUP_OBSOLETE_DATA;\nimport static org.bonitasoft.engine.tenant.SingleNodeTaskCoordinator.TASK_RECOVERY;\n\nimport org.junit.jupiter.api.Test;\n\nclass SingleNodeTaskCoordinatorLocalTest {\n\n    private final SingleNodeTaskCoordinatorLocal coordinator = new SingleNodeTaskCoordinatorLocal();\n\n    @Test\n    void should_always_be_responsible_for_any_task() {\n        assertThat(coordinator.isResponsibleForTask(TASK_RECOVERY)).isTrue();\n        assertThat(coordinator.isResponsibleForTask(TASK_CLEANUP_OBSOLETE_DATA)).isTrue();\n        assertThat(coordinator.isResponsibleForTask(\"ANY_TASK\")).isTrue();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/tenant/TenantElementsRestartSupervisorLocalTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tenant;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.Test;\n\npublic class TenantElementsRestartSupervisorLocalTest {\n\n    private TenantElementsRestartSupervisorLocal tenantElementsRestarterSupervisorLocal = new TenantElementsRestartSupervisorLocal();\n\n    @Test\n    public void should_say_to_restart_elements_initially() {\n\n        boolean shouldRestartElements = tenantElementsRestarterSupervisorLocal.shouldRestartElements();\n\n        assertThat(shouldRestartElements).isTrue();\n    }\n\n    @Test\n    public void should_say_to_not_restart_elements_after_restart_was_completed() {\n        tenantElementsRestarterSupervisorLocal.willRestartElements();\n\n        boolean shouldRestartElements = tenantElementsRestarterSupervisorLocal.shouldRestartElements();\n\n        assertThat(shouldRestartElements).isFalse();\n    }\n\n    @Test\n    public void should_say_to_restart_elements_after_restart_was_completed_but_tenant_was_stopped() {\n        tenantElementsRestarterSupervisorLocal.willRestartElements();\n        tenantElementsRestarterSupervisorLocal.stop();\n\n        boolean shouldRestartElements = tenantElementsRestarterSupervisorLocal.shouldRestartElements();\n\n        assertThat(shouldRestartElements).isTrue();\n    }\n\n    @Test\n    public void should_say_to_restart_elements_after_restart_was_completed_but_tenant_was_paused() {\n        tenantElementsRestarterSupervisorLocal.willRestartElements();\n        tenantElementsRestarterSupervisorLocal.pause();\n\n        boolean shouldRestartElements = tenantElementsRestarterSupervisorLocal.shouldRestartElements();\n\n        assertThat(shouldRestartElements).isTrue();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/tenant/TenantElementsRestarterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tenant;\n\nimport static org.mockito.Mockito.*;\n\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnit;\nimport org.mockito.junit.MockitoRule;\n\npublic class TenantElementsRestarterTest {\n\n    @Rule\n    public MockitoRule mockitoRule = MockitoJUnit.rule();\n\n    @Mock\n    private TenantElementsRestartSupervisor tenantElementsRestartSupervisor;\n    @Mock\n    private TenantRestarter tenantRestarter;\n    private TenantElementsRestarter tenantElementsRestarter;\n\n    @Before\n    public void before() {\n        tenantElementsRestarter = new TenantElementsRestarter(tenantRestarter, tenantElementsRestartSupervisor);\n    }\n\n    @Test\n    public void should_prepareRestartOfElements_when_the_supervisor_says_so() throws Exception {\n        doReturn(true).when(tenantElementsRestartSupervisor).shouldRestartElements();\n\n        tenantElementsRestarter.prepareRestartOfElements();\n\n        verify(tenantRestarter).executeBeforeServicesStart();\n    }\n\n    @Test\n    public void should_not_prepareRestartOfElements_when_the_supervisor_says_so() throws Exception {\n        doReturn(false).when(tenantElementsRestartSupervisor).shouldRestartElements();\n\n        tenantElementsRestarter.prepareRestartOfElements();\n\n        verify(tenantRestarter, never()).executeBeforeServicesStart();\n    }\n\n    @Test\n    public void should_restartElements_and_notify_when_supervisor_says_so() throws Exception {\n        doReturn(true).when(tenantElementsRestartSupervisor).willRestartElements();\n\n        tenantElementsRestarter.restartElements();\n\n        verify(tenantRestarter).executeAfterServicesStart();\n    }\n\n    @Test\n    public void should_not_restartElements_when_supervisor_says_so() throws Exception {\n        doReturn(false).when(tenantElementsRestartSupervisor).willRestartElements();\n\n        tenantElementsRestarter.restartElements();\n\n        verify(tenantRestarter, never()).executeAfterServicesStart();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/tenant/TenantServicesManagerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tenant;\n\nimport static java.util.Arrays.asList;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport java.util.concurrent.Callable;\n\nimport org.bonitasoft.engine.classloader.ClassLoaderIdentifier;\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.commons.TenantLifecycleService;\nimport org.bonitasoft.engine.commons.exceptions.SLifecycleException;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.bonitasoft.engine.sessionaccessor.SessionIdNotSetException;\nimport org.bonitasoft.engine.transaction.TransactionService;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.mockito.InOrder;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnit;\nimport org.mockito.junit.MockitoRule;\n\npublic class TenantServicesManagerTest {\n\n    public static final long TENANT_ID = 12L;\n    @Rule\n    public MockitoRule mockitoRule = MockitoJUnit.rule();\n    @Mock\n    private TransactionService transactionService;\n    @Mock\n    private TenantLifecycleService tenantService1;\n    @Mock\n    private TenantLifecycleService tenantService2;\n    @Mock\n    private TenantLifecycleService tenantService3;\n    @Mock\n    private TenantElementsRestarter tenantElementsRestarter;\n    @Mock\n    private SessionAccessor sessionAccessor;\n    @Mock\n    private SessionService sessionService;\n    private TenantServicesManager tenantServicesManager;\n    @Mock\n    private ClassLoaderService classLoaderService;\n\n    @Before\n    public void before() throws Exception {\n        when(transactionService.executeInTransaction(any()))\n                .thenAnswer(invocationOnMock -> ((Callable) invocationOnMock.getArgument(0)).call());\n        tenantServicesManager = new TenantServicesManager(sessionAccessor, sessionService, transactionService,\n                classLoaderService,\n                asList(tenantService1, tenantService2, tenantService3), tenantElementsRestarter);\n        doThrow(SessionIdNotSetException.class).when(sessionAccessor).getSessionId();\n    }\n\n    @Test\n    public void should_not_refresh_classloaders_on_start() throws Exception {\n        tenantServicesManager.start();\n\n        verify(classLoaderService).getClassLoader(ClassLoaderIdentifier.TENANT);\n        verifyNoMoreInteractions(classLoaderService);\n    }\n\n    @Test\n    public void should_not_get_classloader_on_pause_and_stop() throws Exception {\n        tenantServicesManager.stop();\n\n        verifyNoMoreInteractions(classLoaderService);\n    }\n\n    @Test\n    public void start_should_start_services_and_resume_elements() throws Exception {\n\n        tenantServicesManager.start();\n\n        InOrder inOrder = inOrder(tenantService1, tenantService2, tenantService3, tenantElementsRestarter);\n        inOrder.verify(tenantElementsRestarter).prepareRestartOfElements();\n        inOrder.verify(tenantService1).start();\n        inOrder.verify(tenantService2).start();\n        inOrder.verify(tenantService3).start();\n        inOrder.verify(tenantElementsRestarter).restartElements();\n    }\n\n    @Test\n    public void resume_should_resume_services_and_resume_elements() throws Exception {\n\n        tenantServicesManager.resume();\n\n        InOrder inOrder = inOrder(tenantService1, tenantService2, tenantService3, tenantElementsRestarter);\n        inOrder.verify(tenantElementsRestarter).prepareRestartOfElements();\n        inOrder.verify(tenantService1).resume();\n        inOrder.verify(tenantService2).resume();\n        inOrder.verify(tenantService3).resume();\n        inOrder.verify(tenantElementsRestarter).restartElements();\n    }\n\n    @Test\n    public void stop_should_stop_services_in_reverse_order() throws Exception {\n        tenantServicesManager.start();\n\n        tenantServicesManager.stop();\n\n        InOrder inOrder = inOrder(tenantService1, tenantService2, tenantService3);\n        inOrder.verify(tenantService3).stop();\n        inOrder.verify(tenantService2).stop();\n        inOrder.verify(tenantService1).stop();\n    }\n\n    @Test\n    public void pause_should_stop_services() throws Exception {\n        tenantServicesManager.start();\n\n        tenantServicesManager.pause();\n\n        InOrder inOrder = inOrder(tenantService1, tenantService2, tenantService3);\n        inOrder.verify(tenantService3).pause();\n        inOrder.verify(tenantService2).pause();\n        inOrder.verify(tenantService1).pause();\n    }\n\n    @Test\n    public void tenant_should_be_started_after_start() throws Exception {\n        tenantServicesManager.start();\n\n        assertThat(tenantServicesManager.isStarted()).isTrue();\n    }\n\n    @Test\n    public void tenant_should_be_stopped_initially() {\n\n        assertThat(tenantServicesManager.isStarted()).isFalse();\n    }\n\n    @Test\n    public void tenant_should_be_stopped_after_stop() throws Exception {\n        tenantServicesManager.start();\n        tenantServicesManager.stop();\n\n        assertThat(tenantServicesManager.isStarted()).isFalse();\n    }\n\n    @Test\n    public void tenant_should_be_started_after_resume() throws Exception {\n        tenantServicesManager.resume();\n\n        assertThat(tenantServicesManager.isStarted()).isTrue();\n    }\n\n    @Test\n    public void tenant_should_be_stopped_after_pause() throws Exception {\n        tenantServicesManager.start();\n        tenantServicesManager.pause();\n\n        assertThat(tenantServicesManager.isStarted()).isFalse();\n    }\n\n    @Test\n    public void should_not_start_tenant_that_is_already_started() throws Exception {\n        tenantServicesManager.start();\n\n        tenantServicesManager.start();\n        tenantServicesManager.start();\n\n        verify(tenantService1, times(1)).start();\n    }\n\n    @Test\n    public void should_not_stop_tenant_that_is_already_stopped() throws Exception {\n        tenantServicesManager.start();\n        tenantServicesManager.stop();\n\n        tenantServicesManager.stop();\n        tenantServicesManager.stop();\n\n        verify(tenantService1, times(1)).stop();\n    }\n\n    @Test\n    public void should_stop_services_when_one_service_fail_to_start() throws Exception {\n        doThrow(new IllegalStateException(\"Unable to start service 2\")).when(tenantService2).start();\n\n        assertThatThrownBy(() -> {\n            tenantServicesManager.start();\n        }).isInstanceOf(SLifecycleException.class);\n\n        verify(tenantService1).start();\n        verify(tenantService3, never()).start();\n\n        verify(tenantService1).stop();\n        verify(tenantService2).stop();\n        verify(tenantService3).stop();\n    }\n\n    @Test\n    public void should_report_exception_of_service_that_fails_to_start() throws Exception {\n        doThrow(new UnsupportedOperationException(\"Unable to start service 1\")).when(tenantService1).start();\n        doThrow(new IllegalStateException(\"Unable to start service 2\")).when(tenantService2).start();\n\n        assertThatThrownBy(() -> {\n            tenantServicesManager.start();\n        }).isInstanceOf(SLifecycleException.class)\n                .hasMessageContaining(\"Unable to START a service. All services are kept STOPPED.\")\n                .hasRootCauseExactlyInstanceOf(UnsupportedOperationException.class);\n    }\n\n    @Test\n    public void should_not_execute_restart_of_elements_when_one_service_fails_to_start() throws Exception {\n        doThrow(new IllegalStateException(\"Unable to start service 2\")).when(tenantService2).start();\n\n        assertThatThrownBy(() -> {\n            tenantServicesManager.start();\n        }).isInstanceOf(SLifecycleException.class);\n\n        verify(tenantElementsRestarter).prepareRestartOfElements();\n        verify(tenantElementsRestarter, never()).restartElements();\n    }\n\n    @Test\n    public void should_be_able_to_stop_services_when_one_service_failed_to_start() throws Exception {\n        doThrow(new IllegalStateException(\"Unable to start service 2\")).when(tenantService2).start();\n        assertThatThrownBy(() -> {\n            tenantServicesManager.start();\n        }).isInstanceOf(SLifecycleException.class);\n\n        tenantServicesManager.stop();\n\n        verify(tenantService1).stop();\n        verify(tenantService2).stop();\n        verify(tenantService3).stop();\n        assertThat(tenantServicesManager.isStarted()).isFalse();\n    }\n\n    @Test\n    public void should_be_able_to_restart_again_services_after_a_failure() throws Exception {\n        doThrow(new IllegalStateException(\"Unable to start service 2\")).doNothing().when(tenantService2).start();\n        assertThatThrownBy(() -> {\n            tenantServicesManager.start();\n        }).isInstanceOf(SLifecycleException.class);\n        assertThat(tenantServicesManager.isStarted()).isFalse();\n\n        tenantServicesManager.start();\n\n        verify(tenantService1, times(2)).start();\n        verify(tenantService2, times(2)).start();\n        verify(tenantService3, times(1)).start();\n        assertThat(tenantServicesManager.isStarted()).isTrue();\n    }\n\n    @Test\n    public void should_have_the_tenant_stopped_when_one_service_did_not_start() throws Exception {\n        doThrow(new IllegalStateException(\"Unable to start service 2\")).when(tenantService2).start();\n\n        assertThatThrownBy(() -> {\n            tenantServicesManager.start();\n        }).isInstanceOf(SLifecycleException.class);\n\n        assertThat(tenantServicesManager.isStarted()).isFalse();\n    }\n\n    @Test\n    public void initServices_should_call_init_on_all_the_services() throws Exception {\n\n        tenantServicesManager.initServices();\n        verify(tenantService1).init();\n        verify(tenantService2).init();\n        verify(tenantService3).init();\n\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/tenant/TenantStateManagerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tenant;\n\nimport static java.util.Collections.singletonMap;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Map;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.TimeUnit;\n\nimport org.bonitasoft.engine.commons.exceptions.SLifecycleException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.platform.PlatformService;\nimport org.bonitasoft.engine.platform.configuration.NodeConfiguration;\nimport org.bonitasoft.engine.platform.exception.SPlatformNotFoundException;\nimport org.bonitasoft.engine.platform.model.SPlatform;\nimport org.bonitasoft.engine.scheduler.SchedulerService;\nimport org.bonitasoft.engine.service.BroadcastService;\nimport org.bonitasoft.engine.service.TaskResult;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.bonitasoft.engine.work.SWorkException;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.mockito.InOrder;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnit;\nimport org.mockito.junit.MockitoRule;\n\npublic class TenantStateManagerTest {\n\n    @Rule\n    public MockitoRule mockitoRule = MockitoJUnit.rule();\n\n    @Mock\n    private BroadcastService broadcastService;\n    @Mock\n    private UserTransactionService userTransactionService;\n    @Mock\n    private SchedulerService schedulerService;\n    @Mock\n    private PlatformService platformService;\n    @Mock\n    private NodeConfiguration nodeConfiguration;\n    @Mock\n    private SessionService sessionService;\n    @Mock\n    private TenantServicesManager tenantServicesManager;\n\n    private TenantStateManager tenantStateManager;\n    private SPlatform platform;\n\n    @Before\n    public void before() throws Exception {\n        when(userTransactionService.executeInTransaction(any()))\n                .thenAnswer(invocationOnMock -> ((Callable) invocationOnMock.getArgument(0)).call());\n        tenantStateManager = new TenantStateManager(userTransactionService,\n                platformService, nodeConfiguration, sessionService,\n                schedulerService, broadcastService, tenantServicesManager);\n        platform = new SPlatform();\n        when(platformService.getPlatform()).thenReturn(platform);\n    }\n\n    private static Map<String, TaskResult<String>> okFuture() {\n        return singletonMap(\"workService\", TaskResult.ok(\"ok\"));\n    }\n\n    @Test\n    public void pause_should_change_state_then_pause_platform_and_jobs() throws Exception {\n        platform.setMaintenanceEnabled(false);\n\n        tenantStateManager.pause();\n\n        InOrder inOrder = inOrder(schedulerService, tenantServicesManager, platformService);\n        inOrder.verify(platformService).pauseServices();\n        inOrder.verify(schedulerService).pauseJobs();\n        inOrder.verify(tenantServicesManager).pause();\n    }\n\n    @Test\n    public void resume_should_resume_services_and_resume_jobs() throws Exception {\n        platform.setMaintenanceEnabled(true);\n\n        tenantStateManager.resume();\n\n        InOrder inOrder = inOrder(platformService, tenantServicesManager, schedulerService);\n        inOrder.verify(platformService).resumeServices();\n        inOrder.verify(tenantServicesManager).resume();\n        inOrder.verify(schedulerService).resumeJobs();\n    }\n\n    @Test\n    public void should_throw_exception_when_resuming_a_platform_not_paused() {\n        platform.setMaintenanceEnabled(false);\n\n        assertThatThrownBy(() -> tenantStateManager.resume())\n                .isInstanceOf(UpdateException.class)\n                .hasMessage(\"Can't resume platform in state RESUMED\");\n    }\n\n    @Test\n    public void should_throw_exception_when_pausing_a_platform_already_paused() {\n        platform.setMaintenanceEnabled(true);\n\n        assertThatThrownBy(() -> tenantStateManager.pause())\n                .isInstanceOf(UpdateException.class)\n                .hasMessage(\"Can't pause platform in state PAUSED\");\n    }\n\n    @Test(expected = UpdateException.class)\n    public void resume_should_throw_UpdateException_when_resuming_platform_service_with_a_lifecycle_timeout()\n            throws Exception {\n        // Given\n        TaskResult<Void> taskResult = new TaskResult<>(5L, TimeUnit.HOURS);\n        doReturn(singletonMap(\"workService\", taskResult)).when(broadcastService).executeOnOthersAndWait(any());\n\n        // When platform moved to available mode\n        tenantStateManager.resume();\n    }\n\n    @Test\n    public void pause_should_throw_SPlatformNotFoundException_on_a_non_existing_platform() throws Exception {\n        doThrow(SPlatformNotFoundException.class).when(platformService).getPlatform();\n\n        assertThatThrownBy(() -> tenantStateManager.pause()).isInstanceOf(SPlatformNotFoundException.class);\n    }\n\n    @Test(expected = UpdateException.class)\n    public void resume_should_throw_UpdateException_when_resuming_service_with_lifecycle_fail()\n            throws Exception {\n        // Given\n        TaskResult<Void> taskResult = new TaskResult<>(new SWorkException(\"plop\"));\n        doReturn(singletonMap(\"workService\", taskResult)).when(broadcastService)\n                .executeOnOthersAndWait(any());\n\n        // When platform moved to available mode\n        tenantStateManager.resume();\n    }\n\n    @Test\n    public void pause_should_update_platform_in_pause() throws Exception {\n        whenPlatformIsInPausedStatus(false);\n        doReturn(okFuture()).when(broadcastService).executeOnOthersAndWait(any());\n\n        tenantStateManager.pause();\n\n        verify(platformService).pauseServices();\n    }\n\n    @Test(expected = UpdateException.class)\n    public void pause_should_throw_UpdateException_on_a_paused_platform() throws Exception {\n        whenPlatformIsInPausedStatus(true);\n\n        tenantStateManager.pause();\n    }\n\n    @Test(expected = UpdateException.class)\n    public void resume_should_throw_UpdateException_on_an_activated_platform() throws Exception {\n        whenPlatformIsInPausedStatus(false);\n\n        tenantStateManager.resume();\n    }\n\n    @Test\n    public void resume_should_keep_platform_paused_on_error() throws Exception {\n        whenPlatformIsInPausedStatus(true);\n        doThrow(SLifecycleException.class).when(tenantServicesManager).resume();\n\n        assertThatThrownBy(() -> tenantStateManager.resume()).isInstanceOf(SLifecycleException.class);\n        verify(platformService).pauseServices();\n    }\n\n    @Test\n    public void resume_should_not_delete_sessions() throws Exception {\n        whenPlatformIsInPausedStatus(true);\n        doReturn(okFuture()).when(broadcastService).executeOnOthersAndWait(any());\n\n        tenantStateManager.resume();\n\n        verify(sessionService, times(0)).deleteSessionsExceptTechnicalUser();\n    }\n\n    private void whenPlatformIsInPausedStatus(final boolean status) throws SPlatformNotFoundException {\n        SPlatform platform = new SPlatform(\"10.3\", \"10.3.0\", \"0.0.0\", null, false, \"platformAdmin\", 999888777L, status);\n        when(platformService.getPlatform()).thenReturn(platform);\n    }\n\n    @Test\n    public void pause_should_update_platform_state_on_activated_platform() throws Exception {\n        // Given\n        whenPlatformIsInPausedStatus(false);\n\n        // When\n        tenantStateManager.pause();\n\n        // Then\n        verify(platformService).pauseServices();\n    }\n\n    @Test\n    public void stop_should_stop_services_only() throws Exception {\n        // given:\n        whenPlatformIsInPausedStatus(false);\n        tenantStateManager.start();\n        doReturn(true).when(nodeConfiguration).shouldClearSessions();\n\n        // when:\n        tenantStateManager.stop();\n\n        // then:\n        InOrder inOrder = inOrder(sessionService, tenantServicesManager);\n        inOrder.verify(sessionService).deleteSessions();\n        inOrder.verify(tenantServicesManager).stop();\n        verify(schedulerService, never()).pauseJobs();\n    }\n\n    @Test\n    public void start_should_call_start_on_ServicesManager() throws Exception {\n        whenPlatformIsInPausedStatus(false);\n\n        tenantStateManager.start();\n\n        verify(tenantServicesManager).start();\n    }\n\n    @Test\n    public void start_should_init_services_even_if_platform_paused() throws Exception {\n        whenPlatformIsInPausedStatus(true);\n\n        tenantStateManager.start();\n\n        verify(tenantServicesManager).initServices();\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/tenant/restart/FlowNodesRecoverTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tenant.restart;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\nimport static org.bonitasoft.engine.execution.TestFlowNodeState.stableState;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.model.*;\nimport org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance;\nimport org.bonitasoft.engine.execution.state.FlowNodeStateManager;\nimport org.bonitasoft.engine.execution.work.BPMWorkFactory;\nimport org.bonitasoft.engine.work.SWorkRegisterException;\nimport org.bonitasoft.engine.work.WorkService;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class FlowNodesRecoverTest {\n\n    private static final int ABORTING_STATE_ID = 2;\n    private static final int CANCELLING_STATE_ID = 3;\n\n    @Mock\n    private FlowNodeStateManager flownodeStateManager;\n    @Mock\n    private ActivityInstanceService activityInstanceService;\n    @Mock\n    private WorkService workService;\n    @Mock\n    private RecoveryMonitor recoveryMonitor;\n\n    private final BPMWorkFactory workFactory = new BPMWorkFactory();\n    private FlowNodesRecover flowNodesRecover;\n    private final List<SFlowNodeInstance> allFlowNodes = new ArrayList<>();\n\n    private final FlowNodeState waitingState = stableState(1, SStateCategory.NORMAL);\n    private final FlowNodeState abortingState = stableState(ABORTING_STATE_ID, SStateCategory.ABORTING);\n    private final FlowNodeState cancellingState = stableState(CANCELLING_STATE_ID, SStateCategory.CANCELLING);\n    private final FlowNodeState normalStableState = stableState(4, SStateCategory.NORMAL);\n\n    @Before\n    public void before() throws Exception {\n        flowNodesRecover = new FlowNodesRecover(workService, activityInstanceService,\n                flownodeStateManager, workFactory);\n        when(activityInstanceService.getFlowNodeInstancesByIds(any())).thenAnswer(invocationOnMock -> {\n            List<Long> ids = invocationOnMock.getArgument(0);\n            return allFlowNodes.stream().filter(f -> ids.contains(f.getId())).collect(Collectors.toList());\n        });\n        doReturn(waitingState).when(flownodeStateManager).getState(waitingState.getId());\n        doReturn(abortingState).when(flownodeStateManager).getState(abortingState.getId());\n        doReturn(cancellingState).when(flownodeStateManager).getState(cancellingState.getId());\n        doReturn(normalStableState).when(flownodeStateManager).getState(normalStableState.getId());\n    }\n\n    @Test\n    public final void should_register_FINISH_FLOWNODE_when_flownode_is_in_terminal_state() throws Exception {\n        SAutomaticTaskInstance task = createTask(123L, true /* terminal */);\n\n        flowNodesRecover.execute(recoveryMonitor, flowNodeIds(task));\n\n        verify(workService).registerWork(argThat(work -> work.getType().equals(\"FINISH_FLOWNODE\")));\n\n    }\n\n    @Test\n    public void should_register_EXECUTE_FLOWNODE_when_flownode_is_not_in_terminal_state() throws Exception {\n        SAutomaticTaskInstance task = createTask(123L, false /* not terminal */);\n\n        flowNodesRecover.execute(recoveryMonitor, flowNodeIds(task));\n\n        verify(workService).registerWork(argThat(work -> work.getType().equals(\"EXECUTE_FLOWNODE\")));\n    }\n\n    @Test\n    public void should_register_work_for_each_flow_nodes_executed() throws Exception {\n        List<SFlowNodeInstance> list = new ArrayList<>();\n        for (int i = 1; i <= 21; i++) {\n            list.add(createTask(123L + i, false));\n        }\n\n        flowNodesRecover.execute(recoveryMonitor, flowNodeIds(list.toArray(new SFlowNodeInstance[] {})));\n\n        assertThat(list.size()).isEqualTo(21);\n        verify(workService, times(21)).registerWork(argThat(work -> work.getType().equals(\"EXECUTE_FLOWNODE\")));\n    }\n\n    @Test\n    public void should_execute_flownode_that_is_aborting_non_terminal_and_stable_when_the_FlowNodeState_is_not_in_the_same_state_category()\n            throws Exception {\n        SAutomaticTaskInstance autoTask = createTask(123L, false);\n        setState(autoTask, normalStableState);\n        autoTask.setStateCategory(SStateCategory.ABORTING);\n\n        flowNodesRecover.execute(recoveryMonitor, flowNodeIds(autoTask));\n\n        verify(workService).registerWork(argThat(work -> work.getType().equals(\"EXECUTE_FLOWNODE\")));\n    }\n\n    @Test\n    public final void should_execute_flownode_that_is_cancelling_non_terminal_and_stable_when_the_FlowNodeState_is_not_in_the_same_state_category()\n            throws Exception {\n        SAutomaticTaskInstance autoTask = createTask(123L, false);\n        setState(autoTask, normalStableState);\n        autoTask.setStateCategory(SStateCategory.CANCELLING);\n\n        flowNodesRecover.execute(recoveryMonitor, flowNodeIds(autoTask));\n\n        verify(workService).registerWork(argThat(work -> work.getType().equals(\"EXECUTE_FLOWNODE\")));\n    }\n\n    @Test\n    public final void should_not_execute_flownode_that_is_aborting_non_terminal_and_stable_when_the_FlowNodeState_is_in_the_same_state_category()\n            throws Exception {\n        SAutomaticTaskInstance autoTask = createTask(123L, false);\n        autoTask.setStateCategory(SStateCategory.ABORTING);\n        autoTask.setTerminal(false);\n        autoTask.setStable(true);\n        autoTask.setStateId(ABORTING_STATE_ID);\n\n        flowNodesRecover.execute(recoveryMonitor, flowNodeIds(autoTask));\n\n        verify(workService, never()).registerWork(any());\n    }\n\n    @Test\n    public final void should_not_execute_flownode_that_is_cancelling_non_terminal_and_stable_when_the_FlowNodeState_is_in_the_same_state_category()\n            throws Exception {\n        SAutomaticTaskInstance autoTask = createTask(123L, false);\n        autoTask.setStateCategory(SStateCategory.CANCELLING);\n        autoTask.setTerminal(false);\n        autoTask.setStable(true);\n        autoTask.setStateId(CANCELLING_STATE_ID);\n\n        flowNodesRecover.execute(recoveryMonitor, flowNodeIds(autoTask));\n\n        verify(workService, never()).registerWork(any());\n    }\n\n    @Test\n    public void should_execute_gateway_when_gateway_is_flagged_as_finished() throws Exception {\n        SGatewayInstance gatewayInstance = new SGatewayInstance();\n        gatewayInstance.setId(333L);\n        gatewayInstance.setHitBys(\"FINISH:2\");\n\n        flowNodesRecover.execute(recoveryMonitor, flowNodeIds(gatewayInstance));\n\n        verify(workService).registerWork(argThat(work -> work.getType().equals(\"EXECUTE_FLOWNODE\")));\n    }\n\n    @Test\n    public void should_not_execute_gateway_when_gateway_has_still_an_active_branch() throws Exception {\n        SGatewayInstance gatewayInstance = new SGatewayInstance();\n        gatewayInstance.setId(333L);\n        gatewayInstance.setHitBys(\"1,2\");\n\n        flowNodesRecover.execute(recoveryMonitor, flowNodeIds(gatewayInstance));\n\n        verify(workService, never()).registerWork(argThat(work -> work.getType().equals(\"EXECUTE_FLOWNODE\")));\n    }\n\n    @Test\n    public void should_fail_batch_if_one_register_work_fails() throws Exception {\n        SAutomaticTaskInstance automaticTaskInstance1 = new SAutomaticTaskInstance();\n        automaticTaskInstance1.setId(333L);\n        automaticTaskInstance1.setTerminal(true);\n        SAutomaticTaskInstance automaticTaskInstance2 = new SAutomaticTaskInstance();\n        automaticTaskInstance2.setId(344L);\n        automaticTaskInstance2.setTerminal(true);\n        doThrow(SWorkRegisterException.class).when(workService)\n                .registerWork(argThat(work -> work.getType().equals(\"FINISH_FLOWNODE\")\n                        && work.getParameter(\"flowNodeInstanceId\").equals(333L)));\n\n        assertThatThrownBy(() -> flowNodesRecover.execute(recoveryMonitor,\n                flowNodeIds(automaticTaskInstance1, automaticTaskInstance2)))\n                .isInstanceOf(SWorkRegisterException.class);\n    }\n\n    @Test\n    public void should_not_execute_gateway_when_merging_condition_is_false() throws Exception {\n        SGatewayInstance gatewayInstance = new SGatewayInstance();\n        gatewayInstance.setId(333L);\n\n        flowNodesRecover.execute(recoveryMonitor, flowNodeIds(gatewayInstance));\n\n        verify(workService, never()).registerWork(any());\n    }\n\n    @Test\n    public void should_not_execute_gateway_that_is_not_finished() throws Exception {\n        SGatewayInstance gatewayInstance = new SGatewayInstance();\n\n        boolean shouldExecuteFlownode = flowNodesRecover.shouldBeRecovered(gatewayInstance);\n\n        assertThat(shouldExecuteFlownode).isFalse();\n    }\n\n    @Test\n    public void should_execute_flow_node_in_CANCELLING_when_state_is_stable_but_not_in_the_same_stateCategory()\n            throws Exception {\n        SBoundaryEventInstance boundaryEventInstance = new SBoundaryEventInstance();\n        boundaryEventInstance.setStateCategory(SStateCategory.CANCELLING);\n        setState(boundaryEventInstance, waitingState);\n\n        assertThat(flowNodesRecover.shouldBeRecovered(boundaryEventInstance)).isTrue();\n    }\n\n    @Test\n    public void should_not_execute_flow_node_in_CANCELLING_when_state_is_stable_but_in_the_same_stateCategory()\n            throws Exception {\n        SCallActivityInstance boundaryEventInstance = new SCallActivityInstance();\n        boundaryEventInstance.setStateCategory(SStateCategory.CANCELLING);\n        setState(boundaryEventInstance, cancellingState);\n\n        assertThat(flowNodesRecover.shouldBeRecovered(boundaryEventInstance)).isFalse();\n    }\n\n    @Test\n    public void should_execute_flow_node_in_ABORTING_when_state_is_stable_but_not_in_the_same_stateCategory()\n            throws Exception {\n        SBoundaryEventInstance boundaryEventInstance = new SBoundaryEventInstance();\n        boundaryEventInstance.setStateCategory(SStateCategory.ABORTING);\n        setState(boundaryEventInstance, waitingState);\n\n        assertThat(flowNodesRecover.shouldBeRecovered(boundaryEventInstance)).isTrue();\n    }\n\n    @Test\n    public void should_not_execute_flow_node_in_ABORTING_when_state_is_stable_but_in_the_same_stateCategory()\n            throws Exception {\n        SBoundaryEventInstance boundaryEventInstance = new SBoundaryEventInstance();\n        boundaryEventInstance.setStateCategory(SStateCategory.ABORTING);\n        setState(boundaryEventInstance, abortingState);\n\n        assertThat(flowNodesRecover.shouldBeRecovered(boundaryEventInstance)).isFalse();\n    }\n\n    @Test\n    public void should_execute_gateway_when_stateCategory_is_ABORTING() throws Exception {\n        SGatewayInstance gatewayInstance = new SGatewayInstance();\n        gatewayInstance.setId(333L);\n        gatewayInstance.setStateCategory(SStateCategory.ABORTING);\n\n        flowNodesRecover.execute(recoveryMonitor, flowNodeIds(gatewayInstance));\n\n        verify(workService).registerWork(argThat(work -> work.getType().equals(\"EXECUTE_FLOWNODE\")));\n    }\n\n    @Test\n    public void should_execute_gateway_when_stateCategory_is_CANCELLING() throws Exception {\n        SGatewayInstance gatewayInstance = new SGatewayInstance();\n        gatewayInstance.setId(333L);\n        gatewayInstance.setStateCategory(SStateCategory.CANCELLING);\n\n        flowNodesRecover.execute(recoveryMonitor, flowNodeIds(gatewayInstance));\n\n        verify(workService).registerWork(argThat(work -> work.getType().equals(\"EXECUTE_FLOWNODE\")));\n    }\n\n    private void setState(SFlowNodeInstance flowNodeInstance, FlowNodeState state) {\n        flowNodeInstance.setStateId(state.getId());\n        flowNodeInstance.setStable(state.isStable());\n        flowNodeInstance.setTerminal(state.isTerminal());\n    }\n\n    private List<Long> flowNodeIds(final SFlowNodeInstance... flowNodes) {\n        ArrayList<Long> nodes = new ArrayList<>();\n        for (SFlowNodeInstance node : flowNodes) {\n            nodes.add(node.getId());\n            allFlowNodes.add(node);\n        }\n        return nodes;\n    }\n\n    private SAutomaticTaskInstance createTask(final long id, final boolean terminal) {\n        SAutomaticTaskInstance sAutomaticTaskInstance = new SAutomaticTaskInstance();\n        sAutomaticTaskInstance.setId(id);\n        sAutomaticTaskInstance.setTerminal(terminal);\n        sAutomaticTaskInstance.setLogicalGroup(3, 456L);\n        return sAutomaticTaskInstance;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/tenant/restart/MessagesRestartHandlerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tenant.restart;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.verify;\n\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceRepository;\nimport org.bonitasoft.engine.message.MessagesHandlingService;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.SystemOutRule;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class MessagesRestartHandlerTest {\n\n    @Rule\n    public final SystemOutRule systemOutRule = new SystemOutRule().enableLog().muteForSuccessfulTests();\n    @Mock\n    private UserTransactionService userTransactionService;\n    @Mock\n    private EventInstanceRepository eventInstanceRepository;\n    @Mock\n    private MessagesHandlingService messagesHandlingService;\n    @InjectMocks\n    @Spy\n    private MessagesRestartHandler messagesRestartHandler;\n\n    @Test\n    public void handleRestartShouldLog4Infos() throws Exception {\n        // when:\n        systemOutRule.clearLog();\n        messagesRestartHandler.beforeServicesStart();\n        // then:\n        assertThat(systemOutRule.getLog())\n                .containsPattern(\n                        \"INFO.*.MessagesRestartHandler.*Reinitializing message instances in non-stable state \" +\n                                \"to make them reworked by MessagesHandlingService\")\n                .containsPattern(\"INFO.*.MessagesRestartHandler.*.message instances found and reset.\")\n                .containsPattern(\n                        \"INFO.*.MessagesRestartHandler.*.Reinitializing waiting message events in non-stable \" +\n                                \"state to make them reworked by MessagesHandlingService\")\n                .containsPattern(\"INFO.*.MessagesRestartHandler.*.waiting message events found and reset\");\n    }\n\n    @Test\n    public void handleRestartShouldResetMessageInstances() throws Exception {\n        // when:\n        messagesRestartHandler.beforeServicesStart();\n\n        // then:\n        verify(eventInstanceRepository).resetProgressMessageInstances();\n    }\n\n    @Test\n    public void handleRestartShouldResetWaitingEvents() throws Exception {\n        // when:\n        messagesRestartHandler.beforeServicesStart();\n\n        // then:\n        verify(eventInstanceRepository).resetInProgressWaitingEvents();\n    }\n\n    @Test\n    public void should_execute_event_handling_in_transaction() throws Exception {\n        //given\n\n        //when\n        messagesRestartHandler.afterServicesStart();\n        //then\n        verify(userTransactionService).executeInTransaction(any());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/tenant/restart/ProcessesRecoverTest.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tenant.restart;\n\nimport static java.util.Collections.singletonList;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.bpm.process.ProcessInstanceState.*;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.*;\n\nimport java.util.concurrent.Callable;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.execution.ProcessExecutor;\nimport org.bonitasoft.engine.execution.state.FailedActivityState;\nimport org.bonitasoft.engine.execution.state.FlowNodeStateManager;\nimport org.bonitasoft.engine.execution.work.BPMWorkFactory;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.bonitasoft.engine.work.WorkService;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.SystemOutRule;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnit;\nimport org.mockito.junit.MockitoRule;\n\npublic class ProcessesRecoverTest {\n\n    public static final long CALLER_ID = 55L;\n    public static final long PROCESS_INSTANCE_ID = 42L;\n    @Rule\n    public MockitoRule mockitoRule = MockitoJUnit.rule();\n    @Mock\n    private FlowNodeStateManager flowNodeStateManager;\n    @Mock\n    private WorkService workService;\n    @Mock\n    private ActivityInstanceService activityInstanceService;\n    @Mock\n    private ProcessDefinitionService processDefinitionService;\n    @Mock\n    private ProcessInstanceService processInstanceService;\n    @Mock\n    private ProcessExecutor processExecutor;\n    @Mock\n    private UserTransactionService userTransactionService;\n    private ProcessesRecover processesRecover;\n\n    private RecoveryMonitor recoveryMonitor;\n    @Rule\n    public SystemOutRule systemOutRule = new SystemOutRule().enableLog();\n\n    @Before\n    public void setUp() throws Exception {\n        recoveryMonitor = new RecoveryMonitor();\n        recoveryMonitor.startNow(100);\n        processesRecover = new ProcessesRecover(workService, activityInstanceService,\n                processDefinitionService, processInstanceService, processExecutor, new BPMWorkFactory());\n        doAnswer(args -> ((Callable) args.getArgument(0)).call()).when(userTransactionService)\n                .executeInTransaction(any());\n    }\n\n    @Test\n    public void should_not_execute_process_with_no_caller_id() throws Exception {\n        havingProcessInstance(COMPLETED, -1);\n\n        processesRecover.execute(recoveryMonitor, singletonList(PROCESS_INSTANCE_ID));\n\n        // as it can never happen, because when a process instance goes into COMPLETED state, it is archived\n        // in the same transaction\n        verifyNoMoreInteractions(workService);\n    }\n\n    @Test\n    public void should_not_execute_a_process_called_by_a_failed_call_activity() throws Exception {\n        havingCallActivity(FlowNodeState.ID_ACTIVITY_FAILED, CALLER_ID);\n        havingProcessInstance(COMPLETED, CALLER_ID);\n        when(flowNodeStateManager.getState(FlowNodeState.ID_ACTIVITY_FAILED)).thenReturn(new FailedActivityState());\n\n        processesRecover.execute(recoveryMonitor, singletonList(PROCESS_INSTANCE_ID));\n\n        verifyNoMoreInteractions(workService);\n        assertThat(systemOutRule.getLog().toLowerCase()).doesNotContain(\"error\");\n    }\n\n    @Test\n    public void should_execute_process_called_by_a_call_activity() throws Exception {\n        havingCallActivity(FlowNodeState.ID_ACTIVITY_EXECUTING, CALLER_ID);\n        havingProcessInstance(COMPLETED, CALLER_ID);\n        when(flowNodeStateManager.getState(FlowNodeState.ID_ACTIVITY_FAILED)).thenReturn(new FailedActivityState());\n\n        processesRecover.execute(recoveryMonitor, singletonList(PROCESS_INSTANCE_ID));\n\n        verify(workService)\n                .registerWork(argThat(workDescriptor -> workDescriptor.getType().equals(\"EXECUTE_FLOWNODE\") &&\n                        workDescriptor.getParameter(\"flowNodeInstanceId\").equals(CALLER_ID)));\n    }\n\n    @Test\n    public void should_execute_on_enter_connector_of_process_instance() throws Exception {\n        SProcessInstance processInstance = havingProcessInstance(INITIALIZING, -1);\n\n        processesRecover.execute(recoveryMonitor, singletonList(PROCESS_INSTANCE_ID));\n\n        verify(processExecutor).registerConnectorsToExecute(any(), eq(processInstance), eq(ConnectorEvent.ON_ENTER),\n                any());\n    }\n\n    @Test\n    public void should_execute_on_finish_connector_of_process_instance() throws Exception {\n        SProcessInstance processInstance = havingProcessInstance(COMPLETING, -1);\n\n        processesRecover.execute(recoveryMonitor, singletonList(PROCESS_INSTANCE_ID));\n\n        verify(processExecutor).registerConnectorsToExecute(any(), eq(processInstance), eq(ConnectorEvent.ON_FINISH),\n                any());\n    }\n\n    protected void havingCallActivity(int stateId, long id)\n            throws SActivityInstanceNotFoundException, SActivityReadException {\n        SCallActivityInstance caller = new SCallActivityInstance();\n        caller.setStateId(stateId);\n        caller.setId(id);\n        doReturn(caller).when(activityInstanceService).getActivityInstance(id);\n    }\n\n    protected SProcessInstance havingProcessInstance(ProcessInstanceState state, long callerId)\n            throws SProcessInstanceNotFoundException, SProcessInstanceReadException {\n        SProcessInstance processInstance = new SProcessInstance();\n        processInstance.setStateId(state.getId());\n        processInstance.setCallerId(callerId);\n        processInstance.setId(PROCESS_INSTANCE_ID);\n        doReturn(processInstance).when(processInstanceService).getProcessInstance(PROCESS_INSTANCE_ID);\n        return processInstance;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/tenant/restart/RecoveryHandlerTest.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tenant.restart;\n\nimport static java.util.Arrays.asList;\nimport static org.bonitasoft.engine.tenant.restart.ElementToRecover.Type.FLOWNODE;\nimport static org.bonitasoft.engine.tenant.restart.ElementToRecover.Type.PROCESS;\nimport static org.bonitasoft.engine.tenant.restart.ElementToRecover.builder;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.verify;\n\nimport java.time.Duration;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@ExtendWith(MockitoExtension.class)\nclass RecoveryHandlerTest {\n\n    @Mock\n    private RecoveryService recoveryService;\n\n    @InjectMocks\n    private RecoveryHandler recoveryHandler;\n\n    @Test\n    void should_delegate_to_ProcessInstancesRecoveryService() {\n        final List<ElementToRecover> elementToRecovers = asList(\n                builder().id(17L).type(PROCESS).build(),\n                builder().id(44L).type(FLOWNODE).build());\n        doReturn(elementToRecovers).when(recoveryService).getAllElementsToRecover(Duration.ZERO);\n\n        recoveryHandler.beforeServicesStart();\n        recoveryHandler.afterServicesStart();\n\n        verify(recoveryService).recover(elementToRecovers);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/tenant/restart/RecoverySchedulerTest.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tenant.restart;\n\nimport static org.mockito.Mockito.doThrow;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport org.bonitasoft.engine.tenant.SingleNodeTaskCoordinator;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\n@ExtendWith(MockitoExtension.class)\nclass RecoverySchedulerTest {\n\n    @Mock\n    SingleNodeTaskCoordinator singleNodeTaskCoordinator;\n    @Mock\n    RecoveryService recoveryService;\n    @InjectMocks\n    RecoveryScheduler recoveryScheduler;\n\n    @Test\n    void should_recover_elements_when_node_is_responsible_for_recovery() {\n        when(singleNodeTaskCoordinator.isResponsibleForTask(SingleNodeTaskCoordinator.TASK_RECOVERY)).thenReturn(true);\n\n        recoveryScheduler.triggerRecoveryOfAllElements();\n\n        verify(recoveryService).recoverAllElements();\n    }\n\n    @Test\n    void should_not_recover_elements_when_node_is_not_responsible_for_recovery() {\n        when(singleNodeTaskCoordinator.isResponsibleForTask(SingleNodeTaskCoordinator.TASK_RECOVERY)).thenReturn(false);\n\n        recoveryScheduler.triggerRecoveryOfAllElements();\n\n        verify(recoveryService, never()).recoverAllElements();\n    }\n\n    @Test\n    void should_catch_exception_on_error_during_call_of_isResponsibleForRecovery() {\n        when(singleNodeTaskCoordinator.isResponsibleForTask(SingleNodeTaskCoordinator.TASK_RECOVERY))\n                .thenThrow(new IllegalStateException(\"BAD\"));\n\n        recoveryScheduler.triggerRecoveryOfAllElements();\n\n        verify(recoveryService, never()).recoverAllElements();\n    }\n\n    @Test\n    void should_catch_exception_on_error_during_calls_of_triggerRecoveryAllElements() {\n        when(singleNodeTaskCoordinator.isResponsibleForTask(SingleNodeTaskCoordinator.TASK_RECOVERY)).thenReturn(true);\n        doThrow(new IllegalStateException(\"BAD\")).when(recoveryService).recoverAllElements();\n\n        recoveryScheduler.triggerRecoveryOfAllElements();\n\n        //no exception\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/tenant/restart/RecoveryServiceTest.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tenant.restart;\n\nimport static java.util.Arrays.asList;\nimport static java.util.Collections.emptyList;\nimport static java.util.Collections.singletonList;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.tenant.restart.ElementToRecover.Type.FLOWNODE;\nimport static org.bonitasoft.engine.tenant.restart.ElementToRecover.Type.PROCESS;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.*;\n\nimport java.time.Duration;\nimport java.util.List;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport io.micrometer.core.instrument.Counter;\nimport io.micrometer.core.instrument.Gauge;\nimport io.micrometer.core.instrument.LongTaskTimer;\nimport io.micrometer.core.instrument.MeterRegistry;\nimport io.micrometer.core.instrument.simple.SimpleMeterRegistry;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.InOrder;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport org.springframework.beans.factory.ObjectFactory;\n\n@ExtendWith(MockitoExtension.class)\nclass RecoveryServiceTest {\n\n    @Mock\n    private FlowNodeInstanceService flowNodeInstanceService;\n    @Mock\n    private ProcessInstanceService processInstanceService;\n    @Mock(lenient = true)\n    private UserTransactionService userTransactionService;\n    @Mock\n    private FlowNodesRecover flowNodesRecover;\n    @Mock\n    private ProcessesRecover processesRecover;\n    @Mock\n    private SessionAccessor sessionAccessor;\n    @Mock(lenient = true)\n    private ObjectFactory<RecoveryMonitor> recoveryMonitorObjectFactory;\n    @Mock\n    private RecoveryMonitor recoveryMonitor;\n    @Spy\n    MeterRegistry meterRegistry = new SimpleMeterRegistry();\n\n    @InjectMocks\n    private RecoveryService recoveryService;\n\n    @BeforeEach\n    public void before() throws Exception {\n        recoveryService.initMetrics();\n        doReturn(recoveryMonitor).when(recoveryMonitorObjectFactory).getObject();\n        recoveryService.setReadBatchSize(2);\n        recoveryService.setBatchRestartSize(1000);\n        recoveryService.setConsiderElementsOlderThan(Duration.ofMillis(1000));\n        when(userTransactionService.executeInTransaction(any()))\n                .thenAnswer(invocationOnMock -> ((Callable) invocationOnMock.getArgument(0)).call());\n\n    }\n\n    @Test\n    void should_return_list_of_elements_to_recover() throws Exception {\n        doReturn(asList(1L, 2L)).doReturn(emptyList()).when(flowNodeInstanceService)\n                .getFlowNodeInstanceIdsToRecover(eq(Duration.ZERO), any());\n        doReturn(asList(3L, 4L)).doReturn(emptyList()).when(flowNodeInstanceService)\n                .getGatewayInstanceIdsToRecover(eq(Duration.ZERO), any());\n        doReturn(asList(1L, 2L)).doReturn(emptyList())\n                .when(processInstanceService).getProcessInstanceIdsToRecover(eq(Duration.ZERO), any());\n\n        List<ElementToRecover> allElementsToRecover = recoveryService\n                .getAllElementsToRecover(Duration.ZERO);\n\n        assertThat(allElementsToRecover).containsExactlyInAnyOrder(\n                elementToRecover(1L, PROCESS), elementToRecover(2L, PROCESS),\n                elementToRecover(1L, FLOWNODE), elementToRecover(2L, FLOWNODE),\n                elementToRecover(3L, FLOWNODE), elementToRecover(4L, FLOWNODE));\n    }\n\n    @Test\n    void should_return_all_elements_even_if_over_page_size() throws SBonitaException {\n        doReturn(asList(1L, 2L))\n                .doReturn(singletonList(4L))\n                .when(processInstanceService).getProcessInstanceIdsToRecover(eq(Duration.ZERO), any());\n        doReturn(asList(7L, 9L))\n                .doReturn(singletonList(13L))\n                .when(flowNodeInstanceService).getFlowNodeInstanceIdsToRecover(eq(Duration.ZERO), any());\n\n        List<ElementToRecover> allElementsToRecover = recoveryService\n                .getAllElementsToRecover(Duration.ZERO);\n\n        assertThat(allElementsToRecover).containsExactlyInAnyOrder(\n                elementToRecover(1L, PROCESS), elementToRecover(2L, PROCESS), elementToRecover(4L, PROCESS),\n                elementToRecover(7L, FLOWNODE), elementToRecover(9L, FLOWNODE), elementToRecover(13L, FLOWNODE));\n    }\n\n    @Test\n    void should_recover_elements_provided() throws Exception {\n        recoveryService.recover(asList(\n                elementToRecover(1L, PROCESS),\n                elementToRecover(2L, PROCESS),\n                elementToRecover(4L, PROCESS),\n                elementToRecover(7L, FLOWNODE),\n                elementToRecover(9L, FLOWNODE),\n                elementToRecover(13L, FLOWNODE)));\n\n        verify(flowNodesRecover).execute(any(), eq(asList(7L, 9L, 13L)));\n        verify(processesRecover).execute(any(), eq(asList(1L, 2L, 4L)));\n    }\n\n    @Test\n    void should_recover_all_elements_older_than() throws Exception {\n        Duration considerElementsOlderThan = Duration.ofSeconds(10);\n        recoveryService.setConsiderElementsOlderThan(considerElementsOlderThan);\n\n        doReturn(asList(1L, 2L))\n                .doReturn(singletonList(4L))\n                .when(processInstanceService).getProcessInstanceIdsToRecover(eq(considerElementsOlderThan), any());\n        doReturn(asList(7L, 9L))\n                .doReturn(singletonList(13L))\n                .when(flowNodeInstanceService).getFlowNodeInstanceIdsToRecover(eq(considerElementsOlderThan), any());\n\n        recoveryService.recoverAllElements();\n\n        verify(flowNodesRecover).execute(any(), eq(asList(7L, 9L, 13L)));\n        verify(processesRecover).execute(any(), eq(asList(1L, 2L, 4L)));\n    }\n\n    @Test\n    void should_measure_duration_of_recovery() throws Exception {\n        doReturn(singletonList(1L))\n                .doReturn(singletonList(4L))\n                .when(processInstanceService).getProcessInstanceIdsToRecover(any(), any());\n        LongTaskTimer longTaskTimer = meterRegistry.find(RecoveryService.DURATION_OF_RECOVERY_TASK).longTaskTimer();\n        AtomicInteger activeTasks = new AtomicInteger();\n        AtomicLong durationMillis = new AtomicLong();\n\n        doAnswer(invocationOnMock -> {\n            // do the measure on the long task timer while the recovery is executing (that mock is executed by the recovery)\n            Thread.sleep(2);\n            activeTasks.set(longTaskTimer.activeTasks());\n            durationMillis.set(Math.round(longTaskTimer.duration(TimeUnit.MILLISECONDS)));\n            return null;\n        }).when(processesRecover).execute(any(), any());\n\n        recoveryService.recoverAllElements();\n\n        //during execution of the task, it was:\n        assertThat(activeTasks.get()).isEqualTo(1);\n        assertThat(durationMillis.get()).isGreaterThan(0);\n\n        //when no tasks are being executed\n        assertThat(longTaskTimer.activeTasks()).isEqualTo(0);\n    }\n\n    @Test\n    void should_measure_number_of_element_recovered() {\n        Gauge elementsRecoveredLast = meterRegistry.find(RecoveryService.NUMBER_OF_ELEMENTS_RECOVERED_LAST_RECOVERY)\n                .gauge();\n        Counter elementsRecoveredTotal = meterRegistry.find(RecoveryService.NUMBER_OF_ELEMENTS_RECOVERED_TOTAL)\n                .counter();\n        Counter numberOfExecution = meterRegistry.find(RecoveryService.NUMBER_OF_RECOVERY).counter();\n\n        doReturn(5L, 2L, 6L).when(recoveryMonitor).getNumberOfElementRecovered();\n\n        recoveryService.recoverAllElements();\n\n        assertThat(elementsRecoveredLast.value()).isEqualTo(5);\n        assertThat(elementsRecoveredTotal.count()).isEqualTo(5);\n        assertThat(numberOfExecution.count()).isEqualTo(1);\n\n        recoveryService.recoverAllElements();\n\n        assertThat(elementsRecoveredLast.value()).isEqualTo(2);\n        assertThat(elementsRecoveredTotal.count()).isEqualTo(7);\n        assertThat(numberOfExecution.count()).isEqualTo(2);\n\n        recoveryService.recoverAllElements();\n\n        assertThat(elementsRecoveredLast.value()).isEqualTo(6);\n        assertThat(elementsRecoveredTotal.count()).isEqualTo(13);\n        assertThat(numberOfExecution.count()).isEqualTo(3);\n    }\n\n    private ElementToRecover elementToRecover(long l, ElementToRecover.Type process) {\n        return ElementToRecover.builder().id(l).type(process).build();\n    }\n\n    @Test\n    void should_restart_flownodes_in_batch() throws Exception {\n        recoveryService.setBatchRestartSize(2);\n\n        recoveryService.recover(asList(\n                elementToRecover(1L, FLOWNODE),\n                elementToRecover(2L, FLOWNODE),\n                elementToRecover(3L, FLOWNODE),\n                elementToRecover(4L, FLOWNODE),\n                elementToRecover(5L, FLOWNODE)));\n\n        verify(flowNodesRecover).execute(any(), eq(asList(1L, 2L)));\n        verify(flowNodesRecover).execute(any(), eq(asList(3L, 4L)));\n        verify(flowNodesRecover).execute(any(), eq(singletonList(5L)));\n        verify(userTransactionService, times(3)).executeInTransaction(any());\n    }\n\n    @Test\n    void should_restart_processes_in_batch() throws Exception {\n        recoveryService.setBatchRestartSize(2);\n\n        recoveryService.recover(asList(\n                elementToRecover(1L, PROCESS),\n                elementToRecover(2L, PROCESS),\n                elementToRecover(3L, PROCESS),\n                elementToRecover(4L, PROCESS),\n                elementToRecover(5L, PROCESS)));\n\n        verify(processesRecover).execute(any(), eq(asList(1L, 2L)));\n        verify(processesRecover).execute(any(), eq(asList(3L, 4L)));\n        verify(processesRecover).execute(any(), eq(singletonList(5L)));\n        verify(userTransactionService, times(3)).executeInTransaction(any());\n    }\n\n    @Test\n    void should_continue_to_restart_process_even_if_one_batch_failed() throws Exception {\n        doThrow(new UnsupportedOperationException(\"current batch failed, sorry ¯\\\\_(ツ)_/¯\"))\n                .doAnswer(invocationOnMock -> ((Callable) invocationOnMock.getArgument(0)).call())\n                .when(userTransactionService).executeInTransaction(any());\n        recoveryService.setBatchRestartSize(2);\n\n        recoveryService.recover(asList(\n                elementToRecover(1L, PROCESS),\n                elementToRecover(2L, PROCESS),\n                elementToRecover(3L, PROCESS),\n                elementToRecover(4L, PROCESS),\n                elementToRecover(5L, PROCESS)));\n\n        verify(processesRecover, never()).execute(any(), eq(asList(1L, 2L)));// transaction fail there\n        verify(processesRecover).execute(any(), eq(asList(3L, 4L)));\n        verify(processesRecover).execute(any(), eq(singletonList(5L)));\n        verify(userTransactionService, times(3)).executeInTransaction(any());\n    }\n\n    @Test\n    void should_continue_to_restart_flow_node_even_if_one_batch_failed() throws Exception {\n        doThrow(new UnsupportedOperationException(\"current batch failed, sorry ¯\\\\_(ツ)_/¯\"))\n                .doAnswer(invocationOnMock -> ((Callable) invocationOnMock.getArgument(0)).call())\n                .when(userTransactionService).executeInTransaction(any());\n        recoveryService.setBatchRestartSize(2);\n\n        recoveryService.recover(asList(\n                elementToRecover(1L, FLOWNODE),\n                elementToRecover(2L, FLOWNODE),\n                elementToRecover(3L, FLOWNODE),\n                elementToRecover(4L, FLOWNODE),\n                elementToRecover(5L, FLOWNODE)));\n\n        verify(flowNodesRecover, never()).execute(any(), eq(asList(1L, 2L)));// transaction fail there\n        verify(flowNodesRecover).execute(any(), eq(asList(3L, 4L)));\n        verify(flowNodesRecover).execute(any(), eq(singletonList(5L)));\n        verify(userTransactionService, times(3)).executeInTransaction(any());\n    }\n\n    @Test\n    void should_recover_elements_using_a_new_RecoveryMonitor() throws Exception {\n        InOrder inOrder = inOrder(recoveryMonitor, flowNodesRecover, processesRecover);\n\n        recoveryService.recover(asList(\n                elementToRecover(1L, FLOWNODE),\n                elementToRecover(2L, PROCESS)));\n\n        inOrder.verify(recoveryMonitor).startNow(2);\n        inOrder.verify(flowNodesRecover).execute(recoveryMonitor, singletonList(1L));\n        inOrder.verify(processesRecover).execute(recoveryMonitor, singletonList(2L));\n        inOrder.verify(recoveryMonitor).printSummary();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/resources/AllProfiles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<profiles:profiles xmlns:profiles=\"http://documentation.bonitasoft.com/profile-xml-schema/1.0\">\n\t<profile name=\"Administrator\" isDefault=\"true\">\n\t\t<description>Administrator profile</description>\n\t\t<profileMapping>\n\t\t\t<users>\n\t\t\t\t<user>userName1</user>\n\t\t\t</users>\n\t\t\t<memberships>\n\t\t\t\t<membership>\n\t\t\t\t\t<group>/group1</group>\n\t\t\t\t\t<role>role2</role>\n\t\t\t\t</membership>\n\t\t\t\t<membership>\n\t\t\t\t\t<group>/group2</group>\n\t\t\t\t\t<role>role2</role>\n\t\t\t\t</membership>\n\t\t\t</memberships>\n\t\t\t<roles>\n\t\t\t\t<role>role1</role>\n\t\t\t\t<role>role2</role>\n\t\t\t</roles>\n\t\t</profileMapping>\n\t</profile>\n\t<profile name=\"Team Manager\" isDefault=\"true\">\n\t\t<description>Team Manager profile</description>\n\t\t<profileMapping>\n\t\t\t<users>\n\t\t\t\t<user>userName4</user>\n\t\t\t</users>\n\t\t</profileMapping>\n\t</profile>\n\t<profile name=\"Process owner\" isDefault=\"false\">\n\t\t<description>Process owner profile</description>\n\t\t<profileMapping>\n\t\t\t<memberships>\n\t\t\t\t<membership>\n\t\t\t\t\t<group>/group3</group>\n\t\t\t\t\t<role>role3</role>\n\t\t\t\t</membership>\n\t\t\t</memberships>\n\t\t</profileMapping>\n\t</profile>\n\t<profile name=\"User\" isDefault=\"false\">\n\t\t<description>User profile</description>\n\t\t<profileMapping>\n\t\t\t<groups>\n\t\t\t\t<group>/group1</group>\n\t\t\t</groups>\n\t\t</profileMapping>\n\t</profile>\n</profiles:profiles>\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/resources/classpathresource1",
    "content": "<beans\n        xmlns=\"http://www.springframework.org/schema/beans\"\n        xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n        xmlns:context=\"http://www.springframework.org/schema/context\"\n        xsi:schemaLocation=\"\n            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd\n            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd\">\n\n</beans>"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/resources/classpathresource2",
    "content": "<beans\n        xmlns=\"http://www.springframework.org/schema/beans\"\n        xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n        xmlns:context=\"http://www.springframework.org/schema/context\"\n        xsi:schemaLocation=\"\n            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd\n            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd\">\n\n</beans>"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/resources/complexActorMapping.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<actorMappings:actorMappings xmlns:actorMappings=\"http://www.bonitasoft.org/ns/actormapping/6.0\">\n    <actorMapping name=\"Employee actor\">\n        <users>\n            <user>john</user>\n        </users>\n        <roles>\n            <role>dev</role>\n        </roles>\n        <groups>\n            <group>/RD</group>\n        </groups>\n        <memberships>\n            <membership>\n                <role>dev</role>\n                <group>/RD</group>\n            </membership>\n        </memberships>\n    </actorMapping>\n</actorMappings:actorMappings>\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/resources/icon.docx",
    "content": "test\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/resources/logback-test.xml",
    "content": "<configuration debug=\"false\" scan=\"false\">\n\n    <logger name=\"org.bonitasoft.engine.execution.work.FailedStateSetter\" level=\"DEBUG\" />\n    <logger name=\"org.bonitasoft.engine.bpm.contract.validation.ContractStructureValidator\" level=\"DEBUG\" />\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->\n        <encoder>\n            <pattern>|%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <logger name=\"org.bonitasoft.engine.bpm.contract.validation.ContractConstraintsValidator\" level=\"DEBUG\" />\n    <root level=\"INFO\">\n        <appender-ref ref=\"STDOUT\" />\n    </root>\n\n</configuration>\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-engine/src/test/resources/testThatShouldFail.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<actorMappings xmlns=\"http://www.bonitasoft.org/ns/actormapping/6.0\">\n    <actorMapping name=\"EmployeeMembership1\">\n        <users>\n            <user>william.jobs</user>\n        </users>\n        <groups>\n            <group>RD</group>\n        </groups>\n        <roles>\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/build.gradle",
    "content": "dependencies {\n    api platform(libs.bonitaArtifactsModelBom)\n\n    api project(':bpm:bonita-common')\n    api project(':bpm:bonita-core:bonita-process-definition')\n    api project(':services:bonita-identity')\n    api project(':services:bonita-persistence')\n    api project(':services:bonita-commons')\n    api project(':services:bonita-log')\n    api project(':services:bonita-events')\n    api project(':services:bonita-archive')\n    api project(':services:bonita-data-instance')\n    api project(':services:bonita-classloader')\n    api project(':bpm:bonita-core:bonita-process-comment')\n    api project(':services:bonita-builder')\n    api project(':bpm:bonita-core:bonita-home-server')\n    api project(':services:bonita-session')\n    api project(':services:bonita-connector-executor')\n    api project(':services:bonita-resources')\n    api project(':services:bonita-time-tracker')\n    api project(':services:bonita-expression')\n    api project(':services:bonita-cache')\n    api project(':bpm:bonita-core:bonita-contract-data')\n    api 'org.bonitasoft.engine:bonita-connector-model'\n\n    testImplementation libs.assertj\n    testImplementation libs.systemRules\n\n    testRuntimeOnly libs.logback\n\n    annotationProcessor libs.lombok\n    compileOnly libs.lombok\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/ConnectorInstanceService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.connector;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.connector.exception.SConnectorInstanceCreationException;\nimport org.bonitasoft.engine.core.connector.exception.SConnectorInstanceDeletionException;\nimport org.bonitasoft.engine.core.connector.exception.SConnectorInstanceModificationException;\nimport org.bonitasoft.engine.core.connector.exception.SConnectorInstanceNotFoundException;\nimport org.bonitasoft.engine.core.connector.exception.SConnectorInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.model.SAbstractConnectorInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstanceWithFailureInfo;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAConnectorInstance;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface ConnectorInstanceService {\n\n    String CONNECTOR_INSTANCE = \"CONNECTOR_INSTANCE\";\n\n    String CONNECTOR_INSTANCE_STATE = \"CONNECTOR_INSTANCE_STATE\";\n\n    String CONNECTOR_INSTANCE_STATE_UPDATED = \"CONNECTOR_INSTANCE_STATE_UPDATED\";\n\n    /**\n     * Get a list of connectorInstances for specified container\n     *\n     * @param containerId\n     *        Identifier of container\n     * @param containerType\n     *        Type of container\n     * @param activationEvent\n     *        The event to indicate when the connector will be activated\n     * @return list of connectorInstance objects\n     * @throws SConnectorInstanceReadException\n     *         Error thrown if has exceptions during the connector retrieve\n     */\n    List<SConnectorInstance> getConnectorInstances(long containerId, String containerType,\n            ConnectorEvent activationEvent, int from, int numberOfResult,\n            String state) throws SConnectorInstanceReadException;\n\n    /**\n     * Create connector instance by give connector instance, the connector instance will be stored in database\n     *\n     * @param connectorInstance\n     *        Connector instance\n     * @throws SConnectorInstanceCreationException\n     *         Error thrown if has exceptions during the connector instance creation\n     */\n    void createConnectorInstance(SConnectorInstance connectorInstance) throws SConnectorInstanceCreationException;\n\n    /**\n     * Delete the given connector instance from the database\n     *\n     * @param connectorInstance\n     *        the connector instance\n     * @throws SConnectorInstanceDeletionException\n     *         if has exceptions during the connector instance deletion\n     */\n    void deleteConnectorInstance(SConnectorInstance connectorInstance) throws SConnectorInstanceDeletionException;\n\n    /**\n     * @param sConnectorInstance\n     * @param state\n     * @throws SConnectorInstanceModificationException\n     */\n    void setState(SAbstractConnectorInstance sConnectorInstance, String state)\n            throws SConnectorInstanceModificationException;\n\n    /**\n     * Defines the exception associated to the connector failure\n     *\n     * @param connectorInstanceWithFailure failed connector instance\n     * @param throwable exception responsible for connector failure\n     * @throws SConnectorInstanceModificationException\n     * @since 6.1\n     */\n    void setConnectorInstanceFailureException(SConnectorInstanceWithFailureInfo connectorInstanceWithFailure,\n            Throwable throwable)\n            throws SConnectorInstanceModificationException;\n\n    /**\n     * @param connectorInstanceId\n     * @return\n     * @throws SConnectorInstanceReadException\n     * @throws SConnectorInstanceNotFoundException\n     */\n    SConnectorInstance getConnectorInstance(long connectorInstanceId)\n            throws SConnectorInstanceReadException, SConnectorInstanceNotFoundException;\n\n    /**\n     * Retrieves the connector instance with failure information for the given connector instance id\n     *\n     * @param connectorInstanceId\n     * @return the connector instance with failure information for the given connector instance id\n     * @throws SConnectorInstanceReadException\n     * @throws SConnectorInstanceNotFoundException\n     * @since 6.1\n     */\n    SConnectorInstanceWithFailureInfo getConnectorInstanceWithFailureInfo(long connectorInstanceId)\n            throws SConnectorInstanceReadException,\n            SConnectorInstanceNotFoundException;\n\n    /**\n     * Retrieves the connector instance with failure information for the given container\n     *\n     * @param containerId\n     * @param containerType\n     * @param state\n     * @param from\n     * @param maxResults\n     * @return the connector instance with failure information for the given connector instance id\n     * @throws SConnectorInstanceReadException\n     * @throws SConnectorInstanceNotFoundException\n     * @since 6.1\n     */\n    List<SConnectorInstanceWithFailureInfo> getConnectorInstancesWithFailureInfo(long containerId, String containerType,\n            String state, int from, int maxResults)\n            throws SConnectorInstanceReadException;\n\n    /**\n     * @param containerId\n     * @param containerType\n     * @return\n     * @throws SConnectorInstanceReadException\n     */\n    long getNumberOfConnectorInstances(long containerId, String containerType) throws SConnectorInstanceReadException;\n\n    /**\n     * @param containerId\n     * @param containerType\n     * @param from\n     * @param numberOfResult\n     * @throws SConnectorInstanceReadException\n     */\n    List<SConnectorInstance> getConnectorInstances(long containerId, String containerType, int from, int numberOfResult,\n            String fieldName,\n            OrderByType orderByType) throws SConnectorInstanceReadException;\n\n    /**\n     * @param containerId\n     * @param containerType\n     * @return\n     * @throws SConnectorInstanceReadException\n     */\n    SConnectorInstance getNextExecutableConnectorInstance(long containerId, String containerType,\n            ConnectorEvent activationEvent)\n            throws SConnectorInstanceReadException;\n\n    /**\n     * @param searchOptions\n     * @return\n     */\n    long getNumberOfConnectorInstances(QueryOptions searchOptions) throws SBonitaReadException;\n\n    /**\n     * @param searchOptions\n     * @return\n     */\n    List<SConnectorInstance> searchConnectorInstances(QueryOptions searchOptions) throws SBonitaReadException;\n\n    /**\n     * @param connectorInstance\n     * @param archiveDate\n     * @throws SConnectorInstanceCreationException\n     */\n    void archiveConnectorInstance(SConnectorInstance connectorInstance, long archiveDate)\n            throws SConnectorInstanceCreationException;\n\n    /**\n     * @param searchOptions\n     * @param persistenceService\n     * @return\n     * @throws SBonitaReadException\n     */\n    long getNumberArchivedConnectorInstance(QueryOptions searchOptions, ReadPersistenceService persistenceService)\n            throws SBonitaReadException;\n\n    /**\n     * @param searchOptions\n     * @param persistenceService\n     * @return\n     * @throws SBonitaReadException\n     */\n    List<SAConnectorInstance> searchArchivedConnectorInstance(QueryOptions searchOptions,\n            ReadPersistenceService persistenceService)\n            throws SBonitaReadException;\n\n    /**\n     * @param containerId\n     * @param containerType\n     * @throws SConnectorInstanceReadException\n     * @throws SConnectorInstanceDeletionException\n     * @since 6.1\n     */\n    void deleteConnectors(long containerId, String containerType)\n            throws SConnectorInstanceReadException, SConnectorInstanceDeletionException;\n\n    /**\n     * Delete archived connector instances using a list of container ids and a container type\n     *\n     * @param containerIds ids on the container (source process instance id or source task id)\n     * @param containerType\n     * @throws SBonitaException\n     */\n    void deleteArchivedConnectorInstances(List<Long> containerIds, String containerType) throws SBonitaException;\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/ConnectorResult.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.connector;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport org.bonitasoft.engine.connector.Connector;\n\n/**\n * Contains the instantiated connector and its result\n * It is used to give make the connector service return both the instantiated connector and its result\n * in order to be able to call disconnect on it in the execute operation method\n *\n * @author Baptiste Mesta\n */\n@Data\n@AllArgsConstructor\npublic class ConnectorResult {\n\n    private Connector connector;\n    private Map<String, Object> result;\n    private long executionTimeMillis;\n\n    public Map<String, Object> getResult() {\n        if (result == null) {\n            return Collections.emptyMap();\n        }\n        return Collections.unmodifiableMap(result);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/ConnectorService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.connector;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.CompletableFuture;\n\nimport org.bonitasoft.engine.core.connector.exception.SConnectorException;\nimport org.bonitasoft.engine.core.connector.exception.SInvalidConnectorImplementationException;\nimport org.bonitasoft.engine.core.connector.parser.SConnectorImplementationDescriptor;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstance;\nimport org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.resources.SBARResource;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n * @since 6.0\n */\npublic interface ConnectorService {\n\n    String TO_BE_EXECUTED = \"TO_BE_EXECUTED\";\n\n    String DONE = \"DONE\";\n\n    String FAILED = \"FAILED\";\n\n    String SKIPPED = \"SKIPPED\";\n\n    String TO_RE_EXECUTE = \"TO_RE_EXECUTE\";\n\n    /**\n     * Execute a connector instance by given connectorDefinitionId and connectorDefinitionVersion\n     *\n     * @param processDefinitionId\n     *        The identifier of process definition\n     * @param connectorDefinitionId\n     *        The identifier of connector definition\n     * @param connectorDefinitionVersion\n     *        The version of connector definition\n     * @param connectorInputParameters\n     *        The input of connector\n     * @param inputValues\n     *        The input values of connector\n     * @param classLoader\n     *        The class loader used to load and run connector\n     * @param sexpContext\n     *        The expression context\n     * @return The output after connector executing\n     * @throws SConnectorException\n     *         Error thrown if has exceptions during the connector executing\n     */\n    ConnectorResult executeMultipleEvaluation(long processDefinitionId, String connectorDefinitionId,\n            String connectorDefinitionVersion,\n            Map<String, SExpression> connectorInputParameters, Map<String, Map<String, Serializable>> inputValues,\n            ClassLoader classLoader,\n            SExpressionContext sexpContext) throws SConnectorException;\n\n    /**\n     * Load connectors for given process definition and tenant, connectors will be stored in cache after loading\n     *\n     * @param sDefinition\n     *        The process definition\n     * @return true if all connectors found have all them dependencies resolved and are correctly loaded\n     * @throws SConnectorException\n     *         Error thrown if has exceptions during the connector loading\n     */\n    boolean loadConnectors(SProcessDefinition sDefinition) throws SConnectorException;\n\n    /**\n     * Set connector implementation for id and version specified connector.\n     * Store all connector related files if they are not existed and replace the old implementation with the new one in\n     * file system.\n     * Delete former and load current connectors in cache.\n     *\n     * @param sProcessDefinition\n     *        The process definition which the connector belongs to\n     * @param connectorId\n     *        Id of connector definition.\n     * @param connectorVersion\n     *        Version of connector definition\n     * @param connectorImplementationArchive\n     *        zip byte array containing the connector implementation information\n     * @throws SConnectorException\n     *         Error thrown if has exceptions during the connector implementation setting\n     * @throws SInvalidConnectorImplementationException\n     */\n    void setConnectorImplementation(SProcessDefinition sProcessDefinition, String connectorId, String connectorVersion,\n            byte[] connectorImplementationArchive) throws SConnectorException, SInvalidConnectorImplementationException;\n\n    /**\n     * Get a list of connector implementation descriptors for id specified process definition, the returned list is\n     * paginated\n     *\n     * @param processDefinitionId\n     *        Identifier of process definition\n     * @param fromIndex\n     *        Start index of connector record\n     * @param numberPerPage\n     *        Number of connectors we want to get. Maximum number of connectors returned.\n     * @param field\n     *        The field that the result ordered by\n     * @param order\n     *        The order, ACS or DESC\n     * @return A list of all satisfied connector implementation descriptor objects\n     * @throws SConnectorException\n     *         Error thrown if has exceptions during the connector implementations retrieve\n     */\n    List<SConnectorImplementationDescriptor> getConnectorImplementations(long processDefinitionId, int fromIndex,\n            int numberPerPage,\n            String field, OrderByType order) throws SConnectorException;\n\n    /**\n     * Get connector implementation descriptor for specified connector in a process definition.\n     *\n     * @param processDefinitionId\n     *        Identifier of process definition\n     * @param connectorId\n     *        id of connector definition\n     * @param connectorVersion\n     *        version of connector definition\n     * @return connector implementation descriptor object\n     * @throws SConnectorException\n     *         Error thrown if has exceptions during the connector implementation get\n     */\n    SConnectorImplementationDescriptor getConnectorImplementation(long processDefinitionId, String connectorId,\n            String connectorVersion)\n            throws SConnectorException;\n\n    /**\n     * @param parameters\n     * @param sExpressionContext\n     * @param inputValues\n     * @return\n     * @throws SExpressionTypeUnknownException\n     * @throws SExpressionEvaluationException\n     * @throws SExpressionDependencyMissingException\n     * @throws SInvalidExpressionException\n     */\n    Map<String, Object> evaluateInputParameters(String connectorId, Map<String, SExpression> parameters,\n            SExpressionContext sExpressionContext,\n            Map<String, Map<String, Serializable>> inputValues)\n            throws SExpressionTypeUnknownException, SExpressionEvaluationException,\n            SExpressionDependencyMissingException, SInvalidExpressionException;\n\n    SConnectorImplementationDescriptor getConnectorImplementationDescriptor(long processDefinitionId,\n            String connectorId, String version)\n            throws SConnectorException;\n\n    /**\n     * @param outputs\n     * @param expressionContext\n     * @param result\n     * @throws SConnectorException\n     */\n    void executeOutputOperation(List<SOperation> outputs, SExpressionContext expressionContext, ConnectorResult result)\n            throws SConnectorException;\n\n    /**\n     * @param processDefinitionId\n     * @param sConnectorInstance\n     * @param connectorImplementationDescriptor\n     * @param classLoader\n     * @param inputParameters\n     * @return the result of the connector execution\n     * @throws SConnectorException\n     */\n    CompletableFuture<ConnectorResult> executeConnector(long processDefinitionId, SConnectorInstance sConnectorInstance,\n            SConnectorImplementationDescriptor connectorImplementationDescriptor, ClassLoader classLoader,\n            Map<String, Object> inputParameters)\n            throws SConnectorException;\n\n    /**\n     * @param result\n     * @throws SConnectorException\n     */\n    void disconnect(ConnectorResult result) throws SConnectorException;\n\n    /**\n     * @param processDefinitionId\n     *        the id of the process definition\n     * @return\n     *         the number of connector implementation for this process definition\n     * @throws SConnectorException\n     */\n    Long getNumberOfConnectorImplementations(long processDefinitionId) throws SConnectorException;\n\n    List<SBARResource> getConnectorImplementations(long processDefinitionId, int from, int numberOfElements)\n            throws SBonitaReadException;\n\n    void addConnectorImplementation(Long processDefinitionId, String name, byte[] content) throws SRecorderException;\n\n    void removeConnectorImplementations(long processDefinitionId) throws SBonitaReadException, SRecorderException;\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/exception/SConnectorDefinitionNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.connector.exception;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Celine Souchet\n */\npublic class SConnectorDefinitionNotFoundException extends SBonitaException {\n\n    private static final long serialVersionUID = -4558945817033278955L;\n\n    public SConnectorDefinitionNotFoundException(final long id) {\n        super(\"Unable to find connector definition with id \\\"\" + id + \"\\\".\");\n    }\n\n    public SConnectorDefinitionNotFoundException(final String message) {\n        super(message);\n    }\n\n    public SConnectorDefinitionNotFoundException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SConnectorDefinitionNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/exception/SConnectorException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.connector.exception;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Feng Hui\n */\npublic class SConnectorException extends SBonitaException {\n\n    private static final long serialVersionUID = -3113075377405323282L;\n\n    public SConnectorException(String message) {\n        super(message);\n    }\n\n    public SConnectorException(Throwable t) {\n        super(t);\n    }\n\n    public SConnectorException(String message, Exception e) {\n        super(message, e);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/exception/SConnectorInstanceCreationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.connector.exception;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SConnectorInstanceCreationException extends SBonitaException {\n\n    public SConnectorInstanceCreationException(final Exception e) {\n        super(e);\n    }\n\n    /**\n     * @param string\n     * @param e\n     */\n    public SConnectorInstanceCreationException(final String message, final Exception e) {\n        super(e);\n    }\n\n    private static final long serialVersionUID = -1002690534909571624L;\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/exception/SConnectorInstanceDeletionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.connector.exception;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SConnectorInstanceDeletionException extends SBonitaException {\n\n    private static final long serialVersionUID = -6148115450304632856L;\n\n    public SConnectorInstanceDeletionException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/exception/SConnectorInstanceModificationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.connector.exception;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SConnectorInstanceModificationException extends SConnectorException {\n\n    private static final long serialVersionUID = -7431560273445001133L;\n\n    public SConnectorInstanceModificationException(final Throwable t) {\n        super(t);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/exception/SConnectorInstanceNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.connector.exception;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SConnectorInstanceNotFoundException extends SBonitaException {\n\n    private static final long serialVersionUID = -4558945817033278955L;\n\n    public SConnectorInstanceNotFoundException(final long connectorInstanceId) {\n        super(\"Unable to find connector instance with id \" + connectorInstanceId);\n    }\n\n    public SConnectorInstanceNotFoundException(final String message) {\n        super(message);\n    }\n\n    public SConnectorInstanceNotFoundException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SConnectorInstanceNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/exception/SConnectorInstanceReadException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.connector.exception;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SConnectorInstanceReadException extends SBonitaException {\n\n    private static final long serialVersionUID = -8274379054928302790L;\n\n    public SConnectorInstanceReadException(final Exception e) {\n        super(e);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/exception/SConnectorValidationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.connector.exception;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Feng Hui\n */\npublic class SConnectorValidationException extends SBonitaException {\n\n    private static final long serialVersionUID = -7025831546419799447L;\n\n    public SConnectorValidationException(final String message) {\n        super(message);\n    }\n\n    public SConnectorValidationException(final Throwable t) {\n        super(t);\n    }\n\n    public SConnectorValidationException(final String message, final Exception e) {\n        super(message, e);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/exception/SInvalidConnectorImplementationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.connector.exception;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.connector.parser.SConnectorImplementationDescriptor;\n\n/**\n * @author Feng Hui\n * @author Celine Souchet\n */\npublic class SInvalidConnectorImplementationException extends SBonitaException {\n\n    private static final long serialVersionUID = -3113075377405323282L;\n\n    public SInvalidConnectorImplementationException(final String message,\n            final SConnectorImplementationDescriptor connectorImplementationDescriptor) {\n        this(message);\n        setConnectorImplementationClassNameOnContext(connectorImplementationDescriptor.getImplementationClassName());\n        setConnectorDefinitionIdOnContext(connectorImplementationDescriptor.getDefinitionId());\n        setConnectorDefinitionVersionOnContext(connectorImplementationDescriptor.getDefinitionVersion());\n    }\n\n    public SInvalidConnectorImplementationException(final String message) {\n        super(message);\n    }\n\n    public SInvalidConnectorImplementationException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SInvalidConnectorImplementationException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/impl/ConnectorArchive.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.connector.impl;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author Baptiste Mesta\n */\nclass ConnectorArchive {\n\n    private Map<String, byte[]> dependencies = new HashMap<>();\n    private String connectorImplName;\n    private byte[] connectorImplContent;\n\n    public void addDependency(String entryName, byte[] fileContent) {\n        dependencies.put(entryName, fileContent);\n    }\n\n    public Map<String, byte[]> getDependencies() {\n        return dependencies;\n    }\n\n    public void setConnectorImpl(String connectorImplName, byte[] connectorImplContent) {\n        this.connectorImplName = connectorImplName;\n        this.connectorImplContent = connectorImplContent;\n    }\n\n    public byte[] getConnectorImplContent() {\n        return connectorImplContent;\n    }\n\n    public String getConnectorImplName() {\n        return connectorImplName;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/impl/ConnectorExecutionTimeLogger.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.connector.impl;\n\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.connector.Connector;\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstance;\n\n/**\n * Log connector execution time when the connector took more than the configured threshold\n */\n@Slf4j\npublic class ConnectorExecutionTimeLogger {\n\n    private Long warnWhenLongerThanMillis;\n\n    /**\n     * @param warnWhenLongerThanMillis the duration in milli seconds above which the time taken will be logged as a\n     *        warning\n     */\n    public ConnectorExecutionTimeLogger(Long warnWhenLongerThanMillis) {\n        this.warnWhenLongerThanMillis = warnWhenLongerThanMillis;\n    }\n\n    public void log(long processDefinitionId, SConnectorInstance sConnectorInstance, Connector connector,\n            Map<String, Object> inputParameters, long executionTimeMillis) {\n        if (executionTimeMillis < warnWhenLongerThanMillis) {\n            return;\n        }\n        log.warn(\n                \"Connector {} with id {} with class {} of process definition {} on element {} took {} ms.\",\n                sConnectorInstance.getName(), sConnectorInstance.getId(), connector.getClass().getName(),\n                processDefinitionId, printContext(sConnectorInstance), executionTimeMillis);\n        if (log.isDebugEnabled()) {\n            log.debug(\"Input parameters of the connector with id {}: {}\", sConnectorInstance.getId(),\n                    print(inputParameters));\n        }\n    }\n\n    private String print(Map<String, Object> inputParameters) {\n        return inputParameters.entrySet().stream()\n                .map((e -> e.getKey() + \": [\" + e.getValue().toString().replaceAll(\"([\\\\r\\\\n])\", \" \") + \"]\"))\n                .collect(Collectors.joining(\", \", \"{\", \"}\"));\n    }\n\n    private String printContext(SConnectorInstance sConnectorInstance) {\n        return sConnectorInstance.getContainerType() + \" with id \" + sConnectorInstance.getContainerId();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/impl/ConnectorInstanceServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.connector.impl;\n\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.archive.ArchiveInsertRecord;\nimport org.bonitasoft.engine.archive.ArchiveService;\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.connector.ConnectorInstanceService;\nimport org.bonitasoft.engine.core.connector.exception.SConnectorInstanceCreationException;\nimport org.bonitasoft.engine.core.connector.exception.SConnectorInstanceDeletionException;\nimport org.bonitasoft.engine.core.connector.exception.SConnectorInstanceModificationException;\nimport org.bonitasoft.engine.core.connector.exception.SConnectorInstanceNotFoundException;\nimport org.bonitasoft.engine.core.connector.exception.SConnectorInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.model.SAbstractConnectorInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstanceWithFailureInfo;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAConnectorInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAConnectorInstanceBuilderFactory;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\n\n/**\n * @author Baptiste Mesta\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class ConnectorInstanceServiceImpl implements ConnectorInstanceService {\n\n    /**\n     * Length maximum of the message of the exception\n     */\n    private static final int MAX_MESSAGE_LENGTH = 255;\n\n    private final Recorder recorder;\n\n    private final ReadPersistenceService persistenceService;\n\n    private final ArchiveService archiveService;\n\n    public ConnectorInstanceServiceImpl(final ReadPersistenceService persistenceService, final Recorder recorder,\n            final ArchiveService archiveService) {\n        this.persistenceService = persistenceService;\n        this.recorder = recorder;\n        this.archiveService = archiveService;\n    }\n\n    @Override\n    public void setState(final SAbstractConnectorInstance sConnectorInstance, final String state)\n            throws SConnectorInstanceModificationException {\n        final EntityUpdateDescriptor entityUpdateDescriptor = new EntityUpdateDescriptor();\n        entityUpdateDescriptor.addField(SConnectorInstance.STATE_KEY, state);\n        try {\n            recorder.recordUpdate(UpdateRecord.buildSetFields(sConnectorInstance, entityUpdateDescriptor),\n                    CONNECTOR_INSTANCE_STATE);\n        } catch (final SRecorderException e) {\n            throw new SConnectorInstanceModificationException(e);\n        }\n    }\n\n    @Override\n    public void setConnectorInstanceFailureException(\n            final SConnectorInstanceWithFailureInfo connectorInstanceWithFailure, final Throwable throwable)\n            throws SConnectorInstanceModificationException {\n        final EntityUpdateDescriptor entityUpdateDescriptor = new EntityUpdateDescriptor();\n        entityUpdateDescriptor.addField(SConnectorInstanceWithFailureInfo.EXCEPTION_MESSAGE,\n                getExceptionMessage(throwable));\n        entityUpdateDescriptor.addField(SConnectorInstanceWithFailureInfo.STACK_TRACE,\n                getStringStackTrace(throwable));\n        try {\n            recorder.recordUpdate(UpdateRecord.buildSetFields(connectorInstanceWithFailure, entityUpdateDescriptor),\n                    CONNECTOR_INSTANCE);\n        } catch (final SRecorderException e) {\n            throw new SConnectorInstanceModificationException(e);\n        }\n    }\n\n    private String getExceptionMessage(final Throwable throwable) {\n        if (throwable == null) {\n            return null;\n        }\n        Throwable current = throwable;\n        while (current.getCause() != null) {\n            current = current.getCause();\n        }\n        String message = current.getMessage();\n        if (message != null && message.length() > MAX_MESSAGE_LENGTH) {\n            message = message.substring(0, MAX_MESSAGE_LENGTH);\n        }\n        return message;\n    }\n\n    private static String getStringStackTrace(final Throwable throwable) {\n        if (throwable == null) {\n            return null;\n        }\n        try (StringWriter writer = new StringWriter(); PrintWriter printer = new PrintWriter(writer)) {\n            throwable.printStackTrace(printer);\n            return writer.toString();\n        } catch (Throwable e) {\n            //Unable to print exception to the Writer, simply retrieve all exceptions as stacktrace\n            try {\n                Throwable current = throwable;\n                StringBuilder stacktrace = new StringBuilder();\n                stacktrace.append(\"Unable to retrieve stacktrace, exceptions were:\\n\");\n                do {\n                    stacktrace.append(\" * \").append(current.getClass().getName()).append(\": \")\n                            .append(current.getMessage()).append(\"\\n\");\n                    current = current.getCause();\n                } while (current != null);\n                return stacktrace.toString();\n            } catch (Throwable e1) {\n                return \"Unable to retrieve exception stacktrace\";\n            }\n        }\n    }\n\n    @Override\n    public void createConnectorInstance(final SConnectorInstance connectorInstance)\n            throws SConnectorInstanceCreationException {\n        try {\n            recorder.recordInsert(new InsertRecord(connectorInstance), CONNECTOR_INSTANCE);\n        } catch (final SRecorderException e) {\n            throw new SConnectorInstanceCreationException(e);\n        }\n    }\n\n    @Override\n    public List<SConnectorInstance> getConnectorInstances(final long containerId, final String containerType,\n            final ConnectorEvent activationEvent,\n            final int from, final int numberOfResult, final String state) throws SConnectorInstanceReadException {\n        final Map<String, Object> inputParameters = new HashMap<>(4);\n        inputParameters.put(\"containerId\", containerId);\n        inputParameters.put(\"containerType\", containerType);\n        inputParameters.put(\"activationEvent\", activationEvent);\n        inputParameters.put(\"state\", state);\n        final SelectListDescriptor<SConnectorInstance> selectListDescriptor = new SelectListDescriptor<>(\n                \"getConnectorInstancesWithState\",\n                inputParameters, SConnectorInstance.class,\n                new QueryOptions(from, numberOfResult, SConnectorInstance.class, \"id\", OrderByType.ASC));\n        try {\n            return persistenceService.selectList(selectListDescriptor);\n        } catch (final SBonitaReadException e) {\n            throw new SConnectorInstanceReadException(e);\n        }\n    }\n\n    @Override\n    public List<SConnectorInstance> getConnectorInstances(final long containerId, final String containerType,\n            final int from, final int numberOfResult,\n            final String fieldName, final OrderByType orderByType) throws SConnectorInstanceReadException {\n        final Map<String, Object> inputParameters = new HashMap<>(2);\n        inputParameters.put(\"containerId\", containerId);\n        inputParameters.put(\"containerType\", containerType);\n        final SelectListDescriptor<SConnectorInstance> selectListDescriptor = new SelectListDescriptor<>(\n                \"getConnectorInstances\",\n                inputParameters, SConnectorInstance.class,\n                new QueryOptions(from, numberOfResult, SConnectorInstance.class, fieldName, orderByType));\n        try {\n            return persistenceService.selectList(selectListDescriptor);\n        } catch (final SBonitaReadException e) {\n            throw new SConnectorInstanceReadException(e);\n        }\n    }\n\n    private List<SConnectorInstance> getConnectorInstancesOrderedById(final long containerId,\n            final String containerType, final int from,\n            final int numberOfResult) throws SConnectorInstanceReadException {\n        final Map<String, Object> inputParameters = new HashMap<>(2);\n        inputParameters.put(\"containerId\", containerId);\n        inputParameters.put(\"containerType\", containerType);\n        final SelectListDescriptor<SConnectorInstance> selectListDescriptor = new SelectListDescriptor<>(\n                \"getConnectorInstancesOrderedById\",\n                inputParameters, SConnectorInstance.class, new QueryOptions(from, numberOfResult));\n        try {\n            return persistenceService.selectList(selectListDescriptor);\n        } catch (final SBonitaReadException e) {\n            throw new SConnectorInstanceReadException(e);\n        }\n    }\n\n    @Override\n    public SConnectorInstance getNextExecutableConnectorInstance(final long containerId, final String containerType,\n            final ConnectorEvent activationEvent)\n            throws SConnectorInstanceReadException {\n        final Map<String, Object> inputParameters = new HashMap<>(3);\n        inputParameters.put(\"containerId\", containerId);\n        inputParameters.put(\"containerType\", containerType);\n        inputParameters.put(\"activationEvent\", activationEvent);\n        final SelectListDescriptor<SConnectorInstance> selectOneDescriptor = new SelectListDescriptor<>(\n                \"getNextExecutableConnectorInstance\",\n                inputParameters, SConnectorInstance.class, new QueryOptions(0, 1));\n        try {\n            final List<SConnectorInstance> selectList = persistenceService.selectList(selectOneDescriptor);\n            if (selectList.size() == 1) {\n                return selectList.get(0);\n            }\n            return null;\n        } catch (final SBonitaReadException e) {\n            throw new SConnectorInstanceReadException(e);\n        }\n    }\n\n    @Override\n    public long getNumberOfConnectorInstances(final long containerId, final String containerType)\n            throws SConnectorInstanceReadException {\n        final Map<String, Object> inputParameters = new HashMap<>(2);\n        inputParameters.put(\"containerId\", containerId);\n        inputParameters.put(\"containerType\", containerType);\n        final SelectOneDescriptor<Long> selectListDescriptor = new SelectOneDescriptor<>(\n                \"getNumberOfConnectorInstances\", inputParameters,\n                SConnectorInstance.class);\n        try {\n            return persistenceService.selectOne(selectListDescriptor);\n        } catch (final SBonitaReadException e) {\n            throw new SConnectorInstanceReadException(e);\n        }\n    }\n\n    @Override\n    public SConnectorInstance getConnectorInstance(final long connectorInstanceId)\n            throws SConnectorInstanceReadException, SConnectorInstanceNotFoundException {\n        final SelectByIdDescriptor<SConnectorInstance> selectByIdDescriptor = new SelectByIdDescriptor<>(\n                SConnectorInstance.class, connectorInstanceId);\n        try {\n            final SConnectorInstance connectorInstance = persistenceService.selectById(selectByIdDescriptor);\n            if (connectorInstance == null) {\n                throw new SConnectorInstanceNotFoundException(connectorInstanceId);\n            }\n            return connectorInstance;\n        } catch (final SBonitaReadException e) {\n            throw new SConnectorInstanceReadException(e);\n        }\n    }\n\n    @Override\n    public SConnectorInstanceWithFailureInfo getConnectorInstanceWithFailureInfo(final long connectorInstanceId)\n            throws SConnectorInstanceReadException,\n            SConnectorInstanceNotFoundException {\n        final SelectByIdDescriptor<SConnectorInstanceWithFailureInfo> selectByIdDescriptor = new SelectByIdDescriptor<>(\n                SConnectorInstanceWithFailureInfo.class, connectorInstanceId);\n        try {\n            final SConnectorInstanceWithFailureInfo connectorInstance = persistenceService\n                    .selectById(selectByIdDescriptor);\n            if (connectorInstance == null) {\n                throw new SConnectorInstanceNotFoundException(connectorInstanceId);\n            }\n            return connectorInstance;\n        } catch (final SBonitaReadException e) {\n            throw new SConnectorInstanceReadException(e);\n        }\n    }\n\n    @Override\n    public List<SConnectorInstanceWithFailureInfo> getConnectorInstancesWithFailureInfo(final long containerId,\n            final String containerType, final String state,\n            final int from, final int maxResults) throws SConnectorInstanceReadException {\n        final Map<String, Object> inputParameters = new HashMap<>(3);\n        inputParameters.put(\"containerId\", containerId);\n        inputParameters.put(\"containerType\", containerType);\n        inputParameters.put(\"state\", state);\n        final SelectListDescriptor<SConnectorInstanceWithFailureInfo> selectListDescriptor = new SelectListDescriptor<>(\n                \"getConnectorInstancesWithFailureInfoInState\",\n                inputParameters, SConnectorInstanceWithFailureInfo.class,\n                new QueryOptions(from, maxResults, SConnectorInstanceWithFailureInfo.class, \"id\",\n                        OrderByType.ASC));\n        try {\n            return persistenceService.selectList(selectListDescriptor);\n        } catch (final SBonitaReadException e) {\n            throw new SConnectorInstanceReadException(e);\n        }\n    }\n\n    @Override\n    public long getNumberOfConnectorInstances(final QueryOptions searchOptions) throws SBonitaReadException {\n        return persistenceService.getNumberOfEntities(SConnectorInstance.class, searchOptions, null);\n    }\n\n    // charles\n    @Override\n    public List<SConnectorInstance> searchConnectorInstances(final QueryOptions searchOptions)\n            throws SBonitaReadException {\n        return persistenceService.searchEntity(SConnectorInstance.class, searchOptions, null);\n    }\n\n    @Override\n    public void archiveConnectorInstance(final SConnectorInstance connectorInstance, final long archiveDate)\n            throws SConnectorInstanceCreationException {\n        if (connectorInstance != null) {\n            final SAConnectorInstance saConnectorInstance = BuilderFactory.get(SAConnectorInstanceBuilderFactory.class)\n                    .createNewArchivedConnectorInstance(connectorInstance).done();\n            final ArchiveInsertRecord insertRecord = new ArchiveInsertRecord(saConnectorInstance);\n            try {\n                archiveService.recordInsert(archiveDate, insertRecord);\n            } catch (final SBonitaException e) {\n                throw new SConnectorInstanceCreationException(\n                        \"Unable to archive the connectorInstance instance with id \" + connectorInstance.getId(), e);\n            }\n        }\n    }\n\n    @Override\n    public void deleteConnectorInstance(final SConnectorInstance connectorInstance)\n            throws SConnectorInstanceDeletionException {\n        try {\n            recorder.recordDelete(new DeleteRecord(connectorInstance), CONNECTOR_INSTANCE);\n        } catch (final SRecorderException e) {\n            throw new SConnectorInstanceDeletionException(e);\n        }\n\n    }\n\n    @Override\n    public long getNumberArchivedConnectorInstance(final QueryOptions searchOptions,\n            final ReadPersistenceService persistenceService)\n            throws SBonitaReadException {\n        return persistenceService.getNumberOfEntities(SAConnectorInstance.class, searchOptions, null);\n    }\n\n    @Override\n    public List<SAConnectorInstance> searchArchivedConnectorInstance(final QueryOptions searchOptions,\n            final ReadPersistenceService persistenceService)\n            throws SBonitaReadException {\n        return persistenceService.searchEntity(SAConnectorInstance.class, searchOptions, null);\n    }\n\n    @Override\n    public void deleteArchivedConnectorInstances(List<Long> containerIds, String containerType)\n            throws SBonitaException {\n        HashMap<String, Object> map = new HashMap<>();\n        map.put(\"containerIds\", containerIds);\n        map.put(\"containerType\", containerType);\n        archiveService.deleteFromQuery(\"deleteArchivedConnectorInstances\", map);\n    }\n\n    @Override\n    public void deleteConnectors(final long containerId, final String containerType)\n            throws SConnectorInstanceReadException,\n            SConnectorInstanceDeletionException {\n        List<SConnectorInstance> connetorInstances;\n        do {\n            // the QueryOptions always will use 0 as start index because the retrieved results will be deleted\n            connetorInstances = getConnectorInstancesOrderedById(containerId, containerType, 0, 100);\n            for (final SConnectorInstance sConnectorInstance : connetorInstances) {\n                deleteConnectorInstance(sConnectorInstance);\n            }\n        } while (!connetorInstances.isEmpty());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/impl/ConnectorServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.connector.impl;\n\nimport static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.IOException;\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Set;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.ExecutionException;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipInputStream;\n\nimport javax.xml.bind.JAXBException;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.cache.CacheService;\nimport org.bonitasoft.engine.cache.SCacheException;\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.classloader.SClassLoaderException;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.io.IOUtil;\nimport org.bonitasoft.engine.connector.Connector;\nimport org.bonitasoft.engine.connector.ConnectorExecutor;\nimport org.bonitasoft.engine.core.connector.ConnectorResult;\nimport org.bonitasoft.engine.core.connector.ConnectorService;\nimport org.bonitasoft.engine.core.connector.exception.SConnectorException;\nimport org.bonitasoft.engine.core.connector.exception.SInvalidConnectorImplementationException;\nimport org.bonitasoft.engine.core.connector.parser.ConnectorImplementationFieldComparator;\nimport org.bonitasoft.engine.core.connector.parser.ConnectorImplementationParser;\nimport org.bonitasoft.engine.core.connector.parser.SConnectorImplementationDescriptor;\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.OperationService;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.operation.model.SOperation;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstance;\nimport org.bonitasoft.engine.dependency.DependencyService;\nimport org.bonitasoft.engine.dependency.SDependencyException;\nimport org.bonitasoft.engine.dependency.model.AbstractSDependency;\nimport org.bonitasoft.engine.dependency.model.ScopeType;\nimport org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.resources.BARResourceType;\nimport org.bonitasoft.engine.resources.ProcessResourcesService;\nimport org.bonitasoft.engine.resources.SBARResource;\nimport org.bonitasoft.engine.tracking.TimeTracker;\nimport org.bonitasoft.engine.tracking.TimeTrackerRecords;\n\n/**\n * @author Baptiste Mesta\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\n@Slf4j\npublic class ConnectorServiceImpl implements ConnectorService {\n\n    protected static final String CONNECTOR_CACHE_NAME = \"CONNECTOR\";\n    private static final String LINE_SEPARATOR = System.getProperty(\"line.separator\");\n    private static final String IMPLEMENTATION_EXT = \".impl\";\n    private final CacheService cacheService;\n    private final ConnectorExecutor connectorExecutor;\n    private final ExpressionResolverService expressionResolverService;\n    private final OperationService operationService;\n    private final DependencyService dependencyService;\n    private final ClassLoaderService classLoaderService;\n    private final TimeTracker timeTracker;\n    private final ConnectorExecutionTimeLogger connectorExecutionTimeLogger;\n    private final ProcessResourcesService processResourcesService;\n    private final ConnectorImplementationParser connectorImplementationParser = new ConnectorImplementationParser();\n\n    public ConnectorServiceImpl(final CacheService cacheService, final ConnectorExecutor connectorExecutor,\n            final ExpressionResolverService expressionResolverService, final OperationService operationService,\n            final DependencyService dependencyService, ClassLoaderService classLoaderService,\n            final TimeTracker timeTracker, ProcessResourcesService processResourcesService,\n            ConnectorExecutionTimeLogger connectorExecutionTimeLogger) {\n        this.cacheService = cacheService;\n        this.connectorExecutor = connectorExecutor;\n        this.expressionResolverService = expressionResolverService;\n        this.classLoaderService = classLoaderService;\n        this.processResourcesService = processResourcesService;\n        this.operationService = operationService;\n        this.dependencyService = dependencyService;\n        this.timeTracker = timeTracker;\n        this.connectorExecutionTimeLogger = connectorExecutionTimeLogger;\n    }\n\n    /**\n     * Build the log message using the connector instance's context (name, version, connector id, connector instance id,\n     * container type, container id)\n     *\n     * @param connectorInstance\n     * @return the log message built using the connector instance's context\n     */\n    private static String buildConnectorContextMessage(final SConnectorInstance connectorInstance) {\n        return \" [name: <\" + connectorInstance.getName() + \">, version: <\" + connectorInstance.getVersion()\n                + \">, connector id: <\"\n                + connectorInstance.getConnectorId() + \">, connector instance id: <\" + connectorInstance.getId()\n                + \">, container type: <\"\n                + connectorInstance.getContainerType() + \">, container id: <\" + connectorInstance.getContainerId()\n                + \">, activation event: <\"\n                + connectorInstance.getActivationEvent() + \">]\";\n    }\n\n    private static String buildConnectorInputMessage(final Map<String, Object> inputParameters) {\n        final StringBuilder stb = new StringBuilder();\n        if (!inputParameters.isEmpty()) {\n            stb.append(LINE_SEPARATOR);\n            stb.append(\"Inputs: \");\n            stb.append(LINE_SEPARATOR);\n            stb.append(inputParameters.entrySet().stream()\n                    .map(e -> \" <\" + e.getKey() + \"> : <\" + e.getValue() + \">\")\n                    .collect(Collectors.joining(LINE_SEPARATOR)));\n        }\n        return stb.toString();\n    }\n\n    @Override\n    public CompletableFuture<ConnectorResult> executeConnector(final long processDefinitionId,\n            final SConnectorInstance sConnectorInstance,\n            SConnectorImplementationDescriptor connectorImplementationDescriptor, final ClassLoader classLoader,\n            final Map<String, Object> inputParameters) throws SConnectorException {\n        final String implementationClassName = connectorImplementationDescriptor.getImplementationClassName();\n        if (log.isDebugEnabled()) {\n            log.debug(\"Executing connector {} {}\", buildConnectorContextMessage(sConnectorInstance),\n                    buildConnectorInputMessage(inputParameters));\n        }\n        return executeConnectorInClassloader(implementationClassName, classLoader, inputParameters)\n                .thenApply(result -> {\n                    connectorExecutionTimeLogger.log(processDefinitionId, sConnectorInstance, result.getConnector(),\n                            inputParameters, result.getExecutionTimeMillis());\n                    return result;\n                });\n    }\n\n    @Override\n    public SConnectorImplementationDescriptor getConnectorImplementationDescriptor(long processDefinitionId,\n            String connectorId, String version)\n            throws SConnectorException {\n        try {\n            SConnectorImplementationDescriptor descriptor = getImplementation(processDefinitionId, connectorId,\n                    version);\n            if (descriptor == null) {\n                throw new SConnectorException(\"There is no implementation found for the connector \" + connectorId\n                        + \" with version \" + version);\n            }\n            return descriptor;\n        } catch (final SCacheException e) {\n            throw new SConnectorException(e);\n        }\n    }\n\n    @Override\n    public void executeOutputOperation(final List<SOperation> outputs, final SExpressionContext expressionContext,\n            final ConnectorResult result)\n            throws SConnectorException {\n        final long startTime = System.currentTimeMillis();\n        try {\n            expressionContext.putAllInputValues(result.getResult());\n            operationService.execute(outputs, expressionContext.getContainerId(), expressionContext.getContainerType(),\n                    expressionContext);// data is in\n        } catch (final SOperationExecutionException e) {\n            throw new SConnectorException(e);\n        } finally {\n            if (timeTracker.isTrackable(TimeTrackerRecords.EXECUTE_CONNECTOR_OUTPUT_OPERATIONS)) {\n                final long endTime = System.currentTimeMillis();\n                String desc = \"ConnectorResult: \" + result;\n                timeTracker.track(TimeTrackerRecords.EXECUTE_CONNECTOR_OUTPUT_OPERATIONS, desc, endTime - startTime);\n            }\n            disconnect(result);\n        }\n    }\n\n    @Override\n    public void disconnect(final ConnectorResult result) throws SConnectorException {\n        final long startTime = System.currentTimeMillis();\n        try {\n            connectorExecutor.disconnect(new SConnectorAdapter(result.getConnector()));\n        } catch (final org.bonitasoft.engine.connector.exception.SConnectorException e) {\n            throw new SConnectorException(e);\n        } finally {\n            if (timeTracker.isTrackable(TimeTrackerRecords.EXECUTE_CONNECTOR_DISCONNECT)) {\n                final long endTime = System.currentTimeMillis();\n                final StringBuilder desc = new StringBuilder();\n                desc.append(\"ConnectorResult: \");\n                desc.append(result);\n                timeTracker.track(TimeTrackerRecords.EXECUTE_CONNECTOR_DISCONNECT, desc.toString(),\n                        endTime - startTime);\n            }\n        }\n    }\n\n    private SConnectorImplementationDescriptor getImplementation(final long rootDefinitionId, final String connectorId,\n            final String version) throws SConnectorException, SCacheException {\n        SConnectorImplementationDescriptor descriptor;\n        try {\n            final String key = buildConnectorImplementationKey(rootDefinitionId, connectorId, version);\n\n            descriptor = (SConnectorImplementationDescriptor) cacheService.get(CONNECTOR_CACHE_NAME, key);\n            if (descriptor == null) {\n                // No value in cache : reload connector to ensure the cache stores all connectors for the current process\n                loadConnectors(rootDefinitionId);\n                descriptor = (SConnectorImplementationDescriptor) cacheService.get(CONNECTOR_CACHE_NAME, key);\n            }\n        } catch (final NumberFormatException e) {\n            throw new SConnectorException(e);\n        } catch (final SCacheException e) {\n            throw e;\n        }\n        return descriptor;\n    }\n\n    private void cache(final long processDefinitionId, final SConnectorImplementationDescriptor connectorImplementation)\n            throws SCacheException {\n        final String key = buildConnectorImplementationKey(processDefinitionId,\n                connectorImplementation.getDefinitionId(),\n                connectorImplementation.getDefinitionVersion());\n        cacheService.store(CONNECTOR_CACHE_NAME, key, connectorImplementation);\n    }\n\n    protected String buildConnectorImplementationKey(final long rootDefinitionId, final String connectorId,\n            final String version) {\n        return rootDefinitionId + \":\" + connectorId + \"-\" + version;\n    }\n\n    @Override\n    public ConnectorResult executeMultipleEvaluation(final long processDefinitionId, final String connectorDefinitionId,\n            final String connectorDefinitionVersion, final Map<String, SExpression> connectorInputParameters,\n            final Map<String, Map<String, Serializable>> inputValues, final ClassLoader classLoader,\n            final SExpressionContext expressionContext)\n            throws SConnectorException {\n        final String implementationClassName = getConnectorImplementationDescriptor(processDefinitionId,\n                connectorDefinitionId, connectorDefinitionVersion)\n                .getImplementationClassName();\n        final Map<String, Object> inputParameters;\n        try {\n            inputParameters = evaluateInputParameters(connectorDefinitionId, connectorInputParameters,\n                    expressionContext,\n                    inputValues);\n        } catch (final SBonitaException e) {\n            throw new SConnectorException(e);\n        }\n        final ConnectorResult connectorResult;\n        try {\n            connectorResult = executeConnectorInClassloader(implementationClassName, classLoader, inputParameters)\n                    .get();\n        } catch (InterruptedException | ExecutionException e) {\n            throw new SConnectorException(e);\n        }\n        if (log.isDebugEnabled()) {\n            log.debug(\"Executed connector <{}> with definition id <{}>, version <{}>, {}\", implementationClassName,\n                    connectorDefinitionId, connectorDefinitionVersion, buildConnectorInputMessage(inputParameters));\n        }\n        return connectorResult;\n    }\n\n    private CompletableFuture<ConnectorResult> executeConnectorInClassloader(final String implementationClassName,\n            final ClassLoader classLoader,\n            final Map<String, Object> inputParameters) throws SConnectorException {\n        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        try {\n            Thread.currentThread().setContextClassLoader(classLoader);\n            Connector connector = (Connector) classLoader.loadClass(implementationClassName).newInstance();\n            final SConnectorAdapter sConnectorAdapter = new SConnectorAdapter(connector);\n            return connectorExecutor.execute(sConnectorAdapter, inputParameters, classLoader)\n                    .thenApply(result -> new ConnectorResult(connector, result.getOutputs(),\n                            result.getExecutionTimeMillis()));\n        } catch (final ClassNotFoundException e) {\n            throw new SConnectorException(implementationClassName + \" can not be found.\", e);\n        } catch (final InstantiationException e) {\n            throw new SConnectorException(implementationClassName + \" can not be instantiated.\", e);\n        } catch (Throwable e) {\n            throw new SConnectorException(e);\n        } finally {\n            Thread.currentThread().setContextClassLoader(contextClassLoader);\n        }\n    }\n\n    @Override\n    public Map<String, Object> evaluateInputParameters(final String connectorId,\n            final Map<String, SExpression> parameters,\n            final SExpressionContext sExpressionContext,\n            final Map<String, Map<String, Serializable>> inputValues)\n            throws SExpressionTypeUnknownException, SExpressionEvaluationException,\n            SExpressionDependencyMissingException, SInvalidExpressionException {\n        final long startTime = System.currentTimeMillis();\n        final Map<String, Object> inputParameters = new HashMap<>(parameters.size());\n        try {\n            for (final Entry<String, SExpression> input : parameters.entrySet()) {\n                try {\n                    if (sExpressionContext != null) {\n                        final String key = input.getKey();\n                        if (inputValues != null && !inputValues.isEmpty() && inputValues.containsKey(key)) {\n                            sExpressionContext.setSerializableInputValues(inputValues.get(key));\n                        }\n                        inputParameters.put(input.getKey(),\n                                expressionResolverService.evaluate(input.getValue(), sExpressionContext));\n                    } else {\n                        inputParameters.put(input.getKey(), expressionResolverService.evaluate(input.getValue()));\n                    }\n                } catch (SBonitaException e) {\n                    e.setConnectorInputOnContext(input.getKey());\n                    throw e;\n                }\n            }\n        } finally {\n            if (timeTracker.isTrackable(TimeTrackerRecords.EXECUTE_CONNECTOR_INPUT_EXPRESSIONS)) {\n                final long endTime = System.currentTimeMillis();\n                String desc = \"Connector ID: \" + connectorId + \" - input parameters: \" + inputParameters;\n                timeTracker.track(TimeTrackerRecords.EXECUTE_CONNECTOR_INPUT_EXPRESSIONS, desc,\n                        endTime - startTime);\n            }\n        }\n        return inputParameters;\n    }\n\n    @Override\n    public boolean loadConnectors(final SProcessDefinition sDefinition) throws SConnectorException {\n        return loadConnectors(sDefinition.getId());\n    }\n\n    protected boolean loadConnectors(final long processDefinitionId) throws SConnectorException {\n        String name = null;\n        try {\n            final List<SBARResource> connectorImplementations = getConnectorImplementations(processDefinitionId, 0,\n                    Integer.MAX_VALUE);\n            for (SBARResource connectorImplementationFile : connectorImplementations) {\n                name = connectorImplementationFile.getName();\n                cache(processDefinitionId,\n                        connectorImplementationParser.convert(new String(connectorImplementationFile.getContent())));\n            }\n        } catch (final JAXBException e) {\n            throw new SConnectorException(\"Can not load ConnectorImplementation XML. The file name is <\" + name + \">.\",\n                    e);\n        } catch (final SCacheException e) {\n            throw new SConnectorException(\"Unable to cache the connector implementation \" + name + \".\", e);\n        } catch (SBonitaReadException e) {\n            throw new SConnectorException(\"Unable to list the connector implementations\", e);\n        }\n        return true;\n    }\n\n    @Override\n    public void setConnectorImplementation(final SProcessDefinition sProcessDefinition, final String connectorId,\n            final String connectorVersion, final byte[] connectorImplementationArchive)\n            throws SConnectorException, SInvalidConnectorImplementationException {\n        ConnectorArchive connectorArchive = extractConnectorImplementation(connectorImplementationArchive);\n        replaceConnectorImpl(sProcessDefinition, connectorId, connectorVersion, connectorArchive);\n        reLoadConnectors(sProcessDefinition, connectorId, connectorVersion);\n    }\n\n    private void replaceConnectorImpl(final SProcessDefinition sDefinition,\n            final String connectorId, final String connectorVersion, ConnectorArchive connectorArchive)\n            throws SConnectorException, SInvalidConnectorImplementationException {\n        try {\n            checkConnectorImplementationIsValid(\n                    parseConnectorImplementation(connectorArchive.getConnectorImplContent()), connectorId,\n                    connectorVersion);\n            SBARResource connectorImplementationFile = getConnectorImplementationResource(sDefinition.getId(),\n                    connectorId, connectorVersion);\n            SConnectorImplementationDescriptor connectorImplementationDescriptorToReplace = null;\n            if (connectorImplementationFile != null) {\n                connectorImplementationDescriptorToReplace = parseConnectorImplementation(\n                        connectorImplementationFile.getContent());\n            }\n            updateJarDependencies(sDefinition, connectorArchive, connectorImplementationDescriptorToReplace);\n            updateConnectorImplementationFile(sDefinition, connectorArchive, connectorImplementationFile);\n            classLoaderService.refreshClassLoaderAfterUpdate(identifier(ScopeType.PROCESS, sDefinition.getId()));\n        } catch (final SRecorderException | SDependencyException | SBonitaReadException | SClassLoaderException e) {\n            throw new SConnectorException(\"Problem replacing connector implementation of connector \" + connectorId\n                    + \" of process \" + sDefinition.getId(), e);\n        }\n    }\n\n    private void updateConnectorImplementationFile(SProcessDefinition sDefinition, ConnectorArchive connectorArchive,\n            SBARResource connectorResourceToReplace)\n            throws SRecorderException {\n        if (connectorResourceToReplace != null\n                && connectorResourceToReplace.getName().equals(connectorArchive.getConnectorImplName())) {\n            processResourcesService.update(connectorResourceToReplace, connectorArchive.getConnectorImplContent());\n        } else {\n            if (connectorResourceToReplace != null) {\n                processResourcesService.remove(connectorResourceToReplace);\n            }\n            processResourcesService.add(sDefinition.getId(), connectorArchive.getConnectorImplName(),\n                    BARResourceType.CONNECTOR,\n                    connectorArchive.getConnectorImplContent());\n        }\n    }\n\n    private void updateJarDependencies(SProcessDefinition sDefinition, ConnectorArchive connectorArchive,\n            SConnectorImplementationDescriptor connectorImplementationDescriptorToReplace)\n            throws SBonitaReadException, SDependencyException {\n        List<String> jarFileNames = connectorImplementationDescriptorToReplace == null ? Collections.emptyList()\n                : connectorImplementationDescriptorToReplace.getJarDependencies();\n        Set<String> dependenciesToUpdate = new HashSet<>();\n        if (jarFileNames != null) {\n            for (String jarFileName : jarFileNames) {\n                AbstractSDependency dependencyOfArtifact = dependencyService\n                        .getDependencyOfArtifact(sDefinition.getId(), ScopeType.PROCESS, jarFileName);\n                if (dependencyOfArtifact == null) {\n                    // For compatibility with older versions that may still have the wrong name in database:\n                    dependencyOfArtifact = dependencyService.getDependencyOfArtifact(sDefinition.getId(),\n                            ScopeType.PROCESS,\n                            sDefinition.getId() + \"_\" + jarFileName);\n                }\n                if (dependencyOfArtifact != null) {\n                    if (connectorArchive.getDependencies().keySet().contains(jarFileName)) {\n                        dependenciesToUpdate.add(jarFileName);\n                    } else {\n                        dependencyService.deleteDependency(dependencyOfArtifact);\n                    }\n                }\n            }\n        }\n        final long processDefinitionId = sDefinition.getId();\n        for (final Entry<String, byte[]> file : connectorArchive.getDependencies().entrySet()) {\n            if (dependenciesToUpdate.contains(file.getKey())) {\n                dependencyService.updateDependencyOfArtifact(file.getKey(), file.getValue(), file.getKey(),\n                        processDefinitionId, ScopeType.PROCESS);\n            } else {\n                final AbstractSDependency existingDependency = dependencyService\n                        .getDependencyOfArtifact(sDefinition.getId(), ScopeType.PROCESS, file.getKey());\n                if (existingDependency != null) {\n                    //a dependency with this name did exists event if it was not declared as a dependency of the connector inside the connector impl file\n                    if (connectorImplementationDescriptorToReplace != null) {\n                        log.warn(\n                                \"Updating a dependency of the connector {} in version {} of process definition {}. \" +\n                                        \"The jar file {} was not declared in the previous connector implementation but is in the dependencies of the process.\"\n                                        +\n                                        \" The jar is still updated but this can lead to inconsistencies.\",\n                                connectorImplementationDescriptorToReplace.getDefinitionId(),\n                                connectorImplementationDescriptorToReplace.getDefinitionVersion(),\n                                processDefinitionId,\n                                file.getKey());\n                    }\n                    dependencyService.updateDependencyOfArtifact(file.getKey(), file.getValue(), file.getKey(),\n                            processDefinitionId, ScopeType.PROCESS);\n                } else {\n                    dependencyService.createMappedDependency(file.getKey(), file.getValue(), file.getKey(),\n                            processDefinitionId, ScopeType.PROCESS);\n                }\n            }\n        }\n    }\n\n    protected void checkConnectorImplementationIsValid(\n            final SConnectorImplementationDescriptor connectorImplementationDescriptor, final String connectorId,\n            final String connectorVersion)\n            throws SConnectorException, SInvalidConnectorImplementationException {\n        if (!connectorImplementationDescriptor.getDefinitionId().equals(connectorId)\n                || !connectorImplementationDescriptor.getDefinitionVersion().equals(connectorVersion)) {\n            throw new SInvalidConnectorImplementationException(\n                    \"The connector must implement the connectorDefinition with id = <\" + connectorId\n                            + \"> and version = <\" + connectorVersion + \">.\",\n                    connectorImplementationDescriptor);\n        }\n    }\n\n    ConnectorArchive extractConnectorImplementation(final byte[] connectorImplementationArchive)\n            throws SInvalidConnectorImplementationException {\n        ConnectorArchive connectorArchive = new ConnectorArchive();\n        try (ZipInputStream zipInputstream = new ZipInputStream(\n                new ByteArrayInputStream(connectorImplementationArchive))) {\n\n            ZipEntry zipEntry;\n            while ((zipEntry = zipInputstream.getNextEntry()) != null) {\n                String fileName = getFileName(zipEntry.getName());\n                if (!fileName.endsWith(\".jar\") && !fileName.endsWith(\".impl\")) {\n                    continue;\n                }\n                final byte[] fileContent = IOUtil.getBytes(zipInputstream);\n                if (fileName.endsWith(\".jar\")) {\n                    connectorArchive.addDependency(fileName, fileContent);\n                } else {\n                    connectorArchive.setConnectorImpl(fileName, fileContent);\n                }\n                zipInputstream.closeEntry();\n            }\n        } catch (final IOException e) {\n            throw new SInvalidConnectorImplementationException(e);\n        }\n        if (connectorArchive.getConnectorImplContent() == null) {\n            throw new SInvalidConnectorImplementationException(\n                    \"The connector archive do not contains a connector implementation\");\n        }\n        return connectorArchive;\n    }\n\n    private String getFileName(String name) {\n        return name.substring(name.lastIndexOf('/') + 1);\n    }\n\n    private SConnectorImplementationDescriptor parseConnectorImplementation(final byte[] bytes)\n            throws SInvalidConnectorImplementationException {\n        try {\n            return connectorImplementationParser.convert(new String(bytes));\n        } catch (final JAXBException e) {\n            throw new SInvalidConnectorImplementationException(\"Can not load ConnectorImplementation XML.\", e);\n        }\n    }\n\n    private SBARResource getConnectorImplementationResource(long processId, String connectorId, String connectorVersion)\n            throws SBonitaReadException, SInvalidConnectorImplementationException {\n        final List<SBARResource> listFiles = processResourcesService.get(processId, BARResourceType.CONNECTOR, 0, 1000);\n        final Pattern pattern = Pattern.compile(\"^.*\\\\\" + IMPLEMENTATION_EXT + \"$\");\n        SBARResource connectorToReplace = null;\n        for (final SBARResource resource : listFiles) {\n            final String name = resource.getName();\n            if (pattern.matcher(name).matches()) {\n                final SConnectorImplementationDescriptor connectorImplementation = parseConnectorImplementation(\n                        resource.getContent());\n                if (connectorId.equals(connectorImplementation.getDefinitionId())\n                        && connectorVersion.equals(connectorImplementation.getDefinitionVersion())) {\n                    connectorToReplace = resource;\n                    break;\n                }\n            }\n        }\n        return connectorToReplace;\n    }\n\n    private void reLoadConnectors(final SProcessDefinition sProcessDefinition, final String connectorId,\n            final String connectorVersion)\n            throws SConnectorException {\n        final String connectorKey = buildConnectorImplementationKey(sProcessDefinition.getId(), connectorId,\n                connectorVersion);\n        try {\n            cacheService.remove(CONNECTOR_CACHE_NAME, connectorKey);\n            // re_load connectors\n            loadConnectors(sProcessDefinition);\n        } catch (final SCacheException e) {\n            throw new SConnectorException(e);\n        }\n    }\n\n    @Override\n    public Long getNumberOfConnectorImplementations(final long processDefinitionId) throws SConnectorException {\n        try {\n            return processResourcesService.count(processDefinitionId, BARResourceType.CONNECTOR);\n        } catch (SBonitaReadException e) {\n            throw new SConnectorException(e);\n        }\n    }\n\n    @Override\n    public List<SConnectorImplementationDescriptor> getConnectorImplementations(final long processDefinitionId,\n            final int fromIndex,\n            final int numberPerPage, final String field, final OrderByType order) throws SConnectorException {\n        final List<SConnectorImplementationDescriptor> sConnectorImplementationDescriptors = getAllConnectorImplementations(\n                processDefinitionId);\n        if (sConnectorImplementationDescriptors != null && !sConnectorImplementationDescriptors.isEmpty()) {\n            // pagination\n            if (sConnectorImplementationDescriptors.size() <= fromIndex) {\n                throw new SConnectorException(\n                        \"page out of range exception. Total size is <\" + sConnectorImplementationDescriptors.size()\n                                + \">, but from index is <\" + fromIndex + \">\");\n            }\n            // set the comparison field\n            var connectorComparator = new ConnectorImplementationFieldComparator(field);\n            if (order == OrderByType.DESC) {\n                sConnectorImplementationDescriptors.sort(connectorComparator.reversed());\n            } else {\n                // sort with ASC by default\n                sConnectorImplementationDescriptors.sort(connectorComparator);\n            }\n            // sub list\n            int endIndex = fromIndex + numberPerPage;\n            if (endIndex >= sConnectorImplementationDescriptors.size()) {\n                endIndex = sConnectorImplementationDescriptors.size();\n            }\n            return sConnectorImplementationDescriptors.subList(fromIndex, endIndex);\n        }\n        return Collections.emptyList();\n    }\n\n    /**\n     * @param processDefinitionId\n     * @return\n     * @throws SConnectorException\n     */\n    private List<SConnectorImplementationDescriptor> getAllConnectorImplementations(final long processDefinitionId)\n            throws SConnectorException {\n        // get all connector implementations for processDefinitionId\n        List<SConnectorImplementationDescriptor> sConnectorImplementationDescriptors = null;\n        try {\n            final int size = cacheService.getCacheSize(CONNECTOR_CACHE_NAME);\n            // reload connectors if connector cache size is 0;\n            if (size == 0) {\n                this.loadConnectors(processDefinitionId);\n            }\n            sConnectorImplementationDescriptors = getConnectorImplementationsFromCacheService(processDefinitionId);\n            if (sConnectorImplementationDescriptors.isEmpty()) {\n                // reload connectors if cache is not filed, e.g. server restart\n                this.loadConnectors(processDefinitionId);\n                sConnectorImplementationDescriptors = getConnectorImplementationsFromCacheService(processDefinitionId);\n            }\n        } catch (final SCacheException e) {\n            // If cache name not found, ignore it.\n        }\n        return sConnectorImplementationDescriptors;\n    }\n\n    private List<SConnectorImplementationDescriptor> getConnectorImplementationsFromCacheService(\n            final long processDefinitionId)\n            throws SCacheException, SConnectorException {\n        List<SConnectorImplementationDescriptor> sConnectorImplementationDescriptors;\n        sConnectorImplementationDescriptors = new ArrayList<>();\n        final List<?> cacheKeys = cacheService.getKeys(CONNECTOR_CACHE_NAME);\n        if (cacheKeys.size() > 0) {\n            for (final Object cacheKey : cacheKeys) {\n                if (String.valueOf(cacheKey).startsWith(String.valueOf(processDefinitionId))) { // Is it needed?\n                    SConnectorImplementationDescriptor connectorImplementationDescriptor = (SConnectorImplementationDescriptor) cacheService\n                            .get(\n                                    CONNECTOR_CACHE_NAME, cacheKey);\n                    if (!isGoodImplementation(connectorImplementationDescriptor)) {\n                        this.loadConnectors(processDefinitionId);\n                        connectorImplementationDescriptor = (SConnectorImplementationDescriptor) cacheService\n                                .get(CONNECTOR_CACHE_NAME, cacheKey);\n                    }\n                    sConnectorImplementationDescriptors.add(connectorImplementationDescriptor);\n                }\n            }\n        }\n        return sConnectorImplementationDescriptors;\n    }\n\n    /**\n     * @param connectorImplementationDescriptor check the implementation has all required properties or not\n     * @return\n     */\n    private boolean isGoodImplementation(final SConnectorImplementationDescriptor connectorImplementationDescriptor) {\n        return connectorImplementationDescriptor != null\n                && connectorImplementationDescriptor.getImplementationClassName() != null\n                && connectorImplementationDescriptor.getId() != null\n                && connectorImplementationDescriptor.getVersion() != null\n                && connectorImplementationDescriptor.getDefinitionId() != null\n                && connectorImplementationDescriptor.getDefinitionVersion() != null;\n    }\n\n    @Override\n    public SConnectorImplementationDescriptor getConnectorImplementation(final long processDefinitionId,\n            final String connectorId,\n            final String connectorVersion) throws SConnectorException {\n        SConnectorImplementationDescriptor connectorImplementationDescriptor;\n        try {\n            final String connectorImplementationNameInCache = buildConnectorImplementationKey(processDefinitionId,\n                    connectorId, connectorVersion);\n            connectorImplementationDescriptor = (SConnectorImplementationDescriptor) cacheService\n                    .get(CONNECTOR_CACHE_NAME, connectorImplementationNameInCache);\n            if (connectorImplementationDescriptor == null) {\n                /*\n                 * Maybe connector was out of cache\n                 * We try to reload connector before throwing an exception\n                 */\n                loadConnectors(processDefinitionId);\n                connectorImplementationDescriptor = (SConnectorImplementationDescriptor) cacheService.get(\n                        CONNECTOR_CACHE_NAME,\n                        connectorImplementationNameInCache);\n                if (connectorImplementationDescriptor == null) {\n                    throw new SConnectorException(\"Connector implementation not found with id = \" + connectorId\n                            + \" and version = \" + connectorVersion\n                            + \" in process + \" + processDefinitionId);\n                }\n            }\n        } catch (final SCacheException e) {\n            throw new SConnectorException(e);\n        }\n        return connectorImplementationDescriptor;\n    }\n\n    @Override\n    public List<SBARResource> getConnectorImplementations(long processDefinitionId, int from, int numberOfElements)\n            throws SBonitaReadException {\n        return processResourcesService.get(processDefinitionId, BARResourceType.CONNECTOR, from, numberOfElements);\n    }\n\n    @Override\n    public void addConnectorImplementation(Long processDefinitionId, String name, byte[] content)\n            throws SRecorderException {\n        processResourcesService.add(processDefinitionId, name, BARResourceType.CONNECTOR, content);\n    }\n\n    @Override\n    public void removeConnectorImplementations(long processDefinitionId)\n            throws SBonitaReadException, SRecorderException {\n        processResourcesService.removeAll(processDefinitionId, BARResourceType.CONNECTOR);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/impl/SConnectorAdapter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.connector.impl;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.APIAccessor;\nimport org.bonitasoft.engine.commons.NullCheckingUtil;\nimport org.bonitasoft.engine.connector.AbstractConnector;\nimport org.bonitasoft.engine.connector.Connector;\nimport org.bonitasoft.engine.connector.ConnectorException;\nimport org.bonitasoft.engine.connector.ConnectorValidationException;\nimport org.bonitasoft.engine.connector.EngineExecutionContext;\nimport org.bonitasoft.engine.connector.SConnector;\nimport org.bonitasoft.engine.connector.exception.SConnectorException;\nimport org.bonitasoft.engine.connector.exception.SConnectorValidationException;\n\n/**\n * Adapter to execute client connector objects in the server side\n *\n * @author Baptiste Mesta\n * @author Emmanuel Duchastenier\n */\npublic class SConnectorAdapter implements SConnector {\n\n    private final Connector connector;\n\n    public SConnectorAdapter(final Connector connector) {\n        NullCheckingUtil.checkArgsNotNull(connector);\n        this.connector = connector;\n    }\n\n    public Connector getConnector() {\n        return connector;\n    }\n\n    @Override\n    public void setInputParameters(final Map<String, Object> parameters) {\n        final APIAccessor apiAccessor = (APIAccessor) parameters.remove(\"connectorApiAccessor\");\n        final EngineExecutionContext executionContext = (EngineExecutionContext) parameters\n                .remove(\"engineExecutionContext\");\n        if (connector instanceof AbstractConnector) {\n            ((AbstractConnector) connector).setAPIAccessor(apiAccessor);\n            if (executionContext != null) {\n                ((AbstractConnector) connector).setExecutionContext(executionContext);\n            }\n        }\n        connector.setInputParameters(parameters);\n    }\n\n    @Override\n    public void validate() throws SConnectorValidationException {\n        try {\n            connector.validateInputParameters();\n        } catch (final ConnectorValidationException e) {\n            throw new SConnectorValidationException(e);\n        }\n    }\n\n    @Override\n    public Map<String, Object> execute() throws SConnectorException {\n        try {\n            return connector.execute();\n        } catch (final ConnectorException e) {\n            throw new SConnectorException(e);\n        }\n    }\n\n    @Override\n    public void connect() throws SConnectorException {\n        try {\n            connector.connect();\n        } catch (final ConnectorException e) {\n            throw new SConnectorException(e);\n        }\n    }\n\n    @Override\n    public void disconnect() throws SConnectorException {\n        try {\n            connector.disconnect();\n        } catch (final ConnectorException e) {\n            throw new SConnectorException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/delegation/model/SDelegationRule.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.delegation.model;\n\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * Task delegation rule defined by a user (the <i>delegator</i>) granting another user (the <i>delegate</i>)\n * visibility and execution rights on the delegator's human tasks during a bounded absence period.\n * <p>\n * Delegation is <b>not reassignment</b>: tasks remain assigned to the delegator, and the delegate simply gains the\n * ability to see and execute them through dedicated views. Original ownership and full audit trail are preserved\n * via the existing {@code executedBy} / {@code executedBySubstitute} fields and {@code queryable_log}.\n * <p>\n * A rule applies only to the processes declared in its associated {@link SDelegationRuleProcess} whitelist (matched\n * by process name, covering every deployed version). The rule's lifecycle status — {@code scheduled},\n * {@code active}, or {@code expired} — is derived at query time from {@code startDate} and {@code endDate}; there is\n * no boolean activation flag, and deactivation is performed by deleting the row.\n * <p>\n * A user can hold at most one delegation rule at a time, enforced by a {@code UNIQUE} constraint on\n * {@code delegator_id}. {@code lastUpdatedBy} / {@code lastUpdatedAt} record the last administrative or self-service\n * modification for audit purposes.\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@Builder\n@Entity\n@Table(name = \"delegation_rule\")\npublic class SDelegationRule implements PersistentObject {\n\n    public static final String ID_KEY = \"id\";\n    public static final String DELEGATOR_ID_KEY = \"delegatorId\";\n    public static final String DELEGATE_ID_KEY = \"delegateId\";\n    public static final String START_DATE_KEY = \"startDate\";\n    public static final String END_DATE_KEY = \"endDate\";\n    public static final String LAST_UPDATED_BY_KEY = \"lastUpdatedBy\";\n    public static final String LAST_UPDATED_AT_KEY = \"lastUpdatedAt\";\n\n    @Id\n    private long id;\n\n    @Column(name = \"delegator_id\", nullable = false)\n    private long delegatorId;\n\n    @Column(name = \"delegate_id\", nullable = false)\n    private long delegateId;\n\n    @Column(name = \"start_date\", nullable = false)\n    private long startDate;\n\n    @Column(name = \"end_date\", nullable = false)\n    private long endDate;\n\n    @Column(name = \"last_updated_by\", nullable = false)\n    private long lastUpdatedBy;\n\n    @Column(name = \"last_updated_at\", nullable = false)\n    private long lastUpdatedAt;\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/delegation/model/SDelegationRuleProcess.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.delegation.model;\n\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * Process whitelist entry attached to a {@link SDelegationRule}: declares one process (identified by its name) for\n * which the parent rule applies. Each rule must carry at least one entry — a delegation has no effect on a process\n * that is not listed.\n * <p>\n * Entries are unique per rule ({@code UNIQUE (delegation_rule_id, process_name)}) and are removed together with\n * their parent rule ({@code ON DELETE CASCADE}).\n * <p>\n * Process matching is performed by name rather than by definition id so that a single whitelist entry covers every\n * deployed version of the process and remains stable across redeployments.\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@Builder\n@Entity\n@Table(name = \"delegation_rule_process\")\npublic class SDelegationRuleProcess implements PersistentObject {\n\n    public static final String ID_KEY = \"id\";\n    public static final String DELEGATION_RULE_ID_KEY = \"delegationRuleId\";\n    public static final String PROCESS_NAME_KEY = \"processName\";\n\n    @Id\n    private long id;\n\n    @Column(name = \"delegation_rule_id\", nullable = false)\n    private long delegationRuleId;\n\n    @Column(name = \"process_name\", nullable = false)\n    private String processName;\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/DocumentCriterion.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.document;\n\nimport java.io.Serializable;\nimport java.util.Collection;\n\n/**\n * @author Baptiste Mesta\n */\npublic class DocumentCriterion implements Serializable {\n\n    private static final long serialVersionUID = 8636952840023531275L;\n\n    private final DocumentQueryBuilder builder;\n\n    private final DocumentField fieldName;\n\n    private Object value;\n\n    private Object to;\n\n    private Object from;\n\n    private Collection<?> in;\n\n    /**\n     * @param index\n     * @param builder\n     */\n    public DocumentCriterion(final DocumentField index, final DocumentQueryBuilder builder) {\n        this.fieldName = index;\n        this.builder = builder;\n\n    }\n\n    /**\n     * @return the fieldName\n     */\n    public DocumentField getField() {\n        return fieldName;\n    }\n\n    /**\n     * @return the value\n     */\n    public Object getValue() {\n        return value;\n    }\n\n    /**\n     * @return the to\n     */\n    public Object getTo() {\n        return to;\n    }\n\n    /**\n     * @return the from\n     */\n    public Object getFrom() {\n        return from;\n    }\n\n    public DocumentCriterion equalsTo(final Object value) {\n        this.value = value;\n        return this;\n    }\n\n    public DocumentCriterion between(final Object from, final Object to) {\n        this.from = from;\n        this.to = to;\n        return this;\n    }\n\n    public DocumentCriterion in(final Collection<?> values) {\n        this.in = values;\n        return this;\n    }\n\n    public DocumentQueryBuilder rightParenthesis() {\n        builder.rightParenthesis();\n        return builder;\n    }\n\n    public DocumentQueryBuilder or() {\n        builder.or();\n        return builder;\n    }\n\n    public DocumentQueryBuilder and() {\n        builder.and();\n        return builder;\n    }\n\n    public DocumentQueryBuilder allVersion() {\n        builder.allVersion();\n        return builder;\n    }\n\n    public DocumentQueryBuilder latestVersion() {\n        builder.latestVersion();\n        return builder;\n    }\n\n    /**\n     * @return\n     */\n    public Collection<?> getValues() {\n        return in;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/DocumentField.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.document;\n\n/**\n * @author Baptiste Mesta, Emmanuel Duchastenier\n */\npublic enum DocumentField {\n    ID, SERIES_ID, FILENAME, AUTHOR, CREATION_DATE, IS_EMPTY\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/DocumentQueryBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.document;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * @author Baptiste Mesta, Emmanuel Duchastenier\n */\npublic class DocumentQueryBuilder implements Serializable {\n\n    private static final long serialVersionUID = 5788988731741295555L;\n\n    private final List<Object> query;\n\n    private boolean searchAllVersions = false;\n\n    public DocumentQueryBuilder() {\n        query = new ArrayList<Object>();\n    }\n\n    public DocumentCriterion criterion(final DocumentField index) {\n        DocumentCriterion criterion = new DocumentCriterion(index, this);\n        query.add(criterion);\n        return criterion;\n    }\n\n    public DocumentQueryBuilder leftParenthesis() {\n        query.add(\"(\");\n        return this;\n    }\n\n    public DocumentQueryBuilder rightParenthesis() {\n        query.add(\")\");\n        return this;\n    }\n\n    public DocumentQueryBuilder or() {\n        query.add(\"OR\");\n        return this;\n    }\n\n    public DocumentQueryBuilder and() {\n        query.add(\"AND\");\n        return this;\n    }\n\n    public List<Object> getQuery() {\n        return query;\n    }\n\n    public DocumentQueryBuilder allVersion() {\n        this.searchAllVersions = true;\n        return this;\n    }\n\n    public DocumentQueryBuilder latestVersion() {\n        this.searchAllVersions = false;\n        return this;\n    }\n\n    /**\n     * @return\n     */\n    public boolean isSearchAllVersions() {\n        return searchAllVersions;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/api/DocumentService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.document.api;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.exceptions.SObjectAlreadyExistsException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectCreationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.core.document.model.AbstractSDocumentMapping;\nimport org.bonitasoft.engine.core.document.model.AbstractSMappedDocument;\nimport org.bonitasoft.engine.core.document.model.SDocument;\nimport org.bonitasoft.engine.core.document.model.SLightDocument;\nimport org.bonitasoft.engine.core.document.model.SMappedDocument;\nimport org.bonitasoft.engine.core.document.model.archive.SAMappedDocument;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.SRecorderException;\n\n/**\n * @author Nicolas Chabanoles\n * @author Matthieu Chaffotte\n * @author Baptiste Mesta\n * @since 6.0\n */\npublic interface DocumentService {\n\n    String DOCUMENT = \"DOCUMENT\";\n    String DOCUMENTMAPPING = \"DOCUMENTMAPPING\";\n    String SUPERVISED_BY = \"SupervisedBy\";\n\n    /**\n     * Save a document\n     *\n     * @param document the document to store\n     * @param processInstanceId the process instance id to attach the document to\n     * @return The document image from database\n     * @throws SObjectCreationException when the storage has failed\n     */\n    SMappedDocument attachDocumentToProcessInstance(SDocument document, long processInstanceId, String name,\n            String description)\n            throws SObjectCreationException;\n\n    /**\n     * Save a document\n     *\n     * @param document the document to store\n     * @param processInstanceId the process instance id to attach the document to\n     * @param index the index in the list of document\n     * @return The document image from database\n     * @throws SObjectCreationException when the storage has failed\n     */\n    SMappedDocument attachDocumentToProcessInstance(SDocument document, long processInstanceId, String name,\n            String description, int index)\n            throws SObjectCreationException, SObjectAlreadyExistsException;\n\n    /**\n     * Remove this document.\n     * <p>\n     * This archives and deletes mapping to the process, i.e. the content of the document itself will be kept in\n     * database, use {@link #deleteContentOfArchivedDocument(long)} to delete the content.\n     * </p>\n     *\n     * @param document the document mapping to remove\n     */\n    void removeCurrentVersion(AbstractSMappedDocument document) throws SObjectModificationException;\n\n    /**\n     * Remove the document with the specified process instance and name.\n     * <p>\n     * This archives and deletes mapping to the process, i.e. the content of the document itself will be kept in\n     * database, use {@link #deleteContentOfArchivedDocument(long)} to delete the content.\n     * </p>\n     *\n     * @param processInstanceId id of the process having the document\n     * @param documentName name of the document\n     */\n    void removeCurrentVersion(long processInstanceId, String documentName)\n            throws SObjectNotFoundException, SObjectModificationException;\n\n    /**\n     * Get document content by document id\n     *\n     * @param documentId identifier of the document\n     * @return document content\n     */\n    byte[] getDocumentContent(String documentId) throws SObjectNotFoundException;\n\n    /**\n     * Get document with mapping by its mapping id\n     *\n     * @param mappingId identifier of the mapping of the document\n     * @return an SDocumentMapping object with id corresponding to the parameter\n     */\n    SMappedDocument getMappedDocument(long mappingId) throws SObjectNotFoundException, SBonitaReadException;\n\n    /**\n     * Get document by its id\n     *\n     * @param documentId identifier of document\n     * @return an SDocumentMapping object with id corresponding to the parameter\n     */\n    SLightDocument getDocument(long documentId) throws SObjectNotFoundException, SBonitaReadException;\n\n    /**\n     * Get document with mapping by its name in the specific process instance\n     *\n     * @param processInstanceId identifier of process instance\n     * @param documentName name of process document\n     * @return the corresponding SDocumentMapping object\n     */\n    SMappedDocument getMappedDocument(long processInstanceId, String documentName)\n            throws SObjectNotFoundException, SBonitaReadException;\n\n    /**\n     * Get a list of documents for specific process instance, this can be used for pagination\n     *\n     * @param processInstanceId identifier of process instance\n     * @param fromIndex Index of the record to be retrieved from. First record has index 0\n     * @param numberPerPage Number of result we want to get. Maximum number of result returned\n     * @return a list of SDocumentMapping objects\n     */\n    List<SMappedDocument> getDocumentsOfProcessInstance(long processInstanceId, int fromIndex, int numberPerPage,\n            String field, OrderByType order)\n            throws SBonitaReadException;\n\n    /**\n     * Get total number of documents in the specific process instance\n     *\n     * @param processInstanceId identifier of process instance\n     * @return number of documents in the process instance\n     */\n    long getNumberOfDocumentsOfProcessInstance(long processInstanceId) throws SBonitaReadException;\n\n    /**\n     * Get name specified document archived in a certain time in the process instance\n     *\n     * @param processInstanceId identifier of process instance\n     * @param documentName name of document\n     * @param time the archived time of document\n     * @return an SDocumentMapping object archived in the specific time or not archived\n     */\n    AbstractSMappedDocument getMappedDocument(long processInstanceId, String documentName, long time)\n            throws SObjectNotFoundException, SBonitaReadException;\n\n    /**\n     * Get total number of document according to the query criteria\n     *\n     * @param queryOptions a QueryOptions object containing some query conditions\n     * @return number of document satisfied to the query criteria\n     */\n    long getNumberOfDocuments(QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * Search all documents according to the query criteria\n     *\n     * @param queryOptions a QueryOptions object containing some query conditions\n     * @return a list of SDocumentMapping objects\n     */\n    List<SMappedDocument> searchDocuments(QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * Get total number of documents for the specific supervisor\n     *\n     * @param userId identifier of supervisor user\n     * @param queryOptions a QueryOptions object containing some query conditions\n     * @return number of documents for the specific supervisor\n     */\n    long getNumberOfDocumentsSupervisedBy(long userId, QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * Search all documents for the specific supervisor\n     *\n     * @param userId identifier of supervisor user\n     * @param queryOptions a QueryOptions object containing some query conditions\n     * @return a list of SDocumentMapping objects\n     */\n    List<SMappedDocument> searchDocumentsSupervisedBy(long userId, QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Get total number of archived documents according to the query criteria\n     *\n     * @param queryOptions a QueryOptions object containing some query conditions\n     * @return number of archived documents\n     */\n    long getNumberOfArchivedDocuments(QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * Delete the given document mapping without removing the document content.\n     *\n     * @param mappedDocument the document mapping to delete\n     * @throws SObjectModificationException if an error occurred during the deletion\n     */\n    void deleteMappedDocument(AbstractSMappedDocument mappedDocument) throws SObjectModificationException;\n\n    /**\n     * Search all archived documents according to the query criteria.\n     *\n     * @param queryOptions a QueryOptions object containing some query conditions\n     * @return a list of SADocumentMapping objects\n     */\n    List<SAMappedDocument> searchArchivedDocuments(QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * Get total number of archived documents for the specific supervisor\n     *\n     * @param userId identifier of supervisor user\n     * @param queryOptions a QueryOptions object containing some query conditions\n     * @return number of archived documents for the specific supervisor\n     */\n    long getNumberOfArchivedDocumentsSupervisedBy(long userId, QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * Search all archived documents for the specific supervisor\n     *\n     * @param userId identifier of supervisor user\n     * @param queryOptions a QueryOptions object containing some query conditions\n     * @return a list of SADocumentMapping objects\n     */\n    List<SAMappedDocument> searchArchivedDocumentsSupervisedBy(long userId, QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Get the archived version corresponding to a document\n     *\n     * @param documentId identifier of process document\n     * @return the archive of the corresponding document\n     * @throws SObjectNotFoundException when the document does not exist\n     */\n    SAMappedDocument getArchivedVersionOfProcessDocument(long documentId) throws SObjectNotFoundException;\n\n    String generateDocumentURL(String name, String contentStorageId);\n\n    /**\n     * Retrieve an archived document\n     *\n     * @param archivedProcessDocumentId the id of the archived document\n     * @return the corresponding archive\n     * @throws SObjectNotFoundException when the archive does not exist\n     */\n    SAMappedDocument getArchivedDocument(long archivedProcessDocumentId) throws SObjectNotFoundException;\n\n    /**\n     * Delete a document by its id. It does not delete potential mappings, which might throw an error if they are not\n     * deleted beforehand.\n     *\n     * @param documentId the id of the document to delete\n     * @throws SObjectNotFoundException if the document to delete does not exist\n     * @throws SBonitaReadException if an error occurred while getting the document\n     * @throws SObjectModificationException if an error occurred during the deletion\n     */\n    void deleteDocument(long documentId)\n            throws SObjectNotFoundException, SBonitaReadException, SObjectModificationException;\n\n    /**\n     * Delete the given document. It does not delete potential mappings, which might throw an error if they are not\n     * deleted beforehand.\n     *\n     * @param document the document to delete\n     * @throws SObjectModificationException if an error occurred during the deletion\n     */\n    void deleteDocument(SLightDocument document) throws SObjectModificationException;\n\n    /**\n     * Delete documents and their associated mappings to a specified process instance.\n     *\n     * @param processInstanceId the id of the process instance to delete documents from\n     * @throws SBonitaReadException if an error occurred while getting the documents or their mappings\n     * @throws SObjectModificationException if an error occurred during the deletion\n     * @throws SObjectNotFoundException if an element does not exist\n     * @since 6.1\n     */\n    void deleteDocumentsFromProcessInstance(final Long processInstanceId)\n            throws SBonitaReadException, SObjectModificationException, SObjectNotFoundException;\n\n    /**\n     * Delete the document content of a process instance. It only deletes the content, not the document mapping.\n     * Note that to reach the document content, the document mapping must still exist at the time of call: indeed, this\n     * is the only way to reach the content from the process instance.\n     * So if you need to call this method and also delete the document mappings, ensure this method is called first.\n     *\n     * @param processInstanceId the process instance id to delete the document contents from\n     */\n    void deleteDocumentContentsForProcessInstance(Long processInstanceId)\n            throws SBonitaReadException, SObjectModificationException, SObjectNotFoundException;\n\n    /**\n     * delete archived documents mapping and documents links to a set of processes\n     *\n     * @param processInstanceId ids of the source process instances\n     */\n    void deleteArchivedDocuments(List<Long> processInstanceId) throws SBonitaReadException, SRecorderException;\n\n    /**\n     * archive the specific document mapping in the archive date\n     *\n     * @param documentMapping document mapping will be archived\n     * @param archiveDate the archive time\n     * @since 6.4.0\n     */\n    void archive(AbstractSDocumentMapping documentMapping, long archiveDate) throws SObjectModificationException;\n\n    /**\n     * @param mappedDocument the document to update\n     * @param document the new content\n     * @param index the new index\n     * @since 6.4.0\n     */\n    void updateDocumentOfList(final AbstractSDocumentMapping mappedDocument, final SDocument document, int index)\n            throws SObjectModificationException;\n\n    /**\n     * update the index of a document inside the list\n     *\n     * @param mappedDocument the document to update\n     * @param index the new index\n     * @since 6.4.0\n     */\n    void updateDocumentIndex(final AbstractSDocumentMapping mappedDocument, int index)\n            throws SObjectModificationException;\n\n    /**\n     * Get a list of document. if there is no document in the list returns an empty list\n     *\n     * @param documentName the name of the document list\n     * @param processInstanceId the id of the process instance that contains the list\n     * @param fromIndex pagination parameter\n     * @param numberOfResult pagination parameter\n     * @return the list of document\n     * @since 6.4.0\n     */\n    List<SMappedDocument> getDocumentList(String documentName, long processInstanceId, int fromIndex,\n            int numberOfResult) throws SBonitaReadException;\n\n    /**\n     * @param documentToUpdate the document mapping to udpate\n     * @param sDocument the value to set th emapping with\n     * @return the updated document mapping\n     */\n    SMappedDocument updateDocument(AbstractSDocumentMapping documentToUpdate, SDocument sDocument)\n            throws SObjectModificationException;\n\n    /**\n     * Get a list of document at a given time. if there is no document in the list returns an empty list.\n     * <p>\n     * elements are taken from archive and from non archived mapping if the process is still running\n     * </p>\n     *\n     * @param documentName the name of the document list\n     * @param processInstanceId the id of the process instance that contains the list\n     * @param time time when the list was like that\n     * @return the list of document\n     * @since 6.4.0\n     */\n    List<AbstractSMappedDocument> getDocumentList(String documentName, long processInstanceId, long time)\n            throws SBonitaReadException;\n\n    /**\n     * Remove the content of an archived document while keeping it's metadata.\n     * <p>\n     * After calling this method you will not be able to retrieve the content of the document since it will be erased\n     * from the database.\n     * This method can be useful for keeping history of a document without overloading the database.\n     * </p>\n     *\n     * @param archivedDocumentId the id of the archived document to remove content on\n     * @throws SObjectNotFoundException if the document to delete does not exist\n     * @since 6.4.0\n     */\n    void deleteContentOfArchivedDocument(long archivedDocumentId)\n            throws SObjectNotFoundException, SBonitaReadException, SRecorderException;\n\n    /**\n     * update the document having the documentId with this new version\n     *\n     * @param documentId the id of the document to update\n     * @param sDocument the new version of the document @return\n     */\n    SMappedDocument updateDocument(long documentId, SDocument sDocument)\n            throws SObjectNotFoundException, SObjectModificationException, SBonitaReadException;\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/api/SDocumentMappingCriterion.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.document.api;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic enum SDocumentMappingCriterion {\n    NAME_DESC, NAME_ASC;\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/api/impl/DocumentServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.document.api.impl;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.archive.ArchiveInsertRecord;\nimport org.bonitasoft.engine.archive.ArchiveService;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectAlreadyExistsException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectCreationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.core.document.api.DocumentService;\nimport org.bonitasoft.engine.core.document.model.AbstractSDocumentMapping;\nimport org.bonitasoft.engine.core.document.model.AbstractSMappedDocument;\nimport org.bonitasoft.engine.core.document.model.SDocument;\nimport org.bonitasoft.engine.core.document.model.SDocumentMapping;\nimport org.bonitasoft.engine.core.document.model.SLightDocument;\nimport org.bonitasoft.engine.core.document.model.SMappedDocument;\nimport org.bonitasoft.engine.core.document.model.archive.SADocumentMapping;\nimport org.bonitasoft.engine.core.document.model.archive.SAMappedDocument;\nimport org.bonitasoft.engine.core.document.model.recorder.SelectDescriptorBuilder;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\n\n/**\n * @author Nicolas Chabanoles\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @author Baptiste Mesta\n */\npublic class DocumentServiceImpl implements DocumentService {\n\n    private final SDocumentDownloadURLProvider urlProvider;\n    private final ArchiveService archiveService;\n    private final Recorder recorder;\n    private final ReadPersistenceService persistenceService;\n    private final ReadPersistenceService definitiveArchiveReadPersistenceService;\n\n    public DocumentServiceImpl(final Recorder recorder, final ReadPersistenceService persistenceService,\n            final SDocumentDownloadURLProvider urlProvider, final ArchiveService archiveService) {\n        this.recorder = recorder;\n        this.persistenceService = persistenceService;\n        this.urlProvider = urlProvider;\n        this.archiveService = archiveService;\n        definitiveArchiveReadPersistenceService = archiveService.getDefinitiveArchiveReadPersistenceService();\n\n    }\n\n    @Override\n    public SMappedDocument attachDocumentToProcessInstance(final SDocument document, final long processInstanceId,\n            final String name, final String description)\n            throws SObjectCreationException {\n        try {\n            insertDocument(document);\n            final SDocumentMapping documentMapping = create(document.getId(), processInstanceId, name, description, -1);\n            return new SMappedDocument(documentMapping, document);\n        } catch (final SBonitaException e) {\n            throw new SObjectCreationException(e.getMessage(), e);\n        }\n    }\n\n    @Override\n    public SMappedDocument attachDocumentToProcessInstance(final SDocument document, final long processInstanceId,\n            final String name, final String description,\n            final int index)\n            throws SObjectCreationException, SObjectAlreadyExistsException {\n        try {\n            if (index == -1) {\n                final SMappedDocument mappedDocumentInternal = getMappedDocumentInternal(processInstanceId, name);\n                if (mappedDocumentInternal != null) {\n                    throw new SObjectAlreadyExistsException(\"A document already exists with name \" + name\n                            + \" and process instance id \" + processInstanceId);\n                }\n            }\n            insertDocument(document);\n            final SDocumentMapping documentMapping = create(document.getId(), processInstanceId, name, description,\n                    index);\n            return new SMappedDocument(documentMapping, document);\n        } catch (final SObjectAlreadyExistsException e) {\n            throw new SObjectAlreadyExistsException(e);\n        } catch (final SBonitaException e) {\n            throw new SObjectCreationException(e);\n        }\n    }\n\n    @Override\n    public void updateDocumentOfList(final AbstractSDocumentMapping mappedDocument, final SDocument document,\n            final int index)\n            throws SObjectModificationException {\n        updateDocument(mappedDocument, document, index);\n    }\n\n    @Override\n    public void updateDocumentIndex(final AbstractSDocumentMapping mappedDocument, final int index)\n            throws SObjectModificationException {\n        final Map<String, Object> params = new HashMap<>(2);\n        params.put(\"index\", index);\n        updateFields(mappedDocument, params);\n    }\n\n    private void updateFields(final AbstractSDocumentMapping mappedDocument, final Map<String, Object> params)\n            throws SObjectModificationException {\n        try {\n            recorder.recordUpdate(UpdateRecord.buildSetFields(mappedDocument,\n                    params), DOCUMENTMAPPING);\n        } catch (final SRecorderException e) {\n            throw new SObjectModificationException(e);\n        }\n    }\n\n    private void updateMapping(final long documentId, final AbstractSDocumentMapping sDocumentMapping,\n            final String description,\n            final int index)\n            throws SObjectModificationException {\n        final Map<String, Object> params = new HashMap<>(2);\n        params.put(\"documentId\", documentId);\n        params.put(\"description\", description);\n        params.put(\"version\", incrementVersion(sDocumentMapping.getVersion()));\n        params.put(\"index\", index);\n        updateFields(sDocumentMapping, params);\n    }\n\n    private String incrementVersion(final String version) {\n        return String.valueOf(Integer.parseInt(version) + 1);\n    }\n\n    private void insertDocument(final SDocument document) throws SRecorderException {\n        recorder.recordInsert(new InsertRecord(document), DOCUMENT);\n    }\n\n    @Override\n    public void deleteDocumentsFromProcessInstance(final Long processInstanceId)\n            throws SBonitaReadException, SObjectModificationException, SObjectNotFoundException {\n        List<SMappedDocument> mappedDocuments;\n        do {\n            mappedDocuments = persistenceService\n                    .selectList(SelectDescriptorBuilder.getDocumentMappingsForProcessInstance(\n                            processInstanceId, 0, 100, null, null));\n            for (final SMappedDocument mappedDocument : mappedDocuments) {\n                deleteMappedDocument(mappedDocument);\n            }\n        } while (!mappedDocuments.isEmpty());\n    }\n\n    @Override\n    public void deleteDocumentContentsForProcessInstance(final Long processInstanceId)\n            throws SBonitaReadException, SObjectModificationException, SObjectNotFoundException {\n        List<SMappedDocument> mappedDocuments;\n        do {\n            mappedDocuments = getDocumentsOfProcessInstance(processInstanceId, 0, 100, null, null);\n            for (final SMappedDocument mappedDocument : mappedDocuments) {\n                // remove the document itself\n                deleteDocument(mappedDocument.getDocumentId());\n            }\n        } while (!mappedDocuments.isEmpty());\n    }\n\n    @Override\n    public String generateDocumentURL(final String name, final String contentStorageId) {\n        return urlProvider.generateURL(name, contentStorageId);\n    }\n\n    @Override\n    public SAMappedDocument getArchivedDocument(final long archivedProcessDocumentId) throws SObjectNotFoundException {\n        try {\n            final SAMappedDocument docMapping = definitiveArchiveReadPersistenceService\n                    .selectById(SelectDescriptorBuilder\n                            .getArchivedDocumentById(archivedProcessDocumentId));\n            if (docMapping == null) {\n                throw new SObjectNotFoundException(\"Document not found with identifier: \" + archivedProcessDocumentId);\n            }\n            return docMapping;\n        } catch (final SBonitaReadException e) {\n            throw new SObjectNotFoundException(e);\n        }\n    }\n\n    @Override\n    public SAMappedDocument getArchivedVersionOfProcessDocument(final long documentId) throws SObjectNotFoundException {\n        try {\n            final SAMappedDocument aDocMapping = definitiveArchiveReadPersistenceService\n                    .selectOne(SelectDescriptorBuilder\n                            .getArchivedVersionOfDocument(documentId));\n            if (aDocMapping == null) {\n                throw new SObjectNotFoundException(documentId);\n            }\n            return aDocMapping;\n        } catch (final SBonitaReadException e) {\n            throw new SObjectNotFoundException(\"Document not found with identifier: \" + documentId, e);\n        }\n    }\n\n    @Override\n    public SLightDocument getDocument(final long documentId) throws SObjectNotFoundException, SBonitaReadException {\n        final SLightDocument document = persistenceService\n                .selectById(new SelectByIdDescriptor<>(SLightDocument.class,\n                        documentId));\n        if (document == null) {\n            throw new SObjectNotFoundException(\"Document with id \" + documentId + \" not found\");\n        }\n        return document;\n    }\n\n    @Override\n    public SMappedDocument getMappedDocument(final long processInstanceId, final String documentName)\n            throws SObjectNotFoundException, SBonitaReadException {\n        final SMappedDocument document = getMappedDocumentInternal(processInstanceId, documentName);\n        if (document == null) {\n            throw new SObjectNotFoundException(\n                    \"Document not found: \" + documentName + \" for process instance: \" + processInstanceId);\n        }\n        return document;\n    }\n\n    private SMappedDocument getMappedDocumentInternal(final long processInstanceId, final String documentName)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = new HashMap<>(2);\n        parameters.put(\"processInstanceId\", processInstanceId);\n        parameters.put(\"name\", documentName);\n        final SelectOneDescriptor<SMappedDocument> selectOneDescriptor = new SelectOneDescriptor<>(\n                \"getSMappedDocumentOfProcessWithName\",\n                parameters,\n                SDocument.class);\n        return persistenceService.selectOne(selectOneDescriptor);\n    }\n\n    @Override\n    public AbstractSMappedDocument getMappedDocument(final long processInstanceId, final String documentName,\n            final long time)\n            throws SObjectNotFoundException,\n            SBonitaReadException {\n        final List<SAMappedDocument> docMapping = definitiveArchiveReadPersistenceService\n                .selectList(SelectDescriptorBuilder\n                        .getSAMappedDocumentOfProcessWithName(\n                                processInstanceId,\n                                documentName, time));\n        if (docMapping.isEmpty()) {\n            return getMappedDocument(processInstanceId, documentName);\n        }\n        return docMapping.get(0);\n    }\n\n    @Override\n    public byte[] getDocumentContent(final String documentId) throws SObjectNotFoundException {\n        try {\n            final Long id = Long\n                    .valueOf(documentId);\n            return getDocumentWithContent(id).getContent();\n        } catch (final NumberFormatException e) {\n            throw new SObjectNotFoundException(\"Identifier \" + documentId + \" is not valid, it must be a long\");\n        } catch (final SBonitaReadException e) {\n            throw new SObjectNotFoundException(e);\n        }\n    }\n\n    private SDocument getDocumentWithContent(final Long id) throws SBonitaReadException, SObjectNotFoundException {\n        final SDocument document = persistenceService\n                .selectById(new SelectByIdDescriptor<>(SDocument.class, id));\n        if (document == null) {\n            throw new SObjectNotFoundException(\"Document with id \" + id + \" not found\");\n        }\n        return document;\n    }\n\n    @Override\n    public SMappedDocument getMappedDocument(final long mappingId)\n            throws SObjectNotFoundException, SBonitaReadException {\n        final SMappedDocument document = persistenceService.selectById(new SelectByIdDescriptor<>(\n                SMappedDocument.class,\n                mappingId));\n        if (document == null) {\n            throw new SObjectNotFoundException(\"SMappedDocument with id \" + mappingId + \" not found\");\n        }\n        return document;\n    }\n\n    @Override\n    public List<SMappedDocument> getDocumentsOfProcessInstance(final long processInstanceId, final int fromIndex,\n            final int numberPerPage, final String field,\n            final OrderByType order) throws SBonitaReadException {\n        return persistenceService.selectList(SelectDescriptorBuilder.getDocumentMappingsForProcessInstance(\n                processInstanceId, fromIndex, numberPerPage, field, order));\n    }\n\n    @Override\n    public long getNumberOfArchivedDocuments(final QueryOptions queryOptions) throws SBonitaReadException {\n        return definitiveArchiveReadPersistenceService.getNumberOfEntities(SAMappedDocument.class, queryOptions, null);\n    }\n\n    @Override\n    public long getNumberOfArchivedDocumentsSupervisedBy(final long userId, final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"userId\", userId);\n        return definitiveArchiveReadPersistenceService.getNumberOfEntities(SAMappedDocument.class, SUPERVISED_BY,\n                queryOptions, parameters);\n    }\n\n    @Override\n    public long getNumberOfDocuments(final QueryOptions queryOptions) throws SBonitaReadException {\n        return persistenceService.getNumberOfEntities(SMappedDocument.class, queryOptions, null);\n    }\n\n    @Override\n    public long getNumberOfDocumentsOfProcessInstance(final long processInstanceId) throws SBonitaReadException {\n        return persistenceService\n                .selectOne(SelectDescriptorBuilder.getNumberOfSMappedDocumentOfProcess(processInstanceId));\n    }\n\n    @Override\n    public long getNumberOfDocumentsSupervisedBy(final long userId, final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"userId\", userId);\n        return persistenceService.getNumberOfEntities(SMappedDocument.class, SUPERVISED_BY, queryOptions, parameters);\n    }\n\n    @Override\n    public void removeCurrentVersion(final AbstractSMappedDocument document) throws SObjectModificationException {\n        archive(document, System.currentTimeMillis());\n        deleteMappedDocument(document);\n    }\n\n    @Override\n    public void removeCurrentVersion(final long processInstanceId, final String documentName)\n            throws SObjectNotFoundException, SObjectModificationException {\n        try {\n            removeCurrentVersion(getMappedDocument(processInstanceId, documentName));\n        } catch (final SBonitaReadException e) {\n            throw new SObjectModificationException(e);\n        }\n    }\n\n    @Override\n    public void deleteDocument(long documentId)\n            throws SObjectNotFoundException, SBonitaReadException, SObjectModificationException {\n        deleteDocument(getDocument(documentId));\n    }\n\n    @Override\n    public void deleteDocument(final SLightDocument document) throws SObjectModificationException {\n        try {\n            recorder.recordDelete(new DeleteRecord(document), \"SDocument\");\n        } catch (final SRecorderException e) {\n            throw new SObjectModificationException(e);\n        }\n    }\n\n    @Override\n    public void deleteMappedDocument(final AbstractSMappedDocument mappedDocument) throws SObjectModificationException {\n        try {\n            recorder.recordDelete(new DeleteRecord(mappedDocument), \"SDocumentMapping\");\n        } catch (final SBonitaException e) {\n            throw new SObjectModificationException(e.getMessage(), e);\n        }\n    }\n\n    @Override\n    public List<SAMappedDocument> searchArchivedDocuments(final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        final ReadPersistenceService persistenceService1 = archiveService.getDefinitiveArchiveReadPersistenceService();\n        return persistenceService1.searchEntity(SAMappedDocument.class, queryOptions, null);\n    }\n\n    @Override\n    public List<SAMappedDocument> searchArchivedDocumentsSupervisedBy(final long userId,\n            final QueryOptions queryOptions) throws SBonitaReadException {\n        final ReadPersistenceService persistenceService1 = archiveService.getDefinitiveArchiveReadPersistenceService();\n        final Map<String, Object> parameters = Collections.singletonMap(\"userId\", userId);\n        return persistenceService1.searchEntity(SAMappedDocument.class, SUPERVISED_BY, queryOptions, parameters);\n    }\n\n    @Override\n    public List<SMappedDocument> searchDocuments(final QueryOptions queryOptions) throws SBonitaReadException {\n        try {\n            return persistenceService.searchEntity(SMappedDocument.class, queryOptions, null);\n        } catch (final SBonitaReadException e) {\n            throw new SBonitaReadException(e);\n        }\n    }\n\n    @Override\n    public List<SMappedDocument> searchDocumentsSupervisedBy(final long userId, final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        try {\n            final Map<String, Object> parameters = Collections.singletonMap(\"userId\", userId);\n            return persistenceService.searchEntity(SMappedDocument.class, SUPERVISED_BY, queryOptions, parameters);\n        } catch (final SBonitaReadException e) {\n            throw new SBonitaReadException(e);\n        }\n    }\n\n    @Override\n    public void deleteArchivedDocuments(List<Long> processInstanceIds) throws SBonitaReadException, SRecorderException {\n        List<SAMappedDocument> archivedMappedDocuments = persistenceService\n                .selectList(new SelectListDescriptor<>(\"getArchiveMappingsOfProcessInstances\",\n                        Collections.singletonMap(\"processInstanceIds\", processInstanceIds), SAMappedDocument.class,\n                        QueryOptions.countQueryOptions()));\n        if (archivedMappedDocuments.isEmpty()) {\n            //no documents to delete\n            return;\n        }\n        List<Long> documentIds = new ArrayList<>();\n        List<Long> documentMappingIds = new ArrayList<>();\n        for (SAMappedDocument mappedDocument : archivedMappedDocuments) {\n            documentIds.add(mappedDocument.getDocumentId());\n            documentMappingIds.add(mappedDocument.getId());\n        }\n\n        archiveService.deleteFromQuery(\"deleteArchiveDocumentsByIds\", Collections.singletonMap(\"ids\", documentIds));\n        archiveService.deleteFromQuery(\"deleteArchiveMappingsByIds\",\n                Collections.singletonMap(\"ids\", documentMappingIds));\n    }\n\n    private SDocumentMapping create(final long documentId, final long processInstanceId, final String name,\n            final String description, final int index)\n            throws SRecorderException {\n        final SDocumentMapping documentMapping = new SDocumentMapping(documentId, processInstanceId, name);\n        documentMapping.setDescription(description);\n        documentMapping.setVersion(\"1\");\n        documentMapping.setIndex(index);\n        recorder.recordInsert(new InsertRecord(documentMapping), DOCUMENTMAPPING);\n        return documentMapping;\n    }\n\n    @Override\n    public void archive(final AbstractSDocumentMapping docMapping, final long archiveDate)\n            throws SObjectModificationException {\n        if (archiveService.isArchivable(SDocumentMapping.class)) {\n            final SADocumentMapping saDocumentMapping = new SADocumentMapping(docMapping.getDocumentId(),\n                    docMapping.getProcessInstanceId(),\n                    archiveDate, docMapping.getId(), docMapping.getName(), docMapping.getDescription(),\n                    docMapping.getVersion());\n            saDocumentMapping.setIndex(docMapping.getIndex());\n            final ArchiveInsertRecord insertRecord = new ArchiveInsertRecord(saDocumentMapping);\n            try {\n                archiveService.recordInsert(archiveDate, insertRecord);\n            } catch (final SBonitaException e) {\n                throw new SObjectModificationException(\n                        \"Unable to archive the document with id = <\" + docMapping.getId() + \">\", e);\n            }\n        }\n    }\n\n    @Override\n    public List<SMappedDocument> getDocumentList(final String documentName, final long processInstanceId,\n            final int fromIndex, final int numberOfResult)\n            throws SBonitaReadException {\n        return persistenceService.selectList(\n                SelectDescriptorBuilder.getDocumentList(documentName, processInstanceId, new QueryOptions(fromIndex,\n                        numberOfResult)));\n    }\n\n    @Override\n    public void deleteContentOfArchivedDocument(final long archivedDocumentId)\n            throws SObjectNotFoundException, SBonitaReadException, SRecorderException {\n        final SAMappedDocument archivedDocument = getArchivedDocument(archivedDocumentId);\n        final SDocument document = getDocumentWithContent(archivedDocument.getDocumentId());\n        final HashMap<String, Object> updateFields = new HashMap<>();\n        updateFields.put(\"content\", null);\n        updateFields.put(\"hasContent\", false);\n        recorder.recordUpdate(UpdateRecord.buildSetFields(document, updateFields),\n                DOCUMENT);\n    }\n\n    @Override\n    public SMappedDocument updateDocument(final long documentId, final SDocument sDocument)\n            throws SBonitaReadException, SObjectNotFoundException,\n            SObjectModificationException {\n        final AbstractSDocumentMapping sDocumentMapping = getMappedDocument(documentId);\n        return updateDocument(sDocumentMapping, sDocument);\n    }\n\n    @Override\n    public SMappedDocument updateDocument(final AbstractSDocumentMapping documentToUpdate, final SDocument sDocument)\n            throws SObjectModificationException {\n        return updateDocument(documentToUpdate, sDocument, documentToUpdate.getIndex());\n    }\n\n    private SMappedDocument updateDocument(final AbstractSDocumentMapping documentToUpdate, final SDocument sDocument,\n            final int index)\n            throws SObjectModificationException {\n        //insert new document\n        try {\n            insertDocument(sDocument);\n        } catch (final SRecorderException e) {\n            throw new SObjectModificationException(e);\n        }\n        //update mapping\n        archive(documentToUpdate, System.currentTimeMillis());\n        updateMapping(sDocument.getId(), documentToUpdate, documentToUpdate.getDescription(), index);\n        return new SMappedDocument(documentToUpdate, sDocument);\n    }\n\n    @Override\n    public List<AbstractSMappedDocument> getDocumentList(final String documentName, final long processInstanceId,\n            final long time) throws SBonitaReadException {\n        final List<SAMappedDocument> archivedList = persistenceService\n                .selectList(SelectDescriptorBuilder.getArchivedDocumentList(documentName,\n                        processInstanceId, new QueryOptions(0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS), time));\n        final List<SMappedDocument> elementsInJournal = persistenceService\n                .selectList(SelectDescriptorBuilder.getDocumentListCreatedBefore(documentName,\n                        processInstanceId, new QueryOptions(0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS), time));\n\n        final List<AbstractSMappedDocument> result = new ArrayList<>(\n                archivedList.size() + elementsInJournal.size());\n        result.addAll(archivedList);\n        result.addAll(elementsInJournal);\n        return result;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/api/impl/SDocumentDownloadURLProvider.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.document.api.impl;\n\n/**\n * @author Nicolas Chabanoles\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface SDocumentDownloadURLProvider {\n\n    String generateURL(String documentName, String contentStorageId);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/api/impl/SDocumentDownloadURLProviderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.document.api.impl;\n\nimport java.io.UnsupportedEncodingException;\nimport java.net.URLEncoder;\n\n/**\n * @author Nicolas Chabanoles\n */\npublic class SDocumentDownloadURLProviderImpl implements SDocumentDownloadURLProvider {\n\n    private final String servletUrl;\n\n    public SDocumentDownloadURLProviderImpl(final String servletUrl) {\n        this.servletUrl = servletUrl;\n    }\n\n    @Override\n    public String generateURL(String documentName, String contentStorageId) {\n        final StringBuffer buffer = new StringBuffer(servletUrl);\n        buffer.append(\"?fileName=\").append(getUrlEncodedValue(documentName));\n        buffer.append(\"&contentStorageId=\").append(contentStorageId);\n        return buffer.toString();\n    }\n\n    private String getUrlEncodedValue(String documentName) {\n        String encoding = \"UTF-8\";\n        try {\n            return URLEncoder.encode(documentName, encoding);\n        } catch (UnsupportedEncodingException e) {\n            // This exception should never occur,\n            // as UnsupportedEncodingException is thrown only if the named encoding is not supported\n            throw new IllegalStateException(\"the named encoding \" + encoding + \" is not supported.\", e);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/model/AbstractSDocument.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.document.model;\n\nimport javax.persistence.Column;\nimport javax.persistence.Id;\nimport javax.persistence.MappedSuperclass;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@NoArgsConstructor\n@AllArgsConstructor\n@Data\n@SuperBuilder\n@MappedSuperclass\npublic class AbstractSDocument implements PersistentObject {\n\n    @Id\n    private long id;\n\n    private long author;\n    private String url;\n\n    @Column(name = \"creationdate\")\n    private long creationDate;\n\n    @Column(name = \"hascontent\")\n    private boolean hasContent;\n\n    @Column(name = \"filename\")\n    private String fileName;\n\n    @Column(name = \"mimeType\")\n    private String mimeType;\n\n    public boolean hasContent() {\n        return hasContent;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/model/AbstractSDocumentMapping.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.document.model;\n\nimport javax.persistence.Column;\nimport javax.persistence.Id;\nimport javax.persistence.MappedSuperclass;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * Mapping for a document\n * <p>\n * can be part of a list of document, in that case all documents have the same name and an index\n * If it's standalone documents index = -1\n *\n * @author Baptiste Mesta\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@SuperBuilder\n@MappedSuperclass\npublic class AbstractSDocumentMapping implements PersistentObject {\n\n    @Id\n    private long id;\n\n    @Column(name = \"processinstanceid\")\n    private long processInstanceId;\n\n    @Column(name = \"documentid\")\n    private long documentId;\n\n    private String name;\n    private String description;\n    private String version;\n\n    @Column(name = \"index_\")\n    private int index;\n\n    protected AbstractSDocumentMapping(long documentId, long processInstanceId, String name) {\n        this.documentId = documentId;\n        this.processInstanceId = processInstanceId;\n        this.name = name;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/model/AbstractSMappedDocument.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.document.model;\n\nimport javax.persistence.FetchType;\nimport javax.persistence.JoinColumn;\nimport javax.persistence.ManyToOne;\nimport javax.persistence.MappedSuperclass;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * @author Baptiste Mesta\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@SuperBuilder\n@EqualsAndHashCode(callSuper = true)\n@MappedSuperclass\npublic abstract class AbstractSMappedDocument extends AbstractSDocumentMapping {\n\n    @ManyToOne(fetch = FetchType.EAGER)\n    @JoinColumn(name = \"documentid\", referencedColumnName = \"id\", insertable = false, updatable = false)\n    protected SLightDocument document;\n\n    public AbstractSMappedDocument(AbstractSDocumentMapping documentMapping, SDocument document) {\n        this.setId(documentMapping.getId());\n        this.setName(documentMapping.getName());\n        this.setDescription(documentMapping.getDescription());\n        this.setVersion(documentMapping.getVersion());\n        this.setDocumentId(documentMapping.getDocumentId());\n        this.setProcessInstanceId(documentMapping.getProcessInstanceId());\n        this.setIndex(documentMapping.getIndex());\n        this.document = SLightDocument.builder()\n                .id(document.getId())\n                .fileName(document.getFileName())\n                .hasContent(document.hasContent())\n                .mimeType(document.getMimeType())\n                .author(document.getAuthor())\n                .url(document.getUrl())\n                .creationDate(document.getCreationDate())\n                .build();\n    }\n\n    public AbstractSDocument getDocument() {\n        return document;\n    }\n\n    public void setDocument(SLightDocument document) {\n        this.document = document;\n    }\n\n    public long getAuthor() {\n        return document.getAuthor();\n    }\n\n    public long getCreationDate() {\n        return document.getCreationDate();\n    }\n\n    public String getMimeType() {\n        return document.getMimeType();\n    }\n\n    public String getFileName() {\n        return document.getFileName();\n    }\n\n    public boolean hasContent() {\n        return document.hasContent();\n    }\n\n    public String getUrl() {\n        return document.getUrl();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/model/SDocument.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.document.model;\n\nimport javax.persistence.Cacheable;\nimport javax.persistence.Entity;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.ToString;\nimport lombok.experimental.SuperBuilder;\nimport org.hibernate.annotations.Type;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@EqualsAndHashCode(callSuper = true, exclude = \"content\")\n@ToString(exclude = { \"content\" })\n@SuperBuilder\n@Entity\n@Table(name = \"document\")\n@Cacheable(false)\npublic class SDocument extends AbstractSDocument {\n\n    public static final String ID = \"id\";\n    public static final String NAME = \"name\";\n    public static final String AUTHOR = \"author\";\n    public static final String CREATION_DATE = \"creationDate\";\n    public static final String HAS_CONTENT = \"hasContent\";\n    public static final String FILENAME = \"fileName\";\n    public static final String MIMETYPE = \"mimeType\";\n    public static final String URL = \"url\";\n    public static final String VERSION = \"version\";\n    public static final String DESCRIPTION = \"description\";\n    public static final String INDEX = \"index\";\n\n    @Type(type = \"materialized_blob\")\n    private byte[] content;\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/model/SDocumentMapping.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.document.model;\n\nimport javax.persistence.Cacheable;\nimport javax.persistence.Entity;\nimport javax.persistence.Table;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * Mapping for a document\n * <p>\n * can be part of a list of document, in that case all documents have the same name and an index\n * If it's standalone documents index = -1\n *\n * @author Baptiste Mesta\n */\n@Data\n@NoArgsConstructor\n@SuperBuilder\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@Table(name = \"document_mapping\")\n@Cacheable(false)\npublic class SDocumentMapping extends AbstractSDocumentMapping {\n\n    public SDocumentMapping(long documentId, long processInstanceId, String name) {\n        super(documentId, processInstanceId, name);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/model/SLightDocument.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.document.model;\n\nimport javax.persistence.Cacheable;\nimport javax.persistence.Entity;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@AllArgsConstructor\n@Data\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true)\n@SuperBuilder\n@Entity\n@Table(name = \"document\")\n@Cacheable(false)\npublic class SLightDocument extends AbstractSDocument {\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/model/SMappedDocument.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.document.model;\n\nimport javax.persistence.Cacheable;\nimport javax.persistence.Entity;\nimport javax.persistence.Table;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * @author Baptiste Mesta\n */\n@Data\n@NoArgsConstructor\n@SuperBuilder\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@Table(name = \"document_mapping\")\n@Cacheable(false)\npublic class SMappedDocument extends AbstractSMappedDocument {\n\n    public SMappedDocument(AbstractSDocumentMapping documentMapping, SDocument document) {\n        this.setId(documentMapping.getId());\n        this.setName(documentMapping.getName());\n        this.setDescription(documentMapping.getDescription());\n        this.setVersion(documentMapping.getVersion());\n        this.setDocumentId(documentMapping.getDocumentId());\n        this.setProcessInstanceId(documentMapping.getProcessInstanceId());\n        this.setIndex(documentMapping.getIndex());\n        this.document = SLightDocument.builder()\n                .id(document.getId())\n                .fileName(document.getFileName())\n                .hasContent(document.hasContent())\n                .mimeType(document.getMimeType())\n                .author(document.getAuthor())\n                .url(document.getUrl())\n                .creationDate(document.getCreationDate())\n                .build();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/model/archive/SADocumentMapping.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.document.model.archive;\n\nimport javax.persistence.Cacheable;\nimport javax.persistence.Entity;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\nimport org.bonitasoft.engine.core.document.model.AbstractSDocumentMapping;\nimport org.bonitasoft.engine.core.document.model.SDocumentMapping;\nimport org.bonitasoft.engine.persistence.ArchivedPersistentObject;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@SuperBuilder\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@Table(name = \"arch_document_mapping\")\n@Cacheable(false)\npublic class SADocumentMapping extends AbstractSDocumentMapping implements ArchivedPersistentObject {\n\n    public static final String ID = \"id\";\n    public static final String PROCESS_INSTANCE_ID = \"processInstanceId\";\n    public static final String ARCHIVE_DATE = \"archiveDate\";\n    public static final String SOURCE_OBJECT_ID = \"sourceObjectId\";\n    public static final String DOCUMENT_ID = \"documentId\";\n    public static final String URL = \"url\";\n    public static final String NAME = \"name\";\n    public static final String HAS_CONTENT = \"hasContent\";\n    public static final String AUTHOR = \"author\";\n    public static final String FILE_NAME = \"fileName\";\n    public static final String MIME_TYPE = \"mimeType\";\n    public static final String CREATION_DATE = \"creationDate\";\n    public static final String DESCRIPTION = \"description\";\n    public static final String VERSION = \"version\";\n    public static final String INDEX = \"index\";\n\n    private long archiveDate;\n    private long sourceObjectId;\n\n    public SADocumentMapping(final long documentId, final long processInstanceId, final long archiveDate,\n            final long sourceObjectId, final String name,\n            final String description, final String version) {\n        super(documentId, processInstanceId, name);\n        setDescription(description);\n        setVersion(version);\n        this.archiveDate = archiveDate;\n        this.sourceObjectId = sourceObjectId;\n    }\n\n    @Override\n    public Class<? extends PersistentObject> getPersistentObjectInterface() {\n        return SDocumentMapping.class;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/model/archive/SAMappedDocument.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.document.model.archive;\n\nimport javax.persistence.Cacheable;\nimport javax.persistence.Entity;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\nimport org.bonitasoft.engine.core.document.model.AbstractSMappedDocument;\nimport org.bonitasoft.engine.core.document.model.SMappedDocument;\nimport org.bonitasoft.engine.persistence.ArchivedPersistentObject;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Baptiste Mesta\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@SuperBuilder\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@Table(name = \"arch_document_mapping\")\n@Cacheable(false)\npublic class SAMappedDocument extends AbstractSMappedDocument implements ArchivedPersistentObject {\n\n    private long archiveDate;\n    private long sourceObjectId;\n\n    @Override\n    public Class<? extends PersistentObject> getPersistentObjectInterface() {\n        return SMappedDocument.class;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/model/builder/SDocumentBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.document.model.builder;\n\nimport org.bonitasoft.engine.core.document.model.SDocument;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SDocumentBuilder {\n\n    private final SDocument entity;\n\n    public SDocumentBuilder() {\n        entity = new SDocument();\n    }\n\n    public SDocumentBuilder setAuthor(final long author) {\n        entity.setAuthor(author);\n        return this;\n    }\n\n    public SDocumentBuilder setCreationDate(final long creationDate) {\n        entity.setCreationDate(creationDate);\n        return this;\n    }\n\n    public SDocumentBuilder setHasContent(final boolean hasContent) {\n        entity.setHasContent(hasContent);\n        return this;\n    }\n\n    public SDocumentBuilder setFileName(final String fileName) {\n        entity.setFileName(fileName);\n        return this;\n    }\n\n    public SDocumentBuilder setMimeType(final String mimeType) {\n        entity.setMimeType(mimeType);\n        return this;\n    }\n\n    public SDocumentBuilder setContent(final byte[] content) {\n        entity.setContent(content);\n        return this;\n    }\n\n    public SDocumentBuilder setURL(final String url) {\n        entity.setUrl(url);\n        return this;\n    }\n\n    public SDocument done() {\n        return entity;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/model/builder/SDocumentBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.document.model.builder;\n\n/**\n * @author Nicolas Chabanoles\n * @author Zhang Bole\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class SDocumentBuilderFactory {\n\n    public SDocumentBuilder createNewInstance(final String fileName, final String mimetype, final long authorId) {\n        final SDocumentBuilder sDocumentBuilder = new SDocumentBuilder();\n        sDocumentBuilder.setFileName(fileName);\n        sDocumentBuilder.setMimeType(mimetype);\n        sDocumentBuilder.setAuthor(authorId);\n        sDocumentBuilder.setCreationDate(System.currentTimeMillis());\n        return sDocumentBuilder;\n    }\n\n    public SDocumentBuilder createNewProcessDocument(final String fileName, final String mimetype, final long authorId,\n            final byte[] content) {\n        if (fileName == null || fileName.isEmpty()) {\n            throw new IllegalArgumentException(\"The fileName must be filled for a document with content\");\n        }\n\n        final SDocumentBuilder sDocumentBuilder = createNewInstance(fileName, mimetype, authorId);\n        sDocumentBuilder.setContent(content);\n        sDocumentBuilder.setHasContent(true);\n        return sDocumentBuilder;\n    }\n\n    public SDocumentBuilder createNewExternalProcessDocumentReference(final String fileName, final String mimetype,\n            final long authorId, final String url) {\n        final SDocumentBuilder sDocumentBuilder = createNewInstance(fileName, mimetype, authorId);\n        sDocumentBuilder.setURL(url);\n        sDocumentBuilder.setHasContent(false);\n        return sDocumentBuilder;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/model/builder/SDocumentMappingBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.document.model.builder;\n\nimport org.bonitasoft.engine.core.document.model.SDocumentMapping;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface SDocumentMappingBuilder {\n\n    SDocumentBuilder setProcessInstanceId(final long processInstanceId);\n\n    SDocumentMapping done();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/model/builder/SDocumentMappingBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.document.model.builder;\n\n/**\n * @author Zhao Na\n * @author Baptiste Mesta\n */\npublic interface SDocumentMappingBuilderFactory {\n\n    SDocumentMappingBuilder createNewInstance();\n\n    String getIdKey();\n\n    String getDocumentIdKey();\n\n    String getProcessInstanceIdKey();\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/model/builder/SDocumentUpdateBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.document.model.builder;\n\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Nicolas Chabanoles\n */\npublic interface SDocumentUpdateBuilder {\n\n    SDocumentUpdateBuilder setDocumentName(String documentName);\n\n    SDocumentUpdateBuilder setDocumentAuthor(long author);\n\n    SDocumentUpdateBuilder setDocumentCreationDate(long creationDate);\n\n    SDocumentUpdateBuilder setHasContent(boolean hasContent);\n\n    SDocumentUpdateBuilder setDocumentContentFileName(String contentFileName);\n\n    SDocumentUpdateBuilder setDocumentContentMimeType(String contentMimeType);\n\n    SDocumentUpdateBuilder setDocumentStorageId(final String documentId);\n\n    SDocumentUpdateBuilder setDocumentURL(String generateURL);\n\n    EntityUpdateDescriptor done();\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/model/builder/SDocumentUpdateBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.document.model.builder;\n\n/**\n * @author Nicolas Chabanoles\n * @author Baptiste Mesta\n */\npublic interface SDocumentUpdateBuilderFactory {\n\n    SDocumentUpdateBuilder createNewInstance();\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/model/recorder/SelectDescriptorBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.document.model.recorder;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.core.document.model.SDocument;\nimport org.bonitasoft.engine.core.document.model.SMappedDocument;\nimport org.bonitasoft.engine.core.document.model.archive.SAMappedDocument;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\n\n/**\n * @author Emmanuel Duchastenier\n * @author Nicolas Chabanoles\n * @author Baptiste Mesta\n */\npublic class SelectDescriptorBuilder {\n\n    public static SelectListDescriptor<SMappedDocument> getDocumentMappingsForProcessInstance(\n            final long processInstanceId, final int fromIndex,\n            final int maxResults, final String sortFieldOrder, final OrderByType orderBy) {\n        QueryOptions queryOptions;\n        String queryName = \"getSMappedDocumentOfProcess\";\n        if (sortFieldOrder == null) {\n            queryOptions = new QueryOptions(fromIndex, maxResults);\n            queryName = \"getSMappedDocumentOfProcessOrderedById\";\n        } else {\n            queryOptions = new QueryOptions(fromIndex, maxResults, SMappedDocument.class, sortFieldOrder, orderBy);\n        }\n        final Map<String, Object> parameters = new HashMap<>(1);\n        parameters.put(\"processInstanceId\", processInstanceId);\n        return new SelectListDescriptor<>(queryName, parameters, SMappedDocument.class, queryOptions);\n    }\n\n    public static SelectOneDescriptor<SMappedDocument> getSMappedDocumentOfProcessWithName(final long processInstanceId,\n            final String documentName) {\n        final Map<String, Object> parameters = new HashMap<>(2);\n        parameters.put(\"processInstanceId\", processInstanceId);\n        parameters.put(\"name\", documentName);\n        return new SelectOneDescriptor<>(\"getSMappedDocumentOfProcessWithName\", parameters, SMappedDocument.class);\n    }\n\n    public static SelectOneDescriptor<Long> getNumberOfSMappedDocumentOfProcess(final long processInstanceId) {\n        final Map<String, Object> parameters = new HashMap<>(1);\n        parameters.put(\"processInstanceId\", processInstanceId);\n        return new SelectOneDescriptor<>(\"getNumberOfSMappedDocumentOfProcess\", parameters, SDocument.class);\n    }\n\n    public static SelectListDescriptor<SAMappedDocument> getSAMappedDocumentOfProcessWithName(\n            final long processInstanceId, final String documentName,\n            final long time) {\n        final Map<String, Object> parameters = new HashMap<>(3);\n        parameters.put(\"processInstanceId\", processInstanceId);\n        parameters.put(\"name\", documentName);\n        parameters.put(\"time\", time);\n        return new SelectListDescriptor<>(\"getSAMappedDocumentOfProcessWithName\", parameters, SAMappedDocument.class,\n                new QueryOptions(0, 1));\n    }\n\n    public static SelectByIdDescriptor<SAMappedDocument> getArchivedDocumentById(final long documentId) {\n        return new SelectByIdDescriptor<>(SAMappedDocument.class, documentId);\n    }\n\n    public static SelectOneDescriptor<SAMappedDocument> getArchivedVersionOfDocument(final long documentId) {\n        final Map<String, Object> parameters = new HashMap<>(1);\n        parameters.put(\"sourceObjectId\", documentId);\n        return new SelectOneDescriptor<>(\"getArchivedVersionOfDocument\", parameters, SAMappedDocument.class);\n    }\n\n    public static SelectListDescriptor<SMappedDocument> getDocumentList(String name, long processInstanceId,\n            QueryOptions queryOptions) {\n        final Map<String, Object> parameters = new HashMap<>(3);\n        parameters.put(\"processInstanceId\", processInstanceId);\n        parameters.put(\"name\", name);\n        return new SelectListDescriptor<>(\"getDocumentList\", parameters, SMappedDocument.class,\n                queryOptions);\n    }\n\n    public static SelectListDescriptor<SAMappedDocument> getArchivedDocumentList(String name, long processInstanceId,\n            QueryOptions queryOptions, long time) {\n        final Map<String, Object> parameters = new HashMap<>(3);\n        parameters.put(\"processInstanceId\", processInstanceId);\n        parameters.put(\"name\", name);\n        parameters.put(\"time\", time);\n        return new SelectListDescriptor<>(\"getArchivedDocumentList\", parameters, SAMappedDocument.class,\n                queryOptions);\n    }\n\n    public static SelectListDescriptor<SMappedDocument> getDocumentListCreatedBefore(String name,\n            long processInstanceId, QueryOptions queryOptions, long time) {\n        final Map<String, Object> parameters = new HashMap<>(3);\n        parameters.put(\"processInstanceId\", processInstanceId);\n        parameters.put(\"name\", name);\n        parameters.put(\"time\", time);\n        return new SelectListDescriptor<>(\"getDocumentCreatedBeforeList\", parameters, SMappedDocument.class,\n                queryOptions);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/ActivityInstanceService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityCreationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityModificationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeModificationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.business.SHumanTaskAlreadyAssignedException;\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @author Hongwen Zang\n * @author Yanyan Liu\n * @author Baptiste Mesta\n * @author Celine Souchet\n * @since 6.0\n */\npublic interface ActivityInstanceService extends FlowNodeInstanceService {\n\n    String ACTIVITYINSTANCE = \"ACTIVITYINSTANCE\";\n\n    String PENDINGACTIVITYMAPPING = \"PENDINGACTIVITYMAPPING\";\n\n    /**\n     * Create activityInstance in DB according to the given activityInstance object\n     *\n     * @param activityInstance\n     *        an SActivityInstance object\n     * @throws SActivityCreationException\n     */\n    void createActivityInstance(SActivityInstance activityInstance) throws SActivityCreationException;\n\n    /**\n     * Create a new pending activity mapping in DB\n     *\n     * @param mapping\n     *        pending activity mapping object\n     * @throws SActivityCreationException\n     */\n    void addPendingActivityMappings(SPendingActivityMapping mapping) throws SActivityCreationException;\n\n    /**\n     * deletePendingMappings\n     *\n     * @param mapping\n     *        pending activity mapping object\n     * @throws SActivityModificationException\n     */\n    void deletePendingMappings(long humanTaskInstanceId) throws SActivityModificationException;\n\n    /**\n     * Delete all pending mappings for the connected tenant\n     *\n     * @throws SActivityModificationException\n     * @since 6.1\n     */\n    void deleteAllPendingMappings() throws SActivityModificationException;\n\n    /**\n     * Get activityInstance by its id\n     *\n     * @param activityInstanceId\n     *        identifier of activityInstance\n     * @return an SActivityInstance object with id corresponding to the parameter\n     * @throws SActivityInstanceNotFoundException\n     *         if no activityInstance found\n     * @throws SActivityReadException\n     */\n    SActivityInstance getActivityInstance(long activityInstanceId)\n            throws SActivityInstanceNotFoundException, SActivityReadException;\n\n    /**\n     * Get humanTaskInstance by its id\n     *\n     * @param activityInstanceId\n     *        identifier of humanTaskInstance\n     * @return an SHumanTaskInstance object with id corresponding to the parameter\n     * @throws SActivityInstanceNotFoundException\n     * @throws SActivityReadException\n     */\n    SHumanTaskInstance getHumanTaskInstance(long activityInstanceId)\n            throws SActivityInstanceNotFoundException, SActivityReadException;\n\n    /**\n     * Get activities with specific states in the root container in specific order, this is used for pagination\n     *\n     * @param rootContainerId\n     *        identifier of root container, it always is process definition id\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param maxResults\n     *        Number of result we want to get. Maximum number of result returned\n     * @param sortingField\n     *        the field used to do order\n     * @param sortingOrder\n     *        ASC or DESC\n     * @param stateIds\n     *        Identifiers of states\n     * @return a list of SActivityInstance objects\n     * @throws SActivityReadException\n     */\n    List<SActivityInstance> getActivitiesWithStates(long rootContainerId, Set<Integer> stateIds, int fromIndex,\n            int maxResults, String sortingField,\n            OrderByType sortingOrder) throws SActivityReadException;\n\n    /**\n     * Get the most recent archived version of a specified activity instance\n     *\n     * @param activityInstanceId\n     *        identifier of activity instance\n     * @return an SAActivityInstance object\n     * @throws SActivityReadException\n     *         if a Read error occurs\n     * @throws SActivityInstanceNotFoundException\n     *         it the provided activityInstanceId does not refer to an existing Activity Instance\n     */\n    SAActivityInstance getMostRecentArchivedActivityInstance(long activityInstanceId)\n            throws SActivityReadException, SActivityInstanceNotFoundException;\n\n    /**\n     * Get pending tasks for the user in specific actors. This is used for pagination\n     *\n     * @param userId\n     *        identifier of user\n     * @param actorIds\n     *        identifiers of actor\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param maxResults\n     *        Number of result we want to get. Maximum number of result returned\n     * @param sortFieldName\n     *        the field used to do order\n     * @param order\n     *        ASC or DESC\n     * @return a list of SActivityInstance objects\n     * @throws SActivityReadException\n     */\n    List<SHumanTaskInstance> getPendingTasks(long userId, Set<Long> actorIds, int fromIndex, int maxResults,\n            String sortFieldName, OrderByType order)\n            throws SActivityReadException;\n\n    /**\n     * Get tasks assigned to the user. This is used for pagination\n     *\n     * @param userId\n     *        identifier of user\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param maxResults\n     *        Number of result we want to get. Maximum number of result returned\n     * @param sortFieldName\n     *        the field used to do order\n     * @param order\n     *        ASC or DESC\n     * @return a list of SHumanTaskInstance objects\n     * @throws SActivityReadException\n     */\n    List<SHumanTaskInstance> getAssignedUserTasks(long userId, int fromIndex, int maxResults, String sortFieldName,\n            OrderByType order)\n            throws SActivityReadException;\n\n    /**\n     * Get archived activity instances in the specific root container.\n     *\n     * @param rootContainerId\n     *        identifier of root container, the root container can be process instance\n     * @param queryOptions\n     *        a map of specific parameters of a query\n     * @return a list of SAActivityInstance objects\n     * @throws SActivityReadException\n     */\n    List<SAActivityInstance> getArchivedActivityInstances(long rootContainerId, QueryOptions queryOptions)\n            throws SActivityReadException;\n\n    /**\n     * Get total number of open activity instances for the specific process instance\n     *\n     * @param processInstanceId\n     *        identifier of process instance\n     * @return the number of opened activity instances in the specific process instance\n     * @throws SActivityReadException\n     */\n    int getNumberOfOpenActivityInstances(long processInstanceId) throws SActivityReadException;\n\n    /**\n     * Get all open activity instances in the specific process instance. This is used for pagination\n     *\n     * @param rootContainerId\n     *        identifier of root container, the root container can be process instance\n     * @param pageIndex\n     *        the page index to indicate which page will be retrieved. First page has index 0\n     * @param maxResults\n     *        Number of result we want to get. Maximum number of result returned\n     * @param sortingField\n     *        the field used to do order\n     * @param orderbyType\n     *        ASC or DESC\n     * @return a list of SActivityInstance objects\n     * @throws SActivityReadException\n     */\n    List<SActivityInstance> getOpenActivityInstances(long rootContainerId, int pageIndex, int maxResults,\n            String sortingField, OrderByType orderbyType)\n            throws SActivityReadException;\n\n    /**\n     * Get all activity instances for the specific process instance\n     *\n     * @param rootContainerId\n     *        identifier of root container, the root container can be process instance\n     * @return a list of SActivityInstance objects\n     * @throws SActivityReadException\n     */\n    List<SActivityInstance> getActivityInstances(long rootContainerId, int fromIndex, int numberOfResults)\n            throws SActivityReadException;\n\n    /**\n     * Get all child instances for the specific parent activity instance, order by id ascending.\n     *\n     * @param parentActivityInstanceId\n     *        identifier of parent activity instance\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param numberOfResults\n     *        TODO\n     * @return a list of SActivityInstance objects\n     * @throws SActivityReadException\n     */\n    List<SActivityInstance> getChildrenOfAnActivity(long parentActivityInstanceId, int fromIndex, int numberOfResults)\n            throws SActivityReadException;\n\n    /**\n     * Assign the specific human task to the user\n     *\n     * @param userTaskId\n     *        identifier of human task instance\n     * @param userId\n     *        identifier of user\n     * @throws SFlowNodeNotFoundException\n     * @throws SFlowNodeReadException\n     * @throws SActivityModificationException\n     */\n    void assignHumanTask(long userTaskId, long userId)\n            throws SFlowNodeNotFoundException, SFlowNodeReadException, SActivityModificationException;\n\n    /**\n     * Assign the specific human task to the user if it is not currently assigned\n     * <p>Use a more restrictive mechanism than assignHumanTask method to assign\n     * human task:\n     * <ul>\n     * <li>exception when task is already assign to a different user</li>\n     * <li>only update claimed date when assign to same user</li>\n     * <li>remove claimed date when assign to user with id 0 (un-assign)</li>\n     * </ul>\n     * </p>\n     * <p>\n     * <p>under high load, getting pending tasks could return tasks that are being assign in a previous transaction in a\n     * separate thread, and thus assignee is override </p>\n     *\n     * @since 7.6\n     * @param userTaskId\n     *        identifier of human task instance\n     * @param userId\n     *        identifier of user\n     * @throws SFlowNodeNotFoundException\n     * @throws SFlowNodeReadException\n     * @throws SActivityModificationException\n     * @throws SHumanTaskAlreadyAssignedException\n     */\n    void assignHumanTaskIfNotAssigned(long userTaskId, long userId) throws SFlowNodeNotFoundException,\n            SFlowNodeReadException, SActivityModificationException, SHumanTaskAlreadyAssignedException;\n\n    /**\n     * Get the number of UserTask instances assigned to a specific user\n     *\n     * @param userId\n     *        the id of the user concerned\n     * @return the number of UserTask instances assigned to this specific user\n     * @throws SActivityReadException\n     *         if a Read exception occurs\n     */\n    long getNumberOfAssignedHumanTaskInstances(long userId) throws SActivityReadException;\n\n    /**\n     * Search UserTask instances assigned for a specific supervisor\n     *\n     * @param parameters\n     *        a map of specific parameters of a query\n     * @param parameters\n     *        a map of specific parameters of a query\n     * @return the number of UserTask assigned to this specific supervisor\n     * @throws SActivityReadException\n     *         if a Read exception occurs\n     */\n    long getNumberOfAssignedTasksSupervisedBy(final long supervisorId, final QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Search AUserTask instances archived for a specific supervisor\n     *\n     * @param queryOptions\n     *        the object used to manage all the search parameters of a query\n     * @param parameters\n     *        a map of specific parameters of a query\n     * @return the number of UserTask archived to this specific supervisor\n     * @throws SActivityReadException\n     *         if a Read exception occurs\n     */\n    long getNumberOfArchivedHumanTasksSupervisedBy(final long supervisorId, final QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Search UserTask instances assigned for a specific supervisor\n     *\n     * @param queryOptions\n     *        the object used to manage all the search parameters of a query\n     * @param parameters\n     *        a map of specific parameters of a query\n     * @return the UserTask instances list assigned to this specific supervisor\n     * @throws SActivityReadException\n     *         if a Read exception occurs\n     */\n    List<SHumanTaskInstance> searchAssignedTasksSupervisedBy(final long supervisorId, final QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Search AUserTask instances archived for a specific supervisor\n     *\n     * @param queryOptions\n     *        the object used to manage all the search parameters of a query\n     * @param parameters\n     *        a map of specific parameters of a query\n     * @return the UserTask instances list archived to this specific supervisor\n     * @throws SActivityReadException\n     *         if a Read exception occurs\n     */\n    List<SAHumanTaskInstance> searchArchivedHumanTasksSupervisedBy(final long supervisorId,\n            final QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * Gets the archive instance of the activity according to its identifier at a given state.\n     *\n     * @param activityId\n     *        the activity identifier\n     * @param stateId\n     *        the state identifier\n     * @param persistenceService\n     * @return\n     * @throws SActivityReadException\n     *         if a Read exception occurs\n     * @throws SActivityInstanceNotFoundException\n     */\n\n    SAActivityInstance getArchivedActivityInstance(long activityInstanceId, int stateId)\n            throws SActivityReadException, SActivityInstanceNotFoundException;\n\n    /**\n     * Search archived human tasks according to specific search criteria\n     *\n     * @param searchOptions\n     *        the object used to manage all the search parameters of a query\n     * @param persistenceService\n     *        used to retrieve the archived tasks\n     * @return a list of SAHumanTaskInstance objects\n     * @throws SBonitaReadException\n     */\n    List<SAHumanTaskInstance> searchArchivedTasks(QueryOptions searchOptions) throws SBonitaReadException;\n\n    /**\n     * Get total number of archived tasks according to specific search criteria\n     *\n     * @param searchOptions\n     *        the object used to manage all the search parameters of a query\n     * @param persistenceService\n     *        used to retrieve the archived tasks\n     * @return\n     * @throws SBonitaReadException\n     */\n    long getNumberOfArchivedTasks(QueryOptions searchOptions) throws SBonitaReadException;\n\n    /**\n     * Get total number of assigned tasks managed by the specific manager\n     *\n     * @param managerUserId\n     *        identifier of manager user\n     * @param searchOptions\n     *        the object used to manage all the search parameters of a query\n     * @return number of assigned tasks managed by the specific manager\n     * @throws SBonitaReadException\n     */\n    long getNumberOfAssignedTasksManagedBy(long managerUserId, QueryOptions searchOptions) throws SBonitaReadException;\n\n    /**\n     * Get all assigned tasks managed by the specific manager\n     *\n     * @param managerUserId\n     *        identifier of manager user\n     * @param searchOptions\n     *        the object used to manage all the search parameters of a query\n     * @return a list of SHumanTaskInstance objects\n     */\n    List<SHumanTaskInstance> searchAssignedTasksManagedBy(long managerUserId, QueryOptions searchOptions)\n            throws SBonitaReadException;\n\n    /**\n     * get the total number of archived tasks assigned to subordinates of specified manager.\n     *\n     * @param managerUserId\n     *        the userId of the manager\n     * @param searchOptions\n     *        the search options to paginate, filter, ...\n     * @return the number of elements encountered\n     * @throws SBonitaReadException\n     *         in case a search error occurs\n     */\n    long getNumberOfArchivedTasksManagedBy(long managerUserId, QueryOptions searchOptions) throws SBonitaReadException;\n\n    /**\n     * get the archived tasks assigned to subordinates of specified manager, limited to, sorted, paginated with the\n     * specifies QueryOptions\n     *\n     * @param managerUserId\n     *        the userId of the manager\n     * @param searchOptions\n     *        the search options to paginate, filter, sort ...\n     * @return the elements encountered matching the specified options\n     * @throws SBonitaReadException\n     *         in case a search error occurs\n     */\n    List<SAHumanTaskInstance> searchArchivedTasksManagedBy(long managerUserId, QueryOptions searchOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Search all pending human task instances for the specific supervisor\n     *\n     * @param userId\n     *        identifier of supervisor user\n     * @param searchOptions\n     *        the search options to paginate, filter, sort ...\n     * @return a list of SHumanTaskInstance objects\n     * @throws SBonitaReadException\n     */\n    List<SHumanTaskInstance> searchPendingTasksSupervisedBy(long userId, QueryOptions searchOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Get total number of pending human task instances for the specific supervisor\n     *\n     * @param userId\n     *        identifier of supervisor user\n     * @param queryOptions\n     *        the search options to paginate, filter, sort ...\n     * @return number of pending human task instances for the specific supervisor\n     * @throws SBonitaReadException\n     */\n    long getNumberOfPendingTasksSupervisedBy(long userId, QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * Get number of human task instances according to the criteria\n     *\n     * @param queryOptions\n     *        the search options to paginate, filter, sort ...\n     * @return number of human task instances satisfied to the criteria\n     * @throws SBonitaReadException\n     */\n    long getNumberOfHumanTasks(QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * Search all human task instances according to the criteria\n     *\n     * @param queryOptions\n     *        the search options to paginate, filter, sort ...\n     * @return a list of SHumanTaskInstance objects\n     * @throws SBonitaReadException\n     */\n    List<SHumanTaskInstance> searchHumanTasks(QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * Get number of open tasks for each user\n     *\n     * @param userIds\n     *        identifiers of users\n     * @return a map containing user id and corresponding task number\n     * @throws SBonitaReadException\n     */\n    Map<Long, Long> getNumberOfOpenTasksForUsers(List<Long> userIds) throws SBonitaReadException;\n\n    /**\n     * Search total number of pending tasks for the specific manager\n     *\n     * @param managerUserId\n     *        identifier of manager user\n     * @param searchOptions\n     *        the search options to paginate, filter, sort ...\n     * @return number of pending tasks\n     * @throws SBonitaReadException\n     */\n    long searchNumberOfPendingTasksManagedBy(long managerUserId, QueryOptions searchOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Search all pending tasks for the specific manager\n     *\n     * @param managerUserId\n     *        identifier of manager user\n     * @param searchOptions\n     *        the search options to paginate, filter, sort ...\n     * @return a list of SHumanTaskInstance objects\n     * @throws SBonitaReadException\n     */\n    List<SHumanTaskInstance> searchPendingTasksManagedBy(long managerUserId, QueryOptions searchOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Increase loopCounter(loopCount+1) for the specific loop instance\n     *\n     * @param loopInstance\n     *        the loopCounter in which will be increased\n     * @throws SActivityModificationException\n     */\n    void incrementLoopCounter(final SLoopActivityInstance loopInstance) throws SActivityModificationException;\n\n    /**\n     * Get number of overdue open tasks for each user\n     *\n     * @param userIds\n     *        identifiers of users\n     * @return a map containing userId and corresponding number of tasks\n     * @throws SBonitaReadException\n     */\n    Map<Long, Long> getNumberOfOverdueOpenTasksForUsers(List<Long> userIds) throws SBonitaReadException;\n\n    /**\n     * Set max loop for the specific loopActvity\n     *\n     * @param loopActivity\n     *        the loopActivity\n     * @param result\n     *        value for max loop\n     * @throws SActivityModificationException\n     */\n    void setLoopMax(SLoopActivityInstance loopActivity, Integer result) throws SActivityModificationException;\n\n    /**\n     * Set LoopCardinality for the specific loopActvity\n     *\n     * @param flowNodeInstance\n     *        the loopActvity\n     * @param intLoopCardinality\n     *        value of loop cardinality\n     * @throws SActivityModificationException\n     */\n    void setLoopCardinality(SFlowNodeInstance flowNodeInstance, int intLoopCardinality)\n            throws SActivityModificationException;\n\n    /**\n     * Add number of activeInstances for the specific SMultiInstanceActivityInstance object\n     *\n     * @param flowNodeInstance\n     *        an SMultiInstanceActivityInstance object\n     * @param number\n     *        the number will be added\n     * @throws SActivityModificationException\n     */\n    void addMultiInstanceNumberOfActiveActivities(SMultiInstanceActivityInstance flowNodeInstance, int number)\n            throws SActivityModificationException;\n\n    /**\n     * Add number of terminated activeInstances for the specific SMultiInstanceActivityInstance object\n     *\n     * @param flowNodeInstance\n     *        an SMultiInstanceActivityInstance object\n     * @param number\n     *        will be added to terminated instances of flowNodeInstance\n     *        the number will be added\n     * @throws SActivityModificationException\n     */\n    void addMultiInstanceNumberOfTerminatedActivities(SMultiInstanceActivityInstance flowNodeInstance, int number)\n            throws SActivityModificationException;\n\n    /**\n     * Add number of completed activeInstances for the specific SMultiInstanceActivityInstance object\n     *\n     * @param flowNodeInstance\n     *        an SMultiInstanceActivityInstance object whose completed activity number will be updated\n     * @param number\n     *        the number will be added\n     * @throws SActivityModificationException\n     */\n    void addMultiInstanceNumberOfCompletedActivities(SMultiInstanceActivityInstance flowNodeInstance, int number)\n            throws SActivityModificationException;\n\n    /**\n     * Get total number of activity instances for the specific entity class\n     *\n     * @param entityClass\n     *        to indicate which type of class will be retrieved\n     * @param searchOptions\n     *        the search options to paginate, filter, sort ...\n     * @return number of activity instances for the specific entity class\n     * @throws SBonitaReadException\n     */\n    long getNumberOfActivityInstances(Class<? extends PersistentObject> entityClass, QueryOptions searchOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Search all activity instances for the specific entity class\n     *\n     * @param entityClass\n     *        to indicate which type of class will be retrieved\n     * @param searchOptions\n     *        the search options to paginate, filter, sort ...\n     * @return a list of SActivityInstance objects\n     * @throws SBonitaReadException\n     */\n    List<SActivityInstance> searchActivityInstances(Class<? extends PersistentObject> entityClass,\n            QueryOptions searchOptions) throws SBonitaReadException;\n\n    /**\n     * Get total number of archived activity instances for the specific entity class\n     *\n     * @param entityClass\n     *        to indicate which type of class will be retrieved\n     * @param searchOptions\n     *        the search options to paginate, filter, sort ...\n     * @return number of archived activity instances for the specific entity class\n     * @throws SBonitaReadException\n     */\n    long getNumberOfArchivedActivityInstances(Class<? extends PersistentObject> entityClass,\n            QueryOptions searchOptions)\n            throws SBonitaReadException;\n\n    /***\n     * Search all archived activity instances for the specific entity class\n     *\n     * @param entityClass\n     *        to indicate which type of class will be retrieved\n     * @param searchOptions\n     *        the search options to paginate, filter, sort ...\n     * @return a list of SAActivityInstance objects\n     * @throws SBonitaReadException\n     */\n    List<SAActivityInstance> searchArchivedActivityInstances(Class<? extends PersistentObject> entityClass,\n            QueryOptions searchOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Set tokenCount for the specific activity instance\n     *\n     * @param activityInstance\n     *        the activityInstance will be updated\n     * @param tokenCount\n     *        value of tokenCount will be set to the activity\n     * @throws SFlowNodeModificationException\n     */\n    void setTokenCount(final SActivityInstance activityInstance, int tokenCount) throws SFlowNodeModificationException;\n\n    /**\n     * @param userId\n     * @param searchOptions\n     * @return\n     * @since 6.0\n     */\n    long getNumberOfPendingTasksForUser(long userId, QueryOptions searchOptions) throws SBonitaReadException;\n\n    /**\n     * @param userId\n     * @param searchOptions\n     * @return\n     * @since 6.0\n     */\n    List<SHumanTaskInstance> searchPendingTasksForUser(long userId, QueryOptions searchOptions)\n            throws SBonitaReadException;\n\n    /**\n     * @since 7.5.5\n     */\n    long getNumberOfPendingTasksAssignedTo(long userId, QueryOptions searchOptions) throws SBonitaReadException;\n\n    /**\n     * @since 7.5.5\n     */\n    List<SHumanTaskInstance> searchPendingTasksAssignedTo(long userId, QueryOptions searchOptions)\n            throws SBonitaReadException;\n\n    /**\n     * @param humanTaskInstanceId\n     * @param queryOptions\n     * @return\n     * @throws SBonitaReadException\n     */\n    List<SPendingActivityMapping> getPendingMappings(long humanTaskInstanceId, QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    /**\n     * @param userId\n     * @param searchOptions\n     * @return\n     * @throws SBonitaReadException\n     * @since 6.0\n     */\n    List<SHumanTaskInstance> searchPendingOrAssignedTasks(long userId, QueryOptions searchOptions)\n            throws SBonitaReadException;\n\n    /**\n     * @param userId\n     * @param searchOptions\n     * @return\n     * @throws SBonitaReadException\n     * @since 6.0\n     */\n    long getNumberOfPendingOrAssignedTasks(long userId, QueryOptions searchOptions) throws SBonitaReadException;\n\n    /**\n     * @param userId\n     * @param searchOptions\n     * @return\n     * @throws SBonitaReadException\n     * @since 7.15\n     */\n    List<SHumanTaskInstance> searchPendingOrAssignedOrAssignedToOthersTasks(long userId, QueryOptions searchOptions)\n            throws SBonitaReadException;\n\n    /**\n     * @param userId\n     * @param searchOptions\n     * @return\n     * @throws SBonitaReadException\n     * @since 7.15\n     */\n    long getNumberOfPendingOrAssignedOrAssignedToOthersTasks(long userId, QueryOptions searchOptions)\n            throws SBonitaReadException;\n\n    /**\n     * @param activityInstance\n     * @param boundaryEventId\n     * @throws SActivityModificationException\n     * @since 6.0\n     */\n    void setAbortedByBoundaryEvent(SActivityInstance activityInstance, long boundaryEventId)\n            throws SActivityModificationException;\n\n    List<Long> getPossibleUserIdsOfPendingTasks(long humanTaskInstanceId, int startIndex, int maxResults)\n            throws SActivityReadException;\n\n    boolean isTaskPendingForUser(long humanTaskInstanceId, long userId) throws SBonitaReadException;\n\n    /**\n     * Get total number of users according to specific query options, and who can start the task filtered with the\n     * search option\n     * of the given process definition\n     *\n     * @param searchOptions\n     *        The QueryOptions object containing some query conditions\n     * @return\n     */\n    long getNumberOfUsersWhoCanExecutePendingHumanTaskDeploymentInfo(long humanTaskInstanceId,\n            QueryOptions searchOptions) throws SBonitaReadException;\n\n    /**\n     * Search the users according to specific query options, and who can start the task filtered with the search option\n     * of the given process definition\n     *\n     * @param searchOptions\n     *        The QueryOptions object containing some query conditions\n     * @return\n     */\n    List<SUser> searchUsersWhoCanExecutePendingHumanTaskDeploymentInfo(long humanTaskInstanceId,\n            QueryOptions searchOptions) throws SBonitaReadException;\n\n    /**\n     * Get the total number of the assigned and pending human tasks for the specified user, on the specified root\n     * process definition, corresponding to the\n     * options.\n     *\n     * @param rootProcessDefinitionId\n     *        The identifier of the root process definition\n     * @param userId\n     *        The identifier of the user\n     * @param queryOptions\n     *        The search conditions and the options for sorting and paging the results.\n     * @return The assigned and pending human tasks\n     * @since 6.3.3\n     */\n    long getNumberOfAssignedAndPendingHumanTasksFor(long rootProcessDefinitionId, long userId,\n            QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * Search the assigned and pending human tasks for the specified user, on the specified root process definition,\n     * corresponding to the options.\n     *\n     * @param rootProcessDefinitionId\n     *        The identifier of the root process definition\n     * @param userId\n     *        The identifier of the user\n     * @param queryOptions\n     *        The search conditions and the options for sorting and paging the results.\n     * @return The assigned and pending human tasks\n     * @since 6.3.3\n     */\n    List<SHumanTaskInstance> searchAssignedAndPendingHumanTasksFor(long rootProcessDefinitionId, long userId,\n            QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Get the total number of the assigned and pending human tasks for any user, on the specified root process\n     * definition, corresponding to the\n     * options.\n     *\n     * @param rootProcessDefinitionId\n     *        The identifier of the root process definition\n     * @param queryOptions\n     *        The search conditions and the options for sorting and paging the results.\n     * @return The assigned and pending human tasks\n     * @since 6.3.3\n     */\n    long getNumberOfAssignedAndPendingHumanTasks(long rootProcessDefinitionId, QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Search the assigned and pending human tasks for any user, on the specified root process definition, corresponding\n     * to the options.\n     *\n     * @param rootProcessDefinitionId\n     *        The identifier of the root process definition\n     * @param queryOptions\n     *        The search conditions and the options for sorting and paging the results.\n     * @return The assigned and pending human tasks\n     * @since 6.3.3\n     */\n    List<SHumanTaskInstance> searchAssignedAndPendingHumanTasks(long rootProcessDefinitionId, QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Get the total number of the assigned and pending human tasks for any user corresponding to the\n     * options.\n     *\n     * @param queryOptions\n     *        The search conditions and the options for sorting and paging the results.\n     * @return The assigned and pending human tasks\n     * @since 7.6.1\n     */\n    long getNumberOfAssignedAndPendingHumanTasks(QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * Search the assigned and pending human tasks for any user corresponding to the options.\n     *\n     * @param queryOptions\n     *        The search conditions and the options for sorting and paging the results.\n     * @return The assigned and pending human tasks\n     * @since 7.6.1\n     */\n    List<SHumanTaskInstance> searchAssignedAndPendingHumanTasks(QueryOptions queryOptions) throws SBonitaReadException;\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/BPMFailureService.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.instance.model.SABPMFailure;\nimport org.bonitasoft.engine.core.process.instance.model.SBPMFailure;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.services.SPersistenceException;\n\npublic interface BPMFailureService {\n\n    SBPMFailure createFlowNodeFailure(SFlowNodeInstance flowNodeInstance,\n            Failure failure) throws SPersistenceException;\n\n    List<SBPMFailure> getFlowNodeFailures(long flowNodeInstanceId, int maxResults) throws SBonitaReadException;\n\n    void archiveFlowNodeFailures(long flowNodeInstanceId, long archiveDate) throws SBonitaException;\n\n    void deleteFlowNodeFailures(long flowNodeInstanceId) throws SBonitaException;\n\n    void deleteArchivedFlowNodeFailures(List<Long> flowNodeInstanceIds) throws SBonitaException;\n\n    List<SABPMFailure> getArchivedFlowNodeFailures(long flowNodeInstanceId, int maxResults) throws SBonitaReadException;\n\n    SBPMFailure createProcessInstanceFailure(SProcessInstance processInstance, Failure failure)\n            throws SPersistenceException;\n\n    List<SBPMFailure> getProcessInstanceFailures(long processInstanceId, int maxResults) throws SBonitaReadException;\n\n    void archiveProcessInstanceFailures(long processInstanceId, long archiveDate) throws SBonitaException;\n\n    void deleteProcessInstanceFailures(long processInstanceId) throws SBonitaException;\n\n    void deleteArchivedProcessInstanceFailures(List<Long> processInstanceIds) throws SBonitaException;\n\n    List<SABPMFailure> getArchivedProcessInstanceFailures(long processInstanceId, int maxResults)\n            throws SBonitaReadException;\n\n    List<SBPMFailure> getChildProcessInstancesFailures(long rootProcessInstanceId, int maxResults)\n            throws SBonitaReadException;\n\n    List<SABPMFailure> getArchivedChildProcessInstancesFailures(long rootProcessInstanceId, int maxResults)\n            throws SBonitaReadException;\n\n    record Failure(String scope, Throwable throwable) {}\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/FlowNodeInstanceService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api;\n\nimport java.time.Duration;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeDeletionException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeModificationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstanceStateCounter;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.core.process.instance.model.STaskPriority;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic interface FlowNodeInstanceService {\n\n    String FLOWNODE_INSTANCE = \"FLOWNODE_INSTANCE\";\n\n    String ACTIVITYINSTANCE_STATE = \"ACTIVITYINSTANCE_STATE\";\n\n    String ACTIVITY_INSTANCE_TOKEN_COUNT = \"ACTIVITY_INSTANCE_TOKEN_COUNT\";\n\n    String ACTIVITYINSTANCE_DISPLAY_DESCRIPTION = \"ACTIVITYINSTANCE_DISPLAY_DESCRIPTION\";\n\n    String LOOPINSTANCE_LOOPMAX_MODIFIED = \"LOOPINSTANCE_LOOPMAX_MODIFIED\";\n\n    String MULTIINSTANCE_LOOPCARDINALITY_MODIFIED = \"MULTIINSTANCE_LOOPMAX_MODIFIED\";\n\n    String MULTIINSTANCE_NUMBEROFINSTANCE_MODIFIED = \"MULTIINSTANCE_LOOPMAX_MODIFIED\";\n\n    String ACTIVITYINSTANCE_DISPLAY_NAME = \"ACTIVITYINSTANCE_DISPLAY_NAME\";\n\n    String STATE_CATEGORY = \"STATE_CATEGORY\";\n\n    String EXECUTED_BY_MODIFIED = \"EXECUTED_BY_MODIFIED\";\n\n    String EXECUTED_BY_SUBSTITUTE_MODIFIED = \"EXECUTED_BY_SUBSTITUTE_MODIFIED\";\n\n    String EXPECTED_END_DATE_MODIFIED = \"EXPECTED_END_DATE_MODIFIED\";\n\n    /**\n     * @param flowNodeInstanceId\n     * @return\n     * @throws SFlowNodeNotFoundException\n     * @throws SFlowNodeReadException\n     * @since 6.0\n     */\n    SFlowNodeInstance getFlowNodeInstance(long flowNodeInstanceId)\n            throws SFlowNodeNotFoundException, SFlowNodeReadException;\n\n    /**\n     * get the flow node instances directly contained in the given process instance\n     *\n     * @param processInstanceId the parent process instance\n     */\n    List<SFlowNodeInstance> getAllChildrenOfProcessInstance(long processInstanceId, int fromIndex, int maxResults)\n            throws SFlowNodeReadException, SBonitaReadException;\n\n    /**\n     * get the flow node instances directly contained in the given process instance\n     *\n     * @param processInstanceId the parent process instance\n     */\n    List<SFlowNodeInstance> getDirectChildrenOfProcessInstance(long processInstanceId, int fromIndex, int maxResults)\n            throws SFlowNodeReadException, SBonitaReadException;\n\n    /**\n     * get the flow node instances directly contained in the given activity instance\n     *\n     * @param parentActivityInstanceId the parent process instance\n     */\n    List<SFlowNodeInstance> getDirectChildrenOfActivityInstance(long parentActivityInstanceId, int fromIndex,\n            int maxResults)\n            throws SFlowNodeReadException, SBonitaReadException;\n\n    /**\n     * @param flowNodeInstance\n     * @param state\n     * @throws SFlowNodeModificationException\n     * @since 6.0\n     */\n    void setState(SFlowNodeInstance flowNodeInstance, FlowNodeState state) throws SFlowNodeModificationException;\n\n    /**\n     * @param flowNodeInstance\n     * @param priority\n     * @throws SFlowNodeModificationException\n     * @since 6.0\n     */\n    void setTaskPriority(SFlowNodeInstance flowNodeInstance, STaskPriority priority)\n            throws SFlowNodeModificationException;\n\n    /**\n     * @param flowNodeInstance\n     * @param displayDescription\n     * @throws SFlowNodeModificationException\n     * @since 6.0\n     */\n    void updateDisplayDescription(SFlowNodeInstance flowNodeInstance, String displayDescription)\n            throws SFlowNodeModificationException;\n\n    /**\n     * @param flowNodeInstance\n     * @param displayName\n     * @throws SFlowNodeModificationException\n     * @since 6.0\n     */\n    void updateDisplayName(SFlowNodeInstance flowNodeInstance, String displayName)\n            throws SFlowNodeModificationException;\n\n    /**\n     * @param flowElementInstance\n     * @param stateCategory\n     * @throws SFlowNodeModificationException\n     * @since 6.0\n     */\n    void setStateCategory(SFlowNodeInstance flowElementInstance, SStateCategory stateCategory)\n            throws SFlowNodeModificationException;\n\n    /**\n     * @param entityClass\n     * @param countOptions\n     * @return\n     * @throws SBonitaReadException\n     * @since 6.0\n     */\n    long getNumberOfFlowNodeInstances(Class<? extends SFlowNodeInstance> entityClass, QueryOptions countOptions)\n            throws SBonitaReadException;\n\n    /**\n     * @param entityClass\n     * @param countOptions\n     * @return\n     * @throws SBonitaReadException\n     * @since 6.0\n     */\n    long getNumberOfFlowNodeInstancesSupervisedBy(Long supervisorId, Class<? extends SFlowNodeInstance> entityClass,\n            QueryOptions countOptions)\n            throws SBonitaReadException;\n\n    /**\n     * @param entityClass\n     * @param searchOptions\n     * @return\n     * @throws SBonitaReadException\n     * @since 6.0\n     */\n    <T extends SFlowNodeInstance> List<T> searchFlowNodeInstances(Class<T> entityClass, QueryOptions searchOptions)\n            throws SBonitaReadException;\n\n    /**\n     * @param entityClass\n     * @param searchOptions\n     * @return\n     * @throws SBonitaReadException\n     * @since 6.0\n     */\n    <T extends SFlowNodeInstance> List<T> searchFlowNodeInstancesSupervisedBy(Long supervisorId, Class<T> entityClass,\n            QueryOptions searchOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Counts the number of flownode instances in all states. Only considers flownodes direcly contained in given\n     * process definition. Results are counted per flownode name and per state.\n     *\n     * @param processDefinitionId the ID of the process definition to search flownodes for.\n     * @return a list of FlowNodeInstanceStateCounter. If no results, returns an empty list.\n     * @throws SBonitaReadException if a read exception occurs.\n     */\n    List<SFlowNodeInstanceStateCounter> getNumberOfFlownodesOfProcessDefinitionInAllStates(\n            final long processDefinitionId)\n            throws SBonitaReadException;\n\n    /**\n     * Counts the number of flownode instances in all states. Only considers flownodes direcly contained in given\n     * process instance, not flownodes in\n     * sub-process instances. Results are counted per flownode name and per state.\n     *\n     * @param processInstanceId the ID of the process instance to search flownodes for.\n     * @return a list of FlowNodeInstanceStateCounter. If no results, returns an empty list.\n     * @throws SBonitaReadException if a read exception occurs.\n     */\n    List<SFlowNodeInstanceStateCounter> getNumberOfFlownodesInAllStates(final long processInstanceId)\n            throws SBonitaReadException;\n\n    /**\n     * Counts the number of archived flownode instances in a specific state. Only considers archived flownodes direcly\n     * contained in given process instance, not\n     * flownodes in sub-process instances. Results are counted per flownode name and per state.\n     *\n     * @param processInstanceId the ID of the process instance to search flownodes for. This is the ID of the process\n     *        instance before it was archived\n     *        (corresponding to the sourceObjectId in the archives)\n     * @return a list of FlowNodeInstanceStateCounter. If no results, returns an empty list.\n     * @throws SBonitaReadException if a read exception occurs.\n     */\n    public List<SFlowNodeInstanceStateCounter> getNumberOfArchivedFlownodesInAllStates(final long processInstanceId)\n            throws SBonitaReadException;\n\n    /**\n     * Set execute by for the specific flow node instance\n     *\n     * @param sFlowNodeInstance\n     *        the flowNodeInstance will be updated\n     * @param userId\n     *        value for executedBy\n     * @throws SFlowNodeModificationException\n     * @since 6.0\n     */\n    void setExecutedBy(SFlowNodeInstance sFlowNodeInstance, long userId) throws SFlowNodeModificationException;\n\n    /**\n     * Set execute by delegate for the specific flow node instance\n     *\n     * @param sFlowNodeInstance\n     *        the flowNodeInstance will be updated\n     * @param executerSubstituteId\n     *        value for executedBySubstitute\n     * @throws SFlowNodeModificationException\n     * @since 6.0.1\n     */\n    void setExecutedBySubstitute(SFlowNodeInstance sFlowNodeInstance, long executerSubstituteId)\n            throws SFlowNodeModificationException;\n\n    /**\n     * Retrieve the total number of the archived flow nodes matching the given search criteria.\n     *\n     * @param entityClass\n     *        The type of the archived flow node to search for\n     * @param queryOptions\n     *        The search options to filter the results\n     * @return The number found, 0 if none matching search criteria\n     * @since 6.0\n     */\n    long getNumberOfArchivedFlowNodeInstances(Class<? extends SAFlowNodeInstance> entityClass,\n            QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * Retrieve the total number of the archived flow nodes matching the given search criteria, for a specific\n     * supervisor.\n     *\n     * @param supervisorId\n     *        The identifier of the supervisor\n     * @param entityClass\n     *        The type of the archived flow node to search for\n     * @param queryOptions\n     *        The search options to filter the results\n     * @return The number found, 0 if no matching search criteria\n     * @since 6.3\n     */\n    long getNumberOfArchivedFlowNodeInstancesSupervisedBy(long supervisorId,\n            Class<? extends SAFlowNodeInstance> entityClass, QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Retrieve the total number of the archived flow nodes matching the given search criteria.\n     *\n     * @param entityClass\n     *        The type of the archived flow node to search for\n     * @param queryOptions\n     *        The search options to filter the results\n     * @return The list of paginated results, according to the QueryOptions search criteria\n     * @since 6.0\n     */\n    <T extends SAFlowNodeInstance> List<T> searchArchivedFlowNodeInstances(Class<T> entityClass,\n            QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * Retrieve the total number of the archived flow nodes matching the given search criteria, for a specific\n     * supervisor.\n     *\n     * @param supervisorId\n     *        The identifier of the supervisor\n     * @param entityClass\n     *        The type of the archived flow node to search for\n     * @param queryOptions\n     *        The search options to filter the results\n     * @return The list of paginated results, according to the QueryOptions search criteria\n     * @since 6.3\n     */\n    <T extends SAFlowNodeInstance> List<T> searchArchivedFlowNodeInstancesSupervisedBy(long supervisorId,\n            Class<T> entityClass, QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    /**\n     * @param flowNodeInstance\n     * @param dueDate\n     * @throws SFlowNodeModificationException\n     */\n    void setExpectedEndDate(SFlowNodeInstance flowNodeInstance, Long dueDate) throws SFlowNodeModificationException;\n\n    /**\n     * @param rootContainerId\n     * @param fromIndex\n     * @param maxResults\n     * @return\n     * @throws SFlowNodeReadException\n     */\n    List<SAFlowNodeInstance> getArchivedFlowNodeInstances(long rootContainerId, int fromIndex, int maxResults)\n            throws SFlowNodeReadException;\n\n    /**\n     * @param archivedFlowNodeInstanceId\n     * @return\n     * @throws SFlowNodeReadException\n     * @throws SFlowNodeNotFoundException\n     * @since 6.0\n     */\n    SAFlowNodeInstance getArchivedFlowNodeInstance(long archivedFlowNodeInstanceId)\n            throws SFlowNodeReadException, SFlowNodeNotFoundException;\n\n    /**\n     * @param sourceObjectFlowNodeInstanceId\n     *        The source identifier of the flow node instance\n     * @return The last archived flow node\n     * @since 6.3\n     */\n    <T extends SAFlowNodeInstance> T getLastArchivedFlowNodeInstance(final Class<T> entityClass,\n            final long sourceObjectFlowNodeInstanceId)\n            throws SBonitaReadException;\n\n    /**\n     * @param flowNodeInstance\n     * @throws SFlowNodeModificationException\n     */\n    void setExecuting(SFlowNodeInstance flowNodeInstance) throws SFlowNodeModificationException;\n\n    /**\n     * @param sFlowNodeInstance\n     * @throws SFlowNodeReadException\n     * @throws SFlowNodeDeletionException\n     * @since 6.1\n     */\n    void deleteFlowNodeInstance(SFlowNodeInstance sFlowNodeInstance)\n            throws SFlowNodeReadException, SFlowNodeDeletionException;\n\n    /**\n     * retrieve ids of elements that need to be recovered\n     * Called on start node to set the flag to tell the engine to restart these flow nodes\n     * Should not be called when the engine is started!\n     * This does not retrieve SGatewayInstances\n     *\n     * @param considerElementsOlderThan consider elements older than that duration\n     * @param queryOptions used for pagination\n     */\n    List<Long> getFlowNodeInstanceIdsToRecover(Duration considerElementsOlderThan, QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    List<SFlowNodeInstance> getFlowNodeInstancesByIds(List<Long> ids) throws SBonitaReadException;\n\n    /**\n     * Retrieve ids of SGatewayInstances that need to be recovered\n     *\n     * @param queryOptions\n     * @return\n     * @throws SBonitaReadException\n     */\n    List<Long> getGatewayInstanceIdsToRecover(Duration considerElementsOlderThan, QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    /**\n     * get the number of flow node is this root container\n     *\n     * @param rootContainerId\n     * @return the number of flow node\n     * @since 6.5\n     */\n    int getNumberOfFlowNodes(long rootContainerId) throws SBonitaReadException;\n\n    /**\n     * get all flow nodes contained in the list of root source process instance ids\n     *\n     * @param sourceProcessInstanceIds root source process instance ids\n     * @return list of root source process instance ids\n     * @throws SBonitaReadException\n     */\n    Set<Long> getSourceObjectIdsOfArchivedFlowNodeInstances(List<Long> sourceProcessInstanceIds)\n            throws SBonitaReadException;\n\n    void deleteArchivedFlowNodeInstances(List<Long> sourceObjectIds) throws SBonitaException;\n\n    List<SFlowNodeInstance> getFlowNodeInstancesByNameAndParentContainerId(String name, Long parentContainerId)\n            throws SBonitaReadException;\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/GatewayInstanceService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SGatewayCreationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SGatewayModificationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SGatewayNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SGatewayReadException;\nimport org.bonitasoft.engine.core.process.instance.model.SGatewayInstance;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\n/**\n * @author Feng Hui\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @since 6.0\n */\npublic interface GatewayInstanceService {\n\n    String FINISH = \"FINISH:\";\n\n    String GATEWAYINSTANCE = \"GATEWAYINSTANCE\";\n\n    String GATEWAYINSTANCE_STATE = \"GATEWAYINSTANCE_STATE\";\n\n    String GATEWAYINSTANCE_HITBYS = \"GATEWAYINSTANCE_HITBYS\";\n\n    /**\n     * Create gatewayInstance in DB according to the given gateway instance object\n     *\n     * @param gatewayInstance\n     *        the gatewayInsttance object\n     * @throws SGatewayCreationException\n     */\n    void createGatewayInstance(SGatewayInstance gatewayInstance) throws SGatewayCreationException;\n\n    /**\n     * Get gateway instance by its id\n     *\n     * @param gatewayInstanceId\n     *        identifier of gateway instance\n     * @return an SGatewayInstance object\n     * @throws SGatewayNotFoundException\n     * @throws SGatewayReadException\n     */\n    SGatewayInstance getGatewayInstance(long gatewayInstanceId) throws SGatewayNotFoundException, SGatewayReadException;\n\n    /**\n     * Change state of the specific gateway\n     *\n     * @param gatewayInstance\n     *        the gateway instance will be updated\n     * @param stateId\n     *        identifier of gateway state\n     * @throws SGatewayModificationException\n     */\n    void setState(SGatewayInstance gatewayInstance, int stateId) throws SGatewayModificationException;\n\n    /**\n     * @param sDefinition\n     * @param gatewayInstance\n     * @return\n     * @throws SBonitaException\n     */\n    boolean checkMergingCondition(SProcessDefinition sDefinition, SGatewayInstance gatewayInstance)\n            throws SBonitaException;\n\n    /**\n     * Add transitionDefinitionName to hitBy of specific gatewayInstance\n     *\n     * @param gatewayInstance\n     *        the gateway instance will be updated\n     * @param transitionIndex\n     *        value will be added to hitBy of gatewayInstance\n     * @throws SGatewayModificationException\n     * @throws SGatewayCreationException\n     */\n    void hitTransition(SGatewayInstance gatewayInstance, long transitionIndex)\n            throws SGatewayModificationException, SGatewayCreationException;\n\n    /**\n     * Get active gatewayInstance in the specific process instance\n     *\n     * @param parentProcessInstanceId\n     *        identifier of parent process instance\n     * @param name\n     *        name of gateway instance\n     * @return\n     * @throws SGatewayNotFoundException\n     * @throws SGatewayReadException\n     */\n    SGatewayInstance getActiveGatewayInstanceOfTheProcess(long parentProcessInstanceId, String name)\n            throws SGatewayNotFoundException, SGatewayReadException;\n\n    List<SGatewayInstance> setFinishAndCreateNewGatewayForRemainingToken(SProcessDefinition processDefinition,\n            SGatewayInstance gatewayInstance) throws SBonitaException;\n\n    List<SGatewayInstance> getInclusiveGatewaysOfProcessInstanceThatShouldFire(SProcessDefinition processDefinition,\n            long processInstanceId) throws SBonitaReadException;\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/ProcessInstanceService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api;\n\nimport java.time.Duration;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceCreationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceHierarchicalDeletionException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceModificationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Yanyan Liu\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @since 6.0\n */\npublic interface ProcessInstanceService {\n\n    String PROCESSINSTANCE = \"PROCESSINSTANCE\";\n\n    String PROCESSINSTANCE_STATE = \"PROCESSINSTANCE_STATE\";\n\n    String PROCESS_INSTANCE_CATEGORY_STATE = \"PROCESS_INSTANCE_CATEGORY_STATE\";\n\n    String PROCESSINSTANCE_STATE_UPDATED = \"PROCESSINSTANCE_STATE_UPDATED\";\n\n    String PROCESSINSTANCE_TOKEN_COUNT = \"ACTIVITY_INSTANCE_TOKEN_COUNT\";\n\n    String EVENT_TRIGGER_INSTANCE = \"EVENT_TRIGGER_INSTANCE\";\n\n    /**\n     * Create process instance in DB according to the given process instance object\n     *\n     * @param processInstance\n     *        the processInstance\n     * @throws SProcessInstanceCreationException\n     */\n    void createProcessInstance(SProcessInstance processInstance) throws SProcessInstanceCreationException;\n\n    /**\n     * Delete the id specified process instance\n     *\n     * @param processInstanceId\n     *        identifier of process instance\n     * @throws SProcessInstanceNotFoundException\n     * @throws SProcessInstanceReadException\n     * @throws SFlowNodeReadException\n     * @throws SProcessInstanceModificationException\n     * @throws SProcessInstanceHierarchicalDeletionException\n     */\n    void deleteProcessInstance(long processInstanceId)\n            throws SProcessInstanceNotFoundException, SProcessInstanceReadException, SFlowNodeReadException,\n            SProcessInstanceModificationException, SProcessInstanceHierarchicalDeletionException;\n\n    /**\n     * Delete the specified process instance\n     *\n     * @param processInstance\n     *        the process instance\n     * @throws SFlowNodeReadException\n     * @throws SProcessInstanceModificationException\n     * @throws SProcessInstanceHierarchicalDeletionException\n     * @since 6.0\n     */\n    void deleteProcessInstance(SProcessInstance processInstance)\n            throws SFlowNodeReadException, SProcessInstanceModificationException,\n            SProcessInstanceHierarchicalDeletionException;\n\n    /**\n     * Delete the specified process instances with id, and their elements archived and not, if are not a subProcess\n     *\n     * @param sProcessInstances\n     *        list of process instances to deleted\n     * @return Number of deleted process instances\n     * @since 6.1\n     */\n    long deleteParentProcessInstanceAndElements(List<SProcessInstance> sProcessInstances) throws SBonitaException;\n\n    /**\n     * Delete the specified process instance, and its elements archived and not, if are not a subProcess\n     *\n     * @param processInstance\n     *        The {@link SProcessInstance} to delete\n     * @since 6.4.0\n     */\n    void deleteParentProcessInstanceAndElements(SProcessInstance processInstance) throws SBonitaException;\n\n    /**\n     * Get process instance by its id\n     *\n     * @param processInstanceId\n     *        identifier of process instance\n     * @return the process instance object\n     * @throws SProcessInstanceNotFoundException\n     * @throws SProcessInstanceReadException\n     */\n    SProcessInstance getProcessInstance(long processInstanceId)\n            throws SProcessInstanceNotFoundException, SProcessInstanceReadException;\n\n    /**\n     * Set state for the processInstance\n     *\n     * @param processInstance\n     *        the process instance will be updated\n     * @param state\n     *        the state will be set to the process instance\n     * @throws SProcessInstanceNotFoundException\n     * @throws SProcessInstanceModificationException\n     */\n    void setState(SProcessInstance processInstance, ProcessInstanceState state)\n            throws SProcessInstanceNotFoundException, SProcessInstanceModificationException;\n\n    /**\n     * Set process state category for the given process instance\n     *\n     * @param processInstance\n     *        process instance to update\n     * @param stateCatetory\n     *        new category state for the process instance\n     * @throws SProcessInstanceNotFoundException\n     * @throws SProcessInstanceModificationException\n     * @since 6.0\n     */\n    void setStateCategory(SProcessInstance processInstance, SStateCategory stateCatetory)\n            throws SProcessInstanceNotFoundException,\n            SProcessInstanceModificationException;\n\n    /**\n     * Set which event interrupted the process instance\n     */\n    void setInterruptingEventId(long parentProcessInstanceId, long eventInstanceId)\n            throws SProcessInstanceNotFoundException, SProcessInstanceReadException,\n            SProcessInstanceModificationException;\n\n    /**\n     * Delete specified archived process instance\n     *\n     * @param archivedProcessInstance\n     *        the archived process instance\n     * @throws SProcessInstanceModificationException\n     * @throws SFlowNodeReadException\n     * @since 6.0\n     */\n    void deleteArchivedProcessInstance(SAProcessInstance archivedProcessInstance)\n            throws SProcessInstanceModificationException, SFlowNodeReadException;\n\n    /**\n     * delete archived process instances using a list of root source process instance ids\n     * If the given id is not a root process instance (but a called process) it will not be deleted along with its\n     * elements.\n     *\n     * @param sourceProcessInstanceIds list of root source process instance ids\n     * @return the number of archived elements deleted (might be greater than the number of ids given)\n     * @throws SBonitaException\n     */\n    int deleteArchivedProcessInstances(List<Long> sourceProcessInstanceIds) throws SBonitaException;\n\n    /**\n     * Get child instance identifiers for specific process instance, this can be used for pagination\n     *\n     * @param processInstanceId\n     *        identifier of process instance\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param maxResults\n     *        Number of result we want to get. Maximum number of result returned\n     * @param sortingField\n     *        the field used to do order\n     * @param sortingOrder\n     *        ASC or DESC\n     * @return a list of identifiers\n     * @throws SProcessInstanceReadException\n     */\n    List<Long> getChildInstanceIdsOfProcessInstance(long processInstanceId, int fromIndex, int maxResults,\n            String sortingField, OrderByType sortingOrder)\n            throws SProcessInstanceReadException;\n\n    /**\n     * Get child process instance for the specific call activity or subprocess activity\n     *\n     * @param activityInstId\n     *        identifier of call activity or subprocess activity\n     * @return an SProcessInstance object\n     * @throws SProcessInstanceNotFoundException\n     * @throws SBonitaReadException\n     */\n    SProcessInstance getChildOfActivity(long activityInstId)\n            throws SProcessInstanceNotFoundException, SBonitaReadException;\n\n    /**\n     * Get total number of child instance for specific process instance\n     *\n     * @param processInstanceId\n     *        identifier of process instance\n     * @return number of child instance for the process instance\n     * @throws SProcessInstanceReadException\n     */\n    long getNumberOfChildInstancesOfProcessInstance(long processInstanceId) throws SProcessInstanceReadException;\n\n    /**\n     * Get the archived process instances corresponding to the identifiers\n     *\n     * @param archivedProcessInstanceIds\n     *        Identifier of the {@link SAProcessInstance}s\n     * @return The list of {@link SAProcessInstance}\n     * @throws SProcessInstanceReadException\n     * @since 6.4.0\n     */\n    List<SAProcessInstance> getArchivedProcessInstancesInAllStates(List<Long> processInstanceIds)\n            throws SProcessInstanceReadException;\n\n    /**\n     * Get total number of archived process instances according to specific criteria\n     *\n     * @param queryOptions\n     *        the search criteria containing a map of specific parameters of a query\n     * @return number of archived process instances\n     * @throws SBonitaReadException\n     */\n    long getNumberOfArchivedProcessInstances(QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * Search all archived process instance according to specific criteria\n     *\n     * @param queryOptions\n     *        the search criteria containing a map of specific parameters of a query\n     * @return A list of all archived process instance according to specific criteria\n     * @throws SBonitaReadException\n     */\n    List<SAProcessInstance> searchArchivedProcessInstances(QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * Get the latest archived process instance object for the specific process instance\n     *\n     * @param archivedProcessInstanceId\n     *        identifier of the archived process instance (not the process instance)\n     * @param persistenceService\n     * @return an SAProcessInstance object\n     * @throws SProcessInstanceReadException\n     */\n    SAProcessInstance getArchivedProcessInstance(long archivedProcessInstanceId) throws SProcessInstanceReadException;\n\n    /**\n     * Get total number of process instances\n     *\n     * @param queryOptions\n     *        a map of specific parameters of a query\n     * @return total number of process instances\n     * @throws SBonitaReadException\n     */\n    long getNumberOfProcessInstances(QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * Search all process instance according to specific criteria\n     *\n     * @param queryOptions\n     *        a map of specific parameters of a query\n     * @return a list of SProcessInstance objects\n     * @throws SBonitaReadException\n     */\n    List<SProcessInstance> searchProcessInstances(QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * Get total number of open process instances for the specific supervisor\n     *\n     * @param userId\n     *        identifier of supervisor user\n     * @param queryOptions\n     *        a map of specific parameters of a query\n     * @return number of open process instance for the specific supervisor\n     * @throws SBonitaReadException\n     */\n    long getNumberOfOpenProcessInstancesSupervisedBy(long userId, QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Get total number of {@link SProcessInstance} with at least one failed task or the\n     * {@link org.bonitasoft.engine.bpm.process.ProcessInstanceState#ERROR}\n     * state for the specific supervisor\n     *\n     * @param userId\n     *        identifier of supervisor user\n     * @param queryOptions\n     *        a map of specific parameters of a query\n     * @return The number of the {@link SProcessInstance} with at least one failed task or the\n     *         {@link org.bonitasoft.engine.bpm.process.ProcessInstanceState#ERROR} state for the specific supervisor\n     * @throws SBonitaReadException\n     * @since 7.0\n     */\n    long getNumberOfFailedProcessInstancesSupervisedBy(long userId, QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Search all {@link SProcessInstance} with at least one failed task or the\n     * {@link org.bonitasoft.engine.bpm.process.ProcessInstanceState#ERROR} state\n     * for the specific supervisor\n     *\n     * @param userId\n     *        identifier of supervisor user\n     * @param queryOptions\n     *        a map of specific parameters of a query\n     * @return The list of {@link SProcessInstance} with at least one failed task or the\n     *         {@link org.bonitasoft.engine.bpm.process.ProcessInstanceState#ERROR}\n     *         state for the specific supervisor\n     * @throws SBonitaReadException\n     * @since 7.0\n     */\n    List<SProcessInstance> searchFailedProcessInstancesSupervisedBy(long userId, QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Search all open process instances for the specific supervisor\n     *\n     * @param userId\n     *        identifier of supervisor user\n     * @param queryOptions\n     *        a map of specific parameters of a query\n     * @return a list of SProcessInstance objects\n     * @throws SBonitaReadException\n     */\n    List<SProcessInstance> searchOpenProcessInstancesSupervisedBy(long userId, QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Get total number of open process instance involving the specific user\n     *\n     * @param userId\n     *        identifier of user who can perform or be assigned to tasks in process instance.\n     * @param queryOptions\n     *        a map of specific parameters of a query\n     * @return number of open process instance for the specific user\n     * @throws SBonitaReadException\n     */\n    long getNumberOfOpenProcessInstancesInvolvingUser(long userId, QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Search all open process instance involving the specific user\n     *\n     * @param userId\n     *        identifier of user who can perform or be assigned to tasks in process instance.\n     * @param queryOptions\n     * @return a list of SProcessInstance objects\n     * @throws SBonitaReadException\n     */\n    List<SProcessInstance> searchOpenProcessInstancesInvolvingUser(long userId, QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Get total number of open process instance involving all users of the specific manager\n     *\n     * @param managerUserId\n     * @param queryOptions\n     * @return\n     * @throws SBonitaReadException\n     */\n    long getNumberOfOpenProcessInstancesInvolvingUsersManagedBy(long managerUserId, QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Search all open process instance involving all users of the specific manager\n     *\n     * @param managerUserId\n     * @param queryOptions\n     * @return\n     * @throws SBonitaReadException\n     */\n    List<SProcessInstance> searchOpenProcessInstancesInvolvingUsersManagedBy(long managerUserId,\n            QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * Get the list of all archived process instance start dates since the given date\n     *\n     * @param sinceDateInMillis the date since when we want to get the list of start dates (in milliseconds)\n     * @return the list of all archived process instance start dates, expressed in millis since EPOCH.\n     * @throws SProcessInstanceReadException if an exception occurs while reading the process instances\n     */\n    List<Long> getLastArchivedProcessInstanceStartDates(long sinceDateInMillis) throws SProcessInstanceReadException;\n\n    /**\n     * Get the list of sourceObjectIds for archived process instances children of process instance identified by\n     * rootProcessIntanceId\n     *\n     * @param rootProcessIntanceId\n     *        the root process instance id\n     * @param fromIndex\n     *        index of first result to be retried\n     * @param maxResults\n     *        max number of results to be retrieved\n     * @param sortingOrder\n     *        the searching order (ASC or DESC)\n     * @return the list of sourceObjectIds for archived process instances children of process instance identified by\n     *         rootProcessIntanceId\n     * @throws SBonitaReadException\n     * @since 6.0\n     */\n    List<Long> getArchivedChildrenSourceObjectIdsFromRootProcessInstance(long rootProcessIntanceId, int fromIndex,\n            int maxResults, OrderByType sortingOrder)\n            throws SBonitaReadException;\n\n    /**\n     * Get total number of archived process instance according to the search criteria\n     *\n     * @param queryOptions\n     *        the search criteria containing a map of specific parameters of a query\n     * @return number of archived process instance satisfied to the search criteria\n     * @throws SBonitaReadException\n     */\n    long getNumberOfArchivedProcessInstancesWithoutSubProcess(QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * Search all archived process instance according to the search criteria\n     *\n     * @param queryOptions\n     *        the search criteria containing a map of specific parameters of a query\n     * @return a list of SAProcessInstance objects\n     * @throws SBonitaReadException\n     */\n    List<SAProcessInstance> searchArchivedProcessInstancesWithoutSubProcess(QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Get total number of archived process instance for the specific supervisor\n     *\n     * @param userId\n     *        identifier of user who is the supervisor of archived process instance.\n     * @param countOptions\n     *        the search criteria containing a map of specific parameters of a query\n     * @return number of archived process instance for the specific supervisor\n     * @throws SBonitaReadException\n     */\n    long getNumberOfArchivedProcessInstancesSupervisedBy(long userId, QueryOptions countOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Search all archived process instance for the specific supervisor\n     *\n     * @param userId\n     *        identifier of user who is the supervisor of archived process instance.\n     * @param queryOptions\n     *        the search criteria containing a map of specific parameters of a query\n     * @return a list of SAProcessInstance objects\n     * @throws SBonitaReadException\n     */\n    List<SAProcessInstance> searchArchivedProcessInstancesSupervisedBy(long userId, QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Get total number of archived process instance involving the specific user\n     *\n     * @param userId\n     *        the identifier of user who is assignee of tasks of process instance\n     * @param countOptions\n     *        the search criteria containing a map of specific parameters of a query\n     * @return number of archived process instance involving the specific user\n     * @throws SBonitaReadException\n     */\n    long getNumberOfArchivedProcessInstancesInvolvingUser(long userId, QueryOptions countOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Search all archived process instance involving the specific user\n     *\n     * @param userId\n     *        the identifier of user who is assignee of tasks of process instance\n     * @param queryOptions\n     *        the search criteria containing a map of specific parameters of a query\n     * @return a list of SAProcessInstance objects\n     * @throws SBonitaReadException\n     */\n    List<SAProcessInstance> searchArchivedProcessInstancesInvolvingUser(long userId, QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Update the specific process instance\n     *\n     * @param processInstance\n     *        the processInstance will be updated\n     * @param descriptor\n     *        update description\n     * @throws SProcessInstanceModificationException\n     */\n    void updateProcess(SProcessInstance processInstance, EntityUpdateDescriptor descriptor)\n            throws SProcessInstanceModificationException;\n\n    /**\n     * @param flowNodeInstance\n     * @param processDefinition\n     * @throws SFlowNodeReadException\n     * @throws SProcessInstanceModificationException\n     */\n    void deleteFlowNodeInstance(SFlowNodeInstance flowNodeInstance, SProcessDefinition processDefinition)\n            throws SFlowNodeReadException,\n            SProcessInstanceModificationException;\n\n    /**\n     * @param processDefinitionId\n     * @param fromIndex\n     * @param maxResults\n     * @param sortingOrder\n     * @return\n     * @throws SProcessInstanceReadException\n     */\n    List<Long> getSourceProcessInstanceIdsOfArchProcessInstancesFromDefinition(long processDefinitionId, int fromIndex,\n            int maxResults, OrderByType sortingOrder)\n            throws SProcessInstanceReadException;\n\n    /**\n     * @param sourceObjectProcessInstanceId\n     *        The source identifier of the process instance\n     * @return The last archived process instance\n     * @since 6.3\n     */\n    SAProcessInstance getLastArchivedProcessInstance(long sourceObjectProcessInstanceId) throws SBonitaReadException;\n\n    /**\n     * Get the number of the {@link SProcessInstance} with at least one failed task or the\n     * {@link org.bonitasoft.engine.bpm.process.ProcessInstanceState#ERROR}\n     * state.\n     *\n     * @param queryOptions\n     *        the search criteria containing a map of specific parameters of a query\n     * @return The number of the {@link SProcessInstance} with at least one failed task or the\n     *         {@link org.bonitasoft.engine.bpm.process.ProcessInstanceState#ERROR} state.\n     * @throws SBonitaException\n     * @since 6.4.0\n     */\n    long getNumberOfFailedProcessInstances(QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * List all {@link SProcessInstance} with at least one failed task or the\n     * {@link org.bonitasoft.engine.bpm.process.ProcessInstanceState#ERROR} state.\n     *\n     * @param queryOptions\n     *        the search criteria containing a map of specific parameters of a query\n     * @return The list of {@link SProcessInstance} with at least one failed task or the\n     *         {@link org.bonitasoft.engine.bpm.process.ProcessInstanceState#ERROR}\n     *         state.\n     * @throws SBonitaException\n     * @since 6.4.0\n     */\n    List<SProcessInstance> searchFailedProcessInstances(QueryOptions queryOptions) throws SBonitaReadException;\n\n    long getNumberOfProcessInstances(long processDefinitionId) throws SBonitaReadException;\n\n    /**\n     * Retrieve ids of process instances nodes that needs to be recovered.\n     * This is used by recover mechanism ProcessInstanceRecoveryService\n     *\n     * @param considerElementsOlderThan consider elements older than that duration\n     * @param queryOptions used for pagination\n     */\n    List<Long> getProcessInstanceIdsToRecover(Duration considerElementsOlderThan, QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/RefBusinessDataService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceCreationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceModificationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.model.archive.business.data.SARefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\n/**\n * @author Matthieu Chaffotte\n * @author Emmanuel Duchastenier\n */\npublic interface RefBusinessDataService {\n\n    String NEW_REF_BUSINESS_DATA_INSTANCE_ADDED = \"New reference to a business data added\";\n\n    String REF_BUSINESS_DATA_INSTANCE = \"REF_BUSINESS_DATA_INSTANCE\";\n\n    SRefBusinessDataInstance getRefBusinessDataInstance(String name, long processInstanceId)\n            throws SRefBusinessDataInstanceNotFoundException, SBonitaReadException;\n\n    List<SRefBusinessDataInstance> getRefBusinessDataInstances(long processInstanceId, int startIndex, int maxResults)\n            throws SBonitaReadException;\n\n    SRefBusinessDataInstance getFlowNodeRefBusinessDataInstance(String name, long flowNodeInstanceId)\n            throws SRefBusinessDataInstanceNotFoundException, SBonitaReadException;\n\n    SRefBusinessDataInstance addRefBusinessDataInstance(SRefBusinessDataInstance instance)\n            throws SRefBusinessDataInstanceCreationException;\n\n    void updateRefBusinessDataInstance(SSimpleRefBusinessDataInstance refBusinessDataInstance, Long dataId)\n            throws SRefBusinessDataInstanceModificationException;\n\n    void updateRefBusinessDataInstance(SProcessMultiRefBusinessDataInstance refBusinessDataInstance, List<Long> dataIds)\n            throws SRefBusinessDataInstanceModificationException;\n\n    int getNumberOfDataOfMultiRefBusinessData(String name, long processInstanceId) throws SBonitaReadException;\n\n    SARefBusinessDataInstance getSARefBusinessDataInstance(String name, long processInstanceId)\n            throws SRefBusinessDataInstanceNotFoundException, SBonitaReadException;\n\n    SARefBusinessDataInstance getSAFlowNodeRefBusinessDataInstance(String name, long flowNodeInstanceId)\n            throws SRefBusinessDataInstanceNotFoundException, SBonitaReadException;\n\n    void archiveRefBusinessDataInstance(SRefBusinessDataInstance businessDataInstance)\n            throws SObjectModificationException; // TODO; specific exception\n\n    void deleteArchivedRefBusinessDataInstance(long processInstanceId) throws SObjectModificationException;\n\n    void deleteArchivedRefBusinessDataInstance(List<Long> processInstanceIds) throws SObjectModificationException;\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/event/EventInstanceRepository.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.event;\n\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.SEventInstanceCreationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.SEventInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceCreationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceDeletionException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceModificationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageInstanceCreationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageModificationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventCreationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventModificationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventReadException;\nimport org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageEventCouple;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingErrorEvent;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent;\nimport org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.search.SearchOptions;\n\n/**\n * @author Pascal GARCIA\n */\npublic interface EventInstanceRepository {\n\n    String EVENT_INSTANCE = \"EVENT_INSTANCE\";\n\n    String EVENT_TRIGGER_INSTANCE = \"EVENT_TRIGGER_INSTANCE\";\n\n    String MESSAGE_INSTANCE = \"MESSAGE_INSTANCE\";\n\n    int IN_REQUEST_SIZE = 100;\n\n    void createEventInstance(SEventInstance eventInstance) throws SEventInstanceCreationException;\n\n    /**\n     * STimerEventTriggerInstance is used to keep track of currently running timers\n     * using {@link org.bonitasoft.engine.api.ProcessAPI#searchTimerEventTriggerInstances(long, SearchOptions)}\n     */\n    void createTimerEventTriggerInstance(STimerEventTriggerInstance sEventTriggerInstance)\n            throws SEventTriggerInstanceCreationException;\n\n    void createMessageInstance(SMessageInstance messageInstance) throws SMessageInstanceCreationException;\n\n    void createWaitingEvent(SWaitingEvent waitingEvent) throws SWaitingEventCreationException;\n\n    SWaitingErrorEvent getBoundaryWaitingErrorEvent(long relatedActivityInstanceId, String errorCode)\n            throws SWaitingEventReadException;\n\n    List<SEventInstance> getEventInstances(long rootContainerId, int fromIndex, int maxResults, String fieldName,\n            OrderByType orderByType)\n            throws SEventInstanceReadException;\n\n    /**\n     * @param activityInstanceId\n     * @param fromIndex\n     * @param maxResults\n     * @return List of SBoundaryEventInstance, ordered by identifier ascending\n     * @throws SEventInstanceReadException\n     * @since 6.2\n     */\n    List<SBoundaryEventInstance> getActivityBoundaryEventInstances(long activityInstanceId, int fromIndex,\n            int maxResults) throws SEventInstanceReadException;\n\n    /**\n     * @param entityClass\n     * @param eventTriggerInstanceId\n     * @return\n     * @throws SEventTriggerInstanceReadException\n     * @since 6.4.0\n     */\n    <T extends STimerEventTriggerInstance> T getEventTriggerInstance(Class<T> entityClass, long eventTriggerInstanceId)\n            throws SEventTriggerInstanceReadException;\n\n    void deleteMessageInstance(SMessageInstance messageInstance) throws SMessageModificationException;\n\n    void deleteWaitingEvent(SWaitingEvent waitingEvent) throws SWaitingEventModificationException;\n\n    /**\n     * @param signalName\n     * @param fromIndex\n     * @param maxResults\n     * @return\n     * @throws SEventTriggerInstanceReadException\n     * @since 6.3\n     */\n    List<SWaitingSignalEvent> getWaitingSignalEvents(String signalName, int fromIndex, int maxResults)\n            throws SEventTriggerInstanceReadException;\n\n    /**\n     * search start waiting events related to a process definition (not its event sub processes)\n     *\n     * @param processDefinitionId\n     * @return\n     * @throws SBonitaReadException\n     * @since 6.3\n     */\n    List<SWaitingEvent> getStartWaitingEventsOfProcessDefinition(long processDefinitionId)\n            throws SBonitaReadException;\n\n    List<SMessageEventCouple> getMessageEventCouples(int fromIndex, int maxResults)\n            throws SEventTriggerInstanceReadException;\n\n    SWaitingMessageEvent getWaitingMessage(long waitingMessageId) throws SWaitingEventReadException;\n\n    SMessageInstance getMessageInstance(long messageInstanceId) throws SMessageInstanceReadException;\n\n    void deleteMessageInstanceByIds(List<Long> ids) throws SMessageModificationException;\n\n    List<Long> getMessageInstanceIdOlderThanCreationDate(final long creationDate,\n            QueryOptions queryOptions)\n            throws SEventTriggerInstanceReadException, SMessageInstanceReadException;\n\n    void updateWaitingMessage(SWaitingMessageEvent waitingMessageEvent, EntityUpdateDescriptor descriptor)\n            throws SWaitingEventModificationException;\n\n    void updateMessageInstance(SMessageInstance messageInstance, EntityUpdateDescriptor descriptor)\n            throws SMessageModificationException;\n\n    <T extends SWaitingEvent> List<T> searchWaitingEvents(Class<T> entityClass, QueryOptions searchOptions)\n            throws SBonitaReadException;\n\n    long getNumberOfWaitingEvents(Class<? extends SWaitingEvent> entityClass, QueryOptions countOptions)\n            throws SBonitaReadException;\n\n    /**\n     * @param flowNodeInstanceId the flow node instance id\n     * @return the timer event trigger instance of this flow node if there is one\n     */\n    Optional<STimerEventTriggerInstance> getTimerEventTriggerInstanceOfFlowNode(long flowNodeInstanceId)\n            throws SBonitaReadException;\n\n    SWaitingSignalEvent getWaitingSignalEvent(long id)\n            throws SEventTriggerInstanceReadException, SEventTriggerInstanceNotFoundException;\n\n    /**\n     * @param eventTriggerInstance\n     * @throws SEventTriggerInstanceDeletionException\n     * @since 6.1\n     */\n    void deleteEventTriggerInstance(STimerEventTriggerInstance eventTriggerInstance)\n            throws SEventTriggerInstanceDeletionException;\n\n    /**\n     * Resets all Message Instances marked as handled, so that they are eligible to match Waiting Events again.\n     *\n     * @throws SMessageModificationException\n     *         if an error occurs when resetting the 'handled' flag.\n     */\n    int resetProgressMessageInstances() throws SMessageModificationException;\n\n    /**\n     * Resets all Waiting Message Events marked as 'in progress\", so that they are eligible to match Message Instances\n     * again.\n     *\n     * @return the number of waiting events reset.\n     * @throws SWaitingEventModificationException\n     *         if an error occurs when resetting the 'progress' flag.\n     */\n    int resetInProgressWaitingEvents() throws SWaitingEventModificationException;\n\n    /**\n     * Get the number of STimerEventTriggerInstance on the specific process instance & corresponding to the criteria\n     *\n     * @param processInstanceId\n     *        The identifier of the process instance\n     * @param queryOptions\n     *        Criteria of the search\n     * @return The number of STimerEventTriggerInstance on the specific process instance & corresponding to the criteria\n     * @since 6.4.0\n     */\n    long getNumberOfTimerEventTriggerInstances(long processInstanceId, QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Search the list of STimerEventTriggerInstance on the specific process instance & corresponding to the criteria\n     *\n     * @param processInstanceId\n     *        The identifier of the process instance\n     * @param queryOptions\n     *        Criteria of the search\n     * @return The list of STimerEventTriggerInstance on the specific process instance & corresponding to the criteria\n     * @since 6.4.0\n     */\n    List<STimerEventTriggerInstance> searchTimerEventTriggerInstances(long processInstanceId, QueryOptions queryOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Update an event trigger instance.\n     *\n     * @param sTimerEventTriggerInstance\n     *        The event trigger instance to update\n     * @param descriptor\n     *        The fields to update\n     * @throws SEventTriggerInstanceModificationException\n     * @since 6.4.0\n     */\n    void updateEventTriggerInstance(STimerEventTriggerInstance sTimerEventTriggerInstance,\n            EntityUpdateDescriptor descriptor)\n            throws SEventTriggerInstanceModificationException;\n\n    List<SWaitingEvent> getWaitingEventsForFlowNodeId(long flowNodeInstanceId)\n            throws SEventTriggerInstanceReadException;\n\n    /**\n     * Return waiting events related to the process instance passed as parameter, including\n     * the ones at flow node level.\n     */\n    List<SWaitingEvent> getAllWaitingEventsForProcessInstance(long processInstanceId, final int fromIndex,\n            final int maxResults) throws SEventTriggerInstanceReadException;\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/event/EventInstanceService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.event;\n\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.SEventInstanceCreationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.SEventInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceCreationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceDeletionException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceModificationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageInstanceCreationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageModificationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventCreationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventModificationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventReadException;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageEventCouple;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingErrorEvent;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent;\nimport org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.search.SearchOptions;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic interface EventInstanceService {\n\n    void createEventInstance(SEventInstance eventInstance) throws SEventInstanceCreationException;\n\n    /**\n     * STimerEventTriggerInstance is used to keep track of currently running timers\n     * using {@link org.bonitasoft.engine.api.ProcessAPI#searchTimerEventTriggerInstances(long, SearchOptions)}\n     */\n    void createTimerEventTriggerInstance(STimerEventTriggerInstance sEventTriggerInstance)\n            throws SEventTriggerInstanceCreationException;\n\n    void createMessageInstance(SMessageInstance messageInstance) throws SMessageInstanceCreationException;\n\n    void createWaitingEvent(SWaitingEvent waitingEvent) throws SWaitingEventCreationException;\n\n    SWaitingErrorEvent getBoundaryWaitingErrorEvent(long relatedActivityInstanceId, String errorCode)\n            throws SWaitingEventReadException;\n\n    List<SEventInstance> getEventInstances(long rootContainerId, int fromIndex, int maxResults, String fieldName,\n            OrderByType orderByType)\n            throws SEventInstanceReadException;\n\n    void deleteWaitingEvents(SProcessInstance processInstance)\n            throws SWaitingEventModificationException, SEventTriggerInstanceReadException;\n\n    /**\n     * @param activityInstanceId\n     * @param fromIndex\n     * @param maxResults\n     * @return List of SBoundaryEventInstance, ordered by identifier ascending\n     * @throws SEventInstanceReadException\n     * @since 6.2\n     */\n    List<SBoundaryEventInstance> getActivityBoundaryEventInstances(long activityInstanceId, int fromIndex,\n            int maxResults) throws SEventInstanceReadException;\n\n    /**\n     * @param entityClass\n     * @param eventTriggerInstanceId\n     * @return\n     * @throws SEventTriggerInstanceReadException\n     * @since 6.4.0\n     */\n    <T extends STimerEventTriggerInstance> T getEventTriggerInstance(Class<T> entityClass, long eventTriggerInstanceId)\n            throws SEventTriggerInstanceReadException;\n\n    void deleteMessageInstance(SMessageInstance messageInstance) throws SMessageModificationException;\n\n    void deleteWaitingEvent(SWaitingEvent waitingEvent) throws SWaitingEventModificationException;\n\n    /**\n     * @param signalName\n     * @param fromIndex\n     * @param maxResults\n     * @return\n     * @throws SEventTriggerInstanceReadException\n     * @since 6.3\n     */\n    List<SWaitingSignalEvent> getWaitingSignalEvents(String signalName, int fromIndex, int maxResults)\n            throws SEventTriggerInstanceReadException;\n\n    /**\n     * @param processDefinitionId\n     * @return\n     * @throws SBonitaReadException\n     * @since 6.3\n     */\n    List<SWaitingEvent> getStartWaitingEventsOfProcessDefinition(long processDefinitionId)\n            throws SBonitaReadException;\n\n    List<SMessageEventCouple> getMessageEventCouples(int fromIndex, int maxResults)\n            throws SEventTriggerInstanceReadException;\n\n    SWaitingMessageEvent getWaitingMessage(long waitingMessageId) throws SWaitingEventReadException;\n\n    SMessageInstance getMessageInstance(long messageInstanceId) throws SMessageInstanceReadException;\n\n    void updateWaitingMessage(SWaitingMessageEvent waitingMessageEvent, EntityUpdateDescriptor descriptor)\n            throws SWaitingEventModificationException;\n\n    void updateMessageInstance(SMessageInstance messageInstance, EntityUpdateDescriptor descriptor)\n            throws SMessageModificationException;\n\n    <T extends SWaitingEvent> List<T> searchWaitingEvents(Class<T> entityClass, QueryOptions searchOptions)\n            throws SBonitaReadException;\n\n    long getNumberOfWaitingEvents(Class<? extends SWaitingEvent> entityClass, QueryOptions countOptions)\n            throws SBonitaReadException;\n\n    /**\n     * @param flowNodeInstanceId the flow node instance id\n     * @return the timer event trigger instance of this flow node if there is one\n     */\n    Optional<STimerEventTriggerInstance> getTimerEventTriggerInstanceOfFlowNode(long flowNodeInstanceId)\n            throws SBonitaReadException;\n\n    SWaitingSignalEvent getWaitingSignalEvent(long id)\n            throws SEventTriggerInstanceReadException, SEventTriggerInstanceNotFoundException;\n\n    /**\n     * @param eventTriggerInstance\n     * @throws SEventTriggerInstanceDeletionException\n     * @since 6.1\n     */\n    void deleteEventTriggerInstance(STimerEventTriggerInstance eventTriggerInstance)\n            throws SEventTriggerInstanceDeletionException;\n\n    void deleteEventTriggerInstanceOfFlowNode(long flowNodeInstanceId)\n            throws SBonitaReadException, SEventTriggerInstanceDeletionException;\n\n    /**\n     * @param flowNodeInstance\n     * @throws SWaitingEventModificationException\n     * @throws SEventTriggerInstanceReadException\n     * @since 6.1\n     */\n    void deleteWaitingEvents(SFlowNodeInstance flowNodeInstance)\n            throws SWaitingEventModificationException, SEventTriggerInstanceReadException;\n\n    /**\n     * Resets all Message Instances marked as handled, so that they are eligible to match Waiting Events again.\n     *\n     * @throws SMessageModificationException\n     *         if an error occurs when resetting the 'handled' flag.\n     */\n    int resetProgressMessageInstances() throws SMessageModificationException;\n\n    /**\n     * Resets all Waiting Message Events marked as 'in progress\", so that they are eligible to match Message Instances\n     * again.\n     *\n     * @return the number of waiting events reset.\n     * @throws SWaitingEventModificationException\n     *         if an error occurs when resetting the 'progress' flag.\n     */\n    int resetInProgressWaitingEvents() throws SWaitingEventModificationException;\n\n    /**\n     * Get the number of STimerEventTriggerInstance on the specific process instance & corresponding to the criteria\n     *\n     * @param processInstanceId\n     *        The identifier of the process instance\n     * @param searchOptions\n     *        Criteria of the search\n     * @return The number of STimerEventTriggerInstance on the specific process instance & corresponding to the criteria\n     * @since 6.4.0\n     */\n    long getNumberOfTimerEventTriggerInstances(long processInstanceId, QueryOptions searchOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Search the list of STimerEventTriggerInstance on the specific process instance & corresponding to the criteria\n     *\n     * @param processInstanceId\n     *        The identifier of the process instance\n     * @param searchOptions\n     *        Criteria of the search\n     * @return The list of STimerEventTriggerInstance on the specific process instance & corresponding to the criteria\n     * @since 6.4.0\n     */\n    List<STimerEventTriggerInstance> searchTimerEventTriggerInstances(long processInstanceId,\n            QueryOptions searchOptions) throws SBonitaReadException;\n\n    Integer deleteMessageAndDataInstanceOlderThanCreationDate(long creationDate,\n            QueryOptions queryOptions) throws SMessageModificationException;\n\n    List<Long> getMessageInstanceIdOlderThanCreationDate(long creationDate, QueryOptions queryOptions)\n            throws SEventTriggerInstanceReadException, SMessageInstanceReadException;\n\n    /**\n     * Update an event trigger instance.\n     *\n     * @param sTimerEventTriggerInstance\n     *        The event trigger instance to update\n     * @param descriptor\n     *        The fields to update\n     * @throws SEventTriggerInstanceModificationException\n     * @since 6.4.0\n     */\n    void updateEventTriggerInstance(STimerEventTriggerInstance sTimerEventTriggerInstance,\n            EntityUpdateDescriptor descriptor)\n            throws SEventTriggerInstanceModificationException;\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SAProcessInstanceNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Celine Souchet\n */\npublic class SAProcessInstanceNotFoundException extends SBonitaException {\n\n    private static final long serialVersionUID = 1956687311066803177L;\n\n    public SAProcessInstanceNotFoundException(final long processInstanceId) {\n        super(\"Archived process instance with id <\" + processInstanceId + \"> not found\");\n    }\n\n    public SAProcessInstanceNotFoundException(final long processInstanceId, final String state) {\n        super(\"Archived process instance with id <\" + processInstanceId + \"> and state <\" + state + \"> not found\");\n    }\n\n    public SAProcessInstanceNotFoundException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SActivityCreationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SActivityCreationException extends SBonitaException {\n\n    private static final long serialVersionUID = 831038382346357330L;\n\n    public SActivityCreationException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SActivityCreationException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SActivityCreationException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SActivityExecutionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions;\n\n/**\n * @author Baptiste Mesta\n * @author Nicolas Chabanoles\n * @author Celine Souchet\n *         An unexpected error happened during the execution of the activity\n *         The activity might be in an inconsistent state\n */\npublic class SActivityExecutionException extends SFlowNodeExecutionException {\n\n    private static final long serialVersionUID = -1134776910207667894L;\n\n    public SActivityExecutionException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SActivityExecutionException(final String message) {\n        super(message);\n    }\n\n    public SActivityExecutionException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SActivityExecutionFailedException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n *         An error happened during the execution of the activity\n *         The activity is now in a failed state\n */\npublic class SActivityExecutionFailedException extends SFlowNodeExecutionException {\n\n    private static final long serialVersionUID = 8873662434571064042L;\n\n    public SActivityExecutionFailedException(final Throwable e) {\n        super(e);\n    }\n\n    /**\n     * @param message\n     * @param cause\n     */\n    public SActivityExecutionFailedException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SActivityInstanceNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SActivityInstanceNotFoundException extends SBonitaException {\n\n    private static final long serialVersionUID = 4163590480725860398L;\n\n    public SActivityInstanceNotFoundException(final long activityInstanceId) {\n        super(\"Activity instance with id \" + activityInstanceId + \" not found\");\n    }\n\n    public SActivityInstanceNotFoundException(final long activityInstanceId, final int stateId) {\n        super(\"Activity instance with id \" + activityInstanceId + \" and stateId \" + stateId + \" not found\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SActivityModificationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions;\n\nimport java.io.Serial;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class SActivityModificationException extends SBonitaException {\n\n    @Serial\n    private static final long serialVersionUID = 976164513418178786L;\n\n    public SActivityModificationException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SActivityModificationException(final String message) {\n        super(message);\n    }\n\n    public SActivityModificationException(final String message, final Throwable e) {\n        super(message, e);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SActivityReadException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SActivityReadException extends SFlowNodeReadException {\n\n    private static final long serialVersionUID = 8517963344655147902L;\n\n    public SActivityReadException(final Throwable e) {\n        super(e);\n    }\n\n    public SActivityReadException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SActivityStateExecutionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class SActivityStateExecutionException extends SFlowNodeExecutionException {\n\n    private static final long serialVersionUID = -6135671543460179364L;\n\n    public SActivityStateExecutionException(final String message) {\n        super(message);\n    }\n\n    public SActivityStateExecutionException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SActivityStateExecutionException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SActivityStateExecutionException(final String message, String scope, final Throwable cause) {\n        super(message, scope, cause);\n    }\n\n    public SActivityStateExecutionException(final String message, String scope) {\n        super(message, scope);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SContractViolationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * Thrown when the {@link org.bonitasoft.engine.core.process.definition.model.SContractDefinition} is not fulfilled.\n *\n * @author Emmanuel Duchastenier\n * @since 7.0\n */\npublic class SContractViolationException extends SBonitaException {\n\n    private final List<String> explanations;\n    private final String simpleMessage;\n\n    /**\n     * Constructs an <code>SContractViolationException</code> with the specified detail message and the explanations.\n     *\n     * @param message the specified detail message\n     * @param explanations the explanations\n     */\n    public SContractViolationException(final String message, final List<String> explanations) {\n        super(message + \": \" + ((explanations == null) || (explanations.isEmpty()) ? \"no details\"\n                : String.join(\", \", explanations)));\n        this.simpleMessage = message;\n        if (explanations == null) {\n            this.explanations = Collections.emptyList();\n        } else {\n            this.explanations = new ArrayList<>(explanations);\n        }\n    }\n\n    public SContractViolationException(String message, Exception e) {\n        super(message, e);\n        this.simpleMessage = message;\n        this.explanations = Collections.emptyList();\n    }\n\n    /**\n     * Returns the explanations of why the contract is not fulfilled.\n     *\n     * @return the explanations\n     */\n    public List<String> getExplanations() {\n        return explanations;\n    }\n\n    public String getSimpleMessage() {\n        return simpleMessage;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SFlowElementModificationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SFlowElementModificationException extends SBonitaException {\n\n    private static final long serialVersionUID = 976164513418178786L;\n\n    public SFlowElementModificationException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SFlowNodeDeletionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Celine Souchet\n */\npublic class SFlowNodeDeletionException extends SBonitaException {\n\n    private static final long serialVersionUID = 976164513418178786L;\n\n    public SFlowNodeDeletionException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SFlowNodeExecutionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * An unexpected error happened during the execution of the flow node\n * The flow node might be in an inconsistent state\n *\n * @author Celine Souchet\n */\npublic class SFlowNodeExecutionException extends SBonitaException {\n\n    private static final long serialVersionUID = 5549874111741638842L;\n\n    public SFlowNodeExecutionException(final String message, final String scope, final Throwable cause) {\n        super(message, scope, cause);\n    }\n\n    public SFlowNodeExecutionException(final String message, final String scope) {\n        super(message, scope);\n    }\n\n    public SFlowNodeExecutionException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SFlowNodeExecutionException(final String message) {\n        super(message);\n    }\n\n    public SFlowNodeExecutionException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SFlowNodeModificationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SFlowNodeModificationException extends SBonitaException {\n\n    private static final long serialVersionUID = 976164513418178786L;\n\n    public SFlowNodeModificationException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SFlowNodeModificationException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SFlowNodeNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SFlowNodeNotFoundException extends SBonitaException {\n\n    private static final long serialVersionUID = -3838993791524556098L;\n\n    public SFlowNodeNotFoundException(final long flowNodeId) {\n        super(\"Flow node instance with id \" + flowNodeId + \" not found\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SFlowNodeReadException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SFlowNodeReadException extends SBonitaException {\n\n    private static final long serialVersionUID = 7025419917545646389L;\n\n    public SFlowNodeReadException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SFlowNodeReadException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SFlowNodeReadException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SGatewayCreationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Feng Hui\n */\npublic class SGatewayCreationException extends SBonitaException {\n\n    private static final long serialVersionUID = 831038382346357330L;\n\n    public SGatewayCreationException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SGatewayModificationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Feng Hui\n */\npublic class SGatewayModificationException extends SBonitaException {\n\n    private static final long serialVersionUID = 4889011534447314330L;\n\n    public SGatewayModificationException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SGatewayNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Feng Hui\n * @author Matthieu Chaffotte\n */\npublic class SGatewayNotFoundException extends SBonitaException {\n\n    private static final long serialVersionUID = -7022009042275018689L;\n\n    public SGatewayNotFoundException(final long gatewayInstanceId) {\n        super(\"Gateway instance with id \" + gatewayInstanceId + \" not found\");\n    }\n\n    public SGatewayNotFoundException(final long processInstanceId, final String name) {\n        super(\"Gateway instance with process id '\" + processInstanceId + \"' and name '\" + name + \"' not found\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SGatewayReadException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions;\n\nimport org.bonitasoft.engine.commons.exceptions.SObjectReadException;\n\n/**\n * @author Feng Hui\n */\npublic class SGatewayReadException extends SObjectReadException {\n\n    private static final long serialVersionUID = 8517963344655147902L;\n\n    public SGatewayReadException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SProcessInstanceCreationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions;\n\nimport lombok.Getter;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SProcessInstanceCreationException extends SBonitaException {\n\n    private static final long serialVersionUID = 7581906795549409593L;\n\n    @Getter\n    private long retryAfter = -1L;\n\n    public SProcessInstanceCreationException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SProcessInstanceCreationException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SProcessInstanceCreationException(final String message) {\n        super(message);\n    }\n\n    public SProcessInstanceCreationException(final String message, final long retryAfter) {\n        super(message);\n        this.retryAfter = retryAfter;\n    }\n\n    /**\n     * @param sDefinition\n     *        The process definition to add on context\n     * @since 6.3\n     */\n    public void setProcessDefinitionOnContext(final SProcessDefinition sDefinition) {\n        setProcessDefinitionIdOnContext(sDefinition.getId());\n        setProcessDefinitionNameOnContext(sDefinition.getName());\n        setProcessDefinitionVersionOnContext(sDefinition.getVersion());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SProcessInstanceDeletionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SProcessInstanceDeletionException extends SBonitaException {\n\n    private static final long serialVersionUID = 1704316503564609224L;\n\n    public SProcessInstanceDeletionException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SProcessInstanceDeletionException(final String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SProcessInstanceHierarchicalDeletionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SProcessInstanceHierarchicalDeletionException extends SBonitaException {\n\n    private static final long serialVersionUID = 1704316503564609224L;\n\n    private final long processInstanceId;\n\n    public SProcessInstanceHierarchicalDeletionException(final String string, final long processInstanceId) {\n        super(string);\n        this.processInstanceId = processInstanceId;\n    }\n\n    public long getProcessInstanceId() {\n        return processInstanceId;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SProcessInstanceModificationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SProcessInstanceModificationException extends SBonitaException {\n\n    private static final long serialVersionUID = 4889011534447314330L;\n\n    public SProcessInstanceModificationException(final Throwable cause) {\n        super(cause);\n    }\n\n    /**\n     * @param string\n     * @param e\n     */\n    public SProcessInstanceModificationException(final String string, final Exception e) {\n        super(string, e);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SProcessInstanceNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Baptiste Mesta\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class SProcessInstanceNotFoundException extends SBonitaException {\n\n    private static final long serialVersionUID = 1956687311066803177L;\n\n    public SProcessInstanceNotFoundException(final long processInstanceId) {\n        super(\"Process instance with id <\" + processInstanceId + \"> not found\");\n    }\n\n    public SProcessInstanceNotFoundException(final long processInstanceId, final String state) {\n        super(\"Process instance with id <\" + processInstanceId + \"> and state <\" + state + \"> not found\");\n    }\n\n    public SProcessInstanceNotFoundException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SProcessInstanceReadException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.persistence.AbstractSelectDescriptor;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Emmanuel Duchastenier\n */\npublic class SProcessInstanceReadException extends SBonitaException {\n\n    private static final long serialVersionUID = -5549277707760652364L;\n\n    public SProcessInstanceReadException(final Throwable cause) {\n        super(formatMessage(cause), cause);\n    }\n\n    private static final String formatMessage(final Throwable cause) {\n        String message = null;\n        if (cause instanceof SBonitaReadException) {\n            final AbstractSelectDescriptor<?> descriptor = ((SBonitaReadException) cause).getSelectDescriptor();\n            if (descriptor != null) {\n                message = descriptor.toString();\n            }\n        }\n        return message;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/STaskVisibilityException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n */\npublic class STaskVisibilityException extends SBonitaException {\n\n    private static final long serialVersionUID = 7406562594163981383L;\n\n    public STaskVisibilityException(final String message) {\n        super(message);\n    }\n\n    public STaskVisibilityException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public STaskVisibilityException(final String message, final long activityInstanceId, final long userId) {\n        super(message);\n        setFlowNodeDefinitionIdOnContext(activityInstanceId);\n        setUserIdOnContext(userId);\n    }\n\n    /**\n     * @param taskInstanceId\n     *        the ID of the task whose visibility is being\n     * @param userId\n     *        the ID of the user whose Task visibility is associated to\n     */\n    public STaskVisibilityException(final String message, final long activityInstanceId, final long userId,\n            final Throwable cause) {\n        super(message, cause);\n        setFlowNodeDefinitionIdOnContext(activityInstanceId);\n        setUserIdOnContext(userId);\n    }\n\n    public STaskVisibilityException(final String message, final long activityInstanceId) {\n        super(message);\n        setFlowNodeDefinitionIdOnContext(activityInstanceId);\n    }\n\n    public STaskVisibilityException(final String message, final long activityInstanceId, final Throwable cause) {\n        super(message, cause);\n        setFlowNodeDefinitionIdOnContext(activityInstanceId);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SUnknowStateCategory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SUnknowStateCategory extends SBonitaException {\n\n    private static final long serialVersionUID = 6420290986048627139L;\n\n    public SUnknowStateCategory(final String categoryState) {\n        super(\"Unknown state category: \" + categoryState);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SuserTaskSkipException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Zhang Bole\n */\npublic class SuserTaskSkipException extends SBonitaException {\n\n    /**\n     *\n     */\n    private static final long serialVersionUID = -7556717384908033L;\n\n    public SuserTaskSkipException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/business/SHumanTaskAlreadyAssignedException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions.business;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Pablo Alonso de Linaje\n */\npublic class SHumanTaskAlreadyAssignedException extends SBonitaException {\n\n    public SHumanTaskAlreadyAssignedException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SHumanTaskAlreadyAssignedException(final String message, final Throwable e) {\n        super(message, e);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/business/data/SRefBusinessDataInstanceCreationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions.business.data;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SRefBusinessDataInstanceCreationException extends SBonitaException {\n\n    private static final long serialVersionUID = 4703430668508021915L;\n\n    public SRefBusinessDataInstanceCreationException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/business/data/SRefBusinessDataInstanceModificationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions.business.data;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SRefBusinessDataInstanceModificationException extends SBonitaException {\n\n    private static final long serialVersionUID = 3527430173631664746L;\n\n    public SRefBusinessDataInstanceModificationException(final Throwable cause) {\n        super(cause);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/business/data/SRefBusinessDataInstanceNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions.business.data;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SRefBusinessDataInstanceNotFoundException extends SBonitaException {\n\n    private static final long serialVersionUID = -5163481117317685640L;\n\n    public SRefBusinessDataInstanceNotFoundException(final long processInstanceId, final String name) {\n        super(\"Unable to find a reference to a business data named '\" + name + \"' of process instance \"\n                + processInstanceId);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/event/SEventInstanceCreationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions.event;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SEventInstanceCreationException extends SEventInstanceException {\n\n    private static final long serialVersionUID = -2743737085797834497L;\n\n    public SEventInstanceCreationException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SEventInstanceCreationException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/event/SEventInstanceException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions.event;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SEventInstanceException extends SBonitaException {\n\n    private static final long serialVersionUID = -6849370511498116125L;\n\n    public SEventInstanceException(final String message) {\n        super(message);\n    }\n\n    public SEventInstanceException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SEventInstanceException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/event/SEventInstanceNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions.event;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SEventInstanceNotFoundException extends SEventInstanceException {\n\n    private static final long serialVersionUID = 6394599118486821409L;\n\n    public SEventInstanceNotFoundException(final long eventId) {\n        super(\"Unable to find event instance with id \" + eventId);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/event/SEventInstanceReadException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions.event;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SEventInstanceReadException extends SEventInstanceException {\n\n    private static final long serialVersionUID = 3246685428772443332L;\n\n    public SEventInstanceReadException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SEventInstanceReadException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/event/trigger/SEventTriggerInstanceCreationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SEventTriggerInstanceCreationException extends SEventTriggerInstanceException {\n\n    private static final long serialVersionUID = -3184254325049610898L;\n\n    public SEventTriggerInstanceCreationException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/event/trigger/SEventTriggerInstanceDeletionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SEventTriggerInstanceDeletionException extends SEventTriggerInstanceException {\n\n    private static final long serialVersionUID = -3184254325049610898L;\n\n    public SEventTriggerInstanceDeletionException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/event/trigger/SEventTriggerInstanceException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SEventTriggerInstanceException extends SBonitaException {\n\n    private static final long serialVersionUID = 5400072957387312123L;\n\n    public SEventTriggerInstanceException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SEventTriggerInstanceException(final String message) {\n        super(message);\n    }\n\n    public SEventTriggerInstanceException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/event/trigger/SEventTriggerInstanceModificationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger;\n\n/**\n * @author Celine Souchet\n * @version 6.4.0\n * @since 6.4.0\n */\npublic class SEventTriggerInstanceModificationException extends SEventTriggerInstanceException {\n\n    private static final long serialVersionUID = -2488805930392940258L;\n\n    public SEventTriggerInstanceModificationException(String message) {\n        super(message);\n    }\n\n    public SEventTriggerInstanceModificationException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n    public SEventTriggerInstanceModificationException(Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/event/trigger/SEventTriggerInstanceNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SEventTriggerInstanceNotFoundException extends SEventTriggerInstanceException {\n\n    private static final long serialVersionUID = 4533632552469947077L;\n\n    public SEventTriggerInstanceNotFoundException(final long eventTriggerId) {\n        super(\"Unable to find event trigger instance with id \" + eventTriggerId);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/event/trigger/SEventTriggerInstanceReadException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SEventTriggerInstanceReadException extends SEventTriggerInstanceException {\n\n    private static final long serialVersionUID = -6798342718027113516L;\n\n    public SEventTriggerInstanceReadException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SEventTriggerInstanceReadException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/event/trigger/SMessageInstanceCreationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SMessageInstanceCreationException extends SEventTriggerInstanceException {\n\n    private static final long serialVersionUID = 8177794618577658085L;\n\n    public SMessageInstanceCreationException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SMessageInstanceCreationException(final String message) {\n        super(message);\n    }\n\n    public SMessageInstanceCreationException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/event/trigger/SMessageInstanceNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger;\n\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.SEventInstanceException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SMessageInstanceNotFoundException extends SEventInstanceException {\n\n    private static final long serialVersionUID = 6394599118486821409L;\n\n    public SMessageInstanceNotFoundException(final long messageInstanceId) {\n        super(\"Unable to find message instance with id \" + messageInstanceId);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/event/trigger/SMessageInstanceReadException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SMessageInstanceReadException extends SEventTriggerInstanceException {\n\n    private static final long serialVersionUID = -9015190020329584232L;\n\n    public SMessageInstanceReadException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SMessageInstanceReadException(final String message) {\n        super(message);\n    }\n\n    public SMessageInstanceReadException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/event/trigger/SMessageModificationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SMessageModificationException extends SEventTriggerInstanceException {\n\n    private static final long serialVersionUID = -2488805930392940258L;\n\n    public SMessageModificationException(String message) {\n        super(message);\n    }\n\n    public SMessageModificationException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n    public SMessageModificationException(Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/event/trigger/SWaitingEventCreationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SWaitingEventCreationException extends SEventTriggerInstanceException {\n\n    private static final long serialVersionUID = 8177794618577658085L;\n\n    public SWaitingEventCreationException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n    public SWaitingEventCreationException(String message) {\n        super(message);\n    }\n\n    public SWaitingEventCreationException(Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/event/trigger/SWaitingEventModificationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SWaitingEventModificationException extends SEventTriggerInstanceException {\n\n    private static final long serialVersionUID = -2488805930392940258L;\n\n    public SWaitingEventModificationException(String message) {\n        super(message);\n    }\n\n    public SWaitingEventModificationException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n    public SWaitingEventModificationException(Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/event/trigger/SWaitingEventNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger;\n\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.SEventInstanceException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SWaitingEventNotFoundException extends SEventInstanceException {\n\n    private static final long serialVersionUID = 6394599118486821409L;\n\n    public SWaitingEventNotFoundException(final long waitingMessageId) {\n        super(\"Unable to find waiting message event with id \" + waitingMessageId);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/event/trigger/SWaitingEventReadException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SWaitingEventReadException extends SEventTriggerInstanceException {\n\n    private static final long serialVersionUID = 3000875111256588497L;\n\n    public SWaitingEventReadException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SWaitingEventReadException(final String message) {\n        super(message);\n    }\n\n    public SWaitingEventReadException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/states/FlowNodeState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.states;\n\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface FlowNodeState {\n\n    int ID_ACTIVITY_EXECUTING = 1;\n    int ID_ACTIVITY_READY = 4;\n    int ID_ACTIVITY_FAILED = 3;\n\n    int getId();\n\n    /**\n     * @param processDefinition\n     * @param flowNodeInstance\n     * @return true the state must be executed, false if the execution must skip this state and go directly to the next\n     *         one\n     * @throws SActivityExecutionException\n     */\n    boolean shouldExecuteState(SProcessDefinition processDefinition, SFlowNodeInstance flowNodeInstance)\n            throws SActivityExecutionException;\n\n    /**\n     * Return true if flowNodeInstance instance of SHumanTaskInstance\n     *\n     * @param flowNodeInstance\n     * @return true or false\n     * @since 6.0\n     */\n    boolean mustAddSystemComment(SFlowNodeInstance flowNodeInstance);\n\n    /**\n     * Add a system comment \"User XYZ has XYZ(state change) task XYZ(task name)\"\n     *\n     * @param flowNodeInstance\n     * @return system comment \"User XYZ has XYZ(state change) task XYZ(task name)\"\n     * @since 6.0\n     */\n    String getSystemComment(SFlowNodeInstance flowNodeInstance);\n\n    StateCode execute(SProcessDefinition processDefinition, SFlowNodeInstance instance)\n            throws SActivityStateExecutionException;\n\n    /**\n     * Called when a child of the flow node parentInstance finishes.\n     * Triggers what's next, if applicable.\n     * returns if all children activity is finished / triggered.\n     *\n     * @return true if the state is finished (the flow node will continue its flow),\n     *         false if there are still some children to be triggered / to wait for.\n     */\n    default boolean notifyChildFlowNodeHasFinished(SProcessDefinition processDefinition,\n            SFlowNodeInstance parentInstance, SFlowNodeInstance childInstance)\n            throws SActivityStateExecutionException {\n\n        return false;\n    }\n\n    String getName();\n\n    /**\n     * @return true if the state is stable\n     *         a final state is stable\n     */\n    boolean isStable();\n\n    /**\n     * Checks whether the state is a terminal one.\n     *\n     * @return true is the state is a terminal one; false otherwise\n     */\n    boolean isTerminal();\n\n    /**\n     * Get the state's category\n     *\n     * @return the state's category\n     */\n    SStateCategory getStateCategory();\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/states/StateCode.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.states;\n\n/**\n * This is code that are returned by state when they are execute\n * DONE is when the state is finished and executed correctly\n * EXECUTING when there is still work to be done on the state (so the state must not change)\n *\n * @author Baptiste Mesta\n */\npublic enum StateCode {\n\n    DONE, EXECUTING;\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/event/impl/EventInstanceRepositoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.event.impl;\n\nimport static java.util.Collections.singletonMap;\n\nimport java.util.*;\nimport java.util.stream.Collectors;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections4.ListUtils;\nimport org.bonitasoft.engine.archive.ArchiveService;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceRepository;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.SEventInstanceCreationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.SEventInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.*;\nimport org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.*;\nimport org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance;\nimport org.bonitasoft.engine.core.process.instance.recorder.SelectDescriptorBuilder;\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.persistence.*;\nimport org.bonitasoft.engine.persistence.search.FilterOperationType;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.bonitasoft.engine.services.PersistenceService;\nimport org.bonitasoft.engine.services.SPersistenceException;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @author Frederic Bouquet\n * @author Celine Souchet\n */\n@Slf4j\npublic class EventInstanceRepositoryImpl implements EventInstanceRepository {\n\n    public static final String QUERY_RESET_IN_PROGRESS_WAITING_EVENTS = \"resetInProgressWaitingEvents\";\n\n    private static final String QUERY_RESET_PROGRESS_MESSAGE_INSTANCES = \"resetProgressMessageInstances\";\n\n    private final EventService eventService;\n\n    private final Recorder recorder;\n\n    private final PersistenceService persistenceService;\n\n    public EventInstanceRepositoryImpl(final Recorder recorder, final PersistenceService persistenceService,\n            final EventService eventService, final ArchiveService archiveService) {\n\n        this.recorder = recorder;\n        this.eventService = eventService;\n        this.persistenceService = persistenceService;\n    }\n\n    @Override\n    public void createEventInstance(final SEventInstance eventInstance) throws SEventInstanceCreationException {\n        try {\n            recorder.recordInsert(new InsertRecord(eventInstance), EVENT_INSTANCE);\n        } catch (final SRecorderException e) {\n            throw new SEventInstanceCreationException(e);\n        }\n        if (log.isDebugEnabled()) {\n            final StringBuilder stb = new StringBuilder();\n            stb.append(\"Created \");\n            stb.append(eventInstance.getType().getValue());\n            stb.append(\" <\");\n            stb.append(eventInstance.getName());\n            stb.append(\"> with id = <\");\n            stb.append(eventInstance.getId());\n            stb.append(\">, parent process instance id = <\");\n            stb.append(eventInstance.getParentProcessInstanceId());\n            stb.append(\">, root process instance id = <\");\n            stb.append(eventInstance.getRootProcessInstanceId());\n            stb.append(\">, process definition id = <\");\n            stb.append(eventInstance.getProcessDefinitionId());\n            stb.append(\">\");\n            final String message = stb.toString();\n            log.debug(message);\n        }\n    }\n\n    @Override\n    public void createTimerEventTriggerInstance(final STimerEventTriggerInstance eventTriggerInstance)\n            throws SEventTriggerInstanceCreationException {\n        try {\n            recorder.recordInsert(new InsertRecord(eventTriggerInstance), EVENT_TRIGGER_INSTANCE);\n        } catch (final SRecorderException e) {\n            throw new SEventTriggerInstanceCreationException(e);\n        }\n    }\n\n    @Override\n    public void createMessageInstance(SMessageInstance messageInstance) throws SMessageInstanceCreationException {\n        try {\n            recorder.recordInsert(new InsertRecord(messageInstance), MESSAGE_INSTANCE);\n        } catch (final SRecorderException e) {\n            throw new SMessageInstanceCreationException(e);\n        }\n    }\n\n    @Override\n    public void createWaitingEvent(final SWaitingEvent waitingEvent) throws SWaitingEventCreationException {\n        try {\n            recorder.recordInsert(new InsertRecord(waitingEvent), EVENT_TRIGGER_INSTANCE);\n        } catch (final SRecorderException e) {\n            throw new SWaitingEventCreationException(e);\n        }\n\n    }\n\n    @Override\n    public void deleteEventTriggerInstance(final STimerEventTriggerInstance eventTriggerInstance)\n            throws SEventTriggerInstanceDeletionException {\n        try {\n            recorder.recordDelete(new DeleteRecord(eventTriggerInstance), EVENT_TRIGGER_INSTANCE);\n        } catch (final SRecorderException e) {\n            throw new SEventTriggerInstanceDeletionException(e);\n        }\n    }\n\n    @Override\n    public void deleteMessageInstance(final SMessageInstance messageInstance) throws SMessageModificationException {\n        try {\n            recorder.recordDelete(new DeleteRecord(messageInstance), MESSAGE_INSTANCE);\n        } catch (final SRecorderException e) {\n            throw new SMessageModificationException(e);\n        }\n    }\n\n    @Override\n    public List<SBoundaryEventInstance> getActivityBoundaryEventInstances(final long activityInstanceId,\n            final int fromIndex, final int maxResults)\n            throws SEventInstanceReadException {\n        final SelectListDescriptor<SBoundaryEventInstance> selectDescriptor = SelectDescriptorBuilder\n                .getActivityBoundaryEvents(activityInstanceId, fromIndex,\n                        maxResults);\n        try {\n            return persistenceService.selectList(selectDescriptor);\n        } catch (final SBonitaReadException e) {\n            throw new SEventInstanceReadException(e);\n        }\n    }\n\n    @Override\n    public SWaitingErrorEvent getBoundaryWaitingErrorEvent(final long relatedActivityInstanceId, final String errorCode)\n            throws SWaitingEventReadException {\n        final QueryOptions queryOptions = new QueryOptions(0, 2, SWaitingErrorEvent.class, \"id\", OrderByType.ASC);\n        SelectListDescriptor<SWaitingErrorEvent> selectDescriptor;\n        if (errorCode == null) {\n            selectDescriptor = SelectDescriptorBuilder.getCaughtError(relatedActivityInstanceId, queryOptions);\n        } else {\n            selectDescriptor = SelectDescriptorBuilder.getCaughtError(relatedActivityInstanceId, errorCode,\n                    queryOptions);\n        }\n        SWaitingErrorEvent waitingError = null;\n        try {\n            final List<SWaitingErrorEvent> selectList = persistenceService.selectList(selectDescriptor);\n            if (selectList != null && !selectList.isEmpty()) {\n                if (selectList.size() == 1) {\n                    waitingError = selectList.get(0);\n                } else {\n                    final StringBuilder stb = new StringBuilder();\n                    stb.append(\"Only one catch error event was expected to handle the error code \");\n                    stb.append(errorCode);\n                    stb.append(\" in the activity instance with id \");\n                    stb.append(relatedActivityInstanceId + \".\");\n                    throw new SWaitingEventReadException(stb.toString());\n                }\n            }\n        } catch (final SBonitaReadException e) {\n            throw new SWaitingEventReadException(e);\n        }\n        return waitingError;\n    }\n\n    @Override\n    public List<SEventInstance> getEventInstances(final long rootContainerId, final int fromIndex, final int maxResults,\n            final String fieldName,\n            final OrderByType orderByType) throws SEventInstanceReadException {\n        final SelectListDescriptor<SEventInstance> selectDescriptor = SelectDescriptorBuilder\n                .getEventsFromRootContainer(rootContainerId, fromIndex,\n                        maxResults, fieldName, orderByType);\n        try {\n            return persistenceService.selectList(selectDescriptor);\n        } catch (final SBonitaReadException e) {\n            throw new SEventInstanceReadException(e);\n        }\n    }\n\n    @Override\n    public <T extends STimerEventTriggerInstance> T getEventTriggerInstance(final Class<T> entityClass,\n            final long eventTriggerInstanceId)\n            throws SEventTriggerInstanceReadException {\n        try {\n            return persistenceService.selectById(\n                    SelectDescriptorBuilder.getElementById(entityClass, entityClass.getSimpleName(),\n                            eventTriggerInstanceId));\n        } catch (final SBonitaReadException e) {\n            throw new SEventTriggerInstanceReadException(e);\n        }\n    }\n\n    @Override\n    public int resetProgressMessageInstances() throws SMessageModificationException {\n        try {\n            return persistenceService.update(QUERY_RESET_PROGRESS_MESSAGE_INSTANCES);\n        } catch (final SPersistenceException e) {\n            throw new SMessageModificationException(e);\n        }\n    }\n\n    @Override\n    public int resetInProgressWaitingEvents() throws SWaitingEventModificationException {\n        try {\n            return persistenceService.update(QUERY_RESET_IN_PROGRESS_WAITING_EVENTS);\n        } catch (final SPersistenceException e) {\n            throw new SWaitingEventModificationException(e);\n        }\n    }\n\n    @Override\n    public List<SMessageEventCouple> getMessageEventCouples(final int fromIndex, final int maxResults)\n            throws SEventTriggerInstanceReadException {\n        final SelectListDescriptor<SMessageEventCouple> selectDescriptor = SelectDescriptorBuilder\n                .getMessageEventCouples(fromIndex, maxResults);\n        try {\n            return persistenceService.selectList(selectDescriptor);\n        } catch (final SBonitaReadException e) {\n            throw new SEventTriggerInstanceReadException(e);\n        }\n    }\n\n    @Override\n    public SMessageInstance getMessageInstance(final long messageInstanceId) throws SMessageInstanceReadException {\n        try {\n            return persistenceService\n                    .selectById(SelectDescriptorBuilder.getElementById(SMessageInstance.class, \"MessageInstance\",\n                            messageInstanceId));\n        } catch (final SBonitaReadException e) {\n            throw new SMessageInstanceReadException(e);\n        }\n    }\n\n    @Override\n    public List<Long> getMessageInstanceIdOlderThanCreationDate(final long creationDate,\n            QueryOptions queryOptions) throws SMessageInstanceReadException {\n        try {\n            if (queryOptions != null) {\n                validateFiltersForGetMessage(queryOptions);\n            } else {\n                queryOptions = QueryOptions.ALL_RESULTS;\n            }\n            final SelectListDescriptor<Long> selectDescriptor = SelectDescriptorBuilder\n                    .getMessageInstanceIdOlderThanCreationDate(creationDate, queryOptions);\n            return persistenceService.selectList(selectDescriptor);\n        } catch (final SBonitaReadException e) {\n            throw new SMessageInstanceReadException(e);\n        }\n    }\n\n    private void validateFiltersForGetMessage(QueryOptions queryOptions) {\n        List<FilterOption> notOkField = queryOptions.getFilters().stream()\n                .filter(filterOption -> filterOption.getFilterOperationType() != FilterOperationType.EQUALS\n                        || !filterOption.getFieldName().equals(\"messageName\")\n                        || filterOption.getPersistentClass() != SMessageInstance.class)\n                .collect(Collectors.toList());\n        if (!notOkField.isEmpty()) {\n            throw new IllegalArgumentException(\n                    \"Unsupported filters  \" + notOkField + \", can only filter on messageName\");\n        }\n\n    }\n\n    @Override\n    public void deleteMessageInstanceByIds(List<Long> ids) throws SMessageModificationException {\n        List<List<Long>> listAsFragment = ListUtils.partition(ids, IN_REQUEST_SIZE);\n        for (List<Long> fragmentIds : listAsFragment) {\n            try {\n                Map<String, Object> parameters = new HashMap<>();\n                parameters.put(\"ids\", fragmentIds);\n                persistenceService.update(\"deleteMessageInstanceByIds\", parameters);\n            } catch (SPersistenceException e) {\n                throw new SMessageModificationException(e);\n            }\n        }\n\n    }\n\n    @Override\n    public long getNumberOfWaitingEvents(final Class<? extends SWaitingEvent> entityClass,\n            final QueryOptions countOptions) throws SBonitaReadException {\n        return persistenceService.getNumberOfEntities(entityClass, countOptions, null);\n    }\n\n    @Override\n    public List<SWaitingEvent> getStartWaitingEventsOfProcessDefinition(final long processDefinitionId)\n            throws SBonitaReadException {\n        // event sub-processes are not returned:\n        return persistenceService.selectList(new SelectListDescriptor<>(\"getStartWaitingEvents\",\n                singletonMap(\"processDefinitionId\", processDefinitionId), SWaitingEvent.class,\n                QueryOptions.ALL_RESULTS));\n    }\n\n    @Override\n    public SWaitingMessageEvent getWaitingMessage(final long waitingMessageId) throws SWaitingEventReadException {\n        try {\n            return persistenceService.selectById(\n                    SelectDescriptorBuilder.getElementById(SWaitingMessageEvent.class, \"WaitingMessageEvent\",\n                            waitingMessageId));\n        } catch (final SBonitaReadException e) {\n            throw new SWaitingEventReadException(e);\n        }\n    }\n\n    @Override\n    public void deleteWaitingEvent(final SWaitingEvent waitingEvent) throws SWaitingEventModificationException {\n        try {\n            recorder.recordDelete(new DeleteRecord(waitingEvent), EVENT_TRIGGER_INSTANCE);\n        } catch (final SRecorderException e) {\n            throw new SWaitingEventModificationException(e);\n        }\n    }\n\n    @Override\n    public List<SWaitingEvent> getWaitingEventsForFlowNodeId(long flowNodeInstanceId)\n            throws SEventTriggerInstanceReadException {\n        try {\n            return persistenceService.selectList(new SelectListDescriptor<>(\"getWaitingEventsOfFlowNode\",\n                    Collections.singletonMap(\"flowNodeInstanceId\", flowNodeInstanceId),\n                    SWaitingEvent.class, new QueryOptions(0, 100)));\n        } catch (final SBonitaReadException e) {\n            throw new SEventTriggerInstanceReadException(e);\n        }\n    }\n\n    @Override\n    public List<SWaitingEvent> getAllWaitingEventsForProcessInstance(long processInstanceId, final int fromIndex,\n            final int maxResults) throws SEventTriggerInstanceReadException {\n        try {\n            return persistenceService\n                    .selectList(new SelectListDescriptor<>(\"getWaitingEventsForProcessInstance\",\n                            Collections.singletonMap(\"processInstanceId\", processInstanceId),\n                            SWaitingEvent.class, new QueryOptions(fromIndex, maxResults)));\n        } catch (final SBonitaReadException e) {\n            throw new SEventTriggerInstanceReadException(e);\n        }\n    }\n\n    @Override\n    public List<SWaitingSignalEvent> getWaitingSignalEvents(final String signalName, final int fromIndex,\n            final int maxResults)\n            throws SEventTriggerInstanceReadException {\n        final SelectListDescriptor<SWaitingSignalEvent> descriptor = SelectDescriptorBuilder\n                .getListeningSignals(signalName, fromIndex, maxResults);\n        try {\n            return persistenceService.selectList(descriptor);\n        } catch (final SBonitaReadException e) {\n            throw new SEventTriggerInstanceReadException(e);\n        }\n    }\n\n    @Override\n    public SWaitingSignalEvent getWaitingSignalEvent(final long id)\n            throws SEventTriggerInstanceReadException, SEventTriggerInstanceNotFoundException {\n        final SelectByIdDescriptor<SWaitingSignalEvent> descriptor = new SelectByIdDescriptor<>(\n                SWaitingSignalEvent.class, id);\n        try {\n            SWaitingSignalEvent sWaitingSignalEvent = persistenceService.selectById(descriptor);\n            if (sWaitingSignalEvent == null) {\n                throw new SEventTriggerInstanceNotFoundException(id);\n            }\n            return sWaitingSignalEvent;\n        } catch (final SBonitaReadException e) {\n            throw new SEventTriggerInstanceReadException(e);\n        }\n    }\n\n    @Override\n    public Optional<STimerEventTriggerInstance> getTimerEventTriggerInstanceOfFlowNode(long flowNodeInstanceId)\n            throws SBonitaReadException {\n        return Optional.ofNullable(persistenceService.selectOne(new SelectOneDescriptor<>(\n                \"getEventTriggerInstances\",\n                singletonMap(\"eventInstanceId\", flowNodeInstanceId),\n                STimerEventTriggerInstance.class)));\n    }\n\n    @Override\n    public long getNumberOfTimerEventTriggerInstances(final long processInstanceId, final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = singletonMap(\"processInstanceId\", processInstanceId);\n        return persistenceService.getNumberOfEntities(STimerEventTriggerInstance.class, \"ByProcessInstance\",\n                queryOptions, parameters);\n    }\n\n    @Override\n    public List<STimerEventTriggerInstance> searchTimerEventTriggerInstances(final long processInstanceId,\n            final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = singletonMap(\"processInstanceId\", processInstanceId);\n        return persistenceService.searchEntity(STimerEventTriggerInstance.class, \"ByProcessInstance\", queryOptions,\n                parameters);\n    }\n\n    @Override\n    public <T extends SWaitingEvent> List<T> searchWaitingEvents(final Class<T> entityClass,\n            final QueryOptions searchOptions) throws SBonitaReadException {\n        return persistenceService.searchEntity(entityClass, searchOptions, null);\n    }\n\n    @Override\n    public void updateMessageInstance(final SMessageInstance messageInstance, final EntityUpdateDescriptor descriptor)\n            throws SMessageModificationException {\n        try {\n            recorder.recordUpdate(UpdateRecord.buildSetFields(messageInstance, descriptor), MESSAGE_INSTANCE);\n        } catch (final SRecorderException re) {\n            throw new SMessageModificationException(re);\n        }\n    }\n\n    @Override\n    public void updateWaitingMessage(final SWaitingMessageEvent waitingMessageEvent,\n            final EntityUpdateDescriptor descriptor)\n            throws SWaitingEventModificationException {\n        try {\n            recorder.recordUpdate(UpdateRecord.buildSetFields(waitingMessageEvent, descriptor), MESSAGE_INSTANCE);\n        } catch (final SRecorderException e) {\n            throw new SWaitingEventModificationException(e);\n        }\n    }\n\n    @Override\n    public void updateEventTriggerInstance(final STimerEventTriggerInstance sTimerEventTriggerInstance,\n            final EntityUpdateDescriptor descriptor)\n            throws SEventTriggerInstanceModificationException {\n        try {\n            recorder.recordUpdate(UpdateRecord.buildSetFields(sTimerEventTriggerInstance, descriptor),\n                    EVENT_TRIGGER_INSTANCE);\n        } catch (final SRecorderException e) {\n            throw new SEventTriggerInstanceModificationException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/event/impl/EventInstanceServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.event.impl;\n\nimport java.util.List;\nimport java.util.Optional;\n\nimport io.micrometer.core.instrument.Counter;\nimport io.micrometer.core.instrument.MeterRegistry;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceRepository;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.SEventInstanceCreationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.SEventInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceCreationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceDeletionException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceModificationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageInstanceCreationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageModificationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventCreationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventModificationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventReadException;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageEventCouple;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingErrorEvent;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent;\nimport org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceService;\nimport org.bonitasoft.engine.data.instance.exception.SDataInstanceException;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\npublic class EventInstanceServiceImpl implements EventInstanceService {\n\n    private final DataInstanceService dataInstanceService;\n    public static final String BONITA_BPMENGINE_MESSAGE_SENT = \"bonita.bpmengine.message.sent\";\n    private final Counter messageSentCounter;\n\n    private final EventInstanceRepository eventInstanceRepository;\n\n    public EventInstanceServiceImpl(EventInstanceRepository eventInstanceRepository,\n            DataInstanceService dataInstanceService, MeterRegistry meterRegistry) {\n        this.eventInstanceRepository = eventInstanceRepository;\n        this.dataInstanceService = dataInstanceService;\n        messageSentCounter = meterRegistry.counter(BONITA_BPMENGINE_MESSAGE_SENT);\n    }\n\n    @Override\n    public void createEventInstance(SEventInstance eventInstance)\n            throws SEventInstanceCreationException {\n        this.eventInstanceRepository.createEventInstance(eventInstance);\n    }\n\n    @Override\n    public void createTimerEventTriggerInstance(STimerEventTriggerInstance sEventTriggerInstance)\n            throws SEventTriggerInstanceCreationException {\n        this.eventInstanceRepository.createTimerEventTriggerInstance(sEventTriggerInstance);\n    }\n\n    @Override\n    public void createMessageInstance(SMessageInstance messageInstance) throws SMessageInstanceCreationException {\n        messageSentCounter.increment();\n        this.eventInstanceRepository.createMessageInstance(messageInstance);\n    }\n\n    @Override\n    public void createWaitingEvent(SWaitingEvent sWaitingEvent) throws SWaitingEventCreationException {\n        this.eventInstanceRepository.createWaitingEvent(sWaitingEvent);\n    }\n\n    @Override\n    public void deleteEventTriggerInstance(STimerEventTriggerInstance sTimerEventTriggerInstance)\n            throws SEventTriggerInstanceDeletionException {\n        this.eventInstanceRepository.deleteEventTriggerInstance(sTimerEventTriggerInstance);\n    }\n\n    @Override\n    public void deleteEventTriggerInstanceOfFlowNode(long flowNodeInstanceId)\n            throws SBonitaReadException, SEventTriggerInstanceDeletionException {\n        Optional<STimerEventTriggerInstance> timerEventTriggerInstanceOfFlowNode = getTimerEventTriggerInstanceOfFlowNode(\n                flowNodeInstanceId);\n        if (!timerEventTriggerInstanceOfFlowNode.isPresent()) {\n            return;\n        }\n        deleteEventTriggerInstance(timerEventTriggerInstanceOfFlowNode.get());\n    }\n\n    @Override\n    public void deleteMessageInstance(SMessageInstance messageInstance) throws SMessageModificationException {\n        this.eventInstanceRepository.deleteMessageInstance(messageInstance);\n    }\n\n    @Override\n    public void deleteWaitingEvent(SWaitingEvent sWaitingEvent) throws SWaitingEventModificationException {\n        this.eventInstanceRepository.deleteWaitingEvent(sWaitingEvent);\n    }\n\n    @Override\n    public void deleteWaitingEvents(SFlowNodeInstance flowNodeInstance)\n            throws SWaitingEventModificationException, SEventTriggerInstanceReadException {\n        List<SWaitingEvent> waitingEvents;\n        do {\n            waitingEvents = this.eventInstanceRepository.getWaitingEventsForFlowNodeId(flowNodeInstance.getId());\n            for (final SWaitingEvent sWaitingEvent : waitingEvents) {\n                deleteWaitingEvent(sWaitingEvent);\n            }\n        } while (waitingEvents.size() == 100);\n\n    }\n\n    @Override\n    public void deleteWaitingEvents(SProcessInstance processInstance)\n            throws SWaitingEventModificationException, SEventTriggerInstanceReadException {\n        List<SWaitingEvent> waitingEvents;\n        final int pageCount = 100;\n        do {\n            waitingEvents = this.eventInstanceRepository.getAllWaitingEventsForProcessInstance(processInstance.getId(),\n                    0, pageCount);\n            for (final SWaitingEvent sWaitingEvent : waitingEvents) {\n                deleteWaitingEvent(sWaitingEvent);\n            }\n        } while (waitingEvents.size() == pageCount);\n\n    }\n\n    @Override\n    public List<SBoundaryEventInstance> getActivityBoundaryEventInstances(long id, int fromIndex,\n            int maxResults) throws SEventInstanceReadException {\n        return this.eventInstanceRepository.getActivityBoundaryEventInstances(id, fromIndex, maxResults);\n    }\n\n    @Override\n    public SWaitingErrorEvent getBoundaryWaitingErrorEvent(long id, String catchingErrorCode)\n            throws SWaitingEventReadException {\n        return this.eventInstanceRepository.getBoundaryWaitingErrorEvent(id, catchingErrorCode);\n    }\n\n    @Override\n    public List<SEventInstance> getEventInstances(long rootContainerId, int fromIndex, int maxResults, String fieldName,\n            OrderByType orderByType) throws SEventInstanceReadException {\n        return this.eventInstanceRepository.getEventInstances(rootContainerId, fromIndex, maxResults, fieldName,\n                orderByType);\n    }\n\n    @Override\n    public <T extends STimerEventTriggerInstance> T getEventTriggerInstance(Class<T> entityClass,\n            long eventTriggerInstanceId) throws SEventTriggerInstanceReadException {\n        return this.eventInstanceRepository.getEventTriggerInstance(entityClass, eventTriggerInstanceId);\n    }\n\n    @Override\n    public int resetProgressMessageInstances() throws SMessageModificationException {\n        return this.eventInstanceRepository.resetProgressMessageInstances();\n    }\n\n    @Override\n    public int resetInProgressWaitingEvents() throws SWaitingEventModificationException {\n        return this.eventInstanceRepository.resetInProgressWaitingEvents();\n    }\n\n    @Override\n    public List<SMessageEventCouple> getMessageEventCouples(int i, int maxCouples)\n            throws SEventTriggerInstanceReadException {\n        return this.eventInstanceRepository.getMessageEventCouples(i, maxCouples);\n    }\n\n    @Override\n    public SMessageInstance getMessageInstance(long messageInstanceId) throws SMessageInstanceReadException {\n        return this.eventInstanceRepository.getMessageInstance(messageInstanceId);\n    }\n\n    @Override\n    public long getNumberOfWaitingEvents(final Class<? extends SWaitingEvent> sWaitingEventClass,\n            QueryOptions searchOptions) throws SBonitaReadException {\n        return this.eventInstanceRepository.getNumberOfWaitingEvents(sWaitingEventClass, searchOptions);\n    }\n\n    @Override\n    public List<SWaitingEvent> getStartWaitingEventsOfProcessDefinition(long processDefinitionId)\n            throws SBonitaReadException {\n        return this.eventInstanceRepository.getStartWaitingEventsOfProcessDefinition(processDefinitionId);\n    }\n\n    @Override\n    public SWaitingMessageEvent getWaitingMessage(long waitingMessageId) throws SWaitingEventReadException {\n        return this.eventInstanceRepository.getWaitingMessage(waitingMessageId);\n    }\n\n    @Override\n    public List<SWaitingSignalEvent> getWaitingSignalEvents(String signalName, int fromIndex, int maxResults)\n            throws SEventTriggerInstanceReadException {\n        return this.eventInstanceRepository.getWaitingSignalEvents(signalName, fromIndex, maxResults);\n    }\n\n    @Override\n    public SWaitingSignalEvent getWaitingSignalEvent(long signalId)\n            throws SEventTriggerInstanceReadException, SEventTriggerInstanceNotFoundException {\n        return this.eventInstanceRepository.getWaitingSignalEvent(signalId);\n    }\n\n    @Override\n    public Optional<STimerEventTriggerInstance> getTimerEventTriggerInstanceOfFlowNode(long flowNodeInstanceId)\n            throws SBonitaReadException {\n        return this.eventInstanceRepository.getTimerEventTriggerInstanceOfFlowNode(flowNodeInstanceId);\n    }\n\n    @Override\n    public long getNumberOfTimerEventTriggerInstances(long processInstanceId, QueryOptions searchOptions)\n            throws SBonitaReadException {\n        return this.eventInstanceRepository.getNumberOfTimerEventTriggerInstances(processInstanceId, searchOptions);\n    }\n\n    @Override\n    public List<STimerEventTriggerInstance> searchTimerEventTriggerInstances(long processInstanceId,\n            QueryOptions searchOptions) throws SBonitaReadException {\n        return this.eventInstanceRepository.searchTimerEventTriggerInstances(processInstanceId, searchOptions);\n    }\n\n    @Override\n    public <T extends SWaitingEvent> List<T> searchWaitingEvents(Class<T> waitingEventClass,\n            QueryOptions queryOptions) throws SBonitaReadException {\n        return this.eventInstanceRepository.searchWaitingEvents(waitingEventClass, queryOptions);\n    }\n\n    @Override\n    public void updateMessageInstance(SMessageInstance messageInstance, EntityUpdateDescriptor descriptor)\n            throws SMessageModificationException {\n        this.eventInstanceRepository.updateMessageInstance(messageInstance, descriptor);\n    }\n\n    @Override\n    public void updateWaitingMessage(SWaitingMessageEvent waitingMsg, EntityUpdateDescriptor descriptor)\n            throws SWaitingEventModificationException {\n        this.eventInstanceRepository.updateWaitingMessage(waitingMsg, descriptor);\n    }\n\n    @Override\n    public Integer deleteMessageAndDataInstanceOlderThanCreationDate(long creationDate,\n            QueryOptions queryOptions)\n            throws SMessageModificationException {\n\n        try {\n            List<Long> messageInstanceIdOlderThanCreationDate = eventInstanceRepository\n                    .getMessageInstanceIdOlderThanCreationDate(creationDate, queryOptions);\n            if (messageInstanceIdOlderThanCreationDate.size() > 0) {\n                eventInstanceRepository.deleteMessageInstanceByIds(messageInstanceIdOlderThanCreationDate);\n                for (Long messageId : messageInstanceIdOlderThanCreationDate) {\n                    dataInstanceService.deleteLocalDataInstances(messageId,\n                            DataInstanceContainer.MESSAGE_INSTANCE.name(),\n                            true);\n                    dataInstanceService.deleteLocalArchivedDataInstances(messageId,\n                            DataInstanceContainer.MESSAGE_INSTANCE.name());\n                }\n            }\n            return messageInstanceIdOlderThanCreationDate.size();\n        } catch (SDataInstanceException | SEventTriggerInstanceReadException | SMessageInstanceReadException e) {\n            throw new SMessageModificationException(e);\n        }\n    }\n\n    @Override\n    public List<Long> getMessageInstanceIdOlderThanCreationDate(long creationDate, QueryOptions queryOptions)\n            throws SEventTriggerInstanceReadException, SMessageInstanceReadException {\n        return this.eventInstanceRepository.getMessageInstanceIdOlderThanCreationDate(creationDate, queryOptions);\n    }\n\n    @Override\n    public void updateEventTriggerInstance(STimerEventTriggerInstance sTimerEventTriggerInstance,\n            EntityUpdateDescriptor descriptor) throws SEventTriggerInstanceModificationException {\n        this.eventInstanceRepository.updateEventTriggerInstance(sTimerEventTriggerInstance, descriptor);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/impl/ActivityInstanceServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.impl;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.archive.ArchiveService;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.CollectionUtil;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityCreationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityModificationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeModificationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.business.SHumanTaskAlreadyAssignedException;\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SMultiInstanceActivityInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SUserTaskInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.recorder.SelectDescriptorBuilder;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.persistence.search.FilterOperationType;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteAllRecord;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.bonitasoft.engine.services.PersistenceService;\nimport org.bonitasoft.engine.services.SPersistenceException;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @author Hongwen Zang\n * @author Emmanuel Duchastenier\n * @author Yanyan Liu\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\n@Slf4j\npublic class ActivityInstanceServiceImpl extends FlowNodeInstancesServiceImpl implements ActivityInstanceService {\n\n    private static final String ASSIGNED_AND_PENDING_BY_ROOT_PROCESS = \"AssignedAndPendingByRootProcess\";\n\n    private static final String ASSIGNED_AND_PENDING_BY_ROOT_PROCESS_FOR = \"AssignedAndPendingByRootProcessFor\";\n\n    private static final String ASSIGNED_AND_PENDING = \"AssignedAndPending\";\n\n    private static final String SUPERVISED_BY = \"SupervisedBy\";\n\n    private static final String MANAGED_BY = \"ManagedBy\";\n\n    private static final String PENDING_MANAGED_BY = \"PendingManagedBy\";\n\n    private static final String PENDING_SUPERVISED_BY = \"PendingSupervisedBy\";\n\n    private static final String PENDING_FOR_USER = \"PendingForUser\";\n\n    private static final String PENDING_OR_ASSIGNED = \"PendingOrAssigned\";\n\n    private static final String PENDING_OR_ASSIGNED_OR_ASSIGNED_TO_OTHERS = \"PendingOrAssignedOrAssignedToOthers\";\n\n    private static final String PENDING_ASSIGNED_TO = \"PendingAssignedTo\";\n\n    private static final String HUMAN_TASK_INSTANCE_ASSIGNEE = \"HUMAN_TASK_INSTANCE_ASSIGNEE\";\n\n    private static final String QUERY_HUMAN_TASK_INSTANCE_ASSIGNEE = \"updateStrictHuman\";\n\n    private static final String WHOCANSTART_PENDING_TASK_SUFFIX = \"WhoCanStartPendingTask\";\n\n    private static final int BATCH_SIZE = 100;\n\n    private final SUserTaskInstanceBuilderFactory sUserTaskInstanceBuilder;\n\n    private final SMultiInstanceActivityInstanceBuilderFactory sMultiInstanceActivityInstanceBuilder;\n\n    public ActivityInstanceServiceImpl(final Recorder recorder, final PersistenceService persistenceService,\n            final ArchiveService archiveService) {\n        super(recorder, persistenceService, archiveService);\n        sUserTaskInstanceBuilder = BuilderFactory.get(SUserTaskInstanceBuilderFactory.class);\n        sMultiInstanceActivityInstanceBuilder = BuilderFactory.get(SMultiInstanceActivityInstanceBuilderFactory.class);\n    }\n\n    @Override\n    public void createActivityInstance(final SActivityInstance activityInstance) throws SActivityCreationException {\n        try {\n            getRecorder().recordInsert(new InsertRecord(activityInstance), ACTIVITYINSTANCE);\n        } catch (final SRecorderException e) {\n            throw new SActivityCreationException(e);\n        }\n        if (log.isDebugEnabled()) {\n            final StringBuilder stb = new StringBuilder();\n            stb.append(\"Created \");\n            stb.append(activityInstance.getType().getValue());\n            stb.append(\" <\");\n            stb.append(activityInstance.getName());\n            stb.append(\"> with id = <\");\n            stb.append(activityInstance.getId());\n            if (activityInstance.getParentActivityInstanceId() > 0) {\n                stb.append(\">, parent activity instance id = <\");\n                stb.append(activityInstance.getParentActivityInstanceId());\n            }\n            stb.append(\">, parent process instance id = <\");\n            stb.append(activityInstance.getParentProcessInstanceId());\n            stb.append(\">, root process instance id = <\");\n            stb.append(activityInstance.getRootProcessInstanceId());\n            stb.append(\">, process definition id = <\");\n            stb.append(activityInstance.getProcessDefinitionId());\n            stb.append(\">\");\n            log.debug(stb.toString());\n        }\n    }\n\n    @Override\n    public void addPendingActivityMappings(final SPendingActivityMapping mapping) throws SActivityCreationException {\n        try {\n            getRecorder().recordInsert(new InsertRecord(mapping), PENDINGACTIVITYMAPPING);\n        } catch (final SRecorderException e) {\n            throw new SActivityCreationException(e);\n        }\n    }\n\n    @Override\n    public void deletePendingMappings(final long humanTaskInstanceId) throws SActivityModificationException {\n        try {\n            List<SPendingActivityMapping> mappings = null;\n            final QueryOptions queryOptions = new QueryOptions(0, BATCH_SIZE, SPendingActivityMapping.class, \"id\",\n                    OrderByType.ASC);\n            while (!(mappings = getPendingMappings(humanTaskInstanceId, queryOptions)).isEmpty()) {\n                deletePendingMappings(mappings);\n            }\n        } catch (final SBonitaException e) {\n            throw new SActivityModificationException(e);\n        }\n    }\n\n    private void deletePendingMappings(final List<SPendingActivityMapping> mappings) throws SRecorderException {\n        for (final SPendingActivityMapping mapping : mappings) {\n            getRecorder().recordDelete(new DeleteRecord(mapping), PENDINGACTIVITYMAPPING);\n        }\n    }\n\n    @Override\n    public void deleteAllPendingMappings() throws SActivityModificationException {\n        try {\n            final FilterOption filterOption = new FilterOption(SPendingActivityMapping.class,\n                    SPendingActivityMapping.ACTOR_ID, -1L);\n            final DeleteAllRecord record = new DeleteAllRecord(SPendingActivityMapping.class,\n                    Collections.singletonList(filterOption));\n            getRecorder().recordDeleteAll(record);\n        } catch (final SRecorderException e) {\n            throw new SActivityModificationException(\"Can't delete all pending mappings not attached to an actor.\", e);\n        }\n    }\n\n    /**\n     * @param humanTaskInstanceId\n     * @param queryOptions\n     * @return\n     */\n    @Override\n    public List<SPendingActivityMapping> getPendingMappings(final long humanTaskInstanceId,\n            final QueryOptions queryOptions) throws SBonitaReadException {\n        final Map<String, Object> parameters = CollectionUtil.buildSimpleMap(\"activityId\", humanTaskInstanceId);\n        return getPersistenceService().selectList(\n                new SelectListDescriptor<SPendingActivityMapping>(\"getPendingMappingsOfTask\", parameters,\n                        SPendingActivityMapping.class, queryOptions));\n    }\n\n    @Override\n    public SActivityInstance getActivityInstance(final long activityInstanceId)\n            throws SActivityInstanceNotFoundException, SActivityReadException {\n        try {\n            final SActivityInstance activity = getPersistenceService().selectById(\n                    SelectDescriptorBuilder.getElementById(SActivityInstance.class, \"SActivityInstance\",\n                            activityInstanceId));\n            if (activity == null) {\n                throw new SActivityInstanceNotFoundException(activityInstanceId);\n            }\n            return activity;\n        } catch (final SBonitaReadException e) {\n            throw new SActivityReadException(e);\n        }\n    }\n\n    @Override\n    public SHumanTaskInstance getHumanTaskInstance(final long activityInstanceId)\n            throws SActivityInstanceNotFoundException, SActivityReadException {\n        final SelectByIdDescriptor<SHumanTaskInstance> descriptor = SelectDescriptorBuilder.getElementById(\n                SHumanTaskInstance.class, \"SHumanTaskInstance\",\n                activityInstanceId);\n        SHumanTaskInstance humanTask;\n        try {\n            humanTask = getPersistenceService().selectById(descriptor);\n            if (humanTask == null) {\n                throw new SActivityInstanceNotFoundException(activityInstanceId);\n            }\n            return humanTask;\n        } catch (final SBonitaReadException e) {\n            throw new SActivityReadException(e.getMessage());\n        }\n\n    }\n\n    @Override\n    public List<SActivityInstance> getActivitiesWithStates(final long rootContainerId, final Set<Integer> stateIds,\n            final int fromIndex, final int maxResults,\n            final String sortingField, final OrderByType sortingOrder) throws SActivityReadException {\n        final HashMap<String, Object> parameters = new HashMap<String, Object>();\n        parameters.put(\"rootContainerId\", rootContainerId);\n        parameters.put(\"stateIds\", stateIds);\n        final SelectListDescriptor<SActivityInstance> elements = SelectDescriptorBuilder.getSpecificQueryWithParameters(\n                SActivityInstance.class,\n                \"getActivitiesWithStates\", parameters,\n                new QueryOptions(fromIndex, maxResults, SActivityInstance.class, sortingField, sortingOrder));\n        try {\n            return getPersistenceService().selectList(elements);\n        } catch (final SBonitaReadException e) {\n            throw new SActivityReadException(e);\n        }\n    }\n\n    @Override\n    public List<SActivityInstance> getOpenActivityInstances(final long rootContainerId, final int pageIndex,\n            final int maxResults, final String sortingField,\n            final OrderByType orderbyType) throws SActivityReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"rootContainerId\", rootContainerId);\n        final QueryOptions queryOptions = new QueryOptions(pageIndex * maxResults, maxResults, SActivityInstance.class,\n                sortingField, orderbyType);\n        final SelectListDescriptor<SActivityInstance> elements = SelectDescriptorBuilder.getSpecificQueryWithParameters(\n                SActivityInstance.class,\n                \"getOpenActivitiesFromProcessInstance\", parameters, queryOptions);\n        try {\n            return getPersistenceService().selectList(elements);\n        } catch (final SBonitaReadException e) {\n            throw new SActivityReadException(e);\n        }\n    }\n\n    @Override\n    public SAActivityInstance getMostRecentArchivedActivityInstance(final long activityInstanceId)\n            throws SActivityReadException,\n            SActivityInstanceNotFoundException {\n        final ReadPersistenceService persistenceService = getArchiveService()\n                .getDefinitiveArchiveReadPersistenceService();\n        final SelectOneDescriptor<SAActivityInstance> descriptor = SelectDescriptorBuilder\n                .getMostRecentArchivedActivityInstance(activityInstanceId);\n        try {\n            final SAActivityInstance activity = persistenceService.selectOne(descriptor);\n            if (activity == null) {\n                throw new SActivityInstanceNotFoundException(activityInstanceId);\n            }\n            return activity;\n        } catch (final SBonitaReadException e) {\n            throw new SActivityReadException(e);\n        }\n    }\n\n    @Override\n    public List<SAActivityInstance> getArchivedActivityInstances(final long rootContainerId,\n            final QueryOptions queryOptions) throws SActivityReadException {\n        final ReadPersistenceService persistenceService = getArchiveService()\n                .getDefinitiveArchiveReadPersistenceService();\n        try {\n            final List<SAActivityInstance> activities = persistenceService\n                    .selectList(SelectDescriptorBuilder.getArchivedActivitiesFromProcessInstance(\n                            rootContainerId, queryOptions));\n            return getUnmodifiableList(activities);\n        } catch (final SBonitaReadException e) {\n            throw new SActivityReadException(e);\n        }\n    }\n\n    @Override\n    public List<SHumanTaskInstance> getPendingTasks(final long userId, final Set<Long> actorIds, final int fromIndex,\n            final int maxResults,\n            final String sortFieldName, final OrderByType order) throws SActivityReadException {\n        try {\n            final SelectListDescriptor<SHumanTaskInstance> selectListDescriptor;\n            if (actorIds.isEmpty()) {\n                selectListDescriptor = SelectDescriptorBuilder.getPendingUserTasks(userId, fromIndex, maxResults,\n                        sortFieldName, order);\n            } else {\n                selectListDescriptor = SelectDescriptorBuilder.getPendingUserTasks(userId, actorIds, fromIndex,\n                        maxResults, sortFieldName, order);\n            }\n            return getPersistenceService().selectList(selectListDescriptor);\n        } catch (final SBonitaReadException bre) {\n            throw new SActivityReadException(bre);\n        }\n    }\n\n    @Override\n    public List<SHumanTaskInstance> getAssignedUserTasks(final long assigneeId, final int fromIndex,\n            final int maxResults, final String sortFieldName,\n            final OrderByType order) throws SActivityReadException {\n        try {\n            final SelectListDescriptor<SHumanTaskInstance> selectListDescriptor = SelectDescriptorBuilder\n                    .getAssignedUserTasks(assigneeId, fromIndex,\n                            maxResults, sortFieldName, order);\n            return getPersistenceService().selectList(selectListDescriptor);\n        } catch (final SBonitaReadException bre) {\n            throw new SActivityReadException(bre);\n        }\n    }\n\n    @Override\n    public int getNumberOfOpenActivityInstances(final long rootContainerId) throws SActivityReadException {\n        try {\n            return getPersistenceService().selectOne(SelectDescriptorBuilder.getNumberOfOpenActivities(rootContainerId))\n                    .intValue();\n        } catch (final SBonitaReadException e) {\n            throw new SActivityReadException(e);\n        }\n    }\n\n    @Override\n    public List<SActivityInstance> getActivityInstances(final long rootContainerId, final int fromIndex,\n            final int numberOfResults)\n            throws SActivityReadException {\n        final SelectListDescriptor<SActivityInstance> descriptor = SelectDescriptorBuilder\n                .getActivitiesFromProcessInstance(rootContainerId, fromIndex,\n                        numberOfResults);\n        try {\n            final List<SActivityInstance> selectList = getPersistenceService().selectList(descriptor);\n            return getUnmodifiableList(selectList);\n        } catch (final SBonitaReadException e) {\n            throw new SActivityReadException(e);\n        }\n    }\n\n    @Override\n    public void assignHumanTask(final long userTaskId, final long userId)\n            throws SFlowNodeNotFoundException, SFlowNodeReadException,\n            SActivityModificationException {\n        final SFlowNodeInstance flowNodeInstance = getFlowNodeInstance(userTaskId);\n        if (flowNodeInstance instanceof SHumanTaskInstance) {\n            final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n            descriptor.addField(sUserTaskInstanceBuilder.getAssigneeIdKey(), userId);\n            if (userId > 0) {\n                // if this action is a Assign action:\n                descriptor.addField(sUserTaskInstanceBuilder.getClaimedDateKey(), System.currentTimeMillis());\n            } else {\n                // if this action is a Release action:\n                descriptor.addField(sUserTaskInstanceBuilder.getClaimedDateKey(), 0);\n            }\n            setLastUpdateDate(descriptor);\n            try {\n                getRecorder().recordUpdate(UpdateRecord.buildSetFields(flowNodeInstance, descriptor),\n                        HUMAN_TASK_INSTANCE_ASSIGNEE);\n            } catch (final SRecorderException e) {\n                throw new SActivityModificationException(e);\n            }\n        } else {\n            throw new SActivityReadException(\"the activity with id \" + userTaskId + \" is not a user task\");\n        }\n    }\n\n    @Override\n    public void assignHumanTaskIfNotAssigned(final long userTaskId, final long userId)\n            throws SFlowNodeNotFoundException,\n            SFlowNodeReadException, SActivityModificationException, SHumanTaskAlreadyAssignedException {\n        final SFlowNodeInstance flowNodeInstance = getFlowNodeInstance(userTaskId);\n        if (flowNodeInstance instanceof SHumanTaskInstance) {\n            long assigneeId = ((SHumanTaskInstance) flowNodeInstance).getAssigneeId();\n            if (assigneeId > 0 && assigneeId != userId && userId > 0) {\n                throw new SHumanTaskAlreadyAssignedException(\n                        \"The task with id \" + userTaskId + \" is currently assigned to\" +\n                                \" user with id \" + assigneeId + \". Try to unassign before assigning it again.\",\n                        null);\n            }\n            final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n            descriptor.addField(sUserTaskInstanceBuilder.getIdKey(), userTaskId);\n            descriptor.addField(sUserTaskInstanceBuilder.getAssigneeIdKey(), userId);\n            if (userId > 0) {\n                // if this action is a Assign action:\n                descriptor.addField(sUserTaskInstanceBuilder.getClaimedDateKey(), System.currentTimeMillis());\n            } else {\n                // if this action is a Release action:\n                descriptor.addField(sUserTaskInstanceBuilder.getClaimedDateKey(), 0L);\n            }\n            setLastUpdateDate(descriptor);\n            try {\n                int updatedRows = getRecorder().recordUpdateWithQuery(\n                        UpdateRecord.buildSetFields(flowNodeInstance, descriptor),\n                        HUMAN_TASK_INSTANCE_ASSIGNEE, QUERY_HUMAN_TASK_INSTANCE_ASSIGNEE);\n                if (updatedRows != 1) {\n                    throw new SHumanTaskAlreadyAssignedException(\n                            \"The task with id \" + userTaskId + \" is currently assigned.\" +\n                                    \" Try to unassign before assigning it again.\",\n                            null);\n                }\n            } catch (final SRecorderException e) {\n                throw new SActivityModificationException(e);\n            }\n        } else {\n            throw new SActivityReadException(\"the activity with id \" + userTaskId + \" is not a user task\");\n        }\n    }\n\n    @Override\n    public long getNumberOfAssignedHumanTaskInstances(final long userId) throws SActivityReadException {\n        try {\n            return getPersistenceService()\n                    .selectOne(SelectDescriptorBuilder.getNumberOfAssignedHumanTaskInstances(userId));\n        } catch (final SBonitaReadException e) {\n            throw new SActivityReadException(e);\n        }\n    }\n\n    @Override\n    public SAActivityInstance getArchivedActivityInstance(final long activityInstanceId, final int stateId)\n            throws SActivityReadException,\n            SActivityInstanceNotFoundException {\n        final ReadPersistenceService persistenceService = getArchiveService()\n                .getDefinitiveArchiveReadPersistenceService();\n        SAActivityInstance selectOne;\n        try {\n            selectOne = persistenceService.selectOne(SelectDescriptorBuilder\n                    .getArchivedActivityInstanceWithActivityIdAndStateId(activityInstanceId, stateId));\n        } catch (final SBonitaReadException e) {\n            throw new SActivityReadException(e);\n        }\n        if (selectOne == null) {\n            throw new SActivityInstanceNotFoundException(activityInstanceId, stateId);\n        }\n        return selectOne;\n    }\n\n    @Override\n    public long getNumberOfArchivedTasksManagedBy(final long managerUserId, final QueryOptions searchOptions)\n            throws SBonitaReadException {\n        final ReadPersistenceService persistenceService = getArchiveService()\n                .getDefinitiveArchiveReadPersistenceService();\n        final Map<String, Object> parameters = Collections.singletonMap(\"managerUserId\", managerUserId);\n        return persistenceService.getNumberOfEntities(SAHumanTaskInstance.class, MANAGED_BY, searchOptions, parameters);\n    }\n\n    @Override\n    public List<SAHumanTaskInstance> searchArchivedTasksManagedBy(final long managerUserId,\n            final QueryOptions searchOptions) throws SBonitaReadException {\n        final ReadPersistenceService persistenceService = getArchiveService()\n                .getDefinitiveArchiveReadPersistenceService();\n        final Map<String, Object> parameters = Collections.singletonMap(\"managerUserId\", managerUserId);\n        return persistenceService.searchEntity(SAHumanTaskInstance.class, MANAGED_BY, searchOptions, parameters);\n    }\n\n    @Override\n    public long getNumberOfArchivedHumanTasksSupervisedBy(final long supervisorId, final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"supervisorId\", supervisorId);\n        return getPersistenceService().getNumberOfEntities(SAHumanTaskInstance.class, SUPERVISED_BY, queryOptions,\n                parameters);\n    }\n\n    @Override\n    public long getNumberOfAssignedTasksSupervisedBy(final long supervisorId, final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"supervisorId\", supervisorId);\n        queryOptions.getFilters()\n                .add(new FilterOption(SHumanTaskInstance.class, \"assigneeId\", 0, FilterOperationType.GREATER));\n        return getPersistenceService().getNumberOfEntities(SHumanTaskInstance.class, SUPERVISED_BY, queryOptions,\n                parameters);\n    }\n\n    @Override\n    public List<SHumanTaskInstance> searchAssignedTasksSupervisedBy(final long supervisorId,\n            final QueryOptions queryOptions) throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"supervisorId\", supervisorId);\n        queryOptions.getFilters()\n                .add(new FilterOption(SHumanTaskInstance.class, \"assigneeId\", 0, FilterOperationType.GREATER));\n        return getPersistenceService().searchEntity(SHumanTaskInstance.class, SUPERVISED_BY, queryOptions, parameters);\n    }\n\n    @Override\n    public long getNumberOfHumanTasks(final QueryOptions queryOptions) throws SBonitaReadException {\n        return getPersistenceService().getNumberOfEntities(SHumanTaskInstance.class, queryOptions, null);\n    }\n\n    @Override\n    public List<SHumanTaskInstance> searchHumanTasks(final QueryOptions queryOptions) throws SBonitaReadException {\n        return getPersistenceService().searchEntity(SHumanTaskInstance.class, queryOptions, null);\n    }\n\n    @Override\n    public List<SAHumanTaskInstance> searchArchivedHumanTasksSupervisedBy(final long supervisorId,\n            final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"supervisorId\", supervisorId);\n        return getPersistenceService().searchEntity(SAHumanTaskInstance.class, SUPERVISED_BY, queryOptions, parameters);\n    }\n\n    @Override\n    public List<SAHumanTaskInstance> searchArchivedTasks(final QueryOptions searchOptions) throws SBonitaReadException {\n        final ReadPersistenceService persistenceService = getArchiveService()\n                .getDefinitiveArchiveReadPersistenceService();\n        return persistenceService.searchEntity(SAHumanTaskInstance.class, searchOptions, null);\n    }\n\n    @Override\n    public long getNumberOfArchivedTasks(final QueryOptions searchOptions) throws SBonitaReadException {\n        final ReadPersistenceService persistenceService = getArchiveService()\n                .getDefinitiveArchiveReadPersistenceService();\n        return persistenceService.getNumberOfEntities(SAHumanTaskInstance.class, searchOptions, null);\n    }\n\n    @Override\n    public long getNumberOfAssignedTasksManagedBy(final long managerUserId, final QueryOptions searchOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"managerUserId\", managerUserId);\n        return getPersistenceService().getNumberOfEntities(SHumanTaskInstance.class, MANAGED_BY, searchOptions,\n                parameters);\n    }\n\n    @Override\n    public List<SHumanTaskInstance> searchAssignedTasksManagedBy(final long managerUserId,\n            final QueryOptions searchOptions) throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"managerUserId\", managerUserId);\n        return getPersistenceService().searchEntity(SHumanTaskInstance.class, MANAGED_BY, searchOptions, parameters);\n    }\n\n    @Override\n    public List<SHumanTaskInstance> searchPendingTasksSupervisedBy(final long supervisorId,\n            final QueryOptions queryOptions) throws SBonitaReadException {\n        try {\n            final Map<String, Object> parameters = Collections.singletonMap(\"userId\", supervisorId);\n            return getPersistenceService().searchEntity(SHumanTaskInstance.class, PENDING_SUPERVISED_BY, queryOptions,\n                    parameters);\n        } catch (final SBonitaReadException bre) {\n            throw new SBonitaReadException(bre);\n        }\n    }\n\n    @Override\n    public long getNumberOfPendingTasksSupervisedBy(final long supervisorId, final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"userId\", supervisorId);\n        return getPersistenceService().getNumberOfEntities(SHumanTaskInstance.class, PENDING_SUPERVISED_BY,\n                queryOptions, parameters);\n    }\n\n    @Override\n    public Map<Long, Long> getNumberOfOpenTasksForUsers(final List<Long> userIds) throws SBonitaReadException {\n        if (userIds == null || userIds.isEmpty()) {\n            return Collections.emptyMap();\n        }\n        // get assigned tasks for each user\n        final List<Map<String, Long>> result = getPersistenceService()\n                .selectList(SelectDescriptorBuilder.getNumbersOfAssignedOpenTasks(userIds));\n        final Map<Long, Long> userTaskNumbermap = new HashMap<Long, Long>();\n        for (final Map<String, Long> record : result) {\n            userTaskNumbermap.put(record.get(\"userId\"), record.get(\"numberOfTasks\")); // \"userId\" and \"numberOfTasks\" are embed in mybatis/hibernate query\n                                                                                      // statements named \"getNumbersOfOpenTasksForUsers\"\n        }\n        // get number of pending tasks for each user\n        for (final Long userId : userIds) {\n            final long pendingCount = getNumberOfPendingTasksForUser(userId,\n                    new QueryOptions(0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS));\n            if (!userTaskNumbermap.containsKey(userId)) {\n                userTaskNumbermap.put(userId, pendingCount);\n            } else {\n                userTaskNumbermap.put(userId, userTaskNumbermap.get(userId) + pendingCount);\n            }\n        }\n        return userTaskNumbermap;\n    }\n\n    @Override\n    public long searchNumberOfPendingTasksManagedBy(final long managerUserId, final QueryOptions searchOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"managerUserId\", managerUserId);\n        return getPersistenceService().getNumberOfEntities(SHumanTaskInstance.class, PENDING_MANAGED_BY, searchOptions,\n                parameters);\n    }\n\n    @Override\n    public List<SHumanTaskInstance> searchPendingTasksManagedBy(final long managerUserId,\n            final QueryOptions searchOptions) throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"managerUserId\", managerUserId);\n        return getPersistenceService().searchEntity(SHumanTaskInstance.class, PENDING_MANAGED_BY, searchOptions,\n                parameters);\n    }\n\n    // FIXME synchronized on the object\n    @Override\n    public void incrementLoopCounter(final SLoopActivityInstance loopInstance) throws SActivityModificationException {\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        descriptor.addField(\"loopCounter\", loopInstance.getLoopCounter() + 1);\n        setLastUpdateDate(descriptor);\n        try {\n            getRecorder().recordUpdate(UpdateRecord.buildSetFields(loopInstance, descriptor), ACTIVITYINSTANCE_STATE);\n        } catch (final SRecorderException sre) {\n            throw new SActivityModificationException(sre);\n        }\n    }\n\n    @Override\n    public Map<Long, Long> getNumberOfOverdueOpenTasksForUsers(final List<Long> userIds) throws SBonitaReadException {\n        if (userIds == null || userIds.isEmpty()) {\n            return Collections.emptyMap();\n        }\n        // get assigned overdue open tasks for each user\n        final List<Map<Long, Long>> result = getPersistenceService()\n                .selectList(SelectDescriptorBuilder.getNumbersOfAssignedOverdueOpenTasks(userIds));\n        final Map<Long, Long> userTaskNumbermap = new HashMap<Long, Long>();\n        for (final Map<Long, Long> record : result) {\n            userTaskNumbermap.put(record.get(\"userId\"), record.get(\"numberOfTasks\")); // \"userId\" and \"numberOfTasks\" are embed in mybatis/hibernate query\n                                                                                      // statements named \"getNumbersOfOpenTasksForUsers\"\n        }\n        // get number of pending overdue open tasks for each user\n        for (final Long userId : userIds) {\n            final long pendingCount = getPersistenceService()\n                    .selectOne(SelectDescriptorBuilder.getNumberOfPendingOverdueOpenTasksForUser(userId));\n            if (!userTaskNumbermap.containsKey(userId)) {\n                userTaskNumbermap.put(userId, pendingCount);\n            } else {\n                userTaskNumbermap.put(userId, userTaskNumbermap.get(userId) + pendingCount);\n            }\n        }\n        return userTaskNumbermap;\n    }\n\n    @Override\n    public List<SActivityInstance> getChildrenOfAnActivity(final long parentActivityInstanceId, final int fromIndex,\n            final int numberOfResults)\n            throws SActivityReadException {\n        final HashMap<String, Object> parameters = new HashMap<String, Object>();\n        parameters.put(\"parentActivityInstanceId\", parentActivityInstanceId);\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfResults, SFlowNodeInstance.class, \"id\",\n                OrderByType.ASC);\n        final SelectListDescriptor<SActivityInstance> descriptor = new SelectListDescriptor<SActivityInstance>(\n                \"getChildrenOfAnActivity\", parameters,\n                SActivityInstance.class, queryOptions);\n        try {\n            return getPersistenceService().selectList(descriptor);\n        } catch (final SBonitaReadException e) {\n            throw new SActivityReadException(e);\n        }\n    }\n\n    @Override\n    public void setLoopMax(final SLoopActivityInstance loopActivity, final Integer loopMap)\n            throws SActivityModificationException {\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        descriptor.addField(\"loopMax\", loopMap);\n        try {\n            updateFlowNode(loopActivity, LOOPINSTANCE_LOOPMAX_MODIFIED, descriptor);\n        } catch (final SFlowNodeModificationException e) {\n            throw new SActivityModificationException(e);\n        }\n    }\n\n    @Override\n    public void setLoopCardinality(final SFlowNodeInstance flowNodeInstance, final int intLoopCardinality)\n            throws SActivityModificationException {\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        descriptor.addField(sMultiInstanceActivityInstanceBuilder.getLoopCardinalityKey(), intLoopCardinality);\n        try {\n            updateFlowNode(flowNodeInstance, MULTIINSTANCE_LOOPCARDINALITY_MODIFIED, descriptor);\n        } catch (final SFlowNodeModificationException e) {\n            throw new SActivityModificationException(e);\n        }\n    }\n\n    @Override\n    public void addMultiInstanceNumberOfActiveActivities(final SMultiInstanceActivityInstance flowNodeInstance,\n            final int number)\n            throws SActivityModificationException {\n        updateMultiInstanceCounters(flowNodeInstance, number,\n                \"updateMultiInstanceActiveCounters\", \"active\");\n    }\n\n    @Override\n    public void addMultiInstanceNumberOfTerminatedActivities(final SMultiInstanceActivityInstance flowNodeInstance,\n            final int number)\n            throws SActivityModificationException {\n        updateMultiInstanceCounters(flowNodeInstance, number,\n                \"updateMultiInstanceTerminatedCounters\", \"terminated\");\n    }\n\n    @Override\n    public void addMultiInstanceNumberOfCompletedActivities(final SMultiInstanceActivityInstance flowNodeInstance,\n            final int number)\n            throws SActivityModificationException {\n        updateMultiInstanceCounters(flowNodeInstance, number,\n                \"updateMultiInstanceCompletedCounters\", \"completed\");\n    }\n\n    private void updateMultiInstanceCounters(final SMultiInstanceActivityInstance flowNodeInstance,\n            final int number, final String queryName, final String counterDescription)\n            throws SActivityModificationException {\n        final long now = System.currentTimeMillis();\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        descriptor.addField(\"id\", flowNodeInstance.getId());\n        descriptor.addField(\"number\", number);\n        descriptor.addField(\"lastUpdateDate\", now);\n        log.debug(\"Multi-instance '{}' (id={}, processInstance={}, processDefinition={}): \"\n                + \"updating {} counters (atomic SQL, number={})\",\n                flowNodeInstance.getName(), flowNodeInstance.getId(),\n                flowNodeInstance.getRootProcessInstanceId(), flowNodeInstance.getProcessDefinitionId(),\n                counterDescription, number);\n        try {\n            final int updatedRows = getRecorder().recordUpdateWithQuery(\n                    UpdateRecord.buildSetFields(flowNodeInstance, descriptor),\n                    MULTIINSTANCE_NUMBEROFINSTANCE_MODIFIED, queryName);\n            if (updatedRows != 1) {\n                log.warn(\"Multi-instance '{}' (id={}, processInstance={}, processDefinition={}): \"\n                        + \"failed to update {} counters — entity not found or counter precondition failed \"\n                        + \"(expected 1 updated row but got {})\",\n                        flowNodeInstance.getName(), flowNodeInstance.getId(),\n                        flowNodeInstance.getRootProcessInstanceId(), flowNodeInstance.getProcessDefinitionId(),\n                        counterDescription, updatedRows);\n                throw new SActivityModificationException(\n                        String.format(\"Failed to update multi-instance %s counters for flow node %d: \"\n                                + \"entity not found or counter precondition failed (updated rows: %d)\",\n                                counterDescription, flowNodeInstance.getId(), updatedRows));\n            }\n            // Refresh entity from DB to sync in-memory state with the atomic SQL update.\n            // This prevents Hibernate dirty-checking from overwriting the atomic values,\n            // and keeps the entity managed in the session for downstream operations.\n            getPersistenceService().refresh(flowNodeInstance);\n        } catch (final SRecorderException | SPersistenceException e) {\n            log.warn(\"Multi-instance '{}' (id={}, processInstance={}, processDefinition={}): \"\n                    + \"failed to update {} counters — {}\",\n                    flowNodeInstance.getName(), flowNodeInstance.getId(),\n                    flowNodeInstance.getRootProcessInstanceId(), flowNodeInstance.getProcessDefinitionId(),\n                    counterDescription, e.getMessage(), e);\n            throw new SActivityModificationException(e);\n        }\n    }\n\n    @Override\n    public long getNumberOfActivityInstances(final Class<? extends PersistentObject> entityClass,\n            final QueryOptions searchOptions)\n            throws SBonitaReadException {\n        return getPersistenceService().getNumberOfEntities(entityClass, searchOptions, null);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public List<SActivityInstance> searchActivityInstances(final Class<? extends PersistentObject> entityClass,\n            final QueryOptions searchOptions)\n            throws SBonitaReadException {\n        return (List<SActivityInstance>) getPersistenceService().searchEntity(entityClass, searchOptions, null);\n    }\n\n    @Override\n    public long getNumberOfArchivedActivityInstances(final Class<? extends PersistentObject> entityClass,\n            final QueryOptions searchOptions)\n            throws SBonitaReadException {\n        final ReadPersistenceService persistenceService = getArchiveService()\n                .getDefinitiveArchiveReadPersistenceService();\n        return persistenceService.getNumberOfEntities(entityClass, searchOptions, null);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public List<SAActivityInstance> searchArchivedActivityInstances(\n            final Class<? extends PersistentObject> entityClass,\n            final QueryOptions searchOptions)\n            throws SBonitaReadException {\n        final ReadPersistenceService persistenceService = getArchiveService()\n                .getDefinitiveArchiveReadPersistenceService();\n        return (List<SAActivityInstance>) persistenceService.searchEntity(entityClass, searchOptions, null);\n    }\n\n    @Override\n    public void setTokenCount(final SActivityInstance activityInstance, final int tokenCount)\n            throws SFlowNodeModificationException {\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        descriptor.addField(sUserTaskInstanceBuilder.getTokenCountKey(), tokenCount);\n        setLastUpdateDate(descriptor);\n        try {\n            getRecorder().recordUpdate(UpdateRecord.buildSetFields(activityInstance, descriptor),\n                    ACTIVITY_INSTANCE_TOKEN_COUNT);\n        } catch (final SRecorderException e) {\n            throw new SFlowNodeModificationException(e);\n        }\n    }\n\n    @Override\n    public long getNumberOfPendingTasksForUser(final long userId, final QueryOptions searchOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"userId\", userId);\n        return getPersistenceService().getNumberOfEntities(SHumanTaskInstance.class, PENDING_FOR_USER, searchOptions,\n                parameters);\n    }\n\n    @Override\n    public List<SHumanTaskInstance> searchPendingTasksForUser(final long userId, final QueryOptions searchOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"userId\", userId);\n        return getPersistenceService().searchEntity(SHumanTaskInstance.class, PENDING_FOR_USER, searchOptions,\n                parameters);\n    }\n\n    @Override\n    public List<SHumanTaskInstance> searchPendingTasksAssignedTo(long userId, QueryOptions searchOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"userId\", userId);\n        return getPersistenceService().searchEntity(SHumanTaskInstance.class, PENDING_ASSIGNED_TO, searchOptions,\n                parameters);\n    }\n\n    @Override\n    public long getNumberOfPendingOrAssignedTasks(final long userId, final QueryOptions searchOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"userId\", userId);\n        return getPersistenceService().getNumberOfEntities(SHumanTaskInstance.class, PENDING_OR_ASSIGNED, searchOptions,\n                parameters);\n    }\n\n    @Override\n    public long getNumberOfPendingOrAssignedOrAssignedToOthersTasks(final long userId, final QueryOptions searchOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"userId\", userId);\n        return getPersistenceService().getNumberOfEntities(SHumanTaskInstance.class,\n                PENDING_OR_ASSIGNED_OR_ASSIGNED_TO_OTHERS,\n                searchOptions,\n                parameters);\n    }\n\n    @Override\n    public long getNumberOfPendingTasksAssignedTo(long userId, QueryOptions searchOptions) throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"userId\", userId);\n        return getPersistenceService().getNumberOfEntities(SHumanTaskInstance.class, PENDING_ASSIGNED_TO, searchOptions,\n                parameters);\n    }\n\n    @Override\n    public List<SHumanTaskInstance> searchPendingOrAssignedTasks(final long userId, final QueryOptions searchOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"userId\", userId);\n        return getPersistenceService().searchEntity(SHumanTaskInstance.class, PENDING_OR_ASSIGNED, searchOptions,\n                parameters);\n    }\n\n    @Override\n    public List<SHumanTaskInstance> searchPendingOrAssignedOrAssignedToOthersTasks(final long userId,\n            final QueryOptions searchOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"userId\", userId);\n        return getPersistenceService().searchEntity(SHumanTaskInstance.class, PENDING_OR_ASSIGNED_OR_ASSIGNED_TO_OTHERS,\n                searchOptions,\n                parameters);\n    }\n\n    @Override\n    public void setAbortedByBoundaryEvent(final SActivityInstance activityInstance, final long boundaryEventId)\n            throws SActivityModificationException {\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        descriptor.addField(sUserTaskInstanceBuilder.getAbortedByBoundaryEventIdKey(), boundaryEventId);\n        setLastUpdateDate(descriptor);\n        try {\n            getRecorder().recordUpdate(UpdateRecord.buildSetFields(activityInstance, descriptor), STATE_CATEGORY);\n        } catch (final SRecorderException sre) {\n            throw new SActivityModificationException(sre);\n        }\n    }\n\n    @Override\n    public List<Long> getPossibleUserIdsOfPendingTasks(final long humanTaskInstanceId, final int startIndex,\n            final int maxResults)\n            throws SActivityReadException {\n        final Map<String, Object> parameters = new HashMap<String, Object>();\n        parameters.put(\"humanTaskInstanceId\", humanTaskInstanceId);\n        final QueryOptions queryOptions = new QueryOptions(startIndex, maxResults);\n        final SelectListDescriptor<Long> elements = new SelectListDescriptor<Long>(\"getPossibleUserIdsOfPendingTasks\",\n                parameters, SActivityInstance.class,\n                queryOptions);\n        try {\n            return getPersistenceService().selectList(elements);\n        } catch (final SBonitaReadException e) {\n            throw new SActivityReadException(e);\n        }\n    }\n\n    @Override\n    public boolean isTaskPendingForUser(final long humanTaskInstanceId, final long userId) throws SBonitaReadException {\n        final Map<String, Object> parameters = new HashMap<String, Object>();\n        parameters.put(\"humanTaskInstanceId\", humanTaskInstanceId);\n        parameters.put(\"userId\", userId);\n        final SelectOneDescriptor<Long> elements = new SelectOneDescriptor<Long>(\"isTaskPendingForUser\", parameters,\n                SActivityInstance.class);\n        Long aLong = getPersistenceService().selectOne(elements);\n        return aLong == 1;\n    }\n\n    @Override\n    public long getNumberOfUsersWhoCanExecutePendingHumanTaskDeploymentInfo(final long humanTaskInstanceId,\n            final QueryOptions searchOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"humanTaskInstanceId\", humanTaskInstanceId);\n        return getPersistenceService().getNumberOfEntities(SUser.class, WHOCANSTART_PENDING_TASK_SUFFIX, searchOptions,\n                parameters);\n    }\n\n    @Override\n    public List<SUser> searchUsersWhoCanExecutePendingHumanTaskDeploymentInfo(final long humanTaskInstanceId,\n            final QueryOptions searchOptions)\n            throws SBonitaReadException {\n        try {\n            final Map<String, Object> parameters = Collections.singletonMap(\"humanTaskInstanceId\", humanTaskInstanceId);\n            return getPersistenceService().searchEntity(SUser.class, WHOCANSTART_PENDING_TASK_SUFFIX, searchOptions,\n                    parameters);\n        } catch (final SBonitaReadException bre) {\n            throw new SBonitaReadException(bre);\n        }\n    }\n\n    @Override\n    public long getNumberOfAssignedAndPendingHumanTasksFor(final long rootProcessDefinitionId, final long userId,\n            final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = new HashMap<String, Object>();\n        parameters.put(\"userId\", userId);\n        parameters.put(\"rootProcessDefinitionId\", rootProcessDefinitionId);\n        return getPersistenceService().getNumberOfEntities(SHumanTaskInstance.class,\n                ASSIGNED_AND_PENDING_BY_ROOT_PROCESS_FOR, queryOptions, parameters);\n    }\n\n    @Override\n    public List<SHumanTaskInstance> searchAssignedAndPendingHumanTasksFor(final long rootProcessDefinitionId,\n            final long userId, final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = new HashMap<String, Object>();\n        parameters.put(\"userId\", userId);\n        parameters.put(\"rootProcessDefinitionId\", rootProcessDefinitionId);\n        return getPersistenceService().searchEntity(SHumanTaskInstance.class, ASSIGNED_AND_PENDING_BY_ROOT_PROCESS_FOR,\n                queryOptions, parameters);\n    }\n\n    @Override\n    public long getNumberOfAssignedAndPendingHumanTasks(final long rootProcessDefinitionId,\n            final QueryOptions queryOptions) throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"rootProcessDefinitionId\",\n                rootProcessDefinitionId);\n        return getPersistenceService().getNumberOfEntities(SHumanTaskInstance.class,\n                ASSIGNED_AND_PENDING_BY_ROOT_PROCESS, queryOptions, parameters);\n    }\n\n    @Override\n    public List<SHumanTaskInstance> searchAssignedAndPendingHumanTasks(final long rootProcessDefinitionId,\n            final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"rootProcessDefinitionId\",\n                rootProcessDefinitionId);\n        return getPersistenceService().searchEntity(SHumanTaskInstance.class, ASSIGNED_AND_PENDING_BY_ROOT_PROCESS,\n                queryOptions, parameters);\n    }\n\n    @Override\n    public long getNumberOfAssignedAndPendingHumanTasks(final QueryOptions queryOptions) throws SBonitaReadException {\n        return getPersistenceService().getNumberOfEntities(SHumanTaskInstance.class, ASSIGNED_AND_PENDING, queryOptions,\n                Collections.emptyMap());\n    }\n\n    @Override\n    public List<SHumanTaskInstance> searchAssignedAndPendingHumanTasks(final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        return getPersistenceService().searchEntity(SHumanTaskInstance.class, ASSIGNED_AND_PENDING, queryOptions,\n                Collections.emptyMap());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/impl/BPMFailureServiceImpl.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.impl;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.TreeMap;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.exception.ExceptionUtils;\nimport org.bonitasoft.engine.archive.ArchiveInsertRecord;\nimport org.bonitasoft.engine.archive.ArchiveService;\nimport org.bonitasoft.engine.commons.exceptions.ExceptionContext;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SExceptionContext;\nimport org.bonitasoft.engine.connector.ConnectorValidationException;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.BPMFailureService;\nimport org.bonitasoft.engine.core.process.instance.model.SABPMFailure;\nimport org.bonitasoft.engine.core.process.instance.model.SBPMFailure;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.services.PersistenceService;\nimport org.bonitasoft.engine.services.SPersistenceException;\nimport org.springframework.stereotype.Service;\n\n@Slf4j\n@Service\npublic class BPMFailureServiceImpl implements BPMFailureService {\n\n    private static final String TYPE_SEPARATOR = \"::\";\n    private static final String CONTEXT_PATH_SEPARATOR = \"//\";\n\n    private final PersistenceService persistenceService;\n    private final ArchiveService archiveService;\n\n    public BPMFailureServiceImpl(PersistenceService persistenceService, ArchiveService archiveService) {\n        this.persistenceService = persistenceService;\n        this.archiveService = archiveService;\n    }\n\n    @Override\n    public SBPMFailure createFlowNodeFailure(SFlowNodeInstance flowNodeInstance, Failure failure)\n            throws SPersistenceException {\n        log.debug(\"Adding failure for flow node instance {}\", flowNodeInstance.getId());\n        var bpmFailure = SBPMFailure.builder()\n                .flowNodeInstanceId(flowNodeInstance.getId())\n                .processInstanceId(flowNodeInstance.getParentProcessInstanceId())\n                .rootProcessInstanceId(flowNodeInstance.getRootProcessInstanceId())\n                .processDefinitionId(flowNodeInstance.getProcessDefinitionId())\n                .scope(failure.scope())\n                .context(buildContext(failure.throwable()))\n                .errorMessage(ExceptionUtils.getRootCauseMessage(failure.throwable()))\n                .stackTrace(ExceptionUtils.getStackTrace(failure.throwable()))\n                .build();\n        return persistenceService.insert(bpmFailure);\n    }\n\n    @Override\n    public List<SBPMFailure> getFlowNodeFailures(long flowNodeInstanceId, int maxResults) throws SBonitaReadException {\n        final QueryOptions queryOptions = new QueryOptions(0, maxResults);\n        final Map<String, Object> parameters = Map.ofEntries(Map.entry(\"flowNodeInstanceId\", flowNodeInstanceId));\n        return persistenceService.selectList(new SelectListDescriptor<>(\"getFlowNodeFailures\", parameters,\n                SBPMFailure.class, queryOptions));\n    }\n\n    @Override\n    public void archiveFlowNodeFailures(long flowNodeInstanceId, long archiveDate) throws SBonitaException {\n        log.debug(\"Archiving failures of flow node instance {}\", flowNodeInstanceId);\n        archiveService.recordInserts(archiveDate, getFlowNodeFailures(flowNodeInstanceId, Integer.MAX_VALUE)\n                .stream()\n                .map(SABPMFailure::new)\n                .map(ArchiveInsertRecord::new)\n                .toArray(ArchiveInsertRecord[]::new));\n    }\n\n    @Override\n    public void deleteFlowNodeFailures(long flowNodeInstanceId) throws SBonitaException {\n        log.debug(\"Deleting failures of flow node instance {}\", flowNodeInstanceId);\n        persistenceService.delete(getFlowNodeFailures(flowNodeInstanceId, Integer.MAX_VALUE)\n                .stream()\n                .map(SBPMFailure::getId)\n                .toList(),\n                SBPMFailure.class);\n    }\n\n    @Override\n    public void deleteArchivedFlowNodeFailures(List<Long> flowNodeInstanceIds) throws SBonitaException {\n        if (!flowNodeInstanceIds.isEmpty()) {\n            log.debug(\"Deleting archived failures of flow node instances {}\", flowNodeInstanceIds);\n            archiveService.deleteFromQuery(\"deleteArchivedBPMFailuresByFlowNodeInstanceIds\",\n                    Map.ofEntries(Map.entry(\"flowNodeInstanceIds\", flowNodeInstanceIds)));\n        }\n    }\n\n    @Override\n    public List<SABPMFailure> getArchivedFlowNodeFailures(long flowNodeInstanceId, int maxResults)\n            throws SBonitaReadException {\n        final QueryOptions queryOptions = new QueryOptions(0, maxResults);\n        final Map<String, Object> parameters = Map.ofEntries(Map.entry(\"flowNodeInstanceId\", flowNodeInstanceId));\n        return persistenceService.selectList(new SelectListDescriptor<>(\"getArchivedFlowNodeFailures\", parameters,\n                SABPMFailure.class, queryOptions));\n    }\n\n    @Override\n    public SBPMFailure createProcessInstanceFailure(SProcessInstance processInstance, Failure failure)\n            throws SPersistenceException {\n        log.debug(\"Registering failure for process instance {}\", processInstance.getId());\n        var bpmFailure = SBPMFailure.builder()\n                .processInstanceId(processInstance.getId())\n                .processDefinitionId(processInstance.getProcessDefinitionId())\n                .rootProcessInstanceId(processInstance.getRootProcessInstanceId())\n                .scope(failure.scope())\n                .context(buildContext(failure.throwable()))\n                .errorMessage(ExceptionUtils.getRootCauseMessage(failure.throwable()))\n                .stackTrace(ExceptionUtils.getStackTrace(failure.throwable()))\n                .build();\n        return persistenceService.insert(bpmFailure);\n    }\n\n    @Override\n    public List<SBPMFailure> getProcessInstanceFailures(long processInstanceId, int maxResults)\n            throws SBonitaReadException {\n        final QueryOptions queryOptions = new QueryOptions(0, maxResults);\n        final Map<String, Object> parameters = Map.ofEntries(Map.entry(\"processInstanceId\", processInstanceId));\n        return persistenceService.selectList(new SelectListDescriptor<>(\"getProcessInstanceFailures\", parameters,\n                SBPMFailure.class, queryOptions));\n    }\n\n    @Override\n    public void archiveProcessInstanceFailures(long processInstanceId, long archiveDate) throws SBonitaException {\n        log.debug(\"Archiving failures of process instance {}\", processInstanceId);\n        archiveService.recordInserts(archiveDate, getProcessInstanceFailures(processInstanceId, Integer.MAX_VALUE)\n                .stream()\n                .map(SABPMFailure::new)\n                .map(ArchiveInsertRecord::new)\n                .toArray(ArchiveInsertRecord[]::new));\n    }\n\n    @Override\n    public void deleteProcessInstanceFailures(long processInstanceId) throws SBonitaException {\n        log.debug(\"Deleting failures of process instance {}\", processInstanceId);\n        persistenceService.delete(getProcessInstanceFailures(processInstanceId, Integer.MAX_VALUE)\n                .stream()\n                .map(SBPMFailure::getId)\n                .toList(),\n                SBPMFailure.class);\n    }\n\n    @Override\n    public void deleteArchivedProcessInstanceFailures(List<Long> processInstanceIds) throws SBonitaException {\n        if (!processInstanceIds.isEmpty()) {\n            log.debug(\"Deleting archived failures of process instances {}\", processInstanceIds);\n            archiveService.deleteFromQuery(\"deleteArchivedBPMFailuresByProcessInstanceIds\",\n                    Map.ofEntries(Map.entry(\"processInstanceIds\", processInstanceIds)));\n        }\n    }\n\n    @Override\n    public List<SABPMFailure> getArchivedProcessInstanceFailures(long processInstanceId, int maxResults)\n            throws SBonitaReadException {\n        final QueryOptions queryOptions = new QueryOptions(0, maxResults);\n        final Map<String, Object> parameters = Map.ofEntries(Map.entry(\"processInstanceId\", processInstanceId));\n        return persistenceService\n                .selectList(new SelectListDescriptor<>(\"getArchivedProcessInstanceFailures\", parameters,\n                        SABPMFailure.class, queryOptions));\n    }\n\n    @Override\n    public List<SBPMFailure> getChildProcessInstancesFailures(long rootProcessInstanceId, int maxResults)\n            throws SBonitaReadException {\n        final QueryOptions queryOptions = new QueryOptions(0, maxResults);\n        final Map<String, Object> parameters = Map.ofEntries(Map.entry(\"rootProcessInstanceId\", rootProcessInstanceId));\n        return persistenceService.selectList(new SelectListDescriptor<>(\"getChildProcessInstancesFailures\", parameters,\n                SBPMFailure.class, queryOptions));\n    }\n\n    @Override\n    public List<SABPMFailure> getArchivedChildProcessInstancesFailures(long rootProcessInstanceId, int maxResults)\n            throws SBonitaReadException {\n        final QueryOptions queryOptions = new QueryOptions(0, maxResults);\n        final Map<String, Object> parameters = Map.ofEntries(Map.entry(\"rootProcessInstanceId\", rootProcessInstanceId));\n        return persistenceService\n                .selectList(new SelectListDescriptor<>(\"getArchivedChildProcessInstancesFailures\", parameters,\n                        SABPMFailure.class, queryOptions));\n    }\n\n    private static String buildContext(Throwable e) {\n        Map<SExceptionContext, Serializable> ctx = new TreeMap<>();\n        if (e instanceof ExceptionContext exceptionContext) {\n            ctx = exceptionContext.getContext();\n        }\n        var contextBuilder = new StringBuilder();\n\n        if (ctx.containsKey(SExceptionContext.MESSAGE_INSTANCE_NAME)) {\n            contextBuilder.append(\"message\")\n                    .append(TYPE_SEPARATOR)\n                    .append(ctx.get(SExceptionContext.MESSAGE_INSTANCE_NAME));\n        }\n\n        addConnectorContext(contextBuilder, e, ctx);\n\n        addTransitionContext(contextBuilder, ctx);\n\n        var evaluationException = ExceptionUtils.throwableOfType(e, SExpressionEvaluationException.class);\n        if (evaluationException != null) {\n            addContextPathSeparator(contextBuilder);\n            contextBuilder.append(\"expression\")\n                    .append(TYPE_SEPARATOR)\n                    .append(evaluationException.getExpressionName());\n        }\n        return contextBuilder.toString();\n    }\n\n    private static void addTransitionContext(StringBuilder contextBuilder,\n            Map<SExceptionContext, Serializable> ctx) {\n        if (ctx.containsKey(SExceptionContext.TRANSITION_TARGET_FLOWNODE_NAME)) {\n            if (ctx.containsKey(SExceptionContext.TRANSITION_NAME)) {\n                contextBuilder.append(ctx.get(SExceptionContext.TRANSITION_NAME));\n                addContextPathSeparator(contextBuilder);\n            }\n            contextBuilder.append(\"to\")\n                    .append(TYPE_SEPARATOR)\n                    .append(ctx.get(SExceptionContext.TRANSITION_TARGET_FLOWNODE_NAME));\n        }\n    }\n\n    private static void addConnectorContext(StringBuilder contextBuilder, Throwable e,\n            Map<SExceptionContext, Serializable> ctx) {\n        if (ctx.containsKey(SExceptionContext.CONNECTOR_NAME)) {\n            addContextPathSeparator(contextBuilder);\n            contextBuilder.append(ctx.get(SExceptionContext.CONNECTOR_NAME));\n            if (ctx.containsKey(SExceptionContext.CONNECTOR_DEFINITION_ID)) {\n                contextBuilder.append(TYPE_SEPARATOR);\n                contextBuilder.append(ctx.get(SExceptionContext.CONNECTOR_DEFINITION_ID));\n            }\n            if (ctx.containsKey(SExceptionContext.CONNECTOR_ACTIVATION_EVENT)) {\n                contextBuilder.append(TYPE_SEPARATOR);\n                contextBuilder.append(ctx.get(SExceptionContext.CONNECTOR_ACTIVATION_EVENT).toString().toLowerCase());\n            }\n            var operationExecutionException = ExceptionUtils.throwableOfType(e, SOperationExecutionException.class);\n            if (operationExecutionException != null) {\n                addContextPathSeparator(contextBuilder);\n                contextBuilder.append(\"output\");\n            }\n        }\n\n        if (ctx.containsKey(SExceptionContext.CONNECTOR_INPUT_NAME)) {\n            addContextPathSeparator(contextBuilder);\n            contextBuilder.append(\"input\")\n                    .append(TYPE_SEPARATOR)\n                    .append(ctx.get(SExceptionContext.CONNECTOR_INPUT_NAME));\n        }\n\n        var connectorValidationEx = ExceptionUtils.throwableOfType(e, ConnectorValidationException.class);\n        if (connectorValidationEx != null) {\n            addContextPathSeparator(contextBuilder);\n            contextBuilder.append(\"input-validation\");\n        }\n    }\n\n    private static void addContextPathSeparator(StringBuilder contextBuilder) {\n        if (!contextBuilder.isEmpty()) {\n            contextBuilder.append(CONTEXT_PATH_SEPARATOR);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/impl/FlowNodeInstancesServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.impl;\n\nimport static java.util.Collections.singletonMap;\n\nimport java.text.MessageFormat;\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.archive.ArchiveService;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeDeletionException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeModificationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstanceStateCounter;\nimport org.bonitasoft.engine.core.process.instance.model.SGatewayInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.core.process.instance.model.STaskPriority;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAManualTaskInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SUserTaskInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.recorder.SelectDescriptorBuilder;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.bonitasoft.engine.services.PersistenceService;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Frederic Bouquet\n * @author Celine Souchet\n */\n@Slf4j\npublic abstract class FlowNodeInstancesServiceImpl implements FlowNodeInstanceService {\n\n    private static final String SUPERVISED_BY = \"SupervisedBy\";\n\n    private final SUserTaskInstanceBuilderFactory activityInstanceKeyProvider;\n\n    private final Recorder recorder;\n\n    private final PersistenceService persistenceService;\n\n    private final ArchiveService archiveService;\n\n    public FlowNodeInstancesServiceImpl(final Recorder recorder, final PersistenceService persistenceService,\n            final ArchiveService archiveService) {\n        this.recorder = recorder;\n        this.persistenceService = persistenceService;\n        activityInstanceKeyProvider = BuilderFactory.get(SUserTaskInstanceBuilderFactory.class);\n        this.archiveService = archiveService;\n    }\n\n    public ArchiveService getArchiveService() {\n        return archiveService;\n    }\n\n    @Override\n    public void setState(final SFlowNodeInstance flowNodeInstance, final FlowNodeState state)\n            throws SFlowNodeModificationException {\n        final long now = System.currentTimeMillis();\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        descriptor.addField(activityInstanceKeyProvider.getPreviousStateIdKey(), flowNodeInstance.getStateId());\n        descriptor.addField(activityInstanceKeyProvider.getStateIdKey(), state.getId());\n        descriptor.addField(activityInstanceKeyProvider.getStateNameKey(), state.getName());\n        descriptor.addField(activityInstanceKeyProvider.getStableKey(), state.isStable());\n        descriptor.addField(activityInstanceKeyProvider.getTerminalKey(), state.isTerminal());\n        descriptor.addField(activityInstanceKeyProvider.getReachStateDateKey(), now);\n        descriptor.addField(activityInstanceKeyProvider.getLastUpdateDateKey(), now);\n        descriptor.addField(activityInstanceKeyProvider.getStateExecutingKey(), false);\n        log.debug(MessageFormat.format(\"[{0} with id {1}] changed state {2}->{3}(new={4})\",\n                flowNodeInstance.getClass().getSimpleName(),\n                flowNodeInstance.getId(), flowNodeInstance.getStateId(), state.getId(),\n                state.getClass().getSimpleName()));\n\n        updateOneField(flowNodeInstance, ACTIVITYINSTANCE_STATE, descriptor);\n    }\n\n    @Override\n    public void setExecuting(final SFlowNodeInstance flowNodeInstance) throws SFlowNodeModificationException {\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        descriptor.addField(activityInstanceKeyProvider.getStateExecutingKey(), true);\n        log.debug(MessageFormat.format(\"[{0} with id {1}] have executing flag set to true\",\n                flowNodeInstance.getClass().getSimpleName(),\n                flowNodeInstance.getId()));\n        updateOneField(flowNodeInstance, ACTIVITYINSTANCE_STATE, descriptor);\n    }\n\n    @Override\n    public void updateDisplayName(final SFlowNodeInstance flowNodeInstance, final String displayName)\n            throws SFlowNodeModificationException {\n        if (displayName != null && !displayName.equals(flowNodeInstance.getDisplayName())) {\n            final String key = activityInstanceKeyProvider.getDisplayNameKey();\n            final String event = ACTIVITYINSTANCE_DISPLAY_NAME;\n            updateOneField(flowNodeInstance, key, displayName, 255, event);\n        }\n    }\n\n    private String getTruncated(final String value, final int maxLengh, final SFlowNodeInstance flowNodeInstance,\n            final String key) {\n        if (value.length() > maxLengh) {\n            final String truncatedValue = value.substring(0, maxLengh);\n            logTruncationWarning(value, truncatedValue, maxLengh, flowNodeInstance, key);\n            return truncatedValue;\n        }\n        return value;\n    }\n\n    private void logTruncationWarning(final String value, final String truncatedValue, final int maxLengh,\n            final SFlowNodeInstance flowNodeInstance,\n            final String key) {\n        if (log.isWarnEnabled()) {\n            final StringBuilder stb = new StringBuilder();\n            stb.append(\"The field \");\n            stb.append(key);\n            stb.append(\" is too long in the flow node instance [id: \");\n            stb.append(flowNodeInstance.getId());\n            stb.append(\", name: \");\n            stb.append(flowNodeInstance.getName());\n            stb.append(\", process instance id: \");\n            stb.append(flowNodeInstance.getParentProcessInstanceId());\n            stb.append(\", root process instance id: \");\n            stb.append(flowNodeInstance.getRootProcessInstanceId());\n            stb.append(\"] and will be truncated to the max lengh (\");\n            stb.append(maxLengh);\n            stb.append(\"). The truncated value is: '\");\n            stb.append(truncatedValue);\n            stb.append(\"'. The original value was: '\");\n            stb.append(value);\n            stb.append(\"'.\");\n            final String message = stb.toString();\n            log.warn(message);\n        }\n    }\n\n    private void updateOneField(final SFlowNodeInstance flowNodeInstance, final String attributeKey,\n            final String attributeValue, final int maxLength,\n            final String event) throws SFlowNodeModificationException {\n        final String truncatedValue = getTruncated(attributeValue, maxLength, flowNodeInstance, attributeKey);\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        descriptor.addField(attributeKey, truncatedValue);\n\n        updateOneField(flowNodeInstance, event, descriptor);\n    }\n\n    private void updateOneField(final SFlowNodeInstance flowNodeInstance, final String attributeKey,\n            final Long attributeValue, final String event)\n            throws SFlowNodeModificationException {\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        descriptor.addField(attributeKey, attributeValue);\n        updateOneField(flowNodeInstance, event, descriptor);\n    }\n\n    private void updateOneField(SFlowNodeInstance flowNodeInstance, String event, EntityUpdateDescriptor descriptor)\n            throws SFlowNodeModificationException {\n        setLastUpdateDate(descriptor);\n        try {\n            recorder.recordUpdate(UpdateRecord.buildSetFields(flowNodeInstance, descriptor), event);\n        } catch (final SRecorderException e) {\n            throw new SFlowNodeModificationException(e);\n        }\n    }\n\n    @Override\n    public void updateDisplayDescription(final SFlowNodeInstance flowNodeInstance, final String displayDescription)\n            throws SFlowNodeModificationException {\n        if (displayDescription != null && !displayDescription.equals(flowNodeInstance.getDisplayDescription())) {\n            final String event = ACTIVITYINSTANCE_DISPLAY_DESCRIPTION;\n            final String key = activityInstanceKeyProvider.getDisplayDescriptionKey();\n            updateOneField(flowNodeInstance, key, displayDescription, 255, event);\n        }\n    }\n\n    @Override\n    public void setTaskPriority(final SFlowNodeInstance flowNodeInstance, final STaskPriority priority)\n            throws SFlowNodeModificationException {\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        descriptor.addField(activityInstanceKeyProvider.getPriorityKey(), priority);\n        updateOneField(flowNodeInstance, ACTIVITYINSTANCE_STATE, descriptor);\n    }\n\n    @Override\n    public SFlowNodeInstance getFlowNodeInstance(final long flowNodeInstanceId)\n            throws SFlowNodeNotFoundException, SFlowNodeReadException {\n        SFlowNodeInstance selectOne;\n        try {\n            selectOne = persistenceService.selectById(SelectDescriptorBuilder.getElementById(SFlowNodeInstance.class,\n                    \"SFlowNodeInstance\", flowNodeInstanceId));\n        } catch (final SBonitaReadException e) {\n            throw new SFlowNodeReadException(e);\n        }\n        if (selectOne == null) {\n            throw new SFlowNodeNotFoundException(flowNodeInstanceId);\n        }\n        return selectOne;\n    }\n\n    @Override\n    public List<SFlowNodeInstance> getAllChildrenOfProcessInstance(final long parentProcessInstanceId,\n            final int fromIndex, final int maxResults) throws SBonitaReadException {\n        return getUnmodifiableList(\n                getPersistenceService().selectList(new SelectListDescriptor<>(\"getAllChildrenOfProcessInstance\",\n                        singletonMap(\"parentProcessInstanceId\", parentProcessInstanceId),\n                        SFlowNodeInstance.class, new QueryOptions(fromIndex, maxResults))));\n    }\n\n    @Override\n    public List<SFlowNodeInstance> getDirectChildrenOfProcessInstance(final long parentProcessInstanceId,\n            final int fromIndex, final int maxResults) throws SBonitaReadException {\n        return getUnmodifiableList(getPersistenceService().selectList(new SelectListDescriptor<>(\n                \"getDirectChildrenOfProcessInstance\",\n                singletonMap(\"parentProcessInstanceId\", parentProcessInstanceId),\n                SFlowNodeInstance.class, new QueryOptions(fromIndex, maxResults))));\n    }\n\n    @Override\n    public List<SFlowNodeInstance> getDirectChildrenOfActivityInstance(long parentActivityInstanceId, int fromIndex,\n            int maxResults) throws SBonitaReadException {\n        List<SFlowNodeInstance> selectList;\n        final Map<String, Object> parameters = singletonMap(\"parentActivityInstanceId\",\n                parentActivityInstanceId);\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, maxResults);\n        selectList = getPersistenceService()\n                .selectList(new SelectListDescriptor<>(\"getDirectChildrenOfActivityInstance\",\n                        parameters, SFlowNodeInstance.class, queryOptions));\n        return getUnmodifiableList(selectList);\n    }\n\n    @Override\n    public List<SAFlowNodeInstance> getArchivedFlowNodeInstances(final long rootContainerId, final int fromIndex,\n            final int maxResults)\n            throws SFlowNodeReadException {\n        List<SAFlowNodeInstance> selectList;\n        try {\n            selectList = getPersistenceService().selectList(\n                    SelectDescriptorBuilder.getArchivedFlowNodesFromProcessInstance(rootContainerId, fromIndex,\n                            maxResults));\n        } catch (final SBonitaReadException e) {\n            throw new SFlowNodeReadException(e);\n        }\n        return getUnmodifiableList(selectList);\n    }\n\n    @Override\n    public Set<Long> getSourceObjectIdsOfArchivedFlowNodeInstances(List<Long> sourceProcessInstanceIds)\n            throws SBonitaReadException {\n        return new HashSet<>(getPersistenceService()\n                .selectList(new SelectListDescriptor<>(\"getSourceObjectIdsOfArchivedFlowNodeInstances\",\n                        singletonMap(\"sourceProcessInstanceIds\", sourceProcessInstanceIds),\n                        SAFlowNodeInstance.class, QueryOptions.countQueryOptions())));\n    }\n\n    @Override\n    public void deleteArchivedFlowNodeInstances(List<Long> sourceObjectIds) throws SBonitaException {\n        archiveService.deleteFromQuery(\"deleteArchivedFlowNodeInstances\",\n                singletonMap(\"sourceObjectIds\", sourceObjectIds));\n    }\n\n    @Override\n    public SAFlowNodeInstance getArchivedFlowNodeInstance(final long archivedFlowNodeInstanceId)\n            throws SFlowNodeReadException, SFlowNodeNotFoundException {\n        final ReadPersistenceService persistenceService = archiveService.getDefinitiveArchiveReadPersistenceService();\n        SAFlowNodeInstance selectOne;\n        try {\n            selectOne = persistenceService.selectById(\n                    SelectDescriptorBuilder.getElementById(SAFlowNodeInstance.class, \"SArchivedFlowNodeInstance\",\n                            archivedFlowNodeInstanceId));\n        } catch (final SBonitaReadException e) {\n            throw new SFlowNodeReadException(e);\n        }\n        if (selectOne == null) {\n            throw new SFlowNodeNotFoundException(archivedFlowNodeInstanceId);\n        }\n        return selectOne;\n    }\n\n    @Override\n    public <T extends SAFlowNodeInstance> T getLastArchivedFlowNodeInstance(final Class<T> entityClass,\n            final long sourceObjectFlowNodeInstanceId)\n            throws SBonitaReadException {\n        final SAManualTaskInstanceBuilderFactory builderFactory = BuilderFactory\n                .get(SAManualTaskInstanceBuilderFactory.class);\n        final FilterOption filterOption = new FilterOption(entityClass, builderFactory.getSourceObjectIdKey(),\n                sourceObjectFlowNodeInstanceId);\n        final List<OrderByOption> orderByOptions = new ArrayList<>();\n        orderByOptions.add(new OrderByOption(entityClass, builderFactory.getArchivedDateKey(), OrderByType.DESC));\n        orderByOptions.add(new OrderByOption(entityClass, builderFactory.getLastUpdateKey(), OrderByType.DESC));\n        final QueryOptions queryOptions = new QueryOptions(0, 1, orderByOptions,\n                Collections.singletonList(filterOption), null);\n        final List<T> saFlowNodeInstances = searchArchivedFlowNodeInstances(entityClass, queryOptions);\n        if (!saFlowNodeInstances.isEmpty()) {\n            return saFlowNodeInstances.get(0);\n        }\n        return null;\n    }\n\n    @Override\n    public void setStateCategory(final SFlowNodeInstance flowElementInstance, final SStateCategory stateCategory)\n            throws SFlowNodeModificationException {\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        descriptor.addField(activityInstanceKeyProvider.getStateCategoryKey(), stateCategory);\n        updateFlowNode(flowElementInstance, STATE_CATEGORY, descriptor);\n    }\n\n    @Override\n    public void setExecutedBy(final SFlowNodeInstance flowNodeInstance, final long userId)\n            throws SFlowNodeModificationException {\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        descriptor.addField(activityInstanceKeyProvider.getExecutedBy(), userId);\n        updateFlowNode(flowNodeInstance, EXECUTED_BY_MODIFIED, descriptor);\n    }\n\n    @Override\n    public void setExecutedBySubstitute(final SFlowNodeInstance flowNodeInstance, final long executerSubstituteId)\n            throws SFlowNodeModificationException {\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        descriptor.addField(activityInstanceKeyProvider.getExecutedBySubstitute(), executerSubstituteId);\n        updateFlowNode(flowNodeInstance, EXECUTED_BY_SUBSTITUTE_MODIFIED, descriptor);\n    }\n\n    @Override\n    public void setExpectedEndDate(final SFlowNodeInstance flowNodeInstance, final Long dueDate)\n            throws SFlowNodeModificationException {\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        descriptor.addField(activityInstanceKeyProvider.getExpectedEndDateKey(), dueDate);\n        updateFlowNode(flowNodeInstance, EXPECTED_END_DATE_MODIFIED, descriptor);\n    }\n\n    protected void updateFlowNode(final SFlowNodeInstance flowNodeInstance, final String eventName,\n            final EntityUpdateDescriptor descriptor)\n            throws SFlowNodeModificationException {\n        setLastUpdateDate(descriptor);\n        try {\n            getRecorder().recordUpdate(UpdateRecord.buildSetFields(flowNodeInstance, descriptor), eventName);\n        } catch (final SRecorderException sre) {\n            throw new SFlowNodeModificationException(sre);\n        }\n    }\n\n    /**\n     * Adds the lastUpdateDate field to the descriptor if not already present.\n     * This ensures that any update to a flow node instance also updates the lastUpdateDate.\n     * If another date field (reachStateDate, claimedDate) is already in the descriptor with a\n     * timestamp value, that value is reused to ensure consistency across date fields in the same update.\n     */\n    protected void setLastUpdateDate(final EntityUpdateDescriptor descriptor) {\n        if (!descriptor.getFields().containsKey(activityInstanceKeyProvider.getLastUpdateDateKey())) {\n            Long timestamp = findExistingTimestamp(descriptor);\n            if (timestamp == null) {\n                timestamp = System.currentTimeMillis();\n            }\n            descriptor.addField(activityInstanceKeyProvider.getLastUpdateDateKey(), timestamp);\n        }\n    }\n\n    /**\n     * Looks for an existing timestamp value from known date fields in the descriptor.\n     * Returns the timestamp if found, or null if no date field is present.\n     */\n    private Long findExistingTimestamp(final EntityUpdateDescriptor descriptor) {\n        // Check reachStateDate (used in setState)\n        Object reachStateDate = descriptor.getFields().get(activityInstanceKeyProvider.getReachStateDateKey());\n        if (reachStateDate instanceof Long && (Long) reachStateDate > 0) {\n            return (Long) reachStateDate;\n        }\n        // Check claimedDate (used in assignHumanTask).\n        // Only reuse if > 0: when a task is released (unassigned), claimedDate is set to 0,\n        // which is not a valid timestamp. In that case, we return null to get a fresh timestamp.\n        Object claimedDate = descriptor.getFields().get(activityInstanceKeyProvider.getClaimedDateKey());\n        if (claimedDate instanceof Long && (Long) claimedDate > 0) {\n            return (Long) claimedDate;\n        }\n        return null;\n    }\n\n    protected <T> List<T> getUnmodifiableList(List<T> selectList) {\n        if (selectList == null) {\n            selectList = new ArrayList<>();\n        }\n        return Collections.unmodifiableList(selectList);\n    }\n\n    @Override\n    public long getNumberOfFlowNodeInstances(final Class<? extends SFlowNodeInstance> entityClass,\n            final QueryOptions countOptions)\n            throws SBonitaReadException {\n        return getPersistenceService().getNumberOfEntities(entityClass, countOptions, null);\n    }\n\n    @Override\n    public <T extends SFlowNodeInstance> List<T> searchFlowNodeInstances(final Class<T> entityClass,\n            final QueryOptions searchOptions)\n            throws SBonitaReadException {\n        return getPersistenceService().searchEntity(entityClass, searchOptions, null);\n    }\n\n    @Override\n    public long getNumberOfFlowNodeInstancesSupervisedBy(final Long supervisorId,\n            final Class<? extends SFlowNodeInstance> entityClass,\n            final QueryOptions queryOptions) throws SBonitaReadException {\n        final Map<String, Object> parameters = singletonMap(\"supervisorId\", (Object) supervisorId);\n        return getPersistenceService().getNumberOfEntities(entityClass, SUPERVISED_BY, queryOptions, parameters);\n    }\n\n    @Override\n    public <T extends SFlowNodeInstance> List<T> searchFlowNodeInstancesSupervisedBy(final Long supervisorId,\n            final Class<T> entityClass,\n            final QueryOptions queryOptions) throws SBonitaReadException {\n        final Map<String, Object> parameters = singletonMap(\"supervisorId\", (Object) supervisorId);\n        return getPersistenceService().searchEntity(entityClass, SUPERVISED_BY, queryOptions, parameters);\n    }\n\n    @Override\n    public List<SFlowNodeInstanceStateCounter> getNumberOfFlownodesOfProcessDefinitionInAllStates(\n            final long processDefinitionId)\n            throws SBonitaReadException {\n        final HashMap<String, Object> parameters = new HashMap<>(2);\n        parameters.put(\"processDefinitionId\", processDefinitionId);\n        final List<SFlowNodeInstanceStateCounter> result = persistenceService.selectList(new SelectListDescriptor<>(\n                \"getNumberOfFlowNodesOfProcessDefinitionInAllStates\", parameters, SFlowNodeInstance.class,\n                new QueryOptions(0, Integer.MAX_VALUE)));\n        if (result != null && result.size() > 0) {\n            return result;\n        }\n        return Collections.emptyList();\n    }\n\n    @Override\n    public List<SFlowNodeInstanceStateCounter> getNumberOfFlownodesInAllStates(final long parentProcessInstanceId)\n            throws SBonitaReadException {\n        final HashMap<String, Object> parameters = new HashMap<>(2);\n        parameters.put(\"parentProcessInstanceId\", parentProcessInstanceId);\n        final List<SFlowNodeInstanceStateCounter> result = persistenceService.selectList(new SelectListDescriptor<>(\n                \"getNumberOfFlowNodesInAllStates\", parameters, SFlowNodeInstance.class,\n                new QueryOptions(0, Integer.MAX_VALUE)));\n        if (result != null && result.size() > 0) {\n            return result;\n        }\n        return Collections.emptyList();\n    }\n\n    @Override\n    public List<SFlowNodeInstanceStateCounter> getNumberOfArchivedFlownodesInAllStates(\n            final long parentProcessInstanceId) throws SBonitaReadException {\n        final HashMap<String, Object> parameters = new HashMap<>(2);\n        parameters.put(\"parentProcessInstanceId\", parentProcessInstanceId);\n        final List<SFlowNodeInstanceStateCounter> result = persistenceService.selectList(new SelectListDescriptor<>(\n                \"getNumberOfArchivedFlowNodesInAllStates\", parameters, SAFlowNodeInstance.class,\n                new QueryOptions(0, Integer.MAX_VALUE)));\n        if (result != null && result.size() > 0) {\n            return result;\n        }\n        return Collections.emptyList();\n    }\n\n    @Override\n    public long getNumberOfArchivedFlowNodeInstances(final Class<? extends SAFlowNodeInstance> entityClass,\n            final QueryOptions countOptions)\n            throws SBonitaReadException {\n        return getPersistenceService().getNumberOfEntities(entityClass, countOptions, null);\n    }\n\n    @Override\n    public <T extends SAFlowNodeInstance> List<T> searchArchivedFlowNodeInstances(final Class<T> entityClass,\n            final QueryOptions searchOptions)\n            throws SBonitaReadException {\n        return getPersistenceService().searchEntity(entityClass, searchOptions, null);\n    }\n\n    @Override\n    public long getNumberOfArchivedFlowNodeInstancesSupervisedBy(final long supervisorId,\n            final Class<? extends SAFlowNodeInstance> entityClass,\n            final QueryOptions queryOptions) throws SBonitaReadException {\n        final Map<String, Object> parameters = singletonMap(\"supervisorId\", (Object) supervisorId);\n        return getPersistenceService().getNumberOfEntities(entityClass, SUPERVISED_BY, queryOptions, parameters);\n    }\n\n    @Override\n    public <T extends SAFlowNodeInstance> List<T> searchArchivedFlowNodeInstancesSupervisedBy(final long supervisorId,\n            final Class<T> entityClass,\n            final QueryOptions queryOptions) throws SBonitaReadException {\n        final Map<String, Object> parameters = singletonMap(\"supervisorId\", (Object) supervisorId);\n        return getPersistenceService().searchEntity(entityClass, SUPERVISED_BY, queryOptions, parameters);\n    }\n\n    protected Recorder getRecorder() {\n        return recorder;\n    }\n\n    protected PersistenceService getPersistenceService() {\n        return persistenceService;\n    }\n\n    @Override\n    public void deleteFlowNodeInstance(final SFlowNodeInstance sFlowNodeInstance) throws SFlowNodeDeletionException {\n        try {\n            recorder.recordDelete(new DeleteRecord(sFlowNodeInstance), FLOWNODE_INSTANCE);\n        } catch (final SBonitaException e) {\n            throw new SFlowNodeDeletionException(e);\n        }\n    }\n\n    @Override\n    public List<Long> getFlowNodeInstanceIdsToRecover(Duration considerElementsOlderThan, QueryOptions queryOptions)\n            throws SBonitaReadException {\n        final List<Long> selectList = getPersistenceService().selectList(\n                new SelectListDescriptor<>(\"getFlowNodeInstanceIdsToRecover\",\n                        singletonMap(\"maxLastUpdate\",\n                                System.currentTimeMillis() - considerElementsOlderThan.toMillis()),\n                        SFlowNodeInstance.class,\n                        queryOptions));\n        return getUnmodifiableList(selectList);\n    }\n\n    @Override\n    public List<Long> getGatewayInstanceIdsToRecover(Duration considerElementsOlderThan,\n            final QueryOptions queryOptions) throws SBonitaReadException {\n        final List<Long> selectList = getPersistenceService().selectList(\n                new SelectListDescriptor<>(\"getGatewayInstanceIdsToRecover\",\n                        singletonMap(\"maxLastUpdate\",\n                                System.currentTimeMillis() - considerElementsOlderThan.toMillis()),\n                        SGatewayInstance.class,\n                        queryOptions));\n        return getUnmodifiableList(selectList);\n    }\n\n    @Override\n    public List<SFlowNodeInstance> getFlowNodeInstancesByIds(List<Long> ids) throws SBonitaReadException {\n        return getUnmodifiableList(getPersistenceService().selectList(\n                new SelectListDescriptor<>(\"getFlowNodeInstancesByIds\", singletonMap(\"ids\", ids),\n                        SFlowNodeInstance.class, QueryOptions.ALL_RESULTS)));\n    }\n\n    @Override\n    public int getNumberOfFlowNodes(final long parentProcessInstanceId) throws SBonitaReadException {\n        return getPersistenceService().selectOne(SelectDescriptorBuilder.getNumberOfFlowNode(parentProcessInstanceId))\n                .intValue();\n    }\n\n    @Override\n    public List<SFlowNodeInstance> getFlowNodeInstancesByNameAndParentContainerId(String name, Long parentContainerId)\n            throws SBonitaReadException {\n        Map<String, Object> parameters = new HashMap<>();\n        parameters.put(\"name\", name);\n        parameters.put(\"parentContainerId\", parentContainerId);\n        return getPersistenceService().selectList(\n                new SelectListDescriptor<>(\"getFlowNodeInstancesByNameAndParentContainerId\", parameters,\n                        SFlowNodeInstance.class, QueryOptions.ALL_RESULTS));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/impl/GatewayInstanceServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.impl;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.GatewayInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SGatewayCreationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SGatewayModificationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SGatewayNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SGatewayReadException;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SGatewayInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SGatewayInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.recorder.SelectDescriptorBuilder;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\n\n/**\n * @author Feng Hui\n * @author Zhao Na\n * @author Matthieu Chaffotte\n */\n@Slf4j\npublic class GatewayInstanceServiceImpl implements GatewayInstanceService {\n\n    private final Recorder recorder;\n\n    private final ReadPersistenceService persistenceRead;\n\n    private final SGatewayInstanceBuilderFactory sGatewayInstanceBuilderFactory;\n\n    private final FlowNodeInstanceService flowNodeInstanceService;\n\n    public GatewayInstanceServiceImpl(final Recorder recorder, final ReadPersistenceService persistenceRead,\n            FlowNodeInstanceService flowNodeInstanceService) {\n        this.recorder = recorder;\n        this.persistenceRead = persistenceRead;\n        this.flowNodeInstanceService = flowNodeInstanceService;\n        sGatewayInstanceBuilderFactory = BuilderFactory.get(SGatewayInstanceBuilderFactory.class);\n    }\n\n    @Override\n    public void createGatewayInstance(final SGatewayInstance gatewayInstance) throws SGatewayCreationException {\n        try {\n            recorder.recordInsert(new InsertRecord(gatewayInstance), GATEWAYINSTANCE);\n        } catch (final SRecorderException e) {\n            throw new SGatewayCreationException(e);\n        }\n        if (log.isDebugEnabled()) {\n            String stb = \"Created gateway instance [name = <\" + gatewayInstance.getName() +\n                    \">, id = <\" + gatewayInstance.getId() +\n                    \">, parent process instance id = <\" + gatewayInstance.getParentProcessInstanceId() +\n                    \">, root process instance id = <\" + gatewayInstance.getRootProcessInstanceId() +\n                    \">, process definition id = <\" + gatewayInstance.getRootProcessInstanceId() +\n                    \">]\";\n            log.debug(stb);\n        }\n    }\n\n    @Override\n    public SGatewayInstance getGatewayInstance(final long gatewayInstanceId)\n            throws SGatewayNotFoundException, SGatewayReadException {\n        SGatewayInstance selectOne;\n        try {\n            selectOne = persistenceRead.selectById(SelectDescriptorBuilder.getElementById(SGatewayInstance.class,\n                    \"SGatewayInstance\", gatewayInstanceId));\n        } catch (final SBonitaReadException e) {\n            throw new SGatewayReadException(e);\n        }\n        if (selectOne == null) {\n            throw new SGatewayNotFoundException(gatewayInstanceId);\n        }\n        return selectOne;\n    }\n\n    @Override\n    public boolean checkMergingCondition(final SProcessDefinition sDefinition, final SGatewayInstance gatewayInstance)\n            throws SBonitaException {\n        switch (gatewayInstance.getGatewayType()) {\n            case EXCLUSIVE:\n                return true;\n            case INCLUSIVE:\n                return isInclusiveGatewayActivated(sDefinition, gatewayInstance);\n            case PARALLEL:\n                return isParallelGatewayActivated(sDefinition, gatewayInstance);\n            default:\n                return false;\n        }\n    }\n\n    boolean isInclusiveGatewayActivated(final SProcessDefinition sDefinition, final SGatewayInstance gatewayInstance)\n            throws SBonitaReadException {\n        log.debug(\"Evaluate if gateway \" + gatewayInstance.getName() + \" of instance \"\n                + gatewayInstance.getRootProcessInstanceId() + \" of definition \"\n                + sDefinition.getName() + \" must be activated \");\n        log.debug(\"HitBys = \" + gatewayInstance.getHitBys());\n        if (gatewayInstance.isFinished()) {\n            return false;\n        }\n        SFlowElementContainerDefinition processContainer = sDefinition.getProcessContainer();\n        SFlowNodeDefinition gatewayDefinition = processContainer.getFlowNode(gatewayInstance.getFlowNodeDefinitionId());\n        long processInstanceId = gatewayInstance.getParentContainerId();\n\n        List<String> hitByTransitionList = getHitByTransitionList(gatewayInstance);\n        List<STransitionDefinition> incomingTransitions = gatewayDefinition.getIncomingTransitions();\n        List<STransitionDefinition> incomingWithTokens = new ArrayList<>();\n        List<STransitionDefinition> incomingWithoutTokens = new ArrayList<>();\n\n        for (int i = 0; i < incomingTransitions.size(); i++) {\n            STransitionDefinition currentTransition = incomingTransitions.get(i);\n            if (hitByTransitionList.contains(String.valueOf(i + 1))) {\n                incomingWithTokens.add(currentTransition);\n            } else {\n                incomingWithoutTokens.add(currentTransition);\n            }\n        }\n        /*\n         * We create two lists that will contain transition definitions that have a path to the gateway finishing with\n         * a token and the list of transition that does not have a path to the gateway finishing with a token\n         */\n        List<STransitionDefinition> finishWithAToken = new ArrayList<>();\n        List<STransitionDefinition> doesNotFinishWithAToken = new ArrayList<>();\n\n        /*\n         * we calculate all transition definitions that can block the gateway\n         */\n        addBackwardReachableTransitions(processContainer, gatewayDefinition, incomingWithTokens, finishWithAToken,\n                Collections.<STransitionDefinition> emptyList());\n        addBackwardReachableTransitions(processContainer, gatewayDefinition, incomingWithoutTokens,\n                doesNotFinishWithAToken, finishWithAToken);\n        /*\n         * we check if one of the transitions that are 'blocking' contains a token in this process instance\n         */\n        return !transitionsContainsAToken(doesNotFinishWithAToken, gatewayDefinition, processInstanceId,\n                processContainer);\n    }\n\n    boolean transitionsContainsAToken(List<STransitionDefinition> transitions, SFlowNodeDefinition gatewayDefinition,\n            long processInstanceId,\n            SFlowElementContainerDefinition processContainer) throws SBonitaReadException {\n        List<SFlowNodeDefinition> sourceElements = new ArrayList<>();\n        List<SFlowNodeDefinition> targetElements = new ArrayList<>();\n\n        log.debug(\"Check if there is a token on \" + transitions);\n        for (STransitionDefinition sTransitionDefinition : transitions) {\n            SFlowNodeDefinition source = processContainer.getFlowNode(sTransitionDefinition.getSource());\n            if (!source.equals(gatewayDefinition) && !sourceElements.contains(source)) {\n                sourceElements.add(source);\n            }\n            SFlowNodeDefinition target = processContainer.getFlowNode(sTransitionDefinition.getTarget());\n            if (!target.equals(gatewayDefinition) && !targetElements.contains(target)) {\n                targetElements.add(target);\n            }\n        }\n        List<SFlowNodeDefinition> sourceAndTarget = extractElementThatAreSourceAndTarget(sourceElements,\n                targetElements);\n        if (containsToken(processInstanceId, sourceAndTarget, null))\n            return true;\n        if (containsToken(processInstanceId, sourceElements, true))\n            return true;\n        if (containsToken(processInstanceId, targetElements, false))\n            return true;\n        log.debug(\"No token to wait, gateway will fire\");\n        return false;\n    }\n\n    List<SFlowNodeDefinition> extractElementThatAreSourceAndTarget(List<SFlowNodeDefinition> sourceElements,\n            List<SFlowNodeDefinition> targetElements) {\n        List<SFlowNodeDefinition> sourceAndTarget = new ArrayList<>();\n        Iterator<SFlowNodeDefinition> iterator = sourceElements.iterator();\n        while (iterator.hasNext()) {\n            SFlowNodeDefinition sourceElement = iterator.next();\n            if (targetElements.contains(sourceElement) || sourceElement.getIncomingTransitions().isEmpty() /*\n                                                                                                            * if it's a\n                                                                                                            * start\n                                                                                                            * element\n                                                                                                            * it's also\n                                                                                                            * considered\n                                                                                                            * as a\n                                                                                                            * target\n                                                                                                            */) {\n                iterator.remove();\n                targetElements.remove(sourceElement);\n                sourceAndTarget.add(sourceElement);\n            }\n        }\n        return sourceAndTarget;\n    }\n\n    /**\n     * @param shouldBeTerminal\n     *        is true is the element should be in state terminal\n     *        is false if it should not be in terminal\n     *        is null if it should be either in terminal or not\n     */\n    boolean containsToken(long processInstanceId, List<SFlowNodeDefinition> elements, Boolean shouldBeTerminal)\n            throws SBonitaReadException {\n        for (SFlowNodeDefinition element : elements) {\n            List<SFlowNodeInstance> sFlowNodeInstances = flowNodeInstanceService\n                    .getFlowNodeInstancesByNameAndParentContainerId(element.getName(), processInstanceId);\n            for (SFlowNodeInstance sFlowNodeInstance : sFlowNodeInstances) {\n                if (shouldBeTerminal == null || (shouldBeTerminal ^ !sFlowNodeInstance.isTerminal())) {\n                    log.debug(\"flow node \" + sFlowNodeInstance.getName() + \" contain a token, gateway not merged\");\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n\n    void addBackwardReachableTransitions(SFlowElementContainerDefinition processContainer,\n            SFlowNodeDefinition gatewayDefinition,\n            List<STransitionDefinition> transitions, List<STransitionDefinition> backwardReachable,\n            List<STransitionDefinition> notIn) {\n        for (STransitionDefinition sTransitionDefinition : transitions) {\n            if (!backwardReachable.contains(sTransitionDefinition) && !notIn.contains(sTransitionDefinition)) {\n                backwardReachable.add(sTransitionDefinition);\n                //if the source is the gateway we stop searching backward\n                SFlowNodeDefinition flowNode = processContainer.getFlowNode(sTransitionDefinition.getSource());\n                if (!flowNode.equals(gatewayDefinition)) {\n                    addBackwardReachableTransitions(processContainer, gatewayDefinition,\n                            flowNode.getIncomingTransitions(), backwardReachable, notIn);\n                }\n            }\n        }\n    }\n\n    boolean isParallelGatewayActivated(final SProcessDefinition sDefinition, final SGatewayInstance gatewayInstance) {\n        final List<String> hitsBy = getHitByTransitionList(gatewayInstance);\n        final List<STransitionDefinition> trans = getTransitionDefinitions(gatewayInstance, sDefinition);\n        boolean go = true;\n        int i = 1;\n        while (go && i <= trans.size()) {\n            go = hitsBy.contains(String.valueOf(i));\n            i++;\n        }\n        return go;\n    }\n\n    /**\n     * @return the list of transition indexes that hit the gateway\n     */\n    private List<String> getHitByTransitionList(final SGatewayInstance gatewayInstance) {\n        return Arrays.asList(gatewayInstance.getHitBys().split(\",\"));\n    }\n\n    protected List<STransitionDefinition> getTransitionDefinitions(final SGatewayInstance gatewayInstance,\n            final SProcessDefinition processDefinition) {\n        final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer();\n        final SFlowNodeDefinition gatewayDefinition = processContainer\n                .getFlowNode(gatewayInstance.getFlowNodeDefinitionId());\n        return gatewayDefinition.getIncomingTransitions();\n    }\n\n    @Override\n    public void setState(final SGatewayInstance gatewayInstance, final int stateId)\n            throws SGatewayModificationException {\n        updateOneColumnAndMetaData(gatewayInstance, sGatewayInstanceBuilderFactory.getStateIdKey(), stateId,\n                GATEWAYINSTANCE_STATE);\n    }\n\n    @Override\n    public void hitTransition(final SGatewayInstance gatewayInstance, final long transitionIndex)\n            throws SGatewayModificationException {\n        log.debug(\"Hit gateway \" + gatewayInstance.getName() + \" (\" + gatewayInstance.getId() + \")\" + \" of instance \"\n                + gatewayInstance.getRootProcessInstanceId()\n                + \" with transition index \" + transitionIndex);\n        final String hitBys = gatewayInstance.getHitBys();\n        String columnValue;\n        if (hitBys == null || hitBys.isEmpty()) {\n            columnValue = String.valueOf(transitionIndex);\n        } else {\n            columnValue = hitBys + \",\" + transitionIndex;\n        }\n        updateOneColumnAndMetaData(gatewayInstance, sGatewayInstanceBuilderFactory.getHitBysKey(), columnValue,\n                GATEWAYINSTANCE_HITBYS);\n    }\n\n    private void updateOneColumnAndMetaData(final SGatewayInstance gatewayInstance, final String columnName,\n            final Serializable columnValue, final String event)\n            throws SGatewayModificationException {\n        final long now = System.currentTimeMillis();\n        final EntityUpdateDescriptor entityUpdateDescriptor = new EntityUpdateDescriptor();\n        entityUpdateDescriptor.addField(columnName, columnValue);\n        entityUpdateDescriptor.addField(\"lastUpdateDate\", now);\n        entityUpdateDescriptor.addField(\"reachedStateDate\", now);\n        try {\n            recorder.recordUpdate(UpdateRecord.buildSetFields(gatewayInstance, entityUpdateDescriptor), event);\n        } catch (final SRecorderException e) {\n            throw new SGatewayModificationException(e);\n        }\n    }\n\n    @Override\n    public SGatewayInstance getActiveGatewayInstanceOfTheProcess(final long parentProcessInstanceId, final String name)\n            throws SGatewayNotFoundException,\n            SGatewayReadException {\n        try {\n            return persistenceRead.selectOne(\n                    SelectDescriptorBuilder.getActiveGatewayInstanceOfProcess(parentProcessInstanceId, name));\n        } catch (final SBonitaReadException e) {\n            throw new SGatewayReadException(e);\n        }\n    }\n\n    /*\n     * set the gateway to finish, then if there is some transition that arrived at the gateway multiple times (e.g. a\n     * loop with an inclusive gateway)\n     * it is possible that some of the token are not merged.\n     * in that case a new gateway is created with these remaining token and we check if this new gateway is already\n     * merged\n     * new gateway that are already merged are returned\n     */\n    @Override\n    public List<SGatewayInstance> setFinishAndCreateNewGatewayForRemainingToken(SProcessDefinition processDefinition,\n            final SGatewayInstance gatewayInstance) throws SBonitaException {\n        List<String> hitBys = getHitByTransitionList(gatewayInstance);\n        List<String> merged = getMergedTokens(gatewayInstance, hitBys);\n        setFinished(gatewayInstance, merged.size());\n        List<String> remaining = getRemainingTokens(hitBys, merged);\n        log.debug(\"There is {} remaining token to merge on gateway {} will create a new if there is\", remaining,\n                gatewayInstance.getName());\n        if (remaining.isEmpty()) {\n            return Collections.emptyList();\n        }\n        List<SGatewayInstance> toFire = new ArrayList<>();\n        SGatewayInstance newGatewayInstance = createGatewayWithRemainingTokens(gatewayInstance, remaining);\n        if (checkMergingCondition(processDefinition, newGatewayInstance)) {\n            toFire.add(newGatewayInstance);\n            //recursively add newly created gateway that are already merged\n            toFire.addAll(setFinishAndCreateNewGatewayForRemainingToken(processDefinition, newGatewayInstance));\n        }\n        return toFire;\n    }\n\n    List<String> getMergedTokens(SGatewayInstance gatewayInstance, List<String> hitBys) {\n        List<String> merged = null;\n        switch (gatewayInstance.getGatewayType()) {\n            case PARALLEL:\n                merged = unique(hitBys);\n                break;\n            case INCLUSIVE:\n                merged = unique(hitBys);\n                break;\n            case EXCLUSIVE:\n                merged = Collections.singletonList(hitBys.get(0));\n                break;\n        }\n        return merged;\n    }\n\n    private ArrayList<String> unique(List<String> hitBys) {\n        ArrayList<String> merged = new ArrayList<>();\n        for (String hitBy : hitBys) {\n            if (!merged.contains(hitBy)) {\n                merged.add(hitBy);\n            }\n        }\n        return merged;\n    }\n\n    ArrayList<String> getRemainingTokens(List<String> hitBys, List<String> merged) {\n        ArrayList<String> remaining = new ArrayList<>(hitBys);\n        for (String mergedToken : merged) {\n            remaining.remove(mergedToken);\n        }\n        return remaining;\n    }\n\n    /**\n     * create a new gateway instance with the remaining token\n     *\n     * @param gatewayInstance\n     *        the original gatewayInstance\n     * @param remaining\n     *        the token that already hit the gateway\n     * @return\n     *         the new gateway\n     */\n    private SGatewayInstance createGatewayWithRemainingTokens(SGatewayInstance gatewayInstance, List<String> remaining)\n            throws SGatewayCreationException {\n        SGatewayInstance sGatewayInstance = new SGatewayInstance(gatewayInstance);\n        String remainingTokenAsString = \"\";\n        for (String token : remaining) {\n            remainingTokenAsString += token + \",\";\n        }\n        sGatewayInstance.setHitBys(remainingTokenAsString.substring(0, remainingTokenAsString.length() - 1));\n        createGatewayInstance(sGatewayInstance);\n        return sGatewayInstance;\n    }\n\n    void setFinished(SGatewayInstance gatewayInstance, int numberOfTokenToMerge) throws SGatewayModificationException {\n        final String columnValue = FINISH + numberOfTokenToMerge;\n        log.trace(\"set finish on gateway {} {}\", gatewayInstance.getName(), columnValue);\n        updateOneColumnAndMetaData(gatewayInstance, sGatewayInstanceBuilderFactory.getHitBysKey(), columnValue,\n                GATEWAYINSTANCE_HITBYS);\n    }\n\n    @Override\n    public List<SGatewayInstance> getInclusiveGatewaysOfProcessInstanceThatShouldFire(\n            SProcessDefinition processDefinition, long processInstanceId) throws SBonitaReadException {\n        List<SGatewayInstance> sGatewayInstances = getInclusiveGatewayInstanceOfProcessInstance(processInstanceId);\n        ArrayList<SGatewayInstance> shouldFire = new ArrayList<>();\n        for (SGatewayInstance sGatewayInstance : sGatewayInstances) {\n            if (isInclusiveGatewayActivated(processDefinition, sGatewayInstance)) {\n                shouldFire.add(sGatewayInstance);\n            }\n        }\n        return shouldFire;\n    }\n\n    private List<SGatewayInstance> getInclusiveGatewayInstanceOfProcessInstance(long processInstanceId)\n            throws SBonitaReadException {\n        final HashMap<String, Object> hashMap = new HashMap<>(2);\n        hashMap.put(\"processInstanceId\", processInstanceId);\n        SelectListDescriptor<SGatewayInstance> getGatewayMergingToken = new SelectListDescriptor<>(\n                \"getInclusiveGatewayInstanceOfProcessInstance\", hashMap, SGatewayInstance.class,\n                new QueryOptions(0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS));\n        return persistenceRead.selectList(getGatewayMergingToken);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/impl/ProcessInstanceServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.impl;\n\nimport static java.util.Collections.singletonMap;\nimport static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier;\n\nimport java.text.MessageFormat;\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections4.ListUtils;\nimport org.bonitasoft.engine.archive.ArchiveInsertRecord;\nimport org.bonitasoft.engine.archive.ArchiveService;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.commons.ExceptionUtils;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.connector.ConnectorInstanceService;\nimport org.bonitasoft.engine.core.connector.exception.SConnectorInstanceDeletionException;\nimport org.bonitasoft.engine.core.connector.exception.SConnectorInstanceReadException;\nimport org.bonitasoft.engine.core.contract.data.ContractDataService;\nimport org.bonitasoft.engine.core.document.api.DocumentService;\nimport org.bonitasoft.engine.core.process.comment.api.SCommentService;\nimport org.bonitasoft.engine.core.process.comment.model.archive.SAComment;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.core.process.definition.model.SActivityDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SSubProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SEventDefinition;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.BPMFailureService;\nimport org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.RefBusinessDataService;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceCreationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceHierarchicalDeletionException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceModificationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventModificationException;\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAProcessInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.recorder.SelectDescriptorBuilder;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceContainer;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceService;\nimport org.bonitasoft.engine.data.instance.exception.SDataInstanceException;\nimport org.bonitasoft.engine.dependency.model.ScopeType;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @author Yanyan Liu\n * @author Frederic Bouquet\n * @author Celine Souchet\n */\n@Slf4j\npublic class ProcessInstanceServiceImpl implements ProcessInstanceService {\n\n    private static final String MANAGER_USER_ID = \"managerUserId\";\n\n    private static final String USER_ID = \"userId\";\n\n    private static final String SUPERVISED_BY = \"SupervisedBy\";\n\n    private static final String FAILED_AND_SUPERVISED_BY = \"FailedAndSupervisedBy\";\n\n    private static final String INVOLVING_USER = \"InvolvingUser\";\n\n    private static final String FAILED = \"Failed\";\n\n    private static final String MANAGED_BY = \"ManagedBy\";\n\n    private static final int BATCH_SIZE = 100;\n\n    static final int IN_REQUEST_SIZE = 100;\n\n    private final Recorder recorder;\n\n    private final ReadPersistenceService persistenceRead;\n\n    private final ActivityInstanceService activityService;\n\n    private final EventInstanceService bpmEventInstanceService;\n\n    private final DataInstanceService dataInstanceService;\n\n    private final ArchiveService archiveService;\n\n    private final ProcessDefinitionService processDefinitionService;\n\n    private final ConnectorInstanceService connectorInstanceService;\n\n    private final ClassLoaderService classLoaderService;\n\n    private final DocumentService documentService;\n\n    private final SCommentService commentService;\n\n    private final RefBusinessDataService refBusinessDataService;\n\n    private final ContractDataService contractDataService;\n\n    private final BPMFailureService bpmFailureService;\n\n    public ProcessInstanceServiceImpl(final Recorder recorder,\n            final ReadPersistenceService persistenceRead,\n            final ActivityInstanceService activityService,\n            final EventInstanceService bpmEventInstanceService,\n            final DataInstanceService dataInstanceService,\n            final ArchiveService archiveService,\n            final ProcessDefinitionService processDefinitionService,\n            final ConnectorInstanceService connectorInstanceService,\n            final ClassLoaderService classLoaderService,\n            final DocumentService documentService,\n            final SCommentService commentService,\n            final RefBusinessDataService refBusinessDataService,\n            final ContractDataService contractDataService,\n            final BPMFailureService bpmFailureService) {\n        this.recorder = recorder;\n        this.persistenceRead = persistenceRead;\n        this.activityService = activityService;\n        this.processDefinitionService = processDefinitionService;\n        this.connectorInstanceService = connectorInstanceService;\n        this.classLoaderService = classLoaderService;\n        this.documentService = documentService;\n        this.commentService = commentService;\n        this.refBusinessDataService = refBusinessDataService;\n        this.contractDataService = contractDataService;\n        this.bpmEventInstanceService = bpmEventInstanceService;\n        this.dataInstanceService = dataInstanceService;\n        this.archiveService = archiveService;\n        this.bpmFailureService = bpmFailureService;\n    }\n\n    @Override\n    public void createProcessInstance(final SProcessInstance processInstance) throws SProcessInstanceCreationException {\n        try {\n            recorder.recordInsert(new InsertRecord(processInstance), PROCESSINSTANCE);\n            setProcessState(processInstance, ProcessInstanceState.INITIALIZING);\n        } catch (final SRecorderException | SProcessInstanceModificationException sre) {\n            throw new SProcessInstanceCreationException(sre);\n        }\n    }\n\n    @Override\n    public SProcessInstance getProcessInstance(final long processInstanceId)\n            throws SProcessInstanceReadException, SProcessInstanceNotFoundException {\n        final SProcessInstance instance;\n        try {\n            instance = persistenceRead.selectById(SelectDescriptorBuilder.getElementById(SProcessInstance.class,\n                    \"ProcessInstance\", processInstanceId));\n        } catch (final SBonitaReadException e) {\n            throw new SProcessInstanceReadException(e);\n        }\n        if (instance == null) {\n            throw new SProcessInstanceNotFoundException(processInstanceId);\n        }\n        return instance;\n    }\n\n    @Override\n    public void deleteProcessInstance(final long processInstanceId)\n            throws SProcessInstanceModificationException, SProcessInstanceReadException,\n            SProcessInstanceNotFoundException {\n        final SProcessInstance processInstance = getProcessInstance(processInstanceId);\n        deleteProcessInstance(processInstance);\n    }\n\n    @Override\n    public long deleteParentProcessInstanceAndElements(final List<SProcessInstance> sProcessInstances)\n            throws SBonitaException {\n        long nbDeleted = 0;\n        for (final SProcessInstance sProcessInstance : sProcessInstances) {\n            deleteParentProcessInstanceAndElements(sProcessInstance);\n            nbDeleted++;\n        }\n        return nbDeleted;\n    }\n\n    @Override\n    public void deleteParentProcessInstanceAndElements(final SProcessInstance sProcessInstance)\n            throws SBonitaException {\n        checkIfCallerIsNotActive(sProcessInstance.getCallerId());\n\n        try {\n            deleteProcessInstance(sProcessInstance);\n            deleteArchivedProcessInstances(Collections.singletonList(sProcessInstance.getId()));\n        } catch (final SProcessInstanceModificationException e) {\n            getProcessInstanceAndLogException(sProcessInstance, e);\n        }\n    }\n\n    void getProcessInstanceAndLogException(final SProcessInstance sProcessInstance,\n            final SProcessInstanceModificationException e)\n            throws SProcessInstanceModificationException {\n        try {\n            getProcessInstance(sProcessInstance.getId());\n            // process is still here, that's not normal. The problem must be raised:\n            throw e;\n        } catch (final SProcessInstanceReadException | SProcessInstanceNotFoundException e1) {\n            log.debug(\"{}. It has probably completed.\", e.getMessage());\n        }\n    }\n\n    @Override\n    public int deleteArchivedProcessInstances(List<Long> sourceProcessInstanceIds) throws SBonitaException {\n        //delete all flow node having as root process instances these processes\n        deleteArchivedFlowNodeInstancesAndElements(sourceProcessInstanceIds);\n\n        Set<Long> archivedChildrenProcessInstances = getArchivedChildrenProcessInstances(sourceProcessInstanceIds);\n\n        Set<Long> allSourceObjectIds = new HashSet<>();\n        allSourceObjectIds.addAll(sourceProcessInstanceIds);\n        allSourceObjectIds.addAll(archivedChildrenProcessInstances);\n        // Easier to partition than a set\n        List<Long> allSourceObjectIdsList = new ArrayList<>(allSourceObjectIds);\n        int numberOfDeletedInstances = 0;\n        // Verify that the resulting IN statement in the request has a reasonable size, if not split in smaller requests\n        // See BS-19316\n        Iterable<List<Long>> sourceObjectIdsPartitions = ListUtils.partition(allSourceObjectIdsList, IN_REQUEST_SIZE);\n        for (List<Long> sourceObjectIds2k : sourceObjectIdsPartitions) {\n            //delete all elements\n            deleteElementsOfArchivedProcessInstances(new ArrayList<>(sourceObjectIds2k));\n            //delete all archived processes\n            numberOfDeletedInstances = numberOfDeletedInstances\n                    + archiveService.deleteFromQuery(\"deleteArchiveProcessInstanceBySourceObjectId\",\n                            Collections.singletonMap(\"sourceProcessInstanceIds\", sourceObjectIds2k));\n        }\n        return numberOfDeletedInstances;\n    }\n\n    private void deleteElementsOfArchivedProcessInstances(List<Long> sourceProcessInstanceIds) throws SBonitaException {\n        documentService.deleteArchivedDocuments(sourceProcessInstanceIds);\n        connectorInstanceService.deleteArchivedConnectorInstances(sourceProcessInstanceIds,\n                SConnectorInstance.PROCESS_TYPE);\n        dataInstanceService.deleteLocalArchivedDataInstances(sourceProcessInstanceIds,\n                DataInstanceContainer.PROCESS_INSTANCE.toString());\n        commentService.deleteArchivedComments(sourceProcessInstanceIds);\n        refBusinessDataService.deleteArchivedRefBusinessDataInstance(sourceProcessInstanceIds);\n        contractDataService.deleteArchivedProcessData(sourceProcessInstanceIds);\n        bpmFailureService.deleteArchivedProcessInstanceFailures(sourceProcessInstanceIds);\n    }\n\n    private void deleteArchivedFlowNodeInstancesAndElements(List<Long> sourceProcessInstanceIds)\n            throws SBonitaException {\n        List<Long> flowNodesSourceObjectIds = new ArrayList<>(\n                activityService.getSourceObjectIdsOfArchivedFlowNodeInstances(sourceProcessInstanceIds));\n        if (!flowNodesSourceObjectIds.isEmpty()) {\n            // Verify that the resulting IN statement in the request has a reasonable size, if not split it in smaller requests\n            // See BS-19316\n            List<List<Long>> flowNodesSourceObjectIdsPartitions = ListUtils.partition(flowNodesSourceObjectIds,\n                    IN_REQUEST_SIZE);\n            for (List<Long> flowNodesSourceObjectIds2k : flowNodesSourceObjectIdsPartitions) {\n                connectorInstanceService.deleteArchivedConnectorInstances(flowNodesSourceObjectIds2k,\n                        SConnectorInstance.FLOWNODE_TYPE);\n                dataInstanceService.deleteLocalArchivedDataInstances(flowNodesSourceObjectIds2k,\n                        DataInstanceContainer.ACTIVITY_INSTANCE.toString());\n                contractDataService.deleteArchivedUserTaskData(flowNodesSourceObjectIds2k);\n                bpmFailureService.deleteArchivedFlowNodeFailures(flowNodesSourceObjectIds2k);\n                activityService.deleteArchivedFlowNodeInstances(flowNodesSourceObjectIds2k);\n            }\n        }\n    }\n\n    private Set<Long> getArchivedChildrenProcessInstances(List<Long> sourceProcessInstanceIds) throws SBonitaException {\n        final SelectListDescriptor<Long> selectListDescriptor = new SelectListDescriptor<>(\n                \"getArchivedChildrenProcessInstanceIds\",\n                Collections.singletonMap(\"sourceProcessInstanceIds\", sourceProcessInstanceIds),\n                SAProcessInstance.class, new QueryOptions(0, Integer.MAX_VALUE));\n        return new HashSet<>(persistenceRead.selectList(selectListDescriptor));\n    }\n\n    @Override\n    public void deleteArchivedProcessInstance(final SAProcessInstance archivedProcessInstance)\n            throws SProcessInstanceModificationException {\n        try {\n            recorder.recordDelete(new DeleteRecord(archivedProcessInstance), PROCESSINSTANCE);\n        } catch (final SRecorderException e) {\n            throw new SProcessInstanceModificationException(e);\n        }\n    }\n\n    @Override\n    public List<Long> getSourceProcessInstanceIdsOfArchProcessInstancesFromDefinition(final long processDefinitionId,\n            final int fromIndex, final int maxResults,\n            final OrderByType sortingOrder) throws SProcessInstanceReadException {\n        final ReadPersistenceService persistenceService = archiveService.getDefinitiveArchiveReadPersistenceService();\n        final String saCommentSourceObjectId = SAComment.SOURCEOBJECTID_KEY;\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, maxResults, SAProcessInstance.class,\n                saCommentSourceObjectId, sortingOrder);\n        try {\n            return persistenceService.selectList(SelectDescriptorBuilder\n                    .getSourceProcesInstanceIdsOfArchProcessInstancesFromDefinition(processDefinitionId,\n                            queryOptions));\n        } catch (final SBonitaReadException e) {\n            throw new SProcessInstanceReadException(e);\n        }\n    }\n\n    @Override\n    public void deleteProcessInstance(final SProcessInstance sProcessInstance)\n            throws SProcessInstanceModificationException {\n        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        try {\n            final long processDefinitionId = sProcessInstance.getProcessDefinitionId();\n            final ClassLoader localClassLoader = classLoaderService\n                    .getClassLoader(identifier(ScopeType.PROCESS, processDefinitionId));\n            Thread.currentThread().setContextClassLoader(localClassLoader);\n            deleteProcessInstanceElements(sProcessInstance);\n            final DeleteRecord deleteRecord = new DeleteRecord(sProcessInstance);\n            recorder.recordDelete(deleteRecord, PROCESSINSTANCE);\n        } catch (final SBonitaException e) {\n            throw new SProcessInstanceModificationException(e);\n        } finally {\n            Thread.currentThread().setContextClassLoader(contextClassLoader);\n        }\n    }\n\n    protected void checkIfCallerIsNotActive(final long callerId)\n            throws SFlowNodeReadException, SProcessInstanceHierarchicalDeletionException {\n        try {\n            if (callerId > 0) {\n                final SFlowNodeInstance flowNodeInstance = activityService.getFlowNodeInstance(callerId);\n                final SProcessInstanceHierarchicalDeletionException exception = new SProcessInstanceHierarchicalDeletionException(\n                        \"Unable to delete the process instance, because the parent (call activity) is still active.\",\n                        flowNodeInstance.getRootProcessInstanceId());\n                setExceptionContext(flowNodeInstance, exception);\n                throw exception;\n            }\n        } catch (final SFlowNodeNotFoundException e) {\n            // ok the activity that called this process do not exists anymore\n        }\n    }\n\n    protected void deleteProcessInstanceElements(final SProcessInstance processInstance) throws SBonitaException {\n        SProcessDefinition processDefinition = null;\n        try {\n            processDefinition = processDefinitionService.getProcessDefinition(processInstance.getProcessDefinitionId());\n        } catch (final SProcessDefinitionNotFoundException e) {\n            // delete anyway\n        }\n        deleteFlowNodeInstances(processInstance.getId(), processDefinition);\n        deleteDataInstancesIfNecessary(processInstance, processDefinition);\n        documentService.deleteDocumentsFromProcessInstance(processInstance.getId());\n        deleteConnectorInstancesIfNecessary(processInstance, processDefinition);\n        commentService.deleteComments(processInstance.getId());\n        deleteEventSubprocessWaitingEvents(processInstance, processDefinition);\n        bpmFailureService.deleteProcessInstanceFailures(processInstance.getId());\n    }\n\n    private void deleteEventSubprocessWaitingEvents(SProcessInstance processInstance,\n            SProcessDefinition processDefinition)\n            throws SWaitingEventModificationException, SEventTriggerInstanceReadException {\n        if (processDefinition != null) {\n            boolean containsEventSubProcess = processDefinition.getProcessContainer().getActivities().stream()\n                    .anyMatch(a -> a.getType().equals(SFlowNodeType.SUB_PROCESS)\n                            && ((SSubProcessDefinition) a).isTriggeredByEvent());\n            if (containsEventSubProcess) {\n                bpmEventInstanceService.deleteWaitingEvents(processInstance);\n            }\n        }\n    }\n\n    private void deleteConnectorInstancesIfNecessary(final SProcessInstance processInstance,\n            final SProcessDefinition processDefinition)\n            throws SConnectorInstanceReadException, SConnectorInstanceDeletionException {\n        if (processDefinition != null && processDefinition.hasConnectors()) {\n            connectorInstanceService.deleteConnectors(processInstance.getId(), SConnectorInstance.PROCESS_TYPE);\n        }\n    }\n\n    protected void deleteConnectorInstancesIfNecessary(final SFlowNodeInstance flowNodeInstance,\n            final SProcessDefinition processDefinition)\n            throws SConnectorInstanceReadException, SConnectorInstanceDeletionException {\n        if (hasConnectors(flowNodeInstance, processDefinition)) {\n            connectorInstanceService.deleteConnectors(flowNodeInstance.getId(), SConnectorInstance.FLOWNODE_TYPE);\n        }\n    }\n\n    private boolean hasConnectors(final SFlowNodeInstance flowNodeInstance,\n            final SProcessDefinition processDefinition) {\n        if (processDefinition != null) {\n            final SActivityDefinition activityDefinition = (SActivityDefinition) getFlowNode(flowNodeInstance,\n                    processDefinition);\n            if (activityDefinition != null) {\n                return activityDefinition.hasConnectors();\n            }\n        }\n        return false;\n    }\n\n    private void deleteDataInstancesIfNecessary(final SProcessInstance processInstance,\n            final SProcessDefinition processDefinition)\n            throws SDataInstanceException {\n        boolean dataPresent = true;\n        if (processDefinition != null) {\n            dataPresent = processDefinition.getProcessContainer().getDataDefinitions().size() > 0;\n        }\n        dataInstanceService.deleteLocalDataInstances(processInstance.getId(),\n                DataInstanceContainer.PROCESS_INSTANCE.toString(), dataPresent);\n    }\n\n    private void deleteFlowNodeInstances(final long processInstanceId, final SProcessDefinition processDefinition)\n            throws SProcessInstanceModificationException, SFlowNodeReadException, SBonitaReadException {\n        List<SFlowNodeInstance> activityInstances;\n        do {\n            activityInstances = activityService.getAllChildrenOfProcessInstance(processInstanceId, 0, BATCH_SIZE);\n            for (final SFlowNodeInstance activityInstance : activityInstances) {\n                deleteFlowNodeInstance(activityInstance, processDefinition);\n            }\n        } while (activityInstances.size() == BATCH_SIZE);\n    }\n\n    @Override\n    public void deleteFlowNodeInstance(final SFlowNodeInstance flowNodeInstance,\n            final SProcessDefinition processDefinition)\n            throws SProcessInstanceModificationException {\n        try {\n            deleteFlowNodeInstanceElements(flowNodeInstance, processDefinition);\n            activityService.deleteFlowNodeInstance(flowNodeInstance);\n        } catch (final SBonitaException e) {\n            setExceptionContext(processDefinition, flowNodeInstance, e);\n            throw new SProcessInstanceModificationException(e);\n        }\n    }\n\n    void deleteFlowNodeInstanceElements(final SFlowNodeInstance flowNodeInstance,\n            final SProcessDefinition processDefinition) throws SBonitaException {\n        if (isReceiveTask(flowNodeInstance) || isEventWithTrigger(flowNodeInstance, processDefinition)) {\n            bpmEventInstanceService.deleteWaitingEvents(flowNodeInstance);\n        }\n        if (flowNodeInstance instanceof SActivityInstance activityInstance) {\n            deleteActivityInstanceElements(activityInstance, processDefinition);\n        }\n        bpmFailureService.deleteFlowNodeFailures(flowNodeInstance.getId());\n    }\n\n    private boolean isEventWithTrigger(SFlowNodeInstance flowNodeInstance, SProcessDefinition processDefinition) {\n        if (processDefinition != null) {\n            SFlowNodeDefinition flowNodeDefinition = processDefinition.getProcessContainer()\n                    .getFlowNode(flowNodeInstance.getFlowNodeDefinitionId());\n            return flowNodeDefinition instanceof SEventDefinition\n                    && !((SEventDefinition) flowNodeDefinition).getEventTriggers().isEmpty();\n        }\n        return false;\n    }\n\n    private boolean isReceiveTask(SFlowNodeInstance flowNodeInstance) {\n        return flowNodeInstance.getType().equals(SFlowNodeType.RECEIVE_TASK);\n    }\n\n    private void deleteActivityInstanceElements(final SActivityInstance sActivityInstance,\n            final SProcessDefinition processDefinition) throws SBonitaException {\n        deleteDataInstancesIfNecessary(sActivityInstance, processDefinition);\n        deleteConnectorInstancesIfNecessary(sActivityInstance, processDefinition);\n        if (SFlowNodeType.USER_TASK.equals(sActivityInstance.getType())\n                || SFlowNodeType.MANUAL_TASK.equals(sActivityInstance.getType())) {\n            activityService.deletePendingMappings(sActivityInstance.getId());\n        } else if (SFlowNodeType.CALL_ACTIVITY.equals(sActivityInstance.getType())\n                || SFlowNodeType.SUB_PROCESS.equals(sActivityInstance.getType())) {\n            // in the case of a call activity or subprocess activity delete the child process instance\n            deleteSubProcess(sActivityInstance, processDefinition);\n        }\n    }\n\n    void deleteSubProcess(final SFlowNodeInstance flowNodeInstance, final SProcessDefinition processDefinition)\n            throws SBonitaException {\n        try {\n            deleteProcessInstance(getChildOfActivity(flowNodeInstance.getId()));\n        } catch (final SProcessInstanceNotFoundException e) {\n            setExceptionContext(processDefinition, flowNodeInstance, e);\n\n            // if the child process is not found, it's because it has already finished and archived or it was not created\n            log.debug(\"Can't find the process instance called by the activity. This process may be already finished.\");\n            log.debug(ExceptionUtils.printLightWeightStacktrace(e));\n        }\n    }\n\n    private void setExceptionContext(final SProcessDefinition processDefinition,\n            final SFlowNodeInstance flowNodeInstance,\n            final SBonitaException e) {\n        if (processDefinition != null) {\n            e.setProcessDefinitionIdOnContext(processDefinition.getId());\n            e.setProcessDefinitionNameOnContext(processDefinition.getName());\n            e.setProcessDefinitionVersionOnContext(processDefinition.getVersion());\n        }\n        setExceptionContext(flowNodeInstance, e);\n    }\n\n    private void setExceptionContext(final SFlowNodeInstance flowNodeInstance, final SBonitaException e) {\n        e.setProcessInstanceIdOnContext(flowNodeInstance.getParentProcessInstanceId());\n        e.setRootProcessInstanceIdOnContext(flowNodeInstance.getRootProcessInstanceId());\n        e.setFlowNodeDefinitionIdOnContext(flowNodeInstance.getFlowNodeDefinitionId());\n        e.setFlowNodeInstanceIdOnContext(flowNodeInstance.getId());\n        e.setFlowNodeNameOnContext(flowNodeInstance.getName());\n    }\n\n    void deleteDataInstancesIfNecessary(final SFlowNodeInstance flowNodeInstance,\n            final SProcessDefinition processDefinition)\n            throws SDataInstanceException {\n        boolean hasData = true;\n        if (processDefinition != null) {\n            final SActivityDefinition activityDefinition = (SActivityDefinition) getFlowNode(flowNodeInstance,\n                    processDefinition);\n            if (activityDefinition != null) {\n                hasData = activityDefinition.getSDataDefinitions().size() > 0;\n            }\n        }\n        dataInstanceService.deleteLocalDataInstances(flowNodeInstance.getId(),\n                DataInstanceContainer.ACTIVITY_INSTANCE.toString(), hasData);\n    }\n\n    private SFlowNodeDefinition getFlowNode(final SFlowNodeInstance flowNodeInstance,\n            final SProcessDefinition processDefinition) {\n        if (processDefinition == null) {\n            return null;\n        }\n        return processDefinition.getProcessContainer().getFlowNode(flowNodeInstance.getFlowNodeDefinitionId());\n    }\n\n    @Override\n    public void setState(final SProcessInstance processInstance, final ProcessInstanceState state)\n            throws SProcessInstanceModificationException {\n        // Let's archive the process instance before changing the state (to keep a track of state change):\n        archiveProcessInstance(processInstance);\n        final int previousStateId = processInstance.getStateId();\n        setProcessState(processInstance, state);\n        log.debug(MessageFormat.format(\"[{0} with id {1}]{2}->{3}(new={4})\",\n                processInstance.getClass()\n                        .getSimpleName(),\n                processInstance.getId(), previousStateId, state.getId(), state.name()));\n    }\n\n    private void setProcessState(final SProcessInstance processInstance, final ProcessInstanceState state)\n            throws SProcessInstanceModificationException {\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        descriptor.addField(SProcessInstance.STATE_ID_KEY, state.getId());\n        final long now = System.currentTimeMillis();\n        switch (state) {\n            case COMPLETED:\n            case ABORTED:\n            case CANCELLED:\n                descriptor.addField(SProcessInstance.END_DATE_KEY, now);\n                break;\n            case STARTED:\n                descriptor.addField(SProcessInstance.START_DATE_KEY, now);\n                break;\n            default:\n                break;\n        }\n        descriptor.addField(SProcessInstance.LAST_UPDATE_KEY, now);\n        updateProcessInstance(processInstance, descriptor, PROCESSINSTANCE_STATE);\n    }\n\n    private void updateProcessInstance(final SProcessInstance processInstance, final EntityUpdateDescriptor descriptor,\n            final String eventType)\n            throws SProcessInstanceModificationException {\n        try {\n            recorder.recordUpdate(UpdateRecord.buildSetFields(processInstance, descriptor), eventType);\n        } catch (final SRecorderException e) {\n            throw new SProcessInstanceModificationException(e);\n        }\n    }\n\n    private void archiveProcessInstance(final SProcessInstance processInstance)\n            throws SProcessInstanceModificationException {\n        final SAProcessInstance saProcessInstance = BuilderFactory.get(SAProcessInstanceBuilderFactory.class)\n                .createNewInstance(processInstance).done();\n        final ArchiveInsertRecord insertRecord = new ArchiveInsertRecord(saProcessInstance);\n        try {\n            archiveService.recordInsert(System.currentTimeMillis(), insertRecord);\n        } catch (final SRecorderException e) {\n            throw new SProcessInstanceModificationException(e);\n        }\n    }\n\n    @Override\n    public void setStateCategory(final SProcessInstance processInstance, final SStateCategory stateCatetory)\n            throws SProcessInstanceModificationException {\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        descriptor.addField(SProcessInstance.STATE_CATEGORY_KEY, stateCatetory);\n        updateProcessInstance(processInstance, descriptor, PROCESS_INSTANCE_CATEGORY_STATE);\n    }\n\n    @Override\n    public List<Long> getChildInstanceIdsOfProcessInstance(final long processInstanceId, final int fromIndex,\n            final int maxResults, final String sortingField,\n            final OrderByType sortingOrder) throws SProcessInstanceReadException {\n        try {\n            final QueryOptions queryOptions = new QueryOptions(fromIndex, maxResults, SProcessInstance.class,\n                    sortingField, sortingOrder);\n            final SelectListDescriptor<Long> elements = SelectDescriptorBuilder.getChildInstanceIdsOfProcessInstance(\n                    SProcessInstance.class, processInstanceId,\n                    queryOptions);\n            return persistenceRead.selectList(elements);\n        } catch (final SBonitaReadException e) {\n            throw new SProcessInstanceReadException(e);\n        }\n    }\n\n    @Override\n    public SProcessInstance getChildOfActivity(final long activityInstId)\n            throws SProcessInstanceNotFoundException, SBonitaReadException {\n        final SProcessInstance sProcessInstance = persistenceRead\n                .selectOne(new SelectOneDescriptor<SProcessInstance>(\"getChildOfActivity\", Collections\n                        .<String, Object> singletonMap(\"activityInstanceId\", activityInstId), SProcessInstance.class));\n        if (sProcessInstance == null) {\n            throw new SProcessInstanceNotFoundException(\n                    \"No process instance was found as child of the activity with id = <\" + activityInstId + \">\");\n        }\n        return sProcessInstance;\n\n    }\n\n    @Override\n    public long getNumberOfChildInstancesOfProcessInstance(final long processInstanceId)\n            throws SProcessInstanceReadException {\n        try {\n            return persistenceRead\n                    .selectOne(SelectDescriptorBuilder.getNumberOfChildInstancesOfProcessInstance(processInstanceId));\n        } catch (final SBonitaReadException e) {\n            throw new SProcessInstanceReadException(e);\n        }\n    }\n\n    @Override\n    public long getNumberOfProcessInstances(final QueryOptions queryOptions) throws SBonitaReadException {\n        return persistenceRead.getNumberOfEntities(SProcessInstance.class, queryOptions, Collections.emptyMap());\n    }\n\n    @Override\n    public List<SProcessInstance> searchProcessInstances(final QueryOptions queryOptions) throws SBonitaReadException {\n        return persistenceRead.searchEntity(SProcessInstance.class, queryOptions, Collections.emptyMap());\n    }\n\n    @Override\n    public long getNumberOfOpenProcessInstancesSupervisedBy(final long userId, final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        try {\n            final Map<String, Object> parameters = Collections.singletonMap(USER_ID, (Object) userId);\n            return persistenceRead.getNumberOfEntities(SProcessInstance.class, SUPERVISED_BY, queryOptions, parameters);\n        } catch (final SBonitaReadException e) {\n            throw new SBonitaReadException(e);\n        }\n    }\n\n    @Override\n    public List<SProcessInstance> searchOpenProcessInstancesSupervisedBy(final long userId,\n            final QueryOptions queryOptions) throws SBonitaReadException {\n        try {\n            final Map<String, Object> parameters = Collections.singletonMap(USER_ID, (Object) userId);\n            return persistenceRead.searchEntity(SProcessInstance.class, SUPERVISED_BY, queryOptions, parameters);\n        } catch (final SBonitaReadException e) {\n            throw new SBonitaReadException(e);\n        }\n    }\n\n    @Override\n    public long getNumberOfFailedProcessInstancesSupervisedBy(final long userId, final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        try {\n            final Map<String, Object> parameters = Collections.singletonMap(USER_ID, (Object) userId);\n            return persistenceRead.getNumberOfEntities(SProcessInstance.class, FAILED_AND_SUPERVISED_BY, queryOptions,\n                    parameters);\n        } catch (final SBonitaReadException e) {\n            throw new SBonitaReadException(e);\n        }\n    }\n\n    @Override\n    public List<SProcessInstance> searchFailedProcessInstancesSupervisedBy(final long userId,\n            final QueryOptions queryOptions) throws SBonitaReadException {\n        try {\n            final Map<String, Object> parameters = Collections.singletonMap(USER_ID, (Object) userId);\n            return persistenceRead.searchEntity(SProcessInstance.class, FAILED_AND_SUPERVISED_BY, queryOptions,\n                    parameters);\n        } catch (final SBonitaReadException e) {\n            throw new SBonitaReadException(e);\n        }\n    }\n\n    @Override\n    public long getNumberOfOpenProcessInstancesInvolvingUser(final long userId, final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        try {\n            final Map<String, Object> parameters = new HashMap<>(1);\n            parameters.put(USER_ID, userId);\n            return persistenceRead.getNumberOfEntities(SProcessInstance.class, INVOLVING_USER, queryOptions,\n                    parameters);\n        } catch (final SBonitaReadException e) {\n            throw new SBonitaReadException(e);\n        }\n    }\n\n    @Override\n    public List<SProcessInstance> searchOpenProcessInstancesInvolvingUser(final long userId,\n            final QueryOptions queryOptions) throws SBonitaReadException {\n        final Map<String, Object> parameters = new HashMap<>(1);\n        parameters.put(USER_ID, userId);\n        return persistenceRead.searchEntity(SProcessInstance.class, INVOLVING_USER, queryOptions, parameters);\n    }\n\n    @Override\n    public long getNumberOfOpenProcessInstancesInvolvingUsersManagedBy(final long managerUserId,\n            final QueryOptions queryOptions) throws SBonitaReadException {\n        final Map<String, Object> parameters = new HashMap<>(1);\n        parameters.put(MANAGER_USER_ID, managerUserId);\n        return persistenceRead.getNumberOfEntities(SProcessInstance.class, INVOLVING_USER + \"s\" + MANAGED_BY,\n                queryOptions, parameters);\n    }\n\n    @Override\n    public List<SProcessInstance> searchOpenProcessInstancesInvolvingUsersManagedBy(final long managerUserId,\n            final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        final Map<String, Object> parameters = new HashMap<>(1);\n        parameters.put(MANAGER_USER_ID, managerUserId);\n        return persistenceRead.searchEntity(SProcessInstance.class, INVOLVING_USER + \"s\" + MANAGED_BY, queryOptions,\n                parameters);\n    }\n\n    @Override\n    public long getNumberOfArchivedProcessInstancesWithoutSubProcess(final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        final ReadPersistenceService persistenceService = archiveService.getDefinitiveArchiveReadPersistenceService();\n        return persistenceService.getNumberOfEntities(SAProcessInstance.class, \"WithoutSubProcess\", queryOptions, null);\n    }\n\n    @Override\n    public List<SAProcessInstance> searchArchivedProcessInstancesWithoutSubProcess(final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        final ReadPersistenceService persistenceService = archiveService.getDefinitiveArchiveReadPersistenceService();\n        return persistenceService.searchEntity(SAProcessInstance.class, \"WithoutSubProcess\", queryOptions, null);\n    }\n\n    @Override\n    public List<SAProcessInstance> searchArchivedProcessInstancesSupervisedBy(final long userId,\n            final QueryOptions queryOptions) throws SBonitaReadException {\n        final ReadPersistenceService persistenceService = archiveService.getDefinitiveArchiveReadPersistenceService();\n        final Map<String, Object> parameters = Collections.singletonMap(USER_ID, (Object) userId);\n        return persistenceService.searchEntity(SAProcessInstance.class, SUPERVISED_BY, queryOptions, parameters);\n    }\n\n    @Override\n    public long getNumberOfArchivedProcessInstancesSupervisedBy(final long userId, final QueryOptions countOptions)\n            throws SBonitaReadException {\n        final ReadPersistenceService persistenceService = archiveService.getDefinitiveArchiveReadPersistenceService();\n        final Map<String, Object> parameters = Collections.singletonMap(USER_ID, (Object) userId);\n        return persistenceService.getNumberOfEntities(SAProcessInstance.class, SUPERVISED_BY, countOptions, parameters);\n    }\n\n    @Override\n    public long getNumberOfArchivedProcessInstancesInvolvingUser(final long userId, final QueryOptions countOptions)\n            throws SBonitaReadException {\n        final ReadPersistenceService persistenceService = archiveService.getDefinitiveArchiveReadPersistenceService();\n        try {\n            final Map<String, Object> parameters = new HashMap<>(2);\n            parameters.put(USER_ID, userId);\n            return persistenceService.getNumberOfEntities(SAProcessInstance.class, INVOLVING_USER, countOptions,\n                    parameters);\n        } catch (final SBonitaReadException bre) {\n            throw new SBonitaReadException(bre);\n        }\n    }\n\n    @Override\n    public long getNumberOfArchivedProcessInstances(final QueryOptions queryOptions) throws SBonitaReadException {\n        final ReadPersistenceService persistenceService = archiveService.getDefinitiveArchiveReadPersistenceService();\n        return persistenceService.getNumberOfEntities(SAProcessInstance.class, queryOptions, null);\n    }\n\n    @Override\n    public List<SAProcessInstance> searchArchivedProcessInstances(final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        final ReadPersistenceService persistenceService = archiveService.getDefinitiveArchiveReadPersistenceService();\n        return persistenceService.searchEntity(SAProcessInstance.class, queryOptions, null);\n    }\n\n    @Override\n    public List<SAProcessInstance> searchArchivedProcessInstancesInvolvingUser(final long userId,\n            final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        final ReadPersistenceService persistenceService = archiveService.getDefinitiveArchiveReadPersistenceService();\n        try {\n            final Map<String, Object> parameters = new HashMap<>(1);\n            parameters.put(USER_ID, userId);\n            return persistenceService.searchEntity(SAProcessInstance.class, INVOLVING_USER, queryOptions, parameters);\n        } catch (final SBonitaReadException e) {\n            throw new SBonitaReadException(e);\n        }\n    }\n\n    @Override\n    public void updateProcess(final SProcessInstance processInstance, final EntityUpdateDescriptor descriptor)\n            throws SProcessInstanceModificationException {\n        try {\n            recorder.recordUpdate(UpdateRecord.buildSetFields(processInstance,\n                    descriptor.addField(SProcessInstance.LAST_UPDATE_KEY, System.currentTimeMillis())),\n                    PROCESSINSTANCE);\n        } catch (final SRecorderException e) {\n            throw new SProcessInstanceModificationException(e);\n        }\n    }\n\n    @Override\n    public SAProcessInstance getArchivedProcessInstance(final long archivedProcessInstanceId)\n            throws SProcessInstanceReadException {\n        final ReadPersistenceService persistenceService = archiveService.getDefinitiveArchiveReadPersistenceService();\n        try {\n            final Map<String, Object> parameters = Collections.singletonMap(\"id\", (Object) archivedProcessInstanceId);\n            return persistenceService.selectOne(new SelectOneDescriptor<SAProcessInstance>(\"getArchivedProcessInstance\",\n                    parameters, SAProcessInstance.class));\n        } catch (final SBonitaReadException e) {\n            throw new SProcessInstanceReadException(e);\n        }\n    }\n\n    @Override\n    public List<SAProcessInstance> getArchivedProcessInstancesInAllStates(final List<Long> processInstanceIds)\n            throws SProcessInstanceReadException {\n        final ReadPersistenceService persistenceService = archiveService.getDefinitiveArchiveReadPersistenceService();\n        try {\n            final Map<String, Object> parameters = Collections.singletonMap(\"sourceObjectIds\",\n                    (Object) processInstanceIds);\n            final SelectListDescriptor<SAProcessInstance> saProcessInstances = new SelectListDescriptor<>(\n                    \"getArchivedProcessInstancesInAllStates\", parameters, SAProcessInstance.class,\n                    QueryOptions.countQueryOptions());\n            return persistenceService.selectList(saProcessInstances);\n        } catch (final SBonitaReadException e) {\n            throw new SProcessInstanceReadException(e);\n        }\n    }\n\n    @Override\n    public List<Long> getLastArchivedProcessInstanceStartDates(final long sinceDateInMillis)\n            throws SProcessInstanceReadException {\n        final ReadPersistenceService persistenceService = archiveService.getDefinitiveArchiveReadPersistenceService();\n        try {\n            final Map<String, Object> parameters = Collections.singletonMap(\"sinceDateInMillis\", sinceDateInMillis);\n            final SelectListDescriptor<Long> saProcessInstances = new SelectListDescriptor<>(\n                    \"getLastArchivedProcessInstanceStartDates\", parameters, SAProcessInstance.class,\n                    QueryOptions.countQueryOptions());\n            return persistenceService.selectList(saProcessInstances);\n        } catch (final SBonitaReadException e) {\n            throw new SProcessInstanceReadException(e);\n        }\n    }\n\n    protected Set<Integer> getStateIdsFromStates(final ProcessInstanceState... states) {\n        if (states.length < 1) {\n            throw new IllegalArgumentException(\n                    \"ProcessInstanceServiceImpl.getProcessInstancesInStates() must have at least one state as parameter\");\n        }\n        final Set<Integer> stateIds = new HashSet<>(states.length);\n        for (ProcessInstanceState state : states) {\n            stateIds.add(state.getId());\n        }\n        return stateIds;\n    }\n\n    @Override\n    public List<Long> getArchivedChildrenSourceObjectIdsFromRootProcessInstance(final long rootProcessInstanceId,\n            final int fromIndex, final int maxResults,\n            final OrderByType sortingOrder) throws SBonitaReadException {\n        final Map<String, Object> inputParameters = new HashMap<>(1);\n        inputParameters.put(\"rootProcessInstanceId\", rootProcessInstanceId);\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, maxResults, SAProcessInstance.class,\n                \"sourceObjectId\", sortingOrder);\n        final SelectListDescriptor<Long> selectListDescriptor = new SelectListDescriptor<>(\n                \"getChildrenSourceProcessInstanceIdsFromRootProcessInstance\",\n                inputParameters, SAProcessInstance.class, queryOptions);\n        return persistenceRead.selectList(selectListDescriptor);\n    }\n\n    @Override\n    public SAProcessInstance getLastArchivedProcessInstance(final long processInstanceId) throws SBonitaReadException {\n        final SAProcessInstanceBuilderFactory processInstanceBuilderFact = BuilderFactory\n                .get(SAProcessInstanceBuilderFactory.class);\n        final FilterOption filterOption = new FilterOption(SAProcessInstance.class,\n                processInstanceBuilderFact.getSourceObjectIdKey(), processInstanceId);\n        final List<OrderByOption> orderByOptions = new ArrayList<>();\n        orderByOptions.add(new OrderByOption(SAProcessInstance.class, processInstanceBuilderFact.getArchiveDateKey(),\n                OrderByType.DESC));\n        orderByOptions.add(new OrderByOption(SAProcessInstance.class, processInstanceBuilderFact.getEndDateKey(),\n                OrderByType.DESC));\n        final QueryOptions queryOptions = new QueryOptions(0, 1, orderByOptions,\n                Collections.singletonList(filterOption), null);\n        final List<SAProcessInstance> processInstances = searchArchivedProcessInstances(queryOptions);\n        if (!processInstances.isEmpty()) {\n            return processInstances.get(0);\n        }\n        return null;\n    }\n\n    @Override\n    public long getNumberOfFailedProcessInstances(final QueryOptions queryOptions) throws SBonitaReadException {\n        return persistenceRead.getNumberOfEntities(SProcessInstance.class, FAILED, queryOptions, null);\n    }\n\n    @Override\n    public List<SProcessInstance> searchFailedProcessInstances(final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        return persistenceRead.searchEntity(SProcessInstance.class, FAILED, queryOptions, null);\n    }\n\n    @Override\n    public long getNumberOfProcessInstances(final long processDefinitionId) throws SBonitaReadException {\n        final Map<String, Object> inputParameters = new HashMap<>();\n        inputParameters.put(\"processDefinitionId\", processDefinitionId);\n        final SelectOneDescriptor<Long> countDescriptor = new SelectOneDescriptor<>(\n                \"countProcessInstancesOfProcessDefinition\", inputParameters,\n                SProcessInstance.class);\n        return persistenceRead.selectOne(countDescriptor);\n    }\n\n    @Override\n    public List<Long> getProcessInstanceIdsToRecover(Duration considerElementsOlderThan, QueryOptions queryOptions)\n            throws SBonitaReadException {\n        return persistenceRead.selectList(new SelectListDescriptor<>(\n                \"getProcessInstanceIdsToRecover\",\n                singletonMap(\"maxLastUpdate\",\n                        System.currentTimeMillis() - considerElementsOlderThan.toMillis()),\n                SProcessInstance.class, queryOptions));\n    }\n\n    @Override\n    public void setInterruptingEventId(long parentProcessInstanceId, long eventInstanceId)\n            throws SProcessInstanceNotFoundException, SProcessInstanceReadException,\n            SProcessInstanceModificationException {\n        updateProcess(getProcessInstance(parentProcessInstanceId),\n                new EntityUpdateDescriptor().addField(SProcessInstance.INTERRUPTING_EVENT_ID_KEY, eventInstanceId));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/impl/RefBusinessDataServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.impl;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.archive.ArchiveInsertRecord;\nimport org.bonitasoft.engine.archive.ArchiveService;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.core.process.instance.api.RefBusinessDataService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceCreationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceModificationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.business.data.SARefBusinessDataInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.archive.business.data.SARefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.business.data.SRefBusinessDataInstanceLogBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.business.data.SRefBusinessDataInstanceLogBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.recorder.SelectBusinessDataDescriptorBuilder;\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity;\nimport org.bonitasoft.engine.queriablelogger.model.builder.ActionType;\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class RefBusinessDataServiceImpl implements RefBusinessDataService {\n\n    private final EventService eventService;\n\n    private final Recorder recorder;\n\n    private final ReadPersistenceService persistenceRead;\n\n    private final QueriableLoggerService queriableLoggerService;\n\n    private final ArchiveService archiveService;\n\n    public RefBusinessDataServiceImpl(final Recorder recorder, final ReadPersistenceService persistenceRead,\n            final EventService eventService,\n            final QueriableLoggerService queriableLoggerService, final ArchiveService archiveService) {\n        this.recorder = recorder;\n        this.persistenceRead = persistenceRead;\n        this.eventService = eventService;\n        this.queriableLoggerService = queriableLoggerService;\n        this.archiveService = archiveService;\n    }\n\n    @Override\n    public SRefBusinessDataInstance getRefBusinessDataInstance(final String name, final long processInstanceId)\n            throws SRefBusinessDataInstanceNotFoundException, SBonitaReadException {\n        final SelectOneDescriptor<SRefBusinessDataInstance> descriptor = SelectBusinessDataDescriptorBuilder\n                .getSRefBusinessDataInstance(name,\n                        processInstanceId);\n        final SRefBusinessDataInstance ref = persistenceRead.selectOne(descriptor);\n        if (ref == null) {\n            throw new SRefBusinessDataInstanceNotFoundException(processInstanceId, name);\n        }\n        return ref;\n    }\n\n    @Override\n    public SRefBusinessDataInstance getFlowNodeRefBusinessDataInstance(final String name, final long flowNodeInstanceId)\n            throws SRefBusinessDataInstanceNotFoundException, SBonitaReadException {\n        final SelectOneDescriptor<SRefBusinessDataInstance> descriptor = SelectBusinessDataDescriptorBuilder\n                .getSFlowNodeRefBusinessDataInstance(name,\n                        flowNodeInstanceId);\n        final SRefBusinessDataInstance ref = persistenceRead.selectOne(descriptor);\n        if (ref == null) {\n            throw new SRefBusinessDataInstanceNotFoundException(flowNodeInstanceId, name);\n        }\n        return ref;\n    }\n\n    @Override\n    public SRefBusinessDataInstance addRefBusinessDataInstance(final SRefBusinessDataInstance instance)\n            throws SRefBusinessDataInstanceCreationException {\n        final SRefBusinessDataInstanceLogBuilder logBuilder = getQueriableLog(ActionType.CREATED,\n                NEW_REF_BUSINESS_DATA_INSTANCE_ADDED);\n        try {\n            recorder.recordInsert(new InsertRecord(instance), REF_BUSINESS_DATA_INSTANCE);\n            initiateLogBuilder(instance.getId(), SQueriableLog.STATUS_OK, logBuilder, \"addRefBusinessDataInstance\");\n        } catch (final SBonitaException sbe) {\n            initiateLogBuilder(instance.getId(), SQueriableLog.STATUS_FAIL, logBuilder, \"addRefBusinessDataInstance\");\n            throw new SRefBusinessDataInstanceCreationException(sbe);\n        }\n        return instance;\n    }\n\n    protected SRefBusinessDataInstanceLogBuilder getQueriableLog(final ActionType actionType, final String message) {\n        final SRefBusinessDataInstanceLogBuilder logBuilder = BuilderFactory\n                .get(SRefBusinessDataInstanceLogBuilderFactory.class).createNewInstance();\n        this.initializeLogBuilder(logBuilder, message);\n        this.updateLog(actionType, logBuilder);\n        return logBuilder;\n    }\n\n    protected <T extends SLogBuilder> void initializeLogBuilder(final T logBuilder, final String message) {\n        logBuilder.actionStatus(SQueriableLog.STATUS_FAIL).severity(SQueriableLogSeverity.INTERNAL).rawMessage(message);\n    }\n\n    protected <T extends HasCRUDEAction> void updateLog(final ActionType actionType, final T logBuilder) {\n        logBuilder.setActionType(actionType);\n    }\n\n    private void initiateLogBuilder(final long objectId, final int sQueriableLogStatus,\n            final SPersistenceLogBuilder logBuilder,\n            final String callerMethodName) {\n        logBuilder.actionScope(String.valueOf(objectId));\n        logBuilder.actionStatus(sQueriableLogStatus);\n        logBuilder.objectId(objectId);\n        final SQueriableLog log = logBuilder.build();\n        if (queriableLoggerService.isLoggable(log.getActionType(), log.getSeverity())) {\n            queriableLoggerService.log(this.getClass().getName(), callerMethodName, log);\n        }\n    }\n\n    @Override\n    public void updateRefBusinessDataInstance(final SSimpleRefBusinessDataInstance refBusinessDataInstance,\n            final Long dataId)\n            throws SRefBusinessDataInstanceModificationException {\n        final Map<String, Object> fields = new HashMap<>();\n        fields.put(\"dataId\", dataId);\n        updateRefBusinessDataInstance(refBusinessDataInstance, fields);\n    }\n\n    @Override\n    public void updateRefBusinessDataInstance(final SProcessMultiRefBusinessDataInstance refBusinessDataInstance,\n            final List<Long> dataIds)\n            throws SRefBusinessDataInstanceModificationException {\n        final Map<String, Object> fields = new HashMap<>();\n        fields.put(\"dataIds\", dataIds);\n        updateRefBusinessDataInstance(refBusinessDataInstance, fields);\n    }\n\n    private void updateRefBusinessDataInstance(final SRefBusinessDataInstance refBusinessDataInstance,\n            final Map<String, Object> fields)\n            throws SRefBusinessDataInstanceModificationException {\n        try {\n            recorder.recordUpdate(UpdateRecord.buildSetFields(refBusinessDataInstance, fields),\n                    REF_BUSINESS_DATA_INSTANCE);\n        } catch (final SRecorderException sre) {\n            throw new SRefBusinessDataInstanceModificationException(sre);\n        }\n    }\n\n    @Override\n    public int getNumberOfDataOfMultiRefBusinessData(final String name, final long processInstanceId)\n            throws SBonitaReadException {\n        final SelectOneDescriptor<Integer> descriptor = SelectBusinessDataDescriptorBuilder\n                .getNumberOfDataOfMultiRefBusinessData(name,\n                        processInstanceId);\n        return persistenceRead.selectOne(descriptor);\n    }\n\n    @Override\n    public List<SRefBusinessDataInstance> getRefBusinessDataInstances(final long processInstanceId,\n            final int startIndex, final int maxResults)\n            throws SBonitaReadException {\n        final SelectListDescriptor<SRefBusinessDataInstance> descriptor = SelectBusinessDataDescriptorBuilder\n                .getSRefBusinessDataInstances(processInstanceId,\n                        startIndex, maxResults);\n        return persistenceRead.selectList(descriptor);\n    }\n\n    @Override\n    public SARefBusinessDataInstance getSARefBusinessDataInstance(final String name, final long processInstanceId)\n            throws SRefBusinessDataInstanceNotFoundException, SBonitaReadException {\n        final SelectOneDescriptor<SARefBusinessDataInstance> descriptor = SelectBusinessDataDescriptorBuilder\n                .getSARefBusinessDataInstance(name,\n                        processInstanceId);\n        final SARefBusinessDataInstance ref = persistenceRead.selectOne(descriptor);\n        if (ref == null) {\n            throw new SRefBusinessDataInstanceNotFoundException(processInstanceId, name);\n        }\n        return ref;\n    }\n\n    @Override\n    public SARefBusinessDataInstance getSAFlowNodeRefBusinessDataInstance(final String name,\n            final long flowNodeInstanceId)\n            throws SRefBusinessDataInstanceNotFoundException, SBonitaReadException {\n        final SelectOneDescriptor<SARefBusinessDataInstance> descriptor = SelectBusinessDataDescriptorBuilder\n                .getSAFlowNodeRefBusinessDataInstance(name,\n                        flowNodeInstanceId);\n        final SARefBusinessDataInstance ref = persistenceRead.selectOne(descriptor);\n        if (ref == null) {\n            throw new SRefBusinessDataInstanceNotFoundException(flowNodeInstanceId, name);\n        }\n        return ref;\n    }\n\n    @Override\n    public void archiveRefBusinessDataInstance(SRefBusinessDataInstance businessDataInstance)\n            throws SObjectModificationException {\n        try {\n            final SARefBusinessDataInstance saRefBusinessDataInstance = BuilderFactory\n                    .get(SARefBusinessDataInstanceBuilderFactory.class)\n                    .createNewInstance(businessDataInstance).done();\n            final ArchiveInsertRecord archiveInsertRecord = new ArchiveInsertRecord(saRefBusinessDataInstance);\n            archiveService.recordInsert(0L, archiveInsertRecord); // no archived date (nor sourceObjectId) in this table, as it is not necessary (archived only once at end of process execution)\n        } catch (final SRecorderException e) {\n            throw new SObjectModificationException(\"Unable to archive RefBusinessDataInstance\", e);\n        }\n    }\n\n    @Override\n    public void deleteArchivedRefBusinessDataInstance(long processInstanceId) throws SObjectModificationException {\n        try {\n            archiveService.deleteFromQuery(\"deleteArchivedRefBizDataForProcessInstance\",\n                    Collections.singletonMap(\"processInstanceId\", processInstanceId));\n            //            archiveService.deleteFromQuery(\"deleteArchivedMultiRefBizDataForProcessInstance\",\n            //                    Collections.<String, Object> singletonMap(\"processInstanceId\", processInstanceId));\n        } catch (SRecorderException e) {\n            throw new SObjectModificationException(\n                    \"Unable to delete SARefBusinessDataInstance's for processInstanceId: \" + processInstanceId, e);\n        }\n    }\n\n    @Override\n    public void deleteArchivedRefBusinessDataInstance(List<Long> processInstanceIds)\n            throws SObjectModificationException {\n        try {\n            archiveService.deleteFromQuery(\"deleteArchivedRefBizDataForProcessInstances\",\n                    Collections.singletonMap(\"processInstanceIds\", processInstanceIds));\n        } catch (SRecorderException e) {\n            throw new SObjectModificationException(\n                    \"Unable to delete SARefBusinessDataInstance's for processInstanceIds: \" + processInstanceIds, e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SABPMFailure.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.ArchivedPersistentObject;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.hibernate.annotations.Type;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@Builder\n@Entity\n@Table(name = \"arch_bpm_failure\")\npublic class SABPMFailure implements ArchivedPersistentObject {\n\n    @Id\n    private long id;\n    private long processDefinitionId;\n    private long processInstanceId;\n    private long rootProcessInstanceId;\n    private long flowNodeInstanceId;\n    private String scope;\n    private String context;\n    private String errorMessage;\n    @Type(type = \"materialized_clob\")\n    private String stackTrace;\n    private long failureDate;\n    private long archiveDate;\n    private long sourceObjectId;\n\n    public SABPMFailure(final SBPMFailure bpmFailure) {\n        this.sourceObjectId = bpmFailure.getId();\n        this.processDefinitionId = bpmFailure.getProcessDefinitionId();\n        this.processInstanceId = bpmFailure.getProcessInstanceId();\n        this.rootProcessInstanceId = bpmFailure.getRootProcessInstanceId();\n        this.flowNodeInstanceId = bpmFailure.getFlowNodeInstanceId();\n        this.scope = bpmFailure.getScope();\n        this.context = bpmFailure.getContext();\n        this.errorMessage = bpmFailure.getErrorMessage();\n        this.stackTrace = bpmFailure.getStackTrace();\n        this.failureDate = bpmFailure.getFailureDate();\n    }\n\n    @Override\n    public Class<? extends PersistentObject> getPersistentObjectInterface() {\n        return SBPMFailure.class;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SAbstractConnectorInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport javax.persistence.EnumType;\nimport javax.persistence.Enumerated;\nimport javax.persistence.Id;\nimport javax.persistence.MappedSuperclass;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@SuperBuilder\n@MappedSuperclass\npublic abstract class SAbstractConnectorInstance implements PersistentObject {\n\n    public static final String ID_KEY = \"id\";\n    public static final String NAME_KEY = \"name\";\n    public static final String CONTAINER_ID_KEY = \"containerId\";\n    public static final String CONTAINER_TYPE_KEY = \"containerType\";\n    public static final String CONNECTOR_ID_KEY = \"connectorId\";\n    public static final String VERSION_KEY = \"version\";\n    public static final String ACTIVATION_EVENT_KEY = \"activationEvent\";\n    public static final String STATE_KEY = \"state\";\n    public static final String EXECUTION_ORDER = \"executionOrder\";\n    public static final String FLOWNODE_TYPE = \"flowNode\";\n    public static final String PROCESS_TYPE = \"process\";\n\n    @Id\n    private long id;\n    private String name;\n    private long containerId;\n    private String connectorId;\n    private String version;\n    @Enumerated(EnumType.STRING)\n    private ConnectorEvent activationEvent;\n    private String state;\n    private String containerType;\n    private int executionOrder;\n\n    public SAbstractConnectorInstance(final String name, final long containerId, final String containerType,\n            final String connectorId, final String version,\n            final ConnectorEvent activationEvent) {\n\n        this.name = name;\n        this.containerId = containerId;\n        this.containerType = containerType;\n        this.connectorId = connectorId;\n        this.version = version;\n        this.activationEvent = activationEvent;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SActivityInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\npublic abstract class SActivityInstance extends SFlowNodeInstance {\n\n    private long abortedByBoundary = 0;\n\n    public SActivityInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId,\n            final long parentContainerId,\n            final long logicalGroup1, final long logicalGroup2) {\n        super(name, flowNodeDefinitionId, rootContainerId, parentContainerId, logicalGroup1, logicalGroup2);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SAutomaticTaskInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\n\n/**\n * @author Baptiste Mesta\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"auto\")\npublic class SAutomaticTaskInstance extends SActivityInstance {\n\n    public SAutomaticTaskInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId,\n            final long parentContainerId,\n            final long logicalGroup1, final long logicalGroup2) {\n        super(name, flowNodeDefinitionId, rootContainerId, parentContainerId, logicalGroup1, logicalGroup2);\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.AUTOMATIC_TASK;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SBPMFailure.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport java.time.Instant;\n\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.hibernate.annotations.Type;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@Builder\n@Entity\n@Table(name = \"bpm_failure\")\npublic class SBPMFailure implements PersistentObject {\n\n    @Id\n    private long id;\n    private long processDefinitionId;\n    private long processInstanceId;\n    private long rootProcessInstanceId;\n    private long flowNodeInstanceId;\n    private String scope;\n    private String context;\n    private String errorMessage;\n    @Type(type = \"materialized_clob\")\n    private String stackTrace;\n    @Builder.Default\n    private long failureDate = Instant.now().toEpochMilli();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SCallActivityInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"call\")\npublic class SCallActivityInstance extends SActivityInstance {\n\n    public SCallActivityInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId,\n            final long parentContainerId,\n            final long logicalGroup1, final long logicalGroup2) {\n        super(name, flowNodeDefinitionId, rootContainerId, parentContainerId, logicalGroup1, logicalGroup2);\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.CALL_ACTIVITY;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SConnectorInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport javax.persistence.*;\n\nimport lombok.*;\nimport lombok.experimental.SuperBuilder;\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\n@Data\n@SuperBuilder\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@Table(name = \"connector_instance\")\npublic class SConnectorInstance extends SAbstractConnectorInstance {\n\n    public SConnectorInstance(final String name, final long containerId, final String containerType,\n            final String connectorId, final String version,\n            final ConnectorEvent activationEvent) {\n        super(name, containerId, containerType, connectorId, version, activationEvent);\n    }\n\n    public SConnectorInstance() {\n        super();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SConnectorInstanceWithFailureInfo.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport javax.persistence.Cacheable;\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.Table;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\nimport org.hibernate.annotations.Type;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@SuperBuilder\n@Entity\n@Table(name = \"connector_instance\")\n@Cacheable(false)\npublic class SConnectorInstanceWithFailureInfo extends SAbstractConnectorInstance {\n\n    public static final String EXCEPTION_MESSAGE = \"exceptionMessage\";\n    public static final String STACK_TRACE = \"stackTrace\";\n    @Column\n    @Type(type = \"materialized_clob\")\n    private String stackTrace;\n    private String exceptionMessage;\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SFlowElementsContainerType.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\n/**\n * @author Baptiste Mesta\n */\npublic enum SFlowElementsContainerType {\n\n    PROCESS, FLOWNODE\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SFlowNodeInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport java.util.Date;\n\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorColumn;\nimport javax.persistence.Entity;\nimport javax.persistence.EnumType;\nimport javax.persistence.Enumerated;\nimport javax.persistence.Id;\nimport javax.persistence.Inheritance;\nimport javax.persistence.InheritanceType;\nimport javax.persistence.Table;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Feng Hui\n * @author Baptiste Mesta\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\n@Data\n@NoArgsConstructor\n@Entity\n@Table(name = \"flownode_instance\")\n@DiscriminatorColumn(name = \"kind\")\n@Inheritance(strategy = InheritanceType.SINGLE_TABLE)\npublic abstract class SFlowNodeInstance implements PersistentObject {\n\n    @Id\n    private long id;\n    private long flowNodeDefinitionId;\n    private long rootContainerId;\n    private long parentContainerId;\n    private String name;\n    private String displayName;\n    private String displayDescription;\n    private int stateId;\n    private String stateName;\n    @Column(name = \"prev_state_id\")\n    private int previousStateId;\n    private boolean terminal;\n    private boolean stable;\n    @Enumerated(EnumType.STRING)\n    private SStateCategory stateCategory = SStateCategory.NORMAL;\n    private long reachedStateDate;\n    private long lastUpdateDate;\n    //process definition id\n    private long logicalGroup1;\n    //root process instance id\n    private long logicalGroup2;\n    //parent activity instance id\n    private long logicalGroup3;\n    //parent process instance id\n    private long logicalGroup4;\n    private int tokenCount = 0;\n    private String description;\n    @Column(name = \"loop_counter\")\n    protected int loopCounter;\n    /**\n     * id of the user who originally executed the flow node\n     */\n    @Column\n    private long executedBy;\n    /**\n     * id of the user (delegate) who executed the flow node for the original executer\n     */\n    @Column\n    private long executedBySubstitute;\n    @Column(name = \"state_executing\")\n    private boolean stateExecuting;\n\n    public SFlowNodeInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId,\n            final long parentContainerId,\n            final long logicalGroup1, final long logicalGroup2) {\n        this.name = name;\n        this.rootContainerId = rootContainerId;\n        this.parentContainerId = parentContainerId;\n        this.logicalGroup1 = logicalGroup1;\n        this.logicalGroup2 = logicalGroup2;\n        this.flowNodeDefinitionId = flowNodeDefinitionId;\n        long now = new Date().getTime();\n        lastUpdateDate = now;\n        reachedStateDate = now;\n    }\n\n    public long getProcessDefinitionId() {\n        return logicalGroup1;\n    }\n\n    /**\n     * @return the root process instance is the top level process containing this element\n     */\n    public long getRootProcessInstanceId() {\n        return logicalGroup2;\n    }\n\n    /**\n     * @return\n     *         the id of the activity instance containing this element or 0 if this element is not contained in an\n     *         activity\n     */\n    public long getParentActivityInstanceId() {\n        return logicalGroup3;\n    }\n\n    /**\n     * @return\n     *         the id of the process instance containing this element\n     */\n    public long getParentProcessInstanceId() {\n        return logicalGroup4;\n    }\n\n    /**\n     * @return\n     *         the type of the element that contains this element\n     */\n    public SFlowElementsContainerType getParentContainerType() {\n        return getParentActivityInstanceId() <= 0 ? SFlowElementsContainerType.PROCESS\n                : SFlowElementsContainerType.FLOWNODE;\n    }\n\n    public long getLogicalGroup(final int index) {\n        switch (index) {\n            case 0:\n                return logicalGroup1;\n            case 1:\n                return logicalGroup2;\n            case 2:\n                return logicalGroup3;\n            case 3:\n                return logicalGroup4;\n            default:\n                throw new IllegalArgumentException(\"Invalid index: the index must be 0, 1, 2 or 3\");\n        }\n    }\n\n    public boolean isAborting() {\n        return SStateCategory.ABORTING.equals(stateCategory);\n    }\n\n    public boolean isCanceling() {\n        return SStateCategory.CANCELLING.equals(stateCategory);\n    }\n\n    public void setLogicalGroup(final int index, final long value) {\n        switch (index) {\n            case 0:\n                logicalGroup1 = value;\n                break;\n            case 1:\n                logicalGroup2 = value;\n                break;\n            case 2:\n                logicalGroup3 = value;\n                break;\n            case 3:\n                logicalGroup4 = value;\n                break;\n            default:\n                throw new IllegalArgumentException(\"Invalid index: the index must be 0, 1, 2 or 3\");\n        }\n    }\n\n    public SFlowElementsContainerType getContainerType() {\n        return SFlowElementsContainerType.FLOWNODE;\n    }\n\n    /**\n     * @return true if the execution must continues automatically on abort or cancel the parent process instance\n     */\n    public boolean mustExecuteOnAbortOrCancelProcess() {\n        return isStable() && !isTerminal();\n    }\n\n    public abstract SFlowNodeType getType();\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SFlowNodeInstanceStateCounter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\n/***\n * Represents flownode state counters for a given process instance. Only serves query purposes (like a view), not\n * persisted.\n *\n * @author Emmanuel Duchastenier\n */\npublic class SFlowNodeInstanceStateCounter {\n\n    private String flownodeName;\n    private String stateName;\n    private Long numberOf;\n\n    public SFlowNodeInstanceStateCounter(String flownodeName, String stateName, Long numberOf) {\n        this.flownodeName = flownodeName;\n        this.stateName = stateName;\n        this.numberOf = numberOf;\n    }\n\n    public String getFlownodeName() {\n        return flownodeName;\n    }\n\n    public String getStateName() {\n        return stateName;\n    }\n\n    public Long getNumberOf() {\n        return numberOf;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SGatewayInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport javax.persistence.*;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.definition.model.SGatewayType;\nimport org.bonitasoft.engine.core.process.instance.api.GatewayInstanceService;\n\n/**\n * @author Feng Hui\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"gate\")\npublic class SGatewayInstance extends SFlowNodeInstance {\n\n    @Column\n    @Enumerated(EnumType.STRING)\n    private SGatewayType gatewayType;\n    private String hitBys = \"\";\n\n    public SGatewayInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId,\n            final long parentContainerId,\n            final SGatewayType gatewayType, final long logicalGroup1, final long logicalGroup2) {\n        super(name, flowNodeDefinitionId, rootContainerId, parentContainerId, logicalGroup1, logicalGroup2);\n        this.gatewayType = gatewayType;\n    }\n\n    public SGatewayInstance(SGatewayInstance gatewayInstance) {\n        super(gatewayInstance.getName(), gatewayInstance.getFlowNodeDefinitionId(),\n                gatewayInstance.getRootContainerId(),\n                gatewayInstance.getParentContainerId(),\n                gatewayInstance.getLogicalGroup(0), gatewayInstance.getLogicalGroup(1));\n        setLogicalGroup(2, gatewayInstance.getLogicalGroup(2));\n        setLogicalGroup(3, gatewayInstance.getLogicalGroup(3));\n        this.gatewayType = gatewayInstance.getGatewayType();\n        setStateId(gatewayInstance.getStateId());\n    }\n\n    public boolean isFinished() {\n        return hitBys != null && hitBys.startsWith(GatewayInstanceService.FINISH);\n    }\n\n    public SFlowNodeType getType() {\n        return SFlowNodeType.GATEWAY;\n    }\n\n    @Override\n    public boolean mustExecuteOnAbortOrCancelProcess() {\n        //always call execute when abort the gateway because the gateway that merge wait for the flow node in an unstable state\n        // this fact is a little strange but a full cleanup of the flownode execution mechanism should be done in order to change that\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SHumanTaskInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\n\n/**\n * @author Baptiste Mesta\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\npublic abstract class SHumanTaskInstance extends SActivityInstance {\n\n    private long actorId;\n    private long assigneeId;\n    private Long expectedEndDate;\n    private long claimedDate;\n    private STaskPriority priority;\n\n    public SHumanTaskInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId,\n            final long parenteContainerId,\n            final long actorId, final STaskPriority priority, final long logicalGroup1, final long logicalGroup2) {\n        super(name, flowNodeDefinitionId, rootContainerId, parenteContainerId, logicalGroup1, logicalGroup2);\n        this.actorId = actorId;\n        this.priority = priority;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SLoopActivityInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\n\n/**\n * @author Matthieu Chaffotte\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"loop\")\npublic class SLoopActivityInstance extends SActivityInstance {\n\n    @Column(name = \"loop_max\")\n    private int loopMax;\n\n    public SLoopActivityInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId,\n            final long parentContainerId,\n            final long logicalGroup1, final long logicalGroup2) {\n        super(name, flowNodeDefinitionId, rootContainerId, parentContainerId, logicalGroup1, logicalGroup2);\n        loopMax = -1;\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.LOOP_ACTIVITY;\n    }\n\n    @Override\n    public boolean mustExecuteOnAbortOrCancelProcess() {\n        // it's not necessary to execute it because this will be done when the child reaches the aborted state\n        return false;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SManualTaskInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"manual\")\npublic class SManualTaskInstance extends SHumanTaskInstance {\n\n    public SManualTaskInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId,\n            final long parenteContainerId,\n            final long actorId, final STaskPriority priority, final long logicalGroup1, final long logicalGroup2) {\n        super(name, flowNodeDefinitionId, rootContainerId, parenteContainerId, actorId, priority, logicalGroup1,\n                logicalGroup2);\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.MANUAL_TASK;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SMultiInstanceActivityInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\n\n/**\n * @author Baptiste Mesta\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"multi\")\npublic class SMultiInstanceActivityInstance extends SActivityInstance {\n\n    private boolean sequential;\n    private String loopDataInputRef;\n    private String loopDataOutputRef;\n    private String dataInputItemRef;\n    private String dataOutputItemRef;\n    @Column(name = \"nbActiveInst\")\n    private int numberOfActiveInstances;\n    @Column(name = \"nbCompletedInst\")\n    private int numberOfCompletedInstances;\n    @Column(name = \"nbTerminatedInst\")\n    private int numberOfTerminatedInstances;\n    private int loopCardinality;\n\n    public SMultiInstanceActivityInstance(final String name, final long flowNodeDefinitionId,\n            final long rootContainerId, final long parentContainerId,\n            final long processDefinitionId, final long rootProcessInstanceId, final boolean isSequential) {\n        super(name, flowNodeDefinitionId, rootContainerId, parentContainerId, processDefinitionId,\n                rootProcessInstanceId);\n        sequential = isSequential;\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.MULTI_INSTANCE_ACTIVITY;\n    }\n\n    public int getNumberOfInstances() {\n        return numberOfActiveInstances + numberOfCompletedInstances + numberOfTerminatedInstances;\n    }\n\n    @Override\n    public boolean mustExecuteOnAbortOrCancelProcess() {\n        // it's not necessary to execute it because this will be done when the last child reaches the aborted state\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SPendingActivityMapping.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * Used to get pending activities of a user.\n * Link activity to\n * - an actor if the activity is not filtered\n * - a user if the activity is filtered\n * When the activity is created we insert one or more of this object:\n * - if the activity is not filtered we insert one of them for each actor of the activity with inside the actorId\n * - if the activity is filtered we execute those filter and insert a one of them for each user that is filtered\n *\n * @author Baptiste Mesta\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@Builder\n@Entity\n@Table(name = \"pending_mapping\")\npublic class SPendingActivityMapping implements PersistentObject {\n\n    public static final String ACTOR_ID = \"actorId\";\n    public static final String ACTIVITY_ID = \"activityId\";\n    public static final String USER_ID = \"userId\";\n    @Id\n    private long id;\n    /**\n     * the id of the activity\n     */\n    private long activityId;\n    /**\n     * the id of the actor or -1 if the mapping is on user\n     */\n    @Builder.Default\n    private long actorId = -1;\n    /**\n     * the id of the user or -1 if the mapping is on actor\n     */\n    @Builder.Default\n    private long userId = -1;\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SProcessInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport java.util.Date;\n\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.EnumType;\nimport javax.persistence.Enumerated;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@Builder\n@Entity\n@Table(name = \"process_instance\")\npublic class SProcessInstance implements PersistentObject {\n\n    private static final long DEFAULT_INTERRUPTING_EVENT_ID = -1L;\n    public static final String STRING_INDEX_KEY = \"stringIndex\";\n    public static final String STRING_INDEX_1_KEY = \"stringIndex1\";\n    public static final String STRING_INDEX_2_KEY = \"stringIndex2\";\n    public static final String STRING_INDEX_3_KEY = \"stringIndex3\";\n    public static final String STRING_INDEX_4_KEY = \"stringIndex4\";\n    public static final String STRING_INDEX_5_KEY = \"stringIndex5\";\n    public static final String LAST_UPDATE_KEY = \"lastUpdate\";\n    public static final String INTERRUPTING_EVENT_ID_KEY = \"interruptingEventId\";\n    public static final String ID_KEY = \"id\";\n    public static final String ROOT_PROCESS_INSTANCE_ID_KEY = \"rootProcessInstanceId\";\n    public static final String NAME_KEY = \"name\";\n    public static final String PROCESSDEF_ID_KEY = \"processDefinitionId\";\n    public static final String STATE_ID_KEY = \"stateId\";\n    public static final String STATE_CATEGORY_KEY = \"stateCategory\";\n    public static final String CONTAINER_ID_KEY = \"containerId\";\n    public static final String END_DATE_KEY = \"endDate\";\n    public static final String STARTED_BY_KEY = \"startedBy\";\n    public static final String STARTED_BY_SUBSTITUTE_KEY = \"startedBySubstitute\";\n    public static final String START_DATE_KEY = \"startDate\";\n    public static final String CALLER_ID = \"callerId\";\n    @Id\n    private long id;\n    private String name;\n    private long processDefinitionId;\n    private String description;\n    @Builder.Default\n    private long startDate = new Date().getTime();\n    /**\n     * id of the user who originally started the process\n     */\n    private long startedBy;\n    /**\n     * id of the user (delegate) who started the process for the original starter\n     */\n    private long startedBySubstitute;\n    private long endDate;\n    private int stateId;\n    @Builder.Default\n    @Enumerated(EnumType.STRING)\n    private SStateCategory stateCategory = SStateCategory.NORMAL;\n    private long lastUpdate;\n    private long containerId;\n    @Builder.Default\n    private long rootProcessInstanceId = -1;\n    @Builder.Default\n    private long callerId = -1;\n    /**\n     * The caller's SFlowNodeType if the it's called by a call activity or sub-process, null otherwise\n     */\n    @Column\n    @Enumerated(EnumType.STRING)\n    private SFlowNodeType callerType;\n    /**\n     * Id of the end error event that interrupted the process instance or -1 if the process was not interrupted by a end\n     * error event\n     */\n    @Builder.Default\n    @Column\n    private long interruptingEventId = DEFAULT_INTERRUPTING_EVENT_ID;\n    @Column\n    private String stringIndex1;\n    @Column\n    private String stringIndex2;\n    @Column\n    private String stringIndex3;\n    @Column\n    private String stringIndex4;\n    @Column\n    private String stringIndex5;\n\n    public SProcessInstance(final String name, final long processDefinitionId) {\n        this.name = name;\n        this.processDefinitionId = processDefinitionId;\n    }\n\n    public SProcessInstance(final SProcessDefinition definition) {\n        name = definition.getName();\n        processDefinitionId = definition.getId();\n        description = definition.getDescription();\n    }\n\n    @Override\n    public void setId(final long id) {\n        this.id = id;\n        if (rootProcessInstanceId == -1) {\n            rootProcessInstanceId = id;\n        }\n    }\n\n    public SFlowElementsContainerType getContainerType() {\n        return SFlowElementsContainerType.PROCESS;\n    }\n\n    public boolean hasBeenInterruptedByEvent() {\n        return getInterruptingEventId() != -1;\n    }\n\n    /**\n     * Determines if this instance is a root process instance. That is, it is neither a process called by a call\n     * activity, neither a sub-process\n     *\n     * @return true if it's a root process instance; false otherwise.\n     */\n    public boolean isRootInstance() {\n        return callerId <= 0;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SReceiveTaskInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\n\n/**\n * @author Julien Molinaro\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"receive\")\npublic class SReceiveTaskInstance extends SActivityInstance {\n\n    public SReceiveTaskInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId,\n            final long parentContainerId,\n            final long logicalGroup1, final long logicalGroup2) {\n        super(name, flowNodeDefinitionId, rootContainerId, parentContainerId, logicalGroup1, logicalGroup2);\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.RECEIVE_TASK;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SSendTaskInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\n\n/**\n * @author Baptiste Mesta\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"send\")\npublic class SSendTaskInstance extends SActivityInstance {\n\n    public SSendTaskInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId,\n            final long parentContainerId,\n            final long logicalGroup1, final long logicalGroup2) {\n        super(name, flowNodeDefinitionId, rootContainerId, parentContainerId, logicalGroup1, logicalGroup2);\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.SEND_TASK;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SStateCategory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic enum SStateCategory {\n    /**\n     * The state is part of normal states\n     */\n    NORMAL,\n\n    /**\n     * The state is part of aborting states\n     */\n    ABORTING,\n\n    /**\n     * The state is part of canceling states\n     */\n    CANCELLING\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SSubProcessActivityInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"subProc\")\npublic class SSubProcessActivityInstance extends SActivityInstance {\n\n    private boolean triggeredByEvent;\n\n    public SSubProcessActivityInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId,\n            final long parentContainerId,\n            final long logicalGroup1, final long logicalGroup2, final boolean triggeredByEvent) {\n        super(name, flowNodeDefinitionId, rootContainerId, parentContainerId, logicalGroup1, logicalGroup2);\n        this.triggeredByEvent = triggeredByEvent;\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.SUB_PROCESS;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/STaskPriority.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport org.bonitasoft.engine.commons.EnumToObjectConvertible;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic enum STaskPriority implements EnumToObjectConvertible {\n\n    LOWEST, UNDER_NORMAL, NORMAL, ABOVE_NORMAL, HIGHEST;\n\n    @Override\n    public int fromEnum() {\n        return ordinal();\n    }\n\n    public static STaskPriority fromOrdinal(int n) {\n        for (STaskPriority enumValue : values()) {\n            if (enumValue.ordinal() == n) {\n                return enumValue;\n            }\n        }\n        throw new IllegalArgumentException(\"Invalid ordinal value for STaskPriority\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SUserTaskInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model;\n\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"user\")\npublic class SUserTaskInstance extends SHumanTaskInstance {\n\n    public SUserTaskInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId,\n            final long parentContainerId,\n            final long actorId, final STaskPriority priority, final long logicalGroup1, final long logicalGroup2) {\n        super(name, flowNodeDefinitionId, rootContainerId, parentContainerId, actorId, priority, logicalGroup1,\n                logicalGroup2);\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.USER_TASK;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/SAActivityInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive;\n\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\npublic abstract class SAActivityInstance extends SAFlowNodeInstance {\n\n    public SAActivityInstance(final SActivityInstance activityInstance) {\n        super(activityInstance);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/SAAutomaticTaskInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive;\n\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Baptiste Mesta\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"auto\")\npublic class SAAutomaticTaskInstance extends SAActivityInstance {\n\n    public SAAutomaticTaskInstance(final SAutomaticTaskInstance sAutomaticTaskInstance) {\n        super(sAutomaticTaskInstance);\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.AUTOMATIC_TASK;\n    }\n\n    @Override\n    public Class<? extends PersistentObject> getPersistentObjectInterface() {\n        return SAutomaticTaskInstance.class;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/SACallActivityInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive;\n\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"call\")\npublic class SACallActivityInstance extends SAActivityInstance {\n\n    public SACallActivityInstance(final SCallActivityInstance activityInstance) {\n        super(activityInstance);\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.CALL_ACTIVITY;\n    }\n\n    @Override\n    public Class<? extends PersistentObject> getPersistentObjectInterface() {\n        return SCallActivityInstance.class;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/SAConnectorInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive;\n\nimport javax.persistence.Entity;\nimport javax.persistence.EnumType;\nimport javax.persistence.Enumerated;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstance;\nimport org.bonitasoft.engine.persistence.ArchivedPersistentObject;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n@Data\n@NoArgsConstructor\n@Entity\n@Table(name = \"arch_connector_instance\")\npublic class SAConnectorInstance implements ArchivedPersistentObject {\n\n    private static final String FLOWNODE_TYPE = \"flowNode\";\n    private static final String PROCESS_TYPE = \"process\";\n    @Id\n    private long id;\n    private long archiveDate;\n    private long sourceObjectId;\n    private String name;\n    private long containerId;\n    private String connectorId;\n    private String version;\n    @Enumerated(EnumType.STRING)\n    private ConnectorEvent activationEvent;\n    private String state;\n    private String containerType;\n\n    public SAConnectorInstance(final SConnectorInstance connectorInstance) {\n        sourceObjectId = connectorInstance.getId();\n        name = connectorInstance.getName();\n        containerId = connectorInstance.getContainerId();\n        connectorId = connectorInstance.getConnectorId();\n        version = connectorInstance.getVersion();\n        activationEvent = connectorInstance.getActivationEvent();\n        state = connectorInstance.getState();\n        containerType = connectorInstance.getContainerType();\n    }\n\n    @Override\n    public Class<? extends PersistentObject> getPersistentObjectInterface() {\n        return SConnectorInstance.class;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/SAFlowNodeInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive;\n\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorColumn;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Inheritance;\nimport javax.persistence.InheritanceType;\nimport javax.persistence.Table;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.persistence.ArchivedPersistentObject;\n\n@Data\n@NoArgsConstructor\n@Entity\n@Table(name = \"arch_flownode_instance\")\n@DiscriminatorColumn(name = \"kind\")\n@Inheritance(strategy = InheritanceType.SINGLE_TABLE)\npublic abstract class SAFlowNodeInstance implements ArchivedPersistentObject {\n\n    @Id\n    private long id;\n    private long archiveDate;\n    private long sourceObjectId;\n    private String name;\n    private long rootContainerId;\n    private long parentContainerId;\n    private boolean aborting;\n    private long logicalGroup1;\n    private long logicalGroup2;\n    private long logicalGroup3;\n    private long logicalGroup4;\n    private int stateId;\n    private String stateName;\n    private boolean terminal;\n    private boolean stable;\n    private long reachedStateDate;\n    private long lastUpdateDate;\n    private String displayDescription;\n    private String displayName;\n    private String description;\n    private long executedBy;\n    private long executedBySubstitute;\n    //The field is mapped on the object and is also used for the discriminator, that is why it is not insertable and updatable\n    @Column(insertable = false, updatable = false)\n    private String kind;\n    @Column(name = \"flownodeDefinitionId\")\n    private long flowNodeDefinitionId;\n\n    public SAFlowNodeInstance(final SFlowNodeInstance flowNodeInstance) {\n        sourceObjectId = flowNodeInstance.getId();\n        name = flowNodeInstance.getName();\n        rootContainerId = flowNodeInstance.getRootContainerId();\n        parentContainerId = flowNodeInstance.getParentContainerId();\n        logicalGroup1 = flowNodeInstance.getLogicalGroup(0);\n        logicalGroup2 = flowNodeInstance.getLogicalGroup(1);\n        logicalGroup3 = flowNodeInstance.getLogicalGroup(2);\n        logicalGroup4 = flowNodeInstance.getLogicalGroup(3);\n        stateId = flowNodeInstance.getStateId();\n        stateName = flowNodeInstance.getStateName();\n        reachedStateDate = flowNodeInstance.getReachedStateDate();\n        lastUpdateDate = flowNodeInstance.getLastUpdateDate();\n        terminal = flowNodeInstance.isTerminal();\n        stable = flowNodeInstance.isStable();\n        displayName = flowNodeInstance.getDisplayName();\n        displayDescription = flowNodeInstance.getDisplayDescription();\n        description = flowNodeInstance.getDescription();\n        executedBy = flowNodeInstance.getExecutedBy();\n        executedBySubstitute = flowNodeInstance.getExecutedBySubstitute();\n        flowNodeDefinitionId = flowNodeInstance.getFlowNodeDefinitionId();\n    }\n\n    public long getProcessDefinitionId() {\n        return logicalGroup1;\n    }\n\n    public long getRootProcessInstanceId() {\n        return logicalGroup2;\n    }\n\n    public long getParentActivityInstanceId() {\n        return logicalGroup3;\n    }\n\n    public long getParentProcessInstanceId() {\n        return logicalGroup4;\n    }\n\n    public void setLogicalGroup(final int index, final long id) {\n        switch (index) {\n            case 0:\n                logicalGroup1 = id;\n                break;\n            case 1:\n                logicalGroup2 = id;\n                break;\n            case 2:\n                logicalGroup3 = id;\n                break;\n            case 3:\n                logicalGroup4 = id;\n                break;\n            default:\n                throw new IndexOutOfBoundsException(\"Index out of range for setLogicalGroup: \" + index);\n        }\n    }\n\n    public long getLogicalGroup(final int index) {\n        switch (index) {\n            case 0:\n                return logicalGroup1;\n            case 1:\n                return logicalGroup2;\n            case 2:\n                return logicalGroup3;\n            case 3:\n                return logicalGroup4;\n            default:\n                throw new IllegalArgumentException(\"Invalid index: must be 0, 1, 2 or 3\");\n        }\n    }\n\n    abstract public SFlowNodeType getType();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/SAGatewayInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive;\n\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\nimport javax.persistence.EnumType;\nimport javax.persistence.Enumerated;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.definition.model.SGatewayType;\nimport org.bonitasoft.engine.core.process.instance.model.SGatewayInstance;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"gate\")\npublic class SAGatewayInstance extends SAFlowNodeInstance {\n\n    @Column\n    @Enumerated(EnumType.STRING)\n    private SGatewayType gatewayType;\n    private String hitBys = \"\";\n\n    public SAGatewayInstance(final SGatewayInstance sGatewayInstance) {\n        super(sGatewayInstance);\n        gatewayType = sGatewayInstance.getGatewayType();\n        hitBys = sGatewayInstance.getHitBys();\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.GATEWAY;\n    }\n\n    @Override\n    public Class<? extends PersistentObject> getPersistentObjectInterface() {\n        return SGatewayInstance.class;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/SAHumanTaskInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive;\n\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.STaskPriority;\n\n/**\n * @author Baptiste Mesta\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\npublic abstract class SAHumanTaskInstance extends SAActivityInstance {\n\n    private long actorId;\n    private long assigneeId;\n    private long claimedDate;\n    private Long expectedEndDate;\n    private STaskPriority priority;\n\n    public SAHumanTaskInstance(final SHumanTaskInstance sHumanTaskInstance) {\n        super(sHumanTaskInstance);\n        actorId = sHumanTaskInstance.getActorId();\n        assigneeId = sHumanTaskInstance.getAssigneeId();\n        priority = sHumanTaskInstance.getPriority();\n        expectedEndDate = sHumanTaskInstance.getExpectedEndDate();\n        claimedDate = sHumanTaskInstance.getClaimedDate();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/SALoopActivityInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive;\n\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Baptiste Mesta\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"loop\")\npublic class SALoopActivityInstance extends SAActivityInstance {\n\n    @Column(name = \"loop_counter\")\n    private int loopCounter;\n    @Column(name = \"loop_max\")\n    private int loopMax;\n\n    public SALoopActivityInstance(final SLoopActivityInstance sLoopActivityInstance) {\n        super(sLoopActivityInstance);\n        loopMax = sLoopActivityInstance.getLoopMax();\n        loopCounter = sLoopActivityInstance.getLoopCounter();\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.LOOP_ACTIVITY;\n    }\n\n    @Override\n    public Class<? extends PersistentObject> getPersistentObjectInterface() {\n        return SLoopActivityInstance.class;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/SAManualTaskInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive;\n\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Baptiste Mesta\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"manual\")\npublic class SAManualTaskInstance extends SAHumanTaskInstance {\n\n    public SAManualTaskInstance(final SManualTaskInstance sManualTaskInstance) {\n        super(sManualTaskInstance);\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.MANUAL_TASK;\n    }\n\n    @Override\n    public Class<? extends PersistentObject> getPersistentObjectInterface() {\n        return SManualTaskInstance.class;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/SAMultiInstanceActivityInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive;\n\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Baptiste Mesta\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"multi\")\npublic class SAMultiInstanceActivityInstance extends SAActivityInstance {\n\n    private boolean sequential;\n    private String loopDataInputRef;\n    private String loopDataOutputRef;\n    private String dataInputItemRef;\n    private String dataOutputItemRef;\n    @Column(name = \"nbActiveInst\")\n    private int numberOfActiveInstances;\n    @Column(name = \"nbCompletedInst\")\n    private int numberOfCompletedInstances;\n    @Column(name = \"nbTerminatedInst\")\n    private int numberOfTerminatedInstances;\n    private int loopCardinality;\n\n    public SAMultiInstanceActivityInstance(final SMultiInstanceActivityInstance activityInstance) {\n        super(activityInstance);\n        sequential = activityInstance.isSequential();\n        loopDataInputRef = activityInstance.getLoopDataInputRef();\n        loopDataOutputRef = activityInstance.getLoopDataOutputRef();\n        dataInputItemRef = activityInstance.getDataInputItemRef();\n        dataOutputItemRef = activityInstance.getDataOutputItemRef();\n        numberOfActiveInstances = activityInstance.getNumberOfActiveInstances();\n        numberOfCompletedInstances = activityInstance.getNumberOfCompletedInstances();\n        numberOfTerminatedInstances = activityInstance.getNumberOfTerminatedInstances();\n        loopCardinality = activityInstance.getLoopCardinality();\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.MULTI_INSTANCE_ACTIVITY;\n    }\n\n    public int getNumberOfInstances() {\n        return numberOfActiveInstances + numberOfCompletedInstances + numberOfTerminatedInstances;\n    }\n\n    @Override\n    public Class<? extends PersistentObject> getPersistentObjectInterface() {\n        return SMultiInstanceActivityInstance.class;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/SAProcessInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive;\n\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.persistence.ArchivedPersistentObject;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n@Data\n@NoArgsConstructor\n@Entity\n@Table(name = \"arch_process_instance\")\npublic class SAProcessInstance implements ArchivedPersistentObject {\n\n    @Id\n    private long id;\n    private long archiveDate;\n    private long sourceObjectId;\n    private String name;\n    private long processDefinitionId;\n    private String description;\n    private int stateId;\n    private long startDate;\n    private long startedBy;\n    private long startedBySubstitute;\n    private long endDate;\n    private long lastUpdate;\n    private long rootProcessInstanceId = -1;\n    private long callerId = -1;\n    private String stringIndex1;\n    private String stringIndex2;\n    private String stringIndex3;\n    private String stringIndex4;\n    private String stringIndex5;\n\n    public SAProcessInstance(final SProcessInstance processInstance) {\n        sourceObjectId = processInstance.getId();\n        name = processInstance.getName();\n        processDefinitionId = processInstance.getProcessDefinitionId();\n        description = processInstance.getDescription();\n        startDate = processInstance.getStartDate();\n        endDate = processInstance.getEndDate();\n        startedBy = processInstance.getStartedBy();\n        startedBySubstitute = processInstance.getStartedBySubstitute();\n        lastUpdate = processInstance.getLastUpdate();\n        stateId = processInstance.getStateId();\n        rootProcessInstanceId = processInstance.getRootProcessInstanceId();\n        callerId = processInstance.getCallerId();\n        stringIndex1 = processInstance.getStringIndex1();\n        stringIndex2 = processInstance.getStringIndex2();\n        stringIndex3 = processInstance.getStringIndex3();\n        stringIndex4 = processInstance.getStringIndex4();\n        stringIndex5 = processInstance.getStringIndex5();\n    }\n\n    @Override\n    public Class<? extends PersistentObject> getPersistentObjectInterface() {\n        return SProcessInstance.class;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/SAReceiveTaskInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive;\n\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"receive\")\npublic class SAReceiveTaskInstance extends SAActivityInstance {\n\n    public SAReceiveTaskInstance(final SReceiveTaskInstance sReceiveTaskInstance) {\n        super(sReceiveTaskInstance);\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.RECEIVE_TASK;\n    }\n\n    @Override\n    public Class<? extends PersistentObject> getPersistentObjectInterface() {\n        return SReceiveTaskInstance.class;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/SASendTaskInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive;\n\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Baptiste Mesta\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"send\")\npublic class SASendTaskInstance extends SAActivityInstance {\n\n    public SASendTaskInstance(final SSendTaskInstance sSendTaskInstance) {\n        super(sSendTaskInstance);\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.SEND_TASK;\n    }\n\n    @Override\n    public Class<? extends PersistentObject> getPersistentObjectInterface() {\n        return SSendTaskInstance.class;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/SASubProcessActivityInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive;\n\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"subProc\")\npublic class SASubProcessActivityInstance extends SAActivityInstance {\n\n    private boolean triggeredByEvent;\n\n    public SASubProcessActivityInstance(final SSubProcessActivityInstance activityInstance) {\n        super(activityInstance);\n        triggeredByEvent = activityInstance.isTriggeredByEvent();\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.SUB_PROCESS;\n    }\n\n    @Override\n    public Class<? extends PersistentObject> getPersistentObjectInterface() {\n        return SSubProcessActivityInstance.class;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/SAUserTaskInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive;\n\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"user\")\npublic class SAUserTaskInstance extends SAHumanTaskInstance {\n\n    public SAUserTaskInstance(final SUserTaskInstance sUserTaskInstance) {\n        super(sUserTaskInstance);\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.USER_TASK;\n    }\n\n    @Override\n    public Class<? extends PersistentObject> getPersistentObjectInterface() {\n        return SUserTaskInstance.class;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAActivityInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface SAActivityInstanceBuilder {\n\n    SAActivityInstanceBuilder setProcessDefinitionId(long processDefinitionId);\n\n    SAActivityInstanceBuilder setRootProcessInstanceId(long processInstanceId);\n\n    SAActivityInstanceBuilder setParentActivityInstanceId(long parentActivityInstanceId);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAActivityInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface SAActivityInstanceBuilderFactory extends SAFlowNodeInstanceBuilderFactory {\n\n    String getPriorityKey();\n\n    String getActivityInstanceIdKey();\n\n    String getAssigneeIdKey();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAAutomaticTaskInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAAutomaticTaskInstance;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface SAAutomaticTaskInstanceBuilder extends SAActivityInstanceBuilder {\n\n    SAAutomaticTaskInstance done();\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAAutomaticTaskInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface SAAutomaticTaskInstanceBuilderFactory extends SAActivityInstanceBuilderFactory {\n\n    SAAutomaticTaskInstanceBuilder createNewAutomaticTaskInstance(SAutomaticTaskInstance sAutomaticTaskInstance);\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SACallActivityInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.SACallActivityInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic interface SACallActivityInstanceBuilder extends SAActivityInstanceBuilder {\n\n    SACallActivityInstance done();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SACallActivityInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic interface SACallActivityInstanceBuilderFactory extends SAActivityInstanceBuilderFactory {\n\n    SACallActivityInstanceBuilder createNewArchivedCallActivityInstance(SCallActivityInstance callActivityInstance);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAConnectorInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAConnectorInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SAConnectorInstanceBuilder {\n\n    SAConnectorInstance done();\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAConnectorInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SAConnectorInstanceBuilderFactory extends SANamedElementBuilderFactory {\n\n    SAConnectorInstanceBuilder createNewArchivedConnectorInstance(SConnectorInstance connectorInstance);\n\n    String getContainerIdKey();\n\n    String getConnectorIdKey();\n\n    String getContainerTypeKey();\n\n    String getVersionKey();\n\n    String getActivationEventKey();\n\n    String getStateKey();\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAFlowElementInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder;\n\npublic interface SAFlowElementInstanceBuilder {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAFlowElementInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder;\n\n/**\n * @author Emmanuel Duchastenier\n * @author Elias Ricken de Medeiros\n */\npublic interface SAFlowElementInstanceBuilderFactory extends SANamedElementBuilderFactory {\n\n    String getDescriptionKey();\n\n    String getRootContainerIdKey();\n\n    String getParentContainerIdKey();\n\n    String getProcessDefinitionKey();\n\n    String getRootProcessInstanceKey();\n\n    String getParentProcessInstanceKey();\n\n    String getParentActivityInstanceKey();\n\n    String getKindKey();\n\n    int getProcessDefinitionIndex();\n\n    int getRootProcessInstanceIndex();\n\n    int getParentProcessInstanceIndex();\n\n    int getParentActivityInstanceIndex();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAFlowNodeInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder;\n\npublic interface SAFlowNodeInstanceBuilder extends SAFlowElementInstanceBuilder {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAFlowNodeInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Emmanuel Duchastenier\n * @author Matthieu Chaffotte\n */\npublic interface SAFlowNodeInstanceBuilderFactory extends SAFlowElementInstanceBuilderFactory {\n\n    String getStateIdKey();\n\n    String getStateNameKey();\n\n    String getReachedStateDateKey();\n\n    String getLastUpdateKey();\n\n    String getDisplayNameKey();\n\n    String getTerminalKey();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAGatewayInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAGatewayInstance;\n\n/**\n * @author Hongwen Zang\n * @author Matthieu Chaffotte\n */\npublic interface SAGatewayInstanceBuilder extends SAFlowNodeInstanceBuilder {\n\n    SAGatewayInstance done();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAGatewayInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.SGatewayInstance;\n\n/**\n * @author Hongwen Zang\n * @author Matthieu Chaffotte\n */\npublic interface SAGatewayInstanceBuilderFactory extends SAActivityInstanceBuilderFactory {\n\n    SAGatewayInstanceBuilder createNewGatewayInstance(SGatewayInstance sGatewayInstance);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SALoopActivityInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.SALoopActivityInstance;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface SALoopActivityInstanceBuilder extends SAActivityInstanceBuilder {\n\n    SALoopActivityInstance done();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SALoopActivityInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface SALoopActivityInstanceBuilderFactory extends SAActivityInstanceBuilderFactory {\n\n    SALoopActivityInstanceBuilder createNewLoopActivityInstance(SLoopActivityInstance sLoopActivityInstance);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAManualTaskInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAManualTaskInstance;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface SAManualTaskInstanceBuilder extends SAActivityInstanceBuilder {\n\n    SAManualTaskInstance done();\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAManualTaskInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface SAManualTaskInstanceBuilderFactory extends SAActivityInstanceBuilderFactory {\n\n    SAManualTaskInstanceBuilder createNewManualTaskInstance(SManualTaskInstance sManualTaskInstance);\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAMultiInstanceActivityInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAMultiInstanceActivityInstance;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface SAMultiInstanceActivityInstanceBuilder extends SAActivityInstanceBuilder {\n\n    SAMultiInstanceActivityInstance done();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAMultiInstanceActivityInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface SAMultiInstanceActivityInstanceBuilderFactory extends SAActivityInstanceBuilderFactory {\n\n    SAMultiInstanceActivityInstanceBuilder createNewMultiInstanceActivityInstance(\n            SMultiInstanceActivityInstance sMultiActivityInstance);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SANamedElementBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SANamedElementBuilderFactory {\n\n    String getIdKey();\n\n    String getNameKey();\n\n    String getSourceObjectIdKey();\n\n    String getArchivedDateKey();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAProcessInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance;\n\n/**\n * @author Baptiste Mesta\n * @author Yanyan Liu\n * @author Celine Souchet\n */\npublic interface SAProcessInstanceBuilder {\n\n    SAProcessInstance done();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAProcessInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\n\n/**\n * @author Baptiste Mesta\n * @author Yanyan Liu\n * @author Celine Souchet\n */\npublic interface SAProcessInstanceBuilderFactory {\n\n    /**\n     * create a new Server Archived Process Instance from a Server Process Instance\n     * the id is kept\n     *\n     * @param processInstance\n     * @return\n     */\n    SAProcessInstanceBuilder createNewInstance(SProcessInstance processInstance);\n\n    String getArchiveDateKey();\n\n    String getProcessDefinitionIdKey();\n\n    String getIdKey();\n\n    String getSourceObjectIdKey();\n\n    String getRootProcessInstanceIdKey();\n\n    String getEndDateKey();\n\n    String getStartDateKey();\n\n    String getLastUpdateKey();\n\n    String getStartedByKey();\n\n    String getStartedBySubstituteKey();\n\n    String getStateIdKey();\n\n    String getNameKey();\n\n    String getCallerIdKey();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAReceiveTaskInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAReceiveTaskInstance;\n\n/**\n * @author Julien Molinaro\n */\npublic interface SAReceiveTaskInstanceBuilder extends SAActivityInstanceBuilder {\n\n    SAReceiveTaskInstance done();\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAReceiveTaskInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance;\n\n/**\n * @author Julien Molinaro\n */\npublic interface SAReceiveTaskInstanceBuilderFactory extends SAActivityInstanceBuilderFactory {\n\n    SAReceiveTaskInstanceBuilder createNewReceiveTaskInstance(SReceiveTaskInstance sReceiveTaskInstance);\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SASendTaskInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.SASendTaskInstance;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface SASendTaskInstanceBuilder extends SAActivityInstanceBuilder {\n\n    SASendTaskInstance done();\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SASendTaskInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface SASendTaskInstanceBuilderFactory extends SAActivityInstanceBuilderFactory {\n\n    SASendTaskInstanceBuilder createNewSendTaskInstance(SSendTaskInstance sSendTaskInstance);\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SASubProcessActivityInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.SASubProcessActivityInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Caffotte\n */\npublic interface SASubProcessActivityInstanceBuilder extends SAActivityInstanceBuilder {\n\n    SASubProcessActivityInstance done();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SASubProcessActivityInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Caffotte\n */\npublic interface SASubProcessActivityInstanceBuilderFactory extends SAActivityInstanceBuilderFactory {\n\n    SASubProcessActivityInstanceBuilder createNewArchivedSubProcessActivityInstance(\n            SSubProcessActivityInstance subProcActInst);\n\n    String getTriggeredByEventKey();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAUserTaskInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface SAUserTaskInstanceBuilder extends SAActivityInstanceBuilder {\n\n    SAUserTaskInstance done();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAUserTaskInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface SAUserTaskInstanceBuilderFactory extends SAActivityInstanceBuilderFactory {\n\n    SAUserTaskInstanceBuilder createNewUserTaskInstance(SUserTaskInstance sUserTaskInstance);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/business/data/SARefBusinessDataInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.business.data;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.business.data.SARefBusinessDataInstance;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic interface SARefBusinessDataInstanceBuilder {\n\n    SARefBusinessDataInstance done();\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/business/data/SARefBusinessDataInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.business.data;\n\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SFlowNodeSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic interface SARefBusinessDataInstanceBuilderFactory {\n\n    SARefBusinessDataInstanceBuilder createNewInstance(SProcessSimpleRefBusinessDataInstance businessDataInstance);\n\n    SARefBusinessDataInstanceBuilder createNewInstance(SProcessMultiRefBusinessDataInstance businessDataInstance);\n\n    SARefBusinessDataInstanceBuilder createNewInstanceForFlowNode(\n            SFlowNodeSimpleRefBusinessDataInstance businessDataInstance);\n\n    SARefBusinessDataInstanceBuilder createNewInstance(SRefBusinessDataInstance sRefBusinessDataInstance);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/event/SAEndEventInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.event;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.event.SAEndEventInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic interface SAEndEventInstanceBuilder {\n\n    SAEndEventInstance done();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/event/SAEndEventInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.event;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAFlowNodeInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.event.SEndEventInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic interface SAEndEventInstanceBuilderFactory extends SAFlowNodeInstanceBuilderFactory {\n\n    SAEndEventInstanceBuilder createNewArchivedEndEventInstance(final SEndEventInstance endEvent);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/event/SAStartEventInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.event;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.event.SAStartEventInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic interface SAStartEventInstanceBuilder {\n\n    SAStartEventInstance done();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/event/SAStartEventInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.event;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAFlowNodeInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.event.SStartEventInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic interface SAStartEventInstanceBuilderFactory extends SAFlowNodeInstanceBuilderFactory {\n\n    SAStartEventInstanceBuilder createNewArchivedStartEventInstance(final SStartEventInstance startEventInstance);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/event/impl/SAEndEventInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.event.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.event.SAEndEventInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.event.SAEndEventInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.SAFlowNodeInstanceBuilderFactoryImpl;\nimport org.bonitasoft.engine.core.process.instance.model.archive.event.SAEndEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SEndEventInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SAEndEventInstanceBuilderFactoryImpl extends SAFlowNodeInstanceBuilderFactoryImpl\n        implements SAEndEventInstanceBuilderFactory {\n\n    @Override\n    public SAEndEventInstanceBuilder createNewArchivedEndEventInstance(final SEndEventInstance endEvent) {\n        final SAEndEventInstance entity = new SAEndEventInstance(endEvent);\n        return new SAEndEventInstanceBuilderImpl(entity);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/event/impl/SAEndEventInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.event.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.event.SAEndEventInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.archive.event.SAEndEventInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SAEndEventInstanceBuilderImpl implements SAEndEventInstanceBuilder {\n\n    private final SAEndEventInstance entity;\n\n    public SAEndEventInstanceBuilderImpl(final SAEndEventInstance entity) {\n        super();\n        this.entity = entity;\n    }\n\n    @Override\n    public SAEndEventInstance done() {\n        return entity;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/event/impl/SAStartEventInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.event.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.event.SAStartEventInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.event.SAStartEventInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.SAFlowNodeInstanceBuilderFactoryImpl;\nimport org.bonitasoft.engine.core.process.instance.model.archive.event.SAStartEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SStartEventInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SAStartEventInstanceBuilderFactoryImpl extends SAFlowNodeInstanceBuilderFactoryImpl\n        implements SAStartEventInstanceBuilderFactory {\n\n    @Override\n    public SAStartEventInstanceBuilder createNewArchivedStartEventInstance(\n            final SStartEventInstance startEventInstance) {\n        final SAStartEventInstance entity = new SAStartEventInstance(startEventInstance);\n        return new SAStartEventInstanceBuilderImpl(entity);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/event/impl/SAStartEventInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.event.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.event.SAStartEventInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.archive.event.SAStartEventInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SAStartEventInstanceBuilderImpl implements SAStartEventInstanceBuilder {\n\n    private final SAStartEventInstance entity;\n\n    public SAStartEventInstanceBuilderImpl(final SAStartEventInstance entity) {\n        super();\n        this.entity = entity;\n    }\n\n    @Override\n    public SAStartEventInstance done() {\n        return this.entity;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAActivityInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAActivityInstanceBuilderFactory;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic abstract class SAActivityInstanceBuilderFactoryImpl extends SAFlowNodeInstanceBuilderFactoryImpl\n        implements SAActivityInstanceBuilderFactory {\n\n    protected static final String PRIORITY_KEY = \"priority\";\n\n    protected static final String ASSIGNEE_ID_KEY = \"assigneeId\";\n\n    protected static final String ACTIVITY_INSTANCE_ID_KEY = \"activityInstanceId\";\n\n    @Override\n    public String getPriorityKey() {\n        return PRIORITY_KEY;\n    }\n\n    @Override\n    public String getActivityInstanceIdKey() {\n        return ACTIVITY_INSTANCE_ID_KEY;\n    }\n\n    @Override\n    public String getAssigneeIdKey() {\n        return ASSIGNEE_ID_KEY;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAActivityInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAActivityInstanceBuilder;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic abstract class SAActivityInstanceBuilderImpl extends SAFlowNodeInstanceBuilderImpl\n        implements SAActivityInstanceBuilder {\n\n    protected SAActivityInstanceBuilderImpl(final SAActivityInstance entity) {\n        super(entity);\n    }\n\n    @Override\n    public SAActivityInstanceBuilder setParentActivityInstanceId(final long parentActivityInstanceId) {\n        entity.setLogicalGroup(SAActivityInstanceBuilderFactoryImpl.PROCESS_DEFINITION_INDEX, parentActivityInstanceId);\n        return this;\n    }\n\n    @Override\n    public SAActivityInstanceBuilder setProcessDefinitionId(final long processDefinitionId) {\n        entity.setLogicalGroup(SAActivityInstanceBuilderFactoryImpl.PROCESS_DEFINITION_INDEX, processDefinitionId);\n        return this;\n    }\n\n    @Override\n    public SAActivityInstanceBuilder setRootProcessInstanceId(final long processInstanceId) {\n        entity.setLogicalGroup(SAActivityInstanceBuilderFactoryImpl.ROOT_PROCESS_INSTANCE_INDEX, processInstanceId);\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAAutomaticTaskInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAAutomaticTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAAutomaticTaskInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAAutomaticTaskInstanceBuilderFactory;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SAAutomaticTaskInstanceBuilderFactoryImpl extends SAActivityInstanceBuilderFactoryImpl\n        implements SAAutomaticTaskInstanceBuilderFactory {\n\n    @Override\n    public SAAutomaticTaskInstanceBuilder createNewAutomaticTaskInstance(\n            final SAutomaticTaskInstance sAutomaticTaskInstance) {\n        final SAAutomaticTaskInstance entity = new SAAutomaticTaskInstance(sAutomaticTaskInstance);\n        return new SAAutomaticTaskInstanceBuilderImpl(entity);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAAutomaticTaskInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAAutomaticTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAAutomaticTaskInstanceBuilder;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SAAutomaticTaskInstanceBuilderImpl extends SAActivityInstanceBuilderImpl\n        implements SAAutomaticTaskInstanceBuilder {\n\n    public SAAutomaticTaskInstanceBuilderImpl(final SAAutomaticTaskInstance entity) {\n        super(entity);\n    }\n\n    @Override\n    public SAAutomaticTaskInstance done() {\n        return (SAAutomaticTaskInstance) entity;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SACallActivityInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SACallActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SACallActivityInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SACallActivityInstanceBuilderFactory;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SACallActivityInstanceBuilderFactoryImpl extends SAActivityInstanceBuilderFactoryImpl\n        implements SACallActivityInstanceBuilderFactory {\n\n    @Override\n    public SACallActivityInstanceBuilder createNewArchivedCallActivityInstance(\n            final SCallActivityInstance callActivityInstance) {\n        final SACallActivityInstance entity = new SACallActivityInstance(callActivityInstance);\n        return new SACallActivityInstanceBuilderImpl(entity);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SACallActivityInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.SACallActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SACallActivityInstanceBuilder;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SACallActivityInstanceBuilderImpl extends SAActivityInstanceBuilderImpl\n        implements SACallActivityInstanceBuilder {\n\n    public SACallActivityInstanceBuilderImpl(final SACallActivityInstance entity) {\n        super(entity);\n    }\n\n    @Override\n    public SACallActivityInstance done() {\n        return (SACallActivityInstance) this.entity;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAConnectorInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAConnectorInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAConnectorInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAConnectorInstanceBuilderFactory;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SAConnectorInstanceBuilderFactoryImpl extends SANamedElementBuilderFactoryImpl\n        implements SAConnectorInstanceBuilderFactory {\n\n    private static final String CONTAINER_ID_KEY = \"containerId\";\n\n    private static final String CONTAINER_TYPE_KEY = \"containerType\";\n\n    private static final String CONNECTOR_ID_KEY = \"connectorId\";\n\n    private static final String VERSION_KEY = \"version\";\n\n    private static final String ACTIVATION_EVENT_KEY = \"activationEvent\";\n\n    private static final String STATE_KEY = \"state\";\n\n    @Override\n    public SAConnectorInstanceBuilder createNewArchivedConnectorInstance(final SConnectorInstance connectorInstance) {\n        return new SAConnectorInstanceBuilderImpl(new SAConnectorInstance(connectorInstance));\n    }\n\n    @Override\n    public String getContainerIdKey() {\n        return CONTAINER_ID_KEY;\n    }\n\n    @Override\n    public String getContainerTypeKey() {\n        return CONTAINER_TYPE_KEY;\n    }\n\n    @Override\n    public String getConnectorIdKey() {\n        return CONNECTOR_ID_KEY;\n    }\n\n    @Override\n    public String getVersionKey() {\n        return VERSION_KEY;\n    }\n\n    @Override\n    public String getActivationEventKey() {\n        return ACTIVATION_EVENT_KEY;\n    }\n\n    @Override\n    public String getStateKey() {\n        return STATE_KEY;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAConnectorInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAConnectorInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAConnectorInstanceBuilder;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SAConnectorInstanceBuilderImpl implements SAConnectorInstanceBuilder {\n\n    private final SAConnectorInstance entity;\n\n    public SAConnectorInstanceBuilderImpl(final SAConnectorInstance entity) {\n        this.entity = entity;\n    }\n\n    @Override\n    public SAConnectorInstance done() {\n        return entity;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAFlowElementInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAFlowElementInstanceBuilderFactory;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic abstract class SAFlowElementInstanceBuilderFactoryImpl extends SANamedElementBuilderFactoryImpl\n        implements SAFlowElementInstanceBuilderFactory {\n\n    protected static final String DESCRIPTION_KEY = \"description\";\n\n    protected static final String ROOT_CONTAINER_ID_KEY = \"rootContainerId\";\n\n    protected static final String PARENT_CONTAINER_ID_KEY = \"parentContainerId\";\n\n    protected static final String LOGICAL_GROUP1_KEY = \"logicalGroup1\";\n\n    protected static final String LOGICAL_GROUP2_KEY = \"logicalGroup2\";\n\n    protected static final String LOGICAL_GROUP3_KEY = \"logicalGroup3\";\n\n    protected static final String LOGICAL_GROUP4_KEY = \"logicalGroup4\";\n\n    protected static final String KIND_KEY = \"kind\";\n\n    protected static final int PROCESS_DEFINITION_INDEX = 0;\n\n    protected static final int ROOT_PROCESS_INSTANCE_INDEX = 1;\n\n    protected static final int PARENT_ACTIVITY_INSTANCE_INDEX = 2;\n\n    protected static final int PARENT_PROCESS_INSTANCE_INDEX = 3;\n\n    @Override\n    public String getRootContainerIdKey() {\n        return ROOT_CONTAINER_ID_KEY;\n    }\n\n    @Override\n    public String getParentContainerIdKey() {\n        return PARENT_CONTAINER_ID_KEY;\n    }\n\n    @Override\n    public String getProcessDefinitionKey() {\n        return LOGICAL_GROUP1_KEY;\n    }\n\n    @Override\n    public String getRootProcessInstanceKey() {\n        return LOGICAL_GROUP2_KEY;\n    }\n\n    @Override\n    public String getParentProcessInstanceKey() {\n        return LOGICAL_GROUP4_KEY;\n    }\n\n    @Override\n    public String getParentActivityInstanceKey() {\n        return LOGICAL_GROUP3_KEY;\n    }\n\n    @Override\n    public int getProcessDefinitionIndex() {\n        return PROCESS_DEFINITION_INDEX;\n    }\n\n    @Override\n    public int getRootProcessInstanceIndex() {\n        return ROOT_PROCESS_INSTANCE_INDEX;\n    }\n\n    @Override\n    public int getParentProcessInstanceIndex() {\n        return PARENT_PROCESS_INSTANCE_INDEX;\n    }\n\n    @Override\n    public int getParentActivityInstanceIndex() {\n        return PARENT_ACTIVITY_INSTANCE_INDEX;\n    }\n\n    @Override\n    public String getDescriptionKey() {\n        return DESCRIPTION_KEY;\n    }\n\n    @Override\n    public String getKindKey() {\n        return KIND_KEY;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAFlowNodeInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAFlowNodeInstanceBuilderFactory;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic abstract class SAFlowNodeInstanceBuilderFactoryImpl extends SAFlowElementInstanceBuilderFactoryImpl\n        implements SAFlowNodeInstanceBuilderFactory {\n\n    protected static final String STATE_ID_KEY = \"stateId\";\n\n    protected static final String STATE_NAME_KEY = \"stateName\";\n\n    protected static final String REACHED_STATE_DATE_KEY = \"reachedStateDate\";\n\n    protected static final String LAST_UPDATE_KEY = \"lastUpdateDate\";\n\n    protected static final String DISPLAY_NAME_KEY = \"displayName\";\n\n    protected static final String TERMINAL_KEY = \"terminal\";\n\n    @Override\n    public String getStateIdKey() {\n        return STATE_ID_KEY;\n    }\n\n    @Override\n    public String getStateNameKey() {\n        return STATE_NAME_KEY;\n    }\n\n    @Override\n    public String getReachedStateDateKey() {\n        return REACHED_STATE_DATE_KEY;\n    }\n\n    @Override\n    public String getLastUpdateKey() {\n        return LAST_UPDATE_KEY;\n    }\n\n    @Override\n    public String getDisplayNameKey() {\n        return DISPLAY_NAME_KEY;\n    }\n\n    @Override\n    public String getTerminalKey() {\n        return TERMINAL_KEY;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAFlowNodeInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAFlowNodeInstanceBuilder;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic abstract class SAFlowNodeInstanceBuilderImpl implements SAFlowNodeInstanceBuilder {\n\n    protected final SAFlowNodeInstance entity;\n\n    protected SAFlowNodeInstanceBuilderImpl(final SAFlowNodeInstance entity) {\n        super();\n        this.entity = entity;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAGatewayInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.SGatewayInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAGatewayInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAGatewayInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAGatewayInstanceBuilderFactory;\n\n/**\n * @author Hongwen Zang\n */\npublic class SAGatewayInstanceBuilderFactoryImpl extends SAActivityInstanceBuilderFactoryImpl\n        implements SAGatewayInstanceBuilderFactory {\n\n    @Override\n    public SAGatewayInstanceBuilder createNewGatewayInstance(final SGatewayInstance sGatewayInstance) {\n        final SAGatewayInstance entity = new SAGatewayInstance(sGatewayInstance);\n        return new SAGatewayInstanceBuilderImpl(entity);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAGatewayInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAGatewayInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAGatewayInstanceBuilder;\n\n/**\n * @author Hongwen Zang\n */\npublic class SAGatewayInstanceBuilderImpl extends SAFlowNodeInstanceBuilderImpl implements SAGatewayInstanceBuilder {\n\n    public SAGatewayInstanceBuilderImpl(final SAGatewayInstance entity) {\n        super(entity);\n    }\n\n    @Override\n    public SAGatewayInstance done() {\n        return (SAGatewayInstance) entity;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SALoopActivityInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SALoopActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SALoopActivityInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SALoopActivityInstanceBuilderFactory;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SALoopActivityInstanceBuilderFactoryImpl extends SAActivityInstanceBuilderFactoryImpl\n        implements SALoopActivityInstanceBuilderFactory {\n\n    @Override\n    public SALoopActivityInstanceBuilder createNewLoopActivityInstance(\n            final SLoopActivityInstance sLoopActivityInstance) {\n        final SALoopActivityInstance entity = new SALoopActivityInstance(sLoopActivityInstance);\n        return new SALoopActivityInstanceBuilderImpl(entity);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SALoopActivityInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.SALoopActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SALoopActivityInstanceBuilder;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SALoopActivityInstanceBuilderImpl extends SAActivityInstanceBuilderImpl\n        implements SALoopActivityInstanceBuilder {\n\n    public SALoopActivityInstanceBuilderImpl(final SALoopActivityInstance entity) {\n        super(entity);\n    }\n\n    @Override\n    public SALoopActivityInstance done() {\n        return (SALoopActivityInstance) entity;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAManualTaskInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAManualTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAManualTaskInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAManualTaskInstanceBuilderFactory;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SAManualTaskInstanceBuilderFactoryImpl extends SAActivityInstanceBuilderFactoryImpl\n        implements SAManualTaskInstanceBuilderFactory {\n\n    @Override\n    public SAManualTaskInstanceBuilder createNewManualTaskInstance(final SManualTaskInstance sManualTaskInstance) {\n        final SAManualTaskInstance entity = new SAManualTaskInstance(sManualTaskInstance);\n        return new SAManualTaskInstanceBuilderImpl(entity);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAManualTaskInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAManualTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAManualTaskInstanceBuilder;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SAManualTaskInstanceBuilderImpl extends SAActivityInstanceBuilderImpl\n        implements SAManualTaskInstanceBuilder {\n\n    public SAManualTaskInstanceBuilderImpl(final SAManualTaskInstance entity) {\n        super(entity);\n    }\n\n    @Override\n    public SAManualTaskInstance done() {\n        return (SAManualTaskInstance) entity;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAMultiInstanceActivityInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAMultiInstanceActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAMultiInstanceActivityInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAMultiInstanceActivityInstanceBuilderFactory;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SAMultiInstanceActivityInstanceBuilderFactoryImpl extends SAActivityInstanceBuilderFactoryImpl\n        implements SAMultiInstanceActivityInstanceBuilderFactory {\n\n    @Override\n    public SAMultiInstanceActivityInstanceBuilder createNewMultiInstanceActivityInstance(\n            final SMultiInstanceActivityInstance sMultiActivityInstance) {\n        final SAMultiInstanceActivityInstance entity = new SAMultiInstanceActivityInstance(sMultiActivityInstance);\n        return new SAMultiInstanceActivityInstanceBuilderImpl(entity);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAMultiInstanceActivityInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAMultiInstanceActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAMultiInstanceActivityInstanceBuilder;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SAMultiInstanceActivityInstanceBuilderImpl extends SAActivityInstanceBuilderImpl\n        implements SAMultiInstanceActivityInstanceBuilder {\n\n    public SAMultiInstanceActivityInstanceBuilderImpl(final SAMultiInstanceActivityInstance entity) {\n        super(entity);\n    }\n\n    @Override\n    public SAMultiInstanceActivityInstance done() {\n        return (SAMultiInstanceActivityInstance) entity;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SANamedElementBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SANamedElementBuilderFactory;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SANamedElementBuilderFactoryImpl implements SANamedElementBuilderFactory {\n\n    protected static final String ARCHIVED_DATE = \"archiveDate\";\n\n    protected static final String ID_KEY = \"id\";\n\n    protected static final String NAME_KEY = \"name\";\n\n    @Override\n    public String getIdKey() {\n        return ID_KEY;\n    }\n\n    @Override\n    public String getNameKey() {\n        return NAME_KEY;\n    }\n\n    @Override\n    public String getSourceObjectIdKey() {\n        return \"sourceObjectId\";\n    }\n\n    @Override\n    public String getArchivedDateKey() {\n        return ARCHIVED_DATE;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAProcessInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAProcessInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAProcessInstanceBuilderFactory;\n\n/**\n * @author Baptiste Mesta\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class SAProcessInstanceBuilderFactoryImpl implements SAProcessInstanceBuilderFactory {\n\n    private static final String ARCHIVE_DATE_KEY = \"archiveDate\";\n\n    private static final String ID_KEY = \"id\";\n\n    private static final String NAME_KEY = \"name\";\n\n    private static final String PROCESSDEF_ID_KEY = \"processDefinitionId\";\n\n    private static final String ROOT_PROCESS_INSTANCE_ID = \"rootProcessInstanceId\";\n\n    private static final String STATE_ID_KEY = \"stateId\";\n\n    private static final String SOURCE_OBJECT_ID_KEY = \"sourceObjectId\";\n\n    private static final String CALLER_ID = \"callerId\";\n\n    private static final String END_DATE_KEY = \"endDate\";\n\n    private static final String STARTED_BY_KEY = \"startedBy\";\n\n    private static final String STARTED_BY_SUBSTITUTE_KEY = \"startedBySubstitute\";\n\n    private static final String START_DATE_KEY = \"startDate\";\n\n    private static final String LAST_UPDATE_KEY = \"lastUpdate\";\n\n    @Override\n    public SAProcessInstanceBuilder createNewInstance(final SProcessInstance processInstance) {\n        final SAProcessInstance entity = new SAProcessInstance(processInstance);\n        return new SAProcessInstanceBuilderImpl(entity);\n    }\n\n    @Override\n    public String getArchiveDateKey() {\n        return ARCHIVE_DATE_KEY;\n    }\n\n    @Override\n    public String getProcessDefinitionIdKey() {\n        return PROCESSDEF_ID_KEY;\n    }\n\n    @Override\n    public String getIdKey() {\n        return ID_KEY;\n    }\n\n    public String getRootProcessInstanceIdKey() {\n        return ROOT_PROCESS_INSTANCE_ID;\n    }\n\n    @Override\n    public String getSourceObjectIdKey() {\n        return SOURCE_OBJECT_ID_KEY;\n    }\n\n    @Override\n    public String getEndDateKey() {\n        return END_DATE_KEY;\n    }\n\n    @Override\n    public String getStartDateKey() {\n        return START_DATE_KEY;\n    }\n\n    @Override\n    public String getLastUpdateKey() {\n        return LAST_UPDATE_KEY;\n    }\n\n    @Override\n    public String getStartedByKey() {\n        return STARTED_BY_KEY;\n    }\n\n    @Override\n    public String getStartedBySubstituteKey() {\n        return STARTED_BY_SUBSTITUTE_KEY;\n    }\n\n    @Override\n    public String getStateIdKey() {\n        return STATE_ID_KEY;\n    }\n\n    @Override\n    public String getNameKey() {\n        return NAME_KEY;\n    }\n\n    @Override\n    public String getCallerIdKey() {\n        return CALLER_ID;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAProcessInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAProcessInstanceBuilder;\n\n/**\n * @author Baptiste Mesta\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class SAProcessInstanceBuilderImpl implements SAProcessInstanceBuilder {\n\n    private final SAProcessInstance entity;\n\n    public SAProcessInstanceBuilderImpl(final SAProcessInstance entity) {\n        super();\n        this.entity = entity;\n    }\n\n    @Override\n    public SAProcessInstance done() {\n        return entity;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAReceiveTaskInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAReceiveTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAReceiveTaskInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAReceiveTaskInstanceBuilderFactory;\n\n/**\n * @author Julien Molinaro\n */\npublic class SAReceiveTaskInstanceBuilderFactoryImpl extends SAActivityInstanceBuilderFactoryImpl\n        implements SAReceiveTaskInstanceBuilderFactory {\n\n    @Override\n    public SAReceiveTaskInstanceBuilder createNewReceiveTaskInstance(final SReceiveTaskInstance sReceiveTaskInstance) {\n        final SAReceiveTaskInstance entity = new SAReceiveTaskInstance(sReceiveTaskInstance);\n        return new SAReceiveTaskInstanceBuilderImpl(entity);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAReceiveTaskInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAReceiveTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAReceiveTaskInstanceBuilder;\n\n/**\n * @author Julien Molinaro\n */\npublic class SAReceiveTaskInstanceBuilderImpl extends SAActivityInstanceBuilderImpl\n        implements SAReceiveTaskInstanceBuilder {\n\n    public SAReceiveTaskInstanceBuilderImpl(final SAReceiveTaskInstance entity) {\n        super(entity);\n    }\n\n    @Override\n    public SAReceiveTaskInstance done() {\n        return (SAReceiveTaskInstance) this.entity;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SASendTaskInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SASendTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SASendTaskInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SASendTaskInstanceBuilderFactory;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SASendTaskInstanceBuilderFactoryImpl extends SAActivityInstanceBuilderFactoryImpl\n        implements SASendTaskInstanceBuilderFactory {\n\n    @Override\n    public SASendTaskInstanceBuilder createNewSendTaskInstance(final SSendTaskInstance sSendTaskInstance) {\n        final SASendTaskInstance entity = new SASendTaskInstance(sSendTaskInstance);\n        return new SASendTaskInstanceBuilderImpl(entity);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SASendTaskInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.SASendTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SASendTaskInstanceBuilder;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SASendTaskInstanceBuilderImpl extends SAActivityInstanceBuilderImpl implements SASendTaskInstanceBuilder {\n\n    public SASendTaskInstanceBuilderImpl(final SASendTaskInstance entity) {\n        super(entity);\n    }\n\n    @Override\n    public SASendTaskInstance done() {\n        return (SASendTaskInstance) entity;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SASubProcessActivityInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SASubProcessActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SASubProcessActivityInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SASubProcessActivityInstanceBuilderFactory;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SASubProcessActivityInstanceBuilderFactoryImpl extends SAActivityInstanceBuilderFactoryImpl\n        implements SASubProcessActivityInstanceBuilderFactory {\n\n    @Override\n    public SASubProcessActivityInstanceBuilder createNewArchivedSubProcessActivityInstance(\n            final SSubProcessActivityInstance subProcActInst) {\n        final SASubProcessActivityInstance entity = new SASubProcessActivityInstance(subProcActInst);\n        return new SASubProcessActivityInstanceBuilderImpl(entity);\n    }\n\n    @Override\n    public String getTriggeredByEventKey() {\n        return \"triggeredByEvent\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SASubProcessActivityInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.SASubProcessActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SASubProcessActivityInstanceBuilder;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SASubProcessActivityInstanceBuilderImpl extends SAActivityInstanceBuilderImpl\n        implements SASubProcessActivityInstanceBuilder {\n\n    public SASubProcessActivityInstanceBuilderImpl(final SASubProcessActivityInstance entity) {\n        super(entity);\n    }\n\n    @Override\n    public SASubProcessActivityInstance done() {\n        return (SASubProcessActivityInstance) entity;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAUserTaskInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAUserTaskInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAUserTaskInstanceBuilderFactory;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SAUserTaskInstanceBuilderFactoryImpl extends SAActivityInstanceBuilderFactoryImpl\n        implements SAUserTaskInstanceBuilderFactory {\n\n    @Override\n    public SAUserTaskInstanceBuilder createNewUserTaskInstance(final SUserTaskInstance sUserTaskInstance) {\n        final SAUserTaskInstance entity = new SAUserTaskInstance(sUserTaskInstance);\n        return new SAUserTaskInstanceBuilderImpl(entity);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAUserTaskInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.SAUserTaskInstanceBuilder;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SAUserTaskInstanceBuilderImpl extends SAActivityInstanceBuilderImpl implements SAUserTaskInstanceBuilder {\n\n    public SAUserTaskInstanceBuilderImpl(final SAUserTaskInstance entity) {\n        super(entity);\n    }\n\n    @Override\n    public SAUserTaskInstance done() {\n        return (SAUserTaskInstance) entity;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/business/data/SARefBusinessDataInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.business.data;\n\nimport java.util.ArrayList;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.business.data.SARefBusinessDataInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.business.data.SARefBusinessDataInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.archive.business.data.SAFlowNodeSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.business.data.SAProcessMultiRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.business.data.SAProcessSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.business.data.SARefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SFlowNodeSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class SARefBusinessDataInstanceBuilderFactoryImpl implements SARefBusinessDataInstanceBuilderFactory {\n\n    @Override\n    public SARefBusinessDataInstanceBuilder createNewInstance(SRefBusinessDataInstance sRefBusinessDataInstance) {\n        if (sRefBusinessDataInstance instanceof SFlowNodeSimpleRefBusinessDataInstance) {\n            return createNewInstanceForFlowNode((SFlowNodeSimpleRefBusinessDataInstance) sRefBusinessDataInstance);\n        } else if (sRefBusinessDataInstance instanceof SProcessSimpleRefBusinessDataInstance) {\n            return createNewInstance((SProcessSimpleRefBusinessDataInstance) sRefBusinessDataInstance);\n        } else if (sRefBusinessDataInstance instanceof SProcessMultiRefBusinessDataInstance) {\n            return createNewInstance((SProcessMultiRefBusinessDataInstance) sRefBusinessDataInstance);\n        } else\n            return null;\n    }\n\n    @Override\n    public SARefBusinessDataInstanceBuilder createNewInstance(\n            SProcessSimpleRefBusinessDataInstance businessDataInstance) {\n        final SAProcessSimpleRefBusinessDataInstance entity = new SAProcessSimpleRefBusinessDataInstance();\n        setCommonAttributes(businessDataInstance, entity);\n        entity.setProcessInstanceId(businessDataInstance.getProcessInstanceId());\n        entity.setDataId(businessDataInstance.getDataId());\n        return new SARefBusinessDataInstanceBuilderImpl(entity);\n    }\n\n    @Override\n    public SARefBusinessDataInstanceBuilder createNewInstance(\n            SProcessMultiRefBusinessDataInstance businessDataInstance) {\n        final SAProcessMultiRefBusinessDataInstance entity = new SAProcessMultiRefBusinessDataInstance();\n        setCommonAttributes(businessDataInstance, entity);\n        entity.setProcessInstanceId(businessDataInstance.getProcessInstanceId());\n        final ArrayList<Long> dataIds = new ArrayList<>(businessDataInstance.getDataIds().size());\n        for (Long dataId : businessDataInstance.getDataIds()) {\n            dataIds.add(dataId);\n        }\n        entity.setDataIds(dataIds);\n        return new SARefBusinessDataInstanceBuilderImpl(entity);\n    }\n\n    @Override\n    public SARefBusinessDataInstanceBuilder createNewInstanceForFlowNode(\n            SFlowNodeSimpleRefBusinessDataInstance businessDataInstance) {\n        final SAFlowNodeSimpleRefBusinessDataInstance entity = new SAFlowNodeSimpleRefBusinessDataInstance();\n        setCommonAttributes(businessDataInstance, entity);\n        entity.setFlowNodeInstanceId(businessDataInstance.getFlowNodeInstanceId());\n        entity.setDataId(businessDataInstance.getDataId());\n        return new SARefBusinessDataInstanceBuilderImpl(entity);\n    }\n\n    protected void setCommonAttributes(SRefBusinessDataInstance businessDataInstance,\n            SARefBusinessDataInstance entity) {\n        entity.setName(businessDataInstance.getName());\n        entity.setDataClassName(businessDataInstance.getDataClassName());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/business/data/SARefBusinessDataInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.business.data;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.builder.business.data.SARefBusinessDataInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.archive.business.data.SARefBusinessDataInstance;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class SARefBusinessDataInstanceBuilderImpl implements SARefBusinessDataInstanceBuilder {\n\n    private final SARefBusinessDataInstance entity;\n\n    public SARefBusinessDataInstanceBuilderImpl(final SARefBusinessDataInstance entity) {\n        super();\n        this.entity = entity;\n    }\n\n    @Override\n    public SARefBusinessDataInstance done() {\n        return entity;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/business/data/SAFlowNodeSimpleRefBusinessDataInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.business.data;\n\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SFlowNodeSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"fn_simple_ref\")\npublic class SAFlowNodeSimpleRefBusinessDataInstance extends SASimpleRefBusinessDataInstance {\n\n    @Column(name = \"orig_fn_inst_id\")\n    private long flowNodeInstanceId;\n\n    @Override\n    public SRefBusinessDataInstance toSRefBusinessDataInstance() {\n        SFlowNodeSimpleRefBusinessDataInstance refBusinessDataInstance = new SFlowNodeSimpleRefBusinessDataInstance();\n        refBusinessDataInstance.setId(getSourceObjectId());\n        refBusinessDataInstance.setName(getName());\n        refBusinessDataInstance.setDataClassName(getDataClassName());\n        refBusinessDataInstance.setDataId(getDataId());\n        refBusinessDataInstance.setFlowNodeInstanceId(flowNodeInstanceId);\n        return refBusinessDataInstance;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/business/data/SAProcessMultiRefBusinessDataInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.business.data;\n\nimport java.util.List;\n\nimport javax.persistence.CollectionTable;\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.ElementCollection;\nimport javax.persistence.Entity;\nimport javax.persistence.JoinColumn;\nimport javax.persistence.OrderColumn;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"proc_multi_ref\")\npublic class SAProcessMultiRefBusinessDataInstance extends SARefBusinessDataInstance {\n\n    @Column(name = \"orig_proc_inst_id\")\n    private long processInstanceId;\n    @ElementCollection\n    @CollectionTable(name = \"arch_multi_biz_data\", joinColumns = {\n            @JoinColumn(name = \"id\", referencedColumnName = \"id\") })\n    @OrderColumn(name = \"idx\")\n    @Column(name = \"data_id\")\n    private List<Long> dataIds;\n\n    @Override\n    public SRefBusinessDataInstance toSRefBusinessDataInstance() {\n        SProcessMultiRefBusinessDataInstance refBusinessDataInstance = new SProcessMultiRefBusinessDataInstance();\n        refBusinessDataInstance.setId(getSourceObjectId());\n        refBusinessDataInstance.setName(getName());\n        refBusinessDataInstance.setDataClassName(getDataClassName());\n        refBusinessDataInstance.setDataIds(getDataIds());\n        refBusinessDataInstance.setProcessInstanceId(processInstanceId);\n        return refBusinessDataInstance;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/business/data/SAProcessSimpleRefBusinessDataInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.business.data;\n\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"proc_simple_ref\")\npublic class SAProcessSimpleRefBusinessDataInstance extends SASimpleRefBusinessDataInstance {\n\n    @Column(name = \"orig_proc_inst_id\")\n    private long processInstanceId;\n\n    @Override\n    public SRefBusinessDataInstance toSRefBusinessDataInstance() {\n        SProcessSimpleRefBusinessDataInstance refBusinessDataInstance = new SProcessSimpleRefBusinessDataInstance();\n        refBusinessDataInstance.setId(getSourceObjectId());\n        refBusinessDataInstance.setName(getName());\n        refBusinessDataInstance.setDataClassName(getDataClassName());\n        refBusinessDataInstance.setDataId(getDataId());\n        refBusinessDataInstance.setProcessInstanceId(processInstanceId);\n        return refBusinessDataInstance;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/business/data/SARefBusinessDataInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.business.data;\n\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorColumn;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance;\nimport org.bonitasoft.engine.persistence.ArchivedPersistentObject;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@Data\n@NoArgsConstructor\n@Entity\n@Table(name = \"arch_ref_biz_data_inst\")\n@DiscriminatorColumn(name = \"kind\")\npublic abstract class SARefBusinessDataInstance implements ArchivedPersistentObject {\n\n    @Id\n    protected long id;\n    private String name;\n    @Column(name = \"data_classname\")\n    private String dataClassName;\n\n    @Override\n    public long getSourceObjectId() {\n        // WARNING\n        // There is no column sourceObjectId, return the id of the column\n        // This is an error. we should have this column\n        return getId();\n    }\n\n    @Override\n    public long getArchiveDate() {\n        //There is no archiveDate column\n        return 0;\n    }\n\n    public void setSourceObjectId(long id) {\n        //Nothing to do, not persisted\n    }\n\n    public void setArchiveDate(long id) {\n        //Nothing to do, not persisted\n    }\n\n    @Override\n    public Class<? extends PersistentObject> getPersistentObjectInterface() {\n        return SRefBusinessDataInstance.class;\n    }\n\n    public abstract SRefBusinessDataInstance toSRefBusinessDataInstance();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/business/data/SASimpleRefBusinessDataInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.business.data;\n\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\npublic abstract class SASimpleRefBusinessDataInstance extends SARefBusinessDataInstance {\n\n    @Column(name = \"data_id\")\n    private Long dataId;\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/event/SABoundaryEventInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.event;\n\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"boundaryEvent\")\npublic class SABoundaryEventInstance extends SACatchEventInstance {\n\n    private long activityInstanceId;\n\n    public SABoundaryEventInstance(final SBoundaryEventInstance sBoundaryEventInstance) {\n        super(sBoundaryEventInstance);\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.BOUNDARY_EVENT;\n    }\n\n    @Override\n    public Class<? extends PersistentObject> getPersistentObjectInterface() {\n        return SBoundaryEventInstance.class;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/event/SACatchEventInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.event;\n\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.instance.model.event.SCatchEventInstance;\n\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\npublic abstract class SACatchEventInstance extends SAEventInstance {\n\n    private boolean interrupting = true;\n\n    public SACatchEventInstance(final SCatchEventInstance sCatchEventInstance) {\n        super(sCatchEventInstance);\n        interrupting = sCatchEventInstance.isInterrupting();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/event/SAEndEventInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.event;\n\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.model.event.SEndEventInstance;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"endEvent\")\npublic class SAEndEventInstance extends SAThrowEventInstance {\n\n    public SAEndEventInstance(final SEndEventInstance endEvent) {\n        super(endEvent);\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.END_EVENT;\n    }\n\n    @Override\n    public Class<? extends PersistentObject> getPersistentObjectInterface() {\n        return SEndEventInstance.class;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/event/SAEventInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.event;\n\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SEventInstance;\n\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\npublic abstract class SAEventInstance extends SAFlowNodeInstance {\n\n    public SAEventInstance(final SEventInstance eventInstance) {\n        super(eventInstance);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/event/SAIntermediateCatchEventInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.event;\n\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.model.event.SIntermediateCatchEventInstance;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"intermediateCatchEvent\")\npublic class SAIntermediateCatchEventInstance extends SACatchEventInstance {\n\n    public SAIntermediateCatchEventInstance(final SIntermediateCatchEventInstance sIntermediateCatchEventInstance) {\n        super(sIntermediateCatchEventInstance);\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.INTERMEDIATE_CATCH_EVENT;\n    }\n\n    @Override\n    public Class<? extends PersistentObject> getPersistentObjectInterface() {\n        return SIntermediateCatchEventInstance.class;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/event/SAIntermediateThrowEventInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.event;\n\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.model.event.SIntermediateThrowEventInstance;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"intermediateThrowEvent\")\npublic class SAIntermediateThrowEventInstance extends SAThrowEventInstance {\n\n    public SAIntermediateThrowEventInstance(final SIntermediateThrowEventInstance sIntermediateThrowEventInstance) {\n        super(sIntermediateThrowEventInstance);\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.INTERMEDIATE_THROW_EVENT;\n    }\n\n    @Override\n    public Class<? extends PersistentObject> getPersistentObjectInterface() {\n        return SIntermediateThrowEventInstance.class;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/event/SAStartEventInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.event;\n\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.model.event.SStartEventInstance;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"startEvent\")\npublic class SAStartEventInstance extends SACatchEventInstance {\n\n    public SAStartEventInstance(final SStartEventInstance startEventInstance) {\n        super(startEventInstance);\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.START_EVENT;\n    }\n\n    @Override\n    public Class<? extends PersistentObject> getPersistentObjectInterface() {\n        return SStartEventInstance.class;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/event/SAThrowEventInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.event;\n\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.instance.model.event.SThrowEventInstance;\n\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\npublic abstract class SAThrowEventInstance extends SAEventInstance {\n\n    public SAThrowEventInstance(final SThrowEventInstance sThrowEventInstance) {\n        super(sThrowEventInstance);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SActivityInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\n\n/**\n * @author Baptiste Mesta\n * @author Elias Ricken de Medeiros\n * @author Yanyan Liu\n */\npublic interface SActivityInstanceBuilder extends SFlowNodeInstanceBuilder {\n\n    SActivityInstance done();\n\n    SActivityInstanceBuilder setName(final String name);\n\n    SActivityInstanceBuilder setDescription(String description);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SActivityInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder;\n\n/**\n * @author Baptiste Mesta\n * @author Elias Ricken de Medeiros\n * @author Yanyan Liu\n */\npublic interface SActivityInstanceBuilderFactory extends SFlowNodeInstanceBuilderFactory {\n\n    String getTokenCountKey();\n\n    String getAbortedByBoundaryEventIdKey();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SAutomaticTaskInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic interface SAutomaticTaskInstanceBuilder extends SActivityInstanceBuilder {\n\n    SAutomaticTaskInstance done();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SAutomaticTaskInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic interface SAutomaticTaskInstanceBuilderFactory extends SActivityInstanceBuilderFactory {\n\n    SAutomaticTaskInstanceBuilder createNewAutomaticTaskInstance(final String name, long flowNodeDefinitionId,\n            final long rootContainerId,\n            long parentContainerId, long processDefinitionId, long rootProcessInstanceId, long parentProcessInstanceId);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SCallActivityInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic interface SCallActivityInstanceBuilder extends SActivityInstanceBuilder {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SCallActivityInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic interface SCallActivityInstanceBuilderFactory extends SActivityInstanceBuilderFactory {\n\n    SCallActivityInstanceBuilder createNewCallActivityInstance(final String name, final long flowNodeDefinitionId,\n            final long rootContainerId,\n            long parentContainerId, long processDefinitionId, long rootProcessInstanceId, long parentProcessInstanceId);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SFlowElementInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Yanyan Liu\n */\npublic interface SFlowElementInstanceBuilderFactory {\n\n    String getIdKey();\n\n    String getNameKey();\n\n    String getRootContainerIdKey();\n\n    String getParentContainerIdKey();\n\n    String getProcessDefinitionKey();\n\n    String getRootProcessInstanceKey();\n\n    String getParentProcessInstanceKey();\n\n    String getParentActivityInstanceKey();\n\n    String getStateCategoryKey();\n\n    String getStableKey();\n\n    String getTerminalKey();\n\n    int getProcessDefinitionIndex();\n\n    int getRootProcessInstanceIndex();\n\n    int getParentActivityInstanceIndex();\n\n    int getParentProcessInstanceIndex();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SFlowNodeInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic interface SFlowNodeInstanceBuilder {\n\n    SFlowNodeInstanceBuilder setState(FlowNodeState state);\n\n    SFlowNodeInstanceBuilder setReachedStateDate(final long reachStateDate);\n\n    SFlowNodeInstanceBuilder setLastUpdateDate(final long lastUpdateDate);\n\n    SFlowNodeInstanceBuilder setRootContainerId(final long containerId);\n\n    SFlowNodeInstanceBuilder setParentContainerId(final long containerId);\n\n    SFlowNodeInstanceBuilder setProcessDefinitionId(final long processDefinitionId);\n\n    SFlowNodeInstanceBuilder setRootProcessInstanceId(final long processInstanceId);\n\n    SFlowNodeInstanceBuilder setParentProcessInstanceId(final long processInstanceId);\n\n    SFlowNodeInstanceBuilder setParentActivityInstanceId(final long activityInstanceId);\n\n    SFlowNodeInstanceBuilder setLoopCounter(int loopCounter);\n\n    SFlowNodeInstanceBuilder setStateCategory(SStateCategory stateCategory);\n\n    SFlowNodeInstance done();\n\n    SFlowNodeType getFlowNodeType();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SFlowNodeInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic interface SFlowNodeInstanceBuilderFactory extends SFlowElementInstanceBuilderFactory {\n\n    String getStateIdKey();\n\n    String getStateNameKey();\n\n    String getPreviousStateIdKey();\n\n    String getReachStateDateKey();\n\n    String getLastUpdateDateKey();\n\n    String getDisplayNameKey();\n\n    String getDisplayDescriptionKey();\n\n    String getStateExecutingKey();\n\n    String getExecutedBy();\n\n    String getExecutedBySubstitute();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SGatewayInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder;\n\nimport org.bonitasoft.engine.core.process.definition.model.SGatewayType;\nimport org.bonitasoft.engine.core.process.instance.model.SGatewayInstance;\n\n/**\n * @author Feng Hui\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic interface SGatewayInstanceBuilder extends SFlowNodeInstanceBuilder {\n\n    SGatewayInstanceBuilder setStateId(final int stateId);\n\n    SGatewayInstanceBuilder setGatewayType(final SGatewayType gatewayType);\n\n    SGatewayInstanceBuilder setHitBys(final String hitBys);\n\n    SGatewayInstanceBuilder setProcessInstanceId(final long processInstanceId);\n\n    @Override\n    SGatewayInstance done();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SGatewayInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder;\n\nimport org.bonitasoft.engine.core.process.definition.model.SGatewayType;\n\n/**\n * @author Feng Hui\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic interface SGatewayInstanceBuilderFactory extends SFlowNodeInstanceBuilderFactory {\n\n    SGatewayInstanceBuilder createNewInstance(final String name, final long flowNodeDefinitionId,\n            final long rootContainerId, long parentContainerId,\n            final SGatewayType gatewayType, long processDefinitionId, long rootProcessInstanceId,\n            long parentProcessInstanceId);\n\n    String getGatewayTypeKey();\n\n    String getHitBysKey();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SHumanTaskInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.STaskPriority;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface SHumanTaskInstanceBuilder extends SActivityInstanceBuilder {\n\n    SHumanTaskInstanceBuilder setAssigneeId(long assigneeId);\n\n    SHumanTaskInstanceBuilder setPriority(STaskPriority priority);\n\n    SHumanTaskInstanceBuilder setExpectedEndDate(Long expectedEndDate);\n\n    SHumanTaskInstanceBuilder setDisplayDescription(String displayDescription);\n\n    SHumanTaskInstanceBuilder setDisplayName(String displayName);\n\n    @Override\n    SHumanTaskInstance done();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SHumanTaskInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface SHumanTaskInstanceBuilderFactory extends SActivityInstanceBuilderFactory {\n\n    String getExpectedEndDateKey();\n\n    String getAssigneeIdKey();\n\n    String getClaimedDateKey();\n\n    String getPriorityKey();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SLoopActivityInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic interface SLoopActivityInstanceBuilder extends SActivityInstanceBuilder {\n\n    @Override\n    SLoopActivityInstance done();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SLoopActivityInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic interface SLoopActivityInstanceBuilderFactory extends SActivityInstanceBuilderFactory {\n\n    SLoopActivityInstanceBuilder createNewOuterTaskInstance(final String name, final long flowNodeDefinitionId,\n            final long rootContainerId,\n            long parentContainerId, long processDefinitionId, long rootProcessInstanceId, long parentProcessInstanceId);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SManualTaskInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic interface SManualTaskInstanceBuilder extends SHumanTaskInstanceBuilder {\n\n    @Override\n    SManualTaskInstance done();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SManualTaskInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic interface SManualTaskInstanceBuilderFactory extends SHumanTaskInstanceBuilderFactory {\n\n    SManualTaskInstanceBuilder createNewManualTaskInstance(String name, long flowNodeDefinitionId, long rootContainerId,\n            long parentContainerId, long actorId, long processDefinitionId, long rootProcessInstanceId,\n            long parentProcessInstanceId);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SMultiInstanceActivityInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic interface SMultiInstanceActivityInstanceBuilder extends SActivityInstanceBuilder {\n\n    @Override\n    SMultiInstanceActivityInstance done();\n\n    SMultiInstanceActivityInstanceBuilder setLoopDataInputRef(String loopDataInputRef);\n\n    SMultiInstanceActivityInstanceBuilder setLoopDataOutputRef(String loopDataOutputRef);\n\n    SMultiInstanceActivityInstanceBuilder setDataInputItemRef(String dataInputItemRef);\n\n    SMultiInstanceActivityInstanceBuilder setDataOutputItemRef(String dataOutputItemRef);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SMultiInstanceActivityInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic interface SMultiInstanceActivityInstanceBuilderFactory extends SActivityInstanceBuilderFactory {\n\n    SMultiInstanceActivityInstanceBuilder createNewOuterTaskInstance(final String name, final long flowNodeDefinitionId,\n            final long rootContainerId,\n            long parentContainerId, long processDefinitionId, long rootProcessInstanceId, long parentProcessInstanceId,\n            boolean isSequential);\n\n    String getLoopCardinalityKey();\n\n    String getNumberOfActiveInstancesKey();\n\n    String getNumberOfCompletedInstancesKey();\n\n    String getNumberOfTerminatedInstancesKey();\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SReceiveTaskInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder;\n\n/**\n * @author Julien Molinaro\n */\npublic interface SReceiveTaskInstanceBuilder extends SActivityInstanceBuilder {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SReceiveTaskInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder;\n\n/**\n * @author Julien Molinaro\n */\npublic interface SReceiveTaskInstanceBuilderFactory extends SActivityInstanceBuilderFactory {\n\n    SReceiveTaskInstanceBuilder createNewReceiveTaskInstance(final String name, long flowNodeDefinitionId,\n            final long rootContainerId,\n            long parentContainerId, long processDefinitionId, long rootProcessInstanceId, long parentProcessInstanceId);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SSendTaskInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface SSendTaskInstanceBuilder extends SActivityInstanceBuilder {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SSendTaskInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface SSendTaskInstanceBuilderFactory extends SActivityInstanceBuilderFactory {\n\n    SSendTaskInstanceBuilder createNewSendTaskInstance(final String name, long flowNodeDefinitionId,\n            final long rootContainerId, long parentContainerId,\n            long processDefinitionId, long rootProcessInstanceId, long parentProcessInstanceId);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SSubProcessActivityInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic interface SSubProcessActivityInstanceBuilder extends SActivityInstanceBuilder {\n\n    @Override\n    SSubProcessActivityInstance done();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SSubProcessActivityInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic interface SSubProcessActivityInstanceBuilderFactory extends SActivityInstanceBuilderFactory {\n\n    SSubProcessActivityInstanceBuilder createNewSubProcessActivityInstance(final String name,\n            final long flowNodeDefinitionId, final long rootContainerId,\n            long parentContainerId, long processDefinitionId, long rootProcessInstanceId, long parentProcessInstanceId,\n            boolean isTriggeredByEvent);\n\n    String getTriggeredByEventKey();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SUserTaskInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder;\n\nimport org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic interface SUserTaskInstanceBuilder extends SHumanTaskInstanceBuilder {\n\n    @Override\n    SUserTaskInstance done();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SUserTaskInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic interface SUserTaskInstanceBuilderFactory extends SHumanTaskInstanceBuilderFactory {\n\n    SUserTaskInstanceBuilder createNewUserTaskInstance(String name, long flowNodeDefinitionId, long rootContainerId,\n            long parentContainerId,\n            long actorId, long processDefinitionId, long rootProcessInstanceId, long parentProcessInstanceId);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/business/data/SRefBusinessDataInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.business.data;\n\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface SRefBusinessDataInstanceBuilder {\n\n    SRefBusinessDataInstance done();\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/business/data/SRefBusinessDataInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.business.data;\n\nimport java.util.List;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface SRefBusinessDataInstanceBuilderFactory {\n\n    SRefBusinessDataInstanceBuilder createNewInstance(String name, long processInstanceId, Long dataId,\n            String dataClassName);\n\n    SRefBusinessDataInstanceBuilder createNewInstance(String name, long processInstanceId, List<Long> dataIds,\n            String dataClassName);\n\n    SRefBusinessDataInstanceBuilder createNewInstanceForFlowNode(String name, long flowNodeInstanceId, Long dataId,\n            String dataClassName);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/business/data/SRefBusinessDataInstanceLogBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.business.data;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface SRefBusinessDataInstanceLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/business/data/SRefBusinessDataInstanceLogBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.business.data;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface SRefBusinessDataInstanceLogBuilderFactory\n        extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory {\n\n    @Override\n    SRefBusinessDataInstanceLogBuilder createNewInstance();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/SBoundaryEventInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic interface SBoundaryEventInstanceBuilder extends SEventInstanceBuilder {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/SBoundaryEventInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic interface SBoundaryEventInstanceBuilderFactory extends SEventInstanceBuilderFactory {\n\n    SBoundaryEventInstanceBuilder createNewBoundaryEventInstance(final String name, boolean isInterrupting,\n            final long flowNodeDefinitionId,\n            final long rootContainerId, long parentContainerId, long processDefinitionId, long rootProcessInstanceId,\n            long parentProcessInstanceId, long activityInstanceId);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/SEndEventInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic interface SEndEventInstanceBuilder extends SEventInstanceBuilder {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/SEndEventInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic interface SEndEventInstanceBuilderFactory extends SEventInstanceBuilderFactory {\n\n    SEndEventInstanceBuilder createNewEndEventInstance(final String name, final long flowNodeDefinitionId,\n            final long rootContainerId, long parentContainerId,\n            long processDefinitionId, long rootProcessInstanceId, long parentProcessInstanceId);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/SEventInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event;\n\nimport org.bonitasoft.engine.core.process.instance.model.builder.SFlowNodeInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.event.SEventInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SEventInstanceBuilder extends SFlowNodeInstanceBuilder {\n\n    @Override\n    SEventInstance done();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/SEventInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event;\n\nimport org.bonitasoft.engine.core.process.instance.model.builder.SFlowNodeInstanceBuilderFactory;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SEventInstanceBuilderFactory extends SFlowNodeInstanceBuilderFactory {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/SIntermediateCatchEventInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic interface SIntermediateCatchEventInstanceBuilder extends SEventInstanceBuilder {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/SIntermediateCatchEventInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic interface SIntermediateCatchEventInstanceBuilderFactory extends SEventInstanceBuilderFactory {\n\n    SIntermediateCatchEventInstanceBuilder createNewIntermediateCatchEventInstance(final String name,\n            final long flowNodeDefinitionId,\n            final long rootContainerId, long parentContainerId, long processDefinitionId, long rootProcessInstanceId,\n            long parentProcessInstanceId);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/SIntermediateThrowEventInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic interface SIntermediateThrowEventInstanceBuilder extends SEventInstanceBuilder {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/SIntermediateThrowEventInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic interface SIntermediateThrowEventInstanceBuilderFactory extends SEventInstanceBuilderFactory {\n\n    SIntermediateThrowEventInstanceBuilder createNewIntermediateThrowEventInstance(final String name,\n            final long flowNodeDefinitionId,\n            final long rootContainerId, long parentContainerId, long processDefinitionId, long rootProcessInstanceId,\n            long parentProcessInstanceId);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/SStartEventInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic interface SStartEventInstanceBuilder extends SEventInstanceBuilder {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/SStartEventInstanceBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic interface SStartEventInstanceBuilderFactory extends SEventInstanceBuilderFactory {\n\n    SStartEventInstanceBuilder createNewStartEventInstance(final String name, final long flowNodeDefinitionId,\n            final long rootContainerId,\n            long parentContainerId, long processDefinitionId, long rootProcessInstanceId, long parentProcessInstanceId);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/handling/SCorrelationContainerBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event.handling;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface SCorrelationContainerBuilder {\n\n    SCorrelationContainerBuilder setCorrelation(int index, String correlation);\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/handling/SMessageInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event.handling;\n\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SMessageInstanceBuilder implements SCorrelationContainerBuilder {\n\n    public static final String HANDLED = \"handled\";\n    protected final SMessageInstance entity;\n\n    public static SMessageInstanceBuilder create(String messageName, String targetProcess, String targetFlowNode,\n            Long processDefinitionId, String flowNodeName) {\n        return new SMessageInstanceBuilder(\n                new SMessageInstance(messageName, targetProcess, targetFlowNode, processDefinitionId, flowNodeName));\n    }\n\n    private SMessageInstanceBuilder(SMessageInstance entity) {\n        this.entity = entity;\n    }\n\n    @Override\n    public SMessageInstanceBuilder setCorrelation(final int index, final String correlation) {\n        switch (index) {\n            case 1:\n                entity.setCorrelation1(correlation);\n                break;\n            case 2:\n                entity.setCorrelation2(correlation);\n                break;\n            case 3:\n                entity.setCorrelation3(correlation);\n                break;\n            case 4:\n                entity.setCorrelation4(correlation);\n                break;\n            case 5:\n                entity.setCorrelation5(correlation);\n                break;\n            default:\n                break;\n        }\n        return this;\n    }\n\n    public SMessageInstance done() {\n        return entity;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/handling/SMessageInstanceLogBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event.handling;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SMessageInstanceLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction {\n\n    SMessageInstanceLogBuilder setMessageName(final String messageName);\n\n    SMessageInstanceLogBuilder setTargetProcess(final String processName);\n\n    SMessageInstanceLogBuilder setTargetFlowNode(final String flowNode);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/handling/SMessageInstanceLogBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event.handling;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SMessageInstanceLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory {\n\n    String getProcessNameKey();\n\n    String getStartEventNameKey();\n\n    String getMessageNameKey();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/handling/SWaitingErrorEventBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event.handling;\n\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingErrorEvent;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic interface SWaitingErrorEventBuilder {\n\n    SWaitingErrorEvent done();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/handling/SWaitingErrorEventBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event.handling;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic interface SWaitingErrorEventBuilderFactory extends SWaitingEventKeyProviderBuilderFactory {\n\n    SWaitingErrorEventBuilder createNewWaitingErrorBoundaryEventInstance(long processdefinitionId,\n            final long rootProcessInstanceId,\n            final long parentProcessInstanceId, final long flowNodeInstanceId, final String errorCode,\n            final String processName,\n            final long flowNodeDefinitionId, final String flowNodeName, long relatedActivityInstanceId);\n\n    SWaitingErrorEventBuilder createNewWaitingErrorEventSubProcInstance(final long processdefinitionId,\n            final long parentProcessInstanceId,\n            final long rootProcessInstanceId, final String errorCode, final String processName,\n            final long flowNodeDefinitionId, final String flowNodeName,\n            final long subProcessId);\n\n    String getErrorCodeKey();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/handling/SWaitingEventKeyProviderBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event.handling;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SWaitingEventKeyProviderBuilderFactory {\n\n    String getIdKey();\n\n    String getProcessDefinitionIdKey();\n\n    String getRootProcessInstanceIdKey();\n\n    String getParentProcessInstanceIdKey();\n\n    String getFlowNodeInstanceIdKey();\n\n    String getSubProcessIdKey();\n\n    String getProcessNameKey();\n\n    String getFlowNodeNameKey();\n\n    String getEventTypeKey();\n\n    String getActiveKey();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/handling/SWaitingMessageEventBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event.handling;\n\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic interface SWaitingMessageEventBuilder extends SCorrelationContainerBuilder {\n\n    SWaitingMessageEvent done();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/handling/SWaitingMessageEventBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event.handling;\n\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic interface SWaitingMessageEventBuilderFactory extends SWaitingEventKeyProviderBuilderFactory {\n\n    int PROGRESS_FREE_KEY = 0;\n\n    int PROGRESS_IN_TREATMENT_KEY = 1;\n\n    SWaitingMessageEventBuilder createNewWaitingMessageStartEventInstance(long processdefinitionId,\n            final String messageName, final String processName,\n            final long flowNodeDefinitionId, final String flowNodeName);\n\n    SWaitingMessageEventBuilder createNewWaitingMessageEventSubProcInstance(final long processdefinitionId,\n            final long parentProcessInstanceId,\n            final long rootProcessInstanceId, final String messageName, final String processName,\n            final long flowNodeDefinitionId, final String flowNodeName,\n            final long subProcessId);\n\n    SWaitingMessageEventBuilder createNewWaitingMessageIntermediateEventInstance(long processdefinitionId,\n            final long rootProcessInstanceId,\n            final long processInstanceId, final long flowNodeInstanceId, final String messageName,\n            final String processName, final long flowNodeDefinitionId,\n            final String flowNodeName);\n\n    SWaitingMessageEventBuilder createNewWaitingMessageBoundaryEventInstance(long processdefinitionId,\n            final long rootProcessInstanceId,\n            final long processInstanceId, final long flowNodeInstanceId, final String messageName,\n            final String processName, final long flowNodeDefinitionId,\n            final String flowNodeName);\n\n    SWaitingMessageEventBuilder createNewInstance(SWaitingMessageEvent waitingMessage);\n\n    String getMessageNameKey();\n\n    String getLockedKey();\n\n    String getProgressKey();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/handling/SWaitingSignalEventBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event.handling;\n\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent;\n\n/**\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic interface SWaitingSignalEventBuilder {\n\n    SWaitingSignalEvent done();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/handling/SWaitingSignalEventBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event.handling;\n\n/**\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic interface SWaitingSignalEventBuilderFactory extends SWaitingEventKeyProviderBuilderFactory {\n\n    SWaitingSignalEventBuilder createNewWaitingSignalStartEventInstance(final long processdefinitionId,\n            final String signalName, final String processName,\n            final long flowNodeDefinitionId, final String flowNodeName);\n\n    SWaitingSignalEventBuilder createNewWaitingSignalEventSubProcInstance(final long processdefinitionId,\n            final long parentProcessInstanceId,\n            final long rootProcessInstanceId, final String signalName, final String processName,\n            final long flowNodeDefinitionId, final String flowNodeName,\n            final long subProcessId);\n\n    SWaitingSignalEventBuilder createNewWaitingSignalIntermediateEventInstance(long processdefinitionId,\n            final long rootProcessInstanceId, final long processInstanceId,\n            final long flowNodeInstanceId, final String signalName, final String processName,\n            final long flowNodeDefinitionId, final String flowNodeName);\n\n    SWaitingSignalEventBuilder createNewWaitingSignalBoundaryEventInstance(long processdefinitionId,\n            final long rootProcessInstanceId, final long processInstanceId,\n            final long flowNodeInstanceId, final String signalName, final String processName,\n            final long flowNodeDefinitionId, final String flowNodeName);\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/handling/impl/SWaitingErrorEventBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event.handling.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingErrorEventBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingErrorEventBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SBPMEventType;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingErrorEvent;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class SWaitingErrorEventBuilderFactoryImpl extends SWaitingEventKeyProviderBuilderFactoryImpl\n        implements SWaitingErrorEventBuilderFactory {\n\n    @Override\n    public SWaitingErrorEventBuilder createNewWaitingErrorBoundaryEventInstance(final long processdefinitionId,\n            final long rootProcessInstanceId,\n            final long parentProcessInstanceId, final long flowNodeInstanceId, final String errorCode,\n            final String processName,\n            final long flowNodeDefinitionId, final String flowNodeName, final long relatedActivityInstanceId) {\n        final SWaitingErrorEvent entity = new SWaitingErrorEvent(SBPMEventType.BOUNDARY_EVENT, processdefinitionId,\n                processName, flowNodeDefinitionId, flowNodeName, errorCode);\n        entity.setFlowNodeInstanceId(flowNodeInstanceId);\n        entity.setRootProcessInstanceId(rootProcessInstanceId);\n        entity.setParentProcessInstanceId(parentProcessInstanceId);\n        entity.setRelatedActivityInstanceId(relatedActivityInstanceId);\n        return new SWaitingErrorEventBuilderImpl(entity);\n    }\n\n    @Override\n    public SWaitingErrorEventBuilder createNewWaitingErrorEventSubProcInstance(final long processdefinitionId,\n            final long parentProcessInstanceId,\n            final long rootProcessInstanceId, final String errorCode, final String processName,\n            final long flowNodeDefinitionId, final String flowNodeName,\n            final long subProcessId) {\n        final SWaitingErrorEvent entity = new SWaitingErrorEvent(SBPMEventType.EVENT_SUB_PROCESS, processdefinitionId,\n                processName, flowNodeDefinitionId, flowNodeName, errorCode);\n        entity.setRootProcessInstanceId(rootProcessInstanceId);\n        entity.setParentProcessInstanceId(parentProcessInstanceId);\n        entity.setSubProcessId(subProcessId);\n        return new SWaitingErrorEventBuilderImpl(entity);\n    }\n\n    @Override\n    public String getErrorCodeKey() {\n        return \"errorCode\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/handling/impl/SWaitingErrorEventBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event.handling.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingErrorEventBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingErrorEvent;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class SWaitingErrorEventBuilderImpl implements SWaitingErrorEventBuilder {\n\n    private final SWaitingErrorEvent entity;\n\n    public SWaitingErrorEventBuilderImpl(final SWaitingErrorEvent entity) {\n        super();\n        this.entity = entity;\n    }\n\n    @Override\n    public SWaitingErrorEvent done() {\n        return entity;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/handling/impl/SWaitingEventKeyProviderBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event.handling.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingEventKeyProviderBuilderFactory;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class SWaitingEventKeyProviderBuilderFactoryImpl implements SWaitingEventKeyProviderBuilderFactory {\n\n    @Override\n    public String getIdKey() {\n        return \"id\";\n    }\n\n    @Override\n    public String getProcessDefinitionIdKey() {\n        return \"processDefinitionId\";\n    }\n\n    @Override\n    public String getRootProcessInstanceIdKey() {\n        return \"rootProcessInstanceId\";\n    }\n\n    @Override\n    public String getParentProcessInstanceIdKey() {\n        return \"parentProcessInstanceId\";\n    }\n\n    @Override\n    public String getFlowNodeInstanceIdKey() {\n        return \"flowNodeInstanceId\";\n    }\n\n    @Override\n    public String getSubProcessIdKey() {\n        return \"subProcessId\";\n    }\n\n    @Override\n    public String getProcessNameKey() {\n        return \"processName\";\n    }\n\n    @Override\n    public String getFlowNodeNameKey() {\n        return \"flowNodeName\";\n    }\n\n    @Override\n    public String getEventTypeKey() {\n        return \"eventType\";\n    }\n\n    @Override\n    public String getActiveKey() {\n        return \"active\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/handling/impl/SWaitingMessageEventBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event.handling.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingMessageEventBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingMessageEventBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SBPMEventType;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class SWaitingMessageEventBuilderFactoryImpl extends SWaitingEventKeyProviderBuilderFactoryImpl\n        implements SWaitingMessageEventBuilderFactory {\n\n    @Override\n    public SWaitingMessageEventBuilder createNewWaitingMessageStartEventInstance(final long processdefinitionId,\n            final String messageName,\n            final String processName, final long flowNodeDefinitionId, final String flowNodeName) {\n        final SWaitingMessageEvent entity = new SWaitingMessageEvent(SBPMEventType.START_EVENT, processdefinitionId,\n                processName, flowNodeDefinitionId, flowNodeName, messageName);\n        return new SWaitingMessageEventBuilderImpl(entity);\n    }\n\n    @Override\n    public SWaitingMessageEventBuilder createNewWaitingMessageEventSubProcInstance(final long processdefinitionId,\n            final long parentProcessInstanceId,\n            final long rootProcessInstanceId, final String messageName, final String processName,\n            final long flowNodeDefinitionId, final String flowNodeName,\n            final long subProcessId) {\n        final SWaitingMessageEvent entity = new SWaitingMessageEvent(SBPMEventType.EVENT_SUB_PROCESS,\n                processdefinitionId, processName, flowNodeDefinitionId, flowNodeName,\n                messageName);\n        entity.setRootProcessInstanceId(rootProcessInstanceId);\n        entity.setParentProcessInstanceId(parentProcessInstanceId);\n        entity.setSubProcessId(subProcessId);\n        return new SWaitingMessageEventBuilderImpl(entity);\n    }\n\n    @Override\n    public SWaitingMessageEventBuilder createNewWaitingMessageIntermediateEventInstance(final long processdefinitionId,\n            final long rootProcessInstanceId, final long processInstanceId,\n            final long flowNodeInstanceId, final String messageName, final String processName,\n            final long flowNodeDefinitionId, final String flowNodeName) {\n        final SWaitingMessageEvent entity = new SWaitingMessageEvent(SBPMEventType.INTERMEDIATE_CATCH_EVENT,\n                processdefinitionId, processName, flowNodeDefinitionId, flowNodeName,\n                messageName);\n        entity.setRootProcessInstanceId(rootProcessInstanceId);\n        entity.setParentProcessInstanceId(processInstanceId);\n        entity.setFlowNodeInstanceId(flowNodeInstanceId);\n        return new SWaitingMessageEventBuilderImpl(entity);\n    }\n\n    @Override\n    public SWaitingMessageEventBuilder createNewWaitingMessageBoundaryEventInstance(final long processdefinitionId,\n            final long rootProcessInstanceId, final long processInstanceId,\n            final long flowNodeInstanceId, final String messageName, final String processName,\n            final long flowNodeDefinitionId, final String flowNodeName) {\n        final SWaitingMessageEvent entity = new SWaitingMessageEvent(SBPMEventType.BOUNDARY_EVENT, processdefinitionId,\n                processName, flowNodeDefinitionId, flowNodeName, messageName);\n        entity.setRootProcessInstanceId(rootProcessInstanceId);\n        entity.setParentProcessInstanceId(processInstanceId);\n        entity.setFlowNodeInstanceId(flowNodeInstanceId);\n        return new SWaitingMessageEventBuilderImpl(entity);\n    }\n\n    @Override\n    public SWaitingMessageEventBuilder createNewInstance(final SWaitingMessageEvent waitingMessage) {\n        final SWaitingMessageEvent entity = new SWaitingMessageEvent(waitingMessage.getEventType(),\n                waitingMessage.getProcessDefinitionId(), waitingMessage.getProcessName(),\n                waitingMessage.getFlowNodeDefinitionId(), waitingMessage.getFlowNodeName(),\n                waitingMessage.getMessageName());\n        entity.setRootProcessInstanceId(waitingMessage.getRootProcessInstanceId());\n        entity.setParentProcessInstanceId(waitingMessage.getParentProcessInstanceId());\n        entity.setFlowNodeInstanceId(waitingMessage.getFlowNodeInstanceId());\n        return new SWaitingMessageEventBuilderImpl(entity);\n    }\n\n    @Override\n    public String getMessageNameKey() {\n        return \"messageName\";\n    }\n\n    @Override\n    public String getLockedKey() {\n        return \"locked\";\n    }\n\n    @Override\n    public String getProgressKey() {\n        return \"progress\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/handling/impl/SWaitingMessageEventBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event.handling.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingMessageEventBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class SWaitingMessageEventBuilderImpl implements SWaitingMessageEventBuilder {\n\n    private final SWaitingMessageEvent entity;\n\n    public SWaitingMessageEventBuilderImpl(final SWaitingMessageEvent entity) {\n        super();\n        this.entity = entity;\n    }\n\n    @Override\n    public SWaitingMessageEvent done() {\n        return entity;\n    }\n\n    @Override\n    public SWaitingMessageEventBuilder setCorrelation(final int index, final String correlation) {\n        switch (index) {\n            case 1:\n                entity.setCorrelation1(correlation);\n                break;\n            case 2:\n                entity.setCorrelation2(correlation);\n                break;\n            case 3:\n                entity.setCorrelation3(correlation);\n                break;\n            case 4:\n                entity.setCorrelation4(correlation);\n                break;\n            case 5:\n                entity.setCorrelation5(correlation);\n                break;\n            default:\n                break;\n        }\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/handling/impl/SWaitingSignalEventBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event.handling.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingSignalEventBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingSignalEventBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SBPMEventType;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent;\n\n/**\n * @author Zhao Na\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class SWaitingSignalEventBuilderFactoryImpl extends SWaitingEventKeyProviderBuilderFactoryImpl\n        implements SWaitingSignalEventBuilderFactory {\n\n    @Override\n    public SWaitingSignalEventBuilder createNewWaitingSignalStartEventInstance(final long processdefinitionId,\n            final String signalName,\n            final String processName, final long flowNodeDefinitionId, final String flowNodeName) {\n        final SWaitingSignalEvent entity = new SWaitingSignalEvent(SBPMEventType.START_EVENT, processdefinitionId,\n                processName, flowNodeDefinitionId, flowNodeName, signalName);\n        return new SWaitingSignalEventBuilderImpl(entity);\n    }\n\n    @Override\n    public SWaitingSignalEventBuilder createNewWaitingSignalEventSubProcInstance(final long processdefinitionId,\n            final long parentProcessInstanceId,\n            final long rootProcessInstanceId, final String signalName, final String processName,\n            final long flowNodeDefinitionId, final String flowNodeName,\n            final long subProcessId) {\n        final SWaitingSignalEvent entity = new SWaitingSignalEvent(SBPMEventType.EVENT_SUB_PROCESS, processdefinitionId,\n                processName, flowNodeDefinitionId, flowNodeName, signalName);\n        entity.setParentProcessInstanceId(parentProcessInstanceId);\n        entity.setRootProcessInstanceId(rootProcessInstanceId);\n        entity.setSubProcessId(subProcessId);\n        return new SWaitingSignalEventBuilderImpl(entity);\n    }\n\n    @Override\n    public SWaitingSignalEventBuilder createNewWaitingSignalIntermediateEventInstance(final long processdefinitionId,\n            final long rootProcessInstanceId, final long processInstanceId,\n            final long flowNodeInstanceId, final String signalName, final String processName,\n            final long flowNodeDefinitionId, final String flowNodeName) {\n        return createNonStartEvent(processdefinitionId, rootProcessInstanceId, processInstanceId, flowNodeInstanceId,\n                signalName, processName, flowNodeDefinitionId, flowNodeName,\n                SBPMEventType.INTERMEDIATE_CATCH_EVENT);\n    }\n\n    protected SWaitingSignalEventBuilder createNonStartEvent(final long processdefinitionId,\n            final long rootProcessInstanceId, final long processInstanceId, final long flowNodeInstanceId,\n            final String signalName,\n            final String processName, final long flowNodeDefinitionId, final String flowNodeName,\n            final SBPMEventType eventType) {\n        final SWaitingSignalEvent entity = new SWaitingSignalEvent(eventType, processdefinitionId, processName,\n                flowNodeDefinitionId, flowNodeName, signalName);\n        entity.setFlowNodeInstanceId(flowNodeInstanceId);\n        entity.setRootProcessInstanceId(rootProcessInstanceId);\n        entity.setParentProcessInstanceId(processInstanceId);\n        return new SWaitingSignalEventBuilderImpl(entity);\n    }\n\n    @Override\n    public SWaitingSignalEventBuilder createNewWaitingSignalBoundaryEventInstance(final long processdefinitionId,\n            final long rootProcessInstanceId, final long processInstanceId,\n            final long flowNodeInstanceId, final String signalName, final String processName,\n            final long flowNodeDefinitionId, final String flowNodeName) {\n        return createNonStartEvent(processdefinitionId, rootProcessInstanceId, processInstanceId, flowNodeInstanceId,\n                signalName, processName, flowNodeDefinitionId, flowNodeName,\n                SBPMEventType.BOUNDARY_EVENT);\n    }\n\n    public static SWaitingSignalEventBuilder getInstance() {\n        return new SWaitingSignalEventBuilderImpl(null);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/handling/impl/SWaitingSignalEventBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event.handling.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingSignalEventBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent;\n\n/**\n * @author Zhao Na\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class SWaitingSignalEventBuilderImpl implements SWaitingSignalEventBuilder {\n\n    private final SWaitingSignalEvent entity;\n\n    public SWaitingSignalEventBuilderImpl(final SWaitingSignalEvent entity) {\n        super();\n        this.entity = entity;\n    }\n\n    @Override\n    public SWaitingSignalEvent done() {\n        return entity;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/impl/SBoundaryEventInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SBoundaryEventInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SBoundaryEventInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class SBoundaryEventInstanceBuilderFactoryImpl extends SEventInstanceBuilderFactoryImpl\n        implements SBoundaryEventInstanceBuilderFactory {\n\n    @Override\n    public SBoundaryEventInstanceBuilder createNewBoundaryEventInstance(final String name, final boolean isInterrupting,\n            final long flowNodeDefinitionId,\n            final long rootContainerId, final long parentContainerId, final long processDefinitionId,\n            final long rootProcessInstanceId,\n            final long parentProcessInstanceId, final long activityInstanceId) {\n        final SBoundaryEventInstance entity = new SBoundaryEventInstance(name, flowNodeDefinitionId, rootContainerId,\n                parentContainerId, processDefinitionId, rootProcessInstanceId);\n        entity.setLogicalGroup(PARENT_PROCESS_INSTANCE_INDEX, parentProcessInstanceId);\n        entity.setActivityInstanceId(activityInstanceId);\n        entity.setInterrupting(isInterrupting);\n        return new SBoundaryEventInstanceBuilderImpl(entity);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/impl/SBoundaryEventInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SBoundaryEventInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SEventInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class SBoundaryEventInstanceBuilderImpl extends SEventInstanceBuilderImpl\n        implements SBoundaryEventInstanceBuilder {\n\n    protected SBoundaryEventInstanceBuilderImpl(final SBoundaryEventInstance entity) {\n        super(entity);\n    }\n\n    @Override\n    public SEventInstance done() {\n        return (SEventInstance) entity;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/impl/SEndEventInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SEndEventInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SEndEventInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.event.SEndEventInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class SEndEventInstanceBuilderFactoryImpl extends SEventInstanceBuilderFactoryImpl\n        implements SEndEventInstanceBuilderFactory {\n\n    @Override\n    public SEndEventInstanceBuilder createNewEndEventInstance(final String name, final long flowNodeDefinitionId,\n            final long rootContainerId,\n            final long parentContainerId, final long processDefinitionId, final long rootProcessInstanceId,\n            final long parentProcessInstanceId) {\n        final SEndEventInstance entity = new SEndEventInstance(name, flowNodeDefinitionId, rootContainerId,\n                parentContainerId, processDefinitionId, rootProcessInstanceId);\n        entity.setLogicalGroup(PARENT_PROCESS_INSTANCE_INDEX, parentProcessInstanceId);\n        return new SEndEventInstanceBuilderImpl(entity);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/impl/SEndEventInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SEndEventInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.event.SEndEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SEventInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class SEndEventInstanceBuilderImpl extends SEventInstanceBuilderImpl implements SEndEventInstanceBuilder {\n\n    public SEndEventInstanceBuilderImpl(final SEndEventInstance entity) {\n        super(entity);\n    }\n\n    @Override\n    public SEventInstance done() {\n        return (SEventInstance) entity;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/impl/SEventInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SEventInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.impl.SFlowNodeInstanceBuilderFactoryImpl;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Baptiste Mesta\n */\npublic abstract class SEventInstanceBuilderFactoryImpl extends SFlowNodeInstanceBuilderFactoryImpl\n        implements SEventInstanceBuilderFactory {\n\n    private static final String ID_KEY = \"id\";\n\n    private static final String NAME_KEY = \"name\";\n\n    @Override\n    public String getIdKey() {\n        return ID_KEY;\n    }\n\n    @Override\n    public String getNameKey() {\n        return NAME_KEY;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/impl/SEventInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SEventInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.impl.SFlowNodeInstanceBuilderImpl;\nimport org.bonitasoft.engine.core.process.instance.model.event.SEventInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Baptiste Mesta\n */\npublic abstract class SEventInstanceBuilderImpl extends SFlowNodeInstanceBuilderImpl implements SEventInstanceBuilder {\n\n    protected SEventInstanceBuilderImpl(SEventInstance entity) {\n        super(entity);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/impl/SIntermediateCatchEventInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SIntermediateCatchEventInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SIntermediateCatchEventInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.event.SIntermediateCatchEventInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Baptiste Mesta\n */\npublic class SIntermediateCatchEventInstanceBuilderFactoryImpl extends SEventInstanceBuilderFactoryImpl\n        implements SIntermediateCatchEventInstanceBuilderFactory {\n\n    @Override\n    public SIntermediateCatchEventInstanceBuilder createNewIntermediateCatchEventInstance(final String name,\n            final long flowNodeDefinitionId,\n            final long rootContainerId, final long parentContainerId, final long processDefinitionId,\n            final long rootProcessInstanceId,\n            final long parentProcessInstanceId) {\n        final SIntermediateCatchEventInstance entity = new SIntermediateCatchEventInstance(name, flowNodeDefinitionId,\n                rootContainerId, parentContainerId, processDefinitionId,\n                rootProcessInstanceId);\n        entity.setLogicalGroup(PARENT_PROCESS_INSTANCE_INDEX, parentProcessInstanceId);\n        return new SIntermediateCatchEventInstanceBuilderImpl(entity);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/impl/SIntermediateCatchEventInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SIntermediateCatchEventInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.event.SEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SIntermediateCatchEventInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Baptiste Mesta\n */\npublic class SIntermediateCatchEventInstanceBuilderImpl extends SEventInstanceBuilderImpl\n        implements SIntermediateCatchEventInstanceBuilder {\n\n    public SIntermediateCatchEventInstanceBuilderImpl(final SIntermediateCatchEventInstance entity) {\n        super(entity);\n    }\n\n    @Override\n    public SEventInstance done() {\n        return (SEventInstance) entity;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/impl/SIntermediateThrowEventInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SIntermediateThrowEventInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SIntermediateThrowEventInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.event.SIntermediateThrowEventInstance;\n\n/**\n * @author Matthieu Chaffotte\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class SIntermediateThrowEventInstanceBuilderFactoryImpl extends SEventInstanceBuilderFactoryImpl\n        implements SIntermediateThrowEventInstanceBuilderFactory {\n\n    @Override\n    public SIntermediateThrowEventInstanceBuilder createNewIntermediateThrowEventInstance(final String name,\n            final long flowNodeDefinitionId,\n            final long rootContainerId, final long parentContainerId, final long processDefinitionId,\n            final long rootProcessInstanceId,\n            final long parentProcessInstanceId) {\n        final SIntermediateThrowEventInstance entity = new SIntermediateThrowEventInstance(name, flowNodeDefinitionId,\n                rootContainerId, parentContainerId, processDefinitionId,\n                rootProcessInstanceId);\n        entity.setLogicalGroup(PARENT_PROCESS_INSTANCE_INDEX, parentProcessInstanceId);\n        return new SIntermediateThrowEventInstanceBuilderImpl(entity);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/impl/SIntermediateThrowEventInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SIntermediateThrowEventInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.event.SEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SIntermediateThrowEventInstance;\n\n/**\n * @author Matthieu Chaffotte\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class SIntermediateThrowEventInstanceBuilderImpl extends SEventInstanceBuilderImpl\n        implements SIntermediateThrowEventInstanceBuilder {\n\n    public SIntermediateThrowEventInstanceBuilderImpl(final SIntermediateThrowEventInstance entity) {\n        super(entity);\n    }\n\n    @Override\n    public SEventInstance done() {\n        return (SEventInstance) entity;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/impl/SStartEventInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SStartEventInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SStartEventInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.event.SStartEventInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class SStartEventInstanceBuilderFactoryImpl extends SEventInstanceBuilderFactoryImpl\n        implements SStartEventInstanceBuilderFactory {\n\n    @Override\n    public SStartEventInstanceBuilder createNewStartEventInstance(final String name, final long flowNodeDefinitionId,\n            final long rootContainerId,\n            final long parentContainerId, final long processDefinitionId, final long rootProcessInstanceId,\n            final long parentProcessInstanceId) {\n        final SStartEventInstance entity = new SStartEventInstance(name, flowNodeDefinitionId, rootContainerId,\n                parentContainerId, processDefinitionId, rootProcessInstanceId);\n        entity.setLogicalGroup(PARENT_PROCESS_INSTANCE_INDEX, parentProcessInstanceId);\n        return new SStartEventInstanceBuilderImpl(entity);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/impl/SStartEventInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.event.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.builder.event.SStartEventInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.event.SStartEventInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class SStartEventInstanceBuilderImpl extends SEventInstanceBuilderImpl implements SStartEventInstanceBuilder {\n\n    public SStartEventInstanceBuilderImpl(final SStartEventInstance entity) {\n        super(entity);\n    }\n\n    @Override\n    public SStartEventInstance done() {\n        return (SStartEventInstance) entity;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SActivityInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.builder.SActivityInstanceBuilderFactory;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n */\npublic abstract class SActivityInstanceBuilderFactoryImpl extends SFlowNodeInstanceBuilderFactoryImpl\n        implements SActivityInstanceBuilderFactory {\n\n    private static final String ID_KEY = \"id\";\n\n    private static final String NAME_KEY = \"name\";\n\n    private static final String TOKEN_COUNT_KEY = \"tokenCount\";\n\n    private static final String ABORTED_BY_BOUNDARY_KEY = \"abortedByBoundary\";\n\n    @Override\n    public String getIdKey() {\n        return ID_KEY;\n    }\n\n    @Override\n    public String getNameKey() {\n        return NAME_KEY;\n    }\n\n    @Override\n    public String getTokenCountKey() {\n        return TOKEN_COUNT_KEY;\n    }\n\n    @Override\n    public String getAbortedByBoundaryEventIdKey() {\n        return ABORTED_BY_BOUNDARY_KEY;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SActivityInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SActivityInstanceBuilder;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n */\npublic abstract class SActivityInstanceBuilderImpl extends SFlowNodeInstanceBuilderImpl\n        implements SActivityInstanceBuilder {\n\n    protected SActivityInstanceBuilderImpl(final SActivityInstance entity) {\n        super(entity);\n    }\n\n    @Override\n    public SActivityInstanceBuilder setName(final String name) {\n        this.entity.setName(name);\n        return this;\n    }\n\n    @Override\n    public SActivityInstanceBuilder setDescription(final String description) {\n        this.entity.setDescription(description);\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SAutomaticTaskInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SAutomaticTaskInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SAutomaticTaskInstanceBuilderFactory;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class SAutomaticTaskInstanceBuilderFactoryImpl extends SActivityInstanceBuilderFactoryImpl\n        implements SAutomaticTaskInstanceBuilderFactory {\n\n    @Override\n    public SAutomaticTaskInstanceBuilder createNewAutomaticTaskInstance(final String name,\n            final long flowNodeDefinitionId, final long rootContainerId,\n            final long parentContainerId, final long processDefinitionId, final long rootProcessInstanceId,\n            final long parentProcessInstanceId) {\n        final SAutomaticTaskInstance activityInstanceImpl = new SAutomaticTaskInstance(name, flowNodeDefinitionId,\n                rootContainerId, parentContainerId, processDefinitionId,\n                rootProcessInstanceId);\n        activityInstanceImpl.setLogicalGroup(PARENT_PROCESS_INSTANCE_INDEX, parentProcessInstanceId);\n        return new SAutomaticTaskInstanceBuilderImpl(activityInstanceImpl);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SAutomaticTaskInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SAutomaticTaskInstanceBuilder;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class SAutomaticTaskInstanceBuilderImpl extends SActivityInstanceBuilderImpl\n        implements SAutomaticTaskInstanceBuilder {\n\n    public SAutomaticTaskInstanceBuilderImpl(final SAutomaticTaskInstance activityInstanceImpl) {\n        super(activityInstanceImpl);\n    }\n\n    @Override\n    public SAutomaticTaskInstance done() {\n        return (SAutomaticTaskInstance) this.entity;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SCallActivityInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SCallActivityInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SCallActivityInstanceBuilderFactory;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class SCallActivityInstanceBuilderFactoryImpl extends SActivityInstanceBuilderFactoryImpl\n        implements SCallActivityInstanceBuilderFactory {\n\n    @Override\n    public SCallActivityInstanceBuilder createNewCallActivityInstance(final String name,\n            final long flowNodeDefinitionId, final long rootContainerId,\n            final long parentContainerId, final long processDefinitionId, final long rootProcessInstanceId,\n            final long parentProcessInstanceId) {\n        final SCallActivityInstance callActivityInstance = new SCallActivityInstance(name, flowNodeDefinitionId,\n                rootContainerId, parentContainerId, processDefinitionId,\n                rootProcessInstanceId);\n        callActivityInstance.setLogicalGroup(PARENT_PROCESS_INSTANCE_INDEX, parentProcessInstanceId);\n        return new SCallActivityInstanceBuilderImpl(callActivityInstance);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SCallActivityInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SCallActivityInstanceBuilder;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class SCallActivityInstanceBuilderImpl extends SActivityInstanceBuilderImpl\n        implements SCallActivityInstanceBuilder {\n\n    public SCallActivityInstanceBuilderImpl(final SCallActivityInstance callActivityInstance) {\n        super(callActivityInstance);\n    }\n\n    @Override\n    public SCallActivityInstance done() {\n        return (SCallActivityInstance) this.entity;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SFlowElementInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.builder.SFlowElementInstanceBuilderFactory;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Yanyan Liu\n */\npublic abstract class SFlowElementInstanceBuilderFactoryImpl implements SFlowElementInstanceBuilderFactory {\n\n    protected static final String ROOT_CONTAINER_ID_KEY = \"rootContainerId\";\n\n    protected static final String PARENT_CONTAINER_ID_KEY = \"parentContainerId\";\n\n    protected static final String LOGICAL_GROUP1_KEY = \"logicalGroup1\";\n\n    protected static final String LOGICAL_GROUP2_KEY = \"logicalGroup2\";\n\n    protected static final String LOGICAL_GROUP3_KEY = \"logicalGroup3\";\n\n    protected static final String LOGICAL_GROUP4_KEY = \"logicalGroup4\";\n\n    protected static final String STABLE_KEY = \"stable\";\n\n    protected static final String TERMINAL_KEY = \"terminal\";\n\n    protected static final int PROCESS_DEFINITION_INDEX = 0;\n\n    protected static final int ROOT_PROCESS_INSTANCE_INDEX = 1;\n\n    protected static final int PARENT_ACTIVITY_INSTANCE_INDEX = 2;\n\n    protected static final int PARENT_PROCESS_INSTANCE_INDEX = 3;\n\n    @Override\n    public String getRootContainerIdKey() {\n        return ROOT_CONTAINER_ID_KEY;\n    }\n\n    @Override\n    public String getParentContainerIdKey() {\n        return PARENT_CONTAINER_ID_KEY;\n    }\n\n    @Override\n    public String getProcessDefinitionKey() {\n        return LOGICAL_GROUP1_KEY;\n    }\n\n    @Override\n    public String getRootProcessInstanceKey() {\n        return LOGICAL_GROUP2_KEY;\n    }\n\n    @Override\n    public String getParentProcessInstanceKey() {\n        return LOGICAL_GROUP4_KEY;\n    }\n\n    @Override\n    public String getParentActivityInstanceKey() {\n        return LOGICAL_GROUP3_KEY;\n    }\n\n    @Override\n    public String getStateCategoryKey() {\n        return \"stateCategory\";\n    }\n\n    @Override\n    public int getProcessDefinitionIndex() {\n        return PROCESS_DEFINITION_INDEX;\n    }\n\n    @Override\n    public int getRootProcessInstanceIndex() {\n        return ROOT_PROCESS_INSTANCE_INDEX;\n    }\n\n    @Override\n    public int getParentProcessInstanceIndex() {\n        return PARENT_PROCESS_INSTANCE_INDEX;\n    }\n\n    @Override\n    public int getParentActivityInstanceIndex() {\n        return PARENT_ACTIVITY_INSTANCE_INDEX;\n    }\n\n    @Override\n    public String getStableKey() {\n        return STABLE_KEY;\n    }\n\n    @Override\n    public String getTerminalKey() {\n        return TERMINAL_KEY;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SFlowNodeInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.builder.SFlowNodeInstanceBuilderFactory;\n\n/**\n * @author Baptiste Mesta\n */\npublic abstract class SFlowNodeInstanceBuilderFactoryImpl extends SFlowElementInstanceBuilderFactoryImpl\n        implements SFlowNodeInstanceBuilderFactory {\n\n    protected static final String DISPLAY_DESCRIPTION = \"displayDescription\";\n\n    protected static final String DISPLAY_NAME = \"displayName\";\n\n    protected static final String STATE_ID_KEY = \"stateId\";\n\n    protected static final String STATE_NAME_KEY = \"stateName\";\n\n    protected static final String PREVIOUS_STATE_ID_KEY = \"previousStateId\";\n\n    protected static final String LAST_UPDATE_KEY = \"lastUpdateDate\";\n\n    protected static final String REACHED_STATE_DATE_KEY = \"reachedStateDate\";\n\n    protected static final String EXECUTE_BY_KEY = \"executedBy\";\n\n    protected static final String EXECUTE_FOR_KEY = \"executedBySubstitute\";\n\n    protected static final String STATE_EXECUTING_KEY = \"stateExecuting\";\n\n    @Override\n    public String getDisplayDescriptionKey() {\n        return DISPLAY_DESCRIPTION;\n    }\n\n    @Override\n    public String getDisplayNameKey() {\n        return DISPLAY_NAME;\n    }\n\n    @Override\n    public String getStateExecutingKey() {\n        return STATE_EXECUTING_KEY;\n    }\n\n    @Override\n    public String getExecutedBy() {\n        return EXECUTE_BY_KEY;\n    }\n\n    @Override\n    public String getExecutedBySubstitute() {\n        return EXECUTE_FOR_KEY;\n    }\n\n    @Override\n    public String getStateIdKey() {\n        return STATE_ID_KEY;\n    }\n\n    @Override\n    public String getStateNameKey() {\n        return STATE_NAME_KEY;\n    }\n\n    @Override\n    public String getPreviousStateIdKey() {\n        return PREVIOUS_STATE_ID_KEY;\n    }\n\n    @Override\n    public String getLastUpdateDateKey() {\n        return LAST_UPDATE_KEY;\n    }\n\n    @Override\n    public String getReachStateDateKey() {\n        return REACHED_STATE_DATE_KEY;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SFlowNodeInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.impl;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SFlowNodeInstanceBuilder;\n\n/**\n * @author Baptiste Mesta\n */\npublic abstract class SFlowNodeInstanceBuilderImpl implements SFlowNodeInstanceBuilder {\n\n    protected final SFlowNodeInstance entity;\n\n    public SFlowNodeInstanceBuilderImpl(final SFlowNodeInstance entity) {\n        super();\n        this.entity = entity;\n    }\n\n    @Override\n    public SFlowNodeInstanceBuilder setState(final FlowNodeState state) {\n        entity.setStateId(state.getId());\n        entity.setStateName(state.getName());\n        entity.setStable(state.isStable());\n        entity.setTerminal(state.isTerminal());\n        return this;\n    }\n\n    @Override\n    public SFlowNodeInstanceBuilder setLastUpdateDate(final long lastUpdateDate) {\n        this.entity.setLastUpdateDate(lastUpdateDate);\n        return this;\n    }\n\n    @Override\n    public SFlowNodeInstanceBuilder setReachedStateDate(final long reachedStateDate) {\n        this.entity.setReachedStateDate(reachedStateDate);\n        return this;\n    }\n\n    @Override\n    public SFlowNodeInstanceBuilder setRootContainerId(final long containerId) {\n        this.entity.setRootContainerId(containerId);\n        return this;\n    }\n\n    @Override\n    public SFlowNodeInstanceBuilder setParentContainerId(final long processInstanceId) {\n        this.entity.setParentContainerId(processInstanceId);\n        return this;\n    }\n\n    @Override\n    public SFlowNodeInstanceBuilder setProcessDefinitionId(final long processDefinitionId) {\n        this.entity.setLogicalGroup(SFlowNodeInstanceBuilderFactoryImpl.PROCESS_DEFINITION_INDEX, processDefinitionId);\n        return this;\n    }\n\n    @Override\n    public SFlowNodeInstanceBuilder setRootProcessInstanceId(final long processInstanceId) {\n        this.entity.setLogicalGroup(SFlowNodeInstanceBuilderFactoryImpl.ROOT_PROCESS_INSTANCE_INDEX, processInstanceId);\n        return this;\n    }\n\n    @Override\n    public SFlowNodeInstanceBuilder setParentProcessInstanceId(final long parentProcessInstanceId) {\n        this.entity.setLogicalGroup(SFlowNodeInstanceBuilderFactoryImpl.PARENT_PROCESS_INSTANCE_INDEX,\n                parentProcessInstanceId);\n        return this;\n    }\n\n    @Override\n    public SFlowNodeInstanceBuilder setParentActivityInstanceId(final long activityInstanceId) {\n        this.entity.setLogicalGroup(SFlowNodeInstanceBuilderFactoryImpl.PARENT_ACTIVITY_INSTANCE_INDEX,\n                activityInstanceId);\n        return this;\n    }\n\n    @Override\n    public SFlowNodeInstanceBuilder setLoopCounter(final int loopCounter) {\n        this.entity.setLoopCounter(loopCounter);\n        return this;\n    }\n\n    @Override\n    public SFlowNodeInstanceBuilder setStateCategory(final SStateCategory stateCategory) {\n        this.entity.setStateCategory(stateCategory);\n        return this;\n    }\n\n    @Override\n    public SFlowNodeType getFlowNodeType() {\n        return this.entity.getType();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SGatewayInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.impl;\n\nimport org.bonitasoft.engine.core.process.definition.model.SGatewayType;\nimport org.bonitasoft.engine.core.process.instance.model.SGatewayInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SGatewayInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SGatewayInstanceBuilderFactory;\n\n/**\n * @author Feng Hui\n * @author Celine Souchet\n */\npublic class SGatewayInstanceBuilderFactoryImpl extends SFlowNodeInstanceBuilderFactoryImpl\n        implements SGatewayInstanceBuilderFactory {\n\n    private static final String ID_KEY = \"id\";\n\n    private static final String NAME_KEY = \"name\";\n\n    private static final String STATE_ID_KEY = \"stateId\";\n\n    private static final String GATEWAY_TYPE_KEY = \"gatewayType\";\n\n    private static final String HITBYS = \"hitBys\";\n\n    @Override\n    public SGatewayInstanceBuilder createNewInstance(final String name, final long flowNodeDefinitionId,\n            final long rootContainerId,\n            final long parentContainerId, final SGatewayType gatewayType, final long processDefinitionId,\n            final long rootProcessInstanceId,\n            final long parentProcessInstanceId) {\n        final SGatewayInstance entity = new SGatewayInstance(name, flowNodeDefinitionId, rootContainerId,\n                parentContainerId, gatewayType, processDefinitionId,\n                rootProcessInstanceId);\n        entity.setLogicalGroup(PARENT_PROCESS_INSTANCE_INDEX, parentProcessInstanceId);\n        return new SGatewayInstanceBuilderImpl(entity);\n    }\n\n    @Override\n    public String getIdKey() {\n        return ID_KEY;\n    }\n\n    @Override\n    public String getNameKey() {\n        return NAME_KEY;\n    }\n\n    @Override\n    public String getStateIdKey() {\n        return STATE_ID_KEY;\n    }\n\n    @Override\n    public String getGatewayTypeKey() {\n        return GATEWAY_TYPE_KEY;\n    }\n\n    @Override\n    public String getHitBysKey() {\n        return HITBYS;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SGatewayInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.impl;\n\nimport org.bonitasoft.engine.core.process.definition.model.SGatewayType;\nimport org.bonitasoft.engine.core.process.instance.model.SGatewayInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SGatewayInstanceBuilder;\n\n/**\n * @author Feng Hui\n * @author Celine Souchet\n */\npublic class SGatewayInstanceBuilderImpl extends SFlowNodeInstanceBuilderImpl implements SGatewayInstanceBuilder {\n\n    public SGatewayInstanceBuilderImpl(final SGatewayInstance entity) {\n        super(entity);\n    }\n\n    @Override\n    public SGatewayInstanceBuilder setStateId(final int stateId) {\n        entity.setStateId(stateId);\n        return this;\n    }\n\n    @Override\n    public SGatewayInstanceBuilder setGatewayType(final SGatewayType gatewayType) {\n        ((SGatewayInstance) entity).setGatewayType(gatewayType);\n        return this;\n    }\n\n    @Override\n    public SGatewayInstanceBuilder setHitBys(final String hitBys) {\n        ((SGatewayInstance) entity).setHitBys(hitBys);\n        return this;\n    }\n\n    @Override\n    public SGatewayInstanceBuilder setParentContainerId(final long containerId) {\n        entity.setParentContainerId(containerId);\n        return this;\n    }\n\n    @Override\n    public SGatewayInstanceBuilder setRootContainerId(final long containerId) {\n        entity.setRootContainerId(containerId);\n        return this;\n    }\n\n    @Override\n    public SGatewayInstance done() {\n        return (SGatewayInstance) entity;\n    }\n\n    @Override\n    public SGatewayInstanceBuilder setParentActivityInstanceId(final long parentActivityInstanceId) {\n        entity.setLogicalGroup(SGatewayInstanceBuilderFactoryImpl.PARENT_ACTIVITY_INSTANCE_INDEX,\n                parentActivityInstanceId);\n        return this;\n    }\n\n    @Override\n    public SGatewayInstanceBuilder setProcessInstanceId(final long processInstanceId) {\n        entity.setLogicalGroup(SGatewayInstanceBuilderFactoryImpl.ROOT_PROCESS_INSTANCE_INDEX, processInstanceId);\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SHumanTaskInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.builder.SHumanTaskInstanceBuilderFactory;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic abstract class SHumanTaskInstanceBuilderFactoryImpl extends SActivityInstanceBuilderFactoryImpl\n        implements SHumanTaskInstanceBuilderFactory {\n\n    private static final String ASSIGNEE_ID_KEY = \"assigneeId\";\n\n    private static final String CLAIMED_DATE = \"claimedDate\";\n\n    private static final String EXPECTED_END_DATE_KEY = \"expectedEndDate\";\n\n    private static final String PRIORITY_KEY = \"priority\";\n\n    @Override\n    public String getAssigneeIdKey() {\n        return ASSIGNEE_ID_KEY;\n    }\n\n    @Override\n    public String getClaimedDateKey() {\n        return CLAIMED_DATE;\n    }\n\n    @Override\n    public String getPriorityKey() {\n        return PRIORITY_KEY;\n    }\n\n    @Override\n    public String getExpectedEndDateKey() {\n        return EXPECTED_END_DATE_KEY;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SHumanTaskInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.STaskPriority;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SHumanTaskInstanceBuilder;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic abstract class SHumanTaskInstanceBuilderImpl extends SActivityInstanceBuilderImpl\n        implements SHumanTaskInstanceBuilder {\n\n    protected SHumanTaskInstanceBuilderImpl(final SHumanTaskInstance entity) {\n        super(entity);\n    }\n\n    @Override\n    public SHumanTaskInstanceBuilder setAssigneeId(final long assigneeId) {\n        ((SHumanTaskInstance) this.entity).setAssigneeId(assigneeId);\n        ((SHumanTaskInstance) this.entity).setClaimedDate(System.currentTimeMillis());\n        return this;\n    }\n\n    @Override\n    public SHumanTaskInstanceBuilder setPriority(final STaskPriority priority) {\n        ((SHumanTaskInstance) this.entity).setPriority(priority);\n        return this;\n    }\n\n    @Override\n    public SHumanTaskInstanceBuilder setExpectedEndDate(final Long expectedEndDate) {\n        ((SHumanTaskInstance) this.entity).setExpectedEndDate(expectedEndDate);\n        return this;\n    }\n\n    @Override\n    public SHumanTaskInstanceBuilder setDisplayDescription(final String displayDescription) {\n        this.entity.setDisplayDescription(displayDescription);\n        return this;\n    }\n\n    @Override\n    public SHumanTaskInstanceBuilder setDisplayName(final String displayName) {\n        this.entity.setDisplayName(displayName);\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SLoopActivityInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SLoopActivityInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SLoopActivityInstanceBuilderFactory;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class SLoopActivityInstanceBuilderFactoryImpl extends SActivityInstanceBuilderFactoryImpl\n        implements SLoopActivityInstanceBuilderFactory {\n\n    @Override\n    public SLoopActivityInstanceBuilder createNewOuterTaskInstance(final String name, final long flowNodeDefinitionId,\n            final long rootContainerId,\n            final long parentContainerId, final long processDefinitionId, final long rootProcessInstanceId,\n            final long parentProcessInstanceId) {\n        final SLoopActivityInstance activityInstanceImpl = new SLoopActivityInstance(name, flowNodeDefinitionId,\n                rootContainerId, parentContainerId, processDefinitionId,\n                rootProcessInstanceId);\n        activityInstanceImpl.setLogicalGroup(PARENT_PROCESS_INSTANCE_INDEX, parentProcessInstanceId);\n        return new SLoopActivityInstanceBuilderImpl(activityInstanceImpl);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SLoopActivityInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SLoopActivityInstanceBuilder;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class SLoopActivityInstanceBuilderImpl extends SActivityInstanceBuilderImpl\n        implements SLoopActivityInstanceBuilder {\n\n    public SLoopActivityInstanceBuilderImpl(final SLoopActivityInstance activityInstanceImpl) {\n        super(activityInstanceImpl);\n    }\n\n    @Override\n    public SLoopActivityInstance done() {\n        return (SLoopActivityInstance) this.entity;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SManualTaskInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.STaskPriority;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SManualTaskInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SManualTaskInstanceBuilderFactory;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class SManualTaskInstanceBuilderFactoryImpl extends SHumanTaskInstanceBuilderFactoryImpl\n        implements SManualTaskInstanceBuilderFactory {\n\n    @Override\n    public SManualTaskInstanceBuilder createNewManualTaskInstance(final String name, final long flowNodeDefinitionId,\n            final long rootContainerId,\n            final long parentContainerId, final long actorId, final long processDefinitionId,\n            final long rootProcessInstanceId,\n            final long parentProcessInstanceId) {\n        final SManualTaskInstance activityInst = new SManualTaskInstance(name, flowNodeDefinitionId, rootContainerId,\n                parentContainerId, actorId,\n                STaskPriority.NORMAL, processDefinitionId, rootProcessInstanceId);\n        activityInst.setLogicalGroup(PARENT_PROCESS_INSTANCE_INDEX, parentProcessInstanceId);\n        return new SManualTaskInstanceBuilderImpl(activityInst);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SManualTaskInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SManualTaskInstanceBuilder;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class SManualTaskInstanceBuilderImpl extends SHumanTaskInstanceBuilderImpl\n        implements SManualTaskInstanceBuilder {\n\n    public SManualTaskInstanceBuilderImpl(final SManualTaskInstance activityInst) {\n        super(activityInst);\n    }\n\n    @Override\n    public SManualTaskInstance done() {\n        return (SManualTaskInstance) this.entity;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SMultiInstanceActivityInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SMultiInstanceActivityInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SMultiInstanceActivityInstanceBuilderFactory;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class SMultiInstanceActivityInstanceBuilderFactoryImpl extends SActivityInstanceBuilderFactoryImpl\n        implements SMultiInstanceActivityInstanceBuilderFactory {\n\n    @Override\n    public SMultiInstanceActivityInstanceBuilder createNewOuterTaskInstance(final String name,\n            final long flowNodeDefinitionId, final long rootContainerId,\n            final long parentContainerId, final long processDefinitionId, final long rootProcessInstanceId,\n            final long parentProcessInstanceId,\n            final boolean isSequential) {\n        final SMultiInstanceActivityInstance entity = new SMultiInstanceActivityInstance(name, flowNodeDefinitionId,\n                rootContainerId, parentContainerId, processDefinitionId,\n                rootProcessInstanceId, isSequential);\n        entity.setLogicalGroup(PARENT_PROCESS_INSTANCE_INDEX, parentProcessInstanceId);\n        return new SMultiInstanceActivityInstanceBuilderImpl(entity);\n    }\n\n    @Override\n    public String getLoopCardinalityKey() {\n        return \"loopCardinality\";\n    }\n\n    @Override\n    public String getNumberOfActiveInstancesKey() {\n        return \"numberOfActiveInstances\";\n    }\n\n    @Override\n    public String getNumberOfCompletedInstancesKey() {\n        return \"numberOfCompletedInstances\";\n    }\n\n    @Override\n    public String getNumberOfTerminatedInstancesKey() {\n        return \"numberOfTerminatedInstances\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SMultiInstanceActivityInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SMultiInstanceActivityInstanceBuilder;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class SMultiInstanceActivityInstanceBuilderImpl extends SActivityInstanceBuilderImpl\n        implements SMultiInstanceActivityInstanceBuilder {\n\n    public SMultiInstanceActivityInstanceBuilderImpl(final SMultiInstanceActivityInstance entity) {\n        super(entity);\n    }\n\n    @Override\n    public SMultiInstanceActivityInstance done() {\n        return (SMultiInstanceActivityInstance) entity;\n    }\n\n    @Override\n    public SMultiInstanceActivityInstanceBuilder setLoopDataInputRef(final String loopDataInputRef) {\n        ((SMultiInstanceActivityInstance) entity).setLoopDataInputRef(loopDataInputRef);\n        return this;\n    }\n\n    @Override\n    public SMultiInstanceActivityInstanceBuilder setLoopDataOutputRef(final String loopDataOutputRef) {\n        ((SMultiInstanceActivityInstance) entity).setLoopDataOutputRef(loopDataOutputRef);\n        return this;\n    }\n\n    @Override\n    public SMultiInstanceActivityInstanceBuilder setDataInputItemRef(final String dataInputItemRef) {\n        ((SMultiInstanceActivityInstance) entity).setDataInputItemRef(dataInputItemRef);\n        return this;\n    }\n\n    @Override\n    public SMultiInstanceActivityInstanceBuilder setDataOutputItemRef(final String dataOutputItemRef) {\n        ((SMultiInstanceActivityInstance) entity).setDataOutputItemRef(dataOutputItemRef);\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SProcessInstanceLogIndexesMapper.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.impl;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Feng Hui\n * @author Matthieu Chaffotte\n */\npublic class SProcessInstanceLogIndexesMapper {\n\n    public static final int PROCESS_INSTANCE_INDEX = 0;\n\n    public static final int ACTIVITY_INSTANCE_INDEX = 1;\n\n    public static final int GATEWAY_INSTANCE_INDEX = 2;\n\n    public static final int TRANSITION_INSTANCE_INDEX = 3;\n\n    public static final String PROCESS_INSTANCE_NAME = \"numericIndex1\";\n\n    public static final String ACTIVITY_INSTANCE_NAME = \"numericIndex2\";\n\n    public static final String GATEWAY_INSTANCE_NAME = \"numericIndex3\";\n\n    public static final String TRANSITION_INSTANCE_NAME = \"numericIndex4\";\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SReceiveTaskInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SReceiveTaskInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SReceiveTaskInstanceBuilderFactory;\n\n/**\n * @author Julien Molinaro\n */\npublic class SReceiveTaskInstanceBuilderFactoryImpl extends SActivityInstanceBuilderFactoryImpl\n        implements SReceiveTaskInstanceBuilderFactory {\n\n    @Override\n    public SReceiveTaskInstanceBuilder createNewReceiveTaskInstance(final String name, final long flowNodeDefinitionId,\n            final long rootContainerId,\n            final long parentContainerId, final long processDefinitionId, final long rootProcessInstanceId,\n            final long parentProcessInstanceId) {\n        final SReceiveTaskInstance activityInstanceImpl = new SReceiveTaskInstance(name, flowNodeDefinitionId,\n                rootContainerId, parentContainerId,\n                processDefinitionId, rootProcessInstanceId);\n        activityInstanceImpl.setLogicalGroup(PARENT_PROCESS_INSTANCE_INDEX, parentProcessInstanceId);\n        return new SReceiveTaskInstanceBuilderImpl(activityInstanceImpl);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SReceiveTaskInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SReceiveTaskInstanceBuilder;\n\n/**\n * @author Julien Molinaro\n */\npublic class SReceiveTaskInstanceBuilderImpl extends SActivityInstanceBuilderImpl\n        implements SReceiveTaskInstanceBuilder {\n\n    public SReceiveTaskInstanceBuilderImpl(final SReceiveTaskInstance activityInstanceImpl) {\n        super(activityInstanceImpl);\n    }\n\n    @Override\n    public SReceiveTaskInstance done() {\n        return (SReceiveTaskInstance) this.entity;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SSendTaskInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SSendTaskInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SSendTaskInstanceBuilderFactory;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SSendTaskInstanceBuilderFactoryImpl extends SActivityInstanceBuilderFactoryImpl\n        implements SSendTaskInstanceBuilderFactory {\n\n    @Override\n    public SSendTaskInstanceBuilder createNewSendTaskInstance(final String name, final long flowNodeDefinitionId,\n            final long rootContainerId,\n            final long parentContainerId, final long processDefinitionId, final long rootProcessInstanceId,\n            final long parentProcessInstanceId) {\n        final SSendTaskInstance activityInstanceImpl = new SSendTaskInstance(name, flowNodeDefinitionId,\n                rootContainerId, parentContainerId, processDefinitionId,\n                rootProcessInstanceId);\n        activityInstanceImpl.setLogicalGroup(PARENT_PROCESS_INSTANCE_INDEX, parentProcessInstanceId);\n        return new SSendTaskInstanceBuilderImpl(activityInstanceImpl);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SSendTaskInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SSendTaskInstanceBuilder;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SSendTaskInstanceBuilderImpl extends SActivityInstanceBuilderImpl implements SSendTaskInstanceBuilder {\n\n    public SSendTaskInstanceBuilderImpl(final SSendTaskInstance activityInstanceImpl) {\n        super(activityInstanceImpl);\n    }\n\n    @Override\n    public SSendTaskInstance done() {\n        return (SSendTaskInstance) this.entity;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SSubProcessActivityInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SSubProcessActivityInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SSubProcessActivityInstanceBuilderFactory;\n\n/**\n * @author Celine Souchet\n * @author Elias Ricken de Medeiros\n */\npublic class SSubProcessActivityInstanceBuilderFactoryImpl extends SActivityInstanceBuilderFactoryImpl\n        implements SSubProcessActivityInstanceBuilderFactory {\n\n    @Override\n    public SSubProcessActivityInstanceBuilder createNewSubProcessActivityInstance(final String name,\n            final long flowNodeDefinitionId,\n            final long rootContainerId, final long parentContainerId, final long processDefinitionId,\n            final long rootProcessInstanceId,\n            final long parentProcessInstanceId, final boolean isTriggeredByEvent) {\n        final SSubProcessActivityInstance entity = new SSubProcessActivityInstance(name, flowNodeDefinitionId,\n                rootContainerId, parentContainerId, processDefinitionId,\n                rootProcessInstanceId, isTriggeredByEvent);\n        entity.setLogicalGroup(PARENT_PROCESS_INSTANCE_INDEX, parentProcessInstanceId);\n        entity.setTokenCount(1);\n        return new SSubProcessActivityInstanceBuilderImpl(entity);\n    }\n\n    @Override\n    public String getTriggeredByEventKey() {\n        return \"triggeredByEvent\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SSubProcessActivityInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SSubProcessActivityInstanceBuilder;\n\n/**\n * @author Celine Souchet\n * @author Elias Ricken de Medeiros\n */\npublic class SSubProcessActivityInstanceBuilderImpl extends SActivityInstanceBuilderImpl\n        implements SSubProcessActivityInstanceBuilder {\n\n    public SSubProcessActivityInstanceBuilderImpl(final SSubProcessActivityInstance entity) {\n        super(entity);\n    }\n\n    @Override\n    public SSubProcessActivityInstance done() {\n        return (SSubProcessActivityInstance) entity;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SUserTaskInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.STaskPriority;\nimport org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SUserTaskInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SUserTaskInstanceBuilderFactory;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class SUserTaskInstanceBuilderFactoryImpl extends SHumanTaskInstanceBuilderFactoryImpl\n        implements SUserTaskInstanceBuilderFactory {\n\n    @Override\n    public SUserTaskInstanceBuilder createNewUserTaskInstance(final String name, final long flowNodeDefinitionId,\n            final long rootContainerId,\n            final long parentContainerId, final long actorId, final long processDefinitionId,\n            final long rootProcessInstanceId,\n            final long parentProcessInstanceId) {\n        final SUserTaskInstance activityInst = new SUserTaskInstance(name, flowNodeDefinitionId, rootContainerId,\n                parentContainerId, actorId, STaskPriority.NORMAL,\n                processDefinitionId, rootProcessInstanceId);\n        activityInst.setLogicalGroup(PARENT_PROCESS_INSTANCE_INDEX, parentProcessInstanceId);\n        return new SUserTaskInstanceBuilderImpl(activityInst);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SUserTaskInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.impl;\n\nimport org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SUserTaskInstanceBuilder;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class SUserTaskInstanceBuilderImpl extends SHumanTaskInstanceBuilderImpl implements SUserTaskInstanceBuilder {\n\n    public SUserTaskInstanceBuilderImpl(final SUserTaskInstance activityInst) {\n        super(activityInst);\n    }\n\n    @Override\n    public SUserTaskInstance done() {\n        return (SUserTaskInstance) this.entity;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/business/data/SRefBusinessDataInstanceBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.impl.business.data;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.instance.model.builder.business.data.SRefBusinessDataInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.business.data.SRefBusinessDataInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SFlowNodeSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessSimpleRefBusinessDataInstance;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SRefBusinessDataInstanceBuilderFactoryImpl implements SRefBusinessDataInstanceBuilderFactory {\n\n    @Override\n    public SRefBusinessDataInstanceBuilder createNewInstance(final String name, final long processInstanceId,\n            final Long dataId, final String dataClassName) {\n        final SProcessSimpleRefBusinessDataInstance entity = new SProcessSimpleRefBusinessDataInstance();\n        entity.setProcessInstanceId(processInstanceId);\n        entity.setName(name);\n        entity.setDataId(dataId);\n        entity.setDataClassName(dataClassName);\n        return new SRefBusinessDataInstanceBuilderImpl(entity);\n    }\n\n    @Override\n    public SRefBusinessDataInstanceBuilder createNewInstanceForFlowNode(final String name,\n            final long flowNodeInstanceId, final Long dataId,\n            final String dataClassName) {\n        final SFlowNodeSimpleRefBusinessDataInstance entity = new SFlowNodeSimpleRefBusinessDataInstance();\n        entity.setFlowNodeInstanceId(flowNodeInstanceId);\n        entity.setName(name);\n        entity.setDataId(dataId);\n        entity.setDataClassName(dataClassName);\n        return new SRefBusinessDataInstanceBuilderImpl(entity);\n    }\n\n    @Override\n    public SRefBusinessDataInstanceBuilder createNewInstance(final String name, final long processInstanceId,\n            final List<Long> dataIds,\n            final String dataClassName) {\n        final SProcessMultiRefBusinessDataInstance entity = new SProcessMultiRefBusinessDataInstance();\n        entity.setProcessInstanceId(processInstanceId);\n        entity.setName(name);\n        entity.setDataIds(dataIds);\n        entity.setDataClassName(dataClassName);\n        return new SRefBusinessDataInstanceBuilderImpl(entity);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/business/data/SRefBusinessDataInstanceBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.impl.business.data;\n\nimport org.bonitasoft.engine.core.process.instance.model.builder.business.data.SRefBusinessDataInstanceBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SRefBusinessDataInstanceBuilderImpl implements SRefBusinessDataInstanceBuilder {\n\n    private final SRefBusinessDataInstance entity;\n\n    public SRefBusinessDataInstanceBuilderImpl(final SRefBusinessDataInstance entity) {\n        super();\n        this.entity = entity;\n    }\n\n    @Override\n    public SRefBusinessDataInstance done() {\n        return entity;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/business/data/SRefBusinessDataInstanceLogBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.impl.business.data;\n\nimport org.bonitasoft.engine.core.process.instance.model.builder.business.data.SRefBusinessDataInstanceLogBuilder;\nimport org.bonitasoft.engine.core.process.instance.model.builder.business.data.SRefBusinessDataInstanceLogBuilderFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SRefBusinessDataInstanceLogBuilderFactoryImpl extends CRUDELogBuilderFactory\n        implements SRefBusinessDataInstanceLogBuilderFactory {\n\n    @Override\n    public SRefBusinessDataInstanceLogBuilder createNewInstance() {\n        return new SRefBusinessDataInstanceLogBuilderImpl();\n    }\n\n    @Override\n    public String getObjectIdKey() {\n        return \"numericIndex2\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/business/data/SRefBusinessDataInstanceLogBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.impl.business.data;\n\nimport org.bonitasoft.engine.core.process.instance.model.builder.business.data.SRefBusinessDataInstanceLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SRefBusinessDataInstanceLogBuilderImpl extends CRUDELogBuilder\n        implements SRefBusinessDataInstanceLogBuilder {\n\n    @Override\n    public SPersistenceLogBuilder objectId(final long objectId) {\n        queriableLogBuilder.numericIndex(1, objectId);\n        return this;\n    }\n\n    @Override\n    protected String getActionTypePrefix() {\n        return \"REF_BUSINESS_DATA_INSTANCE\";\n    }\n\n    @Override\n    protected void checkExtraRules(final SQueriableLog log) {\n        if (log.getActionStatus() != SQueriableLog.STATUS_FAIL && log.getNumericIndex(0) == 0L) {\n            throw new MissingMandatoryFieldsException(\"Some mandatory fields are missing: \" + \"Flow Node Instance Id\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/business/data/SFlowNodeSimpleRefBusinessDataInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.business.data;\n\nimport javax.persistence.Cacheable;\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\n\n/**\n * @author Matthieu Chaffotte\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"fn_simple_ref\")\n@Cacheable(false)\npublic class SFlowNodeSimpleRefBusinessDataInstance extends SSimpleRefBusinessDataInstance {\n\n    @Column(name = \"fn_inst_id\")\n    private long flowNodeInstanceId;\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/business/data/SProcessMultiRefBusinessDataInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.business.data;\n\nimport java.util.List;\n\nimport javax.persistence.Cacheable;\nimport javax.persistence.CollectionTable;\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.ElementCollection;\nimport javax.persistence.Entity;\nimport javax.persistence.JoinColumn;\nimport javax.persistence.OrderColumn;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\n\n/**\n * @author Matthieu Chaffotte\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"proc_multi_ref\")\n@Cacheable(false)\npublic class SProcessMultiRefBusinessDataInstance extends SRefBusinessDataInstance {\n\n    @ElementCollection\n    @CollectionTable(name = \"multi_biz_data\", joinColumns = { @JoinColumn(name = \"id\", referencedColumnName = \"id\") })\n    @OrderColumn(name = \"idx\")\n    @Column(name = \"data_id\")\n    private List<Long> dataIds;\n\n    @Column(name = \"proc_inst_id\")\n    private long processInstanceId;\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/business/data/SProcessSimpleRefBusinessDataInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.business.data;\n\nimport javax.persistence.Cacheable;\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\n\n/**\n * @author Matthieu Chaffotte\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"proc_simple_ref\")\n@Cacheable(false)\npublic class SProcessSimpleRefBusinessDataInstance extends SSimpleRefBusinessDataInstance {\n\n    @Column(name = \"proc_inst_id\")\n    private long processInstanceId;\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/business/data/SRefBusinessDataInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.business.data;\n\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorColumn;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Matthieu Chaffotte\n */\n@Data\n@NoArgsConstructor\n@Entity\n@Table(name = \"ref_biz_data_inst\")\n@DiscriminatorColumn(name = \"kind\")\npublic abstract class SRefBusinessDataInstance implements PersistentObject {\n\n    @Id\n    private long id;\n    private String name;\n    @Column(name = \"data_classname\")\n    private String dataClassName;\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/business/data/SSimpleRefBusinessDataInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.business.data;\n\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\n\n/**\n * @author Matthieu Chaffotte\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\npublic abstract class SSimpleRefBusinessDataInstance extends SRefBusinessDataInstance {\n\n    @Column(name = \"data_id\")\n    private Long dataId;\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/event/SBoundaryEventInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.event;\n\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"boundaryEvent\")\npublic class SBoundaryEventInstance extends SCatchEventInstance {\n\n    private long activityInstanceId;\n\n    public SBoundaryEventInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId,\n            final long parentContainerId,\n            final long logicalGroup1, final long logicalGroup2) {\n        super(name, flowNodeDefinitionId, rootContainerId, parentContainerId, logicalGroup1, logicalGroup2);\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.BOUNDARY_EVENT;\n    }\n\n    @Override\n    public boolean mustExecuteOnAbortOrCancelProcess() {\n        //a boundary event never must be executed when the process instance is aborted because it will be aborted by the attached activity\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/event/SCatchEventInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.event;\n\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\npublic abstract class SCatchEventInstance extends SEventInstance {\n\n    private boolean interrupting = true;\n\n    public SCatchEventInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId,\n            final long parentContainerId,\n            final long logicalGroup1, final long logicalGroup2) {\n        super(name, flowNodeDefinitionId, rootContainerId, parentContainerId, logicalGroup1, logicalGroup2);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/event/SEndEventInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.event;\n\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"endEvent\")\npublic class SEndEventInstance extends SThrowEventInstance {\n\n    public SEndEventInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId,\n            final long parentContainerId,\n            final long logicalGroup1, final long logicalGroup2) {\n        super(name, flowNodeDefinitionId, rootContainerId, parentContainerId, logicalGroup1, logicalGroup2);\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.END_EVENT;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/event/SEventInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.event;\n\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\npublic abstract class SEventInstance extends SFlowNodeInstance {\n\n    public SEventInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId,\n            final long parentContainerId,\n            final long logicalGroup1, final long logicalGroup2) {\n        super(name, flowNodeDefinitionId, rootContainerId, parentContainerId, logicalGroup1, logicalGroup2);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/event/SIntermediateCatchEventInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.event;\n\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"intermediateCatchEvent\")\npublic class SIntermediateCatchEventInstance extends SCatchEventInstance {\n\n    public SIntermediateCatchEventInstance(final String name, final long flowNodeDefinitionId,\n            final long rootContainerId, final long parentContainerId,\n            final long logicalGroup1, final long logicalGroup2) {\n        super(name, flowNodeDefinitionId, rootContainerId, parentContainerId, logicalGroup1, logicalGroup2);\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.INTERMEDIATE_CATCH_EVENT;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/event/SIntermediateThrowEventInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.event;\n\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\n\n/**\n * @author Matthieu Chaffotte\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"intermediateThrowEvent\")\npublic class SIntermediateThrowEventInstance extends SThrowEventInstance {\n\n    public SIntermediateThrowEventInstance(final String name, final long flowNodeDefinitionId,\n            final long rootContainerId, final long parentContainerId,\n            final long logicalGroup1, final long logicalGroup2) {\n        super(name, flowNodeDefinitionId, rootContainerId, parentContainerId, logicalGroup1, logicalGroup2);\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.INTERMEDIATE_THROW_EVENT;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/event/SStartEventInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.event;\n\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"startEvent\")\npublic class SStartEventInstance extends SCatchEventInstance {\n\n    public SStartEventInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId,\n            final long parentContainerId,\n            final long logicalGroup1, final long logicalGroup2) {\n        super(name, flowNodeDefinitionId, rootContainerId, parentContainerId, logicalGroup1, logicalGroup2);\n    }\n\n    @Override\n    public SFlowNodeType getType() {\n        return SFlowNodeType.START_EVENT;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/event/SThrowEventInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.event;\n\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\npublic abstract class SThrowEventInstance extends SEventInstance {\n\n    public SThrowEventInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId,\n            final long parentContainerId,\n            final long logicalGroup1, final long logicalGroup2) {\n        super(name, flowNodeDefinitionId, rootContainerId, parentContainerId, logicalGroup1, logicalGroup2);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/event/handling/SBPMEventType.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.event.handling;\n\n/**\n * @author Zhao Na\n * @author Baptiste Mesta\n * @author Elias Ricken de Medeiros\n */\npublic enum SBPMEventType {\n\n    START_EVENT,\n\n    INTERMEDIATE_CATCH_EVENT,\n\n    INTERMEDIATE_THROW_EVENT,\n\n    END_EVENT,\n\n    BOUNDARY_EVENT,\n\n    EVENT_SUB_PROCESS\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/event/handling/SMessageEventCouple.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.event.handling;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Data\n@NoArgsConstructor\npublic class SMessageEventCouple implements PersistentObject {\n\n    private long waitingMessageId;\n    private SBPMEventType waitingMessageEventType;\n    private long messageInstanceId;\n\n    public SMessageEventCouple(final long waitingMessageId, final SBPMEventType waitingMessageEventType,\n            final long messageInstanceId) {\n        this.waitingMessageId = waitingMessageId;\n        this.waitingMessageEventType = waitingMessageEventType;\n        this.messageInstanceId = messageInstanceId;\n    }\n\n    @Override\n    public long getId() {\n        return -1;\n    }\n\n    @Override\n    public void setId(final long id) {\n        throw new IllegalArgumentException();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/event/handling/SMessageInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.event.handling;\n\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Data\n@NoArgsConstructor\n@Entity\n@Table(name = \"message_instance\")\npublic class SMessageInstance implements PersistentObject {\n\n    @Id\n    private long id;\n    private String messageName;\n    private String targetProcess;\n    private String targetFlowNode;\n    private long processDefinitionId;\n    private boolean locked = false;\n    private boolean handled = false;\n    private String flowNodeName;\n    private String correlation1;\n    private String correlation2;\n    private String correlation3;\n    private String correlation4;\n    private String correlation5;\n    private long creationDate;\n\n    public SMessageInstance(final String messageName, final String targetProcess, final String targetFlowNode,\n            final long processDefinitionId,\n            final String flowNodeName) {\n        this.messageName = messageName;\n        this.targetProcess = targetProcess;\n        this.targetFlowNode = targetFlowNode;\n        this.processDefinitionId = processDefinitionId;\n        this.flowNodeName = flowNodeName;\n        this.creationDate = System.currentTimeMillis();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/event/handling/SWaitingErrorEvent.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.event.handling;\n\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerType;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"error\")\npublic class SWaitingErrorEvent extends SWaitingEvent {\n\n    private String errorCode;\n    /**\n     * the id of activity where the boundary event is attached to.\n     */\n    private long relatedActivityInstanceId;\n\n    public SWaitingErrorEvent(final SBPMEventType eventType, final long processdefinitionId, final String processName,\n            final long flowNodeDefinitionId,\n            final String flowNodeName, final String errorCode) {\n        super(eventType, processdefinitionId, processName, flowNodeDefinitionId, flowNodeName);\n        this.errorCode = errorCode;\n    }\n\n    @Override\n    public SEventTriggerType getEventTriggerType() {\n        return SEventTriggerType.ERROR;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/event/handling/SWaitingEvent.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.event.handling;\n\nimport javax.persistence.DiscriminatorColumn;\nimport javax.persistence.Entity;\nimport javax.persistence.EnumType;\nimport javax.persistence.Enumerated;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerType;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Zhao Na\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@Entity\n@Table(name = \"waiting_event\")\n@DiscriminatorColumn(name = \"kind\")\npublic abstract class SWaitingEvent implements PersistentObject {\n\n    @Id\n    private long id;\n    @Enumerated(EnumType.STRING)\n    private SBPMEventType eventType;\n    private String processName;\n    private long processDefinitionId;\n    private long flowNodeDefinitionId;\n    private String flowNodeName;\n    private long subProcessId = -1;\n    private long parentProcessInstanceId = -1;\n    private long rootProcessInstanceId = -1;\n    private long flowNodeInstanceId = -1;\n    private boolean active = true;\n\n    public SWaitingEvent(final SBPMEventType eventType, final long processdefinitionId, final String processName,\n            final long flowNodeDefinitionId,\n            final String flowNodeName) {\n        this.eventType = eventType;\n        this.processName = processName;\n        this.flowNodeDefinitionId = flowNodeDefinitionId;\n        this.flowNodeName = flowNodeName;\n        this.processDefinitionId = processdefinitionId;\n    }\n\n    public abstract SEventTriggerType getEventTriggerType();\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/event/handling/SWaitingMessageEvent.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.event.handling;\n\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerType;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"message\")\npublic class SWaitingMessageEvent extends SWaitingEvent {\n\n    private String messageName;\n    private boolean locked = false;\n    private int progress = 0;\n    private String correlation1;\n    private String correlation2;\n    private String correlation3;\n    private String correlation4;\n    private String correlation5;\n\n    public SWaitingMessageEvent(final SBPMEventType eventType, final long processdefinitionId, final String processName,\n            final long flowNodeDefinitionId,\n            final String flowNodeName, final String messageName) {\n        super(eventType, processdefinitionId, processName, flowNodeDefinitionId, flowNodeName);\n        this.messageName = messageName;\n    }\n\n    @Override\n    public SEventTriggerType getEventTriggerType() {\n        return SEventTriggerType.MESSAGE;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/event/handling/SWaitingSignalEvent.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.event.handling;\n\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerType;\n\n/**\n * @author Matthieu Chaffotte\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@DiscriminatorValue(\"signal\")\npublic class SWaitingSignalEvent extends SWaitingEvent {\n\n    private String signalName;\n\n    public SWaitingSignalEvent(final SBPMEventType eventType, final long processdefinitionId, final String processName,\n            final long flowNodeDefinitionId,\n            final String flowNodeName, final String signalName) {\n        super(eventType, processdefinitionId, processName, flowNodeDefinitionId, flowNodeName);\n        this.signalName = signalName;\n    }\n\n    @Override\n    public SEventTriggerType getEventTriggerType() {\n        return SEventTriggerType.SIGNAL;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/event/trigger/STimerEventTriggerInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.event.trigger;\n\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Data\n@NoArgsConstructor\n@Entity\n@Table(name = \"event_trigger_instance\")\npublic class STimerEventTriggerInstance implements PersistentObject {\n\n    public static final String EXECUTION_DATE = \"executionDate\";\n\n    @Id\n    private long id;\n    private long eventInstanceId;\n\n    /**\n     * @return The date of the execution of the trigger\n     * @since 6.4.0\n     */\n    private long executionDate;\n\n    /**\n     * @return The name of the trigger of the job\n     * @since 6.4.0\n     */\n    private String jobTriggerName;\n\n    /**\n     * @return The name of the {@link org.bonitasoft.engine.core.process.instance.model.event.SEventInstance}\n     * @since 6.4.0\n     */\n    private String eventInstanceName;\n\n    public STimerEventTriggerInstance(final long eventInstanceId, final String eventInstanceName,\n            final long executionDate, final String jobTriggerName) {\n        this.eventInstanceId = eventInstanceId;\n        this.eventInstanceName = eventInstanceName;\n        this.executionDate = executionDate;\n        this.jobTriggerName = jobTriggerName;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/recorder/ProcessInstanceRecordType.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.recorder;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic enum ProcessInstanceRecordType {\n\n    createActivityInstance, deleteActivityInstance, createProcessInstance, deleteProcessInstance\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/recorder/SelectBusinessDataDescriptorBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.recorder;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.core.process.instance.model.archive.business.data.SARefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @author Hongwen Zang\n * @author Celine Souchet\n */\npublic class SelectBusinessDataDescriptorBuilder {\n\n    public static SelectOneDescriptor<SRefBusinessDataInstance> getSRefBusinessDataInstance(final String name,\n            final long processInstanceId) {\n        final Map<String, Object> parameters = new HashMap<>();\n        parameters.put(\"name\", name);\n        parameters.put(\"processInstanceId\", processInstanceId);\n        return new SelectOneDescriptor<>(\"getSRefBusinessDataInstance\", parameters, SRefBusinessDataInstance.class);\n    }\n\n    public static SelectOneDescriptor<SARefBusinessDataInstance> getSARefBusinessDataInstance(final String name,\n            final long processInstanceId) {\n        final Map<String, Object> parameters = new HashMap<>();\n        parameters.put(\"name\", name);\n        parameters.put(\"processInstanceId\", processInstanceId);\n        return new SelectOneDescriptor<>(\"getSARefBusinessDataInstance\", parameters, SARefBusinessDataInstance.class);\n    }\n\n    public static SelectListDescriptor<SRefBusinessDataInstance> getSRefBusinessDataInstances(\n            final long processInstanceId, final int startIndex,\n            final int maxResults) {\n        final Map<String, Object> parameters = new HashMap<>();\n        parameters.put(\"processInstanceId\", processInstanceId);\n        final QueryOptions options = new QueryOptions(startIndex, maxResults);\n        return new SelectListDescriptor<>(\"getSRefBusinessDataInstancesOfProcess\", parameters,\n                SRefBusinessDataInstance.class,\n                options);\n    }\n\n    public static SelectOneDescriptor<SRefBusinessDataInstance> getSFlowNodeRefBusinessDataInstance(final String name,\n            final long flowNodeInstanceId) {\n        final Map<String, Object> parameters = new HashMap<>();\n        parameters.put(\"name\", name);\n        parameters.put(\"flowNodeInstanceId\", flowNodeInstanceId);\n        return new SelectOneDescriptor<>(\"getSFlowNodeRefBusinessDataInstance\", parameters,\n                SRefBusinessDataInstance.class);\n    }\n\n    public static SelectOneDescriptor<SARefBusinessDataInstance> getSAFlowNodeRefBusinessDataInstance(final String name,\n            final long flowNodeInstanceId) {\n        final Map<String, Object> parameters = new HashMap<>();\n        parameters.put(\"name\", name);\n        parameters.put(\"flowNodeInstanceId\", flowNodeInstanceId);\n        return new SelectOneDescriptor<>(\"getSAFlowNodeRefBusinessDataInstance\", parameters,\n                SARefBusinessDataInstance.class);\n    }\n\n    public static SelectOneDescriptor<Integer> getNumberOfDataOfMultiRefBusinessData(final String name,\n            final long processInstanceId) {\n        final Map<String, Object> parameters = new HashMap<>();\n        parameters.put(\"name\", name);\n        parameters.put(\"processInstanceId\", processInstanceId);\n        return new SelectOneDescriptor<>(\"getNumberOfDataOfMultiRefBusinessData\", parameters,\n                SProcessMultiRefBusinessDataInstance.class);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/recorder/SelectDescriptorBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.recorder;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SGatewayInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageEventCouple;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingErrorEvent;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent;\nimport org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @author Hongwen Zang\n * @author Celine Souchet\n */\npublic class SelectDescriptorBuilder {\n\n    // FIXME put in a common model\n    public static <T extends PersistentObject> SelectByIdDescriptor<T> getElementById(final Class<T> clazz,\n            final String elementName, final long id) {\n        return new SelectByIdDescriptor<>(clazz, id);\n    }\n\n    public static SelectListDescriptor<SAFlowNodeInstance> getArchivedFlowNodesFromProcessInstance(\n            final long rootContainerId, final int fromIndex,\n            final int maxResults) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"rootContainerId\", rootContainerId);\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, maxResults);\n        return new SelectListDescriptor<>(\"getArchivedFlowNodesFromProcessInstance\", parameters,\n                SAFlowNodeInstance.class, queryOptions);\n    }\n\n    public static SelectListDescriptor<SAActivityInstance> getArchivedActivitiesFromProcessInstance(\n            final long rootContainerId,\n            final QueryOptions queryOptions) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"rootContainerId\", rootContainerId);\n        return new SelectListDescriptor<>(\"getAActivitiesFromProcessInstance\", parameters, SAActivityInstance.class,\n                queryOptions);\n    }\n\n    public static SelectListDescriptor<Long> getSourceProcesInstanceIdsOfArchProcessInstancesFromDefinition(\n            final long processDefinitionId,\n            final QueryOptions queryOptions) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"processDefinitionId\",\n                processDefinitionId);\n        return new SelectListDescriptor<>(\"getSourceProcessInstanceIdsByProcessDefinitionId\", parameters,\n                SAProcessInstance.class, queryOptions);\n    }\n\n    public static <T extends PersistentObject> SelectListDescriptor<T> getSpecificQueryWithParameters(\n            final Class<T> clazz, final String queryName,\n            final Map<String, Object> parameters, final QueryOptions queryOptions) {\n        return new SelectListDescriptor<>(queryName, parameters, clazz, queryOptions);\n    }\n\n    public static SelectListDescriptor<SHumanTaskInstance> getAssignedUserTasks(final long userId, final int fromIndex,\n            final int maxResults,\n            final String sortFieldName, final OrderByType order) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"assigneeId\", userId);\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, maxResults, SActivityInstance.class,\n                sortFieldName, order);\n        return new SelectListDescriptor<>(\"getAssignedUserTasks\", parameters, SHumanTaskInstance.class, queryOptions);\n    }\n\n    public static SelectListDescriptor<SHumanTaskInstance> getPendingUserTasks(final long userId,\n            final Set<Long> actorIds, final int fromIndex,\n            final int maxResults, final String sortFieldName, final OrderByType order) {\n        final Map<String, Object> parameters = new HashMap<>(3);\n        parameters.put(\"actorIds\", actorIds);\n        parameters.put(\"userId\", userId);\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, maxResults, SActivityInstance.class,\n                sortFieldName, order);\n        return new SelectListDescriptor<>(\"getPendingUserTasks\", parameters, SHumanTaskInstance.class, queryOptions);\n    }\n\n    public static SelectListDescriptor<SHumanTaskInstance> getPendingUserTasks(final long userId, final int fromIndex,\n            final int maxResults,\n            final String sortFieldName, final OrderByType order) {\n        final Map<String, Object> parameters = new HashMap<>(3);\n        parameters.put(\"userId\", userId);\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, maxResults, SActivityInstance.class,\n                sortFieldName, order);\n        return new SelectListDescriptor<>(\"getPendingUserTasksWithoutActorIds\", parameters, SHumanTaskInstance.class,\n                queryOptions);\n    }\n\n    public static SelectOneDescriptor<Long> getNumberOfProcessInstances() {\n        final Map<String, Object> emptyMap = Collections.emptyMap();\n        return new SelectOneDescriptor<>(\"getNumberOfProcessInstances\", emptyMap, SProcessInstance.class, Long.class);\n    }\n\n    public static SelectOneDescriptor<Long> getNumberOfArchivedProcessInstances() {\n        final Map<String, Object> emptyMap = Collections.emptyMap();\n        return new SelectOneDescriptor<>(\"getNumberOfArchivedProcessInstances\", emptyMap, SAProcessInstance.class,\n                Long.class);\n    }\n\n    public static SelectOneDescriptor<Long> getNumberOfOpenActivities(final long rootContainerId) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"rootContainerId\", rootContainerId);\n        return new SelectOneDescriptor<>(\"getNumberOfOpenActivities\", parameters, SActivityInstance.class, Long.class);\n    }\n\n    public static SelectOneDescriptor<Long> getNumberOfAssignedHumanTaskInstances(final long userId) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"assigneeId\", userId);\n        return new SelectOneDescriptor<>(\"getNumberOfAssignedUserTaskInstances\", parameters, SHumanTaskInstance.class,\n                Long.class);\n    }\n\n    public static SelectOneDescriptor<SGatewayInstance> getActiveGatewayInstanceOfProcess(\n            final long parentProcessInstanceId, final String name) {\n        final Map<String, Object> parameters = new HashMap<>(2);\n        parameters.put(\"parentProcessInstanceId\", parentProcessInstanceId);\n        parameters.put(\"name\", name);\n        return new SelectOneDescriptor<>(\"getActiveGatewayInstanceOfProcess\", parameters, SGatewayInstance.class);\n    }\n\n    public static SelectListDescriptor<SActivityInstance> getActivitiesFromProcessInstance(final long rootContainerId,\n            final int fromIndex,\n            final int maxResults) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"rootContainerId\", rootContainerId);\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, maxResults);\n        return new SelectListDescriptor<>(\"getActivitiesFromProcessInstance\", parameters, SActivityInstance.class,\n                queryOptions);\n    }\n\n    public static SelectOneDescriptor<Long> getNumberOfActivitiesFromProcessInstance(final long rootContainerId) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"rootContainerId\", rootContainerId);\n        return new SelectOneDescriptor<>(\"getNumberOfActivitiesFromProcessInstance\", parameters,\n                SFlowNodeInstance.class, Long.class);\n    }\n\n    public static SelectOneDescriptor<Long> getNumberOfFlowNode(final long parentProcessInstanceId) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"parentProcessInstanceId\",\n                parentProcessInstanceId);\n        return new SelectOneDescriptor<>(\"getNumberOfFlowNode\", parameters, SFlowNodeInstance.class, Long.class);\n    }\n\n    public static SelectListDescriptor<SEventInstance> getEventsFromRootContainer(final long rootContainerId,\n            final int fromIndex, final int maxResults,\n            final String field, final OrderByType orderByType) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"rootContainerId\", rootContainerId);\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, maxResults, SEventInstance.class, field,\n                orderByType);\n        return new SelectListDescriptor<>(\"getEventInstancesFromRootContainer\", parameters, SEventInstance.class,\n                queryOptions);\n    }\n\n    public static SelectListDescriptor<SBoundaryEventInstance> getActivityBoundaryEvents(final long activityInstanceId,\n            final int fromIndex,\n            final int maxResults) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"activityInstanceId\",\n                activityInstanceId);\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, maxResults);\n        return new SelectListDescriptor<>(\"getActivityBoundaryEventInstances\", parameters, SBoundaryEventInstance.class,\n                queryOptions);\n    }\n\n    public static SelectListDescriptor<STimerEventTriggerInstance> getEventTriggers(final long eventInstanceId,\n            final QueryOptions queryOptions) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"eventInstanceId\", eventInstanceId);\n        return new SelectListDescriptor<>(\"getEventTriggerInstances\", parameters, STimerEventTriggerInstance.class,\n                queryOptions);\n    }\n\n    public static SelectListDescriptor<Long> getChildInstanceIdsOfProcessInstance(final Class<SProcessInstance> class1,\n            final long processInstanceId,\n            final QueryOptions queryOptions) {\n        final Map<String, Object> map = Collections.singletonMap(\"processInstanceId\", processInstanceId);\n        return new SelectListDescriptor<>(\"getChildInstanceIdsOfProcessInstance\", map, class1, queryOptions);\n    }\n\n    public static SelectOneDescriptor<Long> getNumberOfChildInstancesOfProcessInstance(final long processInstanceId) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"processInstanceId\",\n                processInstanceId);\n        return new SelectOneDescriptor<>(\"getNumberOfChildInstancesOfProcessInstance\", parameters,\n                SProcessInstance.class, Long.class);\n    }\n\n    public static SelectListDescriptor<SWaitingErrorEvent> getCaughtError(final long relatedActivityInstanceId,\n            final QueryOptions queryOptions) {\n        final Map<String, Object> parameters = new HashMap<>(1);\n        parameters.put(\"relatedActivityInstanceId\", relatedActivityInstanceId);\n        return new SelectListDescriptor<>(\"getCaughtErrorByRelatedActivityAndAnyErrorCode\", parameters,\n                SWaitingErrorEvent.class,\n                queryOptions);\n    }\n\n    public static SelectListDescriptor<SWaitingErrorEvent> getCaughtError(final long relatedActivityInstanceId,\n            final String errorCode,\n            final QueryOptions queryOptions) {\n        final Map<String, Object> parameters = new HashMap<>(2);\n        parameters.put(\"relatedActivityInstanceId\", relatedActivityInstanceId);\n        parameters.put(\"errorCode\", errorCode);\n        return new SelectListDescriptor<>(\"getCaughtErrorByRelatedActivityAndErrorCode\", parameters,\n                SWaitingErrorEvent.class, queryOptions);\n    }\n\n    public static SelectListDescriptor<SWaitingSignalEvent> getListeningSignals(final String signalName,\n            final int fromIndex, final int maxResults) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"signalName\", signalName);\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, maxResults);\n        return new SelectListDescriptor<>(\"getListeningSignals\", parameters, SWaitingSignalEvent.class, queryOptions);\n    }\n\n    public static SelectListDescriptor<SMessageEventCouple> getMessageEventCouples(final int fromIndex,\n            final int maxResults) {\n        final Map<String, Object> parameters = Collections.emptyMap();\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, maxResults);\n        return new SelectListDescriptor<>(\"getMessageEventCouples\", parameters, SMessageEventCouple.class,\n                queryOptions);\n    }\n\n    public static SelectOneDescriptor<SAActivityInstance> getArchivedActivityInstanceWithActivityIdAndStateId(\n            final long activityInstanceId,\n            final int stateId) {\n        final Map<String, Object> parameters = new HashMap<>(2);\n        parameters.put(\"activityInstanceId\", activityInstanceId);\n        parameters.put(\"stateId\", stateId);\n        return new SelectOneDescriptor<>(\"getAActivityInstanceByActivityInstanceIdAndStateId\", parameters,\n                SAActivityInstance.class);\n    }\n\n    public static SelectOneDescriptor<SAActivityInstance> getMostRecentArchivedActivityInstance(\n            final long activityInstanceId) {\n        final Map<String, Object> parameters = new HashMap<>(1);\n        parameters.put(\"activityInstanceId\", activityInstanceId);\n        return new SelectOneDescriptor<>(\"getMostRecentArchivedActivityInstance\", parameters, SAActivityInstance.class);\n    }\n\n    public static SelectListDescriptor<SHumanTaskInstance> searchAssignedTasksSupervisedBy(final long supervisorId,\n            final int fromIndex, final int maxResults) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"supervisorId\", supervisorId);\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, maxResults);\n        return new SelectListDescriptor<>(\"searchAssignedTasksSupervisedBy\", parameters, SHumanTaskInstance.class,\n                queryOptions);\n    }\n\n    public static SelectListDescriptor<Map<String, Long>> getNumbersOfAssignedOpenTasks(final List<Long> userIds) {\n        final QueryOptions queryOptions = new QueryOptions(0, userIds.size());\n        final Map<String, Object> parameters = Collections.singletonMap(\"assigneeIds\", userIds);\n        return new SelectListDescriptor<>(\"getNumbersOfOpenTasksForUsers\", parameters, SHumanTaskInstance.class,\n                queryOptions);\n    }\n\n    public static SelectListDescriptor<Map<Long, Long>> getNumbersOfAssignedOverdueOpenTasks(final List<Long> userIds) {\n        final QueryOptions queryOptions = new QueryOptions(0, userIds.size());\n        final Map<String, Object> parameters = new HashMap<>(2);\n        parameters.put(\"assigneeIds\", userIds);\n        parameters.put(\"currentTime\", System.currentTimeMillis());\n        return new SelectListDescriptor<>(\"getNumbersOfAssignedOverdueTasksForUsers\", parameters,\n                SHumanTaskInstance.class, queryOptions);\n    }\n\n    public static SelectOneDescriptor<Long> getNumberOfPendingOverdueOpenTasksForUser(final Long userId) {\n        final Map<String, Object> parameters = new HashMap<>(2);\n        parameters.put(\"userId\", userId);\n        parameters.put(\"currentTime\", System.currentTimeMillis());\n        return new SelectOneDescriptor<>(\"getNumberOfPendingOverdueTasksForUser\", parameters, SHumanTaskInstance.class,\n                Long.class);\n    }\n\n    public static SelectListDescriptor<Long> deleteMessageInstanceByIds(List<Long> ids, final int fromIndex,\n            final int maxResults) {\n        final Map<String, Object> parameters = new HashMap<>();\n        parameters.put(\"ids\", ids);\n\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, maxResults);\n        return new SelectListDescriptor<>(\"deleteMessageInstanceByIds\", parameters,\n                SMessageEventCouple.class, queryOptions);\n    }\n\n    public static SelectListDescriptor<Long> getMessageInstanceIdOlderThanCreationDate(long creationDate,\n            QueryOptions queryOptions) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"creationDate\", creationDate);\n        return new SelectListDescriptor<>(\"getMessageInstanceIdOlderThanCreationDate\", parameters,\n                SMessageInstance.class, queryOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/resources/org/bonitasoft/engine/core/document/model/impl/hibernate/archive.document.queries.hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\"\n        \"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd\">\n<hibernate-mapping auto-import=\"false\">\n\n\n    <query name=\"getNumberOfSAMappedDocument\">\n        SELECT COUNT(mappeddoc.id)\n        FROM org.bonitasoft.engine.core.document.model.archive.SAMappedDocument AS mappeddoc\n    </query>\n\n    <query name=\"searchSAMappedDocument\">\n        SELECT mappeddoc\n        FROM org.bonitasoft.engine.core.document.model.archive.SAMappedDocument AS mappeddoc\n    </query>\n\n    <query name=\"getSAMappedDocumentOfProcessWithName\">\n        SELECT mappeddoc\n        FROM org.bonitasoft.engine.core.document.model.archive.SAMappedDocument AS mappeddoc\n        WHERE mappeddoc.processInstanceId = :processInstanceId\n        AND mappeddoc.name = :name\n        AND mappeddoc.archiveDate >= :time\n        ORDER BY mappeddoc.archiveDate ASC\n    </query>\n\n    <query name=\"getArchivedDocumentList\">\n        SELECT mappeddoc\n        FROM org.bonitasoft.engine.core.document.model.archive.SAMappedDocument AS mappeddoc\n        WHERE mappeddoc.processInstanceId = :processInstanceId\n        AND mappeddoc.name = :name\n        AND mappeddoc.archiveDate >= :time\n        ORDER BY mappeddoc.index ASC\n    </query>\n\n\n    <query name=\"getArchivedVersionOfDocument\">\n        SELECT mappeddoc\n        FROM org.bonitasoft.engine.core.document.model.archive.SAMappedDocument AS mappeddoc\n        WHERE mappeddoc.sourceObjectId = :sourceObjectId\n        ORDER BY mappeddoc.archiveDate DESC, mappeddoc.id DESC\n    </query>\n\n\n    <query name=\"getNumberOfSAMappedDocumentSupervisedBy\">\n        SELECT COUNT(mappeddoc.id)\n        FROM org.bonitasoft.engine.core.document.model.archive.SAMappedDocument AS mappeddoc,\n        org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS p\n        WHERE mappeddoc.processInstanceId = p.sourceObjectId\n        AND p.processDefinitionId IN (\n        SELECT supervisor.processDefId\n        FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor\n        WHERE (supervisor.userId = :userId)\n        OR (supervisor.id IN\n        (\n        SELECT supervisor.id\n        FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor,\n        org.bonitasoft.engine.identity.model.SUserMembership AS um\n        WHERE um.userId = :userId\n        AND (\n        (supervisor.groupId = um.groupId AND supervisor.roleId &lt;= 0)\n        OR (supervisor.roleId = um.roleId AND supervisor.groupId &lt;= 0)\n        OR (supervisor.roleId = um.roleId AND supervisor.groupId = um.groupId)\n        )\n        )\n        )\n        )\n    </query>\n\n    <query name=\"searchSAMappedDocumentSupervisedBy\">\n        SELECT mappeddoc\n        FROM org.bonitasoft.engine.core.document.model.archive.SAMappedDocument AS mappeddoc,\n        org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS p\n        WHERE mappeddoc.processInstanceId = p.sourceObjectId\n        AND p.processDefinitionId IN (\n        SELECT supervisor.processDefId\n        FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor\n        WHERE (supervisor.userId = :userId)\n        OR (supervisor.id IN\n        (\n        SELECT supervisor.id\n        FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor,\n        org.bonitasoft.engine.identity.model.SUserMembership AS um\n        WHERE um.userId = :userId\n        AND (\n        (supervisor.groupId = um.groupId AND supervisor.roleId &lt;= 0)\n        OR (supervisor.roleId = um.roleId AND supervisor.groupId &lt;= 0)\n        OR (supervisor.roleId = um.roleId AND supervisor.groupId = um.groupId)\n        )\n        )\n        )\n        )\n    </query>\n\n    <query name=\"getArchiveMappingsOfProcessInstances\">\n        SELECT mappeddoc\n        FROM org.bonitasoft.engine.core.document.model.archive.SAMappedDocument AS mappeddoc\n        WHERE mappeddoc.processInstanceId IN (:processInstanceIds)\n    </query>\n    <query name=\"deleteArchiveDocumentsByIds\">\n        DELETE\n        FROM org.bonitasoft.engine.core.document.model.SLightDocument AS doc\n        WHERE doc.id IN (:ids)\n    </query>\n    <query name=\"deleteArchiveMappingsByIds\">\n        DELETE\n        FROM org.bonitasoft.engine.core.document.model.archive.SAMappedDocument AS mappeddoc\n        WHERE mappeddoc.id IN (:ids)\n    </query>\n\n</hibernate-mapping>\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/resources/org/bonitasoft/engine/core/document/model/impl/hibernate/document.queries.hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\"\n        \"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd\">\n<hibernate-mapping auto-import=\"false\">\n\n\n    <query name=\"getSMappedDocumentOfProcessWithName\">\n        SELECT mappeddoc\n        FROM org.bonitasoft.engine.core.document.model.SMappedDocument AS mappeddoc\n        WHERE mappeddoc.processInstanceId = :processInstanceId\n        AND mappeddoc.name = :name\n        AND mappeddoc.index = -1\n    </query>\n\n\n    <query name=\"getDocumentCreatedBeforeList\">\n        SELECT mappeddoc\n        FROM org.bonitasoft.engine.core.document.model.SMappedDocument AS mappeddoc\n        WHERE mappeddoc.processInstanceId = :processInstanceId\n        AND mappeddoc.name = :name\n        AND mappeddoc.document.creationDate &lt;= :time\n        ORDER BY mappeddoc.index ASC\n    </query>\n\n\n    <query name=\"getSMappedDocumentOfProcess\">\n        SELECT mappeddoc\n        FROM org.bonitasoft.engine.core.document.model.SMappedDocument AS mappeddoc\n        WHERE mappeddoc.processInstanceId = :processInstanceId\n    </query>\n\n    <query name=\"getNumberOfSMappedDocumentOfProcess\">\n        SELECT COUNT(mappeddoc.id)\n        FROM org.bonitasoft.engine.core.document.model.SMappedDocument AS mappeddoc\n        WHERE mappeddoc.processInstanceId = :processInstanceId\n    </query>\n\n    <query name=\"getSMappedDocumentOfProcessOrderedById\">\n        SELECT mappeddoc\n        FROM org.bonitasoft.engine.core.document.model.SMappedDocument AS mappeddoc\n        WHERE mappeddoc.processInstanceId = :processInstanceId\n        ORDER BY mappeddoc.id\n    </query>\n\n    <query name=\"getNumberOfSMappedDocument\">\n        SELECT COUNT(mappeddoc.id)\n        FROM org.bonitasoft.engine.core.document.model.SMappedDocument AS mappeddoc\n    </query>\n\n    <query name=\"searchSMappedDocument\">\n        SELECT mappeddoc\n        FROM org.bonitasoft.engine.core.document.model.SMappedDocument AS mappeddoc\n    </query>\n\n\n    <query name=\"getNumberOfSMappedDocumentSupervisedBy\">\n        SELECT COUNT(mappeddoc.id)\n        FROM org.bonitasoft.engine.core.document.model.SMappedDocument AS mappeddoc,\n        org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p\n        WHERE mappeddoc.processInstanceId = p.id\n        AND p.processDefinitionId IN (\n        SELECT supervisor.processDefId\n        FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor\n        WHERE (supervisor.userId = :userId)\n        OR (supervisor.id IN (\n        SELECT supervisor.id\n        FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor,\n        org.bonitasoft.engine.identity.model.SUserMembership AS um\n        WHERE um.userId = :userId\n        AND (\n        (supervisor.groupId = um.groupId AND supervisor.roleId &lt;= 0)\n        OR (supervisor.roleId = um.roleId AND supervisor.groupId &lt;= 0)\n        OR (supervisor.roleId = um.roleId AND supervisor.groupId = um.groupId)\n        )\n        )\n        )\n        )\n    </query>\n\n    <query name=\"searchSMappedDocumentSupervisedBy\">\n        SELECT mappeddoc\n        FROM org.bonitasoft.engine.core.document.model.SMappedDocument AS mappeddoc,\n        org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p\n        WHERE mappeddoc.processInstanceId = p.id\n        AND p.processDefinitionId IN (\n        SELECT supervisor.processDefId\n        FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor\n        WHERE (supervisor.userId = :userId)\n        OR (supervisor.id IN (\n        SELECT supervisor.id\n        FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor,\n        org.bonitasoft.engine.identity.model.SUserMembership AS um\n        WHERE um.userId = :userId\n        AND (\n        (supervisor.groupId = um.groupId AND supervisor.roleId &lt;= 0)\n        OR (supervisor.roleId = um.roleId AND supervisor.groupId &lt;= 0)\n        OR (supervisor.roleId = um.roleId AND supervisor.groupId = um.groupId)\n        )\n        )\n        )\n        )\n    </query>\n\n    <query name=\"getDocumentList\">\n        SELECT mappeddoc\n        FROM org.bonitasoft.engine.core.document.model.SMappedDocument AS mappeddoc\n        WHERE mappeddoc.processInstanceId = :processInstanceId\n        AND mappeddoc.name = :name\n        AND mappeddoc.index > -1\n        ORDER BY mappeddoc.index ASC\n    </query>\n\n\n</hibernate-mapping>\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/resources/org/bonitasoft/engine/core/failure/model/impl/hibernate/failure.queries.hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\" \"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd\">\n<hibernate-mapping auto-import=\"false\">\n\n\t<query name=\"getFlowNodeFailures\">\n        SELECT f\n        FROM org.bonitasoft.engine.core.process.instance.model.SBPMFailure AS f\n        WHERE f.flowNodeInstanceId = :flowNodeInstanceId\n        ORDER BY f.failureDate DESC\n    </query>\n\n    <query name=\"deleteByIdsSBPMFailure\">\n        DELETE\n        FROM org.bonitasoft.engine.core.process.instance.model.SBPMFailure AS f\n        WHERE f.id IN (:ids)\n    </query>\n\n    <query name=\"deleteArchivedBPMFailuresByFlowNodeInstanceIds\">\n        DELETE\n        FROM org.bonitasoft.engine.core.process.instance.model.SABPMFailure AS f\n        WHERE f.flowNodeInstanceId IN (:flowNodeInstanceIds)\n    </query>\n\n    <query name=\"getArchivedFlowNodeFailures\">\n        SELECT f\n        FROM org.bonitasoft.engine.core.process.instance.model.SABPMFailure AS f\n        WHERE f.flowNodeInstanceId = :flowNodeInstanceId\n        ORDER BY f.failureDate DESC\n    </query>\n\n    <query name=\"getProcessInstanceFailures\">\n        SELECT f\n        FROM org.bonitasoft.engine.core.process.instance.model.SBPMFailure AS f\n        WHERE f.processInstanceId = :processInstanceId\n        ORDER BY f.failureDate DESC\n    </query>\n\n    <query name=\"deleteArchivedBPMFailuresByProcessInstanceIds\">\n        DELETE\n        FROM org.bonitasoft.engine.core.process.instance.model.SABPMFailure AS f\n        WHERE f.processInstanceId IN (:processInstanceIds)\n    </query>\n\n    <query name=\"getArchivedProcessInstanceFailures\">\n        SELECT f\n        FROM org.bonitasoft.engine.core.process.instance.model.SABPMFailure AS f\n        WHERE f.processInstanceId = :processInstanceId\n        ORDER BY f.failureDate DESC\n    </query>\n\n    <query name=\"getChildProcessInstancesFailures\">\n        SELECT f\n        FROM org.bonitasoft.engine.core.process.instance.model.SBPMFailure AS f\n        WHERE f.rootProcessInstanceId = :rootProcessInstanceId\n        AND f.processInstanceId != f.rootProcessInstanceId\n        ORDER BY f.failureDate DESC\n    </query>\n\n    <query name=\"getArchivedChildProcessInstancesFailures\">\n        SELECT f\n        FROM org.bonitasoft.engine.core.process.instance.model.SABPMFailure AS f\n        WHERE f.rootProcessInstanceId = :rootProcessInstanceId\n        AND f.processInstanceId != f.rootProcessInstanceId\n        ORDER BY f.failureDate DESC\n    </query>\n\n</hibernate-mapping>\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/resources/org/bonitasoft/engine/core/process/instance/model/impl/hibernate/archived.process.instance.queries.hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\"\n                                   \"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd\">\n<hibernate-mapping auto-import=\"false\">\n\n  <query name=\"getNumberOfSAFlowNodeInstance\">\n\tSELECT COUNT(aa.id)\n\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance AS aa\n  </query>\n\n  <query name=\"searchSAFlowNodeInstance\">\n\tSELECT aa\n\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance AS aa\n  </query>\n\n  <query name=\"getNumberOfSAFlowNodeInstanceSupervisedBy\">\n\tSELECT COUNT(DISTINCT aa.id)\n\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance AS aa,\n\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor\n\tWHERE terminal = TRUE\n\tAND aa.logicalGroup1 = supervisor.processDefId\n\tAND (supervisor.userId = :supervisorId\n\t\tOR (supervisor.id IN (\n\t\t\t\tSELECT supervisor.id\n\t\t\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor,\n\t\t\t\t\t org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\tWHERE um.userId = :supervisorId\n\t\t\t\tAND (\n\t\t\t\t\t(supervisor.groupId = um.groupId AND supervisor.roleId &lt;= 0)\n\t\t\t\t\tOR (supervisor.roleId = um.roleId AND supervisor.groupId &lt;= 0)\n\t\t\t\t\tOR (supervisor.groupId = um.groupId AND supervisor.roleId = um.roleId)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t)\n  </query>\n\n  <query name=\"searchSAFlowNodeInstanceSupervisedBy\">\n\tSELECT DISTINCT aa\n\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance AS aa,\n\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor\n\tWHERE terminal = TRUE\n\tAND aa.logicalGroup1 = supervisor.processDefId\n\tAND (supervisor.userId = :supervisorId\n\t\tOR (supervisor.id IN (\n\t\t\t\tSELECT supervisor.id\n\t\t\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\tWHERE um.userId = :supervisorId\n\t\t\t\tAND (\n\t\t\t\t\t(supervisor.groupId = um.groupId AND supervisor.roleId &lt;= 0)\n\t\t\t\t\tOR (supervisor.roleId = um.roleId AND supervisor.groupId &lt;= 0)\n\t\t\t\t\tOR (supervisor.groupId = um.groupId AND supervisor.roleId = um.roleId)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t)\n  </query>\n\n\t<query name=\"searchSAActivityInstanceSupervisedBy\">\n\t\tSELECT DISTINCT aa\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance AS aa,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor\n\t\tWHERE terminal = TRUE\n\t\tAND aa.logicalGroup1 = supervisor.processDefId\n\t\tAND (supervisor.userId = :supervisorId\n\t\t\tOR (supervisor.id IN (\n\t\t\t\t\tSELECT supervisor.id\n\t\t\t\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor,\n\t\t\t\t\t \t org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\t\tWHERE um.userId = :supervisorId\n\t\t\t\t\tAND (\n\t\t\t\t\t\t(supervisor.groupId = um.groupId AND supervisor.roleId &lt;= 0)\n\t\t\t\t\t\tOR (supervisor.roleId = um.roleId AND supervisor.groupId &lt;= 0)\n\t\t\t\t\t\tOR (supervisor.groupId = um.groupId AND supervisor.roleId = um.roleId)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n  \t</query>\n\n\t<query name=\"getNumberOfSAActivityInstanceSupervisedBy\">\n  \t\tSELECT COUNT(DISTINCT aa.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance AS aa,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor\n\t\tWHERE terminal = TRUE\n\n\t\tAND aa.logicalGroup1 = supervisor.processDefId\n\t\tAND (supervisor.userId = :supervisorId\n\t\t\tOR (supervisor.id IN (\n\t\t\t\t\tSELECT supervisor.id\n\t\t\t\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor,\n\t\t\t\t\t \t org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\t\tWHERE um.userId = :supervisorId\n\t\t\t\t\tAND (\n\t\t\t\t\t\t(supervisor.groupId = um.groupId AND supervisor.roleId &lt;= 0)\n\t\t\t\t\t\tOR (supervisor.roleId = um.roleId AND supervisor.groupId &lt;= 0)\n\t\t\t\t\t\tOR (supervisor.groupId = um.groupId AND supervisor.roleId = um.roleId)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t</query>\n\n\t<query name=\"getArchivedFlowNodeInstanceById\">\n\t\tSELECT aa\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance AS aa\n\t\tWHERE aa.id = :id\n\t</query>\n\n\t<query name=\"getMostRecentArchivedActivityInstance\">\n\t\tSELECT aa\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance AS aa\n\t\tWHERE aa.sourceObjectId = :activityInstanceId\n\t\tORDER BY aa.archiveDate DESC, aa.id DESC\n\t</query>\n\n  <query name=\"getAActivityInstanceByActivityInstanceIdAndStateId\">\n    SELECT aa\n    FROM org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance AS aa\n    WHERE aa.sourceObjectId = :activityInstanceId\n    \tAND aa.stateId = :stateId\n  </query>\n\n  <query name=\"getAActivitiesFromProcessInstance\">\n    SELECT aa\n    FROM org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance AS aa\n    WHERE aa.rootContainerId = :rootContainerId\n  </query>\n\n  <query name=\"getAActivitiesWithStates\">\n    SELECT aa\n    FROM org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance AS aa\n    WHERE aa.rootContainerId = :rootContainerId\n    \tAND aa.stateId IN (:stateIds)\n  </query>\n\n  <!-- External Service Queries -->\n\t<query name=\"getNumberOfSAHumanTaskInstanceManagedBy\">\n\t\tSELECT COUNT(aa.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance AS aa\n\t\tWHERE terminal = TRUE\n\t\tAND assigneeId IN ( SELECT u.id\n\t\t\t\t\t\t\tFROM org.bonitasoft.engine.identity.model.SUser u\n\t\t\t\t\t\t\tWHERE u.managerUserId = :managerUserId )\n\t</query>\n\n\t<query name=\"searchSAHumanTaskInstanceManagedBy\">\n\t\tSELECT aa\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance AS aa\n\t\tWHERE terminal = TRUE\n\t\tAND assigneeId IN ( SELECT u.id\n\t\t\t\t\t\t\tFROM org.bonitasoft.engine.identity.model.SUser u\n\t\t\t\t\t\t\tWHERE u.managerUserId = :managerUserId )\n\t</query>\n\n\t<query name=\"getNumberOfSAHumanTaskInstanceSupervisedBy\">\n\t\tSELECT COUNT(DISTINCT aa.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance AS aa,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor\n\t\tWHERE terminal = TRUE\n\t\tAND aa.logicalGroup1 = supervisor.processDefId\n\t\tAND(supervisor.userId = :supervisorId\n\t\t\tOR (supervisor.id IN (\n\t\t\t\t\tSELECT supervisor.id\n\t\t\t\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\t\tWHERE um.userId = :supervisorId\n\t\t\t\t\tAND (\n\t\t\t\t\t\t(supervisor.groupId = um.groupId AND supervisor.roleId &lt;= 0)\n\t\t\t\t\t\tOR (supervisor.roleId = um.roleId AND supervisor.groupId &lt;= 0)\n\t\t\t\t\t\tOR (supervisor.groupId = um.groupId AND supervisor.roleId = um.roleId)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n  \t</query>\n\n\t<query name=\"searchSAHumanTaskInstanceSupervisedBy\">\n\t\tSELECT DISTINCT aa\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance AS aa,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor\n\t\tWHERE terminal = TRUE\n\t\tAND aa.logicalGroup1 = supervisor.processDefId\n\t\tAND(supervisor.userId = :supervisorId\n\t\t\tOR (supervisor.id IN (\n\t\t\t\t\tSELECT supervisor.id\n\t\t\t\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\t\tWHERE um.userId = :supervisorId\n\t\t\t\t\tAND (\n\t\t\t\t\t\t(supervisor.groupId = um.groupId AND supervisor.roleId &lt;= 0)\n\t\t\t\t\t\tOR (supervisor.roleId = um.roleId AND supervisor.groupId &lt;= 0)\n\t\t\t\t\t\tOR (supervisor.groupId = um.groupId AND supervisor.roleId = um.roleId)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t</query>\n\n  \t<query name=\"searchSAHumanTaskInstance\">\n\t\tSELECT aa\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance AS aa\n\t\tWHERE terminal = TRUE\n  \t</query>\n\n\t<query name=\"getNumberOfSAHumanTaskInstance\">\n  \t\tSELECT COUNT(aa.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance AS aa\n\t\tWHERE terminal = TRUE\n\t</query>\n\n\t<query name=\"getNumberOfSAProcessInstancewithSUserTaskInstance\">\n\t\tselect COUNT(DISTINCT ap.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap,\n\t\t     org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a\n\t\tWHERE ap.archiveDate = (\n\t\t\tSELECT MAX(aa2.archiveDate) FROM org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance AS aa2\n\t\t\tWHERE aa2.sourceObjectId = ap.sourceObjectId\n\t\t)\n\t\tAND ap.stateId != 8\n\t\tAND (ap.startedBy = :userId\n\t\t\tOR (a.rootContainerId = ap.sourceObjectId\n\t\t\t\tAND a.stable = TRUE\n\t\t\t\tAND a.assigneeId = :userId)\n\t\t)\n  \t</query>\n\n  \t<query name=\"searchSAProcessInstancewithSUserTaskInstance\">\n\t\tselect DISTINCT ap\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap,\n\t\t     org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a\n\t\tWHERE ap.archiveDate = (\n\t\t\tSELECT MAX(aa2.archiveDate) FROM org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance AS aa2\n\t\t\tWHERE aa2.sourceObjectId = ap.sourceObjectId\n\t\t)\n\t\tAND ap.stateId != 8\n\t\tAND (ap.startedBy = :userId\n\t\t\tOR (a.rootContainerId = ap.sourceObjectId\n\t\t\t\tAND a.stable = TRUE\n\t\t\t\tAND a.assigneeId = :userId)\n\t\t)\n\t</query>\n\n\t<query name=\"searchSAHumanTaskInstancewithSAActivityInstance\">\n\t\tSELECT aa\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance AS aa\n\t\tWHERE terminal = TRUE\n  \t</query>\n\n\t<query name=\"getNumberOfSAHumanTaskInstancewithSAActivityInstance\">\n  \t\tSELECT COUNT(aa.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance AS aa\n\t\tWHERE terminal = TRUE\n\t</query>\n\n\t<query name=\"searchSAManualTaskInstance\">\n\t\tSELECT aa\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAManualTaskInstance AS aa\n\t\tWHERE terminal = TRUE\n  \t</query>\n\n\t<query name=\"getNumberOfSAManualTaskInstance\">\n  \t\tSELECT COUNT(aa.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAManualTaskInstance AS aa\n\t\tWHERE terminal = TRUE\n\t</query>\n\n\t<query name=\"searchSAManualTaskInstancewithSAActivityInstance\">\n\t\tSELECT aa\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAManualTaskInstance AS aa\n\t\tWHERE terminal = TRUE\n  \t</query>\n\n\t<query name=\"getNumberOfSAManualTaskInstancewithSAActivityInstance\">\n  \t\tSELECT COUNT(aa.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAManualTaskInstance AS aa\n\t\tWHERE terminal = TRUE\n\t</query>\n\n\t<query name=\"searchSAUserTaskInstance\">\n\t\tSELECT aa\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance AS aa\n\t\tWHERE terminal = TRUE\n  \t</query>\n\n\t<query name=\"getNumberOfSAUserTaskInstance\">\n  \t\tSELECT COUNT(aa.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance AS aa\n\t\tWHERE terminal = TRUE\n\t</query>\n\n\t<query name=\"searchSAUserTaskInstancewithSAActivityInstance\">\n\t\tSELECT aa\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance AS aa\n\t\tWHERE terminal = TRUE\n  \t</query>\n\n\t<query name=\"getNumberOfSAUserTaskInstancewithSAActivityInstance\">\n  \t\tSELECT COUNT(aa.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance AS aa\n\t\tWHERE terminal = TRUE\n\t</query>\n\n\t<query name=\"searchSAAutomaticTaskInstance\">\n\t\tSELECT aa\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAAutomaticTaskInstance AS aa\n\t\tWHERE terminal = TRUE\n  \t</query>\n\n\t<query name=\"getNumberOfSAAutomaticTaskInstance\">\n  \t\tSELECT COUNT(aa.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAAutomaticTaskInstance AS aa\n\t\tWHERE terminal = TRUE\n\t</query>\n\n\t<query name=\"searchSAReceiveTaskInstance\">\n\t\tSELECT aa\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAReceiveTaskInstance AS aa\n\t\tWHERE terminal = TRUE\n  \t</query>\n\n\t<query name=\"getNumberOfSAReceiveTaskInstance\">\n  \t\tSELECT COUNT(aa.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAReceiveTaskInstance AS aa\n\t\tWHERE terminal = TRUE\n\t</query>\n\n\t<query name=\"searchSAAutomaticTaskInstancewithSAActivityInstance\">\n\t\tSELECT aa\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAAutomaticTaskInstance AS aa\n\t\tWHERE terminal = TRUE\n  \t</query>\n\n\t<query name=\"getNumberOfSAAutomaticTaskInstancewithSAActivityInstance\">\n  \t\tSELECT COUNT(aa.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAAutomaticTaskInstance AS aa\n\t\tWHERE terminal = TRUE\n\t</query>\n\n\t<query name=\"searchSAReceiveTaskInstancewithSAActivityInstance\">\n\t\tSELECT aa\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAReceiveTaskInstance AS aa\n\t\tWHERE terminal = TRUE\n  \t</query>\n\n\t<query name=\"getNumberOfSAReceiveTaskInstancewithSAActivityInstance\">\n  \t\tSELECT COUNT(aa.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAReceiveTaskInstance AS aa\n\t\tWHERE terminal = TRUE\n\t</query>\n\n\t<query name=\"searchSAActivityInstance\">\n\t\tSELECT aa\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance AS aa\n\t\tWHERE terminal = TRUE\n  \t</query>\n\n\t<query name=\"getNumberOfSAActivityInstance\">\n  \t\tSELECT COUNT(aa.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance AS aa\n\t\tWHERE terminal = TRUE\n\t</query>\n\n\t<query name=\"getArchivedFlowNodesFromProcessInstance\">\n\t\tSELECT aa\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance AS aa\n\t\tWHERE aa.rootContainerId = :rootContainerId\n        ORDER BY aa.id\n\t</query>\n\n\t<query name=\"getNumberOfArchivedFlowNodesInAllStates\">\n\t\tSELECT new org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstanceStateCounter(name AS name, stateName AS statename, count(aa) AS numberof)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance AS aa\n\t\tWHERE logicalGroup4 = :parentProcessInstanceId\n\t\tAND terminal = TRUE\n\t\tGROUP BY name, stateName\n\t\tORDER BY name ASC, stateName ASC\n\t</query>\n\n\t<query name=\"searchSAConnectorInstance\">\n\t\tSELECT ac\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAConnectorInstance AS ac\n\t</query>\n\t<query name=\"deleteArchivedConnectorInstances\">\n\t\tDELETE\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAConnectorInstance AS ac\n\t\tWHERE ac.containerType = :containerType AND ac.containerId IN (:containerIds)\n\t</query>\n\n\t<query name=\"getNumberOfSAConnectorInstance\">\n\t\tSELECT COUNT(ac.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAConnectorInstance AS ac\n\t</query>\n\n\t<!-- Process Instance  -->\n\t<query name=\"getSourceProcessInstanceIdsByProcessDefinitionId\">\n\t\tSELECT DISTINCT ap.sourceObjectId\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap\n\t\tWHERE ap.processDefinitionId = :processDefinitionId\n\t</query>\n\n\t<query name=\"searchSAProcessInstance\">\n\t\tSELECT ap\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap\n\t</query>\n\n\t<query name=\"getNumberOfSAProcessInstance\">\n\t\tSELECT COUNT(ap.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap\n\t</query>\n\n\t<query name=\"getArchivedProcessInstancesInAllStates\">\n\t\tSELECT ap\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap\n\t\tWHERE ap.sourceObjectId IN (:sourceObjectIds)\n\t</query>\n\n\t<query name=\"getLastArchivedProcessInstanceStartDates\">\n\t\tSELECT DISTINCT ap.startDate\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap\n\t\tWHERE ap.stateId = 0\n\t\tAND ap.startDate &gt;= :sinceDateInMillis\n\t</query>\n\n\t<query name=\"getArchivedProcessInstance\">\n\t\tSELECT ap\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap\n\t\tWHERE ap.id = :id\n\t</query>\n\n  \t<query name=\"getNumberOfSAProcessInstanceWithoutSubProcess\">\n\t\tSELECT COUNT(ap.sourceObjectId)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap\n\t\tWHERE ap.callerId = -1\n\t\tAND ap.stateId IN (6,7,3,4)\n\t</query>\n\n  \t<query name=\"searchSAProcessInstanceWithoutSubProcess\">\n\t\tSELECT ap\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap\n\t\tWHERE ap.callerId = -1\n\t\tAND ap.stateId IN (6,7,3,4)\n  \t</query>\n\n\t<query name=\"getNumberOfSAProcessInstancewithSUserTaskInstanceWithoutSubProcess\">\n\t\tSELECT COUNT(ap.sourceObjectId)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap,\n\t\t     org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a\n\t\tWHERE ap.callerId = -1\n\t\tAND ap.stateId IN (6,7,3,4)\n\t\tAND a.rootContainerId = ap.sourceObjectId\n\t</query>\n\n\t<query name=\"searchSAProcessInstancewithSUserTaskInstanceWithoutSubProcess\">\n\t\tSELECT ap\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap,\n\t\t     org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a\n\t\tWHERE ap.callerId = -1\n\t\tAND ap.stateId IN (6,7,3,4)\n\t\tAND a.rootContainerId = ap.sourceObjectId\n\t</query>\n\n\n\t<query name=\"getNumberOfSAProcessInstancewithSProcessSupervisorWithoutSubProcess\">\n\t\tSELECT COUNT(ap.sourceObjectId)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap,\n\t\t     org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE ap.callerId = -1\n\t\tAND ap.stateId IN (6,7,3,4)\n\t\tAND ap.processDefinitionId = processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"searchSAProcessInstancewithSProcessSupervisorWithoutSubProcess\">\n\t\tSELECT DISTINCT ap\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap,\n\t\t     org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE ap.callerId = -1\n\t\tAND ap.stateId IN (6,7,3,4)\n\t\tAND ap.processDefinitionId = processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"getNumberOfSAProcessInstancewithSProcessSupervisorSUserTaskInstanceWithoutSubProcess\">\n\t\tSELECT COUNT(ap.sourceObjectId)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap,\n\t\t     org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a,\n\t\t     org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE ap.callerId = -1\n\t\tAND ap.stateId IN (6,7,3,4)\n\t\tAND a.rootContainerId = ap.sourceObjectId\n\t\tAND ap.processDefinitionId = processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"searchSAProcessInstancewithSProcessSupervisorSUserTaskInstanceWithoutSubProcess\">\n\t\tSELECT DISTINCT ap\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap,\n\t\t     org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a,\n\t\t     org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE ap.callerId = -1\n\t\tAND ap.stateId IN (6,7,3,4)\n\t\tAND a.rootContainerId = ap.sourceObjectId\n\t\tAND ap.processDefinitionId = processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"getChildrenSourceProcessInstanceIdsFromRootProcessInstance\">\n\t\tSELECT DISTINCT ap.sourceObjectId\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap\n\t\tWHERE ap.rootProcessInstanceId = :rootProcessInstanceId\n\t\tAND ap.callerId != -1\n  \t</query>\n\t<query name=\"getArchivedChildrenProcessInstanceIds\">\n\t\tSELECT ap.sourceObjectId\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap\n\t\tWHERE ap.rootProcessInstanceId IN (:sourceProcessInstanceIds)\n\t</query>\n\t<query name=\"deleteByIdsSAProcessInstance\">\n\t\tDELETE\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap\n\t\tWHERE ap.id IN (:ids)\n\t</query>\n\n  \t<sql-query name=\"getNumberOfSAProcessInstanceInvolvingUser\">\n\t\t<return-scalar column=\"count\" type=\"long\"/>\n\t\tSELECT count(id) as count from (\n\t\t\tSELECT ap.*\n\t\t\t\tFROM arch_process_instance ap\n\t\t\t\tJOIN (\n\t\t\t\t\tselect afi.logicalGroup2 from arch_flownode_instance afi\n\t\t\t\t\t\tWHERE afi.kind in ('user', 'manual')\n\t\t\t\t\t\tAND ( afi.executedBy = :userId OR afi.executedBySubstitute = :userId )\n\t\t\t\t\t\tAND afi.stateId = 2\n\t\t\t\t) humanTask ON (humanTask.logicalGroup2 = ap.sourceObjectId)\n\t\t\tWHERE (ap.stateId in (6 , 7 , 3 , 4))\n\t\t\tUNION\n\t\t\tSELECT ap.*\n\t\t\t\tFROM arch_process_instance ap\n\t\t\t\tWHERE (ap.stateId in (6 , 7 , 3 , 4))\n\t\t\t\t\tAND ap.startedBy = :userId\n\t\t) ap\n\t</sql-query>\n\n\t<sql-query name=\"searchSAProcessInstanceInvolvingUser\">\n\t\t<return alias=\"ap\" class=\"org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance\" />\n\t\tSELECT * from (\n\t\t\tSELECT ap.*\n\t\t\t\tFROM arch_process_instance ap\n\t\t\t\tJOIN (\n\t\t\t\t\tselect afi.logicalGroup2 from arch_flownode_instance afi\n\t\t\t\t\t\tWHERE afi.kind in ('user', 'manual')\n\t\t\t\t\t\tAND ( afi.executedBy = :userId OR afi.executedBySubstitute = :userId )\n\t\t\t\t\t\tAND afi.stateId = 2\n\t\t\t\t) humanTask ON (humanTask.logicalGroup2 = ap.sourceObjectId)\n\t\t\t\tWHERE (ap.stateId in (6 , 7 , 3 , 4))\n\t\t\tUNION\n\t\t\tSELECT ap.*\n\t\t\t\tFROM arch_process_instance ap\n\t\t\t\t\tWHERE (ap.stateId in (6 , 7 , 3 , 4))\n\t\t\t\t\tAND ap.startedBy = :userId\n\t\t) ap\n\t</sql-query>\n\n  \t<query name=\"getNumberOfSAProcessInstanceSupervisedBy\">\n\t\tSELECT COUNT(DISTINCT ap.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor\n\t\tWHERE ap.stateId IN (6,7,3,4)\n\t\tAND ap.processDefinitionId = supervisor.processDefId\n\t\tAND (supervisor.userId = :userId\n\t\t\tOR (supervisor.id IN (\n\t\t\t\tSELECT supervisor.id\n\t\t\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor,\n\t\t\t\t   \t org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\tWHERE um.userId = :userId\n\t\t\t\tAND (\n\t\t\t\t\t(supervisor.groupId = um.groupId AND supervisor.roleId &lt;= 0)\n\t\t\t\t\tOR (supervisor.roleId = um.roleId AND supervisor.groupId &lt;= 0)\n\t\t\t\t\tOR (supervisor.groupId = um.groupId AND supervisor.roleId = um.roleId)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n  \t</query>\n\n  \t<query name=\"searchSAProcessInstanceSupervisedBy\">\n\t\tSELECT DISTINCT ap\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor\n\t\tWHERE ap.stateId IN (6,7,3,4)\n\t\tAND ap.processDefinitionId = supervisor.processDefId\n\t\tAND (supervisor.userId = :userId\n\t\t\tOR (supervisor.id IN (\n\t\t\t\tSELECT supervisor.id\n\t\t\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor,\n\t\t\t\t   \t org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\tWHERE um.userId = :userId\n\t\t\t\tAND (\n\t\t\t\t\t(supervisor.groupId = um.groupId AND supervisor.roleId &lt;= 0)\n\t\t\t\t\tOR (supervisor.roleId = um.roleId AND supervisor.groupId &lt;= 0)\n\t\t\t\t\tOR (supervisor.groupId = um.groupId AND supervisor.roleId = um.roleId)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n    </query>\n\n  \t<query name=\"getNumberOfSAProcessInstancewithSUserTaskInstanceSupervisedBy\">\n\t\tSELECT COUNT(DISTINCT ap.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor,\n\t\t     org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a\n\t\tWHERE ap.stateId IN (6,7,3,4)\n\t\tAND a.rootContainerId = ap.sourceObjectId\n\t\tAND ap.processDefinitionId = supervisor.processDefId\n\t\tAND (supervisor.userId = :userId\n\t\t\tOR (supervisor.id IN (\n\t\t\t\tSELECT supervisor.id\n\t\t\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor,\n\t\t\t\t   \t org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\tWHERE um.userId = :userId\n\t\t\t\tAND (\n\t\t\t\t\t(supervisor.groupId = um.groupId AND supervisor.roleId &lt;= 0)\n\t\t\t\t\tOR (supervisor.roleId = um.roleId AND supervisor.groupId &lt;= 0)\n\t\t\t\t\tOR (supervisor.groupId = um.groupId AND supervisor.roleId = um.roleId)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n  \t</query>\n\n  \t<query name=\"searchSAProcessInstancewithSUserTaskInstanceSupervisedBy\">\n\t\tSELECT DISTINCT ap\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor,\n             org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a\n\t\tWHERE ap.stateId IN (6,7,3,4)\n\t\tAND a.rootContainerId = ap.sourceObjectId\n\t\tAND ap.processDefinitionId = supervisor.processDefId\n\t\tAND (supervisor.userId = :userId\n\t\t\tOR (supervisor.id IN (\n\t\t\t\tSELECT supervisor.id\n\t\t\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor,\n\t\t\t\t   \t org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\tWHERE um.userId = :userId\n\t\t\t\tAND (\n\t\t\t\t\t(supervisor.groupId = um.groupId AND supervisor.roleId &lt;= 0)\n\t\t\t\t\tOR (supervisor.roleId = um.roleId AND supervisor.groupId &lt;= 0)\n\t\t\t\t\tOR (supervisor.groupId = um.groupId AND supervisor.roleId = um.roleId)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n    </query>\n\n    <query name=\"getSARefBusinessDataInstance\">\n        SELECT ref\n        FROM org.bonitasoft.engine.core.process.instance.model.archive.business.data.SARefBusinessDataInstance AS ref\n        WHERE ref.name = :name\n        AND ref.processInstanceId = :processInstanceId\n    </query>\n\n    <query name=\"getSAFlowNodeRefBusinessDataInstance\">\n        SELECT ref\n        FROM org.bonitasoft.engine.core.process.instance.model.archive.business.data.SARefBusinessDataInstance AS ref\n        WHERE ref.name = :name\n        AND ref.flowNodeInstanceId = :flowNodeInstanceId\n    </query>\n\n\t<query name=\"deleteArchivedRefBizDataForProcessInstance\">\n\t\tDELETE FROM org.bonitasoft.engine.core.process.instance.model.archive.business.data.SARefBusinessDataInstance\n\t\tWHERE processInstanceId = :processInstanceId\n\t</query>\n\t<query name=\"deleteArchivedRefBizDataForProcessInstances\">\n\t\tDELETE FROM org.bonitasoft.engine.core.process.instance.model.archive.business.data.SARefBusinessDataInstance\n\t\tWHERE processInstanceId IN (:processInstanceIds)\n\t</query>\n\n\t<!-- Avoid deleting children without its parent using the rootProcessInstanceId in (:sourceProcessInstanceIds) -->\n\t<query name=\"deleteArchiveProcessInstanceBySourceObjectId\">\n\t\tDELETE FROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance\n\t\tWHERE sourceObjectId IN (:sourceProcessInstanceIds)\n\t\tAND rootProcessInstanceId IN (:sourceProcessInstanceIds)\n\t</query>\n\n\t<query name=\"getSourceObjectIdsOfArchivedFlowNodeInstances\">\n\t\tSELECT f.sourceObjectId\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance AS f\n\t\tWHERE f.logicalGroup2 IN (:sourceProcessInstanceIds)\n\t</query>\n\n\t<query name=\"deleteArchivedFlowNodeInstances\">\n\t\tDELETE\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance AS f\n\t\tWHERE f.sourceObjectId IN (:sourceObjectIds)\n\t</query>\n\n</hibernate-mapping>\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/main/resources/org/bonitasoft/engine/core/process/instance/model/impl/hibernate/process.instance.queries.hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\" \"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd\">\n<hibernate-mapping auto-import=\"false\">\n\n\t<query name=\"getSActivityInstanceById\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SActivityInstance AS a\n\t\tWHERE a.id = :id\n\t</query>\n\n\t<query name=\"getSHumanTaskInstanceById\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a\n\t\tWHERE a.id = :id\n\t</query>\n\n\t<query name=\"getSFlowNodeInstanceById\">\n\t\tSELECT flowNode\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS flowNode\n\t\tWHERE flowNode.id = :id\n\t</query>\n\n\t<query name=\"getActivitiesFromProcessInstance\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SActivityInstance AS a\n\t\tWHERE a.rootContainerId = :rootContainerId\n\t\tORDER BY a.id ASC\n\t</query>\n\n\t<query name=\"getNumberOfFlowNode\">\n\t\tSELECT count(f.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS f\n\t\tWHERE f.logicalGroup4 = :parentProcessInstanceId\n\t</query>\n\t\n\t<query name=\"getNumberOfActivitiesFromProcessInstance\">\n\t\tSELECT count(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SActivityInstance AS a\n\t\tWHERE a.rootContainerId = :rootContainerId\n\t</query>\n\n\t<query name=\"getAllChildrenOfProcessInstance\">\n\t\tSELECT f\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS f\n\t\tWHERE f.logicalGroup4 = :parentProcessInstanceId\n\t\tORDER BY f.id ASC\n\t</query>\n\n\t<query name=\"getDirectChildrenOfProcessInstance\">\n\t\tSELECT f\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS f\n\t\tWHERE f.logicalGroup4 = :parentProcessInstanceId\n\t\tAND f.logicalGroup3 = 0\n\t\tORDER BY f.id ASC\n\t</query>\n\n\t<query name=\"getDirectChildrenOfActivityInstance\">\n\t\tSELECT f\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS f\n\t\tWHERE f.logicalGroup3 = :parentActivityInstanceId\n\t\tORDER BY f.id ASC\n\t</query>\n\n    <query name=\"getNumberOfFlowNodesOfProcessDefinitionInAllStates\">\n        SELECT new org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstanceStateCounter(name AS name, stateName AS statename, count(f) AS numberof)\n        FROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS f\n        WHERE logicalGroup1 = :processDefinitionId\n        GROUP BY name, stateName\n        ORDER BY name ASC, stateName ASC\n    </query>\n\n\t<query name=\"getNumberOfFlowNodesInAllStates\">\n\t\tSELECT new org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstanceStateCounter(name AS name, stateName AS statename, count(f) AS numberof)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS f\n\t\tWHERE logicalGroup4 = :parentProcessInstanceId\n\t\tGROUP BY name, stateName\n\t\tORDER BY name ASC, stateName ASC\n\t</query>\n\n\t<query name=\"getOpenActivitiesFromProcessInstance\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a\n\t\tWHERE a.rootContainerId = :rootContainerId\n\t\tAND a.stateExecuting = FALSE\n\t\tAND a.stable = TRUE\n\t\tAND a.terminal = FALSE\n\t</query>\n\n\t<query name=\"getFlowNodeInstanceIdsToRecover\">\n\t\tSELECT f.id\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS f\n\t\tWHERE f.stateId != 3\n\t\tAND type(f) != org.bonitasoft.engine.core.process.instance.model.SGatewayInstance\n\t\tAND (f.stateExecuting = TRUE\n\t\t\tOR f.stable = FALSE\n\t\t\tOR f.terminal = TRUE\n\t\t\tOR f.stateCategory = 'ABORTING'\n\t\t\tOR f.stateCategory = 'CANCELLING')\n\t\tAND f.lastUpdateDate &lt; :maxLastUpdate\n\t\tORDER BY id\n\t</query>\n\n\t<query name=\"getGatewayInstanceIdsToRecover\">\n\t\tSELECT f.id\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SGatewayInstance AS f\n\t\tWHERE f.stateId != 3\n\t\tAND   (f.stateId != 61\n\t\t      OR f.hitBys LIKE 'FINISH:%'\n\t\t      OR f.stateCategory = 'ABORTING'\n\t\t      OR f.stateCategory = 'CANCELLING')\n\t\tAND f.lastUpdateDate &lt; :maxLastUpdate\n\t\tORDER BY id\n\t</query>\n\n\t<query name=\"getFlowNodeInstancesByNameAndParentContainerId\">\n\t\tSELECT f\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS f\n\t\tWHERE f.name = :name\n\t\tAND f.parentContainerId = :parentContainerId\n\t</query>\n\n\t<query name=\"getFlowNodeInstancesByIds\">\n\t\tSELECT f\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS f\n\t\tWHERE f.id IN (:ids)\n\t</query>\n\n\t<query name=\"getInclusiveGatewayInstanceOfProcessInstance\">\n\t\tSELECT g\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SGatewayInstance AS g\n\t\tWHERE g.logicalGroup4 = :processInstanceId\n\t\tAND g.terminal = FALSE\n\t\tAND g.gatewayType = 'INCLUSIVE'\n\t</query>\n\t<query name=\"getActiveGatewayInstanceOfProcess\">\n\t\tSELECT g\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SGatewayInstance AS g\n\t\tWHERE g.logicalGroup4 = :parentProcessInstanceId\n\t\tAND g.name = :name\n\t\tAND g.terminal = FALSE\n\t</query>\n\n\t<query name=\"getActivitiesWithStates\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SActivityInstance AS a\n\t\tWHERE a.rootContainerId = :rootContainerId\n\t\tAND a.stateId IN (:stateIds)\n\t</query>\n\n\t<query name=\"getChildrenOfAnActivity\">\n\t\tSELECT f\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS f\n\t\tWHERE f.logicalGroup3 = :parentActivityInstanceId\n\t\tAND f.terminal = FALSE\n\t</query>\n\n\t<query name=\"updateStrictHuman\">\n\t\tUPDATE\n\t\torg.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance\n\t\tSET assigneeId = :assigneeId,\n\t\tclaimedDate = :claimedDate,\n\t\tlastUpdateDate = :lastUpdateDate\n\t\tWHERE id = :id\n\t\tAND (\n\t\t\t:assigneeId = 0\n\t\t\tOR assigneeId = 0\n\t\t\tOR :assigneeId = assigneeId\n\t\t)\n\t</query>\n\n\t<query name=\"getAssignedUserTasks\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a\n\t\tWHERE a.assigneeId = :assigneeId\n\t\tAND a.stable = TRUE\n\t\tAND a.terminal = FALSE\n\t\tAND a.stateExecuting = FALSE\n\t\tAND a.stateCategory = 'NORMAL'\n\t</query>\n\n\t<query name=\"getPendingUserTasks\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a\n\t\tWHERE a.stable = TRUE\n\t\tAND a.stateExecuting = FALSE\n\t\tAND a.stateCategory = 'NORMAL'\n\t\tAND a.terminal = FALSE\n\t\tAND a.assigneeId = 0\n\t\tAND EXISTS (SELECT mapping.id\n\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping\n\t\t\tWHERE mapping.activityId=a.id\n\t\t\tAND ( mapping.userId = :userId\n\t\t\t\tOR mapping.actorId in (:actorIds))\n\t\t)\n\t</query>\n\n\t<query name=\"getPendingMappingsOfTask\">\n\t\tSELECT mapping\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping\n\t\tWHERE mapping.activityId=:activityId\n\t</query>\n\n\t<query name=\"getPendingUserTasksWithoutActorIds\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a\n\t\tWHERE a.stable = TRUE\n\t\tAND a.stateExecuting = FALSE\n\t\tAND a.stateCategory = 'NORMAL'\n\t\tAND a.terminal = FALSE\n\t\tAND a.assigneeId = 0\n\t\tAND EXISTS (\n\t\t\tSELECT mapping.id \n\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping\n\t\t\tWHERE mapping.activityId=a.id\n\t\t\tAND mapping.userId = :userId\n\t\t)\n\t</query>\n\n\t<query name=\"getSGatewayInstanceById\">\n\t\tSELECT g\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SGatewayInstance AS g\n\t\tWHERE g.id = :id\n\t</query>\n\n\n\t<query name=\"getSEventInstanceById\">\n\t\tSELECT e\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.event.SEventInstance AS e\n\t\tWHERE e.id = :id\n\t</query>\n\n\t<query name=\"getActivityBoundaryEventInstances\">\n\t\tSELECT e\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance AS e\n\t\tWHERE e.activityInstanceId = :activityInstanceId\n\t\tORDER BY e.id\n\t</query>\n\n\t<query name=\"getEventInstancesFromRootContainer\">\n\t\tSELECT e\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.event.SEventInstance AS e\n\t\tWHERE e.rootContainerId = :rootContainerId\n\t</query>\n\n\t<query name=\"getEventTriggerInstances\">\n\t\tSELECT t\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance AS t\n\t\tWHERE t.eventInstanceId = :eventInstanceId\n\t</query>\n\n\t<query name=\"getEventTriggerInstanceById\">\n\t\tSELECT t\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance AS t\n\t\tWHERE t.id = :id\n\t</query>\n\n\t<!-- get number of -->\n\t<query name=\"getNumberOfAssignedUserTaskInstances\">\n\t\tSELECT count(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a\n\t\tWHERE a.assigneeId = :assigneeId\n\t\tAND a.stable = TRUE\n\t\tAND a.stateCategory = 'NORMAL'\n\t\tAND a.terminal = FALSE\n\t</query>\n\t\n\t<query name=\"getNumbersOfOpenTasksForUsers\">\n\t\tSELECT new map(a.assigneeId as userId, count(a) as numberOfTasks)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a\n\t\tWHERE a.assigneeId IN (:assigneeIds)\n\t\tAND a.stable = TRUE\n\t\tAND a.stateCategory = 'NORMAL'\n\t\tAND a.terminal = FALSE\n\t\tGROUP BY a.assigneeId\n        ORDER BY a.assigneeId ASC\n\t</query>\n\t\n\t<query name=\"getNumberOfSHumanTaskInstancePendingForUser\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a\n\t\tWHERE a.stable = TRUE\n\t\tAND a.stateExecuting = FALSE\n\t\tAND a.stateCategory = 'NORMAL'\n\t\tAND a.terminal = FALSE\n\t\tAND a.assigneeId = 0\n\t\tAND EXISTS (SELECT mapping.id\n\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping\n\t\t\tWHERE mapping.activityId=a.id\n\t\t\tAND ( mapping.userId = :userId\n\t\t\t\tOR mapping.actorId in (SELECT actor.id\n\t\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor,\n\t\t\t\t\t\torg.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n\t\t\t\t\tWHERE actor.id = actormember.actorId\n\t\t\t\t\tAND ( actormember.userId = :userId\n \t  \t\t\t\t\tOR actormember.id IN (\n\t\t\t\t\t\t\tSELECT actormember.id\n\t\t\t\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember, org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\t\t\t\tWHERE um.userId = :userId\n\t\t\t\t\t\t\tAND (\n\t\t\t\t\t\t\t\t(actormember.groupId = um.groupId AND actormember.roleId = -1)\n\t\t\t\t\t\t\t\tOR (actormember.roleId = um.roleId AND actormember.groupId = -1)\n\t\t\t\t\t\t\t\tOR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId)\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t)\n     \t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t</query>\n\t\n\t<query name=\"searchSHumanTaskInstancePendingForUser\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a\n\t\tWHERE a.stable = TRUE\n\t\tAND a.stateExecuting = FALSE\n\t\tAND a.stateCategory = 'NORMAL'\n\t\tAND a.terminal = FALSE\n\t\tAND a.assigneeId = 0\n\t\tAND EXISTS (SELECT mapping.id\n\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping\n\t\t\tWHERE mapping.activityId=a.id\n\t\t\tAND ( mapping.userId = :userId\n\t\t\t\tOR mapping.actorId in (SELECT actor.id\n\t\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor,\n\t\t\t\t\t\torg.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n\t\t\t\t\tWHERE actor.id = actormember.actorId\n\t\t\t\t\tAND ( actormember.userId = :userId\n \t  \t\t\t\t\tOR actormember.id IN (\n\t\t\t\t\t\t\tSELECT actormember.id\n\t\t\t\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember, org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\t\t\t\tWHERE um.userId = :userId\n\t\t\t\t\t\t\tAND (\n\t\t\t\t\t\t\t\t(actormember.groupId = um.groupId AND actormember.roleId = -1)\n\t\t\t\t\t\t\t\tOR (actormember.roleId = um.roleId AND actormember.groupId = -1)\n\t\t\t\t\t\t\t\tOR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId)\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t)\n     \t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t</query>\n\t\n\t<query name=\"getNumberOfSHumanTaskInstancePendingOrAssigned\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a\n\t\tWHERE a.stable = TRUE\n\t\tAND a.stateExecuting = FALSE\n\t\tAND a.stateCategory = 'NORMAL'\n\t\tAND a.terminal = FALSE\n\t\tAND (\n\t\t\t(\n\t\t\t\ta.assigneeId = :userId\n\t\t\t) OR (\n\t\t\t\ta.assigneeId = 0\n\t\t\t\tAND EXISTS (SELECT mapping.id\n\t\t\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping\n\t\t\t\t\tWHERE mapping.activityId=a.id\n\t\t\t\t\tAND ( mapping.userId = :userId\n\t\t\t\t\t\tOR mapping.actorId in (SELECT actor.id\n\t\t\t\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor,\n\t\t\t\t\t\t\t\torg.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n\t\t\t\t\t\t\tWHERE actor.id = actormember.actorId\n\t\t\t\t\t\t\tAND ( actormember.userId = :userId\n\t\t \t  \t\t\t\t\tOR actormember.id IN (\n\t\t\t\t\t\t\t\t\tSELECT actormember.id\n\t\t\t\t\t\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember, org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\t\t\t\t\t\tWHERE um.userId = :userId\n\t\t\t\t\t\t\t\t\tAND (\n\t\t\t\t\t\t\t\t\t\t(actormember.groupId = um.groupId AND actormember.roleId = -1)\n\t\t\t\t\t\t\t\t\t\tOR (actormember.roleId = um.roleId AND actormember.groupId = -1)\n\t\t\t\t\t\t\t\t\t\tOR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId)\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t)\n\t\t     \t\t\t\t)\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t</query>\n\t\n\t<query name=\"searchSHumanTaskInstancePendingOrAssigned\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a\n\t\tWHERE a.stable = TRUE\n\t\tAND a.stateExecuting = FALSE\n\t\tAND a.stateCategory = 'NORMAL'\n\t\tAND a.terminal = FALSE\n\t\tAND (\n\t\t\t(\n\t\t\t\ta.assigneeId = :userId\n\t\t\t) OR (\n\t\t\t\ta.assigneeId = 0\n\t\t\t\tAND EXISTS (SELECT mapping.id\n\t\t\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping\n\t\t\t\t\tWHERE mapping.activityId=a.id\n\t\t\t\t\tAND ( mapping.userId = :userId\n\t\t\t\t\t\tOR mapping.actorId in (SELECT actor.id\n\t\t\t\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor,\n\t\t\t\t\t\t\t\torg.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n\t\t\t\t\t\t\tWHERE actor.id = actormember.actorId\n\t\t\t\t\t\t\tAND ( actormember.userId = :userId\n\t\t \t  \t\t\t\t\tOR actormember.id IN (\n\t\t\t\t\t\t\t\t\tSELECT actormember.id\n\t\t\t\t\t\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember, org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\t\t\t\t\t\tWHERE um.userId = :userId\n\t\t\t\t\t\t\t\t\tAND (\n\t\t\t\t\t\t\t\t\t\t(actormember.groupId = um.groupId AND actormember.roleId = -1)\n\t\t\t\t\t\t\t\t\t\tOR (actormember.roleId = um.roleId AND actormember.groupId = -1)\n\t\t\t\t\t\t\t\t\t\tOR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId)\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t)\n\t\t     \t\t\t\t)\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t</query>\n\n\t<query name=\"getNumberOfSHumanTaskInstancePendingOrAssignedOrAssignedToOthers\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a\n\t\tWHERE a.stable = TRUE\n\t\tAND a.stateExecuting = FALSE\n\t\tAND a.stateCategory = 'NORMAL'\n\t\tAND a.terminal = FALSE\n\t\tAND (\n\t\t\t(\n\t\t\t\ta.assigneeId = :userId\n\t\t\t) OR (\n\t\t\t\tEXISTS (SELECT mapping.id\n\t\t\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping\n\t\t\t\t\tWHERE mapping.activityId=a.id\n\t\t\t\t\tAND ( mapping.userId = :userId\n\t\t\t\t\t\tOR mapping.actorId in (SELECT actor.id\n\t\t\t\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor,\n\t\t\t\t\t\t\t\torg.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n\t\t\t\t\t\t\tWHERE actor.id = actormember.actorId\n\t\t\t\t\t\t\tAND ( actormember.userId = :userId\n\t\t \t  \t\t\t\t\tOR actormember.id IN (\n\t\t\t\t\t\t\t\t\tSELECT actormember.id\n\t\t\t\t\t\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember, org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\t\t\t\t\t\tWHERE um.userId = :userId\n\t\t\t\t\t\t\t\t\tAND (\n\t\t\t\t\t\t\t\t\t\t(actormember.groupId = um.groupId AND actormember.roleId = -1)\n\t\t\t\t\t\t\t\t\t\tOR (actormember.roleId = um.roleId AND actormember.groupId = -1)\n\t\t\t\t\t\t\t\t\t\tOR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId)\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t)\n\t\t     \t\t\t\t)\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t</query>\n\n\t<query name=\"searchSHumanTaskInstancePendingOrAssignedOrAssignedToOthers\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a\n\t\tWHERE a.stable = TRUE\n\t\tAND a.stateExecuting = FALSE\n\t\tAND a.stateCategory = 'NORMAL'\n\t\tAND a.terminal = FALSE\n\t\tAND (\n\t\t\t(\n\t\t\t\ta.assigneeId = :userId\n\t\t\t) OR (\n\t\t\t\tEXISTS (SELECT mapping.id\n\t\t\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping\n\t\t\t\t\tWHERE mapping.activityId=a.id\n\t\t\t\t\tAND ( mapping.userId = :userId\n\t\t\t\t\t\tOR mapping.actorId in (SELECT actor.id\n\t\t\t\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor,\n\t\t\t\t\t\t\t\torg.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n\t\t\t\t\t\t\tWHERE actor.id = actormember.actorId\n\t\t\t\t\t\t\tAND ( actormember.userId = :userId\n\t\t \t  \t\t\t\t\tOR actormember.id IN (\n\t\t\t\t\t\t\t\t\tSELECT actormember.id\n\t\t\t\t\t\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember, org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\t\t\t\t\t\tWHERE um.userId = :userId\n\t\t\t\t\t\t\t\t\tAND (\n\t\t\t\t\t\t\t\t\t\t(actormember.groupId = um.groupId AND actormember.roleId = -1)\n\t\t\t\t\t\t\t\t\t\tOR (actormember.roleId = um.roleId AND actormember.groupId = -1)\n\t\t\t\t\t\t\t\t\t\tOR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId)\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t)\n\t\t     \t\t\t\t)\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t</query>\n\n\t<query name=\"getNumberOfSHumanTaskInstancePendingAssignedTo\">\n        SELECT COUNT(a.id)\n        FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a\n\t\tWHERE a.stable = TRUE\n\t\tAND a.stateExecuting = FALSE\n\t\tAND a.stateCategory = 'NORMAL'\n\t\tAND a.terminal = FALSE\n        AND a.assigneeId = :userId\n\t</query>\n\n\t<query name=\"searchSHumanTaskInstancePendingAssignedTo\">\n        SELECT a\n        FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a\n\t\tWHERE a.stable = TRUE\n\t\tAND a.stateExecuting = FALSE\n\t\tAND a.stateCategory = 'NORMAL'\n\t\tAND a.terminal = FALSE\n        AND a.assigneeId = :userId\n\t</query>\n\n\t<query name=\"getNumberOfOpenActivities\">\n\t\tSELECT count(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SActivityInstance AS a\n\t\tWHERE a.rootContainerId = :rootContainerId\n\t\tAND a.stateExecuting = FALSE\n\t\tAND a.stable = TRUE\n\t\tAND a.terminal = FALSE\n\t</query>\n\n\t<query name=\"getConnectorInstancesWithState\">\n\t\tSELECT c\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SConnectorInstance AS c\n\t\tWHERE c.containerId = :containerId\n\t\tAND c.containerType = :containerType\n\t\tAND c.activationEvent = :activationEvent\n\t\tAND c.state = :state\n\t</query>\n\n    <query name=\"getConnectorInstancesWithFailureInfoInState\">\n        SELECT cfi\n        FROM org.bonitasoft.engine.core.process.instance.model.SConnectorInstanceWithFailureInfo AS cfi\n        WHERE cfi.containerId = :containerId\n        AND cfi.containerType = :containerType\n        AND cfi.state = :state\n    </query>\n\t\n\t\n\t<query name=\"getNumberOfSConnectorInstance\">\n\t\tSELECT COUNT(c.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SConnectorInstance AS c\n\t</query>\n\t\n\t<query name=\"searchSConnectorInstance\">\n\t\tSELECT c\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SConnectorInstance AS c\n\t</query>\n\t\n\t<query name=\"getNextExecutableConnectorInstance\">\n\t\tSELECT c\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SConnectorInstance AS c\n\t\tWHERE c.containerId = :containerId\n\t\tAND c.containerType = :containerType\n\t\tAND c.activationEvent = :activationEvent\n\t\tAND (c.state = 'TO_BE_EXECUTED' OR c.state = 'TO_RE_EXECUTE' OR c.state = 'EXECUTING')\n\t\tORDER BY c.executionOrder\n\t</query>\n\t<query name=\"getConnectorInstances\">\n\t\tSELECT c\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SConnectorInstance AS c\n\t\tWHERE c.containerId = :containerId\n\t\tAND c.containerType = :containerType\n\t</query>\n    <query name=\"getConnectorInstancesOrderedById\">\n        SELECT c\n        FROM org.bonitasoft.engine.core.process.instance.model.SConnectorInstance AS c\n        WHERE c.containerId = :containerId\n        AND c.containerType = :containerType\n        ORDER BY  c.id\n    </query>\n\t<query name=\"getNumberOfConnectorInstances\">\n\t\tSELECT count(c.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SConnectorInstance AS c\n\t\tWHERE c.containerId = :containerId\n\t\tAND c.containerType = :containerType\n\t</query>\n\t<query name=\"getConnectorInstance\">\n\t\tSELECT c\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SConnectorInstance AS c\n\t\tWHERE c.containerId = :id\n\t</query>\n\t\n\t<query name=\"getConnectorInstanceWithFailureInfo\">\n\t\tSELECT cfi\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SConnectorInstanceWithFailureInfo AS cfi\n\t\tWHERE cfi.containerId = :id\n\t</query>\n\n\t<query name=\"getCaughtErrorByRelatedActivityAndAnyErrorCode\">\n\t\tSELECT s\n\t\tFROM  org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingErrorEvent AS s\n\t\tWHERE s.relatedActivityInstanceId = :relatedActivityInstanceId\n\t\tAND s.errorCode IS NULL\n\t</query>\n\n\t<query name=\"getCaughtErrorByRelatedActivityAndErrorCode\">\n\t\tSELECT s\n\t\tFROM  org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingErrorEvent AS s\n\t\tWHERE s.relatedActivityInstanceId = :relatedActivityInstanceId\n\t\tAND s.errorCode = :errorCode\n\t</query>\n\n\t<query name=\"getMessageEventCouples\">\n\t\tSELECT new org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageEventCouple(s.id, s.eventType, m.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent AS s,\n\t\t\t org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance AS m\n\t\tWHERE m.messageName = s.messageName\n\t\tAND m.targetProcess = s.processName\n\t\tAND (m.targetFlowNode = null OR m.targetFlowNode = s.flowNodeName)\n\t\tAND m.locked = false\n\t\tAND s.locked = false\n\t\tAND m.handled = false\n\t\tAND s.active = true\n\t\tAND s.progress = 0\n\t\tAND s.correlation1 = m.correlation1\n\t\tAND s.correlation2 = m.correlation2\n\t\tAND s.correlation3 = m.correlation3\n\t\tAND s.correlation4 = m.correlation4\n\t\tAND s.correlation5 = m.correlation5\n\t</query>\n\n\n\t<query name=\"getMessageInstanceIdOlderThanCreationDate\">\n\t\tSELECT m.id\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance AS m\n\t\tWHERE m.creationDate &lt;= :creationDate\n\t</query>\n\n\t<query name=\"deleteMessageInstanceByIds\">\n\t\tDELETE FROM org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance AS m\n\t\tWHERE m.id IN (:ids)\n\t</query>\n\n\t<query name=\"getListeningSignals\">\n\t\tSELECT s\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent AS s\n\t\tWHERE s.signalName = :signalName\n        ORDER BY s.id ASC\n\t</query>\n\n\t<query name=\"getStartWaitingEvents\">\n        SELECT s\n        FROM org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent AS s\n        WHERE s.processDefinitionId = :processDefinitionId\n        AND (s.eventType = 'START_EVENT')\n\t</query>\n\n    <query name=\"getWaitingEventsForProcessInstance\">\n        SELECT s\n        FROM org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent AS s\n        WHERE s.parentProcessInstanceId = :processInstanceId\n    </query>\n\n\t<query name=\"getWaitingEventsOfFlowNode\">\n\t\tSELECT s\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent AS s\n\t\tWHERE s.flowNodeInstanceId = :flowNodeInstanceId\n\t</query>\n\n\n\t<query name=\"resetProgressMessageInstances\">\n\t\tUPDATE org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance AS m\n\t\tSET m.handled = false\n\t\tWHERE m.handled = true\n\t</query>\n\t\n\t<query name=\"getInProgressMessageInstances\">\n\t\tSELECT m\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance AS m\n\t\tWHERE m.handled = true\n\t</query>\n\t\n\t<query name=\"resetInProgressWaitingEvents\">\n\t\tUPDATE org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent AS s\n\t\tSET s.progress = 0\n\t\tWHERE s.progress = 1\n\t</query>\n\n\t<query name=\"getInProgressWaitingEvents\">\n\t\tSELECT s\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent AS s\n\t\tWHERE s.progress = 1\n\t</query>\n\n\t<query name=\"getNumberOfSWaitingEvent\">\n\t\tSELECT COUNT(s.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent AS s\n\t</query>\n\t\n\t<query name=\"searchSWaitingEvent\">\n\t\tSELECT s\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent AS s\n\t</query>\t\n\n\t<query name=\"getNumberOfSWaitingSignalEvent\">\n\t\tSELECT COUNT(s.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent AS s\n\t</query>\n\t\n\t<query name=\"searchSWaitingSignalEvent\">\n\t\tSELECT s\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent AS s\n\t</query>\t\n\n\t<query name=\"getNumberOfSWaitingMessageEvent\">\n\t\tSELECT COUNT(s.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent AS s\n\t</query>\n\t\n\t<query name=\"searchSWaitingMessageEvent\">\n\t\tSELECT s\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent AS s\n\t</query>\t\n\n\t<query name=\"getNumberOfSWaitingErrorEvent\">\n\t\tSELECT COUNT(s.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingErrorEvent AS s\n\t</query>\n\t\n\t<query name=\"searchSWaitingErrorEvent\">\n\t\tSELECT s\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingErrorEvent AS s\n\t</query>\t\n\t\n\t<query name=\"getNumberOfSEventTriggerInstance\">\n\t\tSELECT COUNT(t.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance AS t\n\t</query>\n\n\t<query name=\"getNumberOfSTimerEventTriggerInstanceByProcessInstance\">\n\t\tSELECT COUNT(t.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance AS t,\n\t\torg.bonitasoft.engine.core.process.instance.model.event.SEventInstance AS e\n\t\tWHERE t.eventInstanceId = e.id\n\t\tAND e.logicalGroup4 = :processInstanceId\n\t</query>\n\t\n\t<query name=\"searchSTimerEventTriggerInstanceByProcessInstance\">\n\t\tSELECT t\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance AS t,\n\t\torg.bonitasoft.engine.core.process.instance.model.event.SEventInstance AS e\n\t\tWHERE t.eventInstanceId = e.id\n\t\tAND e.logicalGroup4 = :processInstanceId\n\t</query>\t\n\t\n\t<query name=\"getNumberOfSTimerEventTriggerInstancewithSEventInstanceByProcessInstance\">\n\t\tSELECT COUNT(t.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance AS t,\n\t\torg.bonitasoft.engine.core.process.instance.model.event.SEventInstance AS e\n\t\tWHERE t.eventInstanceId = e.id\n\t\tAND e.logicalGroup4 = :processInstanceId\n\t</query>\n\t\n\t<query name=\"searchSTimerEventTriggerInstancewithSEventInstanceByProcessInstance\">\n\t\tSELECT t\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance AS t,\n\t\torg.bonitasoft.engine.core.process.instance.model.event.SEventInstance AS e\n\t\tWHERE t.eventInstanceId = e.id\n\t\tAND e.logicalGroup4 = :processInstanceId\n\t</query>\t\n\t\n\t<query name=\"getNumberOfSTimerEventTriggerInstance\">\n\t\tSELECT COUNT(t.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance AS t\n\t</query>\n\t\n\t<query name=\"searchSTimerEventTriggerInstance\">\n\t\tSELECT t\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance AS t\n\t</query>\t\n\n\t<!-- External Service Queries -->\n\t<query name=\"getNumberOfSHumanTaskInstance\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a\n\t</query>\n\n\t<query name=\"searchSHumanTaskInstance\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a\n\t</query>\n\n\t<query name=\"getNumberOfSHumanTaskInstancewithSActivityInstanceSProcessSupervisor\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"searchSHumanTaskInstancewithSActivityInstanceSProcessSupervisor\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"getNumberOfSAutomaticTaskInstancewithSActivityInstanceSProcessSupervisor\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"searchSAutomaticTaskInstancewithSActivityInstanceSProcessSupervisor\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"getNumberOfSManualTaskInstancewithSActivityInstanceSProcessSupervisor\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"searchSManualTaskInstancewithSActivityInstanceSProcessSupervisor\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"getNumberOfSUserTaskInstancewithSActivityInstanceSProcessSupervisor\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"searchSUserTaskInstancewithSActivityInstanceSProcessSupervisor\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"getNumberOfSReceiveTaskInstancewithSActivityInstanceSProcessSupervisor\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"searchSReceiveTaskInstancewithSActivityInstanceSProcessSupervisor\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"getNumberOfSSendTaskInstancewithSActivityInstanceSProcessSupervisor\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"searchSSendTaskInstancewithSActivityInstanceSProcessSupervisor\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"getNumberOfSCallActivityInstancewithSActivityInstanceSProcessSupervisor\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance AS a,\n\t\torg.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"searchSCallActivityInstancewithSActivityInstanceSProcessSupervisor\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"getNumberOfSMultiInstanceActivityInstancewithSActivityInstanceSProcessSupervisor\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance AS a,\n\t\torg.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"searchSMultiInstanceActivityInstancewithSActivityInstanceSProcessSupervisor\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"getNumberOfSSubProcessActivityInstancewithSActivityInstanceSProcessSupervisor\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance AS a,\n\t\torg.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"searchSSubProcessActivityInstancewithSActivityInstanceSProcessSupervisor\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"getNumberOfSLoopActivityInstancewithSActivityInstanceSProcessSupervisor\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance AS a,\n\t\torg.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"searchSLoopActivityInstancewithSActivityInstanceSProcessSupervisor\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\t\n\t<query name=\"getNumberOfSHumanTaskInstancewithSProcessSupervisor\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"searchSHumanTaskInstancewithSProcessSupervisor\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"getNumberOfSActivityInstancewithSProcessSupervisor\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SActivityInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"searchSActivityInstancewithSProcessSupervisor\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SActivityInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"getNumberOfSAutomaticTaskInstancewithSProcessSupervisor\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"searchSAutomaticTaskInstancewithSProcessSupervisor\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"getNumberOfSManualTaskInstancewithSProcessSupervisor\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"searchSManualTaskInstancewithSProcessSupervisor\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"getNumberOfSUserTaskInstancewithSProcessSupervisor\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"searchSUserTaskInstancewithSProcessSupervisor\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"getNumberOfSReceiveTaskInstancewithSProcessSupervisor\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"searchSReceiveTaskInstancewithSProcessSupervisor\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"getNumberOfSSendTaskInstancewithSProcessSupervisor\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"searchSSendTaskInstancewithSProcessSupervisor\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"getNumberOfSCallActivityInstancewithSProcessSupervisor\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance AS a,\n\t\torg.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"searchSCallActivityInstancewithSProcessSupervisor\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"getNumberOfSMultiInstanceActivityInstancewithSProcessSupervisor\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance AS a,\n\t\torg.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"searchSMultiInstanceActivityInstancewithSProcessSupervisor\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"getNumberOfSSubProcessActivityInstancewithSProcessSupervisor\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance AS a,\n\t\torg.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"searchSSubProcessActivityInstancewithSProcessSupervisor\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"getNumberOfSLoopActivityInstancewithSProcessSupervisor\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance AS a,\n\t\torg.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"searchSLoopActivityInstancewithSProcessSupervisor\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE a.logicalGroup1 =  processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"getNumberOfSHumanTaskInstanceSupervisedBy\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor\n\t\tWHERE a.logicalGroup1 =  supervisor.processDefId\n\t\tAND (supervisor.userId = :supervisorId\n\t\t\tOR (supervisor.id IN (\n\t\t\t\t\tSELECT supervisor.id\n\t\t\t\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor,\n\t\t\t\t\torg.bonitasoft.engine.identity.model.SUserMembership AS um\n\t\t\t\t\tWHERE um.userId = :supervisorId\n\t\t\t\t\tAND (\n\t\t\t\t\t\t(supervisor.groupId = um.groupId AND supervisor.roleId &lt;= 0)\n\t\t\t\t\t\tOR (supervisor.roleId = um.roleId AND supervisor.groupId &lt;= 0)\n\t\t\t\t\t\tOR (supervisor.roleId = um.roleId AND supervisor.groupId = um.groupId) \n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t</query>\n\n\t<query name=\"searchSHumanTaskInstanceSupervisedBy\">\n\t\tSELECT DISTINCT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor\n\t\tWHERE a.logicalGroup1 =  supervisor.processDefId\n\t\tAND (supervisor.userId = :supervisorId\n\t\t\tOR (supervisor.id IN (\n\t\t\t\t\tSELECT supervisor.id\n\t\t\t\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor,\n\t\t\t\t\torg.bonitasoft.engine.identity.model.SUserMembership AS um\n\t\t\t\t\tWHERE um.userId = :supervisorId\n\t\t\t\t\tAND (\n\t\t\t\t\t\t(supervisor.groupId = um.groupId AND supervisor.roleId &lt;= 0)\n\t\t\t\t\t\tOR (supervisor.roleId = um.roleId AND supervisor.groupId &lt;= 0)\n\t\t\t\t\t\tOR (supervisor.roleId = um.roleId AND supervisor.groupId = um.groupId) \n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t</query>\n\n\t<!-- processsupervisor is used by search -->\n\t<query name=\"searchSHumanTaskInstancePendingSupervisedBy\">\n\t\tSELECT DISTINCT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor\n\t\tWHERE a.stateExecuting = FALSE\n\t\tAND a.stateCategory = 'NORMAL'\n\t\tAND a.logicalGroup1 = supervisor.processDefId\n\t\tAND (supervisor.userId = :userId\n\t\t\tOR (supervisor.id IN (\n\t\t\t\t\tSELECT supervisor.id\n\t\t\t\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor,\n\t\t\t\t\torg.bonitasoft.engine.identity.model.SUserMembership AS um\n\t\t\t\t\tWHERE um.userId = :userId\n\t\t\t\t\tAND (\n\t\t\t\t\t\t(supervisor.groupId = um.groupId AND supervisor.roleId &lt;= 0)\n\t\t\t\t\t\tOR (supervisor.roleId = um.roleId AND supervisor.groupId &lt;= 0)\n\t\t\t\t\t\tOR (supervisor.roleId = um.roleId AND supervisor.groupId = um.groupId) \n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t</query>\n\t\t\n\t<!-- processsupervisor is used by search -->\n\t<query name=\"getNumberOfSHumanTaskInstancePendingSupervisedBy\">\n\t\tSELECT COUNT(DISTINCT a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor\n\t\tWHERE a.stateExecuting = FALSE\n\t\tAND a.stateCategory = 'NORMAL'\n\t\tAND a.logicalGroup1 = supervisor.processDefId\n\t\tAND (supervisor.userId = :userId\n\t\t\tOR (supervisor.id IN (\n\t\t\t\t\tSELECT supervisor.id\n\t\t\t\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor,\n\t\t\t\t\torg.bonitasoft.engine.identity.model.SUserMembership AS um\n\t\t\t\t\tWHERE um.userId = :userId\n\t\t\t\t\tAND (\n\t\t\t\t\t\t(supervisor.groupId = um.groupId AND supervisor.roleId &lt;= 0)\n\t\t\t\t\t\tOR (supervisor.roleId = um.roleId AND supervisor.groupId &lt;= 0)\n\t\t\t\t\t\tOR (supervisor.roleId = um.roleId AND supervisor.groupId = um.groupId) \n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t</query>\n\t\t\t\n\t<query name=\"getNumberOfSHumanTaskInstanceManagedBy\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a\n\t\tWHERE a.stable = TRUE\n\t\tAND a.terminal = FALSE\n\t\tAND a.stateCategory = 'NORMAL'\n\t\tAND a.assigneeId IN (\n\t\t\tSELECT u.id\n\t\t\tFROM org.bonitasoft.engine.identity.model.SUser AS u\n\t\t\tWHERE u.managerUserId = :managerUserId\n\t\t)\n\t</query>\n\n\t<query name=\"searchSHumanTaskInstanceManagedBy\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a\n\t\tWHERE a.stable = TRUE\n\t\tAND a.terminal = FALSE\n\t\tAND a.stateCategory = 'NORMAL'\n\t\tAND a.assigneeId IN (\n\t\t\tSELECT u.id\n\t\t\tFROM org.bonitasoft.engine.identity.model.SUser AS u\n\t\t\tWHERE u.managerUserId = :managerUserId\n\t\t)\n\t</query>\n\t\n\t<query name=\"getNumberOfSHumanTaskInstancePendingManagedBy\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a\n\t\tWHERE a.stateId = 4\n\t\tAND a.stable = TRUE\n\t\tAND a.terminal = FALSE\n\t\tAND a.stateCategory = 'NORMAL'\n\t\tAND exists (\n    \t\tSELECT mapping.activityId\n        \t\tFROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping\n        \t\tWHERE mapping.activityId=a.id\n        \t\tAND\n           \t\t( mapping.userId IN (\n           \t\t\tSELECT u.id \n           \t\t\tFROM org.bonitasoft.engine.identity.model.SUser AS u\n           \t\t\tWHERE u.managerUserId = :managerUserId )\n            \t\tOR mapping.actorId IN (\n                \t\tSELECT actor.id\n\t\t\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor,\n\t\t\t\t\t\t\t org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n                \t\tWHERE actor.id = actormember.actorId \n                \t\tAND ( actormember.userId IN (\n                \t\t       SELECT u.id \n                \t\t       FROM org.bonitasoft.engine.identity.model.SUser AS u\n                \t\t       WHERE u.managerUserId = :managerUserId)\n                      \t\tOR actormember.id IN (\n                              \tSELECT actormember.id\n                              \tFROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember,\n                              \t     org.bonitasoft.engine.identity.model.SUserMembership as um\n                              \tWHERE um.userId IN (SELECT u.id \n                              \t     \t\t\t\tFROM org.bonitasoft.engine.identity.model.SUser AS u\n                              \t     \t\t\t\tWHERE u.managerUserId = :managerUserId)\n\t\t\t\t\t\t\t\tAND (\n\t\t\t\t\t\t\t\t\t(actormember.groupId = um.groupId AND actormember.roleId = -1)\n\t\t\t\t\t\t\t\t\tOR (actormember.roleId = um.roleId AND actormember.groupId = -1)\n\t\t\t\t\t\t\t\t\tOR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId))\n\t\t\t\t\t\t\t\t )\n                    \t\t)\n                \t  )\n        \t\t)\n   \t\t)\n\t</query>\t\n\t\n\t<query name=\"searchSHumanTaskInstancePendingManagedBy\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a\n\t\tWHERE a.stateId = 4\n\t\tAND a.stable = TRUE\n\t\tAND a.terminal = FALSE\n\t\tAND a.stateCategory = 'NORMAL'\n\t\tAND exists (\n    \t\tSELECT mapping.activityId\n       \t\tFROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping\n       \t\tWHERE mapping.activityId=a.id\n       \t\tAND\n          \t\t( mapping.userId IN (\n          \t\t\tSELECT u.id \n          \t\t\tFROM org.bonitasoft.engine.identity.model.SUser AS u\n          \t\t\tWHERE u.managerUserId = :managerUserId )\n           \t\tOR mapping.actorId IN (\n               \t\tSELECT actor.id\n\t\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor,\n\t\t\t\t\t\t org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n               \t\tWHERE actor.id = actormember.actorId \n               \t\tAND ( actormember.userId IN (\n               \t\t       SELECT u.id \n               \t\t       FROM org.bonitasoft.engine.identity.model.SUser AS u\n               \t\t       WHERE u.managerUserId = :managerUserId)\n                     \t\tOR actormember.id IN (\n                             \tSELECT actormember.id\n                             \tFROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember,\n                             \t     org.bonitasoft.engine.identity.model.SUserMembership as um\n                             \tWHERE um.userId IN (SELECT u.id \n                             \t     \t\t\t\tFROM org.bonitasoft.engine.identity.model.SUser AS u\n                             \t     \t\t\t\tWHERE u.managerUserId = :managerUserId)\n\t\t\t\t\t\t\tAND (\n\t\t\t\t\t\t\t\t(actormember.groupId = um.groupId AND actormember.roleId = -1)\n\t\t\t\t\t\t\t\tOR (actormember.roleId = um.roleId AND actormember.groupId = -1)\n\t\t\t\t\t\t\t\tOR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId))\n\t\t\t\t\t\t\t )\n                   \t\t)\n               \t  )\n       \t\t)\n   \t\t)\n\t</query>\t\n\t\n\t<query name=\"getNumbersOfAssignedOverdueTasksForUsers\">\n\t\tSELECT new map(a.assigneeId as userId, count(a) as numberOfTasks)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a\n\t\tWHERE a.assigneeId IN (:assigneeIds)\n\t\tAND a.stable = TRUE\n\t\tAND a.terminal = FALSE\n\t\tAND a.stateCategory = 'NORMAL'\n\t\tAND a.expectedEndDate &lt; :currentTime\n\t\tGROUP BY a.assigneeId\n        ORDER BY a.assigneeId ASC\n\t</query>\n\t\t\n\t<query name=\"getNumberOfPendingOverdueTasksForUser\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a\n\t\tWHERE a.stable = TRUE\n\t\tAND a.terminal = FALSE\n\t\tAND a.stateCategory = 'NORMAL'\n\t\tAND a.assigneeId = 0\n\t\tAND a.expectedEndDate &lt; :currentTime\n\t\tAND EXISTS (SELECT mapping.id \n\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping\n\t\t\tWHERE mapping.activityId=a.id\n\t\t\tAND ( mapping.userId = :userId\n\t\t\t\tOR mapping.actorId in (SELECT actor.id\n\t\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor,\n\t\t\t\t\t\torg.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n\t\t\t\t\tWHERE actor.id = actormember.actorId \n\t\t\t\t\tAND ( actormember.userId = :userId\n \t  \t\t\t\t\tOR actormember.id IN (\n\t\t\t\t\t\t\tSELECT actormember.id\n\t\t\t\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember,\n\t\t\t\t\t\t\torg.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\t\t\t\tWHERE um.userId = :userId \n\t\t\t\t\t\t\tAND (\n\t\t\t\t\t\t\t\t(actormember.groupId = um.groupId AND actormember.roleId = -1)\n\t\t\t\t\t\t\t\tOR (actormember.roleId = um.roleId AND actormember.groupId = -1)\n\t\t\t\t\t\t\t\tOR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId)\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t)\n     \t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t</query>\n\t\n\t<query name=\"getNumberOfSActivityInstance\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SActivityInstance AS a\n\t</query>\n\t\n\t<query name=\"searchSActivityInstance\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SActivityInstance AS a\n\t</query>\n\n\t<query name=\"getNumberOfSCallActivityInstance\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance AS a\n\t</query>\n\n\t<query name=\"searchSCallActivityInstance\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance AS a\n\t</query>\n\n\t<query name=\"getNumberOfSFlowNodeInstance\">\n\t\tSELECT COUNT(f.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS f\n\t</query>\n\t\n\t<query name=\"searchSFlowNodeInstance\">\n\t\tSELECT f\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS f\n\t</query>\n\t\n\t<query name=\"searchSCatchEventInstance\">\n\t\tSELECT e\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.event.SCatchEventInstance AS e\n\t</query>\n\t\n\t<query name=\"getNumberOfSFlowNodeInstanceSupervisedBy\">\n\t\tSELECT count(DISTINCT f.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS f,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor\n\t\tWHERE f.logicalGroup1 = supervisor.processDefId\n\t\tAND (supervisor.userId = :supervisorId\n\t\t\tOR (supervisor.id IN (\n\t\t\t\t\tSELECT supervisor.id\n\t\t\t\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor,\n\t\t\t\t\t     org.bonitasoft.engine.identity.model.SUserMembership AS um\n\t\t\t\t\tWHERE um.userId = :supervisorId\n\t\t\t\t\tAND (\n\t\t\t\t\t\t(supervisor.groupId = um.groupId AND supervisor.roleId &lt;= 0)\n\t\t\t\t\t\tOR (supervisor.roleId = um.roleId AND supervisor.groupId &lt;= 0)\n\t\t\t\t\t\tOR (supervisor.roleId = um.roleId AND supervisor.groupId = um.groupId) \n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t</query>\n\t\n\t<query name=\"searchSFlowNodeInstanceSupervisedBy\">\n\t\tSELECT DISTINCT f\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS f,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor\n\t\tWHERE f.logicalGroup1 = supervisor.processDefId\n\t\tAND (supervisor.userId = :supervisorId\n\t\t\tOR (supervisor.id IN (\n\t\t\t\t\tSELECT supervisor.id\n\t\t\t\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor,\n\t\t\t\t\t     org.bonitasoft.engine.identity.model.SUserMembership AS um\n\t\t\t\t\tWHERE um.userId = :supervisorId\n\t\t\t\t\tAND (\n\t\t\t\t\t\t(supervisor.groupId = um.groupId AND supervisor.roleId &lt;= 0)\n\t\t\t\t\t\tOR (supervisor.roleId = um.roleId AND supervisor.groupId &lt;= 0)\n\t\t\t\t\t\tOR (supervisor.roleId = um.roleId AND supervisor.groupId = um.groupId) \n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t</query>\n\n\t<query name=\"getNumberOfSSendTaskInstance\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance AS a\n\t</query>\n\n\t<query name=\"searchSSendTaskInstance\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance AS a\n\t</query>\n\n\t<query name=\"getNumberOfSLoopActivity\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance AS a\n\t</query>\n\n\t<query name=\"searchSLoopActivity\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance AS a\n\t</query>\n\n\t<query name=\"getNumberOfSMultiInstanceActivityInstance\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance AS a\n\t</query>\n\n\t<query name=\"searchSMultiInstanceActivityInstance\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance AS a\n\t</query>\n\n\t<query name=\"getNumberOfSSubProcessActivityInstanceImpl\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance AS a\n\t</query>\n\n\t<query name=\"searchSSubProcessActivityInstanceImpl\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance AS a\n\t</query>\n\n\t<query name=\"getNumberOfSAutomaticTaskInstance\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance AS a\n\t</query>\n\t\n\t<query name=\"searchSAutomaticTaskInstance\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance AS a\n\t</query>\n\t\n\t<query name=\"getNumberOfSReceiveTaskInstance\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance AS a\n\t</query>\n\t\n\t<query name=\"searchSReceiveTaskInstance\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance AS a\n\t</query>\n\t\n\t<query name=\"getNumberOfSUserTaskInstance\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a\n\t</query>\n\t\n\t<query name=\"searchSUserTaskInstance\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a\n\t</query>\t\n\t\t\n\t<query name=\"getNumberOfSManualTaskInstance\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance AS a\n\t</query>\n\t\n\t<query name=\"searchSManualTaskInstance\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance AS a\n\t</query>\n\t\n\t<query name=\"getNumberOfSAutomaticTaskInstancewithSActivityInstance\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance AS a\n\t</query>\n\t\n\t<query name=\"searchSAutomaticTaskInstancewithSActivityInstance\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance AS a\n\t</query>\n\t\n\t<query name=\"getNumberOfSReceiveTaskInstancewithSActivityInstance\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance AS a\n\t</query>\n\t\n\t<query name=\"searchSReceiveTaskInstancewithSActivityInstance\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance AS a\n\t</query>\n\t\n\t<query name=\"getNumberOfSUserTaskInstancewithSActivityInstance\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a\n\t</query>\n\t\n\t<query name=\"searchSUserTaskInstancewithSActivityInstance\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a\n\t</query>\t\n\t\t\n\t<query name=\"getNumberOfSManualTaskInstancewithSActivityInstance\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance AS a\n\t</query>\n\t\n\t<query name=\"searchSManualTaskInstancewithSActivityInstance\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance AS a\n\t</query>\n\n\t<query name=\"getNumberOfSSendTaskInstancewithSActivityInstance\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance AS a\n\t</query>\n\n\t<query name=\"searchSSendTaskInstancewithSActivityInstance\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance AS a\n\t</query>\n\n\t<query name=\"getNumberOfSMultiInstanceActivityInstancewithSActivityInstance\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance AS a\n\t</query>\n\n\t<query name=\"searchSMultiInstanceActivityInstancewithSActivityInstance\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance AS a\n\t</query>\n\n\t<query name=\"getNumberOfSSubProcessActivityInstancewithSActivityInstance\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance AS a\n\t</query>\n\n\t<query name=\"searchSSubProcessActivityInstancewithSActivityInstance\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance AS a\n\t</query>\n\n\t<query name=\"getNumberOfSLoopActivityInstancewithSActivityInstance\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance AS a\n\t</query>\n\n\t<query name=\"searchSLoopActivityInstancewithSActivityInstance\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance AS a\n\t</query>\n\n\t<query name=\"getNumberOfSHumanTaskInstancewithSActivityInstance\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a\n\t</query>\n\t\n\t<query name=\"searchSHumanTaskInstancewithSActivityInstance\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a\n\t</query>\n\n\t<query name=\"getNumberOfSCallActivityInstancewithSActivityInstance\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance AS a\n\t</query>\n\n\t<query name=\"searchSCallActivityInstancewithSActivityInstance\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance AS a\n\t</query>\n\n\t<!-- Process Instance  -->\n\t<query name=\"getNumberOfSProcessInstance\">\n\t\tSELECT COUNT(p.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p\n\t</query>\n\t\n\t<query name=\"searchSProcessInstance\">\n\t\tSELECT p\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p\n\t</query>\n\t\n\t<query name=\"getNumberOfSProcessInstancewithSUserTaskInstance\">\n\t\tSELECT COUNT(DISTINCT p.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p,\n\t\t\t org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a\n\t\tWHERE p.id = a.logicalGroup4\n\t</query>\n\t\n\t<query name=\"searchSProcessInstancewithSUserTaskInstance\">\n\t\tSELECT DISTINCT p\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p,\n\t\t\t org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a\n\t\tWHERE p.id = a.logicalGroup4\n\t</query>\n\n\t<query name=\"getNumberOfSProcessInstancewithSProcessSupervisor\">\n\t\tSELECT COUNT(DISTINCT p.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p,\n\t\t     org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE p.processDefinitionId = processsupervisor.processDefId\n\t</query>\n\n\t<query name=\"searchSProcessInstancewithSProcessSupervisor\">\n\t\tSELECT DISTINCT p\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p,\n\t\t     org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE p.processDefinitionId = processsupervisor.processDefId\n\t</query>\n\n    <query name=\"getNumberOfSProcessInstancewithSProcessSupervisorSUserTaskInstance\">\n        SELECT COUNT(DISTINCT p.id)\n        FROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p,\n             org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor,\n             org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a\n        WHERE p.processDefinitionId = processsupervisor.processDefId\n        AND p.id = a.logicalGroup4\n    </query>\n\n    <query name=\"searchSProcessInstancewithSProcessSupervisorSUserTaskInstance\">\n        SELECT DISTINCT p\n        FROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p,\n             org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor,\n             org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a\n        WHERE p.processDefinitionId = processsupervisor.processDefId\n        AND p.id = a.logicalGroup4\n    </query>\n\n\t<query name=\"getNumberOfSProcessInstanceFailed\">\n\t\tSELECT COUNT(p.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p\n\t\tWHERE (\n            p.stateId = 7 \n            OR EXISTS (\n    \t\t\tSELECT f.id \n    \t\t\tFROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS f\n    \t\t\tWHERE f.logicalGroup4 = p.id \n    \t\t\tAND f.stateId = 3\n    \t\t)\n        )\n\t</query>\n\t\n\t<query name=\"searchSProcessInstanceFailed\">\n\t\tSELECT DISTINCT p\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p\n\t\tWHERE (\n            p.stateId = 7 \n            OR EXISTS (\n    \t\t\tSELECT f.id \n    \t\t\tFROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS f\n    \t\t\tWHERE f.logicalGroup4 = p.id \n    \t\t\tAND f.stateId = 3\n    \t\t)\n        )\n\t</query>\n\t\n\t<query name=\"getProcessInstanceIdsToRecover\">\n\t\tSELECT p.id\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p\n\t\tWHERE p.lastUpdate &lt; :maxLastUpdate\n\t\tAND p.stateId IN ( 0, 3, 4, 5, 6)\n\t\tORDER BY id\n\t</query>\n\n\t<query name=\"getNumberOfChildInstancesOfProcessInstance\">\n\t\tSELECT COUNT(p.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p,\n\t\t\torg.bonitasoft.engine.core.process.instance.model.SActivityInstance AS a\n\t\tWHERE p.callerId = a.id and a.logicalGroup4 = :processInstanceId\n\t</query>\n\t\n\t<query name=\"getChildInstanceIdsOfProcessInstance\">\n\t\tSELECT p.id\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p,\n\t\t\torg.bonitasoft.engine.core.process.instance.model.SActivityInstance AS a\n\t\tWHERE p.callerId = a.id and a.logicalGroup4 = :processInstanceId\n\t</query>\n\n\t<query name=\"getChildOfActivity\">\n\t\tSELECT p\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p\n\t\tWHERE p.callerId = :activityInstanceId\n\t</query>\n    \n    <query name=\"getNumberOfSProcessInstanceFailedAndSupervisedBy\">\n        SELECT COUNT(DISTINCT p.id)\n        FROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p,\n             org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor\n        WHERE (\n            p.stateId = 7 \n            OR EXISTS (\n    \t\t\tSELECT f.id \n    \t\t\tFROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS f\n    \t\t\tWHERE f.logicalGroup4 = p.id \n    \t\t\tAND f.stateId = 3\n    \t\t)\n        )\n        AND p.processDefinitionId = supervisor.processDefId\n        AND (supervisor.userId = :userId\n            OR (supervisor.id IN (\n                    SELECT supervisor.id\n                    FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor,\n                         org.bonitasoft.engine.identity.model.SUserMembership AS um\n                    WHERE um.userId = :userId\n                    AND (\n                        (supervisor.groupId = um.groupId AND supervisor.roleId &lt;= 0)\n                        OR (supervisor.roleId = um.roleId AND supervisor.groupId &lt;= 0)\n                        OR (supervisor.roleId = um.roleId AND supervisor.groupId = um.groupId) \n                    )\n                )\n            )\n        )\n    </query>\n\n    <query name=\"searchSProcessInstanceFailedAndSupervisedBy\">\n        SELECT DISTINCT p\n        FROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p,\n             org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor\n        WHERE (\n            p.stateId = 7 \n            OR EXISTS (\n    \t\t\tSELECT f.id \n    \t\t\tFROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS f\n    \t\t\tWHERE f.logicalGroup4 = p.id \n    \t\t\tAND f.stateId = 3\n    \t\t)\n        )\n        AND p.processDefinitionId = supervisor.processDefId\n        AND (supervisor.userId = :userId\n            OR (supervisor.id IN (\n                    SELECT supervisor.id\n                    FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor,\n                         org.bonitasoft.engine.identity.model.SUserMembership AS um\n                    WHERE um.userId = :userId\n                    AND (\n                        (supervisor.groupId = um.groupId AND supervisor.roleId &lt;= 0)\n                        OR (supervisor.roleId = um.roleId AND supervisor.groupId &lt;= 0)\n                        OR (supervisor.roleId = um.roleId AND supervisor.groupId = um.groupId) \n                    )\n                )\n            )\n        )\n    </query>\n\t\n\t<query name=\"getNumberOfSProcessInstanceSupervisedBy\">\n\t\tSELECT COUNT(DISTINCT p.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor\n\t\tWHERE p.stateId != 6\n\t\tAND p.processDefinitionId = supervisor.processDefId\n\t\tAND (supervisor.userId = :userId\n\t\t\tOR (supervisor.id IN (\n\t\t\t\t\tSELECT supervisor.id\n\t\t\t\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor,\n\t\t\t\t\t\t org.bonitasoft.engine.identity.model.SUserMembership AS um\n\t\t\t\t\tWHERE um.userId = :userId\n\t\t\t\t\tAND (\n\t\t\t\t\t\t(supervisor.groupId = um.groupId AND supervisor.roleId &lt;= 0)\n\t\t\t\t\t\tOR (supervisor.roleId = um.roleId AND supervisor.groupId &lt;= 0)\n\t\t\t\t\t\tOR (supervisor.roleId = um.roleId AND supervisor.groupId = um.groupId) \n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t</query>\n\n\t<query name=\"searchSProcessInstanceSupervisedBy\">\n\t\tSELECT DISTINCT p\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p,\n\t\t\t org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor\n\t\tWHERE p.stateId != 6\n\t\tAND p.processDefinitionId = supervisor.processDefId\n\t\tAND (supervisor.userId = :userId\n\t\t\tOR (supervisor.id IN (\n\t\t\t\t\tSELECT supervisor.id\n\t\t\t\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor,\n\t\t\t\t\t\t org.bonitasoft.engine.identity.model.SUserMembership AS um\n\t\t\t\t\tWHERE um.userId = :userId\n\t\t\t\t\tAND (\n\t\t\t\t\t\t(supervisor.groupId = um.groupId AND supervisor.roleId &lt;= 0)\n\t\t\t\t\t\tOR (supervisor.roleId = um.roleId AND supervisor.groupId &lt;= 0)\n\t\t\t\t\t\tOR (supervisor.roleId = um.roleId AND supervisor.groupId = um.groupId) \n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t</query>\n\n\t<sql-query name=\"getNumberOfSProcessInstanceInvolvingUser\">\n\t\t<return-scalar column=\"count\" type=\"long\"/>\n         SELECT COUNT(1) as count\n            FROM process_instance p\n            WHERE (\n                ID IN (\n                    SELECT DISTINCT afi.logicalGroup2\n                    FROM arch_flownode_instance afi\n                    WHERE afi.kind IN ('user', 'manual')\n                    AND (afi.executedBy = :userId OR afi.executedBySubstitute = :userId)\n                    AND afi.stateId = 2)\n                OR startedBy = :userId\n            )\n\t</sql-query>\n\n\n\t<sql-query name=\"searchSProcessInstanceInvolvingUser\">\n\t\t<return alias=\"p\" class=\"org.bonitasoft.engine.core.process.instance.model.SProcessInstance\" />\n         SELECT *\n            FROM process_instance p\n            WHERE (\n                ID IN (\n                    SELECT DISTINCT afi.logicalGroup2\n                    FROM arch_flownode_instance afi\n                    WHERE afi.kind IN ('user', 'manual')\n                    AND (afi.executedBy = :userId OR afi.executedBySubstitute = :userId)\n                    AND afi.stateId = 2)\n                OR startedBy = :userId\n            )\n\t</sql-query>\n\n\t<query name=\"getNumberOfSProcessInstanceInvolvingUsersManagedBy\">\n\t\tSELECT COUNT(DISTINCT p.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p,\n\t\t\t org.bonitasoft.engine.identity.model.SUser AS u\n\t\tWHERE (p.id IN \n\t\t\t\t(SELECT at.logicalGroup2\n\t\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance AS at\n\t\t\t\tWHERE (at.executedBy = u.id OR at.executedBySubstitute = u.id)\n\t\t\t\t\tAND u.managerUserId = :managerUserId\n\t\t\t\t)\n\t\t\tOR (p.startedBy = u.id AND u.managerUserId = :managerUserId)\n\t\t\t)\n\t</query>\n\n\t<query name=\"searchSProcessInstanceInvolvingUsersManagedBy\">\n\t\tSELECT DISTINCT p\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p,\n\t\t\t org.bonitasoft.engine.identity.model.SUser AS u\n\t\tWHERE (p.id IN \n\t\t\t\t(SELECT at.logicalGroup2\n\t\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance AS at\n\t\t\t\tWHERE (at.executedBy = u.id OR at.executedBySubstitute = u.id)\n\t\t\t\t\tAND u.managerUserId = :managerUserId\n\t\t\t\t)\n\t\t\tOR (p.startedBy = u.id AND u.managerUserId = :managerUserId)\n\t\t\t)\n\t</query>\n\n\n\n\t<query name=\"isTaskPendingForUser\">\n\t\tSELECT COUNT(mapping.id)\n\t\tFROM  org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping\n\t\tWHERE\n\t\t\tmapping.activityId = :humanTaskInstanceId\n\t\tAND (\n\t\t\tmapping.userId = :userId\n\t\t\tOR EXISTS (\n\t\t\t\tSELECT actormember.id\n\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n\t\t\t\tWHERE\n\t\t\t\t\tmapping.actorId = actormember.actorId\n\t\t\t\t\tAND actormember.userId = :userId\n\t\t\t)\n\t\t\tOR EXISTS (\n\t\t\t\tSELECT actormember.id\n\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember,\n\t\t\t\torg.bonitasoft.engine.identity.model.SUserMembership AS user_membership\n\t\t\t\tWHERE\n\t\t\t\t\tmapping.actorId = actormember.actorId\n\t\t\t\t\tAND  (\n\t\t\t\t\t\t\t(user_membership.userId = :userId AND actormember.roleId = -1 AND actormember.groupId = user_membership.groupId)\n\t\t\t\t\t\t\tOR (actormember.groupId = -1 AND actormember.roleId = user_membership.roleId)\n\t\t\t\t\t\t\tOR (actormember.roleId = user_membership.roleId AND actormember.groupId = user_membership.groupId)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t</query>\n\n\t<query name=\"getPossibleUserIdsOfPendingTasks\">\n        SELECT user.id\n        FROM org.bonitasoft.engine.identity.model.SUser AS user\n        WHERE\n          user.id IN (\n              SELECT mapping.userId\n              FROM  org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping\n              WHERE mapping.activityId = :humanTaskInstanceId\n          )\n          OR user.id IN (\n              SELECT actormember.userId\n              FROM    org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember,\n                      org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping,\n                      org.bonitasoft.engine.actor.mapping.model.SActor AS actor\n              WHERE \n                  mapping.activityId = :humanTaskInstanceId\n                  AND mapping.actorId = actor.id \n                  AND actor.id = actormember.actorId\n         )\n         OR user.id IN (\n              SELECT user_membership.userId\n              FROM    org.bonitasoft.engine.identity.model.SUserMembership AS user_membership,\n                      org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping,\n                      org.bonitasoft.engine.actor.mapping.model.SActor AS actor,\n                      org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n              WHERE\n                  mapping.activityId = :humanTaskInstanceId\n                  AND mapping.actorId = actor.id\n                  AND actor.id = actormember.actorId\n                  AND (\n                    (actormember.roleId = -1 AND actormember.groupId = user_membership.groupId)\n                    OR (actormember.groupId = -1 AND actormember.roleId = user_membership.roleId)\n                    OR (actormember.roleId = user_membership.roleId AND actormember.groupId = user_membership.groupId) \n                  )                  \n          )\n        GROUP BY user.id\n        ORDER BY MIN(user.userName)\n    </query>\n    \n\t<query name=\"getNumberOfSUserWhoCanStartPendingTask\">\n\t   SELECT count(DISTINCT user.id)\n\t   FROM org.bonitasoft.engine.identity.model.SUser AS user,\n\t        org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping\n\t   WHERE mapping.activityId = :humanTaskInstanceId\n\t   AND (\n\t       user.id = mapping.userId\n\t       OR user.id IN (\n\t           SELECT actormember.userId\n\t           FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember,\n\t                org.bonitasoft.engine.actor.mapping.model.SActor AS actor\n\t           WHERE mapping.actorId = actor.id \n\t           AND actor.id = actormember.actorId\n\t       )\n\t       OR user.id IN (\n\t           SELECT user_membership.userId\n\t           FROM org.bonitasoft.engine.identity.model.SUserMembership AS user_membership,\n\t           \t    org.bonitasoft.engine.actor.mapping.model.SActor AS actor,\n\t                org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n\t           WHERE mapping.actorId = actor.id\n\t           AND actor.id = actormember.actorId\n\t           AND (\n\t               (actormember.roleId = -1 AND actormember.groupId = user_membership.groupId)\n\t               OR (actormember.groupId = -1 AND actormember.roleId = user_membership.roleId)\n\t               OR (actormember.roleId = user_membership.roleId AND actormember.groupId = user_membership.groupId) \n\t           )                  \n\t       )\n\t   )\n\t</query>\n\n\t<query name=\"searchSUserWhoCanStartPendingTask\">\n\t   SELECT DISTINCT user\n\t   FROM org.bonitasoft.engine.identity.model.SUser AS user,\n\t        org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping\n\t   WHERE mapping.activityId = :humanTaskInstanceId\n\t   AND (\n\t       user.id = mapping.userId\n\t       OR user.id IN (\n\t           SELECT actormember.userId\n\t           FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember,\n\t                org.bonitasoft.engine.actor.mapping.model.SActor AS actor\n\t           WHERE mapping.actorId = actor.id \n\t           AND actor.id = actormember.actorId\n\t       )\n\t       OR user.id IN (\n\t           SELECT user_membership.userId\n\t           FROM org.bonitasoft.engine.identity.model.SUserMembership AS user_membership,\n\t           \t    org.bonitasoft.engine.actor.mapping.model.SActor AS actor,\n\t                org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n\t           WHERE mapping.actorId = actor.id\n\t           AND actor.id = actormember.actorId\n\t           AND (\n\t               (actormember.roleId = -1 AND actormember.groupId = user_membership.groupId)\n\t               OR (actormember.groupId = -1 AND actormember.roleId = user_membership.roleId)\n\t               OR (actormember.roleId = user_membership.roleId AND actormember.groupId = user_membership.groupId) \n\t           )                  \n\t       )\n\t   )\n\t</query>\n\t\n\t\n\t<query name=\"getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcessFor\">\n        SELECT COUNT(DISTINCT a.id)\n        FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a,\n        \t org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p\n        WHERE p.processDefinitionId = :rootProcessDefinitionId\n        AND p.id = a.logicalGroup2\n        AND a.stable = TRUE\n\t\tAND a.stateExecuting = FALSE\n\t\tAND a.stateCategory = 'NORMAL'\n\t\tAND a.terminal = FALSE\n\t\tAND ( \n\t\t\ta.assigneeId = :userId\n\t\t\tOR ( \n\t\t\t\ta.assigneeId = 0\n\t\t\t\tAND EXISTS (\n\t\t\t\t\tSELECT mapping.id\n\t\t\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping\n\t\t\t\t\tWHERE mapping.activityId = a.id\n\t\t\t\t\tAND ( \n\t\t\t\t\t\tmapping.userId = :userId\n\t\t\t\t\t\tOR EXISTS (\n\t\t\t\t\t\t\tSELECT actor.id\n\t\t\t\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor,\n\t\t\t\t\t\t\t\t org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n\t\t\t\t\t\t\tWHERE mapping.actorId = actor.id\n\t\t\t\t\t\t\tAND  actor.id = actormember.actorId\n\t\t\t\t\t\t\tAND ( \n\t\t\t\t\t\t\t\tactormember.userId = :userId\n\t\t \t  \t\t\t\t\tOR EXISTS (\n\t\t\t\t\t\t\t\t\tSELECT um.id\n\t\t\t\t\t\t\t\t\tFROM org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\t\t\t\t\t\tWHERE um.userId = :userId\n\t\t\t\t\t\t\t\t\tAND (\n\t\t\t\t\t\t\t\t\t\t(actormember.groupId = um.groupId AND actormember.roleId = -1)\n\t\t\t\t\t\t\t\t\t\tOR (actormember.roleId = um.roleId AND actormember.groupId = -1)\n\t\t\t\t\t\t\t\t\t\tOR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId)\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t)\n\t\t     \t\t\t\t)\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n    </query>\n\t\n\t<query name=\"searchSHumanTaskInstanceAssignedAndPendingByRootProcessFor\">\n        SELECT DISTINCT(a)\n        FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a,\n        \t org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p\n        WHERE p.processDefinitionId = :rootProcessDefinitionId\n        AND p.id = a.logicalGroup2\n        AND a.stable = TRUE\n\t\tAND a.stateExecuting = FALSE\n\t\tAND a.stateCategory = 'NORMAL'\n\t\tAND a.terminal = FALSE\n\t\tAND ( \n\t\t\ta.assigneeId = :userId\n\t\t\tOR ( \n\t\t\t\ta.assigneeId = 0\n\t\t\t\tAND EXISTS (\n\t\t\t\t\tSELECT mapping.id\n\t\t\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping\n\t\t\t\t\tWHERE mapping.activityId = a.id\n\t\t\t\t\tAND ( \n\t\t\t\t\t\tmapping.userId = :userId\n\t\t\t\t\t\tOR EXISTS (\n\t\t\t\t\t\t\tSELECT actor.id\n\t\t\t\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor,\n\t\t\t\t\t\t\t\t org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n\t\t\t\t\t\t\tWHERE mapping.actorId = actor.id\n\t\t\t\t\t\t\tAND  actor.id = actormember.actorId\n\t\t\t\t\t\t\tAND ( \n\t\t\t\t\t\t\t\tactormember.userId = :userId\n\t\t \t  \t\t\t\t\tOR EXISTS (\n\t\t\t\t\t\t\t\t\tSELECT um.id\n\t\t\t\t\t\t\t\t\tFROM org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\t\t\t\t\t\tWHERE um.userId = :userId\n\t\t\t\t\t\t\t\t\tAND (\n\t\t\t\t\t\t\t\t\t\t(actormember.groupId = um.groupId AND actormember.roleId = -1)\n\t\t\t\t\t\t\t\t\t\tOR (actormember.roleId = um.roleId AND actormember.groupId = -1)\n\t\t\t\t\t\t\t\t\t\tOR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId)\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t)\n\t\t     \t\t\t\t)\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n    </query>\n \n \t<query name=\"getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcess\">\n        SELECT COUNT(DISTINCT a.id)\n        FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a,\n        \t org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p,\n        \t org.bonitasoft.engine.identity.model.SUser as user\n        WHERE p.processDefinitionId = :rootProcessDefinitionId\n        AND p.id = a.logicalGroup2\n        AND a.stable = TRUE\n\t\tAND a.stateExecuting = FALSE\n\t\tAND a.stateCategory = 'NORMAL'\n\t\tAND a.terminal = FALSE\n\t\tAND ( \n\t\t\ta.assigneeId = user.id\n\t\t\tOR ( \n\t\t\t\ta.assigneeId = 0\n\t\t\t\tAND EXISTS (\n\t\t\t\t\tSELECT mapping.id\n\t\t\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping\n\t\t\t\t\tWHERE mapping.activityId = a.id\n\t\t\t\t\tAND ( \n\t\t\t\t\t\tmapping.userId = user.id\n\t\t\t\t\t\tOR EXISTS (\n\t\t\t\t\t\t\tSELECT actor.id\n\t\t\t\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor,\n\t\t\t\t\t\t\t\t org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n\t\t\t\t\t\t\tWHERE mapping.actorId = actor.id\n\t\t\t\t\t\t\tAND  actor.id = actormember.actorId\n\t\t\t\t\t\t\tAND ( \n\t\t\t\t\t\t\t\tactormember.userId = user.id\n\t\t\t \t  \t\t\t\tOR EXISTS (\n\t\t\t\t\t\t\t\t\tSELECT um.id\n\t\t\t\t\t\t\t\t\tFROM org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\t\t\t\t\t\tWHERE um.userId = user.id\n\t\t\t\t\t\t\t\t\tAND (\n\t\t\t\t\t\t\t\t\t\t(actormember.groupId = um.groupId AND actormember.roleId = -1)\n\t\t\t\t\t\t\t\t\t\tOR (actormember.roleId = um.roleId AND actormember.groupId = -1)\n\t\t\t\t\t\t\t\t\t\tOR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId)\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t)\n\t\t\t     \t\t\t)\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n    </query>\n\t\n\t<query name=\"searchSHumanTaskInstanceAssignedAndPendingByRootProcess\">\n        SELECT DISTINCT(a)\n        FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a,\n        \t org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p,\n        \t org.bonitasoft.engine.identity.model.SUser as user\n        WHERE p.processDefinitionId = :rootProcessDefinitionId\n        AND p.id = a.logicalGroup2\n        AND a.stable = TRUE\n\t\tAND a.stateExecuting = FALSE\n\t\tAND a.stateCategory = 'NORMAL'\n\t\tAND a.terminal = FALSE\n\t\tAND ( \n\t\t\ta.assigneeId = user.id\n\t\t\tOR ( \n\t\t\t\ta.assigneeId = 0\n\t\t\t\tAND EXISTS (\n\t\t\t\t\tSELECT mapping.id\n\t\t\t\t\tFROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping\n\t\t\t\t\tWHERE mapping.activityId = a.id\n\t\t\t\t\tAND ( \n\t\t\t\t\t\tmapping.userId = user.id\n\t\t\t\t\t\tOR EXISTS (\n\t\t\t\t\t\t\tSELECT actor.id\n\t\t\t\t\t\t\tFROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor,\n\t\t\t\t\t\t\t\t org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember\n\t\t\t\t\t\t\tWHERE mapping.actorId = actor.id\n\t\t\t\t\t\t\tAND  actor.id = actormember.actorId\n\t\t\t\t\t\t\tAND ( \n\t\t\t\t\t\t\t\tactormember.userId = user.id\n\t\t\t \t  \t\t\t\tOR EXISTS (\n\t\t\t\t\t\t\t\t\tSELECT um.id\n\t\t\t\t\t\t\t\t\tFROM org.bonitasoft.engine.identity.model.SUserMembership as um\n\t\t\t\t\t\t\t\t\tWHERE um.userId = user.id\n\t\t\t\t\t\t\t\t\tAND (\n\t\t\t\t\t\t\t\t\t\t(actormember.groupId = um.groupId AND actormember.roleId = -1)\n\t\t\t\t\t\t\t\t\t\tOR (actormember.roleId = um.roleId AND actormember.groupId = -1)\n\t\t\t\t\t\t\t\t\t\tOR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId)\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t)\n\t\t\t     \t\t\t)\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n    </query>\n\n\t<query name=\"getNumberOfSHumanTaskInstanceAssignedAndPending\">\n\t\tSELECT COUNT(a.id)\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a\n\t\tWHERE a.stateExecuting = FALSE\n\t\tAND a.stateCategory = 'NORMAL'\n\t\tAND a.stateId = 4\n\t</query>\n\n\t<query name=\"searchSHumanTaskInstanceAssignedAndPending\">\n\t\tSELECT a\n\t\tFROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a\n\t\tWHERE a.stateExecuting = FALSE\n\t\tAND a.stateCategory = 'NORMAL'\n\t\tAND a.stateId = 4\n\t</query>\n    \n    <query name=\"countProcessInstancesOfProcessDefinition\">\n        SELECT COUNT(p.id)\n        FROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p\n        WHERE p.processDefinitionId = :processDefinitionId\n    </query>\n\n    <query name=\"getSRefBusinessDataInstance\">\n        SELECT ref\n        FROM org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance AS ref\n        WHERE ref.name = :name\n        AND ref.processInstanceId = :processInstanceId\n    </query>\n\n    <query name=\"getSRefBusinessDataInstancesOfProcess\">\n        SELECT ref\n        FROM org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance AS ref\n        WHERE ref.processInstanceId = :processInstanceId\n        ORDER BY ref.id\n    </query>\n\n    <query name=\"getSFlowNodeRefBusinessDataInstance\">\n        SELECT ref\n        FROM org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance AS ref\n        WHERE ref.name = :name\n        AND ref.flowNodeInstanceId = :flowNodeInstanceId\n    </query>\n\n    <query name=\"getNumberOfDataOfMultiRefBusinessData\">\n        SELECT size(ref.dataIds)\n        FROM org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance AS ref\n        WHERE ref.name = :name\n        AND ref.processInstanceId = :processInstanceId\n    </query>\n\n    <!-- Query to update the counters of a multi instance activity: increment the number of completed instances\n    and decrement the number of active instances.\n    Note: condition on `numberOfActiveInstances` is a safeguard to prevent the counter from being negative when\n    decrementing it.-->\n    <query name=\"updateMultiInstanceCompletedCounters\">\n        UPDATE org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance\n        SET numberOfActiveInstances = numberOfActiveInstances - :number,\n            numberOfCompletedInstances = numberOfCompletedInstances + :number,\n            lastUpdateDate = :lastUpdateDate\n        WHERE id = :id\n        AND numberOfActiveInstances >= :number\n    </query>\n\n    <!-- Query to update the counters of a multi instance activity: increment the number of terminated instances\n    and decrement the number of active instances.\n    Note: condition on `numberOfActiveInstances` is a safeguard to prevent the counter from being negative when\n    decrementing it.-->\n    <query name=\"updateMultiInstanceTerminatedCounters\">\n        UPDATE org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance\n        SET numberOfActiveInstances = numberOfActiveInstances - :number,\n            numberOfTerminatedInstances = numberOfTerminatedInstances + :number,\n            lastUpdateDate = :lastUpdateDate\n        WHERE id = :id\n        AND numberOfActiveInstances >= :number\n    </query>\n\n    <!-- Query to update the counters of a multi instance activity: increment the number of active instances.\n    Note: there is no safeguard because `numberOfActiveInstances` is only incremented, never decremented -->\n    <query name=\"updateMultiInstanceActiveCounters\">\n        UPDATE org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance\n        SET numberOfActiveInstances = numberOfActiveInstances + :number,\n            lastUpdateDate = :lastUpdateDate\n        WHERE id = :id\n    </query>\n\n</hibernate-mapping>\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/connector/ConnectorResultTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.connector;\n\nimport static org.junit.Assert.assertTrue;\n\nimport org.junit.Test;\n\npublic class ConnectorResultTest {\n\n    @Test\n    public void test() {\n        final ConnectorResult result = new ConnectorResult(null, null, 100);\n        assertTrue(result.getResult().isEmpty());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/connector/impl/ConnectorExecutionTimeLoggerTest.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.connector.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.Collections;\nimport java.util.HashMap;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstance;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.SystemOutRule;\n\npublic class ConnectorExecutionTimeLoggerTest {\n\n    @Rule\n    public SystemOutRule systemOutRule = new SystemOutRule().enableLog();\n    private static final long MAX_DURATION_IN_MILLIS_THRESHOLD = 1000L;\n    private ConnectorExecutionTimeLogger connectorExecutionTimeLogger = new ConnectorExecutionTimeLogger(\n            MAX_DURATION_IN_MILLIS_THRESHOLD);\n\n    @Test\n    public void should_log_a_warning_when_connector_takes_more_time_than_the_defined_threshold() {\n        systemOutRule.clearLog();\n        HashMap<String, Object> inputParameters = new HashMap<>();\n        inputParameters.put(\"serviceUrl\", \"http://some.external.service/1234?call=true\");\n        inputParameters.put(\"groovyScript\", \"import something\\n\" +\n                \"\\n\" +\n                \"Thread.sleep(10000)\");\n        SConnectorInstance sConnectorInstance = new SConnectorInstance(\"myConnector\", 5555L,\n                SConnectorInstance.FLOWNODE_TYPE, \"theConnectorId\", \"1.0.0\", ConnectorEvent.ON_ENTER);\n        sConnectorInstance.setId(333L);\n\n        connectorExecutionTimeLogger.log(123L,\n                sConnectorInstance,\n                new ConnectorServiceImplTest.MyTestConnector(),\n                inputParameters,\n                1500);\n\n        assertThat(systemOutRule.getLog()).contains(\n                \"Connector myConnector with id 333 with class org.bonitasoft.engine.core.connector.impl.ConnectorServiceImplTest$MyTestConnector\"\n                        +\n                        \" of process definition 123 on element flowNode with id 5555 took 1500 ms.\");\n        assertThat(systemOutRule.getLog()).contains(\n                \"Input parameters of the connector with id 333: {groovyScript: [import something  Thread.sleep(10000)], serviceUrl: [http://some.external.service/1234?call=true]}\");\n    }\n\n    @Test\n    public void should_not_log_when_connector_takes_less_than_the_defined_threashold() {\n        systemOutRule.clearLog();\n        SConnectorInstance sConnectorInstance = new SConnectorInstance(\"myConnector\", 5555L,\n                SConnectorInstance.FLOWNODE_TYPE, \"theConnectorId\", \"1.0.0\", ConnectorEvent.ON_ENTER);\n\n        connectorExecutionTimeLogger.log(123L,\n                sConnectorInstance,\n                new ConnectorServiceImplTest.MyTestConnector(),\n                Collections.emptyMap(),\n                100);\n\n        assertThat(systemOutRule.getLog()).isEmpty();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/connector/impl/ConnectorInstanceServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.connector.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.*;\nimport static org.mockito.ArgumentMatchers.nullable;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstanceWithFailureInfo;\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class ConnectorInstanceServiceImplTest {\n\n    private static final String STACK_TRACE = \"stackTrace\";\n\n    private static final String EXCEPTION_MESSAGE = \"exceptionMessage\";\n\n    private final String message = \"An exception occurred during execution.\";\n\n    @Mock\n    private ReadPersistenceService readPersistenceService;\n\n    @Mock\n    private Recorder recorder;\n\n    @Mock\n    private EventService eventService;\n\n    @Mock\n    private QueriableLoggerService queriableLoggerService;\n\n    @Mock\n    private SConnectorInstanceWithFailureInfo connectorInstanceWithFailureMock;\n\n    @InjectMocks\n    private ConnectorInstanceServiceImpl connectorInstanceServiceImpl;\n\n    @Test\n    public void should_persist_stack_trace_on_failure() throws Exception {\n        SConnectorInstanceWithFailureInfo connectorInstanceWithFailure = new SConnectorInstanceWithFailureInfo();\n        connectorInstanceServiceImpl.setConnectorInstanceFailureException(connectorInstanceWithFailure,\n                new Exception(\"Root cause\"));\n        verify(recorder).recordUpdate(argThat(r -> r.getFields().get(\"exceptionMessage\").equals(\"Root cause\") &&\n                ((String) r.getFields().get(\"stackTrace\")).startsWith(\"java.lang.Exception: Root cause\"\n                        + System.lineSeparator() +\n                        \"\\tat org.bonitasoft.engine.core.connector.impl.ConnectorInstanceServiceImplTest.should_persist_stack_trace_on_failure(ConnectorInstanceServiceImplTest.java:\")),\n                any());\n    }\n\n    @Test\n    public void should_not_fail_when_persisting_stack_trace_on_failure() throws Exception {\n        SConnectorInstanceWithFailureInfo connectorInstanceWithFailure = new SConnectorInstanceWithFailureInfo();\n\n        Exception mockedException = mock(Exception.class);\n        when(mockedException.getMessage()).thenReturn(\"the message of the exception\");\n        when(mockedException.toString()).thenThrow(new AbstractMethodError());\n        connectorInstanceServiceImpl.setConnectorInstanceFailureException(connectorInstanceWithFailure,\n                new Exception(\"wrapping exception\", mockedException));\n\n        String stacktraceStart = \"Unable to retrieve stacktrace, exceptions were:\\n\" +\n                \" * java.lang.Exception: wrapping exception\\n\" +\n                \" * \";\n        String stacktraceEnd = \": the message of the exception\\n\";\n\n        verify(recorder).recordUpdate(\n                argThat(r -> r.getFields().get(\"exceptionMessage\").equals(\"the message of the exception\") &&\n                        ((String) r.getFields().get(\"stackTrace\")).startsWith(stacktraceStart) &&\n                        ((String) r.getFields().get(\"stackTrace\")).endsWith(stacktraceEnd)),\n                any());\n    }\n\n    @Test\n    public void setConnectorInstanceFailureExceptionWithNullMessage() throws Exception {\n        final Exception exception = new Exception();\n\n        //call method\n        connectorInstanceServiceImpl.setConnectorInstanceFailureException(connectorInstanceWithFailureMock, exception);\n\n        //verify\n        final ArgumentCaptor<UpdateRecord> updateRecordCaptor = ArgumentCaptor.forClass(UpdateRecord.class);\n        verify(recorder, times(1)).recordUpdate(updateRecordCaptor.capture(), nullable(String.class));\n        final UpdateRecord updateRecord = updateRecordCaptor.getValue();\n        final String stackTrace = (String) updateRecord.getFields().get(STACK_TRACE);\n\n        assertNull(updateRecord.getFields().get(EXCEPTION_MESSAGE));\n        assertTrue(stackTrace.startsWith(Exception.class.getName()));\n        assertTrue(stackTrace.contains(getClass().getName() + \".setConnectorInstanceFailureExceptionWithNullMessage\"));\n    }\n\n    @Test\n    public void setConnectorInstanceFailureExceptionWithCausedBy() throws Exception {\n        String causedByMessage = \"This is the caused by message.\";\n        final Exception causedByException = new Exception(causedByMessage);\n        final Exception exception = new Exception(message, causedByException);\n\n        //call method\n        connectorInstanceServiceImpl.setConnectorInstanceFailureException(connectorInstanceWithFailureMock, exception);\n\n        //verify\n        final ArgumentCaptor<UpdateRecord> updateRecordCaptor = ArgumentCaptor.forClass(UpdateRecord.class);\n        verify(recorder, times(1)).recordUpdate(updateRecordCaptor.capture(), nullable(String.class));\n        final UpdateRecord updateRecord = updateRecordCaptor.getValue();\n        final String stackTrace = (String) updateRecord.getFields().get(STACK_TRACE);\n\n        assertEquals(causedByMessage, updateRecord.getFields().get(EXCEPTION_MESSAGE));\n        assertTrue(stackTrace.startsWith(Exception.class.getName() + \": \" + message));\n        assertTrue(stackTrace.contains(getClass().getName() + \".setConnectorInstanceFailureExceptionWithCausedBy\"));\n        assertTrue(stackTrace.contains(\"Caused by: \" + Exception.class.getName() + \": \" + causedByMessage));\n    }\n\n    @Test\n    public void cleanConnectorInstanceFailureException() throws Exception {\n        //call method\n        connectorInstanceServiceImpl.setConnectorInstanceFailureException(connectorInstanceWithFailureMock, null);\n\n        //verify\n        final ArgumentCaptor<UpdateRecord> updateRecordCaptor = ArgumentCaptor.forClass(UpdateRecord.class);\n        verify(recorder, times(1)).recordUpdate(updateRecordCaptor.capture(), nullable(String.class));\n        final UpdateRecord updateRecord = updateRecordCaptor.getValue();\n        final String stackTrace = (String) updateRecord.getFields().get(STACK_TRACE);\n\n        assertNull(updateRecord.getFields().get(EXCEPTION_MESSAGE));\n        assertNull(stackTrace);\n    }\n\n    @Test\n    public void setConnectorInstanceFailureExceptionMessageGreaterThen255() throws Exception {\n        final String messageToRepeat = \"This is a message repeated many times. \";\n        final StringBuilder stb = new StringBuilder();\n        int currentLength = 0;\n        while (currentLength < 256) {\n            stb.append(messageToRepeat);\n            currentLength += messageToRepeat.length();\n        }\n        final String longMessage = stb.toString();\n\n        final Exception exception = new Exception(longMessage);\n\n        //call method\n        connectorInstanceServiceImpl.setConnectorInstanceFailureException(connectorInstanceWithFailureMock, exception);\n\n        //verify\n        final ArgumentCaptor<UpdateRecord> updateRecordCaptor = ArgumentCaptor.forClass(UpdateRecord.class);\n        verify(recorder, times(1)).recordUpdate(updateRecordCaptor.capture(), nullable(String.class));\n        final UpdateRecord updateRecord = updateRecordCaptor.getValue();\n        final String stackTrace = (String) updateRecord.getFields().get(STACK_TRACE);\n\n        final String retrievedMessage = (String) updateRecord.getFields().get(EXCEPTION_MESSAGE);\n        assertEquals(longMessage.substring(0, 255), retrievedMessage);\n        assertTrue(stackTrace.startsWith(Exception.class.getName() + \": \" + longMessage));\n        assertTrue(stackTrace\n                .contains(getClass().getName() + \".setConnectorInstanceFailureExceptionMessageGreaterThen255\"));\n    }\n\n    @Test\n    public void getConnectorInstanceWithFailureInfo_should_return_the_result_of_select_list() throws Exception {\n        //given\n        Map<String, Object> parameters = new HashMap<>();\n        parameters.put(\"containerId\", 1L);\n        parameters.put(\"containerType\", \"flowNode\");\n        parameters.put(\"state\", \"failed\");\n\n        List<SConnectorInstanceWithFailureInfo> connectors = Arrays.asList(\n                mock(SConnectorInstanceWithFailureInfo.class),\n                mock(SConnectorInstanceWithFailureInfo.class));\n        given(\n                readPersistenceService.selectList(new SelectListDescriptor<SConnectorInstanceWithFailureInfo>(\n                        \"getConnectorInstancesWithFailureInfoInState\",\n                        parameters,\n                        SConnectorInstanceWithFailureInfo.class,\n                        new QueryOptions(0, 100, SConnectorInstanceWithFailureInfo.class, \"id\", OrderByType.ASC))))\n                .willReturn(connectors);\n\n        //when\n        List<SConnectorInstanceWithFailureInfo> retrievedConnectors = connectorInstanceServiceImpl\n                .getConnectorInstancesWithFailureInfo(1L, \"flowNode\",\n                        \"failed\", 0, 100);\n\n        //then\n        assertThat(retrievedConnectors).isEqualTo(connectors);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/connector/impl/ConnectorServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.connector.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyInt;\nimport static org.mockito.ArgumentMatchers.anyList;\nimport static org.mockito.ArgumentMatchers.anyLong;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.ArgumentMatchers.notNull;\nimport static org.mockito.ArgumentMatchers.nullable;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.doThrow;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.CompletableFuture;\n\nimport org.bonitasoft.engine.bpm.bar.BarResource;\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.cache.CacheService;\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.connector.AbstractConnector;\nimport org.bonitasoft.engine.connector.Connector;\nimport org.bonitasoft.engine.connector.ConnectorException;\nimport org.bonitasoft.engine.connector.ConnectorExecutionResult;\nimport org.bonitasoft.engine.connector.ConnectorExecutor;\nimport org.bonitasoft.engine.connector.ConnectorValidationException;\nimport org.bonitasoft.engine.connector.SConnector;\nimport org.bonitasoft.engine.core.connector.ConnectorResult;\nimport org.bonitasoft.engine.core.connector.exception.SConnectorException;\nimport org.bonitasoft.engine.core.connector.exception.SInvalidConnectorImplementationException;\nimport org.bonitasoft.engine.core.connector.parser.SConnectorImplementationDescriptor;\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.operation.OperationService;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SProcessDefinitionImpl;\nimport org.bonitasoft.engine.core.process.instance.model.SConnectorInstance;\nimport org.bonitasoft.engine.dependency.DependencyService;\nimport org.bonitasoft.engine.dependency.model.SDependency;\nimport org.bonitasoft.engine.dependency.model.ScopeType;\nimport org.bonitasoft.engine.io.IOUtil;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.resources.BARResourceType;\nimport org.bonitasoft.engine.resources.ProcessResourcesService;\nimport org.bonitasoft.engine.resources.SBARResource;\nimport org.bonitasoft.engine.tracking.TimeTracker;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.SystemOutRule;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Baptiste Mesta\n */\n@SuppressWarnings(\"javadoc\")\n@RunWith(MockitoJUnitRunner.class)\npublic class ConnectorServiceImplTest {\n\n    private static final long PROCESS_DEFINITION_ID = 123153L;\n    private SProcessDefinitionImpl processDefinition;\n    @Rule\n    public SystemOutRule systemOutRule = new SystemOutRule().enableLog();\n    @Mock\n    private CacheService cacheService;\n    @Mock\n    private DependencyService dependencyService;\n    @Mock\n    private ProcessResourcesService processResourcesService;\n    @Mock\n    private ConnectorExecutionTimeLogger connectorExecutionTimeLogger;\n    @Mock\n    private ConnectorExecutor connectorExecutor;\n    @Mock\n    private ExpressionResolverService expressionResolverService;\n    @Mock\n    private OperationService operationService;\n    @Mock\n    private TimeTracker timeTracker;\n    @Mock\n    private ClassLoaderService classLoaderService;\n    @Captor\n    private ArgumentCaptor<SConnector> connectorArgumentCaptor;\n    @Captor\n    ArgumentCaptor<SDependency> dependencyArgumentCaptor;\n    @Captor\n    ArgumentCaptor<SBARResource> sBarResourceArgumentCaptor;\n    private ConnectorServiceImpl connectorService;\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @SuppressWarnings(\"unchecked\")\n    @Before\n    public void setup() {\n        connectorService = new ConnectorServiceImpl(cacheService, connectorExecutor, expressionResolverService,\n                operationService,\n                dependencyService, classLoaderService, timeTracker, processResourcesService,\n                connectorExecutionTimeLogger);\n        processDefinition = new SProcessDefinitionImpl(\"proc\", \"1\");\n        processDefinition.setId(PROCESS_DEFINITION_ID);\n    }\n\n    @Test(expected = SInvalidConnectorImplementationException.class)\n    public void setConnectorImplementationWithCorruptFile() throws Exception {\n        connectorService.setConnectorImplementation(processDefinition, \"myConnector\", \"1.0.0\",\n                new byte[] { 1, 5, 6, 87, 9, 9, 36, 1, 6, 6, 5, 3, 5, 5, 5, 64, 6, 5, 5 });\n    }\n\n    @Test(expected = SInvalidConnectorImplementationException.class)\n    public void setConnectorImplementationZipHavingNoImpl() throws Exception {\n        final byte[] zip = IOUtil.zip(Collections.singletonMap(\"connector.notImpl\", \"mocked\".getBytes()));\n        connectorService.setConnectorImplementation(processDefinition, \"myConnector\", \"1.0.0\", zip);\n    }\n\n    @Test(expected = SInvalidConnectorImplementationException.class)\n    public void setConnectorImplementationValidFileButWrongImpl() throws Exception {\n        final byte[] zip = IOUtil.zip(Collections.singletonMap(\"connector.impl\", \"mocked\".getBytes()));\n        connectorService.setConnectorImplementation(processDefinition, \"myConnector\", \"1.0.0\", zip);\n    }\n\n    @Test\n    public void setNewConnectorImplemCleansOldDependencies() throws Exception {\n        final long processDefId = 17L;\n\n        final SProcessDefinitionImpl sProcessDef = new SProcessDefinitionImpl(\"MyProcess\", \"1.0\");\n        sProcessDef.setId(processDefId);\n        final String connectorDefId = \"org.bonitasoft.connector.BeerConnector\";\n        final String connectorDefVersion = \"1.0.0\";\n        final String connectorImplId = \"org.bonitasoft.connector.HoogardenConnector\";\n        final String connectorImplVersion = \"1.0\";\n        final String implementationClassName = \"org.bonitasoft.engine.connectors.HoogardenBeerConnector\";\n        final SConnectorImplementationDescriptor hoogardenConnectorDescriptor = new SConnectorImplementationDescriptor(\n                implementationClassName,\n                connectorImplId,\n                connectorImplVersion, connectorDefId, connectorDefVersion,\n                new ArrayList<>(Arrays.asList(\"some1.jar\", \"HoogardenConnector.jar\")));\n        final SConnectorImplementationDescriptor oldConnectorDescriptor = new SConnectorImplementationDescriptor(\n                implementationClassName, connectorImplId,\n                connectorImplVersion, connectorDefId, connectorDefVersion,\n                new ArrayList<>(Collections.singletonList(\"file.jar\")));\n\n        Map<String, byte[]> zipFileMap = new HashMap<>(3);\n        byte[] connectorImplFile = createConnectorImplFile(hoogardenConnectorDescriptor);\n        zipFileMap.put(\"HoogardenBeerConnector.impl\", connectorImplFile);\n        final byte[] dep1Bytes = { 12, 94, 14, 12 };\n        zipFileMap.put(\"some1.jar\", dep1Bytes);\n        final byte[] hoogardenConnectorBytes = { 12, 94, 14, 9, 54, 65, 98, 54, 21, 32, 65 };\n        zipFileMap.put(\"HoogardenConnector.jar\", hoogardenConnectorBytes);\n        final byte[] zip1 = IOUtil.zip(zipFileMap);\n\n        final SBARResource originalConnector = new SBARResource(\"file.impl\", BARResourceType.CONNECTOR, processDefId,\n                createConnectorImplFile(oldConnectorDescriptor));\n        doReturn(Collections.singletonList(originalConnector)).when(processResourcesService)\n                .get(eq(processDefId), eq(BARResourceType.CONNECTOR), eq(0), anyInt());\n        SDependency dependency = mock(SDependency.class);\n        doReturn(dependency).when(dependencyService).getDependencyOfArtifact(processDefId, ScopeType.PROCESS,\n                \"file.jar\");\n\n        connectorService.setConnectorImplementation(sProcessDef, connectorDefId, connectorDefVersion, zip1);\n        verify(dependencyService).createMappedDependency(\"HoogardenConnector.jar\", hoogardenConnectorBytes,\n                \"HoogardenConnector.jar\", processDefId,\n                ScopeType.PROCESS);\n        verify(dependencyService).createMappedDependency(\"some1.jar\", dep1Bytes, \"some1.jar\", processDefId,\n                ScopeType.PROCESS);\n        verify(processResourcesService).add(processDefId, \"HoogardenBeerConnector.impl\", BARResourceType.CONNECTOR,\n                connectorImplFile);\n        verify(dependencyService).deleteDependency(dependency);\n        verify(processResourcesService).remove(originalConnector);\n    }\n\n    @Test\n    public void setNewConnectorImplemShouldIgnoreSourceFiles() throws Exception {\n        final long processDefId = 1324565477444L;\n\n        Map<String, byte[]> zipFileMap = new HashMap<>(1);\n        zipFileMap.put(\"src/net/company/MyImplem.java\", \"some Java source file content\".getBytes());\n        zipFileMap.put(\"connector.impl\", \"thecontent\".getBytes());\n        final byte[] zip = IOUtil.zip(zipFileMap);\n\n        connectorService.extractConnectorImplementation(zip);\n\n        verify(processResourcesService, times(0)).add(eq(processDefId), anyString(), any(BARResourceType.class),\n                any(byte[].class));\n    }\n\n    @Test\n    public void getConnectorImplementationShouldReadFileWhenCacheIsVoid() throws Exception {\n        checkGetConnectorImplementationUsesCache(0, 1, true);\n    }\n\n    @Test\n    public void getConnectorImplementationShouldReadFileWhenCacheIsNotEmpty() throws Exception {\n        checkGetConnectorImplementationUsesCache(1, 0, true);\n    }\n\n    @Test\n    public void getConnectorImplementationShouldReadFileWhenCacheDoesNotContainsConnector() throws Exception {\n        checkGetConnectorImplementationUsesCache(1, 1, false);\n    }\n\n    @Test\n    public void getConnectorImplementationShouldNoteReadFileWhenCacheContainsConnector() throws Exception {\n        checkGetConnectorImplementationUsesCache(1, 0, true);\n    }\n\n    @Test\n    public void should_executeConnector_call_connector_executor() throws Exception {\n        //given\n        SConnectorImplementationDescriptor connectorImplementationDescriptor = new SConnectorImplementationDescriptor(\n                MyTestConnector.class.getName(), \"implId\",\n                \"impplVersion\", \"defId\", \"defVersion\", new ArrayList<>(Collections.<String> emptyList()));\n        SConnectorInstance connectorInstance = mock(SConnectorInstance.class);\n        when(connectorExecutor.execute(any(), any(), any()))\n                .thenReturn(CompletableFuture\n                        .completedFuture(ConnectorExecutionResult.result(Collections.emptyMap()).tookMillis(100)));\n\n        //when\n        Map<String, Object> inputParameters = Collections.<String, Object> singletonMap(\"key\", \"value\");\n        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        connectorService.executeConnector(PROCESS_DEFINITION_ID, connectorInstance, connectorImplementationDescriptor,\n                contextClassLoader, inputParameters);\n        //then\n        verify(connectorExecutor).execute(connectorArgumentCaptor.capture(), eq(inputParameters),\n                eq(contextClassLoader));\n        SConnector sConnector = connectorArgumentCaptor.getValue();\n        assertThat(sConnector).isInstanceOf(SConnectorAdapter.class);\n        assertThat(((SConnectorAdapter) sConnector).getConnector()).isInstanceOf(MyTestConnector.class);\n    }\n\n    @Test\n    public void should_log_execution_time_when_executing_connector() throws Exception {\n        //given\n        SConnectorImplementationDescriptor connectorImplementationDescriptor = new SConnectorImplementationDescriptor(\n                MyTestConnector.class.getName(), \"implId\",\n                \"impplVersion\", \"defId\", \"defVersion\", new ArrayList<>(Collections.emptyList()));\n        SConnectorInstance connectorInstance = mock(SConnectorInstance.class);\n        when(connectorExecutor.execute(any(), any(), any())).thenReturn(CompletableFuture\n                .completedFuture(ConnectorExecutionResult.result(Collections.emptyMap()).tookMillis(100)));\n\n        //when\n        Map<String, Object> inputParameters = Collections.singletonMap(\"key\", \"value\");\n        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        connectorService.executeConnector(PROCESS_DEFINITION_ID, connectorInstance, connectorImplementationDescriptor,\n                contextClassLoader, inputParameters);\n        //then\n        verify(connectorExecutionTimeLogger).log(eq(PROCESS_DEFINITION_ID), eq(connectorInstance),\n                any(MyTestConnector.class), eq(inputParameters), eq(100L));\n    }\n\n    private void checkGetConnectorImplementationUsesCache(final int givenCacheSizeToBeReturned,\n            final int expectedNumberOfCacheStoreInvocations,\n            final boolean shouldCacheContainsConnectorImplementation)\n            throws Exception {\n\n        final String connectorDefId = \"org.bonitasoft.connector.BeerConnector\";\n        final String connectorDefVersion = \"1.0.0\";\n        final String connectorImplId = \"org.bonitasoft.connector.HoogardenConnector\";\n        final String connectorImplVersion = \"1.0\";\n        final String implementationClassName = \"org.bonitasoft.engine.connectors.HoogardenBeerConnector\";\n        final SConnectorImplementationDescriptor connectorImplDescriptor = new SConnectorImplementationDescriptor(\n                implementationClassName, connectorImplId,\n                connectorImplVersion, connectorDefId, connectorDefVersion,\n                new ArrayList<>(Arrays.asList(\"some1.jar\", \"HoogardenConnector.jar\")));\n        byte[] connectorImplFile = createConnectorImplFile(connectorImplDescriptor);\n\n        final Map<String, byte[]> zipFileMap = new HashMap<>(3);\n\n        zipFileMap.put(\"HoogardenBeerConnector.impl\", connectorImplFile);\n        zipFileMap.put(\"some1.jar\", new byte[] { 12, 94, 14, 12 });\n        zipFileMap.put(\"HoogardenConnector.jar\", new byte[] { 12, 94, 14, 9, 54, 65, 98, 54, 21, 32, 65 });\n        final byte[] zip1 = IOUtil.zip(zipFileMap);\n\n        doReturn(Collections\n                .singletonList(new SBARResource(\"HoogardenBeerConnector.impl\", BARResourceType.CONNECTOR,\n                        processDefinition.getId(),\n                        connectorImplFile)))\n                .when(processResourcesService)\n                .get(eq(processDefinition.getId()), eq(BARResourceType.CONNECTOR), eq(0), anyInt());\n\n        //setConnectorImplementation store to cache\n        connectorService.setConnectorImplementation(processDefinition, connectorDefId, connectorDefVersion, zip1);\n\n        //given\n        doReturn(givenCacheSizeToBeReturned).when(cacheService).getCacheSize(ConnectorServiceImpl.CONNECTOR_CACHE_NAME);\n\n        List<String> cacheContentKeys = Collections.emptyList();\n        final String buildConnectorImplementationKey = connectorService\n                .buildConnectorImplementationKey(processDefinition.getId(), connectorImplId, connectorImplVersion);\n        if (shouldCacheContainsConnectorImplementation) {\n            cacheContentKeys = Collections.singletonList(buildConnectorImplementationKey);\n        }\n        doReturn(cacheContentKeys).when(cacheService).getKeys(ConnectorServiceImpl.CONNECTOR_CACHE_NAME);\n        doReturn(connectorImplDescriptor).when(cacheService).get(ConnectorServiceImpl.CONNECTOR_CACHE_NAME,\n                buildConnectorImplementationKey);\n\n        //when\n        connectorService.getConnectorImplementations(processDefinition.getId(), 0,\n                10, \"\", OrderByType.ASC);\n\n        //then\n        verify(cacheService, times(expectedNumberOfCacheStoreInvocations + 1)).store(anyString(),\n                any(Serializable.class), any(Object.class));\n\n    }\n\n    public static class MyTestConnector extends AbstractConnector {\n\n        @Override\n        public void validateInputParameters() throws ConnectorValidationException {\n\n        }\n\n        @Override\n        protected void executeBusinessLogic() throws ConnectorException {\n\n        }\n    }\n\n    @Test\n    public void getNumberOfConnectorImplementations_should_call_count_on_resource_service() throws Exception {\n        final long processDefinitionId = 451L;\n        final long expectedCount = 11L;\n        doReturn(expectedCount).when(processResourcesService).count(processDefinitionId, BARResourceType.CONNECTOR);\n\n        final Long numberOfConnectorImplementations = connectorService\n                .getNumberOfConnectorImplementations(processDefinitionId);\n\n        assertThat(numberOfConnectorImplementations).isEqualTo(expectedCount);\n    }\n\n    @Test\n    public void should_read_connector_archive_correctly() throws Exception {\n        //given\n        String connectorImplFileName = \"myConnector.impl\";\n        HashMap<String, byte[]> files = new HashMap<>();\n        byte[] connectorImplFileContent = (\"<connectorImplementation>\\n\" +\n                \"\\n\" +\n                \"\\t<definitionId>org.bonitasoft.connector.testConnectorWithOutput</definitionId>\\n\" +\n                \"\\t<definitionVersion>1.0</definitionVersion>\\n\" +\n                \"\\t<implementationClassname>org.bonitasoft.engine.connectors.TestConnectorWithModifiedOutput</implementationClassname>\\n\"\n                +\n                \"\\t<implementationId>org.bonitasoft.connector.testConnectorWithModifiedOutput</implementationId>\\n\" +\n                \"\\t<implementationVersion>1.0</implementationVersion>\\n\" +\n                \"\\n\" +\n                \"\\t<jarDependencies>\\n\" +\n                \"\\t\\t<jarDependency>TestConnectorWithModifiedOutput.jar</jarDependency>\\n\" +\n                \"\\t</jarDependencies>\\n\" +\n                \"</connectorImplementation>\\n\").getBytes();\n        files.put(connectorImplFileName, connectorImplFileContent);\n        files.put(\"classpath/jar1.jar\", new byte[] { 1, 2, 3 });\n        files.put(\"classpath/jar2.jar\", new byte[] { 1, 2, 4 });\n        byte[] zip = IOUtil.zip(files);\n        //when\n        ConnectorArchive connectorArchive = connectorService.extractConnectorImplementation(zip);\n        //then\n        assertThat(connectorArchive.getConnectorImplName()).isEqualTo(connectorImplFileName);\n        assertThat(connectorArchive.getConnectorImplContent()).isEqualTo(connectorImplFileContent);\n        assertThat(connectorArchive.getDependencies()).containsKeys(\"jar1.jar\", \"jar2.jar\").hasSize(2);\n        assertThat(connectorArchive.getDependencies().get(\"jar1.jar\")).isEqualTo(new byte[] { 1, 2, 3 });\n        assertThat(connectorArchive.getDependencies().get(\"jar2.jar\")).isEqualTo(new byte[] { 1, 2, 4 });\n    }\n\n    @Test\n    public void should_setConnectorImplementation_delete_and_create_dependency() throws Exception {\n        //given\n        byte[] zip = createConnectorArchiveZip(\"myConnector2.impl\", \"connectorId\", \"connectorVersion\",\n                new BarResource(\"jar3.jar\", new byte[] { 3 }),\n                new BarResource(\"jar4.jar\", new byte[] { 4 }));\n        havingConnector(processDefinition, \"myConnector1.impl\", \"connectorId\", \"connectorVersion\",\n                new BarResource(\"jar1.jar\", new byte[] { 1 }),\n                new BarResource(\"jar2.jar\", new byte[] { 2 }));\n        //when\n        connectorService.setConnectorImplementation(processDefinition, \"connectorId\", \"connectorVersion\", zip);\n        //then\n        verify(dependencyService, times(2)).deleteDependency(dependencyArgumentCaptor.capture());\n        assertThat(dependencyArgumentCaptor.getAllValues()).extracting(\"fileName\").containsOnly(\"jar1.jar\", \"jar2.jar\");\n        verify(dependencyService).createMappedDependency(\"jar3.jar\", new byte[] { 3 }, \"jar3.jar\",\n                processDefinition.getId(), ScopeType.PROCESS);\n        verify(dependencyService).createMappedDependency(\"jar4.jar\", new byte[] { 4 }, \"jar4.jar\",\n                processDefinition.getId(), ScopeType.PROCESS);\n    }\n\n    @Test\n    public void should_setConnectorImplementation_replace_jar_even_if_jar_was_not_in_jarDependencies()\n            throws Exception {\n        //given\n        byte[] zip = createConnectorArchiveZip(\"myConnector1.impl\", \"connectorId\", \"connectorVersion\",\n                new BarResource(\"jar2.jar\", new byte[] { 3 }));\n        //a process with an inconsistent connector implementation is deployed: connector have a dependency names jar2.jar and jar1.jar but they are not declared in jarDependencies\n        doReturn(Collections.singletonList(\n                new SBARResource(\"myConnector1.impl\", BARResourceType.CONNECTOR, processDefinition.getId(),\n                        createConnectorImplFile(\"connectorId\", \"connectorVersion\"))))\n                .when(processResourcesService)\n                .get(eq(processDefinition.getId()), eq(BARResourceType.CONNECTOR), anyInt(), anyInt());\n        doReturn(new SDependency(\"jar2.jar\", \"jar2.jar\", new byte[] { 2 })).when(dependencyService)\n                .getDependencyOfArtifact(processDefinition.getId(), ScopeType.PROCESS, \"jar2.jar\");\n        systemOutRule.clearLog();\n        //when\n        connectorService.setConnectorImplementation(processDefinition, \"connectorId\", \"connectorVersion\", zip);\n        //then\n        verify(dependencyService, never()).createMappedDependency(anyString(), any(byte[].class), anyString(),\n                anyLong(), any(ScopeType.class));\n        verify(dependencyService, never()).deleteDependency(any(SDependency.class));\n        verify(dependencyService).updateDependencyOfArtifact(\"jar2.jar\", new byte[] { 3 }, \"jar2.jar\",\n                processDefinition.getId(), ScopeType.PROCESS);\n        assertThat(systemOutRule.getLog()).containsPattern(\n                \"WARN.*Updating a dependency of the connector connectorId in version connectorVersion of process definition 123153. \"\n                        +\n                        \"The jar file jar2.jar was not declared in the previous connector implementation but is in the dependencies of the process. \"\n                        +\n                        \"The jar is still updated but this can lead to inconsistencies.\");\n    }\n\n    @Test\n    public void should_setConnectorImplementation_update_existing_dependencies() throws Exception {\n        //given\n        byte[] zip = createConnectorArchiveZip(\"myConnector2.impl\", \"connectorId\", \"connectorVersion\",\n                new BarResource(\"jar2.jar\", new byte[] { 3 }),\n                new BarResource(\"jar4.jar\", new byte[] { 4 }));\n        havingConnector(processDefinition, \"myConnector1.impl\", \"connectorId\", \"connectorVersion\",\n                new BarResource(\"jar1.jar\", new byte[] { 1 }),\n                new BarResource(\"jar2.jar\", new byte[] { 2 }));\n        //when\n        connectorService.setConnectorImplementation(processDefinition, \"connectorId\", \"connectorVersion\", zip);\n        //then\n        verify(dependencyService, times(1)).deleteDependency(dependencyArgumentCaptor.capture());\n        assertThat(dependencyArgumentCaptor.getAllValues()).extracting(\"fileName\").containsOnly(\"jar1.jar\");\n        verify(dependencyService).createMappedDependency(\"jar4.jar\", new byte[] { 4 }, \"jar4.jar\",\n                processDefinition.getId(), ScopeType.PROCESS);\n        verify(dependencyService).updateDependencyOfArtifact(\"jar2.jar\", new byte[] { 3 }, \"jar2.jar\",\n                processDefinition.getId(), ScopeType.PROCESS);\n    }\n\n    @Test\n    public void should_setConnectorImplementation_delete_and_create_impl_file() throws Exception {\n        //given\n        byte[] zip = createConnectorArchiveZip(\"myConnector2.impl\", \"connectorId\", \"connectorVersion\");\n        havingConnector(processDefinition, \"myConnector1.impl\", \"connectorId\", \"connectorVersion\");\n        //when\n        connectorService.setConnectorImplementation(processDefinition, \"connectorId\", \"connectorVersion\", zip);\n        //then\n        verify(processResourcesService).remove(sBarResourceArgumentCaptor.capture());\n        assertThat(sBarResourceArgumentCaptor.getValue().getName()).isEqualTo(\"myConnector1.impl\");\n        byte[] connectorImplFile = createConnectorImplFile(\"connectorId\", \"connectorVersion\");\n        verify(processResourcesService).add(processDefinition.getId(), \"myConnector2.impl\", BARResourceType.CONNECTOR,\n                connectorImplFile);\n    }\n\n    @Test\n    public void should_setConnectorImplementation_update_connector_implementation_file() throws Exception {\n        //given\n        byte[] zip = createConnectorArchiveZip(\"myConnector1.impl\", \"connectorId\", \"connectorVersion\",\n                new BarResource(\"jar1.jar\", new byte[] { 1 }));\n        havingConnector(processDefinition, \"myConnector1.impl\", \"connectorId\", \"connectorVersion\");\n        //when\n        connectorService.setConnectorImplementation(processDefinition, \"connectorId\", \"connectorVersion\", zip);\n        //then\n        verify(processResourcesService, never()).remove(any(SBARResource.class));\n        verify(processResourcesService, never()).add(anyLong(), anyString(), any(BARResourceType.class),\n                any(byte[].class));\n        byte[] connectorImplFile = createConnectorImplFile(\"connectorId\", \"connectorVersion\",\n                new BarResource(\"jar1.jar\", new byte[] { 1 }));\n        verify(processResourcesService).update(sBarResourceArgumentCaptor.capture(), eq(connectorImplFile));\n        assertThat(sBarResourceArgumentCaptor.getValue().getName()).isEqualTo(\"myConnector1.impl\");\n    }\n\n    private void havingConnector(SProcessDefinitionImpl processDefinition, String implName, String connectorId,\n            String connectorVersion, BarResource... jars)\n            throws Exception {\n        doReturn(Collections.singletonList(\n                new SBARResource(implName, BARResourceType.CONNECTOR, processDefinition.getId(),\n                        createConnectorImplFile(connectorId, connectorVersion, jars))))\n                .when(processResourcesService)\n                .get(eq(processDefinition.getId()), eq(BARResourceType.CONNECTOR), anyInt(), anyInt());\n        for (BarResource jar : jars) {\n            doReturn(new SDependency(jar.getName(), jar.getName(), jar.getContent())).when(dependencyService)\n                    .getDependencyOfArtifact(processDefinition.getId(), ScopeType.PROCESS, jar.getName());\n        }\n    }\n\n    @Test\n    public void setConnectorImplementation_should_detect_old_convention_fileName_in_db() throws Exception {\n        //given\n        byte[] zip = createConnectorArchiveZip(\"myConnector2.impl\", \"connectorId\", \"connectorVersion\");\n        havingConnectorWithWrongJarName(processDefinition, \"myConnector1.impl\", \"connectorId\", \"connectorVersion\",\n                new BarResource(\"myJar.jar\", new byte[] { 1 }));\n\n        //when\n        connectorService.setConnectorImplementation(processDefinition, \"connectorId\", \"connectorVersion\", zip);\n\n        //then\n        verify(dependencyService).getDependencyOfArtifact(123153L, ScopeType.PROCESS, \"123153_myJar.jar\");\n    }\n\n    private void havingConnectorWithWrongJarName(SProcessDefinitionImpl processDefinition, String implName,\n            String connectorId, String connectorVersion, BarResource... jars)\n            throws Exception {\n        doReturn(Collections.singletonList(\n                new SBARResource(implName, BARResourceType.CONNECTOR, processDefinition.getId(),\n                        createConnectorImplFile(connectorId, connectorVersion, jars))))\n                .when(processResourcesService)\n                .get(eq(processDefinition.getId()), eq(BARResourceType.CONNECTOR), anyInt(), anyInt());\n        for (BarResource jar : jars) {\n            doReturn(null).when(dependencyService).getDependencyOfArtifact(processDefinition.getId(), ScopeType.PROCESS,\n                    jar.getName());\n            doReturn(new SDependency(jar.getName(), jar.getName(), jar.getContent())).when(dependencyService)\n                    .getDependencyOfArtifact(processDefinition.getId(), ScopeType.PROCESS,\n                            processDefinition.getId() + \"_\" + jar.getName());\n        }\n    }\n\n    private byte[] createConnectorArchiveZip(String connectorImplFileName, String definitionId,\n            final String definitionVersion, BarResource... jars)\n            throws Exception {\n        HashMap<String, byte[]> files = new HashMap<>();\n        for (BarResource jar : jars) {\n            files.put(\"classpath/\" + jar.getName(), jar.getContent());\n        }\n        byte[] connectorImplFileContent = createConnectorImplFile(definitionId, definitionVersion, jars);\n        files.put(connectorImplFileName, connectorImplFileContent);\n        return IOUtil.zip(files);\n    }\n\n    private byte[] createConnectorImplFile(SConnectorImplementationDescriptor connector) {\n        return createConnectorImplFile(connector.getDefinitionId(), connector.getDefinitionVersion(), connector.getId(),\n                connector.getVersion(),\n                connector.getImplementationClassName(), connector.getJarDependencies());\n    }\n\n    private byte[] createConnectorImplFile(String definitionId, String definitionVersion, String id, String version,\n            String className, List<String> jars) {\n        return (\"<connectorImplementation>\\n\" +\n                \"\\n\" +\n                \"\\t<definitionId>\" + definitionId + \"</definitionId>\\n\" +\n                \"\\t<definitionVersion>\" + definitionVersion + \"</definitionVersion>\\n\" +\n                \"\\t<implementationClassname>\" + className + \"</implementationClassname>\\n\" +\n                \"\\t<implementationId>\" + id + \"</implementationId>\\n\" +\n                \"\\t<implementationVersion>\" + version + \"</implementationVersion>\\n\" +\n                \"\\n\" +\n                \"\\t<jarDependencies>\\n\" +\n                getJarsXml(jars) +\n                \"\\t</jarDependencies>\\n\" +\n                \"</connectorImplementation>\\n\").getBytes();\n    }\n\n    private byte[] createConnectorImplFile(String definitionId, String definitionVersion, BarResource... jars) {\n        List<String> jarNames = new ArrayList<>(jars.length);\n        for (BarResource jar : jars) {\n            jarNames.add(jar.getName());\n        }\n        return createConnectorImplFile(definitionId, definitionVersion, \"implId\", \"1.0\", \"TheClass\", jarNames);\n    }\n\n    private String getJarsXml(List<String> jars) {\n        StringBuilder result = new StringBuilder();\n        for (String jar : jars) {\n            result.append(\"\\t\\t<jarDependency>\").append(jar).append(\"</jarDependency>\\n\");\n        }\n        return result.toString();\n    }\n\n    @Test\n    public void should_throw_a_SConnectorException_if_a_Throwable_is_thrown_when_executing_a_connector()\n            throws Exception {\n        //given\n        SConnectorImplementationDescriptor connectorImplementationDescriptor = new SConnectorImplementationDescriptor(\n                MyTestConnector.class.getName(), \"implId\",\n                \"impplVersion\", \"defId\", \"defVersion\", new ArrayList<>(Collections.<String> emptyList()));\n        SConnectorInstance connectorInstance = mock(SConnectorInstance.class);\n\n        Map<String, Object> inputParameters = Collections.<String, Object> singletonMap(\"key\", \"value\");\n        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        when(connectorExecutor.execute(notNull(SConnector.class), eq(inputParameters), eq(contextClassLoader)))\n                .thenThrow(new NoClassDefFoundError());\n\n        expectedException.expect(SConnectorException.class);\n\n        connectorService.executeConnector(PROCESS_DEFINITION_ID, connectorInstance, connectorImplementationDescriptor,\n                contextClassLoader, inputParameters);\n    }\n\n    @Test\n    public void should_log_connector_details_when_executing_it() throws Exception {\n        //given\n        SConnectorImplementationDescriptor connectorImplementationDescriptor = new SConnectorImplementationDescriptor(\n                MyTestConnector.class.getName(), \"implId\",\n                \"impplVersion\", \"defId\", \"defVersion\", new ArrayList<>(Collections.<String> emptyList()));\n        SConnectorInstance connectorInstance = new SConnectorInstance(\"myConnectorInstance\", 123L, \"containerType\",\n                \"connectorId\", \"connectorVersion\", ConnectorEvent.ON_ENTER);\n        when(connectorExecutor.execute(any(), any(), any()))\n                .thenReturn(CompletableFuture\n                        .completedFuture(ConnectorExecutionResult.result(Collections.emptyMap()).tookMillis(100)));\n        systemOutRule.clearLog();\n        //when\n        Map<String, Object> inputParameters = new HashMap<>();\n        inputParameters.put(\"param1\", \"value1\");\n        inputParameters.put(\"param2\", \"value2\");\n        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        connectorService.executeConnector(PROCESS_DEFINITION_ID, connectorInstance, connectorImplementationDescriptor,\n                contextClassLoader, inputParameters);\n        //then\n        assertThat(systemOutRule.getLog()).contains(\n                \"Executing connector  [name: <myConnectorInstance>, version: <connectorVersion>, connector id: <connectorId>, \"\n                        +\n                        \"connector instance id: <0>, container type: <containerType>, container id: <123>, activation event: <ON_ENTER>\");\n    }\n\n    @Test\n    public void should_disconnect_connector_when_executeOutputOperation_throw_a_SOperationExecutionException()\n            throws Exception {\n        // Given\n        doThrow(SOperationExecutionException.class).when(operationService).execute(anyList(), anyLong(),\n                nullable(String.class), nullable(SExpressionContext.class));\n\n        //When\n        ConnectorResult connectorResult = new ConnectorResult(mock(Connector.class), new HashMap<>(), 1);\n        assertThatThrownBy(\n                () -> connectorService.executeOutputOperation(new ArrayList<>(), new SExpressionContext(1L, \"\", 1L),\n                        connectorResult))\n                .isInstanceOf(SConnectorException.class);\n\n        // Then\n        verify(connectorExecutor).disconnect(any(SConnectorAdapter.class));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/document/api/impl/DocumentServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.document.api.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.Mockito.*;\n\nimport java.util.*;\n\nimport org.bonitasoft.engine.archive.ArchiveService;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.core.document.model.AbstractSMappedDocument;\nimport org.bonitasoft.engine.core.document.model.SDocument;\nimport org.bonitasoft.engine.core.document.model.SMappedDocument;\nimport org.bonitasoft.engine.core.document.model.archive.SAMappedDocument;\nimport org.bonitasoft.engine.core.document.model.recorder.SelectDescriptorBuilder;\nimport org.bonitasoft.engine.persistence.*;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.ArgumentMatchers;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Vincent Elcrin\n * @author Baptiste Mesta\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class DocumentServiceImplTest {\n\n    @Mock\n    private Recorder recorder;\n    @Mock\n    private ReadPersistenceService persistenceService;\n    @Mock\n    private ReadPersistenceService definitiveArchiveReadPersistenceService;\n    @Mock\n    private SDocumentDownloadURLProvider urlProvider;\n    @Mock\n    private ArchiveService archiveService;\n\n    private DocumentServiceImpl documentService;\n\n    @Before\n    public void setUp() {\n        when(archiveService.getDefinitiveArchiveReadPersistenceService())\n                .thenReturn(definitiveArchiveReadPersistenceService);\n        documentService = spy(new DocumentServiceImpl(recorder, persistenceService, urlProvider, archiveService));\n    }\n\n    @Test\n    public void generateDocumentURL_should_call_urlProvider() {\n        doReturn(\"generated\").when(urlProvider).generateURL(\"name\", \"docId\");\n        assertEquals(\"generated\", documentService.generateDocumentURL(\"name\", \"docId\"));\n    }\n\n    @Test\n    public void should_getDocumentList_return_the_list() throws Exception {\n        //given\n        final List<SMappedDocument> documentList = Arrays.asList(new SMappedDocument(), new SMappedDocument());\n        doReturn(documentList).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SMappedDocument>> any());\n        //when\n        final List<SMappedDocument> theList = documentService.getDocumentList(\"theList\", 45L, 0, 100);\n        //then\n        assertThat(theList).isEqualTo(documentList);\n    }\n\n    @Test\n    public void should_getDocumentList_return_the_list_with_more_than_100_elements() throws Exception {\n        //given\n        final List<SMappedDocument> documentList1 = constructList(100);\n        final List<SMappedDocument> documentList2 = constructList(50);\n        when(persistenceService.selectList(ArgumentMatchers.<SelectListDescriptor<SMappedDocument>> any()))\n                .thenReturn(documentList1).thenReturn(documentList2);\n        //when\n        final List<SMappedDocument> theList = documentService.getDocumentList(\"theList\", 45L, 0, 100);\n        //then\n        documentList1.addAll(documentList2);\n        assertThat(theList).isEqualTo(documentList1);\n    }\n\n    @Test\n    public void should_getDocumentList_return_the_list_with_100_elements() throws Exception {\n        //given\n        final List<SMappedDocument> documentList1 = constructList(100);\n        final List<SMappedDocument> documentList2 = constructList(0);\n        when(persistenceService.selectList(ArgumentMatchers.<SelectListDescriptor<SMappedDocument>> any()))\n                .thenReturn(documentList1).thenReturn(documentList2);\n        //when\n        final List<SMappedDocument> theList = documentService.getDocumentList(\"theList\", 45L, 0, 100);\n        //then\n        assertThat(theList).isEqualTo(documentList1);\n    }\n\n    @Test\n    public void should_call_deletion_with_right_arguments() throws Exception {\n        //given\n        Long sourceObjectId = 5L;\n        final SAMappedDocument mappedDocument = new SAMappedDocument(1L, sourceObjectId);\n        byte[] docContent = \"theContent\".getBytes();\n        final SDocument document = new SDocument(docContent);\n        ArgumentCaptor<UpdateRecord> argumentCaptor = ArgumentCaptor.forClass(UpdateRecord.class);\n        when(definitiveArchiveReadPersistenceService.selectById(any()))\n                .thenReturn(mappedDocument);\n        when(persistenceService.selectById(any()))\n                .thenReturn(document);\n\n        //when\n        documentService.deleteContentOfArchivedDocument(sourceObjectId);\n        //then\n        verify(recorder).recordUpdate(argumentCaptor.capture(), any());\n        UpdateRecord record = argumentCaptor.getValue();\n        assertThat(record.getFields()).containsOnlyKeys(\"content\", \"hasContent\");\n    }\n\n    private List<SMappedDocument> constructList(final int size) {\n        final ArrayList<SMappedDocument> list = new ArrayList<>();\n        for (int i = 0; i < size; i++) {\n            list.add(new SMappedDocument());\n        }\n        return list;\n    }\n\n    @Test\n    public void should_getDocumentList_return_empty_list() throws Exception {\n        //given\n        doReturn(Collections.emptyList()).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SMappedDocument>> any());\n        //when\n        final List<SMappedDocument> theList = documentService.getDocumentList(\"theList\", 45L, 0, 100);\n        //then\n        assertThat(theList).isEmpty();\n    }\n\n    @Test\n    public void should_getDocumentList_at_give_time_get_archived() throws Exception {\n        //given\n        final List<SMappedDocument> sMappedDocuments = Arrays.asList(new SMappedDocument(), new SMappedDocument());\n        final List<SAMappedDocument> saMappedDocuments = Arrays.asList(new SAMappedDocument(), new SAMappedDocument());\n        doReturn(saMappedDocuments).when(persistenceService).selectList(\n                SelectDescriptorBuilder.getArchivedDocumentList(\"theList\", 45L,\n                        new QueryOptions(0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS), 123456789L));\n        doReturn(sMappedDocuments).when(persistenceService)\n                .selectList(\n                        SelectDescriptorBuilder.getDocumentListCreatedBefore(\"theList\", 45L,\n                                new QueryOptions(0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS),\n                                123456789L));\n        //when\n        final List<AbstractSMappedDocument> theList = documentService.getDocumentList(\"theList\", 45L, 123456789L);\n        //then\n        final ArrayList<AbstractSMappedDocument> expected = new ArrayList<>(saMappedDocuments);\n        expected.addAll(sMappedDocuments);\n        assertThat(theList).isEqualTo(expected);\n    }\n\n    @Test(expected = SObjectNotFoundException.class)\n    public void should_get_throw_not_found_exceptions() throws Exception {\n        //when\n        documentService.getDocument(123456L);\n        //then exception\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/document/api/impl/SDocumentDownloadURLProviderImplTest.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.document.api.impl;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.Mockito.*;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Julien Mege\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class SDocumentDownloadURLProviderImplTest {\n\n    private static final String SERVLET_URL = \"documentDownload\";\n\n    private SDocumentDownloadURLProvider urlProvider = new SDocumentDownloadURLProviderImpl(SERVLET_URL);\n\n    @Test\n    public void generateURL_should_return_encoded_url() {\n        assertEquals(\"documentDownload?fileName=%5BnameWithSpecialChar%5D&contentStorageId=docId\",\n                urlProvider.generateURL(\"[nameWithSpecialChar]\", \"docId\"));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/document/model/builder/impl/SDocumentBuilderFactoryImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.document.model.builder.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.core.document.model.builder.SDocumentBuilder;\nimport org.bonitasoft.engine.core.document.model.builder.SDocumentBuilderFactory;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Celine Souchet\n * @version 6.4.2\n * @since 6.4.2\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class SDocumentBuilderFactoryImplTest {\n\n    @InjectMocks\n    private SDocumentBuilderFactory sDocumentBuilderFactory;\n\n    @Test(expected = IllegalArgumentException.class)\n    public final void createNewProcessDocument_should_throw_exception_if_no_file_name() {\n        // Given\n        final String fileName = null;\n        final String mimeType = \"mimeType\";\n        final long authorId = 3;\n        final byte[] content = \"content\".getBytes();\n\n        // When\n        sDocumentBuilderFactory.createNewProcessDocument(fileName, mimeType, authorId, content);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.bpm.document.DocumentValue#DocumentValue(byte[], java.lang.String, java.lang.String)}.\n     */\n    @Test(expected = IllegalArgumentException.class)\n    public final void createNewProcessDocument_should_throw_exception_if_empty_file_name() {\n        // Given\n        final String fileName = \"\";\n        final String mimeType = \"mimeType\";\n        final long authorId = 3;\n        final byte[] content = \"content\".getBytes();\n\n        // When\n        sDocumentBuilderFactory.createNewProcessDocument(fileName, mimeType, authorId, content);\n    }\n\n    @Test\n    public final void createNewProcessDocument_should_be_valid_if_no_content_but_with_filename() {\n        // Given\n        final String fileName = \"filename\";\n        final String mimeType = \"mimeType\";\n        final long authorId = 3;\n        final byte[] content = null;\n\n        // When\n        final SDocumentBuilder newProcessDocument = sDocumentBuilderFactory.createNewProcessDocument(fileName, mimeType,\n                authorId, content);\n\n        // then\n        assertThat(newProcessDocument).isNotNull();\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.bpm.document.DocumentValue#DocumentValue(byte[], java.lang.String, java.lang.String)}.\n     */\n    @Test\n    public final void createNewProcessDocument_should_be_valid_if_empty_content_but_with_filename() {\n        // Given\n        final String fileName = \"filename\";\n        final String mimeType = \"mimeType\";\n        final long authorId = 3;\n        final byte[] content = \"\".getBytes();\n\n        // When\n        final SDocumentBuilder newProcessDocument = sDocumentBuilderFactory.createNewProcessDocument(fileName, mimeType,\n                authorId, content);\n\n        // then\n        assertThat(newProcessDocument).isNotNull();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SContractViolationExceptionTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.junit.Test;\n\n/**\n * author Emmanuel Duchastenier\n */\npublic class SContractViolationExceptionTest {\n\n    @Test\n    public void should_return_explanation() {\n        final SContractViolationException contractViolationException = new SContractViolationException(\"Bad contract\",\n                Arrays.asList(\"issue1\", \"issue2\"));\n\n        assertThat(contractViolationException.getExplanations()).containsExactly(\"issue1\", \"issue2\");\n    }\n\n    @Test\n    public void should_print_stack_trace_show_explanations() {\n        final SContractViolationException contractViolationException = new SContractViolationException(\"Bad contract\",\n                Arrays.asList(\"issue1\", \"issue2\"));\n\n        assertThat(contractViolationException.getMessage()).isEqualTo(\"Bad contract: issue1, issue2\");\n\n    }\n\n    @Test\n    public void should_print_stack_trace_show_one_explanation() {\n        final SContractViolationException contractViolationException = new SContractViolationException(\"Bad contract\",\n                Arrays.asList(\"issue1\"));\n\n        assertThat(contractViolationException.getMessage()).isEqualTo(\"Bad contract: issue1\");\n\n    }\n\n    @Test\n    public void should_print_stack_trace_with_null_explanations() {\n        List<String> explanations = null;\n        @SuppressWarnings(\"ConstantConditions\")\n        final SContractViolationException contractViolationException = new SContractViolationException(\"Bad contract\",\n                explanations);\n\n        assertThat(contractViolationException.getMessage()).isEqualTo(\"Bad contract: no details\");\n    }\n\n    @Test\n    public void should_print_stack_trace_with_empty_explanations() {\n        List<String> explanations = Collections.emptyList();\n        final SContractViolationException contractViolationException = new SContractViolationException(\"Bad contract\",\n                explanations);\n\n        assertThat(contractViolationException.getMessage()).isEqualTo(\"Bad contract: no details\");\n    }\n\n    @Test\n    public void should_getSimpleMessage_not_show_explanations() {\n        final SContractViolationException contractViolationException = new SContractViolationException(\"Bad contract\",\n                Arrays.asList(\"issue1\", \"issue2\"));\n\n        assertThat(contractViolationException.getSimpleMessage()).isEqualTo(\"Bad contract\");\n\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SProcessInstanceReadExceptionTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.api.exceptions;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.junit.Test;\n\n/**\n * @author Celine Souchet\n */\npublic class SProcessInstanceReadExceptionTest {\n\n    @Test\n    public void constructTheExceptionWithoutADescriptor() {\n        final SBonitaReadException cause = new SBonitaReadException(\"problem\");\n        final SProcessInstanceReadException exception = new SProcessInstanceReadException(cause);\n        assertTrue(exception.getMessage().isEmpty());\n    }\n\n    @Test\n    public void constructTheExceptionWithADescriptor() {\n        final SelectOneDescriptor<PersistentObject> descriptor = new SelectOneDescriptor<PersistentObject>(\n                \"getPersistentObject\", null, PersistentObject.class);\n        final SBonitaReadException cause = new SBonitaReadException(\"problem\", null, descriptor);\n        final SProcessInstanceReadException exception = new SProcessInstanceReadException(cause);\n        assertEquals(descriptor.toString(), exception.getMessage());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/event/impl/EventInstanceRepositoryImplForEventTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.event.impl;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.archive.ArchiveService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.SEventInstanceCreationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.SEventInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SIntermediateCatchEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SStartEventInstance;\nimport org.bonitasoft.engine.core.process.instance.recorder.SelectDescriptorBuilder;\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.services.PersistenceService;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Celine Souchet\n * @version 6.4.0\n * @since 6.4.0\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class EventInstanceRepositoryImplForEventTest {\n\n    @Mock\n    private ArchiveService archiveService;\n\n    @Mock\n    private EventService eventService;\n    @Mock\n    private PersistenceService persistenceService;\n\n    @Mock\n    private Recorder recorder;\n\n    @InjectMocks\n    private EventInstanceRepositoryImpl eventInstanceRepository;\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#createEventInstance(org.bonitasoft.engine.core.process.instance.model.event.SEventInstance)}\n     * .\n     */\n    @Test\n    public final void createEventInstance_should_create_event_instance() throws Exception {\n        // Given\n        final SIntermediateCatchEventInstance eventInstanceImpl = new SIntermediateCatchEventInstance();\n        final InsertRecord insertRecord = new InsertRecord(eventInstanceImpl);\n\n        // When\n        eventInstanceRepository.createEventInstance(eventInstanceImpl);\n\n        // Then\n        verify(recorder).recordInsert(eq(insertRecord), nullable(String.class));\n    }\n\n    @Test(expected = SEventInstanceCreationException.class)\n    public final void createEventInstance_should_throw_exception_when_there_is_error() throws Exception {\n        // Given\n        final SStartEventInstance eventInstanceImpl = new SStartEventInstance();\n        doThrow(new SRecorderException(\"\")).when(recorder).recordInsert(any(InsertRecord.class),\n                nullable(String.class));\n\n        // When\n        eventInstanceRepository.createEventInstance(eventInstanceImpl);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#getActivityBoundaryEventInstances(long, int, int)}\n     * .\n     */\n    @Test\n    public final void getActivityBoundaryEventInstances_should_return_event_instances() throws Exception {\n        // Given\n        final long activityInstanceId = 56L;\n        final int fromIndex = 1;\n        final int maxResults = 6;\n        final List<SBoundaryEventInstance> triggerInstanceImpls = Arrays.asList(new SBoundaryEventInstance());\n        final SelectListDescriptor<SBoundaryEventInstance> selectDescriptor = SelectDescriptorBuilder\n                .getActivityBoundaryEvents(activityInstanceId, fromIndex,\n                        maxResults);\n        doReturn(triggerInstanceImpls).when(persistenceService).selectList(selectDescriptor);\n\n        // When\n        final List<SBoundaryEventInstance> result = eventInstanceRepository\n                .getActivityBoundaryEventInstances(activityInstanceId, fromIndex, maxResults);\n\n        // Then\n        assertEquals(\"Should return the result of the mock.\", triggerInstanceImpls, result);\n    }\n\n    @Test\n    public final void getActivityBoundaryEventInstances_should_return_empty_list_if_doesnt_exist() throws Exception {\n        // Given\n        final long activityInstanceId = 56L;\n        final int fromIndex = 1;\n        final int maxResults = 6;\n        final SelectListDescriptor<SBoundaryEventInstance> selectDescriptor = SelectDescriptorBuilder\n                .getActivityBoundaryEvents(activityInstanceId, fromIndex,\n                        maxResults);\n        doReturn(Arrays.asList()).when(persistenceService).selectList(selectDescriptor);\n\n        // When\n        final List<SBoundaryEventInstance> result = eventInstanceRepository\n                .getActivityBoundaryEventInstances(activityInstanceId, fromIndex, maxResults);\n\n        // Then\n        assertTrue(\"The result must be empty.\", result.isEmpty());\n    }\n\n    @Test(expected = SEventInstanceReadException.class)\n    public final void getActivityBoundaryEventInstances_should_throw_exception_when_there_is_error() throws Exception {\n        // Given\n        final long activityInstanceId = 56L;\n        final int fromIndex = 1;\n        final int maxResults = 6;\n        final SelectListDescriptor<SBoundaryEventInstance> selectDescriptor = SelectDescriptorBuilder\n                .getActivityBoundaryEvents(activityInstanceId, fromIndex,\n                        maxResults);\n        doThrow(new SBonitaReadException(\"\")).when(persistenceService).selectList(selectDescriptor);\n\n        // When\n        eventInstanceRepository.getActivityBoundaryEventInstances(activityInstanceId, fromIndex, maxResults);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#getEventInstances(long, int, int, java.lang.String, org.bonitasoft.engine.persistence.OrderByType)}\n     * .\n     */\n    @Test\n    public final void getEventInstances_should_return_event_instances() throws Exception {\n        // Given\n        final long rootContainerId = 56L;\n        final int fromIndex = 1;\n        final int maxResults = 6;\n        final String fieldName = \"name\";\n        final OrderByType orderByType = OrderByType.ASC;\n        final SelectListDescriptor<SEventInstance> selectDescriptor = SelectDescriptorBuilder\n                .getEventsFromRootContainer(rootContainerId, fromIndex,\n                        maxResults, fieldName, orderByType);\n        final List<SIntermediateCatchEventInstance> eventInstanceImpls = Arrays\n                .asList(new SIntermediateCatchEventInstance());\n        doReturn(eventInstanceImpls).when(persistenceService).selectList(selectDescriptor);\n\n        // When\n        final List<SEventInstance> result = eventInstanceRepository.getEventInstances(rootContainerId, fromIndex,\n                maxResults, fieldName, orderByType);\n\n        // Then\n        assertEquals(\"Should return the result of the mock.\", eventInstanceImpls, result);\n    }\n\n    @Test\n    public final void getEventInstances_should_return_empty_list_if_doesnt_exist() throws Exception {\n        // Given\n        final long rootContainerId = 56L;\n        final int fromIndex = 1;\n        final int maxResults = 6;\n        final String fieldName = \"name\";\n        final OrderByType orderByType = OrderByType.ASC;\n        final SelectListDescriptor<SEventInstance> selectDescriptor = SelectDescriptorBuilder\n                .getEventsFromRootContainer(rootContainerId, fromIndex,\n                        maxResults, fieldName, orderByType);\n        doReturn(Arrays.asList()).when(persistenceService).selectList(selectDescriptor);\n\n        // When\n        final List<SEventInstance> result = eventInstanceRepository.getEventInstances(rootContainerId, fromIndex,\n                maxResults, fieldName, orderByType);\n\n        // Then\n        assertTrue(\"The result must be empty.\", result.isEmpty());\n    }\n\n    @Test(expected = SEventInstanceReadException.class)\n    public final void getEventInstances_should_throw_exception_when_there_is_error() throws Exception {\n        // Given\n        final long rootContainerId = 56L;\n        final int fromIndex = 1;\n        final int maxResults = 6;\n        final String fieldName = \"name\";\n        final OrderByType orderByType = OrderByType.ASC;\n        final SelectListDescriptor<SEventInstance> selectDescriptor = SelectDescriptorBuilder\n                .getEventsFromRootContainer(rootContainerId, fromIndex,\n                        maxResults, fieldName, orderByType);\n        doThrow(new SBonitaReadException(\"\")).when(persistenceService).selectList(selectDescriptor);\n\n        // When\n        eventInstanceRepository.getEventInstances(rootContainerId, fromIndex, maxResults, fieldName, orderByType);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/event/impl/EventInstanceRepositoryImplForEventTriggerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.event.impl;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNull;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.archive.ArchiveService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceCreationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceDeletionException;\nimport org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance;\nimport org.bonitasoft.engine.core.process.instance.recorder.SelectDescriptorBuilder;\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.bonitasoft.engine.services.PersistenceService;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentMatchers;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Celine Souchet\n * @version 6.4.0\n * @since 6.4.0\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class EventInstanceRepositoryImplForEventTriggerTest {\n\n    @Mock\n    private ArchiveService archiveService;\n\n    @Mock\n    private EventService eventService;\n    @Mock\n    private PersistenceService persistenceService;\n\n    @Mock\n    private Recorder recorder;\n\n    @Spy\n    @InjectMocks\n    private EventInstanceRepositoryImpl eventInstanceRepository;\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#createTimerEventTriggerInstance(STimerEventTriggerInstance)}\n     * .\n     */\n    @Test\n    public final void createEventTriggerInstance_should_create_event_trigger_instance() throws Exception {\n        // Given\n        final STimerEventTriggerInstance eventTriggerInstance = new STimerEventTriggerInstance();\n        final InsertRecord insertRecord = new InsertRecord(eventTriggerInstance);\n\n        // When\n        eventInstanceRepository.createTimerEventTriggerInstance(eventTriggerInstance);\n\n        // Then\n        verify(recorder).recordInsert(eq(insertRecord), nullable(String.class));\n    }\n\n    @Test(expected = SEventTriggerInstanceCreationException.class)\n    public final void createEventTriggerInstance_should_throw_exception_when_there_is_error() throws Exception {\n        // Given\n        final STimerEventTriggerInstance eventTriggerInstance = new STimerEventTriggerInstance();\n        doThrow(new SRecorderException(\"\")).when(recorder).recordInsert(any(InsertRecord.class),\n                nullable(String.class));\n\n        // When\n        eventInstanceRepository.createTimerEventTriggerInstance(eventTriggerInstance);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#deleteEventTriggerInstance(STimerEventTriggerInstance)}\n     * .\n     */\n    @Test\n    public final void deleteEventTriggerInstance_should_delete_event_trigger_instance() throws Exception {\n        // Given\n        final STimerEventTriggerInstance eventTriggerInstance = new STimerEventTriggerInstance();\n        final DeleteRecord insertRecord = new DeleteRecord(eventTriggerInstance);\n\n        // When\n        eventInstanceRepository.deleteEventTriggerInstance(eventTriggerInstance);\n\n        // Then\n        verify(recorder).recordDelete(eq(insertRecord), nullable(String.class));\n    }\n\n    @Test(expected = SEventTriggerInstanceDeletionException.class)\n    public final void deleteEventTriggerInstance_should_throw_exception_when_there_is_error() throws Exception {\n        // Given\n        final STimerEventTriggerInstance eventTriggerInstance = new STimerEventTriggerInstance();\n        doThrow(new SRecorderException(\"\")).when(recorder).recordDelete(any(DeleteRecord.class),\n                nullable(String.class));\n\n        // When\n        eventInstanceRepository.deleteEventTriggerInstance(eventTriggerInstance);\n    }\n\n    @Test\n    public final void getEventTriggerInstance_should_return_event_trigger_instance() throws Exception {\n        // Given\n        final long eventTriggerInstanceId = 63L;\n        final STimerEventTriggerInstance eventTriggerInstance = new STimerEventTriggerInstance();\n        final SelectByIdDescriptor<STimerEventTriggerInstance> selectByIdDescriptor = SelectDescriptorBuilder\n                .getElementById(STimerEventTriggerInstance.class,\n                        STimerEventTriggerInstance.class.getSimpleName(), eventTriggerInstanceId);\n        doReturn(eventTriggerInstance).when(persistenceService).selectById(selectByIdDescriptor);\n\n        // When\n        final STimerEventTriggerInstance result = eventInstanceRepository\n                .getEventTriggerInstance(STimerEventTriggerInstance.class, eventTriggerInstanceId);\n\n        // Then\n        assertEquals(\"Should return the result of the mock.\", eventTriggerInstance, result);\n    }\n\n    @Test\n    public final void getEventTriggerInstance_should_return_null_if_doesnt_exist() throws Exception {\n        // Given\n        final long eventTriggerInstanceId = 63L;\n        final SelectByIdDescriptor<STimerEventTriggerInstance> selectByIdDescriptor = SelectDescriptorBuilder\n                .getElementById(STimerEventTriggerInstance.class,\n                        STimerEventTriggerInstance.class.getSimpleName(), eventTriggerInstanceId);\n        doReturn(null).when(persistenceService).selectById(selectByIdDescriptor);\n\n        // When\n        final STimerEventTriggerInstance result = eventInstanceRepository\n                .getEventTriggerInstance(STimerEventTriggerInstance.class, eventTriggerInstanceId);\n\n        // Then\n        assertNull(\"Should return the result of the mock.\", result);\n    }\n\n    @Test\n    public final void getNumberOfEventTriggerInstancesByProcessInstance_should_return_the_number() throws Exception {\n        // Given\n        final int processInstanceId = 2;\n        final QueryOptions queryOptions = new QueryOptions(0, 100, STimerEventTriggerInstance.class, \"id\",\n                OrderByType.ASC);\n        doReturn(3L).when(persistenceService).getNumberOfEntities(eq(STimerEventTriggerInstance.class),\n                eq(\"ByProcessInstance\"), eq(queryOptions),\n                ArgumentMatchers.<Map<String, Object>> any());\n\n        // When\n        final long result = eventInstanceRepository.getNumberOfTimerEventTriggerInstances(processInstanceId,\n                queryOptions);\n\n        // Then\n        assertEquals(\"Should be equals to the result of the mock.\", 3L, result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public final void getNumberOfEventTriggerInstancesByProcessInstance_should_throw_exception_when_there_is_error()\n            throws Exception {\n        // Given\n        final int processInstanceId = 2;\n        final QueryOptions queryOptions = new QueryOptions(0, 100, STimerEventTriggerInstance.class, \"id\",\n                OrderByType.ASC);\n        doThrow(new SBonitaReadException(\"\")).when(persistenceService).getNumberOfEntities(\n                eq(STimerEventTriggerInstance.class), eq(\"ByProcessInstance\"),\n                eq(queryOptions),\n                any());\n\n        // When\n        eventInstanceRepository.getNumberOfTimerEventTriggerInstances(processInstanceId, queryOptions);\n    }\n\n    @Test\n    public final void searchEventTriggerInstancesByProcessInstance_should_return_the_list() throws Exception {\n        // Given\n        final int processInstanceId = 2;\n        final QueryOptions queryOptions = new QueryOptions(0, 100, STimerEventTriggerInstance.class, \"id\",\n                OrderByType.ASC);\n        final List<STimerEventTriggerInstance> triggerInstanceImpls = Arrays.asList(new STimerEventTriggerInstance());\n        doReturn(triggerInstanceImpls).when(persistenceService).searchEntity(eq(STimerEventTriggerInstance.class),\n                eq(\"ByProcessInstance\"), eq(queryOptions),\n                any());\n\n        // When\n        final List<STimerEventTriggerInstance> result = eventInstanceRepository\n                .searchTimerEventTriggerInstances(processInstanceId, queryOptions);\n\n        // Then\n        assertEquals(\"Should be equals to the result of the mock.\", triggerInstanceImpls, result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public final void searchEventTriggerInstancesByProcessInstance_should_throw_exception_when_there_is_error()\n            throws Exception {\n        // Given\n        final int processInstanceId = 2;\n        final QueryOptions queryOptions = new QueryOptions(0, 100, STimerEventTriggerInstance.class, \"id\",\n                OrderByType.ASC);\n        doThrow(new SBonitaReadException(\"\")).when(persistenceService).searchEntity(\n                eq(STimerEventTriggerInstance.class), eq(\"ByProcessInstance\"),\n                eq(queryOptions), any());\n\n        // When\n        eventInstanceRepository.searchTimerEventTriggerInstances(processInstanceId, queryOptions);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#updateEventTriggerInstance(STimerEventTriggerInstance, EntityUpdateDescriptor)}\n     * .\n     */\n    @Test\n    public final void updateEventTriggerInstance_should_update_timer_event_trigger_instance() throws Exception {\n        // Given\n        final STimerEventTriggerInstance sTimerEventTriggerInstance = new STimerEventTriggerInstance();\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        final UpdateRecord updateRecord = UpdateRecord.buildSetFields(sTimerEventTriggerInstance, descriptor);\n\n        // When\n        eventInstanceRepository.updateEventTriggerInstance(sTimerEventTriggerInstance, descriptor);\n\n        // Then\n        verify(recorder).recordUpdate(eq(updateRecord), nullable(String.class));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/event/impl/EventInstanceRepositoryImplForMessageTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.event.impl;\n\nimport static org.junit.Assert.*;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Random;\nimport java.util.stream.Collectors;\n\nimport org.bonitasoft.engine.archive.ArchiveService;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceRepository;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageInstanceCreationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageModificationException;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageEventCouple;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance;\nimport org.bonitasoft.engine.core.process.instance.recorder.SelectDescriptorBuilder;\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.persistence.*;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.bonitasoft.engine.services.PersistenceService;\nimport org.bonitasoft.engine.services.SPersistenceException;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Celine Souchet\n * @version 6.4.0\n * @since 6.4.0\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class EventInstanceRepositoryImplForMessageTest {\n\n    @Mock\n    private ArchiveService archiveService;\n\n    @Mock\n    private EventService eventService;\n    @Mock\n    private PersistenceService persistenceService;\n\n    @Mock\n    private Recorder recorder;\n\n    @InjectMocks\n    private EventInstanceRepositoryImpl eventInstanceRepository;\n\n    /**\n     * Test method for\n     * {@link EventInstanceRepository#createMessageInstance(org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance)}\n     * .\n     */\n    @Test\n    public final void createMessageInstance_should_create_message_instance() throws Exception {\n        // Given\n        final SMessageInstance sMessageInstance = new SMessageInstance();\n        final InsertRecord insertRecord = new InsertRecord(sMessageInstance);\n\n        // When\n        eventInstanceRepository.createMessageInstance(sMessageInstance);\n\n        // Then\n        verify(recorder).recordInsert(eq(insertRecord), nullable(String.class));\n    }\n\n    @Test(expected = SMessageInstanceCreationException.class)\n    public final void createMessageInstance_should_throw_exception_when_there_is_error() throws Exception {\n        // Given\n        final SMessageInstance sMessageInstance = new SMessageInstance();\n        doThrow(new SRecorderException(\"\")).when(recorder).recordInsert(any(InsertRecord.class),\n                nullable(String.class));\n\n        // When\n        eventInstanceRepository.createMessageInstance(sMessageInstance);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#deleteMessageInstance(org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance)}\n     * .\n     */\n    @Test\n    public final void deleteMessageInstance_should_delete_message_instance() throws Exception {\n        // Given\n        final SMessageInstance sMessageInstance = new SMessageInstance();\n        final DeleteRecord insertRecord = new DeleteRecord(sMessageInstance);\n\n        // When\n        eventInstanceRepository.deleteMessageInstance(sMessageInstance);\n\n        // Then\n        verify(recorder).recordDelete(eq(insertRecord), nullable(String.class));\n    }\n\n    @Test(expected = SMessageModificationException.class)\n    public final void deleteMessageInstance_should_throw_exception_when_there_is_error() throws Exception {\n        // Given\n        final SMessageInstance sMessageInstance = new SMessageInstance();\n        doThrow(new SRecorderException(\"\")).when(recorder).recordDelete(any(DeleteRecord.class),\n                nullable(String.class));\n\n        // When\n        eventInstanceRepository.deleteMessageInstance(sMessageInstance);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#resetProgressMessageInstances()}.\n     */\n    @Test\n    public final void resetProgressMessageInstances_should_update_message() throws Exception {\n        // Given\n        doReturn(3).when(persistenceService).update(\"resetProgressMessageInstances\");\n\n        // When\n        final int result = eventInstanceRepository.resetProgressMessageInstances();\n\n        // Then\n        assertEquals(\"Should return the result of the mock.\", 3, result);\n    }\n\n    @Test(expected = SMessageModificationException.class)\n    public final void resetProgressMessageInstances_should_throw_exception_when_there_is_error() throws Exception {\n        // Given\n        doThrow(new SPersistenceException(\"\")).when(persistenceService).update(\"resetProgressMessageInstances\");\n\n        // When\n        eventInstanceRepository.resetProgressMessageInstances();\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#getMessageEventCouples(int, int)}.\n     */\n    @Test\n    public final void getMessageEventCouples_should_return_message_event_couples() throws Exception {\n        // Given\n        final List<SMessageEventCouple> sMessageEventCoupleImpls = Arrays.asList(new SMessageEventCouple());\n        final SelectListDescriptor<SMessageEventCouple> selectDescriptor = SelectDescriptorBuilder\n                .getMessageEventCouples(0, 100);\n        doReturn(sMessageEventCoupleImpls).when(persistenceService).selectList(selectDescriptor);\n\n        // When\n        final List<SMessageEventCouple> result = eventInstanceRepository.getMessageEventCouples(0, 100);\n\n        // Then\n        assertEquals(\"Should return the result of the mock.\", sMessageEventCoupleImpls, result);\n    }\n\n    @Test\n    public final void getMessageEventCouples_should_return_empty_list_if_doesnt_exist() throws Exception {\n        // Given\n        final SelectListDescriptor<SMessageEventCouple> selectDescriptor = SelectDescriptorBuilder\n                .getMessageEventCouples(0, 100);\n        doReturn(Arrays.asList()).when(persistenceService).selectList(selectDescriptor);\n\n        // When\n        final List<SMessageEventCouple> result = eventInstanceRepository.getMessageEventCouples(0, 100);\n\n        // Then\n        assertTrue(\"The result must be empty.\", result.isEmpty());\n    }\n\n    @Test(expected = SEventTriggerInstanceReadException.class)\n    public final void getMessageEventCouples_should_throw_exception_when_there_is_error() throws Exception {\n        // Given\n        final SelectListDescriptor<SMessageEventCouple> selectDescriptor = SelectDescriptorBuilder\n                .getMessageEventCouples(0, 100);\n        doThrow(new SBonitaReadException(\"\")).when(persistenceService).selectList(selectDescriptor);\n\n        // When\n        eventInstanceRepository.getMessageEventCouples(0, 100);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#getMessageInstance(long)}.\n     */\n    @Test\n    public final void getMessageInstance_should_return_message_instance() throws Exception {\n        // Given\n        final long messageInstanceId = 63L;\n        final SMessageInstance sMessageInstance = new SMessageInstance();\n        final SelectByIdDescriptor<SMessageInstance> selectByIdDescriptor = SelectDescriptorBuilder.getElementById(\n                SMessageInstance.class,\n                \"MessageInstance\", messageInstanceId);\n        doReturn(sMessageInstance).when(persistenceService).selectById(selectByIdDescriptor);\n\n        // When\n        final SMessageInstance result = eventInstanceRepository.getMessageInstance(messageInstanceId);\n\n        // Then\n        assertEquals(\"Should return the result of the mock.\", sMessageInstance, result);\n    }\n\n    @Test\n    public final void deleteMessage_should_process_ids_in_batch() throws Exception {\n        // Given\n        List<Long> ids = new Random().longs(0, 350).limit(350).boxed().collect(Collectors.toList());\n\n        // When\n        eventInstanceRepository.deleteMessageInstanceByIds(ids);\n        // Then\n        verify(persistenceService, times(4)).update(anyString(), anyMap());\n    }\n\n    @Test\n    public final void getMessageInstance_should_return_null_if_doesnt_exist() throws Exception {\n        // Given\n        final long messageInstanceId = 63L;\n        final SelectByIdDescriptor<SMessageInstance> selectByIdDescriptor = SelectDescriptorBuilder.getElementById(\n                SMessageInstance.class,\n                \"MessageInstance\", messageInstanceId);\n        doReturn(null).when(persistenceService).selectById(selectByIdDescriptor);\n\n        // When\n        final SMessageInstance result = eventInstanceRepository.getMessageInstance(messageInstanceId);\n\n        // Then\n        assertNull(\"Should return the result of the mock.\", result);\n    }\n\n    @Test(expected = SMessageInstanceReadException.class)\n    public final void getMessageInstance_should_throw_exception_when_there_is_error() throws Exception {\n        // Given\n        final long messageInstanceId = 63L;\n        final SelectByIdDescriptor<SMessageInstance> selectByIdDescriptor = SelectDescriptorBuilder.getElementById(\n                SMessageInstance.class,\n                \"MessageInstance\", messageInstanceId);\n        doThrow(new SBonitaReadException(\"\")).when(persistenceService).selectById(selectByIdDescriptor);\n\n        // When\n        eventInstanceRepository.getMessageInstance(messageInstanceId);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#updateMessageInstance(org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance, org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor)}\n     * .\n     */\n    @Test\n    public final void updateMessageInstance_should_update_message_instance() throws Exception {\n        // Given\n        final SMessageInstance sMessageInstance = new SMessageInstance();\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        final UpdateRecord updateRecord = UpdateRecord.buildSetFields(sMessageInstance, descriptor);\n\n        // When\n        eventInstanceRepository.updateMessageInstance(sMessageInstance, descriptor);\n\n        // Then\n        verify(recorder).recordUpdate(eq(updateRecord), nullable(String.class));\n    }\n\n    @Test(expected = SMessageModificationException.class)\n    public final void updateMessageInstance_should_throw_exception_when_there_is_error() throws Exception {\n        // Given\n        final SMessageInstance sMessageInstance = new SMessageInstance();\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        doThrow(new SRecorderException(\"\")).when(recorder).recordUpdate(any(UpdateRecord.class),\n                nullable(String.class));\n\n        // When\n        eventInstanceRepository.updateMessageInstance(sMessageInstance, descriptor);\n    }\n\n    @Test(expected = SMessageModificationException.class)\n    public final void deleteMessageInstanceByIds_throw_exception_when_there_is_error() throws Exception {\n        // Given\n        doThrow(new SPersistenceException(\"\")).when(persistenceService).update(anyString(), anyMap());\n        // When\n        eventInstanceRepository.deleteMessageInstanceByIds(Collections.singletonList(50L));\n    }\n\n    @Test(expected = SMessageInstanceReadException.class)\n    public final void getMessageInstanceIdOlderThanCreationDateInstanceByIds_throw_exception_when_there_is_error()\n            throws Exception {\n        // Given\n        doThrow(new SBonitaReadException(\"\")).when(persistenceService).selectList(any(SelectListDescriptor.class));\n        // When\n        eventInstanceRepository.getMessageInstanceIdOlderThanCreationDate(14500L, new QueryOptions(0, 100));\n\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public final void should_throw_exception_when_query_option_have_bad_filters()\n            throws Exception {\n        eventInstanceRepository.getMessageInstanceIdOlderThanCreationDate(14500L, new QueryOptions(\n                Collections.singletonList(new FilterOption(SMessageInstance.class, \"toto\", \"testt\")), null));\n\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/event/impl/EventInstanceRepositoryImplForWaitingTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.event.impl;\n\nimport static org.junit.Assert.*;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.archive.ArchiveService;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceRepository;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventCreationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventModificationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventReadException;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent;\nimport org.bonitasoft.engine.core.process.instance.recorder.SelectDescriptorBuilder;\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.persistence.*;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.bonitasoft.engine.services.PersistenceService;\nimport org.bonitasoft.engine.services.SPersistenceException;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Celine Souchet\n * @version 6.4.0\n * @since 6.4.0\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class EventInstanceRepositoryImplForWaitingTest {\n\n    @Mock\n    private ArchiveService archiveService;\n\n    @Mock\n    private EventService eventService;\n    @Mock\n    private PersistenceService persistenceService;\n\n    @Mock\n    private Recorder recorder;\n\n    @Spy\n    @InjectMocks\n    private EventInstanceRepositoryImpl eventInstanceRepository;\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#createWaitingEvent(org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent)}\n     * .\n     */\n    @Test\n    public final void createWaitingEvent_should_create_waiting_instance() throws Exception {\n        // Given\n        final SWaitingSignalEvent sWaitingEventImpl = new SWaitingSignalEvent();\n        final InsertRecord insertRecord = new InsertRecord(sWaitingEventImpl);\n\n        // When\n        eventInstanceRepository.createWaitingEvent(sWaitingEventImpl);\n\n        // Then\n        verify(recorder).recordInsert(eq(insertRecord), nullable(String.class));\n    }\n\n    @Test(expected = SWaitingEventCreationException.class)\n    public final void createWaitingEvent_should_throw_exception_when_there_is_error() throws Exception {\n        // Given\n        final SWaitingMessageEvent sWaitingEventImpl = new SWaitingMessageEvent();\n        doThrow(new SRecorderException(\"\")).when(recorder).recordInsert(any(InsertRecord.class),\n                nullable(String.class));\n\n        // When\n        eventInstanceRepository.createWaitingEvent(sWaitingEventImpl);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#deleteWaitingEvent(org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent)}\n     * .\n     */\n    @Test\n    public final void deleteWaitingEvent_should_delete_waiting_event() throws Exception {\n        // Given\n        final SWaitingMessageEvent sWaitingEventImpl = new SWaitingMessageEvent();\n        final DeleteRecord insertRecord = new DeleteRecord(sWaitingEventImpl);\n\n        // When\n        eventInstanceRepository.deleteWaitingEvent(sWaitingEventImpl);\n\n        // Then\n        verify(recorder).recordDelete(eq(insertRecord), nullable(String.class));\n    }\n\n    @Test(expected = SWaitingEventModificationException.class)\n    public final void deleteWaitingEvent_should_throw_exception_when_there_is_error() throws Exception {\n        // Given\n        final SWaitingSignalEvent sWaitingEventImpl = new SWaitingSignalEvent();\n        doThrow(new SRecorderException(\"\")).when(recorder).recordDelete(any(DeleteRecord.class),\n                nullable(String.class));\n\n        // When\n        eventInstanceRepository.deleteWaitingEvent(sWaitingEventImpl);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#resetInProgressWaitingEvents()}.\n     */\n    @Test\n    public final void resetInProgressWaitingEvents_should_update_waiting_events() throws Exception {\n        // Given\n        doReturn(3).when(persistenceService).update(\"resetInProgressWaitingEvents\");\n\n        // When\n        final int result = eventInstanceRepository.resetInProgressWaitingEvents();\n\n        // Then\n        assertEquals(\"Should return the result of the mock.\", 3, result);\n    }\n\n    @Test(expected = SWaitingEventModificationException.class)\n    public final void resetInProgressWaitingEvents_should_throw_exception_when_there_is_error() throws Exception {\n        // Given\n        doThrow(new SPersistenceException(\"\")).when(persistenceService).update(\"resetInProgressWaitingEvents\");\n\n        // When\n        eventInstanceRepository.resetInProgressWaitingEvents();\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#getNumberOfWaitingEvents(java.lang.Class, org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public final void getNumberOfWaitingEvents_should_return_the_number() throws Exception {\n        // Given\n        final QueryOptions queryOptions = new QueryOptions(0, 100, SWaitingEvent.class, \"id\", OrderByType.ASC);\n        doReturn(2L).when(persistenceService).getNumberOfEntities(SWaitingEvent.class, queryOptions, null);\n\n        // When\n        final long result = eventInstanceRepository.getNumberOfWaitingEvents(SWaitingEvent.class, queryOptions);\n\n        // Then\n        assertEquals(\"Should be equals to the result of the mock.\", 2L, result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public final void getNumberOfWaitingEvents_should_throw_exception_when_there_is_error() throws Exception {\n        // Given\n        final QueryOptions queryOptions = new QueryOptions(0, 100, SWaitingEvent.class, \"id\", OrderByType.ASC);\n        doThrow(new SBonitaReadException(\"\")).when(persistenceService).getNumberOfEntities(SWaitingEvent.class,\n                queryOptions, null);\n\n        // When\n        eventInstanceRepository.getNumberOfWaitingEvents(SWaitingEvent.class, queryOptions);\n    }\n\n    /**\n     * Test method for\n     * {@link EventInstanceRepository#getStartWaitingEventsOfProcessDefinition(long)}\n     * .\n     */\n    @Test\n    public final void searchStartWaitingEvents_should_return_the_list() throws Exception {\n        // Given\n        final List<SWaitingSignalEvent> sWaitingSignalEvents = Arrays.asList(new SWaitingSignalEvent());\n        // event sub-processes are not returned:\n        doReturn(sWaitingSignalEvents).when(persistenceService).selectList(any());\n\n        // When\n        final List<SWaitingEvent> result = eventInstanceRepository\n                .getStartWaitingEventsOfProcessDefinition(9L);\n\n        // Then\n        assertEquals(\"Should be equals to the result of the mock.\", sWaitingSignalEvents, result);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#getWaitingMessage(long)}.\n     */\n    @Test\n    public final void getWaitingMessage_should_return_waiting_message() throws Exception {\n        // Given\n        final long waitingMessageId = 63L;\n        final SWaitingMessageEvent sWaitingMessageEvent = new SWaitingMessageEvent();\n        final SelectByIdDescriptor<SWaitingMessageEvent> selectByIdDescriptor = SelectDescriptorBuilder.getElementById(\n                SWaitingMessageEvent.class,\n                \"WaitingMessageEvent\", waitingMessageId);\n        doReturn(sWaitingMessageEvent).when(persistenceService).selectById(selectByIdDescriptor);\n\n        // When\n        final SWaitingMessageEvent result = eventInstanceRepository.getWaitingMessage(waitingMessageId);\n\n        // Then\n        assertEquals(\"Should return the result of the mock.\", sWaitingMessageEvent, result);\n    }\n\n    @Test\n    public final void getWaitingMessage_should_return_null_if_doesnt_exist() throws Exception {\n        // Given\n        final long waitingMessageId = 63L;\n        final SelectByIdDescriptor<SWaitingMessageEvent> selectByIdDescriptor = SelectDescriptorBuilder.getElementById(\n                SWaitingMessageEvent.class,\n                \"WaitingMessageEvent\", waitingMessageId);\n        doReturn(null).when(persistenceService).selectById(selectByIdDescriptor);\n\n        // When\n        final SWaitingMessageEvent result = eventInstanceRepository.getWaitingMessage(waitingMessageId);\n\n        // Then\n        assertNull(\"Should return the result of the mock.\", result);\n    }\n\n    @Test(expected = SWaitingEventReadException.class)\n    public final void getWaitingMessage_should_throw_exception_when_there_is_error() throws Exception {\n        // Given\n        final long waitingMessageId = 63L;\n        final SelectByIdDescriptor<SWaitingMessageEvent> selectByIdDescriptor = SelectDescriptorBuilder.getElementById(\n                SWaitingMessageEvent.class,\n                \"WaitingMessageEvent\", waitingMessageId);\n        doThrow(new SBonitaReadException(\"\")).when(persistenceService).selectById(selectByIdDescriptor);\n\n        // When\n        eventInstanceRepository.getWaitingMessage(waitingMessageId);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#getWaitingSignalEvents(java.lang.String, int, int)}.\n     */\n    @Test\n    public final void getWaitingSignalEvents_should_return_waiting_signal_event() throws Exception {\n        // Given\n        final String signalName = \"name\";\n        final List<SWaitingSignalEvent> waitingSignalEventImpls = Arrays.asList(new SWaitingSignalEvent());\n        final SelectListDescriptor<SWaitingSignalEvent> selectDescriptor = SelectDescriptorBuilder\n                .getListeningSignals(signalName, 0, 100);\n        doReturn(waitingSignalEventImpls).when(persistenceService).selectList(selectDescriptor);\n\n        // When\n        final List<SWaitingSignalEvent> result = eventInstanceRepository.getWaitingSignalEvents(signalName, 0, 100);\n\n        // Then\n        assertEquals(\"Should return the result of the mock.\", waitingSignalEventImpls, result);\n    }\n\n    @Test\n    public final void getWaitingSignalEvents_should_return_empty_list_if_doesnt_exist() throws Exception {\n        // Given\n        final String signalName = \"name\";\n        final SelectListDescriptor<SWaitingSignalEvent> selectDescriptor = SelectDescriptorBuilder\n                .getListeningSignals(signalName, 0, 100);\n        doReturn(Arrays.asList()).when(persistenceService).selectList(selectDescriptor);\n\n        // When\n        final List<SWaitingSignalEvent> result = eventInstanceRepository.getWaitingSignalEvents(signalName, 0, 100);\n\n        // Then\n        assertTrue(\"The result must be empty.\", result.isEmpty());\n    }\n\n    @Test(expected = SEventTriggerInstanceReadException.class)\n    public final void getEventTriggerInstances_should_throw_exception_when_there_is_error() throws Exception {\n        // Given\n        final String signalName = \"name\";\n        final SelectListDescriptor<SWaitingSignalEvent> selectDescriptor = SelectDescriptorBuilder\n                .getListeningSignals(signalName, 0, 100);\n        doThrow(new SBonitaReadException(\"\")).when(persistenceService).selectList(selectDescriptor);\n\n        // When\n        eventInstanceRepository.getWaitingSignalEvents(signalName, 0, 100);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#searchWaitingEvents(java.lang.Class, org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public final void searchWaitingEvents_should_return_the_list() throws Exception {\n        // Given\n        final QueryOptions queryOptions = new QueryOptions(0, 100, SWaitingEvent.class, \"id\", OrderByType.ASC);\n        final List<SWaitingSignalEvent> sWaitingSignalEvents = Arrays.asList(new SWaitingSignalEvent());\n        doReturn(sWaitingSignalEvents).when(persistenceService).searchEntity(SWaitingEvent.class, queryOptions, null);\n\n        // When\n        final List<SWaitingEvent> result = eventInstanceRepository.searchWaitingEvents(SWaitingEvent.class,\n                queryOptions);\n\n        // Then\n        assertEquals(\"Should be equals to the result of the mock.\", sWaitingSignalEvents, result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public final void searchWaitingEvents_should_throw_exception_when_there_is_error() throws Exception {\n        // Given\n        final QueryOptions queryOptions = new QueryOptions(0, 100, SWaitingEvent.class, \"id\", OrderByType.ASC);\n        doThrow(new SBonitaReadException(\"\")).when(persistenceService).searchEntity(SWaitingEvent.class, queryOptions,\n                null);\n\n        // When\n        eventInstanceRepository.searchWaitingEvents(SWaitingEvent.class, queryOptions);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#updateWaitingMessage(org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent, org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor)}\n     * .\n     */\n    @Test\n    public final void updateWaitingMessage_should_update_waiting_message() throws Exception {\n        // Given\n        final SWaitingMessageEvent waitingMessageEventImpl = new SWaitingMessageEvent();\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        final UpdateRecord updateRecord = UpdateRecord.buildSetFields(waitingMessageEventImpl, descriptor);\n\n        // When\n        eventInstanceRepository.updateWaitingMessage(waitingMessageEventImpl, descriptor);\n\n        // Then\n        verify(recorder).recordUpdate(eq(updateRecord), nullable(String.class));\n    }\n\n    @Test(expected = SWaitingEventModificationException.class)\n    public final void updateWaitingMessage_should_throw_exception_when_there_is_error() throws Exception {\n        // Given\n        final SWaitingMessageEvent waitingMessageEventImpl = new SWaitingMessageEvent();\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        doThrow(new SRecorderException(\"\")).when(recorder).recordUpdate(any(UpdateRecord.class),\n                nullable(String.class));\n\n        // When\n        eventInstanceRepository.updateWaitingMessage(waitingMessageEventImpl, descriptor);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/event/impl/EventInstanceServiceImplForWaitingTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.event.impl;\n\nimport static org.mockito.Mockito.*;\n\nimport java.util.Collections;\n\nimport io.micrometer.core.instrument.MeterRegistry;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceRepository;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventModificationException;\nimport org.bonitasoft.engine.core.process.instance.model.event.SIntermediateCatchEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceService;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class EventInstanceServiceImplForWaitingTest {\n\n    @Mock\n    private EventInstanceRepository instanceRepository;\n\n    @Mock\n    private MeterRegistry meterRegistry;\n\n    @Mock\n    private DataInstanceService dataInstanceService;\n\n    private EventInstanceServiceImpl eventInstanceServiceImpl;\n\n    @Before\n    public void setUp() {\n        eventInstanceServiceImpl = spy(\n                new EventInstanceServiceImpl(instanceRepository, dataInstanceService, meterRegistry));\n    }\n\n    @Test\n    public final void deleteWaitingEvents_should_delete_waiting_events() throws Exception {\n        // Given\n        final SIntermediateCatchEventInstance sIntermediateCatchEventInstance = new SIntermediateCatchEventInstance();\n        final SWaitingMessageEvent waitingMessageEventImpl = new SWaitingMessageEvent();\n        doReturn(Collections.singletonList(waitingMessageEventImpl)).doReturn(Collections.emptyList())\n                .when(instanceRepository)\n                .getWaitingEventsForFlowNodeId(anyLong());\n        doNothing().when(eventInstanceServiceImpl).deleteWaitingEvent(waitingMessageEventImpl);\n\n        // When\n        eventInstanceServiceImpl.deleteWaitingEvents(sIntermediateCatchEventInstance);\n\n        // Then\n        verify(eventInstanceServiceImpl).deleteWaitingEvent(waitingMessageEventImpl);\n    }\n\n    @Test(expected = SWaitingEventModificationException.class)\n    public final void deleteWaitingEvents_should_throw_exception_when_cant_delete_waiting_event() throws Exception {\n        // Given\n        final SIntermediateCatchEventInstance sIntermediateCatchEventInstance = new SIntermediateCatchEventInstance();\n        final SWaitingSignalEvent waitingMessageEventImpl = new SWaitingSignalEvent();\n        doReturn(Collections.singletonList(waitingMessageEventImpl)).doReturn(Collections.emptyList())\n                .when(instanceRepository)\n                .getWaitingEventsForFlowNodeId(anyLong());\n        doThrow(new SWaitingEventModificationException(new Exception(\"\"))).when(instanceRepository)\n                .deleteWaitingEvent(waitingMessageEventImpl);\n        // When\n        eventInstanceServiceImpl.deleteWaitingEvents(sIntermediateCatchEventInstance);\n    }\n\n    @Test(expected = SEventTriggerInstanceReadException.class)\n    public final void deleteWaitingEvents_should_throw_exception_when_cant_search_waiting_event() throws Exception {\n        // Given\n        final SIntermediateCatchEventInstance sIntermediateCatchEventInstance = new SIntermediateCatchEventInstance();\n        doThrow(new SEventTriggerInstanceReadException(new Exception(\"\"))).when(instanceRepository)\n                .getWaitingEventsForFlowNodeId(anyLong());\n        // When\n        eventInstanceServiceImpl.deleteWaitingEvents(sIntermediateCatchEventInstance);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/event/impl/EventInstanceServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.event.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceServiceImpl.BONITA_BPMENGINE_MESSAGE_SENT;\nimport static org.bonitasoft.engine.data.instance.api.DataInstanceContainer.MESSAGE_INSTANCE;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport io.micrometer.core.instrument.Clock;\nimport io.micrometer.core.instrument.MeterRegistry;\nimport io.micrometer.core.instrument.simple.SimpleMeterRegistry;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceRepository;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageInstanceCreationException;\nimport org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceService;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class EventInstanceServiceImplTest {\n\n    @Mock\n    private EventInstanceRepository instanceRepository;\n\n    @Mock\n    private DataInstanceService dataInstanceService;\n\n    private EventInstanceServiceImpl eventInstanceServiceImpl;\n\n    private final MeterRegistry meterRegistry = new SimpleMeterRegistry(\n            // So that micrometer updates its counters every 1 ms:\n            k -> k.equals(\"simple.step\") ? Duration.ofMillis(1).toString() : null,\n            Clock.SYSTEM);\n\n    @Before\n    public void setUp() {\n        eventInstanceServiceImpl = new EventInstanceServiceImpl(instanceRepository, dataInstanceService, meterRegistry);\n    }\n\n    @Test\n    public final void deleteMessageAndDataInstanceOlderCreationDate_should_call_expected_method_and_return_nbMessage_deleted()\n            throws Exception {\n\n        // Given\n        long creationDate = Instant.now().toEpochMilli();\n\n        List<Long> idsToBeDeleted = Arrays.asList(1L, 2L);\n        when(instanceRepository.getMessageInstanceIdOlderThanCreationDate(eq(creationDate), any()))\n                .thenReturn(idsToBeDeleted);\n\n        Integer totalRemoved = eventInstanceServiceImpl.deleteMessageAndDataInstanceOlderThanCreationDate(creationDate,\n                QueryOptions.countQueryOptions());\n\n        assertThat(totalRemoved).isEqualTo(idsToBeDeleted.size());\n        verify(instanceRepository).deleteMessageInstanceByIds(idsToBeDeleted);\n\n        verify(dataInstanceService).deleteLocalDataInstances(1L, MESSAGE_INSTANCE.name(), true);\n        verify(dataInstanceService).deleteLocalDataInstances(2L, MESSAGE_INSTANCE.name(), true);\n        verify(dataInstanceService).deleteLocalArchivedDataInstances(1L,\n                MESSAGE_INSTANCE.name());\n        verify(dataInstanceService).deleteLocalArchivedDataInstances(2L,\n                MESSAGE_INSTANCE.name());\n    }\n\n    @Test\n    public final void should_count_message_thrown() throws SMessageInstanceCreationException {\n\n        eventInstanceServiceImpl.createMessageInstance(new SMessageInstance());\n        eventInstanceServiceImpl.createMessageInstance(new SMessageInstance());\n\n        assertThat(meterRegistry.find(BONITA_BPMENGINE_MESSAGE_SENT).counter().count()).isEqualTo(2);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/impl/ActivityInstanceServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.impl;\n\nimport static org.assertj.core.api.Assertions.*;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityReadException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeModificationException;\nimport org.bonitasoft.engine.core.process.instance.model.SActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstanceStateCounter;\nimport org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.builder.SFlowNodeInstanceBuilderFactory;\nimport org.bonitasoft.engine.core.process.instance.model.builder.impl.SUserTaskInstanceBuilderFactoryImpl;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.bonitasoft.engine.services.PersistenceService;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.ArgumentMatchers;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ActivityInstanceServiceImplTest {\n\n    final static String DISPLAY_NAME_255 = \"123456789 123456789 123456789 123456789 123456789 123456789 123456789 12345123456789 123456789 123456789 123456789 123456789 123456789 123456789 12345123456789 123456789 123456789 123456789 123456789 123456789 123456789 12345000000000000000000000000000000\";\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Mock\n    private PersistenceService persistenceService;\n\n    @Mock\n    private Recorder recorder;\n\n    @InjectMocks\n    private ActivityInstanceServiceImpl activityInstanceServiceImpl;\n\n    private final SFlowNodeInstanceBuilderFactory keyProvider = new SUserTaskInstanceBuilderFactoryImpl();\n\n    @Mock\n    private SFlowNodeInstance sFlowNodeInstance;\n\n    @Test\n    public void getPossibleUserIdsOfPendingTasks() throws Exception {\n        final List<Long> sUserIds = new ArrayList<Long>();\n        Collections.addAll(sUserIds, 78l, 2l, 5l, 486l);\n        when(persistenceService.selectList(ArgumentMatchers.<SelectListDescriptor<Long>> any())).thenReturn(sUserIds);\n\n        final List<Long> userIds = activityInstanceServiceImpl.getPossibleUserIdsOfPendingTasks(2, 0, 10);\n        assertEquals(sUserIds, userIds);\n    }\n\n    @Test(expected = SActivityReadException.class)\n    public void throwExceptionwhenGettingPossibleUserIdsOfPendingTasksDueToPersistenceException() throws Exception {\n        when(persistenceService.selectList(ArgumentMatchers.<SelectListDescriptor<Long>> any()))\n                .thenThrow(new SBonitaReadException(\"database out\"));\n        activityInstanceServiceImpl.getPossibleUserIdsOfPendingTasks(2, 0, 10);\n    }\n\n    @Test\n    public void getEmptyPossibleUserIdsOfPendingTasks() throws Exception {\n        when(persistenceService.selectList(ArgumentMatchers.<SelectListDescriptor<Long>> any()))\n                .thenReturn(Collections.<Long> emptyList());\n\n        final List<Long> userIds = activityInstanceServiceImpl.getPossibleUserIdsOfPendingTasks(2, 0, 10);\n        assertEquals(Collections.emptyList(), userIds);\n    }\n\n    @Test\n    public void updateDisplayName_should_truncate_when_display_name_is_bigger_than_75_characters() throws Exception {\n        // given\n        final String displayName = DISPLAY_NAME_255 + \"__\";\n        final SFlowNodeInstance flowNode = mock(SFlowNodeInstance.class);\n\n        // when\n        activityInstanceServiceImpl.updateDisplayName(flowNode, displayName);\n\n        // then\n        // the value must be truncated to 75 characters\n        checkFlowNodeUpdate(keyProvider.getDisplayNameKey(), DISPLAY_NAME_255);\n    }\n\n    private void checkFlowNodeUpdate(final String attributeKey, final String expectedValue) throws SRecorderException {\n        final ArgumentCaptor<UpdateRecord> recordCaptor = ArgumentCaptor.forClass(UpdateRecord.class);\n        verify(recorder, times(1)).recordUpdate(recordCaptor.capture(), anyString());\n        final Object actualValue = recordCaptor.getValue().getFields().get(attributeKey);\n        assertThat(actualValue).isEqualTo(expectedValue);\n    }\n\n    @Test\n    public void updateDisplayName_should_not_change_value_when_display_name_is_lower_than_255_characters()\n            throws Exception {\n        // given\n        final String displayName = \"simple task\";\n        final SFlowNodeInstance flowNode = mock(SFlowNodeInstance.class);\n\n        // when\n        activityInstanceServiceImpl.updateDisplayName(flowNode, displayName);\n\n        // then\n        // keep original value\n        checkFlowNodeUpdate(keyProvider.getDisplayNameKey(), displayName);\n    }\n\n    @Test\n    public void updateDisplayName_should_not_change_value_when_display_name_is_255_characters_long() throws Exception {\n        // given\n        final SFlowNodeInstance flowNode = mock(SFlowNodeInstance.class);\n\n        // when\n        activityInstanceServiceImpl.updateDisplayName(flowNode, DISPLAY_NAME_255);\n\n        // then\n        // keep original value\n        checkFlowNodeUpdate(keyProvider.getDisplayNameKey(), DISPLAY_NAME_255);\n    }\n\n    @Test\n    public void updateDisplayDescription_should_truncate_when_display_description_is_bigger_than_255_characters()\n            throws Exception {\n        // given\n        final String displayDescr255 = \"123456789 123456789 123456789 123456789 123456789 \"\n                + \"123456789 123456789 123456789 123456789 123456789 \"\n                + \"123456789 123456789 123456789 123456789 123456789 \"\n                + \"123456789 123456789 123456789 123456789 123456789 \"\n                + \"123456789 123456789 123456789 123456789 123456789 12345\";\n        final String displayDescr = displayDescr255 + displayDescr255;\n        final SFlowNodeInstance flowNode = mock(SFlowNodeInstance.class);\n\n        // when\n        activityInstanceServiceImpl.updateDisplayDescription(flowNode, displayDescr);\n\n        // then\n        // the value must be truncated to 255 characters\n        checkFlowNodeUpdate(keyProvider.getDisplayDescriptionKey(), displayDescr255);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.instance.impl.ActivityInstanceServiceImpl#searchAssignedAndPendingHumanTasksFor(long, long, org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public void searchAssignedAndPendingHumanTasksFor() throws Exception {\n        // Given\n        final long rootProcessDefinitionId = 10;\n        final long userId = 6;\n        final QueryOptions options = new QueryOptions(0, 10);\n        final Map<String, Object> parameters = new HashMap<String, Object>();\n        parameters.put(\"userId\", userId);\n        parameters.put(\"rootProcessDefinitionId\", rootProcessDefinitionId);\n        when(persistenceService.searchEntity(SHumanTaskInstance.class, \"AssignedAndPendingByRootProcessFor\", options,\n                parameters)).thenReturn(\n                        new ArrayList<SHumanTaskInstance>());\n\n        // When\n        final List<SHumanTaskInstance> result = activityInstanceServiceImpl\n                .searchAssignedAndPendingHumanTasksFor(rootProcessDefinitionId, userId, options);\n\n        // Then\n        assertNotNull(result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void searchAssignedAndPendingHumanTasksForThrowException() throws Exception {\n        // Given\n        final long rootProcessDefinitionId = 10;\n        final long userId = 6;\n        final QueryOptions options = new QueryOptions(0, 10);\n        final Map<String, Object> parameters = new HashMap<String, Object>();\n        parameters.put(\"userId\", userId);\n        parameters.put(\"rootProcessDefinitionId\", rootProcessDefinitionId);\n        when(persistenceService.searchEntity(SHumanTaskInstance.class, \"AssignedAndPendingByRootProcessFor\", options,\n                parameters)).thenThrow(\n                        new SBonitaReadException(\"\"));\n\n        // When\n        activityInstanceServiceImpl.searchAssignedAndPendingHumanTasksFor(rootProcessDefinitionId, userId, options);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.instance.impl.ActivityInstanceServiceImpl#getNumberOfAssignedAndPendingHumanTasksFor(long, long, org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public void getNumberOfAssignedAndPendingHumanTasksFor() throws Exception {\n        // Given\n        final long rootProcessDefinitionId = 10;\n        final long userId = 6;\n        final QueryOptions options = new QueryOptions(0, 10);\n        final Map<String, Object> parameters = new HashMap<String, Object>();\n        parameters.put(\"userId\", userId);\n        parameters.put(\"rootProcessDefinitionId\", rootProcessDefinitionId);\n        when(persistenceService.getNumberOfEntities(SHumanTaskInstance.class, \"AssignedAndPendingByRootProcessFor\",\n                options, parameters)).thenReturn(1L);\n\n        // When\n        final long result = activityInstanceServiceImpl\n                .getNumberOfAssignedAndPendingHumanTasksFor(rootProcessDefinitionId, userId, options);\n\n        // Then\n        assertEquals(1L, result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getNumberOfAssignedAndPendingHumanTasksForThrowException() throws Exception {\n        // Given\n        final long rootProcessDefinitionId = 10;\n        final long userId = 6;\n        final QueryOptions options = new QueryOptions(0, 10);\n        final Map<String, Object> parameters = new HashMap<String, Object>();\n        parameters.put(\"userId\", userId);\n        parameters.put(\"rootProcessDefinitionId\", rootProcessDefinitionId);\n        when(persistenceService.getNumberOfEntities(SHumanTaskInstance.class, \"AssignedAndPendingByRootProcessFor\",\n                options, parameters)).thenThrow(\n                        new SBonitaReadException(\"\"));\n\n        // When\n        activityInstanceServiceImpl.getNumberOfAssignedAndPendingHumanTasksFor(rootProcessDefinitionId, userId,\n                options);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.instance.impl.ActivityInstanceServiceImpl#searchAssignedAndPendingHumanTasks(long, org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public void searchAssignedAndPendingHumanTasks() throws Exception {\n        // Given\n        final long rootProcessDefinitionId = 10;\n        final QueryOptions options = new QueryOptions(0, 10);\n        final Map<String, Object> parameters = Collections.singletonMap(\"rootProcessDefinitionId\",\n                (Object) rootProcessDefinitionId);\n        when(persistenceService.searchEntity(SHumanTaskInstance.class, \"AssignedAndPendingByRootProcess\", options,\n                parameters)).thenReturn(\n                        new ArrayList<SHumanTaskInstance>());\n\n        // When\n        final List<SHumanTaskInstance> result = activityInstanceServiceImpl\n                .searchAssignedAndPendingHumanTasks(rootProcessDefinitionId, options);\n\n        // Then\n        assertNotNull(result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void searchAssignedAndPendingHumanTasksThrowException() throws Exception {\n        // Given\n        final long rootProcessDefinitionId = 10;\n        final QueryOptions options = new QueryOptions(0, 10);\n        final Map<String, Object> parameters = Collections.singletonMap(\"rootProcessDefinitionId\",\n                (Object) rootProcessDefinitionId);\n        when(persistenceService.searchEntity(SHumanTaskInstance.class, \"AssignedAndPendingByRootProcess\", options,\n                parameters)).thenThrow(\n                        new SBonitaReadException(\"\"));\n\n        // When\n        activityInstanceServiceImpl.searchAssignedAndPendingHumanTasks(rootProcessDefinitionId, options);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.core.process.instance.impl.ActivityInstanceServiceImpl#getNumberOfAssignedAndPendingHumanTasks(long, org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public void getNumberOfAssignedAndPendingHumanTasks() throws Exception {\n        // Given\n        final long rootProcessDefinitionId = 10;\n        final QueryOptions options = new QueryOptions(0, 10);\n        final Map<String, Object> parameters = Collections.singletonMap(\"rootProcessDefinitionId\",\n                (Object) rootProcessDefinitionId);\n        when(persistenceService.getNumberOfEntities(SHumanTaskInstance.class, \"AssignedAndPendingByRootProcess\",\n                options, parameters)).thenReturn(1L);\n\n        // When\n        final long result = activityInstanceServiceImpl.getNumberOfAssignedAndPendingHumanTasks(rootProcessDefinitionId,\n                options);\n\n        // Then\n        assertEquals(1L, result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getNumberOfAssignedAndPendingHumanTasksThrowException() throws Exception {\n        // Given\n        final long rootProcessDefinitionId = 10;\n        final QueryOptions options = new QueryOptions(0, 10);\n        final Map<String, Object> parameters = Collections.singletonMap(\"rootProcessDefinitionId\",\n                (Object) rootProcessDefinitionId);\n        when(persistenceService.getNumberOfEntities(SHumanTaskInstance.class, \"AssignedAndPendingByRootProcess\",\n                options, parameters)).thenThrow(\n                        new SBonitaReadException(\"\"));\n\n        // When\n        activityInstanceServiceImpl.getNumberOfAssignedAndPendingHumanTasks(rootProcessDefinitionId, options);\n    }\n\n    @Test\n    public void getNumberOfFlownodesOfProcessDefinitionInAllStates_should_return_empty_collections_if_no_results()\n            throws SBonitaReadException {\n        when(persistenceService.selectList(ArgumentMatchers.<SelectListDescriptor<Map<String, Object>>> any()))\n                .thenReturn(\n                        Collections.<Map<String, Object>> emptyList());\n\n        final List<SFlowNodeInstanceStateCounter> numberOfFlownodesInState = activityInstanceServiceImpl\n                .getNumberOfFlownodesOfProcessDefinitionInAllStates(1L);\n        assertThat(numberOfFlownodesInState).isEmpty();\n    }\n\n    @Test\n    public void getNumberOfFlownodesInAllStates_should_return_empty_collections_if_no_results()\n            throws SBonitaReadException {\n        when(persistenceService.selectList(ArgumentMatchers.<SelectListDescriptor<Map<String, Object>>> any()))\n                .thenReturn(\n                        Collections.<Map<String, Object>> emptyList());\n\n        final List<SFlowNodeInstanceStateCounter> numberOfFlownodesInState = activityInstanceServiceImpl\n                .getNumberOfFlownodesInAllStates(2L);\n        assertThat(numberOfFlownodesInState).isEmpty();\n    }\n\n    @Test\n    public void getNumberOfArchivedFlownodesInAllStates_should_return_empty_collections_if_no_results()\n            throws SBonitaReadException {\n        when(persistenceService.selectList(ArgumentMatchers.<SelectListDescriptor<Map<String, Object>>> any()))\n                .thenReturn(\n                        Collections.<Map<String, Object>> emptyList());\n\n        final List<SFlowNodeInstanceStateCounter> numberOfFlownodesInState = activityInstanceServiceImpl\n                .getNumberOfArchivedFlownodesInAllStates(2L);\n        assertThat(numberOfFlownodesInState).isEmpty();\n    }\n\n    @Test\n    public void should_update_due_date_on_right_flownode_with_right_update_event_message() throws Exception {\n        //when\n        activityInstanceServiceImpl.setExpectedEndDate(sFlowNodeInstance, 123L);\n\n        ArgumentCaptor<UpdateRecord> updateRecordArgumentCaptor = ArgumentCaptor.forClass(UpdateRecord.class);\n\n        //then\n        verify(recorder).recordUpdate(updateRecordArgumentCaptor.capture(),\n                eq(FlowNodeInstanceService.EXPECTED_END_DATE_MODIFIED));\n        assertThat(updateRecordArgumentCaptor.getValue().getEntity()).as(\"should update entity\")\n                .isEqualTo(sFlowNodeInstance);\n        assertThat(updateRecordArgumentCaptor.getValue().getFields())\n                .as(\"should update expectedEndDate and lastUpdateDate fields\")\n                .containsKey(\"expectedEndDate\")\n                .containsKey(\"lastUpdateDate\");\n        assertThat(updateRecordArgumentCaptor.getValue().getFields().get(\"expectedEndDate\"))\n                .as(\"expectedEndDate should have the new value\")\n                .isEqualTo(123L);\n    }\n\n    @Test\n    public void should_updateExpectedEndDate_throw_SFlowNodeModificationException_when_recorder_fails()\n            throws Exception {\n        //given\n        doThrow(SRecorderException.class).when(recorder).recordUpdate(any(UpdateRecord.class), anyString());\n\n        //expect\n        expectedException.expect(SFlowNodeModificationException.class);\n\n        //when\n        activityInstanceServiceImpl.setExpectedEndDate(sFlowNodeInstance, 123L);\n    }\n\n    @Test\n    public void should_search_pending_tasks_assigned_to_a_user() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        List<SHumanTaskInstance> expectedResult = new ArrayList<>();\n        when(persistenceService.searchEntity(SHumanTaskInstance.class, \"PendingAssignedTo\", options,\n                Collections.singletonMap(\"userId\", 61L)))\n                .thenReturn(expectedResult);\n\n        // When\n        final List<SHumanTaskInstance> result = activityInstanceServiceImpl.searchPendingTasksAssignedTo(61L, options);\n\n        // Then\n        assertThat(result).describedAs(\"Human tasks list\").isSameAs(expectedResult);\n    }\n\n    @Test\n    public void should_search_pending_tasks_assigned_to_a_user_throw_exception_on_persitence_error() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        when(persistenceService.searchEntity(SHumanTaskInstance.class, \"PendingAssignedTo\", options,\n                Collections.singletonMap(\"userId\", 99L)))\n                .thenThrow(new SBonitaReadException(\"Fake for test\"));\n\n        // When\n        Throwable thrown = catchThrowable(() -> {\n            activityInstanceServiceImpl.searchPendingTasksAssignedTo(99L, options);\n        });\n\n        // Then\n        assertThat(thrown)\n                .isInstanceOf(SBonitaReadException.class)\n                .hasMessageContaining(\"Fake for test\");\n    }\n\n    @Test\n    public void should_count_number_of_pending_tasks_assigned_to_user() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        when(persistenceService.getNumberOfEntities(SHumanTaskInstance.class, \"PendingAssignedTo\", options,\n                Collections.singletonMap(\"userId\", 365L)))\n                .thenReturn(14L);\n\n        // When\n        final long count = activityInstanceServiceImpl.getNumberOfPendingTasksAssignedTo(365L, options);\n\n        // Then\n        assertThat(count).describedAs(\"Number of human tasks\").isEqualTo(14L);\n    }\n\n    @Test\n    public void should_count_number_of_pending_tasks_assigned_to_user_throw_exception_on_persitence_error()\n            throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        when(persistenceService.getNumberOfEntities(SHumanTaskInstance.class, \"PendingAssignedTo\", options,\n                Collections.singletonMap(\"userId\", 3365L)))\n                .thenThrow(new SBonitaReadException(\"Fake for test\"));\n\n        // When\n        Throwable thrown = catchThrowable(() -> {\n            activityInstanceServiceImpl.getNumberOfPendingTasksAssignedTo(3365L, options);\n        });\n\n        // Then\n        assertThat(thrown)\n                .isInstanceOf(SBonitaReadException.class)\n                .hasMessageContaining(\"Fake for test\");\n    }\n\n    // === Tests for lastUpdateDate on ActivityInstanceServiceImpl methods ===\n\n    @Test\n    public void should_assignHumanTask_update_lastUpdateDate() throws Exception {\n        // Given\n        SHumanTaskInstance humanTask = mock(SHumanTaskInstance.class);\n        when(persistenceService.selectById(any())).thenReturn(humanTask);\n\n        // When\n        activityInstanceServiceImpl.assignHumanTask(123L, 456L);\n\n        // Then\n        ArgumentCaptor<UpdateRecord> captor = ArgumentCaptor.forClass(UpdateRecord.class);\n        verify(recorder).recordUpdate(captor.capture(), anyString());\n        assertThat(captor.getValue().getFields().keySet())\n                .contains(\"assigneeId\", \"claimedDate\", \"lastUpdateDate\");\n    }\n\n    @Test\n    public void should_incrementLoopCounter_update_lastUpdateDate() throws Exception {\n        // Given\n        SLoopActivityInstance loopInstance = mock(SLoopActivityInstance.class);\n        when(loopInstance.getLoopCounter()).thenReturn(5);\n\n        // When\n        activityInstanceServiceImpl.incrementLoopCounter(loopInstance);\n\n        // Then\n        ArgumentCaptor<UpdateRecord> captor = ArgumentCaptor.forClass(UpdateRecord.class);\n        verify(recorder).recordUpdate(captor.capture(), anyString());\n        assertThat(captor.getValue().getFields().keySet())\n                .contains(\"loopCounter\", \"lastUpdateDate\");\n    }\n\n    @Test\n    public void should_setLoopMax_update_lastUpdateDate() throws Exception {\n        // Given\n        SLoopActivityInstance loopActivity = mock(SLoopActivityInstance.class);\n\n        // When\n        activityInstanceServiceImpl.setLoopMax(loopActivity, 10);\n\n        // Then\n        ArgumentCaptor<UpdateRecord> captor = ArgumentCaptor.forClass(UpdateRecord.class);\n        verify(recorder).recordUpdate(captor.capture(), anyString());\n        assertThat(captor.getValue().getFields().keySet())\n                .contains(\"loopMax\", \"lastUpdateDate\");\n    }\n\n    @Test\n    public void should_setLoopCardinality_update_lastUpdateDate() throws Exception {\n        // Given\n        SFlowNodeInstance flowNode = mock(SFlowNodeInstance.class);\n\n        // When\n        activityInstanceServiceImpl.setLoopCardinality(flowNode, 5);\n\n        // Then\n        ArgumentCaptor<UpdateRecord> captor = ArgumentCaptor.forClass(UpdateRecord.class);\n        verify(recorder).recordUpdate(captor.capture(), anyString());\n        assertThat(captor.getValue().getFields().keySet())\n                .contains(\"loopCardinality\", \"lastUpdateDate\");\n    }\n\n    @Test\n    public void should_addMultiInstanceNumberOfActiveActivities_update_lastUpdateDate() throws Exception {\n        // Given\n        SMultiInstanceActivityInstance multiInstance = mock(SMultiInstanceActivityInstance.class);\n        when(recorder.recordUpdateWithQuery(any(UpdateRecord.class),\n                eq(FlowNodeInstanceService.MULTIINSTANCE_NUMBEROFINSTANCE_MODIFIED),\n                eq(\"updateMultiInstanceActiveCounters\"))).thenReturn(1);\n\n        // When\n        activityInstanceServiceImpl.addMultiInstanceNumberOfActiveActivities(multiInstance, 2);\n\n        // Then\n        ArgumentCaptor<UpdateRecord> captor = ArgumentCaptor.forClass(UpdateRecord.class);\n        verify(recorder).recordUpdateWithQuery(captor.capture(),\n                eq(FlowNodeInstanceService.MULTIINSTANCE_NUMBEROFINSTANCE_MODIFIED),\n                eq(\"updateMultiInstanceActiveCounters\"));\n        assertThat(captor.getValue().getFields().keySet())\n                .contains(\"id\", \"number\", \"lastUpdateDate\");\n    }\n\n    @Test\n    public void should_addMultiInstanceNumberOfTerminatedActivities_update_lastUpdateDate() throws Exception {\n        // Given\n        SMultiInstanceActivityInstance multiInstance = mock(SMultiInstanceActivityInstance.class);\n        when(recorder.recordUpdateWithQuery(any(UpdateRecord.class),\n                eq(FlowNodeInstanceService.MULTIINSTANCE_NUMBEROFINSTANCE_MODIFIED),\n                eq(\"updateMultiInstanceTerminatedCounters\"))).thenReturn(1);\n\n        // When\n        activityInstanceServiceImpl.addMultiInstanceNumberOfTerminatedActivities(multiInstance, 1);\n\n        // Then\n        ArgumentCaptor<UpdateRecord> captor = ArgumentCaptor.forClass(UpdateRecord.class);\n        verify(recorder).recordUpdateWithQuery(captor.capture(),\n                eq(FlowNodeInstanceService.MULTIINSTANCE_NUMBEROFINSTANCE_MODIFIED),\n                eq(\"updateMultiInstanceTerminatedCounters\"));\n        assertThat(captor.getValue().getFields().keySet())\n                .contains(\"id\", \"number\", \"lastUpdateDate\");\n    }\n\n    @Test\n    public void should_addMultiInstanceNumberOfCompletedActivities_update_lastUpdateDate() throws Exception {\n        // Given\n        SMultiInstanceActivityInstance multiInstance = mock(SMultiInstanceActivityInstance.class);\n        when(recorder.recordUpdateWithQuery(any(UpdateRecord.class),\n                eq(FlowNodeInstanceService.MULTIINSTANCE_NUMBEROFINSTANCE_MODIFIED),\n                eq(\"updateMultiInstanceCompletedCounters\"))).thenReturn(1);\n\n        // When\n        activityInstanceServiceImpl.addMultiInstanceNumberOfCompletedActivities(multiInstance, 1);\n\n        // Then\n        ArgumentCaptor<UpdateRecord> captor = ArgumentCaptor.forClass(UpdateRecord.class);\n        verify(recorder).recordUpdateWithQuery(captor.capture(),\n                eq(FlowNodeInstanceService.MULTIINSTANCE_NUMBEROFINSTANCE_MODIFIED),\n                eq(\"updateMultiInstanceCompletedCounters\"));\n        assertThat(captor.getValue().getFields().keySet())\n                .contains(\"id\", \"number\", \"lastUpdateDate\");\n    }\n\n    @Test\n    public void should_setTokenCount_update_lastUpdateDate() throws Exception {\n        // Given\n        SActivityInstance activityInstance = mock(SActivityInstance.class);\n\n        // When\n        activityInstanceServiceImpl.setTokenCount(activityInstance, 5);\n\n        // Then\n        ArgumentCaptor<UpdateRecord> captor = ArgumentCaptor.forClass(UpdateRecord.class);\n        verify(recorder).recordUpdate(captor.capture(), anyString());\n        assertThat(captor.getValue().getFields().keySet())\n                .contains(\"tokenCount\", \"lastUpdateDate\");\n    }\n\n    @Test\n    public void should_setAbortedByBoundaryEvent_update_lastUpdateDate() throws Exception {\n        // Given\n        SActivityInstance activityInstance = mock(SActivityInstance.class);\n\n        // When\n        activityInstanceServiceImpl.setAbortedByBoundaryEvent(activityInstance, 789L);\n\n        // Then\n        ArgumentCaptor<UpdateRecord> captor = ArgumentCaptor.forClass(UpdateRecord.class);\n        verify(recorder).recordUpdate(captor.capture(), anyString());\n        assertThat(captor.getValue().getFields().keySet())\n                .contains(\"abortedByBoundary\", \"lastUpdateDate\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/impl/BPMFailureServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.within;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.commons.lang3.exception.ExceptionUtils;\nimport org.bonitasoft.engine.archive.ArchiveInsertRecord;\nimport org.bonitasoft.engine.archive.ArchiveService;\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException;\nimport org.bonitasoft.engine.commons.exceptions.SExceptionContext;\nimport org.bonitasoft.engine.connector.ConnectorValidationException;\nimport org.bonitasoft.engine.core.operation.exception.SOperationExecutionException;\nimport org.bonitasoft.engine.core.process.instance.api.BPMFailureService;\nimport org.bonitasoft.engine.core.process.instance.model.SABPMFailure;\nimport org.bonitasoft.engine.core.process.instance.model.SBPMFailure;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.services.PersistenceService;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.AdditionalAnswers;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mock;\nimport org.mockito.Mockito;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport org.mockito.junit.jupiter.MockitoSettings;\nimport org.mockito.quality.Strictness;\n\n@ExtendWith(MockitoExtension.class)\n@MockitoSettings(strictness = Strictness.LENIENT)\nclass BPMFailureServiceImplTest {\n\n    @Mock\n    private PersistenceService persistenceService;\n    @Mock\n    private ArchiveService archiveService;\n\n    private BPMFailureServiceImpl service;\n\n    @BeforeEach\n    void setup() throws Exception {\n        service = spy(new BPMFailureServiceImpl(persistenceService, archiveService));\n        when(persistenceService.insert(any(SBPMFailure.class))).thenAnswer(AdditionalAnswers.returnsFirstArg());\n    }\n\n    @Test\n    void should_createFlowNodeFailure_record_failure_with_proper_content() throws Exception {\n        var now = Instant.now();\n        var flowNodeInstance = createFlowNodeInstance();\n        var failure = new BPMFailureService.Failure(\"scope\", new Throwable(\"error message\"));\n\n        var bpmFailure = service.createFlowNodeFailure(flowNodeInstance, failure);\n\n        verify(persistenceService).insert(bpmFailure);\n        assertThat(bpmFailure.getFlowNodeInstanceId()).isEqualTo(flowNodeInstance.getId());\n        assertThat(bpmFailure.getProcessDefinitionId()).isEqualTo(flowNodeInstance.getProcessDefinitionId());\n        assertThat(bpmFailure.getProcessInstanceId()).isEqualTo(flowNodeInstance.getParentProcessInstanceId());\n        assertThat(bpmFailure.getRootProcessInstanceId()).isEqualTo(flowNodeInstance.getRootProcessInstanceId());\n        assertThat(bpmFailure.getScope()).isEqualTo(\"scope\");\n        assertThat(bpmFailure.getErrorMessage()).isEqualTo(\"Throwable: error message\");\n        assertThat(bpmFailure.getStackTrace()).isEqualTo(ExceptionUtils.getStackTrace(failure.throwable()));\n        assertThat(Instant.ofEpochMilli(bpmFailure.getFailureDate())).isCloseTo(now, within(1000, ChronoUnit.MILLIS));\n    }\n\n    @Test\n    void should_createProcessInstanceFailure_record_failure_with_proper_content() throws Exception {\n        var now = Instant.now();\n        var processInstance = createProcessInstance();\n        var failure = new BPMFailureService.Failure(\"scope\", new Throwable(\"error message\"));\n\n        var bpmFailure = service.createProcessInstanceFailure(processInstance, failure);\n\n        verify(persistenceService).insert(bpmFailure);\n        assertThat(bpmFailure.getProcessInstanceId()).isEqualTo(processInstance.getId());\n        assertThat(bpmFailure.getProcessDefinitionId()).isEqualTo(processInstance.getProcessDefinitionId());\n        assertThat(bpmFailure.getRootProcessInstanceId()).isEqualTo(processInstance.getRootProcessInstanceId());\n        assertThat(bpmFailure.getScope()).isEqualTo(\"scope\");\n        assertThat(bpmFailure.getErrorMessage()).isEqualTo(\"Throwable: error message\");\n        assertThat(bpmFailure.getStackTrace()).isEqualTo(ExceptionUtils.getStackTrace(failure.throwable()));\n        assertThat(Instant.ofEpochMilli(bpmFailure.getFailureDate())).isCloseTo(now, within(1000, ChronoUnit.MILLIS));\n    }\n\n    private static SFlowNodeInstance createFlowNodeInstance() {\n        SFlowNodeInstance flowNodeInstance = Mockito.mock(SFlowNodeInstance.class);\n        when(flowNodeInstance.getId()).thenReturn(1L);\n        when(flowNodeInstance.getParentProcessInstanceId()).thenReturn(2L);\n        when(flowNodeInstance.getProcessDefinitionId()).thenReturn(3L);\n        when(flowNodeInstance.getRootProcessInstanceId()).thenReturn(4L);\n        return flowNodeInstance;\n    }\n\n    private static SProcessInstance createProcessInstance() {\n        SProcessInstance processInstance = Mockito.mock(SProcessInstance.class);\n        when(processInstance.getId()).thenReturn(10L);\n        when(processInstance.getProcessDefinitionId()).thenReturn(11L);\n        when(processInstance.getRootProcessInstanceId()).thenReturn(12L);\n        return processInstance;\n    }\n\n    @Test\n    void should_createFlowNodeFailure_record_failure_with_expression_context() throws Exception {\n        var flowNodeInstance = createFlowNodeInstance();\n        var failure = new BPMFailureService.Failure(\"scope\",\n                new SExpressionEvaluationException(new RuntimeException(\"error in expression\"), \"expressionName\"));\n\n        var bpmFailure = service.createFlowNodeFailure(flowNodeInstance, failure);\n\n        assertThat(bpmFailure.getContext()).isEqualTo(\"expression::expressionName\");\n    }\n\n    @Test\n    void should_createFlowNodeFailure_record_failure_with_message_context() throws Exception {\n        var flowNodeInstance = createFlowNodeInstance();\n        var errorInMessage = new SBonitaRuntimeException(\"error in message\");\n        errorInMessage.setMessageInstanceNameOnContext(\"messageName\");\n        var failure = new BPMFailureService.Failure(\"scope\", errorInMessage);\n\n        var bpmFailure = service.createFlowNodeFailure(flowNodeInstance, failure);\n\n        assertThat(bpmFailure.getContext()).isEqualTo(\"message::messageName\");\n    }\n\n    @Test\n    void should_createFlowNodeFailure_record_failure_with_connector_context() throws Exception {\n        var flowNodeInstance = createFlowNodeInstance();\n        var errorInConnector = new SBonitaRuntimeException(\"error in connector\");\n        errorInConnector.setConnectorDefinitionIdOnContext(\"rest-connector\");\n        errorInConnector.setConnectorNameOnContext(\"get-info\");\n        errorInConnector.setConnectorActivationEventOnContext(ConnectorEvent.ON_ENTER.name());\n        var failure = new BPMFailureService.Failure(\"scope\", errorInConnector);\n\n        var bpmFailure = service.createFlowNodeFailure(flowNodeInstance, failure);\n\n        assertThat(bpmFailure.getContext()).isEqualTo(\"get-info::rest-connector::on_enter\");\n    }\n\n    @Test\n    void should_createFlowNodeFailure_record_failure_with_connector_input_context() throws Exception {\n        var flowNodeInstance = createFlowNodeInstance();\n        var errorInConnector = new SBonitaRuntimeException(\"error in connector\");\n        errorInConnector.setConnectorDefinitionIdOnContext(\"rest-connector\");\n        errorInConnector.setConnectorNameOnContext(\"get-info\");\n        errorInConnector.setConnectorActivationEventOnContext(ConnectorEvent.ON_ENTER.name());\n        errorInConnector.setConnectorInputOnContext(\"username\");\n        var failure = new BPMFailureService.Failure(\"scope\", errorInConnector);\n\n        var bpmFailure = service.createFlowNodeFailure(flowNodeInstance, failure);\n\n        assertThat(bpmFailure.getContext()).isEqualTo(\"get-info::rest-connector::on_enter//input::username\");\n    }\n\n    @Test\n    void should_createFlowNodeFailure_record_failure_with_connector_input_expression_context() throws Exception {\n        var flowNodeInstance = createFlowNodeInstance();\n        var errorInConnector = new SBonitaRuntimeException(new SExpressionEvaluationException(\n                new RuntimeException(\"error in input expression\"), \"expressionName\"));\n        errorInConnector.setConnectorDefinitionIdOnContext(\"rest-connector\");\n        errorInConnector.setConnectorNameOnContext(\"get-info\");\n        errorInConnector.setConnectorActivationEventOnContext(ConnectorEvent.ON_ENTER.name());\n        errorInConnector.setConnectorInputOnContext(\"username\");\n        var failure = new BPMFailureService.Failure(\"scope\", errorInConnector);\n\n        var bpmFailure = service.createFlowNodeFailure(flowNodeInstance, failure);\n\n        assertThat(bpmFailure.getContext())\n                .isEqualTo(\"get-info::rest-connector::on_enter//input::username//expression::expressionName\");\n    }\n\n    @Test\n    void should_createFlowNodeFailure_record_failure_with_connector_output_expression_context() throws Exception {\n        var flowNodeInstance = createFlowNodeInstance();\n        var errorInConnector = new SBonitaRuntimeException(\n                new SOperationExecutionException(new SExpressionEvaluationException(\n                        new RuntimeException(\"error in output expression\"), \"expressionName\")));\n        errorInConnector.setConnectorDefinitionIdOnContext(\"rest-connector\");\n        errorInConnector.setConnectorNameOnContext(\"get-info\");\n        errorInConnector.setConnectorActivationEventOnContext(ConnectorEvent.ON_ENTER.name());\n        var failure = new BPMFailureService.Failure(\"scope\", errorInConnector);\n\n        var bpmFailure = service.createFlowNodeFailure(flowNodeInstance, failure);\n\n        assertThat(bpmFailure.getContext())\n                .isEqualTo(\"get-info::rest-connector::on_enter//output//expression::expressionName\");\n    }\n\n    @Test\n    void should_createFlowNodeFailure_record_failure_with_connector_validation_context() throws Exception {\n        var flowNodeInstance = createFlowNodeInstance();\n        var errorInConnector = new SBonitaRuntimeException(new ConnectorValidationException(\"error in validation\"));\n        errorInConnector.setConnectorDefinitionIdOnContext(\"rest-connector\");\n        errorInConnector.setConnectorNameOnContext(\"get-info\");\n        errorInConnector.setConnectorActivationEventOnContext(ConnectorEvent.ON_ENTER.name());\n        var failure = new BPMFailureService.Failure(\"scope\", errorInConnector);\n\n        var bpmFailure = service.createFlowNodeFailure(flowNodeInstance, failure);\n\n        assertThat(bpmFailure.getContext()).isEqualTo(\"get-info::rest-connector::on_enter//input-validation\");\n    }\n\n    @Test\n    void should_createFlowNodeFailure_record_failure_with_named_transition_context() throws Exception {\n        var flowNodeInstance = createFlowNodeInstance();\n        var errorInTransition = new SBonitaRuntimeException(new SExpressionEvaluationException(\n                new RuntimeException(\"error in consition expression\"), \"expressionName\"));\n        errorInTransition.getContext().put(SExceptionContext.TRANSITION_NAME, \"transitionName\");\n        errorInTransition.getContext().put(SExceptionContext.TRANSITION_TARGET_FLOWNODE_NAME, \"gateway::gatewayName\");\n        var failure = new BPMFailureService.Failure(\"scope\", errorInTransition);\n\n        var bpmFailure = service.createFlowNodeFailure(flowNodeInstance, failure);\n\n        assertThat(bpmFailure.getContext())\n                .isEqualTo(\"transitionName//to::gateway::gatewayName//expression::expressionName\");\n    }\n\n    @Test\n    void should_createFlowNodeFailure_record_failure_with_unnamed_transition_context() throws Exception {\n        var flowNodeInstance = createFlowNodeInstance();\n        var errorInTransition = new SBonitaRuntimeException(new SExpressionEvaluationException(\n                new RuntimeException(\"error in consition expression\"), \"expressionName\"));\n        errorInTransition.getContext().put(SExceptionContext.TRANSITION_TARGET_FLOWNODE_NAME, \"gateway::gatewayName\");\n        var failure = new BPMFailureService.Failure(\"scope\", errorInTransition);\n\n        var bpmFailure = service.createFlowNodeFailure(flowNodeInstance, failure);\n\n        assertThat(bpmFailure.getContext())\n                .isEqualTo(\"to::gateway::gatewayName//expression::expressionName\");\n    }\n\n    @Test\n    void should_getFlowNodeFailures_call_the_right_query_with_proper_parameters() throws Exception {\n        service.getFlowNodeFailures(1, 10);\n\n        ArgumentCaptor<SelectListDescriptor<SBPMFailure>> captor = ArgumentCaptor.forClass(SelectListDescriptor.class);\n        verify(persistenceService).selectList(captor.capture());\n\n        var descriptor = captor.getValue();\n        assertThat(descriptor.getQueryName()).isEqualTo(\"getFlowNodeFailures\");\n        assertThat(descriptor.getInputParameter(\"flowNodeInstanceId\")).isEqualTo(1L);\n        assertThat(descriptor.getReturnType()).isEqualTo(SBPMFailure.class);\n        assertThat(descriptor.getStartIndex()).isZero();\n        assertThat(descriptor.getPageSize()).isEqualTo(10);\n    }\n\n    @Test\n    void should_getProcessInstanceFailures_call_the_right_query_with_proper_parameters() throws Exception {\n        service.getProcessInstanceFailures(1, 10);\n\n        ArgumentCaptor<SelectListDescriptor<SBPMFailure>> captor = ArgumentCaptor.forClass(SelectListDescriptor.class);\n        verify(persistenceService).selectList(captor.capture());\n\n        var descriptor = captor.getValue();\n        assertThat(descriptor.getQueryName()).isEqualTo(\"getProcessInstanceFailures\");\n        assertThat(descriptor.getInputParameter(\"processInstanceId\")).isEqualTo(1L);\n        assertThat(descriptor.getReturnType()).isEqualTo(SBPMFailure.class);\n        assertThat(descriptor.getStartIndex()).isZero();\n        assertThat(descriptor.getPageSize()).isEqualTo(10);\n    }\n\n    @Test\n    void should_archiveFlowNodeFailures_call_archive_service_with_proper_parameters() throws Exception {\n        var failureDate = Instant.now().toEpochMilli();\n        doReturn(List.of(SBPMFailure.builder()\n                .failureDate(failureDate)\n                .flowNodeInstanceId(1L)\n                .rootProcessInstanceId(1L)\n                .processDefinitionId(1L)\n                .processInstanceId(1L)\n                .scope(\"scope\")\n                .context(\"context\")\n                .errorMessage(\"errorMessage\")\n                .stackTrace(\"stackTrace\")\n                .build())).when(service).getFlowNodeFailures(1L, Integer.MAX_VALUE);\n        long archiveDate = Instant.now().toEpochMilli();\n\n        service.archiveFlowNodeFailures(1L, archiveDate);\n\n        var captor = ArgumentCaptor.forClass(ArchiveInsertRecord.class);\n        verify(archiveService).recordInserts(eq(archiveDate), captor.capture());\n\n        var archiveInsertRecord = captor.getValue();\n        var entity = archiveInsertRecord.getEntity();\n        assertThat(entity).isInstanceOf(SABPMFailure.class);\n        var archiveBPMFailure = (SABPMFailure) entity;\n        assertThat(archiveBPMFailure.getFailureDate()).isEqualTo(failureDate);\n        assertThat(archiveBPMFailure.getFlowNodeInstanceId()).isEqualTo(1L);\n        assertThat(archiveBPMFailure.getProcessDefinitionId()).isEqualTo(1L);\n        assertThat(archiveBPMFailure.getProcessInstanceId()).isEqualTo(1L);\n    }\n\n    @Test\n    void should_archiveProcessInstanceFailures_call_archive_service_with_proper_parameters() throws Exception {\n        var failureDate = Instant.now().toEpochMilli();\n        doReturn(List.of(SBPMFailure.builder()\n                .failureDate(failureDate)\n                .rootProcessInstanceId(1L)\n                .processDefinitionId(1L)\n                .processInstanceId(1L)\n                .scope(\"scope\")\n                .context(\"context\")\n                .errorMessage(\"errorMessage\")\n                .stackTrace(\"stackTrace\")\n                .build())).when(service).getProcessInstanceFailures(1L, Integer.MAX_VALUE);\n        long archiveDate = Instant.now().toEpochMilli();\n\n        service.archiveProcessInstanceFailures(1L, archiveDate);\n\n        var captor = ArgumentCaptor.forClass(ArchiveInsertRecord.class);\n        verify(archiveService).recordInserts(eq(archiveDate), captor.capture());\n\n        var archiveInsertRecord = captor.getValue();\n        var entity = archiveInsertRecord.getEntity();\n        assertThat(entity).isInstanceOf(SABPMFailure.class);\n        var archiveBPMFailure = (SABPMFailure) entity;\n        assertThat(archiveBPMFailure.getFailureDate()).isEqualTo(failureDate);\n        assertThat(archiveBPMFailure.getRootProcessInstanceId()).isEqualTo(1L);\n        assertThat(archiveBPMFailure.getProcessDefinitionId()).isEqualTo(1L);\n        assertThat(archiveBPMFailure.getProcessInstanceId()).isEqualTo(1L);\n    }\n\n    @Test\n    void should_deleteFlowNodeFailures_call_persistence_service_with_proper_parameters() throws Exception {\n        doReturn(List.of(SBPMFailure.builder()\n                .id(1L)\n                .build(),\n                SBPMFailure.builder()\n                        .id(2L)\n                        .build()))\n                .when(service).getFlowNodeFailures(1L, Integer.MAX_VALUE);\n\n        service.deleteFlowNodeFailures(1L);\n\n        ArgumentCaptor<List<Long>> captor = ArgumentCaptor.forClass(List.class);\n        verify(persistenceService).delete(captor.capture(), eq(SBPMFailure.class));\n\n        assertThat(captor.getValue()).contains(1L, 2L);\n    }\n\n    @Test\n    void should_deleteProcessInstanceFailures_call_persistence_service_with_proper_parameters() throws Exception {\n        doReturn(List.of(SBPMFailure.builder()\n                .id(1L)\n                .build(),\n                SBPMFailure.builder()\n                        .id(2L)\n                        .build()))\n                .when(service).getProcessInstanceFailures(1L, Integer.MAX_VALUE);\n\n        service.deleteProcessInstanceFailures(1L);\n\n        ArgumentCaptor<List<Long>> captor = ArgumentCaptor.forClass(List.class);\n        verify(persistenceService).delete(captor.capture(), eq(SBPMFailure.class));\n\n        assertThat(captor.getValue()).contains(1L, 2L);\n    }\n\n    @Test\n    void should_deleteArchivedFlowNodeFailures_call_the_right_query_with_proper_parameters() throws Exception {\n        service.deleteArchivedFlowNodeFailures(List.of(1L, 2L));\n\n        verify(archiveService).deleteFromQuery(\"deleteArchivedBPMFailuresByFlowNodeInstanceIds\",\n                Map.ofEntries(Map.entry(\"flowNodeInstanceIds\", List.of(1L, 2L))));\n    }\n\n    @Test\n    void should_getArchivedFlowNodeFailures_call_the_right_query_with_proper_parameters() throws Exception {\n        service.getArchivedFlowNodeFailures(1L, 10);\n\n        ArgumentCaptor<SelectListDescriptor<SABPMFailure>> captor = ArgumentCaptor.forClass(SelectListDescriptor.class);\n        verify(persistenceService).selectList(captor.capture());\n\n        var descriptor = captor.getValue();\n        assertThat(descriptor.getQueryName()).isEqualTo(\"getArchivedFlowNodeFailures\");\n        assertThat(descriptor.getInputParameter(\"flowNodeInstanceId\")).isEqualTo(1L);\n        assertThat(descriptor.getReturnType()).isEqualTo(SABPMFailure.class);\n        assertThat(descriptor.getStartIndex()).isZero();\n        assertThat(descriptor.getPageSize()).isEqualTo(10);\n    }\n\n    @Test\n    void should_deleteArchivedProcessInstanceFailures_call_the_right_query_with_proper_parameters() throws Exception {\n        service.deleteArchivedProcessInstanceFailures(List.of(1L, 2L));\n\n        verify(archiveService).deleteFromQuery(\"deleteArchivedBPMFailuresByProcessInstanceIds\",\n                Map.ofEntries(Map.entry(\"processInstanceIds\", List.of(1L, 2L))));\n    }\n\n    @Test\n    void should_getArchivedProcessInstanceFailures_call_the_right_query_with_proper_parameters() throws Exception {\n        service.getArchivedProcessInstanceFailures(1L, 10);\n\n        ArgumentCaptor<SelectListDescriptor<SABPMFailure>> captor = ArgumentCaptor.forClass(SelectListDescriptor.class);\n        verify(persistenceService).selectList(captor.capture());\n\n        var descriptor = captor.getValue();\n        assertThat(descriptor.getQueryName()).isEqualTo(\"getArchivedProcessInstanceFailures\");\n        assertThat(descriptor.getInputParameter(\"processInstanceId\")).isEqualTo(1L);\n        assertThat(descriptor.getReturnType()).isEqualTo(SABPMFailure.class);\n        assertThat(descriptor.getStartIndex()).isZero();\n        assertThat(descriptor.getPageSize()).isEqualTo(10);\n    }\n\n    @Test\n    void should_getChildProcessInstancesFailures_call_the_right_query_with_proper_parameters() throws Exception {\n        service.getChildProcessInstancesFailures(1, 10);\n\n        ArgumentCaptor<SelectListDescriptor<SBPMFailure>> captor = ArgumentCaptor.forClass(SelectListDescriptor.class);\n        verify(persistenceService).selectList(captor.capture());\n\n        var descriptor = captor.getValue();\n        assertThat(descriptor.getQueryName()).isEqualTo(\"getChildProcessInstancesFailures\");\n        assertThat(descriptor.getInputParameter(\"rootProcessInstanceId\")).isEqualTo(1L);\n        assertThat(descriptor.getReturnType()).isEqualTo(SBPMFailure.class);\n        assertThat(descriptor.getStartIndex()).isZero();\n        assertThat(descriptor.getPageSize()).isEqualTo(10);\n    }\n\n    @Test\n    void should_getArchivedChildProcessInstancesFailures_call_the_right_query_with_proper_parameters()\n            throws Exception {\n        service.getArchivedChildProcessInstancesFailures(1L, 10);\n\n        ArgumentCaptor<SelectListDescriptor<SABPMFailure>> captor = ArgumentCaptor.forClass(SelectListDescriptor.class);\n        verify(persistenceService).selectList(captor.capture());\n\n        var descriptor = captor.getValue();\n        assertThat(descriptor.getQueryName()).isEqualTo(\"getArchivedChildProcessInstancesFailures\");\n        assertThat(descriptor.getInputParameter(\"rootProcessInstanceId\")).isEqualTo(1L);\n        assertThat(descriptor.getReturnType()).isEqualTo(SABPMFailure.class);\n        assertThat(descriptor.getStartIndex()).isZero();\n        assertThat(descriptor.getPageSize()).isEqualTo(10);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/impl/FlowNodeInstancesServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.archive.ArchiveService;\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SStateCategory;\nimport org.bonitasoft.engine.core.process.instance.model.STaskPriority;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.bonitasoft.engine.services.PersistenceService;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class FlowNodeInstancesServiceImplTest {\n\n    @Mock\n    private Recorder recorder;\n\n    @Mock\n    private PersistenceService persistenceService;\n\n    @Mock\n    private ArchiveService archiveService;\n\n    @Mock\n    private SFlowNodeInstance flowNodeInstance;\n\n    @Mock\n    private FlowNodeState flowNodeState;\n\n    @Captor\n    private ArgumentCaptor<UpdateRecord> updateRecordCaptor;\n\n    private ActivityInstanceServiceImpl service;\n\n    @Before\n    public void setUp() {\n        service = new ActivityInstanceServiceImpl(recorder, persistenceService, archiveService);\n    }\n\n    // === Tests for setLastUpdateDate helper method behavior ===\n\n    @Test\n    public void should_setState_update_both_reachStateDate_and_lastUpdateDate() throws Exception {\n        // Given\n        when(flowNodeState.getId()).thenReturn(1);\n        when(flowNodeState.getName()).thenReturn(\"ready\");\n        when(flowNodeState.isStable()).thenReturn(true);\n        when(flowNodeState.isTerminal()).thenReturn(false);\n\n        // When\n        service.setState(flowNodeInstance, flowNodeState);\n\n        // Then\n        verify(recorder).recordUpdate(updateRecordCaptor.capture(), anyString());\n        assertThat(updateRecordCaptor.getValue().getFields().keySet())\n                .contains(\"stateId\", \"reachedStateDate\", \"lastUpdateDate\");\n    }\n\n    @Test\n    public void should_setExecuting_update_lastUpdateDate() throws Exception {\n        // When\n        service.setExecuting(flowNodeInstance);\n\n        // Then\n        verify(recorder).recordUpdate(updateRecordCaptor.capture(), anyString());\n        assertThat(updateRecordCaptor.getValue().getFields().keySet())\n                .contains(\"stateExecuting\", \"lastUpdateDate\");\n    }\n\n    @Test\n    public void should_updateDisplayName_update_lastUpdateDate() throws Exception {\n        // Given\n        when(flowNodeInstance.getDisplayName()).thenReturn(\"oldName\");\n\n        // When\n        service.updateDisplayName(flowNodeInstance, \"newName\");\n\n        // Then\n        verify(recorder).recordUpdate(updateRecordCaptor.capture(), anyString());\n        assertThat(updateRecordCaptor.getValue().getFields().keySet())\n                .contains(\"displayName\", \"lastUpdateDate\");\n    }\n\n    @Test\n    public void should_updateDisplayDescription_update_lastUpdateDate() throws Exception {\n        // Given\n        when(flowNodeInstance.getDisplayDescription()).thenReturn(\"oldDesc\");\n\n        // When\n        service.updateDisplayDescription(flowNodeInstance, \"newDesc\");\n\n        // Then\n        verify(recorder).recordUpdate(updateRecordCaptor.capture(), anyString());\n        assertThat(updateRecordCaptor.getValue().getFields().keySet())\n                .contains(\"displayDescription\", \"lastUpdateDate\");\n    }\n\n    @Test\n    public void should_setTaskPriority_update_lastUpdateDate() throws Exception {\n        // When\n        service.setTaskPriority(flowNodeInstance, STaskPriority.HIGHEST);\n\n        // Then\n        verify(recorder).recordUpdate(updateRecordCaptor.capture(), anyString());\n        assertThat(updateRecordCaptor.getValue().getFields().keySet())\n                .contains(\"priority\", \"lastUpdateDate\");\n    }\n\n    @Test\n    public void should_setStateCategory_update_lastUpdateDate() throws Exception {\n        // When\n        service.setStateCategory(flowNodeInstance, SStateCategory.ABORTING);\n\n        // Then\n        verify(recorder).recordUpdate(updateRecordCaptor.capture(), anyString());\n        assertThat(updateRecordCaptor.getValue().getFields().keySet())\n                .contains(\"stateCategory\", \"lastUpdateDate\");\n    }\n\n    @Test\n    public void should_setExecutedBy_update_lastUpdateDate() throws Exception {\n        // When\n        service.setExecutedBy(flowNodeInstance, 123L);\n\n        // Then\n        verify(recorder).recordUpdate(updateRecordCaptor.capture(), anyString());\n        assertThat(updateRecordCaptor.getValue().getFields().keySet())\n                .contains(\"executedBy\", \"lastUpdateDate\");\n    }\n\n    @Test\n    public void should_setExecutedBySubstitute_update_lastUpdateDate() throws Exception {\n        // When\n        service.setExecutedBySubstitute(flowNodeInstance, 456L);\n\n        // Then\n        verify(recorder).recordUpdate(updateRecordCaptor.capture(), anyString());\n        assertThat(updateRecordCaptor.getValue().getFields().keySet())\n                .contains(\"executedBySubstitute\", \"lastUpdateDate\");\n    }\n\n    @Test\n    public void should_setExpectedEndDate_update_lastUpdateDate() throws Exception {\n        // When\n        service.setExpectedEndDate(flowNodeInstance, 123456789L);\n\n        // Then\n        verify(recorder).recordUpdate(updateRecordCaptor.capture(), anyString());\n        assertThat(updateRecordCaptor.getValue().getFields().keySet())\n                .contains(\"expectedEndDate\", \"lastUpdateDate\");\n    }\n\n    // === Tests for timestamp reuse behavior ===\n\n    @Test\n    public void should_setState_use_same_timestamp_for_reachStateDate_and_lastUpdateDate() throws Exception {\n        // Given\n        when(flowNodeState.getId()).thenReturn(1);\n        when(flowNodeState.getName()).thenReturn(\"ready\");\n        when(flowNodeState.isStable()).thenReturn(true);\n        when(flowNodeState.isTerminal()).thenReturn(false);\n\n        // When\n        service.setState(flowNodeInstance, flowNodeState);\n\n        // Then\n        verify(recorder).recordUpdate(updateRecordCaptor.capture(), anyString());\n        Map<String, Object> fields = updateRecordCaptor.getValue().getFields();\n        Long reachedStateDate = (Long) fields.get(\"reachedStateDate\");\n        Long lastUpdateDate = (Long) fields.get(\"lastUpdateDate\");\n        assertThat(lastUpdateDate).isNotNull();\n        assertThat(lastUpdateDate).isEqualTo(reachedStateDate);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/impl/GatewayInstanceServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyList;\nimport static org.mockito.ArgumentMatchers.anyLong;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.ArgumentMatchers.nullable;\nimport static org.mockito.Mockito.doNothing;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SGatewayType;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SFlowNodeDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SProcessDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.impl.STransitionDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SUserTaskDefinitionImpl;\nimport org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SGatewayModificationException;\nimport org.bonitasoft.engine.core.process.instance.model.SGatewayInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.recorder.SelectDescriptorBuilder;\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class GatewayInstanceServiceImplTest {\n\n    private static final long PROCESS_INSTANCE_ID = 12l;\n    @Mock\n    private Recorder recorder;\n    @Mock\n    private EventService eventService;\n    @Mock\n    private ReadPersistenceService persistenceRead;\n\n    @Mock\n    private FlowNodeInstanceService flowNodeInstanceService;\n    @Mock\n    private SFlowElementContainerDefinition processContainer;\n    @Captor\n    private ArgumentCaptor<UpdateRecord> updateRecordCaptor;\n    @InjectMocks\n    @Spy\n    private GatewayInstanceServiceImpl gatewayInstanceService;\n\n    @Test\n    public void should_extractElementThatAreSourceAndTarget_modify_the_lists() {\n        SFlowNodeDefinition step1 = node(1, \"step1\");\n        SFlowNodeDefinition step2 = node(2, \"step2\");\n        SFlowNodeDefinition step3 = node(3, \"step3\");\n\n        List<SFlowNodeDefinition> sourceElements = new ArrayList<>(Arrays.asList(step1, step2));\n        List<SFlowNodeDefinition> targetElements = new ArrayList<>(Arrays.asList(step2, step3));\n        List<SFlowNodeDefinition> sourceAndTarget = gatewayInstanceService\n                .extractElementThatAreSourceAndTarget(sourceElements, targetElements);\n\n        assertThat(sourceElements).isEmpty();\n        assertThat(targetElements).containsOnly(step3);\n        assertThat(sourceAndTarget).containsOnly(step2, step1);//step1 because it's also a start element\n    }\n\n    SFlowNodeDefinition node(long id, String name) {\n        SUserTaskDefinitionImpl definition = new SUserTaskDefinitionImpl(id, name, \"actor\");\n        doReturn(definition).when(processContainer).getFlowNode(id);\n        return definition;\n    }\n\n    @Test\n    public void should_containsToken_for_source_element_with_one_token_return_true() throws Exception {\n        instanceInDatabase(\"step1\", PROCESS_INSTANCE_ID, false);\n        instanceInDatabase(\"step2\", PROCESS_INSTANCE_ID, true);\n\n        boolean containsToken = gatewayInstanceService.containsToken(PROCESS_INSTANCE_ID,\n                flowNodeDefList(\"step0\", \"step1\", \"step2\"), true);\n\n        assertThat(containsToken).isTrue();\n    }\n\n    @Test\n    public void should_containsToken_for_source_element_with_no_token_return_false() throws Exception {\n        instanceInDatabase(\"step1\", PROCESS_INSTANCE_ID, false);\n        instanceInDatabase(\"step2\", PROCESS_INSTANCE_ID, false);\n\n        boolean containsToken = gatewayInstanceService.containsToken(PROCESS_INSTANCE_ID,\n                flowNodeDefList(\"step0\", \"step1\", \"step2\"), true);\n\n        assertThat(containsToken).isFalse();\n    }\n\n    @Test\n    public void should_containsToken_for_target_element_with_token_return_true() throws Exception {\n        instanceInDatabase(\"step1\", PROCESS_INSTANCE_ID, true);\n        instanceInDatabase(\"step2\", PROCESS_INSTANCE_ID, false);\n\n        boolean containsToken = gatewayInstanceService.containsToken(PROCESS_INSTANCE_ID,\n                flowNodeDefList(\"step0\", \"step1\", \"step2\"), false);\n\n        assertThat(containsToken).isTrue();\n    }\n\n    @Test\n    public void should_containsToken_for_target_element_with_no_token_return_false() throws Exception {\n        instanceInDatabase(\"step1\", PROCESS_INSTANCE_ID, true);\n        instanceInDatabase(\"step2\", PROCESS_INSTANCE_ID, true);\n\n        boolean containsToken = gatewayInstanceService.containsToken(PROCESS_INSTANCE_ID,\n                flowNodeDefList(\"step0\", \"step1\", \"step2\"), false);\n\n        assertThat(containsToken).isFalse();\n    }\n\n    @Test\n    public void should_containsToken_for_both_element_with_token_return_true1() throws Exception {\n        instanceInDatabase(\"step2\", PROCESS_INSTANCE_ID, true);\n\n        boolean containsToken = gatewayInstanceService.containsToken(PROCESS_INSTANCE_ID,\n                flowNodeDefList(\"step0\", \"step1\", \"step2\"), null);\n\n        assertThat(containsToken).isTrue();\n    }\n\n    @Test\n    public void should_containsToken_for_both_element_with_token_return_true2() throws Exception {\n        instanceInDatabase(\"step2\", PROCESS_INSTANCE_ID, false);\n\n        boolean containsToken = gatewayInstanceService.containsToken(PROCESS_INSTANCE_ID,\n                flowNodeDefList(\"step0\", \"step1\", \"step2\"), null);\n\n        assertThat(containsToken).isTrue();\n    }\n\n    @Test\n    public void should_containsToken_for_both_element_with_no_token_return_false() throws Exception {\n\n        boolean containsToken = gatewayInstanceService.containsToken(PROCESS_INSTANCE_ID,\n                flowNodeDefList(\"step0\", \"step1\", \"step2\"), null);\n\n        assertThat(containsToken).isFalse();\n    }\n\n    List<SFlowNodeDefinition> flowNodeDefList(String... names) {\n        ArrayList<SFlowNodeDefinition> list = new ArrayList<>();\n        for (String name : names) {\n            list.add(node(1, name));\n        }\n        return list;\n    }\n\n    private void instanceInDatabase(String name, long processInstanceId, boolean terminal) throws Exception {\n        SUserTaskInstance sUserTaskInstance = new SUserTaskInstance();\n        sUserTaskInstance.setName(name);\n        sUserTaskInstance.setTerminal(terminal);\n        doReturn(Arrays.asList(sUserTaskInstance)).when(flowNodeInstanceService)\n                .getFlowNodeInstancesByNameAndParentContainerId(name, processInstanceId);\n\n    }\n\n    @Test\n    public void should_transitionsContainsAToken_calls_containsToken_with_middle_node_terminal() throws Exception {\n        SFlowNodeDefinition gate = node(666, \"gate\");\n        node(1, \"step1\");\n        node(2, \"step2\");\n        node(3, \"step3\");\n        instanceInDatabase(\"step2\", PROCESS_INSTANCE_ID, true);\n\n        boolean containsAToken = gatewayInstanceService.transitionsContainsAToken(\n                Arrays.asList(transition(1, 2), transition(2, 3)), gate, PROCESS_INSTANCE_ID,\n                processContainer);\n\n        assertThat(containsAToken).isTrue();\n    }\n\n    @Test\n    public void should_transitionsContainsAToken_calls_containsToken_with_middle_node_not_terminal() throws Exception {\n        SFlowNodeDefinition gate = node(666, \"gate\");\n        node(1, \"step1\");\n        node(2, \"step2\");\n        node(3, \"step3\");\n        instanceInDatabase(\"step2\", PROCESS_INSTANCE_ID, false);\n\n        boolean containsAToken = gatewayInstanceService.transitionsContainsAToken(\n                Arrays.asList(transition(1, 2), transition(2, 3)), gate, PROCESS_INSTANCE_ID,\n                processContainer);\n\n        assertThat(containsAToken).isTrue();\n    }\n\n    @Test\n    public void should_transitionsContainsAToken_calls_containsToken_with_source_node_true() throws Exception {\n        SFlowNodeDefinition gate = node(666, \"gate\");\n        node(1, \"step1\");\n        node(2, \"step2\");\n        node(3, \"step3\");\n        instanceInDatabase(\"step1\", PROCESS_INSTANCE_ID, true);\n\n        boolean containsAToken = gatewayInstanceService.transitionsContainsAToken(\n                Arrays.asList(transition(1, 2), transition(2, 3)), gate, PROCESS_INSTANCE_ID,\n                processContainer);\n\n        assertThat(containsAToken).isTrue();\n    }\n\n    @Test\n    public void should_transitionsContainsAToken_calls_containsToken_with_source_node_false() throws Exception {\n        SFlowNodeDefinition gate = node(666, \"gate\");\n        node(0, \"step0\");\n        node(1, \"step1\");\n        node(2, \"step2\");\n        node(3, \"step3\");\n        transition(0, 1);\n        transition(1, 2);\n        transition(2, 3);\n        instanceInDatabase(\"step1\", PROCESS_INSTANCE_ID, false);\n\n        boolean containsAToken = gatewayInstanceService.transitionsContainsAToken(\n                Arrays.asList(transition(1, 2), transition(2, 3)), gate, PROCESS_INSTANCE_ID,\n                processContainer);\n\n        assertThat(containsAToken).isFalse();\n    }\n\n    @Test\n    public void should_transitionsContainsAToken_calls_containsToken_with_target_node_true() throws Exception {\n        SFlowNodeDefinition gate = node(666, \"gate\");\n        node(1, \"step1\");\n        node(2, \"step2\");\n        node(3, \"step3\");\n        instanceInDatabase(\"step3\", PROCESS_INSTANCE_ID, false);\n\n        boolean containsAToken = gatewayInstanceService.transitionsContainsAToken(\n                Arrays.asList(transition(1, 2), transition(2, 3)), gate, PROCESS_INSTANCE_ID,\n                processContainer);\n\n        assertThat(containsAToken).isTrue();\n    }\n\n    @Test\n    public void should_transitionsContainsAToken_calls_containsToken_with_target_node_false() throws Exception {\n        SFlowNodeDefinition gate = node(666, \"gate\");\n        node(1, \"step1\");\n        node(2, \"step2\");\n        node(3, \"step3\");\n        instanceInDatabase(\"step3\", PROCESS_INSTANCE_ID, true);\n\n        boolean containsAToken = gatewayInstanceService.transitionsContainsAToken(\n                Arrays.asList(transition(1, 2), transition(2, 3)), gate, PROCESS_INSTANCE_ID,\n                processContainer);\n\n        assertThat(containsAToken).isFalse();\n    }\n\n    @Test\n    public void should_transitionsContainsAToken_calls_containsToken_with_no_node() throws Exception {\n        SFlowNodeDefinition gate = node(666, \"gate\");\n        node(1, \"step1\");\n        node(2, \"step2\");\n        node(3, \"step3\");\n\n        boolean containsAToken = gatewayInstanceService.transitionsContainsAToken(\n                Arrays.asList(transition(1, 2), transition(2, 3)), gate, PROCESS_INSTANCE_ID,\n                processContainer);\n\n        assertThat(containsAToken).isFalse();\n    }\n\n    @Test\n    public void should_addBackwardReachableTransitions_complete_list() {\n        SFlowNodeDefinition gate = node(666, \"gate\");\n        node(1, \"stepa1\");\n        node(2, \"stepa2\");\n        transition(1, 2);\n        node(3, \"stepa3\");\n        transition(2, 3);\n        node(4, \"stepb4\");\n        node(5, \"stepb5\");\n        node(6, \"stepb6\");\n        transition(5, 6);\n        transition(4, 6);\n        List<STransitionDefinition> startTransition = Arrays.asList(transition(6, 666), transition(3, 666));\n        List<STransitionDefinition> toComplete = new ArrayList<>();\n        gatewayInstanceService.addBackwardReachableTransitions(processContainer, gate, startTransition, toComplete,\n                Collections.<STransitionDefinition> emptyList());\n\n        assertThat(toComplete).containsOnly(transition(1, 2), transition(2, 3), transition(4, 6), transition(5, 6),\n                transition(3, 666), transition(6, 666));\n    }\n\n    @Test\n    public void should_addBackwardReachableTransitions_with_gateway_having_a_loop_on_itself() {\n        SFlowNodeDefinition gate = node(666, \"gate\");\n        node(1, \"step1\");\n        node(2, \"step2\");\n        transition(1, 666);\n        transition(666, 1);\n        transition(2, 666);\n        List<STransitionDefinition> startTransition = Arrays.asList(transition(1, 666));\n        List<STransitionDefinition> toComplete = new ArrayList<>();\n        gatewayInstanceService.addBackwardReachableTransitions(processContainer, gate, startTransition, toComplete,\n                Collections.<STransitionDefinition> emptyList());\n\n        assertThat(toComplete).containsOnly(transition(1, 666), transition(666, 1));\n    }\n\n    private STransitionDefinition transition(long source, long target) {\n        STransitionDefinitionImpl transition = new STransitionDefinitionImpl(\"name\", source, target);\n        ((SFlowNodeDefinitionImpl) processContainer.getFlowNode(target)).addIncomingTransition(transition);\n        ((SFlowNodeDefinitionImpl) processContainer.getFlowNode(source)).addOutgoingTransition(transition);\n        return transition;\n    }\n\n    @Test\n    public void should_checkMergingCondition_on_inclusive() throws Exception {\n        doReturn(true).when(gatewayInstanceService).isInclusiveGatewayActivated(any(SProcessDefinition.class),\n                any(SGatewayInstance.class));\n        SProcessDefinitionImpl processDefinition = new SProcessDefinitionImpl(\"P\", \"1.0\");\n        SGatewayInstance gate = new SGatewayInstance();\n        gate.setGatewayType(SGatewayType.INCLUSIVE);\n\n        boolean mergingCondition = gatewayInstanceService.checkMergingCondition(processDefinition, gate);\n\n        verify(gatewayInstanceService).isInclusiveGatewayActivated(processDefinition, gate);\n        assertThat(mergingCondition).isTrue();\n    }\n\n    @Test\n    public void should_checkMergingCondition_on_parallel() throws Exception {\n        doReturn(true).when(gatewayInstanceService).isParallelGatewayActivated(any(SProcessDefinition.class),\n                any(SGatewayInstance.class));\n        SProcessDefinitionImpl processDefinition = new SProcessDefinitionImpl(\"P\", \"1.0\");\n        SGatewayInstance gate = new SGatewayInstance();\n        gate.setGatewayType(SGatewayType.PARALLEL);\n\n        boolean mergingCondition = gatewayInstanceService.checkMergingCondition(processDefinition, gate);\n\n        verify(gatewayInstanceService).isParallelGatewayActivated(processDefinition, gate);\n        assertThat(mergingCondition).isTrue();\n    }\n\n    @Test\n    public void should_checkMergingCondition_on_exclusive() throws Exception {\n        SProcessDefinitionImpl processDefinition = new SProcessDefinitionImpl(\"P\", \"1.0\");\n        SGatewayInstance gate = new SGatewayInstance();\n        gate.setGatewayType(SGatewayType.EXCLUSIVE);\n\n        boolean mergingCondition = gatewayInstanceService.checkMergingCondition(processDefinition, gate);\n\n        assertThat(mergingCondition).isTrue();\n    }\n\n    @Test\n    public void should_getMergedTokens_on_exclusive_return_the_first_token() throws Exception {\n        SGatewayInstance gate = new SGatewayInstance();\n        gate.setGatewayType(SGatewayType.EXCLUSIVE);\n\n        List<String> mergedTokens = gatewayInstanceService.getMergedTokens(gate, Arrays.asList(\"1\", \"2\"));\n\n        assertThat(mergedTokens).containsOnly(\"1\");\n    }\n\n    @Test\n    public void should_getMergedTokens_on_parallel() throws Exception {\n        SGatewayInstance gate = new SGatewayInstance();\n        gate.setGatewayType(SGatewayType.PARALLEL);\n\n        List<String> mergedTokens = gatewayInstanceService.getMergedTokens(gate, Arrays.asList(\"1\", \"2\", \"3\", \"2\"));\n\n        assertThat(mergedTokens).containsOnly(\"1\", \"2\", \"3\");\n    }\n\n    @Test\n    public void should_getMergedTokens_on_inclusive() throws Exception {\n        SGatewayInstance gate = new SGatewayInstance();\n        gate.setGatewayType(SGatewayType.INCLUSIVE);\n\n        List<String> mergedTokens = gatewayInstanceService.getMergedTokens(gate, Arrays.asList(\"1\", \"2\", \"3\", \"2\"));\n\n        assertThat(mergedTokens).containsOnly(\"1\", \"2\", \"3\");\n    }\n\n    @Test\n    public void should_getRemainingToken_return_not_merged_tokens() {\n        List<String> remainingTokens = gatewayInstanceService.getRemainingTokens(Arrays.asList(\"1\", \"2\", \"3\", \"2\", \"1\"),\n                Arrays.asList(\"1\", \"2\", \"3\"));\n\n        assertThat(remainingTokens).containsOnly(\"2\", \"1\");\n    }\n\n    @Test\n    public void should_parallelBehavior_merged() {\n        node(666, \"gate\");\n        node(1, \"step1\");\n        node(2, \"step2\");\n        node(3, \"step3\");\n        SProcessDefinitionImpl processDefinition = new SProcessDefinitionImpl(\"P\", \"1.0\");\n        processDefinition.setProcessContainer(processContainer);\n        SGatewayInstance gate = new SGatewayInstance();\n        gate.setName(\"gate\");\n        gate.setHitBys(\"1,2,3,2\");\n        gate.setFlowNodeDefinitionId(666);\n        node(666, \"gate\");\n        transition(1, 666);\n        transition(2, 666);\n        transition(3, 666);\n\n        boolean isMerged = gatewayInstanceService.isParallelGatewayActivated(processDefinition, gate);\n\n        assertThat(isMerged).isTrue();\n    }\n\n    @Test\n    public void should_parallelBehavior_not_merged() {\n        node(666, \"gate\");\n        node(1, \"step1\");\n        node(2, \"step2\");\n        node(3, \"step3\");\n        SProcessDefinitionImpl processDefinition = new SProcessDefinitionImpl(\"P\", \"1.0\");\n        processDefinition.setProcessContainer(processContainer);\n        SGatewayInstance gate = new SGatewayInstance();\n        gate.setName(\"gate\");\n        gate.setHitBys(\"2,3,2\");\n        gate.setFlowNodeDefinitionId(666);\n        node(666, \"gate\");\n        transition(1, 666);\n        transition(2, 666);\n        transition(3, 666);\n\n        boolean isMerged = gatewayInstanceService.isParallelGatewayActivated(processDefinition, gate);\n\n        assertThat(isMerged).isFalse();\n    }\n\n    @Test\n    public void should_inclusiveBehavior_merged() throws SBonitaReadException {\n        node(666, \"gate\");\n        node(1, \"step1\");\n        node(2, \"step2\");\n        node(3, \"step3\");\n        SProcessDefinitionImpl processDefinition = new SProcessDefinitionImpl(\"P\", \"1.0\");\n        processDefinition.setProcessContainer(processContainer);\n        SGatewayInstance gate = new SGatewayInstance();\n        gate.setName(\"gate\");\n        gate.setHitBys(\"1,2,3,2\");\n        gate.setFlowNodeDefinitionId(666);\n        gate.setParentContainerId(PROCESS_INSTANCE_ID);\n        SFlowNodeDefinition nodeDefinition = node(666, \"gate\");\n        transition(1, 666);\n        transition(2, 666);\n        transition(3, 666);\n        doNothing().when(gatewayInstanceService).addBackwardReachableTransitions(\n                any(SFlowElementContainerDefinition.class), any(SFlowNodeDefinition.class), anyList(), anyList(),\n                anyList());\n        doReturn(true).when(gatewayInstanceService).transitionsContainsAToken(anyList(), any(SFlowNodeDefinition.class),\n                anyLong(), any(SFlowElementContainerDefinition.class));\n\n        boolean isMerged = gatewayInstanceService.isInclusiveGatewayActivated(processDefinition, gate);\n\n        assertThat(isMerged).isFalse();\n        verify(gatewayInstanceService, times(2)).addBackwardReachableTransitions(eq(processContainer),\n                eq(nodeDefinition), anyList(), anyList(), anyList());\n        verify(gatewayInstanceService).transitionsContainsAToken(anyList(), eq(nodeDefinition), eq(PROCESS_INSTANCE_ID),\n                eq(processContainer));\n    }\n\n    @Test\n    public void should_isInclusiveGatewayActivated_return_false_when_finished() throws SBonitaReadException {\n        SProcessDefinitionImpl processDefinition = new SProcessDefinitionImpl(\"P\", \"1.0\");\n        processDefinition.setProcessContainer(processContainer);\n        SGatewayInstance gate = new SGatewayInstance();\n        gate.setName(\"gate\");\n        gate.setHitBys(\"FINISH:1\");\n        gate.setFlowNodeDefinitionId(50L);\n        gate.setParentContainerId(PROCESS_INSTANCE_ID);\n        node(50L, \"gate\");\n\n        boolean activated = gatewayInstanceService.isInclusiveGatewayActivated(processDefinition, gate);\n\n        assertThat(activated).isFalse();\n    }\n\n    @Test\n    public void should_setState_change_lastUpdate_and_reachStateDate()\n            throws SGatewayModificationException, SRecorderException {\n        SGatewayInstance gate = new SGatewayInstance();\n\n        gatewayInstanceService.setState(gate, 12);\n\n        verify(recorder).recordUpdate(updateRecordCaptor.capture(), nullable(String.class));\n\n        assertThat(updateRecordCaptor.getValue().getFields().keySet()).contains(\"stateId\", \"reachedStateDate\",\n                \"lastUpdateDate\");\n    }\n\n    @Test\n    public void should_getActiveGatewayOfProcess_return_the_gateway() throws Exception {\n        SGatewayInstance gate = new SGatewayInstance();\n        doReturn(gate).when(persistenceRead)\n                .selectOne(SelectDescriptorBuilder.getActiveGatewayInstanceOfProcess(PROCESS_INSTANCE_ID, \"myGate\"));\n\n        final SGatewayInstance myGate = gatewayInstanceService.getActiveGatewayInstanceOfTheProcess(PROCESS_INSTANCE_ID,\n                \"myGate\");\n\n        assertThat(myGate).isEqualTo(gate);\n    }\n\n    @Test\n    public void should_getActiveGatewayOfProcess_return_null_if_not_found() throws Exception {\n        doReturn(null).when(persistenceRead)\n                .selectOne(SelectDescriptorBuilder.getActiveGatewayInstanceOfProcess(PROCESS_INSTANCE_ID, \"myGate\"));\n\n        final SGatewayInstance myGate = gatewayInstanceService.getActiveGatewayInstanceOfTheProcess(PROCESS_INSTANCE_ID,\n                \"myGate\");\n\n        assertThat(myGate).isNull();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/impl/ProcessInstanceServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.impl;\n\nimport static java.util.Arrays.asList;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier;\nimport static org.bonitasoft.engine.core.process.instance.impl.ProcessInstanceServiceImpl.IN_REQUEST_SIZE;\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.archive.ArchiveService;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.connector.ConnectorInstanceService;\nimport org.bonitasoft.engine.core.contract.data.ContractDataService;\nimport org.bonitasoft.engine.core.document.api.DocumentService;\nimport org.bonitasoft.engine.core.process.comment.api.SCommentService;\nimport org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;\nimport org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.SEventDefinition;\nimport org.bonitasoft.engine.core.process.definition.model.event.impl.SBoundaryEventDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.event.impl.SIntermediateThrowEventDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.event.impl.SStartEventDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.SCatchSignalEventTriggerDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.SThrowMessageEventTriggerDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.STimerEventTriggerDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SFlowElementContainerDefinitionImpl;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SProcessDefinitionImpl;\nimport org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.BPMFailureService;\nimport org.bonitasoft.engine.core.process.instance.api.RefBusinessDataService;\nimport org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceModificationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceReadException;\nimport org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SGatewayInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance;\nimport org.bonitasoft.engine.core.process.instance.model.STaskPriority;\nimport org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SIntermediateCatchEventInstance;\nimport org.bonitasoft.engine.core.process.instance.model.event.SStartEventInstance;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceService;\nimport org.bonitasoft.engine.dependency.model.ScopeType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.SystemOutRule;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentMatchers;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class ProcessInstanceServiceImplTest {\n\n    @Rule\n    public SystemOutRule systemOutRule = new SystemOutRule().enableLog();\n    private final long processInstanceId = 574815189L;\n\n    private final long archivedProcessInstanceId = 11223344L;\n\n    @Mock\n    private SProcessInstance processInstance;\n\n    @Mock\n    private SAProcessInstance aProcessInstance;\n\n    @Mock\n    private ClassLoaderService classLoaderService;\n\n    @Mock\n    private EventInstanceService eventInstanceService;\n\n    @Mock\n    private Recorder recorder;\n\n    @Mock\n    private ArchiveService archiveService;\n\n    @Mock\n    private ReadPersistenceService readPersistenceService;\n\n    @Mock\n    private ActivityInstanceService activityInstanceService;\n\n    @Mock\n    private DataInstanceService dataInstanceService;\n\n    @Mock\n    private ProcessDefinitionService processDefinitionService;\n\n    @Mock\n    private ConnectorInstanceService connectorInstanceService;\n\n    @Mock\n    private DocumentService documentService;\n\n    @Mock\n    private SCommentService sCommentService;\n\n    @Mock\n    private RefBusinessDataService refBusinessDataService;\n\n    @Mock\n    private ContractDataService contractDataService;\n\n    @Mock\n    private BPMFailureService bpmFailureService;\n\n    @Spy\n    @InjectMocks\n    private ProcessInstanceServiceImpl processInstanceService;\n\n    @Before\n    public void setUp() throws SBonitaException {\n        doCallRealMethod().when(processInstanceService).deleteParentProcessInstanceAndElements(anyList());\n\n        when(processInstance.getId()).thenReturn(processInstanceId);\n\n        when(archiveService.getDefinitiveArchiveReadPersistenceService()).thenReturn(readPersistenceService);\n    }\n\n    @Test\n    public void deleteParentProcessInstanceAndElementsOnAbsentProcessShouldBeIgnored() throws Exception {\n        // given:\n        doThrow(SProcessInstanceModificationException.class).when(processInstanceService)\n                .deleteProcessInstance(processInstance);\n        doThrow(SProcessInstanceNotFoundException.class).when(processInstanceService)\n                .getProcessInstance(processInstanceId);\n\n        // when:\n        processInstanceService.deleteParentProcessInstanceAndElements(processInstance);\n\n        // then:\n        verify(processInstanceService).getProcessInstance(processInstanceId);\n    }\n\n    @Test(expected = SBonitaException.class)\n    public void deleteParentProcessInstanceAndElements_should_throw_exception_when_deleteProcessInstance_failed()\n            throws Exception {\n        // given:\n        doThrow(SProcessInstanceModificationException.class).when(processInstanceService)\n                .deleteProcessInstance(processInstance);\n        // getProcessInstance normally returns:\n        doReturn(mock(SProcessInstance.class)).when(processInstanceService).getProcessInstance(processInstanceId);\n\n        try {\n            // when:\n            processInstanceService.deleteParentProcessInstanceAndElements(processInstance);\n        } finally {\n            // then:\n            verify(processInstanceService).getProcessInstance(processInstanceId);\n        }\n    }\n\n    @Test\n    public void deleteParentProcessInstanceAndElements_returns_0_when_no_elements_are_deleted() throws Exception {\n        assertEquals(0, processInstanceService\n                .deleteParentProcessInstanceAndElements(Collections.<SProcessInstance> emptyList()));\n    }\n\n    @Test\n    public void deleteParentProcessInstanceAndElements_returns_1_when_1_elements_are_deleted() throws Exception {\n        final List<SProcessInstance> processInstances = asList(mock(SProcessInstance.class));\n        assertEquals(1, processInstanceService.deleteParentProcessInstanceAndElements(processInstances));\n    }\n\n    @Test\n    public void deleteParentProcessInstanceAndElements_returns_n_when_n_elements_are_deleted() throws Exception {\n        final List<SProcessInstance> processInstances = asList(mock(SProcessInstance.class),\n                mock(SProcessInstance.class), mock(SProcessInstance.class));\n        assertEquals(3, processInstanceService.deleteParentProcessInstanceAndElements(processInstances));\n    }\n\n    @Test\n    public void testDeleteProcessInstance_delete_archived_activity() throws Exception {\n        final SProcessInstance sProcessInstance = mock(SProcessInstance.class);\n        final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();\n        when(classLoaderService.getClassLoader(identifier(ScopeType.PROCESS, sProcessInstance.getId())))\n                .thenReturn(classLoader);\n        doReturn(new HashSet<>(asList(4L, 5L, 6L))).when(activityInstanceService)\n                .getSourceObjectIdsOfArchivedFlowNodeInstances(any());\n        processInstanceService.deleteParentProcessInstanceAndElements(sProcessInstance);\n        verify(processInstanceService, times(1)).deleteProcessInstanceElements(sProcessInstance);\n        verify(processInstanceService, times(1))\n                .deleteArchivedProcessInstances(Collections.singletonList(sProcessInstance.getId()));\n        verify(activityInstanceService, times(1)).deleteArchivedFlowNodeInstances(asList(4L, 5L, 6L));\n        verify(bpmFailureService).deleteArchivedFlowNodeFailures(asList(4L, 5L, 6L));\n    }\n\n    @Test\n    public void testDeleteProcessInstance_delete_failures() throws Exception {\n        final SProcessInstance sProcessInstance = mock(SProcessInstance.class);\n        final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();\n        when(classLoaderService.getClassLoader(identifier(ScopeType.PROCESS, sProcessInstance.getId())))\n                .thenReturn(classLoader);\n        doReturn(new HashSet<>()).when(activityInstanceService)\n                .getSourceObjectIdsOfArchivedFlowNodeInstances(any());\n        processInstanceService.deleteParentProcessInstanceAndElements(sProcessInstance);\n        verify(bpmFailureService).deleteProcessInstanceFailures(sProcessInstance.getId());\n        verify(bpmFailureService)\n                .deleteArchivedProcessInstanceFailures(Collections.singletonList(sProcessInstance.getId()));\n    }\n\n    @Test\n    public void getNumberOfFailedProcessInstances_should_return_number_of_failed_process_instance() throws Exception {\n        // Given\n        final QueryOptions queryOptions = new QueryOptions(0, 10);\n        final long number = 2L;\n        doReturn(number).when(readPersistenceService).getNumberOfEntities(SProcessInstance.class, \"Failed\",\n                queryOptions, null);\n\n        // When\n        final long result = processInstanceService.getNumberOfFailedProcessInstances(queryOptions);\n\n        // Then\n        assertEquals(\"The result should be equals to the number returned by the mock.\", number, result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getNumberOfFailedProcessInstances_should_throw_exception_when_persistence_service_failed()\n            throws Exception {\n        // Given\n        final QueryOptions queryOptions = new QueryOptions(0, 10);\n        doThrow(new SBonitaReadException(\"plop\")).when(readPersistenceService)\n                .getNumberOfEntities(SProcessInstance.class, \"Failed\", queryOptions, null);\n\n        // When\n        processInstanceService.getNumberOfFailedProcessInstances(queryOptions);\n    }\n\n    @Test\n    public void searchFailedProcessInstances_should_return_list_of_failed_process_instance() throws Exception {\n        // Given\n        final QueryOptions queryOptions = new QueryOptions(0, 10);\n        final List<ProcessInstance> list = asList(mock(ProcessInstance.class));\n        doReturn(list).when(readPersistenceService).searchEntity(SProcessInstance.class, \"Failed\", queryOptions, null);\n\n        // When\n        final List<SProcessInstance> result = processInstanceService.searchFailedProcessInstances(queryOptions);\n\n        // Then\n        assertEquals(\"The result should be equals to the list returned by the mock.\", list, result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void searchFailedProcessInstances_should_throw_exception_when_persistence_service_failed() throws Exception {\n        // Given\n        final QueryOptions queryOptions = new QueryOptions(0, 10);\n        doThrow(new SBonitaReadException(\"plop\")).when(readPersistenceService).searchEntity(SProcessInstance.class,\n                \"Failed\", queryOptions, null);\n\n        // When\n        processInstanceService.searchFailedProcessInstances(queryOptions);\n    }\n\n    @Test\n    public void getNumberOfOpenProcessInstancesSupervisedBy_should_return_number_of_open_process_instance_supervised_by()\n            throws Exception {\n        // Given\n        final QueryOptions queryOptions = new QueryOptions(0, 10);\n        final long userId = 198L;\n        final long number = 2L;\n        doReturn(number).when(readPersistenceService).getNumberOfEntities(eq(SProcessInstance.class),\n                eq(\"SupervisedBy\"), eq(queryOptions),\n                anyMap());\n\n        // When\n        final long result = processInstanceService.getNumberOfOpenProcessInstancesSupervisedBy(userId, queryOptions);\n\n        // Then\n        assertEquals(\"The result should be equals to the number returned by the mock.\", number, result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getNumberOfOpenProcessInstancesSupervisedBy_should_throw_exception_when_persistence_service_failed()\n            throws Exception {\n        // Given\n        final QueryOptions queryOptions = new QueryOptions(0, 10);\n        final long userId = 198L;\n        doThrow(new SBonitaReadException(\"plop\")).when(readPersistenceService).getNumberOfEntities(\n                eq(SProcessInstance.class), eq(\"SupervisedBy\"),\n                eq(queryOptions),\n                anyMap());\n\n        // When\n        processInstanceService.getNumberOfOpenProcessInstancesSupervisedBy(userId, queryOptions);\n    }\n\n    @Test\n    public void searchOpenProcessInstancesSupervisedBy_should_return_list_of_open_process_instance_supervised_by()\n            throws Exception {\n        // Given\n        final QueryOptions queryOptions = new QueryOptions(0, 10);\n        final long userId = 198L;\n        final List<ProcessInstance> list = asList(mock(ProcessInstance.class));\n        doReturn(list).when(readPersistenceService).searchEntity(eq(SProcessInstance.class), eq(\"SupervisedBy\"),\n                eq(queryOptions),\n                anyMap());\n\n        // When\n        final List<SProcessInstance> result = processInstanceService.searchOpenProcessInstancesSupervisedBy(userId,\n                queryOptions);\n\n        // Then\n        assertEquals(\"The result should be equals to the list returned by the mock.\", list, result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void searchOpenProcessInstancesSupervisedBy_should_throw_exception_when_persistence_service_failed()\n            throws Exception {\n        // Given\n        final QueryOptions queryOptions = new QueryOptions(0, 10);\n        final long userId = 198L;\n        doThrow(new SBonitaReadException(\"plop\")).when(readPersistenceService).searchEntity(eq(SProcessInstance.class),\n                eq(\"SupervisedBy\"), eq(queryOptions),\n                anyMap());\n\n        // When\n        processInstanceService.searchOpenProcessInstancesSupervisedBy(userId, queryOptions);\n    }\n\n    @Test\n    public void getNumberOfOpenProcessInstancesInvolvingUser_should_return_number_of_open_process_instance_involving_user()\n            throws Exception {\n        // Given\n        final QueryOptions queryOptions = new QueryOptions(0, 10);\n        final long userId = 198L;\n        final long number = 2L;\n        doReturn(number).when(readPersistenceService).getNumberOfEntities(eq(SProcessInstance.class),\n                eq(\"InvolvingUser\"), eq(queryOptions),\n                anyMap());\n\n        // When\n        final long result = processInstanceService.getNumberOfOpenProcessInstancesInvolvingUser(userId, queryOptions);\n\n        // Then\n        assertEquals(\"The result should be equals to the number returned by the mock.\", number, result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getNumberOfOpenProcessInstancesInvolvingUser_should_throw_exception_when_persistence_service_failed()\n            throws Exception {\n        // Given\n        final QueryOptions queryOptions = new QueryOptions(0, 10);\n        final long userId = 198L;\n        doThrow(new SBonitaReadException(\"plop\")).when(readPersistenceService).getNumberOfEntities(\n                eq(SProcessInstance.class), eq(\"InvolvingUser\"),\n                eq(queryOptions),\n                anyMap());\n\n        // When\n        processInstanceService.getNumberOfOpenProcessInstancesInvolvingUser(userId, queryOptions);\n    }\n\n    @Test\n    public void searchOpenProcessInstancesInvolvingUser_should_return_list_of_open_process_instance_involving_user()\n            throws Exception {\n        // Given\n        final QueryOptions queryOptions = new QueryOptions(0, 10);\n        final long userId = 198L;\n        final List<ProcessInstance> list = asList(mock(ProcessInstance.class));\n        doReturn(list).when(readPersistenceService).searchEntity(eq(SProcessInstance.class), eq(\"InvolvingUser\"),\n                eq(queryOptions),\n                anyMap());\n\n        // When\n        final List<SProcessInstance> result = processInstanceService.searchOpenProcessInstancesInvolvingUser(userId,\n                queryOptions);\n\n        // Then\n        assertEquals(\"The result should be equals to the list returned by the mock.\", list, result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void searchOpenProcessInstancesInvolvingUser_should_throw_exception_when_persistence_service_failed()\n            throws Exception {\n        // Given\n        final QueryOptions queryOptions = new QueryOptions(0, 10);\n        final long userId = 198L;\n        doThrow(new SBonitaReadException(\"plop\")).when(readPersistenceService).searchEntity(eq(SProcessInstance.class),\n                eq(\"InvolvingUser\"), eq(queryOptions),\n                anyMap());\n\n        // When\n        processInstanceService.searchOpenProcessInstancesInvolvingUser(userId, queryOptions);\n    }\n\n    @Test\n    public void getNumberOfOpenProcessInstancesInvolvingUsersManagedBy_should_return_number_of_open_process_instance_involving_users_managed_by()\n            throws Exception {\n        // Given\n        final QueryOptions queryOptions = new QueryOptions(0, 10);\n        final long userId = 198L;\n        final long number = 2L;\n        doReturn(number).when(readPersistenceService).getNumberOfEntities(eq(SProcessInstance.class),\n                eq(\"InvolvingUsersManagedBy\"), eq(queryOptions),\n                anyMap());\n\n        // When\n        final long result = processInstanceService.getNumberOfOpenProcessInstancesInvolvingUsersManagedBy(userId,\n                queryOptions);\n\n        // Then\n        assertEquals(\"The result should be equals to the number returned by the mock.\", number, result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getNumberOfOpenProcessInstancesInvolvingUsersManagedBy_should_throw_exception_when_persistence_service_failed()\n            throws Exception {\n        // Given\n        final QueryOptions queryOptions = new QueryOptions(0, 10);\n        final long userId = 198L;\n        doThrow(new SBonitaReadException(\"plop\")).when(readPersistenceService).getNumberOfEntities(\n                eq(SProcessInstance.class), eq(\"InvolvingUsersManagedBy\"),\n                eq(queryOptions),\n                anyMap());\n\n        // When\n        processInstanceService.getNumberOfOpenProcessInstancesInvolvingUsersManagedBy(userId, queryOptions);\n    }\n\n    @Test\n    public void searchOpenProcessInstancesInvolvingUsersManagedBy_should_return_list_of_open_process_instance_involving_users_managed_by()\n            throws Exception {\n        // Given\n        final QueryOptions queryOptions = new QueryOptions(0, 10);\n        final long userId = 198L;\n        final List<ProcessInstance> list = asList(mock(ProcessInstance.class));\n        doReturn(list).when(readPersistenceService).searchEntity(eq(SProcessInstance.class),\n                eq(\"InvolvingUsersManagedBy\"), eq(queryOptions),\n                anyMap());\n\n        // When\n        final List<SProcessInstance> result = processInstanceService\n                .searchOpenProcessInstancesInvolvingUsersManagedBy(userId, queryOptions);\n\n        // Then\n        assertEquals(\"The result should be equals to the list returned by the mock.\", list, result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void searchOpenProcessInstancesInvolvingUsersManagedBy_should_throw_exception_when_persistence_service_failed()\n            throws Exception {\n        // Given\n        final QueryOptions queryOptions = new QueryOptions(0, 10);\n        final long userId = 198L;\n        doThrow(new SBonitaReadException(\"plop\")).when(readPersistenceService).searchEntity(eq(SProcessInstance.class),\n                eq(\"InvolvingUsersManagedBy\"),\n                eq(queryOptions),\n                anyMap());\n\n        // When\n        processInstanceService.searchOpenProcessInstancesInvolvingUsersManagedBy(userId, queryOptions);\n    }\n\n    @Test\n    public void getArchivedProcessInstancesInAllStates_should_return_list_of_archived_process_instance()\n            throws Exception {\n        // Given\n        final List<Long> archivedProcessInstanceIds = asList(41L);\n        final Map<String, Object> parameters = Collections.singletonMap(\"sourceObjectIds\",\n                (Object) archivedProcessInstanceIds);\n        final SelectListDescriptor<SAProcessInstance> selectListDescriptor = new SelectListDescriptor<SAProcessInstance>(\n                \"getArchivedProcessInstancesInAllStates\", parameters, SAProcessInstance.class,\n                new QueryOptions(0, archivedProcessInstanceIds.size()));\n        final List<SAProcessInstance> saProcessInstances = asList(mock(SAProcessInstance.class));\n        doReturn(saProcessInstances).when(readPersistenceService).selectList(selectListDescriptor);\n\n        // When\n        final List<SAProcessInstance> archivedProcessInstances = processInstanceService\n                .getArchivedProcessInstancesInAllStates(archivedProcessInstanceIds);\n\n        // Then\n        assertEquals(\"The result should be equals to the list returned by the mock.\", saProcessInstances,\n                archivedProcessInstances);\n        verify(readPersistenceService).selectList(selectListDescriptor);\n    }\n\n    @Test(expected = SProcessInstanceReadException.class)\n    public void getArchivedProcessInstancesInAllStates_should_throw_exception_when_there_is_problem() throws Exception {\n        // Given\n        final List<Long> archivedProcessInstanceIds = asList(41L);\n        final Map<String, Object> parameters = Collections.singletonMap(\"sourceObjectIds\",\n                (Object) archivedProcessInstanceIds);\n        final SelectListDescriptor<SAProcessInstance> selectListDescriptor = new SelectListDescriptor<SAProcessInstance>(\n                \"getArchivedProcessInstancesInAllStates\", parameters, SAProcessInstance.class,\n                new QueryOptions(0, archivedProcessInstanceIds.size()));\n        doThrow(new SBonitaReadException(\"plop\")).when(readPersistenceService).selectList(selectListDescriptor);\n\n        // When\n        processInstanceService.getArchivedProcessInstancesInAllStates(archivedProcessInstanceIds);\n    }\n\n    @Test\n    public void getArchivedProcessInstance_should_return_archived_process_instance() throws Exception {\n        // Given\n        final long archivedProcessInstanceId = 41L;\n        final Map<String, Object> parameters = Collections.singletonMap(\"id\", (Object) archivedProcessInstanceId);\n        final SelectOneDescriptor<SAProcessInstance> selectOneDescriptor = new SelectOneDescriptor<SAProcessInstance>(\n                \"getArchivedProcessInstance\", parameters, SAProcessInstance.class);\n        final SAProcessInstance saProcessInstance = mock(SAProcessInstance.class);\n        doReturn(saProcessInstance).when(readPersistenceService).selectOne(selectOneDescriptor);\n\n        // When\n        final SAProcessInstance archivedProcessInstance = processInstanceService\n                .getArchivedProcessInstance(archivedProcessInstanceId);\n\n        // Then\n        assertEquals(\"The result should be equals to the list returned by the mock.\", saProcessInstance,\n                archivedProcessInstance);\n        verify(readPersistenceService).selectOne(selectOneDescriptor);\n    }\n\n    @Test(expected = SProcessInstanceReadException.class)\n    public void getArchivedProcessInstance_should_throw_exception_when_there_is_problem() throws Exception {\n        // Given\n        final long archivedProcessInstanceId = 41L;\n        final Map<String, Object> parameters = Collections.singletonMap(\"id\", (Object) archivedProcessInstanceId);\n        final SelectOneDescriptor<SAProcessInstance> selectOneDescriptor = new SelectOneDescriptor<SAProcessInstance>(\n                \"getArchivedProcessInstance\", parameters, SAProcessInstance.class);\n        doThrow(new SBonitaReadException(\"plop\")).when(readPersistenceService).selectOne(selectOneDescriptor);\n\n        // When\n        processInstanceService.getArchivedProcessInstance(archivedProcessInstanceId);\n    }\n\n    @Test\n    public void deleteFlowNodeInstanceElements_should_delete_child_process_instance_when_flownode_is_sub_process()\n            throws Exception {\n        final SFlowNodeInstance flowNodeInstance = new SSubProcessActivityInstance();\n\n        deleteFlowNodeInstanceElements_should_delete_child_process_instance(flowNodeInstance);\n    }\n\n    @Test\n    public void deleteFlowNodeInstanceElements_should_delete_child_process_instance_when_flownode_is_call_activity()\n            throws Exception {\n        final SFlowNodeInstance flowNodeInstance = new SCallActivityInstance();\n\n        deleteFlowNodeInstanceElements_should_delete_child_process_instance(flowNodeInstance);\n    }\n\n    private void deleteFlowNodeInstanceElements_should_delete_child_process_instance(\n            final SFlowNodeInstance flowNodeInstance)\n            throws SBonitaException {\n        // Given\n        final SProcessDefinition processDefinition = mock(SProcessDefinition.class);\n        doReturn(mock(SFlowElementContainerDefinition.class)).when(processDefinition).getProcessContainer();\n        doNothing().when(processInstanceService).deleteDataInstancesIfNecessary(flowNodeInstance, processDefinition);\n        doNothing().when(processInstanceService).deleteConnectorInstancesIfNecessary(flowNodeInstance,\n                processDefinition);\n        final SProcessInstance sProcessInstance = mock(SProcessInstance.class);\n        doReturn(sProcessInstance).when(processInstanceService).getChildOfActivity(flowNodeInstance.getId());\n        doNothing().when(processInstanceService).deleteProcessInstance(sProcessInstance);\n\n        // When\n        processInstanceService.deleteFlowNodeInstanceElements(flowNodeInstance, processDefinition);\n\n        // Then\n        verify(processInstanceService).deleteProcessInstance(sProcessInstance);\n    }\n\n    @Test\n    public void deleteFlowNodeInstanceElements_should_log_exception_when_getChildOfActivity_failed_and_log_is_active()\n            throws Exception {\n        // Given\n        final SFlowNodeInstance flowNodeInstance = new SSubProcessActivityInstance();\n        final SProcessDefinition processDefinition = mock(SProcessDefinition.class);\n        doReturn(mock(SFlowElementContainerDefinition.class)).when(processDefinition).getProcessContainer();\n        doNothing().when(processInstanceService).deleteDataInstancesIfNecessary(flowNodeInstance, processDefinition);\n        doNothing().when(processInstanceService).deleteConnectorInstancesIfNecessary(flowNodeInstance,\n                processDefinition);\n        final SProcessInstanceNotFoundException exception = new SProcessInstanceNotFoundException(6);\n        doThrow(exception).when(processInstanceService).getChildOfActivity(flowNodeInstance.getId());\n\n        // When\n        systemOutRule.clearLog();\n        processInstanceService.deleteFlowNodeInstanceElements(flowNodeInstance, processDefinition);\n\n        // Then\n        assertThat(systemOutRule.getLog()).contains(\"Process instance with id <6> not found\");\n    }\n\n    @Test\n    public void deleteFlowNodeInstanceElements_should_only_log_in_debug_when_getChildOfActivity_failed()\n            throws Exception {\n        // Given\n        final SFlowNodeInstance flowNodeInstance = new SCallActivityInstance();\n        final SProcessDefinition processDefinition = mock(SProcessDefinition.class);\n        doReturn(mock(SFlowElementContainerDefinition.class)).when(processDefinition).getProcessContainer();\n        doNothing().when(processInstanceService).deleteDataInstancesIfNecessary(flowNodeInstance, processDefinition);\n        doNothing().when(processInstanceService).deleteConnectorInstancesIfNecessary(flowNodeInstance,\n                processDefinition);\n        final SProcessInstanceNotFoundException exception = new SProcessInstanceNotFoundException(6);\n        doThrow(exception).when(processInstanceService).getChildOfActivity(flowNodeInstance.getId());\n\n        // When\n        processInstanceService.deleteFlowNodeInstanceElements(flowNodeInstance, processDefinition);\n        assertThat(systemOutRule.getLog()).containsPattern(\"DEBUG.*.Process instance with id <6> not found\");\n    }\n\n    @Test\n    public void deleteFlowNodeInstanceElements_should_call_deleteWaitingEvents_when_flownode_is_type_INTERMEDIATE_CATCH_EVENT()\n            throws Exception {\n        // Given\n        final SFlowNodeInstance flowNodeInstance = new SIntermediateCatchEventInstance();\n        flowNodeInstance.setFlowNodeDefinitionId(42);\n        SIntermediateThrowEventDefinitionImpl definition = new SIntermediateThrowEventDefinitionImpl(42, \"inter\");\n        definition.addMessageEventTriggerDefinition(new SThrowMessageEventTriggerDefinitionImpl());\n        final SProcessDefinition processDefinition = aProcess().with(definition).done();\n\n        // When\n        processInstanceService.deleteFlowNodeInstanceElements(flowNodeInstance, processDefinition);\n\n        // Then\n        verify(eventInstanceService).deleteWaitingEvents(flowNodeInstance);\n    }\n\n    @Test\n    public void deleteFlowNodeInstanceElements_should_call_deleteWaitingEvents_when_flownode_is_type_RECEIVE_TASK()\n            throws Exception {\n        // Given\n        final SFlowNodeInstance flowNodeInstance = new SReceiveTaskInstance();\n        final SProcessDefinition processDefinition = mock(SProcessDefinition.class);\n        doNothing().when(processInstanceService).deleteDataInstancesIfNecessary(flowNodeInstance, processDefinition);\n        doNothing().when(processInstanceService).deleteConnectorInstancesIfNecessary(flowNodeInstance,\n                processDefinition);\n\n        // When\n        processInstanceService.deleteFlowNodeInstanceElements(flowNodeInstance, processDefinition);\n\n        // Then\n        verify(eventInstanceService).deleteWaitingEvents(flowNodeInstance);\n    }\n\n    @Test\n    public void deleteFlowNodeInstanceElements_should_not_call_deleteWaitingEvents_when_flownode_is_type_START_EVENT_with_no_trigger()\n            throws Exception {\n        // Given\n        final SFlowNodeInstance flowNodeInstance = new SStartEventInstance();\n        flowNodeInstance.setFlowNodeDefinitionId(42);\n        final SProcessDefinition processDefinition = aProcess().with(new SStartEventDefinitionImpl(42, \"start\")).done();\n\n        // When\n        processInstanceService.deleteFlowNodeInstanceElements(flowNodeInstance, processDefinition);\n\n        // Then\n        verify(eventInstanceService, never()).deleteWaitingEvents(flowNodeInstance);\n    }\n\n    @Test\n    public void deleteFlowNodeInstanceElements_should_call_deleteWaitingEvents_when_flownode_is_type_START_EVENT()\n            throws Exception {\n        // Given\n        final SFlowNodeInstance flowNodeInstance = new SStartEventInstance();\n        flowNodeInstance.setFlowNodeDefinitionId(42);\n        SStartEventDefinitionImpl start = new SStartEventDefinitionImpl(42, \"start\");\n        start.addSignalEventTrigger(new SCatchSignalEventTriggerDefinitionImpl(\"signal\"));\n        final SProcessDefinition processDefinition = aProcess().with(start).done();\n\n        // When\n        processInstanceService.deleteFlowNodeInstanceElements(flowNodeInstance, processDefinition);\n\n        // Then\n        verify(eventInstanceService).deleteWaitingEvents(flowNodeInstance);\n    }\n\n    @Test\n    public void deleteFlowNodeInstanceElements_should_call_deleteWaitingEvents_when_flownode_is_type_BOUNDARY_EVENT()\n            throws Exception {\n        // Given\n        final SFlowNodeInstance flowNodeInstance = new SBoundaryEventInstance();\n        flowNodeInstance.setFlowNodeDefinitionId(42);\n        SBoundaryEventDefinitionImpl boundary = new SBoundaryEventDefinitionImpl(42, \"boundary\");\n        boundary.addTimerEventTrigger(new STimerEventTriggerDefinitionImpl(null, null));\n        final SProcessDefinition processDefinition = aProcess().with(boundary).done();\n\n        // When\n        processInstanceService.deleteFlowNodeInstanceElements(flowNodeInstance, processDefinition);\n\n        // Then\n        verify(eventInstanceService).deleteWaitingEvents(flowNodeInstance);\n    }\n\n    @Test\n    public void deleteFlowNodeInstanceElements_should_call_deletePendingMappings_when_flownode_is_type_USER_TASK()\n            throws Exception {\n        // Given\n        final SFlowNodeInstance flowNodeInstance = new SUserTaskInstance(\"name\", 3L, 6L, 9L, 12L,\n                STaskPriority.ABOVE_NORMAL, 7L, 8L);\n        final SProcessDefinition processDefinition = mock(SProcessDefinition.class);\n        doReturn(mock(SFlowElementContainerDefinition.class)).when(processDefinition).getProcessContainer();\n        doNothing().when(processInstanceService).deleteDataInstancesIfNecessary(flowNodeInstance, processDefinition);\n        doNothing().when(processInstanceService).deleteConnectorInstancesIfNecessary(flowNodeInstance,\n                processDefinition);\n\n        // When\n        processInstanceService.deleteFlowNodeInstanceElements(flowNodeInstance, processDefinition);\n\n        // Then\n        verify(activityInstanceService).deletePendingMappings(flowNodeInstance.getId());\n    }\n\n    @Test\n    public void deleteFlowNodeInstanceElements_should_call_deletePendingMappings_when_flownode_is_type_MANUAL_TASK()\n            throws Exception {\n        // Given\n        final SFlowNodeInstance flowNodeInstance = new SManualTaskInstance(\"name\", 1L, 2L, 3L, 4L,\n                STaskPriority.ABOVE_NORMAL, 5L, 6L);\n        final SProcessDefinition processDefinition = mock(SProcessDefinition.class);\n        doReturn(mock(SFlowElementContainerDefinition.class)).when(processDefinition).getProcessContainer();\n        doNothing().when(processInstanceService).deleteDataInstancesIfNecessary(flowNodeInstance, processDefinition);\n        doNothing().when(processInstanceService).deleteConnectorInstancesIfNecessary(flowNodeInstance,\n                processDefinition);\n\n        // When\n        processInstanceService.deleteFlowNodeInstanceElements(flowNodeInstance, processDefinition);\n\n        // Then\n        verify(activityInstanceService).deletePendingMappings(flowNodeInstance.getId());\n    }\n\n    @Test\n    public void deleteFlowNodeInstanceElements_should_just_deleteDataInstancesIfNecessary_and_deleteConnectorInstancesIfNecessary_when_flownode_is_loop()\n            throws Exception {\n        // Given\n        final SFlowNodeInstance flowNodeInstance = new SLoopActivityInstance();\n        final SProcessDefinition processDefinition = mock(SProcessDefinition.class);\n        doReturn(mock(SFlowElementContainerDefinition.class)).when(processDefinition).getProcessContainer();\n        doNothing().when(processInstanceService).deleteDataInstancesIfNecessary(flowNodeInstance, processDefinition);\n        doNothing().when(processInstanceService).deleteConnectorInstancesIfNecessary(flowNodeInstance,\n                processDefinition);\n\n        // When\n        processInstanceService.deleteFlowNodeInstanceElements(flowNodeInstance, processDefinition);\n\n        // Then\n        verify(activityInstanceService, never()).deletePendingMappings(flowNodeInstance.getId());\n        verify(processInstanceService, never()).deleteSubProcess(flowNodeInstance, processDefinition);\n    }\n\n    @Test\n    public void deleteFlowNodeInstanceElements_should_always_delete_bpm_failures()\n            throws Exception {\n        // Given\n        final SFlowNodeInstance flowNodeInstance = new SGatewayInstance();\n        flowNodeInstance.setId(1L);\n        final SProcessDefinition processDefinition = mock(SProcessDefinition.class);\n        doReturn(mock(SFlowElementContainerDefinition.class)).when(processDefinition).getProcessContainer();\n\n        // When\n        processInstanceService.deleteFlowNodeInstanceElements(flowNodeInstance, processDefinition);\n\n        // Then\n        verify(processInstanceService, never()).deleteDataInstancesIfNecessary(flowNodeInstance, processDefinition);\n        verify(processInstanceService, never()).deleteConnectorInstancesIfNecessary(flowNodeInstance,\n                processDefinition);\n        verify(bpmFailureService).deleteFlowNodeFailures(1L);\n    }\n\n    @Test\n    public void getNumberOfProcessInstances_should_call_getNumberOfEntities() throws Exception {\n        final Map<String, Object> inputParameters = new HashMap<String, Object>();\n        inputParameters.put(\"processDefinitionId\", 45L);\n        final SelectOneDescriptor<Long> countDescriptor = new SelectOneDescriptor<Long>(\n                \"countProcessInstancesOfProcessDefinition\", inputParameters,\n                SProcessInstance.class);\n        when(readPersistenceService.selectOne(countDescriptor)).thenReturn(4L);\n\n        processInstanceService.getNumberOfProcessInstances(45L);\n\n        verify(readPersistenceService).selectOne(eq(countDescriptor));\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getNumberOfProcessInstances_should_throw_a_read_exception_if_getNumberOfEntities_does_it()\n            throws Exception {\n        when(readPersistenceService.selectOne(ArgumentMatchers.<SelectOneDescriptor<Long>> any())).thenThrow(\n                new SBonitaReadException(\"error\"));\n\n        processInstanceService.getNumberOfProcessInstances(45L);\n    }\n\n    @Test\n    public void _IN_REQUEST_SIZE_should_be_100_for_good_performance() {\n        // In any case, it should be <= 1000, as this is the limit of the most restrictive RDBMS we support: Oracle.\n        assertThat(IN_REQUEST_SIZE).isLessThanOrEqualTo(100);\n    }\n\n    private SProcessDefinitionBuilder aProcess() {\n        return new SProcessDefinitionBuilder();\n    }\n\n    private class SProcessDefinitionBuilder {\n\n        private SProcessDefinitionImpl processDefinition = new SProcessDefinitionImpl(\"aProcess\", \"1.0\");\n\n        SProcessDefinitionBuilder with(SEventDefinition eventDefinition) {\n            ((SFlowElementContainerDefinitionImpl) processDefinition.getProcessContainer()).addEvent(eventDefinition);\n            return this;\n        }\n\n        SProcessDefinition done() {\n            return processDefinition;\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/impl/RefBusinessDataServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.doThrow;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.core.process.instance.api.RefBusinessDataService;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceCreationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceModificationException;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceNotFoundException;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SProcessSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.model.business.data.SSimpleRefBusinessDataInstance;\nimport org.bonitasoft.engine.core.process.instance.recorder.SelectBusinessDataDescriptorBuilder;\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class RefBusinessDataServiceImplTest {\n\n    @Mock\n    private ReadPersistenceService persistence;\n\n    @Mock\n    private EventService eventService;\n\n    @Mock\n    private Recorder recorder;\n\n    @Mock\n    QueriableLoggerService loggerService;\n\n    @InjectMocks\n    private RefBusinessDataServiceImpl service;\n\n    private SSimpleRefBusinessDataInstance buildSRefBusinessDataInstance() {\n        final SSimpleRefBusinessDataInstance instance = new SProcessSimpleRefBusinessDataInstance();\n        instance.setName(\"myLeaveRequest\");\n        instance.setDataId(45l);\n        instance.setDataClassName(\"org.bonitasoft.LeaveRequest\");\n        return instance;\n    }\n\n    private SelectOneDescriptor<SRefBusinessDataInstance> buildGetDescriptor(final String name, final long dataId) {\n        return SelectBusinessDataDescriptorBuilder.getSRefBusinessDataInstance(name, dataId);\n    }\n\n    @Test\n    public void getRefBusinessDataInstanceReturnsTheRightObject() throws Exception {\n        final String name = \"myLeaveRequest\";\n        final long dataId = 45;\n        final SRefBusinessDataInstance expectedDataInstance = buildSRefBusinessDataInstance();\n        when(persistence.selectOne(buildGetDescriptor(name, dataId))).thenReturn(expectedDataInstance);\n\n        final SRefBusinessDataInstance actualDataInstance = service.getRefBusinessDataInstance(name, dataId);\n\n        assertThat(actualDataInstance).isEqualTo(expectedDataInstance);\n    }\n\n    @Test(expected = SRefBusinessDataInstanceNotFoundException.class)\n    public void getRefBusinessDataInstanceThrowsAnExceptionWhenObjectDoesNotExist() throws Exception {\n        final String name = \"myLeaveRequest\";\n        final long dataId = 45;\n        when(persistence.selectOne(buildGetDescriptor(name, dataId))).thenReturn(null);\n\n        service.getRefBusinessDataInstance(name, dataId);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getRefBusinessDataInstanceThrowsAnExceptionWhenPeristenceServiceIsDown() throws Exception {\n        final String name = \"myLeaveRequest\";\n        final long dataId = 45;\n        when(persistence.selectOne(buildGetDescriptor(name, dataId))).thenThrow(new SBonitaReadException(\"down\"));\n\n        service.getRefBusinessDataInstance(name, dataId);\n    }\n\n    @Test\n    public void updateRefBusinessData() throws Exception {\n        final SSimpleRefBusinessDataInstance refBusinessDataInstance = buildSRefBusinessDataInstance();\n        final long dataId = 564654654654654l;\n        final Map<String, Object> fields = new HashMap<String, Object>();\n        fields.put(\"dataId\", dataId);\n        final UpdateRecord updateRecord = UpdateRecord.buildSetFields(refBusinessDataInstance, fields);\n\n        service.updateRefBusinessDataInstance(refBusinessDataInstance, dataId);\n\n        verify(recorder).recordUpdate(updateRecord, RefBusinessDataService.REF_BUSINESS_DATA_INSTANCE);\n    }\n\n    @Test(expected = SRefBusinessDataInstanceModificationException.class)\n    public void updateRefBusinessDataThrowException() throws Exception {\n        final SSimpleRefBusinessDataInstance refBusinessDataInstance = buildSRefBusinessDataInstance();\n        final long dataId = 564654654654654l;\n        final Map<String, Object> fields = new HashMap<String, Object>();\n        fields.put(\"dataId\", dataId);\n        final UpdateRecord updateRecord = UpdateRecord.buildSetFields(refBusinessDataInstance, fields);\n        doThrow(new SRecorderException(\"ouch!\")).when(recorder).recordUpdate(updateRecord,\n                RefBusinessDataService.REF_BUSINESS_DATA_INSTANCE);\n\n        service.updateRefBusinessDataInstance(refBusinessDataInstance, dataId);\n    }\n\n    @Test\n    public void addRefBusinessData() throws Exception {\n        final SRefBusinessDataInstance refBusinessDataInstance = buildSRefBusinessDataInstance();\n\n        service.addRefBusinessDataInstance(refBusinessDataInstance);\n\n        verify(recorder).recordInsert(new InsertRecord(refBusinessDataInstance),\n                RefBusinessDataService.REF_BUSINESS_DATA_INSTANCE);\n    }\n\n    @Test(expected = SRefBusinessDataInstanceCreationException.class)\n    public void addRefBusinessDataThrowException() throws Exception {\n        final SRefBusinessDataInstance refBusinessDataInstance = buildSRefBusinessDataInstance();\n\n        doThrow(new SRecorderException(\"ouch!\")).when(recorder).recordInsert(new InsertRecord(refBusinessDataInstance),\n                RefBusinessDataService.REF_BUSINESS_DATA_INSTANCE);\n\n        service.addRefBusinessDataInstance(refBusinessDataInstance);\n    }\n\n    @Test\n    public void getRefBusinessDataInstancesShouldCallThePersistenceService() throws Exception {\n        final long processIntanceId = 789798798L;\n        final int startIndex = 0;\n        final int maxResults = 100;\n\n        service.getRefBusinessDataInstances(processIntanceId, startIndex, maxResults);\n\n        verify(persistence)\n                .selectList(SelectBusinessDataDescriptorBuilder.getSRefBusinessDataInstances(processIntanceId,\n                        startIndex, maxResults));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/model/archive/impl/SAHumanTaskInstanceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.archive.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.core.process.instance.model.STaskPriority;\nimport org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance;\nimport org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance;\nimport org.junit.Test;\n\n/**\n * author Emmanuel Duchastenier\n */\npublic class SAHumanTaskInstanceImplTest {\n\n    @Test\n    public void creatingArchiveShouldCopyAllRelevantAttibute() {\n        final SUserTaskInstance taskInstance = new SUserTaskInstance(\"taskName\", 147L, 2565412L, 874555L, 7L,\n                STaskPriority.UNDER_NORMAL, 7777L, 8888L);\n        final long claimedDate = System.currentTimeMillis();\n        taskInstance.setClaimedDate(claimedDate);\n        taskInstance.setStateId(3);\n        taskInstance.setStateName(\"estado\");\n        taskInstance.setAssigneeId(8412L);\n        taskInstance.setDescription(\"tarea de usuario\");\n        taskInstance.setDisplayDescription(\"tarea del usuario Pepo\");\n        taskInstance.setDisplayName(\"task for everyone\");\n        taskInstance.setExecutedBy(987987L);\n        taskInstance.setExecutedBySubstitute(11111L);\n        final long expectedEndDate = System.currentTimeMillis() + 10;\n        taskInstance.setExpectedEndDate(expectedEndDate);\n        final SAUserTaskInstance archivedTaskInstance = new SAUserTaskInstance(taskInstance);\n\n        assertThat(archivedTaskInstance.getType()).isEqualTo(taskInstance.getType());\n        assertThat(archivedTaskInstance.getActorId()).isEqualTo(7L);\n        assertThat(archivedTaskInstance.getAssigneeId()).isEqualTo(8412L);\n        assertThat(archivedTaskInstance.getExpectedEndDate()).isEqualTo(expectedEndDate);\n        assertThat(archivedTaskInstance.getPriority()).isEqualTo(STaskPriority.UNDER_NORMAL);\n        assertThat(archivedTaskInstance.getClaimedDate()).isEqualTo(claimedDate);\n        assertThat(archivedTaskInstance.getDescription()).isEqualTo(\"tarea de usuario\");\n        assertThat(archivedTaskInstance.getDisplayDescription()).isEqualTo(\"tarea del usuario Pepo\");\n        assertThat(archivedTaskInstance.getDisplayName()).isEqualTo(\"task for everyone\");\n        assertThat(archivedTaskInstance.getExecutedBy()).isEqualTo(987987L);\n        assertThat(archivedTaskInstance.getExecutedBySubstitute()).isEqualTo(11111L);\n        assertThat(archivedTaskInstance.getName()).isEqualTo(\"taskName\");\n        assertThat(archivedTaskInstance.getSourceObjectId()).isEqualTo(taskInstance.getId());\n        assertThat(archivedTaskInstance.getStateName()).isEqualTo(\"estado\");\n        assertThat(archivedTaskInstance.getStateId()).isEqualTo(3);\n        assertThat(archivedTaskInstance.getParentActivityInstanceId())\n                .isEqualTo(taskInstance.getParentActivityInstanceId());\n        assertThat(archivedTaskInstance.getParentContainerId()).isEqualTo(874555L);\n        assertThat(archivedTaskInstance.getParentProcessInstanceId())\n                .isEqualTo(taskInstance.getParentProcessInstanceId());\n        assertThat(archivedTaskInstance.getRootContainerId()).isEqualTo(2565412L);\n        assertThat(archivedTaskInstance.getFlowNodeDefinitionId()).isEqualTo(147L);\n        assertThat(archivedTaskInstance.getLogicalGroup(0)).isEqualTo(7777L);\n        assertThat(archivedTaskInstance.getLogicalGroup(1)).isEqualTo(8888L);\n    }\n\n    @Test\n    public void should_allow_null_expected_date() {\n        //given\n        final SUserTaskInstance taskInstance = new SUserTaskInstance(\"taskName\", 147L, 2565412L, 874555L, 7L,\n                STaskPriority.UNDER_NORMAL, 7777L, 8888L);\n\n        //when\n        taskInstance.setExpectedEndDate(null);\n        final SAUserTaskInstance archivedTaskInstance = new SAUserTaskInstance(taskInstance);\n\n        //then\n        assertThat(archivedTaskInstance.getExpectedEndDate()).isNull();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SFlowNodeInstanceBuilderImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.builder.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\nimport org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;\nimport org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance;\nimport org.junit.Test;\n\npublic class SFlowNodeInstanceBuilderImplTest {\n\n    @Test\n    public void setState_should_set_all_fields_related_to_state() throws Exception {\n        //given\n        SAutomaticTaskInstanceBuilderImpl builder = new SAutomaticTaskInstanceBuilderImpl(new SAutomaticTaskInstance());\n        int stateId = 100;\n        String stateName = \"mockState\";\n        FlowNodeState state = mock(FlowNodeState.class);\n        given(state.getId()).willReturn(stateId);\n        given(state.getName()).willReturn(stateName);\n        given(state.isTerminal()).willReturn(true);\n        given(state.isStable()).willReturn(true);\n\n        //when\n        builder.setState(state);\n\n        //then\n        SAutomaticTaskInstance taskInstance = builder.done();\n        assertThat(taskInstance.getStateId()).isEqualTo(stateId);\n        assertThat(taskInstance.getStateName()).isEqualTo(stateName);\n        assertThat(taskInstance.isStable()).isTrue();\n        assertThat(taskInstance.isTerminal()).isTrue();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/model/event/handling/SMessageInstanceTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.event.handling;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.Test;\n\npublic class SMessageInstanceTest {\n\n    @Test\n    public void should_have_a_creation_date() {\n        SMessageInstance sMessageInstance = new SMessageInstance(\"myMessage\", \"target\", \"target\", 1234L, \"fn\");\n\n        assertThat(sMessageInstance.getCreationDate()).isGreaterThan(0);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/model/event/impl/SBoundaryEventInstanceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.event.impl;\n\nimport static org.junit.Assert.assertFalse;\n\nimport org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class SBoundaryEventInstanceImplTest {\n\n    private SBoundaryEventInstance boundary;\n\n    @Before\n    public void setUp() {\n        boundary = new SBoundaryEventInstance();\n    }\n\n    @Test\n    public void mustExecutOnAbortOrCancelProcess_return_false_if_stable() {\n        boundary.setStable(true);\n        assertFalse(boundary.mustExecuteOnAbortOrCancelProcess());\n    }\n\n    // for a boundary we never must execute the flow node on abort the process instance because it will be aborted by the related activity\n    @Test\n    public void mustExecutOnAbortOrCancelProcess_return_false_if_not_stable() {\n        boundary.setStable(false);\n        assertFalse(boundary.mustExecuteOnAbortOrCancelProcess());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/model/impl/SGatewayInstanceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.core.process.instance.model.SGatewayInstance;\nimport org.junit.Test;\n\npublic class SGatewayInstanceImplTest {\n\n    @Test\n    public void isFinished_should_return_true_when_hitbys_starts_with_FINISH() throws Exception {\n        //given\n        SGatewayInstance gatewayInstance = buildGateWithHitBys(\"FINISH:1\");\n\n        //when\n        gatewayInstance.isFinished();\n\n        //then\n        assertThat(gatewayInstance.isFinished()).isTrue();\n    }\n\n    @Test\n    public void isFinished_should_return_false_when_hitbys_doesnt_start_with_FINISH() throws Exception {\n        //given\n        SGatewayInstance gatewayInstance = buildGateWithHitBys(\"1,2\");\n\n        //when\n        gatewayInstance.isFinished();\n\n        //then\n        assertThat(gatewayInstance.isFinished()).isFalse();\n    }\n\n    @Test\n    public void isFinished_should_return_false_when_hitbys_is_null() throws Exception {\n        //given\n        SGatewayInstance gatewayInstance = buildGateWithHitBys(null);\n\n        //when\n        gatewayInstance.isFinished();\n\n        //then\n        assertThat(gatewayInstance.isFinished()).isFalse();\n    }\n\n    private SGatewayInstance buildGateWithHitBys(final String hitBys) {\n        SGatewayInstance gatewayInstance = new SGatewayInstance();\n        gatewayInstance.setHitBys(hitBys);\n        return gatewayInstance;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/model/impl/SLoopActivityInstanceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class SLoopActivityInstanceImplTest {\n\n    private SLoopActivityInstance loop;\n\n    @Before\n    public void setUp() {\n        loop = new SLoopActivityInstance();\n    }\n\n    @Test\n    public void mustExecuteOnAbortOrCancelProcess_return_false_when_is_stable() {\n        // given\n        loop.setStable(true);\n\n        // when\n        boolean executeOnAbortOrCancelProcess = loop.mustExecuteOnAbortOrCancelProcess();\n\n        // then\n        assertThat(executeOnAbortOrCancelProcess).isFalse();\n    }\n\n    @Test\n    public void mustExecuteOnAbortOrCancelProcess_return_false_when_is_not_stable() {\n        // given\n        loop.setStable(false);\n\n        // when\n        boolean executeOnAbortOrCancelProcess = loop.mustExecuteOnAbortOrCancelProcess();\n\n        // then\n        assertThat(executeOnAbortOrCancelProcess).isFalse();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/model/impl/SMultiInstanceActivityInstanceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class SMultiInstanceActivityInstanceImplTest {\n\n    private SMultiInstanceActivityInstance multi;\n\n    @Before\n    public void setUp() {\n        multi = new SMultiInstanceActivityInstance();\n    }\n\n    @Test\n    public void mustExecuteOnAbortOrCancelProcess_should_return_false_when_is_stable() {\n        //given\n        multi.setStable(true);\n\n        //when\n        final boolean executeOnAbortOrCancelProcess = multi.mustExecuteOnAbortOrCancelProcess();\n\n        //then\n        assertThat(executeOnAbortOrCancelProcess).isFalse();\n    }\n\n    @Test\n    public void mustExecuteOnAbortOrCancelProcess_should_return_false_when_is_not_stable() {\n        //given\n        multi.setStable(false);\n\n        //when\n        final boolean executeOnAbortOrCancelProcess = multi.mustExecuteOnAbortOrCancelProcess();\n\n        //then\n        assertThat(executeOnAbortOrCancelProcess).isFalse();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/model/impl/SProcessInstanceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.junit.Test;\n\npublic class SProcessInstanceImplTest {\n\n    @Test\n    public void defaultInterruptingEventIdShouldBeMinusOne() {\n        assertThat(SProcessInstance.builder().build().getInterruptingEventId()).isEqualTo(-1L);\n    }\n\n    @Test\n    public void should_be_root_instance_when_callerId_is_less_than_zero() throws Exception {\n        //given\n        SProcessInstance instance = new SProcessInstance();\n        instance.setCallerId(-1);\n\n        //then\n        assertThat(instance.isRootInstance()).isTrue();\n    }\n\n    @Test\n    public void should_not_be_root_instance_when_callerId_is_greater_than_zero() throws Exception {\n        //given\n        SProcessInstance instance = new SProcessInstance();\n        instance.setCallerId(1);\n\n        //then\n        assertThat(instance.isRootInstance()).isFalse();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/model/impl/SUserTaskInstanceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.process.instance.model.impl;\n\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertTrue;\n\nimport org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class SUserTaskInstanceImplTest {\n\n    private SUserTaskInstance task;\n\n    @Before\n    public void setUp() {\n        task = new SUserTaskInstance();\n    }\n\n    @Test\n    public void mustExecuteOnAbortOrCancelProcess_returns_true_if_stable_state() {\n        task.setStable(true);\n\n        assertTrue(task.mustExecuteOnAbortOrCancelProcess());\n    }\n\n    @Test\n    public void mustExecuteOnAbortOrCancelProcess_returns_false_if_stable_state() {\n        task.setStable(false);\n\n        assertFalse(task.mustExecuteOnAbortOrCancelProcess());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-process-instance/src/test/resources/logback-test.xml",
    "content": "<configuration>\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder>\n            <pattern>[%-5level] %msg%n</pattern>\n        </encoder>\n    </appender>\n    <root level=\"DEBUG\">\n        <appender-ref ref=\"STDOUT\" />\n    </root>\n</configuration>"
  },
  {
    "path": "bpm/bonita-core/bonita-supervisor-mapping/build.gradle",
    "content": "\n\ndependencies {\n    api project(':services:bonita-commons')\n    api project(':services:bonita-builder')\n    api project(':services:bonita-events')\n    api project(':services:bonita-log')\n    api project(':services:bonita-persistence')\n\n    annotationProcessor libs.lombok\n    compileOnly libs.lombok\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-supervisor-mapping/src/main/java/org/bonitasoft/engine/supervisor/mapping/SProcessDefinitionNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.supervisor.mapping;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Yanyan Liu\n */\npublic class SProcessDefinitionNotFoundException extends SBonitaException {\n\n    private static final long serialVersionUID = -7591704670782032169L;\n\n    public SProcessDefinitionNotFoundException(final String message) {\n        super(message);\n    }\n\n    public SProcessDefinitionNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SProcessDefinitionNotFoundException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-supervisor-mapping/src/main/java/org/bonitasoft/engine/supervisor/mapping/SSupervisorAlreadyExistsException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.supervisor.mapping;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Yanyan Liu\n */\npublic class SSupervisorAlreadyExistsException extends SBonitaException {\n\n    private static final long serialVersionUID = 5396310718485439404L;\n\n    public SSupervisorAlreadyExistsException(final String message) {\n        super(message);\n    }\n\n    public SSupervisorAlreadyExistsException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SSupervisorAlreadyExistsException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-supervisor-mapping/src/main/java/org/bonitasoft/engine/supervisor/mapping/SSupervisorCreationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.supervisor.mapping;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Yanyan Liu\n */\npublic class SSupervisorCreationException extends SBonitaException {\n\n    private static final long serialVersionUID = 2848214220076867957L;\n\n    public SSupervisorCreationException(final String message) {\n        super(message);\n    }\n\n    public SSupervisorCreationException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SSupervisorCreationException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-supervisor-mapping/src/main/java/org/bonitasoft/engine/supervisor/mapping/SSupervisorDeletionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.supervisor.mapping;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Yanyan Liu\n */\npublic class SSupervisorDeletionException extends SBonitaException {\n\n    private static final long serialVersionUID = 3748312313790371984L;\n\n    public SSupervisorDeletionException(final String message) {\n        super(message);\n    }\n\n    public SSupervisorDeletionException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SSupervisorDeletionException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-supervisor-mapping/src/main/java/org/bonitasoft/engine/supervisor/mapping/SSupervisorException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.supervisor.mapping;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Yanyan Liu\n */\npublic class SSupervisorException extends SBonitaException {\n\n    private static final long serialVersionUID = 5245682138584055122L;\n\n    public SSupervisorException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SSupervisorException(final String message) {\n        super(message);\n    }\n\n    public SSupervisorException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-supervisor-mapping/src/main/java/org/bonitasoft/engine/supervisor/mapping/SSupervisorNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.supervisor.mapping;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Yanyan Liu\n * @author Celine Souchet\n */\npublic class SSupervisorNotFoundException extends SBonitaException {\n\n    private static final long serialVersionUID = 2848214220076867957L;\n\n    public SSupervisorNotFoundException(final String message) {\n        super(message);\n    }\n\n    public SSupervisorNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SSupervisorNotFoundException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SSupervisorNotFoundException(final Long userId, final Long roleId, final Long groupId,\n            final Long processDefinitionId) {\n        super(\"No supervisor was found !!\");\n        setUserIdOnContext(userId);\n        setRoleIdOnContext(roleId);\n        setGroupIdOnContext(groupId);\n        setProcessDefinitionIdOnContext(processDefinitionId);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-supervisor-mapping/src/main/java/org/bonitasoft/engine/supervisor/mapping/SupervisorMappingService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.supervisor.mapping;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor;\n\n/**\n * @author Yanyan Liu\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @since 6.0\n */\npublic interface SupervisorMappingService {\n\n    String SUPERVISOR = \"SUPERVISOR\";\n\n    /**\n     * Create supervisor in DB according to the given supervisor\n     *\n     * @param supervisor\n     *        a SSupervisor object\n     * @return the new created supervisor\n     * @throws SSupervisorCreationException\n     */\n    SProcessSupervisor createProcessSupervisor(SProcessSupervisor supervisor) throws SSupervisorCreationException;\n\n    /**\n     * get supervisor without display name by its id\n     *\n     * @param supervisorId\n     *        identifier of supervisor\n     * @return the supervisor with id equals the parameter\n     * @throws SSupervisorNotFoundException\n     */\n    SProcessSupervisor getProcessSupervisor(long supervisorId) throws SSupervisorNotFoundException;\n\n    /**\n     * Delete the id specified supervisor\n     *\n     * @param supervisorId\n     *        identifier of supervisor\n     * @throws SSupervisorNotFoundException\n     * @throws SSupervisorDeletionException\n     */\n    void deleteProcessSupervisor(long supervisorId) throws SSupervisorNotFoundException, SSupervisorDeletionException;\n\n    /**\n     * Delete the specific supervisor\n     *\n     * @param supervisor\n     *        the supervisor will be deleted\n     * @throws SSupervisorDeletionException\n     */\n    void deleteProcessSupervisor(SProcessSupervisor supervisor) throws SSupervisorDeletionException;\n\n    /**\n     * Delete all supervisors for the connected tenant\n     *\n     * @throws SSupervisorDeletionException\n     * @since 6.1\n     */\n    void deleteAllProcessSupervisors() throws SSupervisorDeletionException;\n\n    /**\n     * Verify if the id specified user is the supervisor of id specified process definition\n     *\n     * @param processDefinitionId\n     *        identifier of process definition\n     * @param userId\n     *        identifier of user\n     * @return true if user is supervisor of the process, false otherwise\n     * @throws SBonitaReadException\n     */\n    Boolean isProcessSupervisor(long processDefinitionId, long userId) throws SBonitaReadException;\n\n    /**\n     * Search all supervisors suit to the specific criteria\n     *\n     * @param queryOptions\n     *        The QueryOptions object containing some query conditions\n     * @return a list of SSupervisor objects\n     * @throws SBonitaReadException\n     */\n    List<SProcessSupervisor> searchProcessSupervisors(QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * Get total number of supervisors suit to the specific criteria\n     *\n     * @param searchOptions\n     *        The QueryOptions object containing some query conditions\n     * @return a list of SSupervisor objects\n     * @throws SBonitaReadException\n     */\n    long getNumberOfProcessSupervisors(QueryOptions searchOptions) throws SBonitaReadException;\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-supervisor-mapping/src/main/java/org/bonitasoft/engine/supervisor/mapping/impl/SelectDescriptorBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.supervisor.mapping.impl;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor;\n\n/**\n * @author Yanyan Liu\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class SelectDescriptorBuilder {\n\n    public static SelectByIdDescriptor<SProcessSupervisor> getSupervisor(final long supervisorId) {\n        return new SelectByIdDescriptor<SProcessSupervisor>(SProcessSupervisor.class, supervisorId);\n    }\n\n    public static SelectOneDescriptor<Long> getNumberOfSupervisors(final long processDefId) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"processDefId\", (Object) processDefId);\n        return new SelectOneDescriptor<Long>(\"getNumberOfSupervisorsOfProcessDef\", parameters,\n                SProcessSupervisor.class);\n    }\n\n    public static SelectOneDescriptor<SProcessSupervisor> getSupervisor(final long processDefId, final long userId) {\n        final Map<String, Object> parameters = new HashMap<String, Object>();\n        parameters.put(\"processDefId\", processDefId);\n        parameters.put(\"userId\", userId);\n        return new SelectOneDescriptor<SProcessSupervisor>(\"getSupervisor\", parameters, SProcessSupervisor.class);\n    }\n\n    public static SelectListDescriptor<Long> getProcessDefIdsOfUser(final long userId, final int fromIndex,\n            final int maxResult, final OrderByType orderByType) {\n        final Map<String, Object> parameters = new HashMap<String, Object>();\n        parameters.put(\"userId\", userId);\n\n        final OrderByOption orderByOption = new OrderByOption(SProcessSupervisor.class,\n                SProcessSupervisor.PROCESS_DEF_ID_KEY,\n                orderByType);\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, maxResult,\n                Collections.singletonList(orderByOption));\n\n        return new SelectListDescriptor<Long>(\"getProcessDefIdsOfUser\", parameters, SProcessSupervisor.class,\n                queryOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-supervisor-mapping/src/main/java/org/bonitasoft/engine/supervisor/mapping/impl/SupervisorMappingServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.supervisor.mapping.impl;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity;\nimport org.bonitasoft.engine.queriablelogger.model.builder.ActionType;\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteAllRecord;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\nimport org.bonitasoft.engine.supervisor.mapping.SSupervisorCreationException;\nimport org.bonitasoft.engine.supervisor.mapping.SSupervisorDeletionException;\nimport org.bonitasoft.engine.supervisor.mapping.SSupervisorNotFoundException;\nimport org.bonitasoft.engine.supervisor.mapping.SupervisorMappingService;\nimport org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor;\nimport org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisorLogBuilder;\nimport org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisorLogBuilderFactory;\n\n/**\n * @author Yanyan Liu\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class SupervisorMappingServiceImpl implements SupervisorMappingService {\n\n    private final ReadPersistenceService persistenceService;\n\n    private final Recorder recorder;\n\n    private final EventService eventService;\n\n    private final QueriableLoggerService queriableLoggerService;\n\n    public SupervisorMappingServiceImpl(final ReadPersistenceService persistenceService, final Recorder recorder,\n            final EventService eventService,\n            final QueriableLoggerService queriableLoggerService) {\n        this.persistenceService = persistenceService;\n        this.recorder = recorder;\n        this.eventService = eventService;\n        this.queriableLoggerService = queriableLoggerService;\n    }\n\n    @Override\n    public SProcessSupervisor createProcessSupervisor(final SProcessSupervisor supervisor)\n            throws SSupervisorCreationException {\n        final SProcessSupervisorLogBuilder logBuilder = getQueriableLog(ActionType.CREATED, \"Adding a new supervisor\");\n        try {\n            recorder.recordInsert(new InsertRecord(supervisor), SUPERVISOR);\n            log(supervisor.getId(), SQueriableLog.STATUS_OK, logBuilder, \"createSupervisor\");\n            return supervisor;\n        } catch (final SRecorderException re) {\n            log(supervisor.getId(), SQueriableLog.STATUS_FAIL, logBuilder, \"createSupervisor\");\n            throw new SSupervisorCreationException(re);\n        }\n    }\n\n    @Override\n    public SProcessSupervisor getProcessSupervisor(final long supervisorId) throws SSupervisorNotFoundException {\n        final SelectByIdDescriptor<SProcessSupervisor> selectByIdDescriptor = SelectDescriptorBuilder\n                .getSupervisor(supervisorId);\n        try {\n            final SProcessSupervisor supervisor = persistenceService.selectById(selectByIdDescriptor);\n            if (supervisor == null) {\n                throw new SSupervisorNotFoundException(supervisorId + \" does not refer to any supervisor\");\n            }\n            return supervisor;\n        } catch (final SBonitaReadException bre) {\n            throw new SSupervisorNotFoundException(bre);\n        }\n    }\n\n    @Override\n    public void deleteProcessSupervisor(final long supervisorId)\n            throws SSupervisorNotFoundException, SSupervisorDeletionException {\n        final SProcessSupervisor sSupervisor = getProcessSupervisor(supervisorId);\n        deleteProcessSupervisor(sSupervisor);\n    }\n\n    @Override\n    public void deleteProcessSupervisor(final SProcessSupervisor supervisor) throws SSupervisorDeletionException {\n        final SProcessSupervisorLogBuilder logBuilder = getQueriableLog(ActionType.DELETED, \"deleting supervisor\");\n        try {\n            recorder.recordDelete(new DeleteRecord(supervisor), SUPERVISOR);\n            log(supervisor.getId(), SQueriableLog.STATUS_OK, logBuilder, \"createSupervisor\");\n        } catch (final SRecorderException e) {\n            log(supervisor.getId(), SQueriableLog.STATUS_FAIL, logBuilder, \"createSupervisor\");\n            throw new SSupervisorDeletionException(\"Can't delete process supervisor \" + supervisor, e);\n        }\n    }\n\n    @Override\n    public void deleteAllProcessSupervisors() throws SSupervisorDeletionException {\n        try {\n            final DeleteAllRecord record = new DeleteAllRecord(SProcessSupervisor.class, null);\n            recorder.recordDeleteAll(record);\n        } catch (final SRecorderException e) {\n            throw new SSupervisorDeletionException(\"Can't delete all process supervisors.\", e);\n        }\n    }\n\n    private SProcessSupervisorLogBuilder getQueriableLog(final ActionType actionType, final String message) {\n        final SProcessSupervisorLogBuilder logBuilder = BuilderFactory.get(SProcessSupervisorLogBuilderFactory.class)\n                .createNewInstance();\n        this.initializeLogBuilder(logBuilder, message);\n        this.updateLog(actionType, logBuilder);\n        return logBuilder;\n    }\n\n    private <T extends SLogBuilder> void initializeLogBuilder(final T logBuilder, final String message) {\n        logBuilder.actionStatus(SQueriableLog.STATUS_FAIL).severity(SQueriableLogSeverity.INTERNAL).rawMessage(message);\n    }\n\n    private <T extends HasCRUDEAction> void updateLog(final ActionType actionType, final T logBuilder) {\n        logBuilder.setActionType(actionType);\n    }\n\n    @Override\n    public Boolean isProcessSupervisor(final long processDefinitionId, final long userId) throws SBonitaReadException {\n        final SelectOneDescriptor<SProcessSupervisor> descriptor = SelectDescriptorBuilder\n                .getSupervisor(processDefinitionId, userId);\n        final SProcessSupervisor supervisor = persistenceService.selectOne(descriptor);\n        return supervisor != null;\n    }\n\n    @Override\n    public List<SProcessSupervisor> searchProcessSupervisors(final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        return persistenceService.searchEntity(SProcessSupervisor.class, null, queryOptions, null);\n    }\n\n    @Override\n    public long getNumberOfProcessSupervisors(final QueryOptions searchOptions) throws SBonitaReadException {\n        return persistenceService.getNumberOfEntities(SProcessSupervisor.class, null, searchOptions, null);\n    }\n\n    private void log(final long objectId, final int sQueriableLogStatus, final SPersistenceLogBuilder logBuilder,\n            final String callerMethodName) {\n        logBuilder.actionScope(String.valueOf(objectId));\n        logBuilder.actionStatus(sQueriableLogStatus);\n        logBuilder.objectId(objectId);\n        final SQueriableLog log = logBuilder.build();\n        if (queriableLoggerService.isLoggable(log.getActionType(), log.getSeverity())) {\n            queriableLoggerService.log(this.getClass().getName(), callerMethodName, log);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-supervisor-mapping/src/main/java/org/bonitasoft/engine/supervisor/mapping/model/SMemberType.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.supervisor.mapping.model;\n\n/**\n * @author Yanyan Liu\n */\npublic enum SMemberType {\n    USER, GROUP, ROLE, MEMBERSHIP;\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-supervisor-mapping/src/main/java/org/bonitasoft/engine/supervisor/mapping/model/SProcessSupervisor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.supervisor.mapping.model;\n\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@Builder\n@Entity\n@Table(name = \"processsupervisor\")\npublic class SProcessSupervisor implements PersistentObject {\n\n    public static final String ID_KEY = \"id\";\n    public static final String USER_ID_KEY = \"userId\";\n    public static final String GROUP_ID_KEY = \"groupId\";\n    public static final String ROLE_ID_KEY = \"roleId\";\n    public static final String PROCESS_DEF_ID_KEY = \"processDefId\";\n    @Id\n    private long id;\n    @Column\n    private long processDefId;\n    @Builder.Default\n    @Column\n    private long userId = -1;\n    @Builder.Default\n    @Column\n    private long groupId = -1;\n    @Builder.Default\n    @Column\n    private long roleId = -1;\n\n    public SProcessSupervisor(final long processDefId) {\n        this.processDefId = processDefId;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-supervisor-mapping/src/main/java/org/bonitasoft/engine/supervisor/mapping/model/SProcessSupervisorLogBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.supervisor.mapping.model;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\n\n/**\n * @author Yanyan Liu\n */\npublic interface SProcessSupervisorLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction {\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-supervisor-mapping/src/main/java/org/bonitasoft/engine/supervisor/mapping/model/SProcessSupervisorLogBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.supervisor.mapping.model;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory;\n\n/**\n * @author Yanyan Liu\n */\npublic interface SProcessSupervisorLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory {\n\n    SProcessSupervisorLogBuilder createNewInstance();\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-supervisor-mapping/src/main/java/org/bonitasoft/engine/supervisor/mapping/model/impl/SProcessSupervisorLogBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.supervisor.mapping.model.impl;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory;\nimport org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisorLogBuilder;\nimport org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisorLogBuilderFactory;\n\n/**\n * @author Yanyan Liu\n */\npublic class SProcessSupervisorLogBuilderFactoryImpl extends CRUDELogBuilderFactory\n        implements SProcessSupervisorLogBuilderFactory {\n\n    @Override\n    public SProcessSupervisorLogBuilder createNewInstance() {\n        return new SProcessSupervisorLogBuilderImpl();\n    }\n\n    @Override\n    public String getObjectIdKey() {\n        return SProcessSupervisorLogIndexesMapper.SUPERVISOR_INDEX_NAME;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-supervisor-mapping/src/main/java/org/bonitasoft/engine/supervisor/mapping/model/impl/SProcessSupervisorLogBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.supervisor.mapping.model.impl;\n\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException;\nimport org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisorLogBuilder;\n\n/**\n * @author Yanyan Liu\n */\npublic class SProcessSupervisorLogBuilderImpl extends CRUDELogBuilder implements SProcessSupervisorLogBuilder {\n\n    private static final String PREFIX = \"SUPERVISOR\";\n\n    @Override\n    public SPersistenceLogBuilder objectId(final long objectId) {\n        this.queriableLogBuilder.numericIndex(SProcessSupervisorLogIndexesMapper.SUPERVISOR_INDEX, objectId);\n        return this;\n    }\n\n    @Override\n    protected String getActionTypePrefix() {\n        return PREFIX;\n    }\n\n    @Override\n    protected void checkExtraRules(final SQueriableLog log) {\n        if (log.getActionStatus() != SQueriableLog.STATUS_FAIL) {\n            if (log.getNumericIndex(SProcessSupervisorLogIndexesMapper.SUPERVISOR_INDEX) == 0L) {\n                throw new MissingMandatoryFieldsException(\"Some mandatory fields are missing: \" + \"supervisor Id\");\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-supervisor-mapping/src/main/java/org/bonitasoft/engine/supervisor/mapping/model/impl/SProcessSupervisorLogIndexesMapper.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.supervisor.mapping.model.impl;\n\n/**\n * @author Yanyan Liu\n */\npublic class SProcessSupervisorLogIndexesMapper {\n\n    public static final int SUPERVISOR_INDEX = 0;\n\n    public static final String SUPERVISOR_INDEX_NAME = \"numericIndex1\";\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-supervisor-mapping/src/main/resources/org/bonitasoft/engine/supervisor/mapping/model/impl/hibernate/supervisor.queries.hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\" \"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd\">\n<hibernate-mapping auto-import=\"false\">\n\n\t<query name=\"getNumberOfSProcessSupervisor\">\n\t\tSELECT COUNT(DISTINCT processsupervisor.id)\n\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t</query>\n\t\n\t<query name=\"searchSProcessSupervisor\">\n\t\tSELECT DISTINCT processsupervisor\n\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t</query>\n\n\t<query name=\"getNumberOfSProcessSupervisorwithSUser\">\n\t\tSELECT COUNT(DISTINCT processsupervisor.id)\n\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor,\n\t\t\t org.bonitasoft.engine.identity.model.SUser as user\n\t\tWHERE processsupervisor.userId = user.id\n\t</query>\n\t\n\t<query name=\"searchSProcessSupervisorwithSUser\">\n\t\tSELECT DISTINCT processsupervisor\n\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor,\n\t\t\t org.bonitasoft.engine.identity.model.SUser as user\n\t\tWHERE processsupervisor.userId = user.id\n\t</query>\n\t\n\t<query name=\"getNumberOfSProcessSupervisorwithSGroup\">\n\t\tSELECT COUNT(DISTINCT processsupervisor.id)\n\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor,\n\t\t\t org.bonitasoft.engine.identity.model.SGroup AS group_\n\t\tWHERE processsupervisor.groupId = group_.id\n\t</query>\n\t\n\t<query name=\"searchSProcessSupervisorwithSGroup\">\n\t\tSELECT DISTINCT processsupervisor.id\n\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor,\n\t\t\t org.bonitasoft.engine.identity.model.SGroup AS group_\n\t\tWHERE processsupervisor.groupId = group_.id\n\t</query>\n\t\n\t\n\t<query name=\"getNumberOfSProcessSupervisorwithSRole\">\n\t\tSELECT COUNT(DISTINCT processsupervisor.id)\n\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor,\n\t\t\t org.bonitasoft.engine.identity.model.SRole AS role\n\t\tWHERE processsupervisor.roleId = role.id\n\t</query>\n\t\n\t<query name=\"searchSProcessSupervisorwithSRole\">\n\t\tSELECT DISTINCT processsupervisor.id\n\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor,\n\t\t\t org.bonitasoft.engine.identity.model.SRole AS role\n\t\tWHERE processsupervisor.roleId = role.id\n\t</query>\n\t\t\n\t<query name=\"getNumberOfSProcessSupervisorwithSGroupSRole\">\n\t\tSELECT COUNT(DISTINCT processsupervisor.id)\n\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor,\n\t\t\torg.bonitasoft.engine.identity.model.SGroup AS group_,\n\t\t\torg.bonitasoft.engine.identity.model.SRole AS role\n\t\tWHERE processsupervisor.roleId = role.id\n\t\tAND processsupervisor.groupId = group_.id\n\t</query>\n\t\n\t<query name=\"searchSProcessSupervisorwithSGroupSRole\">\n\t\tSELECT DISTINCT processsupervisor.id\n\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor,\n\t\t\torg.bonitasoft.engine.identity.model.SGroup AS group_,\n\t\t\torg.bonitasoft.engine.identity.model.SRole AS role\n\t\tWHERE processsupervisor.roleId = role.id\n\t\tAND processsupervisor.groupId = group_.id\n\t</query>\n\t\n\t<query name=\"getNumberOfSProcessSupervisorwithSUserSRole\">\n\t\tSELECT COUNT(DISTINCT processsupervisor.id)\n\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor,\n\t\t\torg.bonitasoft.engine.identity.model.SUser as user,\n\t\t\torg.bonitasoft.engine.identity.model.SRole AS role\n\t\tWHERE (processsupervisor.userId &lt;= 0 AND  processsupervisor.roleId = role.id)\n\t\tOR (processsupervisor.userId = user.id AND processsupervisor.roleId &lt;= 0)\n\t</query>\n\t\n\t<query name=\"searchSProcessSupervisorwithSUserSRole\">\n\t\tSELECT DISTINCT processsupervisor.id\n\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor,\n\t\t\torg.bonitasoft.engine.identity.model.SUser as user,\n\t\t\torg.bonitasoft.engine.identity.model.SRole AS role\n\t\tWHERE (processsupervisor.userId &lt;= 0 AND  processsupervisor.roleId = role.id)\n\t\tOR (processsupervisor.userId = user.id AND processsupervisor.roleId &lt;= 0)\n\t</query>\n\t\n\t<query name=\"getNumberOfSProcessSupervisorwithSUserSGroup\">\n\t\tSELECT COUNT(DISTINCT processsupervisor.id)\n\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor,\n\t\t\torg.bonitasoft.engine.identity.model.SUser as user,\n\t\t\torg.bonitasoft.engine.identity.model.SGroup AS group_\n\t\tWHERE (processsupervisor.userId = user.id AND processsupervisor.groupId &lt;= 0)\n\t\tOR (processsupervisor.userId &lt;= 0 AND processsupervisor.groupId = group_.id)\n\t</query>\n\t\n\t<query name=\"searchSProcessSupervisorwithSUserSGroup\">\n\t\tSELECT DISTINCT processsupervisor.id\n\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor,\n\t\t\torg.bonitasoft.engine.identity.model.SUser as user,\n\t\t\torg.bonitasoft.engine.identity.model.SGroup AS group_\n\t\tWHERE (processsupervisor.userId = user.id AND processsupervisor.groupId &lt;= 0)\n\t\tOR (processsupervisor.userId &lt;= 0 AND processsupervisor.groupId = group_.id)\n\t</query>\n\t\n\t<query name=\"getNumberOfSProcessSupervisorwithSUserSGroupSRole\">\n\t\tSELECT COUNT(DISTINCT processsupervisor.id)\n\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor,\n\t\t\torg.bonitasoft.engine.identity.model.SUser as user,\n\t\t\torg.bonitasoft.engine.identity.model.SGroup AS group_,\n\t\t\torg.bonitasoft.engine.identity.model.SRole AS role\n\t\tWHERE ((user.id = processsupervisor.userId AND processsupervisor.roleId &lt;= 0 AND processsupervisor.groupId &lt;= 0)\n\t\tOR (processsupervisor.userId &lt;= 0 AND \n\t\t\t((processsupervisor.roleId = role.id AND processsupervisor.groupId = group_.id)\n\t\t\tOR (processsupervisor.roleId = role.id AND processsupervisor.groupId &lt;= 0)\n\t\t\tOR (processsupervisor.roleId &lt;= 0 AND processsupervisor.groupId = group_.id))))\n\t</query>\n\t\n\t<query name=\"searchSProcessSupervisorwithSUserSGroupSRole\">\n\t\tSELECT DISTINCT processsupervisor.id\n\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor,\n\t\t\torg.bonitasoft.engine.identity.model.SUser as user,\n\t\t\torg.bonitasoft.engine.identity.model.SGroup AS group_,\n\t\t\torg.bonitasoft.engine.identity.model.SRole AS role\n\t\tWHERE ((user.id = processsupervisor.userId AND processsupervisor.roleId &lt;= 0 AND processsupervisor.groupId &lt;= 0)\n\t\tOR (processsupervisor.userId &lt;= 0 AND \n\t\t\t((processsupervisor.roleId = role.id AND processsupervisor.groupId = group_.id)\n\t\t\tOR (processsupervisor.roleId = role.id AND processsupervisor.groupId &lt;= 0)\n\t\t\tOR (processsupervisor.roleId &lt;= 0 AND processsupervisor.groupId = group_.id))))\n\t</query>\n\t\n\t<query name=\"getSupervisorById\">\n\t\tSELECT processsupervisor\n\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE processsupervisor.id = :id\n\t</query>\n\n\t<query name=\"getSupervisor\">\n\t\tSELECT processsupervisor\n\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor\n\t\tWHERE processsupervisor.processDefId = :processDefId\n\t\tAND (\n\t\t\tprocesssupervisor.userId = :userId\n\t\t\tOR processsupervisor.id IN (\n\t\t\t\tSELECT processsupervisor.id\n\t\t\t\tFROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor,\n\t\t \t\t\torg.bonitasoft.engine.identity.model.SUserMembership AS user_membership\n\t \t\t\tWHERE  user_membership.userId = :userId\n\t\t\t\tAND (\n\t\t\t\t\t(processsupervisor.groupId = user_membership.groupId AND processsupervisor.roleId &lt;= 0)\n\t\t\t\t\tOR (processsupervisor.roleId = user_membership.roleId AND processsupervisor.groupId &lt;= 0)\n\t\t\t\t\tOR (processsupervisor.roleId = user_membership.roleId AND processsupervisor.groupId = user_membership.groupId)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t</query>\n\n</hibernate-mapping>\n"
  },
  {
    "path": "bpm/bonita-core/bonita-user-filter/build.gradle",
    "content": "\n\ndependencies {\n    api project(':services:bonita-session')\n    api project(':services:bonita-expression')\n    api project(':bpm:bonita-common')\n    api project(':bpm:bonita-core:bonita-process-definition')\n    api project(':services:bonita-cache')\n    api project(':services:bonita-resources')\n    api project(':services:bonita-connector-executor')\n    api project(':bpm:bonita-core:bonita-process-instance')\n    api project(':bpm:bonita-core:bonita-home-server')\n    api project(':services:bonita-commons')\n    testImplementation libs.assertj\n    testImplementation libs.mockitoCore\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-user-filter/src/main/java/org/bonitasoft/engine/core/filter/FilterResult.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.filter;\n\nimport java.io.Serializable;\nimport java.util.List;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface FilterResult extends Serializable {\n\n    List<Long> getResult();\n\n    boolean shouldAutoAssignTaskIfSingleResult();\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-user-filter/src/main/java/org/bonitasoft/engine/core/filter/UserFilterService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.filter;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.filter.exception.SUserFilterExecutionException;\nimport org.bonitasoft.engine.core.filter.exception.SUserFilterLoadingException;\nimport org.bonitasoft.engine.core.process.definition.model.SUserFilterDefinition;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.SRecorderException;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface UserFilterService {\n\n    String IMPLEMENTATION_EXT = \".impl\";\n\n    FilterResult executeFilter(long processDefinitionId, SUserFilterDefinition sUserFilterDefinition,\n            Map<String, SExpression> inputs, ClassLoader classLoader,\n            SExpressionContext expressionContext, final String actorName) throws SUserFilterExecutionException;\n\n    void removeUserFilters(long processDefinitionId) throws SBonitaReadException, SRecorderException;\n\n    boolean loadUserFilters(long processDefinitionId) throws SUserFilterLoadingException;\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-user-filter/src/main/java/org/bonitasoft/engine/core/filter/exception/SUserFilterExecutionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.filter.exception;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SUserFilterExecutionException extends SBonitaException {\n\n    private static final long serialVersionUID = -573004533756971602L;\n\n    public SUserFilterExecutionException(final Throwable throwable) {\n        super(throwable);\n    }\n\n    public SUserFilterExecutionException(final String message) {\n        super(message);\n    }\n\n    public SUserFilterExecutionException(String message, final Throwable cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-user-filter/src/main/java/org/bonitasoft/engine/core/filter/exception/SUserFilterLoadingException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.filter.exception;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SUserFilterLoadingException extends SBonitaException {\n\n    private static final long serialVersionUID = -746939454533910295L;\n\n    /**\n     *\n     */\n    public SUserFilterLoadingException(final Throwable t) {\n        super(t);\n    }\n\n    /**\n     * @param message\n     */\n    public SUserFilterLoadingException(final String message) {\n        super(message);\n    }\n\n    /**\n     * @param message\n     * @param e1\n     */\n    public SUserFilterLoadingException(final String message, final Throwable t) {\n        super(message, t);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-user-filter/src/main/java/org/bonitasoft/engine/core/filter/impl/FilterResultImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.filter.impl;\n\nimport java.util.ArrayList;\nimport java.util.LinkedHashSet;\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.filter.FilterResult;\n\n/**\n * @author Baptiste Mesta\n */\npublic class FilterResultImpl implements FilterResult {\n\n    private static final long serialVersionUID = -6952608023861384532L;\n\n    private final List<Long> result;\n\n    private final boolean shouldAutoAssignTaskIfSingleResult;\n\n    public FilterResultImpl(final List<Long> userIds, final boolean shouldAutoAssignTaskIfSingleResult) {\n        //To avoid errors due to duplicates see BS-16189\n        if (userIds != null) {\n            result = new ArrayList<>(new LinkedHashSet<>(userIds));\n        } else {\n            result = null;\n        }\n        this.shouldAutoAssignTaskIfSingleResult = shouldAutoAssignTaskIfSingleResult;\n    }\n\n    @Override\n    public List<Long> getResult() {\n        return result;\n    }\n\n    @Override\n    public boolean shouldAutoAssignTaskIfSingleResult() {\n        return shouldAutoAssignTaskIfSingleResult;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-user-filter/src/main/java/org/bonitasoft/engine/core/filter/impl/SConnectorUserFilterAdapter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.filter.impl;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.APIAccessor;\nimport org.bonitasoft.engine.connector.ConnectorValidationException;\nimport org.bonitasoft.engine.connector.EngineExecutionContext;\nimport org.bonitasoft.engine.connector.SConnector;\nimport org.bonitasoft.engine.connector.exception.SConnectorException;\nimport org.bonitasoft.engine.connector.exception.SConnectorValidationException;\nimport org.bonitasoft.engine.filter.AbstractUserFilter;\nimport org.bonitasoft.engine.filter.UserFilter;\nimport org.bonitasoft.engine.filter.UserFilterException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SConnectorUserFilterAdapter implements SConnector {\n\n    private final UserFilter filter;\n\n    private List<Long> userIds;\n\n    private boolean shouldAutoAssignTaskIfSingleResult;\n\n    private final String actorName;\n\n    public SConnectorUserFilterAdapter(final UserFilter filter, final String actorName) {\n        this.filter = filter;\n        this.actorName = actorName;\n    }\n\n    @Override\n    public void setInputParameters(final Map<String, Object> parameters) {\n        final APIAccessor apiAccessor = (APIAccessor) parameters.remove(\"connectorApiAccessor\");\n        final EngineExecutionContext executionContext = (EngineExecutionContext) parameters\n                .remove(\"engineExecutionContext\");\n        if (filter instanceof AbstractUserFilter) {\n            ((AbstractUserFilter) filter).setAPIAccessor(apiAccessor);\n            if (executionContext != null) {\n                ((AbstractUserFilter) filter).setExecutionContext(executionContext);\n            }\n        }\n        filter.setInputParameters(parameters);\n    }\n\n    @Override\n    public void validate() throws SConnectorValidationException {\n        try {\n            filter.validateInputParameters();\n        } catch (ConnectorValidationException e) {\n            throw new SConnectorValidationException(e);\n        }\n    }\n\n    @Override\n    public Map<String, Object> execute() throws SConnectorException {\n        try {\n            userIds = filter.filter(actorName);\n            shouldAutoAssignTaskIfSingleResult = filter.shouldAutoAssignTaskIfSingleResult();\n        } catch (final UserFilterException e) {\n            throw new SConnectorException(e);\n        }\n        return null;\n    }\n\n    public List<Long> getUserIds() {\n        return userIds;\n    }\n\n    public boolean shouldAutoAssignTaskIfSingleResult() {\n        return shouldAutoAssignTaskIfSingleResult;\n    }\n\n    @Override\n    public void connect() {\n        // nothing for user filters\n    }\n\n    @Override\n    public void disconnect() {\n        // nothing for user filters\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-user-filter/src/main/java/org/bonitasoft/engine/core/filter/impl/UserFilterServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.filter.impl;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.concurrent.ExecutionException;\nimport java.util.regex.Pattern;\n\nimport javax.xml.bind.JAXBException;\n\nimport org.bonitasoft.engine.cache.CacheService;\nimport org.bonitasoft.engine.cache.SCacheException;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.connector.ConnectorExecutor;\nimport org.bonitasoft.engine.connector.exception.SConnectorException;\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.filter.FilterResult;\nimport org.bonitasoft.engine.core.filter.UserFilterService;\nimport org.bonitasoft.engine.core.filter.exception.SUserFilterExecutionException;\nimport org.bonitasoft.engine.core.filter.exception.SUserFilterLoadingException;\nimport org.bonitasoft.engine.core.filter.model.UserFilterImplementationDescriptor;\nimport org.bonitasoft.engine.core.filter.model.UserFilterImplementationParser;\nimport org.bonitasoft.engine.core.process.definition.model.SUserFilterDefinition;\nimport org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.filter.UserFilter;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.resources.BARResourceType;\nimport org.bonitasoft.engine.resources.ProcessResourcesService;\nimport org.bonitasoft.engine.resources.SBARResource;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\n\npublic class UserFilterServiceImpl implements UserFilterService {\n\n    private static final Logger logger = LoggerFactory.getLogger(UserFilterServiceImpl.class);\n    public static final String FILTER_CACHE_NAME = \"USER_FILTER\";\n\n    private final ConnectorExecutor connectorExecutor;\n    private final CacheService cacheService;\n    private final ExpressionResolverService expressionResolverService;\n    private final ProcessResourcesService processResourcesService;\n    private final UserFilterImplementationParser userFilterImplementationParser = new UserFilterImplementationParser();\n\n    public UserFilterServiceImpl(final ConnectorExecutor connectorExecutor, final CacheService cacheService,\n            final ExpressionResolverService expressionResolverService,\n            ProcessResourcesService processResourcesService) {\n        super();\n        this.connectorExecutor = connectorExecutor;\n        this.cacheService = cacheService;\n        this.expressionResolverService = expressionResolverService;\n        this.processResourcesService = processResourcesService;\n    }\n\n    @Override\n    public FilterResult executeFilter(final long processDefinitionId, final SUserFilterDefinition sUserFilterDefinition,\n            final Map<String, SExpression> inputs,\n            final ClassLoader classLoader, final SExpressionContext expressionContext, final String actorName)\n            throws SUserFilterExecutionException {\n        final FilterResult filterResult;\n        String implementationClassName = \"\";\n        UserFilterImplementationDescriptor descriptor = null;\n        if (logger.isDebugEnabled()) {\n            logger.debug(\n                    Thread.currentThread().toString() + \"-[\" + Thread.currentThread().getId() + \",\"\n                            + Thread.currentThread().getState() + \"]\");\n        }\n        try {\n            descriptor = getDescriptor(processDefinitionId, sUserFilterDefinition);\n            if (descriptor == null) {\n                loadUserFilters(processDefinitionId);\n                descriptor = getDescriptor(processDefinitionId, sUserFilterDefinition);\n                if (descriptor == null) {\n                    throw new SUserFilterExecutionException(\n                            \"unable to load descriptor for filter \" + sUserFilterDefinition.getUserFilterId());\n                }\n            }\n            implementationClassName = descriptor.getImplementationClassName();\n            filterResult = executeFilterInClassloader(implementationClassName, inputs, classLoader, expressionContext,\n                    actorName);\n        } catch (final SConnectorException e) {\n            String dbgInfo = \"\";\n            if (logger.isDebugEnabled()) {\n                dbgInfo = buildDebugMessage(processDefinitionId, sUserFilterDefinition, inputs, classLoader,\n                        expressionContext, actorName,\n                        implementationClassName, descriptor);\n            }\n            if (e.getCause() != null) {\n                throw new SUserFilterExecutionException(dbgInfo, e.getCause());\n            } else {\n                throw new SUserFilterExecutionException(\"SConnectorException: \" + e.getMessage() + dbgInfo, e);\n            }\n        } catch (final SUserFilterExecutionException e) {\n            throw e;\n        } catch (final Throwable e) { // catch throwable because we might have NoClassDefFound... see ENGINE-1333\n            throw new SUserFilterExecutionException(e);\n        }\n\n        if (logger.isDebugEnabled()) {\n            String stb = \"Executed userFilter [name: <\" +\n                    sUserFilterDefinition.getName() +\n                    \">, user filter id: <\" +\n                    sUserFilterDefinition.getUserFilterId() +\n                    \">, version: <\" +\n                    sUserFilterDefinition.getVersion() +\n                    \">] on flow node instance with id: <\" +\n                    expressionContext.getContainerId() +\n                    \">\";\n\n            logger.debug(stb);\n        }\n        return filterResult;\n    }\n\n    protected String buildDebugMessage(long processDefinitionId, SUserFilterDefinition sUserFilterDefinition,\n            Map<String, SExpression> inputs,\n            ClassLoader classLoader, SExpressionContext expressionContext, String actorName,\n            String implementationClassName,\n            UserFilterImplementationDescriptor descriptor) {\n        return \" Flow node instance id: <\" +\n                expressionContext.getContainerId() +\n                \">\" +\n                \"\\n Current Thread ID : <\" +\n                Thread.currentThread().getId() +\n                \">, Current Thread State : <\" +\n                Thread.currentThread().getState() +\n                \">,\\n ProcessDefinitionID : <\" +\n                processDefinitionId +\n                \">, SUserFilterDefinition : <\" +\n                sUserFilterDefinition +\n                \">, Inputs : <\" +\n                inputs.toString() +\n                \">, ClassLoader : <\" +\n                classLoader.toString() +\n                \">, ExpressionContext : <\" +\n                expressionContext +\n                \">, ActorName : <\" +\n                actorName +\n                \">, UserFilterImplementationDescriptor : <\" +\n                descriptor +\n                \">, ImplementationClassName : <\" +\n                implementationClassName +\n                \">\";\n    }\n\n    private UserFilterImplementationDescriptor getDescriptor(final long processDefinitionId,\n            final SUserFilterDefinition sUserFilterDefinition)\n            throws SCacheException {\n        return (UserFilterImplementationDescriptor) cacheService.get(FILTER_CACHE_NAME,\n                getUserFilterImplementationIdInCache(processDefinitionId, sUserFilterDefinition.getUserFilterId(),\n                        sUserFilterDefinition.getVersion()));\n    }\n\n    private String getUserFilterImplementationIdInCache(final long processDefinitionId, final String userFilterId,\n            final String version) {\n        return processDefinitionId + \":\" + userFilterId + \"-\" + version;\n    }\n\n    protected FilterResult executeFilterInClassloader(final String implementationClassName,\n            final Map<String, SExpression> parameters,\n            final ClassLoader classLoader, final SExpressionContext expressionContext, final String actorName)\n            throws InstantiationException,\n            IllegalAccessException, ClassNotFoundException, SUserFilterExecutionException,\n            SExpressionTypeUnknownException, SExpressionEvaluationException,\n            SExpressionDependencyMissingException, SInvalidExpressionException, SConnectorException,\n            InterruptedException, ExecutionException {\n        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        try {\n            Thread.currentThread().setContextClassLoader(classLoader);\n            final UserFilter filter = (UserFilter) Class.forName(implementationClassName, true, classLoader)\n                    .newInstance();\n            final SConnectorUserFilterAdapter adapter = new SConnectorUserFilterAdapter(filter, actorName);\n            final HashMap<String, Object> inputParameters = new HashMap<>(parameters.size());\n            for (final Entry<String, SExpression> input : parameters.entrySet()) {\n                try {\n                    if (expressionContext != null) {\n                        inputParameters.put(input.getKey(),\n                                expressionResolverService.evaluate(input.getValue(), expressionContext));\n                    } else {\n                        inputParameters.put(input.getKey(), expressionResolverService.evaluate(input.getValue()));\n                    }\n                } catch (SBonitaException e) {\n                    e.setConnectorInputOnContext(input.getKey());\n                    throw e;\n                }\n            }\n            connectorExecutor.execute(adapter, inputParameters, classLoader).get();\n            return new FilterResultImpl(adapter.getUserIds(),\n                    adapter.shouldAutoAssignTaskIfSingleResult());\n        } finally {\n            Thread.currentThread().setContextClassLoader(contextClassLoader);\n        }\n    }\n\n    @Override\n    public void removeUserFilters(final long processDefinitionId) throws SBonitaReadException, SRecorderException {\n        processResourcesService.removeAll(processDefinitionId, BARResourceType.USER_FILTER);\n    }\n\n    @Override\n    public boolean loadUserFilters(final long processDefinitionId) throws SUserFilterLoadingException {\n        String name = null;\n        try {\n            final List<SBARResource> listFiles = processResourcesService.get(processDefinitionId,\n                    BARResourceType.USER_FILTER, 0, 1000);//FIXME\n            final Pattern pattern = Pattern.compile(\"^.*\\\\\" + IMPLEMENTATION_EXT + \"$\");\n            for (final SBARResource file : listFiles) {\n                name = file.getName();\n                if (pattern.matcher(name).matches()) {\n                    UserFilterImplementationDescriptor userFilterImplementationDescriptor;\n                    userFilterImplementationDescriptor = userFilterImplementationParser\n                            .convert(new String(file.getContent()));\n                    if (userFilterImplementationDescriptor == null) {\n                        throw new SUserFilterLoadingException(\n                                \"Can not parse ConnectorImplementation XML. The file name is \" + name);\n                    }\n                    cacheService.store(\n                            FILTER_CACHE_NAME,\n                            getUserFilterImplementationIdInCache(processDefinitionId,\n                                    userFilterImplementationDescriptor.getDefinitionId(),\n                                    userFilterImplementationDescriptor.getDefinitionVersion()),\n                            userFilterImplementationDescriptor);\n                }\n            }\n            return true;\n        } catch (final JAXBException e) {\n            throw new SUserFilterLoadingException(\n                    \"Cannot load userFilterImplementationDescriptor XML. The file name is \" + name, e);\n        } catch (final SCacheException e) {\n            throw new SUserFilterLoadingException(\"Unable to cache the user filter implementation\" + name, e);\n        } catch (SBonitaReadException e) {\n            throw new SUserFilterLoadingException(\"Unable to list the user filter implementations\", e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-user-filter/src/test/java/org/bonitasoft/engine/core/filter/impl/FilterResultImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.filter.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.junit.Test;\n\n/**\n * @author Danila Mazour\n */\n\npublic class FilterResultImplTest {\n\n    @Test\n    public void filterResult_should_not_return_duplicates() {\n        //given\n        List<Long> listWithDuplicates = Arrays.asList(15L, 15L, 15L, 28L, 32L, 28L, 39L);\n        FilterResultImpl filterResult = new FilterResultImpl(listWithDuplicates, true);\n\n        //when\n        List<Long> result = filterResult.getResult();\n\n        //then\n        assertThat(result).containsExactly(15L, 28L, 32L, 39L);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-user-filter/src/test/java/org/bonitasoft/engine/core/filter/impl/SConnectorUserFilterAdapterTest.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.filter.impl;\n\nimport static org.mockito.Mockito.verify;\n\nimport org.bonitasoft.engine.filter.UserFilter;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class SConnectorUserFilterAdapterTest {\n\n    @Mock\n    UserFilter userFilter;\n\n    SConnectorUserFilterAdapter sConnectorUserFilterAdapter;\n\n    @Before\n    public void setUp() {\n        sConnectorUserFilterAdapter = new SConnectorUserFilterAdapter(userFilter, \"anActor\");\n    }\n\n    @Test\n    public void validate_should_call_validateInputParameters() throws Exception {\n        sConnectorUserFilterAdapter.validate();\n\n        verify(userFilter).validateInputParameters();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-core/bonita-user-filter/src/test/java/org/bonitasoft/engine/core/filter/impl/UserFilterServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.core.filter.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.hamcrest.core.IsInstanceOf.instanceOf;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyInt;\nimport static org.mockito.ArgumentMatchers.anyMap;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.*;\n\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.concurrent.CompletableFuture;\n\nimport org.bonitasoft.engine.bpm.userfilter.impl.UserFilterDefinitionImpl;\nimport org.bonitasoft.engine.cache.CacheService;\nimport org.bonitasoft.engine.connector.ConnectorExecutor;\nimport org.bonitasoft.engine.connector.ConnectorValidationException;\nimport org.bonitasoft.engine.connector.exception.SConnectorException;\nimport org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService;\nimport org.bonitasoft.engine.core.expression.control.model.SExpressionContext;\nimport org.bonitasoft.engine.core.filter.exception.SUserFilterExecutionException;\nimport org.bonitasoft.engine.core.filter.model.JarDependencies;\nimport org.bonitasoft.engine.core.filter.model.UserFilterImplementationDescriptor;\nimport org.bonitasoft.engine.core.process.definition.model.impl.SUserFilterDefinitionImpl;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.filter.AbstractUserFilter;\nimport org.bonitasoft.engine.filter.UserFilterException;\nimport org.bonitasoft.engine.resources.BARResourceType;\nimport org.bonitasoft.engine.resources.ProcessResourcesService;\nimport org.bonitasoft.engine.resources.SBARResource;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Baptiste Mesta\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class UserFilterServiceImplTest {\n\n    public static final long PROCESS_DEFINITION_ID = 123456L;\n    @InjectMocks\n    private UserFilterServiceImpl userFilterService;\n    @Mock\n    private ConnectorExecutor connectorExecutor;\n    @Mock\n    private CacheService cacheService;\n    @Mock\n    private ExpressionResolverService expressionResolverService;\n    @Mock\n    private ProcessResourcesService resourceService;\n    private SUserFilterDefinitionImpl sUserFilterDefinition;\n    private UserFilterImplementationDescriptor userFilterImplementationDescriptor;\n\n    @Captor\n    private ArgumentCaptor<UserFilterImplementationDescriptor> userFilterImplementationDescriptorArgumentCaptor;\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Before\n    public void setup() {\n        sUserFilterDefinition = new SUserFilterDefinitionImpl(\n                new UserFilterDefinitionImpl(\"UserFiler\", \"filterId\", \"version\"));\n        userFilterImplementationDescriptor = new UserFilterImplementationDescriptor(MyUserFilter.class.getName(), \"id\",\n                \"version\", \"filterId\", \"version\",\n                new JarDependencies(Collections.singletonList(\"dep.jar\")));\n    }\n\n    @Test\n    public void executeFilter_should_work_for_existing_descriptor() throws Exception {\n        doReturn(CompletableFuture.completedFuture(Collections.emptyMap())).when(connectorExecutor).execute(any(),\n                anyMap(), any());\n        doReturn(userFilterImplementationDescriptor).when(cacheService).get(eq(\"USER_FILTER\"),\n                eq(\"\" + PROCESS_DEFINITION_ID + \":filterId-version\"));\n\n        userFilterService.executeFilter(PROCESS_DEFINITION_ID, sUserFilterDefinition, Collections.emptyMap(),\n                new URLClassLoader(new URL[] {}, Thread.currentThread().getContextClassLoader()),\n                new SExpressionContext(), \"actorName\");\n    }\n\n    @Test(expected = SUserFilterExecutionException.class)\n    public void executeFilter_should_fail_for_non_existing_descriptor() throws Exception {\n        userFilterService.executeFilter(PROCESS_DEFINITION_ID, sUserFilterDefinition,\n                Collections.<String, SExpression> emptyMap(),\n                new URLClassLoader(new URL[] {}, Thread.currentThread().getContextClassLoader()),\n                new SExpressionContext(), \"actorName\");\n    }\n\n    @Test\n    public void loadUserFilters_should_load_from_resourceService() throws Exception {\n        userFilterService.loadUserFilters(PROCESS_DEFINITION_ID);\n\n        verify(resourceService).get(eq(PROCESS_DEFINITION_ID), eq(BARResourceType.USER_FILTER), anyInt(), anyInt());\n    }\n\n    @Test\n    public void should_parse_user_filter_implementation_file_and_cache_it_when_loading_userfilters() throws Exception {\n        //given\n        byte[] userFilterImplContent = (\"<connectorImplementation>\\n\" +\n                \"\\n\" +\n                \"\\t<definitionId>user-filter-def</definitionId>\\n\" +\n                \"\\t<definitionVersion>1.0</definitionVersion>\\n\" +\n                \"\\t<implementationClassname>org.bonitasoft.user.filter.TestUserFilter</implementationClassname>\\n\" +\n                \"\\t<implementationId>user-filter-impl</implementationId>\\n\" +\n                \"\\t<implementationVersion>1.0</implementationVersion>\\n\" +\n                \"\\n\" +\n                \"\\t<jarDependencies>\\n\" +\n                \"\\t\\t<jarDependency>UserFilterDependency.jar</jarDependency>\\n\" +\n                \"\\t</jarDependencies>\\n\" +\n                \"</connectorImplementation>\\n\").getBytes();\n        doReturn(Collections.singletonList(new SBARResource(\"my-user-filter.impl\", BARResourceType.USER_FILTER,\n                PROCESS_DEFINITION_ID, userFilterImplContent)))\n                .when(resourceService)\n                .get(eq(PROCESS_DEFINITION_ID), eq(BARResourceType.USER_FILTER), anyInt(), anyInt());\n        //when\n        userFilterService.loadUserFilters(PROCESS_DEFINITION_ID);\n\n        //then\n        verify(cacheService).store(eq(\"USER_FILTER\"), eq(PROCESS_DEFINITION_ID + \":user-filter-def-1.0\"),\n                userFilterImplementationDescriptorArgumentCaptor.capture());\n        UserFilterImplementationDescriptor userFilterImplementationDescriptor = userFilterImplementationDescriptorArgumentCaptor\n                .getValue();\n        assertThat(userFilterImplementationDescriptor.getDefinitionId()).isEqualTo(\"user-filter-def\");\n        assertThat(userFilterImplementationDescriptor.getDefinitionVersion()).isEqualTo(\"1.0\");\n        assertThat(userFilterImplementationDescriptor.getImplementationClassName())\n                .isEqualTo(\"org.bonitasoft.user.filter.TestUserFilter\");\n        assertThat(userFilterImplementationDescriptor.getId()).isEqualTo(\"user-filter-impl\");\n        assertThat(userFilterImplementationDescriptor.getVersion()).isEqualTo(\"1.0\");\n        assertThat(userFilterImplementationDescriptor.getJarDependencies().getDependencies())\n                .containsOnly(\"UserFilterDependency.jar\");\n\n    }\n\n    @Test\n    public void executeFilter_should_throw_a_SUserFilterExecutionException_when_receiving_SConnectorException_with_null_cause()\n            throws Exception {\n\n        //given\n        UserFilterServiceImpl spyUserFilterService = spy(\n                new UserFilterServiceImpl(connectorExecutor, cacheService, expressionResolverService, resourceService));\n        doReturn(userFilterImplementationDescriptor).when(cacheService).get(eq(\"USER_FILTER\"),\n                eq(\"\" + PROCESS_DEFINITION_ID + \":filterId-version\"));\n        doThrow(new SConnectorException(\"Test exception\")).when(spyUserFilterService).executeFilterInClassloader(\n                anyString(), anyMap(), (URLClassLoader) any(),\n                (SExpressionContext) any(), anyString());\n        //then\n        expectedException.expect(SUserFilterExecutionException.class);\n        expectedException.expectMessage(\"Test exception\");\n        //when\n        spyUserFilterService.executeFilter(PROCESS_DEFINITION_ID, sUserFilterDefinition,\n                Collections.<String, SExpression> emptyMap(),\n                new URLClassLoader(new URL[] {}, Thread.currentThread().getContextClassLoader()),\n                new SExpressionContext(), \"actorName\");\n\n    }\n\n    @Test\n    public void executeFilter_should_throw_a_SUserFilterExecutionException_when_receiving_SConnectorException_with_nonNull_cause()\n            throws Exception {\n\n        //given\n        SConnectorException theException = mock(SConnectorException.class);\n        UserFilterServiceImpl spyUserFilterService = spy(\n                new UserFilterServiceImpl(connectorExecutor, cacheService, expressionResolverService, resourceService));\n        doReturn(userFilterImplementationDescriptor).when(cacheService).get(eq(\"USER_FILTER\"),\n                eq(\"\" + PROCESS_DEFINITION_ID + \":filterId-version\"));\n        when(theException.getCause()).thenReturn(new RuntimeException(\" The root cause\"));\n        doThrow(theException).when(spyUserFilterService).executeFilterInClassloader(anyString(), anyMap(),\n                (URLClassLoader) any(), (SExpressionContext) any(),\n                anyString());\n\n        //then\n        expectedException.expect(SUserFilterExecutionException.class);\n        expectedException.expectMessage(\"Flow node instance id:\");\n        expectedException.expectCause(instanceOf(RuntimeException.class));\n        //when\n        spyUserFilterService.executeFilter(PROCESS_DEFINITION_ID, sUserFilterDefinition,\n                Collections.<String, SExpression> emptyMap(),\n                new URLClassLoader(new URL[] {}, Thread.currentThread().getContextClassLoader()),\n                new SExpressionContext(), \"actorName\");\n\n    }\n\n    @Test\n    public void executeFilter_should_throw_a_SUserFilterExecutionException_when_receiving_SConnectorException_in_debug_mode()\n            throws Exception {\n\n        //given\n        SConnectorException theException = mock(SConnectorException.class);\n        UserFilterServiceImpl spyUserFilterService = spy(\n                new UserFilterServiceImpl(connectorExecutor, cacheService, expressionResolverService, resourceService));\n        doReturn(userFilterImplementationDescriptor).when(cacheService).get(eq(\"USER_FILTER\"),\n                eq(\"\" + PROCESS_DEFINITION_ID + \":filterId-version\"));\n        when(theException.getCause()).thenReturn(new RuntimeException(\" The root cause\"));\n        doThrow(theException).when(spyUserFilterService).executeFilterInClassloader(anyString(), anyMap(),\n                (URLClassLoader) any(), (SExpressionContext) any(),\n                anyString());\n\n        //then\n        expectedException.expect(SUserFilterExecutionException.class);\n        expectedException.expectMessage(\"Current Thread ID : <\");\n        //when\n        spyUserFilterService.executeFilter(PROCESS_DEFINITION_ID, sUserFilterDefinition,\n                Collections.<String, SExpression> emptyMap(),\n                new URLClassLoader(new URL[] {}, Thread.currentThread().getContextClassLoader()),\n                new SExpressionContext(), \"actorName\");\n    }\n\n    @Test\n    public void buildDebugMessage_should_contain_all_the_debug_info() {\n\n        //when\n        String result = userFilterService.buildDebugMessage(45L, sUserFilterDefinition,\n                new HashMap<String, SExpression>(),\n                new URLClassLoader(new URL[] {}, Thread.currentThread().getContextClassLoader()),\n                new SExpressionContext(), \"an actor\",\n                \"an implementation class name\", userFilterImplementationDescriptor);\n        //then\n        assertThat(result).contains(\"an actor\");\n        assertThat(result).contains(\"an implementation class name\");\n        assertThat(result).contains(\"45\");\n        assertThat(result).contains(sUserFilterDefinition.toString());\n        assertThat(result).contains(userFilterImplementationDescriptor.toString());\n        assertThat(result).contains(\"RUNNABLE\");\n    }\n\n    public static class MyUserFilter extends AbstractUserFilter {\n\n        @Override\n        public void validateInputParameters() throws ConnectorValidationException {\n\n        }\n\n        @Override\n        public List<Long> filter(String actorName) throws UserFilterException {\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-external/build.gradle",
    "content": "\n\ndependencies {\n    api project(':bpm:bonita-core:bonita-process-engine')\n    api project(':services:bonita-command')\n    api project(':bpm:bonita-core:bonita-process-definition')\n    testImplementation libs.mockitoCore\n}\n"
  },
  {
    "path": "bpm/bonita-external/src/main/java/org/bonitasoft/engine/external/comment/SearchCommentsSupervisedBy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.external.comment;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.command.RuntimeCommand;\nimport org.bonitasoft.engine.command.SCommandExecutionException;\nimport org.bonitasoft.engine.command.SCommandParameterizationException;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.core.process.comment.api.SCommentService;\nimport org.bonitasoft.engine.external.comment.transaction.SearchCommentsSupervisedByTransaction;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor;\nimport org.bonitasoft.engine.service.ServiceAccessor;\n\n/**\n * @author Hongwen Zang\n * @author Matthieu Chaffotte\n */\npublic class SearchCommentsSupervisedBy extends RuntimeCommand {\n\n    private static final String SUPERVISOR_ID_KEY = \"supervisorId\";\n\n    @Override\n    public Serializable execute(final Map<String, Serializable> parameters, final ServiceAccessor serviceAccessor)\n            throws SCommandParameterizationException, SCommandExecutionException {\n\n        final Long supervisorId = (Long) parameters.get(SUPERVISOR_ID_KEY);\n        if (supervisorId == null) {\n            throw new SCommandParameterizationException(SUPERVISOR_ID_KEY + \" is missing\");\n        }\n\n        final SearchOptions searchOptions = (SearchOptions) parameters.get(\"SEARCH_OPTIONS_KEY\");\n        if (searchOptions == null) {\n            throw new SCommandParameterizationException(\"SEARCH_OPTIONS_KEY is missing\");\n        }\n\n        final SCommentService commentService = serviceAccessor.getCommentService();\n        final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor();\n        final SearchCommentsSupervisedByTransaction searchTransaction = new SearchCommentsSupervisedByTransaction(\n                supervisorId, commentService,\n                searchEntitiesDescriptor.getSearchCommentDescriptor(), searchOptions);\n        try {\n            searchTransaction.execute();\n        } catch (final SBonitaException sbe) {\n            throw new SCommandExecutionException(sbe);\n        }\n        return searchTransaction.getResult();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-external/src/main/java/org/bonitasoft/engine/external/comment/transaction/SearchCommentsSupervisedByTransaction.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.external.comment.transaction;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.core.process.comment.api.SCommentService;\nimport org.bonitasoft.engine.core.process.comment.model.SComment;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.search.AbstractCommentSearchEntity;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor;\n\n/**\n * @author Hongwen Zang\n */\npublic class SearchCommentsSupervisedByTransaction extends AbstractCommentSearchEntity {\n\n    private final SCommentService commentService;\n\n    private final Long supervisorId;\n\n    public SearchCommentsSupervisedByTransaction(final Long supervisorId, final SCommentService commentService,\n            final SearchEntityDescriptor searchDescriptor,\n            final SearchOptions searchOptions) {\n        super(searchDescriptor, searchOptions);\n        this.supervisorId = supervisorId;\n        this.commentService = commentService;\n    }\n\n    @Override\n    public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException {\n        return commentService.getNumberOfCommentsSupervisedBy(supervisorId, searchOptions);\n    }\n\n    @Override\n    public List<SComment> executeSearch(final QueryOptions searchOptions) throws SBonitaReadException {\n        return commentService.searchCommentsSupervisedBy(supervisorId, searchOptions);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-sap-jco-connector-api/src/main/java/com/sap/conn/jco/ext/DestinationDataEventListener.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage com.sap.conn.jco.ext;\n\npublic interface DestinationDataEventListener {\n\n    void deleted(String destinationName);\n\n    void updated(String destinationName);\n\n}\n"
  },
  {
    "path": "bpm/bonita-sap-jco-connector-api/src/main/java/com/sap/conn/jco/ext/DestinationDataProvider.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage com.sap.conn.jco.ext;\n\nimport java.util.Properties;\n\npublic interface DestinationDataProvider {\n\n    Properties getDestinationProperties(final String destinationName);\n\n    void setDestinationDataEventListener(final DestinationDataEventListener eventListener);\n\n    boolean supportsEvents();\n\n}\n"
  },
  {
    "path": "bpm/bonita-sap-jco-connector-api/src/main/java/com/sap/conn/jco/ext/Environment.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage com.sap.conn.jco.ext;\n\npublic abstract class Environment {\n\n    public static void registerDestinationDataProvider(DestinationDataProvider destinationDataProvider) {\n        // Mock implementation to remove sap jco connector compile dependency\n        // Concrete implementation should be provided at runtime\n    }\n\n    public static void unregisterDestinationDataProvider(DestinationDataProvider destinationDataProvider) {\n        // Mock implementation to remove sap jco connector compile dependency\n        // Concrete implementation should be provided at runtime\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-server/build.gradle",
    "content": "import org.bonitasoft.engine.gradle.ShadeDependency\n\nplugins { id 'bonita-shade' }\n\ndependencies {\n    // import jackson bom to align jackson dependencies even when resolving shade dependencies\n    // When create the shade version of bonita-server, the bonita-common project is excluded causing the jackson bom not to be imported\n    // This cause issues to the code deposit because an different version of jackson, that was not downloaded to local cache, can be required during offline build.\n    // Remove the shade to solve that issue\n    implementation(platform(libs.jacksonBom))\n    api project(':bpm:bonita-core:bonita-process-engine')\n    api project(':services:bonita-builder')\n    api project(':bpm:bonita-core:bonita-actor-mapping')\n    api project(':bpm:bonita-api:bonita-server-api-http')\n    api project(':services:bonita-archive')\n    api project(':services:bonita-authentication')\n    api project(':services:bonita-business-application')\n    api project(':services:bonita-business-data:bonita-business-data-impl')\n    api project(':services:bonita-business-data:bonita-business-data-client-resources')\n    api project(':services:bonita-business-data:bonita-business-data-generator')\n    api project(':services:bonita-cache')\n    api project(':bpm:bonita-core:bonita-category')\n    api project(':services:bonita-classloader')\n    api project(':services:bonita-command')\n    api project(':bpm:bonita-core:bonita-contract-data')\n    api project(':services:bonita-connector-executor')\n    api project(':services:bonita-data-definition')\n    api project(':services:bonita-data-instance')\n    api project(':services:bonita-events')\n    api project(':services:bonita-expression')\n    api project(':bpm:bonita-core:bonita-form-mapping')\n    api project(':services:bonita-incident')\n    api project(':services:bonita-identity')\n    api project(':services:bonita-lock')\n    api project(':bpm:bonita-core:bonita-login')\n    api project(':services:bonita-log')\n    api project(':services:bonita-page')\n    api project(':bpm:bonita-core:bonita-parameter')\n    api project(':services:bonita-persistence')\n    api project(':services:bonita-platform')\n    api project(':services:bonita-platform-authentication')\n    api project(':services:bonita-platform-command')\n    api project(':bpm:bonita-core:bonita-platform-login')\n    api project(':services:bonita-platform-session')\n    api project(':bpm:bonita-core:bonita-process-comment')\n    api project(':bpm:bonita-core:bonita-process-definition')\n    api project(':bpm:bonita-core:bonita-process-instance')\n    api project(':services:bonita-scheduler')\n    api project(':services:bonita-session')\n    api project(':bpm:bonita-core:bonita-supervisor-mapping')\n    api project(':bpm:bonita-synchro-repository:bonita-synchro-service')\n    api project(':bpm:bonita-synchro-repository:bonita-synchro-service-impl')\n    api project(':bpm:bonita-synchro-repository:bonita-synchro-register')\n    api project(':services:bonita-time-tracker')\n    api project(':services:bonita-transaction')\n    api project(':bpm:bonita-core:bonita-core-data')\n    api project(':bpm:bonita-core:bonita-user-filter')\n    api project(':services:bonita-work')\n    api project(':bpm:bonita-external')\n    api project(':services:bonita-profile')\n    api project(':bpm:bonita-common')\n}\n\nshade {\n    exclude project(':bpm:bonita-common')\n    exclude project(':platform:platform-resources')\n\n    excludeLibs('hibernate-core',\n            new ShadeDependency(group: 'org.jboss.spec.javax.transaction', name: 'jboss-transaction-api_1.2_spec'),\n            new ShadeDependency(group: 'javax.activation', name: 'javax.activation-api')\n    )\n    excludeLibs('jaxb-api',\n            new ShadeDependency(group: 'javax.activation', name: 'javax.activation-api'))\n    excludeLibs('jaxb-runtime',\n            new ShadeDependency(group: 'javax.activation', name: 'javax.activation-api'))\n    excludeLibs('quartz',\n            new ShadeDependency(group: 'com.mchange', name: '*'),\n            new ShadeDependency(group: 'com.zaxxer', name: 'HikariCP-java7'))\n}\n\nafterEvaluate {\n    publishing {\n        publications {\n            shadow(MavenPublication) { publication ->\n                publication.getPom().with {\n                    name = \"Bonita Server\"\n                    description = \"Bonita Server is the Server-side BPM Execution Engine\"\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-synchro-repository/bonita-synchro-register/build.gradle",
    "content": "\n\ndependencies {\n    api project(':bpm:bonita-core:bonita-process-engine')\n    api project(':bpm:bonita-synchro-repository:bonita-synchro-service-impl')\n    api project(':services:bonita-commons')\n    annotationProcessor(libs.lombok)\n    compileOnly(libs.lombok)\n}\n"
  },
  {
    "path": "bpm/bonita-synchro-repository/bonita-synchro-register/src/main/java/org/bonitasoft/engine/synchro/AbstractUpdateHandler.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.synchro;\n\nimport java.io.Serializable;\nimport java.lang.reflect.Method;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.events.model.SEvent;\nimport org.bonitasoft.engine.events.model.SHandler;\nimport org.bonitasoft.engine.events.model.SHandlerExecutionException;\nimport org.bonitasoft.engine.service.ServiceAccessor;\nimport org.bonitasoft.engine.service.impl.ServiceAccessorFactory;\nimport org.bonitasoft.engine.transaction.BonitaTransactionSynchronization;\nimport org.bonitasoft.engine.transaction.STransactionNotFoundException;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\n\n/**\n * @author Baptiste Mesta\n */\npublic abstract class AbstractUpdateHandler implements SHandler<SEvent> {\n\n    private static final long serialVersionUID = 1L;\n\n    private final Map<Class<?>, Method> getIdMethods = Collections.synchronizedMap(new HashMap<Class<?>, Method>());\n\n    public AbstractUpdateHandler() {\n        super();\n    }\n\n    protected abstract Map<String, Serializable> getEvent(final SEvent sEvent);\n\n    @Override\n    public void execute(final SEvent sEvent) throws SHandlerExecutionException {\n        try {\n            final Map<String, Serializable> event = getEvent(sEvent);\n            final Long id = getObjectId(sEvent);\n\n            final ServiceAccessor serviceAccessor = getServiceAccessor();\n            final BonitaTransactionSynchronization synchronization = getSynchronization(event, id, serviceAccessor);\n\n            final UserTransactionService userTransactionService = serviceAccessor.getUserTransactionService();\n            userTransactionService.registerBonitaSynchronization(synchronization);\n        } catch (final STransactionNotFoundException e) {\n            e.printStackTrace();\n            throw new SHandlerExecutionException(e);\n        }\n    }\n\n    protected BonitaTransactionSynchronization getSynchronization(final Map<String, Serializable> event, final Long id,\n            final ServiceAccessor serviceAccessor) {\n        return new WaitForEventSynchronization(event, id, serviceAccessor.getSynchroService());\n    }\n\n    /**\n     * @param sEvent\n     * @return\n     */\n    private Long getObjectId(final SEvent sEvent) {\n        Long id = null;\n        Object object = null;\n        try {\n            object = sEvent.getObject();\n            final Class<?> clazz = object.getClass();\n            Method method = null;\n            if (getIdMethods.containsKey(clazz)) {\n                method = getIdMethods.get(clazz);\n            } else {\n                method = clazz.getMethod(\"getId\");\n            }\n            final Object invoke = method.invoke(object);\n            id = (Long) invoke;\n        } catch (final Exception e) {\n            System.err.println(\"AbstractUpdateHandler: No id on object \" + object);\n        }\n        return id;\n    }\n\n    private ServiceAccessor getServiceAccessor() throws SHandlerExecutionException {\n        try {\n            return ServiceAccessorFactory.getInstance().createServiceAccessor();\n        } catch (final Exception e) {\n            throw new SHandlerExecutionException(e.getMessage(), null);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-synchro-repository/bonita-synchro-register/src/main/java/org/bonitasoft/engine/synchro/AddHandlerCommand.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.synchro;\n\nimport java.io.Serializable;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.command.RuntimeCommand;\nimport org.bonitasoft.engine.command.SCommandExecutionException;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.events.model.SEvent;\nimport org.bonitasoft.engine.events.model.SHandler;\nimport org.bonitasoft.engine.service.ServiceAccessor;\n\npublic class AddHandlerCommand extends RuntimeCommand {\n\n    private static final String PROCESSINSTANCE_STATE_UPDATED = \"PROCESSINSTANCE_STATE_UPDATED\";\n\n    private static final String ACTIVITYINSTANCE_CREATED = \"ACTIVITYINSTANCE_CREATED\";\n\n    private static final String ACTIVITYINSTANCE_STATE_UPDATED = \"ACTIVITYINSTANCE_STATE_UPDATED\";\n\n    private static final String EVENT_INSTANCE_CREATED = \"EVENT_INSTANCE_CREATED\";\n\n    private static final String GATEWAYINSTANCE_CREATED = \"GATEWAYINSTANCE_CREATED\";\n\n    private static final String GATEWAYINSTANCE_STATE_UPDATED = \"GATEWAYINSTANCE_STATE_UPDATED\";\n\n    @Override\n    public Serializable execute(final Map<String, Serializable> parameters, final ServiceAccessor serviceAccessor)\n            throws SCommandExecutionException {\n        final EventService eventService = serviceAccessor.getEventService();\n        try {\n            if (!containsHandler(eventService, PROCESSINSTANCE_STATE_UPDATED, ProcessInstanceHandler.class)) {\n                eventService.addHandler(PROCESSINSTANCE_STATE_UPDATED, new ProcessInstanceHandler());\n            }\n            if (!containsHandler(eventService, ACTIVITYINSTANCE_STATE_UPDATED, FlowNodeHandler.class)) {\n                eventService.addHandler(ACTIVITYINSTANCE_STATE_UPDATED, new FlowNodeHandler());\n                eventService.addHandler(ACTIVITYINSTANCE_CREATED, new FlowNodeHandler());\n                eventService.addHandler(EVENT_INSTANCE_CREATED, new FlowNodeHandler());\n            }\n            if (!containsHandler(eventService, GATEWAYINSTANCE_CREATED, GatewayHandler.class)) {\n                eventService.addHandler(GATEWAYINSTANCE_CREATED, new GatewayHandler());\n                eventService.addHandler(GATEWAYINSTANCE_STATE_UPDATED, new GatewayHandler());\n            }\n        } catch (final SBonitaException e) {\n            throw new SCommandExecutionException(e);\n        }\n        return null;\n    }\n\n    private boolean containsHandler(final EventService eventService, final String eventType, final Class<?> clazz) {\n        final Set<SHandler<SEvent>> handlers = eventService.getHandlers(eventType);\n        if (handlers != null) {\n            for (final SHandler<SEvent> sHandler : handlers) {\n                if (clazz.isInstance(sHandler)) {\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-synchro-repository/bonita-synchro-register/src/main/java/org/bonitasoft/engine/synchro/EventUtil.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.synchro;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\n\n/**\n * Utility methods to get maps that match process as wanted\n *\n * @author Baptiste Mesta\n */\npublic class EventUtil {\n\n    private static final String FLOW_NODE = \"flowNode\";\n\n    private static final String PROCESS = \"process\";\n\n    private static final String TYPE = \"type\";\n\n    private static final String NAME = \"name\";\n\n    private static final String ROOT_CONTAINER_ID = \"rootContainerId\";\n\n    private static final String PARENT_CONTAINER_ID = \"parentContainerId\";\n\n    private static final String PROCESS_DEFINITION_ID = \"processDefinitionId\";\n\n    private static final String STATE_ID = \"stateId\";\n\n    private static final String ID = \"id\";\n\n    private static final String STATE = \"state\";\n\n    public static Map<String, Serializable> getEventForProcess(final SProcessInstance instance) {\n        final Map<String, Serializable> map = new HashMap<String, Serializable>(4);\n        map.put(TYPE, PROCESS);\n        map.put(ID, instance.getId());\n        map.put(STATE_ID, instance.getStateId());\n        map.put(PROCESS_DEFINITION_ID, instance.getProcessDefinitionId());\n        map.put(NAME, instance.getName());\n        return map;\n    }\n\n    public static Map<String, Serializable> getEventForFlowNode(final SFlowNodeInstance flowNodeInstance) {\n        final Map<String, Serializable> map = new HashMap<String, Serializable>(5);\n        map.put(TYPE, FLOW_NODE);\n        map.put(ID, flowNodeInstance.getId());\n        map.put(ROOT_CONTAINER_ID, flowNodeInstance.getRootContainerId());\n        map.put(PARENT_CONTAINER_ID, flowNodeInstance.getParentContainerId());\n        map.put(NAME, flowNodeInstance.getName());\n        map.put(STATE_ID, flowNodeInstance.getStateId());\n        map.put(STATE, flowNodeInstance.getStateName());\n        return map;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-synchro-repository/bonita-synchro-register/src/main/java/org/bonitasoft/engine/synchro/FlowNodeHandler.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.synchro;\n\nimport java.io.Serializable;\nimport java.util.Map;\nimport java.util.UUID;\n\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.events.model.SEvent;\n\n/**\n * @author Baptiste Mesta\n */\npublic class FlowNodeHandler extends AbstractUpdateHandler {\n\n    private static final long serialVersionUID = 1L;\n    private final String identifier;\n\n    public FlowNodeHandler() {\n        super();\n        identifier = UUID.randomUUID().toString();\n    }\n\n    @Override\n    protected Map<String, Serializable> getEvent(final SEvent sEvent) {\n        final SFlowNodeInstance flowNodeInstance = (SFlowNodeInstance) sEvent.getObject();\n        return EventUtil.getEventForFlowNode(flowNodeInstance);\n    }\n\n    @Override\n    public boolean isInterested(final SEvent event) {\n        // the !isStateExecuting avoids having 2 times the same event in case of execution of e.g. connectors\n        return event.getObject() instanceof SFlowNodeInstance\n                && !((SFlowNodeInstance) event.getObject()).isStateExecuting();\n    }\n\n    @Override\n    public String getIdentifier() {\n        return identifier;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-synchro-repository/bonita-synchro-register/src/main/java/org/bonitasoft/engine/synchro/GatewayHandler.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.synchro;\n\nimport java.io.Serializable;\nimport java.util.Map;\nimport java.util.UUID;\n\nimport org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;\nimport org.bonitasoft.engine.core.process.instance.model.SGatewayInstance;\nimport org.bonitasoft.engine.events.model.SEvent;\n\n/**\n * @author Celine Souchet\n */\npublic class GatewayHandler extends AbstractUpdateHandler {\n\n    private static final long serialVersionUID = 1L;\n\n    private final String identifier;\n\n    public GatewayHandler() {\n        super();\n        this.identifier = UUID.randomUUID().toString();\n    }\n\n    @Override\n    protected Map<String, Serializable> getEvent(final SEvent sEvent) {\n        final SFlowNodeInstance flowNodeInstance = (SFlowNodeInstance) sEvent.getObject();\n        return EventUtil.getEventForFlowNode(flowNodeInstance);\n    }\n\n    @Override\n    public boolean isInterested(final SEvent event) {\n        return event.getObject() instanceof SGatewayInstance;\n    }\n\n    @Override\n    public String getIdentifier() {\n        return identifier;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-synchro-repository/bonita-synchro-register/src/main/java/org/bonitasoft/engine/synchro/PerfEventUtil.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.synchro;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Utility methods to get maps that match process as wanted\n *\n * @author Baptiste Mesta\n */\npublic class PerfEventUtil {\n\n    private static final String FLOW_NODE = \"flowNode\";\n\n    private static final String PROCESS = \"process\";\n\n    private static final String TYPE = \"type\";\n\n    private static final String NAME = \"name\";\n\n    private static final String STATE_ID = \"stateId\";\n\n    private static final String ID = \"id\";\n\n    public static Map<String, Serializable> getProcessInstanceFinishedEvent(final long processInstanceId) {\n        final HashMap<String, Serializable> map = new HashMap<String, Serializable>(3);\n        map.put(TYPE, PROCESS);\n        map.put(ID, processInstanceId);\n        map.put(STATE_ID, 6);\n        return map;\n    }\n\n    public static Map<String, Serializable> getReadyTaskEvent(final long processInstanceId, final String taskName) {\n        final HashMap<String, Serializable> map = new HashMap<String, Serializable>(4);\n        map.put(TYPE, FLOW_NODE);\n        map.put(ID, processInstanceId);\n        map.put(NAME, taskName);\n        map.put(STATE_ID, 4);\n        return map;\n    }\n\n    public static Map<String, Serializable> getFlowNodeReachStateEvent(final long processInstanceId,\n            final String taskName, final int stateId) {\n        final HashMap<String, Serializable> map = new HashMap<String, Serializable>(4);\n        map.put(TYPE, FLOW_NODE);\n        map.put(ID, processInstanceId);\n        map.put(NAME, taskName);\n        map.put(STATE_ID, stateId);\n        return map;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-synchro-repository/bonita-synchro-register/src/main/java/org/bonitasoft/engine/synchro/ProcessInstanceHandler.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.synchro;\n\nimport java.io.Serializable;\nimport java.util.Map;\nimport java.util.UUID;\n\nimport org.bonitasoft.engine.core.process.instance.model.SProcessInstance;\nimport org.bonitasoft.engine.events.model.SEvent;\n\n/**\n * @author Baptiste Mesta\n */\npublic class ProcessInstanceHandler extends AbstractUpdateHandler {\n\n    private static final long serialVersionUID = 1L;\n    private final String identifier;\n\n    public ProcessInstanceHandler() {\n        super();\n        identifier = UUID.randomUUID().toString();\n    }\n\n    @Override\n    protected Map<String, Serializable> getEvent(final SEvent sEvent) {\n        final SProcessInstance instance = (SProcessInstance) sEvent.getObject();\n        return EventUtil.getEventForProcess(instance);\n    }\n\n    @Override\n    public boolean isInterested(final SEvent event) {\n        final Object object = event.getObject();\n        return object instanceof SProcessInstance;\n    }\n\n    @Override\n    public String getIdentifier() {\n        return identifier;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-synchro-repository/bonita-synchro-register/src/main/java/org/bonitasoft/engine/synchro/WaitForEventSynchronization.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.synchro;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport javax.transaction.Status;\n\nimport org.bonitasoft.engine.transaction.BonitaTransactionSynchronization;\n\n/**\n * @author Baptiste Mesta\n */\npublic class WaitForEventSynchronization implements BonitaTransactionSynchronization {\n\n    private final Map<String, Serializable> event;\n\n    private final Long id;\n\n    private final SynchroService synchroService;\n\n    public WaitForEventSynchronization(final Map<String, Serializable> event, final Long id,\n            final SynchroService synchroService) {\n        this.event = event;\n        this.id = id;\n        this.synchroService = synchroService;\n    }\n\n    @Override\n    public void afterCompletion(final int status) {\n        if (status == Status.STATUS_COMMITTED) {\n            synchroService.fireEvent(event, id);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-synchro-repository/bonita-synchro-register/src/main/java/org/bonitasoft/engine/synchro/WaitServerCommand.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.synchro;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.command.RuntimeCommand;\nimport org.bonitasoft.engine.command.SCommandExecutionException;\nimport org.bonitasoft.engine.service.ServiceAccessor;\n\n/**\n * @author Baptiste Mesta\n */\npublic class WaitServerCommand extends RuntimeCommand {\n\n    @Override\n    public Serializable execute(final Map<String, Serializable> parameters, final ServiceAccessor serviceAccessor)\n            throws SCommandExecutionException {\n        if (parameters.get(\"clear\") != null) {\n            serviceAccessor.getSynchroService().clearAllEvents();\n            return null;\n        }\n        @SuppressWarnings(\"unchecked\")\n        final Map<String, Serializable> event = (Map<String, Serializable>) parameters.get(\"event\");\n        final int timeout = (Integer) parameters.get(\"timeout\");\n        try {\n            return serviceAccessor.getSynchroService().waitForEvent(event, timeout);\n        } catch (final Exception e) {\n            throw new SCommandExecutionException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-synchro-repository/bonita-synchro-service/build.gradle",
    "content": "\n"
  },
  {
    "path": "bpm/bonita-synchro-repository/bonita-synchro-service/src/main/java/org/bonitasoft/engine/synchro/SynchroService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.synchro;\n\nimport java.io.Serializable;\nimport java.util.Map;\nimport java.util.concurrent.TimeoutException;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic interface SynchroService {\n\n    void fireEvent(Map<String, Serializable> event, Serializable id);\n\n    Serializable waitForEvent(Map<String, Serializable> event, long timeout)\n            throws InterruptedException, TimeoutException;\n\n    void clearAllEvents();\n\n    boolean hasWaiters();\n\n}\n"
  },
  {
    "path": "bpm/bonita-synchro-repository/bonita-synchro-service-impl/build.gradle",
    "content": "\n\ndependencies {\n    api project(':services:bonita-commons')\n    api project(':services:bonita-cache')\n    api project(':bpm:bonita-synchro-repository:bonita-synchro-service')\n}\n"
  },
  {
    "path": "bpm/bonita-synchro-repository/bonita-synchro-service-impl/src/main/java/org/bonitasoft/engine/synchro/AbstractSynchroService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.synchro;\n\nimport java.io.Serializable;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.concurrent.TimeoutException;\nimport java.util.concurrent.locks.Lock;\n\nimport org.bonitasoft.engine.cache.CacheService;\nimport org.bonitasoft.engine.cache.SCacheException;\nimport org.slf4j.Logger;\n\n/**\n * @author Emmanuel Duchastenier\n * @author Charles Souillard\n */\n\npublic abstract class AbstractSynchroService implements SynchroService {\n\n    protected static final String SYNCHRO_SERVICE_CACHE = \"SYNCHRO_SERVICE_CACHE\";\n\n    /**\n     * String value is an identifier of the sempaphore for the current event.\n     */\n    protected abstract Map<Map<String, Serializable>, String> getWaitersMap();\n\n    protected abstract Logger getLogger();\n\n    /**\n     * Maitains a map of <EventKey, ID of the object being waited for>\n     */\n    protected abstract Map<String, Serializable> getEventKeyAndIdMap();\n\n    protected abstract void releaseWaiter(String semaphoreKey);\n\n    protected abstract Lock getServiceLock();\n\n    protected final CacheService cacheService;\n\n    public AbstractSynchroService(final CacheService cacheService) {\n        this.cacheService = cacheService;\n    }\n\n    @Override\n    public void fireEvent(final Map<String, Serializable> event, final Serializable id) {\n        getLogger().debug(\"Firing event \" + event + \" with id \" + id);\n        getServiceLock().lock();\n        try {\n            final String semaphoreKey = getWaiterAndRemoveIt(event);\n            if (semaphoreKey == null) {\n                // No waiter found yet:\n                getLogger().debug(\"No waiter found, storing event \" + event);\n                try {\n                    cacheService.store(SYNCHRO_SERVICE_CACHE, (Serializable) event, id);\n                } catch (SCacheException e) {\n                    throw new RuntimeException(e);\n                }\n            } else {\n                // Waiter already exists, let's release waiter:\n                getEventKeyAndIdMap().put(semaphoreKey, id);\n                getLogger().debug(\n                        \"releasing waiter for event \" + event + \" and id \" + id);\n                releaseWaiter(semaphoreKey);\n            }\n        } finally {\n            getServiceLock().unlock();\n        }\n    }\n\n    protected String getWaiterAndRemoveIt(final Map<String, Serializable> event) {\n        for (final Iterator<Entry<Map<String, Serializable>, String>> iterator = getWaitersMap().entrySet()\n                .iterator(); iterator.hasNext();) {\n            final Entry<Map<String, Serializable>, String> waiter = iterator.next();\n            if (matchedAtLeastAllExpectedEntries(waiter.getKey(), event)) {\n                iterator.remove();\n                return waiter.getValue();\n            }\n        }\n        return null;\n    }\n\n    protected boolean matchedAtLeastAllExpectedEntries(final Map<String, Serializable> expectedEventEntries,\n            final Map<String, Serializable> actualEventEntries) {\n        for (final Entry<String, Serializable> expectedEventEntry : expectedEventEntries.entrySet()) {\n            final Serializable expectedValue = expectedEventEntry.getValue();\n            if (!expectedValue.equals(actualEventEntries.get(expectedEventEntry.getKey()))) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected Serializable getFiredAndRemoveIt(final Map<String, Serializable> expectedEvent) {\n        try {\n            List<?> firedEvents = cacheService.getKeys(SYNCHRO_SERVICE_CACHE);\n            for (Map<String, Serializable> firedEvent : (List<Map<String, Serializable>>) firedEvents) {\n                if (matchedAtLeastAllExpectedEntries(expectedEvent, firedEvent)) {\n                    cacheService.remove(SYNCHRO_SERVICE_CACHE, firedEvent);\n                    return firedEvent.get(\"id\");\n                }\n            }\n        } catch (SCacheException e) {\n            throw new RuntimeException(e);\n        }\n        return null;\n    }\n\n    protected void throwTimeout(final Map<String, Serializable> event, final long timeout) throws TimeoutException {\n        throw new TimeoutException(\n                \"Event '\" + event + \"' has not been received on time after waiting '\" + timeout + \" ms'\");\n    }\n\n    @Override\n    public void clearAllEvents() {\n        try {\n            cacheService.clear(SYNCHRO_SERVICE_CACHE);\n        } catch (SCacheException e) {\n            throw new RuntimeException(e);\n        }\n        getWaitersMap().clear();\n    }\n\n    @Override\n    public boolean hasWaiters() {\n        return !getWaitersMap().isEmpty();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-synchro-repository/bonita-synchro-service-impl/src/main/java/org/bonitasoft/engine/synchro/SynchroServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.synchro;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.TreeMap;\nimport java.util.concurrent.Semaphore;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.TimeoutException;\nimport java.util.concurrent.locks.Lock;\nimport java.util.concurrent.locks.ReentrantLock;\n\nimport org.bonitasoft.engine.cache.CacheService;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Emmanuel Duchastenier\n * @author Baptiste Mesta\n * @author Charles Souillard\n */\npublic class SynchroServiceImpl extends AbstractSynchroService {\n\n    private final Logger logger = LoggerFactory.getLogger(SynchroServiceImpl.class);\n    private final Map<Map<String, Serializable>, String> waiters;\n\n    private final Map<String, Serializable> eventKeyAndIdMap;\n\n    private final Map<String, Semaphore> eventSemaphores;\n\n    private final Lock lock = new SynchroServiceImplReentrantLock();\n\n    /**\n     * @param initialCapacity\n     *        the initial capacity of the map of fired events / waiters (default 50)\n     */\n    private SynchroServiceImpl(final int initialCapacity, final CacheService cacheService) {\n        super(cacheService);\n        waiters = new HashMap<>(initialCapacity);\n        eventKeyAndIdMap = new HashMap<>(initialCapacity);\n        eventSemaphores = new HashMap<>();\n    }\n\n    private static final class SynchroServiceImplReentrantLock extends ReentrantLock {\n\n    }\n\n    @Override\n    protected Map<Map<String, Serializable>, String> getWaitersMap() {\n        return waiters;\n    }\n\n    @Override\n    protected Logger getLogger() {\n        return logger;\n    }\n\n    @Override\n    protected Map<String, Serializable> getEventKeyAndIdMap() {\n        return eventKeyAndIdMap;\n    }\n\n    @Override\n    protected Lock getServiceLock() {\n        return lock;\n    }\n\n    @Override\n    protected void releaseWaiter(final String semaphoreKey) {\n        final Semaphore semaphore = eventSemaphores.get(semaphoreKey);\n        if (semaphore != null) {\n            semaphore.release();\n        }\n    }\n\n    @Override\n    public Serializable waitForEvent(final Map<String, Serializable> event, final long timeout)\n            throws InterruptedException, TimeoutException {\n        Serializable id;\n        String semaphoreKey = null;\n        Semaphore semaphore = null;\n        getServiceLock().lock();\n        try {\n            id = getFiredAndRemoveIt(event);\n            if (id == null) {\n                semaphoreKey = getSemaphoreKey(event);\n                semaphore = new Semaphore(1);\n                eventSemaphores.put(semaphoreKey, semaphore);\n                semaphore.acquire(1);\n                getWaitersMap().put(event, semaphoreKey);\n            }\n        } finally {\n            getServiceLock().unlock();\n        }\n        if (semaphore != null) {\n            try {\n                if (!semaphore.tryAcquire(timeout, TimeUnit.MILLISECONDS)) {\n                    throwTimeout(event, timeout);\n                }\n            } catch (final InterruptedException e) {\n                throwTimeout(event, timeout);\n            } finally {\n                getWaitersMap().remove(event);\n            }\n            return getEventKeyAndIdMap().get(semaphoreKey);\n        }\n        return id;\n    }\n\n    private String getSemaphoreKey(final Map<String, Serializable> event) {\n        final StringBuilder sb = new StringBuilder();\n        final TreeMap<String, Serializable> orderedMap = new TreeMap<>(event);\n        boolean first = true;\n        for (final Map.Entry<String, Serializable> entry : orderedMap.entrySet()) {\n            if (!first) {\n                sb.append(\";;;;;\");\n            } else {\n                first = false;\n            }\n            sb.append(entry.getKey());\n            sb.append(\"=\");\n            sb.append(entry.getValue());\n        }\n        return sb.toString();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-extensions/build.gradle",
    "content": "import org.bonitasoft.engine.gradle.PomUtils\n\ndependencies {\n    api(libs.javaxServletApi)\n    api(project(\":bpm:bonita-client\"))\n    api(project(\":bpm:bonita-common\"))\n    testImplementation libs.assertj\n}\n\ngroup = 'org.bonitasoft.web'\n\njava {\n    withSourcesJar()\n    withJavadocJar()\n}\n\npublishing {\n    publications {\n        mavenJava(MavenPublication) {\n            from components.java\n            pom { pom ->\n                name = 'Bonita Web Extensions'\n                description = 'Provide API to develop custom web extensions like pages and rest apis'\n                PomUtils.pomCommunityPublication(pom)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-extensions/src/main/java/org/bonitasoft/web/extension/ResourceProvider.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.extension;\n\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.InputStream;\nimport java.util.Locale;\nimport java.util.ResourceBundle;\n\n/**\n * Access to the extension resources\n */\npublic interface ResourceProvider {\n\n    /**\n     * Retrieve a resource as an {@link InputStream}\n     *\n     * @param resourceName the name of the resource to retrieve. It can be a path.\n     * @return a {@link InputStream} for this resource\n     */\n    InputStream getResourceAsStream(final String resourceName) throws FileNotFoundException;\n\n    /**\n     * Retrieve a resource as a {@link File}\n     *\n     * @param resourceName the name of the resource to retrieve. It can be a path.\n     * @return a {@link File} for this resource\n     */\n    File getResourceAsFile(final String resourceName);\n\n    /**\n     * Retrieve a resource URL\n     *\n     * @param resourceName the name of the resource to retrieve. It can be a path.\n     * @return the URL of where the resource is available.\n     */\n    String getResourceURL(final String resourceName);\n\n    /**\n     * Retrieve a {@link ResourceBundle} that can be used for localization.\n     *\n     * @param name the name of the resource to retrieve (e.g. : messages for a resource named messages_fr.properties)\n     * @param locale the {@link Locale} of the {@link ResourceBundle} to retrieve.(e.g. : Locale.FRENCH for a resource\n     *        named messages_fr.properties)\n     * @return the {@link ResourceBundle} for given name and {@link Locale}\n     */\n    ResourceBundle getResourceBundle(final String name, final Locale locale);\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-extensions/src/main/java/org/bonitasoft/web/extension/page/PageContext.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.extension.page;\n\nimport java.util.Locale;\n\nimport org.bonitasoft.engine.session.APISession;\n\n/**\n * This class provide access to the data relative to the context in which the custom page is displayed\n *\n * @since 7.2.0\n */\npublic interface PageContext {\n\n    /**\n     * @return the engine {@link APISession}\n     */\n    APISession getApiSession();\n\n    /**\n     * @return the user locale\n     */\n    Locale getLocale();\n\n    /**\n     * @return the ID of the profile in which the page is currently displayed\n     */\n    String getProfileID();\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-extensions/src/main/java/org/bonitasoft/web/extension/page/PageController.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.extension.page;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\n/**\n * The interface to implement for a Custom Page in Bonita.\n */\npublic interface PageController {\n\n    /**\n     * Let the custom page parse request for specific attribute handling.\n     *\n     * @param request\n     *        the HTTP servlet request intended to be used as in a servlet\n     * @param response\n     *        the HTTP servlet response intended to be used as in a servlet\n     * @param pageResourceProvider\n     *        provide access to the resources contained in the custom page zip\n     * @param pageContext\n     *        provide access to the data relative to the context in which the custom page is displayed\n     */\n    void doGet(HttpServletRequest request, HttpServletResponse response, PageResourceProvider pageResourceProvider,\n            PageContext pageContext);\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-extensions/src/main/java/org/bonitasoft/web/extension/page/PageResourceProvider.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.extension.page;\n\nimport java.io.File;\n\nimport org.bonitasoft.engine.api.PageAPI;\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.engine.page.PageNotFoundException;\nimport org.bonitasoft.web.extension.ResourceProvider;\n\n/**\n * This interface provide access to the resources contained in the extension archive\n *\n * @since 7.2.0\n */\npublic interface PageResourceProvider extends ResourceProvider {\n\n    /**\n     * The URL of the CSS used as Bonita Theme.\n     */\n    String getBonitaThemeCSSURL();\n\n    /**\n     * The folder where the extension is deployed.\n     */\n    File getPageDirectory();\n\n    /**\n     * The page name. Set in the page.properties of the extension.\n     */\n    String getPageName();\n\n    /**\n     * The page name with the process definition ID as prefix (format = p%PROCESS_DEF_ID_%pageName).\n     */\n    String getFullPageName();\n\n    /**\n     * The deployed {@link Page} for this extension.\n     */\n    Page getPage(final PageAPI pageAPI) throws PageNotFoundException;\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-extensions/src/main/java/org/bonitasoft/web/extension/rest/RestAPIContext.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.extension.rest;\n\nimport java.util.Locale;\n\nimport org.bonitasoft.engine.api.APIClient;\nimport org.bonitasoft.engine.api.IdentityAPI;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.extension.ResourceProvider;\n\n/**\n * This class provide access to the data relative to the context in which the Rest API extension is called\n *\n * @since 7.2.0\n */\npublic interface RestAPIContext {\n\n    /**\n     * The {@link APIClient} is used to access business data and Bonita APIs such as:\n     * <ul>\n     * <li>{@link IdentityAPI},</li>\n     * <li>{@link ProcessAPI},</li>\n     * <li>...</li>\n     * </ul>\n     *\n     * @return an engine {@link APIClient} logged to the current {@link APISession}\n     */\n    APIClient getApiClient();\n\n    /**\n     * @return Current engine {@link APISession}\n     */\n    APISession getApiSession();\n\n    /**\n     * @return Current selected {@link Locale} in BonitaBPM Portal\n     */\n    Locale getLocale();\n\n    /**\n     * @return a {@link ResourceProvider} to retrieve resources location\n     */\n    ResourceProvider getResourceProvider();\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-extensions/src/main/java/org/bonitasoft/web/extension/rest/RestApiController.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.extension.rest;\n\nimport javax.servlet.http.HttpServletRequest;\n\n/**\n * The interface to implement for Rest API extension in Bonita.\n */\npublic interface RestApiController {\n\n    /**\n     * Let the Rest API Extension parse request for specific attribute handling.\n     *\n     * @param request the HTTP servlet request intended to be used as in a servlet\n     * @param responseBuilder a builder for HTTP response\n     * @param context to access the current execution context data like current session,locale...\n     * @return a RestApiResponse with a body, an HTTP status and other optional http content\n     * @since 7.2\n     */\n    RestApiResponse doHandle(HttpServletRequest request, RestApiResponseBuilder responseBuilder,\n            RestAPIContext context);\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-extensions/src/main/java/org/bonitasoft/web/extension/rest/RestApiResponse.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.extension.rest;\n\nimport java.io.Serializable;\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.servlet.http.Cookie;\n\n/**\n * A Wrapper of an HTTP Response describe by its:\n * <ul>\n * <li>Status</li>\n * <li>Headers</li>\n * <li>Body</li>\n * <li>Charset</li>\n * <li>Media type</li>\n * <li>Cookies</li>\n * </ul>\n */\npublic class RestApiResponse {\n\n    /**\n     * default http status code\n     */\n    public static final int DEFAULT_STATUS = 200;\n\n    public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;\n    /**\n     * default character set\n     */\n    public static final String DEFAULT_CHARACTER_SET = DEFAULT_CHARSET.name();\n\n    /**\n     * default media type\n     */\n    public static final String DEFAULT_MEDIA_TYPE = \"application/json\";\n\n    private final Serializable response;\n    private final int httpStatus;\n    private final Map<String, String> additionalHeaders;\n    private final List<Cookie> additionalCookies;\n    private final String mediaType;\n    private final String characterSet;\n\n    public RestApiResponse(Serializable response, int httpStatus, Map<String, String> additionalHeaders,\n            List<Cookie> additionalCookies, String mediaType,\n            String characterSet) {\n        this.response = response;\n        this.httpStatus = httpStatus;\n        this.additionalHeaders = additionalHeaders;\n        this.additionalCookies = additionalCookies;\n        this.mediaType = mediaType;\n        this.characterSet = characterSet;\n    }\n\n    public Serializable getResponse() {\n        return response;\n    }\n\n    public int getHttpStatus() {\n        return httpStatus;\n    }\n\n    public Map<String, String> getAdditionalHeaders() {\n        return additionalHeaders;\n    }\n\n    public List<Cookie> getAdditionalCookies() {\n        return additionalCookies;\n    }\n\n    public String getCharacterSet() {\n        return characterSet;\n    }\n\n    public String getMediaType() {\n        return mediaType;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-extensions/src/main/java/org/bonitasoft/web/extension/rest/RestApiResponseBuilder.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.extension.rest;\n\nimport static org.bonitasoft.web.extension.rest.RestApiResponse.*;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.servlet.http.Cookie;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.apache.http.HttpHeaders;\n\n/**\n * Build a RestApiResponse specifying response body, status and other HTTP attributes.\n */\npublic class RestApiResponseBuilder {\n\n    protected Serializable response;\n    protected int httpStatus;\n    protected final Map<String, String> additionalHeaders;\n    protected final List<Cookie> additionalCookies;\n    protected String characterSet;\n    protected String mediaType;\n\n    private int pageIndex = -1;\n    private int pageSize = -1;\n    private long totalSize = -1;\n\n    public RestApiResponseBuilder() {\n        this.httpStatus = DEFAULT_STATUS;\n        this.additionalHeaders = new HashMap<>();\n        this.additionalCookies = new ArrayList<>();\n        this.characterSet = DEFAULT_CHARACTER_SET;\n        this.mediaType = DEFAULT_MEDIA_TYPE;\n    }\n\n    /**\n     * Set the body of the response\n     *\n     * @param response the response body\n     */\n    public RestApiResponseBuilder withResponse(Serializable response) {\n        this.response = response;\n        return this;\n    }\n\n    /**\n     * Set the HTTP status of the response. By default, OK status (200) is set.\n     *\n     * @param httpStatus the HTTP status of the response\n     * @see HttpServletResponse\n     */\n    public RestApiResponseBuilder withResponseStatus(int httpStatus) {\n        this.httpStatus = httpStatus;\n        return this;\n    }\n\n    /**\n     * Adds a header in the HTTP response\n     *\n     * @param name the name of the header to add in the response.\n     * @param value the value for this header\n     * @see org.apache.http.HttpHeaders\n     */\n    public RestApiResponseBuilder withAdditionalHeader(String name, String value) {\n        additionalHeaders.put(name, value);\n        return this;\n    }\n\n    /**\n     * Adds a cookie to the HTTP response\n     *\n     * @param cookie the {@link javax.servlet.http.Cookie} to add to the response\n     */\n    public RestApiResponseBuilder withAdditionalCookie(Cookie cookie) {\n        additionalCookies.add(cookie);\n        return this;\n    }\n\n    /**\n     * Set the character set of the HTTP response. By default UTF-8 is set.\n     *\n     * @param characterSet the name of the character set\n     * @see java.nio.charset.Charset\n     */\n    public RestApiResponseBuilder withCharacterSet(String characterSet) {\n        this.characterSet = characterSet;\n        return this;\n    }\n\n    /**\n     * Set the media type of the HTTP response body. By default \"application/json\" is set.\n     *\n     * @param mediaType the media type to set.\n     * @see <a href=\"http://www.iana.org/assignments/media-types/media-types.xhtml\">Registered media types</a>\n     */\n    public RestApiResponseBuilder withMediaType(String mediaType) {\n        this.mediaType = mediaType;\n        return this;\n    }\n\n    /**\n     * When returning a paged result, sets the start index and the page size.\n     * Setting content range overrides the Content-Range header of the response.\n     *\n     * @param pageIndex the start index of the returned page.\n     * @param pageSize the size of the returned page.\n     * @return the {@link RestApiResponseBuilder}\n     */\n    public RestApiResponseBuilder withContentRange(int pageIndex, int pageSize) {\n        this.pageIndex = pageIndex;\n        this.pageSize = pageSize;\n        return this;\n    }\n\n    /**\n     * When returning a paged result, sets the start index, the page size and the total size.\n     * Setting content range overrides the Content-Range header of the response.\n     *\n     * @param pageIndex the start index of the returned page.\n     * @param pageSize the size of the returned page.\n     * @param totalSize the total size of the requested entity.\n     * @return the {@link RestApiResponseBuilder}\n     */\n    public RestApiResponseBuilder withContentRange(int pageIndex, int pageSize, long totalSize) {\n        this.pageIndex = pageIndex;\n        this.pageSize = pageSize;\n        this.totalSize = totalSize;\n        return this;\n    }\n\n    /**\n     * @return the RestApiResponse response\n     */\n    public RestApiResponse build() {\n        if (pageIndex >= 0 && pageSize >= 0) {\n            additionalHeaders.put(HttpHeaders.CONTENT_RANGE,\n                    String.format(\"%s-%s/%s\", pageIndex, pageSize, totalSize >= 0 ? totalSize : \"*\"));\n        }\n        return new RestApiResponse(response, httpStatus, additionalHeaders, additionalCookies, mediaType, characterSet);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-extensions/src/test/java/org/bonitasoft/web/extension/rest/RestApiResponseAssert.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.extension.rest;\n\nimport static java.lang.String.format;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport javax.servlet.http.Cookie;\n\nimport org.assertj.core.api.AbstractAssert;\nimport org.assertj.core.api.Assertions;\n\n/**\n * {@link RestApiResponse} specific assertions - Generated by CustomAssertionGenerator.\n */\npublic class RestApiResponseAssert extends AbstractAssert<RestApiResponseAssert, RestApiResponse> {\n\n    /**\n     * Creates a new </code>{@link RestApiResponseAssert}</code> to make assertions on actual RestApiResponse.\n     *\n     * @param actual the RestApiResponse we want to make assertions on.\n     */\n    public RestApiResponseAssert(RestApiResponse actual) {\n        super(actual, RestApiResponseAssert.class);\n    }\n\n    /**\n     * An entry point for RestApiResponseAssert to follow AssertJ standard <code>assertThat()</code> statements.<br>\n     * With a static import, one's can write directly : <code>assertThat(myRestApiResponse)</code> and get specific\n     * assertion with code completion.\n     *\n     * @param actual the RestApiResponse we want to make assertions on.\n     * @return a new </code>{@link RestApiResponseAssert}</code>\n     */\n    public static RestApiResponseAssert assertThat(RestApiResponse actual) {\n        return new RestApiResponseAssert(actual);\n    }\n\n    /**\n     * Verifies that the actual RestApiResponse's additionalCookies contains the given Cookie elements.\n     *\n     * @param additionalCookies the given elements that should be contained in actual RestApiResponse's\n     *        additionalCookies.\n     * @return this assertion object.\n     * @throws AssertionError if the actual RestApiResponse's additionalCookies does not contain all given Cookie\n     *         elements.\n     */\n    public RestApiResponseAssert hasAdditionalCookies(Cookie... additionalCookies) {\n        // check that actual RestApiResponse we want to make assertions on is not null.\n        isNotNull();\n\n        // check that given Cookie varargs is not null.\n        if (additionalCookies == null)\n            throw new AssertionError(\"Expecting additionalCookies parameter not to be null.\");\n\n        // check with standard error message (see commented below to set your own message).\n        Assertions.assertThat(actual.getAdditionalCookies()).contains(additionalCookies);\n\n        // uncomment the 4 lines below if you want to build your own error message :\n        // WritableAssertionInfo assertionInfo = new WritableAssertionInfo();\n        // String errorMessage = \"my error message\";\n        // assertionInfo.overridingErrorMessage(errorMessage);\n        // Iterables.instance().assertContains(assertionInfo, actual.getTeamMates(), teamMates);\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual RestApiResponse has no additionalCookies.\n     *\n     * @return this assertion object.\n     * @throws AssertionError if the actual RestApiResponse's additionalCookies is not empty.\n     */\n    public RestApiResponseAssert hasNoAdditionalCookies() {\n        // check that actual RestApiResponse we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected :\\n  <%s>\\nnot to have additionalCookies but had :\\n  <%s>\", actual,\n                actual.getAdditionalCookies());\n\n        // check\n        if (!actual.getAdditionalCookies().isEmpty())\n            throw new AssertionError(errorMessage);\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual RestApiResponse's additionalHeaders is equal to the given one.\n     *\n     * @param additionalHeaders the given additionalHeaders to compare the actual RestApiResponse's additionalHeaders\n     *        to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual RestApiResponse's additionalHeaders is not equal to the given one.\n     */\n    public RestApiResponseAssert hasAdditionalHeaders(Map additionalHeaders) {\n        // check that actual RestApiResponse we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> additionalHeaders to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                additionalHeaders,\n                actual.getAdditionalHeaders());\n\n        // check\n        if (!actual.getAdditionalHeaders().equals(additionalHeaders)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual RestApiResponse's characterSet is equal to the given one.\n     *\n     * @param characterSet the given characterSet to compare the actual RestApiResponse's characterSet to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual RestApiResponse's characterSet is not equal to the given one.\n     */\n    public RestApiResponseAssert hasCharacterSet(String characterSet) {\n        // check that actual RestApiResponse we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> characterSet to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                characterSet, actual.getCharacterSet());\n\n        // check\n        if (!actual.getCharacterSet().equals(characterSet)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual RestApiResponse's httpStatus is equal to the given one.\n     *\n     * @param httpStatus the given httpStatus to compare the actual RestApiResponse's httpStatus to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual RestApiResponse's httpStatus is not equal to the given one.\n     */\n    public RestApiResponseAssert hasHttpStatus(int httpStatus) {\n        // check that actual RestApiResponse we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> httpStatus to be:\\n  <%s>\\n but was:\\n  <%s>\", actual, httpStatus,\n                actual.getHttpStatus());\n\n        // check\n        if (actual.getHttpStatus() != httpStatus) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual RestApiResponse's mediaType is equal to the given one.\n     *\n     * @param mediaType the given mediaType to compare the actual RestApiResponse's mediaType to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual RestApiResponse's mediaType is not equal to the given one.\n     */\n    public RestApiResponseAssert hasMediaType(String mediaType) {\n        // check that actual RestApiResponse we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> mediaType to be:\\n  <%s>\\n but was:\\n  <%s>\", actual, mediaType,\n                actual.getMediaType());\n\n        // check\n        if (!actual.getMediaType().equals(mediaType)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual RestApiResponse's response is equal to the given one.\n     *\n     * @param response the given response to compare the actual RestApiResponse's response to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual RestApiResponse's response is not equal to the given one.\n     */\n    public RestApiResponseAssert hasResponse(Serializable response) {\n        // check that actual RestApiResponse we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> response to be:\\n  <%s>\\n but was:\\n  <%s>\", actual, response,\n                actual.getResponse());\n\n        // check\n        if (!actual.getResponse().equals(response)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-extensions/src/test/java/org/bonitasoft/web/extension/rest/RestApiResponseTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.extension.rest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\n\nimport javax.servlet.http.Cookie;\n\nimport org.apache.http.HttpHeaders;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class RestApiResponseTest {\n\n    RestApiResponseBuilder restApiResponseBuilder;\n\n    @Before\n    public void setUp() {\n        restApiResponseBuilder = new RestApiResponseBuilder();\n    }\n\n    @Test\n    public void testGetResponse_with_default_values() {\n        //when\n        final RestApiResponse response = restApiResponseBuilder.withResponse(\"response\").build();\n\n        //then\n        RestApiResponseAssert.assertThat(response)\n                .hasResponse(\"response\")\n                .hasMediaType(\"application/json\")\n                .hasHttpStatus(200)\n                .hasCharacterSet(\"UTF-8\")\n                .hasNoAdditionalCookies();\n\n        assertThat(response.getAdditionalHeaders()).isEmpty();\n    }\n\n    @Test\n    public void testGetHttpStatus() {\n        //when\n        final RestApiResponse response = restApiResponseBuilder.withResponseStatus(404).build();\n\n        //then\n        RestApiResponseAssert.assertThat(response).hasHttpStatus(404);\n\n    }\n\n    @Test\n    public void testGetAdditionalHeaders() {\n        //when\n        final RestApiResponse response = restApiResponseBuilder.withAdditionalHeader(\"x-header1\", \"value1\")\n                .withAdditionalHeader(\"x-header2\", \"value2\")\n                .build();\n\n        //then\n        assertThat(response.getAdditionalHeaders()).hasSize(2).contains(entry(\"x-header1\", \"value1\"),\n                entry(\"x-header1\", \"value1\"));\n    }\n\n    @Test\n    public void testGetAdditionalCookies() {\n        //when\n        final Cookie cookie1 = new Cookie(\"name1\", \"value1\");\n        final Cookie cookie2 = new Cookie(\"name2\", \"value2\");\n\n        final RestApiResponse response = restApiResponseBuilder.withAdditionalCookie(cookie1)\n                .withAdditionalCookie(cookie2)\n                .build();\n\n        //then\n        RestApiResponseAssert.assertThat(response).hasAdditionalCookies(cookie1, cookie2);\n    }\n\n    @Test\n    public void testGetCharacterSet() {\n        //given\n        final String characterSet = \"utf-16\";\n\n        //when\n        final RestApiResponse response = restApiResponseBuilder.withCharacterSet(characterSet)\n                .build();\n\n        //then\n        RestApiResponseAssert.assertThat(response).hasCharacterSet(characterSet);\n    }\n\n    @Test\n    public void testGetMediaType() {\n        //when\n        final String mediaType = \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\";\n\n        //when\n        final RestApiResponse response = restApiResponseBuilder.withMediaType(mediaType)\n                .build();\n\n        //then\n        RestApiResponseAssert.assertThat(response).hasMediaType(mediaType);\n    }\n\n    @Test\n    public void should_not_set_range_when_pageSize_not_set() {\n        //when\n        final RestApiResponse response = restApiResponseBuilder.withContentRange(0, -1)\n                .build();\n\n        //then\n        assertThat(response.getAdditionalHeaders()).doesNotContainKey(HttpHeaders.CONTENT_RANGE);\n    }\n\n    @Test\n    public void should_not_set_range_when_pageIndex_not_set() {\n        //when\n        final RestApiResponse response = restApiResponseBuilder.withContentRange(-1, 10)\n                .build();\n\n        //then\n        assertThat(response.getAdditionalHeaders()).doesNotContainKey(HttpHeaders.CONTENT_RANGE);\n    }\n\n    @Test\n    public void should_set_range_when_pageIndex_and_pageSize_is_set() {\n        //when\n        final RestApiResponse response = restApiResponseBuilder\n                .withContentRange(0, 10)\n                .build();\n\n        //then\n        assertThat(response.getAdditionalHeaders()).contains(entry(HttpHeaders.CONTENT_RANGE, \"0-10/*\"));\n    }\n\n    @Test\n    public void should_set_range_when_pageIndex_and_pageSize_and_totalSize_is_set() {\n        //when\n        final RestApiResponse response = restApiResponseBuilder\n                .withContentRange(0, 10, 100)\n                .build();\n\n        //then\n        assertThat(response.getAdditionalHeaders()).contains(entry(HttpHeaders.CONTENT_RANGE, \"0-10/100\"));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/build.gradle",
    "content": "import org.apache.tools.ant.filters.ReplaceTokens\n\ngroup = 'org.bonitasoft.console'\ndescription = 'Bonita Web Server'\n\ndependencies {\n    implementation(project(\":bpm:bonita-common\"))\n    implementation(project(\":bpm:bonita-web-extensions\"))\n    implementation(project(\":services:bonita-commons\"))\n    implementation libs.commonsIO\n    implementation libs.slf4jApi\n    implementation libs.ehCache\n    implementation libs.commonsFileUpload\n    implementation libs.commonsBeanUtils\n    implementation libs.commonsCollections\n    implementation libs.commonsLang\n    implementation libs.jsonSimple\n    implementation libs.urlrewritefilter\n    implementation libs.jakartaJstl\n    implementation libs.jakartaJstlApi\n    implementation libs.woodstoxCore\n    implementation libs.groovyCore\n    implementation libs.xbeanClassloader\n    implementation libs.springCore\n    implementation libs.springContext\n    implementation libs.springWeb\n    implementation libs.springWebMvc\n    implementation libs.jgettext\n    implementation libs.owaspHtmlSanitizer\n\n    compileOnly libs.lombok\n    annotationProcessor libs.lombok\n\n    testImplementation(project(\":bpm:bonita-server\"))\n    testImplementation(libs.jsonUnit) {\n        exclude(group: \"org.slf4j\", module: \"slf4j-api\")\n    }\n    testImplementation libs.assertj\n    testImplementation libs.logback\n    testImplementation libs.springTest\n    testImplementation libs.systemRules\n    testImplementation libs.mockitoCore\n    testImplementation libs.hamcrest\n    testImplementation libs.systemLambda\n    testRuntimeOnly libs.jsonassert\n}\n\nconfigurations {\n    configureEach {\n        resolutionStrategy.force libs.woodstoxCore\n    }\n    tests\n}\n\nprocessResources {\n    inputs.property(\"version\", project.version)\n    inputs.property(\"brandingVersion\", brandingVersion)\n    from('src/main/resources') {\n        include '**/*'\n        filter(ReplaceTokens, tokens: [projectVersion : project.version,\n                                       brandingVersion: brandingVersion,\n                                       buildYear      : String.valueOf(LocalDate.now().getYear())])\n    }\n}\n\ntasks.register('buildZip', Zip) {\n    from('src/main/resources') {\n        include 'VERSION'\n        filter(ReplaceTokens, tokens: [projectVersion : project.version,\n                                       brandingVersion: brandingVersion,\n                                       buildYear      : String.valueOf(LocalDate.now().getYear())])\n    }\n    from 'src/main/webapp'\n}\n\ntasks.named(\"build\") {\n    dependsOn tasks.named(\"buildZip\")\n}\n\ntasks.register('testsJar', Jar) {\n    dependsOn testClasses\n    archiveClassifier = 'tests'\n    from(sourceSets.test.output)\n}\n\ntest {\n    dependsOn tasks.named(\"testsJar\")\n}\n\nartifacts {\n    tests testsJar\n}\n\npublishing {\n    publications {\n        mavenJava(MavenPublication) {\n            from components.java\n            artifact testsJar\n            artifact buildZip\n            pom { pom ->\n                name = 'Bonita Web Server'\n                description = 'Web API implementation module'\n                org.bonitasoft.engine.gradle.PomUtils.pomCommunityPublication(pom)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/api/CommandCaller.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.api;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\n\n/**\n * @author Vincent Elcrin\n */\npublic final class CommandCaller {\n\n    private final APISession session;\n\n    private final String command;\n\n    private final Map<String, Serializable> parameters;\n\n    /**\n     * Default Constructor.\n     */\n    public CommandCaller(APISession session, final String command) {\n        this.session = session;\n        this.command = command;\n        parameters = new HashMap<>();\n    }\n\n    public CommandCaller addParameter(final String key, final Serializable value) {\n        parameters.put(key, value);\n        return this;\n    }\n\n    public Serializable run() {\n        try {\n            return TenantAPIAccessor.getCommandAPI(this.session).execute(this.command, this.parameters);\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/api/token/APIToken.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.api.token;\n\nimport java.util.UUID;\n\n/**\n * @author Paul AMAR\n */\npublic class APIToken {\n\n    private final String APIToken;\n\n    public APIToken() {\n        this.APIToken = UUID.randomUUID().toString();\n    }\n\n    public String getToken() {\n        return this.APIToken;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/auth/AuthenticationFailedException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.auth;\n\n/**\n * @author Ruiheng Fan\n */\npublic class AuthenticationFailedException extends Exception {\n\n    /**\n     *\n     */\n    private static final long serialVersionUID = 1853195048371475012L;\n\n    /**\n     * Constructor\n     */\n    public AuthenticationFailedException() {\n        super();\n    }\n\n    /**\n     * @param message\n     *        message associated with the exception\n     * @param cause\n     *        cause of the exception\n     */\n    public AuthenticationFailedException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    /**\n     * @param message\n     *        message associated with the exception\n     */\n    public AuthenticationFailedException(final String message) {\n        super(message);\n    }\n\n    /**\n     * @param cause\n     *        cause of the exception\n     */\n    public AuthenticationFailedException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/auth/AuthenticationManager.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.auth;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport javax.servlet.ServletException;\n\nimport org.bonitasoft.console.common.server.login.HttpServletRequestAccessor;\nimport org.bonitasoft.console.common.server.login.credentials.Credentials;\n\n/**\n * Interface to implement in order to delegate the authentication to an external provider\n *\n * @author Ruiheng Fan, Anthony Birembaut\n */\npublic interface AuthenticationManager {\n\n    /**\n     * Redirection URL parameter name\n     */\n    String REDIRECT_URL = \"redirectUrl\";\n\n    /**\n     * the URL param for the login page after logout\n     */\n    String LOGIN_URL_PARAM_NAME = \"loginUrl\";\n\n    /**\n     * the URL param to indicate if we should redirect after login/logout (true by default)\n     */\n    String REDIRECT_AFTER_LOGIN_PARAM_NAME = \"redirect\";\n\n    /**\n     * the URL of the default login page. Only used when @BONITA_RUNTIME_AUTHENTICATION_LOGIN_URL_VAR is not set\n     */\n    String LOGIN_PAGE = \"/login.jsp\";\n\n    /*\n     * The system variable or property name for the login page URL\n     */\n    String BONITA_RUNTIME_AUTHENTICATION_LOGIN_URL_VAR = \"bonita.runtime.authentication.login.url\";\n\n    /**\n     * The default redirect URL.\n     */\n    String DEFAULT_DIRECT_URL = \"apps/appDirectoryBonita\";\n\n    /**\n     * indicate wheather the HTTP session should be invalidated and re created upon login\n     */\n    String INVALIDATE_SESSION = \"authentication.session.invalidate\";\n\n    /**\n     * Get Login Page URL\n     *\n     * @param redirectURL\n     *        redirect url\n     * @return new redirect url\n     */\n    String getLoginPageURL(final HttpServletRequestAccessor requestAccessor, final String redirectURL)\n            throws ServletException;\n\n    /**\n     * Authenticate the user (If no exception is thrown, an engine login will then be performed with the credentials)\n     *\n     * @param credentials\n     *        credentials extracted from the request or from the auto-login config\n     * @return a map of credentials which if not empty (and containing more than just the key\n     *         \"authentication.session.invalidate\") will be used to login on the engine. Otherwise, the username and\n     *         password contained in the\n     *         credentials will be used\n     * @throws AuthenticationFailedException\n     * @throws ServletException\n     */\n    Map<String, Serializable> authenticate(final HttpServletRequestAccessor requestAccessor,\n            final Credentials credentials)\n            throws AuthenticationFailedException, ServletException;\n\n    /**\n     * Get Logout Page URL\n     * If the LoginManager implementation of this method is to return null the default login page will be displayed\n     *\n     * @param redirectURL\n     *        redirect url\n     * @return new redirect url\n     */\n    String getLogoutPageURL(final HttpServletRequestAccessor requestAccessor, final String redirectURL)\n            throws ServletException;\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/auth/AuthenticationManagerFactory.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.auth;\n\nimport org.bonitasoft.console.common.server.auth.impl.standard.StandardAuthenticationManagerImpl;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Ruiheng Fan\n */\npublic class AuthenticationManagerFactory {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(AuthenticationManagerFactory.class.getName());\n\n    static AuthenticationManager authenticationManager;\n\n    public static AuthenticationManager getAuthenticationManager() throws AuthenticationManagerNotFoundException {\n        String authenticationManagerName = null;\n        if (authenticationManager == null) {\n            try {\n                authenticationManagerName = getManagerImplementationClassName();\n                authenticationManager = (AuthenticationManager) Class.forName(authenticationManagerName).newInstance();\n            } catch (final Exception e) {\n                final String message = \"The AuthenticationManager implementation \" + authenticationManagerName\n                        + \" does not exist!\";\n                throw new AuthenticationManagerNotFoundException(message);\n            }\n        }\n        return authenticationManager;\n    }\n\n    private static String getManagerImplementationClassName() {\n        String authenticationManagerName = AuthenticationManagerProperties.getProperties()\n                .getAuthenticationManagerImpl();\n        if (authenticationManagerName == null || authenticationManagerName.isEmpty()) {\n            authenticationManagerName = StandardAuthenticationManagerImpl.class.getName();\n            LOGGER.trace(\"The login manager implementation is undefined. Using default implementation : \"\n                    + authenticationManagerName);\n        }\n        return authenticationManagerName;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/auth/AuthenticationManagerNotFoundException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.auth;\n\n/**\n * @author Ruiheng Fan\n */\npublic class AuthenticationManagerNotFoundException extends Exception {\n\n    /**\n     *\n     */\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * @param message\n     *        message associated with the exception\n     */\n    public AuthenticationManagerNotFoundException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/auth/AuthenticationManagerProperties.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.auth;\n\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport org.apache.commons.lang3.BooleanUtils;\nimport org.bonitasoft.console.common.server.preferences.properties.ConfigurationFile;\n\n/**\n * Utility class for Session Manager access (read in a properties file)\n *\n * @author Ruiheng Fan\n */\npublic class AuthenticationManagerProperties extends ConfigurationFile {\n\n    protected static final String AUTHENTICATION_CONFIG_FILE_NAME = \"authenticationManager-config.properties\";\n    /**\n     * Logout Hidden constant\n     */\n    public static final String LOGOUT_DISABLED = \"logout.link.hidden\";\n\n    /**\n     * Configuration of authentication manager implementation\n     */\n    protected static final String AUTHENTICATION_MANAGER = \"auth.AuthenticationManager\";\n\n    /**\n     * Configuration of OAuth service provider name\n     */\n    protected static final String OAUTH_SERVICE_PROVIDER = \"OAuth.serviceProvider\";\n\n    /**\n     * Configuration of OAuth consumer key\n     */\n    protected static final String OAUTH_CONSUMER_KEY = \"OAuth.consumerKey\";\n\n    /**\n     * Configuration of OAuth consumer secret\n     */\n    protected static final String OAUTH_CONSUMER_SECRET = \"OAuth.consumerSecret\";\n\n    /**\n     * Configuration of OAuth callback URL\n     */\n    protected static final String OAUTH_CALLBACK_URL = \"OAuth.callbackURL\";\n\n    /**\n     * Configuration of CAS Server URL\n     */\n    protected static final String CAS_SERVER_URL = \"Cas.serverUrlPrefix\";\n\n    /**\n     * Configuration of CAS Bonita Service URL\n     */\n    protected static final String CAS_BONITA_SERVICE_URL = \"Cas.bonitaServiceURL\";\n\n    private static final Map<String, Optional<String>> authenticationProperties = new ConcurrentHashMap<>();\n\n    /**\n     * properties\n     */\n    public AuthenticationManagerProperties() {\n        super(AUTHENTICATION_CONFIG_FILE_NAME);\n    }\n\n    public static AuthenticationManagerProperties getProperties() {\n        return new AuthenticationManagerProperties();\n    }\n\n    /**\n     * @return get login manager implementation\n     */\n    public String getAuthenticationManagerImpl() {\n        return getTenantProperty(AUTHENTICATION_MANAGER);\n    }\n\n    /**\n     * @return get OAuth service provider name\n     */\n    public String getOAuthServiceProviderName() {\n        return getTenantProperty(OAUTH_SERVICE_PROVIDER);\n    }\n\n    /**\n     * @return get OAuth consumer key\n     */\n    public String getOAuthConsumerKey() {\n        return getTenantProperty(OAUTH_CONSUMER_KEY);\n    }\n\n    /**\n     * @return get OAuth consumer secret\n     */\n    public String getOAuthConsumerSecret() {\n        return getTenantProperty(OAUTH_CONSUMER_SECRET);\n    }\n\n    /**\n     * @return get OAuth callback URL\n     */\n    public String getOAuthCallbackURL() {\n        return getTenantProperty(OAUTH_CALLBACK_URL);\n    }\n\n    /**\n     * @return get OAuth callback URL\n     */\n    public String getCasServerURL() {\n        return getTenantProperty(CAS_SERVER_URL);\n    }\n\n    /**\n     * @return get OAuth callback URL\n     */\n    public String getCasBonitaServiceUrl() {\n        return getTenantProperty(CAS_BONITA_SERVICE_URL);\n    }\n\n    /**\n     * @return if properties are set up to display the logout button\n     */\n    public boolean isLogoutDisabled() {\n        return BooleanUtils.toBoolean(getTenantProperty(LOGOUT_DISABLED));\n    }\n\n    @Override\n    public String getTenantProperty(String propertyName) {\n        Optional<String> propertyValue = authenticationProperties.get(propertyName);\n        if (propertyValue == null) {\n            propertyValue = Optional.ofNullable(super.getTenantProperty(propertyName));\n            authenticationProperties.put(propertyName, propertyValue);\n        }\n        return propertyValue.orElse(null);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/auth/ConsumerNotFoundException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.auth;\n\nimport javax.servlet.ServletException;\n\n/**\n * This class cannot be removed without the risk of breaking\n * old {@link AuthenticationManager} implementations that may still use it\n *\n * @deprecated since 8.0.0\n * @author Chong Zhao\n */\n@Deprecated(since = \"8.0.0\")\npublic class ConsumerNotFoundException extends ServletException {\n\n    /**\n     * serial UID\n     */\n    private static final long serialVersionUID = -8891020375098355542L;\n\n    /**\n     * Constructor\n     */\n    public ConsumerNotFoundException() {\n        super();\n    }\n\n    /**\n     * @param message\n     * @param cause\n     */\n    public ConsumerNotFoundException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    /**\n     * @param message\n     */\n    public ConsumerNotFoundException(final String message) {\n        super(message);\n    }\n\n    /**\n     * @param cause\n     */\n    public ConsumerNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/auth/impl/jaas/ConsoleCallbackHandler.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.auth.impl.jaas;\n\nimport java.io.IOException;\n\nimport javax.security.auth.callback.Callback;\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.auth.callback.NameCallback;\nimport javax.security.auth.callback.PasswordCallback;\nimport javax.security.auth.callback.UnsupportedCallbackException;\n\n/**\n * Console call back handler\n *\n * @author Qixiang Zhang\n */\npublic class ConsoleCallbackHandler implements CallbackHandler {\n\n    /**\n     * User name\n     */\n    private final String name;\n\n    /**\n     * User password\n     */\n    private final String password;\n\n    /**\n     * Default Constructor.\n     *\n     * @param name\n     *        user name\n     * @param password\n     *        user password\n     */\n    public ConsoleCallbackHandler(final String name, final String password) {\n        this.name = name;\n        this.password = password;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see javax.security.auth.callback.CallbackHandler#handle(javax.security.auth.callback.Callback[])\n     */\n    @Override\n    public void handle(final Callback[] callbacks) throws IOException, UnsupportedCallbackException {\n        for (final Callback callback : callbacks) {\n            if (callback instanceof NameCallback nc) {\n                nc.setName(this.name);\n            } else if (callback instanceof PasswordCallback pc) {\n                pc.setPassword(this.password.toCharArray());\n            }\n\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/auth/impl/jaas/ConsoleIdentityLoginModule.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.auth.impl.jaas;\n\nimport java.security.Principal;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport javax.security.auth.Destroyable;\nimport javax.security.auth.Subject;\nimport javax.security.auth.callback.Callback;\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.auth.callback.NameCallback;\nimport javax.security.auth.callback.PasswordCallback;\nimport javax.security.auth.login.FailedLoginException;\nimport javax.security.auth.login.LoginException;\nimport javax.security.auth.spi.LoginModule;\n\nimport org.bonitasoft.console.common.server.login.credentials.LoginDatastore;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.session.APISession;\n\n/**\n * This {@link LoginModule} is used to verify a user identity against Bonita authentication service.\n *\n * @author Qixiang Zhang\n */\npublic class ConsoleIdentityLoginModule implements LoginModule {\n\n    /**\n     * User name prompt\n     */\n    private static final String NAME_PROMPT = \"Name: \";\n\n    /**\n     * User password prompt\n     */\n    private static final String PASSWORD_PROMPT = \"Password: \";\n\n    /**\n     * javax security auth login password\n     */\n    protected static final String JAVAX_SECURITY_AUTH_LOGIN_PASSWORD = \"javax.security.auth.login.password\";\n\n    /**\n     * javax security auth login name\n     */\n    protected static final String JAVAX_SECURITY_AUTH_LOGIN_NAME = \"javax.security.auth.login.name\";\n\n    /**\n     * Property key for the debug flag. Defined to be \"debug\".\n     * Property Value. If set, should be either \"true\" or \"false\". Default is\n     * \"false\".\n     */\n    public static final String DEBUG_OPTION_NAME = \"debug\";\n\n    /**\n     * The subject to be authenticated\n     */\n    private Subject subject = null;\n\n    /**\n     * A CallbackHandler for communicating with the end user (prompting\n     * for usernames and passwords, for example)\n     */\n    private CallbackHandler callbackHandler = null;\n\n    /**\n     * State shared with other configured LoginModules.\n     */\n    private Map<String, Object> sharedState;\n\n    /**\n     * Debug flag\n     */\n    private boolean debug = false;\n\n    /**\n     * Principal id\n     */\n    private String id;\n\n    /**\n     * Initialize this LoginModule. This method is called by the LoginContext\n     * after this LoginModule has been instantiated. The purpose of this method is\n     * to initialize this LoginModule with the relevant information. If this\n     * LoginModule does not understand any of the data stored in sharedState or\n     * options parameters, they can be ignored.\n     *\n     * @param subject\n     *        the Subject to be authenticated.\n     * @param callbackHandler\n     *        a CallbackHandler for communicating with the end user (prompting\n     *        for usernames and passwords, for example).\n     * @param sharedState\n     *        state shared with other configured LoginModules.\n     * @param options\n     *        options specified in the login Configuration for this particular\n     *        LoginModule.\n     */\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public void initialize(final Subject subject, final CallbackHandler callbackHandler,\n            final Map<String, ?> sharedState, final Map<String, ?> options) {\n\n        this.subject = subject;\n        this.callbackHandler = callbackHandler;\n        this.sharedState = (Map<String, Object>) sharedState;\n        final String debugFlag = (String) options.get(DEBUG_OPTION_NAME);\n        if (debugFlag != null) {\n            this.debug = Boolean.valueOf(debugFlag);\n        }\n    }\n\n    /**\n     * Method to authenticate a Subject (phase 1). The implementation of this\n     * method authenticates a Subject. For example, it may prompt for Subject\n     * information such as a username and password and then attempt to verify the\n     * password. This method saves the result of the authentication attempt as\n     * private state within the LoginModule.\n     *\n     * @return true if the authentication succeeded, or false if this LoginModule\n     *         should be ignored.\n     * @throws LoginException\n     *         if the authentication fails\n     */\n    @Override\n    public boolean login() throws LoginException {\n        if (this.debug) {\n            System.err.println(\"[\" + ConsoleIdentityLoginModule.class.getName() + \"] login() - preparing - step 1\");\n        }\n        try {\n\n            final Map<String, Object> loggingsArgs = getSharedState();\n\n            final Map<String, Callback> callbacks = getPromptCallbacks(loggingsArgs);\n\n            if (!callbacks.isEmpty()) {\n                if (this.debug) {\n                    System.err.println(\n                            \"[\" + ConsoleIdentityLoginModule.class.getName() + \"] login() - callback - step 2\");\n                }\n                this.callbackHandler.handle(callbacks.values().toArray(new Callback[0]));\n                adjustLoggingsArgs(callbacks, loggingsArgs);\n            }\n\n            if (isDebug()) {\n                System.err.println(\n                        \"[\" + ConsoleIdentityLoginModule.class.getName() + \"] login() - authenticating - step 3\");\n            }\n            final APISession aAPISession = (loggingsArgs.containsKey(JAVAX_SECURITY_AUTH_LOGIN_NAME))\n                    ? doLogin(loggingsArgs)\n                    : null;\n\n            if (isDebug()) {\n                System.err.println(\n                        \"[\" + ConsoleIdentityLoginModule.class.getName() + \"] login() - storing data - step 4\");\n            }\n\n            if (aAPISession != null) {\n                this.id = (String) getSharedState().get(JAVAX_SECURITY_AUTH_LOGIN_NAME);\n            }\n\n            if (isDebug()) {\n                System.err.println(\"[\" + ConsoleIdentityLoginModule.class.getName() + \"] login() - returning - step 5\");\n            }\n            if (this.id == null) {\n                throw new FailedLoginException(\"id is null\");\n            }\n            return true;\n        } catch (final Exception e) {\n            e.printStackTrace();\n            final LoginException le = new LoginException();\n            le.initCause(e);\n            throw le;\n        }\n    }\n\n    /**\n     * Log user in\n     *\n     * @param loggingsArgs\n     * @return\n     * @throws BonitaException\n     */\n    protected APISession doLogin(final Map<String, Object> loggingsArgs) throws BonitaException {\n        final LoginDatastore loginDatastore = new LoginDatastore();\n        return loginDatastore.login(String.valueOf(loggingsArgs.get(JAVAX_SECURITY_AUTH_LOGIN_NAME)),\n                String.valueOf(loggingsArgs.get(JAVAX_SECURITY_AUTH_LOGIN_PASSWORD)));\n    }\n\n    /**\n     * Get callback to prompt authentication to user\n     *\n     * @param loggingsArgs\n     * @return\n     */\n    protected Map<String, Callback> getPromptCallbacks(final Map<String, Object> loggingsArgs) {\n        final Map<String, Callback> callbacks = new HashMap<>();\n\n        // login\n        if (!loggingsArgs.containsKey(JAVAX_SECURITY_AUTH_LOGIN_NAME)) {\n            callbacks.put(NAME_PROMPT, new NameCallback(NAME_PROMPT));\n        }\n\n        // password\n        if (!loggingsArgs.containsKey(JAVAX_SECURITY_AUTH_LOGIN_PASSWORD)) {\n            callbacks.put(PASSWORD_PROMPT, new PasswordCallback(PASSWORD_PROMPT, false));\n        }\n\n        return callbacks;\n    }\n\n    /**\n     * Adjust loggings arguments depending on callbacks results\n     *\n     * @param callbacks\n     * @param loggingsArgs\n     */\n    protected void adjustLoggingsArgs(final Map<String, Callback> callbacks, final Map<String, Object> loggingsArgs) {\n        if (!loggingsArgs.containsKey(JAVAX_SECURITY_AUTH_LOGIN_NAME)) {\n            // update name\n            if (callbacks.get(NAME_PROMPT) instanceof NameCallback) {\n                loggingsArgs.put(JAVAX_SECURITY_AUTH_LOGIN_NAME, ((NameCallback) callbacks.get(NAME_PROMPT)).getName());\n            }\n        }\n\n        if (!loggingsArgs.containsKey(JAVAX_SECURITY_AUTH_LOGIN_PASSWORD)) {\n            // update password\n            if (callbacks.get(PASSWORD_PROMPT) instanceof PasswordCallback pwdCallback) {\n                loggingsArgs.put(JAVAX_SECURITY_AUTH_LOGIN_PASSWORD, String.valueOf(pwdCallback.getPassword()));\n                pwdCallback.clearPassword();\n            }\n        }\n    }\n\n    /**\n     * Method to commit the authentication process (phase 2). This method is\n     * called if the LoginContext's overall authentication succeeded (the relevant\n     * REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules succeeded). If\n     * this LoginModule's own authentication attempt succeeded (checked by\n     * retrieving the private state saved by the login method), then this method\n     * associates relevant Principals and Credentials with the Subject located in\n     * the LoginModule. If this LoginModule's own authentication attempted failed,\n     * then this method removes/destroys any state that was originally saved.\n     *\n     * @return true if this method succeeded, or false if this LoginModule should\n     *         be ignored.\n     * @throws LoginException\n     *         if the commit fails\n     */\n    @Override\n    public boolean commit() throws LoginException {\n        if (this.id == null) {\n            throw new FailedLoginException(\"id is null\");\n        }\n        final Set<Principal> principals = this.subject.getPrincipals();\n        principals.add(new ConsolePrincipal(this.id));\n        return true;\n    }\n\n    /**\n     * Method to abort the authentication process (phase 2). This method is called\n     * if the LoginContext's overall authentication failed. (the relevant\n     * REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules did not succeed).\n     * If this LoginModule's own authentication attempt succeeded (checked by\n     * retrieving the private state saved by the login method), then this method\n     * cleans up any state that was originally saved.\n     *\n     * @return true if this method succeeded, or false if this LoginModule should\n     *         be ignored.\n     * @throws LoginException\n     *         if the abort fails\n     */\n    @Override\n    public boolean abort() throws LoginException {\n        if (this.debug) {\n            System.err.println(\"[\" + ConsoleIdentityLoginModule.class.getName() + \"] abort()\");\n        }\n        if (this.id == null) {\n            return false;\n        }\n        this.subject = null;\n        this.id = null;\n        return true;\n    }\n\n    /**\n     * Method which logs out a Subject. An implementation of this method might\n     * remove/destroy a Subject's Principals and Credentials.\n     *\n     * @return true if this method succeeded, or false if this LoginModule should\n     *         be ignored.\n     * @throws LoginException\n     *         if the logout fails\n     */\n    @Override\n    public boolean logout() throws LoginException {\n        if (this.id != null) {\n            if (this.debug) {\n                System.err\n                        .println(\"[\" + ConsoleIdentityLoginModule.class.getName() + \"] logout() - removing principals\");\n            }\n            // Remove only principals added by our commit method\n            final Set<Principal> principals = new HashSet<>(this.subject.getPrincipals());\n            for (final Principal p : principals) {\n                if (p instanceof ConsolePrincipal) {\n                    if (this.debug) {\n                        System.err.println(\"[\" + ConsoleIdentityLoginModule.class.getName()\n                                + \"] logout() - removing principal: \" + p);\n                    }\n                    this.subject.getPrincipals().remove(p);\n                }\n            }\n            if (this.debug) {\n                System.err.println(\"[\" + ConsoleIdentityLoginModule.class.getName()\n                        + \"] logout() - destroying/removing credentials\");\n            }\n            // Remove/destroy only credentials added by our commit method\n            final Set<Object> credentials = new HashSet<>(this.subject.getPublicCredentials());\n            for (final Object o : credentials) {\n                if (o instanceof Destroyable) {\n                    if (this.debug) {\n                        System.err.println(\"[\" + ConsoleIdentityLoginModule.class.getName()\n                                + \"] logout() - destroying credential: \" + o);\n                    }\n                    // Bug: only from this module !!\n                    // ((Destroyable) o).destroy();\n                }\n                if (!this.subject.isReadOnly()) {\n                    if (this.debug) {\n                        System.err.println(\"[\" + ConsoleIdentityLoginModule.class.getName()\n                                + \"] logout() - removing credential: \" + o);\n                    }\n                    this.subject.getPublicCredentials().remove(o);\n                }\n            }\n        }\n        return true;\n    }\n\n    protected Map<String, Object> getSharedState() {\n        return sharedState;\n    }\n\n    protected boolean isDebug() {\n        return debug;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/auth/impl/jaas/ConsolePrincipal.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.auth.impl.jaas;\n\nimport java.io.Serializable;\nimport java.security.Principal;\n\n/**\n * Subject of principal\n *\n * @author Qixiang Zhang\n */\npublic class ConsolePrincipal implements Principal, Serializable {\n\n    /**\n     * Serial version uid\n     */\n    private static final long serialVersionUID = 1120874595111127685L;\n\n    /**\n     * Subject identities\n     */\n    private final String id;\n\n    /**\n     * Default Constructor.\n     *\n     * @param id\n     *        Subject identities\n     */\n    public ConsolePrincipal(final String id) {\n        this.id = id;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see java.security.Principal#getName()\n     */\n    @Override\n    public String getName() {\n        return this.id;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/auth/impl/standard/StandardAuthenticationManagerImpl.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.auth.impl.standard;\n\nimport java.io.Serializable;\nimport java.net.URLDecoder;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Collections;\nimport java.util.Map;\n\nimport javax.servlet.ServletException;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.bonitasoft.console.common.server.auth.AuthenticationFailedException;\nimport org.bonitasoft.console.common.server.auth.AuthenticationManager;\nimport org.bonitasoft.console.common.server.auth.AuthenticationManagerProperties;\nimport org.bonitasoft.console.common.server.login.HttpServletRequestAccessor;\nimport org.bonitasoft.console.common.server.login.credentials.Credentials;\nimport org.bonitasoft.console.common.server.utils.LocaleUtils;\nimport org.bonitasoft.console.common.server.utils.UrlBuilder;\nimport org.bonitasoft.engine.properties.StringProperty;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Chong Zhao\n */\npublic class StandardAuthenticationManagerImpl implements AuthenticationManager {\n\n    /**\n     * Logger\n     */\n    private static final Logger LOGGER = LoggerFactory.getLogger(StandardAuthenticationManagerImpl.class.getName());\n\n    protected String loginPageURL = null;\n\n    @Override\n    public String getLoginPageURL(final HttpServletRequestAccessor request, final String redirectURL)\n            throws ServletException {\n        final UrlBuilder loginURL = new UrlBuilder(getLoginPage(request));\n        //adds the locale to the login URL if it is set in the requested URL\n        String localeFromRequestedURL = LocaleUtils.getLocaleFromRequestURL(request.asHttpServletRequest());\n        if (localeFromRequestedURL != null) {\n            loginURL.appendParameter(LocaleUtils.PORTAL_LOCALE_PARAM, localeFromRequestedURL);\n        }\n        if (StringUtils.isNotBlank(redirectURL)) {\n            //Decodes the redirect URL if it is encoded because LoginUrl already encodes it (avoid double encoding)\n            //since this method is part of a public interface, we cannot change the calling code to pass decoded redirectURL\n            String decodedRedirectURL = URLDecoder.decode(redirectURL, StandardCharsets.UTF_8);\n            loginURL.appendParameter(AuthenticationManager.REDIRECT_URL, decodedRedirectURL);\n        }\n        return loginURL.build();\n    }\n\n    @Override\n    public Map<String, Serializable> authenticate(final HttpServletRequestAccessor requestAccessor,\n            final Credentials credentials)\n            throws AuthenticationFailedException {\n        if (LOGGER.isDebugEnabled()) {\n            LOGGER.debug(\"#authenticate (this implementation of \" + AuthenticationManager.class.getName()\n                    + \" does nothing. The subsequent engine login is enough to authenticate the user.)\");\n        }\n        return Collections.emptyMap();\n    }\n\n    @Override\n    public String getLogoutPageURL(final HttpServletRequestAccessor request, final String redirectURL)\n            throws ServletException {\n        return null;\n    }\n\n    protected String getLoginPage(HttpServletRequestAccessor requestAccessor) {\n        //the login page cannot be different from one request to another, so only compute it once\n        if (loginPageURL == null) {\n            StringProperty loginPage = new StringProperty(\"External Login URL\",\n                    AuthenticationManager.BONITA_RUNTIME_AUTHENTICATION_LOGIN_URL_VAR,\n                    getAuthenticationProperty(\n                            AuthenticationManager.BONITA_RUNTIME_AUTHENTICATION_LOGIN_URL_VAR,\n                            getDefaultLoginPage(requestAccessor)));\n            loginPageURL = loginPage.getValue();\n        }\n        return loginPageURL;\n    }\n\n    protected String getDefaultLoginPage(HttpServletRequestAccessor requestAccessor) {\n        final StringBuilder url = new StringBuilder();\n        String context = requestAccessor.asHttpServletRequest().getContextPath();\n        url.append(context).append(LOGIN_PAGE);\n        return url.toString();\n    }\n\n    protected String getAuthenticationProperty(String propertyName, String defaultValue) {\n        String propertyValue = AuthenticationManagerProperties.getProperties().getTenantProperty(propertyName);\n        return propertyValue != null ? propertyValue : defaultValue;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/filter/CacheFilter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.filter;\n\nimport java.io.IOException;\nimport java.util.Enumeration;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.FilterConfig;\nimport javax.servlet.ServletException;\nimport javax.servlet.ServletRequest;\nimport javax.servlet.ServletResponse;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\n/**\n * @author Haojie Yuan\n * @author Benjamin Parisel\n * @author Anthony Birembaut\n */\npublic class CacheFilter extends ExcludingPatternFilter {\n\n    public static final String ALWAYS_CACHING = \"alwaysCaching\";\n\n    public static final String NO_CUSTOMPAGE_CACHE = \"noCacheCustomPage\";\n\n    protected static final String CACHE_FILTER_EXCLUDED_RESOURCES_PATTERN = \"^/(bonita/)?(apps/.+/$)|(portal/resource/.+/content/$)|(portal/custom-page/.+/$)|(portal/custom-page/API/)\";\n\n    protected Map<String, String> paramMap = new HashMap<>();\n\n    private final String DURATION = \"duration\";\n\n    @Override\n    public void init(final FilterConfig filterConfig) throws ServletException {\n        super.init(filterConfig);\n        final Enumeration<?> names = filterConfig.getInitParameterNames();\n        while (names.hasMoreElements()) {\n            final String name = (String) names.nextElement();\n            final String value = filterConfig.getInitParameter(name);\n            paramMap.put(name, value);\n        }\n    }\n\n    public String getDefaultExcludedPages() {\n        return CACHE_FILTER_EXCLUDED_RESOURCES_PATTERN;\n    }\n\n    @Override\n    public void proceedWithFiltering(final ServletRequest request, final ServletResponse response,\n            final FilterChain chain) throws IOException, ServletException {\n        final HttpServletRequest req = (HttpServletRequest) request;\n        final HttpServletResponse res = (HttpServletResponse) response;\n\n        setResponseHeader(res);\n\n        chain.doFilter(req, res);\n    }\n\n    @Override\n    public void excludePatternFiltering(ServletRequest request, ServletResponse response, FilterChain chain)\n            throws IOException, ServletException {\n        final HttpServletRequest req = (HttpServletRequest) request;\n        final HttpServletResponse res = (HttpServletResponse) response;\n\n        res.setHeader(\"Cache-Control\", \"no-store, no-cache, must-revalidate, proxy-revalidate\");\n\n        chain.doFilter(req, res);\n    }\n\n    private void setResponseHeader(final HttpServletResponse response) {\n        final Integer duration = Integer.valueOf(paramMap.get(DURATION));\n        final boolean alwaysCaching = Boolean.parseBoolean(paramMap.get(ALWAYS_CACHING));\n        final boolean noCustomPageCache = Boolean.parseBoolean(System.getProperty(NO_CUSTOMPAGE_CACHE));\n        if (!noCustomPageCache || alwaysCaching) {\n            response.setHeader(\"Cache-Control\", \"max-age=\" + duration.intValue());\n        } else {\n            response.setHeader(\"Cache-Control\", \"no-store, no-cache, must-revalidate, proxy-revalidate\");\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/filter/ExcludingPatternFilter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.filter;\n\nimport java.io.IOException;\nimport java.util.regex.Pattern;\n\nimport javax.servlet.Filter;\nimport javax.servlet.FilterChain;\nimport javax.servlet.FilterConfig;\nimport javax.servlet.ServletException;\nimport javax.servlet.ServletRequest;\nimport javax.servlet.ServletResponse;\nimport javax.servlet.http.HttpServletRequest;\n\n/**\n * Base filter that supports URL exclude patterns and ensures each filter instance\n * executes at most once per request (similar to Spring's {@code OncePerRequestFilter}).\n * <p>\n * The once-per-request guard uses a request attribute named {@code \"<filter-name>.FILTERED\"}\n * (where {@code <filter-name>} is the {@code <filter-name>} declared in {@code web.xml}).\n * This allows filter-mappings to include both REQUEST and FORWARD dispatchers for\n * defense-in-depth, without the filter logic running twice when a URL-rewritten request\n * is forwarded internally (e.g. {@code /APIToolkit/*} forwarded to {@code /API/*}).\n * <p>\n * Because the attribute key is derived from the filter name (not the class name),\n * two distinct {@code <filter>} declarations of the same class (e.g. {@code CacheFilter}\n * and {@code CustomPageCacheFilter}) are tracked independently.\n *\n * @author Anthony Birembaut\n */\npublic abstract class ExcludingPatternFilter implements Filter {\n\n    protected URLExcludePattern urlExcludePattern;\n\n    /**\n     * Request attribute name used to mark that this filter instance has already\n     * been applied to the current request. Derived from the filter-name in web.xml.\n     */\n    private String alreadyFilteredAttrName;\n\n    @Override\n    public void init(FilterConfig filterConfig) throws ServletException {\n        alreadyFilteredAttrName = filterConfig.getFilterName() + \".FILTERED\";\n        urlExcludePattern = new URLExcludePattern(filterConfig, getDefaultExcludedPages());\n    }\n\n    @Override\n    public void destroy() {\n    }\n\n    @Override\n    public final void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)\n            throws IOException, ServletException {\n        // Skip if this filter instance already executed for this request (e.g. on FORWARD\n        // after the initial REQUEST dispatch). This prevents double-filtering while still\n        // allowing the filter-mapping to cover both REQUEST and FORWARD dispatchers.\n        if (request.getAttribute(alreadyFilteredAttrName) != null) {\n            chain.doFilter(request, response);\n            return;\n        }\n        request.setAttribute(alreadyFilteredAttrName, Boolean.TRUE);\n        final String url = ((HttpServletRequest) request).getRequestURL().toString();\n        if (matchExcludePatterns(url)) {\n            excludePatternFiltering(request, response, chain);\n        } else {\n            proceedWithFiltering(request, response, chain);\n        }\n    }\n\n    public void excludePatternFiltering(ServletRequest request, ServletResponse response, FilterChain chain)\n            throws IOException, ServletException {\n        chain.doFilter(request, response);\n    }\n\n    public abstract void proceedWithFiltering(ServletRequest request, ServletResponse response, FilterChain chain)\n            throws ServletException, IOException;\n\n    public abstract String getDefaultExcludedPages();\n\n    public Pattern compilePattern(final String stringPattern) {\n        return urlExcludePattern.compilePattern(stringPattern);\n    }\n\n    /**\n     * check the given url against the local url exclude pattern\n     *\n     * @param url\n     *        the url to check\n     * @return true if the url match the pattern\n     */\n    public boolean matchExcludePatterns(final String url) {\n        return urlExcludePattern.matchExcludePatterns(url);\n    }\n\n    public Pattern getExcludePattern() {\n        return urlExcludePattern.getExcludePattern();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/filter/MultiReadHttpServletRequest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.filter;\n\nimport java.io.BufferedReader;\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\n\nimport javax.servlet.ReadListener;\nimport javax.servlet.ServletInputStream;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletRequestWrapper;\n\nimport org.apache.commons.fileupload.servlet.ServletFileUpload;\nimport org.apache.commons.io.IOUtils;\nimport org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils;\n\npublic class MultiReadHttpServletRequest extends HttpServletRequestWrapper {\n\n    private File tempFile;\n    private ByteArrayOutputStream readBytes;\n    private boolean isMultipart;\n\n    public MultiReadHttpServletRequest(final HttpServletRequest request) {\n        super(request);\n        isMultipart = ServletFileUpload.isMultipartContent(request);\n    }\n\n    @Override\n    public ServletInputStream getInputStream() throws IOException {\n        if (!isMultipart && readBytes == null\n                || isMultipart && (tempFile == null || !tempFile.exists())) {\n            readInputStream();\n        }\n        return new CachedServletInputStream();\n    }\n\n    @Override\n    public BufferedReader getReader() throws IOException {\n        String enc = getCharacterEncoding();\n        if (enc == null) {\n            enc = \"UTF-8\";\n        }\n        return new BufferedReader(new InputStreamReader(getInputStream(), enc));\n    }\n\n    private void readInputStream() throws IOException {\n        if (!isMultipart) {\n            readBytes = new ByteArrayOutputStream();\n            IOUtils.copy(super.getInputStream(), readBytes);\n        } else {\n            File tempDir = WebBonitaConstantsUtils.getPlatformInstance().getTempFolder();\n            if (!tempDir.exists()) {\n                tempDir.mkdirs();\n            }\n            tempFile = File.createTempFile(\"tmp_\", \".part\", tempDir);\n            tempFile.deleteOnExit();\n            try (FileOutputStream fileOutput = new FileOutputStream(tempFile)) {\n                IOUtils.copy(super.getInputStream(), fileOutput);\n            }\n        }\n    }\n\n    public void cleanMultipartTempContent() {\n        if (tempFile != null && tempFile.exists()) {\n            tempFile.delete();\n        }\n        // clean recursively if there are several layers of wrapped requests\n        if (getRequest() instanceof MultiReadHttpServletRequest) {\n            ((MultiReadHttpServletRequest) getRequest()).cleanMultipartTempContent();\n        }\n    }\n\n    class CachedServletInputStream extends ServletInputStream {\n\n        private final InputStream input;\n        private ReadListener readListener;\n\n        public CachedServletInputStream() throws IOException {\n            if (!isMultipart) {\n                input = new ByteArrayInputStream(readBytes.toByteArray());\n            } else {\n                input = new FileInputStream(tempFile);\n            }\n            readListener = null;\n        }\n\n        @Override\n        public int read() throws IOException {\n            int nextByte = input.read();\n            if (nextByte == -1) {\n                onAllDataRead();\n            }\n            return nextByte;\n        }\n\n        @Override\n        public int read(final byte[] b) throws IOException {\n            int numberOfBytesRead = input.read(b);\n            if (numberOfBytesRead == -1) {\n                onAllDataRead();\n            }\n            return numberOfBytesRead;\n        }\n\n        @Override\n        public int read(final byte[] b, final int off, final int len) throws IOException {\n            int numberOfBytesRead = input.read(b, off, len);\n            if (numberOfBytesRead == -1) {\n                onAllDataRead();\n            }\n            return numberOfBytesRead;\n        }\n\n        @Override\n        public void close() throws IOException {\n            input.close();\n            super.close();\n        }\n\n        @Override\n        public boolean isFinished() {\n            try {\n                return input.available() == 0;\n            } catch (IOException e) {\n                // stream has been closed\n                return true;\n            }\n        }\n\n        @Override\n        public boolean isReady() {\n            return !isFinished();\n        }\n\n        @Override\n        public void setReadListener(ReadListener readListener) {\n            this.readListener = readListener;\n            if (!isFinished()) {\n                try {\n                    readListener.onDataAvailable();\n                } catch (IOException e) {\n                    readListener.onError(e);\n                }\n            } else {\n                onAllDataRead();\n            }\n        }\n\n        private void onAllDataRead() {\n            if (readListener != null) {\n                try {\n                    readListener.onAllDataRead();\n                } catch (IOException e) {\n                    readListener.onError(e);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/filter/NoCacheFilter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.filter;\n\nimport java.io.IOException;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.ServletException;\nimport javax.servlet.ServletRequest;\nimport javax.servlet.ServletResponse;\nimport javax.servlet.http.HttpServletResponse;\n\n/**\n * This Filter adds headers to the response to prevent caching of the page/resource.\n * It extends ExcludingPatternFilter, so that it benefits from its once-per-request behavior.\n *\n * @author Paul AMAR\n */\npublic class NoCacheFilter extends ExcludingPatternFilter {\n\n    @Override\n    public void proceedWithFiltering(ServletRequest request, ServletResponse response, FilterChain chain)\n            throws ServletException, IOException {\n        final HttpServletResponse res = (HttpServletResponse) response;\n        res.setHeader(\"Cache-Control\", \"no-store, no-cache, must-revalidate, proxy-revalidate\");\n        chain.doFilter(request, res);\n    }\n\n    @Override\n    public String getDefaultExcludedPages() {\n        return \"\";\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/filter/PathSanitizer.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.filter;\n\nimport java.util.regex.Pattern;\n\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * Utility class to sanitize URL paths by stripping path parameters (semicolons).\n * <p>\n * Per RFC 3986, semicolons in path segments introduce matrix parameters.\n * Some servlet containers (e.g. Tomcat) strip semicolons when resolving\n * {@code getRequestDispatcher()} paths, while {@code java.net.URI.normalize()}\n * treats them as literal characters. This differential can be exploited for\n * path traversal attacks (e.g. {@code /API/..;/..;/serverAPI}).\n * Using this class prevents such attacks by normalizing and sanitizing\n * URLs before processing them (fixes Bonita CVE-233).\n * </p>\n */\n@Slf4j\npublic final class PathSanitizer {\n\n    private static final Pattern SEMICOLON_PARAMS = Pattern.compile(\";[^/]*\");\n\n    private PathSanitizer() {\n    }\n\n    /**\n     * Strips path parameters (everything from ';' to the next '/' or end of string)\n     * from each segment of the given path.\n     * <p>\n     * Example: {@code /API/..;/..;anything/serverAPI} becomes {@code /API/../../serverAPI}\n     * </p>\n     *\n     * @param path the URL path to sanitize\n     * @return the sanitized path with path parameters removed, or {@code null} if the input is {@code null}\n     */\n    public static String stripPathParameters(String path) {\n        if (path == null || path.indexOf(';') < 0) {\n            return path;\n        }\n        String sanitized = SEMICOLON_PARAMS.matcher(path).replaceAll(\"\");\n        log.debug(\"Path parameters stripped from URL path: [{}] -> [{}]\", path, sanitized);\n        return sanitized;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/filter/RedirectFilter.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.filter;\n\nimport java.io.IOException;\n\nimport javax.servlet.*;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.StringUtils;\nimport org.bonitasoft.console.common.server.auth.AuthenticationManager;\n\n/**\n * Filter that redirects to a URL specified as a parameter in the request.\n * The URL must be relative to the current domain.\n *\n * @author Haroun EL ALAMI\n */\n@Slf4j\npublic class RedirectFilter implements Filter {\n\n    @Override\n    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)\n            throws IOException, ServletException {\n\n        final HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;\n        final HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;\n\n        String redirectUrl = httpRequest.getParameter(AuthenticationManager.REDIRECT_URL);\n        if (StringUtils.isNoneBlank(redirectUrl)) {\n            // avoid redirecting to a different domain\n            if (!redirectUrl.contains(\"//\")) {\n                httpResponse.sendRedirect(redirectUrl);\n                return;\n            }\n            // If the redirectUrl is not valid\n            log.warn(\"Invalid redirect URL: {}\", redirectUrl);\n        }\n\n        filterChain.doFilter(servletRequest, servletResponse);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/filter/RequestIdFilter.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.filter;\n\nimport static org.bonitasoft.engine.mdc.MDCConstants.CORRELATION_REQUEST_ID;\nimport static org.bonitasoft.engine.mdc.MDCConstants.REQUEST_ID;\nimport static org.bonitasoft.engine.mdc.MDCConstants.REQUEST_METHOD;\nimport static org.bonitasoft.engine.mdc.MDCConstants.REQUEST_QUERY_STRING;\nimport static org.bonitasoft.engine.mdc.MDCConstants.REQUEST_REMOTE_HOST_MDC_KEY;\nimport static org.bonitasoft.engine.mdc.MDCConstants.REQUEST_REQUEST_URI;\nimport static org.bonitasoft.engine.mdc.MDCConstants.REQUEST_REQUEST_URL;\nimport static org.bonitasoft.engine.mdc.MDCConstants.REQUEST_USER_AGENT_MDC_KEY;\nimport static org.bonitasoft.engine.mdc.MDCConstants.REQUEST_X_FORWARDED_FOR;\n\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.function.Predicate;\nimport java.util.function.Supplier;\n\nimport javax.servlet.Filter;\nimport javax.servlet.FilterChain;\nimport javax.servlet.ServletException;\nimport javax.servlet.ServletRequest;\nimport javax.servlet.ServletResponse;\nimport javax.servlet.http.HttpServletRequest;\n\nimport org.bonitasoft.console.common.server.preferences.properties.PropertiesFactory;\nimport org.bonitasoft.engine.mdc.AbstractMDC;\nimport org.bonitasoft.engine.mdc.MDCHelper;\nimport org.bonitasoft.web.toolkit.client.common.util.StringUtil;\n\npublic class RequestIdFilter implements Filter {\n\n    private String requestIdAttributeName = PropertiesFactory.getConsoleProperties().getRequestIdAttributeName();\n    private String requestIdHeaderName = PropertiesFactory.getConsoleProperties().getRequestIdHeaderName();\n    private String correlationIdAttributeName = PropertiesFactory.getConsoleProperties()\n            .getCorrelationIdAttributeName();\n    private String correlationIdHeaderName = PropertiesFactory.getConsoleProperties().getCorrelationIdHeaderName();\n\n    /**\n     * Check if the current request has a requestId and a correlationId.\n     * If no requestId is found, tries and finds in header or generates a new requestId and attach it to the request.\n     */\n    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)\n            throws IOException, ServletException {\n        // get request id already attached\n        var attachedReqIdAtt = Optional.ofNullable((String) request.getAttribute(requestIdAttributeName));\n        var requestId = attachedReqIdAtt.orElseGet(() -> {\n            // look in header\n            String headerRequestId = null;\n            if (request instanceof HttpServletRequest httpRequest) {\n                headerRequestId = httpRequest.getHeader(requestIdHeaderName);\n            }\n            var id = Optional.ofNullable(headerRequestId).orElseGet(() -> {\n                // generate a new request id\n                return Long.toHexString(System.nanoTime());\n            });\n            // attach it\n            request.setAttribute(requestIdAttributeName, id);\n            return id;\n        });\n\n        // find correlation id\n        var attachedCorrelIdAtt = Optional.ofNullable((String) request.getAttribute(correlationIdAttributeName));\n        var correlationId = attachedCorrelIdAtt.or(() -> {\n            // look in header\n            String headerCorrelId = null;\n            if (request instanceof HttpServletRequest httpRequest) {\n                headerCorrelId = httpRequest.getHeader(correlationIdHeaderName);\n            }\n            var id = Optional.ofNullable(headerCorrelId);\n            // attach it\n            id.ifPresent(i -> request.setAttribute(correlationIdAttributeName, i));\n            return id;\n        });\n\n        Supplier<AbstractMDC> mdc = () -> new AbstractMDC(buildContextMap(requestId, correlationId, request)) {\n        };\n\n        MDCHelper.CheckedRunnable2<IOException, ServletException> call = () -> {\n            chain.doFilter(request, response);\n        };\n\n        MDCHelper.tryWithMDC(mdc, call);\n\n    }\n\n    private static Map<String, String> buildContextMap(String requestId,\n            Optional<String> correlationId, ServletRequest request) {\n        Map<String, String> map = new HashMap<String, String>(9);\n        // insert request id and correlation id\n        map.put(REQUEST_ID, Objects.requireNonNull(requestId));\n        Predicate<String> isBlank = StringUtil::isBlank;\n        correlationId.filter(isBlank.negate())\n                .ifPresent(id -> map.put(CORRELATION_REQUEST_ID, Objects.requireNonNull(id)));\n        // insert extra information similar to ch.qos.logback.classic.helpers.MDCInsertingServletFilter\n        map.put(REQUEST_REMOTE_HOST_MDC_KEY, request.getRemoteHost());\n        if (request instanceof HttpServletRequest http) {\n            map.put(REQUEST_REQUEST_URI, http.getRequestURI());\n            Optional.ofNullable(http.getRequestURL()).ifPresent(url -> map.put(REQUEST_REQUEST_URL, url.toString()));\n            map.put(REQUEST_METHOD, http.getMethod());\n            Optional.ofNullable(http.getQueryString()).ifPresent(str -> map.put(REQUEST_QUERY_STRING, str));\n            Optional.ofNullable(http.getHeader(\"User-Agent\"))\n                    .ifPresent(agent -> map.put(REQUEST_USER_AGENT_MDC_KEY, agent));\n            Optional.ofNullable(http.getHeader(\"X-Forwarded-For\"))\n                    .ifPresent(forwarded -> map.put(REQUEST_X_FORWARDED_FOR, forwarded));\n        }\n        return map;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/filter/SanitizerFilter.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.filter;\n\nimport static java.lang.String.format;\n\nimport java.io.BufferedReader;\nimport java.io.ByteArrayInputStream;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.function.Consumer;\nimport java.util.stream.Stream;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.ReadListener;\nimport javax.servlet.ServletException;\nimport javax.servlet.ServletInputStream;\nimport javax.servlet.ServletRequest;\nimport javax.servlet.ServletResponse;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletRequestWrapper;\n\nimport com.fasterxml.jackson.databind.JsonNode;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.node.ArrayNode;\nimport com.fasterxml.jackson.databind.node.ObjectNode;\nimport com.fasterxml.jackson.databind.node.TextNode;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.io.IOUtils;\nimport org.bonitasoft.console.common.server.preferences.properties.PropertiesFactory;\nimport org.owasp.html.HtmlChangeListener;\nimport org.owasp.html.HtmlPolicyBuilder;\nimport org.owasp.html.PolicyFactory;\nimport org.owasp.html.Sanitizers;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.util.HtmlUtils;\n\n/**\n * This class is used to filter malicious payload (e.g. XSS injection) by\n * neutralizing the injected code.\n *\n * @author Vincent Hemery\n */\n@Slf4j\npublic class SanitizerFilter extends ExcludingPatternFilter {\n\n    private static final HtmlPolicyBuilder CUSTOM_POLICY = new HtmlPolicyBuilder().allowElements(\"a\", \"pre\")\n            .allowStandardUrlProtocols()\n            .allowAttributes(\"href\").onElements(\"a\").requireRelsOnLinks(\"noopener\", \"noreferrer\", \"nofollow\");\n\n    /**\n     * Sanitizer to apply to values.\n     */\n    private static final PolicyFactory sanitizer = Sanitizers.BLOCKS.and(Sanitizers.FORMATTING).and(Sanitizers.STYLES)\n            .and(Sanitizers.IMAGES).and(Sanitizers.TABLES).and(CUSTOM_POLICY.toFactory());\n\n    /**\n     * The HTTP methods concerned by this filter.\n     */\n    private static final String[] CONCERNED_METHODS = { \"POST\", \"PUT\", \"PATCH\" };\n\n    /**\n     * Json object mapper\n     */\n    private final ObjectMapper mapper = new ObjectMapper();\n\n    private final boolean isEnabled = PropertiesFactory.getSecurityProperties().isSanitizerProtectionEnabled();\n\n    private final List<String> attributesExcluded = PropertiesFactory.getSecurityProperties()\n            .getAttributeExcludedFromSanitizerProtection();\n\n    private final HtmlChangeListener<Object> changeListener = new HtmlChangeListener<>() {\n\n        @Override\n        public void discardedTag(Object context, String elementName) {\n            if (log.isDebugEnabled()) {\n                log.debug(format(\"Tag '%s' has been discarded\", elementName));\n            }\n        }\n\n        @Override\n        public void discardedAttributes(Object context, String elementName, String... attributeNames) {\n            if (log.isDebugEnabled()) {\n                log.debug(format(\"Tag '%s' has been cleaned from the following attributes: %s\", elementName,\n                        String.join(\", \", attributeNames)));\n            }\n        }\n    };\n\n    @Override\n    public void destroy() {\n    }\n\n    @Override\n    public String getDefaultExcludedPages() {\n        // excludes nothing for now\n        return \"\";\n    }\n\n    @Override\n    public void proceedWithFiltering(ServletRequest request, ServletResponse response, FilterChain chain)\n            throws ServletException, IOException {\n        final HttpServletRequest req = (HttpServletRequest) request;\n        var method = req.getMethod();\n        if (!isSanitizerEnabled() || method == null\n                || Stream.of(CONCERNED_METHODS).noneMatch(method::equalsIgnoreCase)) {\n            // no body to sanitize, just skip this filter\n            chain.doFilter(req, response);\n            return;\n        }\n        // get body of request as Json\n        var body = getJsonBody(req);\n        if (body == null) {\n            // request has no body\n            chain.doFilter(req, response);\n            return;\n        }\n        // sanitize body\n        final var sanitized = sanitize(body);\n        if (sanitized.isEmpty() && req instanceof MultiReadHttpServletRequest) {\n            // Body was not altered and request is a MultiReadHttpServletRequest\n            // (supports re-reading): pass the original request through unchanged\n            chain.doFilter(req, response);\n            return;\n        }\n        // Wrap the request with body bytes when either:\n        // - the body was sanitized (must forward sanitized content, even if the request is\n        //   already a MultiReadHttpServletRequest from an earlier filter), or\n        // - the request is a plain HttpServletRequest (input stream was consumed by\n        //   getJsonBody and cannot be re-read, so we must restore it).\n        byte[] saneBodyBytes = mapper.writeValueAsBytes(sanitized.orElse(body));\n        final var wrapper = new HttpServletRequestWrapper(req) {\n\n            private ServletInputStream inputStream = null;\n\n            @Override\n            public ServletInputStream getInputStream() throws IOException {\n                if (inputStream == null) {\n                    final ByteArrayInputStream is = new ByteArrayInputStream(saneBodyBytes);\n                    inputStream = new ServletInputStream() {\n\n                        @Override\n                        public int read() throws IOException {\n                            return is.read();\n                        }\n\n                        @Override\n                        public boolean isFinished() {\n                            return is.available() == 0;\n                        }\n\n                        @Override\n                        public boolean isReady() {\n                            return !isFinished();\n                        }\n\n                        @Override\n                        public void setReadListener(ReadListener readListener) {\n                            throw new UnsupportedOperationException(\n                                    \"Unimplemented method 'setReadListener'\");\n                        }\n                    };\n                }\n                return inputStream;\n            }\n\n            @Override\n            public int getContentLength() {\n                return saneBodyBytes.length;\n            }\n\n            @Override\n            public long getContentLengthLong() {\n                return saneBodyBytes.length;\n            }\n\n            @Override\n            public BufferedReader getReader() throws IOException {\n                String enc = Optional.ofNullable(getCharacterEncoding()).orElse(\"UTF-8\");\n                return new BufferedReader(new InputStreamReader(getInputStream(), enc));\n            }\n\n        };\n        chain.doFilter(wrapper, response);\n    }\n\n    /**\n     * Sanitize the given JsonNode.\n     *\n     * @param node Json node to sanitize\n     * @return the sanitized Json node if it has effectively changed\n     */\n    protected Optional<JsonNode> sanitize(JsonNode node) {\n        if (node == null) {\n            return Optional.empty();\n        } else if (node.isObject()) {\n            AtomicBoolean changed = new AtomicBoolean(false);\n            ObjectNode object = (ObjectNode) node;\n            List<Runnable> operationsToPerformAfterIteration = new ArrayList<>();\n            object.fields().forEachRemaining(entry -> {\n                var key = entry.getKey();\n                var newKey = sanitizeValueAndPerformAction(key, s -> {\n                    // can't remove key while iterating, or we get a ConcurrentModificationException\n                    operationsToPerformAfterIteration.add(() -> {\n                        object.remove(key);\n                        object.set(s, entry.getValue());\n                    });\n                    changed.set(true);\n                }).orElse(key);\n\n                if (getAttributesExcluded() == null || !getAttributesExcluded().contains(key)) {\n                    var value = entry.getValue();\n                    sanitize(value).ifPresent(v -> {\n                        object.set(newKey, v);\n                        changed.set(true);\n                    });\n                }\n            });\n            operationsToPerformAfterIteration.forEach(Runnable::run);\n            return changed.get() ? Optional.of(object) : Optional.empty();\n        } else if (node.isArray()) {\n            AtomicBoolean changed = new AtomicBoolean(false);\n            ArrayNode array = (ArrayNode) node;\n            for (int i = 0; i < array.size(); i++) {\n                var value = array.get(i);\n                final int index = i;\n                sanitize(value).ifPresent(v -> {\n                    array.set(index, v);\n                    changed.set(true);\n                });\n            }\n            return changed.get() ? Optional.of(array) : Optional.empty();\n        } else if (node.isValueNode()) {\n            if (node.isBoolean() || node.isNumber() || node.isPojo() || StringUtils.isEmpty(node.textValue())) {\n                // that's safe\n                return Optional.empty();\n            }\n            var changedValue = sanitizeValueAndPerformAction(node.textValue(), v -> {\n            });\n            return changedValue.map(TextNode::new);\n        }\n        return Optional.empty();\n    }\n\n    private JsonNode getJsonBody(HttpServletRequest request) throws ServletException {\n        if (request.getContentType() != null && request.getContentType().toLowerCase().startsWith(\"application/json\")) {\n            try (ServletInputStream inputStream = request.getInputStream()) {\n                if (inputStream == null) {\n                    return null;\n                }\n                String characterEncoding = Optional.ofNullable(request.getCharacterEncoding()).orElse(\"UTF-8\");\n                var stringBody = IOUtils.toString(inputStream, characterEncoding);\n                if (!stringBody.isBlank()) {\n                    return mapper.readTree(stringBody);\n                }\n            } catch (IOException e) {\n                throw new ServletException(e);\n            }\n        }\n        return null;\n    }\n\n    /**\n     * Sanitize the value and perform the action only when value has changed\n     *\n     * @param value String value to sanitize\n     * @param action action to perform when value has changed\n     * @return the sanitized value if it has changed\n     */\n    private Optional<String> sanitizeValueAndPerformAction(String value, Consumer<String> action) {\n        /*\n         * Sanitize the value.\n         * It's not just about applying the sanitizer...\n         * We want the value to contain unescaped characters, but no script.\n         * To avoid values with multiple escaping, we unescape until the value does not\n         * change.\n         * Then we apply the sanitizer.\n         * And finally, we unescape once again to get values that the frontend can\n         * easily handle.\n         */\n        var previous = value;\n        var unescaped = HtmlUtils.htmlUnescape(previous);\n        while (!unescaped.equals(previous)) {\n            previous = unescaped;\n            unescaped = HtmlUtils.htmlUnescape(previous);\n        }\n        var sanitized = sanitizer.sanitize(unescaped, changeListener, null);\n        sanitized = HtmlUtils.htmlUnescape(sanitized);\n        // check whether value has effectively changed before doing anything\n        if (!sanitized.equals(value)) {\n            log.warn(\"Incoming HTML content has been altered. More details at debug level\");\n            action.accept(sanitized);\n            return Optional.of(sanitized);\n        }\n        return Optional.empty();\n    }\n\n    public List<String> getAttributesExcluded() {\n        return attributesExcluded;\n    }\n\n    public boolean isSanitizerEnabled() {\n        return isEnabled;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/filter/URLExcludePattern.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.filter;\n\nimport java.net.URI;\nimport java.util.regex.Pattern;\n\nimport javax.servlet.FilterConfig;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.util.UriUtils;\n\npublic class URLExcludePattern {\n\n    /**\n     * the Pattern of url not to filter\n     */\n    public final Pattern excludePattern;\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(URLExcludePattern.class.getName());\n\n    public URLExcludePattern(final FilterConfig filterConfig, String defaultExcludePattern) {\n        final String contextPath = filterConfig.getServletContext().getContextPath();\n        String processedDefaultExcludePattern;\n        if (contextPath.length() > 0 && !contextPath.equals(\"/bonita\")) {\n            String webappName = contextPath.substring(1);\n            processedDefaultExcludePattern = defaultExcludePattern.replace(\"bonita\", webappName);\n        } else {\n            processedDefaultExcludePattern = defaultExcludePattern;\n        }\n        final String configExcludePattern = filterConfig.getInitParameter(\"excludePattern\");\n        excludePattern = compilePattern(\n                StringUtils.defaultString(configExcludePattern, processedDefaultExcludePattern));\n    }\n\n    protected Pattern compilePattern(final String stringPattern) {\n        if (StringUtils.isNotBlank(stringPattern)) {\n            try {\n                return Pattern.compile(stringPattern);\n            } catch (final Exception e) {\n                LOGGER.error(\"impossible to create pattern from [ {} ]  : \", stringPattern, e);\n            }\n        }\n        return null;\n    }\n\n    /**\n     * check the given url against the local url exclude pattern\n     *\n     * @param url the url to check\n     * @return true if the url match the pattern\n     */\n    public boolean matchExcludePatterns(final String url) {\n        if (getExcludePattern() == null) {\n            return false;\n        }\n        try {\n            // URL-decode then strip path parameters (semicolons) to handle both\n            // literal ';' and percent-encoded '%3b' before normalization, preventing\n            // ..;-based traversal from fooling the exclude pattern check.\n            // URI handles both absolute (http://host/path) and relative (/path) references.\n            URI uri = new URI(url);\n            String rawPath = uri.getRawPath();\n            if (rawPath == null) {\n                rawPath = url;\n            }\n            String decodedPath = java.net.URLDecoder.decode(rawPath, \"UTF-8\");\n            String sanitizedPath = PathSanitizer.stripPathParameters(decodedPath);\n            // Re-encode the sanitized path before creating the URI for normalization,\n            // because URL-decoding may have introduced literal spaces (from %20) that\n            // are illegal in URI syntax per RFC 3986.\n            String normalizedPath = new URI(UriUtils.encodePath(sanitizedPath, \"UTF-8\"))\n                    .normalize().getPath();\n            boolean isExcluded = getExcludePattern().matcher(sanitizedPath).find()\n                    && getExcludePattern().matcher(normalizedPath).find();\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(\"Exclude pattern {} with this url: {}\",\n                        isExcluded ? \"match\" : \"does not match\", url);\n            }\n            return isExcluded;\n        } catch (final Exception e) {\n            LOGGER.warn(\"impossible to get URL from given input [{}]: {}\", url, e);\n            return false;\n        }\n    }\n\n    public Pattern getExcludePattern() {\n        return excludePattern;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/form/ProcessFormService.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.form;\n\nimport java.io.UnsupportedEncodingException;\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceNotFoundException;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.APISession;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class ProcessFormService {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(ProcessFormService.class.getName());\n\n    public String getProcessPath(final APISession apiSession, final long processDefinitionId)\n            throws BonitaException, UnsupportedEncodingException {\n        final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI(apiSession)\n                .getProcessDeploymentInfo(processDefinitionId);\n        return encodePathSegment(processDeploymentInfo.getName())\n                + \"/\"\n                + encodePathSegment(processDeploymentInfo.getVersion());\n    }\n\n    public String encodePathSegment(final String stringToEncode) throws UnsupportedEncodingException {\n        //URLEncoder#encode encodes spaces to '+' but we want '%20' instead in the path part of the URL\n        // '/' gets encoded to %2F whereas is should be left as it is since it in perfectly safe in the path part of the URL\n        // '+' gets encoded to %2B whereas is should be left as it is since it in perfectly safe in the path part of the URL\n        return URLEncoder.encode(stringToEncode, StandardCharsets.UTF_8).replaceAll(\"\\\\+\", \"%20\").replaceAll(\"%2F\", \"/\")\n                .replaceAll(\"%2B\", \"+\");\n    }\n\n    public long getProcessDefinitionId(final APISession apiSession, final String processName,\n            final String processVersion)\n            throws BonitaException {\n        if (processName != null && processVersion != null) {\n            try {\n                return getProcessAPI(apiSession).getProcessDefinitionId(processName, processVersion);\n            } catch (final ProcessDefinitionNotFoundException e) {\n                if (LOGGER.isDebugEnabled()) {\n                    LOGGER.debug(\"Wrong parameters for process name and version\", e);\n                }\n            }\n        }\n        return -1L;\n    }\n\n    public long getTaskInstanceId(final APISession apiSession, final long processInstanceId, final String taskName,\n            final long userId)\n            throws BonitaException {\n        final ProcessAPI processAPI = getProcessAPI(apiSession);\n        long ensuredUserId;\n        if (userId != -1L) {\n            ensuredUserId = userId;\n        } else {\n            ensuredUserId = apiSession.getUserId();\n        }\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 1);\n        searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.PARENT_CONTAINER_ID, processInstanceId);\n        searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.NAME, taskName);\n        final SearchResult<HumanTaskInstance> searchMyAvailableHumanTasks = processAPI.searchMyAvailableHumanTasks(\n                ensuredUserId,\n                searchOptionsBuilder.done());\n        if (searchMyAvailableHumanTasks.getCount() > 0) {\n            return searchMyAvailableHumanTasks.getResult().get(0).getId();\n        } else {\n            final SearchOptionsBuilder archivedSearchOptionsBuilder = new SearchOptionsBuilder(0, 1);\n            archivedSearchOptionsBuilder.filter(ArchivedHumanTaskInstanceSearchDescriptor.ASSIGNEE_ID, ensuredUserId);\n            archivedSearchOptionsBuilder.filter(ArchivedHumanTaskInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID,\n                    processInstanceId);\n            archivedSearchOptionsBuilder.filter(ArchivedHumanTaskInstanceSearchDescriptor.NAME, taskName);\n            final SearchResult<ArchivedHumanTaskInstance> searchArchivedHumanTasks = processAPI\n                    .searchArchivedHumanTasks(archivedSearchOptionsBuilder\n                            .done());\n            if (searchArchivedHumanTasks.getCount() > 0) {\n                return searchArchivedHumanTasks.getResult().get(0).getSourceObjectId();\n            } else {\n                if (LOGGER.isDebugEnabled()) {\n                    LOGGER.debug(\"Could not find task available with name \" + taskName + \" for process instance \"\n                            + processInstanceId);\n                }\n                throw new ActivityInstanceNotFoundException(-1L);\n            }\n        }\n    }\n\n    public String getTaskName(final APISession apiSession, final long taskInstanceId) throws BonitaException {\n        if (taskInstanceId != -1L) {\n            final ProcessAPI processAPI = getProcessAPI(apiSession);\n            try {\n                final ActivityInstance activity = processAPI.getActivityInstance(taskInstanceId);\n                return activity.getName();\n            } catch (final ActivityInstanceNotFoundException e) {\n                final ArchivedActivityInstance activity = processAPI.getArchivedActivityInstance(taskInstanceId);\n                return activity.getName();\n            }\n        }\n        return null;\n    }\n\n    public long ensureProcessDefinitionId(final APISession apiSession, final long processDefinitionId,\n            final long processInstanceId, final long taskInstanceId)\n            throws BonitaException {\n        if (processDefinitionId != -1L) {\n            return processDefinitionId;\n        } else if (processInstanceId != -1L) {\n            return getProcessDefinitionIdFromProcessInstanceId(apiSession, processInstanceId);\n        } else {\n            return getProcessDefinitionIdFromTaskId(apiSession, taskInstanceId);\n        }\n    }\n\n    protected long getProcessDefinitionIdFromTaskId(final APISession apiSession, final long taskInstanceId)\n            throws BonitaHomeNotSetException,\n            ServerAPIException, UnknownAPITypeException, ActivityInstanceNotFoundException {\n        final ProcessAPI processAPI = getProcessAPI(apiSession);\n        try {\n            final ActivityInstance activity = processAPI.getActivityInstance(taskInstanceId);\n            return activity.getProcessDefinitionId();\n        } catch (final ActivityInstanceNotFoundException e) {\n            final ArchivedActivityInstance activity = processAPI.getArchivedActivityInstance(taskInstanceId);\n            return activity.getProcessDefinitionId();\n        }\n    }\n\n    protected long getProcessDefinitionIdFromProcessInstanceId(final APISession apiSession,\n            final long processInstanceId) throws BonitaHomeNotSetException,\n            ServerAPIException, UnknownAPITypeException, ArchivedProcessInstanceNotFoundException {\n        final ProcessAPI processAPI = getProcessAPI(apiSession);\n        try {\n            final ProcessInstance processInstance = processAPI.getProcessInstance(processInstanceId);\n            return processInstance.getProcessDefinitionId();\n        } catch (final ProcessInstanceNotFoundException e) {\n            final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 1);\n            searchOptionsBuilder.filter(ArchivedProcessInstancesSearchDescriptor.SOURCE_OBJECT_ID, processInstanceId);\n            searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.ARCHIVE_DATE, Order.ASC);\n            SearchResult<ArchivedProcessInstance> searchArchivedProcessInstances = null;\n            try {\n                searchArchivedProcessInstances = processAPI\n                        .searchArchivedProcessInstancesInAllStates(searchOptionsBuilder.done());\n            } catch (final SearchException se) {\n                throw new ArchivedProcessInstanceNotFoundException(se);\n            }\n            if (searchArchivedProcessInstances != null && searchArchivedProcessInstances.getCount() > 0) {\n                return searchArchivedProcessInstances.getResult().get(0).getProcessDefinitionId();\n            } else {\n                throw new ArchivedProcessInstanceNotFoundException(processInstanceId);\n            }\n        }\n    }\n\n    protected ProcessAPI getProcessAPI(final APISession apiSession)\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return TenantAPIAccessor.getProcessAPI(apiSession);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/form/ProcessFormServlet.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.form;\n\nimport java.io.IOException;\nimport java.net.URLDecoder;\nimport java.nio.charset.StandardCharsets;\nimport java.util.List;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.console.common.server.page.PageServlet;\nimport org.bonitasoft.console.common.server.page.ResourceRenderer;\nimport org.bonitasoft.console.common.server.utils.SessionUtil;\nimport org.bonitasoft.console.common.server.utils.UrlBuilder;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Servlet allowing to display a form for a process or a task\n * The servlet only redirect to the generic Page servlet with the right URL\n *\n * @author Anthony Birembaut\n */\npublic class ProcessFormServlet extends HttpServlet {\n\n    /**\n     * UUID\n     */\n    private static final long serialVersionUID = -6397856355139281873L;\n\n    /**\n     * Logger\n     */\n    private static final Logger LOGGER = LoggerFactory.getLogger(ProcessFormServlet.class.getName());\n\n    private static final String PAGE_SERVLET_MAPPING = \"/portal/resource/\";\n\n    private static final String PROCESS_PATH_SEGMENT = \"process\";\n\n    private static final String PROCESS_INSTANCE_PATH_SEGMENT = \"processInstance\";\n\n    private static final String TASK_INSTANCE_PATH_SEGMENT = \"taskInstance\";\n\n    private static final String TASK_PATH_SEGMENT = \"task\";\n\n    private static final String USER_ID_PARAM = \"user\";\n\n    private static final String ID_PARAM = \"id\";\n\n    protected ProcessFormService processFormService = new ProcessFormService();\n\n    private final ResourceRenderer resourceRenderer = new ResourceRenderer();\n\n    @Override\n    protected void doGet(final HttpServletRequest request, final HttpServletResponse response)\n            throws ServletException, IOException {\n        long processDefinitionId = -1L;\n        long processInstanceId = -1L;\n        long taskInstanceId = -1L;\n        String taskName = null;\n        final List<String> pathSegments = resourceRenderer.getPathSegments(request.getPathInfo());\n        final String user = request.getParameter(USER_ID_PARAM);\n        final long userId = convertToLong(USER_ID_PARAM, user);\n        final HttpSession session = request.getSession();\n        final APISession apiSession = (APISession) session.getAttribute(SessionUtil.API_SESSION_PARAM_KEY);\n        try {\n            if (pathSegments.size() > 1) {\n                taskInstanceId = getTaskInstanceId(apiSession, pathSegments, userId);\n                processInstanceId = getProcessInstanceId(pathSegments);\n                processDefinitionId = getProcessDefinitionId(apiSession, pathSegments);\n            }\n            if (processDefinitionId == -1L && processInstanceId == -1L && taskInstanceId == -1L) {\n                response.sendError(HttpServletResponse.SC_BAD_REQUEST,\n                        \"Either process name and version are required or process instance Id (with or without task name) or task instance Id.\");\n                return;\n            }\n            processDefinitionId = processFormService.ensureProcessDefinitionId(apiSession, processDefinitionId,\n                    processInstanceId, taskInstanceId);\n            taskName = processFormService.getTaskName(apiSession, taskInstanceId);\n            redirectToPageServlet(request, response, apiSession, processDefinitionId, processInstanceId, taskInstanceId,\n                    taskName);\n        } catch (final Exception e) {\n            handleException(response, processDefinitionId, taskName, processInstanceId != -1L, e);\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected void redirectToPageServlet(final HttpServletRequest request, final HttpServletResponse response,\n            final APISession apiSession,\n            final long processDefinitionId, final long processInstanceId, final long taskInstanceId,\n            final String taskName) throws BonitaException, IOException {\n        final String pageServletURL = buildPageServletURL(request, apiSession, processDefinitionId, processInstanceId,\n                taskInstanceId, taskName);\n        final UrlBuilder urlBuilder = new UrlBuilder(pageServletURL);\n        urlBuilder.appendParameters(request.getParameterMap());\n        response.sendRedirect(response.encodeRedirectURL(urlBuilder.build()));\n    }\n\n    protected String buildPageServletURL(final HttpServletRequest request, final APISession apiSession,\n            final long processDefinitionId,\n            final long processInstanceId, final long taskInstanceId, final String taskName)\n            throws BonitaException, IOException {\n        final StringBuilder pageServletURL = new StringBuilder(request.getContextPath());\n        pageServletURL.append(PAGE_SERVLET_MAPPING);\n        if (taskInstanceId != -1L) {\n            pageServletURL.append(TASK_INSTANCE_PATH_SEGMENT)\n                    .append(\"/\")\n                    .append(processFormService.getProcessPath(apiSession, processDefinitionId))\n                    .append(\"/\")\n                    .append(processFormService.encodePathSegment(taskName))\n                    .append(PageServlet.RESOURCE_PATH_SEPARATOR)\n                    .append(\"/?\")\n                    .append(ID_PARAM)\n                    .append(\"=\")\n                    .append(taskInstanceId);\n        } else if (processInstanceId != -1L) {\n            pageServletURL.append(PROCESS_INSTANCE_PATH_SEGMENT)\n                    .append(\"/\")\n                    .append(processFormService.getProcessPath(apiSession, processDefinitionId))\n                    .append(PageServlet.RESOURCE_PATH_SEPARATOR)\n                    .append(\"/?\")\n                    .append(ID_PARAM)\n                    .append(\"=\")\n                    .append(processInstanceId);\n        } else {\n            pageServletURL.append(PROCESS_PATH_SEGMENT)\n                    .append(\"/\")\n                    .append(processFormService.getProcessPath(apiSession, processDefinitionId))\n                    .append(PageServlet.RESOURCE_PATH_SEPARATOR)\n                    .append(\"/?\")\n                    .append(ID_PARAM)\n                    .append(\"=\")\n                    .append(processDefinitionId);\n        }\n        return pageServletURL.toString();\n    }\n\n    protected long getProcessInstanceId(final List<String> pathSegments) {\n        long processInstanceId = -1L;\n        if (PROCESS_INSTANCE_PATH_SEGMENT.equals(pathSegments.get(0))) {\n            final String processInstance = pathSegments.get(1);\n            processInstanceId = convertToLong(PROCESS_INSTANCE_PATH_SEGMENT, processInstance);\n        }\n        return processInstanceId;\n    }\n\n    protected long getTaskInstanceId(final APISession apiSession, final List<String> pathSegments, final long userId)\n            throws BonitaException {\n        if (TASK_INSTANCE_PATH_SEGMENT.equals(pathSegments.get(0))) {\n            final String taskInstance = pathSegments.get(1);\n            return convertToLong(TASK_INSTANCE_PATH_SEGMENT, taskInstance);\n        } else if (PROCESS_INSTANCE_PATH_SEGMENT.equals(pathSegments.get(0))) {\n            final String processInstance = pathSegments.get(1);\n            final long processInstanceId = convertToLong(PROCESS_INSTANCE_PATH_SEGMENT, processInstance);\n            if (pathSegments.size() > 2 && TASK_PATH_SEGMENT.equals(pathSegments.get(2))) {\n                final String taskName = URLDecoder.decode(pathSegments.get(3), StandardCharsets.UTF_8);\n                return processFormService.getTaskInstanceId(apiSession, processInstanceId, taskName, userId);\n            }\n        }\n        return -1L;\n\n    }\n\n    protected long getProcessDefinitionId(final APISession apiSession, final List<String> pathSegments)\n            throws BonitaException {\n        long processDefinitionId = -1L;\n        if (PROCESS_PATH_SEGMENT.equals(pathSegments.get(0))) {\n            if (pathSegments.size() > 2) {\n                final String processName = pathSegments.get(1);\n                final String processVersion = pathSegments.get(2);\n                processDefinitionId = processFormService.getProcessDefinitionId(apiSession, processName,\n                        processVersion);\n            }\n        }\n        return processDefinitionId;\n    }\n\n    protected long convertToLong(final String parameterName, final String idAsString) {\n        if (idAsString != null) {\n            try {\n                return Long.parseLong(idAsString);\n            } catch (final NumberFormatException e) {\n                if (LOGGER.isInfoEnabled()) {\n                    LOGGER.info(\"Wrong value for \" + parameterName + \" expecting a number (long value)\");\n                }\n            }\n        }\n        return -1;\n    }\n\n    protected void handleException(final HttpServletResponse response, final long processDefinitionId,\n            final String taskName,\n            final boolean hasProcessInstanceId, final Exception e)\n            throws ServletException {\n        try {\n            if (e instanceof ProcessDefinitionNotFoundException) {\n                response.sendError(HttpServletResponse.SC_NOT_FOUND, \"Cannot find the process\");\n            } else if (e instanceof ArchivedProcessInstanceNotFoundException) {\n                response.sendError(HttpServletResponse.SC_NOT_FOUND, \"Cannot find the process instance\");\n            } else if (e instanceof ActivityInstanceNotFoundException) {\n                response.sendError(HttpServletResponse.SC_NOT_FOUND, \"Cannot find the task instance\");\n            } else {\n                if (LOGGER.isWarnEnabled()) {\n                    String message = \"Error while trying to display a form\";\n                    if (processDefinitionId != -1) {\n                        message = message + \" for process \" + processDefinitionId;\n                    }\n                    if (taskName != null) {\n                        message = message + \" for task \" + taskName;\n                    } else if (hasProcessInstanceId) {\n                        message = message + \" ( instance overview)\";\n                    }\n                    LOGGER.warn(message, e);\n                }\n                response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());\n            }\n        } catch (final IOException ioe) {\n            throw new ServletException(ioe);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/i18n/FileUtils.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.i18n;\n\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * @author Vincent Elcrin\n */\npublic class FileUtils {\n\n    public static List<File> getMatchingFiles(final String regex, List<File> files) {\n        List<File> locales = new ArrayList<>();\n        for (File file : files) {\n            if (isFileMatching(file, regex)) {\n                locales.add(file);\n            }\n        }\n        return locales;\n    }\n\n    private static boolean isFileMatching(File file, String regex) {\n        return file.isFile() && file.getName().matches(regex);\n    }\n\n    public static List<File> listDir(File dir) {\n        File[] files = dir.listFiles();\n        if (files == null) {\n            files = new File[0];\n        }\n        return Arrays.asList(files);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/i18n/I18n.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.i18n;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.TreeMap;\n\nimport org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n;\nimport org.bonitasoft.web.toolkit.client.common.texttemplate.Arg;\nimport org.bonitasoft.web.toolkit.client.common.texttemplate.TextTemplate;\nimport org.springframework.core.io.Resource;\nimport org.springframework.core.io.support.PathMatchingResourcePatternResolver;\nimport org.springframework.core.io.support.ResourcePatternResolver;\n\n/**\n * @author Paul AMAR, Vincent Elcrin\n */\npublic class I18n extends AbstractI18n {\n\n    /**\n     * Property that can be set to override the default i18n *.po files, or to provide translations for new languages.\n     * Points to a folder that contains .po files\n     */\n    public static final String I18N_CUSTOM_DIR_PROPERTY = \"org.bonitasoft.i18n.folder\";\n\n    private static File I18N_CUSTOM_DIR = getI18nCustomDirectory();\n\n    private I18n() {\n        // Singleton\n    }\n\n    public static I18n getInstance() {\n        if (I18N_instance == null) {\n            I18N_instance = new I18n();\n        }\n        return (I18n) I18N_instance;\n    }\n\n    // For test matters only:\n    public static void setInstance(I18n instance) {\n        I18N_instance = instance;\n    }\n\n    // For test matters only:\n    public void refresh() {\n        I18N_CUSTOM_DIR = getI18nCustomDirectory();\n    }\n\n    private static File getI18nCustomDirectory() {\n        String customI18nFolder = System.getProperty(I18N_CUSTOM_DIR_PROPERTY);\n        return customI18nFolder != null ? new File(customI18nFolder) : null;\n    }\n\n    @Override\n    public void loadLocale(final LOCALE locale) {\n        Map<String, String> results = loadLocale(getStreams(locale));\n        if (I18N_CUSTOM_DIR != null) {\n            results.putAll(loadLocale(locale, FileUtils.listDir(I18N_CUSTOM_DIR)));\n        }\n        setLocale(locale, results);\n    }\n\n    private Map<String, String> loadLocale(List<InputStream> streams) {\n        TreeMap<String, String> treeMap = new TreeMap<>();\n        for (InputStream stream : streams) {\n            treeMap.putAll(parsePoResource(stream));\n        }\n        return treeMap;\n    }\n\n    private TreeMap<String, String> loadLocale(final LOCALE locale, List<File> files) {\n        TreeMap<String, String> treeMap = new TreeMap<>();\n        for (File file : getLocaleFiles(locale, files)) {\n            treeMap.putAll(parsePoFile(file));\n        }\n        return treeMap;\n    }\n\n    public List<InputStream> getStreams(LOCALE locale) {\n        ResourcePatternResolver patternResolver = new PathMatchingResourcePatternResolver();\n        try {\n            Resource[] resources = patternResolver.getResources(\"classpath*:i18n/\" + getLocaleRegexForResource(locale));\n            List<InputStream> streams = new ArrayList<>(resources.length);\n            for (Resource resource : resources) {\n                InputStream stream = resource.getInputStream();\n                streams.add(stream);\n            }\n            return streams;\n        } catch (IOException e) {\n            e.printStackTrace();\n            return Collections.emptyList();\n        }\n    }\n\n    private Map<String, String> parsePoFile(final File file) {\n        return POParser.parse(file);\n    }\n\n    private Map<String, String> parsePoResource(final InputStream stream) {\n        return POParser.parsePOFromStream(stream);\n    }\n\n    private List<File> getLocaleFiles(final LOCALE locale, List<File> files) {\n        return FileUtils.getMatchingFiles(makeLocaleRegex(locale), files);\n    }\n\n    private String makeLocaleRegex(final LOCALE locale) {\n        return \"(.*)\" + locale.toString().trim() + \".po\";\n    }\n\n    private String getLocaleRegexForResource(final LOCALE locale) {\n        return \"*\" + locale.toString().trim() + \".po\";\n    }\n\n    public Map<String, String> getAvailableLocalesFor(String application) {\n        final Map<String, String> results = new LinkedHashMap<>();\n        final Map<String, String> locales = getLocales();\n\n        // Add English by default since _en.po files have been removed\n        results.put(\"en\", \"English\");\n        for (final String locale : locales.keySet()) {\n            if (localeExists(locale, application)) {\n                results.put(locale, locales.get(locale));\n            }\n        }\n\n        return results;\n    }\n\n    /**\n     * @return available locale file for a specified application (i.e application_locale.po)\n     */\n    private static boolean localeExists(String locale, String application) {\n        final String fileName = application + \"_\" + locale + \".po\";\n        final File file = new File(I18N_CUSTOM_DIR, fileName);\n        return file.exists() || I18n.class.getResource(\"/i18n/\" + fileName) != null;\n    }\n\n    @Override\n    protected String getText(String string) {\n        return string;\n    }\n\n    @Override\n    protected String getText(String string, Arg... args) {\n        return new TextTemplate(string).toString(args);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/i18n/POParser.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.i18n;\n\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.Map;\nimport java.util.TreeMap;\n\nimport org.fedorahosted.tennera.jgettext.Catalog;\nimport org.fedorahosted.tennera.jgettext.Message;\nimport org.fedorahosted.tennera.jgettext.PoParser;\nimport org.fedorahosted.tennera.jgettext.catalog.parse.ParseException;\n\n/**\n * @author Séverin Moussel\n */\npublic class POParser {\n\n    private final Map<String, String> translations = new TreeMap<>();\n\n    private POParser() {\n    }\n\n    public static Map<String, String> parse(final File file) {\n        return new POParser().parseFile(file);\n    }\n\n    private Map<String, String> parseFile(final File file) {\n        try {\n            parseCatalog(new PoParser().parseCatalog(file));\n        } catch (FileNotFoundException e) {\n            throw new RuntimeException(\"I18N: File not found \" + file.getPath());\n        } catch (ParseException e) {\n            throw new RuntimeException(\"I18N: Could not parse po file \" + file.getPath());\n        } catch (IOException e) {\n            throw new RuntimeException(\"I18N: IO issues while parsing \" + file.getPath());\n        }\n\n        return this.translations;\n    }\n\n    public static Map<String, String> parsePOFromStream(final InputStream stream) {\n        return new POParser().parseFromStream(stream);\n    }\n\n    private Map<String, String> parseFromStream(final InputStream stream) {\n        try {\n            parseCatalog(new PoParser().parseCatalog(stream, false));\n        } catch (IOException e) {\n            throw new RuntimeException(\"I18N: IO issues while parsing translation resource \" + stream.toString());\n        }\n\n        return this.translations;\n    }\n\n    private void parseCatalog(Catalog catalog) {\n        for (Message msg : catalog) {\n            translations.put(msg.getMsgid(), msg.getMsgstr());\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/jsp/JSPI18n.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.jsp;\n\nimport org.bonitasoft.console.common.server.i18n.I18n;\nimport org.bonitasoft.console.common.server.utils.LocaleUtils;\nimport org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n.LOCALE;\n\n/**\n * Used in JSP. Do not delete.\n */\npublic class JSPI18n {\n\n    private final LOCALE locale;\n    private final JSPUtils jspUtils;\n\n    public JSPI18n(JSPUtils jspUtils) {\n        this.jspUtils = jspUtils;\n        this.locale = loadLocale();\n\n        // initialize I18n instance\n        I18n.getInstance();\n    }\n\n    public String t_(String message) {\n        return I18n.t_(message, locale);\n    }\n\n    public LOCALE getLocale() {\n        return locale;\n    }\n\n    private LOCALE loadLocale() {\n        String localeAsString = LocaleUtils.getUserLocaleAsString(jspUtils.getRequest());\n        LOCALE locale = null;\n        try {\n            locale = LOCALE.valueOf(localeAsString);\n        } catch (IllegalArgumentException e) {\n            //this should not happen as the LOCALE enum is supposed to contain all locales\n            LocaleUtils.logUnsupportedLocale(localeAsString, LocaleUtils.DEFAULT_LOCALE);\n        }\n        return locale;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/jsp/JSPUtils.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.jsp;\n\nimport javax.servlet.http.Cookie;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpSession;\n\n/**\n * @author Séverin Moussel\n */\npublic class JSPUtils {\n\n    private final HttpServletRequest request;\n\n    private final HttpSession session;\n\n    public JSPUtils(final HttpServletRequest request, final HttpSession session) {\n        super();\n        this.request = request;\n        this.session = session;\n    }\n\n    public String getSessionOrCookie(final String name, final String defaultValue) {\n\n        // Try reading in session\n        String result = (String) this.session.getAttribute(name);\n\n        // else, try reading in cookies\n        if (result == null) {\n            final Cookie[] cookies = this.request.getCookies();\n            if (cookies != null) {\n                for (int i = 0; i < cookies.length; i++) {\n                    if (cookies[i].getName().equals(name)) {\n                        result = cookies[i].getValue();\n                        break;\n                    }\n                }\n            }\n        }\n\n        // else, set default value\n        if (result == null) {\n            result = defaultValue;\n        }\n\n        return result;\n    }\n\n    public String getParameter(final String name, final String defaultValue) {\n        String result = this.request.getParameter(name);\n\n        if (result == null) {\n            result = defaultValue;\n        }\n\n        return result;\n    }\n\n    public String getParameter(final String name) {\n        return this.getParameter(name, null);\n    }\n\n    public HttpServletRequest getRequest() {\n        return this.request;\n    }\n\n    public boolean getParameter(final String name, final boolean defaultValue) {\n        final String result = this.request.getParameter(name);\n\n        if (result != null) {\n            try {\n\n                // If boolean is passed as an integer\n                final int intValue = Integer.parseInt(result);\n                return intValue > 0;\n\n            } catch (final NumberFormatException e) {\n\n                // If boolean is passed as a recognized string\n                if (\"false\".equalsIgnoreCase(result) || \"no\".equals(result)) {\n                    return false;\n                }\n                if (\"true\".equalsIgnoreCase(result) || \"yes\".equals(result)) {\n                    return false;\n                }\n            }\n        }\n\n        return defaultValue;\n    }\n\n    public int getParameter(final String name, final int defaultValue) {\n        final String result = this.request.getParameter(name);\n\n        if (result != null) {\n            try {\n\n                // If boolean is passed as an integer\n                return Integer.parseInt(result);\n\n            } catch (final NumberFormatException e) {\n                // DO nothing\n            }\n        }\n\n        return defaultValue;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/AccountLockedException.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login;\n\nimport org.bonitasoft.web.server.login.LoginFailureTracker;\n\n/**\n * Thrown when a login attempt is rejected because the user account has been temporarily locked\n * due to too many consecutive failed authentication attempts.\n * <p>\n * This exception is raised by {@link LoginManager} when the {@link LoginFailureTracker} detects that the maximum\n * number of allowed failures has been exceeded for a given username. The account remains locked until the configured\n * lockout duration expires.\n *\n * @see LoginFailureTracker\n * @see LoginManager\n */\npublic class AccountLockedException extends LoginFailedException {\n\n    private static final long serialVersionUID = 1L;\n\n    public AccountLockedException(final String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/HttpServletRequestAccessor.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login;\n\nimport java.util.Map;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.console.common.server.auth.AuthenticationManager;\nimport org.bonitasoft.console.common.server.utils.LocaleUtils;\nimport org.bonitasoft.console.common.server.utils.SessionUtil;\nimport org.bonitasoft.engine.session.APISession;\n\n/**\n * @author Vincent Elcrin\n */\npublic class HttpServletRequestAccessor {\n\n    protected static final String REDIRECT_URL = AuthenticationManager.REDIRECT_URL;\n\n    public static final String USERNAME_PARAM = \"username\";\n\n    public static final String PASSWORD_PARAM = \"password\";\n\n    private final static String OAUTH_VERIFIER = \"oauth_verifier\";\n\n    private final static String OAUTH_TOKEN = \"oauth_token\";\n\n    private final HttpServletRequest httpServletRequest;\n\n    public HttpServletRequestAccessor(final HttpServletRequest httpServletRequest) {\n        this.httpServletRequest = httpServletRequest;\n    }\n\n    public String getUsername() {\n        return httpServletRequest.getParameter(USERNAME_PARAM);\n    }\n\n    public String getPassword() {\n        return httpServletRequest.getParameter(PASSWORD_PARAM);\n    }\n\n    public HttpSession getHttpSession() {\n        return httpServletRequest.getSession();\n    }\n\n    public HttpSession getHttpSession(boolean create) {\n        return httpServletRequest.getSession(create);\n    }\n\n    public String getRedirectUrl() {\n        return httpServletRequest.getParameter(REDIRECT_URL);\n    }\n\n    public String getOAuthToken() {\n        return httpServletRequest.getParameter(OAUTH_TOKEN);\n    }\n\n    public String getOAuthVerifier() {\n        return httpServletRequest.getParameter(OAUTH_VERIFIER);\n    }\n\n    public APISession getApiSession() {\n        return (APISession) getHttpSession().getAttribute(SessionUtil.API_SESSION_PARAM_KEY);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public Map<String, String[]> getParameterMap() {\n        return httpServletRequest.getParameterMap();\n    }\n\n    public String getRequestedUrl() {\n        return httpServletRequest.getRequestURL().toString();\n    }\n\n    public String getRequestedUri() {\n        return httpServletRequest.getRequestURI();\n    }\n\n    public HttpServletRequest asHttpServletRequest() {\n        return httpServletRequest;\n    }\n\n    public String getUserAgent() {\n        return httpServletRequest.getHeader(\"User-Agent\");\n    }\n\n    public String getLocale() {\n        return LocaleUtils.getUserLocaleAsString(httpServletRequest);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/LoginFailedException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login;\n\n/**\n * @author Ruiheng Fan\n */\npublic class LoginFailedException extends Exception {\n\n    /**\n     *\n     */\n    private static final long serialVersionUID = 1853195048371475012L;\n\n    /**\n     * Constructor\n     */\n    public LoginFailedException() {\n        super();\n    }\n\n    /**\n     * @param message\n     *        message associated with the exception\n     * @param cause\n     *        cause of the exception\n     */\n    public LoginFailedException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    /**\n     * @param message\n     *        message associated with the exception\n     */\n    public LoginFailedException(final String message) {\n        super(message);\n    }\n\n    /**\n     * @param cause\n     *        cause of the exception\n     */\n    public LoginFailedException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/LoginManager.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.apache.commons.collections4.MapUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.bonitasoft.console.common.server.auth.AuthenticationFailedException;\nimport org.bonitasoft.console.common.server.auth.AuthenticationManager;\nimport org.bonitasoft.console.common.server.auth.AuthenticationManagerFactory;\nimport org.bonitasoft.console.common.server.auth.AuthenticationManagerNotFoundException;\nimport org.bonitasoft.console.common.server.login.credentials.Credentials;\nimport org.bonitasoft.console.common.server.login.credentials.StandardCredentials;\nimport org.bonitasoft.console.common.server.login.credentials.UserLogger;\nimport org.bonitasoft.console.common.server.login.filter.TokenGenerator;\nimport org.bonitasoft.console.common.server.utils.LocaleUtils;\nimport org.bonitasoft.console.common.server.utils.SessionUtil;\nimport org.bonitasoft.engine.exception.TenantStatusException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.user.User;\nimport org.bonitasoft.web.server.login.LoginFailureTracker;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * This class performs the authentication, the login and initializes the HTTP session\n *\n * @author Anthony Birembaut\n */\npublic class LoginManager {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(LoginManager.class.getName());\n\n    protected TokenGenerator tokenGenerator = new TokenGenerator();\n    protected PortalCookies portalCookies = new PortalCookies();\n    protected LoginFailureTracker loginFailureTracker;\n\n    /**\n     * @deprecated Use {@link #LoginManager(LoginFailureTracker)} instead.\n     *             This constructor disables brute-force login protection.\n     */\n    @Deprecated\n    public LoginManager() {\n        this(null);\n    }\n\n    public LoginManager(LoginFailureTracker loginFailureTracker) {\n        this.loginFailureTracker = loginFailureTracker;\n    }\n\n    /**\n     * Performs the login using the appropriate AuthenticationManager implementation for authentication, logging in on\n     * the engine, initializing the HTTP session, and creating the cookies (tanat and CSRF)\n     */\n    public void login(final HttpServletRequest request, final HttpServletResponse response)\n            throws AuthenticationManagerNotFoundException, LoginFailedException, TenantStatusException,\n            AuthenticationFailedException, ServletException {\n        final HttpServletRequestAccessor requestAccessor = new HttpServletRequestAccessor(request);\n        final StandardCredentials userCredentials = createUserCredentials(requestAccessor);\n        loginInternal(requestAccessor, response, getUserLogger(), userCredentials);\n    }\n\n    public void loginInternal(HttpServletRequestAccessor request, HttpServletResponse response, UserLogger userLoger,\n            Credentials credentials)\n            throws AuthenticationFailedException, ServletException, LoginFailedException {\n        String username = credentials.getName();\n        boolean shouldTrackLoginFailures = loginFailureTracker != null && StringUtils.isNotEmpty(username);\n        // Note: returning 429 (locked) vs 401 (bad credentials) reveals whether a username exists.\n        // This is an intentional usability trade-off — the lockout message helps legitimate users\n        // understand why they cannot log in, and username enumeration is low-risk given that\n        // the login page itself already exposes whether an account is valid.\n        if (shouldTrackLoginFailures && loginFailureTracker.isLockedOut(username)) {\n            LOGGER.warn(\n                    \"Login attempt rejected for user [{}]: account temporarily locked due to too many failed attempts\",\n                    username);\n            throw new AccountLockedException(\"Too many failed login attempts. Please try again later.\");\n        }\n        AuthenticationManager authenticationManager = getAuthenticationManager();\n        APISession apiSession;\n        boolean invalidateAndRecreateHTTPSession;\n        try {\n            Map<String, Serializable> credentialsMap = authenticationManager.authenticate(request, credentials);\n            // In case of a login with the login service we invalidate the session and create a new one.\n            // Otherwise, logging in with the credentials in the request (SSO) it is not mandatory it depends on the AuthenticationManager implementation used\n            // some SSO mechanisms already handle it (SAML, OIDC).\n            Boolean invalidateAndRecreateHTTPSessionIfSet = (Boolean) credentialsMap\n                    .remove(AuthenticationManager.INVALIDATE_SESSION);\n            invalidateAndRecreateHTTPSession = invalidateAndRecreateHTTPSessionIfSet == null\n                    || invalidateAndRecreateHTTPSessionIfSet.booleanValue();\n            if (credentialsMap.isEmpty()) {\n                if (credentials.getName() == null || credentials.getName().isEmpty()) {\n                    LOGGER.debug(\"There are no credentials in the request\");\n                    throw new AuthenticationFailedException(\"No credentials in request\");\n                }\n            }\n            apiSession = loginWithAppropriateCredentials(userLoger, credentials, credentialsMap);\n        } catch (LoginFailedException | AuthenticationFailedException e) {\n            if (shouldTrackLoginFailures) {\n                loginFailureTracker.recordFailure(username);\n            }\n            throw e;\n        }\n        if (shouldTrackLoginFailures) {\n            loginFailureTracker.resetFailures(username);\n        }\n        storeCredentials(request, apiSession, invalidateAndRecreateHTTPSession);\n        portalCookies.addCSRFTokenCookieToResponse(request.asHttpServletRequest(), response,\n                tokenGenerator.createOrLoadToken(request.getHttpSession()));\n    }\n\n    protected StandardCredentials createUserCredentials(final HttpServletRequestAccessor requestAccessor) {\n        return new StandardCredentials(requestAccessor.getUsername(), requestAccessor.getPassword());\n    }\n\n    protected UserLogger getUserLogger() {\n        return new UserLogger();\n    }\n\n    public AuthenticationManager getAuthenticationManager() throws ServletException {\n        try {\n            final AuthenticationManager authenticationManager = AuthenticationManagerFactory.getAuthenticationManager();\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(\"Using the AuthenticationManager implementation: \"\n                        + authenticationManager.getClass().getName());\n            }\n            return authenticationManager;\n        } catch (final AuthenticationManagerNotFoundException e) {\n            throw new ServletException(e);\n        }\n    }\n\n    private APISession loginWithAppropriateCredentials(UserLogger userLoger, Credentials credentials,\n            Map<String, Serializable> credentialsMap)\n            throws LoginFailedException {\n        if (MapUtils.isEmpty(credentialsMap)) {\n            LOGGER.debug(\"Engine login using the username and password\");\n            return userLoger.doLogin(credentials);\n        } else {\n            LOGGER.debug(\"Engine login using the map of credentials retrieved from the request\");\n            return userLoger.doLogin(credentialsMap);\n        }\n    }\n\n    protected void storeCredentials(final HttpServletRequestAccessor request, final APISession session,\n            boolean recreateHTTPSession) throws LoginFailedException {\n        String local = LocaleUtils.getUserLocaleAsString(request.asHttpServletRequest());\n        // Use APISession username instead of request parameter for SSO compatibility (OIDC, SAML, etc.)\n        final User user = new User(session.getUserName(), local);\n        initSession(request, session, user, recreateHTTPSession);\n    }\n\n    protected void initSession(final HttpServletRequestAccessor request, final APISession session, final User user,\n            boolean recreateHTTPSession) {\n        if (LOGGER.isDebugEnabled()) {\n            LOGGER.debug(\"HTTP session initialization\");\n        }\n        if (recreateHTTPSession) {\n            //invalidating session allows to fix session fixation security issue\n            request.getHttpSession().invalidate();\n        }\n        //calling request.getSession() creates a new Session if no any valid exists\n        SessionUtil.sessionLogin(user, session, request.getHttpSession());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/PortalCookies.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login;\n\nimport javax.servlet.http.Cookie;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.bonitasoft.console.common.server.login.filter.TokenGenerator;\nimport org.bonitasoft.console.common.server.preferences.properties.PropertiesFactory;\n\npublic class PortalCookies {\n\n    /**\n     * set the CSRF security token to the HTTP response as cookie.\n     */\n    public void addCSRFTokenCookieToResponse(HttpServletRequest request, HttpServletResponse res,\n            Object apiTokenFromClient) {\n        // set the cookie path to / if the app is deployed at root context\n        String defaultCookiePath = StringUtils.isEmpty(request.getContextPath()) ? \"/\" : request.getContextPath();\n        String path = System.getProperty(\"bonita.csrf.cookie.path\", defaultCookiePath);\n        invalidatePreviousCSRFTokenCookie(request, res, path);\n\n        Cookie csrfCookie = new Cookie(TokenGenerator.X_BONITA_API_TOKEN, apiTokenFromClient.toString());\n        // cookie path can be set via system property.\n        // Can be set to '/' when another app is deployed in same server than bonita and want to share csrf cookie\n        csrfCookie.setPath(path);\n        csrfCookie.setSecure(isCSRFTokenCookieSecure());\n        res.addCookie(csrfCookie);\n    }\n\n    // when a cookie already exists on a different path than the one expected, we need to invalidate it.\n    // since there is no way of knowing the path as it is not sent server-side (getPath return null) we invalidate any cookie found\n    // see https://bonitasoft.atlassian.net/browse/BS-15883 and BS-16241\n    private void invalidatePreviousCSRFTokenCookie(HttpServletRequest request, HttpServletResponse res, String path) {\n        Cookie cookie = getCookie(request, TokenGenerator.X_BONITA_API_TOKEN);\n        if (cookie != null) {\n            cookie.setMaxAge(0);\n            cookie.setValue(\"\");\n            res.addCookie(cookie);\n            invalidateRootCookie(res, cookie, path);\n        }\n    }\n\n    //Studio sets the cookie to the path '/' so this is required when using a bunble after a studio in the same browser\n    public void invalidateRootCookie(HttpServletResponse res, Cookie cookie, String path) {\n        if (!\"/\".equals(path)) {\n            Cookie rootCookie = (Cookie) cookie.clone();\n            rootCookie.setPath(\"/\");\n            res.addCookie(rootCookie);\n        }\n    }\n\n    // protected for testing\n    public boolean isCSRFTokenCookieSecure() {\n        return PropertiesFactory.getSecurityProperties().isCSRFTokenCookieSecure();\n    }\n\n    public Cookie getCookie(HttpServletRequest request, String name) {\n        Cookie[] cookies = request.getCookies();\n        if (cookies != null) {\n            for (Cookie cookie : cookies) {\n                if (cookie.getName().equals(name)) {\n                    return cookie;\n                }\n            }\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/credentials/Credentials.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.credentials;\n\n/**\n * @author Vincent Elcrin\n */\npublic interface Credentials {\n\n    String getName();\n\n    String getPassword();\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/credentials/LoginDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.credentials;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.LoginAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.platform.LoginException;\nimport org.bonitasoft.engine.platform.LogoutException;\nimport org.bonitasoft.engine.platform.UnknownUserException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Yongtao Guo\n */\npublic class LoginDatastore {\n\n    /**\n     * Logger\n     */\n    private static final Logger LOGGER = LoggerFactory.getLogger(LoginDatastore.class.getName());\n\n    public APISession login(final String username, final String password) throws BonitaException {\n        APISession apiSession;\n        final String errorMessage = \"Error while logging in the engine API.\";\n        try {\n            if (username == null || password == null) {\n                LOGGER.error(errorMessage);\n                throw new LoginException(errorMessage);\n            }\n            apiSession = getLoginAPI().login(username, password);\n        } catch (final UnknownUserException e) {\n            LOGGER.error(e.getMessage());\n            throw e;\n        } catch (final LoginException e) {\n            LOGGER.error(errorMessage);\n            throw e;\n        } catch (final BonitaException e) {\n            LOGGER.error(e.getMessage());\n            throw e;\n        }\n        return apiSession;\n    }\n\n    /**\n     * login.\n     *\n     * @return APISession aAPISession\n     * @throws BonitaException\n     */\n    public APISession login(final Map<String, Serializable> credentials) throws BonitaException {\n        APISession apiSession;\n        try {\n            if (credentials == null) {\n                final String errorMessage = \"Error while logging in on the engine API.\";\n                if (LOGGER.isErrorEnabled()) {\n                    LOGGER.error(errorMessage);\n                }\n                throw new LoginException(errorMessage);\n            }\n            apiSession = getLoginAPI().login(credentials);\n        } catch (final LoginException e) {\n            final String errorMessage = \"Error while logging in on the engine API.\";\n            if (LOGGER.isErrorEnabled()) {\n                LOGGER.error(errorMessage);\n            }\n            throw new BonitaException(e);\n        }\n        return apiSession;\n    }\n\n    /**\n     * logout .\n     *\n     * @throws BonitaException\n     */\n    public void logout(final APISession apiSession) throws BonitaException {\n        if (apiSession != null) {\n            try {\n                getLoginAPI().logout(apiSession);\n            } catch (final LogoutException e) {\n                final String errorMessage = \"Logout error while calling the engine API.\";\n                if (LOGGER.isErrorEnabled()) {\n                    LOGGER.error(errorMessage);\n                }\n                throw new BonitaException(errorMessage, e);\n            }\n        }\n    }\n\n    protected LoginAPI getLoginAPI() throws BonitaException {\n        try {\n            return TenantAPIAccessor.getLoginAPI();\n        } catch (final BonitaException e) {\n            final String errorMessage = \"Error while getting the loginAPI.\";\n            if (LOGGER.isErrorEnabled()) {\n                LOGGER.error(errorMessage);\n            }\n            throw new BonitaException(errorMessage, e);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/credentials/StandardCredentials.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.credentials;\n\n/**\n * @author Julien Reboul\n */\npublic class StandardCredentials implements Credentials {\n\n    private final String name;\n\n    private final String password;\n\n    public StandardCredentials(final String name, final String password) {\n        this.name = name;\n        this.password = password;\n    }\n\n    /**\n     * @see org.bonitasoft.console.common.server.login.credentials.Credentials#getName()\n     */\n    @Override\n    public String getName() {\n        return name;\n    }\n\n    /**\n     * @see org.bonitasoft.console.common.server.login.credentials.Credentials#getPassword()\n     */\n    @Override\n    public String getPassword() {\n        return password;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/credentials/UserLogger.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.credentials;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.console.common.server.login.LoginFailedException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.session.APISession;\n\n/**\n * @author Vincent Elcrin\n */\npublic class UserLogger {\n\n    /**\n     * Overridden in SP\n     */\n    public APISession doLogin(Credentials credentials) throws LoginFailedException {\n        try {\n            return getDatastore().login(credentials.getName(),\n                    credentials.getPassword());\n        } catch (final BonitaException e) {\n            throw new LoginFailedException(e.getMessage(), e);\n        }\n    }\n\n    public APISession doLogin(Map<String, Serializable> credentials) throws LoginFailedException {\n        try {\n            return getDatastore().login(credentials);\n        } catch (final BonitaException e) {\n            throw new LoginFailedException(e.getMessage(), e);\n        }\n    }\n\n    public void doLogout(final APISession apiSession) throws LoginFailedException {\n        try {\n            getDatastore().logout(apiSession);\n        } catch (final BonitaException e) {\n            throw new LoginFailedException(e.getMessage(), e);\n        }\n    }\n\n    /**\n     * Overridden in SP\n     */\n    private LoginDatastore getDatastore() {\n        return new LoginDatastore();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/filter/AccountLockedRuntimeException.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.filter;\n\n/**\n * Unchecked bridge exception used at the servlet-filter level to propagate account lockout\n * across filter-chain boundaries (e.g. from {@code AuthenticationRule} to {@code AuthenticationFilter}).\n * <p>\n * Not to be confused with {@link org.bonitasoft.console.common.server.login.AccountLockedException},\n * which is the engine-level checked exception (extends {@code LoginFailedException}) thrown by\n * {@code LoginManager} when brute-force protection triggers a lockout.\n */\npublic class AccountLockedRuntimeException extends RuntimeException {\n\n    private static final long serialVersionUID = 1L;\n\n    public AccountLockedRuntimeException(final String message) {\n        super(message);\n    }\n\n    public AccountLockedRuntimeException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/filter/AlreadyLoggedInRule.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.filter;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.console.common.server.login.HttpServletRequestAccessor;\nimport org.bonitasoft.console.common.server.login.servlet.PlatformLoginServlet;\nimport org.bonitasoft.console.common.server.utils.LocaleUtils;\nimport org.bonitasoft.console.common.server.utils.SessionUtil;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.user.User;\n\npublic class AlreadyLoggedInRule extends AuthenticationRule {\n\n    @Override\n    public boolean doAuthorize(final HttpServletRequestAccessor request, HttpServletResponse response)\n            throws ServletException {\n        if (isUserAlreadyLoggedIn(request)) {\n            ensureUserSession(request.asHttpServletRequest(),\n                    request.getHttpSession(),\n                    request.getApiSession());\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * Overridden is Subscription\n     */\n    protected boolean isUserAlreadyLoggedIn(final HttpServletRequestAccessor request) throws ServletException {\n        HttpServletRequest httpServletRequest = request.asHttpServletRequest();\n        if (httpServletRequest.getPathInfo() != null) {\n            String requestPath = httpServletRequest.getServletPath() + httpServletRequest.getPathInfo();\n            if (requestPath.matches(RestAPIAuthorizationFilter.PLATFORM_API_URI_REGEXP)) {\n                return request.getHttpSession().getAttribute(PlatformLoginServlet.PLATFORM_SESSION_PARAM_KEY) != null;\n            }\n        }\n        return request.getApiSession() != null;\n    }\n\n    private void ensureUserSession(final HttpServletRequest request, final HttpSession session,\n            final APISession apiSession) {\n        if (apiSession != null && session.getAttribute(SessionUtil.USER_SESSION_PARAM_KEY) == null) {\n            reCreateUser(request, session, apiSession);\n        }\n    }\n\n    private void reCreateUser(final HttpServletRequest request, final HttpSession session,\n            final APISession apiSession) {\n        final String locale = getLocale(request);\n        final User user = new User(apiSession.getUserName(), locale);\n        session.setAttribute(SessionUtil.USER_SESSION_PARAM_KEY, user);\n    }\n\n    private String getLocale(final HttpServletRequest request) {\n        return LocaleUtils.getUserLocaleAsString(request);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/filter/AuthenticationFilter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.filter;\n\nimport java.io.IOException;\nimport java.util.LinkedList;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.function.Supplier;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.FilterConfig;\nimport javax.servlet.ServletException;\nimport javax.servlet.ServletRequest;\nimport javax.servlet.ServletResponse;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpSession;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.bonitasoft.console.common.server.auth.AuthenticationManager;\nimport org.bonitasoft.console.common.server.auth.AuthenticationManagerFactory;\nimport org.bonitasoft.console.common.server.auth.AuthenticationManagerNotFoundException;\nimport org.bonitasoft.console.common.server.filter.ExcludingPatternFilter;\nimport org.bonitasoft.console.common.server.login.HttpServletRequestAccessor;\nimport org.bonitasoft.console.common.server.login.utils.LoginUrl;\nimport org.bonitasoft.console.common.server.login.utils.RedirectUrl;\nimport org.bonitasoft.console.common.server.login.utils.RedirectUrlBuilder;\nimport org.bonitasoft.console.common.server.utils.SessionUtil;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.api.TenantAdministrationAPI;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.mdc.MDCHelper;\nimport org.bonitasoft.engine.mdc.MDCHelper.CheckedCallable4;\nimport org.bonitasoft.engine.mdc.UserIdMDC;\nimport org.bonitasoft.engine.session.Session;\nimport org.bonitasoft.web.server.login.LoginFailureTracker;\nimport org.bonitasoft.web.server.login.LoginFailureTrackerAccessor;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\n\n/**\n * @author Vincent Elcrin\n * @author Anthony Birembaut\n */\npublic class AuthenticationFilter extends ExcludingPatternFilter {\n\n    protected static final String AUTHENTICATION_FILTER_EXCLUDED_PAGES_PATTERN = \"^/(bonita/)?(?:(login\\\\.jsp$)|(apps/.+/API/)|(portal/resource/.+/API/))\";\n\n    protected static final String REDIRECT_PARAM = \"redirectWhenUnauthorized\";\n\n    protected static final String USER_NOT_FOUND_JSP = \"/usernotfound.jsp\";\n\n    public static final String ERROR_PAGE_REQUEST_PATH_REGEX = \"/portal/resource/app/appDirectoryBonita/error-\\\\d+/(content|theme)/.*\";\n\n    protected boolean redirectWhenUnauthorized;\n\n    private LoginFailureTracker loginFailureTracker;\n\n    /**\n     * Logger\n     */\n    private static final Logger LOGGER = LoggerFactory.getLogger(AuthenticationFilter.class.getName());\n\n    private final LinkedList<AuthenticationRule> rules = new LinkedList<>();\n\n    public AuthenticationFilter() {\n        addRules();\n    }\n\n    protected void addRules() {\n        addRule(new AlreadyLoggedInRule());\n    }\n\n    protected void addRule(final AuthenticationRule rule) {\n        rules.add(rule);\n    }\n\n    @Override\n    public void init(FilterConfig filterConfig) throws ServletException {\n        String redirectInitParam = filterConfig.getInitParameter(REDIRECT_PARAM);\n        redirectWhenUnauthorized = redirectInitParam != null ? Boolean.parseBoolean(redirectInitParam) : true;\n        super.init(filterConfig);\n        loginFailureTracker = LoginFailureTrackerAccessor\n                .getLoginFailureTracker(filterConfig.getServletContext());\n        for (AuthenticationRule rule : rules) {\n            rule.setLoginFailureTracker(loginFailureTracker);\n        }\n    }\n\n    @Override\n    public String getDefaultExcludedPages() {\n        return AUTHENTICATION_FILTER_EXCLUDED_PAGES_PATTERN;\n    }\n\n    @Override\n    public void proceedWithFiltering(final ServletRequest request, final ServletResponse response,\n            final FilterChain chain) throws ServletException, IOException {\n        final HttpServletRequestAccessor requestAccessor = new HttpServletRequestAccessor((HttpServletRequest) request);\n        doAuthenticationFiltering(requestAccessor, (HttpServletResponse) response, chain);\n    }\n\n    protected void doAuthenticationFiltering(final HttpServletRequestAccessor requestAccessor,\n            final HttpServletResponse response,\n            final FilterChain chain) throws ServletException, IOException {\n\n        try {\n            if (!isAuthorized(requestAccessor, response, chain)) {\n                if (!isAccessingPlatformAPIWithAPISession(requestAccessor)) {\n                    //do not invalidate the API session when trying to access platform API without a platform session\n                    //fix RUNTIME-1562\n                    cleanHttpSession(requestAccessor.getHttpSession());\n                }\n                if (redirectWhenUnauthorized && requestAccessor.asHttpServletRequest().getMethod().equals(\"GET\")) {\n                    response.sendRedirect(createLoginPageUrl(requestAccessor).getLocation());\n                } else {\n                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);\n                }\n            }\n        } catch (AccountLockedRuntimeException e) {\n            handleAccountLockoutException(response, e);\n        } catch (PlatformUnderMaintenanceException e) {\n            handlePlatformUnderMaintenanceException(requestAccessor, response, e);\n        } catch (final EngineUserNotFoundOrInactive e) {\n            handleUserNotFoundOrInactiveException(requestAccessor, response, e);\n        } catch (BonitaException e) {\n            handleBonitaException(requestAccessor, response, e);\n        }\n    }\n\n    protected boolean isAccessingPlatformAPIWithAPISession(final HttpServletRequestAccessor requestAccessor) {\n        String requestPath = requestAccessor.asHttpServletRequest().getServletPath()\n                + requestAccessor.asHttpServletRequest().getPathInfo();\n        return requestPath.matches(RestAPIAuthorizationFilter.PLATFORM_API_URI_REGEXP)\n                && requestAccessor.getApiSession() != null;\n    }\n\n    /**\n     * @return true if one of the rules pass false otherwise\n     */\n    protected boolean isAuthorized(final HttpServletRequestAccessor requestAccessor,\n            final HttpServletResponse response,\n            final FilterChain chain)\n            throws ServletException, IOException, PlatformUnderMaintenanceException, BonitaException {\n\n        for (final AuthenticationRule rule : getRules()) {\n            if (rule.doAuthorize(requestAccessor, response)) {\n                // only when proceeding, log the authenticated user id in context\n                var userId = Optional.ofNullable(requestAccessor).map(HttpServletRequestAccessor::getApiSession)\n                        .filter(Objects::nonNull).map(Session::getUserId).filter(id -> id >= 0L);\n                Supplier<UserIdMDC> withAuthenticatedUser = UserIdMDC.supplierFromOptionalId(userId);\n\n                CheckedCallable4<Boolean, ServletException, IOException, PlatformUnderMaintenanceException, BonitaException> call = () -> {\n                    checkPlatformMaintenanceState(requestAccessor);\n                    rule.proceedWithRequest(chain, requestAccessor.asHttpServletRequest(), response);\n                    return true;\n                };\n                return MDCHelper.tryWithMDC(withAuthenticatedUser, call);\n\n            }\n        }\n        return false;\n    }\n\n    protected void checkPlatformMaintenanceState(final HttpServletRequestAccessor requestAccessor)\n            throws PlatformUnderMaintenanceException, BonitaException {\n        try {\n            // If redirectWhenUnauthorized is set to false it means we are not trying to access a page\n            // Maintenance state will be Handled at REST API Authorization filter in this case\n            if (redirectWhenUnauthorized\n                    && !isLoggedInAsTechnicalUser(requestAccessor)\n                    && isPlaformInMaintenance(requestAccessor)\n                    && !isAccessingErrorPage(requestAccessor)) {\n                throw new PlatformUnderMaintenanceException(\"Platform is under Maintenance\");\n            }\n        } catch (BonitaException e) {\n            if (LOGGER.isWarnEnabled()) {\n                LOGGER.warn(\"Error while checking platform maintenance state : \" + e.getMessage(), e);\n            }\n            throw e;\n        }\n    }\n\n    protected boolean isAccessingErrorPage(HttpServletRequestAccessor requestAccessor) {\n        HttpServletRequest httpRequest = requestAccessor.asHttpServletRequest();\n        if (httpRequest.getPathInfo() != null) {\n            String requestPath = httpRequest.getServletPath() + httpRequest.getPathInfo();\n            return requestPath.matches(ERROR_PAGE_REQUEST_PATH_REGEX);\n        }\n        return false;\n    }\n\n    protected boolean isLoggedInAsTechnicalUser(HttpServletRequestAccessor requestAccessor) {\n        return requestAccessor.getApiSession() != null && requestAccessor.getApiSession().isTechnicalUser();\n    }\n\n    protected boolean isPlaformInMaintenance(HttpServletRequestAccessor requestAccessor) throws BonitaException {\n        TenantAdministrationAPI tenantAdministrationAPI = TenantAPIAccessor\n                .getTenantAdministrationAPI(requestAccessor.getApiSession());\n        return tenantAdministrationAPI.isPaused();\n    }\n\n    protected void handleAccountLockoutException(final HttpServletResponse response,\n            final AccountLockedRuntimeException e) {\n        LOGGER.debug(\"Account locked out: {}\", e.getMessage());\n        response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());\n        if (loginFailureTracker != null) {\n            response.setHeader(HttpHeaders.RETRY_AFTER,\n                    String.valueOf(loginFailureTracker.getLockoutDurationSeconds()));\n        }\n    }\n\n    protected void handleUserNotFoundOrInactiveException(final HttpServletRequestAccessor requestAccessor,\n            final HttpServletResponse response,\n            final EngineUserNotFoundOrInactive e) throws ServletException {\n        if (LOGGER.isDebugEnabled()) {\n            LOGGER.debug(\"redirection to user not found page : \" + e.getMessage(), e);\n        }\n        if (redirectWhenUnauthorized && requestAccessor.asHttpServletRequest().getMethod().equals(\"GET\")) {\n            redirectTo(requestAccessor, response, USER_NOT_FOUND_JSP);\n        } else {\n            response.setStatus(HttpServletResponse.SC_FORBIDDEN);\n        }\n    }\n\n    protected void handlePlatformUnderMaintenanceException(final HttpServletRequestAccessor requestAccessor,\n            final HttpServletResponse response,\n            final PlatformUnderMaintenanceException e) throws IOException {\n        if (LOGGER.isDebugEnabled()) {\n            LOGGER.debug(\"Redirection to maintenance page : \" + e.getMessage(), e);\n        }\n        if (redirectWhenUnauthorized && requestAccessor.asHttpServletRequest().getMethod().equals(\"GET\")) {\n            response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, \"Platform is under maintenance\");\n        } else {\n            response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);\n        }\n    }\n\n    protected void handleBonitaException(final HttpServletRequestAccessor requestAccessor,\n            final HttpServletResponse response,\n            final BonitaException e) throws IOException {\n        if (LOGGER.isDebugEnabled()) {\n            LOGGER.debug(\"redirection to 500 error page : \" + e.getMessage(), e);\n        }\n        if (redirectWhenUnauthorized && requestAccessor.asHttpServletRequest().getMethod().equals(\"GET\")) {\n            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);\n        } else {\n            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);\n        }\n    }\n\n    /**\n     * manage redirection to maintenance page\n     *\n     * @param request\n     * @param response\n     * @param pagePath\n     */\n    protected void redirectTo(final HttpServletRequestAccessor request, final HttpServletResponse response,\n            final String pagePath)\n            throws ServletException {\n        try {\n            response.sendRedirect(request.asHttpServletRequest().getContextPath() + pagePath);\n        } catch (final IOException e) {\n            if (LOGGER.isInfoEnabled()) {\n                LOGGER.info(e.getMessage());\n            }\n        }\n    }\n\n    protected LinkedList<AuthenticationRule> getRules() {\n        return rules;\n    }\n\n    @Override\n    public void destroy() {\n    }\n\n    // protected for test stubbing\n    protected AuthenticationManager getAuthenticationManager() throws ServletException {\n        try {\n            return AuthenticationManagerFactory.getAuthenticationManager();\n        } catch (final AuthenticationManagerNotFoundException e) {\n            throw new ServletException(e);\n        }\n    }\n\n    protected RedirectUrl makeRedirectUrl(final HttpServletRequestAccessor httpRequest) {\n        // if the request has a redirection URL, we use it, otherwise we use the requested URI\n        boolean hasRedirectionURL = StringUtils.isNotBlank(httpRequest.getRedirectUrl());\n        final RedirectUrlBuilder builder = new RedirectUrlBuilder(\n                hasRedirectionURL ? httpRequest.getRedirectUrl() : httpRequest.getRequestedUri());\n        builder.appendParameters(httpRequest.getParameterMap());\n        return builder.build();\n    }\n\n    protected LoginUrl createLoginPageUrl(final HttpServletRequestAccessor requestAccessor) throws ServletException {\n        return new LoginUrl(getAuthenticationManager(),\n                makeRedirectUrl(requestAccessor).getUrl(), requestAccessor);\n    }\n\n    protected void cleanHttpSession(final HttpSession session) {\n        SessionUtil.sessionLogout(session);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/filter/AuthenticationRule.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.filter;\n\nimport java.io.IOException;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.bonitasoft.console.common.server.login.HttpServletRequestAccessor;\nimport org.bonitasoft.console.common.server.login.LoginManager;\nimport org.bonitasoft.web.server.login.LoginFailureTracker;\n\n/**\n * Created by Vincent Elcrin\n * Date: 30/08/13\n * Time: 10:28\n */\npublic abstract class AuthenticationRule {\n\n    private LoginManager loginManager = null;\n    private LoginFailureTracker loginFailureTracker;\n\n    /*\n     * @return whether the process needs to be aborted or not\n     */\n    public abstract boolean doAuthorize(HttpServletRequestAccessor request, HttpServletResponse response)\n            throws ServletException;\n\n    void setLoginFailureTracker(LoginFailureTracker loginFailureTracker) {\n        this.loginFailureTracker = loginFailureTracker;\n        // Reset so that getLoginManager() re-creates it with the new tracker\n        this.loginManager = null;\n    }\n\n    protected LoginManager getLoginManager() {\n        if (loginManager == null) {\n            loginManager = new LoginManager(loginFailureTracker);\n        }\n        return loginManager;\n    }\n\n    public void proceedWithRequest(FilterChain chain, HttpServletRequest request, HttpServletResponse response)\n            throws IOException, ServletException {\n        chain.doFilter(request, response);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/filter/ContentTypeSecurityFilter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.filter;\n\nimport java.io.IOException;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.FilterConfig;\nimport javax.servlet.ServletException;\nimport javax.servlet.ServletRequest;\nimport javax.servlet.ServletResponse;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.bonitasoft.console.common.server.filter.ExcludingPatternFilter;\n\n/**\n * Security filter setting the X-Content-Type-Options in the response headers\n *\n * @author Paul AMAR\n * @author Anthony Birembaut\n */\npublic class ContentTypeSecurityFilter extends ExcludingPatternFilter {\n\n    protected static final String X_CONTENT_TYPE_HEADER = \"X-Content-Type-Options\";\n\n    protected String headerValue;\n\n    @Override\n    public String getDefaultExcludedPages() {\n        return \"\";\n    }\n\n    @Override\n    public void init(FilterConfig filterConfig) throws ServletException {\n        headerValue = StringUtils.defaultIfEmpty(filterConfig.getInitParameter(X_CONTENT_TYPE_HEADER), \"nosniff\");\n        super.init(filterConfig);\n    }\n\n    @Override\n    public void proceedWithFiltering(final ServletRequest request, final ServletResponse response,\n            final FilterChain chain) throws ServletException, IOException {\n        // casting to HTTPServlet(Request|Response)\n        final HttpServletRequest req = (HttpServletRequest) request;\n        final HttpServletResponse res = (HttpServletResponse) response;\n\n        // X-Content-Type-Options (Drive-by download attacks)\n        res.setHeader(X_CONTENT_TYPE_HEADER, headerValue);\n\n        chain.doFilter(req, res);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/filter/EngineUserNotFoundOrInactive.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.filter;\n\npublic class EngineUserNotFoundOrInactive extends RuntimeException {\n\n    private static final long serialVersionUID = 4735182063504328575L;\n\n    public EngineUserNotFoundOrInactive(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/filter/FrameSecurityFilter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.filter;\n\nimport java.io.IOException;\nimport java.util.Objects;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.FilterConfig;\nimport javax.servlet.ServletException;\nimport javax.servlet.ServletRequest;\nimport javax.servlet.ServletResponse;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.bonitasoft.console.common.server.filter.ExcludingPatternFilter;\nimport org.bonitasoft.console.common.server.preferences.properties.PropertiesFactory;\nimport org.bonitasoft.console.common.server.preferences.properties.SecurityProperties;\n\n/**\n * Security filter setting the X-Frame-Options in the response headers\n *\n * @author Anthony Birembaut\n */\npublic class FrameSecurityFilter extends ExcludingPatternFilter {\n\n    protected static final String X_FRAME_OPTIONS_HEADER = \"X-Frame-Options\";\n\n    protected static final String X_FRAME_OPTIONS_HEADER_DEFAULT = \"SAMEORIGIN\";\n\n    protected static final String CONTENT_SECURITY_POLICY_HEADER = \"Content-Security-Policy\";\n\n    protected static final String CONTENT_SECURITY_POLICY_HEADER_DEFAULT = \"frame-ancestors 'self';\";\n\n    protected String xFrameHeaderValue;\n\n    protected String contentSecurityHeaderValue;\n\n    @Override\n    public String getDefaultExcludedPages() {\n        return \"\";\n    }\n\n    @Override\n    public void init(FilterConfig filterConfig) throws ServletException {\n        SecurityProperties securityProperties = getSecurityProperties();\n        String xFrameHeaderPropertyValue = securityProperties.getXFrameOptionsHeader();\n        xFrameHeaderValue = Objects.requireNonNullElse(xFrameHeaderPropertyValue,\n                StringUtils.defaultIfEmpty(filterConfig.getInitParameter(X_FRAME_OPTIONS_HEADER),\n                        X_FRAME_OPTIONS_HEADER_DEFAULT));\n        String contentSecurityHeaderPropertyValue = securityProperties.getContentSecurityPolicyHeader();\n        contentSecurityHeaderValue = Objects.requireNonNullElse(contentSecurityHeaderPropertyValue,\n                StringUtils.defaultIfEmpty(filterConfig.getInitParameter(CONTENT_SECURITY_POLICY_HEADER),\n                        CONTENT_SECURITY_POLICY_HEADER_DEFAULT));\n        super.init(filterConfig);\n    }\n\n    protected SecurityProperties getSecurityProperties() {\n        return PropertiesFactory.getSecurityProperties();\n    }\n\n    @Override\n    public void proceedWithFiltering(final ServletRequest request, final ServletResponse response,\n            final FilterChain chain) throws ServletException, IOException {\n        // casting to HTTPServlet(Request|Response)\n        final HttpServletRequest req = (HttpServletRequest) request;\n        final HttpServletResponse res = (HttpServletResponse) response;\n\n        // X-frame-options (ClickJacking)\n        if (!StringUtils.isBlank(xFrameHeaderValue)) {\n            res.setHeader(X_FRAME_OPTIONS_HEADER, xFrameHeaderValue);\n        }\n        if (!StringUtils.isBlank(contentSecurityHeaderValue)) {\n            res.setHeader(CONTENT_SECURITY_POLICY_HEADER, contentSecurityHeaderValue);\n        }\n\n        chain.doFilter(req, res);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/filter/PlatformUnderMaintenanceException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.filter;\n\npublic class PlatformUnderMaintenanceException extends RuntimeException {\n\n    private static final long serialVersionUID = 4836182063504328577L;\n\n    public PlatformUnderMaintenanceException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/filter/RestAPIAuthorizationFilter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.filter;\n\nimport java.io.IOException;\nimport java.util.Optional;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.ServletException;\nimport javax.servlet.ServletInputStream;\nimport javax.servlet.ServletRequest;\nimport javax.servlet.ServletResponse;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpSession;\n\nimport org.apache.commons.fileupload.servlet.ServletFileUpload;\nimport org.apache.commons.io.IOUtils;\nimport org.bonitasoft.console.common.server.filter.ExcludingPatternFilter;\nimport org.bonitasoft.console.common.server.filter.MultiReadHttpServletRequest;\nimport org.bonitasoft.console.common.server.login.servlet.PlatformLoginServlet;\nimport org.bonitasoft.console.common.server.preferences.properties.PropertiesFactory;\nimport org.bonitasoft.console.common.server.utils.SessionUtil;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.api.permission.APICallContext;\nimport org.bonitasoft.engine.exception.*;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.bonitasoft.engine.session.PlatformSession;\nimport org.bonitasoft.web.rest.server.framework.utils.ParsedRestRequestURI;\nimport org.bonitasoft.web.rest.server.framework.utils.RestRequestURIParser;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIMalformedUrlException;\nimport org.bonitasoft.web.toolkit.client.common.i18n.model.I18nLocaleDefinition;\nimport org.bonitasoft.web.toolkit.client.common.session.SessionDefinition;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Zhiheng Yang, Chong Zhao\n * @author Baptiste Mesta\n * @author Anthony Birembaut\n */\npublic class RestAPIAuthorizationFilter extends ExcludingPatternFilter {\n\n    public static final String PLATFORM_API_URI_REGEXP = \"^/(API|APIToolkit)/platform/.*\";\n\n    protected static final String AUTHORIZATION_FILTER_EXCLUDED_PAGES_PATTERN = \"^/(bonita/)?((apps/.+/)|(portal/resource/.+/))?(API|APIToolkit)/system/(i18ntranslation|feature)\";\n\n    /**\n     * Logger\n     */\n    protected static final Logger LOGGER = LoggerFactory.getLogger(RestAPIAuthorizationFilter.class.getName());\n\n    @Override\n    public void proceedWithFiltering(ServletRequest request, ServletResponse response, FilterChain chain)\n            throws ServletException {\n        HttpServletRequest httpServletRequest = (HttpServletRequest) request;\n        try {\n            // body of multi-parts requests is not needed when checking permissions\n            if (!ServletFileUpload.isMultipartContent(httpServletRequest)) {\n                //we need to use a MultiReadHttpServletRequest wrapper in order to be able to get the input stream twice (in the filter and in the API servlet)\n                httpServletRequest = new MultiReadHttpServletRequest(httpServletRequest);\n            }\n            String pathInfo = Optional.ofNullable(httpServletRequest.getPathInfo()).orElse(\"\");\n            String requestPath = httpServletRequest.getServletPath() + pathInfo;\n            HttpServletResponse httpServletResponse = (HttpServletResponse) response;\n            boolean isAuthorized;\n            if (requestPath.matches(PLATFORM_API_URI_REGEXP)) {\n                isAuthorized = platformAPIsCheck(httpServletRequest, httpServletResponse);\n            } else {\n                isAuthorized = tenantAPIsCheck(httpServletRequest, httpServletResponse);\n            }\n            if (isAuthorized) {\n                chain.doFilter(httpServletRequest, response);\n            }\n        } catch (final InvalidSessionException e) {\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(\"Invalid Bonita engine session: {}\", e.getMessage());\n            }\n            SessionUtil.sessionLogout(httpServletRequest.getSession());\n            ((HttpServletResponse) response).setStatus(HttpServletResponse.SC_UNAUTHORIZED);\n        } catch (final TenantStatusException e) {\n            if (LOGGER.isInfoEnabled()) {\n                LOGGER.info(\"Platform is probably under Maintenance : {}\", e.getMessage());\n            }\n            ((HttpServletResponse) response).setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);\n        } catch (final Exception e) {\n            if (LOGGER.isErrorEnabled()) {\n                LOGGER.error(e.getMessage(), e);\n            }\n            if (e instanceof APIException) {\n                throw new ServletException(e);\n            } else {\n                //wrap exception in APIException to avoid disclose too much information\n                throw new ServletException(new APIException(e));\n            }\n        }\n    }\n\n    protected boolean tenantAPIsCheck(final HttpServletRequest httpRequest, final HttpServletResponse httpResponse)\n            throws ServletException, IOException {\n        final APISession apiSession = (APISession) httpRequest.getSession()\n                .getAttribute(SessionUtil.API_SESSION_PARAM_KEY);\n        try {\n            if (apiSession == null) {\n                httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);\n                httpResponse.flushBuffer();\n                return false;\n            } else if (!checkPermissions(httpRequest)) {\n                httpResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);\n                httpResponse.flushBuffer();\n                return false;\n            } else {\n                return true;\n            }\n        } catch (APIMalformedUrlException e) {\n            LOGGER.info(\"Malformed API URL: {}\", e.getMessage());\n            httpResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST);\n            httpResponse.flushBuffer();\n            return false;\n        } catch (InvalidSessionException e) {\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(\"Invalid Bonita engine session.\", e);\n            }\n            httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);\n            SessionUtil.sessionLogout(httpRequest.getSession());\n            httpResponse.flushBuffer();\n            return false;\n        }\n    }\n\n    protected boolean platformAPIsCheck(final HttpServletRequest httpRequest, final HttpServletResponse httpResponse) {\n        final PlatformSession platformSession = (PlatformSession) httpRequest.getSession()\n                .getAttribute(PlatformLoginServlet.PLATFORM_SESSION_PARAM_KEY);\n        if (platformSession != null) {\n            return true;\n        } else {\n            httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);\n            return false;\n        }\n    }\n\n    protected boolean checkPermissions(final HttpServletRequest request) throws ServletException {\n        final ParsedRestRequestURI parsedURI = new RestRequestURIParser(request).parse();\n        return checkPermissions(request, parsedURI.getApiName(), parsedURI.getResourceName(),\n                parsedURI.getResourceQualifiers());\n    }\n\n    protected boolean checkPermissions(final HttpServletRequest request, final String apiName,\n            final String resourceName, final APIID resourceQualifiers)\n            throws ServletException {\n        final String method = request.getMethod();\n        final HttpSession session = request.getSession();\n        // userPermissions are of type: \"organization_visualization\"\n        //        @SuppressWarnings(\"unchecked\") final Set<String> userPermissions = (Set<String>) session.getAttribute(SessionUtil.PERMISSIONS_SESSION_PARAM_KEY);\n        final APISession apiSession = (APISession) session.getAttribute(SessionUtil.API_SESSION_PARAM_KEY);\n\n        final boolean apiAuthorizationsCheckEnabled = isApiAuthorizationsCheckEnabled();\n        if (!apiAuthorizationsCheckEnabled || apiSession.isTechnicalUser()) {\n            return true;\n        }\n        final String resourceQualifiersAsString = resourceQualifiers != null ? resourceQualifiers.toString() : null;\n        final String body = getRequestBody(request);\n        final APICallContext apiCallContext = new APICallContext(method, apiName, resourceName,\n                resourceQualifiersAsString, request.getQueryString(), body);\n        if (isAlwaysAuthorizedResource(apiCallContext)) {\n            return true;\n        }\n        try {\n            return enginePermissionsCheck(apiCallContext, apiSession);\n        } catch (BonitaException e) {\n            throw new ServletException(e);\n        }\n    }\n\n    protected boolean isAlwaysAuthorizedResource(final APICallContext apiCallContext) {\n        return apiCallContext.isGET()\n                && (isSingleResourceCall(apiCallContext, SessionDefinition.TOKEN)\n                        || isSingleResourceCall(apiCallContext, I18nLocaleDefinition.TOKEN));\n    }\n\n    private boolean isSingleResourceCall(final APICallContext apiCallContext, final String authorizedResourceName) {\n        return authorizedResourceName.equals(apiCallContext.getResourceName())\n                && \"system\".equals(apiCallContext.getApiName());\n    }\n\n    protected String getRequestBody(final HttpServletRequest request) throws ServletException {\n        try {\n            // body of multi-parts requests is not needed when checking permissions\n            if (ServletFileUpload.isMultipartContent(request)) {\n                if (LOGGER.isDebugEnabled()) {\n                    LOGGER.debug(\"[Multi-Part Request] not adding body to APIContext\");\n                }\n                return null;\n            }\n            final ServletInputStream inputStream = request.getInputStream();\n            return IOUtils.toString(inputStream, request.getCharacterEncoding());\n        } catch (final IOException e) {\n            throw new ServletException(e);\n        }\n    }\n\n    protected boolean isApiAuthorizationsCheckEnabled() {\n        return PropertiesFactory.getSecurityProperties().isAPIAuthorizationsCheckEnabled();\n    }\n\n    protected boolean enginePermissionsCheck(final APICallContext apiCallContext, final APISession apiSession)\n            throws ServerAPIException, BonitaHomeNotSetException, UnknownAPITypeException, ExecutionException {\n        return TenantAPIAccessor.getPermissionAPI(apiSession).isAuthorized(apiCallContext);\n    }\n\n    @Override\n    public String getDefaultExcludedPages() {\n        return AUTHORIZATION_FILTER_EXCLUDED_PAGES_PATTERN;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/filter/TokenGenerator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.filter;\n\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.console.common.server.api.token.APIToken;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Julien Reboul\n */\npublic class TokenGenerator {\n\n    protected static final Logger LOGGER = LoggerFactory.getLogger(TokenGenerator.class.getName());\n\n    public static final String API_TOKEN = \"api_token\";\n    public static final String X_BONITA_API_TOKEN = \"X-Bonita-API-Token\";\n\n    /**\n     * generate and store the CSRF security inside HTTP session\n     * or retrieve it token from the HTTP session\n     *\n     * @param session the HTTP session\n     * @return the CSRF security token\n     */\n    public String createOrLoadToken(final HttpSession session) {\n        Object apiTokenFromClient = session.getAttribute(API_TOKEN);\n        if (apiTokenFromClient == null) {\n            apiTokenFromClient = new APIToken().getToken();\n            session.setAttribute(API_TOKEN, apiTokenFromClient);\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(\"Bonita API Token generated: \" + apiTokenFromClient);\n            }\n        }\n        return apiTokenFromClient.toString();\n    }\n\n    /**\n     * set the CSRF security token to the HTTP response as HTTP Header.\n     *\n     * @param res the http response\n     * @param token the security token to set\n     */\n    public void setTokenToResponseHeader(final HttpServletResponse res, final String token) {\n        if (res.containsHeader(X_BONITA_API_TOKEN)) {\n            res.setHeader(X_BONITA_API_TOKEN, token);\n        } else {\n            res.addHeader(X_BONITA_API_TOKEN, token);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/filter/TokenGeneratorFilter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.filter;\n\nimport java.io.IOException;\n\nimport javax.servlet.Filter;\nimport javax.servlet.FilterChain;\nimport javax.servlet.FilterConfig;\nimport javax.servlet.ServletException;\nimport javax.servlet.ServletRequest;\nimport javax.servlet.ServletResponse;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.bonitasoft.console.common.server.login.PortalCookies;\n\n/**\n * @author Paul AMAR\n */\npublic class TokenGeneratorFilter implements Filter {\n\n    protected TokenGenerator tokenGenerator = new TokenGenerator();\n    protected PortalCookies portalCookies = new PortalCookies();\n\n    @Override\n    public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)\n            throws IOException, ServletException {\n        final HttpServletRequest req = (HttpServletRequest) request;\n        final HttpServletResponse res = (HttpServletResponse) response;\n\n        // Create\n        final String apiTokenFromClient = tokenGenerator.createOrLoadToken(req.getSession());\n        tokenGenerator.setTokenToResponseHeader(res, apiTokenFromClient);\n        portalCookies.addCSRFTokenCookieToResponse(req, res, apiTokenFromClient);\n        chain.doFilter(req, res);\n    }\n\n    @Override\n    public void init(final FilterConfig filterConfig) throws ServletException {\n\n    }\n\n    @Override\n    public void destroy() {\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/filter/TokenValidatorFilter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.filter;\n\nimport static org.apache.commons.lang3.StringUtils.isBlank;\n\nimport java.io.IOException;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.ServletException;\nimport javax.servlet.ServletRequest;\nimport javax.servlet.ServletResponse;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.console.common.server.filter.ExcludingPatternFilter;\nimport org.bonitasoft.console.common.server.filter.MultiReadHttpServletRequest;\nimport org.bonitasoft.console.common.server.preferences.properties.PropertiesFactory;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.multipart.MultipartHttpServletRequest;\nimport org.springframework.web.multipart.commons.CommonsMultipartResolver;\n\n/**\n * @author Paul AMAR\n */\npublic class TokenValidatorFilter extends ExcludingPatternFilter {\n\n    public static final String BEARER_HEADER_VERIFIED_ATTRIBUTE = \"bearerVerified\";\n\n    private static final String CSRF_TOKEN_PARAM = \"CSRFToken\";\n    private static final String CSRF_TOKEN_HEADER = \"X-Bonita-API-Token\";\n\n    protected static final String TOKEN_VALIDATOR_FILTER_EXCLUDED_PAGES_PATTERN = \"^/(bonita/)?((apps/.+/)|(portal/resource/.+/))?(API|APIToolkit)/system/(i18ntranslation|feature|session)\";\n\n    /**\n     * Logger\n     */\n    protected static final Logger LOGGER = LoggerFactory.getLogger(TokenValidatorFilter.class.getName());\n\n    @Override\n    public void proceedWithFiltering(ServletRequest request, ServletResponse response, FilterChain chain)\n            throws ServletException, IOException {\n\n        HttpServletRequest httpServletRequest = (HttpServletRequest) request;\n        HttpServletResponse httpServletResponse = (HttpServletResponse) response;\n        if (isCsrfProtectionEnabled() && !isSafeMethod(httpServletRequest.getMethod()) &&\n                !isBearerVerifiedRequest(httpServletRequest.getSession())) {\n            //we need to use a MultiReadHttpServletRequest wrapper in order to be able to get the inputstream twice (in the filter and in the API servlet)\n            MultiReadHttpServletRequest multiReadHttpServletRequest = new MultiReadHttpServletRequest(\n                    httpServletRequest);\n            String headerFromRequest = getCSRFToken(multiReadHttpServletRequest);\n            String apiToken = (String) multiReadHttpServletRequest.getSession().getAttribute(\"api_token\");\n\n            if (headerFromRequest == null || !headerFromRequest.equals(apiToken)) {\n                if (LOGGER.isDebugEnabled()) {\n                    LOGGER.debug(\"Token Validation failed, expected: \" + apiToken + \", received: \" + headerFromRequest);\n                }\n                httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);\n                response.flushBuffer();\n            } else {\n                chain.doFilter(multiReadHttpServletRequest, response);\n            }\n        } else {\n            chain.doFilter(request, response);\n        }\n    }\n\n    protected boolean isBearerVerifiedRequest(HttpSession httpSession) {\n        boolean isBearerVerified = false;\n        Object bearerSessionAttribute = httpSession.getAttribute(BEARER_HEADER_VERIFIED_ATTRIBUTE);\n        if (bearerSessionAttribute != null) {\n            isBearerVerified = Boolean.parseBoolean((String) bearerSessionAttribute);\n            httpSession.removeAttribute(BEARER_HEADER_VERIFIED_ATTRIBUTE);\n        }\n        return isBearerVerified;\n    }\n\n    // protected for testing\n    protected boolean isCsrfProtectionEnabled() {\n        return PropertiesFactory.getSecurityProperties().isCSRFProtectionEnabled();\n    }\n\n    /**\n     * Get CSRF token from request following order as below\n     * - In 'X-Bonita-API-Token' header\n     * - In 'CSRFToken' parameter\n     * - In 'CSRFToken' multipart body parameter\n     */\n    private String getCSRFToken(HttpServletRequest httpRequest) {\n        String token = httpRequest.getHeader(CSRF_TOKEN_HEADER);\n        if (isBlank(token)) {\n            token = httpRequest.getParameter(CSRF_TOKEN_PARAM);\n        }\n        if (isBlank(token) && isFormData(httpRequest.getContentType())) {\n            CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();\n            multipartResolver.setResolveLazily(true);\n            MultipartHttpServletRequest multiPartRequest = multipartResolver.resolveMultipart(httpRequest);\n            if (multiPartRequest.getParameterMap().containsKey(CSRF_TOKEN_PARAM)) {\n                token = multiPartRequest.getParameter(CSRF_TOKEN_PARAM);\n            }\n        }\n        return token;\n    }\n\n    private boolean isFormData(String contentType) {\n        return contentType != null && contentType.toLowerCase().contains(\"multipart/form-data\");\n    }\n\n    private boolean isSafeMethod(String method) {\n        return \"GET\".equalsIgnoreCase(method) || \"HEAD\".equalsIgnoreCase(method) || \"OPTIONS\".equalsIgnoreCase(method);\n    }\n\n    @Override\n    public String getDefaultExcludedPages() {\n        return TOKEN_VALIDATOR_FILTER_EXCLUDED_PAGES_PATTERN;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/servlet/LoginServlet.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.servlet;\n\nimport java.io.IOException;\nimport java.io.UnsupportedEncodingException;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.nio.charset.StandardCharsets;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.bonitasoft.console.common.server.auth.AuthenticationFailedException;\nimport org.bonitasoft.console.common.server.auth.AuthenticationManager;\nimport org.bonitasoft.console.common.server.auth.AuthenticationManagerFactory;\nimport org.bonitasoft.console.common.server.auth.AuthenticationManagerNotFoundException;\nimport org.bonitasoft.console.common.server.filter.PathSanitizer;\nimport org.bonitasoft.console.common.server.login.AccountLockedException;\nimport org.bonitasoft.console.common.server.login.HttpServletRequestAccessor;\nimport org.bonitasoft.console.common.server.login.LoginFailedException;\nimport org.bonitasoft.console.common.server.login.LoginManager;\nimport org.bonitasoft.console.common.server.login.utils.RedirectUrlBuilder;\nimport org.bonitasoft.console.common.server.login.utils.RedirectUrlHandler;\nimport org.bonitasoft.console.common.server.utils.LocaleUtils;\nimport org.bonitasoft.console.common.server.utils.SessionUtil;\nimport org.bonitasoft.console.common.server.utils.TenantsManagementUtils;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.server.login.LoginFailureTracker;\nimport org.bonitasoft.web.server.login.LoginFailureTrackerAccessor;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\n\n/**\n * @author Anthony Birembaut, Ruiheng Fan, Chong Zhao, Haojie Yuan\n */\npublic class LoginServlet extends HttpServlet {\n\n    /**\n     * serialVersionUID\n     */\n    private static final long serialVersionUID = -5326931127638029215L;\n\n    /**\n     * Logger\n     */\n    private static final Logger LOGGER = LoggerFactory.getLogger(LoginServlet.class.getName());\n\n    /**\n     * login fail message\n     */\n    protected static final String LOGIN_FAIL_MESSAGE = \"loginFailMessage\";\n\n    /**\n     * account locked message — must match the string literal in login.jsp (line 58)\n     */\n    protected static final String ACCOUNT_LOCKED_MESSAGE = \"accountLockedMessage\";\n\n    /*\n     * System property to allow login with GET from the development suite\n     */\n    public static final String ENABLE_DEV_SUITE_LOGIN = \"org.bonitasoft.web.login.get.enabled\";\n\n    private LoginFailureTracker loginFailureTracker;\n\n    @Override\n    public void init() throws ServletException {\n        super.init();\n        loginFailureTracker = LoginFailureTrackerAccessor.getLoginFailureTracker(getServletContext());\n    }\n\n    /**\n     * Necessary studio integration (username and password are passed in the URL in development mode)\n     *\n     * @deprecated\n     *             use {@link #doPost(HttpServletRequest, HttpServletResponse)} instead\n     */\n    @Override\n    @Deprecated(since = \"8.0\", forRemoval = false)\n    protected void doGet(final HttpServletRequest req, final HttpServletResponse resp)\n            throws ServletException, IOException {\n        if (!Boolean.parseBoolean(System.getProperty(ENABLE_DEV_SUITE_LOGIN))) {\n            resp.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);\n            return;\n        }\n        if (LOGGER.isTraceEnabled()) {\n            LOGGER.trace(\"query string : \" + dropPassword(req.getQueryString()));\n        }\n        doPost(req, resp);\n    }\n\n    @Override\n    protected void doPost(final HttpServletRequest request, final HttpServletResponse response)\n            throws ServletException {\n\n        // force post request body to UTF-8\n        try {\n            request.setCharacterEncoding(StandardCharsets.UTF_8.name());\n        } catch (final UnsupportedEncodingException e) {\n            // should never appear\n            throw new ServletException(e);\n        }\n        if (request.getContentType() != null\n                && !MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(\n                        MediaType.parseMediaType(request.getContentType()))) {\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(\n                        \"The only content type supported by this service is application/x-www-form-urlencoded. The content-type request header needs to be set accordingly.\");\n            }\n            response.setStatus(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);\n        } else {\n            handleLogin(request, response);\n        }\n    }\n\n    protected void handleLogin(final HttpServletRequest request, final HttpServletResponse response)\n            throws ServletException {\n        final boolean redirectAfterLogin = RedirectUrlHandler.shouldRedirectAfterLogin(request);\n        final String redirectURL = getRedirectUrl(request, redirectAfterLogin);\n        String locale = LocaleUtils.getUserLocaleAsString(request);\n        try {\n            doLogin(request, response);\n            final APISession apiSession = (APISession) request.getSession()\n                    .getAttribute(SessionUtil.API_SESSION_PARAM_KEY);\n            // if there is no redirect=true or redirectURL parameter in the request do nothing (API login), otherwise, redirect (Portal login)\n            if (redirectAfterLogin) {\n                if (apiSession.isTechnicalUser() || hasProfile(apiSession)) {\n                    response.sendRedirect(createRedirectUrl(redirectURL, locale));\n                } else {\n                    String loginURL = getAuthenticationManager()\n                            .getLoginPageURL(new HttpServletRequestAccessor(request), redirectURL);\n                    if (loginURL.startsWith(request.getContextPath() + AuthenticationManager.LOGIN_PAGE)) {\n                        request.setAttribute(LOGIN_FAIL_MESSAGE, \"noProfileForUser\");\n                        getServletContext().getRequestDispatcher(AuthenticationManager.LOGIN_PAGE).forward(request,\n                                response);\n                    } else {\n                        // if the login page is not the default jsp but an external URL the forward cannot work.\n                        // just respond with a 401\n                        if (LOGGER.isDebugEnabled()) {\n                            LOGGER.debug(\"No profiles for user\");\n                        }\n                        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED, \"noProfileForUser\");\n                    }\n                }\n            } else {\n                LocaleUtils.addOrReplaceLocaleCookieResponse(response, locale);\n                response.setStatus(HttpServletResponse.SC_NO_CONTENT);\n            }\n        } catch (final AuthenticationManagerNotFoundException e) {\n            final String message = \"Can't get login manager\";\n            if (LOGGER.isErrorEnabled()) {\n                LOGGER.error(message, e);\n            }\n            throw new ServletException(e);\n        } catch (final LoginFailedException e) {\n            handleException(request, response, redirectAfterLogin, e, locale);\n        } catch (final AuthenticationFailedException e) {\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(\"Authentication failed : \" + e.getMessage(), e);\n            }\n            handleException(request, response, redirectAfterLogin, e, locale);\n        } catch (final Exception e) {\n            LOGGER.error(\"Error while trying to log in\", e);\n            throw new ServletException(e);\n        }\n    }\n\n    protected boolean hasProfile(final APISession apiSession)\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return TenantsManagementUtils.hasProfileForUser(apiSession);\n    }\n\n    private void handleException(final HttpServletRequest request, final HttpServletResponse response,\n            final boolean redirectAfterLogin, final Exception exception, final String locale) throws ServletException {\n        if (redirectAfterLogin) {\n            try {\n                if (exception instanceof AccountLockedException) {\n                    request.setAttribute(LOGIN_FAIL_MESSAGE, ACCOUNT_LOCKED_MESSAGE);\n                } else {\n                    request.setAttribute(LOGIN_FAIL_MESSAGE, LOGIN_FAIL_MESSAGE);\n                }\n                String loginURL = request.getParameter(AuthenticationManager.LOGIN_URL_PARAM_NAME);\n                if (loginURL == null) {\n                    loginURL = createDefaultRedirectUrl(request, redirectAfterLogin);\n                } else {\n                    // Defense-in-depth: sanitize user-supplied loginURL before passing\n                    // to getRequestDispatcher(). Strip path parameters (semicolons) to\n                    // prevent ..;-based traversal, then normalize to collapse any ../\n                    String sanitizedLoginURL;\n                    try {\n                        sanitizedLoginURL = PathSanitizer.stripPathParameters(loginURL);\n                        String normalizedPath = new URI(sanitizedLoginURL).normalize().getPath();\n                        sanitizedLoginURL = normalizedPath != null ? normalizedPath\n                                : AuthenticationManager.LOGIN_PAGE;\n                        loginURL = createRedirectUrl(sanitizedLoginURL, locale);\n                    } catch (URISyntaxException uriEx) {\n                        LOGGER.warn(\"Invalid loginURL [{}], falling back to default login page: {}\",\n                                loginURL, uriEx.getMessage());\n                        loginURL = createDefaultRedirectUrl(request, redirectAfterLogin);\n                    }\n                }\n                if (loginURL.startsWith(request.getContextPath() + AuthenticationManager.LOGIN_PAGE)) {\n                    getServletContext().getRequestDispatcher(AuthenticationManager.LOGIN_PAGE).forward(request,\n                            response);\n                } else {\n                    // if the login page is not the default jsp but an external URL the forward cannot work.\n                    // just respond with a 401\n                    if (LOGGER.isDebugEnabled()) {\n                        LOGGER.debug(exception.getMessage());\n                    }\n                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED, LOGIN_FAIL_MESSAGE);\n                }\n            } catch (final Exception e) {\n                LOGGER.error(e.getMessage());\n                throw new ServletException(e);\n            }\n        } else {\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(exception.getMessage());\n            }\n            if (exception instanceof AccountLockedException) {\n                response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());\n                if (loginFailureTracker != null) {\n                    response.setHeader(HttpHeaders.RETRY_AFTER,\n                            String.valueOf(loginFailureTracker.getLockoutDurationSeconds()));\n                }\n            } else {\n                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);\n            }\n        }\n    }\n\n    private String getRedirectUrl(final HttpServletRequest request, final boolean redirectAfterLogin) {\n        String redirectURL = request.getParameter(AuthenticationManager.REDIRECT_URL);\n        if (LOGGER.isDebugEnabled()) {\n            LOGGER.debug(\"redirecting to : \" + redirectURL);\n        }\n        if (redirectAfterLogin && (redirectURL == null || redirectURL.isEmpty())) {\n            redirectURL = AuthenticationManager.DEFAULT_DIRECT_URL;\n        } else {\n            if (redirectURL != null) {\n                redirectURL = new URLProtector().protectRedirectUrl(redirectURL);\n            }\n        }\n        return redirectURL;\n    }\n\n    private String createRedirectUrl(final String redirectURL, final String locale) {\n        RedirectUrlBuilder redirectUrlBuilder = new RedirectUrlBuilder(redirectURL);\n        redirectUrlBuilder.appendParameter(LocaleUtils.PORTAL_LOCALE_PARAM, locale);\n        return redirectUrlBuilder.build().getUrl();\n    }\n\n    private String createDefaultRedirectUrl(HttpServletRequest request, boolean redirectAfterLogin)\n            throws ServletException, AuthenticationManagerNotFoundException {\n        final String redirectURL = getRedirectUrl(request, redirectAfterLogin);\n        return getAuthenticationManager()\n                .getLoginPageURL(new HttpServletRequestAccessor(request), redirectURL);\n    }\n\n    protected void doLogin(final HttpServletRequest request, final HttpServletResponse response)\n            throws AuthenticationManagerNotFoundException, LoginFailedException, ServletException,\n            AuthenticationFailedException {\n        final LoginManager loginManager = getLoginManager();\n        loginManager.login(request, response);\n    }\n\n    protected AuthenticationManager getAuthenticationManager() throws AuthenticationManagerNotFoundException {\n        return AuthenticationManagerFactory.getAuthenticationManager();\n    }\n\n    protected LoginManager getLoginManager() {\n        return new LoginManager(loginFailureTracker);\n    }\n\n    static String dropPassword(final String content) {\n        String tmp = content;\n        if (content != null && content.contains(\"password\")) {\n            tmp = tmp.replaceAll(\"[&]?password=([^&|#]*)?\", \"\");\n        }\n        return tmp;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/servlet/LogoutServlet.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.servlet;\n\nimport java.io.IOException;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.console.common.server.auth.AuthenticationManager;\nimport org.bonitasoft.console.common.server.auth.AuthenticationManagerFactory;\nimport org.bonitasoft.console.common.server.auth.AuthenticationManagerNotFoundException;\nimport org.bonitasoft.console.common.server.login.HttpServletRequestAccessor;\nimport org.bonitasoft.console.common.server.login.LoginFailedException;\nimport org.bonitasoft.console.common.server.login.credentials.UserLogger;\nimport org.bonitasoft.console.common.server.login.utils.LoginUrl;\nimport org.bonitasoft.console.common.server.login.utils.RedirectUrlBuilder;\nimport org.bonitasoft.console.common.server.login.utils.RedirectUrlHandler;\nimport org.bonitasoft.console.common.server.utils.SessionUtil;\nimport org.bonitasoft.engine.session.APISession;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Servlet used to logout from the applications\n *\n * @author Zhiheng Yang, Chong Zhao\n */\npublic class LogoutServlet extends HttpServlet {\n\n    /**\n     * UID\n     */\n    private static final long serialVersionUID = 739607235407639011L;\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(LogoutServlet.class.getName());\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    protected void doGet(final HttpServletRequest request, final HttpServletResponse response)\n            throws ServletException, IOException {\n        logout(request, response);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    protected void doPost(final HttpServletRequest request, final HttpServletResponse response)\n            throws ServletException, IOException {\n        logout(request, response);\n    }\n\n    /**\n     * Console logout\n     */\n    protected void logout(final HttpServletRequest request, final HttpServletResponse response)\n            throws ServletException {\n        if (LOGGER.isDebugEnabled()) {\n            LOGGER.debug(\"Logging out from Bonita\");\n        }\n        final HttpServletRequestAccessor requestAccessor = new HttpServletRequestAccessor(request);\n        final HttpSession session = requestAccessor.getHttpSession();\n        final APISession apiSession = requestAccessor.getApiSession();\n        try {\n            engineLogout(apiSession);\n            SessionUtil.sessionLogout(session);\n\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(\"Logged out from Bonita\");\n            }\n\n            if (RedirectUrlHandler.shouldRedirectAfterLogout(request)) {\n                final String loginPage = getURLToRedirectTo(requestAccessor);\n                response.sendRedirect(loginPage);\n            }\n        } catch (final Exception e) {\n            if (LOGGER.isErrorEnabled()) {\n                LOGGER.error(\"Error while performing the logout\", e);\n            }\n            throw new ServletException(e);\n        }\n    }\n\n    // protected for test stubbing\n    protected AuthenticationManager getAuthenticationManager() throws ServletException {\n        try {\n            return AuthenticationManagerFactory.getAuthenticationManager();\n        } catch (final AuthenticationManagerNotFoundException e) {\n            throw new ServletException(e);\n        }\n    }\n\n    protected String getURLToRedirectTo(final HttpServletRequestAccessor requestAccessor) throws ServletException {\n        final AuthenticationManager authenticationManager = getAuthenticationManager();\n        final HttpServletRequest request = requestAccessor.asHttpServletRequest();\n\n        final String redirectURL = createRedirectUrl(requestAccessor);\n\n        final String logoutPage = authenticationManager.getLogoutPageURL(requestAccessor, redirectURL);\n        String redirectionPage;\n        if (logoutPage != null) {\n            redirectionPage = logoutPage;\n        } else {\n            final String loginPageURLFromRequest = request.getParameter(AuthenticationManager.LOGIN_URL_PARAM_NAME);\n            if (loginPageURLFromRequest != null) {\n                redirectionPage = sanitizeLoginPageUrl(loginPageURLFromRequest);\n            } else {\n                LoginUrl loginPageURL = new LoginUrl(authenticationManager, redirectURL, requestAccessor);\n                redirectionPage = loginPageURL.getLocation();\n            }\n        }\n        return redirectionPage;\n    }\n\n    protected String createRedirectUrl(final HttpServletRequestAccessor requestAccessor)\n            throws ServletException {\n        return RedirectUrlHandler.retrieveRedirectUrl(requestAccessor);\n    }\n\n    protected String sanitizeLoginPageUrl(final String loginURL) {\n        return new RedirectUrlBuilder(new URLProtector().protectRedirectUrl(loginURL)).build().getUrl();\n    }\n\n    protected void engineLogout(final APISession apiSession) throws LoginFailedException {\n        if (apiSession != null) {\n            getUserLogger().doLogout(apiSession);\n        }\n    }\n\n    protected UserLogger getUserLogger() {\n        return new UserLogger();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/servlet/PlatformLoginServlet.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.servlet;\n\nimport java.io.IOException;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.bonitasoft.console.common.server.auth.AuthenticationManager;\nimport org.bonitasoft.console.common.server.login.PortalCookies;\nimport org.bonitasoft.console.common.server.login.filter.TokenGenerator;\nimport org.bonitasoft.engine.api.PlatformAPIAccessor;\nimport org.bonitasoft.engine.api.PlatformLoginAPI;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.platform.InvalidPlatformCredentialsException;\nimport org.bonitasoft.engine.session.PlatformSession;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Ruiheng Fan, Haojie Yuan\n */\npublic class PlatformLoginServlet extends HttpServlet {\n\n    /**\n     * engine PlatformSession atribute name in HTTP session\n     */\n    public static final String PLATFORM_SESSION_PARAM_KEY = \"platformSession\";\n    /**\n     * the request param for the username\n     */\n    protected static final String USERNAME_PARAM = \"username\";\n    /**\n     * the request param for the password\n     */\n    protected static final String PASSWORD_PARAM = \"password\";\n    /**\n     * login fail message\n     */\n    protected static final String LOGIN_FAIL_MESSAGE = \"loginFailMessage\";\n\n    /**\n     * serialVersionUID\n     */\n    private static final long serialVersionUID = -5326931127638029215L;\n    /**\n     * Logger\n     */\n    private static final Logger LOGGER = LoggerFactory.getLogger(PlatformLoginServlet.class.getName());\n\n    /**\n     * the URL of the login page\n     */\n    // FIXME: page does not exist:\n    protected static final String LOGIN_PAGE = \"/platformLogin.jsp\";\n\n    // FIXME: page does not exist:\n    protected static final String PLATFORM_PAGE = \"platform/BonitaPlatform.html#?_p=Platform\";\n\n    public static final String ERROR_MESSAGE = \"Error while logging in to the platform\";\n\n    protected final TokenGenerator tokenGenerator = new TokenGenerator();\n    protected final PortalCookies portalCookies = new PortalCookies();\n\n    @Override\n    protected void doPost(final HttpServletRequest request, final HttpServletResponse response)\n            throws ServletException, IOException {\n        PlatformSession platformSession;\n        PlatformLoginAPI platformLoginAPI;\n        final String username = request.getParameter(USERNAME_PARAM);\n        final String password = request.getParameter(PASSWORD_PARAM);\n\n        String redirectStr = request.getParameter(AuthenticationManager.REDIRECT_AFTER_LOGIN_PARAM_NAME);\n        boolean redirectAfterLogin = Boolean.parseBoolean(redirectStr);\n\n        try {\n            platformLoginAPI = getPlatformLoginAPI();\n            platformSession = platformLoginAPI.login(username, password);\n            request.getSession().setAttribute(PLATFORM_SESSION_PARAM_KEY, platformSession);\n            String csrfToken = tokenGenerator.createOrLoadToken(request.getSession());\n            portalCookies.addCSRFTokenCookieToResponse(request, response, csrfToken);\n\n            if (redirectAfterLogin) {\n                response.sendRedirect(PLATFORM_PAGE);\n            }\n        } catch (final InvalidPlatformCredentialsException e) {\n            LOGGER.trace(\"Wrong username or password\", e);\n            if (redirectAfterLogin) {\n                response.sendError(HttpServletResponse.SC_FORBIDDEN, \"Wrong username or password\");\n            } else {\n                response.setStatus(HttpServletResponse.SC_FORBIDDEN);\n            }\n        } catch (final Exception e) {\n            LOGGER.error(ERROR_MESSAGE, e);\n            if (redirectAfterLogin) {\n                try {\n                    request.setAttribute(LOGIN_FAIL_MESSAGE, LOGIN_FAIL_MESSAGE);\n                    getServletContext().getRequestDispatcher(LOGIN_PAGE).forward(request, response);\n                } catch (final IOException ioe) {\n                    LOGGER.error(\"Error while redirecting to login.jsp\", ioe);\n                    throw new ServletException(ERROR_MESSAGE, e);\n                }\n            } else {\n                throw new ServletException(ERROR_MESSAGE, e);\n            }\n        }\n\n    }\n\n    PlatformLoginAPI getPlatformLoginAPI()\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return PlatformAPIAccessor.getPlatformLoginAPI();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/servlet/PlatformLogoutServlet.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.servlet;\n\nimport java.io.IOException;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.console.common.server.auth.AuthenticationManager;\nimport org.bonitasoft.engine.api.PlatformAPIAccessor;\nimport org.bonitasoft.engine.api.PlatformLoginAPI;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.session.PlatformSession;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Servlet used to logout from the applications\n *\n * @author Ruiheng Fan\n */\npublic class PlatformLogoutServlet extends HttpServlet {\n\n    /**\n     * UID\n     */\n    private static final long serialVersionUID = 739607235407639011L;\n\n    /**\n     * the URL of the login page\n     */\n    // FIXME: page does not exist:\n    protected final String PLATFORM_LOGIN_PAGE = \"platformLogin.jsp\";\n\n    /**\n     * Logger\n     */\n    private static final Logger LOGGER = LoggerFactory.getLogger(PlatformLogoutServlet.class.getName());\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    protected void doGet(final HttpServletRequest request, final HttpServletResponse response)\n            throws ServletException, IOException {\n        logout(request, response);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    protected void doPost(final HttpServletRequest request, final HttpServletResponse response)\n            throws ServletException, IOException {\n        logout(request, response);\n    }\n\n    /**\n     * Console logout\n     */\n    protected void logout(final HttpServletRequest request, final HttpServletResponse response)\n            throws ServletException, IOException {\n        final HttpSession session = request.getSession();\n\n        final PlatformSession platformSession = (PlatformSession) session\n                .getAttribute(PlatformLoginServlet.PLATFORM_SESSION_PARAM_KEY);\n        if (platformSession != null) {\n            try {\n                final PlatformLoginAPI platformLoginAPI = PlatformAPIAccessor.getPlatformLoginAPI();\n                platformLoginAPI.logout(platformSession);\n            } catch (final BonitaException e) {\n                if (LOGGER.isErrorEnabled()) {\n                    LOGGER.error(\"Error while performing the logout\", e);\n                }\n            }\n        }\n        session.removeAttribute(PlatformLoginServlet.PLATFORM_SESSION_PARAM_KEY);\n        session.invalidate();\n\n        String redirectStr = request.getParameter(AuthenticationManager.REDIRECT_AFTER_LOGIN_PARAM_NAME);\n        if (Boolean.parseBoolean(redirectStr)) {\n            response.sendRedirect(PLATFORM_LOGIN_PAGE);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/servlet/URLProtector.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.servlet;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * @author Paul AMAR\n */\npublic class URLProtector {\n\n    protected final List<String> tokens = Arrays.asList(\"https\", \"http\", \"www\", \"HTTPS\", \"HTTP\", \"WWW\", \"//\");\n\n    public String protectRedirectUrl(String redirectUrl) {\n        if (redirectUrl != null && !redirectUrl.startsWith(\"portal\")) {\n            return removeTokenFromUrl(redirectUrl, new ArrayList<>(tokens));\n        }\n        return redirectUrl;\n    }\n\n    private String removeTokenFromUrl(String redirectUrl, List<String> tokens) {\n        if (tokens.size() > 0) {\n            return removeTokenFromUrl(redirectUrl.replaceAll(tokens.remove(0), \"\"), tokens);\n        }\n        return redirectUrl;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/utils/LoginUrl.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.utils;\n\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\n\nimport javax.servlet.ServletException;\n\nimport org.bonitasoft.console.common.server.auth.AuthenticationManager;\nimport org.bonitasoft.console.common.server.login.HttpServletRequestAccessor;\n\n/**\n * @author Vincent Elcrin\n */\npublic class LoginUrl {\n\n    private final String location;\n\n    /**\n     * @throws ServletException\n     */\n    public LoginUrl(final AuthenticationManager authenticationManager, final String redirectUrl,\n            final HttpServletRequestAccessor request) throws ServletException {\n        location = getLoginPageUrl(authenticationManager, redirectUrl, request);\n    }\n\n    public String getLocation() {\n        return location;\n    }\n\n    private String getLoginPageUrl(final AuthenticationManager authenticationManager, final String redirectURL,\n            final HttpServletRequestAccessor request)\n            throws ServletException {\n        return authenticationManager.getLoginPageURL(request, URLEncoder.encode(redirectURL, StandardCharsets.UTF_8));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/utils/RedirectUrl.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.utils;\n\n/**\n * @author Vincent Elcrin\n */\npublic class RedirectUrl {\n\n    private final String url;\n\n    protected RedirectUrl(String url) {\n        this.url = url;\n    }\n\n    public String getUrl() {\n        return url;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/utils/RedirectUrlBuilder.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.utils;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.bonitasoft.console.common.server.auth.AuthenticationManager;\nimport org.bonitasoft.console.common.server.utils.UrlBuilder;\n\n/**\n * @author Vincent Elcrin\n */\npublic class RedirectUrlBuilder {\n\n    private final UrlBuilder urlBuilder;\n\n    private final List<String> blackList = List.of(\n            AuthenticationManager.REDIRECT_URL);\n\n    public RedirectUrlBuilder(final String redirectUrl) {\n        urlBuilder = new UrlBuilder(redirectUrl != null ? redirectUrl : \"\");\n    }\n\n    public RedirectUrl build() {\n        return new RedirectUrl(urlBuilder.build());\n\n    }\n\n    public void appendParameters(final Map<String, String[]> parameters) {\n        for (final Entry<String, String[]> next : parameters.entrySet()) {\n            appendParameter(next.getKey(), next.getValue());\n        }\n    }\n\n    public void appendParameter(String name, String... values) {\n        if (!isBlackListed(name)) {\n            urlBuilder.appendParameter(name, values);\n        }\n    }\n\n    public void removeParameter(String name) {\n        urlBuilder.removeParameter(name);\n    }\n\n    private boolean isBlackListed(final String key) {\n        return blackList.contains(key);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/utils/RedirectUrlHandler.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.utils;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport org.bonitasoft.console.common.server.auth.AuthenticationManager;\nimport org.bonitasoft.console.common.server.login.HttpServletRequestAccessor;\n\npublic class RedirectUrlHandler {\n\n    public static boolean shouldRedirectAfterLogin(final HttpServletRequest request) {\n        final String redirectAfterLogin = request.getParameter(AuthenticationManager.REDIRECT_AFTER_LOGIN_PARAM_NAME);\n        final String redirectURL = request.getParameter(AuthenticationManager.REDIRECT_URL);\n        // If there is a redirect param in the request use it otherwise check if there is a redirect URL\n        return redirectAfterLogin != null ? Boolean.parseBoolean(redirectAfterLogin) : redirectURL != null;\n    }\n\n    public static boolean shouldRedirectAfterLogout(final HttpServletRequest request) {\n        final String redirectAfterLogin = request.getParameter(AuthenticationManager.REDIRECT_AFTER_LOGIN_PARAM_NAME);\n        final String redirectURL = request.getParameter(AuthenticationManager.REDIRECT_URL);\n        final String loginPageURL = request.getParameter(AuthenticationManager.LOGIN_URL_PARAM_NAME);\n        // If there is a redirect param in the request use it otherwise check if there is a redirect or login URL\n        return redirectAfterLogin != null ? Boolean.parseBoolean(redirectAfterLogin)\n                : (redirectURL != null || loginPageURL != null);\n    }\n\n    public static String retrieveRedirectUrl(final HttpServletRequestAccessor request, String... parametersToRemove) {\n        final String redirectUrlFromRequest = request.getRedirectUrl();\n        String redirectUrl = redirectUrlFromRequest != null ? redirectUrlFromRequest : getDefaultRedirectUrl();\n        RedirectUrlBuilder redirectUrlBuilder = new RedirectUrlBuilder(redirectUrl);\n        for (String parameterToRemove : parametersToRemove) {\n            redirectUrlBuilder.removeParameter(parameterToRemove);\n        }\n        return redirectUrlBuilder.build().getUrl();\n    }\n\n    protected static String getDefaultRedirectUrl() {\n        return AuthenticationManager.DEFAULT_DIRECT_URL;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/ApplicationAuthorizationsHelper.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.page;\n\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.livingapps.ApplicationModel;\nimport org.bonitasoft.livingapps.ApplicationModelFactory;\n\npublic class ApplicationAuthorizationsHelper {\n\n    private final APISession apiSession;\n    private final ApplicationModelFactory applicationFactory;\n\n    public ApplicationAuthorizationsHelper(final APISession apiSession,\n            final ApplicationModelFactory applicationModelFactory) {\n        this.apiSession = apiSession;\n        this.applicationFactory = applicationModelFactory;\n    }\n\n    public boolean isAuthorized(final String applicationToken) {\n        try {\n            final ApplicationModel application = applicationFactory.createApplicationModel(applicationToken);\n            return application.authorize(apiSession);\n        } catch (final Exception e) {\n            return false;\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/BDMClientDependenciesResolver.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.page;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.apache.commons.io.FileUtils;\nimport org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.api.TenantAdministrationAPI;\nimport org.bonitasoft.engine.business.data.BusinessDataRepositoryException;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.io.IOUtil;\nimport org.bonitasoft.engine.session.APISession;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class BDMClientDependenciesResolver {\n\n    private final APISession session;\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(BDMClientDependenciesResolver.class.getName());\n\n    private final Set<String> dependenciesNames = new HashSet<>();\n\n    public BDMClientDependenciesResolver(APISession session) {\n        this.session = session;\n    }\n\n    public URL[] getBDMDependencies() throws IOException {\n        if (!isBDMDeployed()) {\n            return new URL[0];\n        }\n        final File currentBDMFolder = getCurrentBDMFolder();\n        if (shouldUpdateBDMDependencies(currentBDMFolder)) {\n            cleanBDMFolder(currentBDMFolder);\n            createBDMClientFolder(currentBDMFolder);\n        }\n        return getBDMLibrariesURLs(currentBDMFolder);\n    }\n\n    private boolean isBDMDeployed() {\n        return getBusinessDataModelVersion() != null;\n    }\n\n    private boolean shouldUpdateBDMDependencies(File currentBDMFolder) {\n        return !currentBDMFolder.exists() || currentBDMFolder.listFiles().length == 0;\n    }\n\n    private File getCurrentBDMFolder() {\n        File bdmWorkDir = null;\n        final String businessDataModelVersion = getBusinessDataModelVersion();\n        if (businessDataModelVersion != null) {\n            bdmWorkDir = new File(WebBonitaConstantsUtils.getTenantInstance().geBDMWorkFolder(),\n                    businessDataModelVersion);\n        }\n        return bdmWorkDir;\n    }\n\n    private void cleanBDMFolder(final File currentBDMFolder) {\n        final File parentFile = currentBDMFolder.getParentFile();\n        if (parentFile != null && parentFile.exists()) {\n            for (final File previousDeployedBDM : parentFile.listFiles()) {\n                if (previousDeployedBDM.isDirectory()) {\n                    try {\n                        FileUtils.deleteDirectory(previousDeployedBDM);\n                    } catch (final IOException e) {\n                        if (LOGGER.isWarnEnabled()) {\n                            LOGGER.warn(\"Unable to delete obsolete BDM libraries\", e);\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    private void createBDMClientFolder(final File bdmWorkDir) {\n        if (!bdmWorkDir.exists()) {\n            bdmWorkDir.mkdirs();\n        }\n        ByteArrayInputStream inpuStream = null;\n        try {\n            final TenantAdministrationAPI tenantAdministrationAPI = getTenantAdminstrationAPI();\n            inpuStream = new ByteArrayInputStream(tenantAdministrationAPI.getClientBDMZip());\n            IOUtil.unzipToFolder(inpuStream, bdmWorkDir);\n        } catch (final BonitaHomeNotSetException | IOException | BusinessDataRepositoryException | ServerAPIException\n                | UnknownAPITypeException e) {\n            final String message = \"Unable to create the class loader for the BDM libraries\";\n            if (LOGGER.isErrorEnabled()) {\n                LOGGER.error(message, e);\n            }\n        }\n    }\n\n    protected TenantAdministrationAPI getTenantAdminstrationAPI()\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return TenantAPIAccessor.getTenantAdministrationAPI(session);\n    }\n\n    public String getBusinessDataModelVersion() {\n        try {\n            final TenantAdministrationAPI tenantAdministrationAPI = getTenantAdminstrationAPI();\n            return tenantAdministrationAPI.getBusinessDataModelVersion();\n        } catch (final Exception e) {\n            LOGGER.error(\"Unable to retrieve business data model version\", e);\n            return null;\n        }\n    }\n\n    private URL[] getBDMLibrariesURLs(final File bdmFolder) throws IOException {\n        final List<URL> urls = new ArrayList<>();\n        for (final File file : bdmFolder.listFiles()) {\n            if (file.getName().endsWith(\".jar\")) {\n                dependenciesNames.add(file.getName());\n                urls.add(file.toURI().toURL());\n            }\n        }\n        return urls.toArray(new URL[urls.size()]);\n    }\n\n    public boolean isABDMDependency(String resourceName) {\n        return dependenciesNames.contains(resourceName);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/CustomPageChildFirstClassLoader.java",
    "content": "/**\n * Copyright (C) 2011-2015 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.page;\n\nimport static org.apache.commons.io.FileUtils.deleteQuietly;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.URL;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport org.apache.commons.io.FileUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Charles Souillard\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Romain Bioteau\n *         A Classloader adding given custom page resources and bdm resources in its classpath.\n *         This classloader is versioned with a runtime bdm version and should be discard when bdm is updated\n */\npublic class CustomPageChildFirstClassLoader extends MonoParentJarFileClassLoader implements VersionedClassloader {\n\n    protected final Map<String, byte[]> nonJarResources = new HashMap<>();\n\n    private boolean isActive = true;\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(CustomPageChildFirstClassLoader.class.getName());\n\n    private final CustomPageDependenciesResolver customPageDependenciesResolver;\n\n    private final BDMClientDependenciesResolver bdmDependenciesResolver;\n\n    private final String version;\n\n    CustomPageChildFirstClassLoader(String pageName,\n            CustomPageDependenciesResolver customPageDependenciesResolver,\n            BDMClientDependenciesResolver bdmDependenciesResolver,\n            ClassLoader parent) {\n        super(pageName, new URL[] {}, parent);\n        this.customPageDependenciesResolver = customPageDependenciesResolver;\n        this.bdmDependenciesResolver = bdmDependenciesResolver;\n        this.version = bdmDependenciesResolver.getBusinessDataModelVersion();\n    }\n\n    public void addCustomPageResources() throws IOException {\n        addBDMDependencies();\n        addOtherDependencies();\n    }\n\n    private void addBDMDependencies() {\n        try {\n            addURLs(bdmDependenciesResolver.getBDMDependencies());\n        } catch (final IOException e) {\n            LOGGER.error(\"Failed to add BDM dependencies in ClassLoader\", e);\n        }\n    }\n\n    private void addOtherDependencies() throws IOException {\n        final Map<String, byte[]> customPageDependencies = customPageDependenciesResolver\n                .resolveCustomPageDependencies();\n        for (final Map.Entry<String, byte[]> resource : customPageDependencies.entrySet()) {\n            if (resource.getKey().matches(\".*\\\\.jar\") && !bdmDependenciesResolver.isABDMDependency(resource.getKey())) {\n                final byte[] data = resource.getValue();\n                final File file = File.createTempFile(resource.getKey(), null,\n                        customPageDependenciesResolver.getTempFolder());\n                file.deleteOnExit();\n                FileUtils.writeByteArrayToFile(file, data);\n                addURL(new File(file.getAbsolutePath()).toURI().toURL());\n            } else {\n                nonJarResources.put(resource.getKey(), resource.getValue());\n            }\n        }\n    }\n\n    @Override\n    public InputStream getResourceAsStream(final String name) {\n        /*\n         * if (!isActive) {\n         * throw new RuntimeException(this.toString() + \" is not active anymore. Don't use it.\");\n         * }\n         */\n        InputStream is = getResourceAsStreamInternal(name);\n        if (is == null && name.length() > 0 && name.charAt(0) == '/') {\n            is = getResourceAsStreamInternal(name.substring(1));\n        }\n        return is;\n    }\n\n    protected InputStream getResourceAsStreamInternal(final String name) {\n        final byte[] classData = loadProcessResource(name);\n        if (classData != null) {\n            return new ByteArrayInputStream(classData);\n        }\n        return getResourceAsStreamRegular(name);\n    }\n\n    protected InputStream getResourceAsStreamRegular(final String name) {\n        return super.getResourceAsStream(name);\n    }\n\n    private byte[] loadProcessResource(final String resourceName) {\n        return nonJarResources.containsKey(resourceName) ? nonJarResources.get(resourceName) : null;\n    }\n\n    @Override\n    protected Class<?> loadClass(final String name, final boolean resolve) throws ClassNotFoundException {\n        /*\n         * if (!isActive) {\n         * throw new RuntimeException(this.toString() + \" is not active anymore. Don't use it.\");\n         * }\n         */\n        Class<?> c = null;\n        c = findLoadedClass(name);\n        if (c == null) {\n            try {\n                c = findClass(name);\n            } catch (final ClassNotFoundException e) {\n                // ignore\n            } catch (final LinkageError le) {\n                // might be because of a duplicate loading (concurrency loading), retry to find it one time See BS-2483\n                c = findLoadedClass(name);\n                if (c == null) {\n                    // was not because of duplicate loading: throw the exception\n                    throw le;\n                }\n            }\n        }\n\n        if (c == null) {\n            c = getParent().loadClass(name);\n        }\n\n        if (resolve) {\n            resolveClass(c);\n        }\n        return c;\n    }\n\n    // never called. Is it normal?\n    public void release() {\n        deleteQuietly(customPageDependenciesResolver.getTempFolder());\n        isActive = false;\n    }\n\n    @Override\n    public String toString() {\n        return super.toString() + \", name=\" + getName() + \", isActive: \" + isActive + \", parent= \" + getParent();\n    }\n\n    @Override\n    public String getVersion() {\n        return version;\n    }\n\n    @Override\n    public boolean hasVersion(String version) {\n        return Objects.equals(getVersion(), version);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/CustomPageDependenciesResolver.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.page;\n\nimport static org.apache.commons.io.FileUtils.readFileToByteArray;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.FileVisitResult;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.SimpleFileVisitor;\nimport java.nio.file.attribute.BasicFileAttributes;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.apache.commons.io.FileUtils;\nimport org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class CustomPageDependenciesResolver {\n\n    private static final String LIB_FOLDER_NAME = \"lib\";\n\n    static final Map<String, File> PAGES_LIB_TMPDIR = new HashMap<>();\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(CustomPageDependenciesResolver.class.getName());\n\n    private File libTempFolder;\n\n    private final WebBonitaConstantsUtils webBonitaConstantsUtils;\n\n    private final File pageDirectory;\n\n    private final String pageName;\n\n    public CustomPageDependenciesResolver(final String pageName,\n            final File pageDirectory,\n            final WebBonitaConstantsUtils webBonitaConstantsUtils) {\n        this.pageName = pageName;\n        this.pageDirectory = pageDirectory;\n        this.webBonitaConstantsUtils = webBonitaConstantsUtils;\n    }\n\n    public Map<String, byte[]> resolveCustomPageDependencies() {\n        final File customPageLibDirectory = new File(pageDirectory, LIB_FOLDER_NAME);\n        if (customPageLibDirectory.exists()) {\n            this.libTempFolder = new File(this.webBonitaConstantsUtils.getTempFolder(), pageName\n                    + new Date().getTime());\n            if (!this.libTempFolder.exists()) {\n                this.libTempFolder.mkdirs();\n            }\n            removePageLibTempFolder(pageName);\n            PAGES_LIB_TMPDIR.put(pageName, this.libTempFolder);\n            return loadLibraries(customPageLibDirectory);\n        }\n        return Collections.emptyMap();\n    }\n\n    private Map<String, byte[]> loadLibraries(final File customPageLibDirectory) {\n        final Map<String, byte[]> result = new HashMap<>();\n        try {\n            Files.walkFileTree(customPageLibDirectory.toPath(), new SimpleFileVisitor<>() {\n\n                @Override\n                public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {\n                    final File currentFile = file.toFile();\n                    result.put(currentFile.getName(), readFileToByteArray(currentFile));\n                    return super.visitFile(file, attrs);\n                }\n\n            });\n        } catch (final IOException e) {\n            LOGGER.error(e.getMessage(), e);\n        }\n        return result;\n    }\n\n    public static File removePageLibTempFolder(final String pageName) {\n        final File libTempFolder = PAGES_LIB_TMPDIR.remove(pageName);\n        if (libTempFolder != null) {\n            try {\n                FileUtils.deleteDirectory(libTempFolder);\n            } catch (final IOException e) {\n                if (LOGGER.isWarnEnabled()) {\n                    LOGGER.warn(\"The custom page temporary lib directory \" + libTempFolder.getPath()\n                            + \" cannot be deleted. This is likely to be due to a JDK bug on Windows. You can safely delete it after a server restart.\");\n                }\n            }\n        }\n        return libTempFolder;\n    }\n\n    public File getTempFolder() {\n        if (libTempFolder == null) {\n            throw new IllegalStateException(\"Custom page dependencies must be resolved first.\");\n        }\n        return libTempFolder;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/CustomPageRequestModifier.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.page;\n\nimport java.io.IOException;\nimport java.net.URI;\nimport java.net.URISyntaxException;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.bonitasoft.console.common.server.filter.PathSanitizer;\nimport org.bonitasoft.web.toolkit.client.common.util.StringUtil;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.util.UriUtils;\n\n/**\n * @author Julien Mege\n */\npublic class CustomPageRequestModifier {\n\n    /**\n     * Logger\n     */\n    private static final Logger LOGGER = LoggerFactory.getLogger(CustomPageRequestModifier.class.getName());\n\n    public void redirectToValidPageUrl(final HttpServletRequest request, final HttpServletResponse response)\n            throws IOException {\n        final StringBuilder taskURLBuilder = new StringBuilder(request.getContextPath());\n        taskURLBuilder.append(request.getServletPath());\n        if (request.getPathInfo() != null) {\n            taskURLBuilder.append(request.getPathInfo());\n        }\n        taskURLBuilder.append(\"/\");\n\n        if (!StringUtil.isBlank(request.getQueryString())) {\n            taskURLBuilder.append(\"?\").append(request.getQueryString());\n        }\n        response.sendRedirect(response.encodeRedirectURL(taskURLBuilder.toString()));\n    }\n\n    public void forwardIfRequestIsAuthorized(final HttpServletRequest request, final HttpServletResponse response,\n            final String apiPathShouldStartWith, final String apiPath) throws IOException, ServletException {\n        try {\n            // Strip path parameters (semicolons) to prevent parser differential\n            // between URI.normalize() and Tomcat's getRequestDispatcher()\n            String sanitizedPath = PathSanitizer.stripPathParameters(apiPath);\n            String encodedAPIPath = UriUtils.encodePath(sanitizedPath, \"UTF-8\");\n            URI uri = new URI(encodedAPIPath);\n            if (!uri.normalize().toString().startsWith(apiPathShouldStartWith)) {\n                final String message = \"attempt to access unauthorized path \" + encodedAPIPath;\n                if (LOGGER.isDebugEnabled()) {\n                    LOGGER.debug(message);\n                }\n                response.setStatus(HttpServletResponse.SC_FORBIDDEN);\n                response.flushBuffer();\n            } else {\n                request.getRequestDispatcher(encodedAPIPath).forward(request, response);\n            }\n        } catch (URISyntaxException e) {\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(e.getMessage());\n            }\n            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);\n            response.flushBuffer();\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/CustomPageService.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.page;\n\nimport static java.util.Collections.emptySet;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.Arrays;\nimport java.util.Date;\nimport java.util.HashSet;\nimport java.util.Properties;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\n\nimport groovy.lang.GroovyClassLoader;\nimport org.apache.commons.io.FileUtils;\nimport org.bonitasoft.console.common.server.page.extension.PageResourceProviderImpl;\nimport org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils;\nimport org.bonitasoft.console.common.server.preferences.properties.PropertiesFactory;\nimport org.bonitasoft.console.common.server.preferences.properties.PropertiesWithSet;\nimport org.bonitasoft.console.common.server.utils.UnzipUtil;\nimport org.bonitasoft.engine.api.PageAPI;\nimport org.bonitasoft.engine.api.PermissionAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.engine.page.PageNotFoundException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.extension.page.PageController;\nimport org.bonitasoft.web.extension.page.PageResourceProvider;\nimport org.bonitasoft.web.extension.rest.RestApiController;\nimport org.bonitasoft.web.rest.server.api.extension.ControllerClassName;\nimport org.codehaus.groovy.control.CompilationFailedException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Anthony Birembaut, Fabio Lombardi\n */\npublic class CustomPageService {\n\n    public static final String PAGE_CONTROLLER_FILENAME = \"Index.groovy\";\n\n    public static final String PAGE_INDEX_NAME = \"index\";\n\n    public static final String PAGE_INDEX_FILENAME = \"index.html\";\n\n    private static final ConcurrentMap<String, GroovyClassLoader> PAGES_CLASSLOADERS = new ConcurrentHashMap<>();\n\n    private static final ConcurrentMap<String, Long> PAGES_UPDATE_TIMESTAMPS = new ConcurrentHashMap<>();\n\n    private static final ConcurrentMap<String, Long> PAGES_LAST_UPDATE_DB_CHECK = new ConcurrentHashMap<>();\n\n    private static final ConcurrentMap<String, Object> PAGES_LOCKS = new ConcurrentHashMap<>();\n\n    public static final String RESOURCES_PROPERTY = \"resources\";\n    public static final String PROPERTY_CONTENT_TYPE = \"contentType\";\n\n    public static final String NAME_PROPERTY = \"name\";\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(CustomPageService.class.getName());\n\n    public GroovyClassLoader getPageClassloader(final APISession apiSession,\n            final PageResourceProvider pageResourceProvider)\n            throws IOException, CompilationFailedException {\n        return buildPageClassloader(apiSession, pageResourceProvider.getFullPageName(),\n                pageResourceProvider.getPageDirectory());\n    }\n\n    public void ensurePageFolderIsPresent(final APISession apiSession, final PageResourceProvider pageResourceProvider)\n            throws BonitaException, IOException {\n        final String fullPageName = pageResourceProvider.getFullPageName();\n        synchronized (getPageLock(fullPageName)) {\n            File pageDirectory = pageResourceProvider.getPageDirectory();\n            if (!pageDirectory.exists() || pageDirectory.list().length == 0) {\n                if (LOGGER.isDebugEnabled()) {\n                    LOGGER.debug(\"No page folder for page: \" + fullPageName);\n                }\n                retrievePageZipContent(apiSession, pageResourceProvider);\n            }\n        }\n    }\n\n    public void ensurePageFolderIsUpToDate(final APISession apiSession, final PageResourceProvider pageResourceProvider)\n            throws BonitaException, IOException {\n        final String fullPageName = pageResourceProvider.getFullPageName();\n        synchronized (getPageLock(fullPageName)) {\n            final Long pageTimestampFromCache = getPageTimestampFromMemoryCache(fullPageName);\n            if (pageTimestampFromCache != null) {\n                if (shouldVerifyLastUpdateDateInDatabaseAndFolderHealthy(fullPageName)) {\n                    //if it has been less than a certain time since the last database check do not check again\n                    final long databaseLastUpdateTimestamp = getPageLastUpdateDateFromEngine(apiSession,\n                            pageResourceProvider);\n                    if (databaseLastUpdateTimestamp != pageTimestampFromCache) {\n                        if (LOGGER.isDebugEnabled()) {\n                            LOGGER.debug(\"Local update will be performed for page: \" + fullPageName);\n                        }\n                        removePage(pageResourceProvider, true);\n                        retrievePageZipContent(apiSession, pageResourceProvider);\n                    } else {\n                        //make sure the page temp directory has not been altered\n                        ensurePageTempFolderIsHealthy(apiSession, pageResourceProvider);\n                    }\n                }\n            } else {\n                //if the last update date is not in the cache, the page has not been retrieved yet\n                if (LOGGER.isDebugEnabled()) {\n                    LOGGER.debug(\"Local retrieval of page: \" + fullPageName);\n                }\n                retrievePageZipContent(apiSession, pageResourceProvider);\n            }\n        }\n    }\n\n    protected void ensurePageTempFolderIsHealthy(final APISession apiSession,\n            final PageResourceProvider pageResourceProvider) throws IOException, BonitaException {\n        final File pageFolder = pageResourceProvider.getPageDirectory();\n        if (!pageFolder.exists() || pageFolder.list().length == 0) {\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(\n                        \"Page folder does not seem to be healthy for page: \" + pageResourceProvider.getFullPageName());\n            }\n            removePage(pageResourceProvider, true);\n            retrievePageZipContent(apiSession, pageResourceProvider);\n        }\n    }\n\n    protected boolean shouldVerifyLastUpdateDateInDatabaseAndFolderHealthy(final String fullPageName) {\n        Long lastTimePageUpdateWasCheckedInDB = getLastTimePageUpdateWasCheckedInDB(fullPageName);\n        if (lastTimePageUpdateWasCheckedInDB != null) {\n            return System.currentTimeMillis() - lastTimePageUpdateWasCheckedInDB > getPageLastUpdateCheckInterval();\n        }\n        return true;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public Class<?> registerPage(final GroovyClassLoader pageClassLoader,\n            final PageResourceProvider pageResourceProvider)\n            throws CompilationFailedException, IOException {\n        final File pageControllerFile = getGroovyPageFile(pageResourceProvider.getPageDirectory());\n        return pageClassLoader.parseClass(pageControllerFile);\n    }\n\n    public Class<?> registerRestApiPage(final GroovyClassLoader pageClassLoader,\n            PageResourceProviderImpl pageResourceProvider, ControllerClassName restApiControllerClassName,\n            String mappingKey)\n            throws BonitaException {\n        try {\n            if (restApiControllerClassName.isSource()) {\n                File groovyFile = toFile(pageResourceProvider, restApiControllerClassName.getName());\n                if (groovyFile.exists()) {\n                    return pageClassLoader.parseClass(groovyFile);\n                }\n                LOGGER.error(\"resource does not exists:\" + mappingKey);\n                throw new BonitaException(\"unable to handle rest api call to \" + mappingKey);\n            }\n            return pageClassLoader.loadClass(restApiControllerClassName.getName());\n        } catch (CompilationFailedException | IOException | ClassNotFoundException e) {\n            throw new BonitaException(e.getMessage(), e);\n        }\n    }\n\n    protected File toFile(PageResourceProviderImpl pageResourceProvider, String classFileName) {\n        if (classFileName.startsWith(\"/\")) {\n            classFileName = classFileName.substring(1);\n        }\n        final String[] paths = classFileName.split(\"/\");\n        final Path restApiControllerPath = paths.length == 1 ? Paths.get(paths[0])\n                : Paths.get(paths[0], Arrays.copyOfRange(paths, 1, paths.length));\n        return pageResourceProvider.getPageDirectory().toPath().resolve(restApiControllerPath).toFile();\n    }\n\n    public void verifyPageClass(final File tempPageDirectory, APISession session) throws IOException {\n        final File pageControllerFile = getPageFile(tempPageDirectory, PAGE_CONTROLLER_FILENAME);\n        if (pageControllerFile.exists()) {\n            final String classloaderName = String.valueOf(System.currentTimeMillis());\n            final GroovyClassLoader pageClassLoader = buildPageClassloader(session, classloaderName, tempPageDirectory);\n            try {\n                pageClassLoader.parseClass(pageControllerFile);\n            } catch (final CompilationFailedException ex) {\n                LOGGER.error(\"Failed to compile Index.groovy \", ex);\n            } finally {\n                final GroovyClassLoader classLoader = PAGES_CLASSLOADERS.remove(classloaderName);\n                if (classLoader != null) {\n                    classLoader.close();\n                }\n            }\n        }\n    }\n\n    public PageController loadPage(final Class<PageController> pageClass)\n            throws InstantiationException, IllegalAccessException {\n        return pageClass.newInstance();\n    }\n\n    public RestApiController loadRestApiPage(final Class<RestApiController> restApiControllerClass)\n            throws InstantiationException, IllegalAccessException {\n        return restApiControllerClass.newInstance();\n    }\n\n    protected void removePage(final PageResourceProvider pageResourceProvider,\n            final boolean ignoreErrorOnPageDirectoryDelete) throws IOException {\n        String fullPageName = pageResourceProvider.getFullPageName();\n        closeClassloader(fullPageName);\n        removePageZipContent(pageResourceProvider.getPageDirectory(), ignoreErrorOnPageDirectoryDelete);\n        CustomPageDependenciesResolver.removePageLibTempFolder(fullPageName);\n        removePageTimestampsFromMemoryCache(fullPageName);\n    }\n\n    public void removePageLocally(final Page page) throws IOException {\n        final PageResourceProvider pageResourceProvider = new PageResourceProviderImpl(page);\n        removePageLocally(pageResourceProvider);\n    }\n\n    public void removePageLocally(final PageResourceProvider pageResourceProvider) throws IOException {\n        removePage(pageResourceProvider, false);\n        removePageLock(pageResourceProvider.getFullPageName());\n    }\n\n    protected Object getPageLock(final String fullPageName) {\n        return PAGES_LOCKS.computeIfAbsent(fullPageName, k -> new Object());\n    }\n\n    protected void removePageLock(final String fullPageName) {\n        PAGES_LOCKS.remove(fullPageName);\n    }\n\n    protected void addPageTimestampToMemoryCache(final String fullPageName, long timestamp) {\n        PAGES_UPDATE_TIMESTAMPS.put(fullPageName, timestamp);\n    }\n\n    protected void setTimePageUpdateWasCheckedInDB(final String fullPageName) {\n        PAGES_LAST_UPDATE_DB_CHECK.put(fullPageName, System.currentTimeMillis());\n    }\n\n    protected Long getPageTimestampFromMemoryCache(final String fullPageName) {\n        return PAGES_UPDATE_TIMESTAMPS.get(fullPageName);\n    }\n\n    protected Long getLastTimePageUpdateWasCheckedInDB(final String fullPageName) {\n        return PAGES_LAST_UPDATE_DB_CHECK.get(fullPageName);\n    }\n\n    protected void removePageTimestampsFromMemoryCache(final String fullPageName) {\n        PAGES_LAST_UPDATE_DB_CHECK.remove(fullPageName);\n        PAGES_UPDATE_TIMESTAMPS.remove(fullPageName);\n    }\n\n    protected void clearPageTimestampsMemoryCache() {\n        PAGES_UPDATE_TIMESTAMPS.clear();\n        PAGES_LAST_UPDATE_DB_CHECK.clear();\n    }\n\n    private static void closeClassloader(final String pageName) throws IOException {\n        final GroovyClassLoader classloader = PAGES_CLASSLOADERS.remove(pageName);\n        if (classloader != null) {\n            classloader.clearCache();\n            classloader.close();\n        }\n    }\n\n    protected GroovyClassLoader buildPageClassloader(final APISession apiSession, final String pageName,\n            final File pageDirectory)\n            throws CompilationFailedException, IOException {\n        final BDMClientDependenciesResolver bdmDependenciesResolver = new BDMClientDependenciesResolver(apiSession);\n        if (isPageInDebugMode()) {\n            synchronized (CustomPageService.class) {//Handle multiple queries to create several classloaders at the same time\n                return createPageClassloader(pageName, pageDirectory, bdmDependenciesResolver);\n            }\n        } else {\n            //not putting the get in the synchronized block to avoid performance cost when the classloader is already in the map\n            GroovyClassLoader pageClassLoader = PAGES_CLASSLOADERS.get(pageName);\n            if (pageClassLoader == null || isOutdated(pageClassLoader, bdmDependenciesResolver)) {\n                synchronized (CustomPageService.class) {//Handle multiple queries to create several classloaders at the same time\n                    if (pageClassLoader == null) {\n                        //double check in synchronize block to avoid creating the classloader twice if a creation is already in progress in another thread\n                        pageClassLoader = PAGES_CLASSLOADERS.get(pageName);\n                        if (pageClassLoader == null || isOutdated(pageClassLoader, bdmDependenciesResolver)) {\n                            pageClassLoader = createPageClassloader(pageName, pageDirectory, bdmDependenciesResolver);\n                            PAGES_CLASSLOADERS.put(pageName, pageClassLoader);\n                        }\n                    } else {\n                        //classloader is outdated\n                        pageClassLoader.clearCache();\n                        pageClassLoader.close();\n                        pageClassLoader = createPageClassloader(pageName, pageDirectory, bdmDependenciesResolver);\n                        PAGES_CLASSLOADERS.put(pageName, pageClassLoader);\n                    }\n                }\n            }\n            return pageClassLoader;\n        }\n    }\n\n    private GroovyClassLoader createPageClassloader(final String pageName, final File pageDirectory,\n            final BDMClientDependenciesResolver bdmDependenciesResolver) throws IOException {\n        GroovyClassLoader pageClassLoader = new GroovyClassLoader(getParentClassloader(pageName,\n                new CustomPageDependenciesResolver(pageName, pageDirectory, getWebBonitaConstantsUtils()),\n                bdmDependenciesResolver));\n        if (pageDirectory.exists()) {\n            pageClassLoader.addClasspath(pageDirectory.getPath());\n        }\n        return pageClassLoader;\n    }\n\n    private boolean isOutdated(GroovyClassLoader pageClassLoader,\n            BDMClientDependenciesResolver bdmDependenciesResolver) {\n        final ClassLoader parent = pageClassLoader.getParent();\n        if (!(parent instanceof VersionedClassloader cachedClassloader)) {\n            throw new IllegalStateException(\"Parent classloader should be versioned.\");\n        }\n        return !cachedClassloader.hasVersion(bdmDependenciesResolver.getBusinessDataModelVersion());\n    }\n\n    public boolean isPageInDebugMode() {\n        return PropertiesFactory.getConsoleProperties().isPageInDebugMode();\n    }\n\n    public long getPageLastUpdateCheckInterval() {\n        return PropertiesFactory.getConsoleProperties().getPageLastUpdateCheckInterval();\n    }\n\n    protected WebBonitaConstantsUtils getWebBonitaConstantsUtils() {\n        return WebBonitaConstantsUtils.getTenantInstance();\n    }\n\n    protected ClassLoader getParentClassloader(final String pageName,\n            final CustomPageDependenciesResolver customPageDependenciesResolver,\n            final BDMClientDependenciesResolver bdmDependenciesResolver) throws IOException {\n        final CustomPageChildFirstClassLoader classLoader = new CustomPageChildFirstClassLoader(pageName,\n                customPageDependenciesResolver,\n                bdmDependenciesResolver, Thread.currentThread().getContextClassLoader());\n        classLoader.addCustomPageResources();\n        return classLoader;\n    }\n\n    protected void retrievePageZipContent(final APISession apiSession, final PageResourceProvider pageResourceProvider)\n            throws BonitaException, IOException {\n        if (LOGGER.isDebugEnabled()) {\n            LOGGER.debug(\"Retrieving page content for page: \" + pageResourceProvider.getFullPageName());\n        }\n        final PageAPI pageAPI = getPageAPI(apiSession);\n        // retrieve page zip content from engine and cache it\n        final Page page = pageResourceProvider.getPage(pageAPI);\n        final byte[] pageContent = pageAPI.getPageContent(page.getId());\n        if (pageContent.length == 0) {\n            throw new BonitaException(\"No content available for page: \" + page.getName());\n        }\n        final File tempPageFile = ((PageResourceProviderImpl) pageResourceProvider).getTempPageFile();\n        FileUtils.writeByteArrayToFile(tempPageFile, pageContent);\n        UnzipUtil.unzip(tempPageFile, pageResourceProvider.getPageDirectory().getPath(), true);\n        long lastUpdateTimestamp = 0L;\n        if (page.getLastModificationDate() != null) {\n            lastUpdateTimestamp = page.getLastModificationDate().getTime();\n        }\n        String fullPageName = pageResourceProvider.getFullPageName();\n        addPageTimestampToMemoryCache(fullPageName, lastUpdateTimestamp);\n        setTimePageUpdateWasCheckedInDB(fullPageName);\n    }\n\n    protected PageAPI getPageAPI(final APISession apiSession) throws BonitaException {\n        return TenantAPIAccessor.getCustomPageAPI(apiSession);\n    }\n\n    protected PermissionAPI getPermissionAPI(final APISession apiSession) throws BonitaException {\n        return TenantAPIAccessor.getPermissionAPI(apiSession);\n    }\n\n    protected void removePageZipContent(final File pageDirectory, final boolean ignoreErrorOnPageDirectoryDelete)\n            throws IOException {\n        try {\n            FileUtils.deleteDirectory(pageDirectory);\n        } catch (IOException e) {\n            //in some circumstances with java 8 page directory cannot be removed.\n            //this catch and the method ignoreErrorOnPageDirectoryDelete can probably be removed in 7.13.x\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(\"Unable delete page folder. \" + e.getMessage());\n            }\n            if (!ignoreErrorOnPageDirectoryDelete) {\n                if (LOGGER.isDebugEnabled()) {\n                    LOGGER.debug(\"Page folder will be removed when jvm shuts down.\");\n                }\n                pageDirectory.deleteOnExit();\n            }\n        }\n    }\n\n    public File getGroovyPageFile(final File pageDirectory) {\n        return getPageFile(pageDirectory, PAGE_CONTROLLER_FILENAME);\n    }\n\n    public File getPageFile(final File pageDirectory, final String fileName) {\n        return new File(pageDirectory, fileName);\n    }\n\n    protected long getPageLastUpdateDateFromEngine(final APISession apiSession,\n            final PageResourceProvider pageResourceProvider) throws BonitaException {\n        long lastUpdate = 0L;\n        try {\n            final PageAPI pageAPI = getPageAPI(apiSession);\n            final Date lastUpdateDate = pageResourceProvider.getPage(pageAPI).getLastModificationDate();\n            if (lastUpdateDate != null) {\n                lastUpdate = lastUpdateDate.getTime();\n            }\n        } catch (final PageNotFoundException e) {\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(\"Unable to find the page \" + pageResourceProvider);\n            }\n        }\n        setTimePageUpdateWasCheckedInDB(pageResourceProvider.getFullPageName());\n        return lastUpdate;\n    }\n\n    public Properties getPageProperties(final APISession apiSession, final byte[] zipContent,\n            final boolean checkIfItAlreadyExists,\n            final Long processDefinitionId) throws BonitaException {\n        final PageAPI pageAPI = getPageAPI(apiSession);\n        Properties properties;\n        if (processDefinitionId == null) {\n            properties = pageAPI.getPageProperties(zipContent, checkIfItAlreadyExists);\n        } else {\n            properties = pageAPI.getPageProperties(zipContent, false);\n            if (checkIfItAlreadyExists) {\n                final String pageName = properties.getProperty(NAME_PROPERTY);\n                try {\n                    pageAPI.getPageByNameAndProcessDefinitionId(pageName, processDefinitionId);\n                    throw new AlreadyExistsException(\n                            \"A page with name \" + pageName + \" already exists for the process \" + processDefinitionId);\n                } catch (final PageNotFoundException e) {\n                    try {\n                        pageAPI.getPageByName(pageName);\n                        throw new AlreadyExistsException(\n                                \"A page with name \" + pageName + \" already exists for the tenant\");\n                    } catch (final PageNotFoundException e1) {\n                        //Do nothing (if the page was not found, it means a page with the same name doesn't already exist)\n                    }\n                    //Do nothing (if the page was not found, it means a page with the same name doesn't already exist)\n                }\n            }\n        }\n        return properties;\n    }\n\n    public Set<String> getCustomPagePermissions(final Properties pageProperties, APISession apiSession)\n            throws BonitaException {\n        final PropertiesWithSet pagePropertiesWithSet = new PropertiesWithSet(pageProperties);\n        final Set<String> pageRestResources = new HashSet<>(pagePropertiesWithSet.getPropertyAsSet(RESOURCES_PROPERTY));\n        final Set<String> permissions = new HashSet<>();\n        for (final String pageRestResource : pageRestResources) {\n            final Set<String> resourcePermissions = getPermissionAPI(apiSession)\n                    .getResourcePermissions(pageRestResource);\n            if (emptySet().equals(resourcePermissions)) {\n                permissions.add(\"<\" + pageRestResource + \">\");\n            }\n            permissions.addAll(resourcePermissions);\n        }\n        return permissions;\n    }\n\n    public Page getPage(final APISession apiSession, final String pageName, final long processDefinitionId)\n            throws BonitaException {\n        return getPageAPI(apiSession).getPageByNameAndProcessDefinitionId(pageName, processDefinitionId);\n    }\n\n    public Page getPage(final APISession apiSession, final long pageId) throws BonitaException {\n        return getPageAPI(apiSession).getPage(pageId);\n    }\n\n    public PageResourceProvider getPageResourceProvider(final Page page) {\n        return new PageResourceProviderImpl(page);\n    }\n\n    public static void clearCachedClassloaders() throws IOException {\n        for (final String page : PAGES_CLASSLOADERS.keySet()) {\n            closeClassloader(page);\n        }\n    }\n\n    public void writePageToPageDirectory(Page page,\n            PageResourceProvider pageResourceProvider,\n            File unzipPageTempFolder,\n            APISession session) throws IOException {\n        verifyPageClass(unzipPageTempFolder, session);\n        File pageDirectory = pageResourceProvider.getPageDirectory();\n        FileUtils.copyDirectory(unzipPageTempFolder, pageDirectory);\n        long lastUpdateTimestamp = 0L;\n        if (page.getLastModificationDate() != null) {\n            lastUpdateTimestamp = page.getLastModificationDate().getTime();\n        }\n        String fullPageName = pageResourceProvider.getFullPageName();\n        addPageTimestampToMemoryCache(fullPageName, lastUpdateTimestamp);\n        setTimePageUpdateWasCheckedInDB(fullPageName);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/CustomPageServlet.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.page;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.List;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.console.common.server.page.extension.PageResourceProviderImpl;\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.console.common.server.utils.SessionUtil;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnauthorizedAccessException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.livingapps.ApplicationModelFactory;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class CustomPageServlet extends HttpServlet {\n\n    public static final String APP_TOKEN_PARAM = \"appToken\";\n    /**\n     * uuid\n     */\n    private static final long serialVersionUID = -5410859017103815654L;\n    /**\n     * Logger\n     */\n    private static Logger LOGGER = LoggerFactory.getLogger(CustomPageServlet.class.getName());\n    protected ResourceRenderer resourceRenderer = new ResourceRenderer();\n\n    protected PageRenderer pageRenderer = new PageRenderer(resourceRenderer);\n\n    protected BonitaHomeFolderAccessor bonitaHomeFolderAccessor = new BonitaHomeFolderAccessor();\n\n    protected CustomPageRequestModifier customPageRequestModifier = new CustomPageRequestModifier();\n\n    @Override\n    protected void doGet(final HttpServletRequest request, final HttpServletResponse response)\n            throws ServletException, IOException {\n        /*\n         * Check if requested URL is missing last slash, like \"custom-page/page-name\".\n         * If missing, redirect to \"custom-page/page-name/\"\n         */\n        if (isPageUrlWithoutFinalSlash(request)) {\n            customPageRequestModifier.redirectToValidPageUrl(request, response);\n            return;\n        }\n\n        final String appToken = request.getParameter(APP_TOKEN_PARAM);\n        final HttpSession session = request.getSession();\n        final APISession apiSession = (APISession) session.getAttribute(SessionUtil.API_SESSION_PARAM_KEY);\n\n        final List<String> pathSegments = resourceRenderer.getPathSegments(request.getPathInfo());\n        if (pathSegments.isEmpty()) {\n            response.sendError(HttpServletResponse.SC_BAD_REQUEST,\n                    \"The name of the page is required.\");\n            return;\n        }\n        final String pageName = pathSegments.get(0);\n\n        try {\n\n            if (isAuthorized(apiSession, appToken)) {\n                if (isPageRequest(pathSegments)) {\n                    pageRenderer.displayCustomPage(request, response, apiSession, pageName);\n                } else {\n                    final File resourceFile = getResourceFile(request.getPathInfo(), pageName);\n                    pageRenderer.ensurePageFolderIsPresent(apiSession, pageRenderer.getPageResourceProvider(pageName));\n                    resourceRenderer.renderFile(request, response, resourceFile);\n                }\n            } else {\n                response.sendError(HttpServletResponse.SC_FORBIDDEN, \"User not Authorized\");\n            }\n        } catch (UnauthorizedAccessException e) {\n            response.sendError(HttpServletResponse.SC_FORBIDDEN, \"User not Authorized\");\n        } catch (final Exception e) {\n            handleException(pageName, e);\n        }\n\n    }\n\n    private boolean isPageRequest(final List<String> pathSegments) {\n        if (pathSegments.size() == 1) {\n            return true;\n        } else if (pathSegments.size() == 2) {\n            return isAnIndexSegment(pathSegments.get(1));\n        }\n        return false;\n    }\n\n    private boolean isAnIndexSegment(final String segment) {\n        return segment.equalsIgnoreCase(CustomPageService.PAGE_INDEX_FILENAME)\n                || segment.equalsIgnoreCase(CustomPageService.PAGE_CONTROLLER_FILENAME)\n                || segment.equalsIgnoreCase(CustomPageService.PAGE_INDEX_NAME);\n    }\n\n    private boolean isPageUrlWithoutFinalSlash(final HttpServletRequest request) {\n        return request.getPathInfo() == null || request.getPathInfo().matches(\"/[^/]+\");\n    }\n\n    private File getResourceFile(final String resourcePath, final String pageName) throws IOException, BonitaException {\n        final PageResourceProviderImpl pageResourceProvider = pageRenderer.getPageResourceProvider(pageName);\n        final File resourceFile = new File(pageResourceProvider.getPageDirectory(),\n                CustomPageService.RESOURCES_PROPERTY + File.separator\n                        + getResourcePathWithoutPageName(resourcePath, pageName));\n\n        if (!bonitaHomeFolderAccessor.isInFolder(resourceFile, pageResourceProvider.getPageDirectory())) {\n            throw new UnauthorizedAccessException(\"Unauthorized access to the file \" + resourcePath);\n        }\n        return resourceFile;\n    }\n\n    private String getResourcePathWithoutPageName(final String resourcePath, final String pageName) {\n        //resource path match \"/pagename/resourcefolder/filename\"\n        return resourcePath.substring(pageName.length() + 2);\n    }\n\n    private boolean isAuthorized(final APISession apiSession, final String appToken)\n            throws BonitaException {\n        //Technical user should be authorized in order for the custom pages to be displayed in his profile\n        return apiSession.isTechnicalUser()\n                || getCustomPageAuthorizationsHelper(apiSession).isAuthorized(appToken);\n    }\n\n    private void handleException(final String pageName, final Exception e) throws ServletException {\n        if (LOGGER.isWarnEnabled()) {\n            LOGGER.warn(\"Error while trying to render the custom page {}\", pageName, e);\n        }\n        throw new ServletException(e.getMessage());\n    }\n\n    protected ApplicationAuthorizationsHelper getCustomPageAuthorizationsHelper(final APISession apiSession)\n            throws BonitaHomeNotSetException,\n            ServerAPIException, UnknownAPITypeException {\n        return new ApplicationAuthorizationsHelper(apiSession,\n                new ApplicationModelFactory(\n                        TenantAPIAccessor.getLivingApplicationAPI(apiSession),\n                        TenantAPIAccessor.getCustomPageAPI(apiSession),\n                        TenantAPIAccessor.getProfileAPI(apiSession)));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/MonoParentJarFileClassLoader.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.page;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URI;\nimport java.net.URL;\nimport java.security.AccessControlContext;\nimport java.security.AccessController;\nimport java.security.CodeSource;\nimport java.security.PrivilegedAction;\nimport java.security.PrivilegedActionException;\nimport java.security.PrivilegedExceptionAction;\nimport java.security.cert.Certificate;\nimport java.util.Enumeration;\nimport java.util.jar.Attributes;\nimport java.util.jar.Manifest;\n\nimport org.apache.xbean.classloader.NamedClassLoader;\nimport org.apache.xbean.classloader.ResourceHandle;\nimport org.apache.xbean.classloader.UnionEnumeration;\nimport org.apache.xbean.classloader.UrlResourceFinder;\n\n/**\n * This class is highly inspired form JarFileClassLoader from xbean. The main difference is that\n * it inherits from NamedClassLoader instead of MultiParentClassLoader.\n */\npublic class MonoParentJarFileClassLoader extends NamedClassLoader {\n\n    private static final URL[] EMPTY_URLS = new URL[0];\n\n    private final UrlResourceFinder resourceFinder = new UrlResourceFinder();\n\n    private final AccessControlContext acc;\n\n    /**\n     * Creates a JarFileClassLoader that is a child of the specified class loader.\n     *\n     * @param name\n     *        the name of this class loader\n     * @param urls\n     *        a list of URLs from which classes and resources should be loaded\n     * @param parent\n     *        the parent of this class loader\n     */\n    public MonoParentJarFileClassLoader(final String name, final URL[] urls, final ClassLoader parent) {\n        super(name, EMPTY_URLS, parent);\n        acc = AccessController.getContext();\n        addURLs(urls);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public URL[] getURLs() {\n        return resourceFinder.getUrls();\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void addURL(final URL url) {\n        AccessController.doPrivileged((PrivilegedAction) () -> {\n            resourceFinder.addUrl(url);\n            return null;\n        }, acc);\n    }\n\n    /**\n     * Adds an array of urls to the end of this class loader.\n     *\n     * @param urls\n     *        the URLs to add\n     */\n    protected void addURLs(final URL[] urls) {\n        AccessController.doPrivileged((PrivilegedAction) () -> {\n            if (urls != null && urls.length > 0) {\n                for (final URL url : urls) {\n                    resourceFinder.addUrl(url);\n                }\n            }\n            return null;\n        }, acc);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void destroy() {\n        resourceFinder.destroy();\n        super.destroy();\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public URL findResource(final String resourceName) {\n        return (URL) AccessController.doPrivileged((PrivilegedAction) () -> resourceFinder.findResource(resourceName),\n                acc);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public Enumeration findResources(final String resourceName) throws IOException {\n        // TODO this is not right\n        // first get the resources from the parent classloaders\n        final Enumeration<?> parentResources = super.findResources(resourceName);\n\n        // get the classes from my urls\n        final Enumeration myResources = (Enumeration) AccessController\n                .doPrivileged((PrivilegedAction) () -> resourceFinder.findResources(resourceName), acc);\n\n        // join the two together\n        return new UnionEnumeration(parentResources, myResources);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    protected String findLibrary(final String libraryName) {\n        // if the libraryName is actually a directory it is invalid\n        final int pathEnd = libraryName.lastIndexOf('/');\n        if (pathEnd == libraryName.length() - 1) {\n            throw new IllegalArgumentException(\"libraryName ends with a '/' character: \" + libraryName);\n        }\n\n        // get the name if the library file\n        final String resourceName;\n        if (pathEnd < 0) {\n            resourceName = System.mapLibraryName(libraryName);\n        } else {\n            final String path = libraryName.substring(0, pathEnd + 1);\n            final String file = libraryName.substring(pathEnd + 1);\n            resourceName = path + System.mapLibraryName(file);\n        }\n\n        // get a resource handle to the library\n        final ResourceHandle resourceHandle = (ResourceHandle) AccessController\n                .doPrivileged((PrivilegedAction) () -> resourceFinder.getResource(resourceName), acc);\n\n        if (resourceHandle == null) {\n            return null;\n        }\n\n        // the library must be accessable on the file system\n        final URL url = resourceHandle.getUrl();\n        if (!\"file\".equals(url.getProtocol())) {\n            return null;\n        }\n\n        return new File(URI.create(url.toString())).getPath();\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    protected Class<?> findClass(final String className) throws ClassNotFoundException {\n        try {\n            return (Class<?>) AccessController.doPrivileged(new PrivilegedExceptionAction<>() {\n\n                @Override\n                public Object run() throws ClassNotFoundException {\n                    // First think check if we are allowed to define the package\n                    checkPackageDefinition(className);\n\n                    final ResourceHandle resourceHandle = findClassFileResource(className);\n\n                    byte[] bytes;\n                    Manifest manifest;\n                    try {\n                        // get the bytes from the class file\n                        bytes = resourceHandle.getBytes();\n\n                        // get the manifest for defining the packages\n                        manifest = resourceHandle.getManifest();\n                    } catch (final IOException e) {\n                        throw new ClassNotFoundException(className, e);\n                    }\n\n                    // get the certificates for the code source\n                    final Certificate[] certificates = resourceHandle.getCertificates();\n\n                    // the code source url is used to define the package and as the security context for the class\n                    final URL codeSourceUrl = resourceHandle.getCodeSourceUrl();\n\n                    // define the package (required for security)\n                    definePackage(className, codeSourceUrl, manifest);\n\n                    // this is the security context of the class\n                    final CodeSource codeSource = new CodeSource(codeSourceUrl, certificates);\n\n                    // load the class into the vm\n                    return defineClass(className, bytes, 0, bytes.length, codeSource);\n                }\n\n                private ResourceHandle findClassFileResource(final String className) throws ClassNotFoundException {\n                    // convert the class name to a file name\n                    final String resourceName = className.replace('.', '/') + \".class\";\n\n                    // find the class file resource\n                    final ResourceHandle resourceHandle = resourceFinder.getResource(resourceName);\n                    if (resourceHandle == null) {\n                        throw new ClassNotFoundException(className);\n                    }\n                    return resourceHandle;\n                }\n\n                private void checkPackageDefinition(final String className) {\n                    final SecurityManager securityManager = System.getSecurityManager();\n                    if (securityManager != null) {\n                        String packageName;\n                        final int packageEnd = className.lastIndexOf('.');\n                        if (packageEnd >= 0) {\n                            packageName = className.substring(0, packageEnd);\n                            securityManager.checkPackageDefinition(packageName);\n                        }\n                    }\n                }\n            }, acc);\n        } catch (final PrivilegedActionException e) {\n            throw (ClassNotFoundException) e.getException();\n        }\n    }\n\n    private void definePackage(final String className, final URL jarUrl, final Manifest manifest) {\n        final int packageEnd = className.lastIndexOf('.');\n        if (packageEnd < 0) {\n            return;\n        }\n\n        final String packageName = className.substring(0, packageEnd);\n        final String packagePath = packageName.replace('.', '/') + \"/\";\n\n        Attributes packageAttributes = null;\n        Attributes mainAttributes = null;\n        if (manifest != null) {\n            packageAttributes = manifest.getAttributes(packagePath);\n            mainAttributes = manifest.getMainAttributes();\n        }\n        final Package pkg = getPackage(packageName);\n        if (pkg != null) {\n            if (pkg.isSealed()) {\n                if (!pkg.isSealed(jarUrl)) {\n                    throw new SecurityException(\n                            \"Package was already sealed with another URL: package=\" + packageName + \", url=\" + jarUrl);\n                }\n            } else {\n                if (isSealed(packageAttributes, mainAttributes)) {\n                    throw new SecurityException(\"Package was already been loaded and not sealed: package=\" + packageName\n                            + \", url=\" + jarUrl);\n                }\n            }\n        } else {\n            final String specTitle = getAttribute(Attributes.Name.SPECIFICATION_TITLE, packageAttributes,\n                    mainAttributes);\n            final String specVendor = getAttribute(Attributes.Name.SPECIFICATION_VENDOR, packageAttributes,\n                    mainAttributes);\n            final String specVersion = getAttribute(Attributes.Name.SPECIFICATION_VERSION, packageAttributes,\n                    mainAttributes);\n            final String implTitle = getAttribute(Attributes.Name.IMPLEMENTATION_TITLE, packageAttributes,\n                    mainAttributes);\n            final String implVendor = getAttribute(Attributes.Name.IMPLEMENTATION_VENDOR, packageAttributes,\n                    mainAttributes);\n            final String implVersion = getAttribute(Attributes.Name.IMPLEMENTATION_VERSION, packageAttributes,\n                    mainAttributes);\n\n            URL sealBase = null;\n            if (isSealed(packageAttributes, mainAttributes)) {\n                sealBase = jarUrl;\n            }\n\n            definePackage(packageName, specTitle, specVersion, specVendor, implTitle, implVersion, implVendor,\n                    sealBase);\n        }\n    }\n\n    private String getAttribute(final Attributes.Name name, final Attributes packageAttributes,\n            final Attributes mainAttributes) {\n        if (packageAttributes != null) {\n            final String value = packageAttributes.getValue(name);\n            if (value != null) {\n                return value;\n            }\n        }\n        if (mainAttributes != null) {\n            return mainAttributes.getValue(name);\n        }\n        return null;\n    }\n\n    private boolean isSealed(final Attributes packageAttributes, final Attributes mainAttributes) {\n        final String sealed = getAttribute(Attributes.Name.SEALED, packageAttributes, mainAttributes);\n        if (sealed == null) {\n            return false;\n        }\n        return \"true\".equalsIgnoreCase(sealed);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/PageContextHelper.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.page;\n\nimport java.util.Locale;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.console.common.server.utils.LocaleUtils;\nimport org.bonitasoft.engine.session.APISession;\n\npublic class PageContextHelper {\n\n    public static final String PROFILE_PARAM = \"profile\";\n\n    public static final String ATTRIBUTE_API_SESSION = \"apiSession\";\n\n    private final HttpServletRequest request;\n\n    public PageContextHelper(HttpServletRequest request) {\n        this.request = request;\n    }\n\n    public String getCurrentProfile() {\n        return request.getParameter(PROFILE_PARAM);\n    }\n\n    public Locale getCurrentLocale() {\n        return LocaleUtils.getUserLocale(request);\n    }\n\n    public APISession getApiSession() {\n        final HttpSession httpSession = request.getSession();\n        return (APISession) httpSession.getAttribute(ATTRIBUTE_API_SESSION);\n\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/PageDownloadServlet.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.page;\n\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.bonitasoft.console.common.server.utils.SessionUtil;\nimport org.bonitasoft.engine.api.PageAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Anthony Birembaut\n */\npublic class PageDownloadServlet extends HttpServlet {\n\n    private static final String ID_PARAM = \"id\";\n\n    /**\n     * Logger\n     */\n    private static final Logger LOGGER = LoggerFactory.getLogger(PageDownloadServlet.class.getName());\n\n    /**\n     * UID\n     */\n    private static final long serialVersionUID = 7203686892997001991L;\n\n    @Override\n    protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException {\n\n        final String pageIDStr = request.getParameter(ID_PARAM);\n        long pageId = 0L;\n        if (pageIDStr != null) {\n            pageId = Long.parseLong(pageIDStr);\n        } else {\n            throw new ServletException(\"The ID parameter is mandatory.\");\n        }\n        final APISession apiSession = (APISession) request.getSession().getAttribute(SessionUtil.API_SESSION_PARAM_KEY);\n        OutputStream out = null;\n        try {\n            final PageAPI pageAPI = getPageAPI(apiSession);\n            final Page page = pageAPI.getPage(pageId);\n            final byte[] pageContent = pageAPI.getPageContent(pageId);\n            // Set response headers\n            response.setCharacterEncoding(\"UTF-8\");\n            response.setContentType(\"application/octet-stream\");\n            final String encodedfileName = URLEncoder.encode(page.getContentName(), StandardCharsets.UTF_8);\n            final String userAgent = request.getHeader(\"User-Agent\");\n            if (userAgent != null && userAgent.contains(\"Firefox\")) {\n                response.setHeader(\"Content-Disposition\", \"attachment; filename*=UTF-8''\" + encodedfileName);\n            } else {\n                response.setHeader(\"Content-Disposition\",\n                        \"attachment; filename=\\\"\" + encodedfileName.replaceAll(\"\\\\_\", \" \") + \"\\\"; filename*=UTF-8''\"\n                                + encodedfileName);\n            }\n            out = response.getOutputStream();\n\n            if (pageContent == null) {\n                response.setContentLength(0);\n            } else {\n                response.setContentLength(pageContent.length);\n            }\n            out.write(pageContent);\n\n        } catch (final InvalidSessionException e) {\n            final String message = \"Session expired. Please login again.\";\n            if (LOGGER.isInfoEnabled()) {\n                LOGGER.info(message, e);\n            }\n            try {\n                out.write(message.getBytes());\n            } catch (final IOException e1) {\n                throw new ServletException(e1);\n            }\n\n        } catch (final Exception e) {\n            if (LOGGER.isErrorEnabled()) {\n                LOGGER.error(e.getMessage(), e);\n            }\n            try {\n                out.write(\"An exception occurred. Please contact an administrator\".getBytes());\n            } catch (final IOException e1) {\n                throw new ServletException(e1);\n            }\n        }\n    }\n\n    private PageAPI getPageAPI(final APISession apiSession)\n            throws InvalidSessionException, BonitaHomeNotSetException, ServerAPIException,\n            UnknownAPITypeException {\n        return TenantAPIAccessor.getCustomPageAPI(apiSession);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/PageMappingService.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.page;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.stream.Collectors;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport org.bonitasoft.engine.api.PageAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.page.PageURL;\nimport org.bonitasoft.engine.page.URLAdapterConstants;\nimport org.bonitasoft.engine.session.APISession;\n\npublic class PageMappingService {\n\n    public PageReference getPage(final HttpServletRequest request, final APISession apiSession, final String mappingKey,\n            final Locale locale,\n            final boolean executeAuthorizationRules) throws BonitaException {\n        final Map<String, Serializable> context = new HashMap<>();\n        //clone the request parameters map to a HashMap to avoid deserialization exceptions when calling a remote engine\n        //see BS-16992 (the parameters map implementation is specific to the servlet container).\n        Map<String, String[]> parametersMapCopy = request.getParameterMap().entrySet().stream()\n                .collect(Collectors.toMap(Entry::getKey, e -> e.getValue().clone()));\n        context.put(URLAdapterConstants.QUERY_PARAMETERS, (Serializable) parametersMapCopy);\n        context.put(URLAdapterConstants.LOCALE, locale.toString());\n        context.put(URLAdapterConstants.CONTEXT_PATH, request.getContextPath());\n        final PageAPI pageAPI = getPageAPI(apiSession);\n        final PageURL pageURL = pageAPI.resolvePageOrURL(mappingKey, context, executeAuthorizationRules);\n        return new PageReference(pageURL.getPageId(), pageURL.getUrl());\n    }\n\n    protected PageAPI getPageAPI(final APISession apiSession) throws BonitaHomeNotSetException, ServerAPIException,\n            UnknownAPITypeException {\n        return TenantAPIAccessor.getCustomPageAPI(apiSession);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/PageReference.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.page;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class PageReference implements Serializable {\n\n    @Serial\n    private static final long serialVersionUID = -1692145871057019847L;\n\n    private Long pageId;\n    private String url;\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/PageRenderer.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.page;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Locale;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport groovy.lang.GroovyClassLoader;\nimport org.bonitasoft.console.common.server.page.extension.PageContextImpl;\nimport org.bonitasoft.console.common.server.page.extension.PageResourceProviderImpl;\nimport org.bonitasoft.console.common.server.utils.LocaleUtils;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.engine.session.APISession;\nimport org.codehaus.groovy.control.CompilationFailedException;\n\n/**\n * Class used by servlets to display a custom page\n * Since each instance of the servlet carry an instance of this class, it should have absolutely no instance attribute\n *\n * @author Anthony Birembaut\n */\npublic class PageRenderer {\n\n    public static final String PROFILE_PARAM = \"profile\";\n\n    protected CustomPageService customPageService = new CustomPageService();\n\n    protected ResourceRenderer resourceRenderer;\n\n    public PageRenderer(final ResourceRenderer resourceRenderer) {\n        this.resourceRenderer = resourceRenderer;\n    }\n\n    public void displayCustomPage(final HttpServletRequest request, final HttpServletResponse response,\n            final APISession apiSession, final String pageName)\n            throws CompilationFailedException, InstantiationException, IllegalAccessException, IOException,\n            BonitaException {\n\n        final PageResourceProviderImpl pageResourceProvider = getPageResourceProvider(pageName);\n        displayCustomPage(request, response, apiSession, pageResourceProvider, getCurrentLocale(request));\n    }\n\n    public void displayCustomPage(final HttpServletRequest request, final HttpServletResponse response,\n            final APISession apiSession, final long pageId)\n            throws CompilationFailedException, InstantiationException, IllegalAccessException, IOException,\n            BonitaException {\n        displayCustomPage(request, response, apiSession, getPageResourceProvider(pageId, apiSession),\n                getCurrentLocale(request));\n    }\n\n    public void displayCustomPage(final HttpServletRequest request, final HttpServletResponse response,\n            final APISession apiSession, final long pageId, final Locale currentLocale)\n            throws CompilationFailedException, InstantiationException, IllegalAccessException, IOException,\n            BonitaException {\n        displayCustomPage(request, response, apiSession, getPageResourceProvider(pageId, apiSession), currentLocale);\n    }\n\n    public void ensurePageFolderIsPresent(final APISession apiSession,\n            final PageResourceProviderImpl pageResourceProvider) throws BonitaException, IOException {\n        customPageService.ensurePageFolderIsPresent(apiSession, pageResourceProvider);\n    }\n\n    private void displayCustomPage(final HttpServletRequest request, final HttpServletResponse response,\n            final APISession apiSession,\n            final PageResourceProviderImpl pageResourceProvider, final Locale currentLocale)\n            throws BonitaException, IOException, InstantiationException,\n            IllegalAccessException {\n        customPageService.ensurePageFolderIsUpToDate(apiSession, pageResourceProvider);\n        enforceLocaleCookieIfPresentInURLOrBrowser(request, response, currentLocale);\n        if (isGroovyPage(pageResourceProvider)) {\n            displayGroovyPage(request, response, apiSession, pageResourceProvider);\n        } else {\n            displaySimpleHtmlPage(request, response, pageResourceProvider);\n        }\n    }\n\n    private boolean isGroovyPage(final PageResourceProviderImpl pageResourceProvider) {\n        final File pageFolder = pageResourceProvider.getPageDirectory();\n        final File indexGroovy = customPageService.getGroovyPageFile(pageFolder);\n        return indexGroovy.exists();\n    }\n\n    private void displaySimpleHtmlPage(final HttpServletRequest request, final HttpServletResponse response,\n            final PageResourceProviderImpl pageResourceProvider)\n            throws IOException, BonitaException, IllegalAccessException {\n\n        final File resourceFile = getIndexFile(pageResourceProvider);\n        resourceRenderer.renderFile(request, response, resourceFile, true);\n    }\n\n    private File getIndexFile(final PageResourceProviderImpl pageResourceProvider) {\n        final File indexHtml = new File(getResourceFolder(pageResourceProvider), CustomPageService.PAGE_INDEX_FILENAME);\n        if (indexHtml.exists()) {\n            return indexHtml;\n        }\n        //fallback try to found index.html a the root of the zip to support custompage legacy.\n        return new File(getResourceFolder(pageResourceProvider).getParent(), CustomPageService.PAGE_INDEX_FILENAME);\n    }\n\n    private File getResourceFolder(final PageResourceProviderImpl pageResourceProvider) {\n        return new File(pageResourceProvider.getPageDirectory(), CustomPageService.RESOURCES_PROPERTY);\n    }\n\n    private void displayGroovyPage(final HttpServletRequest request, final HttpServletResponse response,\n            final APISession apiSession,\n            final PageResourceProviderImpl pageResourceProvider)\n            throws CompilationFailedException, InstantiationException, IllegalAccessException, IOException {\n        response.setContentType(\"text/html\");\n        response.setCharacterEncoding(\"UTF-8\");\n        final ClassLoader originalClassloader = Thread.currentThread().getContextClassLoader();\n        final GroovyClassLoader pageClassloader = customPageService.getPageClassloader(apiSession,\n                pageResourceProvider);\n        try {\n            Thread.currentThread().setContextClassLoader(pageClassloader);\n            pageResourceProvider.setResourceClassLoader(pageClassloader);\n            final Class<?> pageClass = customPageService.registerPage(pageClassloader, pageResourceProvider);\n            final org.bonitasoft.web.extension.page.PageController pageController = ((Class<org.bonitasoft.web.extension.page.PageController>) pageClass)\n                    .newInstance();\n            pageController.doGet(request, response, pageResourceProvider,\n                    new PageContextImpl(apiSession, getCurrentLocale(request), getCurrentProfile(request)));\n        } finally {\n            Thread.currentThread().setContextClassLoader(originalClassloader);\n        }\n    }\n\n    public String getCurrentProfile(final HttpServletRequest request) {\n        return request.getParameter(PROFILE_PARAM);\n    }\n\n    private void enforceLocaleCookieIfPresentInURLOrBrowser(final HttpServletRequest request,\n            final HttpServletResponse response, final Locale currentLocale) {\n        final String localeFromCookie = LocaleUtils.getStandardizedLocaleFromCookie(request);\n        if (currentLocale != null && !currentLocale.toString().equals(localeFromCookie)) {\n            //Set the cookie if the locale is in the URL and different from the existing cookie value or the cookie does not exist yet\n            LocaleUtils.addOrReplaceLocaleCookieResponse(response, currentLocale.toString());\n        }\n    }\n\n    public Locale getCurrentLocale(final HttpServletRequest request) {\n        return LocaleUtils.getUserLocale(request);\n    }\n\n    public PageResourceProviderImpl getPageResourceProvider(final String pageName) {\n        return new PageResourceProviderImpl(pageName);\n    }\n\n    public PageResourceProviderImpl getPageResourceProvider(final long pageId, final APISession apiSession)\n            throws BonitaException {\n        final Page page = customPageService.getPage(apiSession, pageId);\n        return new PageResourceProviderImpl(page);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/PageServlet.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.page;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.util.List;\nimport java.util.Locale;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpSession;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.http.HttpHeaders;\nimport org.apache.http.NameValuePair;\nimport org.apache.http.client.utils.URLEncodedUtils;\nimport org.bonitasoft.console.common.server.page.extension.PageResourceProviderImpl;\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.console.common.server.utils.SessionUtil;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.NotFoundException;\nimport org.bonitasoft.engine.exception.UnauthorizedAccessException;\nimport org.bonitasoft.engine.page.PageNotFoundException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.bonitasoft.livingapps.ApplicationModelFactory;\nimport org.bonitasoft.livingapps.exception.CreationException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Servlet allowing to display a page or the resource of a page with an URL like\n * /portal/resource/<page-mapping-key>/content/\n * (it can be a custom page or an external page)\n * This servlet is used to display process forms/overview pages\n * Note: whenever there is an InvalidSessionException from the engine it performs an HTTP session logout and send a 401\n * error. since we are in a iframe, we cannot redirect to the login page.\n * However, the page should handle the 401 and refresh the top window.\n *\n * @author Anthony Birembaut\n */\npublic class PageServlet extends HttpServlet {\n\n    /**\n     * UID\n     */\n    private static final long serialVersionUID = 2789496969243916444L;\n\n    /**\n     * Logger\n     */\n    private static Logger LOGGER = LoggerFactory.getLogger(PageServlet.class.getName());\n\n    public static final String RESOURCE_PATH_SEPARATOR = \"/content\";\n\n    public static final String API_PATH_SEPARATOR = \"/API\";\n\n    public static final String THEME_PATH_SEPARATOR = \"/theme\";\n\n    public static final String APPLICATION_PARAM = \"app\";\n\n    protected CustomPageRequestModifier customPageRequestModifier = new CustomPageRequestModifier();\n\n    protected PageMappingService pageMappingService = new PageMappingService();\n\n    protected BonitaHomeFolderAccessor bonitaHomeFolderAccessor = new BonitaHomeFolderAccessor();\n\n    protected ResourceRenderer resourceRenderer = new ResourceRenderer();\n\n    protected PageRenderer pageRenderer = new PageRenderer(resourceRenderer);\n\n    @Override\n    protected void service(final HttpServletRequest request, final HttpServletResponse response)\n            throws ServletException, IOException {\n        final String pathInfo = request.getPathInfo();\n\n        if (!pathInfo.contains(RESOURCE_PATH_SEPARATOR + \"/\") && pathInfo.indexOf(API_PATH_SEPARATOR + \"/\") > 0) {\n            //Support relative calls to the REST API from the forms using ../API/\n            final String apiPath = pathInfo.substring(pathInfo.indexOf(API_PATH_SEPARATOR + \"/\"));\n            //security check against directory traversal attack\n            customPageRequestModifier.forwardIfRequestIsAuthorized(request, response, API_PATH_SEPARATOR, apiPath);\n        } else {\n            super.service(request, response);\n        }\n    }\n\n    @Override\n    protected void doGet(final HttpServletRequest request, final HttpServletResponse response)\n            throws ServletException, IOException {\n\n        final HttpSession session = request.getSession();\n        final APISession apiSession = (APISession) session.getAttribute(SessionUtil.API_SESSION_PARAM_KEY);\n\n        final String pathInfo = request.getPathInfo();\n        // Check if requested URL is missing final slash (necessary in order to be able to use relative URLs for resources)\n        if (pathInfo.endsWith(RESOURCE_PATH_SEPARATOR)) {\n            customPageRequestModifier.redirectToValidPageUrl(request, response);\n        } else if (pathInfo.indexOf(RESOURCE_PATH_SEPARATOR + \"/\") > 0) {\n            final String[] pathInfoSegments = pathInfo.split(RESOURCE_PATH_SEPARATOR + \"/\", 2);\n            String resourcePath = getResourcePath(pathInfoSegments);\n            final String mappingKey = pathInfoSegments[0].substring(1);\n            try {\n                resolveAndDisplayPage(request, response, apiSession, mappingKey, resourcePath);\n            } catch (final Exception e) {\n                handleException(request, response, mappingKey, isNotResourcePath(resourcePath), e);\n            }\n        } else if (pathInfo.indexOf(THEME_PATH_SEPARATOR + \"/\") > 0) {\n            final String[] pathInfoSegments = pathInfo.split(THEME_PATH_SEPARATOR + \"/\", 2);\n            String resourcePath = getResourcePath(pathInfoSegments);\n            final String mappingKey = pathInfoSegments[0].substring(1);\n            try {\n                renderThemeResource(request, response, apiSession, resourcePath);\n            } catch (final Exception e) {\n                handleException(request, response, mappingKey, false, e);\n            }\n        } else {\n            final String message = \"/content or /theme is expected in the URL after the page mapping key\";\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(\"Bad request: \" + message);\n            }\n            response.sendError(HttpServletResponse.SC_BAD_REQUEST, message);\n        }\n    }\n\n    protected void renderThemeResource(final HttpServletRequest request, final HttpServletResponse response,\n            final APISession apiSession, String resourcePath)\n            throws BonitaException, CreationException, IllegalAccessException, IOException, ServletException {\n\n        String appToken = request.getParameter(APPLICATION_PARAM);\n        if (appToken != null) {\n            renderThemeResource(request, response, apiSession, resourcePath, appToken);\n        } else {\n            String appTokenFromReferer = getAppFromReferer(request);\n            if (appTokenFromReferer != null) {\n                if (resourcePath.endsWith(\".css\")) {\n                    if (LOGGER.isDebugEnabled()) {\n                        LOGGER.debug(\n                                \"App parameter retrieved from the referer. Redirecting the request to get it in the URL for resource \"\n                                        + resourcePath);\n                    }\n                    String queryString = StringUtils.isEmpty(request.getQueryString()) ? \"\"\n                            : (request.getQueryString() + \"&\");\n                    response.sendRedirect(\"?\" + queryString + APPLICATION_PARAM + \"=\" + appTokenFromReferer);\n                } else {\n                    renderThemeResource(request, response, apiSession, resourcePath, appTokenFromReferer);\n                }\n            } else {\n                // Try to get requested resource from portal theme\n                if (LOGGER.isDebugEnabled()) {\n                    LOGGER.debug(\"Unable tor retrieve app parameter for resource \" + resourcePath\n                            + \". Request referer is missing an an app parameter. Forwarding to the portal theme.\");\n                }\n                String themePath = THEME_PATH_SEPARATOR + \"/\" + resourcePath;\n                //security check against directory traversal attack\n                customPageRequestModifier.forwardIfRequestIsAuthorized(request, response, THEME_PATH_SEPARATOR,\n                        themePath);\n            }\n        }\n    }\n\n    protected void renderThemeResource(final HttpServletRequest request, final HttpServletResponse response,\n            final APISession apiSession, String resourcePath,\n            String appToken) throws BonitaException, CreationException, IllegalAccessException, IOException {\n        // Try to get requested resource from the current Living application theme\n        Long themeId = getThemeId(apiSession, appToken);\n        resourceRenderer.renderFile(request, response, getResourceFile(response, apiSession, themeId, resourcePath));\n    }\n\n    protected String getResourcePath(final String[] pathInfoSegments) {\n        String resourcePath = null;\n        if (pathInfoSegments.length > 1 && !pathInfoSegments[1].isEmpty()) {\n            resourcePath = pathInfoSegments[1];\n        }\n        return resourcePath;\n    }\n\n    protected String getAppFromReferer(final HttpServletRequest request) {\n        String referer = request.getHeader(HttpHeaders.REFERER);\n        if (referer != null) {\n            List<NameValuePair> paramList = null;\n            try {\n                paramList = URLEncodedUtils.parse(new URI(referer), \"UTF-8\");\n            } catch (URISyntaxException e) {\n                if (LOGGER.isWarnEnabled()) {\n                    LOGGER.warn(\"Unable tor retrieve app parameter. Bad request referer: \" + e.getMessage());\n                }\n            }\n            for (NameValuePair param : paramList) {\n                if (APPLICATION_PARAM.equalsIgnoreCase(param.getName())) {\n                    return param.getValue();\n                }\n            }\n        } else if (LOGGER.isDebugEnabled()) {\n            LOGGER.debug(\"Unable tor retrieve app parameter. Request referer is null.\");\n        }\n        return null;\n    }\n\n    protected Long getThemeId(APISession apiSession, final String appToken) throws BonitaException, CreationException {\n        ApplicationModelFactory applicationModelFactory = new ApplicationModelFactory(\n                TenantAPIAccessor.getLivingApplicationAPI(apiSession),\n                TenantAPIAccessor.getCustomPageAPI(apiSession),\n                TenantAPIAccessor.getProfileAPI(apiSession));\n        return applicationModelFactory.createApplicationModel(appToken).getApplicationThemeId();\n    }\n\n    protected void resolveAndDisplayPage(final HttpServletRequest request, final HttpServletResponse response,\n            final APISession apiSession,\n            final String mappingKey, final String resourcePath)\n            throws BonitaException, IOException, InstantiationException, IllegalAccessException {\n\n        boolean isNotResourcePath = isNotResourcePath(resourcePath);\n        try {\n            Locale currentLocale = pageRenderer.getCurrentLocale(request);\n            final PageReference pageReference = pageMappingService.getPage(request, apiSession, mappingKey,\n                    currentLocale,\n                    isNotResourcePath);\n            if (pageReference.getUrl() != null) {\n                displayExternalPage(response, pageReference.getUrl());\n            } else if (pageReference.getPageId() != null) {\n                displayPageOrResource(request, response, apiSession, pageReference.getPageId(), resourcePath,\n                        currentLocale);\n            } else {\n                if (LOGGER.isDebugEnabled()) {\n                    final String message = \"Both URL and pageId are not set in the page mapping for \" + mappingKey;\n                    LOGGER.debug(message);\n                }\n            }\n        } catch (final UnauthorizedAccessException e) {\n            final String message = \"User not Authorized\";\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(\"Forbidden: \" + message, e);\n            }\n            if (isNotResourcePath) {\n                response.sendError(HttpServletResponse.SC_FORBIDDEN, message);\n            } else {\n                response.setStatus(HttpServletResponse.SC_FORBIDDEN);\n                response.flushBuffer();\n            }\n        } catch (final NotFoundException e) {\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(\"Not found: Cannot find the form mapping for key \" + mappingKey, e);\n            }\n            if (isNotResourcePath) {\n                response.sendError(HttpServletResponse.SC_NOT_FOUND, \"Form mapping not found\");\n            } else {\n                response.setStatus(HttpServletResponse.SC_NOT_FOUND);\n                response.flushBuffer();\n            }\n\n        }\n    }\n\n    protected void displayPageOrResource(final HttpServletRequest request, final HttpServletResponse response,\n            final APISession apiSession,\n            final Long pageId, final String resourcePath, final Locale currentLocale)\n            throws InstantiationException, IllegalAccessException, IOException, BonitaException {\n        boolean isNotResourcePath = isNotResourcePath(resourcePath);\n        try {\n            if (isNotResourcePath) {\n                pageRenderer.displayCustomPage(request, response, apiSession, pageId, currentLocale);\n            } else {\n                resourceRenderer.renderFile(request, response,\n                        getResourceFile(response, apiSession, pageId, resourcePath));\n            }\n        } catch (final PageNotFoundException e) {\n            if (LOGGER.isWarnEnabled()) {\n                LOGGER.warn(\"Cannot find the page with ID \" + pageId);\n            }\n            if (isNotResourcePath) {\n                response.sendError(HttpServletResponse.SC_NOT_FOUND, \"Page not found\");\n            } else {\n                response.setStatus(HttpServletResponse.SC_NOT_FOUND);\n                response.flushBuffer();\n            }\n        }\n    }\n\n    private boolean isNotResourcePath(final String resourcePath) {\n        return resourcePath == null || CustomPageService.PAGE_INDEX_FILENAME.equals(resourcePath)\n                || CustomPageService.PAGE_CONTROLLER_FILENAME.equals(resourcePath)\n                || CustomPageService.PAGE_INDEX_NAME.equals(resourcePath);\n    }\n\n    protected File getResourceFile(final HttpServletResponse response, final APISession apiSession, final Long pageId,\n            final String resourcePath)\n            throws IOException, BonitaException {\n        final PageResourceProviderImpl pageResourceProvider = pageRenderer.getPageResourceProvider(pageId, apiSession);\n        final File resourceFile = pageResourceProvider\n                .getResourceAsFile(CustomPageService.RESOURCES_PROPERTY + File.separator + resourcePath);\n        if (!bonitaHomeFolderAccessor.isInFolder(resourceFile, pageResourceProvider.getPageDirectory())) {\n            final String message = \"For security reasons, access to this file path is forbidden : \" + resourcePath;\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(\"Forbidden: \" + message);\n            }\n            response.setStatus(HttpServletResponse.SC_FORBIDDEN);\n            response.flushBuffer();\n        }\n        pageRenderer.ensurePageFolderIsPresent(apiSession, pageResourceProvider);\n        return resourceFile;\n    }\n\n    protected void displayExternalPage(final HttpServletResponse response, final String url) throws IOException {\n        response.sendRedirect(response.encodeRedirectURL(url));\n    }\n\n    protected void handleException(final HttpServletRequest request, final HttpServletResponse response,\n            final String mappingKey, final boolean isNotResourcePath, final Exception e)\n            throws ServletException, IOException {\n        if (e instanceof IllegalArgumentException) {\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(\"The parameters passed to the servlet are invalid.\", e);\n            }\n            if (isNotResourcePath) {\n                response.sendError(HttpServletResponse.SC_BAD_REQUEST, \"Invalid Request.\");\n            } else {\n                response.setStatus(HttpServletResponse.SC_BAD_REQUEST);\n                response.flushBuffer();\n            }\n        } else if (e instanceof InvalidSessionException) {\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(\"Invalid Bonita engine session.\", e);\n            }\n            SessionUtil.sessionLogout(request.getSession());\n            if (isNotResourcePath) {\n                response.sendError(HttpServletResponse.SC_UNAUTHORIZED, \"Invalid Bonita engine session.\");\n            } else {\n                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);\n                response.flushBuffer();\n            }\n        } else {\n            if (LOGGER.isWarnEnabled()) {\n                final String message = \"Error while trying to display a page or resource for key \" + mappingKey;\n                LOGGER.warn(message, e);\n            }\n            if (!response.isCommitted()) {\n                if (isNotResourcePath) {\n                    response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());\n                } else {\n                    response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);\n                    response.flushBuffer();\n                }\n            } else {\n                throw new IOException(\"The response is already commited.\", e);\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/ResourceRenderer.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.page;\n\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.io.UnsupportedEncodingException;\nimport java.net.URLDecoder;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.codehaus.groovy.control.CompilationFailedException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Class used by servlets to display serve a file\n * Since each instance of the servlet carry an instance of this class, it should have absolutely no instance attribute\n *\n * @author Julien Mege\n */\npublic class ResourceRenderer {\n\n    /**\n     * Logger\n     */\n    private final static Logger LOGGER = LoggerFactory.getLogger(ResourceRenderer.class.getName());\n\n    public void renderFile(final HttpServletRequest request, final HttpServletResponse response,\n            final File resourceFile)\n            throws CompilationFailedException, IllegalAccessException, IOException, BonitaException {\n        renderFile(request, response, resourceFile, false);\n    }\n\n    public void renderFile(final HttpServletRequest request, final HttpServletResponse response,\n            final File resourceFile, final boolean isPage)\n            throws CompilationFailedException, IllegalAccessException, IOException, BonitaException {\n\n        byte[] content;\n        response.setCharacterEncoding(\"UTF-8\");\n\n        try {\n            content = getFileContent(resourceFile);\n\n            response.setContentType(request.getSession().getServletContext()\n                    .getMimeType(resourceFile.getName()));\n            response.setContentLength(content.length);\n            response.setBufferSize(content.length);\n\n            OutputStream out = response.getOutputStream();\n            out.write(content, 0, content.length);\n\n        } catch (final FileNotFoundException e) {\n            if (isPage) {\n                response.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());\n            } else {\n                response.setStatus(HttpServletResponse.SC_NOT_FOUND);\n            }\n        } catch (final IOException e) {\n            if (LOGGER.isErrorEnabled()) {\n                LOGGER.error(\"Error while generating the response.\", e);\n            }\n            throw e;\n        }\n    }\n\n    private byte[] getFileContent(final File resourceFile) throws IOException, BonitaException {\n        if (resourceFile == null) {\n            final String errorMessage = \"Resource file must not be null.\";\n            if (LOGGER.isWarnEnabled()) {\n                LOGGER.warn(errorMessage);\n            }\n            throw new BonitaException(errorMessage);\n        }\n        if (resourceFile.exists()) {\n            return Files.readAllBytes(resourceFile.toPath());\n        } else {\n            final String fileNotFoundMessage = \"Cannot find the resource file \";\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(fileNotFoundMessage + resourceFile.getCanonicalPath());\n            }\n            throw new FileNotFoundException(fileNotFoundMessage + resourceFile.getName());\n        }\n    }\n\n    public List<String> getPathSegments(final String pathInfo) throws UnsupportedEncodingException {\n        final List<String> segments = new ArrayList<>();\n        if (pathInfo != null) {\n            for (final String segment : pathInfo.split(\"/\")) {\n                if (!segment.isEmpty()) {\n                    segments.add(URLDecoder.decode(segment, StandardCharsets.UTF_8));\n                }\n            }\n        }\n        return segments;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/RestApiRenderer.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.page;\n\nimport java.io.IOException;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport groovy.lang.GroovyClassLoader;\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.console.common.server.page.extension.PageResourceProviderImpl;\nimport org.bonitasoft.console.common.server.page.extension.RestAPIContextImpl;\nimport org.bonitasoft.engine.api.APIClient;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.extension.rest.RestApiController;\nimport org.bonitasoft.web.extension.rest.RestApiResponse;\nimport org.bonitasoft.web.extension.rest.RestApiResponseBuilder;\nimport org.bonitasoft.web.rest.server.api.extension.ControllerClassName;\nimport org.bonitasoft.web.rest.server.api.extension.ResourceExtensionResolver;\nimport org.codehaus.groovy.control.CompilationFailedException;\n\n/**\n * Class used by servlets to display a custom rest api\n * Since each instance of the servlet carry an instance of this class, it should have absolutely no instance attribute\n *\n * @author Laurent Leseigneur\n */\n@Slf4j\npublic class RestApiRenderer {\n\n    private final CustomPageService customPageService = new CustomPageService();\n\n    public RestApiResponse handleRestApiCall(final HttpServletRequest request,\n            ResourceExtensionResolver resourceExtensionResolver)\n            throws CompilationFailedException, InstantiationException, IllegalAccessException, IOException,\n            BonitaException {\n        final PageContextHelper pageContextHelper = new PageContextHelper(request);\n        final APISession apiSession = pageContextHelper.getApiSession();\n        final Long pageId = resourceExtensionResolver.resolvePageId(apiSession);\n        final Page page = customPageService.getPage(apiSession, pageId);\n        final PageResourceProviderImpl pageResourceProvider = new PageResourceProviderImpl(page);\n        customPageService.ensurePageFolderIsUpToDate(apiSession, pageResourceProvider);\n        final ControllerClassName restApiControllerClassName = resourceExtensionResolver\n                .resolveRestApiControllerClassName(pageResourceProvider);\n        final String mappingKey = resourceExtensionResolver.generateMappingKey();\n        return renderResponse(request, apiSession, pageContextHelper, pageResourceProvider, restApiControllerClassName,\n                mappingKey);\n    }\n\n    private RestApiResponse renderResponse(final HttpServletRequest request,\n            final APISession apiSession,\n            final PageContextHelper pageContextHelper,\n            final PageResourceProviderImpl pageResourceProvider, ControllerClassName restApiControllerClassName,\n            String mappingKey)\n            throws CompilationFailedException, InstantiationException, IllegalAccessException, IOException,\n            BonitaException {\n        final ClassLoader originalClassloader = Thread.currentThread().getContextClassLoader();\n        final GroovyClassLoader pageClassloader = customPageService.getPageClassloader(apiSession,\n                pageResourceProvider);\n        try {\n            Thread.currentThread().setContextClassLoader(pageClassloader);\n            final Class<?> restApiControllerClass = customPageService.registerRestApiPage(pageClassloader,\n                    pageResourceProvider, restApiControllerClassName, mappingKey);\n            pageResourceProvider.setResourceClassLoader(pageClassloader);\n            try {\n                return doHandle(request, apiSession, pageContextHelper, pageResourceProvider, restApiControllerClass);\n            } catch (final Throwable e) {\n                log.error(\"Error when executing rest api extension call to {}\", mappingKey, e);\n                throw e;\n            }\n        } finally {\n            Thread.currentThread().setContextClassLoader(originalClassloader);\n        }\n    }\n\n    protected RestApiResponse doHandle(final HttpServletRequest request,\n            final APISession apiSession,\n            final PageContextHelper pageContextHelper,\n            final PageResourceProviderImpl pageResourceProvider,\n            final Class<?> restApiControllerClass)\n            throws InstantiationException, IllegalAccessException {\n        final RestApiController restApiController = instantiate(restApiControllerClass);\n        return restApiController.doHandle(request,\n                new RestApiResponseBuilder(),\n                new RestAPIContextImpl(apiSession,\n                        new APIClient(apiSession),\n                        pageContextHelper.getCurrentLocale(),\n                        pageResourceProvider));\n    }\n\n    protected <T> T instantiate(Class<?> baseClass)\n            throws InstantiationException, IllegalAccessException {\n        return (T) baseClass.newInstance();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/VersionedClassloader.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.page;\n\npublic interface VersionedClassloader {\n\n    String getVersion();\n\n    boolean hasVersion(String version);\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/extension/PageContextImpl.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.page.extension;\n\nimport java.util.Locale;\n\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.extension.page.PageContext;\n\n/**\n * This class provide access to the data relative to the context in which the custom page is displayed\n *\n * @author Anthony Birembaut\n */\npublic class PageContextImpl implements PageContext {\n\n    protected final APISession apiSession;\n\n    protected final Locale locale;\n\n    protected final String profileID;\n\n    public PageContextImpl(final APISession apiSession, final Locale locale, final String profileID) {\n        super();\n        this.apiSession = apiSession;\n        this.locale = locale;\n        this.profileID = profileID;\n    }\n\n    /**\n     * @return the engine {@link APISession}\n     */\n    public APISession getApiSession() {\n        return apiSession;\n    }\n\n    /**\n     * @return the user locale\n     */\n    public Locale getLocale() {\n        return locale;\n    }\n\n    /**\n     * @return the ID of the profile in which the page is currently displayed\n     */\n    public String getProfileID() {\n        return profileID;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/extension/PageResourceProviderImpl.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.page.extension;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.Locale;\nimport java.util.ResourceBundle;\n\nimport org.apache.commons.io.IOUtils;\nimport org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils;\nimport org.bonitasoft.engine.api.PageAPI;\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.engine.page.PageNotFoundException;\nimport org.bonitasoft.web.extension.page.PageResourceProvider;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * This class provide access to the resources contained in the custom page zip\n *\n * @author Anthony Birembaut\n */\npublic class PageResourceProviderImpl implements PageResourceProvider {\n\n    /**\n     * Logger\n     */\n    private static final Logger LOGGER = LoggerFactory.getLogger(PageResourceProviderImpl.class.getName());\n\n    private static final String VERSION_FILENAME = \"VERSION\";\n\n    protected final static String THEME_RESOURCE_SERVLET_NAME = \"themeResource\";\n\n    protected final static String PORTAL_THEME_NAME = \"portal\";\n\n    protected final static String BONITA_THEME_CSS_FILENAME = \"bonita.css\";\n\n    protected static String productVersion;\n\n    static {\n        final InputStream versionStream = PageResourceProviderImpl.class.getClassLoader()\n                .getResourceAsStream(VERSION_FILENAME);\n        if (versionStream != null) {\n            try {\n                productVersion = IOUtils.toString(versionStream);\n            } catch (final Exception e) {\n                if (LOGGER.isWarnEnabled()) {\n                    LOGGER.warn(\"Unable to read the file \" + VERSION_FILENAME, e);\n                }\n                productVersion = \"\";\n            } finally {\n                try {\n                    versionStream.close();\n                } catch (final IOException e) {\n                    if (LOGGER.isWarnEnabled()) {\n                        LOGGER.warn(\"Unable to close the input stream for file \" + VERSION_FILENAME, e);\n                    }\n                }\n            }\n        } else {\n            productVersion = \"\";\n        }\n    }\n\n    /**\n     * product version param\n     */\n    protected final static String VERSION_PARAM = \"v\";\n\n    /**\n     * file name\n     */\n    protected final static String LOCATION_PARAM = \"location\";\n\n    /**\n     * theme name : the theme folder's name\n     */\n    protected final static String THEME_PARAM = \"theme\";\n\n    private final String fullPageName;\n\n    protected String pageName;\n\n    protected File pageDirectory;\n\n    private ClassLoader resourceClassLoader;\n\n    private File pageTempFile = null;\n\n    private final Long pageId;\n\n    public PageResourceProviderImpl(final String pageName) {\n        this(pageName, null, null, true);\n    }\n\n    public PageResourceProviderImpl(final Page page) {\n        this(page.getName(), page.getId(), page.getProcessDefinitionId(), true);\n    }\n\n    private PageResourceProviderImpl(final String pageName, final Long pageId, final Long processDefinitionId,\n            final boolean buildPageTempFile) {\n        this.pageName = pageName;\n        this.pageId = pageId;\n        fullPageName = buildFullPageName(pageName, processDefinitionId);\n        pageDirectory = buildPageDirectory(fullPageName);\n        if (buildPageTempFile) {\n            buildPageTempDirectory(fullPageName);\n            pageTempFile = buildPageTempFile(fullPageName);\n        }\n    }\n\n    private String buildFullPageName(final String pageName, final Long processDefinitionId) {\n        final StringBuilder builder = new StringBuilder();\n        if (processDefinitionId != null) {\n            builder.append(\"p\").append(processDefinitionId).append(\"_\");\n        }\n        builder.append(pageName);\n        return builder.toString();\n    }\n\n    protected void buildPageTempDirectory(final String fullPageName) {\n        new File(WebBonitaConstantsUtils.getTenantInstance().getTempFolder(), fullPageName);\n    }\n\n    protected File buildPageTempFile(final String fullPageName) {\n        return new File(WebBonitaConstantsUtils.getTenantInstance().getTempFolder(), fullPageName + \".zip\");\n    }\n\n    protected File buildPageDirectory(final String fullPageName) {\n        return new File(WebBonitaConstantsUtils.getTenantInstance().getPagesFolder(), fullPageName);\n    }\n\n    @Override\n    public InputStream getResourceAsStream(final String resourceName) throws FileNotFoundException {\n        return new FileInputStream(getResourceAsFile(resourceName));\n    }\n\n    @Override\n    public File getResourceAsFile(final String resourceName) {\n        return new File(pageDirectory, resourceName);\n    }\n\n    @Override\n    public String getResourceURL(final String resourceName) {\n        return resourceName + \"?\" + VERSION_PARAM + \"=\" + productVersion;\n    }\n\n    @Override\n    public String getBonitaThemeCSSURL() {\n        return THEME_RESOURCE_SERVLET_NAME + \"?\" + THEME_PARAM +\n                \"=\" + PORTAL_THEME_NAME + \"&\" + LOCATION_PARAM + \"=\" + BONITA_THEME_CSS_FILENAME + \"&\" +\n                VERSION_PARAM + \"=\" + productVersion;\n    }\n\n    @Override\n    public File getPageDirectory() {\n        return pageDirectory;\n    }\n\n    public File getTempPageFile() {\n        return pageTempFile;\n    }\n\n    public void setResourceClassLoader(final ClassLoader resourceClassLoader) {\n        this.resourceClassLoader = resourceClassLoader;\n    }\n\n    @Override\n    public ResourceBundle getResourceBundle(final String name, final Locale locale) {\n        return ResourceBundle.getBundle(name, locale, resourceClassLoader);\n    }\n\n    @Override\n    public String getPageName() {\n        return pageName;\n    }\n\n    @Override\n    public Page getPage(final PageAPI pageAPI) throws PageNotFoundException {\n        if (pageId != null) {\n            return pageAPI.getPage(pageId);\n        }\n        return pageAPI.getPageByName(getPageName());\n    }\n\n    @Override\n    public String getFullPageName() {\n        return fullPageName;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/extension/RestAPIContextImpl.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.page.extension;\n\nimport java.util.Locale;\n\nimport org.bonitasoft.engine.api.APIClient;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.extension.page.PageResourceProvider;\nimport org.bonitasoft.web.extension.rest.RestAPIContext;\n\npublic class RestAPIContextImpl implements RestAPIContext {\n\n    private final APISession apiSession;\n    private final Locale locale;\n    private final PageResourceProvider resourceProvider;\n    private final APIClient apiClient;\n\n    public RestAPIContextImpl(final APISession apiSession, final APIClient apiClient, final Locale locale,\n            PageResourceProvider resourceProvider) {\n        this.apiSession = apiSession;\n        this.locale = locale;\n        this.resourceProvider = resourceProvider;\n        this.apiClient = apiClient;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see org.bonitasoft.console.common.server.page.RestAPIContext#getApiSession()\n     */\n    @Override\n    public APISession getApiSession() {\n        return apiSession;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see org.bonitasoft.console.common.server.page.RestAPIContext#getLocale()\n     */\n    @Override\n    public Locale getLocale() {\n        return locale;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see org.bonitasoft.console.common.server.page.RestAPIContext#getResourceProvider()\n     */\n    @Override\n    public PageResourceProvider getResourceProvider() {\n        return resourceProvider;\n    }\n\n    @Override\n    public APIClient getApiClient() {\n        return apiClient;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/preferences/constants/WebBonitaConstants.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.preferences.constants;\n\nimport java.io.File;\nimport java.lang.management.ManagementFactory;\n\n/**\n * @author Nicolas Chabanoles\n */\npublic interface WebBonitaConstants {\n\n    /**\n     * tenants folder\n     */\n    String tenantsFolderName = \"tenant\";\n\n    /**\n     * Generics folders\n     */\n\n    String clientFolderName = \"client\";\n\n    String tmpFolderName = \"bonita_portal_\";\n\n    // We use a tempFolder specific to the running JVM, so that 2 JVMs running on the same machine are isolated:\n    String rootTempDir = System.getProperty(\"java.io.tmpdir\") + File.separator + tmpFolderName\n            + ManagementFactory.getRuntimeMXBean().getName();\n\n    String formsFolderName = \"forms\";\n\n    String bdmFolderName = \"bdm\";\n\n    /**\n     * Client\n     */\n    String clientFolderPath = clientFolderName + File.separator;\n\n    /**\n     * Get Tenants Folder Path\n     *\n     * @return path\n     */\n    String getTenantsFolderPath();\n\n    /**\n     * Get Tenant TempFolder Path\n     *\n     * @return path\n     */\n    String getTempFolderPath();\n\n    /**\n     * Get Tenant FormsTempFolder Path\n     *\n     * @return path\n     */\n    String getFormsTempFolderPath();\n\n    /**\n     * Get pagesConsoleTempFolder Path\n     *\n     * @return path\n     */\n    String getPagesTempFolderPath();\n\n    /**\n     * Get BDMTempFolderPath Path\n     *\n     * @return path\n     */\n    String getBDMTempFolderPath();\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/preferences/constants/WebBonitaConstantsImpl.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.preferences.constants;\n\nimport java.io.File;\nimport java.nio.file.Paths;\n\n/**\n * @author Ruiheng.Fan\n */\npublic class WebBonitaConstantsImpl implements WebBonitaConstants {\n\n    /**\n     * platform folder\n     */\n    private static final String platformFolderName = \"platform\";\n\n    private String platformFolderPath = null;\n\n    /**\n     * tenants folder\n     */\n    private String tenantsFolderPath = null;\n\n    /**\n     * tmp\n     */\n    private String tempFolderPath = null;\n\n    /**\n     * conf\n     */\n    private String confFolderPath = null;\n\n    private String formsWorkFolderPath = null;\n\n    /**\n     * Default constructor.\n     */\n    public WebBonitaConstantsImpl() {\n    }\n\n    private String getPlatformFolderPath() {\n        if (platformFolderPath == null) {\n            platformFolderPath = clientFolderPath + platformFolderName + File.separator;\n        }\n        return platformFolderPath;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public String getTenantsFolderPath() {\n        if (tenantsFolderPath == null) {\n            tenantsFolderPath = Paths.get(getTempFolderPath()).resolveSibling(tenantsFolderName) + File.separator;\n        }\n        return tenantsFolderPath;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public String getTempFolderPath() {\n        if (tempFolderPath == null) {\n            tempFolderPath = rootTempDir + File.separator + platformFolderName + File.separator;\n        }\n        return tempFolderPath;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public String getPagesTempFolderPath() {\n        return null;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public String getFormsTempFolderPath() {\n        if (formsWorkFolderPath == null) {\n            formsWorkFolderPath = getTempFolderPath() + formsFolderName + File.separator;\n        }\n        return formsWorkFolderPath;\n    }\n\n    @Override\n    public String getBDMTempFolderPath() {\n        return null; // does not means anything at platform level\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/preferences/constants/WebBonitaConstantsTenancyImpl.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.preferences.constants;\n\nimport java.io.File;\n\n/**\n * @author Ruiheng.Fan\n */\npublic class WebBonitaConstantsTenancyImpl implements WebBonitaConstants {\n\n    private static final String PAGES_WORK_FOLDER_NAME = \"pages\";\n    private String tenantsFolderPath = null;\n    private final String tempFolderPath;\n    private String formsWorkFolderPath = null;\n    private String pagesWorkFolderPath = null;\n    private String bdmWorkFolderPath;\n\n    /**\n     * Default constructor.\n     */\n    WebBonitaConstantsTenancyImpl() {\n        tempFolderPath = rootTempDir + File.separator + tenantsFolderName + File.separator;\n    }\n\n    @Override\n    public String getTenantsFolderPath() {\n        if (tenantsFolderPath == null) {\n            tenantsFolderPath = rootTempDir + File.separator + tenantsFolderName + File.separator;\n        }\n        return tenantsFolderPath;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public String getTempFolderPath() {\n        return tempFolderPath;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public String getPagesTempFolderPath() {\n        if (pagesWorkFolderPath == null) {\n            pagesWorkFolderPath = getTempFolderPath() + PAGES_WORK_FOLDER_NAME + File.separator;\n        }\n        return pagesWorkFolderPath;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public String getFormsTempFolderPath() {\n        if (formsWorkFolderPath == null) {\n            formsWorkFolderPath = getTempFolderPath() + formsFolderName + File.separator;\n        }\n        return formsWorkFolderPath;\n    }\n\n    @Override\n    public String getBDMTempFolderPath() {\n        if (bdmWorkFolderPath == null) {\n            bdmWorkFolderPath = getTempFolderPath() + File.separator + bdmFolderName + File.separator;\n        }\n        return bdmWorkFolderPath;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/preferences/constants/WebBonitaConstantsUtils.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.preferences.constants;\n\nimport static org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstants.rootTempDir;\nimport static org.bonitasoft.engine.io.IOUtil.createTempDirectory;\n\nimport java.io.File;\n\n/**\n * @author Anthony Birembaut\n */\npublic class WebBonitaConstantsUtils {\n\n    private static WebBonitaConstantsUtils tenantConstantsUtils = new WebBonitaConstantsUtils(\n            new WebBonitaConstantsTenancyImpl());\n\n    private static WebBonitaConstantsUtils platformConstantsUtils = new WebBonitaConstantsUtils(\n            new WebBonitaConstantsImpl());\n\n    private WebBonitaConstants webBonitaConstants;\n\n    public WebBonitaConstantsUtils(WebBonitaConstants constants) {\n        webBonitaConstants = constants;\n    }\n\n    public static WebBonitaConstantsUtils getTenantInstance() {\n        return tenantConstantsUtils;\n    }\n\n    public static WebBonitaConstantsUtils getPlatformInstance() {\n        return platformConstantsUtils;\n    }\n\n    /**\n     * Get the folder where to write Tenant temporary files commons to all web\n     * applications.\n     */\n    public File getTempFolder() {\n        final File tempFolder = new File(rootTempDir);\n        if (!tempFolder.exists()) {\n            createTempDirectory(tempFolder.toURI());\n        }\n        return getFolder(webBonitaConstants.getTempFolderPath());\n    }\n\n    /**\n     * Get the folder of Tenant pages files\n     */\n    public File getPagesFolder() {\n        return getFolder(webBonitaConstants.getPagesTempFolderPath());\n    }\n\n    /**\n     * Get the Tenant folder where to write Work files.\n     */\n    public File getFormsWorkFolder() {\n        return getFolder(webBonitaConstants.getFormsTempFolderPath());\n    }\n\n    /**\n     * Get the Tenant folder where to write BDM work files.\n     */\n    public File geBDMWorkFolder() {\n        return getFolder(webBonitaConstants.getBDMTempFolderPath());\n    }\n\n    private File getFolder(final String folderPath) {\n        final File folder = new File(folderPath);\n        if (!folder.exists()) {\n            folder.mkdirs();\n        }\n        return folder;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/preferences/properties/ConfigurationFile.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.preferences.properties;\n\nimport static org.bonitasoft.console.common.server.preferences.properties.PropertiesWithSet.stringToSet;\n\nimport java.util.Properties;\nimport java.util.Set;\n\n/**\n * @author Ruiheng Fan, Anthony Birembaut\n */\npublic class ConfigurationFile {\n\n    private final String propertiesFilename;\n\n    public ConfigurationFile(String propertiesFilename) {\n        this.propertiesFilename = propertiesFilename;\n    }\n\n    public String getTenantProperty(final String propertyName) {\n        final Properties properties = getTenantPropertiesOfScope();\n        final String propertyValue = properties.getProperty(propertyName);\n        return propertyValue != null ? propertyValue.trim() : null;\n    }\n\n    public Properties getTenantPropertiesOfScope() {\n        return ConfigurationFilesManager.getInstance().getTenantProperties(propertiesFilename);\n    }\n\n    public Set<String> getPropertyAsSet(final String propertyName) {\n        final String propertyAsString = getTenantProperty(propertyName);\n        return stringToSet(propertyAsString);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/preferences/properties/ConfigurationFilesManager.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.preferences.properties;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Properties;\n\nimport org.apache.commons.io.FileUtils;\nimport org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils;\nimport org.bonitasoft.console.common.server.utils.PlatformManagementUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Baptiste Mesta, Emmanuel Duchastenier, Anthony Birembaut\n */\npublic class ConfigurationFilesManager {\n\n    private static final ConfigurationFilesManager INSTANCE = new ConfigurationFilesManager();\n\n    public static ConfigurationFilesManager getInstance() {\n        return INSTANCE;\n    }\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationFilesManager.class.getName());\n\n    private final Map<String, Properties> tenantConfigurations = new HashMap<>();\n\n    private final Map<String, File> tenantConfigurationFiles = new HashMap<>();\n\n    private Map<String, Properties> platformConfigurations = new HashMap<>();\n\n    private final Map<String, File> platformConfigurationFiles = new HashMap<>();\n\n    public Properties getPlatformProperties(String propertiesFile) {\n        Properties properties = platformConfigurations.get(propertiesFile);\n        if (properties == null) {\n            return new Properties();\n        }\n        return properties;\n    }\n\n    Properties getAlsoCustomAndInternalPropertiesFromFilename(String propertiesFileName) {\n        Properties properties = new Properties();\n        Properties tenantConfiguration = getTenantConfiguration(propertiesFileName);\n        if (tenantConfiguration != null) {\n            properties.putAll(tenantConfiguration);\n            // if -internal properties also exists, merge key/value pairs:\n            final String internalPropertyFilename = getSuffixedPropertyFilename(propertiesFileName, \"-internal\");\n            final Properties internalConfiguration = getTenantConfiguration(internalPropertyFilename);\n            if (internalConfiguration != null) {\n                properties.putAll(internalConfiguration);\n            }\n            // if -custom properties also exists, merge key/value pairs (and overwrite previous values if same key name):\n            final String customPropertyFilename = getSuffixedPropertyFilename(propertiesFileName, \"-custom\");\n            final Properties customConfiguration = getTenantConfiguration(customPropertyFilename);\n            if (customConfiguration != null) {\n                properties.putAll(customConfiguration);\n            }\n        } else {\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(\"File {} not found. Returning empty properties object.\", propertiesFileName);\n            }\n        }\n        return properties;\n    }\n\n    public Properties getTenantProperties(String propertiesFileName) {\n        return getAlsoCustomAndInternalPropertiesFromFilename(propertiesFileName);\n    }\n\n    /**\n     * Parses the content as a Properties object.\n     * If content is null, return empty properties.\n     */\n    public static Properties getProperties(byte[] content) {\n        Properties properties = new Properties();\n        if (content != null) {\n            try (ByteArrayInputStream inputStream = new ByteArrayInputStream(content)) {\n                properties.load(inputStream);\n            } catch (IOException ioe) {\n                LOGGER.error(\"Cannot parse properties file content\", ioe);\n            }\n        }\n        return properties;\n    }\n\n    public void setPlatformConfigurations(Map<String, byte[]> configurationFiles) throws IOException {\n        platformConfigurations = new HashMap<>(configurationFiles.size());\n        for (Map.Entry<String, byte[]> entry : configurationFiles.entrySet()) {\n            if (entry.getKey().endsWith(\".properties\")) {\n                platformConfigurations.put(entry.getKey(), getProperties(entry.getValue()));\n            } else {\n                File file = new File(WebBonitaConstantsUtils.getPlatformInstance().getTempFolder(), entry.getKey());\n                FileUtils.writeByteArrayToFile(file, entry.getValue());\n                platformConfigurationFiles.put(entry.getKey(), file);\n            }\n        }\n    }\n\n    public synchronized void setTenantConfigurationFiles(Map<String, byte[]> configurationFiles) throws IOException {\n        for (Map.Entry<String, byte[]> entry : configurationFiles.entrySet()) {\n            if (!entry.getKey().endsWith(\".properties\")) {\n                File file = new File(WebBonitaConstantsUtils.getTenantInstance().getTempFolder(), entry.getKey());\n                FileUtils.writeByteArrayToFile(file, entry.getValue());\n                tenantConfigurationFiles.put(entry.getKey(), file);\n            }\n            tenantConfigurations.put(entry.getKey(),\n                    ConfigurationFilesManager.getProperties(entry.getValue()));\n        }\n    }\n\n    private String getSuffixedPropertyFilename(String propertiesFilename, String suffix) {\n        return propertiesFilename.replaceAll(\"\\\\.properties$\", suffix + \".properties\");\n    }\n\n    PlatformManagementUtils getPlatformManagementUtils() {\n        return new PlatformManagementUtils();\n    }\n\n    public File getTenantConfigurationFile(String fileName) {\n        if (tenantConfigurationFiles.isEmpty()) {\n            try {\n                setTenantConfigurationFiles(getPlatformManagementUtils().readTenantConfigurationsFromEngine());\n            } catch (IOException e) {\n                LOGGER.error(\"Cannot retrieve tenant configuration files\", e);\n                throw new RuntimeException(e);\n            }\n        }\n        return tenantConfigurationFiles.get(fileName);\n    }\n\n    Properties getTenantConfiguration(String propertiesFilename) {\n        if (tenantConfigurations.isEmpty()) {\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(\"Tenant configuration file {} not yet in cache. Adding it.\", propertiesFilename);\n            }\n            try {\n                setTenantConfigurationFiles(getPlatformManagementUtils().readTenantConfigurationsFromEngine());\n            } catch (IOException e) {\n                LOGGER.error(\"Cannot retrieve tenant configuration\", e);\n                throw new RuntimeException(e);\n            }\n        } else if (LOGGER.isDebugEnabled()) {\n            LOGGER.debug(\"Retrieving tenant configuration file {} from cache.\", propertiesFilename);\n        }\n        return tenantConfigurations.get(propertiesFilename);\n    }\n\n    public File getPlatformConfigurationFile(String fileName) {\n        return platformConfigurationFiles.get(fileName);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/preferences/properties/ConsoleProperties.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.preferences.properties;\n\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Properties;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * @author Yang zhiheng\n */\npublic class ConsoleProperties {\n\n    /**\n     * Document max size\n     */\n    private static final String ATTACHMENT_MAX_SIZE = \"form.attachment.max.size\";\n\n    /**\n     * Image upload max size\n     */\n    private static final String IMAGE_UPLOAD_MAX_SIZE = \"image.upload.max.size\";\n\n    /**\n     * Custom page and rest api ext debug mode\n     */\n    private static final String CUSTOM_PAGE_DEBUG = \"custom.page.debug\";\n\n    /**\n     * time between two database check of custom page and rest api last update date in milliseconds\n     */\n    private static final String PAGE_LAST_UPDATE_CHECK_INTERVAL_MILLIS = \"custom.page.lastupdate.database.check.interval.milliseconds\";\n\n    //Default time between two database check of custom page and rest api last update date in milliseconds\n    private static final int DEFAULT_PAGE_LAST_UPDATE_CHECK_INTERVAL_MILLIS = 3000;\n\n    /** Name of attribute attached to the request to indicate the request id */\n    private static final String REQUEST_ID_ATTRIBUTE_NAME = \"req.requestId.attributeName\";\n    private static final String DEFAULT_REQUEST_ID_ATTRIBUTE_NAME = \"track.requestId\";\n    /** Name of request header containing the request id when not already attached */\n    private static final String REQUEST_ID_HEADER_NAME = \"req.requestId.headerName\";\n    private static final String DEFAULT_REQUEST_ID_HEADER_NAME = \"X-Request-ID\";\n    /** Name of attribute attached to the request to indicate the correlation id */\n    private static final String CORRELATION_ID_ATTRIBUTE_NAME = \"req.correlationId.attributeName\";\n    private static final String DEFAULT_CORRELATION_ID_ATTRIBUTE_NAME = \"track.correlationId\";\n    /** Name of request header containing the correlation id when not already attached */\n    private static final String CORRELATION_ID_HEADER_NAME = \"req.correlationId.headerName\";\n    private static final String DEFAULT_CORRELATION_ID_HEADER_NAME = \"X-Correlation-ID\";\n\n    private static final String PROPERTIES_FILE = \"console-config.properties\";\n\n    private static Map<String, Optional<String>> consoleProperties;\n\n    public Properties getProperties() {\n        return ConfigurationFilesManager.getInstance().getTenantProperties(PROPERTIES_FILE);\n    }\n\n    public long getMaxSize() {\n        final String maxSize = this.getProperty(ATTACHMENT_MAX_SIZE);\n        if (maxSize != null) {\n            return Long.valueOf(maxSize);\n        }\n        return 15;\n    }\n\n    public long getImageMaxSizeInKB() {\n        final String maxSize = this.getProperty(IMAGE_UPLOAD_MAX_SIZE);\n        if (maxSize != null) {\n            return Long.valueOf(maxSize);\n        }\n        return 100;\n    }\n\n    public boolean isPageInDebugMode() {\n        final String debugMode = this.getProperty(CUSTOM_PAGE_DEBUG);\n        return Boolean.parseBoolean(debugMode);\n    }\n\n    public long getPageLastUpdateCheckInterval() {\n        final String pageLastUpdateCheckInterval = this.getProperty(PAGE_LAST_UPDATE_CHECK_INTERVAL_MILLIS);\n        if (pageLastUpdateCheckInterval != null) {\n            return Long.valueOf(pageLastUpdateCheckInterval);\n        }\n        return DEFAULT_PAGE_LAST_UPDATE_CHECK_INTERVAL_MILLIS;\n    }\n\n    public String getRequestIdAttributeName() {\n        return Optional.ofNullable(this.getProperty(REQUEST_ID_ATTRIBUTE_NAME))\n                .orElse(DEFAULT_REQUEST_ID_ATTRIBUTE_NAME);\n    }\n\n    public String getRequestIdHeaderName() {\n        return Optional.ofNullable(this.getProperty(REQUEST_ID_HEADER_NAME)).orElse(DEFAULT_REQUEST_ID_HEADER_NAME);\n    }\n\n    public String getCorrelationIdAttributeName() {\n        return Optional.ofNullable(this.getProperty(CORRELATION_ID_ATTRIBUTE_NAME))\n                .orElse(DEFAULT_CORRELATION_ID_ATTRIBUTE_NAME);\n    }\n\n    public String getCorrelationIdHeaderName() {\n        return Optional.ofNullable(this.getProperty(CORRELATION_ID_HEADER_NAME))\n                .orElse(DEFAULT_CORRELATION_ID_HEADER_NAME);\n    }\n\n    public String getProperty(String propertyName) {\n        if (consoleProperties == null) {\n            consoleProperties = new ConcurrentHashMap<>();\n        }\n        Optional<String> propertyValue = consoleProperties.get(propertyName);\n        if (propertyValue == null) {\n            propertyValue = Optional.ofNullable(getProperties().getProperty(propertyName));\n            consoleProperties.put(propertyName, propertyValue);\n        }\n        return propertyValue.orElse(null);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/preferences/properties/PropertiesFactory.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.preferences.properties;\n\n/**\n * @author Anthony Birembaut\n */\npublic class PropertiesFactory {\n\n    public static SecurityProperties getSecurityProperties() {\n        return new SecurityProperties();\n    }\n\n    public static ConsoleProperties getConsoleProperties() {\n        return new ConsoleProperties();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/preferences/properties/PropertiesWithSet.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.preferences.properties;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Properties;\nimport java.util.Set;\n\n/**\n * @author Baptiste Mesta\n */\npublic class PropertiesWithSet extends Properties {\n\n    public PropertiesWithSet(Properties properties) {\n        super(properties);\n    }\n\n    public PropertiesWithSet(File file) {\n        try (FileInputStream inStream = new FileInputStream(file)) {\n            this.load(inStream);\n        } catch (IOException e) {\n            throw new IllegalStateException(e);\n        }\n    }\n\n    public Set<String> getPropertyAsSet(final String propertyName) {\n        return stringToSet(getProperty(propertyName));\n    }\n\n    public static Set<String> stringToSet(final String propertyValueAsString) {\n        if (propertyValueAsString != null) {\n            final Set<String> propertiesSet = new HashSet<>();\n            final String propertyValueAsStringTrimmed = propertyValueAsString.trim();\n            if (propertyValueAsStringTrimmed.startsWith(\"[\") && propertyValueAsStringTrimmed.endsWith(\"]\")) {\n                String propertyCSV = propertyValueAsStringTrimmed.substring(1,\n                        propertyValueAsStringTrimmed.length() - 1);\n                propertyCSV = propertyCSV.trim();\n                if (propertyCSV.isEmpty()) {\n                    return Collections.emptySet();\n                }\n                final String[] propertyArray = propertyCSV.split(\",\");\n                for (final String propertyValue : propertyArray) {\n                    propertiesSet.add(propertyValue.trim());\n                }\n            } else {\n                propertiesSet.add(propertyValueAsString);\n            }\n            return propertiesSet;\n        } else {\n            return Collections.emptySet();\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/preferences/properties/SecurityProperties.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.preferences.properties;\n\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * Utility class for security properties access\n *\n * @author Anthony Birembaut\n */\npublic class SecurityProperties {\n\n    /**\n     * Default name of the form definition file\n     */\n    public static final String SECURITY_DEFAULT_CONFIG_FILE_NAME = \"security-config.properties\";\n\n    /**\n     * property for the robustness of the password\n     */\n    public static final String PASSWORD_VALIDATOR_CLASSNAME = \"security.password.validator\";\n\n    /**\n     * property for the CSRF protection activation\n     */\n    public static final String CSRF_PROTECTION = \"security.csrf.enabled\";\n\n    /**\n     * Property for the (OWASP) Sanitizer protection activation.\n     * This sanitizer protects against multiple attacks such as XSS, but may restrict the use of some character\n     * sequences.\n     */\n    public static final String SANITIZER_PROTECTION = \"security.sanitizer.enabled\";\n\n    /**\n     * Property for the (OWASP) Sanitizer protection.\n     * The value of this property lists the json attributes that should be excluded when sanitizer protection is active.\n     */\n    public static final String SANITIZER_PROTECTION_EXCLUSIONS = \"security.sanitizer.exclude\";\n\n    /**\n     * default list of attributes excluded from sanitizer protection when the property is not set in\n     * security-config.properties\n     */\n    public static final List<String> DEFAULT_SANITIZER_PROTECTION_EXCLUSIONS = List.of(\"email\", \"password\",\n            \"password_confirm\");\n\n    /**\n     * property for the CSRF token cookie to have the secure flag (HTTPS only)\n     */\n    public static final String SECURE_TOKEN_COOKIE = \"security.csrf.cookie.secure\";\n\n    /**\n     * property allowing to set the X-Frame-Options header value in the response\n     */\n    public static final String X_FRAME_OPTIONS_HEADER = \"bonita.runtime.security.csrf.header.frame.options\";\n\n    /**\n     * property allowing to set the Content-Security-Policy header value in the response\n     */\n    public static final String CONTENT_SECURITY_POLICY_HEADER = \"bonita.runtime.security.csrf.header.content.security.policy\";\n\n    /**\n     * property for the REST API Authorization checks activation\n     */\n    public static final String API_AUTHORIZATIONS_CHECK = \"security.rest.api.authorizations.check.enabled\";\n\n    private static final Map<String, Optional<String>> securityProperties = new ConcurrentHashMap<>();\n\n    /**\n     * @return the password validator property\n     */\n    public String getPasswordValidator() {\n        return getTenantProperty(PASSWORD_VALIDATOR_CLASSNAME);\n    }\n\n    /**\n     * @return the value to allow or not API authorization checks\n     */\n    public boolean isAPIAuthorizationsCheckEnabled() {\n        final String res = getTenantProperty(API_AUTHORIZATIONS_CHECK);\n        return res != null && res.equals(\"true\");\n    }\n\n    /**\n     * @return the value to allow or not CSRF protection\n     */\n    public boolean isCSRFProtectionEnabled() {\n        final String res = getPlatformProperty(CSRF_PROTECTION);\n        return res != null && res.equals(\"true\");\n    }\n\n    /**\n     * @return the value to allow or not Sanitizer activation for protection\n     */\n    public boolean isSanitizerProtectionEnabled() {\n        final String res = getPlatformProperty(SANITIZER_PROTECTION);\n        // keep true as default when not set correctly (empty string, null, or any non-\"false\" value)\n        return !\"false\".equalsIgnoreCase(res);\n    }\n\n    /**\n     * @return the attributes to exclude from Sanitizer protection, comma separated\n     */\n    public List<String> getAttributeExcludedFromSanitizerProtection() {\n        String excludedAttributes = getPlatformProperty(SANITIZER_PROTECTION_EXCLUSIONS);\n        if (excludedAttributes == null) {\n            return DEFAULT_SANITIZER_PROTECTION_EXCLUSIONS;\n        } else if (excludedAttributes.isBlank()) {\n            return Collections.emptyList();\n        }\n        return Arrays.asList(excludedAttributes.trim().split(\"\\\\s*,\\\\s*\"));\n    }\n\n    /**\n     * @return the value to add or not secure flag to the cookies for CSRF token\n     */\n    public boolean isCSRFTokenCookieSecure() {\n        final String res = getPlatformProperty(SECURE_TOKEN_COOKIE);\n        return res != null && res.equals(\"true\");\n    }\n\n    public String getXFrameOptionsHeader() {\n        return getPlatformProperty(X_FRAME_OPTIONS_HEADER);\n    }\n\n    public String getContentSecurityPolicyHeader() {\n        return getPlatformProperty(CONTENT_SECURITY_POLICY_HEADER);\n    }\n\n    protected ConfigurationFilesManager getConfigurationFilesManager() {\n        return ConfigurationFilesManager.getInstance();\n    }\n\n    public String getTenantProperty(String propertyName) {\n        Properties tenantProperties = getConfigurationFilesManager()\n                .getTenantProperties(SECURITY_DEFAULT_CONFIG_FILE_NAME);\n        Optional<String> propertyValue = securityProperties.get(propertyName);\n        if (propertyValue == null) {\n            propertyValue = Optional.ofNullable(tenantProperties.getProperty(propertyName));\n            securityProperties.put(propertyName, propertyValue);\n        }\n        return propertyValue.orElse(null);\n    }\n\n    public String getPlatformProperty(String propertyName) {\n        Properties platformProperties = getConfigurationFilesManager()\n                .getPlatformProperties(SECURITY_DEFAULT_CONFIG_FILE_NAME);\n        Optional<String> propertyValue = securityProperties.get(propertyName);\n        if (propertyValue == null) {\n            propertyValue = Optional.ofNullable(platformProperties.getProperty(propertyName));\n            securityProperties.put(propertyName, propertyValue);\n        }\n        return propertyValue.orElse(null);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/servlet/ApplicationIconServlet.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.servlet;\n\nimport java.util.Optional;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.api.APIClient;\nimport org.bonitasoft.engine.api.ApplicationAPI;\nimport org.bonitasoft.engine.business.application.Application;\nimport org.bonitasoft.engine.business.application.ApplicationNotFoundException;\nimport org.bonitasoft.engine.business.application.ApplicationUpdater;\nimport org.bonitasoft.engine.business.application.Icon;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.NotFoundException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException;\nimport org.bonitasoft.web.toolkit.client.common.exception.http.ServerException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n@Slf4j\npublic class ApplicationIconServlet extends IconServlet {\n\n    @Override\n    protected Optional<IconContent> retrieveIcon(Long iconId, APISession apiSession) {\n        ApplicationAPI applicationApi = getApplicationApi(apiSession);\n        try {\n            Icon icon = applicationApi.getIconOfApplication(iconId);\n            if (icon != null) {\n                return Optional.of(new IconContent(icon.getContent(), icon.getMimeType()));\n            } else {\n                return Optional.empty();\n            }\n        } catch (NotFoundException e) {\n            return Optional.empty();\n        }\n    }\n\n    /**\n     * {@inheritDoc}\n     *\n     * @deprecated as of 9.0.0, Application icon should be deleted/updated at startup.\n     */\n    @Override\n    @Deprecated(since = \"9.0.0\")\n    protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException {\n        log.warn(\"DELETE request on Application Icon is deprecated! \" +\n                \"An application icon should be deleted or updated at startup instead.\");\n        super.doDelete(request, response);\n    }\n\n    /**\n     * {@inheritDoc}\n     *\n     * @deprecated as of 9.0.0, Application icon should be deleted/updated at startup.\n     */\n    @Override\n    @Deprecated(since = \"9.0.0\")\n    protected void deleteIcon(Long entityId, APISession apiSession, HttpServletRequest request) throws ServerException {\n        ApplicationAPI applicationApi = getApplicationApi(apiSession);\n        ApplicationUpdater updater = new ApplicationUpdater();\n        updater.setIcon(null, null);\n        try {\n            applicationApi.updateApplication(entityId, updater);\n        } catch (ApplicationNotFoundException e) {\n            throw new APIItemNotFoundException(Application.class.getName(), APIID.makeAPIID(entityId));\n        } catch (UpdateException | AlreadyExistsException e) {\n            throw new APIException(e);\n        }\n    }\n\n    ApplicationAPI getApplicationApi(APISession apiSession) {\n        return new APIClient(apiSession).getApplicationAPI();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/servlet/DocumentDownloadServlet.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.servlet;\n\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.apache.commons.io.FilenameUtils;\nimport org.bonitasoft.console.common.server.utils.BPMEngineAPIUtil;\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.document.ArchivedDocument;\nimport org.bonitasoft.engine.bpm.document.Document;\nimport org.bonitasoft.engine.bpm.document.DocumentNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ProcessResourceNotFoundException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.io.FileContent;\nimport org.bonitasoft.engine.session.APISession;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Servlet allowing to download process instances attachments\n *\n * @author Anthony Birembaut\n */\npublic class DocumentDownloadServlet extends HttpServlet {\n\n    /**\n     * UID\n     */\n    private static final long serialVersionUID = 5209516978177786895L;\n\n    /**\n     * process ID\n     */\n    protected static final String PROCESS_ID_PARAM = \"process\";\n\n    /**\n     * instance ID\n     */\n    protected static final String INSTANCE_ID_PARAM = \"instance\";\n\n    /**\n     * task ID\n     */\n    protected static final String TASK_ID_PARAM = \"task\";\n\n    /**\n     * document id of the document to download\n     */\n    protected static final String DOCUMENT_ID_PARAM = \"document\";\n\n    /**\n     * attachment : indicate the path of the process attachment\n     */\n    protected static final String FILE_PATH_PARAM = \"filePath\";\n\n    /**\n     * attachment : indicate the file name of the process attachment\n     */\n    protected static final String FILE_NAME_PARAM = \"fileName\";\n\n    /**\n     * resource : indicate the file name of the process resource\n     */\n    protected static final String RESOURCE_FILE_NAME_PARAM = \"resourceFileName\";\n\n    /**\n     * The engine API session param key name\n     */\n    protected static final String API_SESSION_PARAM_KEY = \"apiSession\";\n\n    /**\n     * content storage id of the document downloaded\n     */\n    protected static final String CONTENT_STORAGE_ID_PARAM = \"contentStorageId\";\n\n    /**\n     * Util class allowing to work with the BPM engine API\n     */\n    protected final BPMEngineAPIUtil bpmEngineAPIUtil = new BPMEngineAPIUtil();\n\n    protected final BonitaHomeFolderAccessor bonitaHomeFolderAccessor = new BonitaHomeFolderAccessor();\n\n    /**\n     * Logger\n     */\n    private static final Logger LOGGER = LoggerFactory.getLogger(DocumentDownloadServlet.class.getName());\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    protected void doGet(final HttpServletRequest request, final HttpServletResponse response)\n            throws ServletException, IOException {\n\n        final String filePath = request.getParameter(FILE_PATH_PARAM);\n        String fileName = request.getParameter(FILE_NAME_PARAM);\n        final String resourcePath = request.getParameter(RESOURCE_FILE_NAME_PARAM);\n        final String documentId = request.getParameter(DOCUMENT_ID_PARAM);\n        String contentStorageId = request.getParameter(CONTENT_STORAGE_ID_PARAM);\n        final APISession apiSession = (APISession) request.getSession().getAttribute(API_SESSION_PARAM_KEY);\n        byte[] content = null;\n        if (filePath != null) {\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(\"attachmentPath: \" + filePath);\n            }\n            try {\n                final FileContent fileContent = bonitaHomeFolderAccessor\n                        .retrieveUploadedTempContent(FilenameUtils.separatorsToSystem(filePath));\n                if (fileName == null) {\n                    fileName = fileContent.getFileName();\n                }\n                try (InputStream inputStream = fileContent.getInputStream()) {\n                    content = getFileContent(inputStream, filePath, fileContent.getSize());\n                }\n            } catch (final BonitaException e) {\n                throw new ServletException(e.getMessage());\n            } catch (final IOException e) {\n                throw new ServletException(e);\n            }\n        } else if (fileName != null && contentStorageId != null) {\n            try {\n                content = bpmEngineAPIUtil.getProcessAPI(apiSession).getDocumentContent(contentStorageId);\n            } catch (final Exception e) {\n                final String errorMessage = \"Error while retrieving the document  with content storage ID \"\n                        + contentStorageId + \" from the engine.\";\n                if (LOGGER.isErrorEnabled()) {\n                    LOGGER.error(errorMessage, e);\n                }\n                throw new ServletException(errorMessage, e);\n            }\n        } else if (documentId != null) {\n            try {\n                final ProcessAPI processAPI = bpmEngineAPIUtil.getProcessAPI(apiSession);\n                try {\n                    final Document document = processAPI.getDocument(Long.valueOf(documentId));\n                    fileName = document.getContentFileName();\n                    contentStorageId = document.getContentStorageId();\n                } catch (final DocumentNotFoundException dnfe) {\n                    final ArchivedDocument archivedDocument = processAPI\n                            .getArchivedVersionOfProcessDocument(Long.valueOf(documentId));\n                    fileName = archivedDocument.getContentFileName();\n                    contentStorageId = archivedDocument.getContentStorageId();\n                }\n                if (contentStorageId != null && !contentStorageId.isEmpty()) {\n                    content = processAPI.getDocumentContent(contentStorageId);\n                }\n            } catch (final Exception e) {\n                final String errorMessage = \"Error while retrieving the document  with ID \" + documentId\n                        + \" from the engine.\";\n                if (LOGGER.isErrorEnabled()) {\n                    LOGGER.error(errorMessage, e);\n                }\n                throw new ServletException(errorMessage, e);\n            }\n        } else if (resourcePath != null) {\n            final String processIDStr = request.getParameter(PROCESS_ID_PARAM);\n            final String instanceIDStr = request.getParameter(INSTANCE_ID_PARAM);\n            final String taskIdStr = request.getParameter(TASK_ID_PARAM);\n            long processDefinitionID = -1;\n            try {\n                if (processIDStr != null) {\n                    processDefinitionID = Long.parseLong(processIDStr);\n                } else if (taskIdStr != null) {\n                    processDefinitionID = getProcessDefinitionIDFromActivityInstanceID(apiSession,\n                            Long.parseLong(taskIdStr));\n                } else if (instanceIDStr != null) {\n                    processDefinitionID = getProcessDefinitionIDFromProcessInstanceID(apiSession,\n                            Long.parseLong(instanceIDStr));\n                } else {\n                    response.sendError(HttpServletResponse.SC_BAD_REQUEST,\n                            \"Either a process, instance or task parameter is required in the URL\");\n                    return;\n                }\n                final ProcessAPI processAPI = bpmEngineAPIUtil.getProcessAPI(apiSession);\n                content = processAPI.getDocumentProcessResource(processDefinitionID, resourcePath);\n                fileName = resourcePath.contains(\"/\")\n                        ? resourcePath.substring(resourcePath.lastIndexOf('/') + 1)\n                        : resourcePath;\n            } catch (final ProcessResourceNotFoundException e) {\n                if (LOGGER.isWarnEnabled()) {\n                    LOGGER.warn(e.getMessage());\n                }\n                response.sendError(HttpServletResponse.SC_NOT_FOUND);\n                return;\n            } catch (final Exception e) {\n                final String errorMessage = \"Error while retrieving the resource \" + resourcePath;\n                if (LOGGER.isErrorEnabled()) {\n                    LOGGER.error(errorMessage, e);\n                }\n                throw new ServletException(errorMessage, e);\n            }\n        } else {\n            final String errorMessage = \"Error while getting the file. either a document, a filePath or a resourcePath parameter is required.\";\n            if (LOGGER.isErrorEnabled()) {\n                LOGGER.error(errorMessage);\n            }\n            throw new ServletException(errorMessage);\n        }\n        response.setContentType(\"application/octet-stream\");\n        response.setCharacterEncoding(StandardCharsets.UTF_8.name());\n        try {\n            final String encodedfileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8);\n            final String userAgent = request.getHeader(\"User-Agent\");\n            if (userAgent != null && userAgent.contains(\"Firefox\")) {\n                response.setHeader(\"Content-Disposition\",\n                        \"attachment; filename*=UTF-8''\" + encodedfileName.replace(\"+\", \"%20\"));\n            } else {\n                response.setHeader(\"Content-Disposition\",\n                        \"attachment; filename=\\\"\" + encodedfileName.replaceAll(\"\\\\+\", \" \") + \"\\\"; filename*=UTF-8''\"\n                                + encodedfileName.replace(\"+\", \"%20\"));\n            }\n            final OutputStream out = response.getOutputStream();\n            if (content == null) {\n                response.setContentLength(0);\n            } else {\n                response.setContentLength(content.length);\n                out.write(content);\n            }\n        } catch (final IOException e) {\n            if (LOGGER.isErrorEnabled()) {\n                LOGGER.error(\"Error while generating the response.\", e);\n            }\n            throw new ServletException(e);\n        }\n    }\n\n    protected byte[] getFileContent(final InputStream inputStream, final String filePath, final long size)\n            throws ServletException, IOException {\n\n        int fileLength = 0;\n        if (size > Integer.MAX_VALUE) {\n            throw new ServletException(\"file \" + filePath + \" too big !\");\n        } else {\n            fileLength = (int) size;\n        }\n\n        byte[] content;\n        try {\n            final byte[] fileContent = new byte[fileLength];\n            try {\n                int offset = 0;\n                int length = fileLength;\n                while (length > 0) {\n                    final int read = inputStream.read(fileContent, offset, length);\n                    if (read <= 0) {\n                        break;\n                    }\n                    length -= read;\n                    offset += read;\n                }\n                content = fileContent;\n            } catch (final FileNotFoundException e) {\n                final String errorMessage = \"Error while getting the attachment. The file \" + filePath\n                        + \" does not exist.\";\n                if (LOGGER.isErrorEnabled()) {\n                    LOGGER.error(errorMessage, e);\n                }\n                throw new ServletException(errorMessage, e);\n            } finally {\n                inputStream.close();\n            }\n        } catch (final IOException e) {\n            final String errorMessage = \"Error while reading attachment (file  : \" + filePath;\n            if (LOGGER.isErrorEnabled()) {\n                LOGGER.error(errorMessage, e);\n            }\n            throw new ServletException(errorMessage, e);\n        }\n        return content;\n    }\n\n    protected long getProcessDefinitionIDFromActivityInstanceID(final APISession session, final long activityInstanceID)\n            throws BonitaException {\n        final ProcessAPI processAPI = TenantAPIAccessor.getProcessAPI(session);\n        return processAPI.getProcessDefinitionIdFromActivityInstanceId(activityInstanceID);\n    }\n\n    protected long getProcessDefinitionIDFromProcessInstanceID(final APISession session, final long processInstanceID)\n            throws BonitaException {\n        final ProcessAPI processAPI = TenantAPIAccessor.getProcessAPI(session);\n        return processAPI.getProcessDefinitionIdFromProcessInstanceId(processInstanceID);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/servlet/DocumentImageServlet.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.servlet;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.net.URLConnection;\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.apache.commons.io.FilenameUtils;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.document.ArchivedDocument;\nimport org.bonitasoft.engine.bpm.document.Document;\nimport org.bonitasoft.engine.bpm.document.DocumentNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ProcessResourceNotFoundException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.io.FileContent;\nimport org.bonitasoft.engine.session.APISession;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Servlet allowing to view process instances attachments as images\n * TODO refactor to remove duplicate code with {@link DocumentDownloadServlet}\n *\n * @author Anthony Birembaut\n */\npublic class DocumentImageServlet extends DocumentDownloadServlet {\n\n    /**\n     * UID\n     */\n    private static final long serialVersionUID = -2397573068771431608L;\n\n    /**\n     * Logger\n     */\n    private static final Logger LOGGER = LoggerFactory.getLogger(DocumentImageServlet.class.getName());\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    protected void doGet(final HttpServletRequest request, final HttpServletResponse response)\n            throws ServletException, IOException {\n\n        final String filePath = request.getParameter(FILE_PATH_PARAM);\n        String fileName = request.getParameter(FILE_NAME_PARAM);\n        final String resourcePath = request.getParameter(RESOURCE_FILE_NAME_PARAM);\n        final String documentId = request.getParameter(DOCUMENT_ID_PARAM);\n        final APISession apiSession = (APISession) request.getSession().getAttribute(API_SESSION_PARAM_KEY);\n        byte[] content = null;\n        if (filePath != null) {\n            try {\n                final FileContent fileContent = bonitaHomeFolderAccessor\n                        .retrieveUploadedTempContent(FilenameUtils.separatorsToSystem(filePath));\n                if (fileName == null) {\n                    fileName = fileContent.getFileName();\n                }\n                try (InputStream inputStream = fileContent.getInputStream()) {\n                    content = getFileContent(inputStream, filePath, fileContent.getSize());\n                }\n            } catch (final BonitaException e) {\n                throw new ServletException(e.getMessage());\n            } catch (final IOException e) {\n                throw new ServletException(e);\n            }\n        } else if (documentId != null) {\n            try {\n                final ProcessAPI processAPI = bpmEngineAPIUtil.getProcessAPI(apiSession);\n                String contentStorageId;\n                try {\n                    final Document document = processAPI.getDocument(Long.valueOf(documentId));\n                    fileName = document.getContentFileName();\n                    contentStorageId = document.getContentStorageId();\n                } catch (final DocumentNotFoundException dnfe) {\n                    final ArchivedDocument archivedDocument = processAPI\n                            .getArchivedVersionOfProcessDocument(Long.valueOf(documentId));\n                    fileName = archivedDocument.getContentFileName();\n                    contentStorageId = archivedDocument.getContentStorageId();\n                }\n                if (contentStorageId != null && !contentStorageId.isEmpty()) {\n                    content = processAPI.getDocumentContent(contentStorageId);\n                }\n            } catch (final Exception e) {\n                final String errorMessage = \"Error while retrieving the document  with ID \" + documentId\n                        + \" from the engine.\";\n                if (LOGGER.isErrorEnabled()) {\n                    LOGGER.error(errorMessage, e);\n                }\n                throw new ServletException(errorMessage, e);\n            }\n        } else if (resourcePath != null) {\n            final String processIDStr = request.getParameter(PROCESS_ID_PARAM);\n            final String instanceIDStr = request.getParameter(INSTANCE_ID_PARAM);\n            final String taskIdStr = request.getParameter(TASK_ID_PARAM);\n            long processDefinitionID = -1;\n            try {\n                if (processIDStr != null) {\n                    processDefinitionID = Long.parseLong(processIDStr);\n                } else if (taskIdStr != null) {\n                    processDefinitionID = getProcessDefinitionIDFromActivityInstanceID(apiSession,\n                            Long.parseLong(taskIdStr));\n                } else if (instanceIDStr != null) {\n                    processDefinitionID = getProcessDefinitionIDFromProcessInstanceID(apiSession,\n                            Long.parseLong(instanceIDStr));\n                } else {\n                    response.sendError(HttpServletResponse.SC_BAD_REQUEST,\n                            \"Either a process, instance or task parameter is required in the URL\");\n                    return;\n                }\n                final ProcessAPI processAPI = bpmEngineAPIUtil.getProcessAPI(apiSession);\n                content = processAPI.getDocumentProcessResource(processDefinitionID, resourcePath);\n                fileName = resourcePath.contains(\"/\")\n                        ? resourcePath.substring(resourcePath.lastIndexOf('/') + 1)\n                        : resourcePath;\n            } catch (final ProcessResourceNotFoundException e) {\n                if (LOGGER.isWarnEnabled()) {\n                    LOGGER.warn(e.getMessage());\n                }\n                response.sendError(HttpServletResponse.SC_NOT_FOUND);\n                return;\n            } catch (final Exception e) {\n                final String errorMessage = \"Error while retrieving the resource \" + resourcePath;\n                if (LOGGER.isErrorEnabled()) {\n                    LOGGER.error(errorMessage, e);\n                }\n                throw new ServletException(errorMessage, e);\n            }\n        } else {\n            final String errorMessage = \"Error while getting the file. either a document, a filePath or a resourcePath parameter is required.\";\n            if (LOGGER.isErrorEnabled()) {\n                LOGGER.error(errorMessage);\n            }\n            throw new ServletException(errorMessage);\n        }\n        final String contentType = URLConnection.guessContentTypeFromName(fileName);\n        if (contentType != null) {\n            response.setContentType(contentType);\n        }\n        response.setCharacterEncoding(StandardCharsets.UTF_8.name());\n        if (fileName != null) {\n            try {\n                final String encodedfileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8).replaceAll(\"\\\\+\",\n                        \"%20\");\n                final String userAgent = request.getHeader(\"User-Agent\");\n                if (userAgent != null && userAgent.contains(\"Firefox\")) {\n                    response.setHeader(\"Content-Disposition\", \"inline; filename*=UTF-8''\" + encodedfileName);\n                } else {\n                    response.setHeader(\"Content-Disposition\",\n                            \"inline; filename=\\\"\" + encodedfileName + \"\\\"; filename*=UTF-8''\"\n                                    + encodedfileName);\n                }\n                if (content != null) {\n                    response.setContentLength(content.length);\n                    OutputStream out = response.getOutputStream();\n                    out.write(content);\n                }\n            } catch (final IOException e) {\n                if (LOGGER.isErrorEnabled()) {\n                    LOGGER.error(\"Error while generating the response.\", e);\n                }\n                throw new ServletException(e.getMessage(), e);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/servlet/ErrorPageServlet.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.servlet;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.PrintWriter;\nimport java.nio.charset.StandardCharsets;\nimport java.util.regex.Pattern;\n\nimport javax.servlet.ServletContext;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.bonitasoft.console.common.server.utils.PlatformManagementUtils;\nimport org.bonitasoft.console.common.server.utils.SessionUtil;\nimport org.bonitasoft.engine.session.APISession;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class ErrorPageServlet extends HttpServlet {\n\n    /**\n     * UID\n     */\n    private static final long serialVersionUID = -6981838056314293935L;\n\n    /**\n     * Path of the error page template\n     */\n    protected static final String ERROR_TEMPLATE_PATH = \"/WEB-INF/errors.html\";\n\n    /**\n     * Logger\n     */\n    private static final Logger LOGGER = LoggerFactory.getLogger(ErrorPageServlet.class.getName());\n\n    private static final Pattern HTTP_STATUS_CODE = Pattern.compile(\"\\\\d{3}\");\n\n    /**\n     * Static variable to avoid reading the error HTML page every time the error page is displayed\n     */\n    private static String errorPageString = null;\n\n    /**\n     * {@inheritDoc}\n     *\n     * @throws IOException\n     */\n    @Override\n    protected void doGet(final HttpServletRequest request, final HttpServletResponse response)\n            throws ServletException, IOException {\n        String pathInfo = request.getPathInfo();\n        response.setContentType(\"text/html\");\n        response.setCharacterEncoding(StandardCharsets.UTF_8.name());\n\n        try (PrintWriter output = response.getWriter()) {\n            if (!StringUtils.isEmpty(pathInfo)) {\n                String errorCode = pathInfo.substring(1);\n                // Defense-in-depth: only accept 3-digit HTTP status codes.\n                // errorCode comes from user-controlled pathInfo and is concatenated\n                // into a getRequestDispatcher() path — without validation, a crafted\n                // pathInfo (e.g. \"/../WEB-INF/web.xml%00\") could reach arbitrary JSPs.\n                if (!HTTP_STATUS_CODE.matcher(errorCode).matches()) {\n                    response.sendError(HttpServletResponse.SC_BAD_REQUEST);\n                    return;\n                }\n                if (LOGGER.isInfoEnabled()) {\n                    LOGGER.info(\"Displaying error page with code \" + errorCode);\n                }\n                final APISession apiSession = (APISession) request.getSession()\n                        .getAttribute(SessionUtil.API_SESSION_PARAM_KEY);\n                if (apiSession != null && isPlatformHealthy()) {\n                    String contextPath = request.getContextPath();\n                    if (contextPath.equals(\"/\")) {\n                        //avoid double / in URL if Bonita is deployed at the root\n                        contextPath = \"\";\n                    }\n                    if (errorPageString == null) {\n                        ServletContext sc = getServletContext();\n                        try (InputStream errorPageInputStream = sc.getResourceAsStream(ERROR_TEMPLATE_PATH)) {\n                            errorPageString = new String(errorPageInputStream.readAllBytes(), StandardCharsets.UTF_8);\n                        } catch (Exception e) {\n                            if (LOGGER.isErrorEnabled()) {\n                                LOGGER.error(\"Error while trying to get the error page.\", e);\n                            }\n                            output.println(\"An Error occurred.\");\n                        }\n                    }\n                    writeFormatedResponse(output, errorCode, contextPath);\n                } else {\n                    //if there is no session or if the platform is not available, fallback on generic error pages\n                    getServletContext().getRequestDispatcher(\"/\" + errorCode + \".jsp\").forward(request, response);\n                }\n            } else {\n                if (LOGGER.isWarnEnabled()) {\n                    LOGGER.warn(\"Status code missing from request.\");\n                }\n                output.println(\"Status code missing from request.\");\n            }\n            output.flush();\n        }\n    }\n\n    protected boolean isPlatformHealthy() {\n        try {\n            PlatformManagementUtils platformManagementUtils = new PlatformManagementUtils();\n            return platformManagementUtils.isPlatformAvailable();\n        } catch (Exception e) {\n            if (LOGGER.isInfoEnabled()) {\n                LOGGER.info(\"Platform is not healthy.\");\n            }\n            return false;\n        }\n    }\n\n    protected void writeFormatedResponse(PrintWriter output, String errorCode, String contextPath) throws IOException {\n        try {\n            output.format(errorPageString, errorCode, contextPath, errorCode);\n        } catch (Exception e) {\n            if (LOGGER.isErrorEnabled()) {\n                LOGGER.error(\"Error while trying to display the error page.\", e);\n            }\n            output.println(\"An Error occurred.\");\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/servlet/FileUploadServlet.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.servlet;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.io.Serializable;\nimport java.net.HttpURLConnection;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipInputStream;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.apache.commons.fileupload.FileItem;\nimport org.apache.commons.fileupload.FileItemFactory;\nimport org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException;\nimport org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException;\nimport org.apache.commons.fileupload.disk.DiskFileItemFactory;\nimport org.apache.commons.fileupload.servlet.ServletFileUpload;\nimport org.bonitasoft.console.common.server.filter.MultiReadHttpServletRequest;\nimport org.bonitasoft.console.common.server.utils.DocumentUtil;\nimport org.bonitasoft.engine.api.PlatformAPIAccessor;\nimport org.bonitasoft.engine.api.TemporaryContentAPI;\nimport org.bonitasoft.engine.io.FileContent;\nimport org.bonitasoft.engine.session.SessionNotFoundException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Servlet allowing to upload a File.\n *\n * @author Julien Mege\n */\npublic abstract class FileUploadServlet extends HttpServlet {\n\n    /**\n     * UID\n     */\n    protected static final long serialVersionUID = -948661031179067420L;\n\n    protected static final Logger LOGGER = LoggerFactory.getLogger(FileUploadServlet.class.getName());\n\n    protected String uploadDirectoryPath = null;\n\n    public static final String RESPONSE_SEPARATOR = \"::\";\n\n    protected static final String SUPPORTED_EXTENSIONS_PARAM = \"SupportedExtensions\";\n\n    protected static final String SUPPORTED_EXTENSIONS_SEPARATOR = \",\";\n\n    protected static final String RETURN_ORIGINAL_FILENAME_PARAM = \"ReturnOriginalFilename\";\n\n    protected static final String CHECK_UPLOADED_FILE_SIZE = \"CheckUploadedFileSize\";\n\n    protected static final String CHECK_UPLOADED_IMAGE_SIZE = \"CheckUploadedImageSize\";\n\n    protected static final String RESPONSE_CONTENT_TYPE_PARAM = \"ContentType\";\n\n    protected static final String TEXT_CONTENT_TYPE = \"text\";\n\n    protected static final String JSON_CONTENT_TYPE = \"json\";\n\n    protected static final String TEMP_PATH_RESPONSE_ATTRIBUTE = \"tempPath\";\n\n    protected static final String FILE_NAME_RESPONSE_ATTRIBUTE = \"filename\";\n\n    protected static final String CONTENT_TYPE_ATTRIBUTE = \"contentType\";\n\n    protected static final String JARLESS_BAR_ATTRIBUTE = \"jarlessBar\";\n\n    public static final int MEGABYTE = 1048576;\n\n    public static final int KILOBYTE = 1024;\n\n    protected String[] supportedExtensionsList = new String[0];\n\n    protected boolean alsoReturnOriginalFilename = false;\n\n    protected boolean checkUploadedFileSize = false;\n\n    protected boolean checkUploadedImageSize = false;\n\n    protected String responseContentType = TEXT_CONTENT_TYPE;\n\n    private ObjectMapper objectMapper = new ObjectMapper();\n    protected TemporaryContentAPI temporaryContentAPI;\n\n    @Override\n    public void init() throws ServletException {\n        final String supportedExtensionsParam = getInitParameter(SUPPORTED_EXTENSIONS_PARAM);\n        if (supportedExtensionsParam != null) {\n            supportedExtensionsList = supportedExtensionsParam.split(SUPPORTED_EXTENSIONS_SEPARATOR);\n        }\n        alsoReturnOriginalFilename = Boolean.parseBoolean(getInitParameter(RETURN_ORIGINAL_FILENAME_PARAM));\n        final String responseContentTypeParam = getInitParameter(RESPONSE_CONTENT_TYPE_PARAM);\n        if (responseContentTypeParam != null) {\n            responseContentType = responseContentTypeParam;\n        }\n        checkUploadedFileSize = Boolean.parseBoolean(getInitParameter(CHECK_UPLOADED_FILE_SIZE));\n        checkUploadedImageSize = Boolean.parseBoolean(getInitParameter(CHECK_UPLOADED_IMAGE_SIZE));\n        temporaryContentAPI = getTemporaryContentAPI();\n    }\n\n    protected TemporaryContentAPI getTemporaryContentAPI() {\n        try {\n            return PlatformAPIAccessor.getTemporaryContentAPI();\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    protected abstract void defineUploadDirectoryPath(final HttpServletRequest request) throws SessionNotFoundException;\n\n    protected abstract void setUploadMaxSize(ServletFileUpload serviceFileUpload, final HttpServletRequest request);\n\n    protected void setUploadDirectoryPath(final String uploadDirectoryPath) {\n        this.uploadDirectoryPath = uploadDirectoryPath;\n    }\n\n    /**\n     * Test whether file is a jar less bar (only when check is relevant).\n     *\n     * @param fileName the file name\n     * @param item the file item\n     * @return true when file is a bar file without jar dependency files and check is relevant, false otherwise\n     */\n    private boolean isJarLessBar(String fileName, FileItem item) {\n        if (Arrays.equals(supportedExtensionsList, new String[] { \"bar\" }) && fileName.endsWith(\".bar\")) {\n            ZipEntry zipEntry = null;\n            try (ZipInputStream zipInputstream = new ZipInputStream(item.getInputStream());) {\n                while ((zipEntry = zipInputstream.getNextEntry()) != null) {\n                    if (\".jarless\".equals(zipEntry.getName())) {\n                        return true;\n                    }\n                }\n            } catch (IOException e) {\n                LOGGER.error(\"Error while checking if file is a jar less bar\", e);\n            }\n        }\n        return false;\n    }\n\n    @Override\n    public void doPost(final HttpServletRequest request, final HttpServletResponse response)\n            throws ServletException, IOException {\n        response.setContentType(\"text/plain;charset=UTF-8\");\n        PrintWriter responsePW = null;\n        try {\n            defineUploadDirectoryPath(request);\n            if (!ServletFileUpload.isMultipartContent(request)) {\n                return;\n            }\n\n            final File targetDirectory = new File(uploadDirectoryPath);\n            if (!targetDirectory.exists()) {\n                targetDirectory.mkdirs();\n            }\n\n            responsePW = response.getWriter();\n\n            final FileItemFactory fileItemFactory = new DiskFileItemFactory();\n            final ServletFileUpload serviceFileUpload = createServletFileUpload(fileItemFactory);\n            setUploadMaxSize(serviceFileUpload, request);\n            List<FileItem> items;\n            try {\n                items = serviceFileUpload.parseRequest(request);\n            } catch (final OutOfMemoryError e) {\n                throw new SizeLimitExceededException(\"The file exceeds its maximum permitted size.\", 0L, 0);\n            }\n\n            for (final FileItem item : items) {\n                if (item.isFormField()) {\n                    continue;\n                }\n\n                final String fileName = DocumentUtil.sanitizeFilename(item.getName());\n\n                // Check if extension is allowed\n                if (!isSupportedExtension(fileName)) {\n                    outputMediaTypeError(response, responsePW);\n                    return;\n                }\n\n                // Make unique file name\n                final String uploadedFileKey = storeTempFile(fileName, item);\n\n                //Clean multiread wrapper temp file if it exists\n                if (request instanceof MultiReadHttpServletRequest) {\n                    ((MultiReadHttpServletRequest) request).cleanMultipartTempContent();\n                }\n\n                // Response\n                final String responseString;\n                if (JSON_CONTENT_TYPE.equals(responseContentType)) {\n                    // just check whether it's a jarless bar to display a warning\n                    boolean isJarlessBar = isJarLessBar(fileName, item);\n                    responseString = generateResponseJson(request, fileName, item.getContentType(), uploadedFileKey,\n                            isJarlessBar);\n                } else if (TEXT_CONTENT_TYPE.equals(responseContentType)) {\n                    responseString = generateResponseString(request, fileName, uploadedFileKey);\n                } else {\n                    throw new ServletException(\n                            \"Unsupported content type in servlet configuration : \" + responseContentType);\n                }\n                responsePW.print(responseString);\n                responsePW.flush();\n                break;\n            }\n        } catch (SessionNotFoundException e) {\n            final String message = \"Session expired\";\n            LOGGER.debug(message);\n            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, message);\n        } catch (final SizeLimitExceededException e) {\n            LOGGER.error(\"File is Too Big\", e);\n            generateFileTooBigError(response, responsePW, \"Uploaded file is too large, server is unable to process it\");\n        } catch (final FileSizeLimitExceededException e) {\n            LOGGER.error(\"File is Too Big\", e);\n            generateFileTooBigError(response, responsePW,\n                    e.getFileName() + \" is \" + e.getActualSize() + \" large, limit is set to \" + e.getPermittedSize()\n                            / FileUploadServlet.MEGABYTE + \"Mb\");\n        } catch (final Exception e) {\n            final String theErrorMessage = \"Exception while uploading file.\";\n            if (LOGGER.isErrorEnabled()) {\n                LOGGER.error(theErrorMessage, e);\n            }\n            throw new ServletException(theErrorMessage, e);\n        } finally {\n            if (responsePW != null) {\n                responsePW.close();\n            }\n        }\n    }\n\n    protected String storeTempFile(final String fileName, final FileItem item)\n            throws Exception {\n        temporaryContentAPI = PlatformAPIAccessor.getTemporaryContentAPI();\n        return temporaryContentAPI\n                .storeTempFile(new FileContent(fileName, item.getInputStream(), item.getContentType()));\n    }\n\n    private void generateFileTooBigError(final HttpServletResponse response, final PrintWriter responsePW,\n            final String message) throws JsonProcessingException {\n        response.setStatus(HttpURLConnection.HTTP_ENTITY_TOO_LARGE);\n        if (JSON_CONTENT_TYPE.equals(responseContentType)) {\n            final Map<String, Serializable> errorResponse = new HashMap<>();\n            errorResponse.put(\"type\", \"EntityTooLarge\");\n            errorResponse.put(\"message\", message);\n            errorResponse.put(\"statusCode\", HttpURLConnection.HTTP_ENTITY_TOO_LARGE);\n            responsePW.print(objectMapper.writeValueAsString(errorResponse));\n            responsePW.flush();\n        }\n    }\n\n    //for test purpose\n    protected ServletFileUpload createServletFileUpload(final FileItemFactory fileItemFactory) {\n        return new ServletFileUpload(fileItemFactory);\n    }\n\n    protected String generateResponseString(final HttpServletRequest request, final String fileName,\n            final String uploadedFileName) throws Exception {\n        String responseString = uploadedFileName;\n        if (alsoReturnOriginalFilename) {\n            responseString = responseString + RESPONSE_SEPARATOR + getFilenameLastSegment(fileName);\n        }\n        return responseString;\n    }\n\n    protected String generateResponseJson(final HttpServletRequest request, final String fileName, String contentType,\n            final String uploadedFileName, final boolean isJarlessBar) throws Exception {\n        final Map<String, Serializable> responseMap = new HashMap<>();\n        fillJsonResponseMap(request, responseMap, fileName, contentType, uploadedFileName, isJarlessBar);\n        return objectMapper.writeValueAsString(responseMap);\n    }\n\n    protected void fillJsonResponseMap(HttpServletRequest request, final Map<String, Serializable> responseMap,\n            final String fileName,\n            final String contentType, final String uploadedFileName, final boolean isJarlessBar) {\n        if (alsoReturnOriginalFilename) {\n            responseMap.put(FILE_NAME_RESPONSE_ATTRIBUTE, getFilenameLastSegment(fileName));\n        }\n        responseMap.put(TEMP_PATH_RESPONSE_ATTRIBUTE, uploadedFileName);\n        responseMap.put(CONTENT_TYPE_ATTRIBUTE, contentType);\n        if (isJarlessBar) {\n            responseMap.put(JARLESS_BAR_ATTRIBUTE, Boolean.TRUE);\n        }\n    }\n\n    protected String getFilenameLastSegment(final String fileName) {\n        int slashPos = fileName.lastIndexOf(\"/\");\n        if (slashPos == -1) {\n            slashPos = fileName.lastIndexOf(\"\\\\\");\n        }\n        return fileName.substring(slashPos + 1);\n    }\n\n    protected void outputMediaTypeError(final HttpServletResponse response, final PrintWriter responsePW) {\n        response.setStatus(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);\n        responsePW.print(\"Extension not supported.\");\n        responsePW.flush();\n    }\n\n    protected boolean isSupportedExtension(final String fileName) {\n        if (fileName == null) {\n            return false;\n        }\n\n        // if no extension specified, all extensions are allowed\n        if (supportedExtensionsList.length < 1) {\n            return true;\n        }\n\n        for (final String extension : supportedExtensionsList) {\n            if (fileName.toLowerCase().endsWith(\".\" + extension.toLowerCase())) {\n                return true;\n            }\n        }\n\n        return false;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/servlet/IconContent.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.servlet;\n\nclass IconContent {\n\n    private final byte[] content;\n    private final String mimeType;\n\n    public IconContent(byte[] content, String mimeType) {\n        this.content = content;\n        this.mimeType = mimeType;\n    }\n\n    public byte[] getContent() {\n        return content;\n    }\n\n    public String getMimeType() {\n        return mimeType;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/servlet/IconServlet.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.servlet;\n\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.io.UnsupportedEncodingException;\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Optional;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIMalformedUrlException;\nimport org.bonitasoft.web.toolkit.client.common.exception.http.ServerException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic abstract class IconServlet extends HttpServlet {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(IconServlet.class.getName());\n\n    @Override\n    protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException {\n        String iconIdPath = request.getPathInfo();\n        if (iconIdPath == null || iconIdPath.isEmpty()) {\n            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);\n            return;\n        }\n        Long iconId = parseLong(iconIdPath);\n        if (iconId == null) {\n            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);\n            return;\n        }\n        Optional<IconContent> iconContent = retrieveIcon(iconId,\n                (APISession) request.getSession().getAttribute(\"apiSession\"));\n        if (!iconContent.isPresent()) {\n            response.setStatus(HttpServletResponse.SC_NOT_FOUND);\n            return;\n        }\n        response.setContentType(iconContent.get().getMimeType());\n        response.setCharacterEncoding(\"UTF-8\");\n        try {\n            setHeaders(request, response, iconId);\n        } catch (UnsupportedEncodingException e) {\n            logAndThrowException(e, \"Error while generating the headers.\");\n        }\n        try {\n            OutputStream out = response.getOutputStream();\n            response.setContentLength(iconContent.get().getContent().length);\n            out.write(iconContent.get().getContent());\n        } catch (final IOException e) {\n            logAndThrowException(e, \"Error while generating the response.\");\n        }\n\n    }\n\n    @Override\n    protected void doDelete(final HttpServletRequest request, final HttpServletResponse response)\n            throws ServletException {\n        String pathInfo = request.getPathInfo();\n        if (pathInfo == null || pathInfo.isEmpty()) {\n            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);\n            return;\n        }\n        Long entityId = parseLong(pathInfo);\n        if (entityId == null) {\n            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);\n            return;\n        }\n        try {\n            deleteIcon(entityId, (APISession) request.getSession().getAttribute(\"apiSession\"), request);\n        } catch (APIItemNotFoundException e) {\n            response.setStatus(HttpServletResponse.SC_NOT_FOUND);\n            return;\n        } catch (APIMalformedUrlException e) {\n            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);\n            return;\n        } catch (ServerException e) {\n            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);\n            return;\n        }\n        try {\n            setHeaders(request, response, entityId);\n        } catch (UnsupportedEncodingException e) {\n            logAndThrowException(e, \"Error while generating the headers.\");\n        }\n        response.setStatus(HttpServletResponse.SC_NO_CONTENT);\n    }\n\n    protected abstract Optional<IconContent> retrieveIcon(Long iconId, APISession apiSession);\n\n    protected abstract void deleteIcon(Long entityId, APISession apiSession, HttpServletRequest request)\n            throws ServerException;\n\n    private Long parseLong(String iconIdPath) {\n        try {\n            return Long.valueOf(iconIdPath.substring(1));\n        } catch (NumberFormatException e) {\n            return null;\n        }\n    }\n\n    private void logAndThrowException(IOException e, String msg) throws ServletException {\n        LOGGER.error(msg, e);\n        throw new ServletException(e.getMessage(), e);\n    }\n\n    private void setHeaders(HttpServletRequest request, HttpServletResponse response, Long iconId)\n            throws UnsupportedEncodingException {\n        final String encodedFileName = URLEncoder.encode(String.valueOf(iconId), StandardCharsets.UTF_8);\n        final String userAgent = request.getHeader(\"User-Agent\");\n        if (userAgent != null && userAgent.contains(\"Firefox\")) {\n            response.setHeader(\"Content-Disposition\", \"inline; filename*=UTF-8''\" + encodedFileName);\n        } else {\n            response.setHeader(\"Content-Disposition\",\n                    \"inline; filename=\\\"\" + encodedFileName.replaceAll(\"\\\\+\", \" \") + \"\\\"; filename*=UTF-8''\"\n                            + encodedFileName);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/servlet/OrganizationIconServlet.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.servlet;\n\nimport java.util.Optional;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport org.bonitasoft.engine.api.APIClient;\nimport org.bonitasoft.engine.api.IdentityAPI;\nimport org.bonitasoft.engine.exception.NotFoundException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.identity.Icon;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.identity.UserNotFoundException;\nimport org.bonitasoft.engine.identity.UserUpdater;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIMalformedUrlException;\nimport org.bonitasoft.web.toolkit.client.common.exception.http.ServerException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Anthony Birembaut\n * @author Baptiste Mesta\n */\npublic class OrganizationIconServlet extends IconServlet {\n\n    @Override\n    protected Optional<IconContent> retrieveIcon(Long iconId, APISession apiSession) {\n        IdentityAPI identityAPI = getIdentityApi(apiSession);\n        Icon icon;\n        try {\n            icon = identityAPI.getIcon(iconId);\n        } catch (NotFoundException e) {\n            return Optional.empty();\n        }\n        return Optional.of(new IconContent(icon.getContent(), icon.getMimeType()));\n    }\n\n    @Override\n    protected void deleteIcon(Long entityId, APISession apiSession, HttpServletRequest request) throws ServerException {\n        String entityType = request.getParameter(\"type\");\n        if (entityType == null || !entityType.equals(\"user\")) {\n            throw new APIMalformedUrlException(request.getRequestURL().toString(),\n                    \"Cannot delete a non-user icon. Provide type=user as a query parameter.\");\n        }\n        IdentityAPI identityAPI = getIdentityApi(apiSession);\n        UserUpdater updater = new UserUpdater();\n        updater.setIcon(null, null);\n        try {\n            identityAPI.updateUser(entityId, updater);\n        } catch (UserNotFoundException e) {\n            throw new APIItemNotFoundException(User.class.getName(), APIID.makeAPIID(entityId));\n        } catch (UpdateException e) {\n            throw new APIException(e);\n        }\n    }\n\n    IdentityAPI getIdentityApi(APISession apiSession) {\n        return new APIClient(apiSession).getIdentityAPI();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/servlet/PageUploadServlet.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.servlet;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.Serializable;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.Set;\n\nimport javax.activation.FileTypeMap;\nimport javax.activation.MimetypesFileTypeMap;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.fileupload.FileItem;\nimport org.apache.commons.io.FileUtils;\nimport org.bonitasoft.console.common.server.page.CustomPageService;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.InvalidPageTokenException;\nimport org.bonitasoft.engine.exception.InvalidPageZipContentException;\nimport org.bonitasoft.engine.io.FileContent;\nimport org.bonitasoft.engine.session.APISession;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @deprecated as of 9.0.0, Application page should be created/updated at startup.\n */\n@Slf4j\n@Deprecated(since = \"9.0.0\")\npublic class PageUploadServlet extends TenantFileUploadServlet {\n\n    /**\n     * UID\n     */\n    private static final long serialVersionUID = -5733037726905793432L;\n\n    protected static final String ACTION_PARAM_NAME = \"action\";\n\n    private static final String PROCESS_PARAM_NAME = \"process\";\n\n    protected static final String ADD_ACTION = \"add\";\n\n    protected static final Logger LOGGER = LoggerFactory.getLogger(PageUploadServlet.class.getName());\n\n    protected static final String PERMISSIONS_RESPONSE_ATTRIBUTE = \"permissions\";\n\n    protected File pageTmp;\n\n    /**\n     * {@inheritDoc}\n     *\n     * @deprecated as of 9.0.0, Application page should be created/updated at startup.\n     */\n    @Override\n    @Deprecated(since = \"9.0.0\")\n    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {\n        log.warn(\"Application Page upload is deprecated! \" +\n                \"An application page should be created or updated at startup instead.\");\n        super.doPost(request, response);\n    }\n\n    @Override\n    protected String generateResponseString(final HttpServletRequest request, final String fileName,\n            final String uploadedFileName) throws Exception {\n\n        final String responseString = super.generateResponseString(request, fileName, uploadedFileName);\n        String permissionString;\n        try {\n            final String[] permissions = getPermissions(request);\n            permissionString = \"[\" + String.join(\",\", permissions) + \"]\";\n        } catch (final Exception e) {\n            permissionString = getPermissionsError(e);\n        }\n        return responseString + RESPONSE_SEPARATOR + permissionString;\n    }\n\n    @Override\n    protected void fillJsonResponseMap(final HttpServletRequest request, final Map<String, Serializable> responseMap,\n            final String fileName, final String contentType, final String uploadedFileKey, final boolean isJarlessBar) {\n        super.fillJsonResponseMap(request, responseMap, fileName, contentType, uploadedFileKey, isJarlessBar);\n        // also add the permissions to the map\n        try {\n            final String[] permissions = getPermissions(request);\n            responseMap.put(PERMISSIONS_RESPONSE_ATTRIBUTE, permissions);\n        } catch (final Exception e) {\n            responseMap.put(PERMISSIONS_RESPONSE_ATTRIBUTE, getPermissionsError(e));\n        }\n    }\n\n    @Override\n    protected String storeTempFile(final String fileName, final FileItem item)\n            throws Exception {\n        pageTmp = File.createTempFile(\"tmp_page\", \".tmp\");\n        FileUtils.copyToFile(item.getInputStream(), pageTmp);\n        pageTmp.deleteOnExit();\n        final FileTypeMap mimetypesFileTypeMap = new MimetypesFileTypeMap();\n        String mimeType = mimetypesFileTypeMap.getContentType(pageTmp);\n        return temporaryContentAPI.storeTempFile(new FileContent(fileName, new FileInputStream(pageTmp), mimeType));\n    }\n\n    protected String[] getPermissions(final HttpServletRequest request)\n            throws InvalidPageZipContentException, InvalidPageTokenException, AlreadyExistsException, BonitaException,\n            IOException {\n        final String action = request.getParameter(ACTION_PARAM_NAME);\n        final boolean checkIfItAlreadyExists = ADD_ACTION.equals(action);\n        final Set<String> permissionsSet = getPagePermissions(request, checkIfItAlreadyExists);\n        return permissionsSet != null ? permissionsSet.toArray(new String[permissionsSet.size()]) : new String[0];\n    }\n\n    protected String getPermissionsError(final Exception e) {\n        if (LOGGER.isWarnEnabled()) {\n            LOGGER.warn(e.getMessage());\n        }\n        return e.getClass().getSimpleName();\n    }\n\n    protected Set<String> getPagePermissions(final HttpServletRequest request, final boolean checkIfItAlreadyExists)\n            throws BonitaException, IOException {\n        final APISession apiSession = getAPISession(request);\n        final Long processDefinitionId = getProcessDefinitionId(request);\n        final CustomPageService customPageService = new CustomPageService();\n        final Properties properties = customPageService.getPageProperties(apiSession,\n                FileUtils.readFileToByteArray(pageTmp),\n                checkIfItAlreadyExists, processDefinitionId);\n        Set<String> customPagePermissions = customPageService.getCustomPagePermissions(properties, apiSession);\n        pageTmp.delete();\n        return customPagePermissions;\n    }\n\n    private Long getProcessDefinitionId(final HttpServletRequest request) {\n        final String processStr = request.getParameter(PROCESS_PARAM_NAME);\n        Long processDefinitionId = null;\n        if (processStr != null) {\n            processDefinitionId = Long.parseLong(processStr);\n        }\n        return processDefinitionId;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/servlet/PlatformFileUploadServlet.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.servlet;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport org.apache.commons.fileupload.servlet.ServletFileUpload;\nimport org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils;\n\n/**\n * Servlet allowing to upload a file in the platform common temp folder\n *\n * @author Anthony Birembaut\n */\npublic class PlatformFileUploadServlet extends FileUploadServlet {\n\n    /**\n     * UID\n     */\n    private static final long serialVersionUID = 58370675776169565L;\n\n    @Override\n    protected void defineUploadDirectoryPath(final HttpServletRequest request) {\n        setUploadDirectoryPath(WebBonitaConstantsUtils.getPlatformInstance().getTempFolder().getPath());\n    }\n\n    @Override\n    protected void setUploadMaxSize(final ServletFileUpload serviceFileUpload, final HttpServletRequest request) {\n        //currently no limit check on the platform\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/servlet/PlatformTenantListener.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.servlet;\n\nimport java.io.IOException;\n\nimport javax.servlet.ServletContextEvent;\nimport javax.servlet.ServletContextListener;\n\nimport org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils;\nimport org.bonitasoft.console.common.server.utils.PlatformManagementUtils;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Zhiheng Yang, Anthony Birembaut\n */\npublic class PlatformTenantListener implements ServletContextListener {\n\n    /**\n     * Logger\n     */\n    private static final Logger LOGGER = LoggerFactory.getLogger(PlatformTenantListener.class.getName());\n\n    @Override\n    public void contextInitialized(final ServletContextEvent sce) {\n        PlatformManagementUtils platformManagementUtils = new PlatformManagementUtils();\n        try {\n            platformManagementUtils.initializePlatformConfiguration();\n            // Create temporary folder specific to portal at startup:\n            WebBonitaConstantsUtils.getPlatformInstance().getTempFolder();\n        } catch (BonitaException e) {\n            LOGGER.error(\n                    \"Error initializing platform configuration. Engine most likely failed to start. Check previous error logs for more details.\");\n        } catch (IOException e) {\n            if (LOGGER.isErrorEnabled()) {\n                LOGGER.error(\"Error while retrieving configuration\", e);\n            }\n        }\n    }\n\n    @Override\n    public void contextDestroyed(final ServletContextEvent sce) {\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/servlet/ResourceLocationReader.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.servlet;\n\nimport javax.servlet.http.HttpServletRequest;\n\npublic class ResourceLocationReader {\n\n    /**\n     * file location\n     */\n    public final static String LOCATION_PARAM = \"location\";\n\n    public String getResourceLocationFromRequest(final HttpServletRequest request) {\n        String fileName = request.getParameter(LOCATION_PARAM);\n        if (fileName == null) {\n            final String pathInfo = request.getPathInfo();\n            if (pathInfo != null && pathInfo.startsWith(\"/\") && pathInfo.length() > 1) {\n                //Support relative calls to the THEME from the homepage using ../theme\n                fileName = pathInfo.substring(1);\n            }\n        }\n        return fileName;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/servlet/ResourceServlet.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.servlet;\n\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.io.UnsupportedEncodingException;\nimport java.net.URLDecoder;\n\nimport javax.activation.FileTypeMap;\nimport javax.activation.MimetypesFileTypeMap;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.apache.commons.io.FileUtils;\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Anthony Birembaut\n */\npublic abstract class ResourceServlet extends HttpServlet {\n\n    /**\n     * UID\n     */\n    private static final long serialVersionUID = -2103038794535737881L;\n\n    /**\n     * Logger\n     */\n    private final static Logger LOGGER = LoggerFactory.getLogger(ResourceServlet.class.getName());\n\n    protected abstract String getResourceParameterName();\n\n    protected abstract String getDefaultResourceName();\n\n    protected abstract File getResourcesParentFolder();\n\n    /**\n     * Return null if there is no subfolder\n     */\n    protected abstract String getSubFolderName();\n\n    protected ResourceLocationReader resourceLocationReader = new ResourceLocationReader();\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    protected void doGet(final HttpServletRequest request, final HttpServletResponse response)\n            throws ServletException, IOException {\n        final String fileName = resourceLocationReader.getResourceLocationFromRequest(request);\n        String resourceName = request.getParameter(getResourceParameterName());\n        if (resourceName == null) {\n            resourceName = getDefaultResourceName();\n        }\n        try {\n            getResourceFile(response, resourceName, fileName);\n        } catch (final UnsupportedEncodingException e) {\n            final String errorMessage = \"UnsupportedEncodingException :\" + e;\n            if (LOGGER.isErrorEnabled()) {\n                LOGGER.error(errorMessage);\n            }\n            throw new ServletException(errorMessage);\n        }\n    }\n\n    /**\n     * Get resource file.\n     *\n     * @throws ServletException\n     * @throws UnsupportedEncodingException\n     */\n    protected void getResourceFile(final HttpServletResponse response, String resourceName, String fileName)\n            throws ServletException, IOException {\n        if (resourceName == null) {\n            final String errorMessage = \"Error while using the servlet to get a resource: the parameter \"\n                    + getResourceParameterName() + \" is null.\";\n            if (LOGGER.isWarnEnabled()) {\n                LOGGER.warn(errorMessage);\n            }\n            throw new ServletException(errorMessage);\n        }\n        if (fileName == null) {\n            final String errorMessage = \"Error while using the servlet to get a resource: the parameter \"\n                    + ResourceLocationReader.LOCATION_PARAM + \" is null.\";\n            if (LOGGER.isWarnEnabled()) {\n                LOGGER.warn(errorMessage);\n            }\n            throw new ServletException(errorMessage);\n        }\n        resourceName = URLDecoder.decode(resourceName, \"UTF-8\");\n        fileName = URLDecoder.decode(fileName, \"UTF-8\");\n        response.setCharacterEncoding(\"UTF-8\");\n\n        final File resourcesParentFolder = getResourcesFolder();\n        final String subFolderName = getSubFolderName();\n        String subFolderSuffix;\n        if (subFolderName != null) {\n            subFolderSuffix = File.separator + subFolderName;\n        } else {\n            subFolderSuffix = \"\";\n        }\n        try {\n            final File resourceFolder = new File(resourcesParentFolder, resourceName + subFolderSuffix);\n            final File file = new File(resourceFolder, fileName);\n            final BonitaHomeFolderAccessor tenantFolder = new BonitaHomeFolderAccessor();\n            if (!tenantFolder.isInFolder(resourceFolder, resourcesParentFolder)) {\n                throw new ServletException(\"For security reasons, access to this file path is restricted.\");\n            }\n            if (!tenantFolder.isInFolder(file, resourceFolder)) {\n                throw new ServletException(\"For security reasons, access to this file path is restricted.\");\n            }\n\n            byte[] content;\n            String contentType;\n\n            final String lowerCaseFileName = fileName.toLowerCase();\n            if (lowerCaseFileName.endsWith(\".jpg\")) {\n                contentType = \"image/jpeg\";\n            } else if (lowerCaseFileName.endsWith(\".jpeg\")) {\n                contentType = \"image/jpeg\";\n            } else if (lowerCaseFileName.endsWith(\".gif\")) {\n                contentType = \"image/gif\";\n            } else if (lowerCaseFileName.endsWith(\".png\")) {\n                contentType = \"image/png\";\n            } else if (lowerCaseFileName.endsWith(\".css\") || lowerCaseFileName.endsWith(\".less\")) {\n                contentType = \"text/css\";\n            } else if (lowerCaseFileName.endsWith(\".js\")) {\n                contentType = \"application/x-javascript\";\n            } else if (lowerCaseFileName.endsWith(\".html\")) {\n                contentType = \"text/html; charset=UTF-8\";\n            } else if (lowerCaseFileName.endsWith(\".htc\")) {\n                contentType = \"text/x-component\";\n            } else if (lowerCaseFileName.endsWith(\".svg\")) {\n                contentType = \"image/svg+xml\";\n            } else if (lowerCaseFileName.endsWith(\".eot\")) {\n                contentType = \"application/vnd.ms-fontobject\";\n            } else if (lowerCaseFileName.endsWith(\".woff\")) {\n                contentType = \"application/x-font-woff\";\n            } else if (lowerCaseFileName.endsWith(\".ttf\")) {\n                contentType = \"application/x-font-ttf\";\n            } else if (lowerCaseFileName.endsWith(\".otf\")) {\n                contentType = \"application/x-font-opentype\";\n            } else {\n                final FileTypeMap mimetypesFileTypeMap = new MimetypesFileTypeMap();\n                contentType = mimetypesFileTypeMap.getContentType(file);\n            }\n            if (contentType == null) {\n                contentType = \"application/octet-stream\";\n            }\n            content = FileUtils.readFileToByteArray(file);\n            response.setContentType(contentType);\n            response.setContentLength(content.length);\n            response.setBufferSize(content.length);\n            final OutputStream out = response.getOutputStream();\n            out.write(content, 0, content.length);\n        } catch (FileNotFoundException e) {\n            if (LOGGER.isWarnEnabled()) {\n                LOGGER.warn(e.getMessage());\n            }\n            response.sendError(404);\n        } catch (final IOException e) {\n            if (LOGGER.isErrorEnabled()) {\n                LOGGER.error(\"Error while generating the response.\", e);\n            }\n            throw new ServletException(e.getMessage(), e);\n        }\n    }\n\n    protected File getResourcesFolder() throws ServletException {\n        try {\n            return getResourcesParentFolder();\n        } catch (final RuntimeException e) {\n            final String errorMessage = \"Error while using the servlet to get parent folder.\";\n            if (LOGGER.isWarnEnabled()) {\n                LOGGER.warn(errorMessage);\n            }\n            throw new ServletException(errorMessage);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/servlet/TenantFileUploadServlet.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.servlet;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpSession;\n\nimport org.apache.commons.fileupload.servlet.ServletFileUpload;\nimport org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils;\nimport org.bonitasoft.console.common.server.preferences.properties.ConsoleProperties;\nimport org.bonitasoft.console.common.server.preferences.properties.PropertiesFactory;\nimport org.bonitasoft.engine.session.APISession;\n\n/**\n * Servlet allowing to upload a file in the tenant common temp folder\n *\n * @author Anthony Birembaut\n */\npublic class TenantFileUploadServlet extends FileUploadServlet {\n\n    /**\n     * UID\n     */\n    private static final long serialVersionUID = 58370675886169565L;\n\n    @Override\n    protected void defineUploadDirectoryPath(final HttpServletRequest request) {\n        setUploadDirectoryPath(WebBonitaConstantsUtils.getTenantInstance().getTempFolder().getPath());\n    }\n\n    protected APISession getAPISession(final HttpServletRequest request) {\n        final HttpSession session = request.getSession();\n        return (APISession) session.getAttribute(\"apiSession\");\n    }\n\n    @Override\n    protected void setUploadMaxSize(final ServletFileUpload serviceFileUpload, final HttpServletRequest request) {\n        if (checkUploadedImageSize) {\n            serviceFileUpload.setFileSizeMax(getConsoleProperties().getImageMaxSizeInKB() * KILOBYTE);\n        } else if (checkUploadedFileSize) {\n            serviceFileUpload.setFileSizeMax(getConsoleProperties().getMaxSize() * MEGABYTE);\n        } else {\n            //Some db vendor do not support more than 2GB Blobs\n            serviceFileUpload.setFileSizeMax(Integer.MAX_VALUE);\n        }\n    }\n\n    protected ConsoleProperties getConsoleProperties() {\n        return PropertiesFactory.getConsoleProperties();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/BPMEngineAPIUtil.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.utils;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Util class to work with the BPM engine API\n *\n * @author Anthony Birembaut\n */\npublic class BPMEngineAPIUtil {\n\n    /**\n     * Logger\n     */\n    private static final Logger LOGGER = LoggerFactory.getLogger(BPMEngineAPIUtil.class.getName());\n\n    /**\n     * Get the engine process API\n     *\n     * @param session\n     *        API session\n     * @return an instance of {@link ProcessAPI}\n     */\n    public ProcessAPI getProcessAPI(final APISession session) throws BPMEngineException, InvalidSessionException {\n        try {\n            return TenantAPIAccessor.getProcessAPI(session);\n        } catch (final BonitaHomeNotSetException e) {\n            final String message = \"Bonita home system variable is not defined\";\n            if (LOGGER.isErrorEnabled()) {\n                LOGGER.error(message, e);\n            }\n            throw new BPMEngineException(message);\n        } catch (final UnknownAPITypeException e) {\n            final String message = \"The engine API Implementation is unknown.\";\n            if (LOGGER.isErrorEnabled()) {\n                LOGGER.error(message, e);\n            }\n            throw new BPMEngineException(message);\n        } catch (final ServerAPIException e) {\n            final String message = \"The engine client was not able to communicate with the engine server.\";\n            if (LOGGER.isErrorEnabled()) {\n                LOGGER.error(message, e);\n            }\n            throw new BPMEngineException(message);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/BPMEngineException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.utils;\n\n/**\n * Technical exception thrown when the BPM engine report a API communication error.\n *\n * @author Ruiheng Fan, Anthony Birembaut\n */\npublic class BPMEngineException extends Exception {\n\n    /**\n     * UID\n     */\n    private static final long serialVersionUID = -4812682510242412305L;\n\n    /**\n     * @param message\n     *        message associated with the exception\n     */\n    public BPMEngineException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/BonitaHomeFolderAccessor.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.utils;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.file.Files;\nimport java.nio.file.StandardCopyOption;\n\nimport org.apache.commons.io.FilenameUtils;\nimport org.apache.commons.io.IOUtils;\nimport org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils;\nimport org.bonitasoft.engine.api.PlatformAPIAccessor;\nimport org.bonitasoft.engine.api.TemporaryContentAPI;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\nimport org.bonitasoft.engine.io.FileContent;\nimport org.bonitasoft.engine.io.TemporaryFileNotFoundException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class BonitaHomeFolderAccessor {\n\n    /**\n     * Logger\n     */\n    private static final Logger LOGGER = LoggerFactory.getLogger(BonitaHomeFolderAccessor.class.getName());\n\n    public BonitaHomeFolderAccessor() {\n    }\n\n    /**\n     * @param tempFileKey\n     * @return\n     * @throws IOException\n     * @deprecated use {@link #retrieveUploadedTempContent(String)} instead to avoid creating additional temp file\n     */\n    @Deprecated(since = \"9.0.0\")\n    public File getTempFile(final String tempFileKey) throws IOException {\n        try {\n            FileContent fileContent = retrieveUploadedTempContent(tempFileKey);\n            File file = makeUniqueFilename(fileContent.getFileName());\n            try (InputStream inputStream = fileContent.getInputStream()) {\n                Files.copy(inputStream, file.toPath(), StandardCopyOption.REPLACE_EXISTING);\n            }\n            return file;\n        } catch (BonitaException e) {\n            throw new IOException(e);\n        }\n    }\n\n    public FileContent retrieveUploadedTempContent(final String tempFileKey) throws BonitaException {\n        TemporaryContentAPI temporaryContentAPI = PlatformAPIAccessor.getTemporaryContentAPI();\n        try {\n            return temporaryContentAPI.retrieveTempFile(tempFileKey);\n        } catch (TemporaryFileNotFoundException e) {\n            LOGGER.error(\"Unable to find temporary file with key {}\", tempFileKey);\n            throw e;\n        } catch (BonitaRuntimeException e) {\n            LOGGER.error(\"Unable to retrieve temporary file with key {}\", tempFileKey);\n            throw new BonitaException(e);\n        }\n    }\n\n    public void removeUploadedTempContent(final String tempFileKey) {\n        try {\n            TemporaryContentAPI temporaryContentAPI = PlatformAPIAccessor.getTemporaryContentAPI();\n            temporaryContentAPI.removeTempFile(tempFileKey);\n        } catch (BonitaException | BonitaRuntimeException e) {\n            LOGGER.warn(\n                    \"Unable to remove temporary file with key {}. If still present, it will be cleaned by the scheduler.\",\n                    tempFileKey);\n        }\n    }\n\n    protected File makeUniqueFilename(final String fileName) throws IOException {\n        final File uploadedFile = File.createTempFile(\"tmp_\", getExtension(fileName),\n                getBonitaTenantConstantUtil().getTempFolder());\n        uploadedFile.deleteOnExit();\n        return uploadedFile;\n    }\n\n    protected String getExtension(final String fileName) {\n        return FilenameUtils.getExtension(fileName);\n    }\n\n    public WebBonitaConstantsUtils getBonitaTenantConstantUtil() {\n        return WebBonitaConstantsUtils.getTenantInstance();\n    }\n\n    public boolean isInTempFolder(final File file, final WebBonitaConstantsUtils webBonitaConstantsUtils)\n            throws IOException {\n        return isInFolder(file, webBonitaConstantsUtils.getTempFolder());\n    }\n\n    public boolean isInFolder(final File file, final File parentFolder) throws IOException {\n        try {\n            verifyFolderAuthorization(file, parentFolder);\n        } catch (final UnauthorizedFolderException e) {\n            return false;\n        }\n        return true;\n    }\n\n    private void verifyFolderAuthorization(final File file, final File parentFolder) throws IOException {\n        try {\n            if (!file.getCanonicalPath().startsWith(parentFolder.getCanonicalPath())) {\n                throw new UnauthorizedFolderException(\"Unauthorized access to the file \" + file.getPath());\n            }\n        } catch (final UnauthorizedFolderException e) {\n            LOGGER.error(\n                    \"Unauthorized access to the file {}. For security reasons, access to paths other than {} is restricted.\",\n                    file.getAbsolutePath(), parentFolder.getAbsolutePath(), e);\n            throw e;\n        }\n    }\n\n    public IconDescriptor getIconFromFileSystem(String iconKey) {\n        try {\n            TemporaryContentAPI temporaryContentAPI = PlatformAPIAccessor.getTemporaryContentAPI();\n            FileContent fileContent = temporaryContentAPI.retrieveTempFile(iconKey);\n            return new IconDescriptor(fileContent.getFileName(), IOUtils.toByteArray(fileContent.getInputStream()));\n        } catch (TemporaryFileNotFoundException e) {\n            LOGGER.error(\"Unable to find the icon temporary file with key {}\", iconKey);\n            throw new RuntimeException(e);\n        } catch (BonitaException | IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/CacheUtil.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.utils;\n\nimport static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder;\nimport static org.ehcache.config.builders.CacheManagerBuilder.newCacheManagerBuilder;\nimport static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.ehcache.Cache;\nimport org.ehcache.CacheManager;\nimport org.ehcache.config.units.EntryUnit;\n\n@Slf4j\npublic class CacheUtil {\n\n    private static final int DEFAULT_ON_HEAP_MAX_ENTRIES = 10_000;\n\n    protected static CacheManager CACHE_MANAGER = null;\n\n    protected static synchronized CacheManager getCacheManager() {\n        if (CACHE_MANAGER == null) {\n            if (log.isInfoEnabled()) {\n                log.info(\n                        \"Initializing Ehcache 3 CacheManager with programmatic configuration (heap-only, LRU eviction)\");\n            }\n            // Create CacheManager with programmatic configuration (no XML needed)\n            // Default cache template: heap-only with 10,000 entry capacity and LRU eviction\n            // Note: Ehcache 3 uses LRU (Least Recently Used) eviction by default when heap capacity is exceeded\n            CACHE_MANAGER = newCacheManagerBuilder()\n                    .withCache(\"default\", newCacheConfigurationBuilder(Object.class, Object.class,\n                            newResourcePoolsBuilder().heap(DEFAULT_ON_HEAP_MAX_ENTRIES, EntryUnit.ENTRIES).build())\n                            .build())\n                    .build(true);\n        }\n        return CACHE_MANAGER;\n    }\n\n    protected static synchronized Cache<Object, Object> createCache(final CacheManager cacheManager,\n            final String cacheName) {\n        // Double-check\n        Cache<Object, Object> cache = cacheManager.getCache(cacheName, Object.class, Object.class);\n        if (cache == null) {\n            // In Ehcache 3, we need to provide a configuration when creating a cache\n            // Using a simple heap-based cache with default settings (10,000 entries)\n            // Eviction policy: LRU (Least Recently Used) when heap capacity is exceeded\n            cache = cacheManager.createCache(cacheName,\n                    newCacheConfigurationBuilder(Object.class, Object.class,\n                            newResourcePoolsBuilder().heap(DEFAULT_ON_HEAP_MAX_ENTRIES, EntryUnit.ENTRIES).build())\n                            .build());\n        }\n        return cache;\n    }\n\n    public static void store(final String cacheName, final Object key, final Object value) {\n        final CacheManager cacheManager = getCacheManager();\n        Cache<Object, Object> cache = cacheManager.getCache(cacheName, Object.class, Object.class);\n        if (cache == null) {\n            cache = createCache(cacheManager, cacheName);\n        }\n        // In Ehcache 3, we directly put key-value without Element wrapper\n        cache.put(key, value);\n\n        if (log.isTraceEnabled()) {\n            log.trace(\"####Element {} created in cache with name {}\", key, cacheName);\n        }\n    }\n\n    public static Object get(final String cacheName, final Object key) {\n        Object value = null;\n        final CacheManager cacheManager = getCacheManager();\n        final Cache<Object, Object> cache = cacheManager.getCache(cacheName, Object.class, Object.class);\n        if (cache != null) {\n            // In Ehcache 3, get() returns the value directly (no Element wrapper)\n            value = cache.get(key);\n            if (value != null) {\n                if (log.isTraceEnabled()) {\n                    log.trace(\"####Element {} found in cache with name {}\", key, cacheName);\n                }\n            } else {\n                if (log.isTraceEnabled()) {\n                    log.trace(\"####Element {} not found in cache with name {}\", key, cacheName);\n                }\n            }\n        } else {\n            if (log.isTraceEnabled()) {\n                log.trace(\"####Cache with name {} doesn't exists or wasn't created yet.\", cacheName);\n            }\n        }\n        return value;\n    }\n\n    public static void clear(final String cacheName) {\n        final Cache<Object, Object> cache = getCacheManager().getCache(cacheName, Object.class, Object.class);\n        if (cache != null) {\n            // In Ehcache 3, clear() is used instead of removeAll()\n            cache.clear();\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/ContractTypeConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.utils;\n\nimport static org.bonitasoft.engine.bpm.contract.InputDefinition.FILE_INPUT_ID;\n\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.Serializable;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.OffsetDateTime;\nimport java.time.ZonedDateTime;\nimport java.time.format.DateTimeParseException;\nimport java.util.*;\nimport java.util.Map.Entry;\n\nimport org.apache.commons.beanutils.ConversionException;\nimport org.apache.commons.beanutils.ConvertUtilsBean;\nimport org.apache.commons.beanutils.converters.DateConverter;\nimport org.apache.commons.io.IOUtils;\nimport org.bonitasoft.engine.bpm.contract.*;\nimport org.bonitasoft.engine.bpm.contract.impl.ContractDefinitionImpl;\nimport org.bonitasoft.engine.bpm.contract.impl.InputDefinitionImpl;\nimport org.bonitasoft.engine.bpm.document.DocumentException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.io.FileContent;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Anthony Birembaut\n */\n\npublic class ContractTypeConverter {\n\n    /**\n     * Logger\n     */\n    protected static final Logger LOGGER = LoggerFactory.getLogger(ContractTypeConverter.class.getName());\n\n    public static final String[] ISO_8601_DATE_PATTERNS = new String[] { \"yyyy-MM-dd\", \"yyyy-MM-dd'T'HH:mm:ss\",\n            \"yyyy-MM-dd'T'HH:mm:ss'Z'\",\n            \"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'\" };\n\n    static final String CONTENT_TYPE = \"contentType\";\n    static final String FILE_TEMP_PATH = \"tempPath\";\n    static final String TEMP_PATH_DESCRIPTION = \"file name in the temporary upload directory\";\n\n    protected BonitaHomeFolderAccessor bonitaHomeFolderAccessor = new BonitaHomeFolderAccessor();\n\n    private final ConvertUtilsBean convertUtilsBean;\n\n    private long maxSizeForTenant;\n\n    public ContractTypeConverter(final String[] datePatterns) {\n        convertUtilsBean = new ConvertUtilsBean();\n        convertUtilsBean.register(true, false, 0);\n        final DateConverter dateConverter = new DateConverter();\n        dateConverter.setPatterns(datePatterns);\n        dateConverter.setTimeZone(TimeZone.getTimeZone(\"GMT\"));\n        convertUtilsBean.register(dateConverter, Date.class);\n    }\n\n    Object convertToType(final Type type, final Serializable parameterValue) {\n        final Class<? extends Serializable> clazz = getClassFromType(type);\n        Serializable preprocessedParameterValue = preprocessInputs(type, parameterValue);\n        return convertToType(clazz, preprocessedParameterValue);\n    }\n\n    private Serializable preprocessInputs(final Type type, final Serializable parameterValue) {\n        //Also support Integer as DATE contract input (as deserialization is handled by jackson it can be mapped to an integer instead of a long when it is a small number)\n        if (Type.DATE.equals(type) && parameterValue instanceof Integer) {\n            return Long.valueOf((Integer) parameterValue);\n        }\n        return parameterValue;\n    }\n\n    public Map<String, Serializable> getProcessedInput(final ContractDefinition processContract,\n            final Map<String, Serializable> inputs,\n            final long maxSizeForTenant) throws FileNotFoundException {\n        this.maxSizeForTenant = maxSizeForTenant;\n        final Map<String, Serializable> processedInputs = new HashMap<>();\n        final Map<String, Serializable> contractDefinitionMap = processContract == null ? Collections.emptyMap()\n                : createContractInputMap(processContract.getInputs());\n        if (inputs != null) {\n            for (final Entry<String, Serializable> inputEntry : inputs.entrySet()) {\n                processedInputs.put(inputEntry.getKey(),\n                        convertInputToExpectedType(inputEntry.getValue(),\n                                contractDefinitionMap.get(inputEntry.getKey())));\n            }\n        }\n        return processedInputs;\n    }\n\n    public void deleteTemporaryFiles(Map<String, Serializable> inputs) {\n        if (inputs != null) {\n            deleteTemporaryFilesInternal((Serializable) inputs);\n        }\n    }\n\n    private void deleteTemporaryFilesInternal(Serializable inputValue) {\n        if (inputValue == null) {\n            return;\n        }\n        if (inputValue instanceof List) {\n            for (Object element : ((List) inputValue)) {\n                deleteTemporaryFilesInternal(((Serializable) element));\n            }\n        } else if (inputValue instanceof Map) {\n            for (Map.Entry<String, Serializable> element : ((Map<String, Serializable>) inputValue).entrySet()) {\n                if (element.getKey().equals(FILE_TEMP_PATH) && element.getValue() != null) {\n                    String path = (String) element.getValue();\n                    bonitaHomeFolderAccessor.removeUploadedTempContent(path);\n                } else {\n                    deleteTemporaryFilesInternal(element.getValue());\n                }\n            }\n        }\n\n    }\n\n    private Serializable convertInputToExpectedType(final Serializable inputValue, final Serializable inputDefinition)\n            throws FileNotFoundException {\n        if (inputValue == null) {\n            return null;\n        } else if (inputValue instanceof List) {\n            return convertMultipleInputToExpectedType(inputValue, inputDefinition);\n        } else {\n            return convertSingleInputToExpectedType(inputValue, inputDefinition);\n        }\n    }\n\n    private Serializable convertMultipleInputToExpectedType(final Serializable inputValue,\n            final Serializable inputDefinition)\n            throws FileNotFoundException {\n        @SuppressWarnings(\"unchecked\")\n        final List<Serializable> listOfValues = (List<Serializable>) inputValue;\n        final List<Serializable> convertedListOfValues = new ArrayList<>();\n        for (final Serializable value : listOfValues) {\n            Serializable convertedValue = null;\n            if (value != null) {\n                convertedValue = convertSingleInputToExpectedType(value, inputDefinition);\n            }\n            convertedListOfValues.add(convertedValue);\n        }\n        return (Serializable) convertedListOfValues;\n    }\n\n    private Serializable convertSingleInputToExpectedType(final Serializable inputValue,\n            final Serializable inputDefinition)\n            throws FileNotFoundException {\n        if (inputDefinition == null) {\n            return inputValue;\n        } else if (inputDefinition instanceof Map) {\n            @SuppressWarnings(\"unchecked\")\n            final Map<String, Serializable> mapOfInputDefinition = (Map<String, Serializable>) inputDefinition;\n            return convertComplexInputToExpectedType(inputValue, mapOfInputDefinition);\n        } else {\n            final InputDefinition simpleInputDefinition = (InputDefinition) inputDefinition;\n            if (Type.FILE.equals(simpleInputDefinition.getType())) {\n                return convertFileInputToExpectedType(inputValue);\n            } else {\n                return (Serializable) convertToType(simpleInputDefinition.getType(), inputValue);\n            }\n        }\n    }\n\n    private Serializable convertComplexInputToExpectedType(final Serializable inputValue,\n            final Map<String, Serializable> mapOfInputDefinition) throws FileNotFoundException {\n        if (inputValue instanceof Map) {\n            @SuppressWarnings(\"unchecked\")\n            final Map<String, Serializable> mapOfValues = (Map<String, Serializable>) inputValue;\n            final Map<String, Serializable> convertedMapOfValues = new HashMap<>();\n            for (final Entry<String, Serializable> valueEntry : mapOfValues.entrySet()) {\n                final Serializable childInputDefinition = mapOfInputDefinition.get(valueEntry.getKey());\n                final Serializable convertedValue = convertInputToExpectedType(valueEntry.getValue(),\n                        childInputDefinition);\n                convertedMapOfValues.put(valueEntry.getKey(), convertedValue);\n            }\n            return (Serializable) convertedMapOfValues;\n        } else {\n            return inputValue;\n        }\n    }\n\n    private Serializable convertFileInputToExpectedType(final Serializable inputValue) throws FileNotFoundException {\n        if (inputValue instanceof Map) {\n            @SuppressWarnings(\"unchecked\")\n            final Map<String, Serializable> mapOfValues = (Map<String, Serializable>) inputValue;\n            if (mapOfValues.containsKey(InputDefinition.FILE_INPUT_FILENAME)\n                    && mapOfValues.containsKey(FILE_TEMP_PATH)) {\n                final String filename = (String) mapOfValues.get(InputDefinition.FILE_INPUT_FILENAME);\n                return new FileInputValue(\n                        DocumentUtil.sanitizeFilename(filename),\n                        (String) mapOfValues.get(CONTENT_TYPE),\n                        retrieveFileAndGetContent((String) mapOfValues.get(FILE_TEMP_PATH)),\n                        (String) mapOfValues.get(FILE_INPUT_ID));\n            }\n        }\n        return inputValue;\n    }\n\n    private byte[] retrieveFileAndGetContent(final String fileTempPath) throws FileNotFoundException {\n        byte[] fileContent = null;\n        if (fileTempPath != null) {\n            try {\n                final FileContent sourceFile = bonitaHomeFolderAccessor.retrieveUploadedTempContent(fileTempPath);\n                try (InputStream inputStream = sourceFile.getInputStream()) {\n                    fileContent = getFileContent(inputStream, sourceFile.getSize());\n                }\n            } catch (final BonitaException e) {\n                throw new FileNotFoundException(\"Cannot find \" + fileTempPath + \" temp file.\");\n            } catch (final IOException e) {\n                throw new RuntimeException(e);\n            }\n        }\n        return fileContent;\n    }\n\n    private byte[] getFileContent(final InputStream inputStream, long size)\n            throws DocumentException, IOException {\n        byte[] fileContent;\n        if (size > maxSizeForTenant * 1048576 || size > Integer.MAX_VALUE) { // more than 2 GB\n            final String errorMessage = \"This document is exceeded configured max size (\" + maxSizeForTenant\n                    + \"Mb) or 2GB\";\n            throw new DocumentException(errorMessage);\n        }\n        return IOUtils.toByteArray(inputStream);\n    }\n\n    private Map<String, Serializable> createContractInputMap(final List<InputDefinition> inputDefinitions) {\n        final Map<String, Serializable> contractDefinitionMap = new HashMap<>();\n        for (final InputDefinition inputDefinition : inputDefinitions) {\n            if (inputDefinition.hasChildren() && !Type.FILE.equals(inputDefinition.getType())) {\n                contractDefinitionMap.put(inputDefinition.getName(),\n                        (Serializable) createContractInputMap(inputDefinition.getInputs()));\n            } else {\n                contractDefinitionMap.put(inputDefinition.getName(), inputDefinition);\n            }\n        }\n        return contractDefinitionMap;\n    }\n\n    public ContractDefinition getAdaptedContractDefinition(final ContractDefinition contract) {\n        if (contract == null) {\n            return null;\n        }\n        final List<ConstraintDefinition> constraints = contract.getConstraints();\n        final List<InputDefinition> inputDefinitions = adaptContractInputList(contract.getInputs());\n        return getContractDefinition(constraints, inputDefinitions);\n    }\n\n    private List<InputDefinition> adaptContractInputList(final List<InputDefinition> inputDefinitions) {\n        final List<InputDefinition> contractDefinition = new ArrayList<>();\n        for (final InputDefinition inputDefinition : inputDefinitions) {\n            List<InputDefinition> childInputDefinitions;\n            if (Type.FILE.equals(inputDefinition.getType())) {\n                childInputDefinitions = getFileChildInputDefinitions(inputDefinition);\n            } else if (inputDefinition.hasChildren()) {\n                childInputDefinitions = adaptContractInputList(inputDefinition.getInputs());\n            } else {\n                childInputDefinitions = new ArrayList<>();\n            }\n            final InputDefinition newInputDefinition = new InputDefinitionImpl(inputDefinition.getName(),\n                    inputDefinition.getDescription(),\n                    inputDefinition.isMultiple(), inputDefinition.getType(), childInputDefinitions);\n            contractDefinition.add(newInputDefinition);\n        }\n        return contractDefinition;\n    }\n\n    private List<InputDefinition> getFileChildInputDefinitions(final InputDefinition inputDefinition) {\n        List<InputDefinition> childInputDefinitions;\n        childInputDefinitions = new ArrayList<>();\n        for (final InputDefinition childInputDefinition : inputDefinition.getInputs()) {\n            if (Type.BYTE_ARRAY.equals(childInputDefinition.getType())) {\n                childInputDefinitions.add(new InputDefinitionImpl(FILE_TEMP_PATH, TEMP_PATH_DESCRIPTION, false,\n                        Type.TEXT, new ArrayList<>()));\n            } else {\n                childInputDefinitions.add(childInputDefinition);\n            }\n        }\n        return childInputDefinitions;\n    }\n\n    private ContractDefinitionImpl getContractDefinition(final List<ConstraintDefinition> constraints,\n            final List<InputDefinition> inputDefinitions) {\n        final ContractDefinitionImpl contractDefinition = new ContractDefinitionImpl();\n        for (final ConstraintDefinition constraint : constraints) {\n            contractDefinition.addConstraint(constraint);\n        }\n\n        for (final InputDefinition input : inputDefinitions) {\n            contractDefinition.addInput(input);\n        }\n        return contractDefinition;\n    }\n\n    private Object convertToType(final Class<? extends Serializable> clazz, final Serializable parameterValue) {\n        if (parameterValue == null) {\n            return null;\n        }\n        String paramValueString = parameterValue.toString();\n        try {\n            if (clazz == LocalDate.class) {\n                //We drop useless info received from the widget ex: 2010-12-04T18:42:10Z, we drop T18:42:10Z to allow conversion\n                if (paramValueString.length() > 10) {\n                    LOGGER.debug(\"The string \" + paramValueString\n                            + \" contains information that will be dropped to convert it to a LocalDate (most likely time and timezone information which are not relevant).\");\n                    paramValueString = paramValueString.substring(0, 10);\n                }\n                return LocalDate.parse(paramValueString);\n            } else if (clazz == LocalDateTime.class) {\n                try {\n                    return LocalDateTime.parse(paramValueString);\n                } catch (DateTimeParseException e) {\n                    LOGGER.debug(\"The string \" + paramValueString\n                            + \" contains information that will be dropped to convert it to a LocalDateTime (most likely time and timezone information which are not relevant).\");\n                    //We drop the timezone info from the String:\n                    return ZonedDateTime.parse(paramValueString).toLocalDateTime();\n                }\n            } else if (clazz == OffsetDateTime.class) {\n                ZonedDateTime zonedDateTime = ZonedDateTime.parse(paramValueString);\n                return zonedDateTime.toOffsetDateTime();\n            } else {\n                return convertUtilsBean.convert(parameterValue, clazz);\n            }\n        } catch (final ConversionException | DateTimeParseException e) {\n            LOGGER.info(\"unable to parse '\" + parameterValue + \"' to type \" + clazz.getName(), e);\n            return parameterValue;\n        }\n    }\n\n    private Class<? extends Serializable> getClassFromType(final Type type) {\n        switch (type) {\n            case BOOLEAN:\n                return Boolean.class;\n            case DATE:\n                return Date.class;\n            case INTEGER:\n                return Integer.class;\n            case DECIMAL:\n                return Double.class;\n            case BYTE_ARRAY:\n                return Byte[].class;\n            case LONG:\n                return Long.class;\n            case LOCALDATE:\n                return LocalDate.class;\n            case LOCALDATETIME:\n                return LocalDateTime.class;\n            case OFFSETDATETIME:\n                return OffsetDateTime.class;\n            default:\n                return String.class;\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/DefaultTenantIdException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.utils;\n\n/**\n * @author Vincent Elcrin\n */\npublic class DefaultTenantIdException extends RuntimeException {\n\n    private static final long serialVersionUID = -6433100847740743825L;\n\n    public DefaultTenantIdException(Exception e) {\n        super(\"Can't retrieve default tenant id\", e);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/DocumentUtil.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.utils;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\n\n/**\n * @author Yongtao Guo\n */\npublic class DocumentUtil {\n\n    public static byte[] getArrayByte(final InputStream input, final int estimatedSize) throws IOException {\n        final ByteArrayOutputStream output = new ByteArrayOutputStream(estimatedSize);\n        try (output) {\n            final byte[] buf = new byte[8192];\n            int len;\n\n            while ((len = input.read(buf)) >= 0) {\n                output.write(buf, 0, len);\n            }\n        }\n        return output.toByteArray();\n    }\n\n    public static byte[] getArrayByteFromFile(final File f) throws IOException {\n        final long length = f.length();\n        if (length > Integer.MAX_VALUE) { // more than 2 GB\n            throw new IOException(\"File too big\");\n        }\n\n        try (FileInputStream input = new FileInputStream(f)) {\n            return getArrayByte(input, (int) length);\n        }\n    }\n\n    public static String sanitizeFilename(String filename) {\n        if (filename != null) {\n            return filename.replaceAll(\"[<>\\\"]+\", \"_\").trim();\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/IconDescriptor.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.utils;\n\nimport java.util.Arrays;\nimport java.util.Objects;\n\n/**\n * @author Baptiste Mesta\n */\npublic class IconDescriptor {\n\n    private final String filename;\n    private final byte[] content;\n\n    public IconDescriptor(String filename, byte[] content) {\n        this.filename = filename;\n        this.content = content;\n    }\n\n    public byte[] getContent() {\n        return content;\n    }\n\n    public String getFilename() {\n        return filename;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (o == null || getClass() != o.getClass())\n            return false;\n        IconDescriptor that = (IconDescriptor) o;\n        return Objects.equals(filename, that.filename) &&\n                Arrays.equals(content, that.content);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(filename, content);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/ListUtil.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.utils;\n\nimport java.util.List;\n\n/**\n * @author Haojie Yuan\n */\npublic class ListUtil {\n\n    public static List<?> paginate(final List<?> list, final int page, final int resultsByPage) {\n\n        final int startIndex = page * resultsByPage;\n        if (startIndex > list.size()) {\n            final List<?> result = list.subList(0, 0);\n            result.clear();\n            return result;\n        }\n\n        final int endIndex = Math.min(startIndex + resultsByPage, list.size());\n\n        return list.subList(startIndex, endIndex);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/LocaleUtils.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.utils;\n\nimport java.util.*;\n\nimport javax.servlet.http.Cookie;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.bonitasoft.console.common.server.i18n.I18n;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Anthony Birembaut\n */\npublic class LocaleUtils {\n\n    public static final String LOCALE_PARAM = \"locale\";\n\n    /**\n     * user's locale URL parameter used by the login JSP\n     */\n    public static final String PORTAL_LOCALE_PARAM = \"_l\";\n\n    public static final String DEFAULT_LOCALE = \"en\";\n\n    public static final String LOCALE_COOKIE_NAME = \"BOS_Locale\";\n\n    private static final String DEFAULT_APPLICATION = \"portal\";\n\n    private static final Map<String, String> AVAILABLE_LOCALES = I18n.getInstance()\n            .getAvailableLocalesFor(DEFAULT_APPLICATION);\n\n    /**\n     * Logger\n     */\n    private static final Logger LOGGER = LoggerFactory.getLogger(LocaleUtils.class.getName());\n\n    /**\n     * Return the user locale as set in the the request. If the locale code is invalid, returns the default locale (en).\n     * If the locale is not passed in the request try to get it from the BOS_Locale cookie.\n     * If the cookie is not set returns the Browser locale\n     * If the browser locale is not set returns the default locale (en).\n     * This method should be used sparingly for performances reasons (gather in one call when possible)\n     *\n     * @param request\n     *        the HTTP servlet request\n     * @return the user locale\n     */\n    public static String getUserLocaleAsString(final HttpServletRequest request) {\n        String locale = getLocaleFromRequestURL(request);\n        if (locale == null) {\n            locale = getStandardizedLocaleFromCookie(request);\n        }\n        if (locale == null) {\n            String browserLocale = getLocaleFromBrowser(request);\n            locale = browserLocale != null ? browserLocale : DEFAULT_LOCALE;\n        }\n        return locale;\n    }\n\n    public static void logUnsupportedLocale(String unsupportedLocale, String usedLocale) {\n        if (!unsupportedLocale.equals(usedLocale) && LOGGER.isDebugEnabled()) {\n            LOGGER.debug(\"Locale \\\"\" + unsupportedLocale + \"\\\" is not part of the locales available. Using locale \\\"\"\n                    + usedLocale + \"\\\"\");\n        }\n    }\n\n    public static boolean isLocaleSupportedInPortal(String locale) {\n        return new ArrayList<>(AVAILABLE_LOCALES.keySet()).contains(locale);\n    }\n\n    public static boolean canLocaleBeReducedToSupportedLocale(String locale, String supportedLocale) {\n        if (locale.indexOf(\"-\") > 0) {\n            return locale.substring(0, locale.indexOf(\"-\")).equals(supportedLocale);\n        } else if (locale.indexOf(\"_\") > 0) {\n            return locale.substring(0, locale.indexOf(\"_\")).equals(supportedLocale);\n        }\n        return false;\n    }\n\n    private static String standardizeLocale(String locale) {\n        if (isLocaleSupportedInPortal(locale)) {\n            return locale;\n        } else {\n            List<String> supportedLocales = new ArrayList<>(AVAILABLE_LOCALES.keySet());\n            for (String supportedLocale : supportedLocales) {\n                if (canLocaleBeReducedToSupportedLocale(locale, supportedLocale)) {\n                    if (LOGGER.isTraceEnabled()) {\n                        LOGGER.trace(\n                                \"Using available locale \\\"\" + supportedLocale + \"\\\" instead of \\\"\" + locale + \"\\\"\");\n                    }\n                    return supportedLocale;\n                }\n            }\n        }\n\n        logUnsupportedLocale(locale, \"en\");\n        return DEFAULT_LOCALE;\n    }\n\n    public static String getStandardizedLocaleFromCookie(final HttpServletRequest request) {\n        String locale = getLocaleFromCookies(request);\n        return locale != null ? standardizeLocale(locale) : null;\n    }\n\n    public static String getLocaleFromCookies(HttpServletRequest request) {\n        if (request.getCookies() != null) {\n            for (final Cookie cookie : request.getCookies()) {\n                if (cookie.getName().equals(LOCALE_COOKIE_NAME)) {\n                    return cookie.getValue();\n                }\n            }\n        }\n        return null;\n    }\n\n    public static String getLocaleFromBrowser(final HttpServletRequest request) {\n        Locale browserLocale = request.getLocale();\n        return browserLocale != null ? standardizeLocale(browserLocale.toString()) : null;\n    }\n\n    public static Locale getUserLocale(final HttpServletRequest request) {\n        return org.apache.commons.lang3.LocaleUtils.toLocale(getUserLocaleAsString(request));\n    }\n\n    public static String getLocaleFromRequestURL(final HttpServletRequest request) {\n        String localeAsString = request.getParameter(LOCALE_PARAM);\n        if (localeAsString == null) {\n            localeAsString = request.getParameter(PORTAL_LOCALE_PARAM);\n        }\n        if (localeAsString != null && localeAsString.length() > 0) {\n            try {\n                org.apache.commons.lang3.LocaleUtils.toLocale(localeAsString);\n                localeAsString = standardizeLocale(localeAsString);\n            } catch (Exception e) {\n                logUnsupportedLocale(localeAsString, \"en\");\n                localeAsString = DEFAULT_LOCALE;\n            }\n            return localeAsString;\n        }\n        return null;\n    }\n\n    public static void addOrReplaceLocaleCookieResponse(final HttpServletResponse response, final String locale) {\n        String standardizedLocale = standardizeLocale(locale);\n\n        Cookie localeCookie = new Cookie(LOCALE_COOKIE_NAME, standardizedLocale);\n        localeCookie.setPath(\"/\");\n        response.addCookie(localeCookie);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/PlatformManagementUtils.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.utils;\n\nimport java.io.IOException;\nimport java.lang.reflect.InvocationTargetException;\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.bonitasoft.console.common.server.preferences.properties.ConfigurationFilesManager;\nimport org.bonitasoft.engine.api.ApiAccessType;\nimport org.bonitasoft.engine.api.PlatformAPI;\nimport org.bonitasoft.engine.api.PlatformAPIAccessor;\nimport org.bonitasoft.engine.api.PlatformLoginAPI;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.platform.InvalidPlatformCredentialsException;\nimport org.bonitasoft.engine.session.PlatformSession;\nimport org.bonitasoft.engine.util.APITypeManager;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Baptiste Mesta\n */\npublic class PlatformManagementUtils {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(PlatformManagementUtils.class.getName());\n\n    private final ConfigurationFilesManager configurationFilesManager = ConfigurationFilesManager.getInstance();\n\n    //package local for testing purpose\n    boolean isLocal() throws UnknownAPITypeException, ServerAPIException, IOException {\n        return ApiAccessType.LOCAL.equals(APITypeManager.getAPIType());\n    }\n\n    //package local for testing purpose\n    PlatformAPI getPlatformAPI(final PlatformSession platformSession)\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return PlatformAPIAccessor.getPlatformAPI(platformSession);\n    }\n\n    //package local for testing purpose\n    PlatformLoginAPI getPlatformLoginAPI()\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return PlatformAPIAccessor.getPlatformLoginAPI();\n    }\n\n    public PlatformSession platformLogin() throws BonitaException, IOException {\n        if (isLocal()) {\n            try {\n                return localPlatformLogin();\n            } catch (final Exception e) {\n                throw new ServerAPIException(\"Unable to login locally\", e);\n            }\n        } else {\n            String username = System.getProperty(\"org.bonitasoft.platform.username\");\n            String password = System.getProperty(\"org.bonitasoft.platform.password\");\n            try {\n\n                return getPlatformLoginAPI().login(username,\n                        password);\n            } catch (InvalidPlatformCredentialsException e) {\n                throw new InvalidPlatformCredentialsException(\"The portal is not able to login to the engine because \" +\n                        \"system properties org.bonitasoft.platform.username and org.bonitasoft.platform.password are \" +\n                        \"not set correctly to the platform administrator credentials.\\n \" +\n                        \"These properties must be set when connecting to a Bonita engine that is not local. \" +\n                        \"If the engine is local, change the connection to LOCAL using the APITypeManager\");\n            }\n        }\n    }\n\n    PlatformSession localPlatformLogin()\n            throws ClassNotFoundException, IllegalAccessException, InvocationTargetException, NoSuchMethodException,\n            InstantiationException {\n        final Class<?> api = Class.forName(\"org.bonitasoft.engine.LocalLoginMechanism\");\n        return (PlatformSession) api.getDeclaredMethod(\"login\").invoke(api.getDeclaredConstructor().newInstance());\n    }\n\n    void platformLogout(final PlatformSession platformSession) throws BonitaException {\n        final PlatformLoginAPI platformLoginAPI = getPlatformLoginAPI();\n        platformLoginAPI.logout(platformSession);\n    }\n\n    private void retrieveTenantsConfiguration(final PlatformAPI platformAPI) throws IOException {\n        configurationFilesManager.setTenantConfigurationFiles(platformAPI.getClientTenantConfigurations());\n    }\n\n    private void retrievePlatformConfiguration(final PlatformAPI platformAPI) throws IOException {\n        final Map<String, byte[]> clientPlatformConfigurations = platformAPI.getClientPlatformConfigurations();\n        configurationFilesManager.setPlatformConfigurations(clientPlatformConfigurations);\n    }\n\n    public void initializePlatformConfiguration() throws BonitaException, IOException {\n        final PlatformSession platformSession = platformLogin();\n        final PlatformAPI platformAPI = getPlatformAPI(platformSession);\n        retrievePlatformConfiguration(platformAPI);\n        retrieveTenantsConfiguration(platformAPI);\n        platformLogout(platformSession);\n    }\n\n    public void updateConfigurationFile(final String file, final byte[] content) throws IOException, BonitaException {\n        final PlatformSession platformSession = platformLogin();\n        final PlatformAPI platformAPI = getPlatformAPI(platformSession);\n        platformAPI.updateClientTenantConfigurationFile(file, content);\n        platformLogout(platformSession);\n    }\n\n    /**\n     * String => configuration file name\n     * byte[] => content of the configuration file\n     */\n    public Map<String, byte[]> readTenantConfigurationsFromEngine() {\n        try {\n            final PlatformSession platformSession = platformLogin();\n            try {\n                return getPlatformAPI(platformSession).getClientTenantConfigurations();\n            } finally {\n                platformLogout(platformSession);\n            }\n        } catch (BonitaException | IOException e) {\n            LOGGER.error(\"Cannot retrieve tenant configurations\", e);\n            return Collections.emptyMap();\n        }\n    }\n\n    public boolean isPlatformAvailable() {\n        try {\n            final PlatformSession platformSession = platformLogin();\n            try {\n                PlatformAPI platformAPI = PlatformAPIAccessor.getPlatformAPI(platformSession);\n                return platformAPI.isNodeStarted();\n            } finally {\n                platformLogout(platformSession);\n            }\n        } catch (Exception e) {\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(\"Platform is not available.\", e);\n            } else if (LOGGER.isInfoEnabled()) {\n                LOGGER.info(\"Platform is not available.\");\n            }\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/SessionUtil.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.utils;\n\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.user.User;\n\n/**\n * @author Ruiheng.Fan\n * @author Baptiste Mesta\n */\npublic class SessionUtil {\n\n    /**\n     * the session param for the engine API session\n     */\n    public static final String API_SESSION_PARAM_KEY = \"apiSession\";\n\n    /**\n     * the session param for the user\n     */\n    public static final String USER_SESSION_PARAM_KEY = \"user\";\n\n    /**\n     * the session param for the username\n     */\n    public static final String USERNAME_SESSION_PARAM = \"username\";\n\n    public static void sessionLogin(final User user, final APISession apiSession, final HttpSession session) {\n        session.setAttribute(USERNAME_SESSION_PARAM, user.getUsername());\n        session.setAttribute(USER_SESSION_PARAM_KEY, user);\n        session.setAttribute(API_SESSION_PARAM_KEY, apiSession);\n    }\n\n    public static void sessionLogout(final HttpSession session) {\n        sessionLogout(session, true);\n    }\n\n    public static void sessionLogout(final HttpSession session, final boolean invalidateHTTPSession) {\n        session.removeAttribute(API_SESSION_PARAM_KEY);\n        session.removeAttribute(USERNAME_SESSION_PARAM);\n        session.removeAttribute(USER_SESSION_PARAM_KEY);\n        if (invalidateHTTPSession) {\n            session.invalidate();\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/TenantCacheUtil.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.utils;\n\npublic class TenantCacheUtil {\n\n    protected static final String PROCESS_ACTOR_INITIATOR_CACHE = \"processActorInitiatorCache\";\n\n    protected TenantCacheUtil() {\n    }\n\n    public Long getProcessActorInitiatorId(final Long processId) {\n        return (Long) CacheUtil.get(PROCESS_ACTOR_INITIATOR_CACHE, processId);\n    }\n\n    public Long storeProcessActorInitiatorId(final Long processId, final Long actorInitiatorId) {\n        CacheUtil.store(PROCESS_ACTOR_INITIATOR_CACHE, processId, actorInitiatorId);\n        return actorInitiatorId;\n    }\n\n    public void clearProcessActorInitiators() {\n        CacheUtil.clear(PROCESS_ACTOR_INITIATOR_CACHE);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/TenantCacheUtilFactory.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.utils;\n\n/**\n * @author Julien Mege\n */\npublic class TenantCacheUtilFactory {\n\n    private static TenantCacheUtil tenantCacheUtil;\n\n    /**\n     * Get FormCacheUtil of different Domain\n     *\n     * @return TenatCacheUtil\n     */\n    public static TenantCacheUtil getTenantCacheUtil() {\n        if (tenantCacheUtil == null) {\n            tenantCacheUtil = new TenantCacheUtil();\n        }\n        return tenantCacheUtil;\n    }\n\n    public static void clearTenantCacheUtil() {\n        tenantCacheUtil.clearProcessActorInitiators();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/TenantsManagementUtils.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.utils;\n\nimport org.bonitasoft.engine.api.ProfileAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.profile.ProfileCriterion;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.InvalidSessionException;\n\n/**\n * Tenant management utils class\n *\n * @author Anthony Birembaut\n */\npublic class TenantsManagementUtils {\n\n    /**\n     * Check for user's profile\n     */\n    public static boolean hasProfileForUser(final APISession apiSession)\n            throws InvalidSessionException, BonitaHomeNotSetException,\n            ServerAPIException, UnknownAPITypeException {\n        return !getProfileApi(apiSession).getProfilesForUser(apiSession.getUserId(), 0, 1, ProfileCriterion.ID_ASC)\n                .isEmpty();\n    }\n\n    private static ProfileAPI getProfileApi(final APISession session)\n            throws InvalidSessionException, BonitaHomeNotSetException, ServerAPIException,\n            UnknownAPITypeException {\n        return TenantAPIAccessor.getProfileAPI(session);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/UnauthorizedFolderException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.utils;\n\nimport java.io.IOException;\n\n/**\n * Technical exception thrown when we try to read/write a file out of the authorized file system path.\n *\n * @author Julien Mege\n */\npublic class UnauthorizedFolderException extends IOException {\n\n    private static final long serialVersionUID = 1071342750973031637L;\n\n    public UnauthorizedFolderException(final String message, final Throwable throwable) {\n        super(message, throwable);\n    }\n\n    public UnauthorizedFolderException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/UnzipUtil.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.utils;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\n\nimport org.bonitasoft.engine.io.IOUtil;\n\n/**\n * Utility class extracting zip file\n *\n * @author Zhiheng Yang\n */\npublic class UnzipUtil {\n\n    /**\n     * Unzip a zip file from InputStream.\n     * Client is responsible to close the input stream.\n     */\n    public static synchronized void unzip(final InputStream sourceFile, final String targetPath) throws IOException {\n        IOUtil.unzipToFolder(sourceFile, new File(targetPath));\n    }\n\n    /**\n     * Unzip a zip file from InputStream\n     *\n     * @param zipFile\n     *        of SourceFile\n     * @param targetPath\n     * @throws IOException\n     * @throws FileNotFoundException\n     */\n    public static synchronized void unzip(final File zipFile, final String targetPath)\n            throws FileNotFoundException, IOException {\n        try (final FileInputStream zipFileInputStream = new FileInputStream(zipFile)) {\n            unzip(zipFileInputStream, targetPath);\n        }\n\n    }\n\n    public static synchronized void unzip(final File zipFile, final String targetPath, final boolean deleteFileAfterZip)\n            throws IOException {\n        unzip(zipFile, targetPath);\n        if (deleteFileAfterZip) {\n            zipFile.delete();\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/UrlBuilder.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.utils;\n\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.nio.charset.StandardCharsets;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.apache.http.Consts;\nimport org.apache.http.NameValuePair;\nimport org.apache.http.client.utils.URIBuilder;\nimport org.apache.http.client.utils.URLEncodedUtils;\nimport org.apache.http.message.BasicNameValuePair;\n\n/**\n * @author Vincent Elcrin, Julien Reboul\n */\npublic class UrlBuilder {\n\n    private final URIBuilder uriBuilder;\n\n    private final List<NameValuePair> parameters = new LinkedList<>();\n\n    public UrlBuilder(final String urlString) {\n        URI uri;\n        try {\n            uri = new URI(urlString);\n        } catch (URISyntaxException e) {\n            throw new RuntimeException(e);\n        }\n        uriBuilder = new URIBuilder(uri);\n        uriBuilder.setCharset(Consts.UTF_8);\n        if (uri.getRawQuery() != null) {\n            //avoid NPE thrown by httpClient 4.5.2 regression\n            parameters.addAll(URLEncodedUtils.parse(uri.getRawQuery(), StandardCharsets.UTF_8));\n        }\n    }\n\n    public void appendParameter(final String key, final String... values) {\n        if (!isParameterAlreadyDefined(parameters, key)) {\n            parameters.add(new BasicNameValuePair(key, new UrlValue(values).toString()));\n        }\n    }\n\n    public void removeParameter(final String key) {\n        parameters.removeIf(param -> param.getName().equals(key));\n    }\n\n    private boolean isParameterAlreadyDefined(List<NameValuePair> params, String key) {\n        for (NameValuePair param : params) {\n            if (param.getName().equals(key)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    public void appendParameters(final Map<String, String[]> parameters) {\n        for (Entry<String, String[]> next : parameters.entrySet()) {\n            appendParameter(next.getKey(),\n                    new UrlValue(next.getValue()).toString());\n        }\n    }\n\n    public String build() {\n        if (!parameters.isEmpty()) {\n            uriBuilder.setParameters(parameters);\n        } else {\n            //in case the parameters list is an empty list setParameters does not remove the question mark\n            //removeQuery does remove the question mark in the URL\n            uriBuilder.removeQuery();\n        }\n        return uriBuilder.toString();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/UrlValue.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.utils;\n\n/**\n * @author Vincent Elcrin\n */\npublic class UrlValue {\n\n    private final String value;\n\n    public UrlValue(String value) {\n        this.value = value;\n    }\n\n    public UrlValue(String... values) {\n        this(merge(values));\n    }\n\n    private static String merge(String... values) {\n        final StringBuilder value = new StringBuilder();\n        for (final String parameterValue : values) {\n            if (value.length() > 0) {\n                value.append(\",\");\n            }\n            value.append(parameterValue);\n        }\n        return value.toString();\n    }\n\n    @Override\n    public String toString() {\n        return value;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/server/ConsoleServiceFactory.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.server;\n\nimport org.bonitasoft.console.server.service.ApplicationsImportService;\nimport org.bonitasoft.console.server.service.OrganizationImportService;\nimport org.bonitasoft.console.server.service.ProcessActorImportService;\nimport org.bonitasoft.web.toolkit.server.Service;\nimport org.bonitasoft.web.toolkit.server.ServiceFactory;\nimport org.bonitasoft.web.toolkit.server.ServiceNotFoundException;\n\npublic class ConsoleServiceFactory implements ServiceFactory {\n\n    @Override\n    public Service getService(final String calledToolToken) {\n        if (OrganizationImportService.TOKEN.equals(calledToolToken)) {\n            return new OrganizationImportService();\n        } else if (ProcessActorImportService.TOKEN.equals(calledToolToken)) {\n            return new ProcessActorImportService();\n        } else if (ApplicationsImportService.TOKEN.equals(calledToolToken)) {\n            return new ApplicationsImportService();\n        }\n        throw new ServiceNotFoundException(calledToolToken);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/server/ConsoleServiceServlet.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.server;\n\nimport org.bonitasoft.web.toolkit.server.servlet.ServiceServlet;\n\n/**\n * @author Séverin Moussel\n */\n@SuppressWarnings(\"serial\")\npublic class ConsoleServiceServlet extends ServiceServlet {\n\n    public ConsoleServiceServlet() {\n        super(new ConsoleServiceFactory());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/server/service/ApplicationsImportService.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.server.service;\n\nimport static org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n.t_;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.ApplicationAPI;\nimport org.bonitasoft.engine.api.ImportStatus;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.business.application.ApplicationImportPolicy;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ExecutionException;\nimport org.bonitasoft.engine.exception.ImportException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.bonitasoft.web.common.model.ImportStatusMessages;\nimport org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n;\nimport org.bonitasoft.web.toolkit.client.common.texttemplate.Arg;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Julien Mege\n * @deprecated as of 9.0.0, Applications should be imported at startup.\n */\n@Deprecated(since = \"9.0.0\")\npublic class ApplicationsImportService extends BonitaImportService {\n\n    public final static String TOKEN = \"/application/import\";\n\n    /**\n     * organization data file\n     */\n    public static final String FILE_UPLOAD = \"applicationsDataUpload\";\n\n    /**\n     * Logger\n     */\n    private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationsImportService.class.getName());\n\n    protected ApplicationAPI getApplicationAPI()\n            throws InvalidSessionException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return TenantAPIAccessor.getLivingApplicationAPI(getSession());\n    }\n\n    @Override\n    public String getToken() {\n        return TOKEN;\n    }\n\n    @Override\n    public String getFileUploadParamName() {\n        return FILE_UPLOAD;\n    }\n\n    @Override\n    public ImportStatusMessages importFileContent(final byte[] fileContent, final String importPolicyAsString)\n            throws ExecutionException, ImportException, AlreadyExistsException, InvalidSessionException,\n            BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        final ApplicationImportPolicy importPolicy = ApplicationImportPolicy.valueOf(importPolicyAsString);\n        final List<ImportStatus> ImportStatusList = getApplicationAPI().importApplications(fileContent, importPolicy);\n        return new ImportStatusMessages(ImportStatusList);\n    }\n\n    @Override\n    protected String getFileFormatExceptionMessage() {\n        return AbstractI18n.t_(\"Can't import Applications.\", getLocale());\n    }\n\n    @Override\n    protected String getAlreadyExistsExceptionMessage(final AlreadyExistsException e) {\n        return t_(\"Can't import applications. An application '%token%' already exists\", getLocale(),\n                new Arg(\"token\", e.getName()));\n    }\n\n    @Override\n    protected String getFileReadingError() {\n        return AbstractI18n.t_(\"Error during Application import file reading.\");\n    }\n\n    @Override\n    protected Logger getLogger() {\n        return LOGGER;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/server/service/BonitaImportService.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.server.service;\n\nimport java.io.InputStream;\n\nimport org.apache.commons.io.IOUtils;\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.engine.exception.*;\nimport org.bonitasoft.engine.io.FileContent;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.bonitasoft.web.common.model.ImportStatusMessages;\nimport org.bonitasoft.web.rest.server.framework.json.JacksonSerializer;\nimport org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n;\nimport org.bonitasoft.web.toolkit.server.ServiceException;\nimport org.slf4j.Logger;\n\n/**\n * Import REST Resources and return the Import Status Messages\n *\n * @author Julien Mege\n */\nabstract class BonitaImportService extends ConsoleService {\n\n    /**\n     * import policy\n     */\n    public static final String IMPORT_POLICY_PARAM_NAME = \"importPolicy\";\n\n    @Override\n    public Object run() {\n        FileContent xmlFile;\n        try {\n            xmlFile = getTenantFolder().retrieveUploadedTempContent(getFileUploadParamValue());\n        } catch (final BonitaException e) {\n            throw new ServiceException(getToken(), e.getMessage(), e);\n        }\n\n        final String importPolicyAsString = getParameter(IMPORT_POLICY_PARAM_NAME);\n\n        try {\n            final JacksonSerializer serializer = new JacksonSerializer();\n            final ImportStatusMessages importStatusMessages = importFileContent(\n                    readImportFile(xmlFile.getInputStream()),\n                    importPolicyAsString);\n            return serializer.serialize(importStatusMessages);\n        } catch (final InvalidSessionException e) {\n            if (getLogger().isInfoEnabled()) {\n                getLogger().info(AbstractI18n.t_(\"Session expired. Please log in again.\"), e);\n            }\n            throw e;\n        } catch (final ExecutionException e) {\n            if (getLogger().isWarnEnabled()) {\n                getLogger().warn(e.getMessage(), e);\n            }\n            throw new ServiceException(getToken(), getFileFormatExceptionMessage(), e);\n        } catch (final ImportException e) {\n            if (getLogger().isWarnEnabled()) {\n                getLogger().warn(e.getMessage(), e);\n            }\n            throw new ServiceException(getToken(), getFileFormatExceptionMessage(), e);\n        } catch (final AlreadyExistsException e) {\n            if (getLogger().isWarnEnabled()) {\n                getLogger().warn(e.getMessage(), e);\n            }\n            throw new ServiceException(getToken(), getAlreadyExistsExceptionMessage(e), e);\n        } catch (final Exception e) {\n            if (getLogger().isErrorEnabled()) {\n                getLogger().error(e.getMessage(), e);\n            }\n            throw new ServiceException(getToken(), e);\n        } finally {\n            getTenantFolder().removeUploadedTempContent(getFileUploadParamValue());\n        }\n    }\n\n    protected byte[] readImportFile(final InputStream xmlInputStream) {\n        try (xmlInputStream) {\n            return IOUtils.toByteArray(xmlInputStream);\n        } catch (final Exception e) {\n            throw new ServiceException(getFileReadingError(), e);\n        }\n    }\n\n    protected BonitaHomeFolderAccessor getTenantFolder() {\n        return new BonitaHomeFolderAccessor();\n    }\n\n    protected String getFileUploadParamValue() {\n        return getParameter(getFileUploadParamName());\n    }\n\n    protected abstract String getFileReadingError();\n\n    protected abstract String getToken();\n\n    protected abstract String getFileUploadParamName();\n\n    protected abstract ImportStatusMessages importFileContent(final byte[] fileContent, final String importPolicy)\n            throws ExecutionException, ImportException, AlreadyExistsException, InvalidSessionException,\n            BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException;\n\n    protected abstract String getFileFormatExceptionMessage();\n\n    protected abstract String getAlreadyExistsExceptionMessage(AlreadyExistsException e);\n\n    protected abstract Logger getLogger();\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/server/service/ConsoleService.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.server.service;\n\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.toolkit.server.Service;\n\n/**\n * @author Séverin Moussel\n */\npublic abstract class ConsoleService extends Service {\n\n    private APISession sessionSingleton = null;\n\n    /**\n     * Get the session\n     */\n    protected final APISession getSession() {\n        if (this.sessionSingleton == null) {\n            this.sessionSingleton = (APISession) getHttpSession().getAttribute(\"apiSession\");\n        }\n        return this.sessionSingleton;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/server/service/OrganizationImportService.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.server.service;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.apache.commons.io.IOUtils;\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.engine.api.IdentityAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.identity.ImportPolicy;\nimport org.bonitasoft.engine.identity.InvalidOrganizationFileFormatException;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n;\nimport org.bonitasoft.web.toolkit.server.ServiceException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Séverin Moussel\n */\npublic class OrganizationImportService extends ConsoleService {\n\n    public static final String TOKEN = \"/organization/import\";\n\n    /**\n     * Logger\n     */\n    private static final Logger LOGGER = LoggerFactory.getLogger(OrganizationImportService.class.getName());\n\n    /**\n     * organization data file\n     */\n    private static final String FILE_UPLOAD = \"organizationDataUpload\";\n\n    /**\n     * import policy\n     */\n    private static final String IMPORT_POLICY_PARAM_NAME = \"importPolicy\";\n\n    @Override\n    public Object run() {\n        final BonitaHomeFolderAccessor tenantFolder = new BonitaHomeFolderAccessor();\n        try {\n            final byte[] organizationContent = getOrganizationContent(tenantFolder);\n            getIdentityAPI().importOrganizationWithWarnings(new String(organizationContent), getImportPolicy());\n        } catch (final InvalidSessionException e) {\n            getHttpResponse().setStatus(HttpServletResponse.SC_UNAUTHORIZED);\n            String message = AbstractI18n.t_(\"Session expired. Please log in again.\");\n            if (LOGGER.isInfoEnabled()) {\n                LOGGER.info(message, e.getMessage());\n            }\n            throw new ServiceException(TOKEN, message, e);\n        } catch (InvalidOrganizationFileFormatException | IllegalArgumentException e) {\n            getHttpResponse().setStatus(HttpServletResponse.SC_BAD_REQUEST);\n            String message = AbstractI18n.t_(\"Can't import organization. Please check that your file is well-formed.\");\n            if (LOGGER.isInfoEnabled()) {\n                LOGGER.info(message, e.getMessage());\n            }\n            throw new ServiceException(TOKEN, message, e);\n        } catch (final Exception e) {\n            if (LOGGER.isErrorEnabled()) {\n                LOGGER.error(e.getMessage(), e);\n            }\n            throw new ServiceException(TOKEN, AbstractI18n.t_(\"Can't import organization\"), e);\n        } finally {\n            cleanTempContent(tenantFolder);\n        }\n        return \"\";\n    }\n\n    private ImportPolicy getImportPolicy() {\n        final String importPolicyAsString = getParameter(IMPORT_POLICY_PARAM_NAME);\n        ImportPolicy importPolicy = ImportPolicy.MERGE_DUPLICATES;\n        if (importPolicyAsString != null) {\n            importPolicy = ImportPolicy.valueOf(importPolicyAsString);\n        }\n        return importPolicy;\n    }\n\n    public byte[] getOrganizationContent(final BonitaHomeFolderAccessor tenantFolder)\n            throws IOException, BonitaException {\n        try (InputStream xmlStream = tenantFolder.retrieveUploadedTempContent(getFileUploadParameter())\n                .getInputStream()) {\n            return IOUtils.toByteArray(xmlStream);\n        }\n    }\n\n    public void cleanTempContent(final BonitaHomeFolderAccessor tenantFolder) {\n        tenantFolder.removeUploadedTempContent(getFileUploadParameter());\n    }\n\n    protected IdentityAPI getIdentityAPI()\n            throws InvalidSessionException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return TenantAPIAccessor.getIdentityAPI(getSession());\n    }\n\n    protected String getFileUploadParameter() {\n        return getParameter(FILE_UPLOAD);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/server/service/ProcessActorImportService.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.server.service;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\nimport org.apache.commons.io.IOUtils;\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.io.FileContent;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.toolkit.server.ServiceException;\n\n/**\n * @author Paul AMAR\n */\npublic class ProcessActorImportService extends ConsoleService {\n\n    public final static String TOKEN = \"/bpm/process/importActors\";\n\n    @Override\n    public Object run() {\n        final BonitaHomeFolderAccessor tenantFolder = new BonitaHomeFolderAccessor();\n        try {\n            final FileContent xmlFile = tenantFolder.retrieveUploadedTempContent(getFileUploadParameter());\n            final APISession apiSession = getSession();\n            final ProcessAPI processAPI = TenantAPIAccessor.getProcessAPI(apiSession);\n            try (InputStream xmlStream = xmlFile.getInputStream()) {\n                final byte[] actorsXmlContent = IOUtils.toByteArray(xmlStream);\n                processAPI.importActorMapping(Long.valueOf(getParameter(\"process_id\")), actorsXmlContent);\n            }\n        } catch (final BonitaException | IOException e) {\n            throw new ServiceException(TOKEN, e.getMessage());\n        } finally {\n            tenantFolder.removeUploadedTempContent(getFileUploadParameter());\n        }\n        return \"\";\n    }\n\n    protected String getFileUploadParameter() {\n        return getParameter(\"file\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/server/servlet/ApplicationsExportServlet.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.server.servlet;\n\nimport org.bonitasoft.engine.api.ApplicationAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ExecutionException;\nimport org.bonitasoft.engine.exception.ExportException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Julien MEGE\n */\npublic class ApplicationsExportServlet extends ExportByIdsServlet {\n\n    /**\n     * export file name\n     */\n    private static final String EXPORT_FILE_NAME = \"applicationDescriptorFile.xml\";\n\n    /**\n     * UID\n     */\n    private static final long serialVersionUID = 1800666571090128789L;\n\n    /**\n     * Logger\n     */\n    private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationsExportServlet.class.getName());\n\n    @Override\n    protected String getFileExportName() {\n        return EXPORT_FILE_NAME;\n    }\n\n    @Override\n    protected byte[] exportResources(final long[] ids, final APISession apiSession) throws BonitaHomeNotSetException,\n            ServerAPIException, UnknownAPITypeException, ExecutionException, ExportException {\n        final ApplicationAPI applicationAPI = getApplicationAPI(apiSession);\n        return applicationAPI.exportApplications(ids);\n    }\n\n    protected ApplicationAPI getApplicationAPI(final APISession apiSession)\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return TenantAPIAccessor.getLivingApplicationAPI(apiSession);\n    }\n\n    @Override\n    protected Logger getLogger() {\n        return LOGGER;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/server/servlet/BonitaExportServlet.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.server.servlet;\n\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.io.UnsupportedEncodingException;\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ExecutionException;\nimport org.bonitasoft.engine.exception.ExportException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.slf4j.Logger;\n\n/**\n * Export Resources as XML file\n *\n * @author Cuisha Gai, Anthony Birembaut\n */\nabstract class BonitaExportServlet extends HttpServlet {\n\n    /**\n     * UID\n     */\n    private static final long serialVersionUID = 1800666571090128789L;\n\n    @Override\n    protected void doGet(final HttpServletRequest request, final HttpServletResponse response)\n            throws ServletException, IOException {\n\n        OutputStream out = null;\n        try {\n            final byte[] resourceBytes = exportResources(request);\n\n            // Set response headers\n            setResponseHeaders(request, response);\n            out = response.getOutputStream();\n            out.write(resourceBytes);\n\n        } catch (final InvalidSessionException e) {\n            String message = \"Session expired. Please log in again.\";\n            if (getLogger().isDebugEnabled()) {\n                getLogger().debug(message, e);\n            }\n            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, message);\n        } catch (final FileNotFoundException e) {\n            String message = \"There is no BDM Access control installed.\";\n            if (getLogger().isInfoEnabled()) {\n                getLogger().info(message);\n            }\n            response.sendError(HttpServletResponse.SC_NOT_FOUND, message);\n        } catch (final Exception e) {\n            if (getLogger().isErrorEnabled()) {\n                getLogger().error(e.getMessage(), e);\n            }\n            throw new ServletException(e.getMessage(), e);\n        }\n    }\n\n    protected void setResponseHeaders(final HttpServletRequest request, final HttpServletResponse response)\n            throws UnsupportedEncodingException {\n        response.setCharacterEncoding(\"UTF-8\");\n        response.setContentType(\"application/octet-stream\");\n        final String encodedfileName = URLEncoder.encode(getFileExportName(), StandardCharsets.UTF_8);\n        final String userAgent = request.getHeader(\"User-Agent\");\n        if (userAgent != null && userAgent.contains(\"Firefox\")) {\n            response.setHeader(\"Content-Disposition\", \"attachment; filename*=UTF-8''\" + encodedfileName);\n        } else {\n            response.setHeader(\"Content-Disposition\",\n                    \"attachment; filename=\\\"\" + encodedfileName.replaceAll(\"\\\\_\", \" \") + \"\\\"; filename*=UTF-8''\"\n                            + encodedfileName);\n        }\n    }\n\n    protected abstract String getFileExportName();\n\n    protected abstract byte[] exportResources(final HttpServletRequest request) throws BonitaHomeNotSetException,\n            ServerAPIException, UnknownAPITypeException, ExportException, FileNotFoundException, ExecutionException;\n\n    protected abstract Logger getLogger();\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/server/servlet/ExportByIdsServlet.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.server.servlet;\n\nimport java.io.FileNotFoundException;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport org.bonitasoft.console.common.server.utils.SessionUtil;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ExecutionException;\nimport org.bonitasoft.engine.exception.ExportException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n;\n\n/**\n * Export Resources with Ids as XML file\n *\n * @author Anthony Birembaut\n */\npublic abstract class ExportByIdsServlet extends BonitaExportServlet {\n\n    private static final long serialVersionUID = -671004787066141408L;\n\n    private static final String RESOURCES_PARAM_KEY = \"id\";\n\n    @Override\n    protected byte[] exportResources(final HttpServletRequest request) throws BonitaHomeNotSetException,\n            ServerAPIException, UnknownAPITypeException, ExportException, FileNotFoundException, ExecutionException {\n        final APISession apiSession = (APISession) request.getSession().getAttribute(SessionUtil.API_SESSION_PARAM_KEY);\n        long[] resourcesIds = getResourcesAsList(request);\n        return exportResources(resourcesIds, apiSession);\n    }\n\n    protected final long[] getResourcesAsList(final HttpServletRequest request) {\n        final String resourceIDParamValue = request.getParameter(RESOURCES_PARAM_KEY);\n        final String[] resourcesIDAsStrings = parseIdsParamValue(resourceIDParamValue);\n        final long[] resourceIDs = new long[resourcesIDAsStrings.length];\n        if (resourcesIDAsStrings != null) {\n            for (int i = 0; i < resourcesIDAsStrings.length; i++) {\n                resourceIDs[i] = Long.valueOf(resourcesIDAsStrings[i]);\n            }\n        }\n        return resourceIDs;\n    }\n\n    private String[] parseIdsParamValue(final String resourceIDParamValue) {\n        if (resourceIDParamValue != null) {\n            return resourceIDParamValue.split(\",\");\n        } else {\n            throw new RuntimeException(AbstractI18n.t_(\"Request parameter \\\"id\\\" must be set.\"));\n        }\n    }\n\n    protected abstract byte[] exportResources(final long[] ids, final APISession apiSession)\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException, ExportException,\n            FileNotFoundException, ExecutionException;\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/server/servlet/OrganizationExportServlet.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.server.servlet;\n\nimport java.io.OutputStream;\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.bonitasoft.console.common.server.utils.SessionUtil;\nimport org.bonitasoft.engine.api.IdentityAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Chong Zhao\n */\npublic class OrganizationExportServlet extends HttpServlet {\n\n    private static final String EXPORT_FILE_NAME = \"Organization_Data.xml\";\n\n    /**\n     * Logger\n     */\n    private static final Logger LOGGER = LoggerFactory.getLogger(OrganizationExportServlet.class.getName());\n\n    /**\n     * UID\n     */\n    private static final long serialVersionUID = 7203686892997001991L;\n\n    @Override\n    protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException {\n\n        final APISession apiSession = (APISession) request.getSession().getAttribute(SessionUtil.API_SESSION_PARAM_KEY);\n        OutputStream out = null;\n        try {\n            final String organizationContent = getIdentityAPI(apiSession).exportOrganization();\n\n            // Set response headers\n            response.setCharacterEncoding(\"UTF-8\");\n            response.setContentType(\"application/octet-stream\");\n            final String encodedfileName = URLEncoder.encode(EXPORT_FILE_NAME, StandardCharsets.UTF_8);\n            final String userAgent = request.getHeader(\"User-Agent\");\n            if (userAgent != null && userAgent.contains(\"Firefox\")) {\n                response.setHeader(\"Content-Disposition\", \"attachment; filename*=UTF-8''\" + encodedfileName);\n            } else {\n                response.setHeader(\"Content-Disposition\",\n                        \"attachment; filename=\\\"\" + encodedfileName.replaceAll(\"\\\\_\", \" \") + \"\\\"; filename*=UTF-8''\"\n                                + encodedfileName);\n            }\n\n            out = response.getOutputStream();\n\n            if (organizationContent == null) {\n                response.setContentLength(0);\n            } else {\n                response.setContentLength(organizationContent.getBytes().length);\n            }\n            out.write(organizationContent.getBytes());\n\n        } catch (final InvalidSessionException e) {\n            final String message = \"Session expired. Please log in again.\";\n            if (LOGGER.isInfoEnabled()) {\n                LOGGER.info(message, e);\n            }\n            throw new ServletException(e.getMessage(), e);\n        } catch (final Exception e) {\n            if (LOGGER.isErrorEnabled()) {\n                LOGGER.error(e.getMessage(), e);\n            }\n            throw new ServletException(e.getMessage(), e);\n        }\n    }\n\n    private IdentityAPI getIdentityAPI(final APISession apiSession)\n            throws InvalidSessionException, BonitaHomeNotSetException, ServerAPIException,\n            UnknownAPITypeException {\n        return TenantAPIAccessor.getIdentityAPI(apiSession);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/console/server/servlet/ProcessActorsExportServlet.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.server.servlet;\n\nimport java.io.OutputStream;\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.bonitasoft.console.common.server.utils.SessionUtil;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Chong Zhao\n */\npublic class ProcessActorsExportServlet extends HttpServlet {\n\n    private static final String EXPORT_FILE_SUFFIX = \"_Process_Actors.xml\";\n\n    private static final String PROCESS_ID = \"processId\";\n\n    /**\n     * Logger\n     */\n    private static final Logger LOGGER = LoggerFactory.getLogger(ProcessActorsExportServlet.class.getName());\n\n    /**\n     * UID\n     */\n    private static final long serialVersionUID = -1463938254928196006L;\n\n    @Override\n    protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException {\n        final String processId = request.getParameter(PROCESS_ID);\n        final APISession apiSession = (APISession) request.getSession().getAttribute(SessionUtil.API_SESSION_PARAM_KEY);\n        OutputStream out = null;\n        try {\n            final ProcessAPI processAPI = TenantAPIAccessor.getProcessAPI(apiSession);\n            final String actorContent = processAPI.exportActorMapping(Long.parseLong(processId));\n\n            // Set response headers\n            response.setCharacterEncoding(\"UTF-8\");\n            response.setContentType(\"application/octet-stream\");\n            final String encodedfileName = URLEncoder.encode(processId + EXPORT_FILE_SUFFIX, StandardCharsets.UTF_8);\n            final String userAgent = request.getHeader(\"User-Agent\");\n            if (userAgent != null && userAgent.contains(\"Firefox\")) {\n                response.setHeader(\"Content-Disposition\", \"attachment; filename*=UTF-8''\" + encodedfileName);\n            } else {\n                response.setHeader(\"Content-Disposition\",\n                        \"attachment; filename=\\\"\" + encodedfileName.replaceAll(\"\\\\_\", \" \") + \"\\\"; filename*=UTF-8''\"\n                                + encodedfileName);\n            }\n\n            out = response.getOutputStream();\n\n            if (actorContent == null) {\n                response.setContentLength(0);\n            } else {\n                response.setContentLength(actorContent.length());\n            }\n            out.write(actorContent.getBytes());\n\n        } catch (final InvalidSessionException e) {\n            final String message = \"Session expires. Please login again.\";\n            if (LOGGER.isErrorEnabled()) {\n                LOGGER.error(message, e);\n            }\n            throw new ServletException(e.getMessage(), e);\n        } catch (final Exception e) {\n            if (LOGGER.isErrorEnabled()) {\n                LOGGER.error(e.getMessage(), e);\n            }\n            throw new ServletException(e.getMessage(), e);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/livingapps/ApplicationModel.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.livingapps;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.ApplicationAPI;\nimport org.bonitasoft.engine.api.PageAPI;\nimport org.bonitasoft.engine.api.ProfileAPI;\nimport org.bonitasoft.engine.business.application.Application;\nimport org.bonitasoft.engine.business.application.ApplicationMenuSearchDescriptor;\nimport org.bonitasoft.engine.business.application.ApplicationPageNotFoundException;\nimport org.bonitasoft.engine.business.application.ApplicationVisibility;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.engine.page.PageNotFoundException;\nimport org.bonitasoft.engine.profile.Profile;\nimport org.bonitasoft.engine.profile.ProfileCriterion;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.livingapps.menu.Menu;\nimport org.bonitasoft.livingapps.menu.MenuFactory;\n\npublic class ApplicationModel {\n\n    private final ProfileAPI profileApi;\n    private final ApplicationAPI applicationApi;\n    private final PageAPI pageApi;\n    private final Application application;\n    private final MenuFactory factory;\n\n    public ApplicationModel(\n            final ApplicationAPI applicationApi,\n            final PageAPI pageApi,\n            final ProfileAPI profileApi,\n            final Application application,\n            final MenuFactory factory) {\n        this.applicationApi = applicationApi;\n        this.pageApi = pageApi;\n        this.profileApi = profileApi;\n        this.application = application;\n        this.factory = factory;\n    }\n\n    public long getId() {\n        return application.getId();\n    }\n\n    public String getApplicationLayoutName() throws PageNotFoundException {\n        return pageApi.getPage(application.getLayoutId()).getName();\n    }\n\n    public String getApplicationThemeName() throws PageNotFoundException {\n        return pageApi.getPage(application.getThemeId()).getName();\n    }\n\n    public Long getApplicationThemeId() throws PageNotFoundException {\n        return pageApi.getPage(application.getThemeId()).getId();\n    }\n\n    public String getApplicationHomePage() throws ApplicationPageNotFoundException {\n        return applicationApi.getApplicationHomePage(application.getId()).getToken() + \"/\";\n    }\n\n    public boolean hasPage(final String pageToken) {\n        try {\n            applicationApi.getApplicationPage(application.getToken(), pageToken);\n            return true;\n        } catch (final ApplicationPageNotFoundException e) {\n            return false;\n        }\n    }\n\n    public boolean authorize(final APISession session) {\n        if (ApplicationVisibility.ALL.equals(application.getVisibility())) {\n            return true;\n        } else if (ApplicationVisibility.TECHNICAL_USER.equals(application.getVisibility())) {\n            return session.isTechnicalUser();\n        } else {\n            for (final Profile userProfile : getUserProfiles(session)) {\n                if (userProfile.getId() == application.getProfileId()) {\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n\n    public boolean hasProfileMapped() {\n        if (ApplicationVisibility.RESTRICTED.equals(application.getVisibility())) {\n            return application.getProfileId() != null;\n        }\n        return true;\n    }\n\n    private List<Profile> getUserProfiles(final APISession session) {\n        return profileApi.getProfilesForUser(\n                session.getUserId(),\n                0,\n                Integer.MAX_VALUE,\n                ProfileCriterion.ID_ASC);\n    }\n\n    public Page getCustomPage(final String pageToken) throws ApplicationPageNotFoundException, PageNotFoundException {\n        return pageApi.getPage(applicationApi.getApplicationPage(application.getToken(), pageToken).getPageId());\n    }\n\n    public List<Menu> getMenuList() throws SearchException, ApplicationPageNotFoundException {\n        return factory.create(applicationApi.searchApplicationMenus(new SearchOptionsBuilder(0, Integer.MAX_VALUE)\n                .filter(ApplicationMenuSearchDescriptor.APPLICATION_ID, application.getId())\n                .sort(ApplicationMenuSearchDescriptor.INDEX, Order.ASC).done())\n                .getResult());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/livingapps/ApplicationModelFactory.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.livingapps;\n\nimport org.bonitasoft.engine.api.ApplicationAPI;\nimport org.bonitasoft.engine.api.PageAPI;\nimport org.bonitasoft.engine.api.ProfileAPI;\nimport org.bonitasoft.engine.business.application.Application;\nimport org.bonitasoft.engine.business.application.ApplicationNotFoundException;\nimport org.bonitasoft.livingapps.exception.CreationException;\nimport org.bonitasoft.livingapps.menu.MenuFactory;\n\npublic class ApplicationModelFactory {\n\n    private final ApplicationAPI applicationApi;\n    private final PageAPI customPageApi;\n    private final ProfileAPI profileApi;\n\n    public ApplicationModelFactory(final ApplicationAPI applicationApi, final PageAPI customPageApi,\n            final ProfileAPI profileApi) {\n        this.applicationApi = applicationApi;\n        this.customPageApi = customPageApi;\n        this.profileApi = profileApi;\n    }\n\n    public ApplicationModel createApplicationModel(final String applicationToken) throws CreationException {\n        try {\n            var application = applicationApi.getIApplicationByToken(applicationToken);\n            if (!(application instanceof Application)) {\n                throw new CreationException(\"Only application links were found with name \" + applicationToken);\n            }\n            return new ApplicationModel(\n                    applicationApi,\n                    customPageApi,\n                    profileApi,\n                    (Application) application,\n                    new MenuFactory(applicationApi));\n        } catch (final ApplicationNotFoundException e) {\n            throw new CreationException(\"Error while searching for the application \" + applicationToken, e);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/livingapps/ApplicationRouter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.livingapps;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.bonitasoft.console.common.server.page.CustomPageRequestModifier;\nimport org.bonitasoft.console.common.server.page.CustomPageService;\nimport org.bonitasoft.console.common.server.page.PageRenderer;\nimport org.bonitasoft.console.common.server.page.ResourceRenderer;\nimport org.bonitasoft.console.common.server.page.extension.PageResourceProviderImpl;\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.page.PageNotFoundException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.livingapps.exception.CreationException;\nimport org.bonitasoft.web.toolkit.client.common.util.StringUtil;\n\npublic class ApplicationRouter {\n\n    private final ApplicationModelFactory applicationModelFactory;\n\n    protected final CustomPageRequestModifier customPageRequestModifier = new CustomPageRequestModifier();\n\n    protected final String THEME_TOKEN = \"theme\";\n\n    public ApplicationRouter(final ApplicationModelFactory applicationModelFactory) {\n        this.applicationModelFactory = applicationModelFactory;\n    }\n\n    public void route(final HttpServletRequest hsRequest, final HttpServletResponse hsResponse,\n            final APISession session, final PageRenderer pageRenderer,\n            final ResourceRenderer resourceRenderer, final BonitaHomeFolderAccessor bonitaHomeFolderAccessor)\n            throws CreationException, BonitaException, IOException, ServletException, IllegalAccessException,\n            InstantiationException {\n\n        final ParsedRequest parsedRequest = parse(hsRequest.getContextPath(), hsRequest.getServletPath(),\n                hsRequest.getPathInfo());\n        //Test if url contain at least application name\n        final List<String> pathSegments = resourceRenderer.getPathSegments(hsRequest.getPathInfo());\n        if (pathSegments.isEmpty()) {\n            hsResponse.sendError(HttpServletResponse.SC_NOT_FOUND,\n                    \"The name of the application is required.\");\n            return;\n        }\n        if (\"API\".equals(parsedRequest.getPageToken())) {\n            //Support relative calls to the REST API from the application page using ../API/\n            String apiPath = \"/\" + getResourcePathWithoutApplicationToken(hsRequest.getPathInfo(),\n                    parsedRequest.getApplicationName());\n            //security check against directory traversal attack\n            customPageRequestModifier.forwardIfRequestIsAuthorized(hsRequest, hsResponse, \"/API\", apiPath);\n        } else if (\"GET\".equals(hsRequest.getMethod())) {\n            displayPageOrResource(hsRequest, hsResponse, session, pageRenderer, resourceRenderer,\n                    bonitaHomeFolderAccessor, parsedRequest, pathSegments);\n        } else {\n            hsResponse.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);\n            hsResponse.flushBuffer();\n        }\n    }\n\n    protected void displayPageOrResource(final HttpServletRequest hsRequest, final HttpServletResponse hsResponse,\n            final APISession session,\n            final PageRenderer pageRenderer, final ResourceRenderer resourceRenderer,\n            final BonitaHomeFolderAccessor bonitaHomeFolderAccessor,\n            final ParsedRequest parsedRequest, final List<String> pathSegments) throws IOException,\n            InstantiationException, IllegalAccessException, BonitaException, CreationException {\n        final ApplicationModel application = applicationModelFactory\n                .createApplicationModel(parsedRequest.getApplicationName());\n\n        if (!application.hasProfileMapped()) {\n            hsResponse.sendError(HttpServletResponse.SC_NOT_FOUND, \"No profile mapped to living application\");\n            return;\n        }\n\n        //If no page name, redirect to Home page\n        if (parsedRequest.getPageToken() == null) {\n            hsResponse.sendRedirect(buildHomePageRouteWithParams(hsRequest, application.getApplicationHomePage()));\n            return;\n        }\n        if (isApplicationPageRequest(pathSegments)) {\n            //Application page request\n            if (!application.hasPage(parsedRequest.getPageToken())) {\n                hsResponse.sendError(HttpServletResponse.SC_NOT_FOUND, \"There is no page \"\n                        + parsedRequest.getPageToken() + \" in the application \" + parsedRequest.getApplicationName());\n                return;\n            }\n            if (!application.authorize(session)) {\n                hsResponse.sendError(HttpServletResponse.SC_FORBIDDEN,\n                        \"Unauthorized access for the page \" + parsedRequest.getPageToken() + \" of the application \"\n                                + parsedRequest.getApplicationName());\n                return;\n            }\n            pageRenderer.displayCustomPage(hsRequest, hsResponse, session, application.getApplicationLayoutName());\n        } else {\n            //Layout or theme resource file request\n            final File resourceFile = getResourceFile(pageRenderer, hsRequest.getPathInfo(), pathSegments, application,\n                    bonitaHomeFolderAccessor);\n            pageRenderer\n                    .ensurePageFolderIsPresent(session,\n                            pageRenderer.getPageResourceProvider(getPageName(pathSegments, application)));\n            resourceRenderer.renderFile(hsRequest, hsResponse, resourceFile);\n        }\n    }\n\n    private String buildHomePageRouteWithParams(final HttpServletRequest hsRequest, final String applicationHomePage) {\n        final StringBuilder routeWithParamsBuilder = new StringBuilder(applicationHomePage);\n        if (!StringUtil.isBlank(hsRequest.getQueryString())) {\n            routeWithParamsBuilder.append(\"?\").append(hsRequest.getQueryString());\n        }\n        return routeWithParamsBuilder.toString();\n    }\n\n    private boolean isApplicationPageRequest(final List<String> pathSegments) {\n        return pathSegments.size() == 2;\n    }\n\n    private File getResourceFile(final PageRenderer pageRenderer, final String resourcePath,\n            final List<String> pathSegments,\n            final ApplicationModel application, final BonitaHomeFolderAccessor bonitaHomeFolderAccessor)\n            throws IOException,\n            BonitaException {\n        final String pageName = getPageName(pathSegments, application);\n        final PageResourceProviderImpl pageResourceProvider = pageRenderer.getPageResourceProvider(pageName);\n        final File resourceFile = new File(pageResourceProvider.getPageDirectory(),\n                CustomPageService.RESOURCES_PROPERTY + File.separator\n                        + getResourcePath(resourcePath, pathSegments.get(0), pathSegments.get(1)));\n\n        if (!bonitaHomeFolderAccessor.isInFolder(resourceFile, pageResourceProvider.getPageDirectory())) {\n            throw new BonitaException(\"Unauthorized access to the file \" + resourcePath);\n        }\n        return resourceFile;\n    }\n\n    private String getPageName(final List<String> pathSegments, final ApplicationModel application)\n            throws PageNotFoundException {\n        String pageName;\n        if (THEME_TOKEN.equals(pathSegments.get(1))) {\n            pageName = application.getApplicationThemeName();\n        } else {\n            pageName = application.getApplicationLayoutName();\n        }\n        return pageName;\n    }\n\n    private String getResourcePath(final String fullResourcePath, final String applicationName,\n            final String pageToken) {\n        //resource path match \"/applicationName/pageName/{resourcePath}\"\n        // or \"/applicationName/theme/{resourcePath}\"\n        String resourcePath = getResourcePathWithoutApplicationToken(fullResourcePath, applicationName);\n        resourcePath = getResourcePathWithoutPageToken(resourcePath, pageToken);\n\n        return resourcePath;\n    }\n\n    private String getResourcePathWithoutApplicationToken(final String resourcePath, final String applicationName) {\n        //resource path match \"/applicationName/{resourcePath}\"\n        return resourcePath.substring(applicationName.length() + 2);\n    }\n\n    private String getResourcePathWithoutPageToken(final String resourcePath, final String pageToken) {\n        return resourcePath.substring(pageToken.length() + 1);\n    }\n\n    private ParsedRequest parse(final String context, final String servletPath, final String pathInfo) {\n        final Pattern pattern = Pattern.compile(\"^\" + context + \"/apps/(.*)$\");\n        final Matcher matcher = pattern.matcher(context + servletPath + pathInfo);\n        if (!matcher.find()) {\n            throw new RuntimeException(\"URI badly formed.\");\n        }\n        final String[] fragments = matcher.group(1).split(\"/\");\n        String pageToken = null;\n        if (fragments.length > 1) {\n            pageToken = fragments[1];\n        }\n        return new ParsedRequest(fragments[0], pageToken);\n    }\n\n    private class ParsedRequest {\n\n        private final String applicationToken;\n\n        private final String pageToken;\n\n        public ParsedRequest(final String applicationToken, final String pageToken) {\n            this.applicationToken = applicationToken;\n            this.pageToken = pageToken;\n        }\n\n        public String getApplicationName() {\n            return applicationToken;\n        }\n\n        public String getPageToken() {\n            return pageToken;\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/livingapps/LivingApplicationPageServlet.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.livingapps;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.List;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpSession;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.bonitasoft.console.common.server.page.ApplicationAuthorizationsHelper;\nimport org.bonitasoft.console.common.server.page.CustomPageRequestModifier;\nimport org.bonitasoft.console.common.server.page.CustomPageService;\nimport org.bonitasoft.console.common.server.page.PageRenderer;\nimport org.bonitasoft.console.common.server.page.ResourceRenderer;\nimport org.bonitasoft.console.common.server.page.extension.PageResourceProviderImpl;\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.console.common.server.utils.SessionUtil;\nimport org.bonitasoft.engine.api.ApplicationAPI;\nimport org.bonitasoft.engine.api.PageAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.NotFoundException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Servlet which displays an application page (iframe below the layout menu) with an URL like\n * /portal/resource/app/<app-token>/<application-page-token>/content/\n * Note: whenever there is an InvalidSessionException from the engine it performs an HTTP session logout and send a 401\n * error. since we are in a iframe, we cannot redirect to the login page.\n * However, the page should handle the 401 and refresh the top window.\n */\npublic class LivingApplicationPageServlet extends HttpServlet {\n\n    public static final String RESOURCE_PATH_SEPARATOR = \"/content\";\n    public static final String API_PATH_SEPARATOR = \"/API\";\n    public static final String THEME_PATH_SEPARATOR = \"/theme\";\n    /**\n     * uuid\n     */\n    private static final long serialVersionUID = -5410859017103815654L;\n    /**\n     * Logger\n     */\n    private static Logger LOGGER = LoggerFactory.getLogger(LivingApplicationPageServlet.class.getName());\n\n    protected ResourceRenderer resourceRenderer = new ResourceRenderer();\n\n    protected PageRenderer pageRenderer = new PageRenderer(resourceRenderer);\n\n    protected BonitaHomeFolderAccessor bonitaHomeFolderAccessor = new BonitaHomeFolderAccessor();\n\n    protected CustomPageRequestModifier customPageRequestModifier = new CustomPageRequestModifier();\n\n    @Override\n    protected void service(final HttpServletRequest request, final HttpServletResponse response)\n            throws ServletException, IOException {\n        final String pathInfo = request.getPathInfo();\n        final List<String> pathSegments = resourceRenderer.getPathSegments(pathInfo);\n        if (isValidPathForToken(API_PATH_SEPARATOR, pathSegments)) {\n            //Support relative calls to the REST API from the forms using ../API/\n            final String apiPath = pathInfo.substring(pathInfo.indexOf(API_PATH_SEPARATOR + \"/\"));\n            //security check against directory traversal attack\n            customPageRequestModifier.forwardIfRequestIsAuthorized(request, response, API_PATH_SEPARATOR, apiPath);\n        } else {\n            super.service(request, response);\n        }\n    }\n\n    @Override\n    protected void doGet(final HttpServletRequest request, final HttpServletResponse response)\n            throws ServletException, IOException {\n\n        final String pathInfo = request.getPathInfo();\n        final HttpSession session = request.getSession();\n        final APISession apiSession = (APISession) session.getAttribute(SessionUtil.API_SESSION_PARAM_KEY);\n\n        // Check if requested URL is missing final slash (necessary in order to be able to use relative URLs for resources)\n        if (pathInfo.endsWith(RESOURCE_PATH_SEPARATOR)) {\n            customPageRequestModifier.redirectToValidPageUrl(request, response);\n            return;\n        }\n\n        String appToken = null;\n        String pageToken = null;\n        String customPageName = null;\n        String resourcePath = null;\n\n        //Validate mapping key contain \"AppToken/PageToken/\" and at least one more segment\n        final List<String> pathSegments = resourceRenderer.getPathSegments(pathInfo);\n        if (pathSegments.size() >= 3) {\n            appToken = pathSegments.get(0);\n            pageToken = pathSegments.get(1);\n\n            if (isValidPathForToken(RESOURCE_PATH_SEPARATOR, pathSegments)) {\n                final String pageMapping = \"/\" + appToken + \"/\" + pageToken + RESOURCE_PATH_SEPARATOR + \"/\";\n                if (pathInfo.length() > pageMapping.length()) {\n                    resourcePath = pathInfo.substring(pageMapping.length());\n                }\n                boolean isNotResourcePath = isNotResourcePath(resourcePath);\n                customPageName = getCustomPageName(appToken, pageToken, apiSession, request, response,\n                        isNotResourcePath);\n                if (StringUtils.isBlank(customPageName)) {\n                    if (LOGGER.isWarnEnabled()) {\n                        LOGGER.warn(\"Error while trying to retrieve the application page\");\n                    }\n                    //the response status is set in the error handling of getCustomPageName\n                    return;\n                }\n                try {\n                    if (isAuthorized(apiSession, appToken, customPageName)) {\n                        if (isNotResourcePath) {\n                            pageRenderer.displayCustomPage(request, response, apiSession, customPageName);\n                        } else {\n                            final File resourceFile = getResourceFile(resourcePath, customPageName);\n                            pageRenderer.ensurePageFolderIsPresent(apiSession,\n                                    pageRenderer.getPageResourceProvider(customPageName));\n                            resourceRenderer.renderFile(request, response, resourceFile);\n                        }\n                    } else {\n                        if (isNotResourcePath) {\n                            response.sendError(HttpServletResponse.SC_FORBIDDEN, \"User not Authorized\");\n                        } else {\n                            response.setStatus(HttpServletResponse.SC_FORBIDDEN);\n                            response.flushBuffer();\n                        }\n                    }\n                } catch (final Exception e) {\n                    handleException(customPageName, e, request, response, isNotResourcePath);\n                }\n            } else if (isValidPathForToken(THEME_PATH_SEPARATOR, pathSegments)) {\n                //Support relative calls to the THEME from the application page using ../theme/\n                final String themeResourcePath = pathInfo.substring(pathInfo.indexOf(THEME_PATH_SEPARATOR + \"/\"));\n                String appThemeResourcePrefix = \"/apps/\" + appToken;\n                customPageRequestModifier.forwardIfRequestIsAuthorized(request, response,\n                        appThemeResourcePrefix + THEME_PATH_SEPARATOR + \"/\",\n                        appThemeResourcePrefix + themeResourcePath);\n            } else {\n                if (LOGGER.isTraceEnabled()) {\n                    LOGGER.trace(\n                            \"One of the separator '/content', '/theme' or '/API' is expected in the URL after the application token and the page token.\");\n                }\n                response.setStatus(HttpServletResponse.SC_BAD_REQUEST);\n                response.flushBuffer();\n            }\n\n        } else {\n            if (LOGGER.isTraceEnabled()) {\n                LOGGER.trace(\n                        \"The info path is suppose to contain the application token, the page token and one of the separator '/content', '/theme' or '/API'.\");\n            }\n            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);\n            response.flushBuffer();\n        }\n\n    }\n\n    private String getCustomPageName(final String appToken, final String pageToken, final APISession apiSession,\n            final HttpServletRequest request, final HttpServletResponse response, final boolean isNotResourcePath)\n            throws ServletException, IOException {\n        try {\n            final Long customPageId = getApplicationApi(apiSession).getApplicationPage(appToken, pageToken).getPageId();\n            return getPageApi(apiSession).getPage(customPageId).getName();\n        } catch (final NotFoundException e) {\n            if (LOGGER.isInfoEnabled()) {\n                LOGGER.info(\"The application page \" + appToken + \"/\" + pageToken + \" was not found.\");\n            }\n            if (isNotResourcePath) {\n                response.sendError(HttpServletResponse.SC_NOT_FOUND,\n                        \"Cannot find the page\" + pageToken + \"for the application\" + appToken + \".\");\n            } else {\n                response.setStatus(HttpServletResponse.SC_NOT_FOUND);\n                response.flushBuffer();\n            }\n        } catch (final Exception e) {\n            handleException(appToken + \"/\" + pageToken, e, request, response, isNotResourcePath);\n        }\n        return \"\";\n    }\n\n    private boolean isValidPathForToken(final String TokenSeparator, final List<String> pathSegments) {\n        return pathSegments.size() > 2 && pathSegments.get(2).equals(TokenSeparator.substring(1));\n    }\n\n    private boolean isNotResourcePath(final String resourcePath) {\n        return resourcePath == null || CustomPageService.PAGE_INDEX_FILENAME.equals(resourcePath)\n                || CustomPageService.PAGE_CONTROLLER_FILENAME.equals(resourcePath)\n                || CustomPageService.PAGE_INDEX_NAME.equals(resourcePath);\n    }\n\n    private File getResourceFile(final String resourcePath, final String pageName) throws IOException, BonitaException {\n        final PageResourceProviderImpl pageResourceProvider = pageRenderer.getPageResourceProvider(pageName);\n        final File resourceFile = new File(pageResourceProvider.getPageDirectory(),\n                CustomPageService.RESOURCES_PROPERTY + File.separator\n                        + resourcePath);\n\n        if (!bonitaHomeFolderAccessor.isInFolder(resourceFile, pageResourceProvider.getPageDirectory())) {\n            throw new BonitaException(\"Unauthorized access to the file \" + resourcePath);\n        }\n        return resourceFile;\n    }\n\n    private boolean isAuthorized(final APISession apiSession, final String appToken, final String pageName)\n            throws BonitaException {\n        return getCustomPageAuthorizationsHelper(apiSession).isAuthorized(appToken);\n    }\n\n    private void handleException(final String pageName, final Exception e, final HttpServletRequest request,\n            final HttpServletResponse response, final boolean isNotResourcePath) throws ServletException, IOException {\n        if (e instanceof InvalidSessionException) {\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(\"Invalid Bonita engine session.\", e);\n            }\n            SessionUtil.sessionLogout(request.getSession());\n            if (isNotResourcePath) {\n                response.sendError(HttpServletResponse.SC_UNAUTHORIZED, \"Invalid Bonita engine session.\");\n            } else {\n                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);\n                response.flushBuffer();\n            }\n        } else {\n            if (LOGGER.isWarnEnabled()) {\n                LOGGER.warn(\"Error while trying to render the application page \" + pageName, e);\n            }\n            throw new ServletException(e.getMessage());\n        }\n    }\n\n    protected ApplicationAPI getApplicationApi(final APISession apiSession)\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return TenantAPIAccessor.getLivingApplicationAPI(apiSession);\n    }\n\n    protected PageAPI getPageApi(final APISession apiSession)\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return TenantAPIAccessor.getCustomPageAPI(apiSession);\n    }\n\n    protected ApplicationAuthorizationsHelper getCustomPageAuthorizationsHelper(final APISession apiSession)\n            throws BonitaHomeNotSetException,\n            ServerAPIException, UnknownAPITypeException {\n        return new ApplicationAuthorizationsHelper(apiSession,\n                new ApplicationModelFactory(\n                        TenantAPIAccessor.getLivingApplicationAPI(apiSession),\n                        TenantAPIAccessor.getCustomPageAPI(apiSession),\n                        TenantAPIAccessor.getProfileAPI(apiSession)));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/livingapps/LivingApplicationServlet.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.livingapps;\n\nimport java.io.IOException;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.bonitasoft.console.common.server.auth.AuthenticationManager;\nimport org.bonitasoft.console.common.server.auth.AuthenticationManagerFactory;\nimport org.bonitasoft.console.common.server.auth.AuthenticationManagerNotFoundException;\nimport org.bonitasoft.console.common.server.login.HttpServletRequestAccessor;\nimport org.bonitasoft.console.common.server.login.utils.LoginUrl;\nimport org.bonitasoft.console.common.server.login.utils.RedirectUrl;\nimport org.bonitasoft.console.common.server.login.utils.RedirectUrlBuilder;\nimport org.bonitasoft.console.common.server.page.CustomPageRequestModifier;\nimport org.bonitasoft.console.common.server.page.PageRenderer;\nimport org.bonitasoft.console.common.server.page.ResourceRenderer;\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.console.common.server.utils.SessionUtil;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.business.application.ApplicationPageNotFoundException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.page.PageNotFoundException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.bonitasoft.livingapps.exception.CreationException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Servlet which displays the application layout with an URL like /apps/<app-token>/*\n * Note: whenever there is an InvalidSessionException from the engine it performs an HTTP session logout and redirects\n * to the login page (this is possible because unlike in LivingApplicationPageServlet we are in the top frame).\n */\npublic class LivingApplicationServlet extends HttpServlet {\n\n    private static final long serialVersionUID = -3911437607969651000L;\n\n    /**\n     * Logger\n     */\n    private static final Logger LOGGER = LoggerFactory.getLogger(LivingApplicationServlet.class.getName());\n\n    protected CustomPageRequestModifier customPageRequestModifier = new CustomPageRequestModifier();\n\n    @Override\n    protected void service(final HttpServletRequest hsRequest, final HttpServletResponse hsResponse)\n            throws ServletException, IOException {\n\n        final APISession session = getSession(hsRequest);\n\n        // Check if requested URL is missing final slash (necessary in order to be able to use relative URLs for resources)\n        if (isPageUrlWithoutFinalSlash(hsRequest)) {\n            customPageRequestModifier.redirectToValidPageUrl(hsRequest, hsResponse);\n            return;\n        }\n\n        try {\n            createApplicationRouter(session).route(hsRequest, hsResponse, session, getPageRenderer(),\n                    getResourceRenderer(), new BonitaHomeFolderAccessor());\n        } catch (final ApplicationPageNotFoundException | PageNotFoundException | CreationException e) {\n            hsResponse.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());\n        } catch (final BonitaException | IllegalAccessException | InstantiationException e) {\n            if (LOGGER.isWarnEnabled()) {\n                final String message = \"Error while trying to display application \" + hsRequest.getPathInfo();\n                LOGGER.warn(message, e);\n            }\n            if (!hsResponse.isCommitted()) {\n                hsResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);\n            } else {\n                throw new ServletException(e);\n            }\n        } catch (final InvalidSessionException e) {\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(\"Invalid Bonita engine session.\", e);\n            }\n            SessionUtil.sessionLogout(hsRequest.getSession());\n            HttpServletRequestAccessor requestAccessor = new HttpServletRequestAccessor(hsRequest);\n            LoginUrl loginURL = new LoginUrl(getAuthenticationManager(), makeRedirectUrl(requestAccessor).getUrl(),\n                    requestAccessor);\n            hsResponse.sendRedirect(loginURL.getLocation());\n        }\n\n    }\n\n    ApplicationRouter createApplicationRouter(final APISession session)\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return new ApplicationRouter(new ApplicationModelFactory(\n                TenantAPIAccessor.getLivingApplicationAPI(session),\n                TenantAPIAccessor.getCustomPageAPI(session),\n                TenantAPIAccessor.getProfileAPI(session)));\n    }\n\n    private boolean isPageUrlWithoutFinalSlash(final HttpServletRequest request) {\n        return request.getPathInfo() == null\n                || request.getPathInfo().matches(\"/[^/]+/[^/]+\")\n                || request.getPathInfo().matches(\"/[^/]+\");\n    }\n\n    APISession getSession(final HttpServletRequest hsRequest) {\n        return (APISession) hsRequest.getSession().getAttribute(\"apiSession\");\n    }\n\n    PageRenderer getPageRenderer() {\n        return new PageRenderer(getResourceRenderer());\n    }\n\n    ResourceRenderer getResourceRenderer() {\n        return new ResourceRenderer();\n    }\n\n    protected RedirectUrl makeRedirectUrl(final HttpServletRequestAccessor httpRequest) {\n        final RedirectUrlBuilder builder = new RedirectUrlBuilder(httpRequest.getRequestedUri());\n        builder.appendParameters(httpRequest.getParameterMap());\n        return builder.build();\n    }\n\n    // protected for test stubbing\n    protected AuthenticationManager getAuthenticationManager() throws ServletException {\n        try {\n            return AuthenticationManagerFactory.getAuthenticationManager();\n        } catch (final AuthenticationManagerNotFoundException e) {\n            throw new ServletException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/livingapps/exception/CreationException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.livingapps.exception;\n\npublic class CreationException extends Exception {\n\n    public CreationException(String message, Throwable throwable) {\n        super(message, throwable);\n    }\n\n    public CreationException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/livingapps/menu/ChildrenMenuCollector.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.livingapps.menu;\n\nimport org.bonitasoft.engine.business.application.ApplicationMenu;\nimport org.bonitasoft.livingapps.menu.MenuFactory.Collector;\n\nclass ChildrenMenuCollector implements Collector {\n\n    private final Long parentId;\n\n    public ChildrenMenuCollector(final Long parentId) {\n        this.parentId = parentId;\n    }\n\n    @Override\n    public boolean isCollectible(final ApplicationMenu menu) {\n        return parentId.equals(menu.getParentId());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/livingapps/menu/Menu.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.livingapps.menu;\n\npublic interface Menu {\n\n    String getHtml();\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/livingapps/menu/MenuContainer.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.livingapps.menu;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.business.application.ApplicationMenu;\n\npublic class MenuContainer implements Menu {\n\n    private final ApplicationMenu menu;\n\n    private final List<Menu> children;\n\n    public MenuContainer(final ApplicationMenu menu, final List<Menu> children) {\n        this.menu = menu;\n        this.children = children;\n    }\n\n    @Override\n    public String getHtml() {\n        final StringBuilder builder = new StringBuilder()\n                .append(\"<li class=\\\"dropdown\\\">\")\n                .append(\"<a href=\\\"#\\\" class=\\\"dropdown-toggle\\\" data-toggle=\\\"dropdown\\\">\")\n                .append(menu.getDisplayName()).append(\" <span class=\\\"caret\\\"></span></a>\")\n                .append(\"<ul class=\\\"dropdown-menu\\\" role=\\\"menu\\\">\");\n        for (final Menu child : children) {\n            builder.append(child.getHtml());\n        }\n        return builder.append(\"</ul></li>\").toString();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/livingapps/menu/MenuFactory.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.livingapps.menu;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.ApplicationAPI;\nimport org.bonitasoft.engine.business.application.ApplicationMenu;\nimport org.bonitasoft.engine.business.application.ApplicationPageNotFoundException;\nimport org.bonitasoft.engine.exception.SearchException;\n\npublic class MenuFactory {\n\n    public interface Collector {\n\n        boolean isCollectible(ApplicationMenu item);\n    }\n\n    private final ApplicationAPI applicationApi;\n\n    public MenuFactory(final ApplicationAPI applicationApi) {\n        this.applicationApi = applicationApi;\n    }\n\n    public List<Menu> create(final List<ApplicationMenu> menuList)\n            throws ApplicationPageNotFoundException, SearchException {\n        return collect(menuList, new RootMenuCollector());\n    }\n\n    private Menu create(final ApplicationMenu menu, final List<ApplicationMenu> menuList)\n            throws ApplicationPageNotFoundException, SearchException {\n        if (menu.getApplicationPageId() == null) {\n            return new MenuContainer(menu,\n                    collect(menuList, new ChildrenMenuCollector(menu.getId())));\n        }\n        return new MenuLink(menu, applicationApi.getApplicationPage(menu.getApplicationPageId()).getToken());\n    }\n\n    private List<Menu> collect(final List<ApplicationMenu> items, final Collector collector)\n            throws ApplicationPageNotFoundException, SearchException {\n        final List<Menu> menuList = new ArrayList<>();\n        for (final ApplicationMenu item : items) {\n            if (collector.isCollectible(item)) {\n                menuList.add(create(item, items));\n            }\n        }\n        return menuList;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/livingapps/menu/MenuLink.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.livingapps.menu;\n\nimport org.bonitasoft.engine.business.application.ApplicationMenu;\n\npublic class MenuLink implements Menu {\n\n    private final ApplicationMenu menu;\n    private final String pageToken;\n\n    public MenuLink(final ApplicationMenu menu, final String pageToken) {\n        this.menu = menu;\n        this.pageToken = pageToken;\n    }\n\n    @Override\n    public String getHtml() {\n        return \"<li><a href=\\\"\" + pageToken + \"\\\">\" + menu.getDisplayName() + \"</a></li>\";\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/livingapps/menu/RootMenuCollector.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.livingapps.menu;\n\nimport org.bonitasoft.engine.business.application.ApplicationMenu;\nimport org.bonitasoft.livingapps.menu.MenuFactory.Collector;\n\nclass RootMenuCollector implements Collector {\n\n    @Override\n    public boolean isCollectible(final ApplicationMenu menu) {\n        return menu.getParentId() == null;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/common/model/ImportStatusMessage.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.common.model;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @author Fabio Lombardi\n */\npublic class ImportStatusMessage implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    private Map<String, List<String>> errors;\n\n    private String name;\n\n    private final String statusType;\n\n    public ImportStatusMessage(final String name, final String statusType) {\n        errors = new HashMap<>();\n        this.name = name;\n        this.statusType = statusType;\n    }\n\n    public void addError(final String elementType, final String errorMessage) {\n        if (!errors.containsKey(elementType)) {\n            errors.put(elementType, new ArrayList<>());\n        }\n        errors.get(elementType).add(errorMessage);\n    }\n\n    public void setName(final String name) {\n        this.name = name;\n    }\n\n    public Map<String, List<String>> getErrors() {\n        return errors;\n    }\n\n    public void setErrors(final Map<String, List<String>> errors) {\n        this.errors = errors;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public String getStatusType() {\n        return statusType;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/common/model/ImportStatusMessages.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.common.model;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.ImportError;\nimport org.bonitasoft.engine.api.ImportStatus;\n\n/**\n * @author Fabio Lombardi\n */\npublic class ImportStatusMessages implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    private final List<ImportStatusMessage> errors = new ArrayList<>();\n\n    private final List<ImportStatusMessage> imported = new ArrayList<>();\n\n    private final List<ImportStatusMessage> skipped = new ArrayList<>();\n\n    public ImportStatusMessages(final List<ImportStatus> statusMessages) {\n        setImportStatus(statusMessages);\n    }\n\n    public void addResourceImported(final ImportStatusMessage profileImportStatusMessage) {\n        imported.add(profileImportStatusMessage);\n    }\n\n    public void addResourceSkipped(final ImportStatusMessage profileImportStatusMessage) {\n        skipped.add(profileImportStatusMessage);\n    }\n\n    public void addResourceInError(final ImportStatusMessage profileImportStatusMessage) {\n        errors.add(profileImportStatusMessage);\n    }\n\n    public List<ImportStatusMessage> getErrors() {\n        return errors;\n    }\n\n    public List<ImportStatusMessage> getImported() {\n        return imported;\n    }\n\n    public List<ImportStatusMessage> getSkipped() {\n        return skipped;\n    }\n\n    public void setImportStatus(final List<ImportStatus> statusMessages) {\n\n        for (final ImportStatus statusMessage : statusMessages) {\n            if (statusMessage.getErrors().size() > 0) {\n                addResourceInError(convertImportStatus(statusMessage));\n            } else if (statusMessage.getStatus().equals(ImportStatus.Status.REPLACED)\n                    || statusMessage.getStatus().equals(ImportStatus.Status.ADDED)) {\n                addResourceImported(convertImportStatus(statusMessage));\n            } else if (statusMessage.getStatus().equals(ImportStatus.Status.SKIPPED)) {\n                addResourceSkipped(convertImportStatus(statusMessage));\n            }\n        }\n    }\n\n    private ImportStatusMessage convertImportStatus(final ImportStatus importStatus) {\n        final ImportStatusMessage importStatusMessage = new ImportStatusMessage(importStatus.getName(),\n                importStatus.getStatus().toString());\n        final List<ImportError> errors = importStatus.getErrors();\n        for (final ImportError error : errors) {\n            importStatusMessage.addError(error.getType().toString(), error.getName());\n        }\n        return importStatusMessage;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/ModelFactory.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model;\n\nimport org.bonitasoft.web.rest.model.application.AbstractApplicationDefinition;\nimport org.bonitasoft.web.rest.model.application.AbstractApplicationItem;\nimport org.bonitasoft.web.rest.model.application.ApplicationDefinition;\nimport org.bonitasoft.web.rest.model.application.ApplicationLinkDefinition;\nimport org.bonitasoft.web.rest.model.applicationmenu.ApplicationMenuDefinition;\nimport org.bonitasoft.web.rest.model.applicationpage.ApplicationPageDefinition;\nimport org.bonitasoft.web.rest.model.bpm.cases.ArchivedCaseDefinition;\nimport org.bonitasoft.web.rest.model.bpm.cases.ArchivedCaseDocumentDefinition;\nimport org.bonitasoft.web.rest.model.bpm.cases.ArchivedCommentDefinition;\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseDefinition;\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseDocumentDefinition;\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseVariableDefinition;\nimport org.bonitasoft.web.rest.model.bpm.cases.CommentDefinition;\nimport org.bonitasoft.web.rest.model.bpm.connector.ArchivedConnectorInstanceDefinition;\nimport org.bonitasoft.web.rest.model.bpm.connector.ConnectorInstanceDefinition;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ActivityDefinition;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedActivityDefinition;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedFlowNodeDefinition;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedHumanTaskDefinition;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedTaskDefinition;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedUserTaskDefinition;\nimport org.bonitasoft.web.rest.model.bpm.flownode.FlowNodeDefinition;\nimport org.bonitasoft.web.rest.model.bpm.flownode.HumanTaskDefinition;\nimport org.bonitasoft.web.rest.model.bpm.flownode.TaskDefinition;\nimport org.bonitasoft.web.rest.model.bpm.flownode.UserTaskDefinition;\nimport org.bonitasoft.web.rest.model.bpm.process.ActorDefinition;\nimport org.bonitasoft.web.rest.model.bpm.process.ActorMemberDefinition;\nimport org.bonitasoft.web.rest.model.bpm.process.CategoryDefinition;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessCategoryDefinition;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDefinition;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDependencyDefinition;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessDefinition;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessParameterDefinition;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessResolutionProblemDefinition;\nimport org.bonitasoft.web.rest.model.document.ArchivedDocumentDefinition;\nimport org.bonitasoft.web.rest.model.document.DocumentDefinition;\nimport org.bonitasoft.web.rest.model.identity.CustomUserInfoDefinition;\nimport org.bonitasoft.web.rest.model.identity.CustomUserInfoDefinitionDefinition;\nimport org.bonitasoft.web.rest.model.identity.CustomUserInfoValueDefinition;\nimport org.bonitasoft.web.rest.model.identity.GroupDefinition;\nimport org.bonitasoft.web.rest.model.identity.MembershipDefinition;\nimport org.bonitasoft.web.rest.model.identity.PersonalContactDataDefinition;\nimport org.bonitasoft.web.rest.model.identity.ProfessionalContactDataDefinition;\nimport org.bonitasoft.web.rest.model.identity.RoleDefinition;\nimport org.bonitasoft.web.rest.model.identity.UserDefinition;\nimport org.bonitasoft.web.rest.model.platform.PlatformDefinition;\nimport org.bonitasoft.web.rest.model.portal.page.PageDefinition;\nimport org.bonitasoft.web.rest.model.portal.profile.ProfileDefinition;\nimport org.bonitasoft.web.rest.model.portal.profile.ProfileMemberDefinition;\nimport org.bonitasoft.web.rest.model.tenant.BusinessDataModelDefinition;\nimport org.bonitasoft.web.toolkit.client.ItemDefinitionFactory;\nimport org.bonitasoft.web.toolkit.client.common.session.SessionDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Yongtao Guo\n */\npublic class ModelFactory extends ItemDefinitionFactory {\n\n    @Override\n    public ItemDefinition<?> defineItemDefinitions(final String token) {\n\n        // organization\n        if (UserDefinition.TOKEN.equals(token)) {\n            return new UserDefinition();\n        } else if (PersonalContactDataDefinition.TOKEN.equals(token)) {\n            return new PersonalContactDataDefinition();\n        } else if (ProfessionalContactDataDefinition.TOKEN.equals(token)) {\n            return new ProfessionalContactDataDefinition();\n        } else if (RoleDefinition.TOKEN.equals(token)) {\n            return new RoleDefinition();\n        } else if (GroupDefinition.TOKEN.equals(token)) {\n            return new GroupDefinition();\n        } else if (MembershipDefinition.TOKEN.equals(token)) {\n            return new MembershipDefinition();\n        } else if (CustomUserInfoDefinition.TOKEN.equals(token)) {\n            return new CustomUserInfoDefinition();\n        } else if (CustomUserInfoDefinitionDefinition.TOKEN.equals(token)) {\n            return new CustomUserInfoDefinitionDefinition();\n        } else if (CustomUserInfoValueDefinition.TOKEN.equals(token)) {\n            return new CustomUserInfoValueDefinition();\n        }\n\n        // bpm.process\n        else if (ProcessDefinition.TOKEN.equals(token)) {\n            return new ProcessDefinition();\n        } else if (ProcessConnectorDefinition.TOKEN.equals(token)) {\n            return new ProcessConnectorDefinition();\n        } else if (ProcessConnectorDependencyDefinition.TOKEN.equals(token)) {\n            return new ProcessConnectorDependencyDefinition();\n        } else if (ProcessCategoryDefinition.TOKEN.equals(token)) {\n            return new ProcessCategoryDefinition();\n        } else if (ActorDefinition.TOKEN.equals(token)) {\n            return new ActorDefinition();\n        } else if (ActorMemberDefinition.TOKEN.equals(token)) {\n            return new ActorMemberDefinition();\n        } else if (CategoryDefinition.TOKEN.equals(token)) {\n            return new CategoryDefinition();\n        } else if (ProcessResolutionProblemDefinition.TOKEN.equals(token)) {\n            return new ProcessResolutionProblemDefinition();\n        } else if (ProcessParameterDefinition.TOKEN.equals(token)) {\n            return new ProcessParameterDefinition();\n        }\n\n        // bpm.cases\n        else if (CaseDefinition.TOKEN.equals(token)) {\n            return new CaseDefinition();\n        } else if (CommentDefinition.TOKEN.equals(token)) {\n            return new CommentDefinition();\n        } else if (ArchivedCommentDefinition.TOKEN.equals(token)) {\n            return new ArchivedCommentDefinition();\n        } else if (ArchivedCaseDefinition.TOKEN.equals(token)) {\n            return new ArchivedCaseDefinition();\n        } else if (CaseVariableDefinition.TOKEN.equals(token)) {\n            return new CaseVariableDefinition();\n        } else if (CaseDocumentDefinition.TOKEN.equals(token)) {\n            return new CaseDocumentDefinition();\n        } else if (ArchivedCaseDocumentDefinition.TOKEN.equals(token)) {\n            return new CaseDocumentDefinition();\n        }\n\n        // bpm.flownode\n        else if (FlowNodeDefinition.TOKEN.equals(token)) {\n            return new FlowNodeDefinition();\n        } else if (ActivityDefinition.TOKEN.equals(token)) {\n            return new ActivityDefinition();\n        } else if (TaskDefinition.TOKEN.equals(token)) {\n            return new TaskDefinition();\n        } else if (HumanTaskDefinition.TOKEN.equals(token)) {\n            return new HumanTaskDefinition();\n        } else if (UserTaskDefinition.TOKEN.equals(token)) {\n            return new UserTaskDefinition();\n        } else if (ConnectorInstanceDefinition.TOKEN.equals(token)) {\n            return new ConnectorInstanceDefinition();\n        }\n\n        // bpm.flownode.archive\n        else if (ArchivedFlowNodeDefinition.TOKEN.equals(token)) {\n            return new ArchivedFlowNodeDefinition();\n        } else if (ArchivedActivityDefinition.TOKEN.equals(token)) {\n            return new ArchivedActivityDefinition();\n        } else if (ArchivedTaskDefinition.TOKEN.equals(token)) {\n            return new ArchivedTaskDefinition();\n        } else if (ArchivedHumanTaskDefinition.TOKEN.equals(token)) {\n            return new ArchivedHumanTaskDefinition();\n        } else if (ArchivedUserTaskDefinition.TOKEN.equals(token)) {\n            return new ArchivedUserTaskDefinition();\n        } else if (ArchivedConnectorInstanceDefinition.TOKEN.equals(token)) {\n            return new ArchivedConnectorInstanceDefinition();\n        }\n\n        // system\n        else if (ProfileDefinition.TOKEN.equals(token)) {\n            return new ProfileDefinition();\n        } else if (ProfileMemberDefinition.TOKEN.equals(token)) {\n            return new ProfileMemberDefinition();\n        } else if (SessionDefinition.TOKEN.equals(token)) {\n            return new SessionDefinition();\n        }\n\n        // platform\n        else if (PlatformDefinition.TOKEN.equals(token)) {\n            return new PlatformDefinition();\n        }\n\n        // documents\n        else if (DocumentDefinition.TOKEN.equals(token)) {\n            return new DocumentDefinition();\n        } else if (ArchivedDocumentDefinition.TOKEN.equals(token)) {\n            return new ArchivedDocumentDefinition();\n        }\n\n        // Pages\n        else if (PageDefinition.TOKEN.equals(token)) {\n            return new PageDefinition();\n        }\n        //Applications\n        else if (AbstractApplicationDefinition.TOKEN.equals(token)) {\n            return new AbstractApplicationDefinition<AbstractApplicationItem>();\n        } else if (ApplicationLinkDefinition.TOKEN.equals(token)) {\n            return new ApplicationLinkDefinition();\n        } else if (ApplicationDefinition.TOKEN.equals(token)) {\n            return new ApplicationDefinition();\n        } else if (ApplicationPageDefinition.TOKEN.equals(token)) {\n            return new ApplicationPageDefinition();\n        } else if (ApplicationMenuDefinition.TOKEN.equals(token)) {\n            return new ApplicationMenuDefinition();\n        }\n        //tenant\n        else if (BusinessDataModelDefinition.TOKEN.equals(token)) {\n            return new BusinessDataModelDefinition();\n        }\n\n        // default\n        else {\n            return null;\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/application/AbstractApplicationDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.application;\n\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.function.Supplier;\n\nimport org.bonitasoft.web.toolkit.client.common.TreeIndexed;\nimport org.bonitasoft.web.toolkit.client.common.TreeLeaf;\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.DiscriminatedItemDefinitionHelper;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ValidationError;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ValidationException;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.FileIsImageOrServletPathValidator;\n\n/**\n * Item definition for a Bonita Living Application for the REST API (either legacy or link).\n */\npublic class AbstractApplicationDefinition<ITEM extends AbstractApplicationItem> extends ItemDefinition<ITEM> {\n\n    public static final String TOKEN = \"abstractApplication\";\n\n    @Override\n    protected String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return \"../API/living/application\";\n    }\n\n    @Override\n    protected void defineAttributes() {\n        createAttribute(AbstractApplicationItem.ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(AbstractApplicationItem.ATTRIBUTE_LINK, ItemAttribute.TYPE.BOOLEAN);\n        createAttribute(AbstractApplicationItem.ATTRIBUTE_TOKEN, ItemAttribute.TYPE.STRING);\n        createAttribute(AbstractApplicationItem.ATTRIBUTE_DISPLAY_NAME, ItemAttribute.TYPE.STRING);\n        createAttribute(AbstractApplicationItem.ATTRIBUTE_PROFILE_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(AbstractApplicationItem.ATTRIBUTE_VERSION, ItemAttribute.TYPE.STRING);\n        createAttribute(AbstractApplicationItem.ATTRIBUTE_DESCRIPTION, ItemAttribute.TYPE.TEXT);\n        createAttribute(AbstractApplicationItem.ATTRIBUTE_ICON, ItemAttribute.TYPE.STRING)\n                .addValidator(new FileIsImageOrServletPathValidator(ApplicationItem.ICON_PATH_API_PREFIX));\n        createAttribute(AbstractApplicationItem.ATTRIBUTE_CREATION_DATE, ItemAttribute.TYPE.STRING);\n        createAttribute(AbstractApplicationItem.ATTRIBUTE_CREATED_BY, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(AbstractApplicationItem.ATTRIBUTE_LAST_UPDATE_DATE, ItemAttribute.TYPE.STRING);\n        createAttribute(AbstractApplicationItem.ATTRIBUTE_UPDATED_BY, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(AbstractApplicationItem.ATTRIBUTE_STATE, ItemAttribute.TYPE.STRING);\n        createAttribute(AbstractApplicationItem.ATTRIBUTE_VISIBILITY, ItemAttribute.TYPE.STRING);\n        createAttribute(ApplicationItem.ATTRIBUTE_EDITABLE, ItemAttribute.TYPE.BOOLEAN);\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(AbstractApplicationItem.ATTRIBUTE_ID);\n    }\n\n    public static AbstractApplicationDefinition<? extends AbstractApplicationItem> get() {\n        return (AbstractApplicationDefinition<? extends AbstractApplicationItem>) Definitions.get(TOKEN);\n    }\n\n    @Override\n    protected ITEM _createItem() {\n        // this must not be called by deprecated PUT and POST methods which need to discriminate on \"link\".\n        throw new ValidationException(Collections.singletonList(\n                new ValidationError(\"link\", \"%attribute% is mandatory to discriminate the application type\")));\n    }\n\n    @Override\n    public Optional<DiscriminatedItemDefinitionHelper<ITEM>> getDiscriminatedHelper() {\n        if (AbstractApplicationDefinition.class.equals(getClass())) {\n            return Optional.of(new DiscriminatedItemDefinitionHelper<ITEM>() {\n\n                @Override\n                @SuppressWarnings(\"unchecked\")\n                public Supplier<ITEM> findItemCreator(Map<String, String> attributes) {\n                    // We need the \"link\" attribute to discriminate between legacy application and application link.\n                    boolean isLink = attributes != null\n                            && Boolean.parseBoolean(attributes.get(AbstractApplicationItem.ATTRIBUTE_LINK));\n                    return isLink ? () -> (ITEM) ApplicationLinkDefinition.get()._createItem()\n                            : () -> (ITEM) ApplicationDefinition.get()._createItem();\n                }\n\n                @Override\n                public Supplier<? extends ITEM> findItemCreator(TreeIndexed<String> tree) {\n                    // We need the \"link\" attribute to discriminate between legacy application and application link.\n                    boolean isLink;\n                    if (tree != null && tree.get(AbstractApplicationItem.ATTRIBUTE_LINK) instanceof TreeLeaf<?> v) {\n                        isLink = Optional.ofNullable(v.getValue()).map(Object::toString).map(Boolean::parseBoolean)\n                                .orElse(Boolean.FALSE);\n                    } else {\n                        isLink = false;\n                    }\n                    return isLink ? () -> (ITEM) ApplicationLinkDefinition.get()._createItem()\n                            : () -> (ITEM) ApplicationDefinition.get()._createItem();\n                }\n            });\n        } else {\n            // subclasses do not need the helper and use a concrete implementation\n            return Optional.empty();\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/application/AbstractApplicationItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.application;\n\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasIcon;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId;\n\n/**\n * Contains the meta information of a Bonita Living Application for the REST API.\n */\npublic abstract class AbstractApplicationItem extends Item implements ItemHasUniqueId, ItemHasIcon {\n\n    /**\n     * This attributes is used to distinguish application links from legacy\n     * applications. It can be used for search, filter or ordering.\n     */\n    public static final String ATTRIBUTE_LINK = \"link\";\n\n    public static final String ATTRIBUTE_TOKEN = \"token\";\n\n    public static final String ATTRIBUTE_DISPLAY_NAME = \"displayName\";\n\n    public static final String ATTRIBUTE_VERSION = \"version\";\n\n    public static final String ATTRIBUTE_DESCRIPTION = \"description\";\n\n    public static final String ATTRIBUTE_CREATION_DATE = \"creationDate\";\n\n    public static final String ATTRIBUTE_CREATED_BY = \"createdBy\";\n\n    public static final String ATTRIBUTE_LAST_UPDATE_DATE = \"lastUpdateDate\";\n\n    public static final String ATTRIBUTE_UPDATED_BY = \"updatedBy\";\n\n    public static final String ATTRIBUTE_STATE = \"state\";\n\n    public static final String ATTRIBUTE_PROFILE_ID = \"profileId\";\n\n    public static final String ATTRIBUTE_VISIBILITY = \"visibility\";\n\n    public static final String ATTRIBUTE_EDITABLE = \"editable\";\n\n    public static final String FILTER_USER_ID = \"userId\";\n\n    public static final String ICON_PATH_API_PREFIX = \"../API/applicationIcon/\";\n\n    public AbstractApplicationItem() {\n        super();\n        setAttribute(ATTRIBUTE_LINK, isLink());\n    }\n\n    public AbstractApplicationItem(final IItem item) {\n        super(item);\n        setAttribute(ATTRIBUTE_LINK, isLink());\n    }\n\n    @Override\n    public ApplicationDefinition getItemDefinition() {\n        return ApplicationDefinition.get();\n    }\n\n    @Override\n    public void setId(final String id) {\n        setId(APIID.makeAPIID(id));\n    }\n\n    @Override\n    public void setId(final Long id) {\n        setId(APIID.makeAPIID(id));\n    }\n\n    /*\n     * #isLink should be implemented by subclasses with a static result depending on the application nature.\n     */\n    public abstract boolean isLink();\n\n    public String getToken() {\n        return getAttributeValue(ATTRIBUTE_TOKEN);\n    }\n\n    public void setToken(final String token) {\n        setAttribute(ATTRIBUTE_TOKEN, token);\n    }\n\n    public String getDisplayName() {\n        return getAttributeValue(ATTRIBUTE_DISPLAY_NAME);\n    }\n\n    public void setDisplayName(final String name) {\n        setAttribute(ATTRIBUTE_DISPLAY_NAME, name);\n    }\n\n    public String getVersion() {\n        return getAttributeValue(ATTRIBUTE_VERSION);\n    }\n\n    public void setVersion(final String version) {\n        setAttribute(ATTRIBUTE_VERSION, version);\n    }\n\n    public String getDescription() {\n        return getAttributeValue(ATTRIBUTE_DESCRIPTION);\n    }\n\n    public void setDescription(final String description) {\n        setAttribute(ATTRIBUTE_DESCRIPTION, description);\n    }\n\n    @Override\n    public String getIcon() {\n        return getAttributeValue(ATTRIBUTE_ICON);\n    }\n\n    @Override\n    public void setIcon(String icon) {\n        setAttribute(ATTRIBUTE_ICON, icon);\n    }\n\n    public String getCreationDate() {\n        return getAttributeValue(ATTRIBUTE_CREATION_DATE);\n    }\n\n    public void setCreationDate(final String creationDate) {\n        setAttribute(ATTRIBUTE_CREATION_DATE, creationDate);\n    }\n\n    public long getCreatedBy() {\n        return getAttributeValueAsLong(ATTRIBUTE_CREATED_BY);\n    }\n\n    public void setCreatedBy(final long createdBy) {\n        setAttribute(ATTRIBUTE_CREATED_BY, createdBy);\n    }\n\n    public String getLastUpdateDate() {\n        return getAttributeValue(ATTRIBUTE_LAST_UPDATE_DATE);\n    }\n\n    public void setLastUpdateDate(final String lastUpdateDate) {\n        setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, lastUpdateDate);\n    }\n\n    public long getUpdatedBy() {\n        return getAttributeValueAsLong(ATTRIBUTE_UPDATED_BY);\n    }\n\n    public void setUpdatedBy(final long updatedBy) {\n        setAttribute(ATTRIBUTE_UPDATED_BY, updatedBy);\n    }\n\n    public String getState() {\n        return getAttributeValue(ATTRIBUTE_STATE);\n    }\n\n    public void setState(final String state) {\n        setAttribute(ATTRIBUTE_STATE, state);\n    }\n\n    public APIID getProfileId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_PROFILE_ID);\n    }\n\n    public void setProfileId(final Long profileId) {\n        setAttribute(ATTRIBUTE_PROFILE_ID, profileId);\n    }\n\n    public APIID getUserId() {\n        return getAttributeValueAsAPIID(FILTER_USER_ID);\n    }\n\n    public void setUserId(final String userId) {\n        setAttribute(FILTER_USER_ID, userId);\n    }\n\n    public String getVisibility() {\n        return getAttributeValue(ATTRIBUTE_VISIBILITY);\n    }\n\n    /** FIXME Use Enum instead of String after removing GWT permutations */\n    public void setVisibility(final String visibility) {\n        setAttribute(ATTRIBUTE_VISIBILITY, visibility);\n    }\n\n    public boolean isEditable() {\n        return Boolean.parseBoolean(getAttributeValue(ATTRIBUTE_EDITABLE));\n    }\n\n    public void setEditable(final boolean isEditable) {\n        setAttribute(ATTRIBUTE_EDITABLE, String.valueOf(isEditable));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/application/ApplicationDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.application;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * Item definition for a Legacy Bonita Living Application for the REST API.\n */\npublic class ApplicationDefinition extends AbstractApplicationDefinition<ApplicationItem> {\n\n    public static final String TOKEN = \"application\";\n\n    @Override\n    protected String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        super.defineAttributes();\n        createAttribute(ApplicationItem.ATTRIBUTE_HOME_PAGE_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(ApplicationItem.ATTRIBUTE_LAYOUT_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(ApplicationItem.ATTRIBUTE_THEME_ID, ItemAttribute.TYPE.ITEM_ID);\n    }\n\n    @Override\n    protected ApplicationItem _createItem() {\n        return new ApplicationItem();\n    }\n\n    public static ApplicationDefinition get() {\n        return (ApplicationDefinition) Definitions.get(TOKEN);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/application/ApplicationItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.application;\n\nimport org.bonitasoft.web.rest.model.portal.page.PageItem;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasIcon;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId;\n\n/**\n * Contains the meta information of a legacy Bonita Living Application for the REST API.\n */\npublic class ApplicationItem extends AbstractApplicationItem implements ItemHasUniqueId, ItemHasIcon {\n\n    public static final String ATTRIBUTE_HOME_PAGE_ID = \"homePageId\";\n\n    public static final String ATTRIBUTE_LAYOUT_ID = \"layoutId\";\n\n    public static final String ATTRIBUTE_THEME_ID = \"themeId\";\n\n    public ApplicationItem() {\n        super();\n    }\n\n    public ApplicationItem(final IItem item) {\n        super(item);\n    }\n\n    @Override\n    public boolean isLink() {\n        return false;\n    }\n\n    public APIID getHomePageId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_HOME_PAGE_ID);\n    }\n\n    public void setHomePageId(final Long homePageId) {\n        setAttribute(ATTRIBUTE_HOME_PAGE_ID, homePageId);\n    }\n\n    public APIID getLayoutId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_LAYOUT_ID);\n    }\n\n    public void setLayoutId(final Long layoutId) {\n        setAttribute(ATTRIBUTE_LAYOUT_ID, layoutId);\n    }\n\n    public APIID getThemeId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_THEME_ID);\n    }\n\n    public void setThemeId(final Long themeId) {\n        setAttribute(ATTRIBUTE_THEME_ID, themeId);\n    }\n\n    public PageItem getLayout() {\n        return (PageItem) getDeploy(ATTRIBUTE_LAYOUT_ID);\n    }\n\n    public PageItem getTheme() {\n        return (PageItem) getDeploy(ATTRIBUTE_THEME_ID);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/application/ApplicationLinkDefinition.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.application;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\n\n/**\n * Item definition for an Bonita Living Application Link for the REST API.\n */\npublic class ApplicationLinkDefinition extends AbstractApplicationDefinition<ApplicationLinkItem> {\n\n    public static final String TOKEN = \"applicationLink\";\n\n    @Override\n    protected String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected ApplicationLinkItem _createItem() {\n        return new ApplicationLinkItem();\n    }\n\n    public static ApplicationLinkDefinition get() {\n        return (ApplicationLinkDefinition) Definitions.get(TOKEN);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/application/ApplicationLinkItem.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.application;\n\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasIcon;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId;\n\n/**\n * Contains the meta information of a Bonita Living Application Link for the REST API.\n */\npublic class ApplicationLinkItem extends AbstractApplicationItem implements ItemHasUniqueId, ItemHasIcon {\n\n    public ApplicationLinkItem() {\n        super();\n    }\n\n    public ApplicationLinkItem(final IItem item) {\n        super(item);\n    }\n\n    @Override\n    public boolean isLink() {\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/applicationmenu/ApplicationMenuDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.applicationmenu;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * @author Julien Mege\n */\npublic class ApplicationMenuDefinition extends ItemDefinition<ApplicationMenuItem> {\n\n    public static final String TOKEN = \"applicationmenu\";\n\n    @Override\n    protected String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return \"../API/living/application-menu\";\n    }\n\n    @Override\n    protected void defineAttributes() {\n        createAttribute(ApplicationMenuItem.ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(ApplicationMenuItem.ATTRIBUTE_APPLICATION_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(ApplicationMenuItem.ATTRIBUTE_APPLICATION_PAGE_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(ApplicationMenuItem.ATTRIBUTE_PARENT_MENU_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(ApplicationMenuItem.ATTRIBUTE_MENU_INDEX, ItemAttribute.TYPE.INTEGER);\n        createAttribute(ApplicationMenuItem.ATTRIBUTE_DISPLAY_NAME, ItemAttribute.TYPE.STRING);\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(ApplicationMenuItem.ATTRIBUTE_ID);\n    }\n\n    @Override\n    protected ApplicationMenuItem _createItem() {\n        return new ApplicationMenuItem();\n    }\n\n    public static ApplicationMenuDefinition get() {\n        return (ApplicationMenuDefinition) Definitions.get(TOKEN);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/applicationmenu/ApplicationMenuItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.applicationmenu;\n\nimport org.bonitasoft.web.toolkit.client.common.util.StringUtil;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId;\n\n/**\n * @author Julien Mege\n */\npublic class ApplicationMenuItem extends Item implements ItemHasUniqueId {\n\n    public static final String ATTRIBUTE_DISPLAY_NAME = \"displayName\";\n\n    public static final String ATTRIBUTE_APPLICATION_ID = \"applicationId\";\n\n    public static final String ATTRIBUTE_APPLICATION_PAGE_ID = \"applicationPageId\";\n\n    public static final String ATTRIBUTE_PARENT_MENU_ID = \"parentMenuId\";\n\n    public static final String ATTRIBUTE_MENU_INDEX = \"menuIndex\";\n\n    @Override\n    public void setId(final String id) {\n        this.setAttribute(ATTRIBUTE_ID, id);\n    }\n\n    @Override\n    public void setId(final Long id) {\n        this.setId(id.toString());\n    }\n\n    public void setDisplayName(final String displayName) {\n        setAttribute(ATTRIBUTE_DISPLAY_NAME, displayName);\n    }\n\n    public String getDisplayName() {\n        return getAttributeValue(ATTRIBUTE_DISPLAY_NAME);\n    }\n\n    public void setApplicationPageId(final String id) {\n        this.setAttribute(ATTRIBUTE_APPLICATION_PAGE_ID, id);\n    }\n\n    public void setApplicationPageId(final Long id) {\n        this.setAttribute(ATTRIBUTE_APPLICATION_PAGE_ID, id.toString());\n    }\n\n    public APIID getApplicationPageId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_APPLICATION_PAGE_ID);\n    }\n\n    public void setApplicationId(final String id) {\n        this.setAttribute(ATTRIBUTE_APPLICATION_ID, id);\n    }\n\n    public void setApplicationId(final Long id) {\n        this.setAttribute(ATTRIBUTE_APPLICATION_ID, id.toString());\n    }\n\n    public APIID getApplicationId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_APPLICATION_ID);\n    }\n\n    public void setParentMenuId(final String id) {\n        this.setAttribute(ATTRIBUTE_PARENT_MENU_ID, id);\n    }\n\n    public void setParentMenuId(final Long id) {\n        this.setAttribute(ATTRIBUTE_PARENT_MENU_ID, id.toString());\n    }\n\n    public APIID getParentMenuId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_PARENT_MENU_ID);\n    }\n\n    public void setMenuIndex(final int id) {\n        this.setAttribute(ATTRIBUTE_MENU_INDEX, Integer.toString(id));\n    }\n\n    public Integer getMenuIndex() {\n        return StringUtil.toInteger(getAttributeValue(ATTRIBUTE_MENU_INDEX));\n    }\n\n    @Override\n    public ApplicationMenuDefinition getItemDefinition() {\n        return ApplicationMenuDefinition.get();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/applicationpage/ApplicationPageDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.applicationpage;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * @author Julien Mege\n */\npublic class ApplicationPageDefinition extends ItemDefinition<ApplicationPageItem> {\n\n    public static final String TOKEN = \"applicationpage\";\n\n    @Override\n    protected String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return \"../API/living/application-page\";\n    }\n\n    @Override\n    protected void defineAttributes() {\n        createAttribute(ApplicationPageItem.ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(ApplicationPageItem.ATTRIBUTE_TOKEN, ItemAttribute.TYPE.STRING);\n        createAttribute(ApplicationPageItem.ATTRIBUTE_APPLICATION_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(ApplicationPageItem.ATTRIBUTE_PAGE_ID, ItemAttribute.TYPE.ITEM_ID);\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(ApplicationPageItem.ATTRIBUTE_ID);\n    }\n\n    @Override\n    protected ApplicationPageItem _createItem() {\n        return new ApplicationPageItem();\n    }\n\n    public static ApplicationPageDefinition get() {\n        return (ApplicationPageDefinition) Definitions.get(TOKEN);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/applicationpage/ApplicationPageItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.applicationpage;\n\nimport org.bonitasoft.web.rest.model.application.ApplicationItem;\nimport org.bonitasoft.web.rest.model.portal.page.PageItem;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId;\n\n/**\n * @author Julien Mege\n */\npublic class ApplicationPageItem extends Item implements ItemHasUniqueId {\n\n    /* Token use to access the Page using URL : ../appName/pageToken/ */\n    public static final String ATTRIBUTE_TOKEN = \"token\";\n\n    public static final String ATTRIBUTE_APPLICATION_ID = \"applicationId\";\n\n    public static final String ATTRIBUTE_PAGE_ID = \"pageId\";\n\n    @Override\n    public ApplicationPageDefinition getItemDefinition() {\n        return ApplicationPageDefinition.get();\n    }\n\n    @Override\n    public void setId(final String id) {\n        setId(APIID.makeAPIID(id));\n    }\n\n    @Override\n    public void setId(final Long id) {\n        setId(APIID.makeAPIID(id));\n    }\n\n    public String getToken() {\n        return getAttributeValue(ATTRIBUTE_TOKEN);\n    }\n\n    public void setToken(final String name) {\n        setAttribute(ATTRIBUTE_TOKEN, name);\n    }\n\n    public APIID getApplicationId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_APPLICATION_ID);\n    }\n\n    public ApplicationItem getApplication() {\n        return new ApplicationItem(getDeploy(ATTRIBUTE_APPLICATION_ID));\n    }\n\n    public void setApplicationId(final Long appId) {\n        setAttribute(ATTRIBUTE_APPLICATION_ID, appId);\n    }\n\n    public APIID getPageId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_PAGE_ID);\n    }\n\n    public PageItem getPage() {\n        return new PageItem(getDeploy(ATTRIBUTE_PAGE_ID));\n    }\n\n    public void setPageId(final Long pageId) {\n        setAttribute(ATTRIBUTE_PAGE_ID, pageId);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bdm/BusinessDataModelItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bdm;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\nimport lombok.Getter;\nimport lombok.Setter;\n\n@Setter\n@Getter\npublic class BusinessDataModelItem implements Serializable {\n\n    @Serial\n    private static final long serialVersionUID = -6119736077951213751L;\n\n    private String fileUpload;\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/AbstractDocumentDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm;\n\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Paul AMAR\n */\npublic abstract class AbstractDocumentDefinition extends ItemDefinition {\n\n    /**\n     * Default Constructor.\n     */\n    public AbstractDocumentDefinition() {\n        super();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/AbstractDocumentItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm;\n\nimport java.util.Date;\n\nimport org.bonitasoft.web.toolkit.client.common.util.StringUtil;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId;\n\n/**\n * @author Paul AMAR\n */\npublic abstract class AbstractDocumentItem extends Item implements ItemHasUniqueId {\n\n    public static final String ATTRIBUTE_NAME = \"name\";\n\n    public static final String ATTRIBUTE_AUTHOR = \"author\";\n\n    public static final String ATTRIBUTE_CREATION_DATE = \"creationDate\";\n\n    public static final String ATTRIBUTE_HAS_CONTENT = \"hasContent\";\n\n    public static final String ATTRIBUTE_FILENAME = \"filename\";\n\n    public static final String ATTRIBUTE_CONTENT_MIME_TYPE = \"contentMimetype\";\n\n    public static final String ATTRIBUTE_FILE = \"file\";\n\n    public static final String ATTRIBUTE_URL = \"url\";\n\n    // /////////////////////////////////////////////////////////////////////////////////\n    // / FILTER\n    // /////////////////////////////////////////////////////////////////////////////////\n\n    public static final String FILTER_SUPERVISOR_ID = \"supervisor_id\";\n\n    // /////////////////////////////////////////////////////////////////////////////////\n    // / GETTERS\n    // /////////////////////////////////////////////////////////////////////////////////\n\n    public String getName() {\n        return this.getAttributeValue(ATTRIBUTE_NAME);\n    }\n\n    public String getFileName() {\n        return this.getAttributeValue(ATTRIBUTE_FILENAME);\n    }\n\n    public String getContentMimeType() {\n        return this.getAttributeValue(ATTRIBUTE_CONTENT_MIME_TYPE);\n    }\n\n    public APIID getAuthor() {\n        return this.getAttributeValueAsAPIID(ATTRIBUTE_AUTHOR);\n    }\n\n    public String getContentFileName() {\n        return this.getAttributeValue(ATTRIBUTE_FILENAME);\n    }\n\n    public Date getCreationDate() {\n        return this.getAttributeValueAsDate(ATTRIBUTE_CREATION_DATE);\n    }\n\n    public String getUrl() {\n        return this.getAttributeValue(ATTRIBUTE_URL);\n    }\n\n    public String getFile() {\n        return this.getAttributeValue(ATTRIBUTE_FILE);\n    }\n\n    public boolean hasContent() {\n        return StringUtil.toBoolean(this.getAttributeValue(ATTRIBUTE_HAS_CONTENT));\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////\n    // / SETTERS\n    // /////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public void setId(final String id) {\n        this.setAttribute(ATTRIBUTE_ID, id);\n    }\n\n    @Override\n    public void setId(final Long id) {\n        this.setAttribute(ATTRIBUTE_ID, id);\n    }\n\n    public void setHasContent(final boolean hasContent) {\n        this.setAttribute(ATTRIBUTE_NAME, hasContent);\n    }\n\n    public void setContentMimeType(final String contentMimeType) {\n        this.setAttribute(ATTRIBUTE_CONTENT_MIME_TYPE, contentMimeType);\n    }\n\n    public void setFileName(final String fileName) {\n        this.setAttribute(ATTRIBUTE_FILENAME, fileName);\n    }\n\n    public void setUrl(final String url) {\n        this.setAttribute(ATTRIBUTE_URL, url);\n    }\n\n    public void setAuthor(final long author) {\n        this.setAttribute(ATTRIBUTE_AUTHOR, author);\n    }\n\n    public void setAuthor(final APIID author) {\n        this.setAttribute(ATTRIBUTE_AUTHOR, author);\n    }\n\n    public void setAuthor(final String author) {\n        this.setAttribute(ATTRIBUTE_AUTHOR, author);\n    }\n\n    public void setCreationDate(final Date creationDate) {\n        this.setAttribute(ATTRIBUTE_CREATION_DATE, creationDate);\n    }\n\n    public void setFile(final String file) {\n        this.setAttribute(ATTRIBUTE_FILE, file);\n    }\n\n    public void setName(final String name) {\n        this.setAttribute(ATTRIBUTE_NAME, name);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/cases/ArchivedActivityVariable.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.cases;\n\nimport java.io.Serializable;\n\nimport org.bonitasoft.engine.bpm.data.ArchivedDataInstance;\n\npublic class ArchivedActivityVariable extends ArchivedVariable {\n\n    /**\n     * ID of the container this variable belongs to\n     */\n    private String containerId;\n\n    /**\n     * Type of the container this variable belongs to\n     */\n    private String containerType;\n\n    public String getContainerType() {\n        return containerType;\n    }\n\n    public void setContainerType(String containerType) {\n        this.containerType = containerType;\n    }\n\n    public String getContainerId() {\n        return containerId;\n    }\n\n    public void setContainerId(String containerId) {\n        this.containerId = containerId;\n    }\n\n    public static ArchivedActivityVariable create(ArchivedDataInstance archivedProcessDataInstance) {\n        var instance = new ArchivedActivityVariable();\n        instance.setName(archivedProcessDataInstance.getName());\n        instance.setContainerId(String.valueOf(archivedProcessDataInstance.getContainerId()));\n        instance.setDescription(archivedProcessDataInstance.getDescription());\n        instance.setType(archivedProcessDataInstance.getClassName());\n        instance.setContainerType(archivedProcessDataInstance.getContainerType());\n        Serializable value = archivedProcessDataInstance.getValue();\n        instance.setValue(value == null ? null : String.valueOf(value));\n        instance.setArchivedDate(archivedProcessDataInstance.getArchiveDate());\n        archivedProcessDataInstance.getContainerType();\n        instance.setSourceObjectId(String.valueOf(archivedProcessDataInstance.getSourceObjectId()));\n        return instance;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/cases/ArchivedCaseDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.cases;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * Archived process instance definition\n *\n * @author Séverin Moussel\n */\npublic class ArchivedCaseDefinition extends ItemDefinition<ArchivedCaseItem> {\n\n    /**\n     * Singleton\n     */\n    public static ArchivedCaseDefinition get() {\n        return (ArchivedCaseDefinition) Definitions.get(TOKEN);\n    }\n\n    public static final String TOKEN = \"archivedcases\";\n\n    /**\n     * the URL of user resource\n     */\n    protected static final String API_URL = \"../API/bpm/archivedCase\";\n\n    @Override\n    public String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(CaseItem.ATTRIBUTE_ID);\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        createAttribute(ArchivedCaseItem.ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(ArchivedCaseItem.ATTRIBUTE_STATE, ItemAttribute.TYPE.ENUM);\n        createAttribute(ArchivedCaseItem.ATTRIBUTE_START_DATE, ItemAttribute.TYPE.DATETIME);\n        createAttribute(ArchivedCaseItem.ATTRIBUTE_LAST_UPDATE_DATE, ItemAttribute.TYPE.DATETIME);\n        createAttribute(ArchivedCaseItem.ATTRIBUTE_END_DATE, ItemAttribute.TYPE.DATETIME);\n        createAttribute(ArchivedCaseItem.ATTRIBUTE_PROCESS_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(ArchivedCaseItem.ATTRIBUTE_ARCHIVED_DATE, ItemAttribute.TYPE.DATETIME);\n        createAttribute(ArchivedCaseItem.ATTRIBUTE_STARTED_BY_USER_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(ArchivedCaseItem.ATTRIBUTE_STARTED_BY_SUBSTITUTE_USER_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_1_LABEL, ItemAttribute.TYPE.STRING);\n        createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_2_LABEL, ItemAttribute.TYPE.STRING);\n        createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_3_LABEL, ItemAttribute.TYPE.STRING);\n        createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_4_LABEL, ItemAttribute.TYPE.STRING);\n        createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_5_LABEL, ItemAttribute.TYPE.STRING);\n        createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_1_VALUE, ItemAttribute.TYPE.STRING);\n        createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_2_VALUE, ItemAttribute.TYPE.STRING);\n        createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_3_VALUE, ItemAttribute.TYPE.STRING);\n        createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_4_VALUE, ItemAttribute.TYPE.STRING);\n        createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_5_VALUE, ItemAttribute.TYPE.STRING);\n    }\n\n    @Override\n    public ArchivedCaseItem _createItem() {\n        return new ArchivedCaseItem();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/cases/ArchivedCaseDocumentDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.cases;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * @author Fabio Lombardi\n */\npublic class ArchivedCaseDocumentDefinition extends ItemDefinition {\n\n    /**\n     * Singleton\n     */\n    public static ArchivedCaseDocumentDefinition get() {\n        return (ArchivedCaseDocumentDefinition) Definitions.get(TOKEN);\n    }\n\n    /**\n     * token\n     */\n    public static final String TOKEN = \"archivedcasedocument\";\n\n    /**\n     * the URL of user resource\n     */\n    private static final String API_URL = \"../API/bpm/archviedCaseDocument\";\n\n    @Override\n    public String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        createAttribute(ArchivedCaseDocumentItem.ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(ArchivedCaseDocumentItem.ATTRIBUTE_VERSION, ItemAttribute.TYPE.STRING);\n        createAttribute(ArchivedCaseDocumentItem.ATTRIBUTE_CASE_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(ArchivedCaseDocumentItem.ATTRIBUTE_NAME, ItemAttribute.TYPE.STRING);\n        createAttribute(ArchivedCaseDocumentItem.ATTRIBUTE_DESCRIPTION, ItemAttribute.TYPE.TEXT);\n        createAttribute(ArchivedCaseDocumentItem.ATTRIBUTE_SOURCE_OBJECT_ID, ItemAttribute.TYPE.STRING);\n        createAttribute(ArchivedCaseDocumentItem.ATTRIBUTE_ARCHIVED_DATE, ItemAttribute.TYPE.DATE);\n        createAttribute(ArchivedCaseDocumentItem.ATTRIBUTE_SUBMITTED_BY_USER_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(ArchivedCaseDocumentItem.ATTRIBUTE_CREATION_DATE, ItemAttribute.TYPE.DATE);\n        createAttribute(ArchivedCaseDocumentItem.ATTRIBUTE_HAS_CONTENT, ItemAttribute.TYPE.BOOLEAN);\n        createAttribute(ArchivedCaseDocumentItem.ATTRIBUTE_CONTENT_FILENAME, ItemAttribute.TYPE.STRING);\n        createAttribute(ArchivedCaseDocumentItem.ATTRIBUTE_CONTENT_MIMETYPE, ItemAttribute.TYPE.STRING);\n        createAttribute(ArchivedCaseDocumentItem.ATTRIBUTE_CONTENT_STORAGE_ID, ItemAttribute.TYPE.STRING);\n        createAttribute(ArchivedCaseDocumentItem.ATTRIBUTE_URL, ItemAttribute.TYPE.URL);\n        createAttribute(ArchivedCaseDocumentItem.ATTRIBUTE_UPLOAD_PATH, ItemAttribute.TYPE.STRING);\n        createAttribute(ArchivedCaseDocumentItem.ATTRIBUTE_INDEX, ItemAttribute.TYPE.STRING);\n        createAttribute(ArchivedCaseDocumentItem.ATTRIBUTE_AUTHOR, ItemAttribute.TYPE.STRING);\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(ArchivedCaseDocumentItem.ATTRIBUTE_ID);\n    }\n\n    @Override\n    protected IItem _createItem() {\n        return new ArchivedCaseDocumentItem();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/cases/ArchivedCaseDocumentItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.cases;\n\nimport java.util.Date;\n\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Fabio Lombardi\n */\npublic class ArchivedCaseDocumentItem extends CaseDocumentItem {\n\n    public static final String ATTRIBUTE_SOURCE_OBJECT_ID = \"sourceObjectId\";\n\n    public static final String ATTRIBUTE_ARCHIVED_DATE = \"archivedDate\";\n\n    public static final String FILTER_ARCHIVED_CASE_ID = \"archivedCaseId\";\n\n    public ArchivedCaseDocumentItem() {\n        super();\n    }\n\n    public ArchivedCaseDocumentItem(final IItem item) {\n        super(item);\n    }\n\n    // SETTER\n\n    public void setArchivedDate(final Date date) {\n        setAttribute(ATTRIBUTE_ARCHIVED_DATE, date);\n    }\n\n    public void setArchivedDate(final String date) {\n        setAttribute(ATTRIBUTE_ARCHIVED_DATE, date);\n    }\n\n    public void setSourceObjectId(final Long id) {\n        setAttribute(ATTRIBUTE_SOURCE_OBJECT_ID, id);\n    }\n\n    public void setSourceObjectId(final APIID id) {\n        setAttribute(ATTRIBUTE_SOURCE_OBJECT_ID, id);\n    }\n\n    public void setSourceObjectId(final String id) {\n        setAttribute(ATTRIBUTE_SOURCE_OBJECT_ID, id);\n    }\n\n    // GETTER\n\n    @Override\n    public ItemDefinition getItemDefinition() {\n        return new ArchivedCaseDefinition();\n    }\n\n    public APIID getSourceObjectId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_SOURCE_OBJECT_ID);\n    }\n\n    public Date getArchivedDate() {\n        return getAttributeValueAsDate(ATTRIBUTE_ARCHIVED_DATE);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/cases/ArchivedCaseItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.cases;\n\nimport java.util.Date;\n\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * Archived process instance item\n *\n * @author Séverin Moussel\n */\npublic class ArchivedCaseItem extends CaseItem {\n\n    public static final String ATTRIBUTE_SOURCE_OBJECT_ID = \"sourceObjectId\";\n\n    public ArchivedCaseItem() {\n        super();\n    }\n\n    public ArchivedCaseItem(final IItem item) {\n        super(item);\n    }\n\n    public static final String ATTRIBUTE_ARCHIVED_DATE = \"archivedDate\";\n\n    // SETTER\n\n    public void setArchivedDate(final Date date) {\n        setAttribute(ATTRIBUTE_ARCHIVED_DATE, date);\n    }\n\n    public void setArchivedDate(final String date) {\n        setAttribute(ATTRIBUTE_ARCHIVED_DATE, date);\n    }\n\n    public void setSourceObjectId(final Long id) {\n        setAttribute(ATTRIBUTE_SOURCE_OBJECT_ID, id);\n    }\n\n    public void setSourceObjectId(final APIID id) {\n        setAttribute(ATTRIBUTE_SOURCE_OBJECT_ID, id);\n    }\n\n    public void setSourceObjectId(final String id) {\n        setAttribute(ATTRIBUTE_SOURCE_OBJECT_ID, id);\n    }\n\n    // GETTER\n\n    @Override\n    public ItemDefinition getItemDefinition() {\n        return new ArchivedCaseDefinition();\n    }\n\n    public APIID getSourceObjectId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_SOURCE_OBJECT_ID);\n    }\n\n    public Date getArchivedDate() {\n        return getAttributeValueAsDate(ATTRIBUTE_ARCHIVED_DATE);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/cases/ArchivedCaseVariable.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.cases;\n\nimport java.io.Serializable;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport org.bonitasoft.engine.bpm.data.ArchivedDataInstance;\n\npublic class ArchivedCaseVariable extends ArchivedVariable {\n\n    /**\n     * ID of the case this variable belongs to\n     */\n    @JsonProperty(value = \"case_id\")\n    private String caseId;\n\n    public String getCaseId() {\n        return caseId;\n    }\n\n    public void setCaseId(String caseId) {\n        this.caseId = caseId;\n    }\n\n    public static ArchivedCaseVariable create(ArchivedDataInstance archivedProcessDataInstance) {\n        var instance = new ArchivedCaseVariable();\n        instance.setName(archivedProcessDataInstance.getName());\n        instance.setCaseId(String.valueOf(archivedProcessDataInstance.getContainerId()));\n        instance.setDescription(archivedProcessDataInstance.getDescription());\n        instance.setType(archivedProcessDataInstance.getClassName());\n        Serializable value = archivedProcessDataInstance.getValue();\n        instance.setValue(value == null ? null : String.valueOf(value));\n        instance.setArchivedDate(archivedProcessDataInstance.getArchiveDate());\n        instance.setSourceObjectId(String.valueOf(archivedProcessDataInstance.getSourceObjectId()));\n        return instance;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/cases/ArchivedCommentDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.cases;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * @author Gai Cuisha\n */\npublic class ArchivedCommentDefinition extends ItemDefinition<ArchivedCommentItem> {\n\n    /**\n     * Singleton\n     */\n    public static ArchivedCommentDefinition get() {\n        return (ArchivedCommentDefinition) Definitions.get(TOKEN);\n    }\n\n    public static final String TOKEN = \"archivedComment\";\n\n    /**\n     * the URL of user resource\n     */\n    private static final String API_URL = \"../API/bpm/archivedComment\";\n\n    @Override\n    public String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(CommentItem.ATTRIBUTE_ID);\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        createAttribute(ArchivedCommentItem.ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(ArchivedCommentItem.ATTRIBUTE_USER_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(ArchivedCommentItem.ATTRIBUTE_PROCESS_INSTANCE_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(ArchivedCommentItem.ATTRIBUTE_POST_DATE, ItemAttribute.TYPE.DATETIME);\n        createAttribute(ArchivedCommentItem.ATTRIBUTE_CONTENT, ItemAttribute.TYPE.TEXT)\n                .isMandatory();\n        createAttribute(ArchivedCommentItem.ATTRIBUTE_ARCHIVED_DATE, ItemAttribute.TYPE.DATETIME);\n    }\n\n    @Override\n    public ArchivedCommentItem _createItem() {\n        return new ArchivedCommentItem();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/cases/ArchivedCommentItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.cases;\n\nimport java.util.Date;\n\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Gai Cuisha\n */\npublic class ArchivedCommentItem extends CommentItem {\n\n    public ArchivedCommentItem() {\n        super();\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // ATTRIBUTES NAMES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public static final String ATTRIBUTE_ARCHIVED_DATE = \"archivedDate\";\n\n    // SETTERS\n\n    public void setArchivedDate(final String date) {\n        this.setAttribute(ATTRIBUTE_ARCHIVED_DATE, date);\n    }\n\n    public void setArchivedDate(final Date date) {\n        this.setAttribute(ATTRIBUTE_ARCHIVED_DATE, date);\n    }\n\n    // GETTERS\n\n    public Date getArchivedDate() {\n        return this.getAttributeValueAsDate(ATTRIBUTE_ARCHIVED_DATE);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // FILTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // DEPLOYS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // UTILS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public ItemDefinition getItemDefinition() {\n        return new ArchivedCommentDefinition();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/cases/ArchivedVariable.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.cases;\n\nimport java.util.Date;\n\nimport com.fasterxml.jackson.annotation.JsonFormat;\n\npublic abstract class ArchivedVariable {\n\n    /**\n     * Name of the variable in the case\n     */\n    private String name;\n    /**\n     * Detailed description of the case variable, as set in the definition at\n     * design-time\n     */\n    private String description;\n    /**\n     * The value of the archived case variable\n     */\n    private String value;\n\n    /**\n     * The Java type of the variable\n     */\n    private String type;\n\n    /**\n     * The date and time when this variable was archived\n     */\n    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = \"yyyy-MM-dd HH:mm:ss.SSS\")\n    private Date archivedDate;\n\n    /**\n     * ID of the variable before it was archived\n     */\n    private String sourceObjectId;\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public String getDescription() {\n        return description;\n    }\n\n    public void setDescription(String description) {\n        this.description = description;\n    }\n\n    public String getValue() {\n        return value;\n    }\n\n    public void setValue(String value) {\n        this.value = value;\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    public void setType(String type) {\n        this.type = type;\n    }\n\n    public Date getArchivedDate() {\n        return archivedDate;\n    }\n\n    public void setArchivedDate(Date archivedDate) {\n        this.archivedDate = archivedDate;\n    }\n\n    public String getSourceObjectId() {\n        return sourceObjectId;\n    }\n\n    public void setSourceObjectId(String sourceObjectId) {\n        this.sourceObjectId = sourceObjectId;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/cases/CaseDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.cases;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * Process instance definition\n *\n * @author Séverin Moussel *\n */\npublic class CaseDefinition extends ItemDefinition {\n\n    /**\n     * Singleton\n     */\n    public static CaseDefinition get() {\n        return (CaseDefinition) Definitions.get(TOKEN);\n    }\n\n    public static final String TOKEN = \"cases\";\n\n    /**\n     * the URL of user resource\n     */\n    protected static final String API_URL = \"../API/bpm/case\";\n\n    @Override\n    public String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(CaseItem.ATTRIBUTE_ID);\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        createAttribute(CaseItem.ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(CaseItem.ATTRIBUTE_STATE, ItemAttribute.TYPE.ENUM);\n        createAttribute(CaseItem.ATTRIBUTE_PROCESS_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(CaseItem.ATTRIBUTE_STARTED_BY_USER_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(CaseItem.ATTRIBUTE_STARTED_BY_SUBSTITUTE_USER_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(CaseItem.ATTRIBUTE_START_DATE, ItemAttribute.TYPE.DATETIME);\n        createAttribute(CaseItem.ATTRIBUTE_LAST_UPDATE_DATE, ItemAttribute.TYPE.DATETIME);\n        createAttribute(CaseItem.ATTRIBUTE_END_DATE, ItemAttribute.TYPE.DATETIME);\n        createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_1_LABEL, ItemAttribute.TYPE.STRING);\n        createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_2_LABEL, ItemAttribute.TYPE.STRING);\n        createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_3_LABEL, ItemAttribute.TYPE.STRING);\n        createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_4_LABEL, ItemAttribute.TYPE.STRING);\n        createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_5_LABEL, ItemAttribute.TYPE.STRING);\n        createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_1_VALUE, ItemAttribute.TYPE.STRING);\n        createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_2_VALUE, ItemAttribute.TYPE.STRING);\n        createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_3_VALUE, ItemAttribute.TYPE.STRING);\n        createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_4_VALUE, ItemAttribute.TYPE.STRING);\n        createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_5_VALUE, ItemAttribute.TYPE.STRING);\n    }\n\n    @Override\n    public IItem _createItem() {\n        return new CaseItem();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/cases/CaseDocumentDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.cases;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * @author Paul AMAR\n */\npublic class CaseDocumentDefinition extends ItemDefinition {\n\n    /**\n     * Singleton\n     */\n    public static CaseDocumentDefinition get() {\n        return (CaseDocumentDefinition) Definitions.get(TOKEN);\n    }\n\n    /**\n     * token\n     */\n    public static final String TOKEN = \"casedocument\";\n\n    /**\n     * the URL of user resource\n     */\n    private static final String API_URL = \"../API/bpm/caseDocument\";\n\n    @Override\n    public String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        createAttribute(CaseDocumentItem.ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(CaseDocumentItem.ATTRIBUTE_VERSION, ItemAttribute.TYPE.STRING);\n        createAttribute(CaseDocumentItem.ATTRIBUTE_CASE_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(CaseDocumentItem.ATTRIBUTE_NAME, ItemAttribute.TYPE.STRING);\n        createAttribute(CaseDocumentItem.ATTRIBUTE_DESCRIPTION, ItemAttribute.TYPE.TEXT);\n        createAttribute(CaseDocumentItem.ATTRIBUTE_SUBMITTED_BY_USER_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(CaseDocumentItem.ATTRIBUTE_CREATION_DATE, ItemAttribute.TYPE.DATE);\n        createAttribute(CaseDocumentItem.ATTRIBUTE_HAS_CONTENT, ItemAttribute.TYPE.BOOLEAN);\n        createAttribute(CaseDocumentItem.ATTRIBUTE_CONTENT_FILENAME, ItemAttribute.TYPE.STRING);\n        createAttribute(CaseDocumentItem.ATTRIBUTE_CONTENT_MIMETYPE, ItemAttribute.TYPE.STRING);\n        createAttribute(CaseDocumentItem.ATTRIBUTE_CONTENT_STORAGE_ID, ItemAttribute.TYPE.STRING);\n        createAttribute(CaseDocumentItem.ATTRIBUTE_URL, ItemAttribute.TYPE.URL);\n        createAttribute(CaseDocumentItem.ATTRIBUTE_UPLOAD_PATH, ItemAttribute.TYPE.STRING);\n        createAttribute(CaseDocumentItem.ATTRIBUTE_INDEX, ItemAttribute.TYPE.STRING);\n        createAttribute(CaseDocumentItem.ATTRIBUTE_AUTHOR, ItemAttribute.TYPE.STRING);\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(CaseDocumentItem.ATTRIBUTE_ID);\n    }\n\n    @Override\n    protected IItem _createItem() {\n        return new CaseDocumentItem();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/cases/CaseDocumentItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.cases;\n\nimport java.util.Date;\n\nimport org.bonitasoft.web.rest.model.document.DocumentDefinition;\nimport org.bonitasoft.web.rest.model.identity.UserItem;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Fabio Lombardi\n */\npublic class CaseDocumentItem extends Item {\n\n    public CaseDocumentItem() {\n        super();\n    }\n\n    public CaseDocumentItem(final IItem item) {\n        super(item);\n    }\n\n    public static final String ATTRIBUTE_ID = \"id\";\n\n    public static final String ATTRIBUTE_INDEX = \"index\";\n\n    public static final String ATTRIBUTE_VERSION = \"version\";\n\n    public static final String ATTRIBUTE_CASE_ID = \"caseId\";\n\n    public static final String ATTRIBUTE_NAME = \"name\";\n\n    public static final String ATTRIBUTE_DESCRIPTION = \"description\";\n\n    public static final String ATTRIBUTE_SUBMITTED_BY_USER_ID = \"submittedBy\";\n\n    public static final String ATTRIBUTE_AUTHOR = \"author\";\n\n    public static final String ATTRIBUTE_CREATION_DATE = \"creationDate\";\n\n    public static final String ATTRIBUTE_HAS_CONTENT = \"isInternal\";\n\n    public static final String ATTRIBUTE_CONTENT_FILENAME = \"fileName\";\n\n    public static final String ATTRIBUTE_CONTENT_MIMETYPE = \"contentMimetype\";\n\n    public static final String ATTRIBUTE_CONTENT_STORAGE_ID = \"contentStorageId\";\n\n    public static final String ATTRIBUTE_UPLOAD_PATH = \"file\";\n\n    public static final String ATTRIBUTE_URL = \"url\";\n\n    public static final String FILTER_SUPERVISOR_ID = \"supervisor_id\";\n\n    public void setId(final String id) {\n        this.setAttribute(ATTRIBUTE_ID, id);\n    }\n\n    public void setVersion(final String version) {\n        this.setAttribute(ATTRIBUTE_VERSION, version);\n    }\n\n    public void setCaseId(final String caseId) {\n        this.setAttribute(ATTRIBUTE_CASE_ID, caseId);\n    }\n\n    public void setName(final String name) {\n        this.setAttribute(ATTRIBUTE_NAME, name);\n    }\n\n    public void setDescription(final String description) {\n        setAttribute(ATTRIBUTE_DESCRIPTION, description);\n    }\n\n    public void setSubmittedBy(final APIID userId) {\n        this.setAttribute(ATTRIBUTE_SUBMITTED_BY_USER_ID, userId.toString());\n        this.setAttribute(ATTRIBUTE_AUTHOR, userId);\n    }\n\n    public void setSubmittedBy(final Long userId) {\n        this.setAttribute(ATTRIBUTE_SUBMITTED_BY_USER_ID, userId.toString());\n        this.setAttribute(ATTRIBUTE_AUTHOR, userId.toString());\n    }\n\n    public void setCreationDate(final String creationDate) {\n        this.setAttribute(ATTRIBUTE_CREATION_DATE, creationDate);\n    }\n\n    public void setCreationDate(final Date creationDate) {\n        this.setAttribute(ATTRIBUTE_CREATION_DATE, creationDate);\n    }\n\n    public void setHasContent(final String hasContent) {\n        this.setAttribute(ATTRIBUTE_HAS_CONTENT, hasContent);\n    }\n\n    public void setFileName(final String fileName) {\n        this.setAttribute(ATTRIBUTE_CONTENT_FILENAME, fileName);\n    }\n\n    public void setMIMEType(final String MIMEType) {\n        this.setAttribute(ATTRIBUTE_CONTENT_MIMETYPE, MIMEType);\n    }\n\n    public void setStorageId(final String storageId) {\n        this.setAttribute(ATTRIBUTE_CONTENT_STORAGE_ID, storageId);\n    }\n\n    public void setUploadPath(final String uploadPath) {\n        this.setAttribute(ATTRIBUTE_UPLOAD_PATH, uploadPath);\n    }\n\n    public String getUploadPath() {\n        return getAttributeValue(ATTRIBUTE_UPLOAD_PATH);\n    }\n\n    public void setURL(final String URL) {\n        this.setAttribute(ATTRIBUTE_URL, URL);\n    }\n\n    public void setIndex(final String index) {\n        this.setAttribute(ATTRIBUTE_INDEX, index);\n    }\n\n    public void setIndex(final int index) {\n        this.setAttribute(ATTRIBUTE_INDEX, index);\n    }\n\n    public String getVersion() {\n        return getAttributeValue(ATTRIBUTE_VERSION);\n    }\n\n    public APIID getCaseId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_CASE_ID);\n    }\n\n    public String getName() {\n        return getAttributeValue(ATTRIBUTE_NAME);\n    }\n\n    public String getDescription() {\n        return getAttributeValue(ATTRIBUTE_DESCRIPTION);\n    }\n\n    public APIID getSubmittedBy() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_SUBMITTED_BY_USER_ID);\n    }\n\n    public Date getCreationDate() {\n        return getAttributeValueAsDate(ATTRIBUTE_CREATION_DATE);\n    }\n\n    public boolean hasContent() {\n        return Boolean.parseBoolean(getAttributeValue(ATTRIBUTE_HAS_CONTENT));\n    }\n\n    public String getFileName() {\n        return getAttributeValue(ATTRIBUTE_CONTENT_FILENAME);\n    }\n\n    public String getMIMEType() {\n        return getAttributeValue(ATTRIBUTE_CONTENT_MIMETYPE);\n    }\n\n    public String getStorageId() {\n        return getAttributeValue(ATTRIBUTE_CONTENT_STORAGE_ID);\n    }\n\n    public String getURL() {\n        return getAttributeValue(ATTRIBUTE_URL);\n    }\n\n    public String getIndex() {\n        return getAttributeValue(ATTRIBUTE_INDEX);\n    }\n\n    @Override\n    public ItemDefinition getItemDefinition() {\n        return new DocumentDefinition();\n    }\n\n    public UserItem getSubmittedByUser() {\n        return (UserItem) getDeploy(ATTRIBUTE_SUBMITTED_BY_USER_ID);\n    }\n\n    /* Methods kept here to avoid API break */\n\n    public void setDocumentAuthor(final Long userId) {\n        this.setAttribute(ATTRIBUTE_AUTHOR, userId);\n    }\n\n    public UserItem getAuthorByUser() {\n        return (UserItem) getDeploy(ATTRIBUTE_AUTHOR);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/cases/CaseItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.cases;\n\nimport java.util.Date;\n\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessItem;\nimport org.bonitasoft.web.rest.model.identity.UserItem;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasLastUpdateDate;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId;\n\n/**\n * process instance item\n *\n * @author Haojie Yuan\n * @author Celine Souchet\n */\npublic class CaseItem extends Item implements ItemHasLastUpdateDate, ItemHasUniqueId {\n\n    public static final String ATTRIBUTE_VARIABLES = \"variables\";\n\n    public static final String ATTRIBUTE_STATE = \"state\";\n\n    public static final String ATTRIBUTE_PROCESS_ID = \"processDefinitionId\";\n\n    public static final String ATTRIBUTE_PROCESS_NAME = \"name\";\n\n    public static final String ATTRIBUTE_ROOT_CASE_ID = \"rootCaseId\";\n\n    public static final String ATTRIBUTE_STARTED_BY_USER_ID = \"started_by\";\n\n    public static final String ATTRIBUTE_STARTED_BY_SUBSTITUTE_USER_ID = \"startedBySubstitute\";\n\n    public static final String ATTRIBUTE_START_DATE = \"start\";\n\n    public static final String ATTRIBUTE_END_DATE = \"end_date\";\n\n    public static final String COUNTER_FAILED_FLOW_NODES = \"failedFlowNodes\";\n\n    public static final String COUNTER_ACTIVE_FLOW_NODES = \"activeFlowNodes\";\n\n    public static final String COUNTER_PENDING_FLOW_NODES = \"pendingFlowNodes\";\n\n    public static final String ATTRIBUTE_SEARCH_INDEX_1_LABEL = \"searchIndex1Label\";\n    public static final String ATTRIBUTE_SEARCH_INDEX_1_VALUE = \"searchIndex1Value\";\n    public static final String ATTRIBUTE_SEARCH_INDEX_2_LABEL = \"searchIndex2Label\";\n    public static final String ATTRIBUTE_SEARCH_INDEX_2_VALUE = \"searchIndex2Value\";\n    public static final String ATTRIBUTE_SEARCH_INDEX_3_LABEL = \"searchIndex3Label\";\n    public static final String ATTRIBUTE_SEARCH_INDEX_3_VALUE = \"searchIndex3Value\";\n    public static final String ATTRIBUTE_SEARCH_INDEX_4_LABEL = \"searchIndex4Label\";\n    public static final String ATTRIBUTE_SEARCH_INDEX_4_VALUE = \"searchIndex4Value\";\n    public static final String ATTRIBUTE_SEARCH_INDEX_5_LABEL = \"searchIndex5Label\";\n    public static final String ATTRIBUTE_SEARCH_INDEX_5_VALUE = \"searchIndex5Value\";\n\n    public static final String ATTRIBUTE_CALLER_ID = \"callerId\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // ATTRIBUTES VALUES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // see ProcessInstanceState enum\n    public static final String VALUE_STATE_INITIALIZING = \"0\";\n\n    public static final String VALUE_STATE_STARTED = \"1\";\n\n    public static final String VALUE_STATE_SUSPENDED = \"2\";\n\n    public static final String VALUE_STATE_CANCELLED = \"3\";\n\n    public static final String VALUE_STATE_ABORTED = \"4\";\n\n    public static final String VALUE_STATE_COMPLETING = \"5\";\n\n    public static final String VALUE_STATE_COMPLETED = \"6\";\n\n    public static final String VALUE_STATE_ERROR = \"7\";\n\n    public static final String VALUE_STATE_TO_MIGRATE = \"8\";\n\n    public static final String VALUE_STATE_READY_FOR_MIGRATION = \"9\";\n\n    public static final String VALUE_STATE_MIGRATING = \"10\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // FILTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public static final String FILTER_USER_ID = \"user_id\";\n\n    public static final String FILTER_SUPERVISOR_ID = \"supervisor_id\";\n\n    public static final String FILTER_TEAM_MANAGER_ID = \"team_manager_id\";\n\n    public static final String FILTER_CALLER = \"caller\";\n\n    public static final String FILTER_STATE = \"state\";\n\n    public CaseItem() {\n        super();\n    }\n\n    public CaseItem(final IItem item) {\n        super(item);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // GETTERS AND SETTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // DEPLOYS\n\n    public UserItem getStartedByUser() {\n        return new UserItem(getDeploy(ATTRIBUTE_STARTED_BY_USER_ID));\n    }\n\n    public UserItem getStartedBySubstituteUser() {\n        return new UserItem(getDeploy(ATTRIBUTE_STARTED_BY_SUBSTITUTE_USER_ID));\n    }\n\n    public ProcessItem getProcess() {\n        return new ProcessItem(getDeploy(ATTRIBUTE_PROCESS_ID));\n    }\n\n    // GETTERS\n\n    @Override\n    public Date getLastUpdateDate() {\n        return getAttributeValueAsDate(ATTRIBUTE_LAST_UPDATE_DATE);\n    }\n\n    public String getState() {\n        return getAttributeValue(ATTRIBUTE_STATE);\n    }\n\n    public Date getStartDate() {\n        return getAttributeValueAsDate(ATTRIBUTE_START_DATE);\n    }\n\n    public APIID getStartedByUserId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_STARTED_BY_USER_ID);\n    }\n\n    public APIID getStartedBySubstituteUserId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_STARTED_BY_SUBSTITUTE_USER_ID);\n    }\n\n    public Date getEndDate() {\n        return getAttributeValueAsDate(ATTRIBUTE_END_DATE);\n    }\n\n    public APIID getProcessId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_PROCESS_ID);\n    }\n\n    public APIID getRootCaseId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_ROOT_CASE_ID);\n    }\n\n    public String getSearchIndex1Label() {\n        return getAttributeValue(ATTRIBUTE_SEARCH_INDEX_1_LABEL);\n    }\n\n    public String getSearchIndex1Value() {\n        return getAttributeValue(ATTRIBUTE_SEARCH_INDEX_1_VALUE);\n    }\n\n    public String getSearchIndex2Label() {\n        return getAttributeValue(ATTRIBUTE_SEARCH_INDEX_2_LABEL);\n    }\n\n    public String getSearchIndex2Value() {\n        return getAttributeValue(ATTRIBUTE_SEARCH_INDEX_2_VALUE);\n    }\n\n    public String getSearchIndex3Label() {\n        return getAttributeValue(ATTRIBUTE_SEARCH_INDEX_3_LABEL);\n    }\n\n    public String getSearchIndex3Value() {\n        return getAttributeValue(ATTRIBUTE_SEARCH_INDEX_3_VALUE);\n    }\n\n    public String getSearchIndex4Label() {\n        return getAttributeValue(ATTRIBUTE_SEARCH_INDEX_4_LABEL);\n    }\n\n    public String getSearchIndex4Value() {\n        return getAttributeValue(ATTRIBUTE_SEARCH_INDEX_4_VALUE);\n    }\n\n    public String getSearchIndex5Label() {\n        return getAttributeValue(ATTRIBUTE_SEARCH_INDEX_5_LABEL);\n    }\n\n    public String getSearchIndex5Value() {\n        return getAttributeValue(ATTRIBUTE_SEARCH_INDEX_5_VALUE);\n    }\n\n    public APIID getCallerId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_CALLER_ID);\n    }\n\n    // SETTERS\n\n    @Override\n    public void setId(final Long id) {\n        setAttribute(ATTRIBUTE_ID, id);\n    }\n\n    @Override\n    public void setId(final String id) {\n        setAttribute(ATTRIBUTE_ID, id);\n    }\n\n    @Override\n    public void setLastUpdateDate(final String date) {\n        setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date);\n    }\n\n    @Override\n    public void setLastUpdateDate(final Date date) {\n        setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date);\n    }\n\n    public void setState(final String state) {\n        setAttribute(ATTRIBUTE_STATE, state);\n    }\n\n    /**\n     * @param date Must be SQL formated date\n     */\n    public void setStartDate(final String date) {\n        setAttribute(ATTRIBUTE_START_DATE, date);\n    }\n\n    public void setStartDate(final Date date) {\n        setAttribute(ATTRIBUTE_START_DATE, date);\n    }\n\n    public void setStartedByUserId(final Long userId) {\n        setAttribute(ATTRIBUTE_STARTED_BY_USER_ID, userId);\n    }\n\n    public void setStartedByUserId(final APIID userId) {\n        setAttribute(ATTRIBUTE_STARTED_BY_USER_ID, userId);\n    }\n\n    public void setStartedByUserId(final String userId) {\n        setAttribute(ATTRIBUTE_STARTED_BY_USER_ID, userId);\n    }\n\n    public void setStartedBySubstituteUserId(final Long userId) {\n        setAttribute(ATTRIBUTE_STARTED_BY_SUBSTITUTE_USER_ID, userId);\n    }\n\n    public void setStartedBySubstituteUserId(final APIID userId) {\n        setAttribute(ATTRIBUTE_STARTED_BY_SUBSTITUTE_USER_ID, userId);\n    }\n\n    public void setStartedBySubstituteUserId(final String userId) {\n        setAttribute(ATTRIBUTE_STARTED_BY_SUBSTITUTE_USER_ID, userId);\n    }\n\n    public void setEndDate(final String date) {\n        setAttribute(ATTRIBUTE_END_DATE, date);\n    }\n\n    public void setEndDate(final Date date) {\n        setAttribute(ATTRIBUTE_END_DATE, date);\n    }\n\n    public void setProcessId(final Long processId) {\n        setProcessId(APIID.makeAPIID(processId));\n    }\n\n    public void setProcessId(final String processId) {\n        setAttribute(ATTRIBUTE_PROCESS_ID, processId);\n    }\n\n    public void setProcessId(final APIID processId) {\n        setAttribute(ATTRIBUTE_PROCESS_ID, processId);\n    }\n\n    public void setRootCaseId(final long rootCaseId) {\n        setAttribute(ATTRIBUTE_ROOT_CASE_ID, rootCaseId);\n    }\n\n    public void setSearchIndex1Label(final String attributeSearchIndex1Label) {\n        setAttribute(ATTRIBUTE_SEARCH_INDEX_1_LABEL, attributeSearchIndex1Label);\n    }\n\n    public void setSearchIndex1Value(final String attributeSearchIndex1Value) {\n        setAttribute(ATTRIBUTE_SEARCH_INDEX_1_VALUE, attributeSearchIndex1Value);\n    }\n\n    public void setSearchIndex2Label(final String attributeSearchIndex2Label) {\n        setAttribute(ATTRIBUTE_SEARCH_INDEX_2_LABEL, attributeSearchIndex2Label);\n    }\n\n    public void setSearchIndex2Value(final String attributeSearchIndex2Value) {\n        setAttribute(ATTRIBUTE_SEARCH_INDEX_2_VALUE, attributeSearchIndex2Value);\n    }\n\n    public void setSearchIndex3Label(final String attributeSearchIndex3Label) {\n        setAttribute(ATTRIBUTE_SEARCH_INDEX_3_LABEL, attributeSearchIndex3Label);\n    }\n\n    public void setSearchIndex3Value(final String attributeSearchIndex3Value) {\n        setAttribute(ATTRIBUTE_SEARCH_INDEX_3_VALUE, attributeSearchIndex3Value);\n    }\n\n    public void setSearchIndex4Label(final String attributeSearchIndex4Label) {\n        setAttribute(ATTRIBUTE_SEARCH_INDEX_4_LABEL, attributeSearchIndex4Label);\n    }\n\n    public void setSearchIndex4Value(final String attributeSearchIndex4Value) {\n        setAttribute(ATTRIBUTE_SEARCH_INDEX_4_VALUE, attributeSearchIndex4Value);\n    }\n\n    public void setSearchIndex5Label(final String attributeSearchIndex5Label) {\n        setAttribute(ATTRIBUTE_SEARCH_INDEX_5_LABEL, attributeSearchIndex5Label);\n    }\n\n    public void setSearchIndex5Value(final String attributeSearchIndex5Value) {\n        setAttribute(ATTRIBUTE_SEARCH_INDEX_5_VALUE, attributeSearchIndex5Value);\n    }\n\n    public void setCallerId(final long attributeCallerId) {\n        setAttribute(ATTRIBUTE_CALLER_ID, attributeCallerId);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // UTILS\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public ItemDefinition getItemDefinition() {\n        return new CaseDefinition();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/cases/CaseVariableDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.cases;\n\nimport static org.bonitasoft.web.rest.model.bpm.cases.CaseVariableItem.ATTRIBUTE_CASE_ID;\nimport static org.bonitasoft.web.rest.model.bpm.cases.CaseVariableItem.ATTRIBUTE_NAME;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.StringMaxLengthValidator;\n\n/**\n * @author Colin PUY\n */\npublic class CaseVariableDefinition extends ItemDefinition<CaseVariableItem> {\n\n    private static final String API_URL = \"../API/bpm/caseVariable\";\n\n    public static final String TOKEN = \"caseVariable\";\n\n    public static CaseVariableDefinition get() {\n        return (CaseVariableDefinition) Definitions.get(TOKEN);\n    }\n\n    @Override\n    protected String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        createAttribute(CaseVariableItem.ATTRIBUTE_NAME, ItemAttribute.TYPE.STRING);\n        createAttribute(CaseVariableItem.ATTRIBUTE_TYPE, ItemAttribute.TYPE.STRING);\n        createAttribute(CaseVariableItem.ATTRIBUTE_DESCRIPTION, ItemAttribute.TYPE.TEXT);\n        createAttribute(CaseVariableItem.ATTRIBUTE_VALUE, ItemAttribute.TYPE.STRING)\n                .removeValidator(StringMaxLengthValidator.class.getName());\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(ATTRIBUTE_CASE_ID, ATTRIBUTE_NAME);\n    }\n\n    @Override\n    protected CaseVariableItem _createItem() {\n        return new CaseVariableItem();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/cases/CaseVariableItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.cases;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Colin PUY\n */\npublic class CaseVariableItem extends Item {\n\n    public static final String ATTRIBUTE_CASE_ID = \"case_id\";\n\n    public static final String ATTRIBUTE_NAME = \"name\";\n\n    public static final String ATTRIBUTE_TYPE = \"type\";\n\n    public static final String ATTRIBUTE_VALUE = \"value\";\n\n    public static final String ATTRIBUTE_DESCRIPTION = \"description\";\n\n    public CaseVariableItem() {\n    }\n\n    public CaseVariableItem(final long caseId, final String name, final Serializable value, final String type,\n            final String description) {\n        setAttribute(ATTRIBUTE_CASE_ID, String.valueOf(caseId));\n        setAttribute(ATTRIBUTE_NAME, name);\n        setAttribute(ATTRIBUTE_VALUE, String.valueOf(value));\n        setAttribute(ATTRIBUTE_TYPE, type);\n        setAttribute(ATTRIBUTE_DESCRIPTION, description);\n    }\n\n    public static CaseVariableItem fromIdAndAttributes(final APIID apiid, final Map<String, String> attributes) {\n        return new CaseVariableItem(apiid.getPartAsLong(ATTRIBUTE_CASE_ID),\n                apiid.getPart(ATTRIBUTE_NAME), attributes.get(ATTRIBUTE_VALUE),\n                attributes.get(ATTRIBUTE_TYPE), attributes.get(ATTRIBUTE_DESCRIPTION));\n    }\n\n    @Override\n    public ItemDefinition getItemDefinition() {\n        return CaseVariableDefinition.get();\n    }\n\n    public long getCaseId() {\n        return getAttributeValueAsLong(ATTRIBUTE_CASE_ID);\n    }\n\n    public String getName() {\n        return getAttributeValue(ATTRIBUTE_NAME);\n    }\n\n    public String getType() {\n        return getAttributeValue(ATTRIBUTE_TYPE);\n    }\n\n    public String getValue() {\n        return getAttributeValue(ATTRIBUTE_VALUE);\n    }\n\n    public String getDescription() {\n        return getAttributeValue(ATTRIBUTE_DESCRIPTION);\n    }\n\n    public void setValue(final String value) {\n        setAttribute(ATTRIBUTE_VALUE, value);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/cases/CommentDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.cases;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * @author Gai Cuisha\n */\npublic class CommentDefinition extends ItemDefinition<CommentItem> {\n\n    /**\n     * Singleton\n     */\n    public static CommentDefinition get() {\n        return (CommentDefinition) Definitions.get(TOKEN);\n    }\n\n    public static final String TOKEN = \"comment\";\n\n    /**\n     * the URL of user resource\n     */\n    private static final String API_URL = \"../API/bpm/comment\";\n\n    @Override\n    public String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(CommentItem.ATTRIBUTE_ID);\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        createAttribute(CommentItem.ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(CommentItem.ATTRIBUTE_USER_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(CommentItem.ATTRIBUTE_PROCESS_INSTANCE_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(CommentItem.ATTRIBUTE_POST_DATE, ItemAttribute.TYPE.DATETIME);\n        createAttribute(CommentItem.ATTRIBUTE_CONTENT, ItemAttribute.TYPE.TEXT)\n                .isMandatory();\n    }\n\n    @Override\n    public CommentItem _createItem() {\n        return new CommentItem();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/cases/CommentItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.cases;\n\nimport java.util.Date;\n\nimport org.bonitasoft.web.rest.model.bpm.flownode.TaskItem;\nimport org.bonitasoft.web.rest.model.identity.UserItem;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId;\n\n/**\n * @author Vincent Elcrin\n */\npublic class CommentItem extends Item implements ItemHasUniqueId {\n\n    public CommentItem() {\n        super();\n    }\n\n    public CommentItem(final IItem item) {\n        super(item);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // ATTRIBUTES NAMES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public static final String ATTRIBUTE_USER_ID = \"userId\";\n\n    public static final String ATTRIBUTE_PROCESS_INSTANCE_ID = \"processInstanceId\";\n\n    public static final String ATTRIBUTE_POST_DATE = \"postDate\";\n\n    public static final String ATTRIBUTE_CONTENT = \"content\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // ATTRIBUTES VALUES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // FILTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public static final String FILTER_TEAM_MANAGER_ID = \"team_manager_id\";\n\n    public static final String FILTER_SUPERVISOR_ID = \"supervisor_id\";\n\n    public static final String FILTER_USER_ID = \"user_id\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // COUNTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // GETTERS AND SETTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // GETTERS\n\n    public APIID getUserId() {\n        return APIID.makeAPIID(this.getAttributeValue(ATTRIBUTE_USER_ID));\n    }\n\n    public APIID getProcessInstanceId() {\n        return APIID.makeAPIID(this.getAttributeValue(ATTRIBUTE_PROCESS_INSTANCE_ID));\n    }\n\n    public String getPostDate() {\n        return this.getAttributeValue(ATTRIBUTE_POST_DATE);\n    }\n\n    public String getContent() {\n        return this.getAttributeValue(ATTRIBUTE_CONTENT);\n    }\n\n    // SETTERS\n\n    @Override\n    public void setId(final String id) {\n        this.setAttribute(ATTRIBUTE_ID, id);\n    }\n\n    @Override\n    public void setId(final Long id) {\n        this.setAttribute(ATTRIBUTE_ID, APIID.makeAPIID(id));\n    }\n\n    public void setUserId(final String id) {\n        this.setAttribute(ATTRIBUTE_USER_ID, id);\n    }\n\n    public void setUserId(final Long id) {\n        this.setAttribute(ATTRIBUTE_USER_ID, APIID.makeAPIID(id));\n    }\n\n    public void setUserId(final APIID id) {\n        this.setAttribute(ATTRIBUTE_USER_ID, id);\n    }\n\n    public void setProcessInstanceId(final String id) {\n        this.setAttribute(ATTRIBUTE_PROCESS_INSTANCE_ID, id);\n    }\n\n    public void setProcessInstanceId(final Long id) {\n        this.setAttribute(ATTRIBUTE_PROCESS_INSTANCE_ID, APIID.makeAPIID(id));\n    }\n\n    public void setProcessInstanceId(final APIID id) {\n        this.setAttribute(ATTRIBUTE_PROCESS_INSTANCE_ID, id);\n    }\n\n    public void setPostDate(final String date) {\n        this.setAttribute(ATTRIBUTE_POST_DATE, date);\n    }\n\n    public void setPostDate(final Long date) {\n        setPostDate(new Date(date));\n    }\n\n    public void setPostDate(final Date date) {\n        this.setAttribute(ATTRIBUTE_POST_DATE, date);\n    }\n\n    public void setContent(final String content) {\n        this.setAttribute(ATTRIBUTE_CONTENT, content);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // DEPLOYS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public UserItem getUser() {\n        return new UserItem(getDeploy(ATTRIBUTE_USER_ID));\n    }\n\n    public TaskItem getProcessInstance() {\n        return new TaskItem(getDeploy(ATTRIBUTE_PROCESS_INSTANCE_ID));\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // UTILS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public ItemDefinition getItemDefinition() {\n        return Definitions.get(CommentDefinition.TOKEN);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/connector/ArchivedConnectorInstanceDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.connector;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * @author Julien Mege\n */\npublic class ArchivedConnectorInstanceDefinition extends ConnectorInstanceDefinition {\n\n    public static final String TOKEN = \"archivedConnectorInstance\";\n\n    protected static final String API_URL = \"../API/bpm/archivedConnectorInstance\";\n\n    @Override\n    protected String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        createAttribute(ArchivedConnectorInstanceItem.ATTRIBUTE_ARCHIVED_DATE, ItemAttribute.TYPE.DATETIME);\n        createAttribute(ArchivedConnectorInstanceItem.ATTRIBUTE_NAME, ItemAttribute.TYPE.ITEM_ID);\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(ArchivedConnectorInstanceItem.ATTRIBUTE_ID);\n    }\n\n    @Override\n    protected Item _createItem() {\n        return new ConnectorInstanceItem();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/connector/ArchivedConnectorInstanceItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.connector;\n\nimport java.util.Date;\n\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId;\n\n/**\n * @author Julien Mege\n */\npublic class ArchivedConnectorInstanceItem extends ConnectorInstanceItem implements ItemHasUniqueId {\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // ATTRIBUTES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public static final String ATTRIBUTE_ARCHIVED_DATE = \"archivedDate\";\n\n    public static final String ATTRIBUTE_SOURCE_OBJECT_ID = \"sourceObjectId\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // FILTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // SETTERS AND GETTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // SETTERS\n    public void setArchivedDate(final Date date) {\n        this.setAttribute(ATTRIBUTE_ARCHIVED_DATE, date);\n    }\n\n    public void setSourceObjectId(final Long id) {\n        this.setAttribute(ATTRIBUTE_CONTAINER_ID, id);\n    }\n\n    public void setSourceObjectId(final String id) {\n        this.setAttribute(ATTRIBUTE_CONTAINER_ID, id);\n    }\n\n    public void setSourceObjectId(final APIID id) {\n        this.setAttribute(ATTRIBUTE_CONTAINER_ID, id);\n    }\n\n    // GETTERS\n    public String getArchivedDate() {\n        return this.getAttributeValue(ATTRIBUTE_ARCHIVED_DATE);\n    }\n\n    public APIID getSourceObjectId(final Long id) {\n        return APIID.makeAPIID(this.getAttributeValue(ATTRIBUTE_SOURCE_OBJECT_ID));\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // DEPLOYS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // UTILS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public ItemDefinition getItemDefinition() {\n        return Definitions.get(ArchivedConnectorInstanceDefinition.TOKEN);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/connector/ConnectorInstanceDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.connector;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * @author Vincent Elcrin\n */\npublic class ConnectorInstanceDefinition extends ItemDefinition {\n\n    public static final String TOKEN = \"connectorInstance\";\n\n    protected static final String API_URL = \"../API/bpm/connectorInstance\";\n\n    @Override\n    protected String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        createAttribute(ConnectorInstanceItem.ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(ConnectorInstanceItem.ATTRIBUTE_NAME, ItemAttribute.TYPE.STRING);\n        createAttribute(ConnectorInstanceItem.ATTRIBUTE_CONNECTOR_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(ConnectorInstanceItem.ATTRIBUTE_VERSION, ItemAttribute.TYPE.STRING);\n        createAttribute(ConnectorInstanceItem.ATTRIBUTE_STATE, ItemAttribute.TYPE.ENUM);\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(ConnectorInstanceItem.ATTRIBUTE_ID);\n    }\n\n    @Override\n    protected Item _createItem() {\n        return new ConnectorInstanceItem();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/connector/ConnectorInstanceItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.connector;\n\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId;\n\n/**\n * @author Vincent Elcrin\n */\npublic class ConnectorInstanceItem extends Item implements ItemHasUniqueId {\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // ATTRIBUTES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public static final String ATTRIBUTE_CONNECTOR_ID = \"connectorId\";\n\n    public static final String ATTRIBUTE_NAME = \"name\";\n\n    public static final String ATTRIBUTE_VERSION = \"version\";\n\n    public static final String ATTRIBUTE_ACTIVATION_EVENT = \"activationEvent\";\n\n    public static final String ATTRIBUTE_STATE = \"state\";\n\n    public static final String ATTRIBUTE_CONTAINER_TYPE = \"containerType\";\n\n    public static final String ATTRIBUTE_CONTAINER_ID = \"containerId\";\n\n    public static final String ATTRIBUTE_RESET_STATE = \"resetState\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // ATTRIBUTES VALUES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public static final String VALUE_STATE_TO_BE_EXECUTED = \"TO_BE_EXECUTED\";\n\n    public static final String VALUE_STATE_TO_RE_EXECUTE = \"TO_RE_EXECUTE\";\n\n    public static final String VALUE_STATE_DONE = \"DONE\";\n\n    public static final String VALUE_STATE_FAILED = \"FAILED\";\n\n    public static final String VALUE_STATE_SKIPPED = \"SKIPPED\";\n\n    public static final String VALUE_RESET_STATE_TO_RE_EXECUTE = \"toReExecute\";\n\n    public static final String VALUE_RESET_STATE_SKIPPED = \"skipped\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // FILTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // SETTERS AND GETTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // SETTERS\n\n    @Override\n    public void setId(String id) {\n        this.setAttribute(ATTRIBUTE_ID, id);\n    }\n\n    @Override\n    public void setId(Long id) {\n        this.setAttribute(ATTRIBUTE_ID, id);\n    }\n\n    public void setName(String name) {\n        this.setAttribute(ATTRIBUTE_NAME, name);\n    }\n\n    public void setConnectorId(String id) {\n        this.setAttribute(ATTRIBUTE_CONNECTOR_ID, id);\n    }\n\n    public void setVersion(String version) {\n        this.setAttribute(ATTRIBUTE_VERSION, version);\n    }\n\n    public void setActivationEvent(String activationEvent) {\n        this.setAttribute(ATTRIBUTE_ACTIVATION_EVENT, activationEvent);\n    }\n\n    public void setState(String state) {\n        this.setAttribute(ATTRIBUTE_STATE, state);\n    }\n\n    public void setContainerType(String type) {\n        this.setAttribute(ATTRIBUTE_CONTAINER_TYPE, type);\n    }\n\n    public void setContainerId(Long id) {\n        this.setAttribute(ATTRIBUTE_CONTAINER_ID, id);\n    }\n\n    // GETTERS\n\n    public String getName() {\n        return this.getAttributeValue(ATTRIBUTE_NAME);\n    }\n\n    public APIID getConnectorId() {\n        return this.getAttributeValueAsAPIID(ATTRIBUTE_CONNECTOR_ID);\n    }\n\n    public String getVersion() {\n        return this.getAttributeValue(ATTRIBUTE_VERSION);\n    }\n\n    public String getState() {\n        return this.getAttributeValue(ATTRIBUTE_STATE);\n    }\n\n    public APIID getContainerId() {\n        return this.getAttributeValueAsAPIID(ATTRIBUTE_CONTAINER_ID);\n    }\n\n    public String getContainerType() {\n        return this.getAttributeValue(ATTRIBUTE_CONTAINER_TYPE);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // DEPLOYS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // UTILS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public ItemDefinition getItemDefinition() {\n        return Definitions.get(ConnectorInstanceDefinition.TOKEN);\n    }\n\n    public boolean hasFailed() {\n        return VALUE_STATE_FAILED.equals(getState());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/ActivityDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.flownode;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.EnumValidator;\n\n/**\n * User definition\n *\n * @author Séverin Moussel\n */\npublic class ActivityDefinition extends FlowNodeDefinition {\n\n    public static final String TOKEN = \"activity\";\n\n    protected static final String API_URL = \"../API/bpm/activity\";\n\n    @Override\n    public String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        super.defineAttributes();\n\n        // Restrict type to activities\n        getAttribute(TaskItem.ATTRIBUTE_TYPE)\n                .removeValidator(EnumValidator.class.getName())\n                .addValidator(new EnumValidator(\n                        TaskItem.VALUE_TYPE_USER_TASK,\n                        TaskItem.VALUE_TYPE_AUTOMATIC_TASK,\n                        TaskItem.VALUE_TYPE_MANUAL_TASK,\n                        TaskItem.VALUE_TYPE_CALL_ACTIVITY,\n                        TaskItem.VALUE_TYPE_LOOP_ACTIVITY));\n\n        createAttribute(TaskItem.ATTRIBUTE_LAST_UPDATE_DATE, ItemAttribute.TYPE.DATETIME);\n\n        createAttribute(TaskItem.ATTRIBUTE_REACHED_STATE_DATE, ItemAttribute.TYPE.DATETIME);\n\n    }\n\n    @Override\n    public IActivityItem _createItem() {\n        return new ActivityItem();\n    }\n\n    public static ActivityDefinition get() {\n        return (ActivityDefinition) Definitions.get(TOKEN);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/ActivityItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.flownode;\n\nimport java.util.Date;\n\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Séverin Moussel\n */\npublic class ActivityItem extends FlowNodeItem implements IActivityItem {\n\n    public static final String ATTRIBUTE_VARIABLES = \"variables\";\n\n    public ActivityItem() {\n        super();\n    }\n\n    public ActivityItem(final IItem item) {\n        super(item);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // SETTERS AND GETTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public final void setReachStateDate(final String reachedStateDate) {\n        setAttribute(ATTRIBUTE_REACHED_STATE_DATE, reachedStateDate);\n    }\n\n    @Override\n    public final void setReachStateDate(final Date reachedStateDate) {\n        setAttribute(ATTRIBUTE_REACHED_STATE_DATE, reachedStateDate);\n    }\n\n    @Override\n    public final Date getReachStateDate() {\n        return this.getAttributeValueAsDate(ATTRIBUTE_REACHED_STATE_DATE);\n    }\n\n    @Override\n    public final void setLastUpdateDate(final String date) {\n        setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date);\n    }\n\n    @Override\n    public final void setLastUpdateDate(final Date date) {\n        setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date);\n    }\n\n    @Override\n    public final Date getLastUpdateDate() {\n        return this.getAttributeValueAsDate(ATTRIBUTE_LAST_UPDATE_DATE);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // UTILS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public ItemDefinition getItemDefinition() {\n        return ActivityDefinition.get();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/ArchivedActivityDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.flownode;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * @author Séverin Moussel\n */\npublic class ArchivedActivityDefinition extends ActivityDefinition {\n\n    public static final String TOKEN = \"archivedActivity\";\n\n    protected static final String API_URL = \"../API/bpm/archivedActivity\";\n\n    @Override\n    public String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        super.defineAttributes();\n        createAttribute(ArchivedActivityItem.ATTRIBUTE_ARCHIVED_DATE, ItemAttribute.TYPE.DATETIME);\n    }\n\n    @Override\n    public ArchivedActivityItem _createItem() {\n        return new ArchivedActivityItem();\n    }\n\n    public static ArchivedActivityDefinition get() {\n        return (ArchivedActivityDefinition) Definitions.get(TOKEN);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/ArchivedActivityItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.flownode;\n\nimport java.util.Date;\n\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Séverin Moussel\n */\npublic class ArchivedActivityItem extends ArchivedFlowNodeItem implements IActivityItem {\n\n    public ArchivedActivityItem() {\n        super();\n    }\n\n    public ArchivedActivityItem(final IItem item) {\n        super(item);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // SETTERS AND GETTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public final void setReachStateDate(final String reachedStateDate) {\n        setAttribute(ATTRIBUTE_REACHED_STATE_DATE, reachedStateDate);\n    }\n\n    @Override\n    public final void setReachStateDate(final Date reachedStateDate) {\n        setAttribute(ATTRIBUTE_REACHED_STATE_DATE, reachedStateDate);\n    }\n\n    @Override\n    public final Date getReachStateDate() {\n        return this.getAttributeValueAsDate(ATTRIBUTE_REACHED_STATE_DATE);\n    }\n\n    @Override\n    public final void setLastUpdateDate(final String date) {\n        setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date);\n    }\n\n    @Override\n    public final void setLastUpdateDate(final Date date) {\n        setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date);\n    }\n\n    @Override\n    public final Date getLastUpdateDate() {\n        return this.getAttributeValueAsDate(ATTRIBUTE_LAST_UPDATE_DATE);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // UTILS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public ItemDefinition getItemDefinition() {\n        return new ArchivedActivityDefinition();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/ArchivedFlowNode.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.flownode;\n\nimport java.util.Date;\n\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Séverin Moussel\n */\npublic interface ArchivedFlowNode {\n\n    String ATTRIBUTE_SOURCE_OBJECT_ID = \"sourceObjectId\";\n\n    String FILTER_IS_TERMINAL = \"isTerminal\";\n\n    String ATTRIBUTE_ARCHIVED_DATE = \"archivedDate\";\n\n    void setArchivedDate(final String date);\n\n    void setArchivedDate(final Date date);\n\n    Date getArchivedDate();\n\n    boolean isArchived();\n\n    void setSourceObjectId(final APIID id);\n\n    void setSourceObjectId(final String id);\n\n    void setSourceObjectId(final Long id);\n\n    APIID getSourceObjectId();\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/ArchivedFlowNodeDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.flownode;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * @author Séverin Moussel\n */\npublic class ArchivedFlowNodeDefinition extends FlowNodeDefinition {\n\n    public static final String TOKEN = \"archivedflownode\";\n\n    private static final String API_URL = \"../API/bpm/archivedFlowNode\";\n\n    @Override\n    public String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        super.defineAttributes();\n        createAttribute(ArchivedFlowNodeItem.ATTRIBUTE_ARCHIVED_DATE, ItemAttribute.TYPE.DATETIME);\n    }\n\n    @Override\n    public ArchivedFlowNodeItem _createItem() {\n        return new ArchivedFlowNodeItem();\n    }\n\n    public static ArchivedFlowNodeDefinition get() {\n        return (ArchivedFlowNodeDefinition) Definitions.get(TOKEN);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/ArchivedFlowNodeItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.flownode;\n\nimport java.util.Date;\n\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Séverin Moussel\n */\npublic class ArchivedFlowNodeItem extends FlowNodeItem implements ArchivedFlowNode {\n\n    public ArchivedFlowNodeItem() {\n        super();\n    }\n\n    public ArchivedFlowNodeItem(final IItem item) {\n        super(item);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // GETTERS AND SETTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public final void setArchivedDate(final String archivedDate) {\n        this.setAttribute(ATTRIBUTE_ARCHIVED_DATE, archivedDate);\n    }\n\n    @Override\n    public final void setArchivedDate(final Date date) {\n        this.setAttribute(ATTRIBUTE_ARCHIVED_DATE, date);\n    }\n\n    @Override\n    public Date getArchivedDate() {\n        return getAttributeValueAsDate(ATTRIBUTE_ARCHIVED_DATE);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // UTILS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public ItemDefinition getItemDefinition() {\n        return new ArchivedFlowNodeDefinition();\n    }\n\n    public final boolean isArchived() {\n        return true;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see\n     * org.bonitasoft.console.client.model.bpm.flownode.ArchivedFlowNode#setSourceObjectId(org.bonitasoft.web.toolkit.\n     * client.data.APIID)\n     */\n    @Override\n    public void setSourceObjectId(APIID id) {\n        this.setAttribute(ATTRIBUTE_SOURCE_OBJECT_ID, id);\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see org.bonitasoft.console.client.model.bpm.flownode.ArchivedFlowNode#setSourceObjectId(java.lang.String)\n     */\n    @Override\n    public void setSourceObjectId(String id) {\n        this.setAttribute(ATTRIBUTE_SOURCE_OBJECT_ID, id);\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see org.bonitasoft.console.client.model.bpm.flownode.ArchivedFlowNode#setSourceObjectId(java.lang.Long)\n     */\n    @Override\n    public void setSourceObjectId(Long id) {\n        this.setAttribute(ATTRIBUTE_SOURCE_OBJECT_ID, id);\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see org.bonitasoft.console.client.model.bpm.flownode.ArchivedFlowNode#getSourceObjectId()\n     */\n    @Override\n    public APIID getSourceObjectId() {\n        return this.getAttributeValueAsAPIID(ATTRIBUTE_SOURCE_OBJECT_ID);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/ArchivedHumanTaskDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.flownode;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * @author Séverin Moussel\n */\npublic class ArchivedHumanTaskDefinition extends HumanTaskDefinition {\n\n    public static final String TOKEN = \"archivedhumantask\";\n\n    /**\n     * the URL of users resource\n     */\n    private static final String API_URL = \"../API/bpm/archivedHumanTask\";\n\n    @Override\n    public String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        super.defineAttributes();\n        createAttribute(ArchivedHumanTaskItem.ATTRIBUTE_ARCHIVED_DATE, ItemAttribute.TYPE.DATETIME);\n    }\n\n    @Override\n    public ArchivedHumanTaskItem _createItem() {\n        return new ArchivedHumanTaskItem();\n    }\n\n    public static ArchivedHumanTaskDefinition get() {\n        return (ArchivedHumanTaskDefinition) Definitions.get(TOKEN);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/ArchivedHumanTaskItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.flownode;\n\nimport java.util.Date;\n\nimport org.bonitasoft.web.rest.model.bpm.process.ActorItem;\nimport org.bonitasoft.web.rest.model.identity.UserItem;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Séverin Moussel\n */\npublic class ArchivedHumanTaskItem extends ArchivedTaskItem implements IHumanTaskItem {\n\n    public ArchivedHumanTaskItem() {\n        super();\n    }\n\n    public ArchivedHumanTaskItem(final IItem item) {\n        super(item);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // ATTRIBUTES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // SETTERS\n\n    @Override\n    public void setAssignedId(final String id) {\n        setAttribute(ATTRIBUTE_ASSIGNED_USER_ID, id);\n    }\n\n    @Override\n    public void setAssignedId(final APIID id) {\n        setAttribute(ATTRIBUTE_ASSIGNED_USER_ID, id);\n    }\n\n    @Override\n    public void setAssignedId(final Long id) {\n        setAttribute(ATTRIBUTE_ASSIGNED_USER_ID, id);\n    }\n\n    @Override\n    public void setAssignedDate(final String date) {\n        setAttribute(ATTRIBUTE_ASSIGNED_DATE, date);\n    }\n\n    @Override\n    public void setAssignedDate(final Date date) {\n        setAttribute(ATTRIBUTE_ASSIGNED_DATE, date);\n    }\n\n    @Override\n    public void setPriority(final String priority) {\n        setAttribute(ATTRIBUTE_PRIORITY, priority);\n    }\n\n    @Override\n    public void setDueDate(final String date) {\n        setAttribute(ATTRIBUTE_DUE_DATE, date);\n    }\n\n    @Override\n    public void setDueDate(final Date date) {\n        setAttribute(ATTRIBUTE_DUE_DATE, date);\n    }\n\n    @Override\n    public void setActorId(final APIID id) {\n        setAttribute(ATTRIBUTE_ACTOR_ID, id);\n    }\n\n    @Override\n    public void setActorId(final String actorId) {\n        setAttribute(ATTRIBUTE_ACTOR_ID, actorId);\n    }\n\n    @Override\n    public void setActorId(final Long actorId) {\n        setAttribute(ATTRIBUTE_ACTOR_ID, actorId);\n    }\n\n    // Counters\n    @Override\n    public void setNbOfAttachment(final String count) {\n        setAttribute(COUNT_ATTACHMENT_NUMBER, count);\n    }\n\n    @Override\n    public void setNbOfAttachment(final int count) {\n        setAttribute(COUNT_ATTACHMENT_NUMBER, count);\n    }\n\n    @Override\n    public void setNbOfComment(final String count) {\n        setAttribute(COUNT_COMMENT_NUMBER, count);\n    }\n\n    @Override\n    public void setNbOfComment(final int count) {\n        setAttribute(COUNT_COMMENT_NUMBER, count);\n    }\n\n    @Override\n    public void setNbOfActorUser(final String count) {\n        setAttribute(COUNT_ACTOR_USER_NUMBER, count);\n    }\n\n    @Override\n    public void setNbOfActorUser(final int count) {\n        setAttribute(COUNT_ACTOR_USER_NUMBER, count);\n    }\n\n    // GETTERS\n\n    @Override\n    public final APIID getActorId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_ACTOR_ID);\n    }\n\n    @Override\n    public String getPriority() {\n        return this.getAttributeValue(ATTRIBUTE_PRIORITY);\n    }\n\n    @Override\n    public String getDueDate() {\n        return this.getAttributeValue(ATTRIBUTE_DUE_DATE);\n    }\n\n    @Override\n    public APIID getAssignedId() {\n        return this.getAttributeValueAsAPIID(ATTRIBUTE_ASSIGNED_USER_ID);\n    }\n\n    @Override\n    public String getAssignedDate() {\n        return this.getAttributeValue(ATTRIBUTE_ASSIGNED_DATE);\n    }\n\n    // Counters\n    @Override\n    public Integer getNbOfAttachment() {\n        return Integer.parseInt(this.getAttributeValue(COUNT_ATTACHMENT_NUMBER));\n    }\n\n    @Override\n    public Integer getNbOfComment() {\n        return Integer.parseInt(this.getAttributeValue(COUNT_COMMENT_NUMBER));\n    }\n\n    @Override\n    public Integer getNbOfActorUser() {\n        return Integer.parseInt(this.getAttributeValue(COUNT_ACTOR_USER_NUMBER));\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // DEPLOYS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public UserItem getAssignedUser() {\n        return new UserItem(getDeploy(ATTRIBUTE_ASSIGNED_USER_ID));\n    }\n\n    @Override\n    public final ActorItem getActor() {\n        return new ActorItem(getDeploy(ATTRIBUTE_ACTOR_ID));\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // UTILS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public ItemDefinition getItemDefinition() {\n        return new HumanTaskDefinition();\n    }\n\n    @Override\n    public boolean isAssigned() {\n        return getAssignedId() != null;\n    }\n\n    @Override\n    public boolean isUnassigned() {\n        return !isAssigned();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/ArchivedTaskDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.flownode;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * @author Séverin Moussel\n */\npublic class ArchivedTaskDefinition extends TaskDefinition {\n\n    public static final String TOKEN = \"archivedtask\";\n\n    private static final String API_URL = \"../API/bpm/archivedTask\";\n\n    @Override\n    public String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        super.defineAttributes();\n        createAttribute(ArchivedTaskItem.ATTRIBUTE_ARCHIVED_DATE, ItemAttribute.TYPE.DATETIME);\n    }\n\n    @Override\n    public ArchivedTaskItem _createItem() {\n        return new ArchivedTaskItem();\n    }\n\n    public static ArchivedTaskDefinition get() {\n        return (ArchivedTaskDefinition) Definitions.get(TOKEN);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/ArchivedTaskItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.flownode;\n\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Séverin Moussel\n */\npublic class ArchivedTaskItem extends ArchivedActivityItem implements ITaskItem {\n\n    public ArchivedTaskItem() {\n        super();\n    }\n\n    public ArchivedTaskItem(final IItem item) {\n        super(item);\n    }\n\n    @Override\n    public ItemDefinition getItemDefinition() {\n        return new ArchivedTaskDefinition();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/ArchivedUserTaskDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.flownode;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * @author Séverin Moussel\n */\npublic class ArchivedUserTaskDefinition extends UserTaskDefinition {\n\n    public static final String TOKEN = \"archivedusertask\";\n\n    private static final String API_URL = \"../API/bpm/archivedUserTask\";\n\n    @Override\n    public String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        super.defineAttributes();\n        createAttribute(ArchivedUserTaskItem.ATTRIBUTE_ARCHIVED_DATE, ItemAttribute.TYPE.DATETIME);\n    }\n\n    @Override\n    public ArchivedUserTaskItem _createItem() {\n        return new ArchivedUserTaskItem();\n    }\n\n    public static ArchivedUserTaskDefinition get() {\n        return (ArchivedUserTaskDefinition) Definitions.get(TOKEN);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/ArchivedUserTaskItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.flownode;\n\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Séverin Moussel\n */\npublic class ArchivedUserTaskItem extends ArchivedHumanTaskItem implements IUserTaskItem {\n\n    public ArchivedUserTaskItem() {\n        super();\n    }\n\n    public ArchivedUserTaskItem(final IItem item) {\n        super(item);\n    }\n\n    @Override\n    public ItemDefinition getItemDefinition() {\n        return new ArchivedUserTaskDefinition();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/FlowNodeDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.flownode;\n\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseDefinition;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessDefinition;\nimport org.bonitasoft.web.rest.model.identity.UserDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.modifier.ReplaceRegexpModifier;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.EnumValidator;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.StringRegexpValidator;\n\n/**\n * @author Séverin Moussel\n */\npublic class FlowNodeDefinition extends ItemDefinition {\n\n    public static final String TOKEN = \"flownode\";\n\n    public static final String API_URL = \"../API/bpm/flowNode\";\n\n    private static final String ATTRIBUTE_NAME_FORBIDDEN_CHARACTERS = \":/\\\\?#\\\\[\\\\]@!\\\\$&'\\\\(\\\\)\\\\*\\\\+,;=\";\n\n    @Override\n    protected String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        createAttribute(FlowNodeItem.ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID);\n\n        createAttribute(FlowNodeItem.ATTRIBUTE_NAME, ItemAttribute.TYPE.STRING)\n                .isMandatory()\n                .addValidator(new StringRegexpValidator(ATTRIBUTE_NAME_FORBIDDEN_CHARACTERS, true))\n                .addInputModifier(new ReplaceRegexpModifier(ATTRIBUTE_NAME_FORBIDDEN_CHARACTERS, \"_\"));\n\n        createAttribute(FlowNodeItem.ATTRIBUTE_DISPLAY_NAME, ItemAttribute.TYPE.STRING);\n\n        createAttribute(FlowNodeItem.ATTRIBUTE_DESCRIPTION, ItemAttribute.TYPE.TEXT);\n\n        createAttribute(FlowNodeItem.ATTRIBUTE_DISPLAY_DESCRIPTION, ItemAttribute.TYPE.TEXT);\n\n        createAttribute(FlowNodeItem.ATTRIBUTE_STATE, ItemAttribute.TYPE.ENUM)\n                .setDefaultValue(FlowNodeItem.VALUE_STATE_READY)\n                .addValidator(new EnumValidator(\n                        FlowNodeItem.VALUE_STATE_READY,\n                        FlowNodeItem.VALUE_STATE_COMPLETED,\n                        FlowNodeItem.VALUE_STATE_FAILED,\n                        FlowNodeItem.VALUE_STATE_REPLAY,\n                        FlowNodeItem.VALUE_STATE_SKIPPED));\n\n        createAttribute(FlowNodeItem.ATTRIBUTE_CASE_ID, ItemAttribute.TYPE.ITEM_ID)\n                .isMandatory();\n\n        createAttribute(FlowNodeItem.ATTRIBUTE_ROOT_CONTAINER_ID, ItemAttribute.TYPE.ITEM_ID);\n\n        createAttribute(FlowNodeItem.ATTRIBUTE_PROCESS_ID, ItemAttribute.TYPE.ITEM_ID)\n                .isMandatory();\n\n        createAttribute(FlowNodeItem.ATTRIBUTE_TYPE, ItemAttribute.TYPE.ENUM)\n                .addValidator(new EnumValidator(\n                        FlowNodeItem.VALUE_TYPE_USER_TASK,\n                        FlowNodeItem.VALUE_TYPE_AUTOMATIC_TASK,\n                        FlowNodeItem.VALUE_TYPE_MANUAL_TASK,\n                        FlowNodeItem.VALUE_TYPE_BOUNDARY_EVENT,\n                        FlowNodeItem.VALUE_TYPE_CALL_ACTIVITY,\n                        FlowNodeItem.VALUE_TYPE_END_EVENT,\n                        FlowNodeItem.VALUE_TYPE_GATEWAY,\n                        FlowNodeItem.VALUE_TYPE_INTERMEDIATE_CATCH_EVENT,\n                        FlowNodeItem.VALUE_TYPE_INTERMEDIATE_THROW_EVENT,\n                        FlowNodeItem.VALUE_TYPE_LOOP_ACTIVITY,\n                        FlowNodeItem.VALUE_TYPE_START_EVENT));\n\n        createAttribute(FlowNodeItem.ATTRIBUTE_EXECUTED_BY_USER_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(FlowNodeItem.ATTRIBUTE_EXECUTED_BY_SUBSTITUTE_USER_ID, ItemAttribute.TYPE.ITEM_ID);\n    }\n\n    @Override\n    protected void defineDeploys() {\n        super.defineDeploys();\n        declareDeployable(FlowNodeItem.ATTRIBUTE_PROCESS_ID, Definitions.get(ProcessDefinition.TOKEN));\n        declareDeployable(FlowNodeItem.ATTRIBUTE_CASE_ID, Definitions.get(CaseDefinition.TOKEN));\n        declareDeployable(FlowNodeItem.ATTRIBUTE_ROOT_CONTAINER_ID, Definitions.get(ProcessDefinition.TOKEN));\n        declareDeployable(FlowNodeItem.ATTRIBUTE_EXECUTED_BY_USER_ID, UserDefinition.get());\n        declareDeployable(FlowNodeItem.ATTRIBUTE_EXECUTED_BY_SUBSTITUTE_USER_ID, UserDefinition.get());\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(FlowNodeItem.ATTRIBUTE_ID);\n    }\n\n    @Override\n    protected IFlowNodeItem _createItem() {\n        return new FlowNodeItem();\n    }\n\n    public static FlowNodeDefinition get() {\n        return (FlowNodeDefinition) Definitions.get(TOKEN);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/FlowNodeItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.flownode;\n\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseItem;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessItem;\nimport org.bonitasoft.web.rest.model.identity.UserItem;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Séverin Moussel\n */\npublic class FlowNodeItem extends Item implements IFlowNodeItem {\n\n    public FlowNodeItem() {\n        super();\n    }\n\n    public FlowNodeItem(final IItem item) {\n        super(item);\n    }\n\n    @Override\n    public final void setDescription(final String description) {\n        this.setAttribute(ATTRIBUTE_DESCRIPTION, description);\n    }\n\n    @Override\n    public final void setDisplayDescription(final String displayDescription) {\n        this.setAttribute(ATTRIBUTE_DISPLAY_DESCRIPTION, displayDescription);\n    }\n\n    @Override\n    public final String getDescription() {\n        return getAttributeValue(ATTRIBUTE_DESCRIPTION);\n    }\n\n    @Override\n    public final String getDisplayDescription() {\n        return getAttributeValue(ATTRIBUTE_DISPLAY_DESCRIPTION);\n    }\n\n    @Override\n    public final void setName(final String name) {\n        this.setAttribute(ATTRIBUTE_NAME, name);\n    }\n\n    @Override\n    public final void setDisplayName(final String displayName) {\n        this.setAttribute(ATTRIBUTE_DISPLAY_NAME, displayName);\n    }\n\n    @Override\n    public final String getName() {\n        return getAttributeValue(ATTRIBUTE_NAME);\n    }\n\n    @Override\n    public final String getDisplayName() {\n        return getAttributeValue(ATTRIBUTE_DISPLAY_NAME);\n    }\n\n    @Override\n    public final void setId(final String id) {\n        this.setAttribute(ATTRIBUTE_ID, id);\n    }\n\n    @Override\n    public final void setId(final Long id) {\n        this.setAttribute(ATTRIBUTE_ID, id);\n    }\n\n    @Override\n    public final void setProcessId(final APIID id) {\n        setAttribute(ATTRIBUTE_PROCESS_ID, id);\n    }\n\n    @Override\n    public final void setProcessId(final String id) {\n        setAttribute(ATTRIBUTE_PROCESS_ID, id);\n    }\n\n    @Override\n    public final void setProcessId(final Long id) {\n        setAttribute(ATTRIBUTE_PROCESS_ID, id);\n    }\n\n    @Override\n    public final APIID getProcessId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_PROCESS_ID);\n    }\n\n    @Override\n    @Deprecated\n    public final void setCaseId(final APIID id) {\n        setRootCaseId(id);\n    }\n\n    @Override\n    @Deprecated\n    public final void setCaseId(final String id) {\n        setRootCaseId(id);\n    }\n\n    @Override\n    @Deprecated\n    public final void setCaseId(final Long id) {\n        setRootCaseId(id);\n    }\n\n    @Override\n    @Deprecated\n    public final APIID getCaseId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_CASE_ID);\n    }\n\n    @Override\n    public void setRootCaseId(final APIID id) {\n        setAttribute(ATTRIBUTE_ROOT_CASE_ID, id);\n        setAttribute(ATTRIBUTE_CASE_ID, id);\n    }\n\n    @Override\n    public void setRootCaseId(final String id) {\n        setAttribute(ATTRIBUTE_ROOT_CASE_ID, id);\n        setAttribute(ATTRIBUTE_CASE_ID, id);\n    }\n\n    @Override\n    public void setRootCaseId(final Long id) {\n        setAttribute(ATTRIBUTE_ROOT_CASE_ID, id);\n        setAttribute(ATTRIBUTE_CASE_ID, id);\n    }\n\n    @Override\n    public APIID getRootCaseId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_ROOT_CASE_ID);\n    }\n\n    @Override\n    public void setParentCaseId(final APIID id) {\n        setAttribute(ATTRIBUTE_PARENT_CASE_ID, id);\n    }\n\n    @Override\n    public void setParentCaseId(final String id) {\n        setAttribute(ATTRIBUTE_PARENT_CASE_ID, id);\n    }\n\n    @Override\n    public void setParentCaseId(final Long id) {\n        setAttribute(ATTRIBUTE_PARENT_CASE_ID, id);\n    }\n\n    @Override\n    public APIID getParentCaseId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_PARENT_CASE_ID);\n    }\n\n    @Override\n    public final void setRootContainerId(final APIID rootContainerId) {\n        setAttribute(ATTRIBUTE_ROOT_CONTAINER_ID, rootContainerId);\n    }\n\n    @Override\n    public final void setRootContainerId(final String rootContainerId) {\n        setAttribute(ATTRIBUTE_ROOT_CONTAINER_ID, rootContainerId);\n    }\n\n    @Override\n    public final void setRootContainerId(final Long rootContainerId) {\n        setAttribute(ATTRIBUTE_ROOT_CONTAINER_ID, rootContainerId);\n    }\n\n    @Override\n    public APIID getRootContainerId() {\n        return this.getAttributeValueAsAPIID(ATTRIBUTE_ROOT_CONTAINER_ID);\n    }\n\n    @Override\n    public final void setState(final String state) {\n        setAttribute(ATTRIBUTE_STATE, state);\n    }\n\n    @Override\n    public final String getState() {\n        return getAttributeValue(ATTRIBUTE_STATE);\n    }\n\n    @Override\n    public final void setType(final String type) {\n        setAttribute(ATTRIBUTE_TYPE, type);\n    }\n\n    @Override\n    public final String getType() {\n        return getAttributeValue(ATTRIBUTE_TYPE);\n    }\n\n    @Override\n    public final void setExecutedByUserId(final APIID id) {\n        setAttribute(ATTRIBUTE_EXECUTED_BY_USER_ID, id);\n    }\n\n    @Override\n    public final void setExecutedByUserId(final String id) {\n        setAttribute(ATTRIBUTE_EXECUTED_BY_USER_ID, id);\n    }\n\n    @Override\n    public final void setExecutedByUserId(final Long id) {\n        setAttribute(ATTRIBUTE_EXECUTED_BY_USER_ID, id);\n    }\n\n    @Override\n    public final APIID getExecutedByUserId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_EXECUTED_BY_USER_ID);\n    }\n\n    @Override\n    public final UserItem getExecutedByUser() {\n        return new UserItem(getDeploy(ATTRIBUTE_EXECUTED_BY_USER_ID));\n    }\n\n    @Override\n    public void setExecutedBySubstituteUserId(final APIID id) {\n        setAttribute(ATTRIBUTE_EXECUTED_BY_SUBSTITUTE_USER_ID, id);\n    }\n\n    @Override\n    public void setExecutedBySubstituteUserId(final String id) {\n        setAttribute(ATTRIBUTE_EXECUTED_BY_SUBSTITUTE_USER_ID, id);\n    }\n\n    @Override\n    public void setExecutedBySubstituteUserId(final Long id) {\n        setAttribute(ATTRIBUTE_EXECUTED_BY_SUBSTITUTE_USER_ID, id);\n    }\n\n    @Override\n    public APIID getExecutedBySubstituteUserId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_EXECUTED_BY_SUBSTITUTE_USER_ID);\n    }\n\n    @Override\n    public UserItem getExecutedBySubstituteUser() {\n        return new UserItem(getDeploy(ATTRIBUTE_EXECUTED_BY_SUBSTITUTE_USER_ID));\n    }\n\n    @Override\n    public final ProcessItem getProcess() {\n        return new ProcessItem(getDeploy(ATTRIBUTE_PROCESS_ID));\n    }\n\n    @Override\n    @Deprecated\n    public final CaseItem getCase() {\n        return new CaseItem(getDeploy(ATTRIBUTE_CASE_ID));\n    }\n\n    @Override\n    public final CaseItem getRootCase() {\n        return new CaseItem(getDeploy(ATTRIBUTE_ROOT_CASE_ID));\n    }\n\n    @Override\n    public final CaseItem getParentCase() {\n        return new CaseItem(getDeploy(ATTRIBUTE_PARENT_CASE_ID));\n    }\n\n    @Override\n    public final ProcessItem getRootContainerProcess() {\n        return new ProcessItem(getDeploy(ATTRIBUTE_ROOT_CONTAINER_ID));\n    }\n\n    @Override\n    public ItemDefinition getItemDefinition() {\n        return FlowNodeDefinition.get();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/HumanTaskDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.flownode;\n\nimport org.bonitasoft.web.rest.model.bpm.process.ActorDefinition;\nimport org.bonitasoft.web.rest.model.identity.UserDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.EnumValidator;\n\n/**\n * @author Séverin Moussel\n */\npublic class HumanTaskDefinition extends TaskDefinition {\n\n    public static final String TOKEN = \"humantask\";\n\n    private static final String API_URL = \"../API/bpm/humanTask\";\n\n    @Override\n    public String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        super.defineAttributes();\n\n        // Restrict type to Humantasks\n        getAttribute(TaskItem.ATTRIBUTE_TYPE)\n                .removeValidator(EnumValidator.class.getName())\n                .addValidator(new EnumValidator(\n                        TaskItem.VALUE_TYPE_USER_TASK,\n                        TaskItem.VALUE_TYPE_MANUAL_TASK));\n\n        createAttribute(HumanTaskItem.ATTRIBUTE_ACTOR_ID, ItemAttribute.TYPE.ITEM_ID);\n\n        createAttribute(HumanTaskItem.ATTRIBUTE_ASSIGNED_USER_ID, ItemAttribute.TYPE.ITEM_ID);\n\n        createAttribute(HumanTaskItem.ATTRIBUTE_ASSIGNED_DATE, ItemAttribute.TYPE.DATETIME);\n\n        createAttribute(HumanTaskItem.ATTRIBUTE_PRIORITY, ItemAttribute.TYPE.ENUM)\n                .setDefaultValue(HumanTaskItem.VALUE_PRIORITY_NORMAL)\n                .addValidator(new EnumValidator(\n                        HumanTaskItem.VALUE_PRIORITY_LOWEST,\n                        HumanTaskItem.VALUE_PRIORITY_UNDER_NORMAL,\n                        HumanTaskItem.VALUE_PRIORITY_NORMAL,\n                        HumanTaskItem.VALUE_PRIORITY_ABOVE_NORMAL,\n                        HumanTaskItem.VALUE_PRIORITY_HIGHEST));\n\n        createAttribute(HumanTaskItem.ATTRIBUTE_DUE_DATE, ItemAttribute.TYPE.DATETIME);\n    }\n\n    @Override\n    protected void defineDeploys() {\n        super.defineDeploys();\n        declareDeployable(HumanTaskItem.ATTRIBUTE_ASSIGNED_USER_ID, Definitions.get(UserDefinition.TOKEN));\n        declareDeployable(HumanTaskItem.ATTRIBUTE_ACTOR_ID, Definitions.get(ActorDefinition.TOKEN));\n    }\n\n    @Override\n    public IHumanTaskItem _createItem() {\n        return new HumanTaskItem();\n    }\n\n    public static HumanTaskDefinition get() {\n        return (HumanTaskDefinition) Definitions.get(TOKEN);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/HumanTaskItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.flownode;\n\nimport java.util.Date;\n\nimport org.bonitasoft.web.rest.model.bpm.process.ActorItem;\nimport org.bonitasoft.web.rest.model.identity.UserItem;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Séverin Moussel\n */\npublic class HumanTaskItem extends TaskItem implements IHumanTaskItem {\n\n    public HumanTaskItem() {\n        super();\n    }\n\n    public HumanTaskItem(final IItem item) {\n        super(item);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // ATTRIBUTES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // SETTERS\n\n    @Override\n    public final void setAssignedId(final String id) {\n        setAttribute(ATTRIBUTE_ASSIGNED_USER_ID, id);\n    }\n\n    @Override\n    public final void setAssignedId(final APIID id) {\n        setAttribute(ATTRIBUTE_ASSIGNED_USER_ID, id);\n    }\n\n    @Override\n    public final void setAssignedId(final Long id) {\n        setAttribute(ATTRIBUTE_ASSIGNED_USER_ID, id);\n    }\n\n    @Override\n    public final void setAssignedDate(final String date) {\n        setAttribute(ATTRIBUTE_ASSIGNED_DATE, date);\n    }\n\n    @Override\n    public final void setAssignedDate(final Date date) {\n        setAttribute(ATTRIBUTE_ASSIGNED_DATE, date);\n    }\n\n    @Override\n    public final void setPriority(final String priority) {\n        setAttribute(ATTRIBUTE_PRIORITY, priority);\n    }\n\n    @Override\n    public final void setDueDate(final String date) {\n        setAttribute(ATTRIBUTE_DUE_DATE, date);\n    }\n\n    @Override\n    public final void setDueDate(final Date date) {\n        setAttribute(ATTRIBUTE_DUE_DATE, date);\n    }\n\n    @Override\n    public final void setActorId(final APIID id) {\n        setAttribute(ATTRIBUTE_ACTOR_ID, id);\n    }\n\n    @Override\n    public final void setActorId(final String actorId) {\n        setAttribute(ATTRIBUTE_ACTOR_ID, actorId);\n    }\n\n    @Override\n    public final void setActorId(final Long actorId) {\n        setAttribute(ATTRIBUTE_ACTOR_ID, actorId);\n    }\n\n    // Counters\n    @Override\n    public final void setNbOfAttachment(final String count) {\n        setAttribute(COUNT_ATTACHMENT_NUMBER, count);\n    }\n\n    @Override\n    public final void setNbOfAttachment(final int count) {\n        setAttribute(COUNT_ATTACHMENT_NUMBER, count);\n    }\n\n    @Override\n    public final void setNbOfComment(final String count) {\n        setAttribute(COUNT_COMMENT_NUMBER, count);\n    }\n\n    @Override\n    public final void setNbOfComment(final int count) {\n        setAttribute(COUNT_COMMENT_NUMBER, count);\n    }\n\n    @Override\n    public final void setNbOfActorUser(final String count) {\n        setAttribute(COUNT_ACTOR_USER_NUMBER, count);\n    }\n\n    @Override\n    public final void setNbOfActorUser(final int count) {\n        setAttribute(COUNT_ACTOR_USER_NUMBER, count);\n    }\n\n    // GETTERS\n\n    @Override\n    public final APIID getActorId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_ACTOR_ID);\n    }\n\n    @Override\n    public final String getPriority() {\n        return this.getAttributeValue(ATTRIBUTE_PRIORITY);\n    }\n\n    @Override\n    public final String getDueDate() {\n        return this.getAttributeValue(ATTRIBUTE_DUE_DATE);\n    }\n\n    @Override\n    public final APIID getAssignedId() {\n        return this.getAttributeValueAsAPIID(ATTRIBUTE_ASSIGNED_USER_ID);\n    }\n\n    @Override\n    public final String getAssignedDate() {\n        return this.getAttributeValue(ATTRIBUTE_ASSIGNED_DATE);\n    }\n\n    // Counters\n    @Override\n    public final Integer getNbOfAttachment() {\n        return Integer.parseInt(this.getAttributeValue(COUNT_ATTACHMENT_NUMBER));\n    }\n\n    @Override\n    public final Integer getNbOfComment() {\n        return Integer.parseInt(this.getAttributeValue(COUNT_COMMENT_NUMBER));\n    }\n\n    @Override\n    public final Integer getNbOfActorUser() {\n        return Integer.parseInt(this.getAttributeValue(COUNT_ACTOR_USER_NUMBER));\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // DEPLOYS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public final UserItem getAssignedUser() {\n        IItem item = getDeploy(ATTRIBUTE_ASSIGNED_USER_ID);\n        if (item == null) {\n            return null;\n        }\n        return new UserItem(item);\n    }\n\n    @Override\n    public final ActorItem getActor() {\n        return new ActorItem(getDeploy(ATTRIBUTE_ACTOR_ID));\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // UTILS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public ItemDefinition getItemDefinition() {\n        return new HumanTaskDefinition();\n    }\n\n    @Override\n    public final boolean isAssigned() {\n        return getAssignedId() != null;\n    }\n\n    @Override\n    public final boolean isUnassigned() {\n        return !isAssigned();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/IActivityItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.flownode;\n\nimport java.util.Date;\n\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasLastUpdateDate;\n\n/**\n * @author Séverin Moussel\n */\npublic interface IActivityItem extends ItemHasLastUpdateDate, IFlowNodeItem {\n\n    // // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // ATTRIBUTES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    String ATTRIBUTE_REACHED_STATE_DATE = \"reached_state_date\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // FILTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    String FILTER_SUPERVISOR_ID = \"supervisor_id\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // SETTERS AND GETTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    void setReachStateDate(final String reachedStateDate);\n\n    void setReachStateDate(final Date reachedStateDate);\n\n    Date getReachStateDate();\n\n    @Override\n    void setLastUpdateDate(final String date);\n\n    @Override\n    void setLastUpdateDate(final Date date);\n\n    @Override\n    Date getLastUpdateDate();\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/IFlowNodeItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.flownode;\n\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseItem;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessItem;\nimport org.bonitasoft.web.rest.model.identity.UserItem;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasDualDescription;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasDualName;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId;\n\n/**\n * @author Séverin Moussel\n */\npublic interface IFlowNodeItem extends IItem, ItemHasUniqueId, ItemHasDualName, ItemHasDualDescription {\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // ATTRIBUTES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    String ATTRIBUTE_PROCESS_ID = \"processId\";\n\n    String ATTRIBUTE_CASE_ID = \"caseId\";\n\n    /**\n     * @since 6.4.0\n     */\n    String ATTRIBUTE_ROOT_CASE_ID = \"rootCaseId\";\n\n    /**\n     * @since 6.4.0\n     */\n    String ATTRIBUTE_PARENT_CASE_ID = \"parentCaseId\";\n\n    String ATTRIBUTE_PARENT_ACTIVITY_INSTANCE_ID = \"parentActivityInstanceId\";\n\n    String ATTRIBUTE_ROOT_CONTAINER_ID = \"rootContainerId\";\n\n    String ATTRIBUTE_STATE = \"state\";\n\n    String ATTRIBUTE_TYPE = \"type\";\n\n    String ATTRIBUTE_EXECUTED_BY_USER_ID = \"executedBy\";\n\n    String ATTRIBUTE_EXECUTED_BY_SUBSTITUTE_USER_ID = \"executedBySubstitute\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // ATTRIBUTES VALUES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    String VALUE_STATE_FAILED = \"failed\";\n\n    String VALUE_STATE_READY = \"ready\";\n\n    String VALUE_STATE_COMPLETED = \"completed\";\n\n    String VALUE_STATE_SKIPPED = \"skipped\";\n\n    String VALUE_STATE_REPLAY = \"replay\";\n\n    String VALUE_STATE_PENDING = \"pending\";\n\n    String VALUE_STATE_ONGOING = \"ongoing\";\n\n    String VALUE_TYPE_AUTOMATIC_TASK = \"AUTOMATIC_TASK\";\n\n    String VALUE_TYPE_USER_TASK = \"USER_TASK\";\n\n    String VALUE_TYPE_MANUAL_TASK = \"MANUAL_TASK\";\n\n    String VALUE_TYPE_CALL_ACTIVITY = \"CALL_ACTIVITY\";\n\n    String VALUE_TYPE_LOOP_ACTIVITY = \"LOOP_ACTIVITY\";\n\n    String VALUE_TYPE_MULTI_INSTANCE_ACTIVITY = \"MULTI_INSTANCE_ACTIVITY\";\n\n    String VALUE_TYPE_SUB_PROCESS_ACTIVITY = \"SUB_PROCESS_ACTIVITY\";\n\n    String VALUE_TYPE_GATEWAY = \"GATEWAY\";\n\n    String VALUE_TYPE_START_EVENT = \"EVENT\";\n\n    String VALUE_TYPE_INTERMEDIATE_CATCH_EVENT = \"INTERMEDIATE_CATCH_EVENT\";\n\n    String VALUE_TYPE_BOUNDARY_EVENT = \"BOUNDARY_EVENT\";\n\n    String VALUE_TYPE_INTERMEDIATE_THROW_EVENT = \"\";\n\n    String VALUE_TYPE_END_EVENT = \"END_EVENT\";\n\n    String FILTER_IS_FAILED = \"isFailed\";\n\n    void setProcessId(final APIID id);\n\n    void setProcessId(final String id);\n\n    void setProcessId(final Long id);\n\n    APIID getProcessId();\n\n    /**\n     * @param id\n     * @see IFlowNodeItem#setRootCaseId(APIID)\n     * @deprecated Since 6.4.0\n     */\n    @Deprecated\n    void setCaseId(final APIID id);\n\n    /**\n     * @param id\n     * @see IFlowNodeItem#setRootCaseId(String)\n     * @deprecated Since 6.4.0\n     */\n    @Deprecated\n    void setCaseId(final String id);\n\n    /**\n     * @param id\n     * @see IFlowNodeItem#setRootCaseId(Long)\n     * @deprecated Since 6.4.0\n     */\n    @Deprecated\n    void setCaseId(final Long id);\n\n    void setRootCaseId(final APIID id);\n\n    void setRootCaseId(final String id);\n\n    void setRootCaseId(final Long id);\n\n    void setParentCaseId(final APIID id);\n\n    void setParentCaseId(final String id);\n\n    void setParentCaseId(final Long id);\n\n    /**\n     * @return\n     * @Deprecated Since 6.4.0\n     * @see IFlowNodeItem#getRootCaseId()\n     */\n    @Deprecated\n    APIID getCaseId();\n\n    APIID getRootCaseId();\n\n    APIID getParentCaseId();\n\n    void setRootContainerId(final APIID rootContainerId);\n\n    void setRootContainerId(final String rootContainerId);\n\n    void setRootContainerId(final Long rootContainerId);\n\n    APIID getRootContainerId();\n\n    void setState(final String state);\n\n    String getState();\n\n    void setType(final String type);\n\n    String getType();\n\n    void setExecutedByUserId(final APIID id);\n\n    void setExecutedByUserId(final String id);\n\n    void setExecutedByUserId(final Long id);\n\n    APIID getExecutedByUserId();\n\n    UserItem getExecutedByUser();\n\n    void setExecutedBySubstituteUserId(final APIID id);\n\n    void setExecutedBySubstituteUserId(final String id);\n\n    void setExecutedBySubstituteUserId(final Long id);\n\n    APIID getExecutedBySubstituteUserId();\n\n    UserItem getExecutedBySubstituteUser();\n\n    ProcessItem getProcess();\n\n    /**\n     * @return\n     * @see IFlowNodeItem#getCase()\n     * @deprecated since 6.4.0\n     */\n    @Deprecated\n    CaseItem getCase();\n\n    CaseItem getRootCase();\n\n    CaseItem getParentCase();\n\n    ProcessItem getRootContainerProcess();\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/IHumanTaskItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.flownode;\n\nimport java.util.Date;\n\nimport org.bonitasoft.web.rest.model.bpm.process.ActorItem;\nimport org.bonitasoft.web.rest.model.identity.UserItem;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Séverin Moussel\n */\npublic interface IHumanTaskItem extends ITaskItem {\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // ATTRIBUTES NAMES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    String ATTRIBUTE_ASSIGNED_USER_ID = \"assigned_id\";\n\n    String ATTRIBUTE_ASSIGNED_DATE = \"assigned_date\";\n\n    String ATTRIBUTE_PRIORITY = \"priority\";\n\n    String ATTRIBUTE_DUE_DATE = \"dueDate\";\n\n    String ATTRIBUTE_ACTOR_ID = \"actorId\";\n\n    /*\n     * Same as ATTRIBUTE_PARENT_CONTAINER_ID\n     * Should be in manual task but lives there because of deploy restrictions\n     */\n    String ATTRIBUTE_PARENT_TASK_ID = \"parentTaskId\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // ATTRIBUTES VALUES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    String VALUE_PRIORITY_HIGHEST = \"highest\";\n\n    String VALUE_PRIORITY_ABOVE_NORMAL = \"above_normal\";\n\n    String VALUE_PRIORITY_NORMAL = \"normal\";\n\n    String VALUE_PRIORITY_UNDER_NORMAL = \"under_normal\";\n\n    String VALUE_PRIORITY_LOWEST = \"lowest\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // COUNTS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    String COUNT_ATTACHMENT_NUMBER = \"nb_of_attachment\";\n\n    String COUNT_ACTOR_USER_NUMBER = \"nb_of_actor_user\";\n\n    String COUNT_COMMENT_NUMBER = \"nb_of_comment\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // FILTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    String FILTER_TEAM_MANAGER_ID = \"team_manager_id\";\n\n    String FILTER_USER_ID = \"user_id\";\n\n    String FILTER_HIDDEN_TO_USER_ID = \"hidden_user_id\";\n\n    String FILTER_IS_ASSIGNED = \"is_claimed\";\n\n    String FILTER_SHOW_ASSIGNED_TO_OTHERS = \"show_assigned_to_others\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // ATTRIBUTES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // SETTERS\n\n    void setAssignedId(final String id);\n\n    void setAssignedId(final APIID id);\n\n    void setAssignedId(final Long id);\n\n    void setAssignedDate(final String date);\n\n    void setAssignedDate(final Date date);\n\n    void setPriority(final String priority);\n\n    void setDueDate(final String date);\n\n    void setDueDate(final Date date);\n\n    void setActorId(final APIID id);\n\n    void setActorId(final String actorId);\n\n    void setActorId(final Long actorId);\n\n    // Counters\n    void setNbOfAttachment(final String count);\n\n    void setNbOfAttachment(final int count);\n\n    void setNbOfComment(final String count);\n\n    void setNbOfComment(final int count);\n\n    void setNbOfActorUser(final String count);\n\n    void setNbOfActorUser(final int count);\n\n    // GETTERS\n\n    APIID getActorId();\n\n    String getPriority();\n\n    String getDueDate();\n\n    APIID getAssignedId();\n\n    String getAssignedDate();\n\n    // Counters\n    Integer getNbOfAttachment();\n\n    Integer getNbOfComment();\n\n    Integer getNbOfActorUser();\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // DEPLOYS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    UserItem getAssignedUser();\n\n    ActorItem getActor();\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // UTILS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    boolean isAssigned();\n\n    boolean isUnassigned();\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/ITaskItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.flownode;\n\n/**\n * @author Séverin Moussel\n */\npublic interface ITaskItem extends IActivityItem {\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/IUserTaskItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.flownode;\n\n/**\n * @author Séverin Moussel\n */\npublic interface IUserTaskItem extends IHumanTaskItem {\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/TaskDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.flownode;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.EnumValidator;\n\n/**\n * @author Séverin Moussel\n */\npublic class TaskDefinition extends ActivityDefinition {\n\n    public static final String TOKEN = \"task\";\n\n    public static final String API_URL = \"../API/bpm/task\";\n\n    @Override\n    public String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        super.defineAttributes();\n\n        // Restrict type to tasks\n        getAttribute(TaskItem.ATTRIBUTE_TYPE)\n                .removeValidator(EnumValidator.class.getName())\n                .addValidator(new EnumValidator(\n                        TaskItem.VALUE_TYPE_USER_TASK,\n                        TaskItem.VALUE_TYPE_AUTOMATIC_TASK,\n                        TaskItem.VALUE_TYPE_MANUAL_TASK));\n\n        // add extra state\n        ((EnumValidator) getAttribute(FlowNodeItem.ATTRIBUTE_STATE)\n                .getValidator(EnumValidator.class.getName()))\n                .addValue(TaskItem.VALUE_STATE_REPLAY);\n    }\n\n    @Override\n    public ITaskItem _createItem() {\n        return new TaskItem();\n    }\n\n    public static TaskDefinition get() {\n        return (TaskDefinition) Definitions.get(TOKEN);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/TaskItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.flownode;\n\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Séverin Moussel\n */\npublic class TaskItem extends ActivityItem implements ITaskItem {\n\n    public TaskItem() {\n        super();\n    }\n\n    public TaskItem(final IItem item) {\n        super(item);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // UTILS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public ItemDefinition getItemDefinition() {\n        return new TaskDefinition();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/UserTaskDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.flownode;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.EnumValidator;\n\n/**\n * @author Séverin Moussel\n */\npublic class UserTaskDefinition extends HumanTaskDefinition {\n\n    public static final String TOKEN = \"usertask\";\n\n    private static final String API_URL = \"../API/bpm/userTask\";\n\n    @Override\n    public String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        super.defineAttributes();\n        // Restrict type to manual tasks\n        getAttribute(UserTaskItem.ATTRIBUTE_TYPE)\n                .removeValidator(EnumValidator.class.getName())\n                .addValidator(new EnumValidator(\n                        UserTaskItem.VALUE_TYPE_MANUAL_TASK));\n    }\n\n    @Override\n    public IUserTaskItem _createItem() {\n        return new UserTaskItem();\n    }\n\n    public static UserTaskDefinition get() {\n        return (UserTaskDefinition) Definitions.get(TOKEN);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/UserTaskItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.flownode;\n\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Séverin Moussel\n */\npublic class UserTaskItem extends HumanTaskItem implements IUserTaskItem {\n\n    public UserTaskItem() {\n        super();\n    }\n\n    public UserTaskItem(final IItem item) {\n        super(item);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // UTILS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public ItemDefinition getItemDefinition() {\n        return new UserTaskDefinition();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/ActorDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.process;\n\nimport static org.bonitasoft.web.rest.model.bpm.process.ActorItem.ATTRIBUTE_DESCRIPTION;\nimport static org.bonitasoft.web.rest.model.bpm.process.ActorItem.ATTRIBUTE_PROCESS_ID;\nimport static org.bonitasoft.web.toolkit.client.data.item.template.ItemHasDualName.ATTRIBUTE_DISPLAY_NAME;\nimport static org.bonitasoft.web.toolkit.client.data.item.template.ItemHasDualName.ATTRIBUTE_NAME;\nimport static org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId.ATTRIBUTE_ID;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * @author Haojie Yuan\n * @author Séverin Moussel\n */\npublic class ActorDefinition extends ItemDefinition {\n\n    /**\n     * Singleton\n     */\n    public static ActorDefinition get() {\n        return (ActorDefinition) Definitions.get(TOKEN);\n    }\n\n    public static final String TOKEN = \"actor\";\n\n    @Override\n    public String defineToken() {\n        return TOKEN;\n    }\n\n    private static final String API_URL = \"../API/bpm/actor\";\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(ATTRIBUTE_ID);\n    }\n\n    /**\n     * categoryList\n     */\n    @Override\n    protected void defineAttributes() {\n        createAttribute(ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(ATTRIBUTE_NAME, ItemAttribute.TYPE.STRING).isMandatory();\n        createAttribute(ATTRIBUTE_DISPLAY_NAME, ItemAttribute.TYPE.STRING);\n        createAttribute(ATTRIBUTE_DESCRIPTION, ItemAttribute.TYPE.TEXT);\n        createAttribute(ATTRIBUTE_PROCESS_ID, ItemAttribute.TYPE.ITEM_ID).isMandatory();\n    }\n\n    @Override\n    public IItem _createItem() {\n        return new ActorItem();\n    }\n\n    @Override\n    protected void defineDeploys() {\n        declareDeployable(ATTRIBUTE_PROCESS_ID, ProcessDefinition.get());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/ActorItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.process;\n\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasDualName;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId;\n\n/**\n * @author Haojie Yuan\n * @author Séverin Moussel\n */\npublic class ActorItem extends Item implements ItemHasUniqueId, ItemHasDualName {\n\n    public ActorItem() {\n        super();\n    }\n\n    public ActorItem(final IItem item) {\n        super(item);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // ATTRIBUTES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public static final String ATTRIBUTE_DESCRIPTION = \"description\";\n\n    public static final String ATTRIBUTE_PROCESS_ID = \"process_id\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // COUNTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public static final String COUNTER_USERS = \"users\";\n\n    public static final String COUNTER_GROUPS = \"groups\";\n\n    public static final String COUNTER_ROLES = \"roles\";\n\n    public static final String COUNTER_MEMBERSHIPS = \"memberships\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // SETTERS AND GETTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // Setters\n\n    @Override\n    public void setId(final String id) {\n        setAttribute(ATTRIBUTE_ID, id);\n    }\n\n    @Override\n    public void setId(final Long id) {\n        setAttribute(ATTRIBUTE_ID, id);\n    }\n\n    @Override\n    public void setName(final String name) {\n        setAttribute(ATTRIBUTE_NAME, name);\n    }\n\n    @Override\n    public void setDisplayName(final String name) {\n        setAttribute(ATTRIBUTE_DISPLAY_NAME, name);\n    }\n\n    public void setDescription(final String description) {\n        setAttribute(ATTRIBUTE_DESCRIPTION, description);\n    }\n\n    public void setProcessId(final String id) {\n        setAttribute(ATTRIBUTE_PROCESS_ID, id);\n    }\n\n    public void setProcessId(final Long id) {\n        setAttribute(ATTRIBUTE_PROCESS_ID, id);\n    }\n\n    public void setProcessId(final APIID id) {\n        setAttribute(ATTRIBUTE_PROCESS_ID, id);\n    }\n\n    // Getters\n\n    @Override\n    public String getName() {\n        return getAttributeValue(ATTRIBUTE_NAME);\n    }\n\n    @Override\n    public String getDisplayName() {\n        return getAttributeValue(ATTRIBUTE_DISPLAY_NAME);\n    }\n\n    public String getDescription() {\n        return getAttributeValue(ATTRIBUTE_DESCRIPTION);\n    }\n\n    public APIID getProcessId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_PROCESS_ID);\n    }\n\n    // Counters\n\n    public Long getNbSelectedUsers() {\n        return getAttributeValueAsLong(COUNTER_USERS);\n    }\n\n    public Long getNbSelectedGroups() {\n        return getAttributeValueAsLong(COUNTER_GROUPS);\n    }\n\n    public Long getNbSelectedRoles() {\n        return getAttributeValueAsLong(COUNTER_ROLES);\n    }\n\n    public Long getNbSelectedMembershipss() {\n        return getAttributeValueAsLong(COUNTER_MEMBERSHIPS);\n    }\n\n    // Deploys\n\n    public ProcessItem getProcess() {\n        return new ProcessItem(getDeploy(ATTRIBUTE_PROCESS_ID));\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // UTILS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public ItemDefinition getItemDefinition() {\n        return ActorDefinition.get();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/ActorMemberDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.process;\n\nimport org.bonitasoft.web.rest.model.portal.profile.AbstractMemberDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * @author Haojie Yuan\n * @author Séverin Moussel\n */\npublic class ActorMemberDefinition extends AbstractMemberDefinition {\n\n    /**\n     * Singleton\n     */\n    public static ActorMemberDefinition get() {\n        return (ActorMemberDefinition) Definitions.get(TOKEN);\n    }\n\n    public static final String TOKEN = \"actormember\";\n\n    /**\n     * the URL of users resource\n     */\n    private static final String API_URL = \"../API/bpm/actorMember\";\n\n    @Override\n    public String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n\n        // FIXME : Remove after engine has removed the unique ID.\n        setPrimaryKeys(ActorMemberItem.ATTRIBUTE_ID);\n\n        // FIXME : Uncomment after engine has removed the unique ID.\n        // setPrimaryKeys(\n        // ATTRIBUTE_ACTOR_ID,\n        // ATTRIBUTE_USER_ID,\n        // ATTRIBUTE_ROLE_ID,\n        // ATTRIBUTE_GROUP_ID);\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        createAttribute(ActorMemberItem.ATTRIBUTE_ACTOR_ID, ItemAttribute.TYPE.ITEM_ID);\n        super.defineAttributes();\n    }\n\n    @Override\n    protected void defineDeploys() {\n        declareDeployable(ActorMemberItem.ATTRIBUTE_ACTOR_ID, ActorDefinition.get());\n        super.defineDeploys();\n    }\n\n    @Override\n    public IItem _createItem() {\n        return new ActorMemberItem();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/ActorMemberItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.process;\n\nimport org.bonitasoft.web.rest.model.portal.profile.AbstractMemberItem;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId;\n\n/**\n * @author Séverin Moussel\n */\npublic class ActorMemberItem extends AbstractMemberItem implements ItemHasUniqueId {\n\n    public ActorMemberItem() {\n        super();\n    }\n\n    public ActorMemberItem(final IItem item) {\n        super(item);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // ATTRIBUTES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public static final String ATTRIBUTE_ACTOR_ID = \"actor_id\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // SETTERS AND GETTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // Setters\n\n    @Override\n    public void setId(final String id) {\n        setAttribute(ATTRIBUTE_ID, id);\n    }\n\n    @Override\n    public void setId(final Long id) {\n        setAttribute(ATTRIBUTE_ID, id);\n    }\n\n    public void setActorId(final APIID id) {\n        setAttribute(ATTRIBUTE_ACTOR_ID, id);\n    }\n\n    public void setActorId(final Long id) {\n        setAttribute(ATTRIBUTE_ACTOR_ID, id);\n    }\n\n    public void setActorId(final String id) {\n        setAttribute(ATTRIBUTE_ACTOR_ID, id);\n    }\n\n    // Getters\n\n    public APIID getActorId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_ACTOR_ID);\n    }\n\n    // Deploys\n\n    public ActorItem getActor() {\n        return new ActorItem(getDeploy(ATTRIBUTE_ACTOR_ID));\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // UTILS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public ItemDefinition getItemDefinition() {\n        return ActorMemberDefinition.get();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/CategoryDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.process;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * @author Zhiheng Yang\n */\npublic class CategoryDefinition extends ItemDefinition {\n\n    /**\n     * Singleton\n     */\n    public static CategoryDefinition get() {\n        return (CategoryDefinition) Definitions.get(TOKEN);\n    }\n\n    public static final String TOKEN = \"categories\";\n\n    /**\n     * the URL of categories resource\n     */\n    private static final String API_URL = \"../API/bpm/category\";\n\n    @Override\n    public String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        createAttribute(CategoryItem.ATTRIBUTE_NAME, ItemAttribute.TYPE.STRING).isMandatory();\n        createAttribute(CategoryItem.ATTRIBUTE_CREATED_BY_USER_ID, ItemAttribute.TYPE.STRING);\n        createAttribute(CategoryItem.ATTRIBUTE_DESCRIPTION, ItemAttribute.TYPE.TEXT);\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(CategoryItem.ATTRIBUTE_ID);\n    }\n\n    @Override\n    protected IItem _createItem() {\n        return new CategoryItem();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/CategoryItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.process;\n\nimport java.util.Date;\n\nimport org.bonitasoft.web.rest.model.identity.UserItem;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasCreator;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasDualName;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasLastUpdateDate;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId;\n\n/**\n * category item\n *\n * @author Qixiang Zhang\n */\npublic class CategoryItem extends Item\n        implements ItemHasUniqueId, ItemHasDualName, ItemHasCreator, ItemHasLastUpdateDate {\n\n    public CategoryItem() {\n        super();\n    }\n\n    public CategoryItem(final IItem item) {\n        super(item);\n    }\n\n    /**\n     * category of id\n     */\n    public static final String ATTRIBUTE_ID = \"id\";\n\n    /**\n     * created by\n     */\n    public static final String ATTRIBUTE_CREATED_BY_USER_ID = \"createdBy\";\n\n    /**\n     * category name\n     */\n    public static final String ATTRIBUTE_NAME = \"name\";\n\n    /**\n     * description about category\n     */\n    public static final String ATTRIBUTE_DESCRIPTION = \"description\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // ATTRIBUTES VALUES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // FILTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    public static final String FILTER_PROCESS_ID = \"processId\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // COUNTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // GETTERS AND SETTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // GETTERS\n\n    @Override\n    public String getName() {\n        return this.getAttributeValue(ATTRIBUTE_NAME);\n    }\n\n    public String getDescription() {\n        return this.getAttributeValue(ATTRIBUTE_DESCRIPTION);\n    }\n\n    @Override\n    public APIID getCreatedByUserId() {\n        return this.getAttributeValueAsAPIID(ATTRIBUTE_CREATED_BY_USER_ID);\n    }\n\n    @Override\n    public UserItem getCreatedByUser() {\n        return new UserItem(getDeploy(ATTRIBUTE_CREATED_BY_USER_ID));\n    }\n\n    @Override\n    public Date getCreationDate() {\n        return this.getAttributeValueAsDate(ATTRIBUTE_CREATION_DATE);\n    }\n\n    @Override\n    public String getDisplayName() {\n        return this.getAttributeValue(ATTRIBUTE_DISPLAY_NAME);\n    }\n\n    // SETTERS\n\n    public void setCreatedby(final String attributeCreatedby) {\n        this.setAttribute(ATTRIBUTE_CREATED_BY_USER_ID, attributeCreatedby);\n    }\n\n    @Override\n    public void setCreatedByUserId(final Long id) {\n        this.setAttribute(ATTRIBUTE_CREATED_BY_USER_ID, id.toString());\n\n    }\n\n    @Override\n    public void setCreationDate(final String date) {\n        this.setAttribute(ATTRIBUTE_CREATION_DATE, date);\n\n    }\n\n    @Override\n    public void setCreationDate(final Date date) {\n        this.setAttribute(ATTRIBUTE_CREATION_DATE, date);\n    }\n\n    @Override\n    public void setCreatedByUserId(final APIID id) {\n        this.setAttribute(ATTRIBUTE_CREATED_BY_USER_ID, id);\n    }\n\n    @Override\n    public void setCreatedByUserId(final String id) {\n        this.setAttribute(ATTRIBUTE_CREATED_BY_USER_ID, id);\n\n    }\n\n    @Override\n    public void setId(final String id) {\n        this.setAttribute(\"id\", id);\n    }\n\n    @Override\n    public void setId(final Long id) {\n        this.setAttribute(\"id\", id.toString());\n    }\n\n    @Override\n    public void setName(final String name) {\n        this.setAttribute(ATTRIBUTE_NAME, name);\n\n    }\n\n    @Override\n    public void setDisplayName(final String displayName) {\n        this.setAttribute(ATTRIBUTE_DISPLAY_NAME, displayName);\n\n    }\n\n    public void setDescription(final String description) {\n        this.setAttribute(ATTRIBUTE_DESCRIPTION, description);\n\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // UTILS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public ItemDefinition getItemDefinition() {\n        return new CategoryDefinition();\n    }\n\n    @Override\n    public void setLastUpdateDate(final String date) {\n        this.setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date);\n    }\n\n    @Override\n    public void setLastUpdateDate(final Date date) {\n        this.setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date);\n    }\n\n    @Override\n    public Date getLastUpdateDate() {\n        return this.getAttributeValueAsDate(ATTRIBUTE_LAST_UPDATE_DATE);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/ProcessCategoryDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.process;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute.TYPE;\n\n/**\n * @author Séverin Moussel\n */\npublic class ProcessCategoryDefinition extends ItemDefinition {\n\n    /**\n     * Singleton\n     */\n    public static ProcessCategoryDefinition get() {\n        return (ProcessCategoryDefinition) Definitions.get(TOKEN);\n    }\n\n    public static final String TOKEN = \"processcategory\";\n\n    public static final String API_URL = \"../API/bpm/processCategory\";\n\n    @Override\n    protected String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        createAttribute(ProcessCategoryItem.ATTRIBUTE_PROCESS_ID, TYPE.ITEM_ID).isMandatory();\n        createAttribute(ProcessCategoryItem.ATTRIBUTE_CATEGORY_ID, TYPE.ITEM_ID).isMandatory();\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(\n                ProcessCategoryItem.ATTRIBUTE_PROCESS_ID,\n                ProcessCategoryItem.ATTRIBUTE_CATEGORY_ID);\n    }\n\n    @Override\n    protected IItem _createItem() {\n        return new ProcessCategoryItem();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/ProcessCategoryItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.process;\n\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Séverin Moussel\n */\npublic class ProcessCategoryItem extends Item {\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // ATTRIBUTES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public static final String ATTRIBUTE_PROCESS_ID = \"process_id\";\n\n    public static final String ATTRIBUTE_CATEGORY_ID = \"category_id\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // SETTERS AND GETTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // SETTERS\n\n    public final void setProcessId(final APIID id) {\n        setAttribute(ATTRIBUTE_PROCESS_ID, id);\n    }\n\n    public final void setProcessId(final String id) {\n        setAttribute(ATTRIBUTE_PROCESS_ID, id);\n    }\n\n    public final void setProcessId(final Long id) {\n        setAttribute(ATTRIBUTE_PROCESS_ID, id);\n    }\n\n    public final void setCategoryId(final APIID id) {\n        setAttribute(ATTRIBUTE_CATEGORY_ID, id);\n    }\n\n    public final void setCategoryId(final String id) {\n        setAttribute(ATTRIBUTE_CATEGORY_ID, id);\n    }\n\n    public final void setCategoryId(final Long id) {\n        setAttribute(ATTRIBUTE_CATEGORY_ID, id);\n    }\n\n    // GETTERS\n\n    public final APIID getProcessId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_PROCESS_ID);\n    }\n\n    public final APIID getCategoryId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_CATEGORY_ID);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // DEPLOYS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public final ProcessItem getProcess() {\n        return new ProcessItem(getDeploy(ATTRIBUTE_PROCESS_ID));\n    }\n\n    public final CategoryItem getCategory() {\n        return new CategoryItem(getDeploy(ATTRIBUTE_CATEGORY_ID));\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // UTILS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public final ItemDefinition getItemDefinition() {\n        return ProcessCategoryDefinition.get();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/ProcessConnectorDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.process;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * @author Séverin Moussel\n */\npublic class ProcessConnectorDefinition extends ItemDefinition {\n\n    /**\n     * Singleton\n     */\n    public static ProcessConnectorDefinition get() {\n        return (ProcessConnectorDefinition) Definitions.get(TOKEN);\n    }\n\n    public static final String TOKEN = \"processconnector\";\n\n    private static final String API_URL = \"../API/bpm/processConnector\";\n\n    @Override\n    public String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(\n                ProcessConnectorItem.ATTRIBUTE_PROCESS_ID,\n                ProcessConnectorItem.ATTRIBUTE_NAME,\n                ProcessConnectorItem.ATTRIBUTE_VERSION);\n    }\n\n    @Override\n    protected void defineAttributes() {\n        createAttribute(ProcessConnectorItem.ATTRIBUTE_NAME, ItemAttribute.TYPE.STRING)\n                .isMandatory();\n        createAttribute(ProcessConnectorItem.ATTRIBUTE_VERSION, ItemAttribute.TYPE.STRING)\n                .isMandatory();\n        createAttribute(ProcessConnectorItem.ATTRIBUTE_PROCESS_ID, ItemAttribute.TYPE.ITEM_ID)\n                .isMandatory();\n        createAttribute(ProcessConnectorItem.ATTRIBUTE_IMPLEMENTATION_NAME, ItemAttribute.TYPE.STRING)\n                .isMandatory();\n        createAttribute(ProcessConnectorItem.ATTRIBUTE_IMPLEMENTATION_VERSION, ItemAttribute.TYPE.STRING)\n                .isMandatory();\n        createAttribute(ProcessConnectorItem.ATTRIBUTE_CLASSNAME, ItemAttribute.TYPE.STRING)\n                .isMandatory();\n    }\n\n    @Override\n    protected void defineDeploys() {\n        declareDeployable(ProcessConnectorItem.ATTRIBUTE_PROCESS_ID, ProcessDefinition.get());\n    }\n\n    @Override\n    protected IItem _createItem() {\n        return new ProcessConnectorItem();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/ProcessConnectorDependencyDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.process;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * @author Gai Cuisha\n */\npublic class ProcessConnectorDependencyDefinition extends ItemDefinition {\n\n    /**\n     * Singleton\n     */\n    public static ProcessConnectorDependencyDefinition get() {\n        return (ProcessConnectorDependencyDefinition) Definitions.get(TOKEN);\n    }\n\n    public static final String TOKEN = \"processconnectordependency\";\n\n    /**\n     * the URL of user resource\n     */\n    private static final String API_URL = \"../API/bpm/processConnectorDependency\";\n\n    @Override\n    public String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(\n                ProcessConnectorDependencyItem.ATTRIBUTE_PROCESS_ID,\n                ProcessConnectorDependencyItem.ATTRIBUTE_CONNECTOR_NAME,\n                ProcessConnectorDependencyItem.ATTRIBUTE_CONNECTOR_VERSION,\n                ProcessConnectorDependencyItem.ATTRIBUTE_FILENAME);\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        createAttribute(ProcessConnectorDependencyItem.ATTRIBUTE_PROCESS_ID, ItemAttribute.TYPE.ITEM_ID).isMandatory();\n        createAttribute(ProcessConnectorDependencyItem.ATTRIBUTE_CONNECTOR_NAME, ItemAttribute.TYPE.STRING)\n                .isMandatory();\n        createAttribute(ProcessConnectorDependencyItem.ATTRIBUTE_CONNECTOR_VERSION, ItemAttribute.TYPE.STRING)\n                .isMandatory();\n        createAttribute(ProcessConnectorDependencyItem.ATTRIBUTE_FILENAME, ItemAttribute.TYPE.STRING).isMandatory();\n    }\n\n    @Override\n    protected IItem _createItem() {\n        return new ProcessConnectorDependencyItem();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/ProcessConnectorDependencyItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.process;\n\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Séverin Moussel\n */\npublic class ProcessConnectorDependencyItem extends Item {\n\n    public ProcessConnectorDependencyItem() {\n        super();\n    }\n\n    public ProcessConnectorDependencyItem(final IItem item) {\n        super(item);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // ATTRIBUTES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public static final String ATTRIBUTE_PROCESS_ID = \"connector_process_id\";\n\n    public static final String ATTRIBUTE_CONNECTOR_NAME = \"connector_name\";\n\n    public static final String ATTRIBUTE_CONNECTOR_VERSION = \"connector_version\";\n\n    public static final String ATTRIBUTE_FILENAME = \"filename\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // SETTERS AND GETTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // Setters\n\n    public void setConnectorId(final APIID id) {\n        setProcessId(id.getPart(ATTRIBUTE_PROCESS_ID));\n        setConnectorName(id.getPart(ATTRIBUTE_CONNECTOR_NAME));\n        setConnectorVersion(id.getPart(ATTRIBUTE_CONNECTOR_VERSION));\n    }\n\n    public void setProcessId(final String id) {\n        setAttribute(ATTRIBUTE_PROCESS_ID, id);\n    }\n\n    public void setProcessId(final Long id) {\n        setAttribute(ATTRIBUTE_PROCESS_ID, id);\n    }\n\n    public void setProcessId(final APIID id) {\n        setAttribute(ATTRIBUTE_PROCESS_ID, id);\n    }\n\n    public void setConnectorName(final String name) {\n        setAttribute(ATTRIBUTE_CONNECTOR_NAME, name);\n    }\n\n    public void setConnectorVersion(final String version) {\n        setAttribute(ATTRIBUTE_CONNECTOR_VERSION, version);\n    }\n\n    public void setFilename(final String filename) {\n        setAttribute(ATTRIBUTE_FILENAME, filename);\n    }\n\n    // Getters\n\n    public APIID getProcessId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_PROCESS_ID);\n    }\n\n    public String getConnectorName() {\n        return getAttributeValue(ATTRIBUTE_CONNECTOR_NAME);\n    }\n\n    public String getConnectorVersion() {\n        return getAttributeValue(ATTRIBUTE_CONNECTOR_VERSION);\n    }\n\n    public String getFilename() {\n        return getAttributeValue(ATTRIBUTE_FILENAME);\n    }\n\n    // Deploys\n\n    public ProcessItem getProcess() {\n        return new ProcessItem(getDeploy(ATTRIBUTE_PROCESS_ID));\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // UTILS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public ItemDefinition getItemDefinition() {\n        return ProcessConnectorDependencyDefinition.get();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/ProcessConnectorItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.process;\n\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Séverin Moussel\n */\npublic class ProcessConnectorItem extends Item {\n\n    public ProcessConnectorItem() {\n        super();\n    }\n\n    public ProcessConnectorItem(final IItem item) {\n        super(item);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // ATTRIBUTES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public static final String ATTRIBUTE_NAME = \"definition_id\";\n\n    public static final String ATTRIBUTE_VERSION = \"definition_version\";\n\n    public static final String ATTRIBUTE_PROCESS_ID = \"process_id\";\n\n    public static final String ATTRIBUTE_IMPLEMENTATION_NAME = \"impl_name\";\n\n    public static final String ATTRIBUTE_IMPLEMENTATION_VERSION = \"impl_version\";\n\n    public static final String ATTRIBUTE_CLASSNAME = \"classname\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // SETTERS AND GETTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // Setters\n\n    public void setName(final String name) {\n        setAttribute(ATTRIBUTE_NAME, name);\n    }\n\n    public void setVersion(final String version) {\n        setAttribute(ATTRIBUTE_VERSION, version);\n    }\n\n    public void setProcessId(final String id) {\n        setAttribute(ATTRIBUTE_PROCESS_ID, id);\n    }\n\n    public void setProcessId(final APIID id) {\n        setAttribute(ATTRIBUTE_PROCESS_ID, id);\n    }\n\n    public void setProcessId(final long id) {\n        setAttribute(ATTRIBUTE_PROCESS_ID, id);\n    }\n\n    public void setImplementationName(final String name) {\n        setAttribute(ATTRIBUTE_IMPLEMENTATION_NAME, name);\n    }\n\n    public void setImplementationVersion(final String version) {\n        setAttribute(ATTRIBUTE_IMPLEMENTATION_VERSION, version);\n    }\n\n    public void setClassname(final String classname) {\n        setAttribute(ATTRIBUTE_CLASSNAME, classname);\n    }\n\n    // Getters\n\n    public String getName() {\n        return getAttributeValue(ATTRIBUTE_NAME);\n    }\n\n    public String getVersion() {\n        return getAttributeValue(ATTRIBUTE_VERSION);\n    }\n\n    public APIID getProcessId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_PROCESS_ID);\n    }\n\n    public String getImplementationName() {\n        return getAttributeValue(ATTRIBUTE_IMPLEMENTATION_NAME);\n    }\n\n    public String getImplementationVersion() {\n        return getAttributeValue(ATTRIBUTE_IMPLEMENTATION_VERSION);\n    }\n\n    public String getClassname() {\n        return getAttributeValue(ATTRIBUTE_CLASSNAME);\n    }\n\n    // Deploys\n\n    public ProcessItem getProcess() {\n        return new ProcessItem(getDeploy(ATTRIBUTE_PROCESS_ID));\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // UTILS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public ItemDefinition getItemDefinition() {\n        return new ProcessConnectorDefinition();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/ProcessDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.process;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.EnumValidator;\n\n/**\n * @author Vincent Elcrin\n */\npublic class ProcessDefinition extends ItemDefinition<ProcessItem> {\n\n    /**\n     * Singleton\n     */\n    public static ProcessDefinition get() {\n        return (ProcessDefinition) Definitions.get(TOKEN);\n    }\n\n    public static final String TOKEN = \"process\";\n\n    /**\n     * the URL of users resource\n     */\n    private static final String API_URL = \"../API/bpm/process\";\n\n    @Override\n    public String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(ProcessItem.ATTRIBUTE_ID);\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    /**\n     * categoryList\n     */\n    @Override\n    protected void defineAttributes() {\n        createAttribute(ProcessItem.ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(ProcessItem.ATTRIBUTE_NAME, ItemAttribute.TYPE.STRING)\n                .isMandatory();\n        createAttribute(ProcessItem.ATTRIBUTE_DISPLAY_NAME, ItemAttribute.TYPE.STRING);\n        createAttribute(ProcessItem.ATTRIBUTE_DESCRIPTION, ItemAttribute.TYPE.TEXT);\n        createAttribute(ProcessItem.ATTRIBUTE_DISPLAY_DESCRIPTION, ItemAttribute.TYPE.TEXT);\n        createAttribute(ProcessItem.ATTRIBUTE_VERSION, ItemAttribute.TYPE.STRING)\n                .isMandatory();\n        createAttribute(ProcessItem.ATTRIBUTE_ACTIVATION_STATE, ItemAttribute.TYPE.ENUM)\n                .addValidator(new EnumValidator(ProcessItem.VALUE_ACTIVATION_STATE_DISABLED,\n                        ProcessItem.VALUE_ACTIVATION_STATE_ENABLED));\n        createAttribute(ProcessItem.ATTRIBUTE_CONFIGURATION_STATE, ItemAttribute.TYPE.ENUM)\n                .addValidator(new EnumValidator(ProcessItem.VALUE_CONFIGURATION_STATE_UNRESOLVED,\n                        ProcessItem.VALUE_CONFIGURATION_STATE_RESOLVED));\n        createAttribute(ProcessItem.ATTRIBUTE_DEPLOYED_BY_USER_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(ProcessItem.ATTRIBUTE_DEPLOYMENT_DATE, ItemAttribute.TYPE.DATETIME);\n        createAttribute(ProcessItem.ATTRIBUTE_LAST_UPDATE_DATE, ItemAttribute.TYPE.DATETIME);\n        createAttribute(ProcessItem.ATTRIBUTE_ACTOR_INITIATOR_ID, ItemAttribute.TYPE.ITEM_ID);\n    }\n\n    @Override\n    public ProcessItem _createItem() {\n        return new ProcessItem();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/ProcessDocumentDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.process;\n\nimport org.bonitasoft.web.rest.model.bpm.AbstractDocumentDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\n\n/**\n * @author Paul AMAR\n */\npublic class ProcessDocumentDefinition extends AbstractDocumentDefinition {\n\n    public static final String TOKEN = \"process\";\n\n    /**\n     * the URL of user resource\n     */\n    private static final String API_URL = \"../API/document/process\";\n\n    @Override\n    protected void definePrimaryKeys() {\n    }\n\n    @Override\n    protected IItem _createItem() {\n        return new ProcessDocumentItem();\n    }\n\n    @Override\n    protected String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        // TODO Auto-generated method stub\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/ProcessDocumentItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.process;\n\nimport org.bonitasoft.web.rest.model.bpm.AbstractDocumentItem;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Paul AMAR\n */\npublic class ProcessDocumentItem extends AbstractDocumentItem {\n\n    private final String ATTRIBUTE_PROCESS_ID = \"processId\";\n\n    /**\n     * Default Constructor.\n     */\n    public ProcessDocumentItem() {\n        super();\n    }\n\n    @Override\n    public ItemDefinition getItemDefinition() {\n        return new ProcessDocumentDefinition();\n    }\n\n    public String getProcessId() {\n        return this.getAttributeValue(this.ATTRIBUTE_PROCESS_ID);\n    }\n\n    public IItem setProcessId(final String processId) {\n        this.setAttribute(this.ATTRIBUTE_PROCESS_ID, processId);\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/ProcessItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.process;\n\nimport java.util.Date;\n\nimport org.bonitasoft.web.rest.model.identity.UserItem;\nimport org.bonitasoft.web.toolkit.client.common.util.StringUtil;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasDualName;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasLastUpdateDate;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId;\n\n/**\n * @author Vincent Elcrin\n * @author Celine Souchet\n */\npublic class ProcessItem extends Item implements ItemHasUniqueId, ItemHasLastUpdateDate, ItemHasDualName {\n\n    public ProcessItem() {\n        super();\n    }\n\n    public ProcessItem(final IItem item) {\n        super(item);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // ATTRIBUTES NAMES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public static final String ATTRIBUTE_VERSION = \"version\";\n\n    public static final String ATTRIBUTE_DESCRIPTION = \"description\";\n\n    public static final String ATTRIBUTE_DEPLOYMENT_DATE = \"deploymentDate\";\n\n    public static final String ATTRIBUTE_DEPLOYED_BY_USER_ID = \"deployedBy\";\n\n    public static final String ATTRIBUTE_ACTIVATION_STATE = \"activationState\";\n\n    public static final String ATTRIBUTE_CONFIGURATION_STATE = \"configurationState\";\n\n    public static final String ATTRIBUTE_DISPLAY_DESCRIPTION = \"displayDescription\";\n\n    public static final String ATTRIBUTE_ACTOR_INITIATOR_ID = \"actorinitiatorid\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // ATTRIBUTES VALUES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public static final String VALUE_ACTIVATION_STATE_DISABLED = \"DISABLED\";\n\n    public static final String VALUE_ACTIVATION_STATE_ENABLED = \"ENABLED\";\n\n    public static final String VALUE_CONFIGURATION_STATE_UNRESOLVED = \"UNRESOLVED\";\n\n    public static final String VALUE_CONFIGURATION_STATE_RESOLVED = \"RESOLVED\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // FILTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public static final String FILTER_TEAM_MANAGER_ID = \"team_manager_id\";\n\n    public static final String FILTER_SUPERVISOR_ID = \"supervisor_id\";\n\n    public static final String FILTER_USER_ID = \"user_id\";\n\n    public static final String FILTER_RECENT_PROCESSES = \"recentProcesses\";\n\n    public static final String FILTER_CATEGORY_ID = \"categoryId\";\n\n    public static final String FILTER_FOR_PENDING_OR_ASSIGNED_TASKS = \"forPendingOrAssignedTask\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // COUNTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public static final String COUNTER_FAILED_CASES = \"failedCases\";\n\n    public static final String COUNTER_OPEN_CASES = \"openCases\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // GETTERS AND SETTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // GETTERS\n\n    @Override\n    public String getName() {\n        return this.getAttributeValue(ATTRIBUTE_NAME);\n    }\n\n    public String getVersion() {\n        return this.getAttributeValue(ATTRIBUTE_VERSION);\n    }\n\n    public String ensureName() {\n        if (StringUtil.isBlank(getDisplayName())) {\n            return getName();\n        }\n        return getDisplayName();\n    }\n\n    public String getDescription() {\n        return this.getAttributeValue(ATTRIBUTE_DESCRIPTION);\n    }\n\n    public String getDeploymentDate() {\n        return this.getAttributeValue(ATTRIBUTE_DEPLOYMENT_DATE);\n    }\n\n    public APIID getDeployedByUserId() {\n        return APIID.makeAPIID(this.getAttributeValue(ATTRIBUTE_DEPLOYED_BY_USER_ID));\n    }\n\n    public String getActivationState() {\n        return this.getAttributeValue(ATTRIBUTE_ACTIVATION_STATE);\n    }\n\n    public String getConfigurationState() {\n        return this.getAttributeValue(ATTRIBUTE_CONFIGURATION_STATE);\n    }\n\n    @Override\n    public String getDisplayName() {\n        return this.getAttributeValue(ATTRIBUTE_DISPLAY_NAME);\n    }\n\n    public String getDisplayDescription() {\n        return this.getAttributeValue(ATTRIBUTE_DISPLAY_DESCRIPTION);\n    }\n\n    @Override\n    public Date getLastUpdateDate() {\n        return this.getAttributeValueAsDate(ATTRIBUTE_LAST_UPDATE_DATE);\n    }\n\n    public String getActorInitiatorId() {\n        return this.getAttributeValue(ATTRIBUTE_ACTOR_INITIATOR_ID);\n    }\n\n    // SETTERS\n\n    @Override\n    public void setId(final String id) {\n        this.setAttribute(ATTRIBUTE_ID, id);\n    }\n\n    @Override\n    public void setId(final Long id) {\n        this.setId(id.toString());\n    }\n\n    @Override\n    public void setName(final String name) {\n        this.setAttribute(ATTRIBUTE_NAME, name);\n    }\n\n    public void setVersion(final String version) {\n        this.setAttribute(ATTRIBUTE_VERSION, version);\n    }\n\n    public void setDescription(final String description) {\n        this.setAttribute(ATTRIBUTE_DESCRIPTION, description);\n    }\n\n    public void setDeployedByUserId(final String id) {\n        this.setAttribute(ATTRIBUTE_DEPLOYED_BY_USER_ID, id);\n    }\n\n    public void setDeployedByUserId(final Long id) {\n        setDeployedByUserId(id.toString());\n    }\n\n    public void setDeployedByUserId(final APIID id) {\n        this.setAttribute(ATTRIBUTE_DEPLOYED_BY_USER_ID, id);\n    }\n\n    public void setDeploymentDate(final String date) {\n        this.setAttribute(ATTRIBUTE_DEPLOYMENT_DATE, date);\n    }\n\n    public void setDeploymentDate(final Date date) {\n        this.setAttribute(ATTRIBUTE_DEPLOYMENT_DATE, date);\n    }\n\n    public void setActivationState(final String state) {\n        this.setAttribute(ATTRIBUTE_ACTIVATION_STATE, state);\n    }\n\n    public void setConfigurationState(final String state) {\n        this.setAttribute(ATTRIBUTE_CONFIGURATION_STATE, state);\n    }\n\n    @Override\n    public void setDisplayName(final String displayName) {\n        this.setAttribute(ATTRIBUTE_DISPLAY_NAME, displayName);\n    }\n\n    public void setDisplayDescription(final String displayDescription) {\n        this.setAttribute(ATTRIBUTE_DISPLAY_DESCRIPTION, displayDescription);\n    }\n\n    @Override\n    public void setLastUpdateDate(final String date) {\n        this.setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date);\n    }\n\n    @Override\n    public void setLastUpdateDate(final Date date) {\n        this.setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date);\n    }\n\n    public void setActorInitiatorId(final Long id) {\n        setActorInitiatorId(id.toString());\n    }\n\n    public void setActorInitiatorId(final APIID id) {\n        setActorInitiatorId(id.toString());\n    }\n\n    public void setActorInitiatorId(final String id) {\n        this.setAttribute(ATTRIBUTE_ACTOR_INITIATOR_ID, id);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // DEPLOYS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public UserItem getDeployedByUser() {\n        return new UserItem(getDeploy(ATTRIBUTE_DEPLOYED_BY_USER_ID));\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // UTILS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public boolean isEnabled() {\n        return VALUE_ACTIVATION_STATE_ENABLED.equals(getActivationState());\n    }\n\n    public boolean isResolved() {\n        return VALUE_CONFIGURATION_STATE_RESOLVED.equals(getConfigurationState());\n    }\n\n    public boolean isStartable() {\n        return isEnabled() && isResolved();\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see org.bonitasoft.web.toolkit.client.data.item.Item#getItemDefinition()\n     */\n    @Override\n    public ItemDefinition<ProcessItem> getItemDefinition() {\n        return ProcessDefinition.get();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/ProcessParameterDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.process;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.StringMaxLengthValidator;\n\n/**\n * @author Yongtao Guo, Haojie Yuan, Anthony Birembaut\n */\npublic class ProcessParameterDefinition extends ItemDefinition<ProcessParameterItem> {\n\n    /**\n     * Singleton\n     */\n    public static ProcessParameterDefinition get() {\n        return (ProcessParameterDefinition) Definitions.get(TOKEN);\n    }\n\n    public static final String TOKEN = \"processParameter\";\n\n    /**\n     * the URL of users resource\n     */\n    private static final String API_URL = \"../API/bpm/processParameter\";\n\n    @Override\n    public String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(ProcessParameterItem.ATTRIBUTE_PROCESS_ID, ProcessParameterItem.ATTRIBUTE_NAME);\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    /**\n     * categoryList\n     */\n    @Override\n    protected void defineAttributes() {\n\n        createAttribute(ProcessParameterItem.ATTRIBUTE_NAME, ItemAttribute.TYPE.STRING);\n        createAttribute(ProcessParameterItem.ATTRIBUTE_TYPE, ItemAttribute.TYPE.STRING);\n        createAttribute(ProcessParameterItem.ATTRIBUTE_DESCRIPTION, ItemAttribute.TYPE.TEXT);\n        createAttribute(ProcessParameterItem.ATTRIBUTE_VALUE, ItemAttribute.TYPE.STRING)\n                .removeValidator(StringMaxLengthValidator.class.getName());\n\n    }\n\n    @Override\n    public ProcessParameterItem _createItem() {\n        return new ProcessParameterItem();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/ProcessParameterItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.process;\n\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Ruiheng Fan\n */\npublic class ProcessParameterItem extends Item {\n\n    public ProcessParameterItem() {\n        super();\n    }\n\n    public ProcessParameterItem(final IItem item) {\n        super(item);\n    }\n\n    public static final String ATTRIBUTE_PROCESS_ID = \"process_id\";\n\n    public static final String ATTRIBUTE_NAME = \"name\";\n\n    public static final String ATTRIBUTE_TYPE = \"type\";\n\n    public static final String ATTRIBUTE_VALUE = \"value\";\n\n    public static final String ATTRIBUTE_DESCRIPTION = \"description\";\n\n    public static final String ATTRIBUTE_PROCESS_NAME = \"process_name\";\n\n    public static final String ATTRIBUTE_PROCESS_VERSION = \"process_version\";\n\n    public static final String FILTER_PROCESS_ID = \"process_id\";\n\n    /**\n     * Default Constructor.\n     *\n     * @param ProcessParameter\n     *        name\n     * @param ProcessParameter\n     *        type\n     * @param ProcessParameter\n     *        value\n     * @param ProcessParameter\n     *        description\n     */\n    public ProcessParameterItem(final String processId, final String name, final String type, final String value,\n            final String description,\n            final String processName, final String processVersion) {\n        this.setAttribute(ATTRIBUTE_PROCESS_ID, processId);\n        this.setAttribute(ATTRIBUTE_NAME, name);\n        this.setAttribute(ATTRIBUTE_TYPE, type);\n        this.setAttribute(ATTRIBUTE_VALUE, value);\n        this.setAttribute(ATTRIBUTE_DESCRIPTION, description);\n        this.setAttribute(ATTRIBUTE_PROCESS_NAME, processName);\n        this.setAttribute(ATTRIBUTE_PROCESS_VERSION, processVersion);\n    }\n\n    @Override\n    public ItemDefinition<ProcessParameterItem> getItemDefinition() {\n        return new ProcessParameterDefinition();\n    }\n\n    public String getName() {\n        return getAttributeValue(ATTRIBUTE_NAME);\n    }\n\n    public String getType() {\n        return getAttributeValue(ATTRIBUTE_TYPE);\n    }\n\n    public String getValue() {\n        return getAttributeValue(ATTRIBUTE_VALUE);\n    }\n\n    public String getDescription() {\n        return getAttributeValue(ATTRIBUTE_DESCRIPTION);\n    }\n\n    public void setValue(String value) {\n        setAttribute(ATTRIBUTE_VALUE, value);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/ProcessResolutionProblemDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.process;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute.TYPE;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.EnumValidator;\n\n/**\n * @author Séverin Moussel\n */\npublic class ProcessResolutionProblemDefinition extends ItemDefinition<ProcessResolutionProblemItem> {\n\n    public static ProcessResolutionProblemDefinition get() {\n        return (ProcessResolutionProblemDefinition) Definitions.get(TOKEN);\n    }\n\n    public static final String TOKEN = \"processresolutionerror\";\n\n    private static final String API_URL = \"../API/bpm/processResolutionProblem\";\n\n    @Override\n    protected String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        createAttribute(ProcessResolutionProblemItem.FILTER_PROCESS_ID, TYPE.ITEM_ID)\n                .isMandatory();\n\n        createAttribute(ProcessResolutionProblemItem.ATTRIBUTE_MESSAGE, TYPE.STRING)\n                .isMandatory();\n\n        createAttribute(ProcessResolutionProblemItem.ATTRIBUTE_TARGET_TYPE, TYPE.ENUM)\n                .isMandatory()\n                .addValidator(new EnumValidator(\n                        ProcessResolutionProblemItem.VALUE_STATE_TARGET_TYPE_ACTOR,\n                        ProcessResolutionProblemItem.VALUE_STATE_TARGET_TYPE_CONNECTOR,\n                        ProcessResolutionProblemItem.VALUE_STATE_TARGET_TYPE_PARAMETER));\n\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(ProcessResolutionProblemItem.FILTER_PROCESS_ID);\n    }\n\n    @Override\n    protected ProcessResolutionProblemItem _createItem() {\n        return new ProcessResolutionProblemItem();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/ProcessResolutionProblemItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.bpm.process;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Séverin Moussel\n */\npublic class ProcessResolutionProblemItem extends Item {\n\n    public ProcessResolutionProblemItem() {\n        super();\n    }\n\n    public ProcessResolutionProblemItem(final IItem item) {\n        super(item);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // ATTRIBUTES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public static final String ATTRIBUTE_MESSAGE = \"message\";\n\n    public static final String ATTRIBUTE_TARGET_TYPE = \"target_type\";\n\n    public static final String ATTRIBUTE_RESSOURCE_ID = \"ressource_id\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // ATTRIBUTES VALUES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public static final String VALUE_STATE_TARGET_TYPE_CONNECTOR = \"connector\";\n\n    public static final String VALUE_STATE_TARGET_TYPE_ACTOR = \"actor\";\n\n    public static final String VALUE_STATE_TARGET_TYPE_PARAMETER = \"parameter\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // FILTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public static final String FILTER_PROCESS_ID = \"process_id\";\n\n    public String getMessage() {\n        return getAttributeValue(ATTRIBUTE_MESSAGE);\n    }\n\n    public void setMessage(final String text) {\n        setAttribute(ATTRIBUTE_MESSAGE, text);\n    }\n\n    public String getTargetType() {\n        return getAttributeValue(ATTRIBUTE_TARGET_TYPE);\n    }\n\n    public void setTargetType(final String type) {\n        setAttribute(ATTRIBUTE_TARGET_TYPE, type);\n    }\n\n    public String getRessourceId() {\n        return getAttributeValue(ATTRIBUTE_RESSOURCE_ID);\n    }\n\n    public void setRessourceId(final String ressourceId) {\n        setAttribute(ATTRIBUTE_RESSOURCE_ID, ressourceId);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // UTILS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public ItemDefinition getItemDefinition() {\n        return Definitions.get(ProcessResolutionProblemDefinition.TOKEN);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/builder/bpm/cases/CaseItemBuilder.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.builder.bpm.cases;\n\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseItem;\n\npublic class CaseItemBuilder {\n\n    private long processId;\n\n    private String jsonVariables;\n\n    private long userId;\n\n    public static CaseItemBuilder aCaseItem() {\n        return new CaseItemBuilder();\n    }\n\n    public CaseItemBuilder withProcessId(final long processId) {\n        this.processId = processId;\n        return this;\n    }\n\n    public CaseItemBuilder withUserId(final long userId) {\n        this.userId = userId;\n        return this;\n    }\n\n    public CaseItemBuilder withVariables(final String jsonVariables) {\n        this.jsonVariables = jsonVariables;\n        return this;\n    }\n\n    public CaseItem build() {\n        final CaseItem item = new CaseItem();\n        setAttributeIfNotNull(item, CaseItem.ATTRIBUTE_PROCESS_ID, processId);\n        setAttributeIfNotNull(item, CaseItem.ATTRIBUTE_STARTED_BY_SUBSTITUTE_USER_ID, userId);\n        setAttributeIfNotNull(item, CaseItem.ATTRIBUTE_VARIABLES, jsonVariables);\n        return item;\n    }\n\n    private void setAttributeIfNotNull(final CaseItem caseItem, final String attributeName,\n            final Object attributeValue) {\n        if (attributeValue != null) {\n            caseItem.setAttribute(attributeName, attributeValue);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/builder/bpm/process/ActorItemBuilder.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.builder.bpm.process;\n\nimport org.bonitasoft.engine.bpm.actor.ActorInstance;\nimport org.bonitasoft.web.rest.model.bpm.process.ActorItem;\n\n/**\n * @author Colin PUY\n */\npublic class ActorItemBuilder {\n\n    private Long id;\n    private Long processId;\n    private String name;\n    private String displayName;\n    private String description;\n\n    private ActorItemBuilder() {\n    }\n\n    public static ActorItemBuilder anActorItem() {\n        return new ActorItemBuilder();\n    }\n\n    public ActorItem build() {\n        ActorItem actorItem = new ActorItem();\n        actorItem.setId(id);\n        actorItem.setProcessId(processId);\n        actorItem.setName(name);\n        actorItem.setDisplayName(displayName);\n        actorItem.setDescription(description);\n        return actorItem;\n    }\n\n    public ActorItemBuilder fromActorInstance(ActorInstance engineItem) {\n        id = engineItem.getId();\n        processId = engineItem.getProcessDefinitionId();\n        name = engineItem.getName();\n        displayName = engineItem.getDisplayName();\n        description = engineItem.getDescription();\n        return this;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/builder/bpm/process/ActorMemberItemBuilder.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.builder.bpm.process;\n\nimport org.bonitasoft.engine.bpm.actor.ActorMember;\nimport org.bonitasoft.web.rest.model.bpm.process.ActorMemberItem;\n\n/**\n * @author Colin PUY\n */\npublic class ActorMemberItemBuilder {\n\n    private Long id;\n    private Long actorId;\n    private Long userId;\n    private Long roleId;\n    private Long groupId;\n\n    private ActorMemberItemBuilder() {\n    }\n\n    public static ActorMemberItemBuilder anActorMemberItem() {\n        return new ActorMemberItemBuilder();\n    }\n\n    public ActorMemberItem build() {\n        ActorMemberItem item = new ActorMemberItem();\n        item.setId(id);\n        item.setActorId(actorId);\n        item.setUserId(userId);\n        item.setRoleId(roleId);\n        item.setGroupId(groupId);\n        return item;\n    }\n\n    public ActorMemberItemBuilder fromActorMember(ActorMember actorMember, long actorId) {\n        id = actorMember.getId();\n        this.actorId = actorId;\n        userId = actorMember.getUserId();\n        roleId = actorMember.getRoleId();\n        groupId = actorMember.getGroupId();\n        return this;\n    }\n\n    public ActorMemberItemBuilder withActorId(long actorId) {\n        this.actorId = actorId;\n        return this;\n    }\n\n    public ActorMemberItemBuilder withuserId(long userId) {\n        this.userId = userId;\n        return this;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/builder/bpm/process/CategoryItemBuilder.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.builder.bpm.process;\n\nimport org.bonitasoft.web.rest.model.bpm.process.CategoryItem;\n\n/**\n * @author Colin PUY\n */\npublic class CategoryItemBuilder {\n\n    private final String description = \"aDescription\";\n    private final String name = \"aCategory\";\n\n    public static CategoryItemBuilder aCategoryItem() {\n        return new CategoryItemBuilder();\n    }\n\n    public CategoryItem build() {\n        CategoryItem category = new CategoryItem();\n        category.setName(name);\n        category.setDescription(description);\n        return category;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/builder/bpm/process/ProcessCategoryItemBuilder.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.builder.bpm.process;\n\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessCategoryItem;\n\n/**\n * @author Colin PUY\n */\npublic class ProcessCategoryItemBuilder {\n\n    private final long processId = 1L;\n    private final long categoryId = 1L;\n\n    public static ProcessCategoryItemBuilder aProcessCategory() {\n        return new ProcessCategoryItemBuilder();\n    }\n\n    public ProcessCategoryItem build() {\n        ProcessCategoryItem processCategory = new ProcessCategoryItem();\n        processCategory.setCategoryId(processId);\n        processCategory.setProcessId(categoryId);\n        return processCategory;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/builder/identity/ContactDataBuilder.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.builder.identity;\n\nimport org.bonitasoft.engine.identity.ContactDataCreator;\nimport org.bonitasoft.web.rest.model.identity.ProfessionalContactDataItem;\n\n/**\n * @author Colin PUY\n */\npublic class ContactDataBuilder {\n\n    private String address = \"anAddress\";\n    private final String building = \"aBuilding\";\n    private final String city = \"aCity\";\n    private final String country = \"aCountry\";\n    private final String email = \"anEmail\";\n    private final String faxNumber = \"aFaxNumber\";\n    private final String mobileNumber = \"aMobileNumber\";\n    private final String phoneNumber = \"aPhoneNumber\";\n    private final String room = \"aRoom\";\n    private final String state = \"aState\";\n    private final String website = \"aWebsite\";\n    private final String zipCode = \"aZipCode\";\n\n    public static ContactDataBuilder aContactData() {\n        return new ContactDataBuilder();\n    }\n\n    public ContactDataBuilder withAddress(String address) {\n        this.address = address;\n        return this;\n    }\n\n    public ContactDataCreator toContactDataCreator() {\n        return new ContactDataCreator()\n                .setAddress(address)\n                .setBuilding(building)\n                .setCity(city)\n                .setCountry(country)\n                .setEmail(email)\n                .setFaxNumber(faxNumber)\n                .setMobileNumber(mobileNumber)\n                .setPhoneNumber(phoneNumber)\n                .setRoom(room)\n                .setState(state)\n                .setWebsite(website)\n                .setZipCode(zipCode);\n    }\n\n    public ProfessionalContactDataItem toProfessionalContactDataItem() {\n        ProfessionalContactDataItem item = new ProfessionalContactDataItem();\n        item.setAttribute(ProfessionalContactDataItem.ATTRIBUTE_EMAIL, email);\n        item.setAttribute(ProfessionalContactDataItem.ATTRIBUTE_PHONE, phoneNumber);\n        item.setAttribute(ProfessionalContactDataItem.ATTRIBUTE_MOBILE, mobileNumber);\n        item.setAttribute(ProfessionalContactDataItem.ATTRIBUTE_FAX, faxNumber);\n        item.setAttribute(ProfessionalContactDataItem.ATTRIBUTE_BUILDING, building);\n        item.setAttribute(ProfessionalContactDataItem.ATTRIBUTE_ROOM, room);\n        item.setAttribute(ProfessionalContactDataItem.ATTRIBUTE_ADDRESS, address);\n        item.setAttribute(ProfessionalContactDataItem.ATTRIBUTE_ZIPCODE, zipCode);\n        item.setAttribute(ProfessionalContactDataItem.ATTRIBUTE_CITY, city);\n        item.setAttribute(ProfessionalContactDataItem.ATTRIBUTE_STATE, state);\n        item.setAttribute(ProfessionalContactDataItem.ATTRIBUTE_COUNTRY, country);\n        item.setAttribute(ProfessionalContactDataItem.ATTRIBUTE_WEBSITE, website);\n        return item;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/builder/organisation/GroupItemBuilder.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.builder.organisation;\n\nimport org.bonitasoft.web.rest.model.identity.GroupItem;\n\n/**\n * @author Colin PUY\n */\npublic class GroupItemBuilder {\n\n    private final String name = \"Group1\";\n    private final String displayName = \"Group\";\n    private final String description = \"Text for describe the group\";\n    private final String parentPath = \"\";\n    private final String parentGroupId = \"\";\n\n    public static GroupItemBuilder aGroup() {\n        return new GroupItemBuilder();\n    }\n\n    public GroupItem build() {\n        GroupItem group = new GroupItem();\n        group.setName(name);\n        group.setDisplayName(displayName);\n        group.setDescription(description);\n        group.setParentPath(parentPath);\n        group.setParentGroupId(parentGroupId);\n        return group;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/builder/profile/member/AbstractProfileMemberBuilder.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.builder.profile.member;\n\n/**\n * @author Vincent Elcrin\n */\npublic abstract class AbstractProfileMemberBuilder<O> {\n\n    protected Long id = 1L;\n    protected Long profileId = 2L;\n    protected Long userId = 3L;\n    protected Long groupId = 4L;\n    protected Long roleId = 5L;\n\n    public AbstractProfileMemberBuilder<O> withId(Long id) {\n        this.id = id;\n        return this;\n    }\n\n    public AbstractProfileMemberBuilder<O> withProfileId(Long id) {\n        this.profileId = id;\n        return this;\n    }\n\n    public AbstractProfileMemberBuilder<O> withUserId(Long id) {\n        this.userId = id;\n        return this;\n    }\n\n    public AbstractProfileMemberBuilder<O> withGroupId(Long id) {\n        this.groupId = id;\n        return this;\n    }\n\n    public AbstractProfileMemberBuilder<O> withRoleId(Long id) {\n        this.roleId = id;\n        return this;\n    }\n\n    public abstract O build();\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/builder/profile/member/EngineProfileMemberBuilder.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.builder.profile.member;\n\nimport org.bonitasoft.engine.profile.ProfileMember;\nimport org.bonitasoft.engine.profile.impl.ProfileMemberImpl;\n\n/**\n * @author Vincent Elcrin\n */\npublic class EngineProfileMemberBuilder extends AbstractProfileMemberBuilder<ProfileMember> {\n\n    public static EngineProfileMemberBuilder anEngineProfileMember() {\n        return new EngineProfileMemberBuilder();\n    }\n\n    public ProfileMember build() {\n        ProfileMemberImpl profileMember = new ProfileMemberImpl();\n        profileMember.setProfileId(profileId);\n        profileMember.setUserId(userId);\n        profileMember.setGroupId(groupId);\n        profileMember.setRoleId(roleId);\n        return profileMember;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/builder/profile/member/ProfileMemberItemBuilder.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.builder.profile.member;\n\nimport org.bonitasoft.engine.profile.ProfileMember;\nimport org.bonitasoft.web.rest.model.portal.profile.ProfileMemberItem;\n\n/**\n * @author Vincent Elcrin\n */\npublic class ProfileMemberItemBuilder extends AbstractProfileMemberBuilder<ProfileMemberItem> {\n\n    public static ProfileMemberItemBuilder aProfileMemberItem() {\n        return new ProfileMemberItemBuilder();\n    }\n\n    public ProfileMemberItem build() {\n        ProfileMemberItem item = new ProfileMemberItem();\n        item.setId(id);\n        item.setProfileId(profileId);\n        item.setUserId(userId);\n        item.setGroupId(groupId);\n        item.setRoleId(roleId);\n        return item;\n    }\n\n    public ProfileMemberItemBuilder from(ProfileMember profileMember) {\n        id = profileMember.getId();\n        profileId = profileMember.getProfileId();\n        userId = profileMember.getUserId();\n        groupId = profileMember.getGroupId();\n        roleId = profileMember.getRoleId();\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/document/ArchivedDocumentDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.document;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * @author Gai Cuisha\n */\npublic class ArchivedDocumentDefinition extends ItemDefinition {\n\n    /**\n     * Singleton\n     */\n    public static ArchivedDocumentDefinition get() {\n        return (ArchivedDocumentDefinition) Definitions.get(TOKEN);\n    }\n\n    /**\n     * token\n     */\n    public static final String TOKEN = \"archivedDocument\";\n\n    /**\n     * the URL of document\n     */\n    private static final String API_URL = \"../API/bpm/archiveddocument\";\n\n    @Override\n    public String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        createAttribute(DocumentItem.DOCUMENT_ID, ItemAttribute.TYPE.STRING);\n        createAttribute(DocumentItem.DOCUMENT_VERSION, ItemAttribute.TYPE.STRING);\n        createAttribute(ArchivedDocumentItem.SOURCEOBJECT_ID, ItemAttribute.TYPE.STRING);\n        createAttribute(DocumentItem.PROCESSINSTANCE_NAME, ItemAttribute.TYPE.STRING);\n        createAttribute(DocumentItem.PROCESS_DISPLAY_NAME, ItemAttribute.TYPE.STRING);\n        createAttribute(DocumentItem.PROCESSINSTANCE_ID, ItemAttribute.TYPE.STRING);\n        createAttribute(DocumentItem.PROCESSINSTANCE_NAME, ItemAttribute.TYPE.STRING);\n        createAttribute(DocumentItem.DOCUMENT_NAME, ItemAttribute.TYPE.STRING);\n        createAttribute(DocumentItem.DOCUMENT_AUTHOR, ItemAttribute.TYPE.STRING);\n        createAttribute(DocumentItem.DOCUMENT_CREATIONDATE, ItemAttribute.TYPE.STRING);\n        createAttribute(DocumentItem.DOCUMENT_HAS_CONTENT, ItemAttribute.TYPE.STRING);\n        createAttribute(DocumentItem.DOCUMENT_CONTENT_FILENAME, ItemAttribute.TYPE.STRING);\n        createAttribute(DocumentItem.DOCUMENT_CONTENT_MIMETYPE, ItemAttribute.TYPE.STRING);\n        createAttribute(DocumentItem.CONTENT_STORAGE_ID, ItemAttribute.TYPE.STRING);\n        createAttribute(DocumentItem.DOCUMENT_URL, ItemAttribute.TYPE.STRING);\n        createAttribute(ArchivedDocumentItem.ARCHIVED_DATE, ItemAttribute.TYPE.STRING);\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(DocumentItem.DOCUMENT_ID);\n    }\n\n    @Override\n    protected IItem _createItem() {\n        return new ArchivedDocumentItem();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/document/ArchivedDocumentItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.document;\n\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * Document item\n *\n * @author Gai Cuisha\n */\npublic class ArchivedDocumentItem extends DocumentItem {\n\n    public static final String SOURCEOBJECT_ID = \"sourceObjectId\";\n\n    public static final String ARCHIVED_DATE = \"archivedDate\";\n\n    public ArchivedDocumentItem() {\n        super();\n    }\n\n    public ArchivedDocumentItem(final IItem item) {\n        super(item);\n    }\n\n    public void setDocumentSourceObjectId(final String sourceObjectId) {\n        this.setAttribute(SOURCEOBJECT_ID, sourceObjectId);\n    }\n\n    public void setArchivedDate(final String archivedDate) {\n        this.setAttribute(ARCHIVED_DATE, archivedDate);\n    }\n\n    @Override\n    public ItemDefinition getItemDefinition() {\n        return new ArchivedDocumentDefinition();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/document/DocumentDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.document;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * @author Gai Cuisha\n */\npublic class DocumentDefinition extends ItemDefinition {\n\n    /**\n     * Singleton\n     */\n    public static DocumentDefinition get() {\n        return (DocumentDefinition) Definitions.get(TOKEN);\n    }\n\n    /**\n     * token\n     */\n    public static final String TOKEN = \"document\";\n\n    /**\n     * the URL of document\n     */\n    private static final String API_URL = \"../API/bpm/document\";\n\n    @Override\n    public String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        createAttribute(DocumentItem.DOCUMENT_ID, ItemAttribute.TYPE.STRING);\n        createAttribute(DocumentItem.DOCUMENT_VERSION, ItemAttribute.TYPE.STRING);\n        createAttribute(DocumentItem.PROCESSINSTANCE_ID, ItemAttribute.TYPE.STRING);\n        createAttribute(DocumentItem.PROCESSINSTANCE_NAME, ItemAttribute.TYPE.STRING);\n        createAttribute(DocumentItem.PROCESS_DISPLAY_NAME, ItemAttribute.TYPE.STRING);\n        createAttribute(DocumentItem.PROCESS_VERSION, ItemAttribute.TYPE.STRING);\n        createAttribute(DocumentItem.DOCUMENT_NAME, ItemAttribute.TYPE.STRING);\n        createAttribute(DocumentItem.DOCUMENT_AUTHOR, ItemAttribute.TYPE.STRING);\n        createAttribute(DocumentItem.DOCUMENT_CREATIONDATE, ItemAttribute.TYPE.STRING);\n        createAttribute(DocumentItem.DOCUMENT_HAS_CONTENT, ItemAttribute.TYPE.STRING);\n        createAttribute(DocumentItem.DOCUMENT_CONTENT_FILENAME, ItemAttribute.TYPE.STRING);\n        createAttribute(DocumentItem.DOCUMENT_CONTENT_MIMETYPE, ItemAttribute.TYPE.STRING);\n        createAttribute(DocumentItem.CONTENT_STORAGE_ID, ItemAttribute.TYPE.STRING);\n        createAttribute(DocumentItem.DOCUMENT_URL, ItemAttribute.TYPE.STRING);\n        createAttribute(DocumentItem.DOCUMENT_CREATION_TYPE, ItemAttribute.TYPE.STRING);\n        createAttribute(DocumentItem.DOCUMENT_UPLOAD, ItemAttribute.TYPE.STRING);\n\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(DocumentItem.DOCUMENT_ID);\n    }\n\n    @Override\n    protected IItem _createItem() {\n        return new DocumentItem();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/document/DocumentItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.document;\n\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * Document item\n *\n * @author Gai Cuisha\n */\npublic class DocumentItem extends Item {\n\n    public DocumentItem() {\n        super();\n    }\n\n    public DocumentItem(final IItem item) {\n        super(item);\n    }\n\n    public static final String DOCUMENT_ID = \"id\";\n\n    public static final String DOCUMENT_VERSION = \"version\";\n\n    public static final String PROCESSINSTANCE_ID = \"processinstanceId\";\n\n    public static final String PROCESSINSTANCE_NAME = \"processinstanceName\";\n\n    public static final String PROCESS_DISPLAY_NAME = \"processDisplayName\";\n\n    public static final String PROCESS_VERSION = \"processinstanceVersion\";\n\n    public static final String DOCUMENT_NAME = \"documentName\";\n\n    public static final String DOCUMENT_AUTHOR = \"documentAuthor\";\n\n    public static final String DOCUMENT_CREATIONDATE = \"documentCreationDate\";\n\n    public static final String DOCUMENT_HAS_CONTENT = \"documentHasContent\";\n\n    public static final String DOCUMENT_CONTENT_FILENAME = \"documentContentFileName\";\n\n    public static final String DOCUMENT_CONTENT_MIMETYPE = \"documentContentMimeType\";\n\n    public static final String CONTENT_STORAGE_ID = \"contentStorageId\";\n\n    public static final String DOCUMENT_URL = \"documentURL\";\n\n    public static final String DOCUMENT_CREATION_TYPE = \"DOCUMENT_CREATION_TYPE\";\n\n    public static final String DOCUMENT_UPLOAD = \"documentUpload\";\n\n    /*\n     * Filter are in uppercase due to a refactoring (delete FilterKey class), please put them in lowercase if there is\n     * no side effect\n     */\n    public static final String FILTER_CASE_ID = \"CASE_ID\";\n    public static final String FILTER_VIEW_TYPE = \"VIEW\";\n    public static final String FILTER_USER_ID = \"USER_ID\";\n\n    /* idem for values */\n    public static final String VALUE_VIEW_TYPE_ADMINISTRATOR = \"ADMINISTRATOR\";\n    public static final String VALUE_VIEW_TYPE_USER = \"USER\";\n    public static final String VALUE_VIEW_TYPE_TEAM_MANAGER = \"TEAM_MANAGER\";\n    public static final String VALUE_VIEW_TYPE_PROCESS_OWNER = \"PROCESS_OWNER\";\n\n    public void setDocumentId(final String documentId) {\n        this.setAttribute(DOCUMENT_ID, documentId);\n    }\n\n    public void setDocumentVersion(final String documentVersion) {\n        this.setAttribute(DOCUMENT_VERSION, documentVersion);\n    }\n\n    public void setCaseId(final String caseId) {\n        this.setAttribute(PROCESSINSTANCE_ID, caseId);\n    }\n\n    public void setCaseName(final String caseName) {\n        this.setAttribute(PROCESSINSTANCE_NAME, caseName);\n    }\n\n    public void setProcessDisplayName(final String caseName) {\n        this.setAttribute(PROCESS_DISPLAY_NAME, caseName);\n    }\n\n    public void setProcessVersion(final String caseVersion) {\n        this.setAttribute(PROCESS_VERSION, caseVersion);\n    }\n\n    public void setDocumentName(final String documentName) {\n        this.setAttribute(DOCUMENT_NAME, documentName);\n    }\n\n    public void setDocumentAuthor(final APIID userId) {\n        this.setAttribute(DOCUMENT_AUTHOR, userId.toString());\n    }\n\n    public void setDocumentAuthor(final Long userId) {\n        this.setAttribute(DOCUMENT_AUTHOR, userId.toString());\n    }\n\n    public void setDocumentCreationDate(final String documentCreationDate) {\n        this.setAttribute(DOCUMENT_CREATIONDATE, documentCreationDate);\n    }\n\n    public void setDocumentHasContent(final String documentHasContent) {\n        this.setAttribute(DOCUMENT_HAS_CONTENT, documentHasContent);\n    }\n\n    public void setDocumentFileName(final String documentFileName) {\n        this.setAttribute(DOCUMENT_CONTENT_FILENAME, documentFileName);\n    }\n\n    public void setDocumentMIMEType(final String documentMIMEType) {\n        this.setAttribute(DOCUMENT_CONTENT_MIMETYPE, documentMIMEType);\n    }\n\n    public void setDocumentStorageId(final String documentStorageId) {\n        this.setAttribute(CONTENT_STORAGE_ID, documentStorageId);\n    }\n\n    public void setDocumentURL(final String documentURL) {\n        this.setAttribute(DOCUMENT_URL, documentURL);\n    }\n\n    public void setDocumentCreationType(final String documentCreationType) {\n        this.setAttribute(DOCUMENT_CREATION_TYPE, documentCreationType);\n    }\n\n    public void setDocumentUpload(final String documentUpload) {\n        this.setAttribute(DOCUMENT_UPLOAD, documentUpload);\n    }\n\n    @Override\n    public ItemDefinition getItemDefinition() {\n        return new DocumentDefinition();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/AbstractContactDataDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.identity;\n\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.StringRegexpValidator;\n\n/**\n * @author Paul AMAR\n */\npublic abstract class AbstractContactDataDefinition extends ItemDefinition {\n\n    @Override\n    protected void defineAttributes() {\n        createAttribute(AbstractContactDataItem.ATTRIBUTE_EMAIL, ItemAttribute.TYPE.EMAIL);\n        createAttribute(AbstractContactDataItem.ATTRIBUTE_PHONE, ItemAttribute.TYPE.STRING)\n                .addValidator(new StringRegexpValidator(\"[\\\\d\\\\.\\\\(\\\\)#\\\\+]*\"));\n        createAttribute(AbstractContactDataItem.ATTRIBUTE_MOBILE, ItemAttribute.TYPE.STRING)\n                .addValidator(new StringRegexpValidator(\"[\\\\d\\\\.\\\\(\\\\)#\\\\+]*\"));\n        createAttribute(AbstractContactDataItem.ATTRIBUTE_FAX, ItemAttribute.TYPE.STRING)\n                .addValidator(new StringRegexpValidator(\"[\\\\d\\\\.\\\\(\\\\)#\\\\+]*\"));\n        createAttribute(AbstractContactDataItem.ATTRIBUTE_BUILDING, ItemAttribute.TYPE.STRING);\n        createAttribute(AbstractContactDataItem.ATTRIBUTE_ROOM, ItemAttribute.TYPE.STRING);\n        createAttribute(AbstractContactDataItem.ATTRIBUTE_ADDRESS, ItemAttribute.TYPE.STRING);\n        createAttribute(AbstractContactDataItem.ATTRIBUTE_ZIPCODE, ItemAttribute.TYPE.STRING);\n        createAttribute(AbstractContactDataItem.ATTRIBUTE_CITY, ItemAttribute.TYPE.STRING);\n        createAttribute(AbstractContactDataItem.ATTRIBUTE_STATE, ItemAttribute.TYPE.STRING);\n        createAttribute(AbstractContactDataItem.ATTRIBUTE_COUNTRY, ItemAttribute.TYPE.STRING);\n        createAttribute(AbstractContactDataItem.ATTRIBUTE_WEBSITE, ItemAttribute.TYPE.URL);\n\n    }\n\n    @Override\n    protected abstract IItem _createItem();\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/AbstractContactDataItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.identity;\n\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId;\n\n/**\n * @author Paul AMAR\n */\npublic abstract class AbstractContactDataItem extends Item implements ItemHasUniqueId {\n\n    public static final String ATTRIBUTE_EMAIL = \"email\";\n\n    public static final String ATTRIBUTE_PHONE = \"phone_number\";\n\n    public static final String ATTRIBUTE_MOBILE = \"mobile_number\";\n\n    public static final String ATTRIBUTE_FAX = \"fax_number\";\n\n    public static final String ATTRIBUTE_BUILDING = \"building\";\n\n    public static final String ATTRIBUTE_ROOM = \"room\";\n\n    public static final String ATTRIBUTE_ADDRESS = \"address\";\n\n    public static final String ATTRIBUTE_ZIPCODE = \"zipcode\";\n\n    public static final String ATTRIBUTE_CITY = \"city\";\n\n    public static final String ATTRIBUTE_STATE = \"state\";\n\n    public static final String ATTRIBUTE_COUNTRY = \"country\";\n\n    public static final String ATTRIBUTE_WEBSITE = \"website\";\n\n    public AbstractContactDataItem() {\n        super();\n    }\n\n    public AbstractContactDataItem(final IItem item) {\n        super(item);\n    }\n\n    // ///////////////////////////////////////////////////////\n    // // GETTERS\n    // //////////////////////////////////////////////////////\n\n    public String getEmail() {\n        return this.getAttributeValue(ATTRIBUTE_EMAIL);\n    }\n\n    public String getPhoneNumber() {\n        return this.getAttributeValue(ATTRIBUTE_PHONE);\n    }\n\n    public String getMobileNumber() {\n        return this.getAttributeValue(ATTRIBUTE_MOBILE);\n    }\n\n    public String getFaxNumber() {\n        return this.getAttributeValue(ATTRIBUTE_FAX);\n    }\n\n    public String getBuilding() {\n        return this.getAttributeValue(ATTRIBUTE_BUILDING);\n    }\n\n    public String getRoom() {\n        return this.getAttributeValue(ATTRIBUTE_ROOM);\n    }\n\n    public String getAddress() {\n        return this.getAttributeValue(ATTRIBUTE_ADDRESS);\n    }\n\n    public String getZipCode() {\n        return this.getAttributeValue(ATTRIBUTE_ZIPCODE);\n    }\n\n    public String getCity() {\n        return this.getAttributeValue(ATTRIBUTE_CITY);\n    }\n\n    public String getState() {\n        return this.getAttributeValue(ATTRIBUTE_STATE);\n    }\n\n    public String getCountry() {\n        return this.getAttributeValue(ATTRIBUTE_COUNTRY);\n    }\n\n    public String getWebsite() {\n        return this.getAttributeValue(ATTRIBUTE_WEBSITE);\n    }\n\n    // ///////////////////////////////////////////////////////\n    // // SETTERS\n    // //////////////////////////////////////////////////////\n\n    @Override\n    public void setId(final String id) {\n        setAttribute(ItemHasUniqueId.ATTRIBUTE_ID, id);\n    }\n\n    @Override\n    public void setId(final Long id) {\n        setAttribute(ItemHasUniqueId.ATTRIBUTE_ID, id);\n    }\n\n    public void setEmail(final String Email) {\n        this.setAttribute(ATTRIBUTE_EMAIL, Email);\n    }\n\n    public void setPhoneNumber(final String PhoneNumber) {\n        this.setAttribute(ATTRIBUTE_PHONE, PhoneNumber);\n    }\n\n    public void setMobileNumber(final String MobileNumber) {\n        this.setAttribute(ATTRIBUTE_MOBILE, MobileNumber);\n    }\n\n    public void setFaxNumber(final String FaxNumber) {\n        this.setAttribute(ATTRIBUTE_FAX, FaxNumber);\n    }\n\n    public void setBuilding(final String Building) {\n        this.setAttribute(ATTRIBUTE_BUILDING, Building);\n    }\n\n    public void setRoom(final String Room) {\n        this.setAttribute(ATTRIBUTE_ROOM, Room);\n    }\n\n    public void setAddress(final String Address) {\n        this.setAttribute(ATTRIBUTE_ADDRESS, Address);\n    }\n\n    public void setZipCode(final String ZipCode) {\n        this.setAttribute(ATTRIBUTE_ZIPCODE, ZipCode);\n    }\n\n    public void setCity(final String City) {\n        this.setAttribute(ATTRIBUTE_CITY, City);\n    }\n\n    public void setState(final String State) {\n        this.setAttribute(ATTRIBUTE_STATE, State);\n    }\n\n    public void setCountry(final String Country) {\n        this.setAttribute(ATTRIBUTE_COUNTRY, Country);\n    }\n\n    public void setWebsite(final String Website) {\n        this.setAttribute(ATTRIBUTE_WEBSITE, Website);\n    }\n\n    @Override\n    public abstract ItemDefinition getItemDefinition();\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/CustomUserInfoDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.identity;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * @author Vincent Elcrin\n */\npublic class CustomUserInfoDefinition extends ItemDefinition<CustomUserInfoItem> {\n\n    public static final String TOKEN = \"customuserinfo\";\n\n    @Override\n    protected String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return \"../API/customuserinfo/user\";\n    }\n\n    @Override\n    protected void defineAttributes() {\n        createAttribute(CustomUserInfoItem.ATTRIBUTE_DEFINITION_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(CustomUserInfoItem.ATTRIBUTE_USER_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(CustomUserInfoItem.ATTRIBUTE_VALUE, ItemAttribute.TYPE.STRING);\n    }\n\n    @Override\n    protected void defineDeploys() {\n        declareDeployable(CustomUserInfoItem.ATTRIBUTE_DEFINITION_ID, CustomUserInfoDefinitionDefinition.get());\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(CustomUserInfoItem.ATTRIBUTE_USER_ID, CustomUserInfoItem.ATTRIBUTE_DEFINITION_ID);\n    }\n\n    @Override\n    protected CustomUserInfoItem _createItem() {\n        return new CustomUserInfoItem();\n    }\n\n    public static CustomUserInfoDefinition get() {\n        return (CustomUserInfoDefinition) Definitions.get(TOKEN);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/CustomUserInfoDefinitionDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.identity;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * @author Vincent Elcrin\n */\npublic class CustomUserInfoDefinitionDefinition extends ItemDefinition<CustomUserInfoDefinitionItem> {\n\n    public static final String TOKEN = \"customuserinfo/definitions\";\n\n    @Override\n    protected String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return \"../API/customuserinfo/definition\";\n    }\n\n    @Override\n    protected void defineAttributes() {\n        createAttribute(CustomUserInfoDefinitionItem.ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(CustomUserInfoDefinitionItem.ATTRIBUTE_NAME, ItemAttribute.TYPE.STRING);\n        createAttribute(CustomUserInfoDefinitionItem.ATTRIBUTE_DESCRIPTION, ItemAttribute.TYPE.TEXT);\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(CustomUserInfoDefinitionItem.ATTRIBUTE_ID);\n    }\n\n    @Override\n    protected CustomUserInfoDefinitionItem _createItem() {\n        return new CustomUserInfoDefinitionItem();\n    }\n\n    public static CustomUserInfoDefinitionDefinition get() {\n        return (CustomUserInfoDefinitionDefinition) Definitions.get(TOKEN);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/CustomUserInfoDefinitionItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.identity;\n\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId;\n\n/**\n * @author Vincent Elcrin\n */\npublic class CustomUserInfoDefinitionItem extends Item implements ItemHasUniqueId {\n\n    public static final String ATTRIBUTE_NAME = \"name\";\n\n    public static final String ATTRIBUTE_DESCRIPTION = \"description\";\n\n    @Override\n    public CustomUserInfoDefinitionDefinition getItemDefinition() {\n        return new CustomUserInfoDefinitionDefinition();\n    }\n\n    @Override\n    public void setId(String id) {\n        setId(APIID.makeAPIID(id));\n    }\n\n    @Override\n    public void setId(Long id) {\n        setId(APIID.makeAPIID(id));\n    }\n\n    public String getName() {\n        return getAttributeValue(ATTRIBUTE_NAME);\n    }\n\n    public void setName(String name) {\n        setAttribute(ATTRIBUTE_NAME, name);\n    }\n\n    public String getDescription() {\n        return getAttributeValue(ATTRIBUTE_DESCRIPTION);\n    }\n\n    public void setDescription(String description) {\n        setAttribute(ATTRIBUTE_DESCRIPTION, description);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/CustomUserInfoItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.identity;\n\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\n\n/**\n * @author Vincent Elcrin\n */\npublic class CustomUserInfoItem extends Item {\n\n    public static final String FILTER_USER_ID = \"userId\";\n\n    public static final String ATTRIBUTE_DEFINITION_ID = \"definitionId\";\n\n    public static final String ATTRIBUTE_USER_ID = \"userId\";\n\n    public static final String ATTRIBUTE_VALUE = \"value\";\n\n    @Override\n    public CustomUserInfoDefinition getItemDefinition() {\n        return new CustomUserInfoDefinition();\n    }\n\n    public void setUserId(long userId) {\n        setAttribute(ATTRIBUTE_USER_ID, userId);\n    }\n\n    public void setDefinition(APIID id) {\n        setAttribute(ATTRIBUTE_DEFINITION_ID, id);\n    }\n\n    public void setDefinition(CustomUserInfoDefinitionItem definition) {\n        setDefinition(definition.getId());\n        setDeploy(ATTRIBUTE_DEFINITION_ID, definition);\n    }\n\n    public void setValue(String value) {\n        setAttribute(ATTRIBUTE_VALUE, value);\n    }\n\n    public long getUserId() {\n        return getAttributeValueAsLong(ATTRIBUTE_USER_ID);\n    }\n\n    public CustomUserInfoDefinitionItem getDefinition() {\n        return (CustomUserInfoDefinitionItem) getDeploy(ATTRIBUTE_DEFINITION_ID);\n    }\n\n    public String getValue() {\n        return getAttributeValue(ATTRIBUTE_VALUE);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/CustomUserInfoValueDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.identity;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\n\n/**\n * @author Vincent Elcrin\n */\npublic class CustomUserInfoValueDefinition extends CustomUserInfoDefinition {\n\n    public static final String TOKEN = \"customuserinfo/value\";\n\n    @Override\n    protected String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return \"../API/customuserinfo/value\";\n    }\n\n    public static CustomUserInfoValueDefinition get() {\n        return (CustomUserInfoValueDefinition) Definitions.get(TOKEN);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/GroupDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.identity;\n\nimport org.bonitasoft.web.rest.server.datastore.organization.Avatars;\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.FileIsImageOrServletPathValidator;\n\n/**\n * @author Yongtao Guo\n */\npublic class GroupDefinition extends ItemDefinition<GroupItem> {\n\n    /**\n     * Singleton\n     */\n    public static GroupDefinition get() {\n        return (GroupDefinition) Definitions.get(TOKEN);\n    }\n\n    public static final String TOKEN = \"group\";\n\n    /**\n     * the URL of user resource\n     */\n    private static final String API_URL = \"../API/identity/group\";\n\n    @Override\n    public String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(GroupItem.ATTRIBUTE_ID);\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        createAttribute(GroupItem.ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(GroupItem.ATTRIBUTE_NAME, ItemAttribute.TYPE.STRING);\n        createAttribute(GroupItem.ATTRIBUTE_DISPLAY_NAME, ItemAttribute.TYPE.STRING);\n        createAttribute(GroupItem.ATTRIBUTE_DESCRIPTION, ItemAttribute.TYPE.TEXT);\n        createAttribute(GroupItem.ATTRIBUTE_CREATION_DATE, ItemAttribute.TYPE.DATETIME);\n        createAttribute(GroupItem.ATTRIBUTE_CREATED_BY_USER_ID, ItemAttribute.TYPE.STRING);\n        createAttribute(GroupItem.ATTRIBUTE_LAST_UPDATE_DATE, ItemAttribute.TYPE.DATETIME);\n        createAttribute(GroupItem.ATTRIBUTE_ICON, ItemAttribute.TYPE.STRING)\n                .addValidator(new FileIsImageOrServletPathValidator(Avatars.PATH));\n        createAttribute(GroupItem.ATTRIBUTE_PARENT_PATH, ItemAttribute.TYPE.STRING);\n        createAttribute(GroupItem.ATTRIBUTE_PARENT_GROUP_ID, ItemAttribute.TYPE.STRING);\n    }\n\n    @Override\n    public GroupItem _createItem() {\n        return new GroupItem();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/GroupItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.identity;\n\nimport java.util.Date;\n\nimport org.bonitasoft.web.toolkit.client.common.util.StringUtil;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasCreator;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasDualName;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasIcon;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasLastUpdateDate;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId;\n\n/**\n * @author Yongtao Guo\n */\npublic class GroupItem extends Item\n        implements ItemHasDualName, ItemHasUniqueId, ItemHasCreator, ItemHasLastUpdateDate, ItemHasIcon {\n\n    public static final String ATTRIBUTE_DESCRIPTION = \"description\";\n    public static final String ATTRIBUTE_PATH = \"path\";\n    public static final String ATTRIBUTE_PARENT_PATH = \"parent_path\";\n    public static final String ATTRIBUTE_PARENT_GROUP_ID = \"parent_group_id\";\n\n    public GroupItem() {\n        super();\n    }\n\n    public GroupItem(final IItem item) {\n        super(item);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // COUNTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public static final String COUNTER_NUMBER_OF_USERS = \"number_of_users\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // GETTERS AND SETTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // GETTERS\n\n    @Override\n    public ItemDefinition<GroupItem> getItemDefinition() {\n        return new GroupDefinition();\n    }\n\n    @Override\n    public String getName() {\n        return this.getAttributeValue(ATTRIBUTE_NAME);\n    }\n\n    @Override\n    public String getDisplayName() {\n        if (!StringUtil.isBlank(this.getAttributeValue(ATTRIBUTE_DISPLAY_NAME))) {\n            return this.getAttributeValue(ATTRIBUTE_DISPLAY_NAME);\n        } else {\n            return this.getAttributeValue(ATTRIBUTE_NAME);\n        }\n    }\n\n    public String getDescription() {\n        return this.getAttributeValue(ATTRIBUTE_DESCRIPTION);\n    }\n\n    @Override\n    public APIID getCreatedByUserId() {\n        return this.getAttributeValueAsAPIID(ATTRIBUTE_CREATED_BY_USER_ID);\n    }\n\n    @Override\n    public UserItem getCreatedByUser() {\n        return new UserItem(getDeploy(ATTRIBUTE_CREATED_BY_USER_ID));\n    }\n\n    @Override\n    public Date getCreationDate() {\n        return this.getAttributeValueAsDate(ATTRIBUTE_CREATION_DATE);\n    }\n\n    @Override\n    public Date getLastUpdateDate() {\n        return this.getAttributeValueAsDate(ATTRIBUTE_LAST_UPDATE_DATE);\n    }\n\n    @Override\n    public String getIcon() {\n        return this.getAttributeValue(ATTRIBUTE_ICON);\n    }\n\n    public String getParentPath() {\n        return getAttributeValue(ATTRIBUTE_PARENT_PATH);\n    }\n\n    public String getPath() {\n        return getAttributeValue(ATTRIBUTE_PATH);\n    }\n\n    public String getParentGroupId() {\n        return getAttributeValue(ATTRIBUTE_PARENT_GROUP_ID);\n    }\n\n    // SETTERS\n\n    @Override\n    public void setId(final String id) {\n        this.setAttribute(ATTRIBUTE_ID, id);\n    }\n\n    @Override\n    public void setId(final Long id) {\n        this.setAttribute(ATTRIBUTE_ID, id.toString());// TODO delete the tostring call when severin will commit\n    }\n\n    @Override\n    public void setName(final String name) {\n        this.setAttribute(ATTRIBUTE_NAME, name);\n    }\n\n    @Override\n    public void setCreatedByUserId(final APIID id) {\n        this.setAttribute(ATTRIBUTE_CREATED_BY_USER_ID, id);\n    }\n\n    @Override\n    public void setCreatedByUserId(final String id) {\n        this.setAttribute(ATTRIBUTE_CREATED_BY_USER_ID, id);\n\n    }\n\n    @Override\n    public void setCreatedByUserId(final Long id) {\n        this.setAttribute(ATTRIBUTE_CREATED_BY_USER_ID, id.toString());\n\n    }\n\n    @Override\n    public void setCreationDate(final String date) {\n        this.setAttribute(ATTRIBUTE_CREATION_DATE, date);\n\n    }\n\n    @Override\n    public void setCreationDate(final Date date) {\n        this.setAttribute(ATTRIBUTE_CREATION_DATE, date);\n\n    }\n\n    @Override\n    public void setDisplayName(final String displayName) {\n        this.setAttribute(ATTRIBUTE_DISPLAY_NAME, displayName);\n    }\n\n    public void setDescription(final String description) {\n        this.setAttribute(ATTRIBUTE_DESCRIPTION, description);\n    }\n\n    @Override\n    public void setLastUpdateDate(final String date) {\n        this.setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date);\n    }\n\n    @Override\n    public void setLastUpdateDate(final Date date) {\n        this.setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date);\n    }\n\n    @Override\n    public void setIcon(final String icon) {\n        this.setAttribute(ATTRIBUTE_ICON, icon);\n    }\n\n    public void setParentPath(final String parentPath) {\n        this.setAttribute(ATTRIBUTE_PARENT_PATH, parentPath);\n    }\n\n    public void setPath(final String parentPath) {\n        this.setAttribute(ATTRIBUTE_PATH, parentPath);\n    }\n\n    public void setParentGroupId(final String parentGroupId) {\n        this.setAttribute(ATTRIBUTE_PARENT_GROUP_ID, parentGroupId);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/MemberType.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.identity;\n\n/**\n * @author Yongtao Guo\n */\npublic enum MemberType {\n\n    USER, GROUP, ROLE, MEMBERSHIP;\n\n    @Override\n    public String toString() {\n        return super.toString().toLowerCase();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/MembershipDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.identity;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * User definition\n *\n * @author Séverin Moussel\n */\npublic class MembershipDefinition extends ItemDefinition {\n\n    /**\n     * Singleton\n     */\n    public static MembershipDefinition get() {\n        return (MembershipDefinition) Definitions.get(TOKEN);\n    }\n\n    public static final String TOKEN = \"membership\";\n\n    /**\n     * the URL of user resource\n     */\n    private static final String API_URL = \"../API/identity/membership\";\n\n    @Override\n    public String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(MembershipItem.ATTRIBUTE_USER_ID, MembershipItem.ATTRIBUTE_GROUP_ID,\n                MembershipItem.ATTRIBUTE_ROLE_ID);\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        createAttribute(MembershipItem.ATTRIBUTE_USER_ID, ItemAttribute.TYPE.ITEM_ID)\n                .isMandatory();\n\n        createAttribute(MembershipItem.ATTRIBUTE_GROUP_ID, ItemAttribute.TYPE.ITEM_ID)\n                .isMandatory();\n\n        createAttribute(MembershipItem.ATTRIBUTE_ROLE_ID, ItemAttribute.TYPE.ITEM_ID)\n                .isMandatory();\n\n        createAttribute(MembershipItem.ATTRIBUTE_ASSIGNED_BY_USER_ID, ItemAttribute.TYPE.ITEM_ID);\n\n        createAttribute(MembershipItem.ATTRIBUTE_ASSIGNED_DATE, ItemAttribute.TYPE.DATETIME);\n    }\n\n    @Override\n    public IItem _createItem() {\n        return new MembershipItem();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/MembershipItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.identity;\n\nimport java.util.Date;\n\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Séverin Moussel\n */\npublic class MembershipItem extends Item {\n\n    public MembershipItem() {\n        super();\n    }\n\n    public MembershipItem(final IItem item) {\n        super(item);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // ATTRIBUTES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public static final String ATTRIBUTE_USER_ID = \"user_id\";\n\n    public static final String ATTRIBUTE_GROUP_ID = \"group_id\";\n\n    public static final String ATTRIBUTE_ROLE_ID = \"role_id\";\n\n    public static final String ATTRIBUTE_ASSIGNED_BY_USER_ID = \"assigned_by_user_id\";\n\n    public static final String ATTRIBUTE_ASSIGNED_DATE = \"assigned_date\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // ATTRIBUTES VALUES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // FILTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // COUNTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // DEPLOYS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public UserItem getUser() {\n        return new UserItem(getDeploy(ATTRIBUTE_USER_ID));\n    }\n\n    public RoleItem getRole() {\n        return new RoleItem(getDeploy(ATTRIBUTE_ROLE_ID));\n    }\n\n    public GroupItem getGroup() {\n        return new GroupItem(getDeploy(ATTRIBUTE_GROUP_ID));\n    }\n\n    public UserItem getAssignedByUser() {\n        return new UserItem(getDeploy(ATTRIBUTE_ASSIGNED_BY_USER_ID));\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // GETTERS AND SETTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // GETTERS\n\n    public APIID getUserId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_USER_ID);\n    }\n\n    public APIID getGroupId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_GROUP_ID);\n    }\n\n    public APIID getRoleId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_ROLE_ID);\n    }\n\n    public APIID getAssignedByUserId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_ASSIGNED_BY_USER_ID);\n    }\n\n    public Date getAssignedDate() {\n        return getAttributeValueAsDate(ATTRIBUTE_ASSIGNED_DATE);\n    }\n\n    // SETTERS\n\n    public void setUserId(final String id) {\n        setAttribute(ATTRIBUTE_USER_ID, id);\n    }\n\n    public void setUserId(final Long id) {\n        setAttribute(ATTRIBUTE_USER_ID, id);\n    }\n\n    public void setUserId(final APIID id) {\n        setAttribute(ATTRIBUTE_USER_ID, id);\n    }\n\n    public void setRoleId(final String id) {\n        setAttribute(ATTRIBUTE_ROLE_ID, id);\n    }\n\n    public void setRoleId(final Long id) {\n        setAttribute(ATTRIBUTE_ROLE_ID, id);\n    }\n\n    public void setRoleId(final APIID id) {\n        setAttribute(ATTRIBUTE_ROLE_ID, id);\n    }\n\n    public void setGroupId(final String id) {\n        setAttribute(ATTRIBUTE_GROUP_ID, id);\n    }\n\n    public void setGroupId(final Long id) {\n        setAttribute(ATTRIBUTE_GROUP_ID, id);\n    }\n\n    public void setGroupId(final APIID id) {\n        setAttribute(ATTRIBUTE_GROUP_ID, id);\n    }\n\n    public void setAssignedByUserId(final String id) {\n        setAttribute(ATTRIBUTE_ASSIGNED_BY_USER_ID, id);\n    }\n\n    public void setAssignedByUserId(final Long id) {\n        setAttribute(ATTRIBUTE_ASSIGNED_BY_USER_ID, id);\n    }\n\n    public void setAssignedByUserId(final APIID id) {\n        setAttribute(ATTRIBUTE_ASSIGNED_BY_USER_ID, id);\n    }\n\n    public void setAssignedDate(final Date date) {\n        setAttribute(ATTRIBUTE_ASSIGNED_DATE, date);\n    }\n\n    public void setAssignedDate(final String date) {\n        setAttribute(ATTRIBUTE_ASSIGNED_DATE, date);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // UTILS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public ItemDefinition getItemDefinition() {\n        return new MembershipDefinition();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/PersonalContactDataDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.identity;\n\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\n\n/**\n * @author Paul AMAR\n */\npublic class PersonalContactDataDefinition extends AbstractContactDataDefinition {\n\n    public static final String TOKEN = \"personalcontactdata\";\n\n    /**\n     * the URL of user resource\n     */\n    private static final String API_URL = \"../API/identity/personalcontactdata\";\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(PersonalContactDataItem.ATTRIBUTE_ID);\n    }\n\n    @Override\n    protected IItem _createItem() {\n        return new PersonalContactDataItem();\n    }\n\n    @Override\n    protected String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/PersonalContactDataItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.identity;\n\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Paul AMAR\n */\npublic class PersonalContactDataItem extends AbstractContactDataItem {\n\n    /**\n     * Default Constructor.\n     */\n    public PersonalContactDataItem() {\n        super();\n    }\n\n    /**\n     * Default Constructor.\n     *\n     * @param item\n     */\n    public PersonalContactDataItem(final IItem item) {\n        super(item);\n    }\n\n    @Override\n    public ItemDefinition getItemDefinition() {\n        return new PersonalContactDataDefinition();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/ProfessionalContactDataDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.identity;\n\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId;\n\n/**\n * @author Paul AMAR\n */\npublic class ProfessionalContactDataDefinition extends AbstractContactDataDefinition {\n\n    public static final String TOKEN = \"professionalcontactdata\";\n\n    /**\n     * the URL of user resource\n     */\n    private static final String API_URL = \"../API/identity/professionalcontactdata\";\n\n    @Override\n    protected IItem _createItem() {\n        return new ProfessionalContactDataItem();\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(ItemHasUniqueId.ATTRIBUTE_ID);\n    }\n\n    @Override\n    protected String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/ProfessionalContactDataItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.identity;\n\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Paul AMAR\n */\npublic class ProfessionalContactDataItem extends AbstractContactDataItem {\n\n    /**\n     * Default Constructor.\n     */\n    public ProfessionalContactDataItem() {\n        super();\n    }\n\n    /**\n     * Default Constructor.\n     *\n     * @param item\n     */\n    public ProfessionalContactDataItem(final IItem item) {\n        super(item);\n    }\n\n    @Override\n    public ItemDefinition getItemDefinition() {\n        return new ProfessionalContactDataDefinition();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/RoleDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.identity;\n\nimport org.bonitasoft.web.rest.server.datastore.organization.Avatars;\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.FileIsImageOrServletPathValidator;\n\n/**\n * @author Yongtao Guo\n */\npublic class RoleDefinition extends ItemDefinition<RoleItem> {\n\n    /**\n     * Singleton\n     */\n    public static RoleDefinition get() {\n        return (RoleDefinition) Definitions.get(TOKEN);\n    }\n\n    public static final String TOKEN = \"role\";\n\n    /**\n     * the URL of user resource\n     */\n    private static final String API_URL = \"../API/identity/role\";\n\n    @Override\n    public String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(RoleItem.ATTRIBUTE_ID);\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        createAttribute(RoleItem.ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID);\n\n        createAttribute(RoleItem.ATTRIBUTE_NAME, ItemAttribute.TYPE.STRING);\n\n        createAttribute(GroupItem.ATTRIBUTE_DISPLAY_NAME, ItemAttribute.TYPE.STRING);\n\n        createAttribute(RoleItem.ATTRIBUTE_DESCRIPTION, ItemAttribute.TYPE.TEXT);\n\n        createAttribute(RoleItem.ATTRIBUTE_CREATION_DATE, ItemAttribute.TYPE.DATETIME);\n\n        createAttribute(RoleItem.ATTRIBUTE_CREATED_BY_USER_ID, ItemAttribute.TYPE.ITEM_ID);\n\n        createAttribute(RoleItem.ATTRIBUTE_LAST_UPDATE_DATE, ItemAttribute.TYPE.DATETIME);\n\n        createAttribute(RoleItem.ATTRIBUTE_ICON, ItemAttribute.TYPE.STRING)\n                .addValidator(new FileIsImageOrServletPathValidator(Avatars.PATH));\n    }\n\n    @Override\n    public RoleItem _createItem() {\n        return new RoleItem();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/RoleItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.identity;\n\nimport java.util.Date;\n\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasCreator;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasDualName;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasIcon;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasLastUpdateDate;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId;\n\n/**\n * @author Séverin Moussel\n */\npublic class RoleItem extends Item\n        implements ItemHasDualName, ItemHasUniqueId, ItemHasCreator, ItemHasLastUpdateDate, ItemHasIcon {\n\n    public RoleItem() {\n        super();\n    }\n\n    public RoleItem(final IItem item) {\n        super(item);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // ATTRIBUTES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public static final String ATTRIBUTE_DESCRIPTION = \"description\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // ATTRIBUTES VALUES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // FILTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // COUNTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public static final String COUNTER_NUMBER_OF_USERS = \"number_of_users\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // DEPLOYS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public UserItem getCreatedByUser() {\n        return new UserItem(getDeploy(ATTRIBUTE_CREATED_BY_USER_ID));\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // GETTERS AND SETTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // GETTERS\n\n    @Override\n    public String getName() {\n        return getAttributeValue(ATTRIBUTE_NAME);\n    }\n\n    @Override\n    public String getDisplayName() {\n        return getAttributeValue(ATTRIBUTE_DISPLAY_NAME);\n    }\n\n    public String getDescription() {\n        return getAttributeValue(ATTRIBUTE_DESCRIPTION);\n    }\n\n    @Override\n    public String getIcon() {\n        return getAttributeValue(ATTRIBUTE_ICON);\n    }\n\n    @Override\n    public Date getLastUpdateDate() {\n        return getAttributeValueAsDate(ATTRIBUTE_LAST_UPDATE_DATE);\n    }\n\n    @Override\n    public Date getCreationDate() {\n        return getAttributeValueAsDate(ATTRIBUTE_CREATION_DATE);\n    }\n\n    @Override\n    public APIID getCreatedByUserId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_CREATED_BY_USER_ID);\n    }\n\n    public Long getNumberOfUsers() {\n        return getAttributeValueAsLong(COUNTER_NUMBER_OF_USERS);\n    }\n\n    // SETTERS\n\n    public void setDescription(final String description) {\n        setAttribute(ATTRIBUTE_DESCRIPTION, description);\n    }\n\n    @Override\n    public void setIcon(final String icon) {\n        setAttribute(ATTRIBUTE_ICON, icon);\n    }\n\n    @Override\n    public void setLastUpdateDate(final String date) {\n        setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date);\n    }\n\n    @Override\n    public void setLastUpdateDate(final Date date) {\n        setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date);\n    }\n\n    @Override\n    public void setCreationDate(final String date) {\n        setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date);\n    }\n\n    @Override\n    public void setCreationDate(final Date date) {\n        setAttribute(ATTRIBUTE_CREATION_DATE, date);\n    }\n\n    @Override\n    public void setCreatedByUserId(final String id) {\n        setAttribute(ATTRIBUTE_CREATED_BY_USER_ID, id);\n    }\n\n    @Override\n    public void setCreatedByUserId(final Long id) {\n        setAttribute(ATTRIBUTE_CREATED_BY_USER_ID, id);\n    }\n\n    @Override\n    public void setCreatedByUserId(final APIID id) {\n        setAttribute(ATTRIBUTE_CREATED_BY_USER_ID, id);\n    }\n\n    @Override\n    public void setId(final String id) {\n        setAttribute(ATTRIBUTE_ID, id);\n    }\n\n    @Override\n    public void setId(final Long id) {\n        setAttribute(ATTRIBUTE_ID, id);\n    }\n\n    @Override\n    public void setName(final String name) {\n        setAttribute(ATTRIBUTE_NAME, name);\n    }\n\n    @Override\n    public void setDisplayName(final String displayName) {\n        setAttribute(ATTRIBUTE_DISPLAY_NAME, displayName);\n    }\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // UTILS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public ItemDefinition getItemDefinition() {\n        return Definitions.get(RoleDefinition.TOKEN);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/UserDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.identity;\n\nimport org.bonitasoft.web.rest.server.datastore.organization.Avatars;\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.FileIsImageOrServletPathValidator;\n\n/**\n * User definition\n *\n * @author Séverin Moussel\n */\npublic class UserDefinition extends ItemDefinition<UserItem> {\n\n    /**\n     * Singleton\n     */\n    public static UserDefinition get() {\n        return (UserDefinition) Definitions.get(TOKEN);\n    }\n\n    public static final String TOKEN = \"user\";\n\n    /**\n     * the URL of user resource\n     */\n    private static final String API_URL = \"../API/identity/user\";\n\n    @Override\n    public String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(UserItem.ATTRIBUTE_ID);\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        createAttribute(UserItem.ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(UserItem.ATTRIBUTE_FIRSTNAME, ItemAttribute.TYPE.STRING)\n                .isMandatory();\n        createAttribute(UserItem.ATTRIBUTE_LASTNAME, ItemAttribute.TYPE.STRING)\n                .isMandatory();\n        createAttribute(UserItem.ATTRIBUTE_ICON, ItemAttribute.TYPE.STRING)\n                .addValidator(new FileIsImageOrServletPathValidator(Avatars.PATH));\n        createAttribute(UserItem.ATTRIBUTE_USERNAME, ItemAttribute.TYPE.STRING)\n                .isMandatory();\n        createAttribute(UserItem.ATTRIBUTE_PASSWORD, ItemAttribute.TYPE.PASSWORD);\n\n        createAttribute(UserItem.ATTRIBUTE_CREATION_DATE, ItemAttribute.TYPE.DATETIME);\n        createAttribute(UserItem.ATTRIBUTE_CREATED_BY_USER_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(UserItem.ATTRIBUTE_LAST_UPDATE_DATE, ItemAttribute.TYPE.DATETIME);\n        createAttribute(UserItem.ATTRIBUTE_LAST_CONNECTION_DATE, ItemAttribute.TYPE.DATETIME);\n\n        createAttribute(UserItem.ATTRIBUTE_TITLE, ItemAttribute.TYPE.STRING);\n        createAttribute(UserItem.ATTRIBUTE_JOB_TITLE, ItemAttribute.TYPE.STRING);\n        createAttribute(UserItem.ATTRIBUTE_MANAGER_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(UserItem.ATTRIBUTE_ENABLED, ItemAttribute.TYPE.BOOLEAN);\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see org.bonitasoft.web.toolkit.client.data.item.ItemDefinition#defineDeploys()\n     */\n    @Override\n    protected void defineDeploys() {\n        super.defineDeploys();\n        declareDeployable(UserItem.DEPLOY_PROFESSIONAL_DATA, Definitions.get(ProfessionalContactDataDefinition.TOKEN));\n        declareDeployable(UserItem.DEPLOY_PERSONAL_DATA, Definitions.get(PersonalContactDataDefinition.TOKEN));\n    }\n\n    @Override\n    public UserItem _createItem() {\n        return new UserItem();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/UserItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.identity;\n\nimport java.util.Date;\n\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasCreator;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasIcon;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasLastUpdateDate;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId;\n\n/**\n * @author Séverin Moussel\n */\npublic class UserItem extends Item implements ItemHasUniqueId, ItemHasLastUpdateDate, ItemHasCreator, ItemHasIcon {\n\n    public static final String DEFAULT_USER_ICON = \"icons/default/icon_user.png\";\n\n    public static final String ATTRIBUTE_FIRSTNAME = \"firstname\";\n\n    public static final String ATTRIBUTE_LASTNAME = \"lastname\";\n\n    public static final String ATTRIBUTE_PASSWORD = \"password\";\n\n    public static final String ATTRIBUTE_USERNAME = \"userName\";\n\n    public static final String ATTRIBUTE_MANAGER_ID = \"manager_id\";\n\n    public static final String ATTRIBUTE_LAST_CONNECTION_DATE = \"last_connection\";\n\n    public static final String ATTRIBUTE_TITLE = \"title\";\n\n    public static final String ATTRIBUTE_JOB_TITLE = \"job_title\";\n\n    public static final String ATTRIBUTE_STATE = \"user_state\";\n\n    public static final String ATTRIBUTE_ENABLED = \"enabled\";\n\n    public UserItem() {\n        super();\n    }\n\n    public UserItem(final IItem item) {\n        super(item);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // ATTRIBUTES VALUES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public static final String VALUE_ACTIVATION_STATE_DISABLED = \"DISABLED\";\n\n    public static final String VALUE_ACTIVATION_STATE_ENABLED = \"ENABLED\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // FILTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public static final String FILTER_ROLE_ID = \"role_id\";\n\n    public static final String FILTER_GROUP_ID = \"group_id\";\n\n    public static final String FILTER_PROFILE_ID = \"profile_id\";\n\n    public static final String FILTER_INDIRECT_PROFILE_ID = \"indirect_profile_id\";\n\n    public static final String FILTER_PROCESS_ID = \"process_id\";\n\n    public static final String FILTER_HUMAN_TASK_ID = \"task_id\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // COUNTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public static final String COUNTER_OPEN_TASKS = \"open_tasks\";\n\n    public static final String COUNTER_OVERDUE_TASKS = \"overdue_tasks\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // GETTERS AND SETTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // GETTERS\n\n    public String getFirstName() {\n        return this.getAttributeValue(ATTRIBUTE_FIRSTNAME);\n    }\n\n    public String getLastName() {\n        return this.getAttributeValue(ATTRIBUTE_LASTNAME);\n    }\n\n    public String getPassword() {\n        return this.getAttributeValue(ATTRIBUTE_PASSWORD);\n    }\n\n    public String getUserName() {\n        return this.getAttributeValue(ATTRIBUTE_USERNAME);\n    }\n\n    public APIID getManagerId() {\n        return APIID.makeAPIID(this.getAttributeValue(ATTRIBUTE_MANAGER_ID));\n    }\n\n    @Override\n    public String getIcon() {\n        return this.getAttributeValue(ATTRIBUTE_ICON);\n    }\n\n    @Override\n    public Date getCreationDate() {\n        return this.getAttributeValueAsDate(ATTRIBUTE_CREATION_DATE);\n    }\n\n    @Override\n    public APIID getCreatedByUserId() {\n        return APIID.makeAPIID(this.getAttributeValue(ATTRIBUTE_CREATED_BY_USER_ID));\n    }\n\n    @Override\n    public Date getLastUpdateDate() {\n        return this.getAttributeValueAsDate(ATTRIBUTE_LAST_UPDATE_DATE);\n    }\n\n    public String getState() {\n        return this.getAttributeValue(ATTRIBUTE_STATE);\n    }\n\n    public String getLastConnectionDate() {\n        return this.getAttributeValue(ATTRIBUTE_LAST_CONNECTION_DATE);\n    }\n\n    public String getTitle() {\n        return this.getAttributeValue(ATTRIBUTE_TITLE);\n    }\n\n    public String getJobTitle() {\n        return this.getAttributeValue(ATTRIBUTE_JOB_TITLE);\n    }\n\n    public boolean isEnabled() {\n        return \"true\".equals(getAttributeValue(ATTRIBUTE_ENABLED));\n    }\n\n    public void setEnabled(boolean enabled) {\n        setAttribute(ATTRIBUTE_ENABLED, String.valueOf(enabled));\n    }\n\n    // SETTERS\n\n    @Override\n    public void setId(final String id) {\n        this.setAttribute(\"id\", id);\n    }\n\n    @Override\n    public void setId(final Long id) {\n        this.setAttribute(\"id\", id.toString());\n    }\n\n    public void setFirstName(final String firstName) {\n        this.setAttribute(ATTRIBUTE_FIRSTNAME, firstName);\n    }\n\n    public void setLastName(final String lastName) {\n        this.setAttribute(ATTRIBUTE_LASTNAME, lastName);\n    }\n\n    public void setPassword(final String password) {\n        this.setAttribute(ATTRIBUTE_PASSWORD, password);\n    }\n\n    public void setUserName(final String userName) {\n        this.setAttribute(ATTRIBUTE_USERNAME, userName);\n    }\n\n    public void setManagerId(final String id) {\n        this.setAttribute(ATTRIBUTE_MANAGER_ID, id);\n    }\n\n    public void setManagerId(final Long id) {\n        setManagerId(id.toString());\n    }\n\n    public void setManagerId(final APIID id) {\n        setAttribute(ATTRIBUTE_MANAGER_ID, id);\n    }\n\n    public void setState(final String state) {\n        this.setAttribute(ATTRIBUTE_STATE, state);\n    }\n\n    @Override\n    public void setIcon(final String iconPath) {\n        this.setAttribute(ATTRIBUTE_ICON, iconPath);\n    }\n\n    @Override\n    public void setCreationDate(final String date) {\n        this.setAttribute(ATTRIBUTE_CREATION_DATE, date);\n    }\n\n    @Override\n    public void setCreationDate(final Date date) {\n        this.setAttribute(ATTRIBUTE_CREATION_DATE, date);\n    }\n\n    @Override\n    public void setCreatedByUserId(final String id) {\n        this.setAttribute(ATTRIBUTE_CREATED_BY_USER_ID, id);\n    }\n\n    @Override\n    public void setCreatedByUserId(final Long id) {\n        setCreatedByUserId(id.toString());\n    }\n\n    @Override\n    public void setCreatedByUserId(final APIID id) {\n        this.setAttribute(ATTRIBUTE_CREATED_BY_USER_ID, id);\n    }\n\n    @Override\n    public void setLastUpdateDate(final String date) {\n        this.setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date);\n    }\n\n    @Override\n    public void setLastUpdateDate(final Date date) {\n        this.setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date);\n    }\n\n    public void setLastConnectionDate(final String date) {\n        this.setAttribute(ATTRIBUTE_LAST_CONNECTION_DATE, date);\n    }\n\n    public void setLastConnectionDate(final Date date) {\n        this.setAttribute(ATTRIBUTE_LAST_CONNECTION_DATE, date);\n    }\n\n    public void setTitle(final String title) {\n        this.setAttribute(ATTRIBUTE_TITLE, title);\n    }\n\n    public void setJobTitle(final String jobTitle) {\n        this.setAttribute(ATTRIBUTE_JOB_TITLE, jobTitle);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // DEPLOYS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public static final String DEPLOY_PROFESSIONAL_DATA = \"professional_data\";\n\n    public static final String DEPLOY_PERSONAL_DATA = \"personnal_data\";\n\n    public ProfessionalContactDataItem getProfessionalData() {\n        return new ProfessionalContactDataItem(getDeploy(DEPLOY_PROFESSIONAL_DATA));\n    }\n\n    public PersonalContactDataItem getPersonalData() {\n        return new PersonalContactDataItem(getDeploy(DEPLOY_PERSONAL_DATA));\n    }\n\n    public UserItem getManager() {\n        return new UserItem(getDeploy(ATTRIBUTE_MANAGER_ID));\n    }\n\n    @Override\n    public UserItem getCreatedByUser() {\n        return new UserItem(getDeploy(ATTRIBUTE_CREATED_BY_USER_ID));\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // UTILS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public ItemDefinition<UserItem> getItemDefinition() {\n        return new UserDefinition();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/platform/PlatformDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.platform;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * @author Zhiheng Yang\n */\npublic class PlatformDefinition extends ItemDefinition<PlatformItem> {\n\n    /**\n     * Singleton\n     */\n    public static PlatformDefinition get() {\n        return (PlatformDefinition) Definitions.get(TOKEN);\n    }\n\n    public final static String TOKEN = \"platformInfo\";\n\n    private static final String API_URL = \"../API/platform/platform\";\n\n    @Override\n    public String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        createAttribute(PlatformItem.ATTRIBUTE_CREATED_DATE, ItemAttribute.TYPE.STRING);\n        createAttribute(PlatformItem.ATTRIBUTE_INIT_VERSION, ItemAttribute.TYPE.STRING);\n        createAttribute(PlatformItem.ATTRIBUTE_VERSION, ItemAttribute.TYPE.STRING);\n        createAttribute(PlatformItem.ATTRIBUTE_CREATEDBY, ItemAttribute.TYPE.STRING);\n        createAttribute(PlatformItem.ATTRIBUTE_STATE, ItemAttribute.TYPE.STRING);\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(PlatformItem.ATTRIBUTE_CREATED_DATE);\n    }\n\n    @Override\n    protected PlatformItem _createItem() {\n        return new PlatformItem();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/platform/PlatformItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.platform;\n\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Zhiheng Yang\n */\npublic class PlatformItem extends Item {\n\n    public PlatformItem() {\n        super();\n    }\n\n    public PlatformItem(final IItem item) {\n        super(item);\n    }\n\n    public final static String ATTRIBUTE_VERSION = \"version\";\n\n    public final static String ATTRIBUTE_INIT_VERSION = \"initialVersion\";\n\n    public final static String ATTRIBUTE_CREATED_DATE = \"created\";\n\n    public final static String ATTRIBUTE_CREATEDBY = \"createdBy\";\n\n    public final static String ATTRIBUTE_STATE = \"state\";\n\n    public PlatformItem(final String version, final String initVersion, final String createdDate,\n            final String createdBy, final String state) {\n        this.setAttribute(ATTRIBUTE_VERSION, version);\n        this.setAttribute(ATTRIBUTE_INIT_VERSION, initVersion);\n        this.setAttribute(ATTRIBUTE_CREATED_DATE, createdDate);\n        this.setAttribute(ATTRIBUTE_CREATEDBY, createdBy);\n        this.setAttribute(ATTRIBUTE_STATE, state);\n\n    }\n\n    @Override\n    public ItemDefinition getItemDefinition() {\n        return new PlatformDefinition();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/portal/page/PageDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.portal.page;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * @author Fabio Lombardi\n */\npublic class PageDefinition extends ItemDefinition<PageItem> {\n\n    public static final String TOKEN = \"page\";\n\n    protected static final String API_URL = \"../API/portal/page\";\n\n    public static PageDefinition get() {\n        return (PageDefinition) Definitions.get(TOKEN);\n    }\n\n    @Override\n    protected String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        createAttribute(PageItem.ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(PageItem.ATTRIBUTE_URL_TOKEN, ItemAttribute.TYPE.STRING)\n                .isMandatory(true);\n        createAttribute(PageItem.ATTRIBUTE_DISPLAY_NAME, ItemAttribute.TYPE.TEXT)\n                .isMandatory(true);\n        createAttribute(PageItem.ATTRIBUTE_CONTENT_NAME, ItemAttribute.TYPE.STRING)\n                .isMandatory(true);\n        createAttribute(PageItem.ATTRIBUTE_DESCRIPTION, ItemAttribute.TYPE.TEXT);\n        createAttribute(PageItem.ATTRIBUTE_CREATION_DATE, ItemAttribute.TYPE.DATE);\n        createAttribute(PageItem.ATTRIBUTE_CREATED_BY_USER_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(PageItem.ATTRIBUTE_IS_PROVIDED, ItemAttribute.TYPE.BOOLEAN);\n        createAttribute(PageItem.ATTRIBUTE_LAST_UPDATE_DATE, ItemAttribute.TYPE.DATETIME);\n        createAttribute(PageItem.ATTRIBUTE_UPDATED_BY_USER_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(PageItem.ATTRIBUTE_PROCESS_ID, ItemAttribute.TYPE.ITEM_ID);\n        createAttribute(PageItem.ATTRIBUTE_CONTENT_TYPE, ItemAttribute.TYPE.STRING);\n        createAttribute(PageItem.ATTRIBUTE_IS_EDITABLE, ItemAttribute.TYPE.BOOLEAN);\n        createAttribute(PageItem.ATTRIBUTE_IS_REMOVABLE, ItemAttribute.TYPE.BOOLEAN);\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(PageItem.ATTRIBUTE_ID);\n    }\n\n    @Override\n    protected PageItem _createItem() {\n        return new PageItem();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/portal/page/PageItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.portal.page;\n\nimport java.util.Date;\n\nimport org.bonitasoft.web.rest.model.identity.UserItem;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId;\n\n/**\n * @author Fabio Lombardi\n */\npublic class PageItem extends Item implements ItemHasUniqueId {\n\n    public static final String ATTRIBUTE_PROCESS_ID = \"processDefinitionId\";\n\n    public static final String ATTRIBUTE_URL_TOKEN = \"urlToken\";\n\n    public static final String ATTRIBUTE_DISPLAY_NAME = \"displayName\";\n\n    public static final String ATTRIBUTE_DESCRIPTION = \"description\";\n\n    public static final String ATTRIBUTE_IS_PROVIDED = \"isProvided\";\n\n    public static final String ATTRIBUTE_LAST_UPDATE_DATE = \"lastUpdateDate\";\n\n    public static final String ATTRIBUTE_CREATION_DATE = \"creationDate\";\n\n    public static final String ATTRIBUTE_CREATED_BY_USER_ID = \"createdBy\";\n\n    public static final String ATTRIBUTE_UPDATED_BY_USER_ID = \"updatedBy\";\n\n    public static final String ATTRIBUTE_CONTENT_NAME = \"contentName\";\n\n    public static final String ATTRIBUTE_CONTENT_TYPE = \"contentType\";\n\n    public static final String ATTRIBUTE_IS_EDITABLE = \"isEditable\";\n\n    public static final String ATTRIBUTE_IS_REMOVABLE = \"isRemovable\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // FILTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public static final String FILTER_CONTENT_TYPE = \"contentType\";\n\n    public PageItem() {\n    }\n\n    public PageItem(final IItem item) {\n        super(item);\n    }\n\n    @Override\n    public void setId(final String id) {\n        setAttribute(ATTRIBUTE_ID, id);\n    }\n\n    @Override\n    public void setId(final Long id) {\n        setAttribute(ATTRIBUTE_ID, id);\n    }\n\n    public void setProcessId(final String id) {\n        setAttribute(ATTRIBUTE_PROCESS_ID, id);\n    }\n\n    public void setProcessId(final Long id) {\n        setAttribute(ATTRIBUTE_PROCESS_ID, id);\n    }\n\n    public void setUrlToken(final String name) {\n        setAttribute(ATTRIBUTE_URL_TOKEN, name);\n    }\n\n    public void setDisplayName(final String name) {\n        setAttribute(ATTRIBUTE_DISPLAY_NAME, name);\n    }\n\n    public void setDescription(final String description) {\n        setAttribute(ATTRIBUTE_DESCRIPTION, description);\n    }\n\n    public void setCreationDate(final String date) {\n        setAttribute(ATTRIBUTE_CREATION_DATE, date);\n    }\n\n    public void setCreationDate(final Date date) {\n        setAttribute(ATTRIBUTE_CREATION_DATE, date);\n    }\n\n    public void setCreatedByUserId(final String id) {\n        setAttribute(ATTRIBUTE_CREATED_BY_USER_ID, id);\n    }\n\n    public void setCreatedByUserId(final Long id) {\n        setAttribute(ATTRIBUTE_CREATED_BY_USER_ID, id);\n    }\n\n    public void setCreatedByUserId(final APIID id) {\n        setAttribute(ATTRIBUTE_CREATED_BY_USER_ID, id);\n    }\n\n    public void setUpdatedByUserId(final String id) {\n        setAttribute(ATTRIBUTE_UPDATED_BY_USER_ID, id);\n    }\n\n    public void setUpdatedByUserId(final Long id) {\n        setAttribute(ATTRIBUTE_UPDATED_BY_USER_ID, id);\n    }\n\n    public void setUpdatedByUserId(final APIID id) {\n        setAttribute(ATTRIBUTE_UPDATED_BY_USER_ID, id);\n    }\n\n    public void setLastUpdateDate(final String date) {\n        setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date);\n    }\n\n    public void setLastUpdateDate(final Date date) {\n        setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date);\n    }\n\n    public void setIsProvided(final boolean isProvided) {\n        setAttribute(ATTRIBUTE_IS_PROVIDED, isProvided);\n    }\n\n    public void setContentName(final String name) {\n        setAttribute(ATTRIBUTE_CONTENT_NAME, name);\n    }\n\n    public void setContentType(final String contentType) {\n        setAttribute(ATTRIBUTE_CONTENT_TYPE, contentType);\n    }\n\n    public void setIsEditable(final boolean isEditable) {\n        setAttribute(ATTRIBUTE_IS_EDITABLE, isEditable);\n    }\n\n    public void setIsRemovable(final boolean isRemovable) {\n        setAttribute(ATTRIBUTE_IS_REMOVABLE, isRemovable);\n    }\n\n    public String getUrlToken() {\n        return getAttributeValue(ATTRIBUTE_URL_TOKEN);\n    }\n\n    public String getDisplayName() {\n        return getAttributeValue(ATTRIBUTE_DISPLAY_NAME);\n    }\n\n    public String getDescription() {\n        return getAttributeValue(ATTRIBUTE_DESCRIPTION);\n    }\n\n    public boolean isProvided() {\n        return Boolean.parseBoolean(getAttributeValue(ATTRIBUTE_IS_PROVIDED));\n    }\n\n    public String getContentName() {\n        return getAttributeValue(ATTRIBUTE_CONTENT_NAME);\n    }\n\n    public Date getLastUpdateDate() {\n        return getAttributeValueAsDate(ATTRIBUTE_LAST_UPDATE_DATE);\n    }\n\n    @Override\n    public ItemDefinition<PageItem> getItemDefinition() {\n        return PageDefinition.get();\n    }\n\n    public Date getCreationDate() {\n        return getAttributeValueAsDate(ATTRIBUTE_CREATION_DATE);\n    }\n\n    public APIID getCreatedByUserId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_CREATED_BY_USER_ID);\n    }\n\n    public UserItem getCreatedByUser() {\n        return (UserItem) getDeploy(ATTRIBUTE_CREATED_BY_USER_ID);\n    }\n\n    public APIID getUpdatedByUserId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_UPDATED_BY_USER_ID);\n    }\n\n    public APIID getProcessId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_PROCESS_ID);\n    }\n\n    public String getContentType() {\n        return getAttributeValue(ATTRIBUTE_CONTENT_TYPE);\n    }\n\n    public UserItem getUpdatedByUser() {\n        return (UserItem) getDeploy(ATTRIBUTE_UPDATED_BY_USER_ID);\n    }\n\n    public boolean isEditable() {\n        return Boolean.parseBoolean(getAttributeValue(ATTRIBUTE_IS_EDITABLE));\n    }\n\n    public boolean isRemovable() {\n        return Boolean.parseBoolean(getAttributeValue(ATTRIBUTE_IS_REMOVABLE));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/portal/profile/AbstractMemberDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.portal.profile;\n\nimport static org.bonitasoft.web.rest.model.portal.profile.AbstractMemberItem.ATTRIBUTE_GROUP_ID;\nimport static org.bonitasoft.web.rest.model.portal.profile.AbstractMemberItem.ATTRIBUTE_ROLE_ID;\nimport static org.bonitasoft.web.rest.model.portal.profile.AbstractMemberItem.ATTRIBUTE_USER_ID;\nimport static org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute.TYPE.ITEM_ID;\n\nimport org.bonitasoft.web.rest.model.identity.RoleDefinition;\nimport org.bonitasoft.web.rest.model.identity.UserDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Séverin Moussel\n */\npublic abstract class AbstractMemberDefinition<E extends IItem> extends ItemDefinition<E> {\n\n    @Override\n    protected void defineAttributes() {\n        createAttribute(ATTRIBUTE_GROUP_ID, ITEM_ID);\n        createAttribute(ATTRIBUTE_USER_ID, ITEM_ID);\n        createAttribute(ATTRIBUTE_ROLE_ID, ITEM_ID);\n    }\n\n    @Override\n    protected void defineDeploys() {\n        declareDeployable(ATTRIBUTE_USER_ID, UserDefinition.get());\n        declareDeployable(ATTRIBUTE_GROUP_ID, UserDefinition.get());\n        declareDeployable(ATTRIBUTE_ROLE_ID, RoleDefinition.get());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/portal/profile/AbstractMemberItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.portal.profile;\n\nimport org.bonitasoft.web.rest.model.identity.GroupItem;\nimport org.bonitasoft.web.rest.model.identity.RoleItem;\nimport org.bonitasoft.web.rest.model.identity.UserItem;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\n\n/**\n * @author Séverin Moussel\n */\npublic abstract class AbstractMemberItem extends Item {\n\n    public AbstractMemberItem() {\n        super();\n    }\n\n    public AbstractMemberItem(final IItem item) {\n        super(item);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // ATTRIBUTES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public static final String ATTRIBUTE_USER_ID = \"user_id\";\n\n    public static final String ATTRIBUTE_ROLE_ID = \"role_id\";\n\n    public static final String ATTRIBUTE_GROUP_ID = \"group_id\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // FILTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    public static final String FILTER_MEMBER_TYPE = \"member_type\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // FILTERS VALUES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public static final String VALUE_MEMBER_TYPE_USER = \"user\";\n\n    public static final String VALUE_MEMBER_TYPE_GROUP = \"group\";\n\n    public static final String VALUE_MEMBER_TYPE_ROLE = \"role\";\n\n    public static final String VALUE_MEMBER_TYPE_MEMBERSHIP = \"roleAndGroup\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // SETTERS / GETTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public APIID getUserId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_USER_ID);\n    }\n\n    public void setUserId(final String userId) {\n        setAttribute(ATTRIBUTE_USER_ID, userId);\n    }\n\n    public void setUserId(final APIID userId) {\n        setAttribute(ATTRIBUTE_USER_ID, userId);\n    }\n\n    public void setUserId(final Long userId) {\n        setAttribute(ATTRIBUTE_USER_ID, userId);\n    }\n\n    public APIID getRoleId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_ROLE_ID);\n    }\n\n    public void setRoleId(final String roleId) {\n        setAttribute(ATTRIBUTE_ROLE_ID, roleId);\n    }\n\n    public void setRoleId(final APIID roleId) {\n        setAttribute(ATTRIBUTE_ROLE_ID, roleId);\n    }\n\n    public void setRoleId(final Long roleId) {\n        setAttribute(ATTRIBUTE_ROLE_ID, roleId);\n    }\n\n    public APIID getGroupId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_GROUP_ID);\n    }\n\n    public void setGroupId(final String groupId) {\n        setAttribute(ATTRIBUTE_GROUP_ID, groupId);\n    }\n\n    public void setGroupId(final APIID groupId) {\n        setAttribute(ATTRIBUTE_GROUP_ID, groupId);\n    }\n\n    public void setGroupId(final Long groupId) {\n        setAttribute(ATTRIBUTE_GROUP_ID, groupId);\n    }\n\n    // Deploys\n\n    public UserItem getUser() {\n        return new UserItem(getDeploy(ATTRIBUTE_USER_ID));\n    }\n\n    public RoleItem getRole() {\n        return new RoleItem(getDeploy(ATTRIBUTE_ROLE_ID));\n    }\n\n    public GroupItem getGroup() {\n        return new GroupItem(getDeploy(ATTRIBUTE_GROUP_ID));\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // UTILS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public void setMembershipId(final APIID roleId, final APIID groupId) {\n        setRoleId(roleId);\n        setGroupId(groupId);\n    }\n\n    public boolean isGroup() {\n        return getGroupId() != null && getUserId() == null && getRoleId() == null;\n    }\n\n    public boolean isRole() {\n        return getGroupId() == null && getUserId() == null && getRoleId() != null;\n    }\n\n    public boolean isUser() {\n        return getGroupId() == null && getUserId() != null && getRoleId() == null;\n    }\n\n    public boolean isMembership() {\n        return getGroupId() != null && getUserId() == null && getRoleId() != null;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/portal/profile/ProfileDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.portal.profile;\n\nimport static org.bonitasoft.web.toolkit.client.data.item.template.ItemHasIcon.ATTRIBUTE_ICON;\nimport static org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId.ATTRIBUTE_ID;\n\nimport org.bonitasoft.web.rest.server.datastore.organization.Avatars;\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.FileIsImageOrServletPathValidator;\n\n/**\n * @author Julien Mege\n * @author Séverin Moussel\n */\npublic class ProfileDefinition extends ItemDefinition<ProfileItem> {\n\n    /**\n     * Singleton\n     */\n    public static ProfileDefinition get() {\n        return (ProfileDefinition) Definitions.get(TOKEN);\n    }\n\n    public static final String TOKEN = \"profile\";\n\n    /**\n     * the URL of profile resource\n     */\n    protected static final String API_URL = \"../API/portal/profile\";\n\n    @Override\n    public String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(ATTRIBUTE_ID);\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        createAttribute(ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID);\n\n        createAttribute(ProfileItem.ATTRIBUTE_NAME, ItemAttribute.TYPE.STRING)\n                .isMandatory(true);\n\n        createAttribute(ProfileItem.ATTRIBUTE_DESCRIPTION, ItemAttribute.TYPE.TEXT);\n\n        createAttribute(ATTRIBUTE_ICON, ItemAttribute.TYPE.STRING)\n                .addValidator(new FileIsImageOrServletPathValidator(Avatars.PATH));\n\n        createAttribute(ProfileItem.ATTRIBUTE_UPDATED_BY_USER_ID, ItemAttribute.TYPE.ITEM_ID);\n\n        createAttribute(ProfileItem.ATTRIBUTE_LAST_UPDATE_DATE, ItemAttribute.TYPE.DATETIME);\n\n        createAttribute(ProfileItem.ATTRIBUTE_CREATED_BY_USER_ID, ItemAttribute.TYPE.ITEM_ID);\n\n        createAttribute(ProfileItem.ATTRIBUTE_CREATION_DATE, ItemAttribute.TYPE.DATETIME);\n    }\n\n    @Override\n    public ProfileItem _createItem() {\n        return new ProfileItem();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/portal/profile/ProfileItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.portal.profile;\n\nimport java.util.Date;\n\nimport org.bonitasoft.web.rest.model.identity.UserItem;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasIcon;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId;\n\n/**\n * @author Julien Mege\n * @author Zhiheng Yang\n * @author Séverin Moussel\n * @autor Paul Amar\n */\npublic class ProfileItem extends Item implements ItemHasUniqueId, ItemHasIcon {\n\n    public static final String ATTRIBUTE_NAME = \"name\";\n\n    public static final String ATTRIBUTE_DESCRIPTION = \"description\";\n\n    public static final String FILTER_USER_ID = \"user_id\";\n\n    public static final String ATTRIBUTE_IS_DEFAULT = \"is_default\";\n\n    public static final String ATTRIBUTE_LAST_UPDATE_DATE = \"lastUpdateDate\";\n\n    public static final String ATTRIBUTE_CREATION_DATE = \"creationDate\";\n\n    public static final String ATTRIBUTE_CREATED_BY_USER_ID = \"createdBy\";\n\n    public static final String ATTRIBUTE_UPDATED_BY_USER_ID = \"updatedBy\";\n\n    public ProfileItem() {\n        super();\n    }\n\n    public ProfileItem(final IItem item) {\n        super(item);\n    }\n\n    @Override\n    public void setId(final String id) {\n        setAttribute(ATTRIBUTE_ID, id);\n    }\n\n    @Override\n    public void setId(final Long id) {\n        setAttribute(ATTRIBUTE_ID, id);\n    }\n\n    @Override\n    public void setIcon(final String icon) {\n        setAttribute(ATTRIBUTE_ICON, icon);\n    }\n\n    public void setName(final String name) {\n        setAttribute(ATTRIBUTE_NAME, name);\n    }\n\n    public void setDescription(final String description) {\n        setAttribute(ATTRIBUTE_DESCRIPTION, description);\n    }\n\n    public void setCreationDate(String date) {\n        setAttribute(ATTRIBUTE_CREATION_DATE, date);\n    }\n\n    public void setCreationDate(Date date) {\n        setAttribute(ATTRIBUTE_CREATION_DATE, date);\n    }\n\n    public void setCreatedByUserId(String id) {\n        setAttribute(ATTRIBUTE_CREATED_BY_USER_ID, id);\n    }\n\n    public void setCreatedByUserId(Long id) {\n        setAttribute(ATTRIBUTE_CREATED_BY_USER_ID, id);\n    }\n\n    public void setCreatedByUserId(APIID id) {\n        setAttribute(ATTRIBUTE_CREATED_BY_USER_ID, id);\n    }\n\n    public UserItem getCreatedByUser() {\n        return (UserItem) getDeploy(ATTRIBUTE_CREATED_BY_USER_ID);\n    }\n\n    public void setUpdatedByUserId(String id) {\n        setAttribute(ATTRIBUTE_UPDATED_BY_USER_ID, id);\n    }\n\n    public void setUpdatedByUserId(Long id) {\n        setAttribute(ATTRIBUTE_UPDATED_BY_USER_ID, id);\n    }\n\n    public void setUpdatedByUserId(APIID id) {\n        setAttribute(ATTRIBUTE_UPDATED_BY_USER_ID, id);\n    }\n\n    public void setLastUpdateDate(final String date) {\n        setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date);\n    }\n\n    public void setLastUpdateDate(final Date date) {\n        setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date);\n    }\n\n    public UserItem getUpdatedByUser() {\n        return (UserItem) getDeploy(ATTRIBUTE_UPDATED_BY_USER_ID);\n    }\n\n    public void setIsDefault(final Boolean isDefault) {\n        setAttribute(ATTRIBUTE_IS_DEFAULT, isDefault);\n    }\n\n    @Override\n    public String getIcon() {\n        return getAttributeValue(ATTRIBUTE_ICON);\n    }\n\n    public String getName() {\n        return getAttributeValue(ATTRIBUTE_NAME);\n    }\n\n    public String getDescription() {\n        return getAttributeValue(ATTRIBUTE_DESCRIPTION);\n    }\n\n    public String isDefault() {\n        return getAttributeValue(ATTRIBUTE_IS_DEFAULT);\n    }\n\n    @Override\n    public ItemDefinition<ProfileItem> getItemDefinition() {\n        return ProfileDefinition.get();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/portal/profile/ProfileMemberDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.portal.profile;\n\nimport static org.bonitasoft.web.rest.model.portal.profile.ProfileMemberItem.ATTRIBUTE_PROFILE_ID;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * @author Julien Mege\n * @author Séverin Moussel\n */\npublic class ProfileMemberDefinition extends AbstractMemberDefinition<ProfileMemberItem> {\n\n    /**\n     * Singleton\n     */\n    public static ProfileMemberDefinition get() {\n        return (ProfileMemberDefinition) Definitions.get(TOKEN);\n    }\n\n    public static final String TOKEN = \"memberProfile\";\n\n    /**\n     * the URL of UserProfileAssociation resource\n     */\n    protected static final String API_URL = \"../API/portal/profileMember\";\n\n    @Override\n    public String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        // FIXME : delete when id in engine is deleted\n        setPrimaryKeys(ProfileMemberItem.ATTRIBUTE_ID);\n\n        // FIXME : uncomment when id in engine is deleted\n        // setPrimaryKeys(\n        // ATTRIBUTE_PROFILE_ID,\n        // ATTRIBUTE_USER_ID,\n        // ATTRIBUTE_ROLE_ID,\n        // ATTRIBUTE_GROUP_ID);\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        createAttribute(ATTRIBUTE_PROFILE_ID, ItemAttribute.TYPE.ITEM_ID).isMandatory();\n        super.defineAttributes();\n    }\n\n    @Override\n    protected void defineDeploys() {\n        declareDeployable(ATTRIBUTE_PROFILE_ID, ProfileDefinition.get());\n        super.defineDeploys();\n    }\n\n    @Override\n    public ProfileMemberItem _createItem() {\n        return new ProfileMemberItem();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/portal/profile/ProfileMemberItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.portal.profile;\n\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId;\n\n/**\n * @author Julien Mege\n * @author Séverin Moussel\n */\npublic class ProfileMemberItem extends AbstractMemberItem implements ItemHasUniqueId {\n\n    public ProfileMemberItem() {\n        super();\n    }\n\n    public ProfileMemberItem(final IItem item) {\n        super(item);\n    }\n\n    public static final String ATTRIBUTE_PROFILE_ID = \"profile_id\";\n\n    // FIXME : delete when id in engine is deleted\n    @Override\n    public void setId(final String id) {\n        this.setAttribute(ATTRIBUTE_ID, id);\n    }\n\n    // FIXME : delete when id in engine is deleted\n    @Override\n    public void setId(final Long id) {\n        this.setAttribute(ATTRIBUTE_ID, id);\n    }\n\n    public APIID getProfileId() {\n        return getAttributeValueAsAPIID(ATTRIBUTE_PROFILE_ID);\n    }\n\n    public void setProfileId(final String id) {\n        setAttribute(ATTRIBUTE_PROFILE_ID, id);\n    }\n\n    public void setProfileId(final APIID id) {\n        setAttribute(ATTRIBUTE_PROFILE_ID, id);\n    }\n\n    public void setProfileId(final Long id) {\n        setAttribute(ATTRIBUTE_PROFILE_ID, id);\n    }\n\n    // Deploys\n\n    public ProfileItem getProfile() {\n        return new ProfileItem(getDeploy(ATTRIBUTE_PROFILE_ID));\n    }\n\n    @Override\n    public ItemDefinition<ProfileMemberItem> getItemDefinition() {\n        return ProfileMemberDefinition.get();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/system/MaintenanceDetailsClient.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.system;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.maintenance.MaintenanceDetails;\n\n/**\n * Client implementation object required for Json deserialization\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class MaintenanceDetailsClient implements MaintenanceDetails {\n\n    private State maintenanceState;\n    private String maintenanceMessage;\n    private Boolean maintenanceMessageActive;\n\n    @Override\n    public State getMaintenanceState() {\n        return maintenanceState;\n    }\n\n    @Override\n    public String getMaintenanceMessage() {\n        return maintenanceMessage;\n    }\n\n    @Override\n    public Boolean isMaintenanceMessageActive() {\n        return maintenanceMessageActive;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/tenant/BusinessDataModelDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.tenant;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\npublic class BusinessDataModelDefinition extends ItemDefinition<BusinessDataModelItem> {\n\n    public static final String TOKEN = \"bdm\";\n    private static final String API_URL = \"../API/tenant/\" + TOKEN;\n\n    public static BusinessDataModelDefinition get() {\n        return (BusinessDataModelDefinition) Definitions.get(TOKEN);\n    }\n\n    @Override\n    protected String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n\n    }\n\n    @Override\n    protected BusinessDataModelItem _createItem() {\n        return new BusinessDataModelItem();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/tenant/BusinessDataModelItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.tenant;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\npublic class BusinessDataModelItem extends Item {\n\n    public static final String ATTRIBUTE_FILE_UPLOAD_PATH = \"fileUpload\";\n\n    @Override\n    public ItemDefinition<BusinessDataModelItem> getItemDefinition() {\n        return new BusinessDataModelDefinition();\n    }\n\n    public String getFileUploadPath() {\n        return getAttributeValue(ATTRIBUTE_FILE_UPLOAD_PATH);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/user/User.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.user;\n\nimport java.io.Serializable;\n\n/**\n * @author Guo Yongtao\n */\npublic class User implements Serializable {\n\n    /**\n     * ID used for serialization.\n     */\n    private static final long serialVersionUID = 1940844173066923676L;\n\n    /**\n     * The username\n     */\n    protected String username;\n\n    /**\n     * Indicates the locale to use to display the user interface\n     */\n    protected String locale;\n\n    public User() {\n        super();\n        // Mandatory for serialization.\n    }\n\n    public User(final String username, final String locale) {\n        this.username = username;\n        this.locale = locale;\n    }\n\n    /**\n     * @return the userUUID\n     */\n    public String getUsername() {\n        return username;\n    }\n\n    public String getLocale() {\n        return locale;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/APIPaginationUtils.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.web.rest.server.datastore.filter.Filters;\nimport org.bonitasoft.web.rest.server.datastore.filter.FormMappingTypeCreator;\nimport org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator;\nimport org.bonitasoft.web.rest.server.datastore.utils.Sorts;\n\npublic class APIPaginationUtils {\n\n    /**\n     * Build search options from query parameters.\n     *\n     * @param page the page number (0-indexed)\n     * @param count the number of results per page\n     * @param search the search term\n     * @param order the sort order\n     * @param filters the filters\n     * @return the search options\n     */\n    public static SearchOptions buildSearchOptions(int page, int count, String search, String order,\n            List<String> filters) {\n        Filters parsedFilters = new Filters(QueryParameterUtils.parseFilters(filters), new FormMappingTypeCreator());\n        Sorts sorts = new Sorts(order);\n        return new SearchOptionsCreator(page, count, search, sorts, parsedFilters).create();\n    }\n\n    /**\n     * Build Content-Range header value.\n     *\n     * @param page the page number\n     * @param countOnCurrentPage the page size\n     * @param total the total count\n     * @return the Content-Range header value\n     */\n    public static String buildContentRange(int page, int countOnCurrentPage, long total) {\n        return page + \"-\" + countOnCurrentPage + \"/\" + total;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/BonitaRestAPIFactory.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server;\n\nimport org.bonitasoft.web.rest.server.api.application.APIApplication;\nimport org.bonitasoft.web.rest.server.api.applicationmenu.APIApplicationMenu;\nimport org.bonitasoft.web.rest.server.api.applicationpage.APIApplicationDataStoreFactory;\nimport org.bonitasoft.web.rest.server.api.applicationpage.APIApplicationPage;\nimport org.bonitasoft.web.rest.server.api.bpm.cases.APIArchivedCase;\nimport org.bonitasoft.web.rest.server.api.bpm.cases.APIArchivedCaseDocument;\nimport org.bonitasoft.web.rest.server.api.bpm.cases.APIArchivedComment;\nimport org.bonitasoft.web.rest.server.api.bpm.cases.APICase;\nimport org.bonitasoft.web.rest.server.api.bpm.cases.APICaseDocument;\nimport org.bonitasoft.web.rest.server.api.bpm.cases.APICaseVariable;\nimport org.bonitasoft.web.rest.server.api.bpm.cases.APIComment;\nimport org.bonitasoft.web.rest.server.api.bpm.connector.APIArchivedConnectorInstance;\nimport org.bonitasoft.web.rest.server.api.bpm.connector.APIConnectorInstance;\nimport org.bonitasoft.web.rest.server.api.bpm.flownode.APIActivity;\nimport org.bonitasoft.web.rest.server.api.bpm.flownode.APIFlowNode;\nimport org.bonitasoft.web.rest.server.api.bpm.flownode.APIHumanTask;\nimport org.bonitasoft.web.rest.server.api.bpm.flownode.APITask;\nimport org.bonitasoft.web.rest.server.api.bpm.flownode.APIUserTask;\nimport org.bonitasoft.web.rest.server.api.bpm.flownode.archive.APIArchivedActivity;\nimport org.bonitasoft.web.rest.server.api.bpm.flownode.archive.APIArchivedFlowNode;\nimport org.bonitasoft.web.rest.server.api.bpm.flownode.archive.APIArchivedHumanTask;\nimport org.bonitasoft.web.rest.server.api.bpm.flownode.archive.APIArchivedTask;\nimport org.bonitasoft.web.rest.server.api.bpm.flownode.archive.APIArchivedUserTask;\nimport org.bonitasoft.web.rest.server.api.bpm.process.APIActor;\nimport org.bonitasoft.web.rest.server.api.bpm.process.APIActorMember;\nimport org.bonitasoft.web.rest.server.api.bpm.process.APICategory;\nimport org.bonitasoft.web.rest.server.api.bpm.process.APIProcess;\nimport org.bonitasoft.web.rest.server.api.bpm.process.APIProcessCategory;\nimport org.bonitasoft.web.rest.server.api.bpm.process.APIProcessConnector;\nimport org.bonitasoft.web.rest.server.api.bpm.process.APIProcessConnectorDependency;\nimport org.bonitasoft.web.rest.server.api.bpm.process.APIProcessParameter;\nimport org.bonitasoft.web.rest.server.api.bpm.process.APIProcessResolutionProblem;\nimport org.bonitasoft.web.rest.server.api.document.APIArchivedDocument;\nimport org.bonitasoft.web.rest.server.api.document.APIDocument;\nimport org.bonitasoft.web.rest.server.api.organization.APICustomUserInfoDefinition;\nimport org.bonitasoft.web.rest.server.api.organization.APICustomUserInfoUser;\nimport org.bonitasoft.web.rest.server.api.organization.APICustomUserInfoValue;\nimport org.bonitasoft.web.rest.server.api.organization.APIGroup;\nimport org.bonitasoft.web.rest.server.api.organization.APIMembership;\nimport org.bonitasoft.web.rest.server.api.organization.APIPersonalContactData;\nimport org.bonitasoft.web.rest.server.api.organization.APIProfessionalContactData;\nimport org.bonitasoft.web.rest.server.api.organization.APIRole;\nimport org.bonitasoft.web.rest.server.api.organization.APIUser;\nimport org.bonitasoft.web.rest.server.api.page.APIPage;\nimport org.bonitasoft.web.rest.server.api.platform.APIPlatform;\nimport org.bonitasoft.web.rest.server.api.profile.APIProfile;\nimport org.bonitasoft.web.rest.server.api.profile.APIProfileMember;\nimport org.bonitasoft.web.rest.server.api.system.APII18nLocale;\nimport org.bonitasoft.web.rest.server.api.system.APISession;\nimport org.bonitasoft.web.rest.server.datastore.application.ApplicationDataStoreCreator;\nimport org.bonitasoft.web.rest.server.datastore.applicationmenu.ApplicationMenuDataStoreCreator;\nimport org.bonitasoft.web.rest.server.engineclient.CustomUserInfoEngineClientCreator;\nimport org.bonitasoft.web.rest.server.framework.API;\nimport org.bonitasoft.web.rest.server.framework.RestAPIFactory;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APINotFoundException;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\n\n/**\n * @author Séverin Moussel\n */\npublic class BonitaRestAPIFactory extends RestAPIFactory {\n\n    @Override\n    public API<? extends IItem> defineApis(final String apiToken, final String resourceToken) {\n\n        if (\"identity\".equals(apiToken)) {\n            if (\"user\".equals(resourceToken)) {\n                return new APIUser();\n            } else if (\"role\".equals(resourceToken)) {\n                return new APIRole();\n            } else if (\"group\".equals(resourceToken)) {\n                return new APIGroup();\n            } else if (\"membership\".equals(resourceToken)) {\n                return new APIMembership();\n            } else if (\"professionalcontactdata\".equals(resourceToken)) {\n                return new APIProfessionalContactData();\n            } else if (\"personalcontactdata\".equals(resourceToken)) {\n                return new APIPersonalContactData();\n            }\n        } else if (\"customuserinfo\".equals(apiToken)) {\n            if (\"definition\".equals(resourceToken)) {\n                return new APICustomUserInfoDefinition(new CustomUserInfoEngineClientCreator());\n            } else if (\"user\".equals(resourceToken)) {\n                return new APICustomUserInfoUser(new CustomUserInfoEngineClientCreator());\n            } else if (\"value\".equals(resourceToken)) {\n                return new APICustomUserInfoValue(new CustomUserInfoEngineClientCreator());\n            }\n        } else if (\"system\".equals(apiToken)) {\n            if (\"i18nlocale\".equals(resourceToken)) {\n                return new APII18nLocale();\n            } else if (\"session\".equals(resourceToken)) {\n                return new APISession();\n            }\n\n        } else if (\"portal\".equals(apiToken)) {\n            if (\"profile\".equals(resourceToken)) {\n                return new APIProfile();\n            } else if (\"profileMember\".equals(resourceToken)) {\n                return new APIProfileMember();\n            } else if (\"page\".equals(resourceToken)) {\n                return new APIPage();\n            }\n\n        } else if (\"bpm\".equals(apiToken)) {\n            if (\"humanTask\".equals(resourceToken)) {\n                return new APIHumanTask();\n            } else if (\"userTask\".equals(resourceToken)) {\n                return new APIUserTask();\n            } else if (\"archivedHumanTask\".equals(resourceToken)) {\n                return new APIArchivedHumanTask();\n            } else if (\"archivedUserTask\".equals(resourceToken)) {\n                return new APIArchivedUserTask();\n            } else if (\"process\".equals(resourceToken)) {\n                return new APIProcess();\n            } else if (\"category\".equals(resourceToken)) {\n                return new APICategory();\n            } else if (\"processCategory\".equals(resourceToken)) {\n                return new APIProcessCategory();\n            } else if (\"processConnector\".equals(resourceToken)) {\n                return new APIProcessConnector();\n            } else if (\"case\".equals(resourceToken)) {\n                return new APICase();\n            } else if (\"archivedCase\".equals(resourceToken)) {\n                return new APIArchivedCase();\n            } else if (\"comment\".equals(resourceToken)) {\n                return new APIComment();\n            } else if (\"archivedComment\".equals(resourceToken)) {\n                return new APIArchivedComment();\n            } else if (\"document\".equals(resourceToken)) {\n                return new APIDocument();\n            } else if (\"archiveddocument\".equals(resourceToken)) {\n                return new APIArchivedDocument();\n            } else if (\"actor\".equals(resourceToken)) {\n                return new APIActor();\n            } else if (\"actorMember\".equals(resourceToken)) {\n                return new APIActorMember();\n            } else if (\"delegation\".equals(resourceToken)) {\n                return new APIActorMember();\n            } else if (\"activity\".equals(resourceToken)) {\n                return new APIActivity();\n            } else if (\"archivedActivity\".equals(resourceToken)) {\n                return new APIArchivedActivity();\n            } else if (\"task\".equals(resourceToken)) {\n                return new APITask();\n            } else if (\"archivedTask\".equals(resourceToken)) {\n                return new APIArchivedTask();\n            } else if (\"flowNode\".equals(resourceToken)) {\n                return new APIFlowNode();\n            } else if (\"archivedFlowNode\".equals(resourceToken)) {\n                return new APIArchivedFlowNode();\n            } else if (\"processResolutionProblem\".equals(resourceToken)) {\n                return new APIProcessResolutionProblem();\n            } else if (\"caseDocument\".equals(resourceToken)) {\n                return new APICaseDocument();\n            } else if (\"archivedCaseDocument\".equals(resourceToken)) {\n                return new APIArchivedCaseDocument();\n            } else if (\"connectorInstance\".equals(resourceToken)) {\n                return new APIConnectorInstance();\n            } else if (\"archivedConnectorInstance\".equals(resourceToken)) {\n                return new APIArchivedConnectorInstance();\n            } else if (\"processConnectorDependency\".equals(resourceToken)) {\n                return new APIProcessConnectorDependency();\n            } else if (\"caseVariable\".equals(resourceToken)) {\n                return new APICaseVariable();\n            } else if (\"processParameter\".equals(resourceToken)) {\n                return new APIProcessParameter();\n            }\n        } else if (\"living\".equals(apiToken)) {\n            if (\"application\".equals(resourceToken)) {\n                return new APIApplication(new ApplicationDataStoreCreator(), new APIApplicationDataStoreFactory());\n            } else if (\"application-page\".equals(resourceToken)) {\n                return new APIApplicationPage(new APIApplicationDataStoreFactory());\n            } else if (\"application-menu\".equals(resourceToken)) {\n                return new APIApplicationMenu(new ApplicationMenuDataStoreCreator());\n            }\n\n        } else if (\"platform\".equals(apiToken)) {\n            if (\"platform\".equals(resourceToken)) {\n                return new APIPlatform();\n            }\n        }\n        throw new APINotFoundException(apiToken, resourceToken);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/BonitaRestAPIServlet.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server;\n\nimport java.io.UnsupportedEncodingException;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.bonitasoft.console.common.server.login.HttpServletRequestAccessor;\nimport org.bonitasoft.console.common.server.utils.SessionUtil;\nimport org.bonitasoft.engine.exception.NotFoundException;\nimport org.bonitasoft.engine.exception.TenantStatusException;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.bonitasoft.web.rest.model.ModelFactory;\nimport org.bonitasoft.web.rest.server.datastore.bpm.flownode.FlowNodeConverter;\nimport org.bonitasoft.web.rest.server.framework.RestAPIFactory;\nimport org.bonitasoft.web.rest.server.framework.servlet.APIServlet;\nimport org.bonitasoft.web.toolkit.client.ItemDefinitionFactory;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\n\n/**\n * @author Séverin Moussel\n */\npublic class BonitaRestAPIServlet extends APIServlet {\n\n    private static final long serialVersionUID = 525945083859596909L;\n\n    public BonitaRestAPIServlet() {\n        super();\n\n        FlowNodeConverter.setFlowNodeConverter(new FlowNodeConverter());\n    }\n\n    @Override\n    protected ItemDefinitionFactory defineApplicatioFactoryCommon() {\n        return new ModelFactory();\n    }\n\n    @Override\n    protected RestAPIFactory defineApplicatioFactoryServer() {\n        return new BonitaRestAPIFactory();\n    }\n\n    @Override\n    protected void catchAllExceptions(final Throwable exception, final HttpServletRequest req,\n            final HttpServletResponse resp) {\n        resp.setCharacterEncoding(\"UTF-8\");\n        try {\n            req.setCharacterEncoding(\"UTF-8\");\n        } catch (final UnsupportedEncodingException e) {\n            super.catchAllExceptions(e, req, resp);\n        }\n        if (exception instanceof InvalidSessionException\n                || exception instanceof APIException && exception.getCause() != null\n                        && exception.getCause() instanceof InvalidSessionException) {\n            final HttpServletRequestAccessor requestAccessor = new HttpServletRequestAccessor(req);\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(exception.getMessage(), exception);\n            }\n            outputException(exception, req, resp, HttpServletResponse.SC_UNAUTHORIZED);\n            SessionUtil.sessionLogout(requestAccessor.getHttpSession());\n        } else if (exception.getCause() instanceof NotFoundException) {\n            outputException(null, req, resp, HttpServletResponse.SC_NOT_FOUND);\n        } else if (exception instanceof TenantStatusException) {\n            if (LOGGER.isInfoEnabled()) {\n                LOGGER.info(\"Platform is probably under Maintenance : \" + exception.getMessage());\n            }\n            outputException(null, req, resp, HttpServletResponse.SC_SERVICE_UNAVAILABLE);\n        } else {\n            super.catchAllExceptions(exception, req, resp);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/QueryParameterUtils.java",
    "content": "/**\n * Copyright (C) 2025 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.lang.Nullable;\n\n/**\n * Utilities to handle query parameters.\n * Shared across Spring MVC controllers.\n */\npublic class QueryParameterUtils {\n\n    /**\n     * Builds a map where keys are Engine constants defining filter keys, and values are values corresponding to those\n     * keys.\n     *\n     * @param parameters the filters passed as string according to the form\n     *        {@code [\"key1=value1\", \"key2=value2\"]}\n     * @return a map of the form {@code {key1: value1, key2: value2}}, or {@code null} if\n     *         {@code parameters} is {@code null}\n     */\n    public static Map<String, String> parseFilters(final List<String> parameters) {\n        if (parameters == null) {\n            return null;\n        }\n        final Map<String, String> results = new HashMap<>();\n        for (final String parameter : parameters) {\n            final String[] split = parameter.split(\"=\");\n            if (split.length == 0) {\n                // \"=\".split(\"=\") returns an empty array — skip filters with no key\n                continue;\n            }\n            if (split.length == 1) {\n                // filter with no value (e.g. \"key1\" or \"key1=\"), put null as value in the map\n                results.put(split[0], null);\n            } else {\n                // valid filter, put key and value in the map (e.g. \"key1=value1\")\n                results.put(split[0], parameter.substring(split[0].length() + 1));\n            }\n        }\n        return results;\n    }\n\n    /**\n     * Extracts a string filter value from the filter list.\n     *\n     * @param filters the list of filter strings in format {@code [\"key1=value1\", \"key2=value2\"]}\n     * @param filterName the name of the filter to extract\n     * @return the filter value, or {@code null} if the filter is not found, {@code filters} is {@code null},\n     *         or the value is empty\n     */\n    public static @Nullable String extractStringFilter(List<String> filters, String filterName) {\n        if (filters == null) {\n            return null;\n        }\n        for (String filter : filters) {\n            String filterKey = filterName + \"=\";\n            if (filter.startsWith(filterKey)) {\n                String value = filter.substring(filterKey.length());\n                return value.isEmpty() ? null : value;\n            }\n        }\n        return null;\n    }\n\n    /**\n     * Extracts a mandatory string filter value from the filter list.\n     *\n     * @param filters the list of filter strings in format {@code [\"key1=value1\", \"key2=value2\"]}\n     * @param filterName the name of the filter to extract\n     * @return the filter value\n     * @throws IllegalArgumentException if the filter is not found or its value is empty\n     */\n    public static String extractMandatoryStringFilter(List<String> filters, String filterName) {\n        String value = extractStringFilter(filters, filterName);\n        if (value == null) {\n            throw new IllegalArgumentException(\"filter \" + filterName + \" is mandatory\");\n        }\n        return value;\n    }\n\n    /**\n     * Extracts an optional numeric {@link Long} filter value from the filter list.\n     *\n     * @param filters the list of filter strings in format {@code [\"key1=value1\", \"key2=value2\"]}\n     * @param filterName the name of the filter to extract\n     * @return the filter value as a {@link Long}, or {@code null} if the filter is not found or {@code filters}\n     *         is {@code null}\n     * @throws IllegalArgumentException if the filter value is not a valid number\n     */\n    public static @Nullable Long extractLongFilter(List<String> filters, String filterName) {\n        String value = extractStringFilter(filters, filterName);\n        try {\n            return value == null ? null : Long.parseLong(value);\n        } catch (NumberFormatException e) {\n            throw new IllegalArgumentException(\"filter \" + filterName + \" must be a number\");\n        }\n    }\n\n    /**\n     * Extracts a mandatory numeric {@code long} filter value from the filter list.\n     *\n     * @param filters the list of filter strings in format {@code [\"key1=value1\", \"key2=value2\"]}\n     * @param filterName the name of the filter to extract\n     * @return the filter value as a {@code long}\n     * @throws IllegalArgumentException if the filter is not found, its value is empty, or not a valid number\n     */\n    public static long extractMandatoryLongFilter(List<String> filters, String filterName) {\n        Long value = extractLongFilter(filters, filterName);\n        if (value == null) {\n            throw new IllegalArgumentException(\"filter \" + filterName + \" is mandatory\");\n        }\n        return value;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/SpringWebConfiguration.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.bonitasoft.web.rest.server.utils.BonitaJacksonModuleProvider;\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.format.support.FormattingConversionService;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;\nimport org.springframework.web.accept.ContentNegotiationManager;\nimport org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;\nimport org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;\nimport org.springframework.web.servlet.resource.ResourceUrlProvider;\n\n@Configuration\n// Note: Do NOT use @EnableWebMvc when extending WebMvcConfigurationSupport directly\n@ComponentScan({ \"org.bonitasoft.web.rest.server.api\", \"com.bonitasoft.web.rest.server.api\" })\npublic class SpringWebConfiguration extends WebMvcConfigurationSupport {\n\n    @Bean\n    @Override\n    public RequestMappingHandlerMapping requestMappingHandlerMapping(\n            @Qualifier(\"mvcContentNegotiationManager\") ContentNegotiationManager contentNegotiationManager,\n            @Qualifier(\"mvcConversionService\") FormattingConversionService conversionService,\n            @Qualifier(\"mvcResourceUrlProvider\") ResourceUrlProvider resourceUrlProvider) {\n        RequestMappingHandlerMapping handlerMapping = super.requestMappingHandlerMapping(contentNegotiationManager,\n                conversionService, resourceUrlProvider);\n        handlerMapping.setAlwaysUseFullPath(true);\n        return handlerMapping;\n    }\n\n    /**\n     * Creates the list of HTTP message converters configured for the Bonita REST API.\n     * <p>\n     * Uses Spring's default converter ordering (where StringHttpMessageConverter comes\n     * before MappingJackson2HttpMessageConverter), then replaces the default Jackson\n     * converter with one configured with Bonita custom serializers (_string suffix fields\n     * for numeric IDs, custom date/time formats).\n     * <p>\n     * This ordering is important: controllers returning pre-serialized JSON strings\n     * (e.g. BusinessDataController, ProcessDefinitionDesignController) must be handled\n     * by StringHttpMessageConverter to avoid double-serialization by Jackson.\n     *\n     * @return the configured message converters\n     */\n    public static List<HttpMessageConverter<?>> createBonitaMessageConverters() {\n        var converters = new ArrayList<HttpMessageConverter<?>>();\n        // Use a temporary instance to access the protected addDefaultHttpMessageConverters\n        new SpringWebConfiguration().addDefaultHttpMessageConverters(converters);\n\n        // Replace the default Jackson converter with one configured with Bonita custom serializers\n        converters.removeIf(c -> c instanceof MappingJackson2HttpMessageConverter);\n        ObjectMapper objectMapper = new ObjectMapper();\n        BonitaJacksonModuleProvider.configureObjectMapper(objectMapper);\n        converters.add(new MappingJackson2HttpMessageConverter(objectMapper));\n        return converters;\n    }\n\n    @Override\n    protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {\n        converters.addAll(createBonitaMessageConverters());\n    }\n\n    @Override\n    protected void configureContentNegotiation(ContentNegotiationConfigurer configurer) {\n        // enforces JSON as the default content type for content negotiation for Spring MVC APIs\n        // /!\\ Make sure that RestControllerUtils uses the same configuration /!\\\n        configurer.defaultContentType(MediaType.APPLICATION_JSON);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/APIPreconditions.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.i18n.T_;\n\n/**\n * @author Vincent Elcrin\n */\npublic class APIPreconditions {\n\n    public static void check(boolean condition, T_ message) {\n        if (!condition) {\n            throw new APIException(message);\n        }\n    }\n\n    public static boolean containsOnly(String key, Map<String, String> map) {\n        if (map == null) {\n            return false;\n        }\n        HashMap<String, String> clone = new HashMap<>(map);\n        return clone.remove(key) != null && clone.isEmpty();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/AbstractRESTController.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api;\n\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.console.common.server.utils.SessionUtil;\nimport org.bonitasoft.engine.api.BusinessDataAPI;\nimport org.bonitasoft.engine.api.CommandAPI;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.api.TenantAdministrationAPI;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.web.server.ResponseStatusException;\n\n/**\n * Parent class providing common methods for Bonita REST Controllers\n */\npublic abstract class AbstractRESTController {\n\n    public static final String API_SPRING_INTERNAL = \"APISpringInternal\";\n\n    public APISession getApiSession(HttpSession session) {\n        APISession apiSession = (APISession) session.getAttribute(SessionUtil.API_SESSION_PARAM_KEY);\n        if (apiSession == null) {\n            throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, \"Not authenticated\");\n        }\n        return apiSession;\n    }\n\n    // VisibleForTesting\n    public CommandAPI getCommandAPI(APISession apiSession)\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return TenantAPIAccessor.getCommandAPI(apiSession);\n    }\n\n    protected CommandAPI getCommandAPI(HttpSession session)\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return getCommandAPI(getApiSession(session));\n    }\n\n    // VisibleForTesting\n    public ProcessAPI getProcessAPI(APISession apiSession)\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return TenantAPIAccessor.getProcessAPI(apiSession);\n    }\n\n    protected ProcessAPI getProcessAPI(HttpSession session)\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return getProcessAPI(getApiSession(session));\n    }\n\n    public TenantAdministrationAPI getTenantAdministrationAPI(HttpSession session)\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return TenantAPIAccessor.getTenantAdministrationAPI(getApiSession(session));\n    }\n\n    // VisibleForTesting\n    public BusinessDataAPI getBusinessDataAPI(APISession apiSession)\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return TenantAPIAccessor.getBusinessDataAPI(apiSession);\n    }\n\n    protected BusinessDataAPI getBusinessDataAPI(HttpSession session)\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return getBusinessDataAPI(getApiSession(session));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/ConsoleAPI.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api;\n\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.server.framework.API;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\n\n/**\n * @author Séverin Moussel\n */\npublic abstract class ConsoleAPI<T extends IItem> extends API<T> {\n\n    private APISession sessionSingleton = null;\n\n    /**\n     * Get the session to access the engine SDK\n     */\n    protected APISession getEngineSession() {\n        if (this.sessionSingleton == null) {\n            this.sessionSingleton = (APISession) getHttpSession().getAttribute(\"apiSession\");\n        }\n        return this.sessionSingleton;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/FilterParameterBindingAdvice.java",
    "content": "/**\n * Copyright (C) 2025 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api;\n\nimport static org.bonitasoft.web.rest.server.framework.APIServletCall.PARAMETER_FILTER;\n\nimport java.util.List;\n\nimport org.springframework.beans.propertyeditors.CustomCollectionEditor;\nimport org.springframework.web.bind.WebDataBinder;\nimport org.springframework.web.bind.annotation.ControllerAdvice;\nimport org.springframework.web.bind.annotation.InitBinder;\n\n/**\n * Global binding configuration for REST controllers.\n * <p>\n * Registers a {@link CustomCollectionEditor} for the {@code \"f\"} request parameter only, so that\n * comma-separated filter values are preserved as a single string instead of being split into a\n * list by Spring's default binding.\n * <p>\n * For example, with {@code ?f=names=Harry,Anna}:\n * <ul>\n * <li><b>\"f\" parameter</b>: bound as the single value {@code \"names=Harry,Anna\"} (desired behavior)</li>\n * </ul>\n * This editor is intentionally <b>not</b> applied to other list parameters (e.g. {@code ids}), where\n * the default comma-splitting is expected:\n * <ul>\n * <li><b>\"ids\" parameter</b>: {@code ?ids=1,2,3} is bound as three separate values\n * {@code [\"1\", \"2\", \"3\"]}</li>\n * </ul>\n */\n@ControllerAdvice\npublic class FilterParameterBindingAdvice {\n\n    @InitBinder\n    protected void initBinder(WebDataBinder binder) {\n        if (PARAMETER_FILTER.equals(binder.getObjectName())) {\n            binder.registerCustomEditor(List.class, new CustomCollectionEditor(List.class));\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/PlatformAPI.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api;\n\nimport org.bonitasoft.engine.session.PlatformSession;\nimport org.bonitasoft.web.rest.server.framework.API;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\n\n/**\n * @author Julien Mege\n */\npublic abstract class PlatformAPI<T extends IItem> extends API<T> {\n\n    private PlatformSession sessionSingleton = null;\n\n    /**\n     * Get the session\n     */\n    protected final PlatformSession getPlatformSession() {\n        if (this.sessionSingleton == null) {\n            this.sessionSingleton = (PlatformSession) getHttpSession().getAttribute(\"platformSession\");\n        }\n        return this.sessionSingleton;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/SpringResponseEntityUtils.java",
    "content": "/**\n * Copyright (C) 2025 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.ResponseEntity;\n\n/**\n * Utility class to generate standardized error responses for Spring REST APIs.\n * It encapsulates the logic to create a ResponseEntity with an error message and status.\n */\npublic class SpringResponseEntityUtils {\n\n    public static ResponseEntity<Object> generateErrorResponse(String exceptionClassName, HttpStatus status,\n            String message) {\n        return ResponseEntity.status(status).body(new ResponseError(\"class \" + exceptionClassName, message));\n    }\n\n    public static ResponseEntity<Object> generateErrorResponse(Exception exception, HttpStatus status) {\n        return generateErrorResponse(exception.getClass().getName(), status, exception.getMessage());\n    }\n\n    public record ResponseError(String exception, String message) {}\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/SpringRestResponseEntityExceptionHandler.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api;\n\nimport static org.bonitasoft.engine.commons.ExceptionUtils.printLightWeightStacktrace;\nimport static org.bonitasoft.web.rest.server.api.SpringResponseEntityUtils.generateErrorResponse;\n\nimport java.lang.reflect.UndeclaredThrowableException;\nimport java.util.Map;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpSession;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.console.common.server.utils.SessionUtil;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceNotFoundException;\nimport org.bonitasoft.engine.business.data.BusinessDataCrudOperationException;\nimport org.bonitasoft.engine.business.data.InvalidBusinessDataModelException;\nimport org.bonitasoft.engine.command.CommandExecutionException;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.NotFoundException;\nimport org.bonitasoft.engine.exception.TenantStatusException;\nimport org.bonitasoft.engine.exception.UnavailableLockException;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.http.converter.HttpMessageNotReadableException;\nimport org.springframework.util.ClassUtils;\nimport org.springframework.web.bind.MissingServletRequestParameterException;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\nimport org.springframework.web.bind.annotation.RestControllerAdvice;\nimport org.springframework.web.context.request.WebRequest;\nimport org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;\nimport org.springframework.web.multipart.MaxUploadSizeExceededException;\nimport org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;\n\n@Slf4j\n@RestControllerAdvice\npublic class SpringRestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {\n\n    @ExceptionHandler(value = { TenantStatusException.class })\n    protected ResponseEntity<Object> handleMaintenanceMode(RuntimeException ex, WebRequest request) {\n        return handleExceptionInternal(ex, \"Platform under maintenance\",\n                new HttpHeaders(), HttpStatus.SERVICE_UNAVAILABLE, request);\n    }\n\n    @ExceptionHandler(value = { InvalidSessionException.class })\n    protected ResponseEntity<Object> handleInvalidSession(RuntimeException ex, WebRequest request,\n            HttpSession httpSession) {\n        SessionUtil.sessionLogout(httpSession);\n        return handleExceptionInternal(ex, \"Invalid session\",\n                new HttpHeaders(), HttpStatus.UNAUTHORIZED, request);\n    }\n\n    @ExceptionHandler(value = { Exception.class })\n    protected ResponseEntity<Object> defaultToInternalServerError(Exception exception) {\n        // If no specific exception handler is found, log the error and return an internal server error:\n        log.error(\"Generic server-side error:\\n{}\", printLightWeightStacktrace(exception));\n        log.debug(exception.getMessage(), exception);\n        return bonitaHandleException(exception, HttpStatus.INTERNAL_SERVER_ERROR);\n    }\n\n    @ExceptionHandler(MaxUploadSizeExceededException.class)\n    public ResponseEntity<Object> handleMaxSizeException(MaxUploadSizeExceededException ex) {\n        return generateErrorResponse(ex.getClass().getName(), HttpStatus.BAD_REQUEST, getRootCause(ex).getMessage());\n    }\n\n    @ExceptionHandler(IllegalArgumentException.class)\n    public ResponseEntity<Object> handleIllegalArgumentException(IllegalArgumentException e) {\n        return generateErrorResponse(e.getClass().getName(), HttpStatus.BAD_REQUEST, e.getMessage());\n    }\n\n    private static final Map<String, String> parameterErrorNames = Map.of(\"c\", \"count\", \"p\", \"page\");\n\n    @ExceptionHandler(value = { MethodArgumentTypeMismatchException.class })\n    public ResponseEntity<Object> handleInvalidParameters(HttpServletRequest req,\n            MethodArgumentTypeMismatchException ex) {\n        String parameterName = ex.getName();\n        if (log.isDebugEnabled()) {\n            log.debug(\"Invalid parameter [{}] {}: {}\", req.getPathInfo(), parameterName, ex.getMessage());\n        }\n\n        String mapping = parameterErrorNames.get(parameterName);\n        if (mapping != null) {\n            return bonitaHandleException(\n                    new IllegalArgumentException(\n                            \"query parameter \" + parameterName + \" (\" + mapping + \") should be a number\"),\n                    HttpStatus.BAD_REQUEST);\n        }\n\n        Object value = ex.getValue();\n        Class<?> requiredType = ex.getRequiredType();\n        // Check if the required type is a number (handle primitive types and primitive objects, e.g. int and Integer)\n        boolean isNumber = requiredType != null\n                && Number.class.isAssignableFrom(ClassUtils.resolvePrimitiveIfNecessary(requiredType));\n        if (isNumber) {\n            return bonitaHandleException(new IllegalArgumentException(\"[ \" + value + \" ] must be a number\"),\n                    HttpStatus.BAD_REQUEST);\n        }\n\n        return bonitaHandleException(new IllegalArgumentException(\"Bad parameter \" + parameterName + \"=\" + value),\n                HttpStatus.BAD_REQUEST);\n    }\n\n    @ExceptionHandler(value = { ActivityInstanceNotFoundException.class })\n    public ResponseEntity<Object> handleActivityInstanceNotFound(ActivityInstanceNotFoundException exception) {\n        // The message contains the ID:\n        return generateErrorResponse(exception.getClass().getName(), HttpStatus.NOT_FOUND, exception.getMessage());\n    }\n\n    @ExceptionHandler(value = { NotFoundException.class })\n    public ResponseEntity<Object> handleNotFound(NotFoundException exception) {\n        return bonitaHandleException(exception, HttpStatus.NOT_FOUND);\n    }\n\n    @ExceptionHandler(value = { AlreadyExistsException.class })\n    public ResponseEntity<Object> handleAlreadyExists(AlreadyExistsException exception) {\n        return bonitaHandleException(exception, HttpStatus.CONFLICT);\n    }\n\n    @ExceptionHandler(value = { CommandExecutionException.class })\n    public ResponseEntity<Object> handleExecutionException(CommandExecutionException exception) {\n        Throwable wrapped = exception.getCause();\n\n        final Throwable causedByIsDataNotFoundException = getFirstCauseOfType(wrapped, NotFoundException.class);\n        if (causedByIsDataNotFoundException != null) {\n            return bonitaHandleException(causedByIsDataNotFoundException, HttpStatus.NOT_FOUND);\n        }\n\n        final Throwable causedByBusinessDataCrudOperationException = getFirstCauseOfType(wrapped,\n                BusinessDataCrudOperationException.class);\n        if (causedByBusinessDataCrudOperationException != null) {\n            return generateErrorResponse(causedByBusinessDataCrudOperationException.getClass().getName(),\n                    HttpStatus.BAD_REQUEST,\n                    causedByBusinessDataCrudOperationException.getMessage());\n        }\n\n        return bonitaHandleException(exception, HttpStatus.INTERNAL_SERVER_ERROR);\n    }\n\n    @ExceptionHandler(value = { InvalidBusinessDataModelException.class })\n    public ResponseEntity<Object> handleInvalidBDM(InvalidBusinessDataModelException exception) {\n        return bonitaHandleException(exception, HttpStatus.BAD_REQUEST);\n    }\n\n    @ExceptionHandler(value = { UndeclaredThrowableException.class })\n    protected ResponseEntity<Object> handleUnavailableLockException(UndeclaredThrowableException exception) {\n        final UnavailableLockException unavailableLockException = (UnavailableLockException) getFirstCauseOfType(\n                exception, UnavailableLockException.class);\n        if (unavailableLockException != null) {\n            // UnavailableLockExceptions are not declared in the API, as they are thrown by the engine interceptor (ServerAPIImpl), so it is automatically wrapped in an UndeclaredThrowableException by the HttpClient proxy:\n            return generateErrorResponse(unavailableLockException.getClass().getName(), HttpStatus.NOT_ACCEPTABLE,\n                    unavailableLockException.getMessage());\n        }\n        return bonitaHandleException(exception, HttpStatus.INTERNAL_SERVER_ERROR);\n    }\n\n    private <T extends Throwable> Throwable getFirstCauseOfType(Throwable exception, Class<T> exceptionTypeToSearch) {\n        if (exception == null) {\n            return null;\n        }\n        if (exceptionTypeToSearch.isAssignableFrom(exception.getClass())) {\n            return exception;\n        }\n        return getFirstCauseOfType(exception.getCause(), exceptionTypeToSearch);\n    }\n\n    // get the root cause of the given exception:\n    private <T extends Throwable> Throwable getRootCause(Throwable exception) {\n        if (exception == null) {\n            return null;\n        }\n        Throwable cause = exception.getCause();\n        if (cause == null || cause == exception) {\n            return exception;\n        }\n        return getRootCause(cause);\n    }\n\n    @Override\n    protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException exception,\n            HttpHeaders headers, HttpStatus status, WebRequest request) {\n        return generateErrorResponse(exception.getClass().getName(), HttpStatus.BAD_REQUEST,\n                \"Unable to parse the JSON body\");\n    }\n\n    @Override\n    protected ResponseEntity<Object> handleMissingServletRequestParameter(\n            MissingServletRequestParameterException exception, HttpHeaders headers, HttpStatus status,\n            WebRequest request) {\n        String parameterName = exception.getParameterName();\n\n        String mapping = parameterErrorNames.get(parameterName);\n        String message;\n        if (mapping != null) {\n            message = \"query parameter \" + parameterName + \" (\" + mapping + \") is mandatory\";\n        } else {\n            message = \"query parameter \" + parameterName + \" is mandatory\";\n        }\n\n        return bonitaHandleException(new IllegalArgumentException(message), HttpStatus.BAD_REQUEST);\n    }\n\n    private static ResponseEntity<Object> bonitaHandleException(Throwable exception, HttpStatus status) {\n        final Throwable cause = exception.getCause() != null ? exception.getCause() : exception;\n        return generateErrorResponse(exception.getClass().getName(), status, cause.getMessage());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/application/APIApplication.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.application;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.web.rest.model.application.AbstractApplicationDefinition;\nimport org.bonitasoft.web.rest.model.application.AbstractApplicationItem;\nimport org.bonitasoft.web.rest.model.application.ApplicationItem;\nimport org.bonitasoft.web.rest.server.api.ConsoleAPI;\nimport org.bonitasoft.web.rest.server.api.applicationpage.APIApplicationDataStoreFactory;\nimport org.bonitasoft.web.rest.server.api.deployer.DeployerFactory;\nimport org.bonitasoft.web.rest.server.api.deployer.PageDeployer;\nimport org.bonitasoft.web.rest.server.api.deployer.UserDeployer;\nimport org.bonitasoft.web.rest.server.datastore.application.ApplicationDataStoreCreator;\nimport org.bonitasoft.web.rest.server.datastore.organization.UserDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasAdd;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasDelete;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasGet;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasSearch;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasUpdate;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * Defines the REST API for applications.\n * <p>Since 10.2.0 some types have changed in this interface.\n * Yet, these are not breaking changes as the json conversion does not change when using only legacy applications.\n * This class is not intended to be used as a Java API.</p>\n *\n * @author Julien Mege\n */\npublic class APIApplication extends ConsoleAPI<AbstractApplicationItem>\n        implements APIHasAdd<AbstractApplicationItem>, APIHasSearch<AbstractApplicationItem>,\n        APIHasGet<AbstractApplicationItem>, APIHasUpdate<AbstractApplicationItem>, APIHasDelete {\n\n    private final ApplicationDataStoreCreator creator;\n\n    private final APIApplicationDataStoreFactory applicationDataStoreFactory;\n\n    public APIApplication(final ApplicationDataStoreCreator creator,\n            final APIApplicationDataStoreFactory applicationDataStoreFactory) {\n        this.creator = creator;\n        this.applicationDataStoreFactory = applicationDataStoreFactory;\n    }\n\n    /**\n     * @deprecated as of 9.0.0, Applications should be created at startup.\n     */\n    @Override\n    @Deprecated(since = \"9.0.0\")\n    public AbstractApplicationItem add(final AbstractApplicationItem item) {\n        return creator.create(getEngineSession()).add(item);\n    }\n\n    /**\n     * @deprecated as of 9.0.0, Applications should be updated at startup.\n     */\n    @Override\n    @Deprecated(since = \"9.0.0\")\n    public AbstractApplicationItem update(final APIID id, final Map<String, String> attributes) {\n        return creator.create(getEngineSession()).update(id, attributes);\n    }\n\n    @Override\n    public AbstractApplicationItem get(final APIID id) {\n        return creator.create(getEngineSession()).get(id);\n    }\n\n    @Override\n    public void delete(final List<APIID> ids) {\n        creator.create(getEngineSession()).delete(ids);\n    }\n\n    @Override\n    public ItemSearchResult<AbstractApplicationItem> search(final int page, final int resultsByPage,\n            final String search,\n            final String orders,\n            final Map<String, String> filters) {\n        return creator.create(getEngineSession()).search(page, resultsByPage, search, orders, filters);\n    }\n\n    @Override\n    public String defineDefaultSearchOrder() {\n        return AbstractApplicationItem.ATTRIBUTE_DISPLAY_NAME;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    protected ItemDefinition<AbstractApplicationItem> defineItemDefinition() {\n        return (ItemDefinition<AbstractApplicationItem>) AbstractApplicationDefinition.get();\n    }\n\n    @Override\n    protected void fillDeploys(final AbstractApplicationItem item, final List<String> deploys) {\n        addDeployer(new UserDeployer(\n                new UserDatastore(getEngineSession()), AbstractApplicationItem.ATTRIBUTE_CREATED_BY));\n        addDeployer(new UserDeployer(\n                new UserDatastore(getEngineSession()), AbstractApplicationItem.ATTRIBUTE_UPDATED_BY));\n        addDeployer(getDeployerFactory().createProfileDeployer(AbstractApplicationItem.ATTRIBUTE_PROFILE_ID));\n        if (item instanceof ApplicationItem) {\n            addDeployer(new PageDeployer(\n                    applicationDataStoreFactory.createPageDataStore(getEngineSession()),\n                    ApplicationItem.ATTRIBUTE_LAYOUT_ID));\n            addDeployer(new PageDeployer(\n                    applicationDataStoreFactory.createPageDataStore(getEngineSession()),\n                    ApplicationItem.ATTRIBUTE_THEME_ID));\n        }\n\n        super.fillDeploys(item, deploys);\n    }\n\n    protected DeployerFactory getDeployerFactory() {\n        return new DeployerFactory(getEngineSession());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/applicationmenu/APIApplicationMenu.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.applicationmenu;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.web.rest.model.applicationmenu.ApplicationMenuDefinition;\nimport org.bonitasoft.web.rest.model.applicationmenu.ApplicationMenuItem;\nimport org.bonitasoft.web.rest.server.api.ConsoleAPI;\nimport org.bonitasoft.web.rest.server.api.deployer.ApplicationPageDeployer;\nimport org.bonitasoft.web.rest.server.datastore.applicationmenu.ApplicationMenuDataStoreCreator;\nimport org.bonitasoft.web.rest.server.datastore.applicationpage.ApplicationPageDataStoreCreator;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasAdd;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasDelete;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasGet;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasSearch;\nimport org.bonitasoft.web.rest.server.framework.api.Datastore;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Julien Mege\n */\npublic class APIApplicationMenu extends ConsoleAPI<ApplicationMenuItem>\n        implements APIHasAdd<ApplicationMenuItem>, APIHasSearch<ApplicationMenuItem>,\n        APIHasGet<ApplicationMenuItem>, APIHasDelete {\n\n    private final ApplicationMenuDataStoreCreator creator;\n\n    public APIApplicationMenu(final ApplicationMenuDataStoreCreator creator) {\n        this.creator = creator;\n    }\n\n    /**\n     * @deprecated as of 9.0.0, Application menu should be created at startup.\n     */\n    @Override\n    @Deprecated(since = \"9.0.0\")\n    public ApplicationMenuItem add(final ApplicationMenuItem item) {\n        return creator.create(getEngineSession()).add(item);\n    }\n\n    @Override\n    public ApplicationMenuItem get(final APIID id) {\n        return creator.create(getEngineSession()).get(id);\n    }\n\n    /**\n     * @deprecated as of 9.0.0, Application menu should be updated at startup.\n     */\n    @Override\n    @Deprecated(since = \"9.0.0\")\n    public ApplicationMenuItem update(final APIID id, final Map<String, String> attributes) {\n        return creator.create(getEngineSession()).update(id, attributes);\n    }\n\n    @Override\n    public void delete(final List<APIID> ids) {\n        creator.create(getEngineSession()).delete(ids);\n    }\n\n    @Override\n    protected ItemDefinition<ApplicationMenuItem> defineItemDefinition() {\n        return ApplicationMenuDefinition.get();\n    }\n\n    @Override\n    protected Datastore defineDefaultDatastore() {\n        return creator.create(getEngineSession());\n    }\n\n    @Override\n    public String defineDefaultSearchOrder() {\n        return ApplicationMenuItem.ATTRIBUTE_DISPLAY_NAME;\n    }\n\n    @Override\n    protected void fillDeploys(final ApplicationMenuItem item, final List<String> deploys) {\n        addDeployer(new ApplicationPageDeployer(\n                new ApplicationPageDataStoreCreator().create(getEngineSession()),\n                ApplicationMenuItem.ATTRIBUTE_APPLICATION_PAGE_ID));\n\n        super.fillDeploys(item, deploys);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/applicationpage/APIApplicationDataStoreFactory.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.applicationpage;\n\nimport org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.server.datastore.application.ApplicationDataStore;\nimport org.bonitasoft.web.rest.server.datastore.application.ApplicationDataStoreCreator;\nimport org.bonitasoft.web.rest.server.datastore.applicationpage.ApplicationPageDataStore;\nimport org.bonitasoft.web.rest.server.datastore.applicationpage.ApplicationPageDataStoreCreator;\nimport org.bonitasoft.web.rest.server.datastore.page.PageDatastore;\nimport org.bonitasoft.web.rest.server.datastore.page.PageDatastoreFactory;\nimport org.bonitasoft.web.rest.server.engineclient.EngineAPIAccessor;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\n\npublic class APIApplicationDataStoreFactory {\n\n    public ApplicationPageDataStore createApplicationPageDataStore(final APISession session) {\n        return new ApplicationPageDataStoreCreator().create(session);\n    }\n\n    public PageDatastore createPageDataStore(final APISession session) {\n        try {\n            final PageDatastoreFactory pageDatastoreFactory = new PageDatastoreFactory();\n            return pageDatastoreFactory.create(session,\n                    WebBonitaConstantsUtils.getTenantInstance(),\n                    new EngineAPIAccessor(session).getPageAPI());\n        } catch (final Exception e) {\n            throw new APIException(e);\n        }\n    }\n\n    public ApplicationDataStore createApplicationDataStore(final APISession session) {\n        return new ApplicationDataStoreCreator().create(session);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/applicationpage/APIApplicationPage.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.applicationpage;\n\nimport java.util.List;\n\nimport org.bonitasoft.web.rest.model.applicationpage.ApplicationPageDefinition;\nimport org.bonitasoft.web.rest.model.applicationpage.ApplicationPageItem;\nimport org.bonitasoft.web.rest.server.api.ConsoleAPI;\nimport org.bonitasoft.web.rest.server.api.deployer.ApplicationDeployer;\nimport org.bonitasoft.web.rest.server.api.deployer.PageDeployer;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasAdd;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasDelete;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasGet;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasSearch;\nimport org.bonitasoft.web.rest.server.framework.api.Datastore;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Julien Mege\n */\npublic class APIApplicationPage extends ConsoleAPI<ApplicationPageItem> implements APIHasAdd<ApplicationPageItem>,\n        APIHasSearch<ApplicationPageItem>, APIHasGet<ApplicationPageItem>, APIHasDelete {\n\n    private final APIApplicationDataStoreFactory factory;\n\n    public APIApplicationPage(final APIApplicationDataStoreFactory factory) {\n        this.factory = factory;\n    }\n\n    /**\n     * @deprecated as of 9.0.0, Application page should be created at startup.\n     */\n    @Override\n    @Deprecated(since = \"9.0.0\")\n    public ApplicationPageItem add(final ApplicationPageItem item) {\n        return factory.createApplicationPageDataStore(getEngineSession()).add(item);\n    }\n\n    @Override\n    protected ItemDefinition<ApplicationPageItem> defineItemDefinition() {\n        return ApplicationPageDefinition.get();\n    }\n\n    @Override\n    protected Datastore defineDefaultDatastore() {\n        return factory.createApplicationPageDataStore(getEngineSession());\n    }\n\n    @Override\n    public String defineDefaultSearchOrder() {\n        return ApplicationPageItem.ATTRIBUTE_TOKEN;\n    }\n\n    @Override\n    protected void fillDeploys(final ApplicationPageItem item, final List<String> deploys) {\n        addDeployer(new PageDeployer(\n                factory.createPageDataStore(getEngineSession()), ApplicationPageItem.ATTRIBUTE_PAGE_ID));\n        addDeployer(new ApplicationDeployer(\n                factory.createApplicationDataStore(getEngineSession()), ApplicationPageItem.ATTRIBUTE_APPLICATION_ID));\n        super.fillDeploys(item, deploys);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bdm/BusinessDataController.java",
    "content": "/**\n * Copyright (C) 2025 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bdm;\n\nimport static org.bonitasoft.web.rest.server.QueryParameterUtils.parseFilters;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.servlet.http.HttpSession;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.bpm.businessdata.BusinessDataQueryResult;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.web.rest.server.api.AbstractRESTController;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@Slf4j\n@RestController\n@RequestMapping(\"/API/bdm/businessData/{className}\")\npublic class BusinessDataController extends AbstractRESTController {\n\n    @GetMapping(\"/{id}\")\n    public String getBusinessData(@PathVariable String className, @PathVariable long id, HttpSession session)\n            throws BonitaException {\n        return getBusinessData(className, id, null, session);\n    }\n\n    @GetMapping(\"/{id}/{fieldName}\")\n    public String getBusinessData(@PathVariable String className, @PathVariable long id, @PathVariable String fieldName,\n            HttpSession session) throws BonitaException {\n        final Map<String, Serializable> parameters = new HashMap<>();\n        parameters.put(\"entityClassName\", className);\n        parameters.put(\"businessDataId\", id);\n        parameters.put(\"businessDataURIPattern\", BusinessDataFieldValue.URI_PATTERN);\n        if (fieldName != null) {\n            parameters.put(\"businessDataChildName\", fieldName);\n        }\n        return (String) getCommandAPI(session).execute(\"getBusinessDataById\", parameters);\n    }\n\n    @GetMapping(\"/findByIds\")\n    public String getBusinessData(@PathVariable String className, @RequestParam(\"ids\") List<Long> ids,\n            HttpSession session) throws BonitaException {\n\n        final Map<String, Serializable> parameters = new HashMap<>();\n        parameters.put(\"entityClassName\", className);\n        parameters.put(\"businessDataIds\", (Serializable) ids);\n        parameters.put(\"businessDataURIPattern\", BusinessDataFieldValue.URI_PATTERN);\n        return (String) getCommandAPI(session).execute(\"getBusinessDataByIds\", parameters);\n    }\n\n    @GetMapping(\"\")\n    public ResponseEntity<String> getBusinessDataByQuery(@PathVariable String className,\n            @RequestParam(\"c\") int searchPageSize,\n            @RequestParam(value = \"f\", required = false) List<String> filters,\n            @RequestParam(\"p\") int searchPageNumber,\n            @RequestParam(\"q\") String queryName,\n            HttpSession session)\n            throws BonitaException {\n        final Map<String, Serializable> parameters = new HashMap<>();\n\n        parameters.put(\"queryName\", queryName);\n        parameters.put(\"queryParameters\", (Serializable) parseFilters(filters));\n        parameters.put(\"entityClassName\", className);\n        parameters.put(\"startIndex\", searchPageNumber * searchPageSize);\n        parameters.put(\"maxResults\", searchPageSize);\n        parameters.put(\"businessDataURIPattern\", BusinessDataFieldValue.URI_PATTERN);\n\n        if (log.isDebugEnabled()) {\n            log.debug(\"Executing business Data Query: {}\", parameters.get(\"queryName\"));\n            log.debug(\"entityClassName: {}\", parameters.get(\"entityClassName\"));\n            log.debug(\"queryParameters: {}\", parameters.get(\"queryParameters\").toString());\n            log.debug(\"startIndex: {}\", parameters.get(\"startIndex\"));\n            log.debug(\"maxResults: {}\", parameters.get(\"maxResults\"));\n        }\n\n        BusinessDataQueryResult businessDataQueryResult = (BusinessDataQueryResult) getCommandAPI(session).execute(\n                \"getBusinessDataByQueryCommand\",\n                parameters);\n\n        var responseBody = (String) businessDataQueryResult.getJsonResults();\n\n        // Build response with proper headers\n        final var businessDataQueryMetadata = businessDataQueryResult.getBusinessDataQueryMetadata();\n        Long totalCount = null;\n        if (businessDataQueryMetadata != null) {\n            totalCount = businessDataQueryMetadata.getCount();\n        }\n        HttpHeaders headers = buildHttpHeaders(searchPageSize, searchPageNumber, totalCount);\n\n        return ResponseEntity.ok().headers(headers).body(responseBody);\n    }\n\n    private static HttpHeaders buildHttpHeaders(Integer searchPageSize, Integer searchPageNumber, Long totalCount) {\n        HttpHeaders headers = new HttpHeaders();\n        if (totalCount != null) {\n            // Format content range header similar to what is produced by APIServletCall.doGet and CommonResource.setContentRange\n            // Our API is not conform to the Content-range header specs\n            String contentRangeValue = searchPageNumber + \"-\" + searchPageSize + \"/\" + totalCount;\n            headers.add(HttpHeaders.CONTENT_RANGE, contentRangeValue);\n        }\n        return headers;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bdm/BusinessDataFieldValue.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bdm;\n\n/**\n * @author Laurent Leseigneur\n */\npublic interface BusinessDataFieldValue {\n\n    // used by command. parameter names must not be changed\n    String URI_PATTERN = \"/API/bdm/businessData/{className}/{id}/{field}\";\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bdm/BusinessDataModelController.java",
    "content": "/**\n * Copyright (C) 2022-2025 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bdm;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\nimport javax.servlet.http.HttpSession;\n\nimport org.apache.commons.io.IOUtils;\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.engine.api.TenantAdministrationAPI;\nimport org.bonitasoft.engine.business.data.BusinessDataRepositoryDeploymentException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.TenantStatusException;\nimport org.bonitasoft.engine.io.FileContent;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.bonitasoft.web.rest.model.bdm.BusinessDataModelItem;\nimport org.bonitasoft.web.rest.server.api.AbstractRESTController;\nimport org.bonitasoft.web.rest.server.api.SpringResponseEntityUtils;\nimport org.bonitasoft.web.rest.server.api.tenant.TenantResourceItem;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIForbiddenException;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author Anthony Birembaut\n * @author Emmanuel Duchastenier\n */\n@RestController\n@RequestMapping(\"/API/tenant/bdm\")\npublic class BusinessDataModelController extends AbstractRESTController {\n\n    /**\n     * @deprecated as of 9.0.0. The BDM should only be updated at startup.\n     */\n    @PostMapping\n    @Deprecated(since = \"9.0.0\")\n    public TenantResourceItem installOrUpdateBDM(@RequestBody final BusinessDataModelItem businessDataModelItem,\n            HttpSession httpSession)\n            throws BonitaException {\n        final TenantAdministrationAPI tenantAdministrationAPI = getTenantAdministrationAPI(httpSession);\n        if (!tenantAdministrationAPI.isPaused()) {\n            // Exception handling is done in the method right below:\n            throw new TenantStatusException(\n                    \"Unable to install the Business Data Model. Please pause the BPM Services first. Go to Configuration > BPM Services.\");\n        }\n        try {\n            final FileContent businessDataModel = getBusinessDataModel(businessDataModelItem);\n            final byte[] businessDataModelContent = getBusinessDataModelContent(businessDataModel.getInputStream());\n            tenantAdministrationAPI.updateBusinessDataModel(businessDataModelContent);\n            return new TenantResourceItem(tenantAdministrationAPI.getBusinessDataModelResource(),\n                    businessDataModel.getFileName());\n        } catch (final BusinessDataRepositoryDeploymentException e) {\n            throw new APIException(\"An error has occurred when deploying Business Data Model.\", e);\n        } finally {\n            getBonitaHomeFolderAccessor().removeUploadedTempContent(businessDataModelItem.getFileUpload());\n        }\n    }\n\n    // In the particular case of this controller, we treat the TenantStatusException as a forbidden error.\n    // In other controllers, it is treated in SpringRestResponseEntityExceptionHandler as a Service Unavailable error.\n    @ExceptionHandler(value = { APIForbiddenException.class, TenantStatusException.class })\n    protected ResponseEntity<Object> handleAPIForbidden(Exception ex) {\n        return SpringResponseEntityUtils.generateErrorResponse(ex, HttpStatus.FORBIDDEN);\n    }\n\n    @GetMapping\n    public TenantResourceItem getBDM(HttpSession httpSession) {\n        try {\n            return new TenantResourceItem(getTenantAdministrationAPI(httpSession).getBusinessDataModelResource());\n        } catch (final TenantStatusException | InvalidSessionException e) {\n            throw e; //handled by REST API Authorization filter\n        } catch (final Exception e) {\n            throw new APIException(e);\n        }\n    }\n\n    protected FileContent getBusinessDataModel(final BusinessDataModelItem item) {\n        try {\n            return getBonitaHomeFolderAccessor().retrieveUploadedTempContent(item.getFileUpload());\n        } catch (final BonitaException e) {\n            throw new APIException(\"Can't read business data model file\", e);\n        }\n    }\n\n    private byte[] getBusinessDataModelContent(InputStream inputStream) {\n        try (inputStream) {\n            return IOUtils.toByteArray(inputStream);\n        } catch (IOException e) {\n            throw new APIException(\"Can't read business data model file\", e);\n        }\n    }\n\n    protected BonitaHomeFolderAccessor getBonitaHomeFolderAccessor() {\n        return new BonitaHomeFolderAccessor();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bdm/BusinessDataReferenceClient.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bdm;\n\nimport java.io.Serializable;\n\n/**\n * @author Baptiste Mesta\n */\npublic class BusinessDataReferenceClient implements Serializable {\n\n    private String name;\n    private String type;\n    private String link;\n\n    public BusinessDataReferenceClient(String name, String type, String link) {\n        this.name = name;\n        this.type = type;\n        this.link = link;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    public void setType(String type) {\n        this.type = type;\n    }\n\n    public String getLink() {\n        return link;\n    }\n\n    public void setLink(String link) {\n        this.link = link;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bdm/BusinessDataReferenceController.java",
    "content": "/**\n * Copyright (C) 2025 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bdm;\n\nimport java.util.List;\n\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.engine.bpm.data.DataNotFoundException;\nimport org.bonitasoft.engine.business.data.BusinessDataReference;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.web.rest.server.QueryParameterUtils;\nimport org.bonitasoft.web.rest.server.api.AbstractRESTController;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * Spring MVC controller for BusinessDataReference REST API.\n * <p>\n * Provides endpoints to retrieve business data references for process instances.\n * </p>\n *\n * @author Matthieu Chaffotte\n * @author Colin Puy\n */\n@RestController\n@RequestMapping(\"/API/bdm/businessDataReference\")\npublic class BusinessDataReferenceController extends AbstractRESTController {\n\n    /**\n     * Gets a specific business data reference by case ID and data name.\n     *\n     * @param caseId the process instance ID\n     * @param dataName the name of the business data variable\n     * @param httpSession the HTTP session\n     * @return the business data reference as a client object\n     * @throws DataNotFoundException if the business data reference is not found\n     * @throws BonitaException if an error occurs while accessing the API\n     */\n    @GetMapping(\"/{caseId}/{dataName}\")\n    public BusinessDataReferenceClient getProcessBusinessDataReference(\n            @PathVariable long caseId,\n            @PathVariable String dataName,\n            HttpSession httpSession) throws DataNotFoundException, BonitaException {\n        BusinessDataReference reference = getBusinessDataAPI(httpSession)\n                .getProcessBusinessDataReference(dataName, caseId);\n        return BusinessDataReferenceConverter.toClient(reference);\n    }\n\n    /**\n     * Gets all business data references for a process instance with pagination.\n     * <p>\n     * Example:\n     *\n     * <pre>\n     * f=caseId=123&p=0&c=10\n     * </pre>\n     *\n     * </p>\n     *\n     * @param filters standard filter set that MUST contain a filter named <code>caseId</code> and containing the\n     *        process instance ID: <code>f=caseId=123</code>\n     * @param page the page number (0-based)\n     * @param count the number of results per page\n     * @param httpSession the HTTP session\n     * @return a list of business data references as client objects\n     * @throws BonitaException if an error occurs while accessing the API\n     */\n    @GetMapping\n    public List<BusinessDataReferenceClient> getProcessBusinessDataReferences(\n            @RequestParam(\"f\") List<String> filters,\n            @RequestParam(\"p\") int page,\n            @RequestParam(\"c\") int count,\n            HttpSession httpSession) throws BonitaException {\n\n        long caseId = QueryParameterUtils.extractMandatoryLongFilter(filters, \"caseId\");\n        List<BusinessDataReference> references = getBusinessDataAPI(httpSession)\n                .getProcessBusinessDataReferences(caseId, page * count, count);\n        return references.stream()\n                .map(BusinessDataReferenceConverter::toClient)\n                .toList();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bdm/BusinessDataReferenceConverter.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bdm;\n\nimport java.io.Serializable;\n\nimport org.bonitasoft.engine.business.data.BusinessDataReference;\nimport org.bonitasoft.engine.business.data.MultipleBusinessDataReference;\nimport org.bonitasoft.engine.business.data.SimpleBusinessDataReference;\n\n/**\n * Converts {@link BusinessDataReference} objects to their client representations.\n * Used by context controllers to transform execution context values before returning them to the client.\n */\npublic final class BusinessDataReferenceConverter {\n\n    private static final String BDM_BUSINESS_DATA_URL = \"/bdm/businessData\";\n\n    private BusinessDataReferenceConverter() {\n    }\n\n    /**\n     * If the given object is a {@link BusinessDataReference}, converts it to a\n     * {@link BusinessDataReferenceClient}. Otherwise, returns the object unchanged.\n     */\n    public static Serializable convertIfApplicable(Serializable object) {\n        if (object instanceof BusinessDataReference reference) {\n            return toClient(reference);\n        }\n        return object;\n    }\n\n    /**\n     * Converts a {@link BusinessDataReference} to its client representation.\n     */\n    public static BusinessDataReferenceClient toClient(BusinessDataReference reference) {\n        if (reference instanceof SimpleBusinessDataReference simpleReference) {\n            return new SimpleBusinessDataReferenceClient(\n                    reference.getName(),\n                    reference.getType(),\n                    getUrl(reference.getType(), getStorageIdString(simpleReference)),\n                    simpleReference.getStorageId());\n        } else {\n            MultipleBusinessDataReference multipleReference = (MultipleBusinessDataReference) reference;\n            return new MultipleBusinessDataReferenceClient(\n                    reference.getName(),\n                    reference.getType(),\n                    getUrl(multipleReference.getType(), getStorageIdsValue(multipleReference)),\n                    multipleReference.getStorageIds());\n        }\n    }\n\n    private static String getStorageIdString(SimpleBusinessDataReference reference) {\n        Long storageId = reference.getStorageId();\n        if (storageId != null) {\n            return storageId.toString();\n        }\n        return \"\";\n    }\n\n    private static String getStorageIdsValue(MultipleBusinessDataReference reference) {\n        return \"findByIds?ids=\" + reference.getStorageIds().toString().replaceAll(\"[\\\\[\\\\] ]\", \"\");\n    }\n\n    private static String getUrl(String type, String value) {\n        return \"API\" + BDM_BUSINESS_DATA_URL + \"/\" + type + \"/\" + value;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bdm/MultipleBusinessDataReferenceClient.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bdm;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\n\n/**\n * @author Baptiste Mesta\n */\npublic class MultipleBusinessDataReferenceClient extends BusinessDataReferenceClient {\n\n    private final List<Long> storageIds;\n\n    @JsonProperty(\"storageIds_string\")\n    private final List<String> storageIdsAsString;\n\n    public MultipleBusinessDataReferenceClient(String name, String type, String link, List<Long> storageIds) {\n        super(name, type, link);\n        this.storageIds = storageIds;\n        storageIdsAsString = new ArrayList<>();\n        for (Long storageId : storageIds) {\n            storageIdsAsString.add(storageId.toString());\n        }\n    }\n\n    public List<Long> getStorageIds() {\n        return storageIds;\n    }\n\n    public List<String> getStorageIdsAsString() {\n        return storageIdsAsString;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bdm/SimpleBusinessDataReferenceClient.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bdm;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SimpleBusinessDataReferenceClient extends BusinessDataReferenceClient {\n\n    /**\n     * UID\n     */\n    private static final long serialVersionUID = 4377973199157064562L;\n\n    private Long storageId;\n\n    @JsonProperty(\"storageId_string\")\n    private String storageIdAsString;\n\n    public SimpleBusinessDataReferenceClient(final String name, final String type, final String link,\n            final Long storageId) {\n        super(name, type, link);\n        this.storageId = storageId;\n        if (storageId != null) {\n            storageIdAsString = storageId.toString();\n        }\n    }\n\n    public Long getStorageId() {\n        return storageId;\n    }\n\n    public void setStorageId(final Long storageId) {\n        this.storageId = storageId;\n    }\n\n    public String getStorageIdAsString() {\n        return storageIdAsString;\n    }\n\n    public void setStorageIdAsString(final String storageIdAsString) {\n        this.storageIdAsString = storageIdAsString;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/cases/APIArchivedCase.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.cases;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.web.rest.model.bpm.cases.ArchivedCaseDefinition;\nimport org.bonitasoft.web.rest.model.bpm.cases.ArchivedCaseItem;\nimport org.bonitasoft.web.rest.server.api.ConsoleAPI;\nimport org.bonitasoft.web.rest.server.datastore.bpm.cases.ArchivedCaseDatastore;\nimport org.bonitasoft.web.rest.server.datastore.bpm.process.ProcessDatastore;\nimport org.bonitasoft.web.rest.server.datastore.organization.UserDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasDelete;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasGet;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasSearch;\nimport org.bonitasoft.web.rest.server.framework.api.Datastore;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Séverin Moussel\n */\npublic class APIArchivedCase extends ConsoleAPI<ArchivedCaseItem>\n        implements APIHasGet<ArchivedCaseItem>, APIHasSearch<ArchivedCaseItem>, APIHasDelete {\n\n    @Override\n    public ItemDefinition defineItemDefinition() {\n        return Definitions.get(ArchivedCaseDefinition.TOKEN);\n    }\n\n    @Override\n    protected Datastore defineDefaultDatastore() {\n        return new ArchivedCaseDatastore(getEngineSession());\n    }\n\n    @Override\n    public ArchivedCaseItem get(final APIID id) {\n        return getArchivedCaseDatastore().get(id);\n    }\n\n    @Override\n    public String defineDefaultSearchOrder() {\n        return \"\";\n    }\n\n    @Override\n    public ItemSearchResult<ArchivedCaseItem> search(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n\n        // Check that team manager and supervisor filters are not used together\n        if (filters.containsKey(ArchivedCaseItem.FILTER_TEAM_MANAGER_ID)\n                && filters.containsKey(ArchivedCaseItem.FILTER_SUPERVISOR_ID)) {\n            throw new APIException(\n                    \"Can't set those filters at the same time : \" + ArchivedCaseItem.FILTER_TEAM_MANAGER_ID + \" and \"\n                            + ArchivedCaseItem.FILTER_SUPERVISOR_ID);\n        }\n\n        return getArchivedCaseDatastore().search(page, resultsByPage, search, orders, filters);\n    }\n\n    @Override\n    protected void fillDeploys(final ArchivedCaseItem item, final List<String> deploys) {\n        if (isDeployable(ArchivedCaseItem.ATTRIBUTE_STARTED_BY_USER_ID, deploys, item)) {\n            item.setDeploy(\n                    ArchivedCaseItem.ATTRIBUTE_STARTED_BY_USER_ID,\n                    getUserDatastore().get(item.getStartedByUserId()));\n        }\n\n        if (isDeployable(ArchivedCaseItem.ATTRIBUTE_STARTED_BY_SUBSTITUTE_USER_ID, deploys, item)) {\n            item.setDeploy(\n                    ArchivedCaseItem.ATTRIBUTE_STARTED_BY_SUBSTITUTE_USER_ID,\n                    getUserDatastore().get(item.getStartedBySubstituteUserId()));\n        }\n\n        if (isDeployable(ArchivedCaseItem.ATTRIBUTE_PROCESS_ID, deploys, item)) {\n            item.setDeploy(\n                    ArchivedCaseItem.ATTRIBUTE_PROCESS_ID,\n                    getProcessDatastore().get(item.getProcessId()));\n        }\n    }\n\n    /**\n     * @return\n     */\n    protected ProcessDatastore getProcessDatastore() {\n        return new ProcessDatastore(getEngineSession());\n    }\n\n    /**\n     * @return\n     */\n    protected UserDatastore getUserDatastore() {\n        return new UserDatastore(getEngineSession());\n    }\n\n    @Override\n    protected void fillCounters(final ArchivedCaseItem item, final List<String> counters) {\n    }\n\n    protected ArchivedCaseDatastore getArchivedCaseDatastore() {\n        return new ArchivedCaseDatastore(getEngineSession());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/cases/APIArchivedCaseDocument.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.cases;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.web.rest.model.bpm.cases.ArchivedCaseDocumentDefinition;\nimport org.bonitasoft.web.rest.model.bpm.cases.ArchivedCaseDocumentItem;\nimport org.bonitasoft.web.rest.server.api.ConsoleAPI;\nimport org.bonitasoft.web.rest.server.api.deployer.DeployerFactory;\nimport org.bonitasoft.web.rest.server.datastore.bpm.cases.ArchivedCaseDocumentDatastore;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Fabio Lombardi\n */\npublic class APIArchivedCaseDocument extends ConsoleAPI<ArchivedCaseDocumentItem> {\n\n    @Override\n    protected ItemDefinition defineItemDefinition() {\n        return Definitions.get(ArchivedCaseDocumentDefinition.TOKEN);\n    }\n\n    @Override\n    public ArchivedCaseDocumentItem get(final APIID id) {\n        return getArchivedCaseDocumentDatastore().get(id);\n    }\n\n    @Override\n    public String defineDefaultSearchOrder() {\n        return \"\";\n    }\n\n    @Override\n    protected void fillDeploys(final ArchivedCaseDocumentItem item, final List<String> deploys) {\n        addDeployer(getDeployerFactory().createUserDeployer(ArchivedCaseDocumentItem.ATTRIBUTE_SUBMITTED_BY_USER_ID));\n        addDeployer(getDeployerFactory().createUserDeployer(ArchivedCaseDocumentItem.ATTRIBUTE_AUTHOR));\n        super.fillDeploys(item, deploys);\n    }\n\n    protected DeployerFactory getDeployerFactory() {\n        return new DeployerFactory(getEngineSession());\n    }\n\n    @Override\n    protected void fillCounters(final ArchivedCaseDocumentItem item, final List<String> counters) {\n    }\n\n    @Override\n    public ItemSearchResult<ArchivedCaseDocumentItem> search(final int page, final int resultsByPage,\n            final String search, final String orders,\n            final Map<String, String> filters) {\n\n        return getArchivedCaseDocumentDatastore().search(page, resultsByPage, search, filters, orders);\n    }\n\n    @Override\n    public void delete(final List<APIID> ids) {\n        getArchivedCaseDocumentDatastore().delete(ids);\n    }\n\n    protected ArchivedCaseDocumentDatastore getArchivedCaseDocumentDatastore() {\n        ProcessAPI processAPI;\n        try {\n            processAPI = TenantAPIAccessor.getProcessAPI(getEngineSession());\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n        final WebBonitaConstantsUtils constants = WebBonitaConstantsUtils.getTenantInstance();\n\n        return new ArchivedCaseDocumentDatastore(getEngineSession(), processAPI);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/cases/APIArchivedComment.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.cases;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.web.rest.model.bpm.cases.ArchivedCommentDefinition;\nimport org.bonitasoft.web.rest.model.bpm.cases.ArchivedCommentItem;\nimport org.bonitasoft.web.rest.model.bpm.cases.CommentItem;\nimport org.bonitasoft.web.rest.model.identity.UserItem;\nimport org.bonitasoft.web.rest.server.api.ConsoleAPI;\nimport org.bonitasoft.web.rest.server.datastore.bpm.cases.ArchivedCommentDatastore;\nimport org.bonitasoft.web.rest.server.datastore.organization.UserDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasSearch;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Nicolas Tith\n */\npublic class APIArchivedComment extends ConsoleAPI<ArchivedCommentItem> implements APIHasSearch<ArchivedCommentItem> {\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // CONFIGURE\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    protected ItemDefinition defineItemDefinition() {\n        return Definitions.get(ArchivedCommentDefinition.TOKEN);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // C.R.U.D.S\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public ItemSearchResult<ArchivedCommentItem> search(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n\n        return getDatastore().search(page, resultsByPage, search, orders, filters);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // UTILS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public ArchivedCommentDatastore getDatastore() {\n        return new ArchivedCommentDatastore(getEngineSession());\n    }\n\n    @Override\n    public String defineDefaultSearchOrder() {\n        return \"\";\n    }\n\n    @Override\n    protected void fillDeploys(final ArchivedCommentItem item, final List<String> deploys) {\n        if (isDeployable(ArchivedCommentItem.ATTRIBUTE_USER_ID, deploys, item)) {\n            item.setDeploy(ArchivedCommentItem.ATTRIBUTE_USER_ID,\n                    new UserDatastore(getEngineSession()).get(item.getUserId()));\n        } else {\n            item.setDeploy(CommentItem.ATTRIBUTE_USER_ID, getSystemUser());\n        }\n\n        // TODO: Deploy process instance\n    }\n\n    private UserItem getSystemUser() {\n        final UserItem systemUser = new UserItem();\n        systemUser.setUserName(\"System\");\n        systemUser.setIcon(UserItem.DEFAULT_USER_ICON);\n        return systemUser;\n    }\n\n    @Override\n    protected void fillCounters(final ArchivedCommentItem item, final List<String> counters) {\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/cases/APICase.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.cases;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceCriterion;\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseDefinition;\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.FlowNodeItem;\nimport org.bonitasoft.web.rest.server.api.ConsoleAPI;\nimport org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore;\nimport org.bonitasoft.web.rest.server.datastore.bpm.flownode.FlowNodeDatastore;\nimport org.bonitasoft.web.rest.server.datastore.bpm.process.ProcessDatastore;\nimport org.bonitasoft.web.rest.server.datastore.organization.UserDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.*;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Séverin Moussel\n * @author Celine Souchet\n */\npublic class APICase extends ConsoleAPI<CaseItem>\n        implements APIHasUpdate<CaseItem>, APIHasGet<CaseItem>, APIHasAdd<CaseItem>, APIHasSearch<CaseItem>,\n        APIHasDelete {\n\n    @Override\n    protected ItemDefinition defineItemDefinition() {\n        return Definitions.get(CaseDefinition.TOKEN);\n    }\n\n    @Override\n    public CaseItem add(final CaseItem caseItem) {\n        return getCaseDatastore().add(caseItem);\n    }\n\n    @Override\n    public CaseItem get(final APIID id) {\n        return getCaseDatastore().get(id);\n    }\n\n    @Override\n    public ItemSearchResult<CaseItem> search(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n        // Check that team manager and supervisor filters are not used together\n        if (filters.containsKey(CaseItem.FILTER_TEAM_MANAGER_ID)\n                && filters.containsKey(CaseItem.FILTER_SUPERVISOR_ID)) {\n            throw new APIException(\n                    \"Can't set those filters at the same time : \" + CaseItem.FILTER_TEAM_MANAGER_ID + \" and \"\n                            + CaseItem.FILTER_SUPERVISOR_ID);\n        }\n\n        return getCaseDatastore().search(page, resultsByPage, search, orders, filters);\n    }\n\n    @Override\n    public String defineDefaultSearchOrder() {\n        return ProcessInstanceCriterion.CREATION_DATE_DESC.name();\n    }\n\n    @Override\n    protected void fillDeploys(final CaseItem item, final List<String> deploys) {\n        fillStartedBy(item, deploys);\n        fillStartedBySubstitute(item, deploys);\n        fillProcess(item, deploys);\n    }\n\n    private void fillStartedBy(final CaseItem item, final List<String> deploys) {\n        if (isDeployable(CaseItem.ATTRIBUTE_STARTED_BY_USER_ID, deploys, item)) {\n            item.setDeploy(\n                    CaseItem.ATTRIBUTE_STARTED_BY_USER_ID,\n                    getUserDatastore().get(item.getStartedByUserId()));\n        }\n    }\n\n    private void fillStartedBySubstitute(final CaseItem item, final List<String> deploys) {\n        if (isDeployable(CaseItem.ATTRIBUTE_STARTED_BY_SUBSTITUTE_USER_ID, deploys, item)) {\n            item.setDeploy(\n                    CaseItem.ATTRIBUTE_STARTED_BY_SUBSTITUTE_USER_ID,\n                    getUserDatastore().get(item.getStartedBySubstituteUserId()));\n        }\n    }\n\n    private void fillProcess(final CaseItem item, final List<String> deploys) {\n        if (isDeployable(CaseItem.ATTRIBUTE_PROCESS_ID, deploys, item)) {\n            item.setDeploy(\n                    CaseItem.ATTRIBUTE_PROCESS_ID,\n                    getProcessDatastore().get(item.getProcessId()));\n        }\n    }\n\n    private void fillNumberOfFailedFlowNodesIfFailedCounterExists(final CaseItem item, final List<String> counters,\n            Map<String, String> filters) {\n        if (counters.contains(CaseItem.COUNTER_FAILED_FLOW_NODES)) {\n            final FlowNodeDatastore flowNodeDatastore = getFlowNodeDatastore();\n            final Map<String, String> flowNodeFilters = new HashMap<>();\n            if (\"any\".equalsIgnoreCase(filters.get(CaseItem.FILTER_CALLER))) {\n                flowNodeFilters.put(FlowNodeItem.ATTRIBUTE_PARENT_CASE_ID, String.valueOf(item.getId().toLong()));\n            } else {\n                flowNodeFilters.put(FlowNodeItem.ATTRIBUTE_ROOT_CASE_ID, String.valueOf(item.getId().toLong()));\n            }\n            flowNodeFilters.put(FlowNodeItem.ATTRIBUTE_STATE, FlowNodeItem.VALUE_STATE_FAILED);\n            item.setAttribute(CaseItem.COUNTER_FAILED_FLOW_NODES, flowNodeDatastore.count(null, null, flowNodeFilters));\n        }\n    }\n\n    private void fillNumberOfActiveFlowNodesIfActiveCounterExists(final CaseItem item, final List<String> counters,\n            Map<String, String> filters) {\n        if (counters.contains(CaseItem.COUNTER_ACTIVE_FLOW_NODES)) {\n            final FlowNodeDatastore flowNodeDatastore = getFlowNodeDatastore();\n            final Map<String, String> flowNodeFilters = new HashMap<>();\n            if (\"any\".equalsIgnoreCase(filters.get(CaseItem.FILTER_CALLER))) {\n                flowNodeFilters.put(FlowNodeItem.ATTRIBUTE_PARENT_CASE_ID, String.valueOf(item.getId().toLong()));\n            } else {\n                flowNodeFilters.put(FlowNodeItem.ATTRIBUTE_ROOT_CASE_ID, String.valueOf(item.getId().toLong()));\n            }\n            item.setAttribute(CaseItem.COUNTER_ACTIVE_FLOW_NODES, flowNodeDatastore.count(null, null, flowNodeFilters));\n        }\n    }\n\n    private void fillNumberOfPendingFlowNodesIfActiveCounterExists(final CaseItem item, final List<String> counters,\n            Map<String, String> filters) {\n        if (counters.contains(CaseItem.COUNTER_PENDING_FLOW_NODES)) {\n            final FlowNodeDatastore flowNodeDatastore = getFlowNodeDatastore();\n            final Map<String, String> flowNodeFilters = new HashMap<>();\n            if (\"any\".equalsIgnoreCase(filters.get(CaseItem.FILTER_CALLER))) {\n                flowNodeFilters.put(FlowNodeItem.ATTRIBUTE_PARENT_CASE_ID, String.valueOf(item.getId().toLong()));\n            } else {\n                flowNodeFilters.put(FlowNodeItem.ATTRIBUTE_ROOT_CASE_ID, String.valueOf(item.getId().toLong()));\n            }\n            flowNodeFilters.put(FlowNodeItem.ATTRIBUTE_STATE, FlowNodeItem.VALUE_STATE_PENDING);\n            item.setAttribute(CaseItem.COUNTER_PENDING_FLOW_NODES,\n                    flowNodeDatastore.count(null, null, flowNodeFilters));\n        }\n    }\n\n    @Override\n    public void delete(final List<APIID> ids) {\n        getCaseDatastore().delete(ids);\n    }\n\n    @Override\n    protected void fillCountersDependingOnFilters(final CaseItem item, final List<String> counters,\n            final Map<String, String> filters) {\n        fillNumberOfFailedFlowNodesIfFailedCounterExists(item, counters, filters);\n        fillNumberOfActiveFlowNodesIfActiveCounterExists(item, counters, filters);\n        fillNumberOfPendingFlowNodesIfActiveCounterExists(item, counters, filters);\n    }\n\n    @Override\n    protected void fillCounters(final CaseItem item, final List<String> counters) {\n        fillNumberOfFailedFlowNodesIfFailedCounterExists(item, counters, Map.of(CaseItem.FILTER_CALLER, \"any\"));\n        fillNumberOfActiveFlowNodesIfActiveCounterExists(item, counters, Map.of(CaseItem.FILTER_CALLER, \"any\"));\n        fillNumberOfPendingFlowNodesIfActiveCounterExists(item, counters, Map.of(CaseItem.FILTER_CALLER, \"any\"));\n    }\n\n    UserDatastore getUserDatastore() {\n        return new UserDatastore(getEngineSession());\n    }\n\n    ProcessDatastore getProcessDatastore() {\n        return new ProcessDatastore(getEngineSession());\n    }\n\n    FlowNodeDatastore getFlowNodeDatastore() {\n        return new FlowNodeDatastore(getEngineSession());\n    }\n\n    protected CaseDatastore getCaseDatastore() {\n        return new CaseDatastore(getEngineSession());\n    }\n\n    @Override\n    public CaseItem update(final APIID id, final Map<String, String> attributes) {\n        return getCaseDatastore().update(id, attributes);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/cases/APICaseDocument.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.cases;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils;\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseDocumentDefinition;\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseDocumentItem;\nimport org.bonitasoft.web.rest.server.api.ConsoleAPI;\nimport org.bonitasoft.web.rest.server.api.deployer.DeployerFactory;\nimport org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDocumentDatastore;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Fabio Lombardi\n */\npublic class APICaseDocument extends ConsoleAPI<CaseDocumentItem> {\n\n    @Override\n    protected ItemDefinition defineItemDefinition() {\n        return Definitions.get(CaseDocumentDefinition.TOKEN);\n    }\n\n    @Override\n    public CaseDocumentItem get(final APIID id) {\n        return getCaseDocumentDatastore().get(id);\n    }\n\n    @Override\n    public CaseDocumentItem add(final CaseDocumentItem item) {\n        return getCaseDocumentDatastore().add(item);\n    }\n\n    @Override\n    public CaseDocumentItem update(final APIID id, final Map<String, String> attributes) {\n        return getCaseDocumentDatastore().update(id, attributes);\n    }\n\n    @Override\n    public String defineDefaultSearchOrder() {\n        return \"\";\n    }\n\n    @Override\n    protected void fillDeploys(final CaseDocumentItem item, final List<String> deploys) {\n        addDeployer(getDeployerFactory().createUserDeployer(CaseDocumentItem.ATTRIBUTE_SUBMITTED_BY_USER_ID));\n        addDeployer(getDeployerFactory().createUserDeployer(CaseDocumentItem.ATTRIBUTE_AUTHOR));\n        super.fillDeploys(item, deploys);\n    }\n\n    protected DeployerFactory getDeployerFactory() {\n        return new DeployerFactory(getEngineSession());\n    }\n\n    @Override\n    protected void fillCounters(final CaseDocumentItem item, final List<String> counters) {\n    }\n\n    @Override\n    public ItemSearchResult<CaseDocumentItem> search(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n\n        return getCaseDocumentDatastore().search(page, resultsByPage, search, filters, orders);\n    }\n\n    @Override\n    public void delete(final List<APIID> ids) {\n        getCaseDocumentDatastore().delete(ids);\n    }\n\n    protected CaseDocumentDatastore getCaseDocumentDatastore() {\n        ProcessAPI processAPI;\n        try {\n            processAPI = TenantAPIAccessor.getProcessAPI(getEngineSession());\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n        final WebBonitaConstantsUtils constants = WebBonitaConstantsUtils.getTenantInstance();\n\n        return new CaseDocumentDatastore(getEngineSession(), processAPI, new BonitaHomeFolderAccessor());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/cases/APICaseVariable.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.cases;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseVariableDefinition;\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseVariableItem;\nimport org.bonitasoft.web.rest.server.api.ConsoleAPI;\nimport org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseVariableDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasGet;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasSearch;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasUpdate;\nimport org.bonitasoft.web.rest.server.framework.api.Datastore;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Colin PUY\n */\npublic class APICaseVariable extends ConsoleAPI<CaseVariableItem> implements APIHasSearch<CaseVariableItem>,\n        APIHasUpdate<CaseVariableItem>, APIHasGet<CaseVariableItem> {\n\n    private APICaseVariableAttributeChecker attributeChecker = new APICaseVariableAttributeChecker();\n\n    @Override\n    public CaseVariableItem runUpdate(APIID id, Map<String, String> attributes) {\n        attributeChecker.checkUpdateAttributes(attributes);\n        id.setItemDefinition(CaseVariableDefinition.get());\n        CaseVariableItem item = CaseVariableItem.fromIdAndAttributes(id, attributes);\n        ((CaseVariableDatastore) getDefaultDatastore()).updateVariableValue(item.getCaseId(), item.getName(),\n                item.getType(), item.getValue());\n        return item;\n    }\n\n    @Override\n    public ItemSearchResult<CaseVariableItem> runSearch(int page, int resultsByPage, String search, String orders,\n            Map<String, String> filters, List<String> deploys, List<String> counters) {\n        attributeChecker.checkSearchFilters(filters);\n        long caseId = Long.valueOf(filters.get(CaseVariableItem.ATTRIBUTE_CASE_ID));\n        return ((CaseVariableDatastore) getDefaultDatastore()).findByCaseId(caseId, page, resultsByPage);\n    }\n\n    @Override\n    public CaseVariableItem runGet(APIID id, List<String> deploys, List<String> counters) {\n        id.setItemDefinition(CaseVariableDefinition.get());\n        long caseId = id.getPartAsLong(CaseVariableItem.ATTRIBUTE_CASE_ID);\n        String variableName = id.getPart(CaseVariableItem.ATTRIBUTE_NAME);\n        return ((CaseVariableDatastore) getDefaultDatastore()).findById(caseId, variableName);\n    }\n\n    @Override\n    protected Datastore defineDefaultDatastore() {\n        return new CaseVariableDatastore(getEngineSession());\n    }\n\n    @Override\n    protected ItemDefinition defineItemDefinition() {\n        return CaseVariableDefinition.get();\n    }\n\n    /*\n     * Only used for tests because we cannot pass anything to constructor due to design restrictions\n     */\n    public void setAttributeChecker(APICaseVariableAttributeChecker attributeChecker) {\n        this.attributeChecker = attributeChecker;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/cases/APICaseVariableAttributeChecker.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.cases;\n\nimport static java.lang.String.format;\n\nimport java.util.Map;\n\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseVariableItem;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\n\n/**\n * @author Colin PUY\n */\npublic class APICaseVariableAttributeChecker {\n\n    public void checkUpdateAttributes(Map<String, String> attributes) {\n        if (attributes == null || attributes.isEmpty()) {\n            throw new APIException(format(\"Attributes '%s' and '%s' must be specified\", CaseVariableItem.ATTRIBUTE_TYPE,\n                    CaseVariableItem.ATTRIBUTE_VALUE));\n        }\n        if (!attributes.containsKey(CaseVariableItem.ATTRIBUTE_TYPE)) {\n            throw new APIException(format(\"Attribute '%s' must be specified\", CaseVariableItem.ATTRIBUTE_TYPE));\n        }\n        if (!attributes.containsKey(CaseVariableItem.ATTRIBUTE_VALUE)) {\n            throw new APIException(format(\"Attribute '%s' must be specified\", CaseVariableItem.ATTRIBUTE_VALUE));\n        }\n    }\n\n    // filters could be null...\n    public void checkSearchFilters(Map<String, String> filters) {\n        if (filters == null || !filters.containsKey(CaseVariableItem.ATTRIBUTE_CASE_ID)) {\n            throw new APIException(format(\"Filter '%s' must be specified\", CaseVariableItem.ATTRIBUTE_CASE_ID));\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/cases/APIComment.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.cases;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.web.rest.model.bpm.cases.CommentDefinition;\nimport org.bonitasoft.web.rest.model.bpm.cases.CommentItem;\nimport org.bonitasoft.web.rest.model.identity.UserItem;\nimport org.bonitasoft.web.rest.server.api.ConsoleAPI;\nimport org.bonitasoft.web.rest.server.datastore.bpm.cases.CommentDatastore;\nimport org.bonitasoft.web.rest.server.datastore.organization.UserDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasAdd;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasSearch;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Vincent Elcrin\n */\npublic class APIComment extends ConsoleAPI<CommentItem> implements APIHasAdd<CommentItem>, APIHasSearch<CommentItem> {\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // CONFIGURE\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    protected ItemDefinition defineItemDefinition() {\n        return Definitions.get(CommentDefinition.TOKEN);\n    }\n\n    @Override\n    public String defineDefaultSearchOrder() {\n        // FIXME Define criterion\n        return \"\";\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // C.R.U.D.S\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public ItemSearchResult<CommentItem> search(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n        return new CommentDatastore(getEngineSession()).search(page, resultsByPage, search, orders, filters);\n    }\n\n    @Override\n    public CommentItem add(final CommentItem comment) {\n        return new CommentDatastore(getEngineSession()).add(comment);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // UTILS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    /*\n     * (non-Javadoc)\n     * @see org.bonitasoft.web.toolkit.server.API#fillDeploys(org.bonitasoft.web.toolkit.client.data.item.Item,\n     * java.util.List)\n     */\n    @Override\n    protected void fillDeploys(final CommentItem item, final List<String> deploys) {\n        if (isDeployable(CommentItem.ATTRIBUTE_USER_ID, deploys, item)) {\n            item.setDeploy(CommentItem.ATTRIBUTE_USER_ID,\n                    new UserDatastore(getEngineSession()).get(item.getUserId()));\n        } else {\n            item.setDeploy(CommentItem.ATTRIBUTE_USER_ID, getSystemUser());\n        }\n\n        // FIXME Deploy process instance\n    }\n\n    private UserItem getSystemUser() {\n        final UserItem systemUser = new UserItem();\n        systemUser.setUserName(\"System\");\n        systemUser.setIcon(UserItem.DEFAULT_USER_ICON);\n        return systemUser;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see org.bonitasoft.web.toolkit.server.API#fillCounters(org.bonitasoft.web.toolkit.client.data.item.Item,\n     * java.util.List)\n     */\n    @Override\n    protected void fillCounters(final CommentItem item, final List<String> counters) {\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/cases/ArchivedCaseContextController.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.cases;\n\nimport static org.bonitasoft.web.rest.server.api.AbstractRESTController.API_SPRING_INTERNAL;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.web.rest.server.api.AbstractRESTController;\nimport org.bonitasoft.web.rest.server.api.bdm.BusinessDataReferenceConverter;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * REST API controller for retrieving archived case (process instance) execution context.\n * Provides access to process variables, business data, and other context elements from archived cases.\n */\n@RestController\n@RequestMapping(\"/\" + API_SPRING_INTERNAL + \"/bpm/archivedCase/{archivedCaseId}/context\")\npublic class ArchivedCaseContextController extends AbstractRESTController {\n\n    @GetMapping\n    public Map<String, Serializable> getArchivedCaseContext(@PathVariable long archivedCaseId, HttpSession httpSession)\n            throws Exception {\n\n        Map<String, Serializable> caseExecutionContext = getProcessAPI(httpSession)\n                .getArchivedProcessInstanceExecutionContext(archivedCaseId);\n\n        Map<String, Serializable> resultMap = new HashMap<>();\n        caseExecutionContext\n                .forEach((key, value) -> resultMap.put(key, BusinessDataReferenceConverter.convertIfApplicable(value)));\n        return resultMap;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/cases/ArchivedCaseVariableController.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.cases;\n\nimport static org.bonitasoft.web.rest.server.APIPaginationUtils.buildContentRange;\n\nimport java.util.List;\n\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.engine.bpm.data.ArchivedDataInstance;\nimport org.bonitasoft.web.rest.model.bpm.cases.ArchivedCaseVariable;\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseVariableItem;\nimport org.bonitasoft.web.rest.server.QueryParameterUtils;\nimport org.bonitasoft.web.rest.server.api.AbstractRESTController;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/API/bpm/archivedCaseVariable\")\npublic class ArchivedCaseVariableController extends AbstractRESTController {\n\n    @GetMapping(\"/{caseId}/{variableName}\")\n    public ArchivedCaseVariable getArchivedCaseVariable(\n            @PathVariable long caseId,\n            @PathVariable String variableName,\n            HttpSession session) throws Exception {\n        var archivedProcessDataInstance = getProcessAPI(session)\n                .getArchivedProcessDataInstance(variableName, caseId);\n        return ArchivedCaseVariable.create(archivedProcessDataInstance);\n    }\n\n    @GetMapping\n    public ResponseEntity<List<ArchivedCaseVariable>> getArchivedCaseVariables(\n            @RequestParam(\"p\") int page,\n            @RequestParam(\"c\") int count,\n            @RequestParam(value = \"f\", required = false) List<String> filters,\n            HttpSession session) throws Exception {\n        long caseId = QueryParameterUtils.extractMandatoryLongFilter(filters, CaseVariableItem.ATTRIBUTE_CASE_ID);\n        List<ArchivedDataInstance> allResults = getProcessAPI(session)\n                .getArchivedProcessDataInstances(caseId, 0, Integer.MAX_VALUE);\n        List<ArchivedCaseVariable> pagedResults = allResults.stream()\n                .skip((long) page * count)\n                .limit(count)\n                .map(ArchivedCaseVariable::create)\n                .toList();\n        return ResponseEntity.ok().header(HttpHeaders.CONTENT_RANGE, buildContentRange(page, count, allResults.size()))\n                .body(pagedResults);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/cases/CaseContextController.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.cases;\n\nimport static org.bonitasoft.web.rest.server.api.AbstractRESTController.API_SPRING_INTERNAL;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.web.rest.server.api.AbstractRESTController;\nimport org.bonitasoft.web.rest.server.api.bdm.BusinessDataReferenceConverter;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * REST API controller for retrieving case (process instance) execution context.\n * Provides access to process variables, business data, and other context elements.\n */\n@RestController\n@RequestMapping(\"/\" + API_SPRING_INTERNAL + \"/bpm/case/{caseId}/context\")\npublic class CaseContextController extends AbstractRESTController {\n\n    @GetMapping\n    public Map<String, Serializable> getCaseContext(@PathVariable long caseId, HttpSession httpSession)\n            throws Exception {\n        Map<String, Serializable> resultMap = new HashMap<>();\n        Map<String, Serializable> caseExecutionContext = getProcessAPI(httpSession)\n                .getProcessInstanceExecutionContext(caseId);\n\n        for (Map.Entry<String, Serializable> entry : caseExecutionContext.entrySet()) {\n            resultMap.put(entry.getKey(), BusinessDataReferenceConverter.convertIfApplicable(entry.getValue()));\n        }\n\n        return resultMap;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/connector/APIArchivedConnectorInstance.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.connector;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorInstanceCriterion;\nimport org.bonitasoft.web.rest.model.bpm.connector.ArchivedConnectorInstanceDefinition;\nimport org.bonitasoft.web.rest.model.bpm.connector.ArchivedConnectorInstanceItem;\nimport org.bonitasoft.web.rest.server.api.ConsoleAPI;\nimport org.bonitasoft.web.rest.server.datastore.bpm.connector.ArchivedConnectorInstanceDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasSearch;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasUpdate;\nimport org.bonitasoft.web.rest.server.framework.api.Datastore;\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Julien Mege\n */\npublic class APIArchivedConnectorInstance extends ConsoleAPI<ArchivedConnectorInstanceItem>\n        implements APIHasSearch<ArchivedConnectorInstanceItem>,\n        APIHasUpdate<ArchivedConnectorInstanceItem> {\n\n    @Override\n    protected ItemDefinition defineItemDefinition() {\n        return Definitions.get(ArchivedConnectorInstanceDefinition.TOKEN);\n    }\n\n    @Override\n    public String defineDefaultSearchOrder() {\n        return ConnectorInstanceCriterion.NAME_ASC.name();\n    }\n\n    @Override\n    protected Datastore defineDefaultDatastore() {\n        return new ArchivedConnectorInstanceDatastore(getEngineSession());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/connector/APIConnectorInstance.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.connector;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorInstanceCriterion;\nimport org.bonitasoft.web.rest.model.bpm.connector.ConnectorInstanceDefinition;\nimport org.bonitasoft.web.rest.model.bpm.connector.ConnectorInstanceItem;\nimport org.bonitasoft.web.rest.server.api.ConsoleAPI;\nimport org.bonitasoft.web.rest.server.datastore.bpm.connector.ConnectorInstanceDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasSearch;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasUpdate;\nimport org.bonitasoft.web.rest.server.framework.api.Datastore;\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Vincent Elcrin\n */\npublic class APIConnectorInstance extends ConsoleAPI<ConnectorInstanceItem>\n        implements APIHasSearch<ConnectorInstanceItem>, APIHasUpdate<ConnectorInstanceItem> {\n\n    @Override\n    protected ItemDefinition defineItemDefinition() {\n        return Definitions.get(ConnectorInstanceDefinition.TOKEN);\n    }\n\n    @Override\n    public String defineDefaultSearchOrder() {\n        return ConnectorInstanceCriterion.NAME_ASC.name();\n    }\n\n    @Override\n    protected Datastore defineDefaultDatastore() {\n        return new ConnectorInstanceDatastore(getEngineSession());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/APIActivity.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode;\n\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ActivityItem;\nimport org.bonitasoft.web.rest.server.framework.search.ISearchDirection;\n\n/**\n * @author Séverin Moussel\n */\npublic class APIActivity extends AbstractAPIActivity<ActivityItem> {\n\n    @Override\n    public String defineDefaultSearchOrder() {\n        return ActivityInstanceSearchDescriptor.STATE_NAME + ISearchDirection.SORT_ORDER_ASCENDING;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/APIFlowNode.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode;\n\nimport org.bonitasoft.web.rest.model.bpm.flownode.FlowNodeItem;\n\n/**\n * @author Séverin Moussel\n */\npublic class APIFlowNode extends AbstractAPIFlowNode<FlowNodeItem> {\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/APIHumanTask.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode;\n\nimport org.bonitasoft.web.rest.model.bpm.flownode.HumanTaskItem;\n\n/**\n * @author Séverin Moussel\n */\npublic class APIHumanTask extends AbstractAPIHumanTask<HumanTaskItem> {\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/APITask.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode;\n\nimport org.bonitasoft.web.rest.model.bpm.flownode.TaskItem;\nimport org.bonitasoft.web.rest.server.framework.search.ISearchDirection;\n\n/**\n * @author Séverin Moussel\n */\npublic class APITask extends AbstractAPITask<TaskItem> {\n\n    @Override\n    protected boolean isAllowedState(final String state) {\n        return TaskItem.VALUE_STATE_REPLAY.equals(state)\n                || super.isAllowedState(state);\n    }\n\n    @Override\n    public String defineDefaultSearchOrder() {\n        return TaskItem.ATTRIBUTE_LAST_UPDATE_DATE + ISearchDirection.SORT_ORDER_DESCENDING;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/APIUserTask.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode;\n\nimport org.bonitasoft.web.rest.model.bpm.flownode.UserTaskItem;\n\n/**\n * @author Séverin Moussel\n */\npublic class APIUserTask extends AbstractAPIUserTask<UserTaskItem> {\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/AbstractAPIActivity.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode;\n\nimport java.util.List;\n\nimport org.bonitasoft.web.rest.model.bpm.flownode.ActivityDefinition;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ActivityItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.IActivityItem;\nimport org.bonitasoft.web.rest.server.datastore.bpm.flownode.ActivityDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.Datastore;\n\n/**\n * @author Séverin Moussel\n */\npublic class AbstractAPIActivity<ITEM extends IActivityItem> extends AbstractAPIFlowNode<ITEM> {\n\n    @Override\n    protected ActivityDefinition defineItemDefinition() {\n        return ActivityDefinition.get();\n    }\n\n    @Override\n    protected Datastore defineDefaultDatastore() {\n        return new ActivityDatastore(getEngineSession());\n    }\n\n    @Override\n    protected List<String> defineReadOnlyAttributes() {\n        final List<String> attributes = super.defineReadOnlyAttributes();\n        attributes.add(ActivityItem.ATTRIBUTE_REACHED_STATE_DATE);\n        attributes.add(ActivityItem.ATTRIBUTE_LAST_UPDATE_DATE);\n        return attributes;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/AbstractAPIFlowNode.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstanceSearchDescriptor;\nimport org.bonitasoft.web.rest.model.bpm.cases.ArchivedCaseItem;\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedHumanTaskItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedTaskDefinition;\nimport org.bonitasoft.web.rest.model.bpm.flownode.FlowNodeDefinition;\nimport org.bonitasoft.web.rest.model.bpm.flownode.FlowNodeItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.HumanTaskItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.IFlowNodeItem;\nimport org.bonitasoft.web.rest.server.api.ConsoleAPI;\nimport org.bonitasoft.web.rest.server.api.deployer.GenericDeployer;\nimport org.bonitasoft.web.rest.server.datastore.bpm.cases.ArchivedCaseDatastore;\nimport org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore;\nimport org.bonitasoft.web.rest.server.datastore.bpm.flownode.FlowNodeDatastore;\nimport org.bonitasoft.web.rest.server.datastore.bpm.flownode.TaskDatastore;\nimport org.bonitasoft.web.rest.server.datastore.bpm.flownode.TaskFinder;\nimport org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.ArchivedTaskDatastore;\nimport org.bonitasoft.web.rest.server.datastore.bpm.process.ActorDatastore;\nimport org.bonitasoft.web.rest.server.datastore.bpm.process.ProcessDatastore;\nimport org.bonitasoft.web.rest.server.datastore.organization.UserDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasGet;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasSearch;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasUpdate;\nimport org.bonitasoft.web.rest.server.framework.api.Datastore;\nimport org.bonitasoft.web.rest.server.framework.search.ISearchDirection;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * Contains all the implementation for a APIFlowNode and inherited APIs\n *\n * @author Séverin Moussel\n */\npublic class AbstractAPIFlowNode<ITEM extends IFlowNodeItem> extends ConsoleAPI<ITEM> implements\n        APIHasUpdate<ITEM>,\n        APIHasGet<ITEM>,\n        APIHasSearch<ITEM> {\n\n    @Override\n    protected FlowNodeDefinition defineItemDefinition() {\n        return FlowNodeDefinition.get();\n    }\n\n    @Override\n    public String defineDefaultSearchOrder() {\n        return FlowNodeInstanceSearchDescriptor.DISPLAY_NAME + ISearchDirection.SORT_ORDER_ASCENDING;\n    }\n\n    @Override\n    protected Datastore defineDefaultDatastore() {\n        return new FlowNodeDatastore(getEngineSession());\n    }\n\n    @Override\n    public ItemSearchResult<ITEM> search(final int page, final int resultsByPage, final String search,\n            final String orders, final Map<String, String> filters) {\n        // Check that team manager and supervisor filters are not used together\n        if (filters.containsKey(HumanTaskItem.FILTER_TEAM_MANAGER_ID)\n                && filters.containsKey(HumanTaskItem.FILTER_SUPERVISOR_ID)) {\n            throw new APIException(\"Can't set those filters at the same time : \"\n                    + HumanTaskItem.FILTER_TEAM_MANAGER_ID\n                    + \" and \"\n                    + HumanTaskItem.FILTER_SUPERVISOR_ID);\n        }\n\n        return super.search(page, resultsByPage, search, orders, filters);\n    }\n\n    @Override\n    public ITEM update(final APIID id, final Map<String, String> attributes) {\n        final String state = attributes.get(FlowNodeItem.ATTRIBUTE_STATE);\n        if (state != null && !isAllowedState(state)) {\n            throw new APIException(\"Can't update a flow node state to \\\"\" + state + \"\\\"\");\n        }\n\n        return super.update(id, attributes);\n    }\n\n    protected boolean isAllowedState(final String state) {\n        return FlowNodeItem.VALUE_STATE_READY.equals(state)\n                || FlowNodeItem.VALUE_STATE_SKIPPED.equals(state)\n                || FlowNodeItem.VALUE_STATE_REPLAY.equals(state)\n                || FlowNodeItem.VALUE_STATE_COMPLETED.equals(state);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // UTILS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    protected void fillDeploys(final ITEM item, final List<String> deploys) {\n        /** TODO Refactor to an oriented object (cf. WEB-1637 ) */\n        if (isDeployable(FlowNodeItem.ATTRIBUTE_PROCESS_ID, deploys, item)) {\n            item.setDeploy(FlowNodeItem.ATTRIBUTE_PROCESS_ID,\n                    new ProcessDatastore(getEngineSession()).get(item.getProcessId()));\n        }\n\n        if (isDeployable(FlowNodeItem.ATTRIBUTE_CASE_ID, deploys, item)\n                || isDeployable(FlowNodeItem.ATTRIBUTE_ROOT_CASE_ID, deploys, item)) {\n            final CaseItem openedCaseItem = getCaseDatastore().get(item.getCaseId());\n            if (openedCaseItem != null) {\n                item.setDeploy(FlowNodeItem.ATTRIBUTE_CASE_ID, openedCaseItem);\n                item.setDeploy(FlowNodeItem.ATTRIBUTE_ROOT_CASE_ID, openedCaseItem);\n            } else {\n                final ArchivedCaseItem archivedCaseItem = getArchivedCaseDatastore()\n                        .getUsingSourceObjectId(item.getCaseId());\n                item.setDeploy(FlowNodeItem.ATTRIBUTE_CASE_ID, archivedCaseItem);\n                item.setDeploy(FlowNodeItem.ATTRIBUTE_ROOT_CASE_ID, archivedCaseItem);\n            }\n        }\n\n        if (isDeployable(FlowNodeItem.ATTRIBUTE_PARENT_CASE_ID, deploys, item)) {\n            final CaseItem openedParentCaseItem = getCaseDatastore().get(item.getParentCaseId());\n            if (openedParentCaseItem != null) {\n                item.setDeploy(FlowNodeItem.ATTRIBUTE_PARENT_CASE_ID,\n                        openedParentCaseItem);\n            } else {\n                final ArchivedCaseItem archivedParentCaseItem = getArchivedCaseDatastore()\n                        .getUsingSourceObjectId(item.getParentCaseId());\n                item.setDeploy(FlowNodeItem.ATTRIBUTE_PARENT_CASE_ID,\n                        archivedParentCaseItem);\n            }\n        }\n\n        if (isDeployable(FlowNodeItem.ATTRIBUTE_ROOT_CONTAINER_ID, deploys, item)) {\n            CaseItem rootContainerCase = getCaseDatastore().get(item\n                    .getAttributeValueAsAPIID(HumanTaskItem.ATTRIBUTE_ROOT_CONTAINER_ID));\n            if (rootContainerCase == null) {\n                rootContainerCase = getArchivedCase(item.getAttributeValue(HumanTaskItem.ATTRIBUTE_ROOT_CONTAINER_ID));\n            }\n            if (rootContainerCase != null) {\n                item.setDeploy(FlowNodeItem.ATTRIBUTE_ROOT_CONTAINER_ID,\n                        new ProcessDatastore(getEngineSession()).get(rootContainerCase.getProcessId()));\n            }\n        }\n\n        if (isDeployable(FlowNodeItem.ATTRIBUTE_EXECUTED_BY_USER_ID, deploys, item)) {\n            item.setDeploy(FlowNodeItem.ATTRIBUTE_EXECUTED_BY_USER_ID,\n                    new UserDatastore(getEngineSession()).get(item.getExecutedByUserId()));\n        }\n\n        if (isDeployable(FlowNodeItem.ATTRIBUTE_EXECUTED_BY_SUBSTITUTE_USER_ID, deploys, item)) {\n            item.setDeploy(FlowNodeItem.ATTRIBUTE_EXECUTED_BY_SUBSTITUTE_USER_ID,\n                    new UserDatastore(getEngineSession()).get(item.getExecutedBySubstituteUserId()));\n        }\n\n        if (isDeployable(HumanTaskItem.ATTRIBUTE_ACTOR_ID, deploys, item)) {\n            item.setDeploy(HumanTaskItem.ATTRIBUTE_ACTOR_ID,\n                    new ActorDatastore(getEngineSession())\n                            .get(item.getAttributeValueAsAPIID(HumanTaskItem.ATTRIBUTE_ACTOR_ID)));\n        }\n\n        if (isDeployable(HumanTaskItem.ATTRIBUTE_ASSIGNED_USER_ID, deploys, item)) {\n            item.setDeploy(HumanTaskItem.ATTRIBUTE_ASSIGNED_USER_ID,\n                    new UserDatastore(getEngineSession())\n                            .get(item.getAttributeValueAsAPIID(HumanTaskItem.ATTRIBUTE_ASSIGNED_USER_ID)));\n        }\n\n        addDeployer(new GenericDeployer<>(id -> new TaskFinder(\n                new TaskDatastore(getEngineSession()),\n                new ArchivedTaskDatastore(getEngineSession(), ArchivedTaskDefinition.TOKEN)).find(id),\n                HumanTaskItem.ATTRIBUTE_PARENT_TASK_ID));\n\n        super.fillDeploys(item, deploys);\n    }\n\n    protected CaseDatastore getCaseDatastore() {\n        return new CaseDatastore(getEngineSession());\n    }\n\n    private CaseItem getArchivedCase(final String id) {\n        final List<ArchivedCaseItem> result = getArchivedCaseDatastore().search(\n                0, 1,\n                null,\n                null,\n                Collections.singletonMap(ArchivedHumanTaskItem.ATTRIBUTE_SOURCE_OBJECT_ID, id)).getResults();\n        if (result.size() > 0) {\n            return result.get(0);\n        }\n        return null;\n    }\n\n    protected ArchivedCaseDatastore getArchivedCaseDatastore() {\n        return new ArchivedCaseDatastore(getEngineSession());\n    }\n\n    @Override\n    protected List<String> defineReadOnlyAttributes() {\n        final List<String> attributes = new ArrayList<>();\n\n        attributes.add(FlowNodeItem.ATTRIBUTE_CASE_ID);\n        attributes.add(FlowNodeItem.ATTRIBUTE_ROOT_CASE_ID);\n        attributes.add(FlowNodeItem.ATTRIBUTE_PARENT_CASE_ID);\n        attributes.add(FlowNodeItem.ATTRIBUTE_PROCESS_ID);\n        attributes.add(FlowNodeItem.ATTRIBUTE_DESCRIPTION);\n        attributes.add(FlowNodeItem.ATTRIBUTE_NAME);\n        attributes.add(FlowNodeItem.ATTRIBUTE_EXECUTED_BY_USER_ID);\n        attributes.add(FlowNodeItem.ATTRIBUTE_TYPE);\n        attributes.add(FlowNodeItem.ATTRIBUTE_ID);\n\n        return attributes;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/AbstractAPIHumanTask.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode;\n\nimport static org.bonitasoft.web.rest.model.bpm.flownode.IFlowNodeItem.VALUE_TYPE_MANUAL_TASK;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.web.rest.model.bpm.flownode.FlowNodeItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.HumanTaskDefinition;\nimport org.bonitasoft.web.rest.model.bpm.flownode.HumanTaskItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.IHumanTaskItem;\nimport org.bonitasoft.web.rest.server.datastore.bpm.flownode.HumanTaskDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.Datastore;\nimport org.bonitasoft.web.rest.server.framework.search.ISearchDirection;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIForbiddenException;\nimport org.bonitasoft.web.toolkit.client.common.util.StringUtil;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Séverin Moussel\n */\npublic class AbstractAPIHumanTask<ITEM extends IHumanTaskItem> extends AbstractAPITask<ITEM> {\n\n    @Override\n    protected HumanTaskDefinition defineItemDefinition() {\n        return HumanTaskDefinition.get();\n    }\n\n    @Override\n    protected Datastore defineDefaultDatastore() {\n        return new HumanTaskDatastore(getEngineSession());\n    }\n\n    @Override\n    public String defineDefaultSearchOrder() {\n        return HumanTaskItem.ATTRIBUTE_PRIORITY + ISearchDirection.SORT_ORDER_DESCENDING;\n    }\n\n    // // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // CRUDS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public ITEM update(final APIID id, final Map<String, String> attributes) {\n        // Cant unassigned a manual task\n        final String assignedUserId = attributes.get(HumanTaskItem.ATTRIBUTE_ASSIGNED_USER_ID);\n        if (assignedUserId != null && assignedUserId.length() > 0) {\n            final ITEM humanTask = get(id);\n            if (VALUE_TYPE_MANUAL_TASK.equals(humanTask.getType()) && StringUtil.isBlank(assignedUserId)) {\n                throw new APIForbiddenException(\"Can't unassigned a manual task.\");\n            }\n            if (humanTask.getAttributes().containsKey(HumanTaskItem.ATTRIBUTE_ASSIGNED_USER_ID)) {\n                if (humanTask.getAttributeValue(HumanTaskItem.ATTRIBUTE_ASSIGNED_USER_ID) != null\n                        && !humanTask.getAttributeValue(HumanTaskItem.ATTRIBUTE_ASSIGNED_USER_ID)\n                                .equals(assignedUserId)) {\n                    throw new APIForbiddenException(\"Can't assign this task because it has already been assigned.\");\n                }\n            }\n        }\n\n        return super.update(id, attributes);\n    }\n\n    // // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // UTILS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    protected List<String> defineReadOnlyAttributes() {\n        final List<String> attributes = super.defineReadOnlyAttributes();\n        attributes.add(HumanTaskItem.ATTRIBUTE_ACTOR_ID);\n        attributes.add(HumanTaskItem.ATTRIBUTE_ASSIGNED_DATE);\n        attributes.remove(FlowNodeItem.ATTRIBUTE_EXECUTED_BY_USER_ID);// allow user execute instead someone else\n        return attributes;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/AbstractAPITask.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode;\n\nimport org.bonitasoft.web.rest.model.bpm.flownode.ITaskItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.TaskDefinition;\nimport org.bonitasoft.web.rest.server.datastore.bpm.flownode.TaskDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.Datastore;\n\n/**\n * @author Séverin Moussel\n */\npublic class AbstractAPITask<ITEM extends ITaskItem> extends AbstractAPIActivity<ITEM> {\n\n    @Override\n    protected TaskDefinition defineItemDefinition() {\n        return TaskDefinition.get();\n    }\n\n    @Override\n    protected Datastore defineDefaultDatastore() {\n        return new TaskDatastore(getEngineSession());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/AbstractAPIUserTask.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode;\n\nimport org.bonitasoft.web.rest.model.bpm.flownode.IUserTaskItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.UserTaskDefinition;\nimport org.bonitasoft.web.rest.server.datastore.bpm.flownode.UserTaskDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.Datastore;\n\n/**\n * @author Séverin Moussel\n */\npublic class AbstractAPIUserTask<ITEM extends IUserTaskItem> extends AbstractAPIHumanTask<ITEM> {\n\n    @Override\n    protected UserTaskDefinition defineItemDefinition() {\n        return UserTaskDefinition.get();\n    }\n\n    @Override\n    protected Datastore defineDefaultDatastore() {\n        return new UserTaskDatastore(getEngineSession());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/ActivityVariableController.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode;\n\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.engine.bpm.data.DataInstance;\nimport org.bonitasoft.web.rest.server.api.AbstractRESTController;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/API/bpm/activityVariable/{activityId}/{dataName}\")\npublic class ActivityVariableController extends AbstractRESTController {\n\n    @GetMapping\n    public DataInstance getTaskVariable(@PathVariable long activityId,\n            @PathVariable String dataName,\n            HttpSession session) throws Exception {\n        return getProcessAPI(session).getActivityDataInstance(dataName, activityId);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/TimerEventTrigger.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode;\n\npublic record TimerEventTrigger(Long executionDate) {}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/TimerEventTriggerController.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode;\n\nimport static org.bonitasoft.web.rest.server.APIPaginationUtils.buildContentRange;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.engine.bpm.flownode.TimerEventTriggerInstance;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.web.rest.server.api.AbstractRESTController;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PutMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * Spring MVC Controller for Timer Event Triggers.\n *\n * @author Emmanuel Duchastenier\n */\n@RestController\n@RequestMapping(\"/API/bpm/timerEventTrigger\")\npublic class TimerEventTriggerController extends AbstractRESTController {\n\n    /**\n     * Search timer event triggers for a given case.\n     *\n     * @param caseId the case ID to search timers for\n     * @param page the page number (p parameter)\n     * @param count the number of results per page (c parameter)\n     * @param httpSession the HTTP session\n     * @return the list of timer event triggers with Content-Range header\n     * @throws BonitaException if an error occurs\n     */\n    @GetMapping\n    public ResponseEntity<List<TimerEventTriggerInstance>> searchTimerEventTriggers(\n            @RequestParam(value = \"caseId\") long caseId,\n            @RequestParam(value = \"p\", defaultValue = \"0\") int page,\n            @RequestParam(value = \"c\", defaultValue = \"10\") int count,\n            HttpSession httpSession) throws BonitaException {\n\n        final SearchResult<TimerEventTriggerInstance> searchResult = getProcessAPI(httpSession)\n                .searchTimerEventTriggerInstances(caseId, new SearchOptionsBuilder(page * count, count).done());\n\n        final List<TimerEventTriggerInstance> results = searchResult.getResult();\n\n        // Return 204 No Content for empty results\n        if (results.isEmpty()) {\n            return ResponseEntity.noContent()\n                    .header(HttpHeaders.CONTENT_RANGE, buildContentRange(0, 0, 0))\n                    .build();\n        }\n\n        return ResponseEntity.ok()\n                .header(HttpHeaders.CONTENT_RANGE,\n                        buildContentRange(page, results.size(), searchResult.getCount()))\n                .body(results);\n    }\n\n    /**\n     * Update the execution date of a timer event trigger.\n     *\n     * @param id the timer event trigger instance ID\n     * @param trigger the timer event trigger data containing the new execution date\n     * @param httpSession the HTTP session\n     * @return the updated timer event trigger\n     * @throws BonitaException if an error occurs\n     */\n    @PutMapping(\"/{id}\")\n    public TimerEventTrigger updateTimerEventTrigger(@PathVariable long id, @RequestBody TimerEventTrigger trigger,\n            HttpSession httpSession) throws BonitaException {\n        final Date executionDate = new Date(trigger.executionDate());\n        final Date updatedDate = getProcessAPI(httpSession).updateExecutionDateOfTimerEventTriggerInstance(id,\n                executionDate);\n        return new TimerEventTrigger(updatedDate.getTime());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/UserTaskContextController.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode;\n\nimport static org.bonitasoft.web.rest.server.api.AbstractRESTController.API_SPRING_INTERNAL;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.web.rest.server.api.AbstractRESTController;\nimport org.bonitasoft.web.rest.server.api.bdm.BusinessDataReferenceConverter;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/\" + API_SPRING_INTERNAL + \"/bpm/userTask/{taskId}/context\")\npublic class UserTaskContextController extends AbstractRESTController {\n\n    @GetMapping\n    public Map<String, Serializable> getUserTaskContext(@PathVariable long taskId, HttpSession session)\n            throws Exception {\n        final Map<String, Serializable> resultMap = new HashMap<>();\n\n        Map<String, Serializable> userTaskExecutionContext = getProcessAPI(session).getUserTaskExecutionContext(taskId);\n\n        for (Map.Entry<String, Serializable> entry : userTaskExecutionContext.entrySet()) {\n            resultMap.put(entry.getKey(), BusinessDataReferenceConverter.convertIfApplicable(entry.getValue()));\n        }\n        return resultMap;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/UserTaskContractController.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode;\n\nimport static org.bonitasoft.console.common.server.utils.ContractTypeConverter.ISO_8601_DATE_PATTERNS;\nimport static org.bonitasoft.web.rest.server.api.AbstractRESTController.API_SPRING_INTERNAL;\n\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.console.common.server.utils.ContractTypeConverter;\nimport org.bonitasoft.engine.bpm.contract.ContractDefinition;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.web.rest.server.api.AbstractRESTController;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/\" + API_SPRING_INTERNAL + \"/bpm/userTask/{taskId}/contract\")\npublic class UserTaskContractController extends AbstractRESTController {\n\n    protected final ContractTypeConverter typeConverterUtil = new ContractTypeConverter(ISO_8601_DATE_PATTERNS);\n\n    @GetMapping\n    public ResponseEntity<ContractDefinition> getContract(@PathVariable final long taskId, HttpSession session)\n            throws BonitaException {\n        ContractDefinition contract = getProcessAPI(session).getUserTaskContract(taskId);\n        ContractDefinition adaptedContract = typeConverterUtil.getAdaptedContractDefinition(contract);\n        if (adaptedContract == null) {\n            return ResponseEntity.noContent().build();\n        }\n        return ResponseEntity.ok(adaptedContract);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/UserTaskExecutionController.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode;\n\nimport static org.bonitasoft.web.rest.server.api.AbstractRESTController.API_SPRING_INTERNAL;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport javax.servlet.http.HttpSession;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.console.common.server.preferences.properties.PropertiesFactory;\nimport org.bonitasoft.console.common.server.utils.ContractTypeConverter;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.contract.ContractDefinition;\nimport org.bonitasoft.engine.bpm.contract.ContractViolationException;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeExecutionException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.web.rest.server.api.AbstractRESTController;\nimport org.bonitasoft.web.rest.server.api.resource.ErrorMessageWithExplanations;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseStatus;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * REST controller for user task execution.\n * Public URL: /API/bpm/userTask/{taskId}/execution\n * Internal path (via URL rewrite): /APISpringInternal/bpm/userTask/{taskId}/execution\n *\n * @author Emmanuel Duchastenier\n * @author Fabio Lombardi\n */\n@Slf4j\n@RestController\n@RequestMapping(\"/\" + API_SPRING_INTERNAL + \"/bpm/userTask/{taskId}/execution\")\npublic class UserTaskExecutionController extends AbstractRESTController {\n\n    protected ContractTypeConverter typeConverterUtil = new ContractTypeConverter(\n            ContractTypeConverter.ISO_8601_DATE_PATTERNS);\n\n    /**\n     * Executes a user task with contract inputs.\n     *\n     * @param taskId the ID of the user task to execute\n     * @param userId optional user ID to execute the task for (defaults to session user)\n     * @param assign whether to assign the task before execution\n     * @param inputs contract inputs for the task\n     * @param httpSession the HTTP session\n     * @throws BonitaException if an error occurs during execution\n     */\n    @PostMapping\n    @ResponseStatus(HttpStatus.NO_CONTENT)\n    public void executeTask(\n            @PathVariable final long taskId,\n            @RequestParam(name = \"user\", required = false) final Long userId,\n            @RequestParam(name = \"assign\", defaultValue = \"false\") final boolean assign,\n            @RequestBody(required = false) final Map<String, Serializable> inputs,\n            final HttpSession httpSession) throws BonitaException, java.io.FileNotFoundException {\n\n        final ProcessAPI processAPI = getProcessAPI(httpSession);\n        final long effectiveUserId = userId != null ? userId : getApiSession(httpSession).getUserId();\n\n        try {\n            final ContractDefinition taskContract = processAPI.getUserTaskContract(taskId);\n            final Map<String, Serializable> processedInputs = typeConverterUtil.getProcessedInput(taskContract, inputs,\n                    getMaxSize());\n\n            if (assign) {\n                processAPI.assignAndExecuteUserTask(effectiveUserId, taskId, processedInputs);\n            } else {\n                processAPI.executeUserTask(effectiveUserId, taskId, processedInputs);\n            }\n            typeConverterUtil.deleteTemporaryFiles(inputs);\n        } catch (final FlowNodeExecutionException e) {\n            String errorMessage = \"Unable to execute the task with ID \" + taskId;\n            log.error(\"{}. Caused by: {}\", errorMessage, e.getMessage());\n            // Avoid throwing original exception that may contain sensitive information unwanted in the HTTP response\n            throw new FlowNodeExecutionException(errorMessage + \" (consult the logs for more information).\");\n        } catch (final ContractViolationException e) {\n            logContractViolation(e);\n            throw e;\n        }\n    }\n\n    // Visible for testing\n    protected long getMaxSize() {\n        return PropertiesFactory.getConsoleProperties().getMaxSize();\n    }\n\n    private void logContractViolation(final ContractViolationException e) {\n        if (log.isInfoEnabled()) {\n            final StringBuilder explanations = new StringBuilder();\n            for (final String explanation : e.getExplanations()) {\n                explanations.append(explanation).append(\"\\n\");\n            }\n            log.info(\"{}\\nExplanations:\\n{}\", e.getSimpleMessage(), explanations);\n        }\n    }\n\n    @ExceptionHandler(ContractViolationException.class)\n    public ResponseEntity<ErrorMessageWithExplanations> handleContractViolation(ContractViolationException e) {\n        final ErrorMessageWithExplanations errorMessage = new ErrorMessageWithExplanations(e);\n        errorMessage.setMessage(e.getSimpleMessage());\n        errorMessage.setExplanations(e.getExplanations());\n        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorMessage);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/archive/APIArchivedActivity.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode.archive;\n\nimport java.util.List;\n\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedActivityDefinition;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedActivityItem;\nimport org.bonitasoft.web.rest.server.api.bpm.flownode.AbstractAPIActivity;\nimport org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.ArchivedActivityDatastore;\nimport org.bonitasoft.web.rest.server.framework.search.ISearchDirection;\n\n/**\n * @author Séverin Moussel\n */\npublic class APIArchivedActivity extends AbstractAPIActivity<ArchivedActivityItem> {\n\n    @Override\n    protected ArchivedActivityDefinition defineItemDefinition() {\n        return new ArchivedActivityDefinition();\n    }\n\n    @Override\n    protected ArchivedActivityDatastore defineDefaultDatastore() {\n        return new ArchivedActivityDatastore(getEngineSession(), ArchivedActivityDefinition.TOKEN);\n    }\n\n    @Override\n    protected List<String> defineReadOnlyAttributes() {\n        final List<String> attributes = super.defineReadOnlyAttributes();\n\n        attributes.add(ArchivedActivityItem.ATTRIBUTE_ARCHIVED_DATE);\n\n        return attributes;\n    }\n\n    @Override\n    public String defineDefaultSearchOrder() {\n        return ArchivedActivityItem.ATTRIBUTE_REACHED_STATE_DATE + ISearchDirection.SORT_ORDER_ASCENDING;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/archive/APIArchivedFlowNode.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode.archive;\n\nimport java.util.List;\n\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedFlowNodeDefinition;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedFlowNodeItem;\nimport org.bonitasoft.web.rest.server.api.bpm.flownode.AbstractAPIFlowNode;\nimport org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.ArchivedFlowNodeDatastore;\n\n/**\n * @author Séverin Moussel\n */\npublic class APIArchivedFlowNode extends AbstractAPIFlowNode<ArchivedFlowNodeItem> {\n\n    @Override\n    protected ArchivedFlowNodeDefinition defineItemDefinition() {\n        return new ArchivedFlowNodeDefinition();\n    }\n\n    @Override\n    protected ArchivedFlowNodeDatastore defineDefaultDatastore() {\n        return new ArchivedFlowNodeDatastore(getEngineSession(), ArchivedFlowNodeDefinition.TOKEN);\n    }\n\n    @Override\n    protected List<String> defineReadOnlyAttributes() {\n        final List<String> attributes = super.defineReadOnlyAttributes();\n\n        attributes.add(ArchivedFlowNodeItem.ATTRIBUTE_ARCHIVED_DATE);\n\n        return attributes;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/archive/APIArchivedHumanTask.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode.archive;\n\nimport java.util.List;\n\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedHumanTaskDefinition;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedHumanTaskItem;\nimport org.bonitasoft.web.rest.server.api.bpm.flownode.AbstractAPIHumanTask;\nimport org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.ArchivedHumanTaskDatastore;\n\n/**\n * @author Séverin Moussel\n */\npublic class APIArchivedHumanTask extends AbstractAPIHumanTask<ArchivedHumanTaskItem> {\n\n    @Override\n    protected ArchivedHumanTaskDefinition defineItemDefinition() {\n        return new ArchivedHumanTaskDefinition();\n    }\n\n    @Override\n    protected ArchivedHumanTaskDatastore defineDefaultDatastore() {\n        return new ArchivedHumanTaskDatastore(getEngineSession(), ArchivedHumanTaskDefinition.TOKEN);\n    }\n\n    @Override\n    protected List<String> defineReadOnlyAttributes() {\n        final List<String> attributes = super.defineReadOnlyAttributes();\n\n        attributes.add(ArchivedHumanTaskItem.ATTRIBUTE_ARCHIVED_DATE);\n\n        return attributes;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/archive/APIArchivedTask.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode.archive;\n\nimport java.util.List;\n\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedTaskDefinition;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedTaskItem;\nimport org.bonitasoft.web.rest.server.api.bpm.flownode.AbstractAPITask;\nimport org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.ArchivedTaskDatastore;\n\n/**\n * @author Séverin Moussel\n */\npublic class APIArchivedTask extends AbstractAPITask<ArchivedTaskItem> {\n\n    @Override\n    protected ArchivedTaskDefinition defineItemDefinition() {\n        return new ArchivedTaskDefinition();\n    }\n\n    @Override\n    protected ArchivedTaskDatastore defineDefaultDatastore() {\n        return new ArchivedTaskDatastore(getEngineSession(), ArchivedTaskDefinition.TOKEN);\n    }\n\n    @Override\n    protected List<String> defineReadOnlyAttributes() {\n        final List<String> attributes = super.defineReadOnlyAttributes();\n\n        attributes.add(ArchivedTaskItem.ATTRIBUTE_ARCHIVED_DATE);\n\n        return attributes;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/archive/APIArchivedUserTask.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode.archive;\n\nimport java.util.List;\n\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedUserTaskDefinition;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedUserTaskItem;\nimport org.bonitasoft.web.rest.server.api.bpm.flownode.AbstractAPIUserTask;\nimport org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.ArchivedUserTaskDatastore;\n\n/**\n * @author Séverin Moussel\n */\npublic class APIArchivedUserTask extends AbstractAPIUserTask<ArchivedUserTaskItem> {\n\n    @Override\n    protected ArchivedUserTaskDefinition defineItemDefinition() {\n        return new ArchivedUserTaskDefinition();\n    }\n\n    @Override\n    protected ArchivedUserTaskDatastore defineDefaultDatastore() {\n        return new ArchivedUserTaskDatastore(getEngineSession(), ArchivedUserTaskDefinition.TOKEN);\n    }\n\n    @Override\n    protected List<String> defineReadOnlyAttributes() {\n        final List<String> attributes = super.defineReadOnlyAttributes();\n\n        attributes.add(ArchivedUserTaskItem.ATTRIBUTE_ARCHIVED_DATE);\n\n        return attributes;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/archive/ArchivedActivityVariableController.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode.archive;\n\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.web.rest.model.bpm.cases.ArchivedActivityVariable;\nimport org.bonitasoft.web.rest.server.api.AbstractRESTController;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/API/bpm/archivedActivityVariable/{activityId}/{variableName}\")\npublic class ArchivedActivityVariableController extends AbstractRESTController {\n\n    @GetMapping\n    public ArchivedActivityVariable getArchivedActivityVariable(@PathVariable long activityId,\n            @PathVariable String variableName,\n            HttpSession session) throws Exception {\n        return ArchivedActivityVariable\n                .create(getProcessAPI(session).getArchivedActivityDataInstance(variableName, activityId));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/archive/ArchivedUserTaskContextController.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode.archive;\n\nimport static org.bonitasoft.web.rest.server.api.AbstractRESTController.API_SPRING_INTERNAL;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.web.rest.server.api.AbstractRESTController;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/\" + API_SPRING_INTERNAL + \"/bpm/archivedUserTask/{archivedTaskId}/context\")\npublic class ArchivedUserTaskContextController extends AbstractRESTController {\n\n    @GetMapping\n    public Map<String, Serializable> getArchivedUserTaskContext(@PathVariable long archivedTaskId, HttpSession session)\n            throws Exception {\n        return getProcessAPI(session).getArchivedUserTaskExecutionContext(archivedTaskId);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/message/BPMMessage.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.message;\n\nimport java.util.Map;\n\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\n\n@JsonIgnoreProperties(ignoreUnknown = true)\npublic record BPMMessage(\n        String messageName,\n        String targetProcess,\n        String targetFlowNode,\n        Map<String, BPMMessageValue> messageContent,\n        Map<String, BPMMessageValue> correlations) {\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/message/BPMMessageController.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.message;\n\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.OffsetDateTime;\nimport java.time.format.DateTimeParseException;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Set;\n\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.engine.bpm.flownode.SendEventException;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.expression.ExpressionType;\nimport org.bonitasoft.engine.expression.InvalidExpressionException;\nimport org.bonitasoft.web.rest.server.api.AbstractRESTController;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseStatus;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/API/bpm/message\")\npublic class BPMMessageController extends AbstractRESTController {\n\n    private static final Set<String> SUPPORTED_TYPES = new HashSet<>();\n    static {\n        SUPPORTED_TYPES.add(String.class.getName());\n        SUPPORTED_TYPES.add(Integer.class.getName());\n        SUPPORTED_TYPES.add(Long.class.getName());\n        SUPPORTED_TYPES.add(Float.class.getName());\n        SUPPORTED_TYPES.add(Double.class.getName());\n        SUPPORTED_TYPES.add(Boolean.class.getName());\n        SUPPORTED_TYPES.add(Date.class.getName());\n        SUPPORTED_TYPES.add(LocalDate.class.getName());\n        SUPPORTED_TYPES.add(LocalDateTime.class.getName());\n        SUPPORTED_TYPES.add(OffsetDateTime.class.getName());\n    }\n\n    @PostMapping\n    @ResponseStatus(HttpStatus.NO_CONTENT)\n    public void sendMessage(@RequestBody BPMMessage message, HttpSession httpSession)\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException,\n            SendEventException, InvalidExpressionException {\n        validateMandatoryAttributes(message);\n\n        Map<Expression, Expression> msgContent = new HashMap<>();\n        if (message.messageContent() != null) {\n            for (Map.Entry<String, BPMMessageValue> entry : message.messageContent().entrySet()) {\n                msgContent.put(new ExpressionBuilder().createConstantStringExpression(entry.getKey()),\n                        getExpressionFromObject(entry));\n            }\n        }\n        Map<Expression, Expression> correlations = new HashMap<>();\n        if (message.correlations() != null) {\n            int nbCorrelations = message.correlations().size();\n            if (nbCorrelations > 5) {\n                throw new IllegalArgumentException(\n                        String.format(\"A maximum of 5 correlations is supported. %s found.\", nbCorrelations));\n            }\n            for (Map.Entry<String, BPMMessageValue> entry : message.correlations().entrySet()) {\n                correlations.put(new ExpressionBuilder().createConstantStringExpression(entry.getKey()),\n                        getExpressionFromObject(entry));\n            }\n        }\n\n        Expression targetFlowNodeExpression = null;\n        if (message.targetFlowNode() != null) {\n            targetFlowNodeExpression = new ExpressionBuilder()\n                    .createConstantStringExpression(message.targetFlowNode());\n        }\n        getProcessAPI(httpSession).sendMessage(message.messageName(),\n                new ExpressionBuilder().createConstantStringExpression(message.targetProcess()),\n                targetFlowNodeExpression,\n                msgContent,\n                correlations);\n    }\n\n    private Expression getExpressionFromObject(Entry<String, BPMMessageValue> entry)\n            throws InvalidExpressionException {\n        BPMMessageValue messageValue = entry.getValue();\n        if (messageValue == null) {\n            throw new IllegalArgumentException(String.format(\"%s value cannot be null.\", entry.getKey()));\n        }\n        Object value = messageValue.getValue();\n        if (value == null) {\n            throw new IllegalArgumentException(String.format(\"%s value cannot be null.\", entry.getKey()));\n        }\n        String type = valueType(messageValue.getType(), value);\n        if (!isSupportedType(type)) {\n            throw new InvalidExpressionException(\n                    String.format(\n                            \"BPM send message: unsupported value type '%s' for key '%s'. Only primitive types are supported.\",\n                            messageValue.getType(),\n                            entry.getKey()));\n        }\n        String stringValue = String.valueOf(value);\n        String expressionName = stringValue.trim().isEmpty() ? \"empty-value\" : stringValue;\n        return new ExpressionBuilder().createExpression(expressionName,\n                stringValue, valueType(type, value), ExpressionType.TYPE_CONSTANT);\n    }\n\n    private String valueType(String type, Object value) {\n        if (type != null) {\n            return type;\n        }\n        return guessType(value);\n    }\n\n    private String guessType(Object value) {\n        if (value instanceof String) {\n            try {\n                LocalDate.parse((String) value);\n                return LocalDate.class.getName();\n            } catch (DateTimeParseException e) {\n                //Ignore\n            }\n            try {\n                LocalDateTime.parse((String) value);\n                return LocalDateTime.class.getName();\n            } catch (DateTimeParseException e) {\n                //Ignore\n            }\n            try {\n                OffsetDateTime.parse((String) value);\n                return OffsetDateTime.class.getName();\n            } catch (DateTimeParseException e) {\n                //Ignore\n            }\n            return String.class.getName();\n        } else if (value instanceof Long) {\n            return Long.class.getName();\n        } else if (value instanceof Double) {\n            return Double.class.getName();\n        } else if (value instanceof Float) {\n            return Float.class.getName();\n        } else if (value instanceof Integer) {\n            return Integer.class.getName();\n        } else if (value instanceof Boolean) {\n            return Boolean.class.getName();\n        }\n        return null;\n    }\n\n    private boolean isSupportedType(String type) {\n        return SUPPORTED_TYPES.contains(type);\n    }\n\n    private void validateMandatoryAttributes(BPMMessage message) {\n        if (message.messageName() == null) {\n            throw new IllegalArgumentException(\"'messageName' attribute is mandatory\");\n        }\n        if (message.targetProcess() == null) {\n            throw new IllegalArgumentException(\"'targetProcess' attribute is mandatory\");\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/message/BPMMessageValue.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.message;\n\nimport com.fasterxml.jackson.annotation.JsonInclude;\nimport com.fasterxml.jackson.annotation.JsonInclude.Include;\n\npublic class BPMMessageValue {\n\n    @JsonInclude(Include.NON_EMPTY)\n    private String type;\n    private Object value;\n\n    public String getType() {\n        return type;\n    }\n\n    public void setType(String type) {\n        this.type = type;\n    }\n\n    public Object getValue() {\n        return value;\n    }\n\n    public void setValue(Object value) {\n        this.value = value;\n    }\n\n    public static BPMMessageValue create(Object value, String type) {\n        BPMMessageValue res = new BPMMessageValue();\n        res.setValue(value);\n        res.setType(type);\n        return res;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/process/APIActor.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.process;\n\nimport static org.bonitasoft.web.rest.model.bpm.process.ActorItem.ATTRIBUTE_PROCESS_ID;\nimport static org.bonitasoft.web.toolkit.client.data.item.template.ItemHasDualName.ATTRIBUTE_NAME;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.actor.ActorCriterion;\nimport org.bonitasoft.web.rest.model.bpm.process.ActorDefinition;\nimport org.bonitasoft.web.rest.model.bpm.process.ActorItem;\nimport org.bonitasoft.web.rest.server.api.ConsoleAPI;\nimport org.bonitasoft.web.rest.server.datastore.bpm.process.ActorDatastore;\nimport org.bonitasoft.web.rest.server.datastore.bpm.process.ProcessDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasGet;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasSearch;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasUpdate;\nimport org.bonitasoft.web.rest.server.framework.api.Datastore;\nimport org.bonitasoft.web.rest.server.framework.exception.APIFilterMandatoryException;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.util.MapUtil;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Julien Mege\n * @author Séverin Moussel\n */\npublic class APIActor extends ConsoleAPI<ActorItem> implements\n        APIHasGet<ActorItem>,\n        APIHasSearch<ActorItem>,\n        APIHasUpdate<ActorItem> {\n\n    @Override\n    protected ItemDefinition defineItemDefinition() {\n        return ActorDefinition.get();\n    }\n\n    @Override\n    protected Datastore defineDefaultDatastore() {\n        return new ActorDatastore(getEngineSession());\n    }\n\n    @Override\n    protected List<String> defineReadOnlyAttributes() {\n        return Arrays.asList(ATTRIBUTE_PROCESS_ID, ATTRIBUTE_NAME);\n    }\n\n    @Override\n    public String defineDefaultSearchOrder() {\n        return ActorCriterion.NAME_ASC.name();\n    }\n\n    @Override\n    public ItemSearchResult<ActorItem> search(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n\n        // Process id is a mandatory filter\n        if (MapUtil.isBlank(filters, ActorItem.ATTRIBUTE_PROCESS_ID)) {\n            throw new APIFilterMandatoryException(ActorItem.ATTRIBUTE_PROCESS_ID);\n        }\n\n        return super.search(page, resultsByPage, search, orders, filters);\n    }\n\n    @Override\n    protected void fillDeploys(final ActorItem item, final List<String> deploys) {\n        if (isDeployable(ATTRIBUTE_PROCESS_ID, deploys, item)) {\n            item.setDeploy(ATTRIBUTE_PROCESS_ID, new ProcessDatastore(getEngineSession()).get(item.getProcessId()));\n        }\n    }\n\n    @Override\n    protected void fillCounters(final ActorItem item, final List<String> counters) {\n        final ActorDatastore actorDatastore = (ActorDatastore) getDefaultDatastore();\n\n        if (counters.contains(ActorItem.COUNTER_USERS)) {\n            item.setAttribute(ActorItem.COUNTER_USERS, actorDatastore.countUsers(item.getId()));\n        }\n        if (counters.contains(ActorItem.COUNTER_GROUPS)) {\n            item.setAttribute(ActorItem.COUNTER_GROUPS, actorDatastore.countGroups(item.getId()));\n        }\n        if (counters.contains(ActorItem.COUNTER_ROLES)) {\n            item.setAttribute(ActorItem.COUNTER_ROLES, actorDatastore.countRoles(item.getId()));\n        }\n        if (counters.contains(ActorItem.COUNTER_MEMBERSHIPS)) {\n            item.setAttribute(ActorItem.COUNTER_MEMBERSHIPS, actorDatastore.countMemberships(item.getId()));\n        }\n\n    }\n\n    /**\n     * @deprecated as of 9.0.0, Actor should be updated at startup.\n     */\n    @Override\n    @Deprecated(since = \"9.0.0\")\n    public ActorItem update(APIID id, Map<String, String> attributes) {\n        return super.update(id, attributes);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/process/APIActorMember.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.process;\n\nimport static org.bonitasoft.web.rest.model.bpm.process.ActorMemberItem.ATTRIBUTE_ACTOR_ID;\nimport static org.bonitasoft.web.rest.model.portal.profile.AbstractMemberItem.FILTER_MEMBER_TYPE;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.web.rest.model.bpm.process.ActorMemberDefinition;\nimport org.bonitasoft.web.rest.model.bpm.process.ActorMemberItem;\nimport org.bonitasoft.web.rest.server.api.profile.AbstractAPIMember;\nimport org.bonitasoft.web.rest.server.datastore.bpm.process.ActorDatastore;\nimport org.bonitasoft.web.rest.server.datastore.bpm.process.ActorMemberDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.Datastore;\nimport org.bonitasoft.web.rest.server.framework.exception.APIFilterEmptyException;\nimport org.bonitasoft.web.rest.server.framework.exception.APIFilterMandatoryException;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.util.MapUtil;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Julien Mege\n * @author Séverin Moussel\n */\npublic class APIActorMember extends AbstractAPIMember<ActorMemberItem> {\n\n    @Override\n    protected ItemDefinition defineItemDefinition() {\n        return ActorMemberDefinition.get();\n    }\n\n    @Override\n    public String defineDefaultSearchOrder() {\n        return \"\";\n    }\n\n    @Override\n    protected Datastore defineDefaultDatastore() {\n        return new ActorMemberDatastore(getEngineSession());\n    }\n\n    @Override\n    public ItemSearchResult<ActorMemberItem> search(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n        if (MapUtil.isBlank(filters, ATTRIBUTE_ACTOR_ID)) {\n            throw new APIFilterMandatoryException(ATTRIBUTE_ACTOR_ID);\n        }\n\n        if (filters.containsKey(FILTER_MEMBER_TYPE) && MapUtil.isBlank(filters, FILTER_MEMBER_TYPE)) {\n            throw new APIFilterEmptyException(FILTER_MEMBER_TYPE);\n        }\n\n        return super.search(page, resultsByPage, search, orders, filters);\n    }\n\n    @Override\n    protected void fillDeploys(final ActorMemberItem item, final List<String> deploys) {\n        if (isDeployable(ActorMemberItem.ATTRIBUTE_ACTOR_ID, deploys, item)) {\n            item.setDeploy(ActorMemberItem.ATTRIBUTE_ACTOR_ID,\n                    new ActorDatastore(getEngineSession()).get(item.getActorId()));\n        }\n\n        super.fillDeploys(item, deploys);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/process/APICategory.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.process;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.category.CategoryCriterion;\nimport org.bonitasoft.web.rest.model.bpm.process.CategoryDefinition;\nimport org.bonitasoft.web.rest.model.bpm.process.CategoryItem;\nimport org.bonitasoft.web.rest.server.api.ConsoleAPI;\nimport org.bonitasoft.web.rest.server.datastore.bpm.process.CategoryDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasAdd;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasDelete;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasGet;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasSearch;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasUpdate;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Nicolas Tith\n */\npublic class APICategory extends ConsoleAPI<CategoryItem>\n        implements APIHasAdd<CategoryItem>, APIHasDelete, APIHasGet<CategoryItem>,\n        APIHasSearch<CategoryItem>, APIHasUpdate<CategoryItem> {\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // CONFIGURE\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    protected ItemDefinition defineItemDefinition() {\n        return Definitions.get(CategoryDefinition.TOKEN);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // C.R.U.D.S\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public CategoryItem add(final CategoryItem item) {\n        return new CategoryDatastore(getEngineSession()).add(item);\n    }\n\n    @Override\n    public CategoryItem update(final APIID id, final Map<String, String> attributes) {\n        return new CategoryDatastore(getEngineSession()).update(id, attributes);\n    }\n\n    @Override\n    public CategoryItem get(final APIID id) {\n        return new CategoryDatastore(getEngineSession()).get(id);\n    }\n\n    @Override\n    public String defineDefaultSearchOrder() {\n        return CategoryCriterion.NAME_ASC.toString();\n    }\n\n    @Override\n    public ItemSearchResult<CategoryItem> search(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n        return new CategoryDatastore(getEngineSession()).search(page, resultsByPage, search, orders, filters);\n    }\n\n    @Override\n    public void delete(final List<APIID> ids) {\n        new CategoryDatastore(getEngineSession()).delete(ids);\n    }\n\n    @Override\n    protected void fillDeploys(final CategoryItem item, final List<String> deploys) {\n    }\n\n    @Override\n    protected void fillCounters(final CategoryItem item, final List<String> counters) {\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/process/APIProcess.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.process;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseItem;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessDefinition;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessItem;\nimport org.bonitasoft.web.rest.server.api.ConsoleAPI;\nimport org.bonitasoft.web.rest.server.api.deployer.UserDeployer;\nimport org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore;\nimport org.bonitasoft.web.rest.server.datastore.bpm.process.ProcessDatastore;\nimport org.bonitasoft.web.rest.server.datastore.organization.UserDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasAdd;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasDelete;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasGet;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasSearch;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasUpdate;\nimport org.bonitasoft.web.rest.server.framework.api.Datastore;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Nicolas Tith\n * @author Celine Souchet\n */\npublic class APIProcess extends ConsoleAPI<ProcessItem> implements\n        APIHasAdd<ProcessItem>,\n        APIHasUpdate<ProcessItem>,\n        APIHasGet<ProcessItem>,\n        APIHasSearch<ProcessItem>,\n        APIHasDelete {\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // CONFIGURE\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    protected ItemDefinition<ProcessItem> defineItemDefinition() {\n        return ProcessDefinition.get();\n    }\n\n    @Override\n    public String defineDefaultSearchOrder() {\n        return ProcessItem.ATTRIBUTE_NAME + \" ASC\";\n    }\n\n    @Override\n    protected Datastore defineDefaultDatastore() {\n        return new ProcessDatastore(getEngineSession());\n    }\n\n    /**\n     * @deprecated as of 9.0.0, Process should be created at startup.\n     */\n    @Override\n    @Deprecated(since = \"9.0.0\")\n    public ProcessItem add(final ProcessItem item) {\n        return getProcessDatastore().add(item);\n    }\n\n    /**\n     * @deprecated as of 9.0.0, Process should be updated at startup.\n     */\n    @Override\n    @Deprecated(since = \"9.0.0\")\n    public ProcessItem update(final APIID id, final Map<String, String> attributes) {\n        return getProcessDatastore().update(id, attributes);\n    }\n\n    @Override\n    public ProcessItem get(final APIID id) {\n        return getProcessDatastore().get(id);\n    }\n\n    @Override\n    public ItemSearchResult<ProcessItem> search(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n\n        if (filters.containsKey(ProcessItem.FILTER_TEAM_MANAGER_ID)\n                && filters.containsKey(ProcessItem.FILTER_SUPERVISOR_ID)) {\n            throw new APIException(\n                    \"Can't set those filters at the same time : \" + ProcessItem.FILTER_TEAM_MANAGER_ID + \" and \"\n                            + ProcessItem.FILTER_SUPERVISOR_ID);\n        }\n\n        return getProcessDatastore().search(page, resultsByPage, search, orders, filters);\n    }\n\n    @Override\n    public void delete(final List<APIID> ids) {\n        getProcessDatastore().delete(ids);\n    }\n\n    @Override\n    protected void fillDeploys(final ProcessItem item, final List<String> deploys) {\n        addDeployer(new UserDeployer(\n                new UserDatastore(getEngineSession()), ProcessItem.ATTRIBUTE_DEPLOYED_BY_USER_ID));\n        super.fillDeploys(item, deploys);\n    }\n\n    @Override\n    protected void fillCounters(final ProcessItem item, final List<String> counters) {\n        fillNumberOfFailedCasesIfFailedCounterExists(item, counters);\n        fillNumberOfOpenCasesIfOpenCounterExists(item, counters);\n    }\n\n    private void fillNumberOfFailedCasesIfFailedCounterExists(final ProcessItem item, final List<String> counters) {\n        if (counters.contains(ProcessItem.COUNTER_FAILED_CASES)) {\n            final Map<String, String> filters = new HashMap<>();\n            filters.put(CaseItem.FILTER_CALLER, \"any\");\n            filters.put(CaseItem.ATTRIBUTE_PROCESS_ID, item.getId().toString());\n            filters.put(CaseItem.FILTER_STATE, ProcessInstanceState.ERROR.name());\n            item.setAttribute(ProcessItem.COUNTER_FAILED_CASES, getCaseDatastore().count(null, null, filters));\n        }\n    }\n\n    private void fillNumberOfOpenCasesIfOpenCounterExists(final ProcessItem item, final List<String> counters) {\n        if (counters.contains(ProcessItem.COUNTER_OPEN_CASES)) {\n            // Open is all states without the terminal states\n            final Map<String, String> filters = new HashMap<>();\n            filters.put(CaseItem.FILTER_CALLER, \"any\");\n            filters.put(CaseItem.ATTRIBUTE_PROCESS_ID, item.getId().toString());\n            item.setAttribute(ProcessItem.COUNTER_OPEN_CASES, getCaseDatastore().count(null, null, filters));\n        }\n    }\n\n    protected ProcessDatastore getProcessDatastore() {\n        return new ProcessDatastore(getEngineSession());\n    }\n\n    protected CaseDatastore getCaseDatastore() {\n        return new CaseDatastore(getEngineSession());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/process/APIProcessCategory.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.process;\n\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessCategoryDefinition;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessCategoryItem;\nimport org.bonitasoft.web.rest.server.api.ConsoleAPI;\nimport org.bonitasoft.web.rest.server.datastore.bpm.process.ProcessCategoryDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasAdd;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasDelete;\nimport org.bonitasoft.web.rest.server.framework.api.Datastore;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Séverin Moussel\n */\npublic class APIProcessCategory extends ConsoleAPI<ProcessCategoryItem> implements\n        APIHasAdd<ProcessCategoryItem>,\n        APIHasDelete {\n\n    @Override\n    protected ItemDefinition defineItemDefinition() {\n        return ProcessCategoryDefinition.get();\n    }\n\n    @Override\n    protected Datastore defineDefaultDatastore() {\n        return new ProcessCategoryDatastore(getEngineSession());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/process/APIProcessConnector.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.process;\n\nimport static org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorItem.ATTRIBUTE_PROCESS_ID;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorCriterion;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDefinition;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorItem;\nimport org.bonitasoft.web.rest.server.api.ConsoleAPI;\nimport org.bonitasoft.web.rest.server.datastore.bpm.process.ProcessConnectorDatastore;\nimport org.bonitasoft.web.rest.server.datastore.bpm.process.ProcessDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasGet;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasSearch;\nimport org.bonitasoft.web.rest.server.framework.api.Datastore;\nimport org.bonitasoft.web.rest.server.framework.exception.APIFilterMandatoryException;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.util.MapUtil;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Séverin Moussel\n */\npublic class APIProcessConnector extends ConsoleAPI<ProcessConnectorItem> implements\n        APIHasGet<ProcessConnectorItem>,\n        APIHasSearch<ProcessConnectorItem> {\n\n    @Override\n    protected ItemDefinition defineItemDefinition() {\n        return ProcessConnectorDefinition.get();\n    }\n\n    @Override\n    protected Datastore defineDefaultDatastore() {\n        return new ProcessConnectorDatastore(getEngineSession());\n    }\n\n    @Override\n    public String defineDefaultSearchOrder() {\n        return ConnectorCriterion.DEFINITION_ID_ASC.name();\n    }\n\n    @Override\n    public ItemSearchResult<ProcessConnectorItem> search(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n\n        if (MapUtil.isBlank(filters, ATTRIBUTE_PROCESS_ID)) {\n            throw new APIFilterMandatoryException(ATTRIBUTE_PROCESS_ID);\n        }\n\n        return super.search(page, resultsByPage, search, orders, filters);\n    }\n\n    @Override\n    protected void fillDeploys(final ProcessConnectorItem item, final List<String> deploys) {\n\n        if (isDeployable(ATTRIBUTE_PROCESS_ID, deploys, item)) {\n            item.setDeploy(ATTRIBUTE_PROCESS_ID,\n                    new ProcessDatastore(getEngineSession()).get(item.getProcessId()));\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/process/APIProcessConnectorDependency.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.process;\n\nimport static org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDependencyItem.ATTRIBUTE_CONNECTOR_NAME;\nimport static org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDependencyItem.ATTRIBUTE_CONNECTOR_VERSION;\nimport static org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDependencyItem.ATTRIBUTE_PROCESS_ID;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorCriterion;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDependencyDefinition;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDependencyItem;\nimport org.bonitasoft.web.rest.server.api.ConsoleAPI;\nimport org.bonitasoft.web.rest.server.datastore.bpm.process.ProcessConnectorDependencyDatastore;\nimport org.bonitasoft.web.rest.server.datastore.bpm.process.ProcessDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasSearch;\nimport org.bonitasoft.web.rest.server.framework.exception.APIFilterMandatoryException;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.util.MapUtil;\n\n/**\n * @author Séverin Moussel\n */\npublic class APIProcessConnectorDependency extends ConsoleAPI<ProcessConnectorDependencyItem> implements\n        APIHasSearch<ProcessConnectorDependencyItem> {\n\n    @Override\n    protected ProcessConnectorDependencyDefinition defineItemDefinition() {\n        return ProcessConnectorDependencyDefinition.get();\n    }\n\n    @Override\n    public String defineDefaultSearchOrder() {\n        return ConnectorCriterion.DEFINITION_ID_ASC.name();\n    }\n\n    @Override\n    public ItemSearchResult<ProcessConnectorDependencyItem> search(final int page, final int resultsByPage,\n            final String search, final String orders,\n            final Map<String, String> filters) {\n\n        checkMandatoryAttributes(filters);\n        return super.search(page, resultsByPage, search, orders, filters);\n    }\n\n    protected void checkMandatoryAttributes(final Map<String, String> filters) {\n        if (MapUtil.isBlank(filters, ATTRIBUTE_PROCESS_ID)) {\n            throw new APIFilterMandatoryException(ATTRIBUTE_PROCESS_ID);\n        }\n        if (MapUtil.isBlank(filters, ATTRIBUTE_CONNECTOR_NAME)) {\n            throw new APIFilterMandatoryException(ATTRIBUTE_CONNECTOR_NAME);\n        }\n        if (MapUtil.isBlank(filters, ATTRIBUTE_CONNECTOR_VERSION)) {\n            throw new APIFilterMandatoryException(ATTRIBUTE_CONNECTOR_VERSION);\n        }\n    }\n\n    @Override\n    protected ProcessConnectorDependencyDatastore defineDefaultDatastore() {\n        return new ProcessConnectorDependencyDatastore(getEngineSession());\n    }\n\n    @Override\n    protected void fillDeploys(final ProcessConnectorDependencyItem item, final List<String> deploys) {\n        if (isDeployable(ATTRIBUTE_PROCESS_ID, deploys, item)) {\n            item.setDeploy(ATTRIBUTE_PROCESS_ID, new ProcessDatastore(getEngineSession()).get(item.getProcessId()));\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/process/APIProcessParameter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.process;\n\nimport static org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil.computeIndex;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.parameter.ParameterCriterion;\nimport org.bonitasoft.engine.bpm.parameter.ParameterInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessParameterDefinition;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessParameterItem;\nimport org.bonitasoft.web.rest.server.api.ConsoleAPI;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Nicolas Tith\n */\npublic class APIProcessParameter extends ConsoleAPI<ProcessParameterItem> {\n\n    @Override\n    protected ItemDefinition defineItemDefinition() {\n        return Definitions.get(ProcessParameterDefinition.TOKEN);\n    }\n\n    protected ProcessAPI getProcessAPI()\n            throws InvalidSessionException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return TenantAPIAccessor.getProcessAPI(getEngineSession());\n    }\n\n    @Override\n    /*\n     * id is a composite id which contain processId and parameterName\n     */\n    public ProcessParameterItem get(final APIID id) {\n        final List<String> ids = id.getIds();\n        final String processId = ids.get(0);\n        final String parameterName = ids.get(1);\n        try {\n            final ProcessAPI processAPI = getProcessAPI();\n            final Long lProcessid = Long.valueOf(processId);\n            final ParameterInstance parameterInstance = processAPI.getParameterInstance(lProcessid, parameterName);\n            final ProcessDefinition processDef = processAPI.getProcessDefinition(lProcessid);\n            final ProcessDeploymentInfo processDeploy = processAPI.getProcessDeploymentInfo(lProcessid);\n\n            if (parameterInstance == null) {\n                throw new APIItemNotFoundException(\"parameter\",\n                        APIID.makeAPIID(String.valueOf(lProcessid), parameterName));\n            }\n\n            if (processDef == null) {\n                throw new APIItemNotFoundException(\"process\", APIID.makeAPIID(String.valueOf(lProcessid)));\n            }\n\n            final String paramValue = parameterInstance.getValue() == null ? \"\"\n                    : parameterInstance.getValue().toString();\n            return new ProcessParameterItem(processId, parameterInstance.getName(), parameterInstance.getType(),\n                    paramValue, parameterInstance.getDescription(), processDeploy.getDisplayName(),\n                    processDef.getVersion());\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @Override\n    public String defineDefaultSearchOrder() {\n        return ParameterCriterion.NAME_ASC.toString();\n    }\n\n    @Override\n    public ItemSearchResult<ProcessParameterItem> search(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n        final List<ProcessParameterItem> items = new ArrayList<>();\n        List<ParameterInstance> parameters = new ArrayList<>();\n        int parametersCount = 0;\n        long processId = -1;\n\n        try {\n            final ProcessAPI processAPI = getProcessAPI();\n            if (filters != null) {\n                if (filters.containsKey(ProcessParameterItem.FILTER_PROCESS_ID)) {\n                    final String value = filters.get(ProcessParameterItem.FILTER_PROCESS_ID);\n                    processId = Long.parseLong(value);\n                }\n            }\n            if (processId != -1) {\n                parameters = processAPI.getParameterInstances(processId, computeIndex(page, resultsByPage),\n                        resultsByPage, ParameterCriterion.valueOf(orders.toUpperCase().replace(\" \", \"_\")));\n                parametersCount = processAPI.getNumberOfParameterInstances(processId);\n            }\n            for (final ParameterInstance p : parameters) {\n                final String paramValue = p.getValue() == null ? \"\" : p.getValue().toString();\n                items.add(new ProcessParameterItem(String.valueOf(processId), p.getName(), p.getType(), paramValue,\n                        p.getDescription(), \"\", \"\"));\n            }\n            return new ItemSearchResult<>(page, resultsByPage, parametersCount, items);\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @Override\n    protected void fillDeploys(final ProcessParameterItem item, final List<String> deploys) {\n    }\n\n    @Override\n    protected void fillCounters(final ProcessParameterItem item, final List<String> counters) {\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/process/APIProcessResolutionProblem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.process;\n\nimport java.util.Map;\n\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessResolutionProblemDefinition;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessResolutionProblemItem;\nimport org.bonitasoft.web.rest.server.api.ConsoleAPI;\nimport org.bonitasoft.web.rest.server.datastore.bpm.process.ProcessResolutionProblemDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasSearch;\nimport org.bonitasoft.web.rest.server.framework.api.Datastore;\nimport org.bonitasoft.web.rest.server.framework.exception.APIFilterMandatoryException;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Séverin Moussel\n */\npublic class APIProcessResolutionProblem extends ConsoleAPI<ProcessResolutionProblemItem>\n        implements APIHasSearch<ProcessResolutionProblemItem> {\n\n    @Override\n    protected ItemDefinition defineItemDefinition() {\n        return Definitions.get(ProcessResolutionProblemDefinition.TOKEN);\n    }\n\n    @Override\n    protected Datastore defineDefaultDatastore() {\n        return new ProcessResolutionProblemDatastore(getEngineSession());\n    }\n\n    @Override\n    public String defineDefaultSearchOrder() {\n        return ProcessResolutionProblemItem.ATTRIBUTE_TARGET_TYPE;\n    }\n\n    @Override\n    public ItemSearchResult<ProcessResolutionProblemItem> search(final int page, final int resultsByPage,\n            final String search, final String orders,\n            final Map<String, String> filters) {\n\n        if (!filters.containsKey(ProcessResolutionProblemItem.FILTER_PROCESS_ID)) {\n            throw new APIFilterMandatoryException(ProcessResolutionProblemItem.FILTER_PROCESS_ID);\n        }\n\n        return super.search(page, resultsByPage, search, orders, filters);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/process/ProcessContractController.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.process;\n\nimport static org.bonitasoft.console.common.server.utils.ContractTypeConverter.ISO_8601_DATE_PATTERNS;\nimport static org.bonitasoft.web.rest.server.api.AbstractRESTController.API_SPRING_INTERNAL;\n\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.console.common.server.utils.ContractTypeConverter;\nimport org.bonitasoft.engine.bpm.contract.ContractDefinition;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.web.rest.server.api.AbstractRESTController;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/\" + API_SPRING_INTERNAL + \"/bpm/process/{processDefinitionId}/contract\")\npublic class ProcessContractController extends AbstractRESTController {\n\n    protected final ContractTypeConverter typeConverterUtil = new ContractTypeConverter(ISO_8601_DATE_PATTERNS);\n\n    @GetMapping\n    public ResponseEntity<ContractDefinition> getContract(@PathVariable final long processDefinitionId,\n            HttpSession session)\n            throws BonitaException {\n        ContractDefinition processContract = getProcessAPI(session).getProcessContract(processDefinitionId);\n        if (processContract == null) {\n            return ResponseEntity.noContent().build();\n        }\n        return ResponseEntity.ok(typeConverterUtil.getAdaptedContractDefinition(processContract));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/process/ProcessDefinitionDesignController.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.process;\n\nimport static org.bonitasoft.web.rest.server.api.AbstractRESTController.API_SPRING_INTERNAL;\n\nimport java.io.IOException;\n\nimport javax.servlet.http.HttpSession;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.SerializationFeature;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.web.rest.server.api.AbstractRESTController;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/\" + API_SPRING_INTERNAL + \"/bpm/process/{processDefinitionId}/design\")\npublic class ProcessDefinitionDesignController extends AbstractRESTController {\n\n    final static ObjectMapper objectMapper = new ObjectMapper();\n\n    static {\n        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);\n    }\n\n    @GetMapping\n    public String getDesign(@PathVariable long processDefinitionId, HttpSession httpSession)\n            throws ProcessDefinitionNotFoundException, IOException, BonitaHomeNotSetException, ServerAPIException,\n            UnknownAPITypeException {\n        final DesignProcessDefinition design = getProcessAPI(httpSession)\n                .getDesignProcessDefinition(processDefinitionId);\n        return replaceLongIdToString(objectMapper.writeValueAsString(design));\n    }\n\n    /**\n     * Replace numeric id values with string values in JSON to avoid precision loss in JavaScript\n     *\n     * @param design the JSON string containing the design\n     * @return the JSON string with numeric ids replaced with string ids\n     */\n    protected String replaceLongIdToString(final String design) {\n        return design.replaceAll(\"([^\\\\\\\\]\\\"id\\\"\\\\s*:\\\\s*)(\\\\d+)\", \"$1\\\"$2\\\"\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/process/ProcessInstantiationController.java",
    "content": "/**\n * Copyright (C) 2025 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.process;\n\nimport static java.lang.String.format;\nimport static org.bonitasoft.web.rest.server.api.AbstractRESTController.API_SPRING_INTERNAL;\n\nimport java.io.FileNotFoundException;\nimport java.io.Serializable;\nimport java.util.Date;\nimport java.util.Map;\n\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpSession;\n\nimport com.fasterxml.jackson.databind.node.JsonNodeFactory;\nimport com.fasterxml.jackson.databind.node.ObjectNode;\nimport org.bonitasoft.console.common.server.preferences.properties.PropertiesFactory;\nimport org.bonitasoft.console.common.server.utils.ContractTypeConverter;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.contract.ContractDefinition;\nimport org.bonitasoft.engine.bpm.contract.ContractViolationException;\nimport org.bonitasoft.engine.bpm.process.ProcessExecutionException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.web.rest.server.api.AbstractRESTController;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;\n\n/**\n * REST controller for process instantiation.\n * Public URL: /API/bpm/process/{processDefinitionId}/instantiation\n * Internal path (via URL rewrite): /APISpringInternal/bpm/process/{processDefinitionId}/instantiation\n *\n * @author Nicolas Tith\n */\n@RestController\n@ConditionalOnSingleCandidate(ProcessInstantiationController.class)\n@RequestMapping(\"/\" + API_SPRING_INTERNAL + \"/bpm/process/{processDefinitionId}/instantiation\")\npublic class ProcessInstantiationController extends AbstractRESTController {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(ProcessInstantiationController.class);\n    private static final String CASE_ID_ATTRIBUTE = \"caseId\";\n    private static final long NO_RETRY_AFTER = -1L;\n    protected final ContractTypeConverter typeConverterUtil = new ContractTypeConverter(\n            ContractTypeConverter.ISO_8601_DATE_PATTERNS);\n\n    @PostMapping\n    public ResponseEntity<String> instantiateProcess(\n            @PathVariable final long processDefinitionId,\n            @RequestParam(name = \"user\", required = false) final Long userId,\n            @RequestBody(required = false) final Map<String, Serializable> inputs,\n            final HttpSession httpSession,\n            final HttpServletResponse response)\n            throws BonitaException, FileNotFoundException {\n        final ProcessAPI processAPI = getProcessAPI(httpSession);\n\n        try {\n            final ContractDefinition processContract = processAPI.getProcessContract(processDefinitionId);\n            final long maxSizeForTenant = getMaxFileSize();\n            final Map<String, Serializable> processedInputs = typeConverterUtil.getProcessedInput(processContract,\n                    inputs, maxSizeForTenant);\n\n            long processInstanceId;\n            if (userId == null) {\n                processInstanceId = processAPI.startProcessWithInputs(processDefinitionId, processedInputs).getId();\n            } else {\n                processInstanceId = processAPI.startProcessWithInputs(userId, processDefinitionId, processedInputs)\n                        .getId();\n            }\n\n            final ObjectNode returnedObject = JsonNodeFactory.instance.objectNode();\n            returnedObject.put(CASE_ID_ATTRIBUTE, processInstanceId);\n            return ResponseEntity.ok(returnedObject.toString());\n        } catch (final ProcessExecutionException e) {\n            String errorMessage = \"Unable to start the process with ID \" + processDefinitionId;\n            LOGGER.error(\"{}. Caused by: {}\", errorMessage, e.getMessage());\n            if (e.getRetryAfter() != NO_RETRY_AFTER) {\n                // Return a 429 status code with Retry-After header to indicate the client\n                // that he should retry later in case of case creation limit reached\n                response.addHeader(HttpHeaders.RETRY_AFTER, new Date(e.getRetryAfter()).toString());\n                return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body(\"Case creation limit reached.\");\n            }\n            // Avoid throwing original exception that may contain sensitive information unwanted in the HTTP response\n            throw new ProcessExecutionException(errorMessage + \" (consult the logs for more information).\");\n        } catch (final ContractViolationException e) {\n            // Re-throw as a specific exception to be handled by the global exception\n            // handler\n            throw new IllegalArgumentException(e.getMessage(), e);\n        } finally {\n            // clean temp files\n            typeConverterUtil.deleteTemporaryFiles(inputs);\n        }\n    }\n\n    // Visible for testing\n    protected long getMaxFileSize() {\n        return PropertiesFactory.getConsoleProperties().getMaxSize();\n    }\n\n    @ExceptionHandler(MethodArgumentTypeMismatchException.class)\n    public ResponseEntity<String> handleTypeMismatch(MethodArgumentTypeMismatchException ex) {\n        if (\"user\".equals(ex.getName())) {\n            return ResponseEntity.status(HttpStatus.BAD_REQUEST)\n                    .body(format(\"'user' URL query parameter should be Integer. Received '%s'\", ex.getValue()));\n        }\n        // Re-throw for other parameters\n        throw ex;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/signal/BPMSignal.java",
    "content": "/**\n * Copyright (C) 2022-2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.signal;\n\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\n\n@JsonIgnoreProperties(ignoreUnknown = true)\npublic record BPMSignal(String name) {}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/signal/BPMSignalController.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.signal;\n\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.engine.bpm.flownode.SendEventException;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.web.rest.server.api.AbstractRESTController;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseStatus;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/API/bpm/signal\")\npublic class BPMSignalController extends AbstractRESTController {\n\n    @PostMapping\n    @ResponseStatus(HttpStatus.NO_CONTENT)\n    public void broadcast(@RequestBody BPMSignal signal, HttpSession httpSession)\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException, SendEventException {\n        if (signal.name() == null) {\n            throw new IllegalArgumentException(\"'name' attribute is mandatory\");\n        }\n        getProcessAPI(httpSession).sendSignal(signal.name());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/deployer/ApplicationDeployer.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.deployer;\n\nimport org.bonitasoft.web.rest.model.application.AbstractApplicationItem;\nimport org.bonitasoft.web.rest.server.framework.Deployer;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\n\n/**\n * @author Julien Mege\n */\npublic class ApplicationDeployer implements Deployer {\n\n    private final DatastoreHasGet<AbstractApplicationItem> getter;\n\n    private final String attribute;\n\n    public ApplicationDeployer(final DatastoreHasGet<AbstractApplicationItem> getter, final String attribute) {\n        this.getter = getter;\n        this.attribute = attribute;\n    }\n\n    @Override\n    public String getDeployedAttribute() {\n        return attribute;\n    }\n\n    @Override\n    public void deployIn(final IItem item) {\n        if (isDeployable(attribute, item)) {\n            item.setDeploy(attribute, getter.get(item.getAttributeValueAsAPIID(attribute)));\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/deployer/ApplicationPageDeployer.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.deployer;\n\nimport org.bonitasoft.web.rest.model.applicationpage.ApplicationPageItem;\nimport org.bonitasoft.web.rest.server.framework.Deployer;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\n\n/**\n * @author Julien Mege\n */\npublic class ApplicationPageDeployer implements Deployer {\n\n    private final DatastoreHasGet<ApplicationPageItem> getter;\n\n    private final String attribute;\n\n    public ApplicationPageDeployer(final DatastoreHasGet<ApplicationPageItem> getter, final String attribute) {\n        this.getter = getter;\n        this.attribute = attribute;\n    }\n\n    @Override\n    public String getDeployedAttribute() {\n        return attribute;\n    }\n\n    @Override\n    public void deployIn(final IItem item) {\n        if (isDeployable(attribute, item)) {\n            item.setDeploy(attribute, getApplicationPage(getApplicationPageId(item)));\n        }\n    }\n\n    private APIID getApplicationPageId(final IItem item) {\n        return item.getAttributeValueAsAPIID(attribute);\n    }\n\n    private ApplicationPageItem getApplicationPage(final APIID applicationPageId) {\n        return getter.get(applicationPageId);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/deployer/DeployerFactory.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.deployer;\n\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.server.datastore.organization.UserDatastore;\nimport org.bonitasoft.web.rest.server.datastore.profile.GetProfileHelper;\nimport org.bonitasoft.web.rest.server.engineclient.EngineAPIAccessor;\nimport org.bonitasoft.web.rest.server.engineclient.EngineClientFactory;\nimport org.bonitasoft.web.rest.server.framework.Deployer;\n\n/**\n * @author Vincent Elcrin\n */\npublic class DeployerFactory {\n\n    private final APISession apiSession;\n\n    private final EngineClientFactory factory;\n\n    public DeployerFactory(final APISession apiSession) {\n        this.apiSession = apiSession;\n        factory = new EngineClientFactory(new EngineAPIAccessor(apiSession));\n    }\n\n    public UserDeployer createUserDeployer(final String attribute) {\n        return new UserDeployer(new UserDatastore(apiSession), attribute);\n    }\n\n    public Deployer createProfileDeployer(final String attribute) {\n        return new GenericDeployer<>(createProfileGetter(), attribute);\n    }\n\n    private GetProfileHelper createProfileGetter() {\n        return new GetProfileHelper(factory.createProfileEngineClient());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/deployer/GenericDeployer.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.deployer;\n\nimport org.bonitasoft.web.rest.server.framework.Deployer;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\n\n/**\n * @author Vincent Elcrin\n */\npublic class GenericDeployer<I extends IItem> implements Deployer {\n\n    private final DatastoreHasGet<I> getter;\n\n    private final String attribute;\n\n    public GenericDeployer(DatastoreHasGet<I> getter, String attribute) {\n        this.getter = getter;\n        this.attribute = attribute;\n    }\n\n    @Override\n    public String getDeployedAttribute() {\n        return attribute;\n    }\n\n    @Override\n    public void deployIn(IItem item) {\n        if (isDeployable(attribute, item)) {\n            item.setDeploy(attribute, getItem(getItemId(item)));\n        }\n    }\n\n    private APIID getItemId(IItem item) {\n        return item.getAttributeValueAsAPIID(attribute);\n    }\n\n    private I getItem(APIID profileId) {\n        return getter.get(profileId);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/deployer/PageDeployer.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.deployer;\n\nimport org.bonitasoft.web.rest.model.portal.page.PageItem;\nimport org.bonitasoft.web.rest.server.framework.Deployer;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\n\n/**\n * @author Julien Mege\n */\npublic class PageDeployer implements Deployer {\n\n    private final DatastoreHasGet<PageItem> getter;\n\n    private final String attribute;\n\n    public PageDeployer(final DatastoreHasGet<PageItem> getter, final String attribute) {\n        this.getter = getter;\n        this.attribute = attribute;\n    }\n\n    @Override\n    public String getDeployedAttribute() {\n        return attribute;\n    }\n\n    @Override\n    public void deployIn(final IItem item) {\n        if (isDeployable(attribute, item)) {\n            item.setDeploy(attribute, getPage(getPageId(item)));\n        }\n    }\n\n    private APIID getPageId(final IItem item) {\n        return item.getAttributeValueAsAPIID(attribute);\n    }\n\n    private PageItem getPage(final APIID pageId) {\n        return getter.get(pageId);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/deployer/UserDeployer.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.deployer;\n\nimport org.bonitasoft.web.rest.model.identity.UserItem;\nimport org.bonitasoft.web.rest.server.framework.Deployer;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\n\n/**\n * @author Vincent Elcrin\n */\npublic class UserDeployer implements Deployer {\n\n    private final DatastoreHasGet<UserItem> getter;\n\n    private final String attribute;\n\n    public UserDeployer(DatastoreHasGet<UserItem> getter, String attribute) {\n        this.getter = getter;\n        this.attribute = attribute;\n    }\n\n    @Override\n    public String getDeployedAttribute() {\n        return attribute;\n    }\n\n    @Override\n    public void deployIn(IItem item) {\n        if (isDeployable(attribute, item)) {\n            item.setDeploy(attribute, getUser(getUserId(item)));\n        }\n    }\n\n    private APIID getUserId(IItem item) {\n        return item.getAttributeValueAsAPIID(attribute);\n    }\n\n    private UserItem getUser(APIID userId) {\n        return getter.get(userId);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/document/APIArchivedDocument.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.document;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.document.ArchivedDocument;\nimport org.bonitasoft.engine.bpm.document.ArchivedDocumentsSearchDescriptor;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.document.ArchivedDocumentDefinition;\nimport org.bonitasoft.web.rest.model.document.ArchivedDocumentItem;\nimport org.bonitasoft.web.rest.model.document.DocumentItem;\nimport org.bonitasoft.web.rest.server.api.ConsoleAPI;\nimport org.bonitasoft.web.rest.server.api.document.api.impl.DocumentDatastore;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Julien Mege\n */\npublic class APIArchivedDocument extends ConsoleAPI<ArchivedDocumentItem> {\n\n    @Override\n    protected ItemDefinition defineItemDefinition() {\n        return Definitions.get(ArchivedDocumentDefinition.TOKEN);\n    }\n\n    @Override\n    public ArchivedDocumentItem get(final APIID id) {\n        final APISession apiSession = getEngineSession();\n        ArchivedDocumentItem item = new ArchivedDocumentItem();\n        try {\n            final ProcessAPI processAPI = TenantAPIAccessor.getProcessAPI(apiSession);\n            final DocumentDatastore dataStore = new DocumentDatastore(apiSession);\n            final ArchivedDocument document = processAPI.getArchivedProcessDocument(id.toLong());\n            item = dataStore.mapToArchivedDocumentItem(document);\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n        return item;\n    }\n\n    @Override\n    public String defineDefaultSearchOrder() {\n        return \"\";\n    }\n\n    @Override\n    public ItemSearchResult<ArchivedDocumentItem> search(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n\n        final APISession apiSession = getEngineSession();\n        final List<ArchivedDocumentItem> items = new ArrayList<>();\n        long nbOfDocument = 0;\n        String caseId = null;\n        String viewType = null;\n        String documentName = null;\n        long userId = -1;\n        try {\n            if (filters.containsKey(ArchivedDocumentItem.FILTER_CASE_ID)) {\n                caseId = filters.get(ArchivedDocumentItem.FILTER_CASE_ID);\n            }\n            if (filters.containsKey(ArchivedDocumentItem.FILTER_VIEW_TYPE)) {\n                viewType = filters.get(ArchivedDocumentItem.FILTER_VIEW_TYPE);\n            }\n            if (filters.containsKey(ArchivedDocumentItem.FILTER_USER_ID)) {\n                final String user = filters.get(ArchivedDocumentItem.FILTER_USER_ID);\n                if (user != null) {\n                    userId = Long.valueOf(user);\n                } else {\n                    userId = apiSession.getUserId();\n                }\n            }\n            if (filters.containsKey(DocumentItem.DOCUMENT_NAME)) {\n                documentName = filters.get(DocumentItem.DOCUMENT_NAME);\n            }\n\n            final SearchOptionsBuilder builder = SearchOptionsBuilderUtil.buildSearchOptions(page, resultsByPage,\n                    orders, search);\n            if (caseId != null) {\n                builder.filter(ArchivedDocumentsSearchDescriptor.PROCESSINSTANCE_ID, caseId);\n            }\n            if (documentName != null) {\n                builder.filter(ArchivedDocumentsSearchDescriptor.DOCUMENT_NAME, documentName);\n            }\n\n            SearchResult<ArchivedDocument> result = null;\n            final DocumentDatastore dataStore = new DocumentDatastore(apiSession);\n            if (userId != -1 && viewType != null) {\n                result = dataStore.searchArchivedDocuments(userId, viewType, builder);\n            }\n            if (result != null) {\n                nbOfDocument = result.getCount();\n                for (final ArchivedDocument document : result.getResult()) {\n                    items.add(dataStore.mapToArchivedDocumentItem(document));\n                }\n            }\n        } catch (final BonitaException | IllegalArgumentException e) {\n            throw new APIException(e);\n        }\n        return new ItemSearchResult<>(page, resultsByPage, nbOfDocument, items);\n\n    }\n\n    @Override\n    protected void fillDeploys(final ArchivedDocumentItem item, final List<String> deploys) {\n    }\n\n    @Override\n    protected void fillCounters(final ArchivedDocumentItem item, final List<String> counters) {\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/document/APIDocument.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.document;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.document.ArchivedDocumentsSearchDescriptor;\nimport org.bonitasoft.engine.bpm.document.Document;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.document.DocumentDefinition;\nimport org.bonitasoft.web.rest.model.document.DocumentItem;\nimport org.bonitasoft.web.rest.server.api.ConsoleAPI;\nimport org.bonitasoft.web.rest.server.api.document.api.impl.DocumentDatastore;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Julien Mege\n */\npublic class APIDocument extends ConsoleAPI<DocumentItem> {\n\n    @Override\n    protected ItemDefinition defineItemDefinition() {\n        return Definitions.get(DocumentDefinition.TOKEN);\n    }\n\n    protected DocumentDatastore getDataStore() {\n        return new DocumentDatastore(getEngineSession());\n    }\n\n    @Override\n    public DocumentItem get(final APIID id) {\n        final APISession apiSession = getEngineSession();\n        DocumentItem item = new DocumentItem();\n\n        try {\n            final ProcessAPI processAPI = TenantAPIAccessor.getProcessAPI(apiSession);\n            final Document document = processAPI.getDocument(id.toLong());\n            item = getDataStore().mapToDocumentItem(document);\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n\n        return item;\n    }\n\n    @Override\n    public String defineDefaultSearchOrder() {\n        return \"\";\n    }\n\n    @Override\n    public ItemSearchResult<DocumentItem> search(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n\n        final APISession apiSession = getEngineSession();\n        final List<DocumentItem> items = new ArrayList<>();\n        long nbOfDocument = 0;\n        String caseId = null;\n        String viewType = null;\n        String documentName = null;\n        long userId = -1;\n        try {\n\n            if (filters != null) {\n                if (filters.containsKey(DocumentItem.FILTER_CASE_ID)) {\n                    caseId = filters.get(DocumentItem.FILTER_CASE_ID);\n                }\n                if (filters.containsKey(DocumentItem.FILTER_VIEW_TYPE)) {\n                    viewType = filters.get(DocumentItem.FILTER_VIEW_TYPE);\n                }\n                if (filters.containsKey(DocumentItem.FILTER_USER_ID)) {\n                    final String user = filters.get(DocumentItem.FILTER_USER_ID);\n                    if (user != null) {\n                        userId = Long.valueOf(user);\n                    } else {\n                        userId = apiSession.getUserId();\n                    }\n                }\n                if (filters.containsKey(DocumentItem.DOCUMENT_NAME)) {\n                    documentName = filters.get(DocumentItem.DOCUMENT_NAME);\n                }\n            }\n            final SearchOptionsBuilder builder = SearchOptionsBuilderUtil.buildSearchOptions(page, resultsByPage,\n                    orders, search);\n            if (caseId != null) {\n                builder.filter(ArchivedDocumentsSearchDescriptor.PROCESSINSTANCE_ID, caseId);\n            }\n            if (documentName != null) {\n                builder.filter(ArchivedDocumentsSearchDescriptor.DOCUMENT_NAME, documentName);\n            }\n\n            SearchResult<Document> result = null;\n            if (viewType != null) {\n                result = getDataStore().searchDocuments(userId, viewType, builder);\n            }\n            if (result != null) {\n                nbOfDocument = result.getCount();\n                for (final Document document : result.getResult()) {\n                    items.add(getDataStore().mapToDocumentItem(document));\n                }\n            }\n        } catch (final BonitaException | IllegalArgumentException e) {\n            throw new APIException(e);\n        }\n        return new ItemSearchResult<>(page, resultsByPage, nbOfDocument, items);\n\n    }\n\n    @Override\n    public DocumentItem add(final DocumentItem item) {\n        final long processInstanceId = Long.valueOf(item.getAttributeValue(DocumentItem.PROCESSINSTANCE_ID));\n        final String documentName = item.getAttributeValue(DocumentItem.DOCUMENT_NAME);\n        final String path = item.getAttributeValue(DocumentItem.DOCUMENT_UPLOAD);\n        final String documentCreationType = item.getAttributeValue(DocumentItem.DOCUMENT_CREATION_TYPE);\n        final String urlPath = item.getAttributeValue(DocumentItem.DOCUMENT_URL);\n        try {\n            DocumentItem returnedItem = new DocumentItem();\n            if (processInstanceId != -1 && documentName != null && documentCreationType != null) {\n                if (path != null && !path.isEmpty()) {\n                    returnedItem = getDataStore().createDocument(processInstanceId, documentName, documentCreationType,\n                            path, new BonitaHomeFolderAccessor());\n                } else if (urlPath != null && !urlPath.isEmpty()) {\n                    returnedItem = getDataStore().createDocumentFromUrl(processInstanceId, documentName,\n                            documentCreationType, urlPath);\n                }\n                return returnedItem;\n            } else {\n                throw new APIException(\"Error while attaching a new document. Request with bad param value.\");\n            }\n        } catch (final BonitaException | IOException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @Override\n    protected void fillDeploys(final DocumentItem item, final List<String> deploys) {\n    }\n\n    @Override\n    protected void fillCounters(final DocumentItem item, final List<String> counters) {\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/document/api/impl/DocumentDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.document.api.impl;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.text.DateFormat;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Locale;\n\nimport org.apache.commons.io.IOUtils;\nimport org.bonitasoft.console.common.server.preferences.properties.PropertiesFactory;\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.document.ArchivedDocument;\nimport org.bonitasoft.engine.bpm.document.Document;\nimport org.bonitasoft.engine.bpm.document.DocumentAttachmentException;\nimport org.bonitasoft.engine.bpm.document.DocumentException;\nimport org.bonitasoft.engine.bpm.process.*;\nimport org.bonitasoft.engine.exception.*;\nimport org.bonitasoft.engine.io.FileContent;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.bonitasoft.web.rest.model.document.ArchivedDocumentItem;\nimport org.bonitasoft.web.rest.model.document.DocumentItem;\n\n/**\n * Document data store\n *\n * @author Yongtao Guo\n */\npublic class DocumentDatastore {\n\n    private final APISession apiSession;\n\n    public final static String CREATE_NEW_VERSION_DOCUMENT = \"AddNewVersionDocument\";\n\n    public final static String CREATE_NEW_DOCUMENT = \"AddNewDocument\";\n\n    /**\n     * Default constructor.\n     */\n    public DocumentDatastore(final APISession apiSession) {\n        this.apiSession = apiSession;\n    }\n\n    public SearchResult<Document> searchDocuments(final long userId, final String viewType,\n            final SearchOptionsBuilder builder) throws InvalidSessionException,\n            BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException, SearchException, NotFoundException {\n        final ProcessAPI processAPI = getProcessAPI();\n        if (DocumentItem.VALUE_VIEW_TYPE_ADMINISTRATOR.equals(viewType)\n                || DocumentItem.VALUE_VIEW_TYPE_USER.equals(viewType)) {\n            return processAPI.searchDocuments(builder.done());\n        } else if (DocumentItem.VALUE_VIEW_TYPE_TEAM_MANAGER.equals(viewType)) {\n            return processAPI.searchDocuments(builder.done());\n        } else if (DocumentItem.VALUE_VIEW_TYPE_PROCESS_OWNER.equals(viewType)) {\n            return processAPI.searchDocumentsSupervisedBy(userId, builder.done());\n        }\n        throw new IllegalArgumentException(\"Invalid view type.\");\n    }\n\n    public SearchResult<ArchivedDocument> searchArchivedDocuments(final long userId, final String viewType,\n            final SearchOptionsBuilder builder)\n            throws InvalidSessionException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException,\n            SearchException, NotFoundException {\n        final ProcessAPI processAPI = getProcessAPI();\n        if (DocumentItem.VALUE_VIEW_TYPE_ADMINISTRATOR.equals(viewType)\n                || DocumentItem.VALUE_VIEW_TYPE_USER.equals(viewType)\n                || DocumentItem.VALUE_VIEW_TYPE_TEAM_MANAGER.equals(viewType)) {\n            return processAPI.searchArchivedDocuments(builder.done());\n        } else if (DocumentItem.VALUE_VIEW_TYPE_PROCESS_OWNER.equals(viewType)) {\n            return processAPI.searchArchivedDocumentsSupervisedBy(userId, builder.done());\n        }\n        throw new IllegalArgumentException(\"Invalid view type.\");\n    }\n\n    public DocumentItem createDocument(final long processInstanceId, final String documentName,\n            final String documentCreationType, final String path, final BonitaHomeFolderAccessor tenantFolder)\n            throws BonitaException, IOException, InvalidSessionException, RetrieveException {\n\n        DocumentItem item = new DocumentItem();\n        final ProcessAPI processAPI = getProcessAPI();\n\n        final FileContent theSourceFile = tenantFolder.retrieveUploadedTempContent(path);\n\n        try (InputStream inputStream = theSourceFile.getInputStream()) {\n            final long maxSize = PropertiesFactory.getConsoleProperties().getMaxSize();\n            if (theSourceFile.getSize() > maxSize * 1048576) {\n                final String errorMessage = \"This document is exceeded \" + maxSize + \"Mo\";\n                throw new DocumentException(errorMessage);\n            }\n            byte[] fileContent = IOUtils.toByteArray(inputStream);\n            String fileName = theSourceFile.getFileName();\n            String mimeType = theSourceFile.getMimeType();\n\n            // Attach a new document to a case\n            if (CREATE_NEW_DOCUMENT.equals(documentCreationType)) {\n                final Document document = processAPI.attachDocument(processInstanceId, documentName, fileName, mimeType,\n                        fileContent);\n                item = mapToDocumentItem(document);\n            } else if (CREATE_NEW_VERSION_DOCUMENT.equals(documentCreationType)) {\n                final Document document = processAPI.attachNewDocumentVersion(processInstanceId, documentName, fileName,\n                        mimeType, fileContent);\n                item = mapToDocumentItem(document);\n            }\n            return item;\n        } finally {\n            tenantFolder.removeUploadedTempContent(path);\n        }\n    }\n\n    protected ProcessAPI getProcessAPI() throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return TenantAPIAccessor.getProcessAPI(apiSession);\n    }\n\n    public DocumentItem createDocumentFromUrl(final long processInstanceId, final String documentName,\n            final String documentCreationType, final String path)\n            throws InvalidSessionException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException,\n            ProcessInstanceNotFoundException,\n            DocumentAttachmentException, IOException, RetrieveException, ProcessDefinitionNotFoundException {\n\n        DocumentItem item = new DocumentItem();\n        final ProcessAPI processAPI = getProcessAPI();\n        final String fileName = DocumentUtil.getFileNameFromUrl(path);\n        final String mimeType = DocumentUtil.getMimeTypeFromUrl(path);\n        if (fileName != null && mimeType != null) {\n            // Attach a new document to a case\n            if (CREATE_NEW_DOCUMENT.equals(documentCreationType)) {\n                final Document document = processAPI.attachDocument(processInstanceId, documentName, fileName, mimeType,\n                        path);\n                item = mapToDocumentItem(document);\n            } else if (CREATE_NEW_VERSION_DOCUMENT.equals(documentCreationType)) {\n                final Document document = processAPI.attachNewDocumentVersion(processInstanceId, documentName, fileName,\n                        mimeType, path);\n                item = mapToDocumentItem(document);\n            }\n        }\n\n        return item;\n    }\n\n    public DocumentItem mapToDocumentItem(final Document document)\n            throws InvalidSessionException, BonitaHomeNotSetException, ServerAPIException,\n            UnknownAPITypeException, ProcessDefinitionNotFoundException, RetrieveException {\n\n        if (document == null) {\n            throw new IllegalArgumentException(\"The document must be not null!\");\n        }\n        DocumentItem item = new DocumentItem();\n        final ProcessAPI processAPI = getProcessAPI();\n        ProcessInstance processInstance;\n        String caseName = \"\";\n        String processDisplayName = \"\";\n        String version = \"\";\n        try {\n            processInstance = processAPI.getProcessInstance(document.getProcessInstanceId());\n            caseName = processInstance.getName();\n            final ProcessDeploymentInfo processDeploymentInfo = processAPI\n                    .getProcessDeploymentInfo(processInstance.getProcessDefinitionId());\n            processDisplayName = processDeploymentInfo.getDisplayName();\n            version = processDeploymentInfo.getVersion();\n        } catch (final ProcessInstanceNotFoundException e) {\n            item = buildDocumentItem(caseName, processDisplayName, version, document);\n            return item;\n        }\n\n        item = buildDocumentItem(caseName, processDisplayName, version, document);\n        return item;\n\n    }\n\n    private DocumentItem buildDocumentItem(final String caseName, final String processDisplayName, final String version,\n            final Document document) {\n        final DocumentItem item = new DocumentItem();\n        item.setDocumentId(String.valueOf(document.getId()));\n        item.setCaseId(String.valueOf(document.getProcessInstanceId()));\n        item.setDocumentName(document.getName());\n        item.setDocumentAuthor(document.getAuthor());\n        item.setDocumentFileName(document.getContentFileName());\n        item.setDocumentCreationDate(parseDate(document.getCreationDate()));\n        item.setDocumentMIMEType(document.getContentMimeType());\n        item.setDocumentHasContent(String.valueOf(document.hasContent()));\n        item.setDocumentStorageId(document.getContentStorageId());\n        item.setDocumentURL(document.getUrl());\n        item.setProcessDisplayName(processDisplayName);\n        item.setProcessVersion(version);\n        item.setCaseName(caseName);\n        return item;\n    }\n\n    public ArchivedDocumentItem mapToArchivedDocumentItem(final ArchivedDocument document)\n            throws InvalidSessionException, BonitaHomeNotSetException,\n            ServerAPIException, UnknownAPITypeException, RetrieveException, ProcessDefinitionNotFoundException {\n\n        if (document == null) {\n            throw new IllegalArgumentException(\"The document must be not null!\");\n        }\n        final ProcessAPI processAPI = getProcessAPI();\n        ArchivedDocumentItem item = new ArchivedDocumentItem();\n        String caseName = \"\";\n        String processDisplayName = \"\";\n        String version = \"\";\n\n        List<ArchivedProcessInstance> archivedCaseList;\n        try {\n            archivedCaseList = processAPI.getArchivedProcessInstances(document.getProcessInstanceId(), 0, 1);\n            final ArchivedProcessInstance processInstance = archivedCaseList.get(0);\n            caseName = processInstance.getName();\n            final ProcessDeploymentInfo processDeploymentInfo = processAPI\n                    .getProcessDeploymentInfo(processInstance.getProcessDefinitionId());\n            processDisplayName = processDeploymentInfo.getDisplayName();\n            version = processDeploymentInfo.getVersion();\n        } catch (final NotFoundException e) {\n            item = buildArchivedDocumentItem(caseName, processDisplayName, version, document);\n            return item;\n        }\n\n        item = buildArchivedDocumentItem(caseName, processDisplayName, version, document);\n        return item;\n\n    }\n\n    private ArchivedDocumentItem buildArchivedDocumentItem(final String caseName, final String processDisplayName,\n            final String version,\n            final ArchivedDocument document) {\n        final ArchivedDocumentItem item = new ArchivedDocumentItem();\n        item.setDocumentId(String.valueOf(document.getSourceObjectId()));\n        item.setDocumentSourceObjectId(String.valueOf(document.getSourceObjectId()));\n        item.setCaseId(String.valueOf(document.getProcessInstanceId()));\n        item.setDocumentName(document.getName());\n        item.setDocumentAuthor(document.getAuthor());\n        item.setDocumentFileName(document.getContentFileName());\n        item.setDocumentCreationDate(parseDate(document.getCreationDate()));\n        item.setDocumentMIMEType(document.getContentMimeType());\n        item.setDocumentHasContent(String.valueOf(document.hasContent()));\n        item.setDocumentStorageId(document.getContentStorageId());\n        item.setDocumentURL(document.getUrl());\n        item.setProcessDisplayName(processDisplayName);\n        item.setProcessVersion(version);\n        item.setCaseName(caseName);\n        item.setArchivedDate(parseDate(document.getArchiveDate()));\n        return item;\n    }\n\n    private String parseDate(final Date date) {\n        String dateStr = null;\n        if (date != null) {\n            final DateFormat time = DateFormat.getTimeInstance(DateFormat.MEDIUM, Locale.ENGLISH);\n            final DateFormat df = DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.ENGLISH);\n            dateStr = time.format(date) + \", \" + df.format(date);\n        }\n        return dateStr;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/document/api/impl/DocumentUtil.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.document.api.impl;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.FileNameMap;\nimport java.net.URLConnection;\n\n/**\n * @author Yongtao Guo\n */\npublic class DocumentUtil {\n\n    public static byte[] getArrayByte(final InputStream input, final int estimatedSize) throws IOException {\n        final ByteArrayOutputStream output = new ByteArrayOutputStream(estimatedSize);\n        try {\n            final byte[] buf = new byte[8192];\n            int len;\n\n            while ((len = input.read(buf)) >= 0) {\n                output.write(buf, 0, len);\n            }\n        } finally {\n            output.close();\n        }\n        return output.toByteArray();\n    }\n\n    public static byte[] getArrayByteFromFile(final File f) throws IOException {\n        final long length = f.length();\n        if (length > Integer.MAX_VALUE) { // more than 2 GB\n            throw new IOException(\"File too big\");\n        }\n\n        final FileInputStream input = new FileInputStream(f);\n        try {\n            return getArrayByte(input, (int) length);\n        } finally {\n            input.close();\n        }\n    }\n\n    public static String getFileNameFromUrl(final String file) {\n        String fileName = null;\n        if (file != null) {\n            final int index = file.lastIndexOf(\"/\");\n            if (index != -1 && index != file.length() - 1) {\n                final String queryName = file.substring(index + 1);\n                final int queryIndex = queryName.indexOf(\"?\");\n                if (queryIndex != -1) {\n                    fileName = queryName.substring(0, queryIndex);\n                } else {\n                    fileName = queryName;\n                }\n            }\n        }\n\n        return fileName;\n    }\n\n    public static String getMimeTypeFromUrl(final String fileUrl) {\n        String type = null;\n        final FileNameMap fileNameMap = URLConnection.getFileNameMap();\n        type = fileNameMap.getContentTypeFor(fileUrl);\n\n        return type;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/extension/ApiExtensionController.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.extension;\n\nimport java.nio.charset.Charset;\nimport java.util.Map;\n\nimport javax.servlet.http.Cookie;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.console.common.server.page.PageMappingService;\nimport org.bonitasoft.console.common.server.page.RestApiRenderer;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.web.extension.rest.RestApiResponse;\nimport org.bonitasoft.web.rest.server.api.AbstractRESTController;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@Slf4j\n@RestController\n@ConditionalOnSingleCandidate(ApiExtensionController.class)\n@RequestMapping({ \"/API/extension\", \"/portal/custom-page/API/extension\" })\npublic class ApiExtensionController extends AbstractRESTController {\n\n    private final RestApiRenderer restApiRenderer;\n    private final PageMappingService pageMappingService;\n\n    public ApiExtensionController() {\n        this(new RestApiRenderer(), new PageMappingService());\n    }\n\n    protected ApiExtensionController(RestApiRenderer restApiRenderer, PageMappingService pageMappingService) {\n        this.restApiRenderer = restApiRenderer;\n        this.pageMappingService = pageMappingService;\n    }\n\n    @RequestMapping(\"/**\")\n    public ResponseEntity<String> handleExtensionCall(HttpServletRequest request, HttpServletResponse response) {\n        try {\n            var resolver = new ResourceExtensionResolver(request, request.getMethod(), pageMappingService);\n            var restApiResponse = restApiRenderer.handleRestApiCall(request, resolver);\n            if (restApiResponse == null) {\n                throw new BonitaException(\"error: restApiResponse is null\");\n            }\n            return buildResponse(restApiResponse, response);\n        } catch (Exception e) {\n            log.error(\"Failed to handle API Extension call\", e);\n            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();\n        }\n    }\n\n    private ResponseEntity<String> buildResponse(RestApiResponse restApiResponse, HttpServletResponse servletResponse) {\n        // Set cookies on the servlet response (ResponseEntity doesn't support cookies)\n        for (Cookie cookie : restApiResponse.getAdditionalCookies()) {\n            servletResponse.addCookie(cookie);\n        }\n\n        MediaType mediaType = MediaType.parseMediaType(restApiResponse.getMediaType());\n        if (mediaType.getCharset() == null) {\n            Charset characterSet = Charset.isSupported(restApiResponse.getCharacterSet())\n                    ? Charset.forName(restApiResponse.getCharacterSet()) : RestApiResponse.DEFAULT_CHARSET;\n            mediaType = new MediaType(mediaType, characterSet);\n        }\n        var bodyBuilder = ResponseEntity\n                .status(restApiResponse.getHttpStatus())\n                .contentType(mediaType);\n\n        // Pass through all additional headers directly\n        for (Map.Entry<String, String> entry : restApiResponse.getAdditionalHeaders().entrySet()) {\n            bodyBuilder.header(entry.getKey(), entry.getValue());\n        }\n\n        String body = restApiResponse.getResponse() != null ? restApiResponse.getResponse().toString() : null;\n        return bodyBuilder.body(body);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/extension/ControllerClassName.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.extension;\n\n/**\n * This class has the responsability to store a REST API extension controller class name, and if this REST API is used\n * in\n * compiled mode or in source mode.\n * - In compiled mode, name is supposed to be the qualified name of a Java class present in the Jar.\n * - In source mode, name is supposed to be the path to the main Groovy source file\n */\npublic class ControllerClassName {\n\n    private final String name;\n    private final boolean source;\n\n    public ControllerClassName(String name, boolean source) {\n        this.name = name;\n        this.source = source;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public boolean isSource() {\n        return source;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/extension/ResourceExtensionResolver.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.extension;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.Properties;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.bonitasoft.console.common.server.page.PageMappingService;\nimport org.bonitasoft.console.common.server.page.extension.PageResourceProviderImpl;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.NotFoundException;\nimport org.bonitasoft.engine.session.APISession;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class ResourceExtensionResolver {\n\n    public static final String MAPPING_KEY_SEPARATOR = \"|\";\n    public static final String MAPPING_KEY_PREFIX = \"apiExtension\";\n    public static final String API_EXTENSION_TEMPLATE_PREFIX = \"/API/extension/\";\n    private final HttpServletRequest httpServletRequest;\n    private final String httpMethod;\n    private final PageMappingService pageMappingService;\n\n    public ResourceExtensionResolver(HttpServletRequest httpServletRequest, String httpMethod,\n            PageMappingService pageMappingService) {\n        this.httpServletRequest = httpServletRequest;\n        this.httpMethod = httpMethod;\n        this.pageMappingService = pageMappingService;\n    }\n\n    public Long resolvePageId(APISession apiSession) throws BonitaException {\n        return pageMappingService.getPage(httpServletRequest, apiSession, generateMappingKey(),\n                httpServletRequest.getLocale(), false).getPageId();\n    }\n\n    public String generateMappingKey() {\n        final StringBuilder builder = new StringBuilder();\n        final String requestAsString = httpServletRequest.getRequestURI();\n        final String pathTemplate = StringUtils.substringAfter(requestAsString, API_EXTENSION_TEMPLATE_PREFIX);\n\n        builder.append(MAPPING_KEY_PREFIX)\n                .append(MAPPING_KEY_SEPARATOR)\n                .append(httpMethod)\n                .append(MAPPING_KEY_SEPARATOR)\n                .append(pathTemplate);\n\n        return builder.toString();\n    }\n\n    public ControllerClassName resolveRestApiControllerClassName(PageResourceProviderImpl pageResourceProvider)\n            throws NotFoundException {\n        final Properties properties = new Properties();\n\n        try (final InputStream resourceAsStream = pageResourceProvider.getResourceAsStream(\"page.properties\")) {\n            properties.load(resourceAsStream);\n        } catch (final IOException e) {\n            throw new NotFoundException(\"error while getting resource:\" + generateMappingKey());\n        }\n\n        final String apiExtensionList = (String) properties.get(\"apiExtensions\");\n        final String[] apiExtensions = apiExtensionList.split(\",\");\n\n        return findMatchingControllerClassName(properties, apiExtensions);\n    }\n\n    /**\n     * return the content of the property 'className' or 'classFileName' (if className isn't present).\n     * - 'className' refers to the qualified name of the main class. If 'className' is present then it indicates that\n     * the jar\n     * file\n     * containing the extension is present.\n     * - 'classFileName' is the path to the groovy source file (lagacy mode), it indicates that the rest api needs to be\n     * compiled\n     * at runtime with a groovy compiler.\n     */\n    private ControllerClassName findMatchingControllerClassName(Properties properties, String[] apiExtensions)\n            throws NotFoundException {\n        for (final String apiExtension : apiExtensions) {\n            final String method = (String) properties.get(String.format(\"%s.method\", apiExtension.trim()));\n            String pathTemplate = (String) properties.get(String.format(\"%s.pathTemplate\", apiExtension.trim()));\n            if (pathTemplate != null && pathTemplate.startsWith(\"/\")) {\n                pathTemplate = pathTemplate.substring(1);\n            }\n            final String className = (String) properties.get(String.format(\"%s.className\", apiExtension.trim()));\n            final String classFileName = (String) properties\n                    .get(String.format(\"%s.classFileName\", apiExtension.trim()));\n\n            if (extensionMatches(method, pathTemplate)) {\n                return className != null && !className.isEmpty()\n                        ? new ControllerClassName(className, false)\n                        : new ControllerClassName(classFileName, true);\n            }\n        }\n        throw new NotFoundException(\"error while getting resource:\" + generateMappingKey());\n    }\n\n    private boolean extensionMatches(String method, String pathTemplate) {\n        return httpMethod.equals(method) && httpServletRequest.getRequestURI()\n                .endsWith(String.format(\"%s%s\", API_EXTENSION_TEMPLATE_PREFIX, pathTemplate));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/form/FormMappingController.java",
    "content": "/**\n * Copyright (C) 2025 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.form;\n\nimport static java.util.stream.Collectors.toList;\nimport static org.bonitasoft.web.rest.server.APIPaginationUtils.buildContentRange;\nimport static org.bonitasoft.web.rest.server.APIPaginationUtils.buildSearchOptions;\n\nimport java.util.List;\n\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.form.FormMapping;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.web.rest.server.api.AbstractRESTController;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * REST controller to search Form Mappings.\n * Note: Uses @ConditionalOnSingleCandidate to be registered only when no subclass is present.\n * In subscription builds, FormMappingControllerExt extends this class and takes precedence.\n *\n * @author Anthony Birembaut\n */\n@RestController\n@RequestMapping(\"/API/form/mapping\")\n@ConditionalOnSingleCandidate(FormMappingController.class)\npublic class FormMappingController extends AbstractRESTController {\n\n    /**\n     * Search for form mappings based on query parameters.\n     *\n     * @param page the page number (0-indexed)\n     * @param count the number of results per page\n     * @param search the search term\n     * @param order the sort order (e.g., \"id ASC\")\n     * @param filters the filters as a list of \"key=value\" strings\n     * @param httpSession the HTTP session\n     * @return the list of form mapping items with Content-Range header\n     * @throws BonitaException if an error occurs during the search\n     */\n    @GetMapping\n    public ResponseEntity<List<FormMappingItem>> searchFormMapping(\n            @RequestParam(value = \"p\", defaultValue = \"0\") int page,\n            @RequestParam(value = \"c\", defaultValue = \"10\") int count,\n            @RequestParam(value = \"s\", required = false) String search,\n            @RequestParam(value = \"o\", required = false) String order,\n            @RequestParam(value = \"f\", required = false) List<String> filters,\n            HttpSession httpSession) throws BonitaException {\n\n        ProcessAPI processAPI = getProcessAPI(httpSession);\n\n        SearchOptions searchOptions = buildSearchOptions(page, count, search, order, filters);\n        SearchResult<?> searchResult = processAPI.searchFormMappings(searchOptions);\n\n        List<FormMappingItem> result = searchResult.getResult().stream()\n                .map(item -> new FormMappingItem((FormMapping) item))\n                .collect(toList());\n\n        HttpHeaders headers = new HttpHeaders();\n        headers.set(HttpHeaders.CONTENT_RANGE,\n                buildContentRange(page, searchResult.getResult().size(), searchResult.getCount()));\n\n        return ResponseEntity.ok()\n                .headers(headers)\n                .body(result);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/form/FormMappingItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.form;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\nimport org.bonitasoft.engine.form.FormMapping;\nimport org.bonitasoft.engine.form.FormMappingTarget;\nimport org.bonitasoft.engine.form.FormMappingType;\n\n/**\n * Created by Fabio Lombardi\n */\npublic class FormMappingItem implements Serializable {\n\n    private String id;\n    private String processDefinitionId;\n    private FormMappingType type;\n    private FormMappingTarget target;\n    private String task;\n    private String pageId;\n    private String pageURL;\n    private String pageMappingKey;\n    private String lastUpdatedBy;\n    private Date lastUpdateDate;\n    private boolean formRequired;\n\n    public FormMappingItem(final FormMapping item) {\n        id = String.valueOf(item.getId());\n        processDefinitionId = String.valueOf(item.getProcessDefinitionId());\n        type = item.getType();\n        target = item.getTarget();\n        task = item.getTask();\n        pageId = null;\n        if (item.getPageId() != null) {\n            pageId = String.valueOf(item.getPageId());\n        }\n        pageURL = item.getURL();\n        pageMappingKey = item.getPageMappingKey();\n        lastUpdatedBy = String.valueOf(item.getLastUpdatedBy());\n        lastUpdateDate = item.getLastUpdateDate();\n        formRequired = item.isFormRequired();\n    }\n\n    public String getProcessDefinitionId() {\n        return processDefinitionId;\n    }\n\n    public void setProcessDefinitionId(final String processDefinitionId) {\n        this.processDefinitionId = processDefinitionId;\n    }\n\n    public String getPageMappingKey() {\n        return pageMappingKey;\n    }\n\n    public void setPageMappingKey(final String pageMappingKey) {\n        this.pageMappingKey = pageMappingKey;\n    }\n\n    public String getId() {\n        return id;\n    }\n\n    public void setId(final String id) {\n        this.id = id;\n    }\n\n    public String getTask() {\n        return task;\n    }\n\n    public void setTask(final String task) {\n        this.task = task;\n    }\n\n    public String getPageId() {\n        return pageId;\n    }\n\n    public void setPageId(final String pageId) {\n        this.pageId = pageId;\n    }\n\n    public String getURL() {\n        return pageURL;\n    }\n\n    public void setPageURL(final String pageURL) {\n        this.pageURL = pageURL;\n    }\n\n    public FormMappingType getType() {\n        return type;\n    }\n\n    public void setType(final FormMappingType type) {\n        this.type = type;\n    }\n\n    public String getLastUpdatedBy() {\n        return lastUpdatedBy;\n    }\n\n    public void setLastUpdatedBy(final String lastUpdatedBy) {\n        this.lastUpdatedBy = lastUpdatedBy;\n    }\n\n    public Date getLastUpdateDate() {\n        return lastUpdateDate;\n    }\n\n    public void setLastUpdateDate(final Date lastUpdateDate) {\n        this.lastUpdateDate = lastUpdateDate;\n    }\n\n    public FormMappingTarget getTarget() {\n        return target;\n    }\n\n    public void setTarget(final FormMappingTarget target) {\n        this.target = target;\n    }\n\n    public boolean isFormRequired() {\n        return formRequired;\n    }\n\n    public void setFormRequired(final boolean formRequired) {\n        this.formRequired = formRequired;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/organization/APICustomUserInfoDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.organization;\n\nimport static org.bonitasoft.web.rest.server.api.APIPreconditions.check;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.identity.CustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.CustomUserInfoDefinitionCreator;\nimport org.bonitasoft.web.rest.model.identity.CustomUserInfoDefinitionDefinition;\nimport org.bonitasoft.web.rest.model.identity.CustomUserInfoDefinitionItem;\nimport org.bonitasoft.web.rest.server.api.ConsoleAPI;\nimport org.bonitasoft.web.rest.server.engineclient.CustomUserInfoEngineClient;\nimport org.bonitasoft.web.rest.server.engineclient.CustomUserInfoEngineClientCreator;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasAdd;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasDelete;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasSearch;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.i18n.T_;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Vincent Elcrin\n */\npublic class APICustomUserInfoDefinition extends ConsoleAPI<CustomUserInfoDefinitionItem> implements\n        APIHasAdd<CustomUserInfoDefinitionItem>,\n        APIHasSearch<CustomUserInfoDefinitionItem>,\n        APIHasDelete {\n\n    public static final String FIX_ORDER = \"Fix order\";\n\n    private final CustomUserInfoConverter converter = new CustomUserInfoConverter();\n\n    private final CustomUserInfoEngineClientCreator engineClientCreator;\n\n    public APICustomUserInfoDefinition(CustomUserInfoEngineClientCreator engineClientCreator) {\n        this.engineClientCreator = engineClientCreator;\n    }\n\n    public CustomUserInfoDefinitionItem add(CustomUserInfoDefinitionItem definition) {\n        return converter.convert(engineClientCreator.create(getEngineSession())\n                .createDefinition(new CustomUserInfoDefinitionCreator(\n                        definition.getName(),\n                        definition.getDescription())));\n    }\n\n    public void delete(final List<APIID> ids) {\n        for (APIID id : ids) {\n            engineClientCreator.create(getEngineSession()).deleteDefinition(id.toLong());\n        }\n    }\n\n    public ItemSearchResult<CustomUserInfoDefinitionItem> search(\n            final int page,\n            final int resultsByPage,\n            final String search,\n            final String orders,\n            final Map<String, String> filters) {\n\n        check(search == null, new T_(\"Search terms are not supported by this API\"));\n        check(filters == null || filters.isEmpty(), new T_(\"Filters are not supported by this API\"));\n        check(orders.equals(FIX_ORDER), new T_(\"Sorting is not supported by this API\"));\n\n        CustomUserInfoEngineClient client = engineClientCreator.create(getEngineSession());\n        List<CustomUserInfoDefinitionItem> result = new ArrayList<>();\n        for (CustomUserInfoDefinition definition : client.listDefinitions(page * resultsByPage, resultsByPage)) {\n            result.add(converter.convert(definition));\n        }\n        return new ItemSearchResult<>(page, resultsByPage, client.countDefinitions(), result);\n    }\n\n    @Override\n    protected ItemDefinition<CustomUserInfoDefinitionItem> defineItemDefinition() {\n        return CustomUserInfoDefinitionDefinition.get();\n    }\n\n    public String defineDefaultSearchOrder() {\n        return FIX_ORDER;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/organization/APICustomUserInfoUser.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.organization;\n\nimport static org.bonitasoft.web.rest.model.identity.CustomUserInfoItem.FILTER_USER_ID;\nimport static org.bonitasoft.web.rest.server.api.APIPreconditions.check;\nimport static org.bonitasoft.web.rest.server.api.APIPreconditions.containsOnly;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.identity.CustomUserInfo;\nimport org.bonitasoft.web.rest.model.identity.CustomUserInfoDefinition;\nimport org.bonitasoft.web.rest.model.identity.CustomUserInfoItem;\nimport org.bonitasoft.web.rest.server.api.ConsoleAPI;\nimport org.bonitasoft.web.rest.server.engineclient.CustomUserInfoEngineClient;\nimport org.bonitasoft.web.rest.server.engineclient.CustomUserInfoEngineClientCreator;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasSearch;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.i18n.T_;\nimport org.bonitasoft.web.toolkit.client.common.texttemplate.Arg;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Vincent Elcrin\n */\npublic class APICustomUserInfoUser extends ConsoleAPI<CustomUserInfoItem> implements APIHasSearch<CustomUserInfoItem> {\n\n    public static final String FIX_ORDER = \"Fix order\";\n\n    private final CustomUserInfoEngineClientCreator engineClientCreator;\n\n    private final CustomUserInfoConverter converter = new CustomUserInfoConverter();\n\n    public APICustomUserInfoUser(CustomUserInfoEngineClientCreator engineClientCreator) {\n        this.engineClientCreator = engineClientCreator;\n    }\n\n    @Override\n    public ItemSearchResult<CustomUserInfoItem> search(int page, int resultsByPage, String search, String orders,\n            Map<String, String> filters) {\n        check(containsOnly(FILTER_USER_ID, filters),\n                new T_(\"The only mandatory filter is %name%\", new Arg(\"name\", FILTER_USER_ID)));\n        check(orders.equals(FIX_ORDER), new T_(\"Sorting is not supported by this API\"));\n        check(search == null, new T_(\"Search terms are not supported by this API\"));\n\n        CustomUserInfoEngineClient client = engineClientCreator.create(getEngineSession());\n        List<CustomUserInfo> items = client.listCustomInformation(\n                Long.parseLong(filters.get(FILTER_USER_ID)),\n                page * resultsByPage,\n                resultsByPage);\n\n        List<CustomUserInfoItem> information = new ArrayList<>();\n        for (CustomUserInfo item : items) {\n            information.add(converter.convert(item));\n        }\n        return new ItemSearchResult<>(page, information.size(), client.countDefinitions(), information);\n    }\n\n    @Override\n    protected ItemDefinition<CustomUserInfoItem> defineItemDefinition() {\n        return CustomUserInfoDefinition.get();\n    }\n\n    @Override\n    public String defineDefaultSearchOrder() {\n        return FIX_ORDER;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/organization/APICustomUserInfoValue.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.organization;\n\nimport static org.bonitasoft.web.rest.model.identity.CustomUserInfoItem.ATTRIBUTE_VALUE;\nimport static org.bonitasoft.web.rest.server.api.APIPreconditions.check;\nimport static org.bonitasoft.web.rest.server.api.APIPreconditions.containsOnly;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.identity.CustomUserInfoValue;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.web.rest.model.identity.CustomUserInfoDefinition;\nimport org.bonitasoft.web.rest.model.identity.CustomUserInfoItem;\nimport org.bonitasoft.web.rest.server.api.ConsoleAPI;\nimport org.bonitasoft.web.rest.server.datastore.converter.ItemSearchResultConverter;\nimport org.bonitasoft.web.rest.server.datastore.filter.Filters;\nimport org.bonitasoft.web.rest.server.datastore.filter.GenericFilterCreator;\nimport org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator;\nimport org.bonitasoft.web.rest.server.datastore.utils.Sorts;\nimport org.bonitasoft.web.rest.server.engineclient.CustomUserInfoEngineClient;\nimport org.bonitasoft.web.rest.server.engineclient.CustomUserInfoEngineClientCreator;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasSearch;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasUpdate;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.i18n.T_;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Vincent Elcrin\n */\npublic class APICustomUserInfoValue extends ConsoleAPI<CustomUserInfoItem>\n        implements APIHasSearch<CustomUserInfoItem>, APIHasUpdate<CustomUserInfoItem> {\n\n    private final CustomUserInfoEngineClientCreator engineClientCreator;\n\n    private final CustomUserInfoConverter converter = new CustomUserInfoConverter();\n\n    public APICustomUserInfoValue(CustomUserInfoEngineClientCreator engineClientCreator) {\n        this.engineClientCreator = engineClientCreator;\n    }\n\n    @Override\n    public ItemSearchResult<CustomUserInfoItem> search(int page, int resultsByPage, String search, String orders,\n            Map<String, String> filters) {\n        SearchResult<CustomUserInfoValue> result = getClient().searchCustomUserInfoValues(new SearchOptionsCreator(\n                page,\n                resultsByPage,\n                search,\n                new Sorts(orders),\n                new Filters(filters, new GenericFilterCreator(new CustomUserInfoAttributeConverter()))).create());\n        return new ItemSearchResultConverter<>(\n                page,\n                resultsByPage,\n                result,\n                new CustomUserInfoConverter()).toItemSearchResult();\n    }\n\n    @Override\n    public String defineDefaultSearchOrder() {\n        return ATTRIBUTE_VALUE + \" ASC\";\n    }\n\n    @Override\n    public CustomUserInfoItem update(APIID id, Map<String, String> attributes) {\n        check(containsOnly(ATTRIBUTE_VALUE, attributes), new T_(\"Only the value attribute can be updated\"));\n\n        return converter.convert(getClient().setCustomUserInfoValue(\n                id.getPartAsLong(1),\n                id.getPartAsLong(0),\n                attributes.get(ATTRIBUTE_VALUE)));\n    }\n\n    private CustomUserInfoEngineClient getClient() {\n        return engineClientCreator.create(getEngineSession());\n    }\n\n    @Override\n    protected ItemDefinition<CustomUserInfoItem> defineItemDefinition() {\n        return CustomUserInfoDefinition.get();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/organization/APIGroup.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.organization;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.identity.Group;\nimport org.bonitasoft.engine.identity.GroupCriterion;\nimport org.bonitasoft.web.rest.model.identity.GroupDefinition;\nimport org.bonitasoft.web.rest.model.identity.GroupItem;\nimport org.bonitasoft.web.rest.server.api.ConsoleAPI;\nimport org.bonitasoft.web.rest.server.datastore.organization.GroupDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasAdd;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasDelete;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasGet;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasSearch;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasUpdate;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Nicolas Tith\n */\npublic class APIGroup extends ConsoleAPI<GroupItem> implements\n        APIHasAdd<GroupItem>,\n        APIHasGet<GroupItem>,\n        APIHasDelete,\n        APIHasSearch<GroupItem>,\n        APIHasUpdate<GroupItem> {\n\n    @Override\n    protected ItemDefinition<GroupItem> defineItemDefinition() {\n        return GroupDefinition.get();\n    }\n\n    @Override\n    protected GroupDatastore defineDefaultDatastore() {\n        return new GroupDatastore(getEngineSession());\n    }\n\n    @Override\n    public String defineDefaultSearchOrder() {\n        return GroupCriterion.NAME_ASC.toString();\n    }\n\n    @Override\n    protected void fillDeploys(final GroupItem item, final List<String> deploys) {\n        if (deploys.contains(GroupItem.ATTRIBUTE_PARENT_GROUP_ID) && item.getParentPath() != null\n                && !item.getParentPath().isEmpty()) {\n            Group parentGroup = ((GroupDatastore) getDefaultDatastore()).getGroupEngineClient()\n                    .getGroupByPath(item.getParentPath());\n            item.setParentGroupId(String.valueOf(parentGroup.getId()));\n        }\n    }\n\n    @Override\n    protected void fillCounters(final GroupItem item, final List<String> counters) {\n        if (counters.contains(GroupItem.COUNTER_NUMBER_OF_USERS)) {\n            item.setAttribute(GroupItem.COUNTER_NUMBER_OF_USERS,\n                    ((GroupDatastore) getDefaultDatastore()).getNumberOfUsers(item.getId()));\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/organization/APIMembership.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.organization;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.identity.UserMembershipCriterion;\nimport org.bonitasoft.web.rest.model.identity.MembershipDefinition;\nimport org.bonitasoft.web.rest.model.identity.MembershipItem;\nimport org.bonitasoft.web.rest.server.api.ConsoleAPI;\nimport org.bonitasoft.web.rest.server.datastore.organization.GroupDatastore;\nimport org.bonitasoft.web.rest.server.datastore.organization.MembershipDatastore;\nimport org.bonitasoft.web.rest.server.datastore.organization.RoleDatastore;\nimport org.bonitasoft.web.rest.server.datastore.organization.UserDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasAdd;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasDelete;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasSearch;\nimport org.bonitasoft.web.rest.server.framework.api.Datastore;\nimport org.bonitasoft.web.rest.server.framework.exception.APIFilterException;\nimport org.bonitasoft.web.rest.server.framework.exception.APIFilterMandatoryException;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Séverin Moussel\n */\npublic class APIMembership extends ConsoleAPI<MembershipItem> implements\n        APIHasAdd<MembershipItem>,\n        APIHasSearch<MembershipItem>,\n        APIHasDelete {\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // CONFIGURATION\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    protected ItemDefinition defineItemDefinition() {\n        return Definitions.get(MembershipDefinition.TOKEN);\n    }\n\n    @Override\n    protected List<String> defineReadOnlyAttributes() {\n        return Arrays.asList(\n                MembershipItem.ATTRIBUTE_ASSIGNED_BY_USER_ID,\n                MembershipItem.ATTRIBUTE_ASSIGNED_DATE);\n    }\n\n    @Override\n    protected void fillDeploys(final MembershipItem item, final List<String> deploys) {\n        if (isDeployable(MembershipItem.ATTRIBUTE_USER_ID, deploys, item)) {\n            item.setDeploy(MembershipItem.ATTRIBUTE_USER_ID,\n                    new UserDatastore(getEngineSession()).get(item.getUserId()));\n        }\n\n        if (isDeployable(MembershipItem.ATTRIBUTE_ROLE_ID, deploys, item)) {\n            item.setDeploy(MembershipItem.ATTRIBUTE_ROLE_ID,\n                    new RoleDatastore(getEngineSession()).get(item.getRoleId()));\n        }\n\n        if (isDeployable(MembershipItem.ATTRIBUTE_GROUP_ID, deploys, item)) {\n            item.setDeploy(MembershipItem.ATTRIBUTE_GROUP_ID,\n                    new GroupDatastore(getEngineSession()).get(item.getGroupId()));\n        }\n\n        if (isDeployable(MembershipItem.ATTRIBUTE_ASSIGNED_BY_USER_ID, deploys, item)) {\n            item.setDeploy(MembershipItem.ATTRIBUTE_ASSIGNED_BY_USER_ID,\n                    new UserDatastore(getEngineSession()).get(item.getAssignedByUserId()));\n        }\n    }\n\n    @Override\n    protected Datastore defineDefaultDatastore() {\n        return new MembershipDatastore(getEngineSession());\n    }\n\n    @Override\n    public String defineDefaultSearchOrder() {\n        return UserMembershipCriterion.ROLE_NAME_ASC.name();\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // CRUDS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public ItemSearchResult<MembershipItem> search(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n\n        // user_id filter mandatory\n        if (filters.size() == 0) {\n            throw new APIFilterMandatoryException(MembershipItem.ATTRIBUTE_USER_ID);\n        }\n        // only user_id filter allowed\n        else if (filters.size() > 1 || !filters.containsKey(MembershipItem.ATTRIBUTE_USER_ID)) {\n            throw new APIFilterException(\"Cant search on filter other than \" + MembershipItem.ATTRIBUTE_USER_ID);\n        }\n\n        return super.search(page, resultsByPage, search, orders, filters);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/organization/APIPersonalContactData.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.organization;\n\nimport org.bonitasoft.web.rest.model.identity.PersonalContactDataDefinition;\nimport org.bonitasoft.web.rest.model.identity.PersonalContactDataItem;\nimport org.bonitasoft.web.rest.server.api.ConsoleAPI;\nimport org.bonitasoft.web.rest.server.datastore.organization.PersonalContactDataDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasAdd;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasGet;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasUpdate;\nimport org.bonitasoft.web.rest.server.framework.api.Datastore;\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Paul AMAR\n */\npublic class APIPersonalContactData extends ConsoleAPI<PersonalContactDataItem>\n        implements APIHasGet<PersonalContactDataItem>,\n        APIHasUpdate<PersonalContactDataItem>, APIHasAdd<PersonalContactDataItem> {\n\n    @Override\n    protected ItemDefinition defineItemDefinition() {\n        return Definitions.get(PersonalContactDataDefinition.TOKEN);\n    }\n\n    @Override\n    protected Datastore defineDefaultDatastore() {\n        return new PersonalContactDataDatastore(getEngineSession());\n    }\n\n    @Override\n    public PersonalContactDataItem add(final PersonalContactDataItem item) {\n        return super.add(item);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/organization/APIProfessionalContactData.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.organization;\n\nimport org.bonitasoft.web.rest.model.identity.ProfessionalContactDataDefinition;\nimport org.bonitasoft.web.rest.model.identity.ProfessionalContactDataItem;\nimport org.bonitasoft.web.rest.server.api.ConsoleAPI;\nimport org.bonitasoft.web.rest.server.datastore.organization.ProfessionalContactDataDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasAdd;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasGet;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasUpdate;\nimport org.bonitasoft.web.rest.server.framework.api.Datastore;\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Paul AMAR\n */\npublic class APIProfessionalContactData extends ConsoleAPI<ProfessionalContactDataItem>\n        implements APIHasGet<ProfessionalContactDataItem>,\n        APIHasUpdate<ProfessionalContactDataItem>, APIHasAdd<ProfessionalContactDataItem> {\n\n    @Override\n    protected ItemDefinition defineItemDefinition() {\n        return Definitions.get(ProfessionalContactDataDefinition.TOKEN);\n    }\n\n    @Override\n    protected Datastore defineDefaultDatastore() {\n        return new ProfessionalContactDataDatastore(getEngineSession());\n    }\n\n    @Override\n    public ProfessionalContactDataItem add(final ProfessionalContactDataItem item) {\n        return super.add(item);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/organization/APIRole.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.organization;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.identity.RoleCriterion;\nimport org.bonitasoft.web.rest.model.identity.RoleDefinition;\nimport org.bonitasoft.web.rest.model.identity.RoleItem;\nimport org.bonitasoft.web.rest.server.api.ConsoleAPI;\nimport org.bonitasoft.web.rest.server.datastore.organization.RoleDatastore;\nimport org.bonitasoft.web.rest.server.datastore.organization.UserDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasAdd;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasDelete;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasGet;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasSearch;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasUpdate;\nimport org.bonitasoft.web.rest.server.framework.api.Datastore;\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\npublic class APIRole extends ConsoleAPI<RoleItem> implements\n        APIHasGet<RoleItem>,\n        APIHasSearch<RoleItem>,\n        APIHasUpdate<RoleItem>,\n        APIHasAdd<RoleItem>,\n        APIHasDelete {\n\n    @Override\n    protected ItemDefinition defineItemDefinition() {\n        return Definitions.get(RoleDefinition.TOKEN);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // UTILS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    protected void fillDeploys(final RoleItem item, final List<String> deploys) {\n        if (isDeployable(RoleItem.ATTRIBUTE_CREATED_BY_USER_ID, deploys, item)) {\n            item.setDeploy(RoleItem.ATTRIBUTE_CREATED_BY_USER_ID,\n                    new UserDatastore(getEngineSession()).get(item.getCreatedByUserId()));\n        }\n    }\n\n    @Override\n    protected void fillCounters(final RoleItem item, final List<String> counters) {\n        if (counters.contains(RoleItem.COUNTER_NUMBER_OF_USERS)) {\n            item.setAttribute(RoleItem.COUNTER_NUMBER_OF_USERS,\n                    ((RoleDatastore) defineDefaultDatastore()).getNumberOfUsers(item.getId()));\n        }\n    }\n\n    @Override\n    protected List<String> defineReadOnlyAttributes() {\n        return Arrays.asList(\n                RoleItem.ATTRIBUTE_CREATED_BY_USER_ID,\n                RoleItem.ATTRIBUTE_CREATION_DATE,\n                RoleItem.ATTRIBUTE_LAST_UPDATE_DATE);\n    }\n\n    @Override\n    protected Datastore defineDefaultDatastore() {\n        return new RoleDatastore(getEngineSession());\n    }\n\n    @Override\n    public String defineDefaultSearchOrder() {\n        return RoleCriterion.DISPLAY_NAME_ASC.name();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/organization/APIUser.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.organization;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.console.common.server.preferences.properties.PropertiesFactory;\nimport org.bonitasoft.web.rest.model.identity.UserDefinition;\nimport org.bonitasoft.web.rest.model.identity.UserItem;\nimport org.bonitasoft.web.rest.server.api.ConsoleAPI;\nimport org.bonitasoft.web.rest.server.datastore.bpm.flownode.HumanTaskDatastore;\nimport org.bonitasoft.web.rest.server.datastore.organization.PersonalContactDataDatastore;\nimport org.bonitasoft.web.rest.server.datastore.organization.ProfessionalContactDataDatastore;\nimport org.bonitasoft.web.rest.server.datastore.organization.UserDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.*;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.util.MapUtil;\nimport org.bonitasoft.web.toolkit.client.common.util.StringUtil;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ValidationError;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ValidationException;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.AbstractStringValidator;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Séverin Moussel\n */\n// TODO : implements APIhasFile\npublic class APIUser extends ConsoleAPI<UserItem> implements APIHasAdd<UserItem>, APIHasDelete, APIHasUpdate<UserItem>,\n        APIHasGet<UserItem>, APIHasSearch<UserItem> {\n\n    /**\n     * Logger\n     */\n    private static final Logger LOGGER = LoggerFactory.getLogger(APIUser.class.getName());\n\n    @Override\n    protected ItemDefinition<UserItem> defineItemDefinition() {\n        return UserDefinition.get();\n    }\n\n    @Override\n    public String defineDefaultSearchOrder() {\n        return UserItem.ATTRIBUTE_LASTNAME;\n    }\n\n    @Override\n    public UserItem add(final UserItem item) {\n\n        // Finish the upload of the icon\n        if (StringUtil.isBlank(item.getPassword())) {\n            throw new ValidationException(\n                    Collections.singletonList(new ValidationError(\"Password\", \"%attribute% is mandatory\")));\n        }\n        checkPasswordRobustness(item.getPassword());\n\n        // Add\n        return super.add(item);\n\n    }\n\n    @Override\n    protected Datastore defineDefaultDatastore() {\n        return new UserDatastore(getEngineSession());\n    }\n\n    private void checkPasswordRobustness(final String password) {\n        try {\n            final Class<?> validatorClass = Class.forName(getValidatorClassName());\n            Object instanceClass;\n            try {\n                instanceClass = validatorClass.newInstance();\n                final AbstractStringValidator validator = (AbstractStringValidator) instanceClass;\n                validator.setLocale(getLocale());\n                validator.check(password);\n                if (!validator.getErrors().isEmpty()) {\n                    throw new ValidationException(validator.getErrors());\n                }\n            } catch (final InstantiationException e) {\n                if (LOGGER.isErrorEnabled()) {\n                    LOGGER.error(\"Error while instantiating the class\", e);\n                }\n                e.printStackTrace();\n            } catch (final IllegalAccessException e) {\n                if (LOGGER.isErrorEnabled()) {\n                    LOGGER.error(\"Illegal access with the file \", e);\n                }\n                e.printStackTrace();\n            }\n        } catch (final ClassNotFoundException e) {\n            if (LOGGER.isErrorEnabled()) {\n                LOGGER.error(\"Class not found\", e);\n            }\n            e.printStackTrace();\n        }\n    }\n\n    String getValidatorClassName() {\n        return PropertiesFactory.getSecurityProperties().getPasswordValidator();\n    }\n\n    @Override\n    public UserItem update(final APIID id, final Map<String, String> item) {\n        // Do not update password if not set\n        MapUtil.removeIfBlank(item, UserItem.ATTRIBUTE_PASSWORD);\n        if (item.get(UserItem.ATTRIBUTE_PASSWORD) != null) {\n            checkPasswordRobustness(item.get(UserItem.ATTRIBUTE_PASSWORD));\n        }\n        return super.update(id, item);\n    }\n\n    @Override\n    public UserItem get(final APIID id) {\n        final UserItem item = super.get(id);\n        if (item != null) {\n\n            // Do not let the password output from the API\n            item.setPassword(null);\n            final String iconPath = item.getIcon();\n            if (iconPath == null || iconPath.isEmpty()) {\n                item.setIcon(UserItem.DEFAULT_USER_ICON);\n            }\n        }\n\n        return item;\n    }\n\n    @Override\n    public ItemSearchResult<UserItem> search(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n\n        final ItemSearchResult<UserItem> results = super.search(page, resultsByPage, search, orders, filters);\n\n        for (final UserItem item : results.getResults()) {\n            if (item != null) {\n                // Do not let the password output from the API\n                item.setPassword(null);\n            }\n        }\n\n        return results;\n    }\n\n    @Override\n    public void delete(final List<APIID> ids) {\n        super.delete(ids);\n    }\n\n    @Override\n    protected void fillDeploys(final UserItem item, final List<String> deploys) {\n        if (isDeployable(UserItem.ATTRIBUTE_MANAGER_ID, deploys, item)) {\n            item.setDeploy(UserItem.ATTRIBUTE_MANAGER_ID,\n                    ((UserDatastore) getDefaultDatastore()).get(item.getManagerId()));\n        }\n\n        if (isDeployable(UserItem.ATTRIBUTE_CREATED_BY_USER_ID, deploys, item)) {\n            item.setDeploy(UserItem.ATTRIBUTE_CREATED_BY_USER_ID,\n                    ((UserDatastore) getDefaultDatastore()).get(item.getCreatedByUserId()));\n        }\n\n        if (deploys.contains(UserItem.DEPLOY_PERSONAL_DATA)) {\n            item.setDeploy(UserItem.DEPLOY_PERSONAL_DATA,\n                    new PersonalContactDataDatastore(getEngineSession()).get(item.getId()));\n\n            // not a real deploy. force attribute to fix json conversion (Item#toJson)\n            item.setAttribute(UserItem.DEPLOY_PERSONAL_DATA, (String) null);\n        }\n\n        if (deploys.contains(UserItem.DEPLOY_PROFESSIONAL_DATA)) {\n            item.setDeploy(UserItem.DEPLOY_PROFESSIONAL_DATA,\n                    new ProfessionalContactDataDatastore(getEngineSession()).get(item.getId()));\n\n            // not a real deploy. force attribute to fix json conversion (Item#toJson)\n            item.setAttribute(UserItem.DEPLOY_PROFESSIONAL_DATA, (String) null);\n        }\n\n    }\n\n    @Override\n    protected void fillCounters(final UserItem item, final List<String> counters) {\n\n        if (counters.contains(UserItem.COUNTER_OPEN_TASKS)) {\n            item.setAttribute(UserItem.COUNTER_OPEN_TASKS,\n                    new HumanTaskDatastore(getEngineSession()).getNumberOfOpenTasks(item.getId()));\n        }\n\n        if (counters.contains(UserItem.COUNTER_OVERDUE_TASKS)) {\n            item.setAttribute(UserItem.COUNTER_OVERDUE_TASKS,\n                    new HumanTaskDatastore(getEngineSession()).getNumberOfOverdueOpenTasks(item.getId()));\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/organization/CustomUserInfoAttributeConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.organization;\n\nimport static org.bonitasoft.engine.identity.CustomUserInfoValueSearchDescriptor.*;\nimport static org.bonitasoft.web.rest.model.identity.CustomUserInfoItem.*;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * @author Vincent Elcrin\n */\npublic class CustomUserInfoAttributeConverter implements AttributeConverter {\n\n    private static final Map<String, String> attributes = new HashMap<>();\n\n    static {\n        attributes.put(ATTRIBUTE_USER_ID, USER_ID);\n        attributes.put(ATTRIBUTE_DEFINITION_ID, DEFINITION_ID);\n        attributes.put(ATTRIBUTE_VALUE, VALUE);\n    }\n\n    @Override\n    public String convert(String attribute) {\n        return attributes.get(attribute);\n    }\n\n    @Override\n    public Map<String, ItemAttribute.TYPE> getValueTypeMapping() {\n        return Collections.emptyMap();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/organization/CustomUserInfoConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.organization;\n\nimport org.bonitasoft.engine.identity.CustomUserInfo;\nimport org.bonitasoft.engine.identity.CustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.CustomUserInfoValue;\nimport org.bonitasoft.web.rest.model.identity.CustomUserInfoDefinitionItem;\nimport org.bonitasoft.web.rest.model.identity.CustomUserInfoItem;\nimport org.bonitasoft.web.rest.server.datastore.converter.ItemConverter;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Vincent Elcrin\n */\npublic class CustomUserInfoConverter extends ItemConverter<CustomUserInfoItem, CustomUserInfoValue> {\n\n    public CustomUserInfoDefinitionItem convert(CustomUserInfoDefinition definition) {\n        CustomUserInfoDefinitionItem item = new CustomUserInfoDefinitionItem();\n        item.setId(APIID.makeAPIID(definition.getId()));\n        item.setName(definition.getName());\n        item.setDescription(definition.getDescription());\n        return item;\n    }\n\n    public CustomUserInfoItem convert(CustomUserInfo information) {\n        CustomUserInfoItem item = new CustomUserInfoItem();\n        item.setUserId(information.getUserId());\n        item.setDefinition(convert(information.getDefinition()));\n        item.setValue(information.getValue());\n        return item;\n    }\n\n    @Override\n    public CustomUserInfoItem convert(CustomUserInfoValue value) {\n        CustomUserInfoItem item = new CustomUserInfoItem();\n        item.setUserId(value.getUserId());\n        item.setDefinition(APIID.makeAPIID(value.getDefinitionId()));\n        item.setValue(value.getValue());\n        return item;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/organization/password/validator/DefaultPasswordValidator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.organization.password.validator;\n\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.AbstractStringValidator;\n\n/**\n * Do not delete: this class is used by reflection !\n * See 'security.password.validator' in file 'security-config.properties'\n *\n * @author Paul AMAR\n */\npublic class DefaultPasswordValidator extends AbstractStringValidator {\n\n    @Override\n    protected void _check(String password) {\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/organization/password/validator/RobustnessPasswordValidator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.organization.password.validator;\n\nimport static org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n.t_;\n\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n;\nimport org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n.LOCALE;\nimport org.bonitasoft.web.toolkit.client.common.texttemplate.Arg;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.AbstractStringValidator;\n\n/**\n * Do not delete: this class can be used by reflection !\n * See 'security.password.validator' in file 'security-config.properties'\n * It is referenced in the documentation as an alternative Password Validation\n *\n * @author Paul AMAR\n */\npublic class RobustnessPasswordValidator extends AbstractStringValidator {\n\n    @Override\n    protected void _check(String password) {\n        String regex;\n\n        LOCALE Locale = AbstractI18n.stringToLocale(locale);\n\n        // Check number of digits\n        regex = \"[0-9]\";\n        int numberMinOccurences = 3;\n        if (numberOfOccurenceOfRegex(regex, password) < numberMinOccurences) {\n            addError(t_(\"Password must contain at least %number% digits\", Locale,\n                    new Arg(\"number\", numberMinOccurences)));\n        }\n\n        // Check number of lower case chars\n        regex = \"[a-z]\";\n        numberMinOccurences = 2;\n        if (numberOfOccurenceOfRegex(regex, password) < numberMinOccurences) {\n            addError(t_(\"Password must contain at least %number% lower case characters\", Locale,\n                    new Arg(\"number\", numberMinOccurences)));\n        }\n\n        // Check number of upper case chars\n        regex = \"[A-Z]\";\n        numberMinOccurences = 2;\n        if (numberOfOccurenceOfRegex(regex, password) < numberMinOccurences) {\n            addError(t_(\"Password must contain at least %number% upper case characters\", Locale,\n                    new Arg(\"number\", numberMinOccurences)));\n        }\n\n        // Check number of special chars\n        regex = \"[~@#\\\\^\\\\$&\\\\*\\\\(\\\\)-T_\\\\+=\\\\[\\\\]\\\\{\\\\}\\\\|\\\\,\\\\.\\\\?]\";\n        numberMinOccurences = 2;\n        if (numberOfOccurenceOfRegex(regex, password) < numberMinOccurences) {\n            addError(t_(\"Password must contain at least %number% special characters\", Locale,\n                    new Arg(\"number\", numberMinOccurences)));\n        }\n\n        // Check number of length\n        int minimalLength = 10;\n        if (password.length() < minimalLength) {\n            addError(\n                    t_(\"Password must be at least %number% characters long\", Locale, new Arg(\"number\", minimalLength)));\n        }\n    }\n\n    private int numberOfOccurenceOfRegex(String regex, String password) {\n        Pattern pattern = Pattern.compile(regex);\n        Matcher matcher = pattern.matcher(password);\n\n        int count = 0;\n        while (matcher.find())\n            count++;\n\n        return count;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/page/APIPage.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.page;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils;\nimport org.bonitasoft.engine.api.PageAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.web.rest.model.portal.page.PageDefinition;\nimport org.bonitasoft.web.rest.model.portal.page.PageItem;\nimport org.bonitasoft.web.rest.server.api.ConsoleAPI;\nimport org.bonitasoft.web.rest.server.api.deployer.DeployerFactory;\nimport org.bonitasoft.web.rest.server.datastore.page.PageDatastore;\nimport org.bonitasoft.web.rest.server.datastore.page.PageDatastoreFactory;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasAdd;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasDelete;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasGet;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasSearch;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasUpdate;\nimport org.bonitasoft.web.rest.server.framework.api.Datastore;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Fabio Lombardi\n */\npublic class APIPage extends ConsoleAPI<PageItem>\n        implements APIHasGet<PageItem>, APIHasSearch<PageItem>, APIHasAdd<PageItem>, APIHasUpdate<PageItem>,\n        APIHasDelete {\n\n    @Override\n    protected ItemDefinition<PageItem> defineItemDefinition() {\n        return PageDefinition.get();\n    }\n\n    @Override\n    protected Datastore defineDefaultDatastore() {\n        return getPageDatastore();\n    }\n\n    @Override\n    public PageItem get(final APIID id) {\n        return getPageDatastore().get(id);\n    }\n\n    /**\n     * @deprecated as of 9.0.0, a page should be created at startup.\n     */\n    @Override\n    @Deprecated(since = \"9.0.0\")\n    public PageItem add(final PageItem item) {\n        return getPageDatastore().add(item);\n    }\n\n    /**\n     * @deprecated as of 9.0.0, a page should be updated at startup.\n     */\n    @Override\n    @Deprecated(since = \"9.0.0\")\n    public PageItem update(final APIID id, final Map<String, String> attributes) {\n        return getPageDatastore().update(id, attributes);\n    }\n\n    @Override\n    public void delete(final List<APIID> ids) {\n        getPageDatastore().delete(ids);\n    }\n\n    @Override\n    public ItemSearchResult<PageItem> search(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n        return getPageDatastore().search(page, resultsByPage, search, orders, filters);\n    }\n\n    @Override\n    protected void fillDeploys(final PageItem item, final List<String> deploys) {\n        /*\n         * Need to be done there and not in constructor\n         * because need the engine session which is set\n         * by setter instead of being injected in API constructor...\n         */\n        addDeployer(getDeployerFactory().createUserDeployer(PageItem.ATTRIBUTE_CREATED_BY_USER_ID));\n        addDeployer(getDeployerFactory().createUserDeployer(PageItem.ATTRIBUTE_UPDATED_BY_USER_ID));\n        super.fillDeploys(item, deploys);\n    }\n\n    protected DeployerFactory getDeployerFactory() {\n        return new DeployerFactory(getEngineSession());\n    }\n\n    @Override\n    public String defineDefaultSearchOrder() {\n        return PageItem.ATTRIBUTE_URL_TOKEN;\n    }\n\n    private PageDatastore getPageDatastore() {\n        PageAPI pageAPI;\n        try {\n            pageAPI = TenantAPIAccessor.getCustomPageAPI(getEngineSession());\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n        final WebBonitaConstantsUtils constants = WebBonitaConstantsUtils.getTenantInstance();\n        final PageDatastoreFactory pageDatastoreFactory = new PageDatastoreFactory();\n        return pageDatastoreFactory.create(getEngineSession(), constants, pageAPI);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/platform/APIPlatform.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.platform;\n\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.PlatformAPI;\nimport org.bonitasoft.engine.api.PlatformAPIAccessor;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.platform.Platform;\nimport org.bonitasoft.engine.platform.PlatformNotFoundException;\nimport org.bonitasoft.engine.platform.PlatformState;\nimport org.bonitasoft.engine.platform.StartNodeException;\nimport org.bonitasoft.web.rest.model.platform.PlatformDefinition;\nimport org.bonitasoft.web.rest.model.platform.PlatformItem;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasGet;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasUpdate;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.ui.utils.DateFormat;\n\n/**\n * @author Séverin Moussel\n */\npublic class APIPlatform extends org.bonitasoft.web.rest.server.api.PlatformAPI<PlatformItem>\n        implements APIHasGet<PlatformItem>, APIHasUpdate<PlatformItem> {\n\n    @Override\n    protected ItemDefinition defineItemDefinition() {\n        return Definitions.get(PlatformDefinition.TOKEN);\n    }\n\n    @Override\n    public PlatformItem update(final APIID id, final Map<String, String> attributes) {\n        final String platformState = attributes.get(PlatformItem.ATTRIBUTE_STATE);\n        try {\n            final PlatformAPI platformAPI = getPlatformAPI();\n            if (platformAPI.isPlatformCreated()) {\n                try {\n                    if (platformState.equals(\"start\")) {\n                        platformAPI.startNode();\n                    } else if (platformState.equals(\"stop\")) {\n                        platformAPI.stopNode();\n                    }\n                } catch (final StartNodeException sne) {\n                    throw new APIException(sne);\n                }\n            }\n            return get(null);\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @Override\n    public PlatformItem get(final APIID id) {\n        PlatformItem platformItem;\n        try {\n            final PlatformAPI platformAPI = getPlatformAPI();\n            final Platform platform = platformAPI.getPlatform();\n            final String createdDate = DateFormat.dateToSql(new Date(platform.getCreated()));\n            final PlatformState platformState = platformAPI.getPlatformState();\n            String platformStateStr = \"\";\n            if (platformState != null) {\n                platformStateStr = platformState.toString();\n            }\n            platformItem = new PlatformItem(platform.getVersion(), platform.getInitialVersion(), createdDate,\n                    platform.getCreatedBy(), platformStateStr);\n            return platformItem;\n        } catch (final PlatformNotFoundException ex) {\n            platformItem = new PlatformItem();\n            return platformItem;\n        }\n    }\n\n    private PlatformAPI getPlatformAPI() {\n        try {\n            return PlatformAPIAccessor.getPlatformAPI(getPlatformSession());\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @Override\n    protected void fillDeploys(final PlatformItem item, final List<String> deploys) {\n    }\n\n    @Override\n    protected void fillCounters(final PlatformItem item, final List<String> counters) {\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/platform/SystemInformationController.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.platform;\n\nimport java.util.Map;\n\nimport javax.servlet.http.HttpSession;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.api.platform.PlatformInformationAPI;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.server.api.AbstractRESTController;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@Slf4j\n@RestController\n@RequestMapping(\"/API/system/information\")\npublic class SystemInformationController extends AbstractRESTController {\n\n    @GetMapping\n    public Map<String, String> getPlatformInfo(HttpSession session) {\n        try {\n            return getPlatformInformationAPI(getApiSession(session)).getPlatformInformation();\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    protected PlatformInformationAPI getPlatformInformationAPI(APISession apiSession)\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return TenantAPIAccessor.getPlatformInformationAPI(apiSession);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/profile/APIProfile.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.profile;\n\nimport java.util.List;\n\nimport org.bonitasoft.web.rest.model.portal.page.PageItem;\nimport org.bonitasoft.web.rest.model.portal.profile.ProfileDefinition;\nimport org.bonitasoft.web.rest.model.portal.profile.ProfileItem;\nimport org.bonitasoft.web.rest.server.api.ConsoleAPI;\nimport org.bonitasoft.web.rest.server.api.deployer.DeployerFactory;\nimport org.bonitasoft.web.rest.server.datastore.ComposedDatastore;\nimport org.bonitasoft.web.rest.server.datastore.profile.GetProfileHelper;\nimport org.bonitasoft.web.rest.server.datastore.profile.SearchProfilesHelper;\nimport org.bonitasoft.web.rest.server.engineclient.EngineAPIAccessor;\nimport org.bonitasoft.web.rest.server.engineclient.EngineClientFactory;\nimport org.bonitasoft.web.rest.server.engineclient.ProfileEngineClient;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasGet;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasSearch;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Nicolas Tith\n * @author Séverin Moussel\n */\npublic class APIProfile extends ConsoleAPI<ProfileItem> implements APIHasGet<ProfileItem>, APIHasSearch<ProfileItem> {\n\n    @Override\n    protected void fillDeploys(ProfileItem item, List<String> deploys) {\n        addDeployer(getDeployerFactory().createUserDeployer(PageItem.ATTRIBUTE_CREATED_BY_USER_ID));\n        addDeployer(getDeployerFactory().createUserDeployer(PageItem.ATTRIBUTE_UPDATED_BY_USER_ID));\n\n        super.fillDeploys(item, deploys);\n    }\n\n    protected DeployerFactory getDeployerFactory() {\n        return new DeployerFactory(getEngineSession());\n    }\n\n    @Override\n    protected ComposedDatastore<ProfileItem> defineDefaultDatastore() {\n\n        final ProfileEngineClient profileClient = createProfileEngineClient();\n\n        final ComposedDatastore<ProfileItem> datastore = new ComposedDatastore<>();\n        datastore.setGetHelper(new GetProfileHelper(profileClient));\n        datastore.setSearchHelper(new SearchProfilesHelper(profileClient));\n        return datastore;\n    }\n\n    private ProfileEngineClient createProfileEngineClient() {\n        return new EngineClientFactory(new EngineAPIAccessor(getEngineSession())).createProfileEngineClient();\n    }\n\n    @Override\n    protected ItemDefinition<ProfileItem> defineItemDefinition() {\n        return ProfileDefinition.get();\n    }\n\n    @Override\n    public String defineDefaultSearchOrder() {\n        // FIXME Use an engine descriptor\n        return \"name ASC\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/profile/APIProfileMember.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.profile;\n\nimport java.util.List;\n\nimport org.bonitasoft.web.rest.model.portal.profile.ProfileMemberDefinition;\nimport org.bonitasoft.web.rest.model.portal.profile.ProfileMemberItem;\nimport org.bonitasoft.web.rest.server.api.deployer.DeployerFactory;\nimport org.bonitasoft.web.rest.server.datastore.ComposedDatastore;\nimport org.bonitasoft.web.rest.server.datastore.profile.member.AddProfileMemberHelper;\nimport org.bonitasoft.web.rest.server.datastore.profile.member.DeleteProfileMemberHelper;\nimport org.bonitasoft.web.rest.server.datastore.profile.member.SearchProfileMembersHelper;\nimport org.bonitasoft.web.rest.server.engineclient.EngineAPIAccessor;\nimport org.bonitasoft.web.rest.server.engineclient.EngineClientFactory;\nimport org.bonitasoft.web.rest.server.engineclient.ProfileMemberEngineClient;\nimport org.bonitasoft.web.rest.server.framework.api.Datastore;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Nicolas Tith\n * @author Séverin Moussel\n */\npublic class APIProfileMember extends AbstractAPIMember<ProfileMemberItem> {\n\n    @Override\n    protected ItemDefinition<ProfileMemberItem> defineItemDefinition() {\n        return ProfileMemberDefinition.get();\n    }\n\n    @Override\n    protected Datastore defineDefaultDatastore() {\n\n        ProfileMemberEngineClient engineClient = createProfileMemberEngineClient();\n\n        ComposedDatastore<ProfileMemberItem> datastore = new ComposedDatastore<>();\n        datastore.setAddHelper(new AddProfileMemberHelper(engineClient));\n        datastore.setDeleteHelper(new DeleteProfileMemberHelper(engineClient));\n        datastore.setSearchHelper(new SearchProfileMembersHelper(engineClient));\n        return datastore;\n    }\n\n    @Override\n    public String defineDefaultSearchOrder() {\n        return \"\";\n    }\n\n    @Override\n    protected void fillDeploys(final ProfileMemberItem item, final List<String> deploys) {\n        /*\n         * Need to be done there and not in constructor\n         * because need the engine session which is set\n         * by setter instead of being injected in API constructor...\n         */\n        addDeployer(getDeployerFactory().createProfileDeployer(ProfileMemberItem.ATTRIBUTE_PROFILE_ID));\n        super.fillDeploys(item, deploys);\n\n    }\n\n    protected DeployerFactory getDeployerFactory() {\n        return new DeployerFactory(getEngineSession());\n    }\n\n    private ProfileMemberEngineClient createProfileMemberEngineClient() {\n        return new EngineClientFactory(new EngineAPIAccessor(getEngineSession()))\n                .createProfileMemberEngineClient();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/profile/AbstractAPIMember.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.profile;\n\nimport static org.bonitasoft.web.rest.model.portal.profile.AbstractMemberItem.ATTRIBUTE_GROUP_ID;\nimport static org.bonitasoft.web.rest.model.portal.profile.AbstractMemberItem.ATTRIBUTE_ROLE_ID;\nimport static org.bonitasoft.web.rest.model.portal.profile.AbstractMemberItem.ATTRIBUTE_USER_ID;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.web.rest.model.portal.profile.AbstractMemberItem;\nimport org.bonitasoft.web.rest.server.api.ConsoleAPI;\nimport org.bonitasoft.web.rest.server.datastore.organization.GroupDatastore;\nimport org.bonitasoft.web.rest.server.datastore.organization.RoleDatastore;\nimport org.bonitasoft.web.rest.server.datastore.organization.UserDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasAdd;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasDelete;\nimport org.bonitasoft.web.rest.server.framework.api.APIHasSearch;\nimport org.bonitasoft.web.rest.server.framework.exception.APIAttributesException;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Séverin Moussel\n */\npublic abstract class AbstractAPIMember<T extends AbstractMemberItem> extends ConsoleAPI<T> implements\n        APIHasAdd<T>,\n        APIHasSearch<T>,\n        APIHasDelete {\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // UTILS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    protected void checkAttributes(final APIID userId, final APIID roleId, final APIID groupId) {\n        checkUserAttributeIsAloneOrNull(userId, roleId, groupId);\n        checkAnAttributeIsSet(userId, roleId, groupId);\n    }\n\n    private void checkUserAttributeIsAloneOrNull(final APIID userId, final APIID roleId, final APIID groupId) {\n        if (userId != null && (roleId != null || groupId != null)) {\n            throw new APIAttributesException(Arrays.asList(ATTRIBUTE_USER_ID, ATTRIBUTE_ROLE_ID, ATTRIBUTE_GROUP_ID),\n                    \"User attribute must be alone or null\");\n        }\n    }\n\n    private void checkAnAttributeIsSet(final APIID userId, final APIID roleId, final APIID groupId) {\n        if (userId == null && roleId == null && groupId == null) {\n            throw new APIAttributesException(Arrays.asList(ATTRIBUTE_USER_ID, ATTRIBUTE_ROLE_ID, ATTRIBUTE_GROUP_ID),\n                    \"At least one attribute must be set\");\n        }\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // CRUDS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public T add(final T item) {\n\n        checkAttributes(item.getUserId(), item.getRoleId(), item.getGroupId());\n\n        return super.add(item);\n    }\n\n    @Override\n    public void delete(final List<APIID> ids) {\n        // FIXME : uncomment when id in engine is deleted\n        // for (APIID apiid : ids) {\n        // checkAttributes(apiid.getPartAsAPIID(ATTRIBUTE_USER_ID), apiid.getPartAsAPIID(ATTRIBUTE_ROLE_ID), apiid.getPartAsAPIID(ATTRIBUTE_GROUP_ID));\n        // }\n        super.delete(ids);\n    }\n\n    @Override\n    public ItemSearchResult<T> search(final int page, final int resultsByPage, final String search, final String orders,\n            final Map<String, String> filters) {\n\n        checkUserAttributeIsAloneOrNull(\n                APIID.makeAPIID(filters.get(ATTRIBUTE_USER_ID)),\n                APIID.makeAPIID(filters.get(ATTRIBUTE_ROLE_ID)),\n                APIID.makeAPIID(filters.get(ATTRIBUTE_GROUP_ID)));\n        return super.search(page, resultsByPage, search, orders, filters);\n    }\n\n    @Override\n    protected void fillDeploys(final T item, final List<String> deploys) {\n        if (isDeployable(ATTRIBUTE_USER_ID, deploys, item)) {\n            item.setDeploy(ATTRIBUTE_USER_ID, new UserDatastore(getEngineSession()).get(item.getUserId()));\n        }\n        if (isDeployable(ATTRIBUTE_ROLE_ID, deploys, item)) {\n            item.setDeploy(ATTRIBUTE_ROLE_ID, new RoleDatastore(getEngineSession()).get(item.getRoleId()));\n        }\n        if (isDeployable(ATTRIBUTE_GROUP_ID, deploys, item)) {\n            item.setDeploy(ATTRIBUTE_GROUP_ID, new GroupDatastore(getEngineSession()).get(item.getGroupId()));\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/resource/ErrorMessage.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.resource;\n\nimport static org.bonitasoft.web.toolkit.client.common.json.JSonUtil.escape;\n\n/**\n * Representation for error entity\n *\n * @author Colin Puy\n */\npublic class ErrorMessage {\n\n    private String exception; // might be 'type' with simple name of exception\n    private String message;\n\n    // DO NOT PUT stacktrace, this is not coherent with old API toolkit but as a client of REST API, I do not need stacktrace.\n\n    public ErrorMessage() {\n        // empty constructor for json serialization\n    }\n\n    public ErrorMessage(final Throwable t) {\n        if (t != null) {\n            exception = t.getClass().toString();\n            setMessage(t.getMessage());\n        }\n    }\n\n    public String getException() {\n        return exception;\n    }\n\n    public void setException(final String exception) {\n        this.exception = exception;\n    }\n\n    public String getMessage() {\n        return message;\n    }\n\n    public void setMessage(final String message) {\n        this.message = escape(message);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/resource/ErrorMessageWithExplanations.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.resource;\n\nimport java.util.List;\n\n/**\n * Representation for error entity\n *\n * @author Colin Puy\n */\npublic class ErrorMessageWithExplanations extends ErrorMessage {\n\n    private List<String> explanations;\n\n    // DO NOT PUT stacktrace, this is not coherent with old API toolkit but as a client of REST API, I do not need stacktrace.\n\n    public ErrorMessageWithExplanations() {\n        // empty constructor for json serialization\n    }\n\n    public ErrorMessageWithExplanations(final Throwable t) {\n        super(t);\n    }\n\n    public List<String> getExplanations() {\n        return explanations;\n    }\n\n    public void setExplanations(final List<String> explanations) {\n        this.explanations = explanations;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/system/APII18nLocale.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.system;\n\nimport java.util.LinkedList;\nimport java.util.Map;\n\nimport org.bonitasoft.console.common.server.i18n.I18n;\nimport org.bonitasoft.web.rest.server.framework.API;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.i18n.model.I18nLocaleDefinition;\nimport org.bonitasoft.web.toolkit.client.common.i18n.model.I18nLocaleItem;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Julien Mege\n */\n// TODO add url pathParameter to load available locales by application (portal, platform, ...)\npublic class APII18nLocale extends API<I18nLocaleItem> {\n\n    private static final String DEFAULT_APPLICATION = \"portal\";\n\n    @Override\n    protected ItemDefinition<I18nLocaleItem> defineItemDefinition() {\n        return I18nLocaleDefinition.get();\n    }\n\n    @Override\n    public ItemSearchResult<I18nLocaleItem> search(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n\n        final Map<String, String> availableLocales = I18n.getInstance().getAvailableLocalesFor(DEFAULT_APPLICATION);\n        final LinkedList<I18nLocaleItem> items = new LinkedList<>();\n\n        for (final String locale : availableLocales.keySet()) {\n            final String name = availableLocales.get(locale);\n\n            if (search == null || search.length() == 0 || locale.contains(search) || name.contains(search)) {\n                items.add(new I18nLocaleItem(locale, availableLocales.get(locale)));\n            }\n        }\n\n        return new ItemSearchResult<>(page * resultsByPage, resultsByPage, items.size(), items);\n\n    }\n\n    @Override\n    public String defineDefaultSearchOrder() {\n        return \"\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/system/APISession.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.system;\n\nimport static java.util.Collections.emptyList;\nimport static java.util.Collections.singletonList;\n\nimport org.bonitasoft.console.common.server.auth.AuthenticationManagerProperties;\nimport org.bonitasoft.web.rest.server.api.ConsoleAPI;\nimport org.bonitasoft.web.toolkit.client.common.json.JSonSerializer;\nimport org.bonitasoft.web.toolkit.client.common.session.SessionDefinition;\nimport org.bonitasoft.web.toolkit.client.common.session.SessionItem;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\n\n/**\n * @author Julien Mege\n */\npublic class APISession extends ConsoleAPI<SessionItem> {\n\n    final BonitaVersion bonitaVersion = new BonitaVersion(new VersionFile());\n\n    @Override\n    protected SessionDefinition defineItemDefinition() {\n        return (SessionDefinition) Definitions.get(SessionDefinition.TOKEN);\n    }\n\n    @Override\n    public SessionItem get(final APIID unusedId) {\n        final org.bonitasoft.engine.session.APISession apiSession = getEngineSession();\n        final SessionItem session = new SessionItem();\n\n        if (apiSession != null) {\n            session.setAttribute(SessionItem.ATTRIBUTE_SESSIONID, String.valueOf(apiSession.getId()));\n            session.setAttribute(SessionItem.ATTRIBUTE_USERID, String.valueOf(apiSession.getUserId()));\n            session.setAttribute(SessionItem.ATTRIBUTE_USERNAME, apiSession.getUserName());\n            session.setAttribute(SessionItem.ATTRIBUTE_IS_TECHNICAL_USER, String.valueOf(apiSession.isTechnicalUser()));\n            session.setAttribute(SessionItem.ATTRIBUTE_IS_GUEST_USER,\n                    String.valueOf(isGuestUser(apiSession.getUserName())));\n            session.setAttribute(SessionItem.ATTRIBUTE_VERSION, getVersion());\n            session.setAttribute(SessionItem.ATTRIBUTE_BRANDING_VERSION, getBrandingVersion());\n            session.setAttribute(SessionItem.ATTRIBUTE_BRANDING_VERSION_WITH_DATE, getBrandingVersionWithDate());\n            session.setAttribute(SessionItem.ATTRIBUTE_COPYRIGHT, getCopyright());\n            session.setAttribute(SessionItem.ATTRIBUTE_CONF, getLogoutConfiguration(apiSession));\n        }\n        return session;\n    }\n\n    protected boolean isGuestUser(final String loggedInUsername) {\n        return false;\n    }\n\n    protected AuthenticationManagerProperties getAuthenticationManagerProperties() {\n        return AuthenticationManagerProperties.getProperties();\n    }\n\n    public String getLogoutConfiguration(final org.bonitasoft.engine.session.APISession apiSession) {\n        if (!apiSession.isTechnicalUser() && isLogoutDisabled()) {\n            return JSonSerializer.serialize(singletonList(AuthenticationManagerProperties.LOGOUT_DISABLED));\n        }\n        return JSonSerializer.serialize(emptyList());\n    }\n\n    /**\n     * enable to know if the logout button is visible or not\n     */\n    protected boolean isLogoutDisabled() {\n        return getAuthenticationManagerProperties().isLogoutDisabled();\n    }\n\n    public String getVersion() {\n        return bonitaVersion.getVersion();\n    }\n\n    public String getBrandingVersion() {\n        return bonitaVersion.getBrandingVersion();\n    }\n\n    public String getBrandingVersionWithDate() {\n        return bonitaVersion.getBrandingVersionWithUpdate();\n    }\n\n    public String getCopyright() {\n        return bonitaVersion.getCopyright();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/system/BonitaVersion.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.system;\n\nimport java.io.*;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Vincent Elcrin\n */\npublic class BonitaVersion {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(BonitaVersion.class.getName());\n\n    private List<String> metadata;\n\n    private final VersionFile versionFile;\n\n    public BonitaVersion(final VersionFile file) {\n        this.versionFile = file;\n    }\n\n    private List<String> read(VersionFile versionFile) {\n        List<String> result = new ArrayList<>();\n        try (Stream<String> lines = new BufferedReader(new InputStreamReader(versionFile.getStream())).lines()) {\n            result = lines.collect(Collectors.toList());\n        } catch (final Exception e) {\n            if (LOGGER.isWarnEnabled()) {\n                LOGGER.warn(\"Unable to read the file VERSION\", e);\n            }\n        }\n        return result;\n\n    }\n\n    public String getVersion() {\n        if (metadata == null) {\n            metadata = read(versionFile);\n        }\n        if (metadata.size() > 0) {\n            return metadata.get(0).trim();\n        } else {\n            return \"\";\n        }\n    }\n\n    public String getBrandingVersion() {\n        String brandingVersionWithDate = getBrandingVersionWithUpdate();\n\n        return brandingVersionWithDate.substring(0, brandingVersionWithDate.indexOf(\"-\"));\n    }\n\n    public String getBrandingVersionWithUpdate() {\n        if (metadata == null) {\n            metadata = read(versionFile);\n        }\n        if (metadata.size() > 1) {\n            return metadata.get(1).trim();\n        } else {\n            return \"\";\n        }\n    }\n\n    public String getCopyright() {\n        if (metadata == null) {\n            metadata = read(versionFile);\n        }\n        if (metadata.size() > 2) {\n            return metadata.get(2).trim();\n        } else {\n            return \"\";\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/system/I18nTranslationController.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.system;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.bonitasoft.console.common.server.i18n.I18n;\nimport org.bonitasoft.web.rest.server.QueryParameterUtils;\nimport org.bonitasoft.web.rest.server.api.AbstractRESTController;\nimport org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author Julien Mege\n */\n@RestController\n@RequestMapping(\"/API/system/i18ntranslation\")\npublic class I18nTranslationController extends AbstractRESTController {\n\n    @GetMapping\n    public List<Translation> getI18nTranslation(@RequestParam(value = \"f\", required = false) List<String> filters) {\n        String locale = QueryParameterUtils.extractMandatoryStringFilter(filters, \"locale\");\n\n        return getI18n().getLocale(AbstractI18n.stringToLocale(locale))\n                .entrySet().stream()\n                .map(entry -> new Translation(entry.getKey(), entry.getValue()))\n                .collect(Collectors.toList());\n    }\n\n    protected I18n getI18n() {\n        return I18n.getInstance();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/system/MaintenanceController.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.system;\n\nimport javax.servlet.http.HttpSession;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.api.MaintenanceAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.maintenance.MaintenanceDetails;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.system.MaintenanceDetailsClient;\nimport org.bonitasoft.web.rest.server.api.AbstractRESTController;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.web.bind.annotation.*;\nimport org.springframework.web.server.ResponseStatusException;\n\n@Slf4j\n@RestController\n@RequestMapping(\"/API/system/maintenance\")\npublic class MaintenanceController extends AbstractRESTController {\n\n    @GetMapping\n    public MaintenanceDetails getMaintenanceDetails(HttpSession session) {\n        APISession apiSession = getApiSession(session);\n        try {\n            return getMaintenanceAPI(apiSession).getMaintenanceDetails();\n        } catch (BonitaException e) {\n            String errorMessage = \"Error while getting the maintenance info\";\n            log.error(errorMessage, e);\n            throw new ResponseStatusException(\n                    HttpStatus.INTERNAL_SERVER_ERROR, errorMessage);\n        }\n    }\n\n    @PutMapping\n    public MaintenanceDetails changeMaintenanceState(@RequestBody MaintenanceDetailsClient maintenanceInfo,\n            HttpSession session) {\n        APISession apiSession = getApiSession(session);\n        try {\n            MaintenanceAPI maintenanceAPI = getMaintenanceAPI(apiSession);\n            MaintenanceDetails currentMaintenanceDetails = maintenanceAPI.getMaintenanceDetails();\n            if (maintenanceInfo.getMaintenanceState() != null\n                    && currentMaintenanceDetails.getMaintenanceState() != maintenanceInfo.getMaintenanceState()) {\n                //only enable/disable maintenance mode if needed\n                if (MaintenanceDetails.State.ENABLED == maintenanceInfo.getMaintenanceState()) {\n                    maintenanceAPI.enableMaintenanceMode();\n                } else {\n                    maintenanceAPI.disableMaintenanceMode();\n                }\n            }\n            if (maintenanceInfo.isMaintenanceMessageActive() != null\n                    && currentMaintenanceDetails.isMaintenanceMessageActive() != maintenanceInfo\n                            .isMaintenanceMessageActive()) {\n                //only update if different\n                if (maintenanceInfo.isMaintenanceMessageActive()) {\n                    maintenanceAPI.enableMaintenanceMessage();\n                } else {\n                    maintenanceAPI.disableMaintenanceMessage();\n                }\n            }\n            if (maintenanceInfo.getMaintenanceMessage() != null\n                    && currentMaintenanceDetails.getMaintenanceMessage() != maintenanceInfo\n                            .getMaintenanceMessage()) {\n                //only update if different\n                maintenanceAPI.updateMaintenanceMessage(maintenanceInfo.getMaintenanceMessage());\n            }\n            return maintenanceAPI.getMaintenanceDetails();\n        } catch (BonitaException e) {\n            String errorMessage = \"Error while setting the maintenance state\";\n            log.error(errorMessage, e);\n            throw new ResponseStatusException(\n                    HttpStatus.INTERNAL_SERVER_ERROR, errorMessage);\n        }\n    }\n\n    protected MaintenanceAPI getMaintenanceAPI(APISession apiSession)\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return TenantAPIAccessor.getMaintenanceAPI(apiSession);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/system/Translation.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.system;\n\n/**\n * @author Julien Mege\n */\npublic class Translation {\n\n    private String key;\n\n    private String value;\n\n    public Translation(String key, String value) {\n        this.key = key;\n        this.value = value;\n    }\n\n    public String getKey() {\n        return key;\n    }\n\n    public void setKey(String key) {\n        this.key = key;\n    }\n\n    public String getValue() {\n        return value;\n    }\n\n    public void setValue(String value) {\n        this.value = value;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/system/VersionFile.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.system;\n\nimport java.io.InputStream;\n\n/**\n * @author Vincent Elcrin\n */\npublic class VersionFile {\n\n    public InputStream getStream() {\n        return VersionFile.class.getClassLoader().getResourceAsStream(\"VERSION\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/tenant/TenantResourceItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.tenant;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.time.format.DateTimeFormatter;\n\nimport lombok.Getter;\nimport lombok.Setter;\nimport org.bonitasoft.engine.tenant.TenantResource;\nimport org.bonitasoft.engine.tenant.TenantResourceState;\nimport org.bonitasoft.engine.tenant.TenantResourceType;\n\n/**\n * @author Anthony Birembaut\n */\n@Getter\npublic class TenantResourceItem implements Serializable {\n\n    @Serial\n    private static final long serialVersionUID = -2269943868521108237L;\n\n    @Setter\n    private String id;\n    @Setter\n    private String name;\n    @Setter\n    private TenantResourceType type;\n    @Setter\n    private TenantResourceState state;\n    @Setter\n    private String lastUpdatedBy;\n    private final String lastUpdateDate;\n    @Setter\n    private String fileUpload;\n\n    public TenantResourceItem(final TenantResource tenantResource) {\n        this(tenantResource, \"\");\n    }\n\n    public TenantResourceItem(final TenantResource tenantResource, String fileUpload) {\n        id = String.valueOf(tenantResource.getId());\n        name = tenantResource.getName();\n        type = tenantResource.getType();\n        lastUpdatedBy = String.valueOf(tenantResource.getLastUpdatedBy());\n        lastUpdateDate = tenantResource.getLastUpdateDate().format(DateTimeFormatter.ISO_DATE_TIME);\n        state = tenantResource.getState();\n        this.fileUpload = fileUpload;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/CommonDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.server.framework.api.Datastore;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\n\n/**\n * @author Julien Mege\n */\npublic abstract class CommonDatastore<C extends IItem, E extends Serializable> extends Datastore {\n\n    private APISession engineSession;\n\n    /**\n     * Default Constructor.\n     *\n     * @param engineSession\n     *        The session that will allow to access the engine SDK\n     */\n    public CommonDatastore(final APISession engineSession) {\n        this.engineSession = engineSession;\n    }\n\n    /**\n     * @return the engineSession\n     */\n    protected final APISession getEngineSession() {\n        return this.engineSession;\n    }\n\n    protected void addStringFilterToSearchBuilder(final Map<String, String> filters, final SearchOptionsBuilder builder,\n            final String filterName,\n            final String engineAttributeName) {\n        if (filters != null && filters.containsKey(filterName)) {\n            final String filterValue = filters.get(filterName);\n            if (filterValue != null) {\n                if (filterValue.startsWith(\">\")) {\n                    builder.greaterThan(engineAttributeName, getFilterValueWithoutFirstCharacter(filterValue));\n                } else if (filterValue.startsWith(\"<\")) {\n                    builder.lessThan(engineAttributeName, getFilterValueWithoutFirstCharacter(filterValue));\n                } else {\n                    builder.filter(engineAttributeName, filterValue);\n                }\n            }\n        }\n    }\n\n    private String getFilterValueWithoutFirstCharacter(final String filterValue) {\n        return filterValue.substring(1).trim();\n    }\n\n    protected void addLongFilterToSearchBuilder(final Map<String, String> filters, final SearchOptionsBuilder builder,\n            final String filterName,\n            final String engineAttributeName) {\n        if (filters != null && filters.containsKey(filterName)) {\n            final String filterValue = filters.get(filterName);\n            if (filterValue != null) {\n                if (filterValue.startsWith(\">\")) {\n                    builder.greaterThan(engineAttributeName,\n                            Long.valueOf(getFilterValueWithoutFirstCharacter(filterValue)));\n                } else if (filterValue.startsWith(\"<\")) {\n                    builder.lessThan(engineAttributeName,\n                            Long.valueOf(getFilterValueWithoutFirstCharacter(filterValue)));\n                } else {\n                    builder.filter(engineAttributeName, Long.valueOf(filterValue));\n                }\n            }\n        }\n    }\n\n    protected abstract C convertEngineToConsoleItem(E item);\n\n    protected ItemSearchResult<C> convertEngineToConsoleSearch(final int page, final int resultsByPage,\n            final SearchResult<E> engineSearchResults) {\n        return new ItemSearchResult<>(\n                page,\n                resultsByPage,\n                engineSearchResults.getCount(),\n                convertEngineToConsoleItemsList(engineSearchResults.getResult()));\n    }\n\n    protected List<C> convertEngineToConsoleItemsList(final List<E> engineSearchResults) {\n\n        final List<C> consoleSearchResults = new ArrayList<>();\n\n        for (final E engineItem : engineSearchResults) {\n            consoleSearchResults.add(convertEngineToConsoleItem(engineItem));\n        }\n        return consoleSearchResults;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/ComposedDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.web.rest.server.framework.api.Datastore;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasAdd;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasDelete;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasUpdate;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIMethodNotAllowedException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\n\n/**\n * @author Vincent Elcrin\n */\npublic class ComposedDatastore<T extends IItem> extends Datastore\n        implements\n        DatastoreHasGet<T>,\n        DatastoreHasSearch<T>,\n        DatastoreHasAdd<T>,\n        DatastoreHasUpdate<T>,\n        DatastoreHasDelete {\n\n    private DatastoreHasGet<T> getHelper;\n\n    private DatastoreHasSearch<T> searchHelper;\n\n    private DatastoreHasUpdate<T> updateHelper;\n\n    private DatastoreHasAdd<T> addHelper;\n\n    private DatastoreHasDelete deleteHelper;\n\n    @Override\n    public T get(APIID id) {\n        if (getHelper != null) {\n            return getHelper.get(id);\n        }\n        throw new APIMethodNotAllowedException(\"GET method not allowed.\");\n    }\n\n    @Override\n    public ItemSearchResult<T> search(int page, int resultsByPage, String search, String orders,\n            Map<String, String> filters) {\n        if (searchHelper != null) {\n            return searchHelper.search(page, resultsByPage, search, orders, filters);\n        }\n        throw new APIMethodNotAllowedException(\"SEARCH method not allowed.\");\n    }\n\n    @Override\n    public T add(T item) {\n        if (addHelper != null) {\n            return addHelper.add(item);\n        }\n        throw new APIMethodNotAllowedException(\"ADD method not allowed.\");\n    }\n\n    @Override\n    public T update(APIID id, Map<String, String> attributes) {\n        if (updateHelper != null) {\n            return updateHelper.update(id, attributes);\n        }\n        throw new APIMethodNotAllowedException(\"UPDATE method not allowed.\");\n    }\n\n    @Override\n    public void delete(List<APIID> ids) {\n        if (deleteHelper != null) {\n            deleteHelper.delete(ids);\n        } else {\n            throw new APIMethodNotAllowedException(\"DELETE method not allowed.\");\n        }\n    }\n\n    public void setGetHelper(DatastoreHasGet<T> getHelper) {\n        this.getHelper = getHelper;\n    }\n\n    public void setSearchHelper(DatastoreHasSearch<T> searchHelper) {\n        this.searchHelper = searchHelper;\n    }\n\n    public void setAddHelper(DatastoreHasAdd<T> addHelper) {\n        this.addHelper = addHelper;\n    }\n\n    public void setDeleteHelper(DatastoreHasDelete deleteHelper) {\n        this.deleteHelper = deleteHelper;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/application/ApplicationDataStore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.application;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.engine.api.ApplicationAPI;\nimport org.bonitasoft.engine.api.PageAPI;\nimport org.bonitasoft.engine.business.application.Application;\nimport org.bonitasoft.engine.business.application.ApplicationCreator;\nimport org.bonitasoft.engine.business.application.ApplicationLink;\nimport org.bonitasoft.engine.business.application.ApplicationLinkCreator;\nimport org.bonitasoft.engine.business.application.ApplicationLinkUpdater;\nimport org.bonitasoft.engine.business.application.ApplicationNotFoundException;\nimport org.bonitasoft.engine.business.application.ApplicationPage;\nimport org.bonitasoft.engine.business.application.ApplicationUpdater;\nimport org.bonitasoft.engine.business.application.IApplication;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.application.AbstractApplicationItem;\nimport org.bonitasoft.web.rest.model.application.ApplicationDefinition;\nimport org.bonitasoft.web.rest.model.application.ApplicationItem;\nimport org.bonitasoft.web.rest.model.application.ApplicationLinkItem;\nimport org.bonitasoft.web.rest.server.datastore.CommonDatastore;\nimport org.bonitasoft.web.rest.server.datastore.filter.Filters;\nimport org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator;\nimport org.bonitasoft.web.rest.server.datastore.utils.Sorts;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasAdd;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasDelete;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasUpdate;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ApplicationDataStore extends CommonDatastore<AbstractApplicationItem, IApplication>\n        implements DatastoreHasAdd<AbstractApplicationItem>,\n        DatastoreHasUpdate<AbstractApplicationItem>,\n        DatastoreHasGet<AbstractApplicationItem>, DatastoreHasSearch<AbstractApplicationItem>, DatastoreHasDelete {\n\n    private final ApplicationAPI applicationAPI;\n    private final ApplicationItemConverter converter;\n    private final PageAPI pageAPI;\n    private static final String CUSTOMPAGE_HOME = \"custompage_home\";\n\n    public ApplicationDataStore(final APISession engineSession, final ApplicationAPI applicationAPI,\n            final PageAPI pageAPI, final ApplicationItemConverter converter) {\n        super(engineSession);\n        this.applicationAPI = applicationAPI;\n        this.pageAPI = pageAPI;\n        this.converter = converter;\n    }\n\n    @Override\n    public void delete(final List<APIID> ids) {\n        try {\n            for (final APIID id : ids) {\n                applicationAPI.deleteApplication(id.toLong());\n            }\n        } catch (final BonitaException e) {\n            if (e.getCause() instanceof ApplicationNotFoundException) {\n                throw new APIItemNotFoundException(ApplicationDefinition.TOKEN);\n            } else {\n                throw new APIException(e);\n            }\n        }\n    }\n\n    @Override\n    public AbstractApplicationItem get(final APIID id) {\n        try {\n            final IApplication application = applicationAPI.getIApplication(id.toLong());\n            return converter.toApplicationItem(application);\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    /**\n     * @deprecated as of 9.0.0, Applications should be created at startup.\n     */\n    @Override\n    @Deprecated(since = \"9.0.0\")\n    public AbstractApplicationItem add(final AbstractApplicationItem item) {\n        if (item instanceof ApplicationItem legacy) {\n            return add(legacy);\n        } else if (item instanceof ApplicationLinkItem link) {\n            return add(link);\n        } else {\n            // should not occur anyway\n            throw new APIException(\"Unknown application type.\");\n        }\n    }\n\n    /**\n     * @deprecated as of 9.0.0, Applications should be created at startup.\n     */\n    @Deprecated(since = \"9.0.0\")\n    public ApplicationItem add(final ApplicationItem item) {\n\n        try {\n            final Page homePageDef = pageAPI.getPageByName(CUSTOMPAGE_HOME);\n\n            final ApplicationCreator creator = converter.toApplicationCreator(item);\n\n            final Application application = applicationAPI.createApplication(creator);\n            final ApplicationPage appHomePage = applicationAPI.createApplicationPage(application.getId(),\n                    homePageDef.getId(), \"home\");\n            applicationAPI.setApplicationHomePage(application.getId(), appHomePage.getId());\n            var converted = converter.toApplicationItem(application);\n            if (converted instanceof ApplicationItem res) {\n                return res;\n            } else {\n                // should not occur anyway\n                throw new APIException(\"Use dedicated API for application links.\");\n            }\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    /**\n     * @deprecated as of 9.0.0, Applications should be created at startup.\n     */\n    @Deprecated(since = \"9.0.0\")\n    public ApplicationLinkItem add(final ApplicationLinkItem item) {\n\n        try {\n            final ApplicationLinkCreator creator = converter.toApplicationLinkCreator(item);\n\n            final ApplicationLink application = applicationAPI.createApplicationLink(creator);\n            var converted = converter.toApplicationItem(application);\n            if (converted instanceof ApplicationLinkItem res) {\n                return res;\n            } else {\n                // should not occur anyway\n                throw new APIException(\"Use dedicated API for legacy applications.\");\n            }\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    /**\n     * @deprecated as of 9.0.0, Applications should be updated at startup.\n     */\n    @Override\n    @Deprecated(since = \"9.0.0\")\n    public AbstractApplicationItem update(final APIID id, final Map<String, String> attributes) {\n        try {\n            // no way to know the application type without getting it first\n            IApplication app = applicationAPI.getIApplication(id.toLong());\n            if (app instanceof Application) {\n                final ApplicationUpdater applicationUpdater = converter.toApplicationUpdater(attributes);\n                final Application application = applicationAPI.updateApplication(id.toLong(), applicationUpdater);\n                return converter.toApplicationItem(application);\n            } else if (app instanceof ApplicationLink) {\n                final ApplicationLinkUpdater applicationUpdater = converter.toApplicationLinkUpdater(attributes);\n                final ApplicationLink link = applicationAPI.updateApplicationLink(id.toLong(), applicationUpdater);\n                return converter.toApplicationItem(link);\n            } else {\n                // should not occur anyway\n                throw new APIException(\"Unknown application type.\");\n            }\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @Override\n    public ItemSearchResult<AbstractApplicationItem> search(final int page, final int resultsByPage,\n            final String search,\n            final String orders,\n            final Map<String, String> filters) {\n        // Build search\n        final SearchOptionsCreator creator = makeSearchOptionCreator(page, resultsByPage, search, orders, filters);\n\n        // Run search depending on filters passed\n        SearchResult<IApplication> searchResult;\n        try {\n            searchResult = runSearch(creator);\n\n            // Convert to ConsoleItems\n            return new ItemSearchResult<>(page, resultsByPage, searchResult.getCount(),\n                    convertEngineToConsoleItemsList(searchResult.getResult()));\n        } catch (final SearchException e) {\n            throw new APIException(e);\n        }\n\n    }\n\n    protected SearchOptionsCreator makeSearchOptionCreator(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n        return new SearchOptionsCreator(page, resultsByPage, search, new Sorts(orders, getSearchDescriptorConverter()),\n                new Filters(filters,\n                        new ApplicationFilterCreator(getSearchDescriptorConverter())));\n    }\n\n    protected SearchResult<IApplication> runSearch(final SearchOptionsCreator creator) throws SearchException {\n        return applicationAPI.searchIApplications(creator.create());\n    }\n\n    protected ApplicationSearchDescriptorConverter getSearchDescriptorConverter() {\n        return new ApplicationSearchDescriptorConverter();\n    }\n\n    @Override\n    protected AbstractApplicationItem convertEngineToConsoleItem(final IApplication item) {\n        return new ApplicationItemConverter(new BonitaHomeFolderAccessor()).toApplicationItem(item);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/application/ApplicationDataStoreCreator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.application;\n\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.engine.api.ApplicationAPI;\nimport org.bonitasoft.engine.api.PageAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ApplicationDataStoreCreator {\n\n    public ApplicationDataStore create(final APISession session) {\n        ApplicationAPI applicationAPI;\n        PageAPI pageAPI;\n        try {\n            applicationAPI = getLivingApplicationAPI(session);\n            pageAPI = getCustomPageAPI(session);\n            return new ApplicationDataStore(session, applicationAPI, pageAPI, getApplicationConverter());\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    protected PageAPI getCustomPageAPI(APISession session)\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return TenantAPIAccessor.getCustomPageAPI(session);\n    }\n\n    protected ApplicationAPI getLivingApplicationAPI(APISession session)\n            throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return TenantAPIAccessor.getLivingApplicationAPI(session);\n    }\n\n    protected ApplicationItemConverter getApplicationConverter() {\n        return new ApplicationItemConverter(new BonitaHomeFolderAccessor());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/application/ApplicationFilterCreator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.application;\n\nimport org.bonitasoft.web.rest.server.datastore.filter.GenericFilterCreator;\n\nclass ApplicationFilterCreator extends GenericFilterCreator {\n\n    ApplicationFilterCreator(final ApplicationSearchDescriptorConverter converter) {\n        super(converter);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/application/ApplicationItemConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.application;\n\nimport java.util.Map;\n\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.console.common.server.utils.IconDescriptor;\nimport org.bonitasoft.engine.business.application.Application;\nimport org.bonitasoft.engine.business.application.ApplicationCreator;\nimport org.bonitasoft.engine.business.application.ApplicationLinkCreator;\nimport org.bonitasoft.engine.business.application.ApplicationLinkUpdater;\nimport org.bonitasoft.engine.business.application.ApplicationUpdater;\nimport org.bonitasoft.engine.business.application.IApplication;\nimport org.bonitasoft.web.rest.model.application.AbstractApplicationItem;\nimport org.bonitasoft.web.rest.model.application.ApplicationItem;\nimport org.bonitasoft.web.rest.model.application.ApplicationLinkItem;\nimport org.bonitasoft.web.toolkit.client.common.util.MapUtil;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasIcon;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ApplicationItemConverter {\n\n    protected BonitaHomeFolderAccessor bonitaHomeFolderAccessor;\n\n    public ApplicationItemConverter(BonitaHomeFolderAccessor bonitaHomeFolderAccessor) {\n        this.bonitaHomeFolderAccessor = bonitaHomeFolderAccessor;\n    }\n\n    public AbstractApplicationItem toApplicationItem(final IApplication application) {\n        final AbstractApplicationItem item;\n\n        if (application instanceof Application legacy) {\n            var legacyItem = new ApplicationItem();\n            item = legacyItem;\n            if (legacy.getHomePageId() != null) {\n                legacyItem.setHomePageId(legacy.getHomePageId());\n            } else {\n                legacyItem.setHomePageId(-1L);\n            }\n            if (legacy.getLayoutId() != null) {\n                legacyItem.setLayoutId(legacy.getLayoutId());\n            } else {\n                legacyItem.setLayoutId(-1L);\n            }\n            if (legacy.getThemeId() != null) {\n                legacyItem.setThemeId(legacy.getThemeId());\n            } else {\n                legacyItem.setThemeId(-1L);\n            }\n        } else {\n            item = new ApplicationLinkItem();\n        }\n\n        item.setId(application.getId());\n        item.setToken(application.getToken());\n        item.setDisplayName(application.getDisplayName());\n        item.setVersion(application.getVersion());\n        item.setDescription(application.getDescription());\n        item.setIcon(application.hasIcon() ? ApplicationItem.ICON_PATH_API_PREFIX + application.getId() + \"?t=\"\n                + application.getLastUpdateDate().getTime() : \"\");\n        item.setCreationDate(Long.toString(application.getCreationDate().getTime()));\n        item.setCreatedBy(application.getCreatedBy());\n        item.setLastUpdateDate(Long.toString(application.getLastUpdateDate().getTime()));\n        item.setUpdatedBy(application.getUpdatedBy());\n        item.setState(application.getState());\n        item.setVisibility(application.getVisibility().name());\n        item.setEditable(application.isEditable());\n        if (application.getProfileId() != null) {\n            item.setProfileId(application.getProfileId());\n        } else {\n            item.setProfileId(-1L);\n        }\n\n        return item;\n    }\n\n    /**\n     * @deprecated ApplicationCreator should no longer be used. Since 9.0.0, Applications should be created at startup.\n     */\n    @Deprecated(since = \"10.2.0\")\n    public ApplicationCreator toApplicationCreator(final ApplicationItem appItem) {\n        final ApplicationCreator creator = new ApplicationCreator(appItem.getToken(), appItem.getDisplayName(),\n                appItem.getVersion());\n        creator.setDescription(appItem.getDescription());\n        creator.setProfileId(appItem.getProfileId().toLong());\n        return creator;\n    }\n\n    /**\n     * @deprecated ApplicationCreator should no longer be used. Since 9.0.0, Applications should be created at startup.\n     */\n    @Deprecated(since = \"10.2.0\")\n    public ApplicationLinkCreator toApplicationLinkCreator(final ApplicationLinkItem appLinkItem) {\n        final ApplicationLinkCreator creator = new ApplicationLinkCreator(appLinkItem.getToken(),\n                appLinkItem.getDisplayName(),\n                appLinkItem.getVersion());\n        creator.setDescription(appLinkItem.getDescription());\n        creator.setProfileId(appLinkItem.getProfileId().toLong());\n        return creator;\n    }\n\n    /**\n     * @deprecated ApplicationUpdater should no longer be used. Since 9.0.0, Applications should be created at startup.\n     */\n    @Deprecated(since = \"10.2.0\")\n    public ApplicationUpdater toApplicationUpdater(final Map<String, String> attributes) {\n        final ApplicationUpdater applicationUpdater = getApplicationUpdater();\n\n        if (attributes.containsKey(ApplicationItem.ATTRIBUTE_TOKEN)) {\n            applicationUpdater.setToken(attributes.get(ApplicationItem.ATTRIBUTE_TOKEN));\n        }\n        if (attributes.containsKey(ApplicationItem.ATTRIBUTE_DISPLAY_NAME)) {\n            applicationUpdater.setDisplayName(attributes.get(ApplicationItem.ATTRIBUTE_DISPLAY_NAME));\n        }\n        if (attributes.containsKey(ApplicationItem.ATTRIBUTE_DESCRIPTION)) {\n            applicationUpdater.setDescription(attributes.get(ApplicationItem.ATTRIBUTE_DESCRIPTION));\n        }\n        if (attributes.containsKey(ApplicationItem.ATTRIBUTE_PROFILE_ID)) {\n            applicationUpdater.setProfileId(Long.parseLong(attributes.get(ApplicationItem.ATTRIBUTE_PROFILE_ID)));\n        }\n\n        if (attributes.containsKey(ApplicationItem.ATTRIBUTE_HOME_PAGE_ID)) {\n            Long homePageId = Long.parseLong(attributes.get(ApplicationItem.ATTRIBUTE_HOME_PAGE_ID));\n            if (homePageId == -1) {\n                homePageId = null;\n            }\n            applicationUpdater.setHomePageId(homePageId);\n        }\n        if (attributes.containsKey(ApplicationItem.ATTRIBUTE_STATE)) {\n            applicationUpdater.setState(attributes.get(ApplicationItem.ATTRIBUTE_STATE));\n        }\n        if (attributes.containsKey(ApplicationItem.ATTRIBUTE_VERSION)) {\n            applicationUpdater.setVersion(attributes.get(ApplicationItem.ATTRIBUTE_VERSION));\n        }\n        if (!MapUtil.isBlank(attributes, ItemHasIcon.ATTRIBUTE_ICON)\n                && !attributes.get(ItemHasIcon.ATTRIBUTE_ICON).startsWith(ApplicationItem.ICON_PATH_API_PREFIX)) {\n            IconDescriptor iconDescriptor = bonitaHomeFolderAccessor\n                    .getIconFromFileSystem(attributes.get(ItemHasIcon.ATTRIBUTE_ICON));\n            applicationUpdater.setIcon(iconDescriptor.getFilename(), iconDescriptor.getContent());\n        }\n\n        return applicationUpdater;\n\n    }\n\n    /**\n     * @deprecated ApplicationUpdater should no longer be used. Since 9.0.0, Applications should be created at startup.\n     */\n    @Deprecated(since = \"10.2.0\")\n    public ApplicationLinkUpdater toApplicationLinkUpdater(final Map<String, String> attributes) {\n        final ApplicationLinkUpdater applicationUpdater = getApplicationLinkUpdater();\n\n        if (attributes.containsKey(ApplicationItem.ATTRIBUTE_TOKEN)) {\n            applicationUpdater.setToken(attributes.get(ApplicationItem.ATTRIBUTE_TOKEN));\n        }\n        if (attributes.containsKey(ApplicationItem.ATTRIBUTE_DISPLAY_NAME)) {\n            applicationUpdater.setDisplayName(attributes.get(ApplicationItem.ATTRIBUTE_DISPLAY_NAME));\n        }\n        if (attributes.containsKey(ApplicationItem.ATTRIBUTE_DESCRIPTION)) {\n            applicationUpdater.setDescription(attributes.get(ApplicationItem.ATTRIBUTE_DESCRIPTION));\n        }\n        if (attributes.containsKey(ApplicationItem.ATTRIBUTE_PROFILE_ID)) {\n            applicationUpdater.setProfileId(Long.parseLong(attributes.get(ApplicationItem.ATTRIBUTE_PROFILE_ID)));\n        }\n\n        if (attributes.containsKey(ApplicationItem.ATTRIBUTE_STATE)) {\n            applicationUpdater.setState(attributes.get(ApplicationItem.ATTRIBUTE_STATE));\n        }\n        if (attributes.containsKey(ApplicationItem.ATTRIBUTE_VERSION)) {\n            applicationUpdater.setVersion(attributes.get(ApplicationItem.ATTRIBUTE_VERSION));\n        }\n        if (!MapUtil.isBlank(attributes, ItemHasIcon.ATTRIBUTE_ICON)\n                && !attributes.get(ItemHasIcon.ATTRIBUTE_ICON).startsWith(ApplicationItem.ICON_PATH_API_PREFIX)) {\n            IconDescriptor iconDescriptor = bonitaHomeFolderAccessor\n                    .getIconFromFileSystem(attributes.get(ItemHasIcon.ATTRIBUTE_ICON));\n            applicationUpdater.setIcon(iconDescriptor.getFilename(), iconDescriptor.getContent());\n        }\n\n        return applicationUpdater;\n\n    }\n\n    /**\n     * @deprecated ApplicationUpdater should no longer be used. Since 9.0.0, Applications should be created at startup.\n     */\n    @Deprecated(since = \"10.2.0\")\n    protected ApplicationUpdater getApplicationUpdater() {\n        return new ApplicationUpdater();\n    }\n\n    /**\n     * @deprecated ApplicationUpdater should no longer be used. Since 9.0.0, Applications should be created at startup.\n     */\n    @Deprecated(since = \"10.2.0\")\n    protected ApplicationLinkUpdater getApplicationLinkUpdater() {\n        return new ApplicationLinkUpdater();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/application/ApplicationSearchDescriptorConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.application;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.business.application.ApplicationSearchDescriptor;\nimport org.bonitasoft.web.rest.model.application.AbstractApplicationItem;\nimport org.bonitasoft.web.rest.model.application.ApplicationItem;\nimport org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter;\nimport org.bonitasoft.web.toolkit.client.common.util.MapUtil;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\npublic class ApplicationSearchDescriptorConverter implements AttributeConverter {\n\n    private final Map<String, String> mapping;\n\n    private final Map<String, ItemAttribute.TYPE> valueTypeMapping = new HashMap<>();\n\n    public ApplicationSearchDescriptorConverter() {\n        mapping = createMapping();\n    }\n\n    private Map<String, String> createMapping() {\n        final Map<String, String> mapping = new HashMap<>();\n        mapping.put(AbstractApplicationItem.ATTRIBUTE_ID, ApplicationSearchDescriptor.ID);\n        mapping.put(AbstractApplicationItem.ATTRIBUTE_LINK, ApplicationSearchDescriptor.LINK);\n        valueTypeMapping.put(AbstractApplicationItem.ATTRIBUTE_LINK, ItemAttribute.TYPE.BOOLEAN);\n        mapping.put(AbstractApplicationItem.ATTRIBUTE_TOKEN, ApplicationSearchDescriptor.TOKEN);\n        mapping.put(AbstractApplicationItem.ATTRIBUTE_DISPLAY_NAME, ApplicationSearchDescriptor.DISPLAY_NAME);\n        mapping.put(AbstractApplicationItem.ATTRIBUTE_STATE, ApplicationSearchDescriptor.STATE);\n        mapping.put(AbstractApplicationItem.ATTRIBUTE_CREATED_BY, ApplicationSearchDescriptor.CREATED_BY);\n        mapping.put(AbstractApplicationItem.ATTRIBUTE_CREATION_DATE, ApplicationSearchDescriptor.CREATION_DATE);\n        mapping.put(AbstractApplicationItem.ATTRIBUTE_LAST_UPDATE_DATE, ApplicationSearchDescriptor.LAST_UPDATE_DATE);\n        mapping.put(AbstractApplicationItem.ATTRIBUTE_UPDATED_BY, ApplicationSearchDescriptor.UPDATED_BY);\n        mapping.put(AbstractApplicationItem.ATTRIBUTE_VERSION, ApplicationSearchDescriptor.VERSION);\n        mapping.put(AbstractApplicationItem.ATTRIBUTE_PROFILE_ID, ApplicationSearchDescriptor.PROFILE_ID);\n        mapping.put(AbstractApplicationItem.FILTER_USER_ID, ApplicationSearchDescriptor.USER_ID);\n\n        mapping.put(ApplicationItem.ATTRIBUTE_LAYOUT_ID, ApplicationSearchDescriptor.LAYOUT_ID);\n        mapping.put(ApplicationItem.ATTRIBUTE_THEME_ID, ApplicationSearchDescriptor.THEME_ID);\n\n        return mapping;\n    }\n\n    @Override\n    public String convert(final String attribute) {\n        return MapUtil.getMandatory(mapping, attribute);\n    }\n\n    @Override\n    public Map<String, ItemAttribute.TYPE> getValueTypeMapping() {\n        return valueTypeMapping;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/applicationmenu/ApplicationMenuDataStore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.applicationmenu;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.ApplicationAPI;\nimport org.bonitasoft.engine.business.application.ApplicationMenu;\nimport org.bonitasoft.engine.business.application.ApplicationMenuNotFoundException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.applicationmenu.ApplicationMenuDefinition;\nimport org.bonitasoft.web.rest.model.applicationmenu.ApplicationMenuItem;\nimport org.bonitasoft.web.rest.server.datastore.CommonDatastore;\nimport org.bonitasoft.web.rest.server.datastore.filter.Filters;\nimport org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator;\nimport org.bonitasoft.web.rest.server.datastore.utils.Sorts;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasAdd;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasDelete;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasUpdate;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Julien Mege\n */\npublic class ApplicationMenuDataStore extends CommonDatastore<ApplicationMenuItem, ApplicationMenu>\n        implements DatastoreHasAdd<ApplicationMenuItem>,\n        DatastoreHasUpdate<ApplicationMenuItem>,\n        DatastoreHasGet<ApplicationMenuItem>, DatastoreHasSearch<ApplicationMenuItem>, DatastoreHasDelete {\n\n    private final ApplicationAPI applicationAPI;\n    private final ApplicationMenuItemConverter converter;\n\n    public ApplicationMenuDataStore(final APISession engineSession, final ApplicationAPI applicationAPI,\n            final ApplicationMenuItemConverter converter) {\n        super(engineSession);\n        this.applicationAPI = applicationAPI;\n        this.converter = converter;\n    }\n\n    /**\n     * @deprecated as of 9.0.0, Application menu should be created at startup.\n     */\n    @Override\n    @Deprecated(since = \"9.0.0\")\n    public ApplicationMenuItem add(final ApplicationMenuItem item) {\n        try {\n            final ApplicationMenu applicationMenu = applicationAPI\n                    .createApplicationMenu(converter.toApplicationMenuCreator(item));\n            return converter.toApplicationMenuItem(applicationMenu);\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    /**\n     * @deprecated as of 9.0.0, Application menu should be updated at startup.\n     */\n    @Override\n    @Deprecated(since = \"9.0.0\")\n    public ApplicationMenuItem update(final APIID id, final Map<String, String> attributes) {\n        try {\n            final ApplicationMenu applicationMenu = applicationAPI.updateApplicationMenu(id.toLong(),\n                    converter.toApplicationMenuUpdater(attributes));\n            return converter.toApplicationMenuItem(applicationMenu);\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @Override\n    public ApplicationMenuItem get(final APIID id) {\n        try {\n            final ApplicationMenu applicationMenu = applicationAPI.getApplicationMenu(id.toLong());\n            return converter.toApplicationMenuItem(applicationMenu);\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @Override\n    public void delete(final List<APIID> ids) {\n        try {\n            for (final APIID id : ids) {\n                applicationAPI.deleteApplicationMenu(id.toLong());\n            }\n        } catch (final BonitaException e) {\n            if (e.getCause() instanceof ApplicationMenuNotFoundException) {\n                throw new APIItemNotFoundException(ApplicationMenuDefinition.TOKEN);\n            } else {\n                throw new APIException(e);\n            }\n        }\n    }\n\n    protected ApplicationMenuSearchDescriptorConverter getSearchDescriptorConverter() {\n        return new ApplicationMenuSearchDescriptorConverter();\n    }\n\n    @Override\n    protected ApplicationMenuItem convertEngineToConsoleItem(final ApplicationMenu item) {\n        return new ApplicationMenuItemConverter().toApplicationMenuItem(item);\n    }\n\n    @Override\n    public ItemSearchResult<ApplicationMenuItem> search(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n        final SearchOptionsCreator creator = makeSearchOptionCreator(page, resultsByPage, search, orders, filters);\n        try {\n            final SearchResult<ApplicationMenu> searchResult = runSearch(creator);\n            final List<ApplicationMenuItem> appMenuItems = convertEngineToConsoleItemsList(searchResult.getResult());\n            return new ItemSearchResult<>(page, resultsByPage, searchResult.getCount(),\n                    appMenuItems);\n        } catch (final SearchException e) {\n            throw new APIException(e);\n        }\n    }\n\n    protected SearchOptionsCreator makeSearchOptionCreator(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n        return new SearchOptionsCreator(page, resultsByPage, search, new Sorts(orders, getSearchDescriptorConverter()),\n                new Filters(filters,\n                        new ApplicationMenuFilterCreator(getSearchDescriptorConverter())));\n    }\n\n    protected SearchResult<ApplicationMenu> runSearch(final SearchOptionsCreator creator) throws SearchException {\n        return applicationAPI.searchApplicationMenus(creator.create());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/applicationmenu/ApplicationMenuDataStoreCreator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.applicationmenu;\n\nimport org.bonitasoft.engine.api.ApplicationAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\n\n/**\n * @author Julien Mege\n */\npublic class ApplicationMenuDataStoreCreator {\n\n    public ApplicationMenuDataStore create(final APISession session) {\n        ApplicationAPI applicationAPI;\n        try {\n            applicationAPI = TenantAPIAccessor.getLivingApplicationAPI(session);\n            return new ApplicationMenuDataStore(session, applicationAPI, new ApplicationMenuItemConverter());\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/applicationmenu/ApplicationMenuFilterCreator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.applicationmenu;\n\nimport org.bonitasoft.web.rest.server.datastore.filter.GenericFilterCreator;\n\nclass ApplicationMenuFilterCreator extends GenericFilterCreator {\n\n    ApplicationMenuFilterCreator(final ApplicationMenuSearchDescriptorConverter converter) {\n        super(converter);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/applicationmenu/ApplicationMenuItemConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.applicationmenu;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.business.application.ApplicationMenu;\nimport org.bonitasoft.engine.business.application.ApplicationMenuCreator;\nimport org.bonitasoft.engine.business.application.ApplicationMenuUpdater;\nimport org.bonitasoft.web.rest.model.applicationmenu.ApplicationMenuItem;\n\n/**\n * @author Julien Mege\n */\npublic class ApplicationMenuItemConverter {\n\n    public ApplicationMenuItem toApplicationMenuItem(final ApplicationMenu applicationMenu) {\n        final ApplicationMenuItem item = new ApplicationMenuItem();\n        item.setId(applicationMenu.getId());\n        item.setApplicationId(applicationMenu.getApplicationId());\n        item.setDisplayName(applicationMenu.getDisplayName());\n        if (applicationMenu.getApplicationPageId() != null) {\n            item.setApplicationPageId(applicationMenu.getApplicationPageId());\n        } else {\n            item.setApplicationPageId(\"-1\");\n        }\n        if (applicationMenu.getParentId() != null) {\n            item.setParentMenuId(applicationMenu.getParentId());\n        } else {\n            item.setParentMenuId(\"-1\");\n        }\n        item.setMenuIndex(applicationMenu.getIndex());\n        return item;\n    }\n\n    public ApplicationMenuCreator toApplicationMenuCreator(final ApplicationMenuItem item) {\n\n        Long applicationId = null;\n        if (item.getApplicationId() != null && item.getApplicationId().isValidLongID()) {\n            applicationId = item.getApplicationId().toLong();\n        }\n        Long applicationPageId = null;\n        if (item.getApplicationPageId() != null && item.getApplicationPageId().isValidLongID()) {\n            applicationPageId = item.getApplicationPageId().toLong();\n        }\n\n        final ApplicationMenuCreator menuCreator = new ApplicationMenuCreator(applicationId, item.getDisplayName(),\n                applicationPageId);\n\n        if (item.getParentMenuId() != null && item.getParentMenuId().isValidLongID()) {\n            menuCreator.setParentId(item.getParentMenuId().toLong());\n        }\n\n        return menuCreator;\n    }\n\n    public ApplicationMenuUpdater toApplicationMenuUpdater(final Map<String, String> attributes) {\n        final ApplicationMenuUpdater applicationMenuUpdater = new ApplicationMenuUpdater();\n\n        if (attributes.containsKey(ApplicationMenuItem.ATTRIBUTE_APPLICATION_PAGE_ID)) {\n            Long appPageId = Long.parseLong(attributes.get(ApplicationMenuItem.ATTRIBUTE_APPLICATION_PAGE_ID));\n            if (appPageId == -1) {\n                appPageId = null;\n            }\n            applicationMenuUpdater.setApplicationPageId(appPageId);\n        }\n        if (attributes.containsKey(ApplicationMenuItem.ATTRIBUTE_DISPLAY_NAME)) {\n            applicationMenuUpdater.setDisplayName(attributes.get(ApplicationMenuItem.ATTRIBUTE_DISPLAY_NAME));\n        }\n        if (attributes.containsKey(ApplicationMenuItem.ATTRIBUTE_MENU_INDEX)) {\n            applicationMenuUpdater.setIndex(Integer.parseInt(attributes.get(ApplicationMenuItem.ATTRIBUTE_MENU_INDEX)));\n        }\n        if (attributes.containsKey(ApplicationMenuItem.ATTRIBUTE_PARENT_MENU_ID)) {\n            Long parentMenuId = Long.parseLong(attributes.get(ApplicationMenuItem.ATTRIBUTE_PARENT_MENU_ID));\n            if (parentMenuId == -1) {\n                parentMenuId = null;\n            }\n            applicationMenuUpdater.setParentId(parentMenuId);\n        }\n\n        return applicationMenuUpdater;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/applicationmenu/ApplicationMenuSearchDescriptorConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.applicationmenu;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.business.application.ApplicationMenuSearchDescriptor;\nimport org.bonitasoft.web.rest.model.applicationmenu.ApplicationMenuItem;\nimport org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter;\nimport org.bonitasoft.web.toolkit.client.common.util.MapUtil;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * @author Julien Mege\n */\npublic class ApplicationMenuSearchDescriptorConverter implements AttributeConverter {\n\n    private final Map<String, String> mapping;\n\n    ApplicationMenuSearchDescriptorConverter() {\n        mapping = createMapping();\n    }\n\n    private Map<String, String> createMapping() {\n        final Map<String, String> mapping = new HashMap<>();\n        mapping.put(ApplicationMenuItem.ATTRIBUTE_ID, ApplicationMenuSearchDescriptor.ID);\n        mapping.put(ApplicationMenuItem.ATTRIBUTE_DISPLAY_NAME, ApplicationMenuSearchDescriptor.DISPLAY_NAME);\n        mapping.put(ApplicationMenuItem.ATTRIBUTE_APPLICATION_ID, ApplicationMenuSearchDescriptor.APPLICATION_ID);\n        mapping.put(ApplicationMenuItem.ATTRIBUTE_APPLICATION_PAGE_ID,\n                ApplicationMenuSearchDescriptor.APPLICATION_PAGE_ID);\n        mapping.put(ApplicationMenuItem.ATTRIBUTE_MENU_INDEX, ApplicationMenuSearchDescriptor.INDEX);\n        mapping.put(ApplicationMenuItem.ATTRIBUTE_PARENT_MENU_ID, ApplicationMenuSearchDescriptor.PARENT_ID);\n        return mapping;\n    }\n\n    @Override\n    public String convert(final String attribute) {\n        return MapUtil.getMandatory(mapping, attribute);\n    }\n\n    @Override\n    public Map<String, ItemAttribute.TYPE> getValueTypeMapping() {\n        return Collections.emptyMap();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/applicationpage/ApplicationPageDataStore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.applicationpage;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.ApplicationAPI;\nimport org.bonitasoft.engine.business.application.ApplicationPage;\nimport org.bonitasoft.engine.business.application.ApplicationPageNotFoundException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.applicationpage.ApplicationPageDefinition;\nimport org.bonitasoft.web.rest.model.applicationpage.ApplicationPageItem;\nimport org.bonitasoft.web.rest.server.datastore.CommonDatastore;\nimport org.bonitasoft.web.rest.server.datastore.filter.Filters;\nimport org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator;\nimport org.bonitasoft.web.rest.server.datastore.utils.Sorts;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasAdd;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasDelete;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Julien Mege\n */\npublic class ApplicationPageDataStore extends CommonDatastore<ApplicationPageItem, ApplicationPage>\n        implements DatastoreHasAdd<ApplicationPageItem>,\n        DatastoreHasGet<ApplicationPageItem>, DatastoreHasSearch<ApplicationPageItem>, DatastoreHasDelete {\n\n    private final ApplicationAPI applicationAPI;\n    private final ApplicationPageItemConverter converter;\n\n    public ApplicationPageDataStore(final APISession engineSession, final ApplicationAPI applicationAPI,\n            final ApplicationPageItemConverter converter) {\n        super(engineSession);\n        this.applicationAPI = applicationAPI;\n        this.converter = converter;\n    }\n\n    /**\n     * @deprecated as of 9.0.0, Application page should be created at startup.\n     */\n    @Override\n    @Deprecated(since = \"9.0.0\")\n    public ApplicationPageItem add(final ApplicationPageItem item) {\n        try {\n            final ApplicationPage applicationPage = applicationAPI.createApplicationPage(\n                    item.getApplicationId().toLong(), item.getPageId().toLong(),\n                    item.getToken());\n            return converter.toApplicationPageItem(applicationPage);\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @Override\n    public ApplicationPageItem get(final APIID id) {\n        try {\n            final ApplicationPage applicationPage = applicationAPI.getApplicationPage(id.toLong());\n            return converter.toApplicationPageItem(applicationPage);\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @Override\n    public void delete(final List<APIID> ids) {\n        try {\n            for (final APIID id : ids) {\n                applicationAPI.deleteApplicationPage(id.toLong());\n            }\n        } catch (final BonitaException e) {\n            if (e.getCause() instanceof ApplicationPageNotFoundException) {\n                throw new APIItemNotFoundException(ApplicationPageDefinition.TOKEN);\n            } else {\n                throw new APIException(e);\n            }\n        }\n    }\n\n    protected ApplicationPageSearchDescriptorConverter getSearchDescriptorConverter() {\n        return new ApplicationPageSearchDescriptorConverter();\n    }\n\n    @Override\n    protected ApplicationPageItem convertEngineToConsoleItem(final ApplicationPage item) {\n        return converter.toApplicationPageItem(item);\n    }\n\n    @Override\n    public ItemSearchResult<ApplicationPageItem> search(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n        final SearchOptionsCreator creator = makeSearchOptionCreator(page, resultsByPage, search, orders, filters);\n        try {\n            final SearchResult<ApplicationPage> searchResult = runSearch(creator);\n            final List<ApplicationPageItem> appPageItems = convertEngineToConsoleItemsList(searchResult.getResult());\n            return new ItemSearchResult<>(page, resultsByPage, searchResult.getCount(),\n                    appPageItems);\n        } catch (final SearchException e) {\n            throw new APIException(e);\n        }\n    }\n\n    protected SearchOptionsCreator makeSearchOptionCreator(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n        return new SearchOptionsCreator(page, resultsByPage, search, new Sorts(orders, getSearchDescriptorConverter()),\n                new Filters(filters,\n                        new ApplicationPageFilterCreator(getSearchDescriptorConverter())));\n    }\n\n    protected SearchResult<ApplicationPage> runSearch(final SearchOptionsCreator creator) throws SearchException {\n        return applicationAPI.searchApplicationPages(creator.create());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/applicationpage/ApplicationPageDataStoreCreator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.applicationpage;\n\nimport org.bonitasoft.engine.api.ApplicationAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\n\n/**\n * @author Julien Mege\n */\npublic class ApplicationPageDataStoreCreator {\n\n    public ApplicationPageDataStore create(final APISession session) {\n        try {\n            final ApplicationAPI applicationAPI = TenantAPIAccessor.getLivingApplicationAPI(session);\n            return new ApplicationPageDataStore(session, applicationAPI, new ApplicationPageItemConverter());\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/applicationpage/ApplicationPageFilterCreator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.applicationpage;\n\nimport org.bonitasoft.web.rest.server.datastore.filter.GenericFilterCreator;\n\nclass ApplicationPageFilterCreator extends GenericFilterCreator {\n\n    ApplicationPageFilterCreator(final ApplicationPageSearchDescriptorConverter converter) {\n        super(converter);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/applicationpage/ApplicationPageItemConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.applicationpage;\n\nimport org.bonitasoft.engine.business.application.ApplicationPage;\nimport org.bonitasoft.web.rest.model.applicationpage.ApplicationPageItem;\n\n/**\n * @author Julien Mege\n */\npublic class ApplicationPageItemConverter {\n\n    public ApplicationPageItem toApplicationPageItem(final ApplicationPage applicationPage) {\n        final ApplicationPageItem item = new ApplicationPageItem();\n        item.setId(applicationPage.getId());\n        item.setToken(applicationPage.getToken());\n        item.setPageId(applicationPage.getPageId());\n        item.setApplicationId(applicationPage.getApplicationId());\n        return item;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/applicationpage/ApplicationPageSearchDescriptorConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.applicationpage;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.business.application.ApplicationPageSearchDescriptor;\nimport org.bonitasoft.web.rest.model.applicationpage.ApplicationPageItem;\nimport org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter;\nimport org.bonitasoft.web.toolkit.client.common.util.MapUtil;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ApplicationPageSearchDescriptorConverter implements AttributeConverter {\n\n    private final Map<String, String> mapping;\n\n    ApplicationPageSearchDescriptorConverter() {\n        mapping = createMapping();\n    }\n\n    private Map<String, String> createMapping() {\n        final Map<String, String> mapping = new HashMap<>();\n        mapping.put(ApplicationPageItem.ATTRIBUTE_ID, ApplicationPageSearchDescriptor.ID);\n        mapping.put(ApplicationPageItem.ATTRIBUTE_TOKEN, ApplicationPageSearchDescriptor.TOKEN);\n        mapping.put(ApplicationPageItem.ATTRIBUTE_APPLICATION_ID, ApplicationPageSearchDescriptor.APPLICATION_ID);\n        mapping.put(ApplicationPageItem.ATTRIBUTE_PAGE_ID, ApplicationPageSearchDescriptor.PAGE_ID);\n        return mapping;\n    }\n\n    @Override\n    public String convert(final String attribute) {\n        return MapUtil.getMandatory(mapping, attribute);\n    }\n\n    @Override\n    public Map<String, ItemAttribute.TYPE> getValueTypeMapping() {\n        return Collections.emptyMap();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/ArchivedCaseDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.cases;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.process.*;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.cases.ArchivedCaseDefinition;\nimport org.bonitasoft.web.rest.model.bpm.cases.ArchivedCaseItem;\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseItem;\nimport org.bonitasoft.web.rest.server.datastore.CommonDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasDelete;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException;\nimport org.bonitasoft.web.toolkit.client.common.util.MapUtil;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Séverin Moussel\n */\npublic class ArchivedCaseDatastore extends CommonDatastore<ArchivedCaseItem, ArchivedProcessInstance>\n        implements DatastoreHasGet<ArchivedCaseItem>,\n        DatastoreHasSearch<ArchivedCaseItem>, DatastoreHasDelete {\n\n    public ArchivedCaseDatastore(final APISession engineSession) {\n        super(engineSession);\n    }\n\n    @Override\n    protected ArchivedCaseItem convertEngineToConsoleItem(final ArchivedProcessInstance item) {\n\n        final ArchivedCaseItem result = new ArchivedCaseItem();\n\n        result.setId(item.getId());\n        result.setLastUpdateDate(item.getLastUpdate());\n        result.setState(item.getState());\n        result.setStartDate(item.getStartDate());\n        result.setEndDate(item.getEndDate());\n        result.setProcessId(item.getProcessDefinitionId());\n        result.setArchivedDate(item.getArchiveDate());\n        result.setSourceObjectId(item.getSourceObjectId());\n        result.setRootCaseId(item.getRootProcessInstanceId());\n        result.setStartedByUserId(item.getStartedBy());\n        result.setStartedBySubstituteUserId(item.getStartedBySubstitute());\n        result.setSearchIndex1Label(item.getStringIndexLabel(1));\n        result.setSearchIndex2Label(item.getStringIndexLabel(2));\n        result.setSearchIndex3Label(item.getStringIndexLabel(3));\n        result.setSearchIndex4Label(item.getStringIndexLabel(4));\n        result.setSearchIndex5Label(item.getStringIndexLabel(5));\n        result.setSearchIndex1Value(item.getStringIndexValue(1));\n        result.setSearchIndex2Value(item.getStringIndexValue(2));\n        result.setSearchIndex3Value(item.getStringIndexValue(3));\n        result.setSearchIndex4Value(item.getStringIndexValue(4));\n        result.setSearchIndex5Value(item.getStringIndexValue(5));\n        result.setCallerId(item.getCallerId());\n        return result;\n    }\n\n    @Override\n    public ItemSearchResult<ArchivedCaseItem> search(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n\n        final SearchOptionsBuilder builder = buildSearchOptions(page, resultsByPage, search, orders, filters);\n\n        // Run search depending on filters passed\n        final SearchResult<ArchivedProcessInstance> searchResult = runSearch(filters, builder);\n\n        // Convert to ConsoleItems\n        return new ItemSearchResult<>(\n                page,\n                resultsByPage,\n                searchResult.getCount(),\n                convertEngineToConsoleItemsList(searchResult.getResult()));\n    }\n\n    protected SearchOptionsBuilder buildSearchOptions(final int page, final int resultsByPage, final String search,\n            final String orders, final Map<String, String> filters) {\n        // Build search\n        final SearchOptionsBuilder builder = SearchOptionsBuilderUtil.buildSearchOptions(page, resultsByPage, orders,\n                search);\n\n        addStringFilterToSearchBuilder(filters, builder, ArchivedCaseItem.ATTRIBUTE_PROCESS_NAME,\n                ArchivedProcessInstancesSearchDescriptor.NAME);\n        addLongFilterToSearchBuilder(filters, builder, ArchivedCaseItem.ATTRIBUTE_PROCESS_ID,\n                ArchivedProcessInstancesSearchDescriptor.PROCESS_DEFINITION_ID);\n        addLongFilterToSearchBuilder(filters, builder, ArchivedCaseItem.ATTRIBUTE_STARTED_BY_USER_ID,\n                ArchivedProcessInstancesSearchDescriptor.STARTED_BY);\n        addLongFilterToSearchBuilder(filters, builder, ArchivedCaseItem.ATTRIBUTE_SOURCE_OBJECT_ID,\n                ArchivedProcessInstancesSearchDescriptor.SOURCE_OBJECT_ID);\n        addLongFilterToSearchBuilder(filters, builder, ArchivedCaseItem.ATTRIBUTE_ROOT_CASE_ID,\n                ArchivedProcessInstancesSearchDescriptor.ROOT_PROCESS_INSTANCE_ID);\n        addAddDifferentFromRootIdFilterIfNecessary(filters, builder);\n        addStringFilterToSearchBuilder(filters, builder, ArchivedCaseItem.ATTRIBUTE_SEARCH_INDEX_1_VALUE,\n                ArchivedProcessInstancesSearchDescriptor.STRING_INDEX_1);\n        addStringFilterToSearchBuilder(filters, builder, ArchivedCaseItem.ATTRIBUTE_SEARCH_INDEX_2_VALUE,\n                ArchivedProcessInstancesSearchDescriptor.STRING_INDEX_2);\n        addStringFilterToSearchBuilder(filters, builder, ArchivedCaseItem.ATTRIBUTE_SEARCH_INDEX_3_VALUE,\n                ArchivedProcessInstancesSearchDescriptor.STRING_INDEX_3);\n        addStringFilterToSearchBuilder(filters, builder, ArchivedCaseItem.ATTRIBUTE_SEARCH_INDEX_4_VALUE,\n                ArchivedProcessInstancesSearchDescriptor.STRING_INDEX_4);\n        addStringFilterToSearchBuilder(filters, builder, ArchivedCaseItem.ATTRIBUTE_SEARCH_INDEX_5_VALUE,\n                ArchivedProcessInstancesSearchDescriptor.STRING_INDEX_5);\n        addCallerFilterToSearchBuilderIfNecessary(filters, builder);\n        return builder;\n    }\n\n    protected void addAddDifferentFromRootIdFilterIfNecessary(Map<String, String> filters,\n            SearchOptionsBuilder builder) {\n        /*\n         * When filtering on Root Case Id, we want all the subprocesses of the root case to be returned.\n         * Not the root case itself.\n         */\n        if (filters.containsKey(ArchivedCaseItem.ATTRIBUTE_ROOT_CASE_ID)) {\n            builder.differentFrom(ArchivedProcessInstancesSearchDescriptor.SOURCE_OBJECT_ID,\n                    MapUtil.getValueAsLong(filters, ArchivedCaseItem.ATTRIBUTE_ROOT_CASE_ID));\n        }\n    }\n\n    protected void addCallerFilterToSearchBuilderIfNecessary(final Map<String, String> filters,\n            final SearchOptionsBuilder builder) {\n        /*\n         * By default we add a caller filter of -1 to avoid having sub processes.\n         * If caller is forced to any then we don't need to add the filter.\n         */\n        if (!filters.containsKey(CaseItem.FILTER_CALLER)) {\n            builder.filter(ArchivedProcessInstancesSearchDescriptor.CALLER_ID, -1);\n        } else if (!\"any\".equalsIgnoreCase(filters.get(CaseItem.FILTER_CALLER))) {\n            builder.filter(ArchivedProcessInstancesSearchDescriptor.CALLER_ID,\n                    MapUtil.getValueAsLong(filters, CaseItem.FILTER_CALLER));\n        }\n    }\n\n    // Overridden for testing\n    protected void addLongFilterToSearchBuilder(final Map<String, String> filters, final SearchOptionsBuilder builder,\n            final String filterName,\n            final String engineAttributeName) {\n        super.addLongFilterToSearchBuilder(filters, builder, filterName, engineAttributeName);\n    }\n\n    // protected for testing\n    protected SearchResult<ArchivedProcessInstance> runSearch(final Map<String, String> filters,\n            final SearchOptionsBuilder builder) {\n\n        try {\n            final ProcessAPI processAPI = getProcessApi();\n\n            if (filters.containsKey(ArchivedCaseItem.FILTER_USER_ID)) {\n                return processAPI.searchArchivedProcessInstancesInvolvingUser(\n                        MapUtil.getValueAsLong(filters, ArchivedCaseItem.FILTER_USER_ID),\n                        builder.done());\n            }\n\n            if (filters.containsKey(ArchivedCaseItem.FILTER_SUPERVISOR_ID)) {\n                return processAPI\n                        .searchArchivedProcessInstancesSupervisedBy(\n                                MapUtil.getValueAsLong(filters, ArchivedCaseItem.FILTER_SUPERVISOR_ID), builder.done());\n            }\n\n            if (filters.containsKey(CaseItem.FILTER_CALLER) && !filters.containsKey(CaseItem.FILTER_STATE)) {\n                builder.leftParenthesis()\n                        .filter(ArchivedProcessInstancesSearchDescriptor.STATE_ID,\n                                ProcessInstanceState.COMPLETED.getId())\n                        .or()\n                        .filter(ArchivedProcessInstancesSearchDescriptor.STATE_ID,\n                                ProcessInstanceState.ABORTED.getId())\n                        .or()\n                        .filter(ArchivedProcessInstancesSearchDescriptor.STATE_ID,\n                                ProcessInstanceState.CANCELLED.getId())\n                        .rightParenthesis();\n                return processAPI.searchArchivedProcessInstancesInAllStates(builder.done());\n            }\n\n            return processAPI.searchArchivedProcessInstances(builder.done());\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n\n    }\n\n    @Override\n    public ArchivedCaseItem get(final APIID id) {\n        try {\n            return convertEngineToConsoleItem(getProcessApi()\n                    .getArchivedProcessInstance(id.toLong()));\n        } catch (ArchivedProcessInstanceNotFoundException e) {\n            throw new APIItemNotFoundException(ArchivedCaseDefinition.TOKEN, id);\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    public ArchivedCaseItem getUsingSourceObjectId(final APIID id) {\n        try {\n            return convertEngineToConsoleItem(getProcessApi()\n                    .getFinalArchivedProcessInstance(id.toLong()));\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @Override\n    public void delete(final List<APIID> ids) {\n        try {\n            final ProcessAPI processAPI = getProcessApi();\n            final List<Long> toDeleteIds = new ArrayList<>();\n            for (final APIID apiId : ids) {\n                final ArchivedProcessInstance archivedProcessInstance = processAPI\n                        .getArchivedProcessInstance(apiId.toLong());\n                toDeleteIds.add(archivedProcessInstance.getSourceObjectId());\n            }\n            processAPI.deleteArchivedProcessInstancesInAllStates(toDeleteIds);\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n\n    }\n\n    public ProcessAPI getProcessApi() throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return TenantAPIAccessor.getProcessAPI(getEngineSession());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/ArchivedCaseDocumentDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.cases;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.document.ArchivedDocument;\nimport org.bonitasoft.engine.bpm.document.DocumentException;\nimport org.bonitasoft.engine.bpm.document.DocumentNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.identity.UserNotFoundException;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.cases.ArchivedCaseDocumentItem;\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseDocumentItem;\nimport org.bonitasoft.web.rest.server.datastore.CommonDatastore;\nimport org.bonitasoft.web.rest.server.datastore.filter.Filters;\nimport org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator;\nimport org.bonitasoft.web.rest.server.datastore.utils.Sorts;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasDelete;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Fabio Lombardi\n */\npublic class ArchivedCaseDocumentDatastore extends CommonDatastore<ArchivedCaseDocumentItem, ArchivedDocument>\n        implements DatastoreHasGet<CaseDocumentItem>,\n        DatastoreHasDelete {\n\n    protected final ProcessAPI processAPI;\n\n    protected SearchOptionsCreator searchOptionsCreator;\n\n    /**\n     * Default constructor.\n     */\n    public ArchivedCaseDocumentDatastore(final APISession engineSession, final ProcessAPI processAPI) {\n        super(engineSession);\n        this.processAPI = processAPI;\n    }\n\n    // GET Method\n    @Override\n    public ArchivedCaseDocumentItem get(final APIID id) {\n        try {\n            final ArchivedDocument documentItem = processAPI.getArchivedProcessDocument(id.toLong());\n            return convertEngineToConsoleItem(documentItem);\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    // GET Method for SEARCH\n    public ItemSearchResult<ArchivedCaseDocumentItem> search(final int page, final int resultsByPage,\n            final String search, final Map<String, String> filters, final String orders) {\n        return searchDocument(page, resultsByPage, search, filters, orders);\n    }\n\n    protected ItemSearchResult<ArchivedCaseDocumentItem> searchDocument(final int page, final int resultsByPage,\n            final String search,\n            final Map<String, String> filters, final String orders) {\n\n        try {\n            final APIID supervisorAPIID = APIID.makeAPIID(filters.get(ArchivedCaseDocumentItem.FILTER_SUPERVISOR_ID));\n            final APIID archivedCaseId = APIID.makeAPIID(filters.get(ArchivedCaseDocumentItem.FILTER_ARCHIVED_CASE_ID));\n\n            if (supervisorAPIID != null) {\n                filters.remove(ArchivedCaseDocumentItem.FILTER_SUPERVISOR_ID);\n            }\n\n            if (archivedCaseId != null && archivedCaseId.isValidLongID()) {\n                filters.remove(ArchivedCaseDocumentItem.FILTER_ARCHIVED_CASE_ID);\n                final ArchivedProcessInstance archivedProcessInstance = processAPI\n                        .getArchivedProcessInstance(archivedCaseId.toLong());\n                if (archivedProcessInstance != null) {\n                    final Long sourceCaseId = archivedProcessInstance.getSourceObjectId();\n                    filters.put(ArchivedCaseDocumentItem.ATTRIBUTE_CASE_ID, sourceCaseId.toString());\n                }\n            }\n\n            searchOptionsCreator = buildSearchOptionCreator(page, resultsByPage, search, filters, orders);\n\n            final SearchResult<ArchivedDocument> engineSearchResults;\n            if (supervisorAPIID != null && supervisorAPIID.isValidLongID()) {\n                engineSearchResults = processAPI.searchArchivedDocumentsSupervisedBy(supervisorAPIID.toLong(),\n                        searchOptionsCreator.create());\n            } else {\n                engineSearchResults = processAPI.searchArchivedDocuments(searchOptionsCreator.create());\n            }\n            return new ItemSearchResult<>(page, resultsByPage, engineSearchResults.getCount(),\n                    convertEngineToConsoleItem(engineSearchResults.getResult()));\n        } catch (final ArchivedProcessInstanceNotFoundException e) {\n            throw new APIException(\"archivedCaseId not found. Request with bad param value.\");\n        } catch (final UserNotFoundException e) {\n            throw new APIException(\"supervisor_id not found. Request with bad param value.\");\n        } catch (final SearchException e) {\n            throw new APIException(\"Error while searching.\");\n        }\n    }\n\n    protected SearchOptionsCreator buildSearchOptionCreator(final int page, final int resultsByPage,\n            final String search, final Map<String, String> filters,\n            final String orders) {\n        return new SearchOptionsCreator(page, resultsByPage, search, new Sorts(orders,\n                getArchivedDocumentSearchAttributeConverter()),\n                new Filters(filters, new ArchivedCaseDocumentFilterCreator(\n                        getArchivedDocumentSearchAttributeConverter())));\n    }\n\n    private ArchivedCaseDocumentSearchAttributeConverter getArchivedDocumentSearchAttributeConverter() {\n        return new ArchivedCaseDocumentSearchAttributeConverter();\n    }\n\n    // DELETE Method\n    @Override\n    public void delete(final List<APIID> ids) {\n        if (ids != null) {\n\n            try {\n                for (final APIID id : ids) {\n                    processAPI.deleteContentOfArchivedDocument(id.toLong());\n                }\n            } catch (final DocumentNotFoundException e) {\n                throw new APIException(\"Error while deleting a document. Document not found\");\n            } catch (final DocumentException e) {\n                throw new APIException(e);\n            }\n\n        } else {\n            throw new APIException(\"Error while deleting a document. Document id not specified in the request\");\n        }\n    }\n\n    @Override\n    protected ArchivedCaseDocumentItem convertEngineToConsoleItem(final ArchivedDocument item) {\n        if (item != null) {\n            return new ArchivedCaseDocumentItemConverter().convert(item);\n        }\n        return null;\n    }\n\n    private List<ArchivedCaseDocumentItem> convertEngineToConsoleItem(final List<ArchivedDocument> result) {\n        if (result != null) {\n            return new ArchivedCaseDocumentItemConverter().convert(result);\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/ArchivedCaseDocumentFilterCreator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.cases;\n\nimport org.bonitasoft.web.rest.server.datastore.filter.GenericFilterCreator;\n\n/**\n * @author Fabio Lombardi\n */\nclass ArchivedCaseDocumentFilterCreator extends GenericFilterCreator {\n\n    ArchivedCaseDocumentFilterCreator(final ArchivedCaseDocumentSearchAttributeConverter converter) {\n        super(converter);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/ArchivedCaseDocumentItemConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.cases;\n\nimport org.bonitasoft.engine.bpm.document.ArchivedDocument;\nimport org.bonitasoft.web.rest.model.bpm.cases.ArchivedCaseDocumentItem;\nimport org.bonitasoft.web.rest.server.datastore.converter.ItemConverter;\n\npublic class ArchivedCaseDocumentItemConverter extends ItemConverter<ArchivedCaseDocumentItem, ArchivedDocument> {\n\n    @Override\n    public ArchivedCaseDocumentItem convert(final ArchivedDocument engineItem) {\n        final ArchivedCaseDocumentItem item = new ArchivedCaseDocumentItem();\n        item.setId(String.valueOf(engineItem.getId()));\n        item.setCaseId(String.valueOf(engineItem.getProcessInstanceId()));\n        item.setName(engineItem.getName());\n        item.setVersion(engineItem.getVersion());\n        item.setDescription(engineItem.getDescription());\n        item.setSubmittedBy(engineItem.getAuthor());\n        item.setFileName(engineItem.getContentFileName());\n        item.setCreationDate(engineItem.getCreationDate());\n        item.setMIMEType(engineItem.getContentMimeType());\n        item.setHasContent(String.valueOf(engineItem.hasContent()));\n        item.setStorageId(engineItem.getContentStorageId());\n        item.setURL(engineItem.getUrl());\n        item.setIndex(engineItem.getIndex());\n        item.setSourceObjectId(engineItem.getSourceObjectId());\n        item.setArchivedDate(engineItem.getArchiveDate());\n        return item;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/ArchivedCaseDocumentSearchAttributeConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.cases;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.document.ArchivedDocumentsSearchDescriptor;\nimport org.bonitasoft.web.rest.model.bpm.cases.ArchivedCaseDocumentItem;\nimport org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter;\nimport org.bonitasoft.web.toolkit.client.common.util.MapUtil;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * @author Fabio Lombardi\n */\npublic class ArchivedCaseDocumentSearchAttributeConverter implements AttributeConverter {\n\n    private final Map<String, String> mapping;\n\n    public ArchivedCaseDocumentSearchAttributeConverter() {\n        mapping = createMapping();\n    }\n\n    private Map<String, String> createMapping() {\n        final Map<String, String> mapping = new HashMap<>();\n        mapping.put(ArchivedCaseDocumentItem.ATTRIBUTE_ID, ArchivedCaseDocumentItem.ATTRIBUTE_ID);\n        mapping.put(ArchivedCaseDocumentItem.ATTRIBUTE_SUBMITTED_BY_USER_ID,\n                ArchivedDocumentsSearchDescriptor.DOCUMENT_AUTHOR);\n        mapping.put(ArchivedCaseDocumentItem.ATTRIBUTE_NAME, ArchivedDocumentsSearchDescriptor.DOCUMENT_NAME);\n        mapping.put(ArchivedCaseDocumentItem.ATTRIBUTE_CREATION_DATE,\n                ArchivedDocumentsSearchDescriptor.DOCUMENT_CREATIONDATE);\n        mapping.put(ArchivedCaseDocumentItem.ATTRIBUTE_DESCRIPTION,\n                ArchivedDocumentsSearchDescriptor.DOCUMENT_DESCRIPTION);\n        mapping.put(ArchivedCaseDocumentItem.ATTRIBUTE_INDEX, ArchivedDocumentsSearchDescriptor.LIST_INDEX);\n        mapping.put(ArchivedCaseDocumentItem.ATTRIBUTE_CASE_ID, ArchivedDocumentsSearchDescriptor.PROCESSINSTANCE_ID);\n        mapping.put(ArchivedCaseDocumentItem.ATTRIBUTE_SOURCE_OBJECT_ID,\n                ArchivedDocumentsSearchDescriptor.SOURCEOBJECT_ID);\n        mapping.put(ArchivedCaseDocumentItem.ATTRIBUTE_ARCHIVED_DATE, ArchivedDocumentsSearchDescriptor.ARCHIVE_DATE);\n        return mapping;\n    }\n\n    @Override\n    public String convert(final String attribute) {\n        return MapUtil.getMandatory(mapping, attribute);\n    }\n\n    @Override\n    public Map<String, ItemAttribute.TYPE> getValueTypeMapping() {\n        return Collections.emptyMap();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/ArchivedCommentDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.cases;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.comment.ArchivedComment;\nimport org.bonitasoft.engine.bpm.comment.ArchivedCommentsSearchDescriptor;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.cases.ArchivedCommentItem;\nimport org.bonitasoft.web.rest.server.datastore.CommonDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\n\n/**\n * @author Paul AMAR\n */\npublic class ArchivedCommentDatastore extends CommonDatastore<ArchivedCommentItem, ArchivedComment>\n        implements DatastoreHasSearch<ArchivedCommentItem> {\n\n    /**\n     * Conversion look up table to for sortable fields\n     */\n    private static final String[][] SORTABLE_FIELDS_LUT = {\n            { ArchivedCommentItem.ATTRIBUTE_POST_DATE, ArchivedCommentsSearchDescriptor.POSTDATE },\n    };\n\n    public ArchivedCommentDatastore(final APISession engineSession) {\n        super(engineSession);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // C.R.U.D.\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    /*\n     * (non-Javadoc)\n     * @see org.bonitasoft.web.toolkit.server.api.DatastoreHasSearch#search(int, int, java.lang.String,\n     * java.lang.String, java.util.Map)\n     */\n    @Override\n    public ItemSearchResult<ArchivedCommentItem> search(int page, int resultsByPage, String search, String orders,\n            Map<String, String> filters) {\n        try {\n            final SearchOptionsBuilder builder = SearchOptionsBuilderUtil.buildSearchOptions(page, resultsByPage, \"\",\n                    search);\n            adjustSearchBuilder(filters, builder);\n            /**\n             * Need to convert field name passed by API into sortable field provided by engine.\n             */\n            if (orders != null && !orders.isEmpty()) {\n                String[] sort = orders.split(\" \");\n                for (int i = 0; i < SORTABLE_FIELDS_LUT.length; i++) {\n                    if (sort[0].equals(SORTABLE_FIELDS_LUT[i][0])) {\n                        builder.sort(SORTABLE_FIELDS_LUT[i][1], Order.valueOf(sort[1]));\n                    }\n                }\n            }\n\n            final SearchResult<ArchivedComment> result = TenantAPIAccessor.getProcessAPI(getEngineSession())\n                    .searchArchivedComments(builder.done());\n\n            final List<ArchivedCommentItem> archivedCommentList = new ArrayList<>();\n            for (final ArchivedComment item : result.getResult()) {\n                final ArchivedCommentItem resultArchivedCommentItem = convertEngineToConsoleItem(item);\n                archivedCommentList.add(resultArchivedCommentItem);\n            }\n\n            return new ItemSearchResult<>(page, resultsByPage,\n                    result.getCount(), archivedCommentList);\n\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // CONVERTS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Convert ProcessItem filters into engine filers\n     *\n     * @param filters\n     * @param builder\n     */\n    private void adjustSearchBuilder(final Map<String, String> filters, final SearchOptionsBuilder builder) {\n        addStringFilterToSearchBuilder(filters, builder, ArchivedCommentItem.ATTRIBUTE_PROCESS_INSTANCE_ID,\n                ArchivedCommentsSearchDescriptor.PROCESS_INSTANCE_ID);\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see\n     * org.bonitasoft.console.server.credentials.bpm.CommonDatastore#convertEngineToConsoleItem(java.io.Serializable)\n     */\n    @Override\n    protected ArchivedCommentItem convertEngineToConsoleItem(final ArchivedComment item) {\n        if (item == null) {\n            return null;\n        }\n\n        final ArchivedCommentItem consoleItem = new ArchivedCommentItem();\n        // Il faudra rajouter les get() des comments\n        consoleItem.setId(item.getId());\n        consoleItem.setUserId(item.getUserId());\n        consoleItem.setProcessInstanceId(item.getProcessInstanceId());\n        consoleItem.setPostDate(item.getPostDate());\n        consoleItem.setArchivedDate(item.getArchiveDate());\n        consoleItem.setContent(item.getContent());\n\n        return consoleItem;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/CaseDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.cases;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseDefinition;\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseItem;\nimport org.bonitasoft.web.rest.server.datastore.CommonDatastore;\nimport org.bonitasoft.web.rest.server.engineclient.EngineAPIAccessor;\nimport org.bonitasoft.web.rest.server.engineclient.EngineClientFactory;\nimport org.bonitasoft.web.rest.server.framework.api.*;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIForbiddenException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIIncorrectIdException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException;\nimport org.bonitasoft.web.toolkit.client.common.util.MapUtil;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Séverin Moussel\n * @author Celine Souchet\n */\npublic class CaseDatastore extends CommonDatastore<CaseItem, ProcessInstance>\n        implements DatastoreHasGet<CaseItem>, DatastoreHasSearch<CaseItem>,\n        DatastoreHasDelete, DatastoreHasAdd<CaseItem>, DatastoreHasUpdate<CaseItem> {\n\n    public CaseDatastore(final APISession engineSession) {\n        super(engineSession);\n    }\n\n    @Override\n    protected CaseItem convertEngineToConsoleItem(final ProcessInstance item) {\n        return new CaseItemConverter().convert(item);\n    }\n\n    public long count(final String search, final String orders, final Map<String, String> filters) {\n        return search(0, 0, search, orders, filters).getTotal();\n    }\n\n    /**\n     * convenience for stubbing during unit test\n     *\n     * @see org.bonitasoft.web.rest.server.datastore.CommonDatastore#convertEngineToConsoleSearch(int, int,\n     *      org.bonitasoft.engine.search.SearchResult)\n     */\n    @Override\n    protected ItemSearchResult<CaseItem> convertEngineToConsoleSearch(final int page, final int resultsByPage,\n            final SearchResult<ProcessInstance> engineSearchResults) {\n        return super.convertEngineToConsoleSearch(page, resultsByPage, engineSearchResults);\n    }\n\n    @Override\n    public ItemSearchResult<CaseItem> search(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n        try {\n            final SearchOptionsBuilder builder = buildSearchOptions(page, resultsByPage, search, orders, filters);\n            final SearchResult<ProcessInstance> searchResult = searchProcessInstances(filters, builder.done());\n            return convertEngineToConsoleSearch(page, resultsByPage, searchResult);\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    protected SearchOptionsBuilder buildSearchOptions(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n        // Build search\n        final SearchOptionsBuilder builder = SearchOptionsBuilderUtil.buildSearchOptions(page, resultsByPage, orders,\n                search);\n        addLongFilterToSearchBuilder(filters, builder, CaseItem.ATTRIBUTE_START_DATE,\n                ProcessInstanceSearchDescriptor.START_DATE);\n        addLongFilterToSearchBuilder(filters, builder, CaseItem.ATTRIBUTE_END_DATE,\n                ProcessInstanceSearchDescriptor.END_DATE);\n        addLongFilterToSearchBuilder(filters, builder, CaseItem.ATTRIBUTE_LAST_UPDATE_DATE,\n                ProcessInstanceSearchDescriptor.LAST_UPDATE);\n        addLongFilterToSearchBuilder(filters, builder, CaseItem.ATTRIBUTE_PROCESS_ID,\n                ProcessInstanceSearchDescriptor.PROCESS_DEFINITION_ID);\n        addLongFilterToSearchBuilder(filters, builder, CaseItem.ATTRIBUTE_ROOT_CASE_ID,\n                ProcessInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID);\n        addAddDifferentFromRootIdFilterIfNecessary(filters, builder);\n        addStringFilterToSearchBuilder(filters, builder, CaseItem.ATTRIBUTE_PROCESS_NAME,\n                ProcessInstanceSearchDescriptor.NAME);\n        addLongFilterToSearchBuilder(filters, builder, CaseItem.ATTRIBUTE_STARTED_BY_USER_ID,\n                ProcessInstanceSearchDescriptor.STARTED_BY);\n        addStringFilterToSearchBuilder(filters, builder, CaseItem.ATTRIBUTE_SEARCH_INDEX_1_VALUE,\n                ProcessInstanceSearchDescriptor.STRING_INDEX_1);\n        addStringFilterToSearchBuilder(filters, builder, CaseItem.ATTRIBUTE_SEARCH_INDEX_2_VALUE,\n                ProcessInstanceSearchDescriptor.STRING_INDEX_2);\n        addStringFilterToSearchBuilder(filters, builder, CaseItem.ATTRIBUTE_SEARCH_INDEX_3_VALUE,\n                ProcessInstanceSearchDescriptor.STRING_INDEX_3);\n        addStringFilterToSearchBuilder(filters, builder, CaseItem.ATTRIBUTE_SEARCH_INDEX_4_VALUE,\n                ProcessInstanceSearchDescriptor.STRING_INDEX_4);\n        addStringFilterToSearchBuilder(filters, builder, CaseItem.ATTRIBUTE_SEARCH_INDEX_5_VALUE,\n                ProcessInstanceSearchDescriptor.STRING_INDEX_5);\n        addCallerFilterToSearchBuilderIfNecessary(filters, builder);\n        builder.differentFrom(ProcessInstanceSearchDescriptor.STATE_ID, ProcessInstanceState.COMPLETED.getId());\n        builder.differentFrom(ProcessInstanceSearchDescriptor.STATE_ID, ProcessInstanceState.CANCELLED.getId());\n        builder.differentFrom(ProcessInstanceSearchDescriptor.STATE_ID, ProcessInstanceState.ABORTED.getId());\n        return builder;\n    }\n\n    protected void addAddDifferentFromRootIdFilterIfNecessary(Map<String, String> filters,\n            SearchOptionsBuilder builder) {\n        /*\n         * When filtering on Root Case Id, we want all the subprocesses of the root case to be returned.\n         * Not the root case itself.\n         */\n        if (filters.containsKey(CaseItem.ATTRIBUTE_ROOT_CASE_ID)) {\n            builder.differentFrom(ProcessInstanceSearchDescriptor.ID,\n                    MapUtil.getValueAsLong(filters, CaseItem.ATTRIBUTE_ROOT_CASE_ID));\n        }\n    }\n\n    protected void addCallerFilterToSearchBuilderIfNecessary(final Map<String, String> filters,\n            final SearchOptionsBuilder builder) {\n        /*\n         * By default we add a caller filter of -1 to avoid having sub processes.\n         * If caller is forced to any then we don't need to add the filter.\n         */\n        if (!filters.containsKey(CaseItem.FILTER_CALLER)) {\n            builder.filter(ProcessInstanceSearchDescriptor.CALLER_ID, -1);\n        } else if (!\"any\".equalsIgnoreCase(filters.get(CaseItem.FILTER_CALLER))) {\n            builder.filter(ProcessInstanceSearchDescriptor.CALLER_ID,\n                    MapUtil.getValueAsLong(filters, CaseItem.FILTER_CALLER));\n        }\n    }\n\n    protected SearchResult<ProcessInstance> searchProcessInstances(final Map<String, String> filters,\n            final SearchOptions searchOptions) throws BonitaException {\n        final ProcessAPI processAPI = getProcessAPI();\n\n        if (filters.containsKey(CaseItem.FILTER_USER_ID)) {\n            return processAPI.searchOpenProcessInstancesInvolvingUser(\n                    MapUtil.getValueAsLong(filters, CaseItem.FILTER_USER_ID), searchOptions);\n        }\n        if (filters.containsKey(CaseItem.FILTER_SUPERVISOR_ID)) {\n            if (filters.containsKey(CaseItem.FILTER_STATE)\n                    && (\"failed\".equalsIgnoreCase(filters.get(CaseItem.FILTER_STATE))\n                            || \"error\".equalsIgnoreCase(filters.get(CaseItem.FILTER_STATE)))) {\n                return processAPI.searchFailedProcessInstancesSupervisedBy(\n                        MapUtil.getValueAsLong(filters, CaseItem.FILTER_SUPERVISOR_ID), searchOptions);\n            } else {\n                return processAPI.searchOpenProcessInstancesSupervisedBy(\n                        MapUtil.getValueAsLong(filters, CaseItem.FILTER_SUPERVISOR_ID), searchOptions);\n            }\n        }\n        if (filters.containsKey(CaseItem.FILTER_STATE)\n                && (\"failed\".equalsIgnoreCase(filters.get(CaseItem.FILTER_STATE))\n                        || \"error\".equalsIgnoreCase(filters.get(CaseItem.FILTER_STATE)))) {\n            return processAPI.searchFailedProcessInstances(searchOptions);\n        }\n\n        return processAPI.searchProcessInstances(searchOptions);\n    }\n\n    @Override\n    public CaseItem get(final APIID id) {\n        try {\n            return convertEngineToConsoleItem(getProcessAPI().getProcessInstance(id.toLong()));\n        } catch (final ProcessInstanceNotFoundException e) {\n            return null;\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @Override\n    public void delete(final List<APIID> ids) {\n        try {\n            final ProcessAPI processApi = getProcessAPI();\n            for (final APIID id : ids) {\n                processApi.deleteProcessInstance(id.toLong());\n                processApi.deleteArchivedProcessInstancesInAllStates(id.toLong());\n            }\n        } catch (final BonitaException e) {\n            if (e.getCause() instanceof ProcessInstanceNotFoundException) {\n                throw new APIItemNotFoundException(CaseDefinition.TOKEN);\n            } else {\n                throw new APIException(e);\n            }\n        }\n    }\n\n    @Override\n    public CaseItem add(final CaseItem caseItem) {\n        final EngineClientFactory factory = new EngineClientFactory(new EngineAPIAccessor(getEngineSession()));\n        return new CaseSarter(caseItem, factory.createCaseEngineClient(), factory.createProcessEngineClient()).start();\n    }\n\n    public ProcessAPI getProcessAPI() throws BonitaException {\n        return TenantAPIAccessor.getProcessAPI(getEngineSession());\n    }\n\n    @Override\n    public CaseItem update(APIID id, Map<String, String> attributes) {\n        final String state = MapUtil.getValue(attributes, CaseItem.ATTRIBUTE_STATE, null);\n        try {\n            final ProcessAPI processApi = getProcessAPI();\n            if (state == null) {\n                throw new APIForbiddenException(\"Only \" + CaseItem.ATTRIBUTE_STATE + \"can be updated on a case\");\n            }\n            ProcessInstanceState instanceState = ProcessInstanceState.valueOf(state.toUpperCase());\n            if (instanceState == ProcessInstanceState.CANCELLED) {\n                processApi.cancelProcessInstance(id.toLong());\n                return null;\n            }\n            throw new APIForbiddenException(\"Can't update a case state to \\\"\" + state + \"\\\"\");\n        } catch (final BonitaException e) {\n            if (e.getCause() instanceof ProcessInstanceNotFoundException) {\n                throw new APIItemNotFoundException(CaseDefinition.TOKEN);\n            } else {\n                throw new APIException(e);\n            }\n        } catch (final IllegalArgumentException e) {\n            throw new APIIncorrectIdException(\"Case state \\\"\" + state + \"\\\" doesn't exist\");\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/CaseDocumentDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.cases;\n\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.activation.FileTypeMap;\nimport javax.activation.MimetypesFileTypeMap;\n\nimport org.apache.commons.io.IOUtils;\nimport org.bonitasoft.console.common.server.preferences.properties.PropertiesFactory;\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.document.Document;\nimport org.bonitasoft.engine.bpm.document.DocumentException;\nimport org.bonitasoft.engine.bpm.document.DocumentNotFoundException;\nimport org.bonitasoft.engine.bpm.document.DocumentValue;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.io.FileContent;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseDocumentItem;\nimport org.bonitasoft.web.rest.server.datastore.CommonDatastore;\nimport org.bonitasoft.web.rest.server.datastore.filter.Filters;\nimport org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator;\nimport org.bonitasoft.web.rest.server.datastore.utils.Sorts;\nimport org.bonitasoft.web.rest.server.framework.api.*;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.util.StringUtil;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Fabio Lombardi\n */\npublic class CaseDocumentDatastore extends CommonDatastore<CaseDocumentItem, Document>\n        implements DatastoreHasAdd<CaseDocumentItem>,\n        DatastoreHasGet<CaseDocumentItem>,\n        DatastoreHasUpdate<CaseDocumentItem>, DatastoreHasDelete {\n\n    protected final ProcessAPI processAPI;\n\n    final long maxSizeForTenant;\n\n    final FileTypeMap mimetypesFileTypeMap;\n\n    final BonitaHomeFolderAccessor tenantFolder;\n\n    protected SearchOptionsCreator searchOptionsCreator;\n\n    /**\n     * Default constructor.\n     */\n    public CaseDocumentDatastore(final APISession engineSession, final ProcessAPI processAPI,\n            final BonitaHomeFolderAccessor tenantFolder) {\n        super(engineSession);\n        this.processAPI = processAPI;\n        this.tenantFolder = tenantFolder;\n        maxSizeForTenant = PropertiesFactory.getConsoleProperties().getMaxSize();\n        mimetypesFileTypeMap = new MimetypesFileTypeMap();\n    }\n\n    // GET Method\n    @Override\n    public CaseDocumentItem get(final APIID id) {\n        try {\n            final Document documentItem = processAPI.getDocument(id.toLong());\n            return convertEngineToConsoleItem(documentItem);\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    // POST Method\n    @Override\n    public CaseDocumentItem add(final CaseDocumentItem item) {\n\n        long caseId = -1;\n        int index = -1;\n        String documentDescription = \"\";\n        DocumentValue documentValue = null;\n\n        try {\n            if (item.getAttributeValue(CaseDocumentItem.ATTRIBUTE_CASE_ID) != null) {\n                caseId = Long.valueOf(item.getAttributeValue(CaseDocumentItem.ATTRIBUTE_CASE_ID));\n            }\n\n        } catch (final NumberFormatException e) {\n            throw new APIException(\"Error while attaching a new document. Request with bad case id value.\");\n        }\n\n        final String documentName = item.getAttributeValue(CaseDocumentItem.ATTRIBUTE_NAME);\n        final String uploadPath = item.getAttributeValue(CaseDocumentItem.ATTRIBUTE_UPLOAD_PATH);\n        final String urlPath = item.getAttributeValue(CaseDocumentItem.ATTRIBUTE_URL);\n\n        if (item.getAttributeValue(CaseDocumentItem.ATTRIBUTE_DESCRIPTION) != null) {\n            documentDescription = item.getAttributeValue(CaseDocumentItem.ATTRIBUTE_DESCRIPTION);\n        }\n\n        if (item.getAttributeValue(CaseDocumentItem.ATTRIBUTE_INDEX) != null) {\n            index = Integer.parseInt(item.getAttributeValue(CaseDocumentItem.ATTRIBUTE_INDEX));\n        }\n\n        try {\n\n            if (caseId != -1 && documentName != null) {\n\n                if (urlPath != null) {\n                    documentValue = buildDocumentValueFromUrl(urlPath, -1);\n                } else {\n                    documentValue = buildDocumentValueFromUploadPath(uploadPath, index, item.getFileName());\n                }\n                final Document document = processAPI.addDocument(caseId, documentName, documentDescription,\n                        documentValue);\n                return convertEngineToConsoleItem(document);\n            } else {\n                throw new APIException(\"Error while attaching a new document. Request with bad param value.\");\n            }\n        } catch (final BonitaException | IOException e) {\n            throw new APIException(e);\n        } finally {\n            if (urlPath == null) {\n                tenantFolder.removeUploadedTempContent(uploadPath);\n            }\n        }\n    }\n\n    // PUT Method\n    @Override\n    public CaseDocumentItem update(final APIID id, final Map<String, String> attributes) {\n        DocumentValue documentValue = null;\n\n        try {\n            final String urlPath;\n\n            if (attributes.containsKey(CaseDocumentItem.ATTRIBUTE_UPLOAD_PATH)\n                    || attributes.containsKey(CaseDocumentItem.ATTRIBUTE_URL)) {\n\n                if (attributes.containsKey(CaseDocumentItem.ATTRIBUTE_URL)) {\n                    urlPath = attributes.get(CaseDocumentItem.ATTRIBUTE_URL);\n                    documentValue = buildDocumentValueFromUrl(urlPath, -1);\n                } else {\n                    urlPath = attributes.get(CaseDocumentItem.ATTRIBUTE_UPLOAD_PATH);\n                    documentValue = buildDocumentValueFromUploadPath(urlPath, -1,\n                            attributes.get(CaseDocumentItem.ATTRIBUTE_CONTENT_FILENAME));\n                }\n\n                final Document document = processAPI.updateDocument(id.toLong(), documentValue);\n                return convertEngineToConsoleItem(document);\n\n            } else {\n                throw new APIException(\"Error while attaching a new document. Request with bad param value.\");\n            }\n        } catch (final BonitaException | IOException e) {\n            throw new APIException(e);\n        } finally {\n            if (attributes.containsKey(CaseDocumentItem.ATTRIBUTE_UPLOAD_PATH)) {\n                tenantFolder.removeUploadedTempContent(attributes.get(CaseDocumentItem.ATTRIBUTE_UPLOAD_PATH));\n            }\n        }\n\n    }\n\n    protected DocumentValue buildDocumentValueFromUploadPath(final String uploadKey, final int index, String fileName)\n            throws IOException {\n\n        String mimeType = null;\n        byte[] fileContent = null;\n\n        if (uploadKey != null) {\n            try {\n                final FileContent theSourceFile = tenantFolder.retrieveUploadedTempContent(uploadKey);\n                if (theSourceFile.getSize() > maxSizeForTenant * 1048576) {\n                    final String errorMessage = \"This document is exceeded \" + maxSizeForTenant + \"Mb\";\n                    throw new DocumentException(errorMessage);\n                }\n                try (InputStream inputStream = theSourceFile.getInputStream()) {\n                    fileContent = IOUtils.toByteArray(inputStream);\n                    if (StringUtil.isBlank(fileName)) {\n                        fileName = theSourceFile.getFileName();\n                    }\n                    mimeType = theSourceFile.getMimeType();\n                }\n            } catch (BonitaException e) {\n                throw new FileNotFoundException(\"Cannot find \" + uploadKey + \" in the tenant temp directory.\");\n            }\n        }\n\n        final DocumentValue documentValue = new DocumentValue(fileContent, mimeType, fileName);\n        if (index != -1) {\n            documentValue.setIndex(index);\n        }\n        return documentValue;\n    }\n\n    protected DocumentValue buildDocumentValueFromUrl(final String urlPath, final int index) {\n\n        final DocumentValue documentValue = new DocumentValue(urlPath);\n        if (index != -1) {\n            documentValue.setIndex(index);\n        }\n        return documentValue;\n\n    }\n\n    // GET Method for SEARCH\n    public ItemSearchResult<CaseDocumentItem> search(final int page, final int resultsByPage,\n            final String search, final Map<String, String> filters, final String orders) {\n        return searchDocument(page, resultsByPage, search, filters, orders);\n    }\n\n    protected ItemSearchResult<CaseDocumentItem> searchDocument(final int page, final int resultsByPage,\n            final String search,\n            final Map<String, String> filters, final String orders) {\n\n        try {\n            final APIID supervisorAPIID = APIID.makeAPIID(filters.get(CaseDocumentItem.FILTER_SUPERVISOR_ID));\n\n            if (supervisorAPIID != null) {\n                filters.remove(CaseDocumentItem.FILTER_SUPERVISOR_ID);\n            }\n            searchOptionsCreator = buildSearchOptionCreator(page, resultsByPage, search, filters, orders);\n\n            final SearchResult<Document> engineSearchResults;\n            if (supervisorAPIID != null && supervisorAPIID.isValidLongID()) {\n                engineSearchResults = processAPI.searchDocumentsSupervisedBy(supervisorAPIID.toLong(),\n                        searchOptionsCreator.create());\n            } else {\n                engineSearchResults = processAPI.searchDocuments(searchOptionsCreator.create());\n            }\n            return new ItemSearchResult<>(page, resultsByPage, engineSearchResults.getCount(),\n                    convertEngineToConsoleItem(engineSearchResults.getResult()));\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    protected SearchOptionsCreator buildSearchOptionCreator(final int page, final int resultsByPage,\n            final String search, final Map<String, String> filters,\n            final String orders) {\n        return new SearchOptionsCreator(page, resultsByPage, search, new Sorts(orders,\n                getDocumentSearchAttributeConverter()),\n                new Filters(filters, new CaseDocumentFilterCreator(getDocumentSearchAttributeConverter())));\n    }\n\n    private CaseDocumentSearchAttributeConverter getDocumentSearchAttributeConverter() {\n        return new CaseDocumentSearchAttributeConverter();\n    }\n\n    // DELETE Method\n    @Override\n    public void delete(final List<APIID> ids) {\n        if (ids != null) {\n\n            try {\n                for (final APIID id : ids) {\n                    processAPI.removeDocument(id.toLong());\n                }\n            } catch (final DocumentNotFoundException e) {\n                throw new APIException(\"Error while deleting a document. Document not found\");\n            } catch (final DeletionException e) {\n                throw new APIException(e);\n            }\n\n        } else {\n            throw new APIException(\"Error while deleting a document. Document id not specified in the request\");\n        }\n    }\n\n    @Override\n    protected CaseDocumentItem convertEngineToConsoleItem(final Document item) {\n        if (item != null) {\n            return new CaseDocumentItemConverter().convert(item);\n        }\n        return null;\n    }\n\n    private List<CaseDocumentItem> convertEngineToConsoleItem(final List<Document> result) {\n        if (result != null) {\n            return new CaseDocumentItemConverter().convert(result);\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/CaseDocumentFilterCreator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.cases;\n\nimport org.bonitasoft.web.rest.server.datastore.filter.GenericFilterCreator;\n\n/**\n * @author Fabio Lombardi\n */\n\nclass CaseDocumentFilterCreator extends GenericFilterCreator {\n\n    CaseDocumentFilterCreator(final CaseDocumentSearchAttributeConverter converter) {\n        super(converter);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/CaseDocumentItemConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.cases;\n\nimport org.bonitasoft.engine.bpm.document.Document;\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseDocumentItem;\nimport org.bonitasoft.web.rest.server.datastore.converter.ItemConverter;\n\npublic class CaseDocumentItemConverter extends ItemConverter<CaseDocumentItem, Document> {\n\n    @Override\n    public CaseDocumentItem convert(final Document engineItem) {\n        final CaseDocumentItem item = new CaseDocumentItem();\n        item.setId(String.valueOf(engineItem.getId()));\n        item.setCaseId(String.valueOf(engineItem.getProcessInstanceId()));\n        item.setName(engineItem.getName());\n        item.setVersion(engineItem.getVersion());\n        item.setDescription(engineItem.getDescription());\n        item.setSubmittedBy(engineItem.getAuthor());\n        item.setFileName(engineItem.getContentFileName());\n        item.setCreationDate(engineItem.getCreationDate());\n        item.setMIMEType(engineItem.getContentMimeType());\n        item.setHasContent(String.valueOf(engineItem.hasContent()));\n        item.setStorageId(engineItem.getContentStorageId());\n        item.setURL(engineItem.getUrl());\n        item.setIndex(engineItem.getIndex());\n        return item;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/CaseDocumentSearchAttributeConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.cases;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.document.DocumentsSearchDescriptor;\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseDocumentItem;\nimport org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter;\nimport org.bonitasoft.web.toolkit.client.common.util.MapUtil;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * @author Fabio Lombardi\n */\npublic class CaseDocumentSearchAttributeConverter implements AttributeConverter {\n\n    private final Map<String, String> mapping;\n\n    public CaseDocumentSearchAttributeConverter() {\n        mapping = createMapping();\n    }\n\n    private Map<String, String> createMapping() {\n        final Map<String, String> mapping = new HashMap<>();\n        mapping.put(CaseDocumentItem.ATTRIBUTE_ID, CaseDocumentItem.ATTRIBUTE_ID);\n        mapping.put(CaseDocumentItem.ATTRIBUTE_SUBMITTED_BY_USER_ID, DocumentsSearchDescriptor.DOCUMENT_AUTHOR);\n        mapping.put(CaseDocumentItem.ATTRIBUTE_NAME, DocumentsSearchDescriptor.DOCUMENT_NAME);\n        mapping.put(CaseDocumentItem.ATTRIBUTE_CREATION_DATE, DocumentsSearchDescriptor.DOCUMENT_CREATIONDATE);\n        mapping.put(CaseDocumentItem.ATTRIBUTE_DESCRIPTION, DocumentsSearchDescriptor.DOCUMENT_DESCRIPTION);\n        mapping.put(CaseDocumentItem.ATTRIBUTE_INDEX, DocumentsSearchDescriptor.LIST_INDEX);\n        mapping.put(CaseDocumentItem.ATTRIBUTE_CASE_ID, DocumentsSearchDescriptor.PROCESSINSTANCE_ID);\n\n        return mapping;\n    }\n\n    @Override\n    public String convert(final String attribute) {\n        return MapUtil.getMandatory(mapping, attribute);\n    }\n\n    @Override\n    public Map<String, ItemAttribute.TYPE> getValueTypeMapping() {\n        return Collections.emptyMap();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/CaseItemConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.cases;\n\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseItem;\nimport org.bonitasoft.web.rest.server.datastore.converter.ItemConverter;\n\npublic class CaseItemConverter extends ItemConverter<CaseItem, ProcessInstance> {\n\n    @Override\n    public CaseItem convert(final ProcessInstance process) {\n        final CaseItem item = new CaseItem();\n        item.setId(process.getId());\n        item.setLastUpdateDate(process.getLastUpdate());\n        item.setState(process.getState());\n        item.setStartDate(process.getStartDate());\n        item.setEndDate(process.getEndDate());\n        item.setProcessId(process.getProcessDefinitionId());\n        item.setRootCaseId(process.getRootProcessInstanceId());\n        item.setStartedByUserId(process.getStartedBy());\n        item.setStartedBySubstituteUserId(process.getStartedBySubstitute());\n        item.setSearchIndex1Label(process.getStringIndexLabel(1));\n        item.setSearchIndex2Label(process.getStringIndexLabel(2));\n        item.setSearchIndex3Label(process.getStringIndexLabel(3));\n        item.setSearchIndex4Label(process.getStringIndexLabel(4));\n        item.setSearchIndex5Label(process.getStringIndexLabel(5));\n        item.setSearchIndex1Value(process.getStringIndex1());\n        item.setSearchIndex2Value(process.getStringIndex2());\n        item.setSearchIndex3Value(process.getStringIndex3());\n        item.setSearchIndex4Value(process.getStringIndex4());\n        item.setSearchIndex5Value(process.getStringIndex5());\n        item.setCallerId(process.getCallerId());\n        return item;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/CaseSarter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.cases;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bpm.data.DataDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseItem;\nimport org.bonitasoft.web.rest.server.datastore.utils.VariableMapper;\nimport org.bonitasoft.web.rest.server.datastore.utils.VariablesMapper;\nimport org.bonitasoft.web.rest.server.engineclient.CaseEngineClient;\nimport org.bonitasoft.web.rest.server.engineclient.ProcessEngineClient;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.i18n.T_;\nimport org.bonitasoft.web.toolkit.client.common.texttemplate.Arg;\nimport org.bonitasoft.web.toolkit.client.common.util.StringUtil;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\npublic class CaseSarter {\n\n    private final CaseItem caseItem;\n\n    private final CaseEngineClient caseEngineClient;\n\n    private final ProcessEngineClient processEngineClient;\n\n    private final Long processId;\n\n    private final Long delegateUserId;\n\n    public CaseSarter(final CaseItem caseItem, final CaseEngineClient caseEngineClient,\n            final ProcessEngineClient processEngineClient) {\n        this.caseItem = caseItem;\n        processId = caseItem.getProcessId().toLong();\n        if (caseItem.hasAttribute(CaseItem.ATTRIBUTE_STARTED_BY_SUBSTITUTE_USER_ID)\n                && caseItem.getStartedBySubstituteUserId() != null) {\n            APIID startedBySubstituteUserId = caseItem.getStartedBySubstituteUserId();\n            if (startedBySubstituteUserId.isValidLongID()) {\n                delegateUserId = startedBySubstituteUserId.toLong();\n            } else {\n                delegateUserId = -1L;\n            }\n        } else {\n            delegateUserId = -1L;\n        }\n        this.caseEngineClient = caseEngineClient;\n        this.processEngineClient = processEngineClient;\n    }\n\n    public CaseItem start() {\n        final HashMap<String, Serializable> variables = getVariables(caseItem);\n        if (variables.isEmpty()) {\n            return startCase();\n        } else {\n            return startCaseWithVariables(variables);\n        }\n    }\n\n    private HashMap<String, Serializable> getVariables(final CaseItem caseItem) {\n        final String jsonVariables = caseItem.getAttributeValue(CaseItem.ATTRIBUTE_VARIABLES);\n        if (StringUtil.isBlank(jsonVariables)) {\n            return new HashMap<>();\n        }\n        return buildVariablesMap(jsonVariables);\n    }\n\n    private HashMap<String, Serializable> buildVariablesMap(final String jsonValue) {\n        final List<DataDefinition> dataDefinitions = processEngineClient.getProcessDataDefinitions(processId);\n\n        final HashMap<String, Serializable> map = new HashMap<>();\n        for (final VariableMapper var : VariablesMapper.fromJson(jsonValue).getVariables()) {\n            final DataDefinition data = getDataDefinitionByName(var.getName(), dataDefinitions);\n            map.put(var.getName(), var.getSerializableValue(data.getClassName()));\n        }\n        return map;\n    }\n\n    private CaseItem startCaseWithVariables(final HashMap<String, Serializable> variables) {\n        final ProcessInstance processInstance = caseEngineClient.start(delegateUserId, processId, variables);\n        return new CaseItemConverter().convert(processInstance);\n    }\n\n    private CaseItem startCase() {\n        final ProcessInstance processInstance = caseEngineClient.start(delegateUserId, processId);\n        return new CaseItemConverter().convert(processInstance);\n    }\n\n    private DataDefinition getDataDefinitionByName(final String dataName, final List<DataDefinition> dataDefinitions) {\n        for (final DataDefinition dataDefinition : dataDefinitions) {\n            if (dataDefinition.getName().equals(dataName)) {\n                return dataDefinition;\n            }\n        }\n        throw new APIException(new T_(\"Data definition %dataName% doesn't exists for process %processId%\",\n                new Arg(\"dataName\", dataName), new Arg(\"processId\",\n                        caseItem.getProcessId())));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/CaseVariableDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.cases;\n\nimport static org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil.computeIndex;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.data.DataInstance;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseVariableItem;\nimport org.bonitasoft.web.rest.server.datastore.CommonDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasUpdate;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.rest.server.framework.utils.converter.ConversionException;\nimport org.bonitasoft.web.rest.server.framework.utils.converter.TypeConverter;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIMethodNotAllowedException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Colin PUY\n */\npublic class CaseVariableDatastore extends CommonDatastore<CaseVariableItem, DataInstance>\n        implements DatastoreHasSearch<CaseVariableItem>, DatastoreHasUpdate<CaseVariableItem> {\n\n    private final TypeConverter converter = new TypeConverter();\n\n    public CaseVariableDatastore(final APISession engineSession) {\n        super(engineSession);\n    }\n\n    @Override\n    protected CaseVariableItem convertEngineToConsoleItem(final DataInstance item) {\n        return new CaseVariableItem(item.getContainerId(),\n                item.getName(), item.getValue(), item.getClassName(), item.getDescription());\n    }\n\n    private List<CaseVariableItem> convert(final List<DataInstance> dataInstances) {\n        final List<CaseVariableItem> caseVariables = new ArrayList<>();\n        for (final DataInstance dataInstance : dataInstances) {\n            caseVariables.add(convertEngineToConsoleItem(dataInstance));\n        }\n        return caseVariables;\n    }\n\n    protected ProcessAPI getEngineProcessAPI() {\n        try {\n            return TenantAPIAccessor.getProcessAPI(getEngineSession());\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @Override\n    public CaseVariableItem update(final APIID id, final Map<String, String> attributes) {\n        throw new APIMethodNotAllowedException(\"Not implemented / No need to / Not used\");\n    }\n\n    @Override\n    public ItemSearchResult<CaseVariableItem> search(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n        throw new APIMethodNotAllowedException(\"Not implemented / No need to / Not used\");\n    }\n\n    public void updateVariableValue(final long caseId, final String variableName, final String className,\n            final String newValue) {\n        try {\n            final Serializable converteValue = converter.convert(className, newValue);\n            getEngineProcessAPI().updateProcessDataInstance(variableName, caseId, converteValue);\n        } catch (final BonitaException | ConversionException e) {\n            throw new APIException(\"Error when updating case variable\", e);\n        }\n    }\n\n    public ItemSearchResult<CaseVariableItem> findByCaseId(final long caseId, final int page, final int resultsByPage) {\n        try {\n            final List<DataInstance> processDataInstances = getEngineProcessAPI().getProcessDataInstances(caseId,\n                    computeIndex(page, resultsByPage), resultsByPage);\n            return new ItemSearchResult<>(page, resultsByPage,\n                    countByCaseId(caseId), convert(processDataInstances));\n        } catch (final BonitaException e) {\n            throw new APIException(\"Error when getting case variables\", e);\n        }\n    }\n\n    private long countByCaseId(final long caseId) throws BonitaException {\n        return getEngineProcessAPI().getNumberOfProcessDataInstances(caseId);\n    }\n\n    public CaseVariableItem findById(final long caseId, final String variableName) {\n        try {\n            return convertEngineToConsoleItem(getEngineProcessAPI().getProcessDataInstance(variableName, caseId));\n        } catch (final BonitaException e) {\n            throw new APIException(\"Error while getting case variable\", e);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/CommentDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.cases;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.console.common.server.api.CommandCaller;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.comment.Comment;\nimport org.bonitasoft.engine.bpm.comment.SearchCommentsDescriptor;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.cases.CommentItem;\nimport org.bonitasoft.web.rest.server.datastore.CommonDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasAdd;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Vincent Elcrin\n */\npublic class CommentDatastore extends CommonDatastore<CommentItem, Comment>\n        implements DatastoreHasAdd<CommentItem>, DatastoreHasSearch<CommentItem> {\n\n    /**\n     * Conversion look up table to for sortable fields\n     */\n    private static final String[][] SORTABLE_FIELDS_LUT = {\n            { CommentItem.ATTRIBUTE_POST_DATE, SearchCommentsDescriptor.POSTDATE },\n    };\n\n    /**\n     * Command to fetch comments supervised by\n     */\n    private static final String COMMAND_SEARCH_COMMENTS_SUPERVISEDBY = \"searchSCommentSupervisedBy\";\n\n    private static final String PROPERTY_SEARCH_OPTION_KEY = \"SEARCH_OPTIONS_KEY\";\n\n    private static final String PROPERTY_SUPERVISOR_ID_KEY = \"supervisorId\";\n\n    /**\n     * Default Constructor.\n     *\n     * @param engineSession\n     */\n    public CommentDatastore(final APISession engineSession) {\n        super(engineSession);\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see org.bonitasoft.web.toolkit.server.api.DatastoreHasSearch#search(int, int, java.lang.String,\n     * java.lang.String, java.util.Map)\n     */\n    @Override\n    public ItemSearchResult<CommentItem> search(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n\n        // prepare search\n        final SearchOptionsBuilder builder = SearchOptionsBuilderUtil.buildSearchOptions(page, resultsByPage, \"\",\n                search);\n        adjustSearchBuilder(filters, builder);\n\n        /**\n         * Need to convert field name passed by API into sortable field provided by engine.\n         */\n        if (orders != null && !orders.isEmpty()) {\n            final String[] sort = orders.split(\" \");\n            for (int i = 0; i < SORTABLE_FIELDS_LUT.length; i++) {\n                if (sort[0].equals(SORTABLE_FIELDS_LUT[i][0])) {\n                    builder.sort(SORTABLE_FIELDS_LUT[i][1], Order.valueOf(sort[1]));\n                }\n            }\n        }\n\n        SearchResult<Comment> engineSearchResults = null;\n\n        /*\n         * Search depends on the type of user which is defined in the filter. Only one at a time.\n         */\n        final APIID teamManagerAPIID = APIID.makeAPIID(filters.get(CommentItem.FILTER_TEAM_MANAGER_ID));\n        final APIID supervisorAPIID = APIID.makeAPIID(filters.get(CommentItem.FILTER_SUPERVISOR_ID));\n        final APIID userAPIID = APIID.makeAPIID(filters.get(CommentItem.FILTER_USER_ID));\n\n        if (teamManagerAPIID != null && teamManagerAPIID.isValidLongID()) {\n            engineSearchResults = runTeamManagerSearch(teamManagerAPIID.toLong(), builder);\n        } else if (supervisorAPIID != null && supervisorAPIID.isValidLongID()) {\n            engineSearchResults = runSupervisorSearch(supervisorAPIID.toLong(), builder);\n        } else if (userAPIID != null && userAPIID.isValidLongID()) {\n            engineSearchResults = runUserSearch(userAPIID.toLong(), builder);\n        } else {\n            engineSearchResults = runCustomSearch(builder);\n        }\n\n        if (engineSearchResults != null) {\n            /*\n             * Process result to convert engine items into console items\n             */\n            final List<CommentItem> consoleSearchResults = new ArrayList<>();\n            for (final Comment comment : engineSearchResults.getResult()) {\n                consoleSearchResults.add(convertEngineToConsoleItem(comment));\n            }\n            return new ItemSearchResult<>(page, resultsByPage, engineSearchResults.getCount(), consoleSearchResults);\n        } else {\n            throw new APIException(\"Search failed for the following parameters <page: \" + page + \" - resulsByPage: \"\n                    + resultsByPage + \" - search: \" + search\n                    + \" - filters: \" + filters + \" - orders: \" + orders + \">\");\n        }\n    }\n\n    /**\n     * Search comments managed by specified user (ex administrator)\n     *\n     * @param builder\n     * @return\n     */\n    private SearchResult<Comment> runTeamManagerSearch(final long teamManagerId, final SearchOptionsBuilder builder) {\n        try {\n            final ProcessAPI processAPI = TenantAPIAccessor.getProcessAPI(getEngineSession());\n            return processAPI.searchCommentsManagedBy(teamManagerId, builder.done());\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    /**\n     * Search comment involving specified user.\n     *\n     * @param userId\n     * @param builder\n     * @return\n     */\n    private SearchResult<Comment> runUserSearch(final long userId, final SearchOptionsBuilder builder) {\n        try {\n            final ProcessAPI processAPI = TenantAPIAccessor.getProcessAPI(getEngineSession());\n            return processAPI.searchCommentsInvolvingUser(userId, builder.done());\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    /**\n     * Search custom. It's up to you!\n     *\n     * @param builder\n     * @return\n     */\n    private SearchResult<Comment> runCustomSearch(final SearchOptionsBuilder builder) {\n        try {\n            final ProcessAPI processAPI = TenantAPIAccessor.getProcessAPI(getEngineSession());\n            return processAPI.searchComments(builder.done());\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    /**\n     * Search comment supervised by specified user (ex process owner)\n     *\n     * @param supervisorId\n     * @param builder\n     * @return\n     */\n    @SuppressWarnings(\"unchecked\")\n    private SearchResult<Comment> runSupervisorSearch(final long supervisorId, final SearchOptionsBuilder builder) {\n        // FIXME change me with API method\n        return (SearchResult<Comment>) new CommandCaller(getEngineSession(), COMMAND_SEARCH_COMMENTS_SUPERVISEDBY)\n                .addParameter(PROPERTY_SEARCH_OPTION_KEY, builder.done())\n                .addParameter(PROPERTY_SUPERVISOR_ID_KEY, supervisorId)\n                .run();\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see org.bonitasoft.web.toolkit.server.api.DatastoreHasAdd#add(org.bonitasoft.web.toolkit.client.data.item.Item)\n     */\n    @Override\n    public CommentItem add(final CommentItem item) {\n        try {\n            final ProcessAPI processAPI = TenantAPIAccessor.getProcessAPI(getEngineSession());\n            return convertEngineToConsoleItem(\n                    processAPI.addProcessComment(item.getProcessInstanceId().toLong(), item.getContent()));\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // CONVERTS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Convert ProcessItem filters into engine filers\n     *\n     * @param filters\n     * @param builder\n     */\n    private void adjustSearchBuilder(final Map<String, String> filters, final SearchOptionsBuilder builder) {\n        addStringFilterToSearchBuilder(filters, builder, CommentItem.ATTRIBUTE_PROCESS_INSTANCE_ID,\n                SearchCommentsDescriptor.PROCESS_INSTANCE_ID);\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see\n     * org.bonitasoft.console.server.credentials.bpm.CommonDatastore#convertEngineToConsoleItem(java.io.Serializable)\n     */\n    @Override\n    protected CommentItem convertEngineToConsoleItem(final Comment engineItem) {\n        if (engineItem == null) {\n            return null;\n        }\n\n        final CommentItem consoleItem = new CommentItem();\n        consoleItem.setId(engineItem.getId());\n        consoleItem.setUserId(engineItem.getUserId());\n        consoleItem.setProcessInstanceId(engineItem.getProcessInstanceId());\n        consoleItem.setPostDate(engineItem.getPostDate());\n        consoleItem.setContent(engineItem.getContent());\n\n        return consoleItem;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/connector/ArchivedConnectorInstanceDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.connector;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.connector.ArchivedConnectorInstance;\nimport org.bonitasoft.engine.bpm.connector.ConnectorInstancesSearchDescriptor;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.connector.ArchivedConnectorInstanceItem;\nimport org.bonitasoft.web.rest.model.bpm.connector.ConnectorInstanceItem;\nimport org.bonitasoft.web.rest.server.datastore.CommonDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\n\n/**\n * @author Julien Mege\n */\npublic class ArchivedConnectorInstanceDatastore\n        extends CommonDatastore<ArchivedConnectorInstanceItem, ArchivedConnectorInstance> implements\n        DatastoreHasSearch<ArchivedConnectorInstanceItem> {\n\n    public ArchivedConnectorInstanceDatastore(final APISession engineSession) {\n        super(engineSession);\n    }\n\n    protected ProcessAPI getProcessAPI() {\n        try {\n            return TenantAPIAccessor.getProcessAPI(getEngineSession());\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @Override\n    public ItemSearchResult<ArchivedConnectorInstanceItem> search(final int page, final int resultsByPage,\n            final String search, final String orders,\n            final Map<String, String> filters) {\n\n        SearchResult<ArchivedConnectorInstance> searchConnectorInstances;\n        try {\n            searchConnectorInstances = getProcessAPI()\n                    .searchArchivedConnectorInstances(buildSearchOptions(page, resultsByPage, search, orders, filters));\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n\n        if (searchConnectorInstances != null) {\n            final List<ArchivedConnectorInstanceItem> convertedResult = convertEngineItemsIntoConsoleItems(\n                    searchConnectorInstances.getResult());\n            return new ItemSearchResult<>(page, convertedResult.size(), searchConnectorInstances.getCount(),\n                    convertedResult);\n        } else {\n            throw new APIException(\"Search failed for the following parameters <page: \" + page + \" - resulsByPage: \"\n                    + resultsByPage + \" - search: \" + search\n                    + \" - filters: \" + filters + \" - orders: \" + orders + \">\");\n        }\n    }\n\n    /**\n     * Build search option converting console filters into engine filter\n     *\n     * @param page\n     * @param resultsByPage\n     * @param search\n     * @param orders\n     * @param filters\n     * @return\n     */\n    protected SearchOptions buildSearchOptions(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n        final SearchOptionsBuilder builder = SearchOptionsBuilderUtil.buildSearchOptions(page, resultsByPage, orders,\n                search);\n        addStringFilterToSearchBuilder(filters, builder, ConnectorInstanceItem.ATTRIBUTE_CONTAINER_ID,\n                ConnectorInstancesSearchDescriptor.CONTAINER_ID);\n        addStringFilterToSearchBuilder(filters, builder, ConnectorInstanceItem.ATTRIBUTE_STATE,\n                ConnectorInstancesSearchDescriptor.STATE);\n        return builder.done();\n    }\n\n    /**\n     * Convert engine item into console item used by web\n     *\n     * @param engineItem\n     *        Item provided by engine\n     */\n    @Override\n    protected ArchivedConnectorInstanceItem convertEngineToConsoleItem(final ArchivedConnectorInstance engineItem) {\n        return new ArchivedConnectorInstanceItemWrapper(engineItem);\n    }\n\n    /**\n     * @param searchResult\n     * @return\n     */\n    protected List<ArchivedConnectorInstanceItem> convertEngineItemsIntoConsoleItems(\n            final List<ArchivedConnectorInstance> engineItemList) {\n        if (engineItemList != null) {\n            final List<ArchivedConnectorInstanceItem> consoleItemList = new ArrayList<>();\n            for (final ArchivedConnectorInstance engineItem : engineItemList) {\n                consoleItemList.add(convertEngineToConsoleItem(engineItem));\n            }\n            return consoleItemList;\n        } else {\n            throw new RuntimeException(\"List of engine items is null\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/connector/ArchivedConnectorInstanceItemWrapper.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.connector;\n\nimport org.bonitasoft.engine.bpm.connector.ArchivedConnectorInstance;\nimport org.bonitasoft.web.rest.model.bpm.connector.ArchivedConnectorInstanceItem;\n\n/**\n * Bridge object between engine and console web implementation of an item\n *\n * @author Julien Mege\n */\npublic class ArchivedConnectorInstanceItemWrapper extends ArchivedConnectorInstanceItem {\n\n    public ArchivedConnectorInstanceItemWrapper(final ArchivedConnectorInstance engineItem) {\n        if (engineItem == null) {\n            throw new IllegalArgumentException(\"Can't wrap null item\");\n        }\n        this.setId(engineItem.getId());\n        setName(engineItem.getName());\n        setVersion(engineItem.getVersion());\n        setState(engineItem.getState().toString());\n        this.setConnectorId(engineItem.getConnectorId());\n        setActivationEvent(engineItem.getActivationEvent().toString());\n        this.setContainerId(engineItem.getContainerId());\n        setContainerType(engineItem.getContainerType());\n        this.setSourceObjectId(engineItem.getSourceObjectId());\n        setArchivedDate(engineItem.getArchiveDate());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/connector/ConnectorInstanceDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.connector;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.connector.ConnectorInstance;\nimport org.bonitasoft.engine.bpm.connector.ConnectorInstancesSearchDescriptor;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.connector.ConnectorInstanceItem;\nimport org.bonitasoft.web.rest.server.datastore.CommonDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\n\n/**\n * @author Vincent Elcrin\n */\npublic class ConnectorInstanceDatastore extends CommonDatastore<ConnectorInstanceItem, ConnectorInstance>\n        implements DatastoreHasSearch<ConnectorInstanceItem> {\n\n    public ConnectorInstanceDatastore(final APISession engineSession) {\n        super(engineSession);\n    }\n\n    protected ProcessAPI getProcessAPI() {\n        try {\n            return TenantAPIAccessor.getProcessAPI(getEngineSession());\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @Override\n    public ItemSearchResult<ConnectorInstanceItem> search(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n\n        SearchResult<ConnectorInstance> searchConnectorInstances;\n        try {\n            searchConnectorInstances = getProcessAPI()\n                    .searchConnectorInstances(buildSearchOptions(page, resultsByPage, search, orders, filters));\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n\n        if (searchConnectorInstances != null) {\n            final List<ConnectorInstanceItem> convertedResult = convertEngineItemsIntoConsoleItems(\n                    searchConnectorInstances.getResult());\n            return new ItemSearchResult<>(page, resultsByPage, searchConnectorInstances.getCount(),\n                    convertedResult);\n        } else {\n            throw new APIException(\"Search failed for the following parameters <page: \" + page + \" - resulsByPage: \"\n                    + resultsByPage + \" - search: \" + search\n                    + \" - filters: \" + filters + \" - orders: \" + orders + \">\");\n        }\n    }\n\n    /**\n     * Build search option converting console filters into engine filter\n     *\n     * @param page\n     * @param resultsByPage\n     * @param search\n     * @param orders\n     * @param filters\n     * @return\n     */\n    protected SearchOptions buildSearchOptions(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n        final SearchOptionsBuilder builder = SearchOptionsBuilderUtil.buildSearchOptions(page, resultsByPage, orders,\n                search);\n        addStringFilterToSearchBuilder(filters, builder, ConnectorInstanceItem.ATTRIBUTE_CONTAINER_ID,\n                ConnectorInstancesSearchDescriptor.CONTAINER_ID);\n        addStringFilterToSearchBuilder(filters, builder, ConnectorInstanceItem.ATTRIBUTE_STATE,\n                ConnectorInstancesSearchDescriptor.STATE);\n        addStringFilterToSearchBuilder(filters, builder, ConnectorInstanceItem.ATTRIBUTE_CONTAINER_TYPE,\n                ConnectorInstancesSearchDescriptor.CONTAINER_TYPE);\n        addStringFilterToSearchBuilder(filters, builder, ConnectorInstanceItem.ATTRIBUTE_ACTIVATION_EVENT,\n                ConnectorInstancesSearchDescriptor.ACTIVATION_EVENT);\n        addStringFilterToSearchBuilder(filters, builder, ConnectorInstanceItem.ATTRIBUTE_CONNECTOR_ID,\n                ConnectorInstancesSearchDescriptor.CONNECTOR_DEFINITION_ID);\n        addStringFilterToSearchBuilder(filters, builder, ConnectorInstanceItem.ATTRIBUTE_VERSION,\n                ConnectorInstancesSearchDescriptor.CONNECTOR_DEFINITION_VERSION);\n        addStringFilterToSearchBuilder(filters, builder, ConnectorInstanceItem.ATTRIBUTE_NAME,\n                ConnectorInstancesSearchDescriptor.NAME);\n        return builder.done();\n    }\n\n    /**\n     * Convert engine item into console item used by web\n     *\n     * @param engineItem\n     *        Item provided by engine\n     */\n    @Override\n    protected ConnectorInstanceItem convertEngineToConsoleItem(final ConnectorInstance engineItem) {\n        return new ConnectorInstanceItemWrapper(engineItem);\n    }\n\n    /**\n     * @param searchResult\n     * @return\n     */\n    protected List<ConnectorInstanceItem> convertEngineItemsIntoConsoleItems(\n            final List<ConnectorInstance> engineItemList) {\n        if (engineItemList != null) {\n            final List<ConnectorInstanceItem> consoleItemList = new ArrayList<>();\n            for (final ConnectorInstance engineItem : engineItemList) {\n                consoleItemList.add(convertEngineToConsoleItem(engineItem));\n            }\n            return consoleItemList;\n        } else {\n            throw new RuntimeException(\"List of engine items is null\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/connector/ConnectorInstanceItemWrapper.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.connector;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorInstance;\nimport org.bonitasoft.web.rest.model.bpm.connector.ConnectorInstanceItem;\n\n/**\n * Bridge object between engine and console web implementation of an item\n *\n * @author Vincent Elcrin\n */\npublic class ConnectorInstanceItemWrapper extends ConnectorInstanceItem {\n\n    public ConnectorInstanceItemWrapper(ConnectorInstance engineItem) {\n        if (engineItem == null) {\n            throw new IllegalArgumentException(\"Can't wrap null item\");\n        }\n\n        this.setId(engineItem.getId());\n        this.setName(engineItem.getName());\n        this.setVersion(engineItem.getVersion());\n        this.setState(engineItem.getState().toString());\n        this.setConnectorId(engineItem.getConnectorId());\n        this.setActivationEvent(engineItem.getActivationEvent().toString());\n        this.setContainerId(engineItem.getContainerId());\n        this.setContainerType(engineItem.getContainerType());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/connector/ConnectorInstanceResetStateConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.connector;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorStateReset;\nimport org.bonitasoft.web.rest.model.bpm.connector.ConnectorInstanceItem;\nimport org.bonitasoft.web.rest.server.framework.api.EnumConverter;\n\n/**\n * Convenient object to convert ATTRIBUTE_VALUE from {@link ConnectorInstanceItem} into {@link ConnectorStateReset}\n *\n * @author Vincent Elcrin\n */\npublic class ConnectorInstanceResetStateConverter implements EnumConverter<ConnectorStateReset> {\n\n    public final ConnectorStateReset convert(final String attributeStateValue) {\n        if (ConnectorInstanceItem.VALUE_RESET_STATE_TO_RE_EXECUTE.equals(attributeStateValue)) {\n            return ConnectorStateReset.TO_RE_EXECUTE;\n        } else if (ConnectorInstanceItem.VALUE_RESET_STATE_SKIPPED.equals(attributeStateValue)) {\n            return ConnectorStateReset.SKIPPED;\n        } else {\n            throw new RuntimeException(\"Can't convert following state into engine state <\" + attributeStateValue + \">\");\n        }\n    }\n\n    @Override\n    public String convert(ConnectorStateReset enumValue) {\n        switch (enumValue) {\n            case SKIPPED:\n                return ConnectorInstanceItem.VALUE_RESET_STATE_SKIPPED;\n            case TO_RE_EXECUTE:\n                return ConnectorInstanceItem.VALUE_RESET_STATE_TO_RE_EXECUTE;\n            default:\n                throw new RuntimeException(\"Can't convert <\" + enumValue + \">. Flow node type not supported.\");\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/connector/ConnectorInstanceStateConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.connector;\n\nimport org.bonitasoft.engine.bpm.connector.ConnectorState;\nimport org.bonitasoft.web.rest.model.bpm.connector.ConnectorInstanceItem;\nimport org.bonitasoft.web.rest.server.framework.api.EnumConverter;\n\n/**\n * Convenient object to convert ATTRIBUTE_VALUE from {@link ConnectorInstanceItem} into {@link ConnectorState}\n *\n * @author Vincent Elcrin\n */\npublic class ConnectorInstanceStateConverter implements EnumConverter<ConnectorState> {\n\n    public final ConnectorState convert(final String attributeStateValue) {\n        if (ConnectorInstanceItem.VALUE_STATE_DONE.equals(attributeStateValue)) {\n            return ConnectorState.DONE;\n        } else if (ConnectorInstanceItem.VALUE_STATE_FAILED.equals(attributeStateValue)) {\n            return ConnectorState.FAILED;\n        } else if (ConnectorInstanceItem.VALUE_STATE_SKIPPED.equals(attributeStateValue)) {\n            return ConnectorState.SKIPPED;\n        } else if (ConnectorInstanceItem.VALUE_STATE_TO_BE_EXECUTED.equals(attributeStateValue)) {\n            return ConnectorState.TO_BE_EXECUTED;\n        } else if (ConnectorInstanceItem.VALUE_STATE_TO_RE_EXECUTE.equals(attributeStateValue)) {\n            return ConnectorState.TO_RE_EXECUTE;\n        } else {\n            throw new RuntimeException(\"Can't convert following state into engine state <\" + attributeStateValue + \">\");\n        }\n    }\n\n    @Override\n    public String convert(ConnectorState enumValue) {\n        switch (enumValue) {\n            case DONE:\n                return ConnectorInstanceItem.VALUE_STATE_DONE;\n            case FAILED:\n                return ConnectorInstanceItem.VALUE_STATE_FAILED;\n            case SKIPPED:\n                return ConnectorInstanceItem.VALUE_STATE_SKIPPED;\n            case TO_BE_EXECUTED:\n                return ConnectorInstanceItem.VALUE_STATE_TO_BE_EXECUTED;\n            case TO_RE_EXECUTE:\n                return ConnectorInstanceItem.VALUE_STATE_TO_RE_EXECUTE;\n            default:\n                throw new RuntimeException(\"Can't convert <\" + enumValue + \">. Flow node type not supported.\");\n        }\n\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/AbstractActivityDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.flownode;\n\nimport static org.bonitasoft.web.toolkit.client.common.util.StringUtil.isBlank;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.data.DataInstance;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceNotFoundException;\nimport org.bonitasoft.engine.bpm.flownode.ActivityStates;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ActivityDefinition;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ActivityItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.FlowNodeItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.HumanTaskItem;\nimport org.bonitasoft.web.rest.server.datastore.converter.ActivityAttributeConverter;\nimport org.bonitasoft.web.rest.server.datastore.filter.ActivityFilterCreator;\nimport org.bonitasoft.web.rest.server.datastore.filter.Filters;\nimport org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator;\nimport org.bonitasoft.web.rest.server.datastore.utils.Sorts;\nimport org.bonitasoft.web.rest.server.datastore.utils.VariableMapper;\nimport org.bonitasoft.web.rest.server.datastore.utils.VariablesMapper;\nimport org.bonitasoft.web.rest.server.engineclient.ActivityEngineClient;\nimport org.bonitasoft.web.rest.server.engineclient.EngineAPIAccessor;\nimport org.bonitasoft.web.rest.server.engineclient.EngineClientFactory;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasUpdate;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException;\nimport org.bonitasoft.web.toolkit.client.common.util.MapUtil;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Séverin Moussel\n */\npublic class AbstractActivityDatastore<CONSOLE_ITEM extends ActivityItem, ENGINE_ITEM extends ActivityInstance> extends\n        AbstractFlowNodeDatastore<CONSOLE_ITEM, ENGINE_ITEM>\n        implements DatastoreHasGet<CONSOLE_ITEM>, DatastoreHasUpdate<CONSOLE_ITEM> {\n\n    public AbstractActivityDatastore(final APISession engineSession) {\n        super(engineSession);\n    }\n\n    /**\n     * Fill a console item using the engine item passed.\n     *\n     * @param result\n     *        The console item to fill\n     * @param item\n     *        The engine item to use for filling\n     * @return This method returns the result parameter passed.\n     */\n    protected static ActivityItem fillConsoleItem(final ActivityItem result, final ActivityInstance item) {\n        FlowNodeDatastore.fillConsoleItem(result, item);\n\n        result.setReachStateDate(item.getReachedStateDate());\n        result.setLastUpdateDate(item.getLastUpdateDate());\n\n        return result;\n    }\n\n    @Override\n    public CONSOLE_ITEM get(final APIID id) {\n        try {\n            @SuppressWarnings(\"unchecked\")\n            final ENGINE_ITEM activityInstance = (ENGINE_ITEM) getProcessAPI().getActivityInstance(id.toLong());\n            return convertEngineToConsoleItem(activityInstance);\n        } catch (final ActivityInstanceNotFoundException e) {\n            throw new APIItemNotFoundException(ActivityDefinition.TOKEN, id);\n        }\n    }\n\n    @Override\n    protected SearchResult<ENGINE_ITEM> runSearch(final SearchOptionsBuilder builder,\n            final Map<String, String> filters) {\n        try {\n            @SuppressWarnings(\"unchecked\")\n            final SearchResult<ENGINE_ITEM> results = (SearchResult<ENGINE_ITEM>) getProcessAPI()\n                    .searchActivities(builder.done());\n            return results;\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @Override\n    protected SearchOptionsBuilder makeSearchOptionBuilder(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n\n        return new SearchOptionsCreator(page, resultsByPage, search,\n                new Sorts(orders, new ActivityAttributeConverter()), new Filters(filters,\n                        new ActivityFilterCreator()))\n                .getBuilder();\n    }\n\n    @Override\n    public CONSOLE_ITEM update(final APIID id, final Map<String, String> attributes) {\n        final String jsonVariables = MapUtil.getValue(attributes, ActivityItem.ATTRIBUTE_VARIABLES, null);\n        if (!isBlank(jsonVariables)) {\n            updateActivityVariables(id.toLong(), jsonVariables);\n        }\n        update(get(id), attributes);\n        return null;\n    }\n\n    private void updateActivityVariables(final long activityId, final String jsonValue) {\n        final ActivityEngineClient activityEngineclient = getActivityEngineClient();\n        final HashMap<String, Serializable> variables = buildVariablesMap(activityId, jsonValue, activityEngineclient);\n        activityEngineclient.updateVariables(activityId, variables);\n    }\n\n    private ActivityEngineClient getActivityEngineClient() {\n        return new EngineClientFactory(new EngineAPIAccessor(getEngineSession())).createActivityEngineClient();\n    }\n\n    private HashMap<String, Serializable> buildVariablesMap(final long activityId, final String jsonValue,\n            final ActivityEngineClient client) {\n        final HashMap<String, Serializable> map = new HashMap<>();\n        for (final VariableMapper var : VariablesMapper.fromJson(jsonValue).getVariables()) {\n            final DataInstance data = client.getDataInstance(var.getName(), activityId);\n            map.put(var.getName(), var.getSerializableValue(data.getClassName()));\n        }\n        return map;\n    }\n\n    protected void update(final CONSOLE_ITEM item, final Map<String, String> attributes) {\n        updateState(item, MapUtil.getValue(attributes, FlowNodeItem.ATTRIBUTE_STATE, null),\n                MapUtil.getValue(attributes, FlowNodeItem.ATTRIBUTE_EXECUTED_BY_USER_ID, null));\n    }\n\n    /**\n     * @param item\n     *        The item to update\n     * @param state\n     *        The state to set\n     */\n    protected void updateState(final CONSOLE_ITEM item, final String state, String userExecuteById) {\n        try {\n            if (state == null) {\n                return;\n            }\n            if (HumanTaskItem.VALUE_STATE_SKIPPED.equals(state) && item instanceof FlowNodeItem) {\n                getProcessAPI().setActivityStateByName(item.getId().toLong(), ActivityStates.SKIPPED_STATE);\n            } else if (HumanTaskItem.VALUE_STATE_COMPLETED.equals(state) && item instanceof ActivityItem) {\n                if (userExecuteById != null) {\n                    getProcessAPI().executeFlowNode(Long.valueOf(userExecuteById), item.getId().toLong());\n                } else {\n                    getProcessAPI().executeFlowNode(item.getId().toLong());\n                }\n            } else {\n                throw new APIException(\"Can't update \" + item.getClass().getName() + \" state to \\\"\" + state + \"\\\"\");\n            }\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/AbstractFlowNodeDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.flownode;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstanceSearchDescriptor;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.NotFoundException;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.flownode.FlowNodeDefinition;\nimport org.bonitasoft.web.rest.model.bpm.flownode.FlowNodeItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.TaskItem;\nimport org.bonitasoft.web.rest.server.datastore.CommonDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasUpdate;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIMethodNotAllowedException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Séverin Moussel\n * @author Celine Souchet\n */\npublic class AbstractFlowNodeDatastore<CONSOLE_ITEM extends FlowNodeItem, ENGINE_ITEM extends FlowNodeInstance>\n        extends CommonDatastore<CONSOLE_ITEM, ENGINE_ITEM>\n        implements DatastoreHasSearch<CONSOLE_ITEM>,\n        DatastoreHasGet<CONSOLE_ITEM>,\n        DatastoreHasUpdate<CONSOLE_ITEM> {\n\n    private DatastoreHasUpdate<FlowNodeItem> updateHelper;\n\n    public AbstractFlowNodeDatastore(final APISession engineSession) {\n        super(engineSession);\n    }\n\n    /**\n     * Fill a console item using the engine item passed.\n     *\n     * @param result\n     *        The console item to fill\n     * @param item\n     *        The engine item to use for filling\n     * @return This method returns the result parameter passed.\n     */\n    protected static FlowNodeItem fillConsoleItem(final FlowNodeItem result, final FlowNodeInstance item) {\n        result.setId(item.getId());\n        result.setName(item.getName());\n        result.setDisplayName(item.getDisplayName());\n        result.setDescription(item.getDescription());\n        result.setDisplayDescription(item.getDisplayDescription());\n        result.setExecutedByUserId(item.getExecutedBy());\n        result.setRootCaseId(item.getRootContainerId());\n        result.setParentCaseId(item.getParentProcessInstanceId());\n        result.setProcessId(item.getProcessDefinitionId());\n        result.setState(item.getState());\n        result.setType(item.getType().name());\n        result.setRootContainerId(item.getRootContainerId());\n        result.setExecutedBySubstituteUserId(item.getExecutedBySubstitute());\n        return result;\n    }\n\n    protected ProcessAPI getProcessAPI() {\n        try {\n            return TenantAPIAccessor.getProcessAPI(getEngineSession());\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    protected CONSOLE_ITEM convertEngineToConsoleItem(final ENGINE_ITEM item) {\n        return (CONSOLE_ITEM) FlowNodeConverter.convertEngineToConsoleItem(item);\n    }\n\n    public long count(final String search, final String orders, final Map<String, String> filters) {\n        return search(0, 0, search, orders, filters).getTotal();\n    }\n\n    @Override\n    public CONSOLE_ITEM get(final APIID id) {\n        try {\n            @SuppressWarnings(\"unchecked\")\n            final ENGINE_ITEM flowNodeInstance = (ENGINE_ITEM) getProcessAPI().getFlowNodeInstance(id.toLong());\n            return convertEngineToConsoleItem(flowNodeInstance);\n        } catch (final NotFoundException e) {\n            throw new APIItemNotFoundException(FlowNodeDefinition.TOKEN, id);\n        }\n    }\n\n    @Override\n    public ItemSearchResult<CONSOLE_ITEM> search(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n        final SearchOptionsBuilder builder = makeSearchOptionBuilder(page, resultsByPage, search, orders, filters);\n        final SearchResult<ENGINE_ITEM> results = runSearch(builder, filters);\n\n        return new ItemSearchResult<>(\n                page,\n                resultsByPage,\n                results.getCount(),\n                convertEngineToConsoleItemsList(results.getResult()));\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected SearchResult<ENGINE_ITEM> runSearch(final SearchOptionsBuilder builder,\n            final Map<String, String> filters) {\n        try {\n            return (SearchResult<ENGINE_ITEM>) getProcessAPI().searchFlowNodeInstances(builder.done());\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    protected SearchOptionsBuilder makeSearchOptionBuilder(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n        final SearchOptionsBuilder builder = SearchOptionsBuilderUtil.buildSearchOptions(page, resultsByPage, orders,\n                search);\n        addStringFilterToSearchBuilder(filters, builder, FlowNodeItem.ATTRIBUTE_CASE_ID,\n                FlowNodeInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID);\n        addStringFilterToSearchBuilder(filters, builder, FlowNodeItem.ATTRIBUTE_ROOT_CASE_ID,\n                FlowNodeInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID);\n        addStringFilterToSearchBuilder(filters, builder, FlowNodeItem.ATTRIBUTE_PARENT_CASE_ID,\n                FlowNodeInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID);\n        addStringFilterToSearchBuilder(filters, builder, FlowNodeItem.ATTRIBUTE_PARENT_ACTIVITY_INSTANCE_ID,\n                FlowNodeInstanceSearchDescriptor.PARENT_ACTIVITY_INSTANCE_ID);\n        addStringFilterToSearchBuilder(filters, builder, FlowNodeItem.ATTRIBUTE_PROCESS_ID,\n                FlowNodeInstanceSearchDescriptor.PROCESS_DEFINITION_ID);\n        addStringFilterToSearchBuilder(filters, builder, TaskItem.ATTRIBUTE_LAST_UPDATE_DATE,\n                FlowNodeInstanceSearchDescriptor.LAST_UPDATE_DATE);\n        addStringFilterToSearchBuilder(filters, builder, TaskItem.ATTRIBUTE_NAME,\n                FlowNodeInstanceSearchDescriptor.NAME);\n\n        if (filters.containsKey(FlowNodeItem.ATTRIBUTE_STATE)\n                && FlowNodeItem.VALUE_STATE_PENDING.equalsIgnoreCase(filters.get(FlowNodeItem.ATTRIBUTE_STATE))) {\n            builder.leftParenthesis().filter(FlowNodeInstanceSearchDescriptor.STATE_NAME, \"ready\")\n                    .or().filter(FlowNodeInstanceSearchDescriptor.STATE_NAME, \"waiting\")\n                    .rightParenthesis();\n        } else if (filters.containsKey(FlowNodeItem.ATTRIBUTE_STATE)\n                && FlowNodeItem.VALUE_STATE_ONGOING.equalsIgnoreCase(filters.get(FlowNodeItem.ATTRIBUTE_STATE))) {\n            builder.leftParenthesis().filter(FlowNodeInstanceSearchDescriptor.STATE_NAME, \"executing\")\n                    .or().filter(FlowNodeInstanceSearchDescriptor.STATE_NAME, \"completing\")\n                    .or().filter(FlowNodeInstanceSearchDescriptor.STATE_NAME, \"initializing\")\n                    .rightParenthesis();\n        } else {\n            addStringFilterToSearchBuilder(filters, builder, FlowNodeItem.ATTRIBUTE_STATE,\n                    FlowNodeInstanceSearchDescriptor.STATE_NAME);\n        }\n        builder.differentFrom(FlowNodeInstanceSearchDescriptor.STATE_NAME, \"aborted\");\n        builder.differentFrom(FlowNodeInstanceSearchDescriptor.STATE_NAME, \"cancelled\");\n        builder.differentFrom(FlowNodeInstanceSearchDescriptor.STATE_NAME, \"completed\");\n        return builder;\n    }\n\n    public AbstractFlowNodeDatastore<CONSOLE_ITEM, ENGINE_ITEM> setUpdateHelper(\n            final DatastoreHasUpdate<FlowNodeItem> updateHelper) {\n        this.updateHelper = updateHelper;\n        return this;\n    }\n\n    @Override\n    public CONSOLE_ITEM update(final APIID id, final Map<String, String> attributes) {\n        if (updateHelper != null) {\n            /*\n             * Generics are useless in this class.\n             * It only result in casting issues.\n             * It needs to be badly removed.\n             */\n            return (CONSOLE_ITEM) updateHelper.update(id, attributes);\n        }\n        throw new APIMethodNotAllowedException(\"PUT method not allowed\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/AbstractHumanTaskDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.flownode;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceNotFoundException;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.TaskPriority;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.flownode.HumanTaskDefinition;\nimport org.bonitasoft.web.rest.model.bpm.flownode.HumanTaskItem;\nimport org.bonitasoft.web.rest.server.datastore.converter.ActivityAttributeConverter;\nimport org.bonitasoft.web.rest.server.datastore.utils.Sort;\nimport org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException;\nimport org.bonitasoft.web.toolkit.client.common.util.MapUtil;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.ui.utils.DateFormat;\n\n/**\n * @author Séverin Moussel\n */\npublic class AbstractHumanTaskDatastore<CONSOLE_ITEM extends HumanTaskItem, ENGINE_ITEM extends HumanTaskInstance>\n        extends AbstractTaskDatastore<CONSOLE_ITEM, ENGINE_ITEM> {\n\n    public AbstractHumanTaskDatastore(final APISession engineSession) {\n        super(engineSession);\n    }\n\n    /**\n     * Fill a console item using the engine item passed.\n     *\n     * @param result\n     *        The console item to fill\n     * @param item\n     *        The engine item to use for filling\n     * @return This method returns the result parameter passed.\n     */\n    protected static HumanTaskItem fillConsoleItem(final HumanTaskItem result, final HumanTaskInstance item) {\n        TaskDatastore.fillConsoleItem(result, item);\n\n        result.setActorId(APIID.makeAPIID(item.getActorId()));\n        result.setAssignedId(APIID.makeAPIID(item.getAssigneeId()));\n        result.setAssignedDate(item.getClaimedDate());\n        result.setPriority(item.getPriority() != null ? item.getPriority().toString().toLowerCase() : null);\n        result.setDueDate(item.getExpectedEndDate());\n        return result;\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // C.R.U.D.S\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // SEARCH\n\n    @Override\n    protected SearchOptionsBuilder makeSearchOptionBuilder(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n        String convertedSort = null;\n        if (orders != null) {\n            convertedSort = new Sort(orders, new ActivityAttributeConverter()).toString();\n        }\n        final SearchOptionsBuilder builder = SearchOptionsBuilderUtil.buildSearchOptions(page, resultsByPage,\n                convertedSort, search);\n\n        addStringFilterToSearchBuilder(filters, builder, HumanTaskItem.ATTRIBUTE_CASE_ID,\n                HumanTaskInstanceSearchDescriptor.PROCESS_INSTANCE_ID);\n        addStringFilterToSearchBuilder(filters, builder, HumanTaskItem.ATTRIBUTE_ROOT_CASE_ID,\n                FlowNodeInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID);\n        addStringFilterToSearchBuilder(filters, builder, HumanTaskItem.ATTRIBUTE_PARENT_CASE_ID,\n                FlowNodeInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID);\n        if (!filters.containsKey(HumanTaskItem.FILTER_USER_ID)\n                && !filters.containsKey(HumanTaskItem.FILTER_TEAM_MANAGER_ID)) {\n            addStringFilterToSearchBuilder(filters, builder, HumanTaskItem.ATTRIBUTE_PROCESS_ID,\n                    HumanTaskInstanceSearchDescriptor.PROCESS_DEFINITION_ID);\n        }\n        addStringFilterToSearchBuilder(filters, builder, HumanTaskItem.ATTRIBUTE_STATE,\n                HumanTaskInstanceSearchDescriptor.STATE_NAME);\n        addStringFilterToSearchBuilder(filters, builder, HumanTaskItem.ATTRIBUTE_TYPE,\n                ActivityInstanceSearchDescriptor.ACTIVITY_TYPE);\n        addStringFilterToSearchBuilder(filters, builder, HumanTaskItem.ATTRIBUTE_ASSIGNED_USER_ID,\n                HumanTaskInstanceSearchDescriptor.ASSIGNEE_ID);\n        addStringFilterToSearchBuilder(filters, builder, HumanTaskItem.ATTRIBUTE_PRIORITY,\n                HumanTaskInstanceSearchDescriptor.PRIORITY);\n        addStringFilterToSearchBuilder(filters, builder, HumanTaskItem.ATTRIBUTE_NAME,\n                HumanTaskInstanceSearchDescriptor.NAME);\n        addStringFilterToSearchBuilder(filters, builder, HumanTaskItem.ATTRIBUTE_DISPLAY_NAME,\n                HumanTaskInstanceSearchDescriptor.DISPLAY_NAME);\n\n        return builder;\n    }\n\n    @Override\n    protected SearchResult<ENGINE_ITEM> runSearch(final SearchOptionsBuilder builder,\n            final Map<String, String> filters) {\n        // Using the same id for each test to avoid useless memory usage.\n        APIID id = null;\n\n        // Tasks of all users using a specific supervisor's processes.\n        id = APIID.makeAPIID(filters.get(HumanTaskItem.FILTER_SUPERVISOR_ID));\n        if (id != null && id.isValidLongID()) {\n            filters.remove(HumanTaskItem.FILTER_SUPERVISOR_ID);\n            return runSupervisorSearch(filters, builder, id.toLong());\n        }\n\n        // Tasks of all members of a specific team manager's team.\n        id = APIID.makeAPIID(filters.get(HumanTaskItem.FILTER_TEAM_MANAGER_ID));\n        if (id != null && id.isValidLongID()) {\n            return runTeamManagerSearch(filters, builder, id.toLong());\n        }\n\n        return runGenericSearch(builder, filters);\n    }\n\n    protected SearchResult<ENGINE_ITEM> runGenericSearch(final SearchOptionsBuilder builder,\n            final Map<String, String> filters) {\n        try {\n            // Using the same id for each test to avoid useless memory usage.\n            APIID id = null;\n\n            // Tasks claimed by a specific user\n            id = APIID.makeAPIID(filters.get(HumanTaskItem.ATTRIBUTE_ASSIGNED_USER_ID));\n            if (id != null && id.isValidLongID()) {\n                @SuppressWarnings(\"unchecked\")\n                final SearchResult<ENGINE_ITEM> searchHumanTaskInstances = (SearchResult<ENGINE_ITEM>) getProcessAPI()\n                        .searchPendingTasksAssignedToUser(id.toLong(), builder.done());\n                return searchHumanTaskInstances;\n            }\n\n            // Available tasks for a specific user (pending + assigned)\n            id = APIID.makeAPIID(filters.get(HumanTaskItem.FILTER_USER_ID));\n            if (id != null && id.isValidLongID()) {\n                // Show also assigned to other users\n                if (filters.containsKey(HumanTaskItem.FILTER_SHOW_ASSIGNED_TO_OTHERS)\n                        && Boolean.parseBoolean(filters.get(HumanTaskItem.FILTER_SHOW_ASSIGNED_TO_OTHERS))) {\n                    @SuppressWarnings(\"unchecked\")\n                    final SearchResult<ENGINE_ITEM> searchPendingTasks = (SearchResult<ENGINE_ITEM>) getProcessAPI()\n                            .searchPendingOrAssignedToUserOrAssignedToOthersTasks(\n                                    id.toLong(), builder.done());\n                    return searchPendingTasks;\n                } // ATTRIBUTE_ASSIGNED_USER_ID explicitly passed as NULL\n                else if (filters.containsKey(HumanTaskItem.ATTRIBUTE_ASSIGNED_USER_ID)\n                        && APIID.makeAPIID(filters.get(HumanTaskItem.ATTRIBUTE_ASSIGNED_USER_ID)) == null) {\n                    @SuppressWarnings(\"unchecked\")\n                    final SearchResult<ENGINE_ITEM> searchPendingTasksForUser = (SearchResult<ENGINE_ITEM>) getProcessAPI()\n                            .searchPendingTasksForUser(\n                                    id.toLong(), builder.done());\n                    return searchPendingTasksForUser;\n                } else if (filters.containsKey(HumanTaskItem.ATTRIBUTE_PROCESS_ID)) {\n                    @SuppressWarnings(\"unchecked\")\n                    final SearchResult<ENGINE_ITEM> searchMyAvailableHumanTasks = (SearchResult<ENGINE_ITEM>) getProcessAPI()\n                            .searchAssignedAndPendingHumanTasksFor(\n                                    APIID.makeAPIID(filters.get(HumanTaskItem.ATTRIBUTE_PROCESS_ID)).toLong(),\n                                    id.toLong(),\n                                    builder.done());\n                    return searchMyAvailableHumanTasks;\n                } else {\n                    @SuppressWarnings(\"unchecked\")\n                    final SearchResult<ENGINE_ITEM> searchMyAvailableHumanTasks = (SearchResult<ENGINE_ITEM>) getProcessAPI()\n                            .searchMyAvailableHumanTasks(\n                                    id.toLong(), builder.done());\n                    return searchMyAvailableHumanTasks;\n                }\n            }\n\n            if (HumanTaskItem.VALUE_STATE_READY.equals(filters.get(HumanTaskItem.ATTRIBUTE_STATE))) {\n                @SuppressWarnings(\"unchecked\")\n                SearchResult<ENGINE_ITEM> searchHumanTaskInstances = (SearchResult<ENGINE_ITEM>) getProcessAPI()\n                        .searchAssignedAndPendingHumanTasks(builder.done());\n                return searchHumanTaskInstances;\n            }\n\n            // Custom search\n            @SuppressWarnings(\"unchecked\")\n            final SearchResult<ENGINE_ITEM> searchHumanTaskInstances = (SearchResult<ENGINE_ITEM>) getProcessAPI()\n                    .searchHumanTaskInstances(builder.done());\n            return searchHumanTaskInstances;\n\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    private SearchResult<ENGINE_ITEM> runTeamManagerSearch(final Map<String, String> filters,\n            final SearchOptionsBuilder builder, final Long teamManagerId) {\n        try {\n            if (filters.containsKey(HumanTaskItem.FILTER_IS_ASSIGNED)) {\n                if (Boolean.TRUE.equals(MapUtil.getValueAsBoolean(filters, HumanTaskItem.FILTER_IS_ASSIGNED))) {\n                    @SuppressWarnings(\"unchecked\")\n                    final SearchResult<ENGINE_ITEM> searchResult = (SearchResult<ENGINE_ITEM>) getProcessAPI()\n                            .searchAssignedTasksManagedBy(teamManagerId,\n                                    builder.done());\n                    return searchResult;\n                } else {\n                    if (filters.containsKey(HumanTaskItem.ATTRIBUTE_PROCESS_ID)) {\n                        @SuppressWarnings(\"unchecked\")\n                        final SearchResult<ENGINE_ITEM> searchMyAvailableHumanTasks = (SearchResult<ENGINE_ITEM>) getProcessAPI()\n                                .searchAssignedAndPendingHumanTasks(\n                                        APIID.makeAPIID(filters.get(HumanTaskItem.ATTRIBUTE_PROCESS_ID)).toLong(),\n                                        builder.done());\n                        return searchMyAvailableHumanTasks;\n                    } else {\n                        @SuppressWarnings(\"unchecked\")\n                        final SearchResult<ENGINE_ITEM> searchResult = (SearchResult<ENGINE_ITEM>) getProcessAPI()\n                                .searchPendingTasksManagedBy(teamManagerId,\n                                        builder.done());\n                        return searchResult;\n                    }\n                }\n            } else {\n                // Custom search\n                @SuppressWarnings(\"unchecked\")\n                final SearchResult<ENGINE_ITEM> searchHumanTaskInstances = (SearchResult<ENGINE_ITEM>) getProcessAPI()\n                        .searchHumanTaskInstances(builder.done());\n                return searchHumanTaskInstances;\n            }\n\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    private SearchResult<ENGINE_ITEM> runSupervisorSearch(final Map<String, String> filters,\n            final SearchOptionsBuilder builder, final Long supervisorId) {\n        try {\n\n            final String taskType = filters.get(HumanTaskItem.ATTRIBUTE_STATE);\n            if (taskType == null) {\n                throw new APIException(\"Can't retrieve mixed state tasks for a defined supervisor\");\n            }\n\n            if (StringUtils.equalsIgnoreCase(taskType, HumanTaskItem.VALUE_STATE_READY)) {\n                @SuppressWarnings(\"unchecked\")\n                final SearchResult<ENGINE_ITEM> searchResult = (SearchResult<ENGINE_ITEM>) getProcessAPI()\n                        .searchPendingTasksSupervisedBy(supervisorId,\n                                builder.done());\n                return searchResult;\n            } else {\n                throw new APIException(\"Can't retrieve non pending human task for a Process Manager\");\n            }\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    // GET\n\n    @Override\n    public CONSOLE_ITEM get(final APIID id) {\n        try {\n            @SuppressWarnings(\"unchecked\")\n            final ENGINE_ITEM humanTaskInstance = (ENGINE_ITEM) getProcessAPI().getHumanTaskInstance(id.toLong());\n            return convertEngineToConsoleItem(humanTaskInstance);\n        } catch (final ActivityInstanceNotFoundException e) {\n            throw new APIItemNotFoundException(HumanTaskDefinition.TOKEN, id);\n        }\n    }\n\n    // UPDATE\n    @Override\n    public CONSOLE_ITEM update(final APIID id, final Map<String, String> attributes) {\n        try {\n            // Priority\n            if (attributes.containsKey(HumanTaskItem.ATTRIBUTE_PRIORITY)) {\n                getProcessAPI().setTaskPriority(\n                        id.toLong(),\n                        TaskPriority.valueOf(attributes.get(HumanTaskItem.ATTRIBUTE_PRIORITY).toUpperCase()));\n            }\n\n            // Due date\n            if (attributes.containsKey(HumanTaskItem.ATTRIBUTE_DUE_DATE)) {\n                getProcessAPI().updateDueDateOfTask(\n                        id.toLong(),\n                        DateFormat.sqlToDate(attributes.get(HumanTaskItem.ATTRIBUTE_DUE_DATE)));\n            }\n\n            // Assigned to\n            if (attributes.containsKey(HumanTaskItem.ATTRIBUTE_ASSIGNED_USER_ID)) {\n\n                final APIID userId = APIID.makeAPIID(attributes.get(HumanTaskItem.ATTRIBUTE_ASSIGNED_USER_ID));\n                if (userId == null || !userId.isValidLongID()) {\n                    getProcessAPI().releaseUserTask(id.toLong());\n                } else {\n                    getProcessAPI().assignUserTask(id.toLong(), userId.toLong());\n                }\n            }\n\n            return super.update(id, attributes);\n\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // COUNTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public Long getNumberOfOpenTasks(final APIID userId) {\n        return getProcessAPI().getNumberOfOpenTasks(Collections.singletonList(userId.toLong())).get(userId.toLong());\n    }\n\n    public Long getNumberOfOverdueOpenTasks(final APIID userId) {\n        return getProcessAPI().getNumberOfOverdueOpenTasks(Collections.singletonList(userId.toLong()))\n                .get(userId.toLong());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/AbstractTaskDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.flownode;\n\nimport org.bonitasoft.engine.bpm.flownode.TaskInstance;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.flownode.TaskItem;\n\n/**\n * @author Séverin Moussel\n */\npublic class AbstractTaskDatastore<CONSOLE_ITEM extends TaskItem, ENGINE_ITEM extends TaskInstance>\n        extends AbstractActivityDatastore<CONSOLE_ITEM, ENGINE_ITEM> {\n\n    public AbstractTaskDatastore(final APISession engineSession) {\n        super(engineSession);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/AbstractUserTaskDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.flownode;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceNotFoundException;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.UserTaskInstance;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.flownode.UserTaskDefinition;\nimport org.bonitasoft.web.rest.model.bpm.flownode.UserTaskItem;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Séverin Moussel\n */\npublic class AbstractUserTaskDatastore<CONSOLE_ITEM extends UserTaskItem, ENGINE_ITEM extends UserTaskInstance>\n        extends AbstractHumanTaskDatastore<CONSOLE_ITEM, ENGINE_ITEM> {\n\n    public AbstractUserTaskDatastore(final APISession engineSession) {\n        super(engineSession);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // C.R.U.D.S\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public CONSOLE_ITEM get(final APIID id) {\n        try {\n\n            // FIXME replace by getUserTaskInstance\n            final HumanTaskInstance humanTaskInstance = getProcessAPI().getHumanTaskInstance(id.toLong());\n            if (!(humanTaskInstance instanceof UserTaskInstance)) {\n                throw new APIItemNotFoundException(\"User task\", id);\n            }\n\n            return convertEngineToConsoleItem((ENGINE_ITEM) humanTaskInstance);\n        } catch (final ActivityInstanceNotFoundException e) {\n            throw new APIItemNotFoundException(UserTaskDefinition.TOKEN, id);\n        }\n    }\n\n    @Override\n    protected SearchOptionsBuilder makeSearchOptionBuilder(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n\n        final SearchOptionsBuilder builder = super.makeSearchOptionBuilder(page, resultsByPage, search, orders,\n                filters);\n\n        builder.filter(HumanTaskInstanceSearchDescriptor.PARENT_ACTIVITY_INSTANCE_ID, 0);\n\n        return builder;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/ActivityDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.flownode;\n\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ActivityItem;\n\n/**\n * @author Séverin Moussel\n */\npublic class ActivityDatastore extends AbstractActivityDatastore<ActivityItem, ActivityInstance> {\n\n    public ActivityDatastore(final APISession engineSession) {\n        super(engineSession);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/FlowNodeConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.flownode;\n\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedUserTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstance;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.TaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.UserTaskInstance;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ActivityItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedActivityItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedFlowNodeItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedHumanTaskItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedTaskItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedUserTaskItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.FlowNodeItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.HumanTaskItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.TaskItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.UserTaskItem;\nimport org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.ArchivedActivityDatastore;\nimport org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.ArchivedFlowNodeDatastore;\nimport org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.ArchivedHumanTaskDatastore;\nimport org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.ArchivedTaskDatastore;\nimport org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.ArchivedUserTaskDatastore;\n\n/**\n * @author Séverin Moussel\n */\npublic class FlowNodeConverter {\n\n    public static FlowNodeItem convertEngineToConsoleItem(final FlowNodeInstance item) {\n        return INSTANCE._convertEngineToConsoleItem(item);\n    }\n\n    public static ArchivedFlowNodeItem convertEngineToConsoleItem(final ArchivedFlowNodeInstance item) {\n        return INSTANCE._convertEngineToConsoleItem(item);\n    }\n\n    protected FlowNodeItem _convertEngineToConsoleItem(final FlowNodeInstance item) {\n        if (item instanceof UserTaskInstance) {\n            return UserTaskDatastore.fillConsoleItem(new UserTaskItem(), (UserTaskInstance) item);\n        } else if (item instanceof HumanTaskInstance) {\n            return HumanTaskDatastore.fillConsoleItem(new HumanTaskItem(), (HumanTaskInstance) item);\n        } else if (item instanceof TaskInstance) {\n            return TaskDatastore.fillConsoleItem(new TaskItem(), (TaskInstance) item);\n        } else if (item instanceof ActivityInstance) {\n            return ActivityDatastore.fillConsoleItem(new ActivityItem(), (ActivityInstance) item);\n        }\n\n        return FlowNodeDatastore.fillConsoleItem(new FlowNodeItem(), item);\n    }\n\n    protected ArchivedFlowNodeItem _convertEngineToConsoleItem(final ArchivedFlowNodeInstance item) {\n        if (item instanceof ArchivedUserTaskInstance) {\n            return ArchivedUserTaskDatastore.fillConsoleItem(new ArchivedUserTaskItem(),\n                    (ArchivedUserTaskInstance) item);\n        } else if (item instanceof ArchivedHumanTaskInstance) {\n            return ArchivedHumanTaskDatastore.fillConsoleItem(new ArchivedHumanTaskItem(),\n                    (ArchivedHumanTaskInstance) item);\n        } else if (item instanceof ArchivedTaskInstance) {\n            return ArchivedTaskDatastore.fillConsoleItem(new ArchivedTaskItem(), (ArchivedTaskInstance) item);\n        } else if (item instanceof ArchivedActivityInstance) {\n            return ArchivedActivityDatastore.fillConsoleItem(new ArchivedActivityItem(),\n                    (ArchivedActivityInstance) item);\n        }\n\n        return ArchivedFlowNodeDatastore.fillConsoleItem(new ArchivedFlowNodeItem(), item);\n    }\n\n    private static FlowNodeConverter INSTANCE;\n\n    public static void setFlowNodeConverter(final FlowNodeConverter converter) {\n        INSTANCE = converter;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/FlowNodeDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.flownode;\n\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstance;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.flownode.FlowNodeItem;\n\n/**\n * @author Séverin Moussel\n */\npublic class FlowNodeDatastore extends AbstractFlowNodeDatastore<FlowNodeItem, FlowNodeInstance> {\n\n    public FlowNodeDatastore(final APISession engineSession) {\n        super(engineSession);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/FlowNodeTypeConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.flownode;\n\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeType;\nimport org.bonitasoft.web.rest.model.bpm.flownode.IFlowNodeItem;\nimport org.bonitasoft.web.rest.server.datastore.converter.ValueConverter;\nimport org.bonitasoft.web.rest.server.framework.api.EnumConverter;\n\n/**\n * @author Vincent Elcrin\n */\npublic class FlowNodeTypeConverter implements EnumConverter<FlowNodeType>, ValueConverter<FlowNodeType> {\n\n    @Override\n    public FlowNodeType convert(final String attributeValue) {\n        if (IFlowNodeItem.VALUE_TYPE_AUTOMATIC_TASK.equals(attributeValue)) {\n            return FlowNodeType.AUTOMATIC_TASK;\n        } else if (IFlowNodeItem.VALUE_TYPE_BOUNDARY_EVENT.equals(attributeValue)) {\n            return FlowNodeType.BOUNDARY_EVENT;\n        } else if (IFlowNodeItem.VALUE_TYPE_CALL_ACTIVITY.equals(attributeValue)) {\n            return FlowNodeType.CALL_ACTIVITY;\n        } else if (IFlowNodeItem.VALUE_TYPE_END_EVENT.equals(attributeValue)) {\n            return FlowNodeType.END_EVENT;\n        } else if (IFlowNodeItem.VALUE_TYPE_GATEWAY.equals(attributeValue)) {\n            return FlowNodeType.GATEWAY;\n        } else if (IFlowNodeItem.VALUE_TYPE_INTERMEDIATE_CATCH_EVENT.equals(attributeValue)) {\n            return FlowNodeType.INTERMEDIATE_CATCH_EVENT;\n        } else if (IFlowNodeItem.VALUE_TYPE_INTERMEDIATE_THROW_EVENT.equals(attributeValue)) {\n            return FlowNodeType.INTERMEDIATE_THROW_EVENT;\n        } else if (IFlowNodeItem.VALUE_TYPE_LOOP_ACTIVITY.equals(attributeValue)) {\n            return FlowNodeType.LOOP_ACTIVITY;\n        } else if (IFlowNodeItem.VALUE_TYPE_MANUAL_TASK.equals(attributeValue)) {\n            return FlowNodeType.MANUAL_TASK;\n        } else if (IFlowNodeItem.VALUE_TYPE_START_EVENT.equals(attributeValue)) {\n            return FlowNodeType.START_EVENT;\n        } else if (IFlowNodeItem.VALUE_TYPE_USER_TASK.equals(attributeValue)) {\n            return FlowNodeType.USER_TASK;\n        } else if (IFlowNodeItem.VALUE_TYPE_SUB_PROCESS_ACTIVITY.equals(attributeValue)) {\n            return FlowNodeType.SUB_PROCESS;\n        } /*\n           * else if (IFlowNodeItem.VALUE_TYPE_MULTI_INSTANCE_ACTIVITY.equals(attributeValue)) {\n           * return FlowNodeType.MULTI_INSTANCE;\n           * }\n           */else {\n            throw new RuntimeException(\"Can't convert <\" + attributeValue + \">. Unknown flow node type.\");\n        }\n    }\n\n    @Override\n    public String convert(final FlowNodeType enumValue) {\n        switch (enumValue) {\n            case AUTOMATIC_TASK:\n                return IFlowNodeItem.VALUE_TYPE_AUTOMATIC_TASK;\n            case BOUNDARY_EVENT:\n                return IFlowNodeItem.VALUE_TYPE_BOUNDARY_EVENT;\n            case CALL_ACTIVITY:\n                return IFlowNodeItem.VALUE_TYPE_CALL_ACTIVITY;\n            case END_EVENT:\n                return IFlowNodeItem.VALUE_TYPE_END_EVENT;\n            case GATEWAY:\n                return IFlowNodeItem.VALUE_TYPE_GATEWAY;\n            case INTERMEDIATE_CATCH_EVENT:\n                return IFlowNodeItem.VALUE_TYPE_INTERMEDIATE_CATCH_EVENT;\n            case INTERMEDIATE_THROW_EVENT:\n                return IFlowNodeItem.VALUE_TYPE_INTERMEDIATE_THROW_EVENT;\n            case LOOP_ACTIVITY:\n                return IFlowNodeItem.VALUE_TYPE_LOOP_ACTIVITY;\n            case MANUAL_TASK:\n                return IFlowNodeItem.VALUE_TYPE_MANUAL_TASK;\n            case START_EVENT:\n                return IFlowNodeItem.VALUE_TYPE_START_EVENT;\n            case USER_TASK:\n                return IFlowNodeItem.VALUE_TYPE_USER_TASK;\n            case SUB_PROCESS:\n                return IFlowNodeItem.VALUE_TYPE_SUB_PROCESS_ACTIVITY;\n            /*\n             * case MULTI_INSTANCE:\n             * return IFlowNodeItem.VALUE_TYPE_MULTI_INSTANCE_ACTIVITY;\n             */\n            default:\n                throw new RuntimeException(\"Can't convert <\" + enumValue + \">. Flow node type not supported.\");\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/HumanTaskDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.flownode;\n\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.flownode.HumanTaskItem;\n\n/**\n * @author Séverin Moussel\n */\npublic class HumanTaskDatastore extends AbstractHumanTaskDatastore<HumanTaskItem, HumanTaskInstance> {\n\n    public HumanTaskDatastore(final APISession engineSession) {\n        super(engineSession);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/TaskDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.flownode;\n\nimport org.bonitasoft.engine.bpm.flownode.TaskInstance;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.flownode.TaskItem;\n\n/**\n * @author Séverin Moussel\n */\npublic class TaskDatastore extends AbstractTaskDatastore<TaskItem, TaskInstance> {\n\n    public TaskDatastore(final APISession engineSession) {\n        super(engineSession);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/TaskFinder.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.flownode;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedActivityItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedTaskDefinition;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedTaskItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.TaskItem;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\n\n/**\n * @author Vincent Elcrin\n */\npublic class TaskFinder {\n\n    private final DatastoreHasGet<TaskItem> journal;\n\n    private final DatastoreHasSearch<ArchivedTaskItem> archives;\n\n    public TaskFinder(final DatastoreHasGet<TaskItem> journal, final DatastoreHasSearch<ArchivedTaskItem> archives) {\n        this.journal = journal;\n        this.archives = archives;\n    }\n\n    public IItem find(final APIID taskId) {\n        try {\n            return journal.get(taskId);\n        } catch (final APIItemNotFoundException e) {\n            final Map<String, String> filters = new HashMap<>();\n            filters.put(ArchivedActivityItem.ATTRIBUTE_SOURCE_OBJECT_ID, taskId.toString());\n            final ItemSearchResult<ArchivedTaskItem> result = archives.search(0, 1, null,\n                    ArchivedActivityItem.ATTRIBUTE_ARCHIVED_DATE + \" \"\n                            + Order.DESC,\n                    filters);\n            if (result.getResults().isEmpty()) {\n                throw new APIItemNotFoundException(ArchivedTaskDefinition.TOKEN, taskId);\n            }\n            return result.getResults().get(0);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/UserTaskDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.flownode;\n\nimport org.bonitasoft.engine.bpm.flownode.UserTaskInstance;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.flownode.UserTaskItem;\n\n/**\n * @author Séverin Moussel\n */\npublic class UserTaskDatastore extends AbstractUserTaskDatastore<UserTaskItem, UserTaskInstance> {\n\n    public UserTaskDatastore(final APISession engineSession) {\n        super(engineSession);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/archive/AbstractArchivedActivityDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedActivityItem;\nimport org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.converter.ArchivedActivitySearchDescriptorConverter;\nimport org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Séverin Moussel\n */\npublic abstract class AbstractArchivedActivityDatastore<CONSOLE_ITEM extends ArchivedActivityItem, ENGINE_ITEM extends ArchivedActivityInstance>\n        extends AbstractArchivedFlowNodeDatastore<CONSOLE_ITEM, ENGINE_ITEM> {\n\n    public AbstractArchivedActivityDatastore(final APISession engineSession, String token) {\n        super(engineSession, token);\n    }\n\n    /**\n     * Fill a console item using the engine item passed.\n     *\n     * @param result\n     *        The console item to fill\n     * @param item\n     *        The engine item to use for filling\n     * @return This method returns the result parameter passed.\n     */\n    public static ArchivedActivityItem fillConsoleItem(final ArchivedActivityItem result,\n            final ArchivedActivityInstance item) {\n        ArchivedFlowNodeDatastore.fillConsoleItem(result, item);\n\n        result.setReachStateDate(item.getReachedStateDate());\n        result.setLastUpdateDate(item.getLastUpdateDate());\n\n        return result;\n    }\n\n    @Override\n    protected ArchivedActivitySearchDescriptorConverter getSearchDescriptorConverter() {\n        return new ArchivedActivitySearchDescriptorConverter();\n    }\n\n    @Override\n    protected SearchResult<ENGINE_ITEM> runSearch(final SearchOptionsCreator creator,\n            final Map<String, String> filters) {\n        try {\n            @SuppressWarnings(\"unchecked\")\n            final SearchResult<ENGINE_ITEM> result = (SearchResult<ENGINE_ITEM>) getProcessAPI()\n                    .searchArchivedActivities(\n                            creator.create());\n\n            return result;\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @Override\n    protected ENGINE_ITEM runGet(final APIID id) {\n        try {\n            return super.runGet(id);\n        } catch (ClassCastException e) {\n            throw new APIItemNotFoundException(this.token, id);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/archive/AbstractArchivedFlowNodeDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstanceNotFoundException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedFlowNodeItem;\nimport org.bonitasoft.web.rest.server.datastore.CommonDatastore;\nimport org.bonitasoft.web.rest.server.datastore.bpm.flownode.FlowNodeConverter;\nimport org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.converter.ArchivedFlowNodeSearchDescriptorConverter;\nimport org.bonitasoft.web.rest.server.datastore.filter.Filters;\nimport org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator;\nimport org.bonitasoft.web.rest.server.datastore.utils.Sorts;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Séverin Moussel\n */\npublic abstract class AbstractArchivedFlowNodeDatastore<CONSOLE_ITEM extends ArchivedFlowNodeItem, ENGINE_ITEM extends ArchivedFlowNodeInstance>\n        extends CommonDatastore<CONSOLE_ITEM, ENGINE_ITEM> implements\n        DatastoreHasGet<CONSOLE_ITEM>,\n        DatastoreHasSearch<CONSOLE_ITEM> {\n\n    protected final String token;\n\n    public AbstractArchivedFlowNodeDatastore(final APISession engineSession, String token) {\n        super(engineSession);\n        this.token = token;\n    }\n\n    /**\n     * Fill a console item using the engine item passed.\n     *\n     * @param result\n     *        The console item to fill\n     * @param item\n     *        The engine item to use for filling\n     * @return This method returns the result parameter passed.\n     */\n    public static ArchivedFlowNodeItem fillConsoleItem(final ArchivedFlowNodeItem result,\n            final ArchivedFlowNodeInstance item) {\n        result.setId(item.getId());\n        result.setName(item.getName());\n        result.setDisplayName(item.getDisplayName());\n        result.setDescription(item.getDescription());\n        result.setDisplayDescription(item.getDisplayDescription());\n        result.setExecutedByUserId(item.getExecutedBy());\n        result.setRootCaseId(item.getRootContainerId());\n        result.setParentCaseId(item.getProcessInstanceId());\n        result.setProcessId(item.getProcessDefinitionId());\n        result.setState(item.getState());\n        result.setType(item.getType().name());\n        result.setArchivedDate(item.getArchiveDate());\n        result.setSourceObjectId(item.getSourceObjectId());\n        result.setRootContainerId(item.getRootContainerId());\n        result.setExecutedBySubstituteUserId(item.getExecutedBySubstitute());\n        return result;\n    }\n\n    protected ProcessAPI getProcessAPI() {\n        try {\n            return TenantAPIAccessor.getProcessAPI(getEngineSession());\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @Override\n    protected CONSOLE_ITEM convertEngineToConsoleItem(final ENGINE_ITEM item) {\n\n        @SuppressWarnings(\"unchecked\")\n        final CONSOLE_ITEM result = (CONSOLE_ITEM) FlowNodeConverter.convertEngineToConsoleItem(item);\n\n        return result;\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // C.R.U.D.S\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // GET\n\n    @Override\n    public CONSOLE_ITEM get(final APIID id) {\n        final ENGINE_ITEM archivedFlowNodeInstance = runGet(id);\n        return convertEngineToConsoleItem(archivedFlowNodeInstance);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected ENGINE_ITEM runGet(final APIID id) {\n        try {\n            return (ENGINE_ITEM) getProcessAPI().getArchivedFlowNodeInstance(id.toLong());\n        } catch (ArchivedFlowNodeInstanceNotFoundException e) {\n            throw new APIItemNotFoundException(this.token, id);\n        }\n    }\n\n    @Override\n    public ItemSearchResult<CONSOLE_ITEM> search(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n        final SearchOptionsCreator creator = makeSearchOptionCreator(page, resultsByPage, search, orders, filters);\n\n        final SearchResult<ENGINE_ITEM> results = runSearch(creator, filters);\n\n        return new ItemSearchResult<>(\n                page,\n                resultsByPage,\n                results.getCount(),\n                convertEngineToConsoleItemsList(results.getResult()));\n    }\n\n    /**\n     * Run the engine API search method\n     */\n    protected SearchResult<ENGINE_ITEM> runSearch(final SearchOptionsCreator creator,\n            final Map<String, String> filters) {\n        try {\n            @SuppressWarnings(\"unchecked\")\n            final SearchResult<ENGINE_ITEM> result = (SearchResult<ENGINE_ITEM>) getProcessAPI()\n                    .searchArchivedFlowNodeInstances(\n                            creator.create());\n\n            return result;\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    protected SearchOptionsCreator makeSearchOptionCreator(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n        return new SearchOptionsCreator(page,\n                resultsByPage,\n                search,\n                new Sorts(orders, getSearchDescriptorConverter()),\n                new Filters(filters, new ArchivedFlowNodeFilterCreator(getSearchDescriptorConverter())));\n    }\n\n    protected ArchivedFlowNodeSearchDescriptorConverter getSearchDescriptorConverter() {\n        return new ArchivedFlowNodeSearchDescriptorConverter();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/archive/AbstractArchivedHumanTaskDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive;\n\nimport java.util.Map;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstance;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedHumanTaskItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedTaskItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.HumanTaskItem;\nimport org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.converter.ArchivedActivitySearchDescriptorConverter;\nimport org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.converter.ArchivedHumanTaskSearchDescriptorConverter;\nimport org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.util.MapUtil;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Séverin Moussel\n */\npublic abstract class AbstractArchivedHumanTaskDatastore<CONSOLE_ITEM extends ArchivedHumanTaskItem, ENGINE_ITEM extends ArchivedHumanTaskInstance>\n        extends AbstractArchivedTaskDatastore<CONSOLE_ITEM, ENGINE_ITEM> {\n\n    public AbstractArchivedHumanTaskDatastore(final APISession engineSession, String token) {\n        super(engineSession, token);\n    }\n\n    /**\n     * Fill a console item using the engine item passed.\n     *\n     * @param result The console item to fill\n     * @param item The engine item to use for filling\n     * @return This method returns the result parameter passed.\n     */\n    public static ArchivedHumanTaskItem fillConsoleItem(final ArchivedHumanTaskItem result,\n            final ArchivedHumanTaskInstance item) {\n        ArchivedTaskDatastore.fillConsoleItem(result, item);\n\n        result.setActorId(APIID.makeAPIID(item.getActorId()));\n        result.setAssignedId(APIID.makeAPIID(item.getAssigneeId()));\n        result.setAssignedDate(item.getClaimedDate());\n        result.setPriority(item.getPriority() != null ? item.getPriority().toString().toLowerCase() : null);\n        result.setDueDate(item.getExpectedEndDate());\n\n        return result;\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // C.R.U.D.S\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public ItemSearchResult<CONSOLE_ITEM> search(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n        // can't use the ArchivedFlowNodeSearchDescriptorConverter to map web filter to engine ones since\n        // the supervisor id filter isn't handle in engine but is a specific method\n        String supervisorIdString = filters.remove(HumanTaskItem.FILTER_SUPERVISOR_ID);\n        final SearchOptionsCreator creator = makeSearchOptionCreator(page, resultsByPage, search, orders, filters);\n        if (StringUtils.isNotBlank(supervisorIdString)) {\n            filters.put(HumanTaskItem.FILTER_SUPERVISOR_ID, supervisorIdString);\n        }\n\n        final SearchResult<ENGINE_ITEM> results = runSearch(creator, filters);\n\n        return new ItemSearchResult<>(\n                page,\n                resultsByPage,\n                results.getCount(),\n                convertEngineToConsoleItemsList(results.getResult()));\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    protected SearchResult<ENGINE_ITEM> runSearch(final SearchOptionsCreator creator,\n            final Map<String, String> filters) {\n        try {\n            final SearchResult<ENGINE_ITEM> result;\n            if (!MapUtil.isBlank(filters, ArchivedHumanTaskItem.FILTER_SUPERVISOR_ID)) {\n                result = (SearchResult<ENGINE_ITEM>) getProcessAPI().searchArchivedHumanTasksSupervisedBy(\n                        MapUtil.getValueAsLong(filters, ArchivedTaskItem.FILTER_SUPERVISOR_ID),\n                        creator.create());\n            } else if (!MapUtil.isBlank(filters, ArchivedHumanTaskItem.FILTER_TEAM_MANAGER_ID)) {\n                result = (SearchResult<ENGINE_ITEM>) getProcessAPI().searchArchivedHumanTasksManagedBy(\n                        MapUtil.getValueAsLong(filters, ArchivedHumanTaskItem.FILTER_TEAM_MANAGER_ID),\n                        creator.create());\n            } else {\n                result = (SearchResult<ENGINE_ITEM>) getProcessAPI().searchArchivedHumanTasks(\n                        creator.create());\n            }\n\n            return result;\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @Override\n    protected ArchivedActivitySearchDescriptorConverter getSearchDescriptorConverter() {\n        return new ArchivedHumanTaskSearchDescriptorConverter();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/archive/AbstractArchivedTaskDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive;\n\nimport org.bonitasoft.engine.bpm.flownode.ArchivedTaskInstance;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedTaskItem;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Séverin Moussel\n */\npublic abstract class AbstractArchivedTaskDatastore<CONSOLE_ITEM extends ArchivedTaskItem, ENGINE_ITEM extends ArchivedTaskInstance>\n        extends AbstractArchivedActivityDatastore<CONSOLE_ITEM, ENGINE_ITEM> {\n\n    public AbstractArchivedTaskDatastore(final APISession engineSession, String token) {\n        super(engineSession, token);\n    }\n\n    @Override\n    protected ENGINE_ITEM runGet(final APIID id) {\n        try {\n            return super.runGet(id);\n        } catch (ClassCastException e) {\n            throw new APIItemNotFoundException(this.token, id);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/archive/AbstractArchivedUserTaskDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.flownode.ArchivedUserTaskInstance;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedUserTaskItem;\nimport org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Séverin Moussel\n */\npublic abstract class AbstractArchivedUserTaskDatastore<CONSOLE_ITEM extends ArchivedUserTaskItem, ENGINE_ITEM extends ArchivedUserTaskInstance>\n        extends AbstractArchivedHumanTaskDatastore<CONSOLE_ITEM, ENGINE_ITEM> {\n\n    public AbstractArchivedUserTaskDatastore(final APISession engineSession, String token) {\n        super(engineSession, token);\n    }\n\n    @Override\n    protected SearchResult<ENGINE_ITEM> runSearch(final SearchOptionsCreator creator,\n            final Map<String, String> filters) {\n        if (!filters.containsKey(ArchivedUserTaskItem.ATTRIBUTE_TYPE)) {\n            filters.put(ArchivedUserTaskItem.ATTRIBUTE_TYPE, ArchivedUserTaskItem.VALUE_TYPE_USER_TASK);\n        }\n\n        return super.runSearch(creator, filters);\n    }\n\n    @Override\n    protected ENGINE_ITEM runGet(final APIID id) {\n        try {\n            return super.runGet(id);\n        } catch (ClassCastException e) {\n            throw new APIItemNotFoundException(this.token, id);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/archive/ArchivedActivityDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive;\n\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedActivityItem;\n\n/**\n * @author Séverin Moussel\n */\npublic class ArchivedActivityDatastore\n        extends AbstractArchivedActivityDatastore<ArchivedActivityItem, ArchivedActivityInstance> {\n\n    public ArchivedActivityDatastore(final APISession engineSession, String token) {\n        super(engineSession, token);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/archive/ArchivedFlowNodeDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive;\n\nimport org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedFlowNodeItem;\n\n/**\n * @author Séverin Moussel\n */\npublic class ArchivedFlowNodeDatastore\n        extends AbstractArchivedFlowNodeDatastore<ArchivedFlowNodeItem, ArchivedFlowNodeInstance> {\n\n    public ArchivedFlowNodeDatastore(final APISession engineSession, String token) {\n        super(engineSession, token);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/archive/ArchivedFlowNodeFilterCreator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive;\n\nimport java.io.Serializable;\n\nimport org.bonitasoft.web.rest.model.bpm.flownode.IFlowNodeItem;\nimport org.bonitasoft.web.rest.server.datastore.bpm.flownode.FlowNodeTypeConverter;\nimport org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.converter.ArchivedFlowNodeSearchDescriptorConverter;\nimport org.bonitasoft.web.rest.server.datastore.filter.Field;\nimport org.bonitasoft.web.rest.server.datastore.filter.Filter;\nimport org.bonitasoft.web.rest.server.datastore.filter.GenericFilterCreator;\nimport org.bonitasoft.web.rest.server.datastore.filter.Value;\n\nclass ArchivedFlowNodeFilterCreator extends GenericFilterCreator {\n\n    ArchivedFlowNodeFilterCreator(ArchivedFlowNodeSearchDescriptorConverter converter) {\n        super(converter);\n    }\n\n    @Override\n    public Filter<? extends Serializable> create(String attribute, String value) {\n        if (IFlowNodeItem.ATTRIBUTE_TYPE.equals(attribute)) {\n            return new Filter<>(new Field(attribute, fieldConverter), new Value<>(value, new FlowNodeTypeConverter()));\n        }\n        return super.create(attribute, value);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/archive/ArchivedHumanTaskDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive;\n\nimport org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstance;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedHumanTaskItem;\n\n/**\n * @author Séverin Moussel\n */\npublic class ArchivedHumanTaskDatastore\n        extends AbstractArchivedHumanTaskDatastore<ArchivedHumanTaskItem, ArchivedHumanTaskInstance> {\n\n    public ArchivedHumanTaskDatastore(final APISession engineSession, String token) {\n        super(engineSession, token);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/archive/ArchivedTaskDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive;\n\nimport org.bonitasoft.engine.bpm.flownode.ArchivedTaskInstance;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedTaskItem;\n\n/**\n * @author Séverin Moussel\n */\npublic class ArchivedTaskDatastore extends AbstractArchivedTaskDatastore<ArchivedTaskItem, ArchivedTaskInstance> {\n\n    public ArchivedTaskDatastore(final APISession engineSession, String token) {\n        super(engineSession, token);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/archive/ArchivedUserTaskDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive;\n\nimport org.bonitasoft.engine.bpm.flownode.ArchivedUserTaskInstance;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedUserTaskItem;\n\n/**\n * @author Séverin Moussel\n */\npublic class ArchivedUserTaskDatastore\n        extends AbstractArchivedUserTaskDatastore<ArchivedUserTaskItem, ArchivedUserTaskInstance> {\n\n    public ArchivedUserTaskDatastore(final APISession engineSession, String token) {\n        super(engineSession, token);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/archive/converter/ArchivedActivitySearchDescriptorConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.converter;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstanceSearchDescriptor;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedActivityItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedFlowNodeItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedHumanTaskItem;\n\n/**\n * @author Colin PUY, Vincent Elcrin\n */\npublic class ArchivedActivitySearchDescriptorConverter extends ArchivedFlowNodeSearchDescriptorConverter {\n\n    private static final Map<String, String> additionalAttributes = new HashMap<>();\n\n    static {\n        additionalAttributes.put(ArchivedActivityItem.ATTRIBUTE_REACHED_STATE_DATE,\n                ArchivedActivityInstanceSearchDescriptor.REACHED_STATE_DATE);\n        additionalAttributes.put(ArchivedFlowNodeItem.ATTRIBUTE_CASE_ID,\n                ArchivedActivityInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID);\n        additionalAttributes.put(ArchivedFlowNodeItem.ATTRIBUTE_ROOT_CASE_ID,\n                ArchivedActivityInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID);\n        additionalAttributes.put(ArchivedFlowNodeItem.ATTRIBUTE_PARENT_CASE_ID,\n                ArchivedActivityInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID);\n        additionalAttributes.put(ArchivedFlowNodeItem.ATTRIBUTE_PROCESS_ID,\n                ArchivedActivityInstanceSearchDescriptor.PROCESS_DEFINITION_ID);\n        additionalAttributes.put(ArchivedFlowNodeItem.ATTRIBUTE_STATE,\n                ArchivedActivityInstanceSearchDescriptor.STATE_NAME);\n        additionalAttributes.put(ArchivedHumanTaskItem.ATTRIBUTE_ASSIGNED_USER_ID,\n                ArchivedActivityInstanceSearchDescriptor.ASSIGNEE_ID);\n        additionalAttributes.put(ArchivedHumanTaskItem.ATTRIBUTE_PRIORITY,\n                ArchivedActivityInstanceSearchDescriptor.PRIORITY);\n        additionalAttributes.put(ArchivedHumanTaskItem.ATTRIBUTE_TYPE,\n                ArchivedActivityInstanceSearchDescriptor.ACTIVITY_TYPE);\n        additionalAttributes.put(ArchivedActivityItem.ATTRIBUTE_SOURCE_OBJECT_ID,\n                ArchivedActivityInstanceSearchDescriptor.SOURCE_OBJECT_ID);\n        additionalAttributes.put(ArchivedActivityItem.ATTRIBUTE_ARCHIVED_DATE,\n                ArchivedActivityInstanceSearchDescriptor.ARCHIVE_DATE);\n    }\n\n    public ArchivedActivitySearchDescriptorConverter() {\n        extendsMapping(additionalAttributes);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/archive/converter/ArchivedFlowNodeSearchDescriptorConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.converter;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstanceSearchDescriptor;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedFlowNodeItem;\nimport org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter;\nimport org.bonitasoft.web.toolkit.client.common.util.MapUtil;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute.TYPE;\n\n/**\n * @author Vincent Elcrin\n */\npublic class ArchivedFlowNodeSearchDescriptorConverter implements AttributeConverter {\n\n    protected static final Map<String, String> mapping = new HashMap<>();\n    private static final Map<String, TYPE> valueTypeMapping = new HashMap<>();\n\n    private static void addAttributeConverterItem(String webSearchKey, String engineSearchKey, TYPE attributeType) {\n        mapping.put(webSearchKey, engineSearchKey);\n        valueTypeMapping.put(webSearchKey, attributeType);\n    }\n\n    private static void addAttributeConverterItem(String webSearchKey, String engineSearchKey) {\n        addAttributeConverterItem(webSearchKey, engineSearchKey, TYPE.STRING);\n    }\n\n    @Override\n    public Map<String, TYPE> getValueTypeMapping() {\n        return valueTypeMapping;\n    }\n\n    public ArchivedFlowNodeSearchDescriptorConverter() {\n        createMapping();\n    }\n\n    private void createMapping() {\n        addAttributeConverterItem(ArchivedFlowNodeItem.ATTRIBUTE_CASE_ID,\n                ArchivedFlowNodeInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID);\n        addAttributeConverterItem(ArchivedFlowNodeItem.ATTRIBUTE_NAME, ArchivedFlowNodeInstanceSearchDescriptor.NAME);\n        addAttributeConverterItem(ArchivedFlowNodeItem.ATTRIBUTE_DISPLAY_NAME,\n                ArchivedFlowNodeInstanceSearchDescriptor.DISPLAY_NAME);\n        addAttributeConverterItem(ArchivedFlowNodeItem.ATTRIBUTE_PROCESS_ID,\n                ArchivedFlowNodeInstanceSearchDescriptor.PROCESS_DEFINITION_ID);\n        addAttributeConverterItem(ArchivedFlowNodeItem.ATTRIBUTE_STATE,\n                ArchivedFlowNodeInstanceSearchDescriptor.STATE_NAME);\n        addAttributeConverterItem(ArchivedFlowNodeItem.ATTRIBUTE_TYPE,\n                ArchivedFlowNodeInstanceSearchDescriptor.FLOW_NODE_TYPE);\n        addAttributeConverterItem(ArchivedFlowNodeItem.FILTER_IS_TERMINAL,\n                ArchivedFlowNodeInstanceSearchDescriptor.TERMINAL, TYPE.BOOLEAN);\n        addAttributeConverterItem(ArchivedFlowNodeItem.ATTRIBUTE_ARCHIVED_DATE,\n                ArchivedFlowNodeInstanceSearchDescriptor.ARCHIVE_DATE);\n        addAttributeConverterItem(ArchivedFlowNodeItem.ATTRIBUTE_SOURCE_OBJECT_ID,\n                ArchivedFlowNodeInstanceSearchDescriptor.ORIGINAL_FLOW_NODE_ID);\n    }\n\n    @Override\n    public String convert(final String attribute) {\n        return MapUtil.getMandatory(mapping, attribute);\n    }\n\n    protected final void extendsMapping(final Map<String, String> extension) {\n        mapping.putAll(extension);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/archive/converter/ArchivedHumanTaskSearchDescriptorConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.converter;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstanceSearchDescriptor;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedActivityItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedFlowNodeItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedHumanTaskItem;\n\n/**\n * @author Vincent Elcrin\n */\npublic class ArchivedHumanTaskSearchDescriptorConverter extends ArchivedActivitySearchDescriptorConverter {\n\n    private static final Map<String, String> mapping = new HashMap<>();\n\n    static {\n        mapping.put(ArchivedFlowNodeItem.ATTRIBUTE_CASE_ID,\n                ArchivedActivityInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID);\n        mapping.put(ArchivedFlowNodeItem.ATTRIBUTE_ROOT_CASE_ID,\n                ArchivedActivityInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID);\n        mapping.put(ArchivedFlowNodeItem.ATTRIBUTE_PARENT_CASE_ID,\n                ArchivedActivityInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID);\n        mapping.put(ArchivedFlowNodeItem.ATTRIBUTE_PROCESS_ID,\n                ArchivedHumanTaskInstanceSearchDescriptor.PROCESS_DEFINITION_ID);\n        mapping.put(ArchivedFlowNodeItem.ATTRIBUTE_STATE, ArchivedHumanTaskInstanceSearchDescriptor.STATE_NAME);\n        mapping.put(ArchivedActivityItem.ATTRIBUTE_REACHED_STATE_DATE,\n                ArchivedHumanTaskInstanceSearchDescriptor.REACHED_STATE_DATE);\n        // FIXME Add this filter in the engine\n        // mapping.put(ArchivedFlowNodeItem.ATTRIBUTE_TYPE, ArchivedHumanTaskInstanceSearchDescriptor.FLOW_NODE_TYPE);\n        mapping.put(ArchivedHumanTaskItem.ATTRIBUTE_ASSIGNED_USER_ID,\n                ArchivedHumanTaskInstanceSearchDescriptor.ASSIGNEE_ID);\n        mapping.put(ArchivedHumanTaskItem.ATTRIBUTE_PRIORITY, ArchivedHumanTaskInstanceSearchDescriptor.PRIORITY);\n        mapping.put(ArchivedHumanTaskItem.ATTRIBUTE_ARCHIVED_DATE,\n                ArchivedHumanTaskInstanceSearchDescriptor.ARCHIVE_DATE);\n        mapping.put(ArchivedHumanTaskItem.ATTRIBUTE_SOURCE_OBJECT_ID,\n                ArchivedHumanTaskInstanceSearchDescriptor.ORIGINAL_HUMAN_TASK_ID);\n        mapping.put(ArchivedHumanTaskItem.FILTER_USER_ID, \"\");\n\n    }\n\n    public ArchivedHumanTaskSearchDescriptorConverter() {\n        extendsMapping(mapping);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/process/ActorDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.process;\n\nimport static org.bonitasoft.web.rest.model.bpm.process.ActorItem.ATTRIBUTE_DESCRIPTION;\nimport static org.bonitasoft.web.toolkit.client.data.item.template.ItemHasDualName.ATTRIBUTE_DISPLAY_NAME;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.actor.ActorCriterion;\nimport org.bonitasoft.engine.bpm.actor.ActorInstance;\nimport org.bonitasoft.engine.bpm.actor.ActorUpdater;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.bonitasoft.web.rest.model.bpm.process.ActorItem;\nimport org.bonitasoft.web.rest.server.datastore.CommonDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasUpdate;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.util.MapUtil;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Vincent Elcrin\n * @author Séverin Moussel\n */\npublic class ActorDatastore extends CommonDatastore<ActorItem, ActorInstance> implements\n        DatastoreHasGet<ActorItem>,\n        DatastoreHasSearch<ActorItem>,\n        DatastoreHasUpdate<ActorItem> {\n\n    public ActorDatastore(final APISession engineSession) {\n        super(engineSession);\n    }\n\n    @Override\n    protected ActorItem convertEngineToConsoleItem(final ActorInstance engineItem) {\n        final ActorItem result = new ActorItem();\n\n        result.setId(engineItem.getId());\n        result.setName(engineItem.getName());\n        result.setDisplayName(engineItem.getDisplayName());\n        result.setDescription(engineItem.getDescription());\n        result.setProcessId(engineItem.getProcessDefinitionId());\n\n        return result;\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // UTILS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    /**\n     * @throws InvalidSessionException\n     * @throws BonitaHomeNotSetException\n     * @throws ServerAPIException\n     * @throws UnknownAPITypeException\n     */\n    private ProcessAPI getProcessAPI()\n            throws InvalidSessionException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return TenantAPIAccessor.getProcessAPI(getEngineSession());\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // CRUDS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public ActorItem get(final APIID id) {\n        try {\n            return convertEngineToConsoleItem(getProcessAPI().getActor(id.toLong()));\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @Override\n    public ItemSearchResult<ActorItem> search(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n        try {\n\n            final Long processId = MapUtil.getValueAsLong(filters, ActorItem.ATTRIBUTE_PROCESS_ID);\n\n            final List<ActorInstance> actors = getProcessAPI().getActors(processId,\n                    SearchOptionsBuilderUtil.computeIndex(page, resultsByPage), resultsByPage,\n                    ActorCriterion.valueOf(orders.toUpperCase().replace(\" \", \"_\")));\n\n            return new ItemSearchResult<>(\n                    page,\n                    resultsByPage,\n                    getProcessAPI().getNumberOfActors(processId),\n                    convertEngineToConsoleItemsList(actors));\n\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    /**\n     * @deprecated as of 9.0.0, Actor should be updated at startup.\n     */\n    @Override\n    @Deprecated(since = \"9.0.0\")\n    public ActorItem update(final APIID id, final Map<String, String> attributes) {\n        try {\n            final ActorUpdater updater = new ActorUpdater();\n\n            if (attributes.containsKey(ATTRIBUTE_DISPLAY_NAME)) {\n                updater.setDisplayName(attributes.get(ATTRIBUTE_DISPLAY_NAME));\n            }\n            if (attributes.containsKey(ATTRIBUTE_DESCRIPTION)) {\n                updater.setDescription(attributes.get(ATTRIBUTE_DESCRIPTION));\n            }\n\n            return convertEngineToConsoleItem(getProcessAPI().updateActor(id.toLong(), updater));\n\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // COUNTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public Long countUsers(final APIID actorId) {\n        try {\n            return getProcessAPI().getNumberOfUsersOfActor(actorId.toLong());\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    public Long countGroups(final APIID actorId) {\n        try {\n            return getProcessAPI().getNumberOfGroupsOfActor(actorId.toLong());\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    public Long countRoles(final APIID actorId) {\n        try {\n            return getProcessAPI().getNumberOfRolesOfActor(actorId.toLong());\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    public Long countMemberships(final APIID actorId) {\n        try {\n            return getProcessAPI().getNumberOfMembershipsOfActor(actorId.toLong());\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/process/ActorMemberDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.process;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.console.common.server.utils.ListUtil;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.actor.ActorMember;\nimport org.bonitasoft.engine.exception.*;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.bonitasoft.web.rest.model.bpm.process.ActorMemberItem;\nimport org.bonitasoft.web.rest.model.identity.MemberType;\nimport org.bonitasoft.web.rest.model.portal.profile.AbstractMemberItem;\nimport org.bonitasoft.web.rest.server.datastore.CommonDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasAdd;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasDelete;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch;\nimport org.bonitasoft.web.rest.server.framework.exception.APIAttributesException;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIForbiddenException;\nimport org.bonitasoft.web.toolkit.client.common.i18n.T_;\nimport org.bonitasoft.web.toolkit.client.common.util.MapUtil;\nimport org.bonitasoft.web.toolkit.client.common.util.StringUtil;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Séverin Moussel\n */\npublic class ActorMemberDatastore extends CommonDatastore<ActorMemberItem, ActorMember> implements\n        DatastoreHasAdd<ActorMemberItem>,\n        DatastoreHasSearch<ActorMemberItem>,\n        DatastoreHasDelete {\n\n    public ActorMemberDatastore(final APISession engineSession) {\n        super(engineSession);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // UTILS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    /**\n     * @throws InvalidSessionException\n     * @throws BonitaHomeNotSetException\n     * @throws ServerAPIException\n     * @throws UnknownAPITypeException\n     */\n    private ProcessAPI getProcessAPI()\n            throws InvalidSessionException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return TenantAPIAccessor.getProcessAPI(getEngineSession());\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // CRUDS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public void delete(final List<APIID> ids) {\n        try {\n            for (final APIID id : ids) {\n                getProcessAPI().removeActorMember(id.toLong());\n            }\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @Override\n    public ItemSearchResult<ActorMemberItem> search(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n\n        try {\n            final Long actorId = MapUtil.getValueAsLong(filters, ActorMemberItem.ATTRIBUTE_ACTOR_ID);\n\n            final List<ActorMember> unfilteredResults = getProcessAPI().getActorMembers(\n                    actorId,\n                    0, Integer.MAX_VALUE);\n\n            final List<ActorMember> filteredResults = applyTypeFilter(filters.get(ActorMemberItem.FILTER_MEMBER_TYPE),\n                    unfilteredResults);\n\n            @SuppressWarnings(\"unchecked\")\n            final List<ActorMember> paginatedResults = (List<ActorMember>) ListUtil.paginate(filteredResults, page,\n                    resultsByPage);\n\n            final List<ActorMemberItem> finalResults = convertEngineToConsoleItemsList(paginatedResults);\n            for (final ActorMemberItem actorMemberItem : finalResults) {\n                actorMemberItem.setActorId(actorId);\n            }\n\n            return new ItemSearchResult<>(\n                    page, resultsByPage,\n                    filteredResults.size(),\n                    finalResults);\n\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    /**\n     * @param filterType\n     *        We accept filter value or {@link MemberType} enum value. Better use MemberType enum value\n     */\n    private List<ActorMember> applyTypeFilter(final String filterType, final List<ActorMember> unfilteredResults) {\n        final List<ActorMember> filteredResults = new ArrayList<>();\n        if (StringUtil.isBlank(filterType)) {\n            filteredResults.addAll(unfilteredResults);\n        } else {\n            if (isMemberTypeUser(filterType)) {\n                filterToUser(unfilteredResults, filteredResults);\n            } else if (isMemberTypeRole(filterType)) {\n                filterToRole(unfilteredResults, filteredResults);\n            } else if (isMemberTypeGroup(filterType)) {\n                filterToGroup(unfilteredResults, filteredResults);\n            } else if (isMemberTypeMembership(filterType)) {\n                filterToMembership(unfilteredResults, filteredResults);\n            }\n        }\n\n        return filteredResults;\n    }\n\n    private boolean isMemberTypeUser(final String filterType) {\n        return AbstractMemberItem.VALUE_MEMBER_TYPE_USER.equalsIgnoreCase(filterType)\n                || MemberType.USER.name().equals(filterType);\n    }\n\n    private boolean isMemberTypeRole(final String filterType) {\n        return AbstractMemberItem.VALUE_MEMBER_TYPE_ROLE.equalsIgnoreCase(filterType)\n                || MemberType.ROLE.name().equals(filterType);\n    }\n\n    private boolean isMemberTypeGroup(final String filterType) {\n        return AbstractMemberItem.VALUE_MEMBER_TYPE_GROUP.equalsIgnoreCase(filterType)\n                || MemberType.GROUP.name().equals(filterType);\n    }\n\n    private boolean isMemberTypeMembership(final String filterType) {\n        return AbstractMemberItem.VALUE_MEMBER_TYPE_MEMBERSHIP.equalsIgnoreCase(filterType)\n                || MemberType.MEMBERSHIP.name().equals(filterType);\n    }\n\n    private void filterToUser(final List<ActorMember> unfilteredResults, final List<ActorMember> filteredResults) {\n        for (final ActorMember result : unfilteredResults) {\n            if (APIID.makeAPIID(result.getUserId()) != null) {\n                filteredResults.add(result);\n            }\n        }\n    }\n\n    private void filterToRole(final List<ActorMember> unfilteredResults, final List<ActorMember> filteredResults) {\n        for (final ActorMember result : unfilteredResults) {\n            if (APIID.makeAPIID(result.getRoleId()) != null && APIID.makeAPIID(result.getGroupId()) == null) {\n                filteredResults.add(result);\n            }\n        }\n    }\n\n    private void filterToGroup(final List<ActorMember> unfilteredResults, final List<ActorMember> filteredResults) {\n        for (final ActorMember result : unfilteredResults) {\n            if (APIID.makeAPIID(result.getGroupId()) != null && APIID.makeAPIID(result.getRoleId()) == null) {\n                filteredResults.add(result);\n            }\n        }\n    }\n\n    private void filterToMembership(final List<ActorMember> unfilteredResults,\n            final List<ActorMember> filteredResults) {\n        for (final ActorMember result : unfilteredResults) {\n            if (APIID.makeAPIID(result.getGroupId()) != null && APIID.makeAPIID(result.getRoleId()) != null) {\n                filteredResults.add(result);\n            }\n        }\n    }\n\n    @Override\n    public ActorMemberItem add(final ActorMemberItem item) {\n        try {\n            ActorMember addedActorMember = null;\n            if (isUserActorMember(item)) {\n                addedActorMember = addUserActorMember(item);\n            } else if (isMembershipActorMember(item)) {\n                addedActorMember = addMembershipActorMember(item);\n            } else if (isRoleActorMember(item)) {\n                addedActorMember = addRoleActorMember(item);\n            } else if (isGroupActorMember(item)) {\n                addedActorMember = addGroupActorMember(item);\n            } else {\n                throw new APIAttributesException(ActorMemberItem.ATTRIBUTE_USER_ID, ActorMemberItem.ATTRIBUTE_ROLE_ID,\n                        ActorMemberItem.ATTRIBUTE_GROUP_ID);\n            }\n            final ActorMemberItem addedItem = convertEngineToConsoleItem(addedActorMember);\n            addedItem.setActorId(item.getActorId());\n            return addedItem;\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    private ActorMember addGroupActorMember(final ActorMemberItem item)\n            throws InvalidSessionException, NotFoundException,\n            BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        try {\n            return getProcessAPI().addGroupToActor(item.getActorId().toLong(), item.getGroupId().toLong());\n        } catch (final AlreadyExistsException e) {\n            throw new APIForbiddenException(new T_(\"This group has already been mapped to actor\"), e);\n        } catch (CreationException e) {\n            throw new APIException(new T_(\"Error when adding group to actor member\"), e);\n        }\n    }\n\n    private ActorMember addRoleActorMember(final ActorMemberItem item)\n            throws InvalidSessionException, NotFoundException,\n            BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        try {\n            return getProcessAPI().addRoleToActor(item.getActorId().toLong(), item.getRoleId().toLong());\n        } catch (final CreationException e) {\n            throw new APIForbiddenException(new T_(\"This role has already been mapped to actor\"), e);\n        }\n    }\n\n    private ActorMember addMembershipActorMember(final ActorMemberItem item) throws InvalidSessionException,\n            NotFoundException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        try {\n            return getProcessAPI()\n                    .addRoleAndGroupToActor(item.getActorId().toLong(), item.getRoleId().toLong(),\n                            item.getGroupId().toLong());\n        } catch (final CreationException e) {\n            throw new APIForbiddenException(new T_(\"This membership has already been mapped to actor\"), e);\n        }\n    }\n\n    private ActorMember addUserActorMember(final ActorMemberItem item)\n            throws InvalidSessionException, NotFoundException,\n            BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        try {\n            return getProcessAPI().addUserToActor(item.getActorId().toLong(), item.getUserId().toLong());\n        } catch (final CreationException e) {\n            throw new APIForbiddenException(new T_(\"This user has already been mapped to actor\"), e);\n        }\n    }\n\n    private boolean isGroupActorMember(final ActorMemberItem item) {\n        return item.getGroupId() != null;\n    }\n\n    private boolean isRoleActorMember(final ActorMemberItem item) {\n        return item.getRoleId() != null;\n    }\n\n    private boolean isMembershipActorMember(final ActorMemberItem item) {\n        return isRoleActorMember(item) && isGroupActorMember(item);\n    }\n\n    private boolean isUserActorMember(final ActorMemberItem item) {\n        return item.getUserId() != null;\n    }\n\n    @Override\n    protected ActorMemberItem convertEngineToConsoleItem(final ActorMember item) {\n        final ActorMemberItem actorMemberItem = new ActorMemberItem();\n\n        actorMemberItem.setId(item.getId());\n        actorMemberItem.setUserId(item.getUserId());\n        actorMemberItem.setRoleId(item.getRoleId());\n        actorMemberItem.setGroupId(item.getGroupId());\n\n        return actorMemberItem;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/process/CategoryDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.process;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.category.Category;\nimport org.bonitasoft.engine.bpm.category.CategoryCriterion;\nimport org.bonitasoft.engine.bpm.category.CategoryNotFoundException;\nimport org.bonitasoft.engine.bpm.category.CategoryUpdater;\nimport org.bonitasoft.engine.exception.*;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.bonitasoft.web.rest.model.bpm.process.CategoryDefinition;\nimport org.bonitasoft.web.rest.model.bpm.process.CategoryItem;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessItem;\nimport org.bonitasoft.web.rest.server.datastore.CommonDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasAdd;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasDelete;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasUpdate;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIForbiddenException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException;\nimport org.bonitasoft.web.toolkit.client.common.i18n.T_;\nimport org.bonitasoft.web.toolkit.client.common.texttemplate.Arg;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * Category data store\n *\n * @author Nicolas TITH\n */\npublic class CategoryDatastore extends CommonDatastore<CategoryItem, Category> implements\n        DatastoreHasSearch<CategoryItem>,\n        DatastoreHasGet<CategoryItem>,\n        DatastoreHasDelete,\n        DatastoreHasAdd<CategoryItem>,\n        DatastoreHasUpdate<CategoryItem> {\n\n    public CategoryDatastore(final APISession engineSession) {\n        super(engineSession);\n    }\n\n    /**\n     * Retrieve the total number of categories\n     *\n     * @return the total number of categories\n     * @throws InvalidSessionException\n     *         When session time out throw this exception\n     * @throws BonitaHomeNotSetException\n     *         When bonita home not set throw this exception\n     * @throws ServerAPIException\n     *         When access server api have problem throw this exception\n     * @throws UnknownAPITypeException\n     *         When didn't know the api type throw this exception\n     */\n    public long getNumberOfCategories() {\n        try {\n            return getProcessAPI().getNumberOfCategories();\n        } catch (final InvalidSessionException e) {\n            throw new APIException(e);\n        }\n    }\n\n    /**\n     * Retrieve the total number of categories for a given process\n     */\n    public long getNumberOfCategories(long processId) {\n        return getProcessAPI().getNumberOfCategories(processId);\n    }\n\n    @Override\n    public ItemSearchResult<CategoryItem> search(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n        CategoryCriterion orderCrit = getOrder(orders);\n        if (filters.containsKey(ProcessItem.ATTRIBUTE_ID)) {\n            return searchProcessCategories(page, resultsByPage, filters, orderCrit);\n        } else {\n            return searchCategories(page, resultsByPage, orderCrit);\n        }\n    }\n\n    private ItemSearchResult<CategoryItem> searchCategories(final int page, final int resultsByPage,\n            CategoryCriterion orderCrit) {\n        List<Category> searchResult = getProcessAPI().getCategories(\n                SearchOptionsBuilderUtil.computeIndex(page, resultsByPage),\n                resultsByPage,\n                orderCrit);\n        return new ItemSearchResult<>(page, resultsByPage, getNumberOfCategories(),\n                convertEngineToConsoleItemsList(searchResult));\n    }\n\n    private ItemSearchResult<CategoryItem> searchProcessCategories(final int page, final int resultsByPage,\n            final Map<String, String> filters,\n            CategoryCriterion orderCrit) {\n        final Long processId = Long.valueOf(filters.get(ProcessItem.ATTRIBUTE_ID));\n        List<Category> searchResult = getProcessAPI().getCategoriesOfProcessDefinition(processId,\n                SearchOptionsBuilderUtil.computeIndex(page, resultsByPage),\n                resultsByPage,\n                orderCrit);\n        return new ItemSearchResult<>(page, resultsByPage, getNumberOfCategories(processId),\n                convertEngineToConsoleItemsList(searchResult));\n    }\n\n    private CategoryCriterion getOrder(final String orders) {\n        if (CategoryItem.ATTRIBUTE_NAME.equals(orders)) {\n            return CategoryCriterion.NAME_ASC;\n        } else {\n            return CategoryCriterion.valueOf(orders);\n        }\n    }\n\n    @Override\n    public CategoryItem add(final CategoryItem item) {\n        try {\n            final Category result = getProcessAPI().createCategory(item.getName(), item.getDescription());\n            return convertEngineToConsoleItem(result);\n        } catch (final AlreadyExistsException e) {\n            throw new APIForbiddenException(\n                    new T_(\"Category with name %categoryName% already exists\", new Arg(\"categoryName\", item.getName())),\n                    e);\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @Override\n    public CategoryItem get(final APIID id) {\n        try {\n            final Category result = getProcessAPI().getCategory(id.toLong());\n            return convertEngineToConsoleItem(result);\n        } catch (CategoryNotFoundException e) {\n            throw new APIItemNotFoundException(CategoryDefinition.TOKEN, id);\n        }\n    }\n\n    @Override\n    protected CategoryItem convertEngineToConsoleItem(final Category item) {\n        final CategoryItem categoryItem = new CategoryItem();\n        categoryItem.setCreatedByUserId(item.getCreator());\n        categoryItem.setCreationDate(item.getCreationDate());\n        categoryItem.setDescription(item.getDescription());\n        categoryItem.setDisplayName(item.getName());\n        categoryItem.setId(item.getId());\n        categoryItem.setName(item.getName());\n        return categoryItem;\n    }\n\n    protected final CategoryUpdater createCategoryUpdater(final CategoryItem item) {\n        if (item == null) {\n            return null;\n        }\n        final CategoryUpdater updater = new CategoryUpdater();\n        if (item.getDescription() != null) {\n            updater.setDescription(item.getDescription());\n        }\n        return updater;\n    }\n\n    @Override\n    public void delete(final List<APIID> ids) {\n        try {\n            final ProcessAPI processAPI = getProcessAPI();\n            for (final APIID id : ids) {\n                final Long idCat = id.toLong();\n                do {\n                } while (processAPI.removeProcessDefinitionsFromCategory(idCat, 0, 20) > 0);\n                processAPI.deleteCategory(idCat);\n            }\n        } catch (final BonitaException e) {\n            if (e.getCause() instanceof CategoryNotFoundException) {\n                throw new APIItemNotFoundException(CategoryDefinition.TOKEN);\n            } else {\n                throw new APIException(e);\n            }\n        }\n    }\n\n    @Override\n    public CategoryItem update(final APIID id, final Map<String, String> attributes) {\n        try {\n            final ProcessAPI processAPI = getProcessAPI();\n            final CategoryItem catItem = new CategoryItem();\n            catItem.setAttributes(attributes);\n            catItem.setId(id);\n            processAPI.updateCategory(id.toLong(), createCategoryUpdater(catItem));\n            return get(id);\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    protected ProcessAPI getProcessAPI() {\n        try {\n            return TenantAPIAccessor.getProcessAPI(getEngineSession());\n        } catch (BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/process/ProcessCategoryDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.process;\n\nimport java.io.Serializable;\nimport java.util.*;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessCategoryItem;\nimport org.bonitasoft.web.rest.server.datastore.CommonDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasAdd;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasDelete;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIForbiddenException;\nimport org.bonitasoft.web.toolkit.client.common.i18n.T_;\nimport org.bonitasoft.web.toolkit.client.common.util.MapUtil;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Séverin Moussel\n */\npublic class ProcessCategoryDatastore extends CommonDatastore<ProcessCategoryItem, Serializable> implements\n        DatastoreHasAdd<ProcessCategoryItem>,\n        DatastoreHasDelete {\n\n    public ProcessCategoryDatastore(final APISession engineSession) {\n        super(engineSession);\n    }\n\n    @Override\n    protected ProcessCategoryItem convertEngineToConsoleItem(final Serializable item) {\n        // No conversion here\n        return null;\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // C.R.U.D.S\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public void delete(final List<APIID> ids) {\n        MapUtil.iterate(\n                buildCategoriesIdByProcessIdMapping(ids),\n                new MapUtil.ForEach<>() {\n\n                    @Override\n                    protected void apply(Long processId, List<Long> categoriesId) {\n                        removeCategoriesFromProcess(processId, categoriesId);\n                    }\n                });\n    }\n\n    private void removeCategoriesFromProcess(Long processId, List<Long> categoriesId) {\n        try {\n            getProcessAPI().removeCategoriesFromProcess(processId, categoriesId);\n        } catch (BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    private Map<Long, List<Long>> buildCategoriesIdByProcessIdMapping(List<APIID> ids) {\n        Map<Long, List<Long>> categoriesIdByProcessId = new HashMap<>();\n        for (APIID apiid : ids) {\n            Long processId = apiid.getPartAsLong(ProcessCategoryItem.ATTRIBUTE_PROCESS_ID);\n            Long categoryId = apiid.getPartAsLong(ProcessCategoryItem.ATTRIBUTE_CATEGORY_ID);\n            if (categoriesIdByProcessId.containsKey(processId)) {\n                categoriesIdByProcessId.get(processId).add(categoryId);\n            } else {\n                ArrayList<Long> categoryIds = new ArrayList<>();\n                categoryIds.add(categoryId);\n                categoriesIdByProcessId.put(processId, categoryIds);\n            }\n        }\n        return categoriesIdByProcessId;\n    }\n\n    @Override\n    public ProcessCategoryItem add(final ProcessCategoryItem item) {\n        try {\n            getProcessAPI().addCategoriesToProcess(item.getProcessId().toLong(),\n                    Collections.singletonList(item.getCategoryId().toLong()));\n\n            return item;\n        } catch (AlreadyExistsException e) {\n            throw new APIForbiddenException(new T_(\"This category has already been added to this process\"), e);\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    protected ProcessAPI getProcessAPI() {\n        try {\n            return TenantAPIAccessor.getProcessAPI(getEngineSession());\n        } catch (BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/process/ProcessConnectorDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.process;\n\nimport static org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorItem.ATTRIBUTE_NAME;\nimport static org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorItem.ATTRIBUTE_PROCESS_ID;\nimport static org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorItem.ATTRIBUTE_VERSION;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.connector.ConnectorCriterion;\nimport org.bonitasoft.engine.bpm.connector.ConnectorImplementationDescriptor;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorItem;\nimport org.bonitasoft.web.rest.server.datastore.CommonDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.util.MapUtil;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Séverin Moussel\n */\npublic class ProcessConnectorDatastore extends CommonDatastore<ProcessConnectorItem, ConnectorImplementationDescriptor>\n        implements\n        DatastoreHasGet<ProcessConnectorItem>,\n        DatastoreHasSearch<ProcessConnectorItem> {\n\n    public ProcessConnectorDatastore(final APISession engineSession) {\n        super(engineSession);\n    }\n\n    protected ProcessAPI getProcessAPI() {\n        try {\n            return TenantAPIAccessor.getProcessAPI(getEngineSession());\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @Override\n    public ProcessConnectorItem get(final APIID id) {\n        try {\n\n            final ProcessConnectorItem connector = convertEngineToConsoleItem(\n                    getProcessAPI().getConnectorImplementation(\n                            id.getPartAsLong(ATTRIBUTE_PROCESS_ID),\n                            id.getPart(ATTRIBUTE_NAME),\n                            id.getPart(ATTRIBUTE_VERSION)));\n\n            // Correct missing element in engine object\n            connector.setProcessId(id.getPartAsLong(ATTRIBUTE_PROCESS_ID));\n\n            return connector;\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @Override\n    public ItemSearchResult<ProcessConnectorItem> search(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n\n        final Long processId = MapUtil.getValueAsLong(filters, ATTRIBUTE_PROCESS_ID);\n\n        final List<ConnectorImplementationDescriptor> connectors = getProcessAPI().getConnectorImplementations(\n                processId,\n                SearchOptionsBuilderUtil.computeIndex(page, resultsByPage),\n                resultsByPage,\n                ConnectorCriterion.valueOf(orders.toUpperCase().replace(\" \", \"_\")));\n\n        final List<ProcessConnectorItem> results = new ArrayList<>();\n        for (final ConnectorImplementationDescriptor connector : connectors) {\n            final ProcessConnectorItem result = convertEngineToConsoleItem(connector);\n            // Correct missing element in engine object\n            result.setProcessId(processId);\n\n            results.add(result);\n        }\n\n        final long numtotalConnectorImplem = getProcessAPI().getNumberOfConnectorImplementations(processId);\n\n        return new ItemSearchResult<>(page, resultsByPage, numtotalConnectorImplem, results);\n    }\n\n    @Override\n    protected ProcessConnectorItem convertEngineToConsoleItem(final ConnectorImplementationDescriptor item) {\n\n        final ProcessConnectorItem result = new ProcessConnectorItem();\n        result.setName(item.getDefinitionId());\n        result.setVersion(item.getDefinitionVersion());\n        // setted in get and search because attribute is missing in the engine oject\n        // result.setProcessId(...);\n        result.setImplementationName(item.getId());\n        result.setImplementationVersion(item.getVersion());\n        result.setClassname(item.getImplementationClassName());\n\n        return result;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/process/ProcessConnectorDependencyDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.process;\n\nimport static org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDependencyItem.ATTRIBUTE_CONNECTOR_NAME;\nimport static org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDependencyItem.ATTRIBUTE_CONNECTOR_VERSION;\nimport static org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDependencyItem.ATTRIBUTE_PROCESS_ID;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.console.common.server.utils.ListUtil;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.connector.ConnectorImplementationDescriptor;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDependencyItem;\nimport org.bonitasoft.web.rest.server.datastore.CommonDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.util.MapUtil;\n\n/**\n * @author Séverin Moussel\n */\npublic class ProcessConnectorDependencyDatastore\n        extends CommonDatastore<ProcessConnectorDependencyItem, ConnectorImplementationDescriptor> implements\n        DatastoreHasSearch<ProcessConnectorDependencyItem> {\n\n    public ProcessConnectorDependencyDatastore(final APISession engineSession) {\n        super(engineSession);\n    }\n\n    protected ProcessAPI getProcessAPI() {\n        try {\n            return TenantAPIAccessor.getProcessAPI(getEngineSession());\n        } catch (BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public ItemSearchResult<ProcessConnectorDependencyItem> search(final int page, final int resultsByPage,\n            final String search, final String orders, final Map<String, String> filters) {\n\n        try {\n            final Long processId = MapUtil.getValueAsLong(filters, ATTRIBUTE_PROCESS_ID);\n            final String connectorName = filters.get(ATTRIBUTE_CONNECTOR_NAME);\n            final String connectorVersion = filters.get(ATTRIBUTE_CONNECTOR_VERSION);\n\n            // Get connector definition\n            final ConnectorImplementationDescriptor connectorImplementation = getProcessAPI()\n                    .getConnectorImplementation(processId, connectorName, connectorVersion);\n\n            // If connector definition doesn't exists returns an empty resultset\n            if (connectorImplementation == null) {\n                return new ItemSearchResult<>(page, 0, 0, new ArrayList<>());\n            }\n\n            // Get Jar from definition and Simulate pagination\n            final List<String> jarDependencies = connectorImplementation.getJarDependencies();\n            final List<String> dependencies = (List<String>) ListUtil.paginate(jarDependencies, page, resultsByPage);\n\n            // Convert to consoleItem\n            final List<ProcessConnectorDependencyItem> results = convertEngineToConsoleItems(processId, connectorName,\n                    connectorVersion, dependencies);\n\n            return new ItemSearchResult<>(page, results.size(), jarDependencies.size(), results);\n\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n\n    }\n\n    private List<ProcessConnectorDependencyItem> convertEngineToConsoleItems(final Long processId,\n            final String connectorName, final String connectorVersion, final List<String> dependencies) {\n        final List<ProcessConnectorDependencyItem> results = new ArrayList<>();\n\n        for (final String filename : dependencies) {\n            final ProcessConnectorDependencyItem dependencyItem = convertEngineToConsoleItem(processId, connectorName,\n                    connectorVersion, filename);\n            results.add(dependencyItem);\n        }\n        return results;\n    }\n\n    private ProcessConnectorDependencyItem convertEngineToConsoleItem(final Long processId, final String connectorName,\n            final String connectorVersion, final String filename) {\n        final ProcessConnectorDependencyItem dependencyItem = new ProcessConnectorDependencyItem();\n        dependencyItem.setProcessId(processId);\n        dependencyItem.setConnectorName(connectorName);\n        dependencyItem.setConnectorVersion(connectorVersion);\n        dependencyItem.setFilename(filename);\n        return dependencyItem;\n    }\n\n    @Override\n    protected ProcessConnectorDependencyItem convertEngineToConsoleItem(final ConnectorImplementationDescriptor item) {\n        // Not used. Engine item is not fully populated\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/process/ProcessDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.process;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.console.common.server.page.CustomPageService;\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.console.common.server.utils.PlatformManagementUtils;\nimport org.bonitasoft.engine.api.PageAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchiveFactory;\nimport org.bonitasoft.engine.bpm.bar.InvalidBusinessArchiveFormatException;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoUpdater;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.io.FileContent;\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.engine.page.PageSearchDescriptor;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessItem;\nimport org.bonitasoft.web.rest.server.datastore.CommonDatastore;\nimport org.bonitasoft.web.rest.server.datastore.bpm.process.helper.ProcessItemConverter;\nimport org.bonitasoft.web.rest.server.datastore.bpm.process.helper.SearchProcessHelper;\nimport org.bonitasoft.web.rest.server.engineclient.EngineAPIAccessor;\nimport org.bonitasoft.web.rest.server.engineclient.EngineClientFactory;\nimport org.bonitasoft.web.rest.server.engineclient.ProcessEngineClient;\nimport org.bonitasoft.web.rest.server.framework.api.*;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Process data store\n *\n * @author Vincent Elcrin\n */\npublic class ProcessDatastore extends CommonDatastore<ProcessItem, ProcessDeploymentInfo> implements\n        DatastoreHasAdd<ProcessItem>,\n        DatastoreHasUpdate<ProcessItem>,\n        DatastoreHasGet<ProcessItem>,\n        DatastoreHasSearch<ProcessItem>,\n        DatastoreHasDelete {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(ProcessDatastore.class.getName());\n\n    /**\n     * process file\n     */\n    private static final String FILE_UPLOAD = \"fileupload\";\n\n    private static final int DELETE_PAGES_BUNCH_SIZE = 100;\n\n    public ProcessDatastore(final APISession engineSession) {\n        super(engineSession);\n    }\n\n    /**\n     * @deprecated as of 9.0.0, Process should be created at startup.\n     */\n    @Override\n    @Deprecated(since = \"9.0.0\")\n    public ProcessItem add(final ProcessItem process) {\n        final ProcessEngineClient engineClient = getProcessEngineClient();\n\n        FileContent processFile;\n        try {\n            processFile = getTenantFolder().retrieveUploadedTempContent(process.getAttributes().get(FILE_UPLOAD));\n        } catch (final BonitaException e) {\n            throw new APIException(\"Process file not found\", e);\n        }\n        try {\n            //no need to handle the closing of the stream here as it is handled in BusinessArchiveFactory\n            final BusinessArchive businessArchive = readBusinessArchive(processFile.getInputStream());\n            final ProcessDefinition deployedArchive = engineClient.deploy(businessArchive);\n            final ProcessDeploymentInfo processDeploymentInfo = engineClient\n                    .getProcessDeploymentInfo(deployedArchive.getId());\n            return convertEngineToConsoleItem(processDeploymentInfo);\n        } finally {\n            getTenantFolder().removeUploadedTempContent(process.getAttributes().get(FILE_UPLOAD));\n        }\n    }\n\n    protected BonitaHomeFolderAccessor getTenantFolder() {\n        return new BonitaHomeFolderAccessor();\n    }\n\n    /*\n     * Overridden in SP\n     */\n    protected BusinessArchive readBusinessArchive(final InputStream inputStream) {\n        try {\n            return BusinessArchiveFactory.readBusinessArchive(inputStream);\n        } catch (final IOException | InvalidBusinessArchiveFormatException e) {\n            throw new APIException(e);\n        }\n    }\n\n    /**\n     * @deprecated as of 9.0.0, Process should be updated at startup.\n     */\n    @Override\n    @Deprecated(since = \"9.0.0\")\n    public ProcessItem update(final APIID id, final Map<String, String> attributes) {\n        final ProcessDeploymentInfoUpdater updater = new ProcessDeploymentInfoUpdater();\n\n        final ProcessEngineClient engineClient = getProcessEngineClient();\n        if (attributes.containsKey(ProcessItem.ATTRIBUTE_DISPLAY_DESCRIPTION)) {\n            updater.setDisplayDescription(attributes.get(ProcessItem.ATTRIBUTE_DISPLAY_DESCRIPTION));\n        }\n\n        if (attributes.containsKey(ProcessItem.ATTRIBUTE_DISPLAY_NAME)) {\n            updater.setDisplayName(attributes.get(ProcessItem.ATTRIBUTE_DISPLAY_NAME));\n        }\n\n        // specific engine methods\n        if (attributes.containsKey(ProcessItem.ATTRIBUTE_ACTIVATION_STATE)) {\n            changeProcessState(engineClient, id.toLong(), attributes.get(ProcessItem.ATTRIBUTE_ACTIVATION_STATE));\n        }\n\n        if (!updater.getFields().isEmpty()) {\n            final ProcessDeploymentInfo processDeploymentInfo = engineClient.updateProcessDeploymentInfo(id.toLong(),\n                    updater);\n            return convertEngineToConsoleItem(processDeploymentInfo);\n        } else {\n            return convertEngineToConsoleItem(engineClient.getProcessDeploymentInfo(id.toLong()));\n        }\n    }\n\n    private void changeProcessState(final ProcessEngineClient engineClient, final Long processId, final String state) {\n        if (ProcessItem.VALUE_ACTIVATION_STATE_DISABLED.equals(state)) {\n            engineClient.disableProcess(processId);\n        } else if (ProcessItem.VALUE_ACTIVATION_STATE_ENABLED.equals(state)) {\n            engineClient.enableProcess(processId);\n        }\n    }\n\n    protected PlatformManagementUtils getPlatformManagementUtils() {\n        return new PlatformManagementUtils();\n    }\n\n    @Override\n    public ProcessItem get(final APIID id) {\n        final ProcessEngineClient engineClient = getProcessEngineClient();\n        final ProcessDeploymentInfo processDeploymentInfo = engineClient.getProcessDeploymentInfo(id.toLong());\n        return convertEngineToConsoleItem(processDeploymentInfo);\n    }\n\n    @Override\n    public void delete(final List<APIID> ids) {\n        for (final APIID id : ids) {\n            removeProcessPagesFromHome(id);\n        }\n        final ProcessEngineClient engineClient = getProcessEngineClient();\n        engineClient.deleteDisabledProcesses(APIID.toLongList(ids));\n    }\n\n    protected void removeProcessPagesFromHome(final APIID id) {\n        try {\n            int startIndex = 0;\n            int count = 0;\n            do {\n                final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(startIndex,\n                        DELETE_PAGES_BUNCH_SIZE);\n                searchOptionsBuilder.filter(PageSearchDescriptor.PROCESS_DEFINITION_ID, id.toLong());\n                final SearchResult<Page> result = getPageAPI().searchPages(searchOptionsBuilder.done());\n                if (count == 0) {\n                    count = (int) result.getCount();\n                }\n                startIndex = startIndex + result.getResult().size();\n                for (final Page page : result.getResult()) {\n                    getCustomPageService().removePageLocally(page);\n                }\n            } while (startIndex < count);\n        } catch (final BonitaException | IOException e) {\n            if (LOGGER.isWarnEnabled()) {\n                LOGGER.warn(\"Error when deleting pages for process with ID \" + id, e);\n            }\n        }\n    }\n\n    protected CustomPageService getCustomPageService() {\n        return new CustomPageService();\n    }\n\n    protected PageAPI getPageAPI() {\n        try {\n            return TenantAPIAccessor.getCustomPageAPI(getEngineSession());\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @Override\n    public ItemSearchResult<ProcessItem> search(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n        final ProcessEngineClient engineClient = getProcessEngineClient();\n        return new SearchProcessHelper(engineClient).search(page, resultsByPage, search, orders, filters);\n    }\n\n    @Override\n    protected ProcessItem convertEngineToConsoleItem(final ProcessDeploymentInfo item) {\n        if (item != null) {\n            return new ProcessItemConverter(getProcessEngineClient().getProcessApi()).convert(item);\n        }\n        return null;\n    }\n\n    protected ProcessEngineClient getProcessEngineClient() {\n        return getEngineClientFactory().createProcessEngineClient();\n    }\n\n    private EngineClientFactory getEngineClientFactory() {\n        return new EngineClientFactory(new EngineAPIAccessor(getEngineSession()));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/process/ProcessResolutionProblemDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.process;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bpm.process.Problem;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessResolutionProblemItem;\nimport org.bonitasoft.web.rest.server.datastore.CommonDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.util.MapUtil;\n\n/**\n * @author Séverin Moussel\n */\npublic class ProcessResolutionProblemDatastore extends CommonDatastore<ProcessResolutionProblemItem, Problem> implements\n        DatastoreHasSearch<ProcessResolutionProblemItem> {\n\n    public ProcessResolutionProblemDatastore(final APISession engineSession) {\n        super(engineSession);\n    }\n\n    @Override\n    protected ProcessResolutionProblemItem convertEngineToConsoleItem(final Problem item) {\n        final ProcessResolutionProblemItem consoleItem = new ProcessResolutionProblemItem();\n        consoleItem.setMessage(item.getDescription());\n        consoleItem.setTargetType(item.getResource());\n        consoleItem.setRessourceId(item.getResourceId());\n        return consoleItem;\n    }\n\n    @Override\n    public ItemSearchResult<ProcessResolutionProblemItem> search(final int page, final int resultsByPage,\n            final String search, final String orders,\n            final Map<String, String> filters) {\n        try {\n\n            final List<Problem> errors = TenantAPIAccessor.getProcessAPI(getEngineSession())\n                    .getProcessResolutionProblems(\n                            MapUtil.getValueAsLong(filters, ProcessResolutionProblemItem.FILTER_PROCESS_ID));\n\n            final int startIndex = page * resultsByPage;\n            return new ItemSearchResult<>(\n                    page,\n                    resultsByPage,\n                    errors.size(),\n                    convertEngineToConsoleItemsList(\n                            errors.subList(\n                                    startIndex,\n                                    Math.min(startIndex + resultsByPage, errors.size()))));\n\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/process/helper/ProcessItemConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.process.helper;\n\nimport org.bonitasoft.console.common.server.utils.TenantCacheUtil;\nimport org.bonitasoft.console.common.server.utils.TenantCacheUtilFactory;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.actor.ActorNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessItem;\nimport org.bonitasoft.web.rest.server.datastore.converter.ItemConverter;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n;\nimport org.bonitasoft.web.toolkit.client.common.texttemplate.Arg;\n\n/**\n * @author Vincent Elcrin\n */\npublic class ProcessItemConverter extends ItemConverter<ProcessItem, ProcessDeploymentInfo> {\n\n    private final ProcessAPI processApi;\n\n    public ProcessItemConverter(final ProcessAPI processApi) {\n        this.processApi = processApi;\n    }\n\n    @Override\n    public ProcessItem convert(ProcessDeploymentInfo engineItem) {\n\n        final ProcessItem item = new ProcessItem();\n        item.setId(engineItem.getProcessId());\n        item.setName(engineItem.getName());\n        item.setVersion(engineItem.getVersion());\n        item.setDescription(engineItem.getDescription());\n        item.setDeployedByUserId(engineItem.getDeployedBy());\n        item.setDeploymentDate(engineItem.getDeploymentDate());\n        item.setActivationState(engineItem.getActivationState().name());\n        item.setConfigurationState(engineItem.getConfigurationState().name());\n        item.setDisplayName(engineItem.getDisplayName());\n        item.setDisplayDescription(engineItem.getDisplayDescription());\n        item.setLastUpdateDate(engineItem.getLastUpdateDate());\n        item.setActorInitiatorId(getActorInitiator(engineItem));\n\n        return item;\n    }\n\n    private Long getActorInitiator(ProcessDeploymentInfo engineItem) {\n        TenantCacheUtil tenantCacheUtil = TenantCacheUtilFactory.getTenantCacheUtil();\n        Long actorInitiatorId = tenantCacheUtil.getProcessActorInitiatorId(engineItem.getProcessId());\n        if (actorInitiatorId == null) {\n            actorInitiatorId = tenantCacheUtil.storeProcessActorInitiatorId(engineItem.getProcessId(),\n                    getActorInitiatorFromEngine(engineItem));\n        }\n        return actorInitiatorId;\n    }\n\n    private Long getActorInitiatorFromEngine(ProcessDeploymentInfo engineItem) {\n        Long actorInitiatorId;\n        try {\n            actorInitiatorId = processApi.getActorInitiator(engineItem.getProcessId()).getId();\n        } catch (ActorNotFoundException e) {\n            actorInitiatorId = -1L;\n        } catch (ProcessDefinitionNotFoundException e) {\n            throw new APIException(AbstractI18n.t_(\"Process definition not found for id %processId%\",\n                    new Arg(\"processId\", String.valueOf(engineItem.getProcessId()))), e);\n        }\n        return actorInitiatorId;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/process/helper/ProcessSearchDescriptorConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.process.helper;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoSearchDescriptor;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessItem;\nimport org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute.TYPE;\n\n/**\n * @author Vincent Elcrin\n */\npublic class ProcessSearchDescriptorConverter implements AttributeConverter {\n\n    protected static final Map<String, String> mapping = new HashMap<>();\n\n    @Override\n    public Map<String, TYPE> getValueTypeMapping() {\n        return Collections.emptyMap();\n    }\n\n    static {\n        mapping.put(ProcessItem.ATTRIBUTE_ID, ProcessDeploymentInfoSearchDescriptor.ID);\n        mapping.put(ProcessItem.ATTRIBUTE_ACTIVATION_STATE, ProcessDeploymentInfoSearchDescriptor.ACTIVATION_STATE);\n        mapping.put(ProcessItem.ATTRIBUTE_CONFIGURATION_STATE,\n                ProcessDeploymentInfoSearchDescriptor.CONFIGURATION_STATE);\n        mapping.put(ProcessItem.ATTRIBUTE_DEPLOYED_BY_USER_ID, ProcessDeploymentInfoSearchDescriptor.DEPLOYED_BY);\n        mapping.put(ProcessItem.ATTRIBUTE_DEPLOYMENT_DATE, ProcessDeploymentInfoSearchDescriptor.DEPLOYMENT_DATE);\n        mapping.put(ProcessItem.ATTRIBUTE_DISPLAY_NAME, ProcessDeploymentInfoSearchDescriptor.DISPLAY_NAME);\n        mapping.put(ProcessItem.ATTRIBUTE_LAST_UPDATE_DATE, ProcessDeploymentInfoSearchDescriptor.LAST_UPDATE_DATE);\n        mapping.put(ProcessItem.ATTRIBUTE_NAME, ProcessDeploymentInfoSearchDescriptor.NAME);\n        mapping.put(ProcessItem.ATTRIBUTE_VERSION, ProcessDeploymentInfoSearchDescriptor.VERSION);\n\n        mapping.put(ProcessItem.FILTER_CATEGORY_ID, ProcessDeploymentInfoSearchDescriptor.CATEGORY_ID);\n        mapping.put(ProcessItem.FILTER_RECENT_PROCESSES, \"\"); // code smell. Should return empty object (EmptyField)\n        mapping.put(ProcessItem.FILTER_SUPERVISOR_ID, \"\");\n        mapping.put(ProcessItem.FILTER_TEAM_MANAGER_ID, \"\");\n        mapping.put(ProcessItem.FILTER_USER_ID, \"\");\n        mapping.put(ProcessItem.FILTER_FOR_PENDING_OR_ASSIGNED_TASKS, \"\");\n    }\n\n    @Override\n    public String convert(String attribute) {\n        String value = mapping.get(attribute);\n        if (value == null) {\n            throw new RuntimeException(\"Can't find search descriptor corresponding to \" + attribute);\n        }\n        return value;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/process/helper/SearchProcessFilterCreator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.process.helper;\n\nimport org.bonitasoft.web.rest.server.datastore.filter.GenericFilterCreator;\n\n/**\n * @author Vincent Elcrin\n */\nclass SearchProcessFilterCreator extends GenericFilterCreator {\n\n    SearchProcessFilterCreator(ProcessSearchDescriptorConverter converter) {\n        super(converter);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/process/helper/SearchProcessHelper.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.process.helper;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessItem;\nimport org.bonitasoft.web.rest.server.datastore.converter.ItemSearchResultConverter;\nimport org.bonitasoft.web.rest.server.datastore.filter.Filters;\nimport org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator;\nimport org.bonitasoft.web.rest.server.datastore.utils.Sorts;\nimport org.bonitasoft.web.rest.server.engineclient.ProcessEngineClient;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Vincent Elcrin\n */\npublic class SearchProcessHelper implements DatastoreHasSearch<ProcessItem> {\n\n    private final ProcessEngineClient engineClient;\n\n    public SearchProcessHelper(ProcessEngineClient engineClient) {\n        this.engineClient = engineClient;\n    }\n\n    @Override\n    public ItemSearchResult<ProcessItem> search(int page, int resultsByPage, String search, String orders,\n            Map<String, String> filters) {\n        SearchOptionsCreator searchOptions = new SearchOptionsCreator(page, resultsByPage, search,\n                new Sorts(orders, new ProcessSearchDescriptorConverter()),\n                new Filters(filters, new SearchProcessFilterCreator(new ProcessSearchDescriptorConverter())));\n        final SearchResult<ProcessDeploymentInfo> result = runSearch(filters, searchOptions.create());\n        return convertResult(page, resultsByPage, result);\n    }\n\n    private SearchResult<ProcessDeploymentInfo> runSearch(Map<String, String> filters, SearchOptions searchOptions) {\n\n        if (isFilteringOn(filters, ProcessItem.FILTER_USER_ID, ProcessItem.FILTER_RECENT_PROCESSES)) {\n            return engineClient.searchRecentlyStartedProcessDefinitions(getApiId(filters, ProcessItem.FILTER_USER_ID),\n                    searchOptions);\n        } else if (isFilteringOn(filters, ProcessItem.FILTER_USER_ID, ProcessItem.FILTER_CATEGORY_ID)\n                && filters.get(ProcessItem.FILTER_CATEGORY_ID) == null) {\n            return engineClient.searchUncategorizedProcessDefinitionsUserCanStart(\n                    getApiId(filters, ProcessItem.FILTER_USER_ID), searchOptions);\n        } else if (isFilteringOn(filters, ProcessItem.FILTER_USER_ID,\n                ProcessItem.FILTER_FOR_PENDING_OR_ASSIGNED_TASKS)) {\n            return engineClient.searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(\n                    getApiId(filters, ProcessItem.FILTER_USER_ID), searchOptions);\n        } else if (isFilteringOn(filters, ProcessItem.FILTER_USER_ID)) {\n            return engineClient.searchProcessDeploymentInfos(getApiId(filters, ProcessItem.FILTER_USER_ID),\n                    searchOptions);\n        } else if (isFilteringOn(filters, ProcessItem.FILTER_SUPERVISOR_ID,\n                ProcessItem.FILTER_FOR_PENDING_OR_ASSIGNED_TASKS)) {\n            return engineClient.searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(\n                    getApiId(filters, ProcessItem.FILTER_USER_ID),\n                    searchOptions);\n        } else if (isFilteringOn(filters, ProcessItem.FILTER_SUPERVISOR_ID)) {\n            return engineClient.searchProcessDefinitionsSupervisedBy(\n                    getApiId(filters, ProcessItem.FILTER_SUPERVISOR_ID), searchOptions);\n        } else if (isFilteringOn(filters, ProcessItem.FILTER_FOR_PENDING_OR_ASSIGNED_TASKS)) {\n            return engineClient.searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks(searchOptions);\n        } else {\n            return engineClient.searchProcessDefinitions(searchOptions);\n        }\n    }\n\n    private boolean isFilteringOn(Map<String, String> filters, String... attributes) {\n        for (String attribute : attributes) {\n            if (!filters.containsKey(attribute)) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    private Long getApiId(Map<String, String> filters, String attribute) {\n        return APIID.makeAPIID(filters.get(attribute)).toLong();\n    }\n\n    private ItemSearchResult<ProcessItem> convertResult(int page, int nbResultsByPage,\n            final SearchResult<ProcessDeploymentInfo> result) {\n        return new ItemSearchResultConverter<>(page, nbResultsByPage, result, new ProcessItemConverter(\n                engineClient.getProcessApi())).toItemSearchResult();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/converter/ActivityAttributeConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.converter;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstanceSearchDescriptor;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ActivityItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.TaskItem;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\npublic class ActivityAttributeConverter implements AttributeConverter {\n\n    protected static final Map<String, String> mapping = new HashMap<>();\n\n    static {\n        mapping.put(ActivityItem.ATTRIBUTE_CASE_ID, ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID);\n        mapping.put(ActivityItem.ATTRIBUTE_NAME, ActivityInstanceSearchDescriptor.NAME);\n        mapping.put(ActivityItem.ATTRIBUTE_STATE, ActivityInstanceSearchDescriptor.STATE_NAME);\n        mapping.put(ActivityItem.ATTRIBUTE_PROCESS_ID, ActivityInstanceSearchDescriptor.PROCESS_DEFINITION_ID);\n        mapping.put(ActivityItem.ATTRIBUTE_ROOT_CASE_ID, ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID);\n        mapping.put(ActivityItem.ATTRIBUTE_PARENT_CASE_ID, ActivityInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID);\n        mapping.put(ActivityItem.ATTRIBUTE_TYPE, ActivityInstanceSearchDescriptor.ACTIVITY_TYPE);\n        mapping.put(ActivityItem.FILTER_SUPERVISOR_ID, ActivityInstanceSearchDescriptor.SUPERVISOR_ID);\n        mapping.put(TaskItem.ATTRIBUTE_LAST_UPDATE_DATE, FlowNodeInstanceSearchDescriptor.LAST_UPDATE_DATE);\n    }\n\n    @Override\n    public String convert(final String attribute) {\n        return mapping.get(attribute);\n    }\n\n    @Override\n    public Map<String, ItemAttribute.TYPE> getValueTypeMapping() {\n        return Collections.emptyMap();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/converter/AttributeConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.converter;\n\nimport java.util.Map;\n\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute.TYPE;\n\n/**\n * @author Colin PUY\n * @author Emmanuel Duchastenier\n */\npublic interface AttributeConverter {\n\n    String convert(String attribute);\n\n    /**\n     * return a map of attribute name to the type to convert the value into.\n     */\n    Map<String, TYPE> getValueTypeMapping();\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/converter/BooleanValueConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.converter;\n\n/**\n * @author Vincent Elcrin\n */\npublic class BooleanValueConverter implements ValueConverter<Boolean> {\n\n    @Override\n    public Boolean convert(String value) {\n        return Boolean.valueOf(value);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/converter/EmptyAttributeConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.converter;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * @author Colin PUY, Vincent Elcrin\n *         Empty converter, do nothing\n */\npublic class EmptyAttributeConverter implements AttributeConverter {\n\n    @Override\n    public String convert(String attribute) {\n        return attribute;\n    }\n\n    @Override\n    public Map<String, ItemAttribute.TYPE> getValueTypeMapping() {\n        return Collections.emptyMap();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/converter/ItemConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.converter;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\n\n/**\n * @author Vincent Elcrin\n */\npublic abstract class ItemConverter<I extends IItem, E extends Serializable> {\n\n    public abstract I convert(E engineItem);\n\n    public List<I> convert(List<E> profiles) {\n        ArrayList<I> profileItems = new ArrayList<>();\n        for (E profile : profiles) {\n            profileItems.add(convert(profile));\n        }\n        return profileItems;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/converter/ItemSearchResultConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.converter;\n\nimport java.io.Serializable;\n\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\n\n/**\n * @author Vincent Elcrin\n */\npublic class ItemSearchResultConverter<I extends IItem, E extends Serializable> {\n\n    private final int page;\n    private final SearchResult<E> result;\n    private final ItemConverter<I, E> converter;\n    private final long total;\n    private final int nbResultsByPage;\n\n    public ItemSearchResultConverter(int page, int nbResultsByPage, SearchResult<E> result,\n            ItemConverter<I, E> converter) {\n        this(page, nbResultsByPage, result, result.getCount(), converter);\n    }\n\n    public ItemSearchResultConverter(int page, int nbResultsByPage, SearchResult<E> result, long total,\n            ItemConverter<I, E> converter) {\n        this.page = page;\n        this.nbResultsByPage = nbResultsByPage;\n        this.result = result;\n        this.total = total;\n        this.converter = converter;\n    }\n\n    public ItemSearchResult<I> toItemSearchResult() {\n        return new ItemSearchResult<>(page, nbResultsByPage, total, converter.convert(result.getResult()));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/converter/LongValueConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.converter;\n\n/**\n * @author Vincent Elcrin\n */\npublic class LongValueConverter implements ValueConverter<Long> {\n\n    @Override\n    public Long convert(String value) {\n        return Long.valueOf(value);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/converter/StringValueConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.converter;\n\n/**\n * @author Vincent Elcrin\n */\npublic class StringValueConverter implements ValueConverter<String> {\n\n    @Override\n    public String convert(String value) {\n        return value;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/converter/ValueConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.converter;\n\nimport java.io.Serializable;\n\n/**\n * @author Vincent Elcrin\n */\npublic interface ValueConverter<V extends Serializable> {\n\n    V convert(String value);\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/filter/ActivityFilterCreator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.filter;\n\nimport java.io.Serializable;\n\nimport org.bonitasoft.web.rest.model.bpm.flownode.ActivityItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.FlowNodeItem;\nimport org.bonitasoft.web.rest.server.datastore.bpm.flownode.FlowNodeTypeConverter;\nimport org.bonitasoft.web.rest.server.datastore.converter.ActivityAttributeConverter;\nimport org.bonitasoft.web.rest.server.datastore.converter.EmptyAttributeConverter;\nimport org.bonitasoft.web.rest.server.datastore.converter.StringValueConverter;\nimport org.bonitasoft.web.rest.server.datastore.filter.Filter.Operator;\n\n/**\n * @author Florine Boudin\n */\npublic class ActivityFilterCreator extends GenericFilterCreator {\n\n    public ActivityFilterCreator() {\n        super(new EmptyAttributeConverter());\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see org.bonitasoft.web.rest.server.credentials.filter.FilterCreator#create(java.lang.String, java.lang.String)\n     */\n    @Override\n    public Filter<? extends Serializable> create(String attribute, String value) {\n        if (ActivityItem.ATTRIBUTE_TYPE.equals(attribute)) {\n            return new Filter<>(new Field(attribute, new ActivityAttributeConverter()),\n                    new Value<>(value, new FlowNodeTypeConverter()));\n        }\n        if (ActivityItem.FILTER_IS_FAILED.equals(attribute)) {\n            Operator operator = Boolean.valueOf(value) ? Operator.EQUAL : Operator.DIFFERENT_FROM;\n            return new Filter<>(new Field(ActivityItem.ATTRIBUTE_STATE, new ActivityAttributeConverter()),\n                    new Value<>(FlowNodeItem.VALUE_STATE_FAILED, new StringValueConverter()), operator);\n        }\n        return new GenericFilterCreator(new ActivityAttributeConverter()).create(attribute, value);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/filter/BooleanValue.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.filter;\n\nimport org.bonitasoft.web.rest.server.datastore.converter.BooleanValueConverter;\n\npublic class BooleanValue extends Value<Boolean> {\n\n    public BooleanValue(String value) {\n        super(value, new BooleanValueConverter());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/filter/Field.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.filter;\n\nimport org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter;\nimport org.bonitasoft.web.rest.server.datastore.converter.EmptyAttributeConverter;\n\n/**\n * @author Vincent Elcrin\n */\npublic class Field {\n\n    private final String field;\n\n    public Field(String attribute, AttributeConverter convert) {\n        field = convert.convert(attribute);\n    }\n\n    public Field(String attribute) {\n        this(attribute, new EmptyAttributeConverter());\n    }\n\n    @Override\n    public String toString() {\n        return field;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/filter/Filter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.filter;\n\nimport java.io.Serializable;\n\n/**\n * @author Vincent Elcrin\n */\npublic class Filter<V extends Serializable> {\n\n    public enum Operator {\n        EQUAL, DIFFERENT_FROM\n    }\n\n    private final Field field;\n\n    private final Value<V> value;\n\n    private Operator operator = Operator.EQUAL;\n\n    public Filter(Field field, Value<V> value) {\n        this.field = field;\n        this.value = value;\n    }\n\n    public Filter(Field field, Value<V> value, Operator operator) {\n        this(field, value);\n        this.operator = operator;\n    }\n\n    public String getField() {\n        return field.toString();\n    }\n\n    public V getValue() {\n        return value != null ? value.cast() : null;\n    }\n\n    public Operator getOperator() {\n        return operator;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/filter/FilterAccessor.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.filter;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.web.rest.server.datastore.converter.ValueConverter;\nimport org.bonitasoft.web.rest.server.framework.exception.APIFilterMandatoryException;\n\n/**\n * @author Vincent Elcrin\n */\npublic class FilterAccessor {\n\n    private final Map<String, String> filters;\n\n    public FilterAccessor(Map<String, String> filters) {\n        this.filters = filters;\n    }\n\n    /**\n     * @throws APIFilterMandatoryException\n     *         if the value of the filter is null\n     */\n    public String getMandatory(String filter) {\n        ensureFilterValue(filter);\n        return getFilters().get(filter);\n    }\n\n    /**\n     * @throws APIFilterMandatoryException\n     *         In case of any exception during conversion\n     */\n    public <S extends Serializable> S getMandatory(String filter, ValueConverter<S> converter) {\n        String value = getMandatory(filter);\n        try {\n            return converter.convert(value);\n        } catch (Exception e) {\n            throw new APIFilterMandatoryException(filter, e);\n        }\n    }\n\n    private Map<String, String> getFilters() {\n        return filters;\n    }\n\n    private void ensureFilterValue(String filter) {\n        if (getFilters() == null || !getFilters().containsKey(filter)) {\n            throw new APIFilterMandatoryException(filter);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/filter/FilterCreator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.filter;\n\nimport java.io.Serializable;\n\n/**\n * @author Vincent Elcrin\n */\npublic interface FilterCreator {\n\n    Filter<? extends Serializable> create(String attribute, String value);\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/filter/Filters.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.filter;\n\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.bonitasoft.web.rest.server.datastore.converter.StringValueConverter;\n\n/**\n * @author Vincent Elcrin\n */\npublic class Filters {\n\n    private final List<Filter<?>> filters = new ArrayList<>();\n\n    public Filters(final Map<String, String> filters, final FilterCreator filterCreator) {\n        if (filters != null) {\n            addFilters(filters, filterCreator);\n        }\n    }\n\n    /**\n     * Constructor that directly uses Engine filter keys, instead of having to transcode Web filter keys to Engine\n     * filter keys.\n     *\n     * @param filters the filter key, value map\n     */\n    public Filters(final Map<String, String> filters) {\n        if (filters != null) {\n            final Iterator<Entry<String, String>> it = filters.entrySet().iterator();\n            while (it.hasNext()) {\n                final Entry<String, String> filterEntry = it.next();\n                final Field field = new Field(filterEntry.getKey());\n                final Value<String> fieldValue = new Value<>(filterEntry.getValue(), new StringValueConverter());\n                final Filter<String> filter = new Filter<>(field, fieldValue);\n\n                this.filters.add(filter);\n            }\n        }\n    }\n\n    private void addFilters(final Map<String, String> filters, final FilterCreator filterCreator) {\n        final Iterator<Entry<String, String>> it = filters.entrySet().iterator();\n        while (it.hasNext()) {\n            addEntry(it.next(), filterCreator);\n        }\n    }\n\n    private void addEntry(final Entry<String, String> entry, final FilterCreator filterCreator) {\n        filters.add(createFilter(filterCreator, entry));\n    }\n\n    private Filter<?> createFilter(final FilterCreator filterCreator, final Entry<String, String> entry) {\n        return filterCreator.create(entry.getKey(), entry.getValue());\n    }\n\n    public List<Filter<?>> asList() {\n        return filters;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/filter/FormMappingTypeCreator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.filter;\n\nimport java.io.Serializable;\n\nimport org.bonitasoft.engine.form.FormMappingSearchDescriptor;\nimport org.bonitasoft.engine.form.FormMappingType;\nimport org.bonitasoft.web.rest.server.datastore.converter.EmptyAttributeConverter;\n\n/**\n * author Emmanuel Duchastenier\n */\npublic class FormMappingTypeCreator extends GenericFilterCreator {\n\n    public FormMappingTypeCreator() {\n        super(new EmptyAttributeConverter());\n    }\n\n    @Override\n    public Filter<? extends Serializable> create(String attribute, String value) {\n        if (FormMappingSearchDescriptor.TYPE.equals(attribute)) {\n            return new Filter<>(new Field(attribute), new Value<>(value, FormMappingType::valueOf));\n        }\n        return super.create(attribute, value);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/filter/GenericFilterCreator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.filter;\n\nimport java.io.Serializable;\n\nimport org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute.TYPE;\n\n/**\n * @author Vincent Elcrin\n * @author Emmanuel Duchastenier\n */\npublic class GenericFilterCreator implements FilterCreator {\n\n    protected final AttributeConverter fieldConverter;\n\n    public GenericFilterCreator(AttributeConverter fieldConverter) {\n        this.fieldConverter = fieldConverter;\n    }\n\n    @Override\n    public Filter<? extends Serializable> create(String attribute, String value) {\n        return new Filter<>(new Field(attribute, fieldConverter), getTypedValue(attribute, value));\n    }\n\n    private Value<? extends Serializable> getTypedValue(String attributeName, String attributeValue) {\n        if (fieldConverter.getValueTypeMapping().get(attributeName) == TYPE.BOOLEAN) {\n            return new BooleanValue(attributeValue);\n        }\n        return new StrValue(attributeValue);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/filter/LongValue.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.filter;\n\nimport org.bonitasoft.web.rest.server.datastore.converter.LongValueConverter;\n\n/**\n * @author Vincent Elcrin\n */\npublic class LongValue extends Value<Long> {\n\n    public LongValue(String value) {\n        super(value, new LongValueConverter());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/filter/StrValue.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.filter;\n\nimport org.bonitasoft.web.rest.server.datastore.converter.StringValueConverter;\n\n/**\n * @author Vincent Elcrin\n */\npublic class StrValue extends Value<String> {\n\n    public StrValue(String value) {\n        super(value, new StringValueConverter());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/filter/Value.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.filter;\n\nimport java.io.Serializable;\n\nimport org.bonitasoft.web.rest.server.datastore.converter.ValueConverter;\n\n/**\n * @author Vincent Elcrin\n */\npublic class Value<V extends Serializable> {\n\n    private final String value;\n\n    private final ValueConverter<V> converter;\n\n    public Value(String value, ValueConverter<V> converter) {\n        this.value = value;\n        this.converter = converter;\n    }\n\n    public V cast() {\n        return converter.convert(value);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/organization/Avatars.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.organization;\n\npublic interface Avatars {\n\n    String PATH = \"../API/avatars/\";\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/organization/ContactDataConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.organization;\n\nimport org.bonitasoft.engine.identity.ContactData;\nimport org.bonitasoft.web.rest.model.identity.AbstractContactDataItem;\nimport org.bonitasoft.web.rest.server.datastore.converter.ItemConverter;\n\n/**\n * @author Vincent Elcrin\n */\npublic abstract class ContactDataConverter<C extends AbstractContactDataItem>\n        extends ItemConverter<AbstractContactDataItem, ContactData> {\n\n    @Override\n    public C convert(ContactData item) {\n        if (item == null) {\n            return createContactDataItem();\n        }\n        C contactData = createContactDataItem();\n        contactData.setAddress(item.getAddress());\n        contactData.setEmail(item.getEmail());\n        contactData.setPhoneNumber(item.getPhoneNumber());\n        contactData.setMobileNumber(item.getMobileNumber());\n        contactData.setFaxNumber(item.getFaxNumber());\n        contactData.setBuilding(item.getBuilding());\n        contactData.setRoom(item.getRoom());\n        contactData.setZipCode(item.getZipCode());\n        contactData.setCity(item.getCity());\n        contactData.setState(item.getState());\n        contactData.setCountry(item.getCountry());\n        contactData.setWebsite(item.getWebsite());\n        setContactId(contactData);\n        return contactData;\n    }\n\n    public abstract C createContactDataItem();\n\n    public abstract void setContactId(C contactData);\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/organization/GroupCreatorConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.organization;\n\nimport static org.bonitasoft.web.toolkit.client.common.util.StringUtil.isBlank;\n\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.console.common.server.utils.IconDescriptor;\nimport org.bonitasoft.engine.identity.GroupCreator;\nimport org.bonitasoft.web.rest.model.identity.GroupItem;\nimport org.bonitasoft.web.rest.server.engineclient.GroupEngineClient;\n\n/**\n * @author Colin PUY\n */\npublic class GroupCreatorConverter {\n\n    private final GroupEngineClient groupEngineClient;\n\n    public GroupCreatorConverter(GroupEngineClient groupEngineClient) {\n        this.groupEngineClient = groupEngineClient;\n    }\n\n    public GroupCreator convert(GroupItem item) {\n        if (item == null) {\n            return null;\n        }\n\n        GroupCreator builder = new GroupCreator(item.getName());\n\n        if (!isBlank(item.getDescription())) {\n            builder.setDescription(item.getDescription());\n        }\n\n        if (!isBlank(item.getDisplayName())) {\n            builder.setDisplayName(item.getDisplayName());\n        }\n\n        if (!isBlank(item.getIcon())) {\n            IconDescriptor iconDescriptor = new BonitaHomeFolderAccessor().getIconFromFileSystem(item.getIcon());\n            builder.setIcon(iconDescriptor.getFilename(), iconDescriptor.getContent());\n        }\n\n        if (!isBlank(item.getParentGroupId())) {\n            String parentGroupPath = groupEngineClient.getPath(item.getParentGroupId());\n            builder.setParentPath(parentGroupPath);\n        }\n        return builder;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/organization/GroupDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.organization;\n\nimport static org.bonitasoft.web.toolkit.client.data.APIID.toLongList;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.identity.Group;\nimport org.bonitasoft.engine.identity.GroupCreator;\nimport org.bonitasoft.engine.identity.GroupSearchDescriptor;\nimport org.bonitasoft.engine.identity.GroupUpdater;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.identity.GroupItem;\nimport org.bonitasoft.web.rest.server.datastore.CommonDatastore;\nimport org.bonitasoft.web.rest.server.engineclient.EngineAPIAccessor;\nimport org.bonitasoft.web.rest.server.engineclient.EngineClientFactory;\nimport org.bonitasoft.web.rest.server.engineclient.GroupEngineClient;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasAdd;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasDelete;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasUpdate;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Nicolas Tith\n */\npublic class GroupDatastore extends CommonDatastore<GroupItem, Group> implements\n        DatastoreHasAdd<GroupItem>,\n        DatastoreHasUpdate<GroupItem>,\n        DatastoreHasGet<GroupItem>,\n        DatastoreHasSearch<GroupItem>, DatastoreHasDelete {\n\n    public GroupDatastore(final APISession engineSession) {\n        super(engineSession);\n    }\n\n    public GroupEngineClient getGroupEngineClient() {\n        return new EngineClientFactory(new EngineAPIAccessor(getEngineSession()))\n                .createGroupEngineClient();\n    }\n\n    @Override\n    public void delete(final List<APIID> ids) {\n        getGroupEngineClient().delete(toLongList(ids));\n    }\n\n    @Override\n    public ItemSearchResult<GroupItem> search(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n        try {\n            final SearchOptionsBuilder builder = SearchOptionsBuilderUtil.buildSearchOptions(page, resultsByPage,\n                    orders, search);\n\n            addStringFilterToSearchBuilder(filters, builder, GroupItem.ATTRIBUTE_NAME, GroupSearchDescriptor.NAME);\n            addStringFilterToSearchBuilder(filters, builder, GroupItem.ATTRIBUTE_DISPLAY_NAME,\n                    GroupSearchDescriptor.DISPLAY_NAME);\n            addStringFilterToSearchBuilder(filters, builder, GroupItem.ATTRIBUTE_PARENT_PATH,\n                    GroupSearchDescriptor.PARENT_PATH);\n\n            SearchResult<Group> engineSearchResults;\n            engineSearchResults = TenantAPIAccessor.getIdentityAPI(getEngineSession()).searchGroups(builder.done());\n\n            return new ItemSearchResult<>(page, resultsByPage, engineSearchResults.getCount(),\n                    new GroupItemConverter().convert(engineSearchResults.getResult()));\n\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @Override\n    public GroupItem get(final APIID id) {\n        Group result = getGroupEngineClient().get(id.toLong());\n        return new GroupItemConverter().convert(result);\n    }\n\n    @Override\n    public GroupItem update(final APIID id, final Map<String, String> attributes) {\n        GroupUpdater updater = new GroupUpdaterConverter(getGroupEngineClient()).convert(attributes);\n        Group group = getGroupEngineClient().update(id.toLong(), updater);\n        return new GroupItemConverter().convert(group);\n    }\n\n    @Override\n    public GroupItem add(final GroupItem group) {\n        GroupCreator creator = new GroupCreatorConverter(getGroupEngineClient()).convert(group);\n        Group result = getGroupEngineClient().create(creator);\n        return new GroupItemConverter().convert(result);\n    }\n\n    public Long getNumberOfUsers(final APIID groupId) {\n        try {\n            return TenantAPIAccessor.getIdentityAPI(getEngineSession()).getNumberOfUsersInGroup(groupId.toLong());\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @Override\n    protected GroupItem convertEngineToConsoleItem(final Group group) {\n        throw new RuntimeException(\"Unimplemented method\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/organization/GroupItemConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.organization;\n\nimport org.bonitasoft.engine.identity.Group;\nimport org.bonitasoft.web.rest.model.identity.GroupItem;\nimport org.bonitasoft.web.rest.server.datastore.converter.ItemConverter;\n\npublic class GroupItemConverter extends ItemConverter<GroupItem, Group> {\n\n    @Override\n    public GroupItem convert(Group group) {\n        GroupItem groupItem = new GroupItem();\n        groupItem.setCreatedByUserId(group.getCreatedBy());\n        groupItem.setCreationDate(group.getCreationDate());\n        groupItem.setDescription(group.getDescription());\n        groupItem.setDisplayName(group.getDisplayName());\n        groupItem.setIcon(group.getIconId() == null ? \"\" : Avatars.PATH + group.getIconId());\n        groupItem.setId(group.getId());\n        groupItem.setLastUpdateDate(group.getLastUpdate());\n        groupItem.setName(group.getName());\n        groupItem.setParentPath(group.getParentPath());\n        groupItem.setPath(group.getPath());\n        return groupItem;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/organization/GroupUpdaterConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.organization;\n\nimport java.util.Map;\n\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.console.common.server.utils.IconDescriptor;\nimport org.bonitasoft.engine.identity.GroupUpdater;\nimport org.bonitasoft.web.rest.model.identity.GroupItem;\nimport org.bonitasoft.web.rest.server.engineclient.GroupEngineClient;\nimport org.bonitasoft.web.toolkit.client.common.util.MapUtil;\n\n/**\n * @author Colin PUY\n */\npublic class GroupUpdaterConverter {\n\n    private final GroupEngineClient groupEngineClient;\n\n    public GroupUpdaterConverter(GroupEngineClient groupEngineClient) {\n        this.groupEngineClient = groupEngineClient;\n    }\n\n    public GroupUpdater convert(Map<String, String> attributes) {\n        GroupUpdater updater = new GroupUpdater();\n        if (attributes.containsKey(GroupItem.ATTRIBUTE_DESCRIPTION)) {\n            updater.updateDescription(attributes.get(GroupItem.ATTRIBUTE_DESCRIPTION));\n        }\n        if (!MapUtil.isBlank(attributes, GroupItem.ATTRIBUTE_ICON)) {\n            IconDescriptor iconDescriptor = getBonitaHomeFolderAccessor()\n                    .getIconFromFileSystem(attributes.get(GroupItem.ATTRIBUTE_ICON));\n            updater.updateIcon(iconDescriptor.getFilename(), iconDescriptor.getContent());\n        }\n        if (!MapUtil.isBlank(attributes, GroupItem.ATTRIBUTE_NAME)) {\n            updater.updateName(attributes.get(GroupItem.ATTRIBUTE_NAME));\n        }\n        if (attributes.containsKey(GroupItem.ATTRIBUTE_DISPLAY_NAME)) {\n            updater.updateDisplayName(attributes.get(GroupItem.ATTRIBUTE_DISPLAY_NAME));\n        }\n        if (attributes.containsKey(GroupItem.ATTRIBUTE_PARENT_GROUP_ID)) {\n            String parentGroupPath = getParentGroupPath(attributes.get(GroupItem.ATTRIBUTE_PARENT_GROUP_ID));\n            updater.updateParentPath(parentGroupPath);\n        }\n        return updater;\n    }\n\n    BonitaHomeFolderAccessor getBonitaHomeFolderAccessor() {\n        return new BonitaHomeFolderAccessor();\n    }\n\n    private String getParentGroupPath(String groupId) {\n        if (groupId.isEmpty()) {\n            return \"\";\n        } else {\n            return groupEngineClient.getPath(groupId);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/organization/MembershipDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.organization;\n\nimport static org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil.computeIndex;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.IdentityAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.exception.*;\nimport org.bonitasoft.engine.identity.UserMembership;\nimport org.bonitasoft.engine.identity.UserMembershipCriterion;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.bonitasoft.web.rest.model.identity.MembershipItem;\nimport org.bonitasoft.web.rest.server.datastore.CommonDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasAdd;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasDelete;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIForbiddenException;\nimport org.bonitasoft.web.toolkit.client.common.i18n.T_;\nimport org.bonitasoft.web.toolkit.client.common.util.MapUtil;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Séverin Moussel\n */\npublic class MembershipDatastore extends CommonDatastore<MembershipItem, UserMembership> implements\n        DatastoreHasAdd<MembershipItem>,\n        DatastoreHasSearch<MembershipItem>,\n        DatastoreHasDelete {\n\n    public MembershipDatastore(final APISession engineSession) {\n        super(engineSession);\n    }\n\n    /**\n     * @return\n     * @throws InvalidSessionException\n     * @throws BonitaHomeNotSetException\n     * @throws ServerAPIException\n     * @throws UnknownAPITypeException\n     */\n    private IdentityAPI getIdentityAPI()\n            throws InvalidSessionException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return TenantAPIAccessor.getIdentityAPI(getEngineSession());\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // CONVERT\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    protected MembershipItem convertEngineToConsoleItem(final UserMembership item) {\n        final MembershipItem result = new MembershipItem();\n\n        result.setUserId(item.getUserId());\n        result.setRoleId(item.getRoleId());\n        result.setGroupId(item.getGroupId());\n        result.setAssignedByUserId(item.getAssignedBy());\n        result.setAssignedDate(item.getAssignedDate());\n\n        return result;\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // CRUDS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public void delete(final List<APIID> ids) {\n        try {\n            for (final APIID id : ids) {\n                getIdentityAPI().deleteUserMembership(id.getPartAsLong(0), id.getPartAsLong(1), id.getPartAsLong(2));\n            }\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @Override\n    public ItemSearchResult<MembershipItem> search(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n        try {\n\n            Long userId = MapUtil.getValueAsLong(filters, MembershipItem.ATTRIBUTE_USER_ID);\n            // Get results\n            final List<UserMembership> engineItems = getIdentityAPI().getUserMemberships(\n                    userId, computeIndex(page, resultsByPage), resultsByPage, UserMembershipCriterion.valueOf(orders));\n\n            // Convert results\n            final List<MembershipItem> consoleSearchResults = convertEngineToConsoleItemsList(\n                    engineItems);\n\n            // Get total results\n            final long total = getIdentityAPI()\n                    .getNumberOfUserMemberships(MapUtil.getValueAsLong(filters, MembershipItem.ATTRIBUTE_USER_ID));\n\n            // Return search object\n            return new ItemSearchResult<>(\n                    page,\n                    resultsByPage,\n                    total,\n                    consoleSearchResults);\n\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @Override\n    public MembershipItem add(final MembershipItem item) {\n        try {\n            return convertEngineToConsoleItem(getIdentityAPI()\n                    .addUserMembership(item.getUserId().toLong(), item.getGroupId().toLong(),\n                            item.getRoleId().toLong()));\n        } catch (AlreadyExistsException e) {\n            throw new APIForbiddenException(new T_(\"This membership is already added to user\"), e);\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/organization/PersonalContactDataDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.organization;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.NotFoundException;\nimport org.bonitasoft.engine.identity.ContactData;\nimport org.bonitasoft.engine.identity.ContactDataUpdater;\nimport org.bonitasoft.engine.identity.UserUpdater;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.identity.PersonalContactDataItem;\nimport org.bonitasoft.web.rest.server.datastore.CommonDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasAdd;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasUpdate;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Paul AMAR\n */\npublic class PersonalContactDataDatastore extends CommonDatastore<PersonalContactDataItem, ContactData> implements\n        DatastoreHasGet<PersonalContactDataItem>, DatastoreHasUpdate<PersonalContactDataItem>,\n        DatastoreHasAdd<PersonalContactDataItem> {\n\n    public PersonalContactDataDatastore(final APISession engineSession) {\n        super(engineSession);\n    }\n\n    @Override\n    public PersonalContactDataItem get(final APIID id) {\n        try {\n            // Hard-coded at true because we want to retrieve ContactData\n            final ContactData result = TenantAPIAccessor.getIdentityAPI(getEngineSession())\n                    .getUserContactData(id.toLong(), true);\n            return createContactDataItemConverter(id).convert(result);\n        } catch (final NotFoundException e) {\n            return null;\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @Override\n    public PersonalContactDataItem update(final APIID id, final Map<String, String> attributes) {\n        try {\n            final ContactDataUpdater personalDataUpdater = new ContactDataUpdater();\n\n            if (attributes.containsKey(PersonalContactDataItem.ATTRIBUTE_EMAIL)) {\n                personalDataUpdater.setEmail(attributes.get(PersonalContactDataItem.ATTRIBUTE_EMAIL));\n            }\n            if (attributes.containsKey(PersonalContactDataItem.ATTRIBUTE_PHONE)) {\n                personalDataUpdater.setPhoneNumber(attributes.get(PersonalContactDataItem.ATTRIBUTE_PHONE));\n            }\n            if (attributes.containsKey(PersonalContactDataItem.ATTRIBUTE_MOBILE)) {\n                personalDataUpdater.setMobileNumber(attributes.get(PersonalContactDataItem.ATTRIBUTE_MOBILE));\n            }\n            if (attributes.containsKey(PersonalContactDataItem.ATTRIBUTE_FAX)) {\n                personalDataUpdater.setFaxNumber(attributes.get(PersonalContactDataItem.ATTRIBUTE_FAX));\n            }\n            if (attributes.containsKey(PersonalContactDataItem.ATTRIBUTE_BUILDING)) {\n                personalDataUpdater.setBuilding(attributes.get(PersonalContactDataItem.ATTRIBUTE_BUILDING));\n            }\n            if (attributes.containsKey(PersonalContactDataItem.ATTRIBUTE_ROOM)) {\n                personalDataUpdater.setRoom(attributes.get(PersonalContactDataItem.ATTRIBUTE_ROOM));\n            }\n            if (attributes.containsKey(PersonalContactDataItem.ATTRIBUTE_ADDRESS)) {\n                personalDataUpdater.setAddress(attributes.get(PersonalContactDataItem.ATTRIBUTE_ADDRESS));\n            }\n            if (attributes.containsKey(PersonalContactDataItem.ATTRIBUTE_ZIPCODE)) {\n                personalDataUpdater.setZipCode(attributes.get(PersonalContactDataItem.ATTRIBUTE_ZIPCODE));\n            }\n            if (attributes.containsKey(PersonalContactDataItem.ATTRIBUTE_CITY)) {\n                personalDataUpdater.setCity(attributes.get(PersonalContactDataItem.ATTRIBUTE_CITY));\n            }\n            if (attributes.containsKey(PersonalContactDataItem.ATTRIBUTE_STATE)) {\n                personalDataUpdater.setState(attributes.get(PersonalContactDataItem.ATTRIBUTE_STATE));\n            }\n            if (attributes.containsKey(PersonalContactDataItem.ATTRIBUTE_COUNTRY)) {\n                personalDataUpdater.setCountry(attributes.get(PersonalContactDataItem.ATTRIBUTE_COUNTRY));\n            }\n            if (attributes.containsKey(PersonalContactDataItem.ATTRIBUTE_WEBSITE)) {\n                personalDataUpdater.setWebsite(attributes.get(PersonalContactDataItem.ATTRIBUTE_WEBSITE));\n            }\n\n            UserUpdater userUpdater = new UserUpdater()\n                    .setPersonalContactData(personalDataUpdater)\n                    // TODO remove once handle by engine\n                    .setProfessionalContactData(new ContactDataUpdater());\n            TenantAPIAccessor.getIdentityAPI(getEngineSession()).updateUser(id.toLong(), userUpdater);\n            return get(id);\n\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @Override\n    public PersonalContactDataItem add(final PersonalContactDataItem item) {\n        // get the user id\n        final APIID idUser = item.getId();\n\n        final Map<String, String> attributes = item.getAttributes();\n        return update(idUser, attributes);\n\n    }\n\n    @Override\n    protected PersonalContactDataItem convertEngineToConsoleItem(final ContactData item) {\n        throw new RuntimeException(\"Use ContactDataConverter instead!\");\n    }\n\n    private ContactDataConverter<PersonalContactDataItem> createContactDataItemConverter(final APIID id) {\n        return new ContactDataConverter<>() {\n\n            @Override\n            public PersonalContactDataItem createContactDataItem() {\n                return new PersonalContactDataItem();\n            }\n\n            @Override\n            public void setContactId(PersonalContactDataItem contactData) {\n                contactData.setId(id);\n            }\n\n        };\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/organization/ProfessionalContactDataDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.organization;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.NotFoundException;\nimport org.bonitasoft.engine.identity.ContactData;\nimport org.bonitasoft.engine.identity.ContactDataUpdater;\nimport org.bonitasoft.engine.identity.UserUpdater;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.identity.ProfessionalContactDataItem;\nimport org.bonitasoft.web.rest.server.datastore.CommonDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasAdd;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasUpdate;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Paul AMAR\n */\npublic class ProfessionalContactDataDatastore extends CommonDatastore<ProfessionalContactDataItem, ContactData>\n        implements\n        DatastoreHasGet<ProfessionalContactDataItem>, DatastoreHasUpdate<ProfessionalContactDataItem>,\n        DatastoreHasAdd<ProfessionalContactDataItem> {\n\n    public ProfessionalContactDataDatastore(final APISession engineSession) {\n        super(engineSession);\n    }\n\n    @Override\n    public ProfessionalContactDataItem get(final APIID id) {\n        try {\n            // Hard-coded at true because we want to retrieve ContactData\n            final ContactData result = TenantAPIAccessor.getIdentityAPI(getEngineSession())\n                    .getUserContactData(id.toLong(), false);\n            return createContactDataItemConverter(id).convert(result);\n        } catch (final NotFoundException e) {\n            return null;\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @Override\n    public ProfessionalContactDataItem update(final APIID id, final Map<String, String> attributes) {\n        try {\n            final ContactDataUpdater professionalDataUpdater = new ContactDataUpdater();\n\n            if (attributes.containsKey(ProfessionalContactDataItem.ATTRIBUTE_EMAIL)) {\n                professionalDataUpdater.setEmail(attributes.get(ProfessionalContactDataItem.ATTRIBUTE_EMAIL));\n            }\n            if (attributes.containsKey(ProfessionalContactDataItem.ATTRIBUTE_PHONE)) {\n                professionalDataUpdater.setPhoneNumber(attributes.get(ProfessionalContactDataItem.ATTRIBUTE_PHONE));\n            }\n            if (attributes.containsKey(ProfessionalContactDataItem.ATTRIBUTE_MOBILE)) {\n                professionalDataUpdater.setMobileNumber(attributes.get(ProfessionalContactDataItem.ATTRIBUTE_MOBILE));\n            }\n            if (attributes.containsKey(ProfessionalContactDataItem.ATTRIBUTE_FAX)) {\n                professionalDataUpdater.setFaxNumber(attributes.get(ProfessionalContactDataItem.ATTRIBUTE_FAX));\n            }\n            if (attributes.containsKey(ProfessionalContactDataItem.ATTRIBUTE_BUILDING)) {\n                professionalDataUpdater.setBuilding(attributes.get(ProfessionalContactDataItem.ATTRIBUTE_BUILDING));\n            }\n            if (attributes.containsKey(ProfessionalContactDataItem.ATTRIBUTE_ROOM)) {\n                professionalDataUpdater.setRoom(attributes.get(ProfessionalContactDataItem.ATTRIBUTE_ROOM));\n            }\n            if (attributes.containsKey(ProfessionalContactDataItem.ATTRIBUTE_ADDRESS)) {\n                professionalDataUpdater.setAddress(attributes.get(ProfessionalContactDataItem.ATTRIBUTE_ADDRESS));\n            }\n            if (attributes.containsKey(ProfessionalContactDataItem.ATTRIBUTE_ZIPCODE)) {\n                professionalDataUpdater.setZipCode(attributes.get(ProfessionalContactDataItem.ATTRIBUTE_ZIPCODE));\n            }\n            if (attributes.containsKey(ProfessionalContactDataItem.ATTRIBUTE_CITY)) {\n                professionalDataUpdater.setCity(attributes.get(ProfessionalContactDataItem.ATTRIBUTE_CITY));\n            }\n            if (attributes.containsKey(ProfessionalContactDataItem.ATTRIBUTE_STATE)) {\n                professionalDataUpdater.setState(attributes.get(ProfessionalContactDataItem.ATTRIBUTE_STATE));\n            }\n            if (attributes.containsKey(ProfessionalContactDataItem.ATTRIBUTE_COUNTRY)) {\n                professionalDataUpdater.setCountry(attributes.get(ProfessionalContactDataItem.ATTRIBUTE_COUNTRY));\n            }\n            if (attributes.containsKey(ProfessionalContactDataItem.ATTRIBUTE_WEBSITE)) {\n                professionalDataUpdater.setWebsite(attributes.get(ProfessionalContactDataItem.ATTRIBUTE_WEBSITE));\n            }\n\n            UserUpdater userUpdater = new UserUpdater()\n                    .setProfessionalContactData(professionalDataUpdater)\n                    // TODO remove once handle by engine\n                    .setPersonalContactData(new ContactDataUpdater());\n            TenantAPIAccessor.getIdentityAPI(getEngineSession()).updateUser(id.toLong(), userUpdater);\n            return get(id);\n\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @Override\n    public ProfessionalContactDataItem add(final ProfessionalContactDataItem item) {\n        return update(item.getId(), item.getAttributes());\n\n    }\n\n    @Override\n    protected ProfessionalContactDataItem convertEngineToConsoleItem(ContactData item) {\n        throw new RuntimeException(\"Use ContactDataConverter instead!\");\n    }\n\n    private ContactDataConverter<ProfessionalContactDataItem> createContactDataItemConverter(final APIID id) {\n        return new ContactDataConverter<>() {\n\n            @Override\n            public ProfessionalContactDataItem createContactDataItem() {\n                return new ProfessionalContactDataItem();\n            }\n\n            @Override\n            public void setContactId(ProfessionalContactDataItem contactData) {\n                contactData.setId(id);\n            }\n\n        };\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/organization/RoleDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.organization;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.console.common.server.utils.IconDescriptor;\nimport org.bonitasoft.engine.api.IdentityAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.exception.*;\nimport org.bonitasoft.engine.identity.*;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.bonitasoft.web.rest.model.identity.RoleItem;\nimport org.bonitasoft.web.rest.server.datastore.CommonDatastore;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasAdd;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasDelete;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasUpdate;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIForbiddenException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APINotFoundException;\nimport org.bonitasoft.web.toolkit.client.common.i18n.T_;\nimport org.bonitasoft.web.toolkit.client.common.texttemplate.Arg;\nimport org.bonitasoft.web.toolkit.client.common.util.MapUtil;\nimport org.bonitasoft.web.toolkit.client.common.util.StringUtil;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Séverin Moussel\n */\npublic class RoleDatastore extends CommonDatastore<RoleItem, Role> implements\n        DatastoreHasGet<RoleItem>,\n        DatastoreHasSearch<RoleItem>,\n        DatastoreHasAdd<RoleItem>,\n        DatastoreHasUpdate<RoleItem>,\n        DatastoreHasDelete {\n\n    public RoleDatastore(final APISession engineSession) {\n        super(engineSession);\n    }\n\n    @Override\n    public void delete(final List<APIID> ids) {\n        try {\n            final List<Long> longIds = new ArrayList<>();\n            for (final APIID id : ids) {\n                longIds.add(id.toLong());\n            }\n\n            getIdentityAPI().deleteRoles(longIds);\n\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @Override\n    public RoleItem update(final APIID id, final Map<String, String> attributes) {\n        try {\n            final RoleUpdater updater = new RoleUpdater();\n\n            if (attributes.containsKey(RoleItem.ATTRIBUTE_NAME)) {\n                updater.setName(attributes.get(RoleItem.ATTRIBUTE_NAME));\n            }\n            if (attributes.containsKey(RoleItem.ATTRIBUTE_DISPLAY_NAME)) {\n                updater.setDisplayName(attributes.get(RoleItem.ATTRIBUTE_DISPLAY_NAME));\n            }\n            if (attributes.containsKey(RoleItem.ATTRIBUTE_DESCRIPTION)) {\n                updater.setDescription(attributes.get(RoleItem.ATTRIBUTE_DESCRIPTION));\n            }\n            if (!MapUtil.isBlank(attributes, RoleItem.ATTRIBUTE_ICON)) {\n                IconDescriptor iconDescriptor = getBonitaHomeFolderAccessor()\n                        .getIconFromFileSystem(attributes.get(RoleItem.ATTRIBUTE_ICON));\n                updater.setIcon(iconDescriptor.getFilename(), iconDescriptor.getContent());\n            }\n\n            return convertEngineToConsoleItem(getIdentityAPI().updateRole(id.toLong(), updater));\n        } catch (final RoleNotFoundException e) {\n            throw new APINotFoundException(new T_(\"Unable to find role %roleId%\", new Arg(\"roleId\", id)));\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n\n    }\n\n    @Override\n    public RoleItem add(final RoleItem role) {\n        try {\n            final RoleCreator creator = new RoleCreator(role.getName());\n\n            if (!StringUtil.isBlank(role.getDisplayName())) {\n                creator.setDisplayName(role.getDisplayName());\n            }\n            if (!StringUtil.isBlank(role.getDescription())) {\n                creator.setDescription(role.getDescription());\n            }\n            if (!StringUtil.isBlank(role.getIcon())) {\n                IconDescriptor iconDescriptor = getBonitaHomeFolderAccessor().getIconFromFileSystem(role.getIcon());\n                creator.setIcon(iconDescriptor.getFilename(), iconDescriptor.getContent());\n            }\n\n            return convertEngineToConsoleItem(getIdentityAPI().createRole(creator));\n        } catch (AlreadyExistsException e) {\n            throw new APIForbiddenException(\n                    new T_(\"Can't create role. Role '%roleName%' already exists\", new Arg(\"roleName\", role.getName())),\n                    e);\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n\n    }\n\n    BonitaHomeFolderAccessor getBonitaHomeFolderAccessor() {\n        return new BonitaHomeFolderAccessor();\n    }\n\n    @Override\n    public ItemSearchResult<RoleItem> search(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n        try {\n            final SearchOptionsBuilder builder = SearchOptionsBuilderUtil.buildSearchOptions(page, resultsByPage,\n                    orders, search);\n\n            addStringFilterToSearchBuilder(filters, builder, RoleItem.ATTRIBUTE_NAME, RoleSearchDescriptor.NAME);\n            addStringFilterToSearchBuilder(filters, builder, RoleItem.ATTRIBUTE_DISPLAY_NAME,\n                    RoleSearchDescriptor.DISPLAY_NAME);\n\n            final SearchResult<Role> engineSearchResults = getIdentityAPI().searchRoles(builder.done());\n            final List<RoleItem> consoleSearchResults = new ArrayList<>();\n            for (final Role engineItem : engineSearchResults.getResult()) {\n                consoleSearchResults.add(convertEngineToConsoleItem(engineItem));\n            }\n\n            return new ItemSearchResult<>(page, resultsByPage, engineSearchResults.getCount(), consoleSearchResults);\n\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    @Override\n    public RoleItem get(final APIID id) {\n        try {\n            return convertEngineToConsoleItem(getIdentityAPI().getRole(id.toLong()));\n        } catch (final RoleNotFoundException e) {\n            throw new APINotFoundException(new T_(\"Unable to find role %roleId%\", new Arg(\"roleId\", id)));\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    public Long getNumberOfUsers(final APIID roleId) {\n        try {\n            return getIdentityAPI().getNumberOfUsersInRole(roleId.toLong());\n        } catch (final BonitaException e) {\n            throw new APIException(e);\n        }\n    }\n\n    IdentityAPI getIdentityAPI()\n            throws InvalidSessionException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return TenantAPIAccessor.getIdentityAPI(getEngineSession());\n    }\n\n    @Override\n    protected RoleItem convertEngineToConsoleItem(final Role item) {\n        final RoleItem roleItem = new RoleItem();\n        roleItem.setId(item.getId());\n        roleItem.setName(item.getName());\n        roleItem.setDisplayName(item.getDisplayName());\n        roleItem.setDescription(item.getDescription());\n        roleItem.setIcon(item.getIconId() == null ? \"\" : Avatars.PATH + item.getIconId());\n        roleItem.setCreatedByUserId(item.getCreatedBy());\n        roleItem.setCreationDate(item.getCreationDate());\n        roleItem.setLastUpdateDate(item.getLastUpdate());\n        return roleItem;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/organization/UserCreatorConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.organization;\n\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.console.common.server.utils.IconDescriptor;\nimport org.bonitasoft.engine.identity.UserCreator;\nimport org.bonitasoft.web.rest.model.identity.UserItem;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\npublic class UserCreatorConverter {\n\n    public UserCreator convert(UserItem user) {\n        if (user == null) {\n            return null;\n        }\n\n        final UserCreator userCreator = new UserCreator(user.getUserName(), user.getPassword())\n                .setFirstName(user.getFirstName())\n                .setLastName(user.getLastName())\n                .setTitle(user.getTitle())\n                .setJobTitle(user.getJobTitle())\n                .setEnabled(user.isEnabled());\n\n        if (user.getIcon() != null && !user.getIcon().isEmpty()) {\n            IconDescriptor iconDescriptor = new BonitaHomeFolderAccessor().getIconFromFileSystem(user.getIcon());\n            userCreator.setIcon(iconDescriptor.getFilename(), iconDescriptor.getContent());\n        }\n\n        final APIID managerId = user.getManagerId();\n        if (managerId != null && managerId.isValidLongID()) {\n            userCreator.setManagerUserId(managerId.toLong());\n        }\n        return userCreator;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/organization/UserDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.organization;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.identity.UserCreator;\nimport org.bonitasoft.engine.identity.UserUpdater;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.identity.UserItem;\nimport org.bonitasoft.web.rest.server.datastore.CommonDatastore;\nimport org.bonitasoft.web.rest.server.datastore.filter.Filters;\nimport org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator;\nimport org.bonitasoft.web.rest.server.datastore.utils.Sorts;\nimport org.bonitasoft.web.rest.server.engineclient.EngineAPIAccessor;\nimport org.bonitasoft.web.rest.server.engineclient.EngineClientFactory;\nimport org.bonitasoft.web.rest.server.engineclient.ProcessEngineClient;\nimport org.bonitasoft.web.rest.server.engineclient.UserEngineClient;\nimport org.bonitasoft.web.rest.server.framework.api.*;\nimport org.bonitasoft.web.rest.server.framework.exception.APIAttributeException;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Séverin Moussel\n */\npublic class UserDatastore extends CommonDatastore<UserItem, User>\n        implements DatastoreHasAdd<UserItem>,\n        DatastoreHasGet<UserItem>,\n        DatastoreHasSearch<UserItem>,\n        DatastoreHasUpdate<UserItem>,\n        DatastoreHasDelete {\n\n    protected EngineClientFactory engineClientFactory;\n\n    protected UserItemConverter userItemConverter;\n\n    public UserDatastore(final APISession engineSession) {\n        super(engineSession);\n        userItemConverter = new UserItemConverter();\n        engineClientFactory = new EngineClientFactory(new EngineAPIAccessor(engineSession));\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // C.R.U.D.\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public UserItem add(final UserItem user) {\n        UserCreator userCreator = new UserCreatorConverter().convert(user);\n        User createdUser = getUserEngineClient().create(userCreator);\n        return userItemConverter.convert(createdUser);\n    }\n\n    public UserItem update(final APIID id, final Map<String, String> attributes) {\n        UserUpdater userUpdater = new UserUpdaterConverter().convert(attributes, getBonitaHomeFolderAccessor());\n        User user = getUserEngineClient().update(id.toLong(), userUpdater);\n        return userItemConverter.convert(user);\n    }\n\n    BonitaHomeFolderAccessor getBonitaHomeFolderAccessor() {\n        return new BonitaHomeFolderAccessor();\n    }\n\n    @Override\n    public UserItem get(final APIID id) {\n        User user = getUserEngineClient().get(id.toLong());\n        return userItemConverter.convert(user);\n    }\n\n    /**\n     * Search for users\n     *\n     * @param page\n     *        The page to display\n     * @param resultsByPage\n     *        The number of results by page\n     * @param search\n     *        Search terms\n     * @param filters\n     *        The filters to doAuthorize. There will be an AND operand between filters.\n     * @param orders\n     *        The order to doAuthorize to the search\n     * @return This method returns an ItemSearch result containing the returned data and information about the total\n     *         possible results.\n     */\n    @Override\n    public ItemSearchResult<UserItem> search(final int page, final int resultsByPage, final String search,\n            final String orders, Map<String, String> filters) {\n\n        if (filters.containsKey(UserItem.FILTER_PROCESS_ID)) {\n            String processId = filters.get(UserItem.FILTER_PROCESS_ID);\n            filters.remove(UserItem.FILTER_PROCESS_ID);\n            return searchUsersWhoCanStartProcess(processId, page, resultsByPage, search, filters, orders);\n        } else if (filters.containsKey(UserItem.FILTER_HUMAN_TASK_ID)) {\n            String taskId = filters.get(UserItem.FILTER_HUMAN_TASK_ID);\n            filters.remove(UserItem.FILTER_HUMAN_TASK_ID);\n            return searchUsersWhoCanPerformTask(taskId, page, resultsByPage, search, filters, orders);\n        } else {\n            return searchUsers(page, resultsByPage, search, filters, orders);\n        }\n\n    }\n\n    private ItemSearchResult<UserItem> searchUsers(final int page, final int resultsByPage, final String search,\n            final Map<String, String> filters, final String orders) {\n\n        SearchOptionsCreator searchOptionsCreator = buildSearchOptionCreator(page,\n                resultsByPage, search, filters, orders);\n\n        SearchResult<User> engineSearchResults = getUserEngineClient().search(searchOptionsCreator.create());\n\n        return new ItemSearchResult<>(page, resultsByPage, engineSearchResults.getCount(),\n                userItemConverter.convert(engineSearchResults.getResult()));\n\n    }\n\n    protected ItemSearchResult<UserItem> searchUsersWhoCanStartProcess(final String processId, final int page,\n            final int resultsByPage, final String search,\n            final Map<String, String> filters, final String orders) {\n\n        SearchOptionsCreator searchOptionsCreator = buildSearchOptionCreator(page,\n                resultsByPage, search, filters, orders);\n\n        SearchResult<User> engineSearchResults;\n        try {\n            engineSearchResults = getProcessEngineClient().getProcessApi().searchUsersWhoCanStartProcessDefinition(\n                    Long.valueOf(processId),\n                    searchOptionsCreator.create());\n        } catch (NumberFormatException e) {\n            throw new APIAttributeException(UserItem.FILTER_PROCESS_ID,\n                    \"Cannot convert process id: \" + processId + \" into long.\");\n        } catch (SearchException e) {\n            throw new APIException(e);\n        }\n\n        return new ItemSearchResult<>(page, resultsByPage, engineSearchResults.getCount(),\n                userItemConverter.convert(engineSearchResults.getResult()));\n\n    }\n\n    protected ItemSearchResult<UserItem> searchUsersWhoCanPerformTask(final String taskId, final int page,\n            final int resultsByPage, final String search,\n            final Map<String, String> filters, final String orders) {\n\n        SearchResult<User> engineSearchResults;\n        try {\n            SearchOptionsCreator searchOptionsCreator = buildSearchOptionCreator(page,\n                    resultsByPage, search, filters, orders);\n            engineSearchResults = getProcessEngineClient().getProcessApi().searchUsersWhoCanExecutePendingHumanTask(\n                    Long.valueOf(taskId),\n                    searchOptionsCreator.create());\n\n        } catch (NumberFormatException e) {\n            throw new APIAttributeException(UserItem.FILTER_HUMAN_TASK_ID,\n                    \"Cannot convert human task id: \" + taskId + \" into long.\");\n        }\n\n        return new ItemSearchResult<>(page, resultsByPage, engineSearchResults.getCount(),\n                userItemConverter.convert(engineSearchResults.getResult()));\n\n    }\n\n    protected SearchOptionsCreator buildSearchOptionCreator(final int page,\n            final int resultsByPage, final String search,\n            final Map<String, String> filters, final String orders) {\n        return new SearchOptionsCreator(page, resultsByPage, search,\n                new Sorts(orders, new UserSearchAttributeConverter()),\n                new Filters(filters, new UserFilterCreator(new UserSearchAttributeConverter())));\n    }\n\n    /**\n     * Delete users\n     *\n     * @param ids\n     */\n    public void delete(final List<APIID> ids) {\n        getUserEngineClient().delete(APIID.toLongList(ids));\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // CONVERTS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // protected for tests\n    UserEngineClient getUserEngineClient() {\n        return engineClientFactory.createUserEngineClient();\n    }\n\n    ProcessEngineClient getProcessEngineClient() {\n        return engineClientFactory.createProcessEngineClient();\n    }\n\n    @Override\n    protected UserItem convertEngineToConsoleItem(final User user) {\n        throw new RuntimeException(\"Unimplemented method\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/organization/UserFilterCreator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.organization;\n\nimport org.bonitasoft.web.rest.server.datastore.filter.GenericFilterCreator;\n\nclass UserFilterCreator extends GenericFilterCreator {\n\n    UserFilterCreator(UserSearchAttributeConverter converter) {\n        super(converter);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/organization/UserItemConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.organization;\n\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.web.rest.model.identity.UserItem;\nimport org.bonitasoft.web.rest.server.datastore.converter.ItemConverter;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\npublic class UserItemConverter extends ItemConverter<UserItem, User> {\n\n    @Override\n    public UserItem convert(User user) {\n        if (user == null) {\n            return null;\n        }\n\n        final UserItem result = new UserItem();\n        result.setId(APIID.makeAPIID(user.getId()));\n        result.setFirstName(user.getFirstName());\n        result.setLastName(user.getLastName());\n        result.setPassword(null);\n        result.setUserName(user.getUserName());\n        result.setManagerId(user.getManagerUserId());\n        result.setEnabled(user.isEnabled());\n\n        // Add default icon if icon if empty\n        if (user.getIconId() != null) {\n            result.setIcon(Avatars.PATH + user.getIconId());\n        } else {\n            result.setIcon(UserItem.DEFAULT_USER_ICON);\n        }\n\n        result.setCreationDate(user.getCreationDate());\n        result.setCreatedByUserId(user.getCreatedBy());\n        result.setLastUpdateDate(user.getLastUpdate());\n        result.setLastConnectionDate(user.getLastConnection());\n        result.setTitle(user.getTitle());\n        result.setJobTitle(user.getJobTitle());\n\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/organization/UserSearchAttributeConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.organization;\n\nimport static org.bonitasoft.web.rest.model.identity.UserItem.*;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.identity.UserSearchDescriptor;\nimport org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute.TYPE;\n\n/**\n * @author Colin PUY, Anthony Birembaut\n */\npublic class UserSearchAttributeConverter implements AttributeConverter {\n\n    protected static final Map<String, String> mapping = new HashMap<>();\n    private static final Map<String, TYPE> valueTypeMapping = new HashMap<>();\n\n    static {\n        addAttributeConverterItem(ATTRIBUTE_ID, UserSearchDescriptor.ID);\n        addAttributeConverterItem(ATTRIBUTE_FIRSTNAME, UserSearchDescriptor.FIRST_NAME);\n        addAttributeConverterItem(ATTRIBUTE_LASTNAME, UserSearchDescriptor.LAST_NAME);\n        addAttributeConverterItem(ATTRIBUTE_USERNAME, UserSearchDescriptor.USER_NAME);\n        addAttributeConverterItem(ATTRIBUTE_ENABLED, UserSearchDescriptor.ENABLED, TYPE.BOOLEAN);\n        addAttributeConverterItem(ATTRIBUTE_MANAGER_ID, UserSearchDescriptor.MANAGER_USER_ID);\n        addAttributeConverterItem(FILTER_GROUP_ID, UserSearchDescriptor.GROUP_ID);\n        addAttributeConverterItem(FILTER_ROLE_ID, UserSearchDescriptor.ROLE_ID);\n    }\n\n    @Override\n    public String convert(final String attribute) {\n        return mapping.get(attribute);\n    }\n\n    private static void addAttributeConverterItem(String webSearchKey, String engineSearchKey, TYPE attributeType) {\n        mapping.put(webSearchKey, engineSearchKey);\n        valueTypeMapping.put(webSearchKey, attributeType);\n    }\n\n    private static void addAttributeConverterItem(String webSearchKey, String engineSearchKey) {\n        addAttributeConverterItem(webSearchKey, engineSearchKey, TYPE.STRING);\n    }\n\n    @Override\n    public Map<String, TYPE> getValueTypeMapping() {\n        return valueTypeMapping;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/organization/UserUpdaterConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.organization;\n\nimport java.util.Map;\n\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.console.common.server.utils.IconDescriptor;\nimport org.bonitasoft.engine.identity.UserUpdater;\nimport org.bonitasoft.web.rest.model.identity.UserItem;\nimport org.bonitasoft.web.toolkit.client.common.util.StringUtil;\n\npublic class UserUpdaterConverter {\n\n    public UserUpdater convert(Map<String, String> attributes, BonitaHomeFolderAccessor bonitaHomeFolderAccessor) {\n        UserUpdater userUpdater = new UserUpdater();\n\n        if (attributes.containsKey(UserItem.ATTRIBUTE_FIRSTNAME)) {\n            userUpdater.setFirstName(attributes.get(UserItem.ATTRIBUTE_FIRSTNAME));\n        }\n        if (attributes.containsKey(UserItem.ATTRIBUTE_LASTNAME)) {\n            userUpdater.setLastName(attributes.get(UserItem.ATTRIBUTE_LASTNAME));\n        }\n        if (attributes.containsKey(UserItem.ATTRIBUTE_PASSWORD)) {\n            userUpdater.setPassword(attributes.get(UserItem.ATTRIBUTE_PASSWORD));\n        }\n        if (attributes.containsKey(UserItem.ATTRIBUTE_USERNAME)) {\n            userUpdater.setUserName(attributes.get(UserItem.ATTRIBUTE_USERNAME));\n        }\n        if (attributes.containsKey(UserItem.ATTRIBUTE_MANAGER_ID)) {\n            Long managerId = getManagerId(attributes);\n            userUpdater.setManagerId(managerId);\n        }\n        if (!StringUtil.isBlank(attributes.get(UserItem.ATTRIBUTE_ICON))) {\n            IconDescriptor iconDescriptor = bonitaHomeFolderAccessor\n                    .getIconFromFileSystem(attributes.get(UserItem.ATTRIBUTE_ICON));\n            userUpdater.setIcon(iconDescriptor.getFilename(), iconDescriptor.getContent());\n        }\n        if (attributes.containsKey(UserItem.ATTRIBUTE_TITLE)) {\n            userUpdater.setTitle(attributes.get(UserItem.ATTRIBUTE_TITLE));\n        }\n        if (attributes.containsKey(UserItem.ATTRIBUTE_JOB_TITLE)) {\n            userUpdater.setJobTitle(attributes.get(UserItem.ATTRIBUTE_JOB_TITLE));\n        }\n        if (attributes.containsKey(UserItem.ATTRIBUTE_ENABLED)) {\n            userUpdater.setEnabled(\"true\".equals(attributes.get(UserItem.ATTRIBUTE_ENABLED)));\n        }\n        return userUpdater;\n    }\n\n    private Long getManagerId(final Map<String, String> attributes) {\n        try {\n            return Long.valueOf(attributes.get(UserItem.ATTRIBUTE_MANAGER_ID));\n        } catch (NumberFormatException e) {\n            return 0L;\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/page/CustomPageContentValidator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.page;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Properties;\nimport java.util.stream.Stream;\n\nimport org.bonitasoft.console.common.server.page.CustomPageService;\nimport org.bonitasoft.engine.page.ContentType;\n\npublic class CustomPageContentValidator {\n\n    private static final String INDEX_GROOVY = \"Index.groovy\";\n\n    private static final String INDEX_HTML = \"index.html\";\n\n    private static final String PAGE_PROPERTIES = \"page.properties\";\n\n    private static final String THEME_CSS = \"theme.css\";\n\n    public void validate(File pageFolder) throws InvalidPageZipContentException {\n        final File[] rootFiles = pageFolder.listFiles();\n        if (rootFiles == null) {\n            throw new InvalidPageZipContentException(\n                    \"Content not found.\");\n        }\n        Properties pageProperties = Stream.of(rootFiles)\n                .filter(file -> file.getName().matches(PAGE_PROPERTIES))\n                .findFirst()\n                .map(this::loadProperties)\n                .orElseThrow(() -> new InvalidPageZipContentException(\n                        String.format(\"%s descriptor is missing.\", PAGE_PROPERTIES)));\n\n        String contentType = pageProperties.getProperty(CustomPageService.PROPERTY_CONTENT_TYPE);\n        Optional<File> resouresFolder = Stream.of(rootFiles)\n                .filter(file -> file.getName().matches(CustomPageService.RESOURCES_PROPERTY))\n                .findFirst();\n        if (Objects.equals(contentType, ContentType.THEME)) {\n            if (!resouresFolder.filter(resources -> new File(resources, THEME_CSS).exists()).isPresent()) {\n                throw new InvalidPageZipContentException(String.format(\"%s is missing.\", THEME_CSS));\n            }\n        } else if (!Objects.equals(contentType, ContentType.API_EXTENSION)) {\n            if (Stream.of(rootFiles)\n                    .noneMatch(file -> file.getName().matches(INDEX_HTML) || file.getName().matches(INDEX_GROOVY))\n                    && !resouresFolder.filter(resources -> new File(resources, INDEX_HTML).exists()\n                            || new File(resources, INDEX_GROOVY).exists()).isPresent()) {\n                throw new InvalidPageZipContentException(\n                        String.format(\"%s or %s is missing.\", INDEX_HTML, INDEX_GROOVY));\n            }\n        }\n    }\n\n    private Properties loadProperties(File pagePropertyFile) {\n        Properties pageProperties = new java.util.Properties();\n        try (InputStream is = new FileInputStream(pagePropertyFile)) {\n            pageProperties.load(is);\n        } catch (IOException e) {\n            throw new RuntimeException(\"Failed to load page.properties file.\", e);\n        }\n        return pageProperties;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/page/InvalidPageZipContentException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.page;\n\n/**\n * @author Fabio Lombardi\n */\npublic class InvalidPageZipContentException extends Exception {\n\n    /**\n     * UID\n     */\n    private static final long serialVersionUID = 1L;\n\n    public InvalidPageZipContentException() {\n        super();\n    }\n\n    public InvalidPageZipContentException(final String message) {\n        super(message);\n    }\n\n    public InvalidPageZipContentException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public InvalidPageZipContentException(final Throwable cause) {\n        super(cause);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/page/PageDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.page;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Random;\n\nimport org.apache.commons.io.FileUtils;\nimport org.bonitasoft.console.common.server.page.CustomPageService;\nimport org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils;\nimport org.bonitasoft.console.common.server.servlet.FileUploadServlet;\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.console.common.server.utils.UnzipUtil;\nimport org.bonitasoft.engine.api.PageAPI;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.io.IOUtil;\nimport org.bonitasoft.engine.page.*;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.extension.page.PageResourceProvider;\nimport org.bonitasoft.web.rest.model.portal.page.PageDefinition;\nimport org.bonitasoft.web.rest.model.portal.page.PageItem;\nimport org.bonitasoft.web.rest.server.datastore.CommonDatastore;\nimport org.bonitasoft.web.rest.server.datastore.filter.Filters;\nimport org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator;\nimport org.bonitasoft.web.rest.server.datastore.utils.Sorts;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasAdd;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasDelete;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasUpdate;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.codehaus.groovy.control.CompilationFailedException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Fabio Lombardi, Anthony Birembaut\n */\n\npublic class PageDatastore extends CommonDatastore<PageItem, Page>\n        implements DatastoreHasAdd<PageItem>, DatastoreHasUpdate<PageItem>,\n        DatastoreHasGet<PageItem>, DatastoreHasSearch<PageItem>, DatastoreHasDelete {\n\n    /**\n     * Logger\n     */\n    private static final Logger LOGGER = LoggerFactory.getLogger(PageDatastore.class.getName());\n\n    /**\n     * page files\n     */\n    public static final String UNMAPPED_ATTRIBUTE_ZIP_FILE = \"pageZip\";\n\n    static final String PAGE_TOKEN_PREFIX = \"custompage_\";\n\n    protected final WebBonitaConstantsUtils constants;\n\n    protected final PageAPI pageAPI;\n\n    protected final CustomPageService customPageService;\n\n    private final BonitaHomeFolderAccessor tenantFolder;\n\n    private final CustomPageContentValidator pageContentValidator;\n\n    public PageDatastore(final APISession engineSession, final WebBonitaConstantsUtils constantsValue,\n            final PageAPI pageAPI,\n            final CustomPageService customPageService,\n            final BonitaHomeFolderAccessor tenantFolder) {\n        super(engineSession);\n        constants = constantsValue;\n        this.pageAPI = pageAPI;\n        this.customPageService = customPageService;\n        this.tenantFolder = tenantFolder;\n        this.pageContentValidator = new CustomPageContentValidator();\n    }\n\n    /**\n     * @deprecated as of 9.0.0, a page should be created at startup.\n     */\n    @Override\n    @Deprecated(since = \"9.0.0\")\n    public PageItem add(final PageItem pageItem) {\n        final String zipFileAttribute = pageItem.getAttributeValue(UNMAPPED_ATTRIBUTE_ZIP_FILE);\n        // Name pattern: \"TokenID::originalFileName\"\n        final String[] filenames = zipFileAttribute.split(FileUploadServlet.RESPONSE_SEPARATOR);\n        final String filename = filenames[0];\n        String originalFileName = getOriginalFilename(filenames, filename, pageItem.getAttributes());\n        pageItem.setContentName(originalFileName);\n\n        try {\n            final APISession engineSession = getEngineSession();\n            final File zipFile = tenantFolder.getTempFile(filename);\n            final File unzipPageTempFolder = unzipContentFile(zipFile);\n            pageContentValidator.validate(unzipPageTempFolder);\n            final Page page = createEnginePage(pageItem, zipFile);\n            final PageItem addedPage = convertEngineToConsoleItem(page);\n\n            PageResourceProvider pageResourceProvider = customPageService.getPageResourceProvider(page);\n            customPageService.writePageToPageDirectory(page, pageResourceProvider, unzipPageTempFolder, engineSession);\n            deleteTempDirectory(unzipPageTempFolder);\n            return addedPage;\n        } catch (final BonitaException | IOException | InvalidPageZipContentException e) {\n            throw new APIException(e);\n        } finally {\n            tenantFolder.removeUploadedTempContent(filename);\n        }\n    }\n\n    protected File unzipContentFile(final File zipFile) throws InvalidPageZipContentException {\n        File unzipPageTempFolder = null;\n        try {\n            final Random randomGen = new Random();\n            final int tempPageFolder = randomGen.nextInt();\n            unzipPageTempFolder = new File(constants.getTempFolder(), String.valueOf(tempPageFolder));\n            UnzipUtil.unzip(zipFile, unzipPageTempFolder.getPath(), false);\n        } catch (final Exception e) {\n            deleteTempDirectory(unzipPageTempFolder);\n            throw new InvalidPageZipContentException(\"Unable to unzip the page content.\", e);\n        }\n        return unzipPageTempFolder;\n    }\n\n    protected boolean isPageTokenValid(final String urlToken) {\n        return urlToken.matches(PAGE_TOKEN_PREFIX + \"\\\\p{Alnum}+\");\n    }\n\n    protected void deleteTempDirectory(final File unzipPage) {\n        try {\n            if (unzipPage.isDirectory()) {\n                IOUtilDeleteDir(unzipPage);\n            }\n        } catch (final IOException e) {\n            throw new APIException(e);\n        }\n    }\n\n    protected void IOUtilDeleteDir(final File unzipPage) throws IOException {\n        IOUtil.deleteDir(unzipPage);\n    }\n\n    protected Page createEnginePage(final PageItem pageItem, final File zipFile)\n            throws CreationException, IOException, UpdateException {\n        try {\n            final byte[] zipContent = readZipFile(zipFile);\n            Page page = pageAPI.createPage(pageItem.getContentName(), zipContent);\n            if (pageItem.getProcessId() != null) {\n                final PageUpdater pageUpdater = new PageUpdater();\n                pageUpdater.setProcessDefinitionId(pageItem.getProcessId().toLong());\n                if (pageItem.getContentType() != null) {\n                    pageUpdater.setContentType(pageItem.getContentType());\n                }\n                page = pageAPI.updatePage(page.getId(), pageUpdater);\n            }\n            return page;\n        } finally {\n            zipFile.delete();\n        }\n    }\n\n    protected byte[] readZipFile(final File zipFile) throws IOException {\n        return FileUtils.readFileToByteArray(zipFile);\n    }\n\n    protected PageCreator buildPageCreatorFrom(final PageItem pageItem) {\n        final PageCreator pageCreator = new PageCreator(pageItem.getUrlToken(), pageItem.getContentName());\n        pageCreator.setDescription(pageItem.getDescription());\n        pageCreator.setDisplayName(pageItem.getDisplayName());\n        return pageCreator;\n    }\n\n    @Override\n    public PageItem get(final APIID id) {\n        try {\n            final Page pageItem = pageAPI.getPage(id.toLong());\n            return convertEngineToConsoleItem(pageItem);\n        } catch (PageNotFoundException e) {\n            throw new APIItemNotFoundException(PageDefinition.TOKEN, id);\n        }\n    }\n\n    @Override\n    public void delete(final List<APIID> ids) {\n        try {\n            for (final APIID id : ids) {\n                final Page page = pageAPI.getPage(id.toLong());\n                final APISession engineSession = getEngineSession();\n                PageResourceProvider pageResourceProvider = customPageService.getPageResourceProvider(page);\n                customPageService.ensurePageFolderIsUpToDate(engineSession, pageResourceProvider);\n                pageAPI.deletePage(id.toLong());\n                customPageService.removePageLocally(pageResourceProvider);\n            }\n        } catch (final BonitaException | IOException e) {\n            throw new APIException(e);\n        }\n    }\n\n    protected List<Long> APIIdsToLong(final List<APIID> ids) {\n        final List<Long> result = new ArrayList<>(ids.size());\n        for (final APIID id : ids) {\n            result.add(id.toLong());\n        }\n        return result;\n    }\n\n    @Override\n    public ItemSearchResult<PageItem> search(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n        // Build search\n        final SearchOptionsCreator creator = makeSearchOptionCreator(page, resultsByPage, search, orders, filters);\n\n        // Run search depending on filters passed\n        SearchResult<Page> searchResult;\n        try {\n            searchResult = runSearch(creator);\n            // Convert to ConsoleItems\n            return new ItemSearchResult<>(page, resultsByPage, searchResult.getCount(),\n                    convertEngineToConsoleItemsList(searchResult.getResult()));\n        } catch (final SearchException e) {\n            throw new APIException(e);\n        }\n\n    }\n\n    protected PageSearchDescriptorConverter getSearchDescriptorConverter() {\n        return new PageSearchDescriptorConverter();\n    }\n\n    protected SearchOptionsCreator makeSearchOptionCreator(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters) {\n        final SearchOptionsCreator searchOptionsCreator = new SearchOptionsCreator(page, resultsByPage, search,\n                new Sorts(orders,\n                        getSearchDescriptorConverter()),\n                new Filters(filters,\n                        new PageFilterCreator(getSearchDescriptorConverter())));\n        final SearchOptionsBuilder builder = searchOptionsCreator.getBuilder();\n\n        if (filters.containsKey(PageItem.FILTER_CONTENT_TYPE)\n                && \"processPage\".equalsIgnoreCase(filters.get(PageSearchDescriptor.CONTENT_TYPE))) {\n            builder.leftParenthesis().filter(PageSearchDescriptor.CONTENT_TYPE, \"form\")\n                    .or().filter(PageSearchDescriptor.CONTENT_TYPE, \"page\")\n                    .rightParenthesis();\n        } else {\n            addStringFilterToSearchBuilder(filters, builder, PageItem.FILTER_CONTENT_TYPE,\n                    PageSearchDescriptor.CONTENT_TYPE);\n        }\n        return searchOptionsCreator;\n    }\n\n    /**\n     * @param creator\n     * @return\n     * @throws SearchException\n     */\n    protected SearchResult<Page> runSearch(final SearchOptionsCreator creator)\n            throws SearchException {\n        return pageAPI.searchPages(creator.create());\n    }\n\n    /**\n     * @deprecated as of 9.0.0, a page should be updated at startup.\n     */\n    @Override\n    @Deprecated(since = \"9.0.0\")\n    public PageItem update(final APIID id, final Map<String, String> attributes) {\n        String filename = null;\n        File zipFile = null;\n        try {\n            Long pageId = id.toLong();\n            Page page = pageAPI.getPage(pageId);\n            PageItem updatedPage = null;\n            if (attributes.containsKey(PageDatastore.UNMAPPED_ATTRIBUTE_ZIP_FILE)) {\n                final String zipFileAttribute = attributes.get(UNMAPPED_ATTRIBUTE_ZIP_FILE);\n                if (zipFileAttribute != null && !zipFileAttribute.isEmpty()) {\n                    final String[] filenames = zipFileAttribute.split(FileUploadServlet.RESPONSE_SEPARATOR);\n                    filename = filenames[0];\n                    String originalFileName = getOriginalFilename(filenames, filename, attributes);\n                    final APISession engineSession = getEngineSession();\n                    zipFile = tenantFolder.getTempFile(filename);\n                    final File unzipPageTempFolder = unzipContentFile(zipFile);\n                    pageContentValidator.validate(unzipPageTempFolder);\n                    try {\n                        updatePageContent(page, zipFile);\n                        final PageUpdater pageUpdater = new PageUpdater();\n                        pageUpdater.setContentName(originalFileName);\n                        page = pageAPI.updatePage(pageId, pageUpdater);\n                        updatedPage = convertEngineToConsoleItem(page);\n                    } finally {\n                        PageResourceProvider pageResourceProvider = customPageService.getPageResourceProvider(page);\n                        customPageService.writePageToPageDirectory(page, pageResourceProvider, unzipPageTempFolder,\n                                engineSession);\n                        deleteTempDirectory(unzipPageTempFolder);\n                    }\n                }\n            }\n            return updatedPage;\n        } catch (final BonitaException | IOException | InvalidPageZipContentException e) {\n            throw new APIException(e);\n        } finally {\n            if (filename != null) {\n                tenantFolder.removeUploadedTempContent(filename);\n            }\n            if (zipFile != null) {\n                zipFile.delete();\n            }\n        }\n    }\n\n    protected String getOriginalFilename(final String[] filenames, final String tempFilename,\n            final Map<String, String> attributes) {\n        String originalFileName;\n        if (filenames.length > 1) {\n            originalFileName = filenames[1];\n        } else {\n            originalFileName = attributes.getOrDefault(PageItem.ATTRIBUTE_CONTENT_NAME, tempFilename);\n        }\n        return originalFileName;\n    }\n\n    protected void updatePageContent(final Page page, final File zipFile) throws IOException,\n            CompilationFailedException, BonitaException {\n        if (zipFile != null) {\n            PageResourceProvider pageResourceProvider = customPageService.getPageResourceProvider(page);\n            customPageService.ensurePageFolderIsUpToDate(getEngineSession(), pageResourceProvider);\n            pageAPI.updatePageContent(page.getId(), FileUtils.readFileToByteArray(zipFile));\n        }\n        customPageService.removePageLocally(page);\n    }\n\n    @Override\n    protected PageItem convertEngineToConsoleItem(final Page item) {\n        if (item != null) {\n            return new PageItemConverter().convert(item);\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/page/PageDatastoreFactory.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.page;\n\nimport org.bonitasoft.console.common.server.page.CustomPageService;\nimport org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils;\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.engine.api.PageAPI;\nimport org.bonitasoft.engine.session.APISession;\n\npublic class PageDatastoreFactory {\n\n    public PageDatastore create(final APISession engineSession, final WebBonitaConstantsUtils constantsValue,\n            final PageAPI pageAPI) {\n        return new PageDatastore(engineSession,\n                constantsValue,\n                pageAPI,\n                new CustomPageService(),\n                new BonitaHomeFolderAccessor());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/page/PageFilterCreator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.page;\n\nimport org.bonitasoft.web.rest.server.datastore.filter.GenericFilterCreator;\n\nclass PageFilterCreator extends GenericFilterCreator {\n\n    PageFilterCreator(final PageSearchDescriptorConverter converter) {\n        super(converter);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/page/PageItemConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.page;\n\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.web.rest.model.portal.page.PageItem;\nimport org.bonitasoft.web.rest.server.datastore.converter.ItemConverter;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\npublic class PageItemConverter extends ItemConverter<PageItem, Page> {\n\n    @Override\n    public PageItem convert(final Page engineItem) {\n        final PageItem pageItem = new PageItem();\n        pageItem.setId(engineItem.getId());\n        pageItem.setProcessId(engineItem.getProcessDefinitionId());\n        pageItem.setUrlToken(engineItem.getName());\n        pageItem.setDisplayName(engineItem.getDisplayName());\n        pageItem.setIsProvided(engineItem.isProvided());\n        pageItem.setDescription(engineItem.getDescription());\n        pageItem.setCreatedByUserId(APIID.makeAPIID(engineItem.getInstalledBy()));\n        pageItem.setCreationDate(engineItem.getInstallationDate());\n        pageItem.setLastUpdateDate(engineItem.getLastModificationDate());\n        pageItem.setUpdatedByUserId(engineItem.getLastUpdatedBy());\n        pageItem.setContentName(engineItem.getContentName());\n        pageItem.setContentType(engineItem.getContentType());\n        pageItem.setIsEditable(engineItem.isEditable());\n        pageItem.setIsRemovable(engineItem.isRemovable());\n        return pageItem;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/page/PageSearchDescriptorConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.page;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.page.PageSearchDescriptor;\nimport org.bonitasoft.web.rest.model.portal.page.PageItem;\nimport org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter;\nimport org.bonitasoft.web.toolkit.client.common.util.MapUtil;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute.TYPE;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class PageSearchDescriptorConverter implements AttributeConverter {\n\n    private static final Map<String, String> attributeNameMapping = new HashMap<>();\n    private static final Map<String, TYPE> valueTypeMapping = new HashMap<>();\n\n    static {\n        createMappings();\n    }\n\n    public Map<String, TYPE> getValueTypeMapping() {\n        return valueTypeMapping;\n    }\n\n    private static void createMappings() {\n        addAttributeConverterItem(PageItem.ATTRIBUTE_ID, PageSearchDescriptor.ID, TYPE.STRING);\n        addAttributeConverterItem(PageItem.ATTRIBUTE_URL_TOKEN, PageSearchDescriptor.NAME, TYPE.STRING);\n        addAttributeConverterItem(PageItem.ATTRIBUTE_DISPLAY_NAME, PageSearchDescriptor.DISPLAY_NAME, TYPE.STRING);\n        addAttributeConverterItem(PageItem.ATTRIBUTE_IS_PROVIDED, PageSearchDescriptor.PROVIDED, TYPE.BOOLEAN);\n        addAttributeConverterItem(PageItem.ATTRIBUTE_CREATED_BY_USER_ID, PageSearchDescriptor.INSTALLED_BY,\n                TYPE.STRING);\n        addAttributeConverterItem(PageItem.ATTRIBUTE_CREATION_DATE, PageSearchDescriptor.INSTALLATION_DATE,\n                TYPE.STRING);\n        addAttributeConverterItem(PageItem.ATTRIBUTE_LAST_UPDATE_DATE, PageSearchDescriptor.LAST_MODIFICATION_DATE,\n                TYPE.STRING);\n        //CONTENT_TYPE is managed differently in order to accept a OR with form and page\n        //addAttributeConverterItem(PageItem.FILTER_CONTENT_TYPE, PageSearchDescriptor.CONTENT_TYPE, TYPE.STRING);\n        addAttributeConverterItem(PageItem.ATTRIBUTE_PROCESS_ID, PageSearchDescriptor.PROCESS_DEFINITION_ID,\n                TYPE.STRING);\n    }\n\n    @Override\n    public String convert(final String attribute) {\n        if (PageItem.FILTER_CONTENT_TYPE.equals(attribute)) {\n            return MapUtil.getValue(attributeNameMapping, attribute, \"\");\n        } else {\n            return MapUtil.getMandatory(attributeNameMapping, attribute);\n        }\n    }\n\n    private static void addAttributeConverterItem(String webSearchKey, String engineSearchKey, TYPE attributeType) {\n        attributeNameMapping.put(webSearchKey, engineSearchKey);\n        valueTypeMapping.put(webSearchKey, attributeType);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/profile/GetProfileHelper.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.profile;\n\nimport org.bonitasoft.engine.profile.Profile;\nimport org.bonitasoft.web.rest.model.portal.profile.ProfileItem;\nimport org.bonitasoft.web.rest.server.engineclient.ProfileEngineClient;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Vincent Elcrin\n */\npublic class GetProfileHelper implements DatastoreHasGet<ProfileItem> {\n\n    private final ProfileEngineClient profileClient;\n\n    public GetProfileHelper(ProfileEngineClient profileClient) {\n        this.profileClient = profileClient;\n    }\n\n    @Override\n    public ProfileItem get(APIID id) {\n        Profile profile = profileClient.getProfile(id.toLong());\n        return new ProfileItemConverter().convert(profile);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/profile/ProfileItemConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.profile;\n\nimport org.bonitasoft.engine.profile.Profile;\nimport org.bonitasoft.web.rest.model.portal.profile.ProfileItem;\nimport org.bonitasoft.web.rest.server.datastore.converter.ItemConverter;\n\n/**\n * @author Vincent Elcrin\n */\npublic class ProfileItemConverter extends ItemConverter<ProfileItem, Profile> {\n\n    public static final String USER_PROFILE_NAME = \"User\";\n\n    public static final String ADMIN_PROFILE_NAME = \"Administrator\";\n\n    public static final String PROCESSMANAGER_PROFILE_NAME = \"Process manager\";\n\n    public static final String TEAMMANAGER_PROFILE_NAME = \"Team manager\";\n\n    public static final String USER_PROFILE_ICONPATH = \"icons/profiles/profileUser.png\";\n\n    public static final String ADMIN_PROFILE_ICONPATH = \"icons/profiles/profileAdmin.png\";\n\n    public static final String PROCESSMANAGER_PROFILE_ICONPATH = \"icons/profiles/profileProcessManager.png\";\n\n    public static final String TEAMMANAGER_PROFILE_ICONPATH = \"icons/profiles/profileTeamManager.png\";\n\n    public static final String DEFAULT_PROFILE_ICONPATH = \"icons/profiles/profileDefault.png\";\n\n    @Override\n    public ProfileItem convert(final Profile profile) {\n        final ProfileItem item = new ProfileItem();\n        item.setId(profile.getId());\n        item.setName(profile.getName());\n        item.setDescription(profile.getDescription());\n        item.setIsDefault(profile.isDefault());\n        item.setIcon(getIconPath(profile.getName()));\n        item.setUpdatedByUserId(profile.getLastUpdatedBy());\n        item.setLastUpdateDate(profile.getLastUpdateDate());\n        item.setCreatedByUserId(profile.getCreatedBy());\n        item.setCreationDate(profile.getCreationDate());\n        return item;\n    }\n\n    protected String getIconPath(final String profileName) {\n        if (USER_PROFILE_NAME.equals(profileName)) {\n            return USER_PROFILE_ICONPATH;\n        } else if (ADMIN_PROFILE_NAME.equals(profileName)) {\n            return ADMIN_PROFILE_ICONPATH;\n        } else if (PROCESSMANAGER_PROFILE_NAME.equals(profileName)) {\n            return PROCESSMANAGER_PROFILE_ICONPATH;\n        } else if (TEAMMANAGER_PROFILE_NAME.equals(profileName)) {\n            return TEAMMANAGER_PROFILE_ICONPATH;\n        } else {\n            return DEFAULT_PROFILE_ICONPATH;\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/profile/ProfileSearchDescriptorConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.profile;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.profile.ProfileSearchDescriptor;\nimport org.bonitasoft.web.rest.model.portal.profile.ProfileItem;\nimport org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute.TYPE;\n\n/**\n * @author Vincent Elcrin\n */\npublic class ProfileSearchDescriptorConverter implements AttributeConverter {\n\n    private static final Map<String, String> mapping = new HashMap<>();\n\n    static {\n        mapping.put(ProfileItem.ATTRIBUTE_ID, ProfileSearchDescriptor.ID);\n        mapping.put(ProfileItem.ATTRIBUTE_NAME, ProfileSearchDescriptor.NAME);\n    }\n\n    @Override\n    public String convert(String attribute) {\n        String descriptor = mapping.get(attribute);\n        if (descriptor == null) {\n            throw new RuntimeException(attribute + \" has no valid search descriptor\");\n        }\n        return descriptor;\n    }\n\n    @Override\n    public Map<String, TYPE> getValueTypeMapping() {\n        return Collections.emptyMap();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/profile/SearchProfilesHelper.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.profile;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.profile.Profile;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.web.rest.model.portal.profile.ProfileItem;\nimport org.bonitasoft.web.rest.server.datastore.converter.ItemSearchResultConverter;\nimport org.bonitasoft.web.rest.server.datastore.filter.Filters;\nimport org.bonitasoft.web.rest.server.datastore.filter.GenericFilterCreator;\nimport org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator;\nimport org.bonitasoft.web.rest.server.datastore.utils.Sorts;\nimport org.bonitasoft.web.rest.server.engineclient.ProfileEngineClient;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.util.MapUtil;\n\npublic class SearchProfilesHelper implements DatastoreHasSearch<ProfileItem> {\n\n    private final ProfileEngineClient profileClient;\n\n    public SearchProfilesHelper(ProfileEngineClient profileClient) {\n        this.profileClient = profileClient;\n    }\n\n    @Override\n    public ItemSearchResult<ProfileItem> search(int page, int resultsByPage, String search, String orders,\n            Map<String, String> filters) {\n        if (isFilteredBy(filters, ProfileItem.FILTER_USER_ID)) {\n            return searchProfilesForUser(page, filters);\n        }\n        return searchProfiles(page, resultsByPage, search, orders, filters);\n    }\n\n    private boolean isFilteredBy(Map<String, String> filters, String filterName) {\n        return filters != null && !MapUtil.isBlank(filters, filterName);\n    }\n\n    private ItemSearchResult<ProfileItem> searchProfiles(int page, int resultsByPage, String search, String orders,\n            Map<String, String> filters) {\n        SearchOptionsCreator options = makeSearchOptions(page, resultsByPage, search, orders, filters);\n        SearchResult<Profile> searchProfiles = profileClient.searchProfiles(options.create());\n        return new ItemSearchResultConverter<>(page, resultsByPage, searchProfiles, new ProfileItemConverter())\n                .toItemSearchResult();\n    }\n\n    private ItemSearchResult<ProfileItem> searchProfilesForUser(int page, Map<String, String> filters) {\n        long userId = Long.parseLong(filters.get(ProfileItem.FILTER_USER_ID));\n        List<Profile> profiles = profileClient.listProfilesForUser(userId);\n        return new ItemSearchResult<>(page, profiles.size(), profiles.size(),\n                new ProfileItemConverter().convert(profiles));\n    }\n\n    private SearchOptionsCreator makeSearchOptions(int page, int resultsByPage, String search, String orders,\n            Map<String, String> filters) {\n        return new SearchOptionsCreator(page, resultsByPage, search,\n                new Sorts(orders, new ProfileSearchDescriptorConverter()),\n                new Filters(filters, new GenericFilterCreator(new ProfileSearchDescriptorConverter())));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/profile/member/AddProfileMemberHelper.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.profile.member;\n\nimport org.bonitasoft.engine.profile.ProfileMember;\nimport org.bonitasoft.web.rest.model.portal.profile.ProfileMemberItem;\nimport org.bonitasoft.web.rest.server.engineclient.ProfileMemberEngineClient;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasAdd;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Vincent Elcrin\n */\npublic class AddProfileMemberHelper implements DatastoreHasAdd<ProfileMemberItem> {\n\n    private static final Long UNSET = null;\n\n    private final ProfileMemberEngineClient engineClient;\n\n    public AddProfileMemberHelper(ProfileMemberEngineClient engineClient) {\n        this.engineClient = engineClient;\n    }\n\n    @Override\n    public ProfileMemberItem add(ProfileMemberItem item) {\n        ProfileMember addedProfileMember = addProfileMember(item.getProfileId(), item.getUserId(), item.getGroupId(),\n                item.getRoleId());\n        return new ProfileMemberItemConverter().convert(addedProfileMember);\n    }\n\n    private ProfileMember addProfileMember(APIID profileId, APIID userId, APIID groupId, APIID roleId) {\n        return engineClient.createProfileMember(toLong(profileId), toLong(userId), toLong(groupId), toLong(roleId));\n\n    }\n\n    private Long toLong(APIID apiId) {\n        if (apiId == null || !apiId.isValidLongID()) {\n            return UNSET;\n        }\n        return apiId.toLong();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/profile/member/DeleteProfileMemberHelper.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.profile.member;\n\nimport java.util.List;\n\nimport org.bonitasoft.web.rest.server.engineclient.ProfileMemberEngineClient;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasDelete;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Vincent Elcrin\n */\npublic class DeleteProfileMemberHelper implements DatastoreHasDelete {\n\n    private final ProfileMemberEngineClient engineClient;\n\n    public DeleteProfileMemberHelper(ProfileMemberEngineClient engineClient) {\n        this.engineClient = engineClient;\n    }\n\n    @Override\n    public void delete(List<APIID> ids) {\n        for (APIID id : ids) {\n            delete(id);\n        }\n    }\n\n    private void delete(APIID id) {\n        engineClient.deleteProfileMember(id.toLong());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/profile/member/MemberType.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.profile.member;\n\nimport org.bonitasoft.web.rest.model.portal.profile.AbstractMemberItem;\n\n/**\n * @author Vincent Elcrin\n */\npublic enum MemberType {\n\n    USER(AbstractMemberItem.VALUE_MEMBER_TYPE_USER), GROUP(AbstractMemberItem.VALUE_MEMBER_TYPE_GROUP), ROLE(\n            AbstractMemberItem.VALUE_MEMBER_TYPE_ROLE), MEMBERSHIP(AbstractMemberItem.VALUE_MEMBER_TYPE_MEMBERSHIP);\n\n    private final String type;\n\n    MemberType(String type) {\n        this.type = type;\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    public static MemberType from(String type) {\n        for (MemberType candidate : MemberType.values()) {\n            if (candidate.getType().equals(type)) {\n                return candidate;\n            }\n        }\n        throw new IllegalArgumentException(\"No enum const for \" + type + \" found\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/profile/member/MemberTypeConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.profile.member;\n\nimport org.bonitasoft.web.rest.server.datastore.converter.ValueConverter;\n\n/**\n * @author Vincent Elcrin\n */\npublic class MemberTypeConverter implements ValueConverter<MemberType> {\n\n    @Override\n    public MemberType convert(String value) {\n        return MemberType.from(value);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/profile/member/ProfileMemberItemConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.profile.member;\n\nimport org.bonitasoft.engine.profile.ProfileMember;\nimport org.bonitasoft.web.rest.model.portal.profile.ProfileMemberItem;\nimport org.bonitasoft.web.rest.server.datastore.converter.ItemConverter;\n\n/**\n * @author Vincent Elcrin\n */\npublic class ProfileMemberItemConverter extends ItemConverter<ProfileMemberItem, ProfileMember> {\n\n    @Override\n    public ProfileMemberItem convert(ProfileMember profileMember) {\n        ProfileMemberItem item = new ProfileMemberItem();\n        item.setId(profileMember.getId());\n        item.setProfileId(profileMember.getProfileId());\n        item.setUserId(profileMember.getUserId());\n        item.setRoleId(profileMember.getRoleId());\n        item.setGroupId(profileMember.getGroupId());\n        return item;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/profile/member/ProfileMemberSearchDescriptorConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.profile.member;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.profile.ProfileMemberSearchDescriptor;\nimport org.bonitasoft.web.rest.model.portal.profile.ProfileMemberItem;\nimport org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * @author Vincent Elcrin\n */\npublic class ProfileMemberSearchDescriptorConverter implements AttributeConverter {\n\n    private static final Map<String, String> mapping = new HashMap<>();\n\n    static {\n        mapping.put(ProfileMemberItem.ATTRIBUTE_ID, ProfileMemberSearchDescriptor.ID);\n        mapping.put(ProfileMemberItem.ATTRIBUTE_PROFILE_ID, ProfileMemberSearchDescriptor.PROFILE_ID);\n        mapping.put(ProfileMemberItem.ATTRIBUTE_USER_ID, ProfileMemberSearchDescriptor.USER_ID);\n        mapping.put(ProfileMemberItem.ATTRIBUTE_ROLE_ID, ProfileMemberSearchDescriptor.ROLE_ID);\n        mapping.put(ProfileMemberItem.ATTRIBUTE_GROUP_ID, ProfileMemberSearchDescriptor.GROUP_ID);\n        mapping.put(ProfileMemberItem.FILTER_MEMBER_TYPE, \"\");\n    }\n\n    @Override\n    public String convert(String attribute) {\n        String descriptor = mapping.get(attribute);\n        if (descriptor == null) {\n            throw new RuntimeException(attribute + \" has no valid search descriptor\");\n        }\n        return descriptor;\n    }\n\n    @Override\n    public Map<String, ItemAttribute.TYPE> getValueTypeMapping() {\n        return Collections.emptyMap();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/profile/member/SearchProfileMembersHelper.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.profile.member;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.profile.ProfileMember;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.web.rest.model.portal.profile.ProfileMemberItem;\nimport org.bonitasoft.web.rest.server.datastore.converter.ItemSearchResultConverter;\nimport org.bonitasoft.web.rest.server.datastore.filter.FilterAccessor;\nimport org.bonitasoft.web.rest.server.datastore.filter.Filters;\nimport org.bonitasoft.web.rest.server.datastore.filter.GenericFilterCreator;\nimport org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator;\nimport org.bonitasoft.web.rest.server.datastore.utils.Sorts;\nimport org.bonitasoft.web.rest.server.engineclient.ProfileMemberEngineClient;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\n\n/**\n * @author Vincent Elcrin\n */\npublic class SearchProfileMembersHelper implements DatastoreHasSearch<ProfileMemberItem> {\n\n    private final ProfileMemberEngineClient profileMemberClient;\n\n    public SearchProfileMembersHelper(ProfileMemberEngineClient profileMemberClient) {\n        this.profileMemberClient = profileMemberClient;\n    }\n\n    @Override\n    public ItemSearchResult<ProfileMemberItem> search(int page, int resultsByPage, String search, String orders,\n            Map<String, String> filters) {\n        SearchOptionsCreator options = makeSearchOptions(page, resultsByPage, search, orders, filters);\n        SearchResult<ProfileMember> searchResult = profileMemberClient\n                .searchProfileMembers(getMemberType(filters).getType(), options.create());\n        return new ItemSearchResultConverter<>(page, resultsByPage, searchResult,\n                new ProfileMemberItemConverter()).toItemSearchResult();\n    }\n\n    private MemberType getMemberType(Map<String, String> filters) {\n        FilterAccessor filterAccess = new FilterAccessor(filters);\n        return filterAccess.getMandatory(ProfileMemberItem.FILTER_MEMBER_TYPE, new MemberTypeConverter());\n    }\n\n    private SearchOptionsCreator makeSearchOptions(int page, int resultsByPage, String search, String orders,\n            Map<String, String> filters) {\n        return new SearchOptionsCreator(page, resultsByPage, search,\n                new Sorts(orders, new ProfileMemberSearchDescriptorConverter()),\n                new Filters(filters, new GenericFilterCreator(new ProfileMemberSearchDescriptorConverter())));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/utils/SearchOptionsCreator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.utils;\n\nimport static org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil.computeIndex;\n\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.web.rest.server.datastore.filter.Filter;\nimport org.bonitasoft.web.rest.server.datastore.filter.Filters;\nimport org.bonitasoft.web.toolkit.client.common.util.StringUtil;\n\n/**\n * @author Vincent Elcrin\n */\npublic class SearchOptionsCreator {\n\n    private final SearchOptionsBuilder builder;\n\n    public SearchOptionsCreator(int page, int resultsByPage, String search, Sorts sorts, Filters filters) {\n        builder = new SearchOptionsBuilder(computeIndex(page, resultsByPage), resultsByPage);\n        builder.searchTerm(search);\n        addSorts(builder, sorts);\n        addFilters(builder, filters);\n    }\n\n    private void addSorts(SearchOptionsBuilder builder, Sorts sorts) {\n        for (Sort sort : sorts.asList()) {\n            builder.sort(sort.getField(), sort.getOrder());\n        }\n    }\n\n    private void addFilters(SearchOptionsBuilder builder, Filters filters) {\n        for (Filter<?> filter : filters.asList()) {\n            addFilter(builder, filter);\n        }\n    }\n\n    private void addFilter(SearchOptionsBuilder builder, Filter<?> filter) {\n        if (!StringUtil.isBlank(filter.getField())) {\n            if (filter.getOperator() == Filter.Operator.DIFFERENT_FROM) {\n                builder.differentFrom(filter.getField(), filter.getValue());\n            } else {\n                builder.filter(filter.getField(), filter.getValue());\n            }\n\n        }\n    }\n\n    public SearchOptions create() {\n        return builder.done();\n    }\n\n    public SearchOptionsBuilder getBuilder() {\n        return builder;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/utils/Sort.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.utils;\n\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter;\nimport org.bonitasoft.web.rest.server.datastore.converter.EmptyAttributeConverter;\n\n/**\n * Convenient object to deal with credentials sort options\n * Default sort order is ASCENDING\n *\n * @author Colin PUY\n */\npublic class Sort {\n\n    public static final Order DEFAULT_ORDER = Order.ASC;\n\n    private static final String SEPARATOR = \" \";\n\n    private final String field;\n\n    private final Order order;\n\n    private final AttributeConverter converter;\n\n    public Sort(String sortValue, AttributeConverter converter) {\n        this.converter = converter;\n        field = getSortedFieldValue(sortValue);\n        order = getOrder(sortValue);\n    }\n\n    public Sort(String sortValue) {\n        this(sortValue, new EmptyAttributeConverter());\n    }\n\n    private Order getOrder(String sortValue) {\n        String[] split = sortValue.split(SEPARATOR);\n        if (split.length > 1) {\n            return Order.valueOf(split[1].toUpperCase());\n        }\n        return DEFAULT_ORDER;\n    }\n\n    private String getSortedFieldValue(String sortValue) {\n        String fieldValue = sortValue.split(SEPARATOR)[0];\n        String convertedFieldValue = converter.convert(fieldValue);\n        return convertedFieldValue != null ? convertedFieldValue : fieldValue;\n    }\n\n    public String getField() {\n        return field;\n    }\n\n    public Order getOrder() {\n        return order;\n    }\n\n    @Override\n    public String toString() {\n        return getField() +\n                SEPARATOR +\n                getOrder();\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        Sort other = (Sort) obj;\n        if (field == null) {\n            if (other.field != null) {\n                return false;\n            }\n        } else if (!field.equals(other.field)) {\n            return false;\n        }\n        return order == other.order;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/utils/Sorts.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.utils;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter;\nimport org.bonitasoft.web.rest.server.datastore.converter.EmptyAttributeConverter;\nimport org.bonitasoft.web.toolkit.client.common.util.StringUtil;\n\n/**\n * @author Vincent Elcrin\n *         Build list of sorts from a string order following the pattern\n *         <attribute asc>,<attibute desc>,...\n */\npublic class Sorts {\n\n    private final List<Sort> sorts;\n\n    public Sorts(String orders, AttributeConverter converter) {\n        sorts = parseOrders(orders, converter);\n    }\n\n    public Sorts(String orders) {\n        this(orders, new EmptyAttributeConverter());\n    }\n\n    private List<Sort> parseOrders(final String orders, final AttributeConverter converter) {\n        if (StringUtil.isBlank(orders)) {\n            return new ArrayList<>();\n        } else {\n            return buildSortList(orders, converter);\n        }\n    }\n\n    /**\n     * Convert orders and build list of sort adding items to sorts parameter\n     *\n     * @param sorts\n     * @param orders\n     * @param converter\n     */\n    private List<Sort> buildSortList(final String orders, final AttributeConverter converter) {\n        final List<Sort> sorts = new ArrayList<>();\n        for (String order : orders.split(\",\")) {\n            sorts.add(new Sort(order, converter));\n        }\n        return sorts;\n    }\n\n    public List<Sort> asList() {\n        return sorts;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/utils/Variable.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.utils;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport com.fasterxml.jackson.annotation.JsonAnySetter;\n\n/**\n * Variable - Used for variable Json deserialization\n *\n * @author Colin PUY\n */\npublic class Variable {\n\n    private final Map<String, Object> attributes = new HashMap<>();\n\n    @JsonAnySetter\n    public void set(String name, Object value) {\n        attributes.put(name, value);\n    }\n\n    public String getName() {\n        return (String) attributes.get(\"name\");\n    }\n\n    public Object getValue() {\n        return attributes.get(\"value\");\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + ((getName() == null) ? 0 : getName().hashCode());\n        result = prime * result + ((getValue() == null) ? 0 : getValue().hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj)\n            return true;\n        if (obj == null)\n            return false;\n        if (getClass() != obj.getClass())\n            return false;\n        Variable other = (Variable) obj;\n        return getName() != null && getName().equals(other.getName())\n                && getValue() != null && getValue().equals(other.getValue());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/utils/VariableMapper.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.utils;\n\nimport java.io.Serializable;\n\nimport org.bonitasoft.web.rest.server.framework.json.JacksonDeserializer;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.i18n.T_;\nimport org.bonitasoft.web.toolkit.client.common.texttemplate.Arg;\n\n/**\n * Variable Mapper - Used for variable Json deserialization\n *\n * @author Colin PUY\n */\npublic class VariableMapper {\n\n    private final JacksonDeserializer deserializer;\n    private final Variable variable;\n\n    public VariableMapper(Variable variable, JacksonDeserializer jacksonDeserializer) {\n        this.variable = variable;\n        this.deserializer = jacksonDeserializer;\n    }\n\n    public Serializable getSerializableValue(String className) {\n        try {\n            return deserializer.convertValue(variable.getValue(), Class.forName(className));\n        } catch (IllegalArgumentException e) {\n            throw new APIException(\n                    new T_(\"%value% is not a valid value for %className%\", new Arg(\"value\", variable.getValue()),\n                            new Arg(\"className\", className)));\n        } catch (ClassNotFoundException e) {\n            throw new APIException(\n                    new T_(\"%className% not found. Only jdk types are supported\", new Arg(\"className\", className)));\n        } catch (ClassCastException e) {\n            throw new APIException(new T_(\"%className% is not Serializable\", new Arg(\"className\", className)));\n        }\n    }\n\n    public String getName() {\n        return variable.getName();\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result\n                + ((variable == null) ? 0 : variable.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj)\n            return true;\n        if (obj == null)\n            return false;\n        if (getClass() != obj.getClass())\n            return false;\n        VariableMapper other = (VariableMapper) obj;\n        return variable != null && variable.equals(other.variable);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/utils/VariablesMapper.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.utils;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.web.rest.server.framework.json.JacksonDeserializer;\nimport org.bonitasoft.web.toolkit.client.common.util.StringUtil;\n\n/**\n * Variables Mapper - Used for variables Json deserialization\n *\n * @author Colin PUY\n */\npublic class VariablesMapper {\n\n    private final List<VariableMapper> variables;\n\n    protected VariablesMapper(List<Variable> variables, JacksonDeserializer deserializer) {\n        this.variables = convertToMappers(variables, deserializer);\n    }\n\n    public static VariablesMapper fromJson(String json) {\n        JacksonDeserializer deserializer = new JacksonDeserializer();\n        List<Variable> variables = deserializer.deserializeList(json, Variable.class);\n        return new VariablesMapper(variables, deserializer);\n    }\n\n    private List<VariableMapper> convertToMappers(List<Variable> list, JacksonDeserializer deserializer) {\n        ArrayList<VariableMapper> mappers = new ArrayList<>();\n        for (Variable variable : list) {\n            if (!StringUtil.isBlank(variable.getName())) {\n                mappers.add(new VariableMapper(variable, deserializer));\n            }\n        }\n        return mappers;\n    }\n\n    public List<VariableMapper> getVariables() {\n        return variables;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/engineclient/ActivityEngineClient.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.engineclient;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.data.DataInstance;\nimport org.bonitasoft.engine.bpm.data.DataNotFoundException;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ActivityItem;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APINotFoundException;\nimport org.bonitasoft.web.toolkit.client.common.i18n.T_;\nimport org.bonitasoft.web.toolkit.client.common.texttemplate.Arg;\n\n/**\n * @author Colin PUY\n */\npublic class ActivityEngineClient {\n\n    private final ProcessAPI processAPI;\n\n    public ActivityEngineClient(ProcessAPI processAPI) {\n        this.processAPI = processAPI;\n    }\n\n    public long countFailedActivities() {\n        SearchOptions search = new SearchOptionsBuilder(0, 0)\n                .filter(ActivityInstanceSearchDescriptor.STATE_NAME, ActivityItem.VALUE_STATE_FAILED).done();\n        try {\n            return processAPI.searchActivities(search).getCount();\n        } catch (SearchException e) {\n            throw new APIException(\"Error when counting failed activities\", e);\n        }\n    }\n\n    public DataInstance getDataInstance(String dataName, long activityId) {\n        try {\n            return processAPI.getActivityDataInstance(dataName, activityId);\n        } catch (DataNotFoundException e) {\n            throw new APINotFoundException(new T_(\"Unable to find data instance %dataName% for activity %activityId%\",\n                    new Arg(\"dataName\", dataName), new Arg(\"activityId\", activityId)), e);\n        }\n    }\n\n    public void updateVariables(long activityId, HashMap<String, Serializable> variables) {\n        try {\n            processAPI.updateActivityInstanceVariables(activityId, variables);\n        } catch (UpdateException e) {\n            throw new APIException(new T_(\"Error when updating %activityId% activity variables\",\n                    new Arg(\"activityId\", activityId)), e);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/engineclient/CaseEngineClient.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.engineclient;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.process.ProcessActivationException;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ProcessExecutionException;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.identity.UserNotFoundException;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APINotFoundException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APITooManyRequestException;\nimport org.bonitasoft.web.toolkit.client.common.i18n.T_;\nimport org.bonitasoft.web.toolkit.client.common.texttemplate.Arg;\n\n/**\n * @author Colin PUY\n * @author Elias Ricken de Medeiros\n */\n// TODO migrate all engine methods relating to cases (i.e. especially those in CaseDatastore) in this class\npublic class CaseEngineClient {\n\n    protected final ProcessAPI processAPI;\n\n    public CaseEngineClient(final ProcessAPI processAPI) {\n        this.processAPI = processAPI;\n    }\n\n    public ProcessInstance start(final long userId, final long processId) {\n        return start(userId, processId, null);\n    }\n\n    public ProcessInstance start(final long userId, final long processId, final Map<String, Serializable> variables) {\n        try {\n            if (userId != -1L) {\n                if (variables == null || variables.isEmpty()) {\n                    return processAPI.startProcess(userId, processId);\n                } else {\n                    return processAPI.startProcess(userId, processId, variables);\n                }\n            } else {\n                if (variables == null || variables.isEmpty()) {\n                    return processAPI.startProcess(processId);\n                } else {\n                    return processAPI.startProcess(processId, variables);\n                }\n            }\n        } catch (final ProcessDefinitionNotFoundException e) {\n            throw new APINotFoundException(\n                    new T_(\"Can't start process, process %processId% not found\", new Arg(\"processId\", processId)), e);\n        } catch (final ProcessActivationException e) {\n            throw new APIException(\n                    new T_(\"Can't start process, process %processId% is not enabled\", new Arg(\"processId\", processId)),\n                    e);\n        } catch (final ProcessExecutionException e) {\n            if (e.getRetryAfter() != -1L) {\n                throw new APITooManyRequestException(\n                        new T_(\"Error occurred when starting process %processId%. Case creation limit reached.\",\n                                new Arg(\"processId\", processId)),\n                        e.getRetryAfter());\n            }\n            throw new APIException(\n                    new T_(\"Error occurred when starting process %processId%\", new Arg(\"processId\", processId)), e);\n        } catch (final UserNotFoundException e) {\n            throw new APIException(\n                    new T_(\"Can't start process %processId%, user %userId% not found\", new Arg(\"processId\", processId),\n                            new Arg(\"userId\", userId)),\n                    e);\n        }\n    }\n\n    public long countOpenedCases() {\n        final SearchOptions search = new SearchOptionsBuilder(0, 0).done();\n        try {\n            return processAPI.searchOpenProcessInstances(search).getCount();\n        } catch (final SearchException e) {\n            throw new APIException(\"Error when counting opened cases\", e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/engineclient/CustomUserInfoEngineClient.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.engineclient;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.IdentityAPI;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.identity.CustomUserInfo;\nimport org.bonitasoft.engine.identity.CustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.CustomUserInfoDefinitionCreator;\nimport org.bonitasoft.engine.identity.CustomUserInfoValue;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException;\nimport org.bonitasoft.web.toolkit.client.common.i18n.T_;\nimport org.bonitasoft.web.toolkit.client.common.texttemplate.Arg;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Vincent Elcrin\n */\npublic class CustomUserInfoEngineClient {\n\n    private final IdentityAPI identity;\n\n    public CustomUserInfoEngineClient(IdentityAPI identity) {\n        this.identity = identity;\n    }\n\n    public CustomUserInfoDefinition createDefinition(CustomUserInfoDefinitionCreator creator) {\n        try {\n            return identity.createCustomUserInfoDefinition(creator);\n        } catch (CreationException e) {\n            throw new APIException(new T_(\"An error occurred while creating a definition\"), e);\n        }\n    }\n\n    public void deleteDefinition(long id) {\n        try {\n            identity.deleteCustomUserInfoDefinition(id);\n        } catch (DeletionException e) {\n            throw new APIException(\n                    new T_(\"An error occurred while deleting an item with the id %id%\", new Arg(\"id\", id)), e);\n        }\n    }\n\n    public List<CustomUserInfoDefinition> listDefinitions(int startIndex, int maxResult) {\n        return identity.getCustomUserInfoDefinitions(startIndex, maxResult);\n    }\n\n    public long countDefinitions() {\n        return identity.getNumberOfCustomInfoDefinitions();\n    }\n\n    public List<CustomUserInfo> listCustomInformation(long userId, int startIndex, int maxResult) {\n        return identity.getCustomUserInfo(userId, startIndex, maxResult);\n    }\n\n    public SearchResult<CustomUserInfoValue> searchCustomUserInfoValues(SearchOptions options) {\n        return identity.searchCustomUserInfoValues(options);\n    }\n\n    public CustomUserInfoValue setCustomUserInfoValue(long definitionId, long userId, String value) {\n        try {\n            return identity.setCustomUserInfoValue(definitionId, userId, value);\n        } catch (UpdateException e) {\n            throw new APIItemNotFoundException(org.bonitasoft.web.rest.model.identity.CustomUserInfoDefinition.TOKEN,\n                    APIID.makeAPIID(definitionId, userId));\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/engineclient/CustomUserInfoEngineClientCreator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.engineclient;\n\nimport org.bonitasoft.engine.session.APISession;\n\n/**\n * @author Vincent Elcrin\n */\npublic class CustomUserInfoEngineClientCreator {\n\n    public CustomUserInfoEngineClient create(APISession session) {\n        return new CustomUserInfoEngineClient(new EngineAPIAccessor(session).getIdentityAPI());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/engineclient/EngineAPIAccessor.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.engineclient;\n\nimport org.bonitasoft.engine.api.GroupAPI;\nimport org.bonitasoft.engine.api.IdentityAPI;\nimport org.bonitasoft.engine.api.PageAPI;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.api.ProfileAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.api.TenantAdministrationAPI;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\n\n/**\n * @author Vincent Elcrin\n */\npublic class EngineAPIAccessor {\n\n    private final APISession session;\n\n    public EngineAPIAccessor(final APISession session) {\n        this.session = session;\n    }\n\n    public APISession getSession() {\n        return session;\n    }\n\n    public ProfileAPI getProfileAPI() {\n        try {\n            return TenantAPIAccessor.getProfileAPI(getSession());\n        } catch (final BonitaException e) {\n            throw new APIException(\"Error when getting engine process API\", e);\n        }\n    }\n\n    public ProcessAPI getProcessAPI() {\n        try {\n            return TenantAPIAccessor.getProcessAPI(getSession());\n        } catch (final BonitaException e) {\n            throw new APIException(\"Error when getting engine process API\", e);\n        }\n    }\n\n    public IdentityAPI getIdentityAPI() {\n        try {\n            return TenantAPIAccessor.getIdentityAPI(getSession());\n        } catch (final BonitaException e) {\n            throw new APIException(\"Error when getting engine identity API\", e);\n        }\n    }\n\n    public GroupAPI getGroupAPI() {\n        try {\n            return TenantAPIAccessor.getIdentityAPI(getSession());\n        } catch (final BonitaException e) {\n            throw new APIException(\"Error when getting engine group API\", e);\n        }\n    }\n\n    public PageAPI getPageAPI() {\n        try {\n            return TenantAPIAccessor.getCustomPageAPI(getSession());\n        } catch (final BonitaException e) {\n            throw new APIException(\"Error when getting engine page API\", e);\n        }\n    }\n\n    public TenantAdministrationAPI getTenantAdministrationAPI() {\n        try {\n            return TenantAPIAccessor.getTenantAdministrationAPI(getSession());\n        } catch (final BonitaException e) {\n            throw new APIException(\"Error when getting engine tenant management API\", e);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/engineclient/EngineClientFactory.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.engineclient;\n\n/**\n * @author Vincent Elcrin\n */\npublic class EngineClientFactory {\n\n    private final EngineAPIAccessor apiAccessor;\n\n    public EngineClientFactory(final EngineAPIAccessor apiAccessor) {\n        this.apiAccessor = apiAccessor;\n    }\n\n    public ProfileEngineClient createProfileEngineClient() {\n        return new ProfileEngineClient(apiAccessor.getProfileAPI());\n    }\n\n    public ProfileMemberEngineClient createProfileMemberEngineClient() {\n        return new ProfileMemberEngineClient(apiAccessor.getProfileAPI());\n    }\n\n    public ProcessEngineClient createProcessEngineClient() {\n        return new ProcessEngineClient(apiAccessor.getProcessAPI());\n    }\n\n    public CaseEngineClient createCaseEngineClient() {\n        return new CaseEngineClient(apiAccessor.getProcessAPI());\n    }\n\n    public HumanTaskEngineClient createHumanTaskEngineClient() {\n        return new HumanTaskEngineClient(apiAccessor.getProcessAPI());\n    }\n\n    public ActivityEngineClient createActivityEngineClient() {\n        return new ActivityEngineClient(apiAccessor.getProcessAPI());\n    }\n\n    public UserEngineClient createUserEngineClient() {\n        return new UserEngineClient(apiAccessor.getIdentityAPI());\n    }\n\n    public GroupEngineClient createGroupEngineClient() {\n        return new GroupEngineClient(apiAccessor.getGroupAPI());\n    }\n\n    public TenantManagementEngineClient createTenantManagementEngineClient() {\n        return new TenantManagementEngineClient(apiAccessor.getTenantAdministrationAPI());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/engineclient/GroupEngineClient.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.engineclient;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.GroupAPI;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.identity.Group;\nimport org.bonitasoft.engine.identity.GroupCreator;\nimport org.bonitasoft.engine.identity.GroupCreator.GroupField;\nimport org.bonitasoft.engine.identity.GroupNotFoundException;\nimport org.bonitasoft.engine.identity.GroupUpdater;\nimport org.bonitasoft.web.rest.model.identity.GroupDefinition;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIForbiddenException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APINotFoundException;\nimport org.bonitasoft.web.toolkit.client.common.i18n.T_;\nimport org.bonitasoft.web.toolkit.client.common.texttemplate.Arg;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Paul AMAR\n */\npublic class GroupEngineClient {\n\n    private final GroupAPI groupAPI;\n\n    protected GroupEngineClient(GroupAPI groupAPI) {\n        this.groupAPI = groupAPI;\n    }\n\n    public Group get(Long groupId) {\n        try {\n            return groupAPI.getGroup(groupId);\n        } catch (GroupNotFoundException e) {\n            throw new APIItemNotFoundException(GroupDefinition.TOKEN, APIID.makeAPIID(groupId));\n        }\n    }\n\n    public Group getGroupByPath(String groupPath) {\n        try {\n            return groupAPI.getGroupByPath(groupPath);\n        } catch (GroupNotFoundException e) {\n            throw new APIItemNotFoundException(GroupDefinition.TOKEN, APIID.makeAPIID(groupPath));\n        }\n    }\n\n    public String getPath(String groupId) {\n        try {\n            return groupAPI.getGroup(parseId(groupId)).getPath();\n        } catch (GroupNotFoundException e) {\n            throw new APINotFoundException(new T_(\"Unable to get group path, group not found\"));\n        }\n    }\n\n    private long parseId(String groupId) {\n        try {\n            return Long.parseLong(groupId);\n        } catch (NumberFormatException e) {\n            throw new APIException(\"Illegal argument, groupId must be a number\");\n        }\n    }\n\n    public void delete(List<Long> groupIds) {\n        try {\n            groupAPI.deleteGroups(groupIds);\n        } catch (DeletionException e) {\n            if (e.getCause() instanceof GroupNotFoundException) {\n                throw new APIItemNotFoundException(GroupDefinition.TOKEN);\n            } else {\n                throw new APIException(new T_(\"Error when deleting groups\"), e);\n            }\n        }\n    }\n\n    public Group update(long groupId, GroupUpdater groupUpdater) {\n        try {\n            return groupAPI.updateGroup(groupId, groupUpdater);\n        } catch (GroupNotFoundException e) {\n            throw new APIItemNotFoundException(GroupDefinition.TOKEN, APIID.makeAPIID(groupId));\n        } catch (UpdateException e) {\n            throw new APIException(new T_(\"Error when updating group\"), e);\n        } catch (AlreadyExistsException e) {\n            throw new APIForbiddenException(new T_(\"A group with the name %groupName% already exists\",\n                    new Arg(\"groupName\", groupUpdater.getFields().get(GroupField.NAME))));\n        }\n    }\n\n    public Group create(GroupCreator groupCreator) {\n        try {\n            return groupAPI.createGroup(groupCreator);\n        } catch (AlreadyExistsException e) {\n            throw new APIForbiddenException(new T_(\n                    \"Can't create group. Group '%groupName%' already exists\",\n                    new Arg(\"groupName\", groupCreator.getFields().get(GroupField.NAME))));\n        } catch (CreationException e) {\n            throw new APIException(new T_(\"Error when creating group\"), e);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/engineclient/HumanTaskEngineClient.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.engineclient;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.web.rest.model.bpm.flownode.HumanTaskItem;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\n\n/**\n * @author Colin PUY\n * @author Elias Ricken de Medeiros\n */\npublic class HumanTaskEngineClient {\n\n    private final ProcessAPI processAPI;\n\n    public HumanTaskEngineClient(final ProcessAPI processAPI) {\n        this.processAPI = processAPI;\n    }\n\n    public long countOpenedHumanTasks() {\n        final SearchOptions search = new SearchOptionsBuilder(0, 0)\n                .filter(HumanTaskInstanceSearchDescriptor.STATE_NAME, HumanTaskItem.VALUE_STATE_READY).done();\n        try {\n            return processAPI.searchHumanTaskInstances(search).getCount();\n        } catch (final BonitaException e) {\n            throw new APIException(\"Error when counting opened cases\", e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/engineclient/ProcessEngineClient.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.engineclient;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.data.DataDefinition;\nimport org.bonitasoft.engine.bpm.process.*;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.ProcessInstanceHierarchicalDeletionException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessItem;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIForbiddenException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APINotFoundException;\nimport org.bonitasoft.web.toolkit.client.common.i18n.T_;\nimport org.bonitasoft.web.toolkit.client.common.texttemplate.Arg;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Process engine API client\n * Wrapper to processAPI for all process methods\n *\n * @author Colin PUY\n */\npublic class ProcessEngineClient {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(ProcessEngineClient.class.getName());\n\n    private static final int DELETE_PROCESS_BUNCH_SIZE = 100;\n\n    protected final ProcessAPI processAPI;\n\n    public ProcessEngineClient(final ProcessAPI processAPI) {\n        this.processAPI = processAPI;\n    }\n\n    public ProcessDeploymentInfo getProcessDeploymentInfo(final long processId) {\n        try {\n            return getProcessApi().getProcessDeploymentInfo(processId);\n        } catch (final ProcessDefinitionNotFoundException e) {\n            LOGGER.debug(\"Unable to find process with id \" + processId);\n            throw new APIItemNotFoundException(org.bonitasoft.web.rest.model.bpm.process.ProcessDefinition.TOKEN,\n                    APIID.makeAPIID(processId));\n        }\n    }\n\n    public ProcessAPI getProcessApi() {\n        return processAPI;\n    }\n\n    public ProcessDefinition deploy(final BusinessArchive businessArchive) {\n        try {\n            return getProcessApi().deploy(businessArchive);\n        } catch (final AlreadyExistsException e) {\n            final DesignProcessDefinition processDefinition = businessArchive.getProcessDefinition();\n            throw new APIForbiddenException(new T_(\"Process %appName% in version %version% already exists\",\n                    new Arg(\"appName\", processDefinition.getName()), new Arg(\n                            \"version\", processDefinition.getVersion())),\n                    e);\n        } catch (final V6FormDeployException e) {\n            final DesignProcessDefinition processDefinition = businessArchive.getProcessDefinition();\n            throw new APIException(new T_(\n                    \"Process %appName% in version %version% contains 6.x Legacy artifacts (forms or case overview page). Those are based on Google Web Toolkit (GWT), a technology that is no longer supported by Bonita. To know more, check the documentation.\",\n                    new Arg(\"appName\", processDefinition.getName()),\n                    new Arg(\"version\", processDefinition.getVersion())), e);\n        } catch (final ProcessDeployException e) {\n            throw new APIException(new T_(\"Unable to deploy business archive\"), e);\n        }\n    }\n\n    public void enableProcess(final long processId) {\n        try {\n            getProcessApi().enableProcess(processId);\n        } catch (final BonitaException e) {\n            throw new APIException(new T_(\"Unable to enable process\"), e);\n        }\n    }\n\n    public void disableProcess(final long processId) {\n        try {\n            getProcessApi().disableProcess(processId);\n        } catch (final BonitaException e) {\n            throw new APIException(new T_(\"Unable to disable process\"), e);\n        }\n    }\n\n    public ProcessDeploymentInfo updateProcessDeploymentInfo(final long processId,\n            final ProcessDeploymentInfoUpdater processDeploymentInfoUpdater) {\n        try {\n            getProcessApi().updateProcessDeploymentInfo(processId, processDeploymentInfoUpdater);\n            return getProcessApi().getProcessDeploymentInfo(processId);\n        } catch (final BonitaException e) {\n            throw new APIException(new T_(\"Error when updating process deployment informations\"), e);\n        }\n    }\n\n    public void deleteDisabledProcesses(final List<Long> processIds) {\n        try {\n            for (final Long id : processIds) {\n                deleteProcessInstancesByBunch(id, DELETE_PROCESS_BUNCH_SIZE, processIds);\n                deleteArchivedProcessInstancesByBunch(id, DELETE_PROCESS_BUNCH_SIZE, processIds);\n                getProcessApi().deleteProcessDefinition(id);\n            }\n        } catch (final BonitaException e) {\n            if (e.getCause() instanceof ProcessDefinitionNotFoundException) {\n                throw new APIItemNotFoundException(org.bonitasoft.web.rest.model.bpm.process.ProcessDefinition.TOKEN);\n            } else {\n                throw new APIException(\"Error when deleting some of the processes in the list \" + processIds, e);\n            }\n        }\n    }\n\n    /**\n     * Delete archived process instances by bunch for a given processId\n     */\n    public void deleteArchivedProcessInstancesByBunch(final long processId, final int bunchSize,\n            final List<Long> processesAllowedToBeDeletedIds)\n            throws DeletionException, ProcessDefinitionNotFoundException {\n        long numberOfDeletedArchivedProcessInstances = 0;\n        do {\n            try {\n                numberOfDeletedArchivedProcessInstances = getProcessApi().deleteArchivedProcessInstances(processId, 0,\n                        bunchSize);\n            } catch (final ProcessInstanceHierarchicalDeletionException e) {\n                final long parentProcessInstanceID = e.getProcessInstanceId();\n                final long parentProcessID = getProcessApi()\n                        .getProcessDefinitionIdFromProcessInstanceId(parentProcessInstanceID);\n                if (processesAllowedToBeDeletedIds.contains(parentProcessID)) {\n                    deleteProcessInstancesByBunch(parentProcessID, DELETE_PROCESS_BUNCH_SIZE,\n                            processesAllowedToBeDeletedIds);\n                } else {\n                    LOGGER.warn(\n                            \"Process with ID \" + processId + \" cannot be deleted without also deleting its parent (\"\n                                    + parentProcessID + \").\");\n                }\n            }\n        } while (numberOfDeletedArchivedProcessInstances >= bunchSize);\n    }\n\n    /**\n     * Delete process instances by bunch for a given processId\n     */\n    public void deleteProcessInstancesByBunch(final long processId, final int bunchSize,\n            final List<Long> processesAllowedToBeDeletedIds)\n            throws DeletionException, ProcessDefinitionNotFoundException {\n        long numberOfDeletedProcessInstances = 0;\n        do {\n            try {\n                numberOfDeletedProcessInstances = getProcessApi().deleteProcessInstances(processId, 0, bunchSize);\n            } catch (final ProcessInstanceHierarchicalDeletionException e) {\n                final long parentProcessInstanceID = e.getProcessInstanceId();\n                final long parentProcessID = getProcessApi()\n                        .getProcessDefinitionIdFromProcessInstanceId(parentProcessInstanceID);\n                if (processesAllowedToBeDeletedIds.contains(parentProcessID)) {\n                    deleteProcessInstancesByBunch(parentProcessID, DELETE_PROCESS_BUNCH_SIZE,\n                            processesAllowedToBeDeletedIds);\n                } else {\n                    LOGGER.warn(\n                            \"Process with ID \" + processId + \" cannot be deleted without also deleting its parent (\"\n                                    + parentProcessID + \").\");\n                }\n            }\n        } while (numberOfDeletedProcessInstances >= bunchSize);\n    }\n\n    public SearchResult<ProcessDeploymentInfo> searchProcessDefinitions(final SearchOptions searchOptions) {\n        try {\n            return getProcessApi().searchProcessDeploymentInfos(searchOptions);\n        } catch (final SearchException e) {\n            throw new APIException(\"Error when searching process definition\", e);\n        }\n    }\n\n    public SearchResult<ProcessDeploymentInfo> searchProcessDefinitionsSupervisedBy(final long userId,\n            final SearchOptions searchOptions) {\n        try {\n            return getProcessApi().searchProcessDeploymentInfosSupervisedBy(userId, searchOptions);\n        } catch (final SearchException e) {\n            throw new APIException(\"Error when searching process definition supervised by user \" + userId, e);\n        }\n    }\n\n    public SearchResult<ProcessDeploymentInfo> searchUncategorizedProcessDefinitionsUserCanStart(final long userId,\n            final SearchOptions searchOptions) {\n        try {\n            return getProcessApi().searchUncategorizedProcessDeploymentInfosCanBeStartedBy(userId, searchOptions);\n        } catch (final SearchException e) {\n            throw new APIException(\n                    \"Error when searching uncategorized process definition which can be started by user \" + userId, e);\n        }\n    }\n\n    public SearchResult<ProcessDeploymentInfo> searchRecentlyStartedProcessDefinitions(final long userId,\n            final SearchOptions searchOptions) {\n        try {\n            return getProcessApi().searchProcessDeploymentInfosStartedBy(userId, searchOptions);\n        } catch (final SearchException e) {\n            throw new APIException(\"Error when searching recently started process by user \" + userId, e);\n        }\n    }\n\n    public long countResolvedProcesses() {\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 0);\n        builder.filter(ProcessDeploymentInfoSearchDescriptor.CONFIGURATION_STATE,\n                ProcessItem.VALUE_CONFIGURATION_STATE_RESOLVED);\n        try {\n            return getProcessApi().searchProcessDeploymentInfos(builder.done()).getCount();\n        } catch (final SearchException e) {\n            throw new APIException(\"Error when counting resolved processes\", e);\n        }\n    }\n\n    public SearchResult<ProcessDeploymentInfo> searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(\n            final long supervisorId,\n            final SearchOptions searchOptions) {\n        try {\n            return getProcessApi().searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(supervisorId,\n                    searchOptions);\n        } catch (final SearchException e) {\n            throw new APIException(\"Error when searching process user can start\", e);\n        }\n    }\n\n    public SearchResult<ProcessDeploymentInfo> searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks(\n            final SearchOptions searchOptions) {\n        try {\n            return getProcessApi().searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks(searchOptions);\n        } catch (final SearchException e) {\n            throw new APIException(\"Error when searching process user can start\", e);\n        }\n    }\n\n    public SearchResult<ProcessDeploymentInfo> searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(\n            final long userId,\n            final SearchOptions searchOptions) {\n        try {\n            return getProcessApi().searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(userId,\n                    searchOptions);\n        } catch (final SearchException e) {\n            throw new APIException(\"Error when searching process user can start\", e);\n        }\n    }\n\n    public SearchResult<ProcessDeploymentInfo> searchProcessDeploymentInfos(final long userId,\n            final SearchOptions searchOptions) {\n        try {\n            return getProcessApi().searchProcessDeploymentInfosCanBeStartedBy(userId, searchOptions);\n        } catch (final SearchException e) {\n            throw new APIException(\"Error when searching process user can start\", e);\n        }\n    }\n\n    public List<DataDefinition> getProcessDataDefinitions(final long processId) {\n        try {\n            return processAPI.getProcessDataDefinitions(processId, 0, Integer.MAX_VALUE);\n        } catch (final ProcessDefinitionNotFoundException e) {\n            throw new APINotFoundException(\n                    new T_(\"Unable to get process data definitions, process %processId% not found\",\n                            new Arg(\"processId\", processId)));\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/engineclient/ProfileEngineClient.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.engineclient;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.ProfileAPI;\nimport org.bonitasoft.engine.exception.RetrieveException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.profile.Profile;\nimport org.bonitasoft.engine.profile.ProfileCriterion;\nimport org.bonitasoft.engine.profile.ProfileNotFoundException;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.web.rest.model.portal.profile.ProfileDefinition;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Vincent Elcrin\n */\npublic class ProfileEngineClient {\n\n    private final ProfileAPI profileApi;\n\n    public ProfileEngineClient(ProfileAPI profileApi) {\n        this.profileApi = profileApi;\n    }\n\n    public Profile getProfile(Long id) {\n        try {\n            return profileApi.getProfile(id);\n        } catch (RetrieveException e) {\n            throw new APIException(e);\n        } catch (ProfileNotFoundException e) {\n            throw new APIItemNotFoundException(ProfileDefinition.TOKEN, APIID.makeAPIID(id));\n        }\n    }\n\n    public SearchResult<Profile> searchProfiles(SearchOptions options) {\n        try {\n            return profileApi.searchProfiles(options);\n        } catch (SearchException e) {\n            throw new APIException(e);\n        }\n    }\n\n    public List<Profile> listProfilesForUser(long userId) {\n        try {\n            return profileApi.getProfilesForUser(userId, 0, Integer.MAX_VALUE, ProfileCriterion.ID_ASC);\n        } catch (RetrieveException e) {\n            throw new APIException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/engineclient/ProfileMemberEngineClient.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.engineclient;\n\nimport org.bonitasoft.engine.api.ProfileAPI;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.profile.ProfileMember;\nimport org.bonitasoft.engine.profile.ProfileMemberNotFoundException;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.web.rest.model.portal.profile.ProfileMemberDefinition;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIForbiddenException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException;\nimport org.bonitasoft.web.toolkit.client.common.i18n.T_;\n\n/**\n * @author Vincent Elcrin\n */\npublic class ProfileMemberEngineClient {\n\n    private final ProfileAPI profileApi;\n\n    protected ProfileMemberEngineClient(ProfileAPI profileApi) {\n        this.profileApi = profileApi;\n    }\n\n    public SearchResult<ProfileMember> searchProfileMembers(String memberType, SearchOptions searchOptions) {\n        try {\n            return profileApi.searchProfileMembers(memberType, searchOptions);\n        } catch (SearchException e) {\n            throw new APIException(e);\n        }\n    }\n\n    public ProfileMember createProfileMember(Long profileId, Long userId, Long groupId, Long roleId) {\n        try {\n            return profileApi.createProfileMember(profileId, userId, groupId, roleId);\n        } catch (AlreadyExistsException e) {\n            throw new APIForbiddenException(new T_(\"Profile member already exists\"), e);\n        } catch (CreationException e) {\n            throw new APIException(e);\n        }\n    }\n\n    public void deleteProfileMember(Long id) {\n        try {\n            profileApi.deleteProfileMember(id);\n        } catch (DeletionException e) {\n            if (e.getCause() instanceof ProfileMemberNotFoundException) {\n                throw new APIItemNotFoundException(ProfileMemberDefinition.TOKEN);\n            } else {\n                throw new APIException(e);\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/engineclient/TenantManagementEngineClient.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.engineclient;\n\nimport org.bonitasoft.engine.api.TenantAdministrationAPI;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.i18n.T_;\n\n/**\n * @author Colin PUY\n */\npublic class TenantManagementEngineClient {\n\n    private final TenantAdministrationAPI tenantAdministrationAPI;\n\n    public TenantManagementEngineClient(final TenantAdministrationAPI tenantManagementAPI) {\n        this.tenantAdministrationAPI = tenantManagementAPI;\n    }\n\n    public boolean isTenantPaused() {\n        return tenantAdministrationAPI.isPaused();\n    }\n\n    public void pauseTenant() {\n        if (!isTenantPaused()) {\n            pause();\n        }\n    }\n\n    private void pause() {\n        try {\n            tenantAdministrationAPI.pause();\n        } catch (final UpdateException e) {\n            throw new APIException(new T_(\"Error when pausing BPM services\"), e);\n        }\n    }\n\n    public void resumeTenant() {\n        if (isTenantPaused()) {\n            resume();\n        }\n    }\n\n    private void resume() {\n        try {\n            tenantAdministrationAPI.resume();\n        } catch (final UpdateException e) {\n            throw new APIException(new T_(\"Error when resuming BPM services\"), e);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/engineclient/UserEngineClient.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.engineclient;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.IdentityAPI;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.identity.UserCreator;\nimport org.bonitasoft.engine.identity.UserCreator.UserField;\nimport org.bonitasoft.engine.identity.UserNotFoundException;\nimport org.bonitasoft.engine.identity.UserUpdater;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIForbiddenException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APINotFoundException;\nimport org.bonitasoft.web.toolkit.client.common.i18n.T_;\nimport org.bonitasoft.web.toolkit.client.common.texttemplate.Arg;\n\npublic class UserEngineClient {\n\n    private final IdentityAPI identityAPI;\n\n    public UserEngineClient(IdentityAPI identityAPI) {\n        this.identityAPI = identityAPI;\n    }\n\n    public User update(long userId, UserUpdater userUpdater) {\n        try {\n            return identityAPI.updateUser(userId, userUpdater);\n        } catch (UserNotFoundException e) {\n            throw new APINotFoundException(new T_(\"Can't update user. User not found\"), e);\n        } catch (UpdateException e) {\n            throw new APIException(new T_(\"Error when updating user\"), e);\n        }\n    }\n\n    public User create(UserCreator creator) {\n        try {\n            return identityAPI.createUser(creator);\n        } catch (AlreadyExistsException e) {\n            throw new APIForbiddenException(new T_(\"Can't create user. User '%userName%' already exists\",\n                    new Arg(\"userName\", creator.getFields().get(UserField.NAME))), e);\n        } catch (CreationException e) {\n            throw new APIException(new T_(\"Error when creating user\"), e);\n        }\n    }\n\n    public User get(long userId) {\n        try {\n            return identityAPI.getUser(userId);\n        } catch (UserNotFoundException e) {\n            throw new APINotFoundException(new T_(\"User not found\"), e);\n        }\n    }\n\n    public void delete(List<Long> userIds) {\n        try {\n            identityAPI.deleteUsers(userIds);\n        } catch (DeletionException e) {\n            throw new APIException(new T_(\"Error when deleting users\"), e);\n        }\n    }\n\n    public SearchResult<User> search(SearchOptions searchOptions) {\n        try {\n            return identityAPI.searchUsers(searchOptions);\n        } catch (SearchException e) {\n            throw new APIException(new T_(\"Error when searching users\"), e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/API.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.web.rest.server.framework.api.Datastore;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasAdd;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasDelete;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasUpdate;\nimport org.bonitasoft.web.rest.server.framework.exception.ForbiddenAttributesException;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIMethodNotAllowedException;\nimport org.bonitasoft.web.toolkit.client.common.util.MapUtil;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Séverin Moussel\n */\npublic abstract class API<ITEM extends IItem> {\n\n    protected ItemDefinition<ITEM> itemDefinition = null;\n\n    private final Map<String, Deployer> deployers = new HashMap<>();\n\n    private static Logger LOGGER = LoggerFactory.getLogger(API.class.getName());\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // CONSTRUCTORS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public API() {\n        this.itemDefinition = defineItemDefinition();\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // SETTERS AND GETTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    /**\n     * The ServletCall responsible of this service.\n     */\n    private APIServletCall caller = null;\n\n    /**\n     * Set the caller.\n     *\n     * @param caller\n     *        The ServletCall responsible of this service.\n     */\n    public final void setCaller(final APIServletCall caller) {\n        this.caller = caller;\n    }\n\n    /**\n     * @return the itemDefinition\n     */\n    public final ItemDefinition<ITEM> getItemDefinition() {\n        return this.itemDefinition;\n    }\n\n    /**\n     * Define the ItemDefinition for current class\n     */\n    protected ItemDefinition<ITEM> defineItemDefinition() {\n        // FIXME [API V2] Make this method abstract after suppression of API V1\n        return null;\n    }\n\n    /**\n     * @see org.bonitasoft.web.toolkit.server.ServletCall#getHttpSession()\n     */\n    protected final HttpSession getHttpSession() {\n        return this.caller.getHttpSession();\n    }\n\n    protected String getLocale() {\n        return this.caller.getLocale();\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // ENTRY POINTS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @SuppressWarnings(\"unchecked\")\n    public ITEM runAdd(final IItem item) {\n        // FIXME Activate at end of APIs refactoring\n        // if (!(this instanceof APIHasAdd)) {\n        // throw new APIMethodNotAllowedException(\"POST method not allowed.\");\n        // }\n\n        // Stop there if forbidden attributes are set\n        checkForbiddenAttributes(item.getAttributes());\n\n        // Run specific implementation\n        return add((ITEM) item);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public ITEM add(final ITEM item) {\n        final Datastore datastore = getDefaultDatastore();\n\n        if (!(datastore instanceof DatastoreHasAdd<?>)) {\n            throw new APIMethodNotAllowedException(\"POST method not allowed.\");\n        }\n\n        return ((DatastoreHasAdd<ITEM>) datastore).add(item);\n    }\n\n    public ITEM runUpdate(final APIID id, final Map<String, String> attributes) {\n        // FIXME Activate at end of APIs refactoring\n        // if (!(this instanceof APIHasUpdate)) {\n        // throw new APIMethodNotAllowedException(\"PUT method not allowed.\");\n        // }\n\n        id.setItemDefinition(getItemDefinition());\n\n        // Stop there if forbidden attributes are set\n        checkForbiddenAttributes(attributes);\n\n        // Run specific implementation\n        return this.update(id, attributes);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public ITEM update(final APIID id, final Map<String, String> attributes) {\n        final Datastore datastore = getDefaultDatastore();\n\n        if (datastore == null || !(datastore instanceof DatastoreHasUpdate<?>)) {\n            throw new APIMethodNotAllowedException(\"PUT method not allowed.\");\n        }\n\n        return ((DatastoreHasUpdate<ITEM>) datastore).update(id, attributes);\n\n    }\n\n    public ITEM runGet(final APIID id, final List<String> deploys, final List<String> counters) {\n        // FIXME Activate at end of APIs refactoring\n        // if (!(this instanceof APIHasGet)) {\n        // throw new APIMethodNotAllowedException(\"GET method not allowed.\");\n        // }\n\n        id.setItemDefinition(getItemDefinition());\n\n        final ITEM item = get(id);\n        if (item == null) {\n            throw new APIItemNotFoundException(getItemDefinition().getToken(), id);\n        }\n\n        fillDeploys(item, deploys != null ? deploys : new ArrayList<>());\n        fillCounters(item, counters != null ? counters : new ArrayList<>());\n\n        return item;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public ITEM get(final APIID id) {\n        final Datastore datastore = getDefaultDatastore();\n\n        if (datastore == null || !(datastore instanceof DatastoreHasGet<?>)) {\n            throw new APIMethodNotAllowedException(\"GET method not allowed.\");\n        }\n\n        return ((DatastoreHasGet<ITEM>) datastore).get(id);\n    }\n\n    public ItemSearchResult<ITEM> runSearch(final int page, final int resultsByPage, final String search,\n            final String orders,\n            final Map<String, String> filters, final List<String> deploys, final List<String> counters) {\n\n        // FIXME Activate at end of APIs refactoring\n        // if (!(this instanceof APIHasSearch)) {\n        // throw new APIMethodNotAllowedException(\"SEARCH method not allowed.\");\n        // }\n\n        String realOrders = orders;\n        if (orders == null || orders.length() == 0) {\n            realOrders = defineDefaultSearchOrder();\n\n            // TODO remove this test and exception while the automated unit test over all APis\n            if (realOrders == null) {\n                throw new APIException(\n                        \"No default search order defined. Please, override the defineDefaultSearchOrder method in \"\n                                + this.getClass().toString()\n                                + \".\");\n            }\n        }\n\n        final ItemSearchResult<ITEM> searchResult = search(page, resultsByPage, search, realOrders,\n                filters != null ? filters : new HashMap<>());\n\n        for (final ITEM item : searchResult.getResults()) {\n            fillDeploys(item, deploys != null ? deploys : new ArrayList<>());\n            fillCountersDependingOnFilters(item, counters != null ? counters : List.of(),\n                    filters != null ? filters : Map.of());\n        }\n\n        return searchResult;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public ItemSearchResult<ITEM> search(final int page, final int resultsByPage, final String search,\n            final String orders, final Map<String, String> filters) {\n\n        final Datastore datastore = getDefaultDatastore();\n\n        if (datastore == null || !(datastore instanceof DatastoreHasSearch<?>)) {\n            throw new APIMethodNotAllowedException(\"SEARCH method not allowed.\");\n        }\n\n        return ((DatastoreHasSearch<ITEM>) datastore).search(page, resultsByPage, search, orders, filters);\n    }\n\n    /**\n     * Define the default search order.\n     */\n    public String defineDefaultSearchOrder() {\n        return null;\n    }\n\n    public void runDelete(final List<APIID> ids) {\n        // FIXME Activate at end of APIs refactoring\n        // if (!(this instanceof APIHasDelete)) {\n        // throw new APIMethodNotAllowedException(\"DELETE method not allowed.\");\n        // }\n\n        for (final APIID id : ids) {\n            id.setItemDefinition(getItemDefinition());\n        }\n\n        delete(ids);\n    }\n\n    public void delete(final List<APIID> ids) {\n\n        final Datastore datastore = getDefaultDatastore();\n\n        if (datastore == null || !(datastore instanceof DatastoreHasDelete)) {\n            throw new APIMethodNotAllowedException(\"DELETE method not allowed.\");\n        }\n\n        ((DatastoreHasDelete) datastore).delete(ids);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // AUTOMATED CRUDS BASED ON INTERFACES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    protected Datastore defineDefaultDatastore() {\n        return null;\n    }\n\n    public final Datastore getDefaultDatastore() {\n        return this.defineDefaultDatastore();\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // DEPLOYS AND COUNTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public void addDeployer(final Deployer deployer) {\n        deployers.put(deployer.getDeployedAttribute(), deployer);\n    }\n\n    public Map<String, Deployer> getDeployers() {\n        return Collections.unmodifiableMap(deployers);\n    }\n\n    protected void fillDeploys(final ITEM item, final List<String> deploys) {\n        for (final String attribute : deploys) {\n            deployAttribute(attribute, item);\n        }\n    }\n\n    private void deployAttribute(final String attribute, final ITEM item) {\n        if (deployers.containsKey(attribute)) {\n            try {\n                deployers.get(attribute).deployIn(item);\n            } catch (final Exception e) {\n                if (LOGGER.isDebugEnabled()) {\n                    LOGGER.debug(getFailedDeployMessage(attribute, item), e);\n                } else if (LOGGER.isInfoEnabled()) {\n                    LOGGER.info(getFailedDeployMessage(attribute, item));\n                }\n            }\n        }\n    }\n\n    protected String getFailedDeployMessage(final String attribute, final ITEM item) {\n        return \"Could not deploy attribute '\" + attribute + \"' on item \" + item.toString();\n    }\n\n    protected void fillCounters(final ITEM item, final List<String> counters) {\n        // Do Nothing if not overridden\n    }\n\n    /**\n     * When the values of the filters of a search are required to determine the counters queries,\n     * this method can be overridden instead of fillCounters\n     */\n    protected void fillCountersDependingOnFilters(final ITEM item, final List<String> counters,\n            final Map<String, String> filters) {\n        fillCounters(item, counters);\n        // Do Nothing more if not overridden\n    }\n\n    /**\n     * @param attributeName\n     * @param deploys\n     * @param item\n     */\n    protected final boolean isDeployable(final String attributeName, final List<String> deploys, final IItem item) {\n        final String attributeValue = item.getAttributeValue(attributeName);\n\n        if (deploys.contains(attributeName) && attributeValue != null && !attributeValue.isEmpty()) {\n            try {\n                final long longAttrValue = Long.parseLong(attributeValue);\n                //only positive numeric Ids are supported\n                return longAttrValue > 0L;\n            } catch (final NumberFormatException e) {\n                //non numeric Id are supported\n                return true;\n            }\n        }\n        return false;\n\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // UPLOADS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // FORBIDDEN ATTRIBUTES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Override this method to define attributes that are not allowed to be set manually during ADD or UPDATE.\n     *\n     * @return This method must returns a List of forbidden attributes' name.\n     */\n    protected List<String> defineReadOnlyAttributes() {\n        return null;\n    }\n\n    private void checkForbiddenAttributes(final Map<String, String> attributes) {\n\n        // List forbidden attributes\n        final List<String> forbiddenAttributes = new ArrayList<>();\n        final List<String> definedForbiddenAttributes = defineReadOnlyAttributes();\n        if (definedForbiddenAttributes != null) {\n            forbiddenAttributes.addAll(definedForbiddenAttributes);\n        }\n\n        // No forbidden attributes defined, no need to go further in the check process.\n        if (forbiddenAttributes.size() == 0) {\n            return;\n        }\n\n        // List forbidden attributes found in the request\n        final List<String> errorAttributes = new ArrayList<>();\n        for (final String forbiddenAttribute : forbiddenAttributes) {\n            if (!MapUtil.isBlank(attributes, forbiddenAttribute)) {\n                errorAttributes.add(forbiddenAttribute);\n            }\n        }\n\n        // If at least one is found, throw a ForbiddenAttributesException\n        if (errorAttributes.size() > 0) {\n            throw new ForbiddenAttributesException(errorAttributes);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/APIServletCall.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.bonitasoft.console.common.server.i18n.I18n;\nimport org.bonitasoft.web.rest.server.framework.exception.APIMissingIdException;\nimport org.bonitasoft.web.rest.server.framework.json.JSonSimpleDeserializer;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.rest.server.framework.utils.ParsedRestRequestURI;\nimport org.bonitasoft.web.rest.server.framework.utils.RestRequestURIParser;\nimport org.bonitasoft.web.toolkit.client.common.AbstractTreeNode;\nimport org.bonitasoft.web.toolkit.client.common.Tree;\nimport org.bonitasoft.web.toolkit.client.common.TreeLeaf;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIIncorrectIdException;\nimport org.bonitasoft.web.toolkit.client.common.json.JSonItemReader;\nimport org.bonitasoft.web.toolkit.client.common.json.JSonItemWriter;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ValidatorEngine;\nimport org.bonitasoft.web.toolkit.server.ServletCall;\nimport org.springframework.http.HttpHeaders;\n\n/**\n * @author Séverin Moussel\n * @author Baptiste Mesta\n * @author Fabio Lombardi\n */\npublic class APIServletCall extends ServletCall {\n\n    public static final String PARAMETER_COUNTER = \"n\";\n\n    public static final String PARAMETER_DEPLOY = \"d\";\n\n    public static final String PARAMETER_FILTER = \"f\";\n\n    public static final String PARAMETER_SEARCH = \"s\";\n\n    public static final String PARAMETER_ORDER = \"o\";\n\n    public static final String PARAMETER_LIMIT = \"c\";\n\n    public static final String PARAMETER_PAGE = \"p\";\n\n    public static final String PARAMETER_QUERY = \"q\";\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // REQUEST PARSING\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    protected API<? extends IItem> api;\n\n    private String apiName;\n\n    private String resourceName;\n\n    private APIID id;\n\n    public APIServletCall(final HttpServletRequest request, final HttpServletResponse response) {\n        super(request, response);\n    }\n\n    /**\n     * Constructor for tests\n     */\n    public APIServletCall() {\n        super();\n\n    }\n\n    public String getApiName() {\n        return apiName;\n    }\n\n    public String getResourceName() {\n        return resourceName;\n    }\n\n    public APIID getId() {\n        return id;\n    }\n\n    /**\n     * Read the inputStream and parse it as an IItem compatible with the called API.\n     */\n    private IItem getJSonStreamAsItem() {\n        final IItem item = JSonItemReader.parseItem(getInputStream(), api.getItemDefinition());\n\n        ValidatorEngine.validate(item, false);\n\n        return item;\n    }\n\n    /**\n     * Read elements form the request\n     * <ul>\n     * <li>API tokens</li>\n     * <li>item id (if defined)</li>\n     * <li>parameters</li>\n     * </ul>\n     *\n     * @param request\n     * @param response\n     */\n    @Override\n    protected final void parseRequest(final HttpServletRequest request, final HttpServletResponse response) {\n\n        parsePath(request);\n\n        // Fixes BS-400. This is ugly.\n        I18n.getInstance();\n\n        api = APIs.get(apiName, resourceName);\n        api.setCaller(this);\n\n        super.parseRequest(request, response);\n\n    }\n\n    void parsePath(final HttpServletRequest request) {\n        final ParsedRestRequestURI parsedURI = new RestRequestURIParser(request).parse();\n        APIID resourceQualifiers = parsedURI.getResourceQualifiers();\n        if (resourceQualifiers != null && resourceQualifiers.getIds().size() > 0\n                && isAnyNumberIdNegativeOrZero(resourceQualifiers.getIds())) {\n            throw new APIIncorrectIdException(\"Id must be non-zero positive for \" + parsedURI.getApiName()\n                    + \" on resource \" + parsedURI.getResourceName());\n        }\n        id = resourceQualifiers;\n        apiName = parsedURI.getApiName();\n        resourceName = parsedURI.getResourceName();\n    }\n\n    private boolean isAnyNumberIdNegativeOrZero(List<String> ids) {\n        for (String id : ids) {\n            try {\n                if (Long.parseLong(id) <= 0L) {\n                    return true;\n                }\n            } catch (NumberFormatException e) {\n                // Ignore non-number ids, since they are acceptable\n            }\n        }\n        return false;\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // EXECUTE METHODS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Entry point for GET and SEARCH\n     */\n    @Override\n    public final void doGet() {\n        try {\n            // GET one\n            if (id != null) {\n                output(api.runGet(id, getParameterAsList(PARAMETER_DEPLOY), getParameterAsList(PARAMETER_COUNTER)));\n            } else if (countParameters() == 0) {\n                throw new APIMissingIdException(getRequestURL());\n            }\n            // Search\n            else {\n                final ItemSearchResult<?> result = api.runSearch(Integer.parseInt(getParameter(PARAMETER_PAGE, \"0\")),\n                        Integer.parseInt(getParameter(PARAMETER_LIMIT, \"10\")), getParameter(PARAMETER_SEARCH),\n                        getParameter(PARAMETER_ORDER), parseFilters(getParameterAsList(PARAMETER_FILTER)),\n                        getParameterAsList(PARAMETER_DEPLOY), getParameterAsList(PARAMETER_COUNTER));\n                head(HttpHeaders.CONTENT_RANGE, result.getPage() + \"-\" + result.getLength() + \"/\" + result.getTotal());\n\n                output(result.getResults());\n            }\n        } catch (final APIException e) {\n            e.setApi(apiName);\n            e.setResource(resourceName);\n            throw e;\n        }\n    }\n\n    @Override\n    protected void output(final Object object) {\n        super.output(object);\n    }\n\n    @Override\n    protected void head(final String name, final String value) {\n        super.head(name, value);\n    }\n\n    /**\n     * Entry point for CREATE\n     */\n    @Override\n    public final void doPost() {\n        try {\n            final IItem jSonStreamAsItem = getJSonStreamAsItem();\n            final IItem outputItem = api.runAdd(jSonStreamAsItem);\n\n            output(JSonItemWriter.itemToJSON(outputItem));\n        } catch (final APIException e) {\n            e.setApi(apiName);\n            e.setResource(resourceName);\n            throw e;\n\n        }\n    }\n\n    /**\n     * Entry point for UPDATE\n     */\n    @Override\n    public final void doPut() {\n        try {\n            if (id == null) {\n                throw new APIMissingIdException(getRequestURL());\n            }\n\n            final String inputStream = getInputStream();\n            if (inputStream.length() == 0) {\n                api.runUpdate(id, new HashMap<>());\n                return;\n            }\n\n            Item.setApplyValidatorMandatoryByDefault(false);\n            final IItem item = getJSonStreamAsItem();\n            api.runUpdate(id, getAttributesWithDeploysAsJsonString(item));\n        } catch (final APIException e) {\n            e.setApi(apiName);\n            e.setResource(resourceName);\n            throw e;\n\n        }\n    }\n\n    /**\n     * Get deploys and add them in json representation in map<String, String>\n     * Workaround to be able to have included json objects in main object in PUT request\n     * You have to unserialize them to be able to use them in java representation\n     */\n    private HashMap<String, String> getAttributesWithDeploysAsJsonString(final IItem item) {\n        final HashMap<String, String> map = new HashMap<>();\n        map.putAll(item.getAttributes());\n        for (final Entry<String, IItem> deploy : item.getDeploys().entrySet()) {\n            map.put(deploy.getKey(), deploy.getValue().toJson());\n        }\n        return map;\n    }\n\n    /**\n     * Entry point for DELETE\n     */\n    @Override\n    public final void doDelete() {\n        try {\n            final List<APIID> ids = new ArrayList<>();\n\n            // Using ids in json input stream\n            if (id == null) {\n                final String inputStream = getInputStream();\n                if (inputStream.length() == 0) {\n                    throw new APIMissingIdException(getRequestURL(),\n                            \"Id of the element to delete is missing\");\n                }\n\n                // Parsing ids in Json input stream\n                final AbstractTreeNode<String> tree = JSonSimpleDeserializer.unserializeTree(inputStream);\n\n                if (tree instanceof Tree<?>) {\n                    final List<AbstractTreeNode<String>> nodes = ((Tree<String>) tree).getNodes();\n\n                    for (final AbstractTreeNode<String> node : nodes) {\n                        if (node instanceof Tree<?>) {\n                            ids.add(APIID.makeAPIID(((Tree<String>) node).getValues()));\n                        } else if (node instanceof TreeLeaf<?>) {\n                            ids.add(APIID.makeAPIID(((TreeLeaf<String>) node).getValue()));\n                        } else {\n                            throw new APIMissingIdException(getRequestURL(),\n                                    \"Id of the elements to delete are missing or malformed\");\n                        }\n                    }\n                } else {\n                    throw new APIMissingIdException(getRequestURL(),\n                            \"Id of the elements to delete are missing or malformed\");\n                }\n            }\n\n            // Using id in URL\n            else {\n                ids.add(id);\n            }\n\n            api.runDelete(ids);\n        } catch (final APIException e) {\n            e.setApi(apiName);\n            e.setResource(resourceName);\n            throw e;\n\n        }\n    }\n\n    /**\n     * @param parameters\n     * @return\n     */\n    private Map<String, String> parseFilters(final List<String> parameters) {\n        if (parameters == null) {\n            return null;\n        }\n        final Map<String, String> results = new HashMap<>();\n        for (final String parameter : parameters) {\n            final String[] split = parameter.split(\"=\");\n            if (split.length < 2) {\n                results.put(split[0], null);\n            } else {\n                results.put(split[0], split[1]);\n            }\n        }\n        return results;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/APIs.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework;\n\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\n\n/**\n * @author Séverin Moussel\n */\npublic class APIs {\n\n    public static API<? extends IItem> get(final String apiToken, final String resourceToken) {\n        return RestAPIFactory.getDefaultFactory().defineApis(apiToken, resourceToken);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/Deployer.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework;\n\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\n\n/**\n * @author Vincent Elcrin\n */\npublic interface Deployer {\n\n    String getDeployedAttribute();\n\n    void deployIn(IItem item);\n\n    default boolean isDeployable(final String attributeName, final IItem item) {\n        final String attributeValue = item.getAttributeValue(attributeName);\n\n        if (attributeValue != null && !attributeValue.isEmpty()) {\n            try {\n                final long longAttrValue = Long.valueOf(attributeValue);\n                //only positive numeric Ids are supported\n                return longAttrValue > 0L;\n            } catch (final NumberFormatException e) {\n                //non numeric Id are supported\n                return true;\n            }\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/RestAPIFactory.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework;\n\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\n\n/**\n * @author Séverin Moussel\n */\npublic abstract class RestAPIFactory {\n\n    private static RestAPIFactory defaultFactory = null;\n\n    public static void setDefaultFactory(final RestAPIFactory factory) {\n        defaultFactory = factory;\n    }\n\n    public static RestAPIFactory getDefaultFactory() {\n        return defaultFactory;\n    }\n\n    public abstract API<? extends IItem> defineApis(final String apiToken, final String resourceToken);\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/api/APIHasAdd.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.api;\n\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\n\n/**\n * @author Séverin Moussel\n */\npublic interface APIHasAdd<T extends IItem> {\n\n    T add(final T item);\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/api/APIHasDelete.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.api;\n\nimport java.util.List;\n\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Séverin Moussel\n */\npublic interface APIHasDelete {\n\n    void delete(final List<APIID> ids);\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/api/APIHasGet.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.api;\n\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\n\n/**\n * @author Séverin Moussel\n */\npublic interface APIHasGet<T extends IItem> {\n\n    T get(final APIID id);\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/api/APIHasSearch.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.api;\n\nimport java.util.Map;\n\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\n\n/**\n * @author Séverin Moussel\n */\npublic interface APIHasSearch<T extends IItem> {\n\n    ItemSearchResult<T> search(final int page, final int resultsByPage, final String search, final String orders,\n            final Map<String, String> filters);\n\n    String defineDefaultSearchOrder();\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/api/APIHasUpdate.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.api;\n\nimport java.util.Map;\n\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\n\n/**\n * @author Séverin Moussel\n */\npublic interface APIHasUpdate<T extends IItem> {\n\n    T update(final APIID id, final Map<String, String> attributes);\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/api/Datastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.api;\n\n/**\n * @author Julien Mege\n */\npublic class Datastore {\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/api/DatastoreHasAdd.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.api;\n\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\n\n/**\n * @author Séverin Moussel\n */\npublic interface DatastoreHasAdd<T extends IItem> {\n\n    T add(final T item);\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/api/DatastoreHasDelete.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.api;\n\nimport java.util.List;\n\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Séverin Moussel\n */\npublic interface DatastoreHasDelete {\n\n    void delete(final List<APIID> ids);\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/api/DatastoreHasGet.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.api;\n\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\n\n/**\n * @author Séverin Moussel\n */\npublic interface DatastoreHasGet<T extends IItem> {\n\n    T get(final APIID id);\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/api/DatastoreHasSearch.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.api;\n\nimport java.util.Map;\n\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\n\n/**\n * @author Séverin Moussel\n */\npublic interface DatastoreHasSearch<C extends IItem> {\n\n    ItemSearchResult<C> search(final int page, final int resultsByPage, final String search, final String orders,\n            final Map<String, String> filters);\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/api/DatastoreHasUpdate.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.api;\n\nimport java.util.Map;\n\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\n\n/**\n * @author Séverin Moussel\n */\npublic interface DatastoreHasUpdate<T extends IItem> {\n\n    T update(final APIID id, final Map<String, String> attributes);\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/api/EnumConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.api;\n\n/**\n * @author Vincent Elcrin\n *         Converter doing bridge between engine's enum and web's item\n */\npublic interface EnumConverter<E extends Enum<E>> {\n\n    /**\n     * Convert attribute value stored in items into engine's enum.\n     */\n    E convert(final String attributeValue);\n\n    /**\n     * Convert engine's enum into an item's attribute.\n     */\n    String convert(final E enumValue);\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/exception/APIAttributeException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.exception;\n\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.http.JsonExceptionSerializer;\n\n/**\n * @author Séverin Moussel\n */\npublic class APIAttributeException extends APIException {\n\n    private static final long serialVersionUID = -4611825491187153300L;\n\n    private final String attributeName;\n\n    public APIAttributeException(final String attributeName) {\n        super((Exception) null);\n        this.attributeName = attributeName;\n    }\n\n    public APIAttributeException(final String attributeName, final String message) {\n        super(message);\n        this.attributeName = attributeName;\n    }\n\n    /**\n     * @return the attributeName\n     */\n    public String getAttributeName() {\n        return this.attributeName;\n    }\n\n    @Override\n    protected JsonExceptionSerializer buildJson() {\n        return super.buildJson()\n                .appendAttribute(\"attributeName\", getAttributeName());\n    }\n\n    @Override\n    protected String defaultMessage() {\n        return \"Malformed attribute : \" + this.attributeName;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/exception/APIAttributeMissingException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.exception;\n\n/**\n * @author Séverin Moussel\n */\npublic class APIAttributeMissingException extends APIAttributeException {\n\n    private static final long serialVersionUID = 1254220877755354148L;\n\n    public APIAttributeMissingException(final String attributeName) {\n        super(attributeName);\n    }\n\n    @Override\n    protected String defaultMessage() {\n        return \"Attribute \" + getAttributeName() + \" is missing for API \" + getApi() + \"#\" + getResource();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/exception/APIAttributesException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.exception;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.http.JsonExceptionSerializer;\nimport org.bonitasoft.web.toolkit.client.ui.utils.ListUtils;\n\n/**\n * @author Séverin Moussel\n */\npublic class APIAttributesException extends APIException {\n\n    private static final long serialVersionUID = 7808353918962735013L;\n\n    private final List<String> attributesNames;\n\n    public APIAttributesException(final List<String> attributesNames) {\n        super((Exception) null);\n        this.attributesNames = attributesNames;\n    }\n\n    public APIAttributesException(final String... attributesNames) {\n        super((Exception) null);\n        this.attributesNames = Arrays.asList(attributesNames);\n    }\n\n    public APIAttributesException(final List<String> attributesNames, final String message, final Throwable cause) {\n        super(message, cause);\n        this.attributesNames = attributesNames;\n    }\n\n    public APIAttributesException(final List<String> attributesNames, final String message) {\n        super(message);\n        this.attributesNames = attributesNames;\n    }\n\n    public APIAttributesException(final List<String> attributesNames, final Throwable cause) {\n        super(cause);\n        this.attributesNames = attributesNames;\n    }\n\n    /**\n     * @return the attributesNames\n     */\n    public List<String> getAttributesNames() {\n        return this.attributesNames;\n    }\n\n    @Override\n    protected JsonExceptionSerializer buildJson() {\n        return super.buildJson()\n                .appendAttribute(\"attributesNames\", getAttributesNames());\n    }\n\n    @Override\n    protected String defaultMessage() {\n        return \"Malformed attributes : \" +\n                ListUtils.join(this.attributesNames, \", \");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/exception/APIFileUploadNotFoundException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.exception;\n\nimport org.bonitasoft.web.toolkit.client.common.exception.http.JsonExceptionSerializer;\n\n/**\n * @author Séverin Moussel\n */\npublic class APIFileUploadNotFoundException extends APIAttributeException {\n\n    private static final long serialVersionUID = -5738651518696086103L;\n\n    private final String filePath;\n\n    public APIFileUploadNotFoundException(final String attributeName, final String filePath) {\n        super(attributeName);\n        this.filePath = filePath;\n    }\n\n    /**\n     * @return the filePath\n     */\n    public String getFilePath() {\n        return this.filePath;\n    }\n\n    @Override\n    protected String defaultMessage() {\n        return \"Uploaded file \" + getAttributeName() + \"(\" + getFilePath() + \") not found for API \\\"\" + getApi() + \"#\"\n                + getResource() + \"\\\"\";\n    }\n\n    @Override\n    protected JsonExceptionSerializer buildJson() {\n        return super.buildJson()\n                .appendAttribute(\"filepath\", getFilePath());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/exception/APIFilterEmptyException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.exception;\n\n/**\n * @author Séverin Moussel\n */\npublic class APIFilterEmptyException extends APIFilterException {\n\n    private static final long serialVersionUID = -3372478894238466120L;\n\n    public APIFilterEmptyException(final String filterName) {\n        super(filterName);\n    }\n\n    @Override\n    protected String defaultMessage() {\n        return \"Filter \" + getFilterName() + \" mustn't be empty\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/exception/APIFilterException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.exception;\n\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.http.JsonExceptionSerializer;\n\n/**\n * @author Séverin Moussel\n */\npublic class APIFilterException extends APIException {\n\n    private static final long serialVersionUID = -3674362051034713685L;\n\n    private final String filterName;\n\n    public APIFilterException(final String filterName) {\n        super();\n        this.filterName = filterName;\n    }\n\n    public APIFilterException(final String filterName, final String message, final Throwable cause) {\n        super(message, cause);\n        this.filterName = filterName;\n    }\n\n    public APIFilterException(final String filterName, final String message) {\n        super(message);\n        this.filterName = filterName;\n    }\n\n    public APIFilterException(final String filterName, final Throwable cause) {\n        super(cause);\n        this.filterName = filterName;\n    }\n\n    /**\n     * @return the filterName\n     */\n    public String getFilterName() {\n        return this.filterName;\n    }\n\n    @Override\n    protected JsonExceptionSerializer buildJson() {\n        return super.buildJson()\n                .appendAttribute(\"filterName\", getFilterName());\n    }\n\n    @Override\n    protected String defaultMessage() {\n        return \"Error on filter : \" + getFilterName();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/exception/APIFilterMandatoryException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.exception;\n\n/**\n * @author Séverin Moussel\n */\npublic class APIFilterMandatoryException extends APIFilterException {\n\n    private static final long serialVersionUID = 7067237932975183746L;\n\n    public APIFilterMandatoryException(final String filterName, final String message, final Throwable cause) {\n        super(filterName, message, cause);\n    }\n\n    public APIFilterMandatoryException(final String filterName, final String message) {\n        super(filterName, message);\n    }\n\n    public APIFilterMandatoryException(final String filterName, final Throwable cause) {\n        super(filterName, cause);\n    }\n\n    public APIFilterMandatoryException(final String filterName) {\n        super(filterName);\n    }\n\n    @Override\n    protected String defaultMessage() {\n        return \"Filter \" + getFilterName() + \" is mandatory\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/exception/APIMissingIdException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.exception;\n\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIMalformedUrlException;\n\n/**\n * @author Séverin Moussel\n */\npublic class APIMissingIdException extends APIMalformedUrlException {\n\n    private static final long serialVersionUID = 7387559368848683642L;\n\n    public APIMissingIdException(final String url) {\n        super(url);\n    }\n\n    public APIMissingIdException(final String url, final String message) {\n        super(url, message);\n    }\n\n    @Override\n    protected String defaultMessage() {\n        return \"Id of the item to retrieve is missing in url\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/exception/ForbiddenAttributesException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.exception;\n\nimport java.util.List;\n\nimport org.bonitasoft.web.toolkit.client.ui.utils.ListUtils;\n\n/**\n * @author Séverin Moussel\n */\npublic class ForbiddenAttributesException extends APIAttributesException {\n\n    private static final long serialVersionUID = 8320315567291339726L;\n\n    public ForbiddenAttributesException(final List<String> attributeName) {\n        super(attributeName);\n    }\n\n    @Override\n    protected String defaultMessage() {\n        return \"Manual set of following attributes is forbidden : \" +\n                ListUtils.join(getAttributesNames(), \", \");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/json/JSonSimpleDeserializer.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.json;\n\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.bonitasoft.web.toolkit.client.common.AbstractTreeNode;\nimport org.bonitasoft.web.toolkit.client.common.Tree;\nimport org.bonitasoft.web.toolkit.client.common.TreeIndexed;\nimport org.bonitasoft.web.toolkit.client.common.TreeLeaf;\nimport org.bonitasoft.web.toolkit.client.common.json.JSonUnserializer;\nimport org.json.simple.JSONArray;\nimport org.json.simple.JSONObject;\nimport org.json.simple.parser.JSONParser;\nimport org.json.simple.parser.ParseException;\n\n/**\n * @author Séverin Moussel\n */\npublic class JSonSimpleDeserializer implements JSonUnserializer {\n\n    private static JSonSimpleDeserializer INSTANCE = null;\n\n    private static JSonSimpleDeserializer getInstance() {\n        if (INSTANCE == null) {\n            INSTANCE = new JSonSimpleDeserializer();\n        }\n        return INSTANCE;\n    }\n\n    public static AbstractTreeNode<String> unserializeTree(final String json) {\n\n        return getInstance()._unserializeTree(json);\n    }\n\n    @Override\n    public AbstractTreeNode<String> _unserializeTree(final String json) {\n        try {\n            if (json.length() == 0) {\n                return null;\n            }\n\n            return unserializeTreeNode(new JSONParser().parse(json));\n        } catch (final ParseException e) {\n            throw new IllegalArgumentException(\"Can't parse JSon\", e);\n        }\n    }\n\n    private AbstractTreeNode<String> unserializeTreeNode(final Object object) {\n        if (object instanceof JSONObject) {\n            return unserializeTreeNode((JSONObject) object);\n        } else if (object instanceof JSONArray) {\n            return unserializeTreeNode((JSONArray) object);\n        } else if (object instanceof Boolean) {\n            return new TreeLeaf<>(((Boolean) object).booleanValue() ? \"1\" : \"0\");\n        }\n        return new TreeLeaf<>(object.toString());\n    }\n\n    private TreeIndexed<String> unserializeTreeNode(final JSONObject object) {\n        final TreeIndexed<String> result = new TreeIndexed<>();\n\n        @SuppressWarnings(\"rawtypes\")\n        final Iterator iter = object.entrySet().iterator();\n        while (iter.hasNext()) {\n            @SuppressWarnings(\"rawtypes\")\n            final Map.Entry entry = (Entry) iter.next();\n            result.addNode(entry.getKey().toString(), unserializeTreeNode(entry.getValue()));\n        }\n        return result;\n    }\n\n    private Tree<String> unserializeTreeNode(final JSONArray array) {\n        final Tree<String> result = new Tree<>();\n        final int size = array.size();\n        for (int i = 0; i < size; i++) {\n            result.addNode(unserializeTreeNode(array.get(i)));\n        }\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/json/JacksonDeserializer.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.json;\n\nimport java.io.IOException;\nimport java.util.List;\n\nimport com.fasterxml.jackson.core.JsonParseException;\nimport com.fasterxml.jackson.databind.JavaType;\nimport com.fasterxml.jackson.databind.JsonMappingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n;\n\n/**\n * Jackson ObjectMapper Wrapper to fit our needs\n *\n * @author Colin PUY\n */\npublic class JacksonDeserializer {\n\n    private final ObjectMapper mapper = new ObjectMapper();\n\n    public <T> T deserialize(String json, Class<T> clazz) {\n        return deserialize(json, mapper.getTypeFactory().constructType(clazz));\n    }\n\n    public <T> List<T> deserializeList(String json, Class<T> clazz) {\n        return deserialize(json, mapper.getTypeFactory().constructCollectionType(List.class, clazz));\n    }\n\n    private <T> T deserialize(String json, JavaType javaType) {\n        try {\n            return mapper.readValue(json.getBytes(), javaType);\n        } catch (JsonParseException e) {\n            throw new APIException(AbstractI18n.t_(\"Can't parse json, non-well formed content\"), e);\n        } catch (JsonMappingException e) {\n            throw new APIException(AbstractI18n.t_(\"Json can't be mapped to \" + javaType.getRawClass().getName()), e);\n        } catch (IOException e) {\n            // should never appear\n            throw new APIException(e);\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public <T> T convertValue(Object fromValue, Class<?> toValue) {\n        return (T) mapper.convertValue(fromValue, toValue);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/json/JacksonSerializer.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.json;\n\nimport java.io.IOException;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\n\n/**\n * @author Fabio Lombardi\n */\npublic class JacksonSerializer {\n\n    private final ObjectMapper mapper = new ObjectMapper();\n\n    public String serialize(Object obj) throws IOException {\n        try {\n            return mapper.writeValueAsString(obj);\n        } catch (Throwable e) {\n            e.printStackTrace();\n            throw new RuntimeException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/search/ISearchDirection.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.search;\n\npublic interface ISearchDirection {\n\n    String SORT_ORDER_ASCENDING = \" ASC\";\n    String SORT_ORDER_DESCENDING = \" DESC\";\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/search/ItemSearchResult.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.search;\n\nimport java.util.List;\n\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APISearchIndexOutOfRange;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\n\n/**\n * @author Séverin Moussel\n */\npublic class ItemSearchResult<T extends IItem> {\n\n    private final int page;\n\n    private final int length;\n\n    private final long total;\n\n    private final List<T> results;\n\n    public ItemSearchResult(final int page, final int length, final long total, final List<T> results) {\n        this.page = page;\n        this.length = length;\n        this.total = total;\n        this.results = results;\n\n        if (page < 0 || page > total) {\n            throw new APISearchIndexOutOfRange(page);\n        }\n    }\n\n    /**\n     * @return the page\n     */\n    public int getPage() {\n        return this.page;\n    }\n\n    /**\n     * @return the length\n     */\n    public int getLength() {\n        return this.length;\n    }\n\n    /**\n     * @return the total\n     */\n    public long getTotal() {\n        return this.total;\n    }\n\n    /**\n     * @return the results\n     */\n    public List<T> getResults() {\n        return this.results;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/servlet/APIServlet.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.servlet;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.bonitasoft.web.rest.server.framework.APIServletCall;\nimport org.bonitasoft.web.rest.server.framework.RestAPIFactory;\nimport org.bonitasoft.web.toolkit.client.ItemDefinitionFactory;\nimport org.bonitasoft.web.toolkit.server.ServletCall;\nimport org.bonitasoft.web.toolkit.server.servlet.ToolkitHttpServlet;\n\n/**\n * @author Séverin Moussel\n */\npublic abstract class APIServlet extends ToolkitHttpServlet {\n\n    private static final long serialVersionUID = 1852124460966605504L;\n\n    @Override\n    protected void initializeToolkit() {\n        super.initializeToolkit();\n        ItemDefinitionFactory.setDefaultFactory(defineApplicatioFactoryCommon());\n        RestAPIFactory.setDefaultFactory(defineApplicatioFactoryServer());\n    }\n\n    @Override\n    protected ServletCall defineServletCall(final HttpServletRequest req, final HttpServletResponse resp) {\n        return new APIServletCall(req, resp);\n    }\n\n    protected abstract ItemDefinitionFactory defineApplicatioFactoryCommon();\n\n    protected abstract RestAPIFactory defineApplicatioFactoryServer();\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/utils/FilePathBuilder.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.utils;\n\nimport java.io.File;\n\n/**\n * @author Séverin Moussel\n */\npublic class FilePathBuilder {\n\n    private final StringBuilder path = new StringBuilder();\n\n    public FilePathBuilder(final String path) {\n        super();\n        insert(path);\n    }\n\n    public FilePathBuilder append(final String path) {\n        // If null or empty, do nothing\n        if (path == null || path.isEmpty()) {\n            return this;\n        }\n\n        this.path.append(File.separator);\n\n        insert(path);\n\n        return this;\n    }\n\n    /**\n     * @param path\n     */\n    private void insert(final String path) {\n        if (path.endsWith(File.separator)) {\n            this.path.append(path, 0, path.length() - 1);\n        } else {\n            this.path.append(path);\n        }\n    }\n\n    @Override\n    public String toString() {\n        return this.path.toString();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/utils/ParsedRestRequestURI.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.utils;\n\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * Immutable result of parsing a REST API request URI.\n */\npublic class ParsedRestRequestURI {\n\n    private final String apiName;\n    private final String resourceName;\n    private final APIID resourceQualifiers;\n\n    public ParsedRestRequestURI(String apiName, String resourceName, APIID resourceQualifiers) {\n        this.apiName = apiName;\n        this.resourceName = resourceName;\n        this.resourceQualifiers = resourceQualifiers;\n    }\n\n    public String getApiName() {\n        return apiName;\n    }\n\n    public String getResourceName() {\n        return resourceName;\n    }\n\n    public APIID getResourceQualifiers() {\n        return resourceQualifiers;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/utils/RestRequestURIParser.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.utils;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport org.bonitasoft.web.rest.server.api.AbstractRESTController;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIMalformedUrlException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * Simple parser that extract parameters from a call to the REST api\n *\n * @author Baptiste Mesta\n */\npublic class RestRequestURIParser {\n\n    //This constant should match the servlet name of DispatcherServlet as declared in servlet context\n    public static final String SPRING_REST_SERVLET_NAME = \"SpringRest\";\n    //This constant should match the servlet name of CustomPageServlet as declared in servlet context\n    public static final String CUSTOM_PAGE_SERVLET_NAME = \"CustomPageServlet\";\n\n    private static final String API_SEGMENT = \"API\";\n    private static final String API_TOOLKIT_SEGMENT = \"APIToolkit\";\n    private static final String API_SPRING_INTERNAL_SEGMENT = AbstractRESTController.API_SPRING_INTERNAL;\n\n    private final HttpServletRequest request;\n\n    public RestRequestURIParser(final HttpServletRequest request) {\n        this.request = request;\n    }\n\n    public ParsedRestRequestURI parse() {\n        if (isSpringMvcServlet() || isToolkitServlet() || isCustomPageServlet()) {\n            return parseSpringMvcOrToolkitRequest();\n        }\n        if (request.getPathInfo() != null) {\n            // Non-Spring, non-toolkit wildcard servlet: parse from servletPath + pathInfo,\n            // with special handling to preserve the first path segment as apiName.\n            return parseWildcardServletRequest();\n        }\n        return parseDirectPathRequest();\n    }\n\n    private boolean isToolkitServlet() {\n        String servletPath = request.getServletPath();\n        return (\"/\" + API_TOOLKIT_SEGMENT).equals(servletPath);\n    }\n\n    private boolean isSpringMvcServlet() {\n        return SPRING_REST_SERVLET_NAME.equals(request.getHttpServletMapping().getServletName());\n    }\n\n    /**\n     * Checks whether the request is served by CustomPageServlet, mapped to /portal/custom-page/*.\n     * This assumes the servlet is mapped to that single wildcard prefix; if it were ever mapped\n     * to a deeper sub-path (e.g. /portal/custom-page/API/avatars/*), the request would not\n     * carry this servlet name and would instead be handled by parseWildcardServletRequest().\n     */\n    private boolean isCustomPageServlet() {\n        return CUSTOM_PAGE_SERVLET_NAME.equals(request.getHttpServletMapping().getServletName());\n    }\n\n    /**\n     * For Spring MVC DispatcherServlet (servletName == \"SpringRest\"),\n     * CustomPageServlet (servletName == \"CustomPageServlet\"),\n     * and BonitaRestAPIServlet (/APIToolkit/*).\n     * <p>\n     * Handles both exact-match mappings (e.g. /API/system/maintenance where\n     * servletPath = full path, pathInfo = null) and wildcard mappings\n     * (e.g. /APISpringInternal/* where servletPath = /APISpringInternal,\n     * pathInfo = /bpm/case/42; or /API/bpm/activityVariable/* where\n     * servletPath = /API/bpm/activityVariable, pathInfo = /3/myVar).\n     * Concatenating servletPath + pathInfo always produces the full request path.\n     * <p>\n     * All three servlet types delegate to parseUsingApiSegmentLookup which\n     * locates the API/APIToolkit/APISpringInternal segment dynamically.\n     */\n    private ParsedRestRequestURI parseSpringMvcOrToolkitRequest() {\n        String pathInfo = request.getPathInfo();\n        return parseUsingApiSegmentLookup(\n                request.getServletPath() + (pathInfo != null ? pathInfo : \"\"));\n    }\n\n    /**\n     * Other wildcard servlets not recognised by name (pathInfo != null, not Spring MVC,\n     * not toolkit, not custom-page).\n     * <p>\n     * If the path contains an API segment (e.g. /portal/custom-page/API/avatars/*),\n     * parsing starts from that segment. Otherwise (e.g. /services/*), the servletPath\n     * is just a mapping prefix and is skipped — parsing uses pathInfo alone.\n     */\n    private ParsedRestRequestURI parseWildcardServletRequest() {\n        String pathInfo = request.getPathInfo();\n        String fullPath = request.getServletPath() + pathInfo;\n        String[] path = fullPath.split(\"/\");\n        int apiIndex = findApiSegmentIndex(Arrays.asList(path));\n        if (apiIndex >= 0) {\n            // When the servlet path includes a prefix before the API segment\n            // (e.g. /portal/custom-page/API/avatars/*), we must skip the prefix\n            // and parse from the API segment to match permission entries like \"GET|API/avatars\".\n            return parseRequest(path, apiIndex);\n        }\n        // No API segment: servletPath is just the mapping prefix (e.g. /services).\n        // Parse the resource structure from pathInfo alone.\n        String[] pathInfoSegments = pathInfo.split(\"/\");\n        return parseRequest(pathInfoSegments, 1);\n    }\n\n    /**\n     * Exact-match servlets or default servlet (pathInfo is null, not Spring MVC).\n     * Exact-match servlets: URLs like /API/documentDownload or /portal/imageUpload\n     * that literally match a web.xml url-pattern. The container sets servletPath to\n     * the full path and pathInfo to null.\n     * Default servlet: URLs like /API/bpm/case/42 that don't match any specific\n     * servlet mapping. The container routes these to the default servlet, which also\n     * sets servletPath = full path and pathInfo = null. This case arises because\n     * RestAPIAuthorizationFilter runs before UrlRewriteFilter in the filter chain\n     * (see web.xml filter-mapping order): at auth time, the request still carries\n     * its original URL. UrlRewriteFilter will later rewrite and forward to\n     * /APIToolkit/* or /APISpringInternal/*, but this parser has already extracted\n     * the API name and resource from the original URL.\n     */\n    private ParsedRestRequestURI parseDirectPathRequest() {\n        return parseUsingApiSegmentLookup(request.getServletPath());\n    }\n\n    /**\n     * Shared logic for Spring MVC, toolkit, and direct-path servlets: build the full path,\n     * locate the API segment dynamically, then dispatch to parseRequest.\n     */\n    private ParsedRestRequestURI parseUsingApiSegmentLookup(String fullPath) {\n        String[] path = fullPath.split(\"/\");\n        //find the API segment index to support multiple URL patterns (index 0 is always \"\" as fullPath starts with \"/\")\n        int apiIndex = findApiSegmentIndex(Arrays.asList(path));\n\n        if (apiIndex >= 0) {\n            int segmentsAfterApi = path.length - apiIndex - 1;\n            if (segmentsAfterApi >= 2) {\n                // Deep URL: /API/bpm/case/42 → apiName=bpm, resource=case\n                return parseRequest(path, apiIndex + 1);\n            }\n            if (segmentsAfterApi == 1) {\n                // Flat URL: /API/documentDownload → apiName=API, resource=documentDownload\n                return parseRequest(path, apiIndex);\n            }\n            throw new APIMalformedUrlException(request.getRequestURL().toString(),\n                    \"Missing API or resource name in request URL\");\n        }\n        // No API segment: /portal/imageUpload, /services/something, etc.\n        if (path.length < 3) {\n            //all the URL we support always at least have 2 path segments and index 0 is \"\"\n            throw new APIMalformedUrlException(request.getRequestURL().toString(),\n                    \"Missing API path segment in request URL\");\n        }\n        return parseRequest(path, 1);\n    }\n\n    /**\n     * Find the index of the API prefix segment in the URL path.\n     * Handles /API/..., /APIToolkit/..., and /APISpringInternal/... URL patterns.\n     */\n    private int findApiSegmentIndex(List<String> segments) {\n        int index = segments.indexOf(API_SEGMENT);\n        if (index < 0) {\n            index = segments.indexOf(API_TOOLKIT_SEGMENT);\n        }\n        if (index < 0) {\n            // /API/... URLs rewritten to /APISpringInternal/... by UrlRewriteFilter\n            index = segments.indexOf(API_SPRING_INTERNAL_SEGMENT);\n        }\n        return index;\n    }\n\n    protected ParsedRestRequestURI parseRequest(String[] path, int indexOfAPINameSegment) {\n        int minimalNumberOfPathSegments = indexOfAPINameSegment + 2;\n        if (path.length < minimalNumberOfPathSegments) {\n            throw new APIMalformedUrlException(request.getRequestURL().toString(),\n                    \"Missing API or resource name in request URL\");\n        }\n        String apiName = path[indexOfAPINameSegment];\n        String resourceName = path[indexOfAPINameSegment + 1];\n        // Read id (if defined)\n        APIID resourceQualifiers;\n        if (path.length > minimalNumberOfPathSegments) {\n            final List<String> pathList = Arrays.asList(path);\n            resourceQualifiers = APIID.makeAPIID(pathList.subList(minimalNumberOfPathSegments, pathList.size()));\n        } else {\n            resourceQualifiers = null;\n        }\n        return new ParsedRestRequestURI(apiName, resourceName, resourceQualifiers);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/utils/SearchOptionsBuilderUtil.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.utils;\n\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\n\n/**\n * @author Haojie Yuan\n */\npublic final class SearchOptionsBuilderUtil {\n\n    private SearchOptionsBuilderUtil() {\n        // Utility class\n    }\n\n    /**\n     * build SearchOptionsBuilder\n     *\n     * @deprecated use {@link org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator} instead\n     */\n    @Deprecated(since = \"6.0\")\n    public static SearchOptionsBuilder buildSearchOptions(final int pageIndex, final int numberOfResults,\n            final String sort, final String search) {\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(computeIndex(pageIndex, numberOfResults),\n                numberOfResults);\n        if (sort != null) {\n            final String[] order = sort.split(\" \");\n            if (order.length == 2) {\n                builder.sort(order[0], Order.valueOf(order[1].toUpperCase()));\n            }\n        }\n        if (search != null && !search.isEmpty()) {\n            builder.searchTerm(search);\n        }\n        return builder;\n    }\n\n    public static int computeIndex(int page, int resultsByPage) {\n        return page * resultsByPage;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/utils/converter/ConversionException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.utils.converter;\n\n/**\n * @author Colin PUY\n */\n@SuppressWarnings(\"serial\")\npublic class ConversionException extends Exception {\n\n    public ConversionException(String message, Throwable cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/utils/converter/Converter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.utils.converter;\n\nimport java.io.Serializable;\n\n/**\n * @author Colin PUY\n */\npublic interface Converter<E extends Serializable> {\n\n    E convert(String convert) throws ConversionException;\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/utils/converter/ConverterFactory.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.utils.converter;\n\nimport java.util.Date;\n\nimport org.bonitasoft.web.rest.server.framework.utils.converter.typed.BooleanConverter;\nimport org.bonitasoft.web.rest.server.framework.utils.converter.typed.DateConverter;\nimport org.bonitasoft.web.rest.server.framework.utils.converter.typed.DoubleConverter;\nimport org.bonitasoft.web.rest.server.framework.utils.converter.typed.IntegerConverter;\nimport org.bonitasoft.web.rest.server.framework.utils.converter.typed.LongConverter;\nimport org.bonitasoft.web.rest.server.framework.utils.converter.typed.StringConverter;\n\n/**\n * @author Colin PUY\n */\npublic class ConverterFactory {\n\n    public Converter<?> createConverter(String className) {\n        if (isClass(String.class, className)) {\n            return new StringConverter();\n        } else if (isClass(Date.class, className)) {\n            return new DateConverter();\n        } else if (isClass(Double.class, className)) {\n            return new DoubleConverter();\n        } else if (isClass(Long.class, className)) {\n            return new LongConverter();\n        } else if (isClass(Boolean.class, className)) {\n            return new BooleanConverter();\n        } else if (isClass(Integer.class, className)) {\n            return new IntegerConverter();\n        }\n        throw new UnsupportedOperationException(\"Canno't create converter for class name : \" + className);\n    }\n\n    private boolean isClass(Class<?> classe, String className) {\n        return classe.getName().equals(className);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/utils/converter/TypeConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.utils.converter;\n\nimport java.io.Serializable;\n\n/**\n * @author Colin PUY\n */\npublic class TypeConverter {\n\n    private final ConverterFactory factory;\n\n    // TODO delete this constructor when we'll be using DIP\n    public TypeConverter() {\n        this(new ConverterFactory());\n    }\n\n    public TypeConverter(ConverterFactory factory) {\n        this.factory = factory;\n    }\n\n    public Serializable convert(String className, String convert) throws ConversionException {\n        return factory.createConverter(className).convert(convert);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/utils/converter/typed/BooleanConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.utils.converter.typed;\n\nimport org.bonitasoft.web.rest.server.framework.utils.converter.Converter;\n\n/**\n * @author Colin PUY\n */\npublic class BooleanConverter implements Converter<Boolean> {\n\n    @Override\n    public Boolean convert(String toBeConverted) {\n        if (toBeConverted == null || toBeConverted.isEmpty()) {\n            return null;\n        }\n        return Boolean.valueOf(toBeConverted);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/utils/converter/typed/DateConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.utils.converter.typed;\n\nimport java.text.DateFormatSymbols;\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.Locale;\n\nimport org.bonitasoft.web.rest.server.framework.utils.converter.ConversionException;\nimport org.bonitasoft.web.rest.server.framework.utils.converter.Converter;\n\n/**\n * @author Nicolas Tith\n */\npublic class DateConverter implements Converter<Date> {\n\n    @Override\n    public Date convert(String toBeConverted) throws ConversionException {\n        if (toBeConverted == null || toBeConverted.isEmpty()) {\n            return null;\n        }\n        try {\n            SimpleDateFormat formatter = new SimpleDateFormat(\"EEE MMM dd HH:mm:ss z yyyy\",\n                    DateFormatSymbols.getInstance(Locale.ENGLISH));\n            return formatter.parse(toBeConverted);\n        } catch (ParseException e) {\n            throw new ConversionException(toBeConverted + \" cannot be converted to Date\", e);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/utils/converter/typed/DoubleConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.utils.converter.typed;\n\nimport org.bonitasoft.web.rest.server.framework.utils.converter.ConversionException;\nimport org.bonitasoft.web.rest.server.framework.utils.converter.Converter;\n\n/**\n * @author Colin PUY\n */\npublic class DoubleConverter implements Converter<Double> {\n\n    @Override\n    public Double convert(String toBeConverted) throws ConversionException {\n        if (toBeConverted == null || toBeConverted.isEmpty()) {\n            return null;\n        }\n        try {\n            return Double.parseDouble(toBeConverted);\n        } catch (NumberFormatException e) {\n            throw new ConversionException(toBeConverted + \" cannot be converted to Double\", e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/utils/converter/typed/IntegerConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.utils.converter.typed;\n\nimport org.bonitasoft.web.rest.server.framework.utils.converter.ConversionException;\nimport org.bonitasoft.web.rest.server.framework.utils.converter.Converter;\n\n/**\n * @author Colin PUY\n */\npublic class IntegerConverter implements Converter<Integer> {\n\n    @Override\n    public Integer convert(String toBeConverted) throws ConversionException {\n        if (toBeConverted == null || toBeConverted.isEmpty()) {\n            return null;\n        }\n        try {\n            return Integer.parseInt(toBeConverted);\n        } catch (NumberFormatException e) {\n            throw new ConversionException(toBeConverted + \" cannot be converted to Integer\", e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/utils/converter/typed/LongConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.utils.converter.typed;\n\nimport org.bonitasoft.web.rest.server.framework.utils.converter.ConversionException;\nimport org.bonitasoft.web.rest.server.framework.utils.converter.Converter;\n\n/**\n * @author Colin PUY\n */\npublic class LongConverter implements Converter<Long> {\n\n    @Override\n    public Long convert(String toBeConverted) throws ConversionException {\n        if (toBeConverted == null || toBeConverted.isEmpty()) {\n            return null;\n        }\n        try {\n            return Long.parseLong(toBeConverted);\n        } catch (NumberFormatException e) {\n            throw new ConversionException(toBeConverted + \" cannot be converted to Long\", e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/utils/converter/typed/StringConverter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.utils.converter.typed;\n\nimport org.bonitasoft.web.rest.server.framework.utils.converter.Converter;\n\n/**\n * @author Colin PUY\n */\npublic class StringConverter implements Converter<String> {\n\n    @Override\n    public String convert(String convert) {\n        return convert;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/utils/BonitaJacksonModuleProvider.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.utils;\n\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.OffsetDateTime;\n\nimport com.fasterxml.jackson.databind.Module;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.module.SimpleModule;\nimport org.bonitasoft.engine.bdm.serialization.CustomLocalDateDeserializer;\nimport org.bonitasoft.engine.bdm.serialization.CustomLocalDateSerializer;\nimport org.bonitasoft.engine.bdm.serialization.CustomLocalDateTimeDeserializer;\nimport org.bonitasoft.engine.bdm.serialization.CustomLocalDateTimeSerializer;\nimport org.bonitasoft.engine.bdm.serialization.CustomOffsetDateTimeDeserializer;\nimport org.bonitasoft.engine.bdm.serialization.CustomOffsetDateTimeSerializer;\nimport org.bonitasoft.engine.identity.User;\n\n/**\n * Provides Jackson module configuration for Bonita REST APIs.\n * <p>\n * This ensures backward compatibility with the legacy JSON format, including:\n * <ul>\n * <li>_string suffix fields for numeric IDs (for JavaScript precision)</li>\n * <li>Custom date/time serialization</li>\n * </ul>\n * <p>\n * Used by both Spring MVC configuration and test infrastructure.\n */\npublic final class BonitaJacksonModuleProvider {\n\n    private BonitaJacksonModuleProvider() {\n        // Utility class\n    }\n\n    /**\n     * Creates a Jackson module with Bonita custom serializers.\n     * Includes custom serializers for _string suffix fields and date/time handling.\n     *\n     * @return the configured Jackson module\n     */\n    public static Module createBonitaModule() {\n        SimpleModule bonitaModule = new SimpleModule(\"BonitaModule\");\n\n        // Add custom serializers for _string suffix fields\n        bonitaModule.addSerializer(new DataInstanceSerializer());\n        bonitaModule.addSerializer(new TimerEventTriggerInstanceSerializer());\n\n        // Date/time serializers and deserializers\n        bonitaModule.addDeserializer(LocalDate.class, new CustomLocalDateDeserializer());\n        bonitaModule.addDeserializer(LocalDateTime.class, new CustomLocalDateTimeDeserializer());\n        bonitaModule.addDeserializer(OffsetDateTime.class, new CustomOffsetDateTimeDeserializer());\n        bonitaModule.addSerializer(LocalDate.class, new CustomLocalDateSerializer());\n        bonitaModule.addSerializer(LocalDateTime.class, new CustomLocalDateTimeSerializer());\n        bonitaModule.addSerializer(OffsetDateTime.class, new CustomOffsetDateTimeSerializer());\n\n        // Until User extends BaseRestElement upstream, patch its id to serialize as a JSON string.\n        bonitaModule.setMixInAnnotation(User.class, UserMixIn.class);\n\n        return bonitaModule;\n    }\n\n    /**\n     * Configures the given ObjectMapper with Bonita custom serializers.\n     *\n     * @param mapper the ObjectMapper to configure\n     */\n    public static void configureObjectMapper(ObjectMapper mapper) {\n        mapper.registerModule(createBonitaModule());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/utils/DataInstanceSerializer.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.utils;\n\nimport java.io.IOException;\n\nimport com.fasterxml.jackson.core.JsonGenerator;\nimport com.fasterxml.jackson.databind.JsonSerializer;\nimport com.fasterxml.jackson.databind.SerializerProvider;\nimport org.bonitasoft.engine.bpm.data.impl.DataInstanceImpl;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class DataInstanceSerializer extends JsonSerializer<DataInstanceImpl> {\n\n    final JacksonSerializerHelper jacksonSerializerHelper = new JacksonSerializerHelper();\n\n    @Override\n    public void serialize(final DataInstanceImpl value, final JsonGenerator jgen, final SerializerProvider provider)\n            throws IOException {\n        jgen.writeStartObject();\n        jgen.writeObjectField(\"name\", value.getName());\n        jgen.writeObjectField(\"description\", value.getDescription());\n        jgen.writeObjectField(\"transientData\", value.isTransientData());\n        jgen.writeObjectField(\"className\", value.getClassName());\n        jgen.writeObjectField(\"containerType\", value.getContainerType());\n\n        jacksonSerializerHelper.writeNumberField(jgen, \"id\", value.getId());\n        jacksonSerializerHelper.writeNumberField(jgen, \"containerId\", value.getContainerId());\n        jacksonSerializerHelper.writeNumberField(jgen, \"value\", value.getValue());\n        jgen.writeEndObject();\n    }\n\n    @Override\n    public Class<DataInstanceImpl> handledType() {\n        return DataInstanceImpl.class;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/utils/JacksonSerializerHelper.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.utils;\n\nimport java.io.IOException;\nimport java.io.Serializable;\n\nimport com.fasterxml.jackson.core.JsonGenerator;\n\n/**\n * @author Laurent Leseigneur\n * @author Anthony Birembaut\n */\npublic class JacksonSerializerHelper {\n\n    public JacksonSerializerHelper() {\n    }\n\n    public void writeNumberField(final JsonGenerator jgen, final String fieldName, final Serializable value)\n            throws IOException {\n        jgen.writeObjectField(fieldName, value);\n        jgen.writeObjectField(fieldName + \"_string\", String.valueOf(value));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/utils/TimerEventTriggerInstanceSerializer.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.utils;\n\nimport java.io.IOException;\n\nimport com.fasterxml.jackson.core.JsonGenerator;\nimport com.fasterxml.jackson.databind.JsonSerializer;\nimport com.fasterxml.jackson.databind.SerializerProvider;\nimport org.bonitasoft.engine.bpm.flownode.TimerEventTriggerInstance;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class TimerEventTriggerInstanceSerializer extends JsonSerializer<TimerEventTriggerInstance> {\n\n    final JacksonSerializerHelper jacksonSerializerHelper = new JacksonSerializerHelper();\n\n    @Override\n    public void serialize(final TimerEventTriggerInstance timerEventTriggerInstance, final JsonGenerator jgen,\n            final SerializerProvider provider) throws IOException {\n        jgen.writeStartObject();\n        jgen.writeObjectField(\"eventInstanceName\", timerEventTriggerInstance.getEventInstanceName());\n        jgen.writeObjectField(\"executionDate\", timerEventTriggerInstance.getExecutionDate());\n        jacksonSerializerHelper.writeNumberField(jgen, \"id\", timerEventTriggerInstance.getId());\n        jacksonSerializerHelper.writeNumberField(jgen, \"eventInstanceId\",\n                timerEventTriggerInstance.getEventInstanceId());\n        jgen.writeEndObject();\n    }\n\n    @Override\n    public Class<TimerEventTriggerInstance> handledType() {\n        return TimerEventTriggerInstance.class;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/utils/UserMixIn.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.utils;\n\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\nimport com.fasterxml.jackson.databind.ser.std.ToStringSerializer;\nimport org.bonitasoft.engine.identity.User;\n\n/**\n * Jackson mix-in patching {@link User} to serialize every long-typed id field as a JSON\n * string, to prevent JavaScript precision loss on values exceeding\n * {@code Number.MAX_SAFE_INTEGER}.\n * <p>\n * Covers all long ids exposed by {@code User}:\n * <ul>\n * <li>{@code id} — the user's own primary key (the contract {@code BaseRestElement} will\n * enforce upstream)</li>\n * <li>{@code createdBy} — id of the user who created this account</li>\n * <li>{@code managerUserId} — id of this user's manager</li>\n * <li>{@code iconId} — id of the icon row used as avatar</li>\n * </ul>\n * Date fields are left to Jackson defaults (handled by the date (de)serializers also\n * registered on {@link BonitaJacksonModuleProvider}).\n * <p>\n * This is a workaround until {@code User} extends {@code BaseRestElement} upstream in the\n * {@code bonita-organization-model} artifact, at which point the {@code id} entry can be\n * dropped from this mix-in (or the whole class, if upstream also annotates the other ids).\n * <p>\n * Registered globally via {@link BonitaJacksonModuleProvider} so the contract applies to\n * every {@code User} reaching the REST layer (e.g. embedded in delegation DTOs).\n */\nabstract class UserMixIn {\n\n    @JsonSerialize(using = ToStringSerializer.class)\n    public abstract long getId();\n\n    @JsonSerialize(using = ToStringSerializer.class)\n    public abstract long getCreatedBy();\n\n    @JsonSerialize(using = ToStringSerializer.class)\n    public abstract long getManagerUserId();\n\n    @JsonSerialize(using = ToStringSerializer.class)\n    public abstract Long getIconId();\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/server/login/LoginFailureTracker.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.server.login;\n\n/**\n * Interface for tracking login failures to implement brute-force attack protection.\n * Implementations can store failure counts in memory, a database, or any other storage mechanism.\n */\npublic interface LoginFailureTracker {\n\n    boolean isLockedOut(String username);\n\n    void recordFailure(String username);\n\n    void resetFailures(String username);\n\n    long getLockoutDurationSeconds();\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/server/login/LoginFailureTrackerAccessor.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.server.login;\n\nimport javax.servlet.ServletContext;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.NoSuchBeanDefinitionException;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.web.context.support.WebApplicationContextUtils;\n\n@Slf4j\npublic class LoginFailureTrackerAccessor {\n\n    private LoginFailureTrackerAccessor() {\n    }\n\n    public static LoginFailureTracker getLoginFailureTracker(ServletContext servletContext) {\n        try {\n            ApplicationContext context = WebApplicationContextUtils\n                    .getWebApplicationContext(servletContext);\n            if (context == null) {\n                log.warn(\n                        \"Spring ApplicationContext not available — brute-force login protection is disabled\");\n                return null;\n            }\n            return context.getBean(LoginFailureTracker.class);\n        } catch (NoSuchBeanDefinitionException e) {\n            log.warn(\n                    \"LoginFailureTracker bean not found — brute-force login protection is disabled\", e);\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/server/login/LoginFailureTrackerConfiguration.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.server.login;\n\nimport static org.bonitasoft.engine.Profiles.NOT_IN_CLUSTER;\n\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Profile;\n\n@Configuration\n@Profile(NOT_IN_CLUSTER)\npublic class LoginFailureTrackerConfiguration {\n\n    // Inline @Value defaults mirror bonita-platform-community.properties — keep them in sync.\n    @Value(\"${bonita.runtime.security.bruteforce.enabled:true}\")\n    private boolean bruteForceEnabled;\n\n    @Value(\"${bonita.runtime.security.bruteforce.max.attempts:5}\")\n    private int bruteForceMaxAttempts;\n\n    @Value(\"${bonita.runtime.security.bruteforce.lockout.duration.seconds:600}\")\n    private int bruteForceLockoutDurationSeconds;\n\n    @Bean\n    public LoginFailureTracker loginFailureTracker() {\n        return new MemoryLoginFailureTracker(\n                bruteForceEnabled,\n                bruteForceMaxAttempts,\n                bruteForceLockoutDurationSeconds);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/server/login/MemoryLoginFailureTracker.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.server.login;\n\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.atomic.AtomicInteger;\n\n/**\n * Memory implementation of {@link LoginFailureTracker}\n * Note: this implementation is not distributed and will not work across multiple server instances.\n * <p>\n * Uses a fixed-window counting strategy: the failure count resets when the window expires.\n * This means an attacker could make up to {@code maxAttempts - 1} attempts per window indefinitely\n * without triggering a lockout. A sliding-window approach would be stricter, but the fixed window\n * is simpler and sufficient for the intended threat model (automated brute-force attacks).\n */\npublic class MemoryLoginFailureTracker implements LoginFailureTracker {\n\n    private static final int EVICTION_INTERVAL = 100;\n\n    /** Safety cap to prevent unbounded memory growth under sustained attack from many distinct usernames. */\n    static final int MAX_ENTRIES = 10_000;\n\n    private final boolean enabled;\n    private final int maxAttempts;\n    private final long lockoutDurationMillis;\n    private final ConcurrentHashMap<String, FailureRecord> failures = new ConcurrentHashMap<>();\n    private final AtomicInteger operationCount = new AtomicInteger();\n\n    public MemoryLoginFailureTracker(boolean enabled, int maxAttempts, int lockoutDurationSeconds) {\n        if (maxAttempts <= 0) {\n            throw new IllegalArgumentException(\"maxAttempts must be positive, got: \" + maxAttempts);\n        }\n        if (lockoutDurationSeconds <= 0) {\n            throw new IllegalArgumentException(\n                    \"lockoutDurationSeconds must be positive, got: \" + lockoutDurationSeconds);\n        }\n        this.enabled = enabled;\n        this.maxAttempts = maxAttempts;\n        this.lockoutDurationMillis = lockoutDurationSeconds * 1000L;\n    }\n\n    @Override\n    public boolean isLockedOut(String username) {\n        if (!enabled) {\n            return false;\n        }\n        FailureRecord record = failures.get(username);\n        if (record == null) {\n            return false;\n        }\n        if (isExpired(record)) {\n            failures.remove(username);\n            return false;\n        }\n        return record.count >= maxAttempts;\n    }\n\n    @Override\n    public void recordFailure(String username) {\n        if (!enabled) {\n            return;\n        }\n        failures.compute(username, (key, existing) -> {\n            long now = currentTimeMillis();\n            if (existing == null || isExpired(existing)) {\n                return new FailureRecord(1, now);\n            }\n            return new FailureRecord(existing.count + 1, existing.windowStartTimestamp);\n        });\n        evictExpiredIfNeeded();\n    }\n\n    @Override\n    public long getLockoutDurationSeconds() {\n        return lockoutDurationMillis / 1000;\n    }\n\n    @Override\n    public void resetFailures(String username) {\n        if (!enabled) {\n            return;\n        }\n        failures.remove(username);\n    }\n\n    private void evictExpiredIfNeeded() {\n        if (operationCount.incrementAndGet() % EVICTION_INTERVAL == 0) {\n            failures.entrySet().removeIf(entry -> isExpired(entry.getValue()));\n            // If still over the safety cap after expiry eviction, evict oldest entries\n            int excess = failures.size() - MAX_ENTRIES;\n            if (excess > 0) {\n                failures.entrySet().stream()\n                        .sorted((a, b) -> Long.compare(a.getValue().windowStartTimestamp,\n                                b.getValue().windowStartTimestamp))\n                        .limit(excess)\n                        .forEach(entry -> failures.remove(entry.getKey()));\n            }\n        }\n    }\n\n    private boolean isExpired(FailureRecord record) {\n        return currentTimeMillis() - record.windowStartTimestamp >= lockoutDurationMillis;\n    }\n\n    protected long currentTimeMillis() {\n        return System.currentTimeMillis();\n    }\n\n    static class FailureRecord {\n\n        final int count;\n        final long windowStartTimestamp;\n\n        FailureRecord(int count, long windowStartTimestamp) {\n            this.count = count;\n            this.windowStartTimestamp = windowStartTimestamp;\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/ItemDefinitionFactory.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client;\n\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\npublic abstract class ItemDefinitionFactory {\n\n    private static ItemDefinitionFactory defaultFactory = null;\n\n    public static void setDefaultFactory(final ItemDefinitionFactory factory) {\n        defaultFactory = factory;\n    }\n\n    public static ItemDefinitionFactory getDefaultFactory() {\n        return defaultFactory;\n    }\n\n    public abstract ItemDefinition<?> defineItemDefinitions(String token);\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/AbstractTreeNode.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common;\n\nimport org.bonitasoft.web.toolkit.client.common.json.JsonSerializable;\n\n/**\n * @author Séverin Moussel\n * @param <VALUE_CLASS>\n */\npublic abstract class AbstractTreeNode<VALUE_CLASS> implements JsonSerializable {\n\n    protected AbstractTreeNode<VALUE_CLASS> parent = null;\n\n    public AbstractTreeNode() {\n    }\n\n    public AbstractTreeNode(final AbstractTreeNode<VALUE_CLASS> parent) {\n        this.parent = parent;\n    }\n\n    public AbstractTreeNode<VALUE_CLASS> getParent() {\n        return this.parent;\n    }\n\n    public void setParent(final AbstractTreeNode<VALUE_CLASS> parent) {\n        this.parent = parent;\n    }\n\n    public abstract AbstractTreeNode<VALUE_CLASS> copy();\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/CommonDateFormater.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common;\n\nimport java.util.Date;\n\n/**\n * @author Paul AMAR\n */\npublic abstract class CommonDateFormater {\n\n    private static CommonDateFormater formater = null;\n\n    abstract public Date _parse(final String value, final String format);\n\n    abstract public String _toString(final Date value, final String format);\n\n    public static void setDateFormater(final CommonDateFormater formater) {\n        CommonDateFormater.formater = formater;\n    }\n\n    public static Date parse(final String value, final String format) {\n        return formater._parse(value, format);\n    }\n\n    public static String toString(final Date value, final String format) {\n        return formater._toString(value, format);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/Tree.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common;\n\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\n\nimport org.bonitasoft.web.toolkit.client.common.json.JSonSerializer;\n\n/**\n * @author Séverin Moussel\n * @param <VALUE_CLASS>\n */\npublic class Tree<VALUE_CLASS> extends AbstractTreeNode<VALUE_CLASS>\n        implements Iterable<AbstractTreeNode<VALUE_CLASS>> {\n\n    protected List<AbstractTreeNode<VALUE_CLASS>> children = new ArrayList<>();\n\n    public Tree() {\n    }\n\n    public void addNode(final AbstractTreeNode<VALUE_CLASS> node) {\n        if (node == null) {\n            return;\n        }\n        node.setParent(this);\n        this.children.add(node);\n    }\n\n    public void addValue(final VALUE_CLASS value) {\n        if (value != null && !this.contains(value)) {\n            this.addNode(new TreeLeaf<>(this, value));\n        }\n    }\n\n    public List<VALUE_CLASS> getValues() {\n        final List<VALUE_CLASS> values = new ArrayList<>();\n        for (final AbstractTreeNode<VALUE_CLASS> node : this.children) {\n            if (node instanceof TreeLeaf) {\n                values.add(((TreeLeaf<VALUE_CLASS>) node).getValue());\n            }\n        }\n\n        return values;\n    }\n\n    public AbstractTreeNode<VALUE_CLASS> get(final int index) {\n        return this.children.get(index);\n    }\n\n    @Override\n    public String toString() {\n        return this.children.toString();\n    }\n\n    public int size() {\n        return this.children.size();\n    }\n\n    public boolean contains(final VALUE_CLASS value) {\n        return this.getValues().contains(value);\n    }\n\n    @Override\n    public Iterator<AbstractTreeNode<VALUE_CLASS>> iterator() {\n        return this.children.iterator();\n    }\n\n    @Override\n    public String toJson() {\n        return JSonSerializer.serializeCollection(this.children);\n    }\n\n    @Override\n    public Tree<VALUE_CLASS> copy() {\n        final Tree<VALUE_CLASS> result = new Tree<>();\n\n        for (final AbstractTreeNode<VALUE_CLASS> child : this.children) {\n            result.addNode(child.copy());\n        }\n\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object o) {\n        if (o == this) {\n            return true;\n        }\n        if (!(o instanceof Tree<?> tree)) {\n            return false;\n        }\n\n        if (tree.size() != this.size()) {\n            return false;\n        }\n\n        for (final VALUE_CLASS valueThis : this.getValues()) {\n            for (final Object valueOther : tree.getValues()) {\n                if (!valueThis.equals(valueOther)) {\n                    return false;\n                }\n            }\n        }\n\n        for (final Object valueOther : tree.getValues()) {\n            for (final VALUE_CLASS valueThis : this.getValues()) {\n                if (!valueThis.equals(valueOther)) {\n                    return false;\n                }\n\n            }\n        }\n\n        return true;\n    }\n\n    public List<AbstractTreeNode<VALUE_CLASS>> getNodes() {\n        return this.children;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/TreeIndexed.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common;\n\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.web.toolkit.client.common.json.JSonSerializer;\n\n/**\n * @author Séverin Moussel\n * @param <VALUE_CLASS>\n */\npublic class TreeIndexed<VALUE_CLASS> extends AbstractTreeNode<VALUE_CLASS> {\n\n    protected Map<String, AbstractTreeNode<VALUE_CLASS>> children = new HashMap<>();\n\n    public TreeIndexed() {\n    }\n\n    public TreeIndexed(final AbstractTreeNode<VALUE_CLASS> parent, final Map<String, VALUE_CLASS> map) {\n        this(parent);\n        this.addValues(map);\n    }\n\n    public TreeIndexed(final AbstractTreeNode<VALUE_CLASS> parent) {\n        super(parent);\n    }\n\n    public void addNode(final String key, final AbstractTreeNode<VALUE_CLASS> node) {\n        if (node == null) {\n            return;\n        }\n\n        node.setParent(this);\n\n        this.children.put(key, node);\n    }\n\n    public void addValue(final String key, final VALUE_CLASS value) {\n        if (value != null) {\n            this.addNode(key, new TreeLeaf<>(this, value));\n        }\n    }\n\n    public void addValues(final Map<String, VALUE_CLASS> values) {\n        if (values != null) {\n            for (final String key : values.keySet()) {\n                this.addValue(key, values.get(key));\n            }\n        }\n\n    }\n\n    public LinkedHashMap<String, VALUE_CLASS> getValues() {\n        final LinkedHashMap<String, VALUE_CLASS> values = new LinkedHashMap<>();\n        for (final String key : this.children.keySet()) {\n            final AbstractTreeNode<VALUE_CLASS> node = this.children.get(key);\n            if (node instanceof TreeLeaf) {\n                values.put(key, ((TreeLeaf<VALUE_CLASS>) node).getValue());\n            }\n        }\n\n        return values;\n    }\n\n    public AbstractTreeNode<VALUE_CLASS> get(final String key) {\n        return this.children.get(key);\n    }\n\n    public Set<String> keySet() {\n        return this.children.keySet();\n    }\n\n    public VALUE_CLASS getValue(final String key) {\n        final AbstractTreeNode<VALUE_CLASS> node = this.children.get(key);\n        if (node instanceof TreeLeaf<?>) {\n            return ((TreeLeaf<VALUE_CLASS>) node).getValue();\n        }\n        return null;\n    }\n\n    @Override\n    public String toString() {\n        return this.children.toString();\n    }\n\n    public int size() {\n        return this.children.size();\n    }\n\n    @Override\n    public String toJson() {\n        return JSonSerializer.serializeMap(this.children);\n    }\n\n    @Override\n    public TreeIndexed<VALUE_CLASS> copy() {\n        final TreeIndexed<VALUE_CLASS> result = new TreeIndexed<>();\n\n        for (final String key : this.children.keySet()) {\n            result.addNode(key, this.children.get(key).copy());\n        }\n\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object o) {\n        if (o == this) {\n            return true;\n        }\n        if (!(o instanceof TreeIndexed<?> tree)) {\n            return false;\n        }\n\n        if (tree.size() != this.size()) {\n            return false;\n        }\n\n        for (final String key : this.keySet()) {\n            if (!this.get(key).equals(tree.get(key))) {\n                return false;\n            }\n        }\n\n        for (final String key : tree.keySet()) {\n            if (!tree.get(key).equals(this.get(key))) {\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    public Map<String, AbstractTreeNode<VALUE_CLASS>> getNodes() {\n        return this.children;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/TreeLeaf.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common;\n\nimport org.bonitasoft.web.toolkit.client.common.json.JSonSerializer;\n\n/**\n * @author Séverin Moussel\n * @param <VALUE_CLASS>\n */\npublic class TreeLeaf<VALUE_CLASS> extends AbstractTreeNode<VALUE_CLASS> {\n\n    protected VALUE_CLASS value;\n\n    public TreeLeaf(final VALUE_CLASS value) {\n        super();\n        this.value = value;\n    }\n\n    public TreeLeaf(final AbstractTreeNode<VALUE_CLASS> parent, final VALUE_CLASS value) {\n        super(parent);\n        this.value = value;\n    }\n\n    public VALUE_CLASS getValue() {\n        return this.value;\n    }\n\n    @Override\n    public String toString() {\n        return this.value == null ? null : this.value.toString();\n    }\n\n    @Override\n    public String toJson() {\n        return JSonSerializer.serialize(this.value);\n    }\n\n    @Override\n    public TreeLeaf<VALUE_CLASS> copy() {\n        return new TreeLeaf<>(this.value);\n    }\n\n    @Override\n    public boolean equals(final Object o) {\n        if (o == this) {\n            return true;\n        }\n        if (!(o instanceof TreeLeaf)) {\n            return false;\n        }\n\n        return this.getValue().equals(((TreeLeaf<?>) o).getValue());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/TreeNode.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common;\n\n/**\n * @author Séverin Moussel\n * @param <VALUE_CLASS>\n */\npublic class TreeNode<VALUE_CLASS> extends Tree<VALUE_CLASS> {\n\n    public TreeNode() {\n        super();\n    }\n\n    @Override\n    public TreeNode<VALUE_CLASS> copy() {\n        final TreeNode<VALUE_CLASS> result = new TreeNode<>();\n\n        for (final AbstractTreeNode<VALUE_CLASS> child : this.children) {\n            result.addNode(child.copy());\n        }\n\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/exception/KnownException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.exception;\n\nimport org.bonitasoft.web.toolkit.client.common.util.StringUtil;\n\n/**\n * @author Séverin Moussel\n */\npublic abstract class KnownException extends RuntimeException {\n\n    private static final long serialVersionUID = 4288951779031159740L;\n\n    public KnownException() {\n        super();\n    }\n\n    public KnownException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public KnownException(final String message) {\n        super(message);\n    }\n\n    public KnownException(final Throwable cause) {\n        super(cause);\n    }\n\n    /**\n     * Override this method to define a default message.<br />\n     * Default message will be the getMessage() result if no other message is defined.\n     *\n     * @return This method returns the default message of the exception.\n     */\n    protected String defaultMessage() {\n        return \"\";\n    }\n\n    @Override\n    public String getMessage() {\n        String message = super.getMessage();\n        if (StringUtil.isBlank(message)) {\n            message = defaultMessage();\n        }\n        return message;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/exception/api/APIException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.exception.api;\n\nimport static org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n.LOCALE;\n\nimport org.bonitasoft.web.toolkit.client.common.exception.http.JsonExceptionSerializer;\nimport org.bonitasoft.web.toolkit.client.common.exception.http.ServerException;\nimport org.bonitasoft.web.toolkit.client.common.i18n.T_;\nimport org.bonitasoft.web.toolkit.client.common.json.JsonSerializable;\n\n/**\n * @author Séverin Moussel\n */\npublic class APIException extends ServerException implements JsonSerializable {\n\n    private static final long serialVersionUID = 1820639344042666872L;\n\n    private LOCALE locale;\n\n    private String api = \"...\";\n\n    private String resource = \"...\";\n\n    private T_ localizedMessage;\n\n    protected APIException() {\n        super();\n    }\n\n    public APIException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public APIException(final String message) {\n        super(message);\n    }\n\n    public APIException(final Throwable cause) {\n        super(cause);\n    }\n\n    public APIException(final T_ localizedMessage, final Throwable cause) {\n        super(cause);\n        this.localizedMessage = localizedMessage;\n    }\n\n    public APIException(final T_ localizedMessage) {\n        this.localizedMessage = localizedMessage;\n    }\n\n    /**\n     * @return the api\n     */\n    public String getApi() {\n        return this.api;\n    }\n\n    /**\n     * @param api\n     *        the api to set\n     */\n    public APIException setApi(final String api) {\n        this.api = api;\n        return this;\n    }\n\n    /**\n     * @return the resource\n     */\n    public String getResource() {\n        return this.resource;\n    }\n\n    /**\n     * @param resource\n     *        the resource to set\n     */\n    public APIException setResource(final String resource) {\n        this.resource = resource;\n        return this;\n    }\n\n    @Override\n    protected JsonExceptionSerializer buildJson() {\n        return super.buildJson()\n                .appendAttribute(\"api\", getApi())\n                .appendAttribute(\"resource\", getResource());\n    }\n\n    @Override\n    protected String defaultMessage() {\n        return \"The API \\\"\" + getApi() + \"#\" + getResource() + \"\\\" has encountered an unknown error\";\n    }\n\n    public void setLocale(LOCALE locale) {\n        this.locale = locale;\n    }\n\n    @Override\n    public String getMessage() {\n        if (locale != null && localizedMessage != null) {\n            return localizedMessage.localize(locale);\n        }\n        return super.getMessage();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/exception/api/APIForbiddenException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.exception.api;\n\nimport org.bonitasoft.web.toolkit.client.common.i18n.T_;\n\n/**\n * @author Vincent Elcrin\n */\npublic class APIForbiddenException extends APIException {\n\n    private static final long serialVersionUID = 4021139564084425851L;\n\n    public APIForbiddenException(final String message) {\n        super(message);\n    }\n\n    public APIForbiddenException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public APIForbiddenException(T_ localizedMessage) {\n        super(localizedMessage);\n    }\n\n    public APIForbiddenException(T_ localizedMessage, Throwable cause) {\n        super(localizedMessage, cause);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/exception/api/APIIncorrectIdException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.exception.api;\n\n/**\n * @author Dumitru Corini\n */\npublic class APIIncorrectIdException extends APIException {\n\n    private static final long serialVersionUID = 1054890811602278747L;\n\n    public APIIncorrectIdException(final String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/exception/api/APIItemException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.exception.api;\n\nimport org.bonitasoft.web.toolkit.client.common.exception.http.JsonExceptionSerializer;\n\n/**\n * @author Séverin Moussel\n */\npublic class APIItemException extends APIException {\n\n    private static final long serialVersionUID = -5137218346282966251L;\n\n    protected final String itemType;\n\n    public APIItemException(final String itemType) {\n        this.itemType = itemType.toLowerCase();\n    }\n\n    public APIItemException(final String itemType, final String message) {\n        super(message);\n        this.itemType = itemType.toLowerCase();\n    }\n\n    public String getItemType() {\n        return this.itemType;\n    }\n\n    @Override\n    protected JsonExceptionSerializer buildJson() {\n        return super.buildJson()\n                .appendAttribute(\"itemtype\", getItemType());\n    }\n\n    @Override\n    protected String defaultMessage() {\n        return \"An unknown error occurred on item \" + getItemType() + \" for API \" + getApi() + \"#\" + getResource();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/exception/api/APIItemIdMalformedException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.exception.api;\n\nimport org.bonitasoft.web.toolkit.client.common.exception.http.JsonExceptionSerializer;\n\n/**\n * @author Dumitru Corini\n */\npublic class APIItemIdMalformedException extends APIItemException {\n\n    private static final long serialVersionUID = 7963164445474080661L;\n\n    public APIItemIdMalformedException(final String itemType, final String message) {\n        super(itemType, message);\n    }\n\n    /**\n     * @return the itemType\n     */\n    public String getItemType() {\n        return this.itemType;\n    }\n\n    @Override\n    protected JsonExceptionSerializer buildJson() {\n        return super.buildJson()\n                .appendAttribute(\"itemtype\", getItemType());\n    }\n\n    @Override\n    protected String defaultMessage() {\n        return \"The format of the id was incorrect for \" + getItemType() + \" for API \" + getApi() + \"#\" + getResource();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/exception/api/APIItemNotFoundException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.exception.api;\n\nimport org.bonitasoft.web.toolkit.client.common.exception.http.JsonExceptionSerializer;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Séverin Moussel\n */\npublic class APIItemNotFoundException extends APIItemException {\n\n    private static final long serialVersionUID = -1325943431826471828L;\n\n    private final APIID id;\n\n    public APIItemNotFoundException(final String itemType) {\n        this(itemType, null);\n    }\n\n    public APIItemNotFoundException(final String itemType, final APIID id) {\n        super(itemType);\n        this.id = id;\n    }\n\n    /**\n     * @return the id\n     */\n    public APIID getId() {\n        return this.id;\n    }\n\n    @Override\n    protected JsonExceptionSerializer buildJson() {\n        JsonExceptionSerializer json = super.buildJson();\n        if (id != null) {\n            json.appendAttribute(\"id\", getId());\n        }\n        return json;\n    }\n\n    @Override\n    protected String defaultMessage() {\n        StringBuilder message = new StringBuilder();\n        message.append(this.itemType.substring(0, 1).toUpperCase());\n        message.append(this.itemType.substring(1));\n        if (id != null) {\n            message.append(\" with id (\");\n            message.append(getId().toString());\n            message.append(\")\");\n        }\n        message.append(\" not found for API \");\n        message.append(getApi());\n        message.append(\"#\");\n        message.append(getResource());\n        return message.toString();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/exception/api/APIMalformedUrlException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.exception.api;\n\nimport lombok.Getter;\n\n/**\n * @author Séverin Moussel\n */\npublic class APIMalformedUrlException extends APIException {\n\n    private static final long serialVersionUID = 3418532139473113744L;\n\n    @Getter\n    private final String url;\n\n    public APIMalformedUrlException(final String url) {\n        super();\n        this.url = url;\n    }\n\n    public APIMalformedUrlException(final String url, final String message) {\n        super(message);\n        this.url = url;\n    }\n\n    @Override\n    protected String defaultMessage() {\n        return \"Malformed url\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/exception/api/APIMethodNotAllowedException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.exception.api;\n\n/**\n * @author Séverin Moussel\n */\npublic class APIMethodNotAllowedException extends APIException {\n\n    private static final long serialVersionUID = 1709426532382444831L;\n\n    private final String method;\n\n    public APIMethodNotAllowedException(final String method) {\n        super((Exception) null);\n        this.method = method;\n    }\n\n    @Override\n    protected String defaultMessage() {\n        return \"HTTP method \" + this.method.toUpperCase() + \" not allowed for API \" + getApi() + \"#\" + getResource();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/exception/api/APINotFoundException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.exception.api;\n\nimport org.bonitasoft.web.toolkit.client.common.i18n.T_;\n\n/**\n * @author Séverin Moussel\n */\npublic class APINotFoundException extends APIException {\n\n    private static final long serialVersionUID = 7709846894799079466L;\n\n    public APINotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n    public APINotFoundException(final String api, final String resource) {\n        super((Exception) null);\n        setApi(api);\n        setResource(resource);\n    }\n\n    public APINotFoundException(final T_ localizedMessage, final Throwable cause) {\n        super(localizedMessage, cause);\n    }\n\n    public APINotFoundException(final T_ localizedMessage) {\n        super(localizedMessage);\n    }\n\n    @Override\n    protected String defaultMessage() {\n        return \"API \" +\n                getApi() +\n                \" or resource \" +\n                getResource() +\n                \" doesn't exist or is not declared\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/exception/api/APISearchIndexOutOfRange.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.exception.api;\n\nimport org.bonitasoft.web.toolkit.client.common.exception.http.JsonExceptionSerializer;\n\n/**\n * @author Séverin Moussel\n */\npublic class APISearchIndexOutOfRange extends APIException {\n\n    private static final long serialVersionUID = 2618198001118478389L;\n\n    private final int pageNumber;\n\n    public APISearchIndexOutOfRange(final int pageNumber) {\n        super();\n        this.pageNumber = pageNumber;\n    }\n\n    /**\n     * @return the pageNumber\n     */\n    public int getPageNumber() {\n        return this.pageNumber;\n    }\n\n    @Override\n    protected String defaultMessage() {\n        return \"Page index (\" + getPageNumber() + \") out of range for API \" + getApi() + \"#\" + getResource();\n    }\n\n    @Override\n    protected JsonExceptionSerializer buildJson() {\n        return super.buildJson()\n                .appendAttribute(\"page\", getPageNumber());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/exception/api/APITooManyRequestException.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.exception.api;\n\nimport lombok.Getter;\nimport org.bonitasoft.web.toolkit.client.common.i18n.T_;\n\npublic class APITooManyRequestException extends APIException {\n\n    private static final long serialVersionUID = 1820639344042666872L;\n    @Getter\n    private long retryAfter = -1L;\n\n    public APITooManyRequestException(final T_ message, long retryAfter) {\n        super(message);\n        this.retryAfter = retryAfter;\n        setStatusCode(429);\n    }\n\n    @Override\n    protected String defaultMessage() {\n        return getApi() + \"#\" + getResource() + \": Case creation limit reached.\";\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/exception/http/HttpException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.exception.http;\n\nimport org.bonitasoft.web.toolkit.client.common.exception.KnownException;\nimport org.bonitasoft.web.toolkit.client.common.json.JsonSerializable;\n\n/**\n * @author Séverin Moussel\n */\npublic class HttpException extends KnownException implements JsonSerializable {\n\n    private static final long serialVersionUID = 4351214615157802308L;\n\n    protected int statusCode = 503;\n\n    public HttpException() {\n        super();\n    }\n\n    public HttpException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public HttpException(final String message) {\n        super(message);\n    }\n\n    public HttpException(final Throwable cause) {\n        super(cause);\n    }\n\n    /**\n     * @return the statusCode\n     */\n    public final int getStatusCode() {\n        return this.statusCode;\n    }\n\n    /**\n     * @param statusCode\n     *        the statusCode to set\n     */\n    public final HttpException setStatusCode(final int statusCode) {\n        this.statusCode = statusCode;\n        return this;\n    }\n\n    @Override\n    public final String toJson() {\n        return buildJson().end();\n    }\n\n    protected JsonExceptionSerializer buildJson() {\n        return new JsonExceptionSerializer(this);\n    }\n\n    @Override\n    protected String defaultMessage() {\n        return \"The connection has encountered an unknown error\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/exception/http/JsonExceptionSerializer.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.exception.http;\n\nimport static org.bonitasoft.web.toolkit.client.common.json.JSonUtil.quote;\n\nimport org.bonitasoft.web.toolkit.client.common.json.JSonSerializer;\n\n/**\n * Created by Vincent Elcrin\n * Date: 23/09/13\n * Time: 17:56\n */\npublic class JsonExceptionSerializer {\n\n    private final StringBuilder json;\n\n    public JsonExceptionSerializer(Throwable exception) {\n        if (exception == null) {\n            throw new IllegalArgumentException(\"exception cannot be null\");\n        }\n\n        json = serialize(exception);\n    }\n\n    private StringBuilder serialize(final Throwable e) {\n        final StringBuilder json = new StringBuilder().append(\"{\");\n\n        json.append(exceptionInnerJson(e));\n        if (e.getCause() != null && e.getCause() != e) {\n            json.append(\",\");\n            // only add the first cause (used by some code in portal's frontend)\n            json.append(quote(\"cause\")).append(\":{\").append(exceptionInnerJson(e.getCause())).append(\"}\");\n        }\n\n        return json;\n    }\n\n    private String exceptionInnerJson(final Throwable e) {\n        return quote(\"exception\") + \":\" + quote(e.getClass().toString()) +\n                \",\" +\n                quote(\"message\") + \":\" + quote(e.getMessage());\n    }\n\n    public String end() {\n        return json.append(\"}\").toString();\n    }\n\n    public JsonExceptionSerializer appendAttribute(final String name, final Object value) {\n        addNextAttribute(json, name, value);\n        return this;\n    }\n\n    private void addNextAttribute(final StringBuilder json, final String name, final Object value) {\n        if (value != null) {\n            json.append(\",\");\n            json.append(quote(name))\n                    .append(\":\")\n                    .append(JSonSerializer.serialize(value));\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/exception/http/ServerException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.exception.http;\n\n/**\n * @author Séverin Moussel\n */\npublic class ServerException extends HttpException {\n\n    private static final long serialVersionUID = -2822846735990124977L;\n\n    public ServerException() {\n        super();\n    }\n\n    public ServerException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public ServerException(final String message) {\n        super(message);\n    }\n\n    public ServerException(final Throwable cause) {\n        super(cause);\n    }\n\n    @Override\n    protected String defaultMessage() {\n        return \"The server has encountered an unknown error\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/i18n/AbstractI18n.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.i18n;\n\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.web.toolkit.client.common.texttemplate.Arg;\nimport org.bonitasoft.web.toolkit.client.common.texttemplate.TextTemplate;\nimport org.bonitasoft.web.toolkit.client.common.util.StringUtil;\n\n/**\n * @author Séverin Moussel\n */\npublic abstract class AbstractI18n {\n\n    public enum LOCALE {\n        en, en_US, fr, fr_FR, es, es_ES, it, it_IT, de, de_DE, pt_BR, aa_DJ, aa_ER_SAAHO, aa_ER, aa_ET, aa, af_NA, af, af_ZA, ak_GH, ak, am_ET, am, ar_001, ar_AE, ar_BH, ar_DZ, ar_EG, ar_IQ, ar_JO, ar_KW, ar_LB, ar_LY, ar_MA, ar_OM, ar_QA, ar_SA, ar_SD, ar_SY, ar_TN, ar, ar_YE, as_IN, as, az_AZ, az_Cyrl_AZ, az_Cyrl, az_Latn_AZ, az, be_BY, be, bg_BG, bg, bn_BD, bn_IN, bn, bs_BA, bs, byn_ER, byn, ca_ES, ca, cch_NG, cch, cop, cs_CZ, cs, cy_GB, cy, da_DK, da, de_AT, de_BE, de_CH, de_LI, de_LU, dv_MV, dv, dz_BT, dz, ee_GH, ee_TG, ee, el_CY, el_GR, el_POLYTON, el, en_001, en_150, en_AE, en_AS, en_AU, en_BE, en_BW, en_BZ, en_CA, en_Dsrt_US, en_Dsrt, en_GB, en_GU, en_HK, en_IE, en_IN, en_JM, en_MH, en_MP, en_MT, en_NA, en_NZ, en_PH, en_PK, en_SG, en_Shaw, en_TT, en_UM, en_US_POSIX, en_VI, en_ZA, en_ZW, eo, eo_001, es_419, es_AR, es_BO, es_CL, es_CO, es_CR, es_DO, es_EC, es_GT, es_HN, es_MX, es_NI, es_PA, es_PE, es_PR, es_PY, es_SV, es_US, es_UY, es_VE, et_EE, et, eu_ES, eu, fa_AF, fa_IR, fa, fi_FI, fil_PH, fil, fi, fo_FO, fo, fr_BE, fr_CA, fr_CH, fr_LU, fr_MC, fr_SN, fur_IT, fur, gaa_GH, gaa, ga_IE, ga, gez_ER, gez_ET, gez, gl_ES, gl, gu_IN, gu, gv_GB, gv, ha_Arab_NG, ha_Arab_SD, ha_Arab, ha_GH, ha_Latn_GH, ha_Latn_NE, ha_Latn_NG, ha_Latn, ha_NE, ha_NG, ha_SD, haw_US, haw, ha, he_IL, he, hi_IN, hi, hr_HR, hr, hu_HU, hu, hy_AM_REVISED, hy_AM, hy, ia, ia_001, id_ID, id, ig_NG, ig, ii_CN, ii, in, is_IS, is, it_CH, iu, iw, ja_JP, ja, ka_GE, kaj_NG, kaj, kam_KE, kam, ka, kcg_NG, kcg, kfo_CI, kfo, kk_Cyrl_KZ, kk_Cyrl, kk_KZ, kk, kl_GL, kl, km_KH, km, kn_IN, kn, kok_IN, ko_KR, kok, ko, kpe_GN, kpe_LR, kpe, ku_Arab, ku_Latn_TR, ku_Latn, ku_TR, ku, kw_GB, kw, ky_KG, ky, ln_CD, ln_CG, ln, lo_LA, lo, lt_LT, lt, lv_LV, lv, mk_MK, mk, ml_IN, ml, mn_CN, mn_Cyrl_MN, mn_Cyrl, mn_MN, mn_Mong_CN, mn_Mong, mn, mo, mr_IN, mr, ms_BN, ms_MY, ms, mt_MT, mt, my_MM, my, nb_NO, nb, ne_IN, ne_NP, ne, nl_BE, nl_NL, nl, nn_NO, nn, no, no_NO_NY, nr, nr_ZA, nso, nso_ZA, ny_MW, ny, om_ET, om_KE, om, or_IN, or, pa_Arab_PK, pa_Arab, pa_Guru_IN, pa_Guru, pa_IN, pa_PK, pa, pl_PL, pl, ps_AF, ps, pt_PT, pt, ro_MD, ro_RO, ro, ru_RU, ru_UA, ru, rw_RW, rw, sa_IN, sa, se_FI, se_NO, se, sh_BA, sh_CS, sh, sh_YU, sid_ET, sid, si_LK, si, sk_SK, sk, sl_SI, sl, so_DJ, so_ET, so_KE, so_SO, so, sq_AL, sq, sr_BA, sr_CS, sr_Cyrl_BA, sr_Cyrl_CS, sr_Cyrl_ME, sr_Cyrl_RS, sr_Cyrl, sr_Cyrl_YU, sr_Latn_BA, sr_Latn_CS, sr_Latn_ME, sr_Latn_RS, sr_Latn, sr_Latn_YU, sr_ME, sr_RS, sr, sr_YU, ss_SZ, ss, ss_ZA, st_LS, st, st_ZA, sv_FI, sv_SE, sv, sw_KE, sw_TZ, sw, syr_SY, syr, ta_IN, ta, te_IN, te, tg_Cyrl_TJ, tg_Cyrl, tg_TJ, tg, th_TH, th, ti_ER, ti_ET, tig_ER, tig, ti, tl, tn, tn_ZA, to_TO, to, tr_TR, tr, ts, ts_ZA, tt_RU, tt, ug_Arab_CN, ug_Arab, ug_CN, ug, uk_UA, uk, ur_IN, ur_PK, ur, uz_AF, uz_Arab_AF, uz_Arab, uz_Cyrl_UZ, uz_Cyrl, uz_Latn_UZ, uz_Latn, uz_UZ, uz, ve, ve_ZA, vi_VN, vi, wal_ET, wal, wo_Latn_SN, wo_Latn, wo_SN, wo, xh, xh_ZA, yi, yi_001, yo_BJ, yo_NG, yo, zh_CN, zh_Hans_CN, zh_Hans_HK, zh_Hans_MO, zh_Hans_SG, zh_Hans, zh_Hant_HK, zh_Hant_MO, zh_Hant_TW, zh_Hant, zh_HK, zh_MO, zh_SG, zh_TW, zh, zu, zu_ZA\n    }\n\n    public LOCALE defaultLocale = LOCALE.en;\n\n    private final Map<LOCALE, Map<String, String>> locales = new HashMap<>();\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // SINGLETON\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    protected static AbstractI18n I18N_instance = null;\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // LOCALES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    protected static Map<String, String> getLocales() {\n        return I18N_instance._getLocales();\n    }\n\n    protected final Map<String, String> _getLocales() {\n        final Map<String, String> locales = new LinkedHashMap<>();\n\n        locales.put(\"en_US\", \"U.S. English\");\n        locales.put(\"en\", \"English\");\n        locales.put(\"fr_FR\", \"français (France)\");\n        locales.put(\"fr\", \"Français\");\n        locales.put(\"es_ES\", \"español de España\");\n        locales.put(\"es\", \"Español\");\n        locales.put(\"it_IT\", \"italiano (Italia)\");\n        locales.put(\"it\", \"Italiano\");\n        locales.put(\"de_DE\", \"Deutsch (Deutschland)\");\n        locales.put(\"de\", \"Deutsch\");\n        // locales.put(\"pt_BR\", \"português do Brasil\");\n        locales.put(\"pt_BR\", \"Português (Brasil)\");\n        locales.put(\"aa_DJ\", \"Qafar (Yabuuti)\");\n        locales.put(\"aa_ER_SAAHO\", \"Qafar - Eretria (Saho)\");\n        locales.put(\"aa_ER\", \"Qafar (Eretria)\");\n        locales.put(\"aa_ET\", \"Qafar (Otobbia)\");\n        locales.put(\"aa\", \"Qafar\");\n        locales.put(\"af_NA\", \"Afrikaans (Namibië)\");\n        locales.put(\"af\", \"Afrikaans\");\n        locales.put(\"af_ZA\", \"Afrikaans (Suid-Afrika)\");\n        locales.put(\"ak_GH\", \"Akan (Ghana)\");\n        locales.put(\"ak\", \"Akan\");\n        locales.put(\"am_ET\", \"አማርኛ (ኢትዮጵያ)\");\n        locales.put(\"am\", \"አማርኛ\");\n        locales.put(\"ar_001\", \"العربية العالم\");\n        locales.put(\"ar_AE\", \"العربية (الامارات العربية المتحدة)\");\n        locales.put(\"ar_BH\", \"العربية (البحرين)\");\n        locales.put(\"ar_DZ\", \"العربية (الجزائر)\");\n        locales.put(\"ar_EG\", \"العربية (مصر)\");\n        locales.put(\"ar_IQ\", \"العربية (العراق)\");\n        locales.put(\"ar_JO\", \"العربية (الأردن)\");\n        locales.put(\"ar_KW\", \"العربية (الكويت)\");\n        locales.put(\"ar_LB\", \"العربية (لبنان)\");\n        locales.put(\"ar_LY\", \"العربية (ليبيا)\");\n        locales.put(\"ar_MA\", \"العربية (المغرب)\");\n        locales.put(\"ar_OM\", \"العربية (عمان)\");\n        locales.put(\"ar_QA\", \"العربية (قطر)\");\n        locales.put(\"ar_SA\", \"العربية (المملكة العربية السعودية)\");\n        locales.put(\"ar_SD\", \"العربية (السودان)\");\n        locales.put(\"ar_SY\", \"العربية (سوريا)\");\n        locales.put(\"ar_TN\", \"العربية (تونس)\");\n        locales.put(\"ar\", \"العربية\");\n        locales.put(\"ar_YE\", \"العربية (اليمن)\");\n        locales.put(\"as_IN\", \"অসমীয়া (ভাৰত)\");\n        locales.put(\"as\", \"অসমীয়া\");\n        locales.put(\"az_AZ\", \"azərbaycanca - latın (Azərbaycan)\");\n        locales.put(\"az_Cyrl_AZ\", \"Азәрбајҹан - kiril (Азәрбајҹан)\");\n        locales.put(\"az_Cyrl\", \"Азәрбајҹан (kiril)\");\n        locales.put(\"az_Latn_AZ\", \"azərbaycanca - latın (Azərbaycan)\");\n        locales.put(\"az\", \"azərbaycanca\");\n        locales.put(\"be_BY\", \"беларуская (Беларусь)\");\n        locales.put(\"be\", \"беларуская\");\n        locales.put(\"bg_BG\", \"български (България)\");\n        locales.put(\"bg\", \"български\");\n        locales.put(\"bn_BD\", \"বাংলা (বাংলাদেশ)\");\n        locales.put(\"bn_IN\", \"বাংলা (ভারত)\");\n        locales.put(\"bn\", \"বাংলা\");\n        locales.put(\"bs_BA\", \"bosanski (Bosna i Hercegovina)\");\n        locales.put(\"bs\", \"bosanski\");\n        locales.put(\"byn_ER\", \"ብሊን (ኤርትራ)\");\n        locales.put(\"byn\", \"ብሊን\");\n        locales.put(\"ca_ES\", \"català (Espanya)\");\n        locales.put(\"ca\", \"català\");\n        locales.put(\"cch_NG\", \"Atsam (Nigeria)\");\n        locales.put(\"cch\", \"Atsam\");\n        locales.put(\"cop\", \"Coptic\");\n        locales.put(\"cs_CZ\", \"čeština (Česká republika)\");\n        locales.put(\"cs\", \"čeština\");\n        locales.put(\"cy_GB\", \"Cymraeg (Prydain Fawr)\");\n        locales.put(\"cy\", \"Cymraeg\");\n        locales.put(\"da_DK\", \"dansk (Danmark)\");\n        locales.put(\"da\", \"dansk\");\n        locales.put(\"de_AT\", \"Österreichisches Deutsch\");\n        locales.put(\"de_BE\", \"Deutsch (Belgien)\");\n        locales.put(\"de_CH\", \"Schweizer Hochdeutsch\");\n        locales.put(\"de_LI\", \"Deutsch (Liechtenstein)\");\n        locales.put(\"de_LU\", \"Deutsch (Luxemburg)\");\n        locales.put(\"dv_MV\", \"ދިވެހިބަސް (ދިވެހި ރާއްޖެ)\");\n        locales.put(\"dv\", \"ދިވެހިބަސް\");\n        locales.put(\"dz_BT\", \"རྫོང་ཁ (འབྲུག)\");\n        locales.put(\"dz\", \"རྫོང་ཁ\");\n        locales.put(\"ee_GH\", \"Ewe (Ghana)\");\n        locales.put(\"ee_TG\", \"Ewe (Togo)\");\n        locales.put(\"ee\", \"Ewe\");\n        locales.put(\"el_CY\", \"Ελληνικά (Κύπρος)\");\n        locales.put(\"el_GR\", \"Ελληνικά (Ελλάδα)\");\n        locales.put(\"el_POLYTON\", \"Ἑλληνικά (Πολυτονικό)\");\n        locales.put(\"el\", \"Ελληνικά\");\n        locales.put(\"en_001\", \"English World\");\n        locales.put(\"en_150\", \"English Europe\");\n        locales.put(\"en_AE\", \"English United Arab Emirates\");\n        locales.put(\"en_AS\", \"English (American Samoa)\");\n        locales.put(\"en_AU\", \"Australian English\");\n        locales.put(\"en_BE\", \"English (Belgium)\");\n        locales.put(\"en_BW\", \"English (Botswana)\");\n        locales.put(\"en_BZ\", \"English (Belize)\");\n        locales.put(\"en_CA\", \"Canadian English\");\n        locales.put(\"en_Dsrt_US\", \"𐐀𐑍𐑊𐐮𐑇 - 𐐔𐐯𐑆𐐲𐑉𐐯𐐻 (𐐏𐐭𐑌𐐴𐐻𐐲𐐼 𐐝𐐻𐐩𐐻𐑅)\");\n        locales.put(\"en_Dsrt\", \"𐐀𐑍𐑊𐐮𐑇 (𐐔𐐯𐑆𐐲𐑉𐐯𐐻)\");\n        locales.put(\"en_GB\", \"British English\");\n        locales.put(\"en_GU\", \"English (Guam)\");\n        locales.put(\"en_HK\", \"English (Hong Kong SAR China)\");\n        locales.put(\"en_IE\", \"English (Ireland)\");\n        locales.put(\"en_IN\", \"English (India)\");\n        locales.put(\"en_JM\", \"English (Jamaica)\");\n        locales.put(\"en_MH\", \"English (Marshall Islands)\");\n        locales.put(\"en_MP\", \"English (Northern Mariana Islands)\");\n        locales.put(\"en_MT\", \"English (Malta)\");\n        locales.put(\"en_NA\", \"English (Namibia)\");\n        locales.put(\"en_NZ\", \"English (New Zealand)\");\n        locales.put(\"en_PH\", \"English (Philippines)\");\n        locales.put(\"en_PK\", \"English (Pakistan)\");\n        locales.put(\"en_SG\", \"English (Singapore)\");\n        locales.put(\"en_Shaw\", \"English (Shavian)\");\n        locales.put(\"en_TT\", \"English (Trinidad and Tobago)\");\n        locales.put(\"en_UM\", \"English (United States Minor Outlying Islands)\");\n        locales.put(\"en_US_POSIX\", \"U.S. English (Computer)\");\n        locales.put(\"en_VI\", \"English (U.S. Virgin Islands)\");\n        locales.put(\"en_ZA\", \"English (South Africa)\");\n        locales.put(\"en_ZW\", \"English (Zimbabwe)\");\n        locales.put(\"eo\", \"esperanto\");\n        locales.put(\"eo_001\", \"esperanto world\");\n        locales.put(\"es_419\", \"español (Latinoamérica)\");\n        locales.put(\"es_AR\", \"español (Argentina)\");\n        locales.put(\"es_BO\", \"español (Bolivia)\");\n        locales.put(\"es_CL\", \"español (Chile)\");\n        locales.put(\"es_CO\", \"español (Colombia)\");\n        locales.put(\"es_CR\", \"español (Costa Rica)\");\n        locales.put(\"es_DO\", \"español (República Dominicana)\");\n        locales.put(\"es_EC\", \"español (Ecuador)\");\n        locales.put(\"es_GT\", \"español (Guatemala)\");\n        locales.put(\"es_HN\", \"español (Honduras)\");\n        locales.put(\"es_MX\", \"español (México)\");\n        locales.put(\"es_NI\", \"español (Nicaragua)\");\n        locales.put(\"es_PA\", \"español (Panamá)\");\n        locales.put(\"es_PE\", \"español (Perú)\");\n        locales.put(\"es_PR\", \"español (Puerto Rico)\");\n        locales.put(\"es_PY\", \"español (Paraguay)\");\n        locales.put(\"es_SV\", \"español (El Salvador)\");\n        locales.put(\"es_US\", \"español (Estados Unidos)\");\n        locales.put(\"es_UY\", \"español (Uruguay)\");\n        locales.put(\"es_VE\", \"español (Venezuela)\");\n        locales.put(\"et_EE\", \"eesti (Eesti)\");\n        locales.put(\"et\", \"eesti\");\n        locales.put(\"eu_ES\", \"euskara (Espainia)\");\n        locales.put(\"eu\", \"euskara\");\n        locales.put(\"fa_AF\", \"دری (افغانستان)\");\n        locales.put(\"fa_IR\", \"فارسی (ایران)\");\n        locales.put(\"fa\", \"فارسی\");\n        locales.put(\"fi_FI\", \"suomi (Suomi)\");\n        locales.put(\"fil_PH\", \"Filipino (Pilipinas)\");\n        locales.put(\"fil\", \"Filipino\");\n        locales.put(\"fi\", \"suomi\");\n        locales.put(\"fo_FO\", \"føroyskt (Føroyar)\");\n        locales.put(\"fo\", \"føroyskt\");\n        locales.put(\"fr_BE\", \"français (Belgique)\");\n        locales.put(\"fr_CA\", \"français canadien\");\n        locales.put(\"fr_CH\", \"français suisse\");\n        locales.put(\"fr_LU\", \"français (Luxembourg)\");\n        locales.put(\"fr_MC\", \"français (Monaco)\");\n        locales.put(\"fr_SN\", \"français (Sénégal)\");\n        locales.put(\"fur_IT\", \"furlan (Italie)\");\n        locales.put(\"fur\", \"furlan\");\n        locales.put(\"gaa_GH\", \"Ga (Ghana)\");\n        locales.put(\"gaa\", \"Ga\");\n        locales.put(\"ga_IE\", \"Gaeilge (Éire)\");\n        locales.put(\"ga\", \"Gaeilge\");\n        locales.put(\"gez_ER\", \"ግዕዝኛ (ኤርትራ)\");\n        locales.put(\"gez_ET\", \"ግዕዝኛ (ኢትዮጵያ)\");\n        locales.put(\"gez\", \"ግዕዝኛ\");\n        locales.put(\"gl_ES\", \"galego (España)\");\n        locales.put(\"gl\", \"galego\");\n        locales.put(\"gu_IN\", \"ગુજરાતી (ભારત)\");\n        locales.put(\"gu\", \"ગુજરાતી\");\n        locales.put(\"gv_GB\", \"Gaelg (Rywvaneth Unys)\");\n        locales.put(\"gv\", \"Gaelg\");\n        locales.put(\"ha_Arab_NG\", \"Haoussa - Arabic (Nijeriya)\");\n        locales.put(\"ha_Arab_SD\", \"Haoussa - Arabic (Sudan)\");\n        locales.put(\"ha_Arab\", \"Haoussa (Arabic)\");\n        locales.put(\"ha_GH\", \"Haoussa - Latin (Ghana)\");\n        locales.put(\"ha_Latn_GH\", \"Haoussa - Latin (Ghana)\");\n        locales.put(\"ha_Latn_NE\", \"Haoussa - Latin (Niger)\");\n        locales.put(\"ha_Latn_NG\", \"Haoussa - Latin (Nijeriya)\");\n        locales.put(\"ha_Latn\", \"Haoussa (Latin)\");\n        locales.put(\"ha_NE\", \"Haoussa - Latin (Niger)\");\n        locales.put(\"ha_NG\", \"Haoussa - Latin (Nijeriya)\");\n        locales.put(\"ha_SD\", \"Haoussa - Arabic (Sudan)\");\n        locales.put(\"haw_US\", \"ʻōlelo Hawaiʻi (ʻAmelika Hui Pū ʻIa)\");\n        locales.put(\"haw\", \"ʻōlelo Hawaiʻi\");\n        locales.put(\"ha\", \"Haoussa\");\n        locales.put(\"he_IL\", \"עברית (ישראל)\");\n        locales.put(\"he\", \"עברית\");\n        locales.put(\"hi_IN\", \"हिन्दी (भारत)\");\n        locales.put(\"hi\", \"हिन्दी\");\n        locales.put(\"hr_HR\", \"hrvatski (Hrvatska)\");\n        locales.put(\"hr\", \"hrvatski\");\n        locales.put(\"hu_HU\", \"magyar (Magyarország)\");\n        locales.put(\"hu\", \"magyar\");\n        locales.put(\"hy_AM_REVISED\", \"Հայերէն - Հայաստանի Հանրապետութիւն (Revised Orthography)\");\n        locales.put(\"hy_AM\", \"Հայերէն (Հայաստանի Հանրապետութիւն)\");\n        locales.put(\"hy\", \"Հայերէն\");\n        locales.put(\"ia\", \"interlingua\");\n        locales.put(\"ia_001\", \"interlingua world\");\n        locales.put(\"id_ID\", \"Bahasa Indonesia (Indonesia)\");\n        locales.put(\"id\", \"Bahasa Indonesia\");\n        locales.put(\"ig_NG\", \"Igbo (Nigeria)\");\n        locales.put(\"ig\", \"Igbo\");\n        locales.put(\"ii_CN\", \"ꆈꌠꉙ (ꍏꇩ)\");\n        locales.put(\"ii\", \"ꆈꌠꉙ\");\n        locales.put(\"in\", \"Bahasa Indonesia\");\n        locales.put(\"is_IS\", \"íslenska (Ísland)\");\n        locales.put(\"is\", \"íslenska\");\n        locales.put(\"it_CH\", \"italiano (Svizzera)\");\n        locales.put(\"iu\", \"ᐃᓄᒃᑎᑐᑦ ᑎᑎᕋᐅᓯᖅ\");\n        locales.put(\"iw\", \"עברית\");\n        locales.put(\"ja_JP\", \"日本語 (日本)\");\n        locales.put(\"ja\", \"日本語\");\n        locales.put(\"ka_GE\", \"ქართული (საქართველო)\");\n        locales.put(\"kaj_NG\", \"Jju (Nigeria)\");\n        locales.put(\"kaj\", \"Jju\");\n        locales.put(\"kam_KE\", \"Kamba (Kenya)\");\n        locales.put(\"kam\", \"Kamba\");\n        locales.put(\"ka\", \"ქართული\");\n        locales.put(\"kcg_NG\", \"Tyap (Nigeria)\");\n        locales.put(\"kcg\", \"Tyap\");\n        locales.put(\"kfo_CI\", \"Koro (Ivory Coast)\");\n        locales.put(\"kfo\", \"Koro\");\n        locales.put(\"kk_Cyrl_KZ\", \"Қазақ - Cyrillic (Қазақстан)\");\n        locales.put(\"kk_Cyrl\", \"Қазақ (Cyrillic)\");\n        locales.put(\"kk_KZ\", \"Қазақ - Cyrillic (Қазақстан)\");\n        locales.put(\"kk\", \"Қазақ\");\n        locales.put(\"kl_GL\", \"kalaallisut (Kalaallit Nunaat)\");\n        locales.put(\"kl\", \"kalaallisut\");\n        locales.put(\"km_KH\", \"ភាសាខ្មែរ (កម្ពុជា)\");\n        locales.put(\"km\", \"ភាសាខ្មែរ\");\n        locales.put(\"kn_IN\", \"ಕನ್ನಡ (ಭಾರತ)\");\n        locales.put(\"kn\", \"ಕನ್ನಡ\");\n        locales.put(\"kok_IN\", \"कोंकणी (भारत)\");\n        locales.put(\"ko_KR\", \"한국어 (대한민국)\");\n        locales.put(\"kok\", \"कोंकणी\");\n        locales.put(\"ko\", \"한국어\");\n        locales.put(\"kpe_GN\", \"Kpelle (Guinea)\");\n        locales.put(\"kpe_LR\", \"Kpelle (Liberia)\");\n        locales.put(\"kpe\", \"Kpelle\");\n        locales.put(\"ku_Arab\", \"كوردی (Arabic)\");\n        locales.put(\"ku_Latn_TR\", \"kurdî - Latin (Tirkiye)\");\n        locales.put(\"ku_Latn\", \"kurdî (Latin)\");\n        locales.put(\"ku_TR\", \"كوردی - Latin (Turkey)\");\n        locales.put(\"ku\", \"كوردی\");\n        locales.put(\"kw_GB\", \"kernewek (Rywvaneth Unys)\");\n        locales.put(\"kw\", \"kernewek\");\n        locales.put(\"ky_KG\", \"Кыргыз (Кыргызстан)\");\n        locales.put(\"ky\", \"Кыргыз\");\n        locales.put(\"ln_CD\", \"lingála (Kongó-Kinsásá)\");\n        locales.put(\"ln_CG\", \"lingála (Kongó-Brazzaville)\");\n        locales.put(\"ln\", \"lingála\");\n        locales.put(\"lo_LA\", \"ລາວ (ລາວ)\");\n        locales.put(\"lo\", \"ລາວ\");\n        locales.put(\"lt_LT\", \"lietuvių (Lietuva)\");\n        locales.put(\"lt\", \"lietuvių\");\n        locales.put(\"lv_LV\", \"latviešu (Latvija)\");\n        locales.put(\"lv\", \"latviešu\");\n        locales.put(\"mk_MK\", \"македонски (Македонија)\");\n        locales.put(\"mk\", \"македонски\");\n        locales.put(\"ml_IN\", \"മലയാളം (ഇന്ത്യ)\");\n        locales.put(\"ml\", \"മലയാളം\");\n        locales.put(\"mn_CN\", \"монгол - Mongolian (China)\");\n        locales.put(\"mn_Cyrl_MN\", \"монгол - Cyrillic (Монгол улс)\");\n        locales.put(\"mn_Cyrl\", \"монгол (Cyrillic)\");\n        locales.put(\"mn_MN\", \"монгол - Cyrillic (Монгол улс)\");\n        locales.put(\"mn_Mong_CN\", \"монгол - Mongolian (China)\");\n        locales.put(\"mn_Mong\", \"монгол (Mongolian)\");\n        locales.put(\"mn\", \"монгол\");\n        locales.put(\"mo\", \"Moldavian\");\n        locales.put(\"mr_IN\", \"मराठी (भारत)\");\n        locales.put(\"mr\", \"मराठी\");\n        locales.put(\"ms_BN\", \"Bahasa Melayu (Brunei)\");\n        locales.put(\"ms_MY\", \"Bahasa Melayu (Malaysia)\");\n        locales.put(\"ms\", \"Bahasa Melayu\");\n        locales.put(\"mt_MT\", \"Malti (Malta)\");\n        locales.put(\"mt\", \"Malti\");\n        locales.put(\"my_MM\", \"ဗမာ (မြန်မာ)\");\n        locales.put(\"my\", \"ဗမာ\");\n        locales.put(\"nb_NO\", \"norsk bokmål (Norge)\");\n        locales.put(\"nb\", \"norsk bokmål\");\n        locales.put(\"ne_IN\", \"नेपाली (भारत)\");\n        locales.put(\"ne_NP\", \"नेपाली (नेपाल)\");\n        locales.put(\"ne\", \"नेपाली\");\n        locales.put(\"nl_BE\", \"Vlaams\");\n        locales.put(\"nl_NL\", \"Nederlands (Nederland)\");\n        locales.put(\"nl\", \"Nederlands\");\n        locales.put(\"nn_NO\", \"nynorsk (Noreg)\");\n        locales.put(\"nn\", \"nynorsk\");\n        locales.put(\"no\", \"norsk bokmål\");\n        locales.put(\"no_NO_NY\", \"nynorsk (Noreg)\");\n        locales.put(\"nr\", \"isiNdebele\");\n        locales.put(\"nr_ZA\", \"isiNdebele (South Africa)\");\n        locales.put(\"nso\", \"Sesotho sa Leboa\");\n        locales.put(\"nso_ZA\", \"Sesotho sa Leboa (South Africa)\");\n        locales.put(\"ny_MW\", \"Nyanja (Malawi)\");\n        locales.put(\"ny\", \"Nyanja\");\n        locales.put(\"om_ET\", \"Oromoo (Itoophiyaa)\");\n        locales.put(\"om_KE\", \"Oromoo (Keeniyaa)\");\n        locales.put(\"om\", \"Oromoo\");\n        locales.put(\"or_IN\", \"ଓଡ଼ିଆ (ଭାରତ)\");\n        locales.put(\"or\", \"ଓଡ଼ିଆ\");\n        locales.put(\"pa_Arab_PK\", \"پنجاب - العربية (پکستان)\");\n        locales.put(\"pa_Arab\", \"پنجاب (العربية)\");\n        locales.put(\"pa_Guru_IN\", \"ਪੰਜਾਬੀ - ਗੁਰਮੁਖੀ (ਭਾਰਤ)\");\n        locales.put(\"pa_Guru\", \"ਪੰਜਾਬੀ (ਗੁਰਮੁਖੀ)\");\n        locales.put(\"pa_IN\", \"ਪੰਜਾਬੀ - ਗੁਰਮੁਖੀ (ਭਾਰਤ)\");\n        locales.put(\"pa_PK\", \"ਪੰਜਾਬੀ - Arabic (Pakistan)\");\n        locales.put(\"pa\", \"ਪੰਜਾਬੀ\");\n        locales.put(\"pl_PL\", \"polski (Polska)\");\n        locales.put(\"pl\", \"polski\");\n        locales.put(\"ps_AF\", \"پښتو (افغانستان)\");\n        locales.put(\"ps\", \"پښتو\");\n        locales.put(\"pt_PT\", \"português europeu\");\n        locales.put(\"pt\", \"português\");\n        locales.put(\"ro_MD\", \"română (Moldova, Republica)\");\n        locales.put(\"ro_RO\", \"română (România)\");\n        locales.put(\"ro\", \"română\");\n        locales.put(\"ru_RU\", \"русский (Россия)\");\n        locales.put(\"ru_UA\", \"русский (Украина)\");\n        locales.put(\"ru\", \"русский\");\n        locales.put(\"rw_RW\", \"Kinyarwanda (Rwanda)\");\n        locales.put(\"rw\", \"Kinyarwanda\");\n        locales.put(\"sa_IN\", \"संस्कृत भाषा (भारतम्)\");\n        locales.put(\"sa\", \"संस्कृत भाषा\");\n        locales.put(\"se_FI\", \"se (FI)\");\n        locales.put(\"se_NO\", \"davvisámegiella (Norga)\");\n        locales.put(\"se\", \"davvisámegiella\");\n        locales.put(\"sh_BA\", \"Srpski - Latinica (Bosna i Hercegovina)\");\n        locales.put(\"sh_CS\", \"Srpski - Latinica (Srbija)\");\n        locales.put(\"sh\", \"Srpski (Latinica)\");\n        locales.put(\"sh_YU\", \"Srpski - Latinica (Srbija)\");\n        locales.put(\"sid_ET\", \"Sidaamu Afo (Itiyoophiya)\");\n        locales.put(\"sid\", \"Sidaamu Afo\");\n        locales.put(\"si_LK\", \"සිංහල (ශ්‍රී ලංකාව)\");\n        locales.put(\"si\", \"සිංහල\");\n        locales.put(\"sk_SK\", \"slovenský (Slovenská republika)\");\n        locales.put(\"sk\", \"slovenský\");\n        locales.put(\"sl_SI\", \"slovenščina (Slovenija)\");\n        locales.put(\"sl\", \"slovenščina\");\n        locales.put(\"so_DJ\", \"Soomaali (Jabuuti)\");\n        locales.put(\"so_ET\", \"Soomaali (Itoobiya)\");\n        locales.put(\"so_KE\", \"Soomaali (Kiiniya)\");\n        locales.put(\"so_SO\", \"Soomaali (Soomaaliya)\");\n        locales.put(\"so\", \"Soomaali\");\n        locales.put(\"sq_AL\", \"shqipe (Shqipëria)\");\n        locales.put(\"sq\", \"shqipe\");\n        locales.put(\"sr_BA\", \"српски - Ћирилица (Босна и Херцеговина)\");\n        locales.put(\"sr_CS\", \"Српски - Ћирилица (Србија)\");\n        locales.put(\"sr_Cyrl_BA\", \"српски - Ћирилица (Босна и Херцеговина)\");\n        locales.put(\"sr_Cyrl_CS\", \"Српски - Ћирилица (Србија)\");\n        locales.put(\"sr_Cyrl_ME\", \"Српски - Ћирилица (Црна Гора)\");\n        locales.put(\"sr_Cyrl_RS\", \"Српски - Ћирилица (Србија)\");\n        locales.put(\"sr_Cyrl\", \"Српски (Ћирилица)\");\n        locales.put(\"sr_Cyrl_YU\", \"Српски - Ћирилица (Србија)\");\n        locales.put(\"sr_Latn_BA\", \"Srpski - Latinica (Bosna i Hercegovina)\");\n        locales.put(\"sr_Latn_CS\", \"Srpski - Latinica (Srbija)\");\n        locales.put(\"sr_Latn_ME\", \"Srpski - Latinica (Crna Gora)\");\n        locales.put(\"sr_Latn_RS\", \"Srpski - Latinica (Srbija)\");\n        locales.put(\"sr_Latn\", \"Srpski (Latinica)\");\n        locales.put(\"sr_Latn_YU\", \"Srpski - Latinica (Srbija)\");\n        locales.put(\"sr_ME\", \"Српски - Ћирилица (Црна Гора)\");\n        locales.put(\"sr_RS\", \"Српски - Ћирилица (Србија)\");\n        locales.put(\"sr\", \"Српски\");\n        locales.put(\"sr_YU\", \"Српски - Ћирилица (Србија)\");\n        locales.put(\"ss_SZ\", \"Siswati (Swaziland)\");\n        locales.put(\"ss\", \"Siswati\");\n        locales.put(\"ss_ZA\", \"Siswati (South Africa)\");\n        locales.put(\"st_LS\", \"Sesotho (Lesotho)\");\n        locales.put(\"st\", \"Sesotho\");\n        locales.put(\"st_ZA\", \"Sesotho (South Africa)\");\n        locales.put(\"sv_FI\", \"svenska (Finland)\");\n        locales.put(\"sv_SE\", \"svenska (Sverige)\");\n        locales.put(\"sv\", \"svenska\");\n        locales.put(\"sw_KE\", \"Kiswahili (Kenya)\");\n        locales.put(\"sw_TZ\", \"Kiswahili (Tanzania)\");\n        locales.put(\"sw\", \"Kiswahili\");\n        locales.put(\"syr_SY\", \"ܣܘܪܝܝܐ (ܣܘܪܝܝܐ)\");\n        locales.put(\"syr\", \"ܣܘܪܝܝܐ\");\n        locales.put(\"ta_IN\", \"தமிழ் (இந்தியா)\");\n        locales.put(\"ta\", \"தமிழ்\");\n        locales.put(\"te_IN\", \"తెలుగు (భారత దేళం)\");\n        locales.put(\"te\", \"తెలుగు\");\n        locales.put(\"tg_Cyrl_TJ\", \"Tajik - Cyrillic (Tajikistan)\");\n        locales.put(\"tg_Cyrl\", \"Tajik (Cyrillic)\");\n        locales.put(\"tg_TJ\", \"Tajik (Tajikistan)\");\n        locales.put(\"tg\", \"Tajik\");\n        locales.put(\"th_TH\", \"ไทย (ไทย)\");\n        locales.put(\"th\", \"ไทย\");\n        locales.put(\"ti_ER\", \"ትግርኛ (Eritrea)\");\n        locales.put(\"ti_ET\", \"ትግርኛ (Ethiopia)\");\n        locales.put(\"tig_ER\", \"ትግረ (ኤርትራ)\");\n        locales.put(\"tig\", \"ትግረ\");\n        locales.put(\"ti\", \"ትግርኛ\");\n        locales.put(\"tl\", \"Filipino\");\n        locales.put(\"tn\", \"Setswana\");\n        locales.put(\"tn_ZA\", \"Setswana (South Africa)\");\n        locales.put(\"to_TO\", \"lea fakatonga (Tonga)\");\n        locales.put(\"to\", \"lea fakatonga\");\n        locales.put(\"tr_TR\", \"Türkçe (Türkiye)\");\n        locales.put(\"tr\", \"Türkçe\");\n        locales.put(\"ts\", \"Xitsonga\");\n        locales.put(\"ts_ZA\", \"Xitsonga (South Africa)\");\n        locales.put(\"tt_RU\", \"Татар (Россия)\");\n        locales.put(\"tt\", \"Татар\");\n        locales.put(\"ug_Arab_CN\", \"Uighur - Arabic (China)\");\n        locales.put(\"ug_Arab\", \"Uighur (Arabic)\");\n        locales.put(\"ug_CN\", \"Uighur (China)\");\n        locales.put(\"ug\", \"Uighur\");\n        locales.put(\"uk_UA\", \"українська (Україна)\");\n        locales.put(\"uk\", \"українська\");\n        locales.put(\"ur_IN\", \"اردو (بھارت)\");\n        locales.put(\"ur_PK\", \"اردو (پاکستان)\");\n        locales.put(\"ur\", \"اردو\");\n        locales.put(\"uz_AF\", \"Ўзбек - Араб (Афғонистон)\");\n        locales.put(\"uz_Arab_AF\", \"اۉزبېک - Араб (افغانستان)\");\n        locales.put(\"uz_Arab\", \"اۉزبېک (Араб)\");\n        locales.put(\"uz_Cyrl_UZ\", \"Ўзбек - Кирил (Ўзбекистон)\");\n        locales.put(\"uz_Cyrl\", \"Ўзбек (Кирил)\");\n        locales.put(\"uz_Latn_UZ\", \"o'zbekcha - Lotin (Oʿzbekiston)\");\n        locales.put(\"uz_Latn\", \"o'zbekcha (Lotin)\");\n        locales.put(\"uz_UZ\", \"Ўзбек - Кирил (Ўзбекистон)\");\n        locales.put(\"uz\", \"Ўзбек\");\n        locales.put(\"ve\", \"Tshivenḓa\");\n        locales.put(\"ve_ZA\", \"Tshivenḓa (South Africa)\");\n        locales.put(\"vi_VN\", \"Tiếng Việt (Việt Nam)\");\n        locales.put(\"vi\", \"Tiếng Việt\");\n        locales.put(\"wal_ET\", \"ወላይታቱ (ኢትዮጵያ)\");\n        locales.put(\"wal\", \"ወላይታቱ\");\n        locales.put(\"wo_Latn_SN\", \"Wolof - Latin (Senegal)\");\n        locales.put(\"wo_Latn\", \"Wolof (Latin)\");\n        locales.put(\"wo_SN\", \"Wolof (Senegal)\");\n        locales.put(\"wo\", \"Wolof\");\n        locales.put(\"xh\", \"isiXhosa\");\n        locales.put(\"xh_ZA\", \"isiXhosa (South Africa)\");\n        locales.put(\"yi\", \"ייִדיש\");\n        locales.put(\"yi_001\", \"ייִדיש וועלט\");\n        locales.put(\"yo_BJ\", \"Yorùbá (BJ)\");\n        locales.put(\"yo_NG\", \"Yorùbá (NG)\");\n        locales.put(\"yo\", \"Yorùbá\");\n        locales.put(\"zh_CN\", \"中文（简体） (中国)\");\n        locales.put(\"zh_Hans_CN\", \"中文（简体） (中国)\");\n        locales.put(\"zh_Hans_HK\", \"中文（简体） (中国香港特别行政区)\");\n        locales.put(\"zh_Hans_MO\", \"中文（简体） (中国澳门特别行政区)\");\n        locales.put(\"zh_Hans_SG\", \"中文（简体） (新加坡)\");\n        locales.put(\"zh_Hans\", \"中文（简体）\");\n        locales.put(\"zh_Hant_HK\", \"繁體中文 (中華人民共和國香港特別行政區)\");\n        locales.put(\"zh_Hant_MO\", \"繁體中文 (中華人民共和國澳門特別行政區)\");\n        locales.put(\"zh_Hant_TW\", \"繁體中文 (臺灣)\");\n        locales.put(\"zh_Hant\", \"繁體中文\");\n        locales.put(\"zh_HK\", \"中文（繁体） (中国香港特别行政区)\");\n        locales.put(\"zh_MO\", \"中文（繁体） (中国澳门特别行政区)\");\n        locales.put(\"zh_SG\", \"中文（简体） (新加坡)\");\n        locales.put(\"zh_TW\", \"中文（繁体） (台湾)\");\n        locales.put(\"zh\", \"中文\");\n        locales.put(\"zu\", \"isiZulu\");\n        locales.put(\"zu_ZA\", \"isiZulu (South Africa)\");\n\n        return locales;\n    }\n\n    public Map<String, String> getLocale(final LOCALE locale) {\n        if (locale != null) {\n            if (!this.locales.containsKey(locale)) {\n                loadLocale(locale);\n            }\n            return this.locales.get(locale);\n        } else {\n            return getLocale(getDefaultLocale());\n        }\n    }\n\n    protected final void setLocale(final LOCALE locale, final Map<String, String> map) {\n        this.locales.put(locale, map);\n    }\n\n    public abstract void loadLocale(LOCALE locale);\n\n    public static LOCALE getDefaultLocale() {\n        return I18N_instance._getDefaultLocale();\n    }\n\n    public final LOCALE _getDefaultLocale() {\n        return this.defaultLocale;\n    }\n\n    public static LOCALE stringToLocale(final String localeString) {\n\n        for (final LOCALE locale : LOCALE.values()) {\n            if (locale.toString().equals(localeString)) {\n                return locale;\n            }\n        }\n\n        return null;\n    }\n\n    protected String getText(final String string) {\n        return this.getText(this.defaultLocale, string);\n    }\n\n    protected String getText(final LOCALE locale, final String string) {\n        final Map<String, String> localeMap = getLocale(locale);\n\n        if (localeMap == null) {\n            return string;\n        }\n\n        final String translation = localeMap.get(string);\n        if (StringUtil.isBlank(translation)) {\n            return string;\n        }\n\n        return translation;\n    }\n\n    protected String getText(final String string, final Arg... args) {\n        return new TextTemplate(t_(string)).toString(args);\n    }\n\n    protected String getText(final LOCALE locale, final String string, final Arg... args) {\n        return new TextTemplate(t_(string, locale)).toString(args);\n    }\n\n    public static String t_(final String string) {\n        return StringUtil.isBlank(string) ? \"\" : I18N_instance.getText(string);\n    }\n\n    public static String t_(final String string, final Arg... args) {\n        return string.isEmpty() ? \"\" : I18N_instance.getText(string, args);\n    }\n\n    public static String t_(final String string, final LOCALE locale) {\n        return string.isEmpty() ? \"\" : I18N_instance.getText(locale, string);\n    }\n\n    public static String t_(final String string, final LOCALE locale, final Arg... args) {\n        return StringUtil.isBlank(string) ? \"\" : I18N_instance.getText(locale, string, args);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/i18n/T_.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.i18n;\n\nimport static org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n.LOCALE;\nimport static org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n.t_;\n\nimport org.bonitasoft.web.toolkit.client.common.texttemplate.Arg;\n\npublic class T_ {\n\n    private final String message;\n    private Arg[] args;\n\n    public T_(String message) {\n        this.message = message;\n    }\n\n    public T_(String message, Arg... args) {\n        this(message);\n        this.args = args;\n    }\n\n    public String localize(LOCALE locale) {\n        if (args != null) {\n            return t_(message, locale, args);\n        }\n        return AbstractI18n.t_(message, locale);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/i18n/model/I18nLocaleDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.i18n.model;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * @author Séverin Moussel\n */\npublic class I18nLocaleDefinition extends ItemDefinition<I18nLocaleItem> {\n\n    /**\n     * Singleton\n     */\n    public static I18nLocaleDefinition get() {\n        return (I18nLocaleDefinition) Definitions.get(TOKEN);\n    }\n\n    public static final String TOKEN = \"i18nlocale\";\n\n    /**\n     * the URL of i18nlocale resource\n     */\n    private static final String API_URL = \"../API/system/i18nlocale\";\n\n    @Override\n    public String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(I18nLocaleItem.ATTRIBUTE_LOCALE);\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        createAttribute(I18nLocaleItem.ATTRIBUTE_LOCALE, ItemAttribute.TYPE.STRING);\n        createAttribute(I18nLocaleItem.ATTRIBUTE_NAME, ItemAttribute.TYPE.STRING);\n    }\n\n    @Override\n    protected I18nLocaleItem _createItem() {\n        return new I18nLocaleItem();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/i18n/model/I18nLocaleItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.i18n.model;\n\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Séverin Moussel\n */\npublic class I18nLocaleItem extends Item {\n\n    public static final String ATTRIBUTE_NAME = \"name\";\n    public static final String ATTRIBUTE_LOCALE = \"locale\";\n\n    public I18nLocaleItem() {\n        super();\n    }\n\n    public I18nLocaleItem(final IItem item) {\n        super(item);\n    }\n\n    public I18nLocaleItem(final String locale, final String name) {\n        this();\n        this.setAttribute(ATTRIBUTE_LOCALE, locale);\n        this.setAttribute(ATTRIBUTE_NAME, name);\n    }\n\n    @Override\n    public ItemDefinition<I18nLocaleItem> getItemDefinition() {\n        return new I18nLocaleDefinition();\n    }\n\n    public String getName() {\n        return getAttributeValue(ATTRIBUTE_NAME);\n    }\n\n    public String getLocale() {\n        return getAttributeValue(ATTRIBUTE_LOCALE);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/i18n/model/I18nTranslationDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.i18n.model;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * @author Séverin Moussel\n */\npublic class I18nTranslationDefinition extends ItemDefinition {\n\n    /**\n     * Singleton\n     */\n    public static I18nTranslationDefinition get() {\n        return (I18nTranslationDefinition) Definitions.get(TOKEN);\n    }\n\n    public static final String TOKEN = \"i18ntranslation\";\n\n    /**\n     * the URL of i18nlocale resource\n     */\n    private static final String API_URL = \"../API/system/i18ntranslation\";\n\n    private static final String PLURAL_RESOURCES_URL = \"../API/system/i18ntranslation\";\n\n    @Override\n    public String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(I18nTranslationItem.ATTRIBUTE_KEY);\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        createAttribute(I18nTranslationItem.ATTRIBUTE_KEY, ItemAttribute.TYPE.TEXT);\n        createAttribute(I18nTranslationItem.ATTRIBUTE_VALUE, ItemAttribute.TYPE.TEXT);\n    }\n\n    @Override\n    protected IItem _createItem() {\n        return new I18nTranslationItem();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/i18n/model/I18nTranslationItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.i18n.model;\n\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Séverin Moussel\n */\npublic class I18nTranslationItem extends Item {\n\n    public I18nTranslationItem() {\n        super();\n    }\n\n    public I18nTranslationItem(final IItem item) {\n        super(item);\n    }\n\n    public static final String ATTRIBUTE_KEY = \"key\";\n\n    public static final String ATTRIBUTE_VALUE = \"value\";\n\n    public I18nTranslationItem(final String key, final String value) {\n        this();\n        this.setAttribute(ATTRIBUTE_KEY, key);\n        this.setAttribute(ATTRIBUTE_VALUE, value);\n    }\n\n    @Override\n    public ItemDefinition getItemDefinition() {\n        return new I18nTranslationDefinition();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/json/JSonItemReader.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.json;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Optional;\n\nimport org.bonitasoft.web.toolkit.client.common.AbstractTreeNode;\nimport org.bonitasoft.web.toolkit.client.common.Tree;\nimport org.bonitasoft.web.toolkit.client.common.TreeIndexed;\nimport org.bonitasoft.web.toolkit.client.common.TreeLeaf;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * This class is just a set of functions used to read the JSon returned by the server side\n *\n * @author Séverin Moussel\n */\npublic class JSonItemReader {\n\n    public static final boolean APPLY_VALIDATORS = true;\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // UNSERIALIZER\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    private static JSonUnserializer UNSERIALIZER = null;\n\n    /**\n     * @param unserializer\n     *        the unserializer to set\n     */\n    public static void setUnserializer(final JSonUnserializer unserializer) {\n        UNSERIALIZER = unserializer;\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // PARSING\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Parse a Map on a JSon String\n     *\n     * @param json\n     */\n    public static Map<String, String> parseMap(final String json) {\n        final AbstractTreeNode<String> tree = UNSERIALIZER._unserializeTree(json);\n\n        if (!(tree instanceof TreeIndexed<?>)) {\n            return new HashMap<>();\n        }\n\n        return parseMap((TreeIndexed<String>) tree);\n    }\n\n    /**\n     * Parse a Map on a Tree\n     *\n     * @param tree\n     */\n    private static Map<String, String> parseMap(final TreeIndexed<String> tree) {\n        final Map<String, String> result = new HashMap<>();\n\n        for (final Entry<String, String> entry : tree.getValues().entrySet()) {\n            result.put(entry.getKey(), entry.getValue());\n        }\n\n        return result;\n    }\n\n    /**\n     * Parse an item based on a JSon String\n     *\n     * @param json\n     * @param itemDefinition\n     */\n    public static <E extends IItem> E parseItem(final String json, final ItemDefinition<E> itemDefinition) {\n        AbstractTreeNode<String> tree = UNSERIALIZER._unserializeTree(json);\n\n        if (tree instanceof Tree<?>) {\n            tree = ((Tree<String>) tree).get(0);\n\n            if (!(tree instanceof TreeIndexed<?>)) {\n                return itemDefinition.createItem();\n            }\n        }\n\n        return parseItem((TreeIndexed<String>) tree, itemDefinition);\n    }\n\n    /**\n     * Parse an item based on a TreeIndexed\n     */\n    private static <E extends IItem> E parseItem(final TreeIndexed<String> tree,\n            final ItemDefinition<E> itemDefinition) {\n        return parseItem(tree, itemDefinition, APPLY_VALIDATORS);\n    }\n\n    /**\n     * Parse an item based on a TreeIndexed\n     *\n     * @param tree\n     * @param itemDefinition\n     */\n    private static <E extends IItem> E parseItem(final TreeIndexed<String> tree, final ItemDefinition<E> itemDefinition,\n            final boolean applyValidators) {\n        final Optional<E> discriminatedItem = itemDefinition.getDiscriminatedHelper()\n                .map(h -> h.findItemCreator(tree).get());\n        final E item = discriminatedItem.orElseGet(itemDefinition::createItem);\n\n        item.setApplyValidators(applyValidators);\n\n        for (final Entry<String, AbstractTreeNode<String>> entry : tree.getNodes().entrySet()) {\n            // primitive type\n            if (entry.getValue() instanceof TreeLeaf<?>) {\n                item.setAttribute(entry.getKey(), ((TreeLeaf<String>) entry.getValue()).getValue());\n                // json object\n            } else if (entry.getValue() instanceof TreeIndexed<?>) {\n                item.setDeploy(\n                        entry.getKey(),\n                        parseItem(\n                                (TreeIndexed<String>) entry.getValue(),\n                                itemDefinition.getDeployDefinition(entry.getKey()))\n\n                );\n                // json list - set directly json in attribute value\n            } else if (entry.getValue() instanceof Tree<?>) {\n                item.setAttribute(entry.getKey(), entry.getValue().toJson());\n            }\n        }\n\n        return item;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/json/JSonItemWriter.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.json;\n\nimport java.util.LinkedList;\nimport java.util.List;\n\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\n\n/**\n * This class is a set of functions used to write the JSon to return to the client side\n *\n * @author Séverin Moussel\n * @param <T>\n *        The class of the Items to write\n */\npublic class JSonItemWriter<T extends IItem> {\n\n    /**\n     * The items to write\n     */\n    private final List<IItem> itemList = new LinkedList<>();\n\n    /**\n     * If you use this constructor, you will have to use one of the append functions.\n     */\n    public JSonItemWriter() {\n\n    }\n\n    public JSonItemWriter<T> append(final List<T> datas) {\n        this.itemList.addAll(datas);\n        return this;\n    }\n\n    public JSonItemWriter<T> append(final IItem item) {\n        this.itemList.add(item);\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return JSonSerializer.serializeCollection(this.itemList);\n    }\n\n    public static String itemToJSON(final IItem item) {\n        return JSonSerializer.serialize(item);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/json/JSonSerializer.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.json;\n\nimport java.text.SimpleDateFormat;\nimport java.util.Collection;\nimport java.util.Date;\nimport java.util.Map;\n\nimport org.bonitasoft.web.toolkit.client.common.exception.http.JsonExceptionSerializer;\n\n/**\n * @author Séverin Moussel\n */\npublic class JSonSerializer extends JSonUtil {\n\n    // Thread local as recommended in the javadoc\n    private static final ThreadLocal<SimpleDateFormat> dateTimeFormat = ThreadLocal\n            .withInitial(() -> new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss.SSS\"));\n\n    public static String serialize(final JsonSerializable object) {\n        return serializeInternal(object).toString();\n    }\n\n    private static StringBuilder serializeInternal(JsonSerializable object) {\n        if (object == null) {\n            return new StringBuilder(\"null\");\n        }\n        return new StringBuilder(object.toJson());\n    }\n\n    public static String serialize(final Object object) {\n        return serializeInternal(object).toString();\n    }\n\n    private static StringBuilder serializeInternal(Object object) {\n        if (object == null) {\n            return new StringBuilder(\"null\");\n        } else if (object instanceof JsonSerializable) {\n            return serializeInternal((JsonSerializable) object);\n        } else if (object instanceof Collection<?>) {\n            return serializeCollectionInternal((Collection<?>) object);\n        } else if (object instanceof Map<?, ?>) {\n            return serializeMapInternal((Map<?, ?>) object);\n        } else if (object instanceof Number) {\n            return new StringBuilder(object.toString());\n        } else if (object instanceof Boolean) {\n            return new StringBuilder((Boolean) object ? \"true\" : \"false\");\n        } else if (object instanceof Date) {\n            return quoteInternal(dateTimeFormat.get().format((Date) object));\n        } else if (object instanceof Throwable) {\n            return new StringBuilder(serializeException((Throwable) object));\n        }\n\n        return quoteInternal(object.toString());\n    }\n\n    public static String serializeCollection(final Collection<?> list) {\n        return serializeCollectionInternal(list).toString();\n    }\n\n    private static StringBuilder serializeCollectionInternal(Collection<?> list) {\n        final StringBuilder json = new StringBuilder(\"[\");\n\n        boolean first = true;\n        for (final Object item : list) {\n            json.append(!first ? \",\" : \"\").append(serializeInternal(item));\n            first = false;\n        }\n\n        json.append(\"]\");\n\n        return json;\n    }\n\n    public static String serializeMap(final Map<?, ?> map) {\n        return serializeMapInternal(map).toString();\n    }\n\n    private static StringBuilder serializeMapInternal(Map<?, ?> map) {\n        final StringBuilder json = new StringBuilder().append(\"{\");\n\n        boolean first = true;\n        for (final Object key : map.keySet()) {\n            json.append(!first ? \",\" : \"\").append(quoteInternal(key.toString())).append(\":\")\n                    .append(serializeInternal(map.get(key)));\n            first = false;\n        }\n\n        json.append(\"}\");\n\n        return json;\n    }\n\n    public static String serializeException(final Throwable e) {\n        return new JsonExceptionSerializer(e).end();\n    }\n\n    public static String serializeStringMap(final Map<?, String> map) {\n        return serializeStringMapInternal(map).toString();\n    }\n\n    private static StringBuilder serializeStringMapInternal(Map<?, String> map) {\n        final StringBuilder json = new StringBuilder(\"{\");\n\n        boolean first = true;\n        for (final Object key : map.keySet()) {\n            json.append(!first ? \",\" : \"\").append(quoteInternal(key.toString())).append(\":\")\n                    .append(quoteInternal(map.get(key)));\n            first = false;\n        }\n\n        json.append(\"}\");\n\n        return json;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/json/JSonUnserializer.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.json;\n\nimport org.bonitasoft.web.toolkit.client.common.AbstractTreeNode;\n\n/**\n * @author Séverin Moussel\n */\npublic interface JSonUnserializer {\n\n    AbstractTreeNode<String> _unserializeTree(final String json);\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/json/JSonUtil.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.json;\n\nimport static java.lang.Integer.toHexString;\n\n/**\n * @author Séverin Moussel\n */\npublic class JSonUtil {\n\n    public static String quote(final String value) {\n        return quoteInternal(value).toString();\n    }\n\n    public static StringBuilder quoteInternal(final String value) {\n        return new StringBuilder(\"\\\"\").append(escape(value)).append(\"\\\"\");\n    }\n\n    public static String escape(final String string) {\n        return escapeInternal(string).toString();\n    }\n\n    private static StringBuilder escapeInternal(String string) {\n        if (string == null || string.length() == 0) {\n            return new StringBuilder();\n        }\n\n        char b;\n        char c = 0;\n        int i;\n        final int len = string.length();\n        final StringBuilder sb = new StringBuilder(len + 4);\n\n        for (i = 0; i < len; i += 1) {\n            b = c;\n            c = string.charAt(i);\n            switch (c) {\n                case '<':\n                case '>':\n                case '\\'':\n                case '\\\\':\n                case '\"':\n                    sb.append(convertToUnicodeInternal(c));\n                    break;\n                case '/':\n                    if (b == '<') {\n                        sb.append('\\\\');\n                    }\n                    sb.append(c);\n                    break;\n                case '\\b':\n                    sb.append(\"\\\\b\");\n                    break;\n                case '\\t':\n                    sb.append(\"\\\\t\");\n                    break;\n                case '\\n':\n                    sb.append(\"\\\\n\");\n                    break;\n                case '\\f':\n                    sb.append(\"\\\\f\");\n                    break;\n                case '\\r':\n                    sb.append(\"\\\\r\");\n                    break;\n                default:\n                    if (c < ' ' || c >= '\\u0080' && c < '\\u00a0' || c >= '\\u2000' && c < '\\u2100') {\n                        sb.append(convertToUnicodeInternal(c));\n                    } else {\n                        sb.append(c);\n                    }\n            }\n        }\n\n        return new StringBuilder(sb);\n    }\n\n    private static StringBuffer convertToUnicodeInternal(char character) {\n        StringBuilder hexString = new StringBuilder(\"000\").append(toHexString(character));\n        return new StringBuffer(\"\\\\u\").append(hexString.substring(hexString.length() - 4));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/json/JsonSerializable.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.json;\n\n/**\n * @author Séverin Moussel\n */\npublic interface JsonSerializable {\n\n    String toJson();\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/session/SessionDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.session;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Definitions;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * User definition\n *\n * @author Julien Mege\n */\npublic class SessionDefinition extends ItemDefinition<SessionItem> {\n\n    /**\n     * Singleton\n     */\n    public static SessionDefinition get() {\n        return (SessionDefinition) Definitions.get(TOKEN);\n    }\n\n    public static final String TOKEN = \"session\";\n\n    /**\n     * the URL of user resource\n     */\n    private static final String API_URL = \"../API/system/session\";\n\n    @Override\n    public String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n        setPrimaryKeys(SessionItem.ATTRIBUTE_USERID);\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return API_URL;\n    }\n\n    @Override\n    protected void defineAttributes() {\n        createAttribute(SessionItem.ATTRIBUTE_FIRSTNAME, ItemAttribute.TYPE.STRING);\n        createAttribute(SessionItem.ATTRIBUTE_LASTNAME, ItemAttribute.TYPE.STRING);\n        createAttribute(SessionItem.ATTRIBUTE_ICON, ItemAttribute.TYPE.IMAGE);\n        createAttribute(SessionItem.ATTRIBUTE_USERID, ItemAttribute.TYPE.STRING);\n        createAttribute(SessionItem.ATTRIBUTE_USERNAME, ItemAttribute.TYPE.STRING);\n        createAttribute(SessionItem.ATTRIBUTE_CONF, ItemAttribute.TYPE.STRING);\n    }\n\n    @Override\n    public SessionItem _createItem() {\n        return new SessionItem();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/session/SessionItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.session;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\n\n/**\n * @author Julien Mege\n */\npublic class SessionItem extends Item {\n\n    public SessionItem() {\n        super();\n    }\n\n    public static final String ATTRIBUTE_SESSIONID = \"session_id\";\n\n    public static final String ATTRIBUTE_USERID = \"user_id\";\n\n    public static final String ATTRIBUTE_FIRSTNAME = \"first_name\";\n\n    public static final String ATTRIBUTE_LASTNAME = \"last_name\";\n\n    public static final String ATTRIBUTE_USERNAME = \"user_name\";\n\n    public static final String ATTRIBUTE_ICON = \"icon\";\n\n    public static final String ATTRIBUTE_IS_TECHNICAL_USER = \"is_technical_user\";\n\n    public static final String ATTRIBUTE_VERSION = \"version\";\n\n    public static final String ATTRIBUTE_BRANDING_VERSION = \"branding_version\";\n\n    public static final String ATTRIBUTE_BRANDING_VERSION_WITH_DATE = \"branding_version_with_date\";\n\n    public static final String ATTRIBUTE_CONF = \"conf\";\n\n    public static final String ATTRIBUTE_COPYRIGHT = \"copyright\";\n\n    public static final String ATTRIBUTE_IS_GUEST_USER = \"is_guest_user\";\n\n    @Override\n    public ItemDefinition getItemDefinition() {\n        return new SessionDefinition();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/texttemplate/Arg.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.texttemplate;\n\n/**\n * @author Séverin Moussel\n */\n\npublic class Arg {\n\n    private final String name;\n\n    private final Object value;\n\n    public Arg(final String name, final Object value) {\n        this.name = name;\n        this.value = value;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public String getValue() {\n        return value == null ? null : value.toString();\n    }\n\n    @Override\n    public String toString() {\n        return name + \":\" + getValue();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/texttemplate/TextTemplate.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.texttemplate;\n\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * Text template using %...% syntax <br />\n * The passed parameters will replace %parameter_name% by parameterValue.\n *\n * @author Séverin Moussel\n */\npublic class TextTemplate {\n\n    private String template = null;\n\n    private List<String> expectedParameters = null;\n    private final Pattern regex = Pattern.compile(\"%(.*?)%\");\n\n    public TextTemplate(final String template) {\n        this.template = template;\n    }\n\n    public String toString(final Map<String, String> data) {\n        String result = this.template;\n\n        for (final Entry<String, String> entry : data.entrySet()) {\n            result = result.replaceAll(\"%\" + entry.getKey() + \"%\", entry.getValue() != null ? entry.getValue() : \"\");\n        }\n\n        return result;\n    }\n\n    public String toString(final Arg... parameters) {\n        String result = this.template;\n        for (final Arg parameter : parameters) {\n            result = result.replaceAll(\"%\" + parameter.getName() + \"%\", parameter.getValue());\n        }\n        return result;\n    }\n\n    public String toString(final List<Arg> parameters) {\n        String result = this.template;\n        for (final Arg parameter : parameters) {\n            result = result.replaceAll(\"%\" + parameter.getName() + \"%\", parameter.getValue());\n        }\n        return result;\n    }\n\n    @Override\n    public String toString() {\n        return this.template;\n    }\n\n    /**\n     * Read the template to get the excepted parameters.<br />\n     * Expected parameters are the strings between %...%\n     */\n    public List<String> getExpectedParameters() {\n        // Cache result\n        if (this.expectedParameters == null) {\n            parseExpectedParameters();\n        }\n\n        return this.expectedParameters;\n\n    }\n\n    /**\n     * Read the template to get the excepted parameters and store the result in the class variable \"expectedParameters\"\n     */\n    private void parseExpectedParameters() {\n        this.expectedParameters = new LinkedList<>();\n        Matcher matcher = regex.matcher(this.template);\n        while (matcher.find()) {\n            this.expectedParameters.add(matcher.group(1));\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/util/MapUtil.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.util;\n\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.bonitasoft.web.toolkit.client.common.texttemplate.Arg;\n\npublic class MapUtil {\n\n    /**\n     * Get a value in a Map<String, String>.\n     *\n     * @param map\n     *        The map to search in\n     * @param key\n     *        The key of the element to get.\n     * @param defaultValue\n     *        The value to return if the element at the defined key is not set.\n     * @return This method returns the value corresponding or the defaultValue if the element doesn't exist.\n     */\n    public static String getValue(final Map<String, String> map, final String key, final String defaultValue) {\n        if (!map.containsKey(key)) {\n            return defaultValue;\n        }\n        return map.get(key);\n    }\n\n    /**\n     * Get a value in a Map<String, String> and convert it to a long.\n     *\n     * @param map\n     *        The map to search in\n     * @param key\n     *        The key of the element to get.\n     * @return This method returns the Long value corresponding or NULL if the element doesn't exist or is empty.\n     */\n    public static Long getValueAsLong(final Map<String, String> map, final String key) throws NumberFormatException {\n        final String value = map.get(key);\n        if (StringUtil.isBlank(value)) {\n            return null;\n        }\n\n        return Long.valueOf(value);\n    }\n\n    /**\n     * Get a value in a Map<String, String> and convert it to an integer.\n     *\n     * @param map\n     *        The map to search in\n     * @param key\n     *        The key of the element to get.\n     * @return This method returns the long value corresponding or NULL if the element doesn't exist or is empty.\n     */\n    public static Boolean getValueAsBoolean(final Map<String, String> map, final String key)\n            throws IllegalArgumentException {\n        final String value = map.get(key);\n        if (StringUtil.isBlank(value)) {\n            return null;\n        }\n\n        return StringUtil.toBoolean(value);\n    }\n\n    /**\n     * Check if a map entry is blank (not set, NULL or empty String).\n     *\n     * @param map\n     *        The map to check\n     * @param key\n     *        The key to check\n     * @return This method returns TRUE if the key is not set OR null OR an empty String, otherwise FALSE.\n     */\n    public static boolean isBlank(final Map<String, String> map, final String key) {\n        return !map.containsKey(key) || StringUtil.isBlank(map.get(key));\n    }\n\n    /**\n     * Remove a map entry if its value is blank (NULL or empty String)\n     *\n     * @param map\n     *        The map to check\n     * @param key\n     *        The key to check\n     * @return This method returns TRUE if the key is not set OR has been removed, otherwise FALSE.\n     */\n    public static boolean removeIfBlank(final Map<String, String> map, final String key) {\n        if (isBlank(map, key)) {\n            map.remove(key);\n            return true;\n        }\n        return false;\n    }\n\n    public static Map<String, String> asMap(final Arg... args) {\n        final Map<String, String> results = new HashMap<>();\n        for (final Arg arg : args) {\n            results.put(arg.getName(), arg.getValue());\n        }\n\n        return results;\n    }\n\n    public static abstract class ForEach<K, V> {\n\n        protected abstract void apply(K key, V value);\n    }\n\n    public static <K, V> void iterate(Map<K, V> map, ForEach<K, V> modifier) {\n        Iterator<Entry<K, V>> it = map.entrySet().iterator();\n        while (it.hasNext()) {\n            Entry<K, V> entry = it.next();\n            modifier.apply(entry.getKey(), entry.getValue());\n        }\n    }\n\n    public static String getMandatory(Map<String, String> map, String key) {\n        String value = map.get(key);\n        if (value == null) {\n            throw new RuntimeException(\"Can't find value corresponding to \" + key);\n        }\n        return value;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/util/StringUtil.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.util;\n\n/**\n * @author Julien Mege\n */\npublic abstract class StringUtil {\n\n    /**\n     * Convert a String to a boolean using the smart way<br />\n     * <ul>\n     * <li>if the String is NULL, the Boolean will be NULL.</li>\n     * <li>if the String can be cast to a long, the boolean will be TRUE if the long value is > 0, otherwise FALSE.</li>\n     * <li>if the String is equal to \"true\", \"yes\" or \"ok\" the boolean will be TRUE.</li>\n     * <li>if the String is equal to \"false\", \"no\" or \"ko\" the boolean will be FALSE.</li>\n     * <li>all other cases will throw an IllegalArgumentException</li>\n     *\n     * @param value\n     *        The value to convert\n     * @return This method will return the Boolean value of the value passed.\n     * @throw IllegalArgumentException\n     */\n    public static Boolean toBoolean(final String value) throws IllegalArgumentException {\n        if (value == null) {\n            return null;\n        }\n\n        // FIXME Manage integer values (<=0 false, >=1 true)\n\n        if (\"true\".equals(value) || \"yes\".equals(value) || \"ok\".equals(value)) {\n            return true;\n        } else if (\"false\".equals(value) || \"no\".equals(value) || \"ko\".equals(value)) {\n            return false;\n        }\n\n        try {\n            return Integer.parseInt(value) > 0;\n        } catch (final NumberFormatException e) {\n            throw new IllegalArgumentException(value + \" is not a valid boolean value\");\n        }\n    }\n\n    /**\n     * Converts a String to an integer plus if the String is NULL, the returned value will be NULL.\n     */\n    public static Integer toInteger(final String value) throws NumberFormatException {\n        if (value == null) {\n            return null;\n        }\n        return Integer.valueOf(value);\n    }\n\n    /**\n     * Check if a value is blank (NULL or empty String).\n     *\n     * @param value\n     *        The value to check\n     * @return This method returns TRUE if the value is null OR an empty String, otherwise FALSE.\n     */\n    public static boolean isBlank(final String value) {\n        return value == null || value.isEmpty() || value.trim().isEmpty();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/APIID.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIIncorrectIdException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIItemIdMalformedException;\nimport org.bonitasoft.web.toolkit.client.common.json.JSonSerializer;\nimport org.bonitasoft.web.toolkit.client.common.json.JsonSerializable;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.bonitasoft.web.toolkit.client.ui.utils.ListUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Julien Mege\n */\npublic class APIID implements JsonSerializable {\n\n    private static final String SEPARATOR = \"/\";\n\n    private final List<String> ids = new ArrayList<>();\n\n    private ItemDefinition<?> itemDefinition = null;\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(APIID.class.getName());\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // CONSTRUCTORS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    private APIID(final String... id) {\n        this(Arrays.asList(id));\n    }\n\n    private APIID(final Long... id) {\n        for (final Long i : id) {\n            this.ids.add(i != null ? String.valueOf(i) : null);\n        }\n    }\n\n    private APIID(final List<String> ids) {\n        // if the id passed is a serialized APIID\n        if (ids.size() == 1 && ids.get(0).contains(\"/\")) {\n            this.ids.addAll(Arrays.asList(ids.get(0).split(\"/\")));\n        } else {\n            this.ids.addAll(ids);\n        }\n    }\n\n    public void setItemDefinition(final ItemDefinition<?> definition) {\n        this.itemDefinition = definition;\n\n        final int size = this.itemDefinition.getPrimaryKeys().size();\n\n        if (this.ids.size() < size) {\n            if (size == 0) {\n                throw new APIException(this.itemDefinition.getClass().getName() + \" is missing a valid primaryKey\");\n            }\n\n            if (size == 1) {\n                throw new APIException(\n                        \"Wrong APIID format for  [\" + this.itemDefinition.getClass().getName() + \"].\" +\n                                \" This APIID must be a single id.\");\n            }\n\n            throw new APIException(\n                    \"Wrong APIID format for  [\" + this.itemDefinition.getClass().getName() + \"].\" +\n                            \" This APIID must be compound of [\" +\n                            ListUtils.join(this.itemDefinition.getPrimaryKeys(), \",\") +\n                            \"] in this exact order.\");\n        }\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // STATIC CONSTRUCTORS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public static APIID makeAPIID(final String... id) {\n        if (id == null) {\n            return null;\n        }\n        return makeAPIID(Arrays.asList(id));\n    }\n\n    public static APIID makeAPIID(final Long... ids) {\n        if (ids == null || ids.length == 0) {\n            return null;\n        }\n\n        // If at least one id is not null\n        for (final Long id : ids) {\n            if (id != null && id > 0L) {\n                return new APIID(ids);\n            }\n        }\n\n        return null;\n    }\n\n    public static APIID makeAPIID(final List<String> ids) {\n        if (ids == null || ids.size() == 0) {\n            return null;\n        }\n\n        // If at least one id is not null\n        for (final String id : ids) {\n            if (id != null && !id.isEmpty()) {\n                return new APIID(ids);\n            }\n        }\n\n        return null;\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // GETTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public List<String> getIds() {\n        return this.ids;\n    }\n\n    @Override\n    public String toString() {\n        String resourceId = \"\";\n        if (this.ids != null && this.ids.size() > 0) {\n            for (final String id : this.ids) {\n                if (!\"\".equals(resourceId)) {\n                    resourceId = resourceId + SEPARATOR;\n                }\n                resourceId = resourceId + id;\n            }\n        }\n        return resourceId;\n    }\n\n    public boolean isValidLongID() {\n        if (this.ids.size() == 1) {\n            try {\n                final long lid = Long.parseLong(this.ids.get(0));\n                if (lid > 0L) {\n                    return true;\n                }\n            } catch (final NumberFormatException e) {\n                LOGGER.debug(this.ids.get(0) + \" is not a valid long ID. ID must be non-zero positive number.\");\n            }\n        } else {\n            LOGGER.debug(\"ID is not a valid long ID. ID must not be multiple.\");\n        }\n        return false;\n    }\n\n    public Long toLong() {\n        if (this.ids.size() > 1) {\n            throw new IllegalArgumentException(\"Can't convert compound ID to long\");\n        }\n\n        try {\n            final long lid = Long.parseLong(this.ids.get(0));\n            if (lid > 0L) {\n                return lid;\n            } else {\n                //zero or negative ids are not allowed\n                String errorMessage = lid + \" is not a valid long ID. ID must be non-zero positive.\";\n                LOGGER.debug(errorMessage);\n                throw new APIIncorrectIdException(errorMessage);\n            }\n        } catch (final NumberFormatException e) {\n            throw new APIItemIdMalformedException(\"APIID\", \"Can't convert non numeric ID to long\");\n        }\n\n    }\n\n    /*\n     * Retrieve a part of the id with his index.\n     * @return this method return a part of the id as a String.\n     */\n    public final String getPart(final int partIndex) {\n        return this.ids.get(partIndex);\n    }\n\n    public final Long getPartAsLong(final int partIndex) {\n        return Long.parseLong(getPart(partIndex));\n    }\n\n    public String getPart(final String attributeName) {\n        final int index = this.itemDefinition.getPrimaryKeys().indexOf(attributeName);\n\n        if (index == -1) {\n            throw new APIException(attributeName +\n                    \" is an invalid APIID index. \" +\n                    \"This APIID must be made of \" +\n                    ListUtils.join(this.itemDefinition.getPrimaryKeys(), \", \") +\n                    \" in this exact order.\");\n        }\n\n        return this.ids.get(index);\n    }\n\n    public Long getPartAsLong(final String attributeName) {\n        String part = getPart(attributeName);\n        return part == null ? null : Long.valueOf(part);\n    }\n\n    public APIID getPartAsAPIID(final String attributeName) {\n        return APIID.makeAPIID(getPart(attributeName));\n    }\n\n    public static List<Long> toLongList(final List<APIID> ids) {\n        final List<Long> results = new ArrayList<>();\n        for (final APIID id : ids) {\n            results.add(id.toLong());\n        }\n        return results;\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // EQUALS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public int hashCode() {\n        return toString().hashCode();\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (obj == null) {\n            return false;\n        }\n        return toString().equals(obj.toString());\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // JSON\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public String toJson() {\n        return JSonSerializer.serialize(this.ids);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/Definitions.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.web.toolkit.client.ItemDefinitionFactory;\n\n/**\n * @author Julien Mege\n */\npublic class Definitions {\n\n    private final Map<String, ItemDefinition<?>> itemDefinitions = new HashMap<>();\n\n    private static final Definitions INSTANCE = new Definitions();\n\n    /**\n     * Get the ViewController instance.\n     *\n     * @return the unique instance of the ViewController.\n     */\n    public static Definitions getInstance() {\n        return INSTANCE;\n    }\n\n    public static ItemDefinition<?> get(final String token) {\n        return getInstance().getDefinition(token);\n    }\n\n    public final ItemDefinition<?> getDefinition(final String token) {\n        if (itemDefinitions.containsKey(token)) {\n            return itemDefinitions.get(token);\n        } else if (DummyItemDefinition.TOKEN.equals(token)) {\n            return new DummyItemDefinition();\n        } else {\n            final ItemDefinition<?> itemDefinition = ItemDefinitionFactory.getDefaultFactory()\n                    .defineItemDefinitions(token);\n            if (itemDefinition != null) {\n                itemDefinitions.put(token, itemDefinition);\n                return itemDefinition;\n            }\n            // TODO Throw exception\n            return null;\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/DiscriminatedItemDefinitionHelper.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item;\n\nimport java.util.Map;\nimport java.util.function.Supplier;\n\nimport org.bonitasoft.web.rest.model.application.AbstractApplicationDefinition;\nimport org.bonitasoft.web.toolkit.client.common.TreeIndexed;\n\n/**\n * This helps Items definitions which needs an attribute value to determine their concrete implementation.\n *\n * @see ItemDefinition#getDiscriminatedHelper()\n * @see AbstractApplicationDefinition#getDiscriminatedHelper() for an example\n * @param <E> the {@link IItem} which is defined, same as in {@link ItemDefinition}\n */\npublic interface DiscriminatedItemDefinitionHelper<E extends IItem> {\n\n    /**\n     * Find the appropriate creator with attributes to discriminate\n     *\n     * @param attributes the attributes for creation, one of which should be used to discriminate\n     * @return the item creator\n     */\n    public Supplier<? extends E> findItemCreator(final Map<String, String> attributes);\n\n    /**\n     * Find the appropriate creator with properties tree to discriminate\n     *\n     * @param tree the tree of properties, one of which should be used to discriminate\n     * @return the item creator\n     */\n    /*\n     * We could delegate to the attributes method with a default implementation,\n     * but it wouldn't be as effective as getting the direct value.\n     */\n    public Supplier<? extends E> findItemCreator(final TreeIndexed<String> tree);\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/DummyItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item;\n\n/**\n * @author Séverin Moussel\n */\npublic class DummyItem extends Item {\n\n    public DummyItem() {\n        super();\n    }\n\n    @Override\n    public ItemDefinition<DummyItem> getItemDefinition() {\n        return DummyItemDefinition.get();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/DummyItemDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item;\n\n/**\n * @author Séverin Moussel\n */\npublic class DummyItemDefinition extends ItemDefinition<DummyItem> {\n\n    /**\n     * Singleton\n     */\n    public static DummyItemDefinition get() {\n        return (DummyItemDefinition) Definitions.get(TOKEN);\n    }\n\n    public static final String TOKEN = \"dummy\";\n\n    @Override\n    protected String defineToken() {\n        return TOKEN;\n    }\n\n    @Override\n    protected String defineAPIUrl() {\n        return null;\n    }\n\n    @Override\n    protected void defineAttributes() {\n    }\n\n    @Override\n    protected void definePrimaryKeys() {\n    }\n\n    @Override\n    protected DummyItem _createItem() {\n        return new DummyItem();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/IItem.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.web.toolkit.client.common.json.JsonSerializable;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\n\n/**\n * @author Séverin Moussel\n */\npublic interface IItem extends JsonSerializable {\n\n    List<String> getAPIIDOrder();\n\n    void setId(final APIID id);\n\n    APIID getId();\n\n    /**\n     * @param applyOutputModifiers\n     *        the applyOutputModifiers to set\n     */\n    void setApplyOutputModifiers(final boolean applyOutputModifiers);\n\n    /**\n     * @param applyInputModifiers\n     *        the applyInputModifiers to set\n     */\n    void setApplyInputModifiers(final boolean applyInputModifiers);\n\n    /**\n     * @param applyValidators\n     *        the applyValidators to set\n     */\n    void setApplyValidators(final boolean applyValidators);\n\n    /**\n     * @param applyValidatorMandatory\n     *        the applyValidatorMandatory to set\n     */\n    void setApplyValidatorMandatory(final boolean applyValidatorMandatory);\n\n    /**\n     * Set an attribute value.\n     * <p>\n     * The attribute is consider as non existent before the first call of this function. The JSonItemReader fills it.\n     *\n     * @param name\n     *        The name of the attribute. Must be the same as in the ItemDefinition.\n     * @param value\n     *        The value of the Item.\n     */\n    void setAttribute(final String name, final String value);\n\n    void setAttribute(final String name, final Object value);\n\n    /**\n     * Set a Date attribute value.\n     * <p>\n     * The attribute is consider as non existent before the first call of this function. The JSonItemReader fills it.\n     *\n     * @param name\n     *        The name of the attribute. Must be the same as in the ItemDefinition.\n     * @param value\n     *        The value of the Item.\n     */\n    void setAttribute(final String name, final Date value);\n\n    /**\n     * Set an attribute value.\n     * <p>\n     * The attribute is consider as non existent before the first call of this function. The JSonItemReader fills it.\n     *\n     * @param name\n     *        The name of the attribute. Must be the same as in the ItemDefinition.\n     * @param value\n     *        The value of the Item.\n     */\n    void setAttribute(final String name, final APIID value);\n\n    /**\n     * Set an attribute value.\n     * <p>\n     * The attribute is consider as non existent before the first call of this function. The JSonItemReader fills it.\n     *\n     * @param name\n     *        The name of the attribute. Must be the same as in the ItemDefinition.\n     * @param value\n     *        The value of the Item.\n     */\n    void setAttribute(final String name, final String value, final boolean applyModifiers,\n            final boolean applyValidators);\n\n    /**\n     * Set an attribute value.\n     * <p>\n     * The attribute is consider as non existent before the first call of this function. The JSonItemReader fills it.\n     *\n     * @param name\n     *        The name of the attribute. Must be the same as in the ItemDefinition.\n     * @param value\n     *        The value of the Item.\n     */\n    void setAttribute(final String name, final Object value, final boolean applyModifiers,\n            final boolean applyValidators);\n\n    /**\n     * /**\n     * Set an attribute value.\n     * <p>\n     * The attribute is consider as non existent before the first call of this function. The JSonItemReader fills it.\n     *\n     * @param name\n     *        The name of the attribute. Must be the same as in the ItemDefinition.\n     * @param value\n     *        The value of the Item.\n     */\n    void setAttribute(final String name, final APIID value, final boolean applyModifiers,\n            final boolean applyValidators);\n\n    /**\n     * Set a Date attribute value.\n     * <p>\n     * The attribute is consider as non existent before the first call of this function. The JSonItemReader fills it.\n     *\n     * @param name\n     *        The name of the attribute. Must be the same as in the ItemDefinition.\n     * @param value\n     *        The value of the Item.\n     */\n    void setAttribute(final String name, final Date value, final boolean applyModifiers, final boolean applyValidators);\n\n    /**\n     * Set a deployed version of an attribute\n     *\n     * @param attributeName\n     *        The name of the attribute to deploy\n     * @param item\n     *        The deployed version of the attribute\n     */\n    void setDeploy(final String attributeName, final IItem item);\n\n    /**\n     * Remove a deployed version of an attribute\n     *\n     * @param attributeName\n     *        The name of the attribute deploy to remove\n     */\n    void removeDeploy(final String attributeName);\n\n    /**\n     * Indicate if there are no attribute defined.\n     *\n     * @return This methods returns TRUE if there are no attributes, otherwise FALSE.\n     */\n    boolean isEmpty();\n\n    /**\n     * Indicate if the attribute exists even if its value is NULL or empty.\n     *\n     * @param name\n     *        The name of the attribute to check.\n     * @return This method returns TRUE if the attribute exists, otherwise FALSE.\n     */\n    boolean hasAttribute(final String name);\n\n    /**\n     * Get the value of an attribute\n     *\n     * @param attributeName\n     *        The name of the attribute\n     * @param applyModifiers\n     *        Indicate whether or not the output modifiers defined for this resource need to be apply.\n     * @return This function returns the value of the attribute or the defaultValue set.\n     */\n    String getAttributeValue(final String attributeName, final boolean applyModifiers);\n\n    /**\n     * Get the value of an attribute\n     *\n     * @param attributeName\n     *        The name of the attribute\n     * @return This function returns the value of the attribute or NULL if not set.\n     */\n    String getAttributeValue(final String attributeName);\n\n    /**\n     * Get the value of an attribute\n     *\n     * @param itemAttribute\n     *        The attribute\n     * @return This function returns the value of the attribute or NULL if not set.\n     */\n    String getAttributeValue(final ItemAttribute itemAttribute);\n\n    /**\n     * Get the value of an attribute\n     *\n     * @param itemAttribute\n     *        The name of the attribute\n     * @param applyModifiers\n     *        Indicate whether or not the output modifiers defined for this resource need to be apply.\n     * @return This function returns the value of the attribute or the defaultValue set.\n     */\n    String getAttributeValue(final ItemAttribute itemAttribute, final boolean applyModifiers);\n\n    APIID getAttributeValueAsAPIID(final String attributeName, final boolean applyModifiers);\n\n    APIID getAttributeValueAsAPIID(final String attributeName);\n\n    APIID getAttributeValueAsAPIID(final ItemAttribute itemAttribute, final boolean applyModifiers);\n\n    APIID getAttributeValueAsAPIID(final ItemAttribute itemAttribute);\n\n    Date getAttributeValueAsDate(final String attributeName, final boolean applyModifiers);\n\n    Date getAttributeValueAsDate(final String attributeName);\n\n    Date getAttributeValueAsDate(final ItemAttribute itemAttribute, final boolean applyModifiers);\n\n    Date getAttributeValueAsDate(final ItemAttribute itemAttribute);\n\n    Map<String, String> getAttributes();\n\n    Map<String, String> getAttributes(final boolean applyModifiers);\n\n    /**\n     * Get a deployed version of an attribute\n     *\n     * @param attributeName\n     *        The name of the attribute to deploy\n     * @return This method returns the deployed version of an attribute if it's available, otherwise NULL.\n     */\n    IItem getDeploy(final String attributeName);\n\n    ArrayList<String> getAttributeNames();\n\n    void setAttributes(final Map<String, String> attributes, final boolean applyModifiers,\n            final boolean applyValidators);\n\n    void setAttributes(final Map<String, String> attributes);\n\n    /**\n     * Get the definition of an Item\n     * <p>\n     * This function must be overridden to return the definition corresponding to the IItem type.b\n     *\n     * @return This function return an instance of ItemDefinition for the current IItem type\n     */\n    ItemDefinition<?> getItemDefinition();\n\n    @Override\n    String toString();\n\n    Map<String, IItem> getDeploys();\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/Item.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.bonitasoft.web.toolkit.client.common.json.JSonSerializer;\nimport org.bonitasoft.web.toolkit.client.common.util.StringUtil;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ModifierEngine;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ValidatorEngine;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasDualDescription;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasDualName;\nimport org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId;\nimport org.bonitasoft.web.toolkit.client.ui.utils.DateFormat;\n\n/**\n * @author Julien Mege, Séverin Moussel\n */\npublic abstract class Item implements IItem {\n\n    public Item() {\n        super();\n    }\n\n    public Item(final IItem item) {\n        super();\n        attributes.putAll(item.getAttributes());\n    }\n\n    @Override\n    public List<String> getAPIIDOrder() {\n        return getItemDefinition().getPrimaryKeys();\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // DEFAULT FILTERS SUPERVISOR AND TEAM MANAGER\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    private final Map<String, String> attributes = new HashMap<>();\n\n    private final Map<String, IItem> deploys = new HashMap<>();\n\n    // private final Map<String, Long> counters = new HashMap<String, Long>();\n\n    @Override\n    public final void setId(final APIID id) {\n        setAttribute(ItemHasUniqueId.ATTRIBUTE_ID, id);\n    }\n\n    @Override\n    public APIID getId() {\n        APIID apiid = null;\n        final ItemDefinition<?> itemDefinition = getItemDefinition();\n\n        if (this instanceof ItemHasUniqueId) {\n            apiid = getAttributeValueAsAPIID(ItemHasUniqueId.ATTRIBUTE_ID);\n        } else {\n\n            final List<String> primaryKeysValues = new ArrayList<>();\n\n            // Filling values\n            final List<String> primaryKeys = itemDefinition.getPrimaryKeys();\n            if (primaryKeys.isEmpty()) {\n                primaryKeys.add(ItemHasUniqueId.ATTRIBUTE_ID);\n            }\n\n            for (final String key : primaryKeys) {\n                primaryKeysValues.add(this.getAttributeValue(key));\n            }\n\n            apiid = APIID.makeAPIID(primaryKeysValues);\n        }\n\n        // Setting definition\n        apiid.setItemDefinition(itemDefinition);\n\n        return apiid;\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // DEFAULT BEHAVIOR\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    private static boolean applyOutputModifiersByDefault = true;\n\n    private static boolean applyInputModifiersByDefault = true;\n\n    private static boolean applyValidatorsByDefault = true;\n\n    private static boolean applyValidatorMandatoryByDefault = true;\n\n    private Boolean applyOutputModifiers = null;\n\n    private Boolean applyInputModifiers = null;\n\n    private Boolean applyValidators = null;\n\n    private Boolean applyValidatorMandatory = null;\n\n    /**\n     * @param applyOutputModifiersByDefault\n     *        the applyOutputModifiersByDefault to set\n     */\n    public static void setApplyOutputModifiersByDefault(final boolean applyOutputModifiersByDefault) {\n        Item.applyOutputModifiersByDefault = applyOutputModifiersByDefault;\n    }\n\n    /**\n     * @param applyInputModifiersByDefault\n     *        the applyInputModifiersByDefault to set\n     */\n    public static void setApplyInputModifiersByDefault(final boolean applyInputModifiersByDefault) {\n        Item.applyInputModifiersByDefault = applyInputModifiersByDefault;\n    }\n\n    /**\n     * @param applyValidatorsByDefault\n     *        the applyValidatorsByDefault to set\n     */\n    public static void setApplyValidatorsByDefault(final boolean applyValidatorsByDefault) {\n        Item.applyValidatorsByDefault = applyValidatorsByDefault;\n    }\n\n    /**\n     * @param applyValidatorMandatoryByDefault\n     *        the applyValidatorMandatoryByDefault to set\n     */\n    public static void setApplyValidatorMandatoryByDefault(final boolean applyValidatorMandatoryByDefault) {\n        Item.applyValidatorMandatoryByDefault = applyValidatorMandatoryByDefault;\n    }\n\n    /**\n     * @param applyOutputModifiers\n     *        the applyOutputModifiers to set\n     */\n    @Override\n    public final void setApplyOutputModifiers(final boolean applyOutputModifiers) {\n        this.applyOutputModifiers = applyOutputModifiers;\n    }\n\n    /**\n     * @param applyInputModifiers\n     *        the applyInputModifiers to set\n     */\n    @Override\n    public final void setApplyInputModifiers(final boolean applyInputModifiers) {\n        this.applyInputModifiers = applyInputModifiers;\n    }\n\n    /**\n     * @param applyValidators\n     *        the applyValidators to set\n     */\n    @Override\n    public final void setApplyValidators(final boolean applyValidators) {\n        this.applyValidators = applyValidators;\n    }\n\n    /**\n     * @param applyValidatorMandatory\n     *        the applyValidatorMandatory to set\n     */\n    @Override\n    public final void setApplyValidatorMandatory(final boolean applyValidatorMandatory) {\n        this.applyValidatorMandatory = applyValidatorMandatory;\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // SETTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Set an attribute value.\n     * <p>\n     * The attribute is consider as non existent before the first call of this function. The JSonItemReader fills it.\n     *\n     * @param name\n     *        The name of the attribute. Must be the same as in the ItemDefinition.\n     * @param value\n     *        The value of the Item.\n     */\n    @Override\n    public void setAttribute(final String name, final String value) {\n        setAttribute(\n                name,\n                value,\n                applyInputModifiers == null ? applyInputModifiersByDefault : applyInputModifiers,\n                applyValidators == null ? applyValidatorsByDefault : applyValidators);\n    }\n\n    @Override\n    public void setAttribute(final String name, final Object value) {\n        this.setAttribute(name, value != null ? value.toString() : null);\n    }\n\n    /**\n     * Set a Date attribute value.\n     * <p>\n     * The attribute is consider as non existent before the first call of this function. The JSonItemReader fills it.\n     *\n     * @param name\n     *        The name of the attribute. Must be the same as in the ItemDefinition.\n     * @param value\n     *        The value of the Item.\n     */\n    @Override\n    public final void setAttribute(final String name, final Date value) {\n        this.setAttribute(name, DateFormat.dateToSql(value));\n    }\n\n    /**\n     * Set an attribute value.\n     * <p>\n     * The attribute is consider as non existent before the first call of this function. The JSonItemReader fills it.\n     *\n     * @param name\n     *        The name of the attribute. Must be the same as in the ItemDefinition.\n     * @param value\n     *        The value of the Item.\n     */\n    @Override\n    public final void setAttribute(final String name, final APIID value) {\n        if (value != null) {\n            this.setAttribute(name, value.toString());\n        } else {\n            setAttribute(name, (String) null);\n        }\n    }\n\n    /**\n     * Set an attribute value.\n     * <p>\n     * The attribute is consider as non existent before the first call of this function. The JSonItemReader fills it.\n     *\n     * @param name\n     *        The name of the attribute. Must be the same as in the ItemDefinition.\n     * @param value\n     *        The value of the Item.\n     */\n    @Override\n    public final void setAttribute(final String name, final String value, final boolean applyModifiers,\n            final boolean applyValidators) {\n        final ItemAttribute attribute = getItemDefinition().getAttribute(name);\n\n        String realValue = value;\n        if (attribute != null && applyModifiers) {\n            realValue = ModifierEngine.modify(realValue, attribute.getInputModifiers());\n        }\n\n        attributes.put(name, realValue);\n        if (applyValidators) {\n            ValidatorEngine.validate(this,\n                    applyValidatorMandatory == null ? applyValidatorMandatoryByDefault : applyValidatorMandatory);\n        }\n    }\n\n    /**\n     * Set an attribute value.\n     * <p>\n     * The attribute is consider as non existent before the first call of this function. The JSonItemReader fills it.\n     *\n     * @param name\n     *        The name of the attribute. Must be the same as in the ItemDefinition.\n     * @param value\n     *        The value of the Item.\n     */\n    @Override\n    public final void setAttribute(final String name, final Object value, final boolean applyModifiers,\n            final boolean applyValidators) {\n        if (value != null) {\n            setAttribute(name, value.toString(), applyModifiers, applyValidators);\n        } else {\n            setAttribute(name, (String) null, applyModifiers, applyValidators);\n        }\n    }\n\n    /**\n     * /**\n     * Set an attribute value.\n     * <p>\n     * The attribute is consider as non existent before the first call of this function. The JSonItemReader fills it.\n     *\n     * @param name\n     *        The name of the attribute. Must be the same as in the ItemDefinition.\n     * @param value\n     *        The value of the Item.\n     */\n    @Override\n    public final void setAttribute(final String name, final APIID value, final boolean applyModifiers,\n            final boolean applyValidators) {\n        if (value != null) {\n            setAttribute(name, value.toString(), applyModifiers, applyValidators);\n        } else {\n            setAttribute(name, (String) null, applyModifiers, applyValidators);\n        }\n    }\n\n    /**\n     * Set a Date attribute value.\n     * <p>\n     * The attribute is consider as non existent before the first call of this function. The JSonItemReader fills it.\n     *\n     * @param name\n     *        The name of the attribute. Must be the same as in the ItemDefinition.\n     * @param value\n     *        The value of the Item.\n     */\n    @Override\n    public final void setAttribute(final String name, final Date value, final boolean applyModifiers,\n            final boolean applyValidators) {\n        setAttribute(name, DateFormat.dateToSql(value), applyModifiers, applyValidators);\n    }\n\n    /**\n     * Set a deployed version of an attribute\n     *\n     * @param attributeName\n     *        The name of the attribute to deploy\n     * @param item\n     *        The deployed version of the attribute\n     */\n    @Override\n    public void setDeploy(final String attributeName, final IItem item) {\n        deploys.put(attributeName, item);\n    }\n\n    /**\n     * Remove a deployed version of an attribute\n     *\n     * @param attributeName\n     *        The name of the attribute deploy to remove\n     */\n    @Override\n    public final void removeDeploy(final String attributeName) {\n        deploys.remove(attributeName);\n    }\n\n    /**\n     * Set a counter value.\n     *\n     * @param counterName\n     *        The name of the counter to set\n     * @param value\n     *        The value of the counter\n     */\n    // public final void setCounterValue(final String counterName, final Long value) {\n    // this.counters.put(counterName, value);\n    // }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // GETTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Indicate if there are no attribute defined.\n     *\n     * @return This methods returns TRUE if there are no attributes, otherwise FALSE.\n     */\n    @Override\n    public final boolean isEmpty() {\n        return attributes.isEmpty();\n    }\n\n    /**\n     * Indicate if the attribute exists even if its value is NULL or empty.\n     *\n     * @param name\n     *        The name of the attribute to check.\n     * @return This method returns TRUE if the attribute exists, otherwise FALSE.\n     */\n    @Override\n    public final boolean hasAttribute(final String name) {\n        return attributes.containsKey(name);\n    }\n\n    /**\n     * Get the value of an attribute\n     *\n     * @param attributeName\n     *        The name of the attribute\n     * @param applyModifiers\n     *        Indicate whether or not the output modifiers defined for this resource need to be apply.\n     * @return This function returns the value of the attribute or the defaultValue set.\n     */\n    @Override\n    public final String getAttributeValue(final String attributeName, final boolean applyModifiers) {\n\n        // Detect deploy called using \"thisAttributeToDeployName.deployedItemAttributeName\"\n        final String[] splittedAttribute = attributeName.split(\"\\\\.\");\n\n        // Read a deployed attribute\n        if (splittedAttribute.length == 2) {\n            final IItem deploy = getDeploy(splittedAttribute[0]);\n\n            return deploy.getAttributeValue(splittedAttribute[1]);\n\n        }\n\n        // Read an id from a deployed attribute\n        else if (deploys.containsKey(attributeName)) {\n\n            final IItem deploy = getDeploy(attributeName);\n\n            if (deploy == null) {\n                return null;\n            }\n\n            return deploy.getId().toString();\n        }\n\n        // Read a local attribute\n        else {\n\n            String realValue = attributes.get(attributeName);\n\n            if (this instanceof ItemHasDualName) {\n                if (ItemHasDualName.ATTRIBUTE_DISPLAY_NAME.equals(attributeName) && StringUtil.isBlank(realValue)) {\n                    realValue = this.getAttributeValue(ItemHasDualName.ATTRIBUTE_NAME);\n                }\n            } else if (this instanceof ItemHasDualDescription) {\n                if (ItemHasDualDescription.ATTRIBUTE_DISPLAY_DESCRIPTION.equals(attributeName)\n                        && StringUtil.isBlank(realValue)) {\n                    realValue = this.getAttributeValue(ItemHasDualDescription.ATTRIBUTE_DESCRIPTION);\n                }\n            }\n\n            return realValue;\n        }\n    }\n\n    /**\n     * Get the value of an attribute\n     *\n     * @param attributeName\n     *        The name of the attribute\n     * @return This function returns the value of the attribute or NULL if not set.\n     */\n    @Override\n    public String getAttributeValue(final String attributeName) {\n        return this.getAttributeValue(attributeName,\n                applyOutputModifiers == null ? applyOutputModifiersByDefault : applyOutputModifiers);\n    }\n\n    /**\n     * Get the value of an attribute\n     *\n     * @param itemAttribute\n     *        The attribute\n     * @return This function returns the value of the attribute or NULL if not set.\n     */\n    @Override\n    public final String getAttributeValue(final ItemAttribute itemAttribute) {\n        return this.getAttributeValue(itemAttribute.getName(),\n                applyOutputModifiers == null ? applyOutputModifiersByDefault : applyOutputModifiers);\n    }\n\n    /**\n     * Get the value of an attribute\n     *\n     * @param itemAttribute\n     *        The name of the attribute\n     * @param applyModifiers\n     *        Indicate whether or not the output modifiers defined for this resource need to be apply.\n     * @return This function returns the value of the attribute or the defaultValue set.\n     */\n    @Override\n    public final String getAttributeValue(final ItemAttribute itemAttribute, final boolean applyModifiers) {\n        return this.getAttributeValue(itemAttribute.getName(), applyModifiers);\n    }\n\n    // AS APIID\n\n    @Override\n    public final APIID getAttributeValueAsAPIID(final String attributeName, final boolean applyModifiers) {\n        return APIID.makeAPIID(getAttributeValue(attributeName, applyModifiers));\n    }\n\n    @Override\n    public final APIID getAttributeValueAsAPIID(final String attributeName) {\n        return APIID.makeAPIID(getAttributeValue(attributeName));\n    }\n\n    @Override\n    public final APIID getAttributeValueAsAPIID(final ItemAttribute itemAttribute, final boolean applyModifiers) {\n        return APIID.makeAPIID(getAttributeValue(itemAttribute, applyModifiers));\n    }\n\n    @Override\n    public final APIID getAttributeValueAsAPIID(final ItemAttribute itemAttribute) {\n        return APIID.makeAPIID(getAttributeValue(itemAttribute));\n    }\n\n    // AS Date\n\n    @Override\n    public final Date getAttributeValueAsDate(final String attributeName, final boolean applyModifiers) {\n        return DateFormat.sqlToDate(getAttributeValue(attributeName, applyModifiers));\n    }\n\n    @Override\n    public final Date getAttributeValueAsDate(final String attributeName) {\n        return DateFormat.sqlToDate(getAttributeValue(attributeName));\n    }\n\n    @Override\n    public final Date getAttributeValueAsDate(final ItemAttribute itemAttribute, final boolean applyModifiers) {\n        return DateFormat.sqlToDate(getAttributeValue(itemAttribute, applyModifiers));\n    }\n\n    @Override\n    public final Date getAttributeValueAsDate(final ItemAttribute itemAttribute) {\n        return DateFormat.sqlToDate(getAttributeValue(itemAttribute));\n    }\n\n    // AS Long\n\n    public final Long getAttributeValueAsLong(final String attributeName, final boolean applyModifiers) {\n        return Long.valueOf(getAttributeValue(attributeName, applyModifiers));\n    }\n\n    public final Long getAttributeValueAsLong(final String attributeName) {\n        return Long.valueOf(getAttributeValue(attributeName));\n    }\n\n    public final Long getAttributeValueAsLong(final ItemAttribute itemAttribute, final boolean applyModifiers) {\n        return Long.valueOf(getAttributeValue(itemAttribute, applyModifiers));\n    }\n\n    public final Long getAttributeValueAsLong(final ItemAttribute itemAttribute) {\n        return Long.valueOf(getAttributeValue(itemAttribute));\n    }\n\n    // Get all\n\n    @Override\n    public final Map<String, String> getAttributes() {\n        return this.getAttributes(applyOutputModifiers == null ? applyOutputModifiersByDefault : applyOutputModifiers);\n    }\n\n    @Override\n    public final Map<String, String> getAttributes(final boolean applyModifiers) {\n        final Map<String, String> results = new HashMap<>();\n\n        for (final String attributeName : attributes.keySet()) {\n            results.put(attributeName, this.getAttributeValue(attributeName, applyModifiers));\n        }\n\n        return results;\n    }\n\n    /**\n     * Get a deployed version of an attribute\n     *\n     * @param attributeName\n     *        The name of the attribute to deploy\n     * @return This method returns the deployed version of an attribute if it's available, otherwise NULL.\n     */\n    @Override\n    public final IItem getDeploy(final String attributeName) {\n        // TODO If not deployed, automatically call the API to deploy.\n\n        return deploys.get(attributeName);\n    }\n\n    @Override\n    public Map<String, IItem> getDeploys() {\n        return deploys;\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // ATTRIBUTES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public final ArrayList<String> getAttributeNames() {\n        return new ArrayList<>(attributes.keySet());\n    }\n\n    @Override\n    public final void setAttributes(final Map<String, String> attributes, final boolean applyModifiers,\n            final boolean applyValidators) {\n        if (attributes == null || attributes.size() == 0) {\n            return;\n        }\n\n        for (final String attributeName : attributes.keySet()) {\n            setAttribute(attributeName, attributes.get(attributeName), applyModifiers, false);\n        }\n\n        if (applyValidators) {\n            ValidatorEngine.validate(this);\n        }\n    }\n\n    @Override\n    public final void setAttributes(final Map<String, String> attributes) {\n        setAttributes(\n                attributes,\n                applyInputModifiers == null ? applyInputModifiersByDefault : applyInputModifiers,\n                applyValidators == null ? applyValidatorsByDefault : applyValidators);\n    }\n\n    /**\n     * Get the definition of an Item\n     * <p>\n     * This function must be overridden to return the definition corresponding to the Item type.b\n     *\n     * @return This function return an instance of ItemDefinition for the current Item type\n     */\n    @Override\n    abstract public ItemDefinition<?> getItemDefinition();\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // CONVERT\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public final String toString() {\n        final StringBuilder sb = new StringBuilder();\n        for (final String key : attributes.keySet()) {\n            final String rawValue = attributes.get(key);\n            final String cleanValue = this.getAttributeValue(key);\n\n            sb.append(key).append(\" : \").append(rawValue);\n\n            if (rawValue != null && !rawValue.equals(cleanValue)) {\n                sb.append(\" >> \").append(cleanValue);\n            }\n            sb.append(\"\\r\\n\");\n        }\n\n        for (final Entry<String, IItem> entry : deploys.entrySet()) {\n            sb.append(entry.getKey()).append(\" : \").append(entry.getValue());\n            sb.append(\"\\r\\n\");\n        }\n        return sb.toString();\n    }\n\n    @Override\n    public final String toJson() {\n\n        final StringBuilder json = new StringBuilder().append(\"{\");\n\n        boolean first = true;\n        for (final String attribute : getAttributeNames()) {\n            if (deploys.containsKey(attribute)) {\n                json.append(!first ? \",\" : \"\").append(JSonSerializer.quote(attribute)).append(\":\")\n                        .append(JSonSerializer.serialize(deploys.get(attribute)));\n            } else {\n                json.append(!first ? \",\" : \"\").append(JSonSerializer.quote(attribute)).append(\":\")\n                        .append(JSonSerializer.quote(this.getAttributeValue(attribute)));\n            }\n            first = false;\n        }\n\n        json.append(\"}\");\n\n        return json.toString();\n\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + (applyInputModifiers == null ? 0 : applyInputModifiers.hashCode());\n        result = prime * result + (applyOutputModifiers == null ? 0 : applyOutputModifiers.hashCode());\n        result = prime * result + (applyValidatorMandatory == null ? 0 : applyValidatorMandatory.hashCode());\n        result = prime * result + (applyValidators == null ? 0 : applyValidators.hashCode());\n        result = prime * result + (attributes == null ? 0 : attributes.hashCode());\n        result = prime * result + (deploys == null ? 0 : deploys.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        final Item other = (Item) obj;\n        if (applyInputModifiers == null) {\n            if (other.applyInputModifiers != null) {\n                return false;\n            }\n        } else if (!applyInputModifiers.equals(other.applyInputModifiers)) {\n            return false;\n        }\n        if (applyOutputModifiers == null) {\n            if (other.applyOutputModifiers != null) {\n                return false;\n            }\n        } else if (!applyOutputModifiers.equals(other.applyOutputModifiers)) {\n            return false;\n        }\n        if (applyValidatorMandatory == null) {\n            if (other.applyValidatorMandatory != null) {\n                return false;\n            }\n        } else if (!applyValidatorMandatory.equals(other.applyValidatorMandatory)) {\n            return false;\n        }\n        if (applyValidators == null) {\n            if (other.applyValidators != null) {\n                return false;\n            }\n        } else if (!applyValidators.equals(other.applyValidators)) {\n            return false;\n        }\n        if (attributes == null) {\n            if (other.attributes != null) {\n                return false;\n            }\n        } else if (!attributes.equals(other.attributes)) {\n            return false;\n        }\n        if (deploys == null) {\n            return other.deploys == null;\n        } else {\n            return deploys.equals(other.deploys);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/ItemDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\n\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ValidationException;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.Validator;\n\n/**\n * This class is the super class of all Items definitions.\n * <p>\n * To define a new IItem type, just create a class that extends ItemDefinition.<br>\n * Then, you will have to override the function beginning with <i>define....</i> and in each overridden functions, you\n * will call the corresponding setters.\n * <p>\n * There are some functions for which the override is not mandatory but you can override any define function.\n * <p>\n * <b>It's highly recommended to define attributes names using <code>public static final</code> strings</b>\n *\n * @author Séverin Moussel\n */\npublic abstract class ItemDefinition<E extends IItem> {\n\n    public ItemDefinition() {\n        setToken(defineToken());\n        defineAttributes();\n        setAPIUrl(defineAPIUrl());\n        definePrimaryKeys();\n        defineDeploys();\n    }\n\n    // ///////////////////////////////////////////////////////////////////////////////////////////////////\n    // Token\n    // ///////////////////////////////////////////////////////////////////////////////////////////////////\n\n    private String token = null;\n\n    /**\n     * This function must be override to define the token to use to access to the current view.\n     */\n    protected abstract String defineToken();\n\n    /**\n     * @param token\n     *        the token to set\n     */\n    public final void setToken(final String token) {\n        this.token = token;\n    }\n\n    /**\n     * @return the token\n     */\n    public final String getToken() {\n        return this.token;\n    }\n\n    // ///////////////////////////////////////////////////////////////////////////////////////////////////\n    // SINGULAR RESOURCE URL\n    // ///////////////////////////////////////////////////////////////////////////////////////////////////\n\n    private String APIUrl = null;\n\n    public final String getAPIUrl() {\n        return this.APIUrl;\n    }\n\n    protected final void setAPIUrl(final String url) {\n        this.APIUrl = url;\n    }\n\n    /**\n     * This function must be overridden to define the singular version of the resource URL.\n     * <p>\n     * This function must call this.setSingularResourceUrl(...)\n     * <p>\n     * <b>Example</b> :<br>\n     * <code>this.setUrl(\"/API/organization/user\");</code>\n     */\n    protected abstract String defineAPIUrl();\n\n    // ///////////////////////////////////////////////////////////////////////////////////////////////////\n    // ATTRIBUTES\n    // ///////////////////////////////////////////////////////////////////////////////////////////////////\n\n    private final LinkedHashMap<String, ItemAttribute> attributes = new LinkedHashMap<>();\n\n    private final ArrayList<String> primaryKeys = new ArrayList<>();\n\n    /**\n     * Create and save a new attribute for the current item type.\n     * <p>\n     * <b>Example :</b><br>\n     * <code>final ItemAttribute firstName = this.createAttribute(this.FIRSTNAME, ItemAttribute.TYPE.STRING);</code>\n     *\n     * @param name\n     *        The name of the attribute\n     * @param type\n     *        The type of the attribute. Must be one of ItemAttribute.TYPE.xxx\n     * @return This function returns the Attribute created to allow to add other details on it.\n     */\n    public ItemAttribute createAttribute(final String name, final ItemAttribute.TYPE type) {\n        final ItemAttribute attribute = new ItemAttribute(name, type);\n        this.attributes.put(name, attribute);\n        return attribute;\n    }\n\n    /**\n     * This function must be overridden to define the attributes of an Item.\n     * <p>\n     * Defining attributes is made by calling several <i>createAttribute()</i>\n     * <p>\n     * <b>Example :</b><br>\n     * <code>\n     * final ItemAttribute firstName = this.createAttribute(this.FIRSTNAME, ItemAttribute.TYPE.STRING);<br>\n     * firstName.setLabel(\"Firstname\");<br>\n     * firstName.setTooltip(\"Enter your firstname\");<br>\n     * firstName.setTableSortable(true);<br>\n     * firstName.setFormEditable(true);<br>\n     * <br>\n     * final ItemAttribute firstName = this.createAttribute(this.LASTNAME, ItemAttribute.TYPE.STRING);<br>\n     * firstName.setLabel(\"Lastname\");<br>\n     * firstName.setTooltip(\"Enter your lastname\");<br>\n     * firstName.setTableSortable(true);<br>\n     * firstName.setFormEditable(true);<br>\n     * ...\n     * </code>\n     */\n    protected abstract void defineAttributes();\n\n    public final ArrayList<ItemAttribute> getAttributes() {\n        return new ArrayList<>(this.attributes.values());\n    }\n\n    protected abstract void definePrimaryKeys();\n\n    protected final void setPrimaryKeys(final String... primaryKeys) {\n        this.primaryKeys.clear();\n        for (final String key : primaryKeys) {\n            this.primaryKeys.add(key);\n\n            // Create the attribute if it's missing\n            if (!this.attributes.containsKey(key)) {\n                createAttribute(key, ItemAttribute.TYPE.ITEM_ID);\n            }\n        }\n    }\n\n    public final ArrayList<String> getPrimaryKeys() {\n        return this.primaryKeys;\n    }\n\n    /**\n     * Retrieve an attribute by its name.\n     *\n     * @param name\n     *        The name of the attribute\n     * @return This function returns the attribute or null if there is no attribute with the defined name.\n     */\n    public final ItemAttribute getAttribute(final String name) {\n        return this.attributes.get(name);\n    }\n\n    public final boolean containsAttribute(final String attributeName) {\n        return this.attributes.containsKey(attributeName);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // DEPLOYS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    private final Map<String, ItemDefinition<?>> deploys = new HashMap<>();\n\n    protected void defineDeploys() {\n        // No deploys by default\n    }\n\n    /**\n     * Declare an attribute as deployable.\n     *\n     * @param attributeName\n     *        The name of the deployable attribute.\n     * @param definition\n     *        The ItemDefinition of the item contained in the deployed attribute.\n     */\n    protected final void declareDeployable(final String attributeName, final ItemDefinition<?> definition) {\n        this.deploys.put(attributeName, definition);\n    }\n\n    public final ItemDefinition<?> getDeployDefinition(final String attributeName) {\n        return this.deploys.containsKey(attributeName)\n                ? this.deploys.get(attributeName)\n                : Definitions.get(DummyItemDefinition.TOKEN);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // VALIDATORS AND MODIFIERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Get the validators in a map <attribute name, list of validators>\n     */\n    public final Map<String, List<Validator>> getValidators() {\n        final Map<String, List<Validator>> validators = new HashMap<>();\n        for (final ItemAttribute attribute : getAttributes()) {\n            validators.put(attribute.getName(), attribute.getValidators());\n        }\n        return validators;\n    }\n\n    // ///////////////////////////////////////////////////////////////////////////////////////////////////\n    // AUTOMATICALY INSTANCIATE ITEM AND ITEM DEPENDENT TOOLS\n    // ///////////////////////////////////////////////////////////////////////////////////////////////////\n\n    /**\n     * This helps Items definitions which needs an attribute value to determine their concrete implementation.\n     *\n     * @return helper for discriminating and find the concrete implementation\n     */\n    public Optional<DiscriminatedItemDefinitionHelper<E>> getDiscriminatedHelper() {\n        return Optional.empty();\n    }\n\n    /**\n     * This function create a new empty Item\n     *\n     * @return This function returns the new Item\n     */\n    abstract protected E _createItem();\n\n    public final E createItem() {\n        try {\n            return this.createItem((Map<String, String>) null);\n        } catch (final ValidationException e) {\n            // DO NOTHING : this can't fail due to validation because validation is disabled.\n            return null;\n        }\n    }\n\n    public final E createItem(final Map<String, String> attributes) throws ValidationException {\n        final Optional<E> discriminatedItem = getDiscriminatedHelper().map(h -> h.findItemCreator(attributes).get());\n        final E item = discriminatedItem.orElseGet(this::_createItem);\n\n        if (attributes != null) {\n            item.setAttributes(attributes);\n        }\n\n        return item;\n    }\n\n    public final E createItem(IItem sourceItem) {\n        return sourceItem == null ? null : createItem(sourceItem.getAttributes());\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // MAKE APIID\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public final APIID makeAPIID(final String... id) {\n        return makeAPIID(Arrays.asList(id));\n    }\n\n    public final APIID makeAPIID(final Long... ids) {\n        final APIID apiid = APIID.makeAPIID(ids);\n        apiid.setItemDefinition(this);\n        return apiid;\n    }\n\n    public final APIID makeAPIID(final List<String> ids) {\n        final APIID apiid = APIID.makeAPIID(ids);\n        apiid.setItemDefinition(this);\n        return apiid;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/ItemAttribute.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.attribute;\n\nimport java.util.List;\n\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.modifier.DefaultValueModifier;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.modifier.Modifier;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.FileIsImageValidator;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.FileIsNoScript;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.IsBooleanValidator;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.IsIntegerValidator;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.IsNumericValidator;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.MandatoryValidator;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.StringFormatColorValidator;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.StringFormatEmailValidator;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.StringFormatURLValidator;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.StringMaxLengthValidator;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.StringSingleLineValidator;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.Validator;\n\n/**\n * This class represents an attribute of an {@link Item}\n *\n * @author Séverin Moussel\n */\npublic final class ItemAttribute implements Validable, ModifiableInput {\n\n    /**\n     * The maximum length of a color string (#FDB975).\n     */\n    public static final int MAX_LENGTH_COLOR = 7;\n\n    /**\n     * The maximum length of a simple String (varchar in database).\n     */\n    public static final int MAX_LENGTH_STRING = 255;\n\n    /**\n     * The maximum length of a text string (text in database).\n     */\n    public static final int MAX_LENGTH_TEXT = 2000;\n\n    /**\n     * The maximum length of a URL\n     */\n    public static final int MAX_LENGTH_URL = 1024;\n\n    /**\n     * The type of value an attribute can get.\n     *\n     * @author Séverin Moussel\n     */\n    public enum TYPE {\n        /**\n         * A single line String\n         * <ul>\n         * <li>Validate the string is a single line</li>\n         * <li>Validate max length of {@value #MAX_LENGTH_STRING}</li>\n         * </ul>\n         */\n        STRING,\n\n        /**\n         * A multiline String\n         * <ul>\n         * <li>Validate max length of {@value #MAX_LENGTH_TEXT}</li>\n         * </ul>\n         */\n        TEXT,\n\n        /**\n         * An image path.\n         * <ul>\n         * <li>Validate the extension of the file is a valid image</li>\n         * <li>Validate max length of {@value #MAX_LENGTH_STRING}</li>\n         * </ul>\n         */\n        IMAGE,\n\n        PASSWORD, FILE, BOOLEAN, ENUM, NUMERIC, INTEGER, DATE, TIME, DATETIME, COLOR,\n\n        /**\n         * An email address.\n         * <ul>\n         * <li>Validate the email format xxx@xxx.xxx</li>\n         * <li>Validate max length of 255</li>\n         * </ul>\n         */\n        EMAIL,\n\n        /**\n         * A URL\n         * <ul>\n         * <li>Validate max length of {@value #MAX_LENGTH_URL}</li>\n         * </ul>\n         */\n        URL,\n\n        ITEM_ID\n    }\n\n    private final String name;\n\n    private final TYPE type;\n\n    private final String defaultValue = null;\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // CONSTRUCTORS + INIT\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Default constructor\n     *\n     * @param name\n     *        The name of the DataSource variable\n     * @param type\n     *        The type of the attribute datas choosen in ItemAttribute.TYPE_XXX\n     */\n    public ItemAttribute(final String name, final TYPE type) {\n        this.name = name;\n        this.type = type;\n        initType();\n    }\n\n    /**\n     * Define the default validators using the provided type\n     */\n    private void initType() {\n        switch (this.type) {\n            case PASSWORD:\n            case STRING:\n                // Because Text inputs are not multiline\n                addValidator(new StringSingleLineValidator());\n                // Because databases varchar are limited to 255\n                addValidator(new StringMaxLengthValidator(MAX_LENGTH_STRING));\n                break;\n            case IMAGE:\n                addValidator(new FileIsImageValidator());\n                addValidator(new StringMaxLengthValidator(MAX_LENGTH_STRING));\n                break;\n            case NUMERIC:\n                addValidator(new IsNumericValidator());\n                break;\n            case INTEGER:\n                addValidator(new IsIntegerValidator());\n                break;\n            case TEXT:\n                addValidator(new StringMaxLengthValidator(MAX_LENGTH_TEXT));\n                break;\n            case BOOLEAN:\n                addValidator(new IsBooleanValidator());\n                break;\n            case FILE:\n                addValidator(new FileIsNoScript());\n                addValidator(new StringMaxLengthValidator(MAX_LENGTH_STRING));\n                break;\n            case ENUM:\n                break;\n            case EMAIL:\n                addValidator(new StringFormatEmailValidator());\n                addValidator(new StringMaxLengthValidator(MAX_LENGTH_STRING));\n                break;\n            case URL:\n                addValidator(new StringFormatURLValidator());\n                addValidator(new StringMaxLengthValidator(MAX_LENGTH_URL));\n                break;\n            case COLOR:\n                addValidator(new StringFormatColorValidator());\n                addValidator(new StringMaxLengthValidator(MAX_LENGTH_COLOR));\n                break;\n            default:\n                addValidator(new StringMaxLengthValidator(MAX_LENGTH_URL));\n                break;\n        }\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // GETTERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public String getName() {\n        return this.name;\n    }\n\n    public TYPE getType() {\n        return this.type;\n    }\n\n    public String getDefaultValue() {\n        return this.defaultValue;\n    }\n\n    // // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // VALIDATORS AND MODIFIERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    private final ModifiersList inputModifiers = new ModifiersList();\n\n    private final ValidatorsList validators = new ValidatorsList();\n\n    /**\n     * @see org.bonitasoft.web.toolkit.client.data.item.attribute.ModifiersList#getModifiers()\n     */\n    @Override\n    public List<Modifier> getInputModifiers() {\n        return this.inputModifiers.getModifiers();\n    }\n\n    /**\n     * @param modifier\n     * @see org.bonitasoft.web.toolkit.client.data.item.attribute.ModifiersList#addModifier(org.bonitasoft.web.toolkit.client.data.item.attribute.modifier.Modifier)\n     */\n    @Override\n    public void addInputModifier(final Modifier modifier) {\n        this.inputModifiers.addModifier(modifier);\n    }\n\n    /**\n     * @see org.bonitasoft.web.toolkit.client.data.item.attribute.ValidatorsList#getValidators()\n     */\n    @Override\n    public List<Validator> getValidators() {\n        return this.validators.getValidators();\n    }\n\n    @Override\n    /**\n     * @param validator\n     * @see org.bonitasoft.web.toolkit.client.data.item.attribute.ValidatorsList#addValidator(org.bonitasoft.web.toolkit.client.data.item.attribute.validator.Validator)\n     */\n    public ItemAttribute addValidator(final Validator validator) {\n        validator.setAttributeName(this.name);\n\n        this.validators.addValidator(validator);\n        return this;\n    }\n\n    /**\n     * @param validators\n     * @see org.bonitasoft.web.toolkit.client.data.item.attribute.ValidatorsList#addValidators(java.util.List)\n     */\n    @Override\n    public ItemAttribute addValidators(final List<Validator> validators) {\n        this.validators.addValidators(validators);\n        return this;\n    }\n\n    /**\n     * @param validatorClassName\n     * @see org.bonitasoft.web.toolkit.client.data.item.attribute.ValidatorsList#removeValidator(java.lang.String)\n     */\n    @Override\n    public ItemAttribute removeValidator(final String validatorClassName) {\n        this.validators.removeValidator(validatorClassName);\n        return this;\n    }\n\n    /**\n     * @param validatorClassName\n     * @see org.bonitasoft.web.toolkit.client.data.item.attribute.ValidatorsList#hasValidator(java.lang.String)\n     */\n    @Override\n    public boolean hasValidator(final String validatorClassName) {\n        return this.validators.hasValidator(validatorClassName);\n    }\n\n    /**\n     * @param validatorClassName\n     * @see org.bonitasoft.web.toolkit.client.data.item.attribute.ValidatorsList#getValidator(java.lang.String)\n     */\n    @Override\n    public Validator getValidator(final String validatorClassName) {\n        return this.validators.getValidator(validatorClassName);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // MODIFIERS / VALIDATORS EASY MAPPING\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Set the default value if no value is define for this attribute.<br />\n     * This method add a DefaultValueModifier to the attribute\n     *\n     * @param value\n     */\n    public ItemAttribute setDefaultValue(final String value) {\n        addInputModifier(new DefaultValueModifier(value));\n        return this;\n    }\n\n    /**\n     * Define if this attribute is mandatory (mustn't be empty)<br />\n     * This method add a DefaultValueModifier to the attribute\n     *\n     * @param isMandatory\n     */\n    public ItemAttribute isMandatory(final boolean isMandatory) {\n        if (isMandatory) {\n            addValidator(new MandatoryValidator());\n        } else {\n            removeValidator(MandatoryValidator.class.getName());\n        }\n        return this;\n    }\n\n    /**\n     * Define if this attribute is mandatory (mustn't be empty)<br />\n     * This method add a DefaultValueModifier to the attribute\n     */\n    public ItemAttribute isMandatory() {\n        return isMandatory(true);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/ModifiableInput.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.attribute;\n\nimport java.util.List;\n\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.modifier.Modifier;\n\n/**\n * @author Séverin Moussel\n */\npublic interface ModifiableInput {\n\n    List<Modifier> getInputModifiers();\n\n    void addInputModifier(final Modifier modifier);\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/ModifierEngine.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.attribute;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.modifier.AbstractStringModifier;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.modifier.Modifier;\n\n/**\n * @author Séverin Moussel\n */\npublic class ModifierEngine {\n\n    public static void modify(final Map<String, String> values, final Map<String, List<Modifier>> modifiers) {\n        values.replaceAll((n, v) -> modify(values.get(n), modifiers.get(n)));\n    }\n\n    public static String modify(final String value, final List<Modifier> modifiers) {\n        if (modifiers == null) {\n            return value;\n        }\n\n        String result = value;\n        for (final Modifier modifier : modifiers) {\n            if (result == null) {\n                return null;\n            }\n            if (modifier instanceof AbstractStringModifier) {\n                result = ((AbstractStringModifier) modifier).clean(result);\n            }\n        }\n\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/ModifiersList.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.attribute;\n\nimport java.util.LinkedList;\nimport java.util.List;\n\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.modifier.Modifier;\nimport org.bonitasoft.web.toolkit.client.ui.utils.ListUtils;\n\n/**\n * @author Séverin Moussel\n */\npublic class ModifiersList {\n\n    private final List<Modifier> modifiers = new LinkedList<>();\n\n    public List<Modifier> getModifiers() {\n        return this.modifiers;\n    }\n\n    public ModifiersList addModifier(final Modifier modifier) {\n        ListUtils.removeFromListByClass(this.modifiers, modifier.getClass().toString(), true);\n        this.modifiers.add(modifier);\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/Validable.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.attribute;\n\nimport java.util.List;\n\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.Validator;\n\n/**\n * @author Séverin Moussel\n */\npublic interface Validable {\n\n    List<Validator> getValidators();\n\n    Validable addValidator(final Validator validator);\n\n    Validable addValidators(final List<Validator> validators);\n\n    Validable removeValidator(final String validatorClassName);\n\n    boolean hasValidator(final String validatorClassName);\n\n    Validator getValidator(final String validatorClassName);\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/ValidationError.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.attribute;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.web.toolkit.client.common.texttemplate.Arg;\nimport org.bonitasoft.web.toolkit.client.common.texttemplate.TextTemplate;\n\n/**\n * @author Séverin Moussel\n */\npublic class ValidationError {\n\n    private final String attributeName;\n\n    private final TextTemplate template;\n\n    public ValidationError(final String attributeName, final String template) {\n        this.attributeName = attributeName;\n        this.template = new TextTemplate(template);\n    }\n\n    public String getMessage() {\n        final List<Arg> args = new ArrayList<>();\n        for (final String parameterName : this.template.getExpectedParameters()) {\n            args.add(new Arg(parameterName, parameterName));\n        }\n\n        return this.template.toString(args);\n    }\n\n    @Override\n    public String toString() {\n        return this.getMessage();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/ValidationException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.attribute;\n\nimport java.util.List;\n\nimport org.bonitasoft.web.toolkit.client.common.exception.KnownException;\n\n/**\n * @author Séverin Moussel\n */\n@SuppressWarnings(\"serial\")\npublic class ValidationException extends KnownException {\n\n    private final List<ValidationError> errors;\n\n    public ValidationException(final List<ValidationError> errors) {\n        super(errorsToMessage(errors));\n        this.errors = errors;\n    }\n\n    private static String errorsToMessage(final List<ValidationError> errors) {\n        final StringBuilder sb = new StringBuilder();\n        for (final ValidationError error : errors) {\n            sb.append(error.getMessage());\n            sb.append(\"\\r\\n\");\n        }\n\n        return sb.toString();\n    }\n\n    /**\n     * @return the errors\n     */\n    public List<ValidationError> getErrors() {\n        return this.errors;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/ValidatorEngine.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.attribute;\n\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.web.toolkit.client.common.TreeIndexed;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.AbstractStringValidator;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.MandatoryValidator;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.Validator;\n\n/**\n * @author Séverin Moussel\n */\npublic class ValidatorEngine {\n\n    public static void validateAttribute(final String attributeName, final Map<String, String> values,\n            final List<Validator> validators,\n            final boolean applyMandatory)\n            throws ValidationException {\n\n        final List<ValidationError> errors = new LinkedList<>();\n\n        // Get validators\n        if (validators != null) {\n            // Check validators\n            for (final Validator validator : validators) {\n                // force attribute name as it could be different from the one set in the item definition (case of the deploys)\n                validator.setAttributeName(attributeName);\n\n                // Check mandatory validator\n                if (validator instanceof MandatoryValidator) {\n                    if (applyMandatory) {\n                        ((MandatoryValidator) validator).check(values.get(attributeName));\n                    }\n                }\n                // Check String based validator\n                else if (validator instanceof AbstractStringValidator) {\n                    ((AbstractStringValidator) validator).check(values.get(attributeName));\n                }\n                errors.addAll(validator.getErrors());\n            }\n        }\n        if (errors.size() > 0) {\n            throw new ValidationException(errors);\n        }\n    }\n\n    /**\n     * Validate an Item\n     */\n    public static void validate(final IItem item) throws ValidationException {\n        validate(item, true);\n    }\n\n    /**\n     * Validate an Item\n     */\n    public static void validate(final IItem item, final boolean applyMandatory) throws ValidationException {\n        validate(item.getAttributes(), item.getItemDefinition().getValidators(), applyMandatory);\n    }\n\n    /**\n     * Validate a Tree\n     */\n    public static void validate(final TreeIndexed<String> tree, final Map<String, List<Validator>> validators,\n            final boolean applyMandatory)\n            throws ValidationException {\n        validate(tree.getValues(), validators, applyMandatory);\n    }\n\n    /**\n     * Validate a Map\n     */\n    public static void validate(final Map<String, String> values, final Map<String, List<Validator>> validators,\n            final boolean applyMandatory)\n            throws ValidationException {\n        final List<ValidationError> errors = new LinkedList<>();\n        for (final String attributeName : values.keySet()) {\n            try {\n                validateAttribute(attributeName, values, validators.get(attributeName), applyMandatory);\n            } catch (final ValidationException e) {\n                errors.addAll(e.getErrors());\n            }\n        }\n        if (errors.size() > 0) {\n            throw new ValidationException(errors);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/ValidatorsList.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.attribute;\n\nimport java.util.LinkedList;\nimport java.util.List;\n\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.Validator;\nimport org.bonitasoft.web.toolkit.client.ui.utils.ListUtils;\n\n/**\n * @author Séverin Moussel\n */\npublic class ValidatorsList implements Validable {\n\n    private final List<Validator> validators = new LinkedList<>();\n\n    @Override\n    public List<Validator> getValidators() {\n        return this.validators;\n    }\n\n    @Override\n    public ValidatorsList addValidator(final Validator validator) {\n        ListUtils.removeFromListByClass(this.validators, validator.getClass().getName(), true);\n        this.validators.add(validator);\n        return this;\n    }\n\n    @Override\n    public ValidatorsList addValidators(final List<Validator> validators) {\n        for (final Validator validator : validators) {\n            addValidator(validator);\n        }\n        return this;\n    }\n\n    @Override\n    public ValidatorsList removeValidator(final String validatorClassName) {\n        ListUtils.removeFromListByClass(this.validators, validatorClassName);\n        return this;\n    }\n\n    @Override\n    public boolean hasValidator(final String validatorClassName) {\n        return getValidator(validatorClassName) != null;\n    }\n\n    @Override\n    public Validator getValidator(final String validatorClassName) {\n        return (Validator) ListUtils.getFromListByClass(this.validators, validatorClassName);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/modifier/AbstractStringModifier.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.attribute.modifier;\n\n/**\n * @author Séverin Moussel\n */\npublic abstract class AbstractStringModifier extends Modifier {\n\n    public String[] clean(final String[] values) {\n        for (int i = 0; i < values.length; i++) {\n            values[i] = this.clean(values[i]);\n        }\n        return values;\n    }\n\n    public abstract String clean(String value);\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/modifier/DefaultValueModifier.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.attribute.modifier;\n\n/**\n * @author Séverin Moussel\n */\npublic class DefaultValueModifier extends AbstractStringModifier {\n\n    private final String defaultValue;\n\n    public DefaultValueModifier(final String defaultValue) {\n        super();\n        this.defaultValue = defaultValue;\n    }\n\n    @Override\n    public String clean(final String value) {\n        return value != null ? value : this.defaultValue;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/modifier/Modifier.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.attribute.modifier;\n\n/**\n * @author Séverin Moussel\n */\npublic class Modifier {\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/modifier/ReplaceRegexpModifier.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.attribute.modifier;\n\n/**\n * @author Séverin Moussel\n */\npublic class ReplaceRegexpModifier extends AbstractStringModifier {\n\n    protected final String regexp;\n\n    protected final String replace;\n\n    public ReplaceRegexpModifier(final String regexp, final String replace) {\n        super();\n        this.regexp = regexp;\n        this.replace = replace;\n    }\n\n    @Override\n    public String clean(final String value) {\n        return value.replaceAll(this.regexp, this.replace);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/AbstractCollectionValidator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.attribute.validator;\n\n/**\n * @author Séverin Moussel\n */\npublic abstract class AbstractCollectionValidator extends Validator {\n\n    public final void check(final String[] attributeValue) {\n        reset();\n        _check(attributeValue);\n    }\n\n    protected abstract void _check(String[] attributeValue);\n\n    @Override\n    protected final void addError(final String error) {\n        super.addError(error);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/AbstractNumericValidator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.attribute.validator;\n\nimport org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n;\n\n/**\n * @author Séverin Moussel\n */\npublic abstract class AbstractNumericValidator extends AbstractStringValidator {\n\n    @Override\n    /**\n     * Check if the value is numeric, then delegate to abstract _check(Double)\n     */\n    protected final void _check(final String attributeValue) {\n        Double numericValue = null;\n        try {\n            numericValue = Double.valueOf(attributeValue);\n        } catch (final NumberFormatException e) {\n            addError(AbstractI18n.t_(\"%attribute% must be a numeric value\"));\n        }\n\n        if (numericValue != null) {\n            this._check(numericValue);\n        }\n\n    }\n\n    protected abstract void _check(Double attributeValue);\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/AbstractStringFormatValidator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.attribute.validator;\n\nimport java.util.regex.Pattern;\n\nimport org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n;\n\n/**\n * @author Séverin Moussel\n */\npublic abstract class AbstractStringFormatValidator extends AbstractStringValidator {\n\n    private final Pattern regexp;\n\n    private Boolean exclude;\n\n    /**\n     * Default Constructor.\n     *\n     * @param regexp\n     */\n    public AbstractStringFormatValidator(final String regexp) {\n        this(regexp, false);\n    }\n\n    /**\n     * Default Constructor.\n     *\n     * @param regexp\n     */\n    public AbstractStringFormatValidator(final String regexp, final boolean exclude) {\n        super();\n        this.regexp = Pattern.compile(regexp);\n        this.exclude = exclude;\n    }\n\n    protected void setExclude(final Boolean exclude) {\n        this.exclude = exclude;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see org.bonitasoft.console.client.toolkit.item.attribute.checker.AttributeStringChecker#check(java.lang.String)\n     */\n    @Override\n    protected void _check(final String attributeValue) {\n\n        // use `find()` instead of `matches()` because it was the implementation of the original `com.google.gwt.regexp.shared.RegExp#test()` method\n        final boolean match = regexp.matcher(attributeValue).find();\n        if (attributeValue.contains(\"HTTP Error\")) {\n            addError(AbstractI18n\n                    .t_(\"Error uploading the file. Maybe your session expired. You can try to refresh the page.\"));\n        } else if (exclude && match || !exclude && !match) {\n            addError(defineErrorMessage());\n        }\n    }\n\n    abstract protected String defineErrorMessage();\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/AbstractStringValidator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.attribute.validator;\n\n/**\n * @author Séverin Moussel\n */\npublic abstract class AbstractStringValidator extends AbstractCollectionValidator {\n\n    protected String locale = \"\";\n\n    @Override\n    protected final void _check(final String[] attributeValue) {\n        for (int i = 0; i < attributeValue.length; i++) {\n            this.check(attributeValue[i]);\n        }\n    }\n\n    /**\n     * Function to override to define the checking operation\n     *\n     * @param attributeValue\n     */\n    public final void check(final String attributeValue) {\n        reset();\n        if (attributeValue == null || attributeValue.length() == 0) {\n            // Not an error. The null value will be detected by a mandatory validator.\n            return;\n        }\n\n        this._check(attributeValue);\n    }\n\n    protected abstract void _check(String attributeValue);\n\n    public void setLocale(String locale) {\n        this.locale = locale;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/EnumValidator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.attribute.validator;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n;\n\n/**\n * @author Paul AMAR\n */\npublic class EnumValidator extends AbstractStringValidator {\n\n    private final List<String> listString;\n\n    /**\n     * Default Constructor.\n     */\n    public EnumValidator(final List<String> listString) {\n        this.listString = listString;\n    }\n\n    /**\n     * Default Constructor.\n     */\n    public EnumValidator(final String... listString) {\n        this(new ArrayList<>(Arrays.asList(listString)));\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see\n     * org.bonitasoft.web.toolkit.client.data.item.attribute.validator.AbstractStringValidator#_check(java.lang.String)\n     */\n    @Override\n    protected void _check(final String attributeValue) {\n        final StringBuilder cleanList = new StringBuilder();\n        for (final String s : listString) {\n            cleanList.append(s).append(\", \");\n            if (s.equals(attributeValue)) {\n                return;\n            }\n        }\n        addError(AbstractI18n.t_(\"%attribute% must be one of {%list%}\").replace(\"%list%\",\n                cleanList.substring(0, cleanList.length() - 2)));\n\n    }\n\n    public void addValue(final String value) {\n        listString.add(value);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/FileExtensionAllowedValidator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.attribute.validator;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n;\nimport org.bonitasoft.web.toolkit.client.common.texttemplate.Arg;\nimport org.bonitasoft.web.toolkit.client.ui.utils.ListUtils;\n\n/**\n * @author Séverin Moussel\n */\npublic class FileExtensionAllowedValidator extends AbstractStringFormatValidator {\n\n    private final List<String> extensions;\n\n    public FileExtensionAllowedValidator(final String... extensions) {\n        super(makeRegexp(extensions));\n        this.extensions = Arrays.asList(extensions);\n    }\n\n    private static String makeRegexp(final String[] extensions) {\n        final StringBuilder sb = new StringBuilder();\n        for (final String extension : extensions) {\n            sb.append(extension).append(\"|\");\n        }\n        return \"\\\\.(\" + sb.substring(0, sb.length() - 1) + \")$\";\n    }\n\n    @Override\n    protected String defineErrorMessage() {\n        return AbstractI18n.t_(\"%attribute% file format is not allowed. Only %file_formats% files are allowed.\",\n                new Arg(\"file_formats\",\n                        ListUtils.join(this.extensions, \", \", \" \" + AbstractI18n.t_(\"or\") + \" \", \"\\\".\", \"\\\"\")));\n\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/FileExtensionForbiddenValidator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.attribute.validator;\n\n/**\n * @author Paul AMAR\n */\npublic class FileExtensionForbiddenValidator extends FileExtensionAllowedValidator {\n\n    public FileExtensionForbiddenValidator(final String... extensions) {\n        super(extensions);\n        setExclude(true);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/FileIsImageOrServletPathValidator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.attribute.validator;\n\nimport org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n;\n\n/**\n * This validates that the attribute has either an image file extension or starts with a servlet path\n *\n * @author Dumitru Corini\n * @author Anthony Birembaut\n */\npublic class FileIsImageOrServletPathValidator extends AbstractStringFormatValidator {\n\n    private static final String[] IMAGE_EXTENSIONS = { \"png\", \"jpg\", \"jpeg\", \"bmp\", \"wbmp\", \"tga\", \"gif\", \"PNG\", \"JPG\",\n            \"JPEG\", \"BMP\", \"WBMP\", \"TGA\", \"GIF\" };\n\n    public FileIsImageOrServletPathValidator(final String servletPath) {\n        super(makeRegexp(IMAGE_EXTENSIONS, servletPath));\n    }\n\n    private static String makeRegexp(final String[] extensions, final String servletPath) {\n        final StringBuilder sb = new StringBuilder();\n        for (final String extension : extensions) {\n            sb.append(extension).append(\"|\");\n        }\n\n        String preparedServletPath = servletPath.replace(\".\", \"\\\\.\").replace(\"/\", \"\\\\/\");\n\n        return \"^\" + preparedServletPath + \"|\\\\.(\" + sb.substring(0, sb.length() - 1) + \")$\";\n    }\n\n    @Override\n    protected String defineErrorMessage() {\n        return AbstractI18n.t_(\"%attribute% file format not allowed or not starting with correct servlet path\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/FileIsImageValidator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.attribute.validator;\n\n/**\n * @author Séverin Moussel\n */\npublic class FileIsImageValidator extends FileExtensionAllowedValidator {\n\n    public FileIsImageValidator() {\n        super(\"png\", \"jpg\", \"jpeg\", \"bmp\", \"wbmp\", \"tga\", \"gif\", \"PNG\", \"JPG\", \"JPEG\", \"BMP\", \"WBMP\", \"TGA\", \"GIF\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/FileIsNoScript.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.attribute.validator;\n\n/**\n * @author Paul AMAR\n */\npublic class FileIsNoScript extends FileExtensionForbiddenValidator {\n\n    /**\n     * Default Constructor.\n     */\n    public FileIsNoScript() {\n        super(\"exe\", \"bat\", \"cmd\", \"sh\", \"com\", \"jsp\", \"js\", \"jar\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/IsBooleanValidator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.attribute.validator;\n\nimport org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n;\n\n/**\n * @author Séverin Moussel\n */\npublic class IsBooleanValidator extends AbstractStringValidator {\n\n    @Override\n    protected void _check(final String attributeValue) {\n\n        // Test the case of a String representing a boolean value\n        if (\"true\".equalsIgnoreCase(attributeValue) ||\n                \"false\".equalsIgnoreCase(attributeValue) ||\n                \"on\".equalsIgnoreCase(attributeValue) ||\n                \"off\".equalsIgnoreCase(attributeValue) ||\n                \"yes\".equalsIgnoreCase(attributeValue) ||\n                \"no\".equalsIgnoreCase(attributeValue)) {\n            return;\n        }\n\n        // Test the case of a Numeric representing a boolean value\n        try {\n            Double.valueOf(attributeValue);\n        } catch (final NumberFormatException e) {\n            addError(AbstractI18n.t_(\"%attribute% must be a boolean value\"));\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/IsIntegerValidator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.attribute.validator;\n\nimport org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n;\n\n/**\n * @author Séverin Moussel\n */\npublic class IsIntegerValidator extends AbstractStringValidator {\n\n    @Override\n    protected void _check(final String attributeValue) {\n        try {\n            Long.valueOf(attributeValue);\n        } catch (final NumberFormatException e) {\n            addError(AbstractI18n.t_(\"%attribute% must be an integer value\"));\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/IsNumericValidator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.attribute.validator;\n\n/**\n * @author Séverin Moussel\n */\npublic class IsNumericValidator extends AbstractNumericValidator {\n\n    @Override\n    protected void _check(final Double attributeValue) {\n        // Do nothing. The parent class check is enough\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/MandatoryValidator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.attribute.validator;\n\nimport org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n;\n\n/**\n * @author Séverin Moussel\n */\npublic class MandatoryValidator extends AbstractCollectionValidator {\n\n    private final String message = AbstractI18n.t_(\"%attribute% is mandatory\");\n\n    public MandatoryValidator() {\n        super();\n    }\n\n    @Override\n    protected final void _check(final String[] attributeValue) {\n        for (String s : attributeValue) {\n            this.check(s);\n        }\n    }\n\n    /**\n     * Function to override to define the checking operation\n     *\n     * @param attributeValue\n     */\n    public final void check(final String attributeValue) {\n        reset();\n\n        if (attributeValue == null || attributeValue.trim().length() == 0) {\n            addError(this.message);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/StringFormatColorValidator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.attribute.validator;\n\nimport org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n;\n\n/**\n * @author Paul AMAR\n */\npublic class StringFormatColorValidator extends AbstractStringFormatValidator {\n\n    public StringFormatColorValidator() {\n        super(\"#[A-Fa-f0-9]{6}\");\n    }\n\n    @Override\n    protected String defineErrorMessage() {\n        return AbstractI18n.t_(\"%attribute% is not a valid Color value\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/StringFormatEmailValidator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.attribute.validator;\n\nimport org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n;\n\n/**\n * @author Séverin Moussel\n */\npublic class StringFormatEmailValidator extends AbstractStringFormatValidator {\n\n    public StringFormatEmailValidator() {\n        // RFC 2822 with permissive modification (allow not quoted name) and allow TLD from 2 characters to 32 (32 was arbitrary chosen)\n        super(\"[a-zA-Z0-9!#$%&'*+/=?^T_`{|}~-]+(?:\\\\.[a-zA-Z0-9!#$%&'*+/=?^T_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\\\\.)+(?:[a-zA-Z]{2,32})\");\n    }\n\n    @Override\n    protected final void _check(final String attributeValue) {\n        if (attributeValue.contains(\" \")) {\n            addError(defineErrorMessage());\n        } else {\n            super._check(attributeValue);\n        }\n    }\n\n    @Override\n    protected String defineErrorMessage() {\n        return AbstractI18n.t_(\"%attribute% is not a valid email\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/StringFormatURLValidator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.attribute.validator;\n\nimport org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n;\n\n/**\n * @author Paul AMAR\n */\npublic class StringFormatURLValidator extends AbstractStringFormatValidator {\n\n    public StringFormatURLValidator() {\n        super(\"(((?:http|https|ftp|mailto):\\\\/\\\\/[a-zA-Z0-9\\\\/\\\\?=#&%~\\\\-]+(.[a-zA-Z0-9\\\\/\\\\?=#&%~\\\\-]+)+)|(www(.[a-zA-Z0-9\\\\/\\\\?=T_#&%~\\\\-\\\\)\\\\(]+){2,}))\");\n    }\n\n    @Override\n    protected String defineErrorMessage() {\n        return AbstractI18n.t_(\"%attribute% is not a valid URL\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/StringMaxLengthValidator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.attribute.validator;\n\nimport org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n;\nimport org.bonitasoft.web.toolkit.client.common.texttemplate.Arg;\n\n/**\n * @author Séverin Moussel\n */\npublic class StringMaxLengthValidator extends AbstractStringValidator {\n\n    private final Integer maxLength;\n    private final Boolean includeMax;\n\n    public StringMaxLengthValidator(final Integer maxLength) {\n        this(maxLength, true);\n    }\n\n    public StringMaxLengthValidator(final Integer maxLength, final Boolean includeMax) {\n        this.maxLength = maxLength;\n        this.includeMax = includeMax;\n    }\n\n    @Override\n    protected void _check(final String attributeValue) {\n        final int length = attributeValue.length();\n\n        // Checking for including the maxLength\n        if (includeMax) {\n            if (maxLength != null && length > maxLength) {\n                addError(\n                        AbstractI18n.t_(\"%attribute% must be less or equal than %value%\", new Arg(\"value\", maxLength)));\n            }\n        } else {\n            if (maxLength != null && length >= maxLength) {\n                addError(AbstractI18n.t_(\"%attribute% must be less than %value%\", new Arg(\"value\", maxLength)));\n            }\n        }\n\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/StringRegexpValidator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.attribute.validator;\n\nimport org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n;\n\n/**\n * @author Séverin Moussel\n */\npublic class StringRegexpValidator extends AbstractStringFormatValidator {\n\n    public StringRegexpValidator(final String regexp) {\n        super(regexp);\n    }\n\n    public StringRegexpValidator(final String regexp, final boolean exclude) {\n        super(regexp, exclude);\n    }\n\n    @Override\n    protected String defineErrorMessage() {\n        return AbstractI18n.t_(\"%attribute% is not well written\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/StringSingleLineValidator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.attribute.validator;\n\nimport org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n;\n\n/**\n * @author Séverin Moussel\n */\npublic class StringSingleLineValidator extends AbstractStringValidator {\n\n    @Override\n    protected void _check(final String attributeValue) {\n\n        if (attributeValue.indexOf('\\n') >= 0) {\n            addError(AbstractI18n.t_(\"%attribute% must be on a single line\"));\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/Validator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.attribute.validator;\n\nimport java.util.ArrayList;\n\nimport org.bonitasoft.web.toolkit.client.common.texttemplate.Arg;\nimport org.bonitasoft.web.toolkit.client.common.texttemplate.TextTemplate;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ValidationError;\n\n/**\n * @author Séverin Moussel\n */\npublic abstract class Validator {\n\n    private final ArrayList<ValidationError> errors = new ArrayList<>();\n\n    private String attributeName = null;\n\n    /**\n     * @param attributeName\n     *        the attributeName to set\n     */\n    public void setAttributeName(final String attributeName) {\n        this.attributeName = attributeName;\n    }\n\n    /**\n     * @return the attributeName\n     */\n    public String getAttributeName() {\n        return this.attributeName;\n    }\n\n    public final ArrayList<ValidationError> getErrors() {\n        return this.errors;\n    }\n\n    protected void addError(final String error) {\n        this.errors.add(\n                new ValidationError(\n                        this.attributeName,\n                        new TextTemplate(error).toString(new Arg(\"attribute\", \"%\" + this.attributeName + \"%\"))));\n    }\n\n    public final boolean hasError() {\n        return this.errors.size() > 0;\n    }\n\n    protected void reset() {\n        this.errors.clear();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/template/ItemHasCreator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.template;\n\nimport java.util.Date;\n\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\n\n/**\n * @author Séverin Moussel\n */\npublic interface ItemHasCreator {\n\n    String ATTRIBUTE_CREATION_DATE = \"creation_date\";\n\n    String ATTRIBUTE_CREATED_BY_USER_ID = \"created_by_user_id\";\n\n    void setCreationDate(String date);\n\n    void setCreationDate(Date date);\n\n    Date getCreationDate();\n\n    void setCreatedByUserId(String id);\n\n    void setCreatedByUserId(Long id);\n\n    void setCreatedByUserId(APIID id);\n\n    APIID getCreatedByUserId();\n\n    IItem getCreatedByUser();\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/template/ItemHasDualDescription.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.template;\n\n/**\n * @author Séverin Moussel\n */\npublic interface ItemHasDualDescription {\n\n    String ATTRIBUTE_DESCRIPTION = \"description\";\n\n    String ATTRIBUTE_DISPLAY_DESCRIPTION = \"displayDescription\";\n\n    void setDescription(final String description);\n\n    void setDisplayDescription(final String displayDescription);\n\n    String getDescription();\n\n    String getDisplayDescription();\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/template/ItemHasDualName.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.template;\n\n/**\n * Insert name and display_name in the allowed attributes.\n *\n * @author Julien Mege, Séverin Moussel\n */\npublic interface ItemHasDualName {\n\n    String ATTRIBUTE_NAME = \"name\";\n\n    String ATTRIBUTE_DISPLAY_NAME = \"displayName\";\n\n    void setName(final String name);\n\n    void setDisplayName(final String displayName);\n\n    String getName();\n\n    String getDisplayName();\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/template/ItemHasIcon.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.template;\n\n/**\n * @author Séverin Moussel\n */\npublic interface ItemHasIcon {\n\n    String ATTRIBUTE_ICON = \"icon\";\n\n    String getIcon();\n\n    void setIcon(String icon);\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/template/ItemHasLastUpdateDate.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.template;\n\nimport java.util.Date;\n\n/**\n * @author Julien Mege\n */\npublic interface ItemHasLastUpdateDate {\n\n    String ATTRIBUTE_LAST_UPDATE_DATE = \"last_update_date\";\n\n    void setLastUpdateDate(String date);\n\n    void setLastUpdateDate(Date date);\n\n    Date getLastUpdateDate();\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/template/ItemHasLastUpdater.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.template;\n\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\n\n/**\n * @author Séverin Moussel\n */\npublic interface ItemHasLastUpdater extends ItemHasLastUpdateDate {\n\n    String ATTRIBUTE_LAST_UPDATE_USER_ID = \"last_update_user_id\";\n\n    void setLastUpdateUserId(String id);\n\n    void setLastUpdateUserId(APIID id);\n\n    APIID getLastUpdateUserId();\n\n    IItem getLastUpdateUser();\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/template/ItemHasUniqueId.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.template;\n\nimport org.bonitasoft.web.toolkit.client.data.APIID;\n\n/**\n * @author Julien Mege\n */\npublic interface ItemHasUniqueId {\n\n    String ATTRIBUTE_ID = \"id\";\n\n    void setId(String id);\n\n    void setId(APIID id);\n\n    void setId(Long id);\n\n    APIID getId();\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/ui/utils/DateFormat.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.ui.utils;\n\nimport java.util.Date;\n\nimport org.bonitasoft.web.toolkit.client.common.CommonDateFormater;\nimport org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n;\n\n/**\n * Available formats are\n * <ul>\n * <li>long : a simple long value representing the date in milliseconds</li>\n * <li>SQL (JSON compatible) : yyyy-MM-dd HH:mm:ss.SSS</li>\n * <li>Form input : MM/dd/YY (localized format)</li>\n * <li>Display as a short date : MM/dd/YY (localized format)</li>\n * <li>Display as a full date with time : MM/dd/YYYY HH:mm (localized format)</li>\n * <li>Display as time relative to current time : \"1 hour ago\", \"in 5 minutes\", \"2 years ago\"</li>\n * </ul>\n *\n * @author Séverin Moussel\n */\n// TODO :\n// * pull out all kind of date formatter in different classes like RelativeStringDateFormatter\n// * make an interface implemented by all date formatter\n// * make a switch return a polimorph DateFormatter and just call dateFormatter.format(...)\npublic abstract class DateFormat {\n\n    public enum UNIT {\n        YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MILLISECOND\n    }\n\n    public enum FORMAT {\n\n        SQL(\"yyyy-MM-dd HH:mm:ss.SSS\"), FORM(AbstractI18n.t_(\"MM/dd/yyyy\")), DISPLAY(\n                AbstractI18n.t_(\"MM/dd/yyyy h:mm a\")), DISPLAY_SHORT(\n                        AbstractI18n.t_(\"MMMM dd, yyyy\")), LONG, DISPLAY_RELATIVE;\n\n        private final String formatString;\n\n        FORMAT() {\n            this(\"\");\n        }\n\n        FORMAT(final String formatString) {\n            this.formatString = formatString;\n        }\n\n        public String getFormatString() {\n            return this.formatString;\n        }\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // GENERIC TO DATE conversion\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public static Date formatToDate(final String date, final FORMAT format) throws IllegalArgumentException {\n        if (date == null) {\n            return null;\n        }\n\n        if (format.equals(FORMAT.LONG)) {\n            new Date(Long.parseLong(date));\n        }\n\n        return formatToDate(date, format.getFormatString());\n    }\n\n    public static Date formatToDate(final String date, final String format) throws IllegalArgumentException {\n        if (date == null) {\n            return null;\n        }\n\n        return CommonDateFormater.parse(date, format);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // GENERIC TO LONG CONVERSION\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public static Long formatToLong(final String date, final FORMAT format) throws IllegalArgumentException {\n        if (date == null || date.isEmpty()) {\n            return null;\n        }\n\n        if (format.equals(FORMAT.LONG)) {\n            return Long.parseLong(date);\n        }\n\n        return formatToLong(date, format.getFormatString());\n    }\n\n    public static Long formatToLong(final String date, final String format) throws IllegalArgumentException {\n        if (date == null) {\n            return null;\n        }\n        final Date _date = CommonDateFormater.parse(date, format);\n        return _date.getTime();\n    }\n\n    // // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // DATE OBJECT CONVERSIONS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public static String dateToFormat(final Date date, final String format) {\n        if (date == null) {\n            return null;\n        }\n        return CommonDateFormater.toString(date, format);\n    }\n\n    public static String dateToSql(final Date date) {\n        return dateToFormat(date, FORMAT.SQL.getFormatString());\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // SQL TO ???\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public static Long sqlToLong(final String date) {\n        return formatToLong(date, FORMAT.SQL);\n    }\n\n    public static Date sqlToDate(final String date) {\n        return formatToDate(date, FORMAT.SQL);\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // FORM TO ???\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/ui/utils/ListUtils.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.ui.utils;\n\nimport java.util.List;\n\n/**\n * @author Séverin Moussel\n */\npublic class ListUtils {\n\n    public static void removeFromListByClass(final List<?> list, final String className) {\n        removeFromListByClass(list, className, false);\n    }\n\n    public static void removeFromListByClass(final List<?> list, final String className, final boolean firstOnly) {\n        for (int i = 0; i < list.size(); i++) {\n            final Object o = list.get(i);\n            if (o.getClass().getName().equals(className)) {\n                list.remove(i);\n                if (firstOnly) {\n                    return;\n                }\n                i--;\n            }\n        }\n    }\n\n    public static Object getFromListByClass(final List<?> list, final String className) {\n        for (final Object o : list) {\n            if (o.getClass().getName().equals(className)) {\n                return o;\n            }\n        }\n\n        return null;\n    }\n\n    public static String join(final List<?> list, final String separator) {\n        return join(list, separator, separator, \"\", \"\");\n    }\n\n    public static String join(final List<?> list, final String separator, final String lastSeparator,\n            final String itemPrefix, final String itemSuffix) {\n        if (list == null || list.isEmpty()) {\n            return \"\";\n        }\n\n        final StringBuilder result = new StringBuilder();\n        for (int i = 0; i < list.size(); i++) {\n            final Object item = list.get(i);\n            if (item != null) {\n                if (i == list.size() - 1 && list.size() > 1) {\n                    result.append(lastSeparator);\n                } else if (i > 0) {\n                    result.append(separator);\n                }\n                if (itemPrefix != null) {\n                    result.append(itemPrefix);\n                }\n                result.append(item);\n                if (itemSuffix != null) {\n                    result.append(itemSuffix);\n                }\n            }\n        }\n\n        return result.toString();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/server/Service.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.server;\n\nimport java.io.IOException;\n\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n;\nimport org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n.LOCALE;\n\n/**\n * This class represent a service.<br />\n * It must define a public final static String TOKEN.\n *\n * @author Séverin Moussel\n */\npublic abstract class Service {\n\n    /**\n     * The ServletCall responsible of this service.\n     */\n    private ServiceServletCall caller = null;\n\n    /**\n     * Set the caller.\n     *\n     * @param caller\n     *        The ServletCall responsible of this service.\n     */\n    public void setCaller(final ServiceServletCall caller) {\n        this.caller = caller;\n    }\n\n    /**\n     * @see org.bonitasoft.web.toolkit.server.ServletCall#getHttpSession()\n     */\n    protected HttpSession getHttpSession() {\n        return caller.getHttpSession();\n    }\n\n    /**\n     * @see org.bonitasoft.web.toolkit.server.ServletCall#getResponse()\n     */\n    public HttpServletResponse getHttpResponse() {\n        return caller.getResponse();\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // ENTRY POINT\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Entry point of the service.<br />\n     * The returned object will be send as response.\n     * <ul>\n     * <li>If the object is a String, Integer, Long, ..., the response content will be the value as is.</li>\n     * <li>If the object implements JsonSerializable, the content will be returned as a json String.</li>\n     * <li>If the object is a list or a map, the response will be a json String.</li>\n     * <li>Otherwise, the response content will be the object.toString().</li>\n     * </ul>\n     *\n     * @return This method returns the output to respond.\n     * @throws IOException\n     */\n    public abstract Object run() throws IOException;\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // DELEGATIONS FOR PARAMETERS ACCESS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    /**\n     * @see org.bonitasoft.web.toolkit.server.ServletCall#getParameter(java.lang.String)\n     */\n    public String getParameter(final String name) {\n        return caller.getParameter(name);\n    }\n\n    public LOCALE getLocale() {\n        try {\n            return LOCALE.valueOf(caller.getLocale());\n        } catch (final IllegalArgumentException e) {\n            return AbstractI18n.getDefaultLocale();\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/server/ServiceException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.server;\n\nimport org.bonitasoft.web.toolkit.client.common.exception.http.JsonExceptionSerializer;\nimport org.bonitasoft.web.toolkit.client.common.exception.http.ServerException;\n\n/**\n * @author Séverin Moussel\n */\npublic class ServiceException extends ServerException {\n\n    private static final long serialVersionUID = 4014993729649254349L;\n\n    protected final String path;\n\n    public ServiceException(final String path) {\n        super();\n        this.path = path;\n    }\n\n    public ServiceException(final String path, final String message, final Throwable cause) {\n        super(message, cause);\n        this.path = path;\n    }\n\n    public ServiceException(final String path, final String message) {\n        super(message);\n        this.path = path;\n    }\n\n    public ServiceException(final String path, final Throwable cause) {\n        super(cause);\n        this.path = path;\n    }\n\n    /**\n     * Get the path of the service called\n     *\n     * @return the path\n     */\n    public String getPath() {\n        return this.path;\n    }\n\n    @Override\n    protected String defaultMessage() {\n        return \"The service \\\"\" + getPath() + \"\\\" has encountered an unknown error\";\n    }\n\n    @Override\n    protected JsonExceptionSerializer buildJson() {\n        return super.buildJson()\n                .appendAttribute(\"path\", getPath());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/server/ServiceFactory.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.server;\n\n/**\n * @author Colin PUY\n */\npublic interface ServiceFactory {\n\n    /**\n     * @param calledToolToken\n     *        The token as a path. For example if the tool url is \".../TOOLS/actors/import\", the token will be\n     *        \"actors/import\".\n     */\n    Service getService(String calledToolToken);\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/server/ServiceNotFoundException.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.server;\n\n/**\n * @author Séverin Moussel\n */\npublic class ServiceNotFoundException extends ServiceException {\n\n    private static final long serialVersionUID = -6084401627376022011L;\n\n    public ServiceNotFoundException(final String path) {\n        super(path);\n    }\n\n    @Override\n    protected String defaultMessage() {\n        return \"The service \\\"\" + getPath() + \"\\\" doesn't exist or is not declared\";\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/server/ServiceServletCall.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.server;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIMalformedUrlException;\nimport org.bonitasoft.web.toolkit.client.common.json.JSonItemReader;\nimport org.bonitasoft.web.toolkit.client.common.json.JSonSerializer;\nimport org.bonitasoft.web.toolkit.client.common.json.JsonSerializable;\n\n/**\n * This class represent a call to a service.\n *\n * @author Séverin Moussel\n */\npublic class ServiceServletCall extends ServletCall {\n\n    private String calledToolToken;\n    private final ServiceFactory serviceFactory;\n\n    public ServiceServletCall(ServiceFactory serviceFactory, final HttpServletRequest request,\n            final HttpServletResponse response) {\n        super(request, response);\n        this.serviceFactory = serviceFactory;\n    }\n\n    @Override\n    protected void parseRequest(final HttpServletRequest request, HttpServletResponse response) {\n        super.parseRequest(request, response);\n\n        // Gather all the POST parameters\n        final Map<String, String> postParams = JSonItemReader.parseMap(getInputStream());\n        for (final Map.Entry<String, String> entry : postParams.entrySet()) {\n            this.parameters.put(entry.getKey(), new String[] { entry.getValue() });\n        }\n\n        // Read TOOL tokens\n        this.calledToolToken = request.getPathInfo();\n        if (this.calledToolToken.length() == 0) {\n            throw new APIMalformedUrlException(getRequestURL(), \"Missing tool name\");\n        }\n    }\n\n    @Override\n    public final void doGet() throws IOException {\n        run();\n    }\n\n    @Override\n    public final void doPost() throws IOException {\n        run();\n    }\n\n    @Override\n    public final void doPut() throws IOException {\n        run();\n    }\n\n    @Override\n    public final void doDelete() throws IOException {\n        run();\n    }\n\n    /**\n     * Instantiate and run the service.\n     *\n     * @throws IOException\n     */\n    private void run() throws IOException {\n        final Service service = serviceFactory.getService(this.calledToolToken);\n        service.setCaller(this);\n\n        final Object response = service.run();\n\n        if (response instanceof File) {\n            output((File) response);\n        } else if (response instanceof InputStream) {\n            output((InputStream) response);\n        } else if (response instanceof JsonSerializable || response instanceof Map<?, ?>\n                || response instanceof List<?>) {\n            output(JSonSerializer.serialize(response));\n        } else if (response != null) {\n            output(response.toString());\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/server/ServletCall.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.server;\n\nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.PrintWriter;\nimport java.nio.charset.StandardCharsets;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.servlet.ServletInputStream;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpSession;\n\nimport org.apache.commons.io.IOUtils;\nimport org.bonitasoft.console.common.server.utils.LocaleUtils;\nimport org.bonitasoft.web.toolkit.client.common.exception.http.ServerException;\nimport org.bonitasoft.web.toolkit.client.common.json.JSonSerializer;\n\n/**\n * @author Séverin Moussel\n * @author Baptiste Mesta\n * @author Fabio Lombardi\n */\npublic abstract class ServletCall {\n\n    private String inputStream = null;\n\n    /**\n     * The parameters of the URL.<br />\n     * Result of the parsing of the query string : \"?a=b&c=d&...\"\n     */\n    protected final Map<String, String[]> parameters = new HashMap<>();\n\n    /**\n     * The request made to access this servletCall.\n     */\n    private final HttpServletRequest request;\n\n    /**\n     * The response to return.\n     */\n    private final HttpServletResponse response;\n\n    /**\n     * Default constructor.\n     *\n     * @param request\n     *        The request made to access this servletCall.\n     * @param response\n     *        The response to return.\n     */\n    public ServletCall(final HttpServletRequest request, final HttpServletResponse response) {\n        super();\n        this.request = request;\n        this.response = response;\n\n        parseRequest(request, response);\n    }\n\n    /**\n     * Constructor for tests\n     */\n    public ServletCall() {\n        request = null;\n        response = null;\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // PARAMETERS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Get the current call's HttpSession\n     *\n     * @return This method returns the session from the current call.\n     */\n    public HttpSession getHttpSession() {\n        return request.getSession();\n    }\n\n    /**\n     * @see javax.servlet.http.HttpServletRequest#getQueryString()\n     */\n    public String getQueryString() {\n        return request.getQueryString();\n    }\n\n    /**\n     * Reconstruct the URL the client used to make the request.\n     * The returned URL contains a protocol, server name, port\n     * number, and server path, but it does not include query\n     * string parameters.\n     *\n     * @return This method returns the reconstructed URL\n     */\n    public String getRequestURL() {\n        return request.getRequestURL().toString();\n    }\n\n    /**\n     * Read the input stream and set it in a String\n     */\n    public String getInputStream() {\n        if (inputStream == null) {\n\n            BufferedReader reader = null;\n            try {\n\n                // BS-8474 - use custom reader instead of request reader to avoid JBoss5.1 bug\n                // see https://issues.jboss.org/browse/JBAS-7817\n                final ServletInputStream stream = request.getInputStream();\n                reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8));\n\n                final StringBuilder sb = new StringBuilder();\n                String line = reader.readLine();\n                while (line != null) {\n                    sb.append(line + \"\\n\");\n                    line = reader.readLine();\n                }\n\n                inputStream = sb.toString();\n\n            } catch (final IOException e) {\n                throw new RuntimeException(\"Can't read input Stream.\", e);\n            } finally {\n                closeQuietly(reader);\n            }\n        }\n\n        return inputStream;\n    }\n\n    private void closeQuietly(final BufferedReader reader) {\n        if (reader != null) {\n            try {\n                reader.close();\n            } catch (final IOException e) {\n                // do nothing / close quietly\n            }\n        }\n    }\n\n    /**\n     * Count the number of parameters passed in the URL\n     *\n     * @return This method returns the number of parameters in the URL\n     */\n    public int countParameters() {\n        return parameters.size();\n    }\n\n    /**\n     * Get a parameter values by its name\n     *\n     * @param name\n     *        The name of the parameter (case sensitive)\n     * @return This method returns the values of a parameter as a list of String or null if the parameter isn't defined\n     */\n    public List<String> getParameterAsList(final String name) {\n        return getParameterAsList(name, null);\n    }\n\n    /**\n     * Get a parameter values by its name\n     *\n     * @param name\n     *        The name of the parameter (case sensitive)\n     * @param defaultValue\n     *        The value to return if the parameter isn't define\n     * @return This method returns the values of a parameter as a list of String\n     */\n    public List<String> getParameterAsList(final String name, final String defaultValue) {\n        if (parameters.containsKey(name)) {\n            return Arrays.asList(parameters.get(name));\n        }\n        if (defaultValue != null) {\n            final List<String> results = new ArrayList<>();\n            results.add(defaultValue);\n            return results;\n        }\n        return null;\n    }\n\n    /**\n     * Get a parameter first value by its name\n     *\n     * @param name\n     *        The name of the parameter (case sensitive)\n     * @return This method returns the first value of a parameter as a String or null if the parameter isn't define\n     */\n    public String getParameter(final String name) {\n        return getParameter(name, null);\n    }\n\n    /**\n     * Get a parameter first value by its name\n     *\n     * @param name\n     *        The name of the parameter (case sensitive)\n     * @param defaultValue\n     *        The value to return if the parameter isn't define\n     * @return This method returns the first value of a parameter as a String\n     */\n    public String getParameter(final String name, final String defaultValue) {\n        if (parameters.containsKey(name)) {\n            final String[] result = parameters.get(name);\n            if (result.length > 0) {\n                return result[0];\n            }\n        }\n        return defaultValue;\n    }\n\n    /**\n     * Get all the parameters\n     *\n     * @return This method returns all the parameters as a Map of array of String\n     */\n    public Map<String, String[]> getParameters() {\n        return parameters;\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // GENERATE RESPONSES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Write into the output header.\n     *\n     * @param name\n     *        The name of the header to write.\n     * @param value\n     *        The value of the header to write.\n     */\n    protected void head(final String name, final String value) {\n        response.addHeader(name, value);\n    }\n\n    /**\n     * Output a file\n     *\n     * @param file\n     *        The file to output\n     */\n    protected void output(final File file) {\n        try (InputStream stream = new FileInputStream(file)) {\n            output(stream);\n        } catch (final FileNotFoundException e) {\n            throw new ServerException(e);\n        } catch (IOException e) {\n            throw new ServerException(e);\n        }\n    }\n\n    /**\n     * Output a stream as a file\n     *\n     * @param stream\n     *        The stream to output\n     * @param filename\n     *        The name of the file to retrieve with the stream.\n     */\n    protected void output(final InputStream stream, final String filename) {\n        response.addHeader(\"Content-Disposition\", \"attachment; filename=\" + filename + \";\");\n        output(stream);\n    }\n\n    /**\n     * Output a stream as a file\n     *\n     * @param stream\n     *        The stream to output\n     */\n    protected void output(final InputStream stream) {\n        response.setContentType(\"application/octet-stream\");\n        try {\n            IOUtils.copy(stream, response.getOutputStream());\n        } catch (final IOException e) {\n            throw new ServerException(e);\n        }\n    }\n\n    /**\n     * Write into the output\n     *\n     * @param string\n     *        The string to output\n     */\n    protected void output(final String string) {\n        final PrintWriter outputWriter = getOutputWriter();\n        outputWriter.print(string);\n        outputWriter.flush();\n    }\n\n    /**\n     * Write into the output\n     *\n     * @param object\n     *        An object that will be transform into JSon\n     */\n    protected void output(final Object object) {\n        final PrintWriter outputWriter = getOutputWriter();\n        outputWriter.print(JSonSerializer.serialize(object));\n        outputWriter.flush();\n    }\n\n    /**\n     * The outputWriter in which to write the response String.\n     */\n    private PrintWriter outputWriter = null;\n\n    /**\n     * Prepare the output\n     *\n     * @param response\n     */\n    private PrintWriter getOutputWriter() {\n        if (outputWriter == null) {\n            response.setContentType(\"application/json;charset=UTF-8\");\n            try {\n                outputWriter = response.getWriter();\n            } catch (final IOException e) {\n                throw new RuntimeException(e);\n            }\n        }\n        return outputWriter;\n    }\n\n    /**\n     * Read elements form the request\n     * <ul>\n     * <li>API tokens</li>\n     * <li>item id (if defined)</li>\n     * <li>parameters</li>\n     * </ul>\n     *\n     * @param request\n     * @param response\n     */\n    @SuppressWarnings(\"unchecked\")\n    protected void parseRequest(final HttpServletRequest request, final HttpServletResponse response) {\n        // Create a new HashMap and copy all the elements in the new one\n        parameters.putAll(this.request.getParameterMap());\n    }\n\n    public String getLocale() {\n        return LocaleUtils.getUserLocaleAsString(request);\n    }\n\n    public HttpServletRequest getRequest() {\n        return request;\n    }\n\n    public HttpServletResponse getResponse() {\n        return response;\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // REQUEST ENTRY POINTS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Entry point for GET and SEARCH\n     *\n     * @throws IOException\n     */\n    public abstract void doGet() throws IOException;\n\n    /**\n     * Entry point for CREATE\n     *\n     * @throws IOException\n     */\n    public abstract void doPost() throws IOException;\n\n    /**\n     * Entry point for UPDATE\n     *\n     * @throws IOException\n     */\n    public abstract void doPut() throws IOException;\n\n    /**\n     * Entry point for DELETE\n     *\n     * @throws IOException\n     */\n    public abstract void doDelete() throws IOException;\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/server/servlet/ServiceServlet.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.server.servlet;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.bonitasoft.web.toolkit.server.ServiceFactory;\nimport org.bonitasoft.web.toolkit.server.ServiceServletCall;\nimport org.bonitasoft.web.toolkit.server.ServletCall;\n\n/**\n * This class is the entry point of all server calls\n *\n * @author Séverin Moussel\n */\n@SuppressWarnings(\"serial\")\npublic abstract class ServiceServlet extends ToolkitHttpServlet {\n\n    private final ServiceFactory serviceFactory;\n\n    public ServiceServlet(ServiceFactory serviceFactory) {\n        this.serviceFactory = serviceFactory;\n    }\n\n    @Override\n    protected ServletCall defineServletCall(final HttpServletRequest req, final HttpServletResponse resp) {\n        return new ServiceServletCall(serviceFactory, req, resp);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/server/servlet/ToolkitHttpServlet.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.server.servlet;\n\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.time.Instant;\nimport java.time.ZoneId;\nimport java.time.format.DateTimeFormatter;\nimport java.util.Locale;\nimport java.util.Map;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.ServletRequest;\nimport javax.servlet.ServletResponse;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.bonitasoft.console.common.server.utils.LocaleUtils;\nimport org.bonitasoft.web.rest.server.framework.json.JSonSimpleDeserializer;\nimport org.bonitasoft.web.toolkit.client.common.CommonDateFormater;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIForbiddenException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIIncorrectIdException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIItemIdMalformedException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIMethodNotAllowedException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APINotFoundException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APITooManyRequestException;\nimport org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n.LOCALE;\nimport org.bonitasoft.web.toolkit.client.common.json.JSonItemReader;\nimport org.bonitasoft.web.toolkit.client.common.json.JSonSerializer;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.bonitasoft.web.toolkit.server.ServiceException;\nimport org.bonitasoft.web.toolkit.server.ServiceNotFoundException;\nimport org.bonitasoft.web.toolkit.server.ServletCall;\nimport org.bonitasoft.web.toolkit.server.utils.ServerDateFormater;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.http.HttpHeaders;\n\n/**\n * @author Séverin Moussel\n */\npublic abstract class ToolkitHttpServlet extends HttpServlet {\n\n    private static final long serialVersionUID = -8470006030459575773L;\n\n    public static final DateTimeFormatter RFC1123_DATE_TIME_FORMATTER = DateTimeFormatter\n            .ofPattern(\"EEE, dd MMM yyyy HH:mm:ss 'GMT'\", Locale.US)\n            .withZone(ZoneId.of(\"GMT\"));\n\n    /**\n     * Console logger\n     */\n    protected static final Logger LOGGER = LoggerFactory.getLogger(ToolkitHttpServlet.class.getName());\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // CATCH ALL EXCEPTIONS\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    public ToolkitHttpServlet() {\n        super();\n        initializeToolkit();\n    }\n\n    /**\n     * Initialize\n     *\n     * @see HttpServlet#service(javax.servlet.ServletRequest, javax.servlet.ServletResponse)\n     */\n    @Override\n    protected final void service(final HttpServletRequest req, final HttpServletResponse resp)\n            throws ServletException, IOException {\n        try {\n            super.service(req, resp);\n        } catch (final Exception e) {\n            catchAllExceptions(retrieveLowestAPIException(e), req, resp);\n        }\n    }\n\n    private Throwable retrieveLowestAPIException(final Throwable e) {\n        Throwable lowest = e;\n\n        while (lowest.getCause() != null && lowest.getCause() instanceof APIException) {\n            lowest = lowest.getCause();\n        }\n\n        return lowest;\n    }\n\n    /**\n     * Output an exception in JSon.\n     *\n     * @param e\n     *        The exception to output\n     * @param resp\n     *        The response to fill\n     * @param httpStatusCode\n     *        The status code to return\n     */\n    protected final void outputException(final Throwable e, final HttpServletRequest req,\n            final HttpServletResponse resp, final int httpStatusCode) {\n\n        if (httpStatusCode >= 0) {\n            resp.setStatus(httpStatusCode);\n        }\n        resp.setContentType(\"application/json;charset=UTF-8\");\n\n        try {\n            final PrintWriter output = resp.getWriter();\n            if (e instanceof APIException apiException) {\n                setLocalization(apiException, LocaleUtils.getUserLocaleAsString(req));\n            }\n\n            output.print(e == null ? \"\" : JSonSerializer.serialize(e));\n            output.flush();\n        } catch (final Exception e2) {\n            throw new APIException(e2);\n        }\n    }\n\n    protected final void outputException(final Throwable e, final HttpServletRequest req,\n            final HttpServletResponse resp, final int httpStatusCode, Map<String, String> headers) {\n        for (var header : headers.entrySet()) {\n            resp.addHeader(header.getKey(), header.getValue());\n        }\n        outputException(e, req, resp, httpStatusCode);\n    }\n\n    /**\n     * Output an exception in JSon. Expect the status code to be already set\n     *\n     * @param e\n     *        The exception to output\n     * @param resp\n     *        The response to fill\n     */\n    protected final void outputException(final Throwable e, final HttpServletRequest req,\n            final HttpServletResponse resp) {\n\n        outputException(e, req, resp, -1);\n    }\n\n    private void setLocalization(APIException localizable, String locale) {\n        if (locale != null && !locale.isEmpty()) {\n            localizable.setLocale(LOCALE.valueOf(locale));\n        }\n    }\n\n    /**\n     * Initialize the toolkit\n     */\n    protected void initializeToolkit() {\n        Item.setApplyInputModifiersByDefault(false);\n        Item.setApplyValidatorsByDefault(false);\n        Item.setApplyOutputModifiersByDefault(false);\n        Item.setApplyValidatorMandatoryByDefault(false);\n\n        CommonDateFormater.setDateFormater(new ServerDateFormater());\n        JSonItemReader.setUnserializer(new JSonSimpleDeserializer());\n    }\n\n    /**\n     * @param req\n     *        The request called\n     * @param resp\n     *        The response to send\n     */\n    protected void catchAllExceptions(final Throwable exception, final HttpServletRequest req,\n            final HttpServletResponse resp) {\n        if (exception instanceof APIMethodNotAllowedException) {\n            if (LOGGER.isInfoEnabled()) {\n                LOGGER.info(exception.getMessage(), exception);\n            }\n            outputException(exception, req, resp, HttpServletResponse.SC_METHOD_NOT_ALLOWED);\n        } else if (exception instanceof APINotFoundException || exception instanceof ServiceNotFoundException) {\n            if (LOGGER.isInfoEnabled()) {\n                LOGGER.info(exception.getMessage(), exception);\n            }\n            outputException(exception, req, resp, HttpServletResponse.SC_NOT_FOUND);\n        } else if (exception instanceof APIItemNotFoundException) {\n            LOGGER.debug(exception.getMessage(), exception);\n            outputException(null, req, resp, HttpServletResponse.SC_NOT_FOUND);\n        } else if (exception instanceof APIForbiddenException) {\n            outputException(exception, req, resp, HttpServletResponse.SC_FORBIDDEN);\n        } else if (exception instanceof ServiceException) {\n            if (resp.getStatus() < HttpServletResponse.SC_BAD_REQUEST) {\n                // Response status is not yet set with an error code\n                outputException(exception, req, resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR);\n            } else {\n                // Response status is already set with an error code\n                outputException(exception, req, resp);\n            }\n        } else if (exception instanceof APIIncorrectIdException) {\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(exception.getMessage(), exception);\n            }\n            outputException(exception, req, resp, HttpServletResponse.SC_BAD_REQUEST);\n        } else if (exception instanceof APIItemIdMalformedException) {\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(exception.getMessage(), exception);\n            }\n            outputException(exception, req, resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR);\n        } else if (exception instanceof APITooManyRequestException ex) {\n            if (LOGGER.isErrorEnabled()) {\n                LOGGER.error(exception.getMessage(), exception);\n            }\n            var headers = Map.of(HttpHeaders.RETRY_AFTER,\n                    RFC1123_DATE_TIME_FORMATTER.format(Instant.ofEpochMilli(ex.getRetryAfter())));\n            outputException(exception, req, resp, ex.getStatusCode(), headers);\n        } else {\n            if (LOGGER.isErrorEnabled()) {\n                LOGGER.error(exception.getMessage(), exception);\n            }\n            outputException(exception, req, resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR);\n        }\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // DEFINE SERVLET\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    protected abstract ServletCall defineServletCall(final HttpServletRequest req, final HttpServletResponse resp);\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // INITIATE CALL\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    @Override\n    protected final void doGet(final HttpServletRequest req, final HttpServletResponse resp)\n            throws ServletException, IOException {\n        defineServletCall(req, resp).doGet();\n    }\n\n    @Override\n    protected final void doPost(final HttpServletRequest req, final HttpServletResponse resp)\n            throws ServletException, IOException {\n        defineServletCall(req, resp).doPost();\n    }\n\n    @Override\n    protected final void doPut(final HttpServletRequest req, final HttpServletResponse resp)\n            throws ServletException, IOException {\n        defineServletCall(req, resp).doPut();\n    }\n\n    @Override\n    protected final void doDelete(final HttpServletRequest req, final HttpServletResponse resp)\n            throws ServletException, IOException {\n        defineServletCall(req, resp).doDelete();\n    }\n\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    // LOCKING OVERRIDES\n    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    @Override\n    protected final long getLastModified(final HttpServletRequest req) {\n        return super.getLastModified(req);\n    }\n\n    @Override\n    protected final void doHead(final HttpServletRequest req, final HttpServletResponse resp)\n            throws ServletException, IOException {\n        super.doHead(req, resp);\n    }\n\n    @Override\n    protected final void doOptions(final HttpServletRequest req, final HttpServletResponse resp)\n            throws ServletException, IOException {\n        super.doOptions(req, resp);\n    }\n\n    @Override\n    protected final void doTrace(final HttpServletRequest req, final HttpServletResponse resp)\n            throws ServletException, IOException {\n        super.doTrace(req, resp);\n    }\n\n    @Override\n    public final void service(final ServletRequest req, final ServletResponse res)\n            throws ServletException, IOException {\n        super.service(req, res);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/server/utils/ServerDateFormater.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.server.utils;\n\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\n\nimport org.bonitasoft.web.toolkit.client.common.CommonDateFormater;\n\n/**\n * @author Paul AMAR\n */\n// TODO : remove all reference in rest-server to DateFormat, Modifier, Reader, etc... Then delete this class\npublic class ServerDateFormater extends CommonDateFormater {\n\n    /**\n     * Default Constructor.\n     */\n    public ServerDateFormater() {\n        // TODO Auto-generated constructor stub\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see org.bonitasoft.web.toolkit.client.common.CommonDateFormater#_parse(java.lang.String, java.lang.String)\n     */\n    @Override\n    public Date _parse(final String value, final String format) {\n        Date d = null;\n        final SimpleDateFormat formatter = new SimpleDateFormat(format);\n        try {\n            d = formatter.parse(value);\n        } catch (final ParseException e) {\n            // Exception thrown by parse method\n            e.printStackTrace();\n        }\n        return d;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see org.bonitasoft.web.toolkit.client.common.CommonDateFormater#_toString(java.util.Date, java.lang.String)\n     */\n    @Override\n    public String _toString(final Date value, final String format) {\n        final SimpleDateFormat formatter = new SimpleDateFormat(format);\n        return formatter.format(value);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/resources/VERSION",
    "content": "@projectVersion@\n@brandingVersion@\nBonitasoft © @buildYear@\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/resources/i18n/portal-js_es.po",
    "content": "msgid \"\"\nmsgstr \"\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Project-Id-Version: bonita\\n\"\n\"Plural-Forms: nplurals=2; plural=(n != 1);\\n\"\n\"X-Crowdin-Project: bonita\\n\"\n\"X-Crowdin-Project-ID: 13316\\n\"\n\"X-Crowdin-Language: es-ES\\n\"\n\"X-Crowdin-File: /dev/bonita-web/portal/portal-js.pot\\n\"\n\"X-Crowdin-File-ID: 60594\\n\"\n\"Language-Team: Spanish\\n\"\n\"Language: es_ES\\n\"\n\"PO-Revision-Date: 2024-01-01 00:00\\n\"\n\nmsgid \"1 case has been deleted\"\nmsgstr \"Se ha eliminado 1 caso\"\n\nmsgid \"A Link to your application made outside the portal, e.g. with Bonita UI Builder.\"\nmsgstr \"Un enlace a su aplicación hecha fuera del portal, por ejemplo, con Bonita UI Builder.\"\n\nmsgid \"A classic Bonita Living Application.\"\nmsgstr \"Una Bonita Living Application clásica.\"\n\nmsgid \"A page with this name already exists.\"\nmsgstr \"Ya existe una página con este nombre.\"\n\nmsgid \"A server error occurred. The operation did not complete successfully\"\nmsgstr \"Se ha producido un error en el servidor. La operación no se completó correctamente\"\n\nmsgid \"Above normal\"\nmsgstr \"Por encima de la normal\"\n\nmsgid \"Access denied. For more information, check the log file.\"\nmsgstr \"Acceso denegado. Para obtener más información, consulte el archivo de registro.\"\n\nmsgid \"Actions\"\nmsgstr \"acciones\"\n\nmsgid \"Activation state\"\nmsgstr \"Estado de activación\"\n\nmsgid \"Actor name\"\nmsgstr \"Nombre del actor\"\n\nmsgid \"Actors\"\nmsgstr \"Actores\"\n\nmsgid \"Actors must be resolved before enabling the Process.\"\nmsgstr \"Los actores deben resolverse antes de activar el proceso.\"\n\nmsgid \"Add\"\nmsgstr \"Añadir\"\n\nmsgid \"Add from custom pages\"\nmsgstr \"Agregar desde páginas personalizadas\"\n\nmsgid \"Add membership\"\nmsgstr \"Añadir una membresía\"\n\nmsgid \"Add menu\"\nmsgstr \"Agregar menú\"\n\nmsgid \"Add menu item\"\nmsgstr \"Agregar elemento de menú\"\n\nmsgid \"Add page\"\nmsgstr \"Añade página\"\n\nmsgid \"Add pages in Navigation\"\nmsgstr \"Agregar páginas en Navegación\"\n\nmsgid \"Add top-level menu\"\nmsgstr \"Agregar menú superior\"\n\nmsgid \"Adding on apply\"\nmsgstr \"Aplicar para agregar\"\n\nmsgid \"Address\"\nmsgstr \"Dirección\"\n\nmsgid \"All\"\nmsgstr \"Todos\"\n\nmsgid \"All done. Good job!\"\nmsgstr \"Todo hecho. ¡Buen trabajo!\"\n\nmsgid \"An Error occurred during process activation/deactivation.\"\nmsgstr \"Se ha producido un error durante la activación/desactivación del proceso.\"\n\nmsgid \"An Error occurred during process deletion.\"\nmsgstr \"Se ha producido un error durante la eliminación del proceso.\"\n\nmsgid \"An application is a customized environment for a specific user profile, in which users interact with business data and business processes in the most efficient way.\"\nmsgstr \"Una aplicación es un entorno personalizado para un perfil de usuario específico, en el cual los usuarios interactúan con Datos de Negocio y procesos de negocio de la manera más eficiente.\"\n\nmsgid \"An error has occurred. For more information, check the log file.\"\nmsgstr \"Ha ocurrido un error. Para más información, compruebe el archivo de registro.\"\n\nmsgid \"An error occurred during categories update\"\nmsgstr \"Se produjo un error durante la actualización de las categorías\"\n\nmsgid \"An error occurred when deploying the BDM. Consult the logs for more information.\"\nmsgstr \"Se ha producido un error al desplegar el BDM. Para más información, consulte los registros.\"\n\nmsgid \"An error occurred while submitting the form.\"\nmsgstr \"Ocurrió un error al enviar el formulario.\"\n\nmsgid \"An error occurred while submitting the form.<br/>The task may not be available anymore.\"\nmsgstr \"Error al enviar el formulario.<br/>Puede ser que la tarea ya no esta disponible.\"\n\nmsgid \"An error occurred with the requested operation.\"\nmsgstr \"Ha ocurrido un error con la operación solicitada\"\n\nmsgid \"Application Link\"\nmsgstr \"Application Link\"\n\nmsgid \"Application XML file\"\nmsgstr \"Archivo de la aplicación XML\"\n\nmsgid \"Application id not provided. Unable to retrieve the application details.\"\nmsgstr \"Id de la aplicación no proporcionado. Imposible recuperar los detalles de la aplicación.\"\n\nmsgid \"Application list\"\nmsgstr \"Lista de aplicaciones\"\n\nmsgid \"Apply\"\nmsgstr \"Aplicar\"\n\nmsgid \"Archived cases\"\nmsgstr \"Casos archivados\"\n\nmsgid \"Are you sure you want to delete it?\"\nmsgstr \"¿Seguro que deseas eliminarlo?\"\n\nmsgid \"Are you sure you want to delete this membership ?\"\nmsgstr \"¿Está seguro que desea eliminar esta membresía?\"\n\nmsgid \"Assign to me. Only I will be able to do it\"\nmsgstr \"Asignarme a mí. Sólo yo podré hacerlo\"\n\nmsgid \"Assigned on\"\nmsgstr \"Asignado el\"\n\nmsgid \"Available or My tasks\"\nmsgstr \"Disponible o en Mis tareas\"\n\nmsgid \"BDM access control file can now be installed.\"\nmsgstr \"Ahora se puede instalar el archivo de control de acceso BDM.\"\n\nmsgid \"BDM file\"\nmsgstr \"Archivo BDM\"\n\nmsgid \"BDM has not been uploaded. System was not able to upload the provided BDM file for technical reasons. You can try again later.\"\nmsgstr \"El BDM no ha sido actualizado. El sistema no pudo validar el archivo de BDM proporcionado por razones técnicas. Puede intentarlo más tarde.\"\n\nmsgid \"BDM successfully updated.\"\nmsgstr \"El BDM ha sido actualizado.\"\n\nmsgid \"Back\"\nmsgstr \"Atrás\"\n\nmsgid \"Before updating the BDM, access control file should be deleted.\"\nmsgstr \"Antes de actualizar el BDM, el archivo de control de acceso debe ser suprimido.\"\n\nmsgid \"Before you import the application descriptor, the pages and the profile must already be loaded in the Portal.\"\nmsgstr \"Antes de importar el descriptor de aplicación, las páginas y el perfil ya deben estar cargados en el Portal.\"\n\nmsgid \"Business Data Model definition\"\nmsgstr \"Definición del modelo de datos de negocio (BDM)\"\n\nmsgid \"Business card\"\nmsgstr \"Tarjeta de visita\"\n\nmsgid \"Business card has not been updated. Please retry later or contact an administrator\"\nmsgstr \"La Tarjeta de visita no ha sido actualizada. Por favor, vuelva a intentarlo más tarde o póngase en contacto con un administrador\"\n\nmsgid \"Business card successfully updated\"\nmsgstr \"Tarjeta de visita actualizada con éxito\"\n\nmsgid \"Can't import page or form to the process\"\nmsgstr \"No se puede importar una página o formulario al proceso\"\n\nmsgid \"Can't update expression content\"\nmsgstr \"No se puede actualizar el contenido de la expresión\"\n\nmsgid \"Cancel\"\nmsgstr \"Cancelar\"\n\nmsgid \"Cancelled, skipped instances\"\nmsgstr \"Instancias canceladas, omitidas\"\n\nmsgid \"Cannot upload the file\"\nmsgstr \"No se puede cargar el archivo\"\n\nmsgid \"Case\"\nmsgstr \"Caso\"\n\nmsgid \"Case ID\"\nmsgstr \"ID del Caso\"\n\nmsgid \"Case comments\"\nmsgstr \"Comentarios del Caso\"\n\nmsgid \"Case id: {{caseId}} - Process: {{processName}} ({{processVersion}})\"\nmsgstr \"Caso: {{caseId}} - Proceso: {{processName}} ({{processVersion}})\"\n\nmsgid \"Case overview\"\nmsgstr \"Resumen de caso\"\n\nmsgid \"Case start\"\nmsgstr \"Caso inicio\"\n\nmsgid \"Cases with failures\"\nmsgstr \"Casos con fallos\"\n\nmsgid \"Categories\"\nmsgstr \"Categorías\"\n\nmsgid \"Check that each entity is mapped to a profile, for either Bonita Portal or for applications you have created.\"\nmsgstr \"Verifique que cada entidad está mapeada a un perfil, ya sea para el Bonita Portal o para las aplicaciones que ha creado.\"\n\nmsgid \"City\"\nmsgstr \"Ciudad\"\n\nmsgid \"Class name\"\nmsgstr \"Nombre de la clase\"\n\nmsgid \"Click here to choose the .xml file\"\nmsgstr \"Haga clic para seleccionar el archivo .xml.\"\n\nmsgid \"Click here to choose your .zip file\"\nmsgstr \"Haga clic aquí para seleccionar su archivo .zip\"\n\nmsgid \"Click on Cancel to remain on this page and try to execute the operation again once you logged back in (e.g. in another tab).\"\nmsgstr \"Haz click en Cancelar para permanecer en esta página e intentar de nuevo la operación cuando su sesión sera de nuevo iniciada (p.ej. en otra pestaña).\"\n\nmsgid \"Click on Cancel to remain on this page and wait for the maintenance to end.\"\nmsgstr \"Haz click en Cancelar para permanecer en esta página y esperar a que finalice el mantenimiento.\"\n\nmsgid \"Click on OK to be redirected and log back in.\"\nmsgstr \"Haz click en Aceptar para volver a iniciar sesión.\"\n\nmsgid \"Click on OK to be redirected to the maintenance page.\"\nmsgstr \"Haz click en Aceptar para ser redirigido.\"\n\nmsgid \"Close\"\nmsgstr \"Cerrar\"\n\nmsgid \"Columns selection\"\nmsgstr \"Selección de columnas\"\n\nmsgid \"Comment:\"\nmsgstr \"Comentario:\"\n\nmsgid \"Comments\"\nmsgstr \"Comentarios\"\n\nmsgid \"Compilation failure. Verify that the Index.groovy class implements the PageController interface.\"\nmsgstr \"Error de compilación. Verificar que la clase Index.groovy implementa la interfaz PageController.\"\n\nmsgid \"Completed instances\"\nmsgstr \"Instancias completadas\"\n\nmsgid \"Configuration state\"\nmsgstr \"Estado de la configuración\"\n\nmsgid \"Confirm new Password\"\nmsgstr \"Confirmar nueva Contraseña\"\n\nmsgid \"Connectors\"\nmsgstr \"Conectores\"\n\nmsgid \"Connectors must be resolved before enabling the Process.\"\nmsgstr \"Los conectores deben resolverse antes de activar el proceso.\"\n\nmsgid \"Core to Bonita platform\"\nmsgstr \"Core para la plataforma Bonita\"\n\nmsgid \"Core to Bonita platform; cannot be deleted\"\nmsgstr \"Core para la plataforma Bonita, no puede borrarse\"\n\nmsgid \"Country\"\nmsgstr \"País\"\n\nmsgid \"Create a new membership\"\nmsgstr \"Crear una nueva membresía\"\n\nmsgid \"Create an application\"\nmsgstr \"Crear una aplicación\"\n\nmsgid \"Created by\"\nmsgstr \"Creado por\"\n\nmsgid \"Creation\"\nmsgstr \"Creación\"\n\nmsgid \"Creation date\"\nmsgstr \"Fecha de creación\"\n\nmsgid \"Creation on\"\nmsgstr \"Creado el\"\n\nmsgid \"Custom information\"\nmsgstr \"Información personalizada\"\n\nmsgid \"Custom information has not been updated. Please retry later or contact an administrator\"\nmsgstr \"No se ha actualizado la información personalizada. Por favor, vuelva a intentarlo más tarde o póngase en contacto con un administrador\"\n\nmsgid \"Custom information successfully updated\"\nmsgstr \"Información personalizada actualizada correctamente\"\n\nmsgid \"Delete\"\nmsgstr \"Eliminar\"\n\nmsgid \"Delete application\"\nmsgstr \"Eliminar aplicación\"\n\nmsgid \"Delete membership\"\nmsgstr \"Eliminar membresía\"\n\nmsgid \"Delete process\"\nmsgstr \"Eliminar proceso\"\n\nmsgid \"Deleting a process definition will also delete all open and archived cases of this process.\"\nmsgstr \"Eliminar una definición de proceso también eliminará todos los casos abiertos y archivados de este proceso.\"\n\nmsgid \"Description\"\nmsgstr \"Descripción\"\n\nmsgid \"Disable\"\nmsgstr \"Desactivar\"\n\nmsgid \"Display name\"\nmsgstr \"Título dinámico\"\n\nmsgid \"Done tasks\"\nmsgstr \"Tareas realizadas\"\n\nmsgid \"Drawing process...\"\nmsgstr \"Dibujando proceso...\"\n\nmsgid \"Due date\"\nmsgstr \"Fecha de vencimiento\"\n\nmsgid \"Edit\"\nmsgstr \"Editar\"\n\nmsgid \"Edit menu\"\nmsgstr \"Editar menú\"\n\nmsgid \"Edit menu item\"\nmsgstr \"Editar elemento de menú\"\n\nmsgid \"Email\"\nmsgstr \"Correo Electrónico\"\n\nmsgid \"Enable\"\nmsgstr \"Activar\"\n\nmsgid \"Enable all\"\nmsgstr \"Activar todo\"\n\nmsgid \"Enter a new category name and click \\\"Add\\\" button to create a new category.\"\nmsgstr \"Introduzca un nuevo nombre de categoría y haga clic en el botón \\\"Añadir\\\" para crear una nueva categoría.\"\n\nmsgid \"Enter the user to start the case for...\"\nmsgstr \"Introduzca el usuario para el cual iniciar el caso...\"\n\nmsgid \"Error\"\nmsgstr \"Error\"\n\nmsgid \"Error on drawing process!\"\nmsgstr \"¡Error al dibujar el proceso!\"\n\nmsgid \"Error while trying to start the case. Some required information is missing (contract not fulfilled).\"\nmsgstr \"Error al intentar iniciar un caso. Falta información obligatoria (contrato incompleto).\"\n\nmsgid \"Error!\"\nmsgstr \"¡Error!\"\n\nmsgid \"Error: value must be a boolean\"\nmsgstr \"Error: el valor debe ser booleano\"\n\nmsgid \"Error: value must be a double\"\nmsgstr \"Error: el valor debe ser un doble\"\n\nmsgid \"Error: value must be an integer\"\nmsgstr \"Error: el valor debe ser un entero\"\n\nmsgid \"Executing, completing, initializing instances\"\nmsgstr \"Instancias in curso, completadas, inicializadas\"\n\nmsgid \"Export\"\nmsgstr \"Exportar\"\n\nmsgid \"Export application descriptor\"\nmsgstr \"Exportar el descriptor de aplicación\"\n\nmsgid \"Expression content has been updated\"\nmsgstr \"Se ha actualizado el contenido de la expresión\"\n\nmsgid \"Failed instances\"\nmsgstr \"Instancias fallidas\"\n\nmsgid \"File format may be invalid. For more information, check the log file.\"\nmsgstr \"El formato de archivo puede ser inválido. Para más información, compruebe el archivo de log.\"\n\nmsgid \"Filename too long. The zip filename must be no longer than 50 characters\"\nmsgstr \"Nombre de archivo demasiado largo. El nombre del archivo zip debe ser no más de 50 caracteres\"\n\nmsgid \"Filters\"\nmsgstr \"Filtros\"\n\nmsgid \"First name\"\nmsgstr \"Nombre\"\n\nmsgid \"Form\"\nmsgstr \"Formulario\"\n\nmsgid \"Form submitted.<br/>The next task in the list is now selected.\"\nmsgstr \"Formulario enviado. <br/> La siguiente tarea en la lista está ahora seleccionada.\"\n\nmsgid \"General\"\nmsgstr \"General\"\n\nmsgid \"General information\"\nmsgstr \"Información General\"\n\nmsgid \"General information has not been updated. Please retry later or contact an administrator\"\nmsgstr \"La Información general no ha sido actualizada. Por favor, vuelva a intentarlo más tarde o póngase en contacto con un administrador\"\n\nmsgid \"General information successfully updated\"\nmsgstr \"Información general actualizada correctamente\"\n\nmsgid \"Groups\"\nmsgstr \"Grupos\"\n\nmsgid \"Healthy cases\"\nmsgstr \"Casos sin fallos\"\n\nmsgid \"Hide Menu\"\nmsgstr \"Ocultar menú\"\n\nmsgid \"Hide menu\"\nmsgstr \"Ocultar menú\"\n\nmsgid \"Hide panel\"\nmsgstr \"Ocultar panel\"\n\nmsgid \"Highest\"\nmsgstr \"Mayor\"\n\nmsgid \"However, due to the use of Hibernate for data persistence, some changes on the data may not be well handled. These changes may lead to a corrupted database and CANNOT BE REVERTED.\"\nmsgstr \"Sin embargo, debido al uso de Hibernate para la persistencia de datos, es posible que algunos cambios en los datos no estén bien manejados. Estos cambios pueden llevar a una base de datos corrupta y NO SE PUEDE REVERTIR.\"\n\nmsgid \"I have read and understood the previous warning message. I have prepared a database backup so I can rollback this operation.\"\nmsgstr \"He leído y entendido el mensaje de advertencia anterior. He preparado una copia de seguridad de la base de datos para poder deshacer esta operación.\"\n\nmsgid \"IN ANY CASE, you MUST CREATE A BACKUP of the current BDM database before undergoing a BDM update.\"\nmsgstr \"EN CUALQUIER CASO, DEBES CREAR UN BACKUP de la base de datos BDM actual antes de realizar una actualización BDM.\"\n\nmsgid \"If your changes do not fall into those change types, you can go ahead and proceed with the BDM update now.\"\nmsgstr \"Si sus cambios no están dentro de este tipo de cambios puede seguir adelante y proceder ahora con la actualización de BDM.\"\n\nmsgid \"Implementation version\"\nmsgstr \"Versión de implementación\"\n\nmsgid \"Import\"\nmsgstr \"Importar\"\n\nmsgid \"Import an application\"\nmsgstr \"Importar una aplicación\"\n\nmsgid \"Import an application descriptor\"\nmsgstr \"Importar un descriptor de aplicación\"\n\nmsgid \"In a production environment, this should be used with caution.\"\nmsgstr \"En un entorno de producción, esto debería ser utilizado con precaución.\"\n\nmsgid \"In such cases, you must CANCEL THIS UPDATE and let the database administrator carefully script the changes and apply them to the database. Only then will you be able to update the BDM from this page.\"\nmsgstr \"En tales casos, debe CANCELAR ESTA ACTUALIZACIÓN y permitir al administrador de bases de datos escribir cuidadosamente los cambios y aplicarlos a la base de datos. Sólo entonces podrá actualizar el BDM desde esta página.\"\n\nmsgid \"In task name column\"\nmsgstr \"En la columna Nombre de tarea\"\n\nmsgid \"In the menu <i>Organization > Manage</i>, go to the <i>Users</i> page of the wizard\"\nmsgstr \"En el menú <i>Organización > Administrar</i>, ir a la página <i>Usuarios</i> del asistente\"\n\nmsgid \"Install\"\nmsgstr \"Instalar\"\n\nmsgid \"Install Business Data Model\"\nmsgstr \"Instalar el modelo de datos de negocio\"\n\nmsgid \"Install a BDM file\"\nmsgstr \"Instalar un archivo BDM\"\n\nmsgid \"Installed\"\nmsgstr \"Instalado\"\n\nmsgid \"Installed by\"\nmsgstr \"Instalado por\"\n\nmsgid \"Installed on\"\nmsgstr \"Instalado el\"\n\nmsgid \"Installing\"\nmsgstr \"Instalando…\"\n\nmsgid \"Installing Business Data Model\"\nmsgstr \"Instalando el modelo de datos de negocio\"\n\nmsgid \"Installing or updating the BDM implies to be logged in as Technical User, and to activate the maintenance mode.\"\nmsgstr \"Instalar o actualizar la BDM requiere una conexión como Usuario Técnico y haber activado el modo de mantenimiento.\"\n\nmsgid \"Items per page\"\nmsgstr \"Elementos por página\"\n\nmsgid \"Job title\"\nmsgstr \"Profesión\"\n\nmsgid \"Last login\"\nmsgstr \"Última sesión\"\n\nmsgid \"Last name\"\nmsgstr \"Apellidos\"\n\nmsgid \"Last update\"\nmsgstr \"Última actualización\"\n\nmsgid \"Layout\"\nmsgstr \"Layout\"\n\nmsgid \"Legacy Application\"\nmsgstr \"Legacy Application\"\n\nmsgid \"Loading...\"\nmsgstr \"Cargando…\"\n\nmsgid \"Logo\"\nmsgstr \"Logo\"\n\nmsgid \"Look & Feel\"\nmsgstr \"Apariencia\"\n\nmsgid \"Lowest\"\nmsgstr \"Menor\"\n\nmsgid \"MM/dd/yyyy h:mm a\"\nmsgstr \"dd/MM/yyyy HH:mm\"\n\nmsgid \"MMM DD LT\"\nmsgstr \"DD MMM LT\"\n\nmsgid \"Make sure all artifacts enabled on the platform are compatible with the new BDM.\"\nmsgstr \"Asegúrese de que todos los artefactos instalados en la plataforma son compatibles con el nuevo BDM.\"\n\nmsgid \"Make sure to deactivate the maintenance mode.\"\nmsgstr \"Asegúrese de desactivar el modo de mantenimiento.\"\n\nmsgid \"Make this task available to other team members again\"\nmsgstr \"Liberar - hacer esta tarea disponible de nuevo para los otros miembros del equipo\"\n\nmsgid \"Make this task personal: only I will be able to do it\"\nmsgstr \"Hacer esta tarea personal: sólo yo podré realizarla\"\n\nmsgid \"Manage Categories\"\nmsgstr \"Gestionar categorías\"\n\nmsgid \"Manager\"\nmsgstr \"Responsable\"\n\nmsgid \"Mapped categories\"\nmsgstr \"Categorías asociadas\"\n\nmsgid \"Maximum size 255 characters\"\nmsgstr \"Máximo 250 caracteres\"\n\nmsgid \"Membership has not been added. Please retry later or contact an administrator\"\nmsgstr \"La Membresía no ha sido añadida. Por favor, vuelva a intentarlo más tarde o póngase en contacto con un administrador\"\n\nmsgid \"Membership has not been removed. Please retry later or contact an administrator\"\nmsgstr \"La Membresía no ha sido eliminada. Por favor, vuelva a intentarlo más tarde o póngase en contacto con un administrador\"\n\nmsgid \"Membership successfully added\"\nmsgstr \"Membresía agregada con éxito\"\n\nmsgid \"Membership successfully removed\"\nmsgstr \"La membresía fue eliminada con éxito\"\n\nmsgid \"Memberships\"\nmsgstr \"Membresías\"\n\nmsgid \"Memberships mapped to {}\"\nmsgstr \"Membresias asignadas a {}\"\n\nmsgid \"Mobile\"\nmsgstr \"Móvil\"\n\nmsgid \"Modifying or redeploying the Business Data Model may impact existing data retention rules. After redeployment, review your configuration in the Data Retention (GDPR) section.\"\nmsgstr \"\"\n\nmsgid \"Monitoring status\"\nmsgstr \"Monitorización\"\n\nmsgid \"Multi-page menu\"\nmsgstr \"Menú multi-página\"\n\nmsgid \"My tasks\"\nmsgstr \"Mis tareas\"\n\nmsgid \"Name\"\nmsgstr \"Nombre\"\n\nmsgid \"Navigation\"\nmsgstr \"Navegación\"\n\nmsgid \"New\"\nmsgstr \"Nuevo\"\n\nmsgid \"New Password\"\nmsgstr \"Nueva contraseña\"\n\nmsgid \"New comment\"\nmsgstr \"Nuevo comentario\"\n\nmsgid \"No application available.\"\nmsgstr \"Ninguna aplicación disponible.\"\n\nmsgid \"No application found with id:\"\nmsgstr \"No se encontró ninguna aplicación con id:\"\n\nmsgid \"No comment on this case yet. To add one, use the input field at the bottom of this panel\"\nmsgstr \"Ningún comentario sobre este caso todavía. Para agregar uno, utilice el campo de entrada en la parte inferior de este panel\"\n\nmsgid \"No custom information defined.\"\nmsgstr \"No se ha definido información personalizada.\"\n\nmsgid \"No data\"\nmsgstr \"No hay datos\"\n\nmsgid \"No description.\"\nmsgstr \"No hay descripción.\"\n\nmsgid \"No done task yet.\"\nmsgstr \"Sin tareas realizadas.\"\n\nmsgid \"No form is needed. You can enter a comment and confirm.\"\nmsgstr \"No se necesita ningún formulario. Puede introducir un comentario y confirmar.\"\n\nmsgid \"No mapping\"\nmsgstr \"Ningún mapeo\"\n\nmsgid \"No process found with id:\"\nmsgstr \"No se encontró ningún proceso con id:\"\n\nmsgid \"No profile\"\nmsgstr \"Sin perfil\"\n\nmsgid \"No profile mapped to this application\"\nmsgstr \"Sin perfil asignado a esta aplicación\"\n\nmsgid \"No updates have been made on categories. For more details, check logs.\"\nmsgstr \"Ninguna actualización ha sido efectuada sobre las categorías. Para más información, consulte los registros (logs).\"\n\nmsgid \"No user found with id:\"\nmsgstr \"Ningún usuario encontrado con id:\"\n\nmsgid \"Normal\"\nmsgstr \"Normal\"\n\nmsgid \"Not installed\"\nmsgstr \"No instalado\"\n\nmsgid \"OK\"\nmsgstr \"Aceptar\"\n\nmsgid \"One or more tasks are not available anymore. The list is now up to date.\"\nmsgstr \"Una o más tareas ya no están disponibles. La lista ahora está actualizada.\"\n\nmsgid \"One-page menu\"\nmsgstr \"Menú de una página\"\n\nmsgid \"Only I can do this task\"\nmsgstr \"Sólo yo puedo hacer esta tarea\"\n\nmsgid \"Only available when the platform is under maintenance. Go to the \\\"Platform maintenance\\\" page to activate the maintenance mode.\"\nmsgstr \"Únicamente disponible cuando la plataforma está en mantenimiento. Vaya a la página \\\"Mantenimiento de la plataforma\\\" para activar el modo de mantenimiento.\"\n\nmsgid \"Only one Business Data Model (BDM) can be active at a time.\"\nmsgstr \"Sólo un Modelo de Datos de Negocio (BDM) puede estar activo al mismo tiempo.\"\n\nmsgid \"Open cases\"\nmsgstr \"Casos abiertos\"\n\nmsgid \"Open in a popup\"\nmsgstr \"Abrir en ventana emergente\"\n\nmsgid \"Open the task to perform it\"\nmsgstr \"Abra la tarea para realizarla\"\n\nmsgid \"Overview\"\nmsgstr \"Vista global\"\n\nmsgid \"Page\"\nmsgstr \"Página\"\n\nmsgid \"Pages\"\nmsgstr \"Páginas\"\n\nmsgid \"Parameters\"\nmsgstr \"Parámetros\"\n\nmsgid \"Parameters must be resolved before enabling the Process.\"\nmsgstr \"Los parámetros deben resolverse antes de activar el proceso.\"\n\nmsgid \"Password\"\nmsgstr \"Contraseña\"\n\nmsgid \"Password has not been updated. Please retry later or contact an administrator\"\nmsgstr \"La Contraseña no ha sido actualizada. Por favor, vuelva a intentarlo más tarde o póngase en contacto con un administrador\"\n\nmsgid \"Password successfully updated\"\nmsgstr \"Contraseña modificada correctamente\"\n\nmsgid \"Passwords don't match\"\nmsgstr \"Las contraseñas no coinciden\"\n\nmsgid \"Personal information\"\nmsgstr \"Información personal\"\n\nmsgid \"Personal information has not been updated. Please retry later or contact an administrator\"\nmsgstr \"La Información personal no ha sido actualizada. Por favor, vuelva a intentarlo más tarde o póngase en contacto con un administrador\"\n\nmsgid \"Personal information successfully updated\"\nmsgstr \"Información personal actualizada correctamente\"\n\nmsgid \"Phone\"\nmsgstr \"Teléfono\"\n\nmsgid \"Please refresh the page.\"\nmsgstr \"Por favor, actualiza la página.\"\n\nmsgid \"Priority\"\nmsgstr \"Prioridad\"\n\nmsgid \"Process\"\nmsgstr \"Proceso\"\n\nmsgid \"Process id\"\nmsgstr \"Id de proceso\"\n\nmsgid \"Process id not provided. Unable to retrieve the process details.\"\nmsgstr \"Id de proceso no proporcionado. Imposible recuperar los detalles del proceso.\"\n\nmsgid \"Process name\"\nmsgstr \"Nombre de Proceso\"\n\nmsgid \"Process:\"\nmsgstr \"Proceso:\"\n\nmsgid \"Profile\"\nmsgstr \"Perfil\"\n\nmsgid \"Profiles\"\nmsgstr \"Perfiles\"\n\nmsgid \"Profiles / Memberships\"\nmsgstr \"Perfiles / Membresías\"\n\nmsgid \"Ready, waiting instances\"\nmsgstr \"Instancias listas, en espera\"\n\nmsgid \"Refresh data\"\nmsgstr \"Actualizar datos\"\n\nmsgid \"Release\"\nmsgstr \"Liberar\"\n\nmsgid \"Remove\"\nmsgstr \"Suprimir\"\n\nmsgid \"Remove all\"\nmsgstr \"Eliminar todo\"\n\nmsgid \"Removing on apply\"\nmsgstr \"Aplicar para eliminar\"\n\nmsgid \"Reset\"\nmsgstr \"Reiniciar\"\n\nmsgid \"Reset to default\"\nmsgstr \"Restablecer al valor predeterminado\"\n\nmsgid \"Roles\"\nmsgstr \"Roles\"\n\nmsgid \"Save\"\nmsgstr \"Guardar\"\n\nmsgid \"Search...\"\nmsgstr \"Búsqueda...\"\n\nmsgid \"Select a group...\"\nmsgstr \"Seleccionar un grupo...\"\n\nmsgid \"Select a role...\"\nmsgstr \"Seleccionar un rol...\"\n\nmsgid \"Select all\"\nmsgstr \"Seleccionar todo\"\n\nmsgid \"Select groups...\"\nmsgstr \"Seleccionar los grupos...\"\n\nmsgid \"Select none\"\nmsgstr \"No seleccionar nada\"\n\nmsgid \"Select roles...\"\nmsgstr \"Seleccionar roles...\"\n\nmsgid \"Select the <i>User information management</i> tab to add <i>Custom information</i> on the right\"\nmsgstr \"Seleccione la pestaña <i>Gestión de la información de usuario</i> para agregar <i>Información personalizada</i> a la derecha\"\n\nmsgid \"Select the entities (users, groups, roles, memberships) to map to the actors. These entities will do the human tasks in the process.\"\nmsgstr \"Seleccione las entidades (usuarios, grupos, roles, membresías) para asignar a los actores. Estas entidades harán las tareas humanas en el proceso.\"\n\nmsgid \"Select the page\"\nmsgstr \"Seleccione la página\"\n\nmsgid \"Select users...\"\nmsgstr \"Seleccionar usuarios...\"\n\nmsgid \"Server is under maintenance.\"\nmsgstr \"El servidor está en mantenimiento.\"\n\nmsgid \"Set as Home page\"\nmsgstr \"Establecer como página de inicio\"\n\nmsgid \"Show Menu\"\nmsgstr \"Mostrar el menú\"\n\nmsgid \"Show all the tasks I can do: both personal tasks and tasks available to other team members\"\nmsgstr \"Mostrar todas las tareas que puedo hacer: tanto personales como disponibles para otros miembros del equipo\"\n\nmsgid \"Show menu\"\nmsgstr \"Mostrar menú\"\n\nmsgid \"Show my personal tasks: the ones I took and the ones assigned to me, automatically or by a process manager\"\nmsgstr \"Mostrar mis tareas personales: las tomadas y las asignadas a mí, automáticamente o por un gestor de procesos\"\n\nmsgid \"Show panel\"\nmsgstr \"Mostrar panel\"\n\nmsgid \"Show the tasks that I have done\"\nmsgstr \"Mostrar las tareas que he realizado\"\n\nmsgid \"Something went wrong during the deletion. You might want to cancel and try again.\"\nmsgstr \"Algo salió mal durante la eliminación. Es posible que quiera cancelar y volver a intentarlo.\"\n\nmsgid \"Something went wrong during the modification. You might want to cancel and try again.\"\nmsgstr \"Algo salió mal durante la modificación. Es posible que quieras cancelar e intentarlo de nuevo.\"\n\nmsgid \"Something went wrong. You might want to cancel and try again.\"\nmsgstr \"Algo salió mal. Puede que quieras cancelar e intentarlo de nuevo.\"\n\nmsgid \"Sort by URL\"\nmsgstr \"Ordenar por URL\"\n\nmsgid \"Sort by name\"\nmsgstr \"Ordenar por nombre\"\n\nmsgid \"Sort by type\"\nmsgstr \"Ordenar por tipo\"\n\nmsgid \"Sort by updated on\"\nmsgstr \"Ordenar por actualizada el\"\n\nmsgid \"Sort by version\"\nmsgstr \"Ordenar por versión\"\n\nmsgid \"Specify the menu structure.\"\nmsgstr \"Especificar la estructura del menú.\"\n\nmsgid \"State\"\nmsgstr \"Estado\"\n\nmsgid \"Status\"\nmsgstr \"Estado\"\n\nmsgid \"Status of importation\"\nmsgstr \"Estado de la importación\"\n\nmsgid \"Still, you can customize its look & feel on this page. You can also update some of its pages in the Resources menu.\"\nmsgstr \"Aún así, puedes personalizar su apariencia en esta página. También puedes actualizar algunas de sus páginas en el menú de Recursos.\"\n\nmsgid \"Submit\"\nmsgstr \"Enviar\"\n\nmsgid \"Successfully updated categories\"\nmsgstr \"Categorías actualizadas correctamente\"\n\nmsgid \"Table settings\"\nmsgstr \"Configuración de la tabla\"\n\nmsgid \"Take\"\nmsgstr \"Tomar\"\n\nmsgid \"Task Id\"\nmsgstr \"Id de tarea\"\n\nmsgid \"Task form\"\nmsgstr \"Formulario de tarea\"\n\nmsgid \"Task list\"\nmsgstr \"Lista de tareas\"\n\nmsgid \"Task name\"\nmsgstr \"Nombre de tarea\"\n\nmsgid \"Technical User\"\nmsgstr \"Usuario técnico\"\n\nmsgid \"Technical error.\"\nmsgstr \"Error técnico.\"\n\nmsgid \"The BDM schema and data have been rolled-back to what they were before this update attempt.\"\nmsgstr \"El esquema y los datos de BDM han sido revertidos al estado anterior de este intento de actualización.\"\n\nmsgid \"The application\"\nmsgstr \"La aplicación\"\n\nmsgid \"The application does not exist. Go to application list page to see the new list of applications.\"\nmsgstr \"La aplicación no existe. Vaya a la página de lista de aplicaciones para ver la nueva lista de aplicaciones.\"\n\nmsgid \"The application does not exist. Reload the page to see the new list of applications.\"\nmsgstr \"La aplicación no existe. Recarga la página para ver la nueva lista de aplicaciones.\"\n\nmsgid \"The application page does not exist. Reload the page to see the new list of application pages.\"\nmsgstr \"La página de la aplicación no existe. Recarga la página para ver la nueva lista de páginas de la aplicación.\"\n\nmsgid \"The attachment is too big.<br/>Select a smaller attachment and submit the form again.\"\nmsgstr \"El archivo adjunto es demasiado grande. <br/> Seleccione un archivo más pequeño y envíe el formulario de nuevo.\"\n\nmsgid \"The business data: [ {} ] uses Business Objects which are not defined in the current Business Data model. Deploy a compatible Business Data model before enabling the process.\"\nmsgstr \"El Dato de Negocio:  [ {} ] utiliza Objetos de Negocio que no están definidos en el Modelo de Datos de Negocio actual. Despliegue un Modelo de Datos de Negocio compatible antes de activar el proceso.\"\n\nmsgid \"The case is archived. You cannot add comments anymore\"\nmsgstr \"El caso está archivado. Ya no se pueden agregar comentarios\"\n\nmsgid \"The case {{caseId}} has been started successfully.\"\nmsgstr \"El caso {{caseId}} se ha iniciado con éxito.\"\n\nmsgid \"The category cannot be removed and added at the same time.\"\nmsgstr \"La categoría no puede ser eliminada y añadida al mismo tiempo.\"\n\nmsgid \"The current process has no connectors defined.\"\nmsgstr \"El proceso actual no tiene ningún conector definido\"\n\nmsgid \"The current process has no parameters defined.\"\nmsgstr \"El proceso actual no tiene parámetros definidos.\"\n\nmsgid \"The file you are trying to upload has the wrong mime type\"\nmsgstr \"El archivo que estás intentando subir tiene un mime type incorrecto\"\n\nmsgid \"The form mappings must be resolved before enabling the Process.\"\nmsgstr \"El mapeo de formularios debe resolverse antes de activar el proceso.\"\n\nmsgid \"The image you are trying to upload is too big\"\nmsgstr \"La imagen que estás intentando subir es demasiado grande\"\n\nmsgid \"The item does not exist anymore. Refresh the page to see the new status\"\nmsgstr \"El elemento ya no existe. Actualice la página para ver el nuevo estado\"\n\nmsgid \"The menu does not exist. Reload the page to see the new list of menus.\"\nmsgstr \"El menú no existe. Recarga la página para ver la nueva lista de menús.\"\n\nmsgid \"The name for the URL must start with custompage_ followed only by alphanumeric characters.\"\nmsgstr \"El nombre de la URL debe comenzar con custompage_ seguido de sólo caracteres alfanuméricos.\"\n\nmsgid \"The task has been submitted but an error occurred while adding the comment\"\nmsgstr \"La tarea ha sido enviada pero ocurrió un error al agregar el comentario\"\n\nmsgid \"The whole list of such changes is listed <a ng-href=\\\"{{documentationUrl}}\\\" target=\\\"_blank\\\" translate>here</a>.\"\nmsgstr \"La lista completa de estos cambios está disponible <a ng-href=\\\"{{documentationUrl}}\\\" target=\\\"_blank\\\" translate>aquí</a>.\"\n\nmsgid \"The words 'content', 'API' and 'theme' are reserved for internal use. They must not be used in an application or page URL.\"\nmsgstr \"Las palabras 'content', 'API' y 'theme' están reservadas para uso interno. No debe utilizarse en la URL de una página o en una aplicación.\"\n\nmsgid \"Theme\"\nmsgstr \"Tema\"\n\nmsgid \"Then for each user, fill out the custom information value, in the Studio (<i>List of users</i> tab of the organization), or right on this Portal page.\"\nmsgstr \"Entonces, para cada usuario, complete el valor de la información personalizada, en el Studio (pestaña Organización <i>Lista de usuarios</i>), o en esta página del Portal.\"\n\nmsgid \"They will be removed from the database permanently.\"\nmsgstr \"Serán eliminados de la base de datos permanentemente.\"\n\nmsgid \"This URL is already in use\"\nmsgstr \"Esta URL ya está en uso\"\n\nmsgid \"This application is core to the Bonita platform. To keep it working as it should, it must remain available and its menu should stay as it is.\"\nmsgstr \"Esta aplicación es core para la plataforma Bonita. Para que siga funcionando como debería, debe permanecer disponible y su menú debe permanecer como está.\"\n\nmsgid \"This category has already been added to this process.\"\nmsgstr \"Esta categoría ya ha sido añadida a este proceso.\"\n\nmsgid \"This field is mandatory\"\nmsgstr \"Este campo es obligatorio\"\n\nmsgid \"This is the task details panel. No information to display\"\nmsgstr \"Este es el panel de detalles de la tarea. No hay información para mostrar\"\n\nmsgid \"This name is already in use\"\nmsgstr \"Este nombre ya está en uso\"\n\nmsgid \"This only changes the application link. The application itself is still deployed at the same location.\"\nmsgstr \"Esto sólo cambia el enlace de la aplicación. La aplicación misma está desplegada en la misma ubicación.\"\n\nmsgid \"This only deletes the application link. The application itself is not deleted and is still deployed.\"\nmsgstr \"Esto sólo elimina el enlace de la aplicación. La aplicación en sí misma no se elimina y todavía está desplegada.\"\n\nmsgid \"This process is no longer available.\"\nmsgstr \"Este proceso ya no está disponible.\"\n\nmsgid \"This process is not fully configured, but it is still enabled. Users may still be able to start cases or do tasks, which may lead to errors.\"\nmsgstr \"Este proceso no está completamente configurado, pero todavía está habilitado. Los usuarios todavía pueden iniciar casos o realizar tareas, lo que puede llevar a errores.\"\n\nmsgid \"This process is not fully configured.\"\nmsgstr \"Este proceso no está completamente configurado.\"\n\nmsgid \"This process is not fully configured. It cannot be enabled.\"\nmsgstr \"Este proceso no está completamente configurado. No se puede activar.\"\n\nmsgid \"This task cannot be executed from here.\"\nmsgstr \"Esta tarea no se puede ejecutar desde aquí.\"\n\nmsgid \"This task is completed.\"\nmsgstr \"Esta tarea se ha completado.\"\n\nmsgid \"This task is not available anymore. The list is now up to date.\"\nmsgstr \"Esta tarea ya no está disponible. La lista ahora está actualizada.\"\n\nmsgid \"This task is overdue. It was supposed to be completed by {{dueDate}}\"\nmsgstr \"Esta tarea ha vencido. Debía ser completada el {{dueDate}}\"\n\nmsgid \"Title\"\nmsgstr \"Título\"\n\nmsgid \"To create an application, click New or Import.\"\nmsgstr \"Para crear una aplicación, haga clic en nuevo o importar.\"\n\nmsgid \"To do\"\nmsgstr \"Por hacer\"\n\nmsgid \"To do so, go to\"\nmsgstr \"Para ello, vaya a\"\n\nmsgid \"To do so, log out and then log back in as the Technical User, whose credentials are set upon Bonita platform installation.\"\nmsgstr \"Para ello, cierre la sesión y luego inicie sesión con el Usuario Técnico, cuyas credenciales se han establecido durante la instalación de la plataforma de Bonita.\"\n\nmsgid \"To fill out the form, you need to take this task. This means you will be the only one able to do it. To make it available to the team again, release it.\"\nmsgstr \"Para rellenar el formulario, es necesario tomar esta tarea. Esto significa que serás el único que podrá realizarla. Para que esté disponible de nuevo para el equipo, libérala.\"\n\nmsgid \"To use this feature, create a new version of the process in Bonita Studio version 6.4 and above, then create and install the new .bar file in the Portal.\"\nmsgstr \"Para utilizar esta función, cree una nueva versión del proceso en Bonita Studio versión 6.4 o superior, posteriormente cree e instale el nuevo archivo .bar en el Portal.\"\n\nmsgid \"Type\"\nmsgstr \"Tipo\"\n\nmsgid \"Type here to search for a user...\"\nmsgstr \"Escriba aquí para buscar un usuario...\"\n\nmsgid \"Type here to search...\"\nmsgstr \"Escriba aquí para buscar...\"\n\nmsgid \"Type in a case id and <br> press enter to search for <br> human task of a specific case\"\nmsgstr \"Escriba el id del caso y <br> pulse intro para buscar tareas <br> humanas de un caso en concreto\"\n\nmsgid \"URL\"\nmsgstr \"URL\"\n\nmsgid \"Unable to add the comment. Refresh your task list and try again. If the problem persists, contact your administrator\"\nmsgstr \"No se puede agregar el comentario. Actualiza la lista de tareas e inténtalo de nuevo. Si el problema persiste, ponte en contacto con tu administrador\"\n\nmsgid \"Unable to map category:\"\nmsgstr \"No se puede mapear la categoría:\"\n\nmsgid \"Unassign. This will make it available to team again\"\nmsgstr \"Desasignar. Esto la hará disponible para el equipo de nuevo\"\n\nmsgid \"Under normal\"\nmsgstr \"Bajo normal\"\n\nmsgid \"Update\"\nmsgstr \"Actualizar\"\n\nmsgid \"Update Business Data Model\"\nmsgstr \"Actualizar el Modelo de Datos de Negocio\"\n\nmsgid \"Update business card\"\nmsgstr \"Actualizar tarjeta de visita\"\n\nmsgid \"Update custom information\"\nmsgstr \"Actualizar la información personalizada\"\n\nmsgid \"Update general information\"\nmsgstr \"Actualizar la información general\"\n\nmsgid \"Update password\"\nmsgstr \"Actualizar contraseña\"\n\nmsgid \"Update personal information\"\nmsgstr \"Actualizar la información personal\"\n\nmsgid \"Updated by\"\nmsgstr \"Actualizado por\"\n\nmsgid \"Updated on\"\nmsgstr \"Actualizada\"\n\nmsgid \"Updating the Business Data Model applies changes to both existing database tables and data.\"\nmsgstr \"Actualizar el Modelo de Datos de Negocio aplica cambios tanto a las tablas de base de datos existentes como a los datos.\"\n\nmsgid \"Upload new picture\"\nmsgstr \"Subir nueva imagen\"\n\nmsgid \"Use Arrow up and Arrow down keys to browse among existing categories.\"\nmsgstr \"Use la tecla de flecha arriba y flecha abajo para navegar entre las categorías existentes.\"\n\nmsgid \"User id not provided. Unable to retrieve the user details.\"\nmsgstr \"Id de usuario no proporcionado. No se pueden recuperar los detalles del usuario.\"\n\nmsgid \"Username\"\nmsgstr \"Nombre de usuario\"\n\nmsgid \"Users\"\nmsgstr \"Usuarios\"\n\nmsgid \"Users mapped to {}\"\nmsgstr \"Usuarios asignados a {}\"\n\nmsgid \"Value\"\nmsgstr \"Valor\"\n\nmsgid \"Version\"\nmsgstr \"Version\"\n\nmsgid \"View application details\"\nmsgstr \"Ver detalles de la aplicación\"\n\nmsgid \"View task\"\nmsgstr \"Ver tarea\"\n\nmsgid \"View this task\"\nmsgstr \"Ver esta tarea\"\n\nmsgid \"Warning:\"\nmsgstr \"Advertencia:\"\n\nmsgid \"We couldn't find what you are looking for.\"\nmsgstr \"No pudimos encontrar lo que estás buscando.\"\n\nmsgid \"When you export an application descriptor, pages and profiles are not exported in the file.\"\nmsgstr \"Al exportar un descriptor de aplicación, las páginas y perfiles no se exportan en el archivo.\"\n\nmsgid \"You are about to export the descriptor of the application\"\nmsgstr \"Está a punto de exportar el descriptor de la aplicación\"\n\nmsgid \"You are going to be redirected to the process list.\"\nmsgstr \"Va a ser redirigido a la lista de procesos.\"\n\nmsgid \"You can create custom information in Bonita Studio:\"\nmsgstr \"Puede crear información personalizada en el Studio de Bonita:\"\n\nmsgid \"You must add a page before you can reference it in a menu.\"\nmsgstr \"Debe agregar una página antes de que poder hacer referencia en un menú.\"\n\nmsgid \"Your personal task list is empty. You can take a task from the ​<a href=\\\"#\\\" ng-click=\\\"changeFilter(TASK_FILTERS.TODO)\\\">To do</a> list.\"\nmsgstr \"Tu lista personal de tareas está vacía. Puedes realizar una tarea de la lista <a href=\\\"#\\\" ng-click=\\\"changeFilter(TASK_FILTERS.TODO)\\\"> Por hacer</a>.\"\n\nmsgid \"Your session is no longer active.\"\nmsgstr \"Su sesión ya no es válida o ha expirado.\"\n\nmsgid \"Zip code\"\nmsgstr \"Código postal\"\n\nmsgid \"Zip file structure error. Check that your .zip contains a well-formed page.properties and either the index.html or the Index.groovy file. For details, see the documentation or the example page readme (available in the custom page list)\"\nmsgstr \"Error de estructura del archivo Zip. Verifica que el archivo .zip contiene un page.properties bien formado así como el index.html o el archivo Index.groovy. Para obtener más información, consulta la documentación o el ejemplo en la página readme (disponible en la lista de páginas personalizadas)\"\n\nmsgid \"an Application\"\nmsgstr \"una Aplicación\"\n\nmsgid \"an application\"\nmsgstr \"una Aplicación\"\n\nmsgid \"application descriptor has been partially imported. The following items are not loaded in the Portal.\"\nmsgstr \"el descriptor de aplicación ha sido parcialmente importado. Los siguientes elementos no se han cargado en el Portal.\"\n\nmsgid \"application descriptor has been successfully imported, with complete page mapping and menu mapping.\"\nmsgstr \"el descriptor de aplicación ha sido importado con éxito, con un completo mapeo de páginas y de menú.\"\n\nmsgid \"here\"\nmsgstr \"aquí\"\n\nmsgid \"of\"\nmsgstr \"de\"\n\nmsgid \"will be permanently deleted.\"\nmsgstr \"se eliminará permanentemente.\"\n\nmsgid \"{{firstname}} {{lastname}} already has the membership <b>{{role}} of {{group}}</b>\"\nmsgstr \"{{firstname}} {{lastname}} ya tiene la membresía <b>{{role}} en {{group}}</b>\"\n\nmsgid \"{{membership.role_id.displayName}} of {{membership.group_id.displayName}}\"\nmsgstr \"{{membership.role_id.displayName}} de {{membership.group_id.displayName}}\"\n\nmsgid \"{{nbErrors}} errors on mapping updates\"\nmsgstr \"{{nbErrors}} errores en la actualización de mapeos\"\n\nmsgid \"{{nbMapping}} Process manager mappings could not be updated\"\nmsgstr \"El mapeo del Gestor de Procesos {{nbMapping}} no puede ser modificado\"\n\nmsgid \"{{nbMapping}} Process manager mappings have been updated\"\nmsgstr \"El mapeo del Gestor de Procesos {{nbMapping}} ha sido modificado\"\n\nmsgid \"{{nbOfDeletedCases}} cases have been deleted\"\nmsgstr \"{{nbOfDeletedCases}} casos han sido eliminados\"\n\nmsgid \"{{nbSucess}} actor mapping updates succeeded\"\nmsgstr \"{{nbSucess}} actualizaciones de mapeo de actor tuvieron éxito\"\n\nmsgid \"{{role}} of {{group}}\"\nmsgstr \"{{role}} de {{group}}\"\n\nmsgid \"{{role}} of {{group}} has been created\"\nmsgstr \"{{role}} de {{group}} ha sido creado\"\n\nmsgid \"{} mapped\"\nmsgstr \"{} mapeado\"\n\nmsgid \"{} mappings to delete\"\nmsgstr \"{} mapeos para eliminar\"\n\nmsgid \"{} more\"\nmsgstr \"{} más\"\n\nmsgid \"{} of {}\"\nmsgstr \"{} de {}\"\n\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/resources/i18n/portal-js_fr.po",
    "content": "msgid \"\"\nmsgstr \"\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Project-Id-Version: bonita\\n\"\n\"Plural-Forms: nplurals=2; plural=(n > 1);\\n\"\n\"X-Crowdin-Project: bonita\\n\"\n\"X-Crowdin-Project-ID: 13316\\n\"\n\"X-Crowdin-Language: fr\\n\"\n\"X-Crowdin-File: /dev/bonita-web/portal/portal-js.pot\\n\"\n\"X-Crowdin-File-ID: 60594\\n\"\n\"Language-Team: French\\n\"\n\"Language: fr_FR\\n\"\n\"PO-Revision-Date: 2024-01-01 00:00\\n\"\n\nmsgid \"1 case has been deleted\"\nmsgstr \"1 cas a été supprimé\"\n\nmsgid \"A Link to your application made outside the portal, e.g. with Bonita UI Builder.\"\nmsgstr \"Un Lien vers votre application créée en dehors du portail, par exemple avec Bonita UI Builder.\"\n\nmsgid \"A classic Bonita Living Application.\"\nmsgstr \"Une Living Application Bonita traditionnelle.\"\n\nmsgid \"A page with this name already exists.\"\nmsgstr \"Une page portant ce nom existe déjà.\"\n\nmsgid \"A server error occurred. The operation did not complete successfully\"\nmsgstr \"Une erreur de serveur s'est produite. L'opération n'a pas été terminée avec succès\"\n\nmsgid \"Above normal\"\nmsgstr \"Haute priorité\"\n\nmsgid \"Access denied. For more information, check the log file.\"\nmsgstr \"Accès refusé. Pour plus d'informations, consultez les logs.\"\n\nmsgid \"Actions\"\nmsgstr \"Actions\"\n\nmsgid \"Activation state\"\nmsgstr \"État d'activation\"\n\nmsgid \"Actor name\"\nmsgstr \"Nom de l'acteur\"\n\nmsgid \"Actors\"\nmsgstr \"Acteurs\"\n\nmsgid \"Actors must be resolved before enabling the Process.\"\nmsgstr \"Tous les acteurs doivent être associés à des entités de l'organisation pour que le processus puisse être activé.\"\n\nmsgid \"Add\"\nmsgstr \"Ajouter\"\n\nmsgid \"Add from custom pages\"\nmsgstr \"Ajouter une page parmi les pages personalisées\"\n\nmsgid \"Add membership\"\nmsgstr \"Ajouter une adhésion\"\n\nmsgid \"Add menu\"\nmsgstr \"Ajouter un menu\"\n\nmsgid \"Add menu item\"\nmsgstr \"Ajouter une option de menu\"\n\nmsgid \"Add page\"\nmsgstr \"Ajouter une page\"\n\nmsgid \"Add pages in Navigation\"\nmsgstr \"Ajouter des pages à la navigation\"\n\nmsgid \"Add top-level menu\"\nmsgstr \"Ajouter un menu \"\n\nmsgid \"Adding on apply\"\nmsgstr \"Appliquer pour ajouter\"\n\nmsgid \"Address\"\nmsgstr \"Adresse\"\n\nmsgid \"All\"\nmsgstr \"Tous\"\n\nmsgid \"All done. Good job!\"\nmsgstr \"Vous avez accompli toutes vos tâches. Bravo !\"\n\nmsgid \"An Error occurred during process activation/deactivation.\"\nmsgstr \"Une erreur s'est produite lors de l'activation/désactivation du processus.\"\n\nmsgid \"An Error occurred during process deletion.\"\nmsgstr \"Une erreur s'est produite durant la suppression du processus. \"\n\nmsgid \"An application is a customized environment for a specific user profile, in which users interact with business data and business processes in the most efficient way.\"\nmsgstr \"Une application est un environnement adapté à un profil particulier, dans lequel les utilisateurs interagissent avec les données et les processus métier avec une efficacité optimale.\"\n\nmsgid \"An error has occurred. For more information, check the log file.\"\nmsgstr \"Une erreur s'est produite. Pour plus d'informations, consultez les logs.\"\n\nmsgid \"An error occurred during categories update\"\nmsgstr \"Une erreur s'est produite à la mise à jour des catégories\"\n\nmsgid \"An error occurred when deploying the BDM. Consult the logs for more information.\"\nmsgstr \"Une erreur s’est produite lors du déploiement du Modèle de Données Métier (ou BDM).\\n\"\n\"Pour plus d’informations, consultez les logs.\"\n\nmsgid \"An error occurred while submitting the form.\"\nmsgstr \"Une erreur s'est produite lors de la soumission du formulaire.\"\n\nmsgid \"An error occurred while submitting the form.<br/>The task may not be available anymore.\"\nmsgstr \"Une erreur s’est produite lors de l’envoi du formulaire. <br/> Il se peut que la tâche ne soit plus disponible.\"\n\nmsgid \"An error occurred with the requested operation.\"\nmsgstr \"Une erreur s'est produite lors de l'opération demandée\"\n\nmsgid \"Application Link\"\nmsgstr \"Lien d'Application\"\n\nmsgid \"Application XML file\"\nmsgstr \"Fichier XML de l'application\"\n\nmsgid \"Application id not provided. Unable to retrieve the application details.\"\nmsgstr \"L'identifiant de l'application n'a pas été fourni. Impossible de récupérer les informations.\"\n\nmsgid \"Application list\"\nmsgstr \"Liste des applications\"\n\nmsgid \"Apply\"\nmsgstr \"Appliquer\"\n\nmsgid \"Archived cases\"\nmsgstr \"Cas archivés\"\n\nmsgid \"Are you sure you want to delete it?\"\nmsgstr \"Êtes-vous sûr(e) de vouloir le supprimer ?\"\n\nmsgid \"Are you sure you want to delete this membership ?\"\nmsgstr \"Êtes-vous sûr(e) de vouloir supprimer cette adhésion ?\"\n\nmsgid \"Assign to me. Only I will be able to do it\"\nmsgstr \"Me l'assigner. Je serai le seul en mesure de la faire\"\n\nmsgid \"Assigned on\"\nmsgstr \"Assignée le\"\n\nmsgid \"Available or My tasks\"\nmsgstr \"Disponible ou dans Mes tâches\"\n\nmsgid \"BDM access control file can now be installed.\"\nmsgstr \"Le fichier de contrôle d’accès aux données métier peut maintenant être installé.\"\n\nmsgid \"BDM file\"\nmsgstr \"Fichier BDM\"\n\nmsgid \"BDM has not been uploaded. System was not able to upload the provided BDM file for technical reasons. You can try again later.\"\nmsgstr \"Le Business Data Model n’a pas été mis à jour. Le système n’était pas en mesure de télécharger le fichier fourni pour des raisons techniques. Vous pouvez réessayer plus tard.\"\n\nmsgid \"BDM successfully updated.\"\nmsgstr \"Le Modèle de Données Métier a été mis à jour avec succès.\"\n\nmsgid \"Back\"\nmsgstr \"Retour\"\n\nmsgid \"Before updating the BDM, access control file should be deleted.\"\nmsgstr \"Avant de mettre à jour le BDM, le fichier de contrôle d’accès doit être supprimé.\"\n\nmsgid \"Before you import the application descriptor, the pages and the profile must already be loaded in the Portal.\"\nmsgstr \"Avant d'importer un descripteur d'application, déployez dans le portail les pages utilisées et le profil concerné par l'application.\"\n\nmsgid \"Business Data Model definition\"\nmsgstr \"Définition du Modèle de Données Métier (BDM)\"\n\nmsgid \"Business card\"\nmsgstr \"Carte de visite\"\n\nmsgid \"Business card has not been updated. Please retry later or contact an administrator\"\nmsgstr \"La carte de visite n’a pas été mise à jour. Veuillez réessayer ultérieurement ou contacter un administrateur\"\n\nmsgid \"Business card successfully updated\"\nmsgstr \"La carte de visite a été mise à jour avec succès\"\n\nmsgid \"Can't import page or form to the process\"\nmsgstr \"Impossible d'importer la page ou le formulaire dans le processus\"\n\nmsgid \"Can't update expression content\"\nmsgstr \"Impossible de mettre à jour le contenu de l'expression\"\n\nmsgid \"Cancel\"\nmsgstr \"Annuler\"\n\nmsgid \"Cancelled, skipped instances\"\nmsgstr \"Instances en état cancelled ou skipped (passées)\"\n\nmsgid \"Cannot upload the file\"\nmsgstr \"Impossible de charger le fichier\"\n\nmsgid \"Case\"\nmsgstr \"Cas\"\n\nmsgid \"Case ID\"\nmsgstr \"Cas\"\n\nmsgid \"Case comments\"\nmsgstr \"Commentaires du cas\"\n\nmsgid \"Case id: {{caseId}} - Process: {{processName}} ({{processVersion}})\"\nmsgstr \"Cas : {{caseId}} - Processus : {{processName}} ({{processVersion}})\"\n\nmsgid \"Case overview\"\nmsgstr \"Page de synthèse du cas\"\n\nmsgid \"Case start\"\nmsgstr \"Démarrage d'un cas\"\n\nmsgid \"Cases with failures\"\nmsgstr \"Cas en erreur\"\n\nmsgid \"Categories\"\nmsgstr \"Catégories\"\n\nmsgid \"Check that each entity is mapped to a profile, for either Bonita Portal or for applications you have created.\"\nmsgstr \"Vérifiez que chaque entité est mappée à un profil, pour le Bonita Portal ou pour les applications que vous avez créées.\"\n\nmsgid \"City\"\nmsgstr \"Ville\"\n\nmsgid \"Class name\"\nmsgstr \"Nom de la classe\"\n\nmsgid \"Click here to choose the .xml file\"\nmsgstr \"Cliquez ici pour choisir le fichier .xml\"\n\nmsgid \"Click here to choose your .zip file\"\nmsgstr \"Cliquez ici pour choisir le fichier .zip\"\n\nmsgid \"Click on Cancel to remain on this page and try to execute the operation again once you logged back in (e.g. in another tab).\"\nmsgstr \"Cliquez sur Annuler pour rester sur cette page et essayer d'exécuter à nouveau l'opération après vous être reconnecté (e.g. dans un autre onglet).\"\n\nmsgid \"Click on Cancel to remain on this page and wait for the maintenance to end.\"\nmsgstr \"Cliquez sur Annuler pour rester sur cette page et attendre la fin des opérations de maintenance.\"\n\nmsgid \"Click on OK to be redirected and log back in.\"\nmsgstr \"Cliquez sur OK pour être redirigé et vous reconnecter.\"\n\nmsgid \"Click on OK to be redirected to the maintenance page.\"\nmsgstr \"Cliquez sur OK pour être redirigé vers la page de maintenance.\"\n\nmsgid \"Close\"\nmsgstr \"Fermer\"\n\nmsgid \"Columns selection\"\nmsgstr \"Sélection des colonnes\"\n\nmsgid \"Comment:\"\nmsgstr \"Commentaire :\"\n\nmsgid \"Comments\"\nmsgstr \"Commentaires\"\n\nmsgid \"Compilation failure. Verify that the Index.groovy class implements the PageController interface.\"\nmsgstr \"Échec de compilation. Vérifiez que le fichier Index.groovy implémente l'interface PageController.\"\n\nmsgid \"Completed instances\"\nmsgstr \"Instances en état completed (faites)\"\n\nmsgid \"Configuration state\"\nmsgstr \"Etat de la configuration\"\n\nmsgid \"Confirm new Password\"\nmsgstr \"Confirmez le nouveau mot de passe\"\n\nmsgid \"Connectors\"\nmsgstr \"Connecteurs\"\n\nmsgid \"Connectors must be resolved before enabling the Process.\"\nmsgstr \"Tous les connecteurs doivent avoir une implémentation pour que le processus puisse être activé.\"\n\nmsgid \"Core to Bonita platform\"\nmsgstr \"Core pour la plateforme Bonita\"\n\nmsgid \"Core to Bonita platform; cannot be deleted\"\nmsgstr \"Core pour la plateforme Bonita; ne peut pas être supprimé\"\n\nmsgid \"Country\"\nmsgstr \"Pays\"\n\nmsgid \"Create a new membership\"\nmsgstr \"Créer une nouvelle adhésion\"\n\nmsgid \"Create an application\"\nmsgstr \"Créer une application\"\n\nmsgid \"Created by\"\nmsgstr \"Créé par\"\n\nmsgid \"Creation\"\nmsgstr \"Création\"\n\nmsgid \"Creation date\"\nmsgstr \"Créé le\"\n\nmsgid \"Creation on\"\nmsgstr \"Créée le\"\n\nmsgid \"Custom information\"\nmsgstr \"Informations spécifiques\"\n\nmsgid \"Custom information has not been updated. Please retry later or contact an administrator\"\nmsgstr \"Les informations spécifiques n’ont pas été mises à jour. Veuillez réessayer ultérieurement ou contacter un administrateur\"\n\nmsgid \"Custom information successfully updated\"\nmsgstr \"Les informations spécifiques ont été mises à jour avec succès\"\n\nmsgid \"Delete\"\nmsgstr \"Supprimer\"\n\nmsgid \"Delete application\"\nmsgstr \"Supprimer l'application\"\n\nmsgid \"Delete membership\"\nmsgstr \"Supprimer l'adhésion\"\n\nmsgid \"Delete process\"\nmsgstr \"Supprimer un processus\"\n\nmsgid \"Deleting a process definition will also delete all open and archived cases of this process.\"\nmsgstr \"La suppression d'une définition de processus supprimera également tous les cas, en cours et archivés, de ce processus.\"\n\nmsgid \"Description\"\nmsgstr \"Description\"\n\nmsgid \"Disable\"\nmsgstr \"Désactiver\"\n\nmsgid \"Display name\"\nmsgstr \"Nom métier\"\n\nmsgid \"Done tasks\"\nmsgstr \"Tâches terminées\"\n\nmsgid \"Drawing process...\"\nmsgstr \"Dessin du processus ...\"\n\nmsgid \"Due date\"\nmsgstr \"Echéance\"\n\nmsgid \"Edit\"\nmsgstr \"Modifier\"\n\nmsgid \"Edit menu\"\nmsgstr \"Editer l'option de menu\"\n\nmsgid \"Edit menu item\"\nmsgstr \"Editer l'option de menu\"\n\nmsgid \"Email\"\nmsgstr \"Courriel\"\n\nmsgid \"Enable\"\nmsgstr \"Activer\"\n\nmsgid \"Enable all\"\nmsgstr \"Tout associer\"\n\nmsgid \"Enter a new category name and click \\\"Add\\\" button to create a new category.\"\nmsgstr \"Entrez un nouveau nom de catégorie et cliquez sur le bouton \\\"Ajouter\\\" pour créer une nouvelle catégorie.\"\n\nmsgid \"Enter the user to start the case for...\"\nmsgstr \"Saisissez le nom de l'utilisateur pour qui vous allez démarrer le cas...\"\n\nmsgid \"Error\"\nmsgstr \"Erreur\"\n\nmsgid \"Error on drawing process!\"\nmsgstr \"Erreur lors du dessin de processus !\"\n\nmsgid \"Error while trying to start the case. Some required information is missing (contract not fulfilled).\"\nmsgstr \"Erreur lors de l'exécution la tâche. Certaines informations requises sont manquantes (contrat non respecté).\"\n\nmsgid \"Error!\"\nmsgstr \"Erreur!\"\n\nmsgid \"Error: value must be a boolean\"\nmsgstr \"Erreur : la valeur doit être un booléen\"\n\nmsgid \"Error: value must be a double\"\nmsgstr \"Erreur : valeur doit être un double\"\n\nmsgid \"Error: value must be an integer\"\nmsgstr \"Erreur : la valeur doit être un entier\"\n\nmsgid \"Executing, completing, initializing instances\"\nmsgstr \"Instances en état executing, completing, initializing\"\n\nmsgid \"Export\"\nmsgstr \"Exporter\"\n\nmsgid \"Export application descriptor\"\nmsgstr \"Exporter le descripteur d’application\"\n\nmsgid \"Expression content has been updated\"\nmsgstr \"Le contenu de l'expression a été mis à jour\"\n\nmsgid \"Failed instances\"\nmsgstr \"Instances en état failed (en échec)\"\n\nmsgid \"File format may be invalid. For more information, check the log file.\"\nmsgstr \"Le format du fichier est peut-être invalide. Pour plus d'informations, consultez les logs.\"\n\nmsgid \"Filename too long. The zip filename must be no longer than 50 characters\"\nmsgstr \"Le nom du fichier .zip est trop long. Il ne doit pas dépasser 50 caractères\"\n\nmsgid \"Filters\"\nmsgstr \"Filtres\"\n\nmsgid \"First name\"\nmsgstr \"Prénom\"\n\nmsgid \"Form\"\nmsgstr \"Formulaire\"\n\nmsgid \"Form submitted.<br/>The next task in the list is now selected.\"\nmsgstr \"Formulaire soumis.<br/>La tâche suivante dans la liste est maintenant sélectionnée.\"\n\nmsgid \"General\"\nmsgstr \"Général\"\n\nmsgid \"General information\"\nmsgstr \"Informations générales\"\n\nmsgid \"General information has not been updated. Please retry later or contact an administrator\"\nmsgstr \"Les informations générales n’ont pas été mises à jour. Veuillez réessayer ultérieurement ou contacter un administrateur\"\n\nmsgid \"General information successfully updated\"\nmsgstr \"Les informations générales ont été mises à jour avec succès\"\n\nmsgid \"Groups\"\nmsgstr \"Groupes\"\n\nmsgid \"Healthy cases\"\nmsgstr \"Cas sans erreur\"\n\nmsgid \"Hide Menu\"\nmsgstr \"Masquer le Menu\"\n\nmsgid \"Hide menu\"\nmsgstr \"Masquer le menu\"\n\nmsgid \"Hide panel\"\nmsgstr \"Masquer le panneau\"\n\nmsgid \"Highest\"\nmsgstr \"Plus haute\"\n\nmsgid \"However, due to the use of Hibernate for data persistence, some changes on the data may not be well handled. These changes may lead to a corrupted database and CANNOT BE REVERTED.\"\nmsgstr \"Cependant, en raison de l'utilisation de Hibernate pour la persistance des données, certains changements sur les données peuvent ne pas être bien gérés. Ces changements peuvent conduire à une base de données corrompue et NE PEUT PAS ÊTRE REVERTES.\"\n\nmsgid \"I have read and understood the previous warning message. I have prepared a database backup so I can rollback this operation.\"\nmsgstr \"J'ai lu et compris le message d'avertissement précédent, j'ai préparé une sauvegarde de la base de données pour pouvoir annuler cette opération.\"\n\nmsgid \"IN ANY CASE, you MUST CREATE A BACKUP of the current BDM database before undergoing a BDM update.\"\nmsgstr \"DANS TOUS LES CAS, vous DEVEZ CRÉER UNE BACKUP de la base de données BDM actuelle avant de subir une mise à jour BDM.\"\n\nmsgid \"If your changes do not fall into those change types, you can go ahead and proceed with the BDM update now.\"\nmsgstr \"Si vos modifications ne tombent pas dans ces types de modifications, vous pouvez continuer avec la mise à jour du BDM maintenant.\"\n\nmsgid \"Implementation version\"\nmsgstr \"Version de l'implémentation\"\n\nmsgid \"Import\"\nmsgstr \"Importer\"\n\nmsgid \"Import an application\"\nmsgstr \"Importer une application\"\n\nmsgid \"Import an application descriptor\"\nmsgstr \"Importer un descripteur d’application\"\n\nmsgid \"In a production environment, this should be used with caution.\"\nmsgstr \"Cette action est à mener avec beaucoup de précaution dans un environnement de production.\"\n\nmsgid \"In such cases, you must CANCEL THIS UPDATE and let the database administrator carefully script the changes and apply them to the database. Only then will you be able to update the BDM from this page.\"\nmsgstr \"Dans de tels cas, vous devez ANNULER CETTE MISE À JOUR et laisser l'administrateur de la base de données script soigneusement les modifications et les appliquer à la base de données. Ce n'est qu'alors que vous pourrez mettre à jour le BDM à partir de cette page.\"\n\nmsgid \"In task name column\"\nmsgstr \"Dans la colonne \\\"Tâche\\\"\"\n\nmsgid \"In the menu <i>Organization > Manage</i>, go to the <i>Users</i> page of the wizard\"\nmsgstr \"Dans le menu <i>Organisation > Gérer</i>, allez à la page <i>Utilisateurs</i> de l’assistant graphique\"\n\nmsgid \"Install\"\nmsgstr \"Installer\"\n\nmsgid \"Install Business Data Model\"\nmsgstr \"Installer le Modèle de Données Métier (BDM)\"\n\nmsgid \"Install a BDM file\"\nmsgstr \"Installer un fichier BDM\"\n\nmsgid \"Installed\"\nmsgstr \"Installé\"\n\nmsgid \"Installed by\"\nmsgstr \"Installé par\"\n\nmsgid \"Installed on\"\nmsgstr \"Installé le\"\n\nmsgid \"Installing\"\nmsgstr \"En cours d'installation\"\n\nmsgid \"Installing Business Data Model\"\nmsgstr \"Installation du Modèle de Données Métier (BDM)\"\n\nmsgid \"Installing or updating the BDM implies to be logged in as Technical User, and to activate the maintenance mode.\"\nmsgstr \"Installer ou mettre à jour le BDM nécessite d'être authentifié en tant qu'Utilisateur Technique et d'avoir activé le mode maintenance.\"\n\nmsgid \"Items per page\"\nmsgstr \"Eléments par page\"\n\nmsgid \"Job title\"\nmsgstr \"Intitulé du poste\"\n\nmsgid \"Last login\"\nmsgstr \"Dernière connexion\"\n\nmsgid \"Last name\"\nmsgstr \"Nom\"\n\nmsgid \"Last update\"\nmsgstr \"Dernière mise à jour\"\n\nmsgid \"Layout\"\nmsgstr \"Layout\"\n\nmsgid \"Legacy Application\"\nmsgstr \"Application Traditionnelle\"\n\nmsgid \"Loading...\"\nmsgstr \"Chargement...\"\n\nmsgid \"Logo\"\nmsgstr \"Logo\"\n\nmsgid \"Look & Feel\"\nmsgstr \"Look & Feel\"\n\nmsgid \"Lowest\"\nmsgstr \"Plus basse\"\n\nmsgid \"MM/dd/yyyy h:mm a\"\nmsgstr \"dd/MM/yyyy HH:mm\"\n\nmsgid \"MMM DD LT\"\nmsgstr \"DD MMM LT\"\n\nmsgid \"Make sure all artifacts enabled on the platform are compatible with the new BDM.\"\nmsgstr \"Assurez-vous que tous les artefacts activés sur la plate-forme sont compatibles avec le nouveau BDM.\"\n\nmsgid \"Make sure to deactivate the maintenance mode.\"\nmsgstr \"Assurez-vous de désactiver le mode maintenance.\"\n\nmsgid \"Make this task available to other team members again\"\nmsgstr \"Libérer - rendre cette tâche à nouveau disponible pour les autres membres de l’équipe\"\n\nmsgid \"Make this task personal: only I will be able to do it\"\nmsgstr \"Rendre cette tâche personnelle : je serai le(la) seul(e) à pouvoir la faire\"\n\nmsgid \"Manage Categories\"\nmsgstr \"Gérer les catégories\"\n\nmsgid \"Manager\"\nmsgstr \"Responsable\"\n\nmsgid \"Mapped categories\"\nmsgstr \"Catégories associées\"\n\nmsgid \"Maximum size 255 characters\"\nmsgstr \"Taille maximum de 255 caractères\"\n\nmsgid \"Membership has not been added. Please retry later or contact an administrator\"\nmsgstr \"L'adhésion n’a pas été mise à jour. Veuillez réessayer ultérieurement ou contacter un administrateur\"\n\nmsgid \"Membership has not been removed. Please retry later or contact an administrator\"\nmsgstr \"L'adhésion n'a pas été supprimée. Veuillez réessayer ultérieurement ou contacter un administrateur\"\n\nmsgid \"Membership successfully added\"\nmsgstr \"L'adhésion a été ajoutée avec succès\"\n\nmsgid \"Membership successfully removed\"\nmsgstr \"L'adhésion a été supprimée avec succès\"\n\nmsgid \"Memberships\"\nmsgstr \"Adhésions\"\n\nmsgid \"Memberships mapped to {}\"\nmsgstr \"Adhésions associées à {}\"\n\nmsgid \"Mobile\"\nmsgstr \"Portable\"\n\nmsgid \"Modifying or redeploying the Business Data Model may impact existing data retention rules. After redeployment, review your configuration in the Data Retention (GDPR) section.\"\nmsgstr \"La modification ou le redéploiement du Modèle de Données Métier peuvent avoir une incidence sur les règles de rétention des données existantes. Après le redéploiement, vérifiez votre configuration dans la section Rétention des Données (RGPD).\"\n\nmsgid \"Monitoring status\"\nmsgstr \"Monitoring\"\n\nmsgid \"Multi-page menu\"\nmsgstr \"Menu\"\n\nmsgid \"My tasks\"\nmsgstr \"Mes tâches\"\n\nmsgid \"Name\"\nmsgstr \"Nom\"\n\nmsgid \"Navigation\"\nmsgstr \"Navigation\"\n\nmsgid \"New\"\nmsgstr \"Nouveau\"\n\nmsgid \"New Password\"\nmsgstr \"Nouveau mot de passe\"\n\nmsgid \"New comment\"\nmsgstr \"Nouveau commentaire\"\n\nmsgid \"No application available.\"\nmsgstr \"Aucune application définie.\"\n\nmsgid \"No application found with id:\"\nmsgstr \"Aucune application ne semble correspondre à l'identifiant :\"\n\nmsgid \"No comment on this case yet. To add one, use the input field at the bottom of this panel\"\nmsgstr \"Aucun commentaire sur ce cas pour l'instant. Le champ de saisie se trouve en bas de ce panneau\"\n\nmsgid \"No custom information defined.\"\nmsgstr \"Aucune information personnalisée n'a été définie.\"\n\nmsgid \"No data\"\nmsgstr \"Pas de donnée disponible\"\n\nmsgid \"No description.\"\nmsgstr \"Aucune description disponible.\"\n\nmsgid \"No done task yet.\"\nmsgstr \"Aucune tâche accomplie pour l'instant.\"\n\nmsgid \"No form is needed. You can enter a comment and confirm.\"\nmsgstr \"Aucun formulaire à remplir. Vous pouvez saisir un commentaire et confirmer.\"\n\nmsgid \"No mapping\"\nmsgstr \"Aucun mapping\"\n\nmsgid \"No process found with id:\"\nmsgstr \"Aucun processus ne semble correspondre à l'identifiant :\"\n\nmsgid \"No profile\"\nmsgstr \"Pas de profil\"\n\nmsgid \"No profile mapped to this application\"\nmsgstr \"Cette application n'a pas de profil associé\"\n\nmsgid \"No updates have been made on categories. For more details, check logs.\"\nmsgstr \"Aucune mise à jour n'a été faite sur les catégories. Pour plus de détails, consultez les logs.\"\n\nmsgid \"No user found with id:\"\nmsgstr \"Aucun utilisateur n'a été trouvé avec l'id :\"\n\nmsgid \"Normal\"\nmsgstr \"Normale\"\n\nmsgid \"Not installed\"\nmsgstr \"Non installé\"\n\nmsgid \"OK\"\nmsgstr \"OK\"\n\nmsgid \"One or more tasks are not available anymore. The list is now up to date.\"\nmsgstr \"Une ou plusieurs tâches ne sont plus disponibles. La liste est maintenant à jour.\"\n\nmsgid \"One-page menu\"\nmsgstr \"Lien vers une page\"\n\nmsgid \"Only I can do this task\"\nmsgstr \"Je suis le seul en mesure de faire cette tâche\"\n\nmsgid \"Only available when the platform is under maintenance. Go to the \\\"Platform maintenance\\\" page to activate the maintenance mode.\"\nmsgstr \"Uniquement disponible lorsque la plateforme est en maintenance. Allez sur la page \\\"Maintenance de la plate-forme\\\" pour activer le mode maintenance.\"\n\nmsgid \"Only one Business Data Model (BDM) can be active at a time.\"\nmsgstr \"Il ne peut y avoir qu'un seul Modèle de Données Métier (BDM) actif à la fois.\"\n\nmsgid \"Open cases\"\nmsgstr \"Cas démarrés\"\n\nmsgid \"Open in a popup\"\nmsgstr \"Ouvrir dans une pop-up\"\n\nmsgid \"Open the task to perform it\"\nmsgstr \"Ouvrez la tâche pour l'exécuter\"\n\nmsgid \"Overview\"\nmsgstr \"Synthèse\"\n\nmsgid \"Page\"\nmsgstr \"Page\"\n\nmsgid \"Pages\"\nmsgstr \"Pages\"\n\nmsgid \"Parameters\"\nmsgstr \"Paramètres\"\n\nmsgid \"Parameters must be resolved before enabling the Process.\"\nmsgstr \"Tous les paramètres doivent avoir une valeur pour pouvoir activer le processus.\"\n\nmsgid \"Password\"\nmsgstr \"Mot de passe\"\n\nmsgid \"Password has not been updated. Please retry later or contact an administrator\"\nmsgstr \"Le mot de passe n’a pas été mis à jour. Veuillez réessayer ultérieurement ou contacter un administrateur\"\n\nmsgid \"Password successfully updated\"\nmsgstr \"Le mot de passe a été mis à jour avec succès\"\n\nmsgid \"Passwords don't match\"\nmsgstr \"Les mots de passe ne correspondent pas\"\n\nmsgid \"Personal information\"\nmsgstr \"Informations personnelles\"\n\nmsgid \"Personal information has not been updated. Please retry later or contact an administrator\"\nmsgstr \"Les informations personnelles n’ont pas été mises à jour. Veuillez réessayer ultérieurement ou contacter un administrateur\"\n\nmsgid \"Personal information successfully updated\"\nmsgstr \"Les informations personnelles ont été mises à jour avec succès\"\n\nmsgid \"Phone\"\nmsgstr \"Téléphone\"\n\nmsgid \"Please refresh the page.\"\nmsgstr \"Veuillez actualiser la page.\"\n\nmsgid \"Priority\"\nmsgstr \"Priorité\"\n\nmsgid \"Process\"\nmsgstr \"Processus\"\n\nmsgid \"Process id\"\nmsgstr \"Id du processus\"\n\nmsgid \"Process id not provided. Unable to retrieve the process details.\"\nmsgstr \"L'identifiant du processus n'a pas été fourni. Impossible de récupérer les informations.\"\n\nmsgid \"Process name\"\nmsgstr \"Nom du processus\"\n\nmsgid \"Process:\"\nmsgstr \"Processus :\"\n\nmsgid \"Profile\"\nmsgstr \"Profils\"\n\nmsgid \"Profiles\"\nmsgstr \"Profils\"\n\nmsgid \"Profiles / Memberships\"\nmsgstr \"Profils / Adhésions\"\n\nmsgid \"Ready, waiting instances\"\nmsgstr \"Instances en état ready, waiting (en attente)\"\n\nmsgid \"Refresh data\"\nmsgstr \"Actualiser les données\"\n\nmsgid \"Release\"\nmsgstr \"Libérer\"\n\nmsgid \"Remove\"\nmsgstr \"Supprimer\"\n\nmsgid \"Remove all\"\nmsgstr \"Tout supprimer\"\n\nmsgid \"Removing on apply\"\nmsgstr \"Appliquer pour supprimer\"\n\nmsgid \"Reset\"\nmsgstr \"Réinitialiser\"\n\nmsgid \"Reset to default\"\nmsgstr \"Réinitialiser avec les paramètres par défaut\"\n\nmsgid \"Roles\"\nmsgstr \"Rôles\"\n\nmsgid \"Save\"\nmsgstr \"Enregistrer\"\n\nmsgid \"Search...\"\nmsgstr \"Chercher...\"\n\nmsgid \"Select a group...\"\nmsgstr \"Sélectionnez un groupe ...\"\n\nmsgid \"Select a role...\"\nmsgstr \"Sélectionnez un rôle ...\"\n\nmsgid \"Select all\"\nmsgstr \"Tout sélectionner \"\n\nmsgid \"Select groups...\"\nmsgstr \"Sélectionnez les groupes...\"\n\nmsgid \"Select none\"\nmsgstr \"Tout désélectionner\"\n\nmsgid \"Select roles...\"\nmsgstr \"Sélectionnez des rôles ...\"\n\nmsgid \"Select the <i>User information management</i> tab to add <i>Custom information</i> on the right\"\nmsgstr \"Sélectionnez l’onglet <i>Gestion des informations utilisateur</i> pour ajouter des <i>informations spécifiques</i> sur la droite\"\n\nmsgid \"Select the entities (users, groups, roles, memberships) to map to the actors. These entities will do the human tasks in the process.\"\nmsgstr \"Sélectionnez les entités (utilisateurs, groupes, rôles, adhésions) à associer aux acteurs. Ces entités exécuteront les tâches \\\"humaines\\\" du processus.\"\n\nmsgid \"Select the page\"\nmsgstr \"Sélectionnez la page\"\n\nmsgid \"Select users...\"\nmsgstr \"Sélectionnez des utilisateurs ...\"\n\nmsgid \"Server is under maintenance.\"\nmsgstr \"Une maintenance est en cours sur le serveur.\"\n\nmsgid \"Set as Home page\"\nmsgstr \"Définir en tant que page d'accueil\"\n\nmsgid \"Show Menu\"\nmsgstr \"Afficher le Menu\"\n\nmsgid \"Show all the tasks I can do: both personal tasks and tasks available to other team members\"\nmsgstr \"Afficher toutes les tâches que je peux faire : mes tâches personnelles et les tâches disponibles pour moi et les autres membres de l’équipe\"\n\nmsgid \"Show menu\"\nmsgstr \"Afficher le menu\"\n\nmsgid \"Show my personal tasks: the ones I took and the ones assigned to me, automatically or by a process manager\"\nmsgstr \"Afficher mes tâches personnelles : celles que j’ai prises et celles qui m'ont été assignées, automatiquement ou par un gestionnaire de processus\"\n\nmsgid \"Show panel\"\nmsgstr \"Afficher le panneau\"\n\nmsgid \"Show the tasks that I have done\"\nmsgstr \"Afficher les tâches que j’ai faites\"\n\nmsgid \"Something went wrong during the deletion. You might want to cancel and try again.\"\nmsgstr \"Une erreur s'est produite lors de la suppression. Vous pouvez annuler et réessayer.\"\n\nmsgid \"Something went wrong during the modification. You might want to cancel and try again.\"\nmsgstr \"Une erreur s'est produite lors de la modification. Vous pouvez annuler et réessayer.\"\n\nmsgid \"Something went wrong. You might want to cancel and try again.\"\nmsgstr \"Une erreur s'est produite lors de la suppression. Vous pouvez annuler et réessayer.\"\n\nmsgid \"Sort by URL\"\nmsgstr \"Trier par URL\"\n\nmsgid \"Sort by name\"\nmsgstr \"Trier par nom\"\n\nmsgid \"Sort by type\"\nmsgstr \"Trier par type\"\n\nmsgid \"Sort by updated on\"\nmsgstr \"Trier par date de mise à jour\"\n\nmsgid \"Sort by version\"\nmsgstr \"Trier par version\"\n\nmsgid \"Specify the menu structure.\"\nmsgstr \"Définir le menu de navigation.\"\n\nmsgid \"State\"\nmsgstr \"Etat\"\n\nmsgid \"Status\"\nmsgstr \"Statut\"\n\nmsgid \"Status of importation\"\nmsgstr \"Statut de l'importation\"\n\nmsgid \"Still, you can customize its look & feel on this page. You can also update some of its pages in the Resources menu.\"\nmsgstr \"Néanmoins, vous pouvez personnaliser son apparence sur cette page. Vous pouvez également mettre à jour certaines de ses pages dans le menu Ressources.\"\n\nmsgid \"Submit\"\nmsgstr \"Soumettre\"\n\nmsgid \"Successfully updated categories\"\nmsgstr \"Les catégories ont été mises à jour avec succès\"\n\nmsgid \"Table settings\"\nmsgstr \"Paramètres du tableau\"\n\nmsgid \"Take\"\nmsgstr \"Prendre\"\n\nmsgid \"Task Id\"\nmsgstr \"ID de tâche\"\n\nmsgid \"Task form\"\nmsgstr \"Formulaire de tâche\"\n\nmsgid \"Task list\"\nmsgstr \"Liste de tâches\"\n\nmsgid \"Task name\"\nmsgstr \"Tâche\"\n\nmsgid \"Technical User\"\nmsgstr \"Utilisateur Technique\"\n\nmsgid \"Technical error.\"\nmsgstr \"Erreur technique.\"\n\nmsgid \"The BDM schema and data have been rolled-back to what they were before this update attempt.\"\nmsgstr \"Le schéma et les données du BDM ont été annulés à ce qu'ils étaient avant cette tentative de mise à jour.\"\n\nmsgid \"The application\"\nmsgstr \"L'application\"\n\nmsgid \"The application does not exist. Go to application list page to see the new list of applications.\"\nmsgstr \"L'application n'existe pas. Allez à la page de la liste des applications pour voir la nouvelle liste d'applications.\"\n\nmsgid \"The application does not exist. Reload the page to see the new list of applications.\"\nmsgstr \"L'application n'existe pas. Rechargez la page pour voir la nouvelle liste d'applications.\"\n\nmsgid \"The application page does not exist. Reload the page to see the new list of application pages.\"\nmsgstr \"La page d'application n'existe pas. Rechargez la, pour voir la nouvelle liste des pages d'application.\"\n\nmsgid \"The attachment is too big.<br/>Select a smaller attachment and submit the form again.\"\nmsgstr \"Le fichier joint est trop volumineux.<br/>Sélectionnez un fichier plus petit et soumettez le formulaire à nouveau.\"\n\nmsgid \"The business data: [ {} ] uses Business Objects which are not defined in the current Business Data model. Deploy a compatible Business Data model before enabling the process.\"\nmsgstr \"Les données métier [ {} ] font référence à des objets qui ne sont pas définis dans le Modèle de Données Métier actuel. Déployez un Modèle de Données Métier compatible pour pouvoir activer le processus.\"\n\nmsgid \"The case is archived. You cannot add comments anymore\"\nmsgstr \"Le cas est archivé. Il n'est plus possible d'ajouter de nouveaux commentaires\"\n\nmsgid \"The case {{caseId}} has been started successfully.\"\nmsgstr \"Le cas {{caseId}} a été démarré avec succès.\"\n\nmsgid \"The category cannot be removed and added at the same time.\"\nmsgstr \"La catégorie ne peut pas être supprimée et ajoutée en même temps.\"\n\nmsgid \"The current process has no connectors defined.\"\nmsgstr \"Aucun connecteur n'a été défini pour ce processus.\"\n\nmsgid \"The current process has no parameters defined.\"\nmsgstr \"Aucun paramètre n'a été défini pour ce processus.\"\n\nmsgid \"The file you are trying to upload has the wrong mime type\"\nmsgstr \"Le fichier que vous essayez de charger n'a pas le bon type MIME\"\n\nmsgid \"The form mappings must be resolved before enabling the Process.\"\nmsgstr \"Le mapping de tous les éléments en attente d'un formulaire ou d'une page doit être effectif pour pouvoir activer le processus.\"\n\nmsgid \"The image you are trying to upload is too big\"\nmsgstr \"L'image que vous essayez de charger est trop grande\"\n\nmsgid \"The item does not exist anymore. Refresh the page to see the new status\"\nmsgstr \"L' élément n'existe plus. Rafraîchissez la page pour voir le nouveau statut\"\n\nmsgid \"The menu does not exist. Reload the page to see the new list of menus.\"\nmsgstr \"Le menu n'existe pas. Rechargez la page pour voir la nouvelle liste de menus.\"\n\nmsgid \"The name for the URL must start with custompage_ followed only by alphanumeric characters.\"\nmsgstr \"Le nom de l'URL doit commencer par custompage_ puis contenir uniquement des caractères alphanumériques.\"\n\nmsgid \"The task has been submitted but an error occurred while adding the comment\"\nmsgstr \"La tâche a été soumise, mais une erreur s'est produite à l'ajout du commentaire\"\n\nmsgid \"The whole list of such changes is listed <a ng-href=\\\"{{documentationUrl}}\\\" target=\\\"_blank\\\" translate>here</a>.\"\nmsgstr \"La liste complète de ces changements est listée <a ng-href=\\\"{{documentationUrl}}\\\" target=\\\"_blank\\\" translate>ici</a>.\"\n\nmsgid \"The words 'content', 'API' and 'theme' are reserved for internal use. They must not be used in an application or page URL.\"\nmsgstr \"Les mots 'content', 'API' et 'theme' sont réservés pour un usage interne. Ils ne doivent pas être utilisés dans l'URL d'une page ou d'une application.\"\n\nmsgid \"Theme\"\nmsgstr \" Thème\"\n\nmsgid \"Then for each user, fill out the custom information value, in the Studio (<i>List of users</i> tab of the organization), or right on this Portal page.\"\nmsgstr \"Puis pour chaque utilisateur, remplissez la valeur des informations spécifiques, soit dans le studio (onglet <i>Liste des utilisateurs</i> de l’organisation), ou directement sur cette page du portail.\"\n\nmsgid \"They will be removed from the database permanently.\"\nmsgstr \"Ils seront définitivement effacés de la base de données.\"\n\nmsgid \"This URL is already in use\"\nmsgstr \"Cette URL est déjà utilisée\"\n\nmsgid \"This application is core to the Bonita platform. To keep it working as it should, it must remain available and its menu should stay as it is.\"\nmsgstr \"Cette application est le cœur de la plate-forme Bonita. Pour qu'il fonctionne comme il le devrait, il doit rester disponible et son menu doit rester tel qu'il est.\"\n\nmsgid \"This category has already been added to this process.\"\nmsgstr \"Cette catégorie a déjà été ajoutée à ce processus.\"\n\nmsgid \"This field is mandatory\"\nmsgstr \"Ce champ est obligatoire\"\n\nmsgid \"This is the task details panel. No information to display\"\nmsgstr \"Cette zone est dédiée aux détails d'une tâche. Aucune information à afficher pour l'instant\"\n\nmsgid \"This name is already in use\"\nmsgstr \"Ce nom est déjà utilisé\"\n\nmsgid \"This only changes the application link. The application itself is still deployed at the same location.\"\nmsgstr \"Cela ne modifie que le lien de l'application. L'application elle-même est toujours déployée au même endroit.\"\n\nmsgid \"This only deletes the application link. The application itself is not deleted and is still deployed.\"\nmsgstr \"Ceci ne supprime que le lien de l'application. L'application elle-même n'est pas supprimée et est toujours déployée.\"\n\nmsgid \"This process is no longer available.\"\nmsgstr \"Ce processus n'est plus disponible.\"\n\nmsgid \"This process is not fully configured, but it is still enabled. Users may still be able to start cases or do tasks, which may lead to errors.\"\nmsgstr \"Ce processus n'est pas entièrement configuré, mais il est toujours activé. Les utilisateurs peuvent toujours démarrer des cas ou exécuter des tâches, ce qui pourrait entraîner des erreurs.\"\n\nmsgid \"This process is not fully configured.\"\nmsgstr \"Ce processus n'est pas entièrement configuré. \"\n\nmsgid \"This process is not fully configured. It cannot be enabled.\"\nmsgstr \"Ce processus n'est pas entièrement configuré. Il ne peut pas être activé.\"\n\nmsgid \"This task cannot be executed from here.\"\nmsgstr \"Cette tâche ne peut pas être exécutée dans le Portal. Elle doit être exécutée par le système.\"\n\nmsgid \"This task is completed.\"\nmsgstr \"Cette tâche est terminée.\"\n\nmsgid \"This task is not available anymore. The list is now up to date.\"\nmsgstr \"Cette tâche n'est plus disponible. La liste est maintenant à jour.\"\n\nmsgid \"This task is overdue. It was supposed to be completed by {{dueDate}}\"\nmsgstr \"Cette tâche est en retard. Elle aurait dû être faite pour le {{dueDate}}\"\n\nmsgid \"Title\"\nmsgstr \"Titre\"\n\nmsgid \"To create an application, click New or Import.\"\nmsgstr \"Pour créer une application, cliquez sur Nouveau ou Importer.\"\n\nmsgid \"To do\"\nmsgstr \"À faire\"\n\nmsgid \"To do so, go to\"\nmsgstr \"Pour cela, consultez la page\"\n\nmsgid \"To do so, log out and then log back in as the Technical User, whose credentials are set upon Bonita platform installation.\"\nmsgstr \"Pour cela, déconnectez-vous puis reconnectez-vous en tant qu’Utilisateur Technique, dont les informations de connexion sont définies à l’installation de la plate-forme Bonita.\"\n\nmsgid \"To fill out the form, you need to take this task. This means you will be the only one able to do it. To make it available to the team again, release it.\"\nmsgstr \"Pour remplir le formulaire, vous devez \\\"prendre\\\" cette tâche. Cela signifie que vous serez le(la) seul(e) à pouvoir la faire. Pour la rendre à nouveau disponible à l'équipe, \\\"libérez\\\"-la.\"\n\nmsgid \"To use this feature, create a new version of the process in Bonita Studio version 6.4 and above, then create and install the new .bar file in the Portal.\"\nmsgstr \"Pour utiliser cette fonctionnalité, créez une nouvelle version du processus dans un Bonita Studio version 6.4 ou ultérieure, générez le fichier .bar puis déployez-le dans le portail.\"\n\nmsgid \"Type\"\nmsgstr \"Type\"\n\nmsgid \"Type here to search for a user...\"\nmsgstr \"Saisissez le nom d'un utilisateur...\"\n\nmsgid \"Type here to search...\"\nmsgstr \"Rechercher...\"\n\nmsgid \"Type in a case id and <br> press enter to search for <br> human task of a specific case\"\nmsgstr \"Saisissez un Id de cas puis <br> tapez entrée pour lister <br> les tâches de ce cas\"\n\nmsgid \"URL\"\nmsgstr \"URL\"\n\nmsgid \"Unable to add the comment. Refresh your task list and try again. If the problem persists, contact your administrator\"\nmsgstr \"Impossible d'ajouter le commentaire. Actualisez votre liste de tâches, puis réessayez. Si le problème persiste, contactez votre administrateur\"\n\nmsgid \"Unable to map category:\"\nmsgstr \"Impossible d'associer la catégorie:\"\n\nmsgid \"Unassign. This will make it available to team again\"\nmsgstr \"Désassigner. Cela la rendra à nouveau disponible pour les membres de l'équipe\"\n\nmsgid \"Under normal\"\nmsgstr \"Basse priorité\"\n\nmsgid \"Update\"\nmsgstr \"Mise à jour\"\n\nmsgid \"Update Business Data Model\"\nmsgstr \"Mettre à jour le Modèle de Données Métier (BDM)\"\n\nmsgid \"Update business card\"\nmsgstr \"Mettre à jour la carte de visite\"\n\nmsgid \"Update custom information\"\nmsgstr \"Mettre à jour les informations spécifiques\"\n\nmsgid \"Update general information\"\nmsgstr \"Mettre à jour les informations générales\"\n\nmsgid \"Update password\"\nmsgstr \"Mettre à jour le mot de passe\"\n\nmsgid \"Update personal information\"\nmsgstr \"Mettre à jour les informations personnelles\"\n\nmsgid \"Updated by\"\nmsgstr \"Mise à jour par\"\n\nmsgid \"Updated on\"\nmsgstr \"Mise à jour le\"\n\nmsgid \"Updating the Business Data Model applies changes to both existing database tables and data.\"\nmsgstr \"La mise à jour du Modèle de Données Métier s'applique à la fois aux tables et aux données de base de données existantes.\"\n\nmsgid \"Upload new picture\"\nmsgstr \"Modifier l'image\"\n\nmsgid \"Use Arrow up and Arrow down keys to browse among existing categories.\"\nmsgstr \"Utilisez les flèches haut et bas du clavier pour parcourir la liste des catégories existantes.\"\n\nmsgid \"User id not provided. Unable to retrieve the user details.\"\nmsgstr \"Identifiant utilisateur non fourni. Impossible de récupérer les détails de l'utilisateur.\"\n\nmsgid \"Username\"\nmsgstr \"Nom d'utilisateur\"\n\nmsgid \"Users\"\nmsgstr \"Utilisateurs\"\n\nmsgid \"Users mapped to {}\"\nmsgstr \"Utilisateurs associés à {}\"\n\nmsgid \"Value\"\nmsgstr \"Valeur\"\n\nmsgid \"Version\"\nmsgstr \"Version\"\n\nmsgid \"View application details\"\nmsgstr \"Voir les détails de l'application\"\n\nmsgid \"View task\"\nmsgstr \"Voir la tâche\"\n\nmsgid \"View this task\"\nmsgstr \"Voir cette tâche\"\n\nmsgid \"Warning:\"\nmsgstr \"Attention :\"\n\nmsgid \"We couldn't find what you are looking for.\"\nmsgstr \"Nous n'avons pas trouvé ce que vous recherchez.\"\n\nmsgid \"When you export an application descriptor, pages and profiles are not exported in the file.\"\nmsgstr \"Lorsque vous exportez un descripteur d'application, les pages et profil associés ne sont pas exportés dans le fichier.\"\n\nmsgid \"You are about to export the descriptor of the application\"\nmsgstr \"Vous allez exporter le descripteur de l'application\"\n\nmsgid \"You are going to be redirected to the process list.\"\nmsgstr \"Vous allez être redirigé vers la liste des processus.\"\n\nmsgid \"You can create custom information in Bonita Studio:\"\nmsgstr \"Vous pouvez créer des informations spécifiques dans Bonita Studio :\"\n\nmsgid \"You must add a page before you can reference it in a menu.\"\nmsgstr \"Vous devez ajouter une page avant de pouvoir la référencer dans un menu.\"\n\nmsgid \"Your personal task list is empty. You can take a task from the ​<a href=\\\"#\\\" ng-click=\\\"changeFilter(TASK_FILTERS.TODO)\\\">To do</a> list.\"\nmsgstr \"Votre liste de tâches personnelles est vide. Vous pouvez prendre une tâche dans la liste <a href=\\\"#\\\" ng-click=\\\"changeFilter(TASK_FILTERS.TODO)\\\">A faire</a>.\"\n\nmsgid \"Your session is no longer active.\"\nmsgstr \"Votre session n'est plus active.\"\n\nmsgid \"Zip code\"\nmsgstr \"Code postal\"\n\nmsgid \"Zip file structure error. Check that your .zip contains a well-formed page.properties and either the index.html or the Index.groovy file. For details, see the documentation or the example page readme (available in the custom page list)\"\nmsgstr \"Erreur dans la structure du fichier .zip. Vérifiez que dans votre .zip, le fichier page.properties est bien formé et qu'un fichier index.html ou un fichier Index.groovy est présent. Pour plus d'information, consultez la documentation ou le fichier readme des pages d'exemple (disponibles dans la liste des pages personnalisées)\"\n\nmsgid \"an Application\"\nmsgstr \"une application\"\n\nmsgid \"an application\"\nmsgstr \"une application\"\n\nmsgid \"application descriptor has been partially imported. The following items are not loaded in the Portal.\"\nmsgstr \"descripteur d'application a été partiellement importé. Les composants suivants n'ont pas été ajoutés dans le portail.\"\n\nmsgid \"application descriptor has been successfully imported, with complete page mapping and menu mapping.\"\nmsgstr \"descripteur d'application a été importé avec succès, avec son menu de navigation et les liens vers les pages utilisées.\"\n\nmsgid \"here\"\nmsgstr \"ici\"\n\nmsgid \"of\"\nmsgstr \"de\"\n\nmsgid \"will be permanently deleted.\"\nmsgstr \"va être supprimée définitivement\"\n\nmsgid \"{{firstname}} {{lastname}} already has the membership <b>{{role}} of {{group}}</b>\"\nmsgstr \"{{firstname}} {{lastname}} a déjà l'adhésion <b>{{role}} de {{group}}</b>\"\n\nmsgid \"{{membership.role_id.displayName}} of {{membership.group_id.displayName}}\"\nmsgstr \"{{membership.role_id.displayName}} de {{membership.group_id.displayName}}\"\n\nmsgid \"{{nbErrors}} errors on mapping updates\"\nmsgstr \"La mise à jour des mappings a généré {{nbErrors}} erreurs\"\n\nmsgid \"{{nbMapping}} Process manager mappings could not be updated\"\nmsgstr \"{{nbMapping}} mappings du profil Gestionnaire de processus n'ont pas pu être mis à jour\"\n\nmsgid \"{{nbMapping}} Process manager mappings have been updated\"\nmsgstr \"{{nbMapping}} mappings du profil Gestionnaire de processus ont été mis à jour\"\n\nmsgid \"{{nbOfDeletedCases}} cases have been deleted\"\nmsgstr \"{{nbOfDeletedCases}} cas ont été supprimés avec succès\"\n\nmsgid \"{{nbSucess}} actor mapping updates succeeded\"\nmsgstr \"{{nbSucess}} mappings d'acteurs ont été mis à jour avec succès\"\n\nmsgid \"{{role}} of {{group}}\"\nmsgstr \"{{role}} de {{group}}\"\n\nmsgid \"{{role}} of {{group}} has been created\"\nmsgstr \"{{role}} de {{group}} a été créée\"\n\nmsgid \"{} mapped\"\nmsgstr \"{} associés\"\n\nmsgid \"{} mappings to delete\"\nmsgstr \"{} associés à supprimer\"\n\nmsgid \"{} more\"\nmsgstr \"{} autres\"\n\nmsgid \"{} of {}\"\nmsgstr \"{} de {}\"\n\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/resources/i18n/portal-js_ja.po",
    "content": "msgid \"\"\nmsgstr \"\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Project-Id-Version: bonita\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"X-Crowdin-Project: bonita\\n\"\n\"X-Crowdin-Project-ID: 13316\\n\"\n\"X-Crowdin-Language: ja\\n\"\n\"X-Crowdin-File: /dev/bonita-web/portal/portal-js.pot\\n\"\n\"X-Crowdin-File-ID: 60594\\n\"\n\"Language-Team: Japanese\\n\"\n\"Language: ja_JP\\n\"\n\"PO-Revision-Date: 2024-01-01 00:00\\n\"\n\nmsgid \"1 case has been deleted\"\nmsgstr \"1 案件が削除されました\"\n\nmsgid \"A Link to your application made outside the portal, e.g. with Bonita UI Builder.\"\nmsgstr \"Bonita UI Builderなどのポータル外で作成されたアプリケーションへのリンク.\"\n\nmsgid \"A classic Bonita Living Application.\"\nmsgstr \"クラシックなBonitaリビングアプリケーション.\"\n\nmsgid \"A page with this name already exists.\"\nmsgstr \"この名前のページは既に存在します。\"\n\nmsgid \"A server error occurred. The operation did not complete successfully\"\nmsgstr \"サーバーエラーが発生しました。操作が正常に完了していません。\"\n\nmsgid \"Above normal\"\nmsgstr \"平均以上\"\n\nmsgid \"Access denied. For more information, check the log file.\"\nmsgstr \"アクセスが拒否されました。 詳細についてはログファイルを確認してください。\"\n\nmsgid \"Actions\"\nmsgstr \"アクション\"\n\nmsgid \"Activation state\"\nmsgstr \"有効化の状態\"\n\nmsgid \"Actor name\"\nmsgstr \"アクター名\"\n\nmsgid \"Actors\"\nmsgstr \"アクター\"\n\nmsgid \"Actors must be resolved before enabling the Process.\"\nmsgstr \"アクターは、プロセスを有効にする前に設定する必要があります。\"\n\nmsgid \"Add\"\nmsgstr \"追加\"\n\nmsgid \"Add from custom pages\"\nmsgstr \"カスタム ページを追加します\"\n\nmsgid \"Add membership\"\nmsgstr \"所属を追加\"\n\nmsgid \"Add menu\"\nmsgstr \"メニューを追加します\"\n\nmsgid \"Add menu item\"\nmsgstr \"メニュー項目を追加します\"\n\nmsgid \"Add page\"\nmsgstr \"ページを追加\"\n\nmsgid \"Add pages in Navigation\"\nmsgstr \"ナビゲーションにページを追加します\"\n\nmsgid \"Add top-level menu\"\nmsgstr \"最上位のメニューを追加します\"\n\nmsgid \"Adding on apply\"\nmsgstr \"適用時に追加\"\n\nmsgid \"Address\"\nmsgstr \"住所\"\n\nmsgid \"All\"\nmsgstr \"すべて\"\n\nmsgid \"All done. Good job!\"\nmsgstr \"実行すべきタスクがアサインされていません。\"\n\nmsgid \"An Error occurred during process activation/deactivation.\"\nmsgstr \"プロセスの有効化/無効化中にエラーが発生しました。\"\n\nmsgid \"An Error occurred during process deletion.\"\nmsgstr \"プロセスの削除中にエラーが発生しました。\"\n\nmsgid \"An application is a customized environment for a specific user profile, in which users interact with business data and business processes in the most efficient way.\"\nmsgstr \"アプリケーションは、特定のユーザー プロファイル用にカスタマイズされた環境になり、利用者はビジネス データおよびビジネス プロセスを最も効率的な方法で利用できるようになります。\"\n\nmsgid \"An error has occurred. For more information, check the log file.\"\nmsgstr \"エラーが発生しました。 詳細についてはログファイルを確認してください。\"\n\nmsgid \"An error occurred during categories update\"\nmsgstr \"カテゴリの更新中にエラーが発生しました\"\n\nmsgid \"An error occurred when deploying the BDM. Consult the logs for more information.\"\nmsgstr \"BDMのデプロイ中にエラーが発生しました。\\n\"\n\"詳細についてはログを参照してください。\"\n\nmsgid \"An error occurred while submitting the form.\"\nmsgstr \"フォームの送信中にエラーが発生しました。\"\n\nmsgid \"An error occurred while submitting the form.<br/>The task may not be available anymore.\"\nmsgstr \"フォームの送信中にエラーが発生しました。 <br/>このタスクはもう利用できません。\"\n\nmsgid \"An error occurred with the requested operation.\"\nmsgstr \"要求された操作でエラーが発生しました。\"\n\nmsgid \"Application Link\"\nmsgstr \"アプリケーションリンク\"\n\nmsgid \"Application XML file\"\nmsgstr \"アプリケーションのXMLファイル\"\n\nmsgid \"Application id not provided. Unable to retrieve the application details.\"\nmsgstr \"アプリケーション ID が提供されていません。アプリケーションの詳細を取得できません。\"\n\nmsgid \"Application list\"\nmsgstr \"アプリケーション リスト\"\n\nmsgid \"Apply\"\nmsgstr \"適用\"\n\nmsgid \"Archived cases\"\nmsgstr \"完了済み案件\"\n\nmsgid \"Are you sure you want to delete it?\"\nmsgstr \"削除してよろしいですか？\"\n\nmsgid \"Are you sure you want to delete this membership ?\"\nmsgstr \"この所属を削除してもよろしいですか？\"\n\nmsgid \"Assign to me. Only I will be able to do it\"\nmsgstr \"自分にアサイン。それを実行できるのは、自分だけになります。\"\n\nmsgid \"Assigned on\"\nmsgstr \"アサイン日時\"\n\nmsgid \"Available or My tasks\"\nmsgstr \"このマークはあなただけに指名されたタスクを示します\"\n\nmsgid \"BDM access control file can now be installed.\"\nmsgstr \"BDM のアクセス コントロール ファイルをインストールできるようになりました。\"\n\nmsgid \"BDM file\"\nmsgstr \"BDM ファイル\"\n\nmsgid \"BDM has not been uploaded. System was not able to upload the provided BDM file for technical reasons. You can try again later.\"\nmsgstr \"BDMはアップロードされていません。\\n\"\n\"技術的な理由によりシステムは指定されたBDMファイルをアップロードできませんでした。\\n\"\n\"後ほど再度お試しください。\"\n\nmsgid \"BDM successfully updated.\"\nmsgstr \"BDMは正常に更新されました。\"\n\nmsgid \"Back\"\nmsgstr \"戻る\"\n\nmsgid \"Before updating the BDM, access control file should be deleted.\"\nmsgstr \"BDMを更新する前にアクセス コントロールファイルを削除する必要があります。\"\n\nmsgid \"Before you import the application descriptor, the pages and the profile must already be loaded in the Portal.\"\nmsgstr \"アプリケーション記述子をインポートする前に、ページとプロファイルは、ポータル内に既に読み込まれている必要があります。\"\n\nmsgid \"Business Data Model definition\"\nmsgstr \"ビジネス データ モデル定義\"\n\nmsgid \"Business card\"\nmsgstr \"業務上の連絡先\"\n\nmsgid \"Business card has not been updated. Please retry later or contact an administrator\"\nmsgstr \"業務上の連絡先が、更新されませんでした。後で再試行するか管理者に問い合わせてください。 \"\n\nmsgid \"Business card successfully updated\"\nmsgstr \"業務上の連絡先が正常に更新されました。\"\n\nmsgid \"Can't import page or form to the process\"\nmsgstr \"プロセスにページまたはフォームをインポートできません\"\n\nmsgid \"Can't update expression content\"\nmsgstr \"式のコンテンツを更新できません\"\n\nmsgid \"Cancel\"\nmsgstr \"キャンセル\"\n\nmsgid \"Cancelled, skipped instances\"\nmsgstr \"キャンセル/スキップ\"\n\nmsgid \"Cannot upload the file\"\nmsgstr \"ファイルをアップロードできません\"\n\nmsgid \"Case\"\nmsgstr \"案件\"\n\nmsgid \"Case ID\"\nmsgstr \"案件ID\"\n\nmsgid \"Case comments\"\nmsgstr \"案件コメント\"\n\nmsgid \"Case id: {{caseId}} - Process: {{processName}} ({{processVersion}})\"\nmsgstr \"案件ID: {{caseId}} - プロセス: {{processName}} ({{processVersion}})\"\n\nmsgid \"Case overview\"\nmsgstr \"案件概況\"\n\nmsgid \"Case start\"\nmsgstr \"案件の開始\"\n\nmsgid \"Cases with failures\"\nmsgstr \"ケース エラー\"\n\nmsgid \"Categories\"\nmsgstr \"カテゴリ\"\n\nmsgid \"Check that each entity is mapped to a profile, for either Bonita Portal or for applications you have created.\"\nmsgstr \"各エンティティが Bonita ポータル、またはあなたが作成したアプリケーションのいずれかのプロファイルにマップされていることを確認します。\"\n\nmsgid \"City\"\nmsgstr \"市区町村\"\n\nmsgid \"Class name\"\nmsgstr \"クラス名\"\n\nmsgid \"Click here to choose the .xml file\"\nmsgstr \".xml ファイルを選択するにはここをクリック\"\n\nmsgid \"Click here to choose your .zip file\"\nmsgstr \".zip  ファイルを選択しクリックしてください。\"\n\nmsgid \"Click on Cancel to remain on this page and try to execute the operation again once you logged back in (e.g. in another tab).\"\nmsgstr \"「キャンセル」をクリックすると、このページが表示されたままとなり、ログインし直した後（別のタブなどで）、再度操作を実行することができます。\"\n\nmsgid \"Click on Cancel to remain on this page and wait for the maintenance to end.\"\nmsgstr \"キャンセルをクリックしてこのページに留まり、メンテナンスが終了するのを待ちます。\"\n\nmsgid \"Click on OK to be redirected and log back in.\"\nmsgstr \"OKをクリックするとリダイレクトされ、再度ログインします。\"\n\nmsgid \"Click on OK to be redirected to the maintenance page.\"\nmsgstr \"OKをクリックすると、メンテナンス ページにリダイレクトされます。\"\n\nmsgid \"Close\"\nmsgstr \"閉じる\"\n\nmsgid \"Columns selection\"\nmsgstr \"列の選択\"\n\nmsgid \"Comment:\"\nmsgstr \"コメント：\"\n\nmsgid \"Comments\"\nmsgstr \"コメント\"\n\nmsgid \"Compilation failure. Verify that the Index.groovy class implements the PageController interface.\"\nmsgstr \"コンパイルに失敗しました。Index.groovy クラスが PageController インターフェイスを実装することを確認します。\"\n\nmsgid \"Completed instances\"\nmsgstr \"実行完了\"\n\nmsgid \"Configuration state\"\nmsgstr \"構成設定の状態\"\n\nmsgid \"Confirm new Password\"\nmsgstr \"新しいパスワードを確認\"\n\nmsgid \"Connectors\"\nmsgstr \"コネクタ\"\n\nmsgid \"Connectors must be resolved before enabling the Process.\"\nmsgstr \"コネクタは、プロセスを有効にする前に設定する必要があります。\"\n\nmsgid \"Core to Bonita platform\"\nmsgstr \"Bonita プラットフォームのコア\"\n\nmsgid \"Core to Bonita platform; cannot be deleted\"\nmsgstr \"Bonita プラットフォームのコア; 削除できません\"\n\nmsgid \"Country\"\nmsgstr \"国\"\n\nmsgid \"Create a new membership\"\nmsgstr \"新しいメンバーシップを作成します\"\n\nmsgid \"Create an application\"\nmsgstr \"アプリケーションを作成します\"\n\nmsgid \"Created by\"\nmsgstr \"作成者\"\n\nmsgid \"Creation\"\nmsgstr \"作成\"\n\nmsgid \"Creation date\"\nmsgstr \"作成日時\"\n\nmsgid \"Creation on\"\nmsgstr \"作成対象：\"\n\nmsgid \"Custom information\"\nmsgstr \"カスタム情報\"\n\nmsgid \"Custom information has not been updated. Please retry later or contact an administrator\"\nmsgstr \"カスタム情報が更新されませんでした。後で再試行するか管理者に問い合わせてください。\"\n\nmsgid \"Custom information successfully updated\"\nmsgstr \"カスタム情報が正常に更新されました\"\n\nmsgid \"Delete\"\nmsgstr \"削除\"\n\nmsgid \"Delete application\"\nmsgstr \"アプリケーションを削除\"\n\nmsgid \"Delete membership\"\nmsgstr \"所属を削除しますか？\"\n\nmsgid \"Delete process\"\nmsgstr \"プロセスを削除\"\n\nmsgid \"Deleting a process definition will also delete all open and archived cases of this process.\"\nmsgstr \"プロセス定義を削除すると、このプロセスのオープンおよびアーカイブされたすべてのケースも削除されます。\"\n\nmsgid \"Description\"\nmsgstr \"説明\"\n\nmsgid \"Disable\"\nmsgstr \"無効にする\"\n\nmsgid \"Display name\"\nmsgstr \"表示名\"\n\nmsgid \"Done tasks\"\nmsgstr \"完了したタスク\"\n\nmsgid \"Drawing process...\"\nmsgstr \"プロセス図を描画中 ...\"\n\nmsgid \"Due date\"\nmsgstr \"期限\"\n\nmsgid \"Edit\"\nmsgstr \"編集\"\n\nmsgid \"Edit menu\"\nmsgstr \"メニューを編集します\"\n\nmsgid \"Edit menu item\"\nmsgstr \"メニュー項目を編集\"\n\nmsgid \"Email\"\nmsgstr \"Eメール\"\n\nmsgid \"Enable\"\nmsgstr \"有効にする\"\n\nmsgid \"Enable all\"\nmsgstr \"すべて有効にする\"\n\nmsgid \"Enter a new category name and click \\\"Add\\\" button to create a new category.\"\nmsgstr \"新しいカテゴリ名を入力し、「追加」ボタンをクリックして新しいカテゴリを作成します。\"\n\nmsgid \"Enter the user to start the case for...\"\nmsgstr \"案件を開始するユーザーを入力...\"\n\nmsgid \"Error\"\nmsgstr \"エラー\"\n\nmsgid \"Error on drawing process!\"\nmsgstr \"プロセス図の描画中にエラーが発生しました！\"\n\nmsgid \"Error while trying to start the case. Some required information is missing (contract not fulfilled).\"\nmsgstr \"案件を開始しようとしてエラーが発生しました。一部の必須情報がありません （コントラクトのデータが満たされていない）。\"\n\nmsgid \"Error!\"\nmsgstr \"エラー！\"\n\nmsgid \"Error: value must be a boolean\"\nmsgstr \"エラー: 値はブール値である必要があります\"\n\nmsgid \"Error: value must be a double\"\nmsgstr \"エラー: 値は double 型の値である必要があります\"\n\nmsgid \"Error: value must be an integer\"\nmsgstr \"エラー: 値は整数でなければなりません\"\n\nmsgid \"Executing, completing, initializing instances\"\nmsgstr \"初期化中, 実行中, 完了途中\"\n\nmsgid \"Export\"\nmsgstr \"エクスポート\"\n\nmsgid \"Export application descriptor\"\nmsgstr \"アプリケーション記述子をエクスポート\"\n\nmsgid \"Expression content has been updated\"\nmsgstr \"式のコンテンツが更新されました\"\n\nmsgid \"Failed instances\"\nmsgstr \"エラーがあった\"\n\nmsgid \"File format may be invalid. For more information, check the log file.\"\nmsgstr \"ファイル形式が無効である可能性があります。 詳細についてはログ ファイルを確認してください。\"\n\nmsgid \"Filename too long. The zip filename must be no longer than 50 characters\"\nmsgstr \"ファイル名が長すぎます。Zip ファイル名は 50 文字以内である必要があります。\"\n\nmsgid \"Filters\"\nmsgstr \"フィルター\"\n\nmsgid \"First name\"\nmsgstr \"姓（英語の場合：First name）\"\n\nmsgid \"Form\"\nmsgstr \"フォーム\"\n\nmsgid \"Form submitted.<br/>The next task in the list is now selected.\"\nmsgstr \"フォームを送信しました。 <br/>リスト中の次のタスクが選択されました。\"\n\nmsgid \"General\"\nmsgstr \"全般\"\n\nmsgid \"General information\"\nmsgstr \"全般的な情報\"\n\nmsgid \"General information has not been updated. Please retry later or contact an administrator\"\nmsgstr \"全般的な情報が更新されませんでした。後で再試行するかシステム管理者に問い合わせてください。\"\n\nmsgid \"General information successfully updated\"\nmsgstr \"全般的な情報が正常に更新されました\"\n\nmsgid \"Groups\"\nmsgstr \"グループ\"\n\nmsgid \"Healthy cases\"\nmsgstr \"正常なケース\"\n\nmsgid \"Hide Menu\"\nmsgstr \"メニューを非表示\"\n\nmsgid \"Hide menu\"\nmsgstr \"メニューを非表示\"\n\nmsgid \"Hide panel\"\nmsgstr \"パネルを非表示\"\n\nmsgid \"Highest\"\nmsgstr \"最高\"\n\nmsgid \"However, due to the use of Hibernate for data persistence, some changes on the data may not be well handled. These changes may lead to a corrupted database and CANNOT BE REVERTED.\"\nmsgstr \"ただし、データの永続化にHibernateを使用するため、データの一部の変更はうまく処理されない可能性があります。 これらの変更により、データベースが破損する可能性があり、元に戻すことはできません。\"\n\nmsgid \"I have read and understood the previous warning message. I have prepared a database backup so I can rollback this operation.\"\nmsgstr \"以前の警告メッセージを読んで理解しました。 この操作をロールバックできるように、データベースのバックアップを準備しました。\"\n\nmsgid \"IN ANY CASE, you MUST CREATE A BACKUP of the current BDM database before undergoing a BDM update.\"\nmsgstr \"いずれの場合も、BDMの更新を行う前に現在のBDMデータベースのバックアップを作成する必要があります。\"\n\nmsgid \"If your changes do not fall into those change types, you can go ahead and proceed with the BDM update now.\"\nmsgstr \"変更がこれらの変更タイプに該当しない場合は、先に進んみ今すぐBDMアップデートを継続することができます。\"\n\nmsgid \"Implementation version\"\nmsgstr \"実装のバージョン\"\n\nmsgid \"Import\"\nmsgstr \"インポート\"\n\nmsgid \"Import an application\"\nmsgstr \"アプリケーションをインポート\"\n\nmsgid \"Import an application descriptor\"\nmsgstr \"アプリケーション記述子をインポート\"\n\nmsgid \"In a production environment, this should be used with caution.\"\nmsgstr \"本番環境では、これは慎重に使用する必要があります。\"\n\nmsgid \"In such cases, you must CANCEL THIS UPDATE and let the database administrator carefully script the changes and apply them to the database. Only then will you be able to update the BDM from this page.\"\nmsgstr \"このような場合、このアップデートをキャンセルし、データベース管理者に注意深く変更をスクリプトして、それらをデータベースに適用させなければなりません。 その時のみ、このページからBDMを更新することができます。\"\n\nmsgid \"In task name column\"\nmsgstr \"[タスク名] 列\"\n\nmsgid \"In the menu <i>Organization > Manage</i>, go to the <i>Users</i> page of the wizard\"\nmsgstr \"メニューの <i>組織 > 管理</i>で、 <i>ユーザー</i> のウィザード ページに進む\"\n\nmsgid \"Install\"\nmsgstr \"インストール\"\n\nmsgid \"Install Business Data Model\"\nmsgstr \"ビジネス データ モデルのインストール\"\n\nmsgid \"Install a BDM file\"\nmsgstr \"BDM ファイルのインストール\"\n\nmsgid \"Installed\"\nmsgstr \"インストール済み\"\n\nmsgid \"Installed by\"\nmsgstr \"インストール者 \"\n\nmsgid \"Installed on\"\nmsgstr \"インストール日時 \"\n\nmsgid \"Installing\"\nmsgstr \"インストール中 \"\n\nmsgid \"Installing Business Data Model\"\nmsgstr \"ビジネス データ モデルのインストール \"\n\nmsgid \"Installing or updating the BDM implies to be logged in as Technical User, and to activate the maintenance mode.\"\nmsgstr \"BDM をインストールまたはアップデートするには、テクニカルユーザーとしてログインし、メンテナンスモードを有効にする必要があります。\"\n\nmsgid \"Items per page\"\nmsgstr \"ページあたりの表示件数\"\n\nmsgid \"Job title\"\nmsgstr \"役職名\"\n\nmsgid \"Last login\"\nmsgstr \"最終ログイン日時\"\n\nmsgid \"Last name\"\nmsgstr \"名（英語の場合：Last name）\"\n\nmsgid \"Last update\"\nmsgstr \"最新更新日時\"\n\nmsgid \"Layout\"\nmsgstr \"レイアウト\"\n\nmsgid \"Legacy Application\"\nmsgstr \"従来のアプリケーション\"\n\nmsgid \"Loading...\"\nmsgstr \"ロード中...\"\n\nmsgid \"Logo\"\nmsgstr \"ロゴ\"\n\nmsgid \"Look & Feel\"\nmsgstr \"ルック＆フィール\"\n\nmsgid \"Lowest\"\nmsgstr \"最低\"\n\nmsgid \"MM/dd/yyyy h:mm a\"\nmsgstr \"yyyy/MM/dd HH:mm\"\n\nmsgid \"MMM DD LT\"\nmsgstr \"MMM Do LT \"\n\nmsgid \"Make sure all artifacts enabled on the platform are compatible with the new BDM.\"\nmsgstr \"プラットフォームで有効にされているすべてのアーティファクトが新しいBDMと互換性があることを確認してください。 \"\n\nmsgid \"Make sure to deactivate the maintenance mode.\"\nmsgstr \"メンテナンスモードを無効にしてください。\"\n\nmsgid \"Make this task available to other team members again\"\nmsgstr \"このタスクは他のチーム メンバーでも行えるようにする\"\n\nmsgid \"Make this task personal: only I will be able to do it\"\nmsgstr \"このタスクは自分だけが行えるようにする\"\n\nmsgid \"Manage Categories\"\nmsgstr \"カテゴリを管理\"\n\nmsgid \"Manager\"\nmsgstr \"マネージャ\"\n\nmsgid \"Mapped categories\"\nmsgstr \"マップされたカテゴリ\"\n\nmsgid \"Maximum size 255 characters\"\nmsgstr \"最大 255 文字\"\n\nmsgid \"Membership has not been added. Please retry later or contact an administrator\"\nmsgstr \"所属が追加されませんでした。後で再試行するか管理者に問い合わせてください。 \"\n\nmsgid \"Membership has not been removed. Please retry later or contact an administrator\"\nmsgstr \"所属が削除されませんでした。後で再試行するか管理者に問い合わせてください。 \"\n\nmsgid \"Membership successfully added\"\nmsgstr \"所属が正常に追加されました\"\n\nmsgid \"Membership successfully removed\"\nmsgstr \"所属が正常に削除されました\"\n\nmsgid \"Memberships\"\nmsgstr \"所属\"\n\nmsgid \"Memberships mapped to {}\"\nmsgstr \"{} にマップされたメンバーシップ\"\n\nmsgid \"Mobile\"\nmsgstr \"携帯番号\"\n\nmsgid \"Modifying or redeploying the Business Data Model may impact existing data retention rules. After redeployment, review your configuration in the Data Retention (GDPR) section.\"\nmsgstr \"\"\n\nmsgid \"Monitoring status\"\nmsgstr \"進捗状況のモニタリング \"\n\nmsgid \"Multi-page menu\"\nmsgstr \"複数ページ メニュー\"\n\nmsgid \"My tasks\"\nmsgstr \"マイタスク\"\n\nmsgid \"Name\"\nmsgstr \"名前\"\n\nmsgid \"Navigation\"\nmsgstr \"ナビゲーション\"\n\nmsgid \"New\"\nmsgstr \"新規\"\n\nmsgid \"New Password\"\nmsgstr \"新規パスワード\"\n\nmsgid \"New comment\"\nmsgstr \"新しいコメント\"\n\nmsgid \"No application available.\"\nmsgstr \"利用可能なアプリケーションはありません。\"\n\nmsgid \"No application found with id:\"\nmsgstr \"ID付きのアプリケーションが見つかりません:\"\n\nmsgid \"No comment on this case yet. To add one, use the input field at the bottom of this panel\"\nmsgstr \"まだこの案件に対するコメントがありません。追加するには、このパネルの下部にあるコメント入力フィールドを使用します。\"\n\nmsgid \"No custom information defined.\"\nmsgstr \"カスタムの情報が定義されていません。\"\n\nmsgid \"No data\"\nmsgstr \"データがありません。\"\n\nmsgid \"No description.\"\nmsgstr \"説明がありません。\"\n\nmsgid \"No done task yet.\"\nmsgstr \"まだ完了したタスクはありません。\"\n\nmsgid \"No form is needed. You can enter a comment and confirm.\"\nmsgstr \"フォームは不要です。コメントを入力し、確認することができます。\"\n\nmsgid \"No mapping\"\nmsgstr \"マッピングなし\"\n\nmsgid \"No process found with id:\"\nmsgstr \"IDt付プロセスが見つかりません:\"\n\nmsgid \"No profile\"\nmsgstr \"プロファイルがありません\"\n\nmsgid \"No profile mapped to this application\"\nmsgstr \"このアプリケーションにマップされているプロファイルはありません。\"\n\nmsgid \"No updates have been made on categories. For more details, check logs.\"\nmsgstr \"カテゴリに関する更新はありません。詳細についてはログを確認してください。\"\n\nmsgid \"No user found with id:\"\nmsgstr \"ID付ユーザーが見つかりません:\"\n\nmsgid \"Normal\"\nmsgstr \"普通\"\n\nmsgid \"Not installed\"\nmsgstr \"インストールされていません。\"\n\nmsgid \"OK\"\nmsgstr \"OK\"\n\nmsgid \"One or more tasks are not available anymore. The list is now up to date.\"\nmsgstr \"1つ以上のタスクは、もうご使用いただけません。タスク一覧が新しくなっています。\"\n\nmsgid \"One-page menu\"\nmsgstr \"1 ページ メニュー\"\n\nmsgid \"Only I can do this task\"\nmsgstr \"私だけがこのタスクを実行できる\"\n\nmsgid \"Only available when the platform is under maintenance. Go to the \\\"Platform maintenance\\\" page to activate the maintenance mode.\"\nmsgstr \"プラットフォームがメンテナンス中の場合のみ利用可能です。「プラットフォームのメンテナンス」ページでメンテナンスモードを有効にしてください。\"\n\nmsgid \"Only one Business Data Model (BDM) can be active at a time.\"\nmsgstr \"一度にアクティブにできるビジネスデータモデル（BDM）は1つだけです。\"\n\nmsgid \"Open cases\"\nmsgstr \"進行中案件\"\n\nmsgid \"Open in a popup\"\nmsgstr \"ポップアップで開く\"\n\nmsgid \"Open the task to perform it\"\nmsgstr \"実行するタスクを開く\"\n\nmsgid \"Overview\"\nmsgstr \"案件概況\"\n\nmsgid \"Page\"\nmsgstr \"ページ\"\n\nmsgid \"Pages\"\nmsgstr \"ページ\"\n\nmsgid \"Parameters\"\nmsgstr \"パラメータ\"\n\nmsgid \"Parameters must be resolved before enabling the Process.\"\nmsgstr \"プロセスを有効にする前にパラメータを解決する必要があります。\"\n\nmsgid \"Password\"\nmsgstr \"パスワード\"\n\nmsgid \"Password has not been updated. Please retry later or contact an administrator\"\nmsgstr \"パスワードが更新されませんでした。後で再試行するか管理者に問い合わせてください。\"\n\nmsgid \"Password successfully updated\"\nmsgstr \"パスワードが正常に更新されました\"\n\nmsgid \"Passwords don't match\"\nmsgstr \"パスワードが一致しません\"\n\nmsgid \"Personal information\"\nmsgstr \"個人情報\"\n\nmsgid \"Personal information has not been updated. Please retry later or contact an administrator\"\nmsgstr \"個人情報は更新されませんでした。後で再試行するか管理者に問い合わせてください。\"\n\nmsgid \"Personal information successfully updated\"\nmsgstr \"個人情報が正常に更新されました\"\n\nmsgid \"Phone\"\nmsgstr \"電話番号\"\n\nmsgid \"Please refresh the page.\"\nmsgstr \"ページを更新してください。\"\n\nmsgid \"Priority\"\nmsgstr \"優先度\"\n\nmsgid \"Process\"\nmsgstr \"プロセス\"\n\nmsgid \"Process id\"\nmsgstr \"プロセス ID\"\n\nmsgid \"Process id not provided. Unable to retrieve the process details.\"\nmsgstr \"プロセスID が提供されていません。プロセスの詳細を取得できません。\"\n\nmsgid \"Process name\"\nmsgstr \"プロセス名\"\n\nmsgid \"Process:\"\nmsgstr \"プロセス:\"\n\nmsgid \"Profile\"\nmsgstr \"プロファイル\"\n\nmsgid \"Profiles\"\nmsgstr \"プロファイル\"\n\nmsgid \"Profiles / Memberships\"\nmsgstr \"プロファイル /  所属\"\n\nmsgid \"Ready, waiting instances\"\nmsgstr \"作業開始を待機中\"\n\nmsgid \"Refresh data\"\nmsgstr \"データを再読み込み\"\n\nmsgid \"Release\"\nmsgstr \"手放す\"\n\nmsgid \"Remove\"\nmsgstr \"削除\"\n\nmsgid \"Remove all\"\nmsgstr \"すべて削除\"\n\nmsgid \"Removing on apply\"\nmsgstr \"適用時の削除\"\n\nmsgid \"Reset\"\nmsgstr \"リセット\"\n\nmsgid \"Reset to default\"\nmsgstr \"初期設定にリセットする\"\n\nmsgid \"Roles\"\nmsgstr \"役割\"\n\nmsgid \"Save\"\nmsgstr \"保存\"\n\nmsgid \"Search...\"\nmsgstr \"検索...\"\n\nmsgid \"Select a group...\"\nmsgstr \"グループを選択...\"\n\nmsgid \"Select a role...\"\nmsgstr \"役割を選択...\"\n\nmsgid \"Select all\"\nmsgstr \"すべて選択\"\n\nmsgid \"Select groups...\"\nmsgstr \"グループを選択...\"\n\nmsgid \"Select none\"\nmsgstr \"選択を解除\"\n\nmsgid \"Select roles...\"\nmsgstr \"役割を選択...\"\n\nmsgid \"Select the <i>User information management</i> tab to add <i>Custom information</i> on the right\"\nmsgstr \" <i>ユーザー情報管理</i>タブを選択し、右側の <i>カスタム情報</i> を追加します\"\n\nmsgid \"Select the entities (users, groups, roles, memberships) to map to the actors. These entities will do the human tasks in the process.\"\nmsgstr \"アクターにアサインするエンティティ（ユーザー、グループ、役割、所属など）を選択します。これらのエンティティは、プロセスにヒューマン タスクを実行します。\"\n\nmsgid \"Select the page\"\nmsgstr \"ページを選択します\"\n\nmsgid \"Select users...\"\nmsgstr \"ユーザーを選択...\"\n\nmsgid \"Server is under maintenance.\"\nmsgstr \"サーバーはメンテナンス中です。\"\n\nmsgid \"Set as Home page\"\nmsgstr \"ホーム ページとして設定します\"\n\nmsgid \"Show Menu\"\nmsgstr \"メニューを表示\"\n\nmsgid \"Show all the tasks I can do: both personal tasks and tasks available to other team members\"\nmsgstr \"自分が行うことができるすべてのタスクを表示する： 自分だけにアサインされたタスクおよび他のチーム メンバーでも行えるタスクの両方を表示\"\n\nmsgid \"Show menu\"\nmsgstr \"メニューを表示\"\n\nmsgid \"Show my personal tasks: the ones I took and the ones assigned to me, automatically or by a process manager\"\nmsgstr \"自分だけにアサインされたタスクを表示する： 自分自身で引き受けたもの、自動的、またはプロセス管理者によってアサインされたもの\"\n\nmsgid \"Show panel\"\nmsgstr \"パネルを表示\"\n\nmsgid \"Show the tasks that I have done\"\nmsgstr \"自分が関与し実行したタスクを表示\"\n\nmsgid \"Something went wrong during the deletion. You might want to cancel and try again.\"\nmsgstr \"削除中に問題が発生しました。 キャンセルして再試行してください。\"\n\nmsgid \"Something went wrong during the modification. You might want to cancel and try again.\"\nmsgstr \"変更中に問題が発生しました。キャンセルして再試行してください。\"\n\nmsgid \"Something went wrong. You might want to cancel and try again.\"\nmsgstr \"問題が発生しました。キャンセルして再試行してください。\"\n\nmsgid \"Sort by URL\"\nmsgstr \"URL で並べ替え\"\n\nmsgid \"Sort by name\"\nmsgstr \"名前で並べ替え\"\n\nmsgid \"Sort by type\"\nmsgstr \"種類で並び替え\"\n\nmsgid \"Sort by updated on\"\nmsgstr \"更新日で並べ替え\"\n\nmsgid \"Sort by version\"\nmsgstr \"バージョンで並び替え\"\n\nmsgid \"Specify the menu structure.\"\nmsgstr \"メニュー構造を指定します\"\n\nmsgid \"State\"\nmsgstr \"状態\"\n\nmsgid \"Status\"\nmsgstr \"ステータス\"\n\nmsgid \"Status of importation\"\nmsgstr \"インポートの状態\"\n\nmsgid \"Still, you can customize its look & feel on this page. You can also update some of its pages in the Resources menu.\"\nmsgstr \"それでも、このページでそのルック＆フィールをカスタマイズすることができます。[リソース]メニューのいくつかのページを更新することもできます。\"\n\nmsgid \"Submit\"\nmsgstr \"送信\"\n\nmsgid \"Successfully updated categories\"\nmsgstr \"正常に更新されたカテゴリ\"\n\nmsgid \"Table settings\"\nmsgstr \"テーブルの設定\"\n\nmsgid \"Take\"\nmsgstr \"引き受ける\"\n\nmsgid \"Task Id\"\nmsgstr \"タスクID\"\n\nmsgid \"Task form\"\nmsgstr \"タスク フォーム\"\n\nmsgid \"Task list\"\nmsgstr \"タスク リスト\"\n\nmsgid \"Task name\"\nmsgstr \"タスク名\"\n\nmsgid \"Technical User\"\nmsgstr \"テクニカル ユーザー\"\n\nmsgid \"Technical error.\"\nmsgstr \"テクニカルなエラーが発生。\"\n\nmsgid \"The BDM schema and data have been rolled-back to what they were before this update attempt.\"\nmsgstr \"BDM スキーマとデータは、この更新試行される前のものにロールバックされています。\"\n\nmsgid \"The application\"\nmsgstr \"アプリケーション\"\n\nmsgid \"The application does not exist. Go to application list page to see the new list of applications.\"\nmsgstr \"アプリケーションが存在しません。アプリケーションのリストページに移動して、アプリケーションの新しいリストを表示します。\"\n\nmsgid \"The application does not exist. Reload the page to see the new list of applications.\"\nmsgstr \"アプリケーションが存在しません。 ページをリロードして、アプリケーションの新しいリストを表示します。\"\n\nmsgid \"The application page does not exist. Reload the page to see the new list of application pages.\"\nmsgstr \"アプリケーションページが存在しません。 ページをリロードして、アプリケーションページの新しいリストを表示します。\"\n\nmsgid \"The attachment is too big.<br/>Select a smaller attachment and submit the form again.\"\nmsgstr \"添付ファイルが大きすぎます。 <br/>小さい添付ファイルを選択して、フォームを再度送信してください。\"\n\nmsgid \"The business data: [ {} ] uses Business Objects which are not defined in the current Business Data model. Deploy a compatible Business Data model before enabling the process.\"\nmsgstr \"ビジネス データ:  [ {} ]  は、現在のビジネス データ モデル内に定義されていないビジネス オブジェクトを使用しています。プロセスを有効にする前に互換性のあるビジネス データ モデルをデプロイしてください。\"\n\nmsgid \"The case is archived. You cannot add comments anymore\"\nmsgstr \"この案件は完了しています。もうコメントを追加することはできません。\"\n\nmsgid \"The case {{caseId}} has been started successfully.\"\nmsgstr \"案件ID： %caseId% が正常に開始されました。\"\n\nmsgid \"The category cannot be removed and added at the same time.\"\nmsgstr \"カテゴリーの削除と追加を同時に行うことはできません。\"\n\nmsgid \"The current process has no connectors defined.\"\nmsgstr \"現在のプロセスには、コネクタが定義されていません。\"\n\nmsgid \"The current process has no parameters defined.\"\nmsgstr \"現在のプロセスには、パラメータが定義されていません。\"\n\nmsgid \"The file you are trying to upload has the wrong mime type\"\nmsgstr \"アップロードしようとしているファイルのmimeタイプが間違っています\"\n\nmsgid \"The form mappings must be resolved before enabling the Process.\"\nmsgstr \"プロセスを有効にする前にフォームの マッピングを解決する必要があります。\"\n\nmsgid \"The image you are trying to upload is too big\"\nmsgstr \"アップロードしようとしている画像が大きすぎます\"\n\nmsgid \"The item does not exist anymore. Refresh the page to see the new status\"\nmsgstr \"アイテムは既に存在していません。新しいステータスを表示するにはページを更新してください\"\n\nmsgid \"The menu does not exist. Reload the page to see the new list of menus.\"\nmsgstr \"メニューは存在しません。 ページをリロードしてメニューの新しいリストを表示します。\"\n\nmsgid \"The name for the URL must start with custompage_ followed only by alphanumeric characters.\"\nmsgstr \"URL の名前は、英数字のみが続く custompage_ で始まらなければなりません。\"\n\nmsgid \"The task has been submitted but an error occurred while adding the comment\"\nmsgstr \"タスクが送信されましたが、コメントを追加するときにエラーが発生しました。\"\n\nmsgid \"The whole list of such changes is listed <a ng-href=\\\"{{documentationUrl}}\\\" target=\\\"_blank\\\" translate>here</a>.\"\nmsgstr \"このような変更の一覧は <a ng-href=\\\"{{documentationUrl}}\\\" target=\\\"_blank\\\" translate>こちら</a>です。\"\n\nmsgid \"The words 'content', 'API' and 'theme' are reserved for internal use. They must not be used in an application or page URL.\"\nmsgstr \"'content'、'API' 、 'theme' の単語は、内部使用のため予約されています。アプリケーション、またはページの URL にそれらを使用してはいけません。\"\n\nmsgid \"Theme\"\nmsgstr \"テーマ\"\n\nmsgid \"Then for each user, fill out the custom information value, in the Studio (<i>List of users</i> tab of the organization), or right on this Portal page.\"\nmsgstr \"Studioの組織の <i>ユーザーリスト</i> タブ、またはこのポータル ページの右側で、各ユーザーのカスタム情報の値を入力します。\"\n\nmsgid \"They will be removed from the database permanently.\"\nmsgstr \"それらは完全にデータベースから削除されます。\"\n\nmsgid \"This URL is already in use\"\nmsgstr \"この URL は、既に使用中\"\n\nmsgid \"This application is core to the Bonita platform. To keep it working as it should, it must remain available and its menu should stay as it is.\"\nmsgstr \"このアプリケーションは、Bonitaプラットフォームの中核です。 正常に機能し続けるには、使用可能な状態を維持し、メニューをそのままにしておく必要があります。\"\n\nmsgid \"This category has already been added to this process.\"\nmsgstr \"このカテゴリは既にこのプロセスに追加されています。\"\n\nmsgid \"This field is mandatory\"\nmsgstr \"このフィールドは必須です\"\n\nmsgid \"This is the task details panel. No information to display\"\nmsgstr \"これは、タスクの詳細パネルです。表示する情報がありません。\"\n\nmsgid \"This name is already in use\"\nmsgstr \"この名前は既に使用中\"\n\nmsgid \"This only changes the application link. The application itself is still deployed at the same location.\"\nmsgstr \"これはアプリケーションのリンクを変更するだけです。アプリケーション自体は同じ場所にデプロイされます。\"\n\nmsgid \"This only deletes the application link. The application itself is not deleted and is still deployed.\"\nmsgstr \"アプリケーションのリンクのみが削除されます。アプリケーション自体は削除されず、デプロイされたままです。\"\n\nmsgid \"This process is no longer available.\"\nmsgstr \"このプロセスは既に使用できません。\"\n\nmsgid \"This process is not fully configured, but it is still enabled. Users may still be able to start cases or do tasks, which may lead to errors.\"\nmsgstr \"このプロセスは完全には設定されていませんが有効です。 \\n\"\n\"ユーザーは引き続きケースを開始したりタスクを実行できますがエラーになる可能性があります。\"\n\nmsgid \"This process is not fully configured.\"\nmsgstr \"このプロセスは完全に設定されていません。\"\n\nmsgid \"This process is not fully configured. It cannot be enabled.\"\nmsgstr \"このプロセスは完全い設定されてないので有効ではありません。\"\n\nmsgid \"This task cannot be executed from here.\"\nmsgstr \"このタスクはここから実行できません。\"\n\nmsgid \"This task is completed.\"\nmsgstr \"このタスクは完了しました。\"\n\nmsgid \"This task is not available anymore. The list is now up to date.\"\nmsgstr \"1 つまたは複数のタスクは、もうご使用いただけません。タスク一覧が新しくなっています。\"\n\nmsgid \"This task is overdue. It was supposed to be completed by {{dueDate}}\"\nmsgstr \"このタスクは期限切れです。 {{dueDate}} までに完了する予定でした。\"\n\nmsgid \"Title\"\nmsgstr \"タイトル（性別）\"\n\nmsgid \"To create an application, click New or Import.\"\nmsgstr \"アプリケーションを作成するには、新規またはインポート をクリックします。\"\n\nmsgid \"To do\"\nmsgstr \"To do\"\n\nmsgid \"To do so, go to\"\nmsgstr \"これを行うには、次へ進む: \"\n\nmsgid \"To do so, log out and then log back in as the Technical User, whose credentials are set upon Bonita platform installation.\"\nmsgstr \"そのためにログアウトし、Bonita プラットフォーム のインストール時に設定されたテクニカル ユーザーの資格で再度ログインします。\"\n\nmsgid \"To fill out the form, you need to take this task. This means you will be the only one able to do it. To make it available to the team again, release it.\"\nmsgstr \"フォームに記入するには、このタスクを引き受ける必要があります。つまり、あなただけがこのタスクを実行できることになります。もう一度、チームで実行できるようにする（つまり、応受援タスクにする）には、そのタスクを手放します。\"\n\nmsgid \"To use this feature, create a new version of the process in Bonita Studio version 6.4 and above, then create and install the new .bar file in the Portal.\"\nmsgstr \"この機能を使用するには、Bonita Studio バージョン 6.4 以上でプロセスの新しいバージョンを作成し、その新しい .bar ファイルをポータルにインストールします。\"\n\nmsgid \"Type\"\nmsgstr \"タイプ\"\n\nmsgid \"Type here to search for a user...\"\nmsgstr \"ユーザーを検索するには、ここを入力...\"\n\nmsgid \"Type here to search...\"\nmsgstr \"検索キーワードをここで入力...\"\n\nmsgid \"Type in a case id and <br> press enter to search for <br> human task of a specific case\"\nmsgstr \"特定案件のヒューマンタスクを検索するには、<br>案件ID を入力し <br>エンターキーを押してください。\"\n\nmsgid \"URL\"\nmsgstr \"URL\"\n\nmsgid \"Unable to add the comment. Refresh your task list and try again. If the problem persists, contact your administrator\"\nmsgstr \"コメントを追加することができません。タスク一覧を更新し、もう一度やり直してください。問題が解決しない場合はシステム管理者に問い合わせてください。\"\n\nmsgid \"Unable to map category:\"\nmsgstr \"カテゴリをマッピングができません：\"\n\nmsgid \"Unassign. This will make it available to team again\"\nmsgstr \"アサインを解除します。このタスクはもう一度、応受援タスクに戻ります。\"\n\nmsgid \"Under normal\"\nmsgstr \"平均以下\"\n\nmsgid \"Update\"\nmsgstr \"更新\"\n\nmsgid \"Update Business Data Model\"\nmsgstr \"ビジネス データ モデルの更新\"\n\nmsgid \"Update business card\"\nmsgstr \"業務上の連絡先を更新\"\n\nmsgid \"Update custom information\"\nmsgstr \"カスタム情報を更新\"\n\nmsgid \"Update general information\"\nmsgstr \"全般な情報を更新\"\n\nmsgid \"Update password\"\nmsgstr \"パスワードを更新\"\n\nmsgid \"Update personal information\"\nmsgstr \"個人情報を更新\"\n\nmsgid \"Updated by\"\nmsgstr \"更新者\"\n\nmsgid \"Updated on\"\nmsgstr \"更新日時\"\n\nmsgid \"Updating the Business Data Model applies changes to both existing database tables and data.\"\nmsgstr \"ビジネス データ モデルの更新は、既存のデータベース テーブルとデータの両方に変更を適用します。\"\n\nmsgid \"Upload new picture\"\nmsgstr \"新しい画像をアップロード\"\n\nmsgid \"Use Arrow up and Arrow down keys to browse among existing categories.\"\nmsgstr \"既存のカテゴリを閲覧するには、上矢印キーおよび下矢印キーを使用します。\"\n\nmsgid \"User id not provided. Unable to retrieve the user details.\"\nmsgstr \"ユーザーIDが提供されていません。 ユーザーの詳細を取得できません。\"\n\nmsgid \"Username\"\nmsgstr \"ユーザー名\"\n\nmsgid \"Users\"\nmsgstr \"ユーザー\"\n\nmsgid \"Users mapped to {}\"\nmsgstr \"{} にマップされているユーザー\"\n\nmsgid \"Value\"\nmsgstr \"値\"\n\nmsgid \"Version\"\nmsgstr \"バージョン\"\n\nmsgid \"View application details\"\nmsgstr \"アプリケーションの詳細表示\"\n\nmsgid \"View task\"\nmsgstr \"タスクを見る\"\n\nmsgid \"View this task\"\nmsgstr \"このタスクを見る\"\n\nmsgid \"Warning:\"\nmsgstr \"警告:\"\n\nmsgid \"We couldn't find what you are looking for.\"\nmsgstr \"お探しのものが見つかりませんでした。\"\n\nmsgid \"When you export an application descriptor, pages and profiles are not exported in the file.\"\nmsgstr \"アプリケーション記述子をエクスポートする場合、ページとプロファイルはそのファイルにエクスポートされません。\"\n\nmsgid \"You are about to export the descriptor of the application\"\nmsgstr \"アプリケーションの記述子をエクスポートしようとしています\"\n\nmsgid \"You are going to be redirected to the process list.\"\nmsgstr \"プロセスリストにリダイレクトされます。\"\n\nmsgid \"You can create custom information in Bonita Studio:\"\nmsgstr \"Bonita Studio でカスタム情報を作成できます：\"\n\nmsgid \"You must add a page before you can reference it in a menu.\"\nmsgstr \"それをメニューで参照する前に、ページを追加する必要があります。\"\n\nmsgid \"Your personal task list is empty. You can take a task from the ​<a href=\\\"#\\\" ng-click=\\\"changeFilter(TASK_FILTERS.TODO)\\\">To do</a> list.\"\nmsgstr \"マイタスク リストは空です。​<a href=\\\"#\\\" ng-click=\\\"changeFilter(TASK_FILTERS.TODO)\\\">To do</a> リストからタスクを選ぶことができます。\"\n\nmsgid \"Your session is no longer active.\"\nmsgstr \"セッションは既にアクティブではありません。\"\n\nmsgid \"Zip code\"\nmsgstr \"郵便番号\"\n\nmsgid \"Zip file structure error. Check that your .zip contains a well-formed page.properties and either the index.html or the Index.groovy file. For details, see the documentation or the example page readme (available in the custom page list)\"\nmsgstr \"Zip ファイル構造にエラーがあります。.zip に適切な形式の page.properties と、index.html または Index.groovy ファイルが 含まれているか確認してください。詳細については、ドキュメント、またはサンプル ページの readme （カスタム ページのリストで利用可能） を参照してください。\"\n\nmsgid \"an Application\"\nmsgstr \"アプリケーション\"\n\nmsgid \"an application\"\nmsgstr \"アプリケーション\"\n\nmsgid \"application descriptor has been partially imported. The following items are not loaded in the Portal.\"\nmsgstr \"アプリケーション記述子は、部分的にインポートされています。ポータル内に次の項目が読み込まれていません。\"\n\nmsgid \"application descriptor has been successfully imported, with complete page mapping and menu mapping.\"\nmsgstr \"アプリケーション記述子は正常にインポートされ、ページ マッピングとメニュー マッピングが完了しています。\"\n\nmsgid \"here\"\nmsgstr \"こちら\"\n\nmsgid \"of\"\nmsgstr \"日目/\"\n\nmsgid \"will be permanently deleted.\"\nmsgstr \"恒久的に削除されます。\"\n\nmsgid \"{{firstname}} {{lastname}} already has the membership <b>{{role}} of {{group}}</b>\"\nmsgstr \"{{firstname}} {{lastname}} さんは、既に <b>{{group}} に {{role}} として</b>に所属しています\"\n\nmsgid \"{{membership.role_id.displayName}} of {{membership.group_id.displayName}}\"\nmsgstr \"{{membership.group_id.displayName}} の {{membership.role_id.displayName}}\"\n\nmsgid \"{{nbErrors}} errors on mapping updates\"\nmsgstr \"マッピングの更新で {{nbErrors}} のエラーが発生しました\"\n\nmsgid \"{{nbMapping}} Process manager mappings could not be updated\"\nmsgstr \"{{nbMapping}} のプロセス管理者マッピングを更新できませんでした。\"\n\nmsgid \"{{nbMapping}} Process manager mappings have been updated\"\nmsgstr \"{{nbMapping}} のプロセス管理者マッピングが更新されました\"\n\nmsgid \"{{nbOfDeletedCases}} cases have been deleted\"\nmsgstr \"{{nbOfDeletedCases}} の案件が削除されました\"\n\nmsgid \"{{nbSucess}} actor mapping updates succeeded\"\nmsgstr \"{{nbSucess}} のアクターマッピングの更新に成功しました\"\n\nmsgid \"{{role}} of {{group}}\"\nmsgstr \"{{group}} の {{role}}\"\n\nmsgid \"{{role}} of {{group}} has been created\"\nmsgstr \"{{group}} の {{role}} が作成されました\"\n\nmsgid \"{} mapped\"\nmsgstr \"{} がマップされました\"\n\nmsgid \"{} mappings to delete\"\nmsgstr \"{} のマッピングが削除されます\"\n\nmsgid \"{} more\"\nmsgstr \"{} 以上\"\n\nmsgid \"{} of {}\"\nmsgstr \"{} / {}\"\n\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/resources/i18n/portal-js_pt_BR.po",
    "content": "msgid \"\"\nmsgstr \"\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Project-Id-Version: bonita\\n\"\n\"Plural-Forms: nplurals=2; plural=(n != 1);\\n\"\n\"X-Crowdin-Project: bonita\\n\"\n\"X-Crowdin-Project-ID: 13316\\n\"\n\"X-Crowdin-Language: pt-BR\\n\"\n\"X-Crowdin-File: /dev/bonita-web/portal/portal-js.pot\\n\"\n\"X-Crowdin-File-ID: 60594\\n\"\n\"Language-Team: Portuguese, Brazilian\\n\"\n\"Language: pt_BR\\n\"\n\"PO-Revision-Date: 2024-01-01 00:00\\n\"\n\nmsgid \"1 case has been deleted\"\nmsgstr \"1 caso foi excluído\"\n\nmsgid \"A Link to your application made outside the portal, e.g. with Bonita UI Builder.\"\nmsgstr \"Um link para sua aplicação feita fora do portal, por exemplo, com o Bonita UI Builder.\"\n\nmsgid \"A classic Bonita Living Application.\"\nmsgstr \"Um clássico aplicativo Bonita Live.\"\n\nmsgid \"A page with this name already exists.\"\nmsgstr \"Já existe uma página com esse nome.\"\n\nmsgid \"A server error occurred. The operation did not complete successfully\"\nmsgstr \"Ocorreu um erro de servidor. A operação não foi concluída com sucesso\"\n\nmsgid \"Above normal\"\nmsgstr \"Acima do nomal\"\n\nmsgid \"Access denied. For more information, check the log file.\"\nmsgstr \"Acesso negado. Para mais informações, verifique o arquivo de log.\"\n\nmsgid \"Actions\"\nmsgstr \"Ações\"\n\nmsgid \"Activation state\"\nmsgstr \"Estado de ativação\"\n\nmsgid \"Actor name\"\nmsgstr \"Nome do ator\"\n\nmsgid \"Actors\"\nmsgstr \"Atores\"\n\nmsgid \"Actors must be resolved before enabling the Process.\"\nmsgstr \"Os atores devem ser resolvidos antes de ativar o processo.\"\n\nmsgid \"Add\"\nmsgstr \"Adicionar\"\n\nmsgid \"Add from custom pages\"\nmsgstr \"Adicionar a partir de páginas personalizadas\"\n\nmsgid \"Add membership\"\nmsgstr \"Adicionar membros\"\n\nmsgid \"Add menu\"\nmsgstr \"Adicionar menu\"\n\nmsgid \"Add menu item\"\nmsgstr \"Adicionar item de menu\"\n\nmsgid \"Add page\"\nmsgstr \"Acionar página\"\n\nmsgid \"Add pages in Navigation\"\nmsgstr \"Acione as páginas de navegação\"\n\nmsgid \"Add top-level menu\"\nmsgstr \"Adicionar menu de nível superior\"\n\nmsgid \"Adding on apply\"\nmsgstr \"Adicionando em aplicar\"\n\nmsgid \"Address\"\nmsgstr \"Endereço\"\n\nmsgid \"All\"\nmsgstr \"Todos\"\n\nmsgid \"All done. Good job!\"\nmsgstr \"Tudo feito. Bom trabalho!\"\n\nmsgid \"An Error occurred during process activation/deactivation.\"\nmsgstr \"Ocorreu um erro durante a ativação/desativação do processo\"\n\nmsgid \"An Error occurred during process deletion.\"\nmsgstr \"Ocorreu um erro durante o processo de deleção.\"\n\nmsgid \"An application is a customized environment for a specific user profile, in which users interact with business data and business processes in the most efficient way.\"\nmsgstr \"Uma aplicação é um ambiente personalizado para um perfil específico, no qual usuários interagem com dados e processos de negócios de maneira mais eficaz.\"\n\nmsgid \"An error has occurred. For more information, check the log file.\"\nmsgstr \"Ocorreu um erro. Para mais informações, verifique o arquivo de log.\"\n\nmsgid \"An error occurred during categories update\"\nmsgstr \"Ocorreu um erro durante a atualização de categorias\"\n\nmsgid \"An error occurred when deploying the BDM. Consult the logs for more information.\"\nmsgstr \"Ocorreu um erro ao implantar o BDM. Consulte as logs para mais informações.\"\n\nmsgid \"An error occurred while submitting the form.\"\nmsgstr \"Ocorreu um erro ao enviar o formulário.\"\n\nmsgid \"An error occurred while submitting the form.<br/>The task may not be available anymore.\"\nmsgstr \"Ocorreu um erro ao enviar o formulário. <br/> A tarefa pode não estar disponível mais.\"\n\nmsgid \"An error occurred with the requested operation.\"\nmsgstr \"Ocorreu um erro com a operação solicitada\"\n\nmsgid \"Application Link\"\nmsgstr \"Link do aplicativo\"\n\nmsgid \"Application XML file\"\nmsgstr \"Arquivo XML do aplicativo\"\n\nmsgid \"Application id not provided. Unable to retrieve the application details.\"\nmsgstr \"ID da aplicação não fornecida. Não foi possível recuperar os detalhes do aplicativo.\"\n\nmsgid \"Application list\"\nmsgstr \"Lista de aplicativos\"\n\nmsgid \"Apply\"\nmsgstr \"Aplicar\"\n\nmsgid \"Archived cases\"\nmsgstr \"Instâncias arquivadas\"\n\nmsgid \"Are you sure you want to delete it?\"\nmsgstr \"Tem certeza que deseja excluir \"\n\nmsgid \"Are you sure you want to delete this membership ?\"\nmsgstr \"Tem certeza que deseja excluir este membro?\"\n\nmsgid \"Assign to me. Only I will be able to do it\"\nmsgstr \"Atribua a mim. Só eu serei capaz de fazê-lo\"\n\nmsgid \"Assigned on\"\nmsgstr \"Atribuído para\"\n\nmsgid \"Available or My tasks\"\nmsgstr \"Disponível ou minhas tarefas\"\n\nmsgid \"BDM access control file can now be installed.\"\nmsgstr \"Arquivo de controle de acesso BDM agora pode ser instalado.\"\n\nmsgid \"BDM file\"\nmsgstr \"Arquivo do BDM\"\n\nmsgid \"BDM has not been uploaded. System was not able to upload the provided BDM file for technical reasons. You can try again later.\"\nmsgstr \"BDM não foi carregado. Sistema não foi capaz de fazer upload de arquivo BDM fornecido por razões técnicas. Você pode tentar novamente mais tarde.\"\n\nmsgid \"BDM successfully updated.\"\nmsgstr \"Arquivo BDM atualizado com sucesso.\"\n\nmsgid \"Back\"\nmsgstr \"Voltar\"\n\nmsgid \"Before updating the BDM, access control file should be deleted.\"\nmsgstr \"Antes de atualizar o BDM, arquivo de controle de acesso deve ser excluído.\"\n\nmsgid \"Before you import the application descriptor, the pages and the profile must already be loaded in the Portal.\"\nmsgstr \"Antes de importar o descritor de aplicativos, as páginas e o perfil devem já ser carregados no Portal.\"\n\nmsgid \"Business Data Model definition\"\nmsgstr \"Definição de modelo de dados de negócios\"\n\nmsgid \"Business card\"\nmsgstr \"Cartão de visita\"\n\nmsgid \"Business card has not been updated. Please retry later or contact an administrator\"\nmsgstr \"Cartão de visita não foi atualizado. Por favor, tente novamente mais tarde ou entre em contato com o administrador\"\n\nmsgid \"Business card successfully updated\"\nmsgstr \"Cartão de crédito atualizado com sucesso\"\n\nmsgid \"Can't import page or form to the process\"\nmsgstr \"Não foi possível importar a página ou formulário no processo\"\n\nmsgid \"Can't update expression content\"\nmsgstr \"Não foi possível atualizar o conteúdo da expressão\"\n\nmsgid \"Cancel\"\nmsgstr \"Cancelar\"\n\nmsgid \"Cancelled, skipped instances\"\nmsgstr \"Instâncias canceladas, ignoradas\"\n\nmsgid \"Cannot upload the file\"\nmsgstr \"Não foi possível carregar o arquivo\"\n\nmsgid \"Case\"\nmsgstr \"Instância de processo\"\n\nmsgid \"Case ID\"\nmsgstr \"ID do processo\"\n\nmsgid \"Case comments\"\nmsgstr \"Comentários da instância\"\n\nmsgid \"Case id: {{caseId}} - Process: {{processName}} ({{processVersion}})\"\nmsgstr \"Caso: {{caseId}} - Processo: {{processName}} ({{processVersion}})\"\n\nmsgid \"Case overview\"\nmsgstr \"Visão geral do caso\"\n\nmsgid \"Case start\"\nmsgstr \"Iniciar Instância\"\n\nmsgid \"Cases with failures\"\nmsgstr \"Casos com erros\"\n\nmsgid \"Categories\"\nmsgstr \"Categorias\"\n\nmsgid \"Check that each entity is mapped to a profile, for either Bonita Portal or for applications you have created.\"\nmsgstr \"Verifique que cada entidade é mapeada para um perfil, para qualquer Portal Bonita ou para aplicativos que você tenha criado.\"\n\nmsgid \"City\"\nmsgstr \"Cidade\"\n\nmsgid \"Class name\"\nmsgstr \"Nome da classe\"\n\nmsgid \"Click here to choose the .xml file\"\nmsgstr \"Clique aqui para escolher o arquivo .xml\"\n\nmsgid \"Click here to choose your .zip file\"\nmsgstr \"Clique aqui para escolher o arquivo .zip\"\n\nmsgid \"Click on Cancel to remain on this page and try to execute the operation again once you logged back in (e.g. in another tab).\"\nmsgstr \"Clique em Cancelar para permanecer nesta página e tente executar a operação novamente depois de fazer login novamente (por exemplo, em outra guia).\"\n\nmsgid \"Click on Cancel to remain on this page and wait for the maintenance to end.\"\nmsgstr \"Clique em Cancelar para permanecer nesta página e aguardar o término da manutenção.\"\n\nmsgid \"Click on OK to be redirected and log back in.\"\nmsgstr \"Clique em OK para ser redirecionado e faça login novamente.\"\n\nmsgid \"Click on OK to be redirected to the maintenance page.\"\nmsgstr \"Clique em OK para ser redirecionado para a página de manutenção.\"\n\nmsgid \"Close\"\nmsgstr \"Fechar\"\n\nmsgid \"Columns selection\"\nmsgstr \"Seleção de colunas\"\n\nmsgid \"Comment:\"\nmsgstr \"Comentário:\"\n\nmsgid \"Comments\"\nmsgstr \"Comentários\"\n\nmsgid \"Compilation failure. Verify that the Index.groovy class implements the PageController interface.\"\nmsgstr \"Erro de compilação. Verifique se a classe Index.groovy implementa a interface PageController.\"\n\nmsgid \"Completed instances\"\nmsgstr \"Instâncias concluídas\"\n\nmsgid \"Configuration state\"\nmsgstr \"Estado da configuração\"\n\nmsgid \"Confirm new Password\"\nmsgstr \"Confirmar nova senha\"\n\nmsgid \"Connectors\"\nmsgstr \"Conectores\"\n\nmsgid \"Connectors must be resolved before enabling the Process.\"\nmsgstr \"Conectores devem ser resolvidos antes de ativar o processo.\"\n\nmsgid \"Core to Bonita platform\"\nmsgstr \"Núcleo da plataforma Bonita BPM\"\n\nmsgid \"Core to Bonita platform; cannot be deleted\"\nmsgstr \"O núcleo da plataforma Bonita, não pode ser excluído\"\n\nmsgid \"Country\"\nmsgstr \"País\"\n\nmsgid \"Create a new membership\"\nmsgstr \"Criar uma nova associação\"\n\nmsgid \"Create an application\"\nmsgstr \"Criar uma aplicação\"\n\nmsgid \"Created by\"\nmsgstr \"Criado por\"\n\nmsgid \"Creation\"\nmsgstr \"Criação\"\n\nmsgid \"Creation date\"\nmsgstr \"Data de criação\"\n\nmsgid \"Creation on\"\nmsgstr \"Criada em\"\n\nmsgid \"Custom information\"\nmsgstr \"Informação Personalizada\"\n\nmsgid \"Custom information has not been updated. Please retry later or contact an administrator\"\nmsgstr \"Informação personalizada não foi atualizada. Por favor, tente novamente mais tarde ou entre em contato com o administrador\"\n\nmsgid \"Custom information successfully updated\"\nmsgstr \"Informações personalizadas atualizadas com sucesso\"\n\nmsgid \"Delete\"\nmsgstr \"Excluir\"\n\nmsgid \"Delete application\"\nmsgstr \"Excluir aplicação\"\n\nmsgid \"Delete membership\"\nmsgstr \"Apagar membro\"\n\nmsgid \"Delete process\"\nmsgstr \"Excluir processo\"\n\nmsgid \"Deleting a process definition will also delete all open and archived cases of this process.\"\nmsgstr \"Excluir uma definição de processo também irá apagar todos os casos abertos e arquivados deste processo.\"\n\nmsgid \"Description\"\nmsgstr \"Descrição\"\n\nmsgid \"Disable\"\nmsgstr \"Desativar\"\n\nmsgid \"Display name\"\nmsgstr \"Nome de exibição\"\n\nmsgid \"Done tasks\"\nmsgstr \"Tarefas arquivadas\"\n\nmsgid \"Drawing process...\"\nmsgstr \"Desenhando o processo...\"\n\nmsgid \"Due date\"\nmsgstr \"Prazo final\"\n\nmsgid \"Edit\"\nmsgstr \"Editar\"\n\nmsgid \"Edit menu\"\nmsgstr \"Editar menu\"\n\nmsgid \"Edit menu item\"\nmsgstr \"Editar item de menu\"\n\nmsgid \"Email\"\nmsgstr \"Email\"\n\nmsgid \"Enable\"\nmsgstr \"Ativar\"\n\nmsgid \"Enable all\"\nmsgstr \"Ativar Tudo\"\n\nmsgid \"Enter a new category name and click \\\"Add\\\" button to create a new category.\"\nmsgstr \"Insira um novo nome de categoria e clique no botão \\\"Adicionar\\\" para criar uma categoria nova.\"\n\nmsgid \"Enter the user to start the case for...\"\nmsgstr \"Digite o usuário para o qual o caso será iniciado...\"\n\nmsgid \"Error\"\nmsgstr \"Erro\"\n\nmsgid \"Error on drawing process!\"\nmsgstr \"Erro ao desenhar o processo!\"\n\nmsgid \"Error while trying to start the case. Some required information is missing (contract not fulfilled).\"\nmsgstr \"Erro ao tentar iniciar o caso. Algumas informações necessárias estão faltando (contrato não cumprido).\"\n\nmsgid \"Error!\"\nmsgstr \"Erro!\"\n\nmsgid \"Error: value must be a boolean\"\nmsgstr \"Erro: o valor deve ser um booleano\"\n\nmsgid \"Error: value must be a double\"\nmsgstr \"Erro: o valor deve ser um double\"\n\nmsgid \"Error: value must be an integer\"\nmsgstr \"Erro: o valor deve ser um número inteiro\"\n\nmsgid \"Executing, completing, initializing instances\"\nmsgstr \"Executando, terminando, inicializando instâncias\"\n\nmsgid \"Export\"\nmsgstr \"Exportar\"\n\nmsgid \"Export application descriptor\"\nmsgstr \"Descritor de aplicativos de exportação\"\n\nmsgid \"Expression content has been updated\"\nmsgstr \"O conteúdo da expressão foi atualizado\"\n\nmsgid \"Failed instances\"\nmsgstr \"Instâncias em erro\"\n\nmsgid \"File format may be invalid. For more information, check the log file.\"\nmsgstr \"O formato do arquivo pode ser inválido. Para mais informações, verifique o arquivo de log.\"\n\nmsgid \"Filename too long. The zip filename must be no longer than 50 characters\"\nmsgstr \"O nome do arquivo muito grande. O nome do arquivo zip não deve exceder 50 caracteres\"\n\nmsgid \"Filters\"\nmsgstr \"Filtro\"\n\nmsgid \"First name\"\nmsgstr \"Primeiro nome\"\n\nmsgid \"Form\"\nmsgstr \"Formulário\"\n\nmsgid \"Form submitted.<br/>The next task in the list is now selected.\"\nmsgstr \"Formulário enviado. <br/> A próxima tarefa na lista agora está selecionada.\"\n\nmsgid \"General\"\nmsgstr \"Geral\"\n\nmsgid \"General information\"\nmsgstr \"Informações gerais\"\n\nmsgid \"General information has not been updated. Please retry later or contact an administrator\"\nmsgstr \"Informação gerais não foram atualizadas. Por favor, tente novamente mais tarde ou entre em contato com o administrador\"\n\nmsgid \"General information successfully updated\"\nmsgstr \"Informações gerais atualizadas com sucesso\"\n\nmsgid \"Groups\"\nmsgstr \"Grupos\"\n\nmsgid \"Healthy cases\"\nmsgstr \"Casos saudáveis\"\n\nmsgid \"Hide Menu\"\nmsgstr \"Esconder Menu\"\n\nmsgid \"Hide menu\"\nmsgstr \"Ocultar Menu\"\n\nmsgid \"Hide panel\"\nmsgstr \"Ocultar Painel\"\n\nmsgid \"Highest\"\nmsgstr \"Mais alto\"\n\nmsgid \"However, due to the use of Hibernate for data persistence, some changes on the data may not be well handled. These changes may lead to a corrupted database and CANNOT BE REVERTED.\"\nmsgstr \"No entanto, devido ao uso da Hibernate para a persistência de dados, algumas alterações nos dados podem não ser bem tratadas. Essas alterações podem levar a uma base de dados corrompida e NÃO PODEM SER REVERTIDAS.\"\n\nmsgid \"I have read and understood the previous warning message. I have prepared a database backup so I can rollback this operation.\"\nmsgstr \"Eu li e entendi a mensagem de aviso anterior. Eu preparei um backup de banco de dados para poder reverter esta operação.\"\n\nmsgid \"IN ANY CASE, you MUST CREATE A BACKUP of the current BDM database before undergoing a BDM update.\"\nmsgstr \"EM QUALQUER CASE, você DEVE CRIAR UM BACKUP da base de dados BDM atual antes de passar por uma atualização BDM.\"\n\nmsgid \"If your changes do not fall into those change types, you can go ahead and proceed with the BDM update now.\"\nmsgstr \"Se suas mudanças não caírem nesses tipos de mudança, você pode prosseguir e prosseguir com a atualização BDM agora.\"\n\nmsgid \"Implementation version\"\nmsgstr \"Versão de implementação\"\n\nmsgid \"Import\"\nmsgstr \"Importar\"\n\nmsgid \"Import an application\"\nmsgstr \"Importar uma aplicação\"\n\nmsgid \"Import an application descriptor\"\nmsgstr \"Importar um descritor de aplicação\"\n\nmsgid \"In a production environment, this should be used with caution.\"\nmsgstr \"Num ambiente de produção, isto deve ser utilizado com prudência.\"\n\nmsgid \"In such cases, you must CANCEL THIS UPDATE and let the database administrator carefully script the changes and apply them to the database. Only then will you be able to update the BDM from this page.\"\nmsgstr \"Nesses casos, você deve CANCELAR ESTE UPDATE e permitir que o administrador da base de dados escreva cuidadosamente as alterações e as aplique ao banco de dados. Somente então você poderá atualizar o BDM a partir desta página.\"\n\nmsgid \"In task name column\"\nmsgstr \"Na coluna nome da tarefa\"\n\nmsgid \"In the menu <i>Organization > Manage</i>, go to the <i>Users</i> page of the wizard\"\nmsgstr \"No menu <i>Organização > Gerenciar</i>, vá para a página <i>Usuários</i> do assistente\"\n\nmsgid \"Install\"\nmsgstr \"Instalar\"\n\nmsgid \"Install Business Data Model\"\nmsgstr \"Instalar o Modelo de Dados de Negócio\"\n\nmsgid \"Install a BDM file\"\nmsgstr \"Instalar um arquivo BDM\"\n\nmsgid \"Installed\"\nmsgstr \"Instalado\"\n\nmsgid \"Installed by\"\nmsgstr \"Instalado por\"\n\nmsgid \"Installed on\"\nmsgstr \"Instalado em\"\n\nmsgid \"Installing\"\nmsgstr \"Instalando\"\n\nmsgid \"Installing Business Data Model\"\nmsgstr \"Instalando o Modelo de Dados de Negócios\"\n\nmsgid \"Installing or updating the BDM implies to be logged in as Technical User, and to activate the maintenance mode.\"\nmsgstr \"Instalar ou atualizar o BDM implica estar logado como Usuário Técnico e ativar o modo de manutenção.\"\n\nmsgid \"Items per page\"\nmsgstr \"Itens por página\"\n\nmsgid \"Job title\"\nmsgstr \"Título do trabalho\"\n\nmsgid \"Last login\"\nmsgstr \"Último Acesso\"\n\nmsgid \"Last name\"\nmsgstr \"Sobrenome\"\n\nmsgid \"Last update\"\nmsgstr \"Última atualização\"\n\nmsgid \"Layout\"\nmsgstr \"Layout\"\n\nmsgid \"Legacy Application\"\nmsgstr \"Aplicação Legacy\"\n\nmsgid \"Loading...\"\nmsgstr \"Carregando...\"\n\nmsgid \"Logo\"\nmsgstr \"Logo\"\n\nmsgid \"Look & Feel\"\nmsgstr \"Aparência\"\n\nmsgid \"Lowest\"\nmsgstr \"Mais baixo\"\n\nmsgid \"MM/dd/yyyy h:mm a\"\nmsgstr \"MM/dd/yyyy hh:mm a\"\n\nmsgid \"MMM DD LT\"\nmsgstr \"MMM DD LT\"\n\nmsgid \"Make sure all artifacts enabled on the platform are compatible with the new BDM.\"\nmsgstr \"Certifique-se de todos os artefatos habilitados na plataforma são compatíveis com o novo BDM.\"\n\nmsgid \"Make sure to deactivate the maintenance mode.\"\nmsgstr \"Certifique-se de desativar o modo de manutenção.\"\n\nmsgid \"Make this task available to other team members again\"\nmsgstr \"Disponibilizar esta tarefa para outros membros da equipe novamente\"\n\nmsgid \"Make this task personal: only I will be able to do it\"\nmsgstr \"Tornar esta tarefa pessoal: somente você poderá fazer\"\n\nmsgid \"Manage Categories\"\nmsgstr \"Gerenciar categorias\"\n\nmsgid \"Manager\"\nmsgstr \"Gerente\"\n\nmsgid \"Mapped categories\"\nmsgstr \"Categoria mapeada\"\n\nmsgid \"Maximum size 255 characters\"\nmsgstr \"Tamanho máximo 255 caracteres\"\n\nmsgid \"Membership has not been added. Please retry later or contact an administrator\"\nmsgstr \"Membros não foram atualizados. Por favor, tente novamente mais tarde ou entre em contato com o administrador\"\n\nmsgid \"Membership has not been removed. Please retry later or contact an administrator\"\nmsgstr \"Membros não foram removidos. Por favor, tente novamente mais tarde ou entre em contato com o administrador\"\n\nmsgid \"Membership successfully added\"\nmsgstr \"Membros foram adicionados com sucesso\"\n\nmsgid \"Membership successfully removed\"\nmsgstr \"Membro removido com sucesso\"\n\nmsgid \"Memberships\"\nmsgstr \"Associação\"\n\nmsgid \"Memberships mapped to {}\"\nmsgstr \"Associações mapeadas para {}\"\n\nmsgid \"Mobile\"\nmsgstr \"Celular\"\n\nmsgid \"Modifying or redeploying the Business Data Model may impact existing data retention rules. After redeployment, review your configuration in the Data Retention (GDPR) section.\"\nmsgstr \"\"\n\nmsgid \"Monitoring status\"\nmsgstr \"Status do monitoramento\"\n\nmsgid \"Multi-page menu\"\nmsgstr \"Grupo de menus\"\n\nmsgid \"My tasks\"\nmsgstr \"Minhas tarefas\"\n\nmsgid \"Name\"\nmsgstr \"Nome\"\n\nmsgid \"Navigation\"\nmsgstr \"Navegação\"\n\nmsgid \"New\"\nmsgstr \"Novo\"\n\nmsgid \"New Password\"\nmsgstr \"Nova Senha\"\n\nmsgid \"New comment\"\nmsgstr \"Novos comentários\"\n\nmsgid \"No application available.\"\nmsgstr \"Nenhuma aplicação disponível.\"\n\nmsgid \"No application found with id:\"\nmsgstr \"Nenhum aplicativo encontrado com o ID:\"\n\nmsgid \"No comment on this case yet. To add one, use the input field at the bottom of this panel\"\nmsgstr \"Nenhum comentário sobre este caso ainda. Para adicionar um, use o campo de entrada na parte inferior deste painel\"\n\nmsgid \"No custom information defined.\"\nmsgstr \"Sem informação customizada definida.\"\n\nmsgid \"No data\"\nmsgstr \"Não há dados\"\n\nmsgid \"No description.\"\nmsgstr \"Nenhuma descrição.\"\n\nmsgid \"No done task yet.\"\nmsgstr \"Ainda não acabou a tarefa.\"\n\nmsgid \"No form is needed. You can enter a comment and confirm.\"\nmsgstr \"Nenhum formulário é necessário. Você pode inserir um comentário e confirmar.\"\n\nmsgid \"No mapping\"\nmsgstr \"Nenhum mapeamento\"\n\nmsgid \"No process found with id:\"\nmsgstr \"Nenhum processo encontrado com o ID:\"\n\nmsgid \"No profile\"\nmsgstr \"Sem perfis\"\n\nmsgid \"No profile mapped to this application\"\nmsgstr \"Sem perfil mapeado para esta aplicação\"\n\nmsgid \"No updates have been made on categories. For more details, check logs.\"\nmsgstr \"Nenhuma atualização foi feita nas categorias. Para mais detalhes, verifique os logs.\"\n\nmsgid \"No user found with id:\"\nmsgstr \"Nenhum usuário encontrado com o ID:\"\n\nmsgid \"Normal\"\nmsgstr \"Normal\"\n\nmsgid \"Not installed\"\nmsgstr \"Não Instalado\"\n\nmsgid \"OK\"\nmsgstr \"OK\"\n\nmsgid \"One or more tasks are not available anymore. The list is now up to date.\"\nmsgstr \"Uma ou mais tarefas não estão mais disponíveis. A lista agora esta atualizada.\"\n\nmsgid \"One-page menu\"\nmsgstr \"Menu de uma página\"\n\nmsgid \"Only I can do this task\"\nmsgstr \"Só eu posso fazer essa tarefa\"\n\nmsgid \"Only available when the platform is under maintenance. Go to the \\\"Platform maintenance\\\" page to activate the maintenance mode.\"\nmsgstr \"Apenas disponível quando a plataforma estiver em manutenção. Vá para a página de \\\"Manutenção da plataforma\\\" para ativar o modo de manutenção.\"\n\nmsgid \"Only one Business Data Model (BDM) can be active at a time.\"\nmsgstr \"Somente um Modelo de Dados de Negócios (BDM) pode ser ativado no momento.\"\n\nmsgid \"Open cases\"\nmsgstr \"Instâncias Abertas\"\n\nmsgid \"Open in a popup\"\nmsgstr \"Aberto em um pop-up\"\n\nmsgid \"Open the task to perform it\"\nmsgstr \"Abra a tarefa para executá-lo\"\n\nmsgid \"Overview\"\nmsgstr \"Visão geral\"\n\nmsgid \"Page\"\nmsgstr \"Página\"\n\nmsgid \"Pages\"\nmsgstr \"Páginas\"\n\nmsgid \"Parameters\"\nmsgstr \"Parâmetro\"\n\nmsgid \"Parameters must be resolved before enabling the Process.\"\nmsgstr \"Os parâmetros devem ser resolvidos antes de ativar o processo.\"\n\nmsgid \"Password\"\nmsgstr \"Senha\"\n\nmsgid \"Password has not been updated. Please retry later or contact an administrator\"\nmsgstr \"Senha não foi atualizada. Por favor tente mais tarde ou contate um administrador\"\n\nmsgid \"Password successfully updated\"\nmsgstr \"Senha atualizada com sucesso\"\n\nmsgid \"Passwords don't match\"\nmsgstr \"Senhas não coincidem\"\n\nmsgid \"Personal information\"\nmsgstr \"Informações pessoais\"\n\nmsgid \"Personal information has not been updated. Please retry later or contact an administrator\"\nmsgstr \"Informações pessoais não foram atualizadas. Por favor tente mais tarde ou contate um administrador\"\n\nmsgid \"Personal information successfully updated\"\nmsgstr \"Informações pessoais atualizadas com sucesso\"\n\nmsgid \"Phone\"\nmsgstr \"Telefone\"\n\nmsgid \"Please refresh the page.\"\nmsgstr \"Por favor, atualize a página.\"\n\nmsgid \"Priority\"\nmsgstr \"Prioridade\"\n\nmsgid \"Process\"\nmsgstr \"Processo\"\n\nmsgid \"Process id\"\nmsgstr \"Id do processo\"\n\nmsgid \"Process id not provided. Unable to retrieve the process details.\"\nmsgstr \"ID do processo não fornecido. Não foi possível recuperar os detalhes do processo.\"\n\nmsgid \"Process name\"\nmsgstr \"Nome do Processo\"\n\nmsgid \"Process:\"\nmsgstr \"Processo:\"\n\nmsgid \"Profile\"\nmsgstr \"Perfil\"\n\nmsgid \"Profiles\"\nmsgstr \"Perfis\"\n\nmsgid \"Profiles / Memberships\"\nmsgstr \"Perfis / Membros\"\n\nmsgid \"Ready, waiting instances\"\nmsgstr \"Instâncias prontas, ou em espera\"\n\nmsgid \"Refresh data\"\nmsgstr \"Atualizar dados\"\n\nmsgid \"Release\"\nmsgstr \"Liberar\"\n\nmsgid \"Remove\"\nmsgstr \"Remover\"\n\nmsgid \"Remove all\"\nmsgstr \"Remover tudo\"\n\nmsgid \"Removing on apply\"\nmsgstr \"Removendo ao aplicar\"\n\nmsgid \"Reset\"\nmsgstr \"Restaurar\"\n\nmsgid \"Reset to default\"\nmsgstr \"Redefinir para padrão\"\n\nmsgid \"Roles\"\nmsgstr \"Funções\"\n\nmsgid \"Save\"\nmsgstr \"Salvar\"\n\nmsgid \"Search...\"\nmsgstr \"Pesquisar...\"\n\nmsgid \"Select a group...\"\nmsgstr \"Selecione um grupo...\"\n\nmsgid \"Select a role...\"\nmsgstr \"Selecione uma função...\"\n\nmsgid \"Select all\"\nmsgstr \"Selecionar todos\"\n\nmsgid \"Select groups...\"\nmsgstr \"Selecione grupos...\"\n\nmsgid \"Select none\"\nmsgstr \"Desmarcar tudo\"\n\nmsgid \"Select roles...\"\nmsgstr \"Selecione funções...\"\n\nmsgid \"Select the <i>User information management</i> tab to add <i>Custom information</i> on the right\"\nmsgstr \"Selecione a aba <i>Gerenciamento de Informações de Usuários</i> para adicionar <i>Informações customizadas</i> na direita\"\n\nmsgid \"Select the entities (users, groups, roles, memberships) to map to the actors. These entities will do the human tasks in the process.\"\nmsgstr \"Selecione as entidades (usuários, grupos, funções, associações) a serem mapeadas aos atores. Estas entidades realizarão as tarefas humanas da aplicação.\"\n\nmsgid \"Select the page\"\nmsgstr \"Selecionar a página\"\n\nmsgid \"Select users...\"\nmsgstr \"Selecione usuários...\"\n\nmsgid \"Server is under maintenance.\"\nmsgstr \"O servidor está em manutenção.\"\n\nmsgid \"Set as Home page\"\nmsgstr \"Definir como página principal\"\n\nmsgid \"Show Menu\"\nmsgstr \"Mostrar Menu\"\n\nmsgid \"Show all the tasks I can do: both personal tasks and tasks available to other team members\"\nmsgstr \"Mostrar todas as tarefas que pode fazer: tanto pessoais, tarefas e tarefas disponíveis para outros membros da equipe\"\n\nmsgid \"Show menu\"\nmsgstr \"Mostrar menu\"\n\nmsgid \"Show my personal tasks: the ones I took and the ones assigned to me, automatically or by a process manager\"\nmsgstr \"Mostrar minhas tarefas pessoais: as que eu peguei e as que foram designadas para mim, automaticamente ou por um gerente de processo\"\n\nmsgid \"Show panel\"\nmsgstr \"Mostrar painel\"\n\nmsgid \"Show the tasks that I have done\"\nmsgstr \"Mostrar as tarefas que eu fiz\"\n\nmsgid \"Something went wrong during the deletion. You might want to cancel and try again.\"\nmsgstr \"Algo deu errado durante a exclusão. Você pode cancelar e tentar de novo.\"\n\nmsgid \"Something went wrong during the modification. You might want to cancel and try again.\"\nmsgstr \"Algo deu errado durante a modificação. Talvez você queira cancelar e tentar novamente.\"\n\nmsgid \"Something went wrong. You might want to cancel and try again.\"\nmsgstr \"Algo deu errado. Talvez você queira cancelar e tentar novamente.\"\n\nmsgid \"Sort by URL\"\nmsgstr \"Ordenar pela URL\"\n\nmsgid \"Sort by name\"\nmsgstr \"Ordenar por nome\"\n\nmsgid \"Sort by type\"\nmsgstr \"Ordenar por Tipo\"\n\nmsgid \"Sort by updated on\"\nmsgstr \"Ordenar pela data de atualização\"\n\nmsgid \"Sort by version\"\nmsgstr \"Ordenar por versão\"\n\nmsgid \"Specify the menu structure.\"\nmsgstr \"Defina a estrutura do menu.\"\n\nmsgid \"State\"\nmsgstr \"Estado\"\n\nmsgid \"Status\"\nmsgstr \"Status\"\n\nmsgid \"Status of importation\"\nmsgstr \"Status da importação\"\n\nmsgid \"Still, you can customize its look & feel on this page. You can also update some of its pages in the Resources menu.\"\nmsgstr \"Ainda assim, você pode personalizar sua aparência nesta página. Você também pode atualizar algumas de suas páginas no menu Recursos.\"\n\nmsgid \"Submit\"\nmsgstr \"Enviar\"\n\nmsgid \"Successfully updated categories\"\nmsgstr \"As categorias foram atualizadas com sucesso\"\n\nmsgid \"Table settings\"\nmsgstr \"Parâmetros da tabela\"\n\nmsgid \"Take\"\nmsgstr \"Pegar\"\n\nmsgid \"Task Id\"\nmsgstr \"Id da tarefa\"\n\nmsgid \"Task form\"\nmsgstr \"Formulário da tarefa\"\n\nmsgid \"Task list\"\nmsgstr \"Lista de tarefas\"\n\nmsgid \"Task name\"\nmsgstr \"Nome da tarefa\"\n\nmsgid \"Technical User\"\nmsgstr \"Usuário Técnico\"\n\nmsgid \"Technical error.\"\nmsgstr \"Erro técnico.\"\n\nmsgid \"The BDM schema and data have been rolled-back to what they were before this update attempt.\"\nmsgstr \"O esquema e os dados BDM foram revertidos para o que eram antes desta tentativa de atualização.\"\n\nmsgid \"The application\"\nmsgstr \"A aplicação\"\n\nmsgid \"The application does not exist. Go to application list page to see the new list of applications.\"\nmsgstr \"O aplicativo não existe. Vá para a página da lista de aplicativos para ver a nova lista de aplicativos.\"\n\nmsgid \"The application does not exist. Reload the page to see the new list of applications.\"\nmsgstr \"A aplicação não existe. Recarregue a página para ver a nova lista de aplicações.\"\n\nmsgid \"The application page does not exist. Reload the page to see the new list of application pages.\"\nmsgstr \"A página da aplicação não existe. Recarregue a página para ver a nova lista de páginas da aplicação.\"\n\nmsgid \"The attachment is too big.<br/>Select a smaller attachment and submit the form again.\"\nmsgstr \"O anexo é muito grande. <br/> Selecione um anexo menor e envie novamente.\"\n\nmsgid \"The business data: [ {} ] uses Business Objects which are not defined in the current Business Data model. Deploy a compatible Business Data model before enabling the process.\"\nmsgstr \"O dado de negócio: [ {} ] usa Objetos de Negócio que não estão definidos no modelo de Dados de Negócio atual. Instale um modelo de Dados de Negócio compatível antes de ativar o processo.\"\n\nmsgid \"The case is archived. You cannot add comments anymore\"\nmsgstr \"O instância esta arquivada. Não é possível adicionar comentários mais\"\n\nmsgid \"The case {{caseId}} has been started successfully.\"\nmsgstr \"A instância {{caseId}} foi iniciada com êxito.\"\n\nmsgid \"The category cannot be removed and added at the same time.\"\nmsgstr \"Categoria não pode ser removida e adicionada em simultâneo.\"\n\nmsgid \"The current process has no connectors defined.\"\nmsgstr \"O processo atual tem os conectores não definidos.\"\n\nmsgid \"The current process has no parameters defined.\"\nmsgstr \"O processo atual não tem parâmetros definidos.\"\n\nmsgid \"The file you are trying to upload has the wrong mime type\"\nmsgstr \"O arquivo que você está tentando carregar tem um tipo errado de mime\"\n\nmsgid \"The form mappings must be resolved before enabling the Process.\"\nmsgstr \"Os mapeamentos de formulário devem ser resolvidos antes de ativar o processo.\"\n\nmsgid \"The image you are trying to upload is too big\"\nmsgstr \"A imagem que você está tentando enviar é muito grande\"\n\nmsgid \"The item does not exist anymore. Refresh the page to see the new status\"\nmsgstr \"O item não existe mais. Atualize a página para ver o novo status\"\n\nmsgid \"The menu does not exist. Reload the page to see the new list of menus.\"\nmsgstr \"O menu não existe. Recarregue a página para ver a nova lista de menus.\"\n\nmsgid \"The name for the URL must start with custompage_ followed only by alphanumeric characters.\"\nmsgstr \"O nome da URL deve começar com custompage_ seguido somente por caracteres alfanuméricos.\"\n\nmsgid \"The task has been submitted but an error occurred while adding the comment\"\nmsgstr \"A tarefa foi apresentada, mas ocorreu um erro ao adicionar o comentário\"\n\nmsgid \"The whole list of such changes is listed <a ng-href=\\\"{{documentationUrl}}\\\" target=\\\"_blank\\\" translate>here</a>.\"\nmsgstr \"A lista inteira de tais alterações é listada <a ng-href=\\\"{{documentationUrl}}\\\" target=\\\"_blank\\\" translate>aqui</a>.\"\n\nmsgid \"The words 'content', 'API' and 'theme' are reserved for internal use. They must not be used in an application or page URL.\"\nmsgstr \"As palavras 'conteúdo', 'API' e 'tema' são reservados para uso interno. Eles não devem ser usados em um aplicativo ou a URL da página.\"\n\nmsgid \"Theme\"\nmsgstr \"Tema\"\n\nmsgid \"Then for each user, fill out the custom information value, in the Studio (<i>List of users</i> tab of the organization), or right on this Portal page.\"\nmsgstr \"Então para cada usuário, preencha o valor da informação personalizada, no Studio (<i>Lista de usuários</i> aba da organização), ou na direita da página do Portal.\"\n\nmsgid \"They will be removed from the database permanently.\"\nmsgstr \"Eles serão removidos permanentemente do banco de dados.\"\n\nmsgid \"This URL is already in use\"\nmsgstr \"Essa URL já está em uso\"\n\nmsgid \"This application is core to the Bonita platform. To keep it working as it should, it must remain available and its menu should stay as it is.\"\nmsgstr \"Essa aplicação é essencial para a plataforma Bonita. Para que continue a funcionar como deve ser, tem de permanecer disponível e o seu menu deve permanecer como está.\"\n\nmsgid \"This category has already been added to this process.\"\nmsgstr \"Esta categoria já foi adicionada para este processo.\"\n\nmsgid \"This field is mandatory\"\nmsgstr \"Este campo é obrigatório\"\n\nmsgid \"This is the task details panel. No information to display\"\nmsgstr \"Este é o painel de detalhes de tarefa. Nenhuma informação para exibir\"\n\nmsgid \"This name is already in use\"\nmsgstr \"Este nome já está em uso\"\n\nmsgid \"This only changes the application link. The application itself is still deployed at the same location.\"\nmsgstr \"Isso apenas muda o link do aplicativo. O próprio aplicativo ainda é implantado no mesmo local.\"\n\nmsgid \"This only deletes the application link. The application itself is not deleted and is still deployed.\"\nmsgstr \"Isso só exclui o link do aplicativo. O aplicativo em si não é excluído e ainda está implantado.\"\n\nmsgid \"This process is no longer available.\"\nmsgstr \"Este processo não está mais disponível.\"\n\nmsgid \"This process is not fully configured, but it is still enabled. Users may still be able to start cases or do tasks, which may lead to errors.\"\nmsgstr \"Esse processo não está totalmente configurado, mas ainda está habilitado. Os usuários ainda podem iniciar casos ou realizar tarefas, o que pode levar a erros.\"\n\nmsgid \"This process is not fully configured.\"\nmsgstr \"Este processo não está totalmente configurado.\"\n\nmsgid \"This process is not fully configured. It cannot be enabled.\"\nmsgstr \"Esse processo não está totalmente configurado. Não pode ser habilitado.\"\n\nmsgid \"This task cannot be executed from here.\"\nmsgstr \"Esta tarefa não pode ser executada a partir daqui.\"\n\nmsgid \"This task is completed.\"\nmsgstr \"Esta tarefa esta concluída.\"\n\nmsgid \"This task is not available anymore. The list is now up to date.\"\nmsgstr \"Essa tarefa não está mais disponível. A lista agora esta atualizada.\"\n\nmsgid \"This task is overdue. It was supposed to be completed by {{dueDate}}\"\nmsgstr \"Esta tarefa está em atraso. Era para ser concluída em {{dueDate}}\"\n\nmsgid \"Title\"\nmsgstr \"Título\"\n\nmsgid \"To create an application, click New or Import.\"\nmsgstr \"Para criar uma aplicação clique em Novo ou Importar.\"\n\nmsgid \"To do\"\nmsgstr \"Para fazer\"\n\nmsgid \"To do so, go to\"\nmsgstr \"Para fazer isso, vá para\"\n\nmsgid \"To do so, log out and then log back in as the Technical User, whose credentials are set upon Bonita platform installation.\"\nmsgstr \"Para fazer isso, efetue logout e, em seguida, efetue login novamente como Usuário Técnico, cujas credenciais são definidas na instalação da plataforma Bonita.\"\n\nmsgid \"To fill out the form, you need to take this task. This means you will be the only one able to do it. To make it available to the team again, release it.\"\nmsgstr \"Para preencher o formulário, você precisa assumir esta tarefa. Isto significa que você será o único capaz de fazê-la. Para torná-la disponível para a equipe novamente, clique no botão liberar.\"\n\nmsgid \"To use this feature, create a new version of the process in Bonita Studio version 6.4 and above, then create and install the new .bar file in the Portal.\"\nmsgstr \"Para usar esse recurso, crie uma nova versão do processo no Bonita Studio com versão 6.4 ou superior , então crie e instale o novo arquivo .bar no Portal.\"\n\nmsgid \"Type\"\nmsgstr \"Tipo\"\n\nmsgid \"Type here to search for a user...\"\nmsgstr \"Digite aqui para pesquisar um usuário...\"\n\nmsgid \"Type here to search...\"\nmsgstr \"Digite aqui para pesquisar...\"\n\nmsgid \"Type in a case id and <br> press enter to search for <br> human task of a specific case\"\nmsgstr \"Digite um id de instância e <br>e clique enter para procurar <br>tarefa humana de uma instância específica\"\n\nmsgid \"URL\"\nmsgstr \"URL\"\n\nmsgid \"Unable to add the comment. Refresh your task list and try again. If the problem persists, contact your administrator\"\nmsgstr \"Não é possível adicionar o comentário. Atualizar sua lista de tarefas e tente novamente. Se o problema persistir, contate o administrador\"\n\nmsgid \"Unable to map category:\"\nmsgstr \"Não é possível mapear categoria:\"\n\nmsgid \"Unassign. This will make it available to team again\"\nmsgstr \"Cancelar a atribuição. Isto tornará disponível a equipe novamente\"\n\nmsgid \"Under normal\"\nmsgstr \"Em condições normais\"\n\nmsgid \"Update\"\nmsgstr \"Atualizar\"\n\nmsgid \"Update Business Data Model\"\nmsgstr \"Atualizar Modelo de Dados de Negócios\"\n\nmsgid \"Update business card\"\nmsgstr \"Atualizar cartão de visitas\"\n\nmsgid \"Update custom information\"\nmsgstr \"Atualizar informações personalizadas\"\n\nmsgid \"Update general information\"\nmsgstr \"Atualizar informações gerais\"\n\nmsgid \"Update password\"\nmsgstr \"Atualizar senha\"\n\nmsgid \"Update personal information\"\nmsgstr \"Atualizar informações pessoais\"\n\nmsgid \"Updated by\"\nmsgstr \"Atualizado por\"\n\nmsgid \"Updated on\"\nmsgstr \"Atualizado em\"\n\nmsgid \"Updating the Business Data Model applies changes to both existing database tables and data.\"\nmsgstr \"A atualização do Modelo de Dados de Negócio aplica alterações nas tabelas e dados do banco de dados existentes.\"\n\nmsgid \"Upload new picture\"\nmsgstr \"Carregar nova imagem\"\n\nmsgid \"Use Arrow up and Arrow down keys to browse among existing categories.\"\nmsgstr \"Uso as teclas seta para cima e seta para baixo para navegar entre as categorias existentes.\"\n\nmsgid \"User id not provided. Unable to retrieve the user details.\"\nmsgstr \"ID do usuário não fornecido. Não foi possível recuperar os detalhes do usuário.\"\n\nmsgid \"Username\"\nmsgstr \"Nome de usuário\"\n\nmsgid \"Users\"\nmsgstr \"Usuários\"\n\nmsgid \"Users mapped to {}\"\nmsgstr \"Usuários mapeados a {}\"\n\nmsgid \"Value\"\nmsgstr \"Valor\"\n\nmsgid \"Version\"\nmsgstr \"Versão\"\n\nmsgid \"View application details\"\nmsgstr \"Detalhes do aplicativo\"\n\nmsgid \"View task\"\nmsgstr \"Visualizar tarefas\"\n\nmsgid \"View this task\"\nmsgstr \"Visualizar esta tarefa\"\n\nmsgid \"Warning:\"\nmsgstr \"Aviso:\"\n\nmsgid \"We couldn't find what you are looking for.\"\nmsgstr \"Não encontramos o que você está procurando.\"\n\nmsgid \"When you export an application descriptor, pages and profiles are not exported in the file.\"\nmsgstr \"Quando você exporta um descritor de aplicativo, páginas e perfis não são exportados no arquivo.\"\n\nmsgid \"You are about to export the descriptor of the application\"\nmsgstr \"Você está prestes a exportar o descritor da aplicação\"\n\nmsgid \"You are going to be redirected to the process list.\"\nmsgstr \"Você vai ser redirecionado para a lista de processos.\"\n\nmsgid \"You can create custom information in Bonita Studio:\"\nmsgstr \"Você pode criar informações personalizadas no Bonita Studio:\"\n\nmsgid \"You must add a page before you can reference it in a menu.\"\nmsgstr \"Você deve adicionar uma página antes de poder referenciá-la em um menu.\"\n\nmsgid \"Your personal task list is empty. You can take a task from the ​<a href=\\\"#\\\" ng-click=\\\"changeFilter(TASK_FILTERS.TODO)\\\">To do</a> list.\"\nmsgstr \"Sua lista de tarefas pessoais está vazia. Você pode tomar uma tarefa da lista <a href=\\\"#\\\" ng-click=\\\"changeFilter(TASK_FILTERS.TODO)\\\"> para fazer</a>.\"\n\nmsgid \"Your session is no longer active.\"\nmsgstr \"Sua sessão não está mais ativa.\"\n\nmsgid \"Zip code\"\nmsgstr \"Cep\"\n\nmsgid \"Zip file structure error. Check that your .zip contains a well-formed page.properties and either the index.html or the Index.groovy file. For details, see the documentation or the example page readme (available in the custom page list)\"\nmsgstr \"Erro de estrutura de arquivo de zip. Verifique que o seu .zip contém um page.properties bem formado e um index.html ou um Index.groovy. Para maiores detalhes, consulte a documentação ou a página de exemplo readme (disponível na lista de páginas personalizadas)\"\n\nmsgid \"an Application\"\nmsgstr \"uma Aplicação\"\n\nmsgid \"an application\"\nmsgstr \"uma aplicação\"\n\nmsgid \"application descriptor has been partially imported. The following items are not loaded in the Portal.\"\nmsgstr \"descritor de aplicações foi importado parcialmente. Os seguintes itens não foram carregados no Portal.\"\n\nmsgid \"application descriptor has been successfully imported, with complete page mapping and menu mapping.\"\nmsgstr \"descritor de aplicativos foi importado com sucesso, com mapeamento completo de página e mapeamento de menu.\"\n\nmsgid \"here\"\nmsgstr \"aqui\"\n\nmsgid \"of\"\nmsgstr \"de\"\n\nmsgid \"will be permanently deleted.\"\nmsgstr \"será excluída permanentemente.\"\n\nmsgid \"{{firstname}} {{lastname}} already has the membership <b>{{role}} of {{group}}</b>\"\nmsgstr \"{{firstname}}{{lastname}} já tem o membro <b>{{role}} do {{group}}</b>\"\n\nmsgid \"{{membership.role_id.displayName}} of {{membership.group_id.displayName}}\"\nmsgstr \"{{membership.role_id.displayName}} do {{membership.group_id.displayName}}\"\n\nmsgid \"{{nbErrors}} errors on mapping updates\"\nmsgstr \"Ocorreram {{nbErrors}} erros na atualização das associações\"\n\nmsgid \"{{nbMapping}} Process manager mappings could not be updated\"\nmsgstr \"{{nbMapping}} associações do perfil Gerenciador de processo de não puderam ser atualizadas\"\n\nmsgid \"{{nbMapping}} Process manager mappings have been updated\"\nmsgstr \"{{nbMapping}} associações do perfil Gerenciador de processo foram atualizadas\"\n\nmsgid \"{{nbOfDeletedCases}} cases have been deleted\"\nmsgstr \"{{nbOfDeletedCases}} casos foram excluídos\"\n\nmsgid \"{{nbSucess}} actor mapping updates succeeded\"\nmsgstr \"{{nbSucess}} mapeamento de atores atualizados com sucesso\"\n\nmsgid \"{{role}} of {{group}}\"\nmsgstr \"{{role}} de {{group}}\"\n\nmsgid \"{{role}} of {{group}} has been created\"\nmsgstr \"{{role}} de {{group}} foi criado\"\n\nmsgid \"{} mapped\"\nmsgstr \"{} mapeado\"\n\nmsgid \"{} mappings to delete\"\nmsgstr \"{} mapeamentos para excluir\"\n\nmsgid \"{} more\"\nmsgstr \"{} mais\"\n\nmsgid \"{} of {}\"\nmsgstr \"{} de {}\"\n\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/resources/i18n/portal_es.po",
    "content": "msgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: bonita\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2026-03-19 17:28+0000\\n\"\n\"PO-Revision-Date: 2024-01-01 00:00\\n\"\n\"Last-Translator: \\n\"\n\"Language-Team: Spanish\\n\"\n\"Language: es_ES\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=2; plural=(n != 1);\\n\"\n\"X-Crowdin-Project: bonita\\n\"\n\"X-Crowdin-Project-ID: 13316\\n\"\n\"X-Crowdin-Language: es-ES\\n\"\n\"X-Crowdin-File: /dev/bonita-web/portal/portal.po\\n\"\n\"X-Crowdin-File-ID: 60581\\n\"\n\nmsgid \"%attribute% file format is not allowed. Only %file_formats% files are allowed.\"\nmsgstr \"%attribute% formato de archivo no permitido. Sólo se permiten archivos %file_formats%.\"\n\nmsgid \"%attribute% file format not allowed or not starting with correct servlet path\"\nmsgstr \"El formato del archivo %attribute% no está autorizado o no comienza por un camino de servlet correcto\"\n\nmsgid \"%attribute% is mandatory\"\nmsgstr \"%attribute% es obligatorio\"\n\nmsgid \"%attribute% is not a valid Color value\"\nmsgstr \"%attribute% no es un Color válido\"\n\nmsgid \"%attribute% is not a valid URL\"\nmsgstr \"%attribute% no es una URL valida\"\n\nmsgid \"%attribute% is not a valid email\"\nmsgstr \"%attribute% no es un email válido\"\n\nmsgid \"%attribute% is not well written\"\nmsgstr \"%attribute% no esta bien escrito\"\n\nmsgid \"%attribute% must be a boolean value\"\nmsgstr \"%attribute% debe ser un valor booleano\"\n\nmsgid \"%attribute% must be a numeric value\"\nmsgstr \"%attribute% debe ser un valor numérico\"\n\nmsgid \"%attribute% must be an integer value\"\nmsgstr \"%attribute% debe ser un valor entero\"\n\nmsgid \"%attribute% must be less or equal than %value%\"\nmsgstr \"%attribute% debe ser menor o igual que %value%\"\n\nmsgid \"%attribute% must be less than %value%\"\nmsgstr \"%attribute% debe ser menor que %value%\"\n\nmsgid \"%attribute% must be on a single line\"\nmsgstr \"%attribute% debe estar en una sola línea\"\n\nmsgid \"%attribute% must be one of {%list%}\"\nmsgstr \"%attribute% debe ser uno de {%list%}\"\n\nmsgid \"%className% is not Serializable\"\nmsgstr \"%className% no es Serializable\"\n\nmsgid \"%className% not found. Only jdk types are supported\"\nmsgstr \"%className% no encontrada. Solo los tipos jdk son soportados\"\n\nmsgid \"%value% is not a valid value for %className%\"\nmsgstr \"%value% no es un valor valido para %className%\"\n\nmsgid \"A group with the name %groupName% already exists\"\nmsgstr \"Ya existe un grupo con el mismo nombre %groupName%\"\n\nmsgid \"An error occurred while creating a definition\"\nmsgstr \"Error al crear una definición\"\n\nmsgid \"An error occurred while deleting an item with the id %id%\"\nmsgstr \"Error al eliminar un elemento con el identificador %id%\"\n\nmsgid \"Bonita Applications\"\nmsgstr \"Aplicaciones Bonita\"\n\nmsgid \"Can't create group. Group '%groupName%' already exists\"\nmsgstr \"No se puede crear el grupo. El grupo '%groupName%' ya existe\"\n\nmsgid \"Can't create role. Role '%roleName%' already exists\"\nmsgstr \"No se puede crear el rol. El rol '%roleName%' ya existe\"\n\nmsgid \"Can't create user. User '%userName%' already exists\"\nmsgstr \"No se puede crear el usuario. El usuario '%userName%' ya existe\"\n\nmsgid \"Can't import Applications.\"\nmsgstr \"No se puede importar las aplicaciones.\"\n\nmsgid \"Can't import applications. An application '%token%' already exists\"\nmsgstr \"No se puede importar las aplicaciones. Ya existe una aplicación '%token%'\"\n\nmsgid \"Can't import organization\"\nmsgstr \"No se puede importar la organización\"\n\nmsgid \"Can't import organization. Please check that your file is well-formed.\"\nmsgstr \"No se puede importar la organización. Por favor, compruebe que su archivo está bien formateado.\"\n\nmsgid \"Can't parse json, non-well formed content\"\nmsgstr \"No se puede convertir json, el contenido no está bien formateado\"\n\nmsgid \"Can't start process %processId%, user %userId% not found\"\nmsgstr \"No se puede iniciar proceso %processId%, no se encuentra el usuario %userId%\"\n\nmsgid \"Can't start process, process %processId% is not enabled\"\nmsgstr \"No se puede iniciar el proceso, el proceso %processId% no esta activado\"\n\nmsgid \"Can't start process, process %processId% not found\"\nmsgstr \"No se puede iniciar el proceso, no se encuentra el proceso %processId%\"\n\nmsgid \"Can't update user. User not found\"\nmsgstr \"No se puede actualizar el usuario. Usuario no encontrado\"\n\nmsgid \"Category with name %categoryName% already exists\"\nmsgstr \"La categoría %categoryName% ya existe\"\n\nmsgid \"Data definition %dataName% doesn't exists for process %processId%\"\nmsgstr \"La definición de dato %dataName% no existe para el proceso %processId%\"\n\nmsgid \"Error during Application import file reading.\"\nmsgstr \"Error durante la lectura del archivo de importación de la aplicación.\"\n\nmsgid \"Error occurred when starting process %processId%\"\nmsgstr \"Ha ocurrido un error al iniciar el proceso %processId%\"\n\nmsgid \"Error occurred when starting process %processId%. Case creation limit reached.\"\nmsgstr \"Ocurrió un error al iniciar el proceso %processId%. Se alcanzó el límite de creación de casos.\"\n\nmsgid \"Error uploading the file. Maybe your session expired. You can try to refresh the page.\"\nmsgstr \"Error subiendo el fichero. Posiblemente su sesión ha expirado. Puede probar a refrescar la página.\"\n\nmsgid \"Error when adding group to actor member\"\nmsgstr \"Error al agregar grupo al actor\"\n\nmsgid \"Error when creating group\"\nmsgstr \"Error al crear grupo\"\n\nmsgid \"Error when creating user\"\nmsgstr \"Ha ocurrido un error al crear el usuario\"\n\nmsgid \"Error when deleting groups\"\nmsgstr \"Error al suprimir los grupos\"\n\nmsgid \"Error when deleting users\"\nmsgstr \"Ha ocurrido un error al borrar los usuarios\"\n\nmsgid \"Error when pausing BPM services\"\nmsgstr \"Error al pausar los Servicios BPM\"\n\nmsgid \"Error when resuming BPM services\"\nmsgstr \"Error al arrancar los Servicios BPM\"\n\nmsgid \"Error when searching users\"\nmsgstr \"Ha ocurrido un error al buscar usuarios\"\n\nmsgid \"Error when updating %activityId% activity variables\"\nmsgstr \"Ha ocurrido un error al actualizar las variables de la actividad %activityId%\"\n\nmsgid \"Error when updating group\"\nmsgstr \"Error al actualizar el grupo\"\n\nmsgid \"Error when updating process deployment informations\"\nmsgstr \"Ha ocurrido un error al actualizar la información de despliegue del proceso\"\n\nmsgid \"Error when updating user\"\nmsgstr \"Ha ocurrido un error al actualizar el usuario\"\n\nmsgid \"Filters are not supported by this API\"\nmsgstr \"Los filtros no son soportados con este API\"\n\nmsgid \"Json can't be mapped to \"\nmsgstr \"Json no puede ser mapeado a \"\n\nmsgid \"Login failed. No profile has been set up for this user. Contact your administrator.\"\nmsgstr \"Error de login. Ningún perfil está definido para este usuario. Contacte con tu administrador.\"\n\nmsgid \"Login form\"\nmsgstr \"Formulario de login\"\n\nmsgid \"MM/dd/yyyy\"\nmsgstr \"dd/MM/yyyy\"\n\nmsgid \"MM/dd/yyyy h:mm a\"\nmsgstr \"dd/MM/yyyy HH:mm\"\n\nmsgid \"MMMM dd, yyyy\"\nmsgstr \"MMMM dd, yyyy\"\n\nmsgid \"Only the value attribute can be updated\"\nmsgstr \"Sólo el atributo de valor puede ser actualizado\"\n\nmsgid \"Password\"\nmsgstr \"Contraseña\"\n\nmsgid \"Password must be at least %number% characters long\"\nmsgstr \"Contraseña debe tener al menos %number% caracteres\"\n\nmsgid \"Password must contain at least %number% digits\"\nmsgstr \"La contraseña debe tener al menos %number% dígitos\"\n\nmsgid \"Password must contain at least %number% lower case characters\"\nmsgstr \"La contraseña debe tener al menos %number% minúsculas\"\n\nmsgid \"Password must contain at least %number% special characters\"\nmsgstr \"La contraseña debe tener al menos %number% caracteres especiales\"\n\nmsgid \"Password must contain at least %number% upper case characters\"\nmsgstr \"La contraseña debe tener al menos %number% mayusculas\"\n\nmsgid \"Please, contact your administrator.\"\nmsgstr \"Por favor, ponte en contacto con tu administrador.\"\n\nmsgid \"Process %appName% in version %version% already exists\"\nmsgstr \"El proceso %appName% en la version %version% ya existe\"\n\nmsgid \"Process %appName% in version %version% contains 6.x Legacy artifacts (forms or case overview page). Those are based on Google Web Toolkit (GWT), a technology that is no longer supported by Bonita. To know more, check the documentation.\"\nmsgstr \"El proceso %appName% en la versión %version% contiene 6.x artefactos heredados (formularios o página de resumen de casos). Estos se basan en Google Web Toolkit (GWT), una tecnología que ya no está soportada por Bonita. Para saber más, consulte la documentación.\"\n\nmsgid \"Process definition not found for id %processId%\"\nmsgstr \"No se encuentra la definición del proceso con identificador %processId%\"\n\nmsgid \"Profile member already exists\"\nmsgstr \"El usuario ya está asociado con este perfil \"\n\nmsgid \"Request parameter \\\"id\\\" must be set.\"\nmsgstr \"Debe establecerse el parámetro \\\"id\\\".\"\n\nmsgid \"Search terms are not supported by this API\"\nmsgstr \"Los términos de búsqueda no son soportados por este API\"\n\nmsgid \"Session expired. Please log in again.\"\nmsgstr \"La sesión expiró. Por favor inicie sesión de nuevo.\"\n\nmsgid \"Sorting is not supported by this API\"\nmsgstr \"Ordenes no son soportados por la API\"\n\nmsgid \"The only mandatory filter is %name%\"\nmsgstr \"El único filtro obligatorio es %name%\"\n\nmsgid \"The server is not available\"\nmsgstr \"El servidor no está disponible\"\n\nmsgid \"This category has already been added to this process\"\nmsgstr \"Esta categoría ya ha sido añadida a este proceso\"\n\nmsgid \"This group has already been mapped to actor\"\nmsgstr \"Este grupo ya ha sido mapeado a un actor\"\n\nmsgid \"This membership has already been mapped to actor\"\nmsgstr \"Esta membresía ya ha sido mapeada a un actor\"\n\nmsgid \"This membership is already added to user\"\nmsgstr \"Este usuario ya posee esta membresia\"\n\nmsgid \"This role has already been mapped to actor\"\nmsgstr \"Este rol ya ha sido asignado a un actor\"\n\nmsgid \"This user has already been mapped to actor\"\nmsgstr \"Este usuario ya ha sido mapeado a un actor\"\n\nmsgid \"Too many failed login attempts. Please try again later.\"\nmsgstr \"Demasiados intentos de inicio de sesión. Por favor, inténtelo de nuevo más tarde.\"\n\nmsgid \"Unable to deploy business archive\"\nmsgstr \"No se puede desplegar el archivo de negocios\"\n\nmsgid \"Unable to disable process\"\nmsgstr \"Incapaz de desactivar el proceso\"\n\nmsgid \"Unable to enable process\"\nmsgstr \"Incapaz de activar el proceso\"\n\nmsgid \"Unable to find data instance %dataName% for activity %activityId%\"\nmsgstr \"Imposible encontrar la instancia del dato %dataName% para la actividad %activityId%\"\n\nmsgid \"Unable to find role %roleId%\"\nmsgstr \"No se puede encontrar el rol %roleId%\"\n\nmsgid \"Unable to get group path, group not found\"\nmsgstr \"No se puede obtener la ruta al grupo, grupo no encontrado\"\n\nmsgid \"Unable to get process data definitions, process %processId% not found\"\nmsgstr \"Imposible recuperar la definición de los datos de proceso, no se encuentra el proceso %processId%\"\n\nmsgid \"Unable to log in. Please check your username and password.\"\nmsgstr \"No se puede iniciar la sesión. Por favor comprueba tu nombre de usuario y tu contraseña.\"\n\nmsgid \"User\"\nmsgstr \"Usuario\"\n\nmsgid \"User not found\"\nmsgstr \"Usuario no encontrado\"\n\nmsgid \"Welcome to\"\nmsgstr \"Bienvenido a\"\n\nmsgid \"message\"\nmsgstr \"mensaje\"\n\nmsgid \"or\"\nmsgstr \"o\"\n\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/resources/i18n/portal_fr.po",
    "content": "msgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: bonita\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2026-03-19 17:28+0000\\n\"\n\"PO-Revision-Date: 2024-01-01 00:00\\n\"\n\"Last-Translator: \\n\"\n\"Language-Team: French\\n\"\n\"Language: fr_FR\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=2; plural=(n > 1);\\n\"\n\"X-Crowdin-Project: bonita\\n\"\n\"X-Crowdin-Project-ID: 13316\\n\"\n\"X-Crowdin-Language: fr\\n\"\n\"X-Crowdin-File: /dev/bonita-web/portal/portal.po\\n\"\n\"X-Crowdin-File-ID: 60581\\n\"\n\nmsgid \"%attribute% file format is not allowed. Only %file_formats% files are allowed.\"\nmsgstr \"le format de fichier %attribute%  n'est pas autorisé. Utiliser les fichiers de format %file_formats%.\"\n\nmsgid \"%attribute% file format not allowed or not starting with correct servlet path\"\nmsgstr \"Le format de fichier %attribute% n'est pas autorisé ou ne commence pas par un chemin de servlet correct\"\n\nmsgid \"%attribute% is mandatory\"\nmsgstr \"%attribute% est obligatoire\"\n\nmsgid \"%attribute% is not a valid Color value\"\nmsgstr \"%attribute% n'est pas une valeur de couleur correcte\"\n\nmsgid \"%attribute% is not a valid URL\"\nmsgstr \"%attribute% n'est pas une URL valide\"\n\nmsgid \"%attribute% is not a valid email\"\nmsgstr \"%attribute% n'est pas une adresse email valide\"\n\nmsgid \"%attribute% is not well written\"\nmsgstr \"%attribute% n'est pas correctement écrit\"\n\nmsgid \"%attribute% must be a boolean value\"\nmsgstr \"%attribute% doit être une valeur booléenne\"\n\nmsgid \"%attribute% must be a numeric value\"\nmsgstr \"%attribute% doit être valeur numérique\"\n\nmsgid \"%attribute% must be an integer value\"\nmsgstr \"%attribute% doit être un entier\"\n\nmsgid \"%attribute% must be less or equal than %value%\"\nmsgstr \"%attribute% doit être inférieur ou égale à %value%\"\n\nmsgid \"%attribute% must be less than %value%\"\nmsgstr \"%attribute% doit être inférieure à %value%\"\n\nmsgid \"%attribute% must be on a single line\"\nmsgstr \"%attribute% doit être sur une ligne unique\"\n\nmsgid \"%attribute% must be one of {%list%}\"\nmsgstr \"%attribute% doit être dans {%list%}\"\n\nmsgid \"%className% is not Serializable\"\nmsgstr \"%className% n'est pas serializable\"\n\nmsgid \"%className% not found. Only jdk types are supported\"\nmsgstr \"%className% est introuvable. Seuls les éléments de types jdk sont supportés\"\n\nmsgid \"%value% is not a valid value for %className%\"\nmsgstr \"%value% n'est pas valide pour la valeur de %className%\"\n\nmsgid \"A group with the name %groupName% already exists\"\nmsgstr \"Il existe déjà un groupe portant le nom %groupName%\"\n\nmsgid \"An error occurred while creating a definition\"\nmsgstr \"Une erreur s'est produite lors de la création d'une définition\"\n\nmsgid \"An error occurred while deleting an item with the id %id%\"\nmsgstr \"Une erreur s'est produite lors de la suppression d'un élément dont l'id est %id%\"\n\nmsgid \"Bonita Applications\"\nmsgstr \"Applications Bonita\"\n\nmsgid \"Can't create group. Group '%groupName%' already exists\"\nmsgstr \"Impossible de créer le groupe. Le groupe '%groupName%' existe\"\n\nmsgid \"Can't create role. Role '%roleName%' already exists\"\nmsgstr \"Impossible de créer ce rôle, car '%roleName%' est déjà présent\"\n\nmsgid \"Can't create user. User '%userName%' already exists\"\nmsgstr \"Impossible de créer cet utilisateur, car '%userName%' est déjà présent\"\n\nmsgid \"Can't import Applications.\"\nmsgstr \"Impossible d'importer l'application.\"\n\nmsgid \"Can't import applications. An application '%token%' already exists\"\nmsgstr \"L'application n'a pas pu être importée. L'URL '%token%' est déjà utilisée.\"\n\nmsgid \"Can't import organization\"\nmsgstr \"Impossible d'importer l'organisation\"\n\nmsgid \"Can't import organization. Please check that your file is well-formed.\"\nmsgstr \"Impossible d'importer l'organisation. Vérifiez que votre fichier correspond au XSD.\"\n\nmsgid \"Can't parse json, non-well formed content\"\nmsgstr \"Impossible de lire le json, le contenu n'est pas correctement structuré\"\n\nmsgid \"Can't start process %processId%, user %userId% not found\"\nmsgstr \"Impossible de démarrer le processus %processId%, l'utilisateur %userId% est introuvable\"\n\nmsgid \"Can't start process, process %processId% is not enabled\"\nmsgstr \"Impossible de démarrer le processus, car %processId% n'est pas activé\"\n\nmsgid \"Can't start process, process %processId% not found\"\nmsgstr \"Impossible de démarrer le processus, processus %processId% introuvable\"\n\nmsgid \"Can't update user. User not found\"\nmsgstr \"Impossible de mettre à jour les données de l'utilisateur. L'utilisateur est introuvable\"\n\nmsgid \"Category with name %categoryName% already exists\"\nmsgstr \"La catégorie %categoryName% existe déjà\"\n\nmsgid \"Data definition %dataName% doesn't exists for process %processId%\"\nmsgstr \"Data definition %dataName% n'existe pas pour le process %processId%\"\n\nmsgid \"Error during Application import file reading.\"\nmsgstr \"Erreur lors de l'importation de l'Application.\"\n\nmsgid \"Error occurred when starting process %processId%\"\nmsgstr \"Une erreur s'est produite au démarrage du processus %processId%\"\n\nmsgid \"Error occurred when starting process %processId%. Case creation limit reached.\"\nmsgstr \"Une erreur s'est produite lors du démarrage du processus %processId%. La limite de création de cas a été atteinte.\"\n\nmsgid \"Error uploading the file. Maybe your session expired. You can try to refresh the page.\"\nmsgstr \"Une erreur est survenue lors du chargement du fichier. Votre session a peut-être expiré. Essayez de rafraîchir la page.\"\n\nmsgid \"Error when adding group to actor member\"\nmsgstr \"Erreur lors de l'ajout du groupe aux membres de l'acteur\"\n\nmsgid \"Error when creating group\"\nmsgstr \"Erreur lors de la création du groupe\"\n\nmsgid \"Error when creating user\"\nmsgstr \"Erreur lors de la création de l'utilisateur\"\n\nmsgid \"Error when deleting groups\"\nmsgstr \"Erreur lors de la suppression du(es) groupe(s)\"\n\nmsgid \"Error when deleting users\"\nmsgstr \"Erreur lors de la désactivation des utilisateurs\"\n\nmsgid \"Error when pausing BPM services\"\nmsgstr \"Erreur pendant la suspension des services BPM\"\n\nmsgid \"Error when resuming BPM services\"\nmsgstr \"Erreur lors de la reprise des services BPM\"\n\nmsgid \"Error when searching users\"\nmsgstr \"Erreur lors de la recherche des utilisateurs\"\n\nmsgid \"Error when updating %activityId% activity variables\"\nmsgstr \"Erreur lors de la mise à jour des variables de l'activité de %activityId%\"\n\nmsgid \"Error when updating group\"\nmsgstr \"Erreur lors de la mise à jour du groupe\"\n\nmsgid \"Error when updating process deployment informations\"\nmsgstr \"Erreur lors de l'actualisation des informations de déploiement du processus\"\n\nmsgid \"Error when updating user\"\nmsgstr \"Erreur lors de l'actualisation des données de l'utilisateur\"\n\nmsgid \"Filters are not supported by this API\"\nmsgstr \"Les filtres ne sont pas supportés par cette API\"\n\nmsgid \"Json can't be mapped to \"\nmsgstr \"Json ne peut pas être mappée à\"\n\nmsgid \"Login failed. No profile has been set up for this user. Contact your administrator.\"\nmsgstr \"Échec de la connexion. Aucun permission n'a été mise en place pour cet utilisateur. Contactez votre administrateur.\"\n\nmsgid \"Login form\"\nmsgstr \"Formulaire de connexion\"\n\nmsgid \"MM/dd/yyyy\"\nmsgstr \"dd/MM/yyyy\"\n\nmsgid \"MM/dd/yyyy h:mm a\"\nmsgstr \"dd/MM/yyyy HH:mm\"\n\nmsgid \"MMMM dd, yyyy\"\nmsgstr \"dd/MM/yyyy\"\n\nmsgid \"Only the value attribute can be updated\"\nmsgstr \"Seul l'attribut valeur peut être mis à jour\"\n\nmsgid \"Password\"\nmsgstr \"Mot de passe\"\n\nmsgid \"Password must be at least %number% characters long\"\nmsgstr \"Le mot de passe doit contenir au moins %number% caractères\"\n\nmsgid \"Password must contain at least %number% digits\"\nmsgstr \"Le mot de passe doit contenir au moins %number% chiffres\"\n\nmsgid \"Password must contain at least %number% lower case characters\"\nmsgstr \"Le mot de passe doit contenir au moins %number% lettres en minuscules\"\n\nmsgid \"Password must contain at least %number% special characters\"\nmsgstr \"Le mot de passe doit contenir au moins %number% caractères spéciaux\"\n\nmsgid \"Password must contain at least %number% upper case characters\"\nmsgstr \"Le mot de passe doit contenir au moins %number% lettres en majuscules\"\n\nmsgid \"Please, contact your administrator.\"\nmsgstr \"Merci de contacter votre administrateur.\"\n\nmsgid \"Process %appName% in version %version% already exists\"\nmsgstr \" Le processus %appName% existe déjà en version %version% \"\n\nmsgid \"Process %appName% in version %version% contains 6.x Legacy artifacts (forms or case overview page). Those are based on Google Web Toolkit (GWT), a technology that is no longer supported by Bonita. To know more, check the documentation.\"\nmsgstr \"Le processus %appName% en version %version% contient des artefacts (formulaires ou pages de synthèse des cas) anciens, datant de Bonita 6.x. Ils sont basés sur Google Web Toolkit (GWT), technologie qui n’est plus supportée par Bonita. Pour en savoir plus, consultez la documentation.\"\n\nmsgid \"Process definition not found for id %processId%\"\nmsgstr \"La définition du processus est introuvable pour l'id %processId%\"\n\nmsgid \"Profile member already exists\"\nmsgstr \"Ce membre est déjà associé au profil\"\n\nmsgid \"Request parameter \\\"id\\\" must be set.\"\nmsgstr \"Le paramètre de requête \\\"id\\\" doit être défini.\"\n\nmsgid \"Search terms are not supported by this API\"\nmsgstr \"Les critères de recherche ne sont pas supportés par cette API\"\n\nmsgid \"Session expired. Please log in again.\"\nmsgstr \"La session a expiré. Connectez-vous à nouveau.\"\n\nmsgid \"Sorting is not supported by this API\"\nmsgstr \"Le tri n'est pas supporté par cette API\"\n\nmsgid \"The only mandatory filter is %name%\"\nmsgstr \"Seul le filtre %name% est obligatoire\"\n\nmsgid \"The server is not available\"\nmsgstr \"Le serveur n'est pas disponible\"\n\nmsgid \"This category has already been added to this process\"\nmsgstr \"Cette catégorie a déjà été ajoutée à ce processus\"\n\nmsgid \"This group has already been mapped to actor\"\nmsgstr \"Ce groupe est déjà lié à l'acteur\"\n\nmsgid \"This membership has already been mapped to actor\"\nmsgstr \"Cette adhésion est déjà liée à l'acteur\"\n\nmsgid \"This membership is already added to user\"\nmsgstr \"Cette adhésion est déjà présente pour cet utilisateur\"\n\nmsgid \"This role has already been mapped to actor\"\nmsgstr \"Ce rôle est déjà lié à l'acteur\"\n\nmsgid \"This user has already been mapped to actor\"\nmsgstr \"Cet utilisateur est déjà lié à l'acteur\"\n\nmsgid \"Too many failed login attempts. Please try again later.\"\nmsgstr \"Trop de tentatives de connexion. Veuillez réessayer plus tard.\"\n\nmsgid \"Unable to deploy business archive\"\nmsgstr \"Impossible de déployer le fichier .bar\"\n\nmsgid \"Unable to disable process\"\nmsgstr \"Impossible de desactiver l'app\"\n\nmsgid \"Unable to enable process\"\nmsgstr \"Impossible d'activer l'app\"\n\nmsgid \"Unable to find data instance %dataName% for activity %activityId%\"\nmsgstr \"Impossible de trouver les données de l'instance %dataName% pour l'activité %activityId%\"\n\nmsgid \"Unable to find role %roleId%\"\nmsgstr \"Impossible de trouver le rôle %roleId%\"\n\nmsgid \"Unable to get group path, group not found\"\nmsgstr \"Impossible d'obtenir le chemin d'accès du groupe, groupe introuvable\"\n\nmsgid \"Unable to get process data definitions, process %processId% not found\"\nmsgstr \"Impossible d'obtenir des définitions de données de processus, car %processId% est introuvable\"\n\nmsgid \"Unable to log in. Please check your username and password.\"\nmsgstr \"Impossible de se connecter. Veuillez vérifier votre nom d'utilisateur et le mot de passe.\"\n\nmsgid \"User\"\nmsgstr \"Utilisateur\"\n\nmsgid \"User not found\"\nmsgstr \"Utilisateur introuvable\"\n\nmsgid \"Welcome to\"\nmsgstr \"Bienvenue sur\"\n\nmsgid \"message\"\nmsgstr \"Message\"\n\nmsgid \"or\"\nmsgstr \"ou\"\n\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/resources/i18n/portal_ja.po",
    "content": "msgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: bonita\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2026-03-19 17:28+0000\\n\"\n\"PO-Revision-Date: 2024-01-01 00:00\\n\"\n\"Last-Translator: \\n\"\n\"Language-Team: Japanese\\n\"\n\"Language: ja_JP\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"X-Crowdin-Project: bonita\\n\"\n\"X-Crowdin-Project-ID: 13316\\n\"\n\"X-Crowdin-Language: ja\\n\"\n\"X-Crowdin-File: /dev/bonita-web/portal/portal.po\\n\"\n\"X-Crowdin-File-ID: 60581\\n\"\n\nmsgid \"%attribute% file format is not allowed. Only %file_formats% files are allowed.\"\nmsgstr \"%attribute% ファイルの形式は許可されていません。%file_formats%のファイルのみが許可されます。\"\n\nmsgid \"%attribute% file format not allowed or not starting with correct servlet path\"\nmsgstr \"%attribute% ファイル形式が許可されていないか、正しいサーブレット パスで始まっていません\"\n\nmsgid \"%attribute% is mandatory\"\nmsgstr \"%attribute%は必須です。\"\n\nmsgid \"%attribute% is not a valid Color value\"\nmsgstr \"%attribute% は、有効なカラー値ではありません。\"\n\nmsgid \"%attribute% is not a valid URL\"\nmsgstr \"%attribute% は、有効な URL ではありません。\"\n\nmsgid \"%attribute% is not a valid email\"\nmsgstr \"%attribute% は、有効なメール・アドレスではありません。\"\n\nmsgid \"%attribute% is not well written\"\nmsgstr \"%attribute% は、よく書かれていません。\"\n\nmsgid \"%attribute% must be a boolean value\"\nmsgstr \"%attribute% は、ブール値である必要があります。\"\n\nmsgid \"%attribute% must be a numeric value\"\nmsgstr \"%attribute% は数値でなければなりません\"\n\nmsgid \"%attribute% must be an integer value\"\nmsgstr \"%attribute% は、 整数値を指定する必要があります。\"\n\nmsgid \"%attribute% must be less or equal than %value%\"\nmsgstr \"%attribute% は、%value%以下にする必要があります。\"\n\nmsgid \"%attribute% must be less than %value%\"\nmsgstr \"%attribute% は、%value%より小さくなければなりません\"\n\nmsgid \"%attribute% must be on a single line\"\nmsgstr \"%attribute% は、1 行にする必要があります。\"\n\nmsgid \"%attribute% must be one of {%list%}\"\nmsgstr \"%attribute% は、{%list%}のいずれかする必要があります。\"\n\nmsgid \"%className% is not Serializable\"\nmsgstr \"%className% は、シリアライズ可能ではありません\"\n\nmsgid \"%className% not found. Only jdk types are supported\"\nmsgstr \"%className% が見つかりません。jdk type だけがサポートされます\"\n\nmsgid \"%value% is not a valid value for %className%\"\nmsgstr \"%value% は、%className% の適正な値ではありません\"\n\nmsgid \"A group with the name %groupName% already exists\"\nmsgstr \"%groupName% と同名のグループが既に存在しています。\"\n\nmsgid \"An error occurred while creating a definition\"\nmsgstr \"定義の作成中にエラーが発生しました\"\n\nmsgid \"An error occurred while deleting an item with the id %id%\"\nmsgstr \"Id %id% を持つ項目を削除中にエラーが発生しました\"\n\nmsgid \"Bonita Applications\"\nmsgstr \"Bonita アプリケーション\"\n\nmsgid \"Can't create group. Group '%groupName%' already exists\"\nmsgstr \"グループを作成することはできません。グループ  '%groupName%' が既に存在します。\"\n\nmsgid \"Can't create role. Role '%roleName%' already exists\"\nmsgstr \"役割を作成することはできません。役割 '%roleName%' が既に存在します。\"\n\nmsgid \"Can't create user. User '%userName%' already exists\"\nmsgstr \"利用者を登録することはできません。利用者： '%userName%' が既に存在します。\"\n\nmsgid \"Can't import Applications.\"\nmsgstr \"アプリケーションをインポートできません。\"\n\nmsgid \"Can't import applications. An application '%token%' already exists\"\nmsgstr \"アプリケーションをインポートできません。アプリケーション： '%token%' は既に存在します。\"\n\nmsgid \"Can't import organization\"\nmsgstr \"組織をインポートできません\"\n\nmsgid \"Can't import organization. Please check that your file is well-formed.\"\nmsgstr \"組織をインポートできません。ファイルが正しい形式であることを確認してください。\"\n\nmsgid \"Can't parse json, non-well formed content\"\nmsgstr \"json を解析できません。 しっかり形式化されたコンテンツではありません。\"\n\nmsgid \"Can't start process %processId%, user %userId% not found\"\nmsgstr \"プロセスID: %processId% を起動できません。ユーザーID: %userId% が見つかりません。\"\n\nmsgid \"Can't start process, process %processId% is not enabled\"\nmsgstr \"プロセスを開始できません。プロセス：%processId% は有効になっていません\"\n\nmsgid \"Can't start process, process %processId% not found\"\nmsgstr \"プロセスを開始できません。 プロセス：%processId% が見つかりません\"\n\nmsgid \"Can't update user. User not found\"\nmsgstr \"利用者が見つからないため、更新できません\"\n\nmsgid \"Category with name %categoryName% already exists\"\nmsgstr \"カテゴリ名  %categoryName% は既に存在します。\"\n\nmsgid \"Data definition %dataName% doesn't exists for process %processId%\"\nmsgstr \"データ定義：%dataName% は、プロセス：%processId% に存在しません\"\n\nmsgid \"Error during Application import file reading.\"\nmsgstr \"アプリケーションのインポート ファイルの読み取り中にエラーが発生しました。\"\n\nmsgid \"Error occurred when starting process %processId%\"\nmsgstr \"プロセス：%processId% の開始時にエラーが発生しました\"\n\nmsgid \"Error occurred when starting process %processId%. Case creation limit reached.\"\nmsgstr \"プロセス %processId%の開始時にエラーが発生しました。ケース作成の上限に達しました。\"\n\nmsgid \"Error uploading the file. Maybe your session expired. You can try to refresh the page.\"\nmsgstr \"ファイルのアップロード中にエラーが発生しました。セッションの有効期限が切れた可能性があります。ページを更新してみてください。\"\n\nmsgid \"Error when adding group to actor member\"\nmsgstr \"アクターメンバーにグループを追加するときにエラーが発生しました\"\n\nmsgid \"Error when creating group\"\nmsgstr \"グループを作成するときにエラーが発生\"\n\nmsgid \"Error when creating user\"\nmsgstr \"利用者登録時のエラー\"\n\nmsgid \"Error when deleting groups\"\nmsgstr \"グループを削除するときにエラーが発生\"\n\nmsgid \"Error when deleting users\"\nmsgstr \"利用者削除時のエラー\"\n\nmsgid \"Error when pausing BPM services\"\nmsgstr \"BPM サービスを一時停止するときにエラーが発生しました。\"\n\nmsgid \"Error when resuming BPM services\"\nmsgstr \"BPM サービスを再開するときにエラーが発生しました。\"\n\nmsgid \"Error when searching users\"\nmsgstr \"利用者検索時のエラー\"\n\nmsgid \"Error when updating %activityId% activity variables\"\nmsgstr \"アクティビティ：%activityId% の変数を更新中にエラーが発生しました\"\n\nmsgid \"Error when updating group\"\nmsgstr \"グループを更新するときにエラーが発生\"\n\nmsgid \"Error when updating process deployment informations\"\nmsgstr \"プロセスのデプロイ情報を更新中にエラーが発生しました\"\n\nmsgid \"Error when updating user\"\nmsgstr \"利用者を更新時のエラー\"\n\nmsgid \"Filters are not supported by this API\"\nmsgstr \"この API では、フィルターはサポートされていません\"\n\nmsgid \"Json can't be mapped to \"\nmsgstr \"Json を次にマップできません\"\n\nmsgid \"Login failed. No profile has been set up for this user. Contact your administrator.\"\nmsgstr \"ログインに失敗しました。この利用者のプロファイルが設定されていません。BPMのシステム管理者に問い合わせてください。\"\n\nmsgid \"Login form\"\nmsgstr \"ログイン フォーム\"\n\nmsgid \"MM/dd/yyyy\"\nmsgstr \"yyyy/MM/dd\"\n\nmsgid \"MM/dd/yyyy h:mm a\"\nmsgstr \"yyyy/MM/dd HH:mm\"\n\nmsgid \"MMMM dd, yyyy\"\nmsgstr \"yyyy年MM月dd日\"\n\nmsgid \"Only the value attribute can be updated\"\nmsgstr \"Value 属性のみを更新することができます\"\n\nmsgid \"Password\"\nmsgstr \"パスワード\"\n\nmsgid \"Password must be at least %number% characters long\"\nmsgstr \"パスワードは少なくとも、%number% 文字にする必要があります\"\n\nmsgid \"Password must contain at least %number% digits\"\nmsgstr \"パスワードは少なくとも、%number% 文字である必要があります\"\n\nmsgid \"Password must contain at least %number% lower case characters\"\nmsgstr \"パスワードは少なくとも、 %number% 文字の小文字を含める必要があります\"\n\nmsgid \"Password must contain at least %number% special characters\"\nmsgstr \"パスワードは少なくとも、%number% 文字の特殊文字を含める必要があります\"\n\nmsgid \"Password must contain at least %number% upper case characters\"\nmsgstr \"パスワードは少なくとも、 %number% 文字の大文字を含める必要があります\"\n\nmsgid \"Please, contact your administrator.\"\nmsgstr \"BPMのシステム管理者に問い合わせてください。\"\n\nmsgid \"Process %appName% in version %version% already exists\"\nmsgstr \"プロセス： %appName% バージョン %version% では既に存在します\"\n\nmsgid \"Process %appName% in version %version% contains 6.x Legacy artifacts (forms or case overview page). Those are based on Google Web Toolkit (GWT), a technology that is no longer supported by Bonita. To know more, check the documentation.\"\nmsgstr \"バージョンのプロセスには、6.xレガシーアーティファクト（フォームまたはケースの概要ページ）が含まれています。これらはGoogle Web Toolkit (GWT)に基づきBonitaでサポートされなくなったテクノロジーです。詳細については、ドキュメントを確認してください。\"\n\nmsgid \"Process definition not found for id %processId%\"\nmsgstr \"ID: %processId% のプロセス定義 が見つかりません\"\n\nmsgid \"Profile member already exists\"\nmsgstr \"プロファイルのメンバーは既に存在します\"\n\nmsgid \"Request parameter \\\"id\\\" must be set.\"\nmsgstr \"リクエスト パラメータ　\\\"id\\\"　を設定する必要があります。\"\n\nmsgid \"Search terms are not supported by this API\"\nmsgstr \"その検索用語は、このAPI ではサポートされていません\"\n\nmsgid \"Session expired. Please log in again.\"\nmsgstr \"セッションが切れました。再度ログインしてください。\"\n\nmsgid \"Sorting is not supported by this API\"\nmsgstr \"この API では、並べ替えはサポートされていません\"\n\nmsgid \"The only mandatory filter is %name%\"\nmsgstr \"唯一の必須のフィルターは、%name% です。\"\n\nmsgid \"The server is not available\"\nmsgstr \"サーバーは利用できません。\"\n\nmsgid \"This category has already been added to this process\"\nmsgstr \"このカテゴリは既にこのプロセスに追加されました\"\n\nmsgid \"This group has already been mapped to actor\"\nmsgstr \"このグループは、既にアクターにアサインされています\"\n\nmsgid \"This membership has already been mapped to actor\"\nmsgstr \"この所属は、既にアクターにアサインされています\"\n\nmsgid \"This membership is already added to user\"\nmsgstr \"この所属は既にユーザーに追加されています。\"\n\nmsgid \"This role has already been mapped to actor\"\nmsgstr \"この役割は、既にアクターにアサインされています\"\n\nmsgid \"This user has already been mapped to actor\"\nmsgstr \"このユーザーは既にアクターにアサインされています。\"\n\nmsgid \"Too many failed login attempts. Please try again later.\"\nmsgstr \"ログイン試行回数が多すぎます。しばらくしてからもう一度お試しください。\"\n\nmsgid \"Unable to deploy business archive\"\nmsgstr \"ビジネス アーカイブをデプロイすることはできません。\"\n\nmsgid \"Unable to disable process\"\nmsgstr \"プロセスを無効にすることはできません。\"\n\nmsgid \"Unable to enable process\"\nmsgstr \"プロセスを有効にすることはできません。\"\n\nmsgid \"Unable to find data instance %dataName% for activity %activityId%\"\nmsgstr \"アクティビティ：%activityId% のデータ・インスタンス：%dataName% を見つけることができません\"\n\nmsgid \"Unable to find role %roleId%\"\nmsgstr \"ロール %roleId% が見つかりません\"\n\nmsgid \"Unable to get group path, group not found\"\nmsgstr \"グループが見つからないため、グループのパスを取得することができません\"\n\nmsgid \"Unable to get process data definitions, process %processId% not found\"\nmsgstr \"プロセスのデータ定義を取得できません。 プロセス：%processId% が見つかりません\"\n\nmsgid \"Unable to log in. Please check your username and password.\"\nmsgstr \"ログインできません。ユーザー名とパスワードを確認してください。\"\n\nmsgid \"User\"\nmsgstr \"利用者\"\n\nmsgid \"User not found\"\nmsgstr \"利用者が見つかりません\"\n\nmsgid \"Welcome to\"\nmsgstr \"ようこそ\"\n\nmsgid \"message\"\nmsgstr \"Message\"\n\nmsgid \"or\"\nmsgstr \"または\"\n\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/resources/i18n/portal_pt_BR.po",
    "content": "msgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: bonita\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2026-03-19 17:28+0000\\n\"\n\"PO-Revision-Date: 2024-01-01 00:00\\n\"\n\"Last-Translator: \\n\"\n\"Language-Team: Portuguese, Brazilian\\n\"\n\"Language: pt_BR\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=2; plural=(n != 1);\\n\"\n\"X-Crowdin-Project: bonita\\n\"\n\"X-Crowdin-Project-ID: 13316\\n\"\n\"X-Crowdin-Language: pt-BR\\n\"\n\"X-Crowdin-File: /dev/bonita-web/portal/portal.po\\n\"\n\"X-Crowdin-File-ID: 60581\\n\"\n\nmsgid \"%attribute% file format is not allowed. Only %file_formats% files are allowed.\"\nmsgstr \"formato de arquivo %attribute% não é permitido. Apenas arquivos %file_formats% são permitidos.\"\n\nmsgid \"%attribute% file format not allowed or not starting with correct servlet path\"\nmsgstr \"\"\n\nmsgid \"%attribute% is mandatory\"\nmsgstr \"%attribute% é obrigatório\"\n\nmsgid \"%attribute% is not a valid Color value\"\nmsgstr \"%attribute% não é um valor válido de cor\"\n\nmsgid \"%attribute% is not a valid URL\"\nmsgstr \"%attribute% não é um URL válido\"\n\nmsgid \"%attribute% is not a valid email\"\nmsgstr \"%attribute% não é um e-mail válido\"\n\nmsgid \"%attribute% is not well written\"\nmsgstr \"%attribute% não está bem escrito\"\n\nmsgid \"%attribute% must be a boolean value\"\nmsgstr \"%attribute% deve ser um valor booleano\"\n\nmsgid \"%attribute% must be a numeric value\"\nmsgstr \"%attribute% deve ser um valor numérico\"\n\nmsgid \"%attribute% must be an integer value\"\nmsgstr \"%attribute% deve ser um valor inteiro\"\n\nmsgid \"%attribute% must be less or equal than %value%\"\nmsgstr \"%attribute% deve ser menor ou igual do que %value%\"\n\nmsgid \"%attribute% must be less than %value%\"\nmsgstr \"%attribute% deve ser menor que %value%\"\n\nmsgid \"%attribute% must be on a single line\"\nmsgstr \"%attribute% deve ser uma única linha\"\n\nmsgid \"%attribute% must be one of {%list%}\"\nmsgstr \"%attribute% deve ser um dos {%list%}\"\n\nmsgid \"%className% is not Serializable\"\nmsgstr \"%className% não é serializável\"\n\nmsgid \"%className% not found. Only jdk types are supported\"\nmsgstr \"%className% não encontrado. Somente tipos de jdk são suportados\"\n\nmsgid \"%value% is not a valid value for %className%\"\nmsgstr \"%value% não é um valor válido para %className%\"\n\nmsgid \"A group with the name %groupName% already exists\"\nmsgstr \"Um grupo com o nome %groupName% já existe\"\n\nmsgid \"An error occurred while creating a definition\"\nmsgstr \"Ocorreu um erro ao criar uma definição\"\n\nmsgid \"An error occurred while deleting an item with the id %id%\"\nmsgstr \"Ocorreu um erro ao excluir um item com o id %id%\"\n\nmsgid \"Bonita Applications\"\nmsgstr \"Aplicações Bonita BPM\"\n\nmsgid \"Can't create group. Group '%groupName%' already exists\"\nmsgstr \"Não é possível criar o grupo. Grupo '%groupName%' já existe\"\n\nmsgid \"Can't create role. Role '%roleName%' already exists\"\nmsgstr \"Não é possível criar a função. Função '%roleName%' já existe\"\n\nmsgid \"Can't create user. User '%userName%' already exists\"\nmsgstr \"Não é possível criar o usuário. Usuário '%userName%' já existe\"\n\nmsgid \"Can't import Applications.\"\nmsgstr \"Não foi possível importar aplicações.\"\n\nmsgid \"Can't import applications. An application '%token%' already exists\"\nmsgstr \"Não é possível importar as aplicações. Uma aplicação 'token %' já existe\"\n\nmsgid \"Can't import organization\"\nmsgstr \"Não é possível importar organização\"\n\nmsgid \"Can't import organization. Please check that your file is well-formed.\"\nmsgstr \"Não é possível importar a organização. Por favor, verifique se o arquivo está bem formatado.\"\n\nmsgid \"Can't parse json, non-well formed content\"\nmsgstr \"Impossível analisar json, o conteúdo não é bem formado\"\n\nmsgid \"Can't start process %processId%, user %userId% not found\"\nmsgstr \"Impossível iniciar o processo %processId%, usuário %userId% não encontrado\"\n\nmsgid \"Can't start process, process %processId% is not enabled\"\nmsgstr \"Não pode iniciar o processo, o processo %processId% não está ativado\"\n\nmsgid \"Can't start process, process %processId% not found\"\nmsgstr \"Não pode iniciar processo, o processo %processId% não foi encontrado\"\n\nmsgid \"Can't update user. User not found\"\nmsgstr \"Impossível atualizar usuário. Usuário não foi encontrado\"\n\nmsgid \"Category with name %categoryName% already exists\"\nmsgstr \"Já existe a categoria com o nome %categoryName%\"\n\nmsgid \"Data definition %dataName% doesn't exists for process %processId%\"\nmsgstr \"Definição do dado %dataName% não existe para o processo %processId%\"\n\nmsgid \"Error during Application import file reading.\"\nmsgstr \"Erro na leitura do arquivo de importação de aplicações.\"\n\nmsgid \"Error occurred when starting process %processId%\"\nmsgstr \"Ocorreu um erro ao iniciar o processo %processId%\"\n\nmsgid \"Error occurred when starting process %processId%. Case creation limit reached.\"\nmsgstr \"Ocorreu um erro ao iniciar o processo %processId%. Limite de criação de casos atingido.\"\n\nmsgid \"Error uploading the file. Maybe your session expired. You can try to refresh the page.\"\nmsgstr \"Erro ao carregar o arquivo. Talvez sua sessão tenha expirado. Você pode tentar atualizar a página.\"\n\nmsgid \"Error when adding group to actor member\"\nmsgstr \"Erro ao adicionar grupo como membro do ator\"\n\nmsgid \"Error when creating group\"\nmsgstr \"Erro ao criar grupo\"\n\nmsgid \"Error when creating user\"\nmsgstr \"Erro ao criar o usuário\"\n\nmsgid \"Error when deleting groups\"\nmsgstr \"Error when deleting groups\"\n\nmsgid \"Error when deleting users\"\nmsgstr \"Erro ao remover usuários\"\n\nmsgid \"Error when pausing BPM services\"\nmsgstr \"Erro ao suspender os serviços BPM\"\n\nmsgid \"Error when resuming BPM services\"\nmsgstr \"Erro ao relançar os serviços BPM\"\n\nmsgid \"Error when searching users\"\nmsgstr \"Erro ao pesquisar usuários\"\n\nmsgid \"Error when updating %activityId% activity variables\"\nmsgstr \"Erro ao atualizar as variáveis de atividade %activityId%\"\n\nmsgid \"Error when updating group\"\nmsgstr \"Erro ao atualizar o grupo\"\n\nmsgid \"Error when updating process deployment informations\"\nmsgstr \"Erro ao atualizar informações de implantação do processo\"\n\nmsgid \"Error when updating user\"\nmsgstr \"Erro ao atualizar usuário\"\n\nmsgid \"Filters are not supported by this API\"\nmsgstr \"Filtros não são suportados nesta API\"\n\nmsgid \"Json can't be mapped to \"\nmsgstr \"Json não pode ser mapeado para \"\n\nmsgid \"Login failed. No profile has been set up for this user. Contact your administrator.\"\nmsgstr \"O seu login falhou. Nenhum perfil foi criado para este usuário. Contate o administrador.\"\n\nmsgid \"Login form\"\nmsgstr \"Formulário de login\"\n\nmsgid \"MM/dd/yyyy\"\nmsgstr \"dd/MM/yyyy\"\n\nmsgid \"MM/dd/yyyy h:mm a\"\nmsgstr \"MM/dd/yyyy hh:mm a\"\n\nmsgid \"MMMM dd, yyyy\"\nmsgstr \"MMMM dd, yyyy\"\n\nmsgid \"Only the value attribute can be updated\"\nmsgstr \"Apenas o valor do atributo pode ser atualizado\"\n\nmsgid \"Password\"\nmsgstr \"Senha \"\n\nmsgid \"Password must be at least %number% characters long\"\nmsgstr \"Senha deve ter pelo menos %number% caracteres\"\n\nmsgid \"Password must contain at least %number% digits\"\nmsgstr \"Senha deve conter pelo menos dígitos %number%\"\n\nmsgid \"Password must contain at least %number% lower case characters\"\nmsgstr \"Senha deve conter pelo menos %number% minúsculas caracteres\"\n\nmsgid \"Password must contain at least %number% special characters\"\nmsgstr \"A senha deve conter pelo menos %number% caracteres especiais\"\n\nmsgid \"Password must contain at least %number% upper case characters\"\nmsgstr \"A senha deve conter pelo menos %number% letras maiúsculas\"\n\nmsgid \"Please, contact your administrator.\"\nmsgstr \"Por favor, contate o administrador.\"\n\nmsgid \"Process %appName% in version %version% already exists\"\nmsgstr \"Processo %appName% na versão %version% já existe\"\n\nmsgid \"Process %appName% in version %version% contains 6.x Legacy artifacts (forms or case overview page). Those are based on Google Web Toolkit (GWT), a technology that is no longer supported by Bonita. To know more, check the documentation.\"\nmsgstr \"Processo %appName% na versão %version% contém artefatos de legado 6. x (formulários ou página visão geral do caso). Aqueles são baseados no Google Web Toolkit (GWT), uma tecnologia que não é mais suportada por Bonita. Para saber mais, consulte a documentação.\"\n\nmsgid \"Process definition not found for id %processId%\"\nmsgstr \"Definição de processo não encontrada para o id %processId%\"\n\nmsgid \"Profile member already exists\"\nmsgstr \"Membro do perfil já existe\"\n\nmsgid \"Request parameter \\\"id\\\" must be set.\"\nmsgstr \"O parâmetro id\\\" da interrogação deve ser definido.\"\n\nmsgid \"Search terms are not supported by this API\"\nmsgstr \"Termos de pesquisa não são suportados nesta API\"\n\nmsgid \"Session expired. Please log in again.\"\nmsgstr \"Sessão expirada. Por favor, conecte-se novamente.\"\n\nmsgid \"Sorting is not supported by this API\"\nmsgstr \"Ordenação não é suportada nesta API\"\n\nmsgid \"The only mandatory filter is %name%\"\nmsgstr \"O único filtro obrigatório é %name%\"\n\nmsgid \"The server is not available\"\nmsgstr \"O servidor não está disponível\"\n\nmsgid \"This category has already been added to this process\"\nmsgstr \"Esta categoria já foi adicionada a este processo\"\n\nmsgid \"This group has already been mapped to actor\"\nmsgstr \"Este grupo já foi designado como ator\"\n\nmsgid \"This membership has already been mapped to actor\"\nmsgstr \"Esta associação já foi designada como ator\"\n\nmsgid \"This membership is already added to user\"\nmsgstr \"Esta associação já foi adicionada ao usuário\"\n\nmsgid \"This role has already been mapped to actor\"\nmsgstr \"Esta função já foi designada como ator\"\n\nmsgid \"This user has already been mapped to actor\"\nmsgstr \"Este usuário já foi designado como ator\"\n\nmsgid \"Too many failed login attempts. Please try again later.\"\nmsgstr \"Muitas tentativas de login falharam. Por favor, tente novamente mais tarde.\"\n\nmsgid \"Unable to deploy business archive\"\nmsgstr \"Não foi possível instalar o arquivo\"\n\nmsgid \"Unable to disable process\"\nmsgstr \"Impossível desativar processo\"\n\nmsgid \"Unable to enable process\"\nmsgstr \"Impossível ativar processo\"\n\nmsgid \"Unable to find data instance %dataName% for activity %activityId%\"\nmsgstr \"Impossível encontrar dados de instância %dataName% da atividade %activityId%\"\n\nmsgid \"Unable to find role %roleId%\"\nmsgstr \"Não é possível localizar a função %roleId%\"\n\nmsgid \"Unable to get group path, group not found\"\nmsgstr \"Não é possível obter o caminho do grupo, grupo não encontrado\"\n\nmsgid \"Unable to get process data definitions, process %processId% not found\"\nmsgstr \"Impossível obter definições de dados do processo, processo %processId% não foi encontrado\"\n\nmsgid \"Unable to log in. Please check your username and password.\"\nmsgstr \"Não foi possível logar. Por favor, verifique seu nome de usuário e senha.\"\n\nmsgid \"User\"\nmsgstr \"Usuário\"\n\nmsgid \"User not found\"\nmsgstr \"Usuário não foi encontrado\"\n\nmsgid \"Welcome to\"\nmsgstr \"Bem-vindo ao\"\n\nmsgid \"message\"\nmsgstr \"mensagem\"\n\nmsgid \"or\"\nmsgstr \"ou\"\n\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/resources/i18n/resources_es.po",
    "content": "msgid \"\"\nmsgstr \"\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Project-Id-Version: bonita\\n\"\n\"Plural-Forms: nplurals=2; plural=(n != 1);\\n\"\n\"X-Crowdin-Project: bonita\\n\"\n\"X-Crowdin-Project-ID: 13316\\n\"\n\"X-Crowdin-Language: es-ES\\n\"\n\"X-Crowdin-File: /dev/bonita-web/distrib/resources.pot\\n\"\n\"X-Crowdin-File-ID: 61547\\n\"\n\"Language-Team: Spanish\\n\"\n\"Language: es_ES\\n\"\n\"PO-Revision-Date: 2024-01-01 00:00\\n\"\n\nmsgid \"Add\"\nmsgstr \"Añadir\"\n\nmsgid \"Attention\"\nmsgstr \"Atención\"\n\nmsgid \"Example:\"\nmsgstr \"Ejemplo:\"\n\nmsgid \"Execute\"\nmsgstr \"Ejecutar\"\n\nmsgid \"Expecting a {} value.\"\nmsgstr \"Esperando un valor {}.\"\n\nmsgid \"Not a valid number!\"\nmsgstr \"No es un número válido!\"\n\nmsgid \"Select a file\"\nmsgstr \"Seleccionar un archivo\"\n\nmsgid \"Start\"\nmsgstr \"Inicio\"\n\nmsgid \"no description defined in contract for this input\"\nmsgstr \"No hay descripción definida en el contrato para esta entrada\"\n\nmsgid \"remove\"\nmsgstr \"Eliminar\"\n\nmsgid \"this is a temporary form generated automatically for testing. Before you put your process into production, create and map the necessary forms.\"\nmsgstr \"Este es un formulario temporal generado automáticamente para la prueba. Antes de poner el proceso en producción, cree y asigne los formularios necesarios.\"\n\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/resources/i18n/resources_fr.po",
    "content": "msgid \"\"\nmsgstr \"\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Project-Id-Version: bonita\\n\"\n\"Plural-Forms: nplurals=2; plural=(n > 1);\\n\"\n\"X-Crowdin-Project: bonita\\n\"\n\"X-Crowdin-Project-ID: 13316\\n\"\n\"X-Crowdin-Language: fr\\n\"\n\"X-Crowdin-File: /dev/bonita-web/distrib/resources.pot\\n\"\n\"X-Crowdin-File-ID: 61547\\n\"\n\"Language-Team: French\\n\"\n\"Language: fr_FR\\n\"\n\"PO-Revision-Date: 2024-01-01 00:00\\n\"\n\nmsgid \"Add\"\nmsgstr \"Ajouter\"\n\nmsgid \"Attention\"\nmsgstr \"Attention\"\n\nmsgid \"Example:\"\nmsgstr \"Exemple :\"\n\nmsgid \"Execute\"\nmsgstr \"Exécuter\"\n\nmsgid \"Expecting a {} value.\"\nmsgstr \"Attend une valeur {}.\"\n\nmsgid \"Not a valid number!\"\nmsgstr \"Nombre invalide !\"\n\nmsgid \"Select a file\"\nmsgstr \"Sélectionnez un fichier\"\n\nmsgid \"Start\"\nmsgstr \"Démarrer\"\n\nmsgid \"no description defined in contract for this input\"\nmsgstr \"Cet input n'a pas de description dans le contrat\"\n\nmsgid \"remove\"\nmsgstr \"Enlever\"\n\nmsgid \"this is a temporary form generated automatically for testing. Before you put your process into production, create and map the necessary forms.\"\nmsgstr \"il s'agit d'un formulaire temporaire généré automatiquement à des fins de test. Avant de passer votre processus en production, vous devrez créer les formulaires définitifs et les associer au processus et aux tâches humaines.\"\n\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/resources/i18n/resources_ja.po",
    "content": "msgid \"\"\nmsgstr \"\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Project-Id-Version: bonita\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"X-Crowdin-Project: bonita\\n\"\n\"X-Crowdin-Project-ID: 13316\\n\"\n\"X-Crowdin-Language: ja\\n\"\n\"X-Crowdin-File: /dev/bonita-web/distrib/resources.pot\\n\"\n\"X-Crowdin-File-ID: 61547\\n\"\n\"Language-Team: Japanese\\n\"\n\"Language: ja_JP\\n\"\n\"PO-Revision-Date: 2024-01-01 00:00\\n\"\n\nmsgid \"Add\"\nmsgstr \"追加\"\n\nmsgid \"Attention\"\nmsgstr \"注意\"\n\nmsgid \"Example:\"\nmsgstr \"例：\"\n\nmsgid \"Execute\"\nmsgstr \"実行\"\n\nmsgid \"Expecting a {} value.\"\nmsgstr \"{} の値を期待しています。\"\n\nmsgid \"Not a valid number!\"\nmsgstr \"有効な数値ではないです\"\n\nmsgid \"Select a file\"\nmsgstr \"ファイルを選択します。\"\n\nmsgid \"Start\"\nmsgstr \"開始\"\n\nmsgid \"no description defined in contract for this input\"\nmsgstr \"この入力項目のコントラクトには、説明が定義されていません\"\n\nmsgid \"remove\"\nmsgstr \"削除\"\n\nmsgid \"this is a temporary form generated automatically for testing. Before you put your process into production, create and map the necessary forms.\"\nmsgstr \"これはテスト用に自動生成された一時的なフォームです。本番環境にプロセスを移行する前に必要なフォームを作成し、マップしてください。\"\n\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/resources/i18n/resources_pt_BR.po",
    "content": "msgid \"\"\nmsgstr \"\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Project-Id-Version: bonita\\n\"\n\"Plural-Forms: nplurals=2; plural=(n != 1);\\n\"\n\"X-Crowdin-Project: bonita\\n\"\n\"X-Crowdin-Project-ID: 13316\\n\"\n\"X-Crowdin-Language: pt-BR\\n\"\n\"X-Crowdin-File: /dev/bonita-web/distrib/resources.pot\\n\"\n\"X-Crowdin-File-ID: 61547\\n\"\n\"Language-Team: Portuguese, Brazilian\\n\"\n\"Language: pt_BR\\n\"\n\"PO-Revision-Date: 2024-01-01 00:00\\n\"\n\nmsgid \"Add\"\nmsgstr \"Adicionar\"\n\nmsgid \"Attention\"\nmsgstr \"Atenção\"\n\nmsgid \"Example:\"\nmsgstr \"Exemplo:\"\n\nmsgid \"Execute\"\nmsgstr \"Executar\"\n\nmsgid \"Expecting a {} value.\"\nmsgstr \"Um valor {} é esperado.\"\n\nmsgid \"Not a valid number!\"\nmsgstr \"Não é um número válido!\"\n\nmsgid \"Select a file\"\nmsgstr \"Selecione um arquivo\"\n\nmsgid \"Start\"\nmsgstr \"Iniciar\"\n\nmsgid \"no description defined in contract for this input\"\nmsgstr \"nenhuma descrição definida no contrato para esta entrada\"\n\nmsgid \"remove\"\nmsgstr \"remover\"\n\nmsgid \"this is a temporary form generated automatically for testing. Before you put your process into production, create and map the necessary forms.\"\nmsgstr \"este é um formulário temporário gerado automaticamente para teste. Antes de colocar o processo em produção, crie e mapeie os formulários necessários.\"\n\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/webapp/403.jsp",
    "content": "<%-- Copyright (C) 2022 BonitaSoft S.A.\n BonitaSoft, 31 rue Gustave Eiffel - 38000 Grenoble\n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation, either version 2.0 of the License, or\n (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with this program.  If not, see <http://www.gnu.org/licenses/>. --%>\n<%@page contentType=\"text/html; charset=UTF-8\"%>\n<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/html\"><head>\n<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">\n  <title>Error 403</title>\n  <meta charset=\"utf-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <link rel=\"stylesheet\" type=\"text/css\" href=\"${pageContext.request.contextPath}/css/bootstrap.min.css\">\n  <link rel=\"stylesheet\" type=\"text/css\" href=\"${pageContext.request.contextPath}/css/error-style.css\">\n </head> \n <body>\n   <div class=\"main-container\">\n    <div class=\"error-title\">\n     <h1 class=\"text-danger\">Oh no...</h1>\n    </div>\n    <div class=\"illustration-container illustration-container-403\">\n     <img alt=\"403 error\" src=\"${pageContext.request.contextPath}/images/403.svg\">\n    </div>\n    <div class=\"error-message\">\n     <h2 class=\"text-primary\">You do not have access to this page or resource.</h2>\n     <p class=\"text-primary\">403 Forbidden</p>\n    </div>\n   </div>\n </body>\n</html>\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/webapp/404.jsp",
    "content": "<%-- Copyright (C) 2022 BonitaSoft S.A.\n BonitaSoft, 31 rue Gustave Eiffel - 38000 Grenoble\n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation, either version 2.0 of the License, or\n (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with this program.  If not, see <http://www.gnu.org/licenses/>. --%>\n<%@page contentType=\"text/html; charset=UTF-8\"%>\n<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/html\"><head>\n<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">\n  <title>Error 404</title>\n  <meta charset=\"utf-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <link rel=\"stylesheet\" type=\"text/css\" href=\"${pageContext.request.contextPath}/css/bootstrap.min.css\">\n  <link rel=\"stylesheet\" type=\"text/css\" href=\"${pageContext.request.contextPath}/css/error-style.css\">\n </head> \n <body>\n   <div class=\"main-container\">\n    <div class=\"error-title\">\n     <h1 class=\"text-danger\">Oops!</h1>\n    </div>\n    <div class=\"illustration-container\">\n     <img alt=\"404 error\" src=\"${pageContext.request.contextPath}/images/404.svg\">\n    </div>\n    <div class=\"error-message\">\n     <h2 class=\"text-primary\">The page you are looking for doesn't exist. The page may have moved or you may have mistyped the address.</h2>\n     <p class=\"text-primary\">404 Page not found</p>\n    </div>\n   </div>\n </body>\n</html>\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/webapp/500.jsp",
    "content": "<%-- Copyright (C) 2022 BonitaSoft S.A.\n BonitaSoft, 31 rue Gustave Eiffel - 38000 Grenoble\n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation, either version 2.0 of the License, or\n (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with this program.  If not, see <http://www.gnu.org/licenses/>. --%>\n<%@page contentType=\"text/html; charset=UTF-8\"%>\n<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/html\"><head>\n<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">\n  <title>Error 500</title>\n  <meta charset=\"utf-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <link rel=\"stylesheet\" type=\"text/css\" href=\"${pageContext.request.contextPath}/css/bootstrap.min.css\">\n  <link rel=\"stylesheet\" type=\"text/css\" href=\"${pageContext.request.contextPath}/css/error-style.css\">\n </head> \n <body>\n   <div class=\"main-container\">\n    <div class=\"error-title\">\n     <h1 class=\"text-danger\">Sorry...</h1>\n     <h2 class=\"text-danger\">It's not you, it's us.</h2>\n    </div>\n    <div class=\"illustration-container\">\n     <img alt=\"500 error\" src=\"${pageContext.request.contextPath}/images/500.svg\">\n    </div>\n    <div class=\"error-message\">\n     <h2 class=\"text-primary\">The server is currently unable to handle this request. Please be patient or try again later.</h2>\n     <p class=\"text-primary\">500 Internal server error</p>\n    </div>\n   </div>\n  </div>  \n </body>\n</html>\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/webapp/503.jsp",
    "content": "<%-- Copyright (C) 2023 BonitaSoft S.A.\n BonitaSoft, 31 rue Gustave Eiffel - 38000 Grenoble\n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation, either version 2.0 of the License, or\n (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with this program.  If not, see <http://www.gnu.org/licenses/>. --%>\n<%@page contentType=\"text/html; charset=UTF-8\"%>\n<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/html\"><head>\n<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">\n  <title>Error 503</title>\n  <meta charset=\"utf-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <link rel=\"stylesheet\" type=\"text/css\" href=\"${pageContext.request.contextPath}/css/bootstrap.min.css\">\n  <link rel=\"stylesheet\" type=\"text/css\" href=\"${pageContext.request.contextPath}/css/error-style.css\">\n </head> \n <body>\n   <div class=\"main-container\">\n    <div class=\"error-title\">\n     <h1 class=\"text-danger\">Under maintenance...</h1>\n     <h2 class=\"text-danger\">Please try again later.</h2>\n    </div>\n    <div class=\"illustration-container\">\n     <img alt=\"503 error\" src=\"${pageContext.request.contextPath}/images/503.svg\">\n    </div>\n    <div class=\"error-message\">\n     <h2 class=\"text-primary\">The server is currently unable to handle this request due to scheduled maintenance.</h2>\n     <p class=\"text-primary\">503 Service Unavailable</p>\n    </div>\n   </div>\n  </div>  \n </body>\n</html>\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/webapp/WEB-INF/errors.html",
    "content": "<!doctype html>\n<html xmlns=\"http://www.w3.org/1999/html\"> \n <head> \n  <title>Error %s</title> \n  <meta charset=\"utf-8\"> \n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <style>\n    html,\n    body {\n        height: 100vh;\n        margin: 0;\n        padding: 0;\n    }\n    body > .container-fluid {\n        height: inherit;\n        display: flex;\n        flex-direction: column;\n    }\n    body > .container-fluid > iframe {\n        flex: 1;\n    }\n  </style>\n </head> \n <body> \n  <div class=\"container-fluid\">\n    <iframe id=\"bonitaframe\" src=\"%s/portal/resource/app/appDirectoryBonita/error-%s/content/?app=appDirectoryBonita\" style=\"border: 0px none;\" width=\"100%%\"></iframe>\n  </div>  \n </body>\n</html>"
  },
  {
    "path": "bpm/bonita-web-server/src/main/webapp/WEB-INF/urlrewrite.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE urlrewrite PUBLIC \"-//tuckey.org//DTD UrlRewrite 4.0//EN\"\n        \"http://www.tuckey.org/res/dtds/urlrewrite4.0.dtd\">\n\n<urlrewrite>\n\n    <!--  redirection to old api servlet  -->\n    <rule>\n        <from>^(/portal/custom-page)?/API/bpm/activity([\\?/]|$)</from>\n        <to>/APIToolkit/bpm/activity$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/bpm/actor([\\?/]|$)</from>\n        <to>/APIToolkit/bpm/actor$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/bpm/actorMember([\\?/]|$)</from>\n        <to>/APIToolkit/bpm/actorMember$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/bpm/archivedActivity([\\?/]|$)</from>\n        <to>/APIToolkit/bpm/archivedActivity$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/bpm/archivedCase([\\?]|$)</from>\n        <to>/APIToolkit/bpm/archivedCase$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/bpm/archivedCase/([^/]*)$</from>\n        <to>/APIToolkit/bpm/archivedCase/$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/bpm/archivedComment([\\?/]|$)</from>\n        <to>/APIToolkit/bpm/archivedComment$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/bpm/archivedConnectorInstance([\\?/]|$)</from>\n        <to>/APIToolkit/bpm/archivedConnectorInstance$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/bpm/archiveddocument([\\?/]|$)</from>\n        <to>/APIToolkit/bpm/archiveddocument$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/bpm/archivedFlowNode([\\?/]|$)</from>\n        <to>/APIToolkit/bpm/archivedFlowNode$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/bpm/archivedHumanTask([\\?/]|$)</from>\n        <to>/APIToolkit/bpm/archivedHumanTask$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/bpm/archivedTask([\\?/]|$)</from>\n        <to>/APIToolkit/bpm/archivedTask$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/bpm/archivedUserTask([\\?]|$)</from>\n        <to>/APIToolkit/bpm/archivedUserTask$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/bpm/archivedUserTask/([^/]*)$</from>\n        <to>/APIToolkit/bpm/archivedUserTask/$2</to>\n    </rule>\n    <rule>\n       <from>^(/portal/custom-page)?/API/bpm/case([\\?]|$)</from>\n       <to>/APIToolkit/bpm/case$2</to>\n    </rule>\n    <rule>\n       <from>^(/portal/custom-page)?/API/bpm/case/([^/]*)$</from>\n       <to>/APIToolkit/bpm/case/$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/bpm/caseDocument([\\?/]|$)</from>\n        <to>/APIToolkit/bpm/caseDocument$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/bpm/archivedCaseDocument([\\?/]|$)</from>\n        <to>/APIToolkit/bpm/archivedCaseDocument$2</to>\n    </rule> \n    <rule>\n        <from>^(/portal/custom-page)?/API/bpm/caseVariable([\\?/]|$)</from>\n        <to>/APIToolkit/bpm/caseVariable$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/bpm/category([\\?/]|$)</from>\n        <to>/APIToolkit/bpm/category$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/bpm/comment([\\?/]|$)</from>\n        <to>/APIToolkit/bpm/comment$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/bpm/connectorInstance([\\?/]|$)</from>\n        <to>/APIToolkit/bpm/connectorInstance$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/bpm/delegation([\\?/]|$)</from>\n        <to>/APIToolkit/bpm/delegation$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/bpm/document([\\?/]|$)</from>\n        <to>/APIToolkit/bpm/document$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/bpm/flowNode([\\?/]|$)</from>\n        <to>/APIToolkit/bpm/flowNode$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/bpm/humanTask([\\?/]|$)</from>\n        <to>/APIToolkit/bpm/humanTask$2</to>\n    </rule>\n    <rule>\n       <from>^(/portal/custom-page)?/API/bpm/process([\\?]|$)</from>\n       <to>/APIToolkit/bpm/process$2</to>\n   </rule>\n    <rule>\n       <from>^(/portal/custom-page)?/API/bpm/process/([^/]*)$</from>\n       <to>/APIToolkit/bpm/process/$2</to>\n   </rule>\n    <rule>\n        <note>Rewrite process instantiation URLs to Spring MVC internal endpoint</note>\n        <from>^(/portal.*)?/API/bpm/process/([^/]+)/instantiation$</from>\n        <to>/APISpringInternal/bpm/process/$2/instantiation</to>\n    </rule>\n    <rule>\n        <note>Rewrite process design URLs to Spring MVC internal endpoint</note>\n        <from>^(/portal.*)?/API/bpm/process/([^/]+)/design$</from>\n        <to>/APISpringInternal/bpm/process/$2/design</to>\n    </rule>\n    <rule>\n        <note>Rewrite process contract URLs to Spring MVC internal endpoint</note>\n        <from>^(/portal.*)?/API/bpm/process/([^/]+)/contract$</from>\n        <to>/APISpringInternal/bpm/process/$2/contract</to>\n    </rule>\n    <rule>\n        <note>Rewrite user task contract URLs to Spring MVC internal endpoint</note>\n        <from>^(/portal.*)?/API/bpm/userTask/([^/]+)/contract$</from>\n        <to>/APISpringInternal/bpm/userTask/$2/contract</to>\n    </rule>\n    <rule>\n        <note>Rewrite user task context URLs to Spring MVC internal endpoint</note>\n        <from>^(/portal.*)?/API/bpm/userTask/([^/]+)/context$</from>\n        <to>/APISpringInternal/bpm/userTask/$2/context</to>\n    </rule>\n    <rule>\n        <note>Rewrite user task execution URLs to Spring MVC internal endpoint</note>\n        <from>^(/portal.*)?/API/bpm/userTask/([^/]+)/execution$</from>\n        <to>/APISpringInternal/bpm/userTask/$2/execution</to>\n    </rule>\n    <rule>\n        <note>Rewrite archived user task context URLs to Spring MVC internal endpoint</note>\n        <from>^(/portal.*)?/API/bpm/archivedUserTask/([^/]+)/context$</from>\n        <to>/APISpringInternal/bpm/archivedUserTask/$2/context</to>\n    </rule>\n    <rule>\n        <note>Rewrite case context URLs to Spring MVC internal endpoint</note>\n        <from>^(/portal.*)?/API/bpm/case/([^/]+)/context$</from>\n        <to>/APISpringInternal/bpm/case/$2/context</to>\n    </rule>\n    <rule>\n        <note>Rewrite archived case context URLs to Spring MVC internal endpoint</note>\n        <from>^(/portal.*)?/API/bpm/archivedCase/([^/]+)/context$</from>\n        <to>/APISpringInternal/bpm/archivedCase/$2/context</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/bpm/processCategory([\\?/]|$)</from>\n        <to>/APIToolkit/bpm/processCategory$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/bpm/processConnector([\\?/]|$)</from>\n        <to>/APIToolkit/bpm/processConnector$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/bpm/processConnectorDependency([\\?/]|$)</from>\n        <to>/APIToolkit/bpm/processConnectorDependency$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/bpm/processParameter([\\?/]|$)</from>\n        <to>/APIToolkit/bpm/processParameter$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/bpm/processResolutionProblem([\\?/]|$)</from>\n        <to>/APIToolkit/bpm/processResolutionProblem$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/bpm/task([\\?/]|$)</from>\n        <to>/APIToolkit/bpm/task$2</to>\n    </rule>\n    <rule>\n       <from>^(/portal/custom-page)?/API/bpm/userTask([\\?]|$)</from>\n       <to>/APIToolkit/bpm/userTask$2</to>\n   </rule>\n    <rule>\n       <from>^(/portal/custom-page)?/API/bpm/userTask/([^/]*)$</from>\n       <to>/APIToolkit/bpm/userTask/$2</to>\n   </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/customuserinfo/definition([\\?/]|$)</from>\n        <to>/APIToolkit/customuserinfo/definition$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/customuserinfo/user([\\?/]|$)</from>\n        <to>/APIToolkit/customuserinfo/user$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/customuserinfo/value([\\?/]|$)</from>\n        <to>/APIToolkit/customuserinfo/value$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/identity/group([\\?/]|$)</from>\n        <to>/APIToolkit/identity/group$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/identity/membership([\\?/]|$)</from>\n        <to>/APIToolkit/identity/membership$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/identity/personalcontactdata([\\?/]|$)</from>\n        <to>/APIToolkit/identity/personalcontactdata$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/identity/professionalcontactdata([\\?/]|$)</from>\n        <to>/APIToolkit/identity/professionalcontactdata$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/identity/role([\\?/]|$)</from>\n        <to>/APIToolkit/identity/role$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/identity/user([\\?/]|$)</from>\n        <to>/APIToolkit/identity/user$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/living/application-page([\\?/]|$)</from>\n        <to>/APIToolkit/living/application-page$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/living/application-menu([\\?/]|$)</from>\n        <to>/APIToolkit/living/application-menu$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/living/application([\\?/]|$)</from>\n        <to>/APIToolkit/living/application$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/platform/platform([\\?/]|$)</from>\n        <to>/APIToolkit/platform/platform$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/portal/page([\\?/]|$)</from>\n        <to>/APIToolkit/portal/page$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/portal/profile([\\?/]|$)</from>\n        <to>/APIToolkit/portal/profile$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/portal/profileMember([\\?/]|$)</from>\n        <to>/APIToolkit/portal/profileMember$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/system/i18nlocale([\\?/]|$)</from>\n        <to>/APIToolkit/system/i18nlocale$2</to>\n    </rule>\n    <rule>\n        <from>^(/portal/custom-page)?/API/system/session([\\?/]|$)</from>\n        <to>/APIToolkit/system/session$2</to>\n    </rule>\n\n    <rule>\n        <note>\n            Deal with custom page resources URL backward compatibility.\n            Also there is a bug with qsappend=\"true\" which always append query string\n            using a &amp;.\n            Therefor the ?fix=true is used to fix that issue.\n        </note>\n        <from>^/portal/custom-page/(.*)/pageResource$</from>\n        <to qsappend=\"true\" type=\"redirect\">%{context-path}/portal/pageResource?fix=true</to>\n    </rule>\n    <rule>\n        <from>^/portal/custom-page/(.*)/fileUpload$</from>\n        <to>/portal/fileUpload</to>\n    </rule>\n    <rule>\n        <from>^/portal/custom-page/(.*)/documentDownload?(.*)$</from>\n        <to qsappend=\"true\">/portal/documentDownload?$2</to>\n    </rule>\n    <rule>\n        <from>^/portal/resource/app/([^\\/]+)/([^\\/]+)/content/pageResource?(.*)$</from>\n        <to qsappend=\"true\">/portal/pageResource?$3</to>\n    </rule>\n    <rule>\n        <from>^/portal/resource/app/([^\\/]+)/([^\\/]+)/content/fileUpload$</from>\n        <to>/portal/fileUpload</to>\n    </rule>\n    <rule>\n        <from>^/portal/resource/app/([^\\/]+)/([^\\/]+)/content/documentDownload?(.*)$</from>\n        <to qsappend=\"true\">/portal/documentDownload?$3</to>\n    </rule>\n\n    <!-- support home URL from versions prior to 7.13 -->\n    <rule>\n        <from>^/portal/homepage</from>\n        <to>/apps/appDirectoryBonita</to>\n    </rule>\n\n</urlrewrite>\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/webapp/WEB-INF/web.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<web-app xmlns=\"http://java.sun.com/xml/ns/javaee\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd\"\n         version=\"3.0\">\n    <display-name>Bonita</display-name>\n\n    <!--  Error pages -->\n    <error-page>\n        <error-code>500</error-code>\n        <location>/error/500</location>\n    </error-page>\n    <error-page>\n        <error-code>503</error-code>\n        <location>/error/503</location>\n    </error-page>\n    <error-page>\n        <error-code>403</error-code>\n        <location>/error/403</location>\n    </error-page>\n    <error-page>\n        <error-code>404</error-code>\n        <location>/error/404</location>\n    </error-page>\n\n    <!-- \n        MDC Filter for requests.\n    -->\n    <filter>\n        <filter-name>RequestIdFilter</filter-name>\n        <filter-class>org.bonitasoft.console.common.server.filter.RequestIdFilter</filter-class>\n    </filter>\n\n    <!-- No Cache Filter -->\n    <filter>\n        <filter-name>NoCacheFilter</filter-name>\n        <filter-class>org.bonitasoft.console.common.server.filter.NoCacheFilter</filter-class>\n    </filter>\n    \n    <!-- Security Filters -->\n    <filter>\n        <filter-name>FrameSecurityFilter</filter-name>\n        <filter-class>org.bonitasoft.console.common.server.login.filter.FrameSecurityFilter</filter-class>\n        <init-param>\n            <param-name>X-Frame-Options</param-name>\n            <param-value>SAMEORIGIN</param-value>\n        </init-param>\n        <init-param>\n            <param-name>Content-Security-Policy</param-name>\n            <param-value>frame-ancestors 'self';</param-value>\n        </init-param>\n        <!--\n            There is no default excludePattern for this filter, but you can uncomment it and add one if you need to\n        -->\n        <!-- init-param>\n            <param-name>excludePattern</param-name>\n            <param-value></param-value>\n        </init-param -->\n    </filter>\n    <filter>\n        <filter-name>ContentTypeSecurityFilter</filter-name>\n        <filter-class>org.bonitasoft.console.common.server.login.filter.ContentTypeSecurityFilter</filter-class>\n        <init-param>\n            <param-name>X-Content-Type-Options</param-name>\n            <param-value>nosniff</param-value>\n        </init-param>\n        <!--\n            There is no default excludePattern for this filter, but you can uncomment it and add one if you need to\n        -->\n        <!-- init-param>\n            <param-name>excludePattern</param-name>\n            <param-value></param-value>\n        </init-param -->\n    </filter>\n    \n    <!--Rest filter -->\n    <filter>\n        <filter-name>RestAPIAuthorizationFilter</filter-name>\n        <filter-class>org.bonitasoft.console.common.server.login.filter.RestAPIAuthorizationFilter</filter-class>\n        <!--\n            We need to let a set of URL accessible in order to serve the translations\n            The excludePattern default value is the one commented out below, but you can uncomment it and add new patterns if you need to\n        -->\n        <!-- init-param>\n            <param-name>excludePattern</param-name>\n            <param-value>^/(bonita/)?((apps/.+/)|(portal/resource/.+/))?(API|APIToolkit)/system/(i18ntranslation|feature)</param-value>\n        </init-param-->\n    </filter>\n    <!-- Token Filter -->\n    <filter>\n        <filter-name>TokenGeneratorFilter</filter-name>\n        <filter-class>org.bonitasoft.console.common.server.login.filter.TokenGeneratorFilter</filter-class>\n    </filter>\n    <!-- Token Validator Filter -->\n    <filter>\n        <filter-name>TokenValidatorFilter</filter-name>\n        <filter-class>org.bonitasoft.console.common.server.login.filter.TokenValidatorFilter</filter-class>\n        <!--\n            We need to let a set of URL accessible in order to handle the translations on the login page and token request\n            The excludePattern default value is the one commented out below, but you can uncomment it and add new patterns if you need to\n        -->\n        <!-- init-param>\n            <param-name>excludePattern</param-name>\n            <param-value>^/(bonita/)?((apps/.+/)|(portal/resource/.+/))?(API|APIToolkit)/system/(i18ntranslation|feature|session)</param-value>\n        </init-param-->\n    </filter>\n    <filter>\n        <filter-name>AuthenticationFilter</filter-name>\n        <filter-class>org.bonitasoft.console.common.server.login.filter.AuthenticationFilter</filter-class>\n        <!--\n            The AuthenticationFilter check credentials when access is requested\n            However, to ensure authentication redirect and/or error handling works properly,\n            we need to let a set of pages not securized :\n            The excludePattern default value is the one commented out below, but you can uncomment it and add new patterns if you need to\n        -->\n        <!-- init-param>\n            <param-name>excludePattern</param-name>\n            <param-value>^/(bonita/)?(?:(login\\.jsp$)|(apps/.+/API/)|(portal/resource/.+/API/))</param-value>\n        </init-param -->\n        <init-param>\n            <param-name>redirectWhenUnauthorized</param-name>\n            <param-value>true</param-value>\n        </init-param>\n    </filter>\n    <!-- Redirect filter -->\n    <filter>\n        <filter-name>RedirectFilter</filter-name>\n        <filter-class>org.bonitasoft.console.common.server.filter.RedirectFilter</filter-class>\n    </filter>\n    <!-- Sanitize filter to prevent code injection -->\n    <filter>\n        <filter-name>SanitizerFilter</filter-name>\n        <filter-class>org.bonitasoft.console.common.server.filter.SanitizerFilter</filter-class>\n    </filter>\n    <!-- Cache Filter -->\n    <filter>\n        <filter-name>CacheFilter</filter-name>\n        <filter-class>org.bonitasoft.console.common.server.filter.CacheFilter</filter-class>\n        <init-param>\n            <param-name>duration</param-name>\n            <param-value>36000</param-value>\n        </init-param>\n        <init-param>\n            <param-name>alwaysCaching</param-name>\n            <param-value>true</param-value>\n        </init-param>\n        <!--\n            We need to let a set of URL not cached (the ones serving the HTML content) in order to handle the sessions timeout\n            The excludePattern default value is the one commented out below, but you can uncomment it and add new patterns if you need to\n        -->\n        <!-- init-param>\n            <param-name>excludePattern</param-name>\n            <param-value>^/(bonita/)?(?:(apps/.+/$)|(portal/resource/.+/content/$)|(portal/custom-page/.+/$)|(portal/custom-page/API/))</param-value>\n        </init-param -->\n    </filter>\n\n    <filter>\n        <filter-name>CustomPageCacheFilter</filter-name>\n        <filter-class>org.bonitasoft.console.common.server.filter.CacheFilter</filter-class>\n        <init-param>\n            <param-name>duration</param-name>\n            <param-value>36000</param-value>\n        </init-param>\n        <init-param>\n            <param-name>alwaysCaching</param-name>\n            <param-value>false</param-value>\n        </init-param>\n        <!--\n            We need to let a set of URL not cached (the ones serving the HTML content) in order to handle the sessions timeout\n            The excludePattern default value is the one commented out below, but you can uncomment it and add new patterns if you need to\n        -->\n        <!-- init-param>\n            <param-name>excludePattern</param-name>\n            <param-value>^/(bonita/)?(?:(apps/.+/$)|(portal/resource/.+/content/$)|(portal/custom-page/.+/$)|(portal/custom-page/API/))</param-value>\n        </init-param -->\n    </filter>\n\n    <filter>\n        <filter-name>UrlRewriteFilter</filter-name>\n        <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>\n        <init-param>\n            <param-name>logLevel</param-name>\n            <param-value>slf4j</param-value>\n        </init-param>\n    </filter>\n\n    <filter-mapping>\n        <filter-name>RequestIdFilter</filter-name>\n        <url-pattern>/*</url-pattern>\n        <dispatcher>REQUEST</dispatcher>\n        <dispatcher>FORWARD</dispatcher>\n    </filter-mapping>\n    <filter-mapping>\n        <filter-name>NoCacheFilter</filter-name>\n        <url-pattern>/portal/formsDocumentDownload</url-pattern>\n        <url-pattern>/portal/formsDocumentImage</url-pattern>\n        <url-pattern>/portal/downloadDocument</url-pattern>\n        <url-pattern>/portal/documentDownload</url-pattern>\n        <url-pattern>/portal/runreport</url-pattern>\n        <url-pattern>/API/*</url-pattern>\n        <url-pattern>/APIToolkit/*</url-pattern>\n        <url-pattern>/APISpringInternal/*</url-pattern>\n        <url-pattern>/portal/custom-page/API/*</url-pattern>\n        <url-pattern>/portal.js/index.html</url-pattern>\n        <!-- New manage on no cache Filter -->\n        <url-pattern>/portal/exportOrganization</url-pattern>\n        <url-pattern>/portal/pageDownload</url-pattern>\n        <url-pattern>/portal/exportActors</url-pattern>\n        <dispatcher>REQUEST</dispatcher>\n        <dispatcher>FORWARD</dispatcher>\n    </filter-mapping>\n    <filter-mapping>\n        <filter-name>FrameSecurityFilter</filter-name>\n        <url-pattern>/*</url-pattern>\n        <dispatcher>REQUEST</dispatcher>\n        <dispatcher>FORWARD</dispatcher>\n    </filter-mapping>\n    <filter-mapping>\n        <filter-name>ContentTypeSecurityFilter</filter-name>\n        <url-pattern>/*</url-pattern>\n        <dispatcher>REQUEST</dispatcher>\n        <dispatcher>FORWARD</dispatcher>\n    </filter-mapping>\n    <filter-mapping>\n        <filter-name>TokenValidatorFilter</filter-name>\n        <url-pattern>/API/*</url-pattern>\n        <url-pattern>/APIToolkit/*</url-pattern>\n        <url-pattern>/APISpringInternal/*</url-pattern>\n        <!--\n            In the case of a custom page served in a custom profile calls to the\n            REST API are done though /portal/custom-page/API\n        -->\n        <url-pattern>/portal/custom-page/API/*</url-pattern>\n        <!--\n            In the case of a form or in the case of a custom page served in a living application\n            REST API are done though /portal/resource/*/API or /apps/*/API (layout)\n        -->\n        <url-pattern>/portal/resource/*</url-pattern>\n        <url-pattern>/apps/*</url-pattern>\n        <dispatcher>REQUEST</dispatcher>\n        <dispatcher>FORWARD</dispatcher>\n    </filter-mapping>\n    <filter-mapping>\n        <filter-name>AuthenticationFilter</filter-name>\n        <url-pattern>/portal/*</url-pattern>\n        <url-pattern>/portal.js/*</url-pattern>\n        <url-pattern>/apps/*</url-pattern>\n        <url-pattern>/services/*</url-pattern>\n        <dispatcher>REQUEST</dispatcher>\n        <dispatcher>FORWARD</dispatcher>\n    </filter-mapping>\n    <filter-mapping>\n        <filter-name>RedirectFilter</filter-name>\n        <url-pattern>/apps/sendRedirect</url-pattern>\n        <dispatcher>REQUEST</dispatcher>\n        <dispatcher>FORWARD</dispatcher>\n    </filter-mapping>\n    <filter-mapping>\n        <filter-name>RestAPIAuthorizationFilter</filter-name>\n        <url-pattern>/API/*</url-pattern>\n        <url-pattern>/APIToolkit/*</url-pattern>\n        <url-pattern>/APISpringInternal/*</url-pattern>\n        <!-- see TokenValidatorFilter comment -->\n        <url-pattern>/portal/custom-page/API/*</url-pattern>\n        <url-pattern>/services/*</url-pattern>\n        <url-pattern>/portal/formsDocumentDownload</url-pattern>\n        <url-pattern>/portal/documentDownload</url-pattern>\n        <url-pattern>/portal/downloadDocument</url-pattern>\n        <url-pattern>/portal/pageDownload</url-pattern>\n        <url-pattern>/portal/exportOrganization</url-pattern>\n        <url-pattern>/portal/fileUpload</url-pattern>\n        <url-pattern>/portal/processUpload</url-pattern>\n        <url-pattern>/portal/organizationUpload</url-pattern>\n        <url-pattern>/portal/actorsUpload</url-pattern>\n        <url-pattern>/portal/applicationsUpload</url-pattern>\n        <url-pattern>/portal/pageUpload</url-pattern>\n        <url-pattern>/portal/imageUpload</url-pattern>\n        <!--  Add more servlet mappings here -->\n        <dispatcher>REQUEST</dispatcher>\n        <dispatcher>FORWARD</dispatcher>\n        <dispatcher>INCLUDE</dispatcher>\n    </filter-mapping>\n    <filter-mapping>\n        <filter-name>TokenGeneratorFilter</filter-name>\n        <url-pattern>/API/system/session/*</url-pattern>\n        <url-pattern>/APIToolkit/system/session/*</url-pattern>\n        <!-- see TokenValidatorFilter comment -->\n        <url-pattern>/portal/custom-page/API/system/session/*</url-pattern>\n        <dispatcher>FORWARD</dispatcher>\n    </filter-mapping>\n    <filter-mapping>\n        <filter-name>SanitizerFilter</filter-name>\n        <url-pattern>/API/*</url-pattern>\n        <url-pattern>/APIToolkit/*</url-pattern>\n        <url-pattern>/APISpringInternal/*</url-pattern>\n        <dispatcher>REQUEST</dispatcher>\n        <dispatcher>FORWARD</dispatcher>\n    </filter-mapping>\n    <!-- Cache Filter Mapping Start -->\n    <filter-mapping>\n        <filter-name>CacheFilter</filter-name>\n        <url-pattern>/login.jsp</url-pattern>\n        <url-pattern>/platformloginservice</url-pattern>\n        <url-pattern>/platformlogoutservice</url-pattern>\n        <url-pattern>/API/system/i18ntranslation</url-pattern>\n        <url-pattern>/API/avatars/*</url-pattern>\n        <url-pattern>/API/applicationIcon/*</url-pattern>\n        <!-- War -->\n        <url-pattern>/css</url-pattern>\n        <url-pattern>/images</url-pattern>\n        <url-pattern>/portal-theme</url-pattern>\n        <url-pattern>/portal.js/*</url-pattern>\n        <dispatcher>REQUEST</dispatcher>\n        <dispatcher>FORWARD</dispatcher>\n    </filter-mapping>\n\n    <filter-mapping>\n        <filter-name>CustomPageCacheFilter</filter-name>\n        <url-pattern>/portal/resource/*</url-pattern>\n        <url-pattern>/portal/resource/app/*</url-pattern>\n        <url-pattern>/apps/*</url-pattern>\n        <url-pattern>/portal/custom-page/*</url-pattern>\n        <dispatcher>REQUEST</dispatcher>\n        <dispatcher>FORWARD</dispatcher>\n    </filter-mapping>\n\n    <!-- Cache Filter Mapping End -->\n    <!--  Filter for Toolkit API URLs -->\n    <filter-mapping>\n        <filter-name>UrlRewriteFilter</filter-name>\n        <url-pattern>/*</url-pattern>\n        <dispatcher>REQUEST</dispatcher>\n        <dispatcher>FORWARD</dispatcher>\n    </filter-mapping>\n    <!-- End Filter for Toolkit API URLs -->\n    <!-- Platform and tenant listeners -->\n    <!-- For Apps containing the engine server -->\n    <listener>\n        <listener-class>org.bonitasoft.engine.api.internal.servlet.EngineInitializerListener</listener-class>\n    </listener>\n    <listener>\n        <listener-class>org.bonitasoft.console.common.server.servlet.PlatformTenantListener</listener-class>\n    </listener>\n\n    <servlet>\n        <servlet-name>errorPageServlet</servlet-name>\n        <servlet-class>org.bonitasoft.console.common.server.servlet.ErrorPageServlet</servlet-class>\n    </servlet>\n\n    <servlet>\n        <servlet-name>BonitaRestAPIServlet</servlet-name>\n        <servlet-class>org.bonitasoft.web.rest.server.BonitaRestAPIServlet</servlet-class>\n    </servlet>\n    <servlet>\n        <servlet-name>SpringRest</servlet-name>\n        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>\n        <init-param>\n            <param-name>contextClass</param-name>\n            <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>\n        </init-param>\n        <init-param>\n            <param-name>contextConfigLocation</param-name>\n            <param-value>org.bonitasoft.web.rest.server.SpringWebConfiguration</param-value>\n        </init-param>\n        <load-on-startup>1</load-on-startup>\n    </servlet>\n    <servlet>\n        <servlet-name>ConsoleServiceServlet</servlet-name>\n        <servlet-class>org.bonitasoft.console.server.ConsoleServiceServlet</servlet-class>\n    </servlet>\n    <!-- Portal file upload servlets -->\n    <servlet>\n        <servlet-name>fileUploadServlet</servlet-name>\n        <servlet-class>org.bonitasoft.console.common.server.servlet.TenantFileUploadServlet</servlet-class>\n        <init-param>\n            <param-name>CheckUploadedFileSize</param-name>\n            <param-value>true</param-value>\n        </init-param>\n    </servlet>\n    <servlet>\n        <servlet-name>formFileUploadServlet</servlet-name>\n        <servlet-class>org.bonitasoft.console.common.server.servlet.TenantFileUploadServlet</servlet-class>\n        <init-param>\n            <param-name>ContentType</param-name>\n            <param-value>json</param-value>\n        </init-param>\n        <init-param>\n            <param-name>ReturnOriginalFilename</param-name>\n            <param-value>true</param-value>\n        </init-param>\n        <init-param>\n            <param-name>CheckUploadedFileSize</param-name>\n            <param-value>true</param-value>\n        </init-param>\n    </servlet>\n    <!-- @deprecated as of 9.0.0, Process should be created/updated at startup -->\n    <servlet>\n        <servlet-name>processUploadServlet</servlet-name>\n        <servlet-class>org.bonitasoft.console.common.server.servlet.TenantFileUploadServlet</servlet-class>\n        <init-param>\n            <param-name>SupportedExtensions</param-name>\n            <param-value>bar</param-value>\n        </init-param>\n    </servlet>\n    <!-- @deprecated as of 9.0.0, Process should be created/updated at startup -->\n    <servlet>\n        <servlet-name>apiProcessUploadServlet</servlet-name>\n        <servlet-class>org.bonitasoft.console.common.server.servlet.TenantFileUploadServlet</servlet-class>\n        <init-param>\n            <param-name>SupportedExtensions</param-name>\n            <param-value>bar</param-value>\n        </init-param>\n        <init-param>\n            <param-name>ReturnOriginalFilename</param-name>\n            <param-value>true</param-value>\n        </init-param>\n        <init-param>\n            <param-name>ContentType</param-name>\n            <param-value>json</param-value>\n        </init-param>\n    </servlet>\n    <servlet>\n        <servlet-name>xmlUploadServlet</servlet-name>\n        <servlet-class>org.bonitasoft.console.common.server.servlet.TenantFileUploadServlet</servlet-class>\n        <init-param>\n            <param-name>SupportedExtensions</param-name>\n            <param-value>xml</param-value>\n        </init-param>\n    </servlet>\n    <servlet>\n        <servlet-name>zipUploadServlet</servlet-name>\n        <servlet-class>org.bonitasoft.console.common.server.servlet.TenantFileUploadServlet</servlet-class>\n        <init-param>\n            <param-name>SupportedExtensions</param-name>\n            <param-value>zip</param-value>\n        </init-param>\n    </servlet>\n    <servlet>\n        <servlet-name>imageUploadServlet</servlet-name>\n        <servlet-class>org.bonitasoft.console.common.server.servlet.TenantFileUploadServlet</servlet-class>\n        <init-param>\n            <param-name>SupportedExtensions</param-name>\n            <param-value>png,jpg,gif,jpeg,bmp,wbmp,tga</param-value>\n        </init-param>\n        <init-param>\n            <param-name>CheckUploadedImageSize</param-name>\n            <param-value>true</param-value>\n        </init-param>\n    </servlet>\n    <servlet>\n        <servlet-name>apiImageUploadServlet</servlet-name>\n        <servlet-class>org.bonitasoft.console.common.server.servlet.TenantFileUploadServlet</servlet-class>\n        <init-param>\n            <param-name>SupportedExtensions</param-name>\n            <param-value>png,jpg,gif,jpeg,bmp,wbmp,tga</param-value>\n        </init-param>\n        <init-param>\n            <param-name>ContentType</param-name>\n            <param-value>json</param-value>\n        </init-param>\n        <init-param>\n            <param-name>ReturnOriginalFilename</param-name>\n            <param-value>true</param-value>\n        </init-param>\n        <init-param>\n            <param-name>CheckUploadedImageSize</param-name>\n            <param-value>true</param-value>\n        </init-param>\n    </servlet>\n\n    <servlet>\n        <servlet-name>organizationIconServlet</servlet-name>\n        <servlet-class>org.bonitasoft.console.common.server.servlet.OrganizationIconServlet</servlet-class>\n    </servlet>\n    <servlet>\n        <servlet-name>applicationIconServlet</servlet-name>\n        <servlet-class>org.bonitasoft.console.common.server.servlet.ApplicationIconServlet</servlet-class>\n    </servlet>\n    <servlet>\n        <servlet-name>loginService</servlet-name>\n        <servlet-class>org.bonitasoft.console.common.server.login.servlet.LoginServlet</servlet-class>\n    </servlet>\n    <servlet>\n        <servlet-name>logoutService</servlet-name>\n        <servlet-class>org.bonitasoft.console.common.server.login.servlet.LogoutServlet</servlet-class>\n    </servlet>\n    <servlet>\n        <servlet-name>platformLoginService</servlet-name>\n        <servlet-class>org.bonitasoft.console.common.server.login.servlet.PlatformLoginServlet</servlet-class>\n    </servlet>\n    <servlet>\n        <servlet-name>platformLogoutService</servlet-name>\n        <servlet-class>org.bonitasoft.console.common.server.login.servlet.PlatformLogoutServlet</servlet-class>\n    </servlet>\n    <servlet>\n        <servlet-name>exportOrganizationServlet</servlet-name>\n        <servlet-class>org.bonitasoft.console.server.servlet.OrganizationExportServlet</servlet-class>\n    </servlet>\n    <servlet>\n        <servlet-name>exportApplicationsServlet</servlet-name>\n        <servlet-class>org.bonitasoft.console.server.servlet.ApplicationsExportServlet</servlet-class>\n    </servlet>\n\n    <!-- download -->\n    <servlet>\n        <servlet-name>deprecatedDocumentDownloadServlet</servlet-name>\n        <servlet-class>org.bonitasoft.console.common.server.servlet.DocumentDownloadServlet</servlet-class>\n    </servlet>\n    <servlet>\n        <servlet-name>documentDownload</servlet-name>\n        <servlet-class>org.bonitasoft.console.common.server.servlet.DocumentDownloadServlet</servlet-class>\n    </servlet>\n    <servlet>\n        <servlet-name>formsDocumentDownload</servlet-name>\n        <servlet-class>org.bonitasoft.console.common.server.servlet.DocumentDownloadServlet</servlet-class>\n    </servlet>\n    <servlet>\n        <servlet-name>formsDocumentImage</servlet-name>\n        <servlet-class>org.bonitasoft.console.common.server.servlet.DocumentImageServlet</servlet-class>\n    </servlet>\n    <servlet-mapping>\n        <servlet-name>exportOrganizationServlet</servlet-name>\n        <url-pattern>/portal/exportOrganization</url-pattern>\n        <url-pattern>/portal/custom-page/API/exportOrganization</url-pattern>\n        <url-pattern>/API/exportOrganization</url-pattern>\n    </servlet-mapping>\n    <servlet-mapping>\n        <servlet-name>exportApplicationsServlet</servlet-name>\n        <url-pattern>/portal/exportApplications</url-pattern>\n    </servlet-mapping>\n    <servlet>\n        <servlet-name>exportProcessActorsServlet</servlet-name>\n        <servlet-class>org.bonitasoft.console.server.servlet.ProcessActorsExportServlet</servlet-class>\n    </servlet>\n    <!-- Pages -->\n    <servlet>\n        <servlet-name>CustomPageServlet</servlet-name>\n        <servlet-class>org.bonitasoft.console.common.server.page.CustomPageServlet</servlet-class>\n    </servlet>\n    <servlet>\n        <servlet-name>pageDownload</servlet-name>\n        <servlet-class>org.bonitasoft.console.common.server.page.PageDownloadServlet</servlet-class>\n    </servlet>\n    <servlet>\n        <servlet-name>pageUploadServlet</servlet-name>\n        <servlet-class>org.bonitasoft.console.common.server.servlet.PageUploadServlet</servlet-class>\n        <init-param>\n            <param-name>SupportedExtensions</param-name>\n            <param-value>zip</param-value>\n        </init-param>\n        <init-param>\n            <param-name>ReturnOriginalFilename</param-name>\n            <param-value>true</param-value>\n        </init-param>\n    </servlet>\n    <servlet>\n        <servlet-name>apiPageUploadServlet</servlet-name>\n        <servlet-class>org.bonitasoft.console.common.server.servlet.PageUploadServlet</servlet-class>\n        <init-param>\n            <param-name>SupportedExtensions</param-name>\n            <param-value>zip</param-value>\n        </init-param>\n        <init-param>\n            <param-name>ContentType</param-name>\n            <param-value>json</param-value>\n        </init-param>\n        <init-param>\n            <param-name>ReturnOriginalFilename</param-name>\n            <param-value>true</param-value>\n        </init-param>\n    </servlet>\n    <!-- End Pages -->\n    <!-- Forms -->\n    <servlet>\n        <servlet-name>ProcessFormServlet</servlet-name>\n        <servlet-class>org.bonitasoft.console.common.server.form.ProcessFormServlet</servlet-class>\n    </servlet>\n    <servlet>\n        <servlet-name>livingApplicationServlet</servlet-name>\n        <servlet-class>org.bonitasoft.livingapps.LivingApplicationServlet</servlet-class>\n    </servlet>\n    <servlet>\n        <servlet-name>livingApplicationPageServlet</servlet-name>\n        <servlet-class>org.bonitasoft.livingapps.LivingApplicationPageServlet</servlet-class>\n    </servlet>\n    <servlet>\n        <servlet-name>PageServlet</servlet-name>\n        <servlet-class>org.bonitasoft.console.common.server.page.PageServlet</servlet-class>\n    </servlet>\n    <!-- For engine HTTP API -->\n    <servlet>\n        <servlet-name>HttpAPIServlet</servlet-name>\n        <servlet-class>org.bonitasoft.engine.api.internal.servlet.HttpAPIServlet</servlet-class>\n    </servlet>\n\n    <servlet-mapping>\n        <servlet-name>errorPageServlet</servlet-name>\n        <url-pattern>/error/*</url-pattern>\n    </servlet-mapping>\n\n    <servlet-mapping>\n        <servlet-name>livingApplicationServlet</servlet-name>\n        <url-pattern>/apps/*</url-pattern>\n    </servlet-mapping>\n    <servlet-mapping>\n        <servlet-name>livingApplicationPageServlet</servlet-name>\n        <url-pattern>/portal/resource/app/*</url-pattern>\n    </servlet-mapping>\n    <!-- @deprecated as of 9.0.0, Process should be created/updated at startup -->\n    <servlet-mapping>\n        <servlet-name>processUploadServlet</servlet-name>\n        <url-pattern>/portal/processUpload</url-pattern>\n    </servlet-mapping>\n    <!-- @deprecated as of 9.0.0, Process should be created/updated at startup -->\n    <servlet-mapping>\n        <servlet-name>apiProcessUploadServlet</servlet-name>\n        <url-pattern>/API/processUpload</url-pattern>\n    </servlet-mapping>\n    <servlet-mapping>\n        <servlet-name>xmlUploadServlet</servlet-name>\n        <url-pattern>/portal/organizationUpload</url-pattern>\n    </servlet-mapping>\n    <!-- @deprecated as of 9.0.0, Applications should be created/updated at startup -->\n    <servlet-mapping>\n        <servlet-name>xmlUploadServlet</servlet-name>\n        <url-pattern>/portal/applicationsUpload</url-pattern>\n    </servlet-mapping>\n    <!-- @deprecated as of 9.0.0, Actors should be created/updated at startup -->\n    <servlet-mapping>\n        <servlet-name>xmlUploadServlet</servlet-name>\n        <url-pattern>/portal/actorsUpload</url-pattern>\n    </servlet-mapping>\n    <!-- @deprecated as of 9.0.0, BDM should be created/updated at startup -->\n    <servlet-mapping>\n        <servlet-name>zipUploadServlet</servlet-name>\n        <url-pattern>/portal/bdmUpload</url-pattern>\n    </servlet-mapping>\n    <!-- @deprecated as of 9.0.0, Application page should be created/updated at startup -->\n    <servlet-mapping>\n        <servlet-name>pageUploadServlet</servlet-name>\n        <url-pattern>/portal/pageUpload</url-pattern>\n    </servlet-mapping>\n    <!-- @deprecated as of 9.0.0, Application page should be created/updated at startup -->\n    <servlet-mapping>\n        <servlet-name>apiPageUploadServlet</servlet-name>\n        <url-pattern>/API/pageUpload</url-pattern>\n    </servlet-mapping>\n    <servlet-mapping>\n        <servlet-name>imageUploadServlet</servlet-name>\n        <url-pattern>/portal/imageUpload</url-pattern>\n    </servlet-mapping>\n    <servlet-mapping>\n        <servlet-name>apiImageUploadServlet</servlet-name>\n        <url-pattern>/API/imageUpload</url-pattern>\n    </servlet-mapping>\n    <servlet-mapping>\n        <servlet-name>fileUploadServlet</servlet-name>\n        <url-pattern>/portal/fileUpload</url-pattern>\n    </servlet-mapping>\n    <!-- @deprecated as of 9.0.0, Process Form should be created/updated at startup -->\n    <servlet-mapping>\n        <servlet-name>formFileUploadServlet</servlet-name>\n        <url-pattern>/API/formFileUpload</url-pattern>\n        <url-pattern>/portal/custom-page/API/formFileUpload</url-pattern>\n    </servlet-mapping>\n    <servlet-mapping>\n        <servlet-name>exportProcessActorsServlet</servlet-name>\n        <url-pattern>/portal/exportActors</url-pattern>\n    </servlet-mapping>\n    <servlet-mapping>\n        <servlet-name>organizationIconServlet</servlet-name>\n        <url-pattern>/API/avatars/*</url-pattern>\n        <url-pattern>/portal/custom-page/API/avatars/*</url-pattern>\n    </servlet-mapping>\n    <servlet-mapping>\n        <servlet-name>applicationIconServlet</servlet-name>\n        <url-pattern>/API/applicationIcon/*</url-pattern>\n    </servlet-mapping>\n    <servlet-mapping>\n        <servlet-name>deprecatedDocumentDownloadServlet</servlet-name>\n        <url-pattern>/portal/downloadDocument</url-pattern>\n    </servlet-mapping>\n    <servlet-mapping>\n        <servlet-name>documentDownload</servlet-name>\n        <url-pattern>/portal/documentDownload</url-pattern>\n        <url-pattern>/API/documentDownload</url-pattern>\n        <url-pattern>/portal/custom-page/API/documentDownload</url-pattern>\n    </servlet-mapping>\n    <servlet-mapping>\n        <servlet-name>formsDocumentDownload</servlet-name>\n        <url-pattern>/portal/formsDocumentDownload</url-pattern>\n    </servlet-mapping>\n    <servlet-mapping>\n        <servlet-name>formsDocumentImage</servlet-name>\n        <url-pattern>/portal/formsDocumentImage</url-pattern>\n        <url-pattern>/API/formsDocumentImage</url-pattern>\n        <url-pattern>/portal/custom-page/API/formsDocumentImage</url-pattern>\n    </servlet-mapping>\n    <servlet-mapping>\n        <servlet-name>loginService</servlet-name>\n        <url-pattern>/loginservice</url-pattern>\n    </servlet-mapping>\n    <servlet-mapping>\n        <servlet-name>logoutService</servlet-name>\n        <url-pattern>/logoutservice</url-pattern>\n    </servlet-mapping>\n    <servlet-mapping>\n        <servlet-name>platformLoginService</servlet-name>\n        <url-pattern>/platformloginservice</url-pattern>\n    </servlet-mapping>\n    <servlet-mapping>\n        <servlet-name>platformLogoutService</servlet-name>\n        <url-pattern>/platformlogoutservice</url-pattern>\n    </servlet-mapping>\n    <servlet-mapping>\n        <servlet-name>BonitaRestAPIServlet</servlet-name>\n        <url-pattern>/APIToolkit/*</url-pattern>\n    </servlet-mapping>\n    <servlet-mapping>\n        <servlet-name>SpringRest</servlet-name>\n        <!-- API resource URLs served by Spring MVC DispatcherServlet -->\n        <!-- /APISpringInternal/* serves as a workaround on Tomcat limitation on URL pattern with /* in the middle. See urlrewrite.xml for redirections: -->\n        <url-pattern>/APISpringInternal/*</url-pattern>\n        <url-pattern>/portal/custom-page/API/extension/*</url-pattern>\n        <url-pattern>/API/bdm/businessDataReference/*</url-pattern>\n        <url-pattern>/API/bdm/businessData/*</url-pattern>\n        <url-pattern>/API/bpm/activityVariable/*</url-pattern>\n        <url-pattern>/API/bpm/archivedActivityVariable/*</url-pattern>\n        <url-pattern>/API/bpm/archivedCaseVariable/*</url-pattern>\n        <url-pattern>/API/bpm/message</url-pattern>\n        <url-pattern>/API/bpm/processInfo/*</url-pattern>\n        <url-pattern>/API/bpm/signal</url-pattern>\n        <url-pattern>/API/bpm/timerEventTrigger/*</url-pattern>\n        <url-pattern>/API/extension/*</url-pattern>\n        <url-pattern>/API/form/mapping</url-pattern>\n        <url-pattern>/API/system/i18ntranslation</url-pattern>\n        <url-pattern>/API/system/information</url-pattern>\n        <url-pattern>/API/system/maintenance</url-pattern>\n        <url-pattern>/API/tenant/bdm</url-pattern>\n    </servlet-mapping>\n    <servlet-mapping>\n        <servlet-name>ConsoleServiceServlet</servlet-name>\n        <url-pattern>/services/*</url-pattern>\n        <url-pattern>/API/services/*</url-pattern>\n        <url-pattern>/portal/custom-page/API/services/*</url-pattern>\n    </servlet-mapping>\n    <servlet-mapping>\n        <servlet-name>CustomPageServlet</servlet-name>\n        <url-pattern>/portal/custom-page/*</url-pattern>\n    </servlet-mapping>\n    <servlet-mapping>\n        <servlet-name>pageDownload</servlet-name>\n        <url-pattern>/portal/pageDownload</url-pattern>\n        <url-pattern>/API/pageDownload</url-pattern>\n    </servlet-mapping>\n    <servlet-mapping>\n        <servlet-name>ProcessFormServlet</servlet-name>\n        <url-pattern>/portal/form/*</url-pattern>\n    </servlet-mapping>\n    <servlet-mapping>\n        <servlet-name>PageServlet</servlet-name>\n        <url-pattern>/portal/resource/*</url-pattern>\n    </servlet-mapping>\n    <!-- For engine HTTP API -->\n    <servlet-mapping>\n        <servlet-name>HttpAPIServlet</servlet-name>\n        <url-pattern>/serverAPI/*</url-pattern>\n    </servlet-mapping>\n\n    <!-- Container Resources -->\n    <resource-ref>\n        <res-ref-name>java:comp/env/RawBonitaDS</res-ref-name>\n        <res-type>javax.sql.DataSource</res-type>\n        <res-auth>Container</res-auth>\n    </resource-ref>\n    <resource-ref>\n        <res-ref-name>java:comp/env/bonitaDS</res-ref-name>\n        <res-type>javax.sql.DataSource</res-type>\n        <res-auth>Container</res-auth>\n    </resource-ref>\n    <resource-ref>\n        <res-ref-name>java:comp/env/bonitaSequenceManagerDS</res-ref-name>\n        <res-type>javax.sql.DataSource</res-type>\n        <res-auth>Container</res-auth>\n    </resource-ref>\n    <resource-ref>\n        <res-ref-name>java:comp/env/RawBusinessDataDS</res-ref-name>\n        <res-type>javax.sql.DataSource</res-type>\n        <res-auth>Container</res-auth>\n    </resource-ref>\n    <resource-ref>\n        <res-ref-name>java:comp/env/BusinessDataDS</res-ref-name>\n        <res-type>javax.sql.DataSource</res-type>\n        <res-auth>Container</res-auth>\n    </resource-ref>\n    <resource-ref>\n        <res-ref-name>java:comp/env/NotManagedBizDataDS</res-ref-name>\n        <res-type>javax.sql.DataSource</res-type>\n        <res-auth>Container</res-auth>\n    </resource-ref>\n\n    <!-- Default page to serve -->\n    <welcome-file-list>\n        <welcome-file>index.html</welcome-file>\n    </welcome-file-list>\n\n    <security-constraint>\n        <web-resource-collection>\n            <web-resource-name>bonita-http-api-url</web-resource-name>\n            <url-pattern>/serverAPI/*</url-pattern>\n        </web-resource-collection>\n        <auth-constraint>\n            <role-name>bonita-http-api</role-name>\n        </auth-constraint>\n    </security-constraint>\n\n    <login-config>\n        <auth-method>BASIC</auth-method>\n        <realm-name>Restricted access</realm-name>\n    </login-config>\n\n    <security-role>\n        <role-name>bonita-http-api</role-name>\n    </security-role>\n    \n</web-app>\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/webapp/css/error-style.css",
    "content": "* {\n    margin: 0;\n    padding: 0;\n}\n\nhtml {\n    font-size: 1rem;\n}\n\nbody {\n    background-image: url(../images/background.svg);\n    background-size: cover;\n    background-repeat: no-repeat;\n}\n\n.main-container {\n    position: absolute;\n    top: 50%;\n    left: 50%;\n    transform: translate(-50%, -50%);\n}\n\n.text-primary {\n    color: #2c3e50;\n}\n\n.error-title {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    flex-direction: column;\n}\n\n.error-title h1 {\n    font-weight: bold;\n    font-size: 7rem;\n}\n\n.error-title h2 {\n    font-size: 3rem;\n}\n\n.error-message {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    flex-direction: column;\n    padding: 1rem;\n    text-align: center;\n}\n\n.error-message h2 {\n    font-size: 2rem;\n}\n\n.error-message p {\n    font-size: 1rem;\n    line-height: 1.3rem;\n    padding: 1rem;\n}\n\n.illustration-container {\n    height: 25rem;\n    width: 25rem;\n    position: absolute;\n    top: 0;\n    left: -15rem;\n    z-index: -5;\n}\n\n.illustration-container img {\n    height: 100%;\n    width: 100%;\n}\n\n.illustration-container-403 {\n    left: auto;\n    right: -15rem;\n}\n\n@media screen and (min-width: 768px) and (max-width: 991px) {\n    .illustration-container-403 {\n        left: 18rem;\n    }\n\n    .illustration-container {\n        height: 20rem;\n        width: 20rem;\n    }\n}\n\n@media screen and (max-width: 767px) {\n    .main-container {\n        width: 100%;\n    }\n\n    .error-title h1 {\n        font-size: 5rem;\n    }\n\n    .illustration-container {\n        position: relative;\n        height: 15rem;\n        width: 15rem;\n        margin: 0 auto;\n        right: 0;\n        left: auto;\n    }\n\n    .error-message h2 {\n        font-size: 1.5rem;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/webapp/index.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<html>\n\t<head>\n\t\t<script type=\"text/javascript\">\n\t\t\twindow.location = \"apps/appDirectoryBonita\" + window.location.search + window.location.hash;\n\t\t</script>\n\t</head>\n</html>\n"
  },
  {
    "path": "bpm/bonita-web-server/src/main/webapp/login.jsp",
    "content": "<%-- Copyright (C) 2009 BonitaSoft S.A.\n BonitaSoft, 31 rue Gustave Eiffel - 38000 Grenoble\n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation, either version 2.0 of the License, or\n (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with this program.  If not, see <http://www.gnu.org/licenses/>. --%>\n<%@page language=\"java\"%>\n<%@page contentType=\"text/html; charset=UTF-8\"%>\n<%@page import=\"java.net.URLEncoder\"%>\n<%@page import=\"org.apache.commons.lang3.StringEscapeUtils\"%>\n<%@page import=\"org.bonitasoft.console.common.server.jsp.JSPUtils\"%>\n<%@page import=\"org.bonitasoft.console.common.server.jsp.JSPI18n\"%>\n<%\n    JSPUtils JSP = new JSPUtils(request, session);\n    JSPI18n i18n = new JSPI18n(JSP);\n\n    // Build Action URL\n    String redirectUrl = JSP.getParameter(\"redirectUrl\");\n\n    StringBuffer actionUrl = new StringBuffer(\"loginservice?redirect=true\");\n    StringBuffer styleUrl = new StringBuffer(\"portal-theme\");\n\n    if (redirectUrl != null) {\n        actionUrl.append(\"&redirectUrl=\" + URLEncoder.encode(redirectUrl, \"UTF-8\"));\n    }\n\n    // Error messages\n    String errorMessage = \"\";\n    boolean disableLogin = false;\n    String noBonitaHomeMessage = request.getAttribute(\"noBonitaHomeMessage\") + \"\";\n\tString noBonitaClientFileMessage = request.getAttribute(\"noBonitaClientFileMessage\") + \"\";\n\tString loginFailMessage = request.getAttribute(\"loginFailMessage\") + \"\";\n\n    // Technical problems\n    if (\n        !JSP.getParameter(\"isPlatformCreated\", true) ||\n\t\t!JSP.getParameter(\"isTenantCreated\", true) ||\n\t\t\"tenantNotActivated\".equals(loginFailMessage) ||\n\t\t\"noBonitaHomeMessage\".equals(noBonitaHomeMessage) ||\n\t\t\"noBonitaClientFileMessage\".equals(noBonitaClientFileMessage)\n\t) {\n        errorMessage = i18n.t_(\"The server is not available\") + \"<br />\" + i18n.t_(\"Please, contact your administrator.\");\n        disableLogin = true;\n    }\n    // No profile for this user\n    else if (\"noProfileForUser\".equals(loginFailMessage)) {\n        errorMessage = i18n.t_(\"Login failed. No profile has been set up for this user. Contact your administrator.\");\n    }\n    // Account locked due to too many failed attempts (constant defined in LoginServlet.ACCOUNT_LOCKED_MESSAGE)\n    else if (\"accountLockedMessage\".equals(loginFailMessage)) {\n        errorMessage = i18n.t_(\"Too many failed login attempts. Please try again later.\");\n    }\n \t// Login or password error\n    else if (\"loginFailMessage\".equals(loginFailMessage)) {\n        errorMessage = i18n.t_(\"Unable to log in. Please check your username and password.\");\n    }\n%>\n<!DOCTYPE html>\n<html>\n<head>\n<meta name=\"viewport\" content=\"width=device-width, user-scalable=no\">\n<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\" />\n<title>Bonita Applications</title>\n<link rel=\"icon\" type=\"image/png\" href=\"<%= styleUrl %>/images/favicon2.ico\" />\n<link rel=\"stylesheet\" type=\"text/css\" href=\"<%= styleUrl %>/css/login.css\"/>\n\n<script type=\"text/javascript\">\n  document.addEventListener(\"DOMContentLoaded\", function() {\n\t\tif (window != window.top) {\n\t\t\ttry {\n\t\t\t\tif (window.frameElement.id == \"bonitaframe\") {\n\t\t\t\t\t/* if the login jsp is displayed inside a \"bonitaframe\" iframe it probably means the session is invalid so refresh the whole page */\n\t\t\t\t\twindow.parent.location.reload();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\t/* nothing to do (bonita is probably displayed inside an iframe of a different domain app) */\n\t\t\t}\n\t\t}\n\t\t/* Add url hash to form action url */\n\t\tvar form = document.getElementById('LoginForm');\n\t\tform.setAttribute('action', form.getAttribute('action') + window.location.hash);\n\t});\n</script>\n\n</head>\n<body id=\"LoginPage\">\n\n\t<div id=\"LoginHeader\">\n\t\t<h1><span><%= i18n.t_(\"Welcome to\") %></span> <%= i18n.t_(\"Bonita Applications\") %></h1>\n\t</div>\n\n\t<div id=\"floater\"></div>\n\n\t<div class=\"LoginFormWrapper\">\n\t    <div id=\"LoginFormContainer\" >\n\t\t<div id=\"logo\">\n\t\t\t<img src=\"<%= styleUrl %>/skin/images/login-logo.png\"/>\n\t\t</div>\n\n\t\t<div class=\"body\">\n\t\t\t<form id=\"LoginForm\" action=\"<%=actionUrl%>\" method=\"post\" autocomplete=\"off\">\n\n\t\t\t\t<div class=\"header\">\n\t\t\t\t\t<h2><%=i18n.t_(\"Login form\")%></h2>\n\t\t\t\t</div>\n\n\t\t\t\t<p class=\"error\"><%=errorMessage.length() > 0 ? errorMessage  : \"\"%></p>\n\n\t\t\t\t<div class=\"formentries\">\n\n\t\t\t\t\t<div class=\"formentry\">\n\t\t\t\t\t\t<div class=\"label\">\n\t\t\t\t\t\t\t<label for=\"username\"><%=i18n.t_(\"User\")%></label>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class=\"input\">\n\t\t\t\t\t\t\t<input id=\"username\"\n                                   name=\"username\"\n                                   value=\"<%= StringEscapeUtils.escapeHtml4(JSP.getSessionOrCookie(\"username\", \"\")) %>\"\n                                   placeholder=\"<%=i18n.t_(\"User\")%>\"\n                                   type=\"text\"\n                                   autocomplete=\"off\"\n                                   tabindex=\"1\"\n                                   maxlength=\"255\" <%=disableLogin ? \"disabled=\\\"disabled\\\" \" : \"\"%>\n                            />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div class=\"formentry\">\n\t\t\t\t\t\t<div class=\"label\">\n\t\t\t\t\t\t\t<label for=\"password\"><%=i18n.t_(\"Password\")%></label>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class=\"input\">\n\t\t\t\t\t\t\t<input id=\"password\"\n                                   name=\"password\"\n                                   type=\"password\"\n                                   tabindex=\"2\"\n                                   autocomplete=\"off\"\n                                   maxlength=\"50\"\n                                   placeholder=\"<%=i18n.t_(\"Password\")%>\" <%=disableLogin ? \"disabled=\\\"disabled\\\" \" : \"\"%>\n                            />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<input name=\"_l\" type=\"hidden\" value=\"<%=i18n.getLocale()%>\" />\n\t\t\t\t\t</div>\n\n\t\t\t\t</div>\n\t\t\t\t<div class=\"formactions\">\n\t\t\t\t\t<input type=\"submit\"\n                           value=\"<%=i18n.t_(\"Login\")%>\" <%=disableLogin ? \"disabled=\\\"disabled\\\" \" : \"\"%>\n                    />\n\t\t\t\t</div>\n\t\t\t</form>\n\t\t</div>\n\t</div>\n\t</div>\n</body>\n</html>\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/FakeI18n.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common;\n\nimport org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n;\n\n/**\n * Created by Vincent Elcrin\n * Date: 23/09/13\n * Time: 18:40\n */\npublic class FakeI18n extends AbstractI18n {\n\n    private String l10n;\n\n    public FakeI18n() {\n        I18N_instance = this;\n    }\n\n    @Override\n    public void loadLocale(LOCALE locale) {\n    }\n\n    @Override\n    protected String getText(LOCALE locale, String key) {\n        return l10n;\n    }\n\n    public void setL10n(String value) {\n        l10n = value;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/model/ImportStatusMessagesTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.model;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.Mockito.mock;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\n\nimport org.bonitasoft.console.common.server.i18n.I18n;\nimport org.bonitasoft.engine.api.ImportError;\nimport org.bonitasoft.engine.api.ImportError.Type;\nimport org.bonitasoft.engine.api.ImportStatus;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.bonitasoft.web.common.model.ImportStatusMessages;\nimport org.junit.*;\nimport org.mockito.Mockito;\nimport org.mockito.MockitoAnnotations;\n\npublic class ImportStatusMessagesTest {\n\n    @BeforeClass\n    public static void initEnvironment() {\n        HashMap<String, String> availableLocales;\n        availableLocales = new HashMap<>();\n        availableLocales.put(\"en\", \"English\");\n        availableLocales.put(\"fr\", \"Français\");\n        availableLocales.put(\"es\", \"Español\");\n        availableLocales.put(\"pt_BR\", \"Português (Brasil)\");\n        availableLocales.put(\"ja\", \"日本語\");\n\n        I18n i18n = mock(I18n.class);\n        I18n.setInstance(i18n);\n        Mockito.when(i18n.getAvailableLocalesFor(anyString())).thenReturn(availableLocales);\n    }\n\n    @AfterClass\n    public static void cleanUp() throws Exception {\n        I18n.setInstance(null);\n    }\n\n    @Before\n    public void init()\n            throws InvalidSessionException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        MockitoAnnotations.initMocks(this);\n    }\n\n    @Test\n    public void should_put_elements_Added_or_replaced_in_added_map() throws Exception {\n        //given\n        final ImportStatus statusAdded1 = new ImportStatus(\"statusAdded1\");\n        statusAdded1.setStatus(ImportStatus.Status.ADDED);\n        final ImportStatus statusAdded2 = new ImportStatus(\"statusAdded2\");\n        statusAdded2.setStatus(ImportStatus.Status.REPLACED);\n\n        final List<ImportStatus> importStatus = new ArrayList<>(Arrays.asList(statusAdded1, statusAdded2));\n\n        //when\n        final ImportStatusMessages importStatusMessages = new ImportStatusMessages(importStatus);\n\n        //then\n        assertThat(importStatusMessages.getImported().size()).isEqualTo(2);\n        assertThat(importStatusMessages.getImported().get(0).getName()).isEqualTo(\"statusAdded1\");\n        assertThat(importStatusMessages.getImported().get(0).getStatusType())\n                .isEqualTo(ImportStatus.Status.ADDED.name());\n        assertThat(importStatusMessages.getImported().get(1).getName()).isEqualTo(\"statusAdded2\");\n        assertThat(importStatusMessages.getImported().get(1).getStatusType())\n                .isEqualTo(ImportStatus.Status.REPLACED.name());\n        assertThat(importStatusMessages.getSkipped().size()).isEqualTo(0);\n        assertThat(importStatusMessages.getErrors().size()).isEqualTo(0);\n\n    }\n\n    @Test\n    public void should_put_elements_skipped_in_skipped_map() throws Exception {\n        //given\n        final ImportStatus status1 = new ImportStatus(\"statusSkipped1\");\n        status1.setStatus(ImportStatus.Status.SKIPPED);\n        final ImportStatus status2 = new ImportStatus(\"statusSkipped2\");\n        status2.setStatus(ImportStatus.Status.SKIPPED);\n\n        final List<ImportStatus> importStatus = new ArrayList<>(Arrays.asList(status1, status2));\n\n        //when\n        final ImportStatusMessages importStatusMessages = new ImportStatusMessages(importStatus);\n\n        //then\n        assertThat(importStatusMessages.getSkipped().size()).isEqualTo(2);\n        assertThat(importStatusMessages.getSkipped().get(0).getName()).isEqualTo(\"statusSkipped1\");\n        assertThat(importStatusMessages.getSkipped().get(0).getStatusType())\n                .isEqualTo(ImportStatus.Status.SKIPPED.name());\n        assertThat(importStatusMessages.getSkipped().get(1).getName()).isEqualTo(\"statusSkipped2\");\n        assertThat(importStatusMessages.getSkipped().get(1).getStatusType())\n                .isEqualTo(ImportStatus.Status.SKIPPED.name());\n        assertThat(importStatusMessages.getImported().size()).isEqualTo(0);\n        assertThat(importStatusMessages.getErrors().size()).isEqualTo(0);\n\n    }\n\n    @Test\n    public void should_put_elements_With_error_in_error_map() throws Exception {\n        //given\n        final ImportStatus status1 = new ImportStatus(\"statusError1\");\n        status1.addError(new ImportError(\"Error1\", Type.GROUP));\n        status1.addError(new ImportError(\"Error2\", Type.GROUP));\n        status1.addError(new ImportError(\"Error3\", Type.ROLE));\n        status1.addError(new ImportError(\"Error4\", Type.USER));\n        final ImportStatus status2 = new ImportStatus(\"statusError2\");\n        status2.addError(new ImportError(\"Error1\", Type.PAGE));\n\n        final List<ImportStatus> importStatus = new ArrayList<>(Arrays.asList(status1, status2));\n\n        //when\n        final ImportStatusMessages importStatusMessages = new ImportStatusMessages(importStatus);\n\n        //then\n        assertThat(importStatusMessages.getErrors().size()).isEqualTo(2);\n        assertThat(importStatusMessages.getErrors().get(0).getName()).isEqualTo(\"statusError1\");\n        assertThat(importStatusMessages.getErrors().get(0).getStatusType()).isEqualTo(ImportStatus.Status.ADDED.name());\n        assertThat(importStatusMessages.getErrors().get(0).getErrors().size()).isEqualTo(3);\n        assertThat(importStatusMessages.getErrors().get(0).getErrors().get(Type.GROUP.name()).get(0))\n                .isEqualTo(\"Error1\");\n        assertThat(importStatusMessages.getErrors().get(0).getErrors().get(Type.GROUP.name()).get(1))\n                .isEqualTo(\"Error2\");\n        assertThat(importStatusMessages.getErrors().get(0).getErrors().get(Type.ROLE.name()).get(0))\n                .isEqualTo(\"Error3\");\n        assertThat(importStatusMessages.getErrors().get(0).getErrors().get(Type.USER.name()).get(0))\n                .isEqualTo(\"Error4\");\n        assertThat(importStatusMessages.getErrors().get(1).getName()).isEqualTo(\"statusError2\");\n        assertThat(importStatusMessages.getErrors().get(1).getStatusType()).isEqualTo(ImportStatus.Status.ADDED.name());\n        assertThat(importStatusMessages.getErrors().get(1).getErrors().size()).isEqualTo(1);\n        assertThat(importStatusMessages.getErrors().get(1).getErrors().get(Type.PAGE.name()).get(0))\n                .isEqualTo(\"Error1\");\n        assertThat(importStatusMessages.getImported().size()).isEqualTo(0);\n        assertThat(importStatusMessages.getSkipped().size()).isEqualTo(0);\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/auth/AuthenticationManagerFactoryTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.auth;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.console.common.server.auth.impl.standard.StandardAuthenticationManagerImpl;\nimport org.junit.Test;\n\n/**\n * @author Rohart Bastien\n * @author Emmanuel Duchastenier\n */\npublic class AuthenticationManagerFactoryTest {\n\n    @Test\n    public void testGetLoginManager() throws AuthenticationManagerNotFoundException {\n        assertThat(AuthenticationManagerFactory.getAuthenticationManager()).as(\"Cannot get the login manager\")\n                .isNotNull();\n    }\n\n    @Test\n    public void default_manager_implementation_should_be_StandardAuthenticationManagerImpl_class()\n            throws AuthenticationManagerNotFoundException {\n        // when:\n        AuthenticationManager managerImpl = AuthenticationManagerFactory.getAuthenticationManager();\n\n        // then:\n        assertThat(managerImpl).isInstanceOf(StandardAuthenticationManagerImpl.class);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/auth/AuthenticationManagerPropertiesTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.auth;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.console.common.server.preferences.properties.ConfigurationFilesManager.getProperties;\nimport static org.junit.Assert.assertNotNull;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.spy;\n\nimport java.io.IOException;\n\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Rohart Bastien\n */\npublic class AuthenticationManagerPropertiesTest {\n\n    public AuthenticationManagerProperties loginManagerProperties;\n\n    @Before\n    public void setUp() throws IOException {\n        loginManagerProperties = spy(new AuthenticationManagerProperties());\n        doReturn(getProperties((\"OAuth.serviceProvider = LinkedIn\\n\" +\n                \"OAuth.consumerKey = ove2vcdjptar\\n\" +\n                \"OAuth.consumerSecret = vdaBrCmHvkgJoYz1\\n\" +\n                \"OAuth.callbackURL = http://127.0.0.1:8888/loginservice\").getBytes())).when(loginManagerProperties)\n                .getTenantPropertiesOfScope();\n    }\n\n    @After\n    public void tearDown() {\n        loginManagerProperties = null;\n    }\n\n    @Test\n    public void isLogoutDisabled_should_return_FALSE_if_not_set() {\n        // given:\n        final AuthenticationManagerProperties properties = AuthenticationManagerProperties.getProperties();\n\n        // when:\n        final boolean isLogoutDisabled = properties.isLogoutDisabled();\n\n        // then:\n        assertThat(isLogoutDisabled).isFalse();\n    }\n\n    @Test\n    public void testGetOAuthServiceProviderName() {\n        assertNotNull(\"Cannot get OAuth service provider name\", loginManagerProperties.getOAuthServiceProviderName());\n    }\n\n    @Test\n    public void testGetOAuthConsumerKey() {\n        assertNotNull(\"Cannot get OAuth consumer key\", loginManagerProperties.getOAuthConsumerKey());\n    }\n\n    @Test\n    public void testGetOAuthConsumerSecret() {\n        assertNotNull(\"Cannot get OAuth consumer secret\", loginManagerProperties.getOAuthConsumerSecret());\n    }\n\n    @Test\n    public void testGetOAuthCallbackURL() {\n        assertNotNull(\"Cannot get OAuth callback URL\", loginManagerProperties.getOAuthCallbackURL());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/auth/impl/standard/StandardAuthenticationManagerImplTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.auth.impl.standard;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.spy;\n\nimport org.bonitasoft.console.common.server.login.HttpServletRequestAccessor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.junit.MockitoJUnitRunner;\nimport org.springframework.mock.web.MockHttpServletRequest;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class StandardAuthenticationManagerImplTest {\n\n    private MockHttpServletRequest request;\n    private HttpServletRequestAccessor requestAccessor;\n\n    private StandardAuthenticationManagerImpl standardLoginManagerImpl = spy(new StandardAuthenticationManagerImpl());\n\n    @Before\n    public void setUp() throws Exception {\n        request = new MockHttpServletRequest();\n        request.setContextPath(\"bonita\");\n\n        requestAccessor = new HttpServletRequestAccessor(request);\n    }\n\n    @Test\n    public void testGetSimpleLoginpageURL() throws Exception {\n        String redirectUrl = \"%2Fapps%2FappDirectoryBonita\";\n\n        String loginURL = standardLoginManagerImpl.getLoginPageURL(requestAccessor, redirectUrl);\n\n        assertThat(loginURL).isEqualToIgnoringCase(\"bonita/login.jsp?redirectUrl=%2Fapps%2FappDirectoryBonita\");\n    }\n\n    @Test\n    public void testGetLoginpageURLWithLocale() throws Exception {\n        String redirectUrl = \"%2Fapps%2FappDirectoryBonita\";\n        request.setParameter(\"_l\", \"es\");\n\n        String loginURL = standardLoginManagerImpl.getLoginPageURL(requestAccessor, redirectUrl);\n\n        assertThat(loginURL).isEqualToIgnoringCase(\"bonita/login.jsp?_l=es&redirectUrl=%2Fapps%2FappDirectoryBonita\");\n    }\n\n    @Test\n    public void testGetLoginpageURLFromPortal() throws Exception {\n        String redirectUrl = \"%2Fapps%2FappDirectoryBonita\";\n        request.setServletPath(\"/portal/\");\n\n        String loginURL = standardLoginManagerImpl.getLoginPageURL(requestAccessor, redirectUrl);\n\n        assertThat(loginURL).isEqualToIgnoringCase(\"bonita/login.jsp?redirectUrl=%2Fapps%2FappDirectoryBonita\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/filter/CacheFilterTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.filter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.doNothing;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\nimport static org.mockito.MockitoAnnotations.initMocks;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Enumeration;\nimport java.util.List;\nimport java.util.regex.Pattern;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.FilterConfig;\nimport javax.servlet.ServletContext;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpSession;\n\nimport org.assertj.core.api.Assertions;\nimport org.assertj.core.api.Condition;\nimport org.bonitasoft.console.common.server.login.HttpServletRequestAccessor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\n\npublic class CacheFilterTest {\n\n    @Mock\n    private FilterChain chain;\n\n    @Mock\n    private HttpServletRequestAccessor request;\n\n    @Mock\n    private HttpServletRequest httpRequest;\n\n    @Mock\n    private HttpServletResponse httpResponse;\n\n    @Mock\n    private HttpSession httpSession;\n\n    @Mock\n    private FilterConfig filterConfig;\n\n    @Mock\n    private ServletContext servletContext;\n\n    @Spy\n    CacheFilter cacheFilter;\n\n    @Before\n    public void setUp() throws Exception {\n        initMocks(this);\n        doReturn(httpSession).when(request).getHttpSession();\n        when(request.asHttpServletRequest()).thenReturn(httpRequest);\n        when(httpRequest.getRequestURL()).thenReturn(new StringBuffer());\n        when(servletContext.getContextPath()).thenReturn(\"\");\n        when(filterConfig.getServletContext()).thenReturn(servletContext);\n        when(filterConfig.getInitParameterNames()).thenReturn(Collections.emptyEnumeration());\n    }\n\n    @Test\n    public void testFilterWithExcludedURL() throws Exception {\n        final String url = \"test\";\n        when(httpRequest.getRequestURL()).thenReturn(new StringBuffer(url));\n        doReturn(true).when(cacheFilter).matchExcludePatterns(url);\n\n        cacheFilter.init(filterConfig);\n        cacheFilter.doFilter(httpRequest, httpResponse, chain);\n\n        verify(cacheFilter, times(0)).proceedWithFiltering(httpRequest, httpResponse, chain);\n        verify(cacheFilter, times(1)).excludePatternFiltering(httpRequest, httpResponse, chain);\n        verify(chain, times(1)).doFilter(httpRequest, httpResponse);\n    }\n\n    @Test\n    public void testMatchExcludePatterns() throws Exception {\n\n        cacheFilter.init(filterConfig);\n\n        matchExcludePattern(\"/apps/home/\", true);\n        matchExcludePattern(\"/apps/home/css/style.css\", false);\n        matchExcludePattern(\"http://localhost:8080/bonita/portal/resource/page/content/\", true);\n        matchExcludePattern(\"http://localhost:8080/bonita/portal/resource/page/content/image/logo.png\", false);\n        matchExcludePattern(\"http://localhost:8080/portal/custom-page/API/identity/user/1\", true);\n        matchExcludePattern(\n                \"http://localhost:8080/bonita/portal/custom-page/custompage_cacheBustingBug1/?locale=en&profile=101&_f=allpagesfilter&_id=22\",\n                true);\n    }\n\n    @Test\n    public void testCompileNullPattern() throws Exception {\n\n        cacheFilter.init(filterConfig);\n\n        assertThat(cacheFilter.compilePattern(null)).isNull();\n    }\n\n    @Test\n    public void testCompileWrongPattern() throws Exception {\n\n        cacheFilter.init(filterConfig);\n\n        assertThat(cacheFilter.compilePattern(\"((((\")).isNull();\n    }\n\n    @Test\n    public void testCompileSimplePattern() throws Exception {\n\n        cacheFilter.init(filterConfig);\n\n        final String patternToCompile = \"test\";\n        assertThat(cacheFilter.compilePattern(patternToCompile)).isNotNull().has(new Condition<>() {\n\n            @Override\n            public boolean matches(final Pattern pattern) {\n                return pattern.pattern().equalsIgnoreCase(patternToCompile);\n            }\n        });\n    }\n\n    @Test\n    public void testCompileExcludePattern() throws Exception {\n\n        cacheFilter.init(filterConfig);\n\n        final String patternToCompile = \"^/(bonita/)?(?:(login\\\\.jsp$)|(images/)|(redirectCasToCatchHash\\\\.jsp)|(loginservice)|(serverAPI)|(maintenance\\\\.jsp$)|(API/platform/)|(platformloginservice$)|(portal/scripts)|(/bonita/?$)|(logoutservice))\";\n        assertThat(cacheFilter.compilePattern(patternToCompile)).isNotNull().has(new Condition<>() {\n\n            @Override\n            public boolean matches(final Pattern pattern) {\n                return pattern.pattern().equalsIgnoreCase(patternToCompile);\n            }\n        });\n    }\n\n    @Test\n    public void empty_excludePattern_init_param_should_override_default_excludePattern() throws Exception {\n        final String url = \"test\";\n        List<String> initParamsList = new ArrayList<>();\n        initParamsList.add(\"excludePattern\");\n        Enumeration<String> initParamsEnum = Collections.enumeration(initParamsList);\n        when(filterConfig.getInitParameterNames()).thenReturn(initParamsEnum);\n        when(filterConfig.getInitParameter(\"excludePattern\")).thenReturn(\"\");\n        when(httpRequest.getRequestURL()).thenReturn(new StringBuffer(url));\n        when(httpRequest.getRequestURI()).thenReturn(url);\n        doNothing().when(cacheFilter).proceedWithFiltering(httpRequest, httpResponse, chain);\n\n        cacheFilter.init(filterConfig);\n        cacheFilter.doFilter(httpRequest, httpResponse, chain);\n\n        assertThat(cacheFilter.getExcludePattern()).isNull();\n        verify(cacheFilter, times(1)).proceedWithFiltering(httpRequest, httpResponse, chain);\n        verify(cacheFilter, times(0)).excludePatternFiltering(httpRequest, httpResponse, chain);\n    }\n\n    private void matchExcludePattern(final String urlToMatch, final Boolean mustMatch) {\n\n        doReturn(Pattern.compile(CacheFilter.CACHE_FILTER_EXCLUDED_RESOURCES_PATTERN)).when(cacheFilter)\n                .getExcludePattern();\n        if (cacheFilter.matchExcludePatterns(urlToMatch) != mustMatch) {\n            Assertions.fail(\"Matching excludePattern and the Url \" + urlToMatch + \" must return \" + mustMatch);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/filter/ExcludingPatternFilterTest.java",
    "content": "/**\n * Copyright (C) 2025 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.filter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.*;\n\nimport java.io.IOException;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.FilterConfig;\nimport javax.servlet.ServletContext;\nimport javax.servlet.ServletException;\nimport javax.servlet.ServletRequest;\nimport javax.servlet.ServletResponse;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class ExcludingPatternFilterTest {\n\n    private int proceedCount;\n    private HttpServletRequest request;\n    private HttpServletResponse response;\n    private FilterChain chain;\n    private final Map<String, Object> attributes = new HashMap<>();\n\n    @Before\n    public void setUp() {\n        proceedCount = 0;\n        attributes.clear();\n        request = mock(HttpServletRequest.class);\n        response = mock(HttpServletResponse.class);\n        chain = mock(FilterChain.class);\n        when(request.getRequestURL()).thenReturn(new StringBuffer(\"http://localhost/API/bpm/process\"));\n        when(request.getAttribute(anyString())).thenAnswer(inv -> attributes.get(inv.getArgument(0)));\n        doAnswer(inv -> {\n            attributes.put(inv.getArgument(0), inv.getArgument(1));\n            return null;\n        }).when(request).setAttribute(anyString(), any());\n    }\n\n    @Test\n    public void should_execute_filter_logic_on_first_call() throws Exception {\n        var filter = createFilter(\"MyFilter\");\n\n        filter.doFilter(request, response, chain);\n\n        assertThat(proceedCount).isEqualTo(1);\n        verifyNoInteractions(chain);\n    }\n\n    @Test\n    public void should_skip_filter_logic_on_second_call() throws Exception {\n        var filter = createFilter(\"MyFilter\");\n\n        filter.doFilter(request, response, chain);\n        filter.doFilter(request, response, chain);\n\n        assertThat(proceedCount).isEqualTo(1);\n        verify(chain, times(1)).doFilter(request, response);\n    }\n\n    @Test\n    public void should_allow_different_filter_instances_to_each_execute() throws Exception {\n        var filterA = createFilter(\"FilterA\");\n        var filterB = createFilter(\"FilterB\");\n\n        filterA.doFilter(request, response, chain);\n        filterB.doFilter(request, response, chain);\n\n        assertThat(proceedCount).isEqualTo(2);\n        verifyNoInteractions(chain);\n    }\n\n    @Test\n    public void should_set_request_attribute_with_filter_name() throws Exception {\n        var filter = createFilter(\"TokenValidatorFilter\");\n\n        filter.doFilter(request, response, chain);\n\n        assertThat(attributes).containsKey(\"TokenValidatorFilter.FILTERED\");\n        assertThat(attributes.get(\"TokenValidatorFilter.FILTERED\")).isEqualTo(Boolean.TRUE);\n    }\n\n    private ExcludingPatternFilter createFilter(String filterName) throws ServletException {\n        ExcludingPatternFilter filter = new ExcludingPatternFilter() {\n\n            @Override\n            public void proceedWithFiltering(ServletRequest request, ServletResponse response, FilterChain chain)\n                    throws ServletException, IOException {\n                proceedCount++;\n            }\n\n            @Override\n            public String getDefaultExcludedPages() {\n                return null;\n            }\n        };\n        ServletContext servletContext = mock(ServletContext.class);\n        when(servletContext.getContextPath()).thenReturn(\"\");\n        FilterConfig config = mock(FilterConfig.class);\n        when(config.getFilterName()).thenReturn(filterName);\n        when(config.getInitParameterNames()).thenReturn(Collections.emptyEnumeration());\n        when(config.getServletContext()).thenReturn(servletContext);\n        filter.init(config);\n        return filter;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/filter/MultiReadHttpServletRequestTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.filter;\n\nimport static org.mockito.Mockito.doReturn;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.charset.StandardCharsets;\n\nimport javax.servlet.ReadListener;\nimport javax.servlet.ServletInputStream;\nimport javax.servlet.http.HttpServletRequest;\n\nimport org.apache.commons.io.IOUtils;\nimport org.apache.commons.io.input.CharSequenceInputStream;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class MultiReadHttpServletRequestTest {\n\n    @Mock\n    HttpServletRequest request;\n\n    @Test\n    public void should_getInputStream_work_when_called_twice() throws Exception {\n\n        ServletInputStream fakeInputStream = null;\n        try {\n            fakeInputStream = new FakeServletInputStream();\n            doReturn(fakeInputStream).when(request).getInputStream();\n            final MultiReadHttpServletRequest multiReadHttpServletRequest = new MultiReadHttpServletRequest(request);\n\n            final InputStream inputStream = multiReadHttpServletRequest.getInputStream();\n            Assert.assertEquals(\"body content\", IOUtils.toString(inputStream, StandardCharsets.UTF_8));\n\n            final InputStream inputStream2 = multiReadHttpServletRequest.getInputStream();\n            Assert.assertEquals(\"body content\", IOUtils.toString(inputStream2, StandardCharsets.UTF_8));\n        } finally {\n            if (fakeInputStream != null) {\n                fakeInputStream.close();\n            }\n        }\n    }\n\n    @Test\n    public void should_getInputStream_work_when_called_twice_for_multipart() throws Exception {\n\n        doReturn(\"POST\").when(request).getMethod();\n        doReturn(\"multipart/form-data\").when(request).getContentType();\n        ServletInputStream fakeInputStream = null;\n        try {\n            fakeInputStream = new FakeServletInputStream();\n            doReturn(fakeInputStream).when(request).getInputStream();\n            final MultiReadHttpServletRequest multiReadHttpServletRequest = new MultiReadHttpServletRequest(request);\n\n            final InputStream inputStream = multiReadHttpServletRequest.getInputStream();\n            Assert.assertEquals(\"body content\", IOUtils.toString(inputStream, StandardCharsets.UTF_8));\n\n            final InputStream inputStream2 = multiReadHttpServletRequest.getInputStream();\n            Assert.assertEquals(\"body content\", IOUtils.toString(inputStream2, StandardCharsets.UTF_8));\n        } finally {\n            if (fakeInputStream != null) {\n                fakeInputStream.close();\n            }\n        }\n    }\n\n    @Test\n    public void should_getReader_work_when_called_twice() throws Exception {\n\n        ServletInputStream fakeInputStream = null;\n        try {\n            fakeInputStream = new FakeServletInputStream();\n            doReturn(fakeInputStream).when(request).getInputStream();\n            final MultiReadHttpServletRequest multiReadHttpServletRequest = new MultiReadHttpServletRequest(request);\n\n            final BufferedReader bufferedReader = multiReadHttpServletRequest.getReader();\n            Assert.assertEquals(\"body content\", IOUtils.toString(bufferedReader));\n\n            final BufferedReader bufferedReader2 = multiReadHttpServletRequest.getReader();\n            Assert.assertEquals(\"body content\", IOUtils.toString(bufferedReader2));\n        } finally {\n            if (fakeInputStream != null) {\n                fakeInputStream.close();\n            }\n        }\n    }\n\n    class FakeServletInputStream extends ServletInputStream {\n\n        private final CharSequenceInputStream inputStream = new CharSequenceInputStream(\"body content\",\n                StandardCharsets.UTF_8);\n\n        @Override\n        public int read() throws IOException {\n            return inputStream.read();\n        }\n\n        @Override\n        public int read(final byte[] b) throws IOException {\n            return inputStream.read(b);\n        }\n\n        @Override\n        public void close() throws IOException {\n            inputStream.close();\n            super.close();\n        }\n\n        @Override\n        public boolean isFinished() {\n            try {\n                return inputStream.available() == 0;\n            } catch (IOException e) {\n                throw new RuntimeException(e);\n            }\n        }\n\n        @Override\n        public boolean isReady() {\n            return !isFinished();\n        }\n\n        @Override\n        public void setReadListener(ReadListener readListener) {\n            throw new RuntimeException(\"Not implemented\");\n        }\n\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/filter/NoCacheFilterTest.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.filter;\n\nimport static org.mockito.Mockito.*;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.FilterConfig;\nimport javax.servlet.ServletContext;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class NoCacheFilterTest {\n\n    private NoCacheFilter filter;\n    private HttpServletRequest request;\n    private HttpServletResponse response;\n    private FilterChain chain;\n    private final Map<String, Object> attributes = new HashMap<>();\n\n    @Before\n    public void setUp() throws Exception {\n        attributes.clear();\n        request = mock(HttpServletRequest.class);\n        response = mock(HttpServletResponse.class);\n        chain = mock(FilterChain.class);\n        when(request.getRequestURL()).thenReturn(new StringBuffer(\"http://localhost/API/bpm/process\"));\n        when(request.getAttribute(anyString())).thenAnswer(inv -> attributes.get(inv.getArgument(0)));\n        doAnswer(inv -> {\n            attributes.put(inv.getArgument(0), inv.getArgument(1));\n            return null;\n        }).when(request).setAttribute(anyString(), any());\n\n        filter = new NoCacheFilter();\n        ServletContext servletContext = mock(ServletContext.class);\n        when(servletContext.getContextPath()).thenReturn(\"\");\n        FilterConfig config = mock(FilterConfig.class);\n        when(config.getFilterName()).thenReturn(\"NoCacheFilter\");\n        when(config.getInitParameterNames()).thenReturn(Collections.emptyEnumeration());\n        when(config.getServletContext()).thenReturn(servletContext);\n        filter.init(config);\n    }\n\n    @Test\n    public void should_set_cache_control_headers() throws Exception {\n        filter.doFilter(request, response, chain);\n\n        verify(response).setHeader(\"Cache-Control\", \"no-store, no-cache, must-revalidate, proxy-revalidate\");\n        verify(chain).doFilter(request, response);\n    }\n\n    @Test\n    public void should_not_reapply_headers_on_second_dispatch() throws Exception {\n        filter.doFilter(request, response, chain);\n        filter.doFilter(request, response, chain);\n\n        // Headers set only once (first pass); second pass skips filter logic but still forwards\n        verify(response, times(1)).setHeader(\"Cache-Control\", \"no-store, no-cache, must-revalidate, proxy-revalidate\");\n        verify(chain, times(2)).doFilter(request, response);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/filter/PathSanitizerTest.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.filter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.Test;\n\npublic class PathSanitizerTest {\n\n    @Test\n    public void should_return_null_for_null_input() {\n        assertThat(PathSanitizer.stripPathParameters(null)).isNull();\n    }\n\n    @Test\n    public void should_return_empty_for_empty_input() {\n        assertThat(PathSanitizer.stripPathParameters(\"\")).isEmpty();\n    }\n\n    @Test\n    public void should_not_modify_path_without_semicolons() {\n        assertThat(PathSanitizer.stripPathParameters(\"/API/bpm/process/12345\"))\n                .isEqualTo(\"/API/bpm/process/12345\");\n    }\n\n    @Test\n    public void should_strip_semicolons_from_dotdot_traversal() {\n        assertThat(PathSanitizer.stripPathParameters(\"/API/..;/..;/serverAPI\"))\n                .isEqualTo(\"/API/../../serverAPI\");\n    }\n\n    @Test\n    public void should_strip_semicolons_with_arbitrary_content() {\n        assertThat(PathSanitizer.stripPathParameters(\"/API/..;anything/..;foo/serverAPI\"))\n                .isEqualTo(\"/API/../../serverAPI\");\n    }\n\n    @Test\n    public void should_strip_trailing_semicolon() {\n        assertThat(PathSanitizer.stripPathParameters(\"/API/resource;jsessionid=abc123\"))\n                .isEqualTo(\"/API/resource\");\n    }\n\n    @Test\n    public void should_strip_multiple_semicolons_per_segment() {\n        assertThat(PathSanitizer.stripPathParameters(\"/API/system/session/..;/..;/..;/serverAPI/something\"))\n                .isEqualTo(\"/API/system/session/../../../serverAPI/something\");\n    }\n\n    @Test\n    public void should_handle_semicolon_at_start_of_path() {\n        assertThat(PathSanitizer.stripPathParameters(\";param/API/resource\"))\n                .isEqualTo(\"/API/resource\");\n    }\n\n    @Test\n    public void should_handle_encoded_dotdot_semicolon_pattern() {\n        assertThat(PathSanitizer.stripPathParameters(\"/API/a/..;anything/..;/..;/WEB-INF/web.xml\"))\n                .isEqualTo(\"/API/a/../../../WEB-INF/web.xml\");\n    }\n\n    @Test\n    public void should_preserve_normal_path_segments() {\n        assertThat(PathSanitizer.stripPathParameters(\"/apps/myapp/API/bpm/case/42\"))\n                .isEqualTo(\"/apps/myapp/API/bpm/case/42\");\n    }\n\n    @Test\n    public void should_handle_consecutive_semicolons() {\n        // Double semicolons: first ';' starts skipping until '/' or end of string\n        assertThat(PathSanitizer.stripPathParameters(\"/API/a;;b/next\"))\n                .isEqualTo(\"/API/a/next\");\n        assertThat(PathSanitizer.stripPathParameters(\"/API/..;;/serverAPI\"))\n                .isEqualTo(\"/API/../serverAPI\");\n    }\n\n    @Test\n    public void should_not_strip_double_encoded_semicolons() {\n        // %253b is double-encoded: %25 -> %, so %253b -> %3b (literal text, not a semicolon)\n        // PathSanitizer only strips literal ';', not percent-encoded forms.\n        // URL-decoding is the caller's responsibility (handled in URLExcludePattern).\n        assertThat(PathSanitizer.stripPathParameters(\"/API/..%253b/..%253b/serverAPI\"))\n                .isEqualTo(\"/API/..%253b/..%253b/serverAPI\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/filter/RedirectFilterTest.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.filter;\n\nimport static org.mockito.Mockito.*;\n\nimport java.io.IOException;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\npublic class RedirectFilterTest {\n\n    @Mock\n    private HttpServletRequest httpRequest;\n\n    @Mock\n    private HttpServletResponse httpResponse;\n\n    @Mock\n    private FilterChain filterChain;\n\n    private RedirectFilter redirectFilter;\n\n    @Before\n    public void setUp() {\n        MockitoAnnotations.initMocks(this);\n        redirectFilter = new RedirectFilter();\n    }\n\n    @Test\n    public void testDoFilterWithInternalRedirect() throws IOException, ServletException {\n        when(httpRequest.getParameter(\"redirectUrl\")).thenReturn(\"/redirectLink\");\n        when(httpRequest.getRequestURL()).thenReturn(new StringBuffer(\"http://bonita:8080/currentPath\"));\n        when(httpRequest.getRequestURI()).thenReturn(\"/currentPath\");\n\n        redirectFilter.doFilter(httpRequest, httpResponse, filterChain);\n\n        verify(httpResponse).sendRedirect(\"/redirectLink\");\n        verify(filterChain, never()).doFilter(httpRequest, httpResponse);\n    }\n\n    @Test\n    public void testDoFilterWithExternalRedirect() throws IOException, ServletException {\n        // using http:// as a prefix to simulate an external redirect\n        when(httpRequest.getParameter(\"redirectUrl\")).thenReturn(\"http://external:9090/redirectLink\");\n        when(httpRequest.getRequestURL()).thenReturn(new StringBuffer(\"http://bonita:8080/currentPath\"));\n        when(httpRequest.getRequestURI()).thenReturn(\"/currentPath\");\n\n        redirectFilter.doFilter(httpRequest, httpResponse, filterChain);\n\n        verify(httpResponse, never()).sendRedirect(anyString());\n        verify(filterChain).doFilter(httpRequest, httpResponse);\n\n        // using // as a prefix to simulate an external redirect\n        when(httpRequest.getParameter(\"redirectUrl\")).thenReturn(\"//external:9090/redirectLink\");\n        when(httpRequest.getRequestURL()).thenReturn(new StringBuffer(\"http://bonita:8080/currentPath\"));\n        when(httpRequest.getRequestURI()).thenReturn(\"/currentPath\");\n\n        redirectFilter.doFilter(httpRequest, httpResponse, filterChain);\n\n        verify(httpResponse, never()).sendRedirect(anyString());\n        verify(filterChain, times(2)).doFilter(httpRequest, httpResponse);\n    }\n\n    @Test\n    public void testDoFilterWithoutRedirect() throws IOException, ServletException {\n        when(httpRequest.getParameter(\"redirectUrl\")).thenReturn(null);\n\n        redirectFilter.doFilter(httpRequest, httpResponse, filterChain);\n\n        verify(httpResponse, never()).sendRedirect(anyString());\n        verify(filterChain).doFilter(httpRequest, httpResponse);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/filter/RequestIdFilterTest.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.filter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.doAnswer;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\nimport static org.mockito.quality.Strictness.LENIENT;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.FilterConfig;\nimport javax.servlet.ServletContext;\nimport javax.servlet.ServletRequest;\nimport javax.servlet.ServletResponse;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.console.common.server.login.HttpServletRequestAccessor;\nimport org.bonitasoft.engine.mdc.MDCConstants;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport org.mockito.junit.jupiter.MockitoSettings;\nimport org.slf4j.MDC;\n\n/**\n * Test class RequestIdFilter\n */\n@ExtendWith(MockitoExtension.class)\n@MockitoSettings(strictness = LENIENT)\nclass RequestIdFilterTest {\n\n    @Mock\n    private FilterChain chain;\n\n    @Mock\n    private HttpServletRequestAccessor request;\n\n    @Mock\n    private HttpServletRequest httpRequest;\n\n    @Mock\n    private HttpServletResponse httpResponse;\n\n    @Mock\n    private HttpSession httpSession;\n\n    @Mock\n    private FilterConfig filterConfig;\n\n    @Mock\n    private ServletContext servletContext;\n\n    @Spy\n    RequestIdFilter requestIdFilter;\n\n    @BeforeEach\n    void setUp() {\n        doReturn(httpSession).when(request).getHttpSession();\n        when(request.asHttpServletRequest()).thenReturn(httpRequest);\n        when(httpRequest.getMethod()).thenReturn(\"POST\");\n        when(httpRequest.getCharacterEncoding()).thenReturn(\"UTF-8\");\n        when(httpRequest.getRequestURL()).thenReturn(new StringBuffer());\n        when(servletContext.getContextPath()).thenReturn(\"\");\n        when(filterConfig.getServletContext()).thenReturn(servletContext);\n        when(filterConfig.getInitParameterNames()).thenReturn(Collections.emptyEnumeration());\n    }\n\n    @Test\n    void shouldDetectIdsAttachedToRequest() throws Exception {\n        String correlationId = Long.toHexString(System.nanoTime() - 10000000L);\n        String requestId = Long.toHexString(System.nanoTime());\n\n        when(httpRequest.getContentType()).thenReturn(\"application/json\");\n        when(httpRequest.getAttribute(\"track.requestId\")).thenReturn(requestId);\n        when(httpRequest.getAttribute(\"track.correlationId\")).thenReturn(correlationId);\n\n        Map<String, String> contextMap = new HashMap<>();\n        doAnswer(invocation -> {\n            contextMap.putAll(MDC.getCopyOfContextMap());\n            return null;\n        }).when(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n\n        requestIdFilter.init(filterConfig);\n        requestIdFilter.doFilter(httpRequest, httpResponse, chain);\n\n        // method chain was called once and put REQUEST_ID & CORRELATION_REQUEST_ID in context\n        verify(chain, times(1)).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n        assertThat(contextMap).containsEntry(MDCConstants.REQUEST_ID, requestId);\n        assertThat(contextMap).containsEntry(MDCConstants.CORRELATION_REQUEST_ID, correlationId);\n    }\n\n    @Test\n    void shouldDetectIdsInRequestHeader() throws Exception {\n        String correlationId = Long.toHexString(System.nanoTime() - 10000000L);\n        String requestId = Long.toHexString(System.nanoTime());\n\n        when(httpRequest.getContentType()).thenReturn(\"application/json\");\n        when(httpRequest.getHeader(\"X-Request-ID\")).thenReturn(requestId);\n        when(httpRequest.getHeader(\"X-Correlation-ID\")).thenReturn(correlationId);\n\n        Map<String, String> contextMap = new HashMap<>();\n        doAnswer(invocation -> {\n            contextMap.putAll(MDC.getCopyOfContextMap());\n            return null;\n        }).when(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n\n        requestIdFilter.init(filterConfig);\n        requestIdFilter.doFilter(httpRequest, httpResponse, chain);\n\n        // method chain was called once and put REQUEST_ID & CORRELATION_REQUEST_ID in context\n        verify(chain, times(1)).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n        assertThat(contextMap).containsEntry(MDCConstants.REQUEST_ID, requestId);\n        assertThat(contextMap).containsEntry(MDCConstants.CORRELATION_REQUEST_ID, correlationId);\n    }\n\n    @Test\n    void shouldDetectUserAgent() throws Exception {\n        String userAgent = \"PostmanRuntime/7.43.0\";\n        when(httpRequest.getHeader(\"User-Agent\")).thenReturn(userAgent);\n\n        Map<String, String> contextMap = new HashMap<>();\n        doAnswer(invocation -> {\n            contextMap.putAll(MDC.getCopyOfContextMap());\n            return null;\n        }).when(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n\n        requestIdFilter.init(filterConfig);\n        requestIdFilter.doFilter(httpRequest, httpResponse, chain);\n\n        // method chain was called once and put REQUEST_USER_AGENT_MDC_KEY in context\n        verify(chain, times(1)).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n        assertThat(contextMap).containsEntry(MDCConstants.REQUEST_USER_AGENT_MDC_KEY, userAgent);\n    }\n\n    @Test\n    void shouldIgnoreNullUserAgent() throws Exception {\n        when(httpRequest.getHeader(\"User-Agent\")).thenReturn(null);\n\n        Map<String, String> contextMap = new HashMap<>();\n        doAnswer(invocation -> {\n            contextMap.putAll(MDC.getCopyOfContextMap());\n            return null;\n        }).when(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n\n        requestIdFilter.init(filterConfig);\n        requestIdFilter.doFilter(httpRequest, httpResponse, chain);\n\n        // method chain was called once and put REQUEST_USER_AGENT_MDC_KEY in context\n        verify(chain, times(1)).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n        assertThat(contextMap).doesNotContainKey(MDCConstants.REQUEST_USER_AGENT_MDC_KEY);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/filter/SanitizerFilterTest.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.filter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.*;\nimport static org.mockito.quality.Strictness.LENIENT;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.IOException;\nimport java.util.Collections;\nimport java.util.List;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.FilterConfig;\nimport javax.servlet.ReadListener;\nimport javax.servlet.ServletContext;\nimport javax.servlet.ServletInputStream;\nimport javax.servlet.ServletRequest;\nimport javax.servlet.ServletResponse;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpSession;\n\nimport com.fasterxml.jackson.databind.JsonNode;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.apache.commons.io.IOUtils;\nimport org.bonitasoft.console.common.server.login.HttpServletRequestAccessor;\nimport org.bonitasoft.web.toolkit.client.common.json.JSonUtil;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport org.mockito.junit.jupiter.MockitoSettings;\n\n/**\n * Test class SanitizerFilter\n *\n * @author Vincent Hemery\n */\n@ExtendWith(MockitoExtension.class)\n@MockitoSettings(strictness = LENIENT)\nclass SanitizerFilterTest {\n\n    @Mock\n    private FilterChain chain;\n\n    @Mock\n    private HttpServletRequestAccessor request;\n\n    @Mock\n    private HttpServletRequest httpRequest;\n\n    @Mock\n    private HttpServletResponse httpResponse;\n\n    @Mock\n    private HttpSession httpSession;\n\n    @Mock\n    private FilterConfig filterConfig;\n\n    @Mock\n    private ServletContext servletContext;\n\n    @Spy\n    SanitizerFilter sanitizerFilter;\n\n    @BeforeEach\n    void setUp() {\n        doReturn(httpSession).when(request).getHttpSession();\n        when(request.asHttpServletRequest()).thenReturn(httpRequest);\n        when(httpRequest.getMethod()).thenReturn(\"POST\");\n        when(httpRequest.getCharacterEncoding()).thenReturn(\"UTF-8\");\n        when(httpRequest.getRequestURL()).thenReturn(new StringBuffer());\n        when(servletContext.getContextPath()).thenReturn(\"\");\n        when(filterConfig.getServletContext()).thenReturn(servletContext);\n        when(filterConfig.getInitParameterNames()).thenReturn(Collections.emptyEnumeration());\n    }\n\n    @Test\n    void shouldNotSanitizeWhenDisabled() throws Exception {\n        when(httpRequest.getContentType()).thenReturn(\"application/json\");\n        when(sanitizerFilter.isSanitizerEnabled()).thenReturn(false);\n\n        sanitizerFilter.init(filterConfig);\n        sanitizerFilter.doFilter(httpRequest, httpResponse, chain);\n\n        verify(sanitizerFilter, never()).sanitize(any(JsonNode.class));\n        verify(chain, times(1)).doFilter(httpRequest, httpResponse);\n    }\n\n    @Test\n    void shouldNotAffectAttributeValue() throws Exception {\n        when(httpRequest.getContentType()).thenReturn(\"application/json\");\n        when(sanitizerFilter.isSanitizerEnabled()).thenReturn(true);\n        final String attributeName = \"key\";\n        final String attributeValue = \"value\";\n        when(httpRequest.getAttribute(attributeName)).thenReturn(attributeValue);\n\n        sanitizerFilter.init(filterConfig);\n        sanitizerFilter.doFilter(httpRequest, httpResponse, chain);\n\n        ArgumentCaptor<ServletRequest> requestCaptor = ArgumentCaptor.forClass(ServletRequest.class);\n        verify(chain, times(1)).doFilter(requestCaptor.capture(), any(ServletResponse.class));\n\n        ServletRequest r = requestCaptor.getValue();\n        final String value = r.getAttribute(attributeName).toString();\n        assertThat(value).isEqualTo(attributeValue);\n    }\n\n    @Test\n    void shouldNotAffectParameterValues() throws Exception {\n        when(httpRequest.getContentType()).thenReturn(\"application/json\");\n        when(sanitizerFilter.isSanitizerEnabled()).thenReturn(true);\n        final String parameterName = \"key\";\n        final String parameterValue = \"value\";\n        when(httpRequest.getParameter(parameterName)).thenReturn(parameterValue);\n\n        sanitizerFilter.init(filterConfig);\n        sanitizerFilter.doFilter(httpRequest, httpResponse, chain);\n\n        ArgumentCaptor<ServletRequest> requestCaptor = ArgumentCaptor.forClass(ServletRequest.class);\n        verify(chain, times(1)).doFilter(requestCaptor.capture(), any(ServletResponse.class));\n\n        ServletRequest r = requestCaptor.getValue();\n        final String value = r.getParameter(parameterName);\n        assertThat(value).isEqualTo(parameterValue);\n    }\n\n    @Test\n    void shouldNotAffectFileUpload() throws Exception {\n        when(httpRequest.getContentType()).thenReturn(\"text/xml\");\n        when(sanitizerFilter.isSanitizerEnabled()).thenReturn(true);\n        final String body = String.format(\"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>%n\" +\n                \"<organization:Organization xmlns:organization=\\\"http://documentation.bonitasoft.com/organization-xml-schema/1.1\\\">%n\"\n                +\n                \"  <users>%n\" +\n                \"  </users>%n\" +\n                \"</organization:Organization>\");\n        var is = new ByteArrayInputStream(body.getBytes());\n\n        when(httpRequest.getInputStream()).thenReturn(getServletInputStream(is));\n\n        sanitizerFilter.init(filterConfig);\n        sanitizerFilter.doFilter(httpRequest, httpResponse, chain);\n\n        ArgumentCaptor<ServletRequest> requestCaptor = ArgumentCaptor.forClass(ServletRequest.class);\n        verify(chain, times(1)).doFilter(requestCaptor.capture(), any(ServletResponse.class));\n\n        ServletRequest r = requestCaptor.getValue();\n        var updatedBody = new String(r.getInputStream().readAllBytes());\n        assertThat(updatedBody).isEqualTo(body);\n    }\n\n    @Test\n    void shouldNotAffectNonHtml() throws Exception {\n        when(httpRequest.getContentType()).thenReturn(\"application/json\");\n        when(sanitizerFilter.isSanitizerEnabled()).thenReturn(true);\n        when(sanitizerFilter.getAttributesExcluded()).thenReturn(Collections.emptyList());\n        final String body = \"{\\\"key\\\":\\\"value\\\"}\";\n        var is = new ByteArrayInputStream(body.getBytes());\n\n        when(httpRequest.getInputStream()).thenReturn(getServletInputStream(is));\n\n        sanitizerFilter.init(filterConfig);\n        sanitizerFilter.doFilter(httpRequest, httpResponse, chain);\n\n        ArgumentCaptor<ServletRequest> requestCaptor = ArgumentCaptor.forClass(ServletRequest.class);\n        verify(chain, times(1)).doFilter(requestCaptor.capture(), any(ServletResponse.class));\n\n        ServletRequest r = requestCaptor.getValue();\n        var updatedBody = new String(r.getInputStream().readAllBytes());\n        assertThat(updatedBody).isEqualTo(body);\n    }\n\n    @Test\n    void shouldNotAffectPre() throws Exception {\n        when(httpRequest.getContentType()).thenReturn(\"application/json\");\n        when(sanitizerFilter.isSanitizerEnabled()).thenReturn(true);\n        when(sanitizerFilter.getAttributesExcluded()).thenReturn(Collections.emptyList());\n        final String body = \"{\\\"key\\\":\\\"<div><p>text</p><pre>value formatted</pre></div>\\\"}\";\n        var is = new ByteArrayInputStream(body.getBytes());\n\n        when(httpRequest.getInputStream()).thenReturn(getServletInputStream(is));\n\n        sanitizerFilter.init(filterConfig);\n        sanitizerFilter.doFilter(httpRequest, httpResponse, chain);\n\n        ArgumentCaptor<ServletRequest> requestCaptor = ArgumentCaptor.forClass(ServletRequest.class);\n        verify(chain, times(1)).doFilter(requestCaptor.capture(), any(ServletResponse.class));\n\n        ServletRequest r = requestCaptor.getValue();\n        var updatedBody = new String(r.getInputStream().readAllBytes());\n        assertThat(updatedBody).isEqualTo(body);\n    }\n\n    @Test\n    void shouldNotAffectLinks() throws Exception {\n        when(httpRequest.getContentType()).thenReturn(\"application/json\");\n        when(sanitizerFilter.isSanitizerEnabled()).thenReturn(true);\n        when(sanitizerFilter.getAttributesExcluded()).thenReturn(Collections.emptyList());\n        final String body = \"{\\\"key\\\":\\\"<p><a href=\\\\\\\"https://documentation.ofelia.com/bonita/latest/\\\\\\\">link text</a></p>\\\"}\";\n        var is = new ByteArrayInputStream(body.getBytes());\n\n        when(httpRequest.getInputStream()).thenReturn(getServletInputStream(is));\n\n        sanitizerFilter.init(filterConfig);\n        sanitizerFilter.doFilter(httpRequest, httpResponse, chain);\n\n        ArgumentCaptor<ServletRequest> requestCaptor = ArgumentCaptor.forClass(ServletRequest.class);\n        verify(chain, times(1)).doFilter(requestCaptor.capture(), any(ServletResponse.class));\n\n        ServletRequest r = requestCaptor.getValue();\n        var updatedBody = new String(r.getInputStream().readAllBytes());\n        // Note: rel attribute order is non-deterministic after OWASP HTML Sanitizer removed Guava (v20240325.1+)\n        // Check structure and all required rel values are present\n        assertThat(updatedBody)\n                .startsWith(\n                        \"{\\\"key\\\":\\\"<p><a href=\\\\\\\"https://documentation.ofelia.com/bonita/latest/\\\\\\\" rel=\\\\\\\"\")\n                .endsWith(\"\\\\\\\">link text</a></p>\\\"}\")\n                .contains(\"noreferrer\")\n                .contains(\"noopener\")\n                .contains(\"nofollow\");\n    }\n\n    @Test\n    void shouldSanitizeAttackFromBody() throws Exception {\n        when(httpRequest.getContentType()).thenReturn(\"application/JSON\");\n        when(sanitizerFilter.isSanitizerEnabled()).thenReturn(true);\n        when(sanitizerFilter.getAttributesExcluded()).thenReturn(List.of(\"email\", \"password\"));\n        // Classic XSS attack in value\n        final String attName1 = \"test1\";\n        final String saneValue1 = \"Hello <b>World</b>\";\n        final String attValue1 = saneValue1\n                + \"<style>@keyframes slidein {}</style><xss style=\\\"animation-duration:1s;animation-name:slidein;animation-iteration-count:2\\\" onwebkitanimationiteration=\\\"alert(1)\\\"></xss>\";\n        // Another XSS attack in value\n        final String attName2 = \"test2\";\n        final String saneValue2 = \"<div>test</div>\";\n        final String attValue2 = \"<div onclick=\\\"alert('test')\\\">test</div><script>alert('test')</script>\";\n        // XSS attack in name\n        final String saneAttName3 = \"test3\";\n        final String attName3 = saneAttName3 + \"<script>alert('test')</script>\";\n        final String attValue3 = \"value3\";\n        // XSS attack as exploited in IE (as seen on\n        // https://github.com/OWASP/java-html-sanitizer/blob/master/docs/html-validation.md#valid-according-to-policy)\n        final String attName4 = \"test4\";\n        final String saneValue4 = \"v4\";\n        final String attValue4 = saneValue4 + \"<!--if[true]> <script>alert(1337)</script> -->\";\n        // XSS attack as exploited in foreign content context (as seen on\n        // https://github.com/OWASP/java-html-sanitizer/blob/master/docs/html-validation.md#valid-according-to-policy)\n        final String attName5 = \"test5\";\n        final String saneValue5 = \"v5\";\n        final String attValue5 = saneValue5 + \"<![CDATA[ <!-- ]]><script>alert(1337)</script><!-- -->\";\n        // Don't break my heart (as seen on\n        // https://github.com/OWASP/java-html-sanitizer/blob/master/docs/html-validation.md#dont-break-my-heart)\n        final String attName6 = \"test6\";\n        final String saneValue6 = \"I <3 Poniez!\";\n        // but escaped chars are unescaped anyway...\n        final String attName7 = \"test7\";\n        final String saneValue7 = \"You <3 Poniez 2!\";\n        final String attValue7 = \"You &lt;3 Poniez 2!\";\n        // XSS attack escaped in value\n        final String attName8 = \"test8\";\n        final String saneValue8 = \"value8\";\n        final String attValue8 = saneValue8 + \"&lt;script>alert('test')</script>\";\n\n        final String body = String.format(\"{%n\" +\n                \"  \\\"key1\\\": \\\"value1\\\",%n\" +\n                \"  \\\"keyOfEmpty\\\": \\\"\\\",%n\" +\n                \"  \\\"keyOfNull\\\": null,%n\" +\n                \"  \\\"%s\\\": \\\"%s\\\",%n\" +\n                \"  \\\"%s\\\": \\\"%s\\\",%n\" +\n                \"  \\\"%s\\\": \\\"%s\\\",%n\" +\n                \"  \\\"%s\\\": \\\"%s\\\",%n\" +\n                \"  \\\"%s\\\": \\\"%s\\\",%n\" +\n                \"  \\\"%s\\\": \\\"%s\\\",%n\" +\n                \"  \\\"%s\\\": \\\"%s\\\",%n\" +\n                \"  \\\"%s\\\": \\\"%s\\\",%n\" +\n                \"  \\\"email\\\": \\\"walter.bates@bonitasoft.com\\\"%n\" +\n                \"}\",\n                attName1, JSonUtil.escape(attValue1), attName2, JSonUtil.escape(attValue2),\n                attName3, JSonUtil.escape(attValue3), attName4, JSonUtil.escape(attValue4),\n                attName5, JSonUtil.escape(attValue5), attName6, JSonUtil.escape(saneValue6),\n                attName7, JSonUtil.escape(attValue7), attName8, JSonUtil.escape(attValue8));\n        var is = new ByteArrayInputStream(body.getBytes());\n\n        when(httpRequest.getInputStream()).thenReturn(getServletInputStream(is));\n\n        sanitizerFilter.init(filterConfig);\n        sanitizerFilter.doFilter(httpRequest, httpResponse, chain);\n\n        ArgumentCaptor<ServletRequest> requestCaptor = ArgumentCaptor.forClass(ServletRequest.class);\n        verify(chain, times(1)).doFilter(requestCaptor.capture(), any(ServletResponse.class));\n\n        ServletRequest r = requestCaptor.getValue();\n        try (ServletInputStream inputStream = r.getInputStream()) {\n            var stringBody = IOUtils.toString(inputStream, r.getCharacterEncoding());\n            ObjectMapper mapper = new ObjectMapper();\n            var json = mapper.readTree(stringBody);\n            // check normal values\n            assertThat(json.get(\"key1\").asText()).isEqualTo(\"value1\");\n            assertThat(json.get(\"keyOfEmpty\").asText()).isEqualTo(\"\");\n            assertThat(json.get(\"keyOfNull\").isNull()).isTrue();\n            assertThat(json.get(\"email\").asText()).isEqualTo(\"walter.bates@bonitasoft.com\");\n            // check sanitized values\n            assertThat(json.get(attName1).asText()).isEqualTo(saneValue1);\n            assertThat(json.get(attName2).asText()).isEqualTo(saneValue2);\n            assertThat(json.get(attName3)).isNull();\n            assertThat(json.get(saneAttName3).asText()).isEqualTo(attValue3);\n            assertThat(json.get(attName4).asText()).isEqualTo(saneValue4);\n            assertThat(json.get(attName5).asText()).isEqualTo(saneValue5);\n            assertThat(json.get(attName6).asText()).isEqualTo(saneValue6);\n            assertThat(json.get(attName7).asText()).isEqualTo(saneValue7);\n            assertThat(json.get(attName8).asText()).isEqualTo(saneValue8);\n        } catch (IOException e) {\n            throw new AssertionError(e);\n        }\n    }\n\n    @Test\n    public void shouldSanitizeAttackWhenRequestIsMultiReadHttpServletRequest() throws Exception {\n        // Simulate the production scenario where TokenValidatorFilter or RestAPIAuthorizationFilter\n        // has already wrapped the request in a MultiReadHttpServletRequest\n        MultiReadHttpServletRequest multiReadRequest = mock(MultiReadHttpServletRequest.class);\n        when(multiReadRequest.getMethod()).thenReturn(\"PUT\");\n        when(multiReadRequest.getCharacterEncoding()).thenReturn(\"UTF-8\");\n        when(multiReadRequest.getContentType()).thenReturn(\"application/json\");\n        when(multiReadRequest.getRequestURL()).thenReturn(new StringBuffer());\n        when(sanitizerFilter.isSanitizerEnabled()).thenReturn(true);\n        when(sanitizerFilter.getAttributesExcluded()).thenReturn(Collections.emptyList());\n\n        final String body = \"{\\\"lastname\\\":\\\"<marquee onclick=\\\\\\\"alert('test')\\\\\\\">XSS</marquee>\\\"}\";\n        var is = new ByteArrayInputStream(body.getBytes());\n        when(multiReadRequest.getInputStream()).thenReturn(getServletInputStream(is));\n\n        sanitizerFilter.init(filterConfig);\n        sanitizerFilter.doFilter(multiReadRequest, httpResponse, chain);\n\n        ArgumentCaptor<ServletRequest> requestCaptor = ArgumentCaptor.forClass(ServletRequest.class);\n        verify(chain, times(1)).doFilter(requestCaptor.capture(), any(ServletResponse.class));\n\n        ServletRequest r = requestCaptor.getValue();\n        var updatedBody = IOUtils.toString(r.getInputStream(), r.getCharacterEncoding());\n        ObjectMapper mapper = new ObjectMapper();\n        var json = mapper.readTree(updatedBody);\n        // The marquee tag and onclick should be stripped by the sanitizer\n        assertThat(json.get(\"lastname\").asText()).doesNotContain(\"<marquee\");\n        assertThat(json.get(\"lastname\").asText()).doesNotContain(\"onclick\");\n        assertThat(json.get(\"lastname\").asText()).isEqualTo(\"XSS\");\n    }\n\n    @Test\n    public void shouldSanitizeAttackWhenReadingBodyViaGetReader() throws Exception {\n        // Verify that getReader() also returns the sanitized body, not the original\n        when(httpRequest.getContentType()).thenReturn(\"application/json\");\n        when(sanitizerFilter.isSanitizerEnabled()).thenReturn(true);\n        when(sanitizerFilter.getAttributesExcluded()).thenReturn(Collections.emptyList());\n\n        final String body = \"{\\\"name\\\":\\\"<script>alert('xss')</script>safe\\\"}\";\n        var is = new ByteArrayInputStream(body.getBytes());\n        when(httpRequest.getInputStream()).thenReturn(getServletInputStream(is));\n\n        sanitizerFilter.init(filterConfig);\n        sanitizerFilter.doFilter(httpRequest, httpResponse, chain);\n\n        ArgumentCaptor<ServletRequest> requestCaptor = ArgumentCaptor.forClass(ServletRequest.class);\n        verify(chain, times(1)).doFilter(requestCaptor.capture(), any(ServletResponse.class));\n\n        ServletRequest r = requestCaptor.getValue();\n        // Read via getReader() instead of getInputStream()\n        var readerBody = IOUtils.toString(r.getReader());\n        ObjectMapper mapper = new ObjectMapper();\n        var json = mapper.readTree(readerBody);\n        assertThat(json.get(\"name\").asText()).doesNotContain(\"<script>\");\n        assertThat(json.get(\"name\").asText()).isEqualTo(\"safe\");\n    }\n\n    @Test\n    public void shouldNotWrapMultiReadHttpServletRequestWhenBodyIsUnchanged() throws Exception {\n        // When the body is clean and the request is already a MultiReadHttpServletRequest,\n        // the original request should be passed through without wrapping\n        MultiReadHttpServletRequest multiReadRequest = mock(MultiReadHttpServletRequest.class);\n        when(multiReadRequest.getMethod()).thenReturn(\"PUT\");\n        when(multiReadRequest.getCharacterEncoding()).thenReturn(\"UTF-8\");\n        when(multiReadRequest.getContentType()).thenReturn(\"application/json\");\n        when(multiReadRequest.getRequestURL()).thenReturn(new StringBuffer());\n        when(sanitizerFilter.isSanitizerEnabled()).thenReturn(true);\n        when(sanitizerFilter.getAttributesExcluded()).thenReturn(Collections.emptyList());\n\n        final String body = \"{\\\"lastname\\\":\\\"safe value\\\"}\";\n        var is = new ByteArrayInputStream(body.getBytes());\n        when(multiReadRequest.getInputStream()).thenReturn(getServletInputStream(is));\n\n        sanitizerFilter.init(filterConfig);\n        sanitizerFilter.doFilter(multiReadRequest, httpResponse, chain);\n\n        // Verify the original MultiReadHttpServletRequest is passed through, not a wrapper\n        verify(chain, times(1)).doFilter(multiReadRequest, httpResponse);\n    }\n\n    private ServletInputStream getServletInputStream(ByteArrayInputStream is) {\n        return new ServletInputStream() {\n\n            @Override\n            public int read() throws IOException {\n                return is.read();\n            }\n\n            @Override\n            public boolean isFinished() {\n                return is.available() == 0;\n            }\n\n            @Override\n            public boolean isReady() {\n                return !isFinished();\n            }\n\n            @Override\n            public void setReadListener(ReadListener readListener) {\n                throw new UnsupportedOperationException(\"Unimplemented method 'setReadListener'\");\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/form/ProcessFormServiceTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.form;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyLong;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceNotFoundException;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstance;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.APISession;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ProcessFormServiceTest {\n\n    @Spy\n    ProcessFormService processFormService;\n\n    @Mock\n    ProcessAPI processAPI;\n\n    @Mock\n    APISession apiSession;\n\n    @Before\n    public void beforeEach() throws Exception {\n        doReturn(processAPI).when(processFormService).getProcessAPI(apiSession);\n        when(apiSession.getUserId()).thenReturn(1L);\n    }\n\n    @Test\n    public void ensureProcessDefinitionId_with_processDefinitionId() throws Exception {\n        assertEquals(42L, processFormService.ensureProcessDefinitionId(apiSession, 42L, -1L, -1L));\n    }\n\n    @Test\n    public void ensureProcessDefinitionId_with_processInstanceId() throws Exception {\n        final ProcessInstance processInstance = mock(ProcessInstance.class);\n        when(processInstance.getProcessDefinitionId()).thenReturn(1L);\n        when(processAPI.getProcessInstance(42L)).thenReturn(processInstance);\n        assertEquals(1L, processFormService.ensureProcessDefinitionId(apiSession, -1L, 42L, -1L));\n    }\n\n    @Test\n    public void ensureProcessDefinitionId_with_processInstanceId_on_archived_instance() throws Exception {\n        final ArchivedProcessInstance archivedProcessInstance = mock(ArchivedProcessInstance.class);\n        when(archivedProcessInstance.getProcessDefinitionId()).thenReturn(1L);\n        when(processAPI.getProcessInstance(42L)).thenThrow(ProcessInstanceNotFoundException.class);\n        final List<ArchivedProcessInstance> result = new ArrayList<>();\n        result.add(archivedProcessInstance);\n        final SearchResult<ArchivedProcessInstance> searchResult = mock(SearchResult.class);\n        when(searchResult.getCount()).thenReturn(1L);\n        when(searchResult.getResult()).thenReturn(result);\n        when(processAPI.searchArchivedProcessInstancesInAllStates(any(SearchOptions.class))).thenReturn(searchResult);\n        assertEquals(1L, processFormService.ensureProcessDefinitionId(apiSession, -1L, 42L, -1L));\n    }\n\n    @Test\n    public void ensureProcessDefinitionId_with_taskInstanceId() throws Exception {\n        final ActivityInstance activityInstance = mock(ActivityInstance.class);\n        when(activityInstance.getProcessDefinitionId()).thenReturn(1L);\n        when(processAPI.getActivityInstance(42L)).thenReturn(activityInstance);\n        assertEquals(1L, processFormService.ensureProcessDefinitionId(apiSession, -1L, -1L, 42L));\n    }\n\n    @Test\n    public void ensureProcessDefinitionId_with_taskInstanceId_on_archived_instance() throws Exception {\n        final ArchivedActivityInstance archivedActivityInstance = mock(ArchivedActivityInstance.class);\n        when(archivedActivityInstance.getProcessDefinitionId()).thenReturn(1L);\n        when(processAPI.getActivityInstance(42L)).thenThrow(ActivityInstanceNotFoundException.class);\n        when(processAPI.getArchivedActivityInstance(42L)).thenReturn(archivedActivityInstance);\n        assertEquals(1L, processFormService.ensureProcessDefinitionId(apiSession, -1L, -1L, 42L));\n    }\n\n    @Test\n    public void getTaskName_with_taskInstanceId() throws Exception {\n        final ActivityInstance activityInstance = mock(ActivityInstance.class);\n        when(activityInstance.getName()).thenReturn(\"myTask\");\n        when(processAPI.getActivityInstance(42L)).thenReturn(activityInstance);\n        assertEquals(\"myTask\", processFormService.getTaskName(apiSession, 42L));\n    }\n\n    @Test\n    public void getTaskName_with_taskInstanceId_on_archived_instance() throws Exception {\n        final ArchivedActivityInstance archivedActivityInstance = mock(ArchivedActivityInstance.class);\n        when(archivedActivityInstance.getName()).thenReturn(\"myTask\");\n        when(processAPI.getActivityInstance(42L)).thenThrow(ActivityInstanceNotFoundException.class);\n        when(processAPI.getArchivedActivityInstance(42L)).thenReturn(archivedActivityInstance);\n        assertEquals(\"myTask\", processFormService.getTaskName(apiSession, 42L));\n    }\n\n    @Test\n    public void getTaskInstanceId_with_taskInstanceId() throws Exception {\n        final HumanTaskInstance humanTaskInstance = mock(HumanTaskInstance.class);\n        when(humanTaskInstance.getId()).thenReturn(1L);\n        final List<HumanTaskInstance> result = new ArrayList<>();\n        result.add(humanTaskInstance);\n        final SearchResult<HumanTaskInstance> searchResult = mock(SearchResult.class);\n        when(searchResult.getCount()).thenReturn(1L);\n        when(searchResult.getResult()).thenReturn(result);\n        when(processAPI.searchMyAvailableHumanTasks(anyLong(), any(SearchOptions.class))).thenReturn(searchResult);\n        assertEquals(1L, processFormService.getTaskInstanceId(apiSession, 42L, \"myTask\", -1L));\n    }\n\n    @Test\n    public void getTaskInstanceId_with_taskInstanceId_on_archived_instance() throws Exception {\n        when(processAPI.searchMyAvailableHumanTasks(anyLong(), any(SearchOptions.class)))\n                .thenReturn(mock(SearchResult.class));\n        final ArchivedHumanTaskInstance archivedHumanTaskInstance = mock(ArchivedHumanTaskInstance.class);\n        when(archivedHumanTaskInstance.getSourceObjectId()).thenReturn(1L);\n        final List<ArchivedHumanTaskInstance> result = new ArrayList<>();\n        result.add(archivedHumanTaskInstance);\n        final SearchResult<ArchivedHumanTaskInstance> searchResult = mock(SearchResult.class);\n        when(searchResult.getCount()).thenReturn(1L);\n        when(searchResult.getResult()).thenReturn(result);\n        when(processAPI.searchArchivedHumanTasks(any(SearchOptions.class))).thenReturn(searchResult);\n        assertEquals(1L, processFormService.getTaskInstanceId(apiSession, 42L, \"myTask\", -1L));\n    }\n\n    @Test\n    public void getProcessDefinitionId_with_no_version() throws Exception {\n        assertEquals(-1L, processFormService.getProcessDefinitionId(apiSession, \"myProcess\", null));\n    }\n\n    @Test\n    public void getProcessDefinitionId_with_no_name_and_version() throws Exception {\n        assertEquals(-1L, processFormService.getProcessDefinitionId(apiSession, null, null));\n    }\n\n    @Test\n    public void getProcessDefinitionUUID_should_return_valid_UUID() throws Exception {\n        final ProcessDeploymentInfo processDeploymentInfo = mock(ProcessDeploymentInfo.class);\n        when(processDeploymentInfo.getName()).thenReturn(\"processName\");\n        when(processDeploymentInfo.getVersion()).thenReturn(\"processVersion\");\n        when(processAPI.getProcessDeploymentInfo(1L)).thenReturn(processDeploymentInfo);\n\n        final String uuid = processFormService.getProcessPath(apiSession, 1L);\n\n        assertEquals(\"processName/processVersion\", uuid);\n    }\n\n    @Test\n    public void getProcessDefinitionUUID_should_return_valid_UUID_with_special_characters() throws Exception {\n        final ProcessDeploymentInfo processDeploymentInfo = mock(ProcessDeploymentInfo.class);\n        when(processDeploymentInfo.getName()).thenReturn(\"process Name/é\");\n        when(processDeploymentInfo.getVersion()).thenReturn(\"process Version ø\");\n        when(processAPI.getProcessDeploymentInfo(1L)).thenReturn(processDeploymentInfo);\n\n        final String uuid = processFormService.getProcessPath(apiSession, 1L);\n\n        assertEquals(\"process%20Name/%C3%A9/process%20Version%20%C3%B8\", uuid);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/form/ProcessFormServletTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.form;\n\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.Mockito.*;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Answers;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ProcessFormServletTest {\n\n    @Mock\n    ProcessFormService processFormService;\n\n    @Spy\n    @InjectMocks\n    ProcessFormServlet formServlet;\n\n    @Mock(answer = Answers.RETURNS_MOCKS)\n    HttpServletRequest hsRequest;\n\n    @Mock\n    HttpServletResponse hsResponse;\n\n    @Mock\n    HttpSession httpSession;\n\n    @Mock\n    APISession apiSession;\n\n    @Before\n    public void beforeEach() throws Exception {\n        when(hsRequest.getContextPath()).thenReturn(\"/bonita\");\n        when(hsRequest.getSession()).thenReturn(httpSession);\n        when(httpSession.getAttribute(\"apiSession\")).thenReturn(apiSession);\n    }\n\n    @Test\n    public void should_get_Bad_Request_when_invalid_parameters() throws Exception {\n        when(hsRequest.getPathInfo()).thenReturn(\"\");\n        when(hsRequest.getParameter(anyString())).thenReturn(null);\n        formServlet.doGet(hsRequest, hsResponse);\n        verify(hsResponse, times(1)).sendError(400,\n                \"Either process name and version are required or process instance Id (with or without task name) or task instance Id.\");\n    }\n\n    @Test\n    public void should_redirect_to_page_servlet_for_process() throws Exception {\n        when(hsRequest.getPathInfo()).thenReturn(\"/process/processName/processVersion\");\n        when(processFormService.getProcessDefinitionId(apiSession, \"processName\", \"processVersion\")).thenReturn(1L);\n        when(processFormService.ensureProcessDefinitionId(apiSession, 1L, -1L, -1L)).thenReturn(1L);\n        when(processFormService.getProcessPath(apiSession, 1L)).thenReturn(\"processName/processVersion\");\n\n        formServlet.doGet(hsRequest, hsResponse);\n\n        verify(formServlet, times(1)).redirectToPageServlet(hsRequest, hsResponse, apiSession, 1L, -1L, -1L, null);\n        verify(hsResponse, times(1))\n                .encodeRedirectURL(\"/bonita/portal/resource/process/processName/processVersion/content/?id=1\");\n        verify(hsResponse, times(1)).sendRedirect(any());\n    }\n\n    @Test\n    public void should_redirect_to_page_servlet_for_instance() throws Exception {\n        when(hsRequest.getPathInfo()).thenReturn(\"/processInstance/42\");\n        when(processFormService.ensureProcessDefinitionId(apiSession, -1L, 42L, -1L)).thenReturn(1L);\n        when(processFormService.getProcessPath(apiSession, 1L)).thenReturn(\"processName/processVersion\");\n\n        formServlet.doGet(hsRequest, hsResponse);\n\n        verify(formServlet, times(1)).redirectToPageServlet(hsRequest, hsResponse, apiSession, 1L, 42L, -1L, null);\n        verify(hsResponse, times(1))\n                .encodeRedirectURL(\"/bonita/portal/resource/processInstance/processName/processVersion/content/?id=42\");\n        verify(hsResponse, times(1)).sendRedirect(any());\n    }\n\n    @Test\n    public void should_redirect_to_page_servlet_for_task() throws Exception {\n        when(hsRequest.getPathInfo()).thenReturn(\"/taskInstance/42\");\n        when(processFormService.ensureProcessDefinitionId(apiSession, -1L, -1L, 42L)).thenReturn(1L);\n        when(processFormService.getTaskName(apiSession, 42L)).thenReturn(\"taskName\");\n        when(processFormService.encodePathSegment(\"taskName\")).thenCallRealMethod();\n        when(processFormService.getProcessPath(apiSession, 1L)).thenReturn(\"processName/processVersion\");\n\n        formServlet.doGet(hsRequest, hsResponse);\n\n        verify(formServlet, times(1)).redirectToPageServlet(hsRequest, hsResponse, apiSession, 1L, -1L, 42L,\n                \"taskName\");\n        verify(hsResponse, times(1)).encodeRedirectURL(\n                \"/bonita/portal/resource/taskInstance/processName/processVersion/taskName/content/?id=42\");\n        verify(hsResponse, times(1)).sendRedirect(any());\n    }\n\n    @Test\n    public void should_redirect_to_page_servlet_for_task_from_instance() throws Exception {\n        when(hsRequest.getPathInfo()).thenReturn(\"/processInstance/42/task/taskName\");\n        when(processFormService.getTaskInstanceId(apiSession, 42L, \"taskName\", -1L)).thenReturn(1L);\n        when(processFormService.ensureProcessDefinitionId(apiSession, -1L, 42L, 1L)).thenReturn(2L);\n        when(processFormService.getTaskName(apiSession, 1L)).thenReturn(\"taskName\");\n        when(processFormService.encodePathSegment(\"taskName\")).thenCallRealMethod();\n        when(processFormService.getProcessPath(apiSession, 2L)).thenReturn(\"processName/processVersion\");\n\n        formServlet.doGet(hsRequest, hsResponse);\n\n        verify(formServlet, times(1)).redirectToPageServlet(hsRequest, hsResponse, apiSession, 2L, 42L, 1L, \"taskName\");\n        verify(hsResponse, times(1)).encodeRedirectURL(\n                \"/bonita/portal/resource/taskInstance/processName/processVersion/taskName/content/?id=1\");\n        verify(hsResponse, times(1)).sendRedirect(any());\n    }\n\n    @Test\n    public void should_redirect_to_page_servlet_for_process_with_unicode_characters() throws Exception {\n        when(hsRequest.getPathInfo()).thenReturn(\"/process/processus+%C3%A9%2B%C3%B8/%C3%B8\");\n        when(processFormService.getProcessDefinitionId(apiSession, \"processus é+ø\", \"ø\")).thenReturn(1L);\n        when(processFormService.ensureProcessDefinitionId(apiSession, 1L, -1L, -1L)).thenReturn(1L);\n        when(processFormService.getProcessPath(apiSession, 1L)).thenReturn(\"processus+%C3%A9%2B%C3%B8/%C3%B8\");\n\n        formServlet.doGet(hsRequest, hsResponse);\n\n        verify(formServlet, times(1)).redirectToPageServlet(hsRequest, hsResponse, apiSession, 1L, -1L, -1L, null);\n        verify(hsResponse, times(1))\n                .encodeRedirectURL(\"/bonita/portal/resource/process/processus+%C3%A9%2B%C3%B8/%C3%B8/content/?id=1\");\n        verify(hsResponse, times(1)).sendRedirect(any());\n    }\n\n    @Test\n    public void should_redirect_to_page_servlet_for_task_with_unicode_characters() throws Exception {\n        when(hsRequest.getPathInfo()).thenReturn(\"/taskInstance/42\");\n        when(processFormService.ensureProcessDefinitionId(apiSession, -1L, -1L, 42L)).thenReturn(1L);\n        when(processFormService.getTaskName(apiSession, 42L)).thenReturn(\"taskName é+ø\");\n        when(processFormService.encodePathSegment(\"taskName é+ø\")).thenCallRealMethod();\n        when(processFormService.getProcessPath(apiSession, 1L)).thenReturn(\"processName/processVersion\");\n\n        formServlet.doGet(hsRequest, hsResponse);\n\n        verify(formServlet, times(1)).redirectToPageServlet(hsRequest, hsResponse, apiSession, 1L, -1L, 42L,\n                \"taskName é+ø\");\n        verify(hsResponse, times(1)).encodeRedirectURL(\n                \"/bonita/portal/resource/taskInstance/processName/processVersion/taskName%20%C3%A9+%C3%B8/content/?id=42\");\n        verify(hsResponse, times(1)).sendRedirect(any());\n    }\n\n    @Test\n    public void should_redirect_to_page_servlet_for_task_with_unicode_characters_and_slash() throws Exception {\n        when(hsRequest.getPathInfo()).thenReturn(\"/taskInstance/42\");\n        when(processFormService.ensureProcessDefinitionId(apiSession, -1L, -1L, 42L)).thenReturn(1L);\n        when(processFormService.getTaskName(apiSession, 42L)).thenReturn(\"taskName/é+ø\");\n        when(processFormService.encodePathSegment(\"taskName/é+ø\")).thenCallRealMethod();\n        when(processFormService.getProcessPath(apiSession, 1L)).thenReturn(\"processName/processVersion\");\n\n        formServlet.doGet(hsRequest, hsResponse);\n\n        verify(formServlet, times(1)).redirectToPageServlet(hsRequest, hsResponse, apiSession, 1L, -1L, 42L,\n                \"taskName/é+ø\");\n        verify(hsResponse, times(1)).encodeRedirectURL(\n                \"/bonita/portal/resource/taskInstance/processName/processVersion/taskName/%C3%A9+%C3%B8/content/?id=42\");\n        verify(hsResponse, times(1)).sendRedirect(any());\n    }\n\n    @Test\n    public void should_get_not_found_when_invalid_process() throws Exception {\n        when(hsRequest.getPathInfo()).thenReturn(\"/process/processName/processVersion/\");\n        when(processFormService.getProcessDefinitionId(apiSession, \"processName\", \"processVersion\"))\n                .thenThrow(ProcessDefinitionNotFoundException.class);\n\n        formServlet.doGet(hsRequest, hsResponse);\n\n        verify(hsResponse, times(1)).sendError(404, \"Cannot find the process\");\n    }\n\n    @Test\n    public void should_get_not_found_when_invalid_processInstanceId() throws Exception {\n        when(hsRequest.getPathInfo()).thenReturn(\"/processInstance/42/\");\n        when(processFormService.ensureProcessDefinitionId(apiSession, -1L, 42L, -1L))\n                .thenThrow(ArchivedProcessInstanceNotFoundException.class);\n\n        formServlet.doGet(hsRequest, hsResponse);\n\n        verify(hsResponse, times(1)).sendError(404, \"Cannot find the process instance\");\n    }\n\n    @Test\n    public void should_get_not_found_when_invalid_task() throws Exception {\n        when(hsRequest.getPathInfo()).thenReturn(\"/processInstance/42/task/taskName/\");\n        when(processFormService.getTaskInstanceId(apiSession, 42L, \"taskName\", -1L))\n                .thenThrow(ActivityInstanceNotFoundException.class);\n\n        formServlet.doGet(hsRequest, hsResponse);\n\n        verify(hsResponse, times(1)).sendError(404, \"Cannot find the task instance\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/i18n/I18nTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.i18n;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.io.InputStream;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n;\nimport org.junit.After;\nimport org.junit.Test;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class I18nTest {\n\n    @After\n    public void cleanup() {\n        System.clearProperty(I18n.I18N_CUSTOM_DIR_PROPERTY);\n        final I18n i18n = I18n.getInstance();\n        i18n.refresh();\n    }\n\n    @Test\n    public void getStreams_should_read_from_classpath() {\n        final List<InputStream> streams = I18n.getInstance().getStreams(AbstractI18n.LOCALE.fr);\n        //retrieving resources from both src/test/resources and src/main/resources\n        assertThat(streams).hasSize(6);\n        assertThat(streams.get(0)).isNotNull();\n    }\n\n    @Test\n    public void getStreams_should_not_fail_if_no_resource_found() {\n        final List<InputStream> streams = I18n.getInstance().getStreams(AbstractI18n.LOCALE.de);\n        assertThat(streams).isEmpty();\n    }\n\n    @Test\n    public void getAvailableLocalesFor_should_look_in_classpath() {\n        final Map<String, String> availableLocales = I18n.getInstance().getAvailableLocalesFor(\"test\");\n        assertThat(availableLocales).containsKey(\"fr\");\n    }\n\n    @Test\n    public void loadLocale_should_read_translation_from_classpath() {\n        final I18n i18n = I18n.getInstance();\n        i18n.refresh();\n        i18n.loadLocale(AbstractI18n.LOCALE.fr);\n        final Map<String, String> translations = i18n.getLocale(AbstractI18n.LOCALE.fr);\n\n        assertThat(translations.get(\"test key\")).isEqualTo(\"Valeur de test en français\");\n    }\n\n    @Test\n    public void loadLocale_should_read_from_FS_if_custom_property_set() {\n        System.setProperty(I18n.I18N_CUSTOM_DIR_PROPERTY, this.getClass().getResource(\"/custom_po_resource\").getPath());\n\n        final I18n i18n = I18n.getInstance();\n        i18n.refresh();\n        i18n.loadLocale(AbstractI18n.LOCALE.es);\n        final Map<String, String> translations = i18n.getLocale(AbstractI18n.LOCALE.es);\n\n        assertThat(translations.get(\"test key\")).isEqualTo(\"valor de prueba en Espanol\");\n    }\n\n    @Test\n    public void loadLocale_should_override_value_if_custom_property_set() {\n        System.setProperty(I18n.I18N_CUSTOM_DIR_PROPERTY, this.getClass().getResource(\"/custom_po_resource\").getPath());\n\n        final I18n i18n = I18n.getInstance();\n        i18n.refresh();\n        i18n.loadLocale(AbstractI18n.LOCALE.fr);\n        final Map<String, String> translations = i18n.getLocale(AbstractI18n.LOCALE.fr);\n\n        assertThat(translations.get(\"test key\")).isEqualTo(\"Valeur modifiée\");\n    }\n\n    @Test\n    public void loadLocale_should_merge_values_from_classpath_and_filesystem() {\n        System.setProperty(I18n.I18N_CUSTOM_DIR_PROPERTY, this.getClass().getResource(\"/custom_po_resource\").getPath());\n\n        final I18n i18n = I18n.getInstance();\n        i18n.refresh();\n        i18n.loadLocale(AbstractI18n.LOCALE.fr);\n        final Map<String, String> translations = i18n.getLocale(AbstractI18n.LOCALE.fr);\n\n        assertThat(translations.get(\"test key\")).isEqualTo(\"Valeur modifiée\");\n        assertThat(translations.get(\"about\")).isEqualTo(\"Copyright Bonitasoft 2016\");\n        assertThat(translations.get(\"web site title\")).isEqualTo(\"Bienvenue dans Bonita Portal 7+\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/i18n/POParserTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.i18n;\n\nimport static junit.framework.Assert.assertEquals;\n\nimport java.io.File;\nimport java.util.Map;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Vincent Elcrin\n */\npublic class POParserTest {\n\n    Map<String, String> i18n;\n\n    @Before\n    public void setUp() {\n        i18n = POParser.parse(new File(\"src/test/resources/test.po\"));\n    }\n\n    @Test\n    public void testToParseASimpleMsg() {\n        assertEquals(\"theMsg\", i18n.get(\"theId\"));\n    }\n\n    @Test\n    public void testToParseAMultiLinesMsg() {\n        assertEquals(\"This is a multilines\\nMessage\", i18n.get(\"Multiline\\nMessage\\nId\"));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/login/LoginManagerTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.*;\n\nimport java.io.Serializable;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.console.common.server.auth.AuthenticationFailedException;\nimport org.bonitasoft.console.common.server.auth.AuthenticationManager;\nimport org.bonitasoft.console.common.server.login.credentials.Credentials;\nimport org.bonitasoft.console.common.server.login.credentials.StandardCredentials;\nimport org.bonitasoft.console.common.server.login.credentials.UserLogger;\nimport org.bonitasoft.console.common.server.login.filter.TokenGenerator;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.user.User;\nimport org.bonitasoft.web.server.login.LoginFailureTracker;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\nimport org.springframework.mock.web.MockHttpServletResponse;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class LoginManagerTest {\n\n    @Mock\n    UserLogger userLogger;\n\n    @Mock\n    HttpServletRequest request;\n\n    MockHttpServletResponse response = new MockHttpServletResponse();\n\n    @Mock\n    HttpSession session;\n\n    @Mock\n    AuthenticationManager authenticationManager;\n\n    @Mock\n    APISession apiSession;\n\n    @Mock\n    TokenGenerator tokenGenerator;\n\n    @Mock\n    LoginFailureTracker loginFailureTracker;\n\n    @Spy\n    @InjectMocks\n    LoginManager loginManager = new LoginManager(null);\n\n    HttpServletRequestAccessor requestAccessor;\n\n    @Before\n    public void setUp() throws Exception {\n        when(request.getSession()).thenReturn(session);\n        when(request.getContextPath()).thenReturn(\"/bonita\");\n        requestAccessor = new HttpServletRequestAccessor(request);\n\n        doReturn(\"123\").when(tokenGenerator).createOrLoadToken(session);\n    }\n\n    @Test\n    public void login_should_initSession() throws Exception {\n        final Credentials credentials = new StandardCredentials(\"name\", \"password\");\n        doReturn(authenticationManager).when(loginManager).getAuthenticationManager();\n        doReturn(apiSession).when(userLogger).doLogin(credentials);\n\n        loginManager.loginInternal(requestAccessor, response, userLogger, credentials);\n\n        verify(loginManager).initSession(eq(requestAccessor), eq(apiSession), any(User.class), anyBoolean());\n        verify(session).invalidate();\n    }\n\n    @Test\n    public void login_should_perform_engine_login() throws Exception {\n        final Credentials credentials = new StandardCredentials(\"name\", \"password\");\n        doReturn(authenticationManager).when(loginManager).getAuthenticationManager();\n        doReturn(apiSession).when(userLogger).doLogin(credentials);\n\n        loginManager.loginInternal(requestAccessor, response, userLogger, credentials);\n\n        verify(userLogger).doLogin(credentials);\n    }\n\n    @Test\n    public void login_should_perform_engine_login_with_credentials_map() throws Exception {\n        final Credentials credentials = new StandardCredentials(\"name\", \"password\");\n        doReturn(authenticationManager).when(loginManager).getAuthenticationManager();\n        final Map<String, Serializable> credentialsMap = new HashMap<>();\n        credentialsMap.put(\"principal\", \"userId\");\n        doReturn(credentialsMap).when(authenticationManager).authenticate(requestAccessor, credentials);\n        doReturn(apiSession).when(userLogger).doLogin(credentialsMap);\n\n        loginManager.loginInternal(requestAccessor, response, userLogger, credentials);\n\n        verify(userLogger).doLogin(credentialsMap);\n        verify(loginManager).storeCredentials(requestAccessor, apiSession, true);\n    }\n\n    @Test\n    public void login_should_perform_engine_login_with_credentials_map_without_invalidating_session() throws Exception {\n        final Credentials credentials = new StandardCredentials(\"name\", \"password\");\n        doReturn(authenticationManager).when(loginManager).getAuthenticationManager();\n        final Map<String, Serializable> credentialsMap = new HashMap<>();\n        credentialsMap.put(\"principal\", \"userId\");\n        credentialsMap.put(AuthenticationManager.INVALIDATE_SESSION, Boolean.FALSE);\n        doReturn(credentialsMap).when(authenticationManager).authenticate(requestAccessor, credentials);\n        doReturn(apiSession).when(userLogger).doLogin(credentialsMap);\n\n        loginManager.loginInternal(requestAccessor, response, userLogger, credentials);\n\n        verify(userLogger).doLogin(credentialsMap);\n        verify(loginManager).storeCredentials(requestAccessor, apiSession, false);\n    }\n\n    @Test(expected = LoginFailedException.class)\n    public void login_should_throw_exception_when_login_fails() throws Exception {\n        final Credentials credentials = new StandardCredentials(\"name\", \"password\");\n        doReturn(authenticationManager).when(loginManager).getAuthenticationManager();\n        doThrow(LoginFailedException.class).when(userLogger).doLogin(credentials);\n\n        loginManager.loginInternal(requestAccessor, response, userLogger, credentials);\n    }\n\n    @Test(expected = AuthenticationFailedException.class)\n    public void login_should_throw_exception_when_authentication_fails() throws Exception {\n        final Credentials credentials = new StandardCredentials(\"name\", \"password\");\n        doReturn(authenticationManager).when(loginManager).getAuthenticationManager();\n        doThrow(AuthenticationFailedException.class).when(authenticationManager).authenticate(requestAccessor,\n                credentials);\n\n        loginManager.loginInternal(requestAccessor, response, userLogger, credentials);\n    }\n\n    @Test(expected = AuthenticationFailedException.class)\n    public void login_should_throw_exception_when_no_credentials_are_passed() throws Exception {\n        final Credentials credentials = new StandardCredentials(null, null);\n        doReturn(authenticationManager).when(loginManager).getAuthenticationManager();\n        doReturn(Collections.emptyMap()).when(authenticationManager).authenticate(requestAccessor, credentials);\n\n        loginManager.loginInternal(requestAccessor, response, userLogger, credentials);\n    }\n\n    @Test\n    public void should_store_csrf_token_in_cookies() throws Exception {\n        Credentials credentials = new StandardCredentials(\"name\", \"password\");\n        doReturn(authenticationManager).when(loginManager).getAuthenticationManager();\n        doReturn(apiSession).when(userLogger).doLogin(credentials);\n\n        loginManager.loginInternal(requestAccessor, response, userLogger, credentials);\n\n        verify(tokenGenerator).createOrLoadToken(session);\n        verify(tokenGenerator, never()).setTokenToResponseHeader(any(HttpServletResponse.class), anyString());\n        assertThat(response.getCookie(TokenGenerator.X_BONITA_API_TOKEN).getValue()).isEqualTo(\"123\");\n    }\n\n    @Test\n    public void loginInternal_should_throw_LoginFailedException_when_user_is_locked_out() throws Exception {\n        final Credentials credentials = new StandardCredentials(\"lockedUser\", \"password\");\n        when(loginFailureTracker.isLockedOut(\"lockedUser\")).thenReturn(true);\n\n        assertThatThrownBy(() -> loginManager.loginInternal(requestAccessor, response, userLogger, credentials))\n                .isInstanceOf(AccountLockedException.class)\n                .hasMessageContaining(\"Too many failed login attempts\");\n\n        verify(userLogger, never()).doLogin(any(Credentials.class));\n    }\n\n    @Test\n    public void loginInternal_should_record_failure_on_login_failure() throws Exception {\n        final Credentials credentials = new StandardCredentials(\"user1\", \"wrongPassword\");\n        doReturn(authenticationManager).when(loginManager).getAuthenticationManager();\n        doThrow(LoginFailedException.class).when(userLogger).doLogin(credentials);\n\n        assertThatThrownBy(() -> loginManager.loginInternal(requestAccessor, response, userLogger, credentials))\n                .isInstanceOf(LoginFailedException.class);\n\n        verify(loginFailureTracker).recordFailure(\"user1\");\n    }\n\n    @Test\n    public void loginInternal_should_record_failure_on_authentication_failure() throws Exception {\n        final Credentials credentials = new StandardCredentials(\"user1\", \"password\");\n        doReturn(authenticationManager).when(loginManager).getAuthenticationManager();\n        doThrow(AuthenticationFailedException.class).when(authenticationManager).authenticate(requestAccessor,\n                credentials);\n\n        assertThatThrownBy(() -> loginManager.loginInternal(requestAccessor, response, userLogger, credentials))\n                .isInstanceOf(AuthenticationFailedException.class);\n\n        verify(loginFailureTracker).recordFailure(\"user1\");\n    }\n\n    @Test\n    public void loginInternal_should_reset_failures_on_successful_login() throws Exception {\n        final Credentials credentials = new StandardCredentials(\"user1\", \"password\");\n        doReturn(authenticationManager).when(loginManager).getAuthenticationManager();\n        doReturn(apiSession).when(userLogger).doLogin(credentials);\n\n        loginManager.loginInternal(requestAccessor, response, userLogger, credentials);\n\n        verify(loginFailureTracker).resetFailures(\"user1\");\n    }\n\n    @Test\n    public void loginInternal_should_not_check_lockout_when_username_is_empty() throws Exception {\n        LoginManager managerWithTracker = spy(new LoginManager(loginFailureTracker));\n        managerWithTracker.tokenGenerator = tokenGenerator;\n        managerWithTracker.portalCookies = new PortalCookies();\n        final Credentials credentials = new StandardCredentials(\"\", \"password\");\n        doReturn(authenticationManager).when(managerWithTracker).getAuthenticationManager();\n        doReturn(Collections.emptyMap()).when(authenticationManager).authenticate(requestAccessor, credentials);\n\n        assertThatThrownBy(() -> managerWithTracker.loginInternal(requestAccessor, response, userLogger, credentials))\n                .isInstanceOf(AuthenticationFailedException.class);\n\n        verify(loginFailureTracker, never()).isLockedOut(anyString());\n    }\n\n    @Test\n    public void loginInternal_should_not_NPE_when_tracker_is_null_and_login_succeeds() throws Exception {\n        LoginManager managerWithoutTracker = spy(new LoginManager(null));\n        managerWithoutTracker.tokenGenerator = tokenGenerator;\n        managerWithoutTracker.portalCookies = new PortalCookies();\n        final Credentials credentials = new StandardCredentials(\"user1\", \"password\");\n        doReturn(authenticationManager).when(managerWithoutTracker).getAuthenticationManager();\n        doReturn(apiSession).when(userLogger).doLogin(credentials);\n\n        managerWithoutTracker.loginInternal(requestAccessor, response, userLogger, credentials);\n\n        verify(userLogger).doLogin(credentials);\n    }\n\n    @Test\n    public void loginInternal_should_not_NPE_when_tracker_is_null_and_login_fails() throws Exception {\n        LoginManager managerWithoutTracker = spy(new LoginManager(null));\n        final Credentials credentials = new StandardCredentials(\"user1\", \"wrongPassword\");\n        doReturn(authenticationManager).when(managerWithoutTracker).getAuthenticationManager();\n        doThrow(LoginFailedException.class).when(userLogger).doLogin(credentials);\n\n        assertThatThrownBy(\n                () -> managerWithoutTracker.loginInternal(requestAccessor, response, userLogger, credentials))\n                .isInstanceOf(LoginFailedException.class);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/login/MemoryLoginFailureTrackerTest.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.test.util.ReflectionTestUtils.getField;\n\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport org.bonitasoft.web.server.login.MemoryLoginFailureTracker;\nimport org.junit.Test;\n\npublic class MemoryLoginFailureTrackerTest {\n\n    private final AtomicLong clock = new AtomicLong(System.currentTimeMillis());\n\n    private MemoryLoginFailureTracker createTracker(boolean enabled, int maxAttempts, int lockoutDurationSeconds) {\n        return new MemoryLoginFailureTracker(enabled, maxAttempts, lockoutDurationSeconds) {\n\n            @Override\n            protected long currentTimeMillis() {\n                return clock.get();\n            }\n        };\n    }\n\n    @Test\n    public void isLockedOut_should_return_false_with_no_failures() {\n        var tracker = createTracker(true, 5, 600);\n\n        assertThat(tracker.isLockedOut(\"user1\")).isFalse();\n    }\n\n    @Test\n    public void isLockedOut_should_return_false_after_fewer_than_max_failures() {\n        var tracker = createTracker(true, 5, 600);\n\n        for (int i = 0; i < 4; i++) {\n            tracker.recordFailure(\"user1\");\n        }\n\n        assertThat(tracker.isLockedOut(\"user1\")).isFalse();\n    }\n\n    @Test\n    public void isLockedOut_should_return_true_after_max_failures() {\n        var tracker = createTracker(true, 5, 600);\n\n        for (int i = 0; i < 5; i++) {\n            tracker.recordFailure(\"user1\");\n        }\n\n        assertThat(tracker.isLockedOut(\"user1\")).isTrue();\n    }\n\n    @Test\n    public void isLockedOut_should_return_false_after_lockout_expires() {\n        var tracker = createTracker(true, 5, 60);\n\n        for (int i = 0; i < 5; i++) {\n            tracker.recordFailure(\"user1\");\n        }\n        assertThat(tracker.isLockedOut(\"user1\")).isTrue();\n\n        clock.addAndGet(60_000L);\n\n        assertThat(tracker.isLockedOut(\"user1\")).isFalse();\n    }\n\n    @Test\n    public void resetFailures_should_clear_failure_count() {\n        var tracker = createTracker(true, 5, 600);\n\n        for (int i = 0; i < 5; i++) {\n            tracker.recordFailure(\"user1\");\n        }\n        assertThat(tracker.isLockedOut(\"user1\")).isTrue();\n\n        tracker.resetFailures(\"user1\");\n\n        assertThat(tracker.isLockedOut(\"user1\")).isFalse();\n    }\n\n    @Test\n    public void isLockedOut_should_return_false_when_disabled() {\n        var tracker = createTracker(false, 5, 600);\n\n        for (int i = 0; i < 10; i++) {\n            tracker.recordFailure(\"user1\");\n        }\n\n        assertThat(tracker.isLockedOut(\"user1\")).isFalse();\n    }\n\n    @Test\n    public void recordFailure_should_start_fresh_after_window_expires() {\n        var tracker = createTracker(true, 5, 60);\n\n        for (int i = 0; i < 4; i++) {\n            tracker.recordFailure(\"user1\");\n        }\n\n        clock.addAndGet(60_000L);\n\n        // After window expires, counter should reset\n        tracker.recordFailure(\"user1\");\n\n        assertThat(tracker.isLockedOut(\"user1\")).isFalse();\n    }\n\n    @Test\n    public void isLockedOut_should_track_different_users_independently() {\n        var tracker = createTracker(true, 5, 600);\n\n        for (int i = 0; i < 5; i++) {\n            tracker.recordFailure(\"user1\");\n        }\n        for (int i = 0; i < 3; i++) {\n            tracker.recordFailure(\"user2\");\n        }\n\n        assertThat(tracker.isLockedOut(\"user1\")).isTrue();\n        assertThat(tracker.isLockedOut(\"user2\")).isFalse();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Test\n    public void eviction_should_cap_entries_at_max_and_evict_oldest_first() {\n        var tracker = createTracker(true, 5, 600);\n\n        // Access internal state to pre-populate beyond MAX_ENTRIES without 10k+ method calls\n        var failures = (ConcurrentHashMap<String, Object>) getField(tracker, \"failures\");\n        var operationCount = (AtomicInteger) getField(tracker, \"operationCount\");\n\n        // Insert MAX_ENTRIES + 5 entries with staggered timestamps so we know which are oldest\n        long baseTime = clock.get();\n        int totalEntries = 10_000 + 5;\n        for (int i = 0; i < totalEntries; i++) {\n            // Use MemoryLoginFailureTracker.FailureRecord via reflection — it's package-private\n            // Entries get timestamps from baseTime + i, so user_0..user_4 are the oldest\n            failures.put(\"user_\" + i, createFailureRecord(1, baseTime + i));\n        }\n        assertThat(failures).hasSize(totalEntries);\n\n        // Set operationCount to 99 so the next recordFailure() call triggers eviction\n        operationCount.set(99);\n\n        // This 100th operation triggers evictExpiredIfNeeded()\n        tracker.recordFailure(\"trigger_user\");\n\n        // Map should be capped at MAX_ENTRIES (the trigger_user entry is part of it)\n        assertThat(failures.size()).isLessThanOrEqualTo(10_000);\n\n        // The 5 oldest entries (user_0 through user_4) should have been evicted\n        for (int i = 0; i < 5; i++) {\n            assertThat(failures.containsKey(\"user_\" + i))\n                    .as(\"user_%d (oldest) should be evicted\", i)\n                    .isFalse();\n        }\n\n        // A newer entry should still be present\n        assertThat(failures.containsKey(\"user_\" + (totalEntries - 1))).isTrue();\n    }\n\n    /**\n     * Creates a FailureRecord instance via reflection since the class is package-private.\n     */\n    private Object createFailureRecord(int count, long windowStartTimestamp) {\n        try {\n            Class<?> recordClass = Class\n                    .forName(\"org.bonitasoft.web.server.login.MemoryLoginFailureTracker$FailureRecord\");\n            var constructor = recordClass.getDeclaredConstructor(int.class, long.class);\n            constructor.setAccessible(true);\n            return constructor.newInstance(count, windowStartTimestamp);\n        } catch (Exception e) {\n            throw new RuntimeException(\"Failed to create FailureRecord\", e);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/login/PortalCookiesTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.*;\nimport static org.mockito.Mockito.doReturn;\n\nimport javax.servlet.http.Cookie;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class PortalCookiesTest {\n\n    @Mock\n    HttpServletRequest request;\n\n    @Mock\n    HttpServletResponse response;\n\n    private PortalCookies portalCookies = spy(new PortalCookies());\n\n    @Test\n    public void should_add_csrf_cookie_to_response_with_context_path() throws Exception {\n\n        doReturn(true).when(portalCookies).isCSRFTokenCookieSecure();\n        doReturn(\"/bonita\").when(request).getContextPath();\n\n        portalCookies.addCSRFTokenCookieToResponse(request, response, \"apiTokenFromClient\");\n\n        ArgumentCaptor<Cookie> argument = ArgumentCaptor.forClass(Cookie.class);\n        verify(response).addCookie(argument.capture());\n        assertThat(argument.getValue().getValue()).isEqualTo(\"apiTokenFromClient\");\n        assertThat(argument.getValue().getPath()).isEqualTo(\"/bonita\");\n        assertThat(argument.getValue().getSecure()).isTrue();\n    }\n\n    @Test\n    public void should_add_csrf_cookie_to_response_with_empty_context_path() throws Exception {\n\n        doReturn(false).when(portalCookies).isCSRFTokenCookieSecure();\n        doReturn(\"\").when(request).getContextPath();\n\n        portalCookies.addCSRFTokenCookieToResponse(request, response, \"apiTokenFromClient\");\n\n        ArgumentCaptor<Cookie> argument = ArgumentCaptor.forClass(Cookie.class);\n        verify(response).addCookie(argument.capture());\n        assertThat(argument.getValue().getValue()).isEqualTo(\"apiTokenFromClient\");\n        assertThat(argument.getValue().getPath()).isEqualTo(\"/\");\n        assertThat(argument.getValue().getSecure()).isFalse();\n    }\n\n    @Test\n    public void should_add_csrf_cookie_to_response_with_specified_path() throws Exception {\n\n        try {\n            System.setProperty(\"bonita.csrf.cookie.path\", \"/\");\n            doReturn(false).when(portalCookies).isCSRFTokenCookieSecure();\n            doReturn(\"/bonita\").when(request).getContextPath();\n\n            portalCookies.addCSRFTokenCookieToResponse(request, response, \"apiTokenFromClient\");\n\n            ArgumentCaptor<Cookie> argument = ArgumentCaptor.forClass(Cookie.class);\n            verify(response).addCookie(argument.capture());\n            assertThat(argument.getValue().getValue()).isEqualTo(\"apiTokenFromClient\");\n            assertThat(argument.getValue().getPath()).isEqualTo(\"/\");\n            assertThat(argument.getValue().getSecure()).isFalse();\n        } finally {\n            System.clearProperty(\"bonita.csrf.cookie.path\");\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/login/filter/AlreadyLoggedInRuleTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.filter;\n\nimport static org.hamcrest.MatcherAssert.assertThat;\nimport static org.hamcrest.Matchers.is;\nimport static org.junit.Assert.assertFalse;\nimport static org.mockito.ArgumentMatchers.argThat;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.*;\nimport static org.mockito.MockitoAnnotations.initMocks;\n\nimport java.util.Locale;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.console.common.server.login.HttpServletRequestAccessor;\nimport org.bonitasoft.console.common.server.login.servlet.PlatformLoginServlet;\nimport org.bonitasoft.console.common.server.utils.SessionUtil;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.PlatformSession;\nimport org.bonitasoft.web.rest.model.user.User;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.ArgumentMatcher;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\n\n/**\n * Created by Vincent Elcrin\n * Date: 30/08/13\n * Time: 15:00\n */\npublic class AlreadyLoggedInRuleTest {\n\n    @Mock\n    private APISession apiSession;\n\n    @Mock\n    private HttpSession httpSession;\n\n    @Mock\n    HttpServletRequest httpServletRequest;\n\n    @Mock\n    private HttpServletResponse response;\n\n    @Spy\n    AlreadyLoggedInRule rule;\n\n    private HttpServletRequestAccessor request;\n\n    @Before\n    public void setUp() throws Exception {\n        initMocks(this);\n        doReturn(httpSession).when(httpServletRequest).getSession();\n        request = new HttpServletRequestAccessor(httpServletRequest);\n    }\n\n    @Test\n    public void testIfRuleAuthorizeAlreadyLoggedUser() throws Exception {\n        doReturn(\"/apps\").when(httpServletRequest).getServletPath();\n        doReturn(\"/app/home\").when(httpServletRequest).getPathInfo();\n        doReturn(apiSession).when(httpSession).getAttribute(SessionUtil.API_SESSION_PARAM_KEY);\n        // ensure we won't recreate user session\n        doReturn(\"\").when(httpSession).getAttribute(SessionUtil.USER_SESSION_PARAM_KEY);\n\n        final boolean authorization = rule.doAuthorize(request, response);\n\n        assertThat(authorization, is(true));\n    }\n\n    @Test\n    public void testIfRuleAuthorizeAlreadyLoggedUserWhithNullPathInfo() throws Exception {\n        doReturn(\"/apps\").when(httpServletRequest).getServletPath();\n        doReturn(null).when(httpServletRequest).getPathInfo();\n        doReturn(apiSession).when(httpSession).getAttribute(SessionUtil.API_SESSION_PARAM_KEY);\n        // ensure we won't recreate user session\n        doReturn(\"\").when(httpSession).getAttribute(SessionUtil.USER_SESSION_PARAM_KEY);\n\n        final boolean authorization = rule.doAuthorize(request, response);\n\n        assertThat(authorization, is(true));\n    }\n\n    @Test\n    public void testIfRuleDoesntAuthorizeNullSession() throws Exception {\n        doReturn(\"/apps\").when(httpServletRequest).getServletPath();\n        doReturn(\"/app/home\").when(httpServletRequest).getPathInfo();\n\n        final boolean authorization = rule.doAuthorize(request, response);\n\n        assertFalse(authorization);\n    }\n\n    @Test\n    public void testIfRuleAuthorizeAlreadyLoggedUserIfPlatform() throws Exception {\n        doReturn(\"/API\").when(httpServletRequest).getServletPath();\n        doReturn(\"/platform/license\").when(httpServletRequest).getPathInfo();\n        doReturn(mock(PlatformSession.class)).when(httpSession)\n                .getAttribute(PlatformLoginServlet.PLATFORM_SESSION_PARAM_KEY);\n\n        final boolean authorization = rule.doAuthorize(request, response);\n\n        assertThat(authorization, is(true));\n    }\n\n    @Test\n    public void testIfUserSessionIsRecreatedWhenMissing() throws Exception {\n        doReturn(\"/apps\").when(httpServletRequest).getServletPath();\n        doReturn(\"/app/home\").when(httpServletRequest).getPathInfo();\n        doReturn(apiSession).when(httpSession).getAttribute(SessionUtil.API_SESSION_PARAM_KEY);\n\n        doReturn(null).when(httpSession).getAttribute(SessionUtil.USER_SESSION_PARAM_KEY);\n        // configure user that will be created\n        doReturn(new Locale(\"en\")).when(httpServletRequest).getLocale();\n        doReturn(\"myUser\").when(apiSession).getUserName();\n\n        rule.doAuthorize(request, response);\n\n        verify(httpSession).setAttribute(\n                eq(SessionUtil.USER_SESSION_PARAM_KEY),\n                argThat(new UserMatcher(\"myUser\", \"en\")));\n    }\n\n    // private static class UserMatcher implements ArgumentMatcher<User> {\n    class UserMatcher implements ArgumentMatcher<User> {\n\n        private final String username;\n        private final String local;\n\n        UserMatcher(final String username, final String local) {\n            this.username = username;\n            this.local = local;\n        }\n\n        @Override\n        public boolean matches(User argument) {\n            final User user = argument;\n            return username.equals(user.getUsername())\n                    && local.equals(user.getLocale());\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/login/filter/AuthenticationFilterTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.filter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.*;\nimport static org.mockito.MockitoAnnotations.initMocks;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.regex.Pattern;\n\nimport javax.servlet.*;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpSession;\n\nimport org.assertj.core.api.Assertions;\nimport org.assertj.core.api.Condition;\nimport org.bonitasoft.console.common.server.auth.AuthenticationManager;\nimport org.bonitasoft.console.common.server.login.HttpServletRequestAccessor;\nimport org.bonitasoft.console.common.server.login.LoginManager;\nimport org.bonitasoft.console.common.server.login.utils.RedirectUrl;\nimport org.bonitasoft.console.common.server.utils.SessionUtil;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.server.login.LoginFailureTracker;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.stubbing.Answer;\nimport org.springframework.web.context.WebApplicationContext;\n\npublic class AuthenticationFilterTest {\n\n    @Mock\n    private FilterChain chain;\n\n    @Mock\n    private HttpServletRequest httpRequest;\n\n    @Mock\n    private HttpServletResponse httpResponse;\n\n    @Mock\n    private HttpSession httpSession;\n\n    @Mock\n    private APISession apiSession;\n\n    @Mock\n    private FilterConfig filterConfig;\n\n    @Mock\n    private ServletContext servletContext;\n\n    @Spy\n    AuthenticationFilter authenticationFilter;\n\n    @Spy\n    AuthenticationManager authenticationManager = new FakeAuthenticationManager();\n\n    HttpServletRequestAccessor request;\n\n    @Before\n    public void setUp() throws Exception {\n        initMocks(this);\n        request = spy(new HttpServletRequestAccessor(httpRequest));\n        doReturn(httpSession).when(httpRequest).getSession();\n        doReturn(authenticationManager).when(authenticationFilter).getAuthenticationManager();\n        when(httpRequest.getRequestURL()).thenReturn(new StringBuffer());\n        when(httpRequest.getMethod()).thenReturn(\"GET\");\n        when(servletContext.getContextPath()).thenReturn(\"\");\n        when(filterConfig.getServletContext()).thenReturn(servletContext);\n        when(filterConfig.getInitParameterNames()).thenReturn(Collections.emptyEnumeration());\n        doReturn(false).when(authenticationFilter).isPlaformInMaintenance(request);\n\n        //remove default rules (already logged in) as they have their own tests\n        authenticationFilter.getRules().clear();\n        authenticationFilter.init(filterConfig);\n    }\n\n    @Test\n    public void testIfWeGoThroughFilterWhenAtLeastOneRulePass() throws Exception {\n        when(httpRequest.getServletPath()).thenReturn(\"/apps\");\n        when(httpRequest.getPathInfo()).thenReturn(\"/app/home\");\n\n        AuthenticationRule passingRule = spy(createPassingRule());\n        authenticationFilter.addRule(passingRule);\n        authenticationFilter.addRule(createFailingRule());\n\n        authenticationFilter.doAuthenticationFiltering(request, httpResponse, chain);\n\n        verify(passingRule).proceedWithRequest(chain, httpRequest, httpResponse);\n        verify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n    }\n\n    @Test\n    public void testIfWeAreNotRedirectedIfAtLeastOneRulePass() throws Exception {\n        when(httpRequest.getServletPath()).thenReturn(\"/apps\");\n        when(httpRequest.getPathInfo()).thenReturn(\"/app/home\");\n\n        authenticationFilter.addRule(createFailingRule());\n        authenticationFilter.addRule(createPassingRule());\n\n        authenticationFilter.doAuthenticationFiltering(request, httpResponse, chain);\n\n        verify(httpResponse, never()).setStatus(HttpServletResponse.SC_UNAUTHORIZED);\n        verify(httpResponse, never()).sendRedirect(anyString());\n    }\n\n    @Test\n    public void testIfWeAreRedirectedIfAllRulesFail() throws Exception {\n        AuthenticationRule firstFailingRule = spy(createFailingRule());\n        authenticationFilter.addRule(firstFailingRule);\n\n        when(httpRequest.getServletPath()).thenReturn(\"/apps\");\n        when(httpRequest.getPathInfo()).thenReturn(\"/app/home\");\n\n        AuthenticationRule secondFailingRule = spy(createFailingRule());\n        authenticationFilter.addRule(secondFailingRule);\n        when(httpRequest.getContextPath()).thenReturn(\"/bonita\");\n        authenticationFilter.doAuthenticationFiltering(request, httpResponse, chain);\n\n        verify(firstFailingRule, never()).proceedWithRequest(chain, httpRequest, httpResponse);\n        verify(secondFailingRule, never()).proceedWithRequest(chain, httpRequest, httpResponse);\n        verify(authenticationFilter).cleanHttpSession(httpSession);\n        verify(httpResponse).sendRedirect(anyString());\n    }\n\n    @Test\n    public void testIfSessionIsNotInvalidatedWhenCallingPlatformAPIWithAPISession() throws Exception {\n        AuthenticationRule failingRule = spy(createFailingRule());\n        authenticationFilter.addRule(failingRule);\n        when(httpRequest.getServletPath()).thenReturn(\"/API\");\n        when(httpRequest.getPathInfo()).thenReturn(\"/platform/license\");\n        when(httpRequest.getContextPath()).thenReturn(\"/bonita\");\n        when(httpSession.getAttribute(SessionUtil.API_SESSION_PARAM_KEY)).thenReturn(apiSession);\n\n        authenticationFilter.doAuthenticationFiltering(request, httpResponse, chain);\n\n        verify(authenticationFilter, never()).cleanHttpSession(httpSession);\n        verify(httpResponse).sendRedirect(anyString());\n    }\n\n    @Test\n    public void testIfWeGet401ErrorIfAllRulesFailAndRedirectParamIsFalse() throws Exception {\n        AuthenticationFilter authenticationFilterWithConfig = spy(new AuthenticationFilter());\n        doReturn(authenticationManager).when(authenticationFilterWithConfig).getAuthenticationManager();\n        FilterConfig filterConfig = mock(FilterConfig.class);\n        when(filterConfig.getServletContext()).thenReturn(servletContext);\n        List<String> initParams = new ArrayList<>();\n        initParams.add(AuthenticationFilter.REDIRECT_PARAM);\n        when(filterConfig.getInitParameterNames()).thenReturn(Collections.enumeration(initParams));\n        when(filterConfig.getInitParameter(AuthenticationFilter.REDIRECT_PARAM)).thenReturn(Boolean.FALSE.toString());\n        authenticationFilterWithConfig.init(filterConfig);\n        AuthenticationRule firstFailingRule = spy(createFailingRule());\n        authenticationFilterWithConfig.addRule(firstFailingRule);\n        AuthenticationRule secondFailingRule = spy(createFailingRule());\n        authenticationFilterWithConfig.addRule(secondFailingRule);\n        when(httpRequest.getServletPath()).thenReturn(\"/apps\");\n        when(httpRequest.getPathInfo()).thenReturn(\"/app/home\");\n        when(httpRequest.getContextPath()).thenReturn(\"/bonita\");\n        authenticationFilterWithConfig.doAuthenticationFiltering(request, httpResponse, chain);\n\n        verify(firstFailingRule, never()).proceedWithRequest(chain, httpRequest, httpResponse);\n        verify(secondFailingRule, never()).proceedWithRequest(chain, httpRequest, httpResponse);\n        verify(httpResponse, never()).sendRedirect(anyString());\n        verify(httpResponse).setStatus(HttpServletResponse.SC_UNAUTHORIZED);\n    }\n\n    @Test\n    public void testIfWeAreNotRedirectedIfRequestMethodIsNotGet() throws Exception {\n        when(httpRequest.getMethod()).thenReturn(\"POST\");\n        authenticationFilter.addRule(createFailingRule());\n\n        when(httpRequest.getServletPath()).thenReturn(\"/apps\");\n        when(httpRequest.getPathInfo()).thenReturn(\"/app/home\");\n        when(httpRequest.getContextPath()).thenReturn(\"/bonita\");\n\n        authenticationFilter.doAuthenticationFiltering(request, httpResponse, chain);\n\n        verify(httpResponse).setStatus(HttpServletResponse.SC_UNAUTHORIZED);\n        verify(httpResponse, never()).sendRedirect(anyString());\n    }\n\n    @Test\n    public void testIfWeDontGoThroughTheChainWhenRulesFails() throws Exception {\n        authenticationFilter.addRule(createFailingRule());\n        authenticationFilter.addRule(createFailingRule());\n        when(httpRequest.getServletPath()).thenReturn(\"/apps\");\n        when(httpRequest.getPathInfo()).thenReturn(\"/app/home\");\n        when(httpRequest.getContextPath()).thenReturn(\"/bonita\");\n        authenticationFilter.doAuthenticationFiltering(request, httpResponse, chain);\n\n        verify(chain, never()).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n    }\n\n    @Test\n    public void testFilter() throws Exception {\n        when(httpRequest.getSession()).thenReturn(httpSession);\n        doAnswer((Answer<Object>) invocation -> null).when(authenticationFilter).doAuthenticationFiltering(\n                any(HttpServletRequestAccessor.class), any(HttpServletResponse.class),\n                any(FilterChain.class));\n        authenticationFilter.doFilter(httpRequest, httpResponse, chain);\n        verify(authenticationFilter, times(1)).doAuthenticationFiltering(any(HttpServletRequestAccessor.class),\n                any(HttpServletResponse.class),\n                any(FilterChain.class));\n    }\n\n    @Test\n    public void testFailedLoginOnDoFiltering() throws Exception {\n        when(httpRequest.getServletPath()).thenReturn(\"/apps\");\n        when(httpRequest.getPathInfo()).thenReturn(\"/app/home\");\n\n        EngineUserNotFoundOrInactive engineUserNotFoundOrInactive = new EngineUserNotFoundOrInactive(\"login failed\");\n        authenticationFilter.addRule(createThrowingExceptionRule(engineUserNotFoundOrInactive));\n\n        authenticationFilter.doAuthenticationFiltering(request, httpResponse, chain);\n\n        verify(authenticationManager, never()).getLoginPageURL(eq(request), anyString());\n        verify(chain, never()).doFilter(httpRequest, httpResponse);\n        verify(authenticationFilter).handleUserNotFoundOrInactiveException(request, httpResponse,\n                engineUserNotFoundOrInactive);\n        verify(authenticationFilter).redirectTo(request, httpResponse, AuthenticationFilter.USER_NOT_FOUND_JSP);\n    }\n\n    @Test\n    public void testAccountLockoutExceptionOnDoFilteringReturns429WithRetryAfter() throws Exception {\n        when(httpRequest.getServletPath()).thenReturn(\"/apps\");\n        when(httpRequest.getPathInfo()).thenReturn(\"/app/home\");\n\n        AccountLockedRuntimeException accountLockoutException = new AccountLockedRuntimeException(\"Account is locked\");\n        authenticationFilter.addRule(createThrowingExceptionRule(accountLockoutException));\n\n        // Set up a tracker with a lockout duration\n        LoginFailureTracker tracker = mock(LoginFailureTracker.class);\n        when(tracker.getLockoutDurationSeconds()).thenReturn(300L);\n        WebApplicationContext springContext = mock(WebApplicationContext.class);\n        when(springContext.getBean(LoginFailureTracker.class)).thenReturn(tracker);\n        when(servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE))\n                .thenReturn(springContext);\n        authenticationFilter.init(filterConfig);\n\n        authenticationFilter.doAuthenticationFiltering(request, httpResponse, chain);\n\n        verify(httpResponse).setStatus(429);\n        verify(httpResponse).setHeader(\"Retry-After\", \"300\");\n        verify(chain, never()).doFilter(httpRequest, httpResponse);\n    }\n\n    @Test\n    public void testAccountLockoutExceptionReturns429WithoutRetryAfterWhenNoTracker() throws Exception {\n        when(httpRequest.getServletPath()).thenReturn(\"/apps\");\n        when(httpRequest.getPathInfo()).thenReturn(\"/app/home\");\n\n        AccountLockedRuntimeException accountLockoutException = new AccountLockedRuntimeException(\"Account is locked\");\n        authenticationFilter.addRule(createThrowingExceptionRule(accountLockoutException));\n\n        // No Spring context → loginFailureTracker stays null (setUp default)\n        authenticationFilter.doAuthenticationFiltering(request, httpResponse, chain);\n\n        verify(httpResponse).setStatus(429);\n        verify(httpResponse, never()).setHeader(eq(\"Retry-After\"), anyString());\n        verify(chain, never()).doFilter(httpRequest, httpResponse);\n    }\n\n    @Test\n    public void testTenantIsPausedExceptionOnDoFiltering() throws Exception {\n        when(httpRequest.getServletPath()).thenReturn(\"/apps\");\n        when(httpRequest.getPathInfo()).thenReturn(\"/app/home\");\n        PlatformUnderMaintenanceException platformUnderMaintenanceException = new PlatformUnderMaintenanceException(\n                \"login failed\");\n        authenticationFilter.addRule(createThrowingExceptionRule(platformUnderMaintenanceException));\n\n        authenticationFilter.doAuthenticationFiltering(request, httpResponse, chain);\n\n        verify(authenticationManager, never()).getLoginPageURL(eq(request), anyString());\n        verify(chain, never()).doFilter(httpRequest, httpResponse);\n        verify(authenticationFilter).handlePlatformUnderMaintenanceException(request, httpResponse,\n                platformUnderMaintenanceException);\n        verify(httpResponse).sendError(eq(HttpServletResponse.SC_SERVICE_UNAVAILABLE), anyString());\n    }\n\n    @Test\n    public void should_redirect_to_503_error_page_in_maintenance() throws Exception {\n        when(httpRequest.getServletPath()).thenReturn(\"/apps\");\n        when(httpRequest.getPathInfo()).thenReturn(\"/app/home\");\n        doReturn(true).when(authenticationFilter).isPlaformInMaintenance(request);\n        authenticationFilter.addRule(createPassingRule());\n\n        authenticationFilter.doAuthenticationFiltering(request, httpResponse, chain);\n\n        verify(authenticationManager, never()).getLoginPageURL(eq(request), anyString());\n        verify(chain, never()).doFilter(httpRequest, httpResponse);\n        verify(authenticationFilter).handlePlatformUnderMaintenanceException(eq(request), eq(httpResponse),\n                any(PlatformUnderMaintenanceException.class));\n        verify(httpResponse).sendError(eq(HttpServletResponse.SC_SERVICE_UNAVAILABLE), anyString());\n    }\n\n    @Test\n    public void should_not_redirect_to_503_error_page_in_maintenance_for_rest_requests() throws Exception {\n        AuthenticationFilter authenticationFilterWithConfig = spy(new AuthenticationFilter());\n        doReturn(authenticationManager).when(authenticationFilterWithConfig).getAuthenticationManager();\n        FilterConfig filterConfig = mock(FilterConfig.class);\n        when(filterConfig.getServletContext()).thenReturn(servletContext);\n        List<String> initParams = new ArrayList<>();\n        initParams.add(AuthenticationFilter.REDIRECT_PARAM);\n        when(filterConfig.getInitParameterNames()).thenReturn(Collections.enumeration(initParams));\n        when(filterConfig.getInitParameter(AuthenticationFilter.REDIRECT_PARAM)).thenReturn(Boolean.FALSE.toString());\n        authenticationFilterWithConfig.init(filterConfig);\n        when(httpRequest.getServletPath()).thenReturn(\"/apps\");\n        when(httpRequest.getPathInfo()).thenReturn(\"/app/home\");\n        doReturn(true).when(authenticationFilterWithConfig).isPlaformInMaintenance(request);\n        authenticationFilterWithConfig.addRule(createPassingRule());\n\n        authenticationFilterWithConfig.doAuthenticationFiltering(request, httpResponse, chain);\n\n        verify(chain).doFilter(httpRequest, httpResponse);\n        verify(httpResponse, never()).sendError(eq(HttpServletResponse.SC_SERVICE_UNAVAILABLE), anyString());\n        verify(httpResponse, never()).setStatus(eq(HttpServletResponse.SC_SERVICE_UNAVAILABLE));\n    }\n\n    @Test\n    public void should_be_able_to_display_error_pages_in_maintenance() throws Exception {\n        when(httpRequest.getServletPath()).thenReturn(\"/portal/resource/app\");\n        when(httpRequest.getPathInfo()).thenReturn(\"/appDirectoryBonita/error-503/content/\");\n        doReturn(true).when(authenticationFilter).isPlaformInMaintenance(request);\n        authenticationFilter.addRule(createPassingRule());\n\n        authenticationFilter.doAuthenticationFiltering(request, httpResponse, chain);\n\n        verify(authenticationManager, never()).getLoginPageURL(eq(request), anyString());\n        verify(chain).doFilter(httpRequest, httpResponse);\n        verify(httpResponse, never()).sendError(eq(HttpServletResponse.SC_SERVICE_UNAVAILABLE), anyString());\n    }\n\n    @Test\n    public void should_be_able_to_display_error_pages_theme_in_maintenance() throws Exception {\n        when(httpRequest.getServletPath()).thenReturn(\"/portal/resource/app\");\n        when(httpRequest.getPathInfo()).thenReturn(\"/appDirectoryBonita/error-503/theme/theme.css\");\n        doReturn(true).when(authenticationFilter).isPlaformInMaintenance(request);\n        authenticationFilter.addRule(createPassingRule());\n\n        authenticationFilter.doAuthenticationFiltering(request, httpResponse, chain);\n\n        verify(authenticationManager, never()).getLoginPageURL(eq(request), anyString());\n        verify(chain).doFilter(httpRequest, httpResponse);\n        verify(httpResponse, never()).sendError(eq(HttpServletResponse.SC_SERVICE_UNAVAILABLE), anyString());\n    }\n\n    @Test\n    public void should_let_technical_user_pass_when_platform_is_in_maintenance() throws Exception {\n        when(httpRequest.getServletPath()).thenReturn(\"/apps\");\n        when(httpRequest.getPathInfo()).thenReturn(\"/app/home\");\n        doReturn(true).when(authenticationFilter).isPlaformInMaintenance(request);\n        when(httpSession.getAttribute(SessionUtil.API_SESSION_PARAM_KEY)).thenReturn(apiSession);\n        doReturn(true).when(apiSession).isTechnicalUser();\n        authenticationFilter.addRule(createPassingRule());\n\n        authenticationFilter.doAuthenticationFiltering(request, httpResponse, chain);\n\n        verify(authenticationManager, never()).getLoginPageURL(eq(request), anyString());\n        verify(chain).doFilter(httpRequest, httpResponse);\n        verify(authenticationFilter, never()).handlePlatformUnderMaintenanceException(eq(request), eq(httpResponse),\n                any(PlatformUnderMaintenanceException.class));\n    }\n\n    @Test\n    public void testRedirectTo() throws Exception {\n        final String context = \"/bonita\";\n        when(httpRequest.getContextPath()).thenReturn(context);\n        authenticationFilter.redirectTo(request, httpResponse, AuthenticationFilter.USER_NOT_FOUND_JSP);\n        verify(httpResponse, times(1)).sendRedirect(context + AuthenticationFilter.USER_NOT_FOUND_JSP);\n        verify(httpRequest, times(1)).getContextPath();\n    }\n\n    @Test\n    public void testFilterWithExcludedURL() throws Exception {\n        final String url = \"test\";\n        when(httpRequest.getRequestURL()).thenReturn(new StringBuffer(url));\n        doReturn(true).when(authenticationFilter).matchExcludePatterns(url);\n        authenticationFilter.doFilter(httpRequest, httpResponse, chain);\n        verify(authenticationFilter, times(0)).doAuthenticationFiltering(request, httpResponse, chain);\n        verify(chain, times(1)).doFilter(httpRequest, httpResponse);\n    }\n\n    @Test\n    public void testMatchExcludePatterns() {\n        matchExcludePattern(\"http://host/bonita/portal/resource/page/API/system/session/unusedId\", true);\n        matchExcludePattern(\"http://host/bonita/apps/app/API/system/session/unusedId\", true);\n        matchExcludePattern(\"http://host/bonita/portal/resource/page/content/\", false);\n        matchExcludePattern(\"http://host/bonita/apps/app/page/\", false);\n    }\n\n    @Test\n    public void testSemicolonTraversalUrlsMustNotBeExcluded() {\n        // Semicolon traversal patterns must NOT bypass the authentication filter\n        matchExcludePattern(\"http://host/bonita/apps/FAKE/API/system/session/..;/..;/..;/serverAPI/something\", false);\n        matchExcludePattern(\"http://host/bonita/apps/FAKE/API/..;/..;/serverAPI/something\", false);\n        matchExcludePattern(\n                \"http://host/bonita/portal/resource/page/API/system/session/..;/..;/..;/serverAPI/something\", false);\n    }\n\n    @Test\n    public void testPercentEncodedSemicolonTraversalUrlsMustNotBeExcluded() {\n        // Percent-encoded semicolons (%3b / %3B) must also NOT bypass the authentication filter\n        matchExcludePattern(\"http://host/bonita/apps/FAKE/API/system/session/..%3b/..%3b/..%3b/serverAPI/something\",\n                false);\n        matchExcludePattern(\"http://host/bonita/apps/FAKE/API/..%3b/..%3b/serverAPI/something\", false);\n        matchExcludePattern(\n                \"http://host/bonita/portal/resource/page/API/system/session/..%3B/..%3B/..%3B/serverAPI/something\",\n                false);\n    }\n\n    @Test\n    public void testUrlsWithEncodedSpacesShouldMatchExcludePatterns() {\n        // URLs with %20 encoded spaces (e.g., process names with spaces) must still be correctly\n        // matched by the exclude pattern and not throw URISyntaxException during normalization.\n        matchExcludePattern(\"http://host/bonita/apps/My%20App/API/system/session/unusedId\", true);\n        matchExcludePattern(\"http://host/bonita/portal/resource/page/API/system/session/my%20resource\", true);\n    }\n\n    @Test\n    public void testEncodedSpacesWithSemicolonTraversalMustNotBeExcluded() {\n        // Combined %20 + semicolon traversal must still be blocked (no regression on CVE-233)\n        matchExcludePattern(\n                \"http://host/bonita/apps/My%20App/API/system/session/..;/..;/..;/serverAPI/something\", false);\n        matchExcludePattern(\n                \"http://host/bonita/apps/My%20App/API/..%3b/..%3b/serverAPI/something\", false);\n    }\n\n    @Test\n    public void testMakeRedirectUrl() {\n        when(httpRequest.getRequestURI()).thenReturn(\"/apps/appDirectoryBonita\");\n        final RedirectUrl redirectUrl = authenticationFilter.makeRedirectUrl(request);\n        verify(httpRequest, times(1)).getRequestURI();\n        assertThat(redirectUrl.getUrl()).isEqualToIgnoringCase(\"/apps/appDirectoryBonita\");\n    }\n\n    @Test\n    public void testMakeRedirectUrlWithRedirectUrlIncluded() {\n        when(httpRequest.getRequestURI()).thenReturn(\"/apps/appDirectoryBonita\");\n        when(request.getRedirectUrl()).thenReturn(\"/redirectLink\");\n        final RedirectUrl redirectUrl = authenticationFilter.makeRedirectUrl(request);\n        assertThat(redirectUrl.getUrl()).isEqualToIgnoringCase(\"/redirectLink\");\n    }\n\n    @Test\n    public void testCompileNullPattern() {\n        assertThat(authenticationFilter.compilePattern(null)).isNull();\n    }\n\n    @Test\n    public void testCompileWrongPattern() {\n        assertThat(authenticationFilter.compilePattern(\"((((\")).isNull();\n    }\n\n    @Test\n    public void testCompileSimplePattern() {\n        final String patternToCompile = \"test\";\n        assertThat(authenticationFilter.compilePattern(patternToCompile)).isNotNull().has(new Condition<>() {\n\n            @Override\n            public boolean matches(final Pattern pattern) {\n                return pattern.pattern().equalsIgnoreCase(patternToCompile);\n            }\n        });\n    }\n\n    @Test\n    public void testCompileExcludePattern() {\n        final String patternToCompile = \"^/(bonita/)?(?:(login\\\\.jsp$)|(images/)|(redirectCasToCatchHash\\\\.jsp)|(loginservice)|(serverAPI)|(maintenance\\\\.jsp$)|(API/platform/)|(platformloginservice$)|(/bonita/?$)|(logoutservice))\";\n        assertThat(authenticationFilter.compilePattern(patternToCompile)).isNotNull().has(new Condition<>() {\n\n            @Override\n            public boolean matches(final Pattern pattern) {\n                return pattern.pattern().equalsIgnoreCase(patternToCompile);\n            }\n        });\n    }\n\n    private void matchExcludePattern(final String urlToMatch, final Boolean mustMatch) {\n        if (authenticationFilter.matchExcludePatterns(urlToMatch) != mustMatch) {\n            Assertions.fail(\"Matching excludePattern and the Url \" + urlToMatch + \" must return \" + mustMatch);\n        }\n    }\n\n    private AuthenticationRule createPassingRule() {\n        return new AuthenticationRule() {\n\n            @Override\n            public boolean doAuthorize(final HttpServletRequestAccessor request, HttpServletResponse response) {\n                return true;\n            }\n        };\n    }\n\n    private AuthenticationRule createFailingRule() {\n        return new AuthenticationRule() {\n\n            @Override\n            public boolean doAuthorize(final HttpServletRequestAccessor request, HttpServletResponse response) {\n                return false;\n            }\n        };\n    }\n\n    private AuthenticationRule createThrowingExceptionRule(RuntimeException e) {\n        return new AuthenticationRule() {\n\n            @Override\n            public boolean doAuthorize(final HttpServletRequestAccessor request, HttpServletResponse response) {\n                throw e;\n            }\n        };\n    }\n\n    @Test\n    public void init_should_set_login_failure_tracker_on_rules_when_spring_context_has_bean() throws Exception {\n        LoginFailureTracker tracker = mock(LoginFailureTracker.class);\n        WebApplicationContext springContext = mock(WebApplicationContext.class);\n        when(springContext.getBean(LoginFailureTracker.class)).thenReturn(tracker);\n\n        ServletContext servletContextWithSpring = mock(ServletContext.class);\n        when(servletContextWithSpring.getContextPath()).thenReturn(\"\");\n        when(servletContextWithSpring.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE))\n                .thenReturn(springContext);\n\n        FilterConfig filterConfigWithSpring = mock(FilterConfig.class);\n        when(filterConfigWithSpring.getServletContext()).thenReturn(servletContextWithSpring);\n        when(filterConfigWithSpring.getInitParameterNames()).thenReturn(Collections.emptyEnumeration());\n\n        AuthenticationFilter filter = spy(new AuthenticationFilter());\n        doReturn(authenticationManager).when(filter).getAuthenticationManager();\n        filter.init(filterConfigWithSpring);\n\n        // The default rule (AlreadyLoggedInRule) should have received the tracker\n        assertThat(filter.getRules()).isNotEmpty();\n        // Verify by calling getLoginManager() — it should create a LoginManager with the tracker\n        // We verify indirectly: a rule added before init() should get the tracker via init()\n        AuthenticationRule rule = filter.getRules().getFirst();\n        LoginManager loginManager = rule.getLoginManager();\n        assertThat(loginManager).extracting(\"loginFailureTracker\").isSameAs(tracker);\n    }\n\n    @Test\n    public void init_should_set_null_tracker_on_rules_when_no_spring_context() throws Exception {\n        // servletContext mock does not have a Spring WebApplicationContext attribute\n        AuthenticationFilter filter = spy(new AuthenticationFilter());\n        doReturn(authenticationManager).when(filter).getAuthenticationManager();\n\n        FilterConfig filterConfigNoSpring = mock(FilterConfig.class);\n        ServletContext servletContextNoSpring = mock(ServletContext.class);\n        when(servletContextNoSpring.getContextPath()).thenReturn(\"\");\n        when(filterConfigNoSpring.getServletContext()).thenReturn(servletContextNoSpring);\n        when(filterConfigNoSpring.getInitParameterNames()).thenReturn(Collections.emptyEnumeration());\n\n        filter.init(filterConfigNoSpring);\n\n        assertThat(filter.getRules()).isNotEmpty();\n        AuthenticationRule rule = filter.getRules().getFirst();\n        LoginManager loginManager = rule.getLoginManager();\n        assertThat(loginManager).extracting(\"loginFailureTracker\").isNull();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/login/filter/FakeAuthenticationManager.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.filter;\n\nimport org.bonitasoft.console.common.server.auth.impl.standard.StandardAuthenticationManagerImpl;\n\npublic class FakeAuthenticationManager extends StandardAuthenticationManagerImpl {\n\n    public FakeAuthenticationManager() {\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/login/filter/FrameSecurityFilterTest.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.filter;\n\nimport static org.mockito.Mockito.*;\nimport static org.mockito.MockitoAnnotations.initMocks;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.FilterConfig;\nimport javax.servlet.ServletContext;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.bonitasoft.console.common.server.preferences.properties.SecurityProperties;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class FrameSecurityFilterTest {\n\n    @Mock\n    private HttpServletRequest httpRequest;\n\n    @Mock\n    private HttpServletResponse httpResponse;\n\n    @Mock\n    private FilterConfig filterConfig;\n\n    @Mock\n    private FilterChain chain;\n\n    @Mock\n    private ServletContext servletContext;\n\n    @Mock\n    private SecurityProperties securityProperties;\n\n    @Spy\n    FrameSecurityFilter frameSecurityFilter;\n\n    @Before\n    public void setUp() throws Exception {\n        initMocks(this);\n        doReturn(securityProperties).when(frameSecurityFilter).getSecurityProperties();\n        when(servletContext.getContextPath()).thenReturn(\"\");\n        when(filterConfig.getServletContext()).thenReturn(servletContext);\n    }\n\n    @Test\n    public void should_add_default_headers() throws Exception {\n\n        frameSecurityFilter.init(filterConfig);\n\n        frameSecurityFilter.proceedWithFiltering(httpRequest, httpResponse, chain);\n\n        verify(httpResponse).setHeader(FrameSecurityFilter.X_FRAME_OPTIONS_HEADER,\n                FrameSecurityFilter.X_FRAME_OPTIONS_HEADER_DEFAULT);\n        verify(httpResponse).setHeader(FrameSecurityFilter.CONTENT_SECURITY_POLICY_HEADER,\n                FrameSecurityFilter.CONTENT_SECURITY_POLICY_HEADER_DEFAULT);\n    }\n\n    @Test\n    public void should_add_configured_headers() throws Exception {\n\n        doReturn(\"DENY\").when(securityProperties).getXFrameOptionsHeader();\n        doReturn(\"frame-ancestors 'none';\").when(securityProperties).getContentSecurityPolicyHeader();\n        frameSecurityFilter.init(filterConfig);\n\n        frameSecurityFilter.proceedWithFiltering(httpRequest, httpResponse, chain);\n\n        verify(httpResponse).setHeader(FrameSecurityFilter.X_FRAME_OPTIONS_HEADER, \"DENY\");\n        verify(httpResponse).setHeader(FrameSecurityFilter.CONTENT_SECURITY_POLICY_HEADER, \"frame-ancestors 'none';\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/login/filter/RestAPIAuthorizationFilterTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.filter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.*;\n\nimport java.util.regex.Pattern;\n\nimport javax.servlet.*;\nimport javax.servlet.http.HttpServletMapping;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpSession;\n\nimport org.assertj.core.api.Assertions;\nimport org.assertj.core.api.Condition;\nimport org.bonitasoft.console.common.server.login.servlet.PlatformLoginServlet;\nimport org.bonitasoft.console.common.server.utils.SessionUtil;\nimport org.bonitasoft.engine.api.permission.APICallContext;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.bonitasoft.engine.session.PlatformSession;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class RestAPIAuthorizationFilterTest {\n\n    @Mock\n    private HttpServletRequest request;\n    @Mock\n    private HttpServletResponse response;\n    @Mock\n    private FilterChain chain;\n    @Mock\n    private APISession apiSession;\n    @Mock\n    private HttpSession httpSession;\n    @Mock\n    private FilterConfig filterConfig;\n    @Mock\n    private ServletContext servletContext;\n    @Mock\n    private HttpServletMapping httpServletMapping;\n\n    private final RestAPIAuthorizationFilter restAPIAuthorizationFilter = new RestAPIAuthorizationFilter();\n\n    @Before\n    public void setUp() throws Exception {\n        doReturn(httpServletMapping).when(request).getHttpServletMapping();\n        doReturn(httpSession).when(request).getSession();\n        doReturn(\"\").when(request).getQueryString();\n        doReturn(apiSession).when(httpSession).getAttribute(SessionUtil.API_SESSION_PARAM_KEY);\n        doReturn(false).when(apiSession).isTechnicalUser();\n        when(servletContext.getContextPath()).thenReturn(\"\");\n        when(filterConfig.getServletContext()).thenReturn(servletContext);\n        restAPIAuthorizationFilter.init(filterConfig);\n    }\n\n    private void initSpy(final RestAPIAuthorizationFilter restAPIAuthorizationFilterSpy) throws ServletException {\n        doReturn(\"GET\").when(request).getMethod();\n        doReturn(\"\").when(restAPIAuthorizationFilterSpy).getRequestBody(request);\n    }\n\n    @Test\n    public void should_checkPermissions_call_engine_check_if_security_is_enabled() throws Exception {\n        final RestAPIAuthorizationFilter restAPIAuthorizationFilterSpy = spy(restAPIAuthorizationFilter);\n        initSpy(restAPIAuthorizationFilterSpy);\n        doReturn(true).when(restAPIAuthorizationFilterSpy).isApiAuthorizationsCheckEnabled();\n        doReturn(true).when(restAPIAuthorizationFilterSpy)\n                .enginePermissionsCheck(new APICallContext(\"GET\", \"bpm\", \"case\", null, \"\", \"\"), apiSession);\n\n        //when\n        final boolean isAuthorized = restAPIAuthorizationFilterSpy.checkPermissions(request, \"bpm\", \"case\", null);\n\n        //then\n        assertThat(isAuthorized).isTrue();\n        verify(restAPIAuthorizationFilterSpy)\n                .enginePermissionsCheck(new APICallContext(\"GET\", \"bpm\", \"case\", null, \"\", \"\"), apiSession);\n    }\n\n    @Test\n    public void should_not_call_engine_check_if_secu_is_enabled_but_session_call_is_always_authorized()\n            throws Exception {\n        final RestAPIAuthorizationFilter restAPIAuthorizationFilterSpy = spy(restAPIAuthorizationFilter);\n        doReturn(\"GET\").when(request).getMethod();\n        doReturn(true).when(restAPIAuthorizationFilterSpy).isApiAuthorizationsCheckEnabled();\n        doReturn(\"\").when(restAPIAuthorizationFilterSpy).getRequestBody(any());\n        //when\n        final boolean isAuthorized = restAPIAuthorizationFilterSpy.checkPermissions(request, \"system\", \"session\", null);\n\n        //then\n        assertThat(isAuthorized).isTrue();\n        verify(restAPIAuthorizationFilterSpy).isAlwaysAuthorizedResource(any(APICallContext.class));\n        verify(restAPIAuthorizationFilterSpy, never()).enginePermissionsCheck(any(APICallContext.class),\n                any(APISession.class));\n    }\n\n    @Test\n    public void should_checkPermissions_do_not_call_check_if_technical() throws Exception {\n        doReturn(true).when(apiSession).isTechnicalUser();\n        final RestAPIAuthorizationFilter restAPIAuthorizationFilterSpy = spy(restAPIAuthorizationFilter);\n        initSpy(restAPIAuthorizationFilterSpy);\n        doReturn(true).when(restAPIAuthorizationFilterSpy).isApiAuthorizationsCheckEnabled();\n\n        //when\n        final boolean isAuthorized = restAPIAuthorizationFilterSpy.checkPermissions(request, \"bpm\", \"case\", null);\n\n        //then\n        assertThat(isAuthorized).isTrue();\n        verify(restAPIAuthorizationFilterSpy, never()).enginePermissionsCheck(any(APICallContext.class),\n                any(APISession.class));\n    }\n\n    @Test\n    public void should_checkPermissions_do_nothing_if_secu_is_disabled() throws Exception {\n        final RestAPIAuthorizationFilter restAPIAuthorizationFilterSpy = spy(restAPIAuthorizationFilter);\n        initSpy(restAPIAuthorizationFilterSpy);\n        doReturn(false).when(restAPIAuthorizationFilterSpy).isApiAuthorizationsCheckEnabled();\n\n        //when\n        final boolean isAuthorized = restAPIAuthorizationFilterSpy.checkPermissions(request, \"bpm\", \"case\", null);\n\n        //then\n        assertThat(isAuthorized).isTrue();\n        verify(restAPIAuthorizationFilterSpy, never()).enginePermissionsCheck(any(APICallContext.class),\n                any(APISession.class));\n    }\n\n    @Test\n    public void should_checkPermissions_parse_the_request() throws Exception {\n        doReturn(\"/bpm/case/15\").when(request).getPathInfo();\n        final RestAPIAuthorizationFilter restAPIAuthorizationFilterSpy = spy(restAPIAuthorizationFilter);\n        doReturn(true).when(restAPIAuthorizationFilterSpy).checkPermissions(eq(request), eq(\"bpm\"), eq(\"case\"),\n                eq(APIID.makeAPIID(15L)));\n\n        //when\n        restAPIAuthorizationFilterSpy.checkPermissions(request);\n\n        //then\n        verify(restAPIAuthorizationFilterSpy).checkPermissions(eq(request), eq(\"bpm\"), eq(\"case\"),\n                eq(APIID.makeAPIID(15L)));\n    }\n\n    @Test\n    public void should_checkValidCondition_check_session_is_platform() throws Exception {\n        doReturn(\"/API\").when(request).getServletPath();\n        doReturn(\"/platform/plop\").when(request).getPathInfo();\n        doReturn(mock(PlatformSession.class)).when(httpSession)\n                .getAttribute(PlatformLoginServlet.PLATFORM_SESSION_PARAM_KEY);\n        //when\n        restAPIAuthorizationFilter.proceedWithFiltering(request, response, chain);\n\n        verify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n    }\n\n    @Test\n    public void should_checkValidCondition_check_session_is_platform_with_API_toolkit() throws Exception {\n        doReturn(\"/APIToolkit\").when(request).getServletPath();\n        doReturn(\"/platform/plop\").when(request).getPathInfo();\n        doReturn(mock(PlatformSession.class)).when(httpSession)\n                .getAttribute(PlatformLoginServlet.PLATFORM_SESSION_PARAM_KEY);\n        //when\n        restAPIAuthorizationFilter.proceedWithFiltering(request, response, chain);\n\n        verify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n    }\n\n    @Test\n    public void should_checkValidCondition_check_unauthorized_if_no_platform_session() throws Exception {\n        doReturn(\"/API\").when(request).getServletPath();\n        doReturn(\"/platform/plop\").when(request).getPathInfo();\n        doReturn(null).when(httpSession)\n                .getAttribute(PlatformLoginServlet.PLATFORM_SESSION_PARAM_KEY);\n        //when\n        restAPIAuthorizationFilter.proceedWithFiltering(request, response, chain);\n\n        verify(chain, never()).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n        verify(response).setStatus(HttpServletResponse.SC_UNAUTHORIZED);\n    }\n\n    @Test\n    public void should_checkValidCondition_check_unauthorized_if_no_tenant_session() throws Exception {\n        doReturn(null).when(httpSession).getAttribute(SessionUtil.API_SESSION_PARAM_KEY);\n        doReturn(\"/API\").when(request).getServletPath();\n        doReturn(\"/bpm/case/15\").when(request).getPathInfo();\n        //when\n        restAPIAuthorizationFilter.proceedWithFiltering(request, response, chain);\n\n        verify(chain, never()).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n        verify(response).setStatus(HttpServletResponse.SC_UNAUTHORIZED);\n    }\n\n    @Test\n    public void should_checkValidCondition_check_unauthorized_if_session_is_invalid() throws Exception {\n        final RestAPIAuthorizationFilter restAPIAuthorizationFilterSpy = spy(restAPIAuthorizationFilter);\n        doReturn(\"/API\").when(request).getServletPath();\n        doThrow(InvalidSessionException.class).when(restAPIAuthorizationFilterSpy)\n                .checkPermissions(any(HttpServletRequest.class));\n        //when\n        restAPIAuthorizationFilterSpy.proceedWithFiltering(request, response, chain);\n\n        verify(chain, never()).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n        verify(response).setStatus(HttpServletResponse.SC_UNAUTHORIZED);\n    }\n\n    @Test\n    public void should_checkValidCondition_check_permission_if_is_tenant_is_forbidden() throws Exception {\n        final RestAPIAuthorizationFilter restAPIAuthorizationFilterSpy = spy(restAPIAuthorizationFilter);\n        doReturn(\"/API\").when(request).getServletPath();\n        doReturn(false).when(restAPIAuthorizationFilterSpy).checkPermissions(any(HttpServletRequest.class));\n\n        //when\n        restAPIAuthorizationFilterSpy.proceedWithFiltering(request, response, chain);\n\n        verify(chain, never()).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n        verify(response).setStatus(HttpServletResponse.SC_FORBIDDEN);\n    }\n\n    @Test\n    public void should_checkValidCondition_check_permission_if_is_tenant_is_ok() throws Exception {\n        final RestAPIAuthorizationFilter restAPIAuthorizationFilterSpy = spy(restAPIAuthorizationFilter);\n        doReturn(\"/API\").when(request).getServletPath();\n        doReturn(\"/bpm/case/15\").when(request).getPathInfo();\n        doReturn(true).when(restAPIAuthorizationFilterSpy).checkPermissions(any(HttpServletRequest.class));\n\n        //when\n        restAPIAuthorizationFilterSpy.proceedWithFiltering(request, response, chain);\n\n        verify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n    }\n\n    @Test\n    public void should_checkValidCondition_check_permission_if_pathInfo_is_null() throws Exception {\n        final RestAPIAuthorizationFilter restAPIAuthorizationFilterSpy = spy(restAPIAuthorizationFilter);\n        doReturn(\"/API\").when(request).getServletPath();\n        doReturn(null).when(request).getPathInfo();\n        doReturn(true).when(restAPIAuthorizationFilterSpy).checkPermissions(any(HttpServletRequest.class));\n\n        //when\n        restAPIAuthorizationFilterSpy.proceedWithFiltering(request, response, chain);\n\n        verify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n    }\n\n    @Test(expected = ServletException.class)\n    public void should_checkValidCondition_catch_runtime() throws ServletException {\n        doThrow(new RuntimeException()).when(request).getServletPath();\n\n        //when\n        restAPIAuthorizationFilter.proceedWithFiltering(request, response, chain);\n    }\n\n    @Test\n    public void testFilterWithExcludedURL() throws Exception {\n        final RestAPIAuthorizationFilter restAPIAuthorizationFilterSpy = spy(restAPIAuthorizationFilter);\n        final String url = \"test\";\n        when(request.getRequestURL()).thenReturn(new StringBuffer(url));\n        doReturn(true).when(restAPIAuthorizationFilterSpy).matchExcludePatterns(url);\n        restAPIAuthorizationFilterSpy.doFilter(request, response, chain);\n        verify(restAPIAuthorizationFilterSpy, times(0)).proceedWithFiltering(request, response, chain);\n        verify(chain, times(1)).doFilter(request, response);\n    }\n\n    @Test\n    public void testMatchExcludePatterns() {\n        matchExcludePattern(\"http://host/bonita/portal/resource/page/API/system/i18ntranslation\", true);\n        matchExcludePattern(\"http://host/bonita/apps/app/API/system/i18ntranslation\", true);\n        matchExcludePattern(\"http://host/bonita/API/system/i18ntranslation\", true);\n        matchExcludePattern(\"http://host/bonita/API/bpm/process\", false);\n        matchExcludePattern(\"http://host/bonita/API/bpm/process/;i18ntranslation\", false);\n        matchExcludePattern(\"http://host/bonita/API/bpm/process/../../../API/system/i18ntranslation\", false);\n        matchExcludePattern(\"http://host/bonita/API/system/i18ntranslation/../../bpm/process\", false);\n        matchExcludePattern(\"http://host/bonita/API/bpm/activity/test/../i18ntranslation/..?p=0&c=10\", false);\n        matchExcludePattern(\"http://host/bonita/API/bpm/activity/i18ntranslation/../?p=0&c=10\", false);\n    }\n\n    @Test\n    public void testSemicolonTraversalUrlsMustNotBeExcluded() throws Exception {\n        // Semicolon traversal patterns must NOT bypass the authorization filter\n        matchExcludePattern(\"http://host/bonita/apps/FAKE/API/system/i18ntranslation/..;/..;/..;/serverAPI/something\",\n                false);\n        matchExcludePattern(\"http://host/bonita/apps/FAKE/API/..;/..;/serverAPI/something\", false);\n        matchExcludePattern(\n                \"http://host/bonita/portal/resource/page/API/system/i18ntranslation/..;/..;/..;/serverAPI/something\",\n                false);\n    }\n\n    @Test\n    public void testPercentEncodedSemicolonTraversalUrlsMustNotBeExcluded() throws Exception {\n        // Percent-encoded semicolons (%3b / %3B) must also NOT bypass the authorization filter\n        matchExcludePattern(\n                \"http://host/bonita/apps/FAKE/API/system/i18ntranslation/..%3b/..%3b/..%3b/serverAPI/something\",\n                false);\n        matchExcludePattern(\"http://host/bonita/apps/FAKE/API/..%3b/..%3b/serverAPI/something\", false);\n        matchExcludePattern(\n                \"http://host/bonita/portal/resource/page/API/system/i18ntranslation/..%3B/..%3B/..%3B/serverAPI/something\",\n                false);\n    }\n\n    @Test\n    public void testCompileNullPattern() {\n        assertThat(restAPIAuthorizationFilter.compilePattern(null)).isNull();\n    }\n\n    @Test\n    public void testCompileWrongPattern() {\n        assertThat(restAPIAuthorizationFilter.compilePattern(\"((((\")).isNull();\n    }\n\n    @Test\n    public void testCompileSimplePattern() {\n        final String patternToCompile = \"test\";\n        assertThat(restAPIAuthorizationFilter.compilePattern(patternToCompile)).isNotNull().has(new Condition<>() {\n\n            @Override\n            public boolean matches(final Pattern pattern) {\n                return pattern.pattern().equalsIgnoreCase(patternToCompile);\n            }\n        });\n    }\n\n    @Test\n    public void testCompileExcludePattern() {\n        final String patternToCompile = RestAPIAuthorizationFilter.AUTHORIZATION_FILTER_EXCLUDED_PAGES_PATTERN;\n        assertThat(restAPIAuthorizationFilter.compilePattern(patternToCompile)).isNotNull().has(new Condition<>() {\n\n            @Override\n            public boolean matches(final Pattern pattern) {\n                return pattern.pattern().equalsIgnoreCase(patternToCompile);\n            }\n        });\n    }\n\n    @Test\n    public void should_return_bad_request_when_api_url_is_malformed() throws Exception {\n        doReturn(new StringBuffer(\"http://host/bonita/API\")).when(request).getRequestURL();\n        doReturn(\"/API\").when(request).getServletPath();\n        doReturn(null).when(request).getPathInfo();\n\n        //when\n        restAPIAuthorizationFilter.proceedWithFiltering(request, response, chain);\n\n        //then\n        verify(response).setStatus(HttpServletResponse.SC_BAD_REQUEST);\n        verify(response).flushBuffer();\n        verify(chain, never()).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n    }\n\n    private void matchExcludePattern(final String urlToMatch, final Boolean mustMatch) {\n        if (restAPIAuthorizationFilter.matchExcludePatterns(urlToMatch) != mustMatch) {\n            Assertions.fail(\"Matching excludePattern and the Url \" + urlToMatch + \" must return \" + mustMatch);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/login/filter/TokenGeneratorFilterTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.filter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.verify;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.http.Cookie;\nimport javax.servlet.http.HttpSession;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockHttpSession;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class TokenGeneratorFilterTest {\n\n    @Mock\n    private FilterChain filterChain;\n    private final HttpSession session = new MockHttpSession();\n    private final MockHttpServletRequest request = new MockHttpServletRequest();\n    private final MockHttpServletResponse response = new MockHttpServletResponse();\n\n    final String contextPath = \"bonitaTest\";\n    final String bonitaTokenName = \"X-Bonita-API-Token\";\n    final String bonitaTokenValue = \"sdfsdfjhvèzv\";\n\n    private final TokenGeneratorFilter tokenGeneratorFilter = new TokenGeneratorFilter();\n\n    @Before\n    public void setUp() {\n        request.setSession(session);\n        request.setContextPath(contextPath);\n    }\n\n    @Test\n    public void should_add_security_token_in_headers_and_in_cookies_when_token_already_exists() throws Exception {\n        session.setAttribute(TokenGenerator.API_TOKEN, bonitaTokenValue);\n        response.addHeader(bonitaTokenName, bonitaTokenValue);\n\n        tokenGeneratorFilter.doFilter(request, response, filterChain);\n\n        final Cookie csrfCookie = response.getCookie(bonitaTokenName);\n\n        assertThat(csrfCookie.getName()).isEqualTo(bonitaTokenName);\n        assertThat(csrfCookie.getPath()).isEqualTo(contextPath);\n        assertThat(csrfCookie.getValue()).isEqualTo(bonitaTokenValue);\n\n        assertThat(session.getAttribute(TokenGenerator.API_TOKEN)).isEqualTo(bonitaTokenValue);\n        assertThat(response.getHeader(bonitaTokenName)).isEqualTo(bonitaTokenValue);\n\n        verify(filterChain).doFilter(request, response);\n    }\n\n    @Test\n    public void should_add_security_token_in_headers_and_in_cookies_when_no_previous_call() throws Exception {\n        tokenGeneratorFilter.doFilter(request, response, filterChain);\n\n        final Cookie csrfCookie = response.getCookie(bonitaTokenName);\n\n        assertThat(csrfCookie.getName()).isEqualTo(bonitaTokenName);\n        assertThat(csrfCookie.getPath()).isEqualTo(contextPath);\n        assertThat(csrfCookie.getValue()).isNotEqualTo(bonitaTokenValue);\n\n        assertThat(session.getAttribute(TokenGenerator.API_TOKEN)).isEqualTo(csrfCookie.getValue());\n        assertThat(response.getHeader(bonitaTokenName)).isEqualTo(csrfCookie.getValue());\n\n        verify(filterChain).doFilter(request, response);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/login/filter/TokenGeneratorTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.filter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.*;\n\nimport javax.servlet.http.Cookie;\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.console.common.server.login.PortalCookies;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.RestoreSystemProperties;\nimport org.junit.runner.RunWith;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockHttpSession;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class TokenGeneratorTest {\n\n    private final HttpSession session = new MockHttpSession();\n    private final MockHttpServletResponse response = new MockHttpServletResponse();\n    private final MockHttpServletRequest request = new MockHttpServletRequest();\n\n    private final static String CONTEXT_PATH = \"bonitaTest\";\n    private final static String BONITA_TOKEN_NAME = \"X-Bonita-API-Token\";\n    private final static String BONITA_TOKEN_VALUE = \"sdfsdfjhvèzv\";\n\n    @Spy\n    TokenGenerator tokenGenerator = new TokenGenerator();\n\n    @Spy\n    PortalCookies portalCookies = new PortalCookies();\n\n    @Rule\n    public final RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties();\n\n    @Before\n    public void setUp() throws Exception {\n        request.setContextPath(CONTEXT_PATH);\n    }\n\n    private Cookie getCookieByNameAndPath(MockHttpServletResponse response, String name, String path) {\n        for (Cookie cookie : response.getCookies()) {\n            if (cookie.getName().equals(name) && cookie.getPath().equals(path)) {\n                return cookie;\n            }\n        }\n        return null;\n    }\n\n    private Cookie aCookie(String name, String value, String path) {\n        Cookie cookie = new Cookie(name, value);\n        cookie.setPath(path);\n        return cookie;\n    }\n\n    @Test\n    public void should_create_token_and_store_it_in_session() throws Exception {\n        final String token = tokenGenerator.createOrLoadToken(session);\n        assertThat(token).isNotEmpty();\n        assertThat(session.getAttribute(TokenGenerator.API_TOKEN)).isEqualTo(token);\n    }\n\n    @Test\n    public void should_load_token_from_session() throws Exception {\n        session.setAttribute(TokenGenerator.API_TOKEN, BONITA_TOKEN_VALUE);\n        final String token = tokenGenerator.createOrLoadToken(session);\n        assertThat(token).isNotEmpty();\n        assertThat(token).isEqualTo(BONITA_TOKEN_VALUE);\n    }\n\n    @Test\n    public void testSetTokenToResponseCookie() throws Exception {\n        portalCookies.addCSRFTokenCookieToResponse(request, response, BONITA_TOKEN_VALUE);\n\n        final Cookie csrfCookie = response.getCookie(BONITA_TOKEN_NAME);\n\n        assertThat(csrfCookie.getName()).isEqualTo(BONITA_TOKEN_NAME);\n        assertThat(csrfCookie.getPath()).isEqualTo(CONTEXT_PATH);\n        assertThat(csrfCookie.getValue()).isEqualTo(BONITA_TOKEN_VALUE);\n        assertThat(csrfCookie.getSecure()).isFalse();\n    }\n\n    @Test\n    public void should_set_csrf_token_cookie_path_specified_via_system_property() throws Exception {\n        System.setProperty(\"bonita.csrf.cookie.path\", \"/\");\n        request.setContextPath(\"somepath\");\n\n        portalCookies.addCSRFTokenCookieToResponse(request, response, \"sdfsdfjhvèzv\");\n\n        Cookie csrfCookie = response.getCookie(BONITA_TOKEN_NAME);\n        assertThat(csrfCookie.getPath()).isEqualTo(\"/\");\n    }\n\n    @Test\n    public void should_set_secure_csrf_token_cookie_when_specified() throws Exception {\n        doReturn(true).when(portalCookies).isCSRFTokenCookieSecure();\n\n        portalCookies.addCSRFTokenCookieToResponse(request, response, BONITA_TOKEN_VALUE);\n\n        Cookie csrfCookie = response.getCookie(BONITA_TOKEN_NAME);\n        assertThat(csrfCookie.getName()).isEqualTo(BONITA_TOKEN_NAME);\n        assertThat(csrfCookie.getPath()).isEqualTo(CONTEXT_PATH);\n        assertThat(csrfCookie.getValue()).isEqualTo(BONITA_TOKEN_VALUE);\n        assertThat(csrfCookie.getSecure()).isTrue();\n    }\n\n    @Test\n    public void testSetTokenToResponseHeader() throws Exception {\n        tokenGenerator.setTokenToResponseHeader(response, BONITA_TOKEN_VALUE);\n\n        assertThat(response.getHeader(BONITA_TOKEN_NAME)).isEqualTo(BONITA_TOKEN_VALUE);\n    }\n\n    @Test\n    public void should_invalidate_csrf_cookie_already_existing_on_another_path_than_the_expected_one()\n            throws Exception {\n        System.setProperty(\"bonita.csrf.cookie.path\", \"/\");\n        request.setCookies(aCookie(BONITA_TOKEN_NAME, \"aValue\", \"aPath\"));\n\n        portalCookies.addCSRFTokenCookieToResponse(request, response, \"whatever\");\n\n        Cookie cookie = getCookieByNameAndPath(response, BONITA_TOKEN_NAME, \"aPath\");\n        assertThat(cookie.getMaxAge()).isEqualTo(0);\n        assertThat(cookie.getValue()).isEqualTo(\"\");\n    }\n\n    @Test\n    public void should_invalidate_csrf_cookie_already_existing_on_root_path() throws Exception {\n        request.setCookies(aCookie(BONITA_TOKEN_NAME, \"aValue\", \"aPath\"));\n\n        portalCookies.addCSRFTokenCookieToResponse(request, response, \"whatever\");\n\n        Cookie cookie = getCookieByNameAndPath(response, BONITA_TOKEN_NAME, CONTEXT_PATH);\n        assertThat(cookie.getValue()).isEqualTo(\"whatever\");\n\n        Cookie rootCookie = getCookieByNameAndPath(response, BONITA_TOKEN_NAME, \"/\");\n        assertThat(rootCookie.getMaxAge()).isEqualTo(0);\n        assertThat(rootCookie.getValue()).isEqualTo(\"\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/login/filter/TokenValidatorFilterTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.filter;\n\nimport static java.lang.String.format;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\nimport static org.mockito.MockitoAnnotations.initMocks;\n\nimport java.nio.charset.StandardCharsets;\nimport java.util.Collections;\nimport java.util.regex.Pattern;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.FilterConfig;\nimport javax.servlet.ServletContext;\nimport javax.servlet.ServletRequest;\nimport javax.servlet.ServletResponse;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpSession;\n\nimport org.assertj.core.api.Assertions;\nimport org.assertj.core.api.Condition;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockMultipartHttpServletRequest;\n\npublic class TokenValidatorFilterTest {\n\n    public static final String SESSION_CSRF_TOKEN = \"csrftoken\";\n\n    @Mock\n    private FilterChain chain;\n\n    @Mock\n    private HttpServletRequest httpRequest;\n\n    @Mock\n    private HttpServletResponse httpResponse;\n\n    @Mock\n    private HttpSession httpSession;\n\n    @Mock\n    private FilterConfig filterConfig;\n\n    @Mock\n    private ServletContext servletContext;\n\n    private TokenValidatorFilter filter = spy(new TokenValidatorFilter());\n\n    @Before\n    public void setUp() throws Exception {\n        initMocks(this);\n        doReturn(SESSION_CSRF_TOKEN).when(httpSession).getAttribute(\"api_token\");\n        doReturn(httpSession).when(httpRequest).getSession();\n        when(filter.isCsrfProtectionEnabled()).thenReturn(true);\n        when(servletContext.getContextPath()).thenReturn(\"\");\n        when(filterConfig.getServletContext()).thenReturn(servletContext);\n        when(filterConfig.getInitParameterNames()).thenReturn(Collections.emptyEnumeration());\n        filter.init(filterConfig);\n    }\n\n    @Test\n    public void should_check_csrf_token_from_request_header() throws Exception {\n        doReturn(SESSION_CSRF_TOKEN).when(httpRequest).getHeader(\"X-Bonita-API-Token\");\n\n        filter.proceedWithFiltering(httpRequest, httpResponse, chain);\n\n        verify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n    }\n\n    @Test\n    public void should_not_check_csrf_token_for_GET_request() throws Exception {\n        doReturn(\"GET\").when(httpRequest).getMethod();\n\n        filter.proceedWithFiltering(httpRequest, httpResponse, chain);\n\n        verify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n    }\n\n    @Test\n    public void should_not_check_csrf_token_for_HEAD_request() throws Exception {\n        doReturn(\"HEAD\").when(httpRequest).getMethod();\n\n        filter.proceedWithFiltering(httpRequest, httpResponse, chain);\n\n        verify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n    }\n\n    @Test\n    public void should_not_check_csrf_token_for_OPTIONS_request() throws Exception {\n        doReturn(\"OPTIONS\").when(httpRequest).getMethod();\n\n        filter.proceedWithFiltering(httpRequest, httpResponse, chain);\n\n        verify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n    }\n\n    @Test\n    public void should_not_check_csrf_token_for_bearer_request() throws Exception {\n        doReturn(\"true\").when(httpSession).getAttribute(TokenValidatorFilter.BEARER_HEADER_VERIFIED_ATTRIBUTE);\n        doReturn(\"POST\").when(httpRequest).getMethod();\n\n        filter.proceedWithFiltering(httpRequest, httpResponse, chain);\n\n        verify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n    }\n\n    @Test\n    public void should_set_401_status_when_csrf_request_header_is_wrong() throws Exception {\n        doReturn(\"notAValidToken\").when(httpRequest).getHeader(\"X-Bonita-API-Token\");\n\n        filter.proceedWithFiltering(httpRequest, httpResponse, chain);\n\n        verify(chain, never()).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n        verify(httpResponse).setStatus(HttpServletResponse.SC_UNAUTHORIZED);\n    }\n\n    @Test\n    public void should_set_401_status_when_csrf_request_header_is_not_set() throws Exception {\n\n        filter.proceedWithFiltering(httpRequest, httpResponse, chain);\n\n        verify(chain, never()).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n        verify(httpResponse).setStatus(HttpServletResponse.SC_UNAUTHORIZED);\n    }\n\n    @Test\n    public void should_check_csrf_token_from_request_parameter() throws Exception {\n        doReturn(SESSION_CSRF_TOKEN).when(httpRequest).getParameter(\"CSRFToken\");\n\n        filter.proceedWithFiltering(httpRequest, httpResponse, chain);\n\n        verify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n    }\n\n    @Test\n    public void should_set_401_status_when_csrf_request_parameter_is_wrong() throws Exception {\n        doReturn(\"notAValidToken\").when(httpRequest).getParameter(\"X-Bonita-API-Token\");\n\n        filter.proceedWithFiltering(httpRequest, httpResponse, chain);\n\n        verify(chain, never()).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n        verify(httpResponse).setStatus(HttpServletResponse.SC_UNAUTHORIZED);\n    }\n\n    @Test\n    public void should_check_csrf_token_from_request_form_data() throws Exception {\n        MockHttpServletRequest multipartRequest = mockMultipartRequestFor(SESSION_CSRF_TOKEN);\n\n        filter.proceedWithFiltering(multipartRequest, httpResponse, chain);\n\n        verify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n    }\n\n    @Test\n    public void should_set_401_status_when_csrf_form_data_is_wrong() throws Exception {\n        MockHttpServletRequest multipartRequest = mockMultipartRequestFor(\"notAValidToken\");\n\n        filter.proceedWithFiltering(multipartRequest, httpResponse, chain);\n\n        verify(chain, never()).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n        verify(httpResponse).setStatus(HttpServletResponse.SC_UNAUTHORIZED);\n    }\n\n    @Test\n    public void testFilterWithExcludedURL() throws Exception {\n        final String url = \"test\";\n        when(httpRequest.getRequestURL()).thenReturn(new StringBuffer(url));\n        doReturn(true).when(filter).matchExcludePatterns(url);\n        filter.doFilter(httpRequest, httpResponse, chain);\n        verify(filter, times(0)).proceedWithFiltering(httpRequest, httpResponse, chain);\n        verify(chain, times(1)).doFilter(httpRequest, httpResponse);\n    }\n\n    @Test\n    public void testMatchExcludePatterns() throws Exception {\n        matchExcludePattern(\"http://host/bonita/portal/resource/page/API/system/session/unusedId\", true);\n        matchExcludePattern(\"http://host/bonita/apps/app/API/system/session/unusedId\", true);\n        matchExcludePattern(\"http://host/bonita/API/system/session/unusedId\", true);\n        matchExcludePattern(\"http://host/bonita/portal/resource/page/API/system/i18ntranslation\", true);\n        matchExcludePattern(\"http://host/bonita/apps/app/API/system/i18ntranslation\", true);\n        matchExcludePattern(\"http://host/bonita/API/system/i18ntranslation\", true);\n        matchExcludePattern(\"http://host/bonita/API/bpm/process\", false);\n        matchExcludePattern(\"http://host/bonita/API/bpm/process/;session\", false);\n        matchExcludePattern(\"http://host/bonita/API/bpm/process/../../../API/system/session\", false);\n        matchExcludePattern(\"http://host/bonita/API/system/i18ntranslation/../../bpm/process\", false);\n        matchExcludePattern(\"http://host/bonita/API/bpm/activity/test/../i18ntranslation/..?p=0&c=10\", false);\n        matchExcludePattern(\"http://host/bonita/API/bpm/activity/i18ntranslation/../?p=0&c=10\", false);\n    }\n\n    @Test\n    public void testSemicolonTraversalUrlsMustNotBeExcluded() throws Exception {\n        // Semicolon traversal patterns must NOT bypass the CSRF token validation filter\n        matchExcludePattern(\"http://host/bonita/apps/FAKE/API/system/session/..;/..;/..;/serverAPI/something\", false);\n        matchExcludePattern(\"http://host/bonita/apps/FAKE/API/..;/..;/serverAPI/something\", false);\n        matchExcludePattern(\n                \"http://host/bonita/portal/resource/page/API/system/session/..;/..;/..;/serverAPI/something\", false);\n    }\n\n    @Test\n    public void testPercentEncodedSemicolonTraversalUrlsMustNotBeExcluded() throws Exception {\n        // Percent-encoded semicolons (%3b / %3B) must also NOT bypass the CSRF token validation filter\n        matchExcludePattern(\"http://host/bonita/apps/FAKE/API/system/session/..%3b/..%3b/..%3b/serverAPI/something\",\n                false);\n        matchExcludePattern(\"http://host/bonita/apps/FAKE/API/..%3b/..%3b/serverAPI/something\", false);\n        matchExcludePattern(\n                \"http://host/bonita/portal/resource/page/API/system/session/..%3B/..%3B/..%3B/serverAPI/something\",\n                false);\n    }\n\n    @Test\n    public void testCompileNullPattern() throws Exception {\n        assertThat(filter.compilePattern(null)).isNull();\n    }\n\n    @Test\n    public void testCompileWrongPattern() throws Exception {\n        assertThat(filter.compilePattern(\"((((\")).isNull();\n    }\n\n    @Test\n    public void testCompileSimplePattern() throws Exception {\n        final String patternToCompile = \"test\";\n        assertThat(filter.compilePattern(patternToCompile)).isNotNull().has(new Condition<>() {\n\n            @Override\n            public boolean matches(final Pattern pattern) {\n                return pattern.pattern().equalsIgnoreCase(patternToCompile);\n            }\n        });\n    }\n\n    @Test\n    public void testCompileExcludePattern() throws Exception {\n        final String patternToCompile = TokenValidatorFilter.TOKEN_VALIDATOR_FILTER_EXCLUDED_PAGES_PATTERN;\n        assertThat(filter.compilePattern(patternToCompile)).isNotNull().has(new Condition<>() {\n\n            @Override\n            public boolean matches(final Pattern pattern) {\n                return pattern.pattern().equalsIgnoreCase(patternToCompile);\n            }\n        });\n    }\n\n    private void matchExcludePattern(final String urlToMatch, final Boolean mustMatch) {\n        if (filter.matchExcludePatterns(urlToMatch) != mustMatch) {\n            Assertions.fail(\"Matching excludePattern and the Url \" + urlToMatch + \" must return \" + mustMatch);\n        }\n    }\n\n    private MockHttpServletRequest mockMultipartRequestFor(String csrfToken) {\n        MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest();\n        request.setMethod(\"POST\");\n        request.getSession().setAttribute(\"api_token\", SESSION_CSRF_TOKEN);\n\n        String boundary = \"----WebKitFormBoundaryRmUZEc9hkTjU1FKc\";\n        request.setContentType(format(\"multipart/form-data; boundary=%s\", boundary));\n        request.setContent(multipartContent(boundary, csrfToken));\n        return request;\n    }\n\n    private byte[] multipartContent(String boundary, String csrfToken) {\n        return new StringBuilder()\n                .append(\"--\").append(boundary).append(\"\\r\\n\")\n                .append(\"Content-Disposition: form-data; name=\\\"CSRFToken\\\"\").append(\"\\r\\n\")\n                .append(\"\\r\\n\")\n                .append(csrfToken).append(\"\\r\\n\")\n                .append(\"--\").append(boundary).append(\"--\")\n                .toString().getBytes(StandardCharsets.UTF_8);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/login/servlet/LoginServletTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.servlet;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.hamcrest.MatcherAssert.assertThat;\nimport static org.hamcrest.Matchers.is;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.ArgumentMatchers.startsWith;\nimport static org.mockito.Mockito.doNothing;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.doThrow;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.test.util.ReflectionTestUtils.getField;\nimport static org.springframework.test.util.ReflectionTestUtils.setField;\n\nimport javax.servlet.RequestDispatcher;\nimport javax.servlet.ServletContext;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.console.common.server.auth.AuthenticationFailedException;\nimport org.bonitasoft.console.common.server.auth.AuthenticationManager;\nimport org.bonitasoft.console.common.server.auth.AuthenticationManagerNotFoundException;\nimport org.bonitasoft.console.common.server.auth.impl.standard.StandardAuthenticationManagerImpl;\nimport org.bonitasoft.console.common.server.login.AccountLockedException;\nimport org.bonitasoft.console.common.server.login.LoginFailedException;\nimport org.bonitasoft.console.common.server.login.LoginManager;\nimport org.bonitasoft.console.common.server.utils.SessionUtil;\nimport org.bonitasoft.engine.exception.TenantStatusException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.server.login.LoginFailureTracker;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.SystemOutRule;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * Created by Vincent Elcrin\n * Date: 10/09/13\n * Time: 15:18\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class LoginServletTest {\n\n    @Rule\n    public final SystemOutRule systemOutRule = new SystemOutRule().enableLog().muteForSuccessfulTests();\n    @Mock\n    HttpServletRequest req;\n\n    @Mock\n    HttpServletResponse resp;\n\n    @Mock\n    HttpSession httpSession;\n\n    @Mock\n    APISession apiSession;\n\n    @Before\n    public void setup() {\n        doReturn(\"application/x-www-form-urlencoded\").when(req).getContentType();\n    }\n\n    @After\n    public void tearDown() {\n        System.clearProperty(LoginServlet.ENABLE_DEV_SUITE_LOGIN);\n    }\n\n    @Test\n    public void testPasswordIsDroppedWhenParameterIsLast() throws Exception {\n        final String cleanQueryString = LoginServlet.dropPassword(\"?username=walter.bates&password=bpm\");\n\n        assertThat(cleanQueryString, is(\"?username=walter.bates\"));\n    }\n\n    @Test\n    public void testPasswordIsDroppedWhenParameterIsBeforeHash() throws Exception {\n        final String cleanQueryString = LoginServlet.dropPassword(\"?username=walter.bates&password=bpm#hash\");\n\n        assertThat(cleanQueryString, is(\"?username=walter.bates#hash\"));\n    }\n\n    @Test\n    public void testUrlIsDroppedWhenParameterIsFirstAndBeforeHash() throws Exception {\n        final String cleanQueryString = LoginServlet.dropPassword(\"?username=walter.bates&password=bpm#hash\");\n\n        assertThat(cleanQueryString, is(\"?username=walter.bates#hash\"));\n    }\n\n    @Test\n    public void testUrlStayTheSameIfNoPasswordArePresent() throws Exception {\n        final String cleanQueryString = LoginServlet.dropPassword(\"?param=value#dhash\");\n\n        assertThat(cleanQueryString, is(\"?param=value#dhash\"));\n    }\n\n    @Test\n    public void testPasswordIsDroppedEvenIfQueryMarkUpIsntThere() throws Exception {\n        final String cleanQueryString = LoginServlet.dropPassword(\"password=bpm#dhash1&dhash2\");\n\n        assertThat(cleanQueryString, is(\"#dhash1&dhash2\"));\n    }\n\n    @Test\n    public void testUrlStayEmptyIfParameterIsEmpty() throws Exception {\n        final String cleanQueryString = LoginServlet.dropPassword(\"\");\n\n        assertThat(cleanQueryString, is(\"\"));\n    }\n\n    @Test\n    public void testDropPasswordOnRealUrl() throws Exception {\n        final String cleanUrl = LoginServlet\n                .dropPassword(\n                        \"?username=walter.bates&password=bpm&redirectUrl=http%3A%2F%2Flocalhost%3A8080%2Fbonita%2Fapps%2FappDirectoryBonita%3Flocale%3Den%23form%3DPool-\\n\"\n                                +\n                                \"-1.0%24entry%26process%3D8506394779365952706%26mode%3Dapp\");\n\n        assertThat(cleanUrl,\n                is(\"?username=walter.bates&redirectUrl=http%3A%2F%2Flocalhost%3A8080%2Fbonita%2Fapps%2FappDirectoryBonita%3Flocale%3Den%23form%3DPool-\\n\"\n                        +\n                        \"-1.0%24entry%26process%3D8506394779365952706%26mode%3Dapp\"));\n    }\n\n    @Test\n    public void testDoGetShouldDropPassowrdWhenLoggingQueryString() throws Exception {\n        //given\n        System.setProperty(LoginServlet.ENABLE_DEV_SUITE_LOGIN, \"true\");\n        final LoginServlet servlet = spy(new LoginServlet());\n        doReturn(\"password=123&username=john\").when(req).getQueryString();\n        doNothing().when(servlet).doPost(req, resp);\n\n        systemOutRule.clearLog();\n        //when\n        servlet.doGet(req, resp);\n\n        //then\n        assertThat(systemOutRule.getLog())\n                .containsPattern(\".*TRACE.*.username=john\")\n                .doesNotContain(\"password\")\n                .doesNotContain(\"123\");\n    }\n\n    @Test\n    public void testDoGetShouldfailWhenSysPropNotSet() throws Exception {\n        //given\n        final LoginServlet servlet = spy(new LoginServlet());\n\n        //when\n        servlet.doGet(req, resp);\n\n        //then\n        verify(servlet, never()).doPost(req, resp);\n        verify(resp).setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);\n    }\n\n    @Test\n    public void testDoPostShouldNotUseQueryString() throws Exception {\n\n        //given\n        final LoginServlet servlet = spy(new LoginServlet());\n        doReturn(httpSession).when(req).getSession();\n        doReturn(apiSession).when(httpSession).getAttribute(SessionUtil.API_SESSION_PARAM_KEY);\n        doReturn(\"query string\").when(req).getQueryString();\n        doReturn(null).when(req).getParameter(AuthenticationManager.REDIRECT_AFTER_LOGIN_PARAM_NAME);\n        doNothing().when(servlet).doLogin(req, resp);\n\n        //when\n        systemOutRule.clearLog();\n        servlet.doPost(req, resp);\n\n        //then\n        assertThat(systemOutRule.getLog()).doesNotContain(req.getQueryString());\n    }\n\n    @Test\n    public void should_send_error_401_when_login_with_wrong_credentials_and_no_redirect() throws Exception {\n        final LoginServlet servlet = spy(new LoginServlet());\n        doThrow(new LoginFailedException(\"\")).when(servlet).doLogin(req, resp);\n\n        servlet.doPost(req, resp);\n\n        verify(resp).setStatus(HttpServletResponse.SC_UNAUTHORIZED);\n    }\n\n    @Test\n    public void should_send_error_415_when_login_with_wrong_content_type() throws Exception {\n        final LoginServlet servlet = spy(new LoginServlet());\n        doReturn(\"application/json\").when(req).getContentType();\n\n        servlet.doPost(req, resp);\n\n        verify(resp).setStatus(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);\n    }\n\n    @Test\n    public void should_login_with_no_content_type() throws Exception {\n        final LoginServlet servlet = spy(new LoginServlet());\n        final LoginManager loginManager = mock(LoginManager.class);\n        doReturn(null).when(req).getContentType();\n        doReturn(httpSession).when(req).getSession();\n        doReturn(apiSession).when(httpSession).getAttribute(SessionUtil.API_SESSION_PARAM_KEY);\n        doReturn(loginManager).when(servlet).getLoginManager();\n        doNothing().when(loginManager).login(req, resp);\n\n        servlet.doPost(req, resp);\n\n        verify(loginManager).login(req, resp);\n        verify(resp).setStatus(HttpServletResponse.SC_NO_CONTENT);\n    }\n\n    @Test\n    public void should_login_with_content_type_containing_charset() throws Exception {\n        final LoginServlet servlet = spy(new LoginServlet());\n        final LoginManager loginManager = mock(LoginManager.class);\n        doReturn(\"application/x-www-form-urlencoded; charset=UTF-8\").when(req).getContentType();\n        doReturn(httpSession).when(req).getSession();\n        doReturn(apiSession).when(httpSession).getAttribute(SessionUtil.API_SESSION_PARAM_KEY);\n        doReturn(loginManager).when(servlet).getLoginManager();\n        doNothing().when(loginManager).login(req, resp);\n\n        servlet.doPost(req, resp);\n\n        verify(loginManager).login(req, resp);\n        verify(resp, never()).setStatus(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);\n    }\n\n    @Test\n    public void should_not_redirect_after_login_when_redirect_parameter_is_set_to_false_in_request() throws Exception {\n        final LoginServlet servlet = spy(new LoginServlet());\n        final LoginManager loginManager = mock(LoginManager.class);\n        doReturn(\"false\").when(req).getParameter(AuthenticationManager.REDIRECT_AFTER_LOGIN_PARAM_NAME);\n        doReturn(\"anyurl\").when(req).getParameter(AuthenticationManager.REDIRECT_URL);\n        doReturn(httpSession).when(req).getSession();\n        doReturn(apiSession).when(httpSession).getAttribute(SessionUtil.API_SESSION_PARAM_KEY);\n        doReturn(loginManager).when(servlet).getLoginManager();\n        doNothing().when(loginManager).login(req, resp);\n\n        servlet.doPost(req, resp);\n\n        verify(resp, never()).sendRedirect(anyString());\n    }\n\n    @Test\n    public void should_not_redirect_after_login_when_user_has_no_profile() throws Exception {\n        final LoginServlet servlet = spy(new LoginServlet());\n        final LoginManager loginManager = mock(LoginManager.class);\n        final ServletContext servletContext = mock(ServletContext.class);\n        RequestDispatcher requestDispatcher = mock(RequestDispatcher.class);\n        doReturn(new StandardAuthenticationManagerImpl()).when(servlet).getAuthenticationManager();\n        doReturn(\"/bonita\").when(req).getContextPath();\n        doReturn(\"true\").when(req).getParameter(AuthenticationManager.REDIRECT_AFTER_LOGIN_PARAM_NAME);\n        doReturn(\"anyurl\").when(req).getParameter(AuthenticationManager.REDIRECT_URL);\n        doReturn(httpSession).when(req).getSession();\n        doReturn(apiSession).when(httpSession).getAttribute(SessionUtil.API_SESSION_PARAM_KEY);\n        doReturn(loginManager).when(servlet).getLoginManager();\n        doReturn(servletContext).when(servlet).getServletContext();\n        doReturn(requestDispatcher).when(servletContext).getRequestDispatcher(anyString());\n        doNothing().when(loginManager).login(req, resp);\n        doReturn(false).when(servlet).hasProfile(apiSession);\n\n        servlet.doPost(req, resp);\n\n        verify(resp, never()).sendRedirect(anyString());\n        verify(servletContext).getRequestDispatcher(AuthenticationManager.LOGIN_PAGE);\n        verify(requestDispatcher).forward(req, resp);\n    }\n\n    @Test\n    public void should_redirect_after_login_when_redirect_url_is_set_in_request() throws Exception {\n        final LoginServlet servlet = spy(new LoginServlet());\n        final LoginManager loginManager = mock(LoginManager.class);\n        doReturn(\"anyurl\").when(req).getParameter(AuthenticationManager.REDIRECT_URL);\n        doReturn(httpSession).when(req).getSession();\n        doReturn(apiSession).when(httpSession).getAttribute(SessionUtil.API_SESSION_PARAM_KEY);\n        doReturn(loginManager).when(servlet).getLoginManager();\n        doNothing().when(loginManager).login(req, resp);\n        doReturn(true).when(servlet).hasProfile(apiSession);\n\n        servlet.doPost(req, resp);\n\n        verify(resp).sendRedirect(startsWith(\"anyurl\"));\n    }\n\n    @Test\n    public void should_redirect_after_login_when_redirect_parameter_is_set_to_true_in_request() throws Exception {\n        final LoginServlet servlet = spy(new LoginServlet());\n        final LoginManager loginManager = mock(LoginManager.class);\n        doReturn(\"true\").when(req).getParameter(AuthenticationManager.REDIRECT_AFTER_LOGIN_PARAM_NAME);\n        doReturn(httpSession).when(req).getSession();\n        doReturn(apiSession).when(httpSession).getAttribute(SessionUtil.API_SESSION_PARAM_KEY);\n        doReturn(loginManager).when(servlet).getLoginManager();\n        doNothing().when(loginManager).login(req, resp);\n        doReturn(true).when(servlet).hasProfile(apiSession);\n\n        servlet.doPost(req, resp);\n\n        verify(resp).sendRedirect(startsWith(AuthenticationManager.DEFAULT_DIRECT_URL));\n    }\n\n    @Test(expected = ServletException.class)\n    public void should_send_servlet_exception_when_login_with_wrong_credentials_and_redirect() throws Exception {\n        final LoginServlet servlet = spy(new LoginServlet());\n        doReturn(\"true\").when(req).getParameter(AuthenticationManager.REDIRECT_AFTER_LOGIN_PARAM_NAME);\n        doThrow(new LoginFailedException(\"\")).when(servlet).doLogin(req, resp);\n\n        servlet.doPost(req, resp);\n    }\n\n    @Test(expected = ServletException.class)\n    public void should_throw_tenant_status_exception() throws Exception {\n        final LoginServlet servlet = spy(new LoginServlet());\n        doReturn(\"true\").when(req).getParameter(AuthenticationManager.REDIRECT_AFTER_LOGIN_PARAM_NAME);\n        doThrow(new TenantStatusException(\"\")).when(servlet).doLogin(req, resp);\n\n        servlet.doPost(req, resp);\n    }\n\n    @Test(expected = ServletException.class)\n    public void should_return_servlet_exception_when_throw_authentication_failed_exception() throws Exception {\n        final LoginServlet servlet = spy(new LoginServlet());\n        doReturn(\"true\").when(req).getParameter(AuthenticationManager.REDIRECT_AFTER_LOGIN_PARAM_NAME);\n        doThrow(new AuthenticationFailedException(\"\")).when(servlet).doLogin(req, resp);\n\n        servlet.doPost(req, resp);\n    }\n\n    @Test(expected = ServletException.class)\n    public void should_return_servlet_exception_when_authentication_manager_not_found_exception() throws Exception {\n        final LoginServlet servlet = spy(new LoginServlet());\n        doReturn(\"true\").when(req).getParameter(AuthenticationManager.REDIRECT_AFTER_LOGIN_PARAM_NAME);\n        doThrow(new AuthenticationManagerNotFoundException(\"\")).when(servlet).doLogin(req, resp);\n\n        servlet.doPost(req, resp);\n    }\n\n    @Test\n    public void should_sanitize_semicolon_traversal_in_loginUrl_on_login_failure() throws Exception {\n        final LoginServlet servlet = spy(new LoginServlet());\n        final ServletContext servletContext = mock(ServletContext.class);\n        RequestDispatcher requestDispatcher = mock(RequestDispatcher.class);\n        doReturn(\"\").when(req).getContextPath();\n        doReturn(\"true\").when(req).getParameter(AuthenticationManager.REDIRECT_AFTER_LOGIN_PARAM_NAME);\n        // Malicious loginUrl using semicolon-based path traversal\n        doReturn(\"/login.jsp;/..;/serverAPI\").when(req).getParameter(AuthenticationManager.LOGIN_URL_PARAM_NAME);\n        doThrow(new LoginFailedException(\"\")).when(servlet).doLogin(req, resp);\n\n        servlet.doPost(req, resp);\n\n        // Verify the dispatched path has semicolons stripped and is normalized.\n        // After sanitization: /login.jsp;/..;/serverAPI -> /login.jsp/../serverAPI\n        // After normalization: /login.jsp/../serverAPI -> /serverAPI\n        // The HttpAPIServlet FORWARD does not happen if it does not start with context path + login.jsp\n        verify(servletContext, never()).getRequestDispatcher(anyString());\n        verify(resp).setStatus(HttpServletResponse.SC_UNAUTHORIZED, \"loginFailMessage\");\n    }\n\n    @Test\n    public void should_fallback_to_login_page_when_loginUrl_has_invalid_uri_chars() throws Exception {\n        final LoginServlet servlet = spy(new LoginServlet());\n        final ServletContext servletContext = mock(ServletContext.class);\n        RequestDispatcher requestDispatcher = mock(RequestDispatcher.class);\n        doReturn(\"true\").when(req).getParameter(AuthenticationManager.REDIRECT_AFTER_LOGIN_PARAM_NAME);\n        // loginUrl with characters invalid for URI (curly braces trigger URISyntaxException)\n        doReturn(\"/apps/{invalid}/page\").when(req).getParameter(AuthenticationManager.LOGIN_URL_PARAM_NAME);\n        doReturn(servletContext).when(servlet).getServletContext();\n        doReturn(requestDispatcher).when(servletContext).getRequestDispatcher(anyString());\n        doThrow(new LoginFailedException(\"\")).when(servlet).doLogin(req, resp);\n\n        servlet.doPost(req, resp);\n\n        // Should fall back to the default login page instead of crashing\n        ArgumentCaptor<String> pathCaptor = ArgumentCaptor.forClass(String.class);\n        verify(servletContext).getRequestDispatcher(pathCaptor.capture());\n        assertThat(pathCaptor.getValue()).startsWith(AuthenticationManager.LOGIN_PAGE);\n    }\n\n    @Test\n    public void should_set_account_locked_message_when_AccountLockedException_on_redirect() throws Exception {\n        final LoginServlet servlet = spy(new LoginServlet());\n        final ServletContext servletContext = mock(ServletContext.class);\n        RequestDispatcher requestDispatcher = mock(RequestDispatcher.class);\n        doReturn(\"true\").when(req).getParameter(AuthenticationManager.REDIRECT_AFTER_LOGIN_PARAM_NAME);\n        doReturn(servletContext).when(servlet).getServletContext();\n        doReturn(requestDispatcher).when(servletContext).getRequestDispatcher(anyString());\n        doThrow(new AccountLockedException(\"Too many failed login attempts. Please try again later.\")).when(servlet)\n                .doLogin(req, resp);\n\n        servlet.doPost(req, resp);\n\n        ArgumentCaptor<String> messageCaptor = ArgumentCaptor.forClass(String.class);\n        verify(req).setAttribute(eq(LoginServlet.LOGIN_FAIL_MESSAGE),\n                messageCaptor.capture());\n        assertThat(messageCaptor.getValue()).isEqualTo(\"accountLockedMessage\");\n    }\n\n    @Test\n    public void should_return_429_with_retry_after_header_when_AccountLockedException_and_no_redirect()\n            throws Exception {\n        final LoginServlet servlet = spy(new LoginServlet());\n        LoginFailureTracker tracker = mock(LoginFailureTracker.class);\n        doReturn(600L).when(tracker).getLockoutDurationSeconds();\n        setField(servlet, \"loginFailureTracker\", tracker);\n        doThrow(new AccountLockedException(\"Too many failed login attempts. Please try again later.\")).when(servlet)\n                .doLogin(req, resp);\n\n        servlet.doPost(req, resp);\n\n        verify(resp).setStatus(429);\n        verify(resp).setHeader(\"Retry-After\", \"600\");\n    }\n\n    @Test\n    public void should_init_resolve_tracker_from_spring_context_and_pass_to_login_manager() throws Exception {\n        // given\n        final LoginServlet servlet = spy(new LoginServlet());\n        final ServletContext servletContext = mock(ServletContext.class);\n        final LoginFailureTracker tracker = mock(LoginFailureTracker.class);\n\n        // Mock the ServletConfig to return our mock ServletContext\n        javax.servlet.ServletConfig servletConfig = mock(javax.servlet.ServletConfig.class);\n        doReturn(servletContext).when(servletConfig).getServletContext();\n\n        // Mock Spring context lookup\n        org.springframework.web.context.WebApplicationContext springContext = mock(\n                org.springframework.web.context.WebApplicationContext.class);\n        doReturn(springContext).when(servletContext).getAttribute(\n                org.springframework.web.context.WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);\n        doReturn(tracker).when(springContext).getBean(LoginFailureTracker.class);\n\n        // when\n        servlet.init(servletConfig);\n\n        // then — the tracker resolved from Spring context is stored in the servlet\n        assertThat(getField(servlet, \"loginFailureTracker\")).isSameAs(tracker);\n    }\n\n    @Test\n    public void should_set_login_fail_message_when_LoginFailedException_on_redirect() throws Exception {\n        final LoginServlet servlet = spy(new LoginServlet());\n        final ServletContext servletContext = mock(ServletContext.class);\n        RequestDispatcher requestDispatcher = mock(RequestDispatcher.class);\n        doReturn(\"true\").when(req).getParameter(AuthenticationManager.REDIRECT_AFTER_LOGIN_PARAM_NAME);\n        doReturn(servletContext).when(servlet).getServletContext();\n        doReturn(requestDispatcher).when(servletContext).getRequestDispatcher(anyString());\n        doThrow(new LoginFailedException(\"wrong credentials\")).when(servlet).doLogin(req, resp);\n\n        servlet.doPost(req, resp);\n\n        ArgumentCaptor<String> messageCaptor = ArgumentCaptor.forClass(String.class);\n        verify(req).setAttribute(eq(LoginServlet.LOGIN_FAIL_MESSAGE),\n                messageCaptor.capture());\n        assertThat(messageCaptor.getValue()).isEqualTo(\"loginFailMessage\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/login/servlet/LogoutServletTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.servlet;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.fail;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.doReturn;\n\nimport java.net.URISyntaxException;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport org.bonitasoft.console.common.server.auth.AuthenticationManager;\nimport org.bonitasoft.console.common.server.login.HttpServletRequestAccessor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class LogoutServletTest {\n\n    @Mock\n    HttpServletRequest request;\n\n    @Mock\n    HttpServletRequestAccessor requestAccessor;\n\n    @Mock\n    AuthenticationManager authenticationManager;\n\n    @Spy\n    LogoutServlet logoutServlet = new LogoutServlet();\n\n    @Before\n    public void setup() throws Exception {\n        doReturn(request).when(requestAccessor).asHttpServletRequest();\n        doReturn(authenticationManager).when(logoutServlet).getAuthenticationManager();\n    }\n\n    @Test\n    public void testSanitizeLoginPageUrlEmptyLoginPageUrl() {\n        String loginPage = logoutServlet.sanitizeLoginPageUrl(\"\");\n\n        assertThat(loginPage).isEqualToIgnoringCase(\"\");\n    }\n\n    @Test\n    public void testSanitizeLoginPageUrlFromMaliciousRedirectShouldReturnBrokenUrl() {\n        try {\n            logoutServlet.sanitizeLoginPageUrl(\"http://www.test.com\");\n            fail(\"building a login page with a different host on the redirectURL should fail\");\n        } catch (Exception e) {\n            if (!(e.getCause() instanceof URISyntaxException)) {\n                fail(\"Exception root cause should be a URISyntaxException\");\n            }\n        }\n    }\n\n    @Test\n    public void testSanitizeLoginPageUrlFromMaliciousRedirectShouldReturnBrokenUrl2() {\n        String loginPage = logoutServlet.sanitizeLoginPageUrl(\"test.com\");\n\n        assertThat(loginPage).isEqualToIgnoringCase(\"test.com\");\n    }\n\n    @Test\n    public void testSanitizeLoginPageUrlShouldReturnCorrectUrl() {\n        String loginPage = logoutServlet.sanitizeLoginPageUrl(\"apps/appDirectoryBonita?p=test#poutpout\");\n\n        assertThat(loginPage).isEqualToIgnoringCase(\"apps/appDirectoryBonita?p=test#poutpout\");\n    }\n\n    @Test\n    public void testGetURLToRedirectToFromAuthenticationManagerLogout() throws Exception {\n        doReturn(\"redirectURL\").when(logoutServlet).createRedirectUrl(requestAccessor);\n        doReturn(\"logoutURL\").when(authenticationManager).getLogoutPageURL(requestAccessor, \"redirectURL\");\n\n        String loginPage = logoutServlet.getURLToRedirectTo(requestAccessor);\n\n        assertThat(loginPage).isEqualTo(\"logoutURL\");\n    }\n\n    @Test\n    public void testGetURLToRedirectToFromAuthenticationManagerLogin() throws Exception {\n        doReturn(\"loginURL\").when(authenticationManager).getLoginPageURL(eq(requestAccessor), anyString());\n\n        String loginPage = logoutServlet.getURLToRedirectTo(requestAccessor);\n\n        assertThat(loginPage).isEqualTo(\"loginURL\");\n    }\n\n    @Test\n    public void testGetURLToRedirectToFromRequest() throws Exception {\n        doReturn(\"redirectURLFromRequest\").when(request).getParameter(AuthenticationManager.LOGIN_URL_PARAM_NAME);\n\n        String loginPage = logoutServlet.getURLToRedirectTo(requestAccessor);\n\n        assertThat(loginPage).isEqualTo(\"redirectURLFromRequest\");\n    }\n\n    @Test\n    public void testCreateRedirectUrlWithDefaultRedirect() throws Exception {\n        doReturn(null).when(requestAccessor).getRedirectUrl();\n\n        String redirectURL = logoutServlet.createRedirectUrl(requestAccessor);\n\n        assertThat(redirectURL).isEqualTo(AuthenticationManager.DEFAULT_DIRECT_URL);\n    }\n\n    @Test\n    public void testCreateRedirectUrl() throws Exception {\n        doReturn(\"redirectURL\").when(requestAccessor).getRedirectUrl();\n\n        String redirectURL = logoutServlet.createRedirectUrl(requestAccessor);\n\n        assertThat(redirectURL).isEqualTo(\"redirectURL\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/login/servlet/PlatformLoginServletTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.servlet;\n\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.doThrow;\nimport static org.mockito.Mockito.verify;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.bonitasoft.console.common.server.auth.AuthenticationManager;\nimport org.bonitasoft.engine.api.PlatformLoginAPI;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.platform.InvalidPlatformCredentialsException;\nimport org.bonitasoft.engine.platform.PlatformLoginException;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Baptiste Mesta\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class PlatformLoginServletTest {\n\n    public static final String JOHN = \"john\";\n    public static final String DOE = \"doe\";\n    @Mock\n    private HttpServletRequest httpServletRequest;\n    @Mock\n    private HttpServletResponse httpServletResponse;\n    @Mock\n    private PlatformLoginAPI platformLoginAPI;\n    @InjectMocks\n    @Spy\n    private PlatformLoginServlet platformLoginServlet;\n\n    @Before\n    public void before() throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        doReturn(platformLoginAPI).when(platformLoginServlet).getPlatformLoginAPI();\n        doReturn(JOHN).when(httpServletRequest).getParameter(PlatformLoginServlet.USERNAME_PARAM);\n        doReturn(DOE).when(httpServletRequest).getParameter(PlatformLoginServlet.PASSWORD_PARAM);\n    }\n\n    @Test\n    public void should_send_error_403_on_wrong_credential() throws Exception {\n        //given\n        doThrow(new InvalidPlatformCredentialsException(\"\")).when(platformLoginAPI).login(JOHN, DOE);\n        doReturn(\"true\").when(httpServletRequest).getParameter(AuthenticationManager.REDIRECT_AFTER_LOGIN_PARAM_NAME);\n        //when\n        platformLoginServlet.doPost(httpServletRequest, httpServletResponse);\n        //then\n        verify(httpServletResponse).sendError(eq(HttpServletResponse.SC_FORBIDDEN), anyString());\n    }\n\n    @Test\n    public void should_set_status_403_on_wrong_credential() throws Exception {\n        //given\n        doThrow(new InvalidPlatformCredentialsException(\"\")).when(platformLoginAPI).login(JOHN, DOE);\n        doReturn(\"false\").when(httpServletRequest).getParameter(AuthenticationManager.REDIRECT_AFTER_LOGIN_PARAM_NAME);\n        //when\n        platformLoginServlet.doPost(httpServletRequest, httpServletResponse);\n        //then\n        verify(httpServletResponse).setStatus(eq(HttpServletResponse.SC_FORBIDDEN));\n    }\n\n    @Test(expected = ServletException.class)\n    public void should_throw_exception_on_internal_error() throws Exception {\n        //given\n        doThrow(new PlatformLoginException(\"\")).when(platformLoginAPI).login(JOHN, DOE);\n        doReturn(\"false\").when(httpServletRequest).getParameter(AuthenticationManager.REDIRECT_AFTER_LOGIN_PARAM_NAME);\n        //when\n        platformLoginServlet.doPost(httpServletRequest, httpServletResponse);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/login/servlet/URLProtectorTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.servlet;\n\nimport static org.junit.Assert.*;\n\nimport org.junit.Test;\n\npublic class URLProtectorTest {\n\n    URLProtector urlProtecter = new URLProtector();\n\n    @Test\n    public void testProtectRedirectUrlShouldRemoveHTTPFromURL() {\n        assertEquals(\"google\", urlProtecter.protectRedirectUrl(\"httpgoogle\"));\n    }\n\n    @Test\n    public void testProtectRedirectUrlShouldRemoveHTTPSFromURL() {\n        assertEquals(\"google\", urlProtecter.protectRedirectUrl(\"httpsgoogle\"));\n    }\n\n    @Test\n    public void testProtectRedirectUrlShouldNotChangeURL() {\n        assertEquals(\"apps/#home\", urlProtecter.protectRedirectUrl(\"apps/#home\"));\n        assertEquals(\"/bonita/apps/#login\", urlProtecter.protectRedirectUrl(\"/bonita/apps/#login\"));\n        assertEquals(\"/apps/appDirectoryBonita\", urlProtecter.protectRedirectUrl(\"/apps/appDirectoryBonita\"));\n    }\n\n    @Test\n    public void it_should_filter_capital_letters() {\n        assertEquals(\":.google.com\", urlProtecter.protectRedirectUrl(\"HTTPS://WWW.google.com\"));\n    }\n\n    @Test\n    public void it_should_filter_double_backslash() {\n        assertEquals(\".google.com\", urlProtecter.protectRedirectUrl(\"//www.google.com\"));\n        assertEquals(\"google.com\", urlProtecter.protectRedirectUrl(\"//google.com\"));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/login/utils/RedirectUrlBuilderTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.utils;\n\nimport static org.junit.Assert.assertEquals;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.Test;\n\n/**\n * @author Vincent Elcrin\n */\npublic class RedirectUrlBuilderTest {\n\n    @Test\n    public void testWeCanBuildHashTaggedParamAreKept() throws Exception {\n        final RedirectUrlBuilder redirectUrlBuilder = new RedirectUrlBuilder(\n                \"myredirecturl?parambeforehash=true#hashparam=true\");\n        final String url = redirectUrlBuilder.build().getUrl();\n\n        assertEquals(\"myredirecturl?parambeforehash=true#hashparam=true\", url);\n    }\n\n    @Test\n    public void testPostParamsAreNotAddedToTheUrl() {\n        final Map<String, String[]> parameters = new HashMap<>();\n        parameters.put(\"postParam\", someValues(\"true\"));\n\n        final RedirectUrlBuilder redirectUrlBuilder = new RedirectUrlBuilder(\n                \"myredirecturl?someparam=value#hashparam=true\");\n        final String url = redirectUrlBuilder.build().getUrl();\n\n        assertEquals(\"myredirecturl?someparam=value#hashparam=true\", url);\n    }\n\n    private String[] someValues(final String... values) {\n        return values;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/login/utils/RedirectUrlHandlerTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.login.utils;\n\nimport static org.hamcrest.MatcherAssert.assertThat;\nimport static org.mockito.Mockito.doReturn;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport org.bonitasoft.console.common.server.auth.AuthenticationManager;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class RedirectUrlHandlerTest {\n\n    @Mock\n    HttpServletRequest req;\n\n    @Test\n    public void should_not_redirect_after_login_when_redirect_parameter_is_set_to_false() throws Exception {\n        doReturn(\"false\").when(req).getParameter(AuthenticationManager.REDIRECT_AFTER_LOGIN_PARAM_NAME);\n        doReturn(\"anyurl\").when(req).getParameter(AuthenticationManager.REDIRECT_URL);\n\n        assertThat(\"should not redirect\", !RedirectUrlHandler.shouldRedirectAfterLogin(req));\n    }\n\n    @Test\n    public void should_redirect_after_login_when_redirect_url_is_set_in_request() throws Exception {\n        doReturn(\"anyurl\").when(req).getParameter(AuthenticationManager.REDIRECT_URL);\n\n        assertThat(\"should redirect\", RedirectUrlHandler.shouldRedirectAfterLogin(req));\n    }\n\n    @Test\n    public void should_redirect_after_login_when_redirect_parameter_is_set_to_true() throws Exception {\n        doReturn(\"true\").when(req).getParameter(AuthenticationManager.REDIRECT_AFTER_LOGIN_PARAM_NAME);\n\n        assertThat(\"should redirect\", RedirectUrlHandler.shouldRedirectAfterLogin(req));\n    }\n\n    @Test\n    public void should_not_redirect_after_logout_when_redirect_parameter_is_set_to_false() throws Exception {\n        doReturn(\"false\").when(req).getParameter(AuthenticationManager.REDIRECT_AFTER_LOGIN_PARAM_NAME);\n        doReturn(\"anyurl\").when(req).getParameter(AuthenticationManager.REDIRECT_URL);\n        doReturn(\"anyurl\").when(req).getParameter(AuthenticationManager.LOGIN_URL_PARAM_NAME);\n\n        assertThat(\"should not redirect\", !RedirectUrlHandler.shouldRedirectAfterLogout(req));\n    }\n\n    @Test\n    public void should_redirect_after_logout_when_redirect_url_is_set_in_request() throws Exception {\n        doReturn(\"anyurl\").when(req).getParameter(AuthenticationManager.REDIRECT_URL);\n\n        assertThat(\"should redirect\", RedirectUrlHandler.shouldRedirectAfterLogout(req));\n    }\n\n    @Test\n    public void should_redirect_after_logout_when_login_url_is_set_in_request() throws Exception {\n        doReturn(\"anyurl\").when(req).getParameter(AuthenticationManager.LOGIN_URL_PARAM_NAME);\n\n        assertThat(\"should redirect\", RedirectUrlHandler.shouldRedirectAfterLogout(req));\n    }\n\n    @Test\n    public void should_redirect_after_logout_when_redirect_parameter_is_set_to_true() throws Exception {\n        doReturn(\"true\").when(req).getParameter(AuthenticationManager.REDIRECT_AFTER_LOGIN_PARAM_NAME);\n\n        assertThat(\"should redirect\", RedirectUrlHandler.shouldRedirectAfterLogout(req));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/page/ApplicationAuthorizationsHelperTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.page;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.*;\n\nimport org.bonitasoft.engine.api.ApplicationAPI;\nimport org.bonitasoft.engine.business.application.Application;\nimport org.bonitasoft.livingapps.ApplicationModel;\nimport org.bonitasoft.livingapps.ApplicationModelFactory;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ApplicationAuthorizationsHelperTest {\n\n    @Mock\n    ApplicationAPI applicationAPI;\n\n    @Mock\n    ApplicationModelFactory applicationFactory;\n\n    @InjectMocks\n    ApplicationAuthorizationsHelper applicationAuthorizationsHelper;\n\n    @Mock\n    ApplicationModel applicationModel;\n\n    @Mock\n    Application application;\n\n    @Test\n    public void should_authorize_page_when_appToken_not_null_and_page_authorized_in_application() throws Exception {\n        given(applicationFactory.createApplicationModel(any())).willReturn(applicationModel);\n        when(applicationModel.authorize(any())).thenReturn(true);\n        final boolean isPageAuthorized = applicationAuthorizationsHelper.isAuthorized(\"appToken\");\n\n        assertThat(isPageAuthorized).isTrue();\n        verify(applicationModel).authorize(any());\n    }\n\n    @Test\n    public void should_unAuthorize_page_when_appToken_not_null_and_page_not_authorized_in_application()\n            throws Exception {\n        given(applicationFactory.createApplicationModel(any(String.class))).willReturn(applicationModel);\n        when(applicationModel.authorize(any())).thenReturn(false);\n        final boolean isPageAuthorized = applicationAuthorizationsHelper.isAuthorized(\"appToken\");\n\n        assertThat(isPageAuthorized).isFalse();\n        verify(applicationModel).authorize(any());\n        verify(applicationAPI, never()).searchApplicationPages(any());\n    }\n\n    @Test\n    public void should_not_authorize_page_when_appToken_not_null_and_page_unauthorized_in_application() {\n        final boolean isPageAuthorized = applicationAuthorizationsHelper.isAuthorized(\"appToken\");\n\n        assertThat(isPageAuthorized).isFalse();\n    }\n\n    @Test\n    public void should_not_authorize_page_when_appToken_is_null() {\n        final boolean isPageAuthorized = applicationAuthorizationsHelper.isAuthorized(\"\");\n\n        assertThat(isPageAuthorized).isFalse();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/page/CustomPageChildFirstClassLoaderTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.page;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.*;\n\nimport java.io.File;\nimport java.io.InputStream;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.TemporaryFolder;\nimport org.junit.runner.RunWith;\nimport org.mockito.InOrder;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class CustomPageChildFirstClassLoaderTest {\n\n    @Mock\n    private CustomPageDependenciesResolver customPageDependenciesResolver;\n    @Mock\n    private BDMClientDependenciesResolver bdmDependenciesResolver;\n\n    private CustomPageChildFirstClassLoader classLoader;\n\n    @Rule\n    public TemporaryFolder tmpRule = new TemporaryFolder();\n\n    @Before\n    public void setUp() throws Exception {\n        when(customPageDependenciesResolver.getTempFolder()).thenReturn(tmpRule.newFolder());\n    }\n\n    @Test\n    public void should_add_custom_page_jar_resources_in_classloader_urls() throws Exception {\n        classLoader = newClassloader();\n        when(customPageDependenciesResolver.resolveCustomPageDependencies()).thenReturn(loadedResources(\"util.jar\"));\n\n        classLoader.addCustomPageResources();\n\n        assertThat(classLoader.getURLs()).hasSize(1);\n    }\n\n    @Test\n    public void should_not_add_duplicated_bdm_dependencies_in_lib_folder_in_classloader() throws Exception {\n        classLoader = newClassloader();\n        when(customPageDependenciesResolver.resolveCustomPageDependencies())\n                .thenReturn(loadedResources(\"util.jar\", \"bdm-model.jar\", \"bdm-dao.jar\", \"javassist-3.18.1-GA.jar\"));\n        when(bdmDependenciesResolver.isABDMDependency(\"bdm-model.jar\")).thenReturn(true);\n        when(bdmDependenciesResolver.isABDMDependency(\"bdm-dao.jar\")).thenReturn(true);\n        when(bdmDependenciesResolver.isABDMDependency(\"javassist-3.18.1-GA.jar\")).thenReturn(true);\n\n        classLoader.addCustomPageResources();\n\n        assertThat(classLoader.getURLs()).hasSize(1);\n    }\n\n    @Test\n    public void should_add_bdm_dependencies_in_classloader_before_other_dependencies() throws Exception {\n        classLoader = spy(newClassloader());\n        final URL[] bdmDependenciesURLs = bdmDependenciesURLs();\n        when(bdmDependenciesResolver.getBDMDependencies()).thenReturn(bdmDependenciesURLs);\n        when(customPageDependenciesResolver.resolveCustomPageDependencies())\n                .thenReturn(loadedResources(\"util.jar\"));\n        classLoader.addCustomPageResources();\n\n        final InOrder order = inOrder(classLoader, customPageDependenciesResolver);\n        order.verify(classLoader).addURLs(bdmDependenciesURLs);\n        order.verify(customPageDependenciesResolver).resolveCustomPageDependencies();\n        assertThat(classLoader.getURLs()).hasSize(4);\n    }\n\n    private URL[] bdmDependenciesURLs() throws MalformedURLException {\n        final URL rootFolderURL = CustomPageChildFirstClassLoader.class.getResource(\"/bdmDependencies\");\n        final File rootFolder = new File(rootFolderURL.getFile());\n        final List<URL> urls = new ArrayList<>();\n        for (final File dep : rootFolder.listFiles()) {\n            urls.add(dep.toURI().toURL());\n        }\n        return urls.toArray(new URL[urls.size()]);\n    }\n\n    @Test\n    public void should_add_custom_page_non_jar_resources_in_classloader() throws Exception {\n        classLoader = newClassloader();\n        when(customPageDependenciesResolver.resolveCustomPageDependencies())\n                .thenReturn(loadedResources(\"util.properties\"));\n\n        classLoader.addCustomPageResources();\n\n        assertThat(classLoader.getURLs()).isEmpty();\n        assertThat(classLoader.getResourceAsStream(\"util.properties\")).isNotNull();\n    }\n\n    @Test\n    public void should_get_resources_contained_in_jars_of_classloader() throws Exception {\n        classLoader = spy(newClassloader());\n        when(customPageDependenciesResolver.resolveCustomPageDependencies()).thenReturn(loadedResources(\"util.jar\"));\n        classLoader.addCustomPageResources();\n\n        InputStream resourceNotExistingButShouldBeSoughtInJars = classLoader.getResourceAsStream(\"util.properties\");\n\n        assertThat(resourceNotExistingButShouldBeSoughtInJars).isNull();\n        verify(classLoader).getResourceAsStreamRegular(\"util.properties\");\n\n    }\n\n    private Map<String, byte[]> loadedResources(String... resourceNames) {\n        final Map<String, byte[]> resources = new HashMap<>();\n        for (final String resource : resourceNames) {\n            resources.put(resource, new byte[0]);\n        }\n        return resources;\n    }\n\n    private CustomPageChildFirstClassLoader newClassloader() {\n        return new CustomPageChildFirstClassLoader(\"myPage\", customPageDependenciesResolver, bdmDependenciesResolver,\n                Thread.currentThread().getContextClassLoader());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/page/CustomPageDependenciesResolverTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.page;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.when;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Map;\n\nimport org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.rules.TemporaryFolder;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class CustomPageDependenciesResolverTest {\n\n    @Mock\n    private WebBonitaConstantsUtils webBonitaConstantsUtils;\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n    @Rule\n    public final TemporaryFolder tmpFolder = new TemporaryFolder();\n\n    @Test\n    public void should_throw_an_IllegalStateException_when_accessing_tmp_folder_before_resolving_libraries()\n            throws Exception {\n        final CustomPageDependenciesResolver resolver = newCustomPageDependenciesResolver(null);\n\n        expectedException.expect(IllegalStateException.class);\n\n        resolver.getTempFolder();\n    }\n\n    @Test\n    public void should_resolve_dependencies_content() throws Exception {\n        final CustomPageDependenciesResolver resolver = newCustomPageDependenciesResolver(testPageFolder());\n\n        final Map<String, byte[]> dependenciesContent = resolver.resolveCustomPageDependencies();\n\n        assertThat(dependenciesContent).containsKeys(\"resource.properties\",\n                \"bdm-client.jar\",\n                \"bdm-dao.jar\",\n                \"javassist-3.18.1-GA.jar\",\n                \"util.jar\");\n        assertThat(CustomPageDependenciesResolver.PAGES_LIB_TMPDIR).containsKey(\"myCustomPage\");\n        final File cachedTmpFoler = CustomPageDependenciesResolver.PAGES_LIB_TMPDIR.get(\"myCustomPage\");\n        assertThat(cachedTmpFoler.exists()).isTrue();\n        assertThat(resolver.getTempFolder()).isEqualTo(cachedTmpFoler);\n    }\n\n    @Test\n    public void should_delete_temporary_lib_foler() throws Exception {\n        final CustomPageDependenciesResolver resolver = newCustomPageDependenciesResolver(testPageFolder());\n        resolver.resolveCustomPageDependencies();\n\n        assertThat(CustomPageDependenciesResolver.PAGES_LIB_TMPDIR).containsKey(\"myCustomPage\");\n        assertThat(CustomPageDependenciesResolver.PAGES_LIB_TMPDIR.get(\"myCustomPage\").exists()).isTrue();\n\n        final File tempFolder = CustomPageDependenciesResolver.removePageLibTempFolder(\"myCustomPage\");\n\n        assertThat(CustomPageDependenciesResolver.PAGES_LIB_TMPDIR).doesNotContainKey(\"myCustomPage\");\n        assertThat(tempFolder.exists()).isFalse();\n    }\n\n    @Test\n    public void should_resolve_dependencies_return_an_empty_map_if_no_lib_folder_is_found_in_custom_page()\n            throws Exception {\n        final CustomPageDependenciesResolver resolver = newCustomPageDependenciesResolver(null);\n\n        final Map<String, byte[]> dependenciesContent = resolver.resolveCustomPageDependencies();\n\n        assertThat(dependenciesContent).isEmpty();\n    }\n\n    private File testPageFolder() {\n        return new File(CustomPageDependenciesResolverTest.class.getResource(\"/ARootPageFolder\").getFile());\n    }\n\n    private CustomPageDependenciesResolver newCustomPageDependenciesResolver(File folder) throws IOException {\n        when(webBonitaConstantsUtils.getTempFolder()).thenReturn(tmpFolder.newFolder());\n        return new CustomPageDependenciesResolver(\"myCustomPage\", folder, webBonitaConstantsUtils);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/page/CustomPageRequestModifierTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.page;\n\nimport static org.mockito.ArgumentMatchers.anyInt;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport javax.servlet.RequestDispatcher;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class CustomPageRequestModifierTest {\n\n    @Mock\n    private HttpServletRequest request;\n\n    @Mock\n    private HttpServletResponse response;\n\n    @Mock\n    private RequestDispatcher requestDispatcher;\n\n    @Test\n    public void redirect_with_trailing_slash_should_not_encode_parameter() throws Exception {\n        when(request.getContextPath()).thenReturn(\"bonita/\");\n        when(request.getServletPath()).thenReturn(\"apps/\");\n        when(request.getPathInfo()).thenReturn(\"myapp/mypage\");\n        when(request.getQueryString()).thenReturn(\"time=12:00\");\n        when(response.encodeRedirectURL(\"bonita/apps/myapp/mypage/?time=12:00\"))\n                .thenReturn(\"bonita/apps/myapp/mypage/?time=12:00\");\n\n        CustomPageRequestModifier customPageRequestModifier = new CustomPageRequestModifier();\n        customPageRequestModifier.redirectToValidPageUrl(request, response);\n\n        verify(response).sendRedirect(\"bonita/apps/myapp/mypage/?time=12:00\");\n    }\n\n    @Test\n    public void redirect_with_trailing_slash_should_not_add_question_mark() throws Exception {\n        when(request.getContextPath()).thenReturn(\"bonita/\");\n        when(request.getServletPath()).thenReturn(\"apps/\");\n        when(request.getPathInfo()).thenReturn(\"myapp/mypage\");\n        when(response.encodeRedirectURL(\"bonita/apps/myapp/mypage/\")).thenReturn(\"bonita/apps/myapp/mypage/\");\n\n        CustomPageRequestModifier customPageRequestModifier = new CustomPageRequestModifier();\n        customPageRequestModifier.redirectToValidPageUrl(request, response);\n\n        verify(response).sendRedirect(\"bonita/apps/myapp/mypage/\");\n    }\n\n    @Test\n    public void check_should_not_authorize_requests_to_other_paths() throws Exception {\n        String apiPath = \"/API/living/../../WEB-INF/web.xml\";\n\n        CustomPageRequestModifier customPageRequestModifier = new CustomPageRequestModifier();\n        customPageRequestModifier.forwardIfRequestIsAuthorized(request, response, \"/API\", apiPath);\n\n        verify(response).setStatus(HttpServletResponse.SC_FORBIDDEN);\n        verify(request, never()).getRequestDispatcher(anyString());\n    }\n\n    @Test\n    public void check_should_not_authorize_semicolon_traversal_to_serverAPI() throws Exception {\n        String apiPath = \"/API/system/session/..;/..;/..;/serverAPI/something\";\n\n        CustomPageRequestModifier customPageRequestModifier = new CustomPageRequestModifier();\n        customPageRequestModifier.forwardIfRequestIsAuthorized(request, response, \"/API\", apiPath);\n\n        verify(response).setStatus(HttpServletResponse.SC_FORBIDDEN);\n        verify(request, never()).getRequestDispatcher(anyString());\n    }\n\n    @Test\n    public void check_should_not_authorize_semicolon_traversal_to_WEB_INF() throws Exception {\n        String apiPath = \"/API/a/..;anything/..;/..;/WEB-INF/web.xml\";\n\n        CustomPageRequestModifier customPageRequestModifier = new CustomPageRequestModifier();\n        customPageRequestModifier.forwardIfRequestIsAuthorized(request, response, \"/API\", apiPath);\n\n        verify(response).setStatus(HttpServletResponse.SC_FORBIDDEN);\n        verify(request, never()).getRequestDispatcher(anyString());\n    }\n\n    @Test\n    public void check_should_authorize_valid_requests() throws Exception {\n        String apiPath = \"/API/living/0\";\n        when(request.getRequestDispatcher(apiPath)).thenReturn(requestDispatcher);\n\n        CustomPageRequestModifier customPageRequestModifier = new CustomPageRequestModifier();\n        customPageRequestModifier.forwardIfRequestIsAuthorized(request, response, \"/API\", apiPath);\n\n        verify(request).getRequestDispatcher(apiPath);\n        verify(response, never()).sendError(anyInt(), anyString());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/page/CustomPageServiceTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.page;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.fail;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.*;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.HashSet;\nimport java.util.Properties;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentLinkedQueue;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Future;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport groovy.lang.GroovyClassLoader;\nimport org.apache.commons.io.IOUtils;\nimport org.bonitasoft.console.common.server.page.extension.PageResourceProviderImpl;\nimport org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils;\nimport org.bonitasoft.console.common.server.preferences.properties.PropertiesWithSet;\nimport org.bonitasoft.engine.api.PageAPI;\nimport org.bonitasoft.engine.api.PermissionAPI;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.engine.page.PageNotFoundException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.extension.page.PageController;\nimport org.bonitasoft.web.extension.rest.RestAPIContext;\nimport org.bonitasoft.web.extension.rest.RestApiController;\nimport org.bonitasoft.web.extension.rest.RestApiResponse;\nimport org.bonitasoft.web.extension.rest.RestApiResponseBuilder;\nimport org.bonitasoft.web.rest.server.api.extension.ControllerClassName;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.TemporaryFolder;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class CustomPageServiceTest {\n\n    public static final String PAGE_PROPERTIES = \"page.properties\";\n\n    private CustomPageService customPageService;\n\n    @Mock\n    APISession apiSession;\n\n    @Mock\n    PageResourceProviderImpl pageResourceProvider;\n\n    @Mock\n    PageAPI pageAPI;\n\n    @Mock\n    PermissionAPI permissionAPI;\n\n    @Mock\n    private HttpServletRequest request;\n\n    @Mock\n    private RestAPIContext restAPIContext;\n\n    @Mock\n    private final WebBonitaConstantsUtils webBonitaConstantUtils = WebBonitaConstantsUtils.getTenantInstance();\n\n    @Rule\n    public TemporaryFolder temporaryFolder = new TemporaryFolder();\n\n    private Date databaseLastUpdateDate;\n\n    private String fullPageName = \"page1\";\n\n    @Before\n    public void before() throws IOException, BonitaException {\n        customPageService = spy(new CustomPageService());\n        CustomPageService.clearCachedClassloaders();\n        doReturn(false).when(customPageService).isPageInDebugMode();\n        doReturn(webBonitaConstantUtils).when(customPageService).getWebBonitaConstantsUtils();\n        doReturn(pageAPI).when(customPageService).getPageAPI(apiSession);\n        doReturn(permissionAPI).when(customPageService).getPermissionAPI(apiSession);\n        when(pageResourceProvider.getFullPageName()).thenReturn(fullPageName);\n        databaseLastUpdateDate = new Date();\n        customPageService.clearPageTimestampsMemoryCache();\n    }\n\n    @Test\n    public void should_load_page_return_page_impl() throws Exception {\n        // Given\n        final File pageFile = new File(getClass().getResource(\"/Index.groovy\").toURI());\n        final File pageDir = pageFile.getParentFile();\n        assertThat(pageFile).as(\"no file \" + pageFile.getAbsolutePath()).exists().canRead();\n        when(pageResourceProvider.getPageDirectory()).thenReturn(pageDir);\n        doReturn(pageFile).when(customPageService).getGroovyPageFile(any(File.class));\n        doReturn(Thread.currentThread().getContextClassLoader()).when(customPageService).getParentClassloader(\n                anyString(),\n                any(CustomPageDependenciesResolver.class), any(BDMClientDependenciesResolver.class));\n\n        // When\n        final GroovyClassLoader classloader = customPageService.getPageClassloader(apiSession, pageResourceProvider);\n        final Class<?> pageClass = customPageService.registerPage(classloader, pageResourceProvider);\n        final PageController pageController = customPageService.loadPage((Class<PageController>) pageClass);\n\n        // Then\n        assertNotNull(pageController);\n    }\n\n    @Test\n    public void should_load_rest_api_page_return_api_impl() throws Exception {\n        // Given\n        final File pageFile = new File(getClass().getResource(\"/IndexRestApi.groovy\").toURI());\n        final File pageDir = pageFile.getParentFile();\n        assertThat(pageFile).as(\"no file \" + pageFile.getAbsolutePath()).exists().canRead();\n        when(pageResourceProvider.getPageDirectory()).thenReturn(pageDir);\n        doReturn(Thread.currentThread().getContextClassLoader()).when(customPageService).getParentClassloader(\n                anyString(),\n                any(CustomPageDependenciesResolver.class),\n                any(BDMClientDependenciesResolver.class));\n\n        ControllerClassName controllerClassName = new ControllerClassName(\"IndexRestApi.groovy\", true);\n\n        // When\n        final GroovyClassLoader classloader = customPageService.getPageClassloader(apiSession, pageResourceProvider);\n        final Class<?> restApiControllerClass = customPageService.registerRestApiPage(classloader, pageResourceProvider,\n                controllerClassName, controllerClassName.getName());\n        final RestApiController restApiController = customPageService\n                .loadRestApiPage((Class<RestApiController>) restApiControllerClass);\n\n        // Then\n        assertNotNull(restApiController);\n    }\n\n    @Test\n    public void should_retrievePageZipContent_save_it_in_bonita_home() throws Exception {\n        // Given\n        final Page mockedPage = mock(Page.class);\n        when(mockedPage.getId()).thenReturn(1L);\n        doReturn(pageAPI).when(customPageService).getPageAPI(apiSession);\n        final byte[] zipFile = IOUtils.toByteArray(getClass().getResourceAsStream(\"page.zip\"));\n        when(pageAPI.getPageContent(1L)).thenReturn(zipFile);\n        when(pageResourceProvider.getPage(pageAPI)).thenReturn(mockedPage);\n        when(pageResourceProvider.getTempPageFile()).thenReturn(temporaryFolder.newFile());\n        File pageDirectory = temporaryFolder.newFolder();\n        when(pageResourceProvider.getPageDirectory()).thenReturn(pageDirectory);\n\n        // When\n        customPageService.retrievePageZipContent(apiSession, pageResourceProvider);\n\n        // Validate\n        assertThat(pageDirectory.listFiles()).isNotEmpty();\n    }\n\n    @Test\n    public void should_get_Custom_Page_permissions_from_PageProperties() throws Exception {\n        // Given\n        final String fileContent = \"name=customPage1\\n\" +\n                \"resources=[GET|identity/user, PUT|identity/user]\";\n\n        final File pagePropertiesFile = File.createTempFile(PAGE_PROPERTIES, \".tmp\");\n        IOUtils.write(fileContent.getBytes(), new FileOutputStream(pagePropertiesFile));\n        doReturn(Set.of(\"Application Visualization\")).when(permissionAPI).getResourcePermissions(\"GET|identity/user\");\n        doReturn(Set.of(\"Organization Visualization\", \"Organization Management\")).when(permissionAPI)\n                .getResourcePermissions(\"PUT|identity/user\");\n\n        // When\n        final Set<String> customPagePermissions = customPageService.getCustomPagePermissions(\n                new PropertiesWithSet(pagePropertiesFile),\n                apiSession);\n\n        // Then\n        assertThat(customPagePermissions).contains(\"Organization Visualization\", \"Organization Management\",\n                \"Application Visualization\");\n    }\n\n    @Test\n    public void should_return_unknown_resources_declared_in_PageProperties() throws Exception {\n        // Given\n        final String fileContent = \"name=customPage1\\n\" +\n                \"resources=[GET|unknown/resource, GET|identity/user, PUT|identity/user]\";\n\n        final File pagePropertiesFile = File.createTempFile(PAGE_PROPERTIES, \".tmp\");\n        IOUtils.write(fileContent.getBytes(), new FileOutputStream(pagePropertiesFile));\n        doReturn(Set.of(\"Application Visualization\")).when(permissionAPI).getResourcePermissions(\"GET|identity/user\");\n        doReturn(Set.of(\"Organization Visualization\", \"Organization Management\")).when(permissionAPI)\n                .getResourcePermissions(\"PUT|identity/user\");\n\n        // When\n        final Set<String> customPagePermissions = customPageService.getCustomPagePermissions(\n                new PropertiesWithSet(pagePropertiesFile),\n                apiSession);\n\n        // Then\n        assertThat(customPagePermissions).contains(\"<GET|unknown/resource>\", \"Organization Visualization\",\n                \"Application Visualization\",\n                \"Organization Management\");\n    }\n\n    @Test\n    public void should_get_Custom_Page_permissions_to_CompoundPermissions_with_empty_list() throws Exception {\n        // Given\n        final String fileContent = \"name=customPage1\\nresources=[]\";\n\n        final File pagePropertiesFile = File.createTempFile(PAGE_PROPERTIES, \".tmp\");\n        IOUtils.write(fileContent.getBytes(), new FileOutputStream(pagePropertiesFile));\n\n        // When\n        final Set<String> customPagePermissions = customPageService.getCustomPagePermissions(\n                new PropertiesWithSet(pagePropertiesFile),\n                apiSession);\n\n        // Then\n        assertEquals(customPagePermissions, new HashSet<String>());\n    }\n\n    @Test\n    public void should_GetPageProperties_does_not_throws_already_exist_exception_if_checkIfItAlreadyExists_is_false()\n            throws Exception {\n        //given\n        final boolean checkIfItAlreadyExists = false;\n        doReturn(new Properties()).when(pageAPI).getPageProperties(any(byte[].class), eq(checkIfItAlreadyExists));\n\n        //when\n        final byte[] zipContent = new byte[0];\n        customPageService.getPageProperties(apiSession, zipContent, checkIfItAlreadyExists, 123123L);\n\n        //then\n        verify(pageAPI, times(0)).getPageByNameAndProcessDefinitionId(anyString(), anyLong());\n    }\n\n    @Test\n    public void should_GetPageProperties_throws_already_exist_exception_Page_when_same_page_at_tenant_level()\n            throws Exception {\n        //given\n        final boolean checkIfItAlreadyExists = true;\n        final Properties pageProperties = new Properties();\n        final String pageName = \"test\";\n        pageProperties.put(CustomPageService.NAME_PROPERTY, pageName);\n        doReturn(pageProperties).when(pageAPI).getPageProperties(any(byte[].class), eq(false));\n        final long processDefinitionId = 123123L;\n        doThrow(new PageNotFoundException(\"page not found\")).when(pageAPI).getPageByNameAndProcessDefinitionId(pageName,\n                processDefinitionId);\n\n        //when\n        final byte[] zipContent = new byte[0];\n        try {\n            customPageService.getPageProperties(apiSession, zipContent, checkIfItAlreadyExists, processDefinitionId);\n            fail(\"AlreadyExistsException has not been thrown\");\n        } catch (final AlreadyExistsException ignored) {\n        }\n\n        //then\n        verify(pageAPI, times(1)).getPageByName(pageName);\n    }\n\n    @Test\n    public void should_GetPageProperties_throws_already_exist_exception_Page_when_same_page_at_process_level()\n            throws Exception {\n        //given\n        final boolean checkIfItAlreadyExists = true;\n        final Properties pageProperties = new Properties();\n        final String pageName = \"test\";\n        pageProperties.put(CustomPageService.NAME_PROPERTY, pageName);\n        doReturn(pageProperties).when(pageAPI).getPageProperties(any(byte[].class), eq(false));\n        final long processDefinitionId = 123123L;\n\n        //when\n        final byte[] zipContent = new byte[0];\n        try {\n            customPageService.getPageProperties(apiSession, zipContent, checkIfItAlreadyExists, processDefinitionId);\n            fail(\"AlreadyExistsException has not been thrown\");\n        } catch (final AlreadyExistsException e) {\n        }\n\n        //then\n        verify(pageAPI, times(1)).getPageByNameAndProcessDefinitionId(pageName, processDefinitionId);\n    }\n\n    @Test\n    public void getPageProperties_should_Retrieve_Properties_Without_Errors() throws Exception {\n        //given\n        final boolean checkIfItAlreadyExists = true;\n        final Properties pageProperties = new Properties();\n        final String pageName = \"test\";\n        pageProperties.put(CustomPageService.NAME_PROPERTY, pageName);\n        doReturn(pageProperties).when(pageAPI).getPageProperties(any(byte[].class), eq(false));\n        final long processDefinitionId = 123123L;\n        doThrow(new PageNotFoundException(\"page not found\")).when(pageAPI).getPageByName(pageName);\n        doThrow(new PageNotFoundException(\"page not found\")).when(pageAPI).getPageByNameAndProcessDefinitionId(pageName,\n                processDefinitionId);\n\n        //when\n        final byte[] zipContent = new byte[0];\n        final Properties props = customPageService.getPageProperties(apiSession, zipContent, checkIfItAlreadyExists,\n                processDefinitionId);\n\n        //then\n        verify(pageAPI, times(1)).getPageByNameAndProcessDefinitionId(pageName, processDefinitionId);\n        verify(pageAPI, times(1)).getPageByName(pageName);\n        assertThat(props).isSameAs(pageProperties);\n    }\n\n    @Test\n    public void should_retrieve_class_file() throws Exception {\n        // Given\n        final Page mockedPage = mock(Page.class);\n        when(mockedPage.getId()).thenReturn(1L);\n        final byte[] zipFile = IOUtils.toByteArray(getClass().getResourceAsStream(\"page.zip\"));\n        when(pageAPI.getPageContent(1L)).thenReturn(zipFile);\n        when(pageResourceProvider.getPage(pageAPI)).thenReturn(mockedPage);\n        when(pageResourceProvider.getTempPageFile()).thenReturn(new File(\"target/bonita/home/client/tenant/1/temp\"));\n        when(pageResourceProvider.getPageDirectory())\n                .thenReturn(new File(\"target/bonita/home/client/tenants/1/pages/page1\"));\n\n        // When\n        customPageService.retrievePageZipContent(apiSession, pageResourceProvider);\n        final File file = customPageService.getPageFile(pageResourceProvider.getPageDirectory(), \"readme.txt\");\n\n        // Validate\n        assertThat(file).as(\"should return file\").exists();\n    }\n\n    @Test\n    public void should_register_restApiPage() throws Exception {\n        // Given\n        File pageDirectory = new File(\"target/bonita/home/client/tenants/1/pages/page2\");\n        final GroovyClassLoader pageClassloader = initializePageMocks(pageDirectory);\n        ControllerClassName controllerClassName = new ControllerClassName(\"RestResource.groovy\", true);\n\n        // When\n        customPageService.retrievePageZipContent(apiSession, pageResourceProvider);\n        final Class<?> restApiControllerClass = customPageService.registerRestApiPage(pageClassloader,\n                pageResourceProvider,\n                controllerClassName, controllerClassName.getName());\n\n        // then\n        final org.bonitasoft.web.extension.rest.RestApiController restApiController = (org.bonitasoft.web.extension.rest.RestApiController) restApiControllerClass\n                .newInstance();\n        final RestApiResponse restApiResponse = restApiController.doHandle(request, new RestApiResponseBuilder(),\n                restAPIContext);\n        RestApiResponseAssert.assertThat(restApiResponse).as(\"should return result\").hasResponse(\"RestResource.groovy!\")\n                .hasNoAdditionalCookies().hasHttpStatus(200);\n    }\n\n    @Test\n    public void should_ensure_page_is_up_to_date_with_empty_cache() throws Exception {\n        // Given\n        File pageDirectory = new File(\"target/bonita/home/client/tenants/1/pages/page2\");\n        initializePageMocks(pageDirectory);\n\n        // When\n        customPageService.ensurePageFolderIsUpToDate(apiSession, pageResourceProvider);\n\n        verify(customPageService, times(1)).getPageTimestampFromMemoryCache(fullPageName);\n        verify(customPageService, times(1)).retrievePageZipContent(apiSession, pageResourceProvider);\n        verify(customPageService, never()).getLastTimePageUpdateWasCheckedInDB(fullPageName);\n    }\n\n    @Test\n    public void should_ensure_page_is_up_to_date_with_empty_folder() throws Exception {\n\n        // Given\n        File pageDirectory = spy(new File(\"target/bonita/home/client/tenants/1/pages/page2\"));\n        doReturn(new String[0]).when(pageDirectory).list();\n        doReturn(true).when(pageDirectory).exists();\n        doReturn(\"target/bonita/home/client/tenants/1/pages/page2\").when(pageDirectory).getPath();\n        initializePageMocks(pageDirectory);\n        doNothing().when(customPageService).removePage(pageResourceProvider, true);\n        doNothing().when(customPageService).retrievePageZipContent(apiSession, pageResourceProvider);\n\n        customPageService.addPageTimestampToMemoryCache(fullPageName, databaseLastUpdateDate.getTime());\n\n        // When\n        customPageService.ensurePageFolderIsUpToDate(apiSession, pageResourceProvider);\n\n        verify(customPageService, times(1)).getPageTimestampFromMemoryCache(fullPageName);\n        verify(customPageService, times(1)).getLastTimePageUpdateWasCheckedInDB(fullPageName);\n        verify(customPageService, times(1)).retrievePageZipContent(apiSession, pageResourceProvider);\n        verify(customPageService, times(1)).getPageLastUpdateDateFromEngine(apiSession, pageResourceProvider);\n    }\n\n    @Test\n    public void should_verify_last_update_date_in_database() throws Exception {\n        File pageDirectory = new File(\"target/bonita/home/client/tenants/1/pages/page2\");\n        initializePageMocks(pageDirectory);\n        customPageService.addPageTimestampToMemoryCache(fullPageName, databaseLastUpdateDate.getTime());\n\n        // When\n        customPageService.ensurePageFolderIsUpToDate(apiSession, pageResourceProvider);\n\n        verify(customPageService, times(1)).getLastTimePageUpdateWasCheckedInDB(fullPageName);\n        verify(customPageService, times(1)).getLastTimePageUpdateWasCheckedInDB(fullPageName);\n        verify(customPageService, never()).retrievePageZipContent(apiSession, pageResourceProvider);\n    }\n\n    @Test\n    public void should_verify_last_update_date_in_database_and_update_page() throws Exception {\n        File pageDirectory = new File(\"target/bonita/home/client/tenants/1/pages/page2\");\n        initializePageMocks(pageDirectory);\n        customPageService.addPageTimestampToMemoryCache(fullPageName, databaseLastUpdateDate.getTime() - 1000);\n\n        // When\n        customPageService.ensurePageFolderIsUpToDate(apiSession, pageResourceProvider);\n\n        verify(customPageService, times(1)).getLastTimePageUpdateWasCheckedInDB(fullPageName);\n        verify(customPageService, times(1)).retrievePageZipContent(apiSession, pageResourceProvider);\n        verify(customPageService, times(1)).getPageLastUpdateDateFromEngine(apiSession, pageResourceProvider);\n    }\n\n    @Test\n    public void should_not_verify_last_update_date_in_database_more_often_than_authorized_interval() throws Exception {\n        // Given\n        File pageDirectory = new File(\"target/bonita/home/client/tenants/1/pages/page2\");\n        initializePageMocks(pageDirectory);\n        customPageService.setTimePageUpdateWasCheckedInDB(fullPageName);\n        customPageService.addPageTimestampToMemoryCache(fullPageName, databaseLastUpdateDate.getTime() - 1000);\n\n        // When\n        customPageService.ensurePageFolderIsUpToDate(apiSession, pageResourceProvider);\n\n        verify(customPageService, times(1)).getLastTimePageUpdateWasCheckedInDB(fullPageName);\n        verify(customPageService, never()).retrievePageZipContent(apiSession, pageResourceProvider);\n        verify(customPageService, never()).getPageLastUpdateDateFromEngine(apiSession, pageResourceProvider);\n    }\n\n    protected GroovyClassLoader initializePageMocks(File pageDirectory) throws BonitaException, IOException {\n        final Page mockedPage = mock(Page.class);\n        when(mockedPage.getId()).thenReturn(1L);\n        when(mockedPage.getLastModificationDate()).thenReturn(databaseLastUpdateDate);\n        doReturn(pageAPI).when(customPageService).getPageAPI(apiSession);\n        final byte[] zipFile = IOUtils.toByteArray(getClass().getResourceAsStream(\"pageApiExtension.zip\"));\n        when(pageAPI.getPageContent(1L)).thenReturn(zipFile);\n        when(pageResourceProvider.getPage(pageAPI)).thenReturn(mockedPage);\n        when(pageResourceProvider.getTempPageFile()).thenReturn(new File(\"target/bonita/home/client/tenant/1/temp\"));\n        when(pageResourceProvider.getPageDirectory()).thenReturn(pageDirectory);\n        when(customPageService.isPageInDebugMode()).thenReturn(true);\n        doReturn(Thread.currentThread().getContextClassLoader()).when(customPageService).getParentClassloader(\n                anyString(),\n                any(CustomPageDependenciesResolver.class),\n                any(BDMClientDependenciesResolver.class));\n        return customPageService.getPageClassloader(apiSession, pageResourceProvider);\n    }\n\n    @Test\n    public void should_add_page_root_folder_in_classpath() throws Exception {\n        final File pageDir = new File(getClass().getResource(\"/ARootPageFolder\").getFile());\n        final GroovyClassLoader classloader = customPageService.buildPageClassloader(apiSession, \"pageName\", pageDir);\n        assertThat(classloader.loadClass(\"AbstractIndex\")).isNotNull();\n        assertThat(classloader.loadClass(\"Index\")).isNotNull();\n        assertThat(classloader.loadClass(\"org.company.test.Util\")).isNotNull();\n        assertThat(classloader.getResource(\"org/company/test/config.properties\")).isNotNull();\n    }\n\n    @Test\n    public void should_add_rest_api_jar_in_classpath() throws Exception {\n        final File restAPIDir = new File(\n                getClass().getResource(\"/myRestAPI-1.0.0-SNAPSHOT/\").getFile());\n        final GroovyClassLoader classloader = customPageService.buildPageClassloader(apiSession, \"custompage_myRestAPI\",\n                restAPIDir);\n        assertThat(classloader.loadClass(\"com.compagny.rest.api.MyController\")).isNotNull();\n    }\n\n    @Test\n    public void should_parse_class_when_input_is_source() throws Exception {\n        ControllerClassName sourceController = new ControllerClassName(\"Index.groovy\", true);\n        ControllerClassName compiledController = new ControllerClassName(\"com.company.Index\", false);\n        File file = mock(File.class);\n        GroovyClassLoader loader = spy(new GroovyClassLoader());\n\n        when(file.exists()).thenReturn(true);\n        doReturn(file).when(customPageService).toFile(pageResourceProvider, sourceController.getName());\n        doReturn(null).when(loader).parseClass(file);\n        doReturn(null).when(loader).loadClass(anyString());\n\n        customPageService.registerRestApiPage(loader, pageResourceProvider, sourceController, \"\");\n        verify(loader).parseClass(file);\n        verify(loader, times(0)).loadClass(compiledController.getName());\n\n        customPageService.registerRestApiPage(loader, pageResourceProvider, compiledController, \"\");\n        verify(loader).parseClass(file);\n        verify(loader).loadClass(compiledController.getName());\n    }\n\n    @Test\n    public void should_generate_thread_safe_page_lock_objects() throws Exception {\n        //given\n        int nbOfThreads = 300;\n        var pageLocks = new ConcurrentLinkedQueue<>();\n\n        //when\n        var executorService = Executors.newCachedThreadPool();\n        var results = new ArrayList<Future<?>>();\n        for (int i = 0; i < nbOfThreads; i++) {\n            // All runnable try to get the lock for the same page ('fullPageName' each time)\n            results.add(executorService.submit(() -> pageLocks.add(customPageService.getPageLock(fullPageName))));\n        }\n        for (Future<?> res : results) {\n            res.get();\n        }\n\n        //then\n        //make sure that with many thread in parallel that ask a lock on a page , we have one single unique object created once by page to be used for the lock\n        Object pageLock = customPageService.getPageLock(fullPageName);\n        assertThat(pageLocks).hasSize(nbOfThreads).containsOnly(pageLock);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/page/CustomPageServletTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.page;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.*;\n\nimport java.io.File;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.console.common.server.page.extension.PageResourceProviderImpl;\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.engine.session.APISession;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class CustomPageServletTest {\n\n    MockHttpServletRequest hsRequest = new MockHttpServletRequest();\n\n    @Mock\n    MockHttpServletResponse hsResponse = new MockHttpServletResponse();\n\n    @Mock\n    HttpSession httpSession;\n\n    @Mock\n    APISession apiSession;\n\n    @Mock\n    ApplicationAuthorizationsHelper customPageAuthorizationsHelper;\n\n    @Mock\n    PageRenderer pageRenderer;\n\n    @Mock\n    ResourceRenderer resourceRenderer;\n\n    @Mock\n    PageResourceProviderImpl pageResourceProvider;\n\n    @Mock\n    BonitaHomeFolderAccessor bonitaHomeFolderAccessor;\n\n    @Mock\n    CustomPageRequestModifier customPageRequestModifier;\n\n    @Spy\n    @InjectMocks\n    CustomPageServlet servlet;\n\n    @Before\n    public void beforeEach() throws Exception {\n        hsRequest.setSession(httpSession);\n        doReturn(apiSession).when(httpSession).getAttribute(\"apiSession\");\n        doReturn(customPageAuthorizationsHelper).when(servlet).getCustomPageAuthorizationsHelper(apiSession);\n    }\n\n    @Test\n    public void should_get_Forbidden_Status_when_page_unAuthorize() throws Exception {\n        hsRequest.setPathInfo(\"/pageToken/\");\n        hsRequest.setParameter(\"appToken\", \"myApp\");\n        given(resourceRenderer.getPathSegments(\"/pageToken/\")).willReturn(Arrays.asList(\"pageToken\"));\n        doReturn(false).when(apiSession).isTechnicalUser();\n        given(customPageAuthorizationsHelper.isAuthorized(\"myApp\")).willReturn(false);\n\n        servlet.doGet(hsRequest, hsResponse);\n\n        verify(hsResponse).sendError(403, \"User not Authorized\");\n    }\n\n    @Test\n    public void should_get_badRequest_Status_when_page_name_is_not_set() throws Exception {\n        hsRequest.setPathInfo(\"/\");\n\n        servlet.doGet(hsRequest, hsResponse);\n\n        verify(hsResponse).sendError(400, \"The name of the page is required.\");\n    }\n\n    @Test\n    public void should_redirect_to_valide_url_on_missing_slash() throws Exception {\n        hsRequest.setRequestURI(\"/bonita/portal/custom-page/custompage_htmlexample?anyparam=paramvalue\");\n        hsRequest.setPathInfo(\"/custompage_htmlexample\");\n\n        servlet.doGet(hsRequest, hsResponse);\n\n        verify(customPageRequestModifier).redirectToValidPageUrl(hsRequest, hsResponse);\n    }\n\n    @Test\n    public void getPage_should_call_the_page_renderer() throws Exception {\n        testPageIsWellCalled(\"custompage_htmlexample1\", \"/custompage_htmlexample1/\",\n                Arrays.asList(\"custompage_htmlexample1\"));\n        testPageIsWellCalled(\"custompage_htmlexample2\", \"/custompage_htmlexample2/index\",\n                Arrays.asList(\"custompage_htmlexample2\", \"index\"));\n        testPageIsWellCalled(\"custompage_htmlexample3\", \"/custompage_htmlexample3/Index\",\n                Arrays.asList(\"custompage_htmlexample3\", \"Index\"));\n        testPageIsWellCalled(\"custompage_htmlexample4\", \"/custompage_htmlexample4/index.html\",\n                Arrays.asList(\"custompage_htmlexample4\", \"index.html\"));\n        testPageIsWellCalled(\"custompage_htmlexample5\", \"/custompage_htmlexample5/index.groovy\",\n                Arrays.asList(\"custompage_htmlexample5\", \"index.groovy\"));\n    }\n\n    private void testPageIsWellCalled(final String token, final String path, final List<String> pathSegment)\n            throws Exception {\n        hsRequest.setPathInfo(path);\n        given(resourceRenderer.getPathSegments(path)).willReturn(pathSegment);\n        given(customPageAuthorizationsHelper.isAuthorized(null)).willReturn(true);\n\n        servlet.doGet(hsRequest, hsResponse);\n\n        verify(pageRenderer, times(1)).displayCustomPage(hsRequest, hsResponse, apiSession, token);\n    }\n\n    @Test\n    public void getResource_should_call_the_resource_renderer() throws Exception {\n        hsRequest.setPathInfo(\"/custompage_htmlexample/css/file.css\");\n        final File pageDir = new File(\"/pageDir\");\n        final String pageName = \"custompage_htmlexample\";\n        given(resourceRenderer.getPathSegments(\"/custompage_htmlexample/css/file.css\"))\n                .willReturn(Arrays.asList(pageName, \"css\", \"file.css\"));\n        doReturn(pageResourceProvider).when(pageRenderer).getPageResourceProvider(pageName);\n        doReturn(pageDir).when(pageResourceProvider).getPageDirectory();\n        doReturn(true).when(bonitaHomeFolderAccessor).isInFolder(any(File.class), any(File.class));\n        given(customPageAuthorizationsHelper.isAuthorized(null)).willReturn(true);\n\n        servlet.doGet(hsRequest, hsResponse);\n\n        verify(resourceRenderer, times(1)).renderFile(hsRequest, hsResponse,\n                new File(pageDir, File.separator + \"resources\" + File.separator + \"css\" + File.separator + \"file.css\"));\n    }\n\n    @Test\n    public void should_get_Forbidden_Status_when_we_try_to_access_to_unAuthorize_file_with_pathSegment()\n            throws Exception {\n        hsRequest.setPathInfo(\"/custompage_htmlexample/css/../../../file.css\");\n        final File pageDir = new File(\".\");\n        given(resourceRenderer.getPathSegments(\"/custompage_htmlexample/css/../../../file.css\")).willReturn(\n                Arrays.asList(\"custompage_htmlexample\", \"css\", \"..\", \"..\", \"..\", \"file.css\"));\n        doReturn(pageResourceProvider).when(pageRenderer).getPageResourceProvider(\"custompage_htmlexample\");\n        given(pageResourceProvider.getPageDirectory()).willReturn(pageDir);\n        given(customPageAuthorizationsHelper.isAuthorized(null)).willReturn(true);\n        // folder we wants to access is not authorized\n        doReturn(false).when(bonitaHomeFolderAccessor).isInFolder(any(File.class), any(File.class));\n\n        servlet.doGet(hsRequest, hsResponse);\n        verify(hsResponse).sendError(403, \"User not Authorized\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/page/PageContextAssert.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.page;\n\nimport static java.lang.String.format;\n\nimport java.util.Locale;\n\nimport org.assertj.core.api.AbstractAssert;\nimport org.bonitasoft.console.common.server.page.extension.PageContextImpl;\nimport org.bonitasoft.engine.session.APISession;\n\n/**\n * {@link PageContextImpl} specific assertions - Generated by CustomAssertionGenerator.\n */\npublic class PageContextAssert extends AbstractAssert<PageContextAssert, PageContextImpl> {\n\n    /**\n     * Creates a new </code>{@link PageContextAssert}</code> to make assertions on actual PageContext.\n     *\n     * @param actual the PageContext we want to make assertions on.\n     */\n    public PageContextAssert(PageContextImpl actual) {\n        super(actual, PageContextAssert.class);\n    }\n\n    /**\n     * An entry point for PageContextAssert to follow AssertJ standard <code>assertThat()</code> statements.<br>\n     * With a static import, one's can write directly : <code>assertThat(myPageContext)</code> and get specific\n     * assertion with code completion.\n     *\n     * @param actual the PageContext we want to make assertions on.\n     * @return a new </code>{@link PageContextAssert}</code>\n     */\n    public static PageContextAssert assertThat(PageContextImpl actual) {\n        return new PageContextAssert(actual);\n    }\n\n    /**\n     * Verifies that the actual PageContext's apiSession is equal to the given one.\n     *\n     * @param apiSession the given apiSession to compare the actual PageContext's apiSession to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual PageContext's apiSession is not equal to the given one.\n     */\n    public PageContextAssert hasApiSession(APISession apiSession) {\n        // check that actual PageContext we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> apiSession to be:\\n  <%s>\\n but was:\\n  <%s>\", actual, apiSession,\n                actual.getApiSession());\n\n        // check\n        if (!actual.getApiSession().equals(apiSession)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual PageContext's locale is equal to the given one.\n     *\n     * @param locale the given locale to compare the actual PageContext's locale to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual PageContext's locale is not equal to the given one.\n     */\n    public PageContextAssert hasLocale(Locale locale) {\n        // check that actual PageContext we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> locale to be:\\n  <%s>\\n but was:\\n  <%s>\", actual, locale,\n                actual.getLocale());\n\n        // check\n        if (!actual.getLocale().equals(locale)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual PageContext's profileID is equal to the given one.\n     *\n     * @param profileID the given profileID to compare the actual PageContext's profileID to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual PageContext's profileID is not equal to the given one.\n     */\n    public PageContextAssert hasProfileID(String profileID) {\n        // check that actual PageContext we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> profileID to be:\\n  <%s>\\n but was:\\n  <%s>\", actual, profileID,\n                actual.getProfileID());\n\n        // check\n        if (!actual.getProfileID().equals(profileID)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/page/PageContextHelperTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.page;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.doReturn;\n\nimport java.util.Locale;\n\nimport javax.servlet.http.Cookie;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.console.common.server.utils.LocaleUtils;\nimport org.bonitasoft.engine.session.APISession;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Laurent Leseigneur\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class PageContextHelperTest {\n\n    public static final String MY_PROFILE = \"myProfile\";\n    PageContextHelper pageContextHelper;\n\n    @Mock\n    private HttpServletRequest request;\n\n    @Mock\n    private HttpSession httpSession;\n\n    private Locale locale;\n\n    @Mock\n    private APISession apiSession;\n\n    @Before\n    public void before() throws Exception {\n        locale = Locale.FRANCE;\n    }\n\n    @Test\n    public void should_return_CurrentProfile() throws Exception {\n        //given\n        doReturn(MY_PROFILE).when(request).getParameter(PageContextHelper.PROFILE_PARAM);\n        pageContextHelper = new PageContextHelper(request);\n\n        //when\n        final String currentProfile = pageContextHelper.getCurrentProfile();\n\n        //then\n        assertThat(currentProfile).isEqualTo(MY_PROFILE);\n\n    }\n\n    @Test\n    public void should_getCurrentLocale_return_request_parameter() throws Exception {\n        //given\n        doReturn(locale.toString()).when(request).getParameter(LocaleUtils.LOCALE_PARAM);\n        pageContextHelper = new PageContextHelper(request);\n\n        //when\n        final Locale returnedLocale = pageContextHelper.getCurrentLocale();\n\n        //then\n        assertThat(returnedLocale.getLanguage()).isEqualTo(locale.getLanguage());\n        assertThat(returnedLocale.getCountry()).isEmpty();\n    }\n\n    @Test\n    public void should_getCurrentLocale_return_cookie_locale() throws Exception {\n        //given\n        Cookie[] cookieList = new Cookie[1];\n        cookieList[0] = new Cookie(LocaleUtils.LOCALE_COOKIE_NAME, locale.getLanguage());\n\n        doReturn(null).when(request).getParameter(LocaleUtils.LOCALE_PARAM);\n        doReturn(cookieList).when(request).getCookies();\n        pageContextHelper = new PageContextHelper(request);\n\n        //when\n        final Locale returnedLocale = pageContextHelper.getCurrentLocale();\n\n        //then\n        assertThat(returnedLocale.getLanguage()).isEqualTo(locale.getLanguage());\n        assertThat(returnedLocale.getCountry()).isEmpty();\n\n    }\n\n    @Test\n    public void should_getCurrentLocale_return_default_locale() throws Exception {\n        //given\n        Cookie[] cookieList = new Cookie[1];\n        cookieList[0] = new Cookie(\"otherCookie\", \"otherValue\");\n\n        doReturn(null).when(request).getParameter(LocaleUtils.LOCALE_PARAM);\n        doReturn(cookieList).when(request).getCookies();\n        pageContextHelper = new PageContextHelper(request);\n\n        //when\n        final Locale returnedLocale = pageContextHelper.getCurrentLocale();\n\n        //then\n        assertThat(returnedLocale).hasToString(LocaleUtils.DEFAULT_LOCALE);\n    }\n\n    @Test\n    public void should_return_ApiSession() throws Exception {\n        //given\n        doReturn(httpSession).when(request).getSession();\n        doReturn(apiSession).when(httpSession).getAttribute(PageContextHelper.ATTRIBUTE_API_SESSION);\n        pageContextHelper = new PageContextHelper(request);\n\n        //when\n        final APISession returnedApiSession = pageContextHelper.getApiSession();\n\n        //then\n        assertThat(returnedApiSession).isEqualTo(apiSession);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/page/PageMappingServiceTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.page;\n\nimport static org.junit.Assert.*;\nimport static org.mockito.ArgumentMatchers.anyMap;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.*;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Locale;\nimport java.util.Map;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.engine.api.PageAPI;\nimport org.bonitasoft.engine.page.PageURL;\nimport org.bonitasoft.engine.page.URLAdapterConstants;\nimport org.bonitasoft.engine.session.APISession;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class PageMappingServiceTest {\n\n    @Spy\n    PageMappingService pageMappingService;\n\n    @Mock\n    PageAPI pageAPI;\n\n    @Mock\n    HttpServletRequest hsRequest;\n\n    @Mock\n    HttpSession httpSession;\n\n    @Mock\n    APISession apiSession;\n\n    @Before\n    public void beforeEach() throws Exception {\n        doReturn(pageAPI).when(pageMappingService).getPageAPI(apiSession);\n        when(hsRequest.getContextPath()).thenReturn(\"/bonita\");\n        Map<String, String[]> params = new HashMap<>();\n        params.put(\"key\", new String[] { \"value\" });\n        when(hsRequest.getParameterMap()).thenReturn(params);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Test\n    public void getPage() throws Exception {\n        final PageURL pageURL = mock(PageURL.class);\n        when(pageURL.getUrl()).thenReturn(\"/externalURL\");\n        when(pageURL.getPageId()).thenReturn(null);\n        ArgumentCaptor<Map<String, Serializable>> contextCaptor = ArgumentCaptor.forClass(Map.class);\n        when(pageAPI.resolvePageOrURL(eq(\"process/processName/processVersion\"), anyMap(), eq(true)))\n                .thenReturn(pageURL);\n\n        final PageReference returnedPageReference = pageMappingService.getPage(hsRequest, apiSession,\n                \"process/processName/processVersion\", new Locale(\"en\"),\n                true);\n\n        verify(pageAPI).resolvePageOrURL(eq(\"process/processName/processVersion\"), contextCaptor.capture(), eq(true));\n        Map<String, Serializable> capturedContext = contextCaptor.getValue();\n        assertEquals(\"/bonita\", capturedContext.get(URLAdapterConstants.CONTEXT_PATH));\n        assertEquals(\"en\", capturedContext.get(URLAdapterConstants.LOCALE));\n        assertEquals(\"value\",\n                ((Map<String, String[]>) capturedContext.get(URLAdapterConstants.QUERY_PARAMETERS)).get(\"key\")[0]);\n        assertNotNull(returnedPageReference);\n        assertNull(returnedPageReference.getPageId());\n        assertEquals(\"/externalURL\", returnedPageReference.getUrl());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/page/PageRendererTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.page;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport java.io.File;\nimport java.net.URISyntaxException;\n\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.console.common.server.page.extension.PageResourceProviderImpl;\nimport org.bonitasoft.engine.api.PageAPI;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.engine.session.APISession;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\n\n/**\n * @author Julien Mege\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class PageRendererTest {\n\n    @Mock\n    ResourceRenderer resourceRenderer;\n\n    @Mock\n    CustomPageService customPageService;\n\n    @Spy\n    @InjectMocks\n    PageRenderer pageRenderer = new PageRenderer(resourceRenderer);\n\n    MockHttpServletRequest hsRequest = new MockHttpServletRequest();\n\n    MockHttpServletResponse hsResponse = new MockHttpServletResponse();\n\n    @Mock\n    HttpSession httpSession;\n\n    @Mock\n    PageResourceProviderImpl pageResourceProvider;\n\n    @Mock\n    APISession apiSession;\n\n    @Mock\n    Page page;\n\n    @Mock\n    PageAPI pageAPI;\n\n    @Before\n    public void beforeEach() {\n        hsRequest.setSession(httpSession);\n    }\n\n    @Test\n    public void should_display_html_page() throws Exception {\n        final File indexFile = initializeHTMLFilePage();\n\n        pageRenderer.displayCustomPage(hsRequest, hsResponse, apiSession, 42L);\n\n        verify(customPageService, times(1)).ensurePageFolderIsUpToDate(apiSession, pageResourceProvider);\n        verify(resourceRenderer, times(1)).renderFile(hsRequest, hsResponse, indexFile, true);\n    }\n\n    protected File initializeHTMLFilePage() throws URISyntaxException, BonitaException {\n        final String pageName = \"my_html_page_v_7\";\n        final File pageDir = new File(PageRenderer.class.getResource(pageName).toURI());\n        final File indexFile = new File(pageDir, \"resources\" + File.separator + \"index.html\");\n        doReturn(pageResourceProvider).when(pageRenderer).getPageResourceProvider(42L, apiSession);\n        doReturn(pageDir).when(pageResourceProvider).getPageDirectory();\n        when(customPageService.getGroovyPageFile(any(File.class))).thenReturn(new File(\"none_existing_file\"));\n        return indexFile;\n    }\n\n    @Test\n    public void should_display_fallback_index_if_no_index_in_resource_folder() throws Exception {\n\n        final String pageName = \"my_html_page_v_6\";\n        final File pageDir = new File(PageRenderer.class.getResource(pageName).toURI());\n        final File indexFile = new File(pageDir, \"index.html\");\n        doReturn(pageResourceProvider).when(pageRenderer).getPageResourceProvider(42L, apiSession);\n        doReturn(pageDir).when(pageResourceProvider).getPageDirectory();\n        when(customPageService.getGroovyPageFile(any(File.class))).thenReturn(new File(\"none_existing_file\"));\n\n        pageRenderer.displayCustomPage(hsRequest, hsResponse, apiSession, 42L);\n\n        verify(resourceRenderer, times(1)).renderFile(hsRequest, hsResponse, indexFile, true);\n    }\n\n    @Test\n    public void should_get_pageResourceProvider_by_pageId() throws Exception {\n        //given\n        final String pageName = \"pageName\";\n        doReturn(pageName).when(page).getName();\n        doReturn(page).when(customPageService).getPage(apiSession, 42L);\n\n        //when\n        final PageResourceProviderImpl pageResourceProvider = pageRenderer.getPageResourceProvider(42L, apiSession);\n\n        //then\n        assertThat(pageResourceProvider.getPageName()).isEqualTo(pageName);\n    }\n\n    @Test\n    public void should_get_pageResourceProvider_by_pageName() throws Exception {\n        //given\n        final String pageName = \"pageName\";\n        //when\n        final PageResourceProviderImpl pageResourceProvider = pageRenderer.getPageResourceProvider(pageName);\n\n        //then\n        assertThat(pageResourceProvider.getPageName()).isEqualTo(pageName);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/page/PageServletTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.page;\n\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.doThrow;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport java.io.File;\nimport java.util.Locale;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpSession;\n\nimport org.apache.http.HttpHeaders;\nimport org.bonitasoft.console.common.server.page.extension.PageResourceProviderImpl;\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.engine.exception.NotFoundException;\nimport org.bonitasoft.engine.exception.UnauthorizedAccessException;\nimport org.bonitasoft.engine.page.PageNotFoundException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Answers;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class PageServletTest {\n\n    public static final long PAGE_ID = 42L;\n    @Mock\n    PageRenderer pageRenderer;\n\n    @Mock\n    ResourceRenderer resourceRenderer;\n\n    @Mock\n    PageMappingService pageMappingService;\n\n    @Mock\n    CustomPageService customPageService;\n\n    @Mock\n    BonitaHomeFolderAccessor bonitaHomeFolderAccessor;\n\n    @Spy\n    @InjectMocks\n    PageServlet pageServlet;\n\n    @Mock(answer = Answers.RETURNS_MOCKS)\n    HttpServletRequest hsRequest;\n\n    @Mock\n    HttpServletResponse hsResponse;\n\n    @Mock\n    HttpSession httpSession;\n\n    @Mock\n    APISession apiSession;\n\n    Locale locale;\n\n    @Before\n    public void beforeEach() throws Exception {\n        when(hsRequest.getMethod()).thenReturn(\"GET\");\n        when(hsRequest.getContextPath()).thenReturn(\"/bonita\");\n        when(hsRequest.getSession()).thenReturn(httpSession);\n        when(httpSession.getAttribute(\"apiSession\")).thenReturn(apiSession);\n        locale = new Locale(\"en\");\n        when(pageRenderer.getCurrentLocale(hsRequest)).thenReturn(new Locale(\"en\"));\n    }\n\n    @Test\n    public void should_get_Forbidden_Status_when_unauthorized() throws Exception {\n        when(hsRequest.getPathInfo()).thenReturn(\"/process/processName/processVersion/content/\");\n        doThrow(UnauthorizedAccessException.class).when(pageMappingService).getPage(hsRequest, apiSession,\n                \"process/processName/processVersion\", locale, true);\n\n        pageServlet.service(hsRequest, hsResponse);\n\n        verify(hsResponse, times(1)).sendError(403, \"User not Authorized\");\n    }\n\n    @Test\n    public void should_get_Bad_Request_when_invalid_parameters() throws Exception {\n        when(hsRequest.getPathInfo()).thenReturn(\"\");\n        pageServlet.service(hsRequest, hsResponse);\n        verify(hsResponse, times(1)).sendError(400,\n                \"/content or /theme is expected in the URL after the page mapping key\");\n    }\n\n    @Test\n    public void should_display_externalPage() throws Exception {\n        when(hsRequest.getPathInfo()).thenReturn(\"/process/processName/processVersion/content/\");\n        when(pageMappingService.getPage(hsRequest, apiSession, \"process/processName/processVersion\", locale, true))\n                .thenReturn(\n                        new PageReference(null, \"/externalPage\"));\n\n        pageServlet.service(hsRequest, hsResponse);\n\n        verify(pageServlet, times(1)).displayExternalPage(hsResponse, \"/externalPage\");\n        verify(hsResponse, times(1)).encodeRedirectURL(\"/externalPage\");\n        verify(hsResponse, times(1)).sendRedirect(any());\n    }\n\n    @Test\n    public void should_display_customPage() throws Exception {\n        when(hsRequest.getPathInfo()).thenReturn(\"/process/processName/processVersion/content/\");\n        when(pageMappingService.getPage(hsRequest, apiSession, \"process/processName/processVersion\", locale, true))\n                .thenReturn(new PageReference(PAGE_ID, null));\n\n        pageServlet.service(hsRequest, hsResponse);\n\n        verify(pageRenderer, times(1)).displayCustomPage(hsRequest, hsResponse, apiSession, PAGE_ID, locale);\n    }\n\n    @Test\n    public void should_display_customPage_resource() throws Exception {\n        //given\n        when(hsRequest.getPathInfo()).thenReturn(\"/process/processName/processVersion/content/path/of/resource.css\");\n        final PageReference pageReference = new PageReference(PAGE_ID, null);\n        when(pageMappingService.getPage(hsRequest, apiSession, \"process/processName/processVersion\", locale, false))\n                .thenReturn(pageReference);\n\n        final PageResourceProviderImpl pageResourceProvider = mock(PageResourceProviderImpl.class);\n        final File resourceFile = mock(File.class);\n        when(pageResourceProvider.getResourceAsFile(\"resources\" + File.separator + \"path/of/resource.css\"))\n                .thenReturn(resourceFile);\n        when(pageRenderer.getPageResourceProvider(PAGE_ID, apiSession)).thenReturn(pageResourceProvider);\n        when(bonitaHomeFolderAccessor.isInFolder(resourceFile, null)).thenReturn(true);\n\n        //when\n        pageServlet.service(hsRequest, hsResponse);\n\n        //then\n        verify(pageServlet, times(1)).displayPageOrResource(hsRequest, hsResponse, apiSession, PAGE_ID,\n                \"path/of/resource.css\", locale);\n        verify(pageServlet, times(1)).getResourceFile(hsResponse, apiSession, PAGE_ID, \"path/of/resource.css\");\n        verify(pageRenderer, times(1)).ensurePageFolderIsPresent(apiSession, pageResourceProvider);\n        verify(resourceRenderer, times(1)).renderFile(hsRequest, hsResponse, resourceFile);\n    }\n\n    @Test\n    public void should_get_not_found_when_engine_throw_not_found() throws Exception {\n        final String key = \"process/processName/processVersion\";\n        when(hsRequest.getPathInfo()).thenReturn(\"/\" + key + \"/content/\");\n        doThrow(NotFoundException.class).when(pageMappingService).getPage(hsRequest, apiSession, key, locale, true);\n\n        pageServlet.service(hsRequest, hsResponse);\n\n        verify(hsResponse, times(1)).sendError(404, \"Form mapping not found\");\n    }\n\n    @Test\n    public void should_not_get_not_found_when_empty_mapping() throws Exception {\n        when(hsRequest.getPathInfo()).thenReturn(\"/process/processName/processVersion/content/\");\n        final PageReference pageReference = new PageReference(null, null);\n        doReturn(pageReference).when(pageMappingService).getPage(hsRequest, apiSession,\n                \"process/processName/processVersion\", locale, true);\n\n        pageServlet.service(hsRequest, hsResponse);\n\n        verify(hsResponse, never()).sendError(anyInt(), anyString());\n    }\n\n    @Test\n    public void should_get_not_found_if_the_page_does_not_exist() throws Exception {\n        final String key = \"process/processName/processVersion\";\n        when(hsRequest.getPathInfo()).thenReturn(\"/\" + key + \"/content/\");\n        when(pageMappingService.getPage(hsRequest, apiSession, key, locale, true))\n                .thenReturn(new PageReference(PAGE_ID, null));\n        doThrow(PageNotFoundException.class).when(pageRenderer).displayCustomPage(hsRequest, hsResponse, apiSession,\n                PAGE_ID, locale);\n\n        pageServlet.service(hsRequest, hsResponse);\n\n        verify(hsResponse, times(1)).sendError(404, \"Page not found\");\n    }\n\n    @Test\n    public void should_redirect_for_missing_slash() throws Exception {\n        when(hsRequest.getPathInfo()).thenReturn(\"/process/processName/processVersion/content\");\n        when(hsRequest.getContextPath()).thenReturn(\"/bonita\");\n        when(hsRequest.getServletPath()).thenReturn(\"/portal/resource\");\n\n        pageServlet.service(hsRequest, hsResponse);\n\n        verify(hsResponse, times(1))\n                .encodeRedirectURL(\"/bonita/portal/resource/process/processName/processVersion/content/\");\n        verify(hsResponse, times(1)).sendRedirect(any());\n    }\n\n    @Test\n    public void should_get_server_error_when_issue_with_customPage() throws Exception {\n        when(hsRequest.getPathInfo()).thenReturn(\"/process/processName/processVersion/content/\");\n        when(pageMappingService.getPage(hsRequest, apiSession, \"process/processName/processVersion\", locale, true))\n                .thenReturn(new PageReference(PAGE_ID, null));\n        final InstantiationException instantiationException = new InstantiationException(\"instatiation exception\");\n        doThrow(instantiationException).when(pageRenderer).displayCustomPage(hsRequest, hsResponse, apiSession, PAGE_ID,\n                locale);\n\n        pageServlet.service(hsRequest, hsResponse);\n\n        verify(pageServlet).handleException(hsRequest, hsResponse, \"process/processName/processVersion\", true,\n                instantiationException);\n        verify(hsResponse, times(1)).sendError(500, \"instatiation exception\");\n    }\n\n    @Test\n    public void should_get_bad_request_when_issue_with_parameters() throws Exception {\n        when(hsRequest.getPathInfo()).thenReturn(\"/process/processName/processVersion/content/\");\n        final IllegalArgumentException illegalArgumentException = new IllegalArgumentException();\n        doThrow(illegalArgumentException).when(pageMappingService).getPage(hsRequest, apiSession,\n                \"process/processName/processVersion\", locale, true);\n\n        pageServlet.service(hsRequest, hsResponse);\n\n        verify(pageServlet).handleException(hsRequest, hsResponse, \"process/processName/processVersion\", true,\n                illegalArgumentException);\n        verify(hsResponse, times(1)).sendError(400, \"Invalid Request.\");\n    }\n\n    @Test\n    public void should_get_unauthorized_when_engine_session_is_invalid() throws Exception {\n        when(hsRequest.getPathInfo()).thenReturn(\"/process/processName/processVersion/content/\");\n        when(pageMappingService.getPage(hsRequest, apiSession, \"process/processName/processVersion\", locale, true))\n                .thenReturn(new PageReference(PAGE_ID, null));\n        final InvalidSessionException invalidSessionException = new InvalidSessionException(\"Invalid session\");\n        doThrow(invalidSessionException).when(pageRenderer).displayCustomPage(hsRequest, hsResponse, apiSession,\n                PAGE_ID, locale);\n\n        pageServlet.service(hsRequest, hsResponse);\n\n        verify(pageServlet).displayPageOrResource(hsRequest, hsResponse, apiSession, PAGE_ID, null, locale);\n        verify(pageServlet).handleException(hsRequest, hsResponse, \"process/processName/processVersion\", true,\n                invalidSessionException);\n        verify(hsResponse).sendError(401, \"Invalid Bonita engine session.\");\n    }\n\n    @Test\n    public void should_forward_when_API_call() throws Exception {\n        when(hsRequest.getPathInfo()).thenReturn(\"/process/processName/processVersion/API/bpm/process/1\");\n\n        pageServlet.service(hsRequest, hsResponse);\n\n        verify(hsRequest, times(1)).getRequestDispatcher(\"/API/bpm/process/1\");\n    }\n\n    @Test\n    public void should_forward_when_THEME_call_and_no_app_in_refered() throws Exception {\n        when(hsRequest.getPathInfo()).thenReturn(\"/process/Test/1.0/theme/theme.css\");\n        when(hsRequest.getParameter(\"app\")).thenReturn(null);\n        when(pageServlet.getAppFromReferer(hsRequest)).thenReturn(null);\n\n        pageServlet.service(hsRequest, hsResponse);\n\n        verify(hsRequest, times(1)).getRequestDispatcher(\"/theme/theme.css\");\n    }\n\n    @Test\n    public void should_display_theme_resource_with_app_param() throws Exception {\n        when(hsRequest.getPathInfo()).thenReturn(\"/process/Test/1.0/theme/theme.css\");\n        when(hsRequest.getParameter(\"app\")).thenReturn(\"myApp\");\n        doReturn(12L).when(pageServlet).getThemeId(apiSession, \"myApp\");\n\n        final PageResourceProviderImpl pageResourceProvider = mock(PageResourceProviderImpl.class);\n        final File resourceFile = mock(File.class);\n        when(pageResourceProvider.getResourceAsFile(\"resources\" + File.separator + \"theme.css\"))\n                .thenReturn(resourceFile);\n        when(pageRenderer.getPageResourceProvider(12L, apiSession)).thenReturn(pageResourceProvider);\n        when(bonitaHomeFolderAccessor.isInFolder(resourceFile, null)).thenReturn(true);\n\n        pageServlet.service(hsRequest, hsResponse);\n\n        verify(resourceRenderer, times(1)).renderFile(hsRequest, hsResponse, resourceFile);\n    }\n\n    @Test\n    public void should_redirect_theme_resource_wihout_app_param() throws Exception {\n        when(hsRequest.getPathInfo()).thenReturn(\"/resource/process/Test/1.0/theme/theme.css\");\n        when(hsRequest.getParameter(\"app\")).thenReturn(null);\n        when(hsRequest.getQueryString()).thenReturn(null);\n        when(hsRequest.getHeader(HttpHeaders.REFERER))\n                .thenReturn(\"/bonita/resource/process/Test/1.0/content/?app=myApp\");\n\n        pageServlet.service(hsRequest, hsResponse);\n\n        verify(hsResponse, times(1)).sendRedirect(\"?app=myApp\");\n    }\n\n    @Test\n    public void should_not_redirect_theme_resource_wihout_app_param_for_images() throws Exception {\n        when(hsRequest.getPathInfo()).thenReturn(\"/process/Test/1.0/theme/icon.png\");\n        when(hsRequest.getParameter(\"app\")).thenReturn(null);\n        when(hsRequest.getHeader(HttpHeaders.REFERER))\n                .thenReturn(\"/bonita/resource/process/Test/1.0/theme/theme.css?app=myApp\");\n        doReturn(12L).when(pageServlet).getThemeId(apiSession, \"myApp\");\n\n        final PageResourceProviderImpl pageResourceProvider = mock(PageResourceProviderImpl.class);\n        final File resourceFile = mock(File.class);\n        when(pageResourceProvider.getResourceAsFile(\"resources\" + File.separator + \"icon.png\"))\n                .thenReturn(resourceFile);\n        when(pageRenderer.getPageResourceProvider(12L, apiSession)).thenReturn(pageResourceProvider);\n        when(bonitaHomeFolderAccessor.isInFolder(resourceFile, null)).thenReturn(true);\n\n        pageServlet.service(hsRequest, hsResponse);\n\n        verify(hsResponse, never()).sendRedirect(\"?app=myApp\");\n        verify(resourceRenderer, times(1)).renderFile(hsRequest, hsResponse, resourceFile);\n    }\n\n    @Test\n    public void should_not_authorize_API_requests_to_other_paths() throws Exception {\n        String unauthorizedPath = \"/API/living/../../WEB-INF/web.xml\";\n        when(hsRequest.getPathInfo()).thenReturn(\"/process/Test/1.0\" + unauthorizedPath);\n\n        pageServlet.service(hsRequest, hsResponse);\n\n        verify(hsResponse).setStatus(HttpServletResponse.SC_FORBIDDEN);\n        verify(hsRequest, never()).getRequestDispatcher(anyString());\n    }\n\n    @Test\n    public void should_not_authorize_theme_requests_to_other_paths() throws Exception {\n        when(hsRequest.getParameter(\"app\")).thenReturn(null);\n        when(hsRequest.getHeader(HttpHeaders.REFERER)).thenReturn(\"/bonita/resource/process/Test/1.0/content/\");\n\n        String unauthorizedPath = \"/theme/../WEB-INF/web.xml\";\n        when(hsRequest.getPathInfo()).thenReturn(\"/process/Test/1.0\" + unauthorizedPath);\n\n        pageServlet.service(hsRequest, hsResponse);\n\n        verify(hsResponse).setStatus(HttpServletResponse.SC_FORBIDDEN);\n        verify(hsRequest, never()).getRequestDispatcher(anyString());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/page/ResourceRendererTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.page;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.UnsupportedEncodingException;\nimport java.net.URISyntaxException;\nimport java.util.List;\n\nimport javax.servlet.ServletContext;\nimport javax.servlet.ServletOutputStream;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Julien Mege\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class ResourceRendererTest {\n\n    @Mock\n    private HttpServletRequest req;\n\n    @Mock\n    private HttpServletResponse res;\n\n    @Mock\n    private ServletContext servletContext;\n\n    @Mock\n    ServletOutputStream outputStream;\n\n    @Mock\n    HttpSession httpSession;\n\n    @Spy\n    @InjectMocks\n    ResourceRenderer resourceRenderer;\n\n    public ResourceRendererTest() {\n    }\n\n    @Before\n    public void setup() throws IOException {\n        when(req.getSession()).thenReturn(httpSession);\n        when(res.getOutputStream()).thenReturn(outputStream);\n        when(httpSession.getServletContext()).thenReturn(servletContext);\n    }\n\n    @Test\n    public void renderFile_should_build_a_valid_response()\n            throws BonitaException, URISyntaxException, IOException, IllegalAccessException {\n        final File resourceFile = getResourceFile();\n        final long contentLength = resourceFile.length();\n        when(servletContext.getMimeType(\"file.css\")).thenReturn(\"text/css\");\n        resourceRenderer.renderFile(req, res, resourceFile);\n\n        verify(res).setCharacterEncoding(\"UTF-8\");\n        verify(servletContext).getMimeType(\"file.css\");\n        verify(res).setContentType(\"text/css\");\n        verify(res).setContentLength((int) contentLength);\n        verify(res).setBufferSize((int) contentLength);\n        verify(outputStream).write(any(byte[].class), eq(0), eq((int) contentLength));\n        verify(res, never()).flushBuffer();\n        verify(outputStream, never()).close();\n    }\n\n    private File getResourceFile() throws URISyntaxException {\n        return new File(ResourceRendererTest.class.getResource(\"file.css\").toURI());\n    }\n\n    @Test(expected = BonitaException.class)\n    public void getResourceFile_should_throw_BonitaException_on_passing_null_resources_folder() throws Exception {\n        resourceRenderer.renderFile(req, res, null);\n    }\n\n    @Test\n    public void getResourceFile_should_sendError404_on_passing_none_existing_resources() throws Exception {\n        final File noneExistingFile = new File(\"NoneExistingFile.css\");\n        resourceRenderer.renderFile(req, res, noneExistingFile);\n        verify(res).setStatus(HttpServletResponse.SC_NOT_FOUND);\n    }\n\n    @Test\n    public void getPathSegments_should_return_expected_token_list() throws UnsupportedEncodingException {\n        final List<String> tokens = resourceRenderer.getPathSegments(\"a/b\");\n        assertThat(tokens).hasSize(2).containsExactly(\"a\", \"b\");\n    }\n\n    @Test\n    public void getPathSegments_should_return_expected_token_list_ondouble_slash() throws UnsupportedEncodingException {\n        final List<String> tokens = resourceRenderer.getPathSegments(\"a//b\");\n        assertThat(tokens).hasSize(2).containsExactly(\"a\", \"b\");\n    }\n\n    @Test\n    public void getPathSegments_should_return_expected_token_list_if_no_slash() throws UnsupportedEncodingException {\n        List<String> tokens = resourceRenderer.getPathSegments(\"a\");\n        assertThat(tokens).hasSize(1).containsExactly(\"a\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/page/RestApiResponseAssert.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.page;\n\nimport static java.lang.String.format;\n\nimport java.io.Serializable;\n\nimport org.assertj.core.api.AbstractAssert;\nimport org.bonitasoft.web.extension.rest.RestApiResponse;\n\n/**\n * {@link RestApiResponse} specific assertions - Generated by CustomAssertionGenerator.\n */\npublic class RestApiResponseAssert\n        extends AbstractAssert<RestApiResponseAssert, org.bonitasoft.web.extension.rest.RestApiResponse> {\n\n    /**\n     * Creates a new </code>{@link RestApiResponseAssert}</code> to make assertions on actual RestApiResponse.\n     *\n     * @param actual the RestApiResponse we want to make assertions on.\n     */\n    public RestApiResponseAssert(org.bonitasoft.web.extension.rest.RestApiResponse actual) {\n        super(actual, RestApiResponseAssert.class);\n    }\n\n    /**\n     * An entry point for RestApiResponseAssert to follow AssertJ standard <code>assertThat()</code> statements.<br>\n     * With a static import, one's can write directly : <code>assertThat(myRestApiResponse)</code> and get specific\n     * assertion with code completion.\n     *\n     * @param actual the RestApiResponse we want to make assertions on.\n     * @return a new </code>{@link RestApiResponseAssert}</code>\n     */\n    public static RestApiResponseAssert assertThat(org.bonitasoft.web.extension.rest.RestApiResponse actual) {\n        return new RestApiResponseAssert(actual);\n    }\n\n    /**\n     * Verifies that the actual RestApiResponse has no additionalCookies.\n     *\n     * @return this assertion object.\n     * @throws AssertionError if the actual RestApiResponse's additionalCookies is not empty.\n     */\n    public RestApiResponseAssert hasNoAdditionalCookies() {\n        // check that actual RestApiResponse we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected :\\n  <%s>\\nnot to have additionalCookies but had :\\n  <%s>\", actual,\n                actual.getAdditionalCookies());\n\n        // check\n        if (!actual.getAdditionalCookies().isEmpty())\n            throw new AssertionError(errorMessage);\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual RestApiResponse's httpStatus is equal to the given one.\n     *\n     * @param httpStatus the given httpStatus to compare the actual RestApiResponse's httpStatus to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual RestApiResponse's httpStatus is not equal to the given one.\n     */\n    public RestApiResponseAssert hasHttpStatus(int httpStatus) {\n        // check that actual RestApiResponse we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> httpStatus to be:\\n  <%s>\\n but was:\\n  <%s>\", actual, httpStatus,\n                actual.getHttpStatus());\n\n        // check\n        if (actual.getHttpStatus() != httpStatus) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual RestApiResponse's response is equal to the given one.\n     *\n     * @param response the given response to compare the actual RestApiResponse's response to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual RestApiResponse's response is not equal to the given one.\n     */\n    public RestApiResponseAssert hasResponse(Serializable response) {\n        // check that actual RestApiResponse we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> response to be:\\n  <%s>\\n but was:\\n  <%s>\", actual, response,\n                actual.getResponse());\n\n        // check\n        if (!actual.getResponse().equals(response)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/page/extension/PageContextImplTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.page.extension;\n\nimport java.util.Locale;\n\nimport org.bonitasoft.console.common.server.page.PageContextAssert;\nimport org.bonitasoft.engine.session.APISession;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Laurent Leseigneur\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class PageContextImplTest {\n\n    public static final Locale LOCALE = Locale.FRANCE;\n    public static final String PROFILE_ID = \"profileId\";\n    @Mock\n    private APISession apiSession;\n\n    @Test\n    public void testPageContext() throws Exception {\n        PageContextImpl pageContext = new PageContextImpl(apiSession, LOCALE, PROFILE_ID);\n\n        PageContextAssert.assertThat(pageContext).hasApiSession(apiSession)\n                .hasLocale(LOCALE)\n                .hasProfileID(PROFILE_ID);\n\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/page/extension/PageResourceProviderImplTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.page.extension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\n\nimport org.bonitasoft.engine.api.PageAPI;\nimport org.bonitasoft.engine.page.Page;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Laurent Leseigneur\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class PageResourceProviderImplTest {\n\n    public static final String PAGE_NAME = \"pageName\";\n    public static final long PROCESS_DEFINITION_ID = 123L;\n    public static final long PAGE_ID = 123L;\n\n    @Mock\n    private Page page;\n\n    @Mock\n    private PageAPI pageApi;\n\n    PageResourceProviderImpl pageResourceProvider;\n\n    PageResourceProviderImpl pageResourceProviderWithProcessDefinition;\n\n    @Before\n    public void before() throws Exception {\n        doReturn(PROCESS_DEFINITION_ID).when(page).getProcessDefinitionId();\n        doReturn(PAGE_NAME).when(page).getName();\n        doReturn(PAGE_ID).when(page).getId();\n\n        pageResourceProvider = new PageResourceProviderImpl(PAGE_NAME);\n        pageResourceProviderWithProcessDefinition = new PageResourceProviderImpl(page);\n    }\n\n    @Test\n    public void should_pagedirectory_be_distinct() throws Exception {\n        assertThat(pageResourceProvider.getPageDirectory()).as(\"should be distinct\")\n                .isNotEqualTo(pageResourceProviderWithProcessDefinition.getPageDirectory());\n    }\n\n    @Test\n    public void should_temp_page_file_be_distinct() throws Exception {\n        assertThat(pageResourceProvider.getTempPageFile()).as(\"should be page name\")\n                .isNotEqualTo(pageResourceProviderWithProcessDefinition.getTempPageFile());\n    }\n\n    @Test\n    public void should_testGetFullPageName_return_unique_key() throws Exception {\n        assertThat(pageResourceProvider.getFullPageName()).as(\"should be page name\").isEqualTo(PAGE_NAME);\n        assertThat(pageResourceProvider.getFullPageName()).as(\"should be page name\")\n                .isNotEqualTo(pageResourceProviderWithProcessDefinition.getFullPageName());\n    }\n\n    @Test\n    public void should_getPage_by_name() throws Exception {\n        // when\n        pageResourceProvider.getPage(pageApi);\n\n        //then\n        verify(pageApi).getPageByName(PAGE_NAME);\n    }\n\n    @Test\n    public void should_getPage_by_id() throws Exception {\n        // when\n        pageResourceProviderWithProcessDefinition.getPage(pageApi);\n\n        //then\n        verify(pageApi, never()).getPageByName(anyString());\n        verify(pageApi).getPage(PAGE_ID);\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/preferences/constants/WebBonitaConstantsImplTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.preferences.constants;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.doReturn;\n\nimport org.apache.commons.io.FilenameUtils;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class WebBonitaConstantsImplTest {\n\n    @Spy\n    WebBonitaConstantsImpl webBonitaConstants;\n\n    @Test\n    public void getTenantsFolderPath_should_return_tenants_sibling_folder_aside_platform() {\n        doReturn(\"some/path/to/platform/\").when(webBonitaConstants).getTempFolderPath();\n\n        final String tenantsFolderPath = webBonitaConstants.getTenantsFolderPath();\n\n        assertThat(tenantsFolderPath).isEqualTo(FilenameUtils.separatorsToSystem(\"some/path/to/tenant/\"));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/preferences/properties/ConfigurationFileTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.preferences.properties;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.console.common.server.preferences.properties.ConfigurationFilesManager.getProperties;\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.spy;\n\nimport java.util.Properties;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.commons.io.IOUtil;\nimport org.junit.Assert;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ConfigurationFileTest {\n\n    public static final String COMPOUND_PERMISSIONS_MAPPING_FILE = \"compound.properties\";\n    public static final String RESOURCES_PERMISSIONS_MAPPING_FILE = \"resources.properties\";\n    public static final String CUSTOM_PERMISSIONS_MAPPING_FILE = \"custom.properties\";\n    private static Properties compoundProperties;\n    private static Properties resourcesProperties;\n    private static Properties customProperties;\n\n    @BeforeClass\n    public static void init() throws Exception {\n        compoundProperties = getProperties(IOUtil.getAllContentFrom(\n                ConfigurationFileTest.class.getResourceAsStream(\"/compound-permissions-mapping.properties\")));\n        resourcesProperties = getProperties(IOUtil.getAllContentFrom(\n                ConfigurationFileTest.class.getResourceAsStream(\"/resources-permissions-mapping.properties\")));\n        customProperties = getProperties(IOUtil.getAllContentFrom(\n                ConfigurationFileTest.class.getResourceAsStream(\"/custom-permissions-mapping.properties\")));\n    }\n\n    @Test\n    public void should_getProperty_return_the_right_custom_permissions_with_special_characters() {\n        final ConfigurationFile tenantProperties = spy(new ConfigurationFile(CUSTOM_PERMISSIONS_MAPPING_FILE));\n        doReturn(customProperties).when(tenantProperties).getTenantPropertiesOfScope();\n\n        final String customValue = tenantProperties.getTenantProperty(\"profile|HR manager\");\n\n        assertEquals(\"[ManageProfiles]\", customValue);\n    }\n\n    @Test\n    public void should_getProperty_return_the_right_permissions_with_trailing_spaces() {\n        final ConfigurationFile tenantProperties = spy(new ConfigurationFile(COMPOUND_PERMISSIONS_MAPPING_FILE));\n        doReturn(compoundProperties).when(tenantProperties).getTenantPropertiesOfScope();\n\n        final String value = tenantProperties.getTenantProperty(\"caseListingPage\");\n        final Set<String> valueAsList = tenantProperties.getPropertyAsSet(\"caseListingPage\");\n\n        assertEquals(\"caseVisualizationWithTrailingSpace\", value);\n        assertThat(valueAsList).containsOnly(\"caseVisualizationWithTrailingSpace\");\n    }\n\n    @Test\n    public void should_getProperty_return_null_with_unknown_permissions() {\n        final ConfigurationFile tenantProperties = spy(new ConfigurationFile(COMPOUND_PERMISSIONS_MAPPING_FILE));\n        doReturn(compoundProperties).when(tenantProperties).getTenantPropertiesOfScope();\n\n        final String value = tenantProperties.getTenantProperty(\"unknownListingPage\");\n\n        Assert.assertNull(value);\n    }\n\n    @Test\n    public void should_getProperty_return_the_right_compound_permissions() {\n        final ConfigurationFile tenantProperties = spy(new ConfigurationFile(COMPOUND_PERMISSIONS_MAPPING_FILE));\n        doReturn(compoundProperties).when(tenantProperties).getTenantPropertiesOfScope();\n\n        final String compoundValue = tenantProperties.getTenantProperty(\"taskListingPage\");\n\n        assertEquals(\"[TaskVisualization, CaseVisualization]\", compoundValue);\n    }\n\n    @Test\n    public void should_getProperty_return_the_right_resource_permissions() {\n        final ConfigurationFile tenantProperties = spy(new ConfigurationFile(RESOURCES_PERMISSIONS_MAPPING_FILE));\n        doReturn(resourcesProperties).when(tenantProperties).getTenantPropertiesOfScope();\n\n        final String resourcesValue = tenantProperties.getTenantProperty(\"GET|bpm/identity\");\n\n        assertEquals(\"[UserVisualization, groupVisualization]\", resourcesValue);\n    }\n\n    @Test\n    public void should_getProperty_return_the_right_custom_permissions() {\n        final ConfigurationFile tenantProperties = spy(new ConfigurationFile(CUSTOM_PERMISSIONS_MAPPING_FILE));\n        doReturn(customProperties).when(tenantProperties).getTenantPropertiesOfScope();\n\n        final String customValue = tenantProperties.getTenantProperty(\"profile|User\");\n\n        assertEquals(\"[ManageLooknFeel, ManageProfiles]\", customValue);\n    }\n\n    @Test\n    public void should_getProperty_return_the_right_permissions_list() {\n        final ConfigurationFile tenantProperties = spy(new ConfigurationFile(COMPOUND_PERMISSIONS_MAPPING_FILE));\n        doReturn(compoundProperties).when(tenantProperties).getTenantPropertiesOfScope();\n\n        final Set<String> compoundPermissionsList = tenantProperties.getPropertyAsSet(\"taskListingPage\");\n\n        assertThat(compoundPermissionsList).containsOnly(\"TaskVisualization\", \"CaseVisualization\");\n    }\n\n    @Test\n    public void should_getProperty_return_the_right_permissions_list_with_single_value() {\n        final ConfigurationFile tenantProperties = spy(new ConfigurationFile(COMPOUND_PERMISSIONS_MAPPING_FILE));\n        doReturn(compoundProperties).when(tenantProperties).getTenantPropertiesOfScope();\n\n        final Set<String> compoundPermissionsList = tenantProperties.getPropertyAsSet(\"processListingPage\");\n\n        assertThat(compoundPermissionsList).containsOnly(\"processVisualization\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/preferences/properties/ConfigurationFilesManagerTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.preferences.properties;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.doReturn;\n\nimport java.util.Properties;\n\nimport org.bonitasoft.console.common.server.utils.PlatformManagementUtils;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Baptiste Mesta\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class ConfigurationFilesManagerTest {\n\n    @Mock\n    private PlatformManagementUtils platformManagementUtils;\n    @Spy\n    private ConfigurationFilesManager configurationFilesManager;\n\n    @Before\n    public void before() throws Exception {\n        doReturn(platformManagementUtils).when(configurationFilesManager).getPlatformManagementUtils();\n    }\n\n    @Test\n    public void getAlsoCustomAndInternalPropertiesFromFilename_should_merge_custom_properties_if_exist() {\n        // given:\n        final Properties defaultProps = new Properties();\n        defaultProps.put(\"defaultKey\", \"defaultValue\");\n        doReturn(defaultProps).when(configurationFilesManager).getTenantConfiguration(\"toto.properties\");\n        final Properties customProps = new Properties();\n        customProps.put(\"customKey\", \"customValue\");\n        doReturn(customProps).when(configurationFilesManager).getTenantConfiguration(\"toto-custom.properties\");\n\n        // when:\n        final Properties properties = configurationFilesManager\n                .getAlsoCustomAndInternalPropertiesFromFilename(\"toto.properties\");\n\n        // then:\n        assertThat(properties)\n                .containsEntry(\"defaultKey\", \"defaultValue\")\n                .containsEntry(\"customKey\", \"customValue\");\n    }\n\n    @Test\n    public void getAlsoCustomAndInternalPropertiesFromFilename_should_merge_internal_properties_if_exist() {\n        // given:\n        final Properties defaultProps = new Properties();\n        defaultProps.put(\"defaultKey\", \"defaultValue\");\n        doReturn(defaultProps).when(configurationFilesManager).getTenantConfiguration(\"toto.properties\");\n        final Properties internalProps = new Properties();\n        internalProps.put(\"internalKey\", \"internalValue\");\n        doReturn(internalProps).when(configurationFilesManager).getTenantConfiguration(\"toto-internal.properties\");\n\n        // when:\n        final Properties properties = configurationFilesManager\n                .getAlsoCustomAndInternalPropertiesFromFilename(\"toto.properties\");\n\n        // then:\n        assertThat(properties)\n                .containsEntry(\"defaultKey\", \"defaultValue\")\n                .containsEntry(\"internalKey\", \"internalValue\");\n    }\n\n    @Test\n    public void getAlsoCustomAndInternalPropertiesFromFilename_should_not_fail_if_base_file_does_not_exist() {\n        // when:\n        final Properties properties = configurationFilesManager\n                .getAlsoCustomAndInternalPropertiesFromFilename(\"non-existing.properties\");\n\n        // then:\n        assertThat(properties).isEmpty();\n    }\n\n    @Test\n    public void custom_properties_should_overwrite_internal_properties() {\n        // given:\n        final Properties defaultProps = new Properties();\n        defaultProps.put(\"defaultKey\", \"defaultValue\");\n        doReturn(defaultProps).when(configurationFilesManager).getTenantConfiguration(\"overwrite.properties\");\n\n        final Properties internalProps = new Properties();\n        internalProps.put(\"otherKey\", \"someInternallyManagedValue\");\n        doReturn(internalProps).when(configurationFilesManager).getTenantConfiguration(\"overwrite-internal.properties\");\n\n        final Properties customProps = new Properties();\n        final String expectedOverwrittenValue = \"custom_changed_value\";\n        customProps.put(\"otherKey\", expectedOverwrittenValue);\n        doReturn(customProps).when(configurationFilesManager).getTenantConfiguration(\"overwrite-custom.properties\");\n\n        // when:\n        final Properties properties = configurationFilesManager\n                .getAlsoCustomAndInternalPropertiesFromFilename(\"overwrite.properties\");\n\n        // then:\n        assertThat(properties).containsEntry(\"defaultKey\", \"defaultValue\").containsEntry(\"otherKey\",\n                expectedOverwrittenValue);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/preferences/properties/ConsolePropertiesTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.preferences.properties;\n\nimport static org.bonitasoft.console.common.server.preferences.properties.ConfigurationFilesManager.getProperties;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNull;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.spy;\n\nimport java.io.IOException;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Rohart Bastien\n */\npublic class ConsolePropertiesTest {\n\n    private ConsoleProperties properties;\n\n    @Before\n    public void setUp() throws IOException {\n        properties = spy(new ConsoleProperties());\n        doReturn(getProperties(\"aProperty=aValue\".getBytes())).when(properties).getProperties();\n    }\n\n    @Test\n    public void testGetNotExistingProperty() {\n        assertNull(\"Cannot get a not existing property\", properties.getProperty(\"test\"));\n    }\n\n    @Test\n    public void testWeCanRetrieveAProperty() {\n        String value = properties.getProperty(\"aProperty\");\n\n        assertEquals(\"Cannot retrieve a property\", value, \"aValue\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/preferences/properties/SecurityPropertiesTest.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.preferences.properties;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.console.common.server.preferences.properties.SecurityProperties.SANITIZER_PROTECTION;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.spy;\n\nimport org.junit.Test;\n\npublic class SecurityPropertiesTest {\n\n    private final SecurityProperties securityProperties = spy(new SecurityProperties());\n\n    @Test\n    public void invalid_or_absent_sanitizer_conf_should_be_enabled() {\n        doReturn(null).when(securityProperties).getPlatformProperty(SANITIZER_PROTECTION);\n        assertThat(securityProperties.isSanitizerProtectionEnabled()).isTrue();\n\n        doReturn(\"\").when(securityProperties).getPlatformProperty(SANITIZER_PROTECTION);\n        assertThat(securityProperties.isSanitizerProtectionEnabled()).isTrue();\n\n        doReturn(\"true\").when(securityProperties).getPlatformProperty(SANITIZER_PROTECTION);\n        assertThat(securityProperties.isSanitizerProtectionEnabled()).isTrue();\n    }\n\n    @Test\n    public void sanitizer_should_be_disabled_for_false_value_whatever_the_case() {\n        doReturn(\"false\").when(securityProperties).getPlatformProperty(SANITIZER_PROTECTION);\n        assertThat(securityProperties.isSanitizerProtectionEnabled()).isFalse();\n\n        doReturn(\"falSE\").when(securityProperties).getPlatformProperty(SANITIZER_PROTECTION);\n        assertThat(securityProperties.isSanitizerProtectionEnabled()).isFalse();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/servlet/ApplicationIconServletTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.servlet;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.*;\n\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.bonitasoft.engine.api.ApplicationAPI;\nimport org.bonitasoft.engine.business.application.ApplicationNotFoundException;\nimport org.bonitasoft.engine.business.application.ApplicationUpdater;\nimport org.bonitasoft.engine.business.application.impl.IconImpl;\nimport org.bonitasoft.engine.exception.NotFoundException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\n\n/**\n * @author Anthony Birembaut\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class ApplicationIconServletTest {\n\n    private static final long APPLICATION_ID = 1328970423L;\n    @Spy\n    private ApplicationIconServlet applicationIconServlet;\n    private MockHttpServletRequest httpServletRequest = new MockHttpServletRequest();\n    private MockHttpServletResponse httpServletResponse = new MockHttpServletResponse();\n    @Mock\n    private ApplicationAPI applicationAPI;\n\n    @Before\n    public void before() throws Exception {\n        doReturn(applicationAPI).when(applicationIconServlet).getApplicationApi(any());\n    }\n\n    private void havingIcon(long applicationId, byte[] content) throws NotFoundException {\n        doReturn(new IconImpl(\"mime-type\", content)).when(applicationAPI).getIconOfApplication(applicationId);\n    }\n\n    @Test\n    public void should_return_icon_content_when_valid_icon_id_is_given() throws Exception {\n        havingIcon(APPLICATION_ID, \"content\".getBytes());\n        httpServletRequest.setPathInfo(\"/\" + APPLICATION_ID);\n\n        applicationIconServlet.doGet(httpServletRequest, httpServletResponse);\n\n        assertThat(httpServletResponse.getContentAsByteArray()).isEqualTo(\"content\".getBytes());\n    }\n\n    @Test\n    public void should_set_special_headers_when_user_agent_is_firefox() throws Exception {\n        havingIcon(APPLICATION_ID, \"content\".getBytes());\n        httpServletRequest.addHeader(\"User-Agent\", \"Firefox-1238941\");\n        httpServletRequest.setPathInfo(\"/\" + APPLICATION_ID);\n\n        applicationIconServlet.doGet(httpServletRequest, httpServletResponse);\n\n        assertThat(httpServletResponse.getHeader(\"Content-Disposition\"))\n                .isEqualTo(\"inline; filename*=UTF-8''\" + APPLICATION_ID);\n    }\n\n    @Test\n    public void should_set_normal_headers_when_user_agent_is_not_firefox() throws Exception {\n        havingIcon(APPLICATION_ID, \"content\".getBytes());\n        httpServletRequest.addHeader(\"User-Agent\", \"Chrome-1238941\");\n        httpServletRequest.setPathInfo(\"/\" + APPLICATION_ID);\n\n        applicationIconServlet.doGet(httpServletRequest, httpServletResponse);\n\n        assertThat(httpServletResponse.getHeader(\"Content-Disposition\"))\n                .isEqualTo(\"inline; filename=\\\"\" + APPLICATION_ID + \"\\\"; filename*=UTF-8''\"\n                        + APPLICATION_ID);\n    }\n\n    @Test\n    public void should_status_be_BAD_REQUEST_when_id_is_missing_in_url() throws Exception {\n        httpServletRequest.setPathInfo(\"\");\n\n        applicationIconServlet.doGet(httpServletRequest, httpServletResponse);\n\n        assertThat(httpServletResponse.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);\n    }\n\n    @Test\n    public void should_status_be_BAD_REQUEST_when_id_is_not_a_long_in_url() throws Exception {\n        httpServletRequest.setPathInfo(\"notALong\");\n\n        applicationIconServlet.doGet(httpServletRequest, httpServletResponse);\n\n        assertThat(httpServletResponse.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);\n    }\n\n    @Test\n    public void should_status_be_BAD_REQUEST_when_null_is_set_as_id_in_url() throws Exception {\n        applicationIconServlet.doGet(httpServletRequest, httpServletResponse);\n\n        assertThat(httpServletResponse.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);\n    }\n\n    @Test\n    public void should_status_be_NOT_FOUND_when_application_does_not_exists_in_engine() throws Exception {\n        doThrow(ApplicationNotFoundException.class).when(applicationAPI).getIconOfApplication(APPLICATION_ID);\n        httpServletRequest.setPathInfo(\"/\" + APPLICATION_ID);\n\n        applicationIconServlet.doGet(httpServletRequest, httpServletResponse);\n\n        assertThat(httpServletResponse.getStatus()).isEqualTo(HttpServletResponse.SC_NOT_FOUND);\n    }\n\n    @Test\n    public void should_status_be_NOT_FOUND_when_icon_does_not_exists_in_engine_for_this_application() throws Exception {\n        doReturn(null).when(applicationAPI).getIconOfApplication(APPLICATION_ID);\n        httpServletRequest.setPathInfo(\"/\" + APPLICATION_ID);\n\n        applicationIconServlet.doGet(httpServletRequest, httpServletResponse);\n\n        assertThat(httpServletResponse.getStatus()).isEqualTo(HttpServletResponse.SC_NOT_FOUND);\n    }\n\n    @Test\n    public void should_content_type_be_set_to_what_is_return_from_the_engine() throws Exception {\n        doReturn(new IconImpl(\"theMimeTypeOfTheIcon\", \"content\".getBytes())).when(applicationAPI)\n                .getIconOfApplication(APPLICATION_ID);\n        httpServletRequest.setPathInfo(\"/\" + APPLICATION_ID);\n\n        applicationIconServlet.doGet(httpServletRequest, httpServletResponse);\n\n        assertThat(httpServletResponse.getContentType()).contains(\"theMimeTypeOfTheIcon\");\n    }\n\n    @Test\n    public void should_return_no_content_success_when_deleting_icon() throws Exception {\n        httpServletRequest.setPathInfo(\"/\" + APPLICATION_ID);\n        httpServletRequest.setMethod(\"DELETE\");\n        ApplicationUpdater updater = new ApplicationUpdater();\n        updater.setIcon(null, null);\n        ArgumentCaptor<ApplicationUpdater> captor = ArgumentCaptor.forClass(ApplicationUpdater.class);\n\n        applicationIconServlet.doDelete(httpServletRequest, httpServletResponse);\n\n        verify(applicationAPI, times(1)).updateApplication(eq(APPLICATION_ID), captor.capture());\n        assertEquals(updater.getFields(), captor.getValue().getFields());\n        assertThat(httpServletResponse.getStatus()).isEqualTo(HttpServletResponse.SC_NO_CONTENT);\n    }\n\n    @Test\n    public void should_return_not_found_when_deleting_icon_for_application_that_does_not_exist() throws Exception {\n        doThrow(new ApplicationNotFoundException(APPLICATION_ID)).when(applicationAPI)\n                .updateApplication(eq(APPLICATION_ID), any());\n        httpServletRequest.setPathInfo(\"/\" + APPLICATION_ID);\n        httpServletRequest.setMethod(\"DELETE\");\n\n        applicationIconServlet.doDelete(httpServletRequest, httpServletResponse);\n        assertThat(httpServletResponse.getStatus()).isEqualTo(HttpServletResponse.SC_NOT_FOUND);\n    }\n\n    @Test\n    public void should_return_bad_request_when_deleting_icon_for_application_with_generic_error() throws Exception {\n        doThrow(new UpdateException(\"Server crash\")).when(applicationAPI).updateApplication(eq(APPLICATION_ID), any());\n        httpServletRequest.setPathInfo(\"/\" + APPLICATION_ID);\n        httpServletRequest.setMethod(\"DELETE\");\n\n        applicationIconServlet.doDelete(httpServletRequest, httpServletResponse);\n        assertThat(httpServletResponse.getStatus()).isEqualTo(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/servlet/DocumentDownloadServletTest.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.servlet;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\nimport static org.mockito.ArgumentMatchers.anyLong;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.Mockito.when;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.bonitasoft.console.common.server.utils.BPMEngineAPIUtil;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.process.ProcessResourceNotFoundException;\nimport org.bonitasoft.engine.exception.RetrieveException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.test.util.ReflectionTestUtils;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class DocumentDownloadServletTest {\n\n    @Spy\n    private DocumentDownloadServlet servlet;\n\n    private MockHttpServletRequest request = new MockHttpServletRequest();\n    private MockHttpServletResponse response = new MockHttpServletResponse();\n\n    @Mock\n    private APISession apiSession;\n\n    @Mock\n    private BPMEngineAPIUtil bpmEngineAPIUtil;\n\n    @Mock\n    private ProcessAPI processAPI;\n\n    @Before\n    public void setUp() throws Exception {\n        request.getSession().setAttribute(\"apiSession\", apiSession);\n        ReflectionTestUtils.setField(servlet, \"bpmEngineAPIUtil\", bpmEngineAPIUtil);\n        when(bpmEngineAPIUtil.getProcessAPI(apiSession)).thenReturn(processAPI);\n    }\n\n    // Path traversal is rejected at the DB layer: the resource name is looked up by exact match,\n    // so traversal paths like \"../../../etc/passwd\" simply won't match any stored resource name.\n    @Test\n    public void doGet_should_return_404_for_path_traversal_in_resourceFileName() throws Exception {\n        request.setParameter(\"resourceFileName\", \"../../../etc/passwd\");\n        request.setParameter(\"process\", \"123\");\n        when(processAPI.getDocumentProcessResource(anyLong(), anyString()))\n                .thenThrow(\n                        new ProcessResourceNotFoundException(\"No resource named ../../../etc/passwd in process 123\"));\n\n        servlet.doGet(request, response);\n\n        assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_NOT_FOUND);\n    }\n\n    @Test\n    public void doGet_should_return_404_for_path_traversal_escaping_process_directory() throws Exception {\n        request.setParameter(\"resourceFileName\", \"../../secret.txt\");\n        request.setParameter(\"process\", \"123\");\n        when(processAPI.getDocumentProcessResource(anyLong(), anyString()))\n                .thenThrow(new ProcessResourceNotFoundException(\"No resource named ../../secret.txt in process 123\"));\n\n        servlet.doGet(request, response);\n\n        assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_NOT_FOUND);\n    }\n\n    @Test\n    public void doGet_should_return_400_when_no_process_instance_or_task_param() throws Exception {\n        request.setParameter(\"resourceFileName\", \"valid-resource.txt\");\n\n        servlet.doGet(request, response);\n\n        assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);\n    }\n\n    @Test\n    public void doGet_should_allow_valid_resource_path() throws Exception {\n        request.setParameter(\"resourceFileName\", \"valid-resource.txt\");\n        request.setParameter(\"process\", \"123\");\n        when(processAPI.getDocumentProcessResource(anyLong(), anyString()))\n                .thenReturn(\"test content\".getBytes());\n\n        servlet.doGet(request, response);\n\n        assertThat(response.getContentAsByteArray()).isEqualTo(\"test content\".getBytes());\n    }\n\n    @Test\n    public void doGet_should_wrap_RetrieveException_in_ServletException() throws Exception {\n        request.setParameter(\"resourceFileName\", \"valid-resource.txt\");\n        request.setParameter(\"process\", \"123\");\n        when(processAPI.getDocumentProcessResource(anyLong(), anyString()))\n                .thenThrow(new RetrieveException(\"DB error\"));\n\n        assertThatThrownBy(() -> servlet.doGet(request, response))\n                .isInstanceOf(ServletException.class)\n                .hasCauseInstanceOf(RetrieveException.class);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/servlet/DocumentImageServletTest.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.servlet;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\nimport static org.mockito.ArgumentMatchers.anyLong;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.Mockito.when;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.bonitasoft.console.common.server.utils.BPMEngineAPIUtil;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.process.ProcessResourceNotFoundException;\nimport org.bonitasoft.engine.exception.RetrieveException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.test.util.ReflectionTestUtils;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class DocumentImageServletTest {\n\n    @Spy\n    private DocumentImageServlet servlet;\n\n    private MockHttpServletRequest request = new MockHttpServletRequest();\n    private MockHttpServletResponse response = new MockHttpServletResponse();\n\n    @Mock\n    private APISession apiSession;\n\n    @Mock\n    private BPMEngineAPIUtil bpmEngineAPIUtil;\n\n    @Mock\n    private ProcessAPI processAPI;\n\n    @Before\n    public void setUp() throws Exception {\n        request.getSession().setAttribute(\"apiSession\", apiSession);\n        ReflectionTestUtils.setField(servlet, \"bpmEngineAPIUtil\", bpmEngineAPIUtil);\n        when(bpmEngineAPIUtil.getProcessAPI(apiSession)).thenReturn(processAPI);\n    }\n\n    // Path traversal is rejected at the DB layer: the resource name is looked up by exact match,\n    // so traversal paths like \"../../../etc/passwd\" simply won't match any stored resource name.\n    @Test\n    public void doGet_should_return_404_for_path_traversal_in_resourceFileName() throws Exception {\n        request.setParameter(\"resourceFileName\", \"../../../etc/passwd\");\n        request.setParameter(\"process\", \"123\");\n        when(processAPI.getDocumentProcessResource(anyLong(), anyString()))\n                .thenThrow(\n                        new ProcessResourceNotFoundException(\"No resource named ../../../etc/passwd in process 123\"));\n\n        servlet.doGet(request, response);\n\n        assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_NOT_FOUND);\n    }\n\n    @Test\n    public void doGet_should_return_404_for_path_traversal_escaping_process_directory() throws Exception {\n        request.setParameter(\"resourceFileName\", \"../../secret.txt\");\n        request.setParameter(\"process\", \"123\");\n        when(processAPI.getDocumentProcessResource(anyLong(), anyString()))\n                .thenThrow(new ProcessResourceNotFoundException(\"No resource named ../../secret.txt in process 123\"));\n\n        servlet.doGet(request, response);\n\n        assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_NOT_FOUND);\n    }\n\n    @Test\n    public void doGet_should_return_400_when_no_process_instance_or_task_param() throws Exception {\n        request.setParameter(\"resourceFileName\", \"valid-image.png\");\n\n        servlet.doGet(request, response);\n\n        assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);\n    }\n\n    @Test\n    public void doGet_should_allow_valid_resource_path() throws Exception {\n        request.setParameter(\"resourceFileName\", \"valid-image.png\");\n        request.setParameter(\"process\", \"123\");\n        when(processAPI.getDocumentProcessResource(anyLong(), anyString()))\n                .thenReturn(\"fake-image-content\".getBytes());\n\n        servlet.doGet(request, response);\n\n        assertThat(response.getContentAsByteArray()).isEqualTo(\"fake-image-content\".getBytes());\n    }\n\n    @Test\n    public void doGet_should_wrap_RetrieveException_in_ServletException() throws Exception {\n        request.setParameter(\"resourceFileName\", \"valid-image.png\");\n        request.setParameter(\"process\", \"123\");\n        when(processAPI.getDocumentProcessResource(anyLong(), anyString()))\n                .thenThrow(new RetrieveException(\"DB error\"));\n\n        assertThatThrownBy(() -> servlet.doGet(request, response))\n                .isInstanceOf(ServletException.class)\n                .hasCauseInstanceOf(RetrieveException.class);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/servlet/ErrorPageServletTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.servlet;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.InputStream;\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\nimport java.nio.file.Paths;\n\nimport javax.servlet.RequestDispatcher;\nimport javax.servlet.ServletContext;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.engine.session.APISession;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ErrorPageServletTest {\n\n    @Mock\n    HttpServletRequest request;\n\n    @Mock\n    HttpServletResponse response;\n\n    @Mock\n    ServletContext sc;\n\n    @Mock\n    RequestDispatcher requestDispatcher;\n\n    @Mock\n    HttpSession session;\n\n    @Mock\n    APISession apiSession;\n\n    @Spy\n    ErrorPageServlet errorServlet = new ErrorPageServlet();\n\n    StringWriter stringWriter;\n\n    @Before\n    public void setUp() throws Exception {\n        when(request.getSession()).thenReturn(session);\n        when(session.getAttribute(\"apiSession\")).thenReturn(apiSession);\n        doReturn(true).when(errorServlet).isPlatformHealthy();\n        stringWriter = new StringWriter();\n        PrintWriter printWriter = new PrintWriter(stringWriter);\n        when(response.getWriter()).thenReturn(printWriter);\n        File errorsHTMLFilePaths = Paths.get(\"src/main/webapp/WEB-INF/errors.html\").toFile();\n        InputStream errorPageInputStream = new FileInputStream(errorsHTMLFilePaths);\n        when(sc.getResourceAsStream(ErrorPageServlet.ERROR_TEMPLATE_PATH)).thenReturn(errorPageInputStream);\n        when(sc.getRequestDispatcher(anyString())).thenReturn(requestDispatcher);\n        doReturn(sc).when(errorServlet).getServletContext();\n    }\n\n    @Test\n    public void should_write_formatted_response() throws Exception {\n\n        when(request.getPathInfo()).thenReturn(\"/404\");\n        when(request.getContextPath()).thenReturn(\"/bonita\");\n\n        errorServlet.doGet(request, response);\n\n        assertThat(stringWriter.toString()).contains(\"Error 404\");\n        assertThat(stringWriter.toString()).contains(\n                \"src=\\\"/bonita/portal/resource/app/appDirectoryBonita/error-404/content/?app=appDirectoryBonita\\\"\");\n        assertThat(stringWriter.toString()).contains(\"width=\\\"100%\\\"\");\n    }\n\n    @Test\n    public void should_write_formatted_response_with_root_contextPath() throws Exception {\n\n        when(request.getPathInfo()).thenReturn(\"/500\");\n        when(request.getContextPath()).thenReturn(\"/\");\n\n        errorServlet.doGet(request, response);\n\n        assertThat(stringWriter.toString()).contains(\n                \"src=\\\"/portal/resource/app/appDirectoryBonita/error-500/content/?app=appDirectoryBonita\\\"\");\n    }\n\n    @Test\n    public void should_display_error_when_error_code_is_missing() throws Exception {\n\n        when(request.getPathInfo()).thenReturn(\"\");\n\n        errorServlet.doGet(request, response);\n\n        assertThat(stringWriter.toString()).contains(\"Status code missing from request.\");\n    }\n\n    @Test\n    public void should_forward_request_when_session_is_missing() throws Exception {\n\n        when(request.getPathInfo()).thenReturn(\"/404\");\n        when(session.getAttribute(\"apiSession\")).thenReturn(null);\n\n        errorServlet.doGet(request, response);\n\n        verify(sc, times(1)).getRequestDispatcher(\"/404.jsp\");\n    }\n\n    @Test\n    public void should_forward_request_when_session_is_missing_forbidden_access() throws Exception {\n\n        when(request.getPathInfo()).thenReturn(\"/403\");\n        when(session.getAttribute(\"apiSession\")).thenReturn(null);\n\n        errorServlet.doGet(request, response);\n\n        verify(sc, times(1)).getRequestDispatcher(\"/403.jsp\");\n    }\n\n    @Test\n    public void should_forward_request_when_platform_is_down() throws Exception {\n\n        when(request.getPathInfo()).thenReturn(\"/500\");\n        doReturn(false).when(errorServlet).isPlatformHealthy();\n\n        errorServlet.doGet(request, response);\n\n        verify(sc, times(1)).getRequestDispatcher(\"/500.jsp\");\n    }\n\n    @Test\n    public void should_reject_non_numeric_error_code() throws Exception {\n        when(request.getPathInfo()).thenReturn(\"/../WEB-INF/web.xml\");\n\n        errorServlet.doGet(request, response);\n\n        verify(response).sendError(HttpServletResponse.SC_BAD_REQUEST);\n        verify(sc, never()).getRequestDispatcher(anyString());\n    }\n\n    @Test\n    public void should_reject_semicolon_traversal_in_error_code() throws Exception {\n        when(request.getPathInfo()).thenReturn(\"/..;/WEB-INF/web.xml\");\n\n        errorServlet.doGet(request, response);\n\n        verify(response).sendError(HttpServletResponse.SC_BAD_REQUEST);\n        verify(sc, never()).getRequestDispatcher(anyString());\n    }\n\n    @Test\n    public void should_reject_error_code_with_extra_characters() throws Exception {\n        when(request.getPathInfo()).thenReturn(\"/404abc\");\n\n        errorServlet.doGet(request, response);\n\n        verify(response).sendError(HttpServletResponse.SC_BAD_REQUEST);\n        verify(sc, never()).getRequestDispatcher(anyString());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/servlet/FileUploadServletTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.servlet;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.Mockito.*;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.PrintWriter;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipOutputStream;\n\nimport javax.servlet.ServletConfig;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.apache.commons.fileupload.FileItem;\nimport org.apache.commons.fileupload.FileItemFactory;\nimport org.apache.commons.fileupload.servlet.ServletFileUpload;\nimport org.bonitasoft.engine.api.TemporaryContentAPI;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentMatcher;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class FileUploadServletTest {\n\n    @Mock\n    HttpServletRequest request;\n\n    @Spy\n    FileUploadServlet fileUploadServlet = new TenantFileUploadServlet();\n\n    @Test\n    public void generateResponseJson_should_return_valid_json() throws Exception {\n        final File uploadedFile = mock(File.class);\n        when(uploadedFile.getName()).thenReturn(\"uploadedFile.txt\");\n        when(fileUploadServlet.getServletConfig()).thenReturn(mock(ServletConfig.class));\n        doReturn(mock(TemporaryContentAPI.class)).when(fileUploadServlet).getTemporaryContentAPI();\n        when(fileUploadServlet.getInitParameter(FileUploadServlet.RETURN_ORIGINAL_FILENAME_PARAM)).thenReturn(\"true\");\n        fileUploadServlet.init();\n\n        final String jsonResponse = fileUploadServlet.generateResponseJson(request, \"originalFileName\",\n                \"application/json\",\n                uploadedFile.getName(),\n                false);\n        ObjectMapper mapper = new ObjectMapper();\n        @SuppressWarnings(\"unchecked\")\n        Map<String, String> jsonResponseMap = mapper.readValue(jsonResponse, Map.class);\n\n        assertThat(jsonResponseMap).containsEntry(FileUploadServlet.FILE_NAME_RESPONSE_ATTRIBUTE, \"originalFileName\")\n                .containsEntry(FileUploadServlet.TEMP_PATH_RESPONSE_ATTRIBUTE, \"uploadedFile.txt\")\n                .containsEntry(FileUploadServlet.CONTENT_TYPE_ATTRIBUTE, \"application/json\");\n    }\n\n    @Test\n    public void generateResponseString_should_return_valid_text() throws Exception {\n        final File uploadedFile = mock(File.class);\n        when(uploadedFile.getName()).thenReturn(\"uploadedFile.txt\");\n        when(fileUploadServlet.getServletConfig()).thenReturn(mock(ServletConfig.class));\n        doReturn(mock(TemporaryContentAPI.class)).when(fileUploadServlet).getTemporaryContentAPI();\n        when(fileUploadServlet.getInitParameter(FileUploadServlet.RETURN_ORIGINAL_FILENAME_PARAM)).thenReturn(\"true\");\n        fileUploadServlet.init();\n\n        final String responseString = fileUploadServlet.generateResponseString(request, \"originalFileName\",\n                uploadedFile.getName());\n\n        assertThat(responseString).isEqualTo(\"uploadedFile.txt::originalFileName\");\n    }\n\n    @Test\n    public void getFilenameLastSegment_should_return_proper_filename() {\n        // given\n        final String filename = \"C:\\\\Users\\\\Desktop\\\\process.bar\";\n\n        // when\n        final String filenameLastSegment = fileUploadServlet.getFilenameLastSegment(filename);\n\n        // then\n        assertThat(filenameLastSegment).isEqualTo(\"process.bar\");\n    }\n\n    @Test\n    public void getFilenameLastSegment_should_return_proper_filename_for_linux_paths() {\n        // given\n        final String filename = \"/Users/Deskt.op/process.bar\";\n\n        // when\n        final String filenameLastSegment = fileUploadServlet.getFilenameLastSegment(filename);\n\n        // then\n        assertThat(filenameLastSegment).isEqualTo(\"process.bar\");\n    }\n\n    @Test\n    public void getFilenameLastSegment_should_return_an_empty_filename_for_parent_folder_filename() {\n        // given\n        final String filename = \"../../../\";\n\n        // when\n        final String filenameLastSegment = fileUploadServlet.getFilenameLastSegment(filename);\n\n        // then\n        assertThat(filenameLastSegment).isEmpty();\n    }\n\n    @Test\n    public void doPost_should_return_jarlessBar_indicator() throws Exception {\n        // given\n        // a bar zip file with a .jarless file inside\n        final File barFile = File.createTempFile(\"app\", \".bar\");\n        try (FileOutputStream fos = new FileOutputStream(barFile); ZipOutputStream zos = new ZipOutputStream(fos)) {\n            ZipEntry jarlessEntry = new ZipEntry(\".jarless\");\n            zos.putNextEntry(jarlessEntry);\n            zos.closeEntry();\n        }\n\n        final FileItem fileItem = mock(FileItem.class);\n        final ServletFileUpload serviceFileUpload = mock(ServletFileUpload.class);\n        final HttpServletResponse response = mock(HttpServletResponse.class);\n        final PrintWriter printer = mock(PrintWriter.class);\n        final File tempFolder = File.createTempFile(\"upload\", \"\");\n        tempFolder.mkdir();\n\n        //manage spy\n        fileUploadServlet.uploadDirectoryPath = tempFolder.getAbsolutePath();\n        doNothing().when(fileUploadServlet).defineUploadDirectoryPath(request);\n        doReturn(serviceFileUpload).when(fileUploadServlet).createServletFileUpload(any(FileItemFactory.class));\n        TemporaryContentAPI tempContentApi = mock(TemporaryContentAPI.class);\n        doReturn(\"key\").when(fileUploadServlet).storeTempFile(anyString(), any());\n        doReturn(tempContentApi).when(fileUploadServlet).getTemporaryContentAPI();\n\n        when(fileItem.getInputStream()).then(invoc -> new FileInputStream(barFile));\n        when(fileItem.getName()).then(invoc -> barFile.getName());\n        when(serviceFileUpload.parseRequest(request)).thenReturn(List.of(fileItem));\n        when(request.getMethod()).thenReturn(\"post\");\n        when(request.getContentType()).thenReturn(\"multipart/\");\n        when(response.getWriter()).thenReturn(printer);\n\n        when(fileUploadServlet.getServletConfig()).thenReturn(mock(ServletConfig.class));\n        when(fileUploadServlet.getInitParameter(FileUploadServlet.RESPONSE_CONTENT_TYPE_PARAM))\n                .thenReturn(FileUploadServlet.JSON_CONTENT_TYPE);\n        when(fileUploadServlet.getInitParameter(FileUploadServlet.SUPPORTED_EXTENSIONS_PARAM))\n                .thenReturn(\"bar\");\n\n        // when\n        fileUploadServlet.init();\n        fileUploadServlet.doPost(request, response);\n\n        // then\n        verify(response, never()).setStatus(anyInt());\n        ArgumentMatcher<String> isStringWithJarless = s -> s\n                .contains(String.format(\"\\\"%s\\\":true\", FileUploadServlet.JARLESS_BAR_ATTRIBUTE));\n        verify(printer).print(argThat(isStringWithJarless));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/servlet/OrganizationIconServletTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.servlet;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.*;\n\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.bonitasoft.engine.api.IdentityAPI;\nimport org.bonitasoft.engine.exception.NotFoundException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.identity.UserNotFoundException;\nimport org.bonitasoft.engine.identity.UserUpdater;\nimport org.bonitasoft.engine.identity.impl.IconImpl;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\n\n/**\n * @author Baptiste Mesta\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class OrganizationIconServletTest {\n\n    private static final long ICON_ID = 1238970432L;\n    private static final long USER_ID = 8211558366L;\n    @Spy\n    private OrganizationIconServlet organizationIconServlet;\n    private MockHttpServletRequest httpServletRequest = new MockHttpServletRequest();\n    private MockHttpServletResponse httpServletResponse = new MockHttpServletResponse();\n    @Mock\n    private IdentityAPI identityAPI;\n\n    @Before\n    public void before() throws Exception {\n        doReturn(identityAPI).when(organizationIconServlet).getIdentityApi(any());\n    }\n\n    private void havingIcon(long iconId, byte[] content) throws NotFoundException {\n        doReturn(new IconImpl(iconId, \"mime-type\", content)).when(identityAPI).getIcon(iconId);\n    }\n\n    @Test\n    public void should_return_icon_content_when_valid_icon_id_is_given() throws Exception {\n        havingIcon(ICON_ID, \"content\".getBytes());\n        httpServletRequest.setPathInfo(\"/\" + ICON_ID);\n\n        organizationIconServlet.doGet(httpServletRequest, httpServletResponse);\n\n        assertThat(httpServletResponse.getContentAsByteArray()).isEqualTo(\"content\".getBytes());\n    }\n\n    @Test\n    public void should_set_special_headers_when_user_agent_is_firefox() throws Exception {\n        havingIcon(ICON_ID, \"content\".getBytes());\n        httpServletRequest.addHeader(\"User-Agent\", \"Firefox-1238941\");\n        httpServletRequest.setPathInfo(\"/\" + ICON_ID);\n\n        organizationIconServlet.doGet(httpServletRequest, httpServletResponse);\n\n        assertThat(httpServletResponse.getHeader(\"Content-Disposition\"))\n                .isEqualTo(\"inline; filename*=UTF-8''\" + ICON_ID);\n    }\n\n    @Test\n    public void should_set_normal_headers_when_user_agent_is_not_firefox() throws Exception {\n        havingIcon(ICON_ID, \"content\".getBytes());\n        httpServletRequest.addHeader(\"User-Agent\", \"Chrome-1238941\");\n        httpServletRequest.setPathInfo(\"/\" + ICON_ID);\n\n        organizationIconServlet.doGet(httpServletRequest, httpServletResponse);\n\n        assertThat(httpServletResponse.getHeader(\"Content-Disposition\"))\n                .isEqualTo(\"inline; filename=\\\"\" + ICON_ID + \"\\\"; filename*=UTF-8''\"\n                        + ICON_ID);\n    }\n\n    @Test\n    public void should_status_be_BAD_REQUEST_when_id_is_missing_in_url() throws Exception {\n        httpServletRequest.setPathInfo(\"\");\n\n        organizationIconServlet.doGet(httpServletRequest, httpServletResponse);\n\n        assertThat(httpServletResponse.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);\n    }\n\n    @Test\n    public void should_status_be_BAD_REQUEST_when_id_is_not_a_long_in_url() throws Exception {\n        httpServletRequest.setPathInfo(\"notALong\");\n\n        organizationIconServlet.doGet(httpServletRequest, httpServletResponse);\n\n        assertThat(httpServletResponse.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);\n    }\n\n    @Test\n    public void should_status_be_BAD_REQUEST_when_null_is_set_as_id_in_url() throws Exception {\n        organizationIconServlet.doGet(httpServletRequest, httpServletResponse);\n\n        assertThat(httpServletResponse.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);\n    }\n\n    @Test\n    public void should_status_be_NOT_FOUND_when_icon_does_not_exists_in_engine() throws Exception {\n        doThrow(NotFoundException.class).when(identityAPI).getIcon(ICON_ID);\n        httpServletRequest.setPathInfo(\"/\" + ICON_ID);\n\n        organizationIconServlet.doGet(httpServletRequest, httpServletResponse);\n\n        assertThat(httpServletResponse.getStatus()).isEqualTo(HttpServletResponse.SC_NOT_FOUND);\n    }\n\n    @Test\n    public void should_content_type_be_set_to_what_is_return_from_the_engine() throws Exception {\n        doReturn(new IconImpl(ICON_ID, \"theMimeTypeOfTheIcon\", \"content\".getBytes())).when(identityAPI)\n                .getIcon(ICON_ID);\n        httpServletRequest.setPathInfo(\"/\" + ICON_ID);\n\n        organizationIconServlet.doGet(httpServletRequest, httpServletResponse);\n\n        assertThat(httpServletResponse.getContentType()).contains(\"theMimeTypeOfTheIcon\");\n    }\n\n    @Test\n    public void should_return_no_content_success_when_deleting_icon() throws Exception {\n        httpServletRequest.setPathInfo(\"/\" + USER_ID);\n        httpServletRequest.setParameter(\"type\", \"user\");\n        httpServletRequest.setMethod(\"DELETE\");\n        UserUpdater updater = new UserUpdater();\n        updater.setIcon(null, null);\n        ArgumentCaptor<UserUpdater> captor = ArgumentCaptor.forClass(UserUpdater.class);\n\n        organizationIconServlet.doDelete(httpServletRequest, httpServletResponse);\n\n        verify(identityAPI, times(1)).updateUser(eq(USER_ID), captor.capture());\n        assertEquals(updater.getFields(), captor.getValue().getFields());\n        assertThat(httpServletResponse.getStatus()).isEqualTo(HttpServletResponse.SC_NO_CONTENT);\n    }\n\n    @Test\n    public void should_return_API_Malformed_Exception_when_type_is_not_defined_in_url() throws Exception {\n        httpServletRequest.setPathInfo(\"/\" + USER_ID);\n        httpServletRequest.setMethod(\"DELETE\");\n\n        organizationIconServlet.doDelete(httpServletRequest, httpServletResponse);\n\n        assertThat(httpServletResponse.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);\n    }\n\n    @Test\n    public void should_return_API_Malformed_Exception_when_type_is_incorrect_in_url() throws Exception {\n        httpServletRequest.setPathInfo(\"/\" + USER_ID);\n        httpServletRequest.setMethod(\"DELETE\");\n        httpServletRequest.setParameter(\"type\", \"group\");\n\n        organizationIconServlet.doDelete(httpServletRequest, httpServletResponse);\n        assertThat(httpServletResponse.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);\n    }\n\n    @Test\n    public void should_return_not_found_when_deleting_icon_for_user_that_does_not_exist() throws Exception {\n        doThrow(new UserNotFoundException(\"User not found\")).when(identityAPI).updateUser(eq(USER_ID), any());\n        httpServletRequest.setPathInfo(\"/\" + USER_ID);\n        httpServletRequest.setParameter(\"type\", \"user\");\n        httpServletRequest.setMethod(\"DELETE\");\n\n        organizationIconServlet.doDelete(httpServletRequest, httpServletResponse);\n        assertThat(httpServletResponse.getStatus()).isEqualTo(HttpServletResponse.SC_NOT_FOUND);\n    }\n\n    @Test\n    public void should_return_bad_request_when_deleting_icon_for_user_with_generic_error() throws Exception {\n        doThrow(new UpdateException(\"Server crash\")).when(identityAPI).updateUser(eq(USER_ID), any());\n        httpServletRequest.setPathInfo(\"/\" + USER_ID);\n        httpServletRequest.setParameter(\"type\", \"user\");\n        httpServletRequest.setMethod(\"DELETE\");\n\n        organizationIconServlet.doDelete(httpServletRequest, httpServletResponse);\n        assertThat(httpServletResponse.getStatus()).isEqualTo(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/servlet/PageUploadServletTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.servlet;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.doReturn;\n\nimport java.io.File;\nimport java.io.Serializable;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.ObjectReader;\nimport org.bonitasoft.engine.session.APISession;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class PageUploadServletTest {\n\n    @Mock\n    HttpServletRequest request;\n\n    @Mock\n    APISession apiSession;\n\n    @Spy\n    PageUploadServlet pageUploadServlet = new PageUploadServlet();\n\n    @Before\n    public void setUp() throws Exception {\n        pageUploadServlet.pageTmp = new File(getClass().getResource(\"/pageWithPermissions.zip\").toURI());\n\n    }\n\n    @Test\n    public void should_getPermissions_work_with_valid_zip() throws Exception {\n        doReturn(\"edit\").when(request).getParameter(\"action\");\n        final Set<String> permissionsSet = new HashSet<>();\n        permissionsSet.add(\"Organisation visualization\");\n        permissionsSet.add(\"Organisation management\");\n        doReturn(permissionsSet).when(pageUploadServlet).getPagePermissions(request, false);\n\n        final String[] permissions = pageUploadServlet.getPermissions(request);\n\n        assertThat(permissions).containsExactlyInAnyOrder(\"Organisation visualization\", \"Organisation management\");\n    }\n\n    @Test\n    public void should_getPermissions_work_with_invalid_zip() throws Exception {\n        doReturn(\"add\").when(request).getParameter(\"action\");\n        final Set<String> permissionsSet = new HashSet<>();\n        doReturn(permissionsSet).when(pageUploadServlet).getPagePermissions(request, true);\n\n        final String[] permissions = pageUploadServlet.getPermissions(request);\n\n        assertThat(permissions).isNotNull().isEmpty();\n    }\n\n    @Test\n    public void should_generateResponseString_work_with_permissions() throws Exception {\n        final String[] permissions = new String[] { \"Organisation visualization\", \"Organisation management\" };\n        doReturn(permissions).when(pageUploadServlet).getPermissions(request);\n\n        final String responseString = pageUploadServlet.generateResponseString(request, \"fileName\",\n                pageUploadServlet.pageTmp.getName());\n\n        assertThat(responseString)\n                .isEqualTo(\n                        pageUploadServlet.pageTmp.getName() + \"::[Organisation visualization,Organisation management]\");\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Test\n    public void should_generateResponseJson_work_with_permissions() throws Exception {\n        final String[] permissions = new String[] { \"Organisation visualization\", \"Organisation management\" };\n        doReturn(permissions).when(pageUploadServlet).getPermissions(request);\n\n        final String responseString = pageUploadServlet.generateResponseJson(request, \"fileName\", \"application/zip\",\n                pageUploadServlet.pageTmp.getName(), false);\n\n        ObjectReader reader = new ObjectMapper().readerFor(Map.class);\n        Map<String, Serializable> responseMap = reader.readValue(responseString);\n\n        //jackson deserializes array to list by default\n        assertThat((List<String>) responseMap.get(PageUploadServlet.PERMISSIONS_RESPONSE_ATTRIBUTE))\n                .containsExactlyInAnyOrder(\"Organisation visualization\", \"Organisation management\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/servlet/ResourceLocationReaderTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.servlet;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ResourceLocationReaderTest {\n\n    @Mock\n    private HttpServletRequest req;\n\n    @Spy\n    private final ResourceLocationReader resourceLocationReader = new ResourceLocationReader();\n\n    @Test\n    public void should_use_infopath_if_location_param_is_not_defined() {\n\n        when(req.getParameter(\"location\")).thenReturn(null);\n        when(req.getPathInfo()).thenReturn(\"/bonita.css\");\n\n        final String resourceLocation = resourceLocationReader.getResourceLocationFromRequest(req);\n\n        verify(req, times(1)).getPathInfo();\n        assertThat(resourceLocation).isEqualTo(\"bonita.css\");\n    }\n\n    @Test\n    public void should_not_use_infopath_if_location_param_is_defined() throws Exception {\n\n        when(req.getParameter(\"location\")).thenReturn(\"bonita.css\");\n\n        final String resourceLocation = resourceLocationReader.getResourceLocationFromRequest(req);\n\n        verify(req, never()).getPathInfo();\n        assertThat(resourceLocation).isEqualTo(\"bonita.css\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/servlet/TenantFileUploadServletTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.servlet;\n\nimport static java.lang.String.format;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.Mockito.*;\n\nimport java.io.PrintWriter;\nimport java.net.HttpURLConnection;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.apache.commons.fileupload.FileItemFactory;\nimport org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException;\nimport org.apache.commons.fileupload.servlet.ServletFileUpload;\nimport org.bonitasoft.console.common.server.preferences.properties.ConsoleProperties;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.TemporaryFolder;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class TenantFileUploadServletTest {\n\n    @Rule\n    public TemporaryFolder tempFolder = new TemporaryFolder();\n\n    @Spy\n    protected TenantFileUploadServlet fileUploadServlet;\n    private ConsoleProperties consoleProperties;\n    private HttpServletRequest request;\n\n    @Before\n    public void setUp() throws Exception {\n        request = mock(HttpServletRequest.class);\n        consoleProperties = mock(ConsoleProperties.class);\n\n        doReturn(consoleProperties).when(fileUploadServlet).getConsoleProperties();\n        fileUploadServlet.checkUploadedFileSize = false;\n        fileUploadServlet.checkUploadedImageSize = false;\n    }\n\n    @Test\n    public void should_set_maxFileSize_with_conf_value() throws Exception {\n        final ServletFileUpload serviceFileUpload = mock(ServletFileUpload.class);\n        fileUploadServlet.checkUploadedFileSize = true;\n\n        when(consoleProperties.getMaxSize()).thenReturn(1L); // 1Mb\n        fileUploadServlet.setUploadMaxSize(serviceFileUpload, request);\n        verify(serviceFileUpload).setFileSizeMax(FileUploadServlet.MEGABYTE);\n    }\n\n    @Test\n    public void should_set_maxImageSize_with_conf_value() throws Exception {\n        final ServletFileUpload serviceFileUpload = mock(ServletFileUpload.class);\n        fileUploadServlet.checkUploadedImageSize = true;\n\n        when(consoleProperties.getImageMaxSizeInKB()).thenReturn(100L); // 1Mb\n        fileUploadServlet.setUploadMaxSize(serviceFileUpload, request);\n        verify(serviceFileUpload).setFileSizeMax(100 * FileUploadServlet.KILOBYTE);\n    }\n\n    @Test\n    public void should_set_413_status_code_with_empty_body_when_file_creates_OOMError() throws Exception {\n        final ServletFileUpload serviceFileUpload = mock(ServletFileUpload.class);\n        final HttpServletResponse response = mock(HttpServletResponse.class);\n        final PrintWriter printer = mock(PrintWriter.class);\n\n        //manage spy\n        fileUploadServlet.uploadDirectoryPath = tempFolder.getRoot().getAbsolutePath();\n        fileUploadServlet.checkUploadedFileSize = true;\n        doNothing().when(fileUploadServlet).defineUploadDirectoryPath(request);\n        doReturn(serviceFileUpload).when(fileUploadServlet).createServletFileUpload(any(FileItemFactory.class));\n\n        when(serviceFileUpload.parseRequest(request)).thenThrow(new OutOfMemoryError());\n        when(request.getMethod()).thenReturn(\"post\");\n        when(request.getContentType()).thenReturn(\"multipart/\");\n        when(response.getWriter()).thenReturn(printer);\n\n        fileUploadServlet.doPost(request, response);\n\n        verify(response).setStatus(HttpURLConnection.HTTP_ENTITY_TOO_LARGE);\n        verify(printer, never()).print(anyString());\n        verify(printer, never()).flush();\n    }\n\n    @Test\n    public void should_set_413_status_code_with_empty_body_when_file_is_too_big() throws Exception {\n        final ServletFileUpload serviceFileUpload = mock(ServletFileUpload.class);\n        final HttpServletResponse response = mock(HttpServletResponse.class);\n        final PrintWriter printer = mock(PrintWriter.class);\n\n        //manage spy\n        fileUploadServlet.uploadDirectoryPath = tempFolder.getRoot().getAbsolutePath();\n        doNothing().when(fileUploadServlet).defineUploadDirectoryPath(request);\n        doReturn(serviceFileUpload).when(fileUploadServlet).createServletFileUpload(any(FileItemFactory.class));\n\n        final FileSizeLimitExceededException exception = new FileSizeLimitExceededException(\n                format(\"The field %s exceeds its maximum permitted size of %s bytes.\",\n                        \"uploadedFile.zip\", Long.valueOf(0)),\n                20 * FileUploadServlet.MEGABYTE, 0);\n        exception.setFileName(\"uploadedFile.zip\");\n        when(serviceFileUpload.parseRequest(request)).thenThrow(exception);\n        when(request.getMethod()).thenReturn(\"post\");\n        when(request.getContentType()).thenReturn(\"multipart/\");\n        when(response.getWriter()).thenReturn(printer);\n\n        fileUploadServlet.doPost(request, response);\n\n        verify(response).setStatus(HttpURLConnection.HTTP_ENTITY_TOO_LARGE);\n        verify(printer, never()).print(anyString());\n        verify(printer, never()).flush();\n    }\n\n    @Test\n    public void should_set_413_status_code_with_json_body_when_file_is_too_big_and_json_is_supported()\n            throws Exception {\n        final ServletFileUpload serviceFileUpload = mock(ServletFileUpload.class);\n        final HttpServletResponse response = mock(HttpServletResponse.class);\n        final PrintWriter printer = mock(PrintWriter.class);\n\n        //manage spy\n        fileUploadServlet.uploadDirectoryPath = tempFolder.getRoot().getAbsolutePath();\n        fileUploadServlet.checkUploadedFileSize = true;\n        fileUploadServlet.responseContentType = \"json\";\n        doNothing().when(fileUploadServlet).defineUploadDirectoryPath(request);\n        doReturn(serviceFileUpload).when(fileUploadServlet).createServletFileUpload(any(FileItemFactory.class));\n\n        final FileSizeLimitExceededException exception = new FileSizeLimitExceededException(\n                format(\"The field %s exceeds its maximum permitted size of %s bytes.\",\n                        \"uploadedFile.zip\", Long.valueOf(0)),\n                20 * FileUploadServlet.MEGABYTE, 0);\n        exception.setFileName(\"uploadedFile.zip\");\n        when(serviceFileUpload.parseRequest(request)).thenThrow(exception);\n        when(request.getMethod()).thenReturn(\"post\");\n        when(request.getContentType()).thenReturn(\"multipart/\");\n        when(response.getWriter()).thenReturn(printer);\n\n        fileUploadServlet.doPost(request, response);\n\n        verify(response).setStatus(HttpURLConnection.HTTP_ENTITY_TOO_LARGE);\n        final ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);\n        verify(printer).print(captor.capture());\n        assertThat(captor.getValue())\n                .contains(\"\\\"statusCode\\\":413\")\n                .contains(\"\\\"message\\\":\\\"uploadedFile.zip is 20971520 large, limit is set to 0Mb\\\"\")\n                .contains(\"\\\"type\\\":\\\"EntityTooLarge\\\"\");\n        verify(printer).flush();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/utils/BonitaHomeFolderAccessorTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.utils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.doReturn;\n\nimport java.io.File;\nimport java.io.InputStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.UUID;\n\nimport org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils;\nimport org.bonitasoft.engine.io.FileContent;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class BonitaHomeFolderAccessorTest {\n\n    @Mock\n    private WebBonitaConstantsUtils webBonitaConstantsUtils;\n\n    @Mock\n    private FileContent fileContent;\n\n    @Spy\n    private final BonitaHomeFolderAccessor tenantFolder = new BonitaHomeFolderAccessor();\n\n    @Test\n    public void should_authorized_a_file_in_temp_folder() throws Exception {\n        given(webBonitaConstantsUtils.getTempFolder()).willReturn(new File(\".\" + File.separator + \"tempFolder\"));\n\n        final File file = new File(webBonitaConstantsUtils.getTempFolder().getAbsolutePath(),\n                \"\" + File.separator + \"..\" + File.separator + \"tempFolder\"\n                        + File.separator + \"fileName.txt\");\n\n        final boolean isInTempFolder = tenantFolder.isInTempFolder(file, webBonitaConstantsUtils);\n\n        assertTrue(isInTempFolder);\n    }\n\n    @Test\n    public void should_unauthorized_a_file_not_in_temp_folder() throws Exception {\n        given(webBonitaConstantsUtils.getTempFolder()).willReturn(new File(\".\" + File.separator + \"tempFolder\"));\n\n        final File file = new File(webBonitaConstantsUtils.getTempFolder().getAbsolutePath(),\n                \"\" + File.separator + \"..\" + File.separator + \"..\"\n                        + File.separator + \"..\" + File.separator + \"fileName.txt\");\n\n        final boolean isInTempFolder = tenantFolder.isInTempFolder(file, webBonitaConstantsUtils);\n\n        assertFalse(isInTempFolder);\n    }\n\n    @Test\n    public void should_authorized_a_file_in_a_specific_folder() throws Exception {\n\n        final File folder = new File(\".\" + File.separator + \"anyFolder\");\n\n        final File file = new File(\".\" + File.separator + \"anyFolder\" + File.separator + \"..\" + File.separator\n                + \"anyFolder\" + File.separator + \"fileName.txt\");\n\n        final boolean isInTempFolder = tenantFolder.isInFolder(file, folder);\n\n        assertTrue(isInTempFolder);\n    }\n\n    @Test\n    public void should_unauthorized_a_file_not_in_a_specific_folder() throws Exception {\n\n        final File folder = new File(\".\" + File.separator + \"anyFolder\");\n\n        final File file = new File(\".\" + File.separator + \"anyFolder\" + File.separator + \"..\" + File.separator + \"..\"\n                + File.separator + \"fileName.txt\");\n\n        final boolean isInTempFolder = tenantFolder.isInFolder(file, folder);\n\n        assertFalse(isInTempFolder);\n    }\n\n    @Test\n    public void should_return_completed_temp_file() throws Exception {\n        final String originalFilename = \"text.txt\";\n        final String tempFileName = UUID.randomUUID().toString();\n\n        Path tempFolder = Path.of(\".\" + File.separator + \"tempFolder\");\n        Files.createDirectories(tempFolder);\n\n        given(tenantFolder.getBonitaTenantConstantUtil()).willReturn(webBonitaConstantsUtils);\n        given(webBonitaConstantsUtils.getTempFolder()).willReturn(tempFolder.toFile());\n\n        File newTmpFile = tenantFolder.makeUniqueFilename(originalFilename);\n        doReturn(newTmpFile).when(tenantFolder).makeUniqueFilename(originalFilename);\n        doReturn(InputStream.nullInputStream()).when(fileContent).getInputStream();\n        doReturn(originalFilename).when(fileContent).getFileName();\n        doReturn(fileContent).when(tenantFolder).retrieveUploadedTempContent(tempFileName);\n\n        final File completedFile = tenantFolder.getTempFile(tempFileName);\n\n        assertThat(completedFile.getCanonicalPath()).isEqualTo(\n                new File(\".\" + File.separator + \"tempFolder\" + File.separator + newTmpFile.getName())\n                        .getCanonicalPath());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/utils/CacheUtilTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.utils;\n\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertNull;\n\nimport org.ehcache.CacheManager;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Rohart Bastien\n */\npublic class CacheUtilTest {\n\n    protected CacheManager cacheManager = null;\n    protected String cacheName = \"testCache\";\n\n    @Before\n    public void setUp() {\n        cacheManager = CacheUtil.getCacheManager();\n        assertNotNull(\"Cannot create cache\", CacheUtil.createCache(cacheManager, cacheName));\n    }\n\n    @After\n    public void tearDown() {\n        if (cacheManager != null) {\n            CacheUtil.clear(cacheName);\n        }\n    }\n\n    @Test\n    public void testCreateCaches() {\n        try {\n            assertNotNull(\"Cannot create caches\", CacheUtil.createCache(cacheManager, cacheName));\n        } finally {\n            CacheUtil.clear(cacheName);\n        }\n    }\n\n    @Test\n    public void testStore() {\n        CacheUtil.store(cacheName, \"testStoreKey\", \"testStoreValue\");\n        assertNotNull(\"Cannot store\", cacheManager.getCache(cacheName, Object.class, Object.class));\n    }\n\n    @Test\n    public void testGet() {\n        CacheUtil.store(cacheName, \"testStoreKey\", \"testStoreValue\");\n        assertNotNull(\"Cannot get the element in the cache\",\n                CacheUtil.get(cacheName, \"testStoreKey\"));\n    }\n\n    @Test\n    public void testClear() {\n        CacheUtil.clear(cacheName);\n        assertNull(\"Cannot clear the cache\", CacheUtil.get(cacheName, \"testStoreKey\"));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/utils/ContractTypeConverterTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.utils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.bonitasoft.console.common.server.utils.ContractTypeConverter.ISO_8601_DATE_PATTERNS;\nimport static org.mockito.Mockito.*;\n\nimport java.io.*;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.OffsetDateTime;\nimport java.time.ZoneOffset;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.commons.io.FileUtils;\nimport org.bonitasoft.engine.bpm.contract.ContractDefinition;\nimport org.bonitasoft.engine.bpm.contract.FileInputValue;\nimport org.bonitasoft.engine.bpm.contract.InputDefinition;\nimport org.bonitasoft.engine.bpm.contract.Type;\nimport org.bonitasoft.engine.bpm.contract.impl.ContractDefinitionImpl;\nimport org.bonitasoft.engine.bpm.contract.impl.InputDefinitionImpl;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.io.FileContent;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.SystemOutRule;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ContractTypeConverterTest {\n\n    @Rule\n    public final SystemOutRule systemOutRule = new SystemOutRule().enableLog().muteForSuccessfulTests();\n\n    public static final long DATE_01_01_1970_13H_AS_LONG_GMT = 46800000L;\n\n    public static final String DATE_01_01_1970_13H_AS_STRING_GMT = \"1970-01-01T13:00:00.000Z\";\n\n    public static final LocalDate testLocalDate = LocalDate.of(2012, 4, 21);\n\n    public static final LocalDateTime testLocalDateTime = LocalDateTime.of(2012, 4, 21, 17, 42, 29);\n\n    @Mock\n    ContractDefinition contractDefinition;\n\n    @Mock\n    BonitaHomeFolderAccessor bonitaHomeFolderAccessor;\n\n    long maxSizeForTenant = 1000L;\n\n    String filename = \"file.txt\";\n\n    String fileContentString = \"content\";\n\n    @Spy\n    @InjectMocks\n    ContractTypeConverter contractTypeConverter = new ContractTypeConverter(ISO_8601_DATE_PATTERNS);\n\n    private File generateTempFile() throws IOException {\n        final File tempFile = File.createTempFile(this.getClass().getName(), null);\n        tempFile.deleteOnExit();\n        FileUtils.writeByteArrayToFile(tempFile, fileContentString.getBytes(\"UTF-8\"));\n        return tempFile;\n    }\n\n    private FileContent generateTempFileContent() throws IOException {\n        return new FileContent(filename, new ByteArrayInputStream(fileContentString.getBytes()), \"text/plain\");\n    }\n\n    @Test\n    public void getProcessedInputs_with_empty_contract_should_return_unmodified_inputs() throws Exception {\n        when(contractDefinition.getInputs()).thenReturn(Collections.<InputDefinition> emptyList());\n        final Map<String, Serializable> input = new HashMap<>();\n        input.put(\"input1\", \"value1\");\n        input.put(\"input2\", \"value2\");\n\n        final Map<String, Serializable> processedInput = contractTypeConverter.getProcessedInput(contractDefinition,\n                input, maxSizeForTenant);\n\n        assertThat(processedInput).isEqualTo(input);\n    }\n\n    @Test\n    public void getProcessedInput_when_no_contract() throws Exception {\n        final Map<String, Serializable> input = new HashMap<>();\n\n        final Map<String, Serializable> processedInput = contractTypeConverter.getProcessedInput(null, input,\n                maxSizeForTenant);\n\n        assertThat(processedInput).isEqualTo(input);\n    }\n\n    @Test\n    public void getProcessedInput_when_null_input() throws Exception {\n        when(contractDefinition.getInputs()).thenReturn(Collections.<InputDefinition> emptyList());\n\n        final Map<String, Serializable> processedInput = contractTypeConverter.getProcessedInput(contractDefinition,\n                null, maxSizeForTenant);\n\n        assertThat(processedInput).isEqualTo(new HashMap<>());\n    }\n\n    @Test\n    public void getProcessedInputs_with_invalid_inputs_should_return_unmodified_inputs() throws Exception {\n        final List<InputDefinition> inputDefinition = generateSimpleInputDefinition(false);\n        when(contractDefinition.getInputs()).thenReturn(inputDefinition);\n        final Map<String, Serializable> input = generateInvalidInputMap();\n\n        final Map<String, Serializable> processedInput = contractTypeConverter.getProcessedInput(contractDefinition,\n                input, maxSizeForTenant);\n\n        assertThat(processedInput).containsOnly(entry(\"inputText\", \"0\"), entry(\"inputBoolean\", \"hello\"),\n                entry(\"inputDate\", \"0\"),\n                entry(\"inputInteger\", \"hello\"), entry(\"inputDecimal\", \"hello\"), entry(\"inputLong\", \"hello\"));\n    }\n\n    @Test\n    public void getProcessedInputs_with_simple_input_should_return_processed_input() throws Exception {\n        final List<InputDefinition> inputDefinition = generateSimpleInputDefinition(true);\n        when(contractDefinition.getInputs()).thenReturn(inputDefinition);\n        final String tempFileKey = \"tempFile\";\n        final FileContent tempFile = generateTempFileContent();\n        doReturn(tempFile).when(bonitaHomeFolderAccessor).retrieveUploadedTempContent(tempFileKey);\n        final Map<String, Serializable> input = generateInputMap(tempFileKey);\n\n        final Map<String, Serializable> processedInput = contractTypeConverter.getProcessedInput(contractDefinition,\n                input, maxSizeForTenant);\n\n        assertThat(processedInput).containsOnly(entry(\"inputText\", \"text\"), entry(\"inputBoolean\", true),\n                entry(\"inputDate\", new Date(DATE_01_01_1970_13H_AS_LONG_GMT)),\n                entry(\"inputInteger\", 125686181), entry(\"inputDecimal\", 12.8), entry(\"inputLong\", Long.MAX_VALUE),\n                entry(\"inputLocalDate\", testLocalDate),\n                entry(\"inputLocalDateTime\", testLocalDateTime),\n                entry(\"inputFile\", new FileInputValue(filename, \"\", fileContentString.getBytes(\"UTF-8\"))));\n    }\n\n    @Test\n    public void getProcessedInputs_with_complex_input_should_return_processed_input() throws Exception {\n        final List<InputDefinition> inputDefinition = generateComplexInputDefinition();\n        when(contractDefinition.getInputs()).thenReturn(inputDefinition);\n        final Map<String, Serializable> input = new HashMap<>();\n        final String tempFileKey = \"tempFile\";\n        final FileContent tempFile = generateTempFileContent();\n        doReturn(tempFile).when(bonitaHomeFolderAccessor).retrieveUploadedTempContent(tempFileKey);\n        final Map<String, Serializable> complexInput = generateInputMap(tempFileKey);\n        input.put(\"inputComplex\", (Serializable) complexInput);\n\n        final Map<String, Serializable> processedInput = contractTypeConverter.getProcessedInput(contractDefinition,\n                input, maxSizeForTenant);\n        assertThat(processedInput).containsKey(\"inputComplex\");\n        final Map<String, Serializable> processedComplexInput = (Map<String, Serializable>) processedInput\n                .get(\"inputComplex\");\n        assertThat(processedComplexInput).containsOnly(entry(\"inputText\", \"text\"), entry(\"inputBoolean\", true),\n                entry(\"inputDate\", new Date(DATE_01_01_1970_13H_AS_LONG_GMT)),\n                entry(\"inputInteger\", 125686181), entry(\"inputDecimal\", 12.8), entry(\"inputLong\", Long.MAX_VALUE),\n                entry(\"inputLocalDate\", testLocalDate),\n                entry(\"inputLocalDateTime\", testLocalDateTime),\n                entry(\"inputFile\", new FileInputValue(filename, \"\", fileContentString.getBytes(\"UTF-8\"))));\n    }\n\n    @Test\n    public void getProcessedInputs_with_multiple_complex_input_should_return_processed_input() throws Exception {\n        final List<InputDefinition> inputDefinition = generateComplexInputDefinition();\n        when(contractDefinition.getInputs()).thenReturn(inputDefinition);\n        final Map<String, Serializable> input = new HashMap<>();\n        final Map<String, Serializable> complexInput = generateInputMapWithFile(\"tempFile\");\n        final Map<String, Serializable> complexInput2 = generateInputMapWithFile(\"tempFile2\");\n        final List<Serializable> multipleComplexInput = new ArrayList<>();\n        multipleComplexInput.add((Serializable) complexInput);\n        multipleComplexInput.add((Serializable) complexInput2);\n        input.put(\"inputComplex\", (Serializable) multipleComplexInput);\n\n        final Map<String, Serializable> processedInput = contractTypeConverter.getProcessedInput(contractDefinition,\n                input, maxSizeForTenant);\n        assertThat(processedInput).containsKey(\"inputComplex\");\n        final List<Serializable> processedMultipleComplexInput = (List<Serializable>) processedInput\n                .get(\"inputComplex\");\n        assertThat(processedMultipleComplexInput).hasSize(2);\n        for (final Serializable processedComplexInput : processedMultipleComplexInput) {\n            final Map<String, Serializable> processedComplexInputMap = (Map<String, Serializable>) processedComplexInput;\n            assertThat(processedComplexInputMap).containsOnly(entry(\"inputText\", \"text\"), entry(\"inputBoolean\", true),\n                    entry(\"inputDate\", new Date(DATE_01_01_1970_13H_AS_LONG_GMT)),\n                    entry(\"inputInteger\", 125686181), entry(\"inputDecimal\", 12.8), entry(\"inputLong\", Long.MAX_VALUE),\n                    entry(\"inputLocalDate\", testLocalDate),\n                    entry(\"inputLocalDateTime\", testLocalDateTime),\n                    entry(\"inputFile\", new FileInputValue(filename, \"\", fileContentString.getBytes(\"UTF-8\"))));\n        }\n    }\n\n    @Test\n    public void getProcessedInputs_with_simple_input_should_return_processed_input_with_null() throws Exception {\n        final List<InputDefinition> inputDefinition = generateSimpleInputDefinition(true);\n        when(contractDefinition.getInputs()).thenReturn(inputDefinition);\n        final Map<String, Serializable> input = generateInputMapWithNull();\n\n        final Map<String, Serializable> processedInput = contractTypeConverter.getProcessedInput(contractDefinition,\n                input, maxSizeForTenant);\n\n        assertThat(processedInput).containsOnly(entry(\"inputText\", null), entry(\"inputBoolean\", null),\n                entry(\"inputDate\", null),\n                entry(\"inputInteger\", null), entry(\"inputDecimal\", null), entry(\"inputFile\", null));\n    }\n\n    @Test\n    public void getProcessedInputs_with_simple_input_should_return_processed_input_with_empty_map() throws Exception {\n        final List<InputDefinition> inputDefinition = generateSimpleInputDefinition(true);\n        when(contractDefinition.getInputs()).thenReturn(inputDefinition);\n        final Map<String, Serializable> input = generateInputMapWithEmptyFileInput();\n\n        final Map<String, Serializable> processedInput = contractTypeConverter.getProcessedInput(contractDefinition,\n                input, maxSizeForTenant);\n\n        assertThat(processedInput).containsOnly(entry(\"inputText\", null), entry(\"inputBoolean\", null),\n                entry(\"inputDate\", null),\n                entry(\"inputInteger\", null), entry(\"inputDecimal\", null),\n                entry(\"inputFile\", new HashMap<String, Serializable>()), entry(\"inputLong\", null));\n    }\n\n    @Test\n    public void getProcessedInputs_with_multiple_complex_input_should_return_processed_input_with_null()\n            throws Exception {\n        final List<InputDefinition> inputDefinition = generateComplexInputDefinition();\n        when(contractDefinition.getInputs()).thenReturn(inputDefinition);\n        final Map<String, Serializable> input = new HashMap<>();\n        final Map<String, Serializable> complexInput = generateInputMapWithFile(\"tempFile\");\n        final Map<String, Serializable> complexInput2 = generateInputMapWithNull();\n        final Map<String, Serializable> complexInput3 = null;\n        final List<Serializable> multipleComplexInput = new ArrayList<>();\n        multipleComplexInput.add((Serializable) complexInput);\n        multipleComplexInput.add((Serializable) complexInput2);\n        multipleComplexInput.add((Serializable) complexInput3);\n        input.put(\"inputComplex\", (Serializable) multipleComplexInput);\n\n        final Map<String, Serializable> processedInput = contractTypeConverter.getProcessedInput(contractDefinition,\n                input, maxSizeForTenant);\n        assertThat(processedInput).containsKey(\"inputComplex\");\n        final List<Serializable> processedMultipleComplexInput = (List<Serializable>) processedInput\n                .get(\"inputComplex\");\n        assertThat(processedMultipleComplexInput).hasSize(3);\n        final Serializable processedComplexInput1 = processedMultipleComplexInput.get(0);\n        final Map<String, Serializable> processedComplexInputMap1 = (Map<String, Serializable>) processedComplexInput1;\n        assertThat(processedComplexInputMap1).containsOnly(entry(\"inputText\", \"text\"), entry(\"inputBoolean\", true),\n                entry(\"inputDate\", new Date(DATE_01_01_1970_13H_AS_LONG_GMT)),\n                entry(\"inputInteger\", 125686181), entry(\"inputDecimal\", 12.8), entry(\"inputLong\", Long.MAX_VALUE),\n                entry(\"inputLocalDate\", testLocalDate),\n                entry(\"inputLocalDateTime\", testLocalDateTime),\n                entry(\"inputFile\", new FileInputValue(filename, \"\", fileContentString.getBytes(\"UTF-8\"))));\n        final Serializable processedComplexInput2 = processedMultipleComplexInput.get(1);\n        final Map<String, Serializable> processedComplexInputMap2 = (Map<String, Serializable>) processedComplexInput2;\n        assertThat(processedComplexInputMap2).containsOnly(entry(\"inputText\", null), entry(\"inputBoolean\", null),\n                entry(\"inputDate\", null),\n                entry(\"inputInteger\", null), entry(\"inputDecimal\", null), entry(\"inputFile\", null));\n        assertThat(processedMultipleComplexInput.get(2)).isNull();\n    }\n\n    @Test\n    public void should_not_delete_temporary_files_when_processing_input() throws Exception {\n        final List<InputDefinition> inputDefinition = generateSimpleInputDefinition(true);\n        when(contractDefinition.getInputs()).thenReturn(inputDefinition);\n        final String tempFileKey = \"tempFile\";\n        final FileContent tempFile = generateTempFileContent();\n        doReturn(tempFile).when(bonitaHomeFolderAccessor).retrieveUploadedTempContent(tempFileKey);\n        final Map<String, Serializable> input = generateInputMap(tempFileKey);\n\n        contractTypeConverter.getProcessedInput(contractDefinition, input, maxSizeForTenant);\n\n        //files should not have been deleted\n        verify(bonitaHomeFolderAccessor, times(0)).removeUploadedTempContent(anyString());\n    }\n\n    @Test\n    public void should_delete_temporary_files_of_contract_input() throws Exception {\n        final String tempFileKey = \"tempFile\";\n        final Map<String, Serializable> input = generateInputMap(tempFileKey);\n\n        contractTypeConverter.deleteTemporaryFiles(input);\n\n        verify(bonitaHomeFolderAccessor).removeUploadedTempContent(tempFileKey);\n    }\n\n    @Test\n    public void should_sanitize_filename_of_contract_input() throws Exception {\n        final List<InputDefinition> inputDefinition = generateSimpleInputDefinition(true);\n        when(contractDefinition.getInputs()).thenReturn(inputDefinition);\n        final String tempFileKey = \"tempFile\";\n        final FileContent tempFile = generateTempFileContent();\n        doReturn(tempFile).when(bonitaHomeFolderAccessor).retrieveUploadedTempContent(tempFileKey);\n        final Map<String, Serializable> input = generateInputMap(\"file<with>forbidden\\\".txt\", tempFileKey);\n\n        final Map<String, Serializable> processedInput = contractTypeConverter.getProcessedInput(contractDefinition,\n                input, maxSizeForTenant);\n\n        assertThat(processedInput).contains(entry(\"inputFile\",\n                new FileInputValue(\"file_with_forbidden_.txt\", \"\", fileContentString.getBytes(\"UTF-8\"))));\n    }\n\n    @Test\n    public void getAdaptedContractDefinition_should_return_a_converter_contract() throws IOException {\n        //given\n        final ContractDefinitionImpl processContract = new ContractDefinitionImpl();\n        final List<InputDefinition> inputDefinitions = new ArrayList<>();\n        inputDefinitions.add(\n                new InputDefinitionImpl(InputDefinition.FILE_INPUT_FILENAME, Type.TEXT, \"Name of the file\", false));\n        inputDefinitions.add(new InputDefinitionImpl(InputDefinition.FILE_INPUT_CONTENT, Type.BYTE_ARRAY,\n                \"Content of the file\", false));\n        processContract.addInput(\n                new InputDefinitionImpl(\"inputFile\", \"this is a input file\", false, Type.FILE, inputDefinitions));\n\n        //when\n        final ContractDefinition adaptedContractDefinition = contractTypeConverter\n                .getAdaptedContractDefinition(processContract);\n\n        //assert\n        final InputDefinition tempPathFileInputDefinition = adaptedContractDefinition.getInputs().get(0).getInputs()\n                .get(1);\n        assertThat(tempPathFileInputDefinition.getType()).isEqualTo(Type.TEXT);\n        assertThat(tempPathFileInputDefinition.getName()).isEqualTo(ContractTypeConverter.FILE_TEMP_PATH);\n        assertThat(tempPathFileInputDefinition.getDescription()).isEqualTo(ContractTypeConverter.TEMP_PATH_DESCRIPTION);\n    }\n\n    @Test\n    public void getAdaptedContractDefinition_should_return_null_on_null_contract() {\n        //when\n        final ContractDefinition adaptedContractDefinition = contractTypeConverter.getAdaptedContractDefinition(null);\n\n        //assert\n        assertThat(adaptedContractDefinition).isNull();\n    }\n\n    @Test\n    public void should_be_able_to_convert_Integer_to_Date() throws Exception {\n\n        Object conversionResult = contractTypeConverter.convertToType(Type.DATE, 86400000);\n        assertThat(conversionResult).isNotNull();\n        assertThat(conversionResult).isInstanceOf(Date.class);\n        assertThat(((Date) conversionResult).getTime()).isEqualTo(86400000L);\n    }\n\n    private Map<String, Serializable> generateInputMapWithFile(final String tempFileKey)\n            throws IOException, BonitaException {\n        final FileContent tempFile = generateTempFileContent();\n        doReturn(tempFile).when(bonitaHomeFolderAccessor).retrieveUploadedTempContent(tempFileKey);\n        return generateInputMap(tempFileKey);\n    }\n\n    private Map<String, Serializable> generateInputMap(final String tempFileKey) {\n        return generateInputMap(filename, tempFileKey);\n    }\n\n    private Map<String, Serializable> generateInputMap(final String filename, final String tempFileKey) {\n        final Map<String, Serializable> inputMap = new HashMap<>();\n        inputMap.put(\"inputText\", \"text\");\n        inputMap.put(\"inputBoolean\", \"true\");\n        inputMap.put(\"inputDate\", DATE_01_01_1970_13H_AS_STRING_GMT);\n        inputMap.put(\"inputInteger\", \"125686181\");\n        inputMap.put(\"inputDecimal\", \"12.8\");\n        inputMap.put(\"inputLong\", \"9223372036854775807\");\n        inputMap.put(\"inputLocalDate\", testLocalDate.toString());\n        inputMap.put(\"inputLocalDateTime\", testLocalDateTime.toString());\n        final Map<String, Serializable> fileMap = new HashMap<>();\n        fileMap.put(InputDefinition.FILE_INPUT_FILENAME, filename);\n        fileMap.put(ContractTypeConverter.FILE_TEMP_PATH, tempFileKey);\n        fileMap.put(ContractTypeConverter.CONTENT_TYPE, \"contentType\");\n        inputMap.put(\"inputFile\", (Serializable) fileMap);\n        return inputMap;\n    }\n\n    private Map<String, Serializable> generateInvalidInputMap() {\n        final Map<String, Serializable> inputMap = new HashMap<>();\n        inputMap.put(\"inputText\", 0);\n        inputMap.put(\"inputBoolean\", \"hello\");\n        inputMap.put(\"inputDate\", \"0\");\n        inputMap.put(\"inputInteger\", \"hello\");\n        inputMap.put(\"inputDecimal\", \"hello\");\n        inputMap.put(\"inputLong\", \"hello\");\n        return inputMap;\n    }\n\n    private Map<String, Serializable> generateInputMapWithNull() {\n        final Map<String, Serializable> inputMap = new HashMap<>();\n        inputMap.put(\"inputText\", null);\n        inputMap.put(\"inputBoolean\", null);\n        inputMap.put(\"inputDate\", null);\n        inputMap.put(\"inputInteger\", null);\n        inputMap.put(\"inputDecimal\", null);\n        inputMap.put(\"inputFile\", null);\n        return inputMap;\n    }\n\n    private Map<String, Serializable> generateInputMapWithEmptyFileInput() {\n        final Map<String, Serializable> inputMap = new HashMap<>();\n        inputMap.put(\"inputText\", null);\n        inputMap.put(\"inputBoolean\", null);\n        inputMap.put(\"inputDate\", null);\n        inputMap.put(\"inputInteger\", null);\n        inputMap.put(\"inputDecimal\", null);\n        inputMap.put(\"inputLong\", null);\n        inputMap.put(\"inputFile\", new HashMap<String, Serializable>());\n        return inputMap;\n    }\n\n    private List<InputDefinition> generateSimpleInputDefinition(final boolean withFile) {\n        final List<InputDefinition> inputDefinitions = generateSimpleInputDefinition();\n        if (withFile) {\n            final InputDefinition fileInputDefinition = mock(InputDefinitionImpl.class);\n            when(fileInputDefinition.getType()).thenReturn(Type.FILE);\n            when(fileInputDefinition.getName()).thenReturn(\"inputFile\");\n            inputDefinitions.add(fileInputDefinition);\n        }\n        return inputDefinitions;\n    }\n\n    private List<InputDefinition> generateSimpleInputDefinition() {\n        final List<InputDefinition> inputDefinitions = new ArrayList<>();\n        final InputDefinition textInputDefinition = mock(InputDefinitionImpl.class);\n        when(textInputDefinition.getType()).thenReturn(Type.TEXT);\n        when(textInputDefinition.getName()).thenReturn(\"inputText\");\n        inputDefinitions.add(textInputDefinition);\n        final InputDefinition booleanInputDefinition = mock(InputDefinitionImpl.class);\n        when(booleanInputDefinition.getType()).thenReturn(Type.BOOLEAN);\n        when(booleanInputDefinition.getName()).thenReturn(\"inputBoolean\");\n        inputDefinitions.add(booleanInputDefinition);\n        final InputDefinition dateInputDefinition = mock(InputDefinitionImpl.class);\n        when(dateInputDefinition.getType()).thenReturn(Type.DATE);\n        when(dateInputDefinition.getName()).thenReturn(\"inputDate\");\n        inputDefinitions.add(dateInputDefinition);\n        final InputDefinition integerInputDefinition = mock(InputDefinitionImpl.class);\n        when(integerInputDefinition.getType()).thenReturn(Type.INTEGER);\n        when(integerInputDefinition.getName()).thenReturn(\"inputInteger\");\n        inputDefinitions.add(integerInputDefinition);\n        final InputDefinition decimalInputDefinition = mock(InputDefinitionImpl.class);\n        when(decimalInputDefinition.getType()).thenReturn(Type.DECIMAL);\n        when(decimalInputDefinition.getName()).thenReturn(\"inputDecimal\");\n        inputDefinitions.add(decimalInputDefinition);\n        final InputDefinition longInputDefinition = mock(InputDefinitionImpl.class);\n        when(longInputDefinition.getType()).thenReturn(Type.LONG);\n        when(longInputDefinition.getName()).thenReturn(\"inputLong\");\n        inputDefinitions.add(longInputDefinition);\n        final InputDefinition localDateInputDefinition = mock(InputDefinitionImpl.class);\n        when(localDateInputDefinition.getType()).thenReturn(Type.LOCALDATE);\n        when(localDateInputDefinition.getName()).thenReturn(\"inputLocalDate\");\n        inputDefinitions.add(localDateInputDefinition);\n        final InputDefinition localDateTimeInputDefinition = mock(InputDefinitionImpl.class);\n        when(localDateTimeInputDefinition.getType()).thenReturn(Type.LOCALDATETIME);\n        when(localDateTimeInputDefinition.getName()).thenReturn(\"inputLocalDateTime\");\n        inputDefinitions.add(localDateTimeInputDefinition);\n        return inputDefinitions;\n    }\n\n    private List<InputDefinition> generateComplexInputDefinition() {\n        final List<InputDefinition> inputDefinitions = new ArrayList<>();\n        final InputDefinition inputDefinition = mock(InputDefinition.class);\n        when(inputDefinition.getName()).thenReturn(\"inputComplex\");\n        when(inputDefinition.hasChildren()).thenReturn(true);\n        final List<InputDefinition> childInputDefinitions = generateSimpleInputDefinition(true);\n        when(inputDefinition.getInputs()).thenReturn(childInputDefinitions);\n        inputDefinitions.add(inputDefinition);\n        return inputDefinitions;\n    }\n\n    @Test\n    public void convertToType_should_correctly_parse_incoming_objects() {\n        Object date = new ContractTypeConverter(ISO_8601_DATE_PATTERNS).convertToType(Type.DATE, \"2017-12-25\");\n        assertThat(date.getClass()).isEqualTo(Date.class);\n        Object validLocalDate = new ContractTypeConverter(ISO_8601_DATE_PATTERNS).convertToType(Type.LOCALDATE,\n                \"2017-12-25\");\n        assertThat(validLocalDate).isNotNull().isEqualTo(LocalDate.of(2017, 12, 25));\n        Object validLocalDateTime = new ContractTypeConverter(ISO_8601_DATE_PATTERNS).convertToType(Type.LOCALDATETIME,\n                \"2017-12-17T21:42:57\");\n        assertThat(validLocalDateTime).isEqualTo(LocalDateTime.of(2017, 12, 17, 21, 42, 57));\n        final String invalidDateTime = \"2017-22-37T21:42:57\";\n        Object invalidDateFormat = new ContractTypeConverter(ISO_8601_DATE_PATTERNS).convertToType(Type.LOCALDATETIME,\n                invalidDateTime);\n        assertThat(invalidDateFormat).isEqualTo(invalidDateTime);\n    }\n\n    @Test\n    public void convertToType_with_null_localDate_value_should_silently_return_null() throws Exception {\n        // when:\n        final Object nullLocalDate = new ContractTypeConverter(ISO_8601_DATE_PATTERNS).convertToType(Type.LOCALDATE,\n                null);\n\n        // then:\n        assertThat(nullLocalDate).isNull();\n    }\n\n    @Test\n    public void convertToType_with_null_localDateTime_value_should_silently_return_null() throws Exception {\n        // when:\n        final Object nullLocalDateTime = new ContractTypeConverter(ISO_8601_DATE_PATTERNS)\n                .convertToType(Type.LOCALDATETIME, null);\n\n        // then:\n        assertThat(nullLocalDateTime).isNull();\n    }\n\n    @Test\n    public void convertToType_with_null_String_value_should_silently_return_null() throws Exception {\n        // when:\n        final Object nullString = new ContractTypeConverter(ISO_8601_DATE_PATTERNS).convertToType(Type.TEXT, null);\n\n        // then:\n        assertThat(nullString).isNull();\n    }\n\n    @Test\n    public void convertToType_should_ignore_extra_characters_in_String_for_LocalDates() {\n        ContractTypeConverter converter = new ContractTypeConverter(ISO_8601_DATE_PATTERNS);\n\n        final Object localDateString = converter.convertToType(Type.LOCALDATE, \"1987-12-12T10:00:00\");\n\n        assertThat(localDateString).isEqualTo(LocalDate.of(1987, 12, 12));\n\n    }\n\n    @Test\n    public void convertToType_should_ignore_extra_characters_in_String_for_LocalDatesTime() {\n        ContractTypeConverter converter = new ContractTypeConverter(ISO_8601_DATE_PATTERNS);\n        Map<String, Object> conversionMap = new HashMap<>();\n        conversionMap.put(\"1987-12-12T10:00Z\", LocalDateTime.of(1987, 12, 12, 10, 0, 0, 0));\n        conversionMap.put(\"1987-12-12T10:00:00Z\", LocalDateTime.of(1987, 12, 12, 10, 0, 0, 0));\n        conversionMap.put(\"2017-03-09T12:20:20.258Z\", LocalDateTime.of(2017, 3, 9, 12, 20, 20, 258000000));\n        conversionMap.put(\"2017-03-09T12:20:20.258854753Z\", LocalDateTime.of(2017, 3, 9, 12, 20, 20, 258854753));\n        conversionMap.put(\"2017-03-09T12:20:20+01:00\", LocalDateTime.of(2017, 3, 9, 12, 20, 20));\n        conversionMap.put(\"2017-03-09T12:20:20.258+01:00\", LocalDateTime.of(2017, 3, 9, 12, 20, 20, 258000000));\n        conversionMap.put(\"2017-03-09T12:20:20.258854753+01:00\", LocalDateTime.of(2017, 3, 9, 12, 20, 20, 258854753));\n        conversionMap.put(\"2017-03-09T12:20:20+01:00[Europe/Berlin]\", LocalDateTime.of(2017, 3, 9, 12, 20, 20));\n        conversionMap.put(\"2017-03-09T12:20:20.258+01:00[Europe/Berlin]\",\n                LocalDateTime.of(2017, 3, 9, 12, 20, 20, 258000000));\n        conversionMap.put(\"2017-03-09T12:20:20.258854753+01:00[Europe/Berlin]\",\n                LocalDateTime.of(2017, 3, 9, 12, 20, 20, 258854753));\n        conversionMap.put(\"2017-03-09T12:20:20.258854753-01:00\", LocalDateTime.of(2017, 3, 9, 12, 20, 20, 258854753));\n\n        for (Map.Entry<String, Object> entry : conversionMap.entrySet()) {\n            assertThat(converter.convertToType(Type.LOCALDATETIME, entry.getKey())).isEqualTo(entry.getValue());\n        }\n    }\n\n    @Test\n    public void convertToType_should_convert_to_OffsetDateTime_every_input_format_string() {\n        ContractTypeConverter converter = new ContractTypeConverter(ISO_8601_DATE_PATTERNS);\n        Map<String, Object> conversionMap = new HashMap<>();\n        conversionMap.put(\"1987-12-12T10:00:00Z\", OffsetDateTime.of(1987, 12, 12, 10, 0, 0, 0, ZoneOffset.UTC));\n        conversionMap.put(\"2017-03-09T12:20:20.258Z\",\n                OffsetDateTime.of(2017, 3, 9, 12, 20, 20, 258000000, ZoneOffset.UTC));\n        conversionMap.put(\"2017-03-09T12:20:20.258854753Z\",\n                OffsetDateTime.of(2017, 3, 9, 12, 20, 20, 258854753, ZoneOffset.UTC));\n        conversionMap.put(\"2017-03-09T12:20:20+01:00\",\n                OffsetDateTime.of(2017, 3, 9, 12, 20, 20, 0, ZoneOffset.ofHours(1)));\n        conversionMap.put(\"2017-03-09T12:20:20.258+01:00\",\n                OffsetDateTime.of(2017, 3, 9, 12, 20, 20, 258000000, ZoneOffset.ofHours(1)));\n        conversionMap.put(\"2017-03-09T12:20:20.258854753+01:00\",\n                OffsetDateTime.of(2017, 3, 9, 12, 20, 20, 258854753, ZoneOffset.ofHours(1)));\n        conversionMap.put(\"2017-03-09T12:20:20+01:00[Europe/Berlin]\",\n                OffsetDateTime.of(2017, 3, 9, 12, 20, 20, 0, ZoneOffset.ofHours(1)));\n        conversionMap.put(\"2017-03-09T12:20:20.258+01:00[Europe/Berlin]\",\n                OffsetDateTime.of(2017, 3, 9, 12, 20, 20, 258000000, ZoneOffset.ofHours(1)));\n        conversionMap.put(\"2017-03-09T12:20:20.258854753+01:00[Europe/Berlin]\",\n                OffsetDateTime.of(2017, 3, 9, 12, 20, 20, 258854753, ZoneOffset.ofHours(1)));\n        conversionMap.put(\"2017-03-09T12:20:20.258854753-01:00\",\n                OffsetDateTime.of(2017, 3, 9, 12, 20, 20, 258854753, ZoneOffset.ofHours(-1)));\n        for (Map.Entry<String, Object> entry : conversionMap.entrySet()) {\n            assertThat(converter.convertToType(Type.OFFSETDATETIME, entry.getKey())).isEqualTo(entry.getValue());\n        }\n    }\n\n    @Test\n    public void convertToType_should_not_convert_into_an_OffsetDate_a_String_that_is_too_short() {\n        ContractTypeConverter converter = new ContractTypeConverter(ISO_8601_DATE_PATTERNS);\n\n        final Object unparseableString = converter.convertToType(Type.OFFSETDATETIME, \"1987-12\");\n\n        assertThat(unparseableString).isEqualTo(\"1987-12\");\n    }\n\n    @Test\n    public void convertToType_should_not_convert_into_a_LocalDate_a_String_that_is_too_short() {\n        ContractTypeConverter converter = spy(new ContractTypeConverter(ISO_8601_DATE_PATTERNS));\n\n        systemOutRule.clearLog();\n        final Object localDateString = converter.convertToType(Type.LOCALDATE, \"1987-12\");\n\n        assertThat(localDateString).isEqualTo(\"1987-12\");\n        assertThat(systemOutRule.getLog()).contains(\"unable to parse '1987-12' to type \" + LocalDate.class.getName());\n    }\n\n    @Test\n    public void convertToType_should_not_convert_into_a_LocalDateTime_a_String_that_is_too_short() {\n\n        ContractTypeConverter converter = new ContractTypeConverter(ISO_8601_DATE_PATTERNS);\n\n        final String year = \"1987\";\n        final Object localDateTimeString1 = converter.convertToType(Type.LOCALDATETIME, year);\n        final String yearAndHour = \"1987-10-11T19\";\n        final Object localDateTimeString2 = converter.convertToType(Type.LOCALDATETIME, yearAndHour);\n        final String invalidDateAndTime = \"1987-10-11T19:32:4\";\n        final Object localDateTimeString3 = converter.convertToType(Type.LOCALDATETIME, invalidDateAndTime);\n\n        assertThat(localDateTimeString1).isEqualTo(year);\n        assertThat(localDateTimeString2).isEqualTo(yearAndHour);\n        assertThat(localDateTimeString3).isEqualTo(invalidDateAndTime);\n    }\n\n    @Test\n    public void convertToType_should_convert_a_String_without_seconds_into_a_LocalDateTime_objects() {\n        ContractTypeConverter converter = new ContractTypeConverter(ISO_8601_DATE_PATTERNS);\n\n        final Object localDateTimeString = converter.convertToType(Type.LOCALDATETIME, \"1987-10-11T19:32\");\n\n        assertThat(localDateTimeString).isEqualTo(LocalDateTime.of(1987, 10, 11, 19, 32));\n    }\n\n    @Test\n    public void convertToType_should_convert_a_LongString_to_a_LocalDate() {\n        ContractTypeConverter converter = new ContractTypeConverter(ISO_8601_DATE_PATTERNS);\n\n        final Object localDate = converter.convertToType(Type.LOCALDATE, \"1987-10-11T19:32\");\n\n        assertThat(localDate).isEqualTo(LocalDate.of(1987, 10, 11));\n    }\n\n    @Test\n    public void convertToType_should_log_if_value_is_truncated() {\n        ContractTypeConverter converter = spy(new ContractTypeConverter(ISO_8601_DATE_PATTERNS));\n\n        systemOutRule.clearLog();\n        converter.convertToType(Type.LOCALDATE, \"1987-10-11T19:32\");\n\n        assertThat(systemOutRule.getLog())\n                .contains(\"The string 1987-10-11T19:32 contains information that will be dropped \" +\n                        \"to convert it to a LocalDate (most likely time and timezone information which are not relevant).\");\n\n        converter.convertToType(Type.LOCALDATETIME, \"1987-10-11T19:32Z\");\n\n        assertThat(systemOutRule.getLog())\n                .contains(\"The string 1987-10-11T19:32Z contains information that will be dropped to \" +\n                        \"convert it to a LocalDateTime (most likely time and timezone information which are not relevant).\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/utils/LocaleUtilsTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.utils;\n\nimport static junit.framework.Assert.assertEquals;\nimport static org.mockito.Mockito.*;\nimport static org.mockito.MockitoAnnotations.initMocks;\n\nimport java.util.HashMap;\nimport java.util.Locale;\n\nimport javax.servlet.http.Cookie;\nimport javax.servlet.http.HttpServletRequest;\n\nimport org.bonitasoft.console.common.server.i18n.I18n;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\nimport org.mockito.Mockito;\n\n/**\n * Created by Vincent Elcrin\n * Date: 10/10/13\n * Time: 10:25\n */\npublic class LocaleUtilsTest {\n\n    @Mock\n    private HttpServletRequest request;\n\n    @Before\n    public void setUp() throws Exception {\n        initMocks(this);\n\n        HashMap<String, String> availableLocales;\n        availableLocales = new HashMap<>();\n        availableLocales.put(\"en\", \"English\");\n        availableLocales.put(\"fr\", \"Français\");\n        availableLocales.put(\"es\", \"Español\");\n        availableLocales.put(\"pt_BR\", \"Português (Brasil)\");\n        availableLocales.put(\"ja\", \"日本語\");\n\n        I18n i18n = mock(I18n.class);\n        I18n.setInstance(i18n);\n        Mockito.when(i18n.getAvailableLocalesFor(anyString())).thenReturn(availableLocales);\n    }\n\n    @After\n    public void cleanUp() {\n        I18n.setInstance(null);\n    }\n\n    @Test\n    public void testLocalCanBeRetrieveFromCookie() {\n        Cookie[] cookies = {\n                new Cookie(\"aCookie\", \"value\"),\n                new Cookie(LocaleUtils.LOCALE_COOKIE_NAME, \"fr\"),\n                new Cookie(\"aNullCookie\", null),\n        };\n        doReturn(cookies).when(request).getCookies();\n\n        String locale = LocaleUtils.getUserLocaleAsString(request);\n\n        assertEquals(\"fr\", locale);\n    }\n\n    @Test\n    public void testANullCookieResultsWithBrowserLocale() {\n        doReturn(Locale.CANADA_FRENCH).when(request).getLocale();\n\n        String locale = LocaleUtils.getUserLocaleAsString(request);\n\n        assertEquals(Locale.FRENCH.toString(), locale);\n    }\n\n    @Test\n    public void testANullCookieAndBrowserLocaleResultsWithDefaultLocale() {\n\n        String locale = LocaleUtils.getUserLocaleAsString(request);\n\n        assertEquals(\"en\", locale);\n    }\n\n    @Test\n    public void testWeCanRetrieveLocaleFromCookie() {\n        Cookie[] cookies = {\n                new Cookie(LocaleUtils.LOCALE_COOKIE_NAME, \"en_US\"),\n        };\n        doReturn(cookies).when(request).getCookies();\n\n        String locale = LocaleUtils.getUserLocaleAsString(request);\n\n        assertEquals(\"en\", locale);\n    }\n\n    @Test\n    public void testWeCanRetrieveLocaleFromRequest() {\n        Cookie[] cookies = {\n                new Cookie(LocaleUtils.LOCALE_COOKIE_NAME, \"en_US\"),\n        };\n        doReturn(cookies).when(request).getCookies();\n\n        String locale = LocaleUtils.getUserLocaleAsString(request);\n\n        assertEquals(\"en\", locale);\n    }\n\n    @Test\n    public void testDefaultLocaleIfNotProvided() {\n        String locale = LocaleUtils.getUserLocaleAsString(request);\n        assertEquals(\"en\", locale);\n    }\n\n    @Test\n    public void testAnInvalidLocaleInRequestResultsWithDefaultLocale() {\n        doReturn(\"weirdvalue\").when(request).getParameter(LocaleUtils.LOCALE_PARAM);\n\n        String locale = LocaleUtils.getUserLocaleAsString(request);\n\n        assertEquals(\"en\", locale);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/utils/PlatformManagementUtilsTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.utils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.api.PlatformAPI;\nimport org.bonitasoft.engine.api.PlatformLoginAPI;\nimport org.bonitasoft.engine.platform.InvalidPlatformCredentialsException;\nimport org.bonitasoft.engine.session.PlatformSession;\nimport org.bonitasoft.engine.session.impl.PlatformSessionImpl;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.ClearSystemProperties;\nimport org.junit.rules.ExpectedException;\nimport org.junit.rules.TestRule;\nimport org.junit.runner.RunWith;\nimport org.mockito.InOrder;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Baptiste Mesta\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class PlatformManagementUtilsTest {\n\n    @Mock\n    private PlatformAPI platformAPI;\n    @Mock\n    private PlatformLoginAPI platformLoginAPI;\n    @Spy\n    private PlatformManagementUtils platformManagementUtils;\n    @Rule\n    public TestRule clean = new ClearSystemProperties(\"org.bonitasoft.platform.username\",\n            \"org.bonitasoft.platform.password\");\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Before\n    public void before() throws Exception {\n        doNothing().when(platformManagementUtils).platformLogout(any(PlatformSession.class));\n        doReturn(platformAPI).when(platformManagementUtils).getPlatformAPI(any(PlatformSession.class));\n        doReturn(platformLoginAPI).when(platformManagementUtils).getPlatformLoginAPI();\n    }\n\n    @Test\n    public void should_updateConfigurationFile_call_engine() throws Exception {\n        //when\n        doReturn(new PlatformSessionImpl(1231, new Date(), 54325423, \"testUser\", 75463)).when(platformManagementUtils)\n                .platformLogin();\n        platformManagementUtils.updateConfigurationFile(\"myFile\", \"theNewContent\".getBytes());\n        //then\n        InOrder inOrder = inOrder(platformManagementUtils, platformAPI);\n        inOrder.verify(platformManagementUtils).platformLogin();\n        inOrder.verify(platformAPI).updateClientTenantConfigurationFile(\"myFile\", \"theNewContent\".getBytes());\n        inOrder.verify(platformManagementUtils).platformLogout(any(PlatformSession.class));\n    }\n\n    @Test\n    public void should_login_locally_when_connection_is_local() throws Exception {\n        //given\n        doReturn(true).when(platformManagementUtils).isLocal();\n        PlatformSessionImpl toBeReturned = new PlatformSessionImpl(1231, new Date(), 54325423, \"testUser\", 75463);\n        doReturn(toBeReturned).when(platformManagementUtils)\n                .localPlatformLogin();\n        //when\n        PlatformSession platformSession = platformManagementUtils.platformLogin();\n        //then\n        verify(platformLoginAPI, never()).login(anyString(), anyString());\n        assertThat(platformSession).isEqualTo(toBeReturned);\n    }\n\n    @Test\n    public void should_login_using_system_property_when_connection_is_not_local() throws Exception {\n        //given\n        doReturn(false).when(platformManagementUtils).isLocal();\n        System.setProperty(\"org.bonitasoft.platform.username\", \"john\");\n        System.setProperty(\"org.bonitasoft.platform.password\", \"bpm\");\n        //when\n        platformManagementUtils.platformLogin();\n        //then\n        verify(platformLoginAPI).login(\"john\", \"bpm\");\n    }\n\n    @Test\n    public void should_throw_an_exception_with_comprehensive_message_when_system_property_to_connect_to_the_platform_is_not_set_properly()\n            throws Exception {\n        doReturn(false).when(platformManagementUtils).isLocal();\n        doThrow(new InvalidPlatformCredentialsException(\"wrong credentials\")).when(platformLoginAPI).login(null, null);\n\n        expectedException.expect(InvalidPlatformCredentialsException.class);\n        expectedException.expectMessage(\n                \"The portal is not able to login to the engine because system properties org.bonitasoft.platform.username and org.bonitasoft.platform.password are not set correctly\");\n        platformManagementUtils.platformLogin();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/utils/TenantCacheUtilTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.utils;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\n\nimport org.junit.Test;\n\npublic class TenantCacheUtilTest {\n\n    @Test\n    public void shouldStoreActorInitiator() {\n        final TenantCacheUtil tenantCacheUtil = TenantCacheUtilFactory.getTenantCacheUtil();\n        tenantCacheUtil.storeProcessActorInitiatorId(2L, 3L);\n        final Long actorInitiatorRetrievedFromCacheByParameters = TenantCacheUtilFactory.getTenantCacheUtil()\n                .getProcessActorInitiatorId(2L);\n        assertNotNull(actorInitiatorRetrievedFromCacheByParameters);\n        assertEquals(3L, actorInitiatorRetrievedFromCacheByParameters.longValue());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/utils/UrlBuilderTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.common.server.utils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class UrlBuilderTest {\n\n    UrlBuilder urlBuilder;\n\n    private final static String specialULRCharacters = \"!'();:@&=+$,/?# \";\n\n    private final static String specialULRCharactersEncoded = \"%21%27%28%29%3B%3A%40%26%3D%2B%24%2C%2F%3F%23+\";\n\n    @Before\n    public void setUp() throws Exception {\n    }\n\n    @Test\n    public void appendParameter_should_add_service_param_to_URL() {\n        urlBuilder = new UrlBuilder(\"http://www.cas-service.com/login\");\n        urlBuilder.appendParameter(\"service\", \"http://www.ofelia.com/bonita\");\n\n        assertThat(urlBuilder.build())\n                .isEqualTo(\"http://www.cas-service.com/login?service=http%3A%2F%2Fwww.ofelia.com%2Fbonita\");\n    }\n\n    @Test\n    public void appendParameter_should_add_service_param_to_URL_with_hash() {\n        urlBuilder = new UrlBuilder(\"http://www.cas-service.com/login#_pf=2\");\n        urlBuilder.appendParameter(\"service\", \"http://www.ofelia.com/bonita\");\n\n        assertThat(urlBuilder.build())\n                .isEqualTo(\"http://www.cas-service.com/login?service=http%3A%2F%2Fwww.ofelia.com%2Fbonita#_pf=2\");\n    }\n\n    @Test\n    public void appendParameter_should_add_id_params_to_URL() {\n        urlBuilder = new UrlBuilder(\"http://www.cas-service.com/login\");\n        urlBuilder.appendParameter(\"ids\", new UrlValue(\"1\", \"2\", \"3\").toString());\n\n        assertThat(urlBuilder.build()).isEqualTo(\"http://www.cas-service.com/login?ids=1%2C2%2C3\");\n    }\n\n    @Test\n    public void appendParameter_should_add_params_to_URL() {\n        Map<String, String[]> params = new HashMap<>();\n        params.put(\"service\", new String[] { \"http://www.ofelia.com/bonita\" });\n        params.put(\"ids\", new String[] { \"4\", \"5\", \"6\" });\n        urlBuilder = new UrlBuilder(\"http://www.cas-service.com/login?redirect=false\");\n        urlBuilder.appendParameters(params);\n\n        assertThat(urlBuilder.build())\n                .isEqualTo(\n                        \"http://www.cas-service.com/login?redirect=false&service=http%3A%2F%2Fwww.ofelia.com%2Fbonita&ids=4%2C5%2C6\");\n    }\n\n    @Test\n    public void appendParameter_should_not_add_params_already_defined_to_URL() {\n        urlBuilder = new UrlBuilder(\"http://www.cas-service.com/login?service=http://www.ofelia.com\");\n        urlBuilder.appendParameter(\"service\", \"http://www.ofelia.com/bonita\");\n\n        assertThat(urlBuilder.build())\n                .isEqualTo(\"http://www.cas-service.com/login?service=http%3A%2F%2Fwww.ofelia.com\");\n    }\n\n    @Test\n    public void should_add_params_to_URL_with_special_chars_in_hash() {\n        urlBuilder = new UrlBuilder(\n                \"http://localhost:8080/bonita?param=value#?form=l\" + specialULRCharactersEncoded + \"f\");\n        urlBuilder.appendParameter(\"param2\", \"value2\");\n\n        assertThat(urlBuilder.build()).isEqualTo(\n                \"http://localhost:8080/bonita?param=value&param2=value2#?form=l\" + specialULRCharactersEncoded + \"f\");\n    }\n\n    @Test\n    public void should_add_params_to_URL_with_hash() {\n        urlBuilder = new UrlBuilder(\n                \"http://localhost:8080/bonita?param=value#path/with/slash?hashparam=1&hashparam2=2\");\n        urlBuilder.appendParameter(\"param2\", \"value2\");\n\n        assertThat(urlBuilder.build()).isEqualTo(\n                \"http://localhost:8080/bonita?param=value&param2=value2#path/with/slash?hashparam=1&hashparam2=2\");\n    }\n\n    @Test\n    public void should_add_params_to_URL_with_v6_form_id_in_hash() {\n        urlBuilder = new UrlBuilder(\n                \"http://localhost:8080/bonita?param=value#form=proc%C3%A9with%23+in%20name--2.0--taskwith+%24$entry&task=25&assignTask=true\");\n        urlBuilder.appendParameter(\"param2\", \"value2\");\n\n        assertThat(urlBuilder.build()).isEqualTo(\n                \"http://localhost:8080/bonita?param=value&param2=value2#form=proc%C3%A9with%23+in%20name--2.0--taskwith+%24$entry&task=25&assignTask=true\");\n    }\n\n    @Test\n    public void should_add_params_to_URL_with_special_chars_in_query() {\n        urlBuilder = new UrlBuilder(\n                \"http://localhost:8080/bonita?param=value&form=l\" + specialULRCharactersEncoded + \"f\");\n        urlBuilder.appendParameter(\"param2\", \"value\" + specialULRCharacters + \"2\");\n\n        assertThat(urlBuilder.build()).isEqualTo(\"http://localhost:8080/bonita?param=value&form=l\"\n                + specialULRCharactersEncoded + \"f&param2=value\" + specialULRCharactersEncoded + \"2\");\n    }\n\n    @Test\n    public void should_leave_path_unchanged() {\n        urlBuilder = new UrlBuilder(\"http://localhost:8080/bonita+soft?param=value\");\n\n        assertThat(urlBuilder.build()).isEqualTo(\"http://localhost:8080/bonita+soft?param=value\");\n    }\n\n    @Test\n    public void should_leave_path_unchanged2() {\n        urlBuilder = new UrlBuilder(\"http://localhost:8080/bonita%2Bsoft?param=value\");\n\n        assertThat(urlBuilder.build()).isEqualTo(\"http://localhost:8080/bonita%2Bsoft?param=value\");\n    }\n\n    @Test\n    public void should_leave_path_with_space_unchanged() {\n        urlBuilder = new UrlBuilder(\"http://localhost:8080/bonita%20soft?param=value\");\n\n        assertThat(urlBuilder.build()).isEqualTo(\"http://localhost:8080/bonita%20soft?param=value\");\n    }\n\n    @Test\n    public void should_leave_URL_without_querystring_unchanged() {\n        urlBuilder = new UrlBuilder(\"http://localhost:8080/bonita/homepage\");\n\n        assertThat(urlBuilder.build()).isEqualTo(\"http://localhost:8080/bonita/homepage\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/server/ConsoleServiceFactoryTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.server;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.console.server.service.OrganizationImportService;\nimport org.bonitasoft.console.server.service.ProcessActorImportService;\nimport org.bonitasoft.web.toolkit.server.Service;\nimport org.bonitasoft.web.toolkit.server.ServiceNotFoundException;\nimport org.junit.Test;\n\npublic class ConsoleServiceFactoryTest {\n\n    @Test\n    public void getService_should_return_OrganizationImportService_when_organization_import() throws Exception {\n        // Given\n        ConsoleServiceFactory consoleServiceFacotry = new ConsoleServiceFactory();\n\n        // When\n        Service service = consoleServiceFacotry.getService(\"/organization/import\");\n\n        // Then\n        assertThat(service).isInstanceOf(OrganizationImportService.class);\n    }\n\n    @Test\n    public void getService_should_return_ProcessActorImportService_when_bpm_process_importActors() throws Exception {\n        // Given\n        ConsoleServiceFactory consoleServiceFacotry = new ConsoleServiceFactory();\n\n        // When\n        Service service = consoleServiceFacotry.getService(\"/bpm/process/importActors\");\n\n        // Then\n        assertThat(service).isInstanceOf(ProcessActorImportService.class);\n    }\n\n    @Test(expected = ServiceNotFoundException.class)\n    public void getService_should_throw_ServiceNotFoundException_when_invalid_input() {\n        // Given\n        ConsoleServiceFactory consoleServiceFacotry = new ConsoleServiceFactory();\n\n        // When\n        consoleServiceFacotry.getService(\"invalidService\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/server/service/ApplicationsImportServiceTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.server.service;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.verify;\n\nimport java.util.ArrayList;\n\nimport org.bonitasoft.console.common.server.i18n.I18n;\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.engine.api.ApplicationAPI;\nimport org.bonitasoft.engine.api.ImportStatus;\nimport org.bonitasoft.engine.business.application.ApplicationImportPolicy;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.bonitasoft.web.common.model.ImportStatusMessages;\nimport org.bonitasoft.web.rest.server.BonitaRestAPIServlet;\nimport org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n.LOCALE;\nimport org.junit.Before;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.Mockito;\nimport org.mockito.MockitoAnnotations;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Julien Mege\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class ApplicationsImportServiceTest {\n\n    @Mock\n    private ApplicationAPI applicationAPI;\n\n    @Mock\n    private BonitaHomeFolderAccessor tenantFolder;\n\n    @Spy\n    private ApplicationsImportService spiedApplicationImportService;\n\n    @BeforeClass\n    public static void initEnvironment() {\n        I18n.getInstance();\n        new BonitaRestAPIServlet();\n    }\n\n    @Before\n    public void init()\n            throws InvalidSessionException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        MockitoAnnotations.initMocks(this);\n        doReturn(LOCALE.en).when(spiedApplicationImportService).getLocale();\n        Mockito.doReturn(applicationAPI).when(spiedApplicationImportService).getApplicationAPI();\n    }\n\n    @Test\n    public void should_importFileContent_call_ApplicationAPI_with_valid_param() throws Exception {\n        spiedApplicationImportService.importFileContent(new byte[0], \"FAIL_ON_DUPLICATES\");\n        verify(applicationAPI).importApplications(new byte[0], ApplicationImportPolicy.FAIL_ON_DUPLICATES);\n    }\n\n    @Test\n    public void should_importFileContent_return_ImportStatusMessages() throws Exception {\n        final ArrayList<ImportStatus> statusList = new ArrayList<>();\n        statusList.add(new ImportStatus(\"status\"));\n        doReturn(statusList).when(applicationAPI).importApplications(new byte[0],\n                ApplicationImportPolicy.FAIL_ON_DUPLICATES);\n\n        final ImportStatusMessages importStatusMessages = spiedApplicationImportService.importFileContent(new byte[0],\n                \"FAIL_ON_DUPLICATES\");\n        assertEquals(importStatusMessages.getImported().size(), 1);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void should_importFileContent_with_invalid_policy_throw_error() throws Exception {\n        spiedApplicationImportService.importFileContent(new byte[0], \"NOT_AUTHORIZED_POLICY\");\n    }\n\n    @Test\n    public void should_Logger_log_using_expected_class_name() {\n        assertEquals(spiedApplicationImportService.getLogger().getName(),\n                \"org.bonitasoft.console.server.service.ApplicationsImportService\");\n    }\n\n    @Test\n    public void should_FileReadingError_talk_about_application() {\n        assertEquals(spiedApplicationImportService.getFileReadingError(),\n                \"Error during Application import file reading.\");\n    }\n\n    @Test\n    public void should_getFileFormatExceptionMessage_talk_about_application() {\n        assertEquals(spiedApplicationImportService.getFileFormatExceptionMessage(), \"Can't import Applications.\");\n    }\n\n    @Test\n    public void should_AlreadyExistsExceptionMessage_talk_about_application() {\n        final AlreadyExistsException alreadyExistsException = new AlreadyExistsException(\"name\", \"token\");\n        assertEquals(spiedApplicationImportService.getAlreadyExistsExceptionMessage(alreadyExistsException),\n                \"Can't import applications. An application 'token' already exists\");\n    }\n\n    @Test\n    public void should_getToken_return_expected_name() {\n        assertEquals(spiedApplicationImportService.getToken(), \"/application/import\");\n    }\n\n    @Test\n    public void should_getFileUploadParamName_return_expected_name() {\n        assertEquals(spiedApplicationImportService.getFileUploadParamName(), \"applicationsDataUpload\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/server/service/OrganizationImportServiceTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.server.service;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.Mockito.*;\n\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.bonitasoft.console.common.server.i18n.I18n;\nimport org.bonitasoft.engine.api.IdentityAPI;\nimport org.bonitasoft.engine.identity.ImportPolicy;\nimport org.bonitasoft.engine.identity.InvalidOrganizationFileFormatException;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.bonitasoft.web.toolkit.server.ServiceException;\nimport org.junit.Before;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Julien Mege\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class OrganizationImportServiceTest {\n\n    @Mock\n    IdentityAPI identityAPI;\n    @Mock\n    HttpServletResponse httpServletResponse;\n    private OrganizationImportService organizationImportService;\n\n    @BeforeClass\n    public static void setup() {\n        I18n.getInstance();\n    }\n\n    @Before\n    public void before() throws Exception {\n        organizationImportService = spy(new OrganizationImportService());\n        doReturn(httpServletResponse).when(organizationImportService).getHttpResponse();\n        doReturn(identityAPI).when(organizationImportService).getIdentityAPI();\n    }\n\n    @Test(expected = ServiceException.class)\n    public void should_generate_401_when_session_expires() throws Exception {\n        doReturn(new byte[0]).when(organizationImportService).getOrganizationContent(any());\n        doReturn(\"MERGE_DUPLICATES\").when(organizationImportService).getParameter(anyString());\n\n        doThrow(new InvalidSessionException(\"session expired\")).when(identityAPI)\n                .importOrganizationWithWarnings(anyString(), any());\n\n        try {\n            organizationImportService.run();\n        } finally {\n            verify(httpServletResponse).setStatus(HttpServletResponse.SC_UNAUTHORIZED);\n        }\n    }\n\n    @Test(expected = ServiceException.class)\n    public void should_generate_400_when_file_is_invalid() throws Exception {\n        doReturn(new byte[0]).when(organizationImportService).getOrganizationContent(any());\n        doReturn(\"MERGE_DUPLICATES\").when(organizationImportService).getParameter(anyString());\n        doThrow(new InvalidOrganizationFileFormatException(\"invalid format\")).when(identityAPI)\n                .importOrganizationWithWarnings(anyString(), any());\n\n        try {\n            organizationImportService.run();\n        } finally {\n            verify(httpServletResponse).setStatus(HttpServletResponse.SC_BAD_REQUEST);\n        }\n    }\n\n    @Test\n    public void should_import_organization_with_specified_policy() throws Exception {\n        doReturn(\"organizationXmlContent\".getBytes()).when(organizationImportService).getOrganizationContent(any());\n        doReturn(\"IGNORE_DUPLICATES\").when(organizationImportService).getParameter(\"importPolicy\");\n        doNothing().when(organizationImportService).cleanTempContent(any());\n\n        organizationImportService.run();\n\n        verify(identityAPI).importOrganizationWithWarnings(\"organizationXmlContent\", ImportPolicy.IGNORE_DUPLICATES);\n    }\n\n    @Test\n    public void should_import_organization_with_default_policy() throws Exception {\n        doReturn(\"organizationXmlContent\".getBytes()).when(organizationImportService).getOrganizationContent(any());\n        doReturn(null).when(organizationImportService).getParameter(\"importPolicy\");\n        doNothing().when(organizationImportService).cleanTempContent(any());\n\n        organizationImportService.run();\n\n        verify(identityAPI).importOrganizationWithWarnings(\"organizationXmlContent\", ImportPolicy.MERGE_DUPLICATES);\n    }\n\n    @Test(expected = ServiceException.class)\n    public void should_throw_an_error_when_provided_policy_is_not_valid() throws Exception {\n        doReturn(\"organizationXmlContent\".getBytes()).when(organizationImportService).getOrganizationContent(any());\n        doReturn(\"INVALID\").when(organizationImportService).getParameter(\"importPolicy\");\n        doNothing().when(organizationImportService).cleanTempContent(any());\n\n        try {\n            organizationImportService.run();\n        } finally {\n            verify(httpServletResponse).setStatus(HttpServletResponse.SC_BAD_REQUEST);\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/server/servlet/ApplicationsExportServletTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.server.servlet;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\nimport org.bonitasoft.console.common.server.i18n.I18n;\nimport org.bonitasoft.engine.api.ApplicationAPI;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.junit.Before;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\nimport org.mockito.Mock;\nimport org.mockito.Mockito;\nimport org.mockito.MockitoAnnotations;\nimport org.mockito.Spy;\n\npublic class ApplicationsExportServletTest {\n\n    @Mock\n    APISession session;\n\n    @Mock\n    private ApplicationAPI applicationAPI;\n\n    @Spy\n    private ApplicationsExportServlet spiedApplicationsExportServlet;\n\n    @BeforeClass\n    public static void initEnvironnement() {\n        I18n.getInstance();\n    }\n\n    @Before\n    public void init()\n            throws InvalidSessionException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        MockitoAnnotations.initMocks(this);\n        Mockito.doReturn(applicationAPI).when(spiedApplicationsExportServlet).getApplicationAPI(any(APISession.class));\n    }\n\n    @Test\n    public void should_call_engine_export_with_the_good_id() throws Exception {\n\n        spiedApplicationsExportServlet.exportResources(new long[] { 1 }, session);\n\n        verify(applicationAPI, times(1)).exportApplications(new long[] { 1 });\n    }\n\n    @Test\n    public void should_logger_log_with_name_of_the_Servlet() throws Exception {\n        assertThat(spiedApplicationsExportServlet.getLogger().getName())\n                .isEqualTo(\"org.bonitasoft.console.server.servlet.ApplicationsExportServlet\");\n    }\n\n    @Test\n    public void should_getFileExportName_return_a_valide_file_name() throws Exception {\n        assertThat(spiedApplicationsExportServlet.getFileExportName()).isEqualTo(\"applicationDescriptorFile.xml\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/console/server/servlet/BonitaExportServletTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.console.server.servlet;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.console.common.server.i18n.I18n;\nimport org.bonitasoft.engine.api.ApplicationAPI;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.junit.Before;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\nimport org.mockito.Mock;\nimport org.mockito.Mockito;\nimport org.mockito.MockitoAnnotations;\nimport org.mockito.Spy;\n\npublic class BonitaExportServletTest {\n\n    @Mock\n    HttpServletRequest hsRequest;\n\n    @Mock\n    HttpServletResponse hsResponse;\n\n    @Mock\n    HttpSession httpSession;\n\n    @Mock\n    private ApplicationAPI applicationAPI;\n\n    @Spy\n    private ApplicationsExportServlet spiedApplicationsExportServlet;\n\n    @BeforeClass\n    public static void initEnvironnement() {\n        I18n.getInstance();\n    }\n\n    @Before\n    public void init()\n            throws InvalidSessionException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        MockitoAnnotations.initMocks(this);\n        Mockito.doReturn(applicationAPI).when(spiedApplicationsExportServlet).getApplicationAPI(any(APISession.class));\n    }\n\n    @Test\n    public void should_getResourcesAsList_convert_ids_param_into_a_list() throws Exception {\n\n        given(hsRequest.getParameter(\"id\")).willReturn(\"1,2,3,4\");\n\n        final long[] returnedList = spiedApplicationsExportServlet.getResourcesAsList(hsRequest);\n\n        assertThat(returnedList).isEqualTo(new long[] { 1, 2, 3, 4 });\n    }\n\n    @Test\n    public void should_getResourcesAsList_convert_one_id_param_into_a_list() throws Exception {\n\n        given(hsRequest.getParameter(\"id\")).willReturn(\"1\");\n\n        final long[] returnedList = spiedApplicationsExportServlet.getResourcesAsList(hsRequest);\n\n        assertThat(returnedList).isEqualTo(new long[] { 1 });\n    }\n\n    @Test\n    public void should_getResourcesAsList_convert_null_param_into_empty_list() throws Exception {\n\n        given(hsRequest.getParameter(\"id\")).willReturn(null);\n        try {\n            spiedApplicationsExportServlet.getResourcesAsList(hsRequest);\n        } catch (final Exception e) {\n            assertThat(e.getMessage()).isEqualTo(\"Request parameter \\\"id\\\" must be set.\");\n        }\n\n    }\n\n    @Test(expected = ServletException.class)\n    public void should_do_get_without_session_return_servlet_exception() throws Exception {\n\n        given(hsRequest.getSession()).willReturn(httpSession);\n        given(httpSession.getAttribute(\"apiSession\")).willReturn(null);\n\n        spiedApplicationsExportServlet.doGet(hsRequest, hsResponse);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/livingapps/ApplicationModelFactoryTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.livingapps;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\nimport org.bonitasoft.engine.api.ApplicationAPI;\nimport org.bonitasoft.engine.business.application.ApplicationLink;\nimport org.bonitasoft.engine.business.application.ApplicationNotFoundException;\nimport org.bonitasoft.engine.business.application.impl.ApplicationImpl;\nimport org.bonitasoft.engine.business.application.impl.ApplicationPageImpl;\nimport org.bonitasoft.livingapps.exception.CreationException;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Answers;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ApplicationModelFactoryTest {\n\n    @Mock(answer = Answers.RETURNS_MOCKS)\n    ApplicationAPI applicationApi;\n\n    @InjectMocks\n    ApplicationModelFactory factory;\n\n    @Test(expected = CreationException.class)\n    public void should_throw_create_error_exception_when_search_fail() throws Exception {\n        given(applicationApi.getIApplicationByToken(any(String.class))).willThrow(ApplicationNotFoundException.class);\n\n        factory.createApplicationModel(\"foo\");\n    }\n\n    @Test(expected = CreationException.class)\n    public void should_throw_create_error_exception_when_only_application_link_is_found() throws Exception {\n        given(applicationApi.getIApplicationByToken(any(String.class))).willReturn(mock(ApplicationLink.class));\n\n        factory.createApplicationModel(\"foo\");\n    }\n\n    @Test\n    public void should_return_application_found() throws Exception {\n        final ApplicationImpl application = new ApplicationImpl(\"foobar\", \"1.0\", \"bazqux\");\n        application.setId(3);\n        given(applicationApi.getIApplicationByToken(any(String.class))).willReturn(application);\n        given(applicationApi.getApplicationHomePage(3)).willReturn(new ApplicationPageImpl(1, 1, \"home\"));\n\n        final ApplicationModel model = factory.createApplicationModel(\"foo\");\n\n        assertThat(model.getApplicationHomePage()).isEqualTo(\"home/\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/livingapps/ApplicationModelTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.livingapps;\n\nimport static java.util.Arrays.asList;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verify;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.ApplicationAPI;\nimport org.bonitasoft.engine.api.PageAPI;\nimport org.bonitasoft.engine.api.ProfileAPI;\nimport org.bonitasoft.engine.business.application.ApplicationMenu;\nimport org.bonitasoft.engine.business.application.ApplicationMenuSearchDescriptor;\nimport org.bonitasoft.engine.business.application.ApplicationPageNotFoundException;\nimport org.bonitasoft.engine.business.application.ApplicationVisibility;\nimport org.bonitasoft.engine.business.application.impl.ApplicationImpl;\nimport org.bonitasoft.engine.business.application.impl.ApplicationMenuImpl;\nimport org.bonitasoft.engine.business.application.impl.ApplicationPageImpl;\nimport org.bonitasoft.engine.page.ContentType;\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.engine.page.impl.PageImpl;\nimport org.bonitasoft.engine.profile.Profile;\nimport org.bonitasoft.engine.profile.ProfileCriterion;\nimport org.bonitasoft.engine.profile.impl.ProfileImpl;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.Sort;\nimport org.bonitasoft.engine.search.impl.SearchFilter;\nimport org.bonitasoft.engine.search.impl.SearchResultImpl;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.livingapps.menu.MenuFactory;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ApplicationModelTest {\n\n    @Mock\n    ApplicationAPI applicationApi;\n\n    @Mock\n    PageAPI pageApi;\n\n    @Mock\n    Page page;\n\n    @Mock\n    APISession session;\n\n    @Mock\n    private MenuFactory factory;\n\n    @Mock\n    private ProfileAPI profileApi;\n\n    ApplicationModel model;\n\n    ApplicationImpl application = new ApplicationImpl(\"token\", \"version\", \"description\", 1L, 2L);\n\n    @Before\n    public void beforeEach() throws Exception {\n        application.setId(1L);\n        model = new ApplicationModel(applicationApi, pageApi, profileApi, application, factory);\n    }\n\n    @Test\n    public void should_filter_application_menu_search_for_the_given_application() throws Exception {\n        givenSearchApplicationMenusWillReturns(Collections.<ApplicationMenu> emptyList());\n\n        model.getMenuList();\n        final ArgumentCaptor<SearchOptions> captor = ArgumentCaptor.forClass(SearchOptions.class);\n        verify(applicationApi).searchApplicationMenus(captor.capture());\n\n        final SearchFilter filter = captor.getValue().getFilters().get(0);\n        assertThat(filter.getField()).isEqualTo(ApplicationMenuSearchDescriptor.APPLICATION_ID);\n        assertThat(filter.getValue()).isEqualTo(application.getId());\n    }\n\n    @Test\n    public void should_sort_application_menu_search() throws Exception {\n        givenSearchApplicationMenusWillReturns(Collections.<ApplicationMenu> emptyList());\n\n        model.getMenuList();\n        final ArgumentCaptor<SearchOptions> captor = ArgumentCaptor.forClass(SearchOptions.class);\n        verify(applicationApi).searchApplicationMenus(captor.capture());\n\n        final Sort sort = captor.getValue().getSorts().get(0);\n        assertThat(sort.getField()).isEqualTo(ApplicationMenuSearchDescriptor.INDEX);\n        assertThat(sort.getOrder()).isEqualTo(Order.ASC);\n    }\n\n    @Test\n    public void should_create_menu_using_menuList() throws Exception {\n        final List<ApplicationMenu> menuList = Arrays\n                .<ApplicationMenu> asList(new ApplicationMenuImpl(\"name\", 1L, 2L, 1));\n        givenSearchApplicationMenusWillReturns(menuList);\n\n        model.getMenuList();\n\n        verify(factory).create(menuList);\n    }\n\n    @Test\n    public void should_authorize_a_user_with_the_configured_application_profile() throws Exception {\n        final ProfileImpl profile1 = new ProfileImpl(\"user\");\n        profile1.setId(1L);\n        final ProfileImpl profile2 = new ProfileImpl(\"administrator\");\n        profile1.setId(2L);\n        given(profileApi.getProfilesForUser(1L, 0, Integer.MAX_VALUE, ProfileCriterion.ID_ASC))\n                .willReturn(asList((Profile) profile1, profile2));\n        given(session.getUserId()).willReturn(1L);\n        application.setProfileId(2L);\n\n        assertThat(model.authorize(session)).isTrue();\n    }\n\n    @Test\n    public void should_not_authorize_a_user_without_the_configured_application_profile() throws Exception {\n        final ProfileImpl profile1 = new ProfileImpl(\"user\");\n        profile1.setId(1L);\n        final ProfileImpl profile2 = new ProfileImpl(\"administrator\");\n        profile1.setId(2L);\n        given(profileApi.getProfilesForUser(1L, 0, Integer.MAX_VALUE, ProfileCriterion.ID_ASC))\n                .willReturn(asList((Profile) profile1, profile2));\n        given(session.getUserId()).willReturn(1L);\n        application.setProfileId(3L);\n\n        assertThat(model.authorize(session)).isFalse();\n    }\n\n    private void givenSearchApplicationMenusWillReturns(final List<ApplicationMenu> menuList) throws Exception {\n        given(applicationApi.searchApplicationMenus(any(SearchOptions.class))).willReturn(\n                new SearchResultImpl<>(menuList.size(), menuList));\n    }\n\n    @Test\n    public void should_getId_return_applicationId() throws Exception {\n        assertThat(model.getId()).isEqualTo(1L);\n    }\n\n    @Test\n    public void should_ApplicationHomePage_return_valide_path() throws Exception {\n        given(applicationApi.getApplicationHomePage(1L)).willReturn(new ApplicationPageImpl(1, 1, \"pageToken\"));\n\n        assertThat(model.getApplicationHomePage()).isEqualTo(\"pageToken/\");\n    }\n\n    @Test\n    public void should_getApplicationLayoutName_return_valide_name() throws Exception {\n        given(page.getName()).willReturn(\"layoutPage\");\n        given(pageApi.getPage(1L)).willReturn(page);\n\n        String appLayoutName = model.getApplicationLayoutName();\n\n        assertThat(appLayoutName).isEqualTo(\"layoutPage\");\n    }\n\n    @Test\n    public void should_getApplicationThemeName_return_valide_name() throws Exception {\n        given(page.getName()).willReturn(\"themePage\");\n        given(pageApi.getPage(2L)).willReturn(page);\n\n        String appLayoutName = model.getApplicationThemeName();\n\n        assertThat(appLayoutName).isEqualTo(\"themePage\");\n    }\n\n    @Test\n    public void should_hasPage_return_true() throws Exception {\n        given(applicationApi.getApplicationPage(\"token\", \"pageToken\"))\n                .willReturn(new ApplicationPageImpl(1, 1, \"pageToken\"));\n\n        assertThat(model.hasPage(\"pageToken\")).isTrue();\n    }\n\n    @Test\n    public void should_hasPage_return_false() throws Exception {\n        given(applicationApi.getApplicationPage(\"token\", \"pageToken\"))\n                .willThrow(new ApplicationPageNotFoundException(\"\"));\n\n        assertThat(model.hasPage(\"pageToken\")).isFalse();\n    }\n\n    @Test\n    public void should_getCustomPage_return_expectedPage() throws Exception {\n        given(applicationApi.getApplicationPage(\"token\", \"pageToken\"))\n                .willReturn(new ApplicationPageImpl(1, 1, \"pageToken\"));\n        given(pageApi.getPage(1))\n                .willReturn(new PageImpl(1, \"\", \"\", false, \"\", 0L, 0L, 0L, 0L, \"\", ContentType.PAGE, null));\n\n        assertThat(model.getCustomPage(\"pageToken\").getId()).isEqualTo(1);\n    }\n\n    @Test\n    public void should_check_that_application_has_a_profile_mapped_to_it() throws Exception {\n        application.setProfileId(1L);\n        application.setVisibility(ApplicationVisibility.RESTRICTED);\n        assertThat(model.hasProfileMapped()).isTrue();\n\n        application.setProfileId(null);\n        assertThat(model.hasProfileMapped()).isFalse();\n\n        application.setVisibility(ApplicationVisibility.ALL);\n        assertThat(model.hasProfileMapped()).isTrue();\n\n        application.setVisibility(ApplicationVisibility.TECHNICAL_USER);\n        assertThat(model.hasProfileMapped()).isTrue();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/livingapps/ApplicationRouterTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.livingapps;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\n\nimport java.io.File;\nimport java.util.Arrays;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.bonitasoft.console.common.server.page.PageRenderer;\nimport org.bonitasoft.console.common.server.page.ResourceRenderer;\nimport org.bonitasoft.console.common.server.page.extension.PageResourceProviderImpl;\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.engine.session.APISession;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Answers;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ApplicationRouterTest {\n\n    public static final String LAYOUT_PAGE_NAME = \"layoutPageName\";\n    @Mock(answer = Answers.RETURNS_MOCKS)\n    HttpServletRequest hsRequest;\n\n    @Mock\n    HttpServletResponse hsResponse;\n\n    @Mock\n    APISession apiSession;\n\n    @Mock\n    ApplicationModelFactory applicationModelFactory;\n\n    @Mock\n    ApplicationModel applicationModel;\n\n    @Mock\n    PageRenderer pageRenderer;\n\n    @Mock\n    PageResourceProviderImpl pageResourceProvider;\n\n    @Spy\n    @InjectMocks\n    ResourceRenderer resourceRenderer;\n\n    @Mock\n    BonitaHomeFolderAccessor bonitaHomeFolderAccessor;\n\n    @InjectMocks\n    ApplicationRouter applicationRouter;\n\n    @Before\n    public void beforeEach() throws Exception {\n        given(hsRequest.getMethod()).willReturn(\"GET\");\n        given(hsRequest.getContextPath()).willReturn(\"/bonita\");\n        given(hsRequest.getServletPath()).willReturn(\"/apps\");\n    }\n\n    @Test\n    public void should_redirect_to_home_page_when_accessing_living_application_root() throws Exception {\n        given(applicationModel.getApplicationHomePage()).willReturn(\"home/\");\n        given(applicationModel.hasProfileMapped()).willReturn(true);\n        given(applicationModelFactory.createApplicationModel(\"HumanResources\")).willReturn(applicationModel);\n        given(hsRequest.getPathInfo()).willReturn(\"/HumanResources\");\n        given(resourceRenderer.getPathSegments(\"/HumanResources\")).willReturn(Arrays.asList(\"HumanResources\"));\n\n        applicationRouter.route(hsRequest, hsResponse, apiSession, pageRenderer, resourceRenderer,\n                bonitaHomeFolderAccessor);\n        verify(hsResponse).sendRedirect(\"home/\");\n    }\n\n    @Test\n    public void should_redirect_to_home_page_with_query_parameters() throws Exception {\n        given(applicationModel.getApplicationHomePage()).willReturn(\"home/\");\n        given(applicationModel.hasProfileMapped()).willReturn(true);\n        given(applicationModelFactory.createApplicationModel(\"HumanResources\")).willReturn(applicationModel);\n        given(hsRequest.getQueryString()).willReturn(\"time=12:00\");\n        given(hsRequest.getPathInfo()).willReturn(\"/HumanResources\");\n        given(resourceRenderer.getPathSegments(\"/HumanResources\")).willReturn(Arrays.asList(\"HumanResources\"));\n\n        applicationRouter.route(hsRequest, hsResponse, apiSession, pageRenderer, resourceRenderer,\n                bonitaHomeFolderAccessor);\n        verify(hsResponse).sendRedirect(\"home/?time=12:00\");\n    }\n\n    @Test(expected = RuntimeException.class)\n    public void should_throw_an_error_when_the_uri_is_malformed() throws Exception {\n\n        applicationRouter.route(hsRequest, hsResponse, apiSession, pageRenderer, resourceRenderer,\n                bonitaHomeFolderAccessor);\n    }\n\n    @Test\n    public void should_display_layout_page() throws Exception {\n        accessAuthorizedPage(\"HumanResources\", \"leavingRequests\");\n\n        applicationRouter.route(hsRequest, hsResponse, apiSession, pageRenderer, resourceRenderer,\n                bonitaHomeFolderAccessor);\n\n        verify(pageRenderer).displayCustomPage(hsRequest, hsResponse, apiSession,\n                applicationModel.getApplicationLayoutName());\n    }\n\n    @Test\n    public void should_access_Layout_resource() throws Exception {\n        accessAuthorizedPage(\"HumanResources\", \"layout/css/file.css\");\n        final File layoutFolder = new File(\"layout\");\n        final String customPageLayoutName = \"custompage_layout\";\n        given(applicationModel.getApplicationLayoutName()).willReturn(customPageLayoutName);\n\n        given(pageRenderer.getPageResourceProvider(customPageLayoutName)).willReturn(pageResourceProvider);\n        given(pageResourceProvider.getPageDirectory()).willReturn(layoutFolder);\n        given(bonitaHomeFolderAccessor.isInFolder(any(File.class), any(File.class))).willReturn(true);\n\n        applicationRouter.route(hsRequest, hsResponse, apiSession, pageRenderer, resourceRenderer,\n                bonitaHomeFolderAccessor);\n\n        verify(pageRenderer).ensurePageFolderIsPresent(apiSession, pageResourceProvider);\n        verify(resourceRenderer).renderFile(hsRequest, hsResponse, new File(\"layout/resources/css/file.css\"));\n    }\n\n    @Test\n    public void should_access_Theme_resource() throws Exception {\n        accessAuthorizedPage(\"HumanResources\", \"theme/css/file.css\");\n        final File themeFolder = new File(\"theme\");\n        final String customPageThemeName = \"custompage_theme\";\n        given(applicationModel.getApplicationThemeName()).willReturn(customPageThemeName);\n\n        given(pageRenderer.getPageResourceProvider(customPageThemeName)).willReturn(pageResourceProvider);\n        given(pageResourceProvider.getPageDirectory()).willReturn(themeFolder);\n        given(bonitaHomeFolderAccessor.isInFolder(any(File.class), any(File.class))).willReturn(true);\n\n        applicationRouter.route(hsRequest, hsResponse, apiSession, pageRenderer, resourceRenderer,\n                bonitaHomeFolderAccessor);\n\n        verify(pageRenderer).ensurePageFolderIsPresent(apiSession, pageResourceProvider);\n        verify(resourceRenderer).renderFile(hsRequest, hsResponse, new File(\"theme/resources/css/file.css\"));\n    }\n\n    @Test\n    public void should_not_forward_to_the_application_page_template_when_the_page_is_not_in_the_application()\n            throws Exception {\n        accessUnknownPage(\"HumanResources\", \"leavingRequests\");\n\n        applicationRouter.route(hsRequest, hsResponse, apiSession, pageRenderer, resourceRenderer,\n                bonitaHomeFolderAccessor);\n\n        verify(hsResponse).sendError(HttpServletResponse.SC_NOT_FOUND,\n                \"There is no page \" + \"leavingRequests\" + \" in the application \" + \"HumanResources\");\n        verify(pageRenderer, never()).displayCustomPage(hsRequest, hsResponse, apiSession, LAYOUT_PAGE_NAME);\n    }\n\n    @Test\n    public void should_not_forward_to_the_application_page_template_when_user_is_not_authorized() throws Exception {\n        accessUnauthorizedPage(\"HumanResources\", \"leavingRequests\");\n\n        applicationRouter.route(hsRequest, hsResponse, apiSession, pageRenderer, resourceRenderer,\n                bonitaHomeFolderAccessor);\n\n        verify(hsResponse).sendError(HttpServletResponse.SC_FORBIDDEN,\n                \"Unauthorized access for the page \" + \"leavingRequests\" + \" of the application \" + \"HumanResources\");\n        verify(pageRenderer, never()).displayCustomPage(hsRequest, hsResponse, apiSession, LAYOUT_PAGE_NAME);\n    }\n\n    @Test\n    public void should_send_not_found_response_when_no_profile_is_mapped_to_application() throws Exception {\n        accessAuthorizedPage(\"HumanResources\", \"leavingRequests\");\n        given(applicationModel.hasProfileMapped()).willReturn(false);\n\n        applicationRouter.route(hsRequest, hsResponse, apiSession, pageRenderer, resourceRenderer,\n                bonitaHomeFolderAccessor);\n\n        verify(hsResponse).sendError(HttpServletResponse.SC_NOT_FOUND, \"No profile mapped to living application\");\n        verify(pageRenderer, never()).displayCustomPage(hsRequest, hsResponse, apiSession, LAYOUT_PAGE_NAME);\n    }\n\n    @Test\n    public void should_not_authorize_API_requests_to_other_paths() throws Exception {\n        accessAuthorizedPage(\"HumanResources\", \"API\");\n        String unauthorizedPath = \"/API/living/../../WEB-INF/web.xml\";\n        given(hsRequest.getPathInfo()).willReturn(\"/HumanResources\" + unauthorizedPath);\n\n        applicationRouter.route(hsRequest, hsResponse, apiSession, pageRenderer, resourceRenderer,\n                bonitaHomeFolderAccessor);\n\n        verify(hsResponse).setStatus(HttpServletResponse.SC_FORBIDDEN);\n        verify(hsRequest, never()).getRequestDispatcher(anyString());\n    }\n\n    private void accessAuthorizedPage(final String applicationToken, final String pageToken) throws Exception {\n        accessPage(applicationToken, pageToken, true, true);\n    }\n\n    private void accessUnauthorizedPage(final String applicationToken, final String pageToken) throws Exception {\n        accessPage(applicationToken, pageToken, true, false);\n    }\n\n    private void accessUnknownPage(final String applicationToken, final String pageToken) throws Exception {\n        accessPage(applicationToken, pageToken, false, false);\n    }\n\n    private void accessPage(final String applicationToken, final String pageToken, final boolean hasPage,\n            final boolean isAuthorized) throws Exception {\n        given(applicationModel.hasPage(pageToken)).willReturn(hasPage);\n        given(applicationModel.authorize(apiSession)).willReturn(isAuthorized);\n        given(applicationModel.hasProfileMapped()).willReturn(true);\n        given(applicationModelFactory.createApplicationModel(applicationToken)).willReturn(applicationModel);\n        given(hsRequest.getPathInfo()).willReturn(\"/\" + applicationToken + \"/\" + pageToken + \"/\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/livingapps/LivingApplicationPageServletTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.livingapps;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.*;\n\nimport java.io.File;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.console.common.server.page.ApplicationAuthorizationsHelper;\nimport org.bonitasoft.console.common.server.page.PageRenderer;\nimport org.bonitasoft.console.common.server.page.ResourceRenderer;\nimport org.bonitasoft.console.common.server.page.extension.PageResourceProviderImpl;\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.engine.api.ApplicationAPI;\nimport org.bonitasoft.engine.api.PageAPI;\nimport org.bonitasoft.engine.business.application.ApplicationPage;\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.web.util.UriUtils;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class LivingApplicationPageServletTest {\n\n    @Spy\n    MockHttpServletRequest hsRequest = new MockHttpServletRequest();\n\n    @Mock\n    MockHttpServletResponse hsResponse = new MockHttpServletResponse();\n\n    @Mock\n    HttpSession httpSession;\n\n    @Mock\n    APISession apiSession;\n\n    @Mock\n    ApplicationAuthorizationsHelper customPageAuthorizationsHelper;\n\n    @Mock\n    PageRenderer pageRenderer;\n\n    @Mock\n    ResourceRenderer resourceRenderer;\n\n    @Mock\n    PageResourceProviderImpl pageResourceProvider;\n\n    @Mock\n    BonitaHomeFolderAccessor bonitaHomeFolderAccessor;\n\n    @Mock\n    ApplicationAPI applicationAPI;\n\n    @Mock\n    Page page;\n\n    @Mock\n    ApplicationPage applicationPage;\n\n    @Mock\n    PageAPI pageAPI;\n\n    @Spy\n    @InjectMocks\n    LivingApplicationPageServlet servlet;\n\n    @Before\n    public void beforeEach() throws Exception {\n        hsRequest.setSession(httpSession);\n        hsRequest.setMethod(\"GET\");\n        doReturn(apiSession).when(httpSession).getAttribute(\"apiSession\");\n        doReturn(applicationAPI).when(servlet).getApplicationApi(apiSession);\n        doReturn(pageAPI).when(servlet).getPageApi(apiSession);\n        doReturn(customPageAuthorizationsHelper).when(servlet).getCustomPageAuthorizationsHelper(apiSession);\n    }\n\n    @Test\n    public void should_get_Forbidden_Status_when_page_unAuthorize() throws Exception {\n        hsRequest.setPathInfo(\"/AppToken/pageToken/content/\");\n        given(resourceRenderer.getPathSegments(\"/AppToken/pageToken/content/\"))\n                .willReturn(Arrays.asList(\"AppToken\", \"pageToken\", \"content\"));\n        given(customPageAuthorizationsHelper.isAuthorized(\"AppToken\")).willReturn(false);\n\n        doReturn(applicationPage).when(applicationAPI).getApplicationPage(\"AppToken\", \"pageToken\");\n        doReturn(2L).when(applicationPage).getPageId();\n        doReturn(page).when(pageAPI).getPage(2L);\n        doReturn(\"customPageName\").when(page).getName();\n\n        servlet.service(hsRequest, hsResponse);\n\n        verify(hsResponse).sendError(403, \"User not Authorized\");\n    }\n\n    @Test\n    public void should_get_Unauthorized_Status_when_Invalid_session() throws Exception {\n        hsRequest.setPathInfo(\"/AppToken/pageToken/content/\");\n        given(resourceRenderer.getPathSegments(\"/AppToken/pageToken/content/\"))\n                .willReturn(Arrays.asList(\"AppToken\", \"pageToken\", \"content\"));\n\n        doThrow(new InvalidSessionException(\"invalid session\")).when(applicationAPI).getApplicationPage(\"AppToken\",\n                \"pageToken\");\n\n        servlet.service(hsRequest, hsResponse);\n\n        verify(hsResponse).sendError(401, \"Invalid Bonita engine session.\");\n    }\n\n    @Test\n    public void should_get_badRequest_Status_when_page_name_is_not_set() throws Exception {\n        hsRequest.setPathInfo(\"/AppToken/content/\");\n\n        servlet.service(hsRequest, hsResponse);\n\n        verify(hsResponse).setStatus(400);\n    }\n\n    @Test\n    public void should_redirect_to_valid_url_on_missing_slash() throws Exception {\n        String targetURL = \"/bonita/apps/AppToken/pageToken/content/\";\n        doReturn(targetURL).when(hsResponse).encodeRedirectURL(targetURL);\n        hsRequest.setContextPath(\"/bonita\");\n        hsRequest.setServletPath(\"/apps\");\n        hsRequest.setPathInfo(\"/AppToken/pageToken/content\");\n\n        servlet.service(hsRequest, hsResponse);\n\n        verify(hsResponse).encodeRedirectURL(targetURL);\n        verify(hsResponse).sendRedirect(targetURL);\n    }\n\n    @Test\n    public void getPage_should_call_the_page_renderer() throws Exception {\n        testPageIsWellCalled(\"AppToken\", \"htmlexample1\", \"/AppToken/htmlexample1/content/\",\n                Arrays.asList(\"AppToken\", \"htmlexample1\", \"content\"));\n        testPageIsWellCalled(\"AppToken\", \"htmlexample2\", \"/AppToken/htmlexample2/content/index\",\n                Arrays.asList(\"AppToken\", \"htmlexample2\", \"content\", \"index\"));\n        testPageIsWellCalled(\"AppToken\", \"htmlexample4\", \"/AppToken/htmlexample4/content/index.html\",\n                Arrays.asList(\"AppToken\", \"htmlexample4\", \"content\", \"index.html\"));\n        testPageIsWellCalled(\"AppToken\", \"htmlexample5\", \"/AppToken/htmlexample5/content/Index.groovy\",\n                Arrays.asList(\"AppToken\", \"htmlexample5\", \"content\", \"Index.groovy\"));\n    }\n\n    private void testPageIsWellCalled(final String appToken, final String pageToken, final String path,\n            final List<String> pathSegment) throws Exception {\n        hsRequest.setPathInfo(path);\n        given(resourceRenderer.getPathSegments(path)).willReturn(pathSegment);\n        given(customPageAuthorizationsHelper.isAuthorized(appToken)).willReturn(true);\n\n        doReturn(applicationPage).when(applicationAPI).getApplicationPage(appToken, pageToken);\n        doReturn(2L).when(applicationPage).getPageId();\n        doReturn(page).when(pageAPI).getPage(2L);\n        doReturn(\"customPage_\" + pageToken).when(page).getName();\n\n        servlet.service(hsRequest, hsResponse);\n\n        verify(pageRenderer, times(1)).displayCustomPage(hsRequest, hsResponse, apiSession, \"customPage_\" + pageToken);\n    }\n\n    @Test\n    public void getResource_should_work_with_special_characters() throws Exception {\n        String targetURL = \"/API/htmlexample1/content/~%60@%5E&*()_%20+1234567890-=%5B%5D%7B%7D'%22%7C.,%3E%3C%C3%A8%C3%A9%C3%A7%C3%A0!\";\n        given(resourceRenderer.getPathSegments(any())).willReturn(Arrays.asList(\"adminAppEEBonita\",\n                \"admin-process-visu\", \"API\", \"htmlexample1\", \"content\", \"~`@^&*()_+1234567890-=[]{}'\\\\\\\"|.,><èéçà!\"));\n        hsRequest.setContextPath(\"/bonita\");\n        hsRequest.setServletPath(\"/portal/resource/app\");\n        hsRequest.setPathInfo(UriUtils.decode(targetURL, \"UTF-8\"));\n\n        // This shouldn't fail\n        servlet.service(hsRequest, hsResponse);\n\n        verify(hsRequest).getRequestDispatcher(targetURL);\n    }\n\n    @Test\n    public void getResource_should_call_the_resource_renderer() throws Exception {\n        hsRequest.setPathInfo(\"/AppToken/htmlexample/content/css/file.css\");\n        final File pageDir = new File(\"/pageDir\");\n        given(resourceRenderer.getPathSegments(\"/AppToken/htmlexample/content/css/file.css\"))\n                .willReturn(Arrays.asList(\"AppToken\", \"htmlexample\", \"content\", \"css\", \"file.css\"));\n        final String pageName = \"customPage_htmlexample\";\n        doReturn(true).when(customPageAuthorizationsHelper).isAuthorized(any(String.class));\n        doReturn(pageResourceProvider).when(pageRenderer).getPageResourceProvider(pageName);\n        doReturn(pageDir).when(pageResourceProvider).getPageDirectory();\n        doReturn(true).when(bonitaHomeFolderAccessor).isInFolder(any(File.class), any(File.class));\n\n        doReturn(applicationPage).when(applicationAPI).getApplicationPage(\"AppToken\", \"htmlexample\");\n        doReturn(2L).when(applicationPage).getPageId();\n        doReturn(page).when(pageAPI).getPage(2L);\n        doReturn(pageName).when(page).getName();\n\n        servlet.service(hsRequest, hsResponse);\n\n        verify(pageRenderer, times(1)).ensurePageFolderIsPresent(apiSession, pageResourceProvider);\n        verify(resourceRenderer, times(1)).renderFile(hsRequest, hsResponse,\n                new File(pageDir, File.separator + \"resources\" + File.separator + \"css\" + File.separator + \"file.css\"));\n    }\n\n    @Test\n    public void getResource_should_get_Forbidden_Status_when_unAuthorize() throws Exception {\n        hsRequest.setPathInfo(\"/AppToken/htmlexample/content/css/../../../file.css\");\n        final File pageDir = new File(\".\");\n        given(resourceRenderer.getPathSegments(\"/AppToken/htmlexample/content/css/../../../file.css\")).willReturn(\n                Arrays.asList(\"AppToken\", \"htmlexample\", \"content\", \"css\", \"..\", \"..\", \"..\", \"file.css\"));\n\n        doReturn(applicationPage).when(applicationAPI).getApplicationPage(\"AppToken\", \"htmlexample\");\n        doReturn(2L).when(applicationPage).getPageId();\n        doReturn(page).when(pageAPI).getPage(2L);\n        doReturn(\"customPage_\" + \"htmlexample\").when(page).getName();\n\n        servlet.service(hsRequest, hsResponse);\n        verify(hsResponse).setStatus(403);\n    }\n\n    @Test\n    public void should_forward_when_API_call() throws Exception {\n        hsRequest.setPathInfo(\"/AppToken/htmlexample/API/bpm/process/1\");\n        given(resourceRenderer.getPathSegments(\"/AppToken/htmlexample/API/bpm/process/1\")).willReturn(\n                Arrays.asList(\"AppToken\", \"htmlexample\", \"API\", \"bpm\", \"process\", \"1\"));\n\n        servlet.service(hsRequest, hsResponse);\n\n        verify(servlet, never()).doGet(hsRequest, hsResponse);\n    }\n\n    @Test\n    public void should_not_authorize_API_requests_to_other_paths() throws Exception {\n        String unauthorizedPath = \"/API/living/../../WEB-INF/web.xml\";\n        hsRequest.setPathInfo(\"/AppToken/htmlexample\" + unauthorizedPath);\n        given(resourceRenderer.getPathSegments(\"/AppToken/htmlexample\" + unauthorizedPath)).willReturn(\n                Arrays.asList(\"AppToken\", \"htmlexample\", \"API\", \"living\", \"..\", \"..\", \"WEB-INF\", \"web.xml\"));\n\n        servlet.service(hsRequest, hsResponse);\n\n        verify(hsResponse).setStatus(HttpServletResponse.SC_FORBIDDEN);\n        verify(servlet, never()).doGet(hsRequest, hsResponse);\n    }\n\n    @Test\n    public void should_not_authorize_theme_requests_to_other_paths() throws Exception {\n        String unauthorizedPath = \"/theme/../WEB-INF/web.xml\";\n        hsRequest.setPathInfo(\"/AppToken/htmlexample\" + unauthorizedPath);\n        given(resourceRenderer.getPathSegments(\"/AppToken/htmlexample\" + unauthorizedPath)).willReturn(\n                Arrays.asList(\"AppToken\", \"htmlexample\", \"theme\", \"..\", \"WEB-INF\", \"web.xml\"));\n\n        servlet.service(hsRequest, hsResponse);\n\n        verify(hsResponse).setStatus(HttpServletResponse.SC_FORBIDDEN);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/livingapps/LivingApplicationServletTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.livingapps;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.doThrow;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.console.common.server.auth.AuthenticationManager;\nimport org.bonitasoft.console.common.server.login.HttpServletRequestAccessor;\nimport org.bonitasoft.console.common.server.page.CustomPageRequestModifier;\nimport org.bonitasoft.console.common.server.page.PageRenderer;\nimport org.bonitasoft.console.common.server.page.ResourceRenderer;\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.engine.business.application.ApplicationPageNotFoundException;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.page.PageNotFoundException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.InvalidSessionException;\nimport org.bonitasoft.livingapps.exception.CreationException;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class LivingApplicationServletTest {\n\n    @Mock\n    HttpServletRequest hsRequest;\n\n    @Mock\n    HttpServletResponse hsResponse;\n\n    @Mock\n    HttpSession httpSession;\n\n    @Mock\n    APISession session;\n\n    @Mock\n    ApplicationRouter router;\n\n    @Mock\n    ResourceRenderer resourceRenderer;\n\n    @Mock\n    AuthenticationManager authenticationManager;\n\n    @Mock\n    PageRenderer pageRenderer;\n\n    @Mock\n    CustomPageRequestModifier customPageRequestModifier;\n\n    @Spy\n    LivingApplicationServlet servlet;\n\n    @Before\n    public void beforeEach() throws Exception {\n        doReturn(router).when(servlet).createApplicationRouter(any(APISession.class));\n        doReturn(pageRenderer).when(servlet).getPageRenderer();\n        doReturn(resourceRenderer).when(servlet).getResourceRenderer();\n        doReturn(httpSession).when(hsRequest).getSession();\n        doReturn(session).when(servlet).getSession(hsRequest);\n        doReturn(\"/appToken/pageToken/\").when(hsRequest).getPathInfo();\n        doReturn(authenticationManager).when(servlet).getAuthenticationManager();\n        servlet.customPageRequestModifier = customPageRequestModifier;\n    }\n\n    @Test\n    public void should_send_error_404_when_the_application_page_is_not_found() throws Exception {\n        doThrow(new ApplicationPageNotFoundException(\"error\")).when(router).route(eq(hsRequest), eq(hsResponse),\n                eq(session), eq(pageRenderer), eq(resourceRenderer), any(BonitaHomeFolderAccessor.class));\n\n        servlet.service(hsRequest, hsResponse);\n\n        verify(hsResponse).sendError(404, \"error\");\n    }\n\n    @Test\n    public void should_send_error_404_when_the_custom_page_is_not_associated_to_application() throws Exception {\n        doThrow(new ApplicationPageNotFoundException(\"error\")).when(router).route(eq(hsRequest), eq(hsResponse),\n                eq(session), eq(pageRenderer), eq(resourceRenderer), any(BonitaHomeFolderAccessor.class));\n\n        servlet.service(hsRequest, hsResponse);\n\n        verify(hsResponse).sendError(404, \"error\");\n    }\n\n    @Test\n    public void should_send_error_404_when_the_custom_page_is_not_existing() throws Exception {\n        doThrow(new PageNotFoundException(\"PageName\")).when(router).route(eq(hsRequest), eq(hsResponse), eq(session),\n                eq(pageRenderer), eq(resourceRenderer), any(BonitaHomeFolderAccessor.class));\n\n        servlet.service(hsRequest, hsResponse);\n\n        verify(hsResponse).sendError(404, \"Unable to find page with name: \" + \"PageName\");\n    }\n\n    @Test\n    public void should_send_error_500_on_searchException() throws Exception {\n        doThrow(new CreationException(\"error\")).when(router).route(eq(hsRequest), eq(hsResponse), eq(session),\n                eq(pageRenderer), eq(resourceRenderer), any(BonitaHomeFolderAccessor.class));\n\n        servlet.service(hsRequest, hsResponse);\n\n        verify(hsResponse).sendError(404, \"error\");\n    }\n\n    @Test\n    public void should_send_error_500_on_BonitaHomeNotSetException() throws Exception {\n        doThrow(new BonitaHomeNotSetException(\"error\")).when(router).route(eq(hsRequest), eq(hsResponse), eq(session),\n                eq(pageRenderer), eq(resourceRenderer), any(BonitaHomeFolderAccessor.class));\n\n        servlet.service(hsRequest, hsResponse);\n\n        verify(hsResponse).sendError(500);\n    }\n\n    @Test\n    public void should_send_error_500_on_ServerAPIException() throws Exception {\n        given(servlet.createApplicationRouter(session))\n                .willThrow(new ServerAPIException(new Exception(\"\")));\n\n        servlet.service(hsRequest, hsResponse);\n\n        verify(hsResponse).sendError(500);\n    }\n\n    @Test\n    public void should_send_error_500_on_UnknownAPITypeException() throws Exception {\n        given(servlet.createApplicationRouter(session))\n                .willThrow(new UnknownAPITypeException(\"error\"));\n\n        servlet.service(hsRequest, hsResponse);\n\n        verify(hsResponse).sendError(500);\n    }\n\n    @Test\n    public void should_send_error_404_when_the_page_is_not_found() throws Exception {\n        doThrow(new ApplicationPageNotFoundException(\"error\")).when(router).route(eq(hsRequest), eq(hsResponse),\n                eq(session), eq(pageRenderer), eq(resourceRenderer), any(BonitaHomeFolderAccessor.class));\n\n        servlet.service(hsRequest, hsResponse);\n\n        verify(hsResponse).sendError(404, \"error\");\n    }\n\n    @Test\n    public void should_redirect_to_the_login_page_when_the_session_is_invalid() throws Exception {\n        doThrow(new InvalidSessionException(\"error\")).when(router).route(eq(hsRequest), eq(hsResponse), eq(session),\n                eq(pageRenderer), eq(resourceRenderer), any(BonitaHomeFolderAccessor.class));\n        doReturn(\"/bonita/apps/appToken/pageToken/\").when(hsRequest).getRequestURI();\n        String encodedRedirectURL = \"%2Fbonita%2Fapps%2FappToken%2FpageToken%2F\";\n        String loginURL = \"/bonita/login.jsp?redirectUrl=\" + encodedRedirectURL;\n        doReturn(loginURL).when(authenticationManager).getLoginPageURL(any(HttpServletRequestAccessor.class),\n                eq(encodedRedirectURL));\n\n        servlet.service(hsRequest, hsResponse);\n\n        verify(hsResponse).sendRedirect(loginURL);\n    }\n\n    @Test\n    public void should_redirectToValidPageUrl_on_missing_final_slash() throws Exception {\n        doReturn(\"/appToken/pageToken\").when(hsRequest).getPathInfo();\n\n        servlet.service(hsRequest, hsResponse);\n\n        verify(customPageRequestModifier).redirectToValidPageUrl(hsRequest, hsResponse);\n    }\n\n    @Test\n    public void should_redirectToValidPageUrl_on_missing_final_slash_after_appToken() throws Exception {\n        doReturn(\"/appToken\").when(hsRequest).getPathInfo();\n\n        servlet.service(hsRequest, hsResponse);\n\n        verify(customPageRequestModifier).redirectToValidPageUrl(hsRequest, hsResponse);\n    }\n\n    @Test\n    public void should_not_redirectToValidPageUrl_on_resource_query() throws Exception {\n        doReturn(\"/appToken/pageToken/file.css\").when(hsRequest).getPathInfo();\n\n        servlet.service(hsRequest, hsResponse);\n\n        verify(customPageRequestModifier, never()).redirectToValidPageUrl(hsRequest, hsResponse);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/livingapps/menu/ChildrenMenuCollectorTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.livingapps.menu;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.business.application.impl.ApplicationMenuImpl;\nimport org.junit.Test;\n\npublic class ChildrenMenuCollectorTest {\n\n    ChildrenMenuCollector collector = new ChildrenMenuCollector(1L);\n\n    ApplicationMenuImpl menu = new ApplicationMenuImpl(\"name\", 1L, 2L, 1);\n\n    @Test\n    public void should_be_collectible_when_menu_parentId_is_the_given_parentId() {\n        menu.setParentId(1L);\n\n        assertThat(collector.isCollectible(menu)).isTrue();\n    }\n\n    @Test\n    public void should_not_be_collectible_when_menu_parentId_is_not_the_given_parentId() {\n        menu.setParentId(2L);\n\n        assertThat(collector.isCollectible(menu)).isFalse();\n    }\n\n    @Test\n    public void should_not_be_collectible_when_menu_parentId_is_null() {\n        menu.setParentId(null);\n\n        assertThat(collector.isCollectible(menu)).isFalse();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/livingapps/menu/MenuFactoryTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.livingapps.menu;\n\nimport static java.util.Arrays.asList;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\n\nimport org.bonitasoft.engine.api.ApplicationAPI;\nimport org.bonitasoft.engine.business.application.ApplicationMenu;\nimport org.bonitasoft.engine.business.application.impl.ApplicationMenuImpl;\nimport org.bonitasoft.engine.business.application.impl.ApplicationPageImpl;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Answers;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class MenuFactoryTest {\n\n    ApplicationMenuImpl aMenuLink;\n\n    ApplicationMenuImpl aMenuContainer;\n\n    ApplicationMenuImpl aNestedMenuLink;\n\n    ApplicationPageImpl aPage = new ApplicationPageImpl(1L, 2L, \"token\");\n\n    @Mock(answer = Answers.RETURNS_MOCKS)\n    ApplicationAPI applicationApi;\n\n    @Before\n    public void beforeEach() throws Exception {\n        aMenuLink = new ApplicationMenuImpl(\"link\", 1L, 2L, 1);\n        aMenuLink.setId(1L);\n        aMenuContainer = new ApplicationMenuImpl(\"container\", 1L, null, 2);\n        aMenuContainer.setId(2L);\n        aNestedMenuLink = new ApplicationMenuImpl(\"nested-link\", 1L, 2L, 2);\n        aNestedMenuLink.setId(3L);\n        aNestedMenuLink.setParentId(2L);\n        given(applicationApi.getApplicationPage(aMenuLink.getApplicationPageId())).willReturn(aPage);\n    }\n\n    @Test\n    public void should_create_a_MenuLink_with_the_page_token_it_is_pointing_at_when_menu_is_a_link() throws Exception {\n        MenuFactory factory = new MenuFactory(applicationApi);\n\n        assertThat(factory.create(asList((ApplicationMenu) aMenuLink)).get(0).getHtml())\n                .isEqualTo(\"<li><a href=\\\"token\\\">link</a></li>\");\n    }\n\n    @Test\n    public void should_create_an_empty_MenuContainer() throws Exception {\n        MenuFactory factory = new MenuFactory(applicationApi);\n\n        assertThat(factory.create(asList((ApplicationMenu) aMenuContainer)).get(0).getHtml())\n                .isEqualTo(new StringBuilder()\n                        .append(\"<li class=\\\"dropdown\\\"><a href=\\\"#\\\" class=\\\"dropdown-toggle\\\" data-toggle=\\\"dropdown\\\">container <span class=\\\"caret\\\"></span></a>\")\n                        .append(\"<ul class=\\\"dropdown-menu\\\" role=\\\"menu\\\">\")\n                        .append(\"</ul></li>\").toString());\n    }\n\n    @Test\n    public void should_create_a_MenuContainer_containing_a_MenuLink() throws Exception {\n        MenuFactory factory = new MenuFactory(applicationApi);\n\n        assertThat(factory.create(asList((ApplicationMenu) aMenuContainer, aNestedMenuLink)).get(0).getHtml())\n                .isEqualTo(new StringBuilder()\n                        .append(\"<li class=\\\"dropdown\\\"><a href=\\\"#\\\" class=\\\"dropdown-toggle\\\" data-toggle=\\\"dropdown\\\">container <span class=\\\"caret\\\"></span></a>\")\n                        .append(\"<ul class=\\\"dropdown-menu\\\" role=\\\"menu\\\">\")\n                        .append(\"<li><a href=\\\"token\\\">nested-link</a></li>\")\n                        .append(\"</ul></li>\").toString());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/livingapps/menu/RootMenuCollectorTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.livingapps.menu;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.business.application.impl.ApplicationMenuImpl;\nimport org.junit.Test;\n\npublic class RootMenuCollectorTest {\n\n    RootMenuCollector collector = new RootMenuCollector();\n\n    ApplicationMenuImpl menu = new ApplicationMenuImpl(\"name\", 1L, 2L, 1);\n\n    @Test\n    public void should_be_collectible_when_the_menu_has_no_parentId() {\n        menu.setParentId(null);\n\n        assertThat(collector.isCollectible(menu)).isTrue();\n    }\n\n    @Test\n    public void should_not_be_collectible_when_the_menu_has_a_parentId() {\n        menu.setParentId(3L);\n\n        assertThat(collector.isCollectible(menu)).isFalse();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/model/ModelFactoryExtTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.web.rest.model.application.ApplicationDefinition;\nimport org.bonitasoft.web.rest.model.application.ApplicationLinkDefinition;\nimport org.bonitasoft.web.rest.model.applicationpage.ApplicationPageDefinition;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.junit.Test;\n\npublic class ModelFactoryExtTest {\n\n    private final ModelFactory factory = new ModelFactory();\n\n    @Test\n    public void defineItemDefinitions_should_return_instanceOf_ApplicationLinkDefinition_for_applicationLink_token()\n            throws Exception {\n        //when\n        final ItemDefinition<?> definition = factory.defineItemDefinitions(ApplicationLinkDefinition.TOKEN);\n\n        //then\n        assertThat(definition).isNotNull();\n        assertThat(definition).isInstanceOf(ApplicationLinkDefinition.class);\n    }\n\n    @Test\n    public void defineItemDefinitions_should_return_instanceOf_ApplicationDefinition_for_application_token()\n            throws Exception {\n        //when\n        final ItemDefinition<?> definition = factory.defineItemDefinitions(ApplicationDefinition.TOKEN);\n\n        //then\n        assertThat(definition).isNotNull();\n        assertThat(definition).isInstanceOf(ApplicationDefinition.class);\n    }\n\n    @Test\n    public void defineItemDefinitions_should_return_instanceOf_ApplicationPageDefinition_for_applicationPage_token()\n            throws Exception {\n        //when\n        final ItemDefinition<?> definition = factory.defineItemDefinitions(ApplicationPageDefinition.TOKEN);\n\n        //then\n        assertThat(definition).isNotNull();\n        assertThat(definition).isInstanceOf(ApplicationPageDefinition.class);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/model/builder/profile/ProfileItemBuilder.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.model.builder.profile;\n\nimport org.bonitasoft.engine.profile.Profile;\nimport org.bonitasoft.engine.profile.impl.ProfileImpl;\nimport org.bonitasoft.web.rest.model.portal.profile.ProfileItem;\n\n/**\n * @author Colin PUY\n */\npublic class ProfileItemBuilder {\n\n    protected long id = 1L;\n\n    protected String name = \"aName\";\n\n    protected String description = \"aDescription\";\n\n    protected boolean isDefault = false;\n\n    protected String iconPath;\n\n    protected final long createdBy = 0;\n\n    protected final String createdOn = null;\n\n    protected final long updatedBy = 0;\n\n    protected final String updatedOn = null;\n\n    public static ProfileItemBuilder aProfileItem() {\n        return new ProfileItemBuilder();\n    }\n\n    public ProfileItem build() {\n        final ProfileItem item = new ProfileItem();\n        item.setId(id);\n        item.setName(name);\n        item.setDescription(description);\n        item.setIsDefault(isDefault);\n        item.setIcon(iconPath);\n        item.setUpdatedByUserId(updatedBy);\n        item.setLastUpdateDate(updatedOn);\n        item.setCreatedByUserId(createdBy);\n        item.setCreationDate(createdOn);\n        return item;\n    }\n\n    public Profile toProfile() {\n        final ProfileImpl profile = new ProfileImpl(name);\n        profile.setDescription(description);\n        profile.setDefault(isDefault);\n        return profile;\n    }\n\n    public ProfileItemBuilder fromEngineItem(final Profile profile) {\n        id = profile.getId();\n        name = profile.getName();\n        description = profile.getDescription();\n        isDefault = profile.isDefault();\n        return this;\n    }\n\n    public ProfileItemBuilder withName(final String name) {\n        this.name = name;\n        return this;\n    }\n\n    public ProfileItemBuilder withIcon(final String iconPath) {\n        this.iconPath = iconPath;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/APITestWithMock.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server;\n\nimport org.bonitasoft.console.common.server.i18n.I18n;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.junit.BeforeClass;\n\n/**\n * Base test class for API and Datastore tests classes using mocks\n *\n * @author Colin PUY\n */\npublic class APITestWithMock {\n\n    /**\n     * Initialise minimal environment for {@link Item}\n     */\n    @BeforeClass\n    public static void initEnvironment() {\n        new BonitaRestAPIServlet();\n        I18n.getInstance();\n    }\n\n    protected boolean areEquals(Item item1, Item item2) {\n        return item1.getAttributes().equals(item2.getAttributes());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/BonitaRestAPIFactoryTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server;\n\nimport static org.junit.Assert.assertTrue;\n\nimport org.bonitasoft.web.rest.server.api.organization.APICustomUserInfoDefinition;\nimport org.bonitasoft.web.rest.server.api.organization.APICustomUserInfoUser;\nimport org.bonitasoft.web.rest.server.api.organization.APICustomUserInfoValue;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Vincent Elcrin\n */\npublic class BonitaRestAPIFactoryTest extends APITestWithMock {\n\n    BonitaRestAPIFactory factory;\n\n    @Before\n    public void setUp() throws Exception {\n        factory = new BonitaRestAPIFactory();\n    }\n\n    @Test\n    public void should_provide_an_APICustomUserInfoDefinition_when_requesting_custom_user_info_definition()\n            throws Exception {\n        assertTrue(factory.defineApis(\"customuserinfo\", \"definition\") instanceof APICustomUserInfoDefinition);\n    }\n\n    @Test\n    public void should_provide_an_APICustomUserInfoUser_when_requesting_custom_user_info() throws Exception {\n        assertTrue(factory.defineApis(\"customuserinfo\", \"user\") instanceof APICustomUserInfoUser);\n    }\n\n    @Test\n    public void should_provide_an_APICustomUserInfoValue_when_requesting_custom_user_info_value() throws Exception {\n        assertTrue(factory.defineApis(\"customuserinfo\", \"value\") instanceof APICustomUserInfoValue);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/QueryParameterUtilsTest.java",
    "content": "/**\n * Copyright (C) 2025 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\nimport static org.bonitasoft.web.rest.server.QueryParameterUtils.*;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\n\nclass QueryParameterUtilsTest {\n\n    @Nested\n    class ParseFilters {\n\n        @Test\n        void should_build_map_from_key_value_pairs() {\n            var filters = List.of(\"id=17\", \"name='John'\",\n                    \"filter_key_1=value=with=equal=in=value\",\n                    \"filter_key_2=value1,value2,value3\",\n                    \"filter_key_3='value-1','value-2','value-3'\");\n\n            assertThat(parseFilters(filters))\n                    .hasSize(5)\n                    .containsEntry(\"id\", \"17\")\n                    .containsEntry(\"name\", \"'John'\")\n                    .containsEntry(\"filter_key_1\", \"value=with=equal=in=value\")\n                    .containsEntry(\"filter_key_2\", \"value1,value2,value3\")\n                    .containsEntry(\"filter_key_3\", \"'value-1','value-2','value-3'\");\n        }\n\n        @Test\n        void should_handle_special_characters_in_values() {\n            var filters = List.of(\"path=/d/d,e\");\n\n            assertThat(parseFilters(filters))\n                    .hasSize(1)\n                    .containsEntry(\"path\", \"/d/d,e\");\n        }\n\n        @Test\n        void should_put_null_value_when_filter_has_no_value() {\n            var filters = List.of(\"id=\");\n\n            assertThat(parseFilters(filters))\n                    .hasSize(1)\n                    .containsEntry(\"id\", null);\n        }\n\n        @Test\n        void should_ignore_filter_with_no_name() {\n            assertThat(parseFilters(List.of(\"=\"))).isEmpty();\n        }\n\n        @Test\n        void should_return_null_when_input_is_null() {\n            assertThat(parseFilters(null)).isNull();\n        }\n\n        @Test\n        void should_return_empty_map_for_empty_list() {\n            assertThat(parseFilters(List.of())).isEmpty();\n        }\n\n        @Test\n        void should_put_null_value_when_filter_has_no_equals_sign() {\n            var filters = List.of(\"id\");\n\n            assertThat(parseFilters(filters))\n                    .hasSize(1)\n                    .containsEntry(\"id\", null);\n        }\n\n    }\n\n    @Nested\n    class ExtractStringFilter {\n\n        @Test\n        void should_return_value_when_filter_is_present() {\n            var filters = List.of(\"name=John\", \"age=30\");\n\n            assertThat(extractStringFilter(filters, \"name\")).isEqualTo(\"John\");\n        }\n\n        @Test\n        void should_return_null_when_filter_is_not_present() {\n            var filters = List.of(\"other=John\");\n\n            assertThat(extractStringFilter(filters, \"name\")).isNull();\n        }\n\n        @Test\n        void should_return_null_when_filters_is_null() {\n            assertThat(extractStringFilter(null, \"name\")).isNull();\n        }\n\n        @Test\n        void should_return_null_when_value_is_empty() {\n            var filters = List.of(\"name=\");\n\n            assertThat(extractStringFilter(filters, \"name\")).isNull();\n        }\n\n        @Test\n        void should_return_value_containing_equals_sign() {\n            var filters = List.of(\"name=a=b=c\");\n\n            assertThat(extractStringFilter(filters, \"name\")).isEqualTo(\"a=b=c\");\n        }\n\n        @Test\n        void should_return_first_matching_filter() {\n            var filters = List.of(\"name=first\", \"name=second\");\n\n            assertThat(extractStringFilter(filters, \"name\")).isEqualTo(\"first\");\n        }\n\n        @Test\n        void should_return_null_when_filter_has_no_equals_sign() {\n            var filters = List.of(\"name\");\n\n            assertThat(extractStringFilter(filters, \"name\")).isNull();\n        }\n\n        @Test\n        void should_return_comma_separated_value_as_is() {\n            var filters = List.of(\"name=John,Jane,Bob\");\n\n            assertThat(extractStringFilter(filters, \"name\")).isEqualTo(\"John,Jane,Bob\");\n        }\n    }\n\n    @Nested\n    class ExtractMandatoryStringFilter {\n\n        @Test\n        void should_return_value_when_filter_is_present() {\n            var filters = List.of(\"name=John\");\n\n            assertThat(extractMandatoryStringFilter(filters, \"name\")).isEqualTo(\"John\");\n        }\n\n        @Test\n        void should_throw_when_filter_is_missing() {\n            var filters = List.of(\"other=John\");\n\n            assertThatThrownBy(() -> extractMandatoryStringFilter(filters, \"name\"))\n                    .isInstanceOf(IllegalArgumentException.class)\n                    .hasMessage(\"filter name is mandatory\");\n        }\n\n        @Test\n        void should_throw_when_value_is_empty() {\n            var filters = List.of(\"name=\");\n\n            assertThatThrownBy(() -> extractMandatoryStringFilter(filters, \"name\"))\n                    .isInstanceOf(IllegalArgumentException.class)\n                    .hasMessage(\"filter name is mandatory\");\n        }\n\n        @Test\n        void should_throw_when_filter_has_no_equals_sign() {\n            var filters = List.of(\"name\");\n\n            assertThatThrownBy(() -> extractMandatoryStringFilter(filters, \"name\"))\n                    .isInstanceOf(IllegalArgumentException.class)\n                    .hasMessage(\"filter name is mandatory\");\n        }\n\n        @Test\n        void should_return_comma_separated_value_as_is() {\n            var filters = List.of(\"name=John,Jane,Bob\");\n\n            assertThat(extractMandatoryStringFilter(filters, \"name\")).isEqualTo(\"John,Jane,Bob\");\n        }\n\n        @Test\n        void should_throw_when_filters_is_null() {\n            assertThatThrownBy(() -> extractMandatoryStringFilter(null, \"name\"))\n                    .isInstanceOf(IllegalArgumentException.class)\n                    .hasMessage(\"filter name is mandatory\");\n        }\n    }\n\n    @Nested\n    class ExtractLongFilter {\n\n        @Test\n        void should_return_value_when_filter_is_a_valid_number() {\n            var filters = List.of(\"id=42\");\n\n            assertThat(extractLongFilter(filters, \"id\")).isEqualTo(42L);\n        }\n\n        @Test\n        void should_return_null_when_filter_is_not_present() {\n            var filters = List.of(\"other=42\");\n\n            assertThat(extractLongFilter(filters, \"id\")).isNull();\n        }\n\n        @Test\n        void should_return_null_when_filters_is_null() {\n            assertThat(extractLongFilter(null, \"id\")).isNull();\n        }\n\n        @Test\n        void should_throw_when_value_is_not_a_number() {\n            var filters = List.of(\"id=abc\");\n\n            assertThatThrownBy(() -> extractLongFilter(filters, \"id\"))\n                    .isInstanceOf(IllegalArgumentException.class)\n                    .hasMessage(\"filter id must be a number\");\n        }\n\n        @Test\n        void should_parse_negative_number() {\n            var filters = List.of(\"id=-5\");\n\n            assertThat(extractLongFilter(filters, \"id\")).isEqualTo(-5L);\n        }\n\n        @Test\n        void should_return_null_when_filter_has_no_equals_sign() {\n            var filters = List.of(\"id\");\n\n            assertThat(extractLongFilter(filters, \"id\")).isNull();\n        }\n\n        @Test\n        void should_throw_when_value_contains_commas() {\n            var filters = List.of(\"id=1,2,3\");\n\n            assertThatThrownBy(() -> extractLongFilter(filters, \"id\"))\n                    .isInstanceOf(IllegalArgumentException.class)\n                    .hasMessage(\"filter id must be a number\");\n        }\n    }\n\n    @Nested\n    class ExtractMandatoryLongFilter {\n\n        @Test\n        void should_return_value_when_filter_is_a_valid_number() {\n            var filters = List.of(\"id=99\");\n\n            assertThat(extractMandatoryLongFilter(filters, \"id\")).isEqualTo(99L);\n        }\n\n        @Test\n        void should_throw_when_filter_is_missing() {\n            var filters = List.of(\"other=42\");\n\n            assertThatThrownBy(() -> extractMandatoryLongFilter(filters, \"id\"))\n                    .isInstanceOf(IllegalArgumentException.class)\n                    .hasMessage(\"filter id is mandatory\");\n        }\n\n        @Test\n        void should_throw_when_value_is_not_a_number() {\n            var filters = List.of(\"id=abc\");\n\n            assertThatThrownBy(() -> extractMandatoryLongFilter(filters, \"id\"))\n                    .isInstanceOf(IllegalArgumentException.class)\n                    .hasMessage(\"filter id must be a number\");\n        }\n\n        @Test\n        void should_throw_when_value_is_empty() {\n            var filters = List.of(\"id=\");\n\n            assertThatThrownBy(() -> extractMandatoryLongFilter(filters, \"id\"))\n                    .isInstanceOf(IllegalArgumentException.class)\n                    .hasMessage(\"filter id is mandatory\");\n        }\n\n        @Test\n        void should_throw_when_filter_has_no_equals_sign() {\n            var filters = List.of(\"id\");\n\n            assertThatThrownBy(() -> extractMandatoryLongFilter(filters, \"id\"))\n                    .isInstanceOf(IllegalArgumentException.class)\n                    .hasMessage(\"filter id is mandatory\");\n        }\n\n        @Test\n        void should_throw_when_value_contains_commas() {\n            var filters = List.of(\"id=1,2,3\");\n\n            assertThatThrownBy(() -> extractMandatoryLongFilter(filters, \"id\"))\n                    .isInstanceOf(IllegalArgumentException.class)\n                    .hasMessage(\"filter id must be a number\");\n        }\n\n        @Test\n        void should_throw_when_filters_is_null() {\n            assertThatThrownBy(() -> extractMandatoryLongFilter(null, \"id\"))\n                    .isInstanceOf(IllegalArgumentException.class)\n                    .hasMessage(\"filter id is mandatory\");\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/ServletMappingValidationTest.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport javax.xml.parsers.DocumentBuilder;\nimport javax.xml.parsers.DocumentBuilderFactory;\n\nimport org.junit.jupiter.api.Test;\nimport org.w3c.dom.Document;\nimport org.w3c.dom.Element;\nimport org.w3c.dom.NodeList;\n\n/**\n * Validates that servlet URL patterns in web.xml follow Tomcat constraints.\n * Tomcat servlet mappings do NOT support wildcards in the middle of paths:\n * - ❌ /API/bpm/process/*&#47;design (wildcard in middle)\n * - ✅ /API/bpm/process/* (wildcard at end)\n * - ✅ *.jsp (extension mapping)\n * When wildcards in the middle are needed, use URL rewriting with /APISpringInternal\n * (see doc/RESTLET_TO_SPRING_MVC_CONVERSION_GUIDE.md section on URL rewriting)\n */\nclass ServletMappingValidationTest {\n\n    private static final String WEB_XML_PATH = \"src/main/webapp/WEB-INF/web.xml\";\n\n    @Test\n    void should_not_have_wildcards_in_middle_of_SpringRest_servlet_patterns() throws Exception {\n        // Given\n        File webXmlFile = new File(WEB_XML_PATH);\n        assertThat(webXmlFile)\n                .withFailMessage(\"web.xml not found at: \" + WEB_XML_PATH)\n                .exists();\n\n        // When\n        List<String> springRestPatterns = parseUrlPatternsForServlet(webXmlFile, \"SpringRest\");\n\n        // Then\n        List<String> invalidPatterns = new ArrayList<>();\n        for (String pattern : springRestPatterns) {\n            if (hasWildcardInMiddle(pattern)) {\n                invalidPatterns.add(pattern);\n            }\n        }\n\n        assertThat(invalidPatterns)\n                .withFailMessage(\n                        \"Found servlet URL patterns with wildcards in the middle (not supported by Tomcat):\\n\" +\n                                String.join(\"\\n\", invalidPatterns) +\n                                \"\\n\\nTomcat only supports:\\n\" +\n                                \"  - Wildcards at the end: /API/path/*\\n\" +\n                                \"  - Extension mappings: *.jsp\\n\" +\n                                \"\\nFor patterns like /API/path/*/operation, use URL rewriting with /APISpringInternal\\n\"\n                                +\n                                \"See: doc/RESTLET_TO_SPRING_MVC_CONVERSION_GUIDE.md\")\n                .isEmpty();\n    }\n\n    /**\n     * Checks if a URL pattern has a wildcard in the middle of the path.\n     * Valid patterns:\n     * - /path/* (wildcard at end)\n     * - *.ext (extension mapping)\n     * - /path (no wildcard)\n     * Invalid patterns:\n     * - /path/*&#47;something (wildcard in middle)\n     * - /path/*&#47;*&#47;something (multiple wildcards)\n     */\n    private boolean hasWildcardInMiddle(String pattern) {\n        if (!pattern.contains(\"*\")) {\n            return false; // No wildcard at all\n        }\n\n        // Extension mapping (*.jsp) is valid\n        if (pattern.startsWith(\"*.\")) {\n            return false;\n        }\n\n        // Wildcard at the end (/path/*) is valid\n        return !pattern.endsWith(\"/*\");\n\n        // Wildcard anywhere else is in the middle\n    }\n\n    private List<String> parseUrlPatternsForServlet(File webXml, String servletName) throws Exception {\n        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();\n        DocumentBuilder builder = factory.newDocumentBuilder();\n        Document doc = builder.parse(webXml);\n        doc.getDocumentElement().normalize();\n\n        List<String> patterns = new ArrayList<>();\n\n        // Find servlet-mapping elements\n        NodeList servletMappings = doc.getElementsByTagName(\"servlet-mapping\");\n        for (int i = 0; i < servletMappings.getLength(); i++) {\n            Element mapping = (Element) servletMappings.item(i);\n\n            // Check if this mapping is for the target servlet\n            String mappingServletName = getTextContent(mapping, \"servlet-name\");\n            if (servletName.equals(mappingServletName)) {\n                // Get all url-pattern elements for this servlet\n                NodeList urlPatterns = mapping.getElementsByTagName(\"url-pattern\");\n                for (int j = 0; j < urlPatterns.getLength(); j++) {\n                    String pattern = urlPatterns.item(j).getTextContent().trim();\n                    patterns.add(pattern);\n                }\n            }\n        }\n\n        return patterns;\n    }\n\n    private String getTextContent(Element parent, String tagName) {\n        NodeList nodes = parent.getElementsByTagName(tagName);\n        if (nodes.getLength() > 0) {\n            return nodes.item(0).getTextContent().trim();\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/UrlRewriteConfigurationTest.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport javax.xml.parsers.DocumentBuilder;\nimport javax.xml.parsers.DocumentBuilderFactory;\n\nimport org.junit.jupiter.api.Test;\nimport org.w3c.dom.Document;\nimport org.w3c.dom.Element;\nimport org.w3c.dom.NodeList;\n\n/**\n * Validates that URL rewrite rules are properly configured for Spring MVC endpoints\n * that cannot be expressed with Tomcat servlet mapping wildcards.\n * When servlet patterns like /API/path/*&#47;operation are needed (wildcard in middle),\n * URL rewriting with /APISpringInternal must be used as a workaround.\n */\nclass UrlRewriteConfigurationTest {\n\n    private static final String URLREWRITE_XML_PATH = \"src/main/webapp/WEB-INF/urlrewrite.xml\";\n\n    @Test\n    void should_have_urlrewrite_rule_for_process_design_endpoint() throws Exception {\n        // Given\n        File urlRewriteXml = new File(URLREWRITE_XML_PATH);\n        assertThat(urlRewriteXml)\n                .withFailMessage(\"urlrewrite.xml not found at: \" + URLREWRITE_XML_PATH)\n                .exists();\n\n        // When\n        List<UrlRewriteRule> rules = parseUrlRewriteRules(urlRewriteXml);\n\n        // Then - Should have rule for process design endpoint\n        boolean hasProcessDesignRule = rules.stream()\n                .anyMatch(rule -> rule.from.contains(\"/API/bpm/process/\")\n                        && rule.from.contains(\"/design\")\n                        && rule.to.contains(\"/APISpringInternal/bpm/process/\")\n                        && rule.to.contains(\"/design\"));\n\n        assertThat(hasProcessDesignRule)\n                .withFailMessage(\n                        \"\"\"\n                                Missing URL rewrite rule for /API/bpm/process/{id}/design endpoint.\n\n                                Expected rule:\n                                  <from>^(/portal/custom-page)?/API/bpm/process/([^/]+)/design$</from>\n                                  <to>/APISpringInternal/bpm/process/$2/design</to>\n\n                                This rule is required because Tomcat servlet mappings don't support\n                                wildcards in the middle of paths (/API/bpm/process/*/design).\n\n                                See: doc/RESTLET_TO_SPRING_MVC_CONVERSION_GUIDE.md - Pattern 5: URL Rewriting\"\"\")\n                .isTrue();\n    }\n\n    private List<UrlRewriteRule> parseUrlRewriteRules(File urlRewriteXml) throws Exception {\n        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();\n        DocumentBuilder builder = factory.newDocumentBuilder();\n        Document doc = builder.parse(urlRewriteXml);\n        doc.getDocumentElement().normalize();\n\n        List<UrlRewriteRule> rules = new ArrayList<>();\n\n        NodeList ruleNodes = doc.getElementsByTagName(\"rule\");\n        for (int i = 0; i < ruleNodes.getLength(); i++) {\n            Element ruleElement = (Element) ruleNodes.item(i);\n\n            String from = getTextContent(ruleElement, \"from\");\n            String to = getTextContent(ruleElement, \"to\");\n\n            if (from != null && to != null) {\n                rules.add(new UrlRewriteRule(from, to));\n            }\n        }\n\n        return rules;\n    }\n\n    private String getTextContent(Element parent, String tagName) {\n        NodeList nodes = parent.getElementsByTagName(tagName);\n        if (nodes.getLength() > 0) {\n            return nodes.item(0).getTextContent().trim();\n        }\n        return null;\n    }\n\n    private record UrlRewriteRule(String from, String to) {}\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/AbstractControllerTest.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api;\n\nimport static org.bonitasoft.web.rest.server.api.RestControllerUtils.initMockMvcWithSessionAttributes;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.session.APISession;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport org.mockito.junit.jupiter.MockitoSettings;\nimport org.mockito.quality.Strictness;\nimport org.springframework.test.web.servlet.MockMvc;\n\n/**\n * Abstract base class for Spring MVC controller tests.\n * Provides common MockMvc setup with session attributes and API mocking infrastructure.\n * <p>\n * This base class eliminates 15-20 lines of repetitive test setup boilerplate.\n * <p>\n * <b>Usage example:</b>\n *\n * <pre>\n * class MyControllerTest extends AbstractControllerTest&lt;MyController&gt; {\n *\n *     &#64;Mock\n *     protected ProcessAPI processAPI;\n *\n *     &#64;Override\n *     protected MyController createController() {\n *         return spy(new MyController());\n *     }\n *\n *     &#64;Override\n *     protected void configureMocks(MyController controller) throws Exception {\n *         doReturn(processAPI).when(controller).getProcessAPI(apiSession);\n *     }\n *\n *     &#64;Test\n *     void should_test_something() throws Exception {\n *         // Test implementation (no setup boilerplate!)\n *         mockMvc.perform(get(\"/API/endpoint\")\n *                 .sessionAttrs(sessionAttributes))\n *                 .andExpect(status().isOk());\n *     }\n * }\n * </pre>\n *\n * @param <C> the controller type being tested (must extend AbstractRESTController)\n * @see AbstractPlatformControllerTest for PlatformAPI controller tests\n */\n@ExtendWith(MockitoExtension.class)\n@MockitoSettings(strictness = Strictness.LENIENT)\npublic abstract class AbstractControllerTest<C extends AbstractRESTController> {\n\n    /**\n     * MockMvc instance for performing HTTP requests in tests.\n     * Configured with session attributes and exception handling.\n     */\n    protected MockMvc mockMvc;\n\n    /**\n     * Session attributes map containing the APISession.\n     * Use this in mockMvc.perform() calls: .sessionAttrs(sessionAttributes)\n     */\n    protected final Map<String, Object> sessionAttributes = new HashMap<>();\n\n    /**\n     * Mocked APISession for standard API access.\n     * Automatically configured in setUp().\n     */\n    @Mock\n    protected APISession apiSession;\n\n    /**\n     * Sets up MockMvc and configures API mocks before each test.\n     * Calls createController() and configureMocks() to allow test customization.\n     */\n    @BeforeEach\n    void setUp() throws Exception {\n        C controller = createController();\n        mockMvc = initMockMvcWithSessionAttributes(controller, sessionAttributes, apiSession);\n        configureMocks(controller);\n    }\n\n    /**\n     * Creates the controller instance to be tested.\n     * Typically returns spy(new YourController()) to allow mocking of API accessor methods.\n     *\n     * @return the controller instance\n     */\n    protected abstract C createController();\n\n    /**\n     * Configures mock API dependencies for the controller.\n     * Called after MockMvc setup, before tests run.\n     * <p>\n     * Example:\n     *\n     * <pre>\n     *\n     * protected void configureMocks(MyController controller) throws Exception {\n     *     doReturn(processAPI).when(controller).getProcessAPI(apiSession);\n     *     doReturn(identityAPI).when(controller).getIdentityAPI(apiSession);\n     * }\n     * </pre>\n     *\n     * @param controller the controller instance (same as returned by createController())\n     * @throws Exception if mock configuration fails\n     */\n    protected abstract void configureMocks(C controller) throws Exception;\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/AbstractPlatformControllerTest.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.console.common.server.login.servlet.PlatformLoginServlet;\nimport org.bonitasoft.engine.session.PlatformSession;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport org.mockito.junit.jupiter.MockitoSettings;\nimport org.mockito.quality.Strictness;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\n\n/**\n * Abstract base class for Spring MVC controller tests that use PlatformAPI.\n * Provides common MockMvc setup with PlatformSession and platform API mocking infrastructure.\n * <p>\n * This base class eliminates 15-20 lines of repetitive test setup boilerplate.\n * <p>\n * <b>Differences from AbstractControllerTest:</b>\n * <ul>\n * <li>Uses PlatformSession instead of APISession</li>\n * <li>Uses MockMvcBuilders.standaloneSetup() without exception handler</li>\n * <li>Configures PlatformSession in sessionAttributes directly</li>\n * </ul>\n * <p>\n * <b>Usage example:</b>\n *\n * <pre>\n * class LicenseInfoControllerTest extends AbstractPlatformControllerTest&lt;LicenseInfoController&gt; {\n *\n *     &#64;Mock\n *     protected PlatformAPI platformAPI;\n *\n *     &#64;Override\n *     protected LicenseInfoController createController() {\n *         return spy(new LicenseInfoController());\n *     }\n *\n *     &#64;Override\n *     protected void configureMocks(LicenseInfoController controller) throws Exception {\n *         doReturn(platformAPI).when(controller).getPlatformAPI(any(PlatformSession.class));\n *     }\n *\n *     &#64;Test\n *     void should_return_license_info() throws Exception {\n *         // Test implementation (no setup boilerplate!)\n *         when(platformAPI.getInformation()).thenReturn(mockInfo);\n *\n *         mockMvc.perform(get(\"/API/platform/license\")\n *                 .sessionAttrs(sessionAttributes))\n *                 .andExpect(status().isOk());\n *     }\n * }\n * </pre>\n *\n * @param <C> the controller type being tested (must extend AbstractRESTController)\n * @see AbstractControllerTest for standard API controller tests\n */\n@ExtendWith(MockitoExtension.class)\n@MockitoSettings(strictness = Strictness.LENIENT)\npublic abstract class AbstractPlatformControllerTest<C extends AbstractRESTController> {\n\n    /**\n     * MockMvc instance for performing HTTP requests in tests.\n     * Configured with standalone setup (no global exception handler).\n     */\n    protected MockMvc mockMvc;\n\n    /**\n     * Session attributes map containing the PlatformSession.\n     * Use this in mockMvc.perform() calls: .sessionAttrs(sessionAttributes)\n     */\n    protected final Map<String, Object> sessionAttributes = new HashMap<>();\n\n    /**\n     * Mocked PlatformSession for platform API access.\n     * Automatically configured in setUp().\n     */\n    @Mock\n    protected PlatformSession platformSession;\n\n    /**\n     * Sets up MockMvc and configures platform API mocks before each test.\n     * Calls createController() and configureMocks() to allow test customization.\n     */\n    @BeforeEach\n    void setUp() throws Exception {\n        C controller = createController();\n        sessionAttributes.put(PlatformLoginServlet.PLATFORM_SESSION_PARAM_KEY, platformSession);\n        mockMvc = MockMvcBuilders.standaloneSetup(controller).build();\n        configureMocks(controller);\n    }\n\n    /**\n     * Creates the controller instance to be tested.\n     * Typically returns spy(new YourController()) to allow mocking of API accessor methods.\n     *\n     * @return the controller instance\n     */\n    protected abstract C createController();\n\n    /**\n     * Configures mock platform API dependencies for the controller.\n     * Called after MockMvc setup, before tests run.\n     * <p>\n     * Example:\n     *\n     * <pre>\n     *\n     * protected void configureMocks(MyController controller) throws Exception {\n     *     doReturn(platformAPI).when(controller).getPlatformAPI(any(PlatformSession.class));\n     * }\n     * </pre>\n     *\n     * @param controller the controller instance (same as returned by createController())\n     * @throws Exception if mock configuration fails\n     */\n    protected abstract void configureMocks(C controller) throws Exception;\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/RestControllerUtils.java",
    "content": "/**\n * Copyright (C) 2025 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.doReturn;\n\nimport java.nio.charset.StandardCharsets;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.console.common.server.utils.SessionUtil;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.server.SpringWebConfiguration;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.test.web.servlet.setup.StandaloneMockMvcBuilder;\nimport org.springframework.web.accept.ContentNegotiationManager;\nimport org.springframework.web.accept.FixedContentNegotiationStrategy;\n\n/**\n * Test-purpose utility class to initialize MockMvc for REST controllers with session attributes.\n */\npublic class RestControllerUtils {\n\n    /**\n     * Cached message converters, reusing the production SpringWebConfiguration\n     * to ensure tests use the exact same converter setup as production.\n     */\n    private static final List<HttpMessageConverter<?>> MESSAGE_CONVERTERS = SpringWebConfiguration\n            .createBonitaMessageConverters();\n\n    public static MockMvc initMockMvcWithSessionAttributes(AbstractRESTController controller,\n            Map<String, Object> sessionAttributes, APISession apiSession) {\n        doReturn(apiSession).when(controller).getApiSession(any());\n        sessionAttributes.put(SessionUtil.API_SESSION_PARAM_KEY, apiSession);\n\n        StandaloneMockMvcBuilder builder = MockMvcBuilders\n                .standaloneSetup(controller)\n                .setControllerAdvice(new SpringRestResponseEntityExceptionHandler(),\n                        new FilterParameterBindingAdvice())\n                // Match the production SpringWebConfiguration: default to JSON content type\n                // /!\\ Make sure that both use the same configuration /!\\\n                .setContentNegotiationManager(new ContentNegotiationManager(\n                        new FixedContentNegotiationStrategy(MediaType.APPLICATION_JSON)));\n\n        builder.setMessageConverters(MESSAGE_CONVERTERS.toArray(new HttpMessageConverter<?>[0]));\n\n        // Force UTF-8 response encoding so that MockHttpServletResponse.getContentAsString()\n        // correctly decodes UTF-8 bytes (servlet spec defaults to ISO-8859-1)\n        builder.defaultResponseCharacterEncoding(StandardCharsets.UTF_8);\n\n        return builder.build();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/application/APIApplicationTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.application;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.mock;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.ModelFactory;\nimport org.bonitasoft.web.rest.model.application.AbstractApplicationDefinition;\nimport org.bonitasoft.web.rest.model.application.AbstractApplicationItem;\nimport org.bonitasoft.web.rest.model.application.ApplicationItem;\nimport org.bonitasoft.web.rest.model.application.ApplicationLinkItem;\nimport org.bonitasoft.web.rest.model.portal.profile.ProfileItem;\nimport org.bonitasoft.web.rest.server.api.applicationpage.APIApplicationDataStoreFactory;\nimport org.bonitasoft.web.rest.server.api.deployer.DeployerFactory;\nimport org.bonitasoft.web.rest.server.api.deployer.GenericDeployer;\nimport org.bonitasoft.web.rest.server.api.deployer.PageDeployer;\nimport org.bonitasoft.web.rest.server.api.deployer.UserDeployer;\nimport org.bonitasoft.web.rest.server.datastore.application.ApplicationDataStore;\nimport org.bonitasoft.web.rest.server.datastore.application.ApplicationDataStoreCreator;\nimport org.bonitasoft.web.rest.server.datastore.page.PageDatastore;\nimport org.bonitasoft.web.rest.server.framework.APIServletCall;\nimport org.bonitasoft.web.rest.server.framework.Deployer;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.ItemDefinitionFactory;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class APIApplicationTest {\n\n    static {\n        ItemDefinitionFactory.setDefaultFactory(new ModelFactory());\n    }\n\n    @Mock\n    private ApplicationDataStore dataStore;\n\n    @Mock\n    private APIServletCall caller;\n\n    @Mock\n    private APIApplicationDataStoreFactory applicationDataStoreFactory;\n\n    @Mock\n    private ApplicationDataStoreCreator creator;\n\n    @Mock\n    private PageDatastore pageDatastore;\n\n    @InjectMocks\n    @Spy\n    private APIApplication apiApplication;\n\n    @Mock\n    private HttpSession httpSession;\n\n    @Mock\n    private APISession apiSession;\n\n    @Mock\n    private DeployerFactory deployerFactory;\n\n    @Before\n    public void setUp() throws Exception {\n        apiApplication.setCaller(caller);\n        given(caller.getHttpSession()).willReturn(httpSession);\n        given(httpSession.getAttribute(\"apiSession\")).willReturn(apiSession);\n        given(applicationDataStoreFactory.createPageDataStore(apiSession)).willReturn(pageDatastore);\n        given(creator.create(apiSession)).willReturn(dataStore);\n        doReturn(deployerFactory).when(apiApplication).getDeployerFactory();\n    }\n\n    @Test\n    public void add_legacy_should_return_the_result_of_dataStore_add() throws Exception {\n        //given\n        final ApplicationItem itemToCreate = mock(ApplicationItem.class);\n        final ApplicationItem createdItem = mock(ApplicationItem.class);\n        given(dataStore.add((AbstractApplicationItem) itemToCreate)).willReturn(createdItem);\n        //when\n        final ApplicationItem retrievedItem = (ApplicationItem) apiApplication.add(itemToCreate);\n\n        //then\n        assertThat(retrievedItem).isEqualTo(createdItem);\n    }\n\n    @Test\n    public void add_link_should_return_the_result_of_dataStore_add() throws Exception {\n        //given\n        final ApplicationLinkItem itemToCreate = mock(ApplicationLinkItem.class);\n        final ApplicationLinkItem createdItem = mock(ApplicationLinkItem.class);\n        given(dataStore.add((AbstractApplicationItem) itemToCreate)).willReturn(createdItem);\n        //when\n        final ApplicationLinkItem retrievedItem = (ApplicationLinkItem) apiApplication.add(itemToCreate);\n\n        //then\n        assertThat(retrievedItem).isEqualTo(createdItem);\n    }\n\n    @Test\n    public void search_should_return_the_result_of_dataStore_search() throws Exception {\n        //given\n        @SuppressWarnings(\"unchecked\")\n        final ItemSearchResult<AbstractApplicationItem> result = mock(ItemSearchResult.class);\n        given(dataStore.search(0, 10, \"request\", \"default\", Collections.singletonMap(\"name\", \"hr\"))).willReturn(result);\n\n        //when\n        final ItemSearchResult<AbstractApplicationItem> retrievedResult = apiApplication.search(0, 10, \"request\",\n                \"default\",\n                Collections.singletonMap(\"name\", \"hr\"));\n\n        //then\n        assertThat(retrievedResult).isEqualTo(result);\n    }\n\n    @Test\n    public void defineDefaultSearchOrder_should_return_attribute_name() throws Exception {\n        //when\n        final String defaultSearchOrder = apiApplication.defineDefaultSearchOrder();\n\n        //then\n        assertThat(defaultSearchOrder).isEqualTo(\"displayName\");\n    }\n\n    @Test\n    public void defineItemDefinition_return_an_instance_of_AbstractApplicationDefinition() throws Exception {\n        //when\n        final ItemDefinition<AbstractApplicationItem> itemDefinition = apiApplication.defineItemDefinition();\n\n        //then\n        assertThat(itemDefinition).isExactlyInstanceOf(AbstractApplicationDefinition.class);\n    }\n\n    @Test\n    public void fillDeploys_should_add_deployers_for_createdBy_updatedBy_ProfileId_and_LayoutId() throws Exception {\n        //given\n        final ApplicationItem item = mock(ApplicationItem.class);\n        doReturn(new GenericDeployer<ProfileItem>(null, ApplicationItem.ATTRIBUTE_PROFILE_ID)).when(deployerFactory)\n                .createProfileDeployer(ApplicationItem.ATTRIBUTE_PROFILE_ID);\n        //when\n        apiApplication.fillDeploys(item, Collections.<String> emptyList());\n\n        //then\n        final Map<String, Deployer> deployers = apiApplication.getDeployers();\n        assertThat(deployers).hasSize(5);\n        assertThat(deployers.keySet()).contains(ApplicationItem.ATTRIBUTE_CREATED_BY,\n                ApplicationItem.ATTRIBUTE_UPDATED_BY,\n                ApplicationItem.ATTRIBUTE_PROFILE_ID);\n        assertThat(deployers.get(ApplicationItem.ATTRIBUTE_CREATED_BY)).isExactlyInstanceOf(UserDeployer.class);\n        assertThat(deployers.get(ApplicationItem.ATTRIBUTE_UPDATED_BY)).isExactlyInstanceOf(UserDeployer.class);\n        assertThat(deployers.get(ApplicationItem.ATTRIBUTE_LAYOUT_ID)).isExactlyInstanceOf(PageDeployer.class);\n        assertThat(deployers.get(ApplicationItem.ATTRIBUTE_THEME_ID)).isExactlyInstanceOf(PageDeployer.class);\n    }\n\n    @Test\n    public void update_should_return_the_appropriate_application_kind() throws Exception {\n        //given\n        final APIID idToUpdate = mock(APIID.class);\n        final ApplicationItem updatedItem = mock(ApplicationItem.class);\n        Map<String, String> attributes = Collections.emptyMap();\n        given(dataStore.update(idToUpdate, attributes)).willReturn(updatedItem);\n\n        //when\n        final AbstractApplicationItem retrievedItem = apiApplication.update(idToUpdate, attributes);\n\n        //then\n        assertThat(retrievedItem).isEqualTo(updatedItem);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/applicationpage/APIApplicationPageTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.applicationpage;\n\nimport static java.util.Collections.singletonList;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\nimport java.util.Collections;\n\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.console.common.server.i18n.I18n;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.ModelFactory;\nimport org.bonitasoft.web.rest.model.application.ApplicationItem;\nimport org.bonitasoft.web.rest.model.applicationpage.ApplicationPageDefinition;\nimport org.bonitasoft.web.rest.model.applicationpage.ApplicationPageItem;\nimport org.bonitasoft.web.rest.model.portal.page.PageItem;\nimport org.bonitasoft.web.rest.server.datastore.application.ApplicationDataStore;\nimport org.bonitasoft.web.rest.server.datastore.applicationpage.ApplicationPageDataStore;\nimport org.bonitasoft.web.rest.server.datastore.page.PageDatastore;\nimport org.bonitasoft.web.rest.server.framework.APIServletCall;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.ItemDefinitionFactory;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Answers;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class APIApplicationPageTest {\n\n    static {\n        ItemDefinitionFactory.setDefaultFactory(new ModelFactory());\n        I18n.getInstance();\n    }\n\n    @Mock\n    private APIApplicationDataStoreFactory dataStoreFactory;\n\n    @Mock\n    private ApplicationPageDataStore applicationPageDataStore;\n\n    @Mock\n    private PageDatastore pageDataStore;\n\n    @Mock\n    private ApplicationDataStore applicationDataStore;\n\n    @InjectMocks\n    private APIApplicationPage apiApplicationPage;\n\n    @Mock(answer = Answers.RETURNS_MOCKS)\n    private APIServletCall caller;\n\n    @Mock\n    private HttpSession httpSession;\n\n    @Mock\n    private APISession apiSession;\n\n    @Before\n    public void setUp() throws Exception {\n        apiApplicationPage.setCaller(caller);\n        given(caller.getHttpSession()).willReturn(httpSession);\n        given(httpSession.getAttribute(\"apiSession\")).willReturn(apiSession);\n        given(dataStoreFactory.createApplicationPageDataStore(apiSession)).willReturn(applicationPageDataStore);\n        given(dataStoreFactory.createApplicationDataStore(apiSession)).willReturn(applicationDataStore);\n        given(dataStoreFactory.createPageDataStore(apiSession)).willReturn(pageDataStore);\n    }\n\n    @Test\n    public void add_should_return_the_result_of_dataStore_add() throws Exception {\n        //given\n        final ApplicationPageItem itemToCreate = mock(ApplicationPageItem.class);\n        final ApplicationPageItem createdItem = mock(ApplicationPageItem.class);\n        given(applicationPageDataStore.add(itemToCreate)).willReturn(createdItem);\n\n        //when\n        final ApplicationPageItem retrievedItem = apiApplicationPage.add(itemToCreate);\n\n        //then\n        assertThat(retrievedItem).isEqualTo(createdItem);\n    }\n\n    @Test\n    public void search_should_return_the_result_of_dataStore_search() throws Exception {\n        //given\n        @SuppressWarnings(\"unchecked\")\n        final ItemSearchResult<ApplicationPageItem> result = mock(ItemSearchResult.class);\n        given(applicationPageDataStore.search(0, 10, \"request\", \"default\", Collections.singletonMap(\"token\", \"page\")))\n                .willReturn(result);\n\n        //when\n        final ItemSearchResult<ApplicationPageItem> retrievedResult = apiApplicationPage.search(0, 10, \"request\",\n                \"default\",\n                Collections.singletonMap(\"token\", \"page\"));\n\n        //then\n        assertThat(retrievedResult).isEqualTo(result);\n    }\n\n    @Test\n    public void defineDefaultSearchOrder_should_return_attribute_token() throws Exception {\n        //when\n        final String defaultSearchOrder = apiApplicationPage.defineDefaultSearchOrder();\n\n        //then\n        assertThat(defaultSearchOrder).isEqualTo(\"token\");\n    }\n\n    @Test\n    public void defineItemDefinition_return_an_instance_of_ApplicationDefinition() throws Exception {\n        //when\n        final ItemDefinition<ApplicationPageItem> itemDefinition = apiApplicationPage.defineItemDefinition();\n\n        //then\n        assertThat(itemDefinition).isExactlyInstanceOf(ApplicationPageDefinition.class);\n    }\n\n    @Test\n    public void should_fill_application_deploy_when_requested() {\n        final ApplicationPageItem applicationPage = new ApplicationPageItem();\n        applicationPage.setApplicationId(1L);\n        final ApplicationItem application = new ApplicationItem();\n        application.setDisplayName(\"foo\");\n        given(applicationDataStore.get(APIID.makeAPIID(1L))).willReturn(application);\n\n        apiApplicationPage.fillDeploys(applicationPage, singletonList(ApplicationPageItem.ATTRIBUTE_APPLICATION_ID));\n\n        assertThat(applicationPage.getApplication()).isEqualTo(application);\n    }\n\n    @Test\n    public void should_fill_page_deploy_when_requested() {\n        final ApplicationPageItem applicationPage = new ApplicationPageItem();\n        applicationPage.setPageId(2L);\n        final PageItem customPage = new PageItem();\n        customPage.setContentName(\"bar\");\n        given(pageDataStore.get(APIID.makeAPIID(2L))).willReturn(customPage);\n\n        apiApplicationPage.fillDeploys(applicationPage, singletonList(ApplicationPageItem.ATTRIBUTE_PAGE_ID));\n\n        assertThat(applicationPage.getPage()).isEqualTo(customPage);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/bdm/BusinessDataControllerTest.java",
    "content": "/**\n * Copyright (C) 2025 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bdm;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.*;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.CommandAPI;\nimport org.bonitasoft.engine.bpm.businessdata.impl.BusinessDataQueryMetadataImpl;\nimport org.bonitasoft.engine.bpm.businessdata.impl.BusinessDataQueryResultImpl;\nimport org.bonitasoft.engine.business.data.BusinessDataNotFoundException;\nimport org.bonitasoft.engine.business.data.BusinessDataRepositoryException;\nimport org.bonitasoft.engine.command.CommandExecutionException;\nimport org.bonitasoft.engine.command.CommandNotFoundException;\nimport org.bonitasoft.engine.command.CommandParameterizationException;\nimport org.bonitasoft.engine.command.SCommandExecutionException;\nimport org.bonitasoft.web.rest.server.api.AbstractControllerTest;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mock;\nimport org.mockito.stubbing.Answer;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\n\nclass BusinessDataControllerTest extends AbstractControllerTest<BusinessDataController> {\n\n    private static final String FAKE_CLASS_NAME = \"org.bonitasoft.pojo.Employee\";\n    private static final long FAKE_ID = 1983L;\n    private static final String FAKE_EXCEPTION_MESSAGE = \"fake exception message\";\n\n    @Mock\n    protected CommandAPI commandAPI;\n\n    @Override\n    protected BusinessDataController createController() {\n        return spy(new BusinessDataController());\n    }\n\n    @Override\n    protected void configureMocks(BusinessDataController controller) throws Exception {\n        doReturn(commandAPI).when(controller).getCommandAPI(apiSession);\n    }\n\n    @Test\n    void should_return_the_business_data_based_on_its_id() throws Exception {\n        final Map<String, Serializable> parameters = new HashMap<>();\n        parameters.put(\"entityClassName\", FAKE_CLASS_NAME);\n        parameters.put(\"businessDataId\", FAKE_ID);\n        parameters.put(\"businessDataURIPattern\", \"/API/bdm/businessData/{className}/{id}/{field}\");\n        when(commandAPI.execute(\"getBusinessDataById\", parameters)).thenReturn(\"{\\\"name\\\":\\\"たこ焼き\\\"}\");\n\n        //when\n        mockMvc.perform(\n                get(\"/API/bdm/businessData/{className}/{id}\", FAKE_CLASS_NAME, FAKE_ID)\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n\n                //then\n                .andExpect(status().isOk())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(content().json(\"\"\"\n                        {\"name\":\"たこ焼き\"}\n                        \"\"\", true));\n    }\n\n    @Test\n    void should_get_return_a_not_found_error_status_when_command_is_not_found() throws Exception {\n        doThrow(new CommandNotFoundException(null)).when(commandAPI).execute(anyString(), anyMap());\n\n        //when\n        mockMvc.perform(\n                get(\"/API/bdm/businessData/{className}/{id}\", FAKE_CLASS_NAME, FAKE_ID)\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n\n                //then\n                .andExpect(status().isNotFound())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(jsonPath(\"$.exception\").value(CommandNotFoundException.class.toString()));\n    }\n\n    @Test\n    void should_get_return_an_error_when_id_is_not_a_integer() throws Exception {\n        var id = \"not_a_number\";\n        //when\n        mockMvc.perform(\n                get(\"/API/bdm/businessData/{className}/{id}\", FAKE_CLASS_NAME, id)\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n\n                //then\n                .andExpect(status().isBadRequest())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(jsonPath(\"$.exception\").value(IllegalArgumentException.class.toString()))\n                .andExpect(jsonPath(\"$.message\").value(\"[ %s ] must be a number\".formatted(id)));\n    }\n\n    @Test\n    void should_get_return_an_internal_server_error_status_when_command_is_not_well_parameterized()\n            throws Exception {\n        doThrow(new CommandParameterizationException(FAKE_EXCEPTION_MESSAGE))\n                .when(commandAPI).execute(anyString(), anyMap());\n\n        //when\n        mockMvc.perform(\n                get(\"/API/bdm/businessData/{className}/{id}\", FAKE_CLASS_NAME, FAKE_ID)\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n\n                //then\n                .andExpect(status().isInternalServerError())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(jsonPath(\"$.exception\").value(CommandParameterizationException.class.toString()))\n                .andExpect(jsonPath(\"$.message\").value(FAKE_EXCEPTION_MESSAGE));\n    }\n\n    @Test\n    void should_get_return_a_not_found_status_when_command_fails_business_data_not_found() throws Exception {\n        doThrow(newCommandExecutionException(\n                new BusinessDataNotFoundException(new RuntimeException(FAKE_EXCEPTION_MESSAGE))))\n                .when(commandAPI).execute(anyString(), anyMap());\n\n        //when\n        mockMvc.perform(\n                get(\"/API/bdm/businessData/{className}/{id}\", FAKE_CLASS_NAME, FAKE_ID)\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n\n                //then\n                .andExpect(status().isNotFound())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(jsonPath(\"$.exception\").value(BusinessDataNotFoundException.class.toString()))\n                .andExpect(jsonPath(\"$.message\").value(FAKE_EXCEPTION_MESSAGE));\n    }\n\n    @Test\n    void should_get_return_an_internal_server_error_status_when_command_fails_during_execution() throws Exception {\n        var exception = new BusinessDataRepositoryException(FAKE_EXCEPTION_MESSAGE);\n        doThrow(newCommandExecutionException(exception))\n                .when(commandAPI).execute(anyString(), anyMap());\n\n        //when\n        mockMvc.perform(\n                get(\"/API/bdm/businessData/{className}/{id}\", FAKE_CLASS_NAME, FAKE_ID)\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n\n                //then\n                .andExpect(status().isInternalServerError())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(jsonPath(\"$.exception\").value(CommandExecutionException.class.toString()))\n                .andExpect(jsonPath(\"$.message\").value(exception.toString()));\n    }\n\n    // =================================================================================================================\n    // Get field\n    // =================================================================================================================\n\n    @Test\n    void should_fetch_business_data_child_if_it_is_specified() throws Exception {\n        var fieldName = \"child\";\n        final Map<String, Serializable> parameters = new HashMap<>();\n        parameters.put(\"entityClassName\", FAKE_CLASS_NAME);\n        parameters.put(\"businessDataId\", FAKE_ID);\n        parameters.put(\"businessDataChildName\", fieldName);\n        parameters.put(\"businessDataURIPattern\", \"/API/bdm/businessData/{className}/{id}/{field}\");\n        when(commandAPI.execute(\"getBusinessDataById\", parameters)).thenReturn(\"{\\\"child\\\":\\\"Leo\\\"}\");\n\n        //when\n        mockMvc.perform(\n                get(\"/API/bdm/businessData/{className}/{id}/{fieldName}\", FAKE_CLASS_NAME, FAKE_ID, fieldName)\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n\n                //then\n                .andExpect(status().isOk())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(content().json(\"\"\"\n                        {\"child\":\"Leo\"}\n                        \"\"\", true));\n    }\n\n    @Test\n    void should_fetch_business_data_child_return_an_error_when_id_is_not_a_integer() throws Exception {\n        var id = \"wrong_id\";\n        //when\n        mockMvc.perform(\n                get(\"/API/bdm/businessData/{className}/{id}/{fieldName}\", FAKE_CLASS_NAME, id, \"child\")\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n\n                //then\n                .andExpect(status().isBadRequest())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(jsonPath(\"$.exception\").value(IllegalArgumentException.class.toString()))\n                .andExpect(jsonPath(\"$.message\").value(\"[ %s ] must be a number\".formatted(id)));\n    }\n\n    // =================================================================================================================\n    // Get list by ids\n    // =================================================================================================================\n\n    @Test\n    void should_return_the_list_of_business_objects() throws Exception {\n        final Map<String, Serializable> parameters = new HashMap<>();\n        parameters.put(\"entityClassName\", FAKE_CLASS_NAME);\n        parameters.put(\"businessDataIds\", (Serializable) List.of(1983L, 547862L));\n        parameters.put(\"businessDataURIPattern\", BusinessDataFieldValue.URI_PATTERN);\n\n        String jsonResponse = \"\"\"\n                    [\n                        {\"id\": 1983, \"name\": \"Matti\"},\n                        {\"id\": 547862, \"name\": \"Fred\"}\n                    ]\n                \"\"\";\n        when(commandAPI.execute(\"getBusinessDataByIds\", parameters)).thenReturn(jsonResponse);\n\n        //when\n        mockMvc.perform(\n                get(\"/API/bdm/businessData/{className}/findByIds\", FAKE_CLASS_NAME)\n                        .param(\"ids\", \"1983,547862\")\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n\n                //then\n                .andExpect(status().isOk())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(content().json(jsonResponse, true));\n    }\n\n    @Test\n    public void should_return_the_list_throw_an_error_when_no_ids_are_passed() throws Exception {\n        // when\n        mockMvc.perform(\n                get(\"/API/bdm/businessData/{className}/findByIds\", FAKE_CLASS_NAME)\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n\n                // then\n                .andExpect(status().isBadRequest())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(jsonPath(\"$.exception\").value(IllegalArgumentException.class.toString()))\n                .andExpect(jsonPath(\"$.message\").value(\"query parameter ids is mandatory\"));\n    }\n\n    @Test\n    public void should_return_the_list_throw_an_error_when_ids_not_integer_are_passed() throws Exception {\n        var ids = \"1983,abc\";\n        // when\n        mockMvc.perform(\n                get(\"/API/bdm/businessData/{className}/findByIds\", FAKE_CLASS_NAME)\n                        .param(\"ids\", ids)\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n\n                // then\n                .andExpect(status().isBadRequest())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(jsonPath(\"$.exception\").value(IllegalArgumentException.class.toString()))\n                .andExpect(jsonPath(\"$.message\").value(\"Bad parameter ids=\" + ids));\n    }\n\n    @Test\n    public void should_return_the_list_throw_not_found_when_CommandNotFoundException() throws Exception {\n        // given\n        when(commandAPI.execute(anyString(), anyMap()))\n                .thenThrow(new CommandNotFoundException(new RuntimeException(FAKE_EXCEPTION_MESSAGE)));\n\n        // when\n        mockMvc.perform(\n                get(\"/API/bdm/businessData/{className}/findByIds\", FAKE_CLASS_NAME)\n                        .param(\"ids\", \"1983,1984\")\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n\n                // then\n                .andExpect(status().isNotFound())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(jsonPath(\"$.exception\").value(CommandNotFoundException.class.toString()))\n                .andExpect(jsonPath(\"$.message\").value(FAKE_EXCEPTION_MESSAGE));\n    }\n\n    // =================================================================================================================\n    // Get with custom query\n    // =================================================================================================================\n\n    @Test\n    @SuppressWarnings(\"unchecked\")\n    void should_call_custom_query() throws Exception {\n        String jsonResponse = \"\"\"\n                    [\n                        {\"id\": 123, \"name\": \"Harry\"},\n                        {\"id\": 124, \"name\": \"Anna\"},\n                        {\"id\": 125, \"name\": \"John\"}\n                    ]\n                \"\"\";\n\n        final Answer<Serializable> answer = invocation -> {\n            assertThat(invocation.getArguments()).as(\"should have 2 parameters\").hasSize(2);\n            assertThat(invocation.getArguments()[0]).as(\"should call command\")\n                    .isEqualTo(\"getBusinessDataByQueryCommand\");\n            final Map<String, Serializable> parameters = (Map<String, Serializable>) invocation.getArguments()[1];\n            assertThat(parameters).as(\"should have required  parameters\").hasSize(6);\n            assertThat(parameters).as(\"should compute start index\").containsEntry(\"startIndex\", 3 * 5);\n            assertThat(parameters).containsEntry(\"maxResults\", 5);\n            assertThat(parameters).containsEntry(\"queryName\", \"findByName\");\n            assertThat(parameters).containsEntry(\"businessDataURIPattern\", BusinessDataFieldValue.URI_PATTERN);\n            assertThat(parameters).containsEntry(\"entityClassName\", FAKE_CLASS_NAME);\n            assertThat(parameters).containsKey(\"queryParameters\");\n\n            final Map<String, Serializable> queryParameters = (Map<String, Serializable>) parameters\n                    .get(\"queryParameters\");\n            assertThat(queryParameters).as(\"should compute search filters\").hasSize(2);\n            assertThat(queryParameters).containsEntry(\"name\", \"John\");\n            assertThat(queryParameters).containsEntry(\"country\", \"US\");\n\n            return new BusinessDataQueryResultImpl(jsonResponse, new BusinessDataQueryMetadataImpl(1, 2, 4L));\n        };\n        when(commandAPI.execute(anyString(), anyMap())).then(answer);\n\n        //when\n        mockMvc.perform(\n                get(\"/API/bdm/businessData/{className}\", FAKE_CLASS_NAME)\n                        .param(\"q\", \"findByName\")\n                        .param(\"c\", \"5\")\n                        .param(\"p\", \"3\")\n                        .param(\"f\", \"name=John\")\n                        .param(\"f\", \"country=US\")\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n\n                //then\n                .andExpect(status().isOk())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(content().json(jsonResponse, true))\n                .andExpect(header().string(HttpHeaders.CONTENT_RANGE, \"3-5/4\"));\n    }\n\n    @Test\n    @SuppressWarnings(\"unchecked\")\n    void should_call_custom_query_single_multivalued_query_parameter() throws Exception {\n        String jsonResponse = \"\"\"\n                    [\n                        {\"id\": 123, \"name\": \"Harry\", \"country\": \"US\"},\n                        {\"id\": 124, \"name\": \"Anna\", \"country\": \"US\"},\n                        {\"id\": 129, \"name\": \"Harry\", \"country\": \"Scotland\"},\n                        {\"id\": 130, \"name\": \"Anna\", \"country\": \"Catalunya\"},\n                        {\"id\": 137, \"name\": \"Anna\", \"country\": \"Quebec\"},\n                        {\"id\": 145, \"name\": \"Anna\", \"country\": \"Corsica\"}\n                    ]\n                \"\"\";\n\n        final Answer<Serializable> answer = invocation -> {\n            assertThat(invocation.getArguments()).as(\"should have 2 parameters\").hasSize(2);\n            assertThat(invocation.getArguments()[0]).as(\"should call command\")\n                    .isEqualTo(\"getBusinessDataByQueryCommand\");\n            final Map<String, Serializable> parameters = (Map<String, Serializable>) invocation.getArguments()[1];\n            assertThat(parameters).as(\"should have required  parameters\").hasSize(6);\n            assertThat(parameters).as(\"should compute start index\").containsEntry(\"startIndex\", 2 * 6);\n            assertThat(parameters).containsEntry(\"maxResults\", 6);\n            assertThat(parameters).containsEntry(\"queryName\", \"findByNames\");\n            assertThat(parameters).containsEntry(\"businessDataURIPattern\", BusinessDataFieldValue.URI_PATTERN);\n            assertThat(parameters).containsEntry(\"entityClassName\", FAKE_CLASS_NAME);\n            assertThat(parameters).containsKey(\"queryParameters\");\n\n            final Map<String, Serializable> queryParameters = (Map<String, Serializable>) parameters\n                    .get(\"queryParameters\");\n            assertThat(queryParameters).as(\"should compute search filters\").hasSize(1);\n            assertThat(queryParameters).containsEntry(\"names\", \"Harry,Anna\");\n\n            return new BusinessDataQueryResultImpl(jsonResponse, new BusinessDataQueryMetadataImpl(1, 2, 26L));\n        };\n        when(commandAPI.execute(anyString(), anyMap())).then(answer);\n\n        //when\n        mockMvc.perform(\n                get(\"/API/bdm/businessData/{className}\", FAKE_CLASS_NAME)\n                        .param(\"q\", \"findByNames\")\n                        .param(\"c\", \"6\")\n                        .param(\"p\", \"2\")\n                        .param(\"f\", \"names=Harry,Anna\")\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n\n                //then\n                .andExpect(status().isOk())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(content().json(jsonResponse, true))\n                .andExpect(header().string(HttpHeaders.CONTENT_RANGE, \"2-6/26\"));\n    }\n\n    @Test\n    @SuppressWarnings(\"unchecked\")\n    void should_call_custom_query_multiple_multivalued_query_parameters() throws Exception {\n        String jsonResponse = \"\"\"\n                    [\n                        {\"id\": 129, \"name\": \"Harry\", \"country\": \"Scotland\"},\n                        {\"id\": 130, \"name\": \"Anna\", \"country\": \"Catalunya\"},\n                        {\"id\": 137, \"name\": \"Anna\", \"country\": \"Quebec\"},\n                        {\"id\": 145, \"name\": \"Anna\", \"country\": \"Corsica\"}\n                    ]\n                \"\"\";\n\n        final Answer<Serializable> answer = invocation -> {\n            assertThat(invocation.getArguments()).as(\"should have 2 parameters\").hasSize(2);\n            assertThat(invocation.getArguments()[0]).as(\"should call command\")\n                    .isEqualTo(\"getBusinessDataByQueryCommand\");\n            final Map<String, Serializable> parameters = (Map<String, Serializable>) invocation.getArguments()[1];\n            assertThat(parameters).as(\"should have required  parameters\").hasSize(6);\n            assertThat(parameters).as(\"should compute start index\").containsEntry(\"startIndex\", 4 * 8);\n            assertThat(parameters).containsEntry(\"maxResults\", 8);\n            assertThat(parameters).containsEntry(\"queryName\", \"findByNamesAndCountries\");\n            assertThat(parameters).containsEntry(\"businessDataURIPattern\", BusinessDataFieldValue.URI_PATTERN);\n            assertThat(parameters).containsEntry(\"entityClassName\", FAKE_CLASS_NAME);\n            assertThat(parameters).containsKey(\"queryParameters\");\n\n            final Map<String, Serializable> queryParameters = (Map<String, Serializable>) parameters\n                    .get(\"queryParameters\");\n            assertThat(queryParameters).as(\"should compute search filters\").hasSize(2);\n            assertThat(queryParameters).containsEntry(\"names\", \"Harry,Anna\");\n            assertThat(queryParameters).containsEntry(\"countries\", \"Catalunya,Corsica,Scotland,Quebec\");\n\n            return new BusinessDataQueryResultImpl(jsonResponse, new BusinessDataQueryMetadataImpl(1, 2, 36L));\n        };\n        when(commandAPI.execute(anyString(), anyMap())).then(answer);\n\n        //when\n        mockMvc.perform(\n                get(\"/API/bdm/businessData/{className}\", FAKE_CLASS_NAME)\n                        .param(\"q\", \"findByNamesAndCountries\")\n                        .param(\"c\", \"8\")\n                        .param(\"p\", \"4\")\n                        .param(\"f\", \"names=Harry,Anna\", \"countries=Catalunya,Corsica,Scotland,Quebec\")\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n\n                //then\n                .andExpect(status().isOk())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(content().json(jsonResponse, true))\n                .andExpect(header().string(HttpHeaders.CONTENT_RANGE, \"4-8/36\"));\n    }\n\n    @Test\n    void should_call_custom_query_throw_exception_when_missing_count() throws Exception {\n        // when\n        mockMvc.perform(\n                get(\"/API/bdm/businessData/{className}\", FAKE_CLASS_NAME)\n                        .param(\"q\", \"findByName\")\n                        .param(\"p\", \"0\")\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n\n                // then\n                .andExpect(status().isBadRequest())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(jsonPath(\"$.exception\").value(IllegalArgumentException.class.toString()))\n                .andExpect(jsonPath(\"$.message\").value(\"query parameter c (count) is mandatory\"));\n    }\n\n    @Test\n    void should_call_custom_query_throw_exception_when_count_conversion_fails() throws Exception {\n        // when\n        mockMvc.perform(\n                get(\"/API/bdm/businessData/{className}\", FAKE_CLASS_NAME)\n                        .param(\"q\", \"findByName\")\n                        .param(\"p\", \"0\")\n                        .param(\"c\", \"abc\")\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n\n                // then\n                .andExpect(status().isBadRequest())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(jsonPath(\"$.exception\").value(IllegalArgumentException.class.toString()))\n                .andExpect(jsonPath(\"$.message\").value(\"query parameter c (count) should be a number\"));\n    }\n\n    @Test\n    public void should_call_custom_query_throw_exception_when_missing_page() throws Exception {\n        // when\n        mockMvc.perform(\n                get(\"/API/bdm/businessData/{className}\", FAKE_CLASS_NAME)\n                        .param(\"q\", \"findByName\")\n                        .param(\"c\", \"0\")\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n\n                // then\n                .andExpect(status().isBadRequest())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(jsonPath(\"$.exception\").value(IllegalArgumentException.class.toString()))\n                .andExpect(jsonPath(\"$.message\").value(\"query parameter p (page) is mandatory\"));\n    }\n\n    @Test\n    public void should_call_custom_query_throw_exception_when_page_conversion_fails() throws Exception {\n        // when\n        mockMvc.perform(\n                get(\"/API/bdm/businessData/{className}\", FAKE_CLASS_NAME)\n                        .param(\"q\", \"findByName\")\n                        .param(\"c\", \"0\")\n                        .param(\"p\", \"abc\")\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n\n                // then\n                .andExpect(status().isBadRequest())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(jsonPath(\"$.exception\").value(IllegalArgumentException.class.toString()))\n                .andExpect(jsonPath(\"$.message\").value(\"query parameter p (page) should be a number\"));\n    }\n\n    @Test\n    void should_call_custom_query_throw_not_found_when_CommandNotFoundException() throws Exception {\n        // given\n        when(commandAPI.execute(anyString(), anyMap()))\n                .thenThrow(new CommandNotFoundException(new RuntimeException(FAKE_EXCEPTION_MESSAGE)));\n\n        // when\n        mockMvc.perform(\n                get(\"/API/bdm/businessData/{className}\", FAKE_CLASS_NAME)\n                        .param(\"q\", \"findByName\")\n                        .param(\"c\", \"5\")\n                        .param(\"p\", \"3\")\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n\n                // then\n                .andExpect(status().isNotFound())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(jsonPath(\"$.exception\").value(CommandNotFoundException.class.toString()))\n                .andExpect(jsonPath(\"$.message\").value(FAKE_EXCEPTION_MESSAGE));\n    }\n\n    @Test\n    public void should_call_custom_query_return_an_internal_server_error_status_when_command_fails_during_execution()\n            throws Exception {\n        // given\n        var exception = new BusinessDataRepositoryException(FAKE_EXCEPTION_MESSAGE);\n        when(commandAPI.execute(anyString(), anyMap()))\n                .thenThrow(newCommandExecutionException(exception));\n\n        // when\n        mockMvc.perform(\n                get(\"/API/bdm/businessData/{className}\", FAKE_CLASS_NAME)\n                        .param(\"q\", \"findByName\")\n                        .param(\"c\", \"5\")\n                        .param(\"p\", \"3\")\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n\n                // then\n                .andExpect(status().isInternalServerError())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(jsonPath(\"$.exception\").value(CommandExecutionException.class.toString()))\n                .andExpect(jsonPath(\"$.message\").value(exception.toString()));\n    }\n\n    // wrap the root cause in the same way the command api does\n    private static CommandExecutionException newCommandExecutionException(Exception cause) {\n        return new CommandExecutionException(new SCommandExecutionException((cause)));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/bdm/BusinessDataModelControllerTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bdm;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.time.Instant;\nimport java.time.OffsetDateTime;\nimport java.time.ZoneOffset;\nimport java.time.format.DateTimeFormatter;\nimport java.util.Date;\n\nimport org.apache.commons.io.IOUtils;\nimport org.bonitasoft.engine.api.TenantAdministrationAPI;\nimport org.bonitasoft.engine.business.data.BusinessDataRepositoryDeploymentException;\nimport org.bonitasoft.engine.business.data.InvalidBusinessDataModelException;\nimport org.bonitasoft.engine.exception.TenantStatusException;\nimport org.bonitasoft.engine.io.FileContent;\nimport org.bonitasoft.engine.tenant.TenantResource;\nimport org.bonitasoft.engine.tenant.TenantResourceState;\nimport org.bonitasoft.engine.tenant.TenantResourceType;\nimport org.bonitasoft.web.rest.server.api.AbstractControllerTest;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mock;\nimport org.springframework.http.MediaType;\n\nclass BusinessDataModelControllerTest extends AbstractControllerTest<BusinessDataModelController> {\n\n    @Mock\n    protected TenantAdministrationAPI tenantAdministrationAPI;\n\n    private BusinessDataModelController controller;\n\n    @Override\n    protected BusinessDataModelController createController() {\n        controller = spy(new BusinessDataModelController());\n        return controller;\n    }\n\n    @Override\n    protected void configureMocks(BusinessDataModelController controller) throws Exception {\n        doReturn(tenantAdministrationAPI).when(controller).getTenantAdministrationAPI(any());\n        doReturn(true).when(tenantAdministrationAPI).isPaused();\n    }\n\n    @Test\n    void should_retrieve_bdm_from_engine_when_getting_bdm_from_api() throws Exception {\n        long dateInMillis = new Date().getTime();\n        final String formattedDate = DateTimeFormatter.ISO_OFFSET_DATE_TIME\n                .format(OffsetDateTime.ofInstant(Instant.ofEpochMilli(dateInMillis),\n                        ZoneOffset.UTC));\n        when(tenantAdministrationAPI.getBusinessDataModelResource())\n                .thenReturn(new TenantResource(1, \"bdm.zip\", TenantResourceType.BDM, dateInMillis, 12,\n                        TenantResourceState.INSTALLED));\n\n        mockMvc.perform(\n                get(\"/API/tenant/bdm\")\n                        .contentType(MediaType.APPLICATION_JSON)\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(content().json(\"\"\"\n                        {\n                            \"id\":\"1\",\n                            \"name\":\"bdm.zip\",\n                            \"type\":\"BDM\",\n                            \"state\":\"INSTALLED\",\n                            \"lastUpdatedBy\":\"12\",\n                            \"lastUpdateDate\":\"%s\",\n                            \"fileUpload\":\"\"\n                        }\n                        \"\"\".formatted(formattedDate), true));\n\n        verify(tenantAdministrationAPI).getBusinessDataModelResource();\n    }\n\n    @Test\n    void should_update_new_bdm() throws Exception {\n        byte[] bdmFileContent = getContent(\"bizdatamodel.zip\");\n        final TenantResource tenantResource = new TenantResource(\n                1L, \"bizdatamodel\", TenantResourceType.BDM, 1L, 1L,\n                TenantResourceState.INSTALLED);\n        doReturn(testBDMFile()).when(controller).getBusinessDataModel(any());\n        doReturn(\"1.0\").when(tenantAdministrationAPI).updateBusinessDataModel(bdmFileContent);\n        doReturn(tenantResource).when(tenantAdministrationAPI).getBusinessDataModelResource();\n\n        mockMvc.perform(\n                post(\"/API/tenant/bdm\")\n                        .contentType(MediaType.APPLICATION_JSON)\n                        .content(\"\"\"\n                                {\"fileUpload\": \"bizdatamodel\"}\"\"\")\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(content().json(\"\"\"\n                        {\n                            \"id\":\"1\",\n                            \"name\":\"bizdatamodel\",\n                            \"type\":\"BDM\",\n                            \"state\":\"INSTALLED\",\n                            \"lastUpdatedBy\":\"1\",\n                            \"lastUpdateDate\":\"1970-01-01T00:00:00.001Z\",\n                            \"fileUpload\":\"bizdatamodel.zip\"\n                        }\"\"\", true));\n\n        verify(tenantAdministrationAPI).updateBusinessDataModel(bdmFileContent);\n    }\n\n    private FileContent testBDMFile() {\n        return new FileContent(\"bizdatamodel.zip\",\n                BusinessDataModelControllerTest.class.getResourceAsStream(\"bizdatamodel.zip\"), \"application/zip\");\n    }\n\n    private byte[] getContent(String resource) throws IOException {\n        try (final InputStream resourceAsStream = BusinessDataModelControllerTest.class.getResourceAsStream(resource)) {\n            Assertions.assertNotNull(resourceAsStream);\n            return IOUtils.toByteArray(resourceAsStream);\n        }\n    }\n\n    @Test\n    void install_should_throw_APIException_if_InvalidBusinessDataModelException_occurs() throws Exception {\n        doReturn(testBDMFile()).when(controller).getBusinessDataModel(any());\n        doThrow(new InvalidBusinessDataModelException(new Exception(\"invalid model\"))).when(tenantAdministrationAPI)\n                .updateBusinessDataModel(any(byte[].class));\n\n        mockMvc.perform(\n                post(\"/API/tenant/bdm\")\n                        .contentType(MediaType.APPLICATION_JSON)\n                        .content(\"\"\"\n                                {\"fileUpload\": \"invalid bdm\"}\"\"\")\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isBadRequest())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(jsonPath(\"$.exception\").value(InvalidBusinessDataModelException.class.toString()))\n                .andExpect(jsonPath(\"$.message\").value(\"invalid model\"));\n    }\n\n    @Test\n    void should_throw_APIException_if_BusinessDataRepositoryDeploymentException_occurs()\n            throws Exception {\n        doReturn(testBDMFile()).when(controller).getBusinessDataModel(any());\n        doThrow(new BusinessDataRepositoryDeploymentException(\"repository deployment exception\"))\n                .when(tenantAdministrationAPI)\n                .updateBusinessDataModel(any());\n\n        mockMvc.perform(\n                post(\"/API/tenant/bdm\")\n                        .contentType(MediaType.APPLICATION_JSON)\n                        .content(\"\"\"\n                                {\"fileUpload\": \"bizdatamodel\"}\"\"\")\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isInternalServerError())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(jsonPath(\"$.exception\").value(APIException.class.toString()))\n                .andExpect(jsonPath(\"$.message\").value(\"repository deployment exception\"));\n    }\n\n    @Test\n    void cant_install_bdm_if_tenant_is_not_paused() throws Exception {\n        doReturn(testBDMFile()).when(controller).getBusinessDataModel(any());\n        doReturn(false).when(tenantAdministrationAPI).isPaused();\n\n        mockMvc.perform(\n                post(\"/API/tenant/bdm\")\n                        .contentType(MediaType.APPLICATION_JSON)\n                        .content(\"\"\"\n                                {\"fileUpload\": \"bizdatamodel\"}\"\"\")\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isForbidden())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(jsonPath(\"$.exception\").value(TenantStatusException.class.toString()))\n                .andExpect(jsonPath(\"$.message\").value(\n                        \"Unable to install the Business Data Model. Please pause the BPM Services first. \" +\n                                \"Go to Configuration > BPM Services.\"));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/bdm/BusinessDataReferenceControllerTest.java",
    "content": "/**\n * Copyright (C) 2025 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bdm;\n\nimport static org.mockito.Mockito.*;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.BusinessDataAPI;\nimport org.bonitasoft.engine.bpm.data.DataNotFoundException;\nimport org.bonitasoft.engine.business.data.BusinessDataReference;\nimport org.bonitasoft.engine.business.data.MultipleBusinessDataReference;\nimport org.bonitasoft.engine.business.data.SimpleBusinessDataReference;\nimport org.bonitasoft.engine.business.data.impl.MultipleBusinessDataReferenceImpl;\nimport org.bonitasoft.engine.business.data.impl.SimpleBusinessDataReferenceImpl;\nimport org.bonitasoft.web.rest.server.api.AbstractControllerTest;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mock;\nimport org.springframework.http.MediaType;\n\nclass BusinessDataReferenceControllerTest extends AbstractControllerTest<BusinessDataReferenceController> {\n\n    private static final long FAKE_CASE_ID = 486L;\n    private static final String FAKE_DATA_NAME = \"myEmployee\";\n    private static final String FAKE_EXCEPTION_MESSAGE = \"fake exception message\";\n\n    @Mock\n    protected BusinessDataAPI businessDataAPI;\n\n    @Override\n    protected BusinessDataReferenceController createController() {\n        return spy(new BusinessDataReferenceController());\n    }\n\n    @Override\n    protected void configureMocks(BusinessDataReferenceController controller) throws Exception {\n        doReturn(businessDataAPI).when(controller).getBusinessDataAPI(apiSession);\n    }\n\n    private SimpleBusinessDataReference buildSimpleEmployeeReference(String name, long businessDataId) {\n        return new SimpleBusinessDataReferenceImpl(name, \"com.bonitasoft.pojo.Employee\", businessDataId);\n    }\n\n    private MultipleBusinessDataReference buildMultipleEmployeeReference(String name, long... businessDataIds) {\n        List<Long> ids = new ArrayList<>();\n        for (long businessDataId : businessDataIds) {\n            ids.add(businessDataId);\n        }\n        return new MultipleBusinessDataReferenceImpl(name, \"com.bonitasoft.pojo.Employee\", ids);\n    }\n\n    // =================================================================================================================\n    // GET single reference: /API/bdm/businessDataReference/{caseId}/{dataName}\n    // =================================================================================================================\n\n    @Test\n    void should_return_the_simple_reference_of_the_business_data_of_the_process_instance() throws Exception {\n        // given\n        SimpleBusinessDataReference reference = buildSimpleEmployeeReference(FAKE_DATA_NAME, 487467354L);\n        when(businessDataAPI.getProcessBusinessDataReference(FAKE_DATA_NAME, FAKE_CASE_ID)).thenReturn(reference);\n\n        // when/then\n        mockMvc.perform(\n                get(\"/API/bdm/businessDataReference/{caseId}/{dataName}\", FAKE_CASE_ID, FAKE_DATA_NAME)\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(content().json(\"\"\"\n                        {\n                          \"name\": \"myEmployee\",\n                          \"type\": \"com.bonitasoft.pojo.Employee\",\n                          \"storageId\": 487467354,\n                          \"storageId_string\": \"487467354\",\n                          \"link\": \"API/bdm/businessData/com.bonitasoft.pojo.Employee/487467354\"\n                        }\n                        \"\"\", true));\n    }\n\n    @Test\n    void should_return_the_multi_reference_of_the_business_data_of_the_process_instance() throws Exception {\n        // given\n        MultipleBusinessDataReference reference = buildMultipleEmployeeReference(FAKE_DATA_NAME, 487467354L,\n                48674634L);\n        when(businessDataAPI.getProcessBusinessDataReference(FAKE_DATA_NAME, FAKE_CASE_ID)).thenReturn(reference);\n\n        // when/then\n        mockMvc.perform(\n                get(\"/API/bdm/businessDataReference/{caseId}/{dataName}\", FAKE_CASE_ID, FAKE_DATA_NAME)\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(content().json(\"\"\"\n                        {\n                          \"name\": \"myEmployee\",\n                          \"type\": \"com.bonitasoft.pojo.Employee\",\n                          \"storageIds\": [487467354, 48674634],\n                          \"storageIds_string\": [\"487467354\", \"48674634\"],\n                          \"link\": \"API/bdm/businessData/com.bonitasoft.pojo.Employee/findByIds?ids=487467354,48674634\"\n                        }\n                        \"\"\", true));\n    }\n\n    @Test\n    void should_respond_bad_request_when_caseId_pathparam_is_not_a_number() throws Exception {\n        // when/then\n        var caseId = \"foo\";\n        mockMvc.perform(\n                get(\"/API/bdm/businessDataReference/{caseId}/{dataName}\", caseId, FAKE_DATA_NAME)\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isBadRequest())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(jsonPath(\"$.exception\").value(IllegalArgumentException.class.toString()))\n                .andExpect(jsonPath(\"$.message\").value(\"[ %s ] must be a number\".formatted(caseId)));\n    }\n\n    @Test\n    void should_return_a_not_found_status_when_business_data_is_not_found() throws Exception {\n        // given\n        when(businessDataAPI.getProcessBusinessDataReference(FAKE_DATA_NAME, FAKE_CASE_ID))\n                .thenThrow(new DataNotFoundException(new Exception(FAKE_EXCEPTION_MESSAGE)));\n\n        // when/then\n        mockMvc.perform(\n                get(\"/API/bdm/businessDataReference/{caseId}/{dataName}\", FAKE_CASE_ID, FAKE_DATA_NAME)\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isNotFound())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(jsonPath(\"$.exception\").value(DataNotFoundException.class.toString()))\n                .andExpect(jsonPath(\"$.message\").value(FAKE_EXCEPTION_MESSAGE));\n\n    }\n\n    // =================================================================================================================\n    // GET list of references: /API/bdm/businessDataReference?f=caseId=X&p=X&c=X\n    // =================================================================================================================\n\n    @Test\n    void should_return_the_references_of_the_business_data_of_the_process_instance() throws Exception {\n        // given\n        List<BusinessDataReference> references = new ArrayList<>();\n        references.add(buildSimpleEmployeeReference(\"john\", 487467354L));\n        references.add(buildMultipleEmployeeReference(\"Ateam\", 687646784L, 2313213874354L));\n        when(businessDataAPI.getProcessBusinessDataReferences(FAKE_CASE_ID, 10, 10)).thenReturn(references);\n\n        // when/then\n        mockMvc.perform(\n                get(\"/API/bdm/businessDataReference\")\n                        .param(\"f\", \"caseId=\" + FAKE_CASE_ID)\n                        .param(\"p\", \"1\")\n                        .param(\"c\", \"10\")\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(content().json(\n                        \"\"\"\n                                [\n                                  {\n                                    \"name\": \"john\",\n                                    \"type\": \"com.bonitasoft.pojo.Employee\",\n                                    \"storageId\": 487467354,\n                                    \"storageId_string\": \"487467354\",\n                                    \"link\": \"API/bdm/businessData/com.bonitasoft.pojo.Employee/487467354\"\n                                  },\n                                  {\n                                    \"name\": \"Ateam\",\n                                    \"type\": \"com.bonitasoft.pojo.Employee\",\n                                    \"storageIds\": [687646784, 2313213874354],\n                                    \"storageIds_string\": [\"687646784\", \"2313213874354\"],\n                                    \"link\": \"API/bdm/businessData/com.bonitasoft.pojo.Employee/findByIds?ids=687646784,2313213874354\"\n                                  }\n                                ]\n                                \"\"\",\n                        true));\n    }\n\n    @Test\n    void should_respond_bad_request_when_caseId_filter_not_specified() throws Exception {\n        // when/then\n        mockMvc.perform(\n                get(\"/API/bdm/businessDataReference\")\n                        .param(\"f\", \"unknownfilter=123\")\n                        .param(\"p\", \"0\")\n                        .param(\"c\", \"10\")\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isBadRequest())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(jsonPath(\"$.exception\").value(IllegalArgumentException.class.toString()))\n                .andExpect(jsonPath(\"$.message\").value(\"filter caseId is mandatory\"));\n    }\n\n    @Test\n    void should_be_valid_when_extra_unwanted_filter_is_provided() throws Exception {\n        // when/then\n        mockMvc.perform(\n                get(\"/API/bdm/businessDataReference\")\n                        .param(\"f\", \"unknownfilter=123\")\n                        .param(\"f\", \"caseId=\" + FAKE_CASE_ID)\n                        .param(\"p\", \"0\")\n                        .param(\"c\", \"10\")\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(content().json(\"[]\", true));\n    }\n\n    @Test\n    void should_respond_bad_request_when_caseId_filter_is_not_a_number() throws Exception {\n        // when/then\n        mockMvc.perform(\n                get(\"/API/bdm/businessDataReference\")\n                        .param(\"f\", \"caseId=toto\")\n                        .param(\"p\", \"0\")\n                        .param(\"c\", \"10\")\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isBadRequest())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(jsonPath(\"$.exception\").value(IllegalArgumentException.class.toString()))\n                .andExpect(jsonPath(\"$.message\").value(\"filter caseId must be a number\"));\n    }\n\n    @Test\n    void should_respond_bad_request_when_page_parameter_is_missing() throws Exception {\n        // when/then\n        mockMvc.perform(\n                get(\"/API/bdm/businessDataReference\")\n                        .param(\"f\", \"caseId=\" + FAKE_CASE_ID)\n                        .param(\"c\", \"10\")\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isBadRequest())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(jsonPath(\"$.exception\").value(IllegalArgumentException.class.toString()))\n                .andExpect(jsonPath(\"$.message\").value(\"query parameter p (page) is mandatory\"));\n    }\n\n    @Test\n    void should_respond_bad_request_when_count_parameter_is_missing() throws Exception {\n        // when/then\n        mockMvc.perform(\n                get(\"/API/bdm/businessDataReference\")\n                        .param(\"f\", \"caseId=\" + FAKE_CASE_ID)\n                        .param(\"p\", \"0\")\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isBadRequest())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(jsonPath(\"$.exception\").value(IllegalArgumentException.class.toString()))\n                .andExpect(jsonPath(\"$.message\").value(\"query parameter c (count) is mandatory\"));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/bpm/cases/APIArchivedCaseTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.cases;\n\nimport static org.mockito.ArgumentMatchers.anyLong;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.MockitoAnnotations.initMocks;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstance;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.bonitasoft.web.rest.server.datastore.bpm.cases.ArchivedCaseDatastore;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Nicolas TITH\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class APIArchivedCaseTest extends APITestWithMock {\n\n    @InjectMocks\n    private APIArchivedCase apiArchivedCase;\n\n    @Mock\n    private APISession apiSession;\n\n    @Spy\n    private final ArchivedCaseDatastore archivedCaseDatastore = new ArchivedCaseDatastore(apiSession);\n\n    @Mock\n    private ProcessAPI processAPI;\n\n    @Mock\n    private ArchivedProcessInstance archivedProcessInstance;\n\n    @Before\n    public void initializeMocks() throws Exception {\n        initMocks(this);\n        apiArchivedCase = spy(new APIArchivedCase());\n        doReturn(archivedCaseDatastore).when(apiArchivedCase).defineDefaultDatastore();\n        doReturn(processAPI).when(archivedCaseDatastore).getProcessApi();\n        doReturn(archivedProcessInstance).when(processAPI).getArchivedProcessInstance(anyLong());\n    }\n\n    @Test\n    public void deleteShouldDeleteSeveralItem() throws Exception {\n        //given\n        final List<APIID> idList = Arrays.asList(APIID.makeAPIID(1L), APIID.makeAPIID(2L), APIID.makeAPIID(3L));\n        //when\n        apiArchivedCase.delete(idList);\n\n        //then\n        verify(archivedCaseDatastore).delete(idList);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/bpm/cases/APICaseDocumentTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.cases;\n\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.MockitoAnnotations.initMocks;\n\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseDocumentItem;\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDocumentDatastore;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\n\npublic class APICaseDocumentTest extends APITestWithMock {\n\n    @Spy\n    private APICaseDocument apiDocument;\n\n    @Mock\n    private CaseDocumentDatastore datastore;\n\n    @Mock\n    private CaseDocumentItem documentItemMock;\n\n    @Before\n    public void initializeMocks() {\n        initMocks(this);\n        // apiDocument = spy(new APIDocument());\n        doReturn(datastore).when(apiDocument).getCaseDocumentDatastore();\n    }\n\n    @Test\n    public void it_should_call_the_datastore_get_method() {\n        // Given\n        final APIID id = APIID.makeAPIID(1L);\n\n        // When\n        apiDocument.get(id);\n\n        // Then\n        verify(datastore).get(id);\n    }\n\n    @Test\n    public void it_should_call_the_datastore_add_method() {\n        // Given\n\n        // When\n        apiDocument.add(documentItemMock);\n\n        // Then\n        verify(datastore).add(documentItemMock);\n    }\n\n    @Test\n    public void it_should_call_the_datastore_update_method() {\n        // Given\n        final APIID id = APIID.makeAPIID(1L);\n\n        // When\n        apiDocument.update(id, null);\n\n        // Then\n        verify(datastore).update(id, null);\n    }\n\n    @Test\n    public void it_should_call_the_datastore_search_method() {\n        // When\n        apiDocument.search(0, 10, \"hello\", \"documentName ASC\", null);\n\n        // Then\n        verify(datastore).search(0, 10, \"hello\", null, \"documentName ASC\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/bpm/cases/APICaseTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.cases;\n\nimport static org.junit.Assert.*;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.*;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceCriterion;\nimport org.bonitasoft.web.rest.model.ModelFactory;\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.FlowNodeItem;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessItem;\nimport org.bonitasoft.web.rest.model.identity.UserItem;\nimport org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore;\nimport org.bonitasoft.web.rest.server.datastore.bpm.flownode.FlowNodeDatastore;\nimport org.bonitasoft.web.rest.server.datastore.bpm.process.ProcessDatastore;\nimport org.bonitasoft.web.rest.server.datastore.organization.UserDatastore;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.ItemDefinitionFactory;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Celine Souchet\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class APICaseTest {\n\n    @Mock\n    private UserDatastore userDatastore;\n\n    @Mock\n    private ProcessDatastore processDatastore;\n\n    @Mock\n    private FlowNodeDatastore flowNodeDatastore;\n\n    @Mock\n    private CaseDatastore caseDatastore;\n\n    private APICase apiCase;\n\n    @Before\n    public void before() {\n        ItemDefinitionFactory.setDefaultFactory(new ModelFactory());\n        apiCase = spy(new APICase());\n        doReturn(userDatastore).when(apiCase).getUserDatastore();\n        doReturn(processDatastore).when(apiCase).getProcessDatastore();\n        doReturn(flowNodeDatastore).when(apiCase).getFlowNodeDatastore();\n        doReturn(caseDatastore).when(apiCase).getCaseDatastore();\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.web.rest.server.api.bpm.cases.APICase#defineDefaultSearchOrder()}.\n     */\n    @Test\n    public final void defineDefaultSearchOrder_should_be_descending_creation_date() {\n        // When\n        final String defineDefaultSearchOrder = apiCase.defineDefaultSearchOrder();\n\n        // Then\n        assertEquals(ProcessInstanceCriterion.CREATION_DATE_DESC.name(), defineDefaultSearchOrder);\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.web.rest.server.api.bpm.cases.APICase#delete(java.util.List)}.\n     */\n    @Test\n    public final void delete_should_delete_case_items_on_CaseDatastore() {\n        // Given\n        final List<APIID> ids = Arrays.asList(APIID.makeAPIID(78L));\n\n        // When\n        apiCase.delete(ids);\n\n        // Then\n        verify(caseDatastore).delete(ids);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.web.rest.server.api.bpm.cases.APICase#add(org.bonitasoft.web.rest.model.bpm.cases.CaseItem)}.\n     */\n    @Test\n    public final void add_should_add_case_item_on_CaseDatastore() {\n        // Given\n        final CaseItem item = mock(CaseItem.class);\n        doReturn(item).when(caseDatastore).add(item);\n\n        // When\n        final CaseItem result = apiCase.add(item);\n\n        // Then\n        assertEquals(item, result);\n        verify(caseDatastore).add(item);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.web.rest.server.api.bpm.cases.APICase#get(org.bonitasoft.web.toolkit.client.data.APIID)}.\n     */\n    @Test\n    public final void get_should_get_case_item_on_CaseDatastore() {\n        // Given\n        final APIID id = APIID.makeAPIID(78L);\n        final CaseItem item = mock(CaseItem.class);\n        doReturn(item).when(caseDatastore).get(id);\n\n        // When\n        final CaseItem result = apiCase.get(id);\n\n        // Then\n        assertEquals(item, result);\n        verify(caseDatastore).get(id);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.web.rest.server.api.bpm.cases.APICase#search(int, int, java.lang.String, java.lang.String, java.util.Map)}.\n     */\n    @Test\n    public final void search_should_search_case_items_on_CaseDatastore() {\n        final int page = 6;\n        final int resultsByPage = 10;\n        final String search = \"plop\";\n        final String orders = CaseItem.ATTRIBUTE_END_DATE;\n        final Map<String, String> filters = new HashMap<>();\n\n        final ItemSearchResult<CaseItem> searchResult = new ItemSearchResult<>(page, resultsByPage, resultsByPage,\n                Arrays.asList(new CaseItem()));\n        doReturn(searchResult).when(caseDatastore).search(page, resultsByPage, search, orders, filters);\n\n        // When\n        final ItemSearchResult<CaseItem> result = apiCase.search(page, resultsByPage, search, orders, filters);\n\n        // Then\n        verify(caseDatastore).search(page, resultsByPage, search, orders, filters);\n        assertEquals(searchResult, result);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.web.rest.server.api.bpm.cases.APICase#search(int, int, java.lang.String, java.lang.String, java.util.Map)}.\n     */\n    @Test\n    public final void search_should_search_case_items_on_CaseDatastore_when_supervisor_filter_is_used_without_team_manager_filter() {\n        final int page = 6;\n        final int resultsByPage = 10;\n        final String search = \"plop\";\n        final String orders = CaseItem.ATTRIBUTE_END_DATE;\n        final Map<String, String> filters = Collections.singletonMap(CaseItem.FILTER_SUPERVISOR_ID, \"3\");\n\n        final ItemSearchResult<CaseItem> searchResult = new ItemSearchResult<>(page, resultsByPage, resultsByPage,\n                Arrays.asList(new CaseItem()));\n        doReturn(searchResult).when(caseDatastore).search(page, resultsByPage, search, orders, filters);\n\n        // When\n        final ItemSearchResult<CaseItem> result = apiCase.search(page, resultsByPage, search, orders, filters);\n\n        // Then\n        verify(caseDatastore).search(page, resultsByPage, search, orders, filters);\n        assertEquals(searchResult, result);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.web.rest.server.api.bpm.cases.APICase#search(int, int, java.lang.String, java.lang.String, java.util.Map)}.\n     */\n    @Test\n    public final void search_should_search_case_items_on_CaseDatastore_when_team_manager_filter_is_used_without_supervisor_filter() {\n        final int page = 6;\n        final int resultsByPage = 10;\n        final String search = \"plop\";\n        final String orders = CaseItem.ATTRIBUTE_END_DATE;\n        final Map<String, String> filters = Collections.singletonMap(CaseItem.FILTER_TEAM_MANAGER_ID, \"9\");\n\n        final ItemSearchResult<CaseItem> searchResult = new ItemSearchResult<>(page, resultsByPage, resultsByPage,\n                Arrays.asList(new CaseItem()));\n        doReturn(searchResult).when(caseDatastore).search(page, resultsByPage, search, orders, filters);\n\n        // When\n        final ItemSearchResult<CaseItem> result = apiCase.search(page, resultsByPage, search, orders, filters);\n\n        // Then\n        verify(caseDatastore).search(page, resultsByPage, search, orders, filters);\n        assertEquals(searchResult, result);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.web.rest.server.api.bpm.cases.APICase#search(int, int, java.lang.String, java.lang.String, java.util.Map)}.\n     */\n    @Test(expected = APIException.class)\n    public final void search_should_throw_exception_when_team_manager_and_supervisor_filters_are_used_together() {\n        final int page = 6;\n        final int resultsByPage = 10;\n        final String search = \"plop\";\n        final String orders = CaseItem.ATTRIBUTE_END_DATE;\n        final Map<String, String> filters = new HashMap<>();\n        filters.put(CaseItem.FILTER_TEAM_MANAGER_ID, \"9\");\n        filters.put(CaseItem.FILTER_SUPERVISOR_ID, \"3\");\n\n        // When\n        apiCase.search(page, resultsByPage, search, orders, filters);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.web.rest.server.api.bpm.cases.APICase#fillDeploys(org.bonitasoft.web.rest.model.bpm.cases.CaseItem, java.util.List)}.\n     */\n    @Test\n    public final void fillDeploys_should_fill_user_who_start_case_when_deploy_of_started_by_is_active() {\n        // Given\n        final APIID startedByUserID = APIID.makeAPIID(3L);\n        final CaseItem item = mock(CaseItem.class);\n        doReturn(CaseItem.ATTRIBUTE_STARTED_BY_USER_ID).when(item)\n                .getAttributeValue(CaseItem.ATTRIBUTE_STARTED_BY_USER_ID);\n        doReturn(startedByUserID).when(item).getStartedByUserId();\n\n        final List<String> deploys = Arrays.asList(CaseItem.ATTRIBUTE_STARTED_BY_USER_ID);\n\n        final UserItem userItem = new UserItem();\n        doReturn(userItem).when(userDatastore).get(startedByUserID);\n\n        // When\n        apiCase.fillDeploys(item, deploys);\n\n        // Then\n        verify(item).setDeploy(CaseItem.ATTRIBUTE_STARTED_BY_USER_ID, userItem);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.web.rest.server.api.bpm.cases.APICase#fillDeploys(org.bonitasoft.web.rest.model.bpm.cases.CaseItem, java.util.List)}.\n     */\n    @Test\n    public final void fillDeploys_should_do_nothing_when_deploy_of_started_by_is_not_active() {\n        // Given\n        final CaseItem item = mock(CaseItem.class);\n        final List<String> deploys = new ArrayList<>();\n\n        // When\n        apiCase.fillDeploys(item, deploys);\n\n        // Then\n        verify(item, never()).setDeploy(anyString(), any(Item.class));\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.web.rest.server.api.bpm.cases.APICase#fillDeploys(org.bonitasoft.web.rest.model.bpm.cases.CaseItem, java.util.List)}.\n     */\n    @Test\n    public final void fillDeploys_should_fill_substitute_user_who_start_case_when_deploy_of_started_by_substitute_is_active() {\n        // Given\n        final APIID startedBySubstituteUserID = APIID.makeAPIID(6L);\n        final CaseItem item = mock(CaseItem.class);\n        doReturn(CaseItem.ATTRIBUTE_STARTED_BY_SUBSTITUTE_USER_ID).when(item)\n                .getAttributeValue(CaseItem.ATTRIBUTE_STARTED_BY_SUBSTITUTE_USER_ID);\n        doReturn(startedBySubstituteUserID).when(item).getStartedBySubstituteUserId();\n\n        final List<String> deploys = Arrays.asList(CaseItem.ATTRIBUTE_STARTED_BY_SUBSTITUTE_USER_ID);\n\n        final UserItem userItem = new UserItem();\n        doReturn(userItem).when(userDatastore).get(startedBySubstituteUserID);\n\n        // When\n        apiCase.fillDeploys(item, deploys);\n\n        // Then\n        verify(item).setDeploy(CaseItem.ATTRIBUTE_STARTED_BY_SUBSTITUTE_USER_ID, userItem);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.web.rest.server.api.bpm.cases.APICase#fillDeploys(org.bonitasoft.web.rest.model.bpm.cases.CaseItem, java.util.List)}.\n     */\n    @Test\n    public final void fillDeploys_should_do_nothing_when_deploy_of_started_by_substitute_is_not_active() {\n        // Given\n        final CaseItem item = mock(CaseItem.class);\n        final List<String> deploys = new ArrayList<>();\n\n        // When\n        apiCase.fillDeploys(item, deploys);\n\n        // Then\n        verify(item, never()).setDeploy(anyString(), any(Item.class));\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.web.rest.server.api.bpm.cases.APICase#fillDeploys(org.bonitasoft.web.rest.model.bpm.cases.CaseItem, java.util.List)}.\n     */\n    @Test\n    public final void fillDeploys_should_fill_process_when_deploy_of_process_is_active() {\n        // Given\n        final APIID processId = APIID.makeAPIID(9L);\n        final CaseItem item = mock(CaseItem.class);\n        doReturn(CaseItem.ATTRIBUTE_PROCESS_ID).when(item).getAttributeValue(CaseItem.ATTRIBUTE_PROCESS_ID);\n        doReturn(processId).when(item).getProcessId();\n\n        final List<String> deploys = Arrays.asList(CaseItem.ATTRIBUTE_PROCESS_ID);\n\n        final ProcessItem processItem = new ProcessItem();\n        doReturn(processItem).when(processDatastore).get(processId);\n\n        // When\n        apiCase.fillDeploys(item, deploys);\n\n        // Then\n        verify(item).setDeploy(CaseItem.ATTRIBUTE_PROCESS_ID, processItem);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.web.rest.server.api.bpm.cases.APICase#fillDeploys(org.bonitasoft.web.rest.model.bpm.cases.CaseItem, java.util.List)}.\n     */\n    @Test\n    public final void fillDeploys_should_do_nothing_when_deploy_of_process_is_not_active() {\n        // Given\n        final CaseItem item = mock(CaseItem.class);\n        final List<String> deploys = new ArrayList<>();\n\n        // When\n        apiCase.fillDeploys(item, deploys);\n\n        // Then\n        verify(item, never()).setDeploy(anyString(), any(Item.class));\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.web.rest.server.api.bpm.cases.APICase#fillDeploys(org.bonitasoft.web.rest.model.bpm.cases.CaseItem, java.util.List)}.\n     */\n    @Test\n    public final void fillDeploys_should_do_nothing_when_id_is_invalid() {\n        // Given\n        final CaseItem item = mock(CaseItem.class);\n        doReturn(\"0\").when(item).getAttributeValue(CaseItem.ATTRIBUTE_PROCESS_ID);\n        doReturn(\"-1\").when(item).getAttributeValue(CaseItem.ATTRIBUTE_STARTED_BY_USER_ID);\n\n        final List<String> deploys = Arrays.asList(CaseItem.ATTRIBUTE_PROCESS_ID);\n\n        // When\n        apiCase.fillDeploys(item, deploys);\n\n        // Then\n        verify(item, never()).setDeploy(anyString(), any(Item.class));\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.web.rest.server.api.bpm.cases.APICase#fillCountersDependingOnFilters(org.bonitasoft.web.rest.model.bpm.cases.CaseItem, java.util.List, java.util.Map)}.\n     */\n    @Test\n    public final void fillCountersDependingOnFilters_should_fill_number_of_failed_flownodes_for_child_case_if_failed_counter_exists() {\n        // Given\n        final APIID id = APIID.makeAPIID(78L);\n        final CaseItem item = mock(CaseItem.class);\n        doReturn(id).when(item).getId();\n\n        final List<String> counters = Arrays.asList(CaseItem.COUNTER_FAILED_FLOW_NODES);\n\n        final Map<String, String> flowNodeFilters = new HashMap<>();\n        flowNodeFilters.put(FlowNodeItem.ATTRIBUTE_STATE, FlowNodeItem.VALUE_STATE_FAILED);\n        flowNodeFilters.put(FlowNodeItem.ATTRIBUTE_PARENT_CASE_ID, String.valueOf(id.toLong()));\n        final long numberOfFailedFlowNodes = 2L;\n        doReturn(numberOfFailedFlowNodes).when(flowNodeDatastore).count(null, null, flowNodeFilters);\n\n        // When\n        final Map<String, String> caseFilters = new HashMap<>();\n        caseFilters.put(CaseItem.FILTER_CALLER, \"any\");\n        apiCase.fillCountersDependingOnFilters(item, counters, caseFilters);\n\n        // Then\n        verify(item).setAttribute(CaseItem.COUNTER_FAILED_FLOW_NODES, numberOfFailedFlowNodes);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.web.rest.server.api.bpm.cases.APICase#fillCountersDependingOnFilters(org.bonitasoft.web.rest.model.bpm.cases.CaseItem, java.util.List, java.util.Map)}.\n     */\n    @Test\n    public final void fillCountersDependingOnFilters_should_fill_number_of_failed_flownodes_for_root_case_if_failed_counter_exists() {\n        // Given\n        final APIID id = APIID.makeAPIID(78L);\n        final CaseItem item = mock(CaseItem.class);\n        doReturn(id).when(item).getId();\n\n        final List<String> counters = Arrays.asList(CaseItem.COUNTER_FAILED_FLOW_NODES);\n\n        final Map<String, String> flowNodeFilters = new HashMap<>();\n        flowNodeFilters.put(FlowNodeItem.ATTRIBUTE_STATE, FlowNodeItem.VALUE_STATE_FAILED);\n        flowNodeFilters.put(FlowNodeItem.ATTRIBUTE_ROOT_CASE_ID, String.valueOf(id.toLong()));\n        final long numberOfFailedFlowNodes = 2L;\n        doReturn(numberOfFailedFlowNodes).when(flowNodeDatastore).count(null, null, flowNodeFilters);\n\n        // When\n        apiCase.fillCountersDependingOnFilters(item, counters, Map.of());\n\n        // Then\n        verify(item).setAttribute(CaseItem.COUNTER_FAILED_FLOW_NODES, numberOfFailedFlowNodes);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.web.rest.server.api.bpm.cases.APICase#fillCountersDependingOnFilters(org.bonitasoft.web.rest.model.bpm.cases.CaseItem, java.util.List, java.util.Map)}.\n     */\n    @Test\n    public final void fillCounters_should_do_nothing_when_counter_of_failed_flow_nodes_is_not_active() {\n        // Given\n        final CaseItem item = mock(CaseItem.class);\n        final List<String> counters = new ArrayList<>();\n\n        // When\n        apiCase.fillCounters(item, counters);\n\n        // Then\n        verify(item, never()).setAttribute(anyString(), anyLong());\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.web.rest.server.api.bpm.cases.APICase#fillCountersDependingOnFilters(org.bonitasoft.web.rest.model.bpm.cases.CaseItem, java.util.List, java.util.Map)}.\n     */\n    @Test\n    public void fillCountersDependingOnFilters_should_fill_number_of_active_flownodes_for_child_case_if_active_counter_exists() {\n        // Given\n        final APIID id = APIID.makeAPIID(78L);\n        final CaseItem item = mock(CaseItem.class);\n        doReturn(id).when(item).getId();\n\n        final List<String> counters = Arrays.asList(CaseItem.COUNTER_ACTIVE_FLOW_NODES);\n\n        final Map<String, String> filters = new HashMap<>();\n        filters.put(FlowNodeItem.ATTRIBUTE_PARENT_CASE_ID, String.valueOf(id.toLong()));\n        final long numberOfActiveFlowNodes = 3L;\n        doReturn(numberOfActiveFlowNodes).when(flowNodeDatastore).count(null, null, filters);\n\n        // When\n        final Map<String, String> caseFilters = new HashMap<>();\n        caseFilters.put(CaseItem.FILTER_CALLER, \"any\");\n        apiCase.fillCountersDependingOnFilters(item, counters, caseFilters);\n\n        // Then\n        verify(item).setAttribute(CaseItem.COUNTER_ACTIVE_FLOW_NODES, numberOfActiveFlowNodes);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.web.rest.server.api.bpm.cases.APICase#fillCountersDependingOnFilters(org.bonitasoft.web.rest.model.bpm.cases.CaseItem, java.util.List, java.util.Map)}.\n     */\n    @Test\n    public void fillCountersDependingOnFilters_should_fill_number_of_active_flownodes_for_root_case_if_active_counter_exists() {\n        // Given\n        final APIID id = APIID.makeAPIID(78L);\n        final CaseItem item = mock(CaseItem.class);\n        doReturn(id).when(item).getId();\n\n        final List<String> counters = Arrays.asList(CaseItem.COUNTER_ACTIVE_FLOW_NODES);\n\n        final Map<String, String> filters = new HashMap<>();\n        filters.put(FlowNodeItem.ATTRIBUTE_ROOT_CASE_ID, String.valueOf(id.toLong()));\n        final long numberOfActiveFlowNodes = 3L;\n        doReturn(numberOfActiveFlowNodes).when(flowNodeDatastore).count(null, null, filters);\n\n        // When\n        apiCase.fillCountersDependingOnFilters(item, counters, Map.of());\n\n        // Then\n        verify(item).setAttribute(CaseItem.COUNTER_ACTIVE_FLOW_NODES, numberOfActiveFlowNodes);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.web.rest.server.api.bpm.cases.APICase#fillCountersDependingOnFilters(org.bonitasoft.web.rest.model.bpm.cases.CaseItem, java.util.List, java.util.Map)}.\n     */\n    @Test\n    public void fillCountersDependingOnFilters_should_fill_number_of_pending_flownodes_for_child_case_if_pending_counter_exists() {\n        // Given\n        final APIID id = APIID.makeAPIID(78L);\n        final CaseItem item = mock(CaseItem.class);\n        doReturn(id).when(item).getId();\n\n        final List<String> counters = Arrays.asList(CaseItem.COUNTER_PENDING_FLOW_NODES);\n\n        final Map<String, String> filters = new HashMap<>();\n        filters.put(FlowNodeItem.ATTRIBUTE_PARENT_CASE_ID, String.valueOf(id.toLong()));\n        filters.put(FlowNodeItem.ATTRIBUTE_STATE, FlowNodeItem.VALUE_STATE_PENDING);\n        final long numberOfPendingFlowNodes = 4L;\n        doReturn(numberOfPendingFlowNodes).when(flowNodeDatastore).count(null, null, filters);\n\n        // When\n        final Map<String, String> caseFilters = new HashMap<>();\n        caseFilters.put(CaseItem.FILTER_CALLER, \"any\");\n        apiCase.fillCountersDependingOnFilters(item, counters, caseFilters);\n\n        // Then\n        verify(item).setAttribute(CaseItem.COUNTER_PENDING_FLOW_NODES, numberOfPendingFlowNodes);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.web.rest.server.api.bpm.cases.APICase#fillCountersDependingOnFilters(org.bonitasoft.web.rest.model.bpm.cases.CaseItem, java.util.List, java.util.Map)}.\n     */\n    @Test\n    public void fillCountersDependingOnFilters_should_fill_number_of_pending_flownodes_for_root_case_if_pending_counter_exists() {\n        // Given\n        final APIID id = APIID.makeAPIID(78L);\n        final CaseItem item = mock(CaseItem.class);\n        doReturn(id).when(item).getId();\n\n        final List<String> counters = Arrays.asList(CaseItem.COUNTER_PENDING_FLOW_NODES);\n\n        final Map<String, String> filters = new HashMap<>();\n        filters.put(FlowNodeItem.ATTRIBUTE_ROOT_CASE_ID, String.valueOf(id.toLong()));\n        filters.put(FlowNodeItem.ATTRIBUTE_STATE, FlowNodeItem.VALUE_STATE_PENDING);\n        final long numberOfPendingFlowNodes = 4L;\n        doReturn(numberOfPendingFlowNodes).when(flowNodeDatastore).count(null, null, filters);\n\n        // When\n        apiCase.fillCountersDependingOnFilters(item, counters, Map.of());\n\n        // Then\n        verify(item).setAttribute(CaseItem.COUNTER_PENDING_FLOW_NODES, numberOfPendingFlowNodes);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/bpm/cases/APICaseVariableAttributeCheckerTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.cases;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseVariableItem;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Colin PUY\n */\npublic class APICaseVariableAttributeCheckerTest {\n\n    private APICaseVariableAttributeChecker attributeChecker;\n\n    @Before\n    public void initChecker() {\n        attributeChecker = new APICaseVariableAttributeChecker();\n    }\n\n    private Map<String, String> buildSuitableUpdateAttributes() {\n        Map<String, String> attributes = new HashMap<>();\n        attributes.put(CaseVariableItem.ATTRIBUTE_TYPE, String.class.getName());\n        attributes.put(CaseVariableItem.ATTRIBUTE_VALUE, \"aValue\");\n        return attributes;\n    }\n\n    private Map<String, String> buildSuitableSearchFilters() {\n        Map<String, String> filters = new HashMap<>();\n        filters.put(CaseVariableItem.ATTRIBUTE_CASE_ID, \"1\");\n        return filters;\n    }\n\n    @Test\n    public void checkUpdateAttributesPassForSuitableValues() throws Exception {\n        Map<String, String> attributes = buildSuitableUpdateAttributes();\n\n        attributeChecker.checkUpdateAttributes(attributes);\n    }\n\n    @Test(expected = APIException.class)\n    public void checkUpdateAttributesCheckForAttributeType() throws Exception {\n        Map<String, String> attributes = buildSuitableUpdateAttributes();\n        attributes.remove(CaseVariableItem.ATTRIBUTE_TYPE);\n\n        attributeChecker.checkUpdateAttributes(attributes);\n    }\n\n    @Test(expected = APIException.class)\n    public void checkUpdateAttributesCheckForAttributeValue() throws Exception {\n        Map<String, String> attributes = buildSuitableUpdateAttributes();\n        attributes.remove(CaseVariableItem.ATTRIBUTE_VALUE);\n\n        attributeChecker.checkUpdateAttributes(attributes);\n    }\n\n    @Test\n    public void checkSearchFiltersPassForSuitableValues() throws Exception {\n        Map<String, String> filters = buildSuitableSearchFilters();\n\n        attributeChecker.checkSearchFilters(filters);\n    }\n\n    @Test(expected = APIException.class)\n    public void checkSearchFiltersCheckForCaseIdFilters() throws Exception {\n        Map<String, String> filters = buildSuitableSearchFilters();\n        filters.remove(CaseVariableItem.ATTRIBUTE_CASE_ID);\n\n        attributeChecker.checkSearchFilters(filters);\n    }\n\n    @Test(expected = APIException.class)\n    public void should_throw_exception_when_filters_are_null() throws Exception {\n        attributeChecker.checkSearchFilters(null);\n    }\n\n    @Test(expected = APIException.class)\n    public void should_throw_exception_when_attributes_are_null() throws Exception {\n        attributeChecker.checkUpdateAttributes(null);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/bpm/cases/APICaseVariableTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.cases;\n\nimport static java.util.Arrays.asList;\nimport static org.bonitasoft.web.toolkit.client.data.APIID.makeAPIID;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.MockitoAnnotations.initMocks;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseVariableDefinition;\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseVariableItem;\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseVariableDatastore;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\n\n/**\n * @author Colin PUY\n */\npublic class APICaseVariableTest extends APITestWithMock {\n\n    private APICaseVariable apiCaseVariable;\n\n    @Mock\n    private CaseVariableDatastore datastore;\n\n    @Before\n    public void initializeMocks() {\n        initMocks(this);\n        apiCaseVariable = spy(new APICaseVariable());\n\n        doReturn(datastore).when(apiCaseVariable).getDefaultDatastore();\n    }\n\n    private APIID buildAPIID(long caseId, String variableName) {\n        APIID makeAPIID = makeAPIID(asList(String.valueOf(caseId), variableName));\n        makeAPIID.setItemDefinition(CaseVariableDefinition.get());\n        return makeAPIID;\n    }\n\n    private Map<String, String> buildUpdateParameters(String newValue, String className) {\n        Map<String, String> map = new HashMap<>();\n        map.put(CaseVariableItem.ATTRIBUTE_VALUE, newValue);\n        map.put(CaseVariableItem.ATTRIBUTE_TYPE, className);\n        return map;\n    }\n\n    private Map<String, String> buildCaseIdFilter(long caseId) {\n        Map<String, String> filters = new HashMap<>();\n        filters.put(CaseVariableItem.ATTRIBUTE_CASE_ID, String.valueOf(caseId));\n        return filters;\n    }\n\n    @Test\n    public void updateUpdateTheVariableValue() throws Exception {\n        long caseId = 1L;\n        String variableName = \"aName\";\n        String newValue = \"newValue\";\n        Map<String, String> parameters = buildUpdateParameters(newValue, String.class.getName());\n        APIID apiid = buildAPIID(caseId, variableName);\n\n        apiCaseVariable.runUpdate(apiid, parameters);\n\n        verify(datastore).updateVariableValue(caseId, variableName, String.class.getName(), newValue);\n    }\n\n    @Test\n    public void weCheckAttributesBeforeUpdating() throws Exception {\n        APICaseVariableAttributeChecker attributeChecker = mock(APICaseVariableAttributeChecker.class);\n        apiCaseVariable.setAttributeChecker(attributeChecker);\n        APIID apiid = buildAPIID(1L, \"aName\");\n        Map<String, String> attributes = buildUpdateParameters(\"newValue\", String.class.getName());\n\n        apiCaseVariable.runUpdate(apiid, attributes);\n\n        verify(attributeChecker).checkUpdateAttributes(attributes);\n    }\n\n    @Test\n    public void weCheckFiltersBeforeSearching() throws Exception {\n        APICaseVariableAttributeChecker attributeChecker = mock(APICaseVariableAttributeChecker.class);\n        apiCaseVariable.setAttributeChecker(attributeChecker);\n        Map<String, String> filters = buildCaseIdFilter(1L);\n\n        apiCaseVariable.runSearch(0, 10, null, null, filters, null, null);\n\n        verify(attributeChecker).checkSearchFilters(filters);\n    }\n\n    @Test\n    public void searchIsFilteredByCaseId() throws Exception {\n        long expectedCaseId = 1L;\n        Map<String, String> filters = buildCaseIdFilter(expectedCaseId);\n\n        apiCaseVariable.runSearch(0, 10, null, null, filters, null, null);\n\n        verify(datastore).findByCaseId(expectedCaseId, 0, 10);\n    }\n\n    @Test\n    public void getSearchInDatastoreById() throws Exception {\n        long expectedCaseId = 1L;\n        String expectedVariableName = \"aName\";\n        APIID apiid = buildAPIID(expectedCaseId, expectedVariableName);\n\n        apiCaseVariable.runGet(apiid, null, null);\n\n        verify(datastore).findById(expectedCaseId, expectedVariableName);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/bpm/cases/ArchivedCaseContextControllerTest.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.cases;\n\nimport static org.bonitasoft.web.rest.server.api.AbstractRESTController.API_SPRING_INTERNAL;\nimport static org.mockito.Mockito.*;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.expression.ExpressionEvaluationException;\nimport org.bonitasoft.web.rest.server.api.AbstractControllerTest;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mock;\nimport org.springframework.http.MediaType;\n\nclass ArchivedCaseContextControllerTest extends AbstractControllerTest<ArchivedCaseContextController> {\n\n    private static final long ARCHIVED_CASE_ID = 2L;\n    private static final String TEST_API_URL = \"/\" + API_SPRING_INTERNAL + \"/bpm/archivedCase/\" + ARCHIVED_CASE_ID\n            + \"/context\";\n\n    @Mock\n    protected ProcessAPI processAPI;\n\n    @Override\n    protected ArchivedCaseContextController createController() {\n        return spy(new ArchivedCaseContextController());\n    }\n\n    @Override\n    protected void configureMocks(ArchivedCaseContextController controller) throws Exception {\n        doReturn(processAPI).when(controller).getProcessAPI(apiSession);\n    }\n\n    @Test\n    void should_return_archived_case_context_with_transformed_values() throws Exception {\n        // given\n        Map<String, Serializable> context = new HashMap<>();\n        context.put(\"Ticket\", \"ticketValue\");\n        context.put(\"Count\", 42);\n\n        doReturn(context).when(processAPI).getArchivedProcessInstanceExecutionContext(ARCHIVED_CASE_ID);\n\n        // when/then\n        mockMvc.perform(get(TEST_API_URL)\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(content().json(\"\"\"\n                        {\n                            \"Ticket\": \"ticketValue\",\n                            \"Count\": 42\n                        }\n                        \"\"\", true));\n    }\n\n    @Test\n    void should_respond_404_when_archived_case_not_found() throws Exception {\n        doThrow(new ProcessInstanceNotFoundException(\"archived case not found\"))\n                .when(processAPI).getArchivedProcessInstanceExecutionContext(ARCHIVED_CASE_ID);\n\n        mockMvc.perform(get(TEST_API_URL)\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isNotFound());\n    }\n\n    @Test\n    void should_respond_500_when_expression_evaluation_fails() throws Exception {\n        doThrow(new ExpressionEvaluationException(new RuntimeException(\"evaluation failed\")))\n                .when(processAPI).getArchivedProcessInstanceExecutionContext(ARCHIVED_CASE_ID);\n\n        mockMvc.perform(get(TEST_API_URL)\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isInternalServerError());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/bpm/cases/ArchivedCaseVariableControllerTest.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.cases;\n\nimport static org.bonitasoft.web.rest.server.api.bpm.cases.ArchivedDataInstanceBuilder.anArchivedDataInstance;\nimport static org.mockito.Mockito.*;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.data.ArchivedDataNotFoundException;\nimport org.bonitasoft.engine.data.instance.exception.SDataInstanceException;\nimport org.bonitasoft.web.rest.server.api.AbstractControllerTest;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mock;\nimport org.springframework.http.MediaType;\n\nclass ArchivedCaseVariableControllerTest extends AbstractControllerTest<ArchivedCaseVariableController> {\n\n    private static final String API_URL = \"/API/bpm/archivedCaseVariable\";\n\n    @Mock\n    protected ProcessAPI processAPI;\n\n    @Override\n    protected ArchivedCaseVariableController createController() {\n        return spy(new ArchivedCaseVariableController());\n    }\n\n    @Override\n    protected void configureMocks(ArchivedCaseVariableController controller) throws Exception {\n        doReturn(processAPI).when(controller).getProcessAPI(apiSession);\n    }\n\n    @Test\n    void getArchivedCaseVariable_should_return_variable_with_all_fields() throws Exception {\n        var archivedDate = new Date();\n        doReturn(anArchivedDataInstance(\"myVar\")\n                .withContainerId(12)\n                .withType(String.class.getName())\n                .withValue(\"Hello World\")\n                .withArchivedDate(archivedDate)\n                .build())\n                .when(processAPI).getArchivedProcessDataInstance(\"myVar\", 12L);\n\n        mockMvc.perform(get(API_URL + \"/12/myVar\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(jsonPath(\"$.name\").value(\"myVar\"))\n                .andExpect(jsonPath(\"$.case_id\").value(\"12\"))\n                .andExpect(jsonPath(\"$.type\").value(String.class.getName()))\n                .andExpect(jsonPath(\"$.value\").value(\"Hello World\"));\n    }\n\n    @Test\n    void getArchivedCaseVariable_should_return_404_when_variable_not_found() throws Exception {\n        doThrow(new ArchivedDataNotFoundException(new SDataInstanceException(\"No archived data instance found\")))\n                .when(processAPI).getArchivedProcessDataInstance(\"unknownVar\", 12L);\n\n        mockMvc.perform(get(API_URL + \"/12/unknownVar\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isNotFound());\n    }\n\n    @Test\n    void getArchivedCaseVariable_should_return_404_when_case_not_found() throws Exception {\n        doThrow(new ArchivedDataNotFoundException(\"Archived process instance not found: \" + 12L))\n                .when(processAPI).getArchivedProcessDataInstance(\"unknownCase\", 12L);\n\n        mockMvc.perform(get(API_URL + \"/12/unknownCase\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isNotFound());\n    }\n\n    @Test\n    void getArchivedCaseVariable_should_return_400_when_case_id_is_not_a_number() throws Exception {\n        var caseId = \"notANumber\";\n        mockMvc.perform(get(API_URL + \"/\" + caseId + \"/myVar\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isBadRequest())\n                .andExpect(jsonPath(\"$.exception\").value(IllegalArgumentException.class.toString()))\n                .andExpect(jsonPath(\"$.message\").value(\"[ %s ] must be a number\".formatted(caseId)));\n    }\n\n    @Test\n    void getArchivedCaseVariables_should_return_paginated_results_with_content_range() throws Exception {\n        doReturn(List.of(\n                anArchivedDataInstance(\"myVar1\").withContainerId(12).build(),\n                anArchivedDataInstance(\"myVar2\").withContainerId(12).build(),\n                anArchivedDataInstance(\"myVar3\").withContainerId(12).build(),\n                anArchivedDataInstance(\"myVar4\").withContainerId(12).build()))\n                .when(processAPI).getArchivedProcessDataInstances(12L, 0, Integer.MAX_VALUE);\n\n        mockMvc.perform(get(API_URL)\n                .param(\"f\", \"case_id=12\")\n                .param(\"p\", \"1\")\n                .param(\"c\", \"2\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(header().string(\"Content-Range\", \"1-2/4\"))\n                .andExpect(jsonPath(\"$\").isArray())\n                .andExpect(jsonPath(\"$.length()\").value(2))\n                .andExpect(jsonPath(\"$[0].name\").value(\"myVar3\"))\n                .andExpect(jsonPath(\"$[1].name\").value(\"myVar4\"));\n    }\n\n    @Test\n    void getArchivedCaseVariables_should_return_first_page() throws Exception {\n        doReturn(List.of(\n                anArchivedDataInstance(\"myVar1\").withContainerId(12).build(),\n                anArchivedDataInstance(\"myVar2\").withContainerId(12).build()))\n                .when(processAPI).getArchivedProcessDataInstances(12L, 0, Integer.MAX_VALUE);\n\n        mockMvc.perform(get(API_URL)\n                .param(\"f\", \"case_id=12\")\n                .param(\"p\", \"0\")\n                .param(\"c\", \"10\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(header().string(\"Content-Range\", \"0-10/2\"))\n                .andExpect(jsonPath(\"$.length()\").value(2));\n    }\n\n    @Test\n    void getArchivedCaseVariables_should_return_400_when_case_id_filter_is_missing() throws Exception {\n        mockMvc.perform(get(API_URL)\n                .param(\"p\", \"0\")\n                .param(\"c\", \"10\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isBadRequest())\n                .andExpect(jsonPath(\"$.exception\").value(IllegalArgumentException.class.toString()))\n                .andExpect(jsonPath(\"$.message\").value(\"filter case_id is mandatory\"));\n    }\n\n    @Test\n    void getArchivedCaseVariables_should_return_empty_list_when_no_variables_exist() throws Exception {\n        doReturn(List.of())\n                .when(processAPI).getArchivedProcessDataInstances(12L, 0, Integer.MAX_VALUE);\n\n        mockMvc.perform(get(API_URL)\n                .param(\"f\", \"case_id=12\")\n                .param(\"p\", \"0\")\n                .param(\"c\", \"10\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(header().string(\"Content-Range\", \"0-10/0\"))\n                .andExpect(jsonPath(\"$\").isArray())\n                .andExpect(jsonPath(\"$.length()\").value(0));\n    }\n\n    @Test\n    void getArchivedCaseVariables_should_return_empty_list_when_page_exceeds_available_data() throws Exception {\n        doReturn(List.of(\n                anArchivedDataInstance(\"myVar1\").withContainerId(12).build(),\n                anArchivedDataInstance(\"myVar2\").withContainerId(12).build()))\n                .when(processAPI).getArchivedProcessDataInstances(12L, 0, Integer.MAX_VALUE);\n\n        mockMvc.perform(get(API_URL)\n                .param(\"f\", \"case_id=12\")\n                .param(\"p\", \"100\")\n                .param(\"c\", \"10\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(header().string(\"Content-Range\", \"100-10/2\"))\n                .andExpect(jsonPath(\"$\").isArray())\n                .andExpect(jsonPath(\"$.length()\").value(0));\n    }\n\n    @Test\n    void getArchivedCaseVariables_should_return_400_when_case_id_is_not_a_number() throws Exception {\n        mockMvc.perform(get(API_URL)\n                .param(\"f\", \"case_id=notANumber\")\n                .param(\"p\", \"0\")\n                .param(\"c\", \"10\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isBadRequest())\n                .andExpect(jsonPath(\"$.exception\").value(IllegalArgumentException.class.toString()))\n                .andExpect(jsonPath(\"$.message\").value(\"filter case_id must be a number\"));\n    }\n\n    @Test\n    void getArchivedCaseVariables_should_extract_case_id_when_multiple_filters_provided() throws Exception {\n        doReturn(List.of(\n                anArchivedDataInstance(\"myVar1\").withContainerId(42).build()))\n                .when(processAPI).getArchivedProcessDataInstances(42L, 0, Integer.MAX_VALUE);\n\n        mockMvc.perform(get(API_URL)\n                .param(\"f\", \"other_filter=ignored\")\n                .param(\"f\", \"case_id=42\")\n                .param(\"p\", \"0\")\n                .param(\"c\", \"10\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(jsonPath(\"$.length()\").value(1))\n                .andExpect(jsonPath(\"$[0].name\").value(\"myVar1\"));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/bpm/cases/ArchivedDataInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.cases;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\nimport org.bonitasoft.engine.bpm.data.ArchivedDataInstance;\nimport org.bonitasoft.engine.bpm.data.impl.ArchivedDataInstanceImpl;\n\npublic class ArchivedDataInstanceBuilder {\n\n    public static ArchivedDataInstanceBuilder anArchivedDataInstance(String name) {\n        return new ArchivedDataInstanceBuilder(name);\n    }\n\n    private final String name;\n    private String description;\n    private String className;\n    private Serializable value;\n    private long containerId;\n    private Date archivedDate;\n    private long sourceObjectId;\n\n    private ArchivedDataInstanceBuilder(String name) {\n        this.name = name;\n    }\n\n    public ArchivedDataInstanceBuilder withType(String className) {\n        this.className = className;\n        return this;\n    }\n\n    public ArchivedDataInstanceBuilder withValue(Serializable value) {\n        this.value = value;\n        return this;\n    }\n\n    public ArchivedDataInstanceBuilder withContainerId(long containerId) {\n        this.containerId = containerId;\n        return this;\n    }\n\n    public ArchivedDataInstanceBuilder withArchivedDate(Date archivedDate) {\n        this.archivedDate = archivedDate;\n        return this;\n    }\n\n    public ArchivedDataInstance build() {\n        var instance = new ArchivedDataInstanceImpl();\n        instance.setName(name);\n        instance.setClassName(className);\n        instance.setContainerId(containerId);\n        instance.setContainerType(\"PROCESS_INSTANCE\");\n        instance.setDescription(description);\n        instance.setSourceObjectId(sourceObjectId);\n        instance.setArchiveDate(archivedDate);\n        instance.setValue(value);\n        return instance;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/bpm/cases/CaseContextControllerTest.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.cases;\n\nimport static org.bonitasoft.web.rest.server.api.AbstractRESTController.API_SPRING_INTERNAL;\nimport static org.mockito.Mockito.*;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.business.data.impl.MultipleBusinessDataReferenceImpl;\nimport org.bonitasoft.engine.business.data.impl.SimpleBusinessDataReferenceImpl;\nimport org.bonitasoft.engine.expression.ExpressionEvaluationException;\nimport org.bonitasoft.web.rest.server.api.AbstractControllerTest;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mock;\nimport org.springframework.http.MediaType;\n\nclass CaseContextControllerTest extends AbstractControllerTest<CaseContextController> {\n\n    private static final long CASE_ID = 2L;\n    private static final String TEST_API_URL = \"/\" + API_SPRING_INTERNAL + \"/bpm/case/\" + CASE_ID + \"/context\";\n\n    @Mock\n    protected ProcessAPI processAPI;\n\n    @Override\n    protected CaseContextController createController() {\n        return spy(new CaseContextController());\n    }\n\n    @Override\n    protected void configureMocks(CaseContextController controller) throws Exception {\n        doReturn(processAPI).when(controller).getProcessAPI(apiSession);\n    }\n\n    @Test\n    void should_return_case_context_with_transformed_values() throws Exception {\n        // given\n        Map<String, Serializable> context = new HashMap<>();\n        context.put(\"Ticket\", \"ticketValue\");\n        context.put(\"Count\", 42);\n\n        doReturn(context).when(processAPI).getProcessInstanceExecutionContext(CASE_ID);\n\n        // when/then\n        mockMvc.perform(get(TEST_API_URL)\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(content().json(\"\"\"\n                        {\n                            \"Ticket\": \"ticketValue\",\n                            \"Count\": 42\n                        }\n                        \"\"\", true));\n    }\n\n    @Test\n    void should_respond_404_when_case_not_found() throws Exception {\n        doThrow(new ProcessInstanceNotFoundException(\"case not found\"))\n                .when(processAPI).getProcessInstanceExecutionContext(CASE_ID);\n\n        mockMvc.perform(get(TEST_API_URL)\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isNotFound());\n    }\n\n    @Test\n    void should_respond_500_when_expression_evaluation_fails() throws Exception {\n        doThrow(new ExpressionEvaluationException(new RuntimeException(\"evaluation failed\")))\n                .when(processAPI).getProcessInstanceExecutionContext(CASE_ID);\n\n        mockMvc.perform(get(TEST_API_URL)\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isInternalServerError());\n    }\n\n    @Test\n    void should_convert_simple_business_data_reference_to_client_representation() throws Exception {\n        // given\n        Map<String, Serializable> context = new HashMap<>();\n        context.put(\"myEmployee_ref\",\n                new SimpleBusinessDataReferenceImpl(\"myEmployee\", \"com.bonitasoft.pojo.Employee\", 487467354L));\n\n        doReturn(context).when(processAPI).getProcessInstanceExecutionContext(CASE_ID);\n\n        // when/then\n        mockMvc.perform(get(TEST_API_URL)\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(content().json(\"\"\"\n                        {\n                            \"myEmployee_ref\": {\n                                \"name\": \"myEmployee\",\n                                \"type\": \"com.bonitasoft.pojo.Employee\",\n                                \"storageId\": 487467354,\n                                \"storageId_string\": \"487467354\",\n                                \"link\": \"API/bdm/businessData/com.bonitasoft.pojo.Employee/487467354\"\n                            }\n                        }\n                        \"\"\", true));\n    }\n\n    @Test\n    void should_convert_multiple_business_data_reference_to_client_representation() throws Exception {\n        // given\n        Map<String, Serializable> context = new HashMap<>();\n        List<Long> ids = new ArrayList<>();\n        ids.add(687646784L);\n        ids.add(2313213874354L);\n        context.put(\"myTeam_ref\",\n                new MultipleBusinessDataReferenceImpl(\"myTeam\", \"com.bonitasoft.pojo.Employee\", ids));\n\n        doReturn(context).when(processAPI).getProcessInstanceExecutionContext(CASE_ID);\n\n        // when/then\n        mockMvc.perform(get(TEST_API_URL)\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(content()\n                        .json(\"\"\"\n                                {\n                                    \"myTeam_ref\": {\n                                        \"name\": \"myTeam\",\n                                        \"type\": \"com.bonitasoft.pojo.Employee\",\n                                        \"storageIds\": [687646784, 2313213874354],\n                                        \"storageIds_string\": [\"687646784\", \"2313213874354\"],\n                                        \"link\": \"API/bdm/businessData/com.bonitasoft.pojo.Employee/findByIds?ids=687646784,2313213874354\"\n                                    }\n                                }\n                                \"\"\",\n                                true));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/bpm/connector/APIConnectorInstanceTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.connector;\n\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.MockitoAnnotations.initMocks;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.console.common.server.i18n.I18n;\nimport org.bonitasoft.web.rest.server.BonitaRestAPIServlet;\nimport org.bonitasoft.web.rest.server.datastore.bpm.connector.ConnectorInstanceDatastore;\nimport org.junit.Before;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\n\n/**\n * @author Vincent Elcrin\n */\npublic class APIConnectorInstanceTest {\n\n    @Spy\n    private APIConnectorInstance spiedAPIConnectorInstance;\n\n    @Mock\n    private ConnectorInstanceDatastore mockedConnectorInstanceDatastore;\n\n    @BeforeClass\n    public static void initEnvironnement() {\n        I18n.getInstance();\n        new BonitaRestAPIServlet();\n    }\n\n    @Before\n    public void init() {\n        initMocks(this);\n        doReturn(this.mockedConnectorInstanceDatastore).when(this.spiedAPIConnectorInstance).defineDefaultDatastore();\n    }\n\n    @Test\n    public void testDatastoreSearchIsCalled() {\n        final int page = 1;\n        final int resultsByPage = 2;\n        final String search = \"search\";\n        final String orders = \"orders\";\n        final Map<String, String> filters = new HashMap<>();\n\n        this.spiedAPIConnectorInstance.search(page, resultsByPage, search, orders, filters);\n\n        verify(this.mockedConnectorInstanceDatastore).search(page, resultsByPage, search, orders, filters);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/bpm/flownode/APIActivityTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.junit.Test;\n\npublic class APIActivityTest extends APITestWithMock {\n\n    @Test\n    public void should_have_default_search_order() throws Exception {\n        final APIActivity apiActivity = new APIActivity();\n\n        final String defineDefaultSearchOrder = apiActivity.defineDefaultSearchOrder();\n\n        assertThat(defineDefaultSearchOrder).as(\"sould have a default search order\").isEqualTo(\"state ASC\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/bpm/flownode/APITaskTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.junit.Test;\n\npublic class APITaskTest extends APITestWithMock {\n\n    @Test\n    public void should_have_default_search_order() throws Exception {\n        //given\n        final APITask apiActivity = new APITask();\n\n        //when\n        final String defineDefaultSearchOrder = apiActivity.defineDefaultSearchOrder();\n\n        //then\n        assertThat(defineDefaultSearchOrder).as(\"sould have a default search order\").isEqualTo(\"last_update_date DESC\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/bpm/flownode/APIUserTaskTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.junit.Test;\n\npublic class APIUserTaskTest extends APITestWithMock {\n\n    @Test\n    public void should_have_default_search_order() throws Exception {\n        //given\n        final APIUserTask api = new APIUserTask();\n\n        //when\n        final String defineDefaultSearchOrder = api.defineDefaultSearchOrder();\n\n        //then\n        assertThat(defineDefaultSearchOrder).as(\"sould have a default search order\").isEqualTo(\"priority DESC\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/bpm/flownode/ActivityVariableControllerTest.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode;\n\nimport static org.mockito.Mockito.*;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.data.DataDefinition;\nimport org.bonitasoft.engine.bpm.data.DataNotFoundException;\nimport org.bonitasoft.engine.bpm.data.impl.LongDataInstanceImpl;\nimport org.bonitasoft.engine.bpm.data.impl.ShortTextDataInstanceImpl;\nimport org.bonitasoft.engine.bpm.process.ModelFinderVisitor;\nimport org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.web.rest.server.api.AbstractControllerTest;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mock;\nimport org.springframework.http.MediaType;\n\nclass ActivityVariableControllerTest extends AbstractControllerTest<ActivityVariableController> {\n\n    private static final String API_URL = \"/API/bpm/activityVariable\";\n\n    @Mock\n    protected ProcessAPI processAPI;\n\n    @Override\n    protected ActivityVariableController createController() {\n        return spy(new ActivityVariableController());\n    }\n\n    @Override\n    protected void configureMocks(ActivityVariableController controller) throws Exception {\n        doReturn(processAPI).when(controller).getProcessAPI(apiSession);\n    }\n\n    @Test\n    void should_return_Long_data_instance() throws Exception {\n        var dataInstance = createLongDataInstance(123L);\n        when(processAPI.getActivityDataInstance(\"myVar\", 10L)).thenReturn(dataInstance);\n\n        mockMvc.perform(get(API_URL + \"/10/myVar\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(jsonPath(\"$.name\").value(\"dataInstanceName\"))\n                .andExpect(jsonPath(\"$.value\").value(123))\n                .andExpect(jsonPath(\"$.className\").value(\"com.company.Model\"));\n    }\n\n    @Test\n    void should_return_String_data_instance() throws Exception {\n        var dataInstance = createShortTextDataInstance(\"abc\");\n        when(processAPI.getActivityDataInstance(\"myVar\", 10L)).thenReturn(dataInstance);\n\n        mockMvc.perform(get(API_URL + \"/10/myVar\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(jsonPath(\"$.name\").value(\"dataInstanceName\"))\n                .andExpect(jsonPath(\"$.value\").value(\"abc\"))\n                .andExpect(jsonPath(\"$.value_string\").value(\"abc\"));\n    }\n\n    @Test\n    void should_return_Float_data_instance() throws Exception {\n        var dataInstance = new org.bonitasoft.engine.bpm.data.impl.FloatDataInstanceImpl(createDataDefinition(),\n                123.456F);\n        dataInstance.setId(5L);\n        dataInstance.setContainerId(7L);\n        when(processAPI.getActivityDataInstance(\"myVar\", 10L)).thenReturn(dataInstance);\n\n        mockMvc.perform(get(API_URL + \"/10/myVar\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(jsonPath(\"$.value\").value(123.456F))\n                .andExpect(jsonPath(\"$.value_string\").value(\"123.456\"));\n    }\n\n    @Test\n    void should_return_Double_data_instance() throws Exception {\n        var dataInstance = new org.bonitasoft.engine.bpm.data.impl.DoubleDataInstanceImpl(createDataDefinition(),\n                123.5D);\n        dataInstance.setId(5L);\n        dataInstance.setContainerId(7L);\n        when(processAPI.getActivityDataInstance(\"myVar\", 10L)).thenReturn(dataInstance);\n\n        mockMvc.perform(get(API_URL + \"/10/myVar\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(jsonPath(\"$.value\").value(123.5D))\n                .andExpect(jsonPath(\"$.value_string\").value(\"123.5\"));\n    }\n\n    @Test\n    void should_return_null_data_instance() throws Exception {\n        var dataInstance = createLongDataInstance(null);\n        when(processAPI.getActivityDataInstance(\"myVar\", 10L)).thenReturn(dataInstance);\n\n        mockMvc.perform(get(API_URL + \"/10/myVar\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(jsonPath(\"$.value\").isEmpty())\n                .andExpect(jsonPath(\"$.value_string\").value(\"null\"));\n    }\n\n    @Test\n    void should_return_LocalDate_data_instance() throws Exception {\n        var dataInstance = new LocalDateDataInstanceImpl(createDataDefinition(),\n                java.time.LocalDate.parse(\"2016-08-16\"));\n        dataInstance.setId(5L);\n        dataInstance.setContainerId(7L);\n        when(processAPI.getActivityDataInstance(\"myVar\", 10L)).thenReturn(dataInstance);\n\n        mockMvc.perform(get(API_URL + \"/10/myVar\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(jsonPath(\"$.value\").value(\"2016-08-16\"))\n                .andExpect(jsonPath(\"$.value_string\").value(\"2016-08-16\"));\n    }\n\n    @Test\n    void should_respond_404_when_data_not_found() throws Exception {\n        when(processAPI.getActivityDataInstance(\"unknownVar\", 10L))\n                .thenThrow(new DataNotFoundException(new Exception(\"not found\")));\n\n        mockMvc.perform(get(API_URL + \"/10/unknownVar\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isNotFound());\n    }\n\n    @Test\n    void should_respond_404_when_flownode_not_found() throws Exception {\n        final long activityInstanceId = 10L;\n        when(processAPI.getActivityDataInstance(\"unknownFlownode\", activityInstanceId))\n                .thenThrow(new DataNotFoundException(new SFlowNodeNotFoundException(activityInstanceId)));\n\n        mockMvc.perform(get(API_URL + \"/10/unknownFlownode\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isNotFound());\n    }\n\n    @Test\n    void should_respond_400_when_activity_id_is_not_a_number() throws Exception {\n        mockMvc.perform(get(API_URL + \"/notANumber/myVar\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isBadRequest());\n    }\n\n    private LongDataInstanceImpl createLongDataInstance(Long value) {\n        var dataInstance = new LongDataInstanceImpl(createDataDefinition(), value);\n        dataInstance.setId(5L);\n        dataInstance.setContainerId(7L);\n        return dataInstance;\n    }\n\n    private ShortTextDataInstanceImpl createShortTextDataInstance(String value) {\n        var dataInstance = new ShortTextDataInstanceImpl(createDataDefinition(), value);\n        dataInstance.setId(5L);\n        dataInstance.setContainerId(7L);\n        return dataInstance;\n    }\n\n    private DataDefinition createDataDefinition() {\n        return new DataDefinition() {\n\n            @Override\n            public String getClassName() {\n                return \"com.company.Model\";\n            }\n\n            @Override\n            public boolean isTransientData() {\n                return false;\n            }\n\n            @Override\n            public Expression getDefaultValueExpression() {\n                return null;\n            }\n\n            @Override\n            public String getDescription() {\n                return \"description\";\n            }\n\n            @Override\n            public String getName() {\n                return \"dataInstanceName\";\n            }\n\n            @Override\n            public void accept(ModelFinderVisitor visitor, long modelId) {\n            }\n        };\n    }\n\n    // Custom DataInstance for LocalDate (not provided by the engine)\n    static class LocalDateDataInstanceImpl extends org.bonitasoft.engine.bpm.data.impl.DataInstanceImpl {\n\n        private java.time.LocalDate value;\n\n        public LocalDateDataInstanceImpl(DataDefinition dataDefinition, java.time.LocalDate value) {\n            super(dataDefinition);\n            this.value = value;\n        }\n\n        @Override\n        public java.time.LocalDate getValue() {\n            return value;\n        }\n\n        @Override\n        public void setValue(java.io.Serializable value) {\n            this.value = (java.time.LocalDate) value;\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/bpm/flownode/Dummy.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode;\n\npublic class Dummy {\n\n    private final String field;\n\n    public Dummy(final String fieldValue) {\n        field = fieldValue;\n    }\n\n    public String getField() {\n        return field;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/bpm/flownode/TimerEventTriggerControllerTest.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyLong;\nimport static org.mockito.Mockito.*;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.flownode.TimerEventTriggerInstanceNotFoundException;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.TimerEventTriggerInstanceImpl;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.impl.SearchResultImpl;\nimport org.bonitasoft.web.rest.server.api.AbstractControllerTest;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mock;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\n\nclass TimerEventTriggerControllerTest extends AbstractControllerTest<TimerEventTriggerController> {\n\n    @Mock\n    protected ProcessAPI processAPI;\n\n    @Override\n    protected TimerEventTriggerController createController() {\n        return spy(new TimerEventTriggerController());\n    }\n\n    @Override\n    protected void configureMocks(TimerEventTriggerController controller) throws Exception {\n        doReturn(processAPI).when(controller).getProcessAPI(apiSession);\n    }\n\n    @Test\n    void should_search_timer_event_triggers_for_given_case() throws Exception {\n        // Given\n        final long caseId = 123L;\n        final long triggerId = 42L;\n        final long eventInstanceId = 100L;\n\n        when(processAPI.searchTimerEventTriggerInstances(anyLong(), any(SearchOptions.class)))\n                .thenReturn(new SearchResultImpl<>(1,\n                        List.of(new TimerEventTriggerInstanceImpl(triggerId, eventInstanceId, \"カキクケコ\", new Date()))));\n\n        // When & Then\n        mockMvc.perform(get(\"/API/bpm/timerEventTrigger\")\n                .param(\"caseId\", String.valueOf(caseId))\n                .param(\"p\", \"0\")\n                .param(\"c\", \"10\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(header().string(HttpHeaders.CONTENT_RANGE, \"0-1/1\"))\n                .andExpect(jsonPath(\"$\").isArray())\n                .andExpect(jsonPath(\"$.length()\").value(1))\n                // Verify _string fields are present (backward compatibility)\n                .andExpect(jsonPath(\"$[0].id\").value(triggerId))\n                .andExpect(jsonPath(\"$[0].id_string\").value(String.valueOf(triggerId)))\n                .andExpect(jsonPath(\"$[0].eventInstanceId\").value(eventInstanceId))\n                .andExpect(jsonPath(\"$[0].eventInstanceId_string\").value(String.valueOf(eventInstanceId)))\n                .andExpect(jsonPath(\"$[0].eventInstanceName\").value(\"カキクケコ\"));\n\n        verify(processAPI).searchTimerEventTriggerInstances(eq(caseId), any(SearchOptions.class));\n    }\n\n    @Test\n    void should_return_string_fields_for_numeric_ids() throws Exception {\n        // Given - test with large IDs that could lose precision in JavaScript\n        final long caseId = 123L;\n        final long triggerId = 9007199254740993L; // > Number.MAX_SAFE_INTEGER\n        final long eventInstanceId = 9007199254740994L;\n\n        when(processAPI.searchTimerEventTriggerInstances(anyLong(), any(SearchOptions.class)))\n                .thenReturn(new SearchResultImpl<>(1,\n                        List.of(new TimerEventTriggerInstanceImpl(triggerId, eventInstanceId, \"bigTimer\",\n                                new Date()))));\n\n        // When & Then - _string fields preserve precision for JavaScript clients\n        mockMvc.perform(get(\"/API/bpm/timerEventTrigger\")\n                .param(\"caseId\", String.valueOf(caseId))\n                .param(\"p\", \"0\")\n                .param(\"c\", \"10\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(jsonPath(\"$[0].id\").value(triggerId))\n                .andExpect(jsonPath(\"$[0].id_string\").value(\"9007199254740993\"))\n                .andExpect(jsonPath(\"$[0].eventInstanceId\").value(eventInstanceId))\n                .andExpect(jsonPath(\"$[0].eventInstanceId_string\").value(\"9007199254740994\"));\n    }\n\n    @Test\n    void should_return_bad_request_when_caseId_is_missing() throws Exception {\n        // When & Then\n        mockMvc.perform(get(\"/API/bpm/timerEventTrigger\")\n                .param(\"p\", \"0\")\n                .param(\"c\", \"10\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isBadRequest());\n    }\n\n    @Test\n    void should_update_timer_event_trigger_execution_date() throws Exception {\n        // Given\n        final long triggerId = 456L;\n        final long newExecutionTime = System.currentTimeMillis() + 3600000; // +1 hour\n        final Date updatedDate = new Date(newExecutionTime);\n\n        when(processAPI.updateExecutionDateOfTimerEventTriggerInstance(anyLong(), any(Date.class)))\n                .thenReturn(updatedDate);\n\n        final String requestBody = \"\"\"\n                {\n                    \"executionDate\": %d\n                }\n                \"\"\".formatted(newExecutionTime);\n\n        // When & Then\n        mockMvc.perform(put(\"/API/bpm/timerEventTrigger/{id}\", triggerId)\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(requestBody)\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(jsonPath(\"$.executionDate\").value(newExecutionTime));\n\n        verify(processAPI).updateExecutionDateOfTimerEventTriggerInstance(eq(triggerId), any(Date.class));\n    }\n\n    @Test\n    void should_return_not_found_when_timer_event_trigger_does_not_exist() throws Exception {\n        // Given\n        final long triggerId = 999L;\n        final long newExecutionTime = System.currentTimeMillis() + 3600000;\n\n        when(processAPI.updateExecutionDateOfTimerEventTriggerInstance(anyLong(), any(Date.class)))\n                .thenThrow(new TimerEventTriggerInstanceNotFoundException(triggerId));\n\n        final String requestBody = \"\"\"\n                {\n                    \"executionDate\": %d\n                }\n                \"\"\".formatted(newExecutionTime);\n\n        // When & Then\n        mockMvc.perform(put(\"/API/bpm/timerEventTrigger/{id}\", triggerId)\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(requestBody)\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isNotFound());\n    }\n\n    @Test\n    void should_return_bad_request_when_execution_date_is_missing() throws Exception {\n        // Given\n        final long triggerId = 456L;\n        final String requestBody = \"{}\";\n\n        // When & Then\n        mockMvc.perform(put(\"/API/bpm/timerEventTrigger/{id}\", triggerId)\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(requestBody)\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isInternalServerError());\n    }\n\n    @Test\n    void should_return_no_content_when_no_timer_triggers_found() throws Exception {\n        // Given\n        final long caseId = 123L;\n\n        when(processAPI.searchTimerEventTriggerInstances(anyLong(), any(SearchOptions.class)))\n                .thenReturn(new SearchResultImpl<>(0, List.of()));\n\n        // When & Then - 204 No Content has no body\n        mockMvc.perform(get(\"/API/bpm/timerEventTrigger\")\n                .param(\"caseId\", String.valueOf(caseId))\n                .param(\"p\", \"0\")\n                .param(\"c\", \"10\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isNoContent())\n                .andExpect(header().string(HttpHeaders.CONTENT_RANGE, \"0-0/0\"));\n    }\n\n    @Test\n    void should_return_no_content_when_page_out_of_bounds() throws Exception {\n        // Given\n        final long caseId = 123L;\n\n        when(processAPI.searchTimerEventTriggerInstances(anyLong(), any(SearchOptions.class)))\n                .thenReturn(new SearchResultImpl<>(2, List.of())); // Total 2, but empty results for this page\n\n        // When & Then - 204 No Content has no body\n        mockMvc.perform(get(\"/API/bpm/timerEventTrigger\")\n                .param(\"caseId\", String.valueOf(caseId))\n                .param(\"p\", \"10\") // Page 10 when only 2 results exist\n                .param(\"c\", \"10\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isNoContent())\n                .andExpect(header().string(HttpHeaders.CONTENT_RANGE, \"0-0/0\"));\n    }\n\n    @Test\n    void should_handle_different_pagination_parameters() throws Exception {\n        // Given\n        final long caseId = 123L;\n\n        when(processAPI.searchTimerEventTriggerInstances(anyLong(), any(SearchOptions.class)))\n                .thenReturn(new SearchResultImpl<>(50,\n                        List.of(new TimerEventTriggerInstanceImpl(1L, 1L, \"timer1\", new Date()),\n                                new TimerEventTriggerInstanceImpl(2L, 1L, \"timer2\", new Date()),\n                                new TimerEventTriggerInstanceImpl(3L, 1L, \"timer3\", new Date()),\n                                new TimerEventTriggerInstanceImpl(4L, 1L, \"timer4\", new Date()),\n                                new TimerEventTriggerInstanceImpl(5L, 1L, \"timer5\", new Date()))));\n\n        // When & Then - page 2 with 5 items per page\n        // Content-Range format: page-countOnCurrentPage/total\n        mockMvc.perform(get(\"/API/bpm/timerEventTrigger\")\n                .param(\"caseId\", String.valueOf(caseId))\n                .param(\"p\", \"2\")\n                .param(\"c\", \"5\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(header().string(HttpHeaders.CONTENT_RANGE, \"2-5/50\"))\n                .andExpect(jsonPath(\"$\").isArray())\n                .andExpect(jsonPath(\"$.length()\").value(5));\n\n        // Verify correct search options were used (page 2, count 5 -> startIndex 10, maxResults 5)\n        var searchOptionsCaptor = org.mockito.ArgumentCaptor.forClass(SearchOptions.class);\n        verify(processAPI).searchTimerEventTriggerInstances(eq(caseId), searchOptionsCaptor.capture());\n        var searchOptions = searchOptionsCaptor.getValue();\n        org.assertj.core.api.Assertions.assertThat(searchOptions.getStartIndex()).isEqualTo(10);\n        org.assertj.core.api.Assertions.assertThat(searchOptions.getMaxResults()).isEqualTo(5);\n    }\n\n    @Test\n    void should_use_default_pagination_values_when_not_provided() throws Exception {\n        // Given\n        final long caseId = 123L;\n\n        when(processAPI.searchTimerEventTriggerInstances(anyLong(), any(SearchOptions.class)))\n                .thenReturn(new SearchResultImpl<>(5,\n                        List.of(new TimerEventTriggerInstanceImpl(1L, 1L, \"timer1\", new Date()))));\n\n        // When & Then - no p and c parameters, should use defaults (p=0, c=10)\n        mockMvc.perform(get(\"/API/bpm/timerEventTrigger\")\n                .param(\"caseId\", String.valueOf(caseId))\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk());\n\n        // Verify defaults were used (p=0, c=10 -> startIndex 0, maxResults 10)\n        var searchOptionsCaptor = org.mockito.ArgumentCaptor.forClass(SearchOptions.class);\n        verify(processAPI).searchTimerEventTriggerInstances(eq(caseId), searchOptionsCaptor.capture());\n        var searchOptions = searchOptionsCaptor.getValue();\n        org.assertj.core.api.Assertions.assertThat(searchOptions.getStartIndex()).isEqualTo(0);\n        org.assertj.core.api.Assertions.assertThat(searchOptions.getMaxResults()).isEqualTo(10);\n    }\n\n    @Test\n    void should_return_bad_request_when_p_parameter_is_invalid() throws Exception {\n        // When & Then\n        mockMvc.perform(get(\"/API/bpm/timerEventTrigger\")\n                .param(\"caseId\", \"123\")\n                .param(\"p\", \"invalid\")\n                .param(\"c\", \"10\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isBadRequest());\n    }\n\n    @Test\n    void should_return_bad_request_when_c_parameter_is_invalid() throws Exception {\n        // When & Then\n        mockMvc.perform(get(\"/API/bpm/timerEventTrigger\")\n                .param(\"caseId\", \"123\")\n                .param(\"p\", \"0\")\n                .param(\"c\", \"invalid\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isBadRequest());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/bpm/flownode/UserTaskContextControllerTest.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode;\n\nimport static org.bonitasoft.web.rest.server.api.AbstractRESTController.API_SPRING_INTERNAL;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.when;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.flownode.UserTaskNotFoundException;\nimport org.bonitasoft.web.rest.server.api.AbstractControllerTest;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mock;\nimport org.springframework.http.MediaType;\n\nclass UserTaskContextControllerTest extends AbstractControllerTest<UserTaskContextController> {\n\n    private static final long TASK_ID = 2L;\n    private static final String TEST_API_URL = \"/\" + API_SPRING_INTERNAL + \"/bpm/userTask/\" + TASK_ID + \"/context\";\n\n    @Mock\n    protected ProcessAPI processAPI;\n\n    @Override\n    protected UserTaskContextController createController() {\n        return spy(new UserTaskContextController());\n    }\n\n    @Override\n    protected void configureMocks(UserTaskContextController controller) throws Exception {\n        doReturn(processAPI).when(controller).getProcessAPI(apiSession);\n    }\n\n    @Test\n    void should_return_task_context() throws Exception {\n        // given\n        Map<String, Serializable> context = new HashMap<>();\n        context.put(\"Ticket\", \"ticketValue\");\n        context.put(\"Employee\", \"employeeValue\");\n\n        when(processAPI.getUserTaskExecutionContext(TASK_ID)).thenReturn(context);\n\n        // when/then\n        mockMvc.perform(get(TEST_API_URL)\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(content().json(\"\"\"\n                        {\n                            \"Ticket\": \"ticketValue\",\n                            \"Employee\": \"employeeValue\"\n                        }\n                        \"\"\", true));\n    }\n\n    @Test\n    void should_respond_404_when_task_not_found() throws Exception {\n        when(processAPI.getUserTaskExecutionContext(TASK_ID))\n                .thenThrow(new UserTaskNotFoundException(\"task not found\"));\n\n        mockMvc.perform(get(TEST_API_URL)\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isNotFound());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/bpm/flownode/UserTaskContractControllerTest.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode;\n\nimport static org.bonitasoft.web.rest.server.api.AbstractRESTController.API_SPRING_INTERNAL;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.when;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.contract.Type;\nimport org.bonitasoft.engine.bpm.contract.impl.ConstraintDefinitionImpl;\nimport org.bonitasoft.engine.bpm.contract.impl.ContractDefinitionImpl;\nimport org.bonitasoft.engine.bpm.contract.impl.InputDefinitionImpl;\nimport org.bonitasoft.engine.bpm.flownode.UserTaskNotFoundException;\nimport org.bonitasoft.web.rest.server.api.AbstractControllerTest;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mock;\nimport org.springframework.http.MediaType;\n\nclass UserTaskContractControllerTest extends AbstractControllerTest<UserTaskContractController> {\n\n    private static final long TASK_ID = 2L;\n    private static final String TEST_CONTRACT_API_URL = \"/\" + API_SPRING_INTERNAL + \"/bpm/userTask/\" + TASK_ID\n            + \"/contract\";\n\n    @Mock\n    private ProcessAPI processAPI;\n\n    @Override\n    protected UserTaskContractController createController() {\n        return spy(new UserTaskContractController());\n    }\n\n    @Override\n    protected void configureMocks(UserTaskContractController controller) throws Exception {\n        doReturn(processAPI).when(controller).getProcessAPI(apiSession);\n    }\n\n    @Test\n    void should_return_a_contract_for_a_given_task_instance() throws Exception {\n        // given\n        final ContractDefinitionImpl contract = new ContractDefinitionImpl();\n        contract.addInput(new InputDefinitionImpl(\"anInput\", Type.TEXT, \"aDescription\"));\n        final InputDefinitionImpl complexInputDefinitionImpl = new InputDefinitionImpl(\"complexInput\", \"description\",\n                true, null, null);\n        complexInputDefinitionImpl.getInputs().add(new InputDefinitionImpl(\"anInput\", Type.TEXT, \"aDescription\"));\n\n        contract.addInput(complexInputDefinitionImpl);\n        contract.addConstraint(new ConstraintDefinitionImpl(\"aRule\", \"an expression\", \"an explanation\"));\n\n        when(processAPI.getUserTaskContract(TASK_ID)).thenReturn(contract);\n\n        // when/then\n        mockMvc.perform(get(TEST_CONTRACT_API_URL)\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(content()\n                        .json(\"\"\"\n                                {\n                                    \"constraints\":[\n                                        {\"name\":\"aRule\",\"expression\":\"an expression\",\"explanation\":\"an explanation\",\"inputNames\":[]}\n                                    ],\n                                    \"inputs\":[\n                                        {\"description\":\"aDescription\",\"name\":\"anInput\",\"multiple\":false,\"type\":\"TEXT\",\"inputs\":[]},\n                                        {\"description\":\"description\",\"name\":\"complexInput\",\"multiple\":true,\"type\":null,\"inputs\":[\n                                            {\"description\":\"aDescription\",\"name\":\"anInput\",\"multiple\":false,\"type\":\"TEXT\",\"inputs\":[]}\n                                        ]}\n                                    ]\n                                }\n                                \"\"\",\n                                true));\n    }\n\n    @Test\n    void should_respond_404_Not_found_when_task_is_not_found_when_getting_contract() throws Exception {\n        when(processAPI.getUserTaskContract(TASK_ID))\n                .thenThrow(new UserTaskNotFoundException(\"task not found\"));\n\n        mockMvc.perform(get(TEST_CONTRACT_API_URL)\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isNotFound());\n    }\n\n    @Test\n    void should_respond_204_when_there_is_no_contract_on_the_task() throws Exception {\n        when(processAPI.getUserTaskContract(TASK_ID)).thenReturn(null);\n\n        mockMvc.perform(get(TEST_CONTRACT_API_URL)\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isNoContent());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/bpm/flownode/UserTaskExecutionControllerTest.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode;\n\nimport static com.github.stefanbirkner.systemlambda.SystemLambda.tapSystemOut;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.*;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\nimport java.io.Serializable;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.console.common.server.i18n.I18n;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.contract.ContractDefinition;\nimport org.bonitasoft.engine.bpm.contract.ContractViolationException;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeExecutionException;\nimport org.bonitasoft.engine.bpm.flownode.UserTaskNotFoundException;\nimport org.bonitasoft.web.rest.server.api.AbstractControllerTest;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mock;\nimport org.springframework.http.MediaType;\n\nclass UserTaskExecutionControllerTest extends AbstractControllerTest<UserTaskExecutionController> {\n\n    private static final long TASK_ID = 2L;\n    private static final long USER_ID = 4L;\n\n    @Mock\n    private ProcessAPI processAPI;\n\n    @Mock\n    private ContractDefinition contractDefinition;\n\n    @BeforeAll\n    static void initClass() {\n        I18n.getInstance();\n    }\n\n    @Override\n    protected UserTaskExecutionController createController() {\n        UserTaskExecutionController controller = spy(new UserTaskExecutionController());\n        doReturn(15L).when(controller).getMaxSize();\n        return controller;\n    }\n\n    @Override\n    protected void configureMocks(UserTaskExecutionController controller) throws Exception {\n        doReturn(processAPI).when(controller).getProcessAPI(apiSession);\n        when(contractDefinition.getInputs()).thenReturn(Collections.emptyList());\n    }\n\n    private String aComplexInputAsJson() {\n        return \"{\\\"aBoolean\\\":true, \\\"aString\\\":\\\"hello world\\\", \\\"a_complex_type\\\":{\\\"aNumber\\\":2, \\\"aBoolean\\\":false}}\";\n    }\n\n    private Map<String, Serializable> aComplexInput() {\n        final HashMap<String, Serializable> aComplexInput = new HashMap<>();\n        aComplexInput.put(\"aBoolean\", true);\n        aComplexInput.put(\"aString\", \"hello world\");\n\n        final HashMap<String, Serializable> childMap = new HashMap<>();\n        childMap.put(\"aNumber\", 2);\n        childMap.put(\"aBoolean\", false);\n\n        aComplexInput.put(\"a_complex_type\", childMap);\n\n        return aComplexInput;\n    }\n\n    @Test\n    void should_execute_a_task_with_given_inputs() throws Exception {\n        final Map<String, Serializable> expectedComplexInput = aComplexInput();\n        when(apiSession.getUserId()).thenReturn(0L);\n        when(processAPI.getUserTaskContract(TASK_ID)).thenReturn(contractDefinition);\n        doNothing().when(processAPI).executeUserTask(anyLong(), eq(TASK_ID), any(Map.class));\n\n        mockMvc.perform(post(\"/APISpringInternal/bpm/userTask/\" + TASK_ID + \"/execution\")\n                .sessionAttrs(sessionAttributes)\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(aComplexInputAsJson()))\n                .andExpect(status().isNoContent());\n\n        verify(processAPI).executeUserTask(0L, TASK_ID, expectedComplexInput);\n        verify(processAPI, never()).assignAndExecuteUserTask(anyLong(), anyLong(), any(Map.class));\n    }\n\n    @Test\n    void should_assign_and_execute_a_task_with_given_inputs() throws Exception {\n        final Map<String, Serializable> expectedComplexInput = aComplexInput();\n        when(apiSession.getUserId()).thenReturn(USER_ID);\n        when(processAPI.getUserTaskContract(TASK_ID)).thenReturn(contractDefinition);\n        doNothing().when(processAPI).assignAndExecuteUserTask(eq(USER_ID), eq(TASK_ID), any(Map.class));\n\n        mockMvc.perform(post(\"/APISpringInternal/bpm/userTask/\" + TASK_ID + \"/execution\")\n                .sessionAttrs(sessionAttributes)\n                .param(\"assign\", \"true\")\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(aComplexInputAsJson()))\n                .andExpect(status().isNoContent());\n\n        verify(processAPI).assignAndExecuteUserTask(USER_ID, TASK_ID, expectedComplexInput);\n        verify(processAPI, never()).executeUserTask(anyLong(), anyLong(), any(Map.class));\n    }\n\n    @Test\n    void should_execute_a_task_with_given_inputs_for_a_specific_user() throws Exception {\n        final Map<String, Serializable> expectedComplexInput = aComplexInput();\n        final long specificUserId = 1L;\n        when(processAPI.getUserTaskContract(TASK_ID)).thenReturn(contractDefinition);\n        doNothing().when(processAPI).executeUserTask(eq(specificUserId), eq(TASK_ID), any(Map.class));\n\n        mockMvc.perform(post(\"/APISpringInternal/bpm/userTask/\" + TASK_ID + \"/execution\")\n                .sessionAttrs(sessionAttributes)\n                .param(\"user\", String.valueOf(specificUserId))\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(aComplexInputAsJson()))\n                .andExpect(status().isNoContent());\n\n        verify(processAPI).executeUserTask(specificUserId, TASK_ID, expectedComplexInput);\n    }\n\n    @Test\n    void should_respond_400_Bad_request_when_contract_is_not_valid() throws Exception {\n        when(processAPI.getUserTaskContract(TASK_ID)).thenReturn(contractDefinition);\n        doThrow(new ContractViolationException(\"aMessage\", \"aMessage\",\n                Arrays.asList(\"first explanation\", \"second explanation\"), null))\n                .when(processAPI).executeUserTask(anyLong(), anyLong(), any(Map.class));\n\n        final String log = tapSystemOut(() -> mockMvc\n                .perform(post(\"/APISpringInternal/bpm/userTask/\" + TASK_ID + \"/execution\")\n                        .sessionAttrs(sessionAttributes)\n                        .contentType(MediaType.APPLICATION_JSON)\n                        .content(\"{\\\"key\\\": \\\"value\\\"}\"))\n                // Note: Exception response format in MockMvc may differ from runtime,\n                // so we only verify the status code as per the conversion guide's recommendation\n                .andExpect(status().isBadRequest()));\n\n        assertThat(log).containsPattern(\"INFO.*\\nExplanations:\\nfirst explanation\\nsecond explanation\");\n    }\n\n    @Test\n    void should_respond_500_Internal_server_error_when_error_occurs_on_task_execution() throws Exception {\n        when(processAPI.getUserTaskContract(TASK_ID)).thenReturn(contractDefinition);\n        doThrow(new FlowNodeExecutionException(\"aMessage\"))\n                .when(processAPI).executeUserTask(anyLong(), anyLong(), any(Map.class));\n\n        mockMvc.perform(post(\"/APISpringInternal/bpm/userTask/\" + TASK_ID + \"/execution\")\n                .sessionAttrs(sessionAttributes)\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(\"{\\\"key\\\": \\\"value\\\"}\"))\n                .andExpect(status().isInternalServerError());\n    }\n\n    @Test\n    void should_respond_404_Not_found_when_task_is_not_found() throws Exception {\n        when(processAPI.getUserTaskContract(TASK_ID)).thenReturn(contractDefinition);\n        doThrow(new UserTaskNotFoundException(\"task not found\"))\n                .when(processAPI).executeUserTask(anyLong(), anyLong(), any(Map.class));\n\n        mockMvc.perform(post(\"/APISpringInternal/bpm/userTask/\" + TASK_ID + \"/execution\")\n                .sessionAttrs(sessionAttributes)\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(\"{\\\"key\\\": \\\"value\\\"}\"))\n                .andExpect(status().isNotFound());\n    }\n\n    @Test\n    void should_execute_task_without_inputs() throws Exception {\n        when(apiSession.getUserId()).thenReturn(0L);\n        when(processAPI.getUserTaskContract(TASK_ID)).thenReturn(contractDefinition);\n        doNothing().when(processAPI).executeUserTask(anyLong(), eq(TASK_ID), any());\n\n        mockMvc.perform(post(\"/APISpringInternal/bpm/userTask/\" + TASK_ID + \"/execution\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isNoContent());\n\n        verify(processAPI).executeUserTask(eq(0L), eq(TASK_ID), any());\n    }\n\n    // Direct method call tests to verify typeConverterUtil.deleteTemporaryFiles behavior\n    @Test\n    void should_call_deleteFiles_after_executeUserTask() throws Exception {\n        // given\n        UserTaskExecutionController controller = spy(new UserTaskExecutionController());\n        controller.typeConverterUtil = spy(controller.typeConverterUtil);\n        doReturn(15L).when(controller).getMaxSize();\n        doReturn(apiSession).when(controller).getApiSession(any());\n        doReturn(processAPI).when(controller).getProcessAPI(apiSession);\n        when(apiSession.getUserId()).thenReturn(1L);\n        when(processAPI.getUserTaskContract(TASK_ID)).thenReturn(contractDefinition);\n\n        final Map<String, Serializable> inputs = new HashMap<>();\n        inputs.put(\"testKey\", \"testValue\");\n\n        HttpSession mockSession = mock(HttpSession.class);\n\n        // when\n        controller.executeTask(TASK_ID, 1L, false, inputs, mockSession);\n\n        // then\n        verify(processAPI, never()).assignAndExecuteUserTask(anyLong(), anyLong(), any(Map.class));\n        verify(processAPI, times(1)).executeUserTask(1L, TASK_ID, inputs);\n        verify(controller.typeConverterUtil, times(1)).deleteTemporaryFiles(anyMap());\n    }\n\n    @Test\n    void should_call_deleteFiles_after_assignAndExecuteUserTask() throws Exception {\n        // given\n        UserTaskExecutionController controller = spy(new UserTaskExecutionController());\n        controller.typeConverterUtil = spy(controller.typeConverterUtil);\n        doReturn(15L).when(controller).getMaxSize();\n        doReturn(apiSession).when(controller).getApiSession(any());\n        doReturn(processAPI).when(controller).getProcessAPI(apiSession);\n        when(apiSession.getUserId()).thenReturn(1L);\n        when(processAPI.getUserTaskContract(TASK_ID)).thenReturn(contractDefinition);\n\n        final Map<String, Serializable> inputs = new HashMap<>();\n        inputs.put(\"testKey\", \"testValue\");\n\n        HttpSession mockSession = mock(HttpSession.class);\n\n        // when\n        controller.executeTask(TASK_ID, 1L, true, inputs, mockSession);\n\n        // then\n        verify(processAPI, times(1)).assignAndExecuteUserTask(1L, TASK_ID, inputs);\n        verify(processAPI, never()).executeUserTask(anyLong(), anyLong(), any(Map.class));\n        verify(controller.typeConverterUtil, times(1)).deleteTemporaryFiles(anyMap());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/bpm/flownode/archive/APIArchivedActivityTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode.archive;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.junit.Test;\n\npublic class APIArchivedActivityTest extends APITestWithMock {\n\n    @Test\n    public void should_have_default_search_order() throws Exception {\n        final APIArchivedActivity apiActivity = new APIArchivedActivity();\n\n        final String defineDefaultSearchOrder = apiActivity.defineDefaultSearchOrder();\n\n        assertThat(defineDefaultSearchOrder).as(\"sould have a default search order\")\n                .isEqualTo(\"reached_state_date ASC\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/bpm/flownode/archive/APIArchivedFlowNodeTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode.archive;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.junit.Test;\n\npublic class APIArchivedFlowNodeTest extends APITestWithMock {\n\n    @Test\n    public void should_have_default_search_order() throws Exception {\n        final APIArchivedFlowNode apiActivity = new APIArchivedFlowNode();\n\n        final String defineDefaultSearchOrder = apiActivity.defineDefaultSearchOrder();\n\n        assertThat(defineDefaultSearchOrder).as(\"sould have a default search order\").isEqualTo(\"displayName ASC\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/bpm/flownode/archive/APIArchivedHumanTaskTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode.archive;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.junit.Test;\n\npublic class APIArchivedHumanTaskTest extends APITestWithMock {\n\n    @Test\n    public void should_have_default_search_order() throws Exception {\n        //given\n        final APIArchivedHumanTask api = new APIArchivedHumanTask();\n\n        //when\n        final String defineDefaultSearchOrder = api.defineDefaultSearchOrder();\n\n        //then\n        assertThat(defineDefaultSearchOrder).as(\"sould have a default search order\").isEqualTo(\"priority DESC\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/bpm/flownode/archive/APIArchivedUserTaskTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode.archive;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.junit.Test;\n\npublic class APIArchivedUserTaskTest extends APITestWithMock {\n\n    @Test\n    public void should_have_default_search_order() throws Exception {\n        final APIArchivedUserTask apiActivity = new APIArchivedUserTask();\n\n        final String defineDefaultSearchOrder = apiActivity.defineDefaultSearchOrder();\n\n        assertThat(defineDefaultSearchOrder).as(\"sould have a default search order\").isEqualTo(\"priority DESC\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/bpm/flownode/archive/ArchivedActivityVariableControllerTest.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode.archive;\n\nimport static org.bonitasoft.web.rest.server.api.bpm.cases.ArchivedDataInstanceBuilder.anArchivedDataInstance;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.when;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.data.ArchivedDataNotFoundException;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstanceNotFoundException;\nimport org.bonitasoft.web.rest.server.api.AbstractControllerTest;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mock;\nimport org.springframework.http.MediaType;\n\nclass ArchivedActivityVariableControllerTest extends AbstractControllerTest<ArchivedActivityVariableController> {\n\n    private static final String API_URL = \"/API/bpm/archivedActivityVariable\";\n\n    @Mock\n    protected ProcessAPI processAPI;\n\n    @Override\n    protected ArchivedActivityVariableController createController() {\n        return spy(new ArchivedActivityVariableController());\n    }\n\n    @Override\n    protected void configureMocks(ArchivedActivityVariableController controller) throws Exception {\n        doReturn(processAPI).when(controller).getProcessAPI(apiSession);\n    }\n\n    @Test\n    void should_return_archived_activity_variable() throws Exception {\n        when(processAPI.getArchivedActivityDataInstance(\"myVar\", 12L))\n                .thenReturn(anArchivedDataInstance(\"myVar\")\n                        .withContainerId(12)\n                        .withType(String.class.getName())\n                        .withValue(\"Hello World\")\n                        .withArchivedDate(new Date(1700000000000L))\n                        .build());\n\n        mockMvc.perform(get(API_URL + \"/12/myVar\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(jsonPath(\"$.name\").value(\"myVar\"))\n                .andExpect(jsonPath(\"$.containerId\").value(\"12\"))\n                .andExpect(jsonPath(\"$.type\").value(String.class.getName()))\n                .andExpect(jsonPath(\"$.value\").value(\"Hello World\"));\n    }\n\n    @Test\n    void should_respond_404_when_archived_data_not_found() throws Exception {\n        when(processAPI.getArchivedActivityDataInstance(\"unknownVar\", 12L))\n                .thenThrow(new ArchivedDataNotFoundException(new Exception(\"not found\")));\n\n        mockMvc.perform(get(API_URL + \"/12/unknownVar\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isNotFound());\n    }\n\n    @Test\n    void should_respond_404_when_archived_flownode_not_found() throws Exception {\n        when(processAPI.getArchivedActivityDataInstance(\"unknownFlownode\", 12L))\n                .thenThrow(new ArchivedDataNotFoundException(new ArchivedActivityInstanceNotFoundException(12L)));\n\n        mockMvc.perform(get(API_URL + \"/12/unknownFlownode\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isNotFound());\n    }\n\n    @Test\n    void should_respond_400_when_activity_id_is_not_a_number() throws Exception {\n        mockMvc.perform(get(API_URL + \"/notANumber/myVar\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isBadRequest());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/bpm/flownode/archive/ArchivedUserTaskContextControllerTest.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.flownode.archive;\n\nimport static org.bonitasoft.web.rest.server.api.AbstractRESTController.API_SPRING_INTERNAL;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.when;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.flownode.UserTaskNotFoundException;\nimport org.bonitasoft.web.rest.server.api.AbstractControllerTest;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mock;\nimport org.springframework.http.MediaType;\n\nclass ArchivedUserTaskContextControllerTest extends AbstractControllerTest<ArchivedUserTaskContextController> {\n\n    private static final long ARCHIVED_TASK_ID = 2L;\n    private static final String TEST_API_URL = \"/\" + API_SPRING_INTERNAL + \"/bpm/archivedUserTask/\"\n            + ARCHIVED_TASK_ID + \"/context\";\n\n    @Mock\n    protected ProcessAPI processAPI;\n\n    @Override\n    protected ArchivedUserTaskContextController createController() {\n        return spy(new ArchivedUserTaskContextController());\n    }\n\n    @Override\n    protected void configureMocks(ArchivedUserTaskContextController controller) throws Exception {\n        doReturn(processAPI).when(controller).getProcessAPI(apiSession);\n    }\n\n    @Test\n    void should_return_archived_task_context() throws Exception {\n        // given\n        Map<String, Serializable> context = new HashMap<>();\n        context.put(\"Ticket\", \"ticketValue\");\n        context.put(\"Employee\", \"employeeValue\");\n\n        when(processAPI.getArchivedUserTaskExecutionContext(ARCHIVED_TASK_ID)).thenReturn(context);\n\n        // when/then\n        mockMvc.perform(get(TEST_API_URL)\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(content().json(\"\"\"\n                        {\n                            \"Ticket\": \"ticketValue\",\n                            \"Employee\": \"employeeValue\"\n                        }\n                        \"\"\", true));\n    }\n\n    @Test\n    void should_respond_404_when_archived_task_not_found() throws Exception {\n        when(processAPI.getArchivedUserTaskExecutionContext(ARCHIVED_TASK_ID))\n                .thenThrow(new UserTaskNotFoundException(\"archived task not found\"));\n\n        mockMvc.perform(get(TEST_API_URL)\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isNotFound());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/bpm/message/BPMMessageControllerTest.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.message;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.hamcrest.Matchers.containsString;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyMap;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.doThrow;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.OffsetDateTime;\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.flownode.SendEventException;\nimport org.bonitasoft.engine.expression.Expression;\nimport org.bonitasoft.engine.expression.ExpressionBuilder;\nimport org.bonitasoft.engine.expression.ExpressionType;\nimport org.bonitasoft.engine.expression.InvalidExpressionException;\nimport org.bonitasoft.web.rest.server.api.AbstractControllerTest;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mock;\nimport org.springframework.http.MediaType;\n\nclass BPMMessageControllerTest extends AbstractControllerTest<BPMMessageController> {\n\n    @Mock\n    protected ProcessAPI processAPI;\n\n    @Override\n    protected BPMMessageController createController() {\n        return spy(new BPMMessageController());\n    }\n\n    @Override\n    protected void configureMocks(BPMMessageController controller) throws Exception {\n        doReturn(processAPI).when(controller).getProcessAPI(apiSession);\n    }\n\n    @Test\n    void should_send_message_with_target_flow_node() throws Exception {\n        mockMvc.perform(post(\"/API/bpm/message\")\n                .sessionAttrs(sessionAttributes)\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(\"\"\"\n                        {\n                          \"messageName\": \"msg\",\n                          \"targetProcess\": \"myProcess\",\n                          \"targetFlowNode\": \"activity\"\n                        }\n                        \"\"\"))\n                .andExpect(status().isNoContent());\n\n        verify(processAPI).sendMessage(eq(\"msg\"),\n                eq(new ExpressionBuilder().createConstantStringExpression(\"myProcess\")),\n                eq(new ExpressionBuilder().createConstantStringExpression(\"activity\")),\n                eq(Collections.emptyMap()), eq(Collections.emptyMap()));\n    }\n\n    @Test\n    void should_return_error_when_engine_throws_SendEventException() throws Exception {\n        doThrow(new SendEventException(\"wrong params\")).when(processAPI).sendMessage(eq(\"test\"), any(Expression.class),\n                any(Expression.class), anyMap(), anyMap());\n\n        mockMvc.perform(post(\"/API/bpm/message\")\n                .sessionAttrs(sessionAttributes)\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(\"\"\"\n                        {\n                          \"messageName\": \"test\",\n                          \"targetProcess\": \"test\",\n                          \"targetFlowNode\": \"test\"\n                        }\n                        \"\"\"))\n                .andExpect(status().isInternalServerError());\n    }\n\n    @Test\n    void should_return_bad_request_when_messageName_is_not_set() throws Exception {\n        mockMvc.perform(post(\"/API/bpm/message\")\n                .sessionAttrs(sessionAttributes)\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(\"\"\"\n                        {\n                          \"targetProcess\": \"myProcess\",\n                          \"targetFlowNode\": \"activity\"\n                        }\n                        \"\"\"))\n                .andExpect(status().isBadRequest())\n                .andExpect(content().string(containsString(\"'messageName' attribute is mandatory\")));\n    }\n\n    @Test\n    void should_return_bad_request_when_targetProcess_is_not_set() throws Exception {\n        mockMvc.perform(post(\"/API/bpm/message\")\n                .sessionAttrs(sessionAttributes)\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(\"\"\"\n                        {\n                          \"messageName\": \"my msg\",\n                          \"targetFlowNode\": \"activity\"\n                        }\n                        \"\"\"))\n                .andExpect(status().isBadRequest())\n                .andExpect(content().string(containsString(\"'targetProcess' attribute is mandatory\")));\n    }\n\n    @Test\n    void should_return_bad_request_with_error_body_when_request_body_is_empty() throws Exception {\n        mockMvc.perform(post(\"/API/bpm/message\")\n                .sessionAttrs(sessionAttributes)\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(\"\"))\n                .andExpect(status().isBadRequest())\n                .andExpect(content().string(containsString(\"Unable to parse the JSON body\")));\n    }\n\n    @Test\n    void should_return_bad_request_when_json_is_malformed() throws Exception {\n        mockMvc.perform(post(\"/API/bpm/message\")\n                .sessionAttrs(sessionAttributes)\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(\"{not valid json\"))\n                .andExpect(status().isBadRequest())\n                .andExpect(content().string(containsString(\"Unable to parse the JSON body\")));\n    }\n\n    @Test\n    void should_accept_primitive_types_in_message_content_values() throws Exception {\n        mockMvc.perform(post(\"/API/bpm/message\")\n                .sessionAttrs(sessionAttributes)\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(\n                        \"\"\"\n                                {\n                                  \"messageName\": \"msg\",\n                                  \"targetProcess\": \"myProcess\",\n                                  \"targetFlowNode\": \"activity\",\n                                  \"messageContent\": {\n                                    \"id\": { \"value\": 123, \"type\": \"java.lang.Long\" },\n                                    \"name\": { \"value\": \"john\", \"type\": \"java.lang.String\" },\n                                    \"amount\": { \"value\": 1243.234, \"type\": \"java.lang.Double\" },\n                                    \"nbDay\": { \"value\": 34, \"type\": \"java.lang.Integer\" },\n                                    \"validated\": { \"value\": true, \"type\": \"java.lang.Boolean\" },\n                                    \"rate\": { \"value\": 3.0, \"type\": \"java.lang.Float\" },\n                                    \"expectedDate\": { \"value\": \"2018-09-09\", \"type\": \"java.time.LocalDate\" },\n                                    \"startDate\": { \"value\": \"2018-08-09T14:30:00\", \"type\": \"java.time.LocalDateTime\" },\n                                    \"globalEndDate\": { \"value\": \"2018-08-09T14:30:00+01:00\", \"type\": \"java.time.OffsetDateTime\" }\n                                  }\n                                }\n                                \"\"\"))\n                .andExpect(status().isNoContent());\n\n        ArgumentCaptor<Map<Expression, Expression>> msgContentCaptor = ArgumentCaptor.forClass(Map.class);\n\n        verify(processAPI).sendMessage(eq(\"msg\"),\n                eq(expression(\"myProcess\", \"myProcess\", String.class)),\n                eq(expression(\"activity\", \"activity\", String.class)),\n                msgContentCaptor.capture(),\n                eq(Collections.emptyMap()));\n\n        assertThat(msgContentCaptor.getValue())\n                .containsValue(expression(\"123\", \"123\", Long.class))\n                .containsValue(expression(\"john\", \"john\", String.class))\n                .containsValue(expression(\"1243.234\", \"1243.234\", Double.class))\n                .containsValue(expression(\"34\", \"34\", Integer.class))\n                .containsValue(expression(\"true\", \"true\", Boolean.class))\n                .containsValue(expression(\"3.0\", \"3.0\", Float.class))\n                .containsValue(expression(\"2018-09-09\", \"2018-09-09\", LocalDate.class))\n                .containsValue(expression(\"2018-08-09T14:30:00\", \"2018-08-09T14:30:00\", LocalDateTime.class))\n                .containsValue(\n                        expression(\"2018-08-09T14:30:00+01:00\", \"2018-08-09T14:30:00+01:00\", OffsetDateTime.class));\n    }\n\n    @Test\n    void should_return_bad_request_when_message_content_value_is_missing() throws Exception {\n        mockMvc.perform(post(\"/API/bpm/message\")\n                .sessionAttrs(sessionAttributes)\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(\"\"\"\n                        {\n                          \"messageName\": \"msg\",\n                          \"targetProcess\": \"myProcess\",\n                          \"targetFlowNode\": \"activity\",\n                          \"messageContent\": {\n                            \"id\": null\n                          }\n                        }\n                        \"\"\"))\n                .andExpect(status().isBadRequest());\n    }\n\n    @Test\n    void should_return_bad_request_when_message_content_value_is_null() throws Exception {\n        mockMvc.perform(post(\"/API/bpm/message\")\n                .sessionAttrs(sessionAttributes)\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(\"\"\"\n                        {\n                          \"messageName\": \"msg\",\n                          \"targetProcess\": \"myProcess\",\n                          \"targetFlowNode\": \"activity\",\n                          \"messageContent\": {\n                            \"id\": { \"value\": null }\n                          }\n                        }\n                        \"\"\"))\n                .andExpect(status().isBadRequest());\n    }\n\n    @Test\n    void should_accept_empty_string_in_message_content_values() throws Exception {\n        mockMvc.perform(post(\"/API/bpm/message\")\n                .sessionAttrs(sessionAttributes)\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(\"\"\"\n                        {\n                          \"messageName\": \"msg\",\n                          \"targetProcess\": \"myProcess\",\n                          \"targetFlowNode\": \"activity\",\n                          \"messageContent\": {\n                            \"id\": { \"value\": \"\" }\n                          }\n                        }\n                        \"\"\"))\n                .andExpect(status().isNoContent());\n\n        ArgumentCaptor<Map<Expression, Expression>> msgContentCaptor = ArgumentCaptor.forClass(Map.class);\n\n        verify(processAPI).sendMessage(eq(\"msg\"),\n                eq(expression(\"myProcess\", \"myProcess\", String.class)),\n                eq(expression(\"activity\", \"activity\", String.class)),\n                msgContentCaptor.capture(),\n                eq(Collections.emptyMap()));\n\n        assertThat(msgContentCaptor.getValue())\n                .containsValue(expression(\"empty-value\", \"\", String.class));\n    }\n\n    @Test\n    void should_accept_primitive_types_in_correlation_values() throws Exception {\n        mockMvc.perform(post(\"/API/bpm/message\")\n                .sessionAttrs(sessionAttributes)\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(\"\"\"\n                        {\n                          \"messageName\": \"msg\",\n                          \"targetProcess\": \"myProcess\",\n                          \"targetFlowNode\": \"activity\",\n                          \"correlations\": {\n                            \"id\": { \"value\": 123, \"type\": \"java.lang.Long\" },\n                            \"name\": { \"value\": \"john\", \"type\": \"java.lang.String\" }\n                          }\n                        }\n                        \"\"\"))\n                .andExpect(status().isNoContent());\n\n        ArgumentCaptor<Map<Expression, Expression>> correlationsCaptor = ArgumentCaptor.forClass(Map.class);\n\n        verify(processAPI).sendMessage(eq(\"msg\"),\n                eq(expression(\"myProcess\", \"myProcess\", String.class)),\n                eq(expression(\"activity\", \"activity\", String.class)),\n                eq(Collections.emptyMap()),\n                correlationsCaptor.capture());\n\n        assertThat(correlationsCaptor.getValue())\n                .containsValue(expression(\"123\", \"123\", Long.class))\n                .containsValue(expression(\"john\", \"john\", String.class));\n    }\n\n    @Test\n    void should_return_error_when_messageContent_has_unsupported_value_type() throws Exception {\n        mockMvc.perform(post(\"/API/bpm/message\")\n                .sessionAttrs(sessionAttributes)\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(\"\"\"\n                        {\n                          \"messageName\": \"msg\",\n                          \"targetProcess\": \"myProcess\",\n                          \"targetFlowNode\": \"activity\",\n                          \"messageContent\": {\n                            \"array\": { \"value\": \"someValue\", \"type\": \"java.lang.String[]\" }\n                          }\n                        }\n                        \"\"\"))\n                .andExpect(status().isInternalServerError());\n    }\n\n    @Test\n    void should_return_bad_request_when_more_than_5_correlations() throws Exception {\n        mockMvc.perform(post(\"/API/bpm/message\")\n                .sessionAttrs(sessionAttributes)\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(\"\"\"\n                        {\n                          \"messageName\": \"msg\",\n                          \"targetProcess\": \"myProcess\",\n                          \"targetFlowNode\": \"activity\",\n                          \"correlations\": {\n                            \"k1\": { \"value\": \"1\", \"type\": \"java.lang.String\" },\n                            \"k2\": { \"value\": \"1\", \"type\": \"java.lang.String\" },\n                            \"k3\": { \"value\": \"1\", \"type\": \"java.lang.String\" },\n                            \"k4\": { \"value\": \"1\", \"type\": \"java.lang.String\" },\n                            \"k5\": { \"value\": \"1\", \"type\": \"java.lang.String\" },\n                            \"k6\": { \"value\": \"1\", \"type\": \"java.lang.String\" }\n                          }\n                        }\n                        \"\"\"))\n                .andExpect(status().isBadRequest());\n    }\n\n    @Test\n    void should_send_message_with_message_content() throws Exception {\n        mockMvc.perform(post(\"/API/bpm/message\")\n                .sessionAttrs(sessionAttributes)\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(\"\"\"\n                        {\n                          \"messageName\": \"message\",\n                          \"targetProcess\": \"myProcess\",\n                          \"messageContent\": { \"id\": { \"value\": 12 } }\n                        }\n                        \"\"\"))\n                .andExpect(status().isNoContent());\n    }\n\n    @Test\n    void should_guess_LocalDateTime_type_when_not_specified() throws Exception {\n        mockMvc.perform(post(\"/API/bpm/message\")\n                .sessionAttrs(sessionAttributes)\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(\"\"\"\n                        {\n                          \"messageName\": \"msg\",\n                          \"targetProcess\": \"myProcess\",\n                          \"targetFlowNode\": \"activity\",\n                          \"messageContent\": {\n                            \"startDate\": { \"value\": \"2018-08-09T14:30:00\" }\n                          }\n                        }\n                        \"\"\"))\n                .andExpect(status().isNoContent());\n\n        ArgumentCaptor<Map<Expression, Expression>> msgContentCaptor = ArgumentCaptor.forClass(Map.class);\n\n        verify(processAPI).sendMessage(eq(\"msg\"),\n                eq(expression(\"myProcess\", \"myProcess\", String.class)),\n                eq(expression(\"activity\", \"activity\", String.class)),\n                msgContentCaptor.capture(),\n                eq(Collections.emptyMap()));\n\n        assertThat(msgContentCaptor.getValue())\n                .containsValue(expression(\"2018-08-09T14:30:00\", \"2018-08-09T14:30:00\", LocalDateTime.class));\n    }\n\n    @Test\n    void should_support_date_type() throws Exception {\n        mockMvc.perform(post(\"/API/bpm/message\")\n                .sessionAttrs(sessionAttributes)\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(\"\"\"\n                        {\n                          \"messageName\": \"message\",\n                          \"targetProcess\": \"myProcess\",\n                          \"targetFlowNode\": \"wait\",\n                          \"messageContent\": {\n                            \"updated\": { \"value\": \"2012-04-23T18:25:43.511Z\", \"type\": \"java.time.OffsetDateTime\" }\n                          }\n                        }\n                        \"\"\"))\n                .andExpect(status().isNoContent());\n    }\n\n    private Expression expression(String name, String content, Class<?> returnType) throws InvalidExpressionException {\n        return new ExpressionBuilder().createExpression(name, content, returnType.getName(),\n                ExpressionType.TYPE_CONSTANT);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/bpm/process/APIProcessConnectorDependencyTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.process;\n\nimport static org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDependencyItem.ATTRIBUTE_CONNECTOR_NAME;\nimport static org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDependencyItem.ATTRIBUTE_CONNECTOR_VERSION;\nimport static org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDependencyItem.ATTRIBUTE_PROCESS_ID;\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.MockitoAnnotations.initMocks;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.bonitasoft.web.rest.server.datastore.bpm.process.ProcessConnectorDependencyDatastore;\nimport org.bonitasoft.web.rest.server.framework.exception.APIFilterMandatoryException;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\n\n/**\n * @author Colin PUY\n */\npublic class APIProcessConnectorDependencyTest extends APITestWithMock {\n\n    @Mock\n    private ProcessConnectorDependencyDatastore datastore;\n\n    @Spy\n    private APIProcessConnectorDependency apiProcessConnectorDependency;\n\n    @Before\n    public void initializeMocks() {\n        initMocks(this);\n\n        doReturn(datastore).when(apiProcessConnectorDependency).defineDefaultDatastore();\n    }\n\n    private Map<String, String> buildFilters(String processId, String connectorName, String connectorVersion) {\n        Map<String, String> filters = new HashMap<>();\n        filters.put(ATTRIBUTE_PROCESS_ID, processId);\n        filters.put(ATTRIBUTE_CONNECTOR_NAME, connectorName);\n        filters.put(ATTRIBUTE_CONNECTOR_VERSION, connectorVersion);\n        return filters;\n    }\n\n    @Test(expected = APIFilterMandatoryException.class)\n    public void checkProcessIdIsMandatory() throws Exception {\n        Map<String, String> filters = buildFilters(null, \"aConnectorName\", \"aConnectorVersion\");\n\n        apiProcessConnectorDependency.checkMandatoryAttributes(filters);\n    }\n\n    @Test(expected = APIFilterMandatoryException.class)\n    public void checkConnectorNameIsMandatory() throws Exception {\n        Map<String, String> filters = buildFilters(\"1\", \"\", \"aConnectorVersion\");\n\n        apiProcessConnectorDependency.checkMandatoryAttributes(filters);\n    }\n\n    @Test(expected = APIFilterMandatoryException.class)\n    public void checkConnectorVersionIsMandatory() throws Exception {\n        Map<String, String> filters = buildFilters(\"1\", \"aConnectorName\", \"\");\n\n        apiProcessConnectorDependency.checkMandatoryAttributes(filters);\n    }\n\n    @Test\n    public void checkMandatoryAttributesDontThrowExceptionIfAllAtributesAreSet() throws Exception {\n        Map<String, String> filters = buildFilters(\"1\", \"aConnectorName\", \"aConnectorVersion\");\n\n        apiProcessConnectorDependency.checkMandatoryAttributes(filters);\n\n        assertTrue(\"no exception thrown\", true);\n    }\n\n    @Test(expected = APIFilterMandatoryException.class)\n    public void searchCheckMandatoryAttributes() throws Exception {\n        Map<String, String> filtersWithoutProcessId = buildFilters(null, \"aConnectorName\", \"aConnectorVersion\");\n\n        apiProcessConnectorDependency.search(0, 10, null, null, filtersWithoutProcessId);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/bpm/process/APIProcessConnectorTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.process;\n\nimport static java.util.Collections.EMPTY_MAP;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.MockitoAnnotations.initMocks;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorItem;\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.bonitasoft.web.rest.server.datastore.bpm.process.ProcessConnectorDatastore;\nimport org.bonitasoft.web.rest.server.framework.exception.APIFilterMandatoryException;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\n\n/**\n * @author Colin PUY\n */\n@SuppressWarnings(\"unchecked\")\npublic class APIProcessConnectorTest extends APITestWithMock {\n\n    @Mock\n    private ProcessConnectorDatastore processConnectorDatastore;\n\n    private APIProcessConnector apiProcessConnector;\n\n    @Before\n    public void initializeMocks() {\n        initMocks(this);\n\n        this.apiProcessConnector = spy(new APIProcessConnector());\n\n        doReturn(this.processConnectorDatastore).when(this.apiProcessConnector).defineDefaultDatastore();\n    }\n\n    @Test(expected = APIFilterMandatoryException.class)\n    public void whenSearchingFilterProcessIdIsMandatory() throws Exception {\n        this.apiProcessConnector.search(0, 10, null, null, EMPTY_MAP);\n    }\n\n    @Test\n    public void searchIsDoneWhenProcessIdFilterIsSet() throws Exception {\n        final Map<String, String> filters = new HashMap<>();\n        filters.put(ProcessConnectorItem.ATTRIBUTE_PROCESS_ID, \"1\");\n\n        this.apiProcessConnector.search(0, 10, null, null, filters);\n\n        verify(this.processConnectorDatastore).search(0, 10, null, null, filters);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/bpm/process/APIProcessTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.process;\n\nimport static org.mockito.ArgumentMatchers.anyLong;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.Mockito.*;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.console.common.server.i18n.I18n;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.ModelFactory;\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseItem;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessItem;\nimport org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore;\nimport org.bonitasoft.web.rest.server.datastore.bpm.process.ProcessDatastore;\nimport org.bonitasoft.web.rest.server.framework.APIServletCall;\nimport org.bonitasoft.web.toolkit.client.ItemDefinitionFactory;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Alexis Hassler\n * @author Celine Souchet\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class APIProcessTest {\n\n    private APIProcess apiProcess;\n\n    @Mock\n    private APIServletCall caller;\n\n    @Mock\n    private HttpSession session;\n\n    @Mock\n    private APISession engineSession;\n\n    @Mock\n    private ProcessDatastore processDatastore;\n\n    @Mock\n    private CaseDatastore caseDatastore;\n\n    @Before\n    public void consoleTestSetUp() throws IOException {\n        ItemDefinitionFactory.setDefaultFactory(new ModelFactory());\n        I18n.getInstance();\n\n        apiProcess = spy(new APIProcess());\n        apiProcess.setCaller(caller);\n        doReturn(caseDatastore).when(apiProcess).getCaseDatastore();\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.web.rest.server.api.bpm.process.APIProcess#fillCounters(ProcessItem, List).\n     */\n    @Test\n    public final void fillCounters_should_fill_number_of_failed_cases_when_counter_of_failed_cases_is_active() {\n        // Given\n        final APIID id = APIID.makeAPIID(78L);\n        final ProcessItem item = mock(ProcessItem.class);\n        doReturn(id).when(item).getId();\n\n        final List<String> counters = Arrays.asList(ProcessItem.COUNTER_FAILED_CASES);\n\n        final long numberOfFailedCases = 2L;\n        final Map<String, String> filters = new HashMap<>();\n        filters.put(CaseItem.FILTER_CALLER, \"any\");\n        filters.put(CaseItem.ATTRIBUTE_PROCESS_ID, item.getId().toString());\n        filters.put(CaseItem.FILTER_STATE, ProcessInstanceState.ERROR.name());\n        doReturn(numberOfFailedCases).when(caseDatastore).count(null, null, filters);\n\n        // When\n        apiProcess.fillCounters(item, counters);\n\n        // Then\n        verify(item).setAttribute(ProcessItem.COUNTER_FAILED_CASES, numberOfFailedCases);\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.web.rest.server.api.bpm.process.APIProcess#fillCounters(ProcessItem, List).\n     */\n    @Test\n    public final void fillCounters_should_do_nothing_when_counter_of_failed_cases_is_not_active() {\n        // Given\n        final ProcessItem item = mock(ProcessItem.class);\n        final List<String> counters = new ArrayList<>();\n\n        // When\n        apiProcess.fillCounters(item, counters);\n\n        // Then\n        verify(item, never()).setAttribute(anyString(), anyLong());\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.web.rest.server.api.bpm.process.APIProcess#fillCounters(ProcessItem, List).\n     */\n    @Test\n    public final void fillCounters_should_fill_number_of_open_cases_when_counter_of_open_cases_is_active() {\n        // Given\n        final APIID id = APIID.makeAPIID(78L);\n        final ProcessItem item = mock(ProcessItem.class);\n        doReturn(id).when(item).getId();\n\n        final List<String> counters = Arrays.asList(ProcessItem.COUNTER_OPEN_CASES);\n        final Map<String, String> filters = new HashMap<>();\n        filters.put(CaseItem.FILTER_CALLER, \"any\");\n        filters.put(CaseItem.ATTRIBUTE_PROCESS_ID, id.toString());\n\n        final long numberOfOpenCases = 2L;\n        doReturn(numberOfOpenCases).when(caseDatastore).count(null, null, filters);\n\n        // When\n        apiProcess.fillCounters(item, counters);\n\n        // Then\n        verify(item).setAttribute(ProcessItem.COUNTER_OPEN_CASES, numberOfOpenCases);\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.web.rest.server.api.bpm.process.APIProcess#fillCounters(ProcessItem, List).\n     */\n    @Test\n    public final void fillCounters_should_do_nothing_when_counter_of_open_cases_is_not_active() {\n        // Given\n        final ProcessItem item = mock(ProcessItem.class);\n        final List<String> counters = new ArrayList<>();\n\n        // When\n        apiProcess.fillCounters(item, counters);\n\n        // Then\n        verify(item, never()).setAttribute(anyString(), anyLong());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/bpm/process/ProcessContractControllerTest.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.process;\n\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.when;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.contract.Type;\nimport org.bonitasoft.engine.bpm.contract.impl.ConstraintDefinitionImpl;\nimport org.bonitasoft.engine.bpm.contract.impl.ContractDefinitionImpl;\nimport org.bonitasoft.engine.bpm.contract.impl.InputDefinitionImpl;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException;\nimport org.bonitasoft.web.rest.server.api.AbstractControllerTest;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mock;\nimport org.springframework.http.MediaType;\n\nclass ProcessContractControllerTest extends AbstractControllerTest<ProcessContractController> {\n\n    private static final long PROCESS_DEFINITION_ID = 4L;\n    private static final String TEST_CONTRACT_API_URL = \"/APISpringInternal/bpm/process/\" + PROCESS_DEFINITION_ID\n            + \"/contract\";\n\n    @Mock\n    private ProcessAPI processAPI;\n\n    @Override\n    protected ProcessContractController createController() {\n        return spy(new ProcessContractController());\n    }\n\n    @Override\n    protected void configureMocks(ProcessContractController controller) throws Exception {\n        doReturn(processAPI).when(controller).getProcessAPI(apiSession);\n    }\n\n    @Test\n    void should_return_a_contract_for_a_given_process_definition_id() throws Exception {\n        // given\n        final ContractDefinitionImpl contract = new ContractDefinitionImpl();\n        contract.addInput(new InputDefinitionImpl(\"anInput\", Type.TEXT, \"aDescription\"));\n        final InputDefinitionImpl complexInputDefinitionImpl = new InputDefinitionImpl(\"complexInput\",\n                \"description\",\n                true, null, null);\n        complexInputDefinitionImpl.getInputs()\n                .add(new InputDefinitionImpl(\"anInput\", Type.TEXT, \"aDescription\"));\n\n        contract.addInput(complexInputDefinitionImpl);\n        contract.addConstraint(new ConstraintDefinitionImpl(\"aRule\", \"an expression\", \"an explanation\"));\n\n        when(processAPI.getProcessContract(PROCESS_DEFINITION_ID)).thenReturn(contract);\n\n        // when\n        mockMvc.perform(get(TEST_CONTRACT_API_URL)\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(content().json(\"\"\"\n                        {\n                          \"inputs\": [\n                            {\n                              \"inputs\": [],\n                              \"type\": \"TEXT\",\n                              \"description\": \"aDescription\",\n                              \"name\": \"anInput\",\n                              \"multiple\": false\n                            },\n                            {\n                              \"inputs\": [\n                                {\n                                  \"inputs\": [],\n                                  \"type\": \"TEXT\",\n                                  \"description\": \"aDescription\",\n                                  \"name\": \"anInput\",\n                                  \"multiple\": false\n                                }\n                              ],\n                              \"type\": null,\n                              \"description\": \"description\",\n                              \"name\": \"complexInput\",\n                              \"multiple\": true\n                            }\n                          ],\n                          \"constraints\": [\n                            {\n                              \"name\": \"aRule\",\n                              \"expression\": \"an expression\",\n                              \"explanation\": \"an explanation\",\n                              \"inputNames\": []\n                            }\n                          ]\n                        }\"\"\", true));\n    }\n\n    @Test\n    void should_respond_404_Not_found_when_process_definition_is_not_found_when_getting_contract()\n            throws Exception {\n        when(processAPI.getProcessContract(PROCESS_DEFINITION_ID))\n                .thenThrow(new ProcessDefinitionNotFoundException(\"process definition not found\"));\n\n        mockMvc.perform(get(TEST_CONTRACT_API_URL)\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isNotFound());\n    }\n\n    @Test\n    void should_respond_204_when_there_is_no_contract_on_the_process() throws Exception {\n        doReturn(null).when(processAPI).getProcessContract(PROCESS_DEFINITION_ID);\n\n        mockMvc.perform(get(TEST_CONTRACT_API_URL)\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isNoContent());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/bpm/process/ProcessDefinitionDesignControllerTest.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.process;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.*;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.process.DesignProcessDefinition;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;\nimport org.bonitasoft.web.rest.server.api.AbstractControllerTest;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mock;\nimport org.springframework.http.MediaType;\n\nclass ProcessDefinitionDesignControllerTest extends AbstractControllerTest<ProcessDefinitionDesignController> {\n\n    private static final long PROCESS_DEFINITION_ID = 4L;\n\n    @Mock\n    protected ProcessAPI processAPI;\n\n    @Override\n    protected ProcessDefinitionDesignController createController() {\n        return spy(new ProcessDefinitionDesignController());\n    }\n\n    @Override\n    protected void configureMocks(ProcessDefinitionDesignController controller) throws Exception {\n        doReturn(processAPI).when(controller).getProcessAPI(apiSession);\n    }\n\n    @Test\n    void should_respond_404_Not_found_when_process_definition_is_not_found() throws Exception {\n        when(processAPI.getDesignProcessDefinition(PROCESS_DEFINITION_ID))\n                .thenThrow(new ProcessDefinitionNotFoundException(\"process definition not found\"));\n\n        mockMvc.perform(get(\"/APISpringInternal/bpm/process/\" + PROCESS_DEFINITION_ID + \"/design\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isNotFound());\n    }\n\n    @Test\n    void should_return_utf8_string_when_get_process_design_is_called()\n            throws Exception {\n        // given\n        final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder()\n                .createNewInstance(\"processWithAccents\", \"1.0\");\n        processDefinitionBuilder.addUserTask(\"étape1\", \"employee\");\n        final DesignProcessDefinition designProcessDefinition = processDefinitionBuilder.done();\n        when(processAPI.getDesignProcessDefinition(PROCESS_DEFINITION_ID)).thenReturn(designProcessDefinition);\n\n        // when/then\n        mockMvc.perform(get(\"/APISpringInternal/bpm/process/\" + PROCESS_DEFINITION_ID + \"/design\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(result -> {\n                    String content = result.getResponse().getContentAsString();\n                    assertThat(content).contains(\"étape1\");\n                    assertThat(content).doesNotContain(\"Ã©tape1\");\n                });\n    }\n\n    @Test\n    void replace_long_ids_to_string() {\n        ProcessDefinitionDesignController controller = new ProcessDefinitionDesignController();\n\n        assertThat(controller.replaceLongIdToString(\"\"\"\n                { \"id\": 123}\"\"\"))\n                .isEqualToIgnoringCase(\"\"\"\n                        { \"id\": \"123\"}\"\"\");\n        assertThat(controller.replaceLongIdToString(\"\"\"\n                { \"id\":123, \"test\": [ otherid: \"zerze\"]}\"\"\"))\n                .isEqualToIgnoringCase(\"\"\"\n                        { \"id\":\"123\", \"test\": [ otherid: \"zerze\"]}\"\"\");\n        assertThat(controller.replaceLongIdToString(\"\"\"\n                { \"iaed\": 123}\"\"\"))\n                .isEqualToIgnoringCase(\"\"\"\n                        { \"iaed\": 123}\"\"\");\n        assertThat(controller.replaceLongIdToString(\"\"\"\n                { \"name\": \"\\\\\"id\\\\\": 123\"}\"\"\"))\n                .isEqualToIgnoringCase(\"\"\"\n                        { \"name\": \"\\\\\"id\\\\\": 123\"}\"\"\");\n    }\n\n    @Test\n    void replaceLongIdToString_should_handle_edge_cases() {\n        ProcessDefinitionDesignController controller = new ProcessDefinitionDesignController();\n\n        // Empty string\n        assertThat(controller.replaceLongIdToString(\"\")).isEmpty();\n\n        // Zero value\n        assertThat(controller.replaceLongIdToString(\"\"\"\n                { \"id\": 0}\"\"\"))\n                .isEqualTo(\"\"\"\n                        { \"id\": \"0\"}\"\"\");\n\n        // Large number (Long.MAX_VALUE)\n        assertThat(controller.replaceLongIdToString(\"\"\"\n                { \"id\": 9223372036854775807}\"\"\"))\n                .isEqualTo(\"\"\"\n                        { \"id\": \"9223372036854775807\"}\"\"\");\n\n        // Multiple ids in nested structures\n        assertThat(controller.replaceLongIdToString(\"\"\"\n                { \"id\": 123, \"nested\": { \"id\": 456 }}\"\"\"))\n                .isEqualTo(\"\"\"\n                        { \"id\": \"123\", \"nested\": { \"id\": \"456\" }}\"\"\");\n\n        // Ids in array of objects\n        assertThat(controller.replaceLongIdToString(\"\"\"\n                [{\"id\": 1}, {\"id\": 2}, {\"id\": 3}]\"\"\"))\n                .isEqualTo(\"\"\"\n                        [{\"id\": \"1\"}, {\"id\": \"2\"}, {\"id\": \"3\"}]\"\"\");\n\n        // No whitespace around colon\n        assertThat(controller.replaceLongIdToString(\"\"\"\n                {\"id\":123}\"\"\"))\n                .isEqualTo(\"\"\"\n                        {\"id\":\"123\"}\"\"\");\n\n        // Multiple whitespace around colon\n        assertThat(controller.replaceLongIdToString(\"\"\"\n                { \"id\"  :  456 }\"\"\"))\n                .isEqualTo(\"\"\"\n                        { \"id\"  :  \"456\" }\"\"\");\n\n        // Id at end of object\n        assertThat(controller.replaceLongIdToString(\"\"\"\n                { \"name\": \"test\", \"id\": 789 }\"\"\"))\n                .isEqualTo(\"\"\"\n                        { \"name\": \"test\", \"id\": \"789\" }\"\"\");\n\n        // Id followed by comma\n        assertThat(controller.replaceLongIdToString(\"\"\"\n                { \"id\": 999, \"name\": \"test\" }\"\"\"))\n                .isEqualTo(\"\"\"\n                        { \"id\": \"999\", \"name\": \"test\" }\"\"\");\n\n        // Multiline JSON\n        assertThat(controller.replaceLongIdToString(\"\"\"\n                {\n                  \"id\": 111,\n                  \"name\": \"test\"\n                }\"\"\"))\n                .isEqualTo(\"\"\"\n                        {\n                          \"id\": \"111\",\n                          \"name\": \"test\"\n                        }\"\"\");\n\n        // Complex nested structure with multiple ids\n        assertThat(controller.replaceLongIdToString(\"\"\"\n                { \"id\": 1, \"items\": [{ \"id\": 2 }, { \"id\": 3 }], \"parent\": { \"id\": 4, \"child\": { \"id\": 5 }}}\"\"\"))\n                .isEqualTo(\n                        \"\"\"\n                                { \"id\": \"1\", \"items\": [{ \"id\": \"2\" }, { \"id\": \"3\" }], \"parent\": { \"id\": \"4\", \"child\": { \"id\": \"5\" }}}\"\"\");\n\n        // Id is not replaced when part of longer field name\n        assertThat(controller.replaceLongIdToString(\"\"\"\n                { \"userId\": 123, \"id\": 456, \"customId\": 789 }\"\"\"))\n                .isEqualTo(\"\"\"\n                        { \"userId\": 123, \"id\": \"456\", \"customId\": 789 }\"\"\");\n\n        // Case sensitivity - only lowercase \"id\" is replaced\n        assertThat(controller.replaceLongIdToString(\"\"\"\n                { \"Id\": 123, \"ID\": 456, \"id\": 789 }\"\"\"))\n                .isEqualTo(\"\"\"\n                        { \"Id\": 123, \"ID\": 456, \"id\": \"789\" }\"\"\");\n    }\n\n    @Test\n    void replaceLongIdToString_should_not_replace_negative_numbers() {\n        ProcessDefinitionDesignController controller = new ProcessDefinitionDesignController();\n\n        // Negative numbers are not converted (regex limitation - \\d+ doesn't match minus sign)\n        // This documents current behavior - negative ids remain numeric\n        assertThat(controller.replaceLongIdToString(\"\"\"\n                { \"id\": -123}\"\"\"))\n                .isEqualTo(\"\"\"\n                        { \"id\": -123}\"\"\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/bpm/process/ProcessInstantiationControllerTest.java",
    "content": "/**\n * Copyright (C) 2025 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.process;\n\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.*;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;\n\nimport java.io.Serializable;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.console.common.server.i18n.I18n;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.contract.ContractDefinition;\nimport org.bonitasoft.engine.bpm.contract.ContractViolationException;\nimport org.bonitasoft.engine.bpm.process.ProcessExecutionException;\nimport org.bonitasoft.engine.bpm.process.impl.internal.ProcessInstanceImpl;\nimport org.bonitasoft.web.rest.server.api.AbstractControllerTest;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mock;\nimport org.springframework.http.MediaType;\n\npublic class ProcessInstantiationControllerTest extends AbstractControllerTest<ProcessInstantiationController> {\n\n    private static final long PROCESS_DEFINITION_ID = 2L;\n\n    @Mock\n    private ProcessAPI processAPI;\n\n    @Mock\n    private ContractDefinition contractDefinition;\n\n    @BeforeAll\n    public static void initClass() {\n        I18n.getInstance();\n    }\n\n    @Override\n    protected ProcessInstantiationController createController() {\n        ProcessInstantiationController controller = spy(new ProcessInstantiationController());\n        doReturn(3L).when(controller).getMaxFileSize();\n        return controller;\n    }\n\n    @Override\n    protected void configureMocks(ProcessInstantiationController controller) throws Exception {\n        doReturn(processAPI).when(controller).getProcessAPI(apiSession);\n        when(contractDefinition.getInputs()).thenReturn(Collections.emptyList());\n    }\n\n    private String aComplexInputAsJson() {\n        return \"\"\"\n                {\n                    \"aBoolean\": true,\n                    \"aString\": \"hello world\",\n                    \"a_complex_type\": {\n                        \"aNumber\": 2,\n                        \"aBoolean\": false\n                    }\n                }\"\"\";\n    }\n\n    private Map<String, Serializable> aComplexInput() {\n        final HashMap<String, Serializable> aComplexInput = new HashMap<>();\n        aComplexInput.put(\"aBoolean\", true);\n        aComplexInput.put(\"aString\", \"hello world\");\n\n        final HashMap<String, Serializable> childMap = new HashMap<>();\n        childMap.put(\"aNumber\", 2);\n        childMap.put(\"aBoolean\", false);\n\n        aComplexInput.put(\"a_complex_type\", childMap);\n\n        return aComplexInput;\n    }\n\n    @Test\n    public void should_instantiate_a_process_with_given_inputs() throws Exception {\n        final Map<String, Serializable> expectedComplexInput = aComplexInput();\n        final ProcessInstanceImpl processInstance = new ProcessInstanceImpl(\"complexProcessInstance\");\n        processInstance.setId(12L);\n        when(processAPI.getProcessContract(PROCESS_DEFINITION_ID)).thenReturn(contractDefinition);\n        when(processAPI.startProcessWithInputs(anyLong(), anyMap()))\n                .thenReturn(processInstance);\n\n        mockMvc.perform(post(\"/APISpringInternal/bpm/process/\" + PROCESS_DEFINITION_ID + \"/instantiation\")\n                .sessionAttrs(sessionAttributes)\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(aComplexInputAsJson()))\n                .andExpect(status().isOk())\n                .andExpect(content().json(\"\"\"\n                        {\"caseId\": 12}\"\"\", true));\n\n        verify(processAPI).startProcessWithInputs(eq(PROCESS_DEFINITION_ID), eq(expectedComplexInput));\n    }\n\n    @Test\n    public void should_instantiate_process_with_specific_user_id() throws Exception {\n        final long userId = 42L;\n        final Map<String, Serializable> expectedComplexInput = aComplexInput();\n        final ProcessInstanceImpl processInstance = new ProcessInstanceImpl(\"processForUser\");\n        processInstance.setId(99L);\n        doReturn(contractDefinition).when(processAPI).getProcessContract(PROCESS_DEFINITION_ID);\n        doReturn(processInstance).when(processAPI).startProcessWithInputs(eq(userId), eq(PROCESS_DEFINITION_ID),\n                anyMap());\n\n        mockMvc.perform(post(\"/APISpringInternal/bpm/process/\" + PROCESS_DEFINITION_ID + \"/instantiation\")\n                .sessionAttrs(sessionAttributes)\n                .param(\"user\", String.valueOf(userId))\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(aComplexInputAsJson()))\n                .andExpect(status().isOk())\n                .andExpect(content().json(\"\"\"\n                        {\"caseId\": 99}\"\"\", true));\n\n        verify(processAPI).startProcessWithInputs(eq(userId), eq(PROCESS_DEFINITION_ID),\n                eq(expectedComplexInput));\n    }\n\n    @Test\n    public void should_return_429_with_retry_after_header_when_rate_limit_exceeded() throws Exception {\n        final long retryAfterTimestamp = System.currentTimeMillis() + 60000; // 1 minute from now\n        final ProcessExecutionException exception = new ProcessExecutionException(\n                new RuntimeException(\"Rate limit exceeded\"), retryAfterTimestamp);\n\n        when(processAPI.getProcessContract(PROCESS_DEFINITION_ID)).thenReturn(contractDefinition);\n        when(processAPI.startProcessWithInputs(anyLong(), anyMap()))\n                .thenThrow(exception);\n\n        mockMvc.perform(post(\"/APISpringInternal/bpm/process/\" + PROCESS_DEFINITION_ID + \"/instantiation\")\n                .sessionAttrs(sessionAttributes)\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(aComplexInputAsJson()))\n                .andExpect(status().isTooManyRequests())\n                .andExpect(content().string(\"Case creation limit reached.\"));\n    }\n\n    @Test\n    public void should_throw_process_execution_exception_with_enhanced_message() throws Exception {\n        final ProcessExecutionException exception = new ProcessExecutionException(\"Process execution failed\");\n\n        when(processAPI.getProcessContract(PROCESS_DEFINITION_ID)).thenReturn(contractDefinition);\n        when(processAPI.startProcessWithInputs(anyLong(), anyMap()))\n                .thenThrow(exception);\n\n        mockMvc.perform(post(\"/APISpringInternal/bpm/process/\" + PROCESS_DEFINITION_ID + \"/instantiation\")\n                .sessionAttrs(sessionAttributes)\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(aComplexInputAsJson()))\n                .andExpect(status().isInternalServerError())\n                .andExpect(jsonPath(\"$.exception\").value(ProcessExecutionException.class.toString()))\n                .andExpect(jsonPath(\"$.message\")\n                        .value(\"Unable to start the process with ID 2 (consult the logs for more information).\"));\n    }\n\n    @Test\n    public void should_handle_contract_violation_exception() throws Exception {\n        final ContractViolationException exception = new ContractViolationException(\n                \"Contract validation failed\",\n                \"Detailed message about contract violations\",\n                Collections.emptyList(),\n                null);\n\n        when(processAPI.getProcessContract(PROCESS_DEFINITION_ID)).thenReturn(contractDefinition);\n        when(processAPI.startProcessWithInputs(anyLong(), anyMap()))\n                .thenThrow(exception);\n\n        // ContractViolationException is wrapped in BonitaException and handled by the\n        // global exception handler as HTTP 500\n        mockMvc.perform(post(\"/APISpringInternal/bpm/process/\" + PROCESS_DEFINITION_ID + \"/instantiation\")\n                .sessionAttrs(sessionAttributes)\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(aComplexInputAsJson()))\n                .andExpect(status().isBadRequest())\n                .andExpect(jsonPath(\"$.exception\").value(IllegalArgumentException.class.toString()))\n                .andExpect(jsonPath(\"$.message\").value(\"Detailed message about contract violations\"));\n    }\n\n    @Test\n    public void should_instantiate_process_without_inputs() throws Exception {\n        final ProcessInstanceImpl processInstance = new ProcessInstanceImpl(\"processWithoutInputs\");\n        processInstance.setId(77L);\n        when(processAPI.getProcessContract(PROCESS_DEFINITION_ID)).thenReturn(contractDefinition);\n        when(processAPI.startProcessWithInputs(eq(PROCESS_DEFINITION_ID), any()))\n                .thenReturn(processInstance);\n\n        mockMvc.perform(post(\"/APISpringInternal/bpm/process/\" + PROCESS_DEFINITION_ID + \"/instantiation\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(content().json(\"\"\"\n                        {\"caseId\": 77}\"\"\", true));\n\n        verify(processAPI).startProcessWithInputs(eq(PROCESS_DEFINITION_ID), any());\n    }\n\n    @Test\n    public void should_return_400_when_user_parameter_is_not_a_valid_number() throws Exception {\n        mockMvc.perform(post(\"/APISpringInternal/bpm/process/\" + PROCESS_DEFINITION_ID + \"/instantiation\")\n                .sessionAttrs(sessionAttributes)\n                .param(\"user\", \"invalid\")\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(aComplexInputAsJson()))\n                .andExpect(status().isBadRequest())\n                .andExpect(content().string(\"'user' URL query parameter should be Integer. Received 'invalid'\"));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/bpm/signal/BPMSignalControllerTest.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.bpm.signal;\n\nimport static org.hamcrest.Matchers.containsString;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.web.rest.server.api.AbstractControllerTest;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mock;\nimport org.springframework.http.MediaType;\n\nclass BPMSignalControllerTest extends AbstractControllerTest<BPMSignalController> {\n\n    @Mock\n    protected ProcessAPI processAPI;\n\n    @Override\n    protected BPMSignalController createController() {\n        return spy(new BPMSignalController());\n    }\n\n    @Override\n    protected void configureMocks(BPMSignalController controller) throws Exception {\n        doReturn(processAPI).when(controller).getProcessAPI(apiSession);\n    }\n\n    @Test\n    void should_broadcast_signal_of_a_given_name() throws Exception {\n        mockMvc.perform(post(\"/API/bpm/signal\")\n                .sessionAttrs(sessionAttributes)\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(\"\"\"\n                        {\"name\": \"signalName\"}\n                        \"\"\"))\n                .andExpect(status().isNoContent());\n\n        verify(processAPI).sendSignal(\"signalName\");\n    }\n\n    @Test\n    void should_return_bad_request_if_signal_name_not_set() throws Exception {\n        mockMvc.perform(post(\"/API/bpm/signal\")\n                .sessionAttrs(sessionAttributes)\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(\"\"\"\n                        {\"name\": null}\n                        \"\"\"))\n                .andExpect(status().isBadRequest())\n                .andExpect(content().string(containsString(\"'name' attribute is mandatory\")));\n    }\n\n    @Test\n    void should_return_bad_request_if_request_body_is_empty() throws Exception {\n        mockMvc.perform(post(\"/API/bpm/signal\")\n                .sessionAttrs(sessionAttributes)\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(\"\"))\n                .andExpect(status().isBadRequest())\n                .andExpect(content().string(containsString(\"Unable to parse the JSON body\")));\n    }\n\n    @Test\n    void should_return_bad_request_when_json_is_malformed() throws Exception {\n        mockMvc.perform(post(\"/API/bpm/signal\")\n                .sessionAttrs(sessionAttributes)\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(\"{invalid json\"))\n                .andExpect(status().isBadRequest())\n                .andExpect(content().string(containsString(\"Unable to parse the JSON body\")));\n    }\n\n    @Test\n    void should_support_encoded_characters_for_signal_name() throws Exception {\n        mockMvc.perform(post(\"/API/bpm/signal\")\n                .sessionAttrs(sessionAttributes)\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(\"\"\"\n                        {\"name\": \"My Sign\\u00e0l\"}\n                        \"\"\"))\n                .andExpect(status().isNoContent());\n\n        verify(processAPI).sendSignal(\"My Sign\\u00e0l\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/deployer/UserDeployerTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.deployer;\n\nimport static junit.framework.Assert.assertEquals;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\nimport static org.mockito.MockitoAnnotations.initMocks;\n\nimport org.bonitasoft.web.rest.model.ModelFactory;\nimport org.bonitasoft.web.rest.model.identity.GroupItem;\nimport org.bonitasoft.web.rest.model.identity.UserItem;\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet;\nimport org.bonitasoft.web.toolkit.client.ItemDefinitionFactory;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\n\n/**\n * @author Vincent Elcrin\n */\npublic class UserDeployerTest extends APITestWithMock {\n\n    @Mock\n    private DatastoreHasGet<UserItem> getter;\n\n    @Before\n    public void setUp() {\n        initMocks(this);\n        ItemDefinitionFactory.setDefaultFactory(new ModelFactory());\n    }\n\n    @Test\n    public void testDeployableAttributeIsDeployed() {\n        UserItem user = prepareGetterToReturnAUser();\n        GroupItem group = aGroupInstalledBy(APIID.makeAPIID(6L));\n\n        UserDeployer installedByDeployer = new UserDeployer(getter, GroupItem.ATTRIBUTE_CREATED_BY_USER_ID);\n        installedByDeployer.deployIn(group);\n\n        assertEquals(user, group.getCreatedByUser());\n    }\n\n    @Test\n    public void testStringValueIsDeployed() {\n        prepareGetterToReturnAUser();\n        GroupItem group = spy(aGroupInstalledBy(APIID.makeAPIID(\"unusedId\")));\n\n        UserDeployer nameDeployer = new UserDeployer(getter, GroupItem.ATTRIBUTE_CREATED_BY_USER_ID);\n        nameDeployer.deployIn(group);\n\n        verify(group, times(1)).setDeploy(any(), any());\n    }\n\n    @Test\n    public void testCompoundLongValueIsDeployed() {\n        prepareGetterToReturnAUser();\n        GroupItem group = spy(aGroupInstalledBy(APIID.makeAPIID(3L, -1L, null)));\n\n        UserDeployer nameDeployer = new UserDeployer(getter, GroupItem.ATTRIBUTE_CREATED_BY_USER_ID);\n        nameDeployer.deployIn(group);\n\n        verify(group, times(1)).setDeploy(any(), any());\n    }\n\n    @Test\n    public void testCompoundStringValueIsDeployed() {\n        prepareGetterToReturnAUser();\n        GroupItem group = spy(aGroupInstalledBy(APIID.makeAPIID(\"3\", \"-1\", null, \"unusedId\")));\n\n        UserDeployer nameDeployer = new UserDeployer(getter, GroupItem.ATTRIBUTE_CREATED_BY_USER_ID);\n        nameDeployer.deployIn(group);\n\n        verify(group, times(1)).setDeploy(any(), any());\n    }\n\n    @Test\n    public void testNotDeployableAttributeIsNotDeployed() {\n        prepareGetterToReturnAUser();\n        GroupItem group = spy(aGroupInstalledBy(null));\n\n        UserDeployer nameDeployer = new UserDeployer(getter, GroupItem.ATTRIBUTE_CREATED_BY_USER_ID);\n        nameDeployer.deployIn(group);\n\n        verify(group, never()).setDeploy(any(), any());\n    }\n\n    @Test\n    public void testNegativeStringValueIsNotDeployed() {\n        prepareGetterToReturnAUser();\n        GroupItem group = spy(aGroupInstalledBy(APIID.makeAPIID(\"-1\")));\n\n        UserDeployer nameDeployer = new UserDeployer(getter, GroupItem.ATTRIBUTE_CREATED_BY_USER_ID);\n        nameDeployer.deployIn(group);\n\n        verify(group, never()).setDeploy(any(), any());\n    }\n\n    @Test\n    public void testNegativeLongValueIsNotDeployed() {\n        prepareGetterToReturnAUser();\n        GroupItem group = spy(aGroupInstalledBy(APIID.makeAPIID(-1L)));\n\n        UserDeployer nameDeployer = new UserDeployer(getter, GroupItem.ATTRIBUTE_CREATED_BY_USER_ID);\n        nameDeployer.deployIn(group);\n\n        verify(group, never()).setDeploy(any(), any());\n    }\n\n    private UserItem prepareGetterToReturnAUser() {\n        UserItem user = new UserItem();\n        doReturn(user).when(getter).get(any(APIID.class));\n        return user;\n    }\n\n    private GroupItem aGroupInstalledBy(APIID userId) {\n        GroupItem item = new GroupItem();\n        item.setCreatedByUserId(userId);\n        return item;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/document/APIDocumentTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.document;\n\nimport static org.junit.Assert.fail;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport java.io.IOException;\n\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.console.common.server.i18n.I18n;\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.document.DocumentItem;\nimport org.bonitasoft.web.rest.server.api.document.api.impl.DocumentDatastore;\nimport org.bonitasoft.web.rest.server.framework.APIServletCall;\nimport org.bonitasoft.web.toolkit.client.ItemDefinitionFactory;\nimport org.bonitasoft.web.toolkit.client.common.CommonDateFormater;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIForbiddenException;\nimport org.bonitasoft.web.toolkit.server.utils.ServerDateFormater;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Julien Mege\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class APIDocumentTest {\n\n    @Mock\n    APIServletCall caller;\n\n    @Mock\n    private HttpSession session;\n\n    @Mock\n    DocumentItem item;\n\n    APIDocument apiDocument;\n\n    @Mock\n    private ItemDefinitionFactory factory;\n\n    @Mock\n    private DocumentDatastore documentDatastore;\n\n    @Mock\n    private APISession engineSession;\n\n    @Before\n    public void setup() throws Exception {\n        ItemDefinitionFactory.setDefaultFactory(factory);\n        I18n.getInstance();\n        CommonDateFormater.setDateFormater(new ServerDateFormater());\n        apiDocument = spy(new APIDocument());\n        apiDocument.setCaller(caller);\n        doReturn(documentDatastore).when(apiDocument).getDataStore();\n        doReturn(\"../../..\").when(item).getAttributeValue(DocumentItem.DOCUMENT_UPLOAD);\n        doReturn(\"doc\").when(item).getAttributeValue(DocumentItem.DOCUMENT_NAME);\n        doReturn(\"1\").when(item).getAttributeValue(DocumentItem.PROCESSINSTANCE_ID);\n        doReturn(\"type\").when(item).getAttributeValue(DocumentItem.DOCUMENT_CREATION_TYPE);\n\n    }\n\n    @Test(expected = APIException.class)\n    public void it_throws_an_exception_when_cannot_write_file_on_add() throws Exception {\n        // Given\n        doThrow(new IOException(\"error\")).when(documentDatastore).createDocument(any(Long.class), any(String.class),\n                any(String.class),\n                any(String.class), any(BonitaHomeFolderAccessor.class));\n\n        // When\n        try {\n            apiDocument.add(item);\n        } catch (final APIForbiddenException e) {\n            fail(\"Don't expect the APIException to be an APIForbiddenException\");\n        }\n\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/document/api/impl/DocumentDatastoreTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.document.api.impl;\n\nimport static org.junit.Assert.assertNotNull;\nimport static org.mockito.Mockito.*;\n\nimport java.io.InputStream;\n\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.document.Document;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.io.FileContent;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.document.DocumentItem;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Julien Mege\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class DocumentDatastoreTest {\n\n    @Mock\n    private APISession session;\n\n    @Mock\n    private ProcessAPI processAPI;\n\n    @Mock\n    private Document document;\n\n    @Mock\n    private ProcessInstance processInstance;\n\n    @Mock\n    private ProcessDeploymentInfo processDeploymentInfo;\n\n    private DocumentDatastore documentDatastore;\n\n    @Mock\n    private BonitaHomeFolderAccessor tenantFolder;\n\n    @Test\n    public void should_retrieve_temporary_file() throws Exception {\n        documentDatastore = spy(new DocumentDatastore(session));\n        doReturn(processAPI).when(documentDatastore).getProcessAPI();\n        FileContent fileContent = new FileContent(\"doc.txt\", InputStream.nullInputStream(), \"text/plain\");\n        doReturn(fileContent).when(tenantFolder).retrieveUploadedTempContent(\"docKey\");\n        doReturn(1L).when(document).getProcessInstanceId();\n        doReturn(document).when(processAPI).attachDocument(1L, \"docName\", \"doc.txt\", \"text/plain\", new byte[0]);\n        doReturn(processInstance).when(processAPI).getProcessInstance(1L);\n        doReturn(2L).when(processInstance).getProcessDefinitionId();\n        doReturn(processDeploymentInfo).when(processAPI).getProcessDeploymentInfo(2L);\n\n        final DocumentItem item = documentDatastore.createDocument(1L, \"docName\", DocumentDatastore.CREATE_NEW_DOCUMENT,\n                \"docKey\", tenantFolder);\n\n        assertNotNull(item);\n        verify(processAPI).attachDocument(1L, \"docName\", \"doc.txt\", \"text/plain\", new byte[0]);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/extension/ApiExtensionControllerTest.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.extension;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;\n\nimport javax.servlet.http.Cookie;\n\nimport org.bonitasoft.console.common.server.page.PageMappingService;\nimport org.bonitasoft.console.common.server.page.RestApiRenderer;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.web.extension.rest.RestApiResponseBuilder;\nimport org.bonitasoft.web.rest.server.api.AbstractControllerTest;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mock;\nimport org.springframework.http.MediaType;\n\nclass ApiExtensionControllerTest extends AbstractControllerTest<ApiExtensionController> {\n\n    private static final String API_URL = \"/API/extension/myResource\";\n\n    @Mock\n    private RestApiRenderer restApiRenderer;\n\n    @Mock\n    private PageMappingService pageMappingService;\n\n    @Override\n    protected ApiExtensionController createController() {\n        return spy(new ApiExtensionController(restApiRenderer, pageMappingService));\n    }\n\n    @Override\n    protected void configureMocks(ApiExtensionController controller) {\n        // No additional mock configuration needed\n    }\n\n    @Test\n    void should_return_response_body() throws Exception {\n        var restApiResponse = new RestApiResponseBuilder().withResponse(\"{'key':'value'}\").build();\n        when(restApiRenderer.handleRestApiCall(any(), any())).thenReturn(restApiResponse);\n\n        mockMvc.perform(get(API_URL)\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(content().string(\"{'key':'value'}\"));\n    }\n\n    @Test\n    void should_return_empty_response() throws Exception {\n        var restApiResponse = new RestApiResponseBuilder().withResponse(\"\").build();\n        when(restApiRenderer.handleRestApiCall(any(), any())).thenReturn(restApiResponse);\n\n        mockMvc.perform(get(API_URL)\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(content().string(\"\"));\n    }\n\n    @Test\n    void should_return_null_body() throws Exception {\n        var restApiResponse = new RestApiResponseBuilder().build();\n        when(restApiRenderer.handleRestApiCall(any(), any())).thenReturn(restApiResponse);\n\n        mockMvc.perform(get(API_URL)\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk());\n    }\n\n    @Test\n    void should_return_500_when_response_is_null() throws Exception {\n        when(restApiRenderer.handleRestApiCall(any(), any())).thenReturn(null);\n\n        mockMvc.perform(get(API_URL)\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isInternalServerError());\n    }\n\n    @Test\n    void should_return_500_on_bonita_exception() throws Exception {\n        when(restApiRenderer.handleRestApiCall(any(), any())).thenThrow(new BonitaException(\"error message\"));\n\n        mockMvc.perform(get(API_URL)\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isInternalServerError());\n    }\n\n    @Test\n    void should_pass_through_content_range_header() throws Exception {\n        var restApiResponse = new RestApiResponseBuilder()\n                .withAdditionalHeader(\"Content-Range\", \"1-10/100\")\n                .withResponse(\"[]\")\n                .build();\n        when(restApiRenderer.handleRestApiCall(any(), any())).thenReturn(restApiResponse);\n\n        mockMvc.perform(get(API_URL)\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(header().string(\"Content-Range\", \"1-10/100\"));\n    }\n\n    @Test\n    void should_pass_through_location_header() throws Exception {\n        var restApiResponse = new RestApiResponseBuilder()\n                .withAdditionalHeader(\"Location\", \"https://documentation.ofelia.com/bonita/7.7/\")\n                .withResponse(\"\")\n                .build();\n        when(restApiRenderer.handleRestApiCall(any(), any())).thenReturn(restApiResponse);\n\n        mockMvc.perform(get(API_URL)\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(header().string(\"Location\", \"https://documentation.ofelia.com/bonita/7.7/\"));\n    }\n\n    @Test\n    void should_pass_through_content_disposition_header() throws Exception {\n        var restApiResponse = new RestApiResponseBuilder()\n                .withAdditionalHeader(\"Content-Disposition\", \"attachment; filename=\\\"Mon Fichier.png\\\"\")\n                .withResponse(\"\")\n                .build();\n        when(restApiRenderer.handleRestApiCall(any(), any())).thenReturn(restApiResponse);\n\n        mockMvc.perform(get(API_URL)\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(header().string(\"Content-Disposition\", \"attachment; filename=\\\"Mon Fichier.png\\\"\"));\n    }\n\n    @Test\n    void should_return_custom_status_code() throws Exception {\n        var restApiResponse = new RestApiResponseBuilder()\n                .withResponseStatus(201)\n                .withResponse(\"{}\")\n                .build();\n        when(restApiRenderer.handleRestApiCall(any(), any())).thenReturn(restApiResponse);\n\n        mockMvc.perform(post(API_URL)\n                .sessionAttrs(sessionAttributes)\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(\"{}\"))\n                .andExpect(status().isCreated());\n    }\n\n    @Test\n    void should_handle_post_request() throws Exception {\n        var restApiResponse = new RestApiResponseBuilder().withResponse(\"{'created':true}\").build();\n        when(restApiRenderer.handleRestApiCall(any(), any())).thenReturn(restApiResponse);\n\n        mockMvc.perform(post(API_URL)\n                .sessionAttrs(sessionAttributes)\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(\"{}\"))\n                .andExpect(status().isOk())\n                .andExpect(content().string(\"{'created':true}\"));\n    }\n\n    @Test\n    void should_handle_put_request() throws Exception {\n        var restApiResponse = new RestApiResponseBuilder().withResponse(\"{'updated':true}\").build();\n        when(restApiRenderer.handleRestApiCall(any(), any())).thenReturn(restApiResponse);\n\n        mockMvc.perform(put(API_URL)\n                .sessionAttrs(sessionAttributes)\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(\"{}\"))\n                .andExpect(status().isOk())\n                .andExpect(content().string(\"{'updated':true}\"));\n    }\n\n    @Test\n    void should_handle_delete_request() throws Exception {\n        var restApiResponse = new RestApiResponseBuilder().withResponse(\"\").build();\n        when(restApiRenderer.handleRestApiCall(any(), any())).thenReturn(restApiResponse);\n\n        mockMvc.perform(delete(API_URL)\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk());\n    }\n\n    @Test\n    void should_handle_patch_request() throws Exception {\n        var restApiResponse = new RestApiResponseBuilder().withResponse(\"{'patched':true}\").build();\n        when(restApiRenderer.handleRestApiCall(any(), any())).thenReturn(restApiResponse);\n\n        mockMvc.perform(patch(API_URL)\n                .sessionAttrs(sessionAttributes)\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(\"{}\"))\n                .andExpect(status().isOk())\n                .andExpect(content().string(\"{'patched':true}\"));\n    }\n\n    @Test\n    void should_return_404_status_from_extension() throws Exception {\n        var restApiResponse = new RestApiResponseBuilder()\n                .withResponseStatus(404)\n                .withResponse(\"{\\\"error\\\":\\\"not found\\\"}\")\n                .build();\n        when(restApiRenderer.handleRestApiCall(any(), any())).thenReturn(restApiResponse);\n\n        mockMvc.perform(get(API_URL)\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isNotFound())\n                .andExpect(content().string(\"{\\\"error\\\":\\\"not found\\\"}\"));\n    }\n\n    @Test\n    void should_set_cookies_from_response() throws Exception {\n        var cookie = new Cookie(\"myCookie\", \"myValue\");\n        cookie.setPath(\"/\");\n        var restApiResponse = new RestApiResponseBuilder()\n                .withAdditionalCookie(cookie)\n                .withResponse(\"\")\n                .build();\n        when(restApiRenderer.handleRestApiCall(any(), any())).thenReturn(restApiResponse);\n\n        mockMvc.perform(get(API_URL)\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(cookie().value(\"myCookie\", \"myValue\"));\n    }\n\n    @Test\n    void handleExtensionCall_should_use_default_utf8_charset_with_json_media_type() throws Exception {\n        var restApiResponse = new RestApiResponseBuilder()\n                .withResponse(\"{}\")\n                .build();\n        when(restApiRenderer.handleRestApiCall(any(), any())).thenReturn(restApiResponse);\n\n        mockMvc.perform(get(API_URL)\n                .sessionAttrs(sessionAttributes))\n                .andExpect(status().isOk())\n                .andExpect(content().contentType(\"application/json;charset=UTF-8\"));\n    }\n\n    @Test\n    void handleExtensionCall_should_use_custom_charset_when_specified() throws Exception {\n        var restApiResponse = new RestApiResponseBuilder()\n                .withCharacterSet(\"ISO-8859-1\")\n                .withResponse(\"{}\")\n                .build();\n        when(restApiRenderer.handleRestApiCall(any(), any())).thenReturn(restApiResponse);\n\n        mockMvc.perform(get(API_URL)\n                .sessionAttrs(sessionAttributes))\n                .andExpect(status().isOk())\n                .andExpect(content().contentType(\"application/json;charset=ISO-8859-1\"));\n    }\n\n    @Test\n    void handleExtensionCall_should_use_custom_media_type_with_default_charset() throws Exception {\n        var restApiResponse = new RestApiResponseBuilder()\n                .withMediaType(\"text/plain\")\n                .withResponse(\"hello\")\n                .build();\n        when(restApiRenderer.handleRestApiCall(any(), any())).thenReturn(restApiResponse);\n\n        mockMvc.perform(get(API_URL)\n                .sessionAttrs(sessionAttributes))\n                .andExpect(status().isOk())\n                .andExpect(content().contentType(\"text/plain;charset=UTF-8\"));\n    }\n\n    @Test\n    void handleExtensionCall_should_not_append_charset_when_already_present_in_media_type() throws Exception {\n        var restApiResponse = new RestApiResponseBuilder()\n                .withMediaType(\"application/json;charset=ISO-8859-1\")\n                .withResponse(\"{}\")\n                .build();\n        when(restApiRenderer.handleRestApiCall(any(), any())).thenReturn(restApiResponse);\n\n        mockMvc.perform(get(API_URL)\n                .sessionAttrs(sessionAttributes))\n                .andExpect(status().isOk())\n                .andExpect(content().contentType(\"application/json;charset=ISO-8859-1\"));\n    }\n\n    @Test\n    void handleExtensionCall_should_fallback_to_utf8_when_charset_is_unsupported() throws Exception {\n        var restApiResponse = new RestApiResponseBuilder()\n                .withCharacterSet(\"UNSUPPORTED-CHARSET\")\n                .withResponse(\"{}\")\n                .build();\n        when(restApiRenderer.handleRestApiCall(any(), any())).thenReturn(restApiResponse);\n\n        mockMvc.perform(get(API_URL)\n                .sessionAttrs(sessionAttributes))\n                .andExpect(status().isOk())\n                .andExpect(content().contentType(\"application/json;charset=UTF-8\"));\n    }\n\n    @Test\n    void handleExtensionCall_should_handle_portal_custom_page_extension_url() throws Exception {\n        var restApiResponse = new RestApiResponseBuilder().withResponse(\"{'key':'value'}\").build();\n        when(restApiRenderer.handleRestApiCall(any(), any())).thenReturn(restApiResponse);\n\n        mockMvc.perform(get(\"/portal/custom-page/API/extension/myResource\")\n                .sessionAttrs(sessionAttributes))\n                .andExpect(status().isOk())\n                .andExpect(content().string(\"{'key':'value'}\"));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/extension/ResourceExtensionResolverTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.extension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.*;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.net.URL;\nimport java.util.Locale;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport org.bonitasoft.console.common.server.page.PageMappingService;\nimport org.bonitasoft.console.common.server.page.PageReference;\nimport org.bonitasoft.console.common.server.page.extension.PageResourceProviderImpl;\nimport org.bonitasoft.engine.exception.NotFoundException;\nimport org.bonitasoft.engine.session.APISession;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport org.mockito.junit.jupiter.MockitoSettings;\nimport org.mockito.quality.Strictness;\n\n@ExtendWith(MockitoExtension.class)\n@MockitoSettings(strictness = Strictness.LENIENT)\nclass ResourceExtensionResolverTest {\n\n    public static final String API_EXTENSION_POST_MAPPING_KEY = \"apiExtension|POST|myPostResource\";\n    public static final String API_EXTENSION_GET_MAPPING_KEY = \"apiExtension|GET|helloWorld\";\n\n    public static final long PAGE_ID = 2L;\n\n    @Mock\n    private PageResourceProviderImpl pageResourceProvider;\n\n    @Mock\n    private APISession apiSession;\n\n    @Mock\n    private PageMappingService pageMappingService;\n\n    @Mock\n    HttpServletRequest httpServletRequest;\n\n    @Mock\n    private PageReference pageReference;\n\n    @BeforeEach\n    void before() throws Exception {\n        final URL resource = ResourceExtensionResolverTest.class.getResource(\"page.properties\");\n        doReturn(new FileInputStream(new File(resource.toURI()))).when(pageResourceProvider)\n                .getResourceAsStream(\"page.properties\");\n    }\n\n    @Test\n    void should_post_resolve_class_file_name() throws Exception {\n        //given\n        var resourceExtensionResolver = createResolver(\"POST\", \"/bonita/API/extension/myPostResource\");\n\n        //when\n        final ControllerClassName controllerClassName = resourceExtensionResolver\n                .resolveRestApiControllerClassName(pageResourceProvider);\n\n        //then\n        assertThat(controllerClassName.isSource()).isTrue();\n        assertThat(controllerClassName.getName()).isEqualTo(\"PostResource.groovy\");\n    }\n\n    @Test\n    void should_get_resolve_class_file_name() throws Exception {\n        //given\n        var resourceExtensionResolver = createResolver(\"GET\", \"/bonita/API/extension/helloWorld\");\n\n        //when\n        final ControllerClassName restApiControllerClassName = resourceExtensionResolver\n                .resolveRestApiControllerClassName(pageResourceProvider);\n\n        //then\n        assertThat(restApiControllerClassName.isSource()).isTrue();\n        assertThat(restApiControllerClassName.getName()).isEqualTo(\"Index.groovy\");\n    }\n\n    @Test\n    void should_get_resolve_class_name() throws Exception {\n        //given\n        var resourceExtensionResolver = createResolver(\"GET\", \"/bonita/API/extension/myCompiledRestApi\");\n\n        //when\n        final ControllerClassName restApiControllerClassName = resourceExtensionResolver\n                .resolveRestApiControllerClassName(pageResourceProvider);\n\n        //then\n        assertThat(restApiControllerClassName.isSource()).isFalse();\n        assertThat(restApiControllerClassName.getName()).isEqualTo(\"com.company.MyController\");\n    }\n\n    @Test\n    void should_get_resolve_class_name_over_class_file_name_when_both_are_present() throws Exception {\n        //given\n        var resourceExtensionResolver = createResolver(\"GET\", \"/bonita/API/extension/myCompiledRestApi2\");\n\n        //when\n        final ControllerClassName restApiControllerClassName = resourceExtensionResolver\n                .resolveRestApiControllerClassName(pageResourceProvider);\n\n        //then\n        assertThat(restApiControllerClassName.isSource()).isFalse();\n        assertThat(restApiControllerClassName.getName()).isEqualTo(\"com.company.MyController\");\n    }\n\n    @Test\n    void should_resolve_class_file_name_when_name_begins_the_same_than_another() throws Exception {\n        // two api extensions with names that begins the same are installed (myPostResource and myPostResourceB)\n        // see https://bonitasoft.atlassian.net/browse/BS-16791\n        var resourceExtensionResolver = createResolver(\"POST\", \"/bonita/API/extension/myPostResourceB\");\n\n        final ControllerClassName controllerClassName = resourceExtensionResolver\n                .resolveRestApiControllerClassName(pageResourceProvider);\n\n        assertThat(controllerClassName.isSource()).isTrue();\n        assertThat(controllerClassName.getName()).isEqualTo(\"PostResourceB.groovy\");\n    }\n\n    @Test\n    void should_not_resolve_class_file_name() {\n        //given\n        var resourceExtensionResolver = createResolver(\"POST\", \"/bonita/API/extension/notResource\");\n\n        //when then exception\n        assertThatThrownBy(() -> resourceExtensionResolver.resolveRestApiControllerClassName(pageResourceProvider))\n                .isInstanceOf(NotFoundException.class)\n                .hasMessageContaining(\"error while getting resource:apiExtension|POST|notResource\");\n    }\n\n    @Test\n    void should_generate_mapping_key() {\n        //given\n        var resourceExtensionResolver = createResolver(\"POST\", \"/bonita/API/extension/myPostResource\");\n\n        //when\n        final String mappingKey = resourceExtensionResolver.generateMappingKey();\n\n        //then\n        assertThat(mappingKey).isEqualTo(API_EXTENSION_POST_MAPPING_KEY);\n    }\n\n    @Test\n    void should_resolve_pageId() throws Exception {\n        //given\n        doReturn(pageReference).when(pageMappingService).getPage(httpServletRequest, apiSession,\n                API_EXTENSION_POST_MAPPING_KEY, Locale.FRENCH, false);\n        doReturn(PAGE_ID).when(pageReference).getPageId();\n\n        var resourceExtensionResolver = createResolver(\"POST\", \"/bonita/API/extension/myPostResource\");\n\n        //when\n        final Long pageId = resourceExtensionResolver.resolvePageId(apiSession);\n\n        //then\n        verify(pageMappingService).getPage(any(HttpServletRequest.class), eq(apiSession),\n                eq(API_EXTENSION_POST_MAPPING_KEY),\n                any(Locale.class), eq(false));\n        assertThat(pageId).isEqualTo(PAGE_ID);\n    }\n\n    @Test\n    void should_resolve_pageId_with_parameters() throws Exception {\n        //given\n        doReturn(pageReference).when(pageMappingService).getPage(httpServletRequest, apiSession,\n                API_EXTENSION_GET_MAPPING_KEY, Locale.FRENCH, false);\n        doReturn(PAGE_ID).when(pageReference).getPageId();\n\n        var resourceExtensionResolver = createResolver(\"GET\", \"/bonita/API/extension/helloWorld\");\n\n        //when\n        final Long pageId = resourceExtensionResolver.resolvePageId(apiSession);\n\n        //then\n        verify(pageMappingService).getPage(any(HttpServletRequest.class), eq(apiSession),\n                eq(API_EXTENSION_GET_MAPPING_KEY),\n                any(Locale.class), eq(false));\n        assertThat(pageId).isEqualTo(PAGE_ID);\n    }\n\n    @Test\n    void should_mapping_key_exclude_parameters() {\n        //given\n        var resourceExtensionResolver = createResolver(\"GET\", \"/bonita/API/extension/helloWorld\");\n\n        //when\n        final String mappingKey = resourceExtensionResolver.generateMappingKey();\n\n        //then\n        assertThat(mappingKey).isEqualTo(API_EXTENSION_GET_MAPPING_KEY);\n    }\n\n    @Test\n    void should_unresolved_pageId_throw_exception() throws Exception {\n        //given\n        final NotFoundException notFoundException = new NotFoundException(\"page not found\");\n        doThrow(notFoundException).when(pageMappingService).getPage(httpServletRequest, apiSession,\n                API_EXTENSION_POST_MAPPING_KEY, Locale.FRENCH, false);\n\n        var resourceExtensionResolver = createResolver(\"POST\", \"/bonita/API/extension/myPostResource\");\n\n        //when then exception\n        assertThatThrownBy(() -> resourceExtensionResolver.resolvePageId(apiSession))\n                .isInstanceOf(NotFoundException.class);\n    }\n\n    private ResourceExtensionResolver createResolver(String httpMethod, String uri) {\n        doReturn(uri).when(httpServletRequest).getRequestURI();\n        doReturn(Locale.FRENCH).when(httpServletRequest).getLocale();\n\n        return new ResourceExtensionResolver(httpServletRequest, httpMethod, pageMappingService);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/form/FormMappingControllerTest.java",
    "content": "/**\n * Copyright (C) 2025 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.form;\n\nimport static java.util.Collections.nCopies;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.tuple;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.form.FormMapping;\nimport org.bonitasoft.engine.form.FormMappingType;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.search.impl.SearchFilter;\nimport org.bonitasoft.web.rest.server.api.AbstractControllerTest;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mock;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\n\nclass FormMappingControllerTest extends AbstractControllerTest<FormMappingController> {\n\n    @Mock\n    protected ProcessAPI processAPI;\n\n    @Override\n    protected FormMappingController createController() {\n        return spy(new FormMappingController());\n    }\n\n    @Override\n    protected void configureMocks(FormMappingController controller) throws Exception {\n        doReturn(processAPI).when(controller).getProcessAPI(apiSession);\n    }\n\n    @Test\n    void should_return_content_range_header() throws Exception {\n        SearchResult<FormMapping> searchResult = mock(SearchResult.class);\n        FormMapping formMapping = mock(FormMapping.class);\n        List<FormMapping> formMappings = new ArrayList<>();\n        formMappings.add(formMapping);\n        doReturn(formMappings).when(searchResult).getResult();\n        doReturn(21L).when(searchResult).getCount();\n        doReturn(searchResult).when(processAPI).searchFormMappings(any(SearchOptions.class));\n\n        mockMvc.perform(get(\"/API/form/mapping\")\n                .sessionAttrs(sessionAttributes)\n                .param(\"p\", \"2\")\n                .param(\"c\", \"10\")\n                .param(\"f\", \"type=TASK\")\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(header().string(HttpHeaders.CONTENT_RANGE, \"2-1/21\"));\n    }\n\n    @Test\n    void should_return_form_mapping_data() throws Exception {\n        SearchResult<FormMapping> searchResult = mock(SearchResult.class);\n        FormMapping formMapping = new FormMapping();\n        formMapping.setTask(\"myTask\");\n        List<FormMapping> formMappings = new ArrayList<>();\n        formMappings.add(formMapping);\n        doReturn(formMappings).when(searchResult).getResult();\n        doReturn(1L).when(searchResult).getCount();\n        doReturn(searchResult).when(processAPI).searchFormMappings(any(SearchOptions.class));\n\n        mockMvc.perform(get(\"/API/form/mapping\")\n                .sessionAttrs(sessionAttributes)\n                .param(\"p\", \"0\")\n                .param(\"c\", \"10\")\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(jsonPath(\"$[0].task\").value(\"myTask\"));\n    }\n\n    @Test\n    void should_return_long_ids_as_strings() throws Exception {\n        SearchResult<FormMapping> searchResult = mock(SearchResult.class);\n        FormMapping formMapping = new FormMapping();\n        formMapping.setId(11125555888888L);\n        formMapping.setTask(\"myTask\");\n        formMapping.setProcessDefinitionId(4871148324840256385L);\n        formMapping.setPageId(1L);\n        formMapping.setLastUpdatedBy(1L);\n        List<FormMapping> formMappings = new ArrayList<>();\n        formMappings.add(formMapping);\n        doReturn(formMappings).when(searchResult).getResult();\n        doReturn(1L).when(searchResult).getCount();\n        doReturn(searchResult).when(processAPI).searchFormMappings(any(SearchOptions.class));\n\n        mockMvc.perform(get(\"/API/form/mapping\")\n                .sessionAttrs(sessionAttributes)\n                .param(\"p\", \"0\")\n                .param(\"c\", \"10\")\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(jsonPath(\"$[0].id\").value(\"11125555888888\"))\n                .andExpect(jsonPath(\"$[0].processDefinitionId\").value(\"4871148324840256385\"))\n                .andExpect(jsonPath(\"$[0].pageId\").value(\"1\"))\n                .andExpect(jsonPath(\"$[0].lastUpdatedBy\").value(\"1\"));\n    }\n\n    @Test\n    void should_use_default_pagination_parameters() throws Exception {\n        SearchResult<FormMapping> searchResult = mock(SearchResult.class);\n        doReturn(nCopies(10, mock(FormMapping.class))).when(searchResult).getResult();\n        doReturn(11L).when(searchResult).getCount();\n        doReturn(searchResult).when(processAPI).searchFormMappings(any(SearchOptions.class));\n\n        mockMvc.perform(get(\"/API/form/mapping\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(header().string(HttpHeaders.CONTENT_RANGE, \"0-10/11\"));\n    }\n\n    @Test\n    void should_pass_correct_search_options() throws Exception {\n        SearchResult<FormMapping> searchResult = mock(SearchResult.class);\n        doReturn(List.of()).when(searchResult).getResult();\n        doReturn(0L).when(searchResult).getCount();\n        ArgumentCaptor<SearchOptions> captor = ArgumentCaptor.forClass(SearchOptions.class);\n        doReturn(searchResult).when(processAPI).searchFormMappings(captor.capture());\n\n        mockMvc.perform(get(\"/API/form/mapping\")\n                .sessionAttrs(sessionAttributes)\n                .param(\"p\", \"2\")\n                .param(\"c\", \"10\")\n                .param(\"s\", \"mySearch\")\n                .param(\"o\", \"id ASC\")\n                .param(\"f\", \"type=TASK\")\n                .param(\"f\", \"processDefinitionId=12345\")\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk());\n\n        SearchOptions options = captor.getValue();\n        assertThat(options.getStartIndex()).isEqualTo(20); // page 2 * count 10\n        assertThat(options.getMaxResults()).isEqualTo(10);\n        assertThat(options.getSearchTerm()).isEqualTo(\"mySearch\");\n        assertThat(options.getSorts()).hasSize(1);\n        assertThat(options.getSorts().get(0).getField()).isEqualTo(\"id\");\n        assertThat(options.getSorts().get(0).getOrder()).isEqualTo(Order.ASC);\n        assertThat(options.getFilters())\n                .hasSize(2)\n                .extracting(SearchFilter::getField, SearchFilter::getValue)\n                .containsExactlyInAnyOrder(\n                        tuple(\"type\", FormMappingType.TASK),\n                        tuple(\"processDefinitionId\", \"12345\"));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/form/FormMappingItemTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.form;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.when;\n\nimport org.bonitasoft.engine.form.FormMapping;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class FormMappingItemTest {\n\n    @Mock\n    FormMapping formMapping;\n\n    @Test\n    public void it_should_handle_a_null_pageId() {\n        //given\n        when(formMapping.getPageId()).thenReturn(null);\n\n        //when\n        FormMappingItem formMappingItem = new FormMappingItem(formMapping);\n\n        assertThat(formMappingItem.getPageId()).isEqualTo(null);\n    }\n\n    @Test\n    public void it_should_properly_handle_long_values() {\n        //given\n        when(formMapping.getId()).thenReturn(1L);\n        when(formMapping.getProcessDefinitionId()).thenReturn(885556662223334788L);\n        when(formMapping.getLastUpdatedBy()).thenReturn(2L);\n        when(formMapping.getPageId()).thenReturn(2L);\n\n        //when\n        FormMappingItem formMappingItem = new FormMappingItem(formMapping);\n\n        assertThat(formMappingItem.getId()).isEqualTo(\"1\");\n        assertThat(formMappingItem.getProcessDefinitionId()).isEqualTo(\"885556662223334788\");\n        assertThat(formMappingItem.getLastUpdatedBy()).isEqualTo(\"2\");\n        assertThat(formMappingItem.getPageId()).isEqualTo(\"2\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/organization/APICustomUserInfoDefinitionTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.organization;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.engine.identity.CustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.CustomUserInfoDefinitionCreator;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.ModelFactory;\nimport org.bonitasoft.web.rest.model.identity.CustomUserInfoDefinitionItem;\nimport org.bonitasoft.web.rest.server.engineclient.CustomUserInfoEngineClient;\nimport org.bonitasoft.web.rest.server.engineclient.CustomUserInfoEngineClientCreator;\nimport org.bonitasoft.web.rest.server.framework.APIServletCall;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.ItemDefinitionFactory;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Answers;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Vincent Elcrin\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class APICustomUserInfoDefinitionTest {\n\n    @Mock(answer = Answers.RETURNS_MOCKS)\n    private APIServletCall caller;\n\n    @Mock\n    private HttpSession httpSession;\n\n    @Mock\n    private APISession apiSession;\n\n    @Mock\n    private CustomUserInfoEngineClient engine;\n\n    @Mock(answer = Answers.RETURNS_MOCKS)\n    private CustomUserInfoEngineClientCreator engineClientCreator;\n\n    private APICustomUserInfoDefinition api;\n\n    @Before\n    public void setUp() throws Exception {\n        given(caller.getHttpSession()).willReturn(httpSession);\n        given(httpSession.getAttribute(\"apiSession\")).willReturn(apiSession);\n        given(engineClientCreator.create(apiSession)).willReturn(engine);\n        ItemDefinitionFactory.setDefaultFactory(new ModelFactory());\n        api = new APICustomUserInfoDefinition(engineClientCreator);\n        api.setCaller(caller);\n    }\n\n    @Test\n    public void add_should_return_the_added_item() throws Exception {\n        given(engine.createDefinition(any(CustomUserInfoDefinitionCreator.class)))\n                .willReturn(new EngineCustomUserInfoDefinition(2L));\n\n        CustomUserInfoDefinitionItem added = api.add(new CustomUserInfoDefinitionItem());\n\n        assertThat(added.getId()).isEqualTo(APIID.makeAPIID(2L));\n    }\n\n    @Test\n    public void add_should_create_an_item_based_on_item_passed_in_parameter() throws Exception {\n        given(engine.createDefinition(any(CustomUserInfoDefinitionCreator.class)))\n                .willReturn(new EngineCustomUserInfoDefinition(1L));\n        ArgumentCaptor<CustomUserInfoDefinitionCreator> argument = ArgumentCaptor\n                .forClass(CustomUserInfoDefinitionCreator.class);\n        CustomUserInfoDefinitionItem item = new CustomUserInfoDefinitionItem();\n        item.setName(\"foo\");\n        item.setDescription(\"bar\");\n\n        api.add(item);\n        verify(engine).createDefinition(argument.capture());\n\n        assertThat(argument.getValue().getName()).isEqualTo(\"foo\");\n        assertThat(argument.getValue().getDescription()).isEqualTo(\"bar\");\n    }\n\n    @Test\n    public void delete_should_delete_all_items_with_id_passed_through() throws Exception {\n        ArgumentCaptor<Long> argument = ArgumentCaptor.forClass(Long.class);\n\n        api.delete(Arrays.asList(\n                APIID.makeAPIID(1L),\n                APIID.makeAPIID(2L)));\n        verify(engine, times(2)).deleteDefinition(argument.capture());\n\n        assertThat(argument.getAllValues()).containsOnly(1L, 2L);\n    }\n\n    @Test\n    public void search_should_retrieve_the_list_of_item_from_the_specified_range() throws Exception {\n        given(engine.listDefinitions(4, 2)).willReturn(Arrays.<CustomUserInfoDefinition> asList(\n                new EngineCustomUserInfoDefinition(3L),\n                new EngineCustomUserInfoDefinition(4L)));\n        given(engine.countDefinitions()).willReturn(6L);\n\n        List<CustomUserInfoDefinitionItem> result = api.search(2, 2,\n                null,\n                APICustomUserInfoDefinition.FIX_ORDER,\n                null).getResults();\n\n        assertThat(result.get(0).getId()).isEqualTo(APIID.makeAPIID(3L));\n        assertThat(result.get(1).getId()).isEqualTo(APIID.makeAPIID(4L));\n    }\n\n    @Test\n    public void search_should_retrieve_the_total_number_of_definitions() throws Exception {\n        given(engine.countDefinitions()).willReturn(42L);\n\n        ItemSearchResult<CustomUserInfoDefinitionItem> result = api.search(0, 2,\n                null,\n                APICustomUserInfoDefinition.FIX_ORDER,\n                null);\n\n        assertThat(result.getTotal()).isEqualTo(42);\n    }\n\n    @Test\n    public void should_allow_an_empty_filter() throws Exception {\n        given(engine.countDefinitions()).willReturn(42L);\n\n        ItemSearchResult<CustomUserInfoDefinitionItem> result = api.search(0, 2,\n                null,\n                APICustomUserInfoDefinition.FIX_ORDER,\n                Collections.<String, String> emptyMap());\n\n        assertThat(result.getTotal()).isEqualTo(42);\n    }\n\n    @Test(expected = APIException.class)\n    public void search_should_throw_an_exception_when_filters_are_passed_through() throws Exception {\n        api.search(0, 1,\n                null,\n                APICustomUserInfoDefinition.FIX_ORDER,\n                Collections.singletonMap(\"name\", \"foo\"));\n    }\n\n    @Test(expected = APIException.class)\n    public void search_should_throw_an_exception_when_an_order_is_passed_through() throws Exception {\n        api.search(0, 1, null, \"NAME ASC\", null);\n    }\n\n    @Test(expected = APIException.class)\n    public void search_should_throw_an_exception_when_a_search_is_passed_through() throws Exception {\n        api.search(0, 1, \"foo\", APICustomUserInfoDefinition.FIX_ORDER, null);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/organization/APICustomUserInfoUserTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.organization;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verify;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.engine.identity.CustomUserInfo;\nimport org.bonitasoft.engine.identity.impl.CustomUserInfoValueImpl;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.ModelFactory;\nimport org.bonitasoft.web.rest.model.identity.CustomUserInfoItem;\nimport org.bonitasoft.web.rest.server.engineclient.CustomUserInfoEngineClient;\nimport org.bonitasoft.web.rest.server.engineclient.CustomUserInfoEngineClientCreator;\nimport org.bonitasoft.web.rest.server.framework.APIServletCall;\nimport org.bonitasoft.web.toolkit.client.ItemDefinitionFactory;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Answers;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Vincent Elcrin\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class APICustomUserInfoUserTest {\n\n    @Mock(answer = Answers.RETURNS_MOCKS)\n    private APIServletCall caller;\n\n    @Mock\n    private HttpSession httpSession;\n\n    @Mock\n    private APISession apiSession;\n\n    @Mock\n    private CustomUserInfoEngineClient engine;\n\n    @Mock(answer = Answers.RETURNS_MOCKS)\n    private CustomUserInfoEngineClientCreator engineClientCreator;\n\n    private APICustomUserInfoUser api;\n\n    @Before\n    public void setUp() throws Exception {\n        ItemDefinitionFactory.setDefaultFactory(new ModelFactory());\n        api = new APICustomUserInfoUser(engineClientCreator);\n        api.setCaller(caller);\n        given(caller.getHttpSession()).willReturn(httpSession);\n        given(httpSession.getAttribute(\"apiSession\")).willReturn(apiSession);\n        given(engineClientCreator.create(apiSession)).willReturn(engine);\n    }\n\n    @Test\n    public void should_retrieve_the_CustomUserInfo_for_a_given_user_id() throws Exception {\n        given(engine.listCustomInformation(3L, 0, 2)).willReturn(Arrays.asList(\n                new CustomUserInfo(3L, new EngineCustomUserInfoDefinition(5L), new CustomUserInfoValueImpl()),\n                new CustomUserInfo(3L, new EngineCustomUserInfoDefinition(6L), new CustomUserInfoValueImpl())));\n        given(engine.countDefinitions()).willReturn(2L);\n\n        List<CustomUserInfoItem> information = api\n                .search(0, 2, null, \"Fix order\", Collections.singletonMap(\"userId\", \"3\")).getResults();\n\n        assertThat(information.get(0).getDefinition().getId()).isEqualTo(APIID.makeAPIID(5L));\n        assertThat(information.get(1).getDefinition().getId()).isEqualTo(APIID.makeAPIID(6L));\n    }\n\n    @Test\n    public void should_paginate_CustomUserInfo_search() throws Exception {\n        given(engine.countDefinitions()).willReturn(2L);\n\n        api.search(2, 2, null, \"Fix order\", Collections.singletonMap(\"userId\", \"3\")).getResults();\n\n        verify(engine).listCustomInformation(3L, 4, 2);\n    }\n\n    @Test(expected = APIException.class)\n    public void should_fail_when_passing_an_order_to_the_search() {\n        api.search(0, 2, null, \"NAME ASC\", Collections.singletonMap(\"userId\", \"3\")).getResults();\n    }\n\n    @Test(expected = APIException.class)\n    public void should_fail_when_passing_a_term_to_the_search() {\n        api.search(0, 2, \"foo\", \"Fix order\", Collections.singletonMap(\"userId\", \"3\")).getResults();\n    }\n\n    @Test(expected = APIException.class)\n    public void should_fail_when_passing_a_no_filter_to_the_search() {\n        api.search(0, 2, null, \"Fix order\", null).getResults();\n    }\n\n    @Test(expected = APIException.class)\n    public void should_fail_when_passing_wrong_filter_to_the_search() {\n        api.search(0, 2, null, \"Fix order\", Collections.singletonMap(\"foo\", \"bar\")).getResults();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/organization/APICustomUserInfoValueTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.organization;\n\nimport static java.util.Collections.emptyList;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verify;\n\nimport java.util.Arrays;\nimport java.util.Collections;\n\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.engine.identity.impl.CustomUserInfoValueImpl;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.impl.SearchResultImpl;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.ModelFactory;\nimport org.bonitasoft.web.rest.model.identity.CustomUserInfoItem;\nimport org.bonitasoft.web.rest.server.engineclient.CustomUserInfoEngineClient;\nimport org.bonitasoft.web.rest.server.engineclient.CustomUserInfoEngineClientCreator;\nimport org.bonitasoft.web.rest.server.framework.APIServletCall;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.ItemDefinitionFactory;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Answers;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Vincent Elcrin\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class APICustomUserInfoValueTest {\n\n    static {\n        ItemDefinitionFactory.setDefaultFactory(new ModelFactory());\n    }\n\n    @Mock(answer = Answers.RETURNS_MOCKS)\n    private APIServletCall caller;\n\n    @Mock\n    private HttpSession httpSession;\n\n    @Mock\n    private APISession apiSession;\n\n    @Mock\n    private CustomUserInfoEngineClient engine;\n\n    @Mock(answer = Answers.RETURNS_MOCKS)\n    private CustomUserInfoEngineClientCreator engineClientCreator;\n\n    @InjectMocks\n    private APICustomUserInfoValue api;\n\n    @Before\n    public void setUp() throws Exception {\n        api.setCaller(caller);\n        given(caller.getHttpSession()).willReturn(httpSession);\n        given(httpSession.getAttribute(\"apiSession\")).willReturn(apiSession);\n        given(engineClientCreator.create(apiSession)).willReturn(engine);\n    }\n\n    @Test\n    public void should_retrieve_custom_user_info() {\n        given(engine.searchCustomUserInfoValues(any(SearchOptions.class))).willReturn(\n                new SearchResultImpl<>(3, Arrays.asList(\n                        createValue(\"foo\"),\n                        createValue(\"bar\"))));\n\n        ItemSearchResult<CustomUserInfoItem> result = api.search(0, 2, null, null, Collections.emptyMap());\n\n        assertThat(result.getPage()).isEqualTo(0);\n        assertThat(result.getTotal()).isEqualTo(3);\n        assertThat(result.getLength()).isEqualTo(2);\n        assertThat(result.getResults().get(0).getValue()).isEqualTo(\"foo\");\n        assertThat(result.getResults().get(1).getValue()).isEqualTo(\"bar\");\n    }\n\n    private CustomUserInfoValueImpl createValue(String value) {\n        CustomUserInfoValueImpl information = new CustomUserInfoValueImpl();\n        information.setValue(value);\n        return information;\n    }\n\n    @Test\n    public void should_retrieve_custom_user_info_sorted() {\n        given(engine.searchCustomUserInfoValues(any(SearchOptions.class))).willReturn(\n                new SearchResultImpl<>(0, emptyList()));\n        ArgumentCaptor<SearchOptions> argument = ArgumentCaptor.forClass(SearchOptions.class);\n\n        api.search(0, 2, null, \"userId ASC\", Collections.emptyMap());\n\n        verify(engine).searchCustomUserInfoValues(argument.capture());\n        assertThat(argument.getValue().getSorts().get(0).getField()).isEqualTo(\"userId\");\n        assertThat(argument.getValue().getSorts().get(0).getOrder()).isEqualTo(Order.ASC);\n    }\n\n    @Test\n    public void should_retrieve_custom_user_info_filtered() {\n        given(engine.searchCustomUserInfoValues(any(SearchOptions.class))).willReturn(\n                new SearchResultImpl<>(0, emptyList()));\n        ArgumentCaptor<SearchOptions> argument = ArgumentCaptor.forClass(SearchOptions.class);\n\n        api.search(0, 2, null, null, Collections.singletonMap(CustomUserInfoItem.ATTRIBUTE_VALUE, \"bar\"));\n\n        verify(engine).searchCustomUserInfoValues(argument.capture());\n        assertThat(argument.getValue().getFilters().get(0).getField()).isEqualTo(CustomUserInfoItem.ATTRIBUTE_VALUE);\n        assertThat(argument.getValue().getFilters().get(0).getValue()).isEqualTo(\"bar\");\n    }\n\n    @Test\n    public void should_retrieve_custom_user_info_term_filtered() {\n        given(engine.searchCustomUserInfoValues(any(SearchOptions.class))).willReturn(\n                new SearchResultImpl<>(0, emptyList()));\n        ArgumentCaptor<SearchOptions> argument = ArgumentCaptor.forClass(SearchOptions.class);\n\n        api.search(0, 2, \"foo\", null, Collections.emptyMap());\n\n        verify(engine).searchCustomUserInfoValues(argument.capture());\n        assertThat(argument.getValue().getSearchTerm()).isEqualTo(\"foo\");\n    }\n\n    @Test\n    public void should_update_a_given_custom_item_value() {\n        CustomUserInfoValueImpl update = new CustomUserInfoValueImpl();\n        update.setValue(\"foo\");\n        given(engine.setCustomUserInfoValue(1L, 2L, \"foo\")).willReturn(update);\n\n        CustomUserInfoItem value = api.update(APIID.makeAPIID(2L, 1L), Collections.singletonMap(\"value\", \"foo\"));\n\n        assertThat(value.getValue()).isEqualTo(\"foo\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/organization/APIUserTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.organization;\n\nimport static org.mockito.Mockito.*;\n\nimport java.util.Collections;\nimport java.util.HashMap;\n\nimport org.bonitasoft.console.common.server.i18n.I18n;\nimport org.bonitasoft.web.rest.model.identity.UserItem;\nimport org.bonitasoft.web.rest.server.datastore.organization.UserDatastore;\nimport org.bonitasoft.web.rest.server.framework.APIServletCall;\nimport org.bonitasoft.web.toolkit.client.ItemDefinitionFactory;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ValidationException;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.rules.TemporaryFolder;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class APIUserTest {\n\n    public static final APIID USER_ID = APIID.makeAPIID(123L);\n    @Mock\n    private UserItem userItem;\n    private APIUser apiUser;\n    @Mock\n    private UserDatastore userDatastore;\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Rule\n    public TemporaryFolder temporaryFolder = new TemporaryFolder();\n\n    @Before\n    public void before() throws Exception {\n        ItemDefinitionFactory.setDefaultFactory(mock(ItemDefinitionFactory.class));\n        apiUser = spy(new APIUser());\n        doReturn(userDatastore).when(apiUser).getDefaultDatastore();\n        doReturn(TestValidator.class.getName()).when(apiUser).getValidatorClassName();\n        I18n.getInstance();\n        APIServletCall caller = mock(APIServletCall.class);\n        apiUser.setCaller(caller);\n        doReturn(\"en_US\").when(caller).getLocale();\n    }\n\n    @Test\n    public void should_user_be_updated_with_icon_as_submit_by_the_API() throws Exception {\n        //when\n        apiUser.update(USER_ID, map(UserItem.ATTRIBUTE_ICON, \"theAvatar.jpg\"));\n        //then\n        verify(userDatastore).update(eq(USER_ID), eq(map(UserItem.ATTRIBUTE_ICON, \"theAvatar.jpg\")));\n    }\n\n    private HashMap<String, String> map(String key, String value) {\n        HashMap<String, String> item = new HashMap<>();\n        item.put(key, value);\n        return item;\n    }\n\n    @Test\n    public void should_not_update_password_if_empty() throws Exception {\n        apiUser.update(USER_ID, map(UserItem.ATTRIBUTE_PASSWORD, \"\"));\n\n        verify(userDatastore).update(eq(USER_ID), eq(Collections.<String, String> emptyMap()));\n    }\n\n    @Test\n    public void should_check_password_robustness_on_update() throws Exception {\n        expectedException.expect(ValidationException.class);\n        //see TestValidator class\n        expectedException.expectMessage(\"the validator TestValidator rejected this password\");\n\n        apiUser.update(USER_ID,\n                map(UserItem.ATTRIBUTE_PASSWORD, \"this password is not accepted by the TestValidator validator\"));\n    }\n\n    @Test\n    public void should_update_password_if_valid() throws Exception {\n        apiUser.update(USER_ID, map(UserItem.ATTRIBUTE_PASSWORD, \"accepted password\"));\n\n        verify(userDatastore).update(eq(USER_ID), eq(map(UserItem.ATTRIBUTE_PASSWORD, \"accepted password\")));\n    }\n\n    @Test\n    public void should_check_password_robustness_on_add() throws Exception {\n        UserItem userItem = new UserItem();\n        userItem.setUserName(\"John\");\n        userItem.setPassword(\"this password is not accepted by the TestValidator validator\");\n\n        expectedException.expect(ValidationException.class);\n        //see TestValidator class\n        expectedException.expectMessage(\"the validator TestValidator rejected this password\");\n        apiUser.add(userItem);\n    }\n\n    @Test\n    public void should_throw_exception_when_adding_a_user_with_no_password() throws Exception {\n        UserItem userItem = new UserItem();\n        userItem.setUserName(\"John\");\n\n        expectedException.expect(ValidationException.class);\n        apiUser.add(userItem);\n    }\n\n    @Test\n    public void should_add_user_if_password_is_valid() throws Exception {\n        UserItem userItem = new UserItem();\n        userItem.setUserName(\"John\");\n        userItem.setPassword(\"accepted password\");\n\n        apiUser.add(userItem);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/organization/CustomUserInfoConverterTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.organization;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\n\nimport org.bonitasoft.engine.identity.CustomUserInfo;\nimport org.bonitasoft.engine.identity.CustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.impl.CustomUserInfoValueImpl;\nimport org.bonitasoft.web.rest.model.identity.CustomUserInfoDefinitionItem;\nimport org.bonitasoft.web.rest.model.identity.CustomUserInfoItem;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.Test;\n\n/**\n * @author Vincent Elcrin\n */\npublic class CustomUserInfoConverterTest {\n\n    private CustomUserInfoConverter converter = new CustomUserInfoConverter();\n\n    @Test\n    public void should_return_a_fully_configured_definition() throws Exception {\n        CustomUserInfoDefinition dummy = new EngineCustomUserInfoDefinition(1L, \"foo\", \"bar\");\n\n        CustomUserInfoDefinitionItem definition = converter.convert(dummy);\n\n        assertThat(definition.getAttributes()).containsOnly(\n                entry(\"id\", \"1\"),\n                entry(\"name\", \"foo\"),\n                entry(\"description\", \"bar\"));\n    }\n\n    @Test\n    public void should_return_a_fully_configured_custom_information() throws Exception {\n        CustomUserInfoDefinition definition = new EngineCustomUserInfoDefinition(3L);\n        CustomUserInfoValueImpl value = new CustomUserInfoValueImpl();\n        value.setValue(\"foo\");\n\n        CustomUserInfoItem information = converter.convert(new CustomUserInfo(2L, definition, value));\n\n        assertThat(information.getAttributes()).containsOnly(\n                entry(\"userId\", \"2\"),\n                entry(\"definitionId\", \"3\"),\n                entry(\"value\", \"foo\"));\n        assertThat(information.getDefinition().getId()).isEqualTo(APIID.makeAPIID(3L));\n    }\n\n    @Test\n    public void should_return_a_fully_configured_custom_information_form_a_value() throws Exception {\n        CustomUserInfoValueImpl value = new CustomUserInfoValueImpl();\n        value.setUserId(5);\n        value.setDefinitionId(6);\n        value.setValue(\"foo\");\n\n        CustomUserInfoItem information = converter.convert(value);\n\n        assertThat(information.getAttributes()).containsOnly(\n                entry(\"userId\", \"5\"),\n                entry(\"definitionId\", \"6\"),\n                entry(\"value\", \"foo\"));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/organization/EngineCustomUserInfoDefinition.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.organization;\n\nimport org.bonitasoft.engine.identity.CustomUserInfoDefinition;\n\n/**\n * @author Vincent Elcrin\n */\npublic class EngineCustomUserInfoDefinition implements CustomUserInfoDefinition {\n\n    private static final long serialVersionUID = -6151441522473783097L;\n\n    private long id;\n\n    private String name;\n\n    private String description;\n\n    public EngineCustomUserInfoDefinition(long id) {\n        this(id, \"\", \"\");\n    }\n\n    public EngineCustomUserInfoDefinition(long id, String name, String description) {\n        this.id = id;\n        this.name = name;\n        this.description = description;\n    }\n\n    @Override\n    public long getId() {\n        return id;\n    }\n\n    @Override\n    public String getName() {\n        return name;\n    }\n\n    @Override\n    public String getDescription() {\n        return description;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/organization/TestValidator.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.organization;\n\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.validator.AbstractStringValidator;\n\n/**\n * @author Baptiste Mesta\n */\npublic class TestValidator extends AbstractStringValidator {\n\n    public TestValidator() {\n    }\n\n    @Override\n    protected void _check(String attributeValue) {\n        if (\"this password is not accepted by the TestValidator validator\".equals(attributeValue)) {\n            addError(\"the validator TestValidator rejected this password\");\n        }\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/organization/password/validator/DefaultPasswordValidatorTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.organization.password.validator;\n\nimport static org.junit.Assert.assertTrue;\n\nimport org.bonitasoft.console.common.server.i18n.I18n;\nimport org.junit.Test;\n\npublic class DefaultPasswordValidatorTest {\n\n    DefaultPasswordValidator defaultPasswordValidator = new DefaultPasswordValidator();\n\n    @Test\n    public void testWithPassword() {\n        I18n.getInstance();\n        defaultPasswordValidator.setLocale(\"en\");\n        defaultPasswordValidator.check(\"password\");\n        assertTrue(defaultPasswordValidator.getErrors().isEmpty());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/organization/password/validator/RobustnessPasswordValidatorTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.organization.password.validator;\n\nimport static org.junit.Assert.*;\n\nimport org.bonitasoft.console.common.server.i18n.I18n;\nimport org.junit.Test;\n\npublic class RobustnessPasswordValidatorTest {\n\n    RobustnessPasswordValidator robustnessPasswordValidator = new RobustnessPasswordValidator();\n\n    @Test\n    public void testWithWrongPassword() {\n        I18n.getInstance();\n        robustnessPasswordValidator.setLocale(\"en\");\n        robustnessPasswordValidator.check(\"password\");\n        assertFalse(robustnessPasswordValidator.getErrors().isEmpty());\n    }\n\n    @Test\n    public void testwithLongPassword() {\n        I18n.getInstance();\n        robustnessPasswordValidator.setLocale(\"en\");\n        robustnessPasswordValidator.check(\"myreallylongpassword\");\n        assertFalse(robustnessPasswordValidator.getErrors().isEmpty());\n    }\n\n    @Test\n    public void testwithGoodPassword() {\n        I18n.getInstance();\n        robustnessPasswordValidator.setLocale(\"en\");\n        robustnessPasswordValidator.check(\"MyPasswOrd!?321D*\");\n        assertTrue(robustnessPasswordValidator.getErrors().isEmpty());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/platform/SystemInformationControllerTest.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.platform;\n\nimport static org.assertj.core.api.AssertionsForClassTypes.assertThat;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.mock;\n\nimport java.util.Map;\n\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.engine.api.platform.PlatformInformationAPI;\nimport org.bonitasoft.engine.session.APISession;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\n@ExtendWith(MockitoExtension.class)\nclass SystemInformationControllerTest {\n\n    @Mock\n    private PlatformInformationAPI platformInformationAPI;\n\n    @Spy\n    private SystemInformationController systemInformationController;\n\n    @Test\n    void get_platform_information_should_return_raw_value_from_Engine() throws Exception {\n        //given\n        APISession apiSession = mock(APISession.class);\n        final HttpSession httpSession = mock(HttpSession.class);\n        doReturn(apiSession).when(systemInformationController).getApiSession(httpSession);\n        doReturn(platformInformationAPI).when(systemInformationController).getPlatformInformationAPI(apiSession);\n        final Map<String, String> expectedInfo = Map.of(\"key\", \"value\");\n        doReturn(expectedInfo).when(platformInformationAPI).getPlatformInformation();\n\n        //when\n        final Map<String, String> platformInfo = systemInformationController.getPlatformInfo(httpSession);\n\n        //then\n        assertThat(platformInfo).isEqualTo(expectedInfo);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/resource/ErrorMessageTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.resource;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.Test;\n\npublic class ErrorMessageTest {\n\n    @Test\n    public void should_encode_message_when_using_constructor() throws Exception {\n        ErrorMessage errorMessage = new ErrorMessage(new RuntimeException(\"<script>alert('bad')</script>\"));\n\n        assertThat(errorMessage.getMessage())\n                .isEqualTo(\"\\\\u003cscript\\\\u003ealert(\\\\u0027bad\\\\u0027)\\\\u003c\\\\/script\\\\u003e\");\n    }\n\n    @Test\n    public void should_encode_message_when_using_setter() throws Exception {\n        ErrorMessage errorMessage = new ErrorMessage();\n        errorMessage.setMessage(\"<script>alert('bad')</script>\");\n\n        assertThat(errorMessage.getMessage())\n                .isEqualTo(\"\\\\u003cscript\\\\u003ealert(\\\\u0027bad\\\\u0027)\\\\u003c\\\\/script\\\\u003e\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/system/BonitaVersionTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.system;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\n\nimport java.io.InputStream;\nimport java.nio.charset.StandardCharsets;\n\nimport org.apache.commons.io.IOUtils;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Vincent Elcrin\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class BonitaVersionTest {\n\n    @Mock\n    private VersionFile file;\n\n    @Mock\n    private InputStream stream;\n\n    @Test\n    public void should_read_version_stream_to_return_its_content() throws Exception {\n        final InputStream stream = IOUtils.toInputStream(\"1.0.0\\n2021.2-u0\\nBonitasoft © 2021\", StandardCharsets.UTF_8);\n        given(file.getStream()).willReturn(stream);\n\n        final BonitaVersion version = new BonitaVersion(file);\n\n        assertThat(version.getVersion()).isEqualTo(\"1.0.0\");\n        assertThat(version.getBrandingVersion()).isEqualTo(\"2021.2\");\n        assertThat(version.getBrandingVersionWithUpdate()).isEqualTo(\"2021.2-u0\");\n        assertThat(version.getCopyright()).isEqualTo(\"Bonitasoft © 2021\");\n        IOUtils.closeQuietly(stream);\n    }\n\n    @Test\n    public void should_read_version_stream_to_return_its_version() throws Exception {\n        final InputStream stream = IOUtils.toInputStream(\"1.0.0\");\n        given(file.getStream()).willReturn(stream);\n\n        final BonitaVersion version = new BonitaVersion(file);\n\n        assertThat(version.getVersion()).isEqualTo(\"1.0.0\");\n        IOUtils.closeQuietly(stream);\n    }\n\n    @Test\n    public void should_trim_extra_new_line_character() {\n        final InputStream stream = IOUtils.toInputStream(\"1.0.0\\n\", StandardCharsets.UTF_8);\n        given(file.getStream()).willReturn(stream);\n\n        final BonitaVersion version = new BonitaVersion(file);\n\n        assertThat(version.getVersion()).isEqualTo(\"1.0.0\");\n        IOUtils.closeQuietly(stream);\n    }\n\n    @Test\n    public void should_return_an_empty_version_when_file_is_invalid() {\n        given(file.getStream()).willReturn(null);\n\n        final BonitaVersion version = new BonitaVersion(file);\n\n        assertThat(version.getVersion()).isEqualTo(\"\");\n        assertThat(version.getCopyright()).isEqualTo(\"\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/system/I18nTranslationControllerTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.system;\n\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.when;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.console.common.server.i18n.I18n;\nimport org.bonitasoft.web.rest.server.api.AbstractControllerTest;\nimport org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mock;\nimport org.springframework.http.MediaType;\n\n/**\n * @author Julien Mege\n */\nclass I18nTranslationControllerTest extends AbstractControllerTest<I18nTranslationController> {\n\n    @Mock\n    private I18n i18n;\n\n    @Override\n    protected I18nTranslationController createController() {\n        return spy(new I18nTranslationController());\n    }\n\n    @Override\n    protected void configureMocks(I18nTranslationController controller) throws Exception {\n        doReturn(i18n).when(controller).getI18n();\n    }\n\n    @Test\n    void should_return_translation_for_the_given_locale() throws Exception {\n        Map<String, String> translations = new HashMap<>();\n        translations.put(\"key1\", \"<strong>message 1</strong>\");\n        translations.put(\"key2\", \"autre méssage\");\n        translations.put(\"key3\", \"~%^*µ\");\n\n        when(i18n.getLocale(AbstractI18n.LOCALE.fr)).thenReturn(translations);\n\n        mockMvc.perform(\n                get(\"/API/system/i18ntranslation\")\n                        .param(\"f\", \"locale=fr\")\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isOk())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(content().json(\"\"\"\n                        [\n                            {\"key\": \"key1\", \"value\": \"<strong>message 1</strong>\"},\n                            {\"key\": \"key2\", \"value\": \"autre méssage\"},\n                            {\"key\": \"key3\", \"value\": \"~%^*µ\"}\n                        ]\n                        \"\"\", true));\n    }\n\n    @Test\n    void should_return_http400_error_code_when_no_filter_param() throws Exception {\n        mockMvc.perform(get(\"/API/system/i18ntranslation\")\n                .sessionAttrs(sessionAttributes)\n                .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isBadRequest())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(jsonPath(\"$.exception\").value(IllegalArgumentException.class.toString()))\n                .andExpect(jsonPath(\"$.message\").value(\"filter locale is mandatory\"));\n    }\n\n    @Test\n    void should_return_http400_error_code_when_no_locale_filter_param() throws Exception {\n        mockMvc.perform(\n                get(\"/API/system/i18ntranslation\")\n                        .param(\"f\", \"test\")\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isBadRequest())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(jsonPath(\"$.exception\").value(IllegalArgumentException.class.toString()))\n                .andExpect(jsonPath(\"$.message\").value(\"filter locale is mandatory\"));\n    }\n\n    @Test\n    void should_return_http400_error_code_when_empty_locale_filter_param() throws Exception {\n        mockMvc.perform(\n                get(\"/API/system/i18ntranslation\")\n                        .param(\"f\", \"locale=\")\n                        .sessionAttrs(sessionAttributes)\n                        .accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isBadRequest())\n                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))\n                .andExpect(jsonPath(\"$.exception\").value(IllegalArgumentException.class.toString()))\n                .andExpect(jsonPath(\"$.message\").value(\"filter locale is mandatory\"));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/system/MaintenanceControllerTest.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.system;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertSame;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.mockito.Mockito.*;\n\nimport javax.servlet.http.HttpSession;\n\nimport org.bonitasoft.console.common.server.utils.SessionUtil;\nimport org.bonitasoft.engine.api.MaintenanceAPI;\nimport org.bonitasoft.engine.exception.BonitaException;\nimport org.bonitasoft.engine.maintenance.MaintenanceDetails;\nimport org.bonitasoft.engine.maintenance.impl.MaintenanceDetailsImpl;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.system.MaintenanceDetailsClient;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.web.server.ResponseStatusException;\n\n@ExtendWith(MockitoExtension.class)\nclass MaintenanceControllerTest {\n\n    @Mock\n    private HttpSession session;\n\n    @Mock\n    private MaintenanceAPI maintenanceAPI;\n\n    @Spy\n    @InjectMocks\n    private MaintenanceController maintenanceController;\n\n    @Test\n    public void should_get_maintenance_info_from_java_api() throws BonitaException {\n        //given\n        APISession apiSession = mock(APISession.class);\n        doReturn(apiSession).when(maintenanceController).getApiSession(session);\n\n        MaintenanceDetails maintenanceDetails = mock(MaintenanceDetails.class);\n        doReturn(maintenanceAPI).when(maintenanceController).getMaintenanceAPI(apiSession);\n        doReturn(maintenanceDetails).when(maintenanceAPI).getMaintenanceDetails();\n\n        //when\n        MaintenanceDetails result = maintenanceController.getMaintenanceDetails(session);\n\n        //then\n        verify(maintenanceAPI).getMaintenanceDetails();\n        assertSame(maintenanceDetails, result);\n    }\n\n    @Test\n    public void should_enable_maintenance_and_change_and_enable_maintenance_msg() throws BonitaException {\n        //given\n        APISession apiSession = mock(APISession.class);\n        doReturn(apiSession).when(maintenanceController).getApiSession(session);\n\n        MaintenanceDetails maintenanceDetails = MaintenanceDetailsImpl.builder()\n                .maintenanceState(MaintenanceDetails.State.DISABLED)\n                .maintenanceMessage(null)\n                .maintenanceMessageActive(false)\n                .build();\n        doReturn(maintenanceAPI).when(maintenanceController).getMaintenanceAPI(apiSession);\n        doReturn(maintenanceDetails).when(maintenanceAPI).getMaintenanceDetails();\n\n        MaintenanceDetailsClient maintenanceInfoClient = new MaintenanceDetailsClient(\n                MaintenanceDetails.State.ENABLED, \"new Scheduled Maintenance msg\", true);\n\n        //when\n        maintenanceController.changeMaintenanceState(maintenanceInfoClient, session);\n\n        //then\n        verify(maintenanceAPI).updateMaintenanceMessage(\"new Scheduled Maintenance msg\");\n        verify(maintenanceAPI).enableMaintenanceMode();\n        verify(maintenanceAPI, never()).disableMaintenanceMode();\n        verify(maintenanceAPI).enableMaintenanceMessage();\n        verify(maintenanceAPI, never()).disableMaintenanceMessage();\n    }\n\n    @Test\n    public void should_disable_maintenance_and_change_and_disable_maintenance_msg() throws BonitaException {\n        //given\n        APISession apiSession = mock(APISession.class);\n        doReturn(apiSession).when(maintenanceController).getApiSession(session);\n\n        MaintenanceDetails maintenanceDetails = MaintenanceDetailsImpl.builder()\n                .maintenanceState(MaintenanceDetails.State.ENABLED)\n                .maintenanceMessage(\"msg\")\n                .maintenanceMessageActive(true)\n                .build();\n        doReturn(maintenanceAPI).when(maintenanceController).getMaintenanceAPI(apiSession);\n        doReturn(maintenanceDetails).when(maintenanceAPI).getMaintenanceDetails();\n\n        MaintenanceDetailsClient maintenanceInfoClient = new MaintenanceDetailsClient(\n                MaintenanceDetails.State.DISABLED, \"new Scheduled Maintenance msg\", false);\n\n        //when\n        maintenanceController.changeMaintenanceState(maintenanceInfoClient, session);\n\n        //then\n        verify(maintenanceAPI).updateMaintenanceMessage(\"new Scheduled Maintenance msg\");\n        verify(maintenanceAPI, never()).enableMaintenanceMode();\n        verify(maintenanceAPI).disableMaintenanceMode();\n        verify(maintenanceAPI, never()).enableMaintenanceMessage();\n        verify(maintenanceAPI).disableMaintenanceMessage();\n    }\n\n    @Test\n    public void should_disable_maintenance_only() throws BonitaException {\n        //given\n        APISession apiSession = mock(APISession.class);\n        doReturn(apiSession).when(maintenanceController).getApiSession(session);\n\n        MaintenanceDetails maintenanceDetails = MaintenanceDetailsImpl.builder()\n                .maintenanceState(MaintenanceDetails.State.ENABLED)\n                .maintenanceMessage(\"msg\")\n                .maintenanceMessageActive(true)\n                .build();\n        doReturn(maintenanceAPI).when(maintenanceController).getMaintenanceAPI(apiSession);\n        doReturn(maintenanceDetails).when(maintenanceAPI).getMaintenanceDetails();\n\n        MaintenanceDetailsClient maintenanceInfoClient = new MaintenanceDetailsClient(\n                MaintenanceDetails.State.DISABLED, \"msg\", true);\n\n        //when\n        maintenanceController.changeMaintenanceState(maintenanceInfoClient, session);\n\n        //then\n        verify(maintenanceAPI, never()).updateMaintenanceMessage(any());\n        verify(maintenanceAPI, never()).enableMaintenanceMode();\n        verify(maintenanceAPI).disableMaintenanceMode();\n        verify(maintenanceAPI, never()).enableMaintenanceMessage();\n        verify(maintenanceAPI, never()).disableMaintenanceMessage();\n    }\n\n    @Test\n    public void should_disable_scheduled_maintenance_msg_only() throws BonitaException {\n        //given\n        APISession apiSession = mock(APISession.class);\n        doReturn(apiSession).when(maintenanceController).getApiSession(session);\n\n        MaintenanceDetails maintenanceDetails = MaintenanceDetailsImpl.builder()\n                .maintenanceState(MaintenanceDetails.State.DISABLED)\n                .maintenanceMessage(\"msg\")\n                .maintenanceMessageActive(true)\n                .build();\n        doReturn(maintenanceAPI).when(maintenanceController).getMaintenanceAPI(apiSession);\n        doReturn(maintenanceDetails).when(maintenanceAPI).getMaintenanceDetails();\n\n        MaintenanceDetailsClient maintenanceInfoClient = new MaintenanceDetailsClient(\n                MaintenanceDetails.State.DISABLED, \"msg\", false);\n\n        //when\n        maintenanceController.changeMaintenanceState(maintenanceInfoClient, session);\n\n        //then\n        verify(maintenanceAPI, never()).updateMaintenanceMessage(any());\n        verify(maintenanceAPI, never()).enableMaintenanceMode();\n        verify(maintenanceAPI, never()).disableMaintenanceMode();\n        verify(maintenanceAPI, never()).enableMaintenanceMessage();\n        verify(maintenanceAPI).disableMaintenanceMessage();\n    }\n\n    @Test\n    public void should_respond_unauthorized_if_no_session() {\n        //given\n        when(session.getAttribute(SessionUtil.API_SESSION_PARAM_KEY)).thenReturn(null);\n\n        //when\n        ResponseStatusException exception = assertThrows(ResponseStatusException.class,\n                () -> maintenanceController.getApiSession(session));\n        //then\n        assertThat(exception.getStatus()).isEqualTo(HttpStatus.UNAUTHORIZED);\n\n        //when\n        exception = assertThrows(ResponseStatusException.class,\n                () -> maintenanceController.getMaintenanceDetails(session));\n        //then\n        assertThat(exception.getStatus()).isEqualTo(HttpStatus.UNAUTHORIZED);\n\n        //when\n        exception = assertThrows(ResponseStatusException.class,\n                () -> maintenanceController.changeMaintenanceState(null, session));\n        //then\n        assertThat(exception.getStatus()).isEqualTo(HttpStatus.UNAUTHORIZED);\n    }\n\n    @Test\n    public void should_getApiSession_return_session_from_HttpRequestSession() {\n        // Create a mock APISession\n        APISession apiSession = mock(APISession.class);\n        when(session.getAttribute(SessionUtil.API_SESSION_PARAM_KEY)).thenReturn(apiSession);\n\n        // Call the method and expect the same APISession to be returned\n        APISession result = maintenanceController.getApiSession(session);\n        assertSame(apiSession, result);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/tenant/TenantResourceItemTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.api.tenant;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.when;\n\nimport java.time.Instant;\nimport java.time.OffsetDateTime;\nimport java.time.ZoneOffset;\n\nimport org.bonitasoft.engine.tenant.TenantResource;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class TenantResourceItemTest {\n\n    @Mock\n    private TenantResource tenantResource;\n\n    @Test\n    public void should_format_lastUpdateDate_as_ISO8601_string_when_tenantResourceItem_is_created() {\n        String dateAsString = \"2018-01-05T09:04:19Z\";\n        Instant instant = Instant.ofEpochSecond(1515143059);\n        when(tenantResource.getLastUpdateDate()).thenReturn(OffsetDateTime.ofInstant(instant, ZoneOffset.UTC));\n\n        TenantResourceItem tenantResourceItem = new TenantResourceItem(tenantResource);\n\n        assertThat(tenantResourceItem.getLastUpdateDate()).isEqualTo(dateAsString);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/CommonDatastoreTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.search.SearchFilterOperation;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.impl.SearchFilter;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Celine Souchet\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class CommonDatastoreTest {\n\n    @InjectMocks\n    private FakeCommonDatastore commonDatastore;\n\n    /**\n     * Test method for {@link CommonDatastore#addStringFilterToSearchBuilder(java.util.Map,\n     * org.bonitasoft.engine.search.SearchOptionsBuilder, String, String).\n     */\n    @Test\n    public void addStringFilterToSearchBuilder_should_do_nothing_when_filters_is_null() {\n        // given:\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        // when:\n        commonDatastore.addStringFilterToSearchBuilder(null, searchOptionsBuilder, null, null);\n        // then:\n        assertThat(searchOptionsBuilder.done().getFilters()).isEmpty();\n    }\n\n    /**\n     * Test method for {@link CommonDatastore#addStringFilterToSearchBuilder(java.util.Map,\n     * org.bonitasoft.engine.search.SearchOptionsBuilder, String, String).\n     */\n    @Test\n    public void addStringFilterToSearchBuilder_should_do_nothing_when_filters_is_empty() {\n        // given:\n        final Map<String, String> filters = new HashMap<>(0);\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        // when:\n        commonDatastore.addStringFilterToSearchBuilder(filters, searchOptionsBuilder, null, null);\n        // then:\n        assertThat(searchOptionsBuilder.done().getFilters()).isEmpty();\n    }\n\n    /**\n     * Test method for {@link CommonDatastore#addStringFilterToSearchBuilder(java.util.Map,\n     * org.bonitasoft.engine.search.SearchOptionsBuilder, String, String).\n     */\n    @Test\n    public void addStringFilterToSearchBuilder_should_add_greater_than_filter_when_is_applicable() {\n        // given:\n        final String filterName = \"someFilterKey\";\n        final String engineAttributeName = \"EngineNumericKey\";\n        final Map<String, String> filters = new HashMap<>(1);\n        filters.put(filterName, \"> someNumericValue \");\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        // when:\n        commonDatastore.addStringFilterToSearchBuilder(filters, searchOptionsBuilder, filterName, engineAttributeName);\n        // then:\n        assertThat(searchOptionsBuilder.done().getFilters().size()).isEqualTo(1);\n        final SearchFilter searchFilter = searchOptionsBuilder.done().getFilters().get(0);\n        assertThat(searchFilter.getOperation()).isEqualTo(SearchFilterOperation.GREATER_THAN);\n        assertThat(searchOptionsBuilder.done().getFilters().get(0).getField()).isEqualTo(engineAttributeName);\n        assertThat(searchOptionsBuilder.done().getFilters().get(0).getValue()).isEqualTo(\"someNumericValue\");\n    }\n\n    /**\n     * Test method for {@link CommonDatastore#addStringFilterToSearchBuilder(java.util.Map,\n     * org.bonitasoft.engine.search.SearchOptionsBuilder, String, String).\n     */\n    @Test\n    public void addStringFilterToSearchBuilder_should_do_nothing_when_filter_value_is_null() {\n        // given:\n        final String filterName = \"someFilterKey\";\n        final Map<String, String> filters = new HashMap<>(1);\n        filters.put(filterName, null);\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        // when:\n        commonDatastore.addStringFilterToSearchBuilder(filters, searchOptionsBuilder, filterName, null);\n        // then:\n        assertThat(searchOptionsBuilder.done().getFilters()).isEmpty();\n    }\n\n    /**\n     * Test method for {@link CommonDatastore#addStringFilterToSearchBuilder(java.util.Map,\n     * org.bonitasoft.engine.search.SearchOptionsBuilder, String, String).\n     */\n    @Test\n    public void addStringFilterToSearchBuilder_should_add_less_than_filter_when_is_applicable() {\n        // given:\n        final String filterName = \"someFilterKey\";\n        final String engineAttributeName = \"EngineNumericKey\";\n        final Map<String, String> filters = new HashMap<>(1);\n        filters.put(filterName, \"< someNumericValue \");\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        // when:\n        commonDatastore.addStringFilterToSearchBuilder(filters, searchOptionsBuilder, filterName, engineAttributeName);\n        // then:\n        assertThat(searchOptionsBuilder.done().getFilters().size()).isEqualTo(1);\n        final SearchFilter searchFilter = searchOptionsBuilder.done().getFilters().get(0);\n        assertThat(searchFilter.getOperation()).isEqualTo(SearchFilterOperation.LESS_THAN);\n        assertThat(searchOptionsBuilder.done().getFilters().get(0).getField()).isEqualTo(engineAttributeName);\n        assertThat(searchOptionsBuilder.done().getFilters().get(0).getValue()).isEqualTo(\"someNumericValue\");\n    }\n\n    /**\n     * Test method for {@link CommonDatastore#addStringFilterToSearchBuilder(java.util.Map,\n     * org.bonitasoft.engine.search.SearchOptionsBuilder, String, String).\n     */\n    @Test\n    public void addStringFilterToSearchBuilder_should_add_equals_filter_when_is_applicable() {\n        // given:\n        final String filterName = \"someFilterKey\";\n        final String engineAttributeName = \"EngineNumericKey\";\n        final String filterValue = \"174\";\n        final Map<String, String> filters = new HashMap<>(1);\n        filters.put(filterName, filterValue);\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        // when:\n        commonDatastore.addStringFilterToSearchBuilder(filters, searchOptionsBuilder, filterName, engineAttributeName);\n        // then:\n        assertThat(searchOptionsBuilder.done().getFilters().size()).isEqualTo(1);\n        final SearchFilter searchFilter = searchOptionsBuilder.done().getFilters().get(0);\n        assertThat(searchFilter.getOperation()).isEqualTo(SearchFilterOperation.EQUALS);\n        assertThat(searchOptionsBuilder.done().getFilters().get(0).getField()).isEqualTo(engineAttributeName);\n        assertThat(searchOptionsBuilder.done().getFilters().get(0).getValue()).isEqualTo(filterValue);\n    }\n\n    /**\n     * Test method for {@link CommonDatastore#addLongFilterToSearchBuilder(java.util.Map,\n     * org.bonitasoft.engine.search.SearchOptionsBuilder, String, String).\n     */\n    @Test\n    public void addLongFilterToSearchBuilder_should_do_nothing_when_filters_is_null() {\n        // given:\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        // when:\n        commonDatastore.addLongFilterToSearchBuilder(null, searchOptionsBuilder, null, null);\n        // then:\n        assertThat(searchOptionsBuilder.done().getFilters()).isEmpty();\n    }\n\n    /**\n     * Test method for {@link CommonDatastore#addLongFilterToSearchBuilder(java.util.Map,\n     * org.bonitasoft.engine.search.SearchOptionsBuilder, String, String).\n     */\n    @Test\n    public void addLongFilterToSearchBuilder_should_do_nothing_when_filters_is_empty() {\n        // given:\n        final Map<String, String> filters = new HashMap<>(0);\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        // when:\n        commonDatastore.addLongFilterToSearchBuilder(filters, searchOptionsBuilder, null, null);\n        // then:\n        assertThat(searchOptionsBuilder.done().getFilters()).isEmpty();\n    }\n\n    /**\n     * Test method for {@link CommonDatastore#addLongFilterToSearchBuilder(java.util.Map,\n     * org.bonitasoft.engine.search.SearchOptionsBuilder, String, String).\n     */\n    @Test\n    public void addLongFilterToSearchBuilder_should_add_greater_than_filter_when_is_applicable() {\n        // given:\n        final String filterName = \"someFilterKey\";\n        final String engineAttributeName = \"EngineNumericKey\";\n        final Map<String, String> filters = new HashMap<>(1);\n        filters.put(filterName, \"> 2 \");\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        // when:\n        commonDatastore.addLongFilterToSearchBuilder(filters, searchOptionsBuilder, filterName, engineAttributeName);\n        // then:\n        assertThat(searchOptionsBuilder.done().getFilters().size()).isEqualTo(1);\n        final SearchFilter searchFilter = searchOptionsBuilder.done().getFilters().get(0);\n        assertThat(searchFilter.getOperation()).isEqualTo(SearchFilterOperation.GREATER_THAN);\n        assertThat(searchOptionsBuilder.done().getFilters().get(0).getField()).isEqualTo(engineAttributeName);\n        assertThat(searchOptionsBuilder.done().getFilters().get(0).getValue()).isEqualTo(2L);\n    }\n\n    /**\n     * Test method for {@link CommonDatastore#addLongFilterToSearchBuilder(java.util.Map,\n     * org.bonitasoft.engine.search.SearchOptionsBuilder, String, String).\n     */\n    @Test\n    public void addLongFilterToSearchBuilder_should_do_nothing_when_filter_value_is_null() {\n        // given:\n        final String filterName = \"someFilterKey\";\n        final Map<String, String> filters = new HashMap<>(1);\n        filters.put(filterName, null);\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        // when:\n        commonDatastore.addLongFilterToSearchBuilder(filters, searchOptionsBuilder, filterName, null);\n        // then:\n        assertThat(searchOptionsBuilder.done().getFilters()).isEmpty();\n    }\n\n    /**\n     * Test method for {@link CommonDatastore#addLongFilterToSearchBuilder(java.util.Map,\n     * org.bonitasoft.engine.search.SearchOptionsBuilder, String, String).\n     */\n    @Test\n    public void addLongFilterToSearchBuilder_should_add_less_than_filter_when_is_applicable() {\n        // given:\n        final String filterName = \"someFilterKey\";\n        final String engineAttributeName = \"EngineNumericKey\";\n        final Map<String, String> filters = new HashMap<>(1);\n        filters.put(filterName, \"< 8 \");\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        // when:\n        commonDatastore.addLongFilterToSearchBuilder(filters, searchOptionsBuilder, filterName, engineAttributeName);\n        // then:\n        assertThat(searchOptionsBuilder.done().getFilters().size()).isEqualTo(1);\n        final SearchFilter searchFilter = searchOptionsBuilder.done().getFilters().get(0);\n        assertThat(searchFilter.getOperation()).isEqualTo(SearchFilterOperation.LESS_THAN);\n        assertThat(searchOptionsBuilder.done().getFilters().get(0).getField()).isEqualTo(engineAttributeName);\n        assertThat(searchOptionsBuilder.done().getFilters().get(0).getValue()).isEqualTo(8L);\n    }\n\n    /**\n     * Test method for {@link CommonDatastore#addLongFilterToSearchBuilder(java.util.Map,\n     * org.bonitasoft.engine.search.SearchOptionsBuilder, String, String).\n     */\n    @Test\n    public void addLongFilterToSearchBuilder_should_add_equals_filter_when_is_applicable() {\n        // given:\n        final String filterName = \"someFilterKey\";\n        final String engineAttributeName = \"EngineNumericKey\";\n        final String filterValue = \"174\";\n        final Map<String, String> filters = new HashMap<>(1);\n        filters.put(filterName, filterValue);\n        final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10);\n        // when:\n        commonDatastore.addLongFilterToSearchBuilder(filters, searchOptionsBuilder, filterName, engineAttributeName);\n        // then:\n        assertThat(searchOptionsBuilder.done().getFilters().size()).isEqualTo(1);\n        final SearchFilter searchFilter = searchOptionsBuilder.done().getFilters().get(0);\n        assertThat(searchFilter.getOperation()).isEqualTo(SearchFilterOperation.EQUALS);\n        assertThat(searchOptionsBuilder.done().getFilters().get(0).getField()).isEqualTo(engineAttributeName);\n        assertThat(searchOptionsBuilder.done().getFilters().get(0).getValue()).isEqualTo(174L);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/FakeCommonDatastore.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore;\n\nimport org.bonitasoft.engine.bpm.process.ProcessDefinition;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessItem;\n\n/**\n * @author Celine Souchet\n */\npublic class FakeCommonDatastore extends CommonDatastore<ProcessItem, ProcessDefinition> {\n\n    public FakeCommonDatastore(final APISession engineSession) {\n        super(engineSession);\n    }\n\n    @Override\n    protected ProcessItem convertEngineToConsoleItem(final ProcessDefinition item) {\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/application/ApplicationDataStoreTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.application;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.doThrow;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\n\nimport org.bonitasoft.engine.api.ApplicationAPI;\nimport org.bonitasoft.engine.api.PageAPI;\nimport org.bonitasoft.engine.business.application.ApplicationCreator;\nimport org.bonitasoft.engine.business.application.ApplicationLinkCreator;\nimport org.bonitasoft.engine.business.application.ApplicationLinkUpdater;\nimport org.bonitasoft.engine.business.application.ApplicationNotFoundException;\nimport org.bonitasoft.engine.business.application.ApplicationPage;\nimport org.bonitasoft.engine.business.application.ApplicationUpdater;\nimport org.bonitasoft.engine.business.application.ApplicationVisibility;\nimport org.bonitasoft.engine.business.application.IApplication;\nimport org.bonitasoft.engine.business.application.impl.ApplicationImpl;\nimport org.bonitasoft.engine.business.application.impl.ApplicationLinkImpl;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.impl.SearchResultImpl;\nimport org.bonitasoft.web.rest.model.application.AbstractApplicationItem;\nimport org.bonitasoft.web.rest.model.application.ApplicationDefinition;\nimport org.bonitasoft.web.rest.model.application.ApplicationItem;\nimport org.bonitasoft.web.rest.model.application.ApplicationLinkItem;\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.ItemDefinitionFactory;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ApplicationDataStoreTest extends APITestWithMock {\n\n    @Mock\n    private ApplicationAPI applicationAPI;\n\n    @Mock\n    private PageAPI pageAPI;\n\n    @Mock\n    private ApplicationItemConverter converter;\n\n    @InjectMocks\n    private ApplicationDataStore dataStore;\n\n    @Mock\n    private ApplicationPage applicationPage;\n\n    @Mock\n    private Page homePage;\n\n    @Before\n    public void setUp() throws Exception {\n        ItemDefinitionFactory.setDefaultFactory(new ItemDefinitionFactory() {\n\n            @Override\n            public ItemDefinition<?> defineItemDefinitions(final String token) {\n                return new ApplicationDefinition();\n            }\n        });\n    }\n\n    @Test\n    public void should_return_application_created_by_ApplicationAPI_converted_to_ApplicationItem_on_add()\n            throws Exception {\n        //given\n        final ApplicationCreator creator = new ApplicationCreator(\"app\", \"My application\", \"1.0\");\n        ApplicationItem app = new ApplicationItem();\n        given(converter.toApplicationCreator(app)).willReturn(creator);\n        final ApplicationImpl application = new ApplicationImpl(\"app\", \"1.0\", \"app desc\", 2L, 3L);\n        given(applicationAPI.createApplication(creator)).willReturn(application);\n        final ApplicationItem item = new ApplicationItem();\n        given(converter.toApplicationItem(application)).willReturn(item);\n\n        given(pageAPI.getPageByName(\"custompage_home\")).willReturn(homePage);\n        given(homePage.getId()).willReturn(1L);\n\n        given(applicationAPI.createApplicationPage(application.getId(), 1, \"home\")).willReturn(applicationPage);\n\n        //when\n        final ApplicationItem createdItem = dataStore.add(new ApplicationItem());\n\n        //then\n        assertThat(createdItem).isEqualTo(item);\n    }\n\n    @Test\n    public void should_return_application_link_created_by_ApplicationAPI_converted_to_ApplicationLinkItem_on_add()\n            throws Exception {\n        //given\n        final ApplicationLinkCreator creator = new ApplicationLinkCreator(\"app\", \"My application\", \"1.0\");\n        ApplicationLinkItem app = new ApplicationLinkItem();\n        given(converter.toApplicationLinkCreator(app)).willReturn(creator);\n        final ApplicationLinkImpl application = new ApplicationLinkImpl(\"app\", \"1.0\", \"app desc\");\n        given(applicationAPI.createApplicationLink(creator)).willReturn(application);\n        final ApplicationLinkItem item = new ApplicationLinkItem();\n        given(converter.toApplicationItem(application)).willReturn(item);\n\n        //when\n        final ApplicationLinkItem createdItem = dataStore.add(new ApplicationLinkItem());\n\n        //then\n        assertThat(createdItem).isEqualTo(item);\n    }\n\n    @Test\n    public void should_create_default_home_page_on_add() throws Exception {\n        //given\n        final ApplicationCreator creator = new ApplicationCreator(\"app\", \"My application\", \"1.0\");\n        ApplicationItem item = new ApplicationItem();\n        given(converter.toApplicationCreator(item)).willReturn(creator);\n        final ApplicationImpl application = new ApplicationImpl(\"app\", \"1.0\", \"app desc\", 2L, 3L);\n        application.setId(1);\n        given(applicationAPI.createApplication(creator)).willReturn(application);\n        final ApplicationItem convertedAppItem = new ApplicationItem();\n        given(converter.toApplicationItem(application)).willReturn(convertedAppItem);\n        given(pageAPI.getPageByName(\"custompage_home\")).willReturn(homePage);\n        given(homePage.getId()).willReturn(1L);\n        given(applicationAPI.createApplicationPage(application.getId(), 1L, \"home\")).willReturn(applicationPage);\n        given(applicationPage.getId()).willReturn(3L);\n\n        //when\n        dataStore.add(new ApplicationItem());\n\n        //then\n        verify(applicationAPI, times(1)).setApplicationHomePage(application.getId(), 3);\n    }\n\n    @Test\n    public void should_return_application_updated_by_ApplicationAPI_converted_to_ApplicationItem_on_update()\n            throws Exception {\n        //given\n        final HashMap<String, String> attributesToUpDate = new HashMap<>();\n        attributesToUpDate.put(ApplicationItem.ATTRIBUTE_TOKEN, \"app_name\");\n        attributesToUpDate.put(ApplicationItem.ATTRIBUTE_DISPLAY_NAME, \"App display name\");\n        final ApplicationUpdater applicationUpdater = new ApplicationUpdater();\n        given(converter.toApplicationUpdater(attributesToUpDate)).willReturn(applicationUpdater);\n\n        final ApplicationImpl application = new ApplicationImpl(\"app\", \"1.0\", \"app desc\", 2L, 3L);\n        given(applicationAPI.updateApplication(1, applicationUpdater)).willReturn(application);\n        given(applicationAPI.getIApplication(1)).willReturn(application);\n        final ApplicationItem item = new ApplicationItem();\n        given(converter.toApplicationItem(application)).willReturn(item);\n\n        //when\n        final ApplicationItem createdItem = (ApplicationItem) dataStore.update(APIID.makeAPIID(1L), attributesToUpDate);\n\n        //then\n        verify(converter, times(1)).toApplicationUpdater(attributesToUpDate);\n        verify(applicationAPI, times(1)).updateApplication(1, applicationUpdater);\n        verify(converter, times(1)).toApplicationItem(application);\n        assertThat(createdItem).isEqualTo(new ApplicationItem());\n    }\n\n    @Test\n    public void should_return_application_link_updated_by_ApplicationAPI_converted_to_ApplicationLinkItem_on_update()\n            throws Exception {\n        //given\n        final HashMap<String, String> attributesToUpDate = new HashMap<>();\n        attributesToUpDate.put(ApplicationLinkItem.ATTRIBUTE_TOKEN, \"app_name\");\n        attributesToUpDate.put(ApplicationLinkItem.ATTRIBUTE_DISPLAY_NAME, \"App display name\");\n        final ApplicationLinkUpdater applicationUpdater = new ApplicationLinkUpdater();\n        given(converter.toApplicationLinkUpdater(attributesToUpDate)).willReturn(applicationUpdater);\n\n        final ApplicationLinkImpl application = new ApplicationLinkImpl(\"app\", \"1.0\", \"app desc\");\n        given(applicationAPI.updateApplicationLink(1, applicationUpdater)).willReturn(application);\n        given(applicationAPI.getIApplication(1)).willReturn(application);\n        final ApplicationLinkItem item = new ApplicationLinkItem();\n        given(converter.toApplicationItem(application)).willReturn(item);\n\n        //when\n        final ApplicationLinkItem createdItem = (ApplicationLinkItem) dataStore.update(APIID.makeAPIID(1L),\n                attributesToUpDate);\n\n        //then\n        verify(converter, times(1)).toApplicationLinkUpdater(attributesToUpDate);\n        verify(applicationAPI, times(1)).updateApplicationLink(1, applicationUpdater);\n        verify(converter, times(1)).toApplicationItem(application);\n        assertThat(createdItem).isEqualTo(new ApplicationLinkItem());\n    }\n\n    @Test(expected = APIException.class)\n    public void should_throw_APIException_when_ApplicationAPI_throws_an_exception_on_add() throws Exception {\n        ApplicationItem app = new ApplicationItem();\n        final ApplicationCreator creator = new ApplicationCreator(\"app\", \"My application\", \"1.0\");\n\n        given(pageAPI.getPageByName(\"custompage_home\")).willReturn(homePage);\n        given(converter.toApplicationCreator(app)).willReturn(creator);\n\n        //given\n        when(applicationAPI.createApplication(any())).thenThrow(new CreationException(\"\"));\n\n        //when\n        dataStore.add(app);\n    }\n\n    @Test(expected = APIException.class)\n    public void should_throw_APIException_when_ApplicationAPI_throws_an_exception_on_UpDate() throws Exception {\n        //given\n        given(applicationAPI.getIApplication(eq(1L))).willReturn(mock(ApplicationImpl.class));\n        when(applicationAPI.updateApplication(eq(1L), any())).thenThrow(new UpdateException(\"\"));\n        //when\n        dataStore.update(APIID.makeAPIID(1L), new HashMap<>());\n    }\n\n    @Test\n    public void should_return_the_good_application_on_get() throws Exception {\n        //given\n        final ApplicationImpl application = new ApplicationImpl(\"app\", \"1.0\", \"app desc\");\n        final ApplicationItem item = new ApplicationItem();\n        given(converter.toApplicationItem(application)).willReturn(item);\n        application.setId(1);\n        given(applicationAPI.getIApplication(1)).willReturn(application);\n\n        //when\n        final AbstractApplicationItem retrivedItem = dataStore.get(APIID.makeAPIID(\"1\"));\n\n        //then\n        assertThat(retrivedItem).isEqualTo(item);\n    }\n\n    @Test\n    public void should_return_the_good_application_link_on_get() throws Exception {\n        //given\n        final ApplicationLinkImpl application = new ApplicationLinkImpl(\"app\", \"1.0\", \"app desc\");\n        final ApplicationLinkItem item = new ApplicationLinkItem();\n        given(converter.toApplicationItem(application)).willReturn(item);\n        application.setId(1);\n        given(applicationAPI.getIApplication(1)).willReturn(application);\n\n        //when\n        final AbstractApplicationItem retrivedItem = dataStore.get(APIID.makeAPIID(\"1\"));\n\n        //then\n        assertThat(retrivedItem).isEqualTo(item);\n    }\n\n    @Test(expected = APIException.class)\n    public void should_return_throw_APIException_on_get_when_engine_throws_exception() throws Exception {\n        //given\n        given(applicationAPI.getIApplication(1)).willThrow(new ApplicationNotFoundException(1));\n\n        //when\n        dataStore.get(APIID.makeAPIID(\"1\"));\n\n        //then exception\n    }\n\n    @Test\n    public void should_delete_the_good_Application_on_delete() throws Exception {\n        //given\n\n        //when\n        dataStore.delete(Arrays.<APIID> asList(APIID.makeAPIID(\"1\"), APIID.makeAPIID(\"2\")));\n\n        //then\n        verify(applicationAPI, times(1)).deleteApplication(1);\n        verify(applicationAPI, times(1)).deleteApplication(2);\n    }\n\n    @Test(expected = APIException.class)\n    public void should_throw_APIException_on_delete_when_engine_throws_exception() throws Exception {\n        doThrow(new DeletionException(\"\")).when(applicationAPI).deleteApplication(1);\n\n        //when\n        dataStore.delete(Arrays.<APIID> asList(APIID.makeAPIID(\"1\")));\n\n        //then exception\n    }\n\n    @Test\n    public void should_call_engine_with_good_parameters_on_search() throws Exception {\n        //given\n        final int page = 0;\n        final int resultsByPage = 1;\n        final String search = \"string to Match\";\n        final String orders = ApplicationItem.ATTRIBUTE_TOKEN + \" DESC\";\n        final HashMap<String, String> filters = new HashMap<>();\n        filters.put(ApplicationItem.ATTRIBUTE_CREATED_BY, \"1\");\n        filters.put(ApplicationItem.ATTRIBUTE_VERSION, \"1.0\");\n        filters.put(ApplicationItem.FILTER_USER_ID, \"4\");\n        final ApplicationImpl application = new ApplicationImpl(\"app\", \"1.0\", \"app desccription\");\n        application.setId(1);\n        application.setCreationDate(new Date());\n        application.setLastUpdateDate(new Date());\n        application.setVisibility(ApplicationVisibility.ALL);\n\n        given(applicationAPI.searchIApplications(any(SearchOptions.class)))\n                .willReturn(new SearchResultImpl<>(2, Arrays.<IApplication> asList(application)));\n\n        //when\n        dataStore.search(page, resultsByPage, search, orders, filters);\n\n        //then\n        final ArgumentCaptor<SearchOptions> searchOptionCaptor = ArgumentCaptor.forClass(SearchOptions.class);\n        verify(applicationAPI, times(1)).searchIApplications(searchOptionCaptor.capture());\n\n        final SearchOptions searchOption = searchOptionCaptor.getValue();\n        assertThat(searchOption.getFilters().get(0).getField()).isEqualTo(ApplicationItem.ATTRIBUTE_CREATED_BY);\n        assertThat(searchOption.getFilters().get(0).getValue()).isEqualTo(\"1\");\n        assertThat(searchOption.getFilters().get(1).getField()).isEqualTo(ApplicationItem.ATTRIBUTE_VERSION);\n        assertThat(searchOption.getFilters().get(1).getValue()).isEqualTo(\"1.0\");\n        assertThat(searchOption.getFilters().get(2).getField()).isEqualTo(ApplicationItem.FILTER_USER_ID);\n        assertThat(searchOption.getFilters().get(2).getValue()).isEqualTo(\"4\");\n        assertThat(searchOption.getSearchTerm()).isEqualTo(search);\n        assertThat(searchOption.getMaxResults()).isEqualTo(1);\n        assertThat(searchOption.getStartIndex()).isEqualTo(0);\n        assertThat(searchOption.getSorts().get(0).getField()).isEqualTo(ApplicationItem.ATTRIBUTE_TOKEN);\n        assertThat(searchOption.getSorts().get(0).getOrder()).isEqualTo(Order.DESC);\n    }\n\n    @Test\n    public void should_return_a_valid_ItemSearchResult_on_search() throws Exception {\n        //given\n        final int page = 0;\n        final int resultsByPage = 1;\n        final String search = \"string to Match\";\n        final String orders = ApplicationItem.ATTRIBUTE_TOKEN + \" DESC\";\n        final HashMap<String, String> filters = new HashMap<>();\n        filters.put(ApplicationItem.ATTRIBUTE_CREATED_BY, \"1\");\n        filters.put(ApplicationItem.ATTRIBUTE_VERSION, \"1.0\");\n        final ApplicationImpl application = new ApplicationImpl(\"app\", \"1.0\", \"app desccription\");\n        application.setId(1);\n        application.setCreationDate(new Date());\n        application.setLastUpdateDate(new Date());\n        application.setVisibility(ApplicationVisibility.ALL);\n\n        given(applicationAPI.searchIApplications(any(SearchOptions.class)))\n                .willReturn(new SearchResultImpl<>(2, Arrays.<IApplication> asList(application)));\n\n        //when\n        final ItemSearchResult<AbstractApplicationItem> retrievedItems = dataStore.search(page, resultsByPage, search,\n                orders, filters);\n\n        //then\n        assertThat(retrievedItems.getLength()).isEqualTo(1);\n        assertThat(retrievedItems.getPage()).isEqualTo(0);\n        assertThat(retrievedItems.getTotal()).isEqualTo(2);\n        assertThat(retrievedItems.getResults().get(0).getToken()).isEqualTo(\"app\");\n\n    }\n\n    @Test(expected = APIException.class)\n    public void should_throw_APIException_on_search_when_engine_throws_exception() throws Exception {\n        //given\n        given(applicationAPI.searchIApplications(any(SearchOptions.class)))\n                .willThrow(new SearchException(new Exception()));\n\n        //when\n        final String orders = ApplicationItem.ATTRIBUTE_TOKEN + \" DESC\";\n        dataStore.search(1, 2, \"search\", orders, Collections.<String, String> emptyMap());\n\n        //then exception\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/application/ApplicationItemConverterTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.application;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.mock;\n\nimport java.io.Serializable;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.UUID;\n\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.console.common.server.utils.IconDescriptor;\nimport org.bonitasoft.engine.business.application.*;\nimport org.bonitasoft.engine.business.application.impl.ApplicationImpl;\nimport org.bonitasoft.web.rest.model.application.ApplicationItem;\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class ApplicationItemConverterTest extends APITestWithMock {\n\n    private static final String STATE = ApplicationState.DEACTIVATED.name();\n    private static final long UPDATED_BY = 12L;\n    private static final long CREATED_BY = 11;\n    private static final String ICON = UUID.randomUUID().toString();\n    private static final String DESCRIPTION = \"App description\";\n    private static final String VERSION = \"1.0\";\n    private static final String TOKEN = \"app\";\n    private static final String DISPLAY_NAME = \"display app name\";\n    private static final Date CREATION_DATE = new Date();\n    private static final Date UPDATE_DATE = new Date(CREATION_DATE.getTime() + 1000);\n    private static final long PROFILE_ID = 1L;\n    private static final long HOME_PAGE_ID = 2L;\n    private static final long LAYOUT_ID = 3L;\n    private static final long THEME_ID = 4L;\n    private static final ApplicationVisibility APPLICATION_VISIBILITY = ApplicationVisibility.RESTRICTED;\n\n    private ApplicationItemConverter converter;\n\n    private BonitaHomeFolderAccessor bonitaHomeFolderAccessor = mock(BonitaHomeFolderAccessor.class);\n\n    @Before\n    public void setUp() throws Exception {\n        converter = new ApplicationItemConverter(bonitaHomeFolderAccessor);\n    }\n\n    @Test\n    public void toApplicationItem_should_map_all_fields() throws Exception {\n        //given\n        final ApplicationImpl application = new ApplicationImpl(TOKEN, VERSION, DESCRIPTION, LAYOUT_ID, THEME_ID);\n        application.setId(15);\n        application.setDisplayName(DISPLAY_NAME);\n        application.setHasIcon(true);\n        application.setCreationDate(CREATION_DATE);\n        application.setCreatedBy(CREATED_BY);\n        application.setLastUpdateDate(UPDATE_DATE);\n        application.setUpdatedBy(UPDATED_BY);\n        application.setState(STATE);\n        application.setProfileId(PROFILE_ID);\n        application.setHomePageId(HOME_PAGE_ID);\n        application.setVisibility(APPLICATION_VISIBILITY);\n        application.setEditable(true);\n\n        //when\n        ApplicationItem item = (ApplicationItem) converter.toApplicationItem(application);\n\n        //then\n        assertThat(item).isNotNull();\n        assertThat(item.getId().toLong()).isEqualTo(15);\n        assertThat(item.getToken()).isEqualTo(TOKEN);\n        assertThat(item.getDisplayName()).isEqualTo(DISPLAY_NAME);\n        assertThat(item.getVersion()).isEqualTo(VERSION);\n        assertThat(item.getDescription()).isEqualTo(DESCRIPTION);\n        assertThat(item.getIcon()).isEqualTo(\"../API/applicationIcon/15?t=\" + UPDATE_DATE.getTime());\n        assertThat(item.getCreationDate()).isEqualTo(String.valueOf(CREATION_DATE.getTime()));\n        assertThat(item.getCreatedBy()).isEqualTo(CREATED_BY);\n        assertThat(item.getLastUpdateDate()).isEqualTo(String.valueOf(UPDATE_DATE.getTime()));\n        assertThat(item.getUpdatedBy()).isEqualTo(UPDATED_BY);\n        assertThat(item.getState()).isEqualTo(STATE);\n        assertThat(item.getProfileId().toLong()).isEqualTo(PROFILE_ID);\n        assertThat(item.getHomePageId().toLong()).isEqualTo(HOME_PAGE_ID);\n        assertThat(item.getLayoutId().toLong()).isEqualTo(LAYOUT_ID);\n        assertThat(item.getVisibility()).isEqualTo(APPLICATION_VISIBILITY.name());\n        assertThat(item.isEditable()).isEqualTo(true);\n\n        application.setHasIcon(false);\n\n        item = (ApplicationItem) converter.toApplicationItem(application);\n        assertThat(item.getIcon()).isEmpty();\n    }\n\n    @Test\n    public void applicationItem_with_null_homepage_id_should_not_return_null() throws Exception {\n        //given\n        final ApplicationImpl application = new ApplicationImpl(DISPLAY_NAME, VERSION, DESCRIPTION, LAYOUT_ID,\n                THEME_ID);\n        application.setId(15);\n        application.setDisplayName(DISPLAY_NAME);\n        application.setCreationDate(CREATION_DATE);\n        application.setCreatedBy(CREATED_BY);\n        application.setLastUpdateDate(UPDATE_DATE);\n        application.setUpdatedBy(UPDATED_BY);\n        application.setState(STATE);\n        application.setProfileId(PROFILE_ID);\n        application.setHomePageId(null);\n        application.setVisibility(APPLICATION_VISIBILITY);\n\n        //when\n        final ApplicationItem item = (ApplicationItem) converter.toApplicationItem(application);\n\n        //then\n        assertThat(item).isNotNull();\n        assertThat(item.getAttributeValue(ApplicationItem.ATTRIBUTE_HOME_PAGE_ID)).isEqualTo(\"-1\");\n    }\n\n    @Test\n    public void applicationItem_with_null_Layout_id_should_not_return_null() throws Exception {\n        //given\n        final ApplicationImpl application = new ApplicationImpl(DISPLAY_NAME, VERSION, DESCRIPTION, null, THEME_ID);\n        application.setId(15);\n        application.setDisplayName(DISPLAY_NAME);\n        application.setCreationDate(CREATION_DATE);\n        application.setCreatedBy(CREATED_BY);\n        application.setLastUpdateDate(UPDATE_DATE);\n        application.setUpdatedBy(UPDATED_BY);\n        application.setState(STATE);\n        application.setProfileId(PROFILE_ID);\n        application.setHomePageId(null);\n        application.setVisibility(APPLICATION_VISIBILITY);\n\n        //when\n        final ApplicationItem item = (ApplicationItem) converter.toApplicationItem(application);\n\n        //then\n        assertThat(item).isNotNull();\n        assertThat(item.getAttributeValue(ApplicationItem.ATTRIBUTE_LAYOUT_ID)).isEqualTo(\"-1\");\n    }\n\n    @Test\n    public void applicationItem_with_null_Theme_id_should_not_return_null() throws Exception {\n        //given\n        final ApplicationImpl application = new ApplicationImpl(DISPLAY_NAME, VERSION, DESCRIPTION, LAYOUT_ID, null);\n        application.setId(15);\n        application.setDisplayName(DISPLAY_NAME);\n        application.setCreationDate(CREATION_DATE);\n        application.setCreatedBy(CREATED_BY);\n        application.setLastUpdateDate(UPDATE_DATE);\n        application.setUpdatedBy(UPDATED_BY);\n        application.setState(STATE);\n        application.setProfileId(PROFILE_ID);\n        application.setHomePageId(null);\n        application.setVisibility(APPLICATION_VISIBILITY);\n\n        //when\n        final ApplicationItem item = (ApplicationItem) converter.toApplicationItem(application);\n\n        //then\n        assertThat(item).isNotNull();\n        assertThat(item.getAttributeValue(ApplicationItem.ATTRIBUTE_THEME_ID)).isEqualTo(\"-1\");\n    }\n\n    @Test\n    public void toApplicationCreator_should_map_all_fields() throws Exception {\n        //given\n        final ApplicationItem item = new ApplicationItem();\n        item.setToken(TOKEN);\n        item.setDisplayName(DISPLAY_NAME);\n        item.setVersion(VERSION);\n        item.setDescription(DESCRIPTION);\n        item.setProfileId(PROFILE_ID);\n\n        //when\n        final ApplicationCreator creator = converter.toApplicationCreator(item);\n\n        //then\n        assertThat(creator).isNotNull();\n        final Map<ApplicationField, Serializable> fields = creator.getFields();\n        assertThat(fields.get(ApplicationField.TOKEN)).isEqualTo(TOKEN);\n        assertThat(fields.get(ApplicationField.DISPLAY_NAME)).isEqualTo(DISPLAY_NAME);\n        assertThat(fields.get(ApplicationField.VERSION)).isEqualTo(VERSION);\n        assertThat(fields.get(ApplicationField.DESCRIPTION)).isEqualTo(DESCRIPTION);\n        assertThat(fields.get(ApplicationField.PROFILE_ID)).isEqualTo(PROFILE_ID);\n    }\n\n    @Test\n    public void toApplicationUpdater_should_map_all_fields() throws Exception {\n\n        //given\n        final HashMap<String, String> fields = new HashMap<>();\n        fields.put(ApplicationItem.ATTRIBUTE_TOKEN, TOKEN);\n        fields.put(ApplicationItem.ATTRIBUTE_DISPLAY_NAME, DISPLAY_NAME);\n        fields.put(ApplicationItem.ATTRIBUTE_DESCRIPTION, DESCRIPTION);\n        fields.put(ApplicationItem.ATTRIBUTE_VERSION, VERSION);\n        fields.put(ApplicationItem.ATTRIBUTE_ICON, ICON);\n        fields.put(ApplicationItem.ATTRIBUTE_PROFILE_ID, String.valueOf(PROFILE_ID));\n        fields.put(ApplicationItem.ATTRIBUTE_HOME_PAGE_ID, String.valueOf(HOME_PAGE_ID));\n        fields.put(ApplicationItem.ATTRIBUTE_STATE, STATE);\n\n        IconDescriptor iconDescriptor = new IconDescriptor(ICON, \"theContent\".getBytes());\n        doReturn(iconDescriptor).when(bonitaHomeFolderAccessor).getIconFromFileSystem(ICON);\n\n        //when\n        ApplicationUpdater updater = converter.toApplicationUpdater(fields);\n\n        //then\n        assertThat(updater).isNotNull();\n        assertThat(updater.getFields().get(ApplicationField.TOKEN)).isEqualTo(TOKEN);\n        assertThat(updater.getFields().get(ApplicationField.DISPLAY_NAME)).isEqualTo(DISPLAY_NAME);\n        assertThat(updater.getFields().get(ApplicationField.DESCRIPTION)).isEqualTo(DESCRIPTION);\n        assertThat(updater.getFields().get(ApplicationField.VERSION)).isEqualTo(VERSION);\n        assertThat(updater.getFields().get(ApplicationField.ICON_CONTENT)).isEqualTo(\"theContent\".getBytes());\n        assertThat(updater.getFields().get(ApplicationField.ICON_FILE_NAME)).isEqualTo(ICON);\n        assertThat(updater.getFields().get(ApplicationField.PROFILE_ID)).isEqualTo(PROFILE_ID);\n        assertThat(updater.getFields().get(ApplicationField.HOME_PAGE_ID)).isEqualTo(HOME_PAGE_ID);\n\n    }\n\n    @Test\n    public void update_with_no_homePageId_should_send_null() {\n\n        //given\n        final HashMap<String, String> fields = new HashMap<>();\n        fields.put(ApplicationItem.ATTRIBUTE_HOME_PAGE_ID, \"-1\");\n\n        //when\n        final ApplicationUpdater updater = converter.toApplicationUpdater(fields);\n\n        //then\n        assertThat(updater).isNotNull();\n        assertThat(updater.getFields().get(ApplicationField.HOME_PAGE_ID)).isEqualTo(null);\n\n    }\n\n    @Test\n    public void update_with_no_layout_page_should_send_null() {\n\n        //given\n        final HashMap<String, String> fields = new HashMap<>();\n        fields.put(ApplicationItem.ATTRIBUTE_LAYOUT_ID, \"-1\");\n\n        //when\n        final ApplicationUpdater updater = converter.toApplicationUpdater(fields);\n\n        //then\n        assertThat(updater).isNotNull();\n        assertThat(updater.getFields().get(ApplicationField.LAYOUT_ID)).isEqualTo(null);\n\n    }\n\n    @Test\n    public void update_with_no_Theme_page_should_send_null() {\n\n        //given\n        final HashMap<String, String> fields = new HashMap<>();\n        fields.put(ApplicationItem.ATTRIBUTE_THEME_ID, \"-1\");\n\n        //when\n        final ApplicationUpdater updater = converter.toApplicationUpdater(fields);\n\n        //then\n        assertThat(updater).isNotNull();\n        assertThat(updater.getFields().get(ApplicationField.THEME_ID)).isEqualTo(null);\n\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/application/ApplicationSearchDescriptorConverterTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.application;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.business.application.ApplicationSearchDescriptor;\nimport org.bonitasoft.web.rest.model.application.ApplicationItem;\nimport org.junit.Test;\n\npublic class ApplicationSearchDescriptorConverterTest {\n\n    private final ApplicationSearchDescriptorConverter converter = new ApplicationSearchDescriptorConverter();\n\n    @Test\n    public void should_return_ApplicationSearchDescriptor_id_on_convert_attribute_id() {\n        //when\n        final String value = converter.convert(ApplicationItem.ATTRIBUTE_ID);\n\n        //then\n        assertThat(value).isEqualTo(ApplicationSearchDescriptor.ID);\n    }\n\n    @Test\n    public void should_return_ApplicationSearchDescriptor_name_on_convert_attribute_name() {\n        //when\n        final String value = converter.convert(ApplicationItem.ATTRIBUTE_TOKEN);\n\n        //then\n        assertThat(value).isEqualTo(ApplicationSearchDescriptor.TOKEN);\n    }\n\n    @Test\n    public void should_return_ApplicationSearchDescriptor_name_on_convert_attribute_displayName() {\n        //when\n        final String value = converter.convert(ApplicationItem.ATTRIBUTE_DISPLAY_NAME);\n\n        //then\n        assertThat(value).isEqualTo(ApplicationSearchDescriptor.DISPLAY_NAME);\n    }\n\n    @Test\n    public void should_return_ApplicationSearchDescriptor_version_on_convert_attribute_version() {\n        //when\n        final String value = converter.convert(ApplicationItem.ATTRIBUTE_VERSION);\n\n        //then\n        assertThat(value).isEqualTo(ApplicationSearchDescriptor.VERSION);\n    }\n\n    @Test\n    public void should_return_ApplicationSearchDescriptor_creationDate_on_convert_attribute_creationDate() {\n        //when\n        final String value = converter.convert(ApplicationItem.ATTRIBUTE_CREATION_DATE);\n\n        //then\n        assertThat(value).isEqualTo(ApplicationSearchDescriptor.CREATION_DATE);\n    }\n\n    @Test\n    public void should_return_ApplicationSearchDescriptor_createdBy_on_convert_attribute_createdBy() {\n        //when\n        final String value = converter.convert(ApplicationItem.ATTRIBUTE_CREATED_BY);\n\n        //then\n        assertThat(value).isEqualTo(ApplicationSearchDescriptor.CREATED_BY);\n    }\n\n    @Test\n    public void should_return_ApplicationSearchDescriptor_lastUpdateDate_on_convert_attribute_lastUpdateDate() {\n        //when\n        final String value = converter.convert(ApplicationItem.ATTRIBUTE_LAST_UPDATE_DATE);\n\n        //then\n        assertThat(value).isEqualTo(ApplicationSearchDescriptor.LAST_UPDATE_DATE);\n    }\n\n    @Test\n    public void should_return_ApplicationSearchDescriptor_updatedBy_on_convert_attribute_updatedBy() {\n        //when\n        final String value = converter.convert(ApplicationItem.ATTRIBUTE_UPDATED_BY);\n\n        //then\n        assertThat(value).isEqualTo(ApplicationSearchDescriptor.UPDATED_BY);\n    }\n\n    @Test\n    public void should_return_ApplicationSearchDescriptor_state_on_convert_attribute_state() {\n        //when\n        final String value = converter.convert(ApplicationItem.ATTRIBUTE_STATE);\n\n        //then\n        assertThat(value).isEqualTo(ApplicationSearchDescriptor.STATE);\n    }\n\n    @Test\n    public void should_return_ApplicationSearchDescriptor_layoutid_on_convert_attribute_layoutid() {\n        //when\n        final String value = converter.convert(ApplicationItem.ATTRIBUTE_LAYOUT_ID);\n\n        //then\n        assertThat(value).isEqualTo(ApplicationSearchDescriptor.LAYOUT_ID);\n    }\n\n    @Test\n    public void should_return_ApplicationSearchDescriptor_themeid_on_convert_attribute_themeid() {\n        //when\n        final String value = converter.convert(ApplicationItem.ATTRIBUTE_THEME_ID);\n\n        //then\n        assertThat(value).isEqualTo(ApplicationSearchDescriptor.THEME_ID);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/applicationmenu/ApplicationMenuDataStoreTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.applicationmenu;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.ApplicationAPI;\nimport org.bonitasoft.engine.business.application.ApplicationMenu;\nimport org.bonitasoft.engine.business.application.ApplicationMenuNotFoundException;\nimport org.bonitasoft.engine.business.application.ApplicationMenuUpdater;\nimport org.bonitasoft.engine.business.application.impl.ApplicationMenuImpl;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.impl.SearchResultImpl;\nimport org.bonitasoft.web.rest.model.applicationmenu.ApplicationMenuDefinition;\nimport org.bonitasoft.web.rest.model.applicationmenu.ApplicationMenuItem;\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.ItemDefinitionFactory;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ApplicationMenuDataStoreTest extends APITestWithMock {\n\n    @Mock\n    private ApplicationAPI applicationAPI;\n\n    @Mock\n    private ApplicationMenuItemConverter converter;\n\n    @Mock\n    private ApplicationMenuUpdater applicationMenuUpdater;\n\n    @InjectMocks\n    private ApplicationMenuDataStore dataStore;\n\n    @Before\n    public void setUp() throws Exception {\n        ItemDefinitionFactory.setDefaultFactory(new ItemDefinitionFactory() {\n\n            @Override\n            public ItemDefinition<?> defineItemDefinitions(final String token) {\n                return new ApplicationMenuDefinition();\n            }\n        });\n\n    }\n\n    @Test\n    public void should_return_result_of_engine_call_converted_to_item_on_add() throws Exception {\n        //given\n        final ApplicationMenuItem itemToCreate = new ApplicationMenuItem();\n        final ApplicationMenuImpl applicationMenu = new ApplicationMenuImpl(\"firstMenu\", 11L, 14L, 1);\n        given(applicationAPI.createApplicationMenu(any())).willReturn(applicationMenu);\n\n        given(converter.toApplicationMenuItem(applicationMenu)).willReturn(new ApplicationMenuItem());\n        //when\n        final ApplicationMenuItem createdItem = dataStore.add(itemToCreate);\n\n        //then\n        assertThat(createdItem).isEqualTo(itemToCreate);\n\n    }\n\n    @Test(expected = APIException.class)\n    public void should_throw_APIException_when_engine_throws_CreationException_on_add() throws Exception {\n        //given\n        given(applicationAPI.createApplicationMenu(any()))\n                .willThrow(new CreationException(\"\"));\n\n        //when\n        dataStore.add(new ApplicationMenuItem());\n\n        //then exception\n    }\n\n    @Test\n    public void should_return_application_menu_updated_by_ApplicationAPI_and_converted_to_ApplicationItem_on_update()\n            throws Exception {\n        //given\n        final HashMap<String, String> attributesToUpDate = new HashMap<>();\n        given(converter.toApplicationMenuUpdater(any(Map.class))).willReturn(applicationMenuUpdater);\n\n        final ApplicationMenuImpl applicationMenu = new ApplicationMenuImpl(\"menu name\", 1L, 1L, 2);\n        given(applicationAPI.updateApplicationMenu(1, applicationMenuUpdater)).willReturn(applicationMenu);\n        final ApplicationMenuItem item = new ApplicationMenuItem();\n        given(converter.toApplicationMenuItem(applicationMenu)).willReturn(item);\n\n        //when\n        final ApplicationMenuItem createdItem = dataStore.update(APIID.makeAPIID(1L), attributesToUpDate);\n\n        //then\n        verify(converter, times(1)).toApplicationMenuUpdater(attributesToUpDate);\n        verify(applicationAPI, times(1)).updateApplicationMenu(1, applicationMenuUpdater);\n        verify(converter, times(1)).toApplicationMenuItem(applicationMenu);\n        assertThat(createdItem).isEqualTo(new ApplicationMenuItem());\n    }\n\n    @Test\n    public void should_return_the_ApplicationMenu_supplied_by_the_engine_converted_to_item_on_get() throws Exception {\n        //given\n        final ApplicationMenuItem itemToCreate = new ApplicationMenuItem();\n        final ApplicationMenuImpl applicationMenu = new ApplicationMenuImpl(\"firstMenu\", 11L, 14L, 1);\n        given(applicationAPI.getApplicationMenu(1L)).willReturn(applicationMenu);\n        given(converter.toApplicationMenuItem(applicationMenu)).willReturn(new ApplicationMenuItem());\n\n        //when\n        final ApplicationMenuItem createdItem = dataStore.get(APIID.makeAPIID(1L));\n\n        //then\n        assertThat(createdItem).isEqualTo(itemToCreate);\n    }\n\n    @Test(expected = APIException.class)\n    public void should_throw_APIException_when_the_engine_throw_NotFoundException_on_get() throws Exception {\n        //given\n        given(applicationAPI.getApplicationMenu(1)).willThrow(new ApplicationMenuNotFoundException(\"\"));\n\n        //when\n        dataStore.get(APIID.makeAPIID(\"1\"));\n\n        //then exception\n    }\n\n    @Test\n    public void should_delete_the_good_Application_Page_on_delete() throws Exception {\n        //given\n\n        //when\n        dataStore.delete(Arrays.<APIID> asList(APIID.makeAPIID(\"1\"), APIID.makeAPIID(\"2\")));\n\n        //then\n        verify(applicationAPI, times(1)).deleteApplicationMenu(1);\n        verify(applicationAPI, times(1)).deleteApplicationMenu(2);\n    }\n\n    @Test(expected = APIException.class)\n    public void should_throw_APIException_on_delete_when_engine_throws_exception() throws Exception {\n        doThrow(new DeletionException(\"\")).when(applicationAPI).deleteApplicationMenu(1);\n\n        //when\n        dataStore.delete(Arrays.<APIID> asList(APIID.makeAPIID(\"1\")));\n\n        //then exception\n    }\n\n    @Test\n    public void should_return_a_valid_ItemSearchResult_on_search() throws Exception {\n        //given\n        final String orders = ApplicationMenuItem.ATTRIBUTE_DISPLAY_NAME + \" DESC\";\n        final ApplicationMenuImpl appMenu = new ApplicationMenuImpl(\"MyMenu\", 11L, 2L, 1);\n        appMenu.setParentId(-1L);\n\n        given(applicationAPI.searchApplicationMenus(any(SearchOptions.class))).willReturn(\n                new SearchResultImpl<>(2, Arrays.<ApplicationMenu> asList(appMenu)));\n\n        //when\n        final ItemSearchResult<ApplicationMenuItem> retrievedItems = dataStore.search(0, 1, null, orders,\n                Collections.<String, String> emptyMap());\n\n        //then\n        assertThat(retrievedItems).isNotNull();\n        assertThat(retrievedItems.getLength()).isEqualTo(1);\n        assertThat(retrievedItems.getPage()).isEqualTo(0);\n        assertThat(retrievedItems.getTotal()).isEqualTo(2);\n        assertThat(retrievedItems.getResults().get(0).getDisplayName()).isEqualTo(\"MyMenu\");\n\n    }\n\n    @Test\n    public void should_call_engine_with_good_parameters_on_search() throws Exception {\n        //given\n        final int page = 0;\n        final int resultsByPage = 1;\n        final String search = \"string to Match\";\n        final String orders = ApplicationMenuItem.ATTRIBUTE_DISPLAY_NAME + \" DESC\";\n        final HashMap<String, String> filters = new HashMap<>();\n        filters.put(ApplicationMenuItem.ATTRIBUTE_APPLICATION_PAGE_ID, \"1\");\n        final ApplicationMenuImpl appPage = new ApplicationMenuImpl(\"MyMenu\", 11L, 1L, 2);\n        appPage.setParentId(-1L);\n\n        given(applicationAPI.searchApplicationMenus(any(SearchOptions.class))).willReturn(\n                new SearchResultImpl<>(2, Arrays.<ApplicationMenu> asList(appPage)));\n\n        //when\n        dataStore.search(page, resultsByPage, search, orders, filters);\n\n        //then\n        final ArgumentCaptor<SearchOptions> captor = ArgumentCaptor.forClass(SearchOptions.class);\n        verify(applicationAPI, times(1)).searchApplicationMenus(captor.capture());\n\n        final SearchOptions searchOption = captor.getValue();\n        assertThat(searchOption.getFilters()).hasSize(1);\n        assertThat(searchOption.getFilters().get(0).getField())\n                .isEqualTo(ApplicationMenuItem.ATTRIBUTE_APPLICATION_PAGE_ID);\n        assertThat(searchOption.getFilters().get(0).getValue()).isEqualTo(\"1\");\n        assertThat(searchOption.getSearchTerm()).isEqualTo(search);\n        assertThat(searchOption.getMaxResults()).isEqualTo(1);\n        assertThat(searchOption.getStartIndex()).isEqualTo(0);\n        assertThat(searchOption.getSorts()).hasSize(1);\n        assertThat(searchOption.getSorts().get(0).getField()).isEqualTo(ApplicationMenuItem.ATTRIBUTE_DISPLAY_NAME);\n        assertThat(searchOption.getSorts().get(0).getOrder()).isEqualTo(Order.DESC);\n\n    }\n\n    @Test(expected = APIException.class)\n    public void should_throw_APIException_when_engine_throws_SearchException_on_search() throws Exception {\n        //given\n        final String orders = ApplicationMenuItem.ATTRIBUTE_DISPLAY_NAME + \" DESC\";\n        given(applicationAPI.searchApplicationMenus(any(SearchOptions.class))).willThrow(new SearchException(null));\n\n        //when\n        dataStore.search(0, 1, null, orders, Collections.<String, String> emptyMap());\n\n        //then exception\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/applicationmenu/ApplicationMenuFilterCreatorTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.applicationmenu;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\n\nimport java.io.Serializable;\n\nimport org.bonitasoft.web.rest.server.datastore.filter.Filter;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ApplicationMenuFilterCreatorTest {\n\n    @Mock\n    private ApplicationMenuSearchDescriptorConverter converter;\n\n    @InjectMocks\n    private ApplicationMenuFilterCreator creator;\n\n    @Test\n    public void should_return_filter_based_on_given_field_and_value_on_create() throws Exception {\n        //given\n        given(converter.convert(\"name\")).willReturn(\"name\");\n\n        //when\n        final Filter<? extends Serializable> filter = creator.create(\"name\", \"a name\");\n\n        //then\n        assertThat(filter).isNotNull();\n        assertThat(filter.getField()).isEqualTo(\"name\");\n        assertThat(filter.getValue()).isEqualTo(\"a name\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/applicationmenu/ApplicationMenuItemConverterTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.applicationmenu;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.business.application.ApplicationMenuCreator;\nimport org.bonitasoft.engine.business.application.ApplicationMenuField;\nimport org.bonitasoft.engine.business.application.ApplicationMenuUpdater;\nimport org.bonitasoft.engine.business.application.impl.ApplicationMenuImpl;\nimport org.bonitasoft.web.rest.model.applicationmenu.ApplicationMenuItem;\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class ApplicationMenuItemConverterTest extends APITestWithMock {\n\n    private static final String DISPLAY_NAME = \"menu name\";\n    private static final long APPLICATION_ID = 1L;\n    private static final long APPLICATION_PAGE_ID = 2L;\n    private static final long PARENT_MENU_ID = 3L;\n    private static final int INDEX = 4;\n\n    private static final long MENU_ID = 10L;\n\n    private ApplicationMenuItemConverter converter;\n\n    @Before\n    public void setUp() throws Exception {\n        converter = new ApplicationMenuItemConverter();\n    }\n\n    @Test\n    public void toApplicationMenuItem_should_map_all_fields() throws Exception {\n        //given\n        final ApplicationMenuImpl applicationMenu = new ApplicationMenuImpl(DISPLAY_NAME, APPLICATION_ID,\n                APPLICATION_PAGE_ID, INDEX);\n        applicationMenu.setParentId(PARENT_MENU_ID);\n        applicationMenu.setId(MENU_ID);\n\n        //when\n        final ApplicationMenuItem item = converter.toApplicationMenuItem(applicationMenu);\n\n        //then\n        assertThat(item).isNotNull();\n        assertThat(item.getId().toLong()).isEqualTo(MENU_ID);\n        assertThat(item.getDisplayName()).isEqualTo(DISPLAY_NAME);\n        assertThat(item.getApplicationId().toLong()).isEqualTo(APPLICATION_ID);\n        assertThat(item.getApplicationPageId().toLong()).isEqualTo(APPLICATION_PAGE_ID);\n        assertThat(item.getParentMenuId().toLong()).isEqualTo(PARENT_MENU_ID);\n        assertThat(item.getMenuIndex()).isEqualTo(INDEX);\n    }\n\n    @Test\n    public void applicationMenuItem_with_null_parent_id_should_not_return_null() throws Exception {\n        //given\n        final ApplicationMenuImpl applicationMenu = new ApplicationMenuImpl(DISPLAY_NAME, APPLICATION_ID,\n                APPLICATION_PAGE_ID, INDEX);\n        applicationMenu.setParentId(null);\n        applicationMenu.setId(MENU_ID);\n\n        //when\n        final ApplicationMenuItem item = converter.toApplicationMenuItem(applicationMenu);\n\n        //then\n        assertThat(item.getAttributeValue(ApplicationMenuItem.ATTRIBUTE_PARENT_MENU_ID)).isEqualTo(\"-1\");\n    }\n\n    @Test\n    public void applicationMenuItem_with_null_page_id_should_not_return_null() throws Exception {\n        //given\n        final ApplicationMenuImpl applicationMenu = new ApplicationMenuImpl(DISPLAY_NAME, APPLICATION_ID, null, INDEX);\n        applicationMenu.setParentId(PARENT_MENU_ID);\n        applicationMenu.setId(MENU_ID);\n\n        //when\n        final ApplicationMenuItem item = converter.toApplicationMenuItem(applicationMenu);\n\n        //then\n        assertThat(item).isNotNull();\n        assertThat(item.getAttributeValue(ApplicationMenuItem.ATTRIBUTE_APPLICATION_PAGE_ID)).isEqualTo(\"-1\");\n    }\n\n    @Test\n    public void toApplicationMenuCreator_should_map_all_fields() throws Exception {\n        //given\n        final ApplicationMenuItem item = new ApplicationMenuItem();\n        item.setDisplayName(DISPLAY_NAME);\n        item.setApplicationId(APPLICATION_ID);\n        item.setApplicationPageId(APPLICATION_PAGE_ID);\n        item.setMenuIndex(INDEX);\n        item.setParentMenuId(PARENT_MENU_ID);\n\n        //when\n        final ApplicationMenuCreator creator = converter.toApplicationMenuCreator(item);\n\n        //then\n        assertThat(creator).isNotNull();\n        final Map<ApplicationMenuField, Serializable> fields = creator.getFields();\n        assertThat(fields.get(ApplicationMenuField.DISPLAY_NAME)).isEqualTo(DISPLAY_NAME);\n        assertThat(fields.get(ApplicationMenuField.APPLICATION_ID)).isEqualTo(APPLICATION_ID);\n        assertThat(fields.get(ApplicationMenuField.APPLICATION_PAGE_ID)).isEqualTo(APPLICATION_PAGE_ID);\n        assertThat(fields.get(ApplicationMenuField.PARENT_ID)).isEqualTo(PARENT_MENU_ID);\n    }\n\n    @Test\n    public void toApplicationMenuCreator_should_not_map_negativeId() throws Exception {\n        //given\n        final ApplicationMenuItem item = new ApplicationMenuItem();\n        item.setDisplayName(DISPLAY_NAME);\n        item.setApplicationId(APPLICATION_ID);\n        item.setApplicationPageId(-1L);\n        item.setMenuIndex(INDEX);\n        item.setParentMenuId(-1L);\n\n        //when\n        final ApplicationMenuCreator creator = converter.toApplicationMenuCreator(item);\n\n        //then\n        assertThat(creator).isNotNull();\n        final Map<ApplicationMenuField, Serializable> fields = creator.getFields();\n        assertThat(fields.get(ApplicationMenuField.DISPLAY_NAME)).isEqualTo(DISPLAY_NAME);\n        assertThat(fields.get(ApplicationMenuField.APPLICATION_ID)).isEqualTo(APPLICATION_ID);\n        assertThat(fields.get(ApplicationMenuField.APPLICATION_PAGE_ID)).isNull();\n        assertThat(fields.get(ApplicationMenuField.PARENT_ID)).isNull();\n    }\n\n    @Test\n    public void toApplicationMenuUpdater_should_map_all_fields() {\n\n        //given\n        final HashMap<String, String> fields = new HashMap<>();\n        fields.put(ApplicationMenuItem.ATTRIBUTE_APPLICATION_ID, String.valueOf(APPLICATION_ID));\n        fields.put(ApplicationMenuItem.ATTRIBUTE_APPLICATION_PAGE_ID, String.valueOf(APPLICATION_PAGE_ID));\n        fields.put(ApplicationMenuItem.ATTRIBUTE_DISPLAY_NAME, DISPLAY_NAME);\n        fields.put(ApplicationMenuItem.ATTRIBUTE_MENU_INDEX, String.valueOf(INDEX));\n        fields.put(ApplicationMenuItem.ATTRIBUTE_PARENT_MENU_ID, String.valueOf(PARENT_MENU_ID));\n\n        //when\n        final ApplicationMenuUpdater updater = converter.toApplicationMenuUpdater(fields);\n\n        //then\n        assertThat(updater).isNotNull();\n        assertThat(updater.getFields().get(ApplicationMenuField.DISPLAY_NAME)).isEqualTo(DISPLAY_NAME);\n        assertThat(updater.getFields().get(ApplicationMenuField.APPLICATION_ID)).isEqualTo(null);\n        assertThat(updater.getFields().get(ApplicationMenuField.APPLICATION_PAGE_ID)).isEqualTo(APPLICATION_PAGE_ID);\n        assertThat(updater.getFields().get(ApplicationMenuField.INDEX)).isEqualTo(INDEX);\n        assertThat(updater.getFields().get(ApplicationMenuField.PARENT_ID)).isEqualTo(PARENT_MENU_ID);\n\n    }\n\n    @Test\n    public void update_with_no_pageId_should_send_null() {\n\n        //given\n        final HashMap<String, String> fields = new HashMap<>();\n        fields.put(ApplicationMenuItem.ATTRIBUTE_APPLICATION_ID, \"-1\");\n\n        //when\n        final ApplicationMenuUpdater updater = converter.toApplicationMenuUpdater(fields);\n\n        //then\n        assertThat(updater).isNotNull();\n        assertThat(updater.getFields().get(ApplicationMenuField.APPLICATION_ID)).isEqualTo(null);\n\n    }\n\n    @Test\n    public void update_with_no_parent_menu_should_send_null() {\n\n        //given\n        final HashMap<String, String> fields = new HashMap<>();\n        fields.put(ApplicationMenuItem.ATTRIBUTE_APPLICATION_PAGE_ID, \"-1\");\n\n        //when\n        final ApplicationMenuUpdater updater = converter.toApplicationMenuUpdater(fields);\n\n        //then\n        assertThat(updater).isNotNull();\n        assertThat(updater.getFields().get(ApplicationMenuField.PARENT_ID)).isEqualTo(null);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/applicationmenu/ApplicationMenuSearchDescriptorConverterTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.applicationmenu;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.business.application.ApplicationMenuSearchDescriptor;\nimport org.bonitasoft.web.rest.model.applicationmenu.ApplicationMenuItem;\nimport org.junit.Test;\n\npublic class ApplicationMenuSearchDescriptorConverterTest {\n\n    private final ApplicationMenuSearchDescriptorConverter converter = new ApplicationMenuSearchDescriptorConverter();\n\n    @Test\n    public void should_return_ApplicationMenuSearchDescriptor_id_on_convert_attribute_id() throws Exception {\n        //when\n        final String convertedValue = converter.convert(ApplicationMenuItem.ATTRIBUTE_ID);\n\n        //then\n        assertThat(convertedValue).isEqualTo(ApplicationMenuSearchDescriptor.ID);\n    }\n\n    @Test\n    public void should_return_ApplicationMenuSearchDescriptor_display_name_on_convert_attribute_display_name()\n            throws Exception {\n        //when\n        final String convertedValue = converter.convert(ApplicationMenuItem.ATTRIBUTE_DISPLAY_NAME);\n\n        //then\n        assertThat(convertedValue).isEqualTo(ApplicationMenuSearchDescriptor.DISPLAY_NAME);\n    }\n\n    @Test\n    public void should_return_ApplicationMenuSearchDescriptor_applicationPageId_on_convert_attribute_applicationPageId()\n            throws Exception {\n        //when\n        final String convertedValue = converter.convert(ApplicationMenuItem.ATTRIBUTE_APPLICATION_PAGE_ID);\n\n        //then\n        assertThat(convertedValue).isEqualTo(ApplicationMenuSearchDescriptor.APPLICATION_PAGE_ID);\n    }\n\n    @Test\n    public void should_return_ApplicationMenuSearchDescriptor_applicationId_on_convert_attribute_applicationId()\n            throws Exception {\n        //when\n        final String convertedValue = converter.convert(ApplicationMenuItem.ATTRIBUTE_APPLICATION_ID);\n\n        //then\n        assertThat(convertedValue).isEqualTo(ApplicationMenuSearchDescriptor.APPLICATION_ID);\n    }\n\n    @Test\n    public void should_return_ApplicationMenuSearchDescriptor_index_on_convert_attribute_menu_index() throws Exception {\n        //when\n        final String convertedValue = converter.convert(ApplicationMenuItem.ATTRIBUTE_MENU_INDEX);\n\n        //then\n        assertThat(convertedValue).isEqualTo(ApplicationMenuSearchDescriptor.INDEX);\n    }\n\n    @Test\n    public void should_return_ApplicationMenuSearchDescriptor_index_on_convert_attribute_parent_menu()\n            throws Exception {\n        //when\n        final String convertedValue = converter.convert(ApplicationMenuItem.ATTRIBUTE_PARENT_MENU_ID);\n\n        //then\n        assertThat(convertedValue).isEqualTo(ApplicationMenuSearchDescriptor.PARENT_ID);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/applicationpage/ApplicationPageDataStoreTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.applicationpage;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.ApplicationAPI;\nimport org.bonitasoft.engine.business.application.ApplicationPage;\nimport org.bonitasoft.engine.business.application.ApplicationPageNotFoundException;\nimport org.bonitasoft.engine.business.application.impl.ApplicationPageImpl;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.impl.SearchResultImpl;\nimport org.bonitasoft.web.rest.model.applicationpage.ApplicationPageDefinition;\nimport org.bonitasoft.web.rest.model.applicationpage.ApplicationPageItem;\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.ItemDefinitionFactory;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ApplicationPageDataStoreTest extends APITestWithMock {\n\n    @Mock\n    private ApplicationAPI applicationAPI;\n\n    @Mock\n    private ApplicationPageItemConverter converter;\n\n    @Spy\n    @InjectMocks\n    private ApplicationPageDataStore dataStore;\n\n    @Before\n    public void setUp() throws Exception {\n        ItemDefinitionFactory.setDefaultFactory(new ItemDefinitionFactory() {\n\n            @Override\n            public ItemDefinition<?> defineItemDefinitions(final String token) {\n                return new ApplicationPageDefinition();\n            }\n        });\n    }\n\n    @Test\n    public void should_return_result_of_engine_call_converted_to_item_on_add() throws Exception {\n        //given\n        final ApplicationPageItem itemToCreate = new ApplicationPageItem();\n        itemToCreate.setToken(\"firstPage\");\n        itemToCreate.setApplicationId(14L);\n        itemToCreate.setPageId(28L);\n        final ApplicationPageImpl applicationPage = new ApplicationPageImpl(14L, 28L, \"firstPage\");\n\n        given(applicationAPI.createApplicationPage(14L, 28L, \"firstPage\")).willReturn(applicationPage);\n\n        //when\n        final ApplicationPageItem createdItem = dataStore.add(itemToCreate);\n\n        //then\n        assertThat(createdItem).isEqualTo(createdItem);\n    }\n\n    @Test(expected = APIException.class)\n    public void should_throw_APIException_when_engine_throws_CreationException_on_add() throws Exception {\n        //given\n        ApplicationPageItem applicationPageItem = new ApplicationPageItem();\n        applicationPageItem.setApplicationId(1L);\n        applicationPageItem.setPageId(1L);\n        applicationPageItem.setToken(\"token\");\n        doThrow(new CreationException(\"test\")).when(applicationAPI).createApplicationPage(anyLong(), anyLong(),\n                anyString());\n        //when\n        dataStore.add(applicationPageItem);\n\n        //then exception\n    }\n\n    @Test\n    public void should_return_the_applicationPage_supplied_by_the_engine_converted_to_item_on_get() throws Exception {\n        //given\n        final ApplicationPage applicationPage = mock(ApplicationPage.class);\n        final ApplicationPageItem item = mock(ApplicationPageItem.class);\n        given(applicationAPI.getApplicationPage(1)).willReturn(applicationPage);\n        given(converter.toApplicationPageItem(applicationPage)).willReturn(item);\n\n        //when\n        final ApplicationPageItem retrievedItem = dataStore.get(APIID.makeAPIID(\"1\"));\n\n        //then\n        assertThat(retrievedItem).isNotNull();\n        assertThat(retrievedItem).isEqualTo(item);\n    }\n\n    @Test(expected = APIException.class)\n    public void should_throw_APIException_when_the_engine_throw_NotFoundException_on_get() throws Exception {\n        //given\n        given(applicationAPI.getApplicationPage(1)).willThrow(new ApplicationPageNotFoundException(\"\"));\n\n        //when\n        dataStore.get(APIID.makeAPIID(\"1\"));\n\n        //then exception\n    }\n\n    @Test\n    public void should_delete_the_good_Application_Page_on_delete() throws Exception {\n        //given\n\n        //when\n        dataStore.delete(Arrays.asList(APIID.makeAPIID(\"1\"), APIID.makeAPIID(\"2\")));\n\n        //then\n        verify(applicationAPI, times(1)).deleteApplicationPage(1);\n        verify(applicationAPI, times(1)).deleteApplicationPage(2);\n    }\n\n    @Test(expected = APIException.class)\n    public void should_throw_APIException_on_delete_when_engine_throws_exception() throws Exception {\n        doThrow(new DeletionException(\"\")).when(applicationAPI).deleteApplicationPage(1);\n\n        //when\n        dataStore.delete(List.of(APIID.makeAPIID(\"1\")));\n\n        //then exception\n    }\n\n    @Test\n    public void should_return_a_valid_ItemSearchResult_on_search() throws Exception {\n        //given\n        final String orders = ApplicationPageItem.ATTRIBUTE_TOKEN + \" DESC\";\n        final ApplicationPageImpl appPage = new ApplicationPageImpl(1, 11, \"MyAppPage\");\n        appPage.setId(1);\n        final ApplicationPageItem item = new ApplicationPageItem();\n        item.setApplicationId(1L);\n        item.setPageId(1L);\n        item.setToken(\"MyAppPage\");\n        given(converter.toApplicationPageItem(appPage)).willReturn(item);\n\n        given(applicationAPI.searchApplicationPages(any(SearchOptions.class))).willReturn(\n                new SearchResultImpl<>(2, List.of(appPage)));\n\n        //when\n        final ItemSearchResult<ApplicationPageItem> retrievedItems = dataStore.search(0, 1, null, orders,\n                Collections.emptyMap());\n\n        //then\n        assertThat(retrievedItems).isNotNull();\n        assertThat(retrievedItems.getLength()).isEqualTo(1);\n        assertThat(retrievedItems.getPage()).isEqualTo(0);\n        assertThat(retrievedItems.getTotal()).isEqualTo(2);\n        assertThat(retrievedItems.getResults().get(0).getToken()).isEqualTo(\"MyAppPage\");\n\n    }\n\n    @Test\n    public void should_call_engine_with_good_parameters_on_search() throws Exception {\n        //given\n        final int page = 0;\n        final int resultsByPage = 1;\n        final String search = \"string to Match\";\n        final String orders = ApplicationPageItem.ATTRIBUTE_TOKEN + \" DESC\";\n        final HashMap<String, String> filters = new HashMap<>();\n        filters.put(ApplicationPageItem.ATTRIBUTE_APPLICATION_ID, \"1\");\n        final ApplicationPageImpl appPage = new ApplicationPageImpl(1, 11, \"MyAppPage\");\n        appPage.setId(1);\n        final ApplicationPageItem item = new ApplicationPageItem();\n        item.setApplicationId(1L);\n        item.setPageId(1L);\n        item.setToken(\"token\");\n        doReturn(item).when(converter).toApplicationPageItem(appPage);\n\n        given(applicationAPI.searchApplicationPages(any(SearchOptions.class))).willReturn(\n                new SearchResultImpl<>(2, List.of(appPage)));\n\n        //when\n        dataStore.search(page, resultsByPage, search, orders, filters);\n\n        //then\n        final ArgumentCaptor<SearchOptions> captor = ArgumentCaptor.forClass(SearchOptions.class);\n        verify(applicationAPI, times(1)).searchApplicationPages(captor.capture());\n\n        final SearchOptions searchOption = captor.getValue();\n        assertThat(searchOption.getFilters()).hasSize(1);\n        assertThat(searchOption.getFilters().get(0).getField()).isEqualTo(ApplicationPageItem.ATTRIBUTE_APPLICATION_ID);\n        assertThat(searchOption.getFilters().get(0).getValue()).isEqualTo(\"1\");\n        assertThat(searchOption.getSearchTerm()).isEqualTo(search);\n        assertThat(searchOption.getMaxResults()).isEqualTo(1);\n        assertThat(searchOption.getStartIndex()).isEqualTo(0);\n        assertThat(searchOption.getSorts()).hasSize(1);\n        assertThat(searchOption.getSorts().get(0).getField()).isEqualTo(ApplicationPageItem.ATTRIBUTE_TOKEN);\n        assertThat(searchOption.getSorts().get(0).getOrder()).isEqualTo(Order.DESC);\n\n    }\n\n    @Test(expected = APIException.class)\n    public void should_throw_APIException_when_engine_throws_SearchException_on_search() throws Exception {\n        //given\n        final String orders = ApplicationPageItem.ATTRIBUTE_TOKEN + \" DESC\";\n        given(applicationAPI.searchApplicationPages(any(SearchOptions.class))).willThrow(new SearchException(null));\n\n        //when\n        dataStore.search(0, 1, null, orders, Collections.emptyMap());\n\n        //then exception\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/applicationpage/ApplicationPageFilterCreatorTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.applicationpage;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\n\nimport java.io.Serializable;\n\nimport org.bonitasoft.web.rest.server.datastore.filter.Filter;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ApplicationPageFilterCreatorTest {\n\n    @Mock\n    private ApplicationPageSearchDescriptorConverter converter;\n\n    @InjectMocks\n    private ApplicationPageFilterCreator creator;\n\n    @Test\n    public void should_return_filter_based_on_given_field_and_value_on_create() throws Exception {\n        //given\n        given(converter.convert(\"name\")).willReturn(\"name\");\n\n        //when\n        final Filter<? extends Serializable> filter = creator.create(\"name\", \"a name\");\n\n        //then\n        assertThat(filter).isNotNull();\n        assertThat(filter.getField()).isEqualTo(\"name\");\n        assertThat(filter.getValue()).isEqualTo(\"a name\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/applicationpage/ApplicationPageSearchDescriptorConverterTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.applicationpage;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.business.application.ApplicationPageSearchDescriptor;\nimport org.bonitasoft.web.rest.model.applicationpage.ApplicationPageItem;\nimport org.junit.Test;\n\npublic class ApplicationPageSearchDescriptorConverterTest {\n\n    private final ApplicationPageSearchDescriptorConverter converter = new ApplicationPageSearchDescriptorConverter();\n\n    @Test\n    public void should_return_ApplicationPageSearchDescriptor_id_on_convert_attribute_id() throws Exception {\n        //when\n        final String convertedValue = converter.convert(ApplicationPageItem.ATTRIBUTE_ID);\n\n        //then\n        assertThat(convertedValue).isEqualTo(ApplicationPageSearchDescriptor.ID);\n    }\n\n    @Test\n    public void should_return_ApplicationPageSearchDescriptor_name_on_convert_attribute_name() throws Exception {\n        //when\n        final String convertedValue = converter.convert(ApplicationPageItem.ATTRIBUTE_TOKEN);\n\n        //then\n        assertThat(convertedValue).isEqualTo(ApplicationPageSearchDescriptor.TOKEN);\n    }\n\n    @Test\n    public void should_return_ApplicationPageSearchDescriptor_applicationId_on_convert_attribute_applicationId()\n            throws Exception {\n        //when\n        final String convertedValue = converter.convert(ApplicationPageItem.ATTRIBUTE_APPLICATION_ID);\n\n        //then\n        assertThat(convertedValue).isEqualTo(ApplicationPageSearchDescriptor.APPLICATION_ID);\n    }\n\n    @Test\n    public void should_return_ApplicationPageSearchDescriptor_pageId_on_convert_attribute_pageId() throws Exception {\n        //when\n        final String convertedValue = converter.convert(ApplicationPageItem.ATTRIBUTE_PAGE_ID);\n\n        //then\n        assertThat(convertedValue).isEqualTo(ApplicationPageSearchDescriptor.PAGE_ID);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/ArchivedCaseDatastoreTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.cases;\n\nimport static java.util.Collections.emptyMap;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.anyList;\nimport static org.mockito.ArgumentMatchers.anyLong;\nimport static org.mockito.Mockito.*;\nimport static org.mockito.MockitoAnnotations.initMocks;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstanceNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.web.rest.model.bpm.cases.ArchivedCaseItem;\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ArchivedCaseDatastoreTest extends APITestWithMock {\n\n    private ArchivedCaseDatastore datastore;\n\n    @Mock\n    private ProcessAPI processAPI;\n\n    @Mock\n    private ArchivedProcessInstance archivedProcessInstance1;\n    @Mock\n    private ArchivedProcessInstance archivedProcessInstance2;\n    @Mock\n    private ArchivedProcessInstance archivedProcessInstance3;\n\n    private long archivedProcessInstanceId1;\n\n    private long archivedProcessInstanceId2;\n\n    private long archivedProcessInstanceId3;\n\n    private long sourceProcessInstanceId1;\n\n    private long sourceProcessInstanceId2;\n\n    private long sourceProcessInstanceId3;\n\n    @Before\n    public void initializeMocks() throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException,\n            ArchivedProcessInstanceNotFoundException {\n        initMocks(this);\n        datastore = spy(new ArchivedCaseDatastore(null));\n        doReturn(processAPI).when(datastore).getProcessApi();\n\n        archivedProcessInstanceId1 = 1L;\n        archivedProcessInstanceId2 = 2L;\n        archivedProcessInstanceId3 = 3L;\n        sourceProcessInstanceId1 = 11L;\n        sourceProcessInstanceId2 = 12L;\n        sourceProcessInstanceId3 = 13L;\n\n        doReturn(sourceProcessInstanceId1).when(archivedProcessInstance1).getSourceObjectId();\n        doReturn(sourceProcessInstanceId2).when(archivedProcessInstance2).getSourceObjectId();\n        doReturn(sourceProcessInstanceId3).when(archivedProcessInstance3).getSourceObjectId();\n\n        doReturn(archivedProcessInstance1).when(processAPI).getArchivedProcessInstance(archivedProcessInstanceId1);\n        doReturn(archivedProcessInstance2).when(processAPI).getArchivedProcessInstance(archivedProcessInstanceId2);\n        doReturn(archivedProcessInstance3).when(processAPI).getArchivedProcessInstance(archivedProcessInstanceId3);\n    }\n\n    @Test\n    public void search_should_pass_long_parameters_as_long_to_engine_and_not_as_string() {\n        // given:\n        doReturn(mock(SearchResult.class)).when(datastore).runSearch(any(), any());\n\n        // when:\n        datastore.search(0, 10, null, null, emptyMap());\n\n        // then:\n        verify(datastore).addLongFilterToSearchBuilder(any(), any(), anyString(),\n                eq(ProcessInstanceSearchDescriptor.PROCESS_DEFINITION_ID));\n        verify(datastore).addLongFilterToSearchBuilder(any(), any(), anyString(),\n                eq(ProcessInstanceSearchDescriptor.STARTED_BY));\n        verify(datastore).addLongFilterToSearchBuilder(any(), any(), anyString(),\n                eq(ArchivedProcessInstancesSearchDescriptor.SOURCE_OBJECT_ID));\n    }\n\n    @Test\n    public void should_delete_archive_case_call_right_engine_method()\n            throws DeletionException, ArchivedProcessInstanceNotFoundException {\n        //given\n\n        final List<APIID> idList = Arrays.asList(APIID.makeAPIID(archivedProcessInstanceId1),\n                APIID.makeAPIID(archivedProcessInstanceId2),\n                APIID.makeAPIID(archivedProcessInstanceId3));\n\n        //when\n        datastore.delete(idList);\n\n        //then\n        verify(processAPI, times(3)).getArchivedProcessInstance(anyLong());\n        verify(processAPI).deleteArchivedProcessInstancesInAllStates(\n                Arrays.asList(\n                        sourceProcessInstanceId1,\n                        sourceProcessInstanceId2,\n                        sourceProcessInstanceId3));\n    }\n\n    @Test\n    public void should_delete_all_archived_cases_when_one_id_is_given() throws DeletionException,\n            ArchivedProcessInstanceNotFoundException {\n        //given\n        final List<APIID> idList = Collections.singletonList(APIID.makeAPIID(archivedProcessInstanceId1));\n\n        //when\n        datastore.delete(idList);\n\n        //then\n        verify(processAPI).getArchivedProcessInstance(archivedProcessInstanceId1);\n        verify(processAPI, times(1)).getArchivedProcessInstance(archivedProcessInstanceId1);\n        verify(processAPI)\n                .deleteArchivedProcessInstancesInAllStates(Collections.singletonList(sourceProcessInstanceId1));\n    }\n\n    @Test(expected = APIException.class)\n    public void should_throw_an_api_exception_when_deletion_exception_is_rised() throws DeletionException {\n        //given\n        doThrow(new DeletionException(\"exception!\")).when(processAPI)\n                .deleteArchivedProcessInstancesInAllStates(anyList());\n        final List<APIID> idList = Collections.singletonList(APIID.makeAPIID(archivedProcessInstanceId1));\n\n        //when\n        datastore.delete(idList);\n\n    }\n\n    @Test\n    public void testConvertEngineToConsoleItem() {\n        //given\n        ArchivedProcessInstance archivedProcessInstance = mock(ArchivedProcessInstance.class);\n        doReturn(\"labelOne\").when(archivedProcessInstance).getStringIndexLabel(1);\n        doReturn(\"labelTwo\").when(archivedProcessInstance).getStringIndexLabel(2);\n        doReturn(\"labelThree\").when(archivedProcessInstance).getStringIndexLabel(3);\n        doReturn(\"labelFour\").when(archivedProcessInstance).getStringIndexLabel(4);\n        doReturn(\"labelFive\").when(archivedProcessInstance).getStringIndexLabel(5);\n        doReturn(\"valueOne\").when(archivedProcessInstance).getStringIndexValue(1);\n        doReturn(\"valueTwo\").when(archivedProcessInstance).getStringIndexValue(2);\n        doReturn(\"valueThree\").when(archivedProcessInstance).getStringIndexValue(3);\n        doReturn(\"valueFour\").when(archivedProcessInstance).getStringIndexValue(4);\n        doReturn(\"valueFive\").when(archivedProcessInstance).getStringIndexValue(5);\n\n        // when\n        final ArchivedCaseItem archivedCaseItem = datastore.convertEngineToConsoleItem(archivedProcessInstance);\n\n        // then\n        //check labels\n        assertThat(archivedCaseItem.getSearchIndex1Label()).isEqualTo(\"labelOne\");\n        assertThat(archivedCaseItem.getSearchIndex2Label()).isEqualTo(\"labelTwo\");\n        assertThat(archivedCaseItem.getSearchIndex3Label()).isEqualTo(\"labelThree\");\n        assertThat(archivedCaseItem.getSearchIndex4Label()).isEqualTo(\"labelFour\");\n        assertThat(archivedCaseItem.getSearchIndex5Label()).isEqualTo(\"labelFive\");\n        //check values\n        assertThat(archivedCaseItem.getSearchIndex1Value()).isEqualTo(\"valueOne\");\n        assertThat(archivedCaseItem.getSearchIndex2Value()).isEqualTo(\"valueTwo\");\n        assertThat(archivedCaseItem.getSearchIndex3Value()).isEqualTo(\"valueThree\");\n        assertThat(archivedCaseItem.getSearchIndex4Value()).isEqualTo(\"valueFour\");\n        assertThat(archivedCaseItem.getSearchIndex5Value()).isEqualTo(\"valueFive\");\n    }\n\n    @Test\n    public void testConvertEngineToConsoleItemWithNullValues() {\n        //given\n        ArchivedProcessInstance archivedProcessInstance = mock(ArchivedProcessInstance.class);\n        doReturn(null).when(archivedProcessInstance).getStringIndexLabel(1);\n        doReturn(null).when(archivedProcessInstance).getStringIndexValue(1);\n\n        // when\n        final ArchivedCaseItem archivedCaseItem = datastore.convertEngineToConsoleItem(archivedProcessInstance);\n\n        // then\n        //check labels\n        assertThat(archivedCaseItem.getSearchIndex1Label()).isNull();\n        assertThat(archivedCaseItem.getSearchIndex1Value()).isNull();\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/ArchivedCaseDocumentDatastoreTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.cases;\n\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\nimport static org.mockito.MockitoAnnotations.initMocks;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils;\nimport org.bonitasoft.console.common.server.preferences.properties.ConfigurationFilesManager;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.document.ArchivedDocument;\nimport org.bonitasoft.engine.bpm.document.ArchivedDocumentNotFoundException;\nimport org.bonitasoft.engine.bpm.document.DocumentException;\nimport org.bonitasoft.engine.bpm.document.DocumentNotFoundException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.cases.ArchivedCaseDocumentItem;\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseDocumentItem;\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ArchivedCaseDocumentDatastoreTest extends APITestWithMock {\n\n    private ArchivedCaseDocumentDatastore documentDatastore;\n\n    @Mock\n    private WebBonitaConstantsUtils constantsValue;\n\n    @Mock\n    private APISession engineSession;\n\n    @Mock\n    private ProcessAPI processAPI;\n\n    @Mock\n    private ArchivedDocument mockedDocument;\n\n    @Mock\n    private SearchResult<ArchivedDocument> mockedEngineSearchResults;\n\n    @Rule\n    public ExpectedException expectedEx = ExpectedException.none();\n\n    @Before\n    public void setUp() throws Exception {\n        ConfigurationFilesManager.getInstance()\n                .setTenantConfigurationFiles(Collections.singletonMap(\"console-config.properties\",\n                        \"form.attachment.max.size=30\".getBytes()));\n        initMocks(this);\n        when(mockedDocument.getName()).thenReturn(\"Doc 1\");\n        when(mockedDocument.getId()).thenReturn(1L);\n        documentDatastore = spy(new ArchivedCaseDocumentDatastore(engineSession, processAPI));\n    }\n\n    // ---------- GET METHOD TESTS ------------------------------//\n\n    @Test\n    public void it_should_call_engine_processAPI_getDocument() throws Exception {\n        // Given\n        final APIID id = APIID.makeAPIID(1l);\n\n        // When\n        documentDatastore.get(id);\n\n        // Then\n        verify(processAPI).getArchivedProcessDocument(id.toLong());\n    }\n\n    @Test(expected = APIException.class)\n    public void it_should_catch_and_throw_APIException_for_not_find_document()\n            throws ArchivedDocumentNotFoundException {\n        // Given\n        final APIID id = APIID.makeAPIID(1l);\n        when(processAPI.getArchivedProcessDocument(id.toLong()))\n                .thenThrow(new ArchivedDocumentNotFoundException(new Exception()));\n\n        // When\n        documentDatastore.get(id);\n    }\n\n    @Test\n    public void it_should_call_convertEngineToConsole_method() {\n        // Given\n        final APIID id = APIID.makeAPIID(1l);\n\n        // When\n        documentDatastore.get(id);\n\n        // Then\n        verify(documentDatastore).convertEngineToConsoleItem(any());\n    }\n\n    // ---------- CONVERT ITEM TESTS ------------------------------//\n\n    @Test\n    public void it_should_convert_item_return_item() {\n        // When\n        final ArchivedCaseDocumentItem convertedEngineToConsoleItem = documentDatastore\n                .convertEngineToConsoleItem(mockedDocument);\n        // Then\n        assertTrue(convertedEngineToConsoleItem != null);\n    }\n\n    @Test\n    public void it_should_not_convert_null_item_return_null() {\n        // When\n        final CaseDocumentItem convertedEngineToConsoleItem = documentDatastore.convertEngineToConsoleItem(null);\n        // Then\n        assertTrue(convertedEngineToConsoleItem == null);\n    }\n\n    // ---------- SEARCH TESTS -------------------------------------------------//\n    @Test\n    public void it_should_call_buildSearchOptionCreator_method() throws SearchException {\n        // Given\n        when(processAPI.searchArchivedDocuments(any())).thenReturn(mockedEngineSearchResults);\n        final Map<String, String> filters = new HashMap<>();\n        filters.put(\"submittedBy\", \"1\");\n\n        // When\n        documentDatastore.searchDocument(0, 10, \"hello\", filters, \"name ASC\");\n\n        // Then\n        verify(documentDatastore).buildSearchOptionCreator(0, 10, \"hello\", filters, \"name ASC\");\n    }\n\n    @Test\n    public void it_should_call_processAPI_searchDocuments_method() throws SearchException {\n        // Given\n        when(processAPI.searchArchivedDocuments(any(SearchOptions.class))).thenReturn(mockedEngineSearchResults);\n        final Map<String, String> filters = new HashMap<>();\n        filters.put(\"submittedBy\", \"1\");\n\n        // When\n        documentDatastore.searchDocument(0, 10, \"hello\", filters, \"name ASC\");\n\n        // Then\n        verify(processAPI).searchArchivedDocuments(documentDatastore.searchOptionsCreator.create());\n    }\n\n    // -------------DELETE METHOD TESTS ------------------------------------------//\n    @Test\n    public void it_should_delete_one_document() throws DocumentNotFoundException, DocumentException {\n        final List<APIID> docs = new ArrayList<>();\n        docs.add(APIID.makeAPIID(mockedDocument.getId()));\n\n        // When\n        documentDatastore.delete(docs);\n\n        // Then\n        verify(processAPI).deleteContentOfArchivedDocument(1L);\n        verify(processAPI, times(1)).deleteContentOfArchivedDocument(any(Long.class));\n    }\n\n    @Test\n    public void it_should_delete_two_documents() throws DocumentNotFoundException, DocumentException {\n        final List<APIID> docs = new ArrayList<>();\n        docs.add(APIID.makeAPIID(mockedDocument.getId()));\n        docs.add(APIID.makeAPIID(mockedDocument.getId()));\n\n        // When\n        documentDatastore.delete(docs);\n\n        // Then\n        verify(processAPI, times(2)).deleteContentOfArchivedDocument(1L);\n    }\n\n    @Test\n    public void it_should_throw_an_exception_when_input_is_null() throws DocumentNotFoundException, DeletionException {\n        expectedEx.expect(APIException.class);\n        expectedEx.expectMessage(\"Error while deleting a document. Document id not specified in the request\");\n\n        // When\n        documentDatastore.delete(null);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/ArchivedCaseDocumentItemConverterTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.cases;\n\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.bpm.document.ArchivedDocument;\nimport org.bonitasoft.web.rest.model.bpm.cases.ArchivedCaseDocumentItem;\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ArchivedCaseDocumentItemConverterTest extends APITestWithMock {\n\n    @Test\n    public void should_convert_engine_document_into_portal_document() {\n\n        // Given\n        final ArchivedCaseDocumentItemConverter documentItemConverter = new ArchivedCaseDocumentItemConverter();\n        final ArchivedDocument engineItem = mock(ArchivedDocument.class);\n        when(engineItem.getId()).thenReturn(1l);\n        when(engineItem.getProcessInstanceId()).thenReturn(1l);\n        when(engineItem.getName()).thenReturn(\"Doc 1\");\n        when(engineItem.getAuthor()).thenReturn(1l);\n        when(engineItem.getContentFileName()).thenReturn(\"doc.jpg\");\n        when(engineItem.getCreationDate()).thenReturn(new Date());\n        when(engineItem.getContentMimeType()).thenReturn(\"image\");\n        when(engineItem.hasContent()).thenReturn(true);\n        when(engineItem.getContentStorageId()).thenReturn(\"1\");\n        when(engineItem.getUrl()).thenReturn(\"http://url.com?test=d\");\n        when(engineItem.getSourceObjectId()).thenReturn(1l);\n\n        // When\n        final ArchivedCaseDocumentItem documentItem = documentItemConverter.convert(engineItem);\n\n        // Assert\n        assertTrue(documentItem.getId().equals(1l));\n        assertTrue(documentItem.getCaseId().equals(1l));\n        assertTrue(documentItem.getName().equals(\"Doc 1\"));\n        assertTrue(documentItem.getSubmittedBy().equals(1l));\n        assertTrue(documentItem.getFileName().equals(\"doc.jpg\"));\n        assertTrue(documentItem.getCreationDate().equals(engineItem.getCreationDate()));\n        assertTrue(documentItem.getMIMEType().equals(\"image\"));\n        assertTrue(documentItem.hasContent());\n        assertTrue(documentItem.getStorageId().equals(\"1\"));\n        assertTrue(documentItem.getURL().equals(\"http://url.com?test=d\"));\n        assertTrue(documentItem.getSourceObjectId().equals(1l));\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/CaseDatastoreTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.cases;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.process.ProcessInstanceState;\nimport org.bonitasoft.engine.bpm.process.impl.internal.ProcessInstanceImpl;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.search.impl.SearchFilter;\nimport org.bonitasoft.engine.search.impl.SearchResultImpl;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseItem;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class CaseDatastoreTest {\n\n    private CaseDatastore caseDatastore;\n\n    @Mock\n    private ProcessAPI processAPI;\n\n    @Before\n    public void setUp() throws Exception {\n        caseDatastore = spy(new CaseDatastore(mock(APISession.class)));\n        doReturn(processAPI).when(caseDatastore).getProcessAPI();\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore#search(int, int, String,\n     * String, Map).\n     */\n    @Test\n    public final void search_should_search_process_instances_and_convert_them_to_CaseItem() throws SearchException {\n        // Given\n        final int page = 0;\n        final int resultsByPage = 1;\n        final String search = \"plop\";\n        final String orders = CaseItem.ATTRIBUTE_ID;\n        final Map<String, String> filters = Collections.emptyMap();\n\n        final ProcessInstance processInstance = new ProcessInstanceImpl(\"name\");\n        doReturn(new SearchResultImpl<>(1L, Arrays.asList(processInstance))).when(processAPI).searchProcessInstances(\n                any(SearchOptions.class));\n        final CaseItem caseItem = new CaseItem();\n        doReturn(caseItem).when(caseDatastore).convertEngineToConsoleItem(processInstance);\n\n        // When\n        final ItemSearchResult<CaseItem> result = caseDatastore.search(page, resultsByPage, search, orders, filters);\n\n        // Then\n        verify(processAPI).searchProcessInstances(any(SearchOptions.class));\n        verify(caseDatastore).addCallerFilterToSearchBuilderIfNecessary(eq(filters), any(SearchOptionsBuilder.class));\n        final List<CaseItem> caseItems = result.getResults();\n        assertEquals(1, caseItems.size());\n        assertEquals(caseItem, caseItems.get(0));\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore#search(int, int, String,\n     * String, Map).\n     */\n    @Test\n    public final void search_should_throw_an_exception_when_search_process_instances_failed() throws SearchException {\n        // Given\n        final int page = 0;\n        final int resultsByPage = 1;\n        final String search = \"plop\";\n        final String orders = CaseItem.ATTRIBUTE_END_DATE;\n        final Map<String, String> filters = Collections.singletonMap(CaseItem.FILTER_STATE, \"started\");\n\n        doThrow(new SearchException(new Exception(\"toto\"))).when(processAPI)\n                .searchProcessInstances(any(SearchOptions.class));\n\n        try {\n            // When\n            caseDatastore.search(page, resultsByPage, search, orders, filters);\n        } catch (final APIException e) {\n            // Then\n            assertTrue(e.getCause() instanceof SearchException);\n        } finally {\n            // Then\n            verify(processAPI).searchProcessInstances(any(SearchOptions.class));\n            verify(caseDatastore).addCallerFilterToSearchBuilderIfNecessary(eq(filters),\n                    any(SearchOptionsBuilder.class));\n        }\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore#search(int, int, String,\n     * String, Map).\n     */\n    @Test\n    public final void search_should_search_failed_process_instances_and_convert_them_to_CaseItem_when_filter_on_failed_state()\n            throws SearchException {\n        search_should_search_failed_process_instances_and_convert_them_to_CaseItem_when_filter_on_state(\"failed\");\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore#search(int, int, String,\n     * String, Map).\n     */\n    @Test\n    public final void search_should_throw_an_exception_when_search_failed_process_instances_failed_and_filter_on_failed_state()\n            throws SearchException {\n        search_should_throw_an_exception_when_search_failed_process_instances_failed_and_filter_on_state(\"failed\");\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore#search(int, int, String,\n     * String, Map).\n     */\n    @Test\n    public final void search_should_search_failed_process_instances_and_convert_them_to_CaseItem_when_filter_on_error_state()\n            throws SearchException {\n        search_should_search_failed_process_instances_and_convert_them_to_CaseItem_when_filter_on_state(\"error\");\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore#search(int, int, String,\n     * String, Map).\n     */\n    @Test\n    public final void search_should_search_failed_process_instances_and_convert_them_to_CaseItem_when_filter_on_error_of_ProcessInstanceState_state()\n            throws SearchException {\n        search_should_search_failed_process_instances_and_convert_them_to_CaseItem_when_filter_on_state(\n                ProcessInstanceState.ERROR.name());\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore#search(int, int, String,\n     * String, Map).\n     */\n    @Test\n    public final void search_should_throw_an_exception_when_search_failed_process_instances_failed_and_filter_on_error_state()\n            throws SearchException {\n        search_should_throw_an_exception_when_search_failed_process_instances_failed_and_filter_on_state(\"error\");\n    }\n\n    private void search_should_search_failed_process_instances_and_convert_them_to_CaseItem_when_filter_on_state(\n            final String state) throws SearchException {\n        // Given\n        final int page = 0;\n        final int resultsByPage = 1;\n        final String search = \"plop\";\n        final String orders = CaseItem.ATTRIBUTE_ID;\n        final Map<String, String> filters = Collections.singletonMap(CaseItem.FILTER_STATE, state);\n\n        final ProcessInstance processInstance = new ProcessInstanceImpl(\"name\");\n        doReturn(new SearchResultImpl<>(1L, Arrays.asList(processInstance))).when(processAPI)\n                .searchFailedProcessInstances(\n                        any(SearchOptions.class));\n        final CaseItem caseItem = new CaseItem();\n        doReturn(caseItem).when(caseDatastore).convertEngineToConsoleItem(processInstance);\n\n        // When\n        final ItemSearchResult<CaseItem> result = caseDatastore.search(page, resultsByPage, search, orders, filters);\n\n        // Then\n        verify(processAPI).searchFailedProcessInstances(any(SearchOptions.class));\n        verify(caseDatastore).addCallerFilterToSearchBuilderIfNecessary(eq(filters), any(SearchOptionsBuilder.class));\n        final List<CaseItem> caseItems = result.getResults();\n        assertEquals(1, caseItems.size());\n        assertEquals(caseItem, caseItems.get(0));\n    }\n\n    private void search_should_throw_an_exception_when_search_failed_process_instances_failed_and_filter_on_state(\n            final String state) throws SearchException {\n        // Given\n        final int page = 0;\n        final int resultsByPage = 1;\n        final String search = \"plop\";\n        final String orders = CaseItem.ATTRIBUTE_END_DATE;\n        final Map<String, String> filters = Collections.singletonMap(CaseItem.FILTER_STATE, state);\n\n        doThrow(new SearchException(new Exception(\"toto\"))).when(processAPI)\n                .searchFailedProcessInstances(any(SearchOptions.class));\n\n        try {\n            // When\n            caseDatastore.search(page, resultsByPage, search, orders, filters);\n        } catch (final APIException e) {\n            // Then\n            assertTrue(e.getCause() instanceof SearchException);\n        } finally {\n            // Then\n            verify(processAPI).searchFailedProcessInstances(any(SearchOptions.class));\n            verify(caseDatastore).addCallerFilterToSearchBuilderIfNecessary(eq(filters),\n                    any(SearchOptionsBuilder.class));\n        }\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore#search(int, int, String,\n     * String, Map).\n     */\n    @Test\n    public final void search_should_search_open_process_instances_involving_user_and_convert_them_to_CaseItem_when_filter_on_user()\n            throws SearchException {\n        // Given\n        final int page = 0;\n        final int resultsByPage = 1;\n        final String search = \"plop\";\n        final String orders = CaseItem.ATTRIBUTE_ID;\n        final Map<String, String> filters = new HashMap<>();\n        filters.put(CaseItem.FILTER_USER_ID, \"9\");\n        filters.put(CaseItem.ATTRIBUTE_STATE, \"plop\");\n\n        final ProcessInstance processInstance = new ProcessInstanceImpl(\"name\");\n        doReturn(new SearchResultImpl<>(1L, Arrays.asList(processInstance))).when(processAPI)\n                .searchOpenProcessInstancesInvolvingUser(eq(9L),\n                        any(SearchOptions.class));\n        final CaseItem caseItem = new CaseItem();\n        doReturn(caseItem).when(caseDatastore).convertEngineToConsoleItem(processInstance);\n\n        // When\n        final ItemSearchResult<CaseItem> result = caseDatastore.search(page, resultsByPage, search, orders, filters);\n\n        // Then\n        verify(processAPI).searchOpenProcessInstancesInvolvingUser(eq(9L), any(SearchOptions.class));\n        verify(caseDatastore).addCallerFilterToSearchBuilderIfNecessary(eq(filters), any(SearchOptionsBuilder.class));\n        final List<CaseItem> caseItems = result.getResults();\n        assertEquals(1, caseItems.size());\n        assertEquals(caseItem, caseItems.get(0));\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore#search(int, int, String,\n     * String, Map).\n     */\n    @Test\n    public final void search_should_throw_an_exception_when_search_open_process_instances_involving_user_filter_on_user()\n            throws SearchException {\n        // Given\n        final int page = 0;\n        final int resultsByPage = 1;\n        final String search = \"plop\";\n        final String orders = CaseItem.ATTRIBUTE_END_DATE;\n        final Map<String, String> filters = new HashMap<>();\n        filters.put(CaseItem.FILTER_USER_ID, \"9\");\n        filters.put(CaseItem.ATTRIBUTE_STATE, \"plop\");\n\n        doThrow(new SearchException(new Exception(\"toto\"))).when(processAPI)\n                .searchOpenProcessInstancesInvolvingUser(eq(9L), any(SearchOptions.class));\n\n        try {\n            // When\n            caseDatastore.search(page, resultsByPage, search, orders, filters);\n        } catch (final APIException e) {\n            // Then\n            assertTrue(e.getCause() instanceof SearchException);\n        } finally {\n            // Then\n            verify(processAPI).searchOpenProcessInstancesInvolvingUser(eq(9L), any(SearchOptions.class));\n            verify(caseDatastore).addCallerFilterToSearchBuilderIfNecessary(eq(filters),\n                    any(SearchOptionsBuilder.class));\n        }\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore#search(int, int, String,\n     * String, Map).\n     */\n    @Test\n    public final void search_should_search_open_process_instances_supervised_by_and_convert_them_to_CaseItem_when_filter_on_supervisor()\n            throws SearchException {\n        // Given\n        final int page = 0;\n        final int resultsByPage = 1;\n        final String search = \"plop\";\n        final String orders = CaseItem.ATTRIBUTE_ID;\n        final Map<String, String> filters = new HashMap<>();\n        filters.put(CaseItem.FILTER_SUPERVISOR_ID, \"9\");\n        filters.put(CaseItem.ATTRIBUTE_STATE, \"plop\");\n\n        final ProcessInstance processInstance = new ProcessInstanceImpl(\"name\");\n        doReturn(new SearchResultImpl<>(1L, Arrays.asList(processInstance))).when(processAPI)\n                .searchOpenProcessInstancesSupervisedBy(eq(9L),\n                        any(SearchOptions.class));\n        final CaseItem caseItem = new CaseItem();\n        doReturn(caseItem).when(caseDatastore).convertEngineToConsoleItem(processInstance);\n\n        // When\n        final ItemSearchResult<CaseItem> result = caseDatastore.search(page, resultsByPage, search, orders, filters);\n\n        // Then\n        verify(processAPI).searchOpenProcessInstancesSupervisedBy(eq(9L), any(SearchOptions.class));\n        verify(caseDatastore).addCallerFilterToSearchBuilderIfNecessary(eq(filters), any(SearchOptionsBuilder.class));\n        final List<CaseItem> caseItems = result.getResults();\n        assertEquals(1, caseItems.size());\n        assertEquals(caseItem, caseItems.get(0));\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore#search(int, int, String,\n     * String, Map).\n     */\n    @Test\n    public final void search_should_throw_an_exception_when_search_open_process_instances_supervised_by_filter_on_supervisor()\n            throws SearchException {\n        // Given\n        final int page = 0;\n        final int resultsByPage = 1;\n        final String search = \"plop\";\n        final String orders = CaseItem.ATTRIBUTE_END_DATE;\n        final Map<String, String> filters = new HashMap<>();\n        filters.put(CaseItem.FILTER_SUPERVISOR_ID, \"9\");\n        filters.put(CaseItem.ATTRIBUTE_STATE, \"plop\");\n\n        doThrow(new SearchException(new Exception(\"toto\"))).when(processAPI)\n                .searchOpenProcessInstancesSupervisedBy(eq(9L), any(SearchOptions.class));\n\n        try {\n            // When\n            caseDatastore.search(page, resultsByPage, search, orders, filters);\n        } catch (final APIException e) {\n            // Then\n            assertTrue(e.getCause() instanceof SearchException);\n        } finally {\n            // Then\n            verify(processAPI).searchOpenProcessInstancesSupervisedBy(eq(9L), any(SearchOptions.class));\n            verify(caseDatastore).addCallerFilterToSearchBuilderIfNecessary(eq(filters),\n                    any(SearchOptionsBuilder.class));\n        }\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore#addCallerFilterToSearchBuilderIfNecessary(Map,\n     * SearchOptionsBuilder).\n     */\n    @Test\n    public final void addCallerFilterToSearchBuilderIfNecessary_should_add_caller_filter_to_builder_when_no_caller_filter() {\n        // Given\n        final Map<String, String> filters = Collections.emptyMap();\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n\n        // When\n        caseDatastore.addCallerFilterToSearchBuilderIfNecessary(filters, builder);\n\n        // Then\n        final SearchFilter searchFilter = builder.done().getFilters().get(0);\n        assertEquals(ProcessInstanceSearchDescriptor.CALLER_ID, searchFilter.getField());\n        assertEquals(-1, searchFilter.getValue());\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore#addCallerFilterToSearchBuilderIfNecessary(Map,\n     * SearchOptionsBuilder).\n     */\n    @Test\n    public final void addCallerFilterToSearchBuilderIfNecessary_should_add_caller_filter_to_builder_when_filter_on_caller_with_value_different_of_any() {\n        // Given\n        final Map<String, String> filters = Collections.singletonMap(CaseItem.FILTER_CALLER, \"9\");\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n\n        // When\n        caseDatastore.addCallerFilterToSearchBuilderIfNecessary(filters, builder);\n\n        // Then\n        final SearchFilter searchFilter = builder.done().getFilters().get(0);\n        assertEquals(ProcessInstanceSearchDescriptor.CALLER_ID, searchFilter.getField());\n        assertEquals(9L, searchFilter.getValue());\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore#addCallerFilterToSearchBuilderIfNecessary(Map,\n     * SearchOptionsBuilder).\n     */\n    @Test\n    public final void addCallerFilterToSearchBuilderIfNecessary_should_do_nothing_when_filter_on_any_caller() {\n        // Given\n        final Map<String, String> filters = Collections.singletonMap(CaseItem.FILTER_CALLER, \"any\");\n        final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10);\n\n        // When\n        caseDatastore.addCallerFilterToSearchBuilderIfNecessary(filters, builder);\n\n        // Then\n        assertTrue(builder.done().getFilters().isEmpty());\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore#count(String, String,\n     * Map).\n     */\n    @Test\n    public final void count_should_return_total_of_search() {\n        // Given\n        final String search = \"plop\";\n        final String orders = CaseItem.ATTRIBUTE_ID;\n        final Map<String, String> filters = Collections.emptyMap();\n        final long total = 7L;\n        final ItemSearchResult<IItem> itemSearchResult = new ItemSearchResult<>(0, 0, total, null);\n        doReturn(itemSearchResult).when(caseDatastore).search(0, 0, search, orders, filters);\n\n        // When\n        final long result = caseDatastore.count(search, orders, filters);\n\n        // Then\n        assertEquals(total, result);\n    }\n\n    @SuppressWarnings({ \"rawtypes\", \"unchecked\" })\n    @Test\n    public void searchProcessInstances_With_PM_Filter_And_Failed_State_should_call_API() throws Exception {\n        // Given\n        final Map<String, String> filters = new HashMap<>();\n        final SearchOptions searchOptions = mock(SearchOptions.class);\n        final long userId = 9L;\n        filters.put(CaseItem.FILTER_SUPERVISOR_ID, String.valueOf(userId));\n        filters.put(CaseItem.ATTRIBUTE_STATE, \"failed\");\n        final SearchOptionsBuilder searchOptionsBuilder = mock(SearchOptionsBuilder.class);\n        final SearchResult searchResult = mock(SearchResult.class);\n        when(searchOptionsBuilder.done()).thenReturn(searchOptions);\n        doReturn(searchOptionsBuilder).when(caseDatastore).buildSearchOptions(0, 1, \"\", \"\", filters);\n        final ItemSearchResult itemSearchResult = mock(ItemSearchResult.class);\n        doReturn(itemSearchResult).when(caseDatastore).convertEngineToConsoleSearch(0, 1, searchResult);\n\n        when(processAPI.searchFailedProcessInstancesSupervisedBy(userId, searchOptions)).thenReturn(searchResult);\n        // when\n        final ItemSearchResult<CaseItem> caseSearchResult = caseDatastore.search(0, 1, \"\", \"\", filters);\n\n        // Then\n        verify(processAPI).searchFailedProcessInstancesSupervisedBy(userId, searchOptions);\n        assertThat(itemSearchResult).isSameAs(caseSearchResult);\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore#update}.\n     */\n    @Test\n    public void update_should_cancel_instance_and_return_null_when_state_is_cancelled() throws Exception {\n        // Given\n        final APIID id = APIID.makeAPIID(42L);\n        final Map<String, String> attributes = new HashMap<>();\n        attributes.put(CaseItem.ATTRIBUTE_STATE, \"CANCELLED\");\n\n        // When\n        final CaseItem result = caseDatastore.update(id, attributes);\n\n        // Then\n        verify(processAPI).cancelProcessInstance(42L);\n        assertThat(result).isNull();\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore#update}.\n     */\n    @Test(expected = APIException.class)\n    public void update_should_throw_APIException_when_state_is_missing() {\n        // Given\n        final APIID id = APIID.makeAPIID(43L);\n        final Map<String, String> attributes = new HashMap<>(); // no state\n\n        // When\n        caseDatastore.update(id, attributes);\n\n        // Then (exception expected)\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore#update}.\n     */\n    @Test(expected = APIException.class)\n    public void update_should_throw_APIException_when_state_is_not_cancelled() {\n        // Given\n        final APIID id = APIID.makeAPIID(44L);\n        final Map<String, String> attributes = new HashMap<>();\n        attributes.put(CaseItem.ATTRIBUTE_STATE, \"STARTED\"); // anything != CANCELLED\n\n        // When\n        caseDatastore.update(id, attributes);\n\n        // Then (exception expected)\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/CaseDocumentDatastoreTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.cases;\n\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.*;\nimport static org.mockito.MockitoAnnotations.initMocks;\n\nimport java.io.FileNotFoundException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.document.Document;\nimport org.bonitasoft.engine.bpm.document.DocumentNotFoundException;\nimport org.bonitasoft.engine.bpm.document.DocumentValue;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.io.FileContent;\nimport org.bonitasoft.engine.io.TemporaryFileNotFoundException;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseDocumentItem;\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class CaseDocumentDatastoreTest extends APITestWithMock {\n\n    private CaseDocumentDatastore documentDatastore;\n\n    @Mock\n    private APISession engineSession;\n\n    @Mock\n    private ProcessAPI processAPI;\n\n    @Mock\n    private Document mockedDocument;\n\n    @Mock\n    private BonitaHomeFolderAccessor tenantFolder;\n\n    @Mock\n    private SearchResult<Document> mockedEngineSearchResults;\n\n    @Rule\n    public ExpectedException expectedEx = ExpectedException.none();\n\n    private final CaseDocumentItem mockedDocumentItem = new CaseDocumentItem();\n\n    @Before\n    public void setUp() throws Exception {\n        initMocks(this);\n        when(mockedDocument.getName()).thenReturn(\"Doc 1\");\n        when(mockedDocument.getId()).thenReturn(1L);\n        documentDatastore = spy(new CaseDocumentDatastore(engineSession, processAPI, tenantFolder));\n    }\n\n    // ---------- GET METHOD TESTS ------------------------------//\n\n    @Test\n    public void it_should_call_engine_processAPI_getDocument() throws Exception {\n        // Given\n        final APIID id = APIID.makeAPIID(1L);\n\n        // When\n        documentDatastore.get(id);\n\n        // Then\n        verify(processAPI).getDocument(id.toLong());\n    }\n\n    @Test(expected = APIException.class)\n    public void it_should_catch_and_throw_APIException_for_not_find_document() throws Exception {\n        // Given\n        final APIID id = APIID.makeAPIID(1L);\n        when(processAPI.getDocument(id.toLong()))\n                .thenThrow(new DocumentNotFoundException(\"not found\", new Exception()));\n\n        // When\n        documentDatastore.get(id);\n    }\n\n    @Test\n    public void it_should_call_convertEngineToConsole_method() {\n        // Given\n        final APIID id = APIID.makeAPIID(1L);\n\n        // When\n        documentDatastore.get(id);\n\n        // Then\n        verify(documentDatastore).convertEngineToConsoleItem(any());\n    }\n\n    // ---------- CONVERT ITEM TESTS ------------------------------//\n\n    @Test\n    public void it_should_convert_item_return_item() {\n        // When\n        final CaseDocumentItem convertedEngineToConsoleItem = documentDatastore\n                .convertEngineToConsoleItem(mockedDocument);\n        // Then\n        assertTrue(convertedEngineToConsoleItem != null);\n    }\n\n    @Test\n    public void it_should_not_convert_null_item_return_null() {\n        // When\n        final CaseDocumentItem convertedEngineToConsoleItem = documentDatastore.convertEngineToConsoleItem(null);\n        // Then\n        assertTrue(convertedEngineToConsoleItem == null);\n    }\n\n    // ---------- buildDocumentValueFromUploadPath TESTS ------------------------------//\n    @Test\n    public void it_should_create_documentvalue_with_given_filename() throws Exception {\n        String uploadKey = \"3544697\";\n        when(tenantFolder.retrieveUploadedTempContent(uploadKey))\n                .thenReturn(new FileContent(\"doc.jpg\", InputStream.nullInputStream(), \"img/jpg\"));\n\n        // When\n        final DocumentValue documentValue = documentDatastore.buildDocumentValueFromUploadPath(uploadKey, 1,\n                \"fileName\");\n        // Then\n        assertTrue(documentValue.getFileName().equals(\"fileName\"));\n    }\n\n    @Test\n    public void it_should_create_documentvalue_with_name_of_the_uploaded_file() throws Exception {\n        String uploadKey = \"46645\";\n        when(tenantFolder.retrieveUploadedTempContent(uploadKey))\n                .thenReturn(new FileContent(\"doc.jpg\", InputStream.nullInputStream(), \"img/jpg\"));\n\n        // When\n        final DocumentValue documentValue = documentDatastore.buildDocumentValueFromUploadPath(uploadKey, 1, \"\");\n        // Then\n        assertTrue(documentValue.getFileName().equals(\"doc.jpg\"));\n    }\n\n    // ---------- ADD METHOD TESTS ------------------------------//\n\n    @Test\n    public void it_should_add_a_document_calling_addDocument_with_upload_Path() throws Exception {\n        // Given\n        String uploadKey = \"1456\";\n        mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_CASE_ID, 1L);\n        mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_NAME, \"doc 1\");\n        mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_UPLOAD_PATH, uploadKey);\n\n        when(tenantFolder.retrieveUploadedTempContent(uploadKey))\n                .thenReturn(new FileContent(\"doc.jpg\", InputStream.nullInputStream(), \"img/jpg\"));\n\n        // When\n        documentDatastore.add(mockedDocumentItem);\n\n        // Then\n        verify(documentDatastore).buildDocumentValueFromUploadPath(uploadKey, -1, null);\n        verify(processAPI).addDocument(eq(1L), eq(\"doc 1\"), eq(\"\"), any(DocumentValue.class));\n    }\n\n    @Test\n    public void it_should_add_a_document_calling_addDocument_with_upload_Path_and_fileName() throws Exception {\n        // Given\n        String uploadKey = \"58768\";\n        mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_CASE_ID, 1L);\n        mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_NAME, \"doc 1\");\n        mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_CONTENT_FILENAME, \"doc_file_name.jpg\");\n        mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_UPLOAD_PATH, uploadKey);\n\n        when(tenantFolder.retrieveUploadedTempContent(uploadKey))\n                .thenReturn(new FileContent(\"doc.jpg\", InputStream.nullInputStream(), \"img/jpg\"));\n\n        // When\n        documentDatastore.add(mockedDocumentItem);\n\n        // Then\n        verify(documentDatastore).buildDocumentValueFromUploadPath(uploadKey, -1, \"doc_file_name.jpg\");\n        verify(processAPI).addDocument(eq(1L), eq(\"doc 1\"), eq(\"\"), any(DocumentValue.class));\n    }\n\n    @Test\n    public void it_should_add_a_document_calling_addDocument_with_upload_Path_with_index_and_description()\n            throws Exception {\n        // Given\n        String uploadKey = \"546374\";\n        mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_CASE_ID, 1L);\n        mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_NAME, \"doc 1\");\n        mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_UPLOAD_PATH, uploadKey);\n        mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_DESCRIPTION, \"This is a description\");\n        mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_INDEX, \"2\");\n\n        when(tenantFolder.retrieveUploadedTempContent(uploadKey))\n                .thenReturn(new FileContent(\"doc.jpg\", InputStream.nullInputStream(), \"img/jpg\"));\n\n        // When\n        documentDatastore.add(mockedDocumentItem);\n\n        // Then\n        verify(documentDatastore).buildDocumentValueFromUploadPath(uploadKey, 2, null);\n        verify(processAPI).addDocument(eq(1L), eq(\"doc 1\"), eq(\"This is a description\"), any(DocumentValue.class));\n    }\n\n    @Test(expected = APIException.class)\n    public void it_should_not_add_a_document_calling_addDocument_with_invalid_upload_Path() throws Exception {\n        // Given\n        mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_CASE_ID, 1L);\n        mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_NAME, \"doc 1\");\n        mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_UPLOAD_PATH, \"unexisting.document\");\n\n        when(tenantFolder.retrieveUploadedTempContent(\"unexisting.document\"))\n                .thenThrow(TemporaryFileNotFoundException.class);\n\n        try {\n            // When\n            documentDatastore.add(mockedDocumentItem);\n        } finally {\n            // Then\n            verify(documentDatastore).buildDocumentValueFromUploadPath(\"unexisting.document\", -1, null);\n            verify(processAPI, times(0)).addDocument(eq(1L), eq(\"doc 1\"), eq(\"\"), any(DocumentValue.class));\n        }\n    }\n\n    @Test\n    public void it_should_add_a_document_calling_addDocument_with_external_Url() throws Exception {\n        // Given\n        mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_CASE_ID, 1L);\n        mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_NAME, \"doc 1\");\n        mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_URL, \"http://images/doc.jpg\");\n\n        // When\n        documentDatastore.add(mockedDocumentItem);\n\n        // Then\n        verify(documentDatastore).buildDocumentValueFromUrl(\"http://images/doc.jpg\", -1);\n        verify(processAPI).addDocument(eq(1L), eq(\"doc 1\"), eq(\"\"), any(DocumentValue.class));\n    }\n\n    @Test(expected = APIException.class)\n    public void it_throws_an_exception_adding_a_document_with_invalid_inputs() {\n        // Given\n        mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_CASE_ID, -1);\n        mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_NAME, \"\");\n        // byte[] fileContent = DocumentUtil.getArrayByteFromFile(new File(docUrl));\n        // When\n        documentDatastore.add(mockedDocumentItem);\n\n    }\n\n    @Test(expected = APIException.class)\n    public void it_throws_an_exception_adding_a_document_with_missing_inputs() {\n        // Given\n        mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_NAME, \"\");\n        // byte[] fileContent = DocumentUtil.getArrayByteFromFile(new File(docUrl));\n        // When\n        documentDatastore.add(mockedDocumentItem);\n\n    }\n\n    // ---------- UPDATE METHOD TESTS ------------------------------//\n\n    @Test\n    public void it_should_update_a_document_calling_updateDocument_with_upload_Path() throws Exception {\n        // Given\n        String uploadKey = \"5464187\";\n        final Map<String, String> attributes = new HashMap<>();\n        attributes.put(CaseDocumentItem.ATTRIBUTE_UPLOAD_PATH, uploadKey);\n        when(tenantFolder.retrieveUploadedTempContent(uploadKey))\n                .thenReturn(new FileContent(\"doc.jpg\", InputStream.nullInputStream(), \"img/jpg\"));\n\n        // When\n        documentDatastore.update(APIID.makeAPIID(1L), attributes);\n\n        // Then\n        verify(documentDatastore).buildDocumentValueFromUploadPath(uploadKey, -1, null);\n        verify(processAPI).updateDocument(eq(1L), any(DocumentValue.class));\n    }\n\n    @Test\n    public void it_should_update_a_document_calling_updateDocument_with_upload_Path_and_fileName() throws Exception {\n        // Given\n        String uploadKey = \"357898\";\n        final Map<String, String> attributes = new HashMap<>();\n        attributes.put(CaseDocumentItem.ATTRIBUTE_UPLOAD_PATH, uploadKey);\n        attributes.put(CaseDocumentItem.ATTRIBUTE_CONTENT_FILENAME, \"doc_file_name.jpg\");\n\n        when(tenantFolder.retrieveUploadedTempContent(uploadKey))\n                .thenReturn(new FileContent(\"doc.jpg\", InputStream.nullInputStream(), \"img/jpg\"));\n\n        // When\n        documentDatastore.update(APIID.makeAPIID(1L), attributes);\n\n        // Then\n        verify(documentDatastore).buildDocumentValueFromUploadPath(uploadKey, -1, \"doc_file_name.jpg\");\n        verify(processAPI).updateDocument(eq(1L), any(DocumentValue.class));\n    }\n\n    @Test\n    public void it_should_update_a_document_calling_updateDocument_with_external_Url() throws Exception {\n        // Given\n        final Map<String, String> attributes = new HashMap<>();\n        attributes.put(CaseDocumentItem.ATTRIBUTE_URL, \"http://images/doc.jpg\");\n\n        // When\n        documentDatastore.update(APIID.makeAPIID(1L), attributes);\n\n        // Then\n        verify(documentDatastore).buildDocumentValueFromUrl(\"http://images/doc.jpg\", -1);\n        verify(processAPI).updateDocument(eq(1L), any(DocumentValue.class));\n    }\n\n    @Test(expected = APIException.class)\n    public void it_should_not_update_document_and_throws_exception_for_missing_uploadPath() {\n        // Given\n        final Map<String, String> attributes = new HashMap<>();\n        attributes.put(CaseDocumentItem.ATTRIBUTE_NAME, \"Doc 1\");\n        final APIID id = APIID.makeAPIID(1L);\n\n        // When\n        documentDatastore.update(id, attributes);\n    }\n\n    @Test(expected = APIException.class)\n    public void it_should_not_update_document_and_throws_exception_for_invalid_uploadPath() throws Exception {\n        // Given\n        final Map<String, String> attributes = new HashMap<>();\n        attributes.put(CaseDocumentItem.ATTRIBUTE_NAME, \"Doc 1\");\n        attributes.put(CaseDocumentItem.ATTRIBUTE_UPLOAD_PATH, \"unexisting.document\");\n        final APIID id = APIID.makeAPIID(1L);\n        doThrow(FileNotFoundException.class).when(documentDatastore)\n                .buildDocumentValueFromUploadPath(\"unexisting.document\", -1, null);\n\n        try {\n            // When\n            documentDatastore.update(id, attributes);\n        } finally {\n            // Then\n            verify(documentDatastore).buildDocumentValueFromUploadPath(\"unexisting.document\", -1, null);\n            verify(processAPI, times(0)).updateDocument(eq(1L), any(DocumentValue.class));\n        }\n    }\n\n    // ---------- SEARCH TESTS -------------------------------------------------//\n    @Test\n    public void it_should_call_buildSearchOptionCreator_method() throws SearchException {\n        // Given\n        when(processAPI.searchDocuments(any(SearchOptions.class))).thenReturn(mockedEngineSearchResults);\n        final Map<String, String> filters = new HashMap<>();\n        filters.put(\"submittedBy\", \"1\");\n\n        // When\n        documentDatastore.searchDocument(0, 10, \"hello\", filters, \"name ASC\");\n\n        // Then\n        verify(documentDatastore).buildSearchOptionCreator(0, 10, \"hello\", filters, \"name ASC\");\n    }\n\n    @Test\n    public void it_should_call_processAPI_searchDocuments_method() throws SearchException {\n        // Given\n        when(processAPI.searchDocuments(any(SearchOptions.class))).thenReturn(mockedEngineSearchResults);\n        final Map<String, String> filters = new HashMap<>();\n        filters.put(\"submittedBy\", \"1\");\n\n        // When\n        documentDatastore.searchDocument(0, 10, \"hello\", filters, \"name ASC\");\n\n        // Then\n        verify(processAPI).searchDocuments(documentDatastore.searchOptionsCreator.create());\n    }\n\n    // -------------DELETE METHOD TESTS ------------------------------------------//\n    @Test\n    public void it_should_delete_one_document() throws DocumentNotFoundException, DeletionException {\n        final List<APIID> docs = new ArrayList<>();\n        docs.add(APIID.makeAPIID(mockedDocument.getId()));\n\n        // When\n        documentDatastore.delete(docs);\n\n        // Then\n        verify(processAPI).removeDocument(1L);\n        verify(processAPI, times(1)).removeDocument(any(Long.class));\n    }\n\n    @Test\n    public void it_should_delete_two_documents() throws DocumentNotFoundException, DeletionException {\n        final List<APIID> docs = new ArrayList<>();\n        docs.add(APIID.makeAPIID(mockedDocument.getId()));\n        docs.add(APIID.makeAPIID(mockedDocument.getId()));\n\n        // When\n        documentDatastore.delete(docs);\n\n        // Then\n        verify(processAPI, times(2)).removeDocument(1L);\n    }\n\n    @Test\n    public void it_should_throw_an_exception_when_input_is_null() {\n        expectedEx.expect(APIException.class);\n        expectedEx.expectMessage(\"Error while deleting a document. Document id not specified in the request\");\n\n        // When\n        documentDatastore.delete(null);\n    }\n\n    @Test\n    public void it_should_throw_an_exception_when_document_is_not_found()\n            throws DocumentNotFoundException, DeletionException {\n        expectedEx.expect(APIException.class);\n        expectedEx.expectMessage(\"Error while deleting a document. Document not found\");\n        // When\n        when(processAPI.removeDocument(3L)).thenThrow(DocumentNotFoundException.class);\n\n        final List<APIID> docs = new ArrayList<>();\n        docs.add(APIID.makeAPIID(3L));\n\n        // When\n        documentDatastore.delete(docs);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/CaseDocumentItemConverterTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.cases;\n\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.bpm.document.Document;\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseDocumentItem;\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class CaseDocumentItemConverterTest extends APITestWithMock {\n\n    @Test\n    public void should_convert_engine_document_into_portal_document() {\n\n        // Given\n        final CaseDocumentItemConverter documentItemConverter = new CaseDocumentItemConverter();\n        final Document engineItem = mock(Document.class);\n        when(engineItem.getId()).thenReturn(1l);\n        when(engineItem.getProcessInstanceId()).thenReturn(1l);\n        when(engineItem.getName()).thenReturn(\"Doc 1\");\n        when(engineItem.getAuthor()).thenReturn(1l);\n        when(engineItem.getContentFileName()).thenReturn(\"doc.jpg\");\n        when(engineItem.getCreationDate()).thenReturn(new Date());\n        when(engineItem.getContentMimeType()).thenReturn(\"image\");\n        when(engineItem.hasContent()).thenReturn(true);\n        when(engineItem.getContentStorageId()).thenReturn(\"1\");\n        when(engineItem.getUrl()).thenReturn(\"http://url.com?test=d\");\n\n        // When\n        final CaseDocumentItem documentItem = documentItemConverter.convert(engineItem);\n\n        // Assert\n        assertTrue(documentItem.getId().equals(1l));\n        assertTrue(documentItem.getCaseId().equals(1l));\n        assertTrue(documentItem.getName().equals(\"Doc 1\"));\n        assertTrue(documentItem.getSubmittedBy().equals(1l));\n        assertTrue(documentItem.getFileName().equals(\"doc.jpg\"));\n        assertTrue(documentItem.getCreationDate().equals(engineItem.getCreationDate()));\n        assertTrue(documentItem.getMIMEType().equals(\"image\"));\n        assertTrue(documentItem.hasContent());\n        assertTrue(documentItem.getStorageId().equals(\"1\"));\n        assertTrue(documentItem.getURL().equals(\"http://url.com?test=d\"));\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/CaseItemConverterTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.cases;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.doReturn;\n\nimport org.bonitasoft.console.common.server.i18n.I18n;\nimport org.bonitasoft.engine.bpm.process.ProcessInstance;\nimport org.bonitasoft.web.rest.model.bpm.cases.CaseItem;\nimport org.bonitasoft.web.toolkit.client.common.CommonDateFormater;\nimport org.bonitasoft.web.toolkit.server.utils.ServerDateFormater;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class CaseItemConverterTest {\n\n    @Mock\n    private ProcessInstance processInstance;\n\n    private final CaseItemConverter caseItemConverter = new CaseItemConverter();\n\n    @BeforeClass\n    public static void beforeClass() {\n        I18n.getInstance();\n        CommonDateFormater.setDateFormater(new ServerDateFormater());\n    }\n\n    @Test\n    public void testConvertShouldReturnAEngineCaseConvertedIntoAConsoleCase() throws Exception {\n        //given\n        doReturn(\"labelOne\").when(processInstance).getStringIndexLabel(1);\n        doReturn(\"labelTwo\").when(processInstance).getStringIndexLabel(2);\n        doReturn(\"labelThree\").when(processInstance).getStringIndexLabel(3);\n        doReturn(\"labelFour\").when(processInstance).getStringIndexLabel(4);\n        doReturn(\"labelFive\").when(processInstance).getStringIndexLabel(5);\n        doReturn(\"valueOne\").when(processInstance).getStringIndex1();\n        doReturn(\"valueTwo\").when(processInstance).getStringIndex2();\n        doReturn(\"valueThree\").when(processInstance).getStringIndex3();\n        doReturn(\"valueFour\").when(processInstance).getStringIndex4();\n        doReturn(\"valueFive\").when(processInstance).getStringIndex5();\n\n        // when\n        final CaseItem caseItem = caseItemConverter.convert(processInstance);\n\n        // then\n        //check labels\n        assertThat(caseItem.getSearchIndex1Label()).isEqualTo(\"labelOne\");\n        assertThat(caseItem.getSearchIndex2Label()).isEqualTo(\"labelTwo\");\n        assertThat(caseItem.getSearchIndex3Label()).isEqualTo(\"labelThree\");\n        assertThat(caseItem.getSearchIndex4Label()).isEqualTo(\"labelFour\");\n        assertThat(caseItem.getSearchIndex5Label()).isEqualTo(\"labelFive\");\n        //check values\n        assertThat(caseItem.getSearchIndex1Value()).isEqualTo(\"valueOne\");\n        assertThat(caseItem.getSearchIndex2Value()).isEqualTo(\"valueTwo\");\n        assertThat(caseItem.getSearchIndex3Value()).isEqualTo(\"valueThree\");\n        assertThat(caseItem.getSearchIndex4Value()).isEqualTo(\"valueFour\");\n        assertThat(caseItem.getSearchIndex5Value()).isEqualTo(\"valueFive\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/CaseVariableDatastoreTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.cases;\n\nimport static org.mockito.Mockito.*;\nimport static org.mockito.MockitoAnnotations.initMocks;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\n\n/**\n * @author Colin PUY\n */\npublic class CaseVariableDatastoreTest extends APITestWithMock {\n\n    private CaseVariableDatastore datastore;\n\n    @Mock\n    private ProcessAPI processAPI;\n\n    @Before\n    public void initializeMocks() {\n        initMocks(this);\n\n        datastore = spy(new CaseVariableDatastore(null));\n        doReturn(processAPI).when(datastore).getEngineProcessAPI();\n    }\n\n    @Test\n    public void testUpdateVariableValue() throws Exception {\n        long caseId = 1L;\n        String name = \"aName\";\n        String newValue = \"newValue\";\n\n        datastore.updateVariableValue(caseId, name, String.class.getName(), newValue);\n\n        verify(processAPI).updateProcessDataInstance(name, caseId, newValue);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/connector/ConnectorInstanceDatastoreTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.connector;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport junit.framework.Assert;\nimport org.bonitasoft.console.common.server.i18n.I18n;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.bpm.connector.ConnectorInstance;\nimport org.bonitasoft.engine.bpm.connector.ConnectorState;\nimport org.bonitasoft.engine.bpm.connector.impl.ConnectorInstanceImpl;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.search.impl.SearchResultImpl;\nimport org.bonitasoft.web.rest.model.bpm.connector.ConnectorInstanceItem;\nimport org.bonitasoft.web.rest.server.BonitaRestAPIServlet;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.junit.Before;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\nimport org.mockito.Mock;\nimport org.mockito.Mockito;\nimport org.mockito.MockitoAnnotations;\nimport org.mockito.Spy;\n\n/**\n * @author Vincent Elcrin\n */\npublic class ConnectorInstanceDatastoreTest {\n\n    @Spy\n    private final ConnectorInstanceDatastore spiedDatastore = new ConnectorInstanceDatastore(null);\n\n    @Mock\n    private ProcessAPI mockedProcessAPI;\n\n    @BeforeClass\n    public static void initEnvironnement() {\n        I18n.getInstance();\n        new BonitaRestAPIServlet();\n    }\n\n    @Before\n    public void init() {\n        MockitoAnnotations.initMocks(this);\n        Mockito.doReturn(this.mockedProcessAPI).when(this.spiedDatastore).getProcessAPI();\n    }\n\n    // //////////////////////////////////////////////////////////////////////////\n    // / Test search\n    // //////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Test right parameters go through engine API call method\n     *\n     * @throws Exception\n     */\n    @Test\n    public void searchBuildRightParameters() throws Exception {\n        final Map<String, String> filters = new HashMap<>();\n        filters.put(ConnectorInstanceItem.ATTRIBUTE_CONTAINER_ID, \"1\");\n        filters.put(ConnectorInstanceItem.ATTRIBUTE_STATE, \"2\");\n\n        final SearchOptions searchOptions = this.spiedDatastore.buildSearchOptions(0, 123, \"searchTerm\",\n                \"order \" + Order.ASC, filters);\n\n        Assert.assertEquals(0, searchOptions.getStartIndex());\n        Assert.assertEquals(123, searchOptions.getMaxResults());\n        Assert.assertEquals(\"searchTerm\", searchOptions.getSearchTerm());\n        Assert.assertEquals(\"order\", searchOptions.getSorts().get(0).getField());\n        Assert.assertEquals(Order.ASC, searchOptions.getSorts().get(0).getOrder());\n        Assert.assertEquals(filters.size(), searchOptions.getFilters().size());\n    }\n\n    /**\n     * Test search result conversion\n     *\n     * @throws Exception\n     */\n    @Test\n    public void searchReturnAllItems() throws Exception {\n        final ConnectorInstance connectorInstance1 = createConnectorInstanceImpl(1L, \"instance 1\");\n        final ConnectorInstance connectorInstance2 = createConnectorInstanceImpl(1L, \"instance 2\");\n        final SearchResult<ConnectorInstance> expected = new SearchResultImpl<>(2,\n                Arrays.asList(connectorInstance1, connectorInstance2));\n\n        Mockito.when(this.mockedProcessAPI.searchConnectorInstances(Mockito.any(SearchOptions.class)))\n                .thenReturn(expected);\n\n        final ItemSearchResult<ConnectorInstanceItem> searchResult = this.spiedDatastore.search(0, 10, null, null,\n                new HashMap<>());\n\n        Mockito.verify(this.mockedProcessAPI).searchConnectorInstances(Mockito.any(SearchOptions.class));\n        Assert.assertTrue(areEquals(new ConnectorInstanceItemWrapper(expected.getResult().get(0)),\n                searchResult.getResults().get(0)));\n        Assert.assertTrue(areEquals(new ConnectorInstanceItemWrapper(expected.getResult().get(1)),\n                searchResult.getResults().get(1)));\n    }\n\n    // //////////////////////////////////////////////////////////////////////////\n    // / Convenient tools\n    // //////////////////////////////////////////////////////////////////////////\n\n    private boolean areEquals(final Item expected, final Item actual) {\n        return expected.getAttributes().equals(actual.getAttributes());\n    }\n\n    private ConnectorInstance createConnectorInstanceImpl(final long id, final String name) {\n        final ConnectorInstanceImpl connectorInstance = new ConnectorInstanceImpl(name, 2L, \"containerType\",\n                String.valueOf(id), \"version\",\n                ConnectorState.DONE,\n                ConnectorEvent.ON_ENTER);\n        connectorInstance.setId(1L);\n        return connectorInstance;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/connector/ConnectorInstanceItemWrapperTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.connector;\n\nimport junit.framework.Assert;\nimport org.bonitasoft.engine.bpm.connector.ConnectorEvent;\nimport org.bonitasoft.engine.bpm.connector.ConnectorInstance;\nimport org.bonitasoft.engine.bpm.connector.ConnectorState;\nimport org.bonitasoft.engine.bpm.connector.impl.ConnectorInstanceImpl;\nimport org.bonitasoft.web.rest.model.bpm.connector.ConnectorInstanceItem;\nimport org.bonitasoft.web.rest.server.BonitaRestAPIServlet;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.AfterClass;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\n\n/**\n * @author Colin PUY\n */\npublic class ConnectorInstanceItemWrapperTest {\n\n    private static BonitaRestAPIServlet consoleAPIServlet;\n\n    @BeforeClass\n    public static void initContextForDefinition() {\n        consoleAPIServlet = new BonitaRestAPIServlet();\n    }\n\n    @AfterClass\n    public static void destroyContext() {\n        consoleAPIServlet.destroy();\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void cantWrapANullItem() throws Exception {\n        new ConnectorInstanceItemWrapper(null);\n    }\n\n    @Test\n    public void convertItemReturnRightItem() {\n        final ConnectorInstance expected = createConnectorInstanceImpl(1L, \"instance1\");\n        final ConnectorInstanceItem actual = new ConnectorInstanceItemWrapper(expected);\n        Assert.assertTrue(areEquals(expected, actual));\n    }\n\n    private boolean areEquals(final ConnectorInstance expected, final ConnectorInstanceItem actual) {\n        return APIID.makeAPIID(expected.getConnectorId()).equals(actual.getConnectorId())\n                && APIID.makeAPIID(expected.getContainerId()).equals(actual.getContainerId())\n                && expected.getContainerType().equals(actual.getContainerType())\n                && APIID.makeAPIID(expected.getId()).equals(actual.getId())\n                && expected.getName().equals(actual.getName())\n                && expected.getState().equals(new ConnectorInstanceStateConverter().convert(actual.getState()))\n                && expected.getVersion().equals(actual.getVersion());\n    }\n\n    private ConnectorInstance createConnectorInstanceImpl(final long id, final String name) {\n        final ConnectorInstanceImpl connectorInstance = new ConnectorInstanceImpl(name, 2L, \"containerType\",\n                String.valueOf(id), \"version\",\n                ConnectorState.DONE,\n                ConnectorEvent.ON_ENTER);\n        connectorInstance.setId(1L);\n        return connectorInstance;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/AbstractFlowNodeDatastoreTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.flownode;\n\nimport static org.assertj.core.api.Assertions.*;\nimport static org.mockito.Mockito.*;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstance;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstanceSearchDescriptor;\nimport org.bonitasoft.engine.search.SearchFilterOperation;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.flownode.FlowNodeItem;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class AbstractFlowNodeDatastoreTest {\n\n    AbstractFlowNodeDatastore<FlowNodeItem, FlowNodeInstance> datastore;\n\n    @Before\n    public void setUp() throws Exception {\n        final APISession engineSession = mock(APISession.class);\n        datastore = new AbstractFlowNodeDatastore<>(engineSession);\n    }\n\n    @Test\n    public void makeSearchOptionBuilder_state_Filter_Pending_Adds_multiple_entries_in_SearchOption() throws Exception {\n        final Map<String, String> filters = new HashMap<>();\n        filters.put(FlowNodeInstanceSearchDescriptor.STATE_NAME, \"pending\");\n        final SearchOptionsBuilder makeSearchOptionBuilder = datastore.makeSearchOptionBuilder(0, 10, \"\", \"\", filters);\n        assertThat(makeSearchOptionBuilder.done().getFilters()).extracting(\"field\", \"operation\", \"value\").contains(\n                tuple(null, SearchFilterOperation.L_PARENTHESIS, null),\n                tuple(\"state\", SearchFilterOperation.EQUALS, \"ready\"),\n                tuple(null, SearchFilterOperation.OR, null),\n                tuple(\"state\", SearchFilterOperation.EQUALS, \"waiting\"),\n                tuple(null, SearchFilterOperation.R_PARENTHESIS, null),\n                tuple(\"state\", SearchFilterOperation.DIFFERENT, \"aborted\"),\n                tuple(\"state\", SearchFilterOperation.DIFFERENT, \"cancelled\"),\n                tuple(\"state\", SearchFilterOperation.DIFFERENT, \"completed\"));\n    }\n\n    @Test\n    public void makeSearchOptionBuilder_state_Filter_Ongoing_Adds_multiple_entries_in_SearchOption() throws Exception {\n        final Map<String, String> filters = new HashMap<>();\n        filters.put(FlowNodeInstanceSearchDescriptor.STATE_NAME, \"ongoing\");\n        final SearchOptionsBuilder makeSearchOptionBuilder = datastore.makeSearchOptionBuilder(0, 10, \"\", \"\", filters);\n        assertThat(makeSearchOptionBuilder.done().getFilters()).extracting(\"field\", \"operation\", \"value\").contains(\n                tuple(null, SearchFilterOperation.L_PARENTHESIS, null),\n                tuple(\"state\", SearchFilterOperation.EQUALS, \"executing\"),\n                tuple(null, SearchFilterOperation.OR, null),\n                tuple(\"state\", SearchFilterOperation.EQUALS, \"completing\"),\n                tuple(null, SearchFilterOperation.OR, null),\n                tuple(\"state\", SearchFilterOperation.EQUALS, \"initializing\"),\n                tuple(null, SearchFilterOperation.R_PARENTHESIS, null),\n                tuple(\"state\", SearchFilterOperation.DIFFERENT, \"aborted\"),\n                tuple(\"state\", SearchFilterOperation.DIFFERENT, \"cancelled\"),\n                tuple(\"state\", SearchFilterOperation.DIFFERENT, \"completed\"));\n    }\n\n    @Test\n    public void makeSearchOptionBuilder_state_Filter_Ready_Adds_single_entries_in_SearchOption() throws Exception {\n        final Map<String, String> filters = new HashMap<>();\n        filters.put(FlowNodeInstanceSearchDescriptor.STATE_NAME, \"ready\");\n        final SearchOptionsBuilder makeSearchOptionBuilder = datastore.makeSearchOptionBuilder(0, 10, \"\", \"\", filters);\n        assertThat(makeSearchOptionBuilder.done().getFilters()).extracting(\"field\", \"operation\", \"value\").contains(\n                tuple(\"state\", SearchFilterOperation.EQUALS, \"ready\"),\n                tuple(\"state\", SearchFilterOperation.DIFFERENT, \"aborted\"),\n                tuple(\"state\", SearchFilterOperation.DIFFERENT, \"cancelled\"),\n                tuple(\"state\", SearchFilterOperation.DIFFERENT, \"completed\"));\n    }\n\n    @Test\n    public void makeSearchOptionBuilder_without_state_Filter_Adds_only_Unwanted_state_SearchOption() throws Exception {\n        final Map<String, String> filters = new HashMap<>();\n        filters.put(FlowNodeInstanceSearchDescriptor.STATE_NAME, \"ready\");\n        final SearchOptionsBuilder makeSearchOptionBuilder = datastore.makeSearchOptionBuilder(0, 10, \"\", \"\", filters);\n        assertThat(makeSearchOptionBuilder.done().getFilters()).extracting(\"field\", \"operation\", \"value\").contains(\n                tuple(\"state\", SearchFilterOperation.DIFFERENT, \"aborted\"),\n                tuple(\"state\", SearchFilterOperation.DIFFERENT, \"cancelled\"),\n                tuple(\"state\", SearchFilterOperation.DIFFERENT, \"completed\"));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/AbstractHumanTaskDatastoreTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.flownode;\n\nimport static org.bonitasoft.web.rest.model.bpm.flownode.IHumanTaskItem.FILTER_SHOW_ASSIGNED_TO_OTHERS;\nimport static org.bonitasoft.web.rest.model.bpm.flownode.IHumanTaskItem.FILTER_USER_ID;\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.verify;\n\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor;\nimport org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.engine.search.SearchOptionsBuilder;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.engine.session.impl.APISessionImpl;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ActivityItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.HumanTaskItem;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class AbstractHumanTaskDatastoreTest {\n\n    @Mock\n    private ProcessAPI processAPI;\n    private final APISession session = new APISessionImpl(55L, new Date(), 5000, \"john\", 44L);\n    private final AbstractHumanTaskDatastore<HumanTaskItem, HumanTaskInstance> datastore = new AbstractHumanTaskDatastore<>(\n            session) {\n\n        @Override\n        protected ProcessAPI getProcessAPI() {\n            return processAPI;\n        }\n    };\n\n    @Test\n    public void should_use_searchAssignedAndPendingHumanTasks_when_filtering_ready_state() throws SearchException {\n        datastore.runSearch(new SearchOptionsBuilder(1, 100), Collections.singletonMap(\"state\", \"ready\"));\n\n        verify(processAPI).searchAssignedAndPendingHumanTasks(any());\n    }\n\n    @Test\n    public void should_use_searchPendingOrAssignedToUserOrAssignedToOthersTasks_when_filtering_show_assigned_to_others_tasks()\n            throws SearchException {\n        HashMap<String, String> filters = new HashMap<>();\n        filters.put(FILTER_USER_ID, \"44\");\n        filters.put(FILTER_SHOW_ASSIGNED_TO_OTHERS, \"true\");\n        datastore.runSearch(new SearchOptionsBuilder(1, 100), filters);\n\n        verify(processAPI).searchPendingOrAssignedToUserOrAssignedToOthersTasks(eq(44L), any());\n    }\n\n    @Test\n    public void should_use_searchHumanTaskInstances_when_there_is_no_particular_filter() throws SearchException {\n        datastore.runSearch(new SearchOptionsBuilder(1, 100), Collections.singletonMap(\"tpye\", \"toto\"));\n\n        verify(processAPI).searchHumanTaskInstances(any());\n    }\n\n    @Test\n    public void should_SearchOptionBuilderConvertSortParameter() throws SearchException {\n        SearchOptionsBuilder builder = datastore.makeSearchOptionBuilder(0, 10, null,\n                ActivityItem.ATTRIBUTE_ROOT_CASE_ID + \" \" + Order.DESC, new HashMap<>());\n\n        assertEquals(1, builder.done().getSorts().size());\n        assertEquals(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID,\n                builder.done().getSorts().get(0).getField());\n        assertEquals(Order.DESC, builder.done().getSorts().get(0).getOrder());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/FlowNodeDatastoreTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.flownode;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.doThrow;\nimport static org.mockito.Mockito.spy;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.flownode.FlowNodeInstance;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.UserTaskInstanceImpl;\nimport org.bonitasoft.engine.exception.SearchException;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.search.impl.SearchResultImpl;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.ModelFactory;\nimport org.bonitasoft.web.rest.model.bpm.flownode.FlowNodeItem;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.ItemDefinitionFactory;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Celine Souchet\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class FlowNodeDatastoreTest {\n\n    @Mock\n    private APISession engineSession;\n\n    @Mock\n    private ProcessAPI processAPI;\n\n    @Mock\n    private FlowNodeConverter converter;\n\n    @InjectMocks\n    private FlowNodeDatastore flowNodeDatastore;\n\n    @Before\n    public void before() {\n        ItemDefinitionFactory.setDefaultFactory(new ModelFactory());\n        FlowNodeConverter.setFlowNodeConverter(converter);\n        flowNodeDatastore = spy(new FlowNodeDatastore(engineSession));\n        doReturn(processAPI).when(flowNodeDatastore).getProcessAPI();\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.web.rest.server.datastore.bpm.flownode.AbstractFlowNodeDatastore#count(java.lang.String, java.lang.String, java.util.Map)}.\n     */\n    @Test\n    public final void count_should_return_number_of_flow_nodes_on_Engine() throws SearchException {\n        final String search = \"plop\";\n        final String orders = FlowNodeItem.ATTRIBUTE_PARENT_CASE_ID;\n        final Map<String, String> filters = Collections.emptyMap();\n        final List<FlowNodeInstance> flowNodeInstances = Arrays\n                .asList((FlowNodeInstance) new UserTaskInstanceImpl(\"name\", 9L, 18L));\n        final SearchResult<FlowNodeInstance> searchResult = new SearchResultImpl<>(1L, flowNodeInstances);\n        doReturn(searchResult).when(processAPI).searchFlowNodeInstances(any(SearchOptions.class));\n\n        // When\n        final long result = flowNodeDatastore.count(search, orders, filters);\n\n        // Then\n        assertEquals(searchResult.getCount(), result);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.web.rest.server.datastore.bpm.flownode.AbstractFlowNodeDatastore#search(int, int, java.lang.String, java.lang.String, java.util.Map)}\n     * .\n     */\n    @Test\n    public final void search_should_get_list_of_flow_nodes_on_Engine_and_convert_them_to_FlowNodeItem()\n            throws SearchException {\n        final int page = 0;\n        final int resultsByPage = 1;\n        final String search = \"plop\";\n        final String orders = FlowNodeItem.ATTRIBUTE_DESCRIPTION;\n        final Map<String, String> filters = Collections.emptyMap();\n        final FlowNodeInstance flowNodeInstance = new UserTaskInstanceImpl(\"name\", 9L, 18L);\n        final List<FlowNodeInstance> flowNodeInstances = Arrays.asList(flowNodeInstance);\n        final SearchResult<FlowNodeInstance> searchResult = new SearchResultImpl<>(1L, flowNodeInstances);\n        doReturn(searchResult).when(processAPI).searchFlowNodeInstances(any(SearchOptions.class));\n        final FlowNodeItem flowNodeItem = new FlowNodeItem();\n        doReturn(flowNodeItem).when(converter)._convertEngineToConsoleItem(flowNodeInstance);\n\n        // When\n        final ItemSearchResult<FlowNodeItem> result = flowNodeDatastore.search(page, resultsByPage, search, orders,\n                filters);\n\n        // Then\n        final List<FlowNodeItem> flowNodeItems = result.getResults();\n        assertEquals(1, flowNodeItems.size());\n        assertEquals(flowNodeItem, flowNodeItems.get(0));\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.web.rest.server.datastore.bpm.flownode.AbstractFlowNodeDatastore#search(int, int, java.lang.String, java.lang.String, java.util.Map)}\n     * .\n     */\n    @Test(expected = APIException.class)\n    public final void search_should_throw_an_exception_when_Engine_failed() throws SearchException {\n        final int page = 0;\n        final int resultsByPage = 1;\n        final String search = \"plop\";\n        final String orders = FlowNodeItem.ATTRIBUTE_DESCRIPTION;\n        final Map<String, String> filters = Collections.emptyMap();\n        doThrow(new SearchException(new Exception(\"toto\"))).when(processAPI)\n                .searchFlowNodeInstances(any(SearchOptions.class));\n\n        // When\n        flowNodeDatastore.search(page, resultsByPage, search, orders, filters);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/TaskFinderTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.flownode;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.console.common.server.i18n.I18n;\nimport org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstanceSearchDescriptor;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedActivityItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedTaskItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.TaskItem;\nimport org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.ArchivedTaskDatastore;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.ItemDefinitionFactory;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.bonitasoft.web.toolkit.client.data.item.ItemDefinition;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class TaskFinderTest {\n\n    @Mock\n    private TaskDatastore journal;\n\n    @Mock\n    private ArchivedTaskDatastore archives;\n\n    private TaskFinder taskFinder;\n\n    private final APIID id = APIID.makeAPIID(658L);\n\n    @Before\n    public void setUp() throws Exception {\n        I18n.getInstance();\n        ItemDefinitionFactory.setDefaultFactory(new ItemDefinitionFactory() {\n\n            @Override\n            public ItemDefinition<?> defineItemDefinitions(final String token) {\n                return null;\n            }\n        });\n        taskFinder = new TaskFinder(journal, archives);\n    }\n\n    @Test\n    public void should_return_task_from_the_journal_when_it_belong_to_the_journal() throws Exception {\n        final TaskItem task = new TaskItem();\n        task.setId(id);\n        when(journal.get(id)).thenReturn(task);\n\n        final IItem item = taskFinder.find(id);\n\n        assertThat(item.getId()).isEqualTo(task.getId());\n    }\n\n    @Test\n    public void should_return_task_from_the_archives_when_not_found_in_the_journal() throws Exception {\n        final ArchivedTaskItem task = new ArchivedTaskItem();\n        task.setId(id);\n        when(journal.get(id)).thenThrow(new APIItemNotFoundException(\"type\", id));\n        final Map<String, String> filters = new HashMap<>();\n        filters.put(ArchivedActivityInstanceSearchDescriptor.SOURCE_OBJECT_ID, id.toString());\n        final ItemSearchResult<ArchivedTaskItem> result = mock(ItemSearchResult.class);\n        when(result.getResults()).thenReturn(Arrays.asList(task));\n        when(archives.search(0, 1, null, ArchivedActivityItem.ATTRIBUTE_ARCHIVED_DATE + \" \"\n                + Order.DESC, filters)).thenReturn(result);\n\n        final IItem item = taskFinder.find(id);\n\n        assertThat(item.getId()).isEqualTo(task.getId());\n    }\n\n    @Test(expected = APIItemNotFoundException.class)\n    public void should_throw_an_exception_when_the_task_does_not_exist() throws Exception {\n        when(journal.get(id)).thenThrow(new APIItemNotFoundException(\"type\", id));\n        final Map<String, String> filters = new HashMap<>();\n        filters.put(ArchivedActivityInstanceSearchDescriptor.SOURCE_OBJECT_ID, id.toString());\n        when(archives.search(0, 1, null, ArchivedActivityItem.ATTRIBUTE_ARCHIVED_DATE + \" \"\n                + Order.DESC, filters)).thenThrow(new APIItemNotFoundException(\"type\", id));\n\n        taskFinder.find(id);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/archive/AbstractArchivedFlowNodeDatastoreTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.search.impl.SearchFilter;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedFlowNodeItem;\nimport org.junit.Test;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class AbstractArchivedFlowNodeDatastoreTest {\n\n    private ArchivedHumanTaskDatastore datastore = new ArchivedHumanTaskDatastore(null, \"token\");\n\n    @Test\n    public void makeSearchOptionCreator_should_converts_TERMINAL_field_to_boolean() {\n        final List<SearchFilter> filters = datastore.makeSearchOptionCreator(0, 10, \"\", \"displayName ASC\",\n                Collections.singletonMap(ArchivedFlowNodeItem.FILTER_IS_TERMINAL, \"true\")).create().getFilters();\n        assertThat(filters.get(0).getValue()).isEqualTo(true);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/archive/ArchivedActivityDatastoreTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.assertThrows;\nimport static org.mockito.Mockito.*;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.ArchivedCallActivityInstanceImpl;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.ArchivedGatewayInstanceImpl;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedActivityDefinition;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedActivityItem;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedFlowNodeItem;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class ArchivedActivityDatastoreTest {\n\n    @Mock\n    ProcessAPI processAPI;\n\n    @Test\n    public void should_return_ArchivedFlowNodeItem() throws Exception {\n\n        APIID id = APIID.makeAPIID(1L);\n        ArchivedActivityDatastore abstractArchivedActivityDatastore = spy(\n                new ArchivedActivityDatastore(null, ArchivedActivityDefinition.TOKEN));\n\n        doReturn(processAPI).when(abstractArchivedActivityDatastore).getProcessAPI();\n        doReturn(new ArchivedCallActivityInstanceImpl(\"test\")).when(processAPI)\n                .getArchivedFlowNodeInstance(id.toLong());\n        doReturn(new ArchivedActivityItem()).when(abstractArchivedActivityDatastore).convertEngineToConsoleItem(any());\n\n        ArchivedFlowNodeItem archivedFlowNodeItem = abstractArchivedActivityDatastore.get(id);\n        assertThat(archivedFlowNodeItem).isNotNull();\n\n    }\n\n    @Test\n    public void should_thrown_APIItemNotFoundException_when_ArchiveActivityInstance_not_exists() throws Exception {\n        APIID id = APIID.makeAPIID(1L);\n        ArchivedActivityDatastore archivedActivityDatastore = spy(\n                new ArchivedActivityDatastore(null, ArchivedActivityDefinition.TOKEN));\n\n        doReturn(processAPI).when(archivedActivityDatastore).getProcessAPI();\n        doReturn(new ArchivedGatewayInstanceImpl(\"test\")).when(processAPI).getArchivedFlowNodeInstance(id.toLong());\n\n        APIItemNotFoundException apiException = assertThrows(APIItemNotFoundException.class,\n                () -> archivedActivityDatastore.get(id));\n        assertThat(apiException).isNotNull();\n        assertThat(apiException.getMessage()).containsIgnoringCase(ArchivedActivityDefinition.TOKEN);\n        assertThat(apiException.getMessage()).containsIgnoringCase(id.toString());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/archive/ArchivedHumanTaskDatastoreTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.assertThrows;\nimport static org.mockito.Mockito.*;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.ArchivedAutomaticTaskInstanceImpl;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedHumanTaskDefinition;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ArchivedHumanTaskDatastoreTest {\n\n    ProcessAPI processAPI = mock(ProcessAPI.class);\n\n    @Test\n    public void should_thrown_APIItemNotFoundException_when_ArchivedHumanTaskInstance_not_exists() throws Exception {\n        APIID id = APIID.makeAPIID(1L);\n        ArchivedHumanTaskDatastore abstractArchivedActivityDatastore = spy(\n                new ArchivedHumanTaskDatastore(null, ArchivedHumanTaskDefinition.TOKEN));\n\n        doReturn(processAPI).when(abstractArchivedActivityDatastore).getProcessAPI();\n        doReturn(new ArchivedAutomaticTaskInstanceImpl(\"test\")).when(processAPI)\n                .getArchivedFlowNodeInstance(id.toLong());\n\n        APIItemNotFoundException apiException = assertThrows(APIItemNotFoundException.class,\n                () -> abstractArchivedActivityDatastore.get(id));\n        assertThat(apiException).isNotNull();\n        assertThat(apiException.getMessage()).containsIgnoringCase(ArchivedHumanTaskDefinition.TOKEN);\n        assertThat(apiException.getMessage()).containsIgnoringCase(id.toString());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/archive/ArchivedUserTaskDatastoreTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.assertThrows;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.spy;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.flownode.impl.internal.ArchivedReceiveTaskInstanceImpl;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ArchivedHumanTaskDefinition;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ArchivedUserTaskDatastoreTest {\n\n    @Mock\n    ProcessAPI processAPI;\n\n    @Test\n    public void should_thrown_APIItemNotFoundException_when_ArchivedUserTaskInstance_not_exists() throws Exception {\n        APIID id = APIID.makeAPIID(1L);\n        ArchivedUserTaskDatastore abstractArchivedActivityDatastore = spy(\n                new ArchivedUserTaskDatastore(null, ArchivedHumanTaskDefinition.TOKEN));\n\n        doReturn(processAPI).when(abstractArchivedActivityDatastore).getProcessAPI();\n        doReturn(new ArchivedReceiveTaskInstanceImpl(\"test\")).when(processAPI).getArchivedFlowNodeInstance(id.toLong());\n\n        APIItemNotFoundException apiException = assertThrows(APIItemNotFoundException.class,\n                () -> abstractArchivedActivityDatastore.get(id));\n        assertThat(apiException).isNotNull();\n        assertThat(apiException.getMessage()).containsIgnoringCase(ArchivedHumanTaskDefinition.TOKEN);\n        assertThat(apiException.getMessage()).containsIgnoringCase(id.toString());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/process/CategoryDatastoreTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.process;\n\nimport static org.bonitasoft.web.rest.model.builder.bpm.process.CategoryItemBuilder.*;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.*;\nimport static org.mockito.MockitoAnnotations.*;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIForbiddenException;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\n\n/**\n * @author Colin PUY\n */\npublic class CategoryDatastoreTest extends APITestWithMock {\n\n    @Mock\n    private ProcessAPI processAPI;\n\n    private CategoryDatastore categoryDatastore;\n\n    @Before\n    public void initializeMocks() {\n        initMocks(this);\n\n        categoryDatastore = spy(new CategoryDatastore(null));\n\n        doReturn(this.processAPI).when(this.categoryDatastore).getProcessAPI();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Test(expected = APIForbiddenException.class)\n    public void addingTwiceSameCategoryIsForbidden() throws Exception {\n        when(processAPI.createCategory(anyString(), anyString()))\n                .thenThrow(new AlreadyExistsException(\"category already exists\"));\n\n        categoryDatastore.add(aCategoryItem().build());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/process/ProcessCategoryDatastoreTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.process;\n\nimport static java.util.Arrays.*;\nimport static org.bonitasoft.web.rest.model.builder.bpm.process.ProcessCategoryItemBuilder.*;\nimport static org.mockito.Mockito.*;\nimport static org.mockito.MockitoAnnotations.*;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessCategoryItem;\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIForbiddenException;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\n\n/**\n * @author Colin PUY\n */\npublic class ProcessCategoryDatastoreTest extends APITestWithMock {\n\n    @Mock\n    private ProcessAPI processAPI;\n\n    private ProcessCategoryDatastore processCategoryDatastore;\n\n    @Before\n    public void initializeMocks() {\n        initMocks(this);\n\n        processCategoryDatastore = spy(new ProcessCategoryDatastore(null));\n\n        doReturn(this.processAPI).when(processCategoryDatastore).getProcessAPI();\n    }\n\n    @Test(expected = APIForbiddenException.class)\n    public void addingTwiceSameCategoryOnProcessIsForbidden() throws Exception {\n        ProcessCategoryItem processCategory = aProcessCategory().build();\n        doThrow(new AlreadyExistsException(\"this thing already exists!\")).when(processAPI)\n                .addCategoriesToProcess(processCategory.getProcessId().toLong(),\n                        asList(processCategory.getCategoryId().toLong()));\n\n        processCategoryDatastore.add(processCategory);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/process/ProcessConnectorDatastoreTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.process;\n\nimport static java.util.Arrays.*;\nimport static java.util.Collections.*;\nimport static junit.framework.Assert.*;\nimport static org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorItem.*;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.*;\nimport static org.mockito.MockitoAnnotations.*;\n\nimport java.util.HashMap;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.connector.ConnectorCriterion;\nimport org.bonitasoft.engine.bpm.connector.ConnectorImplementationDescriptor;\nimport org.bonitasoft.engine.bpm.connector.ConnectorNotFoundException;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDefinition;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorItem;\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\n\n/**\n * @author Colin PUY\n */\n@SuppressWarnings(\"unchecked\")\npublic class ProcessConnectorDatastoreTest extends APITestWithMock {\n\n    @Mock\n    private ProcessAPI processAPI;\n\n    private ProcessConnectorDatastore processConnectorDatastore;\n\n    @Before\n    public void initializeMocks() {\n        initMocks(this);\n\n        this.processConnectorDatastore = spy(new ProcessConnectorDatastore(null));\n\n        doReturn(this.processAPI).when(this.processConnectorDatastore).getProcessAPI();\n    }\n\n    private APIID anAPIID(final String id, final String name, final String version) {\n        final APIID apiid = APIID.makeAPIID(id, name, version);\n        apiid.setItemDefinition(ProcessConnectorDefinition.get());\n        return apiid;\n    }\n\n    private ConnectorImplementationDescriptor aConnectorImplementationDescriptor(final String name) {\n        final ConnectorImplementationDescriptor descriptor = new ConnectorImplementationDescriptor(\n                \"implementationClassName\",\n                name, \"version\", \"definitionId\", \"definitionVersion\", EMPTY_LIST);\n        return descriptor;\n    }\n\n    private HashMap<String, String> aProcessIdFilter(final String processId) {\n        final HashMap<String, String> filters = new HashMap<>();\n        filters.put(ATTRIBUTE_PROCESS_ID, processId);\n        return filters;\n    }\n\n    private ProcessConnectorItem convertToItem(final ConnectorImplementationDescriptor descriptor1,\n            final String processId) {\n        final ProcessConnectorItem item = this.processConnectorDatastore.convertEngineToConsoleItem(descriptor1);\n        item.setProcessId(processId);\n        return item;\n    }\n\n    @Test\n    public void getRetrieveConnectorImplementationAndSetProcessId() throws Exception {\n        final ConnectorImplementationDescriptor descriptor = aConnectorImplementationDescriptor(\"aName\");\n        when(this.processAPI.getConnectorImplementation(1L, \"name\", \"1\")).thenReturn(descriptor);\n\n        final ProcessConnectorItem fetchedItem = this.processConnectorDatastore.get(anAPIID(\"1\", \"name\", \"1\"));\n\n        final ProcessConnectorItem expectedItem = convertToItem(descriptor, \"1\");\n        assertTrue(areEquals(expectedItem, fetchedItem));\n    }\n\n    @Test(expected = APIException.class)\n    public void getThrowExceptionIfProcessDefinitionIsNotFound() throws Exception {\n        when(this.processAPI.getConnectorImplementation(anyLong(), anyString(), anyString()))\n                .thenThrow(new ConnectorNotFoundException(null));\n\n        this.processConnectorDatastore.get(anAPIID(\"1\", \"name\", \"1\"));\n    }\n\n    @Test\n    public void searchReturnAllProcessConnectorForAProcessDefinitionId() throws Exception {\n        final ConnectorImplementationDescriptor descriptor1 = aConnectorImplementationDescriptor(\"aName\");\n        final ConnectorImplementationDescriptor descriptor2 = aConnectorImplementationDescriptor(\"anOtherName\");\n        when(this.processAPI.getConnectorImplementations(anyLong(), anyInt(), anyInt(), any(ConnectorCriterion.class)))\n                .thenReturn(asList(descriptor1, descriptor2));\n\n        final ItemSearchResult<ProcessConnectorItem> search = this.processConnectorDatastore.search(0, 10, null,\n                \"DEFINITION_ID_ASC\", aProcessIdFilter(\"1\"));\n\n        final ProcessConnectorItem expectedItem1 = convertToItem(descriptor1, \"1\");\n        final ProcessConnectorItem expectedItem2 = convertToItem(descriptor2, \"1\");\n        assertTrue(areEquals(search.getResults().get(0), expectedItem1));\n        assertTrue(areEquals(search.getResults().get(1), expectedItem2));\n    }\n\n    @Test\n    public void testConvertEngineToConsoleItem() throws Exception {\n        final ConnectorImplementationDescriptor descriptor = new ConnectorImplementationDescriptor(\n                \"implementationClassName\",\n                \"name\", \"version\", \"definitionId\", \"definitionVersion\", EMPTY_LIST);\n        final ProcessConnectorItem expectedItem = new ProcessConnectorItem();\n        expectedItem.setName(\"definitionId\");\n        expectedItem.setVersion(\"definitionVersion\");\n        expectedItem.setImplementationName(\"name\");\n        expectedItem.setImplementationVersion(\"version\");\n        expectedItem.setClassname(\"implementationClassName\");\n\n        final ProcessConnectorItem convertedItem = this.processConnectorDatastore\n                .convertEngineToConsoleItem(descriptor);\n\n        assertTrue(areEquals(expectedItem, convertedItem));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/process/ProcessConnectorDependencyDatastoreTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.process;\n\nimport static java.util.Arrays.*;\nimport static junit.framework.Assert.*;\nimport static org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDependencyItem.*;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.*;\nimport static org.mockito.MockitoAnnotations.*;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.connector.ConnectorImplementationDescriptor;\nimport org.bonitasoft.engine.bpm.connector.ConnectorNotFoundException;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDependencyItem;\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\n\n/**\n * @author Colin PUY\n */\npublic class ProcessConnectorDependencyDatastoreTest extends APITestWithMock {\n\n    @Mock\n    private ProcessAPI processAPI;\n\n    private ProcessConnectorDependencyDatastore datastore;\n\n    @Before\n    public void initializeMocks() {\n        initMocks(this);\n\n        this.datastore = spy(new ProcessConnectorDependencyDatastore(null));\n\n        doReturn(this.processAPI).when(this.datastore).getProcessAPI();\n    }\n\n    private Map<String, String> buildFilters(final Long processId, final String connectorName,\n            final String connectorVersion) {\n        final Map<String, String> filters = new HashMap<>();\n        filters.put(ATTRIBUTE_PROCESS_ID, String.valueOf(processId));\n        filters.put(ATTRIBUTE_CONNECTOR_NAME, connectorName);\n        filters.put(ATTRIBUTE_CONNECTOR_VERSION, connectorVersion);\n        return filters;\n    }\n\n    @Test\n    public void searchReturnAnEmptyResultIfNoConnectorImplementationIsFound() throws Exception {\n        when(this.processAPI.getConnectorImplementation(anyLong(), anyString(), anyString())).thenReturn(null);\n        final Map<String, String> filters = buildFilters(1L, \"aConnectorName\", \"1\");\n\n        final ItemSearchResult<ProcessConnectorDependencyItem> searchResult = this.datastore.search(0, 10, null, null,\n                filters);\n\n        assertTrue(searchResult.getResults().isEmpty());\n        assertEquals(0L, searchResult.getTotal());\n    }\n\n    @Test(expected = APIException.class)\n    @SuppressWarnings(\"unchecked\")\n    public void searchThrowExceptionIfProcessIdIsUnknown() throws Exception {\n        when(this.processAPI.getConnectorImplementation(anyLong(), anyString(), anyString())).thenThrow(\n                new ConnectorNotFoundException(new NullPointerException()));\n        final Map<String, String> filters = buildFilters(1L, \"aConnectorName\", \"1\");\n\n        this.datastore.search(0, 10, null, null, filters);\n    }\n\n    @Test\n    public void searchCanBePaginated() throws Exception {\n        final ConnectorImplementationDescriptor connectorWith3Dependencies = new ConnectorImplementationDescriptor(\n                \"implementationClassName\", \"connectorId\", \"connectorVersion\", \"1\", \"definitionVersion\",\n                asList(\"dependency1\", \"dependency2\", \"dependency3\"));\n        when(this.processAPI.getConnectorImplementation(1L, \"connectorId\", \"connectorVersion\"))\n                .thenReturn(connectorWith3Dependencies);\n        final Map<String, String> filters = buildFilters(1L, \"connectorId\", \"connectorVersion\");\n\n        final ItemSearchResult<ProcessConnectorDependencyItem> searchResult = this.datastore.search(0, 2, null, null,\n                filters);\n\n        assertEquals(2L, searchResult.getResults().size());\n        assertEquals(3L, searchResult.getTotal());\n        assertEquals(searchResult.getResults().get(0).getFilename(), \"dependency1\");\n        assertEquals(searchResult.getResults().get(1).getFilename(), \"dependency2\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/process/ProcessDatastoreTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.process;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.console.common.server.page.CustomPageService;\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.console.common.server.utils.PlatformManagementUtils;\nimport org.bonitasoft.engine.api.PageAPI;\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessItem;\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.bonitasoft.web.rest.server.engineclient.ProcessEngineClient;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ProcessDatastoreTest extends APITestWithMock {\n\n    private ProcessDatastore processDatastore;\n\n    @Mock\n    private APISession engineSession;\n\n    @Mock\n    private ProcessEngineClient processEngineClient;\n\n    @Mock\n    private BonitaHomeFolderAccessor tenantFolder;\n\n    @Mock\n    private PageAPI pageAPI;\n\n    @Mock\n    private CustomPageService customPageService;\n\n    @Mock\n    private SearchResult<Page> searchResult;\n\n    @Mock\n    private PlatformManagementUtils platformManagementUtils;\n\n    private final ProcessItem processItem = new ProcessItem();\n\n    @Before\n    public void setUp() throws Exception {\n        processDatastore = spy(new ProcessDatastore(engineSession));\n        doReturn(processEngineClient).when(processDatastore).getProcessEngineClient();\n        doReturn(customPageService).when(processDatastore).getCustomPageService();\n        doReturn(pageAPI).when(processDatastore).getPageAPI();\n        doReturn(searchResult).when(pageAPI).searchPages(any(SearchOptions.class));\n    }\n\n    @Test\n    public void it_removes_the_pages_when_deleting_a_process() throws IOException {\n\n        final Page page1 = mock(Page.class);\n        final Page page2 = mock(Page.class);\n        doReturn(Arrays.asList(page1, page2)).when(searchResult).getResult();\n        doReturn(2L).when(searchResult).getCount();\n\n        final APIID id = APIID.makeAPIID(2L);\n        processDatastore.delete(List.of(id));\n\n        verify(processDatastore).removeProcessPagesFromHome(id);\n        verify(customPageService, times(1)).removePageLocally(page1);\n        verify(customPageService, times(1)).removePageLocally(page2);\n    }\n\n    @Test\n    public void it_removes_the_pages_when_deleting_a_process_with_pagination() throws IOException {\n\n        final long nbOfPages = 130L;\n        final Page page = mock(Page.class);\n        final List<Page> pages = new ArrayList<>();\n        for (int i = 0; i < nbOfPages; i++) {\n            pages.add(page);\n        }\n        doReturn(pages).when(searchResult).getResult();\n        doReturn(nbOfPages).when(searchResult).getCount();\n\n        final APIID id = APIID.makeAPIID(2L);\n        processDatastore.delete(List.of(id));\n\n        verify(processDatastore).removeProcessPagesFromHome(id);\n        verify(customPageService, times((int) nbOfPages)).removePageLocally(page);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/process/helper/ProcessItemConverterTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.bpm.process.helper;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Date;\n\nimport org.bonitasoft.console.common.server.i18n.I18n;\nimport org.bonitasoft.console.common.server.utils.TenantCacheUtilFactory;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.actor.ActorInstance;\nimport org.bonitasoft.engine.bpm.actor.ActorNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ActivationState;\nimport org.bonitasoft.engine.bpm.process.ConfigurationState;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo;\nimport org.bonitasoft.engine.bpm.process.impl.internal.ProcessDeploymentInfoImpl;\nimport org.bonitasoft.web.rest.model.ModelFactory;\nimport org.bonitasoft.web.rest.model.bpm.process.ProcessItem;\nimport org.bonitasoft.web.toolkit.client.ItemDefinitionFactory;\nimport org.bonitasoft.web.toolkit.client.common.CommonDateFormater;\nimport org.bonitasoft.web.toolkit.server.utils.ServerDateFormater;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ProcessItemConverterTest {\n\n    private ProcessItemConverter processItemConverter;\n\n    @Mock\n    private ProcessAPI processAPI;\n\n    @Mock\n    private ActorInstance actorInstance1;\n\n    @Mock\n    private ActorInstance actorInstance2;\n\n    @Before\n    public void setUp() throws Exception {\n        I18n.getInstance();\n        CommonDateFormater.setDateFormater(new ServerDateFormater());\n        ItemDefinitionFactory.setDefaultFactory(new ModelFactory());\n        processItemConverter = spy(new ProcessItemConverter(processAPI));\n        TenantCacheUtilFactory.clearTenantCacheUtil();\n    }\n\n    @Test\n    public void shouldReadActorInitiatorFromCacheOnSecondCall()\n            throws ProcessDefinitionNotFoundException, ActorNotFoundException {\n\n        when(processAPI.getActorInitiator(3L)).thenReturn(actorInstance2);\n        doReturn(6L).when(actorInstance2).getId();\n\n        ProcessDeploymentInfo processDeploymentInfo = new ProcessDeploymentInfoImpl(1, 3L, \"ProcessName\", \"Version\",\n                \"Description\", new Date(), 3, ActivationState.ENABLED, ConfigurationState.RESOLVED, \"displayName\",\n                new Date(), \"iconPath\",\n                \"displayDescription\");\n\n        ProcessItem processItem = processItemConverter.convert(processDeploymentInfo);\n\n        //Get 2 ActorInitiatorId from engine then store them in cache\n        assertEquals(\"6\", processItem.getActorInitiatorId());\n\n        processItem = processItemConverter.convert(processDeploymentInfo);\n\n        //Get  ActorInitiatorId from cache\n        assertEquals(\"6\", processItem.getActorInitiatorId());\n        //it should call getActorInitiator only one times because the second should be read from the cache\n        verify(processAPI, times(1)).getActorInitiator(3L);\n    }\n\n    @Test\n    public void shouldStoreDifferentActorInitiatorIntoCache()\n            throws ActorNotFoundException, ProcessDefinitionNotFoundException {\n\n        when(processAPI.getActorInitiator(1L)).thenReturn(actorInstance1);\n        when(processAPI.getActorInitiator(2L)).thenReturn(actorInstance2);\n        doReturn(5L).when(actorInstance1).getId();\n        doReturn(6L).when(actorInstance2).getId();\n\n        ProcessDeploymentInfo firstProcessDeploymentInfo = new ProcessDeploymentInfoImpl(1, 1, \"ProcessName1\",\n                \"Version1\",\n                \"Description1\", new Date(), 3, ActivationState.ENABLED, ConfigurationState.RESOLVED, \"displayName1\",\n                new Date(), \"iconPath1\",\n                \"displayDescription1\");\n        ProcessDeploymentInfo secondProcessDeploymentInfo = new ProcessDeploymentInfoImpl(2, 2, \"ProcessName2\",\n                \"Version2\",\n                \"Description2\", new Date(), 3, ActivationState.ENABLED, ConfigurationState.RESOLVED, \"displayName2\",\n                new Date(), \"iconPath2\",\n                \"displayDescription2\");\n\n        //Get 2 ActorInitiatorId from engine then store them in cache\n        ProcessItem processItem = processItemConverter.convert(firstProcessDeploymentInfo);\n        assertEquals(\"5\", processItem.getActorInitiatorId());\n        processItem = processItemConverter.convert(secondProcessDeploymentInfo);\n        assertEquals(\"6\", processItem.getActorInitiatorId());\n\n        // Read 2 different ActorInitiatorId from cache\n        processItem = processItemConverter.convert(firstProcessDeploymentInfo);\n        assertEquals(\"5\", processItem.getActorInitiatorId());\n        processItem = processItemConverter.convert(secondProcessDeploymentInfo);\n        assertEquals(\"6\", processItem.getActorInitiatorId());\n\n        //it should call getActorInitiator only one times because the second should be read from the cache\n        verify(processAPI, times(1)).getActorInitiator(1L);\n        verify(processAPI, times(1)).getActorInitiator(2L);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/converter/ItemSearchResultConverterTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.converter;\n\nimport static java.util.Arrays.asList;\nimport static junit.framework.Assert.assertEquals;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\nimport static org.mockito.MockitoAnnotations.initMocks;\n\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APISearchIndexOutOfRange;\nimport org.bonitasoft.web.toolkit.client.data.item.IItem;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\n\n/**\n * @author Vincent Elcrin\n */\npublic class ItemSearchResultConverterTest {\n\n    @Mock\n    SearchResult<String> result;\n\n    @Mock\n    ItemConverter<IItem, String> converter;\n\n    @Before\n    public void initializeMocks() {\n        initMocks(this);\n    }\n\n    @Test\n    public void testTotalCanBeRetrieved() {\n        when(result.getCount()).thenReturn(5L);\n\n        ItemSearchResultConverter<IItem, String> itemSearchResult = new ItemSearchResultConverter<>(3, 2, result,\n                converter);\n\n        assertEquals(5L, itemSearchResult.toItemSearchResult().getTotal());\n    }\n\n    @Test\n    public void testTotalSetCanBeRetrieved() {\n        ItemSearchResultConverter<IItem, String> itemSearchResult = new ItemSearchResultConverter<>(3, 2, result, 8L,\n                converter);\n\n        assertEquals(8L, itemSearchResult.toItemSearchResult().getTotal());\n    }\n\n    @Test(expected = APISearchIndexOutOfRange.class)\n    public void testPageOutOfResultNumberThrowsException() {\n        when(result.getCount()).thenReturn(1L);\n\n        new ItemSearchResultConverter<>(2, 10, result, converter).toItemSearchResult();\n    }\n\n    @Test\n    public void testPageNumberCanBeRetrieved() {\n        ItemSearchResultConverter<IItem, String> itemSearchResult = new ItemSearchResultConverter<>(5, 10, result, 8L,\n                converter);\n\n        assertEquals(5, itemSearchResult.toItemSearchResult().getPage());\n    }\n\n    @Test\n    public void testResultingItemsCanBeRetrieved() {\n        IItem item1 = mock(IItem.class);\n        IItem item2 = mock(IItem.class);\n        when(result.getResult()).thenReturn(asList(\"item1\", \"item2\"));\n        when(converter.convert(result.getResult())).thenReturn(asList(item1, item2));\n\n        ItemSearchResultConverter<IItem, String> itemSearchResult = new ItemSearchResultConverter<>(1, 10, result, 2,\n                converter);\n\n        assertEquals(item1, itemSearchResult.toItemSearchResult().getResults().get(0));\n        assertEquals(item2, itemSearchResult.toItemSearchResult().getResults().get(1));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/converter/LongValueConverterTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.converter;\n\nimport static junit.framework.Assert.assertEquals;\n\nimport org.junit.Test;\n\n/**\n * @author Vincent Elcrin\n */\npublic class LongValueConverterTest {\n\n    @Test\n    public void testLongValueConvertion() throws Exception {\n        Long value = new LongValueConverter().convert(\"54\");\n\n        assertEquals(Long.valueOf(54L), value);\n    }\n\n    @Test(expected = NumberFormatException.class)\n    public void testStringConvertionThrowsExceptio() throws Exception {\n        new LongValueConverter().convert(\"abc\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/converter/StringValueConverterTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.converter;\n\nimport static org.junit.Assert.assertEquals;\n\nimport org.junit.Test;\n\n/**\n * @author Vincent Elcrin\n */\npublic class StringValueConverterTest {\n\n    @Test\n    public void testStringConvertion() throws Exception {\n        String value = new StringValueConverter().convert(\"cba\");\n\n        assertEquals(\"cba\", value);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/filter/FieldTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.filter;\n\nimport junit.framework.Assert;\nimport org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter;\nimport org.junit.Test;\nimport org.mockito.Mock;\nimport org.mockito.Mockito;\n\n/**\n * @author Vincent Elcrin\n */\npublic class FieldTest {\n\n    @Mock\n    private AttributeConverter converter = Mockito.mock(AttributeConverter.class);\n\n    @Test\n    public void testFieldConvertion() {\n        Mockito.doReturn(\"converted\").when(converter).convert(\"attribute\");\n        Field field = new Field(\"attribute\", converter);\n\n        String s = field.toString();\n\n        Assert.assertEquals(\"converted\", s);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/filter/FilterAccessorTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.filter;\n\nimport static org.junit.Assert.assertEquals;\n\nimport java.util.Collections;\n\nimport org.bonitasoft.web.rest.server.datastore.profile.member.MemberTypeConverter;\nimport org.bonitasoft.web.rest.server.framework.exception.APIFilterMandatoryException;\nimport org.junit.Test;\n\n/**\n * @author Vincent Elcrin\n */\npublic class FilterAccessorTest {\n\n    @Test\n    public void testWeCanRetrieveMandatoryValue() throws Exception {\n        FilterAccessor filterAccess = new FilterAccessor(Collections.singletonMap(\"key\", \"value\"));\n\n        String value = filterAccess.getMandatory(\"key\");\n\n        assertEquals(\"value\", value);\n    }\n\n    @Test(expected = APIFilterMandatoryException.class)\n    public void testAccessToMandatoryValueWhichItDoesntExitThrowException() {\n        FilterAccessor filterAccess = new FilterAccessor(Collections.<String, String> emptyMap());\n\n        filterAccess.getMandatory(\"key\");\n    }\n\n    @Test(expected = APIFilterMandatoryException.class)\n    public void testAccessToMandatoryValueNotConvertibleThrowException() {\n        FilterAccessor filterAccess = new FilterAccessor(Collections.singletonMap(\"key\", \"value\"));\n\n        filterAccess.getMandatory(\"key\", new MemberTypeConverter());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/filter/FilterTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.filter;\n\nimport junit.framework.Assert;\nimport org.junit.Test;\nimport org.mockito.Mock;\nimport org.mockito.Mockito;\n\n/**\n * @author Vincent Elcrin\n */\npublic class FilterTest {\n\n    @Mock\n    Field field = Mockito.mock(Field.class);\n\n    @Mock\n    @SuppressWarnings(\"unchecked\")\n    Value<String> value = Mockito.mock(Value.class);\n\n    @Test\n    public void testFilterField() throws Exception {\n        Mockito.doReturn(\"field\").when(field).toString();\n        Filter<String> filter = new Filter<>(field, value);\n\n        Assert.assertEquals(\"field\", filter.getField());\n    }\n\n    @Test\n    public void testFilterValue() throws Exception {\n        Mockito.doReturn(\"value\").when(value).cast();\n        Filter<String> filter = new Filter<>(field, value);\n\n        Assert.assertEquals(\"value\", filter.getValue());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/filter/FiltersTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.filter;\n\nimport static junit.framework.Assert.assertTrue;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.when;\nimport static org.mockito.MockitoAnnotations.initMocks;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport junit.framework.Assert;\nimport org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter;\nimport org.bonitasoft.web.toolkit.client.common.texttemplate.Arg;\nimport org.bonitasoft.web.toolkit.client.common.util.MapUtil;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\n\n/**\n * @author Vincent Elcrin\n */\npublic class FiltersTest {\n\n    @Mock\n    FilterCreator filterCreator;\n\n    @Mock\n    Filter<Long> longFilter;\n\n    @Mock\n    Filter<String> strFilter;\n\n    @Mock\n    AttributeConverter fiedConverter;\n\n    @Before\n    public void initFilters() {\n        initMocks(this);\n\n        doReturn(\"field1\").when(longFilter).getField();\n        doReturn(3L).when(longFilter).getValue();\n\n        doReturn(\"field2\").when(strFilter).getField();\n        doReturn(\"str\").when(strFilter).getValue();\n    }\n\n    @Test\n    public void testFiltersListWithoutFilterCreator() throws Exception {\n        when(fiedConverter.convert(\"field\"))\n                .thenReturn(\"field\");\n        Filters filters = new Filters(aMapWith(new Arg(\"field\", \"value\")), new GenericFilterCreator(fiedConverter));\n\n        List<Filter<?>> filterList = filters.asList();\n\n        Assert.assertEquals(\"field\", filterList.get(0).getField());\n        Assert.assertEquals(\"value\", filterList.get(0).getValue());\n    }\n\n    @Test\n    public void testFilterListWithMultiTypeFilterCreator() throws Exception {\n        Map<String, String> map = aMapWith(new Arg(\"field1\", \"value\"),\n                new Arg(\"field2\", \"value\"));\n\n        doReturn(longFilter).when(filterCreator).create(eq(\"field1\"), anyString());\n        doReturn(strFilter).when(filterCreator).create(eq(\"field2\"), anyString());\n\n        Filters filters = new Filters(map, filterCreator);\n\n        assertTrue(IsRightValue(filters.asList().get(0)));\n        assertTrue(IsRightValue(filters.asList().get(1)));\n    }\n\n    private boolean IsRightValue(Filter<?> filter) {\n        if (\"field1\".equals(filter.getField())) {\n            return filter.getValue().equals(3L);\n        } else if (\"field2\".equals(filter.getField())) {\n            return filter.getValue().equals(\"str\");\n        } else {\n            return false;\n        }\n    }\n\n    private Map<String, String> aMapWith(Arg... args) {\n        return MapUtil.asMap(args);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/filter/GenericFilterCreatorTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.filter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.MockitoAnnotations.initMocks;\n\nimport java.io.Serializable;\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter;\nimport org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Vincent Elcrin\n * @author Emmanuel Duchastenier\n */\npublic class GenericFilterCreatorTest {\n\n    @Before\n    public void setUp() {\n        initMocks(this);\n    }\n\n    @Test\n    public void method_create_should_handle_value_as_String_by_default() {\n        GenericFilterCreator creator = new GenericFilterCreator(new AttributeConverter() {\n\n            @Override\n            public String convert(String attribute) {\n                return attribute;\n            }\n\n            @Override\n            public Map<String, ItemAttribute.TYPE> getValueTypeMapping() {\n                return Collections.emptyMap();\n            }\n        });\n\n        Filter<? extends Serializable> filter = creator.create(\"attribute\", \"value\");\n\n        assertEquals(\"attribute\", filter.getField());\n        assertEquals(\"value\", filter.getValue());\n    }\n\n    @Test\n    public void method_create_should_handle_boolean_values() {\n        AttributeConverter converter = new AttributeConverter() {\n\n            @Override\n            public String convert(String attribute) {\n                return attribute;\n            }\n\n            @Override\n            public Map<String, ItemAttribute.TYPE> getValueTypeMapping() {\n                return Collections.singletonMap(\"myAttribute\", ItemAttribute.TYPE.BOOLEAN);\n            }\n        };\n        GenericFilterCreator creator = new GenericFilterCreator(converter);\n\n        Filter<? extends Serializable> filter = creator.create(\"myAttribute\", \"true\");\n\n        assertThat(filter.getValue()).isEqualTo(true);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/filter/LongValueTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.filter;\n\nimport static junit.framework.Assert.assertEquals;\n\nimport org.junit.Test;\n\n/**\n * @author Vincent Elcrin\n */\npublic class LongValueTest {\n\n    @Test\n    public void testLongValue() {\n        LongValue value = new LongValue(\"8\");\n\n        assertEquals(Long.valueOf(8), value.cast());\n    }\n\n    @Test(expected = NumberFormatException.class)\n    public void testNoneLongValueThrowException() {\n        LongValue value = new LongValue(\"abc\");\n\n        value.cast();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/filter/StrValueTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.filter;\n\nimport static org.junit.Assert.assertEquals;\n\nimport org.junit.Test;\n\n/**\n * @author Vincent Elcrin\n */\npublic class StrValueTest {\n\n    @Test\n    public void testStringValue() throws Exception {\n        StrValue value = new StrValue(\"abc\");\n\n        assertEquals(\"abc\", value.cast());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/filter/ValueTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.filter;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.mock;\n\nimport org.bonitasoft.web.rest.server.datastore.converter.ValueConverter;\nimport org.junit.Test;\n\n/**\n * @author Vincent Elcrin\n */\npublic class ValueTest {\n\n    @Test\n    public void testName() throws Exception {\n        @SuppressWarnings(\"unchecked\")\n        ValueConverter<Long> converter = mock(ValueConverter.class);\n        doReturn(5L).when(converter).convert(\"12\");\n\n        Value<Long> value = new Value<>(\"12\", converter);\n\n        assertEquals(Long.valueOf(5), value.cast());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/organization/GroupUpdaterConverterTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.organization;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.Mockito.*;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\n\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.console.common.server.utils.IconDescriptor;\nimport org.bonitasoft.engine.identity.GroupUpdater;\nimport org.bonitasoft.engine.identity.GroupUpdater.GroupField;\nimport org.bonitasoft.web.rest.model.identity.GroupItem;\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.bonitasoft.web.rest.server.engineclient.GroupEngineClient;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Colin PUY\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class GroupUpdaterConverterTest extends APITestWithMock {\n\n    @Mock\n    private GroupEngineClient groupEngineClient;\n    @Mock\n    private BonitaHomeFolderAccessor bonitaHomeFolderAccessor;\n    @InjectMocks\n    @Spy\n    private GroupUpdaterConverter groupUpdaterConverter;\n\n    @Before\n    public void init() {\n        doReturn(bonitaHomeFolderAccessor).when(groupUpdaterConverter).getBonitaHomeFolderAccessor();\n    }\n\n    private Serializable getFieldValue(GroupUpdater groupUpdater, GroupField field) {\n        return groupUpdater.getFields().get(field);\n    }\n\n    private HashMap<String, String> buildSimpleAttribute(String attributeName, String attributeValue) {\n        HashMap<String, String> attributes = new HashMap<>();\n        attributes.put(attributeName, attributeValue);\n        return attributes;\n    }\n\n    @Test\n    public void convert_return_a_groupUpdater_with_required_fields() throws Exception {\n        HashMap<String, String> attributes = new HashMap<>();\n        attributes.put(GroupItem.ATTRIBUTE_DESCRIPTION, \"aNewDescription\");\n        attributes.put(GroupItem.ATTRIBUTE_ICON, \"aNewIcon\");\n        attributes.put(GroupItem.ATTRIBUTE_NAME, \"aNewName\");\n        attributes.put(GroupItem.ATTRIBUTE_DISPLAY_NAME, \"aNewDisplayName\");\n        byte[] content = { 1, 2, 3 };\n        doReturn(new IconDescriptor(\"aNewIcon.png\", content)).when(bonitaHomeFolderAccessor)\n                .getIconFromFileSystem(\"aNewIcon\");\n\n        GroupUpdater updater = groupUpdaterConverter.convert(attributes);\n\n        assertThat(getFieldValue(updater, GroupField.DESCRIPTION)).isEqualTo(\"aNewDescription\");\n        assertThat(getFieldValue(updater, GroupField.ICON_FILENAME)).isEqualTo(\"aNewIcon.png\");\n        assertThat(getFieldValue(updater, GroupField.ICON_CONTENT)).isEqualTo(content);\n        assertThat(getFieldValue(updater, GroupField.NAME)).isEqualTo(\"aNewName\");\n        assertThat(getFieldValue(updater, GroupField.DISPLAY_NAME)).isEqualTo(\"aNewDisplayName\");\n    }\n\n    @Test\n    public void convert_should_skip_empty_icon() throws Exception {\n        //given\n        HashMap<String, String> attributes = new HashMap<>();\n        attributes.put(GroupItem.ATTRIBUTE_ICON, \"\");\n        attributes.put(GroupItem.ATTRIBUTE_NAME, \"aNewName\");\n\n        //when\n        GroupUpdater updater = groupUpdaterConverter.convert(attributes);\n\n        //then\n        assertThat(getFieldValue(updater, GroupField.ICON_FILENAME)).isNull();\n        assertThat(getFieldValue(updater, GroupField.ICON_CONTENT)).isNull();\n        assertThat(getFieldValue(updater, GroupField.NAME)).isEqualTo(\"aNewName\");\n\n        verify(bonitaHomeFolderAccessor, never()).getIconFromFileSystem(anyString());\n\n    }\n\n    @Test\n    public void convert_dont_update_name_if_this_is_a_blank_value() throws Exception {\n        String unexpectedName = \" \";\n        HashMap<String, String> attribute = buildSimpleAttribute(GroupItem.ATTRIBUTE_NAME, unexpectedName);\n\n        GroupUpdater updater = groupUpdaterConverter.convert(attribute);\n\n        assertThat(getFieldValue(updater, GroupField.NAME)).isNull();\n    }\n\n    @Test\n    public void convert_update_parent_path_if_a_parent_group_id_is_specified() throws Exception {\n        HashMap<String, String> attributes = buildSimpleAttribute(GroupItem.ATTRIBUTE_PARENT_GROUP_ID, \"101\");\n        when(groupEngineClient.getPath(\"101\")).thenReturn(\"/Expected/Parent/Path\");\n\n        GroupUpdater updater = groupUpdaterConverter.convert(attributes);\n\n        assertThat(getFieldValue(updater, GroupField.PARENT_PATH)).isEqualTo(\"/Expected/Parent/Path\");\n    }\n\n    @Test\n    public void convert_set_parent_path_to_empty_if_parentGroupId_is_an_empty_string() throws Exception {\n        HashMap<String, String> attributes = buildSimpleAttribute(GroupItem.ATTRIBUTE_PARENT_GROUP_ID, \"\");\n\n        GroupUpdater updater = groupUpdaterConverter.convert(attributes);\n\n        assertThat(getFieldValue(updater, GroupField.PARENT_PATH)).isEqualTo(\"\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/organization/RoleDatastoreTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.organization;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyLong;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Collections;\n\nimport org.bonitasoft.console.common.server.i18n.I18n;\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.console.common.server.utils.IconDescriptor;\nimport org.bonitasoft.engine.api.IdentityAPI;\nimport org.bonitasoft.engine.identity.RoleCreator;\nimport org.bonitasoft.engine.identity.RoleUpdater;\nimport org.bonitasoft.engine.identity.impl.RoleImpl;\nimport org.bonitasoft.web.rest.model.ModelFactory;\nimport org.bonitasoft.web.rest.model.identity.RoleItem;\nimport org.bonitasoft.web.toolkit.client.ItemDefinitionFactory;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Baptiste Mesta\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class RoleDatastoreTest {\n\n    @InjectMocks\n    @Spy\n    private RoleDatastore roleDatastore;\n    @Mock\n    private IdentityAPI identityAPI;\n    @Mock\n    private BonitaHomeFolderAccessor bonitaHomeFolderAccessor;\n    @Captor\n    private ArgumentCaptor<RoleUpdater> roleUpdaterArgumentCaptor;\n    @Captor\n    private ArgumentCaptor<RoleCreator> roleCreatorArgumentCaptor;\n\n    @Before\n    public void before() throws Exception {\n        ItemDefinitionFactory.setDefaultFactory(new ModelFactory());\n        I18n.getInstance();\n        Item.setApplyInputModifiersByDefault(false);\n        Item.setApplyValidatorsByDefault(false);\n        Item.setApplyOutputModifiersByDefault(false);\n        Item.setApplyValidatorMandatoryByDefault(false);\n        doReturn(identityAPI).when(roleDatastore).getIdentityAPI();\n        doReturn(bonitaHomeFolderAccessor).when(roleDatastore).getBonitaHomeFolderAccessor();\n    }\n\n    @Test\n    public void should_get_retrieve_role_with_icon() throws Exception {\n        //given\n        RoleImpl myRole = new RoleImpl(12L, \"myRole\");\n        myRole.setIconId(2134L);\n        doReturn(myRole).when(identityAPI).getRole(12L);\n        //when\n        RoleItem roleItem = roleDatastore.get(APIID.makeAPIID(12L));\n        //then\n        assertThat(roleItem.getIcon()).isEqualTo(\"../API/avatars/2134\");\n    }\n\n    @Test\n    public void should_get_retrieve_role_without_icon() throws Exception {\n        //given\n        RoleImpl myRole = new RoleImpl(12L, \"myRole\");\n        doReturn(myRole).when(identityAPI).getRole(12L);\n        //when\n        RoleItem roleItem = roleDatastore.get(APIID.makeAPIID(12L));\n        //then\n        assertThat(roleItem.getIcon()).isEmpty();\n    }\n\n    @Test\n    public void should_retrieve_role_from_engine() throws Exception {\n        //given\n        doReturn(new RoleImpl(123, \"myRole\")).when(identityAPI).getRole(123L);\n        //when\n        RoleItem roleItem = roleDatastore.get(APIID.makeAPIID(123L));\n        //then\n        assertThat(roleItem.getName()).isEqualTo(\"myRole\");\n    }\n\n    @Test\n    public void should_update_role_in_the_engine() throws Exception {\n        RoleUpdater roleUpdater = new RoleUpdater().setName(\"newName\");\n        doReturn(new RoleImpl(123, \"newName\")).when(identityAPI).updateRole(eq(123L), eq(roleUpdater));\n        //when\n        roleDatastore.update(APIID.makeAPIID(123L), Collections.singletonMap(\"name\", \"newName\"));\n        //then\n        verify(identityAPI).updateRole(eq(123L), eq(roleUpdater));\n    }\n\n    @Test\n    public void should_update_icon_of_role_give_content_to_engine() throws Exception {\n        doReturn(new RoleImpl(123, \"newName\")).when(identityAPI).updateRole(anyLong(), any(RoleUpdater.class));\n        IconDescriptor iconDescriptor = new IconDescriptor(\"iconName\", \"content\".getBytes());\n        doReturn(iconDescriptor).when(bonitaHomeFolderAccessor).getIconFromFileSystem(eq(\"temp_icon_on_fs\"));\n        //when\n        roleDatastore.update(APIID.makeAPIID(123L), Collections.singletonMap(\"icon\", \"temp_icon_on_fs\"));\n        //then\n        verify(identityAPI).updateRole(eq(123L), roleUpdaterArgumentCaptor.capture());\n        RoleUpdater roleUpdater = roleUpdaterArgumentCaptor.getValue();\n        assertThat(roleUpdater.getFields().get(RoleUpdater.RoleField.ICON_FILENAME)).isEqualTo(\"iconName\");\n        assertThat(roleUpdater.getFields().get(RoleUpdater.RoleField.ICON_CONTENT)).isEqualTo(\"content\".getBytes());\n    }\n\n    @Test\n    public void should_update_role_with_empty_icon() throws Exception {\n        //given\n        doReturn(new RoleImpl(123, \"newName\")).when(identityAPI).updateRole(anyLong(), any(RoleUpdater.class));\n\n        //when\n        roleDatastore.update(APIID.makeAPIID(123L), Collections.singletonMap(\"icon\", \"\"));\n\n        //then\n        verify(identityAPI).updateRole(eq(123L), roleUpdaterArgumentCaptor.capture());\n        RoleUpdater roleUpdater = roleUpdaterArgumentCaptor.getValue();\n        assertThat(roleUpdater.getFields().get(RoleUpdater.RoleField.ICON_FILENAME)).isNull();\n        assertThat(roleUpdater.getFields().get(RoleUpdater.RoleField.ICON_CONTENT)).isNull();\n\n        verify(bonitaHomeFolderAccessor, never()).getIconFromFileSystem(anyString());\n    }\n\n    @Test\n    public void should_add_role_with_icon_give_content_to_engine() throws Exception {\n        doReturn(new RoleImpl(123, \"newName\")).when(identityAPI).createRole(any(RoleCreator.class));\n        IconDescriptor iconDescriptor = new IconDescriptor(\"iconName\", \"content\".getBytes());\n        doReturn(iconDescriptor).when(bonitaHomeFolderAccessor).getIconFromFileSystem(eq(\"temp_icon_on_fs\"));\n        RoleItem roleItem = new RoleItem();\n        roleItem.setIcon(\"temp_icon_on_fs\");\n        roleItem.setName(\"name\");\n        //when\n        roleDatastore.add(roleItem);\n        //then\n        verify(identityAPI).createRole(roleCreatorArgumentCaptor.capture());\n        RoleCreator roleUpdater = roleCreatorArgumentCaptor.getValue();\n        assertThat(roleUpdater.getFields().get(RoleCreator.RoleField.ICON_FILENAME)).isEqualTo(\"iconName\");\n        assertThat(roleUpdater.getFields().get(RoleCreator.RoleField.ICON_CONTENT)).isEqualTo(\"content\".getBytes());\n        assertThat(roleUpdater.getFields().get(RoleCreator.RoleField.NAME)).isEqualTo(\"name\");\n    }\n\n    @Test\n    public void add_role_without_icon_should_create_role_without_retrieving_icon_from_filesystem() throws Exception {\n        doReturn(new RoleImpl(123, \"name\")).when(identityAPI).createRole(any(RoleCreator.class));\n        RoleItem roleItem = new RoleItem();\n        roleItem.setIcon(\"\");\n        roleItem.setName(\"name\");\n        //when\n        roleDatastore.add(roleItem);\n        //then\n        verify(bonitaHomeFolderAccessor, never()).getIconFromFileSystem(anyString());\n        verify(identityAPI).createRole(any(RoleCreator.class));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/organization/UserDatastoreTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.organization;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.console.common.server.utils.IconDescriptor;\nimport org.bonitasoft.engine.api.IdentityAPI;\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.identity.User;\nimport org.bonitasoft.engine.identity.UserUpdater;\nimport org.bonitasoft.engine.identity.impl.UserImpl;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.SearchResult;\nimport org.bonitasoft.engine.search.impl.SearchFilter;\nimport org.bonitasoft.engine.search.impl.SearchResultImpl;\nimport org.bonitasoft.web.rest.model.identity.UserItem;\nimport org.bonitasoft.web.rest.server.engineclient.ProcessEngineClient;\nimport org.bonitasoft.web.rest.server.engineclient.UserEngineClient;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Mockito;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Vincent Elcrin\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class UserDatastoreTest {\n\n    @Mock\n    private ProcessAPI processAPI;\n    @Mock\n    private ProcessEngineClient processEngineClient;\n    @Mock\n    private IdentityAPI identityAPI;\n    @Mock\n    UserItemConverter userItemConverter;\n    @Mock\n    private BonitaHomeFolderAccessor bonitaHomeFolderAccessor;\n    @Spy\n    @InjectMocks\n    private UserDatastore datastore = new UserDatastore(null);\n    @Captor\n    private ArgumentCaptor<UserUpdater> userUpdaterArgumentCaptor;\n\n    @Before\n    public void init() {\n        UserEngineClient userEngineClient = new UserEngineClient(identityAPI);\n        doReturn(userEngineClient).when(datastore).getUserEngineClient();\n        doReturn(processEngineClient).when(datastore).getProcessEngineClient();\n        when(processEngineClient.getProcessApi()).thenReturn(processAPI);\n        doReturn(bonitaHomeFolderAccessor).when(datastore).getBonitaHomeFolderAccessor();\n    }\n\n    @Test\n    public void testSearchWithMultipleSortOrderDoesNotThrowException() throws Exception {\n        final String sort = UserItem.ATTRIBUTE_FIRSTNAME + \",\" + UserItem.ATTRIBUTE_LASTNAME;\n        Mockito.doReturn(new SearchResultImpl<>(0, Collections.<User> emptyList())).when(identityAPI)\n                .searchUsers(Mockito.any(SearchOptions.class));\n        try {\n            datastore.search(0, 1, \"search\", sort, Collections.emptyMap());\n        } catch (Exception e) {\n            Assert.fail(\"Search should be able to handle multiple sort\");\n        }\n    }\n\n    @Test\n    public void should_updateUser_call_engine_api() throws Exception {\n        //given\n        doReturn(new UserImpl(12L, \"john\")).when(identityAPI).updateUser(eq(12L), any(UserUpdater.class));\n        //when\n        Map<String, String> attributes = new HashMap<>();\n        attributes.put(\"userName\", \"jack\");\n        attributes.put(\"icon\", \"\");\n        datastore.update(APIID.makeAPIID(12L), attributes);\n        //then\n        verify(identityAPI).updateUser(eq(12L), userUpdaterArgumentCaptor.capture());\n        UserUpdater userUpdater = userUpdaterArgumentCaptor.getValue();\n        assertThat(userUpdater.getFields()).containsOnly(entry(UserUpdater.UserField.USER_NAME, \"jack\"));\n    }\n\n    @Test\n    public void should_updateUser_with_icon_call_engine_api_with_content_from_FS() throws Exception {\n        //given\n        doReturn(new UserImpl(12L, \"john\")).when(identityAPI).updateUser(eq(12L), any(UserUpdater.class));\n        IconDescriptor iconDescriptor = new IconDescriptor(\"iconName\", \"content\".getBytes());\n        doReturn(iconDescriptor).when(bonitaHomeFolderAccessor).getIconFromFileSystem(eq(\"temp_icon_on_fs\"));\n        //when\n        datastore.update(APIID.makeAPIID(12L), Collections.singletonMap(\"icon\", \"temp_icon_on_fs\"));\n        //then\n        verify(identityAPI).updateUser(eq(12L), userUpdaterArgumentCaptor.capture());\n        UserUpdater userUpdater = userUpdaterArgumentCaptor.getValue();\n        assertThat(userUpdater.getFields().get(UserUpdater.UserField.ICON_FILENAME)).isEqualTo(\"iconName\");\n        assertThat(userUpdater.getFields().get(UserUpdater.UserField.ICON_CONTENT)).isEqualTo(\"content\".getBytes());\n    }\n\n    @Test\n    public void testSearchUsersWhoCanPerformTask_with_should_return_nothing() {\n        when(processAPI.searchUsersWhoCanExecutePendingHumanTask(eq(0L), any(SearchOptions.class)))\n                .thenReturn(mock(SearchResult.class));\n        ItemSearchResult<UserItem> results = datastore.searchUsersWhoCanPerformTask(\"0\", 0, 10, \"jan\",\n                Collections.EMPTY_MAP, \"\");\n        verify(processAPI, times(1)).searchUsersWhoCanExecutePendingHumanTask(anyLong(), any(SearchOptions.class));\n        assertThat(results.getLength()).isEqualTo(10);\n        assertThat(results.getPage()).isEqualTo(0);\n        assertThat(results.getTotal()).isEqualTo(0);\n        assertThat(results.getResults()).isEmpty();\n    }\n\n    @Test\n    public void testSearchUsersWhoCanPerformTask_with_should_return_one_result() {\n        @SuppressWarnings(\"rawtypes\")\n        SearchResult engineSearchResults = mock(SearchResult.class);\n        long expected = 1;\n        when(engineSearchResults.getCount()).thenReturn(expected);\n        User user = mock(User.class);\n        List<User> userList = Collections.singletonList(user);\n        when(engineSearchResults.getResult()).thenReturn(userList);\n        when(processAPI.searchUsersWhoCanExecutePendingHumanTask(eq(18L), any(SearchOptions.class)))\n                .thenReturn(engineSearchResults);\n        UserItem userItem = mock(UserItem.class);\n        List<UserItem> userItemList = Collections.singletonList(userItem);\n        when(userItemConverter.convert(userList)).thenReturn(userItemList);\n        int page = 1;\n        int resultsByPage = 8;\n        ItemSearchResult<UserItem> results = datastore.searchUsersWhoCanPerformTask(\"18\", page, resultsByPage, \"jan\",\n                Collections.EMPTY_MAP, \"\");\n        assertThat(results.getLength()).isEqualTo(resultsByPage);\n        assertThat(results.getPage()).isEqualTo(page);\n        assertThat(results.getTotal()).isEqualTo(expected);\n        assertThat(results.getResults()).isNotEmpty().hasSize(1).containsExactly(userItem);\n        verify(processAPI, times(1)).searchUsersWhoCanExecutePendingHumanTask(anyLong(), any(SearchOptions.class));\n        verify(userItemConverter, times(1)).convert(userList);\n    }\n\n    @Test\n    public void buildSearchOptionCreator_should_convert_enabled_attribute_to_boolean() {\n        final List<SearchFilter> filters = datastore.buildSearchOptionCreator(0, 10, \"\",\n                Collections.singletonMap(UserItem.ATTRIBUTE_ENABLED, \"true\"), \"displayName ASC\").create().getFilters();\n        assertThat(filters.get(0).getValue()).isEqualTo(true);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/organization/UserSearchAttributeConverterTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.organization;\n\nimport static junit.framework.Assert.assertEquals;\n\nimport org.bonitasoft.engine.identity.UserSearchDescriptor;\nimport org.bonitasoft.web.rest.model.identity.UserItem;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Colin PUY\n */\npublic class UserSearchAttributeConverterTest {\n\n    private UserSearchAttributeConverter converter;\n\n    @Before\n    public void initConverter() {\n        converter = new UserSearchAttributeConverter();\n    }\n\n    @Test\n    public void convertFistName() throws Exception {\n        String convert = converter.convert(UserItem.ATTRIBUTE_FIRSTNAME);\n\n        assertEquals(UserSearchDescriptor.FIRST_NAME, convert);\n    }\n\n    @Test\n    public void convertLastName() throws Exception {\n        String convert = converter.convert(UserItem.ATTRIBUTE_LASTNAME);\n\n        assertEquals(UserSearchDescriptor.LAST_NAME, convert);\n    }\n\n    @Test\n    public void convertUserName() throws Exception {\n        String convert = converter.convert(UserItem.ATTRIBUTE_USERNAME);\n\n        assertEquals(UserSearchDescriptor.USER_NAME, convert);\n    }\n\n    @Test\n    public void convertGroupId() throws Exception {\n        String convert = converter.convert(UserItem.FILTER_GROUP_ID);\n\n        assertEquals(UserSearchDescriptor.GROUP_ID, convert);\n    }\n\n    @Test\n    public void convertManagerId() throws Exception {\n        String convert = converter.convert(UserItem.ATTRIBUTE_MANAGER_ID);\n\n        assertEquals(UserSearchDescriptor.MANAGER_USER_ID, convert);\n    }\n\n    @Test\n    public void convertRoleId() throws Exception {\n        String convert = converter.convert(UserItem.FILTER_ROLE_ID);\n\n        assertEquals(UserSearchDescriptor.ROLE_ID, convert);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/page/CustomPageContentValidatorTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.page;\n\nimport java.io.File;\n\nimport org.bonitasoft.console.common.server.utils.UnzipUtil;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\n\npublic class CustomPageContentValidatorTest {\n\n    private CustomPageContentValidator customPageContentValidator;\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Before\n    public void setUp() throws Exception {\n        customPageContentValidator = new CustomPageContentValidator();\n    }\n\n    @Test\n    public void zip_with_index_in_resources_should_be_valid() throws Exception {\n        final File zipFileResource = new File(getClass().getResource(\"/pageWithIndexInResources.zip\").toURI());\n        UnzipUtil.unzip(zipFileResource, new File(\"target\" + File.separator + \"pageWithIndexInResources\").getPath(),\n                false);\n        final File unzipFolder = new File(\"target\" + File.separator + \"pageWithIndexInResources\");\n\n        customPageContentValidator.validate(unzipFolder);\n    }\n\n    @Test\n    public void should_throw_exception_when_theme_css_not_found() throws Exception {\n        final File invalidThemePage = new File(getClass().getResource(\"/invalidThemePage\").toURI());\n\n        expectedException.expect(InvalidPageZipContentException.class);\n        expectedException.expectMessage(\"theme.css is missing.\");\n\n        customPageContentValidator.validate(invalidThemePage);\n    }\n\n    @Test\n    public void should_throw_exception_when_page_properties_not_found() throws Exception {\n        final File invalidCustomPage = new File(getClass().getResource(\"/invalidCustomPage\").toURI());\n\n        expectedException.expect(InvalidPageZipContentException.class);\n        expectedException.expectMessage(\"page.properties descriptor is missing.\");\n\n        customPageContentValidator.validate(invalidCustomPage);\n    }\n\n    @Test\n    public void should_throw_exception_when_index_not_found() throws Exception {\n        final File invalidFormPage = new File(getClass().getResource(\"/invalidFormPage\").toURI());\n\n        expectedException.expect(InvalidPageZipContentException.class);\n        expectedException.expectMessage(\"index.html or Index.groovy is missing.\");\n\n        customPageContentValidator.validate(invalidFormPage);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/page/PageDatastoreTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.page;\n\nimport static java.util.Collections.singletonList;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.catchThrowable;\nimport static org.bonitasoft.web.toolkit.client.data.APIID.makeAPIID;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.*;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.Serializable;\nimport java.net.URISyntaxException;\nimport java.net.URL;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.commons.io.FileUtils;\nimport org.assertj.core.groups.Tuple;\nimport org.bonitasoft.console.common.server.page.CustomPageService;\nimport org.bonitasoft.console.common.server.page.extension.PageResourceProviderImpl;\nimport org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils;\nimport org.bonitasoft.console.common.server.servlet.FileUploadServlet;\nimport org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor;\nimport org.bonitasoft.engine.api.PageAPI;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.io.IOUtil;\nimport org.bonitasoft.engine.page.*;\nimport org.bonitasoft.engine.page.PageCreator.PageField;\nimport org.bonitasoft.engine.search.SearchFilterOperation;\nimport org.bonitasoft.engine.search.impl.SearchFilter;\nimport org.bonitasoft.engine.session.APISession;\nimport org.bonitasoft.web.extension.page.PageResourceProvider;\nimport org.bonitasoft.web.rest.model.portal.page.PageItem;\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.bonitasoft.web.rest.server.datastore.filter.Filters;\nimport org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator;\nimport org.bonitasoft.web.rest.server.datastore.utils.Sorts;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Fabio Lombardi\n */\n\n@RunWith(MockitoJUnitRunner.class)\npublic class PageDatastoreTest extends APITestWithMock {\n\n    public static final long TENANT_ID = 1L;\n    public static final long PAGE_ID = 123L;\n    public static final String PAGE_ZIP = \"page.zip\";\n    public static final String PAGE_REST_API_ZIP = \"pageApiExtension.zip\";\n    private PageDatastore pageDatastore;\n\n    @Mock\n    APISession engineSession;\n\n    @Mock\n    PageAPI pageAPI;\n\n    @Mock\n    PageItem pageItem;\n\n    @Mock\n    WebBonitaConstantsUtils constantsValue;\n\n    @Mock\n    Page mockedPage;\n\n    @Mock\n    Page mockedApiExtension;\n\n    @Mock\n    BonitaHomeFolderAccessor tenantFolder;\n\n    @Mock\n    CustomPageService customPageService;\n\n    @Mock\n    private File mockedZipFile;\n\n    @Rule\n    public ExpectedException expectedEx = ExpectedException.none();\n\n    PageItem pageToBeAdded;\n    PageItem apiExtensionToBeAdded;\n\n    File pagesDir = new File(\"target/bonita-home/client/tenants/1/work/pages\");\n\n    @Mock\n    private PageResourceProviderImpl pageResourceProvider;\n\n    @Before\n    public void setUp() throws Exception {\n        final Date mockedDate = new Date(0L);\n\n        final File apiExtensionZipFile = deployZipFileToTarget(PAGE_REST_API_ZIP);\n        final File pageZipFile = deployZipFileToTarget(PAGE_ZIP);\n\n        deleteDir(pagesDir);\n        // Given\n        when(mockedPage.getId()).thenReturn(PAGE_ID);\n        when(mockedPage.getName()).thenReturn(\"custompage_page1\");\n        when(mockedPage.getDisplayName()).thenReturn(\"Page 1\");\n        when(mockedPage.getDescription()).thenReturn(\"This is a page description\");\n        when(mockedPage.getInstallationDate()).thenReturn(mockedDate);\n        when(mockedPage.getLastModificationDate()).thenReturn(mockedDate);\n        when(mockedPage.getInstalledBy()).thenReturn(1L);\n        when(mockedPage.isProvided()).thenReturn(false);\n\n        when(mockedApiExtension.getId()).thenReturn(PAGE_ID);\n        when(mockedApiExtension.getName()).thenReturn(\"custompage_apiExt\");\n        when(mockedApiExtension.getDisplayName()).thenReturn(\"Page 1\");\n        when(mockedApiExtension.getDescription()).thenReturn(\"This is a page description\");\n        when(mockedApiExtension.getInstallationDate()).thenReturn(mockedDate);\n        when(mockedApiExtension.getLastModificationDate()).thenReturn(mockedDate);\n        when(mockedApiExtension.getInstalledBy()).thenReturn(1L);\n\n        when(tenantFolder.getTempFile(PAGE_ZIP)).thenReturn(pageZipFile);\n        when(tenantFolder.getTempFile(PAGE_REST_API_ZIP)).thenReturn(pageZipFile);\n\n        when(constantsValue.getTempFolder()).thenReturn(pageZipFile.getParentFile());\n\n        pageDatastore = spy(new PageDatastore(engineSession, constantsValue, pageAPI, customPageService, tenantFolder));\n\n        pageToBeAdded = new PageItem();\n        pageToBeAdded.setUrlToken(\"custompage_page1\");\n        pageToBeAdded.setDisplayName(\"Page 1\");\n        pageToBeAdded.setDescription(\"This is a page description\");\n        pageToBeAdded.setAttribute(PageDatastore.UNMAPPED_ATTRIBUTE_ZIP_FILE, pageZipFile.getName());\n\n        apiExtensionToBeAdded = new PageItem();\n        apiExtensionToBeAdded.setUrlToken(\"custompage_apiExt\");\n        apiExtensionToBeAdded.setDisplayName(\"Page 1\");\n        apiExtensionToBeAdded.setDescription(\"This is a page description\");\n        apiExtensionToBeAdded.setAttribute(PageDatastore.UNMAPPED_ATTRIBUTE_ZIP_FILE, apiExtensionZipFile.getName());\n\n    }\n\n    private File deployZipFileToTarget(final String zipFileName) throws IOException, URISyntaxException {\n        final File file = new File(getClass().getResource(zipFileName).toURI());\n        assertThat(file).as(\"file should exists \" + file.getAbsolutePath()).exists();\n        FileUtils.copyFileToDirectory(file, new File(\"target\"));\n        return new File(\"target/\" + zipFileName);\n    }\n\n    @Test\n    public void should_add_a_page_return_valid_page() throws Exception {\n\n        // Given\n        deleteDir(pagesDir);\n        when(pageAPI.createPage(any(String.class), any(byte[].class))).thenReturn(mockedPage);\n\n        // When\n        final PageItem addedPage = pageDatastore.add(pageToBeAdded);\n\n        // Validate\n        assertNotNull(addedPage);\n    }\n\n    @Test\n    public void should_add_a_api_extension_add_related_permission() throws Exception {\n        // given\n        when(pageAPI.createPage(any(String.class), any(byte[].class))).thenReturn(mockedApiExtension);\n        doReturn(pageResourceProvider).when(customPageService).getPageResourceProvider(eq(mockedApiExtension));\n\n        // when\n        pageDatastore.add(apiExtensionToBeAdded);\n\n        //then\n        verify(customPageService).writePageToPageDirectory(any(Page.class), eq(pageResourceProvider), any(File.class),\n                eq(engineSession));\n    }\n\n    @Test(expected = APIException.class)\n    public void should_add_a_not_valid_page_rise_exception() {\n        // Given\n        deleteDir(pagesDir);\n        final URL zipFileUrl = getClass().getResource(\"/InvalidPage.zip\");\n        pageToBeAdded.setAttribute(PageDatastore.UNMAPPED_ATTRIBUTE_ZIP_FILE, zipFileUrl.getPath());\n\n        // When\n        final PageItem addedPage = pageDatastore.add(pageToBeAdded);\n\n        // Validate\n        assertNotNull(addedPage);\n    }\n\n    @Test(expected = APIException.class)\n    public void should_get_a_not_existing_page_rise_exception() throws Exception {\n        // Given\n        deleteDir(pagesDir);\n        when(pageAPI.getPage(1L)).thenThrow(new PageNotFoundException(\"newPage\"));\n\n        // When\n        pageDatastore.get(makeAPIID(1L));\n    }\n\n    @Test\n    public void should_thrown_ApiException_and_not_remove_contentFile_when_delete_a_not_existing() throws Exception {\n        // Given\n        deleteDir(pagesDir);\n        when(pageAPI.getPage(1L)).thenThrow(new PageNotFoundException(\"newPage\"));\n\n        // When\n        final Throwable throwable = catchThrowable(() -> pageDatastore.delete(singletonList(makeAPIID(1L))));\n\n        assertThat(throwable).isInstanceOf(APIException.class);\n        verify(pageAPI, never()).deletePage(1L);\n        verify(customPageService, never()).removePageLocally(any(PageResourceProvider.class));\n    }\n\n    @Test\n    public void should_call_customPageService_for_custom_page_permissions_when_adding_a_page() throws Exception {\n        // Given\n        deleteDir(pagesDir);\n        when(pageAPI.createPage(any(String.class), any(byte[].class))).thenReturn(mockedPage);\n\n        // When\n        pageDatastore.add(pageToBeAdded);\n\n        // Then\n        verify(customPageService).writePageToPageDirectory(any(Page.class), eq(null), any(File.class),\n                eq(engineSession));\n    }\n\n    @Test\n    public void should_thrown_APIException_when_deletePage_thrown_DeletionException() throws Exception {\n        // Given\n        when(pageAPI.getPage(PAGE_ID)).thenReturn(mockedPage);\n        doThrow(new DeletionException(\"\")).when(pageAPI).deletePage(PAGE_ID);\n\n        // When\n        final Throwable throwable = catchThrowable(() -> pageDatastore.delete(singletonList(APIID.makeAPIID(PAGE_ID))));\n\n        assertThat(throwable).isInstanceOf(APIException.class);\n        verify(customPageService).ensurePageFolderIsUpToDate(any(), any());\n        verify(customPageService, never()).removePageLocally(any(PageResourceProvider.class));\n    }\n\n    @Test\n    public void should_deletePage_and_remove_all_dependencies_when_deleting_page() throws Exception {\n        // Given\n        when(pageAPI.getPage(PAGE_ID)).thenReturn(mockedApiExtension);\n        doReturn(pageResourceProvider).when(customPageService).getPageResourceProvider(eq(mockedApiExtension));\n\n        // When\n        pageDatastore.delete(singletonList(makeAPIID(PAGE_ID)));\n\n        // then\n        verify(pageAPI).deletePage(mockedApiExtension.getId());\n        verify(customPageService).removePageLocally(pageResourceProvider);\n        verify(customPageService).ensurePageFolderIsUpToDate(any(), any());\n    }\n\n    @Test\n    public void should_update_resource_permission_when_updating_api_extension() throws Exception {\n        // Given\n        when(pageAPI.getPage(mockedApiExtension.getId())).thenReturn(mockedApiExtension);\n        when(pageAPI.updatePage(eq(mockedApiExtension.getId()), any(PageUpdater.class))).thenReturn(mockedApiExtension);\n\n        doReturn(pageResourceProvider).when(customPageService).getPageResourceProvider(any(Page.class));\n\n        final File apiExtensionZipFile = deployZipFileToTarget(PAGE_REST_API_ZIP);\n        doReturn(apiExtensionZipFile).when(tenantFolder).getTempFile(eq(apiExtensionZipFile.getAbsolutePath()));\n\n        // When\n        final Map<String, String> attributes = new HashMap<>();\n        attributes.put(PageDatastore.UNMAPPED_ATTRIBUTE_ZIP_FILE,\n                apiExtensionZipFile.getAbsolutePath() + FileUploadServlet.RESPONSE_SEPARATOR\n                        + apiExtensionZipFile.getName());\n        pageDatastore.update(makeAPIID(mockedApiExtension.getId()), attributes);\n\n        // then\n        verify(pageAPI).updatePage(eq(PAGE_ID), any());\n        verify(customPageService).ensurePageFolderIsUpToDate(any(), any());\n        verify(customPageService).removePageLocally(any(Page.class));\n        verify(customPageService).writePageToPageDirectory(any(Page.class), eq(pageResourceProvider), any(File.class),\n                eq(engineSession));\n    }\n\n    @Test\n    public void should_not_update_resource_and_permission_pageContent_when_pageApi_updatePageContent_thown_exception()\n            throws Exception {\n        // Given\n        when(pageAPI.getPage(mockedApiExtension.getId())).thenReturn(mockedApiExtension);\n        doReturn(pageResourceProvider).when(customPageService).getPageResourceProvider(any(Page.class));\n        doThrow(new UpdateException(\"\")).when(pageAPI).updatePageContent(eq(PAGE_ID), any());\n\n        final File apiExtensionZipFile = deployZipFileToTarget(PAGE_REST_API_ZIP);\n        doReturn(apiExtensionZipFile).when(tenantFolder).getTempFile(eq(apiExtensionZipFile.getAbsolutePath()));\n\n        // When\n        final Map<String, String> attributes = new HashMap<>();\n        attributes.put(PageDatastore.UNMAPPED_ATTRIBUTE_ZIP_FILE,\n                apiExtensionZipFile.getAbsolutePath() + FileUploadServlet.RESPONSE_SEPARATOR\n                        + apiExtensionZipFile.getName());\n        Throwable throwable = catchThrowable(\n                () -> pageDatastore.update(makeAPIID(mockedApiExtension.getId()), attributes));\n\n        // then\n        assertThat(throwable).isInstanceOf(APIException.class);\n        verify(customPageService).ensurePageFolderIsUpToDate(any(), any());\n        verify(customPageService, never()).removePageLocally(any(Page.class));\n        verify(customPageService).writePageToPageDirectory(any(Page.class), eq(pageResourceProvider), any(File.class),\n                eq(engineSession));\n    }\n\n    @Test\n    public void it_throws_an_exception_when_cannot_write_file_on_add() throws IOException {\n        // Given\n        pageToBeAdded.setAttribute(PageDatastore.UNMAPPED_ATTRIBUTE_ZIP_FILE, \"error_page.zip\");\n        doThrow(new IOException(\"error\")).when(tenantFolder).getTempFile(\"error_page.zip\");\n\n        // When\n        try {\n            pageDatastore.add(pageToBeAdded);\n        } catch (final APIException e) {\n            assertEquals(e.getMessage(), \"java.io.IOException: error\");\n        }\n\n    }\n\n    @Test\n    public void it_throws_an_exception_when_cannot_write_file_on_update() throws IOException, PageNotFoundException {\n        // Given\n        final Map<String, String> attributes = new HashMap<>();\n        attributes.put(PageDatastore.UNMAPPED_ATTRIBUTE_ZIP_FILE, \"error_page.zip\");\n\n        pageToBeAdded.setAttribute(PageDatastore.UNMAPPED_ATTRIBUTE_ZIP_FILE, \"error_page.zip\");\n        doReturn(mockedPage).when(pageAPI).getPage(TENANT_ID);\n        doThrow(new IOException(\"error\")).when(tenantFolder).getTempFile(\"error_page.zip\");\n\n        // When\n\n        final Throwable throwable = catchThrowable(() -> pageDatastore.update(APIID.makeAPIID(TENANT_ID), attributes));\n\n        assertThat(throwable).isInstanceOf(APIException.class).hasMessage(\"java.io.IOException: error\");\n\n    }\n\n    private void deleteDir(final File pagesDir) {\n        if (pagesDir.exists()) {\n            try {\n                IOUtil.deleteDir(pagesDir);\n            } catch (final IOException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    @Test\n    public void it_should_set_the_process_definition_id_on_creation() throws Exception {\n        final byte[] zipContent = new byte[1];\n        final APIID processId = APIID.makeAPIID(2555L);\n        doReturn(\"contentName\").when(pageItem).getContentName();\n        doReturn(null).when(pageItem).getContentType();\n        doReturn(processId).when(pageItem).getProcessId();\n        doReturn(zipContent).when(pageDatastore).readZipFile(mockedZipFile);\n        doReturn(mockedPage).when(pageAPI).createPage(anyString(), any(byte[].class));\n        pageDatastore.createEnginePage(pageItem, mockedZipFile);\n        final PageUpdater pageUpdater = new PageUpdater();\n        pageUpdater.setProcessDefinitionId(processId.toLong());\n        final ArgumentCaptor<PageUpdater> argumentCaptor = ArgumentCaptor.forClass(PageUpdater.class);\n\n        verify(pageAPI, times(1)).updatePage(eq(mockedPage.getId()), argumentCaptor.capture());\n        assertThat(argumentCaptor.getValue()).isEqualToComparingFieldByField(pageUpdater);\n    }\n\n    @Test\n    public void it_should_not_set_the_process_definition_id_on_creation() throws Exception {\n        final byte[] zipContent = new byte[1];\n        doReturn(\"contentName\").when(pageItem).getContentName();\n        doReturn(null).when(pageItem).getProcessId();\n        doReturn(zipContent).when(pageDatastore).readZipFile(mockedZipFile);\n        doReturn(mockedPage).when(pageAPI).createPage(anyString(), any(byte[].class));\n        pageDatastore.createEnginePage(pageItem, mockedZipFile);\n\n        verify(pageAPI, times(0)).updatePage(anyLong(), any(PageUpdater.class));\n    }\n\n    @Test\n    public void it_should_set_the_content_type_on_creation_if_it_is_given() throws Exception {\n        final byte[] zipContent = new byte[1];\n        final APIID processId = APIID.makeAPIID(2555L);\n        doReturn(\"contentName\").when(pageItem).getContentName();\n        doReturn(ContentType.PAGE).when(pageItem).getContentType();\n        doReturn(processId).when(pageItem).getProcessId();\n        doReturn(zipContent).when(pageDatastore).readZipFile(mockedZipFile);\n        doReturn(mockedPage).when(pageAPI).createPage(anyString(), any(byte[].class));\n        pageDatastore.createEnginePage(pageItem, mockedZipFile);\n        final PageUpdater pageUpdater = new PageUpdater();\n        pageUpdater.setContentType(ContentType.PAGE);\n        pageUpdater.setProcessDefinitionId(processId.toLong());\n        final ArgumentCaptor<PageUpdater> argumentCaptor = ArgumentCaptor.forClass(PageUpdater.class);\n\n        verify(pageAPI, times(1)).updatePage(eq(mockedPage.getId()), argumentCaptor.capture());\n        assertThat(argumentCaptor.getValue()).isEqualToComparingFieldByField(pageUpdater);\n    }\n\n    @Test\n    public void it_should_set_the_new_zip_name_value_to_original_file_name_field_on_creation() throws Exception {\n\n        // Given\n        deleteDir(pagesDir);\n        pageToBeAdded.setAttribute(PageDatastore.UNMAPPED_ATTRIBUTE_ZIP_FILE, \"page.zip::newPage.zip\");\n        when(pageAPI.createPage(any(String.class), any(byte[].class))).thenReturn(mockedPage);\n\n        // When\n        pageDatastore.add(pageToBeAdded);\n\n        final ArgumentCaptor<PageItem> argumentCaptor = ArgumentCaptor.forClass(PageItem.class);\n\n        //then\n        verify(pageDatastore, times(1)).createEnginePage(argumentCaptor.capture(), any(File.class));\n\n        assertThat(argumentCaptor.getValue().getContentName()).isEqualTo(\"newPage.zip\");\n    }\n\n    @Test\n    public void it_should_set_the_new_zip_name_value_to_original_file_name_field_on_creation_with_contentName_attribute()\n            throws Exception {\n\n        // Given\n        deleteDir(pagesDir);\n        pageToBeAdded.setAttribute(PageDatastore.UNMAPPED_ATTRIBUTE_ZIP_FILE, \"page.zip\");\n        pageToBeAdded.setAttribute(PageItem.ATTRIBUTE_CONTENT_NAME, \"newPage.zip\");\n        when(pageAPI.createPage(any(String.class), any(byte[].class))).thenReturn(mockedPage);\n\n        // When\n        pageDatastore.add(pageToBeAdded);\n\n        final ArgumentCaptor<PageItem> argumentCaptor = ArgumentCaptor.forClass(PageItem.class);\n\n        //then\n        verify(pageDatastore, times(1)).createEnginePage(argumentCaptor.capture(), any(File.class));\n\n        assertThat(argumentCaptor.getValue().getContentName()).isEqualTo(\"newPage.zip\");\n    }\n\n    @Test\n    public void testBuildPageCreatorFrom() {\n        final String testUrlTokenString = \"testUrlTokenString\";\n        final String testContentNameString = \"testContentName\";\n        final String testDescriptionString = \"testDescriptionString\";\n        final String testDisplayNameString = \"testDescriptionString\";\n\n        doReturn(testUrlTokenString).when(pageItem).getUrlToken();\n        doReturn(testContentNameString).when(pageItem).getContentName();\n        doReturn(testDescriptionString).when(pageItem).getDescription();\n        doReturn(testDisplayNameString).when(pageItem).getDisplayName();\n\n        final PageCreator pageCreator = pageDatastore.buildPageCreatorFrom(pageItem);\n\n        final Map<PageField, Serializable> expectedFields = new HashMap<>();\n        expectedFields.put(PageField.NAME, testUrlTokenString);\n        expectedFields.put(PageField.CONTENT_NAME, testContentNameString);\n        expectedFields.put(PageField.CONTENT_TYPE, ContentType.PAGE);\n        expectedFields.put(PageField.DESCRIPTION, testDescriptionString);\n        expectedFields.put(PageField.DISPLAY_NAME, testDisplayNameString);\n\n        assertThat(pageCreator.getFields()).isEqualTo(expectedFields);\n    }\n\n    @Test\n    public void should_IsPageTokenValid_returns_true_when_url_token_starts_with_custom_page_prefix() {\n        assertThat(pageDatastore.isPageTokenValid(PageDatastore.PAGE_TOKEN_PREFIX + \"anySuffix123456\")).isTrue();\n    }\n\n    @Test\n    public void should_IsPageTokenValid_returns_false_when_url_token_contains_non_alphanumeric_values() {\n        assertThat(pageDatastore.isPageTokenValid(PageDatastore.PAGE_TOKEN_PREFIX + \"suffixxx_dsqds\")).isFalse();\n    }\n\n    @Test\n    public void should_IsPageTokenValid_returns_false_when_url_token_does_not_starts_with_custom_page_prefix() {\n        assertThat(pageDatastore.isPageTokenValid(\"WrongStartString\" + PageDatastore.PAGE_TOKEN_PREFIX + \"suffixx\"))\n                .isFalse();\n    }\n\n    @Test\n    public void should_ConvertEngineToConsoleItem_returns_null_if_item_is_null() {\n        assertThat(pageDatastore.convertEngineToConsoleItem(null)).isNull();\n    }\n\n    @Test\n    public void testAPIIdsToLong() {\n        final APIID id1 = makeAPIID(\"1\");\n        final APIID id2 = makeAPIID(\"2\");\n        final List<APIID> listId = Arrays.asList(id1, id2);\n\n        assertThat(pageDatastore.APIIdsToLong(listId)).isEqualTo(Arrays.asList(1L, 2L));\n    }\n\n    @Test\n    public void testRunSearch() throws Exception {\n        final SearchOptionsCreator creator = new SearchOptionsCreator(0, 1, null, new Sorts(null), new Filters(null));\n        pageDatastore.runSearch(creator);\n        verify(pageAPI, times(1)).searchPages(creator.create());\n    }\n\n    @Test\n    public void should_Get_returns_a_page_item_for_a_given_APIID() throws Exception {\n        final APIID apiid = makeAPIID(2L);\n        doReturn(mockedPage).when(pageAPI).getPage(apiid.toLong());\n\n        pageDatastore.get(apiid);\n\n        verify(pageAPI, times(1)).getPage(apiid.toLong());\n        verify(pageDatastore, times(1)).convertEngineToConsoleItem(mockedPage);\n    }\n\n    @Test\n    public void should_ConvertEngineToConsoleItem_return_null_when_null_page_is_given() {\n        //when\n        final PageItem testPageItem = pageDatastore.convertEngineToConsoleItem(null);\n        //then\n        assertThat(testPageItem).isNull();\n    }\n\n    @Test\n    public void should_ConvertEngineToConsoleItem_return_a_page_item_when_page_is_given() {\n        //when\n        final PageItem testPageItem = pageDatastore.convertEngineToConsoleItem(mockedPage);\n        //then\n        assertThat(testPageItem).isNotNull();\n    }\n\n    @Test\n    public void should_DeleteTempDirectory_throw_API_Exception_when_an_exception_is_raised() throws Exception {\n        //given\n        when(mockedZipFile.isDirectory()).thenReturn(true);\n        doThrow(new IOException(\"to be test\")).when(pageDatastore).IOUtilDeleteDir(mockedZipFile);\n\n        //when\n        try {\n            pageDatastore.deleteTempDirectory(mockedZipFile);\n        } catch (final Exception e) {\n            assertThat(e).isInstanceOf(APIException.class);\n        }\n    }\n\n    @Test\n    public void makeSearchOptionCreator_converts_isProvided_field_to_boolean() {\n        final List<SearchFilter> filters = pageDatastore.makeSearchOptionCreator(0, 10, \"\", \"displayName ASC\",\n                Collections.singletonMap(PageItem.ATTRIBUTE_IS_PROVIDED, \"false\")).create().getFilters();\n        assertThat(filters.get(0).getValue()).isEqualTo(false);\n    }\n\n    @Test\n    public void makeSearchOptionCreator_with_empty_filter_map_should_return_empty_filter_list() {\n        final SearchOptionsCreator searchOptionsCreator = pageDatastore.makeSearchOptionCreator(0, 10, \"\",\n                \"displayName ASC\", new HashMap<>());\n        final List<SearchFilter> filters = searchOptionsCreator.create().getFilters();\n        assertThat(filters).isEmpty();\n    }\n\n    @Test\n    public void makeSearchOptionCreator_with_ATTRIBUTE_PROCESS_ID_filter_map_should_return_ATTRIBUTE_PROCESS_ID_in_filter_list() {\n        final Map<String, String> filters = new HashMap<>();\n        final String processID = \"2124654\";\n        filters.put(PageItem.ATTRIBUTE_PROCESS_ID, processID);\n        final SearchOptionsCreator searchOptionsCreator = pageDatastore.makeSearchOptionCreator(0, 10, \"\",\n                \"displayName ASC\", filters);\n        final List<SearchFilter> filtersResult = searchOptionsCreator.create().getFilters();\n        assertThat(filtersResult).extracting(\"field\", \"operation\", \"value\")\n                .contains(new Tuple(\"processDefinitionId\", SearchFilterOperation.EQUALS, processID));\n    }\n\n    @Test\n    public void makeSearchOptionCreator_with_FILTER_CONTENT_TYPE_form_filter_map_should_return_FILTER_CONTENT_TYPE_in_filter_list() {\n        final Map<String, String> filters = new HashMap<>();\n        final String form = \"form\";\n        filters.put(PageItem.FILTER_CONTENT_TYPE, form);\n        final SearchOptionsCreator searchOptionsCreator = pageDatastore.makeSearchOptionCreator(0, 10, \"\",\n                \"displayName ASC\", filters);\n        final List<SearchFilter> filtersResult = searchOptionsCreator.create().getFilters();\n        assertThat(filtersResult).extracting(\"field\", \"operation\", \"value\")\n                .contains(new Tuple(\"contentType\", SearchFilterOperation.EQUALS, form));\n    }\n\n    @Test\n    public void makeSearchOptionCreator_with_FILTER_CONTENT_TYPE_processPage_filter_map_should_return_FILTER_CONTENT_TYPE_form_or_page_in_filter_list() {\n        final Map<String, String> filters = new HashMap<>();\n        final String form = \"processPage\";\n        filters.put(PageItem.FILTER_CONTENT_TYPE, form);\n        final SearchOptionsCreator searchOptionsCreator = pageDatastore.makeSearchOptionCreator(0, 10, \"\",\n                \"displayName ASC\", filters);\n        final List<SearchFilter> filtersResult = searchOptionsCreator.create().getFilters();\n        assertThat(filtersResult).extracting(\"field\", \"operation\", \"value\").contains(\n                new Tuple(null, SearchFilterOperation.L_PARENTHESIS, null),\n                new Tuple(\"contentType\", SearchFilterOperation.EQUALS, \"form\"),\n                new Tuple(null, SearchFilterOperation.OR, null),\n                new Tuple(\"contentType\", SearchFilterOperation.EQUALS, \"page\"),\n                new Tuple(null, SearchFilterOperation.R_PARENTHESIS, null));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/page/PageItemConverterTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.page;\n\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.page.ContentType;\nimport org.bonitasoft.engine.page.Page;\nimport org.bonitasoft.web.rest.model.ModelFactory;\nimport org.bonitasoft.web.rest.model.portal.page.PageItem;\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.bonitasoft.web.rest.server.BonitaRestAPIFactory;\nimport org.bonitasoft.web.rest.server.framework.RestAPIFactory;\nimport org.bonitasoft.web.toolkit.client.ItemDefinitionFactory;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class PageItemConverterTest extends APITestWithMock {\n\n    @BeforeClass\n    public static void initEnvironnement() {\n        RestAPIFactory.setDefaultFactory(new BonitaRestAPIFactory());\n        ItemDefinitionFactory.setDefaultFactory(new ModelFactory());\n    }\n\n    @Test\n    public void should_convert_engineItem_Page_to_portalItem_PageItem() {\n\n        //Given\n        final PageItemConverter pageitemConverter = new PageItemConverter();\n        final Page engineItem = mock(Page.class);\n        when(engineItem.getId()).thenReturn(1l);\n        when(engineItem.getName()).thenReturn(\"page1\");\n        when(engineItem.getDisplayName()).thenReturn(\"Page 1\");\n        when(engineItem.isProvided()).thenReturn(false);\n        when(engineItem.getDescription()).thenReturn(\"This is a page description\");\n        when(engineItem.getInstalledBy()).thenReturn(1l);\n        when(engineItem.getInstallationDate()).thenReturn(new Date(1));\n        when(engineItem.getLastModificationDate()).thenReturn(new Date(1));\n        when(engineItem.getLastUpdatedBy()).thenReturn(1l);\n        when(engineItem.getContentName()).thenReturn(\"page1.zip\");\n        when(engineItem.getProcessDefinitionId()).thenReturn(2L);\n        when(engineItem.getContentType()).thenReturn(ContentType.FORM);\n        when(engineItem.isEditable()).thenReturn(true);\n        when(engineItem.isRemovable()).thenReturn(false);\n\n        //When\n        final PageItem pageItem = pageitemConverter.convert(engineItem);\n\n        //Assert\n        assertTrue(pageItem.getId().equals(engineItem.getId()));\n        assertTrue(pageItem.getUrlToken().equals(engineItem.getName()));\n        assertTrue(pageItem.getDisplayName().equals(engineItem.getDisplayName()));\n        assertTrue(pageItem.isProvided() == engineItem.isProvided());\n        assertTrue(pageItem.getDescription().equals(engineItem.getDescription()));\n        assertTrue(pageItem.getCreatedByUserId().equals(engineItem.getInstalledBy()));\n        assertTrue(pageItem.getCreationDate().equals(engineItem.getInstallationDate()));\n        assertTrue(pageItem.getLastUpdateDate().equals(engineItem.getLastModificationDate()));\n        assertTrue(pageItem.getUpdatedByUserId().equals(engineItem.getLastUpdatedBy()));\n        assertTrue(pageItem.getContentName().equals(engineItem.getContentName()));\n        assertTrue(pageItem.getProcessId().equals(engineItem.getProcessDefinitionId()));\n        assertTrue(pageItem.getContentType().equals(engineItem.getContentType()));\n        assertTrue(pageItem.isEditable() == engineItem.isEditable());\n        assertTrue(pageItem.isRemovable() == engineItem.isRemovable());\n\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/profile/EngineProfileBuilder.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.profile;\n\nimport org.bonitasoft.engine.profile.Profile;\nimport org.bonitasoft.engine.profile.impl.ProfileImpl;\n\n/**\n * @author Vincent Elcrin\n */\npublic class EngineProfileBuilder {\n\n    private String name;\n\n    private String description;\n\n    public static EngineProfileBuilder anEngineProfile() {\n        return new EngineProfileBuilder();\n    }\n\n    public EngineProfileBuilder withName(final String name) {\n        this.name = name;\n        return this;\n    }\n\n    public EngineProfileBuilder withDescription(final String description) {\n        this.description = description;\n        return this;\n    }\n\n    public Profile build() {\n        final ProfileImpl profile = new ProfileImpl(name);\n        profile.setDescription(description);\n        return profile;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/profile/GetProfileHelperTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.profile;\n\nimport static junit.framework.Assert.assertTrue;\nimport static org.bonitasoft.web.rest.model.builder.profile.ProfileItemBuilder.aProfileItem;\nimport static org.bonitasoft.web.rest.server.datastore.profile.EngineProfileBuilder.anEngineProfile;\nimport static org.mockito.Mockito.when;\nimport static org.mockito.MockitoAnnotations.initMocks;\n\nimport org.bonitasoft.engine.profile.Profile;\nimport org.bonitasoft.web.rest.model.portal.profile.ProfileItem;\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.bonitasoft.web.rest.server.engineclient.ProfileEngineClient;\nimport org.bonitasoft.web.toolkit.client.data.APIID;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\n\n/**\n * @author Vincent Elcrin\n */\npublic class GetProfileHelperTest extends APITestWithMock {\n\n    @Mock\n    ProfileEngineClient profileClient;\n\n    GetProfileHelper getProfileHelper;\n\n    @Before\n    public void setUp() {\n        initMocks(this);\n        getProfileHelper = new GetProfileHelper(profileClient);\n    }\n\n    @Test\n    public void testWeCanRetrieveAProfile() throws Exception {\n        final Profile aKnownProfile = anEngineProfile().withName(\"aName\").withDescription(\"aDescription\").build();\n        when(profileClient.getProfile(1L)).thenReturn(aKnownProfile);\n\n        final ProfileItem item = getProfileHelper.get(APIID.makeAPIID(1L));\n\n        assertTrue(areEquals(aProfileItem().fromEngineItem(aKnownProfile)\n                .withIcon(ProfileItemConverter.DEFAULT_PROFILE_ICONPATH).build(), item));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/profile/ProfileItemConverterTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.profile;\n\nimport static junit.framework.Assert.assertTrue;\nimport static org.bonitasoft.web.rest.model.builder.profile.ProfileItemBuilder.aProfileItem;\nimport static org.bonitasoft.web.rest.server.datastore.profile.EngineProfileBuilder.anEngineProfile;\n\nimport org.bonitasoft.engine.profile.Profile;\nimport org.bonitasoft.web.rest.model.portal.profile.ProfileItem;\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.junit.Test;\n\n/**\n * @author Vincent Elcrin\n */\npublic class ProfileItemConverterTest extends APITestWithMock {\n\n    @Test\n    public void testProfileItemConvertion() {\n        final Profile profile = anEngineProfile().withName(\"aName\").withDescription(\"aDescription\").build();\n\n        final ProfileItem item = new ProfileItemConverter().convert(profile);\n\n        assertTrue(areEquals(\n                aProfileItem().fromEngineItem(profile).withIcon(ProfileItemConverter.DEFAULT_PROFILE_ICONPATH).build(),\n                item));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/profile/ProfileSearchDescriptorConverterTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.profile;\n\nimport static org.junit.Assert.assertEquals;\n\nimport org.bonitasoft.engine.profile.ProfileSearchDescriptor;\nimport org.bonitasoft.web.rest.model.portal.profile.ProfileItem;\nimport org.junit.Test;\n\n/**\n * @author Vincent Elcrin\n */\npublic class ProfileSearchDescriptorConverterTest {\n\n    @Test\n    public void testWeCanConvertTheProfileId() {\n        String descriptor = new ProfileSearchDescriptorConverter().convert(ProfileItem.ATTRIBUTE_ID);\n\n        assertEquals(ProfileSearchDescriptor.ID, descriptor);\n    }\n\n    @Test\n    public void testWeCanConvertTheProfileName() {\n        String descriptor = new ProfileSearchDescriptorConverter().convert(ProfileItem.ATTRIBUTE_NAME);\n\n        assertEquals(ProfileSearchDescriptor.NAME, descriptor);\n    }\n\n    @Test(expected = RuntimeException.class)\n    public void testTryingToConvertNonExistingDescriptorThrowAnException() {\n        new ProfileSearchDescriptorConverter().convert(\"someUnsupportedAttribute\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/profile/SearchProfilesHelperTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.profile;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.web.rest.server.datastore.profile.EngineProfileBuilder.anEngineProfile;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\nimport static org.mockito.MockitoAnnotations.initMocks;\n\nimport java.util.HashMap;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.ProfileAPI;\nimport org.bonitasoft.engine.profile.Profile;\nimport org.bonitasoft.engine.profile.ProfileCriterion;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.impl.SearchResultImpl;\nimport org.bonitasoft.web.rest.model.portal.profile.ProfileItem;\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.bonitasoft.web.rest.server.datastore.utils.SearchUtils;\nimport org.bonitasoft.web.rest.server.engineclient.ProfileEngineClient;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\n\n/**\n * @author Vincent Elcrin\n */\npublic class SearchProfilesHelperTest extends APITestWithMock {\n\n    @Mock\n    ProfileAPI profileAPI;\n\n    SearchProfilesHelper searchProfilesHelper;\n\n    @Before\n    public void setUp() {\n        initMocks(this);\n        searchProfilesHelper = new SearchProfilesHelper(new ProfileEngineClient(profileAPI));\n    }\n\n    @Test\n    public void testWeCanSearchProfiles() throws Exception {\n        final SearchResultImpl<Profile> aKnownSearchResult = aKnownSearchResult();\n        final List<ProfileItem> expectedProfiles = new ProfileItemConverter().convert(aKnownSearchResult.getResult());\n        when(profileAPI.searchProfiles(any(SearchOptions.class))).thenReturn(aKnownSearchResult);\n\n        final ItemSearchResult<ProfileItem> searchResult = searchProfilesHelper.search(0, 10, null, null, null);\n\n        assertThat(SearchUtils.areEquals(expectedProfiles, searchResult.getResults())).isTrue();\n    }\n\n    @Test\n    public void testWeCanListUserProfiles() {\n        final SearchResultImpl<Profile> aKnownSearchResult = aKnownSearchResult();\n        final List<ProfileItem> expectedProfiles = new ProfileItemConverter().convert(aKnownSearchResult.getResult());\n        when(profileAPI.getProfilesForUser(2L, 0, Integer.MAX_VALUE, ProfileCriterion.ID_ASC))\n                .thenReturn(aKnownSearchResult.getResult());\n\n        final ItemSearchResult<ProfileItem> searchResult = searchProfilesHelper.search(0, 10, null, null,\n                filterOnUserId(2L));\n\n        verify(profileAPI).getProfilesForUser(2L, 0, Integer.MAX_VALUE, ProfileCriterion.ID_ASC);\n        assertThat(SearchUtils.areEquals(expectedProfiles, searchResult.getResults())).isTrue();\n    }\n\n    private HashMap<String, String> filterOnUserId(final long id) {\n        final HashMap<String, String> filters = new HashMap<>();\n        filters.put(ProfileItem.FILTER_USER_ID, String.valueOf(id));\n        return filters;\n    }\n\n    private SearchResultImpl<Profile> aKnownSearchResult() {\n        final Profile aKnownProfile = anEngineProfile().withName(\"aName\").withDescription(\"aDescription\").build();\n        final Profile anotherKnownProfile = anEngineProfile().withName(\"anotherName\")\n                .withDescription(\"anotherDescription\").build();\n        return SearchUtils.createEngineSearchResult(aKnownProfile, anotherKnownProfile);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/profile/member/AddProfileMemberHelperTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.profile.member;\n\nimport static junit.framework.Assert.assertTrue;\nimport static org.bonitasoft.web.rest.model.builder.profile.member.EngineProfileMemberBuilder.anEngineProfileMember;\nimport static org.bonitasoft.web.rest.model.builder.profile.member.ProfileMemberItemBuilder.aProfileMemberItem;\nimport static org.mockito.Mockito.when;\nimport static org.mockito.MockitoAnnotations.initMocks;\n\nimport org.bonitasoft.engine.profile.ProfileMember;\nimport org.bonitasoft.web.rest.model.portal.profile.ProfileMemberItem;\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.bonitasoft.web.rest.server.engineclient.ProfileMemberEngineClient;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\n\n/**\n * @author Vincent Elcrin\n */\npublic class AddProfileMemberHelperTest extends APITestWithMock {\n\n    @Mock\n    ProfileMemberEngineClient engineClient;\n\n    AddProfileMemberHelper addProfileHelper;\n\n    @Before\n    public void setUp() {\n        initMocks(this);\n        addProfileHelper = new AddProfileMemberHelper(engineClient);\n    }\n\n    @Test\n    public void testWeCanCreateAMembershipForAUSer() {\n        ProfileMember aKnownProfile = anEngineProfileMember().build();\n        when(engineClient.createProfileMember(1L, 2L, null, null)).thenReturn(aKnownProfile);\n\n        ProfileMemberItem item = aProfileMemberItem().withProfileId(1L).withUserId(2L).withGroupId(null)\n                .withRoleId(null).build();\n\n        ProfileMemberItem newItem = addProfileHelper.add(item);\n\n        assertTrue(areEquals(aProfileMemberItem().from(aKnownProfile).build(), newItem));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/profile/member/MemberTypeConverterTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.profile.member;\n\nimport static junit.framework.Assert.assertEquals;\n\nimport org.bonitasoft.web.rest.model.portal.profile.AbstractMemberItem;\nimport org.junit.Test;\n\n/**\n * @author Vincent Elcrin\n */\npublic class MemberTypeConverterTest {\n\n    @Test\n    public void testMemberTypeConversion() throws Exception {\n        MemberTypeConverter converter = new MemberTypeConverter();\n\n        MemberType value = converter.convert(AbstractMemberItem.VALUE_MEMBER_TYPE_ROLE);\n\n        assertEquals(MemberType.ROLE, value);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/profile/member/MemberTypeTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.profile.member;\n\nimport static junit.framework.Assert.assertEquals;\n\nimport org.bonitasoft.web.rest.model.portal.profile.AbstractMemberItem;\nimport org.junit.Test;\n\n/**\n * @author Vincent Elcrin\n */\npublic class MemberTypeTest {\n\n    @Test\n    public void testWeCanRetrieveMemberTypeFromAString() throws Exception {\n        MemberType type = MemberType.from(AbstractMemberItem.VALUE_MEMBER_TYPE_USER);\n\n        assertEquals(MemberType.USER, type);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void testNotExistingConstConversionThrowsException() throws Exception {\n        MemberType type = MemberType.from(\"notExistingConst\");\n\n        assertEquals(MemberType.USER, type);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/profile/member/ProfileMemberItemConverterTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.profile.member;\n\nimport static junit.framework.Assert.assertTrue;\nimport static org.bonitasoft.web.rest.model.builder.profile.member.EngineProfileMemberBuilder.anEngineProfileMember;\nimport static org.bonitasoft.web.rest.model.builder.profile.member.ProfileMemberItemBuilder.aProfileMemberItem;\n\nimport org.bonitasoft.engine.profile.ProfileMember;\nimport org.bonitasoft.web.rest.model.portal.profile.ProfileMemberItem;\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.junit.Test;\n\n/**\n * @author Vincent Elcrin\n */\npublic class ProfileMemberItemConverterTest extends APITestWithMock {\n\n    @Test\n    public void testProfileMemberItemConversion() {\n        ProfileMemberItemConverter converter = new ProfileMemberItemConverter();\n        ProfileMember profileMember = anEngineProfileMember().build();\n\n        ProfileMemberItem item = converter.convert(profileMember);\n\n        assertTrue(areEquals(aProfileMemberItem().from(profileMember).build(), item));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/profile/member/ProfileMemberSearchDescriptorConverterTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.profile.member;\n\nimport static junit.framework.Assert.assertTrue;\n\nimport org.bonitasoft.engine.profile.ProfileMemberSearchDescriptor;\nimport org.bonitasoft.web.rest.model.portal.profile.ProfileMemberItem;\nimport org.junit.Test;\n\n/**\n * @author Vincent Elcrin\n */\npublic class ProfileMemberSearchDescriptorConverterTest {\n\n    @Test\n    public void testWeCanConvertProfileMemberId() {\n        assertTrue(testConversion(ProfileMemberSearchDescriptor.ID,\n                ProfileMemberItem.ATTRIBUTE_ID));\n    }\n\n    @Test\n    public void testWeCanConvertProfileId() {\n        assertTrue(testConversion(ProfileMemberSearchDescriptor.PROFILE_ID,\n                ProfileMemberItem.ATTRIBUTE_PROFILE_ID));\n    }\n\n    @Test\n    public void testWeCanConvertUserId() {\n        assertTrue(testConversion(ProfileMemberSearchDescriptor.USER_ID,\n                ProfileMemberItem.ATTRIBUTE_USER_ID));\n    }\n\n    @Test\n    public void testWeCanConvertGroupId() {\n        assertTrue(testConversion(ProfileMemberSearchDescriptor.GROUP_ID,\n                ProfileMemberItem.ATTRIBUTE_GROUP_ID));\n    }\n\n    @Test\n    public void testWeCanConvertRoleId() {\n        assertTrue(testConversion(ProfileMemberSearchDescriptor.ROLE_ID,\n                ProfileMemberItem.ATTRIBUTE_ROLE_ID));\n    }\n\n    public boolean testConversion(String expected, String actual) {\n        ProfileMemberSearchDescriptorConverter converter = new ProfileMemberSearchDescriptorConverter();\n\n        String descriptor = converter.convert(actual);\n\n        return expected.equals(descriptor);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/profile/member/SearchProfileMembersHelperTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.profile.member;\n\nimport static junit.framework.Assert.assertTrue;\nimport static org.bonitasoft.web.rest.model.builder.profile.member.EngineProfileMemberBuilder.anEngineProfileMember;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.when;\nimport static org.mockito.MockitoAnnotations.initMocks;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\n\nimport org.bonitasoft.engine.profile.ProfileMember;\nimport org.bonitasoft.engine.search.SearchOptions;\nimport org.bonitasoft.engine.search.impl.SearchResultImpl;\nimport org.bonitasoft.web.rest.model.portal.profile.ProfileMemberItem;\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.bonitasoft.web.rest.server.datastore.utils.SearchUtils;\nimport org.bonitasoft.web.rest.server.engineclient.ProfileMemberEngineClient;\nimport org.bonitasoft.web.rest.server.framework.exception.APIFilterMandatoryException;\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\n\n/**\n * @author Vincent Elcrin\n */\npublic class SearchProfileMembersHelperTest extends APITestWithMock {\n\n    @Mock\n    ProfileMemberEngineClient engineClient;\n\n    SearchProfileMembersHelper searchProfilesHelper;\n\n    @Before\n    public void setUp() {\n        initMocks(this);\n        searchProfilesHelper = new SearchProfileMembersHelper(engineClient);\n    }\n\n    @Test\n    public void testWeCanSearchProfileMembers() throws Exception {\n        SearchResultImpl<ProfileMember> aKnownSearchResult = aKnownSearchResult();\n        List<ProfileMemberItem> expectedProfileMemberItems = new ProfileMemberItemConverter()\n                .convert(aKnownSearchResult().getResult());\n        when(engineClient.searchProfileMembers(eq(MemberType.ROLE.getType()), any(SearchOptions.class)))\n                .thenReturn(aKnownSearchResult);\n        HashMap<String, String> filters = filterOnProfileIdAndMemberType(5L, MemberType.ROLE);\n\n        ItemSearchResult<ProfileMemberItem> searchResult = searchProfilesHelper.search(0, 10, null, null, filters);\n\n        assertTrue(SearchUtils.areEquals(expectedProfileMemberItems, searchResult.getResults()));\n    }\n\n    @Test(expected = APIFilterMandatoryException.class)\n    public void testSearchWithoutMandatoryFiltersThrowError() {\n\n        searchProfilesHelper.search(0, 10, null, null, Collections.<String, String> emptyMap());\n    }\n\n    private SearchResultImpl<ProfileMember> aKnownSearchResult() {\n        return SearchUtils.createEngineSearchResult(aKnownProfile(), anotherKnownProfile());\n\n    }\n\n    private HashMap<String, String> filterOnProfileIdAndMemberType(long id, MemberType type) {\n        HashMap<String, String> filters = new HashMap<>();\n        filters.put(ProfileMemberItem.ATTRIBUTE_PROFILE_ID, String.valueOf(id));\n        filters.put(ProfileMemberItem.FILTER_MEMBER_TYPE, type.getType());\n        return filters;\n    }\n\n    private ProfileMember aKnownProfile() {\n        return anEngineProfileMember().build();\n    }\n\n    private ProfileMember anotherKnownProfile() {\n        return anEngineProfileMember()\n                .withId(2L)\n                .withProfileId(3L)\n                .withUserId(4L)\n                .withGroupId(5L)\n                .withRoleId(6l)\n                .build();\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/utils/NotSerializableObject.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.utils;\n\npublic class NotSerializableObject {\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/utils/SearchUtils.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.utils;\n\nimport java.io.Serializable;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.search.impl.SearchResultImpl;\nimport org.bonitasoft.web.toolkit.client.data.item.Item;\n\n/**\n * @author Vincent Elcrin\n */\npublic class SearchUtils {\n\n    public static <S extends Serializable> SearchResultImpl<S> createEngineSearchResult(S... items) {\n        return new SearchResultImpl<>(items.length, Arrays.<S> asList(items));\n    }\n\n    private static boolean areEquals(Item item1, Item item2) {\n        return item1.getAttributes().equals(item2.getAttributes());\n    }\n\n    public static <I extends Item> boolean areEquals(List<I> list1, List<I> list2) {\n        int i = 0;\n        for (I item : list1) {\n            if (!areEquals(\n                    item, list2.get(i++))) {\n                return false;\n            }\n        }\n        return true;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/utils/SortTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.utils;\n\nimport org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor;\nimport org.bonitasoft.engine.search.Order;\nimport org.bonitasoft.web.rest.model.bpm.flownode.ActivityItem;\nimport org.bonitasoft.web.rest.server.datastore.converter.ActivityAttributeConverter;\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author Vincent Elcrin\n */\npublic class SortTest {\n\n    @Test\n    public void testAscSortCreation() {\n        String order = \"attribute \" + Order.ASC;\n\n        Sort sort = new Sort(order);\n\n        Assert.assertEquals(sort.getField(), \"attribute\");\n        Assert.assertEquals(sort.getOrder(), Order.ASC);\n    }\n\n    @Test\n    public void testDescSortCreation() {\n        String order = \"attribute \" + Order.DESC;\n\n        Sort sort = new Sort(order);\n\n        Assert.assertEquals(sort.getField(), \"attribute\");\n        Assert.assertEquals(sort.getOrder(), Order.DESC);\n    }\n\n    @Test\n    public void testDefaultSortOrder() {\n        String order = \"attribute\";\n\n        Sort sort = new Sort(order);\n\n        Assert.assertEquals(sort.getField(), \"attribute\");\n        Assert.assertEquals(sort.getOrder(), Sort.DEFAULT_ORDER);\n    }\n\n    @Test\n    public void testToStringForParamWithConvertionValue() {\n        Sort sort = new Sort(ActivityItem.ATTRIBUTE_ROOT_CASE_ID + \" \" + Order.DESC, new ActivityAttributeConverter());\n\n        Assert.assertEquals(sort.toString(), ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID + \" \" + Order.DESC);\n    }\n\n    @Test\n    public void testToStringForParamWithoutConvertionValue() {\n        Sort sort = new Sort(\"TestParam \" + Order.DESC, new ActivityAttributeConverter());\n\n        Assert.assertEquals(sort.toString(), \"TestParam \" + Order.DESC);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/utils/SortsTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.utils;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.search.Order;\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author Vincent Elcrin\n */\npublic class SortsTest {\n\n    @Test\n    public void testNullSortListing() throws Exception {\n        Sorts sorts = new Sorts(null);\n\n        List<Sort> sortList = sorts.asList();\n\n        Assert.assertTrue(sortList.isEmpty());\n    }\n\n    @Test\n    public void testEmptySortListing() throws Exception {\n        Sorts sorts = new Sorts(\"\");\n\n        List<Sort> sortList = sorts.asList();\n\n        Assert.assertTrue(sortList.isEmpty());\n    }\n\n    @Test\n    public void testSingleSortListing() throws Exception {\n        Sorts sorts = new Sorts(\"attribute \" + Order.ASC);\n\n        List<Sort> sortList = sorts.asList();\n\n        Assert.assertEquals(new Sort(\"attribute \" + Order.ASC), sortList.get(0));\n    }\n\n    @Test\n    public void testMultipleSortListing() throws Exception {\n        String attribute1Order = \"attribute1 \" + Order.ASC;\n        String attribute2Order = \"attribute2 \" + Order.DESC;\n        Sorts sorts = new Sorts(attribute1Order + \",\" + attribute2Order);\n\n        List<Sort> sortList = sorts.asList();\n\n        Assert.assertEquals(new Sort(attribute1Order), sortList.get(0));\n        Assert.assertEquals(new Sort(attribute2Order), sortList.get(1));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/utils/VariableMapperTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.utils;\n\nimport static org.hamcrest.MatcherAssert.assertThat;\nimport static org.hamcrest.Matchers.is;\n\nimport java.io.Serializable;\n\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.bonitasoft.web.rest.server.framework.json.JacksonDeserializer;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class VariableMapperTest extends APITestWithMock {\n\n    private JacksonDeserializer jacksonDeserializer;\n\n    @Before\n    public void setUp() {\n        jacksonDeserializer = new JacksonDeserializer();\n    }\n\n    @Test(expected = APIException.class)\n    public void getSerializableValue_throw_exception_if_class_is_not_in_classpath() {\n        new VariableMapper(new Variable(), jacksonDeserializer).getSerializableValue(\"a.class.not.in.classpath\");\n    }\n\n    @Test(expected = APIException.class)\n    public void getSerializableValue_throw_exception_if_classname_name_an_unserializable_object() {\n        Variable variable = new Variable();\n        variable.set(\"value\", new NotSerializableObject());\n\n        new VariableMapper(variable, jacksonDeserializer).getSerializableValue(NotSerializableObject.class.getName());\n    }\n\n    @Test(expected = APIException.class)\n    public void getSerializableValue_cannot_convert_values_that_ont_fits_to_given_className() {\n        Variable variable = new Variable();\n        variable.set(\"value\", \"coucou\");\n\n        Serializable value = new VariableMapper(variable, jacksonDeserializer)\n                .getSerializableValue(Long.class.getName());\n\n        assertThat(value, is(1L));\n    }\n\n    @Test\n    public void getSerializableValue_return_variable_value_converted_in_given_class_name_object() {\n        Variable variable = new Variable();\n        variable.set(\"value\", 1);\n\n        Serializable value = new VariableMapper(variable, jacksonDeserializer)\n                .getSerializableValue(Long.class.getName());\n\n        assertThat(value, is(1L));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/utils/VariablesMapperTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.datastore.utils;\n\nimport static org.hamcrest.MatcherAssert.assertThat;\nimport static org.hamcrest.Matchers.hasItems;\n\nimport org.bonitasoft.web.rest.server.framework.json.JacksonDeserializer;\nimport org.junit.Test;\n\npublic class VariablesMapperTest {\n\n    private static final String JSON_VARIABLES = \"[\" +\n            \"{\\\"name\\\": \\\"variable1\\\", \\\"value\\\": \\\"newValue\\\"},\" +\n            \"{\\\"name\\\": \\\"variable2\\\", \\\"value\\\": 9},\" +\n            \"{\\\"name\\\": \\\"variable3\\\", \\\"value\\\": 349246800000}\" +\n            \"]\";\n\n    @Test\n    public void variablesMapper_convert_json_variable_list_to_variableMappers() throws Exception {\n\n        VariablesMapper variablesMapper = VariablesMapper.fromJson(JSON_VARIABLES);\n\n        assertThat(variablesMapper.getVariables(), hasItems(\n                aVariableMapper(\"variable1\", \"newValue\"),\n                aVariableMapper(\"variable2\", 9),\n                aVariableMapper(\"variable3\", 349246800000L)));\n    }\n\n    private VariableMapper aVariableMapper(String name, Object value) {\n        Variable variable = new Variable();\n        variable.set(\"name\", name);\n        variable.set(\"value\", value);\n        return new VariableMapper(variable, new JacksonDeserializer());\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/engineclient/ActivityEngineClientTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.engineclient;\n\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.*;\nimport static org.mockito.MockitoAnnotations.*;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.data.DataNotFoundException;\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\n\npublic class ActivityEngineClientTest extends APITestWithMock {\n\n    @Mock\n    private ProcessAPI processAPI;\n\n    private ActivityEngineClient activityEngineClient;\n\n    @Before\n    public void initializeClient() {\n        initMocks(this);\n        activityEngineClient = new ActivityEngineClient(processAPI);\n    }\n\n    @Test(expected = APIException.class)\n    public void getDataInstance_throw_exception_if_data_is_not_found() throws Exception {\n        when(processAPI.getActivityDataInstance(anyString(), anyLong()))\n                .thenThrow(new DataNotFoundException(new NullPointerException()));\n\n        activityEngineClient.getDataInstance(\"aName\", 1L);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/engineclient/CaseEngineClientTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.engineclient;\n\nimport static org.mockito.ArgumentMatchers.anyLong;\nimport static org.mockito.ArgumentMatchers.anyMap;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\nimport static org.mockito.MockitoAnnotations.initMocks;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.process.ProcessActivationException;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ProcessExecutionException;\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\n\npublic class CaseEngineClientTest extends APITestWithMock {\n\n    @Mock\n    private ProcessAPI processAPI;\n\n    private CaseEngineClient caseEngineClient;\n\n    @Before\n    public void setUp() {\n        initMocks(this);\n        caseEngineClient = new CaseEngineClient(processAPI);\n    }\n\n    private Map<String, Serializable> someVariables() {\n        final Map<String, Serializable> map = new HashMap<>();\n        map.put(\"variable\", 1L);\n        return map;\n    }\n\n    @Test\n    public void a_process_can_be_started_without_variables() throws Exception {\n        final long expectedProcessId = 1L;\n        final long userId = 1L;\n\n        caseEngineClient.start(userId, expectedProcessId);\n\n        verify(processAPI).startProcess(userId, expectedProcessId);\n    }\n\n    @Test\n    public void a_process_can_be_started_with_variables() throws Exception {\n        final long expectedProcessId = 1L;\n        final long userId = 1L;\n        final Map<String, Serializable> variables = someVariables();\n\n        caseEngineClient.start(userId, expectedProcessId, variables);\n\n        verify(processAPI).startProcess(userId, expectedProcessId, variables);\n    }\n\n    @Test(expected = APIException.class)\n    public void cant_create_case_if_process_definition_is_not_found() throws Exception {\n        when(processAPI.startProcess(anyLong())).thenThrow(new ProcessDefinitionNotFoundException(\"\"));\n\n        caseEngineClient.start(-1L, 1L);\n    }\n\n    @Test(expected = APIException.class)\n    public void cant_create_case_if_process_is_not_activated() throws Exception {\n        when(processAPI.startProcess(anyLong())).thenThrow(new ProcessActivationException(\"\"));\n\n        caseEngineClient.start(-1L, 1L);\n    }\n\n    @Test(expected = APIException.class)\n    public void we_get_an_exception_if_process_fail_to_start() throws Exception {\n        when(processAPI.startProcess(anyLong())).thenThrow(new ProcessExecutionException(\"\"));\n\n        caseEngineClient.start(-1L, 1L);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Test(expected = APIException.class)\n    public void cant_create_case_with_variables_if_process_definition_is_not_found() throws Exception {\n        when(processAPI.startProcess(anyLong(), anyMap())).thenThrow(new ProcessDefinitionNotFoundException(\"\"));\n\n        caseEngineClient.start(-1L, 1L, someVariables());\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Test(expected = APIException.class)\n    public void cant_create_case_with_variables_if_process_is_not_activated() throws Exception {\n        when(processAPI.startProcess(anyLong(), anyMap())).thenThrow(new ProcessActivationException(\"\"));\n\n        caseEngineClient.start(-1L, 1L, someVariables());\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Test(expected = APIException.class)\n    public void we_get_an_exception_if_process_fail_to_start_with_variables() throws Exception {\n        when(processAPI.startProcess(anyLong(), anyMap())).thenThrow(new ProcessExecutionException(\"\"));\n\n        caseEngineClient.start(-1L, 1L, someVariables());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/engineclient/CustomUserInfoEngineClientTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.engineclient;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.willThrow;\nimport static org.mockito.Mockito.verify;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.IdentityAPI;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.identity.CustomUserInfo;\nimport org.bonitasoft.engine.identity.CustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.CustomUserInfoDefinitionCreator;\nimport org.bonitasoft.engine.identity.CustomUserInfoValue;\nimport org.bonitasoft.engine.identity.impl.CustomUserInfoValueImpl;\nimport org.bonitasoft.web.rest.server.api.organization.EngineCustomUserInfoDefinition;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Vincent Elcrin\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class CustomUserInfoEngineClientTest {\n\n    @Mock\n    private IdentityAPI engine;\n\n    @InjectMocks\n    private CustomUserInfoEngineClient client;\n\n    @Test\n    public void should_create_a_given_definition() throws Exception {\n        given(engine.createCustomUserInfoDefinition(any(CustomUserInfoDefinitionCreator.class)))\n                .willReturn(new EngineCustomUserInfoDefinition(1L, \"foo\", \"bar\"));\n\n        CustomUserInfoDefinition definition = client.createDefinition(new CustomUserInfoDefinitionCreator(\"foo\"));\n\n        assertThat(definition.getName()).isEqualTo(\"foo\");\n    }\n\n    @Test(expected = APIException.class)\n    public void should_fail_to_create_a_given_definition_when_engine_throw_an_exception() throws Exception {\n        given(engine.createCustomUserInfoDefinition(any(CustomUserInfoDefinitionCreator.class)))\n                .willThrow(new CreationException(\"failure\"));\n\n        client.createDefinition(new CustomUserInfoDefinitionCreator(\"foo\"));\n    }\n\n    @Test\n    public void should_delete_a_given_definition() throws Exception {\n\n        client.deleteDefinition(1L);\n\n        verify(engine).deleteCustomUserInfoDefinition(1L);\n    }\n\n    @Test(expected = APIException.class)\n    public void should_fail_to_delete_a_given_definition_when_engine_throw_an_exception() throws Exception {\n        willThrow(new DeletionException(\"failure\")).given(engine)\n                .deleteCustomUserInfoDefinition(1L);\n\n        client.deleteDefinition(1L);\n    }\n\n    @Test\n    public void should_list_definitions_for_a_given_range() throws Exception {\n        given(engine.getCustomUserInfoDefinitions(0, 2)).willReturn(Arrays.<CustomUserInfoDefinition> asList(\n                new EngineCustomUserInfoDefinition(1L),\n                new EngineCustomUserInfoDefinition(2L)));\n\n        List<CustomUserInfoDefinition> definitions = client.listDefinitions(0, 2);\n\n        assertThat(definitions.get(0).getId()).isEqualTo(1L);\n        assertThat(definitions.get(1).getId()).isEqualTo(2L);\n    }\n\n    @Test\n    public void should_count_definitions() throws Exception {\n        given(engine.getNumberOfCustomInfoDefinitions()).willReturn(5L);\n\n        assertThat(client.countDefinitions()).isEqualTo(5);\n    }\n\n    @Test\n    public void should_list_custom_information_for_a_given_user() {\n        given(engine.getCustomUserInfo(1L, 0, 2)).willReturn(Arrays.asList(\n                new CustomUserInfo(1L, new EngineCustomUserInfoDefinition(1L), new CustomUserInfoValueImpl()),\n                new CustomUserInfo(1L, new EngineCustomUserInfoDefinition(2L), new CustomUserInfoValueImpl())));\n\n        List<CustomUserInfo> information = client.listCustomInformation(1L, 0, 2);\n\n        assertThat(information.get(0).getDefinition().getId()).isEqualTo(1L);\n        assertThat(information.get(1).getDefinition().getId()).isEqualTo(2L);\n    }\n\n    @Test\n    public void should_the_value_of_a_given_custom_user_info() throws UpdateException {\n        CustomUserInfoValueImpl value = new CustomUserInfoValueImpl();\n        value.setValue(\"foo\");\n        given(engine.setCustomUserInfoValue(1L, 2L, \"foo\")).willReturn(value);\n\n        CustomUserInfoValue foo = client.setCustomUserInfoValue(1L, 2L, \"foo\");\n\n        assertThat(foo.getValue()).isEqualTo(\"foo\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/engineclient/GroupEngineClientTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.engineclient;\n\nimport static java.util.Arrays.asList;\nimport static org.hamcrest.Matchers.is;\nimport static org.junit.Assert.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.doThrow;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\nimport static org.mockito.MockitoAnnotations.initMocks;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.GroupAPI;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.identity.Group;\nimport org.bonitasoft.engine.identity.GroupCreator;\nimport org.bonitasoft.engine.identity.GroupNotFoundException;\nimport org.bonitasoft.engine.identity.GroupUpdater;\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\n\n/**\n * @author Colin PUY\n */\npublic class GroupEngineClientTest extends APITestWithMock {\n\n    @Mock\n    private GroupAPI groupAPI;\n\n    private GroupEngineClient groupEngineClient;\n\n    @Before\n    public void init() {\n        initMocks(this);\n        groupEngineClient = new GroupEngineClient(groupAPI);\n    }\n\n    @Test\n    public void get_get_a_group_in_engine_repository() throws Exception {\n\n        groupEngineClient.get(1L);\n\n        verify(groupAPI).getGroup(1L);\n    }\n\n    @Test(expected = APIException.class)\n    public void get_throw_APIException_if_group_is_not_found_in_engine_repository() throws Exception {\n        when(groupAPI.getGroup(1L)).thenThrow(new GroupNotFoundException(new Exception()));\n\n        groupEngineClient.get(1L);\n    }\n\n    @Test\n    public void delete_delete_groups_in_engine_repository() throws Exception {\n        List<Long> groupIds = asList(1L, 2L);\n\n        groupEngineClient.delete(groupIds);\n\n        verify(groupAPI).deleteGroups(groupIds);\n    }\n\n    @Test(expected = APIException.class)\n    public void delete_throw_APIException_if_an_error_occurs_when_deleting_groups_in_engine_repository()\n            throws Exception {\n        List<Long> groupIds = asList(1L, 2L);\n        doThrow(new DeletionException(\"error\")).when(groupAPI).deleteGroups(groupIds);\n\n        groupEngineClient.delete(groupIds);\n    }\n\n    @Test\n    public void update_update_a_group_in_engine_repository() throws Exception {\n        GroupUpdater groupUpdater = new GroupUpdater();\n\n        groupEngineClient.update(1L, groupUpdater);\n\n        verify(groupAPI).updateGroup(1L, groupUpdater);\n    }\n\n    @Test(expected = APIException.class)\n    public void update_throw_APIException_if_group_not_exists_in_engine_repository() throws Exception {\n        when(groupAPI.updateGroup(eq(1L), any(GroupUpdater.class)))\n                .thenThrow(new GroupNotFoundException(new Exception()));\n\n        groupEngineClient.update(1L, new GroupUpdater());\n    }\n\n    @Test(expected = APIException.class)\n    public void update_throw_APIException_if_an_exception_occurs_when_updating_in_engine_repository() throws Exception {\n        when(groupAPI.updateGroup(eq(1L), any(GroupUpdater.class))).thenThrow(new UpdateException(\"\"));\n\n        groupEngineClient.update(1L, new GroupUpdater());\n    }\n\n    @Test(expected = APIException.class)\n    public void update_throw_APIException_if_an_exception_occurs_when_updating_with_name_alreayExist()\n            throws Exception {\n        when(groupAPI.updateGroup(eq(1L), any(GroupUpdater.class))).thenThrow(new AlreadyExistsException(\"\"));\n\n        groupEngineClient.update(1L, new GroupUpdater());\n    }\n\n    @Test(expected = APIException.class)\n    public void getPath_throw_APIexception_if_groupId_is_not_a_number() throws Exception {\n        groupEngineClient.getPath(\"notANumber\");\n    }\n\n    @Test\n    public void getPath_return_the_group_path_for_the_specified_group_id() throws Exception {\n        Group group = mock(Group.class);\n        when(group.getPath()).thenReturn(\"/expected/group/path\");\n        when(groupAPI.getGroup(1L)).thenReturn(group);\n\n        String groupPath = groupEngineClient.getPath(\"1\");\n\n        assertThat(groupPath, is(\"/expected/group/path\"));\n    }\n\n    @Test\n    public void create_create_a_group_in_engine_repository() throws Exception {\n        GroupCreator creator = new GroupCreator(\"aName\");\n\n        groupEngineClient.create(creator);\n\n        verify(groupAPI).createGroup(creator);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/engineclient/ProcessEngineClientTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.engineclient;\n\nimport static org.mockito.ArgumentMatchers.anyInt;\nimport static org.mockito.ArgumentMatchers.anyLong;\nimport static org.mockito.Mockito.*;\nimport static org.mockito.MockitoAnnotations.initMocks;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.ProcessAPI;\nimport org.bonitasoft.engine.bpm.bar.BusinessArchive;\nimport org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException;\nimport org.bonitasoft.engine.bpm.process.ProcessDeployException;\nimport org.bonitasoft.engine.bpm.process.V6FormDeployException;\nimport org.bonitasoft.engine.bpm.process.impl.internal.DesignProcessDefinitionImpl;\nimport org.bonitasoft.engine.exception.AlreadyExistsException;\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.mockito.Mock;\n\n/**\n * @author Colin PUY\n */\npublic class ProcessEngineClientTest extends APITestWithMock {\n\n    private static Integer BUNCH_SIZE = 10;\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n    @Mock\n    private ProcessAPI processAPI;\n    private ProcessEngineClient processEngineClient;\n\n    @Before\n    public void setUp() {\n        initMocks(this);\n        processEngineClient = new ProcessEngineClient(processAPI);\n    }\n\n    @Test\n    public void deleteArchivedProcessInstancesByBunch_delete_archived_processes_instances_by_bunch() throws Exception {\n        when(processAPI.deleteArchivedProcessInstances(1L, 0, BUNCH_SIZE))\n                .thenReturn(BUNCH_SIZE.longValue(), BUNCH_SIZE.longValue(), 0L);\n\n        final List<Long> processList = new ArrayList<>();\n        processList.add(1L);\n        processEngineClient.deleteArchivedProcessInstancesByBunch(1L, 10, processList);\n\n        verify(processAPI, times(3)).deleteArchivedProcessInstances(1L, 0, 10);\n    }\n\n    @Test\n    public void deleteProcessInstancesByBunch_delete_archived_processes_instances_by_bunch() throws Exception {\n        when(processAPI.deleteProcessInstances(1L, 0, BUNCH_SIZE))\n                .thenReturn(BUNCH_SIZE.longValue(), BUNCH_SIZE.longValue(), 0L);\n\n        final List<Long> processList = new ArrayList<>();\n        processList.add(1L);\n        processEngineClient.deleteProcessInstancesByBunch(1L, 10, processList);\n\n        verify(processAPI, times(3)).deleteProcessInstances(1L, 0, 10);\n    }\n\n    @Test(expected = Exception.class)\n    public void getProcessDataDefinitions_throw_exception_if_process_definition_is_not_found() throws Exception {\n        when(processAPI.getProcessDataDefinitions(anyLong(), anyInt(), anyInt()))\n                .thenThrow(new ProcessDefinitionNotFoundException(\"\"));\n\n        processEngineClient.getProcessDataDefinitions(1L);\n    }\n\n    @Test\n    public void getProcessDataDefinitions_get_all_process_data_definition() throws Exception {\n        final long expectedProcessId = 1L;\n\n        processEngineClient.getProcessDataDefinitions(expectedProcessId);\n\n        verify(processAPI).getProcessDataDefinitions(expectedProcessId, 0, Integer.MAX_VALUE);\n    }\n\n    @Test\n    public void deploying_a_process_with_v6_forms_fails_with_the_right_message()\n            throws AlreadyExistsException, ProcessDeployException {\n\n        DesignProcessDefinitionImpl designProcessDefinition = new DesignProcessDefinitionImpl(\"processName\", \"1.0\");\n        BusinessArchive businessArchive = new BusinessArchive();\n        businessArchive.setProcessDefinition(designProcessDefinition);\n\n        when(processAPI.deploy((BusinessArchive) any()))\n                .thenThrow(new V6FormDeployException(new Exception(\"a cause\")));\n\n        expectedException.expect(APIException.class);\n        processEngineClient.deploy(businessArchive);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/engineclient/TenantManagementEngineClientTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.engineclient;\n\nimport static org.mockito.Mockito.doThrow;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport org.bonitasoft.engine.api.TenantAdministrationAPI;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class TenantManagementEngineClientTest {\n\n    @Mock\n    private TenantAdministrationAPI tenantAdministrationAPI;\n\n    @InjectMocks\n    private TenantManagementEngineClient tenantManagementEngineClient;\n\n    private void pauseTenant() {\n        when(tenantAdministrationAPI.isPaused()).thenReturn(true);\n    }\n\n    private void resumeTenant() {\n        when(tenantAdministrationAPI.isPaused()).thenReturn(false);\n    }\n\n    @Before\n    public void startTenant() {\n        resumeTenant();\n    }\n\n    @Test\n    public void pauseTenant_pause_the_tenant() throws Exception {\n\n        tenantManagementEngineClient.pauseTenant();\n\n        verify(tenantAdministrationAPI).pause();\n    }\n\n    @Test\n    public void pauseTenant_dont_pause_tenant_if_it_is_already_paused() throws Exception {\n        pauseTenant();\n\n        tenantManagementEngineClient.pauseTenant();\n\n        verify(tenantAdministrationAPI, never()).pause();\n    }\n\n    @Test(expected = APIException.class)\n    public void pauseTenant_throw_APIException_if_error_occurs_when_pausing_tenant() throws Exception {\n        doThrow(new UpdateException(new NullPointerException())).when(tenantAdministrationAPI).pause();\n\n        tenantManagementEngineClient.pauseTenant();\n    }\n\n    @Test\n    public void resumeTenant_resume_the_tenant() throws Exception {\n        pauseTenant();\n\n        tenantManagementEngineClient.resumeTenant();\n\n        verify(tenantAdministrationAPI).resume();\n    }\n\n    @Test\n    public void resumeTenant_dont_resume_tenant_if_it_not_paused() throws Exception {\n\n        tenantManagementEngineClient.resumeTenant();\n\n        verify(tenantAdministrationAPI, never()).resume();\n    }\n\n    @Test(expected = APIException.class)\n    public void resumeTenant_throw_APIException_if_error_occurs_when_resuming_tenant() throws Exception {\n        pauseTenant();\n        doThrow(new UpdateException(new NullPointerException())).when(tenantAdministrationAPI).resume();\n\n        tenantManagementEngineClient.resumeTenant();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/engineclient/UserEngineClientTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.engineclient;\n\nimport static java.util.Arrays.asList;\nimport static org.mockito.Mockito.doThrow;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\nimport static org.mockito.MockitoAnnotations.initMocks;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.api.IdentityAPI;\nimport org.bonitasoft.engine.exception.CreationException;\nimport org.bonitasoft.engine.exception.DeletionException;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.identity.UserCreator;\nimport org.bonitasoft.engine.identity.UserNotFoundException;\nimport org.bonitasoft.engine.identity.UserUpdater;\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\n\n/**\n * @author Colin PUY\n */\npublic class UserEngineClientTest extends APITestWithMock {\n\n    @Mock\n    private IdentityAPI identityAPI;\n\n    private UserEngineClient userEngineClient;\n\n    @Before\n    public void init() {\n        initMocks(this);\n        userEngineClient = new UserEngineClient(identityAPI);\n    }\n\n    @Test\n    public void update_update_a_user_in_engine_repository() throws Exception {\n        UserUpdater userUpdater = new UserUpdater();\n\n        userEngineClient.update(1L, userUpdater);\n\n        verify(identityAPI).updateUser(1L, userUpdater);\n    }\n\n    @Test(expected = APIException.class)\n    public void update_throw_APIException_if_user_is_not_found_in_engine_repository() throws Exception {\n        UserUpdater userUpdater = new UserUpdater();\n        when(identityAPI.updateUser(1L, userUpdater)).thenThrow(new UserNotFoundException(\"aMessage\"));\n\n        userEngineClient.update(1L, userUpdater);\n    }\n\n    @Test(expected = APIException.class)\n    public void update_throw_APIException_if_exception_occur_when_updating_user_in_engine_repository()\n            throws Exception {\n        UserUpdater userUpdater = new UserUpdater();\n        when(identityAPI.updateUser(1L, userUpdater)).thenThrow(new UpdateException(\"aMessage\"));\n\n        userEngineClient.update(1L, userUpdater);\n    }\n\n    @Test\n    public void create_create_a_user_in_engine_repository() throws Exception {\n        UserCreator userCreator = new UserCreator(\"aName\", \"aPassword\");\n\n        userEngineClient.create(userCreator);\n\n        verify(identityAPI).createUser(userCreator);\n    }\n\n    @Test(expected = APIException.class)\n    public void create_throw_APIException_if_exception_occur_when_creating_user_in_engine_repository()\n            throws Exception {\n        UserCreator userCreator = new UserCreator(\"aName\", \"aPassword\");\n        when(identityAPI.createUser(userCreator)).thenThrow(new CreationException(\"aMessage\"));\n\n        userEngineClient.create(userCreator);\n    }\n\n    @Test\n    public void get_fetch_a_user_from_engine_repository() throws Exception {\n\n        userEngineClient.get(1L);\n\n        verify(identityAPI).getUser(1L);\n    }\n\n    @Test(expected = APIException.class)\n    public void get_throw_APIException_if_user_is_not_found_in_engine_repository() throws Exception {\n        when(identityAPI.getUser(1L)).thenThrow(new UserNotFoundException(\"aMessage\"));\n\n        userEngineClient.get(1L);\n    }\n\n    @Test\n    public void delete_delete_users_in_engine_repository() throws Exception {\n        List<Long> idsToBeDeleted = asList(1L, 2L);\n\n        userEngineClient.delete(idsToBeDeleted);\n\n        verify(identityAPI).deleteUsers(idsToBeDeleted);\n    }\n\n    @Test(expected = APIException.class)\n    public void delete_throw_APIException_if_exception_occur_when_deleting_users_in_engine_repository()\n            throws Exception {\n        List<Long> idsToBeDeleted = asList(1L, 2L);\n        doThrow(new DeletionException(\"aMessage\")).when(identityAPI).deleteUsers(idsToBeDeleted);\n\n        userEngineClient.delete(idsToBeDeleted);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/framework/APIServletCallTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.Mockito.*;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\n\nimport javax.servlet.http.HttpServletMapping;\nimport javax.servlet.http.HttpServletRequest;\n\nimport org.bonitasoft.web.rest.server.framework.search.ItemSearchResult;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIIncorrectIdException;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\nimport org.springframework.http.HttpHeaders;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class APIServletCallTest {\n\n    @Mock\n    private HttpServletRequest request;\n    @Mock\n    private HttpServletMapping httpServletMapping;\n    @Mock\n    private API api;\n\n    @Spy\n    private final APIServletCall apiServletCall = new APIServletCall();\n\n    @Rule\n    public ExpectedException thrown = ExpectedException.none();\n\n    @Before\n    public void before() {\n        doReturn(httpServletMapping).when(request).getHttpServletMapping();\n        apiServletCall.api = api;\n    }\n\n    @Test\n    public void should_parsePath_request_info_with_id() {\n        doReturn(\"/bpm/case/15\").when(request).getPathInfo();\n\n        apiServletCall.parsePath(request);\n\n        assertThat(apiServletCall.getId().getPart(0)).isEqualTo(\"15\");\n        assertThat(apiServletCall.getResourceName()).isEqualTo(\"case\");\n        assertThat(apiServletCall.getApiName()).isEqualTo(\"bpm\");\n\n    }\n\n    @Test\n    public void should_parsePath_request_info_with_negative_id() {\n        doReturn(\"/identity/user/-1\").when(request).getPathInfo();\n        thrown.expect(APIIncorrectIdException.class);\n        thrown.expectMessage(\"Id must be non-zero positive for identity on resource user\");\n\n        apiServletCall.parsePath(request);\n    }\n\n    @Test\n    public void doGet_On_Search_Should_Set_Content_Range_Headers_Correctly() throws Exception {\n        String parameterSearchValue = \"\";\n        String parameterPageValue = \"10\";\n        String parameterLimitValue = \"0\";\n        String parameterOrderValue = \"id ASC\";\n        List<String> parameterDeployValue = new ArrayList<>();\n        List<String> parameterCounterValue = new ArrayList<>();\n\n        doReturn(parameterDeployValue).when(apiServletCall).getParameterAsList(\"d\");\n        doReturn(parameterCounterValue).when(apiServletCall).getParameterAsList(\"n\");\n        doReturn(parameterPageValue).when(apiServletCall).getParameter(\"p\", \"0\");\n        doReturn(parameterLimitValue).when(apiServletCall).getParameter(\"c\", \"10\");\n        doReturn(parameterSearchValue).when(apiServletCall).getParameter(\"s\");\n        doReturn(parameterOrderValue).when(apiServletCall).getParameter(\"o\");\n        doReturn(new ArrayList<>()).when(apiServletCall).getParameterAsList(\"f\");\n\n        doNothing().when(apiServletCall).head(anyString(), anyString());\n        doNothing().when(apiServletCall).output(any(List.class));\n        doReturn(2).when(apiServletCall).countParameters();\n\n        final ItemSearchResult itemSearchResult = mock(ItemSearchResult.class);\n        when(itemSearchResult.getPage()).thenReturn(4);\n        when(itemSearchResult.getLength()).thenReturn(8);\n        when(itemSearchResult.getTotal()).thenReturn(789L);\n\n        when(api.runSearch(Integer.parseInt(parameterPageValue), Integer.parseInt(parameterLimitValue),\n                parameterSearchValue, parameterOrderValue, new HashMap<>(), parameterDeployValue,\n                parameterCounterValue)).thenReturn(itemSearchResult);\n\n        apiServletCall.doGet();\n        verify(api, times(1)).runSearch(Integer.parseInt(parameterPageValue), Integer.parseInt(parameterLimitValue),\n                parameterSearchValue, parameterOrderValue, new HashMap<>(), parameterDeployValue,\n                parameterCounterValue);\n        verify(apiServletCall).head(HttpHeaders.CONTENT_RANGE, 4 + \"-\" + 8 + \"/\" + 789L);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/framework/json/JSonSimpleDeserializerTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.json;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNull;\nimport static org.junit.Assert.fail;\n\nimport org.bonitasoft.web.toolkit.client.common.AbstractTreeNode;\nimport org.bonitasoft.web.toolkit.client.common.Tree;\nimport org.bonitasoft.web.toolkit.client.common.TreeIndexed;\nimport org.bonitasoft.web.toolkit.client.common.TreeLeaf;\nimport org.junit.Test;\n\n/**\n * @author Séverin Moussel\n */\npublic class JSonSimpleDeserializerTest {\n\n    @Test\n    public void testMalformedJson() {\n        try {\n            JSonSimpleDeserializer.unserializeTree(\"[toto}\");\n        } catch (final Exception e) {\n            return;\n        }\n        fail(\"Malformed Json must throw an exception\");\n    }\n\n    @Test\n    public void testEmptyInput() {\n        final AbstractTreeNode<String> tree = JSonSimpleDeserializer.unserializeTree(\"\");\n\n        assertNull(tree);\n    }\n\n    @Test\n    public void testSimpleObject() {\n        final AbstractTreeNode<String> tree = JSonSimpleDeserializer\n                .unserializeTree(\"{\\\"name\\\":\\\"toto\\\",\\\"path\\\":\\\"titi\\\"}\");\n\n        if (!(tree instanceof TreeIndexed<?>)) {\n            fail(\"Fail to parse a simple object in JSON\");\n        }\n\n        final TreeIndexed<String> tree2 = (TreeIndexed<String>) tree;\n        assertEquals(tree2.getValue(\"name\"), \"toto\");\n        assertEquals(tree2.getValue(\"path\"), \"titi\");\n        assertNull(tree2.getValue(\"unassigned\"));\n    }\n\n    @Test\n    public void testEmptyObject() {\n        final AbstractTreeNode<String> tree = JSonSimpleDeserializer.unserializeTree(\"{}\");\n\n        if (!(tree instanceof TreeIndexed<?>)) {\n            fail(\"Fail to parse a simple object in JSON\");\n        }\n\n        assertEquals(\"{}\", tree.toJson());\n    }\n\n    @Test\n    public void testSimpleArray() {\n        final AbstractTreeNode<String> tree = JSonSimpleDeserializer.unserializeTree(\" [\\\"name\\\",5,true]\");\n\n        if (!(tree instanceof Tree<?>)) {\n            fail(\"Fail to parse a simple array in JSON\");\n        }\n\n        final Tree<String> tree2 = (Tree<String>) tree;\n\n        assertEquals(\"name\", tree2.getValues().get(0));\n        assertEquals(\"5\", tree2.getValues().get(1));\n        assertEquals(\"1\", tree2.getValues().get(2));\n\n        assertEquals(\"name\", ((TreeLeaf<String>) tree2.get(0)).getValue());\n        assertEquals(\"5\", ((TreeLeaf<String>) tree2.get(1)).getValue());\n        assertEquals(\"1\", ((TreeLeaf<String>) tree2.get(2)).getValue());\n    }\n\n    @Test\n    public void testEmptyArray() {\n        final AbstractTreeNode<String> tree = JSonSimpleDeserializer.unserializeTree(\"[]\");\n\n        if (!(tree instanceof Tree<?>)) {\n            fail(\"Fail to parse a simple array in JSON\");\n        }\n\n        assertEquals(\"[]\", tree.toJson());\n    }\n\n    @Test\n    public void testOneElementArray() {\n        final AbstractTreeNode<String> tree = JSonSimpleDeserializer.unserializeTree(\"[101]\");\n\n        if (!(tree instanceof Tree<?>)) {\n            fail(\"Fail to parse a simple array in JSON\");\n        }\n\n        assertEquals(\"[\\\"101\\\"]\", tree.toJson());\n\n        final Tree<String> tree2 = (Tree<String>) tree;\n\n        assertEquals(tree2.getValues().get(0), \"101\");\n    }\n\n    @Test\n    public void testObjectWithArray() {\n        final AbstractTreeNode<String> tree = JSonSimpleDeserializer\n                .unserializeTree(\"{\\\"name\\\":\\\"toto\\\",\\\"categories_id\\\":[1,2,5]}\");\n\n        if (!(tree instanceof TreeIndexed<?>)) {\n            fail(\"Fail to parse a compound object in JSON\");\n        }\n\n        final AbstractTreeNode<String> treeCat = ((TreeIndexed<String>) tree).get(\"categories_id\");\n\n        if (!(treeCat instanceof Tree<?>)) {\n            fail(\"Fail to parse an array in an object in JSON\");\n        }\n\n        final Tree<String> tree2 = (Tree<String>) treeCat;\n\n        assertEquals(tree2.getValues().get(0), \"1\");\n        assertEquals(tree2.getValues().get(1), \"2\");\n        assertEquals(tree2.getValues().get(2), \"5\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/framework/json/JacksonDeserializerTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.json;\n\nimport static org.hamcrest.Matchers.equalTo;\nimport static org.hamcrest.Matchers.hasItem;\nimport static org.hamcrest.Matchers.hasItems;\nimport static org.hamcrest.Matchers.is;\nimport static org.junit.Assert.assertThat;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport org.bonitasoft.web.rest.server.APITestWithMock;\nimport org.bonitasoft.web.rest.server.framework.json.model.Address;\nimport org.bonitasoft.web.rest.server.framework.json.model.User;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/** extends APITestWithMock to avoid NullPointerException on I18n */\npublic class JacksonDeserializerTest extends APITestWithMock {\n\n    private JacksonDeserializer jacksonDeserializer;\n\n    @Before\n    public void initializeDeserializer() {\n        jacksonDeserializer = new JacksonDeserializer();\n    }\n\n    @Test(expected = APIException.class)\n    public void deserialize_throw_exception_if_json_is_non_well_formed() {\n        String nonWellFormedJson = \"someJsonNonWellFormedJson\";\n\n        jacksonDeserializer.deserialize(nonWellFormedJson, String.class);\n    }\n\n    @Test(expected = APIException.class)\n    public void deserialize_throw_exception_if_mapping_between_class_and_json_is_incorrect() {\n        String notUserJson = \"{\\\"unknownUserAttribute\\\": \\\"unknownAttributeValue\\\"}\";\n\n        jacksonDeserializer.deserialize(notUserJson, User.class);\n    }\n\n    @Test\n    public void deserialize_can_deserialize_primitives_types() {\n\n        Long deserializedLong = jacksonDeserializer.deserialize(\"1\", Long.class);\n\n        assertThat(deserializedLong, is(1L));\n    }\n\n    @Test\n    public void deserialize_can_deserialize_complex_types() {\n        User expectedUser = new User(1, \"Colin\", \"Puy\", new Date(428558400000L),\n                new Address(\"310 La Gouterie\", \"Charnecles\"));\n        String json = \"{\\\"address\\\":{\\\"street\\\":\\\"310 La Gouterie\\\",\\\"city\\\":\\\"Charnecles\\\"},\\\"id\\\":1,\\\"firstName\\\":\\\"Colin\\\",\\\"lastName\\\":\\\"Puy\\\",\\\"birthday\\\":428558400000}\";\n\n        User deserializedUser = jacksonDeserializer.deserialize(json, User.class);\n\n        assertThat(deserializedUser, equalTo(expectedUser));\n    }\n\n    @Test(expected = APIException.class)\n    public void deserializeList_throw_exception_if_json_is_not_a_list() {\n        String json = \"{\\\"address\\\":{\\\"street\\\":\\\"310 La Gouterie\\\",\\\"city\\\":\\\"Charnecles\\\"},\\\"id\\\":1,\\\"firstName\\\":\\\"Colin\\\",\\\"lastName\\\":\\\"Puy\\\",\\\"birthday\\\":428558400000}\";\n\n        jacksonDeserializer.deserializeList(json, User.class);\n    }\n\n    @Test\n    public void deserializeList_can_deserialize_primitives_types() {\n\n        List<Long> longs = jacksonDeserializer.deserializeList(\"[1, 2, 3]\", Long.class);\n\n        assertThat(longs, hasItems(1L, 2L, 3L));\n    }\n\n    @Test\n    public void deserializeList_can_deserialize_list_of_complex_type() {\n        User expectedUser1 = new User(1, \"Colin\", \"Puy\", new Date(428558400000L),\n                new Address(\"310 La Gouterie\", \"Charnecles\"));\n        User expectedUser2 = new User(2, \"Clara\", \"Morgan\", new Date(349246800000L),\n                new Address(\"somewhere i don't know\", \"Paris\"));\n        String json = \"[\"\n                +\n                \"{\\\"address\\\":{\\\"city\\\":\\\"Charnecles\\\",\\\"street\\\":\\\"310 La Gouterie\\\"},\\\"id\\\":1,\\\"firstName\\\":\\\"Colin\\\",\\\"lastName\\\":\\\"Puy\\\",\\\"birthday\\\":428558400000},\"\n                +\n                \"{\\\"address\\\":{\\\"city\\\":\\\"Paris\\\",\\\"street\\\":\\\"somewhere i don't know\\\"},\\\"id\\\":2,\\\"firstName\\\":\\\"Clara\\\",\\\"lastName\\\":\\\"Morgan\\\",\\\"birthday\\\":349246800000}\"\n                +\n                \"]\";\n\n        List<User> users = jacksonDeserializer.deserializeList(json, User.class);\n\n        assertThat(users, hasItem(expectedUser1));\n        assertThat(users, hasItem(expectedUser2));\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/framework/json/JacksonSerializerTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.json;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.web.rest.server.framework.json.model.ProfileImportStatusMessageFake;\nimport org.junit.Test;\n\npublic class JacksonSerializerTest {\n\n    @Test\n    public void testSerialize() throws Exception {\n        // Given\n        JacksonSerializer serializer = new JacksonSerializer();\n        ProfileImportStatusMessageFake message = new ProfileImportStatusMessageFake(\"profile1\", \"will be replaced\");\n        message.addError(\"Organization: skks\");\n        message.addError(\"Page: page1\");\n\n        // When\n        String serialize = serializer.serialize(message);\n\n        // Then\n        assertThat(serialize)\n                .isEqualTo(\"{\\\"errors\\\":[\\\"Organization: skks\\\",\\\"Page: page1\\\"],\\\"profileName\\\":\\\"profile1\\\"}\");\n\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/framework/json/model/Address.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.json.model;\n\nimport java.io.Serializable;\n\n@SuppressWarnings(\"serial\")\npublic class Address implements Serializable {\n\n    private String street;\n    private String city;\n\n    // Do not remove. Used by Json Serialization.\n    public Address() {\n    }\n\n    public Address(String street, String city) {\n        this.street = street;\n        this.city = city;\n    }\n\n    // Do not remove. Used by Json Serialization.\n    public String getStreet() {\n        return street;\n    }\n\n    // Do not remove. Used by Json Serialization.\n    public void setStreet(String street) {\n        this.street = street;\n    }\n\n    // Do not remove. Used by Json Serialization.\n    public String getCity() {\n        return city;\n    }\n\n    // Do not remove. Used by Json Serialization.\n    public void setCity(String city) {\n        this.city = city;\n    }\n\n    @Override\n    public String toString() {\n        return \"Address [street=\" + street + \", city=\" + city + \"]\";\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + ((city == null) ? 0 : city.hashCode());\n        result = prime * result + ((street == null) ? 0 : street.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj)\n            return true;\n        if (obj == null)\n            return false;\n        if (getClass() != obj.getClass())\n            return false;\n        Address other = (Address) obj;\n        if (city == null) {\n            if (other.city != null)\n                return false;\n        } else if (!city.equals(other.city))\n            return false;\n        if (street == null) {\n            if (other.street != null)\n                return false;\n        } else if (!street.equals(other.street))\n            return false;\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/framework/json/model/ProfileImportStatusMessageFake.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.json.model;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * @author Fabio Lombardi\n */\npublic class ProfileImportStatusMessageFake {\n\n    private List<String> errors;\n\n    private String profileName;\n\n    private String status;\n\n    public ProfileImportStatusMessageFake(String profileName, String status) {\n        errors = new ArrayList<>();\n        this.profileName = profileName;\n        this.status = status;\n    }\n\n    public void addError(String errorMessage) {\n        errors.add(errorMessage);\n    }\n\n    public void addErrors(List<String> errorMessages) {\n        errors.addAll(errorMessages);\n    }\n\n    public void setProfileName(String name) {\n        this.profileName = name;\n    }\n\n    public List<String> getErrors() {\n        return errors;\n    }\n\n    public String getProfileName() {\n        return profileName;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/framework/json/model/User.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.json.model;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n@SuppressWarnings(\"serial\")\npublic class User implements Serializable {\n\n    private Integer id;\n    private String firstName;\n    private String lastName;\n    private Date birthday;\n    private Address address;\n\n    public User() {\n\n    }\n\n    public User(Integer id, String firstName, String lastName, Date birthday, Address address) {\n        this.id = id;\n        this.firstName = firstName;\n        this.lastName = lastName;\n        this.birthday = birthday;\n        this.address = address;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    public void setId(Integer id) {\n        this.id = id;\n    }\n\n    public String getFirstName() {\n        return firstName;\n    }\n\n    public void setFirstName(String firstName) {\n        this.firstName = firstName;\n    }\n\n    public String getLastName() {\n        return lastName;\n    }\n\n    public void setLastName(String lastName) {\n        this.lastName = lastName;\n    }\n\n    public Address getAddress() {\n        return address;\n    }\n\n    public void setAddress(Address address) {\n        this.address = address;\n    }\n\n    public Date getBirthday() {\n        return birthday;\n    }\n\n    public void setBirthday(Date birthday) {\n        this.birthday = birthday;\n    }\n\n    @Override\n    public String toString() {\n        return \"User [id=\" + id + \", firstName=\" + firstName + \", lastName=\"\n                + lastName + \", address=\" + address + \"]\";\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + ((address == null) ? 0 : address.hashCode());\n        result = prime * result\n                + ((birthday == null) ? 0 : birthday.hashCode());\n        result = prime * result\n                + ((firstName == null) ? 0 : firstName.hashCode());\n        result = prime * result + ((id == null) ? 0 : id.hashCode());\n        result = prime * result\n                + ((lastName == null) ? 0 : lastName.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj)\n            return true;\n        if (obj == null)\n            return false;\n        if (getClass() != obj.getClass())\n            return false;\n        User other = (User) obj;\n        if (address == null) {\n            if (other.address != null)\n                return false;\n        } else if (!address.equals(other.address))\n            return false;\n        if (birthday == null) {\n            if (other.birthday != null)\n                return false;\n        } else if (!birthday.equals(other.birthday))\n            return false;\n        if (firstName == null) {\n            if (other.firstName != null)\n                return false;\n        } else if (!firstName.equals(other.firstName))\n            return false;\n        if (id == null) {\n            if (other.id != null)\n                return false;\n        } else if (!id.equals(other.id))\n            return false;\n        if (lastName == null) {\n            if (other.lastName != null)\n                return false;\n        } else if (!lastName.equals(other.lastName))\n            return false;\n        return true;\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/framework/utils/FilePathBuilderTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.utils;\n\nimport static junit.framework.Assert.assertEquals;\n\nimport java.io.File;\n\nimport org.junit.Test;\n\n/**\n * @author Colin PUY\n */\npublic class FilePathBuilderTest {\n\n    @Test\n    public void testAppend() throws Exception {\n        String path1 = \"org\", path2 = \"bonita\", path3 = \"web/service\";\n\n        FilePathBuilder path = new FilePathBuilder(path1).append(path2).append(path3);\n\n        assertEquals(buildExpectedPath(path1, path2, path3), path.toString());\n    }\n\n    private String buildExpectedPath(String root, String... paths) {\n        StringBuilder expected = new StringBuilder(root);\n        for (String path : paths) {\n            addPath(expected, path);\n        }\n        return expected.toString();\n    }\n\n    private void addPath(StringBuilder expected, String path) {\n        expected.append(File.separator);\n        expected.append(path);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/framework/utils/RestRequestURIParserTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.utils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\nimport static org.mockito.Mockito.doReturn;\n\nimport javax.servlet.http.HttpServletMapping;\nimport javax.servlet.http.HttpServletRequest;\n\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIMalformedUrlException;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class RestRequestURIParserTest {\n\n    @Mock\n    private HttpServletRequest httpServletRequest;\n\n    @Mock\n    private HttpServletMapping httpServletMapping;\n\n    @InjectMocks\n    private RestRequestURIParser restRequestURIParser;\n\n    @Before\n    public void before() {\n        doReturn(httpServletMapping).when(httpServletRequest).getHttpServletMapping();\n        doReturn(\"\").when(httpServletRequest).getServletPath();\n    }\n\n    @Test\n    public void should_parsePath_request_info_with_id() {\n        doReturn(\"/bpm/case/15\").when(httpServletRequest).getPathInfo();\n\n        ParsedRestRequestURI result = restRequestURIParser.parse();\n\n        assertThat(result.getApiName()).isEqualTo(\"bpm\");\n        assertThat(result.getResourceName()).isEqualTo(\"case\");\n        assertThat(result.getResourceQualifiers().getPart(0)).isEqualTo(\"15\");\n    }\n\n    @Test\n    public void should_parsePath_request_info() {\n        doReturn(\"/bpm/case\").when(httpServletRequest).getPathInfo();\n\n        ParsedRestRequestURI result = restRequestURIParser.parse();\n\n        assertThat(result.getResourceName()).isEqualTo(\"case\");\n        assertThat(result.getApiName()).isEqualTo(\"bpm\");\n        assertThat(result.getResourceQualifiers()).isNull();\n    }\n\n    @Test\n    public void should_parsePath_when_exact_match_servlet_mapped_under_API() {\n        // When an exact-match servlet is mapped to /API/documentDownload,\n        // servletPath = \"/API/documentDownload\" and pathInfo = null.\n        // \"API\" is included as apiName to match permission entry \"GET|API/documentDownload\".\n        doReturn(null).when(httpServletRequest).getPathInfo();\n        doReturn(\"/API/documentDownload\").when(httpServletRequest).getServletPath();\n\n        ParsedRestRequestURI result = restRequestURIParser.parse();\n\n        assertThat(result.getApiName()).isEqualTo(\"API\");\n        assertThat(result.getResourceName()).isEqualTo(\"documentDownload\");\n        assertThat(result.getResourceQualifiers()).isNull();\n    }\n\n    @Test\n    public void should_throw_when_pathInfo_is_null_and_servletPath_has_only_API() {\n        // When servletPath is just \"/API\" with no resource at all, parsing should fail.\n        doReturn(null).when(httpServletRequest).getPathInfo();\n        doReturn(\"/API\").when(httpServletRequest).getServletPath();\n        var requestUrl = new StringBuffer(\"http://my-host/API\");\n        doReturn(requestUrl).when(httpServletRequest).getRequestURL();\n\n        assertThatThrownBy(() -> restRequestURIParser.parse())\n                .isInstanceOf(APIMalformedUrlException.class)\n                .hasMessage(\"Missing API or resource name in request URL\");\n    }\n\n    @Test\n    public void should_parsePath_when_exact_match_servlet_under_portal() {\n        // When an exact-match servlet is mapped to /portal/imageUpload,\n        // there is no \"API\" segment. Parse from index 1 so that\n        // apiName = \"portal\" and resourceName = \"imageUpload\",\n        // matching permission entry \"POST|portal/imageUpload\".\n        doReturn(null).when(httpServletRequest).getPathInfo();\n        doReturn(\"/portal/imageUpload\").when(httpServletRequest).getServletPath();\n\n        ParsedRestRequestURI result = restRequestURIParser.parse();\n\n        assertThat(result.getApiName()).isEqualTo(\"portal\");\n        assertThat(result.getResourceName()).isEqualTo(\"imageUpload\");\n        assertThat(result.getResourceQualifiers()).isNull();\n    }\n\n    @Test\n    public void should_parsePath_when_wildcard_servlet_under_services() {\n        // The RestAPIAuthorizationFilter is also mapped to /services/*.\n        // servletPath = \"/services\" and pathInfo = \"/application/import\".\n        doReturn(\"/application/import\").when(httpServletRequest).getPathInfo();\n        doReturn(\"/services\").when(httpServletRequest).getServletPath();\n\n        ParsedRestRequestURI result = restRequestURIParser.parse();\n\n        assertThat(result.getApiName()).isEqualTo(\"application\");\n        assertThat(result.getResourceName()).isEqualTo(\"import\");\n        assertThat(result.getResourceQualifiers()).isNull();\n    }\n\n    @Test\n    public void should_parsePath_spring_mvc_request_info() {\n        doReturn(RestRequestURIParser.SPRING_REST_SERVLET_NAME).when(httpServletMapping).getServletName();\n        doReturn(\"/API/system/maintenance\").when(httpServletRequest).getServletPath();\n\n        ParsedRestRequestURI result = restRequestURIParser.parse();\n\n        assertThat(result.getApiName()).isEqualTo(\"system\");\n        assertThat(result.getResourceName()).isEqualTo(\"maintenance\");\n        assertThat(result.getResourceQualifiers()).isNull();\n    }\n\n    @Test\n    public void should_parsePath_spring_mvc_request_info_with_id() {\n        doReturn(RestRequestURIParser.SPRING_REST_SERVLET_NAME).when(httpServletMapping).getServletName();\n        doReturn(\"/API/system/maintenance\").when(httpServletRequest).getServletPath();\n        doReturn(\"/1\").when(httpServletRequest).getPathInfo();\n\n        ParsedRestRequestURI result = restRequestURIParser.parse();\n\n        assertThat(result.getApiName()).isEqualTo(\"system\");\n        assertThat(result.getResourceName()).isEqualTo(\"maintenance\");\n        assertThat(result.getResourceQualifiers().getPart(0)).isEqualTo(\"1\");\n    }\n\n    @Test\n    public void should_parsePath_spring_mvc_custom_page_api_extension() {\n        doReturn(RestRequestURIParser.SPRING_REST_SERVLET_NAME).when(httpServletMapping).getServletName();\n        doReturn(\"/portal/custom-page/API/extension\").when(httpServletRequest).getServletPath();\n        doReturn(\"/my-rest-api\").when(httpServletRequest).getPathInfo();\n\n        ParsedRestRequestURI result = restRequestURIParser.parse();\n\n        assertThat(result.getApiName()).isEqualTo(\"extension\");\n        assertThat(result.getResourceName()).isEqualTo(\"my-rest-api\");\n        assertThat(result.getResourceQualifiers()).isNull();\n    }\n\n    @Test\n    public void should_throw_when_spring_mvc_request_has_no_resource_name() {\n        doReturn(RestRequestURIParser.SPRING_REST_SERVLET_NAME).when(httpServletMapping).getServletName();\n        var path = \"/API\";\n        doReturn(path).when(httpServletRequest).getServletPath();\n        var requestUrl = new StringBuffer(\"http://my-host\" + path);\n        doReturn(requestUrl).when(httpServletRequest).getRequestURL();\n\n        assertThatThrownBy(() -> restRequestURIParser.parse())\n                .isInstanceOf(APIMalformedUrlException.class)\n                .hasMessage(\"Missing API or resource name in request URL\")\n                .hasMessageNotContainingAny(requestUrl.toString(), path);\n    }\n\n    @Test\n    public void should_parsePath_when_no_servlet_mapped_to_API_wildcard() {\n        // When no servlet is mapped to /API/*, the default servlet handles the request.\n        // In that case pathInfo is null and servletPath contains the full path including /API.\n        doReturn(null).when(httpServletRequest).getPathInfo();\n        doReturn(\"/API/living/application\").when(httpServletRequest).getServletPath();\n\n        ParsedRestRequestURI result = restRequestURIParser.parse();\n\n        assertThat(result.getApiName()).isEqualTo(\"living\");\n        assertThat(result.getResourceName()).isEqualTo(\"application\");\n        assertThat(result.getResourceQualifiers()).isNull();\n    }\n\n    @Test\n    public void should_parsePath_when_no_servlet_mapped_to_API_wildcard_with_id() {\n        doReturn(null).when(httpServletRequest).getPathInfo();\n        doReturn(\"/API/bpm/case/42\").when(httpServletRequest).getServletPath();\n\n        ParsedRestRequestURI result = restRequestURIParser.parse();\n\n        assertThat(result.getApiName()).isEqualTo(\"bpm\");\n        assertThat(result.getResourceName()).isEqualTo(\"case\");\n        assertThat(result.getResourceQualifiers().getPart(0)).isEqualTo(\"42\");\n    }\n\n    @Test\n    public void should_parsePath_when_no_servlet_mapped_to_APIToolkit_path() {\n        doReturn(null).when(httpServletRequest).getPathInfo();\n        doReturn(\"/APIToolkit/bpm/process\").when(httpServletRequest).getServletPath();\n\n        ParsedRestRequestURI result = restRequestURIParser.parse();\n\n        assertThat(result.getApiName()).isEqualTo(\"bpm\");\n        assertThat(result.getResourceName()).isEqualTo(\"process\");\n        assertThat(result.getResourceQualifiers()).isNull();\n    }\n\n    @Test\n    public void should_parsePath_when_servlet_mapped_to_specific_API_subpath() {\n        // When a servlet is mapped to a specific sub-path like /API/avatars/*,\n        // servletPath = \"/API/avatars\" and pathInfo = \"/17\" (just the ID).\n        // \"API\" must be included as apiName to match permission entry \"GET|API/avatars\".\n        doReturn(\"/API/avatars\").when(httpServletRequest).getServletPath();\n        doReturn(\"/17\").when(httpServletRequest).getPathInfo();\n\n        ParsedRestRequestURI result = restRequestURIParser.parse();\n\n        assertThat(result.getApiName()).isEqualTo(\"API\");\n        assertThat(result.getResourceName()).isEqualTo(\"avatars\");\n        assertThat(result.getResourceQualifiers().getPart(0)).isEqualTo(\"17\");\n    }\n\n    @Test\n    public void should_parsePath_when_servlet_mapped_to_portal_custom_page_API_subpath() {\n        doReturn(RestRequestURIParser.CUSTOM_PAGE_SERVLET_NAME).when(httpServletMapping).getServletName();\n        // When a servlet is mapped to /portal/custom-page/*,\n        doReturn(\"/portal/custom-page\").when(httpServletRequest).getServletPath();\n        // pathInfo includes the API, resource and ID.\n        doReturn(\"/API/identity/user/17\").when(httpServletRequest).getPathInfo();\n\n        ParsedRestRequestURI result = restRequestURIParser.parse();\n\n        assertThat(result.getApiName()).isEqualTo(\"identity\");\n        assertThat(result.getResourceName()).isEqualTo(\"user\");\n        assertThat(result.getResourceQualifiers().getPart(0)).isEqualTo(\"17\");\n    }\n\n    @Test\n    public void should_parsePath_when_servlet_mapped_to_portal_custom_page_API_avatars_subpath() {\n        // When a servlet is mapped to /portal/custom-page/API/avatars/*,\n        // servletPath includes the full prefix and pathInfo is just the ID.\n        doReturn(\"/portal/custom-page/API/avatars\").when(httpServletRequest).getServletPath();\n        doReturn(\"/17\").when(httpServletRequest).getPathInfo();\n\n        ParsedRestRequestURI result = restRequestURIParser.parse();\n\n        assertThat(result.getApiName()).isEqualTo(\"API\");\n        assertThat(result.getResourceName()).isEqualTo(\"avatars\");\n        assertThat(result.getResourceQualifiers().getPart(0)).isEqualTo(\"17\");\n    }\n\n    @Test\n    public void should_parsePath_spring_mvc_custom_page_api_extension_with_qualifier() {\n        doReturn(RestRequestURIParser.SPRING_REST_SERVLET_NAME).when(httpServletMapping).getServletName();\n        doReturn(\"/portal/custom-page/API/extension\").when(httpServletRequest).getServletPath();\n        doReturn(\"/my-rest-api/resource1\").when(httpServletRequest).getPathInfo();\n\n        ParsedRestRequestURI result = restRequestURIParser.parse();\n\n        assertThat(result.getApiName()).isEqualTo(\"extension\");\n        assertThat(result.getResourceName()).isEqualTo(\"my-rest-api\");\n        assertThat(result.getResourceQualifiers().getPart(0)).isEqualTo(\"resource1\");\n    }\n\n    @Test\n    public void should_parsePath_when_APIToolkit_servlet_with_pathInfo() {\n        // BonitaRestAPIServlet is mapped to /APIToolkit/*.\n        // servletPath = \"/APIToolkit\" and pathInfo = \"/bpm/case/15\".\n        doReturn(\"/APIToolkit\").when(httpServletRequest).getServletPath();\n        doReturn(\"/bpm/case/15\").when(httpServletRequest).getPathInfo();\n\n        ParsedRestRequestURI result = restRequestURIParser.parse();\n\n        assertThat(result.getApiName()).isEqualTo(\"bpm\");\n        assertThat(result.getResourceName()).isEqualTo(\"case\");\n        assertThat(result.getResourceQualifiers().getPart(0)).isEqualTo(\"15\");\n    }\n\n    @Test\n    public void should_parsePath_spring_mvc_with_APISpringInternal_rewritten_url() {\n        // When UrlRewriteFilter rewrites /API/bpm/userTask/3/execution to\n        // /APISpringInternal/bpm/userTask/3/execution and forwards to Spring MVC.\n        doReturn(RestRequestURIParser.SPRING_REST_SERVLET_NAME).when(httpServletMapping).getServletName();\n        doReturn(\"/APISpringInternal\").when(httpServletRequest).getServletPath();\n        doReturn(\"/bpm/userTask/3/execution\").when(httpServletRequest).getPathInfo();\n\n        ParsedRestRequestURI result = restRequestURIParser.parse();\n\n        assertThat(result.getApiName()).isEqualTo(\"bpm\");\n        assertThat(result.getResourceName()).isEqualTo(\"userTask\");\n        assertThat(result.getResourceQualifiers().getPart(0)).isEqualTo(\"3\");\n        assertThat(result.getResourceQualifiers().getPart(1)).isEqualTo(\"execution\");\n    }\n\n    @Test\n    public void should_throw_when_url_path_too_short_and_no_API_segment() {\n        // A path with fewer than 3 segments and no API marker should fail\n        doReturn(null).when(httpServletRequest).getPathInfo();\n        doReturn(\"/x\").when(httpServletRequest).getServletPath();\n        var requestUrl = new StringBuffer(\"http://my-host/x\");\n        doReturn(requestUrl).when(httpServletRequest).getRequestURL();\n\n        assertThatThrownBy(() -> restRequestURIParser.parse())\n                .isInstanceOf(APIMalformedUrlException.class)\n                .hasMessage(\"Missing API path segment in request URL\");\n    }\n\n    @Test\n    public void should_parsePath_spring_mvc_with_direct_wildcard_mapping() {\n        // When Spring MVC has a direct wildcard mapping like /API/bpm/activityVariable/*,\n        // servletPath = \"/API/bpm/activityVariable\" and pathInfo = \"/3/myVar\".\n        doReturn(RestRequestURIParser.SPRING_REST_SERVLET_NAME).when(httpServletMapping).getServletName();\n        doReturn(\"/API/bpm/activityVariable\").when(httpServletRequest).getServletPath();\n        doReturn(\"/3/myVar\").when(httpServletRequest).getPathInfo();\n\n        ParsedRestRequestURI result = restRequestURIParser.parse();\n\n        assertThat(result.getApiName()).isEqualTo(\"bpm\");\n        assertThat(result.getResourceName()).isEqualTo(\"activityVariable\");\n        assertThat(result.getResourceQualifiers().getPart(0)).isEqualTo(\"3\");\n        assertThat(result.getResourceQualifiers().getPart(1)).isEqualTo(\"myVar\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/framework/utils/converter/ConverterFactoryTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.utils.converter;\n\nimport static org.junit.Assert.assertTrue;\n\nimport javax.servlet.Servlet;\n\nimport org.bonitasoft.web.rest.server.framework.utils.converter.typed.BooleanConverter;\nimport org.bonitasoft.web.rest.server.framework.utils.converter.typed.DoubleConverter;\nimport org.bonitasoft.web.rest.server.framework.utils.converter.typed.IntegerConverter;\nimport org.bonitasoft.web.rest.server.framework.utils.converter.typed.LongConverter;\nimport org.bonitasoft.web.rest.server.framework.utils.converter.typed.StringConverter;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Colin PUY\n */\npublic class ConverterFactoryTest {\n\n    private ConverterFactory factory;\n\n    @Before\n    public void initFactory() {\n        factory = new ConverterFactory();\n    }\n\n    @Test\n    public void factoryCreateABooleanConverterForBooleanClassName() {\n\n        Converter<?> createConverter = factory.createConverter(Boolean.class.getName());\n\n        assertTrue(createConverter instanceof BooleanConverter);\n    }\n\n    @Test\n    public void factoryCreateADoubleConverterForDoubleClassName() {\n\n        Converter<?> createConverter = factory.createConverter(Double.class.getName());\n\n        assertTrue(createConverter instanceof DoubleConverter);\n    }\n\n    @Test\n    public void factoryCreateALongConverterForLongClassName() {\n\n        Converter<?> createConverter = factory.createConverter(Long.class.getName());\n\n        assertTrue(createConverter instanceof LongConverter);\n    }\n\n    @Test\n    public void factoryCreateAStringConverterForStringClassName() {\n\n        Converter<?> createConverter = factory.createConverter(String.class.getName());\n\n        assertTrue(createConverter instanceof StringConverter);\n    }\n\n    @Test\n    public void factoryCreateAnIntegerConverterForIntegerClassName() {\n\n        Converter<?> createConverter = factory.createConverter(Integer.class.getName());\n\n        assertTrue(createConverter instanceof IntegerConverter);\n    }\n\n    @Test(expected = UnsupportedOperationException.class)\n    public void factoryThrowExceptionForUnsuportedConverter() {\n        factory.createConverter(Servlet.class.getName());\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/framework/utils/converter/TypeConverterTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.utils.converter;\n\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\n/**\n * @author Colin PUY\n */\npublic class TypeConverterTest {\n\n    private TypeConverter converter;\n\n    @Mock\n    private ConverterFactory factory;\n\n    @Before\n    public void initConverter() {\n        MockitoAnnotations.initMocks(this);\n\n        converter = new TypeConverter(factory);\n    }\n\n    @Test\n    @SuppressWarnings({ \"rawtypes\", \"unchecked\" })\n    public void convertCreateAFactoryAndConvertStringValueToSerializableObject() throws Exception {\n        Converter typedConverter = mock(Converter.class);\n        when(factory.createConverter(anyString())).thenReturn(typedConverter);\n\n        converter.convert(\"aClassName\", \"somethingToconvert\");\n\n        verify(typedConverter).convert(\"somethingToconvert\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/framework/utils/converter/typed/BooleanConverterTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.utils.converter.typed;\n\nimport static junit.framework.Assert.assertFalse;\nimport static junit.framework.Assert.assertNull;\nimport static junit.framework.Assert.assertTrue;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Colin PUY\n */\npublic class BooleanConverterTest {\n\n    private BooleanConverter converter;\n\n    @Before\n    public void initConverter() {\n        converter = new BooleanConverter();\n    }\n\n    @Test\n    public void trueIsConvertedToTrue() throws Exception {\n\n        Boolean converted = converter.convert(\"true\");\n\n        assertTrue(converted);\n    }\n\n    @Test\n    public void falseIsConvertedToFalse() throws Exception {\n\n        Boolean converted = converter.convert(\"false\");\n\n        assertFalse(converted);\n    }\n\n    @Test\n    public void nullIsConvertedToNull() throws Exception {\n\n        Boolean converted = converter.convert(null);\n\n        assertNull(converted);\n    }\n\n    @Test\n    public void emptyIsConvertedToNull() throws Exception {\n\n        Boolean converted = converter.convert(\"\");\n\n        assertNull(converted);\n    }\n\n    @Test\n    public void anythingElseIsConvertedToFalse() throws Exception {\n\n        Boolean converted = converter.convert(\"something\");\n\n        assertFalse(converted);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/framework/utils/converter/typed/DateConverterTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.utils.converter.typed;\n\nimport static junit.framework.Assert.assertNull;\nimport static org.junit.Assert.assertEquals;\n\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.TimeZone;\n\nimport org.bonitasoft.web.rest.server.framework.utils.converter.ConversionException;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Nicolas Tith\n */\npublic class DateConverterTest {\n\n    private DateConverter converter;\n\n    @Before\n    public void initConverter() {\n        converter = new DateConverter();\n    }\n\n    @Test\n    public void nullIsConvertedToNull() throws Exception {\n\n        Date converted = converter.convert(null);\n\n        assertNull(converted);\n    }\n\n    @Test\n    public void emptyIsConvertedToNull() throws Exception {\n\n        Date converted = converter.convert(\"\");\n\n        assertNull(converted);\n    }\n\n    @Test\n    public void shouldConvertDateStringIntoDate() throws Exception {\n\n        Calendar c = Calendar.getInstance();\n        int hourOfDay = 11;\n        int minute = 43;\n        int second = 30;\n        int dayOfDateate = 18;\n        int year = 2014;\n        c.set(year, Calendar.AUGUST, dayOfDateate, hourOfDay, minute, second);\n        String timeZone = \"GMT\";\n        c.setTimeZone(TimeZone.getTimeZone(timeZone));\n        c.set(Calendar.MILLISECOND, 0);\n        Date date = c.getTime();\n\n        Date converted = converter.convert(\"Mon Aug \" + dayOfDateate + \" \" + hourOfDay + \":\" + minute + \":\" + second\n                + \" \" + timeZone + \" \" + year);\n\n        assertEquals(date.toString() + \" is not well converted\", date, converted);\n    }\n\n    @Test(expected = ConversionException.class)\n    public void nonParsableStringThrowAConversionException() throws Exception {\n        converter.convert(\"nonParsable\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/framework/utils/converter/typed/DoubleConverterTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.utils.converter.typed;\n\nimport static junit.framework.Assert.assertNull;\nimport static org.junit.Assert.assertEquals;\n\nimport org.bonitasoft.web.rest.server.framework.utils.converter.ConversionException;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Colin PUY\n */\npublic class DoubleConverterTest {\n\n    private DoubleConverter converter;\n\n    @Before\n    public void initConverter() {\n        converter = new DoubleConverter();\n    }\n\n    @Test\n    public void nullIsConvertedToNull() throws Exception {\n\n        Double converted = converter.convert(null);\n\n        assertNull(converted);\n    }\n\n    @Test\n    public void emptyIsConvertedToNull() throws Exception {\n\n        Double converted = converter.convert(\"\");\n\n        assertNull(converted);\n    }\n\n    @Test\n    public void doubleNumberIsParsedToDouble() throws Exception {\n\n        double converted = converter.convert(\"1.23\");\n\n        assertEquals(1.23d, converted, 0d);\n    }\n\n    @Test(expected = ConversionException.class)\n    public void nonParsableStringThrowAConversionException() throws Exception {\n        converter.convert(\"nonParsable\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/framework/utils/converter/typed/IntegerConverterTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.utils.converter.typed;\n\nimport static junit.framework.Assert.assertNull;\nimport static org.junit.Assert.assertEquals;\n\nimport org.bonitasoft.web.rest.server.framework.utils.converter.ConversionException;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Colin PUY\n */\npublic class IntegerConverterTest {\n\n    private IntegerConverter converter;\n\n    @Before\n    public void initConverter() {\n        converter = new IntegerConverter();\n    }\n\n    @Test\n    public void nullIsConvertedToNull() throws Exception {\n\n        Integer converted = converter.convert(null);\n\n        assertNull(converted);\n    }\n\n    @Test\n    public void emptyIsConvertedToNull() throws Exception {\n\n        Integer converted = converter.convert(\"\");\n\n        assertNull(converted);\n    }\n\n    @Test\n    public void intNumberIsParsedToInteger() throws Exception {\n\n        int converted = converter.convert(\"456789\");\n\n        assertEquals(456789, converted);\n    }\n\n    @Test(expected = ConversionException.class)\n    public void nonParsableStringThrowAConversionException() throws Exception {\n        converter.convert(\"nonParsable\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/framework/utils/converter/typed/LongConverterTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.utils.converter.typed;\n\nimport static junit.framework.Assert.assertNull;\nimport static org.junit.Assert.assertEquals;\n\nimport org.bonitasoft.web.rest.server.framework.utils.converter.ConversionException;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Colin PUY\n */\npublic class LongConverterTest {\n\n    private LongConverter converter;\n\n    @Before\n    public void initConverter() {\n        converter = new LongConverter();\n    }\n\n    @Test\n    public void nullIsConvertedToNull() throws Exception {\n\n        Long converted = converter.convert(null);\n\n        assertNull(converted);\n    }\n\n    @Test\n    public void emptyIsConvertedToNull() throws Exception {\n\n        Long converted = converter.convert(\"\");\n\n        assertNull(converted);\n    }\n\n    @Test\n    public void longNumberIsParsedToLong() throws Exception {\n\n        long converted = converter.convert(\"456789\");\n\n        assertEquals(456789L, converted);\n    }\n\n    @Test(expected = ConversionException.class)\n    public void nonParsableStringThrowAConversionException() throws Exception {\n        converter.convert(\"nonParsable\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/framework/utils/converter/typed/StringConverterTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.rest.server.framework.utils.converter.typed;\n\nimport static junit.framework.Assert.assertEquals;\n\nimport org.junit.Test;\n\n/**\n * @author Colin PUY\n */\npublic class StringConverterTest {\n\n    @Test\n    public void stringConverterIsDummy() throws Exception {\n\n        String converted = new StringConverter().convert(\"any string\");\n\n        assertEquals(\"any string\", converted);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/server/login/LoginFailureTrackerAccessorTest.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.server.login;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport javax.servlet.ServletContext;\n\nimport org.junit.Test;\nimport org.springframework.beans.factory.NoSuchBeanDefinitionException;\nimport org.springframework.web.context.WebApplicationContext;\n\npublic class LoginFailureTrackerAccessorTest {\n\n    @Test\n    public void should_return_null_when_application_context_is_not_available() {\n        ServletContext servletContext = mock(ServletContext.class);\n        // getAttribute returns null by default → WebApplicationContextUtils returns null\n\n        LoginFailureTracker result = LoginFailureTrackerAccessor.getLoginFailureTracker(servletContext);\n\n        assertThat(result).isNull();\n    }\n\n    @Test\n    public void should_return_null_when_bean_is_not_found() {\n        ServletContext servletContext = mock(ServletContext.class);\n        WebApplicationContext appContext = mock(WebApplicationContext.class);\n        when(servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE))\n                .thenReturn(appContext);\n        when(appContext.getBean(LoginFailureTracker.class))\n                .thenThrow(new NoSuchBeanDefinitionException(LoginFailureTracker.class));\n\n        LoginFailureTracker result = LoginFailureTrackerAccessor.getLoginFailureTracker(servletContext);\n\n        assertThat(result).isNull();\n    }\n\n    @Test\n    public void should_return_tracker_when_bean_is_found() {\n        ServletContext servletContext = mock(ServletContext.class);\n        WebApplicationContext appContext = mock(WebApplicationContext.class);\n        LoginFailureTracker tracker = mock(LoginFailureTracker.class);\n        when(servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE))\n                .thenReturn(appContext);\n        when(appContext.getBean(LoginFailureTracker.class)).thenReturn(tracker);\n\n        LoginFailureTracker result = LoginFailureTrackerAccessor.getLoginFailureTracker(servletContext);\n\n        assertThat(result).isSameAs(tracker);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/toolkit/client/common/exception/http/JsonExceptionSerializerTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.exception.http;\n\nimport static org.hamcrest.CoreMatchers.equalTo;\nimport static org.junit.Assert.assertThat;\n\nimport org.bonitasoft.console.common.FakeI18n;\nimport org.bonitasoft.console.common.server.i18n.I18n;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n.LOCALE;\nimport org.bonitasoft.web.toolkit.client.common.i18n.T_;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * Created by Vincent Elcrin\n * Date: 23/09/13\n * Time: 18:12\n */\npublic class JsonExceptionSerializerTest {\n\n    private FakeI18n fakeI18n;\n\n    @Before\n    public void setUp() throws Exception {\n        fakeI18n = new FakeI18n();\n    }\n\n    @After\n    public void cleanUp() throws Exception {\n        I18n.setInstance(null);\n    }\n\n    @Test\n    public void testWeCanConvertExceptionWithoutMessage() throws Exception {\n        HttpException exception = new HttpException();\n        JsonExceptionSerializer serializer = new JsonExceptionSerializer(exception);\n\n        String json = serializer.end();\n\n        assertEquals(exception, json);\n    }\n\n    @Test\n    public void testWeCanConvertExceptionWithMessageToJson() throws Exception {\n        HttpException exception = new HttpException(\"message\");\n        JsonExceptionSerializer serializer = new JsonExceptionSerializer(exception);\n\n        String json = serializer.end();\n\n        assertEquals(exception, json);\n    }\n\n    @Test\n    public void testWeCanAppendNewAttributeBeforeEndCall() throws Exception {\n        HttpException exception = new HttpException();\n        JsonExceptionSerializer serializer = new JsonExceptionSerializer(exception);\n\n        String json = serializer\n                .appendAttribute(\"attributeKey\", \"attributeValue\")\n                .end();\n\n        assertThat(json, equalTo(\n                \"{\\\"exception\\\":\\\"\" + exception.getClass().toString() + \"\\\",\" +\n                        \"\\\"message\\\":\\\"\" + exception.getMessage() + \"\\\",\" +\n                        \"\\\"attributeKey\\\":\\\"attributeValue\\\"}\"));\n    }\n\n    @Test\n    public void testJsonContainsInternationalizedMessageWhenLocalIsSet() throws Exception {\n        APIException exception = new APIException(new T_(\"message\"));\n        fakeI18n.setL10n(\"localization\");\n        exception.setLocale(LOCALE.en);\n        JsonExceptionSerializer serializer = new JsonExceptionSerializer(exception);\n\n        String json = serializer.end();\n\n        assertThat(json, equalTo(\n                \"{\\\"exception\\\":\\\"\" + exception.getClass().toString() + \"\\\",\" +\n                        \"\\\"message\\\":\\\"localization\\\"\" + \"}\"));\n    }\n\n    private void assertEquals(Exception e, String json) {\n        assertThat(json, equalTo(\"{\\\"exception\\\":\\\"\" + e.getClass().toString() + \"\\\",\" +\n                \"\\\"message\\\":\\\"\" + e.getMessage() + \"\\\"}\"));\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/toolkit/client/common/json/JSonItemReaderTest.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.json;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.web.rest.model.application.AbstractApplicationDefinition;\nimport org.bonitasoft.web.rest.model.application.ApplicationItem;\nimport org.bonitasoft.web.rest.model.application.ApplicationLinkItem;\nimport org.junit.Test;\n\npublic class JSonItemReaderTest {\n\n    @Test\n    public void should_parse_an_implicit_LegacyApplication_from_AbstractApplicationDefinition() throws Exception {\n        // given\n        String json = \"\"\"\n                {\n                  \"version\": \"1.0\",\n                  \"profileId\": \"2\",\n                  \"token\": \"myapp\",\n                  \"displayName\": \"My app\",\n                  \"description\": \"My application description\"\n                }\n                \"\"\";\n        // when\n        var app = JSonItemReader.parseItem(json, AbstractApplicationDefinition.get());\n        // then\n        assertThat(app).isExactlyInstanceOf(ApplicationItem.class);\n    }\n\n    @Test\n    public void should_parse_an_explicit_LegacyApplication_from_AbstractApplicationDefinition() throws Exception {\n        // given\n        String json = \"\"\"\n                {\n                  \"link\": \"false\",\n                  \"version\": \"1.0\",\n                  \"profileId\": \"2\",\n                  \"token\": \"myapp\",\n                  \"displayName\": \"My app\",\n                  \"description\": \"My application description\"\n                }\n                \"\"\";\n        // when\n        var app = JSonItemReader.parseItem(json, AbstractApplicationDefinition.get());\n        // then\n        assertThat(app).isExactlyInstanceOf(ApplicationItem.class);\n    }\n\n    @Test\n    public void should_parse_an_ApplicationLink_from_AbstractApplicationDefinition() throws Exception {\n        // given\n        String json = \"\"\"\n                {\n                  \"link\": \"true\",\n                  \"version\": \"1.0\",\n                  \"profileId\": \"2\",\n                  \"token\": \"myapp\",\n                  \"displayName\": \"My app\",\n                  \"description\": \"My application description\"\n                }\n                \"\"\";\n        // when\n        var app = JSonItemReader.parseItem(json, AbstractApplicationDefinition.get());\n        // then\n        assertThat(app).isExactlyInstanceOf(ApplicationLinkItem.class);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/toolkit/client/common/json/JSonSerializerTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.json;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.Test;\n\npublic class JSonSerializerTest {\n\n    @Test\n    public void should_serialize_an_exception() throws Exception {\n        Exception exception = new Exception(\"an exception\");\n\n        String serialize = JSonSerializer.serialize(exception);\n\n        assertThat(serialize).isEqualTo(\"{\\\"exception\\\":\\\"class java.lang.Exception\\\",\\\"message\\\":\\\"an exception\\\"}\");\n    }\n\n    @Test\n    public void should_serialize_only_first_cause_of_an_exception() throws Exception {\n        Exception exception = new Exception(\"first one\", new Exception(\"second one\", new Exception(\"third one\")));\n\n        String serialize = JSonSerializer.serialize(exception);\n\n        assertThat(serialize).isEqualTo(\n                \"{\\\"exception\\\":\\\"class java.lang.Exception\\\",\"\n                        + \"\\\"message\\\":\\\"first one\\\",\"\n                        + \"\\\"cause\\\":{\"\n                        + \"\\\"exception\\\":\\\"class java.lang.Exception\\\",\"\n                        + \"\\\"message\\\":\\\"second one\\\"\"\n                        + \"}\"\n                        + \"}\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/toolkit/client/common/json/JSonUtilTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.json;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.web.toolkit.client.common.json.JSonUtil.escape;\n\nimport org.junit.Test;\n\npublic class JSonUtilTest {\n\n    @Test\n    public void should_escape_unsafe_html_characters() {\n        assertThat(escape(\"<script>alert('bad')</script>\"))\n                .isEqualTo(\"\\\\u003cscript\\\\u003ealert(\\\\u0027bad\\\\u0027)\\\\u003c\\\\/script\\\\u003e\");\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/toolkit/client/common/texttemplate/TextTemplateTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.common.texttemplate;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.junit.Test;\n\npublic class TextTemplateTest {\n\n    public static final String BASE_TEXT = \"This is a text template made by %the_user% \\n\" +\n            \"It has %multiple_vars% with even single %\\n\" +\n            \"and also %vars with spaces@@%\\n\" +\n            \"and %missing vars%\";\n\n    @Test\n    public void should_find_expected_parameters_of_text_template() {\n        TextTemplate textTemplate = new TextTemplate(BASE_TEXT);\n\n        List<String> expectedParameters = textTemplate.getExpectedParameters();\n\n        assertThat(expectedParameters).containsExactly(\n                \"the_user\",\n                \"multiple_vars\",\n                \"vars with spaces@@\",\n                \"missing vars\");\n    }\n\n    @Test\n    public void should_replace_parameters_of_text_template() {\n        TextTemplate textTemplate = new TextTemplate(BASE_TEXT);\n\n        Map<String, String> parameters = new HashMap<>();\n        parameters.put(\"the_user\", \"Walter bates\");\n        parameters.put(\"multiple_vars\", \"Multiple variables to replace\");\n        parameters.put(\"vars with spaces@@\", \"Variables With all kind of special \\nchars and space: @#%ˆ&*(%%%\");\n        String output = textTemplate.toString(parameters);\n\n        assertThat(output).isEqualTo(\"This is a text template made by Walter bates \\n\" +\n                \"It has Multiple variables to replace with even single %\\n\" +\n                \"and also Variables With all kind of special \\nchars and space: @#%ˆ&*(%%%\\n\" +\n                \"and %missing vars%\");\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/toolkit/client/data/APIIDTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIItemIdMalformedException;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\n\n/**\n * @author Dumitru Corini\n */\npublic class APIIDTest {\n\n    @Rule\n    public ExpectedException thrown = ExpectedException.none();\n\n    @Test\n    public void makeAPIID_with_list_should_return_correct_result() throws Exception {\n        List<String> ids = new ArrayList<>();\n        ids.add(\"1\");\n        APIID result = APIID.makeAPIID(ids);\n        assertThat(result.toLong()).isEqualTo(1L);\n\n        ids = new ArrayList<>();\n        ids.add(\"1\");\n        ids.add(\"6\");\n        ids.add(\"-1\");\n        result = APIID.makeAPIID(ids);\n        assertThat(result.getIds()).isEqualTo(ids);\n\n        ids = new ArrayList<>();\n        ids.add(\"\");\n        ids.add(\"1\");\n        ids.add(\"-1\");\n        ids.add(\"instantiation\");\n        ids.add(null);\n        result = APIID.makeAPIID(ids);\n        assertThat(result.getIds()).isEqualTo(ids);\n    }\n\n    @Test\n    public void makeAPIID_with_array_should_return_correct_result() throws Exception {\n        Long[] ids = new Long[] { 1L };\n        APIID result = APIID.makeAPIID(ids);\n        assertThat(result.toLong()).isEqualTo(1L);\n\n        ids = new Long[] { 1L, null, 6L, -1L };\n        List<String> resultingAPIIDs = new ArrayList<>();\n        resultingAPIIDs.add(\"1\");\n        resultingAPIIDs.add(null);\n        resultingAPIIDs.add(\"6\");\n        resultingAPIIDs.add(\"-1\");\n        result = APIID.makeAPIID(ids);\n        assertThat(result.getIds()).isEqualTo(resultingAPIIDs);\n    }\n\n    @Test(expected = APIItemIdMalformedException.class)\n    public void toLong_should_throw_APIItemIdMalformedException_with_string() {\n        APIID.makeAPIID(\"undefined\").toLong();\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/StringFormatURLValidatorTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.client.data.item.attribute.validator;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.console.common.server.i18n.I18n;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class StringFormatURLValidatorTest {\n\n    private StringFormatURLValidator stringFormatURLValidator = new StringFormatURLValidator();\n\n    @Before\n    public void before() {\n        // to initialize the system:\n        I18n.getInstance();\n    }\n\n    @Test\n    public void should_verify_urls() {\n        checkUrl(\"toto\", true);\n        checkUrl(\"www.toto\", false);\n        checkUrl(\"https://toto\", false);\n        checkUrl(\"ftp://toto\", false);\n        checkUrl(\"http://toto\", false);\n        checkUrl(\"http://toto?tata.titi=tutu\", false);\n    }\n\n    private void checkUrl(String url, boolean shouldHaveErrors) throws AssertionError {\n        stringFormatURLValidator.reset();\n        stringFormatURLValidator._check(url);\n        assertThat(stringFormatURLValidator.getErrors().isEmpty()).isEqualTo(!shouldHaveErrors);\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/java/org/bonitasoft/web/toolkit/server/servlet/ToolkitHttpServletTest.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.web.toolkit.server.servlet;\n\nimport static org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n.LOCALE;\nimport static org.mockito.Mockito.*;\nimport static org.mockito.MockitoAnnotations.initMocks;\n\nimport java.io.PrintWriter;\nimport java.util.HashMap;\nimport java.util.Locale;\n\nimport javax.servlet.http.Cookie;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.bonitasoft.console.common.server.i18n.I18n;\nimport org.bonitasoft.console.common.server.utils.LocaleUtils;\nimport org.bonitasoft.web.toolkit.client.common.exception.api.APIException;\nimport org.bonitasoft.web.toolkit.server.ServletCall;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\nimport org.mockito.Mockito;\n\n/**\n * Created by Vincent Elcrin\n * Date: 23/09/13\n * Time: 16:50\n */\npublic class ToolkitHttpServletTest {\n\n    ToolkitHttpServlet toolkitHttpServlet = new ToolkitHttpServlet() {\n\n        @Override\n        protected ServletCall defineServletCall(HttpServletRequest req, HttpServletResponse resp) {\n            return null;\n        }\n    };\n\n    @Mock\n    HttpServletRequest req;\n\n    @Mock\n    HttpServletResponse resp;\n\n    @Mock\n    PrintWriter writer;\n\n    @Before\n    public void setUp() throws Exception {\n        initMocks(this);\n\n        HashMap<String, String> availableLocales;\n        availableLocales = new HashMap<>();\n        availableLocales.put(\"en\", \"English\");\n        availableLocales.put(\"fr\", \"Français\");\n        availableLocales.put(\"es\", \"Español\");\n        availableLocales.put(\"pt_BR\", \"Português (Brasil)\");\n        availableLocales.put(\"ja\", \"日本語\");\n\n        I18n i18n = mock(I18n.class);\n        I18n.setInstance(i18n);\n        Mockito.when(i18n.getAvailableLocalesFor(anyString())).thenReturn(availableLocales);\n    }\n\n    @After\n    public void cleanUp() throws Exception {\n        I18n.setInstance(null);\n    }\n\n    @Test\n    public void testOutputExceptionPrintProperJson() throws Exception {\n        APIException exception = new APIException(\"message\");\n        doReturn(writer).when(resp).getWriter();\n\n        toolkitHttpServlet.outputException(exception, req, resp, 500);\n\n        verify(writer).print(exception.toJson());\n    }\n\n    @Test\n    public void testLocaleIsPassedFromRequestToException() throws Exception {\n        APIException exception = mock(APIException.class,\n                withSettings().defaultAnswer(RETURNS_MOCKS));\n\n        doReturn(writer).when(resp).getWriter();\n        doReturn(new Cookie[] {\n                new Cookie(LocaleUtils.LOCALE_COOKIE_NAME, \"fr_FR\")\n        }).when(req).getCookies();\n\n        toolkitHttpServlet.outputException(exception, req, resp, 500);\n\n        verify(exception).setLocale(LOCALE.fr);\n    }\n\n    @Test\n    public void testIfLocaleIsNotInACookieThatBrowserLocaleIsPassedThrough() throws Exception {\n        APIException exception = mock(APIException.class,\n                withSettings().defaultAnswer(RETURNS_MOCKS));\n\n        doReturn(writer).when(resp).getWriter();\n        doReturn(new Cookie[0]).when(req).getCookies();\n        doReturn(Locale.CANADA_FRENCH).when(req).getLocale();\n\n        toolkitHttpServlet.outputException(exception, req, resp, 500);\n\n        verify(exception).setLocale(LOCALE.fr);\n    }\n\n    @Test\n    public void testIfLocaleIsNotInACookieNorBrowserThatDefaultLocaleIsPassedThrough() throws Exception {\n        APIException exception = mock(APIException.class,\n                withSettings().defaultAnswer(RETURNS_MOCKS));\n\n        doReturn(writer).when(resp).getWriter();\n        doReturn(new Cookie[0]).when(req).getCookies();\n        doReturn(null).when(req).getLocale();\n\n        toolkitHttpServlet.outputException(exception, req, resp, 500);\n\n        verify(exception).setLocale(LOCALE.en);\n    }\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/ARootPageFolder/AbstractIndex.groovy",
    "content": "public class AbstractIndex {\n\n    public String name(){\n        return \"name\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/ARootPageFolder/Index.groovy",
    "content": "\npublic class Index extends AbstractIndex {\n\n    public void doGet() {\n\n        \n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/ARootPageFolder/lib/bdm-client.jar",
    "content": ""
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/ARootPageFolder/lib/bdm-dao.jar",
    "content": ""
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/ARootPageFolder/lib/javassist-3.18.1-GA.jar",
    "content": ""
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/ARootPageFolder/lib/subdir/resource.properties",
    "content": "key=value"
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/ARootPageFolder/lib/util.jar",
    "content": ""
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/ARootPageFolder/org/company/test/Util.groovy",
    "content": "package org.company.test;\n\npublic class Util {\n\n    public static String name(){\n        return \"My Name\";\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/ARootPageFolder/org/company/test/config.properties",
    "content": "aProperty=Value"
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/Index.groovy",
    "content": "package org.bonitasoft.test.page\n\nimport javax.servlet.http.HttpServletRequest\nimport javax.servlet.http.HttpServletResponse\nimport javax.servlet.http.HttpSession\n\nimport org.bonitasoft.engine.api.IdentityAPI\nimport org.bonitasoft.engine.api.TenantAPIAccessor\nimport org.bonitasoft.engine.identity.User\nimport org.bonitasoft.web.extension.page.PageContext\nimport org.bonitasoft.web.extension.page.PageController\nimport org.bonitasoft.web.extension.page.PageResourceProvider\n\nclass Index implements PageController {\n\n    @Override\n    void doGet(HttpServletRequest request, HttpServletResponse response, PageResourceProvider pageResourceProvider, PageContext pageContext) {\n        try {\n            HttpSession session = request.getSession()\n            IdentityAPI identityAPI = TenantAPIAccessor.getIdentityAPI(pageContext.getApiSession())\n            User user = identityAPI.getUserByUserName((String)session.getAttribute(\"username\"))\n            PrintWriter out = response.getWriter()\n            out.write(\"<div class=\\\"user\\\">\")\n            out.write(\"<span>User details returned from Engine API Call using the existing API Session:</span>\")\n            out.write(user.toString())\n            out.write(\"</div>\")\n            out.flush()\n            out.close()\n        } catch (Exception e) {\n            e.printStackTrace()\n        }\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/IndexRestApi.groovy",
    "content": "package org.bonitasoft.test.page\n\nimport javax.servlet.http.HttpServletRequest\n\nimport org.bonitasoft.web.extension.rest.RestAPIContext\nimport org.bonitasoft.web.extension.rest.RestApiController\nimport org.bonitasoft.web.extension.rest.RestApiResponse\nimport org.bonitasoft.web.extension.rest.RestApiResponseBuilder\n\nclass IndexRestApi implements RestApiController {\n\n    @Override\n    RestApiResponse doHandle(HttpServletRequest request, RestApiResponseBuilder responseBuilder, RestAPIContext context) {\n        return responseBuilder.withResponse(\"result\").build()\n    }\n\n}\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/bdmDependencies/bdm-client.jar",
    "content": ""
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/bdmDependencies/bdm-dao.jar",
    "content": ""
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/bdmDependencies/javassist-3.18.1-GA.jar",
    "content": ""
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/compound-permissions-mapping.properties",
    "content": "processListingPage processVisualization\ncaseListingPage caseVisualizationWithTrailingSpace \ntaskListingPage [TaskVisualization, CaseVisualization]\n\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/custom-permissions-mapping.properties",
    "content": "profile|User=[ManageLooknFeel, ManageProfiles]\nprofile|HR\\u0020manager=[ManageProfiles]\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/custom_po_resource/added_custom_i18n_fr.po",
    "content": "msgid \"about\"\nmsgstr \"Copyright Bonitasoft 2016\"\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/custom_po_resource/test_es.po",
    "content": "msgid \"test key\"\nmsgstr \"valor de prueba en Espanol\"\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/custom_po_resource/test_fr.po",
    "content": "msgid \"test key\"\nmsgstr \"Valeur modifiée\"\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/i18n/other_translations_fr.po",
    "content": "msgid \"web site title\"\nmsgstr \"Bienvenue dans Bonita Portal 7+\"\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/i18n/portal_fr.po",
    "content": "# only here to make test org.bonitasoft.console.common.server.page.PageContextHelperTest work\nmsgid \"test key\"\nmsgstr \"test value\"\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/i18n/test_fr.po",
    "content": "msgid \"test key\"\nmsgstr \"Valeur de test en français\"\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/invalidCustomPage/descriptor.properties",
    "content": "contentType=theme"
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/invalidCustomPage/resources/style.css",
    "content": ""
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/invalidFormPage/page.properties",
    "content": "contentType=form"
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/invalidFormPage/resources/index.js",
    "content": ""
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/invalidThemePage/page.properties",
    "content": "contentType=theme"
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/invalidThemePage/resources/style.css",
    "content": ""
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/logback-test.xml",
    "content": "<configuration>\n\n\t<appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n\t\t<!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder\n\t\t\tby default -->\n\t\t<encoder>\n\t\t\t<pattern>|%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg%n</pattern>\n\t\t</encoder>\n\t</appender>\n\n\n\t<logger name=\"org.bonitasoft.web.rest.server.api.bpm.flownode\" level=\"INFO\" />\n\t<logger name=\"org.bonitasoft.console.common.server.utils\" level=\"DEBUG\" />\n\t<logger name=\"org.bonitasoft.web.rest.server.api.bpm.process\" level=\"INFO\" />\n\t<logger name=\"org.bonitasoft.web.rest.server.api.resource\" level=\"INFO\" />\n\t<logger name=\"org.bonitasoft.console.common.server.login.servlet\" level=\"TRACE\" />\n\t<logger name=\"org.bonitasoft.console.common.server.filter.SanitizerFilter\" level=\"DEBUG\" />\n\n\t<root level=\"WARN\">\n\t\t<appender-ref ref=\"STDOUT\" />\n\t</root>\n</configuration>"
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/myRestAPI-1.0.0-SNAPSHOT/page.properties",
    "content": "name=custompage_myRestAPI\ndisplayName=My REST API\ndescription=REST API to manage resourceName\ncontentType=apiExtension\n\napiExtensions=myRestAPI\n\nmyRestAPI.method=GET\nmyRestAPI.pathTemplate=myRestAPI\nmyRestAPI.className=com.compagny.rest.api.MyController\nmyRestAPI.permissions=myPermission\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/org/bonitasoft/console/common/server/page/file.css",
    "content": "html, body {\n margin: 0;\n padding: 0;\n}"
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/org/bonitasoft/console/common/server/page/my_html_page_v_6/index.html",
    "content": "<!DOCTYPE html>\n<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n  ~ Copyright (C) 2015 BonitaSoft S.A.\n  ~ BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n  ~ This library is free software; you can redistribute it and/or modify it under the terms\n  ~ of the GNU Lesser General Public License as published by the Free Software Foundation\n  ~ version 2.1 of the License.\n  ~ This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n  ~ without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n  ~ See the GNU Lesser General Public License for more details.\n  ~ You should have received a copy of the GNU Lesser General Public License along with this\n  ~ program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n  ~ Floor, Boston, MA 02110-1301, USA.\n  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->\n<html>\n<head lang=\"en\">\n    <meta charset=\"UTF-8\">\n    <title></title>\n</head>\n<body>\n\n</body>\n</html>"
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/org/bonitasoft/console/common/server/page/my_html_page_v_7/resources/index.html",
    "content": "<!DOCTYPE html>\n<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n  ~ Copyright (C) 2015 BonitaSoft S.A.\n  ~ BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n  ~ This library is free software; you can redistribute it and/or modify it under the terms\n  ~ of the GNU Lesser General Public License as published by the Free Software Foundation\n  ~ version 2.1 of the License.\n  ~ This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n  ~ without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n  ~ See the GNU Lesser General Public License for more details.\n  ~ You should have received a copy of the GNU Lesser General Public License along with this\n  ~ program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n  ~ Floor, Boston, MA 02110-1301, USA.\n  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->\n<html>\n<head lang=\"en\">\n    <meta charset=\"UTF-8\">\n    <title></title>\n</head>\n<body>\n\n</body>\n</html>"
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/org/bonitasoft/console/common/server/page/page.properties",
    "content": "#The name must start with 'custompage_'\nname=custompage_restApi\ndisplayName=aDisplayName\ndescription=aDescription\nresources=[]\n\ncontentType=apiExtension\n\napiExtensions=restResource1,restResource2\n\nrestResource1.method=GET\nrestResource1.pathTemplate=restApiGet\nrestResource1.classFileName=restResource1.groovy\nrestResource1.permissions=permission1\n\nrestResource2.method=POST\nrestResource2.pathTemplate=restApiPost\nrestResource2.classFileName=restResource1.groovy\nrestResource2.permissions=permission2,permission3\n\n\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/org/bonitasoft/web/rest/server/api/bpm/flownode/contract.json",
    "content": "{\n\t\"constraints\":[\n\t\t{\"name\":\"aRule\",\"expression\":\"an expression\",\"explanation\":\"an explanation\",\"inputNames\":[]}\n\t],\n\t\"inputs\":[\n\t\t{\"description\":\"aDescription\",\"name\":\"anInput\",\"multiple\":false,\"type\":\"TEXT\",\"inputs\":[]},\n\t\t{\"description\":\"description\",\"name\":\"complexInput\",\"multiple\":true,\"type\":null,\"inputs\":[\n\t\t\t{\"description\":\"aDescription\",\"name\":\"anInput\",\"multiple\":false,\"type\":\"TEXT\",\"inputs\":[]}\n\t\t]}\n\t]\n}\t"
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/org/bonitasoft/web/rest/server/api/bpm/flownode/timerEventTriggerInstance.json",
    "content": "{\n  \"executionDate\": 9223372036854775807\n}"
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/org/bonitasoft/web/rest/server/api/bpm/flownode/timerEventTriggerInstances.json",
    "content": "[\n  {\n    \"eventInstanceId\": 2,\n    \"eventInstanceId_string\": \"2\",\n    \"id\": 1,\n    \"id_string\": \"1\",\n    \"executionDate\": 123,\n    \"eventInstanceName\": \"name\"\n  }\n]\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/org/bonitasoft/web/rest/server/api/bpm/process/contract.json",
    "content": "{\n\t\"constraints\":[\n\t\t{\"name\":\"aRule\",\"expression\":\"an expression\",\"explanation\":\"an explanation\",\"inputNames\":[]}\n\t],\n\t\"inputs\":[\n\t\t{\"description\":\"aDescription\",\"name\":\"anInput\",\"multiple\":false,\"type\":\"TEXT\",\"inputs\":[]},\n\t\t{\"description\":\"description\",\"name\":\"complexInput\",\"multiple\":true,\"type\":null,\"inputs\":[\n\t\t\t{\"description\":\"aDescription\",\"name\":\"anInput\",\"multiple\":false,\"type\":\"TEXT\",\"inputs\":[]}\n\t\t]}\n\t]\n}"
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/org/bonitasoft/web/rest/server/api/extension/page.properties",
    "content": "# api extensions list\napiExtensions=myGetResource,myPostResource,MyPutResource,myDeleteResource,myPostResourceB,myCompiledRestApi,myCompiledRestApi2\n\n#http method\nmyGetResource.method=GET\n\n# resulting url wille be ../API/extension/helloWorld\nmyGetResource.pathTemplate=helloWorld\n\n# implementation file that will serve this resource\nmyGetResource.classFileName=Index.groovy\n\nmyPostResource.method=POST\nmyPostResource.pathTemplate=myPostResource\n# Empty className -> classFileName should be used\nmyPostResource.className=\nmyPostResource.classFileName=PostResource.groovy\n\nmyPostResourceB.method=POST\nmyPostResourceB.pathTemplate=myPostResourceB\nmyPostResourceB.classFileName=PostResourceB.groovy\n\nmyCompiledRestApi.method=GET\nmyCompiledRestApi.pathTemplate=myCompiledRestApi\nmyCompiledRestApi.className=com.company.MyController\n\nmyCompiledRestApi2.method=GET\nmyCompiledRestApi2.pathTemplate=myCompiledRestApi2\nmyCompiledRestApi2.className=com.company.MyController\nmyCompiledRestApi2.classFileName=MyController.groovy\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/resources-permissions-mapping.properties",
    "content": "GET|bpm/identity [UserVisualization, groupVisualization]\n"
  },
  {
    "path": "bpm/bonita-web-server/src/test/resources/test.po",
    "content": "msgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: bonita-bpm-60\\n\"\n\"POT-Creation-Date: 2013-06-07 14:58+0100\\n\"\n\"PO-Revision-Date: 2024-01-01 00:00\\n\"\n\"Last-Translator: \\n\"\n\"Language-Team: English\\n\"\n\"Language: en_US\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"X-Generator: Poedit 1.5.5\\n\"\n\"X-Poedit-KeywordsList: _;gettext;gettext_noop\\n\"\n\"X-Poedit-Basepath: .\\n\"\n\"X-Poedit-SourceCharset: UTF-8\\n\"\n\"Plural-Forms: nplurals=2; plural=(n != 1);\\n\"\n\"X-Poedit-SearchPath-0: common\\n\"\n\"X-Poedit-SearchPath-1: console\\n\"\n\"X-Poedit-SearchPath-2: platform\\n\"\n\"X-Poedit-SearchPath-3: toolkit\\n\"\n\n#: toolkit/toolkit-view/src/main/java/org/bonitasoft/web/toolkit/client/ui/page/SupportCodePage.java:42\nmsgid \"\"\n\"Multiline\\n\"\n\"Message\\n\"\n\"Id\"\nmsgstr \"\"\n\"This is a multilines\\n\"\n\"Message\"\n\n#: toolkit/toolkit-view/src/main/java/org/bonitasoft/web/toolkit/client/ui/page/SupportCodePage.java:43\nmsgid \"theId\"\nmsgstr \"theMsg\"\n"
  },
  {
    "path": "build.gradle",
    "content": "plugins {\n    id \"org.sonarqube\" version \"7.2.3.7755\"\n    alias(libs.plugins.taskInfo)\n    alias(libs.plugins.testRetry)\n}\n\nsonarqube {\n    properties {\n          property \"sonar.projectKey\", \"bonitasoft_bonita-engine\"\n          property \"sonar.organization\", \"bonitasoft\"\n          property \"sonar.host.url\", \"https://sonarcloud.io\"\n    }\n}\n\napply from: \"common.gradle\"\n"
  },
  {
    "path": "buildSrc/build.gradle",
    "content": "plugins {\n    id(\"groovy\")\n    id(\"java-gradle-plugin\")\n    id \"com.github.johnrengelman.shadow\" version \"8.1.1\"\n}\n\nrepositories {\n    mavenCentral()\n    gradlePluginPortal()\n}\n\ndependencies {\n    api \"com.github.johnrengelman:shadow:8.1.1\"\n    api \"com.adarshr:gradle-test-logger-plugin:3.2.0\"\n\n    // for database tests:\n    api \"com.bmuschko:gradle-docker-plugin:6.7.0\"\n}\n\ngradlePlugin {\n    plugins {\n        bonitaShade {\n            id = \"bonita-shade\"\n            implementationClass = \"org.bonitasoft.engine.gradle.ShadePlugin\"\n        }\n        bonitaTests {\n            // Add this plugin to every module that has *IT.java (and only those modules):\n            id = \"bonita-tests\"\n            implementationClass = \"org.bonitasoft.engine.gradle.TestsPlugin\"\n        }\n        bonitaHttpTests {\n            id = \"bonita-http-test\"\n            implementationClass = \"org.bonitasoft.engine.gradle.HttpTestPlugin\"\n        }\n        bonitaDatabaseTest {\n            id = \"bonita-docker-database\"\n            implementationClass = \"org.bonitasoft.engine.gradle.docker.DockerDatabasePlugin\"\n        }\n    }\n}"
  },
  {
    "path": "buildSrc/settings.gradle",
    "content": "dependencyResolutionManagement {\n    versionCatalogs {\n        libs {\n            from(files(\"../gradle/libs.versions.toml\"))\n        }\n    }\n}"
  },
  {
    "path": "buildSrc/src/main/groovy/org/bonitasoft/engine/gradle/HttpTestPlugin.groovy",
    "content": "package org.bonitasoft.engine.gradle\n\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.api.artifacts.VersionCatalogsExtension\nimport org.gradle.api.tasks.TaskProvider\nimport org.gradle.api.tasks.testing.Test\n\n/**\n * @author Emmanuel Duchastenier\n */\nclass HttpTestPlugin implements Plugin<Project> {\n\n    @Override\n    void apply(Project project) {\n\n        def httpTests = project.extensions.create(\"httpTests\", TestsExtension)\n\n        TaskProvider<Test> httpIntegrationTests = project.tasks.register(\"httpIT\", Test) {\n            group = \"Verification\"\n            description = \"Runs all integration tests to remote HTTP server.\"\n        }\n\n        project.afterEvaluate {\n            if (httpTests.integrationTestsSuite) {\n                httpIntegrationTests.configure { include(httpTests.integrationTestsSuite) }\n            } else {\n                // to be able to run only one class with right-click in IDE\n                // Still not possible for now, as *IT classes are not yet in src/test/java folder\n                httpIntegrationTests.configure { include(\"**/*IT.class\") }\n            }\n            // So that TestEngine start in HTTP instead of local:\n            httpIntegrationTests.configure { jvmArgs(\"-Dorg.bonitasoft.engine.access.mode=http\") }\n\n            // Add HTTP server dependencies only for this task:\n            project.configurations {\n                httpTestConfig\n            }\n            project.dependencies {\n                def versionCatalog = project.extensions.getByType(VersionCatalogsExtension.class).named(\"libs\")\n                httpTestConfig(versionCatalog.findLibrary(\"jettyServer\").get())\n                httpTestConfig(versionCatalog.findLibrary(\"jettyServlet\").get())\n            }\n            httpIntegrationTests.configure { classpath += project.configurations.httpTestConfig }\n\n\n            JVMModifier.setTestJVM(project, httpIntegrationTests)\n            JVMModifier.setJvmArgs(project, httpIntegrationTests)\n        }\n    }\n\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/org/bonitasoft/engine/gradle/JVMModifier.groovy",
    "content": "package org.bonitasoft.engine.gradle\n\nimport org.gradle.api.Project\nimport org.gradle.api.tasks.TaskProvider\nimport org.gradle.api.tasks.testing.Test\nimport org.gradle.jvm.toolchain.JavaLanguageVersion\nimport org.gradle.jvm.toolchain.JavaToolchainService\n\n/**\n * @author Emmanuel Duchastenier\n */\nclass JVMModifier {\n\n    public static final String TEST_JVM_VERSION = \"test.jvm.version\"\n\n    static void setJvmArgs(Project project, TaskProvider<Test> task) {\n        ArrayList<String> jvmArgs = [\"-Dorg.bonitasoft.h2.database.dir=./build/h2databasedir\"]\n        def property = project.property('org.gradle.jvmargs')\n        if (property) {\n            jvmArgs.addAll property.toString().split(\" \")\n        }\n        def sysProperty = System.getProperty(\"org.gradle.jvmargs\")\n        if (sysProperty) {\n            jvmArgs.addAll sysProperty.split(\" \")\n        }\n        System.getProperties().each { p ->\n            if (p.key.contains('sysprop.bonita') || p.key.startsWith('bonita.runtime')) {\n                jvmArgs.add(\"-D${p.key}=${p.value}\")\n            }\n        }\n        project.logger.info(\"jvmArgs: $jvmArgs\")\n        task.configure { it.jvmArgs(jvmArgs) }\n    }\n\n    static void setTestJVM(Project project, TaskProvider<Test> task) {\n        if (project.hasProperty(TEST_JVM_VERSION)) {\n            def alternateJvm = project.property(TEST_JVM_VERSION)\n            project.logger.info(\"Parameter '$TEST_JVM_VERSION' detected...\")\n\n            // to work around error \"Toolchain from `executable` property does not match toolchain from `javaLauncher` property\",\n            // when upgrading to Gradle 8:\n            JavaToolchainService service = project.getExtensions().getByType(JavaToolchainService.class);\n            task.configure { javaLauncher.set(service.launcherFor { languageVersion = JavaLanguageVersion.of(alternateJvm as int) }) }\n            project.logger.info(\"${project.name} will use alternate JVM '$alternateJvm' (${task.configure { javaLauncher.get().executablePath.asFile.absolutePath }}) to run $task\")\n        }\n    }\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/org/bonitasoft/engine/gradle/PomUtils.groovy",
    "content": "package org.bonitasoft.engine.gradle\n\n\nimport org.gradle.api.publish.maven.MavenPom\n\n/**\n * Utility class to generate Community information necessary to publish to\n * Maven Central.\n *\n * @author Emmanuel Duchastenier\n */\nclass PomUtils {\n\n    static void pomCommunityPublication(MavenPom pom) {\n        pom.with {\n            url = 'https://community.bonitasoft.com/'\n            organization {\n                name = 'Bonitasoft S.A.'\n                url = 'https://community.bonitasoft.com/'\n            }\n            developers {\n                developer {\n                    id = \"bonita-engine-team\"\n                    name = \"The Bonita Engine Development Team\"\n                    organization = \"Bonitasoft S.A.\"\n                    organizationUrl = \"http://community.bonitasoft.com/\"\n                }\n            }\n            scm {\n                connection = \"scm:git:http://github.com/bonitasoft/bonita-engine.git\"\n                developerConnection = \"scm:git:git@github.com:bonitasoft/bonita-engine.git\"\n                url = \"http://github.com/bonitasoft/bonita-engine\"\n            }\n            licenses {\n                license {\n                    name = 'GNU Lesser General Public License Version 2.1'\n                    url = 'http://www.gnu.org/licenses/lgpl-2.1.html'\n                    distribution = 'repo'\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/org/bonitasoft/engine/gradle/SetupE2ETask.groovy",
    "content": "package org.bonitasoft.engine.gradle\n\nimport org.gradle.api.tasks.*\nimport org.gradle.internal.os.OperatingSystem\n\nimport static org.gradle.api.tasks.PathSensitivity.NONE\n\n@CacheableTask\nclass SetupE2ETask extends Exec {\n\n    @PathSensitive(NONE)\n    @InputFile\n    File scriptFile\n\n    @OutputFile\n    File outputFile = project.layout.buildDirectory.file(\"setup-e2e-output.log\").get().asFile\n\n    SetupE2ETask() {\n        onlyIf { OperatingSystem.current().isLinux() }\n    }\n\n    @TaskAction\n    @Override\n    void exec() {\n        commandLine 'sh', scriptFile.path\n        // Ensure the parent directory of the output file exists\n        outputFile.parentFile.mkdirs()\n        // Redirect standard output to a file\n        standardOutput = new FileOutputStream(outputFile)\n        super.exec()\n    }\n\n}"
  },
  {
    "path": "buildSrc/src/main/groovy/org/bonitasoft/engine/gradle/ShadeDependency.groovy",
    "content": "package org.bonitasoft.engine.gradle\n\nimport groovy.transform.EqualsAndHashCode\nimport groovy.transform.ToString\n\n@ToString\n@EqualsAndHashCode\nclass ShadeDependency {\n\n    String group\n    String name\n\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/org/bonitasoft/engine/gradle/ShadeExtension.groovy",
    "content": "package org.bonitasoft.engine.gradle\n\nimport org.gradle.api.Project\n\nclass ShadeExtension {\n\n    List<ShadeDependency> includes = []\n    List<Project> excludes = []\n    Map<String, List<ShadeDependency>> libExclusions = [:] as Map\n\n    def include(Map<String, String> artifact) {\n        includes.add(new ShadeDependency(artifact))\n    }\n\n    def exclude(Project project) {\n        excludes.add(project)\n    }\n\n    def excludeLibs(String refereeLib, ShadeDependency... libsToExclude) {\n        libExclusions.put(refereeLib, Arrays.asList(libsToExclude))\n    }\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/org/bonitasoft/engine/gradle/ShadePlugin.groovy",
    "content": "package org.bonitasoft.engine.gradle\n\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.api.artifacts.ResolvedDependency\nimport org.gradle.api.artifacts.component.ProjectComponentIdentifier\nimport org.gradle.api.publish.maven.MavenPublication\nimport org.gradle.jvm.tasks.Jar\n\nclass ShadePlugin implements Plugin<Project> {\n\n    private static final String PLATFORM_CONFIGURATION_NAME = \"platform-runtime\"\n\n    @Override\n    void apply(Project project) {\n        project.plugins.apply(\"com.github.johnrengelman.shadow\")\n        project.plugins.apply(\"maven-publish\")\n\n        def extension = project.extensions.create(\"shade\", ShadeExtension)\n\n        project.jar {\n            archiveClassifier = 'original'\n        }\n\n        project.afterEvaluate {\n            project.shadowJar {\n                archiveClassifier = \"\" // we replace the original jar by the shadow jar\n                dependencies {\n                    include({\n                        if (!project.ext.has(\"shadedDependencies\")) {\n                            // \"shadedDependencies\" property is used for display only\n                            project.ext.shadedDependencies = [] as Set\n                        }\n                        def allProjectsAlreadyShaded = getProjectsAlreadyShaded(project, extension)\n                        if (shouldBeIncludedInShade(project, it, extension, allProjectsAlreadyShaded)) {\n                            project.ext.shadedDependencies.add(it)\n                            return true\n                        }\n                        return false\n                    })\n                }\n                doFirst {\n                    project.logger.info(\"Shading for project {} : \", project.path)\n                    project.ext.projectsToShade.each {\n                        project.logger.info(\" - {}\", it)\n                    }\n                    project.logger.info(\"Effective shading of project {} : \", project.path)\n                    project.ext.shadedDependencies.each {\n                        project.logger.info(\" - {}\", it)\n                    }\n\n                }\n            }\n            // not mandatory. Only here to have the production jars built when running `gradle build`:\n            project.tasks.named(\"build\") {\n                dependsOn project.tasks.named(\"shadowJar\")\n            }\n\n            project.javadoc {\n                source {\n                    getShadedProjects(project, extension, getProjectsAlreadyShaded(project, extension)).collect {\n                        it.sourceSets.main.allJava\n                    }\n                }\n                classpath = project.files({\n                    getShadedProjects(project, extension, getProjectsAlreadyShaded(project, extension)).collect {\n                        it.sourceSets.main.compileClasspath\n                    }\n                })\n                options.addStringOption('Xdoclint:none', '-quiet')\n                options.addBooleanOption(\"author\", true)\n                // FIXME update studio test org.bonitasoft.studio.tests.engine.TestJavaDoc.testHasJavaDoc\n            }\n            project.tasks.register(\"sourcesJar\", Jar) {\n                from {\n                    getShadedProjects(project, extension, getProjectsAlreadyShaded(project, extension)).collect {\n                        it.sourceSets.main.allJava\n                    }\n                }\n                archiveClassifier = 'sources'\n            }\n            project.tasks.register(\"javadocJar\", Jar) {\n                from project.javadoc\n                archiveClassifier = 'javadoc'\n            }\n\n            project.publishing.publications {\n                shadow(MavenPublication) { publication ->\n                    project.shadow.component(publication)\n                    PomUtils.pomCommunityPublication(publication.getPom())\n                    pom.withXml {\n                        def allProjectsAlreadyShaded = getProjectsAlreadyShaded(project, extension)\n                        Set<ResolvedDependency> inPom = getPomDependencies(project, extension, allProjectsAlreadyShaded, true)\n                        project.logger.info(\"Include in pom:\")\n                        inPom.each {\n                            project.logger.info(\" - {}\", it)\n                        }\n                        def rootNode = asNode()\n                        Node dependencies = rootNode.children().find { Node child -> child.name() == \"dependencies\" }\n                        inPom.each { gradleDep ->\n                            Node dependency =\n                                    dependencies\n                                            .appendNode(\"dependency\")\n                            dependency.appendNode(\"groupId\", gradleDep.moduleGroup)\n                            dependency.appendNode(\"artifactId\", gradleDep.moduleName)\n                            dependency.appendNode(\"version\", gradleDep.moduleVersion)\n                            if (extension.libExclusions.containsKey(gradleDep.moduleName)) {\n                                Node es = dependency.appendNode('exclusions')\n                                List<ShadeDependency> excludes = extension.libExclusions.get(gradleDep.moduleName)\n                                excludes.each {\n                                    Node e = es.appendNode('exclusion')\n                                    e.appendNode('groupId', it.group)\n                                    e.appendNode('artifactId', it.name)\n                                }\n                            }\n                        }\n                    }\n                    artifact project.sourcesJar\n                    artifact project.javadocJar\n                }\n            }\n        }\n    }\n\n    private boolean shouldBeIncludedInShade(Project project, ResolvedDependency currentDependency, ShadeExtension extension, Set<Project> allProjectsAlreadyShaded) {\n        Set<Project> projectsToShade = getShadedProjects(project, extension, allProjectsAlreadyShaded)\n        if (extension.includes.contains(new ShadeDependency(group: currentDependency.moduleGroup, name: currentDependency.moduleName))) {\n            return true\n        }\n        def projectDep = getAssociatedProjectFromDependency(project, currentDependency)\n        if (projectDep == null) {\n            return false // dependency is not a project\n        }\n        return projectsToShade.contains(projectDep)\n    }\n\n    /**\n     *  get the list of projects to shade\n     */\n    private Set<Project> getShadedProjects(Project project, ShadeExtension extension, Set<Project> allProjectsAlreadyShaded) {\n        if (!project.ext.has(\"projectsToShade\")) {\n            project.ext.projectsToShade = getAllProjectsToShade(project, extension, allProjectsAlreadyShaded)\n        }\n        project.ext.projectsToShade\n    }\n\n    private void printTree(Project project, String indentation) {\n        project.configurations.compile.resolvedConfiguration.firstLevelModuleDependencies.each {\n            def dependencyAsProject = getAssociatedProjectFromDependency(project, it)\n            if (dependencyAsProject) {\n                println indentation + dependencyAsProject.name\n                printTree(dependencyAsProject, indentation + \"--\")\n            }\n        }\n    }\n\n    /**\n     *  get the list of project that are already shaded by other shade\n     */\n    private Set<Project> getProjectsAlreadyShaded(Project rootProject, ShadeExtension extension) {\n        if (!rootProject.ext.has(\"projectsAlreadyShaded\")) { // add property \"projectsAlreadyShaded\" to act like a cache\n            rootProject.ext.projectsAlreadyShaded = getAllProjectsAlreadyShaded(rootProject, extension)\n        }\n        rootProject.ext.projectsAlreadyShaded\n    }\n\n    /**\n     * get the Project (object) from the ResolvedDependency (object)\n     */\n    private Project getAssociatedProjectFromDependency(Project project, ResolvedDependency dependency) {\n        def artifacts = dependency.getModuleArtifacts()\n        if (artifacts.isEmpty()) {\n            //it happens when a dependency is a bom pulled from gradle's artifacts metadata (variant)\n            return null\n        }\n        def identifier = artifacts.first().id.componentIdentifier\n        if (!(identifier instanceof ProjectComponentIdentifier)) {\n            return null\n        }\n        return project.project(identifier.projectPath)\n    }\n\n    private boolean isAShadeProject(Project it) {\n        it.plugins.find { it instanceof ShadePlugin }\n    }\n\n    private Set<Project> getAllProjectsToShade(Project project, ShadeExtension extension, Set<Project> allProjectsAlreadyShaded) {\n        Set allProjects = []\n        project.configurations.runtimeClasspath.resolvedConfiguration.firstLevelModuleDependencies.forEach {\n            def projectDependency = getAssociatedProjectFromDependency(project, it)\n            if (projectDependency) { // is an engine project (service)\n                if (!isAShadeProject(projectDependency) && !extension.excludes.contains(projectDependency) && !allProjectsAlreadyShaded.contains(projectDependency)) {\n                    allProjects.add(projectDependency)\n                    allProjects.addAll(getAllProjectsToShade(projectDependency, extension, allProjectsAlreadyShaded))\n                }\n            }\n        }\n        allProjects\n    }\n\n    private Set<Project> getAllProjectsAlreadyShaded(Project project, ShadeExtension extension) {\n        Set allProjects = []\n        // Take all declared compilation dependencies:\n        project.configurations.runtimeClasspath.resolvedConfiguration.firstLevelModuleDependencies.forEach {\n            def projectDependency = getAssociatedProjectFromDependency(project, it)\n            if (projectDependency) {\n                if (isAShadeProject(projectDependency)) { // this dependency is a shade project\n                    //all dependencies of this shade project are already shaded, let's add them all to the list:\n                    allProjects.addAll(getAllProjectsToShade(projectDependency, extension, [] as Set))\n                } else {\n                    allProjects.addAll(getAllProjectsAlreadyShaded(projectDependency, extension))\n                }\n            }\n        }\n        allProjects\n    }\n\n    private Set<ResolvedDependency> getPomDependencies(Project project, ShadeExtension extension, Set<Project> allProjectsAlreadyShaded, boolean isRootProject) {\n        Set<ResolvedDependency> allDependencies = []\n        def allScopes = project.configurations.runtimeClasspath.resolvedConfiguration.firstLevelModuleDependencies\n        allScopes.forEach {\n            Project projectDependency = getAssociatedProjectFromDependency(project, it)\n            if (projectDependency) {\n                if (allProjectsAlreadyShaded.contains(projectDependency)) {\n                    return // no need to go further\n                }\n                if (extension.excludes.contains(projectDependency)) {\n                    //excluded from shade, add this project but NOT its dependencies:\n                    allDependencies.add(it)\n                } else if (isAShadeProject(projectDependency)) {\n                    // the project is a shaded project (e.g. bonita-common-sp in bonita-server-sp)\n                    // only add it if it is not a shade pulled by transitivity:\n                    if (isRootProject) {\n                        allDependencies.add(it)\n                    } else {\n                        project.logger.info(\" Shade POM generation: ignoring {}, as it is pulled as transitive shade project\", projectDependency.name)\n                    }\n                } else {\n                    // do not add it: project is shaded inside this project\n                    allDependencies.addAll(getPomDependencies(projectDependency, extension, allProjectsAlreadyShaded, false))\n                }\n            } else {\n                // also add transitive dependencies of third-party libs:\n                allDependencies.addAll(getTransitiveThirdPartyDependencies(it, \"\", project, extension))\n            }\n        }\n        // remove all dependencies that are in the configuration \"platform-runtime\". it's used by gradle to resolve versions (like a bom)\n        // those dependencies should be imported in the dependency management instead. It is manually done right now (see `bonita-engine` bom)\n        allDependencies.findAll { it.configuration != PLATFORM_CONFIGURATION_NAME }\n    }\n\n    /**\n     * Returns a Set of the passed ResolvedDependency itself + all its children, recursively\n     * @param indent indentation string, for display purposes\n     */\n    private Set<ResolvedDependency> getTransitiveThirdPartyDependencies(ResolvedDependency current, String indent, Project project, ShadeExtension extension) {\n        def res = [] as Set\n        project.logger.debug(\" Shade POM generation: adding ${indent}${current.name}\")\n        // if the external dependency is shaded, do not add it in the pom, but add its dependencies\n        if (!extension.includes.contains(new ShadeDependency(group: current.moduleGroup, name: current.moduleName))) {\n            res.add(current)\n        }\n        def thirdPartyExclusion = extension.libExclusions.get(current.moduleName)\n        current.getChildren().forEach { child ->\n            if (!thirdPartyExclusion || !thirdPartyExclusion.contains(new ShadeDependency(group: child.moduleGroup, name: child.moduleName))) {\n                res.addAll(getTransitiveThirdPartyDependencies(child, indent + \"  \", project, extension))\n            }\n        }\n        return res\n    }\n\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/org/bonitasoft/engine/gradle/TestsExtension.groovy",
    "content": "package org.bonitasoft.engine.gradle\n\nclass TestsExtension {\n    String testPattern = \"**/*Test.class\"\n    String integrationTestsPattern = \"**/*IT.class\"\n    String integrationTestsSuite\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/org/bonitasoft/engine/gradle/TestsPlugin.groovy",
    "content": "package org.bonitasoft.engine.gradle\n\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.api.tasks.TaskProvider\nimport org.gradle.api.tasks.testing.Test\n\nclass TestsPlugin implements Plugin<Project> {\n\n    @Override\n    void apply(Project project) {\n\n        def tests = project.extensions.create(\"tests\", TestsExtension)\n\n        TaskProvider<Test> integrationTest = project.tasks.register(\"integrationTest\", Test) {\n            group = \"Verification\"\n            description = \"Runs all integration tests on H2 database.\"\n        }\n\n        project.afterEvaluate {\n            JVMModifier.setTestJVM(project, integrationTest)\n            JVMModifier.setJvmArgs(project, integrationTest)\n            if (tests.integrationTestsSuite) {\n                integrationTest.configure { include(tests.integrationTestsSuite) }\n            } else {\n                integrationTest.configure { include(tests.integrationTestsPattern) }\n            }\n            integrationTest.configure { systemProperty(\"bonita.version\", project.version) }\n\n            TaskProvider<Test> testTask = project.tasks.named(\"test\", Test)\n            if (testTask) {\n                JVMModifier.setTestJVM(project, testTask)\n                JVMModifier.setJvmArgs(project, testTask)\n                testTask.configure { include(tests.testPattern) }\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/org/bonitasoft/engine/gradle/docker/DatabaseExtraConfiguration.groovy",
    "content": "package org.bonitasoft.engine.gradle.docker\n\nimport groovy.transform.Canonical\nimport org.gradle.api.Project\n\n@Canonical\nclass DatabaseExtraConfiguration {\n    /**\n     * Include an additional project in the test classpath\n     */\n    Project includeTestProject\n    /**\n     * Excludes test class patterns (e.g. '**&#47;*Test.class') applied to this database vendor.\n     * It can be combined with {@link #excludeTags}.\n     */\n    List<String> excludes\n    /**\n     * Excludes tests marked by JUnit tags (e.g. 'my-tag') applied to this database vendor.\n     * It can be combined with {@link #excludes}.\n     */\n    List<String> excludeTags\n    /**\n     * Enable or disable the execution of the test task for this database configuration\n     */\n    boolean enabled = false\n\n    def excludes(String... excludes) {\n        this.excludes = []\n        this.excludes.addAll(excludes)\n    }\n\n    def exclude(String excludes) {\n        if (this.excludes == null) {\n            this.excludes = []\n        }\n        this.excludes.add(excludes)\n    }\n\n    def excludeTags(String... tags) {\n        this.excludeTags = []\n        this.excludeTags.addAll(tags)\n    }\n\n    def excludeTag(String tag) {\n        if (this.excludeTags == null) {\n            this.excludeTags = []\n        }\n        this.excludeTags.add(tag)\n    }\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/org/bonitasoft/engine/gradle/docker/DatabasePluginExtension.groovy",
    "content": "/**\n * Copyright (C) 2015 Bonitasoft S.A.\n * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.gradle.docker\n\nimport org.gradle.api.Action\n\nclass DatabasePluginExtension {\n\n    /**\n     * Include test class patterns applied to database vendors\n     */\n    List<String> includes\n    /**\n     * Exclude test class patterns applied to all database vendors\n     */\n    List<String> excludes\n    /**\n     * Extra configuration for the postgres database\n     */\n    DatabaseExtraConfiguration postgres = new DatabaseExtraConfiguration(enabled: true)\n    /**\n     * Extra configuration for the mysql database\n     */\n    DatabaseExtraConfiguration mysql = new DatabaseExtraConfiguration()\n    /**\n     * Extra configuration for the oracle database\n     */\n    DatabaseExtraConfiguration oracle = new DatabaseExtraConfiguration()\n    /**\n     * Extra configuration for the sqlserver database\n     */\n    DatabaseExtraConfiguration sqlserver = new DatabaseExtraConfiguration()\n\n    def includes(String... includes) {\n        this.includes = []\n        this.includes.addAll(includes)\n    }\n\n    def include(String include) {\n        if (this.includes == null) {\n            this.includes = []\n        }\n        this.includes.add(include)\n    }\n\n    def excludes(String... excludes) {\n        this.excludes = []\n        this.excludes.addAll(excludes)\n    }\n\n    def exclude(String exclude) {\n        if (this.excludes == null) {\n            this.excludes = []\n        }\n        this.excludes.add(exclude)\n    }\n\n    def postgres(Action<DatabaseExtraConfiguration> action) {\n        action.execute(postgres)\n    }\n\n    def mysql(Action<DatabaseExtraConfiguration> action) {\n        action.execute(mysql)\n    }\n\n    def oracle(Action<DatabaseExtraConfiguration> action) {\n        action.execute(oracle)\n    }\n\n    def sqlserver(Action<DatabaseExtraConfiguration> action) {\n        action.execute(sqlserver)\n    }\n\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/org/bonitasoft/engine/gradle/docker/DbParser.groovy",
    "content": "/**\n * Copyright (C) 2018 Bonitasoft S.A.\n * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.gradle.docker\n\nimport groovy.transform.EqualsAndHashCode\nimport groovy.transform.ToString\n\nclass DbParser {\n\n    static DbConnectionSettings extractDbConnectionSettings(String dburl) {\n        if (dburl.contains(\"sqlserver\")) {\n            return extractSqlServerDbConnectionSettings(dburl)\n        }\n        else if (dburl.contains(\"oracle\")) {\n            return extractOracleDbConnectionSettings(dburl)\n        }\n        return extractGenericDbConnectionSettings(dburl)\n    }\n\n    private static DbConnectionSettings extractGenericDbConnectionSettings(String dburl) {\n        DbConnectionSettings settings = new DbConnectionSettings()\n        settings.dbUrl = dburl\n\n        def parsedUrl = (dburl =~ /(jdbc:\\w+:\\/\\/)([\\w\\d\\.-]+):(\\d+)\\/([\\w\\-_\\d]+).*/)\n        settings.serverName = parsedUrl[0][2]\n        settings.portNumber = parsedUrl[0][3]\n        settings.databaseName = parsedUrl[0][4]\n        settings.genericUrl = parsedUrl[0][1] + settings.serverName + \":\" + settings.portNumber + \"/\"\n\n        settings\n    }\n\n    private static DbConnectionSettings extractOracleDbConnectionSettings(String dburl) {\n        DbConnectionSettings settings = new DbConnectionSettings()\n        settings.dbUrl = dburl\n\n        def parsedUrl = (dburl =~ /(jdbc:.*:@\\/\\/)([\\w\\d\\.-]+):(\\d+)\\/([\\w\\-_\\.\\d]+).*/)\n        settings.serverName = parsedUrl[0][2]\n        settings.portNumber = parsedUrl[0][3]\n        settings.databaseName = parsedUrl[0][4]\n        settings.genericUrl = dburl\n\n        settings\n    }\n\n    private static DbConnectionSettings extractSqlServerDbConnectionSettings(String dburl) {\n        DbConnectionSettings settings = new DbConnectionSettings()\n        settings.dbUrl = dburl\n\n        def parsedUrl = (dburl =~ /(jdbc:\\w+:\\/\\/)([\\w\\d\\.-]+):(\\d+);database=([\\w\\-_\\d]+).*/)\n        settings.serverName = parsedUrl[0][2]\n        settings.portNumber = parsedUrl[0][3]\n        settings.databaseName = parsedUrl[0][4]\n        settings.genericUrl = parsedUrl[0][1] + settings.serverName + \":\" + settings.portNumber\n\n        settings\n    }\n\n    @ToString\n    @EqualsAndHashCode\n    static class DbConnectionSettings {\n        String dbUrl\n        String serverName\n        String portNumber\n        String databaseName\n        String genericUrl\n    }\n\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/org/bonitasoft/engine/gradle/docker/DockerDatabaseContainerTasksCreator.groovy",
    "content": "/*\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n */\npackage org.bonitasoft.engine.gradle.docker\n\nimport com.bmuschko.gradle.docker.tasks.container.DockerCreateContainer\nimport com.bmuschko.gradle.docker.tasks.container.DockerInspectContainer\nimport com.bmuschko.gradle.docker.tasks.container.DockerRemoveContainer\nimport com.bmuschko.gradle.docker.tasks.container.DockerStartContainer\nimport com.bmuschko.gradle.docker.tasks.container.extras.DockerWaitHealthyContainer\nimport com.bmuschko.gradle.docker.tasks.image.DockerPullImage\nimport org.bonitasoft.engine.gradle.JVMModifier\nimport org.gradle.api.Project\nimport org.gradle.api.tasks.TaskProvider\nimport org.gradle.api.tasks.bundling.Zip\nimport org.gradle.api.tasks.testing.Test\n/**\n * Gradle plugin to start docker database containers and perform tests against them\n */\nclass DockerDatabaseContainerTasksCreator {\n\n    private static String getDockerHost(def project) {\n        def dockerHost = System.getenv('DOCKER_HOST')\n        if (dockerHost?.trim()) {\n            project.logger.quiet(\"using DOCKER_HOST: ${dockerHost}\")\n            return new URI(dockerHost).host\n        }\n        return 'localhost'\n    }\n\n    private static final String SYS_PROP_DB_URL = 'db.url'\n    private static final String SYS_PROP_DB_USER = 'db.user'\n    private static final String SYS_PROP_DB_PASSWORD = 'db.password'\n\n    def static createTasks(Project project, DatabasePluginExtension extension, List vendors) {\n        // required to have the environment correctly setup: see https://github.com/bmuschko/gradle-docker-plugin/issues/575#issuecomment-383704012\n        if (!project.rootProject.plugins.hasPlugin('com.bmuschko.docker-remote-api')) {\n            project.rootProject.plugins.apply('com.bmuschko.docker-remote-api')\n        }\n        project.plugins.apply('com.bmuschko.docker-remote-api')\n        vendors.each { vendor ->\n            if (!extension.\"${vendor.name}\".enabled) {\n                return // do not create docker tasks for disabled database configurations\n            }\n            def uniqueName = vendor.name.capitalize()\n\n            DbParser.DbConnectionSettings dbConnectionSettings = new DbParser.DbConnectionSettings()\n            DbParser.DbConnectionSettings bdmDbConnectionSettings = new DbParser.DbConnectionSettings()\n\n            def pullImage = project.tasks.register(\"pull${uniqueName}Image\", DockerPullImage) {\n                description \"Pull docker image for $uniqueName db vendor\"\n                group null // do not show task when running `gradle tasks`\n\n                image = vendor.image\n\n                if (vendor.registryUrlEnv != null) {\n                    registryCredentials.with {\n                        url = System.getenv(vendor.registryUrlEnv)\n                        username = System.getenv(vendor.registryUsernameEnv)\n                        password = System.getenv(vendor.registryPasswordEnv)\n                    }\n                }\n            }\n\n            def createContainer = project.tasks.register(\"create${uniqueName}Container\", DockerCreateContainer) {\n                description \"Create a docker container for $uniqueName db vendor\"\n                group null // do not show task when running `gradle tasks`\n\n                if (project.hasProperty(\"docker-container-alias\")) {\n                    containerName = project.getProperty(\"docker-container-alias\")\n                }\n                hostConfig.portBindings = [\":$vendor.portBinding\"]\n                targetImageId pullImage.get().getImage()\n                if ('oracle' == vendor.name) {\n                    // 1Go\n                    hostConfig.shmSize = 1099511627776\n                }\n                hostConfig.autoRemove = true\n            }\n\n            def startContainer = project.tasks.register(\"start${uniqueName}Container\", DockerStartContainer) {\n                description \"Start a docker container for $uniqueName db vendor\"\n                group \"docker\"\n\n                targetContainerId createContainer.get().getContainerId()\n            }\n\n            def waitForContainerStartup = project.tasks.register(\"waitFor${uniqueName}ContainerStartup\", DockerWaitHealthyContainer) {\n                description \"Wait for a started docker container for $vendor.name db vendor to be healthy\"\n                group null // do not show task when running `gradle tasks`\n\n                targetContainerId startContainer.get().getContainerId()\n                // Oracle requires more time due to initialization, other databases are faster\n                awaitStatusTimeout = ('oracle' == vendor.name) ? 480 : 360\n            }\n\n            def inspectContainer = project.tasks.register(\"inspect${uniqueName}ContainerUrl\", DockerInspectContainer) {\n                description = \"Get url of a docker container for $uniqueName db vendor\"\n                group = null // do not show task when running `gradle tasks`\n\n                targetContainerId(startContainer.get().getContainerId())\n\n                onNext {\n                    it.networkSettings.ports.getBindings().each { exposedPort, bindingArr ->\n                        if (exposedPort.port == vendor.portBinding) {\n                            int portBinding = bindingArr.first().hostPortSpec as int\n                            def dockerHost = getDockerHost(project)\n                            dbConnectionSettings.dbUrl = vendor.name == \"oracle\" ? String.format(vendor.uriTemplate, dockerHost, portBinding) : String.format(vendor.uriTemplate, dockerHost, portBinding, \"bonita\")\n                            dbConnectionSettings.serverName = dockerHost\n                            dbConnectionSettings.portNumber = portBinding\n                            bdmDbConnectionSettings.dbUrl = vendor.name == \"oracle\" ? String.format(vendor.uriTemplate, dockerHost, portBinding) : String.format(vendor.uriTemplate, dockerHost, portBinding, \"business_data\")\n                            bdmDbConnectionSettings.serverName = dockerHost\n                            bdmDbConnectionSettings.portNumber = portBinding\n                            project.logger.quiet(\"db.url set to ${dbConnectionSettings.dbUrl}\")\n                        }\n                    }\n                }\n            }\n\n            def removeContainer = project.tasks.register(\"remove${uniqueName}Container\", DockerRemoveContainer) {\n                description \"Remove a docker container for $uniqueName db vendor\"\n                group \"docker\"\n\n                force = true\n                removeVolumes = true\n                targetContainerId createContainer.get().getContainerId()\n            }\n\n            TaskProvider databaseTestTask = project.tasks.register(\"${vendor.name}DatabaseTest\", Test) {\n                group = \"Verification\"\n                description = \"Runs integration test suite on $vendor.name database.\"\n                systemProperty \"bonita.version\", project.version\n                jvmArgs += ['--add-opens', 'java.base/java.util=ALL-UNNAMED', '--add-opens', 'java.base/java.lang=ALL-UNNAMED', '-Dfile.encoding=UTF-8']\n                if (extension.\"${vendor.name}\"?.includeTestProject) {\n                    testClassesDirs += extension.\"${vendor.name}\".includeTestProject.sourceSets.test.output.classesDirs\n                    classpath += extension.\"${vendor.name}\".includeTestProject.sourceSets.test.runtimeClasspath\n                }\n                classpath += project.files(project.configurations.drivers)\n                if (extension.\"${vendor.name}\"?.excludes) {\n                    exclude(extension.\"${vendor.name}\".excludes)\n                }\n                if (extension.\"${vendor.name}\"?.excludeTags) {\n                    useJUnitPlatform {\n                        excludeTags = extension.\"${vendor.name}\".excludeTags\n                    }\n                }\n                onlyIf { extension.\"${vendor.name}\"?.enabled }\n\n                doFirst {\n                    String dbUrl = project.hasProperty(SYS_PROP_DB_URL) ? project.property(SYS_PROP_DB_URL) : dbConnectionSettings.dbUrl\n                    def connectionSettings = DbParser.extractDbConnectionSettings(dbUrl)\n                    def dbValues = [\n                            \"sysprop.bonita.db.vendor\"    : vendor.name,\n                            \"sysprop.bonita.bdm.db.vendor\": vendor.name,\n                            \"db.url\"                      : dbUrl,\n                            \"db.user\"                     : project.hasProperty('db.user') ? project.property(SYS_PROP_DB_URL) : (System.getProperty(SYS_PROP_DB_USER) ? System.getProperty(SYS_PROP_DB_USER) : 'bonita'),\n                            \"db.password\"                 : project.hasProperty('db.password') ? project.property(SYS_PROP_DB_URL) : (System.getProperty(SYS_PROP_DB_PASSWORD) ? System.getProperty(SYS_PROP_DB_PASSWORD) : 'bpm'),\n                            \"bdm.db.url\"                  : bdmDbConnectionSettings.dbUrl,\n                            \"bdm.db.user\"                 : project.hasProperty('db.user') ? project.property(SYS_PROP_DB_URL) : (System.getProperty(SYS_PROP_DB_USER) ? System.getProperty(SYS_PROP_DB_USER) : 'business_data'),\n                            \"db.server.name\"              : connectionSettings.serverName,\n                            \"db.server.port\"              : connectionSettings.portNumber,\n                            \"db.database.name\"            : connectionSettings.databaseName\n                    ]\n\n                    if ('oracle' == vendor.name) {\n                        // fix for https://community.oracle.com/message/3701989\n                        // http://www.thezonemanager.com/2015/07/whats-so-special-about-devurandom.html\n                        dbValues.put('java.security.egd', 'file:/dev/./urandom')\n                        // fix for ORA-01882\n                        dbValues.put('user.timezone', 'UTC')\n                    }\n                    //  /!\\ warning: do NOT use setSystemProperties, as it would erase existing system properties.\n                    // rather use systemProperties to merge the new ones with the existing ones.\n                    systemProperties(dbValues)\n                }\n            }\n\n            TaskProvider zipReport = project.tasks.register(\"zip${vendor.name}DatabaseTestReport\" as String, Zip) {\n                archiveFileName = databaseTestTask.get().reports.html.outputLocation.get().getAsFile().name + \".zip\"\n                destinationDirectory = databaseTestTask.get().reports.html.outputLocation.get().getAsFile().parentFile\n                from databaseTestTask.get().reports.html.outputLocation.get().getAsFile()\n            }\n            project.afterEvaluate {\n                JVMModifier.setTestJVM(project, databaseTestTask)\n                JVMModifier.setJvmArgs(project, databaseTestTask)\n                databaseTestTask.configure {\n                    if (extension.includes) {\n                        include(extension.includes)\n                    }\n                    if (extension.excludes) {\n                        exclude(extension.excludes)\n                    }\n                }\n                pullImage.configure { onlyIf { extension.\"${vendor.name}\"?.enabled } }\n                createContainer.configure { onlyIf { extension.\"${vendor.name}\"?.enabled } }\n                startContainer.configure { onlyIf { extension.\"${vendor.name}\"?.enabled } }\n                waitForContainerStartup.configure { onlyIf { extension.\"${vendor.name}\"?.enabled } }\n                inspectContainer.configure { onlyIf { extension.\"${vendor.name}\"?.enabled } }\n                removeContainer.configure { onlyIf { extension.\"${vendor.name}\"?.enabled } }\n            }\n\n            if (createContainer) {\n                createContainer.configure { dependsOn(pullImage) }\n            }\n            if (startContainer) {\n                startContainer.configure {\n                    dependsOn(createContainer)\n                    finalizedBy(removeContainer)\n                }\n            }\n            if (waitForContainerStartup) {\n                waitForContainerStartup.configure { dependsOn(startContainer) }\n            }\n            if (inspectContainer) {\n                inspectContainer.configure { dependsOn(waitForContainerStartup) }\n            }\n            if (databaseTestTask) {\n                databaseTestTask.configure {\n                    dependsOn(inspectContainer)\n                    finalizedBy(zipReport)\n                }\n            }\n            if (removeContainer) {\n                removeContainer.configure { mustRunAfter(databaseTestTask) }\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/org/bonitasoft/engine/gradle/docker/DockerDatabasePlugin.groovy",
    "content": "package org.bonitasoft.engine.gradle.docker\n\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.api.artifacts.VersionCatalogsExtension\n\n/**\n * @author Emmanuel Duchastenier\n */\nclass DockerDatabasePlugin implements Plugin<Project> {\n\n    @Override\n    void apply(Project project) {\n        project.configurations {\n            drivers\n        }\n        driversConfiguration(project)\n        def databaseIntegrationTest = project.extensions.create(\"databaseIntegrationTest\", DatabasePluginExtension)\n\n\n        project.afterEvaluate {\n            DockerDatabaseContainerTasksCreator.createTasks(project, databaseIntegrationTest, getVendors())\n            if (!databaseIntegrationTest.includes) {\n                println \"No databaseIntegrationTest.include found. No tests to run!\"\n            }\n        }\n    }\n\n    def driversConfiguration(project) {\n        project.dependencies {\n            // the following jdbc drivers are available for integration tests\n            def versionCatalog = project.extensions.getByType(VersionCatalogsExtension.class).named(\"libs\")\n            drivers(versionCatalog.findLibrary(\"postgresql\").get())\n        }\n    }\n\n    List getVendors() {\n        return [\n                [name       : 'postgres',\n                 image      : 'bonitasoft/bonita-postgres:16.4',\n                 portBinding: 5432,\n                 uriTemplate: 'jdbc:postgresql://%s:%s/%s',\n                ]\n        ]\n    }\n\n}\n"
  },
  {
    "path": "common.gradle",
    "content": "apply plugin: libs.plugins.taskInfo.get().pluginId\n\nallprojects {\n    apply plugin: 'maven-publish'\n\n    group = 'org.bonitasoft.engine'\n\n    tasks.withType(Copy).configureEach {\n        setDuplicatesStrategy(DuplicatesStrategy.INCLUDE)\n    }\n\n    publishing {\n        repositories {\n            if (project.hasProperty(\"altDeploymentRepository\")) {\n                def repoProperties = project.getProperties().\"altDeploymentRepository\".split(\"::\")\n                maven {\n                    name = repoProperties.first()\n                    url = repoProperties.last()\n                    //those credentials can be given using -P<repo_name>Username and -P<repo_name>Password see README.md\n                    credentials(PasswordCredentials)\n                }\n            }\n        }\n    }\n}\n\nsubprojects {\n    apply plugin: 'java-library'\n    apply plugin: 'maven-publish'\n    apply plugin: 'com.adarshr.test-logger'\n    apply plugin: 'org.gradle.test-retry'\n\n    repositories {\n        mavenCentral()\n        if (project.hasProperty(\"extraRepositories\")) {\n\n            def extraRepositories = project.getProperties().get(\"extraRepositories\")\n            extraRepositories.split(\",\").each { repo ->\n                def repoProperties = repo.split(\"::\")\n                maven {\n                    name = repoProperties.first()\n                    url = repoProperties.last()\n                    //those credentials can be given using -P<repo_name>Username and -P<repo_name>Password see README.md\n                    credentials(PasswordCredentials)\n                }\n            }\n        }\n\n        maven {\n            url=\"https://central.sonatype.com/repository/maven-snapshots/\"\n            mavenContent {\n                snapshotsOnly()\n            }\n        }\n        mavenLocal()\n    }\n\n    dependencies {\n        testImplementation libs.junit5api\n        testImplementation libs.mockitoJunitJupiter\n        testCompileOnly libs.junit4\n        testRuntimeOnly libs.junitJupiterEngine\n        // in Gradle 8, this declaration is mandatory. See https://docs.gradle.org/8.3/userguide/upgrading_version_8.html#manually_declaring_dependencies :\n        testRuntimeOnly(\"org.junit.platform:junit-platform-launcher\")\n        // to support junit4 tests\n        testRuntimeOnly libs.junitVintageEngine\n    }\n\n    java {\n        toolchain {\n            languageVersion = JavaLanguageVersion.of(17)\n        }\n    }\n    afterEvaluate {\n        tasks.withType(AbstractCompile).configureEach { options.encoding = 'UTF-8' }\n    }\n    tasks.withType(JavaCompile).configureEach {\n        options.encoding = 'UTF-8'\n        options.fork = true\n    }\n    tasks.withType(Javadoc).configureEach {\n        options.addStringOption('Xdoclint:none', '-quiet')\n        options.encoding = 'UTF-8'\n    }\n    tasks.withType(DependencyReportTask).configureEach {\n        group = \"Documentation\"\n        description = \"List runtime dependencies for a specified configuration\"\n        configurations = [project.configurations.runtimeClasspath]\n    }\n\n    // Configure all test tasks (test, integrationTest, httpIT, databaseTest)\n    tasks.withType(Test).configureEach {\n        useJUnitPlatform {\n            includeEngines 'junit-jupiter', 'junit-vintage'\n        }\n        jvmArgs += ['--add-opens', 'java.base/java.util=ALL-UNNAMED', '--add-opens', 'java.base/java.lang=ALL-UNNAMED', '-Dfile.encoding=UTF-8']\n        if (!project.hasProperty(\"createTestReports\")) {\n            reports.html.required = false\n            reports.junitXml.required = false\n        }\n\n        retry {\n            // Test Retry Plugin Configuration\n            // Can be overridden from command line:\n            //   -PtestRetryMaxRetries=5       (Gradle property - recommended)\n            //   -Dtest.retry.maxRetries=5     (System property)\n            //   -PtestRetryMaxFailures=20     (Max failures before stopping)\n            //   -Dtest.retry.maxFailures=20   (System property)\n            //   -PtestRetryFailOnFlaky=false  (Don't fail on flaky tests)\n            //\n            // Examples:\n            //   ./gradlew integrationTest -PtestRetryMaxRetries=5\n            //   ./gradlew iT -PtestRetryMaxRetries=0  # Disable retry\n            //   ./gradlew mysqlDatabaseTest -PtestRetryMaxRetries=10\n\n            maxRetries = project.hasProperty('testRetryMaxRetries') ?\n                project.property('testRetryMaxRetries').toInteger() :\n                System.getProperty('test.retry.maxRetries', '2').toInteger()\n\n            maxFailures = project.hasProperty('testRetryMaxFailures') ?\n                project.property('testRetryMaxFailures').toInteger() :\n                System.getProperty('test.retry.maxFailures', '6').toInteger()\n\n            if (!System.getenv().containsKey(\"GITHUB_ACTIONS\")) {\n                // locally, we want to fail the build on flaky tests passing after retry. On CI, no:\n                failOnPassedAfterRetry = project.hasProperty('testRetryFailOnFlaky') ?\n                    project.property('testRetryFailOnFlaky').toBoolean() : true\n            }\n            filter {\n                // filter by qualified class name (* matches zero or more of any character)\n                includeClasses.add(\"*IT\")\n            }\n        }\n    }\n    testlogger {\n        showFullStackTraces=false\n        showCauses=true\n        showPassed=false\n        showSkipped=true\n        showSummary=false\n    }\n}\n"
  },
  {
    "path": "engine-settings.gradle",
    "content": "include(':bonita-engine')\ninclude(':platform:platform-resources')\ninclude(':platform:platform-setup')\ninclude(':platform:platform-setup-test')\ninclude(':platform')\ninclude(':services:bonita-commons')\ninclude(':services:bonita-resources')\ninclude(':services:bonita-log')\ninclude(':services:bonita-identity')\ninclude(':services:bonita-transaction')\ninclude(':services:bonita-persistence')\ninclude(':services:bonita-cache')\ninclude(':services:bonita-classloader')\ninclude(':services:bonita-scheduler')\ninclude(':services:bonita-events')\ninclude(':services:bonita-platform')\ninclude(':services:bonita-archive')\ninclude(':services:bonita-authentication')\ninclude(':services:bonita-authorization')\ninclude(':services:bonita-session')\ninclude(':services:bonita-platform-authentication')\ninclude(':services:bonita-platform-session')\ninclude(':services:bonita-expression')\ninclude(':services:bonita-data-definition')\ninclude(':services:bonita-data-instance')\ninclude(':services:bonita-connector-executor')\ninclude(':services:bonita-command')\ninclude(':services:bonita-platform-command')\ninclude(':services:bonita-profile')\ninclude(':services:bonita-lock')\ninclude(':services:bonita-work')\ninclude(':services:bonita-incident')\ninclude(':services:bonita-builder')\ninclude(':services:bonita-page')\ninclude(':services:bonita-time-tracker')\ninclude(':services:bonita-business-application')\ninclude(':services:bonita-business-data:bonita-business-data-api')\ninclude(':services:bonita-business-data:bonita-business-data-client-resources')\ninclude(':services:bonita-business-data:bonita-business-data-impl')\ninclude(':services:bonita-business-data:bonita-business-data-generator')\ninclude(':services:bonita-business-data')\ninclude(':services:bonita-temporary-content')\ninclude(':services')\ninclude(':bpm:bonita-sap-jco-connector-api')\ninclude(':bpm:bonita-common')\ninclude(':bpm:bonita-client')\ninclude(':bpm:bonita-server')\ninclude(':bpm:bonita-web-extensions')\ninclude(':bpm:bonita-web-server')\ninclude(':bpm:bonita-core:bonita-process-definition')\ninclude(':bpm:bonita-core:bonita-process-instance')\ninclude(':bpm:bonita-core:bonita-login')\ninclude(':bpm:bonita-core:bonita-platform-login')\ninclude(':bpm:bonita-core:bonita-process-engine')\ninclude(':bpm:bonita-core:bonita-home-server')\ninclude(':bpm:bonita-core:bonita-actor-mapping')\ninclude(':bpm:bonita-core:bonita-core-data')\ninclude(':bpm:bonita-core:bonita-category')\ninclude(':bpm:bonita-core:bonita-supervisor-mapping')\ninclude(':bpm:bonita-core:bonita-process-comment')\ninclude(':bpm:bonita-core:bonita-user-filter')\ninclude(':bpm:bonita-core:bonita-contract-data')\ninclude(':bpm:bonita-core:bonita-parameter')\ninclude(':bpm:bonita-core:bonita-form-mapping')\ninclude(':bpm:bonita-core')\ninclude(':bpm:bonita-api:bonita-server-api-http')\ninclude(':bpm:bonita-api')\ninclude(':bpm:bonita-external')\ninclude(':bpm:bonita-synchro-repository:bonita-synchro-service')\ninclude(':bpm:bonita-synchro-repository:bonita-synchro-service-impl')\ninclude(':bpm:bonita-synchro-repository:bonita-synchro-register')\ninclude(':bpm:bonita-synchro-repository')\ninclude(':bonita-engine-standalone')\ninclude(':bonita-test-api')\ninclude(':bonita-engine-spring-boot-starter')\ninclude(':bonita-integration-tests:bonita-test-utils')\ninclude(':bonita-integration-tests:bonita-query-tests')\ninclude(':bonita-integration-tests:bonita-integration-tests-client')\ninclude(':bonita-integration-tests:bonita-integration-tests-local')\ninclude(':bonita-integration-tests:bonita-integration-tests-web')\ninclude(':bonita-integration-tests:benchmarks')\ninclude(':bonita-integration-tests')\n"
  },
  {
    "path": "gradle/libs.versions.toml",
    "content": "[versions]\n# The groovy version must be in synch with the bonita-project-parent POM (bonita-project repository):\ngroovyVersion = \"3.0.25\"\nspringVersion = \"5.3.39\"\nspringSessionVersion = \"2.7.4\"\nspringBootVersion = \"2.7.18\"\ncommonsLangVersion = \"3.20.0\"\nbonitaArtifactsModelVersion = \"1.2.2\"\nbonitaSecurityVersion = \"1.31.0\"\ncommonsIOVersion = \"2.21.0\"\ncommonsFileUploadVersion = \"1.6.0\"\ncommonsBeanutilsVersion = \"1.11.0\"\ncommonsCollectionsVersion = \"4.5.0\"\ntomcatVersion = \"9.0.117\"\ncommonsCLIVersion = \"1.11.0\"\ncommonsTextVersion = \"1.15.0\"\ncommonsValidatorVersion = \"1.10.1\"\nsemver4jVersion = \"3.1.0\"\nslf4jVersion = \"1.7.36\"\n# Attention, see PassingPropertiesJCacheRegionFactory javadoc if this version changes:\nhibernateVersion = \"5.6.15.Final\"\n# version used by hibernate 5.6.15 (also used by BDM proxy generation):\njavassistVersion = \"3.29.2-GA\"\n# javax.persistence-api is used by hibernate:\njavaxPersistenceApiVersion = \"2.2\"\njacksonBomVersion = \"2.21.2\"\njakartaTransactionVersion = \"1.3.3\"\njakartaServletVersion = \"4.0.4\"\n# Keep this until all client projects have migrated to jakarta or it will break their builds !\njavaxServletVersion = \"4.0.1\"\nhttpComponentsVersion = \"4.5.14\"\nxstreamVersion = \"1.4.21\"\nehCacheVersion = \"3.11.1\"\neclipseCompilerVersion = \"3.45.0\"\njakartaActivationVersion = \"1.2.2\"\njakartaValidationApiVersion = \"3.1.1\"\nquartzVersion = \"2.3.2\"\nmicrometerVersion = \"1.16.5\"\n# DB drivers:\nmysqlVersion = \"8.4.0\"\nmsSqlServerVersion = \"12.10.2.jre11\"\noracleVersion = \"23.26.1.0.0\"\npostgresqlVersion = \"42.7.11\"\n\nnarayanaVersion = \"5.10.6.Final\"\nlogbackVersion = \"1.2.13\"\njaxbVersion = \"2.3.9\"\njavaxAnnotationsVersion = \"1.3.2\"\nhazelcastVersion = \"5.4.0\" # Also update http://www.hazelcast.com/schema/config/hazelcast-config-<VERSION>.xsd if needed\njcacheVersion = \"1.1.1\"\nguavaVersion = \"33.6.0-jre\"\nantlr4RuntimeVersion = \"4.7.2\"\ncasClientCoreVersion = \"4.0.4\"\njtidyVersion = \"r938\"\nsquigglyFilterJacksonVersion = \"1.3.18\"\naspectjVersion = \"1.9.25.1\"\n\n# bonita-web specific dependencies:\njsonSimpleVersion = \"1.1.1\"\nurlrewritefilterVersion = \"4.0.4\"\njakartaJstlVersion = \"1.2.6\"\njakartaJstlApiVersion = \"1.2.7\"\nxbeanClassloaderVersion = \"4.30\"\njgettextVersion = \"0.15.1\"\nhamcrestVersion = \"3.0\"\nwoodstoxCoreVersion = \"7.1.1\"\n# When updating Keycloak version, if necessary, make sure to update the code in\n# subscription/bpm/bonita-web-server-sp package org.bonitasoft.console.common.server.auth.impl\nkeycloakVersion = \"21.1.2\"\nxmlsecVersion = \"2.3.5\"\nbouncyCastleVersion = \"1.84\"\nspnegoVersion = \"1.1.1\"\nowaspHtmlSanitizerVersion = \"20260313.1\"\n\n# Test dependency versions\njunit4Version = \"4.13.2\"\njunit5Version = \"6.0.3\"\nawaitilityVersion = \"4.3.0\"\nassertjVersion = \"3.27.7\"\nxmlunitVersion = \"1.6\"\nmockitoVersion = \"5.23.0\"\njsonUnitVersion = \"5.1.1\"\nsystemRulesVersion = \"1.19.0\"\nsystemLambdaVersion = \"1.2.1\"\nconcurrentUnitVersion = \"0.4.6\"\njettyVersion = \"9.4.58.v20250814\"\njbossLoggingVersion = \"3.6.3.Final\"\njsonassertVersion = \"1.5.3\"\ncommonsExecVersion = \"1.6.0\"\njmockitVersion = \"1.50\"\nmockServerJunitVersion = \"5.15.0\"\n\n\n[libraries]\nspringCore = { module = \"org.springframework:spring-core\", version.ref = \"springVersion\" }\nspringBeans = { module = \"org.springframework:spring-beans\", version.ref = \"springVersion\" }\nspringContext = { module = \"org.springframework:spring-context\", version.ref = \"springVersion\" }\nspringTx = { module = \"org.springframework:spring-tx\", version.ref = \"springVersion\" }\nspringJdbc = { module = \"org.springframework:spring-jdbc\", version.ref = \"springVersion\" }\nspringWebMvc = { module = \"org.springframework:spring-webmvc\", version.ref = \"springVersion\" }\nspringWeb = { module = \"org.springframework:spring-web\", version.ref = \"springVersion\" }\nspringOrm = { module = \"org.springframework:spring-orm\", version.ref = \"springVersion\" }\nspringSessionCore = { module = \"org.springframework.session:spring-session-core\", version.ref = \"springSessionVersion\" }\nspringSessionHazelcast = { module = \"org.springframework.session:spring-session-hazelcast\", version.ref = \"springSessionVersion\" }\nspringAop = { module = \"org.springframework:spring-aop\", version.ref = \"springVersion\" }\n\nspringBootAutoconfigure = { module = \"org.springframework.boot:spring-boot-autoconfigure\", version.ref = \"springBootVersion\" }\nspringBootConfigurationProcessor = { module = \"org.springframework.boot:spring-boot-configuration-processor\", version.ref = \"springBootVersion\" }\nspringBootTest = { module = \"org.springframework.boot:spring-boot-test\", version.ref = \"springBootVersion\" }\nspringBootStarter = { module = \"org.springframework.boot:spring-boot-starter\", version.ref = \"springBootVersion\" }\nspringBootStarterJdbc = { module = \"org.springframework.boot:spring-boot-starter-jdbc\", version.ref = \"springBootVersion\" }\nspringBootStarterTest = { module = \"org.springframework.boot:spring-boot-starter-test\", version.ref = \"springBootVersion\" }\nspringBootStarterWeb = { module = \"org.springframework.boot:spring-boot-starter-web\", version.ref = \"springBootVersion\" }\n\ngroovyBom = { module = \"org.codehaus.groovy:groovy-bom\", version.ref = \"groovyVersion\" }\ngroovyCore = { module = \"org.codehaus.groovy:groovy\", version.ref = \"groovyVersion\" }\ngroovyServlet = { module = \"org.codehaus.groovy:groovy-servlet\", version.ref = \"groovyVersion\" }\ngroovyXml = { module = \"org.codehaus.groovy:groovy-xml\", version.ref = \"groovyVersion\" }\ngroovyJson = { module = \"org.codehaus.groovy:groovy-json\", version.ref = \"groovyVersion\" }\ngroovyJmx = { module = \"org.codehaus.groovy:groovy-jmx\", version.ref = \"groovyVersion\" }\ngroovyNio = { module = \"org.codehaus.groovy:groovy-nio\", version.ref = \"groovyVersion\" }\ngroovyGroovysh = { module = \"org.codehaus.groovy:groovy-groovysh\", version.ref = \"groovyVersion\" }\ngroovyDatetime = { module = \"org.codehaus.groovy:groovy-datetime\", version.ref = \"groovyVersion\" }\ngroovyDateutil = { module = \"org.codehaus.groovy:groovy-dateutil\", version.ref = \"groovyVersion\" }\ngroovyDocgenerator = { module = \"org.codehaus.groovy:groovy-docgenerator\", version.ref = \"groovyVersion\" }\ngroovyJsr223 = { module = \"org.codehaus.groovy:groovy-jsr223\", version.ref = \"groovyVersion\" }\ngroovySql = { module = \"org.codehaus.groovy:groovy-sql\", version.ref = \"groovyVersion\" }\ngroovyTemplates = { module = \"org.codehaus.groovy:groovy-templates\", version.ref = \"groovyVersion\" }\ngroovyYaml = { module = \"org.codehaus.groovy:groovy-yaml\", version.ref = \"groovyVersion\" }\n\n\nbonitaArtifactsModelBom = { module = \"org.bonitasoft.engine:bonita-artifacts-model-dependencies\", version.ref = \"bonitaArtifactsModelVersion\" }\n\nbonitaCommonArtifactsModel = { module = \"org.bonitasoft.engine:bonita-common-artifacts-model\", version.ref = \"bonitaArtifactsModelVersion\" }\nbonitaBusinessArchiveModel = { module = \"org.bonitasoft.engine:bonita-business-archive\", version.ref = \"bonitaArtifactsModelVersion\" }\nbonitaProcessDefinitionModel = { module = \"org.bonitasoft.engine:bonita-process-definition-model\", version.ref = \"bonitaArtifactsModelVersion\" }\nbonitaFormMappingModel = { module = \"org.bonitasoft.engine:bonita-form-mapping-model\", version.ref = \"bonitaArtifactsModelVersion\" }\nbonitaBusinessObjectModel = { module = \"org.bonitasoft.engine:bonita-business-object-model\", version.ref = \"bonitaArtifactsModelVersion\" }\nbonitaBdmAccessControlModel = { module = \"org.bonitasoft.engine:bonita-bdm-access-control-model\", version.ref = \"bonitaArtifactsModelVersion\" }\nbonitaProfileModel = { module = \"org.bonitasoft.engine:bonita-profile-model\", version.ref = \"bonitaArtifactsModelVersion\" }\nbonitaOrganizationModel = { module = \"org.bonitasoft.engine:bonita-organization-model\", version.ref = \"bonitaArtifactsModelVersion\" }\nbonitaApplicationModel = { module = \"org.bonitasoft.engine:bonita-application-model\", version.ref = \"bonitaArtifactsModelVersion\" }\nbonitaConnectorModel = { module = \"org.bonitasoft.engine:bonita-connector-model\", version.ref = \"bonitaArtifactsModelVersion\" }\n\nbonitaManager = { group = \"com.bonitasoft.manager\", name = \"manager\", version.ref = \"bonitaSecurityVersion\" }\nbonitaTestLicenses = { group = \"org.bonitasoft.security\", name = \"test-licenses\", version.ref = \"bonitaSecurityVersion\" }\n\nguava = { group = \"com.google.guava\", name = \"guava\", version.ref = \"guavaVersion\" }\nantlr4Runtime = { group = \"org.antlr\", name = \"antlr4-runtime\", version.ref = \"antlr4RuntimeVersion\" }\ncommonsLang = { group = \"org.apache.commons\", name = \"commons-lang3\", version.ref = \"commonsLangVersion\" }\ncommonsIO = { group = \"commons-io\", name = \"commons-io\", version.ref = \"commonsIOVersion\" }\ncommonsFileUpload = { group = \"commons-fileupload\", name = \"commons-fileupload\", version.ref = \"commonsFileUploadVersion\" }\ncommonsBeanUtils = { group = \"commons-beanutils\", name = \"commons-beanutils\", version.ref = \"commonsBeanutilsVersion\" }\ncommonsCollections = { group = \"org.apache.commons\", name = \"commons-collections4\", version.ref = \"commonsCollectionsVersion\" }\ntomcatDbcp = { group = \"org.apache.tomcat\", name = \"tomcat-dbcp\", version.ref = \"tomcatVersion\" }\ntomcatEmbedCore = { group = \"org.apache.tomcat.embed\", name = \"tomcat-embed-core\", version.ref = \"tomcatVersion\"}\ncommonsCLI = { group = \"commons-cli\", name = \"commons-cli\", version.ref = \"commonsCLIVersion\" }\ncommonsText = { group = \"org.apache.commons\", name = \"commons-text\", version.ref = \"commonsTextVersion\" }\ncommonsValidator = { group = \"commons-validator\", name = \"commons-validator\", version.ref = \"commonsValidatorVersion\" }\nsemver4j = { group = \"com.vdurmont\", name = \"semver4j\", version.ref = \"semver4jVersion\" }\nslf4jApi = { group = \"org.slf4j\", name = \"slf4j-api\", version.ref = \"slf4jVersion\" }\n\nhibernateCore = { group = \"org.hibernate\", name = \"hibernate-core\", version.ref = \"hibernateVersion\" }\njavaxPersistenceApi = { group = \"javax.persistence\", name = \"javax.persistence-api\", version.ref = \"javaxPersistenceApiVersion\" }\nhibernateJCache = { group = \"org.hibernate\", name = \"hibernate-jcache\", version.ref = \"hibernateVersion\" }\njcache = { group = \"javax.cache\", name = \"cache-api\", version.ref = \"jcacheVersion\" }\njavassist = { group = \"org.javassist\", name = \"javassist\", version.ref = \"javassistVersion\" }\n\njacksonBom = { group = \"com.fasterxml.jackson\", name = \"jackson-bom\", version.ref = \"jacksonBomVersion\" }\n\njakartaTransactionApi = { group = \"jakarta.transaction\", name = \"jakarta.transaction-api\", version.ref = \"jakartaTransactionVersion\" }\njakartaServletApi = { group = \"jakarta.servlet\", name = \"jakarta.servlet-api\", version.ref = \"jakartaServletVersion\" }\njavaxServletApi = { group = \"javax.servlet\", name = \"javax.servlet-api\", version.ref = \"javaxServletVersion\" }\n\nhttpComponentsClient = { group = \"org.apache.httpcomponents\", name = \"httpclient\", version.ref = \"httpComponentsVersion\" }\nhttpComponentsMime = { group = \"org.apache.httpcomponents\", name = \"httpmime\", version.ref = \"httpComponentsVersion\" }\n\nxstream = { group = \"com.thoughtworks.xstream\", name = \"xstream\", version.ref = \"xstreamVersion\" }\nehCache = { group = \"org.ehcache\", name = \"ehcache\", version.ref = \"ehCacheVersion\" }\neclipseCompiler = { group = \"org.eclipse.jdt\", name = \"ecj\", version.ref = \"eclipseCompilerVersion\" }\njakartaActivation = { group = \"com.sun.activation\", name = \"jakarta.activation\", version.ref = \"jakartaActivationVersion\" }\njakartaValidationApi = { group = \"jakarta.validation\", name = \"jakarta.validation-api\", version.ref = \"jakartaValidationApiVersion\" }\njavaxAnnotations = { group = \"javax.annotation\", name = \"javax.annotation-api\", version.ref = \"javaxAnnotationsVersion\" }\n\nhazelcast = { group = \"com.hazelcast\", name = \"hazelcast\", version.ref = \"hazelcastVersion\" }\nhazelcastSpring = { group = \"com.hazelcast\", name = \"hazelcast-spring\", version.ref = \"hazelcastVersion\" }\n\nquartz = { group = \"org.quartz-scheduler\", name = \"quartz\", version.ref = \"quartzVersion\" }\nmicrometerCore = { group = \"io.micrometer\", name = \"micrometer-core\", version.ref = \"micrometerVersion\" }\nmicrometerRegistryJmx = { group = \"io.micrometer\", name = \"micrometer-registry-jmx\", version.ref = \"micrometerVersion\" }\nmicrometerRegistryPrometheus = { group = \"io.micrometer\", name = \"micrometer-registry-prometheus\", version.ref = \"micrometerVersion\" }\n\nnarayanaJta = { group = \"org.jboss.narayana.jta\", name = \"narayana-jta\", version.ref = \"narayanaVersion\" }\njaxbCodeModel = { group = \"org.glassfish.jaxb\", name = \"codemodel\", version.ref = \"jaxbVersion\" }\n\nh2 = \"com.h2database:h2:1.4.200\"\nmysql = { group = \"com.mysql\", name = \"mysql-connector-j\", version.ref = \"mysqlVersion\" }\nmsSqlServer = { group = \"com.microsoft.sqlserver\", name = \"mssql-jdbc\", version.ref = \"msSqlServerVersion\" }\noracle = { group = \"com.oracle.database.jdbc\", name = \"ojdbc11\", version.ref = \"oracleVersion\" }\npostgresql = { group = \"org.postgresql\", name = \"postgresql\", version.ref = \"postgresqlVersion\" }\n\nlombok = \"org.projectlombok:lombok:1.18.44\"\n\nlogback = { group = \"ch.qos.logback\", name = \"logback-classic\", version.ref = \"logbackVersion\" }\n\ncasClientCore = { group = \"org.apereo.cas.client\", name = \"cas-client-core\", version.ref = \"casClientCoreVersion\" }\njtidy = { group = \"net.sf.jtidy\", name = \"jtidy\", version.ref = \"jtidyVersion\" }\n\nsquigglyFilterJackson = { group = \"com.github.bohnman\", name = \"squiggly-filter-jackson\", version.ref = \"squigglyFilterJacksonVersion\" }\n\naspectjWeaver = { module = \"org.aspectj:aspectjweaver\", version.ref = \"aspectjVersion\" }\n\n\n# bonita-web specific dependencies:\njsonSimple = { group = \"com.googlecode.json-simple\", name = \"json-simple\", version.ref = \"jsonSimpleVersion\" }\nurlrewritefilter = { group = \"org.tuckey\", name = \"urlrewritefilter\", version.ref = \"urlrewritefilterVersion\" }\njakartaJstl = { group = \"org.glassfish.web\", name = \"jakarta.servlet.jsp.jstl\", version.ref = \"jakartaJstlVersion\" }\njakartaJstlApi = { group = \"jakarta.servlet.jsp.jstl\", name = \"jakarta.servlet.jsp.jstl-api\", version.ref = \"jakartaJstlApiVersion\" }\nxbeanClassloader = { group = \"org.apache.xbean\", name = \"xbean-classloader\", version.ref = \"xbeanClassloaderVersion\" }\njgettext = { group = \"org.fedorahosted.tennera\", name = \"jgettext\", version.ref = \"jgettextVersion\" }\nhamcrest = { group = \"org.hamcrest\", name = \"hamcrest\", version.ref = \"hamcrestVersion\" }\nwoodstoxCore = { group = \"com.fasterxml.woodstox\", name = \"woodstox-core\", version.ref = \"woodstoxCoreVersion\" }\nkeycloakSamlAdapterApiPublic = { group = \"org.keycloak\", name = \"keycloak-saml-adapter-api-public\", version.ref = \"keycloakVersion\" }\nkeycloakSamlServletFilterAdapter = { group = \"org.keycloak\", name = \"keycloak-saml-servlet-filter-adapter\", version.ref = \"keycloakVersion\" }\nkeycloakAdapterCore = { group = \"org.keycloak\", name = \"keycloak-adapter-core\", version.ref = \"keycloakVersion\" }\nkeycloakServletFilterAdapter = { group = \"org.keycloak\", name = \"keycloak-servlet-filter-adapter\", version.ref = \"keycloakVersion\" }\nxmlsec = { group = \"org.apache.santuario\", name = \"xmlsec\", version.ref = \"xmlsecVersion\" }\nbouncyCastleBcprov = { group = \"org.bouncycastle\", name = \"bcprov-jdk18on\", version.ref = \"bouncyCastleVersion\" }\nbouncyCastleBcpkix = { group = \"org.bouncycastle\", name = \"bcpkix-jdk18on\", version.ref = \"bouncyCastleVersion\" }\nbouncyCastleBcutil = { group = \"org.bouncycastle\", name = \"bcutil-jdk18on\", version.ref = \"bouncyCastleVersion\" }\nspnego = { group = \"org.codelibs\", name = \"spnego\", version.ref = \"spnegoVersion\" }\nowaspHtmlSanitizer = { group = \"com.googlecode.owasp-java-html-sanitizer\", name = \"owasp-java-html-sanitizer\", version.ref = \"owaspHtmlSanitizerVersion\" }\n\n# Test dependencies\njunit5api = { group = \"org.junit.jupiter\", name = \"junit-jupiter-api\", version.ref = \"junit5Version\" }\njunit5params = { group = \"org.junit.jupiter\", name = \"junit-jupiter-params\", version.ref = \"junit5Version\" }\njunitJupiterEngine = { group = \"org.junit.jupiter\", name = \"junit-jupiter-engine\", version.ref = \"junit5Version\" }\njunitVintageEngine = { group = \"org.junit.vintage\", name = \"junit-vintage-engine\", version.ref = \"junit5Version\" }\njunit4 = { group = \"junit\", name = \"junit\", version.ref = \"junit4Version\" }\nspringTest = { module = \"org.springframework:spring-test\", version.ref = \"springVersion\" }\nawaitility = { group = \"org.awaitility\", name = \"awaitility\", version.ref = \"awaitilityVersion\" }\nassertj = { group = \"org.assertj\", name = \"assertj-core\", version.ref = \"assertjVersion\" }\nxmlunit = { group = \"xmlunit\", name = \"xmlunit\", version.ref = \"xmlunitVersion\" }\nmockitoCore = { group = \"org.mockito\", name = \"mockito-core\", version.ref = \"mockitoVersion\" }\nmockitoJunitJupiter = { group = \"org.mockito\", name = \"mockito-junit-jupiter\", version.ref = \"mockitoVersion\" }\njsonUnit = { group = \"net.javacrumbs.json-unit\", name = \"json-unit-assertj\", version.ref = \"jsonUnitVersion\" }\nsystemRules = { group = \"com.github.stefanbirkner\", name = \"system-rules\", version.ref = \"systemRulesVersion\" }\nsystemLambda = { group = \"com.github.stefanbirkner\", name = \"system-lambda\", version.ref = \"systemLambdaVersion\" }\nconcurrentUnit = { group = \"net.jodah\", name = \"concurrentunit\", version.ref = \"concurrentUnitVersion\" }\njettyServer = { group = \"org.eclipse.jetty\", name = \"jetty-server\", version.ref = \"jettyVersion\" }\njettyServlet = { group = \"org.eclipse.jetty\", name = \"jetty-servlet\", version.ref = \"jettyVersion\" }\njettySecurity = { group = \"org.eclipse.jetty\", name = \"jetty-security\", version.ref = \"jettyVersion\" }\njbossLogging = { group = \"org.jboss.logging\", name = \"jboss-logging\", version.ref = \"jbossLoggingVersion\" }\ncommonsExec = { group = \"org.apache.commons\", name = \"commons-exec\", version.ref = \"commonsExecVersion\" }\njmockit = { group = \"org.jmockit\", name = \"jmockit\", version.ref = \"jmockitVersion\" }\nmockServerJunit = { group = \"org.mock-server\", name = \"mockserver-junit-jupiter\", version.ref = \"mockServerJunitVersion\" }\njsonassert = { group = \"org.skyscreamer\", name = \"jsonassert\", version.ref = \"jsonassertVersion\" }\n\n[bundles]\ngroovy = [\"groovyCore\", \"groovyServlet\", \"groovyXml\", \"groovyJson\", \"groovyJmx\", \"groovyNio\", \"groovyGroovysh\", \"groovyDatetime\",\n    \"groovyDateutil\", \"groovyDocgenerator\", \"groovyJsr223\", \"groovySql\", \"groovyTemplates\", \"groovyYaml\"]\n\n[plugins]\nbonitaFormatting = { id = \"com.bonitasoft.gradle.bonita-formatting\", version = \"1.0.1\" }\ndependencyUpdates = { id = \"com.github.ben-manes.versions\", version = \"0.53.0\" } # used by \"List out-of-date dependencies\" script\ntaskInfo = { id = \"org.barfuin.gradle.taskinfo\", version = \"3.0.2\" } # Adds a 'tiTree' task to display Gradle task-graph\ntestRetry = { id = \"org.gradle.test-retry\", version = \"1.6.4\" } # used to retry flaky tests automatically\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.4-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "gradle.properties",
    "content": "org.gradle.jvmargs=--add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED -Dfile.encoding=UTF-8\norg.gradle.caching=true\norg.gradle.vfs.watch=true\n\nversion=11.1-SNAPSHOT\nbrandingVersion=2026.2-SNAPSHOT"
  },
  {
    "path": "gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=\"\\\\\\\"\\\\\\\"\"\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "license/license.txt",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n"
  },
  {
    "path": "license/licenseHeaderDefinition.xml",
    "content": "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n<additionalHeaders>\n    <javadoc_style>\n        <firstLineDetectionPattern>(\\s|\\t)*/\\*.*$</firstLineDetectionPattern>\n        <lastLineDetectionPattern>.*\\*/(\\s|\\t)*$</lastLineDetectionPattern>\n        <allowBlankLines>false</allowBlankLines>\n        <isMultiline>true</isMultiline>\n        <padLines>false</padLines>\n    </javadoc_style>\n</additionalHeaders>"
  },
  {
    "path": "platform/platform-resources/build.gradle",
    "content": "import org.apache.tools.ant.filters.PrefixLines\nimport org.apache.tools.ant.filters.ReplaceTokens\nimport org.bonitasoft.engine.gradle.PomUtils\n\nplugins { id 'distribution' }\n\ngroup = 'org.bonitasoft.platform'\ndescription = ''\n\ndependencies {\n    implementation platform(libs.jacksonBom)\n\n    annotationProcessor libs.lombok\n    compileOnly libs.lombok\n    api libs.springTx\n    api libs.springJdbc\n    api libs.springContext\n    api libs.slf4jApi\n    api libs.commonsIO\n    api libs.springBootAutoconfigure\n    implementation \"com.fasterxml.jackson.core:jackson-databind\"\n\n    testImplementation libs.assertj\n    testImplementation libs.mockitoCore\n    testImplementation libs.logback\n}\n\n\nprocessResources {\n    inputs.property(\"version\", project.version)\n    from('src/main/resources') {\n        include 'PLATFORM_ENGINE_VERSION'\n        filter(ReplaceTokens, tokens: [version: project.version])\n    }\n    from(\"${projectDir}/../../bpm/bonita-core/bonita-process-engine/src/main/resources/bonita-platform-community.properties\") {\n        filter(PrefixLines, prefix: \"#\")\n        rename 'bonita-platform-community.properties', 'platform_engine/bonita-platform-community-custom.properties'\n    }\n    from(\"${projectDir}/../../bpm/bonita-core/bonita-process-engine/src/main/resources/bonita-tenant-community.properties\") {\n        filter(PrefixLines, prefix: \"#\")\n        rename 'bonita-tenant-community.properties', 'tenant_engine/bonita-tenant-community-custom.properties'\n    }\n}\n\nconfigurations { distributionZip }\n\ntasks.register(\"sourcesJar\", Jar) {\n    from sourceSets.main.allJava\n    archiveClassifier = 'sources'\n}\n\ndistTar.enabled = false\ndistributions {\n    main {\n        contents { from sourceSets.main.output }\n    }\n}\n\nartifacts { distributionZip distZip }\n\npublishing {\n    publications {\n        mavenJava(MavenPublication) {\n            from project.components.java\n            artifact distZip\n            artifact project.sourcesJar\n            pom { pom ->\n                name = \"Bonita Platform Resources\"\n                description = \"Bonita Platform Resources serves to setup Bonita platform\"\n                PomUtils.pomCommunityPublication(pom)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/ConfigurationService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration;\n\nimport java.io.File;\nimport java.nio.file.Path;\nimport java.util.List;\n\nimport org.bonitasoft.platform.configuration.model.BonitaConfiguration;\nimport org.bonitasoft.platform.configuration.model.FullBonitaConfiguration;\nimport org.bonitasoft.platform.configuration.model.LightBonitaConfiguration;\nimport org.bonitasoft.platform.exception.PlatformException;\n\n/**\n * Give access to Bonita Platform configuration.\n * Is used by setup mechanism to retrieve configuration before running the Engine + Portal, on a system that does give\n * access to a persistent filesystem.\n *\n * @author Emmanuel Duchastenier\n */\npublic interface ConfigurationService {\n\n    /**\n     * Retrieves the portal configuration at platform-level.\n     *\n     * @return a list of BonitaConfiguration that represents each file\n     */\n    List<BonitaConfiguration> getPlatformPortalConf();\n\n    /**\n     * Retrieves the platform configuration at platform level.\n     *\n     * @return a list of BonitaConfiguration that represents each file\n     */\n    List<BonitaConfiguration> getPlatformEngineConf();\n\n    /**\n     * Retrieves the engine tenant configuration for a tenant\n     *\n     * @return a list of BonitaConfiguration that represents each file\n     */\n\n    List<BonitaConfiguration> getTenantEngineConf();\n\n    /**\n     * Retrieves the security scripts for a tenant\n     *\n     * @return a list of BonitaConfiguration that represents each file\n     */\n\n    List<BonitaConfiguration> getTenantSecurityScripts();\n\n    /**\n     * store security script for a tenant\n     *\n     * @param bonitaConfigurations list of files\n     */\n    void storeTenantSecurityScripts(List<BonitaConfiguration> bonitaConfigurations);\n\n    /**\n     * store tenant configuration files for portal\n     *\n     * @param bonitaConfigurations list of files\n     */\n    void storeTenantPortalConf(List<BonitaConfiguration> bonitaConfigurations);\n\n    /**\n     * updates tenant configurations for portal, for all tenants and for tenant template.\n     *\n     * @param bonitaConfigurations list of configurations to store\n     */\n    void updateTenantPortalConf(List<BonitaConfiguration> bonitaConfigurations);\n\n    void updateDefaultConfiguration(Path configurationRootFolder) throws PlatformException;\n\n    /**\n     * Retrieves the portal configuration for a tenant\n     *\n     * @return list of files\n     */\n    List<BonitaConfiguration> getTenantPortalConf();\n\n    /**\n     * Retrieves a portal configuration file for a tenant\n     */\n    BonitaConfiguration getTenantPortalConfiguration(String file);\n\n    /**\n     * store platform configuration file in database\n     *\n     * @param bonitaConfigurations list of files\n     */\n    void storePlatformEngineConf(List<BonitaConfiguration> bonitaConfigurations);\n\n    /**\n     * store platform configuration files for engine\n     *\n     * @param configurationRootFolder root folder containing configuration files\n     */\n    void storePlatformConfiguration(File configurationRootFolder) throws PlatformException;\n\n    /**\n     * store whole configuration files for engine and portal, excluding licenses files\n     *\n     * @param configurationRootFolder path to root folder\n     */\n    void storeAllConfiguration(Path configurationRootFolder) throws PlatformException;\n\n    /**\n     * write all configuration files\n     * directory structure :\n     * .\n     * ├── platform_engine\n     * ├── platform_portal\n     * ├── tenant_engine\n     * ├── tenant_portal\n     * └── tenant_security_scripts\n     */\n    List<File> writeAllConfigurationToFolder(File configurationFolder, File licenseFolder) throws PlatformException;\n\n    /**\n     * read licensesFolder for license files\n     * sub-folders are ignored\n     * each *.lic file is stored in database\n     */\n    void storeLicenses(File licensesFolder) throws PlatformException;\n\n    /**\n     * Retrieves all license files stored in database.\n     *\n     * @return a list of BonitaConfiguration that represents each license file\n     */\n    List<BonitaConfiguration> getLicenses() throws PlatformException;\n\n    /**\n     * Delete all configuration and license files\n     */\n    void deleteAllConfiguration();\n\n    List<LightBonitaConfiguration> getMandatoryStructureConfiguration();\n\n    void storeConfigurationsIfNotExist(List<FullBonitaConfiguration> configurations);\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/impl/BonitaAllConfigurationContentTypeCleaner.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.impl;\n\nimport java.sql.PreparedStatement;\nimport java.sql.SQLException;\nimport java.util.List;\n\nimport org.bonitasoft.platform.configuration.model.FullBonitaConfiguration;\nimport org.springframework.jdbc.core.BatchPreparedStatementSetter;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class BonitaAllConfigurationContentTypeCleaner implements BatchPreparedStatementSetter {\n\n    public static final String DELETE_CONFIGURATION = \"DELETE from configuration where content_type = ? and resource_name = ? \";\n\n    private final List<FullBonitaConfiguration> bonitaConfigurations;\n\n    public BonitaAllConfigurationContentTypeCleaner(List<FullBonitaConfiguration> bonitaConfigurations) {\n        this.bonitaConfigurations = bonitaConfigurations;\n\n    }\n\n    @Override\n    public void setValues(PreparedStatement ps, int i) throws SQLException {\n        final FullBonitaConfiguration bonitaConfiguration = bonitaConfigurations.get(i);\n        ps.setString(1, bonitaConfiguration.getConfigurationType());\n        ps.setString(2, bonitaConfiguration.getResourceName());\n\n    }\n\n    @Override\n    public int getBatchSize() {\n        return bonitaConfigurations.size();\n    }\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/impl/BonitaAllConfigurationPreparedStatementSetter.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.impl;\n\nimport java.sql.PreparedStatement;\nimport java.sql.SQLException;\nimport java.util.List;\n\nimport org.bonitasoft.platform.configuration.model.FullBonitaConfiguration;\nimport org.bonitasoft.platform.database.DatabaseVendor;\nimport org.bonitasoft.platform.setup.PlatformSetup;\nimport org.springframework.jdbc.core.BatchPreparedStatementSetter;\nimport org.springframework.jdbc.support.lob.TemporaryLobCreator;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class BonitaAllConfigurationPreparedStatementSetter\n        implements BatchPreparedStatementSetter, ConfigurationColumns {\n\n    public static final String INSERT_CONFIGURATION = \"INSERT into configuration(content_type, resource_name, resource_content) values (?,?,?)\";\n    private final List<FullBonitaConfiguration> bonitaConfigurations;\n\n    private final String dbVendor;\n\n    public BonitaAllConfigurationPreparedStatementSetter(List<FullBonitaConfiguration> bonitaConfigurations,\n            String dbVendor) {\n        this.bonitaConfigurations = bonitaConfigurations;\n        this.dbVendor = dbVendor == null ? PlatformSetup.getPropertyBonitaDbVendor() : dbVendor;\n    }\n\n    @Override\n    public void setValues(PreparedStatement ps, int i) throws SQLException {\n        final FullBonitaConfiguration bonitaConfiguration = bonitaConfigurations.get(i);\n        ps.setString(COLUMN_INDEX_TYPE, bonitaConfiguration.getConfigurationType());\n        ps.setString(COLUMN_INDEX_RESOURCE_NAME, bonitaConfiguration.getResourceName());\n        switch (DatabaseVendor.parseValue(dbVendor)) {\n            case H2, POSTGRES:\n                ps.setBytes(COLUMN_INDEX_RESOURCE_CONTENT, bonitaConfiguration.getResourceContent());\n                break;\n            case ORACLE, MYSQL, SQLSERVER:\n                TemporaryLobCreator temporaryLobCreator = new TemporaryLobCreator();\n                temporaryLobCreator.setBlobAsBytes(ps, COLUMN_INDEX_RESOURCE_CONTENT,\n                        bonitaConfiguration.getResourceContent());\n                break;\n            default:\n                throw new IllegalArgumentException(\"unsupported db vendor:\" + dbVendor);\n        }\n\n    }\n\n    @Override\n    public int getBatchSize() {\n        return bonitaConfigurations.size();\n    }\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/impl/BonitaConfigurationCleaner.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.impl;\n\nimport java.sql.PreparedStatement;\n\nimport org.springframework.jdbc.core.BatchPreparedStatementSetter;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class BonitaConfigurationCleaner implements BatchPreparedStatementSetter {\n\n    public static final String DELETE_ALL_CONFIGURATION = \"DELETE FROM configuration\";\n\n    @Override\n    public void setValues(PreparedStatement preparedStatement, int i) {\n    }\n\n    @Override\n    public int getBatchSize() {\n        return 1;\n    }\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/impl/BonitaConfigurationContentTypeCleaner.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.impl;\n\nimport java.sql.PreparedStatement;\nimport java.sql.SQLException;\n\nimport org.bonitasoft.platform.configuration.type.ConfigurationType;\nimport org.springframework.jdbc.core.BatchPreparedStatementSetter;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class BonitaConfigurationContentTypeCleaner implements BatchPreparedStatementSetter {\n\n    public static final String DELETE_CONFIGURATION = \"DELETE from configuration where content_type = ? \";\n\n    private final ConfigurationType type;\n\n    public BonitaConfigurationContentTypeCleaner(ConfigurationType type) {\n        this.type = type;\n    }\n\n    @Override\n    public void setValues(PreparedStatement ps, int i) throws SQLException {\n        ps.setString(1, type.toString());\n\n    }\n\n    @Override\n    public int getBatchSize() {\n        return 1;\n    }\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/impl/BonitaConfigurationPreparedStatementCleaner.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.impl;\n\nimport java.sql.PreparedStatement;\nimport java.sql.SQLException;\nimport java.util.List;\n\nimport org.bonitasoft.platform.configuration.model.BonitaConfiguration;\nimport org.bonitasoft.platform.configuration.type.ConfigurationType;\nimport org.springframework.jdbc.core.BatchPreparedStatementSetter;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class BonitaConfigurationPreparedStatementCleaner implements BatchPreparedStatementSetter {\n\n    public static final String DELETE_CONFIGURATION = \"DELETE from configuration where content_type = ? and resource_name = ?\";\n\n    private final List<BonitaConfiguration> bonitaConfigurations;\n    private final ConfigurationType type;\n\n    public BonitaConfigurationPreparedStatementCleaner(List<BonitaConfiguration> bonitaConfigurations,\n            ConfigurationType type) {\n        this.bonitaConfigurations = bonitaConfigurations;\n        this.type = type;\n    }\n\n    @Override\n    public void setValues(PreparedStatement ps, int i) throws SQLException {\n        final BonitaConfiguration bonitaConfiguration = bonitaConfigurations.get(i);\n        ps.setString(1, type.toString());\n        ps.setString(2, bonitaConfiguration.getResourceName());\n\n    }\n\n    @Override\n    public int getBatchSize() {\n        return bonitaConfigurations.size();\n    }\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/impl/BonitaConfigurationPreparedStatementSetter.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.impl;\n\nimport java.sql.PreparedStatement;\nimport java.sql.SQLException;\nimport java.util.List;\n\nimport org.bonitasoft.platform.configuration.model.BonitaConfiguration;\nimport org.bonitasoft.platform.configuration.type.ConfigurationType;\nimport org.bonitasoft.platform.database.DatabaseVendor;\nimport org.bonitasoft.platform.setup.PlatformSetup;\nimport org.springframework.jdbc.core.BatchPreparedStatementSetter;\nimport org.springframework.jdbc.support.lob.TemporaryLobCreator;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class BonitaConfigurationPreparedStatementSetter implements BatchPreparedStatementSetter, ConfigurationColumns {\n\n    public static final String INSERT_CONFIGURATION = \"INSERT into configuration(content_type, resource_name, resource_content) values (?,?,?)\";\n    private final List<BonitaConfiguration> bonitaConfigurations;\n\n    private final String dbVendor;\n    private final ConfigurationType type;\n\n    public BonitaConfigurationPreparedStatementSetter(List<BonitaConfiguration> bonitaConfigurations, String dbVendor,\n            ConfigurationType type) {\n        this.bonitaConfigurations = bonitaConfigurations;\n        this.dbVendor = dbVendor == null ? PlatformSetup.getPropertyBonitaDbVendor() : dbVendor;\n        this.type = type;\n    }\n\n    @Override\n    public void setValues(PreparedStatement ps, int i) throws SQLException {\n        final BonitaConfiguration bonitaConfiguration = bonitaConfigurations.get(i);\n        ps.setString(COLUMN_INDEX_TYPE, type.toString());\n        ps.setString(COLUMN_INDEX_RESOURCE_NAME, bonitaConfiguration.getResourceName());\n        switch (DatabaseVendor.parseValue(dbVendor)) {\n            case H2, POSTGRES:\n                ps.setBytes(COLUMN_INDEX_RESOURCE_CONTENT, bonitaConfiguration.getResourceContent());\n                break;\n            case ORACLE, MYSQL, SQLSERVER:\n                new TemporaryLobCreator().setBlobAsBytes(ps, COLUMN_INDEX_RESOURCE_CONTENT,\n                        bonitaConfiguration.getResourceContent());\n                break;\n            default:\n                throw new IllegalArgumentException(\"unsupported db vendor:\" + dbVendor);\n        }\n\n    }\n\n    @Override\n    public int getBatchSize() {\n        return bonitaConfigurations.size();\n    }\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/impl/BonitaConfigurationRowMapper.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.impl;\n\nimport static org.bonitasoft.platform.configuration.impl.ConfigurationFields.RESOURCE_CONTENT;\nimport static org.bonitasoft.platform.configuration.impl.ConfigurationFields.RESOURCE_NAME;\n\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\n\nimport org.bonitasoft.platform.configuration.model.BonitaConfiguration;\nimport org.springframework.jdbc.core.RowMapper;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class BonitaConfigurationRowMapper implements RowMapper<BonitaConfiguration> {\n\n    public static final String SELECT_CONFIGURATION_FOR_TYPE = \"SELECT content_type, resource_name, resource_content FROM configuration WHERE content_type = ? ORDER BY resource_name\";\n    public static final String SELECT_CONFIGURATION = \"SELECT content_type, resource_name, resource_content FROM configuration WHERE content_type = ? AND resource_name = ?\";\n\n    @Override\n    public BonitaConfiguration mapRow(ResultSet rs, int rowNum) throws SQLException {\n        return new BonitaConfiguration(rs.getString(RESOURCE_NAME), rs.getBytes(RESOURCE_CONTENT));\n    }\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/impl/BonitaConfigurationTenantUpdater.java",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.impl;\n\nimport java.sql.PreparedStatement;\nimport java.sql.SQLException;\nimport java.util.List;\n\nimport org.bonitasoft.platform.configuration.model.BonitaConfiguration;\nimport org.bonitasoft.platform.configuration.type.ConfigurationType;\nimport org.bonitasoft.platform.database.DatabaseVendor;\nimport org.springframework.jdbc.core.BatchPreparedStatementSetter;\nimport org.springframework.jdbc.support.lob.TemporaryLobCreator;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class BonitaConfigurationTenantUpdater implements BatchPreparedStatementSetter {\n\n    public static final String UPDATE_ALL_TENANTS_CONFIGURATION = \"UPDATE configuration SET resource_content=? WHERE content_type=? AND resource_name=?\";\n\n    private final List<BonitaConfiguration> bonitaConfigurations;\n    private final String dbVendor;\n    private final ConfigurationType type;\n\n    public BonitaConfigurationTenantUpdater(List<BonitaConfiguration> bonitaConfigurations, String dbVendor,\n            ConfigurationType type) {\n        this.bonitaConfigurations = bonitaConfigurations;\n        this.dbVendor = dbVendor;\n        this.type = type;\n    }\n\n    @Override\n    public void setValues(PreparedStatement ps, int i) throws SQLException {\n        final BonitaConfiguration bonitaConfiguration = bonitaConfigurations.get(i);\n        ps.setString(2, type.toString());\n        ps.setString(3, bonitaConfiguration.getResourceName());\n        switch (DatabaseVendor.parseValue(dbVendor)) {\n            case H2, POSTGRES:\n                ps.setBytes(1, bonitaConfiguration.getResourceContent());\n                break;\n            case ORACLE, MYSQL, SQLSERVER:\n                new TemporaryLobCreator().setBlobAsBytes(ps, 1, bonitaConfiguration.getResourceContent());\n                break;\n            default:\n                throw new IllegalArgumentException(\"unsupported db vendor:\" + dbVendor);\n        }\n    }\n\n    @Override\n    public int getBatchSize() {\n        return bonitaConfigurations.size();\n    }\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/impl/ConfigurationColumns.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.impl;\n\n/**\n * @author Laurent Leseigneur\n */\npublic interface ConfigurationColumns {\n\n    int COLUMN_INDEX_TYPE = 1;\n    int COLUMN_INDEX_RESOURCE_NAME = 2;\n    int COLUMN_INDEX_RESOURCE_CONTENT = 3;\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/impl/ConfigurationFields.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.impl;\n\n/**\n * @author Laurent Leseigneur\n */\npublic interface ConfigurationFields {\n\n    String CONTENT_TYPE = \"content_type\";\n    String RESOURCE_NAME = \"resource_name\";\n    String RESOURCE_CONTENT = \"resource_content\";\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/impl/ConfigurationServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.impl;\n\nimport static org.bonitasoft.platform.configuration.type.ConfigurationType.*;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.commons.io.IOUtils;\nimport org.bonitasoft.platform.configuration.ConfigurationService;\nimport org.bonitasoft.platform.configuration.model.BonitaConfiguration;\nimport org.bonitasoft.platform.configuration.model.FullBonitaConfiguration;\nimport org.bonitasoft.platform.configuration.model.LightBonitaConfiguration;\nimport org.bonitasoft.platform.configuration.type.ConfigurationType;\nimport org.bonitasoft.platform.configuration.util.AllConfigurationResourceVisitor;\nimport org.bonitasoft.platform.configuration.util.AutoUpdateConfigurationVisitor;\nimport org.bonitasoft.platform.configuration.util.CleanAndStoreAllConfigurationInTransaction;\nimport org.bonitasoft.platform.configuration.util.CleanAndStoreConfigurationInTransaction;\nimport org.bonitasoft.platform.configuration.util.ConfigurationResourceVisitor;\nimport org.bonitasoft.platform.configuration.util.DeleteAllConfigurationInTransaction;\nimport org.bonitasoft.platform.configuration.util.GetAllConfigurationInTransaction;\nimport org.bonitasoft.platform.configuration.util.GetConfigurationInTransaction;\nimport org.bonitasoft.platform.configuration.util.GetConfigurationsInTransaction;\nimport org.bonitasoft.platform.configuration.util.GetMandatoryStructureConfiguration;\nimport org.bonitasoft.platform.configuration.util.LicensesResourceVisitor;\nimport org.bonitasoft.platform.configuration.util.StoreConfigurationInTransaction;\nimport org.bonitasoft.platform.configuration.util.StoreConfigurationsIfNotExist;\nimport org.bonitasoft.platform.configuration.util.UpdateConfigurationInTransaction;\nimport org.bonitasoft.platform.exception.PlatformException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.support.TransactionTemplate;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@Service\npublic class ConfigurationServiceImpl implements ConfigurationService {\n\n    public static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationServiceImpl.class);\n\n    private final JdbcTemplate jdbcTemplate;\n\n    private final TransactionTemplate transactionTemplate;\n\n    private final String dbVendor;\n\n    public ConfigurationServiceImpl(JdbcTemplate jdbcTemplate, TransactionTemplate transactionTemplate,\n            @Value(\"${db.vendor}\") String dbVendor) {\n        this.jdbcTemplate = jdbcTemplate;\n        this.transactionTemplate = transactionTemplate;\n        this.dbVendor = dbVendor;\n    }\n\n    @Override\n    public List<BonitaConfiguration> getPlatformPortalConf() {\n        return getBonitaConfigurations(PLATFORM_PORTAL);\n    }\n\n    @Override\n    public List<BonitaConfiguration> getPlatformEngineConf() {\n        return getBonitaConfigurations(PLATFORM_ENGINE);\n    }\n\n    @Override\n    public void storeTenantSecurityScripts(List<BonitaConfiguration> bonitaConfigurations) {\n        storeConfiguration(bonitaConfigurations, TENANT_SECURITY_SCRIPTS);\n    }\n\n    @Override\n    public void storePlatformConfiguration(File configurationRootFolder) throws PlatformException {\n        storeConfiguration(configurationRootFolder, PLATFORM_ENGINE);\n    }\n\n    @Override\n    public void storeAllConfiguration(Path configurationRootFolder) throws PlatformException {\n        List<FullBonitaConfiguration> fullBonitaConfigurations = new ArrayList<>();\n        AllConfigurationResourceVisitor allConfigurationResourceVisitor = new AllConfigurationResourceVisitor(\n                fullBonitaConfigurations);\n        try {\n            Files.walkFileTree(configurationRootFolder, allConfigurationResourceVisitor);\n            transactionTemplate.execute(\n                    new CleanAndStoreAllConfigurationInTransaction(jdbcTemplate, dbVendor, fullBonitaConfigurations));\n        } catch (IOException e) {\n            throw new PlatformException(e);\n        }\n    }\n\n    @Override\n    public void updateDefaultConfiguration(Path configurationRootFolder)\n            throws PlatformException {\n        List<BonitaConfiguration> bonitaConfigurations = new ArrayList<>();\n        try {\n            Files.walkFileTree(configurationRootFolder, new AutoUpdateConfigurationVisitor(bonitaConfigurations));\n        } catch (IOException e) {\n            throw new PlatformException(e);\n        }\n        updateTenantPortalConf(bonitaConfigurations);\n    }\n\n    @Override\n    public void storeTenantPortalConf(List<BonitaConfiguration> bonitaConfigurations) {\n        storeConfiguration(bonitaConfigurations, TENANT_PORTAL);\n    }\n\n    @Override\n    public void updateTenantPortalConf(List<BonitaConfiguration> bonitaConfigurations) {\n        // update default configuration at TENANT_PORTAL level:\n        transactionTemplate.execute(\n                new UpdateConfigurationInTransaction(jdbcTemplate, dbVendor, bonitaConfigurations, TENANT_PORTAL));\n    }\n\n    @Override\n    public List<BonitaConfiguration> getTenantPortalConf() {\n        return getBonitaConfigurations(TENANT_PORTAL);\n    }\n\n    @Override\n    public BonitaConfiguration getTenantPortalConfiguration(String file) {\n        return getBonitaConfiguration(TENANT_PORTAL, file);\n    }\n\n    @Override\n    public List<File> writeAllConfigurationToFolder(File configurationFolder, File licenseFolder)\n            throws PlatformException {\n        FolderResolver folderResolver = new FolderResolver(configurationFolder.toPath(), licenseFolder.toPath());\n        List<File> writtenFiles = new ArrayList<>();\n        for (FullBonitaConfiguration fullBonitaConfiguration : getAllConfiguration()) {\n            File confFile = new File(folderResolver.getFolder(fullBonitaConfiguration),\n                    fullBonitaConfiguration.getResourceName());\n            writtenFiles.add(confFile);\n            LOGGER.debug(String.format(\"writing file %s to folder %s\", confFile.getName(),\n                    confFile.getParentFile().getAbsolutePath()));\n            try (FileOutputStream output = new FileOutputStream(confFile)) {\n                IOUtils.write(fullBonitaConfiguration.getResourceContent(), output);\n            } catch (IOException e) {\n                throw new PlatformException(e);\n            }\n        }\n        return writtenFiles;\n    }\n\n    protected List<FullBonitaConfiguration> getAllConfiguration() {\n        return transactionTemplate.execute(new GetAllConfigurationInTransaction(jdbcTemplate));\n    }\n\n    private void storeConfiguration(File configurationRootFolder, ConfigurationType type)\n            throws PlatformException {\n        final Path path = configurationRootFolder.toPath();\n        List<BonitaConfiguration> bonitaConfigurations = new ArrayList<>();\n        ConfigurationResourceVisitor configurationResourceVisitor = new ConfigurationResourceVisitor(\n                bonitaConfigurations);\n        try {\n            Files.walkFileTree(path, configurationResourceVisitor);\n            storeConfiguration(bonitaConfigurations, type);\n        } catch (IOException e) {\n            throw new PlatformException(e);\n        }\n    }\n\n    // Needed by Bonita Central\n    @Override\n    public void storePlatformEngineConf(List<BonitaConfiguration> bonitaConfigurations) {\n        storeConfiguration(bonitaConfigurations, PLATFORM_ENGINE);\n    }\n\n    private void storeConfiguration(List<BonitaConfiguration> bonitaConfigurations, ConfigurationType type) {\n        transactionTemplate.execute(\n                new StoreConfigurationInTransaction(jdbcTemplate, dbVendor, bonitaConfigurations, type));\n    }\n\n    private void cleanAndStoreLicenseConfiguration(List<BonitaConfiguration> bonitaConfigurations) {\n        transactionTemplate.execute(new CleanAndStoreConfigurationInTransaction(jdbcTemplate, dbVendor,\n                bonitaConfigurations, ConfigurationType.LICENSES));\n    }\n\n    @Override\n    public List<BonitaConfiguration> getTenantEngineConf() {\n        return getBonitaConfigurations(TENANT_ENGINE);\n    }\n\n    List<BonitaConfiguration> getBonitaConfigurations(ConfigurationType type) {\n        return transactionTemplate.execute(new GetConfigurationsInTransaction(jdbcTemplate, type));\n    }\n\n    @Override\n    public List<BonitaConfiguration> getTenantSecurityScripts() {\n        return getBonitaConfigurations(TENANT_SECURITY_SCRIPTS);\n    }\n\n    private BonitaConfiguration getBonitaConfiguration(ConfigurationType type, String resourceName) {\n        return transactionTemplate\n                .execute(new GetConfigurationInTransaction(jdbcTemplate, type, resourceName));\n    }\n\n    @Override\n    public void storeLicenses(File licensesFolder) throws PlatformException {\n        final Path path = licensesFolder.toPath();\n        List<BonitaConfiguration> bonitaConfigurations = new ArrayList<>();\n        LicensesResourceVisitor licensesResourceVisitor = new LicensesResourceVisitor(bonitaConfigurations);\n        try {\n            Files.walkFileTree(path, licensesResourceVisitor);\n            cleanAndStoreLicenseConfiguration(bonitaConfigurations);\n        } catch (IOException e) {\n            throw new PlatformException(e);\n        }\n    }\n\n    @Override\n    public List<BonitaConfiguration> getLicenses() {\n        return getBonitaConfigurations(LICENSES);\n    }\n\n    @Override\n    public void deleteAllConfiguration() {\n        transactionTemplate.execute(new DeleteAllConfigurationInTransaction(jdbcTemplate));\n    }\n\n    @Override\n    public List<LightBonitaConfiguration> getMandatoryStructureConfiguration() {\n        return transactionTemplate.execute(new GetMandatoryStructureConfiguration(jdbcTemplate));\n    }\n\n    @Override\n    public void storeConfigurationsIfNotExist(List<FullBonitaConfiguration> configurations) {\n        transactionTemplate.execute(new StoreConfigurationsIfNotExist(jdbcTemplate, dbVendor, configurations));\n    }\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/impl/FolderResolver.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.impl;\n\nimport java.io.File;\nimport java.nio.file.Path;\n\nimport org.bonitasoft.platform.configuration.model.FullBonitaConfiguration;\n\n/**\n * utility class to map configuration files and licenses to pulling folder.\n * <ul>\n * <li>license files are pulled to licenseFolder</li>\n * <li>other files are pulled to configurationFolder/CONFIGURATION_TYPE</li>\n * </ul>\n *\n * @author Laurent Leseigneur\n */\npublic class FolderResolver {\n\n    private final Path configurationFolder;\n    private final Path licenseFolder;\n\n    public FolderResolver(Path configurationFolder, Path licenseFolder) {\n\n        this.configurationFolder = configurationFolder;\n        this.licenseFolder = licenseFolder;\n    }\n\n    public File getFolder(FullBonitaConfiguration fullBonitaConfiguration) {\n        File confFolder = resolveFolder(fullBonitaConfiguration).toFile();\n        confFolder.mkdirs();\n        return confFolder;\n    }\n\n    private Path resolveSubFolder(Path rootPath, FullBonitaConfiguration fullBonitaConfiguration) {\n        if (fullBonitaConfiguration.isLicenseFile()) {\n            return rootPath;\n        }\n        return rootPath.resolve(fullBonitaConfiguration.getConfigurationType().toLowerCase());\n    }\n\n    private Path resolveFolder(FullBonitaConfiguration fullBonitaConfiguration) {\n        if (fullBonitaConfiguration.isLicenseFile()) {\n            return resolveSubFolder(licenseFolder, fullBonitaConfiguration);\n        }\n        return resolveSubFolder(configurationFolder, fullBonitaConfiguration);\n    }\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/impl/FullBonitaConfigurationRowMapper.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.impl;\n\nimport static org.bonitasoft.platform.configuration.impl.ConfigurationFields.*;\n\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\n\nimport org.bonitasoft.platform.configuration.model.FullBonitaConfiguration;\nimport org.springframework.jdbc.core.RowMapper;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class FullBonitaConfigurationRowMapper implements RowMapper<FullBonitaConfiguration> {\n\n    public static final String SELECT_CONFIGURATION = \"SELECT content_type, resource_name, resource_content FROM configuration ORDER BY content_type, resource_name\";\n\n    @Override\n    public FullBonitaConfiguration mapRow(ResultSet rs, int rowNum) throws SQLException {\n        return new FullBonitaConfiguration(rs.getString(RESOURCE_NAME), rs.getBytes(RESOURCE_CONTENT),\n                rs.getString(CONTENT_TYPE));\n    }\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/model/BonitaConfiguration.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.model;\n\nimport java.io.Serializable;\nimport java.util.Arrays;\nimport java.util.Objects;\n\nimport lombok.Getter;\nimport lombok.Setter;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@Setter\n@Getter\npublic class BonitaConfiguration implements Serializable {\n\n    private String resourceName;\n    private byte[] resourceContent;\n\n    public BonitaConfiguration(String resourceName, byte[] resourceContent) {\n        this.resourceName = resourceName;\n        this.resourceContent = resourceContent;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (o == null || getClass() != o.getClass())\n            return false;\n        BonitaConfiguration that = (BonitaConfiguration) o;\n        return Objects.equals(resourceName, that.resourceName) && Arrays.equals(resourceContent, that.resourceContent);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(resourceName, Arrays.hashCode(resourceContent));\n    }\n\n    @Override\n    public String toString() {\n        return \"BonitaConfiguration{resourceName='\" + resourceName + \"'}\";\n    }\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/model/FullBonitaConfiguration.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.model;\n\nimport lombok.EqualsAndHashCode;\nimport lombok.Getter;\nimport org.bonitasoft.platform.configuration.type.ConfigurationType;\n\n/**\n * @author Laurent Leseigneur\n */\n@Getter\n@EqualsAndHashCode(callSuper = true)\npublic class FullBonitaConfiguration extends BonitaConfiguration {\n\n    private final String configurationType;\n\n    public FullBonitaConfiguration(String resourceName, byte[] resourceContent, String configurationType) {\n        super(resourceName, resourceContent);\n        this.configurationType = configurationType;\n    }\n\n    public boolean isLicenseFile() {\n        return getConfigurationType().equals(ConfigurationType.LICENSES.name());\n    }\n\n    @Override\n    public String toString() {\n        return String.format(\"FullBonitaConfiguration{ resourceName='%s' , configurationType='%s' }\",\n                getResourceName(), getConfigurationType());\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/model/LightBonitaConfiguration.java",
    "content": "/**\n * Copyright (C) 2018 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.model;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic record LightBonitaConfiguration(String type) {\n\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/type/ConfigurationType.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.type;\n\npublic enum ConfigurationType {\n    LICENSES, PLATFORM_PORTAL, PLATFORM_ENGINE, TENANT_PORTAL, TENANT_ENGINE, TENANT_SECURITY_SCRIPTS\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/util/AllConfigurationResourceVisitor.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.util;\n\nimport static org.bonitasoft.platform.configuration.type.ConfigurationType.*;\n\nimport java.io.IOException;\nimport java.nio.file.FileVisitResult;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.SimpleFileVisitor;\nimport java.nio.file.attribute.BasicFileAttributes;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.platform.configuration.model.FullBonitaConfiguration;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class AllConfigurationResourceVisitor extends SimpleFileVisitor<Path> {\n\n    private final List<FullBonitaConfiguration> fullBonitaConfigurations;\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(AllConfigurationResourceVisitor.class);\n\n    private static final List<String> CONFIGURATION_FOLDERS = Arrays.asList(PLATFORM_PORTAL.name().toLowerCase(),\n            PLATFORM_ENGINE.name().toLowerCase(), TENANT_PORTAL.name().toLowerCase(),\n            TENANT_ENGINE.name().toLowerCase(), TENANT_SECURITY_SCRIPTS.name().toLowerCase());\n\n    public AllConfigurationResourceVisitor(List<FullBonitaConfiguration> fullBonitaConfigurations) {\n        this.fullBonitaConfigurations = fullBonitaConfigurations;\n    }\n\n    @Override\n    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {\n        return FileVisitResult.CONTINUE;\n    }\n\n    private String getFolderName(Path dir) {\n        return dir.getFileName().toString().toUpperCase();\n    }\n\n    private boolean isConfigurationFolder(Path dir) {\n        return CONFIGURATION_FOLDERS.contains(dir.getFileName().toString());\n    }\n\n    @Override\n    public FileVisitResult visitFile(Path path, BasicFileAttributes basicFileAttributes) throws IOException {\n        if (isConfigurationFile(path)) {\n            final String configurationType = getFolderName(path.getParent());\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(\"found file: {}/{}\", configurationType.toLowerCase(), path.getFileName());\n            }\n            fullBonitaConfigurations.add(new FullBonitaConfiguration(path.getFileName().toString(),\n                    Files.readAllBytes(path), configurationType));\n        }\n        return FileVisitResult.CONTINUE;\n    }\n\n    private boolean isConfigurationFile(Path path) {\n        return path.toFile().isFile() && isConfigurationFolder(path.getParent());\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/util/AutoUpdateConfigurationVisitor.java",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.util;\n\nimport java.io.IOException;\nimport java.nio.file.FileVisitResult;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.SimpleFileVisitor;\nimport java.nio.file.attribute.BasicFileAttributes;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.platform.configuration.model.BonitaConfiguration;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class AutoUpdateConfigurationVisitor extends SimpleFileVisitor<Path> {\n\n    private final List<BonitaConfiguration> bonitaConfigurations;\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(AutoUpdateConfigurationVisitor.class);\n\n    public AutoUpdateConfigurationVisitor(List<BonitaConfiguration> bonitaConfigurations) {\n        this.bonitaConfigurations = bonitaConfigurations;\n    }\n\n    private static final List<String> AUTO_UPDATE_CONFIGURATION_FILES = Arrays.asList(\n            \"compound-permissions-mapping.properties\",\n            \"dynamic-permissions-checks.properties\",\n            \"resources-permissions-mapping.properties\",\n            \"user-creation-attribute-mapping.properties\");\n\n    @Override\n    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attributes) {\n        return FileVisitResult.CONTINUE;\n    }\n\n    private String getFolderName(Path dir) {\n        return dir.getFileName().toString().toUpperCase();\n    }\n\n    @Override\n    public FileVisitResult visitFile(Path path, BasicFileAttributes basicFileAttributes) throws IOException {\n        if (isAutoUpdateConfigurationFile(path)) {\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(buildMessage(path, getFolderName(path.getParent())));\n            }\n            bonitaConfigurations.add(new BonitaConfiguration(path.getFileName().toString(), Files.readAllBytes(path)));\n        }\n        return FileVisitResult.CONTINUE;\n    }\n\n    private String buildMessage(Path path, String configurationType) {\n        return \"found file: \" + configurationType.toLowerCase() + \"/\" + path.getFileName();\n    }\n\n    boolean isAutoUpdateConfigurationFile(Path path) {\n        return path.toFile().isFile() && AUTO_UPDATE_CONFIGURATION_FILES.contains(path.getFileName().toString());\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/util/CleanAndStoreAllConfigurationInTransaction.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.util;\n\nimport java.util.List;\n\nimport org.bonitasoft.platform.configuration.impl.BonitaAllConfigurationContentTypeCleaner;\nimport org.bonitasoft.platform.configuration.impl.BonitaAllConfigurationPreparedStatementSetter;\nimport org.bonitasoft.platform.configuration.impl.ConfigurationServiceImpl;\nimport org.bonitasoft.platform.configuration.model.FullBonitaConfiguration;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.transaction.TransactionStatus;\nimport org.springframework.transaction.support.TransactionCallbackWithoutResult;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class CleanAndStoreAllConfigurationInTransaction extends TransactionCallbackWithoutResult {\n\n    private final JdbcTemplate jdbcTemplate;\n    private final List<FullBonitaConfiguration> bonitaConfigurations;\n    private final String dbVendor;\n\n    private final static org.slf4j.Logger LOGGER = LoggerFactory.getLogger(ConfigurationServiceImpl.class);\n\n    public CleanAndStoreAllConfigurationInTransaction(JdbcTemplate jdbcTemplate, String dbVendor,\n            List<FullBonitaConfiguration> bonitaConfigurations) {\n\n        this.jdbcTemplate = jdbcTemplate;\n        this.dbVendor = dbVendor;\n        this.bonitaConfigurations = bonitaConfigurations;\n    }\n\n    @Override\n    protected void doInTransactionWithoutResult(TransactionStatus status) {\n        LOGGER.debug(\"delete existing configurations {}\", bonitaConfigurations);\n\n        jdbcTemplate.batchUpdate(BonitaAllConfigurationContentTypeCleaner.DELETE_CONFIGURATION,\n                new BonitaAllConfigurationContentTypeCleaner(bonitaConfigurations));\n\n        jdbcTemplate.batchUpdate(BonitaAllConfigurationPreparedStatementSetter.INSERT_CONFIGURATION,\n                new BonitaAllConfigurationPreparedStatementSetter(bonitaConfigurations, dbVendor));\n\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/util/CleanAndStoreConfigurationInTransaction.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.util;\n\nimport java.util.List;\n\nimport org.bonitasoft.platform.configuration.impl.BonitaConfigurationContentTypeCleaner;\nimport org.bonitasoft.platform.configuration.impl.BonitaConfigurationPreparedStatementSetter;\nimport org.bonitasoft.platform.configuration.impl.ConfigurationServiceImpl;\nimport org.bonitasoft.platform.configuration.model.BonitaConfiguration;\nimport org.bonitasoft.platform.configuration.type.ConfigurationType;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.transaction.TransactionStatus;\nimport org.springframework.transaction.support.TransactionCallbackWithoutResult;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class CleanAndStoreConfigurationInTransaction extends TransactionCallbackWithoutResult {\n\n    private final JdbcTemplate jdbcTemplate;\n    private final List<BonitaConfiguration> bonitaConfigurations;\n    private final ConfigurationType type;\n    private final String dbVendor;\n\n    private final static org.slf4j.Logger LOGGER = LoggerFactory.getLogger(ConfigurationServiceImpl.class);\n\n    public CleanAndStoreConfigurationInTransaction(JdbcTemplate jdbcTemplate, String dbVendor,\n            List<BonitaConfiguration> bonitaConfigurations, ConfigurationType type) {\n        this.jdbcTemplate = jdbcTemplate;\n        this.dbVendor = dbVendor;\n        this.bonitaConfigurations = bonitaConfigurations;\n        this.type = type;\n    }\n\n    @Override\n    protected void doInTransactionWithoutResult(TransactionStatus status) {\n        LOGGER.debug(\"delete existing configurations for type:{}\", type.name());\n\n        jdbcTemplate.batchUpdate(BonitaConfigurationContentTypeCleaner.DELETE_CONFIGURATION,\n                new BonitaConfigurationContentTypeCleaner(type));\n\n        jdbcTemplate.batchUpdate(BonitaConfigurationPreparedStatementSetter.INSERT_CONFIGURATION,\n                new BonitaConfigurationPreparedStatementSetter(bonitaConfigurations, dbVendor, type));\n\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/util/ConfigurationResourceVisitor.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.util;\n\nimport java.io.IOException;\nimport java.nio.file.FileVisitResult;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.SimpleFileVisitor;\nimport java.nio.file.attribute.BasicFileAttributes;\nimport java.util.List;\n\nimport org.bonitasoft.platform.configuration.model.BonitaConfiguration;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class ConfigurationResourceVisitor extends SimpleFileVisitor<Path> {\n\n    private final List<BonitaConfiguration> bonitaConfigurations;\n\n    private final static Logger LOGGER = LoggerFactory.getLogger(ConfigurationResourceVisitor.class);\n\n    public ConfigurationResourceVisitor(List<BonitaConfiguration> bonitaConfigurations) {\n        this.bonitaConfigurations = bonitaConfigurations;\n    }\n\n    @Override\n    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {\n        return FileVisitResult.CONTINUE;\n    }\n\n    @Override\n    public FileVisitResult visitFile(Path path, BasicFileAttributes basicFileAttributes) throws IOException {\n        LOGGER.info(\"found file {}\", path.getFileName());\n        bonitaConfigurations.add(new BonitaConfiguration(path.getFileName().toString(), Files.readAllBytes(path)));\n        return FileVisitResult.CONTINUE;\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/util/DeleteAllConfigurationInTransaction.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.util;\n\nimport org.bonitasoft.platform.configuration.impl.BonitaConfigurationCleaner;\nimport org.bonitasoft.platform.configuration.impl.ConfigurationServiceImpl;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.transaction.TransactionStatus;\nimport org.springframework.transaction.support.TransactionCallbackWithoutResult;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class DeleteAllConfigurationInTransaction extends TransactionCallbackWithoutResult {\n\n    private final JdbcTemplate jdbcTemplate;\n\n    private final static org.slf4j.Logger LOGGER = LoggerFactory.getLogger(ConfigurationServiceImpl.class);\n\n    public DeleteAllConfigurationInTransaction(JdbcTemplate jdbcTemplate) {\n        this.jdbcTemplate = jdbcTemplate;\n    }\n\n    @Override\n    protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {\n        LOGGER.debug(\"Execute DeleteAllConfigurationInTransaction transaction.\");\n        jdbcTemplate.batchUpdate(BonitaConfigurationCleaner.DELETE_ALL_CONFIGURATION,\n                new BonitaConfigurationCleaner());\n\n    }\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/util/FlattenFolderVisitor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.util;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.FileVisitResult;\nimport java.nio.file.Path;\nimport java.nio.file.SimpleFileVisitor;\nimport java.nio.file.attribute.BasicFileAttributes;\nimport java.util.Map;\nimport java.util.Objects;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class FlattenFolderVisitor extends SimpleFileVisitor<Path> {\n\n    private final Map<String, File> flatFileMap;\n\n    public FlattenFolderVisitor(Map<String, File> flatFileMap) {\n        this.flatFileMap = flatFileMap;\n    }\n\n    @Override\n    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {\n        return FileVisitResult.CONTINUE;\n    }\n\n    @Override\n    public FileVisitResult visitFile(Path path, BasicFileAttributes basicFileAttributes) throws IOException {\n        Objects.requireNonNull(path);\n        Objects.requireNonNull(basicFileAttributes);\n        final File file = path.toFile();\n        if (file.isFile()) {\n            flatFileMap.put(file.getName(), file);\n        }\n        return FileVisitResult.CONTINUE;\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/util/GetAllConfigurationInTransaction.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.util;\n\nimport java.util.List;\n\nimport org.bonitasoft.platform.configuration.impl.FullBonitaConfigurationRowMapper;\nimport org.bonitasoft.platform.configuration.model.FullBonitaConfiguration;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.transaction.TransactionStatus;\nimport org.springframework.transaction.support.TransactionCallback;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class GetAllConfigurationInTransaction implements TransactionCallback<List<FullBonitaConfiguration>> {\n\n    private final static org.slf4j.Logger LOGGER = LoggerFactory.getLogger(GetAllConfigurationInTransaction.class);\n    private final JdbcTemplate jdbcTemplate;\n\n    public GetAllConfigurationInTransaction(JdbcTemplate jdbcTemplate) {\n        this.jdbcTemplate = jdbcTemplate;\n    }\n\n    @Override\n    public List<FullBonitaConfiguration> doInTransaction(TransactionStatus transactionStatus) {\n        LOGGER.debug(\"Get all configurations from database\");\n\n        final List<FullBonitaConfiguration> fullBonitaConfigurations = jdbcTemplate.query(\n                FullBonitaConfigurationRowMapper.SELECT_CONFIGURATION,\n                new FullBonitaConfigurationRowMapper());\n\n        LOGGER.debug(\"Configurations found:{}\", fullBonitaConfigurations);\n\n        return fullBonitaConfigurations;\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/util/GetConfigurationInTransaction.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.util;\n\nimport java.util.List;\n\nimport org.bonitasoft.platform.configuration.impl.BonitaConfigurationRowMapper;\nimport org.bonitasoft.platform.configuration.impl.ConfigurationServiceImpl;\nimport org.bonitasoft.platform.configuration.model.BonitaConfiguration;\nimport org.bonitasoft.platform.configuration.type.ConfigurationType;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.transaction.TransactionStatus;\nimport org.springframework.transaction.support.TransactionCallback;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class GetConfigurationInTransaction implements TransactionCallback<BonitaConfiguration> {\n\n    private final JdbcTemplate jdbcTemplate;\n    private final ConfigurationType type;\n    private final String resourceName;\n\n    private final static org.slf4j.Logger LOGGER = LoggerFactory.getLogger(ConfigurationServiceImpl.class);\n\n    public GetConfigurationInTransaction(JdbcTemplate jdbcTemplate, ConfigurationType type, String resourceName) {\n        this.jdbcTemplate = jdbcTemplate;\n        this.type = type;\n        this.resourceName = resourceName;\n    }\n\n    @Override\n    public BonitaConfiguration doInTransaction(TransactionStatus status) {\n        LOGGER.debug(\"get configurations for type:{} resource:{}\", type.name(), resourceName);\n\n        final List<BonitaConfiguration> bonitaConfigurations = jdbcTemplate.query(\n                BonitaConfigurationRowMapper.SELECT_CONFIGURATION,\n                new Object[] { type.name(), resourceName },\n                new BonitaConfigurationRowMapper());\n\n        LOGGER.debug(\"configurations found:{}\", bonitaConfigurations);\n\n        if (bonitaConfigurations.size() == 1) {\n            return bonitaConfigurations.get(0);\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/util/GetConfigurationsInTransaction.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.util;\n\nimport java.util.List;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.platform.configuration.impl.BonitaConfigurationRowMapper;\nimport org.bonitasoft.platform.configuration.model.BonitaConfiguration;\nimport org.bonitasoft.platform.configuration.type.ConfigurationType;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.transaction.TransactionStatus;\nimport org.springframework.transaction.support.TransactionCallback;\n\n/**\n * @author Laurent Leseigneur\n */\n@Slf4j\npublic class GetConfigurationsInTransaction implements TransactionCallback<List<BonitaConfiguration>> {\n\n    private final JdbcTemplate jdbcTemplate;\n    private final ConfigurationType type;\n\n    public GetConfigurationsInTransaction(JdbcTemplate jdbcTemplate, ConfigurationType type) {\n        this.jdbcTemplate = jdbcTemplate;\n        this.type = type;\n    }\n\n    @Override\n    public List<BonitaConfiguration> doInTransaction(TransactionStatus status) {\n        log.debug(\"get configurations for type:{}\", type.name());\n\n        final List<BonitaConfiguration> bonitaConfigurations = jdbcTemplate.query(\n                BonitaConfigurationRowMapper.SELECT_CONFIGURATION_FOR_TYPE,\n                new Object[] { type.name() },\n                new BonitaConfigurationRowMapper());\n\n        log.debug(\"configurations found:{}\", bonitaConfigurations);\n\n        return bonitaConfigurations;\n    }\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/util/GetMandatoryStructureConfiguration.java",
    "content": "/**\n * Copyright (C) 2018 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.util;\n\nimport static org.bonitasoft.platform.configuration.impl.ConfigurationFields.CONTENT_TYPE;\n\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.List;\n\nimport org.bonitasoft.platform.configuration.model.LightBonitaConfiguration;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.core.RowMapper;\nimport org.springframework.transaction.TransactionStatus;\nimport org.springframework.transaction.support.TransactionCallback;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class GetMandatoryStructureConfiguration implements TransactionCallback<List<LightBonitaConfiguration>> {\n\n    private final static Logger LOGGER = LoggerFactory.getLogger(GetMandatoryStructureConfiguration.class);\n    private final JdbcTemplate jdbcTemplate;\n\n    public GetMandatoryStructureConfiguration(JdbcTemplate jdbcTemplate) {\n        this.jdbcTemplate = jdbcTemplate;\n    }\n\n    @Override\n    public List<LightBonitaConfiguration> doInTransaction(TransactionStatus transactionStatus) {\n        final List<LightBonitaConfiguration> lightBonitaConfigurations = jdbcTemplate\n                .query(LightBonitaConfigurationRowMapper.SELECT, new LightBonitaConfigurationRowMapper());\n\n        LOGGER.debug(\"configurations found:{}\", lightBonitaConfigurations);\n\n        return lightBonitaConfigurations;\n    }\n\n    class LightBonitaConfigurationRowMapper implements RowMapper<LightBonitaConfiguration> {\n\n        public static final String SELECT = \"SELECT distinct content_type\" +\n                \" FROM configuration\" +\n                \" WHERE content_type <> 'LICENSES'\" +\n                \" ORDER BY content_type\";\n\n        @Override\n        public LightBonitaConfiguration mapRow(ResultSet rs, int rowNum) throws SQLException {\n            return new LightBonitaConfiguration(rs.getString(CONTENT_TYPE));\n        }\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/util/LicensesResourceVisitor.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.util;\n\nimport static java.nio.file.FileVisitResult.CONTINUE;\nimport static java.nio.file.FileVisitResult.SKIP_SUBTREE;\n\nimport java.io.IOException;\nimport java.nio.file.FileVisitResult;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.SimpleFileVisitor;\nimport java.nio.file.attribute.BasicFileAttributes;\nimport java.util.List;\n\nimport org.bonitasoft.platform.configuration.model.BonitaConfiguration;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class LicensesResourceVisitor extends SimpleFileVisitor<Path> {\n\n    private final List<BonitaConfiguration> bonitaConfigurations;\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(LicensesResourceVisitor.class);\n    private Path dir;\n\n    public LicensesResourceVisitor(List<BonitaConfiguration> bonitaConfigurations) {\n        this.bonitaConfigurations = bonitaConfigurations;\n    }\n\n    @Override\n    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {\n        initRootFolder(dir);\n        if (isSubFolder(dir)) {\n            return SKIP_SUBTREE;\n        }\n        return CONTINUE;\n    }\n\n    private boolean isSubFolder(Path dir) {\n        return !this.dir.equals(dir);\n    }\n\n    private void initRootFolder(Path dir) {\n        if (this.dir == null) {\n            this.dir = dir;\n        }\n    }\n\n    @Override\n    public FileVisitResult visitFile(Path path, BasicFileAttributes basicFileAttributes) throws IOException {\n        if (isLicenseFile(path)) {\n            LOGGER.info(\"Found license file: {}\", path.getFileName());\n            bonitaConfigurations.add(new BonitaConfiguration(path.getFileName().toString(), Files.readAllBytes(path)));\n        }\n        return CONTINUE;\n    }\n\n    private boolean isLicenseFile(Path path) {\n        return path.getFileName().toString().endsWith(\".lic\");\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/util/StoreConfigurationInTransaction.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.util;\n\nimport java.util.List;\n\nimport org.bonitasoft.platform.configuration.impl.BonitaConfigurationPreparedStatementCleaner;\nimport org.bonitasoft.platform.configuration.impl.BonitaConfigurationPreparedStatementSetter;\nimport org.bonitasoft.platform.configuration.model.BonitaConfiguration;\nimport org.bonitasoft.platform.configuration.type.ConfigurationType;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.transaction.TransactionStatus;\nimport org.springframework.transaction.support.TransactionCallbackWithoutResult;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class StoreConfigurationInTransaction extends TransactionCallbackWithoutResult {\n\n    private final JdbcTemplate jdbcTemplate;\n    private final List<BonitaConfiguration> bonitaConfigurations;\n    private final ConfigurationType type;\n    private final String dbVendor;\n\n    private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(StoreConfigurationInTransaction.class);\n\n    public StoreConfigurationInTransaction(JdbcTemplate jdbcTemplate, String dbVendor,\n            List<BonitaConfiguration> bonitaConfigurations, ConfigurationType type) {\n\n        this.jdbcTemplate = jdbcTemplate;\n        this.dbVendor = dbVendor;\n        this.bonitaConfigurations = bonitaConfigurations;\n        this.type = type;\n    }\n\n    @Override\n    protected void doInTransactionWithoutResult(TransactionStatus status) {\n        LOGGER.debug(\"delete configurations for type:{} bonitaConfigurations:{}\", type.name(), bonitaConfigurations);\n\n        jdbcTemplate.batchUpdate(BonitaConfigurationPreparedStatementCleaner.DELETE_CONFIGURATION,\n                new BonitaConfigurationPreparedStatementCleaner(bonitaConfigurations, type));\n\n        LOGGER.debug(\"store configurations for type:{} bonitaConfigurations:{}\", type.name(), bonitaConfigurations);\n        jdbcTemplate.batchUpdate(BonitaConfigurationPreparedStatementSetter.INSERT_CONFIGURATION,\n                new BonitaConfigurationPreparedStatementSetter(bonitaConfigurations, dbVendor, type));\n\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/util/StoreConfigurationsIfNotExist.java",
    "content": "/**\n * Copyright (C) 2021 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.util;\n\nimport static java.util.Collections.singletonList;\nimport static org.bonitasoft.platform.configuration.impl.ConfigurationServiceImpl.LOGGER;\n\nimport java.util.List;\n\nimport org.bonitasoft.platform.configuration.impl.BonitaAllConfigurationPreparedStatementSetter;\nimport org.bonitasoft.platform.configuration.model.FullBonitaConfiguration;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.transaction.TransactionStatus;\nimport org.springframework.transaction.support.TransactionCallbackWithoutResult;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class StoreConfigurationsIfNotExist extends TransactionCallbackWithoutResult {\n\n    public static final String SELECT_CONFIGURATION_EXISTS = \"SELECT count(1) FROM configuration WHERE content_type = ? AND resource_name = ?\";\n\n    private final JdbcTemplate jdbcTemplate;\n    private final String dbVendor;\n    private final List<FullBonitaConfiguration> configurations;\n\n    public StoreConfigurationsIfNotExist(JdbcTemplate jdbcTemplate, String dbVendor,\n            List<FullBonitaConfiguration> configurations) {\n        this.jdbcTemplate = jdbcTemplate;\n        this.dbVendor = dbVendor;\n        this.configurations = configurations;\n    }\n\n    @Override\n    public void doInTransactionWithoutResult(TransactionStatus status) {\n        for (FullBonitaConfiguration configuration : configurations) {\n            final Integer nbRows = jdbcTemplate.queryForObject(SELECT_CONFIGURATION_EXISTS, Integer.class,\n                    configuration.getConfigurationType(), configuration.getResourceName());\n\n            if (nbRows == 0) {\n                // only keep elements that do not already exist in database...\n                LOGGER.info(\"New configuration file detected '{}'. Storing it to database.\",\n                        configuration.getResourceName());\n                jdbcTemplate.batchUpdate(BonitaAllConfigurationPreparedStatementSetter.INSERT_CONFIGURATION,\n                        new BonitaAllConfigurationPreparedStatementSetter(singletonList(configuration), dbVendor));\n            } else {\n                LOGGER.debug(\"Configuration already exists for type: {}, resource: {}. Ignoring it.\",\n                        configuration.getConfigurationType(),\n                        configuration.getResourceName());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/util/UpdateConfigurationInTransaction.java",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.util;\n\nimport java.util.List;\n\nimport org.bonitasoft.platform.configuration.impl.BonitaConfigurationTenantUpdater;\nimport org.bonitasoft.platform.configuration.model.BonitaConfiguration;\nimport org.bonitasoft.platform.configuration.type.ConfigurationType;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.transaction.TransactionStatus;\nimport org.springframework.transaction.support.TransactionCallbackWithoutResult;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class UpdateConfigurationInTransaction extends TransactionCallbackWithoutResult {\n\n    private final JdbcTemplate jdbcTemplate;\n    private final List<BonitaConfiguration> bonitaConfigurations;\n    private final ConfigurationType type;\n    private final String dbVendor;\n\n    private final static Logger LOGGER = LoggerFactory.getLogger(UpdateConfigurationInTransaction.class);\n\n    public UpdateConfigurationInTransaction(JdbcTemplate jdbcTemplate, String dbVendor,\n            List<BonitaConfiguration> bonitaConfigurations,\n            ConfigurationType type) {\n        this.jdbcTemplate = jdbcTemplate;\n        this.dbVendor = dbVendor;\n        this.bonitaConfigurations = bonitaConfigurations;\n        this.type = type;\n    }\n\n    @Override\n    protected void doInTransactionWithoutResult(TransactionStatus status) {\n        LOGGER.debug(\"Updating configuration files {} of type:{}\", bonitaConfigurations.toString(), type.name());\n\n        jdbcTemplate.batchUpdate(BonitaConfigurationTenantUpdater.UPDATE_ALL_TENANTS_CONFIGURATION,\n                new BonitaConfigurationTenantUpdater(bonitaConfigurations, dbVendor, type));\n\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/database/DatabaseVendor.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.database;\n\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\n\n@RequiredArgsConstructor\n@Getter\npublic enum DatabaseVendor {\n\n    H2(\"h2\"), //\n    MYSQL(\"mysql\"), //\n    ORACLE(\"oracle\"), //\n    POSTGRES(\"postgres\"), //\n    SQLSERVER(\"sqlserver\");\n\n    private final String value;\n\n    public static DatabaseVendor parseValue(String databaseVendorValue) {\n        for (DatabaseVendor databaseVendor : DatabaseVendor.values()) {\n            if (databaseVendor.value.equalsIgnoreCase(databaseVendorValue)) {\n                return databaseVendor;\n            }\n        }\n        throw new IllegalArgumentException(\"Unknown database vendor: \" + databaseVendorValue);\n    }\n\n    public boolean equalsValue(String anotherValue) {\n        return value.equals(anotherValue);\n    }\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/exception/PlatformException.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.exception;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class PlatformException extends Exception {\n\n    public PlatformException(Exception e) {\n        super(e);\n    }\n\n    public PlatformException(String message) {\n        super(message);\n    }\n\n    public PlatformException(String message, Exception cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/setup/PlatformSetup.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup;\n\nimport static java.lang.System.lineSeparator;\nimport static org.bonitasoft.platform.configuration.type.ConfigurationType.*;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.sql.Connection;\nimport java.sql.DatabaseMetaData;\nimport java.sql.SQLException;\nimport java.text.MessageFormat;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport javax.sql.DataSource;\n\nimport lombok.Getter;\nimport org.apache.commons.io.FileUtils;\nimport org.apache.commons.io.IOUtils;\nimport org.apache.commons.io.filefilter.RegexFileFilter;\nimport org.bonitasoft.platform.configuration.ConfigurationService;\nimport org.bonitasoft.platform.configuration.model.BonitaConfiguration;\nimport org.bonitasoft.platform.configuration.model.FullBonitaConfiguration;\nimport org.bonitasoft.platform.configuration.model.LightBonitaConfiguration;\nimport org.bonitasoft.platform.configuration.type.ConfigurationType;\nimport org.bonitasoft.platform.database.DatabaseVendor;\nimport org.bonitasoft.platform.exception.PlatformException;\nimport org.bonitasoft.platform.version.VersionService;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;\nimport org.springframework.core.io.Resource;\nimport org.springframework.core.io.support.PathMatchingResourcePatternResolver;\nimport org.springframework.core.io.support.ResourcePatternResolver;\nimport org.springframework.stereotype.Component;\n\n/**\n * Manages database schema creation, initialization, and configuration for the Bonita Platform.\n * <p>\n * This component is responsible for preparing the database environment required by the Bonita Engine.\n * It handles both initial setup (first run) and updates (subsequent runs), ensuring the database schema\n * and configuration are consistent with the platform binaries version.\n * <p>\n * <b>Main Responsibilities:</b>\n * <ul>\n * <li><b>Database Schema Management</b>: Creates all required tables on first run via {@link ScriptExecutor}\n * (platform, configuration, sequence, and 50+ BPM tables)</li>\n * <li><b>Configuration Management</b>: Stores and retrieves configuration files in the database via\n * {@link ConfigurationService}</li>\n * <li><b>Version Compatibility</b>: Validates platform binaries version matches database schema version\n * via {@link VersionService}</li>\n * <li><b>Configuration Push/Pull</b>: Synchronizes configuration files between filesystem and database</li>\n * <li><b>License Management</b>: Handles license file storage (subscription edition)</li>\n * </ul>\n * <p>\n * <b>Key Methods:</b>\n * <ul>\n * <li>{@link #init()}: Main entry point - creates tables on first run or updates configuration on subsequent runs</li>\n * <li>{@link #push()}: Pushes configuration from {@code platform_conf/current/} to database</li>\n * <li>{@link #pull()}: Pulls configuration from database to {@code platform_conf/current/}</li>\n * <li>{@link #destroy()}: Drops all database tables</li>\n * </ul>\n * <p>\n * <b>Configuration Types Managed:</b>\n * <ul>\n * <li>PLATFORM_ENGINE: Platform-level engine configuration</li>\n * <li>PLATFORM_PORTAL: Platform-level portal configuration</li>\n * <li>TENANT_ENGINE: Tenant-level engine configuration</li>\n * <li>TENANT_PORTAL: Tenant-level portal configuration (permissions, security)</li>\n * <li>TENANT_SECURITY_SCRIPTS: Authorization and security scripts</li>\n * </ul>\n * <p>\n * The class uses the following folder structure:\n *\n * <pre>\n * platform_conf/\n *   ├── initial/          - Initial configuration (used on first setup)\n *   ├── current/          - Current configuration (for push/pull operations)\n *   ├── licenses/         - License files (subscription only)\n *   └── backup-[timestamp]/ - Backup before push operations\n * </pre>\n *\n * @author Baptiste Mesta\n * @see ScriptExecutor\n * @see ConfigurationService\n * @see VersionService\n */\n@Component\n@ConditionalOnSingleCandidate(PlatformSetup.class)\npublic class PlatformSetup {\n\n    public static final String BONITA_SETUP_FOLDER = \"org.bonitasoft.platform.setup.folder\";\n\n    public static final String PLATFORM_CONF_FOLDER_NAME = \"platform_conf\";\n\n    public static final String BONITA_CLIENT_HOME_FOLDER = \"bonita.client.home\";\n\n    public static final String BONITA_DB_VENDOR_PROPERTY = \"sysprop.bonita.db.vendor\";\n    public static final String BONITA_BDM_DB_VENDOR_PROPERTY = \"sysprop.bonita.bdm.db.vendor\";\n\n    protected static final Logger LOGGER = LoggerFactory.getLogger(PlatformSetup.class);\n\n    private final ScriptExecutor scriptExecutor;\n\n    @Getter // Used by distrib bundle tests\n    private final ConfigurationService configurationService;\n\n    @Getter\n    private final VersionService versionService;\n\n    private final DataSource dataSource;\n\n    protected String dbVendor;\n    protected String bdmDbVendor;\n\n    private Path initialConfigurationFolder;\n    private Path currentConfigurationFolder;\n    private Path backupConfigurationFolder;\n    protected Path licensesFolder;\n    private Path backupLicensesFolder;\n    private final ResourcePatternResolver cpResourceResolver = new PathMatchingResourcePatternResolver(\n            PlatformSetup.class.getClassLoader());\n\n    public PlatformSetup(ScriptExecutor scriptExecutor, ConfigurationService configurationService,\n            VersionService versionService, DataSource dataSource, @Value(\"${db.vendor}\") String dbVendor,\n            @Value(\"${bdm.db.vendor}\") String bdmDbVendor) {\n        this.scriptExecutor = scriptExecutor;\n        this.configurationService = configurationService;\n        this.versionService = versionService;\n        this.dataSource = dataSource;\n        this.dbVendor = dbVendor;\n        this.bdmDbVendor = bdmDbVendor;\n    }\n\n    /**\n     * Gets the value of the system property indicated by the key {@link PlatformSetup#BONITA_DB_VENDOR_PROPERTY}\n     *\n     * @return the string value of the system property, or null if there is no property with that key\n     */\n    public static String getPropertyBonitaDbVendor() {\n        return System.getProperty(BONITA_DB_VENDOR_PROPERTY);\n    }\n\n    /**\n     * Gets the value of the system property indicated by the key {@link PlatformSetup#BONITA_BDM_DB_VENDOR_PROPERTY}\n     *\n     * @return the string value of the system property, or null if there is no property with that key\n     */\n    public static String getPropertyBonitaBdmDbVendor() {\n        return System.getProperty(BONITA_BDM_DB_VENDOR_PROPERTY);\n    }\n\n    /**\n     * Entry point that create the tables and insert the default configuration\n     */\n    public void init() throws PlatformException {\n        initPlatformSetup();\n        if (isPlatformAlreadyCreated()) {\n            LOGGER.info(\"Platform is already created.\");\n            if (Files.isDirectory(initialConfigurationFolder)) {\n                LOGGER.info(\"Upgrading default configuration with files from folder: {}\", initialConfigurationFolder);\n                updateDefaultConfigurationFromFolder(initialConfigurationFolder);\n            } else {\n                LOGGER.info(\"Upgrading default configuration with files from classpath\");\n                updateDefaultConfigurationFromClasspath();\n            }\n            insertNewConfigurationsFromClasspathIfExist();\n            return;\n        }\n        preventFromPushingZeroLicense();\n        initializePlatform();\n        LOGGER.info(\"Platform created.\");\n        if (Files.isDirectory(initialConfigurationFolder)) {\n            LOGGER.info(\"Database will be initialized with configuration files from folder: {}\",\n                    initialConfigurationFolder);\n            pushFromFolder(initialConfigurationFolder);\n        } else {\n            LOGGER.warn(\"Database will be initialized with configuration files from classpath\");\n            insertNewConfigurationsFromClasspathIfExist();\n        }\n        pushLicenses(true);\n        LOGGER.info(\"Initial configuration files successfully pushed to database\");\n    }\n\n    boolean isPlatformAlreadyCreated() {\n        return scriptExecutor.isPlatformAlreadyCreated();\n    }\n\n    private void pushFromFolder(Path folderToPush) throws PlatformException {\n        configurationService.storeAllConfiguration(folderToPush);\n    }\n\n    private void checkPushFolderExists(Path folderToPush) throws PlatformException {\n        if (!Files.isDirectory(folderToPush)) {\n            throw new PlatformException(\n                    \"Unable to push configuration from \" + folderToPush\n                            + \", as directory does not exists. To modify your configuration, run 'setup pull', update your configuration files from \"\n                            + currentConfigurationFolder + \" folder, and then push your new configuration.\");\n        }\n    }\n\n    void clean() {\n        configurationService.deleteAllConfiguration();\n    }\n\n    /**\n     * push all configuration files and licenses\n     */\n    public void push() throws PlatformException {\n        push(false);\n    }\n\n    public void forcePush() throws PlatformException {\n        push(true);\n    }\n\n    /**\n     * push all configuration files and licenses\n     *\n     * @param forcePush shall we skip the check for removed folders?\n     */\n    public void push(boolean forcePush) throws PlatformException {\n        initPlatformSetup();\n        if (!isPlatformAlreadyCreated()) {\n            throw new PlatformException(\"Platform is not created. Run 'setup init' first.\");\n        }\n        preventFromPushingZeroLicense();\n        checkPlatformVersion();\n        checkPushFolderExists(currentConfigurationFolder);\n        LOGGER.info(\"Configuration currently in database will be replaced by configuration from folder: {}\",\n                currentConfigurationFolder);\n        ensureNoCriticalFoldersAreDeleted(forcePush);\n        pull(backupConfigurationFolder, backupLicensesFolder);\n        LOGGER.info(\"Backup directory created: {}\", backupConfigurationFolder);\n        var hasLicenses = !getConfigurationService().getLicenses().isEmpty();\n        clean();\n        pushFromFolder(currentConfigurationFolder);\n        pushLicenses(hasLicenses);\n        LOGGER.info(\n                \"Configuration files successfully pushed to database. You can now restart Bonita to reflect your changes.\");\n    }\n\n    private void ensureNoCriticalFoldersAreDeleted(boolean forcePush) throws PlatformException {\n        final List<LightBonitaConfiguration> configurations = configurationService\n                .getMandatoryStructureConfiguration();\n        for (LightBonitaConfiguration configuration : configurations) {\n            // check no mandatory folder from database is no more in the FileSystem and about to be deleted:\n            final Path folder = getFolderFromConfiguration(configuration);\n            if (!Files.isDirectory(folder)) {\n                if (forcePush) {\n                    LOGGER.warn(\"Force-pushing the deletion of folder {}\", folder);\n                } else {\n                    throw new PlatformException(\"You are trying to remove a protected folder from configuration: \" +\n                            getSpecificErrorMessage(folder));\n                }\n            }\n        }\n    }\n\n    protected Path getFolderFromConfiguration(LightBonitaConfiguration configuration) {\n        return currentConfigurationFolder.resolve(configuration.type().toLowerCase());\n    }\n\n    private String getSpecificErrorMessage(Path folder) {\n        return \"You are not allowed to remove folder '\" + folder.toString() + \"'\" + lineSeparator()\n                + \"To restore the deleted folders, run 'setup pull'. You will lose the locally modified configuration.\";\n    }\n\n    /**\n     * Entry point to retrieve all configuration files and write them to folder\n     * each file will be located under sub folder according to its purpose. See\n     * {@link org.bonitasoft.platform.configuration.type.ConfigurationType} for all\n     * available values\n     */\n    public void pull() throws PlatformException {\n        initPlatformSetup();\n        checkPlatformVersion();\n        LOGGER.info(\"Pulling configuration into folder: {}\", currentConfigurationFolder);\n        if (Files.isDirectory(licensesFolder)) {\n            LOGGER.info(\"Pulling licenses into folder: {}\", licensesFolder);\n        }\n        pull(currentConfigurationFolder, licensesFolder);\n        LOGGER.info(\n                \"Configuration (and license) files successfully pulled. You can now edit them. Use \\\"setup push\\\" when done.\");\n    }\n\n    public void pull(Path configurationFolder, Path licensesFolder) throws PlatformException {\n        try {\n            recreateDirectory(configurationFolder);\n            if (Files.isDirectory(licensesFolder)) {\n                FileUtils.cleanDirectory(licensesFolder.toFile());\n            }\n            List<File> licenses = new ArrayList<>();\n            List<File> files = configurationService.writeAllConfigurationToFolder(configurationFolder.toFile(),\n                    licensesFolder.toFile());\n            LOGGER.info(\"Retrieved following files in {}\", configurationFolder);\n            for (File file : files) {\n                if (file.toPath().getParent().equals(licensesFolder)) {\n                    licenses.add(file);\n                } else {\n                    LOGGER.info(configurationFolder.relativize(file.toPath()).toString());\n                }\n            }\n            if (!licenses.isEmpty()) {\n                LOGGER.info(\"Retrieved following licenses in {}\", licensesFolder);\n                for (File license : licenses) {\n                    LOGGER.info(licensesFolder.relativize(license.toPath()).toString());\n                }\n            }\n        } catch (IOException e) {\n            throw new PlatformException(e);\n        }\n    }\n\n    private void recreateDirectory(Path... folders) throws IOException {\n        for (Path folder : folders) {\n            if (Files.exists(folder)) {\n                FileUtils.deleteDirectory(folder.toFile());\n            }\n            Files.createDirectories(folder);\n        }\n    }\n\n    private void checkPlatformVersion() throws PlatformException {\n        if (!versionService.isValidPlatformVersion()) {\n            String message = \"The version of the platform (binaries) you are running [{0}] \" +\n                    \"only support database schema in version [{1}] but the current database schema version is [{2}]. \" +\n                    \"You might need to migrate your platform or use a different version of the binaries.\";\n            throw new PlatformException(MessageFormat.format(message,\n                    versionService.getPlatformSetupVersion(),\n                    versionService.getSupportedDatabaseSchemaVersion(),\n                    versionService.retrieveDatabaseSchemaVersion()));\n        }\n    }\n\n    /**\n     * lookup for license file and push them to database\n     */\n    protected void pushLicenses(boolean hasLicenses) throws PlatformException {\n        // Nothing to do in community\n    }\n\n    private void initializePlatform() throws PlatformException {\n        scriptExecutor.createAndInitializePlatformIfNecessary();\n    }\n\n    void initProperties() throws PlatformException {\n        if (dbVendor == null) {\n            dbVendor = getPropertyBonitaDbVendor();\n        }\n        checkSupportedVendor(dbVendor);\n\n        if (bdmDbVendor == null) {\n            bdmDbVendor = getPropertyBonitaBdmDbVendor();\n        }\n        checkSupportedVendor(bdmDbVendor);\n\n        String setupFolderPath = System.getProperty(BONITA_SETUP_FOLDER);\n        Path platformConfFolder;\n        if (setupFolderPath != null) {\n            LOGGER.info(\"System property {} is set to {}\", BONITA_SETUP_FOLDER, setupFolderPath);\n            platformConfFolder = Paths.get(setupFolderPath).resolve(PLATFORM_CONF_FOLDER_NAME);\n        } else {\n            platformConfFolder = Paths.get(PLATFORM_CONF_FOLDER_NAME);\n        }\n        initializeFoldersPaths(platformConfFolder);\n    }\n\n    /**\n     * Check that the given `databaseVendor` is supported using the community edition.\n     *\n     * @param databaseVendor database vendor to check\n     * @throws PlatformException if the given database vendor is not supported\n     * @throws IllegalArgumentException if `databaseVendor` is null or is not a recognized database vendor\n     */\n    protected void checkSupportedVendor(String databaseVendor) throws PlatformException, IllegalArgumentException {\n        DatabaseVendor vendor = DatabaseVendor.parseValue(databaseVendor);\n        if (vendor != DatabaseVendor.H2 && vendor != DatabaseVendor.POSTGRES) {\n            throw new PlatformException(\"Database vendor '\" + vendor + \"' is not supported with the community edition\");\n        }\n        LOGGER.debug(\"Database vendor '{}' is supported\", vendor);\n    }\n\n    private void initializeFoldersPaths(Path platformConfFolder) {\n        initialConfigurationFolder = platformConfFolder.resolve(\"initial\");\n        currentConfigurationFolder = platformConfFolder.resolve(\"current\");\n\n        Path rootBackupFolder = platformConfFolder.resolve(\"backup-\" + System.currentTimeMillis());\n        backupConfigurationFolder = rootBackupFolder.resolve(\"current\");\n        backupLicensesFolder = rootBackupFolder.resolve(\"licenses\");\n\n        licensesFolder = getLicenseInitialFolder(platformConfFolder);\n    }\n\n    private Path getLicenseInitialFolder(Path platformConfFolder) {\n        final String bonita_client_home = System.getProperty(BONITA_CLIENT_HOME_FOLDER);\n        if (bonita_client_home != null) {\n            return Paths.get(bonita_client_home);\n        }\n        return platformConfFolder.resolve(\"licenses\");\n    }\n\n    private void updateDefaultConfigurationFromFolder(Path folderToPush) throws PlatformException {\n        configurationService.updateDefaultConfiguration(folderToPush);\n    }\n\n    private void insertNewConfigurationsFromClasspathIfExist() throws PlatformException {\n        final ArrayList<FullBonitaConfiguration> configurations = new ArrayList<>();\n        try {\n            configurations.addAll(getConfigurationsMatchingPattern(PLATFORM_ENGINE));\n            configurations.addAll(getConfigurationsMatchingPattern(PLATFORM_PORTAL));\n            configurations.addAll(getConfigurationsMatchingPattern(TENANT_ENGINE));\n            configurations.addAll(getConfigurationsMatchingPattern(TENANT_PORTAL));\n            configurations.addAll(getConfigurationsMatchingPattern(TENANT_SECURITY_SCRIPTS));\n        } catch (IOException e) {\n            throw new PlatformException(e);\n        }\n        configurationService.storeConfigurationsIfNotExist(configurations);\n    }\n\n    public List<FullBonitaConfiguration> getConfigurationsMatchingPattern(ConfigurationType type)\n            throws IOException {\n        final ArrayList<FullBonitaConfiguration> configurations = new ArrayList<>();\n        final String typeLowercase = type.name().toLowerCase();\n        Resource[] resources = cpResourceResolver\n                .getResources(ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + \"/\" + typeLowercase + \"/**\");\n        for (Resource resource : resources) {\n            if (resource.exists() && resource.isReadable() && resource.contentLength() > 0) {\n                String resourceName = resource.getFilename();\n                LOGGER.debug(\"Found configuration file '{}' of type '{}' in classpath\", resourceName, type);\n                try (InputStream resourceAsStream = resource.getInputStream()) {\n                    final byte[] content = IOUtils.toByteArray(resourceAsStream);\n                    configurations.add(new FullBonitaConfiguration(resourceName, content, type.name()));\n                }\n            }\n        }\n        return configurations;\n    }\n\n    private void updateDefaultConfigurationFromClasspath() throws PlatformException {\n        List<BonitaConfiguration> portalTenant = new ArrayList<>(3);\n        try {\n            addIfExists(portalTenant, TENANT_PORTAL, \"compound-permissions-mapping.properties\");\n            addIfExists(portalTenant, TENANT_PORTAL, \"dynamic-permissions-checks.properties\");\n            addIfExists(portalTenant, TENANT_PORTAL, \"resources-permissions-mapping.properties\");\n        } catch (IOException e) {\n            throw new PlatformException(e);\n        }\n        configurationService.updateTenantPortalConf(portalTenant);\n    }\n\n    private void addIfExists(List<BonitaConfiguration> configurations, ConfigurationType configurationType,\n            String resourceName) throws IOException {\n        BonitaConfiguration bonitaConfiguration = getBonitaConfigurationFromClassPath(\n                configurationType.name().toLowerCase(), resourceName);\n        if (bonitaConfiguration != null) {\n            configurations.add(bonitaConfiguration);\n        }\n    }\n\n    private void initDataSource() throws PlatformException {\n        try {\n            try (Connection connection = dataSource.getConnection()) {\n                DatabaseMetaData metaData = connection.getMetaData();\n                LOGGER.info(\"Connected to '{}' database with url: '{}' with user: '{}'\", dbVendor, metaData.getURL(),\n                        metaData.getUserName());\n            }\n        } catch (SQLException e) {\n            throw new PlatformException(e);\n        }\n    }\n\n    private BonitaConfiguration getBonitaConfigurationFromClassPath(String folder, String resourceName)\n            throws IOException {\n        try (InputStream resourceAsStream = this.getClass().getResourceAsStream(\"/\" + folder + \"/\" + resourceName)) {\n            if (resourceAsStream == null) {\n                return null;\n            }\n            LOGGER.debug(\"Using configuration from classpath {}\", resourceName);\n            return new BonitaConfiguration(resourceName, IOUtils.toByteArray(resourceAsStream));\n        }\n    }\n\n    public void destroy() throws PlatformException {\n        initPlatformSetup();\n        if (isPlatformAlreadyCreated()) {\n            scriptExecutor.deleteTables();\n        }\n    }\n\n    public void initPlatformSetup() throws PlatformException {\n        initProperties();\n        initDataSource();\n    }\n\n    void preventFromPushingZeroLicense() throws PlatformException {\n        if (Files.isDirectory(licensesFolder)) {\n            final String[] licenseFiles = licensesFolder.toFile().list(new RegexFileFilter(\".*\\\\.lic\"));\n            if (licenseFiles == null || licenseFiles.length == 0) {\n                throw new PlatformException(\"No license (.lic file) found.\" + lineSeparator()\n                        + \"This would prevent Bonita Platform subscription edition to start normally.\" + lineSeparator()\n                        + \"Place your license file in '\" + licensesFolder.toAbsolutePath().toString()\n                        + \"' and then try again.\");\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/setup/PlatformSetupAccessor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup;\n\nimport javax.naming.Context;\nimport javax.naming.InitialContext;\nimport javax.naming.NamingException;\nimport javax.sql.DataSource;\n\nimport org.bonitasoft.platform.configuration.ConfigurationService;\nimport org.bonitasoft.platform.configuration.impl.ConfigurationServiceImpl;\nimport org.bonitasoft.platform.version.VersionService;\nimport org.bonitasoft.platform.version.impl.VersionServiceImpl;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.datasource.DataSourceTransactionManager;\nimport org.springframework.transaction.support.TransactionTemplate;\n\n/**\n * access platform setup outside of a spring context (to be replaced by a spring context...)\n *\n * @author Baptiste Mesta\n */\npublic class PlatformSetupAccessor {\n\n    private static final PlatformSetupAccessor _UNIQUE = new PlatformSetupAccessor();\n\n    private PlatformSetup instance;\n\n    protected PlatformSetupAccessor() {\n        // Empty protected constructor to prevent instantiation\n    }\n\n    public static PlatformSetupAccessor getInstance() {\n        return _UNIQUE;\n    }\n\n    public PlatformSetup getPlatformSetup() throws NamingException {\n        if (instance == null) {\n            instance = initPlatformSetup();\n        }\n        return instance;\n    }\n\n    private PlatformSetup initPlatformSetup() throws NamingException {\n        final DataSource dataSource = lookupDataSource();\n        String dbVendor = PlatformSetup.getPropertyBonitaDbVendor();\n        String bdmDbVendor = PlatformSetup.getPropertyBonitaBdmDbVendor();\n        return createNewPlatformSetup(dataSource, dbVendor, bdmDbVendor);\n    }\n\n    /**\n     * WARNING: for internal use only. In normal cases, you should use PlatformSetupAccessor.getPlatformSetup() !\n     *\n     * @param dataSource the datasource to use to access the database\n     * @param dbVendor the Database vendor (default H2) to point at\n     * @param bdmDbVendor the BDM Database vendor (default H2) to point at\n     * @return a NEW instance of Platform Setup\n     */\n    public PlatformSetup createNewPlatformSetup(DataSource dataSource, String dbVendor, String bdmDbVendor) {\n        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);\n        final DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(dataSource);\n        TransactionTemplate transactionTemplate = new TransactionTemplate(dataSourceTransactionManager);\n        VersionService versionService = new VersionServiceImpl(jdbcTemplate);\n        return createPlatformSetup(\n                createScriptExecutor(dataSource, dbVendor, versionService),\n                new ConfigurationServiceImpl(jdbcTemplate, transactionTemplate, dbVendor),\n                versionService, dataSource, dbVendor, bdmDbVendor);\n    }\n\n    protected PlatformSetup createPlatformSetup(ScriptExecutor scriptExecutor,\n            ConfigurationService configurationService, VersionService versionService, DataSource dataSource,\n            String dbVendor, String bdmDbVendor) {\n        return new PlatformSetup(scriptExecutor, configurationService, versionService, dataSource, dbVendor,\n                bdmDbVendor);\n    }\n\n    protected ScriptExecutor createScriptExecutor(DataSource dataSource, String dbVendor,\n            VersionService versionService) {\n        return new ScriptExecutor(dbVendor, dataSource, versionService);\n    }\n\n    private static DataSource lookupDataSource() throws NamingException {\n        Context ctx = new InitialContext();\n        return (DataSource) ctx.lookup(\n                System.getProperty(\"sysprop.bonita.database.sequence.manager.datasource.name\",\n                        \"java:comp/env/bonitaSequenceManagerDS\"));\n    }\n\n    public static ConfigurationService getConfigurationService() throws NamingException {\n        final DataSource dataSource = lookupDataSource();\n        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);\n        final DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(dataSource);\n        TransactionTemplate transactionTemplate = new TransactionTemplate(dataSourceTransactionManager);\n        return new ConfigurationServiceImpl(jdbcTemplate, transactionTemplate,\n                PlatformSetup.getPropertyBonitaDbVendor());\n    }\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/setup/ScriptExecutor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup;\n\nimport static java.util.Arrays.asList;\nimport static org.bonitasoft.platform.setup.PlatformSetup.BONITA_SETUP_FOLDER;\nimport static org.bonitasoft.platform.setup.PlatformSetup.PLATFORM_CONF_FOLDER_NAME;\nimport static org.bonitasoft.platform.setup.SimpleEncryptor.encrypt;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URL;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.security.GeneralSecurityException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport javax.sql.DataSource;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.platform.exception.PlatformException;\nimport org.bonitasoft.platform.version.VersionService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;\nimport org.springframework.context.annotation.PropertySource;\nimport org.springframework.core.io.FileSystemResource;\nimport org.springframework.core.io.Resource;\nimport org.springframework.core.io.UrlResource;\nimport org.springframework.dao.DataAccessException;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@Slf4j\n@Component\n@ConditionalOnSingleCandidate(ScriptExecutor.class)\n@PropertySource(\"classpath:/application.properties\")\npublic class ScriptExecutor {\n\n    private static final boolean CONTINUE_ON_ERROR = true;\n\n    public static final boolean FAIL_ON_ERROR = false;\n\n    private final String sqlFolder;\n\n    private final DataSource datasource;\n\n    private final String dbVendor;\n\n    private final VersionService versionService;\n\n    @Autowired\n    public ScriptExecutor(@Value(\"${db.vendor}\") String dbVendor, DataSource datasource,\n            VersionService versionService) {\n        if (dbVendor == null) {\n            throw new IllegalArgumentException(\"dbVendor is null\");\n        }\n        this.dbVendor = dbVendor;\n        this.datasource = datasource;\n        log.info(\"configuration for Database vendor: {}\", dbVendor);\n        this.sqlFolder = \"/sql/\" + dbVendor;\n        this.versionService = versionService;\n    }\n\n    public void createTables() throws PlatformException {\n        try {\n            executeSQLResources(asList(\"createTables.sql\", \"createQuartzTables.sql\"),\n                    FAIL_ON_ERROR);\n        } catch (final IOException e) {\n            throw new PlatformException(e);\n        }\n    }\n\n    /**\n     * Creates and initializes the platform if it is not already done. It creates database tables and populates it.\n     *\n     * @return <code>true</code> if it is a first initialization of the platform.\n     * @throws PlatformException if it fails to create or initialize tables.\n     */\n    public void createAndInitializePlatformIfNecessary() throws PlatformException {\n        if (!isPlatformAlreadyCreated()) {\n            createTables();\n            initializePlatformStructure();\n            insertPlatform();\n        } else {\n            log.info(\"Bonita platform already exists. Nothing to do. Stopping.\");\n        }\n    }\n\n    protected void insertPlatform() {\n        String version = versionService.getPlatformSetupVersion();\n        String databaseSchemaVersion = versionService.getSupportedDatabaseSchemaVersion();\n\n        final String sql = \"INSERT INTO platform (id, version, initial_bonita_version, application_version, \" +\n                \"maintenance_message_active, created, created_by, information, maintenance_enabled) \"\n                + \"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\";\n\n        new JdbcTemplate(datasource).update(sql, 1L, databaseSchemaVersion, version, \"0.0.0\", false,\n                System.currentTimeMillis(), \"platformAdmin\", getInformationInitialValue(), false);\n    }\n\n    protected String getInformationInitialValue() {\n        try {\n            return encrypt(new ObjectMapper().writeValueAsBytes(new ArrayList<Long>()));\n        } catch (GeneralSecurityException | JsonProcessingException e) {\n            log.debug(e.getMessage(), e);\n            throw new IllegalStateException(\"Cannot properly setup Bonita platform\");\n        }\n    }\n\n    public boolean isPlatformAlreadyCreated() {\n        try {\n            return new JdbcTemplate(datasource).queryForObject(\"select count(*) from sequence\", Integer.class) > 0;\n        } catch (DataAccessException e) {\n            return false;\n        }\n    }\n\n    /**\n     * @param sqlFiles the sql files to execute\n     * @param shouldContinueOnError\n     */\n    protected void executeSQLResources(final List<String> sqlFiles, boolean shouldContinueOnError) throws IOException {\n        for (final String sqlFile : sqlFiles) {\n            executeSQLResource(sqlFile, shouldContinueOnError);\n        }\n    }\n\n    /**\n     * @param sqlFolder the folder to look in.\n     * @param sqlFile the name of the file to load.\n     * @return null if not found, the SQL text content in normal cases.\n     */\n    private Resource getSQLResource(final String sqlFolder, final String sqlFile) {\n        String setupFolderPath = System.getProperty(BONITA_SETUP_FOLDER);\n        if (setupFolderPath != null) {\n            return getResourceFromFileSystem(setupFolderPath, sqlFile);\n        } else {\n            return getResourceFromClassPath(sqlFolder, sqlFile);\n        }\n    }\n\n    private Resource getResourceFromFileSystem(String setupFolderPath, String sqlFile) {\n        Path path = Paths.get(setupFolderPath).resolve(PLATFORM_CONF_FOLDER_NAME).resolve(\"sql\").resolve(dbVendor)\n                .resolve(sqlFile);\n        final File file = path.toFile();\n        if (file.exists()) {\n            return new FileSystemResource(file);\n        } else {\n            final String msg = \"SQL resource file not found in filesystem: \" + file.getAbsolutePath();\n            log.error(msg);\n            throw new RuntimeException(msg);\n        }\n    }\n\n    private Resource getResourceFromClassPath(String sqlFolder, String sqlFile) {\n        final String resourcePath = sqlFolder + \"/\" + sqlFile; // Must always be forward slash, even on Windows.\n        final URL url = this.getClass().getResource(resourcePath);\n        if (url != null) {\n            return new UrlResource(url);\n        } else {\n            final String msg = \"SQL resource file not found in classpath: \" + resourcePath;\n            log.warn(msg);\n            throw new RuntimeException(msg);\n        }\n    }\n\n    /**\n     * @param sqlFile the sql file to execute\n     * @param shouldContinueOnError\n     * @throws IOException\n     */\n    protected void executeSQLResource(final String sqlFile, boolean shouldContinueOnError) throws IOException {\n        final Resource sqlResource = getSQLResource(sqlFolder, sqlFile);\n        ResourceDatabasePopulator populate = new ResourceDatabasePopulator();\n        populate.setContinueOnError(shouldContinueOnError);\n        populate.setIgnoreFailedDrops(true);\n        populate.addScript(sqlResource);\n        populate.execute(datasource);\n        log.info(\"Executed SQL script {}\", sqlResource.getURL().getFile());\n    }\n\n    public void initializePlatformStructure() throws PlatformException {\n        try {\n            executeSQLResources(Collections.singletonList(\"initTables.sql\"), FAIL_ON_ERROR);\n        } catch (final IOException e) {\n            throw new PlatformException(e);\n        }\n    }\n\n    public void deleteTables() throws PlatformException {\n        try {\n            executeSQLResources(asList(\"preDropStructure.sql\", \"dropQuartzTables.sql\", \"dropTables.sql\"),\n                    CONTINUE_ON_ERROR);\n        } catch (final IOException e) {\n            throw new PlatformException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/setup/SimpleEncryptor.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup;\n\nimport java.security.GeneralSecurityException;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.spec.InvalidKeySpecException;\nimport java.security.spec.KeySpec;\nimport java.util.Base64;\n\nimport javax.crypto.Cipher;\nimport javax.crypto.SecretKey;\nimport javax.crypto.SecretKeyFactory;\nimport javax.crypto.spec.PBEKeySpec;\nimport javax.crypto.spec.SecretKeySpec;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic final class SimpleEncryptor {\n\n    private static final byte[] SALT = new byte[] { 0x47, 0x6f, 0x6f, 0x64, 0x4a, 0x6f, 0x62, 0x21 };\n    private static final char[] PASSPHRASE = new String(new byte[] { 0x48, 0x34, 0x76, 0x33, 0x46, 0x75, 0x6e,\n            0x57, 0x31, 0x37, 0x68, 0x38, 0x30, 0x6e, 0x31, 0x37, 0x34 }).toCharArray();\n\n    private static final SecretKey SECRET_KEY = SimpleEncryptor.generateKey();\n\n    private SimpleEncryptor() {\n    }\n\n    public static SecretKey generateKey() {\n        try {\n            SecretKeyFactory factory = SecretKeyFactory.getInstance(\"PBKDF2WithHmacSHA256\");\n            KeySpec spec = new PBEKeySpec(PASSPHRASE, SALT, 1000, 256);\n            return new SecretKeySpec(factory.generateSecret(spec).getEncoded(), \"AES\");\n        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {\n            throw new IllegalStateException(e);\n        }\n    }\n\n    public static String encrypt(byte[] data) throws GeneralSecurityException {\n        Cipher cipher = Cipher.getInstance(\"AES\");\n        cipher.init(Cipher.ENCRYPT_MODE, SECRET_KEY);\n        byte[] encryptedBytes = cipher.doFinal(data);\n        return Base64.getEncoder().encodeToString(encryptedBytes);\n    }\n\n    public static byte[] decrypt(String encryptedData) throws GeneralSecurityException {\n        Cipher cipher = Cipher.getInstance(\"AES\");\n        cipher.init(Cipher.DECRYPT_MODE, SECRET_KEY);\n        byte[] decodedBytes = Base64.getDecoder().decode(encryptedData);\n        return cipher.doFinal(decodedBytes);\n    }\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/version/VersionService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.version;\n\nimport org.bonitasoft.platform.exception.PlatformException;\n\n/**\n * @author Laurent Leseigneur\n */\npublic interface VersionService {\n\n    /**\n     * Retrieves the platform version in database\n     *\n     * @return platform current version\n     */\n    String retrieveDatabaseSchemaVersion() throws PlatformException;\n\n    /**\n     * Retrieves the platform information in database\n     *\n     * @return platform information\n     * @throws PlatformException\n     */\n    String retrievePlatformInformation() throws PlatformException;\n\n    /**\n     * Clear platform information\n     *\n     * @throws PlatformException\n     */\n    void clearPlatformInformation() throws PlatformException;\n\n    /**\n     * Retrieves the platform setup tool version\n     *\n     * @return platform setup tool current version\n     */\n    String getPlatformSetupVersion();\n\n    /**\n     * @return the version of the database schema used by this setup tool\n     */\n    String getSupportedDatabaseSchemaVersion();\n\n    /**\n     * Check if platform an platform setup tool are in same version\n     *\n     * @return true if same version, false otherwise\n     */\n    boolean isValidPlatformVersion() throws PlatformException;\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/java/org/bonitasoft/platform/version/impl/VersionServiceImpl.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.version.impl;\n\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.util.List;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport org.apache.commons.io.IOUtils;\nimport org.bonitasoft.platform.exception.PlatformException;\nimport org.bonitasoft.platform.version.VersionService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.dao.DataAccessException;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.stereotype.Service;\n\n/**\n * @author Laurent Leseigneur\n */\n@Service\npublic class VersionServiceImpl implements VersionService {\n\n    private static final String SQL_PLATFORM_VERSION = \"SELECT p.version FROM platform p\";\n    private static final String SQL_PLATFORM_INFORMATION = \"SELECT p.information FROM platform p\";\n    private static final String SQL_CLEAR_PLATFORM_INFORMATION = \"UPDATE platform SET information = NULL\";\n\n    private JdbcTemplate jdbcTemplate;\n\n    @Autowired\n    public VersionServiceImpl(JdbcTemplate jdbcTemplate) {\n        this.jdbcTemplate = jdbcTemplate;\n    }\n\n    @Override\n    public String retrieveDatabaseSchemaVersion() throws PlatformException {\n        final List<String> strings;\n        try {\n            strings = jdbcTemplate.queryForList(SQL_PLATFORM_VERSION, String.class);\n        } catch (DataAccessException e) {\n            throw new PlatformException(\"Platform is not created. Run 'setup init' first.\", e);\n        }\n        if (hasNotSingleResult(strings)) {\n            throw new PlatformException(\"Platform is not created. Run 'setup init' first.\");\n        }\n        return strings.get(0);\n    }\n\n    @Override\n    public String retrievePlatformInformation() throws PlatformException {\n        final List<String> strings;\n        try {\n            strings = jdbcTemplate.queryForList(SQL_PLATFORM_INFORMATION, String.class);\n        } catch (DataAccessException e) {\n            throw new PlatformException(\"Platform is not created. Run 'setup init' first.\", e);\n        }\n        if (hasNotSingleResult(strings)) {\n            throw new PlatformException(\"Platform is not created. Run 'setup init' first.\");\n        }\n        return strings.get(0);\n    }\n\n    @Override\n    public void clearPlatformInformation() throws PlatformException {\n        try {\n            jdbcTemplate.execute(SQL_CLEAR_PLATFORM_INFORMATION);\n        } catch (DataAccessException e) {\n            throw new PlatformException(\"Unable to clear platform information\", e);\n        }\n    }\n\n    private boolean hasNotSingleResult(List<String> strings) {\n        return strings == null || strings.size() != 1;\n    }\n\n    @Override\n    public String getPlatformSetupVersion() {\n        return getVersionProperty(\"PLATFORM_ENGINE_VERSION\");\n    }\n\n    @Override\n    public String getSupportedDatabaseSchemaVersion() {\n        // right now the supported database schema version is equal to the minor version of the product:\n        return extractMinorVersion(getPlatformSetupVersion());\n    }\n\n    /**\n     * This method is duplicate in class CheckPlatformVersion.\n     * This is accepted to limit over-engineering just to extract an util method.\n     */\n    private String extractMinorVersion(String version) {\n        final Matcher matcher = Pattern.compile(\"(\\\\d+\\\\.\\\\d+).*\").matcher(version);\n        if (matcher.matches()) {\n            return matcher.group(1);\n        } else {\n            throw new IllegalArgumentException(version + \" does not respect Semantic Versioning\");\n        }\n    }\n\n    private String getVersionProperty(String versionFileName) {\n        try {\n            return IOUtils.toString(this.getClass().getResource(\"/\" + versionFileName), StandardCharsets.UTF_8);\n        } catch (IOException e) {\n            throw new IllegalStateException(versionFileName + \" file in jar resources does not exists\");\n        }\n    }\n\n    @Override\n    public boolean isValidPlatformVersion() throws PlatformException {\n        return getSupportedDatabaseSchemaVersion().equals(retrieveDatabaseSchemaVersion());\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-resources/src/main/resources/PLATFORM_ENGINE_VERSION",
    "content": "@version@"
  },
  {
    "path": "platform/platform-resources/src/main/resources/platform_engine/bonita-platform-custom.xml",
    "content": "<beans xmlns=\"http://www.springframework.org/schema/beans\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:p=\"http://www.springframework.org/schema/p\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd\">\n\n    <!-- ADD ANY BEAN DEFINITION YOU WANT TO BE AVAILABLE TO THE PLATFORM ACCESSOR -->\n\n</beans>\n"
  },
  {
    "path": "platform/platform-resources/src/main/resources/platform_portal/security-config.properties",
    "content": "#Enable/disable CSRF security filter\nsecurity.csrf.enabled true\n#Enable/disable the Sanitizer protection activation (true/false). This sanitizer protects against multiple attacks such as XSS, but may restrict the use of some character sequences.\nsecurity.sanitizer.enabled true\n#Name of the Attributes excluded from sanitizer protection (comma separated)\nsecurity.sanitizer.exclude email,password,password_confirm\n#Add  or not the secure flag to the CSRF token cookie (HTTPS only)\nsecurity.csrf.cookie.secure false\n#X-Frame-Options response header value\nbonita.runtime.security.csrf.header.frame.options=SAMEORIGIN\n#Content-Security-Policy response header value\nbonita.runtime.security.csrf.header.content.security.policy=frame-ancestors 'self';\n"
  },
  {
    "path": "platform/platform-resources/src/main/resources/sql/h2/cleanTables.sql",
    "content": "DELETE FROM arch_contract_data;\nDELETE FROM contract_data;\n\nDELETE FROM actormember;\nDELETE FROM actor;\nDELETE FROM processcategorymapping;\nDELETE FROM category;\nDELETE FROM arch_process_comment;\nDELETE FROM process_comment;\nDELETE FROM process_definition;\nDELETE FROM arch_document_mapping;\nDELETE FROM document;\nDELETE FROM document_mapping;\nDELETE FROM arch_flownode_instance;\nDELETE FROM arch_process_instance;\nDELETE FROM arch_connector_instance;\nDELETE FROM arch_multi_biz_data;\nDELETE FROM arch_ref_biz_data_inst;\nDELETE FROM multi_biz_data;\nDELETE FROM ref_biz_data_inst;\nDELETE FROM pending_mapping;\nDELETE FROM message_instance;\nDELETE FROM waiting_event;\nDELETE FROM event_trigger_instance;\nDELETE FROM connector_instance;\nDELETE FROM flownode_instance;\nDELETE FROM process_instance;\nDELETE FROM processsupervisor;\nDELETE FROM business_app_menu;\nDELETE FROM business_app_page;\nDELETE FROM business_app;\nDELETE FROM command;\nDELETE FROM arch_data_instance;\nDELETE FROM data_instance;\nDELETE FROM dependencymapping;\nDELETE FROM dependency;\nDELETE FROM user_membership;\nDELETE FROM custom_usr_inf_val;\nDELETE FROM custom_usr_inf_def;\nDELETE FROM user_contactinfo;\nDELETE FROM user_login;\nDELETE FROM user_;\nDELETE FROM role;\nDELETE FROM group_;\nDELETE FROM queriable_log;\nDELETE FROM page;\nDELETE FROM sequence;\nDELETE FROM profilemember;\nDELETE FROM profile;\nDELETE FROM job_log;\nDELETE FROM job_param;\nDELETE FROM job_desc;\nDELETE FROM tenant;\nDELETE FROM platformCommand;\nDELETE FROM form_mapping;\nDELETE FROM page_mapping;\nDELETE FROM proc_parameter;\nDELETE FROM arch_bpm_failure;\nDELETE FROM bpm_failure;\nDELETE FROM data_retention_config;\nDELETE FROM data_retention_bdm_tracking;\nDELETE FROM delegation_rule_process;\nDELETE FROM delegation_rule;\n\n-- do NOT clear directly PLATFORM table, Hibernate needs to update its cache to know the platform has been deleted\n "
  },
  {
    "path": "platform/platform-resources/src/main/resources/sql/h2/createQuartzTables.sql",
    "content": "CREATE TABLE QRTZ_CALENDARS (\n  SCHED_NAME VARCHAR(120) NOT NULL,\n  CALENDAR_NAME VARCHAR (200)  NOT NULL ,\n  CALENDAR IMAGE NOT NULL\n);\n\nCREATE TABLE QRTZ_CRON_TRIGGERS (\n  SCHED_NAME VARCHAR(120) NOT NULL,\n  TRIGGER_NAME VARCHAR (200)  NOT NULL ,\n  TRIGGER_GROUP VARCHAR (200)  NOT NULL ,\n  CRON_EXPRESSION VARCHAR (120)  NOT NULL ,\n  TIME_ZONE_ID VARCHAR (80) \n);\n\nCREATE TABLE QRTZ_FIRED_TRIGGERS (\n  SCHED_NAME VARCHAR(120) NOT NULL,\n  ENTRY_ID VARCHAR (95)  NOT NULL ,\n  TRIGGER_NAME VARCHAR (200)  NOT NULL ,\n  TRIGGER_GROUP VARCHAR (200)  NOT NULL ,\n  INSTANCE_NAME VARCHAR (200)  NOT NULL ,\n  FIRED_TIME BIGINT NOT NULL ,\n  SCHED_TIME BIGINT NOT NULL ,\n  PRIORITY INTEGER NOT NULL ,\n  STATE VARCHAR (16)  NOT NULL,\n  JOB_NAME VARCHAR (200)  NULL ,\n  JOB_GROUP VARCHAR (200)  NULL ,\n  IS_NONCONCURRENT BOOLEAN  NULL ,\n  REQUESTS_RECOVERY BOOLEAN  NULL \n);\n\nCREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS (\n  SCHED_NAME VARCHAR(120) NOT NULL,\n  TRIGGER_GROUP VARCHAR (200)  NOT NULL \n);\n\nCREATE TABLE QRTZ_SCHEDULER_STATE (\n  SCHED_NAME VARCHAR(120) NOT NULL,\n  INSTANCE_NAME VARCHAR (200)  NOT NULL ,\n  LAST_CHECKIN_TIME BIGINT NOT NULL ,\n  CHECKIN_INTERVAL BIGINT NOT NULL\n);\n\nCREATE TABLE QRTZ_LOCKS (\n  SCHED_NAME VARCHAR(120) NOT NULL,\n  LOCK_NAME VARCHAR (40)  NOT NULL \n);\n\nCREATE TABLE QRTZ_JOB_DETAILS (\n  SCHED_NAME VARCHAR(120) NOT NULL,\n  JOB_NAME VARCHAR (200)  NOT NULL ,\n  JOB_GROUP VARCHAR (200)  NOT NULL ,\n  DESCRIPTION VARCHAR (250) NULL ,\n  JOB_CLASS_NAME VARCHAR (250)  NOT NULL ,\n  IS_DURABLE BOOLEAN  NOT NULL ,\n  IS_NONCONCURRENT BOOLEAN  NOT NULL ,\n  IS_UPDATE_DATA BOOLEAN  NOT NULL ,\n  REQUESTS_RECOVERY BOOLEAN  NOT NULL ,\n  JOB_DATA IMAGE NULL\n);\n\nCREATE TABLE QRTZ_SIMPLE_TRIGGERS (\n  SCHED_NAME VARCHAR(120) NOT NULL,\n  TRIGGER_NAME VARCHAR (200)  NOT NULL ,\n  TRIGGER_GROUP VARCHAR (200)  NOT NULL ,\n  REPEAT_COUNT BIGINT NOT NULL ,\n  REPEAT_INTERVAL BIGINT NOT NULL ,\n  TIMES_TRIGGERED BIGINT NOT NULL\n);\n\nCREATE TABLE QRTZ_SIMPROP_TRIGGERS\n  (          \n    SCHED_NAME VARCHAR(120) NOT NULL,\n    TRIGGER_NAME VARCHAR(200) NOT NULL,\n    TRIGGER_GROUP VARCHAR(200) NOT NULL,\n    STR_PROP_1 VARCHAR(512) NULL,\n    STR_PROP_2 VARCHAR(512) NULL,\n    STR_PROP_3 VARCHAR(512) NULL,\n    INT_PROP_1 INTEGER NULL,\n    INT_PROP_2 INTEGER NULL,\n    LONG_PROP_1 BIGINT NULL,\n    LONG_PROP_2 BIGINT NULL,\n    DEC_PROP_1 NUMERIC(13,4) NULL,\n    DEC_PROP_2 NUMERIC(13,4) NULL,\n    BOOL_PROP_1 BOOLEAN NULL,\n    BOOL_PROP_2 BOOLEAN NULL\n);\n\nCREATE TABLE QRTZ_BLOB_TRIGGERS (\n  SCHED_NAME VARCHAR(120) NOT NULL,\n  TRIGGER_NAME VARCHAR (200)  NOT NULL ,\n  TRIGGER_GROUP VARCHAR (200)  NOT NULL ,\n  BLOB_DATA IMAGE NULL\n);\n\nCREATE TABLE QRTZ_TRIGGERS (\n  SCHED_NAME VARCHAR(120) NOT NULL,\n  TRIGGER_NAME VARCHAR (200)  NOT NULL ,\n  TRIGGER_GROUP VARCHAR (200)  NOT NULL ,\n  JOB_NAME VARCHAR (200)  NOT NULL ,\n  JOB_GROUP VARCHAR (200)  NOT NULL ,\n  DESCRIPTION VARCHAR (250) NULL ,\n  NEXT_FIRE_TIME BIGINT NULL ,\n  PREV_FIRE_TIME BIGINT NULL ,\n  PRIORITY INTEGER NULL ,\n  TRIGGER_STATE VARCHAR (16)  NOT NULL ,\n  TRIGGER_TYPE VARCHAR (8)  NOT NULL ,\n  START_TIME BIGINT NOT NULL ,\n  END_TIME BIGINT NULL ,\n  CALENDAR_NAME VARCHAR (200)  NULL ,\n  MISFIRE_INSTR SMALLINT NULL ,\n  JOB_DATA IMAGE NULL\n);\n\nALTER TABLE QRTZ_CALENDARS  ADD\n  CONSTRAINT PK_QRTZ_CALENDARS PRIMARY KEY  \n  (\n    SCHED_NAME,\n    CALENDAR_NAME\n  );\n\nALTER TABLE QRTZ_CRON_TRIGGERS  ADD\n  CONSTRAINT PK_QRTZ_CRON_TRIGGERS PRIMARY KEY  \n  (\n    SCHED_NAME,\n    TRIGGER_NAME,\n    TRIGGER_GROUP\n  );\n\nALTER TABLE QRTZ_FIRED_TRIGGERS  ADD\n  CONSTRAINT PK_QRTZ_FIRED_TRIGGERS PRIMARY KEY  \n  (\n    SCHED_NAME,\n    ENTRY_ID\n  );\n\nALTER TABLE QRTZ_PAUSED_TRIGGER_GRPS  ADD\n  CONSTRAINT PK_QRTZ_PAUSED_TRIGGER_GRPS PRIMARY KEY  \n  (\n    SCHED_NAME,\n    TRIGGER_GROUP\n  );\n\nALTER TABLE QRTZ_SCHEDULER_STATE  ADD\n  CONSTRAINT PK_QRTZ_SCHEDULER_STATE PRIMARY KEY  \n  (\n    SCHED_NAME,\n    INSTANCE_NAME\n  );\n\nALTER TABLE QRTZ_LOCKS  ADD\n  CONSTRAINT PK_QRTZ_LOCKS PRIMARY KEY  \n  (\n    SCHED_NAME,\n    LOCK_NAME\n  );\n\nALTER TABLE QRTZ_JOB_DETAILS  ADD\n  CONSTRAINT PK_QRTZ_JOB_DETAILS PRIMARY KEY  \n  (\n    SCHED_NAME,\n    JOB_NAME,\n    JOB_GROUP\n  );\n\nALTER TABLE QRTZ_SIMPLE_TRIGGERS  ADD\n  CONSTRAINT PK_QRTZ_SIMPLE_TRIGGERS PRIMARY KEY  \n  (\n    SCHED_NAME,\n    TRIGGER_NAME,\n    TRIGGER_GROUP\n  );\n\nALTER TABLE QRTZ_SIMPROP_TRIGGERS  ADD\n  CONSTRAINT PK_QRTZ_SIMPROP_TRIGGERS PRIMARY KEY  \n  (\n    SCHED_NAME,\n    TRIGGER_NAME,\n    TRIGGER_GROUP\n  );\n\nALTER TABLE QRTZ_TRIGGERS  ADD\n  CONSTRAINT PK_QRTZ_TRIGGERS PRIMARY KEY  \n  (\n    SCHED_NAME,\n    TRIGGER_NAME,\n    TRIGGER_GROUP\n  );\n\nALTER TABLE QRTZ_CRON_TRIGGERS ADD\n  CONSTRAINT FK_QRTZ_CRON_TRIGGERS FOREIGN KEY\n  (\n    SCHED_NAME,\n    TRIGGER_NAME,\n    TRIGGER_GROUP\n  ) REFERENCES QRTZ_TRIGGERS (\n    SCHED_NAME,\n    TRIGGER_NAME,\n    TRIGGER_GROUP\n  ) ON DELETE CASCADE;\n\n\nALTER TABLE QRTZ_SIMPLE_TRIGGERS ADD\n  CONSTRAINT FK_QRTZ_SIMPLE_TRIGGERS FOREIGN KEY\n  (\n    SCHED_NAME,\n    TRIGGER_NAME,\n    TRIGGER_GROUP\n  ) REFERENCES QRTZ_TRIGGERS (\n    SCHED_NAME,\n    TRIGGER_NAME,\n    TRIGGER_GROUP\n  ) ON DELETE CASCADE;\n\nALTER TABLE QRTZ_SIMPROP_TRIGGERS ADD\n  CONSTRAINT FK_QRTZ_SIMPROP_TRIGGERS FOREIGN KEY\n  (\n    SCHED_NAME,\n    TRIGGER_NAME,\n    TRIGGER_GROUP\n  ) REFERENCES QRTZ_TRIGGERS (\n    SCHED_NAME,\n    TRIGGER_NAME,\n    TRIGGER_GROUP\n  ) ON DELETE CASCADE;\n\n\nALTER TABLE QRTZ_TRIGGERS ADD\n  CONSTRAINT FK_QRTZ_TRIGGERS FOREIGN KEY\n  (\n    SCHED_NAME,\n    JOB_NAME,\n    JOB_GROUP\n  ) REFERENCES QRTZ_JOB_DETAILS (\n    SCHED_NAME,\n    JOB_NAME,\n    JOB_GROUP\n  );\n  \nCREATE INDEX IDX_QRTZ_J_REQ_RECOVERY ON QRTZ_JOB_DETAILS(SCHED_NAME,REQUESTS_RECOVERY);\nCREATE INDEX IDX_QRTZ_J_GRP ON QRTZ_JOB_DETAILS(SCHED_NAME,JOB_GROUP);\n\nCREATE INDEX IDX_QRTZ_T_J ON QRTZ_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);\nCREATE INDEX IDX_QRTZ_T_JG ON QRTZ_TRIGGERS(SCHED_NAME,JOB_GROUP);\nCREATE INDEX IDX_QRTZ_T_C ON QRTZ_TRIGGERS(SCHED_NAME,CALENDAR_NAME);\nCREATE INDEX IDX_QRTZ_T_G ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);\nCREATE INDEX IDX_QRTZ_T_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE);\nCREATE INDEX IDX_QRTZ_T_N_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE);\nCREATE INDEX IDX_QRTZ_T_N_G_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE);\nCREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME ON QRTZ_TRIGGERS(SCHED_NAME,NEXT_FIRE_TIME);\nCREATE INDEX IDX_QRTZ_T_NFT_ST ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME);\nCREATE INDEX IDX_QRTZ_T_NFT_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME);\nCREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE);\nCREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE);\n\nCREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME);\nCREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY);\nCREATE INDEX IDX_QRTZ_FT_J_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);\nCREATE INDEX IDX_QRTZ_FT_JG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_GROUP);\nCREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP);\nCREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);"
  },
  {
    "path": "platform/platform-resources/src/main/resources/sql/h2/createTables.sql",
    "content": "CREATE TABLE configuration (\n  content_type VARCHAR(50) NOT NULL,\n  resource_name VARCHAR(120) NOT NULL,\n  resource_content LONGBLOB NOT NULL,\n  CONSTRAINT pk_configuration PRIMARY KEY (content_type, resource_name)\n);\nCREATE INDEX idx_configuration ON configuration (content_type);\n\nCREATE TABLE contract_data (\n  id BIGINT NOT NULL,\n  kind VARCHAR(20) NOT NULL,\n  scopeId BIGINT NOT NULL,\n  name VARCHAR(50) NOT NULL,\n  val CLOB,\n  CONSTRAINT pk_contract_data PRIMARY KEY (id, scopeId),\n  CONSTRAINT uk_contract_data_kind_scopeid_name UNIQUE (kind, scopeId, name)\n);\n\nCREATE TABLE arch_contract_data (\n  id BIGINT NOT NULL,\n  kind VARCHAR(20) NOT NULL,\n  scopeId BIGINT NOT NULL,\n  name VARCHAR(50) NOT NULL,\n  val CLOB,\n  archiveDate BIGINT NOT NULL,\n  sourceObjectId BIGINT NOT NULL,\n  CONSTRAINT pk_arch_contract_data PRIMARY KEY (id, scopeId),\n  CONSTRAINT uk_arch_contract_data_kind_scopeid_name UNIQUE (kind, scopeId, name)\n);\n\nCREATE TABLE actor (\n  id BIGINT NOT NULL,\n  scopeId BIGINT NOT NULL,\n  name VARCHAR(50) NOT NULL,\n  displayName VARCHAR(75),\n  description TEXT,\n  initiator BOOLEAN,\n  CONSTRAINT uk_actor_id_scopeid_name UNIQUE (id, scopeId, name),\n  CONSTRAINT pk_actor PRIMARY KEY (id)\n);\n\nCREATE TABLE actormember (\n  id BIGINT NOT NULL,\n  actorId BIGINT NOT NULL,\n  userId BIGINT NOT NULL,\n  groupId BIGINT NOT NULL,\n  roleId BIGINT NOT NULL,\n  CONSTRAINT uk_actormember_actorid_userid_groupid_roleid UNIQUE (actorId, userId, groupId, roleId),\n  CONSTRAINT pk_actormember PRIMARY KEY (id)\n);\n\nALTER TABLE actormember ADD CONSTRAINT fk_actormember_actorid FOREIGN KEY (actorId) REFERENCES actor(id);\n\nCREATE TABLE category (\n  id BIGINT NOT NULL,\n  name VARCHAR(50) NOT NULL,\n  creator BIGINT,\n  description LONGVARCHAR,\n  creationDate BIGINT NOT NULL,\n  lastUpdateDate BIGINT NOT NULL,\n  CONSTRAINT pk_category PRIMARY KEY (id),\n  CONSTRAINT uk_category_name UNIQUE (name)\n);\n\nCREATE TABLE processcategorymapping (\n  id BIGINT NOT NULL,\n  categoryid BIGINT NOT NULL,\n  processid BIGINT NOT NULL,\n  CONSTRAINT pk_processcategorymapping PRIMARY KEY (id),\n  CONSTRAINT uk_processcategorymapping_categoryid_processid UNIQUE (categoryid, processid)\n);\nALTER TABLE processcategorymapping ADD CONSTRAINT fk_processcategorymapping_categoryid FOREIGN KEY (categoryid) REFERENCES category(id) ON DELETE CASCADE;\n\nCREATE TABLE arch_process_comment(\n  id BIGINT NOT NULL,\n  userId BIGINT,\n  processInstanceId BIGINT NOT NULL,\n  postDate BIGINT NOT NULL,\n  content VARCHAR(512) NOT NULL,\n  archiveDate BIGINT NOT NULL,\n  sourceObjectId BIGINT NOT NULL,\n  CONSTRAINT pk_arch_process_comment PRIMARY KEY (id)\n);\nCREATE INDEX idx1_arch_process_comment on arch_process_comment (sourceobjectid);\nCREATE INDEX idx2_arch_process_comment on arch_process_comment (processInstanceId, archivedate);\n\nCREATE TABLE process_comment (\n  id BIGINT NOT NULL,\n  kind VARCHAR(25) NOT NULL,\n  userId BIGINT,\n  processInstanceId BIGINT NOT NULL,\n  postDate BIGINT NOT NULL,\n  content VARCHAR(512) NOT NULL,\n  CONSTRAINT pk_process_comment PRIMARY KEY (id)\n);\nCREATE INDEX idx1_process_comment on process_comment (processInstanceId);\n\nCREATE TABLE process_content (\n  id BIGINT NOT NULL,\n  content MEDIUMTEXT NOT NULL,\n  CONSTRAINT pk_process_content PRIMARY KEY (id)\n);\n\nCREATE TABLE process_definition (\n  id BIGINT NOT NULL,\n  processId BIGINT NOT NULL,\n  name VARCHAR(150) NOT NULL,\n  version VARCHAR(50) NOT NULL,\n  description VARCHAR(255),\n  deploymentDate BIGINT NOT NULL,\n  deployedBy BIGINT NOT NULL,\n  activationState VARCHAR(30) NOT NULL,\n  configurationState VARCHAR(30) NOT NULL,\n  displayName VARCHAR(75),\n  displayDescription VARCHAR(255),\n  lastUpdateDate BIGINT,\n  categoryId BIGINT,\n  iconPath VARCHAR(255),\n  content_id BIGINT NOT NULL,\n  CONSTRAINT pk_process_definition PRIMARY KEY (id),\n  CONSTRAINT uk_process_definition_name_version UNIQUE (name, version)\n);\nALTER TABLE process_definition ADD CONSTRAINT fk_process_definition_content_id FOREIGN KEY (content_id) REFERENCES process_content(id);\n\nCREATE TABLE document (\n  id BIGINT NOT NULL,\n  author BIGINT,\n  creationdate BIGINT NOT NULL,\n  hascontent BOOLEAN NOT NULL,\n  filename VARCHAR(255),\n  mimetype VARCHAR(255),\n  url VARCHAR(1024),\n  content LONGBLOB NULL,\n  CONSTRAINT pk_document PRIMARY KEY (id)\n);\n\nCREATE TABLE document_mapping (\n  id BIGINT NOT NULL,\n  processinstanceid BIGINT NOT NULL,\n  documentid BIGINT NOT NULL,\n  name VARCHAR(50) NOT NULL,\n  description TEXT,\n  version VARCHAR(50) NOT NULL,\n  index_ INT NOT NULL,\n  CONSTRAINT pk_document_mapping PRIMARY KEY (id)\n);\nALTER TABLE document_mapping ADD CONSTRAINT fk_document_mapping_documentid FOREIGN KEY (documentid) REFERENCES document(id) ON DELETE CASCADE;\n\nCREATE TABLE arch_document_mapping (\n  id BIGINT NOT NULL,\n  sourceObjectId BIGINT,\n  processinstanceid BIGINT NOT NULL,\n  documentid BIGINT NOT NULL,\n  name VARCHAR(50) NOT NULL,\n  description TEXT,\n  version VARCHAR(50) NOT NULL,\n  index_ INT NOT NULL,\n  archiveDate BIGINT NOT NULL,\n  CONSTRAINT pk_arch_document_mapping PRIMARY KEY (id)\n);\nCREATE INDEX idx_a_doc_mp_pr_id ON arch_document_mapping (processinstanceid);\nALTER TABLE arch_document_mapping ADD CONSTRAINT fk_arch_document_mapping_documentid FOREIGN KEY (documentid) REFERENCES document(id) ON DELETE CASCADE;\n\nCREATE TABLE arch_process_instance (\n  id BIGINT NOT NULL,\n  name VARCHAR(75) NOT NULL,\n  processDefinitionId BIGINT NOT NULL,\n  description VARCHAR(255),\n  startDate BIGINT NOT NULL,\n  startedBy BIGINT NOT NULL,\n  startedBySubstitute BIGINT NOT NULL,\n  endDate BIGINT NOT NULL,\n  archiveDate BIGINT NOT NULL,\n  stateId INT NOT NULL,\n  lastUpdate BIGINT NOT NULL,\n  rootProcessInstanceId BIGINT,\n  callerId BIGINT,\n  sourceObjectId BIGINT NOT NULL,\n  stringIndex1 VARCHAR(255),\n  stringIndex2 VARCHAR(255),\n  stringIndex3 VARCHAR(255),\n  stringIndex4 VARCHAR(255),\n  stringIndex5 VARCHAR(255),\n  CONSTRAINT pk_arch_process_instance PRIMARY KEY (id)\n);\nCREATE INDEX idx1_arch_process_instance ON arch_process_instance (sourceObjectId, rootProcessInstanceId, callerId);\nCREATE INDEX idx2_arch_process_instance ON arch_process_instance (processDefinitionId, archiveDate);\nCREATE INDEX idx3_arch_process_instance ON arch_process_instance (sourceObjectId, callerId, stateId);\n\nCREATE TABLE arch_flownode_instance (\n  id BIGINT NOT NULL,\n  flownodeDefinitionId BIGINT NOT NULL,\n  kind VARCHAR(25) NOT NULL,\n  sourceObjectId BIGINT,\n  archiveDate BIGINT NOT NULL,\n  rootContainerId BIGINT NOT NULL,\n  parentContainerId BIGINT NOT NULL,\n  name VARCHAR(255) NOT NULL,\n  displayName VARCHAR(255),\n  displayDescription VARCHAR(255),\n  stateId INT NOT NULL,\n  stateName VARCHAR(50),\n  terminal BOOLEAN NOT NULL,\n  stable BOOLEAN ,\n  actorId BIGINT NULL,\n  assigneeId BIGINT DEFAULT 0 NOT NULL,\n  reachedStateDate BIGINT,\n  lastUpdateDate BIGINT,\n  expectedEndDate BIGINT,\n  claimedDate BIGINT,\n  priority TINYINT,\n  gatewayType VARCHAR(50),\n  hitBys VARCHAR(255),\n  logicalGroup1 BIGINT NOT NULL,\n  logicalGroup2 BIGINT NOT NULL,\n  logicalGroup3 BIGINT,\n  logicalGroup4 BIGINT NOT NULL,\n  loop_counter INT,\n  loop_max INT,\n  loopCardinality INT,\n  loopDataInputRef VARCHAR(255),\n  loopDataOutputRef VARCHAR(255),\n  description VARCHAR(255),\n  sequential BOOLEAN,\n  dataInputItemRef VARCHAR(255),\n  dataOutputItemRef VARCHAR(255),\n  nbActiveInst INT,\n  nbCompletedInst INT,\n  nbTerminatedInst INT,\n  executedBy BIGINT,\n  executedBySubstitute BIGINT,\n  activityInstanceId BIGINT,\n  aborting BOOLEAN NOT NULL,\n  triggeredByEvent BOOLEAN,\n  interrupting BOOLEAN,\n  CONSTRAINT pk_arch_flownode_instance PRIMARY KEY (id)\n);\nCREATE INDEX idx_afi_kind_lg2_executedBy ON arch_flownode_instance(logicalGroup2, kind, executedBy);\nCREATE INDEX idx_afi_kind_lg3 ON arch_flownode_instance(kind, logicalGroup3);\nCREATE INDEX idx_afi_lg4 ON arch_flownode_instance(logicalGroup4);\nCREATE INDEX idx_afi_sourceid_kind ON arch_flownode_instance (sourceObjectId, kind);\nCREATE INDEX idx1_afi_root_parent ON arch_flownode_instance (rootContainerId, parentContainerId);\nCREATE INDEX idx_lg4_lg2 on arch_flownode_instance(logicalGroup4, logicalGroup2);\n\nCREATE TABLE arch_connector_instance (\n  id BIGINT NOT NULL,\n  containerId BIGINT NOT NULL,\n  containerType VARCHAR(10) NOT NULL,\n  connectorId VARCHAR(255) NOT NULL,\n  version VARCHAR(50) NOT NULL,\n  name VARCHAR(255) NOT NULL,\n  activationEvent VARCHAR(30),\n  state VARCHAR(50),\n  sourceObjectId BIGINT,\n  archiveDate BIGINT NOT NULL,\n  CONSTRAINT pk_arch_connector_instance PRIMARY KEY (id)\n);\n\nCREATE INDEX idx1_arch_connector_instance ON arch_connector_instance (containerId, containerType);\nCREATE TABLE process_instance (\n  id BIGINT NOT NULL,\n  name VARCHAR(75) NOT NULL,\n  processDefinitionId BIGINT NOT NULL,\n  description VARCHAR(255),\n  startDate BIGINT NOT NULL,\n  startedBy BIGINT NOT NULL,\n  startedBySubstitute BIGINT NOT NULL,\n  endDate BIGINT NOT NULL,\n  stateId INT NOT NULL,\n  stateCategory VARCHAR(50) NOT NULL,\n  lastUpdate BIGINT NOT NULL,\n  containerId BIGINT,\n  rootProcessInstanceId BIGINT,\n  callerId BIGINT,\n  callerType VARCHAR(50),\n  interruptingEventId BIGINT,\n  stringIndex1 VARCHAR(255),\n  stringIndex2 VARCHAR(255),\n  stringIndex3 VARCHAR(255),\n  stringIndex4 VARCHAR(255),\n  stringIndex5 VARCHAR(255),\n  PRIMARY KEY (id)\n);\n\nCREATE INDEX idx1_proc_inst_pdef_state ON process_instance (processdefinitionid, stateid);\n\nCREATE TABLE flownode_instance (\n  id BIGINT NOT NULL,\n  flownodeDefinitionId BIGINT NOT NULL,\n  kind VARCHAR(25) NOT NULL,\n  rootContainerId BIGINT NOT NULL,\n  parentContainerId BIGINT NOT NULL,\n  name VARCHAR(255) NOT NULL,\n  displayName VARCHAR(255),\n  displayDescription VARCHAR(255),\n  stateId INT NOT NULL,\n  stateName VARCHAR(50),\n  prev_state_id INT NOT NULL,\n  terminal BOOLEAN NOT NULL,\n  stable BOOLEAN ,\n  actorId BIGINT NULL,\n  assigneeId BIGINT DEFAULT 0 NOT NULL,\n  reachedStateDate BIGINT,\n  lastUpdateDate BIGINT,\n  expectedEndDate BIGINT,\n  claimedDate BIGINT,\n  priority TINYINT,\n  gatewayType VARCHAR(50),\n  hitBys VARCHAR(255),\n  stateCategory VARCHAR(50) NOT NULL,\n  logicalGroup1 BIGINT NOT NULL,\n  logicalGroup2 BIGINT NOT NULL,\n  logicalGroup3 BIGINT,\n  logicalGroup4 BIGINT NOT NULL,\n  loop_counter INT,\n  loop_max INT,\n  description VARCHAR(255),\n  sequential BOOLEAN,\n  loopDataInputRef VARCHAR(255),\n  loopDataOutputRef VARCHAR(255),\n  dataInputItemRef VARCHAR(255),\n  dataOutputItemRef VARCHAR(255),\n  loopCardinality INT,\n  nbActiveInst INT,\n  nbCompletedInst INT,\n  nbTerminatedInst INT,\n  executedBy BIGINT,\n  executedBySubstitute BIGINT,\n  activityInstanceId BIGINT,\n  state_executing BOOLEAN DEFAULT FALSE,\n  abortedByBoundary BIGINT,\n  triggeredByEvent BOOLEAN,\n  interrupting BOOLEAN,\n  tokenCount INT NOT NULL,\n  PRIMARY KEY (id)\n);\nCREATE INDEX idx_fni_rootcontid ON flownode_instance (rootContainerId);\nCREATE INDEX idx_fni_loggroup4 ON flownode_instance (logicalGroup4);\nCREATE INDEX idx_fni_loggroup3_terminal ON flownode_instance(logicalgroup3, terminal);\nCREATE INDEX idx_fn_lg2_state ON flownode_instance (logicalGroup2, stateName);\nCREATE INDEX idx_fni_activity_instance_id_kind ON flownode_instance(activityInstanceId, kind);\n\nCREATE TABLE connector_instance (\n  id BIGINT NOT NULL,\n  containerId BIGINT NOT NULL,\n  containerType VARCHAR(10) NOT NULL,\n  connectorId VARCHAR(255) NOT NULL,\n  version VARCHAR(50) NOT NULL,\n  name VARCHAR(255) NOT NULL,\n  activationEvent VARCHAR(30),\n  state VARCHAR(50),\n  executionOrder INT,\n  exceptionMessage VARCHAR(255),\n  stackTrace CLOB,\n  CONSTRAINT pk_connector_instance PRIMARY KEY (id)\n);\nCREATE INDEX idx_ci_container_activation ON connector_instance (containerId, containerType, activationEvent);\n\nCREATE TABLE event_trigger_instance (\n  \tid BIGINT NOT NULL,\n  \teventInstanceId BIGINT NOT NULL,\n  \teventInstanceName VARCHAR(50),\n  \texecutionDate BIGINT,\n  \tjobTriggerName VARCHAR(255),\n    CONSTRAINT pk_event_trigger_instance PRIMARY KEY (id)\n);\n\nCREATE TABLE waiting_event (\n  \tid BIGINT NOT NULL,\n  \tkind VARCHAR(15) NOT NULL,\n  \teventType VARCHAR(50),\n  \tmessageName VARCHAR(255),\n  \tsignalName VARCHAR(255),\n  \terrorCode VARCHAR(255),\n  \tprocessName VARCHAR(150),\n  \tflowNodeName VARCHAR(50),\n  \tflowNodeDefinitionId BIGINT,\n  \tsubProcessId BIGINT,\n  \tprocessDefinitionId BIGINT,\n  \trootProcessInstanceId BIGINT,\n  \tparentProcessInstanceId BIGINT,\n  \tflowNodeInstanceId BIGINT,\n  \trelatedActivityInstanceId BIGINT,\n  \tlocked BOOLEAN,\n  \tactive BOOLEAN,\n  \tprogress TINYINT,\n  \tcorrelation1 VARCHAR(128),\n  \tcorrelation2 VARCHAR(128),\n  \tcorrelation3 VARCHAR(128),\n  \tcorrelation4 VARCHAR(128),\n  \tcorrelation5 VARCHAR(128),\n    CONSTRAINT pk_waiting_event PRIMARY KEY (id)\n);\nCREATE INDEX idx_waiting_event ON waiting_event (progress, kind, locked, active);\nCREATE INDEX idx_waiting_event_correl ON waiting_event (correlation1, correlation2, correlation3, correlation4, correlation5);\n\nCREATE TABLE message_instance (\n  \tid BIGINT NOT NULL,\n  \tmessageName VARCHAR(255) NOT NULL,\n  \ttargetProcess VARCHAR(255) NOT NULL,\n  \ttargetFlowNode VARCHAR(255) NULL,\n  \tlocked BOOLEAN NOT NULL,\n  \thandled BOOLEAN NOT NULL,\n  \tprocessDefinitionId BIGINT NOT NULL,\n  \tflowNodeName VARCHAR(255),\n  \tcorrelation1 VARCHAR(128),\n  \tcorrelation2 VARCHAR(128),\n  \tcorrelation3 VARCHAR(128),\n  \tcorrelation4 VARCHAR(128),\n  \tcorrelation5 VARCHAR(128),\n  \tcreationDate BIGINT NOT NULL,\n    CONSTRAINT pk_message_instance PRIMARY KEY (id)\n);\nCREATE INDEX idx_message_instance ON message_instance (messageName, targetProcess, correlation1, correlation2, correlation3);\nCREATE INDEX idx_message_instance_correl ON message_instance (correlation1, correlation2, correlation3, correlation4, correlation5);\n\nCREATE TABLE pending_mapping (\n  \tid BIGINT NOT NULL,\n  \tactivityId BIGINT NOT NULL,\n  \tactorId BIGINT,\n  \tuserId BIGINT,\n    CONSTRAINT pk_pending_mapping PRIMARY KEY (id),\n    CONSTRAINT uk_pending_mapping_activityid_userid_actorid UNIQUE (activityId, userId, actorId)\n);\nALTER TABLE pending_mapping ADD CONSTRAINT fk_pending_mapping_activityid FOREIGN KEY (activityId) REFERENCES flownode_instance(id);\n\nCREATE TABLE ref_biz_data_inst (\n  id BIGINT NOT NULL,\n  kind VARCHAR(15) NOT NULL,\n  name VARCHAR(255) NOT NULL,\n  proc_inst_id BIGINT,\n  fn_inst_id BIGINT,\n  data_id BIGINT,\n  data_classname VARCHAR(255) NOT NULL,\n  CONSTRAINT pk_ref_biz_data_inst PRIMARY KEY (id)\n);\nCREATE INDEX idx_biz_data_inst2 ON ref_biz_data_inst (fn_inst_id);\nCREATE INDEX idx_biz_data_inst3 ON ref_biz_data_inst (proc_inst_id);\nALTER TABLE ref_biz_data_inst ADD CONSTRAINT fk_ref_biz_data_inst_proc_inst_id FOREIGN KEY (proc_inst_id) REFERENCES process_instance(id) ON DELETE CASCADE;\nALTER TABLE ref_biz_data_inst ADD CONSTRAINT fk_ref_biz_data_inst_fn_inst_id FOREIGN KEY (fn_inst_id) REFERENCES flownode_instance(id) ON DELETE CASCADE;\n\nCREATE TABLE multi_biz_data (\n  id BIGINT NOT NULL,\n  idx BIGINT NOT NULL,\n  data_id BIGINT NOT NULL,\n  CONSTRAINT pk_multi_biz_data PRIMARY KEY (id, data_id)\n);\nALTER TABLE multi_biz_data ADD CONSTRAINT fk_multi_biz_data_id FOREIGN KEY (id) REFERENCES ref_biz_data_inst(id) ON DELETE CASCADE;\n\nCREATE TABLE arch_ref_biz_data_inst (\n  id BIGINT NOT NULL,\n  kind VARCHAR(15) NOT NULL,\n  name VARCHAR(255) NOT NULL,\n  orig_proc_inst_id BIGINT,\n  orig_fn_inst_id BIGINT,\n  data_id BIGINT,\n  data_classname VARCHAR(255) NOT NULL,\n  CONSTRAINT pk_arch_ref_biz_data_inst PRIMARY KEY (id)\n);\nCREATE INDEX idx_arch_biz_data_inst1 ON arch_ref_biz_data_inst (orig_proc_inst_id);\nCREATE INDEX idx_arch_biz_data_inst2 ON arch_ref_biz_data_inst (orig_fn_inst_id);\n\nCREATE TABLE arch_multi_biz_data (\n  id BIGINT NOT NULL,\n  idx BIGINT NOT NULL,\n  data_id BIGINT NOT NULL,\n  CONSTRAINT pk_arch_multi_biz_data PRIMARY KEY (id, data_id)\n);\nALTER TABLE arch_multi_biz_data ADD CONSTRAINT fk_arch_multi_biz_data_id FOREIGN KEY (id) REFERENCES arch_ref_biz_data_inst(id) ON DELETE CASCADE;\n\nCREATE TABLE processsupervisor (\n  id BIGINT NOT NULL,\n  processDefId BIGINT NOT NULL,\n  userId BIGINT NOT NULL,\n  groupId BIGINT NOT NULL,\n  roleId BIGINT NOT NULL,\n  CONSTRAINT pk_processsupervisor PRIMARY KEY (id),\n  CONSTRAINT uk_processsupervisor_processdefid_userid_groupid_roleid UNIQUE (processDefId, userId, groupId, roleId)\n);\n\nCREATE TABLE page (\n  id BIGINT NOT NULL,\n  name VARCHAR(255) NOT NULL,\n  displayName VARCHAR(255) NOT NULL,\n  description LONGVARCHAR,\n  installationDate BIGINT NOT NULL,\n  installedBy BIGINT NOT NULL,\n  provided BOOLEAN,\n  editable BOOLEAN,\n  removable BOOLEAN,\n  lastModificationDate BIGINT NOT NULL,\n  lastUpdatedBy BIGINT NOT NULL,\n  contentName VARCHAR(280) NOT NULL,\n  content LONGBLOB,\n  contentType VARCHAR(50) NOT NULL,\n  processDefinitionId BIGINT NOT NULL,\n  pageHash VARCHAR(32),\n  CONSTRAINT pk_page PRIMARY KEY (id),\n  CONSTRAINT uk_page_name_processdefinitionid UNIQUE (name, processDefinitionId)\n);\n\nCREATE TABLE profile (\n  id BIGINT NOT NULL,\n  isDefault BOOLEAN NOT NULL,\n  name VARCHAR(50) NOT NULL,\n  description LONGVARCHAR,\n  creationDate BIGINT NOT NULL,\n  createdBy BIGINT NOT NULL,\n  lastUpdateDate BIGINT NOT NULL,\n  lastUpdatedBy BIGINT NOT NULL,\n  CONSTRAINT uk_profile_name UNIQUE (name),\n  CONSTRAINT pk_profile PRIMARY KEY (id)\n);\n\nCREATE TABLE profilemember (\n  id BIGINT NOT NULL,\n  profileId BIGINT NOT NULL,\n  userId BIGINT NOT NULL,\n  groupId BIGINT NOT NULL,\n  roleId BIGINT NOT NULL,\n  CONSTRAINT pk_profilemember PRIMARY KEY (id),\n  CONSTRAINT uk_profilemember_profileid_userid_groupid_roleid UNIQUE (profileId, userId, groupId, roleId)\n);\nALTER TABLE profilemember ADD CONSTRAINT fk_profilemember_profileid FOREIGN KEY (profileId) REFERENCES profile(id);\n\nCREATE TABLE business_app (\n  id BIGINT NOT NULL,\n  token VARCHAR(50) NOT NULL,\n  version VARCHAR(50) NOT NULL,\n  description LONGVARCHAR,\n  iconPath VARCHAR(255),\n  creationDate BIGINT NOT NULL,\n  createdBy BIGINT NOT NULL,\n  lastUpdateDate BIGINT NOT NULL,\n  updatedBy BIGINT NOT NULL,\n  state VARCHAR(30) NOT NULL,\n  homePageId BIGINT,\n  profileId BIGINT,\n  layoutId BIGINT,\n  themeId BIGINT,\n  iconMimeType VARCHAR(255),\n  iconContent LONGBLOB,\n  displayName VARCHAR(255) NOT NULL,\n  editable BOOLEAN,\n  internalProfile VARCHAR(255),\n  isLink BOOLEAN DEFAULT FALSE,\n  CONSTRAINT pk_business_app PRIMARY KEY (id),\n  CONSTRAINT uk_business_app_token_version UNIQUE (token, version)\n);\nALTER TABLE business_app ADD CONSTRAINT fk_business_app_profileid FOREIGN KEY (profileId) REFERENCES profile (id);\nALTER TABLE business_app ADD CONSTRAINT fk_business_app_layoutid FOREIGN KEY (layoutId) REFERENCES page (id);\nALTER TABLE business_app ADD CONSTRAINT fk_business_app_themeid FOREIGN KEY (themeId) REFERENCES page (id);\n\nCREATE INDEX idx_app_token ON business_app (token);\nCREATE INDEX idx_app_profile ON business_app (profileId);\nCREATE INDEX idx_app_homepage ON business_app (homePageId);\n\nCREATE TABLE business_app_page (\n  id BIGINT NOT NULL,\n  applicationId BIGINT NOT NULL,\n  pageId BIGINT NOT NULL,\n  token VARCHAR(255) NOT NULL,\n  CONSTRAINT pk_business_app_page PRIMARY KEY (id),\n  CONSTRAINT uk_business_app_page_applicationid_token UNIQUE (applicationId, token)\n);\nALTER TABLE business_app_page ADD CONSTRAINT fk_business_app_page_applicationid FOREIGN KEY (applicationId) REFERENCES business_app (id) ON DELETE CASCADE;\nALTER TABLE business_app_page ADD CONSTRAINT fk_business_app_page_pageid FOREIGN KEY (pageId) REFERENCES page (id);\n\nCREATE INDEX idx_app_page_token ON business_app_page (applicationId, token);\nCREATE INDEX idx_app_page_pageId ON business_app_page (pageId);\n\nCREATE TABLE business_app_menu (\n  id BIGINT NOT NULL,\n  displayName VARCHAR(255) NOT NULL,\n  applicationId BIGINT NOT NULL,\n  applicationPageId BIGINT,\n  parentId BIGINT,\n  index_ BIGINT,\n  CONSTRAINT pk_business_app_menu PRIMARY KEY (id)\n);\n-- cannot have both fk_business_app_menu_applicationid and fk_business_app_menu_applicationpageid because this create to path for deletion of business_app_menu elements:\n-- business_app -> business_app_menu\n-- business_app -> business_app_page -> business_app_menu\n-- this is not allowed in SQL Server\nALTER TABLE business_app_menu ADD CONSTRAINT fk_business_app_menu_applicationid FOREIGN KEY (applicationId) REFERENCES business_app (id);\nALTER TABLE business_app_menu ADD CONSTRAINT fk_business_app_menu_applicationpageid FOREIGN KEY (applicationPageId) REFERENCES business_app_page (id);\nALTER TABLE business_app_menu ADD CONSTRAINT fk_business_app_menu_parentid FOREIGN KEY (parentId) REFERENCES business_app_menu (id);\n\nCREATE INDEX idx_app_menu_app ON business_app_menu (applicationId);\nCREATE INDEX idx_app_menu_page ON business_app_menu (applicationPageId);\nCREATE INDEX idx_app_menu_parent ON business_app_menu (parentId);\n\nCREATE TABLE command (\n  id BIGINT NOT NULL,\n  name VARCHAR(50) NOT NULL,\n  description LONGVARCHAR,\n  IMPLEMENTATION VARCHAR(100) NOT NULL,\n  isSystem BOOLEAN,\n  CONSTRAINT pk_command PRIMARY KEY (id),\n  CONSTRAINT uk_command_name UNIQUE (name)\n);\n\nCREATE TABLE arch_data_instance (\n  id BIGINT NOT NULL,\n  name VARCHAR(50),\n  description VARCHAR(50),\n  transientData BOOLEAN,\n  className VARCHAR(100),\n  containerId BIGINT,\n  containerType VARCHAR(60),\n  namespace VARCHAR(100),\n  element VARCHAR(60),\n  intValue INT,\n  longValue BIGINT,\n  shortTextValue VARCHAR(255),\n  booleanValue BOOLEAN,\n  doubleValue NUMERIC(19,5),\n  floatValue REAL,\n  blobValue MEDIUMBLOB,\n  clobValue CLOB,\n  discriminant VARCHAR(50) NOT NULL,\n  archiveDate BIGINT NOT NULL,\n  sourceObjectId BIGINT NOT NULL,\n  CONSTRAINT pk_arch_data_instance PRIMARY KEY (id)\n);\nCREATE INDEX idx1_arch_data_instance ON arch_data_instance (containerId, containerType, archiveDate, name, sourceObjectId);\nCREATE INDEX idx2_arch_data_instance ON arch_data_instance (sourceObjectId, containerId, archiveDate, id);\n\nCREATE TABLE data_instance (\n  id BIGINT NOT NULL,\n  name VARCHAR(50),\n  description VARCHAR(50),\n  transientData BOOLEAN,\n  className VARCHAR(100),\n  containerId BIGINT,\n  containerType VARCHAR(60),\n  namespace VARCHAR(100),\n  element VARCHAR(60),\n  intValue INT,\n  longValue BIGINT,\n  shortTextValue VARCHAR(255),\n  booleanValue BOOLEAN,\n  doubleValue NUMERIC(19,5),\n  floatValue REAL,\n  blobValue MEDIUMBLOB,\n  clobValue CLOB,\n  discriminant VARCHAR(50) NOT NULL,\n  CONSTRAINT pk_data_instance PRIMARY KEY (id)\n);\nCREATE INDEX idx_datai_container ON data_instance (containerId, containerType, name);\n\nCREATE TABLE dependency (\n  id BIGINT NOT NULL,\n  name VARCHAR(150) NOT NULL,\n  description LONGVARCHAR,\n  filename VARCHAR(255) NOT NULL,\n  value_ LONGVARBINARY NOT NULL,\n  CONSTRAINT pk_dependency PRIMARY KEY (id),\n  CONSTRAINT uk_dependency_name UNIQUE (name)\n);\n\nCREATE TABLE dependencymapping (\n  id BIGINT NOT NULL,\n  artifactid BIGINT NOT NULL,\n  artifacttype VARCHAR(50) NOT NULL,\n  dependencyid BIGINT NOT NULL,\n  CONSTRAINT pk_dependencymapping PRIMARY KEY (id),\n  CONSTRAINT uk_dependencymapping_dependencyid_artifactid_artifacttype UNIQUE (dependencyid, artifactid, artifacttype)\n);\nCREATE INDEX idx_dependencymapping_depid ON dependencymapping (dependencyid);\nALTER TABLE dependencymapping ADD CONSTRAINT fk_dependencymapping_dependencyid FOREIGN KEY (dependencyid) REFERENCES dependency(id) ON DELETE CASCADE;\n\nCREATE TABLE pdependency (\n  id BIGINT NOT NULL,\n  name VARCHAR(50) NOT NULL,\n  description LONGVARCHAR,\n  filename VARCHAR(255) NOT NULL,\n  value_ LONGVARBINARY NOT NULL,\n  CONSTRAINT pk_pdependency PRIMARY KEY (id),\n  CONSTRAINT uk_pdependency_name UNIQUE (name)\n);\n\nCREATE TABLE pdependencymapping (\n  id BIGINT NOT NULL,\n  artifactid BIGINT NOT NULL,\n  artifacttype VARCHAR(50) NOT NULL,\n  dependencyid BIGINT NOT NULL,\n  CONSTRAINT pk_pdependencymapping PRIMARY KEY (id),\n  CONSTRAINT uk_pdependencymapping_dependencyid_artifactid_artifacttype UNIQUE (dependencyid, artifactid, artifacttype)\n);\nCREATE INDEX idx_pdependencymapping_depid ON pdependencymapping (dependencyid);\nALTER TABLE pdependencymapping ADD CONSTRAINT fk_pdependencymapping_dependencyid FOREIGN KEY (dependencyid) REFERENCES pdependency(id) ON DELETE CASCADE;\n\n\nCREATE TABLE group_ (\n  id BIGINT NOT NULL,\n  name VARCHAR(125) NOT NULL,\n  parentPath VARCHAR(255),\n  displayName VARCHAR(255),\n  description LONGVARCHAR,\n  createdBy BIGINT,\n  creationDate BIGINT,\n  lastUpdate BIGINT,\n  iconid BIGINT,\n  CONSTRAINT pk_group PRIMARY KEY (id)\n);\nCREATE INDEX idx_group_name ON group_ (parentPath, name);\n\nCREATE TABLE role (\n  id BIGINT NOT NULL,\n  name VARCHAR(255) NOT NULL,\n  displayName VARCHAR(255),\n  description LONGVARCHAR,\n  createdBy BIGINT,\n  creationDate BIGINT,\n  lastUpdate BIGINT,\n  iconid BIGINT,\n  CONSTRAINT pk_role PRIMARY KEY (id),\n  CONSTRAINT uk_role_name UNIQUE (name)\n);\n\nCREATE TABLE user_ (\n  id BIGINT NOT NULL,\n  enabled BOOLEAN NOT NULL,\n  userName VARCHAR(255) NOT NULL,\n  password VARCHAR(60),\n  firstName VARCHAR(255),\n  lastName VARCHAR(255),\n  title VARCHAR(50),\n  jobTitle VARCHAR(255),\n  managerUserId BIGINT,\n  createdBy BIGINT,\n  creationDate BIGINT,\n  lastUpdate BIGINT,\n  iconid BIGINT,\n  CONSTRAINT pk_user PRIMARY KEY (id),\n  CONSTRAINT uk_user_username UNIQUE (userName)\n);\n\nCREATE TABLE user_login (\n  id BIGINT NOT NULL,\n  lastConnection BIGINT,\n  CONSTRAINT pk_user_login PRIMARY KEY (id)\n);\n\nCREATE TABLE user_contactinfo (\n  id BIGINT NOT NULL,\n  userId BIGINT NOT NULL,\n  email VARCHAR(255),\n  phone VARCHAR(50),\n  mobile VARCHAR(50),\n  fax VARCHAR(50),\n  building VARCHAR(50),\n  room VARCHAR(50),\n  address VARCHAR(255),\n  zipCode VARCHAR(50),\n  city VARCHAR(255),\n  state VARCHAR(255),\n  country VARCHAR(255),\n  website VARCHAR(255),\n  personal BOOLEAN NOT NULL,\n  CONSTRAINT pk_user_contactinfo PRIMARY KEY (id),\n  CONSTRAINT uk_user_contactinfo_userid_personal UNIQUE (userId, personal)\n);\nALTER TABLE user_contactinfo ADD CONSTRAINT fk_user_contactinfo_userid FOREIGN KEY (userId) REFERENCES user_ (id) ON DELETE CASCADE;\n\nCREATE TABLE custom_usr_inf_def (\n  id BIGINT NOT NULL,\n  name VARCHAR(75) NOT NULL,\n  description LONGVARCHAR,\n  CONSTRAINT pk_custom_usr_inf_def PRIMARY KEY (id),\n  CONSTRAINT uk_custom_usr_inf_def_name UNIQUE (name)\n);\n\nCREATE TABLE custom_usr_inf_val (\n  id BIGINT NOT NULL,\n  definitionId BIGINT NOT NULL,\n  userId BIGINT NOT NULL,\n  value VARCHAR(255),\n  CONSTRAINT pk_custom_usr_inf_val PRIMARY KEY (id),\n  CONSTRAINT uk_custom_usr_inf_val_definitionid_userid UNIQUE (definitionId, userId)\n);\nALTER TABLE custom_usr_inf_val ADD CONSTRAINT fk_custom_usr_inf_val_userid FOREIGN KEY (userId) REFERENCES user_ (id) ON DELETE CASCADE;\nALTER TABLE custom_usr_inf_val ADD CONSTRAINT fk_custom_usr_inf_val_definitionid FOREIGN KEY (definitionId) REFERENCES custom_usr_inf_def (id) ON DELETE CASCADE;\n\nCREATE TABLE user_membership (\n  id BIGINT NOT NULL,\n  userId BIGINT NOT NULL,\n  roleId BIGINT NOT NULL,\n  groupId BIGINT NOT NULL,\n  assignedBy BIGINT,\n  assignedDate BIGINT,\n  CONSTRAINT pk_user_membership PRIMARY KEY (id),\n  CONSTRAINT uk_user_membership_userid_roleid_groupid UNIQUE (userId, roleId, groupId)\n);\n\nCREATE TABLE icon (\n  id BIGINT NOT NULL,\n  mimetype VARCHAR(255) NOT NULL,\n  content LONGBLOB NOT NULL,\n  CONSTRAINT pk_icon PRIMARY KEY (id)\n);\n\nCREATE TABLE queriable_log (\n  id BIGINT NOT NULL,\n  log_timestamp BIGINT NOT NULL,\n  whatYear SMALLINT NOT NULL,\n  whatMonth TINYINT NOT NULL,\n  dayOfYear SMALLINT NOT NULL,\n  weekOfYear TINYINT NOT NULL,\n  userId VARCHAR(255) NOT NULL,\n  threadNumber BIGINT NOT NULL,\n  clusterNode VARCHAR(50),\n  productVersion VARCHAR(50) NOT NULL,\n  severity VARCHAR(50) NOT NULL,\n  actionType VARCHAR(50) NOT NULL,\n  actionScope VARCHAR(100),\n  actionStatus TINYINT NOT NULL,\n  rawMessage VARCHAR(255) NOT NULL,\n  callerClassName VARCHAR(200),\n  callerMethodName VARCHAR(80),\n  numericIndex1 BIGINT,\n  numericIndex2 BIGINT,\n  numericIndex3 BIGINT,\n  numericIndex4 BIGINT,\n  numericIndex5 BIGINT,\n  CONSTRAINT pk_queriable_log PRIMARY KEY (id)\n);\n\nCREATE TABLE sequence (\n  id BIGINT NOT NULL,\n  nextid BIGINT NOT NULL,\n  CONSTRAINT pk_sequence PRIMARY KEY (id)\n);\n\nCREATE TABLE platform (\n  id BIGINT NOT NULL,\n  version VARCHAR(50) NOT NULL,\n  initial_bonita_version VARCHAR(50) NOT NULL,\n  application_version VARCHAR(50) NOT NULL,\n  maintenance_message LONGVARCHAR,\n  maintenance_message_active BOOLEAN NOT NULL,\n  created BIGINT NOT NULL,\n  created_by VARCHAR(50) NOT NULL,\n  information CLOB,\n  maintenance_enabled BOOLEAN NOT NULL,\n  CONSTRAINT pk_platform PRIMARY KEY (id)\n);\n\nCREATE TABLE platformCommand (\n  id BIGINT PRIMARY KEY,\n  name VARCHAR(50) NOT NULL UNIQUE,\n  description LONGVARCHAR,\n  IMPLEMENTATION VARCHAR(100) NOT NULL\n);\n\nCREATE TABLE job_desc (\n  id BIGINT NOT NULL,\n  jobclassname VARCHAR(100) NOT NULL,\n  jobname VARCHAR(100) NOT NULL,\n  description VARCHAR(50),\n  CONSTRAINT pk_job_desc PRIMARY KEY (id)\n);\n\nCREATE TABLE job_param (\n  id BIGINT NOT NULL,\n  jobDescriptorId BIGINT NOT NULL,\n  key_ VARCHAR(50) NOT NULL,\n  value_ MEDIUMBLOB NOT NULL,\n  CONSTRAINT pk_job_param PRIMARY KEY (id)\n);\nALTER TABLE job_param ADD CONSTRAINT fk_job_param_jobdescriptorid FOREIGN KEY (jobDescriptorId) REFERENCES job_desc(id) ON DELETE CASCADE;\nCREATE INDEX idx_job_param_jobid ON job_param(jobDescriptorId);\n\nCREATE TABLE job_log (\n  id BIGINT NOT NULL,\n  jobDescriptorId BIGINT NOT NULL,\n  retryNumber BIGINT,\n  lastUpdateDate BIGINT,\n  lastMessage LONGVARCHAR,\n  CONSTRAINT pk_job_log PRIMARY KEY (id),\n  CONSTRAINT uk_job_log_jobdescriptorid UNIQUE (jobDescriptorId)\n);\nALTER TABLE job_log ADD CONSTRAINT fk_job_log_jobdescriptorid FOREIGN KEY (jobDescriptorId) REFERENCES job_desc(id) ON DELETE CASCADE;\n\nCREATE TABLE page_mapping (\n  id BIGINT NOT NULL,\n  key_ VARCHAR(255) NOT NULL,\n  pageId BIGINT NULL,\n  url VARCHAR(1024) NULL,\n  urladapter VARCHAR(255) NULL,\n  page_authoriz_rules TEXT NULL,\n  lastUpdateDate BIGINT NULL,\n  lastUpdatedBy BIGINT NULL,\n  CONSTRAINT pk_page_mapping PRIMARY KEY (id),\n  CONSTRAINT uk_page_mapping_key UNIQUE (key_)\n);\n\nCREATE TABLE form_mapping (\n  id BIGINT NOT NULL,\n  process BIGINT NOT NULL,\n  type INT NOT NULL,\n  task VARCHAR(255),\n  page_mapping_id BIGINT,\n  lastUpdateDate BIGINT,\n  lastUpdatedBy BIGINT,\n  target VARCHAR(16) NOT NULL,\n  CONSTRAINT pk_form_mapping PRIMARY KEY (id)\n);\n\nALTER TABLE form_mapping ADD CONSTRAINT fk_form_mapping_key FOREIGN KEY (page_mapping_id) REFERENCES page_mapping(id);\n\nCREATE TABLE proc_parameter (\n  id BIGINT NOT NULL,\n  process_id BIGINT NOT NULL,\n  name VARCHAR(255) NOT NULL,\n  value CLOB NULL,\n  CONSTRAINT pk_proc_parameter PRIMARY KEY (id)\n);\n\nCREATE TABLE bar_resource (\n  id BIGINT NOT NULL,\n  process_id BIGINT NOT NULL,\n  name VARCHAR(255) NOT NULL,\n  type VARCHAR(16) NOT NULL,\n  content LONGBLOB NOT NULL,\n  CONSTRAINT pk_bar_resource PRIMARY KEY (id),\n  CONSTRAINT uk_bar_resource_processid_name_type UNIQUE (process_id, name, type)\n);\n\nCREATE TABLE temporary_content (\n  id BIGINT NOT NULL,\n  creationDate BIGINT NOT NULL,\n  key_ VARCHAR(255) NOT NULL,\n  fileName VARCHAR(255) NOT NULL,\n  mimeType VARCHAR(255) NOT NULL,\n  content LONGBLOB NOT NULL,\n  UNIQUE (key_),\n  PRIMARY KEY (id)\n);\nCREATE INDEX idx_temporary_content ON temporary_content (key_);\n\nCREATE TABLE tenant_resource (\n  id BIGINT NOT NULL,\n  name VARCHAR(255) NOT NULL,\n  type VARCHAR(16) NOT NULL,\n  content LONGBLOB NOT NULL,\n  lastUpdatedBy BIGINT NOT NULL,\n  lastUpdateDate BIGINT,\n  state VARCHAR(50) NOT NULL,\n  CONSTRAINT pk_tenant_resource PRIMARY KEY (id),\n  CONSTRAINT uk_tenant_resource_name_type UNIQUE (name, type)\n);\n\nCREATE TABLE bpm_failure (\n  id BIGINT NOT NULL,\n  processDefinitionId BIGINT NOT NULL,\n  processInstanceId BIGINT NOT NULL,\n  rootProcessInstanceId BIGINT,\n  flowNodeInstanceId BIGINT,\n  scope VARCHAR(255),\n  context VARCHAR(1024),\n  errorMessage VARCHAR(1024),\n  stackTrace TEXT,\n  failureDate BIGINT NOT NULL,\n  CONSTRAINT pk_bpm_failure PRIMARY KEY (id)\n);\nCREATE INDEX idx_bpm_failure_flownodeinstanceid ON bpm_failure (flowNodeInstanceId);\nCREATE INDEX idx_bpm_failure_processinstanceid ON bpm_failure (processInstanceId);\nCREATE INDEX idx_bpm_failure_rootprocessinstanceid ON bpm_failure (rootProcessInstanceId);\nCREATE INDEX idx_bpm_failure_processdefinitionid ON bpm_failure (processDefinitionId);\n\nCREATE TABLE arch_bpm_failure (\n  id BIGINT NOT NULL,\n  processDefinitionId BIGINT NOT NULL,\n  processInstanceId BIGINT NOT NULL,\n  rootProcessInstanceId BIGINT,\n  flowNodeInstanceId BIGINT,\n  scope VARCHAR(255),\n  context VARCHAR(1024),\n  errorMessage VARCHAR(1024),\n  stackTrace TEXT,\n  failureDate BIGINT NOT NULL,\n  archiveDate BIGINT NOT NULL,\n  sourceObjectId BIGINT NOT NULL,\n  CONSTRAINT pk_arch_bpm_failure PRIMARY KEY (id)\n);\nCREATE INDEX idx_arch_bpm_failure_flownodeinstanceid ON arch_bpm_failure (flowNodeInstanceId);\nCREATE INDEX idx_arch_bpm_failure_processinstanceid ON arch_bpm_failure (processInstanceId);\nCREATE INDEX idx_arch_bpm_failure_rootprocessinstanceid ON arch_bpm_failure (rootProcessInstanceId);\nCREATE INDEX idx_arch_bpm_failure_processdefinitionid ON arch_bpm_failure (processDefinitionId);\n\nCREATE TABLE data_retention_config (\n    id                  BIGINT NOT NULL,\n    data_classname      VARCHAR(255) NOT NULL,\n    reference_date      VARCHAR(20) NOT NULL,\n    retention_days      INT NOT NULL,\n    created_at          BIGINT NOT NULL,\n    updated_at          BIGINT NOT NULL,\n    CONSTRAINT pk_data_retention_config PRIMARY KEY (id),\n    CONSTRAINT uk_data_retention_config_data_classname UNIQUE (data_classname)\n);\n\nCREATE TABLE data_retention_bdm_tracking (\n    id                  BIGINT NOT NULL,\n    data_id             BIGINT NOT NULL,\n    data_classname      VARCHAR(255) NOT NULL,\n    created_at          BIGINT NOT NULL,\n    last_modified_at    BIGINT NOT NULL,\n    CONSTRAINT pk_data_retention_bdm_tracking PRIMARY KEY (id),\n    CONSTRAINT uk_data_retention_bdm_tracking_data_id_data_classname UNIQUE (data_id, data_classname)\n);\nCREATE INDEX idx_data_retention_bdm_tracking_data_classname ON data_retention_bdm_tracking (data_classname);\n\nCREATE TABLE delegation_rule (\n    id                  BIGINT NOT NULL,\n    delegator_id        BIGINT NOT NULL,\n    delegate_id         BIGINT NOT NULL,\n    start_date          BIGINT NOT NULL,\n    end_date            BIGINT NOT NULL,\n    last_updated_by     BIGINT NOT NULL,\n    last_updated_at     BIGINT NOT NULL,\n    CONSTRAINT pk_delegation_rule PRIMARY KEY (id),\n    CONSTRAINT uk_delegation_rule_delegator_id UNIQUE (delegator_id)\n);\n\nCREATE TABLE delegation_rule_process (\n    id                  BIGINT NOT NULL,\n    delegation_rule_id  BIGINT NOT NULL,\n    process_name        VARCHAR(255) NOT NULL,\n    CONSTRAINT pk_delegation_rule_process PRIMARY KEY (id),\n    CONSTRAINT uk_delegation_rule_process_delegation_rule_id_process_name UNIQUE (delegation_rule_id, process_name)\n);\nALTER TABLE delegation_rule_process ADD CONSTRAINT fk_delegation_rule_process_delegation_rule_id FOREIGN KEY (delegation_rule_id) REFERENCES delegation_rule(id) ON DELETE CASCADE;\n"
  },
  {
    "path": "platform/platform-resources/src/main/resources/sql/h2/dropQuartzTables.sql",
    "content": "DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;\nDROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;\nDROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;\nDROP TABLE IF EXISTS QRTZ_LOCKS;\nDROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;\nDROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;\nDROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;\nDROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;\nDROP TABLE IF EXISTS QRTZ_TRIGGERS;\nDROP TABLE IF EXISTS QRTZ_JOB_DETAILS;\nDROP TABLE IF EXISTS QRTZ_CALENDARS;\n"
  },
  {
    "path": "platform/platform-resources/src/main/resources/sql/h2/dropTables.sql",
    "content": "DROP TABLE configuration;\nDROP TABLE arch_contract_data;\nDROP TABLE contract_data;\nDROP TABLE actormember;\nDROP TABLE actor;\nDROP TABLE processcategorymapping;\nDROP TABLE category;\nDROP TABLE arch_process_comment;\nDROP TABLE process_comment;\nDROP TABLE process_definition;\nDROP TABLE arch_document_mapping;\nDROP TABLE document_mapping;\nDROP TABLE document;\nDROP TABLE arch_flownode_instance;\nDROP TABLE arch_process_instance;\nDROP TABLE arch_connector_instance;\nDROP TABLE arch_multi_biz_data;\nDROP TABLE arch_ref_biz_data_inst;\nDROP TABLE multi_biz_data;\nDROP TABLE ref_biz_data_inst;\nDROP TABLE pending_mapping;\nDROP TABLE connector_instance;\nDROP TABLE flownode_instance;\nDROP TABLE process_instance;\nDROP TABLE event_trigger_instance;\nDROP TABLE waiting_event;\nDROP TABLE message_instance;\nDROP TABLE processsupervisor;\nDROP TABLE business_app_menu;\nDROP TABLE business_app_page;\nDROP TABLE business_app;\nDROP TABLE command;\nDROP TABLE arch_data_instance;\nDROP TABLE data_instance;\nDROP TABLE dependencymapping;\nDROP TABLE dependency;\nDROP TABLE pdependencymapping;\nDROP TABLE pdependency;\nDROP TABLE user_membership;\nDROP TABLE custom_usr_inf_val;\nDROP TABLE custom_usr_inf_def;\nDROP TABLE user_contactinfo;\nDROP TABLE user_login;\nDROP TABLE user_;\nDROP TABLE role;\nDROP TABLE group_;\nDROP TABLE queriable_log;\nDROP TABLE page;\nDROP TABLE profilemember;\nDROP TABLE profile;\nDROP TABLE job_log;\nDROP TABLE job_param;\nDROP TABLE job_desc;\nDROP TABLE sequence;\nDROP TABLE tenant;\nDROP TABLE platform;\nDROP TABLE platformCommand;\nDROP TABLE form_mapping;\nDROP TABLE page_mapping;\nDROP TABLE process_content;\nDROP TABLE proc_parameter;\nDROP TABLE bar_resource;\nDROP TABLE temporary_content;\nDROP TABLE tenant_resource;\nDROP TABLE icon;\nDROP TABLE arch_bpm_failure;\nDROP TABLE bpm_failure;\nDROP TABLE data_retention_config;\nDROP TABLE data_retention_bdm_tracking;\nDROP TABLE delegation_rule_process;\nDROP TABLE delegation_rule;"
  },
  {
    "path": "platform/platform-resources/src/main/resources/sql/h2/initTables.sql",
    "content": "INSERT INTO sequence VALUES (1, 1);\nINSERT INTO sequence VALUES (2, 1);\nINSERT INTO sequence VALUES (3, 1);\nINSERT INTO sequence VALUES (4, 1);\nINSERT INTO sequence VALUES (5, 1);\nINSERT INTO sequence VALUES (6, 1);\nINSERT INTO sequence VALUES (7, 1);\nINSERT INTO sequence VALUES (8, 1);\nINSERT INTO sequence VALUES (9, 1);\nINSERT INTO sequence VALUES(10, 1);\nINSERT INTO sequence VALUES(11, 1);\nINSERT INTO sequence VALUES(20, 1);\nINSERT INTO sequence VALUES(21, 1);\nINSERT INTO sequence VALUES(22, 1);\nINSERT INTO sequence VALUES(23, 1);\nINSERT INTO sequence VALUES(24, 1);\nINSERT INTO sequence VALUES(25, 1);\nINSERT INTO sequence VALUES(26, 1);\nINSERT INTO sequence VALUES(27, 1);\nINSERT INTO sequence VALUES(30, 1);\nINSERT INTO sequence VALUES(70, 1);\nINSERT INTO sequence VALUES(71, 1);\nINSERT INTO sequence VALUES(72, 1);\nINSERT INTO sequence VALUES(90, 1);\nINSERT INTO sequence VALUES(9990, 1);\nINSERT INTO sequence VALUES(9992, 1);\nINSERT INTO sequence VALUES(10000, 1);\nINSERT INTO sequence VALUES(10001, 1);\nINSERT INTO sequence VALUES(10010, 1);\nINSERT INTO sequence VALUES(10011, 1);\nINSERT INTO sequence VALUES(10012, 1);\nINSERT INTO sequence VALUES(10014, 1);\nINSERT INTO sequence VALUES(10015, 1);\nINSERT INTO sequence VALUES(10016, 1);\nINSERT INTO sequence VALUES(10017, 1);\nINSERT INTO sequence VALUES(10018, 1);\nINSERT INTO sequence VALUES(10020, 1);\nINSERT INTO sequence VALUES(10021, 1);\nINSERT INTO sequence VALUES(10030, 1);\nINSERT INTO sequence VALUES(10031, 1);\nINSERT INTO sequence VALUES(10040, 1);\nINSERT INTO sequence VALUES(20051, 1);\nINSERT INTO sequence VALUES(10050, 1);\nINSERT INTO sequence VALUES(10060, 1);\nINSERT INTO sequence VALUES(10080, 1);\nINSERT INTO sequence VALUES(10090, 1);\nINSERT INTO sequence VALUES(10096, 1);\nINSERT INTO sequence VALUES(10120, 1);\nINSERT INTO sequence VALUES(10121, 1);\nINSERT INTO sequence VALUES(10200, 1);\nINSERT INTO sequence VALUES(10201, 1);\nINSERT INTO sequence VALUES(10202, 1);\nINSERT INTO sequence VALUES(10210, 1);\nINSERT INTO sequence VALUES(10220, 1);\nINSERT INTO sequence VALUES(10300, 1);\nINSERT INTO sequence VALUES(10310, 1);\nINSERT INTO sequence VALUES(10400, 1);\nINSERT INTO sequence VALUES(10500, 1);\nINSERT INTO sequence VALUES(10501, 1);\nINSERT INTO sequence VALUES(20010, 1);\nINSERT INTO sequence VALUES(20011, 1);\nINSERT INTO sequence VALUES(20013, 1);\nINSERT INTO sequence VALUES(20040, 1);\nINSERT INTO sequence VALUES(20050, 1);\nINSERT INTO sequence VALUES(20210, 1);\nINSERT INTO sequence VALUES(20220, 1);\nINSERT INTO sequence VALUES(20096, 1);\nINSERT INTO sequence VALUES(20097, 1);\nINSERT INTO sequence VALUES(20098, 1);\n"
  },
  {
    "path": "platform/platform-resources/src/main/resources/sql/h2/preDropStructure.sql",
    "content": "-- ------------------------------------------------ Foreign Keys -----------------------------------------------\nALTER TABLE actormember DROP CONSTRAINT fk_actormember_actorid;\nALTER TABLE profilemember DROP CONSTRAINT fk_profilemember_profileid;\n\nALTER TABLE document_mapping DROP CONSTRAINT fk_document_mapping_documentid;\nALTER TABLE pending_mapping DROP CONSTRAINT fk_pending_mapping_activityid;\nALTER TABLE process_definition DROP CONSTRAINT fk_process_definition_content_id;\n\n-- business application\nALTER TABLE business_app_menu DROP CONSTRAINT fk_business_app_menu_applicationid;\nALTER TABLE business_app_menu DROP CONSTRAINT fk_business_app_menu_applicationpageid;\nALTER TABLE business_app_menu DROP CONSTRAINT fk_business_app_menu_parentid;\nALTER TABLE business_app_page DROP CONSTRAINT fk_business_app_page_applicationid;\nALTER TABLE business_app_page DROP CONSTRAINT fk_business_app_page_pageid;\nALTER TABLE business_app DROP CONSTRAINT fk_business_app_profileid;\nALTER TABLE business_app DROP CONSTRAINT fk_business_app_layoutid;\nALTER TABLE business_app DROP CONSTRAINT fk_business_app_themeid;\n\n-- delegation\nALTER TABLE delegation_rule_process DROP CONSTRAINT fk_delegation_rule_process_delegation_rule_id;\n\n--  ------------------------ Foreign Keys to disable if archiving is on another BD ------------------\nALTER TABLE arch_document_mapping DROP CONSTRAINT fk_arch_document_mapping_documentid;\n"
  },
  {
    "path": "platform/platform-resources/src/main/resources/sql/postgres/cleanTables.sql",
    "content": "DELETE FROM arch_contract_data;\nDELETE FROM contract_data;\nDELETE FROM actormember;\nDELETE FROM actor;\nDELETE FROM processcategorymapping;\nDELETE FROM category;\nDELETE FROM arch_process_comment;\nDELETE FROM process_comment;\nDELETE FROM process_definition;\nDELETE FROM arch_document_mapping;\nDELETE FROM document;\nDELETE FROM document_mapping;\nDELETE FROM arch_flownode_instance;\nDELETE FROM arch_process_instance;\nDELETE FROM arch_connector_instance;\nDELETE FROM arch_multi_biz_data;\nDELETE FROM arch_ref_biz_data_inst;\nDELETE FROM multi_biz_data;\nDELETE FROM ref_biz_data_inst;\nDELETE FROM pending_mapping;\nDELETE FROM message_instance;\nDELETE FROM waiting_event;\nDELETE FROM event_trigger_instance;\nDELETE FROM connector_instance;\nDELETE FROM flownode_instance;\nDELETE FROM process_instance;\nDELETE FROM processsupervisor;\nDELETE FROM business_app_menu;\nDELETE FROM business_app_page;\nDELETE FROM business_app;\nDELETE FROM command;\nDELETE FROM arch_data_instance;\nDELETE FROM data_instance;\nDELETE FROM dependencymapping;\nDELETE FROM dependency;\nDELETE FROM user_membership;\nDELETE FROM custom_usr_inf_val;\nDELETE FROM custom_usr_inf_def;\nDELETE FROM user_contactinfo;\nDELETE FROM user_login;\nDELETE FROM user_;\nDELETE FROM role;\nDELETE FROM group_;\nDELETE FROM queriable_log;\nDELETE FROM page;\nDELETE FROM sequence;\nDELETE FROM profilemember;\nDELETE FROM profile;\nDELETE FROM job_log;\nDELETE FROM job_param;\nDELETE FROM job_desc;\nDELETE FROM tenant;\nDELETE FROM platformCommand;\nDELETE FROM form_mapping;\nDELETE FROM page_mapping;\nDELETE FROM proc_parameter;\nDELETE FROM arch_bpm_failure;\nDELETE FROM bpm_failure;\nDELETE FROM data_retention_config;\nDELETE FROM data_retention_bdm_tracking;\nDELETE FROM delegation_rule_process;\nDELETE FROM delegation_rule;\n-- do NOT clear directly PLATFORM table, Hibernate needs to update its cache to know the platform has been deleted\n "
  },
  {
    "path": "platform/platform-resources/src/main/resources/sql/postgres/createQuartzTables.sql",
    "content": "CREATE TABLE QRTZ_CALENDARS (\n  SCHED_NAME VARCHAR(120) NOT NULL,\n  CALENDAR_NAME VARCHAR (200)  NOT NULL ,\n  CALENDAR BYTEA NOT NULL\n);\n\nCREATE TABLE QRTZ_CRON_TRIGGERS (\n  SCHED_NAME VARCHAR(120) NOT NULL,\n  TRIGGER_NAME VARCHAR (200)  NOT NULL ,\n  TRIGGER_GROUP VARCHAR (200)  NOT NULL ,\n  CRON_EXPRESSION VARCHAR (120)  NOT NULL ,\n  TIME_ZONE_ID VARCHAR (80) \n);\n\nCREATE TABLE QRTZ_FIRED_TRIGGERS (\n  SCHED_NAME VARCHAR(120) NOT NULL,\n  ENTRY_ID VARCHAR (95)  NOT NULL ,\n  TRIGGER_NAME VARCHAR (200)  NOT NULL ,\n  TRIGGER_GROUP VARCHAR (200)  NOT NULL ,\n  INSTANCE_NAME VARCHAR (200)  NOT NULL ,\n  FIRED_TIME INT8 NOT NULL ,\n  SCHED_TIME INT8 NOT NULL ,\n  PRIORITY INTEGER NOT NULL ,\n  STATE VARCHAR (16)  NOT NULL,\n  JOB_NAME VARCHAR (200)  NULL ,\n  JOB_GROUP VARCHAR (200)  NULL ,\n  IS_NONCONCURRENT BOOLEAN  NULL ,\n  REQUESTS_RECOVERY BOOLEAN  NULL \n);\n\nCREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS (\n  SCHED_NAME VARCHAR(120) NOT NULL,\n  TRIGGER_GROUP VARCHAR (200)  NOT NULL \n);\n\nCREATE TABLE QRTZ_SCHEDULER_STATE (\n  SCHED_NAME VARCHAR(120) NOT NULL,\n  INSTANCE_NAME VARCHAR (200)  NOT NULL ,\n  LAST_CHECKIN_TIME INT8 NOT NULL ,\n  CHECKIN_INTERVAL INT8 NOT NULL\n);\n\nCREATE TABLE QRTZ_LOCKS (\n  SCHED_NAME VARCHAR(120) NOT NULL,\n  LOCK_NAME VARCHAR (40)  NOT NULL \n);\n\nCREATE TABLE QRTZ_JOB_DETAILS (\n  SCHED_NAME VARCHAR(120) NOT NULL,\n  JOB_NAME VARCHAR (200)  NOT NULL ,\n  JOB_GROUP VARCHAR (200)  NOT NULL ,\n  DESCRIPTION VARCHAR (250) NULL ,\n  JOB_CLASS_NAME VARCHAR (250)  NOT NULL ,\n  IS_DURABLE BOOLEAN  NOT NULL ,\n  IS_NONCONCURRENT BOOLEAN  NOT NULL ,\n  IS_UPDATE_DATA BOOLEAN  NOT NULL ,\n  REQUESTS_RECOVERY BOOLEAN  NOT NULL ,\n  JOB_DATA BYTEA NULL\n);\n\nCREATE TABLE QRTZ_SIMPLE_TRIGGERS (\n  SCHED_NAME VARCHAR(120) NOT NULL,\n  TRIGGER_NAME VARCHAR (200)  NOT NULL ,\n  TRIGGER_GROUP VARCHAR (200)  NOT NULL ,\n  REPEAT_COUNT INT8 NOT NULL ,\n  REPEAT_INTERVAL INT8 NOT NULL ,\n  TIMES_TRIGGERED INT8 NOT NULL\n);\n\nCREATE TABLE qrtz_simprop_triggers\n  (          \n    SCHED_NAME VARCHAR(120) NOT NULL,\n    TRIGGER_NAME VARCHAR(200) NOT NULL,\n    TRIGGER_GROUP VARCHAR(200) NOT NULL,\n    STR_PROP_1 VARCHAR(512) NULL,\n    STR_PROP_2 VARCHAR(512) NULL,\n    STR_PROP_3 VARCHAR(512) NULL,\n    INT_PROP_1 INTEGER NULL,\n    INT_PROP_2 INTEGER NULL,\n    LONG_PROP_1 INT8 NULL,\n    LONG_PROP_2 INT8 NULL,\n    DEC_PROP_1 NUMERIC(13,4) NULL,\n    DEC_PROP_2 NUMERIC(13,4) NULL,\n    BOOL_PROP_1 BOOLEAN NULL,\n    BOOL_PROP_2 BOOLEAN NULL\n);\n\nCREATE TABLE QRTZ_BLOB_TRIGGERS (\n  SCHED_NAME VARCHAR(120) NOT NULL,\n  TRIGGER_NAME VARCHAR (200)  NOT NULL ,\n  TRIGGER_GROUP VARCHAR (200)  NOT NULL ,\n  BLOB_DATA BYTEA NULL\n);\n\nCREATE TABLE QRTZ_TRIGGERS (\n  SCHED_NAME VARCHAR(120) NOT NULL,\n  TRIGGER_NAME VARCHAR (200)  NOT NULL ,\n  TRIGGER_GROUP VARCHAR (200)  NOT NULL ,\n  JOB_NAME VARCHAR (200)  NOT NULL ,\n  JOB_GROUP VARCHAR (200)  NOT NULL ,\n  DESCRIPTION VARCHAR (250) NULL ,\n  NEXT_FIRE_TIME INT8 NULL ,\n  PREV_FIRE_TIME INT8 NULL ,\n  PRIORITY INTEGER NULL ,\n  TRIGGER_STATE VARCHAR (16)  NOT NULL ,\n  TRIGGER_TYPE VARCHAR (8)  NOT NULL ,\n  START_TIME INT8 NOT NULL ,\n  END_TIME INT8 NULL ,\n  CALENDAR_NAME VARCHAR (200)  NULL ,\n  MISFIRE_INSTR SMALLINT NULL ,\n  JOB_DATA BYTEA NULL\n);\n\nALTER TABLE QRTZ_CALENDARS  ADD\n  CONSTRAINT PK_QRTZ_CALENDARS PRIMARY KEY  \n  (\n    SCHED_NAME,\n    CALENDAR_NAME\n  );\n\nALTER TABLE QRTZ_CRON_TRIGGERS  ADD\n  CONSTRAINT PK_QRTZ_CRON_TRIGGERS PRIMARY KEY  \n  (\n    SCHED_NAME,\n    TRIGGER_NAME,\n    TRIGGER_GROUP\n  );\n\nALTER TABLE QRTZ_FIRED_TRIGGERS  ADD\n  CONSTRAINT PK_QRTZ_FIRED_TRIGGERS PRIMARY KEY  \n  (\n    SCHED_NAME,\n    ENTRY_ID\n  );\n\nALTER TABLE QRTZ_PAUSED_TRIGGER_GRPS  ADD\n  CONSTRAINT PK_QRTZ_PAUSED_TRIGGER_GRPS PRIMARY KEY  \n  (\n    SCHED_NAME,\n    TRIGGER_GROUP\n  );\n\nALTER TABLE QRTZ_SCHEDULER_STATE  ADD\n  CONSTRAINT PK_QRTZ_SCHEDULER_STATE PRIMARY KEY  \n  (\n    SCHED_NAME,\n    INSTANCE_NAME\n  );\n\nALTER TABLE QRTZ_LOCKS  ADD\n  CONSTRAINT PK_QRTZ_LOCKS PRIMARY KEY  \n  (\n    SCHED_NAME,\n    LOCK_NAME\n  );\n\nALTER TABLE QRTZ_JOB_DETAILS  ADD\n  CONSTRAINT PK_QRTZ_JOB_DETAILS PRIMARY KEY  \n  (\n    SCHED_NAME,\n    JOB_NAME,\n    JOB_GROUP\n  );\n\nALTER TABLE QRTZ_SIMPLE_TRIGGERS  ADD\n  CONSTRAINT PK_QRTZ_SIMPLE_TRIGGERS PRIMARY KEY  \n  (\n    SCHED_NAME,\n    TRIGGER_NAME,\n    TRIGGER_GROUP\n  );\n\nALTER TABLE QRTZ_SIMPROP_TRIGGERS  ADD\n  CONSTRAINT PK_QRTZ_SIMPROP_TRIGGERS PRIMARY KEY  \n  (\n    SCHED_NAME,\n    TRIGGER_NAME,\n    TRIGGER_GROUP\n  );\n\nALTER TABLE QRTZ_TRIGGERS  ADD\n  CONSTRAINT PK_QRTZ_TRIGGERS PRIMARY KEY  \n  (\n    SCHED_NAME,\n    TRIGGER_NAME,\n    TRIGGER_GROUP\n  );\n\nALTER TABLE QRTZ_CRON_TRIGGERS ADD\n  CONSTRAINT FK_QRTZ_CRON_TRIGGERS FOREIGN KEY\n  (\n    SCHED_NAME,\n    TRIGGER_NAME,\n    TRIGGER_GROUP\n  ) REFERENCES QRTZ_TRIGGERS (\n    SCHED_NAME,\n    TRIGGER_NAME,\n    TRIGGER_GROUP\n  ) ON DELETE CASCADE;\n\n\nALTER TABLE QRTZ_SIMPLE_TRIGGERS ADD\n  CONSTRAINT FK_QRTZ_SIMPLE_TRIGGERS FOREIGN KEY\n  (\n    SCHED_NAME,\n    TRIGGER_NAME,\n    TRIGGER_GROUP\n  ) REFERENCES QRTZ_TRIGGERS (\n    SCHED_NAME,\n    TRIGGER_NAME,\n    TRIGGER_GROUP\n  ) ON DELETE CASCADE;\n\nALTER TABLE QRTZ_SIMPROP_TRIGGERS ADD\n  CONSTRAINT FK_QRTZ_SIMPROP_TRIGGERS FOREIGN KEY\n  (\n    SCHED_NAME,\n    TRIGGER_NAME,\n    TRIGGER_GROUP\n  ) REFERENCES QRTZ_TRIGGERS (\n    SCHED_NAME,\n    TRIGGER_NAME,\n    TRIGGER_GROUP\n  ) ON DELETE CASCADE;\n\n\nALTER TABLE QRTZ_TRIGGERS ADD\n  CONSTRAINT FK_QRTZ_TRIGGERS FOREIGN KEY\n  (\n    SCHED_NAME,\n    JOB_NAME,\n    JOB_GROUP\n  ) REFERENCES QRTZ_JOB_DETAILS (\n    SCHED_NAME,\n    JOB_NAME,\n    JOB_GROUP\n  );\n  \nCREATE INDEX IDX_QRTZ_J_REQ_RECOVERY ON QRTZ_JOB_DETAILS(SCHED_NAME,REQUESTS_RECOVERY);\nCREATE INDEX IDX_QRTZ_J_GRP ON QRTZ_JOB_DETAILS(SCHED_NAME,JOB_GROUP);\n\nCREATE INDEX IDX_QRTZ_T_J ON QRTZ_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);\nCREATE INDEX IDX_QRTZ_T_JG ON QRTZ_TRIGGERS(SCHED_NAME,JOB_GROUP);\nCREATE INDEX IDX_QRTZ_T_C ON QRTZ_TRIGGERS(SCHED_NAME,CALENDAR_NAME);\nCREATE INDEX IDX_QRTZ_T_G ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);\nCREATE INDEX IDX_QRTZ_T_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE);\nCREATE INDEX IDX_QRTZ_T_N_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE);\nCREATE INDEX IDX_QRTZ_T_N_G_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE);\nCREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME ON QRTZ_TRIGGERS(SCHED_NAME,NEXT_FIRE_TIME);\nCREATE INDEX IDX_QRTZ_T_NFT_ST ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME);\nCREATE INDEX IDX_QRTZ_T_NFT_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME);\nCREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE);\nCREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE);\n\nCREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME);\nCREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY);\nCREATE INDEX IDX_QRTZ_FT_J_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);\nCREATE INDEX IDX_QRTZ_FT_JG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_GROUP);\nCREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP);\nCREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);"
  },
  {
    "path": "platform/platform-resources/src/main/resources/sql/postgres/createTables.sql",
    "content": "CREATE TABLE configuration (\n  content_type VARCHAR(50) NOT NULL,\n  resource_name VARCHAR(120) NOT NULL,\n  resource_content BYTEA NOT NULL,\n  CONSTRAINT pk_configuration PRIMARY KEY (content_type, resource_name)\n);\nCREATE INDEX idx_configuration ON configuration (content_type);\n\nCREATE TABLE contract_data (\n  id INT8 NOT NULL,\n  kind VARCHAR(20) NOT NULL,\n  scopeId INT8 NOT NULL,\n  name VARCHAR(50) NOT NULL,\n  val TEXT,\n  CONSTRAINT pk_contract_data PRIMARY KEY (id, scopeId),\n  CONSTRAINT uk_contract_data_kind_scopeid_name UNIQUE (kind, scopeId, name)\n);\n\nCREATE TABLE arch_contract_data (\n  id INT8 NOT NULL,\n  kind VARCHAR(20) NOT NULL,\n  scopeId INT8 NOT NULL,\n  name VARCHAR(50) NOT NULL,\n  val TEXT,\n  archiveDate INT8 NOT NULL,\n  sourceObjectId INT8 NOT NULL,\n  CONSTRAINT pk_arch_contract_data PRIMARY KEY (id, scopeId),\n  CONSTRAINT uk_arch_contract_data_kind_scopeid_name UNIQUE (kind, scopeId, name)\n);\n\nCREATE TABLE actor (\n  id INT8 NOT NULL,\n  scopeId INT8 NOT NULL,\n  name VARCHAR(50) NOT NULL,\n  displayName VARCHAR(75),\n  description TEXT,\n  initiator BOOLEAN,\n  CONSTRAINT uk_actor_id_scopeid_name UNIQUE (id, scopeId, name),\n  CONSTRAINT pk_actor PRIMARY KEY (id)\n);\n\nCREATE TABLE actormember (\n  id INT8 NOT NULL,\n  actorId INT8 NOT NULL,\n  userId INT8 NOT NULL,\n  groupId INT8 NOT NULL,\n  roleId INT8 NOT NULL,\n  CONSTRAINT uk_actormember_actorid_userid_groupid_roleid UNIQUE (actorId, userId, groupId, roleId),\n  CONSTRAINT pk_actormember PRIMARY KEY (id)\n);\n\nALTER TABLE actormember ADD CONSTRAINT fk_actormember_actorid FOREIGN KEY (actorId) REFERENCES actor(id);\n\nCREATE TABLE category (\n  id INT8 NOT NULL,\n  name VARCHAR(50) NOT NULL,\n  creator INT8,\n  description TEXT,\n  creationDate INT8 NOT NULL,\n  lastUpdateDate INT8 NOT NULL,\n  CONSTRAINT pk_category PRIMARY KEY (id),\n  CONSTRAINT uk_category_name UNIQUE (name)\n);\n\nCREATE TABLE processcategorymapping (\n  id INT8 NOT NULL,\n  categoryid INT8 NOT NULL,\n  processid INT8 NOT NULL,\n  CONSTRAINT pk_processcategorymapping PRIMARY KEY (id),\n  CONSTRAINT uk_processcategorymapping_categoryid_processid UNIQUE (categoryid, processid)\n);\nALTER TABLE processcategorymapping ADD CONSTRAINT fk_processcategorymapping_categoryid FOREIGN KEY (categoryid) REFERENCES category(id) ON DELETE CASCADE;\n\nCREATE TABLE arch_process_comment(\n  id INT8 NOT NULL,\n  userId INT8,\n  processInstanceId INT8 NOT NULL,\n  postDate INT8 NOT NULL,\n  content VARCHAR(512) NOT NULL,\n  archiveDate INT8 NOT NULL,\n  sourceObjectId INT8 NOT NULL,\n  CONSTRAINT pk_arch_process_comment PRIMARY KEY (id)\n);\nCREATE INDEX idx1_arch_process_comment on arch_process_comment (sourceobjectid);\nCREATE INDEX idx2_arch_process_comment on arch_process_comment (processInstanceId, archivedate);\n\nCREATE TABLE process_comment (\n  id INT8 NOT NULL,\n  kind VARCHAR(25) NOT NULL,\n  userId INT8,\n  processInstanceId INT8 NOT NULL,\n  postDate INT8 NOT NULL,\n  content VARCHAR(512) NOT NULL,\n  CONSTRAINT pk_process_comment PRIMARY KEY (id)\n);\nCREATE INDEX idx1_process_comment on process_comment (processInstanceId);\n\nCREATE TABLE process_content (\n  id INT8 NOT NULL,\n  content TEXT NOT NULL,\n  CONSTRAINT pk_process_content PRIMARY KEY (id)\n);\n\nCREATE TABLE process_definition (\n  id INT8 NOT NULL,\n  processId INT8 NOT NULL,\n  name VARCHAR(150) NOT NULL,\n  version VARCHAR(50) NOT NULL,\n  description VARCHAR(255),\n  deploymentDate INT8 NOT NULL,\n  deployedBy INT8 NOT NULL,\n  activationState VARCHAR(30) NOT NULL,\n  configurationState VARCHAR(30) NOT NULL,\n  displayName VARCHAR(75),\n  displayDescription VARCHAR(255),\n  lastUpdateDate INT8,\n  categoryId INT8,\n  iconPath VARCHAR(255),\n  content_id INT8 NOT NULL,\n  CONSTRAINT pk_process_definition PRIMARY KEY (id),\n  CONSTRAINT uk_process_definition_name_version UNIQUE (name, version)\n);\nALTER TABLE process_definition ADD CONSTRAINT fk_process_definition_content_id FOREIGN KEY (content_id) REFERENCES process_content(id);\n\nCREATE TABLE document (\n  id INT8 NOT NULL,\n  author INT8,\n  creationdate INT8 NOT NULL,\n  hascontent BOOLEAN NOT NULL,\n  filename VARCHAR(255),\n  mimetype VARCHAR(255),\n  url VARCHAR(1024),\n  content BYTEA,\n  CONSTRAINT pk_document PRIMARY KEY (id)\n);\n\nCREATE TABLE document_mapping (\n  id INT8 NOT NULL,\n  processinstanceid INT8 NOT NULL,\n  documentid INT8 NOT NULL,\n  name VARCHAR(50) NOT NULL,\n  description TEXT,\n  version VARCHAR(50) NOT NULL,\n  index_ INT NOT NULL,\n  CONSTRAINT pk_document_mapping PRIMARY KEY (id)\n);\nALTER TABLE document_mapping ADD CONSTRAINT fk_document_mapping_documentid FOREIGN KEY (documentid) REFERENCES document(id) ON DELETE CASCADE;\n\nCREATE TABLE arch_document_mapping (\n  id INT8 NOT NULL,\n  sourceObjectId INT8,\n  processinstanceid INT8 NOT NULL,\n  documentid INT8 NOT NULL,\n  name VARCHAR(50) NOT NULL,\n  description TEXT,\n  version VARCHAR(50) NOT NULL,\n  index_ INT NOT NULL,\n  archiveDate INT8 NOT NULL,\n  CONSTRAINT pk_arch_document_mapping PRIMARY KEY (id)\n);\nCREATE INDEX idx_a_doc_mp_pr_id ON arch_document_mapping (processinstanceid);\nALTER TABLE arch_document_mapping ADD CONSTRAINT fk_arch_document_mapping_documentid FOREIGN KEY (documentid) REFERENCES document(id) ON DELETE CASCADE;\n\nCREATE TABLE arch_process_instance (\n  id INT8 NOT NULL,\n  name VARCHAR(75) NOT NULL,\n  processDefinitionId INT8 NOT NULL,\n  description VARCHAR(255),\n  startDate INT8 NOT NULL,\n  startedBy INT8 NOT NULL,\n  startedBySubstitute INT8 NOT NULL,\n  endDate INT8 NOT NULL,\n  archiveDate INT8 NOT NULL,\n  stateId INT NOT NULL,\n  lastUpdate INT8 NOT NULL,\n  rootProcessInstanceId INT8,\n  callerId INT8,\n  sourceObjectId INT8 NOT NULL,\n  stringIndex1 VARCHAR(255),\n  stringIndex2 VARCHAR(255),\n  stringIndex3 VARCHAR(255),\n  stringIndex4 VARCHAR(255),\n  stringIndex5 VARCHAR(255),\n  CONSTRAINT pk_arch_process_instance PRIMARY KEY (id)\n);\nCREATE INDEX idx1_arch_process_instance ON arch_process_instance (sourceObjectId, rootProcessInstanceId, callerId);\nCREATE INDEX idx2_arch_process_instance ON arch_process_instance (processDefinitionId, archiveDate);\nCREATE INDEX idx3_arch_process_instance ON arch_process_instance (sourceObjectId, callerId, stateId);\n\nCREATE TABLE arch_flownode_instance (\n  id INT8 NOT NULL,\n  flownodeDefinitionId INT8 NOT NULL,\n  kind VARCHAR(25) NOT NULL,\n  sourceObjectId INT8,\n  archiveDate INT8 NOT NULL,\n  rootContainerId INT8 NOT NULL,\n  parentContainerId INT8 NOT NULL,\n  name VARCHAR(255) NOT NULL,\n  displayName VARCHAR(255),\n  displayDescription VARCHAR(255),\n  stateId INT NOT NULL,\n  stateName VARCHAR(50),\n  terminal BOOLEAN NOT NULL,\n  stable BOOLEAN ,\n  actorId INT8 NULL,\n  assigneeId INT8 DEFAULT 0 NOT NULL,\n  reachedStateDate INT8,\n  lastUpdateDate INT8,\n  expectedEndDate INT8,\n  claimedDate INT8,\n  priority SMALLINT,\n  gatewayType VARCHAR(50),\n  hitBys VARCHAR(255),\n  logicalGroup1 INT8 NOT NULL,\n  logicalGroup2 INT8 NOT NULL,\n  logicalGroup3 INT8,\n  logicalGroup4 INT8 NOT NULL,\n  loop_counter INT,\n  loop_max INT,\n  loopCardinality INT,\n  loopDataInputRef VARCHAR(255),\n  loopDataOutputRef VARCHAR(255),\n  description VARCHAR(255),\n  sequential BOOLEAN,\n  dataInputItemRef VARCHAR(255),\n  dataOutputItemRef VARCHAR(255),\n  nbActiveInst INT,\n  nbCompletedInst INT,\n  nbTerminatedInst INT,\n  executedBy INT8,\n  executedBySubstitute INT8,\n  activityInstanceId INT8,\n  aborting BOOLEAN NOT NULL,\n  triggeredByEvent BOOLEAN,\n  interrupting BOOLEAN,\n  CONSTRAINT pk_arch_flownode_instance PRIMARY KEY (id)\n);\nCREATE INDEX idx_afi_kind_lg2_executedBy ON arch_flownode_instance(logicalGroup2, kind, executedBy);\nCREATE INDEX idx_afi_kind_lg3 ON arch_flownode_instance(kind, logicalGroup3);\nCREATE INDEX idx_afi_lg4 ON arch_flownode_instance(logicalGroup4);\nCREATE INDEX idx_afi_sourceid_kind ON arch_flownode_instance (sourceObjectId, kind);\nCREATE INDEX idx1_afi_root_parent ON arch_flownode_instance (rootContainerId, parentContainerId);\nCREATE INDEX idx_lg4_lg2 on arch_flownode_instance(logicalGroup4, logicalGroup2);\n\nCREATE TABLE arch_connector_instance (\n  id INT8 NOT NULL,\n  containerId INT8 NOT NULL,\n  containerType VARCHAR(10) NOT NULL,\n  connectorId VARCHAR(255) NOT NULL,\n  version VARCHAR(50) NOT NULL,\n  name VARCHAR(255) NOT NULL,\n  activationEvent VARCHAR(30),\n  state VARCHAR(50),\n  sourceObjectId INT8,\n  archiveDate INT8 NOT NULL,\n  CONSTRAINT pk_arch_connector_instance PRIMARY KEY (id)\n);\nCREATE INDEX idx1_arch_connector_instance ON arch_connector_instance (containerId, containerType);\n\nCREATE TABLE process_instance (\n  id INT8 NOT NULL,\n  name VARCHAR(75) NOT NULL,\n  processDefinitionId INT8 NOT NULL,\n  description VARCHAR(255),\n  startDate INT8 NOT NULL,\n  startedBy INT8 NOT NULL,\n  startedBySubstitute INT8 NOT NULL,\n  endDate INT8 NOT NULL,\n  stateId INT NOT NULL,\n  stateCategory VARCHAR(50) NOT NULL,\n  lastUpdate INT8 NOT NULL,\n  containerId INT8,\n  rootProcessInstanceId INT8,\n  callerId INT8,\n  callerType VARCHAR(50),\n  interruptingEventId INT8,\n  stringIndex1 VARCHAR(255),\n  stringIndex2 VARCHAR(255),\n  stringIndex3 VARCHAR(255),\n  stringIndex4 VARCHAR(255),\n  stringIndex5 VARCHAR(255),\n  PRIMARY KEY (id)\n);\n\nCREATE INDEX idx1_proc_inst_pdef_state ON process_instance (processdefinitionid, stateid);\n\nCREATE TABLE flownode_instance (\n  id INT8 NOT NULL,\n  flownodeDefinitionId INT8 NOT NULL,\n  kind VARCHAR(25) NOT NULL,\n  rootContainerId INT8 NOT NULL,\n  parentContainerId INT8 NOT NULL,\n  name VARCHAR(255) NOT NULL,\n  displayName VARCHAR(255),\n  displayDescription VARCHAR(255),\n  stateId INT NOT NULL,\n  stateName VARCHAR(50),\n  prev_state_id INT NOT NULL,\n  terminal BOOLEAN NOT NULL,\n  stable BOOLEAN ,\n  actorId INT8 NULL,\n  assigneeId INT8 DEFAULT 0 NOT NULL,\n  reachedStateDate INT8,\n  lastUpdateDate INT8,\n  expectedEndDate INT8,\n  claimedDate INT8,\n  priority SMALLINT,\n  gatewayType VARCHAR(50),\n  hitBys VARCHAR(255),\n  stateCategory VARCHAR(50) NOT NULL,\n  logicalGroup1 INT8 NOT NULL,\n  logicalGroup2 INT8 NOT NULL,\n  logicalGroup3 INT8,\n  logicalGroup4 INT8 NOT NULL,\n  loop_counter INT,\n  loop_max INT,\n  description VARCHAR(255),\n  sequential BOOLEAN,\n  loopDataInputRef VARCHAR(255),\n  loopDataOutputRef VARCHAR(255),\n  dataInputItemRef VARCHAR(255),\n  dataOutputItemRef VARCHAR(255),\n  loopCardinality INT,\n  nbActiveInst INT,\n  nbCompletedInst INT,\n  nbTerminatedInst INT,\n  executedBy INT8,\n  executedBySubstitute INT8,\n  activityInstanceId INT8,\n  state_executing BOOLEAN DEFAULT FALSE,\n  abortedByBoundary INT8,\n  triggeredByEvent BOOLEAN,\n  interrupting BOOLEAN,\n  tokenCount INT NOT NULL,\n  PRIMARY KEY (id)\n);\nCREATE INDEX idx_fni_rootcontid ON flownode_instance (rootContainerId);\nCREATE INDEX idx_fni_loggroup4 ON flownode_instance (logicalGroup4);\nCREATE INDEX idx_fni_loggroup3_terminal ON flownode_instance(logicalgroup3, terminal);\nCREATE INDEX idx_fn_lg2_state ON flownode_instance (logicalGroup2, stateName);\nCREATE INDEX idx_fni_activity_instance_id_kind ON flownode_instance(activityInstanceId, kind);\n\nCREATE TABLE connector_instance (\n  id INT8 NOT NULL,\n  containerId INT8 NOT NULL,\n  containerType VARCHAR(10) NOT NULL,\n  connectorId VARCHAR(255) NOT NULL,\n  version VARCHAR(50) NOT NULL,\n  name VARCHAR(255) NOT NULL,\n  activationEvent VARCHAR(30),\n  state VARCHAR(50),\n  executionOrder INT,\n  exceptionMessage VARCHAR(255),\n  stackTrace TEXT,\n  CONSTRAINT pk_connector_instance PRIMARY KEY (id)\n);\nCREATE INDEX idx_ci_container_activation ON connector_instance (containerId, containerType, activationEvent);\n\nCREATE TABLE event_trigger_instance (\n  \tid INT8 NOT NULL,\n  \teventInstanceId INT8 NOT NULL,\n  \teventInstanceName VARCHAR(50),\n  \texecutionDate INT8,\n  \tjobTriggerName VARCHAR(255),\n    CONSTRAINT pk_event_trigger_instance PRIMARY KEY (id)\n);\n\nCREATE TABLE waiting_event (\n  \tid INT8 NOT NULL,\n  \tkind VARCHAR(15) NOT NULL,\n  \teventType VARCHAR(50),\n  \tmessageName VARCHAR(255),\n  \tsignalName VARCHAR(255),\n  \terrorCode VARCHAR(255),\n  \tprocessName VARCHAR(150),\n  \tflowNodeName VARCHAR(50),\n  \tflowNodeDefinitionId INT8,\n  \tsubProcessId INT8,\n  \tprocessDefinitionId INT8,\n  \trootProcessInstanceId INT8,\n  \tparentProcessInstanceId INT8,\n  \tflowNodeInstanceId INT8,\n  \trelatedActivityInstanceId INT8,\n  \tlocked BOOLEAN,\n  \tactive BOOLEAN,\n  \tprogress SMALLINT,\n  \tcorrelation1 VARCHAR(128),\n  \tcorrelation2 VARCHAR(128),\n  \tcorrelation3 VARCHAR(128),\n  \tcorrelation4 VARCHAR(128),\n  \tcorrelation5 VARCHAR(128),\n    CONSTRAINT pk_waiting_event PRIMARY KEY (id)\n);\nCREATE INDEX idx_waiting_event ON waiting_event (progress, kind, locked, active);\nCREATE INDEX idx_waiting_event_correl ON waiting_event (correlation1, correlation2, correlation3, correlation4, correlation5);\n\nCREATE TABLE message_instance (\n  \tid INT8 NOT NULL,\n  \tmessageName VARCHAR(255) NOT NULL,\n  \ttargetProcess VARCHAR(255) NOT NULL,\n  \ttargetFlowNode VARCHAR(255) NULL,\n  \tlocked BOOLEAN NOT NULL,\n  \thandled BOOLEAN NOT NULL,\n  \tprocessDefinitionId INT8 NOT NULL,\n  \tflowNodeName VARCHAR(255),\n  \tcorrelation1 VARCHAR(128),\n  \tcorrelation2 VARCHAR(128),\n  \tcorrelation3 VARCHAR(128),\n  \tcorrelation4 VARCHAR(128),\n  \tcorrelation5 VARCHAR(128),\n  \tcreationDate INT8 NOT NULL,\n    CONSTRAINT pk_message_instance PRIMARY KEY (id)\n);\nCREATE INDEX idx_message_instance ON message_instance (messageName, targetProcess, correlation1, correlation2, correlation3);\nCREATE INDEX idx_message_instance_correl ON message_instance (correlation1, correlation2, correlation3, correlation4, correlation5);\n\nCREATE TABLE pending_mapping (\n  \tid INT8 NOT NULL,\n  \tactivityId INT8 NOT NULL,\n  \tactorId INT8,\n  \tuserId INT8,\n  \tCONSTRAINT pk_pending_mapping PRIMARY KEY (id),\n    CONSTRAINT uk_pending_mapping_activityid_userid_actorid UNIQUE (activityId, userId, actorId)\n);\nALTER TABLE pending_mapping ADD CONSTRAINT fk_pending_mapping_activityid FOREIGN KEY (activityId) REFERENCES flownode_instance(id);\n\nCREATE TABLE ref_biz_data_inst (\n  id INT8 NOT NULL,\n  kind VARCHAR(15) NOT NULL,\n  name VARCHAR(255) NOT NULL,\n  proc_inst_id INT8,\n  fn_inst_id INT8,\n  data_id INT8,\n  data_classname VARCHAR(255) NOT NULL,\n  CONSTRAINT pk_ref_biz_data_inst PRIMARY KEY (id)\n);\nCREATE INDEX idx_biz_data_inst2 ON ref_biz_data_inst (fn_inst_id);\nCREATE INDEX idx_biz_data_inst3 ON ref_biz_data_inst (proc_inst_id);\nALTER TABLE ref_biz_data_inst ADD CONSTRAINT fk_ref_biz_data_inst_proc_inst_id FOREIGN KEY (proc_inst_id) REFERENCES process_instance(id) ON DELETE CASCADE;\nALTER TABLE ref_biz_data_inst ADD CONSTRAINT fk_ref_biz_data_inst_fn_inst_id FOREIGN KEY (fn_inst_id) REFERENCES flownode_instance(id) ON DELETE CASCADE;\n\nCREATE TABLE multi_biz_data (\n  id INT8 NOT NULL,\n  idx INT8 NOT NULL,\n  data_id INT8 NOT NULL,\n  CONSTRAINT pk_multi_biz_data PRIMARY KEY (id, data_id)\n);\nALTER TABLE multi_biz_data ADD CONSTRAINT fk_multi_biz_data_id FOREIGN KEY (id) REFERENCES ref_biz_data_inst(id) ON DELETE CASCADE;\n\nCREATE TABLE arch_ref_biz_data_inst (\n  id BIGINT NOT NULL,\n  kind VARCHAR(15) NOT NULL,\n  name VARCHAR(255) NOT NULL,\n  orig_proc_inst_id BIGINT,\n  orig_fn_inst_id BIGINT,\n  data_id BIGINT,\n  data_classname VARCHAR(255) NOT NULL,\n  CONSTRAINT pk_arch_ref_biz_data_inst PRIMARY KEY (id)\n);\nCREATE INDEX idx_arch_biz_data_inst1 ON arch_ref_biz_data_inst (orig_proc_inst_id);\nCREATE INDEX idx_arch_biz_data_inst2 ON arch_ref_biz_data_inst (orig_fn_inst_id);\n\nCREATE TABLE arch_multi_biz_data (\n  id BIGINT NOT NULL,\n  idx BIGINT NOT NULL,\n  data_id BIGINT NOT NULL,\n  CONSTRAINT pk_arch_multi_biz_data PRIMARY KEY (id, data_id)\n);\nALTER TABLE arch_multi_biz_data ADD CONSTRAINT fk_arch_multi_biz_data_id FOREIGN KEY (id) REFERENCES arch_ref_biz_data_inst(id) ON DELETE CASCADE;\n\nCREATE TABLE processsupervisor (\n  id INT8 NOT NULL,\n  processDefId INT8 NOT NULL,\n  userId INT8 NOT NULL,\n  groupId INT8 NOT NULL,\n  roleId INT8 NOT NULL,\n  CONSTRAINT pk_processsupervisor PRIMARY KEY (id),\n  CONSTRAINT uk_processsupervisor_processdefid_userid_groupid_roleid UNIQUE (processDefId, userId, groupId, roleId)\n);\n\nCREATE TABLE page (\n  id INT8 NOT NULL,\n  name VARCHAR(255) NOT NULL,\n  displayName VARCHAR(255) NOT NULL,\n  description TEXT,\n  installationDate INT8 NOT NULL,\n  installedBy INT8 NOT NULL,\n  provided BOOLEAN,\n  editable BOOLEAN,\n  removable BOOLEAN,\n  lastModificationDate INT8 NOT NULL,\n  lastUpdatedBy INT8 NOT NULL,\n  contentName VARCHAR(280) NOT NULL,\n  content BYTEA,\n  contentType VARCHAR(50) NOT NULL,\n  processDefinitionId INT8 NOT NULL,\n  pageHash VARCHAR(32),\n  CONSTRAINT pk_page PRIMARY KEY (id),\n  CONSTRAINT uk_page_name_processdefinitionid UNIQUE (name, processDefinitionId)\n);\n\nCREATE TABLE profile (\n  id INT8 NOT NULL,\n  isDefault BOOLEAN NOT NULL,\n  name VARCHAR(50) NOT NULL,\n  description TEXT,\n  creationDate INT8 NOT NULL,\n  createdBy INT8 NOT NULL,\n  lastUpdateDate INT8 NOT NULL,\n  lastUpdatedBy INT8 NOT NULL,\n  CONSTRAINT uk_profile_name UNIQUE (name),\n  CONSTRAINT pk_profile PRIMARY KEY (id)\n);\n\nCREATE TABLE profilemember (\n  id INT8 NOT NULL,\n  profileId INT8 NOT NULL,\n  userId INT8 NOT NULL,\n  groupId INT8 NOT NULL,\n  roleId INT8 NOT NULL,\n  CONSTRAINT pk_profilemember PRIMARY KEY (id),\n  CONSTRAINT uk_profilemember_profileid_userid_groupid_roleid UNIQUE (profileId, userId, groupId, roleId)\n);\nALTER TABLE profilemember ADD CONSTRAINT fk_profilemember_profileid FOREIGN KEY (profileId) REFERENCES profile(id);\n\nCREATE TABLE business_app (\n  id INT8 NOT NULL,\n  token VARCHAR(50) NOT NULL,\n  version VARCHAR(50) NOT NULL,\n  description TEXT,\n  iconPath VARCHAR(255),\n  creationDate INT8 NOT NULL,\n  createdBy INT8 NOT NULL,\n  lastUpdateDate INT8 NOT NULL,\n  updatedBy INT8 NOT NULL,\n  state VARCHAR(30) NOT NULL,\n  homePageId INT8,\n  profileId INT8,\n  layoutId INT8,\n  themeId INT8,\n  iconMimeType VARCHAR(255),\n  iconContent BYTEA,\n  displayName VARCHAR(255) NOT NULL,\n  editable BOOLEAN,\n  internalProfile VARCHAR(255),\n  isLink BOOLEAN DEFAULT FALSE,\n  CONSTRAINT pk_business_app PRIMARY KEY (id),\n  CONSTRAINT uk_business_app_token_version UNIQUE (token, version)\n);\nCREATE INDEX idx_app_token ON business_app (token);\nCREATE INDEX idx_app_profile ON business_app (profileId);\nCREATE INDEX idx_app_homepage ON business_app (homePageId);\nALTER TABLE business_app ADD CONSTRAINT fk_business_app_profileid FOREIGN KEY (profileId) REFERENCES profile (id);\nALTER TABLE business_app ADD CONSTRAINT fk_business_app_layoutid FOREIGN KEY (layoutId) REFERENCES page (id);\nALTER TABLE business_app ADD CONSTRAINT fk_business_app_themeid FOREIGN KEY (themeId) REFERENCES page (id);\n\nCREATE TABLE business_app_page (\n  id INT8 NOT NULL,\n  applicationId INT8 NOT NULL,\n  pageId INT8 NOT NULL,\n  token VARCHAR(255) NOT NULL,\n  CONSTRAINT pk_business_app_page PRIMARY KEY (id),\n  CONSTRAINT uk_business_app_page_applicationid_token UNIQUE (applicationId, token)\n);\nCREATE INDEX idx_app_page_token ON business_app_page (applicationId, token);\nCREATE INDEX idx_app_page_pageId ON business_app_page (pageId);\nALTER TABLE business_app_page ADD CONSTRAINT fk_business_app_page_applicationid FOREIGN KEY (applicationId) REFERENCES business_app (id) ON DELETE CASCADE;\nALTER TABLE business_app_page ADD CONSTRAINT fk_business_app_page_pageid FOREIGN KEY (pageId) REFERENCES page (id);\n\nCREATE TABLE business_app_menu (\n  id INT8 NOT NULL,\n  displayName VARCHAR(255) NOT NULL,\n  applicationId INT8 NOT NULL,\n  applicationPageId INT8,\n  parentId INT8,\n  index_ INT8,\n  CONSTRAINT pk_business_app_menu PRIMARY KEY (id)\n);\nCREATE INDEX idx_app_menu_app ON business_app_menu (applicationId);\nCREATE INDEX idx_app_menu_page ON business_app_menu (applicationPageId);\nCREATE INDEX idx_app_menu_parent ON business_app_menu (parentId);\n-- cannot have both fk_business_app_menu_applicationid and fk_business_app_menu_applicationpageid because this create to path for deletion of business_app_menu elements:\n-- business_app -> business_app_menu\n-- business_app -> business_app_page -> business_app_menu\n-- this is not allowed in SQL Server\nALTER TABLE business_app_menu ADD CONSTRAINT fk_business_app_menu_applicationid FOREIGN KEY (applicationId) REFERENCES business_app (id);\nALTER TABLE business_app_menu ADD CONSTRAINT fk_business_app_menu_applicationpageid FOREIGN KEY (applicationPageId) REFERENCES business_app_page (id);\nALTER TABLE business_app_menu ADD CONSTRAINT fk_business_app_menu_parentid FOREIGN KEY (parentId) REFERENCES business_app_menu (id);\n\nCREATE TABLE command (\n  id INT8 NOT NULL,\n  name VARCHAR(50) NOT NULL,\n  description TEXT,\n  IMPLEMENTATION VARCHAR(100) NOT NULL,\n  isSystem BOOLEAN,\n  CONSTRAINT pk_command PRIMARY KEY (id),\n  CONSTRAINT uk_command_name UNIQUE (name)\n);\n\nCREATE TABLE arch_data_instance (\n  id INT8 NOT NULL,\n  name VARCHAR(50),\n  description VARCHAR(50),\n  transientData BOOLEAN,\n  className VARCHAR(100),\n  containerId INT8,\n  containerType VARCHAR(60),\n  namespace VARCHAR(100),\n  element VARCHAR(60),\n  intValue INT,\n  longValue INT8,\n  shortTextValue VARCHAR(255),\n  booleanValue BOOLEAN,\n  doubleValue NUMERIC(19,5),\n  floatValue REAL,\n  blobValue BYTEA,\n  clobValue TEXT,\n  discriminant VARCHAR(50) NOT NULL,\n  archiveDate INT8 NOT NULL,\n  sourceObjectId INT8 NOT NULL,\n  CONSTRAINT pk_arch_data_instance PRIMARY KEY (id)\n);\nCREATE INDEX idx1_arch_data_instance ON arch_data_instance (containerId, containerType, archiveDate, name, sourceObjectId);\nCREATE INDEX idx2_arch_data_instance ON arch_data_instance (sourceObjectId, containerId, archiveDate, id);\n\nCREATE TABLE data_instance (\n  id INT8 NOT NULL,\n  name VARCHAR(50),\n  description VARCHAR(50),\n  transientData BOOLEAN,\n  className VARCHAR(100),\n  containerId INT8,\n  containerType VARCHAR(60),\n  namespace VARCHAR(100),\n  element VARCHAR(60),\n  intValue INT,\n  longValue INT8,\n  shortTextValue VARCHAR(255),\n  booleanValue BOOLEAN,\n  doubleValue NUMERIC(19,5),\n  floatValue REAL,\n  blobValue BYTEA,\n  clobValue TEXT,\n  discriminant VARCHAR(50) NOT NULL,\n  CONSTRAINT pk_data_instance PRIMARY KEY (id)\n);\nCREATE INDEX idx_datai_container ON data_instance (containerId, containerType, name);\n\nCREATE TABLE dependency (\n  id INT8 NOT NULL,\n  name VARCHAR(150) NOT NULL,\n  description TEXT,\n  filename VARCHAR(255) NOT NULL,\n  value_ BYTEA NOT NULL,\n  CONSTRAINT pk_dependency PRIMARY KEY (id),\n  CONSTRAINT uk_dependency_name UNIQUE (name)\n);\n\nCREATE TABLE dependencymapping (\n  id INT8 NOT NULL,\n  artifactid INT8 NOT NULL,\n  artifacttype VARCHAR(50) NOT NULL,\n  dependencyid INT8 NOT NULL,\n  CONSTRAINT pk_dependencymapping PRIMARY KEY (id),\n  CONSTRAINT uk_dependencymapping_dependencyid_artifactid_artifacttype UNIQUE (dependencyid, artifactid, artifacttype)\n);\nCREATE INDEX idx_dependencymapping_depid ON dependencymapping (dependencyid);\nALTER TABLE dependencymapping ADD CONSTRAINT fk_dependencymapping_dependencyid FOREIGN KEY (dependencyid) REFERENCES dependency(id) ON DELETE CASCADE;\n\nCREATE TABLE pdependency (\n  id INT8 NOT NULL,\n  name VARCHAR(50) NOT NULL,\n  description TEXT,\n  filename VARCHAR(255) NOT NULL,\n  value_ BYTEA NOT NULL,\n  CONSTRAINT pk_pdependency PRIMARY KEY (id),\n  CONSTRAINT uk_pdependency_name UNIQUE (name)\n);\n\nCREATE TABLE pdependencymapping (\n  id INT8 NOT NULL,\n  artifactid INT8 NOT NULL,\n  artifacttype VARCHAR(50) NOT NULL,\n  dependencyid INT8 NOT NULL,\n  CONSTRAINT pk_pdependencymapping PRIMARY KEY (id),\n  CONSTRAINT uk_pdependencymapping_dependencyid_artifactid_artifacttype UNIQUE (dependencyid, artifactid, artifacttype)\n);\nCREATE INDEX idx_pdependencymapping_depid ON pdependencymapping (dependencyid);\nALTER TABLE pdependencymapping ADD CONSTRAINT fk_pdependencymapping_dependencyid FOREIGN KEY (dependencyid) REFERENCES pdependency(id) ON DELETE CASCADE;\n\nCREATE TABLE group_ (\n  id INT8 NOT NULL,\n  name VARCHAR(125) NOT NULL,\n  parentPath VARCHAR(255),\n  displayName VARCHAR(255),\n  description TEXT,\n  createdBy INT8,\n  creationDate INT8,\n  lastUpdate INT8,\n  iconid INT8,\n  CONSTRAINT pk_group PRIMARY KEY (id)\n);\nCREATE INDEX idx_group_name ON group_ (parentPath, name);\n\nCREATE TABLE role (\n  id INT8 NOT NULL,\n  name VARCHAR(255) NOT NULL,\n  displayName VARCHAR(255),\n  description TEXT,\n  createdBy INT8,\n  creationDate INT8,\n  lastUpdate INT8,\n  iconid INT8,\n  CONSTRAINT pk_role PRIMARY KEY (id),\n  CONSTRAINT uk_role_name UNIQUE (name)\n);\n\nCREATE TABLE user_ (\n  id INT8 NOT NULL,\n  enabled BOOLEAN NOT NULL,\n  userName VARCHAR(255) NOT NULL,\n  password VARCHAR(60),\n  firstName VARCHAR(255),\n  lastName VARCHAR(255),\n  title VARCHAR(50),\n  jobTitle VARCHAR(255),\n  managerUserId INT8,\n  createdBy INT8,\n  creationDate INT8,\n  lastUpdate INT8,\n  iconid INT8,\n  CONSTRAINT pk_user PRIMARY KEY (id),\n  CONSTRAINT uk_user_username UNIQUE (userName)\n);\n\nCREATE TABLE user_login (\n  id INT8 NOT NULL,\n  lastConnection INT8,\n  CONSTRAINT pk_user_login PRIMARY KEY (id)\n);\n\nCREATE TABLE user_contactinfo (\n  id INT8 NOT NULL,\n  userId INT8 NOT NULL,\n  email VARCHAR(255),\n  phone VARCHAR(50),\n  mobile VARCHAR(50),\n  fax VARCHAR(50),\n  building VARCHAR(50),\n  room VARCHAR(50),\n  address VARCHAR(255),\n  zipCode VARCHAR(50),\n  city VARCHAR(255),\n  state VARCHAR(255),\n  country VARCHAR(255),\n  website VARCHAR(255),\n  personal BOOLEAN NOT NULL,\n  CONSTRAINT pk_user_contactinfo PRIMARY KEY (id),\n  CONSTRAINT uk_user_contactinfo_userid_personal UNIQUE (userId, personal)\n);\nALTER TABLE user_contactinfo ADD CONSTRAINT fk_user_contactinfo_userid FOREIGN KEY (userId) REFERENCES user_ (id) ON DELETE CASCADE;\n\nCREATE TABLE custom_usr_inf_def (\n  id INT8 NOT NULL,\n  name VARCHAR(75) NOT NULL,\n  description TEXT,\n  CONSTRAINT pk_custom_usr_inf_def PRIMARY KEY (id),\n  CONSTRAINT uk_custom_usr_inf_def_name UNIQUE (name)\n);\n\nCREATE TABLE custom_usr_inf_val (\n  id INT8 NOT NULL,\n  definitionId INT8 NOT NULL,\n  userId INT8 NOT NULL,\n  value VARCHAR(255),\n  CONSTRAINT pk_custom_usr_inf_val PRIMARY KEY (id),\n  CONSTRAINT uk_custom_usr_inf_val_definitionid_userid UNIQUE (definitionId, userId)\n);\nALTER TABLE custom_usr_inf_val ADD CONSTRAINT fk_custom_usr_inf_val_userid FOREIGN KEY (userId) REFERENCES user_ (id) ON DELETE CASCADE;\nALTER TABLE custom_usr_inf_val ADD CONSTRAINT fk_custom_usr_inf_val_definitionid FOREIGN KEY (definitionId) REFERENCES custom_usr_inf_def (id) ON DELETE CASCADE;\n\nCREATE TABLE user_membership (\n  id INT8 NOT NULL,\n  userId INT8 NOT NULL,\n  roleId INT8 NOT NULL,\n  groupId INT8 NOT NULL,\n  assignedBy INT8,\n  assignedDate INT8,\n  CONSTRAINT pk_user_membership PRIMARY KEY (id),\n  CONSTRAINT uk_user_membership_userid_roleid_groupid UNIQUE (userId, roleId, groupId)\n);\n\nCREATE TABLE icon (\n  id INT8 NOT NULL,\n  mimetype VARCHAR(255) NOT NULL,\n  content BYTEA NOT NULL,\n  CONSTRAINT pk_icon PRIMARY KEY (id)\n);\n\nCREATE TABLE queriable_log (\n  id INT8 NOT NULL,\n  log_timestamp INT8 NOT NULL,\n  whatYear SMALLINT NOT NULL,\n  whatMonth SMALLINT NOT NULL,\n  dayOfYear SMALLINT NOT NULL,\n  weekOfYear SMALLINT NOT NULL,\n  userId VARCHAR(255) NOT NULL,\n  threadNumber INT8 NOT NULL,\n  clusterNode VARCHAR(50),\n  productVersion VARCHAR(50) NOT NULL,\n  severity VARCHAR(50) NOT NULL,\n  actionType VARCHAR(50) NOT NULL,\n  actionScope VARCHAR(100),\n  actionStatus SMALLINT NOT NULL,\n  rawMessage VARCHAR(255) NOT NULL,\n  callerClassName VARCHAR(200),\n  callerMethodName VARCHAR(80),\n  numericIndex1 INT8,\n  numericIndex2 INT8,\n  numericIndex3 INT8,\n  numericIndex4 INT8,\n  numericIndex5 INT8,\n  CONSTRAINT pk_queriable_log PRIMARY KEY (id)\n);\n\nCREATE TABLE sequence (\n  id INT8 NOT NULL,\n  nextid INT8 NOT NULL,\n  CONSTRAINT pk_sequence PRIMARY KEY (id)\n);\n\nCREATE TABLE platform (\n  id INT8 NOT NULL,\n  version VARCHAR(50) NOT NULL,\n  initial_bonita_version VARCHAR(50) NOT NULL,\n  application_version VARCHAR(50) NOT NULL,\n  maintenance_message TEXT,\n  maintenance_message_active BOOLEAN NOT NULL,\n  created INT8 NOT NULL,\n  created_by VARCHAR(50) NOT NULL,\n  information TEXT,\n  maintenance_enabled BOOLEAN NOT NULL,\n  CONSTRAINT pk_platform PRIMARY KEY (id)\n);\n\nCREATE TABLE platformCommand (\n  id INT8 PRIMARY KEY,\n  name VARCHAR(50) NOT NULL UNIQUE,\n  description TEXT,\n  IMPLEMENTATION VARCHAR(100) NOT NULL\n);\n\nCREATE TABLE job_desc (\n  id INT8 NOT NULL,\n  jobclassname VARCHAR(100) NOT NULL,\n  jobname VARCHAR(100) NOT NULL,\n  description VARCHAR(50),\n  CONSTRAINT pk_job_desc PRIMARY KEY (id)\n);\n\nCREATE TABLE job_param (\n  id INT8 NOT NULL,\n  jobDescriptorId INT8 NOT NULL,\n  key_ VARCHAR(50) NOT NULL,\n  value_ BYTEA NOT NULL,\n  CONSTRAINT pk_job_param PRIMARY KEY (id)\n);\nALTER TABLE job_param ADD CONSTRAINT fk_job_param_jobdescriptorid FOREIGN KEY (jobDescriptorId) REFERENCES job_desc(id) ON DELETE CASCADE;\nCREATE INDEX idx_job_param_jobid ON job_param(jobDescriptorId);\n\nCREATE TABLE job_log (\n  id INT8 NOT NULL,\n  jobDescriptorId INT8 NOT NULL,\n  retryNumber INT8,\n  lastUpdateDate INT8,\n  lastMessage TEXT,\n  CONSTRAINT pk_job_log PRIMARY KEY (id),\n  CONSTRAINT uk_job_log_jobdescriptorid UNIQUE (jobDescriptorId)\n);\nALTER TABLE job_log ADD CONSTRAINT fk_job_log_jobdescriptorid FOREIGN KEY (jobDescriptorId) REFERENCES job_desc(id) ON DELETE CASCADE;\n\nCREATE TABLE page_mapping (\n  id INT8 NOT NULL,\n  key_ VARCHAR(255) NOT NULL,\n  pageId INT8 NULL,\n  url VARCHAR(1024) NULL,\n  urladapter VARCHAR(255) NULL,\n  page_authoriz_rules TEXT NULL,\n  lastUpdateDate INT8 NULL,\n  lastUpdatedBy INT8 NULL,\n  CONSTRAINT uk_page_mapping_key UNIQUE (key_),\n  CONSTRAINT pk_page_mapping PRIMARY KEY (id)\n);\n\nCREATE TABLE form_mapping (\n  id INT8 NOT NULL,\n  process INT8 NOT NULL,\n  type INT NOT NULL,\n  task VARCHAR(255),\n  page_mapping_id INT8,\n  lastUpdateDate INT8,\n  lastUpdatedBy INT8,\n  target VARCHAR(16) NOT NULL,\n  CONSTRAINT pk_form_mapping PRIMARY KEY (id)\n);\n\nALTER TABLE form_mapping ADD CONSTRAINT fk_form_mapping_key FOREIGN KEY (page_mapping_id) REFERENCES page_mapping(id);\n\nCREATE TABLE proc_parameter (\n  id INT8 NOT NULL,\n  process_id INT8 NOT NULL,\n  name VARCHAR(255) NOT NULL,\n  value TEXT NULL,\n  CONSTRAINT pk_proc_parameter PRIMARY KEY (id)\n);\n\nCREATE TABLE bar_resource (\n  id INT8 NOT NULL,\n  process_id INT8 NOT NULL,\n  name VARCHAR(255) NOT NULL,\n  type VARCHAR(16) NOT NULL,\n  content BYTEA NOT NULL,\n  CONSTRAINT pk_bar_resource PRIMARY KEY (id),\n  CONSTRAINT uk_bar_resource_processid_name_type UNIQUE (process_id, name, type)\n);\n\nCREATE TABLE temporary_content (\n  id INT8 NOT NULL,\n  creationDate INT8 NOT NULL,\n  key_ VARCHAR(255) NOT NULL,\n  fileName VARCHAR(255) NOT NULL,\n  mimeType VARCHAR(255) NOT NULL,\n  content OID NOT NULL,\n  UNIQUE (key_),\n  PRIMARY KEY (id)\n);\nCREATE INDEX idx_temporary_content ON temporary_content (key_);\n\n------------------------- PostgreSQL large objects cleanup for temporary_content ----------------------\nDO\n'\nBEGIN\n  EXECUTE ''CREATE OR REPLACE FUNCTION temporary_content_lo_cleanup()\n    RETURNS trigger\n    LANGUAGE plpgsql\n  AS ''''\n  BEGIN\n    IF OLD.content IS NOT NULL THEN\n      PERFORM lo_unlink(OLD.content);\n    END IF;\n    RETURN OLD;\n  END;\n  '''''';\nEND\n';\n\nDROP TRIGGER IF EXISTS trg_temporary_content_lo_cleanup ON temporary_content;\n\nCREATE TRIGGER trg_temporary_content_lo_cleanup\n    AFTER DELETE ON temporary_content\n    FOR EACH ROW\n    EXECUTE FUNCTION temporary_content_lo_cleanup();\n\nCREATE TABLE tenant_resource (\n  id INT8 NOT NULL,\n  name VARCHAR(255) NOT NULL,\n  type VARCHAR(16) NOT NULL,\n  content BYTEA NOT NULL,\n  lastUpdatedBy INT8 NOT NULL,\n  lastUpdateDate INT8,\n  state VARCHAR(50) NOT NULL,\n  CONSTRAINT pk_tenant_resource PRIMARY KEY (id),\n  CONSTRAINT uk_tenant_resource_name_type UNIQUE (name, type)\n);\n\nCREATE TABLE bpm_failure (\n  id INT8 NOT NULL,\n  processDefinitionId INT8 NOT NULL,\n  processInstanceId INT8 NOT NULL,\n  rootProcessInstanceId INT8,\n  flowNodeInstanceId INT8,\n  scope VARCHAR(255),\n  context VARCHAR(1024),\n  errorMessage VARCHAR(1024),\n  stackTrace TEXT,\n  failureDate INT8 NOT NULL,\n  CONSTRAINT pk_bpm_failure PRIMARY KEY (id)\n);\nCREATE INDEX idx_bpm_failure_flownodeinstanceid ON bpm_failure (flowNodeInstanceId);\nCREATE INDEX idx_bpm_failure_processinstanceid ON bpm_failure (processInstanceId);\nCREATE INDEX idx_bpm_failure_rootprocessinstanceid ON bpm_failure (rootProcessInstanceId);\nCREATE INDEX idx_bpm_failure_processdefinitionid ON bpm_failure (processDefinitionId);\n\nCREATE TABLE arch_bpm_failure (\n  id INT8 NOT NULL,\n  processDefinitionId INT8 NOT NULL,\n  processInstanceId INT8 NOT NULL,\n  rootProcessInstanceId INT8,\n  flowNodeInstanceId INT8,\n  scope VARCHAR(255),\n  context VARCHAR(1024),\n  errorMessage VARCHAR(1024),\n  stackTrace TEXT,\n  failureDate INT8 NOT NULL,\n  archiveDate INT8 NOT NULL,\n  sourceObjectId INT8 NOT NULL,\n  CONSTRAINT pk_arch_bpm_failure PRIMARY KEY (id)\n);\nCREATE INDEX idx_arch_bpm_failure_flownodeinstanceid ON arch_bpm_failure (flowNodeInstanceId);\nCREATE INDEX idx_arch_bpm_failure_processinstanceid ON arch_bpm_failure (processInstanceId);\nCREATE INDEX idx_arch_bpm_failure_rootprocessinstanceid ON arch_bpm_failure (rootProcessInstanceId);\nCREATE INDEX idx_arch_bpm_failure_processdefinitionid ON arch_bpm_failure (processDefinitionId);\n\nCREATE TABLE data_retention_config (\n    id                  INT8 NOT NULL,\n    data_classname      VARCHAR(255) NOT NULL,\n    reference_date      VARCHAR(20) NOT NULL,\n    retention_days      INT NOT NULL,\n    created_at          INT8 NOT NULL,\n    updated_at          INT8 NOT NULL,\n    CONSTRAINT pk_data_retention_config PRIMARY KEY (id),\n    CONSTRAINT uk_data_retention_config_data_classname UNIQUE (data_classname)\n);\n\nCREATE TABLE data_retention_bdm_tracking (\n    id                  INT8 NOT NULL,\n    data_id             INT8 NOT NULL,\n    data_classname      VARCHAR(255) NOT NULL,\n    created_at          INT8 NOT NULL,\n    last_modified_at    INT8 NOT NULL,\n    CONSTRAINT pk_data_retention_bdm_tracking PRIMARY KEY (id),\n    CONSTRAINT uk_data_retention_bdm_tracking_data_id_data_classname UNIQUE (data_id, data_classname)\n);\nCREATE INDEX idx_data_retention_bdm_tracking_data_classname ON data_retention_bdm_tracking (data_classname);\n\nCREATE TABLE delegation_rule (\n    id                  INT8 NOT NULL,\n    delegator_id        INT8 NOT NULL,\n    delegate_id         INT8 NOT NULL,\n    start_date          INT8 NOT NULL,\n    end_date            INT8 NOT NULL,\n    last_updated_by     INT8 NOT NULL,\n    last_updated_at     INT8 NOT NULL,\n    CONSTRAINT pk_delegation_rule PRIMARY KEY (id),\n    CONSTRAINT uk_delegation_rule_delegator_id UNIQUE (delegator_id)\n);\n\nCREATE TABLE delegation_rule_process (\n    id                  INT8 NOT NULL,\n    delegation_rule_id  INT8 NOT NULL,\n    process_name        VARCHAR(255) NOT NULL,\n    CONSTRAINT pk_delegation_rule_process PRIMARY KEY (id),\n    CONSTRAINT uk_delegation_rule_process_delegation_rule_id_process_name UNIQUE (delegation_rule_id, process_name)\n);\nALTER TABLE delegation_rule_process ADD CONSTRAINT fk_delegation_rule_process_delegation_rule_id FOREIGN KEY (delegation_rule_id) REFERENCES delegation_rule(id) ON DELETE CASCADE;\n"
  },
  {
    "path": "platform/platform-resources/src/main/resources/sql/postgres/dropQuartzTables.sql",
    "content": "DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;\nDROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;\nDROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;\nDROP TABLE IF EXISTS QRTZ_LOCKS;\nDROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;\nDROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;\nDROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;\nDROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;\nDROP TABLE IF EXISTS QRTZ_TRIGGERS;\nDROP TABLE IF EXISTS QRTZ_JOB_DETAILS;\nDROP TABLE IF EXISTS QRTZ_CALENDARS;\n"
  },
  {
    "path": "platform/platform-resources/src/main/resources/sql/postgres/dropTables.sql",
    "content": "DROP TABLE IF EXISTS configuration;\nDROP TABLE IF EXISTS arch_contract_data;\nDROP TABLE IF EXISTS contract_data;\nDROP TABLE IF EXISTS actormember;\nDROP TABLE IF EXISTS actor;\nDROP TABLE IF EXISTS processcategorymapping;\nDROP TABLE IF EXISTS category;\nDROP TABLE IF EXISTS arch_process_comment;\nDROP TABLE IF EXISTS process_comment;\nDROP TABLE IF EXISTS process_definition;\nDROP TABLE IF EXISTS arch_document_mapping CASCADE;\nDROP TABLE IF EXISTS document_mapping;\nDROP TABLE IF EXISTS document;\nDROP TABLE IF EXISTS arch_flownode_instance;\nDROP TABLE IF EXISTS arch_process_instance;\nDROP TABLE IF EXISTS arch_connector_instance;\nDROP TABLE IF EXISTS arch_multi_biz_data;\nDROP TABLE IF EXISTS arch_ref_biz_data_inst;\nDROP TABLE IF EXISTS multi_biz_data;\nDROP TABLE IF EXISTS ref_biz_data_inst;\nDROP TABLE IF EXISTS pending_mapping;\nDROP TABLE IF EXISTS connector_instance;\nDROP TABLE IF EXISTS flownode_instance;\nDROP TABLE IF EXISTS process_instance;\nDROP TABLE IF EXISTS event_trigger_instance;\nDROP TABLE IF EXISTS waiting_event;\nDROP TABLE IF EXISTS message_instance;\nDROP TABLE IF EXISTS processsupervisor;\nDROP TABLE IF EXISTS business_app_menu;\nDROP TABLE IF EXISTS business_app_page;\nDROP TABLE IF EXISTS business_app;\nDROP TABLE IF EXISTS command;\nDROP TABLE IF EXISTS arch_data_instance;\nDROP TABLE IF EXISTS data_instance;\nDROP TABLE IF EXISTS dependencymapping;\nDROP TABLE IF EXISTS dependency;\nDROP TABLE IF EXISTS pdependencymapping;\nDROP TABLE IF EXISTS pdependency;\nDROP TABLE IF EXISTS user_membership;\nDROP TABLE IF EXISTS custom_usr_inf_val;\nDROP TABLE IF EXISTS custom_usr_inf_def;\nDROP TABLE IF EXISTS user_contactinfo;\nDROP TABLE IF EXISTS user_login;\nDROP TABLE IF EXISTS user_;\nDROP TABLE IF EXISTS role;\nDROP TABLE IF EXISTS group_;\nDROP TABLE IF EXISTS queriable_log;\nDROP TABLE IF EXISTS page;\nDROP TABLE IF EXISTS profilemember;\nDROP TABLE IF EXISTS profile;\nDROP TABLE IF EXISTS job_log;\nDROP TABLE IF EXISTS job_param;\nDROP TABLE IF EXISTS job_desc;\nDROP TABLE IF EXISTS sequence;\nDROP TABLE IF EXISTS tenant;\nDROP TABLE IF EXISTS platform;\nDROP TABLE IF EXISTS platformCommand;\nDROP TABLE IF EXISTS form_mapping;\nDROP TABLE IF EXISTS page_mapping;\nDROP TABLE IF EXISTS process_content;\nDROP TABLE IF EXISTS proc_parameter;\nDROP TABLE IF EXISTS bar_resource;\nDROP TABLE IF EXISTS temporary_content;\nDROP TABLE IF EXISTS tenant_resource;\nDROP TABLE IF EXISTS icon;\nDROP TABLE IF EXISTS arch_bpm_failure;\nDROP TABLE IF EXISTS bpm_failure;\nDROP TABLE IF EXISTS data_retention_config;\nDROP TABLE IF EXISTS data_retention_bdm_tracking;\nDROP TABLE IF EXISTS delegation_rule_process;\nDROP TABLE IF EXISTS delegation_rule;"
  },
  {
    "path": "platform/platform-resources/src/main/resources/sql/postgres/initTables.sql",
    "content": "INSERT INTO sequence VALUES (1, 1);\nINSERT INTO sequence VALUES (2, 1);\nINSERT INTO sequence VALUES (3, 1);\nINSERT INTO sequence VALUES (4, 1);\nINSERT INTO sequence VALUES (5, 1);\nINSERT INTO sequence VALUES (6, 1);\nINSERT INTO sequence VALUES (7, 1);\nINSERT INTO sequence VALUES (8, 1);\nINSERT INTO sequence VALUES (9, 1);\nINSERT INTO sequence VALUES(10, 1);\nINSERT INTO sequence VALUES(11, 1);\nINSERT INTO sequence VALUES(20, 1);\nINSERT INTO sequence VALUES(21, 1);\nINSERT INTO sequence VALUES(22, 1);\nINSERT INTO sequence VALUES(23, 1);\nINSERT INTO sequence VALUES(24, 1);\nINSERT INTO sequence VALUES(25, 1);\nINSERT INTO sequence VALUES(26, 1);\nINSERT INTO sequence VALUES(27, 1);\nINSERT INTO sequence VALUES(30, 1);\nINSERT INTO sequence VALUES(70, 1);\nINSERT INTO sequence VALUES(71, 1);\nINSERT INTO sequence VALUES(72, 1);\nINSERT INTO sequence VALUES(90, 1);\nINSERT INTO sequence VALUES(9990, 1);\nINSERT INTO sequence VALUES(9992, 1);\nINSERT INTO sequence VALUES(10000, 1);\nINSERT INTO sequence VALUES(10001, 1);\nINSERT INTO sequence VALUES(10010, 1);\nINSERT INTO sequence VALUES(10011, 1);\nINSERT INTO sequence VALUES(10012, 1);\nINSERT INTO sequence VALUES(10014, 1);\nINSERT INTO sequence VALUES(10015, 1);\nINSERT INTO sequence VALUES(10016, 1);\nINSERT INTO sequence VALUES(10017, 1);\nINSERT INTO sequence VALUES(10018, 1);\nINSERT INTO sequence VALUES(10020, 1);\nINSERT INTO sequence VALUES(10021, 1);\nINSERT INTO sequence VALUES(10030, 1);\nINSERT INTO sequence VALUES(10031, 1);\nINSERT INTO sequence VALUES(10040, 1);\nINSERT INTO sequence VALUES(20051, 1);\nINSERT INTO sequence VALUES(10050, 1);\nINSERT INTO sequence VALUES(10060, 1);\nINSERT INTO sequence VALUES(10080, 1);\nINSERT INTO sequence VALUES(10090, 1);\nINSERT INTO sequence VALUES(10096, 1);\nINSERT INTO sequence VALUES(10120, 1);\nINSERT INTO sequence VALUES(10121, 1);\nINSERT INTO sequence VALUES(10200, 1);\nINSERT INTO sequence VALUES(10201, 1);\nINSERT INTO sequence VALUES(10202, 1);\nINSERT INTO sequence VALUES(10210, 1);\nINSERT INTO sequence VALUES(10220, 1);\nINSERT INTO sequence VALUES(10300, 1);\nINSERT INTO sequence VALUES(10310, 1);\nINSERT INTO sequence VALUES(10400, 1);\nINSERT INTO sequence VALUES(10500, 1);\nINSERT INTO sequence VALUES(10501, 1);\nINSERT INTO sequence VALUES(20010, 1);\nINSERT INTO sequence VALUES(20011, 1);\nINSERT INTO sequence VALUES(20013, 1);\nINSERT INTO sequence VALUES(20040, 1);\nINSERT INTO sequence VALUES(20050, 1);\nINSERT INTO sequence VALUES(20210, 1);\nINSERT INTO sequence VALUES(20220, 1);\nINSERT INTO sequence VALUES(20096, 1);\nINSERT INTO sequence VALUES(20097, 1);\nINSERT INTO sequence VALUES(20098, 1);\n"
  },
  {
    "path": "platform/platform-resources/src/main/resources/sql/postgres/preDropStructure.sql",
    "content": "-- ------------------------------------------------ Foreign Keys -----------------------------------------------\nALTER TABLE actormember DROP CONSTRAINT fk_actormember_actorid;\nALTER TABLE profilemember DROP CONSTRAINT fk_profilemember_profileid;\n\nALTER TABLE document_mapping DROP CONSTRAINT fk_document_mapping_documentid;\nALTER TABLE pending_mapping DROP CONSTRAINT fk_pending_mapping_activityid;\nALTER TABLE process_definition DROP CONSTRAINT fk_process_definition_content_id;\n\n-- business application\nALTER TABLE business_app_menu DROP CONSTRAINT fk_business_app_menu_applicationid;\nALTER TABLE business_app_menu DROP CONSTRAINT fk_business_app_menu_applicationpageid;\nALTER TABLE business_app_menu DROP CONSTRAINT fk_business_app_menu_parentid;\nALTER TABLE business_app_page DROP CONSTRAINT fk_business_app_page_applicationid;\nALTER TABLE business_app_page DROP CONSTRAINT fk_business_app_page_pageid;\nALTER TABLE business_app DROP CONSTRAINT fk_business_app_profileid;\nALTER TABLE business_app DROP CONSTRAINT fk_business_app_layoutid;\nALTER TABLE business_app DROP CONSTRAINT fk_business_app_themeid;\n\n\n-- delegation\nALTER TABLE delegation_rule_process DROP CONSTRAINT fk_delegation_rule_process_delegation_rule_id;\n\n--  ------------------------ Foreign Keys to disable if archiving is on another BD ------------------\nALTER TABLE arch_document_mapping DROP CONSTRAINT fk_arch_document_mapping_documentid;\n\n--  ------------------------ Temporary_content Trigger ------------------\nDROP TRIGGER IF EXISTS trg_temporary_content_lo_cleanup ON temporary_content;\nDROP FUNCTION IF EXISTS temporary_content_lo_cleanup();\n"
  },
  {
    "path": "platform/platform-resources/src/main/resources/tenant_engine/bonita-tenants-custom.xml",
    "content": "<beans xmlns=\"http://www.springframework.org/schema/beans\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd\">\n\n    <!-- ADD ANY BEAN DEFINITION YOU WANT TO BE AVAILABLE TO THE TENANT ACCESSOR -->\n\n    <!--\n    <bean id=\"flushEventListeners\" parent=\"defaultFlushEventListeners\" class=\"org.springframework.beans.factory.config.ListFactoryBean\">\n        <property name=\"sourceList\">\n            <list merge=\"true\">\n            </list>\n        </property>\n    </bean>\n    -->\n\n    <!--\n    <bean id=\"activatedRecords\" parent=\"defaultActivatedRecords\" class=\"org.springframework.beans.factory.config.ListFactoryBean\">\n        <property name=\"sourceList\">\n            <list merge=\"true\">\n            </list>\n        </property>\n    </bean>\n    -->\n\n\n    <!-- custom authorization rule mapping must implement org.bonitasoft.engine.core.form.AuthorizationRuleMapping -->\n    <!--\n    <bean id=\"customAuthorizationRuleMapping\"\n          class=\"org.bonitasoft.engine.core.form.impl.custom.CustomAuthorizationRuleMappingImpl\"/>\n    -->\n\n    <!-- custom authorization rules must implements org.bonitasoft.engine.page.AuthorizationRule -->\n    <!--\n    <bean id=\"customIsProcessInitiatorRule\" class=\"org.bonitasoft.engine.core.form.impl.custom.CustomIsProcessInitiatorRule\">\n        <constructor-arg name=\"processInstanceService\" ref=\"processInstanceService\"/>\n        <constructor-arg name=\"sessionService\" ref=\"sessionService\"/>\n    </bean>\n    -->\n\n\n    <!-- Authorization rule that also grants access to case overview page for a manager of a user involved in the case -->\n    <!--\n    <bean id=\"managerInvolvedAuthorizationRuleMappingImpl\"\n          class=\"org.bonitasoft.engine.core.form.impl.ManagerInvolvedAuthorizationRuleMappingImpl\"/>\n\n    <bean id=\"isManagerOfUserInvolvedInProcessInstanceRule\" class=\"org.bonitasoft.engine.page.IsManagerOfUserInvolvedInProcessInstanceRule\">\n        <constructor-arg name=\"sessionService\" ref=\"sessionService\"/>\n        <constructor-arg name=\"processInvolvementDelegate\" ref=\"processInvolvementDelegate\"/>\n    </bean>\n    -->\n\n\n    <!-- Schema Manager that avoids to update the database schema when updating BDM -->\n    <!--\n    <bean id=\"schemaManager\" class=\"org.bonitasoft.engine.business.data.impl.SchemaManagerReadOnly\">\n    </bean>\n        -->\n</beans>\n"
  },
  {
    "path": "platform/platform-resources/src/main/resources/tenant_portal/compound-permissions-mapping-custom.properties",
    "content": "#\n# List of permissions used for custom pages.\n# Default Bonita Portal permissions can be found in file 'compound-permissions-mapping.properties'\n#\n# Add you custom permissions below:\n#\n"
  },
  {
    "path": "platform/platform-resources/src/main/resources/tenant_portal/compound-permissions-mapping-internal.properties",
    "content": "# internal use only. Do not modify manually"
  },
  {
    "path": "platform/platform-resources/src/main/resources/tenant_portal/compound-permissions-mapping.properties",
    "content": "##\n# List of permissions used for each pages.\n# The content of this file is handled by the portal, it should not be modified\n##\ncustompage_layoutBonita=[application_visualization, avatars, organization_visualization, profile_visualization, tenant_platform_visualization]\ncustompage_layoutWithoutMenuBonita=[application_visualization]\ncustompage_tasklist=[flownode_visualization, bdm_visualization, task_visualization, form_visualization, case_visualization, document_visualization, form_file_upload, task_management, process_visualization, download_document, tenant_platform_visualization, process_comment, avatars]\ncustompage_userCaseListBonita=[case_visualization, process_visualization, tenant_platform_visualization]\ncustompage_userCaseDetailsBonita=[flownode_visualization, task_visualization, bdm_visualization, document_visualization, case_visualization, tenant_platform_visualization, download_document, process_comment]\ncustompage_processlistBonita=[process_categories_visualization, form_visualization, form_file_upload, case_start, process_visualization, download_document, tenant_platform_visualization]\ncustompage_adminMonitoringBonita=[organization_visualization, profile_visualization, case_visualization, process_visualization, tenant_platform_visualization]\ncustompage_adminProcessListBonita=[process_deploy, process_management, connector_management, process_visualization, process_enablement, process_categories_management, process_actor_mapping_management]\ncustompage_adminProcessDetailsBonita=[connector_visualization, process_manager_visualization, activity_visualization, process_manager_management, case_visualization, process_deploy, form_file_upload, case_start, process_visualization, process_enablement, process_categories_management, tenant_platform_visualization, process_categories_visualization, flownode_visualization, organization_visualization, form_visualization, form_management, process_management, connector_management, process_actor_mapping_management]\ncustompage_adminProcessVisuBonita=[process_management, process_visualization]\ncustompage_adminCaseListBonita=[case_visualization, case_delete, process_visualization]\ncustompage_adminCaseDetailsBonita=[flownode_visualization, task_visualization, bdm_visualization, document_visualization, case_visualization, case_management, process_visualization, download_document, tenant_platform_visualization, process_comment]\ncustompage_adminCaseVisuBonita=[case_visualization, process_visualization]\ncustompage_adminTaskListBonita=[flownode_visualization, process_visualization, task_visualization]\ncustompage_adminTaskDetailsBonita=[connector_visualization, task_visualization, case_visualization, form_file_upload, tenant_platform_visualization, flownode_visualization, bdm_visualization, organization_visualization, form_visualization, task_management, flownode_management, download_document, process_comment]\ncustompage_adminUserListBonita=[organization_management, organization_visualization]\ncustompage_adminUserDetailsBonita=[organization_visualization, organization_management, avatars]\ncustompage_adminGroupListBonita=[organization_visualization, organization_management]\ncustompage_adminRoleListBonita=[organization_visualization, organization_management]\ncustompage_adminInstallExportOrganizationBonita=[form_file_upload, organization_management, tenant_platform_visualization]\ncustompage_adminProfileListBonita=[organization_visualization, profile_management, profile_member_management, profile_member_visualization, profile_visualization, tenant_platform_visualization]\ncustompage_adminBDMBonita=[bdm_access_control, bdm_management, bdm_visualization, organization_visualization, tenant_platform_visualization]\ncustompage_adminResourceListBonita=[application_visualization, form_visualization, page_management, profile_management, profile_visualization]\ncustompage_adminApplicationListBonita=[application_management, application_visualization, organization_visualization, profile_visualization, tenant_platform_visualization]\ncustompage_adminApplicationDetailsBonita=[application_management, application_visualization, organization_visualization, page_management, profile_visualization, tenant_platform_visualization]\ncustompage_adminLicenseBonita=[tenant_platform_visualization]\ncustompage_adminDataRetentionBonita=[data_retention_management, tenant_platform_visualization]\ncustompage_tenantStatusBonita=[license, tenant_platform_management, tenant_platform_visualization]\ncustompage_applicationDirectoryBonita=[application_visualization, organization_visualization, tenant_platform_visualization]\ncustompage_home=[]\ncustompage_error404Bonita=[]\ncustompage_error500Bonita=[]\n"
  },
  {
    "path": "platform/platform-resources/src/main/resources/tenant_portal/console-config.properties",
    "content": "#This value represents MB, e.g 25 means 25MB.\nform.attachment.max.size                25\n#This value represents KB, e.g 100 means 100KB.\nimage.upload.max.size                   100\n#Set this value to true to reload the Index.groovy class every time a custom page is displayed\ncustom.page.debug                       false\n#Min time (in millis) between two database check of custom pages and REST API extensions last update date\n#if the page has been modified in the database it is retrieved locally, but this property is used to avoid doing the database check at each HTTP request\n#the value can be increased to improve page or REST API extension loading/response time if live update is not used\ncustom.page.lastupdate.database.check.interval.milliseconds 3000\n#Name of attribute attached to the request to indicate the request id\nreq.requestId.attributeName             track.requestId\n#Name of request header containing the request id when not already attached\nreq.requestId.headerName                X-Request-ID\n#Name of attribute attached to the request to indicate the correlation id\nreq.correlationId.attributeName         track.correlationId\n#Name of request header containing the correlation id when not already attached\nreq.correlationId.headerName            X-Correlation-ID"
  },
  {
    "path": "platform/platform-resources/src/main/resources/tenant_portal/custom-permissions-mapping.properties",
    "content": "##\n# Custom permissions file\n#\n# custom permissions can be defined like this\n# <type>|<identifier>=[<permission list>]\n#\n# type can be 'profile' or 'user'\n# identifier is the username or the profile name. Special characters like white space must be replaced with their unicode value (For example \\u0020 for the white space)\n# possible values for permissions can be found in the resources-permissions-mapping.properties file\n#\n##\n# example: the profile User have now the permission Organization visualization\n#profile|User=[organization_visualization]\n#profile|Process\\u0020manager=[organization_visualization]\n#\n# example: the user having username john have now the permission Organization management and Organization visualization\n#user|john=[organization_management, organization_visualization]\n"
  },
  {
    "path": "platform/platform-resources/src/main/resources/tenant_portal/resources-permissions-mapping-custom.properties",
    "content": "##\n# Define which permissions are needed to access resources using the REST API\n# Resource permissions can be defined like this\n# <verb>|<api name>/<resource name>/<id>=[<resource permission list>]\n#\n# verb can be 'GET', 'POST', 'PUT' or 'DELETE'\n#\n##\n# example of a default resource permission:\n# GET|identity/user=[organization_visualization]\n# It means that in order to GET user information, the logged user needs to have the organization_visualization permission.\n#\n# example of a custom resource permission:\n# GET|identity/user/3=[my_custom_permission]\n# It means that in order to GET the information of the user with id 3, the logged user needs to have my_custom_permission.\n##\n# Default resource permissions can be found in file 'resources-permissions-mapping.properties'\n#\n# Add you custom resource permissions below:\n"
  },
  {
    "path": "platform/platform-resources/src/main/resources/tenant_portal/resources-permissions-mapping-internal.properties",
    "content": "# internal use only. Do not modify manually"
  },
  {
    "path": "platform/platform-resources/src/main/resources/tenant_portal/resources-permissions-mapping.properties",
    "content": "##\n# Define which permissions allow to access a resources using the REST API\n# Resource permissions can be defined like this\n# <verb>|<api name>/<resource name>/<id>=[<resource permission list>]\n#\n# verb can be 'GET', 'POST', 'PUT' or 'DELETE'\n#\n##\n# example of a default resource permission:\n# GET|identity/user=[organization_visualization]\n# It means that in order to GET user information, the logged user needs to have the organization_visualization permission.\n#\n# example of a custom resource permission:\n# GET|identity/user/3=[my_custom_permission]\n# It means that in order to GET the information of the user with id 3, the logged user needs to have my_custom_permission.\n##\n\n# Identity resources\nGET|identity/user=[organization_visualization]\nPOST|identity/user=[organization_management]\nPUT|identity/user=[organization_management]\nDELETE|identity/user=[organization_management]\nGET|identity/personalcontactdata=[organization_visualization]\nPOST|identity/personalcontactdata=[organization_management]\nPUT|identity/personalcontactdata=[organization_management]\nGET|identity/professionalcontactdata=[organization_visualization]\nPOST|identity/professionalcontactdata=[organization_management]\nPUT|identity/professionalcontactdata=[organization_management]\nGET|identity/role=[organization_visualization]\nPOST|identity/role=[organization_management]\nPUT|identity/role=[organization_management]\nDELETE|identity/role=[organization_management]\nGET|identity/group=[organization_visualization]\nPOST|identity/group=[organization_management]\nPUT|identity/group=[organization_management]\nDELETE|identity/group=[organization_management]\nGET|identity/membership=[organization_visualization]\nPOST|identity/membership=[organization_management]\nPUT|identity/membership=[organization_management]\nDELETE|identity/membership=[organization_management]\nGET|customuserinfo/user=[organization_visualization]\nGET|customuserinfo/definition=[organization_visualization]\nPOST|customuserinfo/definition=[organization_management]\nDELETE|customuserinfo/definition=[organization_management]\nGET|customuserinfo/value=[organization_visualization]\nPUT|customuserinfo/value=[organization_management]\n\n# BPM resources\nGET|bpm/process=[process_visualization]\nPOST|bpm/process=[process_deploy]\nPUT|bpm/process=[process_management, process_enablement]\nDELETE|bpm/process=[process_deploy]\nGET|bpm/processInfo=[process_management]\nGET|bpm/process/*/contract=[process_visualization]\nPOST|bpm/process/*/instantiation=[case_start]\nGET|bpm/processConnector=[process_management, connector_visualization]\nPUT|bpm/processConnector=[process_management, connector_management]\nGET|bpm/processConnectorDependency=[process_management, connector_visualization]\nPOST|bpm/processCategory=[process_management, process_categories_management]\nDELETE|bpm/processCategory=[process_management, process_categories_management]\nGET|bpm/processParameter=[process_management]\nPUT|bpm/processParameter=[process_management]\nGET|bpm/processSupervisor=[process_manager_visualization]\nPOST|bpm/processSupervisor=[process_manager_management]\nDELETE|bpm/processSupervisor=[process_manager_management]\nGET|bpm/actor=[process_visualization]\nGET|bpm/actorMember=[process_visualization, process_manager_visualization]\nPOST|bpm/actorMember=[process_management, process_manager_management]\nPUT|bpm/actorMember=[process_management, process_manager_management]\nDELETE|bpm/actorMember=[process_management, process_manager_management]\nGET|bpm/category=[process_categories_visualization]\nPOST|bpm/category=[process_categories_management]\nPUT|bpm/category=[process_categories_management]\nDELETE|bpm/category=[process_categories_management]\nGET|bpm/processResolutionProblem=[activity_visualization, flownode_visualization]\nGET|bpm/case=[case_visualization]\nPUT|bpm/case=[case_management]\nPOST|bpm/case=[case_start]\nDELETE|bpm/case=[case_delete]\nGET|bpm/case/*/context=[case_visualization]\nGET|bpm/caseInfo=[case_visualization]\nGET|bpm/comment=[process_comment]\nPOST|bpm/comment=[process_comment]\nGET|bpm/archivedComment=[process_comment]\nGET|bpm/archivedCase=[case_visualization]\nDELETE|bpm/archivedCase=[case_delete]\nGET|bpm/archivedCase/*/context=[case_visualization]\nGET|bpm/caseVariable=[case_visualization]\nPUT|bpm/caseVariable=[case_management]\nGET|bpm/archivedCaseVariable=[case_visualization]\nGET|bpm/caseDocument=[document_visualization, case_visualization]\nPOST|bpm/caseDocument=[document_management, case_management]\nDELETE|bpm/caseDocument=[document_management, case_management]\nPUT|bpm/caseDocument=[document_management, case_management]\nGET|bpm/flowNode=[flownode_visualization]\nPUT|bpm/flowNode=[flownode_management]\nGET|bpm/activity=[flownode_visualization]\nPUT|bpm/activity=[flownode_management]\nPUT|bpm/activityReplay=[flownode_management]\nGET|bpm/task=[flownode_visualization]\nPUT|bpm/task=[flownode_management]\nGET|bpm/humanTask=[task_visualization]\nPUT|bpm/humanTask=[task_management]\nGET|bpm/userTask=[task_visualization]\nPUT|bpm/userTask=[task_management]\nPOST|bpm/userTask=[task_management]\nGET|bpm/userTask/*/contract=[task_visualization]\nGET|bpm/userTask/*/context=[task_visualization]\nPOST|bpm/userTask/*/execution=[task_management]\nGET|bpm/manualTask=[task_visualization]\nPOST|bpm/manualTask=[task_management]\nPUT|bpm/manualTask=[task_management]\nGET|bpm/activityVariable=[flownode_visualization]\nGET|bpm/archivedActivityVariable=[flownode_visualization]\nGET|bpm/connectorInstance=[connector_visualization]\nPUT|bpm/connectorInstance=[connector_management]\nGET|bpm/archivedFlowNode=[flownode_visualization]\nGET|bpm/archivedActivity=[flownode_visualization]\nGET|bpm/archivedTask=[flownode_visualization]\nGET|bpm/archivedHumanTask=[task_visualization]\nGET|bpm/archivedUserTask=[task_visualization]\nGET|bpm/archivedUserTask/*/context=[task_visualization]\nGET|bpm/archivedManualTask=[task_visualization]\nGET|bpm/archivedConnectorInstance=[connector_visualization]\nGET|bpm/document=[document_visualization, case_visualization]\nPOST|bpm/document=[document_management, case_management]\nPUT|bpm/document=[document_management, case_management]\nDELETE|bpm/document=[document_management, case_management]\nGET|bpm/archiveddocument=[document_visualization, case_visualization]\nGET|bpm/archivedCaseDocument=[document_visualization, case_visualization]\nDELETE|bpm/archivedCaseDocument=[document_management, case_management]\nGET|bpm/command=[command_visualization]\nPOST|bpm/command=[command_management]\nPUT|bpm/command=[command_management]\nDELETE|bpm/command=[command_management]\nGET|bpm/connectorFailure=[connector_visualization]\nGET|bpm/timerEventTrigger=[flownode_visualization]\nPUT|bpm/timerEventTrigger=[flownode_management]\nGET|bpm/diagram=[process_visualization]\nPOST|bpm/message=[flownode_management]\nPOST|bpm/signal=[flownode_management]\nGET|bpm/failure=[flownode_management, case_management]\nGET|bpm/archivedFailure=[flownode_management, case_management]\n\n# Portal resources\nGET|portal/profile=[profile_visualization]\nPOST|portal/profile=[profile_management]\nPUT|portal/profile=[profile_management]\nDELETE|portal/profile=[profile_management]\nGET|portal/page=[profile_visualization, page_management]\nPOST|portal/page=[profile_management, page_management]\nPUT|portal/page=[profile_management, page_management]\nDELETE|portal/page=[profile_management, page_management]\nGET|portal/profileMember=[profile_member_visualization]\nPOST|portal/profileMember=[profile_member_management]\nDELETE|portal/profileMember=[profile_member_management]\n\n# Platform resources\nGET|system/session=[tenant_platform_visualization]\nGET|system/log=[tenant_platform_visualization]\nGET|system/maintenance=[tenant_platform_visualization]\nPUT|system/maintenance=[tenant_platform_management]\nGET|system/feature=[tenant_platform_visualization]\nGET|system/license=[license]\nGET|system/monitoring=[tenant_platform_visualization]\nGET|system/i18nlocale=[tenant_platform_visualization]\nGET|system/i18ntranslation=[tenant_platform_visualization]\nGET|platform/platform=[tenant_platform_visualization]\nPOST|platform/platform=[tenant_platform_management]\nPUT|platform/platform=[tenant_platform_management]\nDELETE|platform/platform=[tenant_platform_management]\nGET|platform/jvmDynamic=[tenant_platform_visualization]\nGET|platform/jvmStatic=[tenant_platform_visualization]\nGET|platform/systemProperty=[tenant_platform_visualization]\nGET|platform/license=[platform_management]\nPOST|tenant/bdm=[bdm_management]\nGET|tenant/bdm=[bdm_management]\nGET|system/information=[tenant_platform_visualization]\n\n# Living apps\nGET|living/application=[application_visualization]\nPOST|living/application=[application_management]\nPUT|living/application=[application_management]\nDELETE|living/application=[application_management]\nGET|living/application-page=[application_visualization]\nPOST|living/application-page=[application_management]\nPUT|living/application-page=[application_management]\nDELETE|living/application-page=[application_management]\nGET|living/application-menu=[application_visualization]\nPOST|living/application-menu=[application_management]\nPUT|living/application-menu=[application_management]\nDELETE|living/application-menu=[application_management]\n\n# BDM resources\nGET|bdm/businessData=[bdm_visualization]\nPOST|bdm/businessData=[bdm_management]\nDELETE|bdm/businessData=[bdm_management]\nPUT|bdm/businessData=[bdm_management]\nGET|bdm/businessDataReference=[bdm_visualization]\nGET|bdm/businessDataQuery=[bdm_visualization]\n\n# BDM Access control resources\nGET|accessControl/bdm=[bdm_access_control]\nDELETE|accessControl/bdm=[bdm_access_control]\n\n# Retention resources\nGET|retention/object=[data_retention_management]\nGET|retention/schedule=[data_retention_management]\nPOST|retention/rule=[data_retention_management]\nPUT|retention/rule=[data_retention_management]\nDELETE|retention/rule=[data_retention_management]\n\n# Delegation resources\nGET|delegation/rule=[task_delegation_management]\nPOST|delegation/rule=[task_delegation_management]\nPUT|delegation/rule=[task_delegation_management]\nDELETE|delegation/rule=[task_delegation_management]\nGET|delegation/task=[task_delegation_visualization]\n\n# Form resources\nGET|form/mapping=[form_visualization]\nPUT|form/mapping=[form_management]\n\n# Servlets\nPOST|API/formFileUpload=[form_file_upload]\nPOST|portal/custom-page/API/formFileUpload=[form_file_upload]\nPOST|API/imageUpload=[organization_management]\nPOST|API/pageUpload=[profile_management]\nPOST|API/processUpload=[process_deploy]\nPOST|API/profilesUpload=[profile_management]\nPOST|portal/fileUpload=[form_file_upload]\nPOST|portal/processUpload=[process_deploy]\nPOST|portal/organizationUpload=[organization_management]\nPOST|portal/actorsUpload=[process_management]\nPOST|portal/profilesUpload=[profile_management]\nPOST|portal/applicationsUpload=[application_management]\nPOST|portal/bdmUpload=[bdm_management]\nPOST|portal/bdmAccessControlUpload=[bdm_access_control]\nPOST|portal/connectorImplementation=[process_management]\nPOST|portal/pageUpload=[profile_management]\nPOST|portal/resourceUpload=[tenant_platform_management]\nPOST|portal/imageUpload=[organization_management]\nGET|API/avatars=[avatars]\nGET|portal/custom-page/API/avatars=[avatars]\nDELETE|API/avatars=[organization_management]\nDELETE|portal/custom-page/API/avatars=[organization_management]\nGET|API/documentDownload=[download_document]\nGET|portal/custom-page/API/documentDownload=[download_document]\nGET|portal/documentDownload=[download_document]\nGET|API/formsDocumentImage=[download_document]\nGET|portal/custom-page/API/formsDocumentImage=[download_document]\nGET|portal/formsDocumentImage=[download_document]\nGET|portal/custom-page/API/formsDocumentDownload=[download_document]\nGET|portal/formsDocumentDownload=[download_document]\nGET|portal/exportOrganization=[organization_management]\nGET|API/exportOrganization=[organization_management]\nGET|portal/custom-page/API/exportOrganization=[organization_management]\nGET|portal/pageDownload=[profile_management]\nGET|API/pageDownload=[profile_management]\nGET|portal/exportProfiles=[profile_management]\nGET|API/exportProfiles=[profile_management]\nGET|portal/exportAccessControl=[bdm_access_control]\nGET|API/applicationIcon=[application_visualization]\nDELETE|API/applicationIcon=[application_management]\n# Deprecated\nGET|portal/downloadDocument=[download_document]\nGET|portal/custom-page/API/downloadDocument=[download_document]\n\n# Services\nPOST|application/import=[application_management]\nPOST|organization/import=[organization_management]\nPOST|bpm/process/importActors=[process_actor_mapping_management]\nPOST|profile/import=[profile_management]\nPOST|bdmAccessControl/install=[bdm_access_control]\nPOST|bdmAccessControl/validation=[bdm_access_control]\n\n# api extension examples\nGET|extension/demo/getExample=[demoPermission]\nGET|extension/demo/headerExample=[demoPermission]\nGET|extension/demo/logExample=[demoPermission]\nGET|extension/demo/soapExample=[demoPermission]\nGET|extension/demo/xmlExample=[demoPermission]\nPOST|extension/demo/postExample=[demoPermission]\n"
  },
  {
    "path": "platform/platform-resources/src/main/resources/tenant_portal/security-config.properties",
    "content": "#It declares a Password Validator class, the default value can be changed according to your needs\nsecurity.password.validator org.bonitasoft.web.rest.server.api.organization.password.validator.DefaultPasswordValidator\n#Setting this value to false will deactivate the permissions checks on the REST API\nsecurity.rest.api.authorizations.check.enabled true\n"
  },
  {
    "path": "platform/platform-resources/src/test/java/org/bonitasoft/platform/configuration/impl/BonitaConfigurationRowMapperTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.impl;\n\nimport static org.bonitasoft.platform.configuration.impl.ConfigurationFields.RESOURCE_CONTENT;\nimport static org.bonitasoft.platform.configuration.impl.ConfigurationFields.RESOURCE_NAME;\nimport static org.bonitasoft.platform.configuration.model.BonitaConfigurationAssert.assertThat;\n\nimport java.sql.ResultSet;\n\nimport org.bonitasoft.platform.configuration.model.BonitaConfiguration;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.Mockito;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Laurent Leseigneur\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class BonitaConfigurationRowMapperTest {\n\n    @Mock\n    private ResultSet rs;\n\n    @Before\n    public void setup() throws Exception {\n        Mockito.doReturn(\"my resource\").when(rs).getString(RESOURCE_NAME);\n        Mockito.doReturn(\"my content\".getBytes()).when(rs).getBytes(RESOURCE_CONTENT);\n    }\n\n    @Test\n    public void testMapRow() throws Exception {\n        //given\n        BonitaConfigurationRowMapper bonitaConfigurationRowMapper = new BonitaConfigurationRowMapper();\n\n        //when\n        final BonitaConfiguration bonitaConfiguration = bonitaConfigurationRowMapper.mapRow(rs, 5);\n\n        //then\n        assertThat(bonitaConfiguration)\n                .hasResourceName(\"my resource\")\n                .hasResourceContent(\"my content\".getBytes());\n    }\n}\n"
  },
  {
    "path": "platform/platform-resources/src/test/java/org/bonitasoft/platform/configuration/impl/ConfigurationServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.impl;\n\nimport static org.mockito.Mockito.*;\n\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.assertj.core.api.Assertions;\nimport org.bonitasoft.platform.configuration.model.FullBonitaConfiguration;\nimport org.bonitasoft.platform.configuration.type.ConfigurationType;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.rules.TemporaryFolder;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentMatchers;\nimport org.mockito.InjectMocks;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author laurent Leseigneur\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class ConfigurationServiceImplTest {\n\n    @Rule\n    public TemporaryFolder temporaryFolder = new TemporaryFolder();\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @InjectMocks\n    @Spy\n    ConfigurationServiceImpl configurationService;\n\n    @Test\n    public void getPlatformPortalConf_should_call_query_for_PLATFORM_PORTAL_type() {\n        doReturn(Collections.EMPTY_LIST).when(configurationService)\n                .getBonitaConfigurations(ArgumentMatchers.any(ConfigurationType.class));\n\n        configurationService.getPlatformPortalConf();\n\n        verify(configurationService).getBonitaConfigurations(ConfigurationType.PLATFORM_PORTAL);\n    }\n\n    @Test\n    public void getPlatformEngineConf_should_call_query_for_PLATFORM_ENGINE_type() {\n        doReturn(Collections.EMPTY_LIST).when(configurationService)\n                .getBonitaConfigurations(ArgumentMatchers.any(ConfigurationType.class));\n\n        configurationService.getPlatformEngineConf();\n\n        verify(configurationService).getBonitaConfigurations(ConfigurationType.PLATFORM_ENGINE);\n    }\n\n    @Test\n    public void getLicenses_should_call_query_for_LICENSES_type() {\n        doReturn(Collections.EMPTY_LIST).when(configurationService)\n                .getBonitaConfigurations(ArgumentMatchers.any(ConfigurationType.class));\n\n        configurationService.getLicenses();\n\n        verify(configurationService).getBonitaConfigurations(ConfigurationType.LICENSES);\n    }\n\n    @Test\n    public void should_write_file_within_sub_folder() throws Exception {\n        //given\n        final File configFolder = temporaryFolder.newFolder(\"conf\");\n        final File licFolder = temporaryFolder.newFolder(\"lic\");\n        List<FullBonitaConfiguration> confs = new ArrayList<>();\n        confs.add(new FullBonitaConfiguration(\"conf1.properties\", \"content 1\".getBytes(), \"PLATFORM_TYPE\"));\n        confs.add(new FullBonitaConfiguration(\"conf2.properties\", \"content 2\".getBytes(), \"TENANT_TYPE\"));\n\n        doReturn(confs).when(configurationService).getAllConfiguration();\n        doCallRealMethod().when(configurationService).writeAllConfigurationToFolder(configFolder, licFolder);\n\n        //when\n        configurationService.writeAllConfigurationToFolder(configFolder, licFolder);\n\n        // then\n        Assertions.assertThat(configFolder.toPath().resolve(\"platform_type\").resolve(\"conf1.properties\").toFile())\n                .as(\"should lowercase configuration type\").exists();\n        Assertions\n                .assertThat(configFolder.toPath().resolve(\"tenant_type\").resolve(\"conf2.properties\").toFile())\n                .as(\"should create 'tenant_type' sub folder\").exists();\n\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-resources/src/test/java/org/bonitasoft/platform/configuration/impl/FolderResolverTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.impl;\n\nimport java.nio.file.Path;\n\nimport org.assertj.core.api.Assertions;\nimport org.bonitasoft.platform.configuration.model.FullBonitaConfiguration;\nimport org.bonitasoft.platform.configuration.type.ConfigurationType;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.TemporaryFolder;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class FolderResolverTest {\n\n    @Rule\n    public TemporaryFolder temporaryFolder = new TemporaryFolder();\n\n    @Test\n    public void should_resolve_license_folder() throws Exception {\n        //given\n        Path confFolder = temporaryFolder.newFolder(\"configuration\").toPath();\n        Path LicFolder = temporaryFolder.newFolder(\"licenses\").toPath();\n        FolderResolver folderResolver = new FolderResolver(confFolder, LicFolder);\n\n        FullBonitaConfiguration fullBonitaConfiguration = new FullBonitaConfiguration(\"licence1.lic\",\n                \"license content\".getBytes(),\n                ConfigurationType.LICENSES.name());\n\n        //then\n        Assertions.assertThat(folderResolver.getFolder(fullBonitaConfiguration)).isEqualTo(LicFolder.toFile());\n\n    }\n\n    @Test\n    public void should_resolve_non_tenant_folder() throws Exception {\n        //given\n        Path confFolder = temporaryFolder.newFolder(\"configuration\").toPath();\n        Path LicFolder = temporaryFolder.newFolder(\"licenses\").toPath();\n        FolderResolver folderResolver = new FolderResolver(confFolder, LicFolder);\n\n        FullBonitaConfiguration fullBonitaConfiguration = new FullBonitaConfiguration(\"conf.properties\",\n                \"key=value\".getBytes(),\n                ConfigurationType.PLATFORM_ENGINE.name());\n\n        //then\n        Assertions.assertThat(folderResolver.getFolder(fullBonitaConfiguration))\n                .isEqualTo(confFolder.resolve(\"platform_engine\").toFile());\n\n    }\n\n    @Test\n    public void should_resolve_tenant_folder() throws Exception {\n        //given\n        Path confFolder = temporaryFolder.newFolder(\"configuration\").toPath();\n        Path LicFolder = temporaryFolder.newFolder(\"licenses\").toPath();\n        FolderResolver folderResolver = new FolderResolver(confFolder, LicFolder);\n\n        FullBonitaConfiguration fullBonitaConfiguration = new FullBonitaConfiguration(\"conf.properties\",\n                \"key=value\".getBytes(),\n                ConfigurationType.TENANT_PORTAL.name());\n\n        //then\n        Assertions.assertThat(folderResolver.getFolder(fullBonitaConfiguration))\n                .isEqualTo(confFolder.resolve(\"tenant_portal\").toFile());\n\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-resources/src/test/java/org/bonitasoft/platform/configuration/model/BonitaConfigurationAssert.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.model;\n\nimport org.assertj.core.api.AbstractAssert;\nimport org.assertj.core.api.Assertions;\nimport org.assertj.core.util.Objects;\n\n/**\n * {@link BonitaConfiguration} specific assertions - Generated by CustomAssertionGenerator.\n */\npublic class BonitaConfigurationAssert extends AbstractAssert<BonitaConfigurationAssert, BonitaConfiguration> {\n\n    /**\n     * Creates a new <code>{@link BonitaConfigurationAssert}</code> to make assertions on actual BonitaConfiguration.\n     *\n     * @param actual the BonitaConfiguration we want to make assertions on.\n     */\n    public BonitaConfigurationAssert(BonitaConfiguration actual) {\n        super(actual, BonitaConfigurationAssert.class);\n    }\n\n    /**\n     * An entry point for BonitaConfigurationAssert to follow AssertJ standard <code>assertThat()</code> statements.<br>\n     * With a static import, one can write directly: <code>assertThat(myBonitaConfiguration)</code> and get specific\n     * assertion with code completion.\n     *\n     * @param actual the BonitaConfiguration we want to make assertions on.\n     * @return a new <code>{@link BonitaConfigurationAssert}</code>\n     */\n    public static BonitaConfigurationAssert assertThat(BonitaConfiguration actual) {\n        return new BonitaConfigurationAssert(actual);\n    }\n\n    /**\n     * Verifies that the actual BonitaConfiguration's resourceContent contains the given byte elements.\n     *\n     * @param resourceContent the given elements that should be contained in actual BonitaConfiguration's\n     *        resourceContent.\n     * @return this assertion object.\n     * @throws AssertionError if the actual BonitaConfiguration's resourceContent does not contain all given byte\n     *         elements.\n     */\n    public BonitaConfigurationAssert hasResourceContent(byte... resourceContent) {\n        // check that actual BonitaConfiguration we want to make assertions on is not null.\n        isNotNull();\n\n        // check that given byte varargs is not null.\n        if (resourceContent == null)\n            failWithMessage(\"Expecting resourceContent parameter not to be null.\");\n\n        // check with standard error message (use overridingErrorMessage before contains to set your own message).\n        Assertions.assertThat(actual.getResourceContent()).contains(resourceContent);\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual BonitaConfiguration's resourceContent contains <b>only<b> the given byte elements and\n     * nothing else in whatever order.\n     *\n     * @param resourceContent the given elements that should be contained in actual BonitaConfiguration's\n     *        resourceContent.\n     * @return this assertion object.\n     * @throws AssertionError if the actual BonitaConfiguration's resourceContent does not contain all given byte\n     *         elements and nothing else.\n     */\n    public BonitaConfigurationAssert hasOnlyResourceContent(byte... resourceContent) {\n        // check that actual BonitaConfiguration we want to make assertions on is not null.\n        isNotNull();\n\n        // check that given byte varargs is not null.\n        if (resourceContent == null)\n            failWithMessage(\"Expecting resourceContent parameter not to be null.\");\n\n        // check with standard error message (use overridingErrorMessage before contains to set your own message).\n        Assertions.assertThat(actual.getResourceContent()).containsOnly(resourceContent);\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual BonitaConfiguration's resourceContent does not contain the given byte elements.\n     *\n     * @param resourceContent the given elements that should not be in actual BonitaConfiguration's resourceContent.\n     * @return this assertion object.\n     * @throws AssertionError if the actual BonitaConfiguration's resourceContent contains any given byte elements.\n     */\n    public BonitaConfigurationAssert doesNotHaveResourceContent(byte... resourceContent) {\n        // check that actual BonitaConfiguration we want to make assertions on is not null.\n        isNotNull();\n\n        // check that given byte varargs is not null.\n        if (resourceContent == null)\n            failWithMessage(\"Expecting resourceContent parameter not to be null.\");\n\n        // check with standard error message (use overridingErrorMessage before contains to set your own message).\n        Assertions.assertThat(actual.getResourceContent()).doesNotContain(resourceContent);\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual BonitaConfiguration has no resourceContent.\n     *\n     * @return this assertion object.\n     * @throws AssertionError if the actual BonitaConfiguration's resourceContent is not empty.\n     */\n    public BonitaConfigurationAssert hasNoResourceContent() {\n        // check that actual BonitaConfiguration we want to make assertions on is not null.\n        isNotNull();\n\n        // we override the default error message with a more explicit one\n        String assertjErrorMessage = \"\\nExpecting :\\n  <%s>\\nnot to have resourceContent but had :\\n  <%s>\";\n\n        // check\n        if (actual.getResourceContent().length > 0) {\n            failWithMessage(assertjErrorMessage, actual, java.util.Arrays.toString(actual.getResourceContent()));\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual BonitaConfiguration's resourceName is equal to the given one.\n     *\n     * @param resourceName the given resourceName to compare the actual BonitaConfiguration's resourceName to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual BonitaConfiguration's resourceName is not equal to the given one.\n     */\n    public BonitaConfigurationAssert hasResourceName(String resourceName) {\n        // check that actual BonitaConfiguration we want to make assertions on is not null.\n        isNotNull();\n\n        // overrides the default error message with a more explicit one\n        String assertjErrorMessage = \"\\nExpecting resourceName of:\\n  <%s>\\nto be:\\n  <%s>\\nbut was:\\n  <%s>\";\n\n        // null safe check\n        String actualResourceName = actual.getResourceName();\n        if (!Objects.areEqual(actualResourceName, resourceName)) {\n            failWithMessage(assertjErrorMessage, actual, resourceName, actualResourceName);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-resources/src/test/java/org/bonitasoft/platform/configuration/model/BonitaConfigurationTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.model;\n\nimport org.junit.Test;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class BonitaConfigurationTest {\n\n    @Test\n    public void should_build_configuration() {\n        //given\n        BonitaConfiguration bonitaConfiguration = new BonitaConfiguration(\"my resource\", \"my content\".getBytes());\n\n        //then\n        BonitaConfigurationAssert.assertThat(bonitaConfiguration)\n                .hasResourceName(\"my resource\")\n                .hasResourceContent(\"my content\".getBytes());\n\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-resources/src/test/java/org/bonitasoft/platform/configuration/model/FullBonitaConfigurationTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.model;\n\nimport static org.bonitasoft.platform.configuration.type.ConfigurationType.*;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.assertj.core.api.Assertions;\nimport org.bonitasoft.platform.configuration.type.ConfigurationType;\nimport org.junit.Test;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class FullBonitaConfigurationTest {\n\n    @Test\n    public void should_have_readable_toString() {\n        //given\n        FullBonitaConfiguration fullBonitaConfiguration = new FullBonitaConfiguration(\"resourceName\",\n                \"content\".getBytes(), \"type\");\n\n        //then\n        Assertions\n                .assertThat(fullBonitaConfiguration.toString())\n                .isEqualTo(\n                        \"FullBonitaConfiguration{ resourceName='resourceName' , configurationType='type' }\");\n    }\n\n    @Test\n    public void should_be_a_licence_file() {\n        //given\n        FullBonitaConfiguration fullBonitaConfiguration = new FullBonitaConfiguration(\"resourceName\",\n                \"content\".getBytes(), LICENSES.name());\n\n        //then\n        Assertions.assertThat(fullBonitaConfiguration.isLicenseFile()).isTrue();\n    }\n\n    @Test\n    public void should_not_be_a_licence_file() {\n        //given\n        final List<ConfigurationType> allExceptLicense = Arrays.asList(PLATFORM_PORTAL, PLATFORM_ENGINE, TENANT_PORTAL,\n                TENANT_ENGINE, TENANT_ENGINE, TENANT_SECURITY_SCRIPTS, TENANT_SECURITY_SCRIPTS,\n                TENANT_PORTAL);\n\n        for (ConfigurationType configurationType : allExceptLicense) {\n\n            //when\n            FullBonitaConfiguration fullBonitaConfiguration = new FullBonitaConfiguration(\"resourceName\",\n                    \"content\".getBytes(), configurationType.name());\n\n            //then\n            Assertions.assertThat(fullBonitaConfiguration.isLicenseFile()).isFalse();\n\n        }\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-resources/src/test/java/org/bonitasoft/platform/configuration/util/AllConfigurationResourceVisitorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.util;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.platform.configuration.model.FullBonitaConfiguration;\nimport org.junit.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class AllConfigurationResourceVisitorTest {\n\n    private final static Logger LOGGER = LoggerFactory.getLogger(ConfigurationResourceVisitor.class);\n\n    @Test\n    public void should_store_files_from_configuration_folder() throws Exception {\n        //given\n        Path rootFolder = Paths.get(getClass().getResource(\"/allConfiguration\").toURI());\n        LOGGER.info(\"folder:\" + rootFolder);\n        final List<FullBonitaConfiguration> bonitaConfigurations = new ArrayList<>();\n\n        //when\n        final AllConfigurationResourceVisitor resourceVisitor = new AllConfigurationResourceVisitor(\n                bonitaConfigurations);\n        Files.walkFileTree(rootFolder, resourceVisitor);\n\n        //then\n        assertThat(bonitaConfigurations).hasSize(5);\n        assertThat(bonitaConfigurations).as(\"should visit all configuration folders\")\n                .extracting(\"configurationType\")\n                .containsOnly(\"TENANT_ENGINE\", \"TENANT_PORTAL\", \"PLATFORM_PORTAL\", \"PLATFORM_ENGINE\",\n                        \"TENANT_SECURITY_SCRIPTS\");\n        assertThat(bonitaConfigurations).as(\"should add all configuration files and skip licenses\")\n                .extracting(\"resourceName\")\n                .containsOnly(\"security-config.properties\",\n                        \"compound-permissions-mapping.properties\",\n                        \"SamplePermissionRule.groovy.sample\",\n                        \"bonita-tenant-community.properties\",\n                        \"bonita-platform-community.properties\");\n\n    }\n}\n"
  },
  {
    "path": "platform/platform-resources/src/test/java/org/bonitasoft/platform/configuration/util/AutoUpdateConfigurationVisitorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.util;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\n\nimport org.apache.commons.io.FileUtils;\nimport org.junit.AfterClass;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class AutoUpdateConfigurationVisitorTest {\n\n    private AutoUpdateConfigurationVisitor autoUpdateVisitor = new AutoUpdateConfigurationVisitor(null);\n    private static Path initial;\n\n    @BeforeClass\n    public static void createFolders() throws IOException {\n        initial = Paths.get(System.getProperty(\"java.io.tmpdir\")).resolve(\"initial\");\n        Files.createDirectories(initial);\n    }\n\n    @AfterClass\n    public static void deleteFolders() throws IOException {\n        FileUtils.deleteDirectory(initial.toFile());\n    }\n\n    @Test\n    public void isAutoUpdateConfigurationFile_should_return_true_for_compound_permissions_mapping() throws Exception {\n        // given:\n        final Path path = initial.resolve(\"compound-permissions-mapping.properties\");\n        Files.createFile(path);\n\n        // when:\n        final boolean autoUpdateConfigurationFile = autoUpdateVisitor.isAutoUpdateConfigurationFile(path);\n\n        // then:\n        assertThat(autoUpdateConfigurationFile).isTrue();\n    }\n\n    @Test\n    public void isAutoUpdateConfigurationFile_should_return_true_for_dynamic_permissions_checks() throws Exception {\n        // given:\n        final Path path = initial.resolve(\"dynamic-permissions-checks.properties\");\n        Files.createFile(path);\n\n        // when:\n        final boolean autoUpdateConfigurationFile = autoUpdateVisitor.isAutoUpdateConfigurationFile(path);\n\n        // then:\n        assertThat(autoUpdateConfigurationFile).isTrue();\n    }\n\n    @Test\n    public void isAutoUpdateConfigurationFile_should_return_true_for_resources_permissions_mapping() throws Exception {\n        // given:\n        final Path path = initial.resolve(\"resources-permissions-mapping.properties\");\n        Files.createFile(path);\n\n        // when:\n        final boolean autoUpdateConfigurationFile = autoUpdateVisitor.isAutoUpdateConfigurationFile(path);\n\n        // then:\n        assertThat(autoUpdateConfigurationFile).isTrue();\n    }\n\n    @Test\n    public void isAutoUpdateConfigurationFile_should_return_true_for_user_creation_attribute_mapping()\n            throws Exception {\n        // given:\n        final Path path = initial.resolve(\"user-creation-attribute-mapping.properties\");\n        Files.createFile(path);\n\n        // when:\n        final boolean autoUpdateConfigurationFile = autoUpdateVisitor.isAutoUpdateConfigurationFile(path);\n\n        // then:\n        assertThat(autoUpdateConfigurationFile).isTrue();\n    }\n\n    @Test\n    public void isAutoUpdateConfigurationFile_should_return_false_for_other_file() throws Exception {\n        // given:\n        final Path path = initial.resolve(\"wrong_file_name.lst\");\n        Files.createFile(path);\n\n        // when:\n        final boolean autoUpdateConfigurationFile = autoUpdateVisitor.isAutoUpdateConfigurationFile(path);\n\n        // then:\n        assertThat(autoUpdateConfigurationFile).isFalse();\n    }\n\n    @Test\n    public void isAutoUpdateConfigurationFile_should_return_false_for_non_existing_file() throws Exception {\n        // given:\n        final Path path = initial.resolve(\"non-existing-folder/resources-permissions-mapping.properties\");\n\n        // when:\n        final boolean autoUpdateConfigurationFile = autoUpdateVisitor.isAutoUpdateConfigurationFile(path);\n\n        // then:\n        assertThat(autoUpdateConfigurationFile).isFalse();\n    }\n}\n"
  },
  {
    "path": "platform/platform-resources/src/test/java/org/bonitasoft/platform/configuration/util/ConfigurationResourceVisitorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.util;\n\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.assertj.core.api.Assertions;\nimport org.bonitasoft.platform.configuration.model.BonitaConfiguration;\nimport org.junit.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class ConfigurationResourceVisitorTest {\n\n    public static final int CURRENT_NUMBER_OF_CONFIGURATION_FILES = 2;\n\n    private final static Logger LOGGER = LoggerFactory.getLogger(ConfigurationResourceVisitor.class);\n\n    @Test\n    public void should_read_configuration_folder() throws Exception {\n        //given\n        Path rootFolder = Paths.get(getClass().getResource(\"/conf\").toURI());\n        LOGGER.error(\"folder:\" + rootFolder);\n        final List<BonitaConfiguration> bonitaConfigurations = new ArrayList<>();\n\n        //when\n        final ConfigurationResourceVisitor resourceVisitor = new ConfigurationResourceVisitor(bonitaConfigurations);\n        Files.walkFileTree(rootFolder, resourceVisitor);\n\n        //then\n        Assertions.assertThat(bonitaConfigurations).hasSize(CURRENT_NUMBER_OF_CONFIGURATION_FILES);\n    }\n}\n"
  },
  {
    "path": "platform/platform-resources/src/test/java/org/bonitasoft/platform/configuration/util/LicensesResourceVisitorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.util;\n\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.assertj.core.api.Assertions;\nimport org.bonitasoft.platform.configuration.model.BonitaConfiguration;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.TemporaryFolder;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class LicensesResourceVisitorTest {\n\n    @Rule\n    public TemporaryFolder temporaryFolder = new TemporaryFolder();\n\n    @Test\n    public void should_read_licenses_folder() throws Exception {\n        //given\n        Path licenseFolder = temporaryFolder.newFolder().toPath();\n        Files.createDirectories(licenseFolder.resolve(\"subFolder\"));\n        Files.write(licenseFolder.resolve(\"license1.lic\"), \"license 1 content\".getBytes());\n        Files.write(licenseFolder.resolve(\"license2.lic\"), \"license 2 content\".getBytes());\n        Files.write(licenseFolder.resolve(\"not_a_license\"), \"this is not a license\".getBytes());\n        Files.write(licenseFolder.resolve(\"subFolder\").resolve(\"ignoreMe.lic\"),\n                \"this is an ignored license\".getBytes());\n        final List<BonitaConfiguration> bonitaConfigurations = new ArrayList<>();\n        BonitaConfiguration expectedLicense1 = new BonitaConfiguration(\"license1.lic\", \"license 1 content\".getBytes());\n        BonitaConfiguration expectedLicense2 = new BonitaConfiguration(\"license2.lic\", \"license 2 content\".getBytes());\n\n        //when\n        final LicensesResourceVisitor resourceVisitor = new LicensesResourceVisitor(bonitaConfigurations);\n        Files.walkFileTree(licenseFolder, resourceVisitor);\n\n        //then\n        Assertions.assertThat(bonitaConfigurations).containsOnly(expectedLicense1, expectedLicense2);\n\n    }\n}\n"
  },
  {
    "path": "platform/platform-resources/src/test/java/org/bonitasoft/platform/version/impl/VersionServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.version.impl;\n\nimport java.util.List;\n\nimport org.assertj.core.api.Assertions;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.rules.TemporaryFolder;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentMatchers;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Mockito;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\nimport org.springframework.jdbc.core.JdbcTemplate;\n\n/**\n * @author laurent Leseigneur\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class VersionServiceImplTest {\n\n    @Rule\n    public TemporaryFolder temporaryFolder = new TemporaryFolder();\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Mock\n    JdbcTemplate jdbcTemplate;\n\n    @InjectMocks\n    @Spy\n    VersionServiceImpl versionService;\n\n    @Test\n    public void should_return_platform_database_version() throws Exception {\n        //given\n        Mockito.doReturn(List.of(\"a.b.c\")).when(jdbcTemplate).queryForList(ArgumentMatchers.anyString(),\n                ArgumentMatchers.eq(String.class));\n\n        //when\n        final String platformVersion = versionService.retrieveDatabaseSchemaVersion();\n\n        //then\n        Assertions.assertThat(platformVersion).as(\"should return same version\").isEqualTo(\"a.b.c\");\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-resources/src/test/resources/allConfiguration/licenses/license1.lic",
    "content": "license content"
  },
  {
    "path": "platform/platform-resources/src/test/resources/allConfiguration/platform_engine/bonita-platform-community.properties",
    "content": "#content not used for testing"
  },
  {
    "path": "platform/platform-resources/src/test/resources/allConfiguration/platform_portal/security-config.properties",
    "content": "# content not used for testing"
  },
  {
    "path": "platform/platform-resources/src/test/resources/allConfiguration/tenant_template_engine/bonita-tenant-community.properties",
    "content": "#content not used for testing"
  },
  {
    "path": "platform/platform-resources/src/test/resources/allConfiguration/tenant_template_security_scripts/SamplePermissionRule.groovy.sample",
    "content": "//content not used for testing"
  },
  {
    "path": "platform/platform-resources/src/test/resources/allConfiguration/tenants/456/tenant_engine/bonita-tenant-community.properties",
    "content": "#content not used for testing"
  },
  {
    "path": "platform/platform-resources/src/test/resources/allConfiguration/tenants/456/tenant_portal/compound-permissions-mapping.properties",
    "content": "#content not used for testing\n"
  },
  {
    "path": "platform/platform-resources/src/test/resources/allConfiguration/tenants/456/tenant_security_scripts/SamplePermissionRule.groovy.sample",
    "content": "//content not used for testing"
  },
  {
    "path": "platform/platform-resources/src/test/resources/conf/bonita-platform-community.properties",
    "content": "#content not used for testing"
  },
  {
    "path": "platform/platform-resources/src/test/resources/conf/bonita-platform-custom.xml",
    "content": "<beans xmlns=\"http://www.springframework.org/schema/beans\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:p=\"http://www.springframework.org/schema/p\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd\">\n    <!-- content not used for testing -->\n</beans>\n"
  },
  {
    "path": "platform/platform-setup/.gitignore",
    "content": "# this bin folder is useful for tests:\n!bin"
  },
  {
    "path": "platform/platform-setup/bin/.gitignore",
    "content": "/main/\n/test/\n"
  },
  {
    "path": "platform/platform-setup/build.gradle",
    "content": "import org.apache.tools.ant.filters.ReplaceTokens\nimport org.bonitasoft.engine.gradle.PomUtils\nimport org.bonitasoft.engine.gradle.SetupE2ETask\n\nplugins {\n    id 'distribution'\n    id 'bonita-tests'\n    id 'bonita-docker-database'\n}\n\nconfigurations {\n    distributionZip\n    inDistrib\n}\n\ndependencies {\n    api project(':platform:platform-resources')\n    api libs.commonsText\n    api libs.slf4jApi\n    api(libs.springBootStarter) {\n        exclude(module: 'jul-to-slf4j')\n        exclude(module: 'log4j-to-slf4j')\n        exclude(module: 'snakeyaml')\n    }\n    api libs.springBootStarterJdbc\n    api libs.h2\n    api libs.postgresql\n    api libs.commonsCLI\n\n    annotationProcessor libs.lombok\n    compileOnly libs.lombok\n\n    runtimeOnly libs.logback\n\n    testImplementation libs.assertj\n    testImplementation libs.mockitoCore\n    testImplementation libs.systemRules\n    testImplementation(libs.springBootStarterTest) {\n        exclude(module: 'json-path')\n    }\n    testImplementation libs.xmlunit\n    testImplementation project(':platform:platform-setup-test')\n\n    inDistrib project(path: ':platform:platform-resources', configuration: 'distributionZip')\n}\n\ngroup = 'org.bonitasoft.platform'\n\npublishing {\n    publications {\n        mavenJava(MavenPublication) {\n            from project.components.java\n            artifact distZip\n            pom { pom ->\n                name = \"Bonita Platform Setup\"\n                description = \"Bonita Platform Setup is the standalone tool to setup a new Bonita platform\"\n                PomUtils.pomCommunityPublication(pom)\n            }\n        }\n    }\n}\n\nprocessResources {\n    from('src/main/resources') {\n        include '*'\n        filter(ReplaceTokens, tokens: [version: project.version])\n    }\n}\n\ndistTar.enabled = false\n\ndistributions {\n    main {\n        distributionBaseName = \"Bonita-platform-setup\"\n        contents {\n            includeEmptyDirs = false\n            into('/') {\n                from('src/main/standalone')\n                include('*.sh')\n                include('*.bat')\n                filePermissions {\n                    user {\n                        read = true\n                        write = true\n                        execute = true\n                    }\n                    group {\n                        read = true\n                        write = false\n                        execute = false\n                    }\n                    other {\n                        read = false\n                        write = false\n                        execute = false\n\n                    }\n                }\n            }\n            into('/') {\n                from('src/main/standalone')\n                exclude('*.sh')\n                exclude('*.bat')\n            }\n            into('/lib') {\n                from jar\n                from project.configurations.runtimeClasspath {\n                    exclude(module: 'jul-to-slf4j')\n                    exclude(module: 'log4j-over-slf4j')\n                    exclude(module: 'commons-logging')\n                    exclude(module: 'log4j-api')\n                    exclude(module: 'log4j-to-slf4j')\n                    exclude(module: 'snakeyaml')\n                }\n            }\n            configurations.inDistrib.resolvedConfiguration.resolvedArtifacts.each { artifact ->\n                // Copy all sql files under the distrib zip folder /platform_conf/sql/\n                from(zipTree(artifact.file)) {\n                    include '**/sql/**'\n                    eachFile { fcp ->\n                        fcp.path = fcp.path.replaceAll(\".*/sql/\", \"/platform_conf/sql/\")\n                    }\n                }\n                // Copy all files excepting sql and classes under the distrib zip folder /platform_conf/initial/\n                from(zipTree(artifact.file)) {\n                    exclude '**/sql/**'\n                    exclude '**/*.class'\n                    eachFile { fcp ->\n                        fcp.path = fcp.path.replaceAll(\".*${version}/\", \"/platform_conf/initial/\")\n                    }\n                }\n            }\n            filePermissions {\n                user {\n                    read = true\n                    write = true\n                    execute = false\n                }\n                group {\n                    read = true\n                    write = false\n                    execute = false\n                }\n                other {\n                    read = false\n                    write = false\n                    execute = false\n\n                }\n            }\n        }\n    }\n}\n\ntasks.distZip.dependsOn configurations.inDistrib\n\nartifacts { distributionZip distZip }\n\ndatabaseIntegrationTest {\n    include '**/*IT.class'\n    // No need to execute PlatformSetupDistributionIT on databases\n    exclude '**/PlatformSetupDistributionIT.class'\n}\n\ntasks.matching { task -> task.name == \"integrationTest\" || task.name.endsWith(\"DatabaseTest\") }\n        .configureEach { task ->\n            def testDir = layout.buildDirectory.dir(task.name).get().asFile\n            doFirst {\n                testDir.mkdirs()\n                systemProperty \"bonita.distribution.path\", distZip.outputs.files.first()\n            }\n            workingDir testDir\n            dependsOn distZip\n        }\n\ndef setupE2e = tasks.register('setup-e2e', SetupE2ETask) {\n    scriptFile = file('src/test/e2e/e2e-postgres-bos.sh')\n    inputs.files project(':platform:platform-setup').tasks.named('distZip').get().outputs.files\n    dependsOn distZip\n}\n\nintegrationTest.configure {\n    dependsOn setupE2e\n}"
  },
  {
    "path": "platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/ConfigurationChecker.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup;\n\nimport java.nio.file.Paths;\nimport java.util.Properties;\n\nimport org.bonitasoft.platform.exception.PlatformException;\nimport org.bonitasoft.platform.setup.command.configure.DatabaseConfiguration;\n\n/**\n * Preliminary checks done by platform setup tool before it tries to go further:\n * <ul>\n * <li>checks that all mandatory properties are set into database.properties</li>\n * <li>checks that the driver class if found and can be loaded</li>\n * </ul>\n *\n * @author Emmanuel Duchastenier\n */\nclass ConfigurationChecker {\n\n    private Properties datasourceProperties;\n\n    private String driverClassName;\n    private DatabaseConfiguration dbConfiguration;\n\n    ConfigurationChecker(Properties datasourceProperties) {\n        this.datasourceProperties = datasourceProperties;\n    }\n\n    void loadProperties() throws PlatformException {\n        dbConfiguration = new DatabaseConfiguration(\"\", datasourceProperties, Paths.get(\".\"));\n        driverClassName = dbConfiguration.getNonXaDriverClassName();\n    }\n\n    public void validate() throws PlatformException {\n        loadProperties();\n        tryToLoadDriverClass();\n    }\n\n    void tryToLoadDriverClass() throws PlatformException {\n        try {\n            Class.forName(driverClassName);\n        } catch (ClassNotFoundException e) {\n            throw new PlatformException(\"The driver class named '\" + driverClassName\n                    + \"' specified in 'internal.properties' configuration file, to connect to your '\"\n                    + dbConfiguration.getDbVendor()\n                    + \"' database, cannot be found.\" +\n                    \" Either there is an error in the name of the class or the class is not available in the classpath.\"\n                    +\n                    \" Make sure the driver class name is correct and that the suitable driver is available in the lib/ folder and then try again.\",\n                    e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/PlatformSetupApplication.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Properties;\n\nimport org.apache.commons.cli.CommandLine;\nimport org.apache.commons.cli.CommandLineParser;\nimport org.apache.commons.cli.DefaultParser;\nimport org.apache.commons.cli.Option;\nimport org.apache.commons.cli.Options;\nimport org.apache.commons.cli.ParseException;\nimport org.bonitasoft.platform.exception.PlatformException;\nimport org.bonitasoft.platform.setup.command.CommandException;\nimport org.bonitasoft.platform.setup.command.HelpCommand;\nimport org.bonitasoft.platform.setup.command.InitCommand;\nimport org.bonitasoft.platform.setup.command.PlatformSetupCommand;\nimport org.bonitasoft.platform.setup.command.PullCommand;\nimport org.bonitasoft.platform.setup.command.PushCommand;\nimport org.bonitasoft.platform.setup.command.configure.ConfigureCommand;\nimport org.bonitasoft.platform.setup.command.configure.PropertyLoader;\nimport org.bonitasoft.platform.setup.jndi.MemoryJNDISetup;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.annotation.ComponentScan;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@SpringBootApplication\n@ComponentScan(basePackages = { \"org.bonitasoft.platform.setup\", \"org.bonitasoft.platform.configuration\",\n        \"org.bonitasoft.platform.version\", \"com.bonitasoft.platform.setup\" })\npublic class PlatformSetupApplication {\n\n    // /!\\ Leave this logger NON-STATIC, so that DEBUG property may be set before it is initialized:\n    private final Logger LOGGER = LoggerFactory.getLogger(PlatformSetupApplication.class);\n    private HelpCommand helpCommand;\n    private List<PlatformSetupCommand> commands;\n    private Options options;\n\n    @Autowired\n    MemoryJNDISetup memoryJNDISetup;\n\n    @Autowired\n    PlatformSetup platformSetup;\n\n    public static void main(String[] args) {\n        if (args != null && Arrays.asList(args).contains(\"--debug\")) {\n            System.setProperty(\"bonita.platform.setup.log\", \"DEBUG\"); // so that it is set before used by Logger\n        }\n        new PlatformSetupApplication().run(args);\n    }\n\n    public static PlatformSetup getPlatformSetup(String[] args) throws PlatformException {\n        new ConfigurationChecker(new PropertyLoader().loadProperties()).validate();\n        return SpringApplication.run(PlatformSetupApplication.class, args).getBean(PlatformSetup.class);\n    }\n\n    private void run(String[] args) {\n        CommandLineParser parser = new DefaultParser();\n        options = createOptions();\n        commands = createCommands();\n        helpCommand.setCommands(commands);\n        CommandLine line = parseArguments(args, parser);\n        configureApplication(line);\n        execute(line);\n    }\n\n    private PlatformSetupCommand getCommand(CommandLine line) {\n        List<String> argList = line.getArgList();\n        if (argList.isEmpty()) {\n            return helpCommand;\n        }\n        final String commandName = argList.get(0);\n        for (PlatformSetupCommand platformSetupCommand : commands) {\n            if (commandName.equals(platformSetupCommand.getName())) {\n                return platformSetupCommand;\n            }\n        }\n        return helpCommand;\n    }\n\n    private void configureApplication(CommandLine line) {\n        Properties systemProperties = line.getOptionProperties(\"D\");\n        for (Map.Entry<Object, Object> systemProperty : systemProperties.entrySet()) {\n            System.setProperty(systemProperty.getKey().toString(), systemProperty.getValue().toString());\n        }\n    }\n\n    private void execute(CommandLine line) {\n        try {\n            getCommand(line).execute(options, line);\n        } catch (CommandException e) {\n            //this is an known exception we do not show any stack trace\n            LOGGER.error(e.getMessage());\n            System.exit(1);\n        } catch (Exception e) {\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(\"ERROR: \", e);\n            } else {\n                LOGGER.error(e.getMessage());\n                LOGGER.error(\n                        \"You might get more detailed information about the error by adding '--debug' to the command line, and run again\");\n            }\n            // Exit code allows the calling script to catch an invalid execution:\n            System.exit(1);\n        }\n        System.exit(0);\n    }\n\n    private CommandLine parseArguments(String[] args, CommandLineParser parser) {\n        try {\n            // parse the command line arguments\n            return parser.parse(options, args);\n        } catch (ParseException exp) {\n            System.err.println(\"ERROR: error while parsing arguments \" + exp.getMessage());\n            System.exit(1);\n        }\n        return null;\n    }\n\n    private List<PlatformSetupCommand> createCommands() {\n        List<PlatformSetupCommand> commandList = new ArrayList<>();\n        commandList.add(new InitCommand());\n        commandList.add(new ConfigureCommand());\n        commandList.add(new PullCommand());\n        commandList.add(new PushCommand());\n        helpCommand = new HelpCommand();\n        commandList.add(helpCommand);\n        return commandList;\n    }\n\n    private Options createOptions() {\n        Options opts = new Options();\n        Option systemPropertyOption = new Option(\"D\",\n                \"specify system property to override configuration from database.properties\");\n        systemPropertyOption.setArgName(\"property=value\");\n        systemPropertyOption.setValueSeparator('=');\n        systemPropertyOption.setArgs(2);\n        opts.addOption(systemPropertyOption);\n        opts.addOption(\"f\", \"force\", false, \"Force push even if critical folders will be deleted\");\n        opts.addOption(\"d\", \"debug\", false, \"Provides more details in case of error\");\n        return opts;\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/command/CommandException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup.command;\n\n/**\n * @author Baptiste Mesta\n */\npublic class CommandException extends Exception {\n\n    public CommandException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/command/CommandUtils.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup.command;\n\nimport java.io.IOException;\n\nimport org.apache.commons.io.IOUtils;\n\n/**\n * @author Baptiste Mesta\n */\npublic class CommandUtils {\n\n    public static String getFileContentFromClassPath(String filename) {\n        try {\n            return IOUtils.toString(PlatformSetupCommand.class.getResourceAsStream(\"/\" + filename), \"UTF-8\");\n        } catch (IOException e) {\n            throw new IllegalStateException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/command/HelpCommand.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup.command;\n\nimport static java.lang.System.lineSeparator;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.commons.cli.CommandLine;\nimport org.apache.commons.cli.HelpFormatter;\nimport org.apache.commons.cli.Options;\nimport org.apache.commons.lang3.StringUtils;\nimport org.bonitasoft.platform.exception.PlatformException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class HelpCommand extends PlatformSetupCommand {\n\n    private List<PlatformSetupCommand> commands;\n\n    public HelpCommand() {\n        super(\"help\", \"Display the help\", \"Display the help\", null);\n    }\n\n    @Override\n    public void execute(Options options, CommandLine commandLine) throws PlatformException, CommandException {\n        String[] args = commandLine.getArgs();\n        if (args.length == 0) {\n            printUsage(options);\n            throw new CommandException(\"Need to specify a command, see usage above.\");\n        } else if (getName().equals(args[0])) {\n            if (args.length > 1) {\n                printHelpFor(options, args[1]);\n            } else {\n                printCommonHelp(options);\n            }\n        } else {\n            printUsage(options);\n            throw new CommandException(\"ERROR: no command named: \" + args[0]);\n        }\n    }\n\n    private void printHelpFor(Options options, String commandNameForHelp) throws CommandException {\n        PlatformSetupCommand platformSetupCommand = getCommand(commandNameForHelp);\n        if (platformSetupCommand == null) {\n            printCommonHelp(options);\n            throw new CommandException(\"ERROR: no command named: \" + commandNameForHelp);\n        }\n        printHelpFor(options, platformSetupCommand);\n    }\n\n    public void setCommands(List<PlatformSetupCommand> commands) {\n        this.commands = commands;\n    }\n\n    private PlatformSetupCommand getCommand(String commandName) {\n        PlatformSetupCommand command = null;\n        for (PlatformSetupCommand platformSetupCommand : commands) {\n            if (commandName.equals(platformSetupCommand.getName())) {\n                command = platformSetupCommand;\n                break;\n            }\n        }\n        return command;\n    }\n\n    private void printCommonHelp(Options options) {\n        printUsage(options);\n        printGlobalHelpHeader();\n        printCommandsUsage();\n        printGlobalHelpFooter();\n    }\n\n    private void printUsage(Options options) {\n        List<String> names = new ArrayList<>(commands.size());\n        for (PlatformSetupCommand command : commands) {\n            if (!command.equals(this)) {\n                names.add(command.getName());\n            }\n        }\n        String footer = \"use `setup help` or `setup help <command>` for more details\" + lineSeparator();\n        printUsageFor(options, \"( \" + StringUtils.join(names.iterator(), \" | \") + \" )\", footer);\n    }\n\n    private void printCommandsUsage() {\n        StringBuilder usage = new StringBuilder();\n        usage.append(lineSeparator());\n        usage.append(\"Available commands:\").append(lineSeparator()).append(lineSeparator());\n        for (PlatformSetupCommand command : commands) {\n            usage.append(\" \").append(command.getName()).append(\"  --  \").append(command.getSummary())\n                    .append(lineSeparator());\n        }\n        System.out.println(usage.toString());\n    }\n\n    private void printHelpFor(Options options, PlatformSetupCommand command) {\n        printUsageFor(options, command.getName(), lineSeparator());\n        printCommandDescriptionHeader(command);\n        printCommandUsage(command);\n        printCommandDescriptionFooter(command);\n    }\n\n    private void printCommandDescriptionHeader(PlatformSetupCommand command) {\n        if (command.getDescriptionHeader() != null) {\n            System.out.println(\"  \" + command.getDescriptionHeader().replace(lineSeparator(), lineSeparator() + \"  \"));\n        }\n    }\n\n    private void printCommandDescriptionFooter(PlatformSetupCommand command) {\n        if (command.getDescriptionFooter() != null) {\n            System.out.println(\"  \" + command.getDescriptionFooter().replace(lineSeparator(), lineSeparator() + \"  \"));\n        }\n    }\n\n    private void printUsageFor(Options options, String commandName, String footer) {\n        HelpFormatter formatter = new HelpFormatter();\n        formatter.printHelp(\"setup \" + commandName, lineSeparator() + \"Available options:\", options, footer, true);\n    }\n\n    private void printCommandUsage(PlatformSetupCommand command) {\n        System.out.println(lineSeparator() +\n                \" \" + command.getName() + \"  --  \" + command.getSummary() + lineSeparator());\n    }\n\n    private void printGlobalHelpHeader() {\n        System.out.println(CommandUtils.getFileContentFromClassPath(\"global_usage_header.txt\"));\n    }\n\n    private void printGlobalHelpFooter() {\n        System.out.println(CommandUtils.getFileContentFromClassPath(\"global_usage_footer.txt\"));\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/command/InitCommand.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup.command;\n\nimport static org.bonitasoft.platform.setup.command.configure.DatabaseConfiguration.H2_DB_VENDOR;\n\nimport java.io.IOException;\nimport java.util.Properties;\n\nimport org.apache.commons.cli.CommandLine;\nimport org.apache.commons.cli.Options;\nimport org.bonitasoft.platform.exception.PlatformException;\nimport org.bonitasoft.platform.setup.command.configure.PropertyReader;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Baptiste Mesta\n */\npublic class InitCommand extends PlatformSetupCommand {\n\n    private final static Logger LOGGER = LoggerFactory.getLogger(InitCommand.class);\n\n    public InitCommand() {\n        super(\"init\", \"Initialise the database so that Bonita is ready to run with this database\",\n                CommandUtils.getFileContentFromClassPath(\"init_header.txt\"),\n                CommandUtils.getFileContentFromClassPath(\"init_footer.txt\"));\n    }\n\n    @Override\n    public void execute(Options options, CommandLine commandLine) throws PlatformException, CommandException {\n        askConfirmationIfH2();\n        getPlatformSetup(commandLine.getArgs()).init();\n    }\n\n    void askConfirmationIfH2() throws PlatformException, CommandException {\n        final Properties properties = new Properties();\n        try {\n            properties.load(this.getClass().getResourceAsStream(\"/database.properties\"));\n            final PropertyReader propertyReader = new PropertyReader(properties);\n            if (H2_DB_VENDOR.equals(propertyReader.getPropertyAndFailIfNull(\"db.vendor\"))\n                    && H2_DB_VENDOR.equals(propertyReader.getPropertyAndFailIfNull(\"bdm.db.vendor\"))\n                    && System.getProperty(\"h2.noconfirm\") == null) {\n                warn(\"Default H2 configuration detected. This is not recommended for production. If this is not the required configuration, change file 'database.properties' and run again.\");\n                warn(\"To skip this warning message, add '-Dh2.noconfirm' to your command line.\");\n                System.out.print(\"Are you sure you want to continue? (y/n): \");\n                final String answer = readAnswer();\n                if (!\"y\".equalsIgnoreCase(answer)) {\n                    throw new CommandException(\"Default H2 configuration not confirmed. Exiting.\");\n                }\n            }\n        } catch (IOException e) {\n            throw new PlatformException(\"Error reading configuration file database.properties.\" +\n                    \" Please make sure the file is present at the root of the Platform Setup Tool folder, and that is has not been moved of deleted\",\n                    e);\n        }\n    }\n\n    String readAnswer() throws IOException {\n        final byte[] read = new byte[1];\n        System.in.read(read);\n        return new String(read);\n    }\n\n    void warn(String message) {\n        LOGGER.warn(message);\n    }\n}\n"
  },
  {
    "path": "platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/command/PlatformSetupCommand.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup.command;\n\nimport org.apache.commons.cli.CommandLine;\nimport org.apache.commons.cli.Options;\nimport org.bonitasoft.platform.exception.PlatformException;\nimport org.bonitasoft.platform.setup.PlatformSetup;\nimport org.bonitasoft.platform.setup.PlatformSetupApplication;\n\n/**\n * @author Baptiste Mesta\n */\npublic abstract class PlatformSetupCommand {\n\n    private String name;\n    private String summary;\n    private String descriptionHeader;\n\n    private String descriptionFooter;\n\n    public PlatformSetupCommand(String name, String summary, String descriptionHeader, String descriptionFooter) {\n        this.name = name;\n        this.summary = summary;\n        this.descriptionHeader = descriptionHeader;\n        this.descriptionFooter = descriptionFooter;\n    }\n\n    public abstract void execute(Options options, CommandLine commandLine) throws PlatformException, CommandException;\n\n    public String getName() {\n        return name;\n    }\n\n    public String getSummary() {\n        return summary;\n    }\n\n    public String getDescriptionHeader() {\n        return descriptionHeader;\n    }\n\n    public String getDescriptionFooter() {\n        return descriptionFooter;\n    }\n\n    PlatformSetup getPlatformSetup(String[] args) throws PlatformException {\n        return PlatformSetupApplication.getPlatformSetup(args);\n    }\n}\n"
  },
  {
    "path": "platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/command/PullCommand.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup.command;\n\nimport org.apache.commons.cli.CommandLine;\nimport org.apache.commons.cli.Options;\nimport org.bonitasoft.platform.exception.PlatformException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class PullCommand extends PlatformSetupCommand {\n\n    public PullCommand() {\n        super(\"pull\", \"Pull configuration from the database\", null,\n                CommandUtils.getFileContentFromClassPath(\"pull.txt\"));\n    }\n\n    @Override\n    public void execute(Options options, CommandLine commandLine) throws PlatformException {\n        getPlatformSetup(commandLine.getArgs()).pull();\n    }\n}\n"
  },
  {
    "path": "platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/command/PushCommand.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup.command;\n\nimport org.apache.commons.cli.CommandLine;\nimport org.apache.commons.cli.Options;\nimport org.bonitasoft.platform.exception.PlatformException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class PushCommand extends PlatformSetupCommand {\n\n    public PushCommand() {\n        super(\"push\", \"Push configuration to the database\", null, CommandUtils.getFileContentFromClassPath(\"push.txt\"));\n    }\n\n    @Override\n    public void execute(Options options, CommandLine commandLine) throws PlatformException {\n        getPlatformSetup(commandLine.getArgs()).push(commandLine.hasOption(\"force\"));\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/command/configure/BundleConfigurator.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup.command.configure;\n\nimport java.io.File;\nimport java.io.FilenameFilter;\nimport java.io.IOException;\nimport java.net.URISyntaxException;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.text.SimpleDateFormat;\nimport java.util.Collection;\nimport java.util.Date;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.regex.Matcher;\n\nimport org.apache.commons.io.FileUtils;\nimport org.apache.commons.io.IOCase;\nimport org.apache.commons.io.filefilter.RegexFileFilter;\nimport org.apache.commons.text.StringEscapeUtils;\nimport org.bonitasoft.platform.database.DatabaseVendor;\nimport org.bonitasoft.platform.exception.PlatformException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * This class configures a supported Bundle with configuration values given in file database.properties.\n * For Tomcat bundle:\n * <ul>\n * <li>sets db vendor + bdm db vendor in file setenv.sh / .bat</li>\n * <li>sets all database configuration in file conf/Catalina/localhost/bonita.xml</li>\n * <li>copies the required drivers from setup/lib to lib/bonita</li>\n * </ul>\n *\n * @author Emmanuel Duchastenier\n */\nabstract class BundleConfigurator {\n\n    static final Logger LOGGER = LoggerFactory.getLogger(BundleConfigurator.class);\n\n    private static final String TOMCAT_TEMPLATES_FOLDER = \"tomcat-templates\";\n\n    static final String APPSERVER_FOLDERNAME = \"server\";\n\n    private final Path rootPath;\n\n    DatabaseConfiguration standardConfiguration;\n    DatabaseConfiguration bdmConfiguration;\n    private Path backupsFolder;\n    private final String timestamp;\n\n    BundleConfigurator(Path rootPath) throws PlatformException {\n        try {\n            this.rootPath = rootPath.toRealPath();\n        } catch (IOException e) {\n            throw new PlatformException(\"Unable to determine root path for \" + getBundleName());\n        }\n        timestamp = new SimpleDateFormat(\"yyyy-MM-dd_HH'h'mm'm'ss's'\").format(new Date());\n    }\n\n    void loadProperties() throws PlatformException {\n        final Properties properties = new PropertyLoader().loadProperties();\n\n        standardConfiguration = new DatabaseConfiguration(\"\", properties, rootPath);\n        bdmConfiguration = new DatabaseConfiguration(\"bdm.\", properties, rootPath);\n        try {\n            final Path dbFile = Paths.get(this.getClass().getResource(\"/database.properties\").toURI());\n            LOGGER.info(getBundleName() + \" environment detected with root \" + rootPath);\n            LOGGER.info(\"Running auto-configuration using file \" + dbFile.normalize());\n        } catch (URISyntaxException e) {\n            throw new PlatformException(\"Configuration file 'database.properties' not found\");\n        }\n    }\n\n    protected abstract String getBundleName();\n\n    abstract void configureApplicationServer() throws PlatformException;\n\n    void createBackupFolderIfNecessary(String backupFolder) throws PlatformException {\n        backupsFolder = getPath(backupFolder);\n        if (Files.notExists(backupsFolder)) {\n            try {\n                Files.createDirectory(backupsFolder);\n            } catch (IOException e) {\n                throw new PlatformException(\"Could not create backup folder: \" + backupFolder, e);\n            }\n        }\n    }\n\n    Path getTemplateFolderPath(String templateFile) throws PlatformException {\n        return getPath(\"setup\").resolve(TOMCAT_TEMPLATES_FOLDER).resolve(templateFile);\n    }\n\n    void backupAndReplaceContentIfNecessary(Path path, String newContent, String message) throws PlatformException {\n        if (Files.exists(path)) {\n            String previousContent = readContentFromFile(path);\n            if (!previousContent.equals(newContent)) {\n                makeBackupOfFile(path);\n                writeContentToFile(path, newContent);\n                LOGGER.info(message);\n            } else {\n                LOGGER.info(\n                        \"Same configuration detected for file '\" + getRelativePath(path) + \"'. No need to change it.\");\n            }\n        } else {\n            LOGGER.info(\"File '\" + getRelativePath(path) + \"' did not already exist. Creating it.\");\n            writeContentToFile(path, newContent);\n        }\n    }\n\n    void copyDatabaseDriversIfNecessary(Path srcDriverFile, Path targetDriverFile, String dbVendor)\n            throws PlatformException {\n        if (srcDriverFile == null || targetDriverFile == null) {\n            return;\n        }\n        if (Files.exists(targetDriverFile)) {\n            LOGGER.info(\"Your \" + dbVendor + \" driver file '\" + getRelativePath(targetDriverFile)\n                    + \"' already exists. Skipping the copy.\");\n            return;\n        }\n        copyDriverFile(srcDriverFile, targetDriverFile, dbVendor);\n    }\n\n    //This method will remove h2 from bonita the classPath to mitigate the cve https://nvd.nist.gov/vuln/detail/CVE-2022-23221\n    void removeH2DriverIfNecessary(Path targetBonitaDbDriverFile, String bonitaDbVendor, String bdmDbVendor)\n            throws PlatformException {\n        if (!bonitaDbVendor.equals(\"h2\") && !bdmDbVendor.equals(\"h2\")) {\n            FilenameFilter filter = (file, s) -> s.contains(\"h2\");\n            File[] driversFiles = targetBonitaDbDriverFile.toFile().listFiles(filter);\n\n            if (driversFiles != null && driversFiles.length > 0) {\n                for (File driver : driversFiles) {\n                    try {\n                        LOGGER.info(\n                                \"H2 driver has been found located in bonita classpath {}, the driver file  will be removed\",\n                                driver.getPath());\n                        Files.delete(driver.toPath());\n                        LOGGER.info(\"File has been removed\");\n                    } catch (IOException e) {\n                        LOGGER.error(\n                                \"Fail to delete driver file lib/ {}\", driver.getPath(),\n                                e);\n                    }\n                }\n            }\n        }\n    }\n\n    private Path getRelativePath(Path pathToRelativize) {\n        return rootPath.toAbsolutePath().relativize(pathToRelativize.toAbsolutePath());\n    }\n\n    void copyDriverFile(Path srcDriverFile, Path targetDriverFile, String dbVendor) throws PlatformException {\n        try {\n            final Path targetDriverFolder = targetDriverFile.getParent();\n            targetDriverFolder.toFile().mkdirs();\n            Files.copy(srcDriverFile, targetDriverFile);\n            LOGGER.info(\"Copying your {} driver file ''{}'' to tomcat lib folder ''{}''\",\n                    dbVendor,\n                    getRelativePath(srcDriverFile),\n                    getRelativePath(targetDriverFolder));\n        } catch (IOException e) {\n            throw new PlatformException(\n                    \"Fail to copy driver file lib/\" + srcDriverFile.getFileName() + \" to \"\n                            + targetDriverFile.toAbsolutePath() + \": \" + e.getMessage(),\n                    e);\n        }\n    }\n\n    static String replaceValues(String content, Map<String, String> replacementMap) {\n        for (Map.Entry<String, String> entry : replacementMap.entrySet()) {\n            content = content.replaceAll(entry.getKey(), entry.getValue());\n        }\n        return content;\n    }\n\n    // Visible for testing\n    static String convertWindowsBackslashes(String value) {\n        // forward slashes is valid in database connection URLs on Windows, and and easier and more homogeneous to manage in\n        return value.replace(\"\\\\\", \"/\");\n    }\n\n    private void writeContentToFile(Path path, String content) throws PlatformException {\n        try {\n            Files.write(path, content.getBytes(StandardCharsets.UTF_8));\n        } catch (IOException e) {\n            throw new PlatformException(\"Fail to replace content in file \" + path + \": \" + e.getMessage(), e);\n        }\n    }\n\n    String readContentFromFile(Path bonitaXmlFile) throws PlatformException {\n        try {\n            return new String(Files.readAllBytes(bonitaXmlFile), StandardCharsets.UTF_8);\n        } catch (IOException e) {\n            throw new PlatformException(\n                    \"Cannot read content of text file \" + bonitaXmlFile.toAbsolutePath().toString(), e);\n        }\n    }\n\n    void restoreOriginalFile(Path bonitaXmlFile) throws PlatformException {\n        try {\n            if (Files.exists(bonitaXmlFile)) {\n                Files.delete(bonitaXmlFile);\n            }\n            final Path backupFile = getBackupFile(bonitaXmlFile);\n            if (Files.exists(backupFile)) {\n                Files.move(backupFile, bonitaXmlFile);\n            }\n        } catch (IOException e) {\n            throw new PlatformException(\"Fail to restore original file for \" + bonitaXmlFile + \": \" + e.getMessage(),\n                    e);\n        }\n    }\n\n    private Path makeBackupOfFile(Path originalFile) throws PlatformException {\n        final Path originalFileName = originalFile.getFileName();\n        final Path backup = getBackupFile(originalFile);\n        LOGGER.info(\n                \"Creating a backup of configuration file '\" + getRelativePath(originalFile).normalize() + \"' to '\"\n                        + getRelativePath(backup).normalize() + \"'\");\n        try {\n            Files.copy(originalFile, backup);\n            return backup;\n        } catch (IOException e) {\n            throw new PlatformException(\"Fail to make backup file for \" + originalFileName + \": \" + e.getMessage(), e);\n        }\n    }\n\n    private Path getBackupFile(Path originalFile) {\n        return backupsFolder.resolve(originalFile.getFileName() + \".\" + getTimestamp());\n    }\n\n    private String getTimestamp() {\n        return timestamp;\n    }\n\n    File getDriverFile(String dbVendor) throws PlatformException {\n        final Path driverFolder = getPath(\"setup/lib\");\n        if (!Files.exists(driverFolder)) {\n            throw new PlatformException(\"Drivers folder not found: \" + driverFolder.toString()\n                    + \". Make sure it exists and put a jar or zip file containing drivers there.\");\n        }\n        final Collection<File> driversFiles = FileUtils.listFiles(driverFolder.toFile(), getDriverFilter(dbVendor),\n                null);\n        if (driversFiles.isEmpty()) {\n            throw new PlatformException(\"No \" + dbVendor + \" drivers found in folder \" + driverFolder.toString()\n                    + \". Make sure to put a jar or zip file containing drivers there.\");\n        } else if (driversFiles.size() == 1) {\n            return (File) driversFiles.toArray()[0];\n        } else {\n            throw new PlatformException(\n                    \"Found more than 1 file containing \" + dbVendor + \" drivers  in folder \" + driverFolder.toString()\n                            + \". Make sure to put only 1 jar or zip file containing drivers there.\");\n        }\n    }\n\n    RegexFileFilter getDriverFilter(String dbVendor) {\n        return new RegexFileFilter(getDriverPattern(dbVendor), IOCase.INSENSITIVE);\n    }\n\n    private String getDriverPattern(String dbVendor) {\n        if (DatabaseVendor.ORACLE.equalsValue(dbVendor)) {\n            return \".*(ojdbc|oracle).*\\\\.(jar|zip)\";\n        }\n        if (DatabaseVendor.SQLSERVER.equalsValue(dbVendor)) {\n            return \".*(sqlserver|mssql|sqljdbc).*\\\\.(jar|zip)\";\n        }\n        return \".*\" + dbVendor + \".*\";\n    }\n\n    /**\n     * Constructs path relative to rootPath.\n     *\n     * @param partialPath path relative to rootPath\n     * @return the real path, constructed from rootPath, appending the partialPath\n     */\n    private Path getPath(String partialPath) throws PlatformException {\n        return getPath(partialPath, false);\n    }\n\n    /**\n     * Constructs path relative to rootPath.\n     *\n     * @param partialPath path relative to rootPath\n     * @param failIfNotExist should we fail if file does not exist?\n     * @return the real path, constructed from rootPath, appending the partialPath\n     */\n    private Path getPath(String partialPath, boolean failIfNotExist) throws PlatformException {\n        final String[] paths = partialPath.split(\"/\");\n        Path build = rootPath;\n        for (String path : paths) {\n            build = build.resolve(path);\n        }\n        if (failIfNotExist && Files.notExists(build)) {\n            throw new PlatformException(\"File \" + build.getFileName() + \" is mandatory but is not found\");\n        }\n        return build;\n    }\n\n    // Visible for testing\n    static String escapeXmlCharacters(String url) {\n        return StringEscapeUtils.escapeXml11(url);\n    }\n\n    protected Path getOptionalPathUnderAppServer(String path) throws PlatformException {\n        return getPath(APPSERVER_FOLDERNAME + \"/\" + path, false);\n    }\n\n    Path getPathUnderAppServer(String path, boolean failIfNotExist) throws PlatformException {\n        return getPath(APPSERVER_FOLDERNAME + \"/\" + path, failIfNotExist);\n    }\n\n    static String getDatabaseConnectionUrlForXmlFile(DatabaseConfiguration configuration) {\n        return escapeXmlCharacters(Matcher.quoteReplacement(getDatabaseConnectionUrl(configuration)));\n    }\n\n    static String getDatabaseConnectionUrlForPropertiesFile(DatabaseConfiguration configuration) {\n        String url = getDatabaseConnectionUrl(configuration);\n        if (DatabaseVendor.H2.equalsValue(configuration.getDbVendor())) {\n            url = StringEscapeUtils.escapeJava(url);\n        }\n        return Matcher.quoteReplacement(url);\n    }\n\n    private static String getDatabaseConnectionUrl(DatabaseConfiguration configuration) {\n        String url = configuration.getUrl();\n        if (DatabaseVendor.H2.equalsValue(configuration.getDbVendor())) {\n            url = convertWindowsBackslashes(url);\n        }\n        return url;\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/command/configure/BundleResolver.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup.command.configure;\n\nimport static org.bonitasoft.platform.setup.PlatformSetup.BONITA_SETUP_FOLDER;\nimport static org.bonitasoft.platform.setup.command.configure.BundleConfigurator.APPSERVER_FOLDERNAME;\n\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.platform.exception.PlatformException;\n\n/**\n * This class contains the logic to determine if we are in the context of an application server that we must configure.\n * Only Tomcat is supported.\n */\n@Slf4j\nclass BundleResolver {\n\n    private final Path rootPath;\n\n    BundleResolver() {\n        final String setupFolder = System.getProperty(BONITA_SETUP_FOLDER);\n        if (setupFolder != null) {\n            rootPath = Paths.get(setupFolder).getParent();\n        } else {\n            rootPath = Paths.get(\"..\");\n        }\n    }\n\n    private boolean fileExists(Path filePath) {\n        final boolean exists = Files.exists(filePath);\n        if (!exists) {\n            log.debug(\"File {} does not exist.\", filePath);\n        }\n        return exists;\n    }\n\n    private Path getPath(String partialPath) {\n        final String[] paths = partialPath.split(\"/\");\n        Path build = rootPath;\n        for (String path : paths) {\n            build = build.resolve(path);\n        }\n        return build;\n    }\n\n    private boolean isTomcatEnvironment() {\n        return fileExists(getPath(APPSERVER_FOLDERNAME + \"/bin/catalina.sh\"))\n                || fileExists(getPath(APPSERVER_FOLDERNAME + \"/bin/catalina.bat\"));\n    }\n\n    BundleConfigurator getConfigurator() throws PlatformException {\n        if (isTomcatEnvironment()) {\n            return new TomcatBundleConfigurator(rootPath);\n        } else {\n            log.info(\"No Application Server detected. You may need to manually configure the access to the database. \" +\n                    \"Only Tomcat 9.0.x is supported\");\n            return null;\n        }\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/command/configure/ConfigureCommand.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup.command.configure;\n\nimport org.apache.commons.cli.CommandLine;\nimport org.apache.commons.cli.Options;\nimport org.bonitasoft.platform.exception.PlatformException;\nimport org.bonitasoft.platform.setup.command.CommandUtils;\nimport org.bonitasoft.platform.setup.command.PlatformSetupCommand;\n\n/**\n * @author Baptiste Mesta\n */\npublic class ConfigureCommand extends PlatformSetupCommand {\n\n    public ConfigureCommand() {\n        super(\"configure\",\n                \"Configure a Bonita bundle to use your specific database configuration (defined in database.properties or via command line parameters)\",\n                CommandUtils.getFileContentFromClassPath(\"configure_header.txt\"),\n                CommandUtils.getFileContentFromClassPath(\"configure_footer.txt\"));\n    }\n\n    @Override\n    public void execute(Options options, CommandLine commandLine) throws PlatformException {\n        BundleConfigurator bundleConfigurator = createBundleResolver().getConfigurator();\n        if (bundleConfigurator != null) {\n            bundleConfigurator.configureApplicationServer();\n        }\n    }\n\n    BundleResolver createBundleResolver() {\n        return new BundleResolver();\n    }\n}\n"
  },
  {
    "path": "platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/command/configure/DatabaseConfiguration.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup.command.configure;\n\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.Properties;\n\nimport lombok.Getter;\nimport org.bonitasoft.platform.exception.PlatformException;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class DatabaseConfiguration {\n\n    public static final String H2_DB_VENDOR = \"h2\";\n    private static final String H2_DATABASE_DIR = \"${h2.database.dir}\";\n\n    @Getter\n    private String dbVendor;\n    @Getter\n    private String nonXaDriverClassName;\n    @Getter\n    private String xaDriverClassName;\n    @Getter\n    private String xaDataSourceFactory;\n    @Getter\n    private String databaseUser;\n    @Getter\n    private String databasePassword;\n    @Getter\n    private String databaseName;\n    @Getter\n    private String serverName = \"\";\n    @Getter\n    private String serverPort = \"\";\n    @Getter\n    private String url;\n    @Getter\n    private String testQuery;\n    @Getter\n    private Integer connectionPoolInitialSize;\n    @Getter\n    private Integer connectionPoolMaxTotal;\n    @Getter\n    private Integer connectionPoolMaxIdle;\n    @Getter\n    private Integer connectionPoolMinIdle;\n    private PropertyReader propertyReader;\n\n    public DatabaseConfiguration(String prefix, Properties properties, Path rootPath) throws PlatformException {\n        propertyReader = new PropertyReader(properties);\n        dbVendor = getMandatoryProperty(prefix + \"db.vendor\");\n\n        nonXaDriverClassName = getMandatoryProperty(dbVendor + \".nonXaDriver\");\n        xaDriverClassName = getMandatoryProperty(dbVendor + \".xaDriver\");\n        xaDataSourceFactory = getMandatoryProperty(dbVendor + \".xaDSFactory\");\n        databaseName = getMandatoryProperty(prefix + \"db.database.name\");\n        url = getMandatoryProperty(dbVendor + \".\" + prefix + \"url\");\n        // Configuration for H2 is a little different from other DB vendors:\n        if (H2_DB_VENDOR.equals(dbVendor)) {\n            String h2DatabaseDir = getMandatoryProperty(\"h2.database.dir\");\n            Path h2DatabasePath = Paths.get(h2DatabaseDir);\n            if (h2DatabasePath.isAbsolute() || h2DatabaseDir.startsWith(\"${\")) {\n                url = url.replace(H2_DATABASE_DIR, h2DatabasePath.normalize().toString());\n            } else {\n                // generate absolute path\n                url = url.replace(H2_DATABASE_DIR,\n                        rootPath.resolve(\"setup\").resolve(h2DatabaseDir).toAbsolutePath().normalize().toString());\n            }\n            // h2 path on windows must have forward slashes (H2 convention):\n            url = url.replace(\"\\\\\", \"/\");\n        } else {\n            serverName = getMandatoryProperty(prefix + \"db.server.name\");\n            url = url.replace(\"${\" + prefix + \"db.server.name}\", serverName);\n            serverPort = getMandatoryProperty(prefix + \"db.server.port\");\n            url = url.replace(\"${\" + prefix + \"db.server.port}\", serverPort);\n        }\n        url = url.replace(\"${\" + prefix + \"db.database.name}\", databaseName);\n        databaseUser = getMandatoryProperty(prefix + \"db.user\");\n        databasePassword = getMandatoryProperty(prefix + \"db.password\");\n        testQuery = getMandatoryProperty(dbVendor + \".\" + prefix + \"testQuery\");\n        connectionPoolInitialSize = getMandatoryIntegerProperty(prefix + \"connection-pool.initialSize\");\n        connectionPoolMaxTotal = getMandatoryIntegerProperty(prefix + \"connection-pool.maxTotal\");\n        connectionPoolMaxIdle = getMandatoryIntegerProperty(prefix + \"connection-pool.maxIdle\");\n        connectionPoolMinIdle = getMandatoryIntegerProperty(prefix + \"connection-pool.minIdle\");\n    }\n\n    private String getMandatoryProperty(String s) throws PlatformException {\n        return propertyReader.getPropertyAndFailIfNull(s);\n    }\n\n    private Integer getMandatoryIntegerProperty(String s) throws PlatformException {\n        var value = propertyReader.getPropertyAndFailIfNull(s);\n        try {\n            return Integer.parseInt(value);\n        } catch (NumberFormatException e) {\n            throw new PlatformException(String.format(\"Invalid integer value '%s' for property '%s'\", value, s));\n        }\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/command/configure/PropertyLoader.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup.command.configure;\n\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Properties;\n\nimport org.bonitasoft.platform.exception.PlatformException;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class PropertyLoader {\n\n    private final List<String> propertyFiles;\n\n    public PropertyLoader(String... propertyFiles) {\n        this.propertyFiles = Arrays.asList(propertyFiles);\n    }\n\n    public PropertyLoader() {\n        this(\"/internal.properties\", \"/database.properties\");\n    }\n\n    public Properties loadProperties() throws PlatformException {\n        final Properties properties = new Properties();\n        for (String propertyFile : propertyFiles) {\n            try {\n                properties.load(this.getClass().getResourceAsStream(propertyFile));\n            } catch (IOException e) {\n                throw new PlatformException(\"Error reading configuration file \" + propertyFile +\n                        \". Please make sure the file is present at the root of the Platform Setup Tool folder, and that is has not been moved of deleted\",\n                        e);\n            }\n        }\n        return properties;\n    }\n}\n"
  },
  {
    "path": "platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/command/configure/PropertyReader.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup.command.configure;\n\nimport java.util.Properties;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.platform.exception.PlatformException;\n\n@Slf4j\npublic class PropertyReader {\n\n    private final Properties properties;\n\n    public PropertyReader(Properties properties) {\n        this.properties = properties;\n    }\n\n    public String getPropertyAndFailIfNull(String propertyName) throws PlatformException {\n        // Any property value can be overridden by system property with the same name:\n        final String sysPropValue = System.getProperty(propertyName);\n        if (sysPropValue != null) {\n            log.info(\"System property '{}' set to '{}', overriding value from file database.properties.\",\n                    propertyName, sysPropValue);\n            return sysPropValue.trim();\n        }\n\n        final String property = properties.getProperty(propertyName);\n        if (property == null) {\n            throw new PlatformException(\n                    \"Mandatory property '\" + propertyName + \"' is missing.\" +\n                            \" Ensure you did not remove lines from file 'database.properties' (neither from file 'internal.properties')\"\n                            + \" and that the line is NOT commented out with a '#' character at start of line.\");\n        }\n        return property.trim();\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/command/configure/TomcatBundleConfigurator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup.command.configure;\n\nimport java.io.File;\nimport java.nio.file.Path;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.regex.Matcher;\n\nimport org.bonitasoft.platform.exception.PlatformException;\nimport org.bonitasoft.platform.setup.PlatformSetup;\n\n/**\n * @author Emmanuel Duchastenier\n */\nclass TomcatBundleConfigurator extends BundleConfigurator {\n\n    private static final String TOMCAT_BACKUP_FOLDER = \"tomcat-backups\";\n\n    TomcatBundleConfigurator(Path rootPath) throws PlatformException {\n        super(rootPath);\n    }\n\n    @Override\n    protected String getBundleName() {\n        return \"Tomcat\";\n    }\n\n    @Override\n    public void configureApplicationServer() throws PlatformException {\n        loadProperties();\n\n        final String dbVendor = standardConfiguration.getDbVendor();\n        final String bdmDbVendor = bdmConfiguration.getDbVendor();\n        final Path setEnvUnixFile = getPathUnderAppServer(\"bin/setenv.sh\", true);\n        final Path setEnvWindowsFile = getPathUnderAppServer(\"bin/setenv.bat\", true);\n        final Path bonitaXmlFile = getPathUnderAppServer(\"conf/Catalina/localhost/bonita.xml\", false);\n        final File bonitaDbDriverFile = getDriverFile(dbVendor);\n        final File bdmDriverFile = getDriverFile(bdmDbVendor);\n\n        try {\n            createBackupFolderIfNecessary(\"setup/\" + TOMCAT_BACKUP_FOLDER);\n\n            // 1. update setenv(.sh|.bat):\n            String newContent = readContentFromFile(getTemplateFolderPath(\"setenv.bat\"));\n            newContent = updateSetEnvFile(newContent, dbVendor, PlatformSetup.BONITA_DB_VENDOR_PROPERTY);\n            newContent = updateSetEnvFile(newContent, bdmDbVendor, PlatformSetup.BONITA_BDM_DB_VENDOR_PROPERTY);\n            backupAndReplaceContentIfNecessary(setEnvWindowsFile, newContent,\n                    \"Setting Bonita internal database vendor to '\" + dbVendor\n                            + \"' and Business Data database vendor to '\" + bdmDbVendor\n                            + \"' in 'setenv.bat' file\");\n\n            newContent = readContentFromFile(getTemplateFolderPath(\"setenv.sh\"));\n            newContent = updateSetEnvFile(newContent, dbVendor, PlatformSetup.BONITA_DB_VENDOR_PROPERTY);\n            newContent = updateSetEnvFile(newContent, bdmDbVendor, PlatformSetup.BONITA_BDM_DB_VENDOR_PROPERTY);\n            backupAndReplaceContentIfNecessary(setEnvUnixFile, newContent,\n                    \"Setting Bonita internal database vendor to '\" + dbVendor\n                            + \"' and Business Data database vendor to '\" + bdmDbVendor\n                            + \"' in 'setenv.sh' file\");\n\n            //2. update bonita.xml:\n            newContent = readContentFromFile(getTemplateFolderPath(\"bonita.xml\"));\n            newContent = updateBonitaXmlFile(newContent, standardConfiguration, \"ds1\");\n            newContent = updateBonitaXmlFile(newContent, bdmConfiguration, \"ds2\");\n            backupAndReplaceContentIfNecessary(bonitaXmlFile, newContent,\n                    \"Configuring file 'conf/Catalina/localhost/bonita.xml' with your DB values for Bonita internal database on '\"\n                            + dbVendor\n                            + \"' and for Business Data database on '\" + bdmDbVendor + \"'\");\n\n            //3. copy the JDBC drivers:\n            final Path srcDriverFile = bonitaDbDriverFile.toPath();\n            final Path targetBonitaDbDriverFile = getPathUnderAppServer(\"lib/bonita\", true)\n                    .resolve(srcDriverFile.getFileName());\n            copyDatabaseDriversIfNecessary(srcDriverFile, targetBonitaDbDriverFile, dbVendor);\n            final Path srcBdmDriverFile = bdmDriverFile.toPath();\n            final Path targetBdmDriverFile = getPathUnderAppServer(\"lib/bonita\", true)\n                    .resolve(srcBdmDriverFile.getFileName());\n            copyDatabaseDriversIfNecessary(srcBdmDriverFile, targetBdmDriverFile, bdmDbVendor);\n            removeH2DriverIfNecessary(targetBonitaDbDriverFile.getParent(), dbVendor, bdmDbVendor);\n            LOGGER.info(\"Tomcat auto-configuration complete.\");\n        } catch (PlatformException e) {\n            restorePreviousConfiguration(setEnvUnixFile, setEnvWindowsFile, bonitaXmlFile);\n            throw e;\n        }\n    }\n\n    private String updateBonitaXmlFile(String content, DatabaseConfiguration configuration,\n            final String datasourceAlias) {\n        Map<String, String> replacements = new HashMap<>(15);\n        replacements.put(\"@@\" + datasourceAlias + \".database_connection_user@@\",\n                Matcher.quoteReplacement(configuration.getDatabaseUser()));\n        replacements.put(\"@@\" + datasourceAlias + \".database_connection_password@@\",\n                Matcher.quoteReplacement(configuration.getDatabasePassword()));\n        replacements.put(\"@@\" + datasourceAlias + \".driver_class_name@@\", configuration.getNonXaDriverClassName());\n        replacements.put(\"@@\" + datasourceAlias + \".xa.driver_class_name@@\", configuration.getXaDriverClassName());\n        replacements.put(\"@@\" + datasourceAlias + \".xa_datasource_factory@@\", configuration.getXaDataSourceFactory());\n        replacements.put(\"@@\" + datasourceAlias + \".database_connection_url@@\",\n                getDatabaseConnectionUrlForXmlFile(configuration));\n        replacements.put(\"@@\" + datasourceAlias + \"_database_server_name@@\", configuration.getServerName());\n        replacements.put(\"@@\" + datasourceAlias + \"_database_port_number@@\", configuration.getServerPort());\n        replacements.put(\"@@\" + datasourceAlias + \"_database_database_name@@\",\n                Matcher.quoteReplacement(configuration.getDatabaseName()));\n        replacements.put(\"@@\" + datasourceAlias + \".database_test_query@@\", configuration.getTestQuery());\n        replacements.put(\"@@\" + datasourceAlias + \"_connection_pool_initialSize@@\",\n                String.valueOf(configuration.getConnectionPoolInitialSize()));\n        replacements.put(\"@@\" + datasourceAlias + \"_connection_pool_maxTotal@@\",\n                String.valueOf(configuration.getConnectionPoolMaxTotal()));\n        replacements.put(\"@@\" + datasourceAlias + \"_connection_pool_minIdle@@\",\n                String.valueOf(configuration.getConnectionPoolMinIdle()));\n        replacements.put(\"@@\" + datasourceAlias + \"_connection_pool_maxIdle@@\",\n                String.valueOf(configuration.getConnectionPoolMaxIdle()));\n        return replaceValues(content, replacements);\n    }\n\n    private String updateSetEnvFile(String setEnvFileContent, String dbVendor, final String systemPropertyName) {\n        final Map<String, String> replacementMap = Collections.singletonMap(\"-D\" + systemPropertyName + \"=.*\\\"\",\n                \"-D\" + systemPropertyName + \"=\" + dbVendor + \"\\\"\");\n\n        return replaceValues(setEnvFileContent, replacementMap);\n    }\n\n    void restorePreviousConfiguration(Path setEnvUnixFile, Path setEnvWindowsFile, Path bonitaXmlFile)\n            throws PlatformException {\n        LOGGER.warn(\"Problem encountered, restoring previous configuration\");\n        // undo file copies:\n        restoreOriginalFile(bonitaXmlFile);\n        restoreOriginalFile(setEnvUnixFile);\n        restoreOriginalFile(setEnvWindowsFile);\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/dbconfig/DataSourceConfig.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup.dbconfig;\n\nimport org.springframework.context.annotation.PropertySource;\nimport org.springframework.stereotype.Component;\n\n/**\n * Used by Spring to create an instance of {@link javax.sql.DataSource} using parameters provided in properties.\n */\n@Component\n@PropertySource(value = { \"classpath:/database.properties\", \"classpath:/internal.properties\" })\npublic class DataSourceConfig {\n\n}\n"
  },
  {
    "path": "platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/jndi/MemoryJNDISetup.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup.jndi;\n\nimport javax.naming.Context;\nimport javax.naming.NamingException;\nimport javax.sql.DataSource;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jndi.JndiTemplate;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class MemoryJNDISetup implements DisposableBean, InitializingBean {\n\n    public static final String BONITA_NON_MANAGED_DS_JNDI_NAME = \"java:comp/env/bonitaSequenceManagerDS\";\n    private final Logger logger = LoggerFactory.getLogger(MemoryJNDISetup.class.getSimpleName());\n\n    private final JndiTemplate jndiTemplate;\n    private final DataSource datasource;\n\n    @Autowired\n    public MemoryJNDISetup(final DataSource datasource) throws NamingException {\n        super();\n        this.datasource = datasource;\n        System.setProperty(Context.INITIAL_CONTEXT_FACTORY,\n                \"org.bonitasoft.platform.setup.jndi.SimpleMemoryContextFactory\");\n        System.setProperty(Context.URL_PKG_PREFIXES, \"org.bonitasoft.platform.setup.jndi\");\n        jndiTemplate = new JndiTemplate();\n    }\n\n    public void afterPropertiesSet() throws NamingException {\n        logger.info(\"Binding \" + BONITA_NON_MANAGED_DS_JNDI_NAME + \" @ \" + datasource.toString());\n        jndiTemplate.bind(BONITA_NON_MANAGED_DS_JNDI_NAME, datasource);\n    }\n\n    public void destroy() throws NamingException {\n        logger.info(\"Unbinding \" + BONITA_NON_MANAGED_DS_JNDI_NAME);\n        jndiTemplate.unbind(BONITA_NON_MANAGED_DS_JNDI_NAME);\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/jndi/SimpleMemoryContext.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup.jndi;\n\nimport java.util.Hashtable;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport javax.naming.Binding;\nimport javax.naming.Context;\nimport javax.naming.Name;\nimport javax.naming.NameAlreadyBoundException;\nimport javax.naming.NameClassPair;\nimport javax.naming.NameNotFoundException;\nimport javax.naming.NameParser;\nimport javax.naming.NamingEnumeration;\nimport javax.naming.NamingException;\n\n/**\n * A JNDI context implementation that uses the memory as a dictionary of objects.\n */\npublic class SimpleMemoryContext implements Context {\n\n    private static final String NOT_SUPPORTED_YET = \"Not supported yet.\";\n\n    private final Map<String, Object> dictionary = new ConcurrentHashMap<String, Object>();\n\n    public void clear() {\n        dictionary.clear();\n    }\n\n    @Override\n    public Object lookup(final Name name) throws NamingException {\n        return lookup(name.toString());\n    }\n\n    @Override\n    public Object lookup(final String name) throws NamingException {\n        if (dictionary.containsKey(name)) {\n            return dictionary.get(name);\n        }\n        throw new NameNotFoundException(\"Name \" + name + \" is not bound !\");\n    }\n\n    @Override\n    public void bind(final Name name, final Object o) throws NamingException {\n        bind(name.toString(), o);\n    }\n\n    @Override\n    public void bind(final String name, final Object o) throws NamingException {\n        if (dictionary.containsKey(name)) {\n            throw new NameAlreadyBoundException(\"Name \" + name + \" already bound!\");\n        }\n        rebind(name, o);\n    }\n\n    @Override\n    public void rebind(final Name name, final Object o) {\n        rebind(name.toString(), o);\n    }\n\n    @Override\n    public void rebind(final String name, final Object o) {\n        dictionary.put(name, o);\n    }\n\n    @Override\n    public void unbind(final Name name) throws NamingException {\n        unbind(name.toString());\n    }\n\n    @Override\n    public void unbind(final String name) throws NamingException {\n        if (!dictionary.containsKey(name)) {\n            throw new NameNotFoundException(\"No such name \" + name + \" is bound!\");\n        }\n        dictionary.remove(name);\n    }\n\n    @Override\n    public void rename(final Name oldName, final Name newName) throws NamingException {\n        rename(oldName.toString(), newName.toString());\n    }\n\n    @Override\n    public void rename(final String oldName, final String newName) throws NamingException {\n        final Object object = lookup(oldName);\n        bind(newName, object);\n        unbind(oldName);\n    }\n\n    @Override\n    public NamingEnumeration<NameClassPair> list(final Name name) {\n        throw new UnsupportedOperationException(NOT_SUPPORTED_YET);\n    }\n\n    @Override\n    public NamingEnumeration<NameClassPair> list(final String string) {\n        throw new UnsupportedOperationException(NOT_SUPPORTED_YET);\n    }\n\n    @Override\n    public NamingEnumeration<Binding> listBindings(final Name name) {\n        throw new UnsupportedOperationException(NOT_SUPPORTED_YET);\n    }\n\n    @Override\n    public NamingEnumeration<Binding> listBindings(final String string) {\n        throw new UnsupportedOperationException(NOT_SUPPORTED_YET);\n    }\n\n    @Override\n    public void destroySubcontext(final Name name) {\n        destroySubcontext(name.toString());\n    }\n\n    @Override\n    public void destroySubcontext(final String name) {\n        dictionary.remove(name);\n    }\n\n    @Override\n    public Context createSubcontext(final Name name) throws NamingException {\n        return createSubcontext(name.toString());\n    }\n\n    @Override\n    public Context createSubcontext(final String name) throws NamingException {\n        final Context subContext = new SimpleMemoryContext();\n        bind(name, subContext);\n        return subContext;\n    }\n\n    @Override\n    public Object lookupLink(final Name name) {\n        throw new UnsupportedOperationException(NOT_SUPPORTED_YET);\n    }\n\n    @Override\n    public Object lookupLink(final String string) {\n        throw new UnsupportedOperationException(NOT_SUPPORTED_YET);\n    }\n\n    @Override\n    public NameParser getNameParser(final Name name) {\n        throw new UnsupportedOperationException(NOT_SUPPORTED_YET);\n    }\n\n    @Override\n    public NameParser getNameParser(final String name) {\n        return new SimpleNameParser(name);\n    }\n\n    @Override\n    public Name composeName(final Name name, final Name name1) {\n        throw new UnsupportedOperationException(NOT_SUPPORTED_YET);\n    }\n\n    @Override\n    public String composeName(final String string, final String string1) {\n        throw new UnsupportedOperationException(NOT_SUPPORTED_YET);\n    }\n\n    @Override\n    public Object addToEnvironment(final String string, final Object o) {\n        throw new UnsupportedOperationException(NOT_SUPPORTED_YET);\n    }\n\n    @Override\n    public Object removeFromEnvironment(final String string) {\n        throw new UnsupportedOperationException(NOT_SUPPORTED_YET);\n    }\n\n    @Override\n    public Hashtable<?, ?> getEnvironment() {\n        throw new UnsupportedOperationException(NOT_SUPPORTED_YET);\n    }\n\n    @Override\n    public void close() {\n        // Thread.dumpStack();\n        // clear();\n    }\n\n    @Override\n    public String getNameInNamespace() {\n        throw new UnsupportedOperationException(NOT_SUPPORTED_YET);\n    }\n}\n"
  },
  {
    "path": "platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/jndi/SimpleMemoryContextFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup.jndi;\n\nimport java.util.Hashtable;\n\nimport javax.naming.Context;\nimport javax.naming.spi.InitialContextFactory;\n\n/**\n * A factory of a naming context that uses the memory as dictionary of objects. Useful to tests\n * objects using JNDI to get dependencies.\n */\npublic class SimpleMemoryContextFactory implements InitialContextFactory {\n\n    private static final SimpleMemoryContext context = new SimpleMemoryContext();\n\n    @Override\n    public Context getInitialContext(final Hashtable<?, ?> environment) {\n        return context;\n    }\n}\n"
  },
  {
    "path": "platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/jndi/SimpleNameParser.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup.jndi;\n\nimport javax.naming.CompositeName;\nimport javax.naming.Name;\nimport javax.naming.NameParser;\nimport javax.naming.NamingException;\n\npublic class SimpleNameParser implements NameParser {\n\n    public SimpleNameParser(final String name) {\n        // TODO Auto-generated constructor stub\n    }\n\n    @Override\n    public Name parse(final String name) throws NamingException {\n        return new CompositeName(name);\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-setup/src/main/resources/application.properties",
    "content": "db.vendor=${sysprop.bonita.db.vendor:h2}\n"
  },
  {
    "path": "platform/platform-setup/src/main/resources/banner.txt",
    "content": " ____              _ _\n|  _ \\            (_) |\n| |_) | ___  _ __  _| |_ __ _\n|  _ < / _ \\| '_ \\| | __/ _` |\n| |_) | (_) | | | | | || (_| |\n|____/ \\___/|_| |_|_|\\__\\__,_|\n\n(Platform Setup @version@)\n"
  },
  {
    "path": "platform/platform-setup/src/main/resources/configure_footer.txt",
    "content": "Remember that this command only works on a Tomcat bundle provided by Bonitasoft.\nDuring the `configure` script execution, the setup tool verifies that the parent folder is a Tomcat bundle.\nIf it is so, it:\n   - sets db vendor + bdm db vendor in file server/bin/setenv (.sh|.bat)\n   - sets all database configuration in file server/conf/Catalina/localhost/bonita.xml\n   - copies the required drivers from setup/lib/ to server/lib/bonita/\n\n\nADVANCED CONFIGURATION WARNING:\nIf you need to edit the server configuration, do not directly edit Tomcat files anymore, as they will be overwritten by the templates used by the setup tool.\nTherefore, if you need to finely tune the server configuration, modify the following Tomcat templates:\n    - setup/tomcat-templates/bonita.xml\n    - setup/tomcat-templates/setenv.sh\n    - setup/tomcat-templates/setenv.bat\n"
  },
  {
    "path": "platform/platform-setup/src/main/resources/configure_header.txt",
    "content": "Important: \nFor basic bundle installations and starts, launch the `bonita-start`(.bat|.sh) script, at top level of Bonita bundles.\nIt will create the database tables, configure the server using information from `database.properties`, and then start the server for you.\nSo you normally don't need to run any of this `setup` script manually.\n\nHowever, for specific needs, here are details about `setup.(bat|sh) configure`:"
  },
  {
    "path": "platform/platform-setup/src/main/resources/global_usage_footer.txt",
    "content": "All commands here interact with the database configured in the file database.properties.\nBy default, the bundle is configured with an embedded H2 database, suitable for development and testing, not suitable for production purposes.\nTo switch to another database vendor, change values in the file database.properties or use system properties.\nYou can specify system properties directly from the command line, using `-Dproperty=value`.\nFor example, to change the database name and initialize the database, use:\n  `setup.bat init -Ddb.database.name=myDatabase`\n\nHere is the list of available properties:\n * db.vendor\n * db.server.name\n * db.server.port\n * db.database.name\n * db.user\n * db.password\n * bdm.db.vendor\n * bdm.db.server.name\n * bdm.db.server.port\n * bdm.db.database.name\n * bdm.db.user\n * bdm.db.password\n\nTo know more about the setup tool, go to the Documentation web site, and search with the keywords \"platform setup\"\n"
  },
  {
    "path": "platform/platform-setup/src/main/resources/global_usage_header.txt",
    "content": "Important:\nFor basic bundle installations and starts, launch the `bonita-start`(.bat|.sh) script, at top level of Bonita bundles.\nIt will create the database tables, configure the server using information from `database.properties`, and then start the server for you.\nSo you normally don't need to run this `setup` script manually.\n\nHowever, for specific needs, here are the details of use:"
  },
  {
    "path": "platform/platform-setup/src/main/resources/init_footer.txt",
    "content": "Run this init command once to create database structure before launching Bonita.\n\nAt execution, it:\n * creates database structure (tables) on target database.\n * writes all configuration files found in `platform_conf/initial` and licenses found in `platform_conf/licenses` in the database.\n"
  },
  {
    "path": "platform/platform-setup/src/main/resources/init_header.txt",
    "content": "Important: \nFor basic bundle installations and starts, launch the `bonita-start`(.bat|.sh) script, at top level of Bonita bundles.\nIt will create the database tables, configure the server using information from `database.properties`, and then start the server for you.\nSo you normally don't need to run any of this `setup` script manually.\n\nHowever, for specific needs, here are details about `setup.(bat|sh) init`:"
  },
  {
    "path": "platform/platform-setup/src/main/resources/pull.txt",
    "content": "At execution, it:\n * writes the configuration currently in database in the folder platform_conf/current.\n * writes license files found in the database to the folder platform_conf/licenses (Subscription editions only).\n * overwrites any previous configuration and license files previously in platform_conf/current folder.\n\nImportant:\nThis step is mandatory before updating the configuration to ensure database consistency.\nFor example, when a tenant is created by Bonita, its related configuration files need to be extracted before you can edit the configuration."
  },
  {
    "path": "platform/platform-setup/src/main/resources/push.txt",
    "content": "At execution, it:\n* ensures no configuration folder has been deleted locally before pushing. Otherwise it stops and fails.\n* writes all configuration files found in platform_conf/current in database.\n* writes license files found in platform_conf/licenses folder in database (Subscription editions only).\n* overwrites any previous configuration in the database.\n\nImportant:\n- Only use this command once the current configuration has been pulled from the database with `setup.(bat|sh) pull` script and modified locally.\n- Those modifications will be applied only after you restart Bonita."
  },
  {
    "path": "platform/platform-setup/src/main/standalone/README.md",
    "content": "Bonita Platform Setup tool\n===\n\nWhat it does?\n---\n**Bonita Platform Setup tool** sets up Bonita Platform before a Bonita runtime can be run:\n\n* it creates the structure of the database (tables, primary / foreign keys, indices, ...)\n* it inserts the default minimum data \n* it inserts the Bonita Engine + Bonita Portal minimum configuration in database\n* it also allows to automatically configure a Bonita Tomcat bundle to run on the right database\n\nRequirements\n---\n>     Java JDK 8 / 11\n\nRunning bonita-platform-setup\n---\nTo run the tool:\n\n* extract the Zip file\n* configure access to the database in file `database.properties` at the root directory.\n* run `./setup.sh [init | push | pull | configure | help]`\n\nMore details\n---\nSee [the dedicated page](https://documentation.ofelia.com/bonita/latest/BonitaBPM_platform_setup) for more info\nabout the Bonita Platform Setup tool."
  },
  {
    "path": "platform/platform-setup/src/main/standalone/database.properties",
    "content": "####################################################################################\n#\n# Modify the following values to suit your database needs.\n# Fore more information, see file ../HOW_TO_CONFIGURE_AND_RUN.txt\n#\n####################################################################################\n\n\n#########################################\n# Bonita database properties\n#########################################\n\n# Valid values for Community edition: h2, postgres\n# Valid values for Enterprise editions: h2, postgres, sqlserver, oracle, mysql\ndb.vendor=h2\n# when using h2, no server or port setting is needed since connexion is made using file protocol mode using relative directory:\ndb.server.name=SERVER_NAME\ndb.server.port=SERVER_PORT\n# if your database name contains a backslash (\\) character, you must double it (\\\\):\ndb.database.name=bonita_journal.db\ndb.user=sa\n# if your database password contains a backslash (\\) character, you must double it (\\\\):\ndb.password=\n\n###################################\n# Business Data database properties\n###################################\n# Valid values for Community edition: h2, postgres\n# Valid values for Enterprise editions: h2, postgres, sqlserver, oracle, mysql\nbdm.db.vendor=h2\nbdm.db.server.name=SERVER_NAME\nbdm.db.server.port=SERVER_PORT\nbdm.db.database.name=business_data.db\nbdm.db.user=sa\nbdm.db.password=\n\n\n# IMPORTANT NOTE regarding H2 database:\n# in case you move whole setup folder to another directory, you must change property below\n# to point to original folder containing h2 database folder\n# new value can be relative or absolute since it still points to the right folder\n# WARNING for Windows users: keep forward slashes like below (instead of backslashes):\nh2.database.dir=../h2_database\n"
  },
  {
    "path": "platform/platform-setup/src/main/standalone/internal.properties",
    "content": "##################################################################################\n#                                                                                #\n#       properties below must NOT be modified unless specific requirements       #\n#                                                                                #\n##################################################################################\n#                       /!\\          WARNING          /!\\                        #\n#       Any value containing a backslash (\\) character MUST be doubled (\\\\)      #\n##################################################################################\n\nh2.nonXaDriver=org.h2.Driver\nh2.xaDriver=org.h2.jdbcx.JdbcDataSource\nh2.xaDSFactory=org.h2.jdbcx.JdbcDataSourceFactory\n\npostgres.nonXaDriver=org.postgresql.Driver\npostgres.xaDriver=org.postgresql.xa.PGXADataSource\npostgres.xaDSFactory=org.postgresql.xa.PGXADataSourceFactory\n\n###########################\n## Bonita database\n###########################\n\n# h2 properties\nh2.url=jdbc:h2:file:${h2.database.dir}/${db.database.name};DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTO_SERVER=TRUE;\nh2.testQuery=SELECT 1\n\n# postgres properties\npostgres.url=jdbc:postgresql://${db.server.name}:${db.server.port}/${db.database.name}\npostgres.testQuery=SELECT 1\n\n\n# spring properties\nspring.datasource.username=${db.user}\nspring.datasource.password=${db.password}\nspring.datasource.driver-class-name=${${db.vendor}.nonXaDriver}\nspring.datasource.url=${${db.vendor}.url}\n\n# The initial number of connections when the connection pool starts.\nconnection-pool.initialSize=8\n# The maximum number of active connections that can be allocated from this pool at the same time.\nconnection-pool.maxTotal=50\n# The minimum number of active connections that always established after pool created and connection has reached this size.\nconnection-pool.minIdle=8\n# The maximum number of connections that should be kept in the pool at all times.\nconnection-pool.maxIdle=16\n\n###########################\n# Business Data database\n###########################\n\n# h2 properties\nh2.bdm.url=jdbc:h2:file:${h2.database.dir}/${bdm.db.database.name};DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTO_SERVER=TRUE;\nh2.bdm.testQuery=SELECT 1\n\n# postgres properties\npostgres.bdm.url=jdbc:postgresql://${bdm.db.server.name}:${bdm.db.server.port}/${bdm.db.database.name}\npostgres.bdm.testQuery=SELECT 1\n\n# The initial number of connections when the connection pool starts.\nbdm.connection-pool.initialSize=4\n# The maximum number of active connections that can be allocated from this pool at the same time.\nbdm.connection-pool.maxTotal=20\n# The minimum number of active connections that always established after pool created and connection has reached this size.\nbdm.connection-pool.minIdle=4\n# The maximum number of connections that should be kept in the pool at all times.\nbdm.connection-pool.maxIdle=10\n"
  },
  {
    "path": "platform/platform-setup/src/main/standalone/lib/PUT_YOUR_JDBC_DRIVER_HERE",
    "content": ""
  },
  {
    "path": "platform/platform-setup/src/main/standalone/logback.xml",
    "content": "<configuration debug=\"false\" scan=\"false\">\n    <statusListener class=\"ch.qos.logback.core.status.NopStatusListener\" />\n\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->\n        <encoder>\n            <pattern>[%level] %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <logger name=\"org.bonitasoft\" level=\"INFO\" />\n    <logger name=\"org.bonitasoft.platform\" level=\"INFO\" />\n    <logger name=\"org.bonitasoft.platform.setup\" level=\"INFO\" />\n\n\n    <!-- SET level below to DEBUG for more information about errors: -->\n    <logger name=\"org.bonitasoft.platform.setup.PlatformSetupApplication\" level=\"${bonita.platform.setup.log:-WARN}\" />\n\n    <!--TRACE level displays all relevant jdbc transaction information-->\n    <!--<logger name=\"org.springframework\" level=\"TRACE\" />-->\n\n    <logger name=\"org.springframework\" level=\"WARN\" />\n    <logger name=\"org.springframework.jdbc\" level=\"INFO\" />\n    <logger name=\"org.springframework.jdbc.support\" level=\"WARN\" />\n\n    <!-- to avoid connection stacktrace (message is already logged in PlatformSetupApplication: -->\n    <logger name=\"org.apache.tomcat.jdbc.pool.ConnectionPool\" level=\"OFF\" />\n    <logger name=\"org.springframework.jdbc.datasource.init.ScriptUtils\" level=\"OFF\" />\n\n\n    <root level=\"WARN\">\n        <appender-ref ref=\"STDOUT\" />\n    </root>\n\n</configuration>\n"
  },
  {
    "path": "platform/platform-setup/src/main/standalone/setup.bat",
    "content": "@echo off\nsetlocal EnableDelayedExpansion\n\n:: Let's position into the folder containing this script:\nset CURRENTDIR=\"%cd%\"\nset BASEDIR=%~dp0\ncd %BASEDIR%\n\n:: Check if JAVA_CMD has been passed by start-bonita.bat:\nif not \"%JAVA_CMD%\" == \"\" goto gotJavaCmd\nset JAVA_CMD=\"java\"\n:gotJavaCmd\n\nset CFG_FOLDER=%BASEDIR%\\platform_conf\nset INITIAL_CFG_FOLDER=%CFG_FOLDER%\\initial\nset LIB_FOLDER=%BASEDIR%\\lib\n\nFOR /F \"tokens=1,* delims== eol=#\" %%A IN (database.properties) DO (set %%A=%%B)\nset BONITA_DATABASE=%db.vendor%\nset BONITA_BDM_DATABASE=%bdm.db.vendor%\n\ncall :checkVendorSupported %BONITA_DATABASE%\nif ERRORLEVEL 1 (\n    exit /b 1\n)\ncall :checkVendorSupported %BONITA_BDM_DATABASE%\nif ERRORLEVEL 1 (\n    exit /b 1\n)\n\n\"%JAVA_CMD%\" -cp \"%BASEDIR%;%CFG_FOLDER%;%INITIAL_CFG_FOLDER%;%LIB_FOLDER%\\*\" ^\n    -Dsysprop.bonita.db.vendor=%BONITA_DATABASE% ^\n    -Dsysprop.bonita.bdm.db.vendor=%BONITA_BDM_DATABASE% ^\n    org.bonitasoft.platform.setup.PlatformSetupApplication %*\n\nif ERRORLEVEL 1 (\n    exit /b 1\n)\n\n:: restore previous folder:\ncd %CURRENTDIR%\n\n:: exit script with success\nexit /b 0\n\n\nREM Function that exits with an error message if the vendor is not supported\nREM - first argument is the database vendor value to check\n:checkVendorSupported\nset DB_VENDOR=%1\nset IS_SUPPORTED=false\nfor %%d in (h2 postgres) do (\n    if \"%%d\" == \"!DB_VENDOR!\" (\n        set IS_SUPPORTED=true\n        goto :breakLoop\n    )\n)\n:breakLoop\nif \"!IS_SUPPORTED!\" == \"false\" (\n    echo ERROR: Unsupported database vendor ^(valid values are h2, postgres^).\n    echo For access to additional databases ^(oracle, mysql, sqlserver^), please consider upgrading to the Enterprise Edition.\n    echo Please update file %BASEDIR%database.properties to set a valid value.\n    exit /b 1\n)\nexit /b 0\n"
  },
  {
    "path": "platform/platform-setup/src/main/standalone/setup.sh",
    "content": "#!/bin/sh\nset -e\n\n# Function that exits with an error message if the vendor is not supported\n# - first argument is the database vendor value to check\ncheck_vendor_supported() {\n  db_vendor=$1\n  is_supported=false\n  # supported databases:\n  set -- h2 postgres\n  for db in \"$@\"; do\n    if [ \"$db\" = \"$db_vendor\" ]; then\n      is_supported=true\n      break\n    fi\n  done\n  if [ \"$is_supported\" = false ]; then\n    echo \"ERROR: Unsupported database vendor (valid values are h2, postgres).\"\n    echo \"For access to additional databases (oracle, mysql, sqlserver), please consider upgrading to the Enterprise Edition.\"\n    echo \"Please update file ${BASEDIR}/database.properties to set a valid value.\"\n    exit 1\n  fi\n}\n\n# Let's position into the folder containing this script:\nBASEDIR=$(cd \"$(dirname \"$(dirname \"$0\")/..\")\" && pwd -P)\ncd \"${BASEDIR}\"\n\n# JAVA_CMD is exported by start-bonita.sh, so that same Java command is used:\nJAVA_EXE=${JAVA_CMD:-java}\n\nCFG_FOLDER=${BASEDIR}/platform_conf\nINITIAL_CFG_FOLDER=$CFG_FOLDER/initial\n\nfor lib in lib/*.jar; do\n  LIBS_CP=\"${LIBS_CP}:${lib}\"\ndone\n\nBONITA_DATABASE=$(grep '^db.vendor=' database.properties | sed -e 's/db.vendor=//g')\ncheck_vendor_supported \"$BONITA_DATABASE\"\n\nBONITA_BDM_DATABASE=$(grep '^bdm.db.vendor=' database.properties | sed -e 's/bdm.db.vendor=//g')\ncheck_vendor_supported \"$BONITA_BDM_DATABASE\"\n\n\"${JAVA_EXE}\" -cp \"${BASEDIR}:${CFG_FOLDER}:${INITIAL_CFG_FOLDER}${LIBS_CP}\" \\\n    -Dsysprop.bonita.db.vendor=\"${BONITA_DATABASE}\" \\\n    -Dsysprop.bonita.bdm.db.vendor=\"${BONITA_BDM_DATABASE}\" \\\n    org.bonitasoft.platform.setup.PlatformSetupApplication \"$@\"\nCOD_RET=$?\nif [ ${COD_RET} -ne 0 ]; then\n  cd - 1>/dev/null\n  exit ${COD_RET}\nfi\n# restore previous folder:\ncd - 1>/dev/null\n"
  },
  {
    "path": "platform/platform-setup/src/main/standalone/tomcat-templates/bonita.xml",
    "content": "<?xml version='1.0' encoding='utf-8'?>\n<Context>\n\n    <!--  To not persist the session after reboot tomcat -->\n    <Manager pathname=\"\" />\n\n    <!-- #################################################################################################################### -->\n    <!-- ##############   BETWEEN THESE 2 SECTIONS IS A TEMPLATE MODIFIED BY BONITA SETUP TOOL.              ################ -->\n    <!-- #######   BELOW VALUES SURROUNDED BY '@@' MUST NOT BE TOUCHED, AS THEY ARE USED INTERNALLY BY SETUP TOOL.   ######## -->\n    <!-- #########      FOR OTHER VALUES, DO NOT CHANGE ANYTHING UNLESS YOU ARE SURE OF WHAT YOU ARE DOING.      ############ -->\n    <!-- ##############          YOU CAN ADD CUSTOM CONFIGURATION ABOVE AND BELOW THIS TEMPLATE.            ################# -->\n    <!-- #################################################################################################################### -->\n\n    <!-- ##################################################### -->\n    <!-- Configure Datasource for Bonita standard database -->\n    <!-- ##################################################### -->\n    <Resource name=\"RawBonitaDS\"\n              auth=\"Container\"\n              type=\"@@ds1.xa.driver_class_name@@\"\n              class=\"@@ds1.xa.driver_class_name@@\"\n              factory=\"@@ds1.xa_datasource_factory@@\"\n              description=\"Raw Bonita Datasource\"\n              closeMethod=\"close\"\n              loginTimeout=\"0\"\n              serverName=\"@@ds1_database_server_name@@\"\n              portNumber=\"@@ds1_database_port_number@@\"\n              port=\"@@ds1_database_port_number@@\"\n              databaseName=\"@@ds1_database_database_name@@\"\n              user=\"@@ds1.database_connection_user@@\"\n              password=\"@@ds1.database_connection_password@@\"\n              explicitUrl=\"true\"\n              url=\"@@ds1.database_connection_url@@\"\n              dataSourceURL=\"@@ds1.database_connection_url@@\" />\n\n    <Resource name=\"bonitaDS\"\n              uniqueName=\"jdbc/bonitaDSXA\"\n              auth=\"Container\"\n              factory=\"org.jboss.narayana.tomcat.jta.TransactionalDataSourceFactory\"\n              transactionManager=\"TransactionManager\"\n              type=\"javax.sql.XADataSource\"\n              initialSize=\"@@ds1_connection_pool_initialSize@@\"\n              maxTotal=\"@@ds1_connection_pool_maxTotal@@\"\n              minIdle=\"@@ds1_connection_pool_minIdle@@\"\n              maxIdle=\"@@ds1_connection_pool_maxIdle@@\"\n              defaultAutoCommit=\"false\"\n              validationQuery=\"@@ds1.database_test_query@@\"\n              removeAbandonedOnBorrow=\"true\"\n              removeAbandonedOnMaintenance=\"true\"\n              logAbandoned=\"false\"\n              testOnBorrow=\"true\"\n              timeBetweenEvictionRunsMillis=\"60000\"\n              minEvictableIdleTimeMillis=\"600000\"\n              xaDataSource=\"RawBonitaDS\" />\n\n    <Resource name=\"bonitaSequenceManagerDS\"\n              auth=\"Container\"\n              type=\"javax.sql.DataSource\"\n              factory=\"org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory\"\n              maxTotal=\"5\"\n              minIdle=\"1\"\n              maxIdle=\"5\"\n              maxWaitMillis=\"10000\"\n              initialSize=\"1\"\n              removeAbandonedOnBorrow=\"true\"\n              removeAbandonedOnMaintenance=\"true\"\n              logAbandoned=\"false\"\n              testOnBorrow=\"true\"\n              timeBetweenEvictionRunsMillis=\"60000\"\n              minEvictableIdleTimeMillis=\"600000\"\n              validationQuery=\"@@ds1.database_test_query@@\"\n              username=\"@@ds1.database_connection_user@@\"\n              password=\"@@ds1.database_connection_password@@\"\n              driverClassName=\"@@ds1.driver_class_name@@\"\n              url=\"@@ds1.database_connection_url@@\" />\n\n    <!-- ##################################################### -->\n    <!-- Configure Business Data Datasource -->\n    <!-- ##################################################### -->\n    <Resource name=\"RawBusinessDataDS\"\n              auth=\"Container\"\n              type=\"@@ds2.xa.driver_class_name@@\"\n              class=\"@@ds2.xa.driver_class_name@@\"\n              factory=\"@@ds2.xa_datasource_factory@@\"\n              description=\"Raw Bonita Business Data Datasource\"\n              closeMethod=\"close\"\n              loginTimeout=\"0\"\n              serverName=\"@@ds2_database_server_name@@\"\n              portNumber=\"@@ds2_database_port_number@@\"\n              port=\"@@ds2_database_port_number@@\"\n              databaseName=\"@@ds2_database_database_name@@\"\n              user=\"@@ds2.database_connection_user@@\"\n              password=\"@@ds2.database_connection_password@@\"\n              explicitUrl=\"true\"\n              url=\"@@ds2.database_connection_url@@\"\n              dataSourceURL=\"@@ds2.database_connection_url@@\" />\n\n    <Resource\n            name=\"BusinessDataDS\"\n            uniqueName=\"jdbc/BusinessDataDSXA\"\n            auth=\"Container\"\n            factory=\"org.jboss.narayana.tomcat.jta.TransactionalDataSourceFactory\"\n            transactionManager=\"TransactionManager\"\n            type=\"javax.sql.XADataSource\"\n            initialSize=\"@@ds2_connection_pool_initialSize@@\"\n            maxTotal=\"@@ds2_connection_pool_maxTotal@@\"\n            minIdle=\"@@ds2_connection_pool_minIdle@@\"\n            maxIdle=\"@@ds2_connection_pool_maxIdle@@\"\n            defaultAutoCommit=\"false\"\n            removeAbandonedOnBorrow=\"true\"\n            removeAbandonedOnMaintenance=\"true\"\n            logAbandoned=\"false\"\n            testOnBorrow=\"true\"\n            timeBetweenEvictionRunsMillis=\"60000\"\n            minEvictableIdleTimeMillis=\"600000\"\n            validationQuery=\"@@ds2.database_test_query@@\"\n            xaDataSource=\"RawBusinessDataDS\" />\n\n    <Resource name=\"NotManagedBizDataDS\"\n              auth=\"Container\"\n              type=\"javax.sql.DataSource\"\n              factory=\"org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory\"\n              maxTotal=\"5\"\n              minIdle=\"1\"\n              maxIdle=\"5\"\n              maxWaitMillis=\"10000\"\n              initialSize=\"1\"\n              removeAbandonedOnBorrow=\"true\"\n              removeAbandonedOnMaintenance=\"true\"\n              logAbandoned=\"false\"\n              testOnBorrow=\"true\"\n              timeBetweenEvictionRunsMillis=\"60000\"\n              minEvictableIdleTimeMillis=\"600000\"\n              validationQuery=\"@@ds2.database_test_query@@\"\n              username=\"@@ds2.database_connection_user@@\"\n              password=\"@@ds2.database_connection_password@@\"\n              driverClassName=\"@@ds2.driver_class_name@@\"\n              url=\"@@ds2.database_connection_url@@\" />\n\n    <!-- #################################################################################################################### -->\n    <!-- ########################         END OF TEMPLATE MODIFIED BY BONITA SETUP TOOL             ######################### -->\n    <!-- ###################            YOU CAN ADD CUSTOM CONFIGURATION *BELOW* THIS TEMPLATE              ################# -->\n    <!-- #################################################################################################################### -->\n\n</Context>\n"
  },
  {
    "path": "platform/platform-setup/src/main/standalone/tomcat-templates/setenv.bat",
    "content": "@echo on\n\nrem Add the JAVA 9 specific start-up parameters required by Hazelcast\nset \"JDK_JAVA_OPTIONS=%JDK_JAVA_OPTIONS% --add-modules=java.se\"\nset \"JDK_JAVA_OPTIONS=%JDK_JAVA_OPTIONS% --add-exports=java.base/jdk.internal.ref=ALL-UNNAMED\"\nset \"JDK_JAVA_OPTIONS=%JDK_JAVA_OPTIONS% --add-opens=java.base/java.nio=ALL-UNNAMED\"\nset \"JDK_JAVA_OPTIONS=%JDK_JAVA_OPTIONS% --add-opens=java.base/sun.nio.ch=ALL-UNNAMED\"\nset \"JDK_JAVA_OPTIONS=%JDK_JAVA_OPTIONS% --add-opens=java.management/sun.management=ALL-UNNAMED\"\nset \"JDK_JAVA_OPTIONS=%JDK_JAVA_OPTIONS% --add-opens=jdk.management/com.sun.management.internal=ALL-UNNAMED\"\n\nrem Add the JAVA 9 specific start-up parameters required by Xstream serialization\nset \"JDK_JAVA_OPTIONS=%JDK_JAVA_OPTIONS% --add-opens=java.base/java.time=ALL-UNNAMED\"\nset \"JDK_JAVA_OPTIONS=%JDK_JAVA_OPTIONS% --add-opens=java.base/java.time.chrono=ALL-UNNAMED\"\nset \"JDK_JAVA_OPTIONS=%JDK_JAVA_OPTIONS% --add-opens=java.base/java.text=ALL-UNNAMED\"\n\nrem Add the JAVA 9 specific start-up parameters required by Webservice connector/Xstream serialization\nset \"JDK_JAVA_OPTIONS=%JDK_JAVA_OPTIONS% --add-opens=java.xml/com.sun.org.apache.xerces.internal.dom=ALL-UNNAMED\"\nset \"JDK_JAVA_OPTIONS=%JDK_JAVA_OPTIONS% --add-opens=java.xml/com.sun.org.apache.xerces.internal.xni=ALL-UNNAMED\"\n\nrem Add the JAVA 9 specific start-up parameters required by Salesforce connector/Xstream serialization\nset \"JDK_JAVA_OPTIONS=%JDK_JAVA_OPTIONS% --add-opens=java.xml/javax.xml.namespace=ALL-UNNAMED\"\n\nrem Set some JVM system properties required by Bonita\n\nrem This variable is automatically taken into account by catalina.bat:\nset LOGGING_MANAGER=-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager\nset LOG_CONF_FILE_PATH=\"-Dlog4j.configurationFile=%CATALINA_BASE%\\conf\\log4j2-appenders.xml,%CATALINA_BASE%\\conf\\log4j2-loggers.xml\"\n\nset PLATFORM_SETUP=\"-Dorg.bonitasoft.platform.setup.folder=%CATALINA_HOME%\\..\\setup\"\nset H2_DATABASE_DIR=\"-Dorg.bonitasoft.h2.database.dir=%CATALINA_HOME%\\..\\h2_database\"\nset INCIDENT_LOG_DIR=\"-Dorg.bonitasoft.engine.incident.folder=%CATALINA_HOME%\\logs\"\n\nrem Define the RDMBS vendor use by Bonita Engine to store data. Valid values are: h2, postgres, sqlserver, oracle, mysql.\nset DB_OPTS=\"-Dsysprop.bonita.db.vendor=h2\"\n\nrem Define the RDMBS vendor use by Bonita Engine to store Business Data. Valid values are: h2, postgres, sqlserver, oracle, mysql.\nrem If you use different DB engines by tenants, please update directly bonita-tenant-community-custom.properties\nset BDM_DB_OPTS=\"-Dsysprop.bonita.bdm.db.vendor=h2\"\n\nrem Arjuna (JTA service added to Tomcat and required by Bonita Engine for transaction management)\nset ARJUNA_OPTS=\"-Dcom.arjuna.ats.arjuna.common.propertiesFile=%CATALINA_HOME%\\conf\\jbossts-properties.xml\"\n\nrem use env variable BONITA_RUNTIME_TRANSACTION_XATIMEOUT=180 to override default XA Transaction timeout (in seconds):\nif not \"%BONITA_RUNTIME_TRANSACTION_XATIMEOUT%\" == \"\" (set XA_TIMEOUT_OPTS=\"-Dbonita.runtime.transaction.xa-timeout=%BONITA_RUNTIME_TRANSACTION_XATIMEOUT%\")\n\nrem Optional JAAS configuration. Usually used when delegating authentication to LDAP / Active Directory server\nrem set SECURITY_OPTS=\"-Djava.security.auth.login.config=%CATALINA_HOME%\\conf\\jaas-standard.cfg\"\n\nrem Optional JMX remote access Configuration. Used to enable remote JMX agent in tomcat to monitor Heap Memory, Threads, CPU Usage, Classes, and configure various MBeans.\nif \"%JMX_REMOTE_ACCESS%\" == \"true\" (set JMX_REMOTE_ACCESS_OPTS=\"-Dcom.sun.management.jmxremote.port=9000 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=true -Dcom.sun.management.jmxremote.password.file=%CATALINA_HOME%\\conf\\jmxremote.password -Dcom.sun.management.jmxremote.access.file=%CATALINA_HOME%\\conf\\jmxremote.access\")\n\nrem Pass the JVM system properties to Tomcat JVM using CATALINA_OPTS variable\nset CATALINA_OPTS=%CATALINA_OPTS% %LOG_CONF_FILE_PATH% %PLATFORM_SETUP% %XA_TIMEOUT_OPTS% %H2_DATABASE_DIR% %DB_OPTS% %BDM_DB_OPTS% %ARJUNA_OPTS% %INCIDENT_LOG_DIR% %JMX_REMOTE_ACCESS_OPTS% -Dfile.encoding=UTF-8 -Xshare:auto -Xms1024m -Xmx1024m -XX:+HeapDumpOnOutOfMemoryError -Dlog4j2.formatMsgNoLookups=true\n\nset CATALINA_PID=%CATALINA_BASE%\\catalina.pid\n\n@rem extra lib at Tomcat startup\nset CLASSPATH=\"%CATALINA_HOME%\\lib\\ext\\*\"\n"
  },
  {
    "path": "platform/platform-setup/src/main/standalone/tomcat-templates/setenv.sh",
    "content": "#!/bin/sh\n\n# Add the JAVA 9 specific start-up parameters required by Hazelcast\nJDK_JAVA_OPTIONS=\"$JDK_JAVA_OPTIONS --add-modules=java.se\"\nJDK_JAVA_OPTIONS=\"$JDK_JAVA_OPTIONS --add-exports=java.base/jdk.internal.ref=ALL-UNNAMED\"\nJDK_JAVA_OPTIONS=\"$JDK_JAVA_OPTIONS --add-opens=java.base/java.nio=ALL-UNNAMED\"\nJDK_JAVA_OPTIONS=\"$JDK_JAVA_OPTIONS --add-opens=java.base/sun.nio.ch=ALL-UNNAMED\"\nJDK_JAVA_OPTIONS=\"$JDK_JAVA_OPTIONS --add-opens=java.management/sun.management=ALL-UNNAMED\"\nJDK_JAVA_OPTIONS=\"$JDK_JAVA_OPTIONS --add-opens=jdk.management/com.sun.management.internal=ALL-UNNAMED\"\n\n# Add the JAVA 9 specific start-up parameters required by Xstream serialization\nJDK_JAVA_OPTIONS=\"$JDK_JAVA_OPTIONS --add-opens=java.base/java.time=ALL-UNNAMED\"\nJDK_JAVA_OPTIONS=\"$JDK_JAVA_OPTIONS --add-opens=java.base/java.time.chrono=ALL-UNNAMED\"\nJDK_JAVA_OPTIONS=\"$JDK_JAVA_OPTIONS --add-opens=java.base/java.text=ALL-UNNAMED\"\n\n# Add the JAVA 9 specific start-up parameters required by Webservice connector/Xstream serialization\nJDK_JAVA_OPTIONS=\"$JDK_JAVA_OPTIONS --add-opens=java.xml/com.sun.org.apache.xerces.internal.dom=ALL-UNNAMED\"\nJDK_JAVA_OPTIONS=\"$JDK_JAVA_OPTIONS --add-opens=java.xml/com.sun.org.apache.xerces.internal.xni=ALL-UNNAMED\"\n\n# Add the JAVA 9 specific start-up parameters required by Salesforce connector/Xstream serialization\nJDK_JAVA_OPTIONS=\"$JDK_JAVA_OPTIONS --add-opens=java.xml/javax.xml.namespace=ALL-UNNAMED\"\n\n# Set some JVM system properties required by Bonita\n\n# This variable is automatically taken into account by catalina.sh\nLOGGING_MANAGER=\"-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager\"\nLOG_CONF_FILE_PATH=\"-Dlog4j.configurationFile=${CATALINA_BASE}/conf/log4j2-appenders.xml,${CATALINA_BASE}/conf/log4j2-loggers.xml\"\n\nPLATFORM_SETUP=\"-Dorg.bonitasoft.platform.setup.folder=${CATALINA_HOME}/../setup\"\nH2_DATABASE_DIR=\"-Dorg.bonitasoft.h2.database.dir=${CATALINA_HOME}/../h2_database\"\nINCIDENT_LOG_DIR=\"-Dorg.bonitasoft.engine.incident.folder=${CATALINA_HOME}/logs\"\n\n# Define the RDMBS vendor use by Bonita Engine to store data. Valid values are: h2, postgres, sqlserver, oracle, mysql.\nDB_OPTS=\"-Dsysprop.bonita.db.vendor=h2\"\n\n# Define the RDMBS vendor use by Bonita Engine to store Business Data. Valid values are: h2, postgres, sqlserver, oracle, mysql.\n# If you use different DB engines by tenants, please update directly bonita-tenant-community-custom.properties\nBDM_DB_OPTS=\"-Dsysprop.bonita.bdm.db.vendor=h2\"\n\n# Arjuna (JTA service added to Tomcat and required by Bonita Engine for transaction management)\nARJUNA_OPTS=\"-Dcom.arjuna.ats.arjuna.common.propertiesFile=${CATALINA_HOME}/conf/jbossts-properties.xml\"\n\n# use env variable BONITA_RUNTIME_TRANSACTION_XATIMEOUT=180 to override default XA Transaction timeout (in seconds):\nXA_TIMEOUT_OPTS=\"-Dbonita.runtime.transaction.xa-timeout=${BONITA_RUNTIME_TRANSACTION_XATIMEOUT:-180}\"\n\n# Optional JAAS configuration. Usually used when delegating authentication to LDAP / Active Directory server\n#SECURITY_OPTS=\"-Djava.security.auth.login.config=${CATALINA_HOME}/conf/jaas-standard.cfg\"\n\n# Optional JMX remote access Configuration. Used to enable remote JMX agent in tomcat to monitor Heap Memory, Threads, CPU Usage, Classes, and configure various MBeans.\nif [ \"$JMX_REMOTE_ACCESS\" = 'true' ]; then\n    JMX_REMOTE_ACCESS_OPTS=\"-Dcom.sun.management.jmxremote.port=9000 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=true -Dcom.sun.management.jmxremote.password.file=${CATALINA_HOME}/conf/jmxremote.password -Dcom.sun.management.jmxremote.access.file=${CATALINA_HOME}/conf/jmxremote.access\"\nfi\n\n# Pass the JVM system properties to Tomcat JVM using CATALINA_OPTS variable\nCATALINA_OPTS=\"${CATALINA_OPTS} ${LOG_CONF_FILE_PATH} ${PLATFORM_SETUP} ${XA_TIMEOUT_OPTS} ${H2_DATABASE_DIR} ${DB_OPTS} ${BDM_DB_OPTS} ${ARJUNA_OPTS} ${INCIDENT_LOG_DIR} ${JMX_REMOTE_ACCESS_OPTS} -Dfile.encoding=UTF-8 -Xshare:auto -Xms1024m -Xmx1024m -XX:+HeapDumpOnOutOfMemoryError -Djava.security.egd=file:/dev/./urandom -Dlog4j2.formatMsgNoLookups=true\"\nexport CATALINA_OPTS\n\n# Only set CATALINA_PID if not already set (check for empty value) by startup script (usually done by /etc/init.d/tomcat8 but not by startup.sh nor catalina.sh)\nif [ -z ${CATALINA_PID+x} ]; then\n        CATALINA_PID=${CATALINA_BASE}/catalina.pid;\n        export CATALINA_PID;\nfi\n\n# extra lib required at Tomcat startup\nexport CLASSPATH=\"${CATALINA_HOME}/lib/ext/*\"\n"
  },
  {
    "path": "platform/platform-setup/src/test/e2e/clean-postgres.sh",
    "content": "#!/bin/sh\n\n\necho \"========================================\"\necho \"disconnect users\"\necho \"========================================\"\npsql postgresql://postgres:postgres@localhost:5432/postgres <<EOF\n SELECT pg_terminate_backend(pg_stat_activity.pid)\n                    FROM pg_stat_activity\n                    WHERE pg_stat_activity.datname = 'bonita'\n                    AND pid in ( SELECT pid\n                    FROM pg_stat_activity\n                    WHERE upper(pg_stat_activity.datname) = upper('bonita')\n                      AND pid <> pg_backend_pid())\nEOF\n\necho \"========================================\"\necho \"drop DB\"\necho \"========================================\"\npsql postgresql://postgres:postgres@localhost:5432/postgres <<EOF\nDROP DATABASE bonita;\nEOF\n\n\necho \"========================================\"\necho \"create DB\"\necho \"========================================\"\npsql postgresql://postgres:postgres@localhost:5432/postgres <<EOF\nCREATE DATABASE bonita\n  WITH OWNER = bonita\n       ENCODING = 'UTF8'\n       TABLESPACE = pg_default\n       LC_COLLATE = 'fr_FR.UTF-8'\n       LC_CTYPE = 'fr_FR.UTF-8'\n       CONNECTION LIMIT = -1;\nGRANT CONNECT, TEMPORARY ON DATABASE bonita TO public;\nGRANT ALL ON DATABASE bonita TO bonita;\nEOF\n\n\n\necho \"========================================\"\necho \"clean all tables\"\necho \"========================================\"\npsql postgresql://bonita:bpm@localhost:5432/bonita -f ../platform-resources/src/main/resources/sql/postgres/dropTables.sql\npsql postgresql://bonita:bpm@localhost:5432/bonita -f ../platform-resources/src/main/resources/sql/postgres/dropQuartzTables.sql\n\n\n\necho \"========================================\"\necho \"check tables\"\necho \"========================================\"\npsql postgresql://bonita:bpm@localhost:5432/bonita <<EOF\nselect t.table_catalog, t.table_schema, t.table_name from information_schema.tables t\nwhere t.table_schema='public'\norder by t.table_name;\nEOF\n\n"
  },
  {
    "path": "platform/platform-setup/src/test/e2e/e2e-distrib.sh",
    "content": "#!/bin/sh\n\ncd ../../../\n./gradlew build -x test\ncd -\n\nexport VERSION=`cat ../platform-resources/build/resources/main/PLATFORM_ENGINE_VERSION`\n\necho \"========================================\"\necho \"version:${VERSION}\"\necho \"========================================\"\n\nexport E2E_DIR=\"build/e2e-distrib\"\nexport ZIP=Bonita-platform-setup-${VERSION}.zip\n\nrm -rf ${E2E_DIR}\nunzip -q -d ${E2E_DIR} build/distributions/${ZIP}\nrm -rf ${E2E_DIR}-jar-exploded\nunzip -q -d ${E2E_DIR}-jar-exploded ${E2E_DIR}/lib/platform-setup-${VERSION}.jar\n\necho \"========================================\"\necho \"platform-setup-${VERSION}.jar exploded:\"\ntree ${E2E_DIR}-jar-exploded\necho \"========================================\"\n\necho \"========================================\"\necho \"distribution structure:\"\necho \"========================================\"\ntree -L 3 ${E2E_DIR}\n\necho \"========================================\"\necho \"check permissions:\"\necho \"========================================\"\nls -ltrR ${E2E_DIR}\n\n\n\n"
  },
  {
    "path": "platform/platform-setup/src/test/e2e/e2e-postgres-bos.sh",
    "content": "#!/bin/sh\n\ntestReturnCode() {\n  COD_RET=$1\n  if [ ${COD_RET} -ne 0 ]; then\n    echo \"ERROR $1 $2\"\n    echo \"############################ W A R N I N G ###########################\"\n    echo \"################# command '$2' should NOT have failed!! ##############\"\n    echo \"############################ W A R N I N G ###########################\"\n    docker rm -vf bonita-postgres\n    exit ${COD_RET}\n  fi\n}\n\ntestValue() {\n  if [ \"$1\" != \"$2\" ]; then\n    echo \"ERROR: $1 should be equal to $2\"\n    exit 114\n  fi\n}\n\necho \"========================================================================\"\necho \"Remove potential previously-started docker container. Ignore the errors.\"\necho \"========================================================================\"\ndocker rm -vf bonita-postgres 2> /dev/null\n\necho \"=============================================\"\necho \"Start the Postgres database docker container\"\necho \"=============================================\"\ndocker run --rm -p 5432:5432 --name bonita-postgres -d bonitasoft/bonita-postgres:16.4\n\nexport VERSION=\"$(cat ../platform-resources/build/resources/main/PLATFORM_ENGINE_VERSION)\"\n\necho \"========================================\"\necho \"version: ${VERSION}\"\necho \"========================================\"\nexport E2E_DIR=\"build/e2e-postgres-bos\"\nexport ZIP=Bonita-platform-setup-${VERSION}.zip\n\nunzip -o -q -d ${E2E_DIR} build/distributions/${ZIP}\n\necho \"Setting 'org.bonitasoft.platform.setup' log level to DEBUG\"\n# Activate in debug development phase:\n#sed -i \"s/org.bonitasoft.platform.setup\\\" level=\\\"INFO/org.bonitasoft.platform.setup\\\" level=\\\"DEBUG/g\" ${E2E_DIR}/logback.xml\n#sed -i \"s/org.bonitasoft.platform\\\" level=\\\"INFO/org.bonitasoft.platform\\\" level=\\\"DEBUG/g\" ${E2E_DIR}/logback.xml\n#sed -i \"s/PlatformSetupApplication\\\" level=\\\"WARN/PlatformSetupApplication\\\" level=\\\"DEBUG/g\" ${E2E_DIR}/logback.xml\n\n\necho \"==================================================================================================\"\necho \"Default H2 configuration detected should NOT ask for confirmation if sysprop 'h2.noconfirm' is set\"\necho \"==================================================================================================\"\n${E2E_DIR}/setup.sh init -Dh2.noconfirm\ntestReturnCode $? \"setup.sh init with default H2 configuration auto-confirmed with System property\"\n\necho \"=============================================================\"\necho \"Default H2 configuration detected should ask for confirmation\"\necho \"=============================================================\"\n${E2E_DIR}/setup.sh init <<EOF\ny\nEOF\ntestReturnCode $? \"setup.sh init with default H2 configuration confirmed\"\n\n${E2E_DIR}/setup.sh init <<EOF\nn\nEOF\n\nif [ $? -eq 0 ]; then\n    echo \"setup should have exited with code != 0\"\n    exit 12\nfi\n\necho \"========================================\"\necho \"configure postgres\"\nsed -i s/^db.vendor=h2/db.vendor=postgres/g ${E2E_DIR}/database.properties\nsed -i s/^db.user=.*/db.user=bonita/g ${E2E_DIR}/database.properties\nsed -i s/^db.password=.*/db.password=bpm/g ${E2E_DIR}/database.properties\nsed -i \"s/^[# ]*db.server.name=.*/db.server.name=localhost/g\" ${E2E_DIR}/database.properties\nsed -i \"s/^[# ]*db.server.port=.*/db.server.port=5432/g\" ${E2E_DIR}/database.properties\nsed -i s/^db.database.name=.*/db.database.name=bonita/g ${E2E_DIR}/database.properties\ncat ${E2E_DIR}/database.properties\necho \"========================================\"\n\necho \"==================================================\"\necho \"should skip non-supported environment for 'configure'\"\necho \"==================================================\"\n${E2E_DIR}/setup.sh configure\n\ntestReturnCode $? \"setup.sh configure\"\n\necho \"========================================\"\necho \"should configure tomcat environment\"\necho \"========================================\"\ncp -rf src/test/resources/tomcat_conf/* ${E2E_DIR}/..\n\n${E2E_DIR}/setup.sh configure\ntestReturnCode $? \"setup.sh configure\"\n\ncat build/server/conf/Catalina/localhost/bonita.xml | grep \"driverClassName=\\\"org.postgresql.Driver\\\"\" > /dev/null\ntestReturnCode $? \"Configuring bonita.xml file with Postgres\"\n\ncat build/server/bin/setenv.sh | grep \"\\-Dsysprop.bonita.db.vendor=postgres\" > /dev/null\ntestReturnCode $? \"Configuring setenv.sh file with Postgres\"\n\necho \"========================================\"\necho \"should store to database \"\necho \"========================================\"\n${E2E_DIR}/setup.sh init\n\ntestReturnCode $? \"setup.sh init\"\n\necho \"========================================\"\necho \"check tables\"\necho \"========================================\"\ndocker exec bonita-postgres psql postgresql://bonita:bpm@localhost:5432/bonita -c \"\nselect t.table_catalog, t.table_schema, t.table_name from information_schema.tables t\nwhere t.table_schema='public'\norder by t.table_name;\"\n\necho \"========================================\"\necho \"check platform data\"\necho \"========================================\"\ndocker exec bonita-postgres psql postgresql://bonita:bpm@localhost:5432/bonita -c \"\nSELECT\n    p.id,\n    p.version,\n    p.initial_bonita_version,\n    p.created_by,\n    TO_CHAR(\n        TO_TIMESTAMP(\n            p.created / 1000\n        ),\n        'DD/MM/YYYY HH24:MI:SS'\n    ) as creation_date,\n    maintenance_enabled\nFROM\n    platform p\"\n\n\necho \"========================================\"\necho \"check configuration\"\necho \"========================================\"\n\ndocker exec bonita-postgres psql postgresql://bonita:bpm@localhost:5432/bonita -c \"\nSELECT\n    c.content_type,\n    c.resource_name\nFROM\n    configuration c\nORDER BY\n    c.content_type,\n    c.resource_name\"\n\n#\n#echo \"========================================\"\n#echo \"simulation of engine start\"\n#echo \"========================================\"\n#docker exec bonita-postgres psql postgresql://bonita:bpm@localhost:5432/bonita -c \"\n#INSERT\n#    INTO\n#        configuration(\n#            content_type,\n#            resource_name,\n#            resource_content\n#        ) SELECT\n#            'TENANT_SECURITY_SCRIPTS',\n#            c.resource_name,\n#            c.resource_content\n#        FROM\n#            configuration c\n#        WHERE c.content_type ='TENANT_SECURITY_SCRIPTS'\"\n\necho \"================================================================================\"\necho \"simulate a version upgrade (configuration files have changed in folder initial/)\"\necho \"================================================================================\"\n\necho \"dynamic-permissions-checks\" > ${E2E_DIR}/platform_conf/initial/tenant_portal/dynamic-permissions-checks.properties\necho \"resources-permissions-mapping\" > ${E2E_DIR}/platform_conf/initial/tenant_portal/resources-permissions-mapping.properties\necho \"compound-permissions-mapping\" > ${E2E_DIR}/platform_conf/initial/tenant_portal/compound-permissions-mapping.properties\n\n${E2E_DIR}/setup.sh init\ntestReturnCode $? \"setup.sh init\"\n\necho \"===========================================\"\necho \"retrieve configuration (to default folder) \"\necho \"===========================================\"\n\n${E2E_DIR}/setup.sh pull\n\necho \"==================================================================================\"\necho \"verify version upgrade has updated configuration file changes (in folder current/)\"\necho \"==================================================================================\"\n\nnew_content=`cat ${E2E_DIR}/platform_conf/current/tenant_portal/dynamic-permissions-checks.properties`\ntestValue $new_content \"dynamic-permissions-checks\"\n\nres_mapp=`cat ${E2E_DIR}/platform_conf/current/tenant_portal/resources-permissions-mapping.properties`\ntestValue $res_mapp \"resources-permissions-mapping\"\n\ncompound=`cat ${E2E_DIR}/platform_conf/current/tenant_portal/compound-permissions-mapping.properties`\ntestValue $compound \"compound-permissions-mapping\"\n\necho \"=> Verification Ok\"\n\necho \"========================================\"\necho \"remove some files & push\"\necho \"========================================\"\n\nrm -rf  ${E2E_DIR}/platform_conf/current\nmkdir -p ${E2E_DIR}/platform_conf/current/platform_engine\ncp ${E2E_DIR}/platform_conf/initial/platform_engine/bonita-platform-custom.xml ${E2E_DIR}/platform_conf/current/platform_engine\n\ntree ${E2E_DIR}/platform_conf/current\n\n${E2E_DIR}/setup.sh push\necho \"===========================================\"\necho \"setup.sh push should have failed just above\"\necho \"Now let's FORCE push it\"\necho \"===========================================\"\n\n${E2E_DIR}/setup.sh push --force\ntestReturnCode $? \"setup.sh push --force should be successful\"\n\necho \"============================================\"\necho \"Backup folder should be created when pushing\"\necho \"============================================\"\n\nif [ \"`ls ${E2E_DIR}/platform_conf/ | grep 'backup-'`\" = \"\" ]; then\n    echo 'ERROR. Should have created backup folder';\n    exit 52\nelse\n    echo 'OK. Backup folder present.'\nfi\n\necho \"========================================\"\necho \"should contain only bonita-platform-custom.xml:\"\ntree ${E2E_DIR}/platform_conf/current\necho \"========================================\"\n\necho \"========================================\"\necho \"should fail if driver class cannot be loaded:\"\necho \"========================================\"\nsed -i s/^postgres.nonXaDriver=.*$/postgres.nonXaDriver=org.UnknownClass/g ${E2E_DIR}/internal.properties\n${E2E_DIR}/setup.sh push\n\necho \"========================================\"\necho \"should fail if drivers not found:\"\necho \"========================================\"\nsed -i s/^postgres.nonXaDriver=org.UnknownClass*$/postgres.nonXaDriver=org.postgresql.Driver/g ${E2E_DIR}/internal.properties\nrm ${E2E_DIR}/lib/postgres*.jar\n${E2E_DIR}/setup.sh push --debug\n\necho \"========================================\"\necho \"Docker database cleanup\"\necho \"========================================\"\ndocker rm -vf bonita-postgres\n\necho \"========================================\"\necho \"Test cleanup\"\necho \"========================================\"\necho \"Cleaning up $(cd ${E2E_DIR} && pwd) and $(cd ./build/h2_database && pwd)\"\nrm -Rf ${E2E_DIR}\nrm -Rf ./build/h2_database\n\necho \"========================================\"\necho \"END\"\necho \"========================================\"\n\n\n"
  },
  {
    "path": "platform/platform-setup/src/test/java/org/bonitasoft/platform/configuration/impl/ConfigurationServiceImplIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.impl;\n\nimport static java.nio.charset.StandardCharsets.UTF_8;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.jdbc.datasource.init.ScriptUtils.*;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.sql.Connection;\n\nimport javax.sql.DataSource;\n\nimport org.bonitasoft.platform.configuration.model.BonitaConfiguration;\nimport org.bonitasoft.platform.configuration.util.FolderComparator;\nimport org.bonitasoft.platform.setup.PlatformSetupApplication;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.TemporaryFolder;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.context.annotation.PropertySource;\nimport org.springframework.core.io.InputStreamResource;\nimport org.springframework.core.io.support.EncodedResource;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.datasource.init.ScriptUtils;\nimport org.springframework.stereotype.Component;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n/**\n * @author laurent Leseigneur\n */\n@RunWith(SpringRunner.class)\n\n//keep order\n@SpringBootTest(classes = {\n        PlatformSetupApplication.class })\n@ComponentScan(basePackages = { \"org.bonitasoft.platform.setup\", \"org.bonitasoft.platform.configuration\" })\n@PropertySource(\"classpath:/application.properties\")\n@Component\npublic class ConfigurationServiceImplIT {\n\n    @Autowired\n    JdbcTemplate jdbcTemplate;\n\n    @Value(\"${db.vendor}\")\n    private String dbVendor;\n\n    @Autowired\n    ConfigurationServiceImpl configurationService;\n\n    @Autowired\n    DataSource dataSource;\n\n    @Rule\n    public TemporaryFolder temporaryFolder = new TemporaryFolder();\n\n    @Before\n    public void setUpDb() throws Exception {\n        dropTables();\n        createTables();\n    }\n\n    @After\n    public void cleanUpDB() throws Exception {\n        dropTables();\n    }\n\n    @Test\n    public void should_store_configuration() throws Exception {\n        //given\n        Path configFolder = Paths.get(getClass().getResource(\"/conf\").toURI());\n\n        //when\n        configurationService.storePlatformConfiguration(configFolder.toFile());\n\n        //then\n        assertThat(configurationService.getPlatformEngineConf()).as(\"should retrieve configuration\")\n                .extracting(\"resourceName\")\n                .containsOnly(\"bonita-platform-community.properties\", \"bonita-platform-custom.xml\");\n    }\n\n    @Test\n    public void should_write_configuration_to_fileSystem() throws Exception {\n        //given\n        Path configFolder = Paths.get(getClass().getResource(\"/conf\").toURI());\n        configurationService.storePlatformConfiguration(configFolder.toFile());\n\n        //when\n        final File destFolder = temporaryFolder.newFolder();\n        final File licFolder = temporaryFolder.newFolder();\n\n        configurationService.writeAllConfigurationToFolder(destFolder, licFolder);\n\n        //then\n        assertThat(destFolder).as(\"should retrieve config files\")\n                .exists()\n                .isDirectory();\n        new FolderComparator().compare(configFolder.toFile(), destFolder);\n\n    }\n\n    @Test\n    public void should_store_licenses() throws Exception {\n        //given\n        File licenses = createLicenseFolder();\n\n        //when\n        configurationService.storeLicenses(licenses);\n\n        //then\n        BonitaConfiguration expectedLicense1 = new BonitaConfiguration(\"license1.lic\",\n                \"license 1 content\".getBytes(UTF_8));\n        BonitaConfiguration expectedLicense2 = new BonitaConfiguration(\"license2.lic\",\n                \"license 2 content\".getBytes(UTF_8));\n        assertThat(configurationService.getLicenses()).as(\"should retrieve configuration\")\n                .containsOnly(expectedLicense1, expectedLicense2);\n\n    }\n\n    @Test\n    public void should_store_licenses_override_previous_licenses() throws Exception {\n        //given\n        configurationService.storeLicenses(createLicenseFolder());\n\n        //when\n        configurationService.storeLicenses(createNewLicenseFolder());\n\n        //then\n        BonitaConfiguration newLicense2 = new BonitaConfiguration(\"license2.lic\",\n                \"new license 2 content\".getBytes(UTF_8));\n        BonitaConfiguration newLicense3 = new BonitaConfiguration(\"license3.lic\", \"license 3 content\".getBytes(UTF_8));\n        assertThat(configurationService.getLicenses()).as(\"should retrieve configuration\")\n                .containsOnly(newLicense2, newLicense3);\n\n    }\n\n    private void createTables() throws Exception {\n        final InputStream createTableResource = this.getClass()\n                .getResourceAsStream(\"/sql/\" + dbVendor + \"/createTables.sql\");\n        try (Connection connection = getConnection()) {\n            ScriptUtils.executeSqlScript(connection,\n                    new EncodedResource(new InputStreamResource(createTableResource)), false, false,\n                    DEFAULT_COMMENT_PREFIX, DEFAULT_STATEMENT_SEPARATOR,\n                    DEFAULT_BLOCK_COMMENT_START_DELIMITER, DEFAULT_BLOCK_COMMENT_END_DELIMITER);\n        }\n    }\n\n    private void dropTables() throws Exception {\n        final InputStream dropTablesResource = this.getClass()\n                .getResourceAsStream(\"/sql/\" + dbVendor + \"/dropTables.sql\");\n        try (Connection connection = getConnection()) {\n            ScriptUtils.executeSqlScript(connection,\n                    new EncodedResource(new InputStreamResource(dropTablesResource)), true, true,\n                    DEFAULT_COMMENT_PREFIX, DEFAULT_STATEMENT_SEPARATOR,\n                    DEFAULT_BLOCK_COMMENT_START_DELIMITER, DEFAULT_BLOCK_COMMENT_END_DELIMITER);\n        }\n    }\n\n    private File createLicenseFolder() throws IOException {\n        File licenses = temporaryFolder.newFolder(\"licenses\");\n        Files.write(licenses.toPath().resolve(\"license1.lic\"), \"license 1 content\".getBytes(UTF_8));\n        Files.write(licenses.toPath().resolve(\"license2.lic\"), \"license 2 content\".getBytes(UTF_8));\n        Files.write(licenses.toPath().resolve(\"not_a_license.txt\"), \"*\".getBytes(UTF_8));\n        Path subFolder = licenses.toPath().resolve(\"subFolder\");\n        Files.createDirectories(licenses.toPath().resolve(\"subFolder\"));\n        Files.write(subFolder.resolve(\"ignoreMe.lic\"), \"ignore this license content\".getBytes(UTF_8));\n        return licenses;\n    }\n\n    private File createNewLicenseFolder() throws IOException {\n        File newLicenses = temporaryFolder.newFolder(\"newLicenses\");\n        Files.write(newLicenses.toPath().resolve(\"license2.lic\"), \"new license 2 content\".getBytes(UTF_8));\n        Files.write(newLicenses.toPath().resolve(\"license3.lic\"), \"license 3 content\".getBytes(UTF_8));\n        return newLicenses;\n    }\n\n    private Connection getConnection() throws Exception {\n        return dataSource.getConnection();\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-setup/src/test/java/org/bonitasoft/platform/configuration/util/FolderComparator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.configuration.util;\n\nimport static org.apache.commons.io.FilenameUtils.getExtension;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileReader;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Properties;\n\nimport org.apache.commons.io.comparator.ExtensionFileComparator;\nimport org.assertj.core.api.Assertions;\nimport org.custommonkey.xmlunit.DetailedDiff;\nimport org.custommonkey.xmlunit.XMLUnit;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class FolderComparator {\n\n    private static final int ARE_EQUALS = 0;\n    private static final String XML = \"xml\";\n    private static final String PROPERTIES = \"properties\";\n\n    public void compare(File configFolder, File destinationFolder) throws Exception {\n        final Map<String, File> expectedFiles = flattenFolderFiles(configFolder);\n        final Map<String, File> files = flattenFolderFiles(destinationFolder);\n        Assertions.assertThat(files).as(\"should have same size\").hasSize(expectedFiles.size());\n        Assertions.assertThat(expectedFiles.keySet()).as(\"should have same file names\").isEqualTo(files.keySet());\n        for (String name : expectedFiles.keySet()) {\n            compareFileContent(expectedFiles.get(name), files.get(name));\n        }\n\n    }\n\n    private Map<String, File> flattenFolderFiles(File folder) throws IOException {\n        Map<String, File> fileMap = new HashMap<>();\n        final FlattenFolderVisitor flattenFolderVisitor = new FlattenFolderVisitor(fileMap);\n        Files.walkFileTree(folder.toPath(), flattenFolderVisitor);\n        return fileMap;\n    }\n\n    private void compareFileContent(File expectedFile, File givenFile) throws Exception {\n        final String givenFileAbsolutePath = givenFile.getAbsolutePath();\n\n        final String expectedFileExtension = getExtension(expectedFile.getName());\n        final String expectedFileAbsolutePath = expectedFile.getAbsolutePath();\n        Assertions.assertThat(new ExtensionFileComparator().compare(expectedFile, givenFile))\n                .as(expectedFileAbsolutePath + \" and \" + givenFileAbsolutePath + \" should have same extension\")\n                .isEqualTo(ARE_EQUALS);\n\n        switch (expectedFileExtension) {\n            case PROPERTIES:\n                Assertions.assertThat(getProperties(expectedFile))\n                        .as(expectedFileAbsolutePath + \" and \" + givenFileAbsolutePath\n                                + \" should contain same properties\")\n                        .isEqualTo(getProperties(givenFile));\n                break;\n            case XML:\n                final List allDifferences = new DetailedDiff(\n                        XMLUnit.compareXML(new FileReader(givenFile), new FileReader(expectedFile)))\n                        .getAllDifferences();\n                Assertions.assertThat(allDifferences).as(\"should xml file be equals\").isEmpty();\n                break;\n            default:\n                Assertions.fail(\"unexpected file:\" + expectedFile.getAbsolutePath());\n                break;\n        }\n\n    }\n\n    private Properties getProperties(File propertyFile) throws IOException {\n        final Properties properties = new Properties();\n        try (FileInputStream fileInputStream = new FileInputStream(propertyFile)) {\n            properties.load(fileInputStream);\n            return properties;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-setup/src/test/java/org/bonitasoft/platform/setup/ConfigurationCheckerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup;\n\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\n\nimport java.util.Properties;\n\nimport org.bonitasoft.platform.exception.PlatformException;\nimport org.bonitasoft.platform.setup.command.configure.PropertyLoader;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.ClearSystemProperties;\nimport org.junit.rules.ExpectedException;\nimport org.junit.rules.TemporaryFolder;\nimport org.junit.rules.TestRule;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class ConfigurationCheckerTest {\n\n    private static final String TEST_DATASOURCE_CONFIG_DIR = \"/datasource-config/\";\n\n    @Rule\n    public TestRule clean = new ClearSystemProperties(\"db.admin.user\", \"sysprop.bonita.db.vendor\", \"db.user\",\n            \"db.password\", \"db.vendor\", \"db.server.name=\",\n            \"db.admin.password\", \"sysprop.bonita.bdm.db.vendor\", \"db.server.port\", \"db.database.name\");\n\n    @Rule\n    public final ExpectedException expectedException = ExpectedException.none();\n\n    @Rule\n    public TemporaryFolder temporaryFolder = new TemporaryFolder();\n\n    @Test\n    public void validate_should_load_class_if_present() throws Exception {\n        // Default org.h2.Driver should be found:\n        new ConfigurationChecker(new PropertyLoader().loadProperties()).validate();\n    }\n\n    @Test\n    public void validate_should_fail_to_load_class_if_not_found() throws Exception {\n        final ConfigurationChecker configurationChecker = new ConfigurationChecker(\n                new PropertyLoader(TEST_DATASOURCE_CONFIG_DIR + \"database.properties\",\n                        TEST_DATASOURCE_CONFIG_DIR + \"missingDriverClass_internal.properties\").loadProperties());\n        configurationChecker.loadProperties();\n\n        expectedException.expect(PlatformException.class);\n        expectedException.expectMessage(\"The driver class named 'org.404.NonExistent'\");\n\n        configurationChecker.validate();\n    }\n\n    @Test\n    public void validate_should_fail_mandatory_property_is_not_set() throws Exception {\n        final String dbVendor = \"dbVendor\";\n        System.setProperty(PlatformSetup.BONITA_DB_VENDOR_PROPERTY, dbVendor);\n        final Properties propertiesWithMissingServerName = new PropertyLoader(\n                TEST_DATASOURCE_CONFIG_DIR + \"incomplete_database.properties\")\n                .loadProperties();\n\n        expectedException.expect(PlatformException.class);\n        expectedException.expectMessage(\"Mandatory property\");\n\n        new ConfigurationChecker(propertiesWithMissingServerName).validate();\n    }\n\n    @Test\n    public void validate_should_fail_if_class_to_load_is_not_set() throws Exception {\n        final ConfigurationChecker configurationChecker = new ConfigurationChecker(\n                new PropertyLoader(TEST_DATASOURCE_CONFIG_DIR + \"database.properties\",\n                        TEST_DATASOURCE_CONFIG_DIR + \"incomplete_internal.properties\").loadProperties());\n\n        expectedException.expect(PlatformException.class);\n        expectedException.expectMessage(\"Mandatory property 'postgres.nonXaDriver'\");\n\n        configurationChecker.validate();\n    }\n\n    @Test\n    public void validate_should_check_driver_loadability() throws Exception {\n        final ConfigurationChecker checker = spy(new ConfigurationChecker(new PropertyLoader().loadProperties()));\n\n        checker.validate();\n\n        verify(checker).tryToLoadDriverClass();\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-setup/src/test/java/org/bonitasoft/platform/setup/PlatformSetupApplicationTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.ExpectedSystemExit;\nimport org.junit.contrib.java.lang.system.SystemErrRule;\nimport org.junit.contrib.java.lang.system.SystemOutRule;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class PlatformSetupApplicationTest {\n\n    @Rule\n    public SystemErrRule systemErrRule = new SystemErrRule().enableLog().muteForSuccessfulTests();\n    @Rule\n    public SystemOutRule systemOutRule = new SystemOutRule().enableLog().muteForSuccessfulTests();\n\n    @Rule\n    public ExpectedSystemExit systemExit = ExpectedSystemExit.none();\n\n    @Test\n    public void should_gracefully_fail_with_error_message_when_null_action() {\n        //then\n        systemExit.expectSystemExitWithStatus(1);\n\n        //when\n        PlatformSetupApplication.main(null);\n    }\n\n    @Test\n    public void should_gracefully_fail_with_error_message_when_action_unknown() {\n        systemExit.expectSystemExitWithStatus(1);\n        systemExit.checkAssertionAfterwards(\n                () -> assertThat(systemOutRule.getLog()).contains(\"no command named: wrong value\"));\n\n        PlatformSetupApplication.main(new String[] { \"wrong value\" });\n    }\n\n    @Test\n    public void main_should_accept_configure_action() {\n        systemExit.expectSystemExitWithStatus(0);\n\n        PlatformSetupApplication.main(new String[] { \"configure\" });\n    }\n\n    @Test\n    public void should_show_error_if_no_command_specified() {\n        //then\n        systemExit.expectSystemExitWithStatus(1);\n        systemExit.checkAssertionAfterwards(() -> {\n            assertThat(systemOutRule.getLog()).contains(\"Need to specify a command, see usage above.\");\n            assertThat(systemOutRule.getLog())\n                    .contains(\"usage: setup ( init | configure | pull | push ) [-D <property=value>]\");\n        });\n        //when\n        PlatformSetupApplication.main(new String[] {});\n    }\n\n    @Test\n    public void should_show_help_of_a_command() {\n        //then\n        systemExit.expectSystemExitWithStatus(0);\n        systemExit.checkAssertionAfterwards(() -> assertThat(systemOutRule.getLog())\n                .contains(\"Run this init command once to create database structure\"));\n        //when\n        PlatformSetupApplication.main(new String[] { \"help\", \"init\" });\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-setup/src/test/java/org/bonitasoft/platform/setup/PlatformSetupDistributionIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.platform.setup.command.configure.BundleConfiguratorTest.checkFileContains;\nimport static org.junit.Assert.assertNotNull;\n\nimport java.io.File;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.Statement;\n\nimport org.apache.commons.exec.CommandLine;\nimport org.apache.commons.exec.DefaultExecutor;\nimport org.apache.commons.io.FileUtils;\nimport org.apache.commons.io.file.PathUtils;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.ClearSystemProperties;\nimport org.junit.rules.TemporaryFolder;\nimport org.junit.rules.TestRule;\n\n/**\n * @author Baptiste Mesta\n */\npublic class PlatformSetupDistributionIT {\n\n    private File setupFolder;\n\n    @Rule\n    public TestRule clean = new ClearSystemProperties(\"db.admin.user\", \"sysprop.bonita.db.vendor\", \"db.user\",\n            \"db.password\", \"db.vendor\", \"db.server.name\",\n            \"db.admin.password\", \"sysprop.bonita.bdm.db.vendor\", \"db.server.port\", \"db.database.name\");\n\n    @Rule\n    public TemporaryFolder temporaryFolder = new TemporaryFolder();\n\n    @Before\n    public void before() throws Exception {\n        setupFolder = temporaryFolder.newFolder();\n        PlatformSetupTestUtils.extractDistributionTo(setupFolder);\n        if (!PlatformSetupTestUtils.isCommunityEdition(getClass())) {\n            // Tests executed in the subscription edition need a fake license file\n            Files.createFile(Paths.get(setupFolder.getAbsolutePath()).resolve(\"platform_conf\").resolve(\"licenses\")\n                    .resolve(\"some.lic\"));\n        }\n    }\n\n    @Test\n    public void setupSh_should_work_with_init_on_h2_and_prevent_pushing_deletion() throws Exception {\n        //given\n        CommandLine oCmdLine = PlatformSetupTestUtils.createCommandLine();\n        oCmdLine.addArgument(\"init\");\n        DefaultExecutor executor = PlatformSetupTestUtils.createExecutor(setupFolder);\n        executor.setStreamHandler(PlatformSetupTestUtils.getExecuteStreamHandler(\"yes\"));\n\n        //when\n        int iExitValue = executor.execute(oCmdLine);\n\n        //then\n        assertThat(iExitValue).isZero();\n        Connection jdbcConnection = PlatformSetupTestUtils.getJdbcConnection(setupFolder);\n        Statement statement = jdbcConnection.createStatement();\n        ResultSet resultSet = statement.executeQuery(\"SELECT COUNT(*) AS nb FROM CONFIGURATION\");\n        resultSet.next();\n        assertThat(resultSet.getInt(\"nb\")).isPositive();\n\n        oCmdLine = PlatformSetupTestUtils.createCommandLine();\n        oCmdLine.addArgument(\"pull\");\n        iExitValue = executor.execute(oCmdLine);\n        assertThat(iExitValue).isZero();\n\n        final Path platformEngine = setupFolder.toPath().resolve(\"platform_conf\").resolve(\"current\")\n                .resolve(\"platform_engine\");\n        FileUtils.deleteDirectory(platformEngine.toFile());\n\n        oCmdLine = PlatformSetupTestUtils.createCommandLine();\n        oCmdLine.addArgument(\"push\");\n        executor.setExitValue(1);\n        iExitValue = executor.execute(oCmdLine);\n        assertThat(iExitValue).isEqualTo(1);\n\n        oCmdLine.addArgument(\"--force\");\n        executor.setExitValue(0);\n        iExitValue = executor.execute(oCmdLine);\n        assertThat(iExitValue).isZero();\n    }\n\n    @Test\n    public void setupSh_should_work_with_init_on_h2_with_overridden_system_property() throws Exception {\n        //given\n        CommandLine oCmdLine = PlatformSetupTestUtils.createCommandLine();\n        oCmdLine.addArguments(\"init -Ddb.user=myUser\");\n        DefaultExecutor executor = PlatformSetupTestUtils.createExecutor(setupFolder);\n        executor.setStreamHandler(PlatformSetupTestUtils.getExecuteStreamHandler(\"Y\"));\n        //when\n        int iExitValue = executor.execute(oCmdLine);\n        //then\n        assertThat(iExitValue).isZero();\n        Connection jdbcConnection = PlatformSetupTestUtils.getJdbcConnection(setupFolder, \"myUser\");\n        Statement statement = jdbcConnection.createStatement();\n        ResultSet resultSet = statement.executeQuery(\"SELECT COUNT(*) AS nb FROM CONFIGURATION\");\n        resultSet.next();\n        assertThat(resultSet.getInt(\"nb\")).isPositive();\n    }\n\n    @Test\n    public void setupSh_should_have_error_with_no_argument() throws Exception {\n        //given\n        CommandLine oCmdLine = PlatformSetupTestUtils.createCommandLine();\n        DefaultExecutor oDefaultExecutor = PlatformSetupTestUtils.createExecutor(setupFolder);\n        oDefaultExecutor.setExitValue(1);\n        //when\n        int iExitValue = oDefaultExecutor.execute(oCmdLine);\n        //then\n        assertThat(iExitValue).isEqualTo(1);\n    }\n\n    @Test\n    public void commandLine_should_support_H2_path_with_space_characters() throws Exception {\n        //setup\n        final File temporaryFolderRoot = temporaryFolder.newFolder();\n        Path bundleFolder = temporaryFolderRoot.toPath().toRealPath();\n        Path tomcatFolder = bundleFolder.resolve(\"server\");\n        var tomcatServerFolder = getClass().getResource(\"/tomcat_conf/server\");\n        assertNotNull(tomcatServerFolder);\n        PathUtils.copyDirectory(Paths.get(tomcatServerFolder.toURI()), tomcatFolder);\n        final File newSetupFolder = bundleFolder.resolve(\"setup\").toFile();\n        FileUtils.copyDirectory(setupFolder, newSetupFolder);\n        //given\n        CommandLine oCmdLine = PlatformSetupTestUtils.createCommandLine();\n        oCmdLine.addArguments(\"configure\");\n        oCmdLine.addArguments(\"-Dh2.database.dir=\\\"my h2 path\\\"\", false);\n        DefaultExecutor executor = PlatformSetupTestUtils.createExecutor(newSetupFolder);\n        executor.setStreamHandler(PlatformSetupTestUtils.getExecuteStreamHandler(\"yes\"));\n\n        //when\n        int iExitValue = executor.execute(oCmdLine);\n\n        //then\n        assertThat(iExitValue).isZero();\n        checkFileContains(tomcatFolder.resolve(\"conf\").resolve(\"Catalina\").resolve(\"localhost\").resolve(\"bonita.xml\"),\n                \"/my h2 path/\");\n    }\n}\n"
  },
  {
    "path": "platform/platform-setup/src/test/java/org/bonitasoft/platform/setup/PlatformSetupIT.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup;\n\nimport static java.lang.System.lineSeparator;\nimport static org.apache.commons.lang3.SystemUtils.IS_OS_WINDOWS;\nimport static org.assertj.core.api.Assertions.*;\nimport static org.bonitasoft.platform.configuration.type.ConfigurationType.PLATFORM_ENGINE;\nimport static org.bonitasoft.platform.configuration.type.ConfigurationType.TENANT_PORTAL;\nimport static org.bonitasoft.platform.setup.PlatformSetup.BONITA_SETUP_FOLDER;\nimport static org.bonitasoft.platform.setup.PlatformSetup.PLATFORM_CONF_FOLDER_NAME;\nimport static org.junit.jupiter.api.Assumptions.assumeFalse;\n\nimport java.io.File;\nimport java.nio.charset.Charset;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.UUID;\n\nimport org.apache.commons.io.FileUtils;\nimport org.bonitasoft.platform.configuration.model.FullBonitaConfiguration;\nimport org.bonitasoft.platform.configuration.type.ConfigurationType;\nimport org.bonitasoft.platform.configuration.util.AllConfigurationResourceVisitor;\nimport org.bonitasoft.platform.exception.PlatformException;\nimport org.bonitasoft.platform.setup.jndi.MemoryJNDISetup;\nimport org.bonitasoft.platform.util.ConfigurationFolderUtil;\nimport org.bonitasoft.platform.version.VersionService;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Tag;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.boot.test.system.CapturedOutput;\nimport org.springframework.boot.test.system.OutputCaptureExtension;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.jdbc.JdbcTestUtils;\n\n/**\n * @author Baptiste Mesta\n */\n@ExtendWith({ SpringExtension.class, OutputCaptureExtension.class })\n@SpringBootTest(classes = {\n        PlatformSetupApplication.class\n})\nclass PlatformSetupIT {\n\n    @Value(\"${db.vendor}\")\n    private String dbVendor;\n\n    @Autowired\n    MemoryJNDISetup memoryJNDISetup;\n\n    @Autowired\n    JdbcTemplate jdbcTemplate;\n\n    @Autowired\n    PlatformSetup platformSetup;\n\n    @Autowired\n    VersionService versionService;\n\n    private final ConfigurationFolderUtil configurationFolderUtil = new ConfigurationFolderUtil();\n\n    @BeforeEach\n    void setUp() throws Exception {\n        System.clearProperty(BONITA_SETUP_FOLDER);\n        platformSetup.destroy();\n    }\n\n    @Test\n    @Tag(\"community-only\")\n    void init_method_should_init_table_and_insert_conf() throws Exception {\n        //when\n        platformSetup.init();\n\n        //then\n        final Integer sequences = jdbcTemplate.queryForObject(\"select count(*) from sequence\", Integer.class);\n        assertThat(sequences).isGreaterThan(1);\n        final List<String> platformRows = jdbcTemplate.queryForList(\"SELECT information FROM platform\", String.class);\n        assertThat(platformRows).hasSize(1);\n        assertThat(platformRows.get(0)).isNotBlank(); // In Community, should contain the initial case counter value for information\n        final int configurationFiles = JdbcTestUtils.countRowsInTable(jdbcTemplate, \"configuration\");\n        assertThat(configurationFiles).isGreaterThan(1);\n    }\n\n    @Test\n    void init_method_should_init_configuration_from_folder_if_exists(@TempDir Path setupFolder,\n            CapturedOutput capturedOutput) throws Exception {\n        //given\n        System.setProperty(BONITA_SETUP_FOLDER, setupFolder.toAbsolutePath().toString());\n        FileUtils.write(setupFolder.resolve(PLATFORM_CONF_FOLDER_NAME).resolve(\"initial\")\n                .resolve(\"platform_engine\")\n                .resolve(\"whatever.properties\").toFile(), \"custom content\", Charset.defaultCharset());\n        configurationFolderUtil.buildSqlFolder(setupFolder, dbVendor);\n\n        //when\n        platformSetup.init();\n\n        //then\n        List<Map<String, Object>> rows = jdbcTemplate\n                .queryForList(\"SELECT * FROM configuration WHERE resource_name = 'whatever.properties'\");\n        assertThat(rows).hasSize(1);\n        assertThat(rows.get(0)).containsEntry(\"resource_content\", \"custom content\".getBytes());\n        assertThat(capturedOutput.getOut())\n                .contains(\"Database will be initialized with configuration files from folder: \"\n                        + setupFolder.resolve(PLATFORM_CONF_FOLDER_NAME).resolve(\"initial\"));\n    }\n\n    @Test\n    @Tag(\"community-only\")\n    void init_method_should_store_tenant_portal_resources_from_classpath() throws Exception {\n        //when\n        platformSetup.init();\n        //then\n        List<Map<String, Object>> rows = jdbcTemplate\n                .queryForList(\"SELECT * FROM configuration WHERE content_type= '\"\n                        + ConfigurationType.TENANT_PORTAL + \"' ORDER BY resource_name\");\n        assertThat(rows).hasSize(9);\n        int rowId = 0;\n        assertThat(rows.get(rowId++)).containsEntry(\"RESOURCE_NAME\", \"compound-permissions-mapping-custom.properties\");\n        assertThat(rows.get(rowId++)).containsEntry(\"RESOURCE_NAME\",\n                \"compound-permissions-mapping-internal.properties\");\n        assertThat(rows.get(rowId++)).containsEntry(\"RESOURCE_NAME\", \"compound-permissions-mapping.properties\");\n        assertThat(rows.get(rowId++)).containsEntry(\"RESOURCE_NAME\", \"console-config.properties\");\n        assertThat(rows.get(rowId++)).containsEntry(\"RESOURCE_NAME\", \"custom-permissions-mapping.properties\");\n        assertThat(rows.get(rowId++)).containsEntry(\"RESOURCE_NAME\", \"resources-permissions-mapping-custom.properties\");\n        assertThat(rows.get(rowId++)).containsEntry(\"RESOURCE_NAME\",\n                \"resources-permissions-mapping-internal.properties\");\n        assertThat(rows.get(rowId++)).containsEntry(\"RESOURCE_NAME\", \"resources-permissions-mapping.properties\");\n        assertThat(rows.get(rowId)).containsEntry(\"RESOURCE_NAME\", \"security-config.properties\");\n    }\n\n    @Test\n    void init_method_should_store_platform_portal_resources_from_classpath() throws Exception {\n        //when\n        platformSetup.init();\n        //then\n        List<Map<String, Object>> rows = jdbcTemplate\n                .queryForList(\"SELECT * FROM configuration WHERE content_type= '\" + ConfigurationType.PLATFORM_PORTAL\n                        + \"' ORDER BY resource_name\");\n        assertThat(rows).hasSize(1);\n        assertThat(rows.get(0)).containsEntry(\"RESOURCE_NAME\", \"security-config.properties\");\n    }\n\n    @Test\n    @Tag(\"community-only\")\n    void should_extract_configuration(@TempDir Path setupFolder) throws Exception {\n        //given\n        platformSetup.init();\n\n        //when\n        System.setProperty(BONITA_SETUP_FOLDER, setupFolder.toAbsolutePath().toString());\n        platformSetup.pull();\n\n        //then\n        File folderContainingResultOfGet = setupFolder.resolve(PLATFORM_CONF_FOLDER_NAME)\n                .resolve(\"current\").toFile();\n        assertThat(folderContainingResultOfGet).as(\"should retrieve config files\")\n                .exists()\n                .isDirectory();\n\n        List<FullBonitaConfiguration> configurations = new ArrayList<>();\n        AllConfigurationResourceVisitor allConfigurationResourceVisitor = new AllConfigurationResourceVisitor(\n                configurations);\n        Files.walkFileTree(setupFolder, allConfigurationResourceVisitor);\n\n        assertThat(configurations).extracting(\"resourceName\").containsOnly(\n                \"bonita-platform-community-custom.properties\",\n                \"bonita-platform-custom.xml\",\n                \"security-config.properties\",\n                \"bonita-tenant-community-custom.properties\",\n                \"bonita-tenants-custom.xml\",\n                \"compound-permissions-mapping.properties\",\n                \"compound-permissions-mapping-custom.properties\",\n                \"compound-permissions-mapping-internal.properties\",\n                \"console-config.properties\",\n                \"custom-permissions-mapping.properties\",\n                \"resources-permissions-mapping.properties\",\n                \"resources-permissions-mapping-custom.properties\",\n                \"resources-permissions-mapping-internal.properties\");\n    }\n\n    @Test\n    void init_method_should_log_when_created(CapturedOutput capturedOutput) throws Exception {\n        //given\n        assertThat(platformSetup.isPlatformAlreadyCreated()).isFalse();\n\n        //when\n        platformSetup.init();\n\n        //then\n        assertThat(platformSetup.isPlatformAlreadyCreated()).isTrue();\n\n        assertThat(capturedOutput.getOut()).as(\"should setup log message\")\n                .doesNotContain(\"Platform is already created. Nothing to do.\")\n                .contains(\"Platform created.\")\n                .contains(\"Initial configuration files successfully pushed to database\");\n    }\n\n    @Test\n    void init_method_should_upgrade_default_configuration_when_already_created(CapturedOutput capturedOutput)\n            throws Exception {\n        //given\n        platformSetup.init();\n\n        //when\n        platformSetup.init();\n\n        //then\n        assertThat(platformSetup.isPlatformAlreadyCreated()).isTrue();\n\n        assertThat(capturedOutput.getOut())\n                .containsOnlyOnce(\"Platform created.\")\n                .containsOnlyOnce(\"Platform is already created.\")\n                .containsOnlyOnce(\"Upgrading default configuration\");\n    }\n\n    @Test\n    void push_method_should_log_when_created_and_create_backup(@TempDir File setupFolder,\n            CapturedOutput capturedOutput) throws Exception {\n        // given\n        platformSetup.init();\n        System.setProperty(BONITA_SETUP_FOLDER, setupFolder.getAbsolutePath());\n        configurationFolderUtil.buildCurrentFolder(setupFolder.toPath());\n\n        // when\n        platformSetup.forcePush();\n\n        // then\n        assertThat(setupFolder.listFiles()).hasSize(1);\n        List<File> backupDirectory = Arrays.stream(setupFolder.listFiles()[0].listFiles())\n                .filter(it -> it.getName().contains(\"backup\")).toList();\n        assertThat(backupDirectory).hasSize(1);\n        assertThat(capturedOutput.getOut())\n                .contains(\"Backup directory created:\")\n                .as(\"should push new configuration and log message\")\n                .contains(\"Configuration files successfully pushed to database. \" +\n                        \"You can now restart Bonita to reflect your changes.\");\n    }\n\n    @Test\n    void push_should_fail_if_required_folder_would_be_deleted(@TempDir Path temporaryFolder) throws Exception {\n        // on windows, the test fails to delete the 'platform init engine' directory\n        // so do not run it for now\n        assumeFalse(IS_OS_WINDOWS);\n\n        // given\n        platformSetup.init();\n        Path setupFolder = Files.createDirectory(temporaryFolder.resolve(\"conf\"));\n        System.setProperty(BONITA_SETUP_FOLDER, setupFolder.toAbsolutePath().toString());\n        platformSetup.pull();\n        final Path platformEngine = setupFolder.resolve(\"platform_conf\").resolve(\"current\")\n                .resolve(\"platform_engine\");\n        FileUtils.deleteDirectory(platformEngine.toFile());\n\n        // when - then\n        assertThatExceptionOfType(PlatformException.class)\n                .isThrownBy(platformSetup::push)\n                .withMessageStartingWith(\"You are trying to remove a protected folder from configuration\")\n                .withMessageContaining(platformEngine.toString())\n                .withMessageContaining(\"To restore the deleted folders\");\n    }\n\n    @Test\n    void push_should_throw_exception_when_platform_is_not_created() {\n        //given\n        assertThat(platformSetup.isPlatformAlreadyCreated()).isFalse();\n\n        // when - then\n        assertThatExceptionOfType(PlatformException.class)\n                .isThrownBy(platformSetup::push)\n                .withMessage(\"Platform is not created. Run 'setup init' first.\");\n    }\n\n    @Test\n    void clean_method_should_delete_and_log(@TempDir Path temporaryFolder, CapturedOutput capturedOutput)\n            throws Exception {\n        //given\n        final Path path = Files.createDirectory(temporaryFolder.resolve(\"afterClean\"));\n        final Path licensePath = Files.createDirectory(temporaryFolder.resolve(\"licenses\"));\n        platformSetup.init();\n\n        //when\n        platformSetup.clean();\n\n        //then\n        assertThat(capturedOutput.getOut()).as(\"should log message\")\n                .isNotEmpty()\n                .contains(\"Execute DeleteAllConfigurationInTransaction transaction.\");\n\n        platformSetup.pull(path, licensePath);\n        List<FullBonitaConfiguration> configurations = new ArrayList<>();\n        Files.walkFileTree(path, new AllConfigurationResourceVisitor(configurations));\n        assertThat(configurations).as(\"should remove all files\").isEmpty();\n\n        Files.walkFileTree(licensePath, new AllConfigurationResourceVisitor(configurations));\n        assertThat(configurations).as(\"should remove all files\").isEmpty();\n    }\n\n    @Test\n    void push_method_should_clean_previous_config(@TempDir Path temporaryFolder) throws Exception {\n        //given\n        List<FullBonitaConfiguration> configurations = new ArrayList<>();\n        final Path initPath = Files.createDirectory(temporaryFolder.resolve(\"init\"));\n        final Path pushPath = Files.createDirectory(temporaryFolder.resolve(\"push\"));\n        final Path checkPath = Files.createDirectory(temporaryFolder.resolve(\"check\"));\n        final Path licensesPath = Files.createDirectory(temporaryFolder.resolve(\"lic\"));\n\n        FileUtils.writeByteArrayToFile(\n                initPath.resolve(PLATFORM_CONF_FOLDER_NAME).resolve(\"initial\")\n                        .resolve(PLATFORM_ENGINE.name().toLowerCase()).resolve(\"initial.properties\")\n                        .toFile(),\n                \"key1=value1\".getBytes());\n\n        FileUtils.writeByteArrayToFile(\n                pushPath.resolve(PLATFORM_CONF_FOLDER_NAME).resolve(\"current\")\n                        .resolve(TENANT_PORTAL.name().toLowerCase())\n                        .resolve(\"current.properties\").toFile(),\n                \"key2=value2\".getBytes());\n\n        System.setProperty(BONITA_SETUP_FOLDER, initPath.toString());\n        configurationFolderUtil.buildSqlFolder(initPath.toFile().toPath(), dbVendor);\n        platformSetup.init();\n\n        //when\n        System.setProperty(BONITA_SETUP_FOLDER, pushPath.toString());\n        platformSetup.forcePush();\n\n        //then\n        platformSetup.pull(checkPath, licensesPath);\n        Files.walkFileTree(checkPath, new AllConfigurationResourceVisitor(configurations));\n        assertThat(configurations).as(\"should remove all files\").hasSize(1)\n                .extracting(\"resourceName\").containsOnly(\"current.properties\");\n    }\n\n    @Test\n    void push_method_should_throw_exception_if_no_current_folder(@TempDir Path temporaryFolder) throws Exception {\n        //given\n        List<FullBonitaConfiguration> configurations = new ArrayList<>();\n        final Path confFolder = Files.createDirectory(temporaryFolder.resolve(UUID.randomUUID().toString()));\n        configurationFolderUtil.buildPlatformEngineFolder(confFolder);\n        configurationFolderUtil.buildSqlFolder(confFolder, dbVendor);\n\n        System.setProperty(BONITA_SETUP_FOLDER, confFolder.toString());\n        platformSetup.init();\n        Path current = confFolder.resolve(\"platform_conf\").resolve(\"current\");\n\n        //when - then\n        assertThatExceptionOfType(PlatformException.class).isThrownBy(platformSetup::push)\n                .withMessage(\"Unable to push configuration from %1$s, as directory does not exists. \" +\n                        \"To modify your configuration, run 'setup pull', \" +\n                        \"update your configuration files from %1$s folder, \" +\n                        \"and then push your new configuration.\", current);\n\n        platformSetup.pull();\n        Files.walkFileTree(current, new AllConfigurationResourceVisitor(configurations));\n        assertThat(configurations).as(\"should have kept old files\").hasSize(1)\n                .extracting(\"resourceName\").containsOnly(\"initialConfig.properties\");\n    }\n\n    @Test\n    void should_push_check_platform_version(@TempDir Path temporaryFolder) throws Exception {\n        //given\n        platformSetup.init();\n        jdbcTemplate.execute(\"UPDATE platform SET version='bad version'\");\n\n        final Path confFolder = Files.createDirectory(temporaryFolder.resolve(UUID.randomUUID().toString()));\n        configurationFolderUtil.buildCurrentFolder(confFolder);\n        System.setProperty(BONITA_SETUP_FOLDER, confFolder.toFile().getAbsolutePath());\n\n        // when - then\n        assertThatExceptionOfType(PlatformException.class)\n                .isThrownBy(platformSetup::push)\n                .withMessage(\"The version of the platform (binaries) you are running [\"\n                        + versionService.getPlatformSetupVersion() + \"] only support database schema in version [\"\n                        + versionService.getSupportedDatabaseSchemaVersion() + \"]\" +\n                        \" but the current database schema version is [bad version].\" +\n                        \" You might need to migrate your platform or use a different version of the binaries.\");\n    }\n\n    @Test\n    void should_pull_check_platform_version() throws Exception {\n        //given\n        platformSetup.init();\n        jdbcTemplate.execute(\"UPDATE platform SET version='bad version'\");\n\n        // when - then\n        assertThatExceptionOfType(PlatformException.class)\n                .isThrownBy(platformSetup::pull)\n                .withMessage(\"The version of the platform (binaries) you are running [\"\n                        + versionService.getPlatformSetupVersion() + \"] only support database schema in version [\"\n                        + versionService.getSupportedDatabaseSchemaVersion() + \"]\" +\n                        \" but the current database schema version is [bad version].\" +\n                        \" You might need to migrate your platform or use a different version of the binaries.\");\n    }\n\n    @Test\n    void pushLicences_should_pass_if_licence_folder_does_not_exists() throws Exception {\n        platformSetup.initProperties();\n        assertThatNoException().isThrownBy(platformSetup::preventFromPushingZeroLicense);\n    }\n\n    @Test\n    void should_not_fail_when_pulling_twice_in_the_same_jvm(@TempDir Path temporaryFolder) throws Exception {\n        final Path setupFolder = Files.createDirectory(temporaryFolder.resolve(UUID.randomUUID().toString()));\n        System.setProperty(BONITA_SETUP_FOLDER, setupFolder.toString());\n        configurationFolderUtil.buildPlatformEngineFolder(setupFolder);\n        configurationFolderUtil.buildSqlFolder(setupFolder, dbVendor);\n        platformSetup.init();\n        platformSetup.pull();\n        assertThatNoException().isThrownBy(platformSetup::pull);\n    }\n\n    @Test\n    void pushLicences_should_fail_if_licence_folder_exists_but_is_empty(@TempDir Path temporaryFolder)\n            throws Exception {\n        final Path setupFolder = Files.createDirectory(temporaryFolder.resolve(UUID.randomUUID().toString()));\n        System.setProperty(BONITA_SETUP_FOLDER, setupFolder.toString());\n        final Path platformConf = configurationFolderUtil.buildPlatformConfFolder(setupFolder);\n\n        final Path licenseFolder = platformConf.resolve(\"licenses\");\n        Files.createDirectories(licenseFolder);\n\n        platformSetup.initProperties();\n\n        assertThatExceptionOfType(PlatformException.class)\n                .isThrownBy(platformSetup::preventFromPushingZeroLicense)\n                .withMessageStartingWith(\"No license (.lic file) found.\" + lineSeparator()\n                        + \"This would prevent Bonita Platform subscription edition\"\n                        + \" to start normally.\" + lineSeparator() + \"Place your license file\");\n    }\n\n    @Test\n    void pushLicences_should_fail_if_no_license_file_with_lic_extension_exists(@TempDir Path temporaryFolder)\n            throws Exception {\n        final Path setupFolder = Files.createDirectory(temporaryFolder.resolve(UUID.randomUUID().toString()));\n        System.setProperty(BONITA_SETUP_FOLDER, setupFolder.toString());\n        final Path platformConf = configurationFolderUtil.buildPlatformConfFolder(setupFolder);\n\n        final Path licenseFolder = platformConf.resolve(\"licenses\");\n        Files.createDirectories(licenseFolder);\n        Files.createFile(licenseFolder.resolve(\"bonita-file.renamed\"));\n\n        platformSetup.initProperties();\n\n        assertThatExceptionOfType(PlatformException.class)\n                .isThrownBy(platformSetup::preventFromPushingZeroLicense)\n                .withMessageStartingWith(\"No license (.lic file) found.\" + lineSeparator()\n                        + \"This would prevent Bonita Platform subscription edition\"\n                        + \" to start normally.\" + lineSeparator() + \"Place your license file\");\n    }\n\n    @Test\n    void init_method_should_update_configuration_files(@TempDir Path temporaryFolder) throws Exception {\n        //given\n        Path setupFolder = Files.createDirectory(temporaryFolder.resolve(\"conf\"));\n        System.setProperty(BONITA_SETUP_FOLDER, setupFolder.toAbsolutePath().toString());\n        final File permissionFile = setupFolder.resolve(PLATFORM_CONF_FOLDER_NAME).resolve(\"initial\")\n                .resolve(\"tenant_portal\")\n                .resolve(\"resources-permissions-mapping.properties\").toFile();\n        FileUtils.write(permissionFile, \"default 7.5.4 content\", Charset.defaultCharset());\n        configurationFolderUtil.buildSqlFolder(setupFolder, dbVendor);\n        platformSetup.init();\n\n        // Simulate new 7.6.0 configuration file content:\n        final String new_7_6_0_content = \"default 7.6.0 content\";\n        FileUtils.write(permissionFile, new_7_6_0_content, Charset.defaultCharset());\n\n        //when\n        platformSetup.init();\n\n        //then\n        Map<String, Object> rows = jdbcTemplate\n                .queryForMap(\n                        \"SELECT * FROM configuration WHERE resource_name = 'resources-permissions-mapping.properties'\");\n        assertThat(rows.get(\"RESOURCE_CONTENT\")).isEqualTo(new_7_6_0_content.getBytes());\n    }\n\n    @Test\n    void init_on_existing_platform_should_add_new_config_files(CapturedOutput capturedOutput) throws Exception {\n        //given\n        platformSetup.init();\n        final String countConfigFile = \"SELECT * FROM configuration WHERE resource_name = 'bonita-tenant-community-custom.properties'\";\n        assertThat(jdbcTemplate.queryForList(countConfigFile)).hasSize(1)\n                .anyMatch(map -> map.get(\"CONTENT_TYPE\").equals(\"TENANT_ENGINE\"));\n\n        // Delete it to check that init method adds it again:\n        jdbcTemplate\n                .update(\"DELETE from configuration WHERE resource_name = 'bonita-tenant-community-custom.properties'\");\n\n        assertThat(jdbcTemplate.queryForList(countConfigFile)).isEmpty();\n\n        //when\n        platformSetup.init();\n\n        //then\n        assertThat(jdbcTemplate.queryForList(countConfigFile)).hasSize(1)\n                .anyMatch(map -> map.get(\"CONTENT_TYPE\").equals(\"TENANT_ENGINE\"));\n        assertThat(capturedOutput.getOut())\n                .contains(\"New configuration file detected 'bonita-tenant-community-custom.properties'\");\n    }\n}\n"
  },
  {
    "path": "platform/platform-setup/src/test/java/org/bonitasoft/platform/setup/PlatformSetupTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup;\n\nimport static org.apache.commons.io.FilenameUtils.separatorsToSystem;\nimport static org.assertj.core.api.Assertions.*;\nimport static org.bonitasoft.platform.setup.PlatformSetup.*;\nimport static org.mockito.Mockito.doReturn;\n\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.sql.Connection;\nimport java.sql.DatabaseMetaData;\n\nimport javax.sql.DataSource;\n\nimport org.bonitasoft.platform.configuration.ConfigurationService;\nimport org.bonitasoft.platform.configuration.model.LightBonitaConfiguration;\nimport org.bonitasoft.platform.database.DatabaseVendor;\nimport org.bonitasoft.platform.exception.PlatformException;\nimport org.bonitasoft.platform.util.ConfigurationFolderUtil;\nimport org.bonitasoft.platform.version.VersionService;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.RestoreSystemProperties;\nimport org.junit.rules.TemporaryFolder;\nimport org.junit.rules.TestRule;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\nimport org.springframework.test.util.ReflectionTestUtils;\n\n/**\n * @author Baptiste Mesta\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class PlatformSetupTest {\n\n    @Mock\n    private DataSource dataSource;\n    @Mock\n    private Connection connection;\n    @Mock\n    private DatabaseMetaData metaData;\n    @Mock\n    private ScriptExecutor scriptExecutor;\n    @Mock\n    private ConfigurationService configurationService;\n    @Mock\n    private VersionService versionService;\n    @InjectMocks\n    private PlatformSetup platformSetup;\n\n    private final ConfigurationFolderUtil configurationFolderUtil = new ConfigurationFolderUtil();\n\n    @Rule\n    public TemporaryFolder temporaryFolder = new TemporaryFolder();\n    @Rule\n    public final TestRule restoreSystemProperties = new RestoreSystemProperties();\n\n    @Before\n    public void before() throws Exception {\n        setPlatformSetupDbVendor(DatabaseVendor.H2.getValue());\n        setPlatformSetupBdmDbVendor(DatabaseVendor.H2.getValue());\n        doReturn(connection).when(dataSource).getConnection();\n        doReturn(metaData).when(connection).getMetaData();\n    }\n\n    private void setPlatformSetupDbVendor(String dbVendor) {\n        ReflectionTestUtils.setField(platformSetup, \"dbVendor\", dbVendor);\n    }\n\n    private void setPlatformSetupBdmDbVendor(String bdmDbVendor) {\n        ReflectionTestUtils.setField(platformSetup, \"bdmDbVendor\", bdmDbVendor);\n    }\n\n    @Test\n    public void should_not_check_license_if_platform_already_init() throws Exception {\n        final Path setupFolder = temporaryFolder.newFolder().toPath();\n        System.setProperty(BONITA_SETUP_FOLDER, setupFolder.toString());\n        final Path platformConf = configurationFolderUtil.buildPlatformConfFolder(setupFolder);\n        final Path licenseFolder = platformConf.resolve(\"licenses\");\n        Files.createDirectories(licenseFolder);\n        doReturn(true).when(scriptExecutor).isPlatformAlreadyCreated();\n\n        //no exception\n        assertThatNoException().isThrownBy(platformSetup::init);\n    }\n\n    @Test\n    public void should_fail_if_init_with_no_license() throws Exception {\n        final Path setupFolder = temporaryFolder.newFolder().toPath();\n        System.setProperty(BONITA_SETUP_FOLDER, setupFolder.toString());\n        final Path platformConf = configurationFolderUtil.buildPlatformConfFolder(setupFolder);\n        final Path licenseFolder = platformConf.resolve(\"licenses\");\n        Files.createDirectories(licenseFolder);\n\n        assertThatExceptionOfType(PlatformException.class)\n                .isThrownBy(platformSetup::init)\n                .withMessageStartingWith(\"No license (.lic file) found.\");\n    }\n\n    @Test\n    public void should_fail_if_init_with_incorrect_database_vendor() {\n        //given\n        String dbVendor = \"foobar\";\n        setPlatformSetupDbVendor(dbVendor);\n\n        //when - then\n        assertThatExceptionOfType(IllegalArgumentException.class)\n                .isThrownBy(platformSetup::init)\n                .withMessage(\"Unknown database vendor: %s\", dbVendor);\n    }\n\n    @Test\n    public void should_fail_if_init_with_incorrect_bdm_database_vendor() {\n        //given\n        String dbVendor = \"foobar\";\n        setPlatformSetupBdmDbVendor(dbVendor);\n\n        //when - then\n        assertThatExceptionOfType(IllegalArgumentException.class)\n                .isThrownBy(platformSetup::init)\n                .withMessage(\"Unknown database vendor: %s\", dbVendor);\n    }\n\n    @Test\n    public void should_fail_if_init_with_unsupported_database_vendor() {\n        //given\n        DatabaseVendor dbVendor = DatabaseVendor.ORACLE;\n        setPlatformSetupDbVendor(dbVendor.getValue());\n\n        //when - then\n        assertThatExceptionOfType(PlatformException.class)\n                .isThrownBy(platformSetup::init)\n                .withMessage(\"Database vendor '%s' is not supported with the community edition\", dbVendor);\n    }\n\n    @Test\n    public void should_fail_if_init_with_unsupported_bdm_database_vendor() {\n        //given\n        DatabaseVendor dbVendor = DatabaseVendor.ORACLE;\n        setPlatformSetupBdmDbVendor(dbVendor.getValue());\n\n        //when - then\n        assertThatExceptionOfType(PlatformException.class)\n                .isThrownBy(platformSetup::init)\n                .withMessage(\"Database vendor '%s' is not supported with the community edition\", dbVendor);\n    }\n\n    @Test\n    public void should_not_fail_if_init_with_postgres_database_vendor() {\n        //given\n        setPlatformSetupDbVendor(DatabaseVendor.POSTGRES.getValue());\n\n        //when - then\n        assertThatNoException().isThrownBy(platformSetup::init);\n    }\n\n    @Test\n    public void should_not_fail_if_init_with_postgres_bdm_database_vendor() {\n        //given\n        setPlatformSetupBdmDbVendor(DatabaseVendor.POSTGRES.getValue());\n\n        //when - then\n        assertThatNoException().isThrownBy(platformSetup::init);\n    }\n\n    @Test\n    public void should_init_dbVendor_with_system_prop_if_null() throws Exception {\n        //given\n        setPlatformSetupDbVendor(null);\n        DatabaseVendor dbVendor = DatabaseVendor.POSTGRES;\n        System.setProperty(BONITA_DB_VENDOR_PROPERTY, dbVendor.getValue());\n\n        //when\n        platformSetup.initProperties();\n\n        //then\n        assertThat(platformSetup.dbVendor).isEqualTo(dbVendor.getValue());\n    }\n\n    @Test\n    public void should_init_bdmDbVendor_with_system_prop_if_null() throws Exception {\n        //given\n        setPlatformSetupBdmDbVendor(null);\n        DatabaseVendor dbVendor = DatabaseVendor.POSTGRES;\n        System.setProperty(BONITA_BDM_DB_VENDOR_PROPERTY, dbVendor.getValue());\n\n        //when\n        platformSetup.initProperties();\n\n        //then\n        assertThat(platformSetup.bdmDbVendor).isEqualTo(dbVendor.getValue());\n    }\n\n    @Test\n    public void getFolderFromConfiguration_should_work_for_platform_level_folder() throws Exception {\n        // given:\n        final Path setupFolder = temporaryFolder.newFolder().toPath();\n        System.setProperty(BONITA_SETUP_FOLDER, setupFolder.toString());\n        platformSetup.initProperties();\n\n        LightBonitaConfiguration configuration = new LightBonitaConfiguration(\"some_folder\");\n\n        // when:\n        final Path folder = platformSetup.getFolderFromConfiguration(configuration);\n\n        // then:\n        assertThat(folder).hasToString(separatorsToSystem(setupFolder + \"/platform_conf/current/some_folder\"));\n    }\n\n    @Test\n    public void getFolderFromConfiguration_should_work_for_tenant_level_folder() throws Exception {\n        // given:\n        final Path setupFolder = temporaryFolder.newFolder().toPath();\n        System.setProperty(BONITA_SETUP_FOLDER, setupFolder.toString());\n        platformSetup.initProperties();\n\n        LightBonitaConfiguration configuration = new LightBonitaConfiguration(\"TENANT-LEVEL-FOLDER\");\n\n        // when:\n        final Path folder = platformSetup.getFolderFromConfiguration(configuration);\n\n        // then:\n        assertThat(folder)\n                .hasToString(separatorsToSystem(setupFolder + \"/platform_conf/current/tenant-level-folder\"));\n    }\n}\n"
  },
  {
    "path": "platform/platform-setup/src/test/java/org/bonitasoft/platform/setup/ScriptExecutorIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup;\n\nimport static org.assertj.core.api.Assertions.*;\nimport static org.bonitasoft.platform.setup.PlatformSetup.BONITA_SETUP_FOLDER;\nimport static org.bonitasoft.platform.setup.PlatformSetup.PLATFORM_CONF_FOLDER_NAME;\nimport static org.bonitasoft.platform.setup.ScriptExecutor.FAIL_ON_ERROR;\n\nimport java.io.File;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.Map;\n\nimport org.bonitasoft.platform.setup.jndi.MemoryJNDISetup;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.ClearSystemProperties;\nimport org.junit.rules.TemporaryFolder;\nimport org.junit.rules.TestRule;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.test.jdbc.JdbcTestUtils;\n\n/**\n * author Laurent Leseigneur\n */\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = {\n        PlatformSetupApplication.class\n})\npublic class ScriptExecutorIT {\n\n    @Rule\n    public TestRule clean = new ClearSystemProperties(BONITA_SETUP_FOLDER);\n\n    @Autowired\n    MemoryJNDISetup memoryJNDISetup;\n\n    @Autowired\n    JdbcTemplate jdbcTemplate;\n\n    @Autowired\n    private ScriptExecutor scriptExecutor;\n\n    @Value(\"${db.vendor}\")\n    String dbVendor;\n\n    @Rule\n    public TemporaryFolder temporaryFolder = new TemporaryFolder();\n\n    @Before\n    public void before() throws Exception {\n        removeSetupFolderProperty();\n    }\n\n    @After\n    public void after() throws Exception {\n        removeSetupFolderProperty();\n        scriptExecutor.deleteTables();\n    }\n\n    private void removeSetupFolderProperty() {\n        System.clearProperty(BONITA_SETUP_FOLDER);\n    }\n\n    @Test\n    public void should_be_able_to_create_platform_tables() throws Exception {\n        scriptExecutor.createAndInitializePlatformIfNecessary();\n        final Integer sequences = jdbcTemplate.queryForObject(\"select count(*) from sequence\", Integer.class);\n        assertThat(sequences).isEqualTo(69);\n\n        final int platformRows = JdbcTestUtils.countRowsInTable(jdbcTemplate, \"platform\");\n        assertThat(platformRows).isEqualTo(1);\n        final Map<String, Object> rowPlatform = jdbcTemplate.queryForMap(\"select * from platform\");\n        assertThat(\"\" + rowPlatform.get(\"id\")).isEqualTo(\"1\"); // convert to String as not all RDBMS convert the same way (long, int, bigDecimal...)\n        assertThat(rowPlatform).containsEntry(\"created_by\", \"platformAdmin\");\n    }\n\n    @Test\n    public void executeSQLResource_should_be_successful_using_filesystem_scripts() throws Exception {\n        //given\n        final File confFolder = temporaryFolder.newFolder();\n        final Path createTableScript = confFolder.toPath().resolve(PLATFORM_CONF_FOLDER_NAME).resolve(\"sql\")\n                .resolve(dbVendor).resolve(\"myScript.sql\");\n        final Path dropTableScript = confFolder.toPath().resolve(PLATFORM_CONF_FOLDER_NAME).resolve(\"sql\")\n                .resolve(dbVendor).resolve(\"cleanup.sql\");\n        Files.createDirectories(createTableScript.getParent());\n        final String tableName = dbVendor + \"_test\";\n        final String createTable = \"CREATE TABLE \" + tableName + \" (id INT NOT NULL)\";\n        Files.write(createTableScript, createTable.getBytes());\n        Files.write(dropTableScript, (\"DROP TABLE \" + tableName).getBytes());\n        System.setProperty(BONITA_SETUP_FOLDER, confFolder.getAbsolutePath());\n\n        //when - then\n        assertThatNoException().isThrownBy(() -> scriptExecutor.executeSQLResource(\"myScript.sql\", FAIL_ON_ERROR));\n        final int platformRows = JdbcTestUtils.countRowsInTable(jdbcTemplate, tableName);\n        assertThatNoException().isThrownBy(() -> scriptExecutor.executeSQLResource(\"cleanup.sql\", FAIL_ON_ERROR));\n        assertThat(platformRows).as(\"should create empty table from filesystem \").isZero();\n    }\n\n    @Test\n    public void executeSQLResource_should_fail_when_script_is_missing() throws Exception {\n        //given\n        final File confFolder = temporaryFolder.newFolder();\n        final Path sqlScript = confFolder.toPath().resolve(PLATFORM_CONF_FOLDER_NAME).resolve(\"sql\").resolve(dbVendor)\n                .resolve(\"missingScript.sql\");\n        System.setProperty(BONITA_SETUP_FOLDER, confFolder.getAbsolutePath());\n\n        //when - then\n        assertThatExceptionOfType(RuntimeException.class)\n                .isThrownBy(() -> scriptExecutor.executeSQLResource(\"missingScript.sql\", FAIL_ON_ERROR))\n                .withMessage(\"SQL resource file not found in filesystem: \" + sqlScript.toFile().getAbsolutePath());\n    }\n}\n"
  },
  {
    "path": "platform/platform-setup/src/test/java/org/bonitasoft/platform/setup/ScriptExecutorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup;\n\nimport static org.mockito.Mockito.*;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n/**\n * author Emmanuel Duchastenier\n */\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = PlatformSetupApplication.class,\n        // use an in-memory h2 database for those tests\n        properties = \"h2.url=jdbc:h2:mem:${db.database.name}\")\npublic class ScriptExecutorTest {\n\n    @Autowired\n    private ScriptExecutor scriptExecutor;\n\n    @Test\n    public void createAndInitializePlatformIfNecessary_should_not_create_platform_if_already_existing()\n            throws Exception {\n        //given\n        ScriptExecutor spy = spy(scriptExecutor);\n        doReturn(true).when(spy).isPlatformAlreadyCreated();\n\n        //when\n        spy.createAndInitializePlatformIfNecessary();\n\n        //then\n        verify(spy, times(0)).createTables();\n        verify(spy, times(0)).initializePlatformStructure();\n        verify(spy, times(0)).insertPlatform();\n    }\n\n    @Test\n    public void createAndInitializePlatformIfNecessary_should_create_platform_if_not_already_existing()\n            throws Exception {\n        //given\n        ScriptExecutor spy = spy(scriptExecutor);\n\n        //when\n        spy.createAndInitializePlatformIfNecessary();\n\n        //then\n        verify(spy).createTables();\n        verify(spy).initializePlatformStructure();\n        verify(spy).insertPlatform();\n\n        //cleanup\n        scriptExecutor.deleteTables();\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-setup/src/test/java/org/bonitasoft/platform/setup/command/CommandTestUtils.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup.command;\n\nimport org.apache.commons.cli.CommandLine;\nimport org.apache.commons.cli.GnuParser;\nimport org.apache.commons.cli.Options;\nimport org.apache.commons.cli.ParseException;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class CommandTestUtils {\n\n    public static CommandLine buildCommandLine(String... strings) throws ParseException {\n        return new GnuParser().parse(new Options(), strings);\n    }\n}\n"
  },
  {
    "path": "platform/platform-setup/src/test/java/org/bonitasoft/platform/setup/command/HelpCommandTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup.command;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Fail.fail;\nimport static org.bonitasoft.platform.setup.command.CommandTestUtils.buildCommandLine;\n\nimport java.util.Arrays;\n\nimport org.apache.commons.cli.CommandLine;\nimport org.apache.commons.cli.Options;\nimport org.bonitasoft.platform.exception.PlatformException;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.SystemErrRule;\nimport org.junit.contrib.java.lang.system.SystemOutRule;\n\n/**\n * @author Baptiste Mesta\n */\npublic class HelpCommandTest {\n\n    @Rule\n    public SystemOutRule systemOutRule = new SystemOutRule().enableLog().muteForSuccessfulTests();\n    @Rule\n    public SystemErrRule systemErrRule = new SystemErrRule().enableLog().muteForSuccessfulTests();\n    private HelpCommand helpCommand;\n\n    @Before\n    public void before() throws Exception {\n        helpCommand = new HelpCommand();\n        helpCommand.setCommands(\n                Arrays.asList(new PlatformSetupCommand(\"command1\", \"summary1\", \"description of command 1\", null) {\n\n                    @Override\n                    public void execute(Options options, CommandLine commandLine) throws PlatformException {\n\n                    }\n                }, new PlatformSetupCommand(\"command2\", \"summary2\", \"description of command 2\", null) {\n\n                    @Override\n                    public void execute(Options options, CommandLine commandLine) throws PlatformException {\n\n                    }\n                }, helpCommand));\n    }\n\n    @Test\n    public void should_print_only_usage_when_no_command_is_given() throws Exception {\n        //when\n        try {\n            helpCommand.execute(new Options(), buildCommandLine());\n            fail(\"no option should throw exception\");\n        } catch (CommandException e) {\n            assertThat(e.getMessage()).isEqualTo(\"Need to specify a command, see usage above.\");\n        }\n        //then\n        assertThat(systemOutRule.getLog()).as(\"contains the usage\").contains(\"usage: setup ( command1 | command2 )\");\n        assertThat(systemOutRule.getLog()).as(\"do not contains other help\").doesNotContain(\"command1  --  summary1\");\n        assertThat(systemOutRule.getLog()).as(\"contains how to run help\")\n                .contains(\"use `setup help` or `setup help <command>` for more details\");\n    }\n\n    @Test\n    public void should_print_common_help_when_asking_help_on_unknown_command() throws Exception {\n        try {\n            helpCommand.execute(new Options(), buildCommandLine(\"help\", \"thisIsAnUnknownCommand\"));\n            fail(\"should throw exception\");\n        } catch (CommandException e) {\n            assertThat(e.getMessage()).isEqualTo(\"ERROR: no command named: thisIsAnUnknownCommand\");\n        }\n        assertThat(systemOutRule.getLog()).as(\"contains the usage\").contains(\"usage: setup ( command1 | command2 )\");\n        assertThat(systemOutRule.getLog()).as(\"contains how to run help\")\n                .contains(\"use `setup help` or `setup help <command>` for more details\");\n    }\n\n    @Test\n    public void should_print_only_usage_when_called_with_unknown_command() throws Exception {\n        try {\n            helpCommand.execute(new Options(), buildCommandLine(\"thisIsAnUnknownCommand\"));\n            fail(\"should throw exception\");\n        } catch (CommandException e) {\n            assertThat(e.getMessage()).isEqualTo(\"ERROR: no command named: thisIsAnUnknownCommand\");\n        }\n        assertThat(systemOutRule.getLog()).as(\"contains the usage\").contains(\"usage: setup ( command1 | command2 )\");\n        assertThat(systemOutRule.getLog()).as(\"do not contains other help\").doesNotContain(\"command1  --  summary1\");\n        assertThat(systemOutRule.getLog()).as(\"contains how to run help\")\n                .contains(\"use `setup help` or `setup help <command>` for more details\");\n    }\n\n    @Test\n    public void should_print_command_help_when_asking_help_on_a_command() throws Exception {\n        helpCommand.execute(new Options(), buildCommandLine(\"help\", \"command2\"));\n\n        assertThat(systemOutRule.getLog()).contains(\"description of command 2\");\n        assertThat(systemOutRule.getLog()).contains(\"usage: setup command2\");\n    }\n\n    @Test\n    public void should_print_usage_with_options() throws Exception {\n        helpCommand.execute(new Options().addOption(\"a\", \"anOption\", false, \"the option\"),\n                buildCommandLine(\"help\", \"command2\"));\n\n        assertThat(systemOutRule.getLog()).as(\"contains the usage\").contains(\"usage: setup command2 [-a]\");\n        assertThat(systemOutRule.getLog()).as(\"contains the option\").contains(\"-a,--anOption   the option\");\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-setup/src/test/java/org/bonitasoft/platform/setup/command/InitCommandTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup.command;\n\nimport static org.bonitasoft.platform.setup.command.CommandTestUtils.buildCommandLine;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.Mockito.*;\n\nimport org.apache.commons.cli.Options;\nimport org.bonitasoft.platform.setup.PlatformSetup;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.RestoreSystemProperties;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Baptiste Mesta\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class InitCommandTest {\n\n    @Spy\n    private InitCommand initCommand;\n    @Mock\n    private PlatformSetup platformSetup;\n\n    @Rule\n    public RestoreSystemProperties rule = new RestoreSystemProperties();\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Before\n    public void before() throws Exception {\n        doReturn(platformSetup).when(initCommand).getPlatformSetup(any(String[].class));\n    }\n\n    @Test\n    public void should_execute_init() throws Exception {\n        doNothing().when(initCommand).askConfirmationIfH2();\n\n        initCommand.execute(new Options(), buildCommandLine());\n\n        verify(platformSetup).init();\n    }\n\n    @Test\n    public void should_call_h2_confirmation() throws Exception {\n        doNothing().when(initCommand).askConfirmationIfH2();\n\n        initCommand.execute(new Options(), buildCommandLine());\n\n        verify(initCommand).askConfirmationIfH2();\n    }\n\n    @Test\n    public void execute_should_not_ask_confirmation_if_dbVendor_not_H2() throws Exception {\n        // given:\n        System.setProperty(\"db.vendor\", \"postgres\");\n        System.setProperty(\"bdm.db.vendor\", \"h2\");\n\n        // when:\n        initCommand.execute(new Options(), buildCommandLine());\n\n        // then:\n        verify(initCommand, times(0)).warn(anyString());\n    }\n\n    @Test\n    public void execute_should_not_ask_confirmation_if_bdmDbVendor_not_H2() throws Exception {\n        // given:\n        System.setProperty(\"db.vendor\", \"h2\");\n        System.setProperty(\"bdm.db.vendor\", \"postgres\");\n\n        // when:\n        initCommand.execute(new Options(), buildCommandLine());\n\n        // then:\n        verify(initCommand, times(0)).warn(anyString());\n    }\n\n    @Test\n    public void execute_should_throw_CommandException_if_answer_is_not_YES() throws Exception {\n        // given:\n        System.setProperty(\"db.vendor\", \"h2\");\n        System.setProperty(\"bdm.db.vendor\", \"h2\");\n        doReturn(\"N\").when(initCommand).readAnswer();\n\n        // then:\n        expectedException.expect(CommandException.class);\n        expectedException.expectMessage(\"Exiting\");\n\n        // when:\n        initCommand.execute(new Options(), buildCommandLine());\n    }\n\n    @Test\n    public void execute_should_continue_if_answer_is_YES() throws Exception {\n        // given:\n        System.setProperty(\"db.vendor\", \"h2\");\n        System.setProperty(\"bdm.db.vendor\", \"h2\");\n        doReturn(\"y\").when(initCommand).readAnswer();\n\n        // when:\n        initCommand.execute(new Options(), buildCommandLine());\n\n        // then:\n        verify(platformSetup).init();\n    }\n\n    @Test\n    public void execute_should_continue_if_H2_YES_property_is_defined() throws Exception {\n        // given:\n        System.setProperty(\"db.vendor\", \"h2\");\n        System.setProperty(\"bdm.db.vendor\", \"h2\");\n        System.setProperty(\"h2.noconfirm\", \"\");\n\n        // when:\n        initCommand.execute(new Options(), buildCommandLine());\n\n        // then:\n        verify(platformSetup).init();\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-setup/src/test/java/org/bonitasoft/platform/setup/command/PullCommandTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup.command;\n\nimport static org.bonitasoft.platform.setup.command.CommandTestUtils.buildCommandLine;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.verify;\n\nimport org.apache.commons.cli.Options;\nimport org.bonitasoft.platform.setup.PlatformSetup;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Baptiste Mesta\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class PullCommandTest {\n\n    @Spy\n    private PullCommand pullCommand;\n    @Mock\n    private PlatformSetup platformSetup;\n\n    @Before\n    public void before() throws Exception {\n        doReturn(platformSetup).when(pullCommand).getPlatformSetup(any(String[].class));\n    }\n\n    @Test\n    public void should_execute_pull() throws Exception {\n        pullCommand.execute(new Options(), buildCommandLine());\n\n        verify(platformSetup).pull();\n    }\n}\n"
  },
  {
    "path": "platform/platform-setup/src/test/java/org/bonitasoft/platform/setup/command/PushCommandTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup.command;\n\nimport static org.bonitasoft.platform.setup.command.CommandTestUtils.buildCommandLine;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.verify;\n\nimport org.apache.commons.cli.Options;\nimport org.bonitasoft.platform.setup.PlatformSetup;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Baptiste Mesta\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class PushCommandTest {\n\n    @Spy\n    private PushCommand pushCommand;\n    @Mock\n    private PlatformSetup platformSetup;\n\n    @Before\n    public void before() throws Exception {\n        doReturn(platformSetup).when(pushCommand).getPlatformSetup(any(String[].class));\n    }\n\n    @Test\n    public void should_execute_push() throws Exception {\n        pushCommand.execute(new Options(), buildCommandLine());\n\n        verify(platformSetup).push(false);\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-setup/src/test/java/org/bonitasoft/platform/setup/command/configure/BundleConfiguratorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup.command.configure;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\n\nimport org.apache.commons.io.filefilter.RegexFileFilter;\nimport org.assertj.core.api.SoftAssertions;\nimport org.bonitasoft.platform.exception.PlatformException;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mockito;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class BundleConfiguratorTest {\n\n    private final BundleConfigurator configurator = Mockito.spy(_BundleConfigurator.class);\n\n    private abstract static class _BundleConfigurator extends BundleConfigurator {\n\n        public _BundleConfigurator() throws PlatformException {\n            super(Paths.get(\"\"));\n        }\n    }\n\n    @Test\n    public void escapeWindowsBackslashesIfAny_should_double_backslashes() {\n        // when:\n        final String windowsValue = BundleConfigurator.convertWindowsBackslashes(\"C:\\\\Windows\\\\Path\");\n\n        // then:\n        assertThat(windowsValue).isEqualTo(\"C:/Windows/Path\");\n    }\n\n    @Test\n    public void xml_chars_in_URLs_should_be_escaped_before_replacing() {\n        // given:\n        String url = \"jdbc:mysql://${bdm.db.server.name}:${bdm.db.server.port}/${bdm.db.database.name}\" +\n                \"?dontTrackOpenResources=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&allowPublicKeyRetrieval=true\";\n\n        // when:\n        final String escapedXmlCharacters = BundleConfigurator.escapeXmlCharacters(url);\n\n        // then:\n        assertThat(escapedXmlCharacters)\n                .isEqualTo(\"jdbc:mysql://${bdm.db.server.name}:${bdm.db.server.port}/${bdm.db.database.name}\" +\n                        \"?dontTrackOpenResources=true&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;useSSL=false&amp;allowPublicKeyRetrieval=true\");\n    }\n\n    @Test\n    public void getDriverFilter_should_detect_Oracle_drivers() {\n        // when:\n        final RegexFileFilter driverFilter = configurator.getDriverFilter(\"oracle\");\n\n        // then:\n        assertThat(driverFilter.accept(new File(\"myFavoriteOjdbc1.4drivers.JAR\"))).isTrue();\n        assertThat(driverFilter.accept(new File(\"OraCLE-4.jar\"))).isTrue();\n        assertThat(driverFilter.accept(new File(\"OraCLE.zip\"))).isTrue();\n        assertThat(driverFilter.accept(new File(\"OJdbc-1.4.2.ZIP\"))).isTrue();\n    }\n\n    @Test\n    public void getDriverFilter_should_detect_Postgres_drivers() {\n        // when:\n        final RegexFileFilter driverFilter = configurator.getDriverFilter(\"postgres\");\n\n        // then:\n        assertThat(driverFilter.accept(new File(\"postgres.JAR\"))).isTrue();\n        assertThat(driverFilter.accept(new File(\"POSTGRESsql-5.jar\"))).isTrue();\n        assertThat(driverFilter.accept(new File(\"drivers_postgres.LAST.zip\"))).isTrue();\n    }\n\n    @Test\n    public void getDriverFilter_should_detect_SQLSERVER_drivers() {\n        // when:\n        final RegexFileFilter driverFilter = configurator.getDriverFilter(\"sqlserver\");\n\n        // then:\n        assertThat(driverFilter.accept(new File(\"sqlserver.JAR\"))).isTrue();\n        assertThat(driverFilter.accept(new File(\"SQLSERVER-5.jar\"))).isTrue();\n        assertThat(driverFilter.accept(new File(\"drivers_SQLServer.zip\"))).isTrue();\n        assertThat(driverFilter.accept(new File(\"old-sqljdbc.jar\"))).isTrue();\n        assertThat(driverFilter.accept(new File(\"sqljdbc.jar\"))).isTrue();\n        assertThat(driverFilter.accept(new File(\"sqljdbc4.jar\"))).isTrue();\n        assertThat(driverFilter.accept(new File(\"sqljdbc41.jar\"))).isTrue();\n        assertThat(driverFilter.accept(new File(\"sqljdbc42.jar\"))).isTrue();\n        assertThat(driverFilter.accept(new File(\"mssql-jdbc-6.2.1.jre8.jar\"))).isTrue();\n    }\n\n    @Test\n    public void getDriverFilter_should_detect_MySQL_drivers() {\n        // when:\n        final RegexFileFilter driverFilter = configurator.getDriverFilter(\"mysql\");\n\n        // then:\n        assertThat(driverFilter.accept(new File(\"MySQL.JAR\"))).isTrue();\n        assertThat(driverFilter.accept(new File(\"mySQL-5.jar\"))).isTrue();\n        assertThat(driverFilter.accept(new File(\"drivers_mysql.zIp\"))).isTrue();\n    }\n\n    @Test\n    public void getDriverFilter_should_detect_H2_drivers() {\n        // when:\n        final RegexFileFilter driverFilter = configurator.getDriverFilter(\"h2\");\n\n        // then:\n        assertThat(driverFilter.accept(new File(\"h2-1.4.JAR\"))).isTrue();\n        assertThat(driverFilter.accept(new File(\"drivers-H2.ZIP\"))).isTrue();\n        assertThat(driverFilter.accept(new File(\"my-custom-h2_package.jar\"))).isTrue();\n    }\n\n    @Test\n    public void should_escape_db_url_in_properties_for_H2() throws Exception {\n        // given:\n        DatabaseConfiguration dbConfig = mock(DatabaseConfiguration.class);\n        when(dbConfig.getUrl()).thenReturn(\"/opt/bonità\");\n        when(dbConfig.getDbVendor()).thenReturn(\"h2\");\n\n        // when:\n        String escapedURL = BundleConfigurator.getDatabaseConnectionUrlForPropertiesFile(dbConfig);\n\n        // then:\n        assertThat(escapedURL).isEqualTo(\"/opt/bonit\\\\\\\\u00E0\");\n    }\n\n    // =================================================================================================================\n    // UTILS\n    // =================================================================================================================\n\n    public static void checkFileContains(Path file, String... expectedTexts) throws IOException {\n        final String content = new String(Files.readAllBytes(file), StandardCharsets.UTF_8);\n        assertThat(content).contains(expectedTexts);\n    }\n\n    public static void checkFileDoesNotContain(Path file, String... expectedTexts) throws IOException {\n        final String content = new String(Files.readAllBytes(file), StandardCharsets.UTF_8);\n        SoftAssertions softly = new SoftAssertions();\n        for (String text : expectedTexts) {\n            softly.assertThat(content).doesNotContain(text);\n        }\n        softly.assertAll();\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-setup/src/test/java/org/bonitasoft/platform/setup/command/configure/ConfigureCommandTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup.command.configure;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.verify;\n\nimport org.apache.commons.cli.CommandLine;\nimport org.apache.commons.cli.Options;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Baptiste Mesta\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class ConfigureCommandTest {\n\n    @Spy\n    private ConfigureCommand configureCommand;\n    @Mock\n    private BundleConfigurator bundleConfigurator;\n    @Mock\n    private BundleResolver bundleResolver;\n\n    @Before\n    public void before() throws Exception {\n        doReturn(bundleResolver).when(configureCommand).createBundleResolver();\n        doReturn(bundleConfigurator).when(bundleResolver).getConfigurator();\n    }\n\n    @Test\n    public void execute() throws Exception {\n        configureCommand.execute(any(Options.class), any(CommandLine.class));\n\n        verify(bundleConfigurator).configureApplicationServer();\n    }\n}\n"
  },
  {
    "path": "platform/platform-setup/src/test/java/org/bonitasoft/platform/setup/command/configure/DatabaseConfigurationTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup.command.configure;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\n\nimport java.io.File;\nimport java.nio.file.Path;\nimport java.util.Properties;\n\nimport org.bonitasoft.platform.exception.PlatformException;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.RestoreSystemProperties;\nimport org.junit.rules.TestRule;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class DatabaseConfigurationTest {\n\n    private static final String TEST_DATASOURCE_CONFIG_DIR = \"/datasource-config/\";\n\n    @Rule\n    public TestRule clean = new RestoreSystemProperties();\n\n    @Test\n    public void bonita_database_values_can_be_overridden_by_system_properties() throws Exception {\n        // given:\n        final Properties properties = new PropertyLoader().loadProperties();\n\n        System.setProperty(\"db.vendor\", \"postgres\");\n        System.setProperty(\"db.server.name\", \"postgresServer\");\n        System.setProperty(\"db.server.port\", \"3333\");\n        System.setProperty(\"db.database.name\", \"bonita_database\");\n        System.setProperty(\"db.user\", \"root\");\n        System.setProperty(\"db.password\", \"secret\");\n\n        // when:\n        final DatabaseConfiguration bonitaConfig = new DatabaseConfiguration(\"\", properties, null);\n\n        // then:\n        assertThat(bonitaConfig.getDbVendor()).isEqualTo(\"postgres\");\n        assertThat(bonitaConfig.getServerName()).isEqualTo(\"postgresServer\");\n        assertThat(bonitaConfig.getServerPort()).isEqualTo(\"3333\");\n        assertThat(bonitaConfig.getDatabaseName()).isEqualTo(\"bonita_database\");\n        assertThat(bonitaConfig.getDatabaseUser()).isEqualTo(\"root\");\n        assertThat(bonitaConfig.getDatabasePassword()).isEqualTo(\"secret\");\n    }\n\n    @Test\n    public void bdm_database_values_can_be_overridden_by_system_properties() throws Exception {\n        // given:\n        final Properties properties = new Properties();\n        properties.load(this.getClass().getResourceAsStream(TEST_DATASOURCE_CONFIG_DIR + \"internal.properties\"));\n\n        System.setProperty(\"bdm.db.vendor\", \"postgres\");\n        System.setProperty(\"bdm.db.server.name\", \"myServer\");\n        System.setProperty(\"bdm.db.server.port\", \"1111\");\n        System.setProperty(\"bdm.db.database.name\", \"internal_database\");\n        System.setProperty(\"bdm.db.user\", \"_user_\");\n        System.setProperty(\"bdm.db.password\", \"_pwd_\");\n\n        // when:\n        final DatabaseConfiguration bdmConfig = new DatabaseConfiguration(\"bdm.\", properties, null);\n\n        // then:\n        assertThat(bdmConfig.getDbVendor()).isEqualTo(\"postgres\");\n        assertThat(bdmConfig.getServerName()).isEqualTo(\"myServer\");\n        assertThat(bdmConfig.getServerPort()).isEqualTo(\"1111\");\n        assertThat(bdmConfig.getDatabaseName()).isEqualTo(\"internal_database\");\n        assertThat(bdmConfig.getDatabaseUser()).isEqualTo(\"_user_\");\n        assertThat(bdmConfig.getDatabasePassword()).isEqualTo(\"_pwd_\");\n    }\n\n    @Test\n    public void database_values_should_be_trimmed() throws Exception {\n        // given:\n        final Properties properties = new Properties();\n        properties.load(this.getClass()\n                .getResourceAsStream(TEST_DATASOURCE_CONFIG_DIR + \"database_with_space_values.properties\"));\n        properties.load(this.getClass().getResourceAsStream(TEST_DATASOURCE_CONFIG_DIR + \"internal.properties\"));\n\n        System.setProperty(\"db.server.name\", \"  localhost   \");\n        System.setProperty(\"db.server.port\", \"   5135   \");\n\n        // when:\n        final DatabaseConfiguration dbConfig = new DatabaseConfiguration(\"\", properties, null);\n        final DatabaseConfiguration bdmDbConfig = new DatabaseConfiguration(\"bdm.\", properties, null);\n\n        // then:\n        assertThat(dbConfig.getUrl()).isEqualTo(\"jdbc:postgresql://localhost:5135/bonita\");\n        assertThat(bdmDbConfig.getUrl())\n                .isEqualTo(\"jdbc:postgresql://postgres.rd.lan:5432/business_data\");\n    }\n\n    @Test\n    public void support_absolute_path_in_h2_database_dir() throws Exception {\n        // given:\n        Path rootPath = new File(\".\").toPath();\n        String h2DatabaseDir = \"/h2Database\";\n        Properties properties = new PropertyLoader().loadProperties();\n\n        if (org.apache.commons.lang3.SystemUtils.IS_OS_WINDOWS) {\n            h2DatabaseDir = \"C:/h2Database\";\n        }\n\n        System.setProperty(\"db.vendor\", \"h2\");\n        System.setProperty(\"h2.database.dir\", h2DatabaseDir);\n\n        // when:\n        DatabaseConfiguration bonitaConfig = new DatabaseConfiguration(\"\", properties, rootPath);\n\n        // then:\n        assertThat(bonitaConfig.getUrl())\n                .isEqualTo(\"jdbc:h2:file:\"\n                        + h2DatabaseDir\n                        + \"/bonita_journal.db;DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTO_SERVER=TRUE;\");\n    }\n\n    @Test\n    public void support_properties_in_h2_database_dir() throws Exception {\n        // given:\n        Path rootPath = new File(\".\").toPath();\n        String h2DatabaseDir = \"${org.bonitasoft.h2.database.dir}\";\n        Properties properties = new PropertyLoader().loadProperties();\n\n        System.setProperty(\"db.vendor\", \"h2\");\n        System.setProperty(\"h2.database.dir\", h2DatabaseDir);\n\n        // when:\n        DatabaseConfiguration dbConfig = new DatabaseConfiguration(\"\", properties, rootPath);\n\n        // then:\n        assertThat(dbConfig.getUrl())\n                .isEqualTo(\"jdbc:h2:file:\"\n                        + h2DatabaseDir\n                        + \"/bonita_journal.db;DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTO_SERVER=TRUE;\");\n    }\n\n    @Test\n    public void convert_relative_path_to_absolute_path_in_h2_database_dir() throws Exception {\n        // given:\n        Path rootPath = new File(\".\").toPath();\n        String h2DatabaseDir = \"../h2Database\";\n        Properties properties = new PropertyLoader().loadProperties();\n\n        System.setProperty(\"db.vendor\", \"h2\");\n        System.setProperty(\"h2.database.dir\", h2DatabaseDir);\n\n        // when:\n        DatabaseConfiguration dbConfig = new DatabaseConfiguration(\"\", properties, rootPath);\n\n        // then:\n        assertThat(dbConfig.getUrl())\n                .isEqualTo(\"jdbc:h2:file:\"\n                        + rootPath.resolve(\"setup\").resolve(h2DatabaseDir).toAbsolutePath().normalize().toString()\n                                .replace(\"\\\\\", \"/\")\n                        + \"/bonita_journal.db;DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTO_SERVER=TRUE;\");\n    }\n\n    @Test\n    public void jdbc_pool_size_values_must_be_integers() throws Exception {\n        // given:\n        final Properties properties = new Properties();\n        properties.load(this.getClass().getResourceAsStream(TEST_DATASOURCE_CONFIG_DIR + \"internal.properties\"));\n\n        System.setProperty(\"bdm.db.vendor\", \"postgres\");\n        System.setProperty(\"bdm.db.server.name\", \"myServer\");\n        System.setProperty(\"bdm.db.server.port\", \"1111\");\n        System.setProperty(\"bdm.db.database.name\", \"internal_database\");\n        System.setProperty(\"bdm.db.user\", \"_user_\");\n        System.setProperty(\"bdm.db.password\", \"_pwd_\");\n\n        System.setProperty(\"bdm.connection-pool.maxIdle\", \"ten\");\n\n        // expect:\n        assertThatThrownBy(\n                () -> new DatabaseConfiguration(\"bdm.\", properties, null))\n                .isExactlyInstanceOf(PlatformException.class)\n                .hasMessage(\"Invalid integer value 'ten' for property 'bdm.connection-pool.maxIdle'\");\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-setup/src/test/java/org/bonitasoft/platform/setup/command/configure/PropertyReaderTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup.command.configure;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.Properties;\n\nimport org.bonitasoft.platform.exception.PlatformException;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.RestoreSystemProperties;\nimport org.junit.rules.ExpectedException;\nimport org.junit.rules.TestRule;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class PropertyReaderTest {\n\n    @Rule\n    public TestRule clean = new RestoreSystemProperties();\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void properties_file_values_can_be_overridden_by_system_properties() throws Exception {\n        // given:\n        final Properties properties = new Properties();\n        properties.load(this.getClass().getResourceAsStream(\"/datasource-config/database.properties\"));\n\n        final PropertyReader bdmConfig = new PropertyReader(properties);\n        assertThat(bdmConfig.getPropertyAndFailIfNull(\"bdm.db.vendor\")).isEqualTo(\"oracle\");\n\n        // when:\n        System.setProperty(\"bdm.db.vendor\", \"otherValue\");\n\n        // then:\n        assertThat(bdmConfig.getPropertyAndFailIfNull(\"bdm.db.vendor\")).isEqualTo(\"otherValue\");\n    }\n\n    @Test\n    public void should_fail_if_mandatory_property_is_not_set() throws Exception {\n        // given:\n        final PropertyReader reader = new PropertyReader(new Properties());\n\n        // then:\n        expectedException.expect(PlatformException.class);\n        expectedException.expectMessage(\"Mandatory property\");\n\n        // when:\n        reader.getPropertyAndFailIfNull(\"db.server.name\");\n\n    }\n}\n"
  },
  {
    "path": "platform/platform-setup/src/test/java/org/bonitasoft/platform/setup/command/configure/TomcatBundleConfiguratorTest.java",
    "content": "/**\n * Copyright (C) 2016-2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup.command.configure;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.bonitasoft.platform.setup.PlatformSetup.BONITA_SETUP_FOLDER;\nimport static org.bonitasoft.platform.setup.command.configure.BundleConfiguratorTest.checkFileContains;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.doThrow;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\nimport java.io.File;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\n\nimport org.apache.commons.io.FileUtils;\nimport org.apache.commons.io.file.PathUtils;\nimport org.apache.commons.io.filefilter.RegexFileFilter;\nimport org.bonitasoft.platform.exception.PlatformException;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.RestoreSystemProperties;\nimport org.junit.rules.TemporaryFolder;\nimport org.junit.rules.TestRule;\nimport org.junit.runner.RunWith;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class TomcatBundleConfiguratorTest {\n\n    @Rule\n    public final TestRule restoreSystemProperties = new RestoreSystemProperties();\n\n    @Rule\n    public TemporaryFolder temporaryFolder = new TemporaryFolder();\n\n    private TomcatBundleConfigurator configurator;\n\n    private TomcatBundleConfigurator spy;\n\n    private Path bundleFolder;\n    private Path tomcatFolder;\n    private String databaseAbsolutePath;\n\n    @Before\n    public void setupTempConfFolder() throws Exception {\n        final File temporaryFolderRoot = temporaryFolder.newFolder();\n        bundleFolder = temporaryFolderRoot.toPath().toRealPath();\n        tomcatFolder = bundleFolder.resolve(\"server\");\n        configurator = new TomcatBundleConfigurator(bundleFolder);\n        FileUtils.copyDirectory(Paths.get(\"src/test/resources/tomcat_conf\").toFile(), temporaryFolderRoot);\n        System.setProperty(BONITA_SETUP_FOLDER, bundleFolder.resolve(\"setup\").toString());\n        databaseAbsolutePath = BundleConfigurator\n                .convertWindowsBackslashes(bundleFolder.resolve(\"h2_database\").normalize().toString());\n        spy = spy(configurator);\n    }\n\n    @Test\n    public void should_delete_h2_jar_from_classpath_if_h2_is_not_used() throws Exception {\n        // given:\n        System.setProperty(\"db.vendor\", \"postgres\");\n        System.setProperty(\"db.database.name\", \"bonita\");\n        System.setProperty(\"db.user\", \"bonita\");\n        System.setProperty(\"db.password\", \"bpm\");\n\n        System.setProperty(\"bdm.db.vendor\", \"postgres\");\n        System.setProperty(\"bdm.db.database.name\", \"business_data\");\n        System.setProperty(\"bdm.db.user\", \"bizUser\");\n        System.setProperty(\"bdm.db.password\", \"bizPwd\");\n\n        // given:\n        final Path h2jarPath = tomcatFolder.resolve(\"lib\").resolve(\"bonita\").resolve(\"h2.jar\");\n\n        assertThat(h2jarPath).exists();\n\n        // when:\n        configurator.configureApplicationServer();\n\n        // then:\n        assertThat(h2jarPath).doesNotExist();\n    }\n\n    @Test\n    public void should_not_delete_h2_jar_from_classpath_if_h2_is_used_for_bdm() throws Exception {\n        // given:\n        System.setProperty(\"db.vendor\", \"postgres\");\n        System.setProperty(\"db.database.name\", \"bonita\");\n        System.setProperty(\"db.user\", \"bonita\");\n        System.setProperty(\"db.password\", \"bpm\");\n\n        System.setProperty(\"bdm.db.vendor\", \"h2\");\n        System.setProperty(\"bdm.db.database.name\", \"business_data.db\");\n        System.setProperty(\"bdm.db.user\", \"sa\");\n        System.setProperty(\"bdm.db.password\", \"\");\n\n        // given:\n        final Path h2jarPath = tomcatFolder.resolve(\"lib\").resolve(\"bonita\").resolve(\"h2.jar\");\n\n        assertThat(h2jarPath).exists();\n\n        // when:\n        configurator.configureApplicationServer();\n\n        // then:\n        assertThat(h2jarPath).exists();\n    }\n\n    @Test\n    public void should_not_delete_h2_jar_from_classpath_if_h2_is_used_for_bonita() throws Exception {\n        // given:\n        System.setProperty(\"db.vendor\", \"h2\");\n        System.setProperty(\"db.database.name\", \"internal_database.db\");\n        System.setProperty(\"db.user\", \"myUser\");\n        System.setProperty(\"db.password\", \"myPwd\");\n\n        System.setProperty(\"bdm.db.vendor\", \"postgres\");\n        System.setProperty(\"bdm.db.database.name\", \"business_data\");\n        System.setProperty(\"bdm.db.user\", \"bizUser\");\n        System.setProperty(\"bdm.db.password\", \"bizPwd\");\n\n        // given:\n        final Path h2jarPath = tomcatFolder.resolve(\"lib\").resolve(\"bonita\").resolve(\"h2.jar\");\n\n        assertThat(h2jarPath).exists();\n\n        // when:\n        configurator.configureApplicationServer();\n\n        // then:\n        assertThat(h2jarPath).exists();\n    }\n\n    @Test\n    public void should_not_delete_h2_jar_from_classpath_if_h2_is_used_for_bonita_and_for_BDM() throws Exception {\n        // given:\n        System.setProperty(\"db.vendor\", \"h2\");\n        System.setProperty(\"db.database.name\", \"internal_database.db\");\n        System.setProperty(\"db.user\", \"myUser\");\n        System.setProperty(\"db.password\", \"myPwd\");\n\n        System.setProperty(\"bdm.db.vendor\", \"h2\");\n        System.setProperty(\"bdm.db.database.name\", \"business_data.db\");\n        System.setProperty(\"bdm.db.user\", \"sa\");\n        System.setProperty(\"bdm.db.password\", \"\");\n\n        // given:\n        final Path h2jarPath = tomcatFolder.resolve(\"lib\").resolve(\"bonita\").resolve(\"h2.jar\");\n\n        assertThat(h2jarPath).exists();\n\n        // when:\n        configurator.configureApplicationServer();\n\n        // then:\n        assertThat(h2jarPath).exists();\n    }\n\n    @Test\n    public void should_not_fail_if_bonitaXml_file_does_not_pre_exist() throws Exception {\n        // given:\n        final Path bonitaXmlPath = tomcatFolder.resolve(\"conf\").resolve(\"Catalina\").resolve(\"localhost\")\n                .resolve(\"bonita.xml\");\n        PathUtils.deleteFile(bonitaXmlPath);\n        assertThat(bonitaXmlPath).doesNotExist();\n\n        // when:\n        configurator.configureApplicationServer();\n\n        // then:\n        assertThat(bonitaXmlPath).exists();\n    }\n\n    @Test\n    public void configureApplicationServer_should_update_setEnv_file() throws Exception {\n        // given:\n        System.setProperty(\"db.vendor\", \"postgres\");\n        System.setProperty(\"bdm.db.vendor\", \"postgres\");\n\n        // when:\n        configurator.configureApplicationServer();\n\n        // then:\n        assertThat(numberOfBackups(\"setenv.sh\")).isEqualTo(1);\n        assertThat(numberOfBackups(\"setenv.bat\")).isEqualTo(1);\n        checkFileContains(tomcatFolder.resolve(\"bin\").resolve(\"setenv.sh\"), \"-Dsysprop.bonita.db.vendor=postgres\",\n                \"-Dsysprop.bonita.bdm.db.vendor=postgres\");\n        checkFileContains(tomcatFolder.resolve(\"bin\").resolve(\"setenv.bat\"), \"-Dsysprop.bonita.db.vendor=postgres\",\n                \"-Dsysprop.bonita.bdm.db.vendor=postgres\");\n    }\n\n    private int numberOfBackups(String file) {\n        final String[] backupFiles = bundleFolder.resolve(\"setup\").resolve(\"tomcat-backups\").toFile()\n                .list(new RegexFileFilter(file + \"\\\\.[0-9-_hms]*\"));\n        return backupFiles == null ? 0 : backupFiles.length;\n    }\n\n    @Test\n    public void configureApplicationServer_should_fail_if_no_driver_folder() throws Exception {\n        // given:\n        final Path driverFolder = bundleFolder.resolve(\"setup\").resolve(\"lib\");\n        FileUtils.deleteDirectory(driverFolder.toFile());\n\n        // when - then:\n        assertThatExceptionOfType(PlatformException.class).isThrownBy(configurator::configureApplicationServer)\n                .withMessage(\"Drivers folder not found: \" + driverFolder\n                        + \". Make sure it exists and put a jar or zip file containing drivers there.\");\n    }\n\n    @Test\n    public void configureApplicationServer_should_update_bonitaXml_file() throws Exception {\n        // given:\n        System.setProperty(\"db.vendor\", \"postgres\");\n        System.setProperty(\"db.server.name\", \"db.localhost\");\n        System.setProperty(\"db.server.port\", \"5432\");\n        System.setProperty(\"db.database.name\", \"bonita\");\n        System.setProperty(\"db.user\", \"bonita\");\n        System.setProperty(\"db.password\", \"bpm\");\n\n        System.setProperty(\"bdm.db.vendor\", \"postgres\");\n        System.setProperty(\"bdm.db.server.name\", \"biz.localhost\");\n        System.setProperty(\"bdm.db.server.port\", \"5433\");\n        System.setProperty(\"bdm.db.database.name\", \"business_data\");\n        System.setProperty(\"bdm.db.user\", \"bizUser\");\n        System.setProperty(\"bdm.db.password\", \"bizPwd\");\n\n        System.setProperty(\"connection-pool.maxTotal\", \"200\");\n\n        // when:\n        configurator.configureApplicationServer();\n\n        // then:\n        final Path bonitaXml = tomcatFolder.resolve(\"conf\").resolve(\"Catalina\").resolve(\"localhost\")\n                .resolve(\"bonita.xml\");\n        checkFileContains(bonitaXml,\n                \"validationQuery=\\\"SELECT 1\\\"\", \"username=\\\"bonita\\\"\", \"password=\\\"bpm\\\"\",\n                \"serverName=\\\"db.localhost\\\"\", \"portNumber=\\\"5432\\\"\", \"port=\\\"5432\\\"\", \"databaseName=\\\"bonita\\\"\",\n                \"url=\\\"jdbc:postgresql://db.localhost:5432/bonita\\\"\",\n                \"dataSourceURL=\\\"jdbc:postgresql://db.localhost:5432/bonita\\\"\");\n        checkFileContains(bonitaXml,\n                \"validationQuery=\\\"SELECT 1\\\"\", \"username=\\\"bizUser\\\"\", \"password=\\\"bizPwd\\\"\",\n                \"serverName=\\\"biz.localhost\\\"\", \"portNumber=\\\"5433\\\"\", \"port=\\\"5433\\\"\",\n                \"databaseName=\\\"business_data\\\"\",\n                \"url=\\\"jdbc:postgresql://biz.localhost:5433/business_data\\\"\",\n                \"dataSourceURL=\\\"jdbc:postgresql://biz.localhost:5433/business_data\\\"\");\n        checkFileContains(bonitaXml, \"driverClassName=\\\"org.postgresql.Driver\\\"\",\n                \"type=\\\"org.postgresql.xa.PGXADataSource\\\"\", \"class=\\\"org.postgresql.xa.PGXADataSource\\\"\",\n                \"factory=\\\"org.postgresql.xa.PGXADataSourceFactory\\\"\");\n        checkFileContains(bonitaXml, \"initialSize=\\\"8\\\"\",\n                // maxTotal value overridden in database.properties\n                \"maxTotal=\\\"200\\\"\",\n                \"minIdle=\\\"8\\\"\",\n                \"maxIdle=\\\"16\\\"\");\n\n        assertThat(numberOfBackups(\"bonita.xml\")).isEqualTo(1);\n    }\n\n    @Test\n    public void configureApplicationServer_should_support_H2_replacements_for_Bonita_database_and_BDM()\n            throws Exception {\n        // given:\n        System.setProperty(\"db.vendor\", \"h2\");\n        System.setProperty(\"db.database.name\", \"internal_database.db\");\n        System.setProperty(\"db.user\", \"myUser\");\n        System.setProperty(\"db.password\", \"myPwd\");\n\n        System.setProperty(\"bdm.db.vendor\", \"h2\");\n        System.setProperty(\"bdm.db.database.name\", \"internal_business_data.db\");\n        System.setProperty(\"bdm.db.user\", \"bizUser\");\n        System.setProperty(\"bdm.db.password\", \"bizPwd\");\n\n        // when:\n        configurator.configureApplicationServer();\n\n        // then:\n        final Path bonitaXml = tomcatFolder.resolve(\"conf\").resolve(\"Catalina\").resolve(\"localhost\")\n                .resolve(\"bonita.xml\");\n\n        checkFileContains(bonitaXml, \"validationQuery=\\\"SELECT 1\\\"\", \"username=\\\"myUser\\\"\", \"password=\\\"myPwd\\\"\",\n                \"driverClassName=\\\"org.h2.Driver\\\"\",\n                \"url=\\\"jdbc:h2:file:\" + databaseAbsolutePath\n                        + \"/internal_database.db;DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTO_SERVER=TRUE;\\\"\",\n                \"dataSourceURL=\\\"jdbc:h2:file:\" + databaseAbsolutePath\n                        + \"/internal_database.db;DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTO_SERVER=TRUE;\\\"\");\n        checkFileContains(bonitaXml, \"validationQuery=\\\"SELECT 1\\\"\", \"username=\\\"bizUser\\\"\", \"password=\\\"bizPwd\\\"\",\n                \"driverClassName=\\\"org.h2.Driver\\\"\",\n                \"url=\\\"jdbc:h2:file:\" + databaseAbsolutePath\n                        + \"/internal_business_data.db;DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTO_SERVER=TRUE;\\\"\",\n                \"dataSourceURL=\\\"jdbc:h2:file:\" + databaseAbsolutePath\n                        + \"/internal_business_data.db;DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTO_SERVER=TRUE;\\\"\");\n    }\n\n    @Test\n    public void configureApplicationServer_should_support_special_characters_for_h2_database() throws Exception {\n        // given:\n        System.setProperty(\"db.vendor\", \"h2\");\n        System.setProperty(\"db.database.name\", \"bonita_with$dollarXXX.db\");\n        System.setProperty(\"db.user\", \"_bonita_with$dollar\\\\andBackSlash\");\n        System.setProperty(\"db.password\", \"bpm_With$dollar\\\\andBackSlash\");\n\n        System.setProperty(\"bdm.db.vendor\", \"h2\");\n        System.setProperty(\"bdm.db.database.name\", \"bonita_bdm_with$dollarXXX.db\");\n        System.setProperty(\"bdm.db.user\", \"_bdmWith$dollar\\\\andBackSlash\");\n        System.setProperty(\"bdm.db.password\", \"bdm_bpm_With$dollar\\\\andBackSlash\");\n\n        // when:\n        configurator.configureApplicationServer();\n\n        // then:\n        final Path bonitaXml = tomcatFolder.resolve(\"conf\").resolve(\"Catalina\").resolve(\"localhost\")\n                .resolve(\"bonita.xml\");\n\n        checkFileContains(bonitaXml, \"validationQuery=\\\"SELECT 1\\\"\", \"username=\\\"_bonita_with$dollar\\\\andBackSlash\\\"\",\n                \"password=\\\"bpm_With$dollar\\\\andBackSlash\\\"\", \"driverClassName=\\\"org.h2.Driver\\\"\",\n                \"url=\\\"jdbc:h2:file:\" + databaseAbsolutePath\n                        + \"/bonita_bdm_with$dollarXXX.db;DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTO_SERVER=TRUE;\\\"\",\n                \"dataSourceURL=\\\"jdbc:h2:file:\" + databaseAbsolutePath\n                        + \"/bonita_bdm_with$dollarXXX.db;DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTO_SERVER=TRUE;\\\"\");\n    }\n\n    @Test\n    public void configureApplicationServer_should_support_special_characters_for_database() throws Exception {\n        // given:\n        System.setProperty(\"db.vendor\", \"postgres\");\n        System.setProperty(\"db.server.name\", \"localhost\");\n        System.setProperty(\"db.server.port\", \"5432\");\n        System.setProperty(\"db.database.name\", \"bonita_with$dollarXXX\\\\myInstance.of.bonita&perf=good\");\n        System.setProperty(\"db.user\", \"_bonita_with$dollar\\\\andBackSlash\");\n        System.setProperty(\"db.password\", \"bpm_With$dollar\\\\andBackSlash\");\n\n        System.setProperty(\"bdm.db.vendor\", \"postgres\");\n        System.setProperty(\"bdm.db.server.name\", \"localhost\");\n        System.setProperty(\"bdm.db.server.port\", \"5432\");\n        System.setProperty(\"bdm.db.database.name\",\n                \"bonita_bdm_with$dollarXXX\\\\myInstance.of.bdm&perf=good?host.net.disableOob=true\");\n        System.setProperty(\"bdm.db.user\", \"_bdmWith$dollar\\\\andBackSlash\");\n        System.setProperty(\"bdm.db.password\", \"bdm_bpm_With$dollar\\\\andBackSlash\");\n\n        // when:\n        configurator.configureApplicationServer();\n\n        // then:\n        final Path bonitaXml = tomcatFolder.resolve(\"conf\").resolve(\"Catalina\").resolve(\"localhost\")\n                .resolve(\"bonita.xml\");\n\n        checkFileContains(bonitaXml, \"validationQuery=\\\"SELECT 1\\\"\",\n                \"username=\\\"_bonita_with$dollar\\\\andBackSlash\\\"\", \"password=\\\"bpm_With$dollar\\\\andBackSlash\\\"\",\n                \"driverClassName=\\\"org.postgresql.Driver\\\"\",\n                \"url=\\\"jdbc:postgresql://localhost:5432/bonita_with$dollarXXX\\\\myInstance.of.bonita&amp;perf=good\\\"\",\n                \"dataSourceURL=\\\"jdbc:postgresql://localhost:5432/bonita_with$dollarXXX\\\\myInstance.of.bonita&amp;perf=good\\\"\",\n                \"url=\\\"jdbc:postgresql://localhost:5432/bonita_bdm_with$dollarXXX\\\\myInstance.of.bdm&amp;perf=good?host.net.disableOob=true\\\"\",\n                \"dataSourceURL=\\\"jdbc:postgresql://localhost:5432/bonita_bdm_with$dollarXXX\\\\myInstance.of.bdm&amp;perf=good?host.net.disableOob=true\\\"\");\n    }\n\n    @Test\n    public void should_copy_both_drivers_if_not_the_same_dbVendor_for_bdm() throws Exception {\n        // given:\n        System.setProperty(\"db.vendor\", \"h2\");\n        System.setProperty(\"bdm.db.vendor\", \"postgres\");\n\n        // when:\n        spy.configureApplicationServer();\n\n        // then:\n        verify(spy).copyDriverFile(any(Path.class), any(Path.class), eq(\"h2\"));\n        verify(spy).copyDriverFile(any(Path.class), any(Path.class), eq(\"postgres\"));\n    }\n\n    @Test\n    public void should_not_copy_drivers_again_if_same_dbVendor_for_bdm() throws Exception {\n        // given:\n        System.setProperty(\"db.vendor\", \"postgres\");\n        System.setProperty(\"bdm.db.vendor\", \"postgres\");\n\n        // when:\n        spy.configureApplicationServer();\n\n        // then:\n        verify(spy, times(1)).copyDriverFile(any(Path.class), any(Path.class), anyString());\n    }\n\n    @Test\n    public void should_fail_if_tomcat_mandatory_file_not_present() throws Exception {\n        final Path confFile = tomcatFolder.resolve(\"bin\").resolve(\"setenv.sh\");\n        FileUtils.delete(confFile.toFile());\n\n        // when - then:\n        assertThatExceptionOfType(PlatformException.class).isThrownBy(configurator::configureApplicationServer)\n                .withMessage(\"File setenv.sh is mandatory but is not found\");\n    }\n\n    @Test\n    public void configureApplicationServer_should_fail_if_drivers_not_found() throws Exception {\n        // given:\n        final String dbVendor = \"postgres\";\n        System.setProperty(\"db.vendor\", dbVendor);\n        final Path libFolder = bundleFolder.resolve(\"setup\").resolve(\"lib\");\n        final Path driverJar = libFolder.resolve(\"postgres9.2-drivers.jar\");\n        FileUtils.delete(driverJar.toFile());\n\n        // when - then:\n        assertThatExceptionOfType(PlatformException.class).isThrownBy(configurator::configureApplicationServer)\n                .withMessage(\"No \" + dbVendor + \" drivers found in folder \" + libFolder\n                        + \". Make sure to put a jar or zip file containing drivers there.\");\n    }\n\n    @Test\n    public void exception_in_configure_should_restore_previous_configuration() throws Exception {\n        // given:\n        doThrow(PlatformException.class).when(spy).copyDatabaseDriversIfNecessary(any(Path.class), any(Path.class),\n                eq(\"h2\"));\n\n        // when - then:\n        assertThatExceptionOfType(PlatformException.class).isThrownBy(spy::configureApplicationServer);\n        verify(spy).restorePreviousConfiguration(any(Path.class), any(Path.class), any(Path.class));\n    }\n\n    @Test\n    public void should_not_make_backup_if_content_is_the_same() throws Exception {\n        // given:\n        System.setProperty(\"db.vendor\", \"postgres\");\n        System.setProperty(\"db.database.name\", \"bonita\");\n        System.setProperty(\"db.user\", \"bonita\");\n        System.setProperty(\"db.password\", \"bpm\");\n\n        configurator.configureApplicationServer();\n\n        assertThat(numberOfBackups(\"bonita.xml\")).isEqualTo(1);\n        assertThat(numberOfBackups(\"setenv.bat\")).isEqualTo(1);\n        assertThat(numberOfBackups(\"setenv.sh\")).isEqualTo(1);\n\n        // when:\n        configurator.configureApplicationServer();\n\n        // then:\n        assertThat(numberOfBackups(\"bonita.xml\")).isEqualTo(1);\n        assertThat(numberOfBackups(\"setenv.bat\")).isEqualTo(1);\n        assertThat(numberOfBackups(\"setenv.sh\")).isEqualTo(1);\n    }\n\n    @Test\n    public void should_make_new_backup_if_configuration_changes() throws Exception {\n        // given:\n        System.setProperty(\"db.vendor\", \"postgres\");\n        System.setProperty(\"db.database.name\", \"bonita\");\n        System.setProperty(\"db.user\", \"bonita\");\n        System.setProperty(\"db.password\", \"bpm\");\n\n        configurator.configureApplicationServer();\n\n        assertThat(numberOfBackups(\"bonita.xml\")).isEqualTo(1);\n        assertThat(numberOfBackups(\"setenv.bat\")).isEqualTo(1);\n        assertThat(numberOfBackups(\"setenv.sh\")).isEqualTo(1);\n\n        System.setProperty(\"db.vendor\", \"h2\");\n        System.setProperty(\"db.database.name\", \"bonita_journal.db\");\n        System.setProperty(\"db.user\", \"sa\");\n        System.setProperty(\"db.password\", \"\");\n\n        // so that horodated file has different\n        Thread.sleep(1020);\n\n        // when:\n        new TomcatBundleConfigurator(bundleFolder).configureApplicationServer();\n\n        // then:\n        assertThat(numberOfBackups(\"bonita.xml\")).isEqualTo(2);\n        assertThat(numberOfBackups(\"setenv.bat\")).isEqualTo(2);\n        assertThat(numberOfBackups(\"setenv.sh\")).isEqualTo(2);\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-setup/src/test/java/org/bonitasoft/platform/util/ConfigurationFolderUtilTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.util;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.platform.setup.PlatformSetup.PLATFORM_CONF_FOLDER_NAME;\nimport static org.bonitasoft.platform.util.ConfigurationFolderUtil.ALL_SQL_FILES;\n\nimport java.io.File;\nimport java.nio.file.Path;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.TemporaryFolder;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class ConfigurationFolderUtilTest {\n\n    @Rule\n    public TemporaryFolder temporaryFolder = new TemporaryFolder();\n\n    @Test\n    public void should_copy_all_sql_files() throws Exception {\n        //given\n        ConfigurationFolderUtil configurationFolderUtil = new ConfigurationFolderUtil();\n        Path setupFolder = temporaryFolder.newFolder().toPath();\n\n        //when\n        configurationFolderUtil.buildSqlFolder(setupFolder, \"h2\");\n\n        //then\n        final File expectedFolder = setupFolder.resolve(PLATFORM_CONF_FOLDER_NAME).resolve(\"sql\").resolve(\"h2\")\n                .toFile();\n        assertThat(expectedFolder).exists().isDirectory();\n        assertThat(expectedFolder.listFiles()).extracting(\"name\").hasSize(7).containsOnly(ALL_SQL_FILES);\n    }\n\n    @Test\n    public void should_build_initial_folder() throws Exception {\n        //given\n        ConfigurationFolderUtil configurationFolderUtil = new ConfigurationFolderUtil();\n        Path setupFolder = temporaryFolder.newFolder().toPath();\n\n        //when\n        configurationFolderUtil.buildPlatformEngineFolder(setupFolder);\n\n        //then\n        final File expectedFolder = setupFolder.resolve(PLATFORM_CONF_FOLDER_NAME).resolve(\"initial\")\n                .resolve(\"platform_engine\").toFile();\n        assertThat(expectedFolder).exists().isDirectory();\n        assertThat(expectedFolder.listFiles()).extracting(\"name\").hasSize(1).containsOnly(\"initialConfig.properties\");\n    }\n\n    @Test\n    public void should_build_current_folder() throws Exception {\n        //given\n        ConfigurationFolderUtil configurationFolderUtil = new ConfigurationFolderUtil();\n        Path setupFolder = temporaryFolder.newFolder().toPath();\n\n        //when\n        configurationFolderUtil.buildCurrentFolder(setupFolder);\n\n        //then\n        final File expectedFolder = setupFolder.resolve(PLATFORM_CONF_FOLDER_NAME).resolve(\"current\")\n                .resolve(\"platform_engine\").toFile();\n        assertThat(expectedFolder).exists().isDirectory();\n        assertThat(expectedFolder.listFiles()).extracting(\"name\").hasSize(1).containsOnly(\"currentConfig.properties\");\n    }\n}\n"
  },
  {
    "path": "platform/platform-setup/src/test/java/org/bonitasoft/platform/version/impl/VersionServiceImplIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.version.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport javax.sql.DataSource;\n\nimport org.bonitasoft.platform.setup.PlatformSetupApplication;\nimport org.bonitasoft.platform.setup.ScriptExecutor;\nimport org.bonitasoft.platform.setup.jndi.MemoryJNDISetup;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.context.annotation.PropertySource;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.stereotype.Component;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n/**\n * @author laurent Leseigneur\n */\n@RunWith(SpringRunner.class)\n\n//keep order\n@SpringBootTest(classes = {\n        PlatformSetupApplication.class })\n@ComponentScan(basePackages = { \"org.bonitasoft.platform.setup\", \"org.bonitasoft.platform.configuration\",\n        \"org.bonitasoft.platform.version\" })\n@PropertySource(\"classpath:/application.properties\")\n@Component\npublic class VersionServiceImplIT {\n\n    @Autowired\n    MemoryJNDISetup memoryJNDISetup;\n\n    @Autowired\n    JdbcTemplate jdbcTemplate;\n\n    @Value(\"${db.vendor}\")\n    String dbVendor;\n\n    @Autowired\n    VersionServiceImpl versionService;\n\n    @Autowired\n    DataSource dataSource;\n\n    @Autowired\n    ScriptExecutor scriptExecutor;\n\n    @Before\n    public void setUpDb() throws Exception {\n        scriptExecutor.createAndInitializePlatformIfNecessary();\n    }\n\n    @After\n    public void cleanUpDB() throws Exception {\n        scriptExecutor.deleteTables();\n    }\n\n    @Test\n    public void should_insert_version_in_database() throws Exception {\n        //when\n        final String platformVersion = versionService.retrieveDatabaseSchemaVersion();\n\n        //then\n        assertThat(platformVersion).as(\"should return same version\")\n                .isEqualTo(versionService.getSupportedDatabaseSchemaVersion());\n    }\n\n    @Test\n    public void should_have_same_version() throws Exception {\n        //then\n        assertThat(versionService.isValidPlatformVersion()).as(\"should insert valid version\").isTrue();\n    }\n\n    @Test\n    public void should_not_have_same_version() throws Exception {\n        //given\n        jdbcTemplate.execute(\"UPDATE platform set version='a.b.c' \");\n\n        //then\n        assertThat(versionService.isValidPlatformVersion()).as(\"should insert valid version\").isFalse();\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-setup/src/test/resources/ConfigurationChecker/lib/postgres_drivers_1.2.3.4.jar",
    "content": ""
  },
  {
    "path": "platform/platform-setup/src/test/resources/ConfigurationChecker_KO/lib/otherJar.jar",
    "content": ""
  },
  {
    "path": "platform/platform-setup/src/test/resources/conf/bonita-platform-community.properties",
    "content": "#content not used for testing"
  },
  {
    "path": "platform/platform-setup/src/test/resources/conf/bonita-platform-custom.xml",
    "content": "<beans xmlns=\"http://www.springframework.org/schema/beans\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:p=\"http://www.springframework.org/schema/p\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd\">\n    <!-- content not used for testing -->\n</beans>\n"
  },
  {
    "path": "platform/platform-setup/src/test/resources/database.properties",
    "content": "#####################################################################\n# Nominal database.properties copied from src/main/standalone folder\n#####################################################################\n\n\n####################################################################################\n#\n# Modify the following values to suit your database needs.\n# Fore more information, see file ../HOW_TO_CONFIGURE_AND_RUN.txt\n#\n####################################################################################\n\n\n#########################################\n# Bonita database properties\n#########################################\n\n# Valid values for Community edition: h2, postgres\n# Valid values for Enterprise editions: h2, postgres, sqlserver, oracle, mysql\ndb.vendor=h2\n# when using h2, no server or port setting is needed since connexion is made using file protocol mode using relative directory:\ndb.server.name=SERVER_NAME\ndb.server.port=SERVER_PORT\n# if your database name contains a backslash (\\) character, you must double it (\\\\):\ndb.database.name=bonita_journal.db\ndb.user=sa\n# if your database password contains a backslash (\\) character, you must double it (\\\\):\ndb.password=\n\n###################################\n# Business Data database properties\n###################################\n# Valid values for Community edition: h2, postgres\n# Valid values for Enterprise editions: h2, postgres, sqlserver, oracle, mysql\nbdm.db.vendor=h2\nbdm.db.server.name=SERVER_NAME\nbdm.db.server.port=SERVER_PORT\nbdm.db.database.name=business_data.db\nbdm.db.user=sa\nbdm.db.password=\n\n\n# IMPORTANT NOTE regarding H2 database:\n# in case you move whole setup folder to another directory, you must change property below\n# to point to original folder containing h2 database folder\n# new value can be relative or absolute since it still points to the right folder\n# WARNING for Windows users: keep forward slashes like below (instead of backslashes):\nh2.database.dir=../h2_database\n"
  },
  {
    "path": "platform/platform-setup/src/test/resources/datasource-config/database.properties",
    "content": "#####################################\n# Bonita internal database properties\n#####################################\n\ndb.vendor=postgres\ndb.server.name=localhost\ndb.server.port=5432\ndb.database.name=bonita\ndb.user=bonita\ndb.password=bpm\n\n# Override default maxTotal value \nconnection-pool.maxTotal = 200\n\n#####################################\n# Business Data database properties\n#####################################\nbdm.db.vendor=oracle\nbdm.db.server.name=ora1.rd.lan\nbdm.db.server.port=1521\nbdm.db.database.name=ORCL_with\\\\backslash\nbdm.db.user=bizUser\nbdm.db.password=bizPwd\n\nh2.database.dir=../h2_database\n"
  },
  {
    "path": "platform/platform-setup/src/test/resources/datasource-config/database_with_space_values.properties",
    "content": "#####################################\n# Bonita internal database properties\n#####################################\n\ndb.vendor=postgres\ndb.server.name=localhost\ndb.server.port=5432\ndb.database.name=  bonita  \ndb.user=bonita\ndb.password=bpm\n\n#####################################\n# Business Data database properties\n#####################################\nbdm.db.vendor=postgres\nbdm.db.server.name= postgres.rd.lan \nbdm.db.server.port= 5432 \nbdm.db.database.name= business_data \nbdm.db.user=bonita\nbdm.db.password=bpm\n"
  },
  {
    "path": "platform/platform-setup/src/test/resources/datasource-config/incomplete_database.properties",
    "content": "#####################################\n# Test: db.server.port property is missing\n#####################################\n\ndb.vendor=postgres\ndb.server.name=localhost\n# db.server.port=5432\ndb.database.name=bonita\ndb.user=bonita\ndb.password=bpm\n"
  },
  {
    "path": "platform/platform-setup/src/test/resources/datasource-config/incomplete_internal.properties",
    "content": "postgres.xaDriver=org.postgresql.xa.PGXADataSource\npostgres.xaDSFactory=org.postgresql.xa.PGXADataSourceFactory\n\n# non-XA Driver is missing here !!\n\n###########################\n## Bonita internal database\n###########################\n\n# postgres properties\npostgres.url=jdbc:postgresql://${db.server.name}:${db.server.port}/${db.database.name}\npostgres.testQuery=SELECT 1\n\n# spring properties\nspring.datasource.username=${db.user}\nspring.datasource.password=${db.password}\nspring.datasource.driver-class-name=${${db.vendor}.nonXaDriver}\nspring.datasource.url=${${db.vendor}.url}\n\nconnection-pool.initialSize=8\nconnection-pool.maxTotal=50\nconnection-pool.minIdle=8\nconnection-pool.maxIdle=16\n\n###########################\n# BusinessData database\n###########################\n\n# postgres properties\npostgres.bdm.url=jdbc:postgresql://${bdm.db.server.name}:${bdm.db.server.port}/${bdm.db.database.name}\npostgres.bdm.testQuery=SELECT 1\n\nbdm.connection-pool.initialSize=4\nbdm.connection-pool.maxTotal=20\nbdm.connection-pool.minIdle=4\nbdm.connection-pool.maxIdle=10\n"
  },
  {
    "path": "platform/platform-setup/src/test/resources/datasource-config/internal.properties",
    "content": "# Nominal internal.properties copied from src/main/standalone module\n\nh2.nonXaDriver=org.h2.Driver\nh2.xaDriver=org.h2.jdbcx.JdbcDataSource\nh2.xaDSFactory=org.h2.jdbcx.JdbcDataSourceFactory\n\npostgres.nonXaDriver=org.postgresql.Driver\npostgres.xaDriver=org.postgresql.xa.PGXADataSource\npostgres.xaDSFactory=org.postgresql.xa.PGXADataSourceFactory\n\n###########################\n## Bonita database\n###########################\n\n# h2 properties\nh2.url=jdbc:h2:file:${h2.database.dir}/${db.database.name};DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTO_SERVER=TRUE;\nh2.testQuery=SELECT 1\n\n# postgres properties\npostgres.url=jdbc:postgresql://${db.server.name}:${db.server.port}/${db.database.name}\npostgres.testQuery=SELECT 1\n\n\n# spring properties\nspring.datasource.username=${db.user}\nspring.datasource.password=${db.password}\nspring.datasource.driver-class-name=${${db.vendor}.nonXaDriver}\nspring.datasource.url=${${db.vendor}.url}\n\n# The initial number of connections when the connection pool starts.\nconnection-pool.initialSize=8\n# The maximum number of active connections that can be allocated from this pool at the same time.\nconnection-pool.maxTotal=50\n# The minimum number of active connections that always established after pool created and connection has reached this size.\nconnection-pool.minIdle=8\n# The maximum number of connections that should be kept in the pool at all times.\nconnection-pool.maxIdle=16\n\n###########################\n# Business Data database\n###########################\n\n# h2 properties\nh2.bdm.url=jdbc:h2:file:${h2.database.dir}/${bdm.db.database.name};DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTO_SERVER=TRUE;\nh2.bdm.testQuery=SELECT 1\n\n# postgres properties\npostgres.bdm.url=jdbc:postgresql://${bdm.db.server.name}:${bdm.db.server.port}/${bdm.db.database.name}\npostgres.bdm.testQuery=SELECT 1\n\n# The initial number of connections when the connection pool starts.\nbdm.connection-pool.initialSize=4\n# The maximum number of active connections that can be allocated from this pool at the same time.\nbdm.connection-pool.maxTotal=20\n# The minimum number of active connections that always established after pool created and connection has reached this size.\nbdm.connection-pool.minIdle=4\n# The maximum number of connections that should be kept in the pool at all times.\nbdm.connection-pool.maxIdle=10\n"
  },
  {
    "path": "platform/platform-setup/src/test/resources/datasource-config/missingDriverClass_internal.properties",
    "content": "postgres.xaDriver=org.postgresql.xa.PGXADataSource\npostgres.nonXaDriver=org.404.NonExistent\npostgres.xaDSFactory=org.postgresql.xa.PGXADataSourceFactory\n\nconnection-pool.initialSize=8\nconnection-pool.maxTotal=50\nconnection-pool.minIdle=8\nconnection-pool.maxIdle=16\n\n# non-XA Driver is missing here !!\n\n###########################\n## Bonita internal database\n###########################\n\n# postgres properties\npostgres.url=jdbc:postgresql://${db.server.name}:${db.server.port}/${db.database.name}\npostgres.testQuery=SELECT 1\n\n# spring properties\nspring.datasource.username=${db.user}\nspring.datasource.password=${db.password}\nspring.datasource.driver-class-name=${${db.vendor}.nonXaDriver}\nspring.datasource.url=${${db.vendor}.url}\n\n\n###########################\n# BusinessData database\n###########################\n\n# postgres properties\npostgres.bdm.url=jdbc:postgresql://${bdm.db.server.name}:${bdm.db.server.port}/${bdm.db.database.name}\npostgres.bdm.testQuery=SELECT 1\n\nbdm.connection-pool.initialSize=4\nbdm.connection-pool.maxTotal=20\nbdm.connection-pool.minIdle=4\nbdm.connection-pool.maxIdle=10"
  },
  {
    "path": "platform/platform-setup/src/test/resources/internal.properties",
    "content": "#####################################################################\n# Nominal internal.properties copied from src/main/standalone folder\n#####################################################################\n\n##################################################################################\n#                                                                                #\n#       properties below must NOT be modified unless specific requirements       #\n#                                                                                #\n##################################################################################\n#                       /!\\          WARNING          /!\\                        #\n#       Any value containing a backslash (\\) character MUST be doubled (\\\\)      #\n##################################################################################\n\nh2.nonXaDriver=org.h2.Driver\nh2.xaDriver=org.h2.jdbcx.JdbcDataSource\nh2.xaDSFactory=org.h2.jdbcx.JdbcDataSourceFactory\n\npostgres.nonXaDriver=org.postgresql.Driver\npostgres.xaDriver=org.postgresql.xa.PGXADataSource\npostgres.xaDSFactory=org.postgresql.xa.PGXADataSourceFactory\n\n###########################\n## Bonita database\n###########################\n\n# h2 properties\nh2.url=jdbc:h2:file:${h2.database.dir}/${db.database.name};DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTO_SERVER=TRUE;\nh2.testQuery=SELECT 1\n\n# postgres properties\npostgres.url=jdbc:postgresql://${db.server.name}:${db.server.port}/${db.database.name}\npostgres.testQuery=SELECT 1\n\n\n# spring properties\nspring.datasource.username=${db.user}\nspring.datasource.password=${db.password}\nspring.datasource.driver-class-name=${${db.vendor}.nonXaDriver}\nspring.datasource.url=${${db.vendor}.url}\n\n# The initial number of connections when the connection pool starts.\nconnection-pool.initialSize=8\n# The maximum number of active connections that can be allocated from this pool at the same time.\nconnection-pool.maxTotal=50\n# The minimum number of active connections that always established after pool created and connection has reached this size.\nconnection-pool.minIdle=8\n# The maximum number of connections that should be kept in the pool at all times.\nconnection-pool.maxIdle=16\n\n###########################\n# Business Data database\n###########################\n\n# h2 properties\nh2.bdm.url=jdbc:h2:file:${h2.database.dir}/${bdm.db.database.name};DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTO_SERVER=TRUE;\nh2.bdm.testQuery=SELECT 1\n\n# postgres properties\npostgres.bdm.url=jdbc:postgresql://${bdm.db.server.name}:${bdm.db.server.port}/${bdm.db.database.name}\npostgres.bdm.testQuery=SELECT 1\n\n# The initial number of connections when the connection pool starts.\nbdm.connection-pool.initialSize=4\n# The maximum number of active connections that can be allocated from this pool at the same time.\nbdm.connection-pool.maxTotal=20\n# The minimum number of active connections that always established after pool created and connection has reached this size.\nbdm.connection-pool.minIdle=4\n# The maximum number of connections that should be kept in the pool at all times.\nbdm.connection-pool.maxIdle=10\n"
  },
  {
    "path": "platform/platform-setup/src/test/resources/logback.xml",
    "content": "<configuration debug=\"false\" scan=\"false\">\n    <statusListener class=\"ch.qos.logback.core.status.NopStatusListener\" />\n\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->\n        <encoder>\n            <pattern>%d{HH:mm:ss.SSS}|%-5level|%logger{16}| %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <logger name=\"org.bonitasoft\" level=\"INFO\" />\n    <logger name=\"org.bonitasoft.platform\" level=\"DEBUG\" />\n\n    <!--TRACE level displays all relevant jdbc transaction information-->\n    <!--<logger name=\"org.springframework\" level=\"TRACE\" />-->\n\n    <logger name=\"org.springframework\" level=\"WARN\" />\n    <logger name=\"org.springframework.jdbc\" level=\"INFO\" />\n    <logger name=\"org.springframework.jdbc.support\" level=\"WARN\" />\n\n    <logger name=\"org.springframework.jdbc.datasource.init.ScriptUtils\" level=\"OFF\" />\n\n\n    <root level=\"WARN\">\n        <appender-ref ref=\"STDOUT\" />\n    </root>\n\n</configuration>\n"
  },
  {
    "path": "platform/platform-setup/src/test/resources/tomcat_conf/server/bin/catalina.sh",
    "content": ""
  },
  {
    "path": "platform/platform-setup/src/test/resources/tomcat_conf/server/bin/setenv.bat",
    "content": "rem This is a test file, no need to put the full content here\n\nset DB_OPTS=\"-Dsysprop.bonita.db.vendor=h2\"\nset BDM_DB_OPTS=\"-Dsysprop.bonita.bdm.db.vendor=h2\"\n"
  },
  {
    "path": "platform/platform-setup/src/test/resources/tomcat_conf/server/bin/setenv.sh",
    "content": "# This is a test file, no need to put the full content here\n\nDB_OPTS=\"-Dsysprop.bonita.db.vendor=h2\"\nBDM_DB_OPTS=\"-Dsysprop.bonita.bdm.db.vendor=h2\"\n"
  },
  {
    "path": "platform/platform-setup/src/test/resources/tomcat_conf/server/conf/Catalina/localhost/bonita.xml",
    "content": "<?xml version='1.0' encoding='utf-8'?>\n<!-- #################################################################################################################### -->\n<!-- #   WARNING: Do not edit this file.                                                                                # -->\n<!-- #   Instead, edit the bonita.xml template located in the folder setup/tomcat-templates.                        # -->\n<!-- #   During start-bonita script execution, the template will overwrite this file.                                   # -->\n<!-- #################################################################################################################### -->\n\n<Context>\n\n    <!--  To not persist the session after reboot tomcat -->\n    <Manager pathname=\"\" />\n\n    <!-- ##################################################### -->\n    <!-- Configure Datasource for Bonita standard database -->\n    <!-- ##################################################### -->\n\n    <!-- H2 Example -->\n    <Resource name=\"RawBonitaDS\"\n              auth=\"Container\"\n              type=\"org.h2.jdbcx.JdbcDataSource\"\n              factory=\"org.h2.jdbcx.JdbcDataSourceFactory\"\n              description=\"Raw Bonita Datasource\"\n              user=\"sa\"\n              password=\"\"\n              loginTimeout=\"0\"\n              url=\"jdbc:h2:file:${org.bonitasoft.h2.database.dir}/bonita_journal;DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTO_SERVER=TRUE;\" />\n\n    <Resource\n            name=\"bonitaDS\"\n            uniqueName=\"jdbc/bonitaDSXA\"\n            auth=\"Container\"\n            jmxEnabled=\"true\" jmxName=\"org.bonitasoft:name=bonitaDS\"\n            factory=\"org.jboss.narayana.tomcat.jta.TransactionalDataSourceFactory\"\n            transactionManager=\"TransactionManager\"\n            type=\"javax.sql.XADataSource\"\n            xaDataSource=\"RawBonitaDS\" />\n\n    <Resource name=\"bonitaSequenceManagerDS\"\n              auth=\"Container\"\n              type=\"javax.sql.DataSource\"\n              factory=\"org.apache.tomcat.jdbc.pool.DataSourceFactory\"\n              maxTotal=\"17\"\n              minIdle=\"5\"\n              maxIdle=\"17\"\n              maxWaitMillis=\"10000\"\n              initialSize=\"3\"\n              testOnBorrow=\"true\"\n              validationQuery=\"SELECT 1\"\n              validationInterval=\"30000\"\n              removeAbandonedOnBorrow=\"true\"\n              removeAbandonedOnMaintenance=\"true\"\n              logAbandoned=\"false\"\n              testWithIdle=\"false\"\n              timeBetweenEvictionRunsMillis=\"60000\"\n              minEvictableIdleTimeMillis=\"600000\"\n              username=\"sa\"\n              password=\"\"\n              driverClassName=\"org.h2.Driver\"\n              url=\"jdbc:h2:file:${org.bonitasoft.h2.database.dir}/bonita_journal;DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTO_SERVER=TRUE;\" />\n\n    <!-- Postgresql Example -->\n    <!--\n    <Resource name=\"bonitaSequenceManagerDS\"\n              auth=\"Container\"\n              type=\"javax.sql.DataSource\"\n              factory=\"org.apache.tomcat.jdbc.pool.DataSourceFactory\"\n              maxTotal=\"17\"\n              minIdle=\"5\"\n              maxIdle=\"17\"\n              maxWaitMillis=\"10000\"\n              initialSize=\"3\"\n              testOnBorrow=\"true\"\n              validationQuery=\"SELECT 1\"\n              validationInterval=\"30000\"\n              removeAbandonedOnBorrow=\"true\"\n              removeAbandonedOnMaintenance=\"true\"\n              logAbandoned=\"false\"\n              testWithIdle=\"false\"\n              timeBetweenEvictionRunsMillis=\"60000\"\n              minEvictableIdleTimeMillis=\"600000\"\n              username=\"bonita\"\n              password=\"bpm\"\n              driverClassName=\"org.postgresql.Driver\"\n              url=\"jdbc:postgresql://localhost:5432/bonita\"/>\n    -->\n\n    <!-- Oracle Example -->\n    <!--\n    <Resource name=\"bonitaSequenceManagerDS\"\n              auth=\"Container\"\n              type=\"javax.sql.DataSource\"\n              factory=\"org.apache.tomcat.jdbc.pool.DataSourceFactory\"\n              maxTotal=\"17\"\n              minIdle=\"5\"\n              maxIdle=\"17\"\n              maxWaitMillis=\"10000\"\n              initialSize=\"3\"\n              testOnBorrow=\"true\"\n              validationQuery=\"SELECT 1 FROM dual\"\n              validationInterval=\"30000\"\n              removeAbandonedOnBorrow=\"true\"\n              removeAbandonedOnMaintenance=\"true\"\n              logAbandoned=\"false\"\n              testWithIdle=\"false\"\n              timeBetweenEvictionRunsMillis=\"60000\"\n              minEvictableIdleTimeMillis=\"600000\"\n              username=\"bonita\"\n              password=\"bpm\"\n              driverClassName=\"oracle.jdbc.OracleDriver\"\n              url=\"jdbc:oracle:thin:@localhost:1521:XE?oracle.net.disableOob=true\"/>\n    -->\n\n    <!-- SQL Server Example -->\n    <!-- NOTE: encrypt=false in url attribute is used for development/test environments. -->\n    <!-- For production environments, you should configure proper SSL certificates and use encrypt=true or encrypt=true;trustServerCertificate=false -->\n    <!--\n    <Resource name=\"bonitaSequenceManagerDS\"\n              auth=\"Container\"\n              type=\"javax.sql.DataSource\"\n              factory=\"org.apache.tomcat.jdbc.pool.DataSourceFactory\"\n              maxTotal=\"17\"\n              minIdle=\"5\"\n              maxIdle=\"17\"\n              maxWaitMillis=\"10000\"\n              initialSize=\"3\"\n              testOnBorrow=\"true\"\n              validationQuery=\"SELECT 1\"\n              validationInterval=\"30000\"\n              removeAbandonedOnBorrow=\"true\"\n              removeAbandonedOnMaintenance=\"true\"\n              logAbandoned=\"false\"\n              testWithIdle=\"false\"\n              timeBetweenEvictionRunsMillis=\"60000\"\n              minEvictableIdleTimeMillis=\"600000\"\n              username=\"bonita\"\n              password=\"bpm\"\n              driverClassName=\"com.microsoft.sqlserver.jdbc.SQLServerDriver\"\n              dataSourceURL=\"jdbc:sqlserver://localhost:1433;database=journal\"/>\n    -->\n\n    <!-- MySQL Example -->\n    <!--\n    <Resource name=\"bonitaSequenceManagerDS\"\n              auth=\"Container\"\n              type=\"javax.sql.DataSource\"\n              factory=\"org.apache.tomcat.jdbc.pool.DataSourceFactory\"\n              maxTotal=\"17\"\n              minIdle=\"5\"\n              maxIdle=\"17\"\n              maxWaitMillis=\"10000\"\n              initialSize=\"3\"\n              testOnBorrow=\"true\"\n              validationQuery=\"SELECT 1\"\n              validationInterval=\"30000\"\n              removeAbandonedOnBorrow=\"true\"\n              removeAbandonedOnMaintenance=\"true\"\n              logAbandoned=\"false\"\n              testWithIdle=\"false\"\n              timeBetweenEvictionRunsMillis=\"60000\"\n              minEvictableIdleTimeMillis=\"600000\"\n              username=\"bonita\"\n              password=\"bpm\"\n              driverClassName=\"com.mysql.cj.jdbc.Driver\"\n              url=\"jdbc:mysql://localhost:3306/bonita?dontTrackOpenResources=true&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;useSSL=false&amp;allowPublicKeyRetrieval=true\"/>\n    -->\n\n    <!-- ##################################################### -->\n    <!-- Configure Business Data Datasource -->\n    <!-- ##################################################### -->\n\n    <!-- H2 Example -->\n    <Resource name=\"RawBusinessDataDS\"\n              auth=\"Container\"\n              type=\"org.h2.jdbcx.JdbcDataSource\"\n              factory=\"org.h2.jdbcx.JdbcDataSourceFactory\"\n              description=\"Raw Bonita Datasource\"\n              user=\"sa\"\n              password=\"\"\n              loginTimeout=\"0\"\n              url=\"jdbc:h2:file:${org.bonitasoft.h2.database.dir}/business_data;DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTO_SERVER=TRUE;\" />\n\n    <Resource\n            name=\"BusinessDataDS\"\n            uniqueName=\"jdbc/bonitaDSXA\"\n            auth=\"Container\"\n            jmxEnabled=\"true\" jmxName=\"org.bonitasoft:name=BusinessDataDS\"\n            factory=\"org.jboss.narayana.tomcat.jta.TransactionalDataSourceFactory\"\n            transactionManager=\"TransactionManager\"\n            type=\"javax.sql.XADataSource\"\n            xaDataSource=\"RawBusinessDataDS\" />\n\n    <Resource name=\"NotManagedBizDataDS\"\n              auth=\"Container\"\n              type=\"javax.sql.DataSource\"\n              factory=\"org.apache.tomcat.jdbc.pool.DataSourceFactory\"\n              maxTotal=\"17\"\n              minIdle=\"5\"\n              maxIdle=\"17\"\n              maxWaitMillis=\"10000\"\n              initialSize=\"3\"\n              testOnBorrow=\"true\"\n              validationQuery=\"SELECT 1\"\n              validationInterval=\"30000\"\n              removeAbandonedOnBorrow=\"true\"\n              removeAbandonedOnMaintenance=\"true\"\n              logAbandoned=\"false\"\n              testWithIdle=\"false\"\n              timeBetweenEvictionRunsMillis=\"60000\"\n              minEvictableIdleTimeMillis=\"600000\"\n              username=\"sa\"\n              password=\"\"\n              driverClassName=\"org.h2.Driver\"\n              url=\"jdbc:h2:file:${org.bonitasoft.h2.database.dir}/business_data;DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTO_SERVER=TRUE;\" />\n\n    <!-- Postgresql Example -->\n    <!--\n    <Resource name=\"NotManagedBizDataDS\"\n              auth=\"Container\"\n              type=\"javax.sql.DataSource\"\n              factory=\"org.apache.tomcat.jdbc.pool.DataSourceFactory\"\n              maxTotal=\"17\"\n              minIdle=\"5\"\n              maxIdle=\"17\"\n              maxWaitMillis=\"10000\"\n              initialSize=\"3\"\n              testOnBorrow=\"true\"\n              validationQuery=\"SELECT 1\"\n              validationInterval=\"30000\"\n              removeAbandonedOnBorrow=\"true\"\n              removeAbandonedOnMaintenance=\"true\"\n              logAbandoned=\"false\"\n              testWithIdle=\"false\"\n              timeBetweenEvictionRunsMillis=\"60000\"\n              minEvictableIdleTimeMillis=\"600000\"\n              username=\"bonita\"\n              password=\"bpm\"\n              driverClassName=\"org.postgresql.Driver\"\n              url=\"jdbc:postgresql://localhost:5432/business_data\"/>\n    -->\n\n    <!-- Oracle Example -->\n    <!--\n    <Resource name=\"NotManagedBizDataDS\"\n              auth=\"Container\"\n              type=\"javax.sql.DataSource\"\n              factory=\"org.apache.tomcat.jdbc.pool.DataSourceFactory\"\n              maxTotal=\"17\"\n              minIdle=\"5\"\n              maxIdle=\"17\"\n              maxWaitMillis=\"10000\"\n              initialSize=\"3\"\n              testOnBorrow=\"true\"\n              validationQuery=\"SELECT 1 FROM dual\"\n              validationInterval=\"30000\"\n              removeAbandonedOnBorrow=\"true\"\n              removeAbandonedOnMaintenance=\"true\"\n              logAbandoned=\"false\"\n              testWithIdle=\"false\"\n              timeBetweenEvictionRunsMillis=\"60000\"\n              minEvictableIdleTimeMillis=\"600000\"\n              username=\"business_data\"\n              password=\"bpm\"\n              driverClassName=\"oracle.jdbc.OracleDriver\"\n              url=\"jdbc:oracle:thin:@localhost:1521:XE?oracle.net.disableOob=true\"/>\n    -->\n\n    <!-- SQL Server Example -->\n    <!--\n    <Resource name=\"NotManagedBizDataDS\"\n              auth=\"Container\"\n              type=\"javax.sql.DataSource\"\n              factory=\"org.apache.tomcat.jdbc.pool.DataSourceFactory\"\n              maxTotal=\"17\"\n              minIdle=\"5\"\n              maxIdle=\"17\"\n              maxWaitMillis=\"10000\"\n              initialSize=\"3\"\n              testOnBorrow=\"true\"\n              validationQuery=\"SELECT 1\"\n              validationInterval=\"30000\"\n              removeAbandonedOnBorrow=\"false\"\n              removeAbandonedOnMaintenance=\"false\"\n              logAbandoned=\"false\"\n              removeAbandoned=\"false\"\n              testWithIdle=\"false\"\n              timeBetweenEvictionRunsMillis=\"60000\"\n              minEvictableIdleTimeMillis=\"600000\"\n              username=\"bonita\"\n              password=\"bpm\"\n              driverClassName=\"com.microsoft.sqlserver.jdbc.SQLServerDriver\"\n              dataSourceURL=\"jdbc:sqlserver://localhost:1433;database=business_data\"/>\n    -->\n\n    <!-- MySQL Example -->\n    <!--\n    <Resource name=\"NotManagedBizDataDS\"\n              auth=\"Container\"\n              type=\"javax.sql.DataSource\"\n              factory=\"org.apache.tomcat.jdbc.pool.DataSourceFactory\"\n              maxTotal=\"17\"\n              minIdle=\"5\"\n              maxIdle=\"17\"\n              maxWaitMillis=\"10000\"\n              initialSize=\"3\"\n              testOnBorrow=\"true\"\n              validationQuery=\"SELECT 1\"\n              validationInterval=\"30000\"\n              removeAbandonedOnBorrow=\"false\"\n              removeAbandonedOnMaintenance=\"false\"\n              logAbandoned=\"false\"\n              removeAbandoned=\"false\"\n              testWithIdle=\"false\"\n              timeBetweenEvictionRunsMillis=\"60000\"\n              minEvictableIdleTimeMillis=\"600000\"\n              username=\"bonita\"\n              password=\"bpm\"\n              driverClassName=\"com.mysql.cj.jdbc.Driver\"\n              url=\"jdbc:mysql://localhost:3306/business_data?dontTrackOpenResources=true&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;useSSL=false&amp;allowPublicKeyRetrieval=true\"/>\n    -->\n\n</Context>\n"
  },
  {
    "path": "platform/platform-setup/src/test/resources/tomcat_conf/server/lib/bonita/.gitignore",
    "content": "# Created by .ignore support plugin (hsz.mobi)\n"
  },
  {
    "path": "platform/platform-setup/src/test/resources/tomcat_conf/server/lib/bonita/h2.jar",
    "content": ""
  },
  {
    "path": "platform/platform-setup/src/test/resources/tomcat_conf/setup/lib/drivers-h2-2.12.117.jar",
    "content": ""
  },
  {
    "path": "platform/platform-setup/src/test/resources/tomcat_conf/setup/lib/ojdbc-6.jar",
    "content": ""
  },
  {
    "path": "platform/platform-setup/src/test/resources/tomcat_conf/setup/lib/postgres9.2-drivers.jar",
    "content": ""
  },
  {
    "path": "platform/platform-setup/src/test/resources/tomcat_conf/setup/tomcat-templates/bonita.xml",
    "content": "<?xml version='1.0' encoding='utf-8'?>\n<Context>\n\n    <!--  To not persist the session after reboot tomcat -->\n    <Manager pathname=\"\" />\n\n    <!-- #################################################################################################################### -->\n    <!-- ##############   BETWEEN THESE 2 SECTIONS IS A TEMPLATE MODIFIED BY BONITA SETUP TOOL.              ################ -->\n    <!-- #######   BELOW VALUES SURROUNDED BY '@@' MUST NOT BE TOUCHED, AS THEY ARE USED INTERNALLY BY SETUP TOOL.   ######## -->\n    <!-- #########      FOR OTHER VALUES, DO NOT CHANGE ANYTHING UNLESS YOU ARE SURE OF WHAT YOU ARE DOING.      ############ -->\n    <!-- ##############          YOU CAN ADD CUSTOM CONFIGURATION ABOVE AND BELOW THIS TEMPLATE.            ################# -->\n    <!-- #################################################################################################################### -->\n\n    <!-- ##################################################### -->\n    <!-- Configure Datasource for Bonita standard database -->\n    <!-- ##################################################### -->\n    <Resource name=\"RawBonitaDS\"\n              auth=\"Container\"\n              type=\"@@ds1.xa.driver_class_name@@\"\n              class=\"@@ds1.xa.driver_class_name@@\"\n              factory=\"@@ds1.xa_datasource_factory@@\"\n              description=\"Raw Bonita Datasource\"\n              maxTotal=\"50\"\n              closeMethod=\"close\"\n              loginTimeout=\"0\"\n              serverName=\"@@ds1_database_server_name@@\"\n              portNumber=\"@@ds1_database_port_number@@\"\n              port=\"@@ds1_database_port_number@@\"\n              databaseName=\"@@ds1_database_database_name@@\"\n              user=\"@@ds1.database_connection_user@@\"\n              password=\"@@ds1.database_connection_password@@\"\n              explicitUrl=\"true\"\n              url=\"@@ds1.database_connection_url@@\"\n              dataSourceURL=\"@@ds1.database_connection_url@@\" />\n\n    <Resource name=\"bonitaDS\"\n              uniqueName=\"jdbc/bonitaDSXA\"\n              auth=\"Container\"\n              jmxEnabled=\"true\" jmxName=\"org.bonitasoft:name=bonitaDS\"\n              factory=\"org.jboss.narayana.tomcat.jta.TransactionalDataSourceFactory\"\n              transactionManager=\"TransactionManager\"\n              type=\"javax.sql.XADataSource\"\n              initialSize=\"@@ds1_connection_pool_initialSize@@\"\n              maxTotal=\"@@ds1_connection_pool_maxTotal@@\"\n              minIdle=\"@@ds1_connection_pool_minIdle@@\"\n              maxIdle=\"@@ds1_connection_pool_maxIdle@@\"\n              xaDataSource=\"RawBonitaDS\" />\n\n    <Resource name=\"bonitaSequenceManagerDS\"\n              auth=\"Container\"\n              type=\"javax.sql.DataSource\"\n              factory=\"org.apache.tomcat.jdbc.pool.DataSourceFactory\"\n              maxTotal=\"17\"\n              minIdle=\"5\"\n              maxIdle=\"17\"\n              maxWaitMillis=\"10000\"\n              initialSize=\"3\"\n              validationInterval=\"30000\"\n              removeAbandonedOnBorrow=\"true\"\n              removeAbandonedOnMaintenance=\"true\"\n              logAbandoned=\"false\"\n              testOnBorrow=\"true\"\n              testWithIdle=\"false\"\n              timeBetweenEvictionRunsMillis=\"60000\"\n              minEvictableIdleTimeMillis=\"600000\"\n              validationQuery=\"@@ds1.database_test_query@@\"\n              username=\"@@ds1.database_connection_user@@\"\n              password=\"@@ds1.database_connection_password@@\"\n              driverClassName=\"@@ds1.driver_class_name@@\"\n              url=\"@@ds1.database_connection_url@@\" />\n\n    <!-- ##################################################### -->\n    <!-- Configure Business Data Datasource -->\n    <!-- ##################################################### -->\n    <Resource name=\"RawBusinessDataDS\"\n              auth=\"Container\"\n              type=\"@@ds2.xa.driver_class_name@@\"\n              class=\"@@ds2.xa.driver_class_name@@\"\n              factory=\"@@ds2.xa_datasource_factory@@\"\n              description=\"Raw Bonita Business Data Datasource\"\n              maxTotal=\"5\"\n              closeMethod=\"close\"\n              loginTimeout=\"0\"\n              serverName=\"@@ds2_database_server_name@@\"\n              portNumber=\"@@ds2_database_port_number@@\"\n              port=\"@@ds2_database_port_number@@\"\n              databaseName=\"@@ds2_database_database_name@@\"\n              user=\"@@ds2.database_connection_user@@\"\n              password=\"@@ds2.database_connection_password@@\"\n              explicitUrl=\"true\"\n              url=\"@@ds2.database_connection_url@@\"\n              dataSourceURL=\"@@ds2.database_connection_url@@\" />\n\n    <Resource\n            name=\"BusinessDataDS\"\n            uniqueName=\"jdbc/bonitaDSXA\"\n            auth=\"Container\"\n            jmxEnabled=\"true\" \n            jmxName=\"org.bonitasoft:name=BusinessDataDS\"\n            factory=\"org.jboss.narayana.tomcat.jta.TransactionalDataSourceFactory\"\n            transactionManager=\"TransactionManager\"\n            type=\"javax.sql.XADataSource\"\n            initialSize=\"@@ds2_connection_pool_initialSize@@\"\n            maxTotal=\"@@ds2_connection_pool_maxTotal@@\"\n            minIdle=\"@@ds2_connection_pool_minIdle@@\"\n            maxIdle=\"@@ds2_connection_pool_maxIdle@@\"\n            xaDataSource=\"RawBusinessDataDS\" />\n\n    <Resource name=\"NotManagedBizDataDS\"\n              auth=\"Container\"\n              type=\"javax.sql.DataSource\"\n              factory=\"org.apache.tomcat.jdbc.pool.DataSourceFactory\"\n              maxTotal=\"17\"\n              minIdle=\"5\"\n              maxIdle=\"17\"\n              maxWaitMillis=\"10000\"\n              initialSize=\"3\"\n              validationInterval=\"30000\"\n              removeAbandonedOnBorrow=\"true\"\n              removeAbandonedOnMaintenance=\"true\"\n              logAbandoned=\"false\"\n              testOnBorrow=\"true\"\n              testWithIdle=\"false\"\n              timeBetweenEvictionRunsMillis=\"60000\"\n              minEvictableIdleTimeMillis=\"600000\"\n              validationQuery=\"@@ds2.database_test_query@@\"\n              username=\"@@ds2.database_connection_user@@\"\n              password=\"@@ds2.database_connection_password@@\"\n              driverClassName=\"@@ds2.driver_class_name@@\"\n              url=\"@@ds2.database_connection_url@@\" />\n\n    <!-- #################################################################################################################### -->\n    <!-- ########################         END OF TEMPLATE MODIFIED BY BONITA SETUP TOOL             ######################### -->\n    <!-- ###################            YOU CAN ADD CUSTOM CONFIGURATION *BELOW* THIS TEMPLATE              ################# -->\n    <!-- #################################################################################################################### -->\n\n</Context>\n"
  },
  {
    "path": "platform/platform-setup/src/test/resources/tomcat_conf/setup/tomcat-templates/setenv.bat",
    "content": "rem This is a test file, no need to put the full content here\n\nset DB_OPTS=\"-Dsysprop.bonita.db.vendor=h2\"\nset BDM_DB_OPTS=\"-Dsysprop.bonita.bdm.db.vendor=h2\"\n"
  },
  {
    "path": "platform/platform-setup/src/test/resources/tomcat_conf/setup/tomcat-templates/setenv.sh",
    "content": "# This is a test file, no need to put the full content here\n\nDB_OPTS=\"-Dsysprop.bonita.db.vendor=h2\"\nBDM_DB_OPTS=\"-Dsysprop.bonita.bdm.db.vendor=h2\"\n"
  },
  {
    "path": "platform/platform-setup-test/build.gradle",
    "content": "\n\ndependencies {\n    api libs.commonsLang\n    api libs.commonsText\n    api libs.commonsExec\n}\n\ngroup = 'org.bonitasoft.platform'\n"
  },
  {
    "path": "platform/platform-setup-test/src/main/java/org/bonitasoft/platform/setup/PlatformSetupTestUtils.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.setup;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.file.Paths;\nimport java.sql.Connection;\nimport java.sql.DriverManager;\nimport java.sql.SQLException;\nimport java.util.HashMap;\nimport java.util.Objects;\nimport java.util.Properties;\nimport java.util.regex.Pattern;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipInputStream;\n\nimport org.apache.commons.exec.CommandLine;\nimport org.apache.commons.exec.DefaultExecutor;\nimport org.apache.commons.exec.OS;\nimport org.apache.commons.exec.PumpStreamHandler;\nimport org.apache.commons.text.StringSubstitutor;\n\n/**\n * @author Baptiste Mesta\n */\npublic class PlatformSetupTestUtils {\n\n    private static void writeZipInputToFile(final ZipInputStream zipInputstream, final File outputFile)\n            throws FileNotFoundException, IOException {\n        // The input is a file. An FileOutputStream is created to write the content of the new file.\n        outputFile.getParentFile().mkdirs();\n        try (FileOutputStream fileOutputStream = new FileOutputStream(outputFile)) {\n            // The contents of the new file, that is read from the ZipInputStream using a buffer (byte []), is written.\n            int bytesRead;\n            final byte[] buffer = new byte[1024];\n            while ((bytesRead = zipInputstream.read(buffer)) > -1) {\n                fileOutputStream.write(buffer, 0, bytesRead);\n            }\n            fileOutputStream.flush();\n        } catch (final IOException ioe) {\n            // In case of error, the file is deleted\n            outputFile.delete();\n            throw ioe;\n        }\n    }\n\n    public static PumpStreamHandler getExecuteStreamHandler(String answer) {\n        return new PumpStreamHandler(System.out, System.err, new ByteArrayInputStream(answer.getBytes()));\n    }\n\n    private static void extractZipEntries(final ZipInputStream zipInputstream, final File outputFolder)\n            throws IOException {\n        final String canonicalDestDir = outputFolder.getCanonicalPath() + File.separator;\n        ZipEntry zipEntry;\n        while ((zipEntry = zipInputstream.getNextEntry()) != null) {\n            try {\n                // For each entry, a file is created in the output directory \"folder\"\n                final File outputFile = new File(outputFolder.getAbsolutePath(), zipEntry.getName());\n                // Prevent Zip Slip: ensure the resolved path stays within the output folder\n                if (!outputFile.getCanonicalPath().startsWith(canonicalDestDir)) {\n                    throw new IOException(\"Zip entry is outside of the target directory\");\n                }\n                // If the entry is a directory, it creates in the output folder, and we go to the next entry (continue).\n                if (zipEntry.isDirectory()) {\n                    outputFile.mkdirs();\n                    continue;\n                }\n                writeZipInputToFile(zipInputstream, outputFile);\n            } finally {\n                zipInputstream.closeEntry();\n            }\n        }\n    }\n\n    private static void unzipToFolder(final InputStream inputStream, final File outputFolder) throws IOException {\n        try (ZipInputStream zipInputstream = new ZipInputStream(inputStream)) {\n            extractZipEntries(zipInputstream, outputFolder);\n        }\n    }\n\n    public static void extractDistributionTo(File distFolder) throws IOException {\n        String distZipPath = System.getProperty(\"bonita.distribution.path\");\n        File dist = null;\n        if (distZipPath != null) {\n            dist = new File(distZipPath);\n        } else {\n\n            File target = Paths.get(\"build\").resolve(\"distributions\").toFile();\n            if (!target.isDirectory()) {\n                throw new IllegalStateException(\"No bonita.distribution.path set and no build folder exists\");\n            }\n            Pattern distribPattern = Pattern.compile(\"Bonita-platform-setup-.*\\\\.zip\");\n            for (File file : Objects.requireNonNull(target.listFiles())) {\n                if (distribPattern.matcher(file.getName()).matches()) {\n                    dist = file;\n                    break;\n                }\n            }\n        }\n        if (dist == null) {\n            throw new IllegalStateException(\"Unable to locate the distribution\");\n        }\n        if (!dist.isFile()) {\n            throw new IllegalStateException(\"Distribution is not a file: \" + dist.getPath());\n        }\n\n        try (InputStream inputStream = new FileInputStream(dist)) {\n            unzipToFolder(inputStream, distFolder);\n        }\n    }\n\n    public static Connection getJdbcConnection(File distFolder) throws Exception {\n        return getJdbcConnection(distFolder, null);\n    }\n\n    public static Connection getJdbcConnection(File distFolder, String dbUser) throws IOException, SQLException {\n        Properties properties = getDatabaseProperties(distFolder);\n        properties.put(\"h2.database.dir\",\n                distFolder.toPath().resolve(properties.getProperty(\"h2.database.dir\")).toString());\n        StringSubstitutor stringSubstitutor = new StringSubstitutor(new HashMap(properties));\n        return DriverManager.getConnection(stringSubstitutor.replace(properties.getProperty(\"h2.url\")),\n                dbUser != null ? dbUser : properties.getProperty(\"db.user\"), properties.getProperty(\"db.password\"));\n    }\n\n    private static Properties getDatabaseProperties(File distFolder) throws IOException {\n        Properties properties = new Properties();\n        properties.load(new FileInputStream(distFolder.toPath().resolve(\"database.properties\").toFile()));\n        properties.load(new FileInputStream(distFolder.toPath().resolve(\"internal.properties\").toFile()));\n        return properties;\n    }\n\n    public static DefaultExecutor createExecutor(File distFolder) {\n        DefaultExecutor oDefaultExecutor = new DefaultExecutor();\n        oDefaultExecutor.setWorkingDirectory(distFolder);\n        return oDefaultExecutor;\n    }\n\n    public static CommandLine createCommandLine() {\n        if (OS.isFamilyWindows() || OS.isFamilyWin9x()) {\n            CommandLine oCmdLine = new CommandLine(\"cmd\");\n            oCmdLine.addArgument(\"/c\");\n            oCmdLine.addArgument(\"setup.bat\");\n            return oCmdLine;\n        } else {\n            CommandLine oCmdLine = new CommandLine(\"sh\");\n            oCmdLine.addArgument(\"setup.sh\");\n            return oCmdLine;\n        }\n    }\n\n    public static boolean isCommunityEdition(Class<?> clazz) {\n        try {\n            return clazz.getClassLoader().loadClass(\"com.bonitasoft.platform.setup.PlatformSetupSP\") == null;\n        } catch (ClassNotFoundException e) {\n            return true;\n        }\n    }\n\n}\n"
  },
  {
    "path": "platform/platform-setup-test/src/main/java/org/bonitasoft/platform/util/ConfigurationFolderUtil.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.platform.util;\n\nimport static java.util.Arrays.asList;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class ConfigurationFolderUtil {\n\n    public static final String PLATFORM_CONF_FOLDER_NAME = \"platform_conf\";\n    public static final String[] ALL_SQL_FILES = new String[] { \"cleanTables.sql\",\n            \"createQuartzTables.sql\",\n            \"createTables.sql\",\n            \"dropQuartzTables.sql\",\n            \"dropTables.sql\",\n            \"initTables.sql\",\n            \"preDropStructure.sql\" };\n\n    public Path buildPlatformConfFolder(Path rootFolder) throws IOException {\n        Path initialFolder = rootFolder.resolve(PLATFORM_CONF_FOLDER_NAME);\n        Files.createDirectories(initialFolder);\n        return initialFolder;\n    }\n\n    public void buildPlatformEngineFolder(Path rootFolder) throws IOException {\n        Path platform_engine = rootFolder.resolve(PLATFORM_CONF_FOLDER_NAME).resolve(\"initial\")\n                .resolve(\"platform_engine\");\n        Files.createDirectories(platform_engine);\n        Files.write(platform_engine.resolve(\"initialConfig.properties\"), \"key=value\".getBytes());\n    }\n\n    public Path buildCurrentFolder(Path rootFolder) throws IOException {\n        Path platform_engine = rootFolder.resolve(PLATFORM_CONF_FOLDER_NAME).resolve(\"current\")\n                .resolve(\"platform_engine\");\n        Files.createDirectories(platform_engine);\n        Files.write(platform_engine.resolve(\"currentConfig.properties\"), \"key=value\".getBytes());\n        return platform_engine;\n    }\n\n    public void buildSqlFolder(Path rootFolder, String dbVendor) throws IOException {\n        Path sqlPath = rootFolder.resolve(PLATFORM_CONF_FOLDER_NAME).resolve(\"sql\").resolve(dbVendor);\n        Files.createDirectories(sqlPath);\n        for (String sqlFile : asList(ALL_SQL_FILES)) {\n            Files.copy(ConfigurationFolderUtil.class.getResourceAsStream(\"/sql/\" + dbVendor + \"/\" + sqlFile),\n                    sqlPath.resolve(sqlFile));\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-archive/build.gradle",
    "content": "\n\ndependencies {\n    api project(':services:bonita-commons')\n    api project(':services:bonita-transaction')\n    api project(':services:bonita-persistence')\n    testImplementation libs.mockitoCore\n    testImplementation libs.logback\n}\n"
  },
  {
    "path": "services/bonita-archive/src/main/java/org/bonitasoft/engine/archive/ArchiveInsertRecord.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.archive;\n\nimport org.bonitasoft.engine.persistence.ArchivedPersistentObject;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\n\n/**\n * @author Baptiste Mesta\n */\npublic class ArchiveInsertRecord extends InsertRecord {\n\n    public ArchiveInsertRecord(final ArchivedPersistentObject entity) {\n        super(entity);\n    }\n\n    @Override\n    public ArchivedPersistentObject getEntity() {\n        return (ArchivedPersistentObject) super.getEntity();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-archive/src/main/java/org/bonitasoft/engine/archive/ArchiveService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.archive;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\n\n/**\n * @author Feng Hui\n * @author Matthieu Chaffotte\n * @since 6.0\n */\npublic interface ArchiveService {\n\n    /**\n     * Archive the given entity in sliding archive if relevant and in the appropriate definitive archive\n     *\n     * @param time\n     *        The archive date\n     * @param record\n     *        Archive insert record containing the entity to be archived\n     * @throws SRecorderException\n     */\n    void recordInsert(long time, ArchiveInsertRecord record) throws SRecorderException;\n\n    /**\n     * Archive the given entities in the definitive archive\n     *\n     * @param time\n     *        the time of archiving\n     * @param records\n     *        Archive inserts record containing the entity to be archived\n     * @throws SRecorderException\n     *         in case of a write error\n     */\n    void recordInserts(long time, ArchiveInsertRecord... records) throws SRecorderException;\n\n    /**\n     * Remove the given entity from both sliding archive (if present) and the right archive level (if present)\n     * This operation should normally to be used. This is for admin purpose only\n     *\n     * @param record\n     *        The delete record containing archived entity to be deleted\n     * @throws SRecorderException\n     */\n    void recordDelete(DeleteRecord record) throws SRecorderException;\n\n    /**\n     * Get the ReadPersistenceService corresponding to the definitive archive\n     *\n     * @return the ReadPersistenceService corresponding to the definitive archive\n     */\n    ReadPersistenceService getDefinitiveArchiveReadPersistenceService();\n\n    /**\n     * @param sourceObjectClass\n     *        Persistent object to be judged achievable or not\n     * @return Return true if the objects of the given class can be archived.\n     */\n    boolean isArchivable(Class<? extends PersistentObject> sourceObjectClass);\n\n    int deleteFromQuery(String queryName, Map<String, Object> parameters) throws SRecorderException;\n}\n"
  },
  {
    "path": "services/bonita-archive/src/main/java/org/bonitasoft/engine/archive/ArchivingStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.archive;\n\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface ArchivingStrategy {\n\n    boolean isArchivable(Class<? extends PersistentObject> srcClass);\n\n}\n"
  },
  {
    "path": "services/bonita-archive/src/main/java/org/bonitasoft/engine/archive/impl/AbstractArchivingStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.archive.impl;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.archive.ArchivingStrategy;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Celine Souchet\n */\npublic abstract class AbstractArchivingStrategy implements ArchivingStrategy {\n\n    final Map<String, Boolean> archives;\n\n    AbstractArchivingStrategy() {\n        archives = new HashMap<>();\n    }\n\n    public AbstractArchivingStrategy(final Map<String, Boolean> archives) {\n        this.archives = archives;\n    }\n\n    @Override\n    public boolean isArchivable(final Class<? extends PersistentObject> srcClass) {\n        final Boolean isArchivable = archives.get(srcClass.getName());\n        if (isArchivable == null) {\n            throw new SBonitaRuntimeException(\"The class '\" + srcClass.getName() + \"' is not known as archivable\");\n        }\n        return isArchivable;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-archive/src/main/java/org/bonitasoft/engine/archive/impl/ArchiveServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.archive.impl;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.archive.ArchiveInsertRecord;\nimport org.bonitasoft.engine.archive.ArchiveService;\nimport org.bonitasoft.engine.archive.ArchivingStrategy;\nimport org.bonitasoft.engine.commons.ClassReflector;\nimport org.bonitasoft.engine.commons.LogUtil;\nimport org.bonitasoft.engine.persistence.ArchivedPersistentObject;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.services.PersistenceService;\nimport org.bonitasoft.engine.services.SPersistenceException;\nimport org.bonitasoft.engine.transaction.STransactionNotFoundException;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Matthieu Chaffotte\n * @author Hongwen Zang\n * @author Celine Souchet\n */\n\npublic class ArchiveServiceImpl implements ArchiveService {\n\n    private static final Logger log = LoggerFactory.getLogger(ArchiveServiceImpl.class);\n    private final UserTransactionService transactionService;\n\n    private final PersistenceService definitiveArchivePersistenceService;\n\n    private ArchivingStrategy archivingStrategy;\n\n    public ArchiveServiceImpl(final PersistenceService definitiveArchivePersistenceService,\n            final ArchivingStrategy archivingStrategy, final UserTransactionService transactionService) {\n        super();\n        this.definitiveArchivePersistenceService = definitiveArchivePersistenceService;\n        this.archivingStrategy = archivingStrategy;\n        this.transactionService = transactionService;\n    }\n\n    @Override\n    public void recordInsert(final long time, final ArchiveInsertRecord record) throws SRecorderException {\n        if (isArchivable(record.getEntity().getPersistentObjectInterface())) {\n            recordInserts(time, record);\n        }\n    }\n\n    @Override\n    public void recordInserts(final long time, final ArchiveInsertRecord... records) throws SRecorderException {\n        final String methodName = \"recordInserts\";\n        logBeforeMethod(methodName);\n        if (records != null) {\n            assignArchiveDate(time, records);\n            final BatchArchiveCallable callable = buildBatchArchiveCallable(records);\n\n            try {\n                transactionService.registerBeforeCommitCallable(callable);\n            } catch (final STransactionNotFoundException e) {\n                if (log.isTraceEnabled()) {\n                    log.error(\n                            \"Unable to register the beforeCommitCallable to log queriable logs: transaction not found\",\n                            e);\n                }\n            }\n        }\n\n        logAfterMethod(methodName);\n    }\n\n    // As a protected method for test purposes.\n    protected BatchArchiveCallable buildBatchArchiveCallable(final ArchiveInsertRecord... records)\n            throws SRecorderException {\n        return new BatchArchiveCallable(definitiveArchivePersistenceService, records);\n    }\n\n    private void assignArchiveDate(final long time, final ArchiveInsertRecord... records) throws SRecorderException {\n        for (final ArchiveInsertRecord record : records) {\n            if (record != null) {\n                setArchiveDate(record.getEntity(), time);\n            }\n        }\n    }\n\n    private void setArchiveDate(final ArchivedPersistentObject entity, final long time) throws SRecorderException {\n        if (entity.getArchiveDate() <= 0) {\n            try {\n                ClassReflector.invokeSetter(entity, \"setArchiveDate\", long.class, time);\n            } catch (final Exception e) {\n                throw new SRecorderException(e);\n            }\n        }\n    }\n\n    @Override\n    public void recordDelete(final DeleteRecord record) throws SRecorderException {\n        String methodName = \"recordDelete\";\n        try {\n            logBeforeMethod(methodName);\n            definitiveArchivePersistenceService.delete(record.getEntity());\n            logAfterMethod(methodName);\n        } catch (final SPersistenceException e) {\n            logOnExceptionMethod(methodName, e);\n            throw new SRecorderException(e);\n        }\n    }\n\n    private void logOnExceptionMethod(final String methodName,\n            final Exception e) {\n        if (log.isTraceEnabled()) {\n            log.trace(LogUtil.getLogOnExceptionMethod(this.getClass(), methodName, e));\n        }\n    }\n\n    private void logAfterMethod(final String methodName) {\n        if (log.isTraceEnabled()) {\n            log.trace(LogUtil.getLogAfterMethod(this.getClass(), methodName));\n        }\n    }\n\n    private void logBeforeMethod(final String methodName) {\n        if (log.isTraceEnabled()) {\n            log.trace(LogUtil.getLogBeforeMethod(this.getClass(), methodName));\n        }\n    }\n\n    @Override\n    public boolean isArchivable(final Class<? extends PersistentObject> sourceObjectClass) {\n        return archivingStrategy.isArchivable(sourceObjectClass);\n    }\n\n    @Override\n    public ReadPersistenceService getDefinitiveArchiveReadPersistenceService() {\n        return definitiveArchivePersistenceService;\n    }\n\n    @Override\n    public int deleteFromQuery(String queryName, Map<String, Object> parameters) throws SRecorderException {\n        try {\n            return definitiveArchivePersistenceService.update(queryName, parameters);\n        } catch (SPersistenceException e) {\n            throw new SRecorderException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-archive/src/main/java/org/bonitasoft/engine/archive/impl/BatchArchiveCallable.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.archive.impl;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.Callable;\n\nimport org.bonitasoft.engine.archive.ArchiveInsertRecord;\nimport org.bonitasoft.engine.persistence.ArchivedPersistentObject;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.services.PersistenceService;\nimport org.bonitasoft.engine.services.SPersistenceException;\n\npublic class BatchArchiveCallable implements Callable<Void> {\n\n    private final PersistenceService persistenceService;\n\n    private final List<? extends ArchivedPersistentObject> archivedObjects;\n\n    public BatchArchiveCallable(final PersistenceService persistenceService, final ArchiveInsertRecord... records) {\n        this.persistenceService = persistenceService;\n        if (records == null) {\n            archivedObjects = new ArrayList<>();\n        } else {\n            archivedObjects = createArchivedObjectsList(records);\n        }\n    }\n\n    /**\n     * @param records\n     * @return\n     */\n    protected List<ArchivedPersistentObject> createArchivedObjectsList(final ArchiveInsertRecord... records) {\n        final List<ArchivedPersistentObject> archivedObjects = new ArrayList<>();\n        for (final ArchiveInsertRecord record : records) {\n            if (record != null) {\n                archivedObjects.add(record.getEntity());\n            }\n        }\n        return archivedObjects;\n    }\n\n    @Override\n    public Void call() throws SPersistenceException {\n        if (hasObjects()) {\n            try {\n                if (archivedObjects.size() == 1) {\n                    persistenceService.insert(archivedObjects.get(0));\n                } else {\n                    persistenceService.insertInBatch(new ArrayList<PersistentObject>(archivedObjects));\n                }\n            } finally {\n                // Do we still need to clear the list even if there was some Exceptions ?\n                // What happens with the retry ?\n                archivedObjects.clear();\n            }\n        }\n        return null;\n    }\n\n    public boolean hasObjects() {\n        return archivedObjects != null && !archivedObjects.isEmpty();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-archive/src/main/java/org/bonitasoft/engine/archive/impl/DefaultArchivingStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.archive.impl;\n\nimport java.util.Map;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class DefaultArchivingStrategy extends AbstractArchivingStrategy {\n\n    public DefaultArchivingStrategy(Map<String, Boolean> additionalConfiguration) {\n        super();\n        archives.put(\"org.bonitasoft.engine.core.process.comment.model.SComment\", true);\n        archives.put(\"org.bonitasoft.engine.core.document.model.SDocumentMapping\", true);\n        archives.put(\"org.bonitasoft.engine.core.process.instance.model.SProcessInstance\", true);\n        archives.put(\"org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance\", true);\n        archives.put(\"org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance\", true);\n        archives.put(\"org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance\", true);\n        archives.put(\"org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance\", true);\n        archives.put(\"org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance\", true);\n        archives.put(\"org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance\", true);\n        archives.put(\"org.bonitasoft.engine.core.process.instance.model.SGatewayInstance\", true);\n        archives.put(\"org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance\", true);\n        archives.put(\"org.bonitasoft.engine.core.process.instance.model.SConnectorInstance\", true);\n        archives.put(\"org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance\", true);\n        archives.put(\"org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance\", true);\n        archives.put(\"org.bonitasoft.engine.data.instance.model.SDataInstance\", true);\n        archives.put(\"org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance\", true);\n        archives.put(\"org.bonitasoft.engine.core.contract.data.SContractData\", true);\n        archives.put(\"org.bonitasoft.engine.core.process.instance.model.SBPMFailure\", true);\n        for (Map.Entry<String, Boolean> entry : additionalConfiguration.entrySet()) {\n            if (!archives.containsKey(entry.getKey())) {\n                archives.put(entry.getKey(), entry.getValue());\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-archive/src/test/java/org/bonitasoft/engine/archive/impl/ArchiveServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.archive.impl;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.*;\n\nimport org.bonitasoft.engine.archive.ArchiveInsertRecord;\nimport org.bonitasoft.engine.persistence.ArchivedPersistentObject;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.junit.Test;\n\npublic class ArchiveServiceImplTest {\n\n    @Test\n    public void should_recordInserts_register_beforeCommitCallable_v2() throws Exception {\n        final UserTransactionService transactionService = mock(UserTransactionService.class);\n\n        ArchiveServiceImpl archiveService = spy(new ArchiveServiceImpl(null, null, transactionService));\n\n        final ArchivedPersistentObjectWithSetter mockArchivedPersistentObject = mock(\n                ArchivedPersistentObjectWithSetter.class);\n        ArchiveInsertRecord record = new ArchiveInsertRecord(mockArchivedPersistentObject);\n\n        BatchArchiveCallable mockBatchArchiveCallable = mock(BatchArchiveCallable.class);\n        when(archiveService.buildBatchArchiveCallable(any(ArchiveInsertRecord.class)))\n                .thenReturn(mockBatchArchiveCallable);\n\n        long archiveDate = 3L;\n        archiveService.recordInserts(archiveDate, record);\n\n        verify(mockArchivedPersistentObject).setArchiveDate(eq(archiveDate));\n        verify(transactionService).registerBeforeCommitCallable(eq(mockBatchArchiveCallable));\n    }\n\n    // Test with exception on TxService\n\n    // Seen with Nicolas C. for this \"interface extension\" :)\n    // Needed as the implementation calls setArchiveDate through reflection.\n    interface ArchivedPersistentObjectWithSetter extends ArchivedPersistentObject {\n\n        void setArchiveDate(long archiveDate);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-archive/src/test/java/org/bonitasoft/engine/archive/impl/BatchArchiveCallableTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.archive.impl;\n\nimport static org.hamcrest.CoreMatchers.is;\nimport static org.junit.Assert.*;\nimport static org.mockito.ArgumentMatchers.anyList;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.archive.ArchiveInsertRecord;\nimport org.bonitasoft.engine.persistence.ArchivedPersistentObject;\nimport org.bonitasoft.engine.services.PersistenceService;\nimport org.bonitasoft.engine.services.SPersistenceException;\nimport org.junit.Test;\n\npublic class BatchArchiveCallableTest {\n\n    @Test\n    public void testCreateArchivedObjectsList() {\n        final ArchiveInsertRecord record1 = new ArchiveInsertRecord(mock(ArchivedPersistentObject.class));\n        final ArchiveInsertRecord record2 = new ArchiveInsertRecord(mock(ArchivedPersistentObject.class));\n        final ArchiveInsertRecord record3 = new ArchiveInsertRecord(mock(ArchivedPersistentObject.class));\n        final ArchiveInsertRecord[] records = new ArchiveInsertRecord[] { record1, record2, record3 };\n        final BatchArchiveCallable callable = new BatchArchiveCallable(null, records);\n\n        final List<ArchivedPersistentObject> createArchivedObjectsList = callable.createArchivedObjectsList(records);\n\n        assertThat(createArchivedObjectsList.size(), is(records.length));\n        for (int i = 0; i < records.length; i++) {\n            final ArchivedPersistentObject archivedPersistentObject = createArchivedObjectsList.get(i);\n            assertThat(archivedPersistentObject, is(records[i].getEntity()));\n        }\n\n    }\n\n    @Test\n    public void testCreateArchivedObjectsListWithNoRecords() {\n        final ArchiveInsertRecord[] records = new ArchiveInsertRecord[] {};\n        final BatchArchiveCallable callable = new BatchArchiveCallable(null, records);\n\n        final List<ArchivedPersistentObject> createArchivedObjectsList = callable.createArchivedObjectsList(records);\n\n        assertTrue(createArchivedObjectsList.isEmpty());\n    }\n\n    @Test\n    public void testCreateArchivedObjectsListWithNullRecords() {\n        final ArchiveInsertRecord record1 = new ArchiveInsertRecord(mock(ArchivedPersistentObject.class));\n        final ArchiveInsertRecord record2 = null;\n        final ArchiveInsertRecord record3 = new ArchiveInsertRecord(mock(ArchivedPersistentObject.class));\n        final ArchiveInsertRecord[] records = new ArchiveInsertRecord[] { record1, record2, record3 };\n        final BatchArchiveCallable callable = new BatchArchiveCallable(null, records);\n\n        final List<ArchivedPersistentObject> createArchivedObjectsList = callable.createArchivedObjectsList(records);\n\n        // The second one that is null was skipped.\n        assertThat(createArchivedObjectsList.size(), is(2));\n\n        ArchivedPersistentObject archivedPersistentObject;\n        // Check the first one\n        archivedPersistentObject = createArchivedObjectsList.get(0);\n        assertThat(archivedPersistentObject, is(records[0].getEntity()));\n\n        // Check the second that is the third one.\n        archivedPersistentObject = createArchivedObjectsList.get(1);\n        assertThat(archivedPersistentObject, is(records[2].getEntity()));\n    }\n\n    @Test\n    public void testHasObjectsReturnsTrueWhenNotEmpty() {\n        final ArchiveInsertRecord record1 = new ArchiveInsertRecord(mock(ArchivedPersistentObject.class));\n        final ArchiveInsertRecord record2 = new ArchiveInsertRecord(mock(ArchivedPersistentObject.class));\n        final ArchiveInsertRecord record3 = new ArchiveInsertRecord(mock(ArchivedPersistentObject.class));\n        final ArchiveInsertRecord[] records = new ArchiveInsertRecord[] { record1, record2, record3 };\n        final BatchArchiveCallable callable = new BatchArchiveCallable(null, records);\n\n        assertTrue(callable.hasObjects());\n    }\n\n    @Test\n    public void testHasObjectsReturnsFalseWhenEmpty() {\n        final BatchArchiveCallable callable = new BatchArchiveCallable(null, new ArchiveInsertRecord[] {});\n\n        assertFalse(callable.hasObjects());\n    }\n\n    @Test\n    public void testHasObjectsReturnsFalseWhenNull() {\n        final BatchArchiveCallable callable = new BatchArchiveCallable(null, (ArchiveInsertRecord) null);\n\n        assertFalse(callable.hasObjects());\n    }\n\n    @Test\n    public void testCallWithOneRecord() throws SPersistenceException {\n        final ArchivedPersistentObject entity = mock(ArchivedPersistentObject.class);\n        final ArchiveInsertRecord record1 = new ArchiveInsertRecord(entity);\n        final ArchiveInsertRecord[] records = new ArchiveInsertRecord[] { record1 };\n        final PersistenceService persistenceService = mock(PersistenceService.class);\n        final BatchArchiveCallable callable = new BatchArchiveCallable(persistenceService, records);\n\n        callable.call();\n\n        verify(persistenceService).insert(eq(entity));\n    }\n\n    @Test\n    public void testCallWithMoreThanOneRecord() throws SPersistenceException {\n        final ArchiveInsertRecord record1 = new ArchiveInsertRecord(mock(ArchivedPersistentObject.class));\n        final ArchiveInsertRecord record2 = new ArchiveInsertRecord(mock(ArchivedPersistentObject.class));\n        final ArchiveInsertRecord record3 = new ArchiveInsertRecord(mock(ArchivedPersistentObject.class));\n        final ArchiveInsertRecord[] records = new ArchiveInsertRecord[] { record1, record2, record3 };\n        final PersistenceService persistenceService = mock(PersistenceService.class);\n        final BatchArchiveCallable callable = new BatchArchiveCallable(persistenceService, records);\n\n        callable.call();\n\n        verify(persistenceService).insertInBatch(anyList());\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-authentication/build.gradle",
    "content": "\n\ndependencies {\n    api project(':services:bonita-identity')\n    api project(':services:bonita-commons')\n    testImplementation libs.mockitoCore\n}\n"
  },
  {
    "path": "services/bonita-authentication/src/main/java/org/bonitasoft/engine/authentication/AuthenticationConstants.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.authentication;\n\n/**\n * @author Julien Reboul\n * @author Celine Souchet\n */\npublic class AuthenticationConstants {\n\n    public static final String BASIC_USERNAME = \"authentication.username\";\n\n    public static final String BASIC_PASSWORD = \"authentication.password\";\n\n    public static final String SAML_CREDENTIALS_PASSPHRASE = \"authentication.passphrase\";\n\n    public static final String BASIC_TENANT_ID = \"authentication.tenant.id\";\n\n    public static final String CAS_TICKET = \"ticket\";\n\n    public static final String CAS_SERVICE = \"service\";\n\n    private AuthenticationConstants() {\n        // For Sonar\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-authentication/src/main/java/org/bonitasoft/engine/authentication/AuthenticationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.authentication;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic class AuthenticationException extends SBonitaException {\n\n    private static final long serialVersionUID = 7204454677855421061L;\n\n    public AuthenticationException(String message) {\n        super(message);\n    }\n\n    public AuthenticationException() {\n        super(\"The user name or password is not valid.\");\n    }\n\n    public AuthenticationException(final Throwable t) {\n        super(t);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-authentication/src/main/java/org/bonitasoft/engine/authentication/GenericAuthenticationService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.authentication;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\n/**\n * @author Julien Reboul\n * @since 6.3\n */\npublic interface GenericAuthenticationService {\n\n    /**\n     * Check user credentials by give user name and passwordHash\n     *\n     * @param credentials\n     *        the credentials elements to use to authenticate\n     * @return the username of the user authenticated.\n     * @throws AuthenticationException\n     *         Error thrown if either the password is invalid or the user is not found.\n     */\n    String checkUserCredentials(Map<String, Serializable> credentials) throws AuthenticationException;\n}\n"
  },
  {
    "path": "services/bonita-authentication/src/main/java/org/bonitasoft/engine/authentication/GenericAuthenticationServiceAccessor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.authentication;\n\n/**\n * @author Charles Souillard\n * @since 7.0\n */\npublic class GenericAuthenticationServiceAccessor {\n\n    private final GenericAuthenticationService genericAuthenticationService;\n\n    public GenericAuthenticationServiceAccessor(GenericAuthenticationService genericAuthenticationService) {\n        this.genericAuthenticationService = genericAuthenticationService;\n    }\n\n    public GenericAuthenticationService getAuthenticationService() {\n        return this.genericAuthenticationService;\n    }\n}\n"
  },
  {
    "path": "services/bonita-authentication/src/main/java/org/bonitasoft/engine/authentication/impl/AuthenticationServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.authentication.impl;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.authentication.AuthenticationConstants;\nimport org.bonitasoft.engine.authentication.GenericAuthenticationService;\nimport org.bonitasoft.engine.commons.LogUtil;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.SUserNotFoundException;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @author Hongwen Zang\n * @author Julien Reboul\n * @author Celine Souchet\n */\n@Component(\"authenticationService\")\n@Lazy\npublic class AuthenticationServiceImpl implements GenericAuthenticationService {\n\n    private Logger logger = LoggerFactory.getLogger(AuthenticationServiceImpl.class);\n    private final IdentityService identityService;\n\n    public AuthenticationServiceImpl(final IdentityService identityService) {\n        this.identityService = identityService;\n    }\n\n    /**\n     * @see org.bonitasoft.engine.authentication.GenericAuthenticationService#checkUserCredentials(java.util.Map)\n     */\n    @Override\n    public String checkUserCredentials(Map<String, Serializable> credentials) {\n        final String methodName = \"checkUserCredentials\";\n        try {\n            final String password = String.valueOf(credentials.get(AuthenticationConstants.BASIC_PASSWORD));\n            final String userName = String.valueOf(credentials.get(AuthenticationConstants.BASIC_USERNAME));\n            if (logger.isTraceEnabled()) {\n                logger.trace(\n                        LogUtil.getLogBeforeMethod(this.getClass(), methodName));\n            }\n            final SUser user = identityService.getUserByUserName(userName);\n            if (identityService.checkCredentials(user, password)) {\n                if (logger.isTraceEnabled()) {\n                    logger.trace(\n                            LogUtil.getLogAfterMethod(this.getClass(), methodName));\n                }\n                return userName;\n            }\n            if (logger.isTraceEnabled()) {\n                logger.trace(\n                        LogUtil.getLogAfterMethod(this.getClass(), methodName));\n            }\n        } catch (final SUserNotFoundException sunfe) {\n            if (logger.isTraceEnabled()) {\n                logger.trace(\n                        LogUtil.getLogOnExceptionMethod(this.getClass(), methodName, sunfe));\n            }\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-authorization/build.gradle",
    "content": "dependencies {\n    api project(':services:bonita-cache')\n    api project(':services:bonita-business-application')\n    api project(':services:bonita-persistence')\n    api project(':services:bonita-session')\n    api project(':services:bonita-commons')\n    api project(':bpm:bonita-core:bonita-home-server')\n    api project(':bpm:bonita-common')\n\n    testImplementation libs.assertj\n    testImplementation libs.mockitoCore\n    testImplementation libs.logback\n\n    annotationProcessor libs.lombok\n    compileOnly libs.lombok\n}"
  },
  {
    "path": "services/bonita-authorization/src/main/java/org/bonitasoft/engine/authorization/PermissionService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.authorization;\n\nimport java.util.Properties;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.api.permission.APICallContext;\nimport org.bonitasoft.engine.commons.TenantLifecycleService;\nimport org.bonitasoft.engine.commons.exceptions.SExecutionException;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface PermissionService extends TenantLifecycleService {\n\n    public String USER_TYPE_AUTHORIZATION_PREFIX = \"user\";\n    public String PROFILE_TYPE_AUTHORIZATION_PREFIX = \"profile\";\n    public String SCRIPT_TYPE_AUTHORIZATION_PREFIX = \"check\";\n\n    boolean isAuthorized(APICallContext apiCallContext) throws SExecutionException;\n\n    void addPermissions(String pageName, Properties pageProperties);\n\n    void removePermissions(Properties pageProperties);\n\n    Set<String> getResourcePermissions(String resourceKey);\n}\n"
  },
  {
    "path": "services/bonita-authorization/src/main/java/org/bonitasoft/engine/authorization/PermissionsBuilder.java",
    "content": "/**\n * Copyright (C) 2021 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.authorization;\n\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.authorization.properties.CompoundPermissionsMapping;\nimport org.bonitasoft.engine.authorization.properties.CustomPermissionsMapping;\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class PermissionsBuilder {\n\n    public static final String PROFILE_TYPE_AUTHORIZATION_PREFIX = \"profile\";\n\n    public static final String USER_TYPE_AUTHORIZATION_PREFIX = \"user\";\n\n    private final CustomPermissionsMapping customPermissionsMapping;\n    private final CompoundPermissionsMapping compoundPermissionsMapping;\n    private final ApplicationService applicationService;\n\n    PermissionsBuilder(final ApplicationService applicationService,\n            final CustomPermissionsMapping customPermissionsMapping,\n            final CompoundPermissionsMapping compoundPermissionsMapping) {\n        this.applicationService = applicationService;\n        this.customPermissionsMapping = customPermissionsMapping;\n        this.compoundPermissionsMapping = compoundPermissionsMapping;\n    }\n\n    public Set<String> getPermissions(boolean isTechnicalUser, List<String> profiles, String userName)\n            throws SBonitaReadException {\n        Set<String> permissions;\n        if (isTechnicalUser) {\n            permissions = Collections.emptySet();\n        } else {\n            permissions = new HashSet<>();\n            permissions.addAll(getProfilesPermissions(profiles));\n            permissions.addAll(getCustomUserPermissions(userName));\n            permissions.add(getUserPermission(userName));\n        }\n        return permissions;\n    }\n\n    protected Set<String> getProfilesPermissions(List<String> profiles) throws SBonitaReadException {\n        Set<String> permissions = new HashSet<>();\n        for (final String pageToken : getAllPagesForUser(profiles)) {\n            permissions.addAll(getCompoundPermissions(pageToken));\n        }\n        for (String profile : profiles) {\n            permissions.addAll(getCustomProfilePermissions(profile));\n            permissions.add(getProfilePermission(profile));\n        }\n        return permissions;\n    }\n\n    private Set<String> getAllPagesForUser(List<String> profiles) throws SBonitaReadException {\n        final Set<String> pageTokens = new HashSet<>();\n        for (final String profile : profiles) {\n            pageTokens.addAll(getPageTokensForApplicationsMappedToProfile(profile));\n        }\n        return pageTokens;\n    }\n\n    private String getProfilePermission(final String profile) {\n        return PROFILE_TYPE_AUTHORIZATION_PREFIX + \"|\" + profile;\n    }\n\n    private String getUserPermission(final String username) {\n        return USER_TYPE_AUTHORIZATION_PREFIX + \"|\" + username;\n    }\n\n    private List<String> getPageTokensForApplicationsMappedToProfile(final String profile) throws SBonitaReadException {\n        return applicationService.getAllPagesForProfile(profile);\n    }\n\n    private Set<String> getCustomProfilePermissions(final String profile) {\n        return getCustomPermissions(PROFILE_TYPE_AUTHORIZATION_PREFIX, profile);\n    }\n\n    protected Set<String> getCustomUserPermissions(String userName) {\n        return getCustomPermissions(USER_TYPE_AUTHORIZATION_PREFIX, userName);\n    }\n\n    protected Set<String> getCustomPermissions(final String type, final String identifier) {\n        final Set<String> profileSinglePermissions = new HashSet<>();\n        final Set<String> customPermissionsForEntity = getCustomPermissionsRaw(type, identifier);\n        for (final String customPermissionForEntity : customPermissionsForEntity) {\n            final Set<String> simplePermissions = getCompoundPermissions(customPermissionForEntity);\n            if (!simplePermissions.isEmpty()) {\n                profileSinglePermissions.addAll(simplePermissions);\n            } else {\n                profileSinglePermissions.add(customPermissionForEntity);\n            }\n        }\n        return profileSinglePermissions;\n    }\n\n    private Set<String> getCustomPermissionsRaw(final String type, final String identifier) {\n        return customPermissionsMapping.getPropertyAsSet(type + \"|\" + identifier);\n    }\n\n    private Set<String> getCompoundPermissions(final String compoundName) {\n        return compoundPermissionsMapping.getPropertyAsSet(compoundName);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-authorization/src/main/java/org/bonitasoft/engine/authorization/properties/CompoundPermissionsMapping.java",
    "content": "/**\n * Copyright (C) 2021 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.authorization.properties;\n\nimport org.bonitasoft.engine.cache.CacheService;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Anthony Birembaut\n */\n@Component\n@Order(3)\npublic class CompoundPermissionsMapping extends ConfigurationFile {\n\n    public static final String PROPERTIES_FILENAME = \"compound-permissions-mapping.properties\";\n\n    public CompoundPermissionsMapping(CacheService cacheService, ConfigurationFilesManager configurationFilesManager) {\n        super(cacheService, configurationFilesManager);\n    }\n\n    @Override\n    protected String getPropertiesFileName() {\n        return PROPERTIES_FILENAME;\n    }\n\n    @Override\n    protected boolean hasCustomVersion() {\n        return true;\n    }\n\n    @Override\n    protected boolean hasInternalVersion() {\n        return true;\n    }\n}\n"
  },
  {
    "path": "services/bonita-authorization/src/main/java/org/bonitasoft/engine/authorization/properties/ConfigurationFile.java",
    "content": "/**\n * Copyright (C) 2021 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.authorization.properties;\n\nimport static java.lang.String.format;\nimport static org.bonitasoft.engine.authorization.properties.ConfigurationFilesManager.getCustomPropertiesFilename;\nimport static org.bonitasoft.engine.authorization.properties.ConfigurationFilesManager.getInternalPropertiesFilename;\n\nimport java.io.IOException;\nimport java.util.Properties;\nimport java.util.Set;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.cache.CacheService;\nimport org.bonitasoft.engine.cache.SCacheException;\n\n/**\n * @author Anthony Birembaut\n */\n@Slf4j\npublic abstract class ConfigurationFile {\n\n    protected static final String CONFIGURATION_FILES_CACHE = \"CONFIGURATION_FILES_CACHE\";\n\n    private final String propertiesFilename;\n\n    protected final String cacheKey;\n\n    protected boolean setKeysToLowerCase;\n\n    CacheService cacheService;\n\n    ConfigurationFilesManager configurationFilesManager;\n\n    public ConfigurationFile(CacheService cacheService, ConfigurationFilesManager configurationFilesManager) {\n        this(cacheService, configurationFilesManager, false);\n    }\n\n    public ConfigurationFile(CacheService cacheService,\n            ConfigurationFilesManager configurationFilesManager, boolean setKeysToLowerCase) {\n        this.propertiesFilename = getPropertiesFileName();\n        this.cacheKey = propertiesFilename;\n        this.cacheService = cacheService;\n        this.configurationFilesManager = configurationFilesManager;\n        this.setKeysToLowerCase = setKeysToLowerCase;\n    }\n\n    protected Properties readPropertiesAndStoreThemInCache() {\n        return readPropertiesFromDatabaseAndStoreThemInCache();\n    }\n\n    protected Properties readPropertiesFromDatabaseAndStoreThemInCache() {\n        Properties properties = configurationFilesManager.getTenantProperties(propertiesFilename,\n                setKeysToLowerCase);\n        storePropertiesInCache(properties);\n        return properties;\n    }\n\n    protected Properties readPropertiesFromClasspathAndStoreThemInCache() {\n        //Read properties from classpath\n        Properties classpathProperties = new Properties();\n        try {\n            classpathProperties.load(ConfigurationFile.class.getResourceAsStream(getPropertiesFileName()));\n            storePropertiesInCache(classpathProperties);\n        } catch (IOException e) {\n            log.error(\"Cannot retrieve dynamic authorizations\", e);\n        }\n        return classpathProperties;\n    }\n\n    protected abstract String getPropertiesFileName();\n\n    abstract protected boolean hasCustomVersion();\n\n    abstract protected boolean hasInternalVersion();\n\n    void storePropertiesInCache(Properties properties) {\n        try {\n            cacheService.store(CONFIGURATION_FILES_CACHE, cacheKey, properties);\n            log.debug(\"Successfully stored configuration file {} in dedicated cache\", propertiesFilename);\n        } catch (SCacheException e) {\n            log.warn(\"Problem storing configuration file {} in dedicated cache ({})\", propertiesFilename,\n                    e.getMessage());\n        }\n    }\n\n    Properties getProperties() {\n        Properties properties;\n        try {\n            properties = (Properties) cacheService.get(CONFIGURATION_FILES_CACHE, cacheKey);\n        } catch (SCacheException e) {\n            log.warn(\"Problem retrieving configuration file {} from dedicated cache\", propertiesFilename);\n            return new Properties(); // Should we return null?\n        }\n        if (properties == null) {\n            properties = readPropertiesAndStoreThemInCache();\n        }\n        return properties;\n    }\n\n    public String getProperty(final String propertyName) {\n        final String propertyValue = getProperties().getProperty(propertyName);\n        return propertyValue != null ? propertyValue.trim() : null;\n    }\n\n    public Set<String> getPropertyAsSet(final String propertyName) {\n        return PropertiesWithSet.stringToSet(getProperty(propertyName));\n    }\n\n    public void removeProperty(final String propertyName) {\n        try {\n            if (hasCustomVersion() || hasInternalVersion()) {\n                throw new IllegalArgumentException(\n                        format(\"File %s cannot be modified directly, as a writable version exists\", propertyName));\n            }\n            final Properties properties = getProperties();\n            if (properties.remove(propertyName) != null) { // if the property was present\n                storePropertiesInCache(properties);\n                configurationFilesManager.removeProperty(propertiesFilename, propertyName);\n            }\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public void removeCustomProperty(final String propertyName) {\n        try {\n            if (!hasCustomVersion()) {\n                throw new IllegalArgumentException(format(\"File %s does not have a -custom version\", propertyName));\n            }\n            final Properties properties = getProperties();\n            // FIXME: is there a risk to remove a property that is not custom, here?\n            if (properties.remove(propertyName) != null) { // if the property was present\n                storePropertiesInCache(properties);\n                configurationFilesManager.removeProperty(getCustomPropertiesFilename(propertiesFilename),\n                        propertyName);\n            }\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public void removeInternalProperty(final String propertyName) {\n        try {\n            if (!hasInternalVersion()) {\n                throw new IllegalArgumentException(format(\"File %s does not have a -internal version\", propertyName));\n            }\n            final Properties properties = getProperties();\n            // FIXME: is there a risk to remove a property that is not internal, here?\n            if (properties.remove(propertyName) != null) { // if the property was present\n                storePropertiesInCache(properties);\n                configurationFilesManager.removeProperty(getInternalPropertiesFilename(propertiesFilename),\n                        propertyName);\n            }\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public void setProperty(final String propertyName, final String propertyValue) {\n        try {\n            if (hasCustomVersion() || hasInternalVersion()) {\n                throw new IllegalArgumentException(\n                        format(\"File %s cannot be modified directly, as a writable version exists\", propertyName));\n            }\n            final Properties properties = getProperties();\n            properties.setProperty(propertyName, propertyValue);\n            storePropertiesInCache(properties);\n            configurationFilesManager.setProperty(propertiesFilename, propertyName,\n                    propertyValue);\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public void setCustomProperty(final String propertyName, final String propertyValue) {\n        try {\n            if (!hasCustomVersion()) {\n                throw new IllegalArgumentException(format(\"File %s does not have a -custom version\", propertyName));\n            }\n            final Properties properties = getProperties();\n            properties.setProperty(propertyName, propertyValue);\n            storePropertiesInCache(properties);\n            configurationFilesManager.setProperty(getCustomPropertiesFilename(propertiesFilename),\n                    propertyName, propertyValue);\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public void setInternalProperty(final String propertyName, final String propertyValue) {\n        try {\n            if (!hasInternalVersion()) {\n                throw new IllegalArgumentException(format(\"File %s does not have a -internal version\", propertyName));\n            }\n            final Properties properties = getProperties();\n            properties.setProperty(propertyName, propertyValue);\n            storePropertiesInCache(properties);\n            configurationFilesManager.setProperty(getInternalPropertiesFilename(propertiesFilename),\n                    propertyName, propertyValue);\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public void setPropertyAsSet(final String property, final Set<String> permissions) {\n        setProperty(property, permissions.toString());\n    }\n\n    public void setCustomPropertyAsSet(final String property, final Set<String> permissions) {\n        setCustomProperty(property, permissions.toString());\n    }\n\n    public void setInternalPropertyAsSet(final String property, final Set<String> permissions) {\n        setInternalProperty(property, permissions.toString());\n    }\n}\n"
  },
  {
    "path": "services/bonita-authorization/src/main/java/org/bonitasoft/engine/authorization/properties/ConfigurationFilesManager.java",
    "content": "/**\n * Copyright (C) 2021 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.authorization.properties;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Properties;\nimport java.util.stream.Collectors;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.exception.UpdateException;\nimport org.bonitasoft.engine.home.BonitaHomeServer;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Baptiste Mesta\n * @author Emmanuel Duchastenier\n * @author Anthony Birembaut\n */\n@Slf4j\n@Component\npublic class ConfigurationFilesManager {\n\n    protected Properties getAlsoCustomAndInternalPropertiesFromFilename(String propertiesFileName,\n            boolean setKeysToLowerCase) {\n        Properties properties = new Properties();\n        try {\n            final Map<String, Properties> propertiesByFilename = getTenantConfigurations();\n            if (propertiesByFilename.containsKey(propertiesFileName)) {\n                properties.putAll(propertiesKeysToLowerCaseIfNeeded(propertiesByFilename.get(propertiesFileName),\n                        setKeysToLowerCase));\n                // if -internal properties also exists, merge key/value pairs:\n                final String internalSuffixedVersion = getInternalPropertiesFilename(propertiesFileName);\n                if (propertiesByFilename.containsKey(internalSuffixedVersion)) {\n                    properties.putAll(propertiesKeysToLowerCaseIfNeeded(\n                            propertiesByFilename.get(internalSuffixedVersion), setKeysToLowerCase));\n                }\n                // if -custom properties also exists, merge key/value pairs (and overwrite previous values if same key name):\n                final String customSuffixedVersion = getCustomPropertiesFilename(propertiesFileName);\n                if (propertiesByFilename.containsKey(customSuffixedVersion)) {\n                    properties.putAll(propertiesKeysToLowerCaseIfNeeded(propertiesByFilename.get(customSuffixedVersion),\n                            setKeysToLowerCase));\n                }\n            } else {\n                if (log.isTraceEnabled()) {\n                    log.trace(\"File {} not found. Returning empty properties object.\", propertiesFileName);\n                }\n            }\n        } catch (IOException e) {\n            log.error(\"Cannot retrieve tenant configurations\", e);\n        }\n        return properties;\n    }\n\n    protected Properties propertiesKeysToLowerCaseIfNeeded(Properties properties, boolean setKeysToLowerCase) {\n        if (setKeysToLowerCase) {\n            Properties reworkedProperties = new Properties();\n            properties.forEach((k, v) -> {\n                reworkedProperties.put(k.toString().toLowerCase(), v);\n            });\n            return reworkedProperties;\n        } else {\n            return properties;\n        }\n    }\n\n    public Properties getTenantProperties(String propertiesFileName) {\n        return getTenantProperties(propertiesFileName, false);\n    }\n\n    public Properties getTenantProperties(String propertiesFileName, boolean setKeysToLowerCase) {\n        return getAlsoCustomAndInternalPropertiesFromFilename(propertiesFileName, setKeysToLowerCase);\n    }\n\n    /**\n     * Parses the content as a Properties object.\n     * If content is null, return empty properties.\n     */\n    public static Properties getProperties(byte[] content) {\n        Properties properties = new Properties();\n        if (content != null) {\n            try (ByteArrayInputStream inputStream = new ByteArrayInputStream(content)) {\n                properties.load(inputStream);\n            } catch (IOException ioe) {\n                log.error(\"Cannot parse properties file content\", ioe);\n            }\n        }\n        return properties;\n    }\n\n    public void removeProperty(String propertiesFilename, String propertyName) throws IOException {\n        Map<String, Properties> resources = getTenantConfigurations();\n        Properties properties = resources.get(propertiesFilename);\n        if (properties != null) {\n            properties.remove(propertyName);\n            update(propertiesFilename, properties);\n        } else {\n            if (log.isDebugEnabled()) {\n                log.debug(\"File {} not found. Cannot remove property '{}'.\", propertiesFilename, propertyName);\n            }\n        }\n    }\n\n    public static String getInternalPropertiesFilename(String propertiesFilename) {\n        // Internal behavior stores and removes from -internal file (for automatic updates when deploying/updating a page/API extension)\n        return propertiesFilename.replaceAll(\"\\\\.properties$\", \"-internal\" + \".properties\");\n    }\n\n    public static String getCustomPropertiesFilename(String propertiesFilename) {\n        // Custom behavior stores and removes from -custom files (for manual updates):\n        return propertiesFilename.replaceAll(\"\\\\.properties$\", \"-custom\" + \".properties\");\n    }\n\n    protected void update(String propertiesFilename, Properties properties) throws IOException {\n        try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {\n            properties.store(byteArrayOutputStream, \"\");\n            getConfigurationFilesUtils().updateTenantPortalConfigurationFile(propertiesFilename,\n                    byteArrayOutputStream.toByteArray());\n        } catch (UpdateException e) {\n            throw new IOException(e);\n        }\n    }\n\n    protected BonitaHomeServer getConfigurationFilesUtils() {\n        return BonitaHomeServer.getInstance();\n    }\n\n    protected Properties getTenantPortalConfiguration(String propertiesFilename) {\n        return ConfigurationFilesManager\n                .getProperties(getConfigurationFilesUtils().getTenantPortalConfiguration(propertiesFilename));\n    }\n\n    protected Map<String, Properties> getTenantConfigurations()\n            throws IOException {\n        Map<String, byte[]> clientTenantConfigurations = getConfigurationFilesUtils()\n                .getTenantPortalConfigurations();\n        return clientTenantConfigurations.entrySet().stream().collect(Collectors.toMap(\n                Entry::getKey, v -> ConfigurationFilesManager.getProperties(v.getValue())));\n    }\n\n    public void setProperty(String propertiesFilename, String propertyName, String propertyValue)\n            throws IOException {\n        Properties properties = getTenantPortalConfiguration(propertiesFilename);\n        if (properties != null) {\n            properties.setProperty(propertyName, propertyValue);\n            update(propertiesFilename, properties); // store them back in database\n        } else {\n            if (log.isDebugEnabled()) {\n                log.debug(\"File {} not found. Cannot set property '{}'.\", propertiesFilename, propertyName);\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-authorization/src/main/java/org/bonitasoft/engine/authorization/properties/CustomPermissionsMapping.java",
    "content": "/**\n * Copyright (C) 2021 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.authorization.properties;\n\nimport org.bonitasoft.engine.cache.CacheService;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Anthony Birembaut\n */\n@Component\n@Order(3)\npublic class CustomPermissionsMapping extends ConfigurationFile {\n\n    /**\n     * Default name of the preferences file\n     */\n    public static final String PROPERTIES_FILENAME = \"custom-permissions-mapping.properties\";\n\n    @Override\n    protected String getPropertiesFileName() {\n        return PROPERTIES_FILENAME;\n    }\n\n    public CustomPermissionsMapping(CacheService cacheService, ConfigurationFilesManager configurationFilesManager) {\n        super(cacheService, configurationFilesManager);\n    }\n\n    @Override\n    protected boolean hasCustomVersion() {\n        return false;\n    }\n\n    @Override\n    protected boolean hasInternalVersion() {\n        return false;\n    }\n}\n"
  },
  {
    "path": "services/bonita-authorization/src/main/java/org/bonitasoft/engine/authorization/properties/DynamicPermissionsChecks.java",
    "content": "/**\n * Copyright (C) 2021 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.authorization.properties;\n\nimport java.util.Properties;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.cache.CacheService;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Anthony Birembaut\n */\n@Component\n@Order(3)\n@Slf4j\n@ConditionalOnSingleCandidate(DynamicPermissionsChecks.class)\npublic class DynamicPermissionsChecks extends ResourcesPermissionsMapping {\n\n    /**\n     * Default name of the preferences file\n     */\n    public static final String PROPERTIES_FILENAME = \"dynamic-permissions-checks.properties\";\n\n    public DynamicPermissionsChecks(CacheService cacheService, ConfigurationFilesManager configurationFilesManager) {\n        super(cacheService, configurationFilesManager);\n    }\n\n    @Override\n    protected String getPropertiesFileName() {\n        return PROPERTIES_FILENAME;\n    }\n\n    @Override\n    protected boolean hasInternalVersion() {\n        return false;\n    }\n\n    @Override\n    protected Properties readPropertiesAndStoreThemInCache() {\n        return readPropertiesFromClasspathAndStoreThemInCache();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-authorization/src/main/java/org/bonitasoft/engine/authorization/properties/PropertiesWithSet.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.authorization.properties;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Properties;\nimport java.util.Set;\n\n/**\n * @author Baptiste Mesta\n */\npublic class PropertiesWithSet extends Properties {\n\n    private static final long serialVersionUID = 756391412537822704L;\n\n    public PropertiesWithSet(Properties properties) {\n        super(properties);\n    }\n\n    public PropertiesWithSet(File file) {\n        try (FileInputStream inStream = new FileInputStream(file)) {\n            this.load(inStream);\n        } catch (IOException e) {\n            throw new IllegalStateException(e);\n        }\n    }\n\n    public Set<String> getPropertyAsSet(final String propertyName) {\n        return stringToSet(getProperty(propertyName));\n    }\n\n    public static Set<String> stringToSet(final String propertyValueAsString) {\n        if (propertyValueAsString != null) {\n            final Set<String> propertiesSet = new HashSet<>();\n            final String propertyValueAsStringTrimmed = propertyValueAsString.trim();\n            if (propertyValueAsStringTrimmed.startsWith(\"[\") && propertyValueAsStringTrimmed.endsWith(\"]\")) {\n                String propertyCSV = propertyValueAsStringTrimmed.substring(1,\n                        propertyValueAsStringTrimmed.length() - 1);\n                propertyCSV = propertyCSV.trim();\n                if (propertyCSV.isEmpty()) {\n                    return Collections.emptySet();\n                }\n                final String[] propertyArray = propertyCSV.split(\",\");\n                for (final String propertyValue : propertyArray) {\n                    propertiesSet.add(propertyValue.trim());\n                }\n            } else {\n                propertiesSet.add(propertyValueAsString);\n            }\n            return propertiesSet;\n        } else {\n            return Collections.emptySet();\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-authorization/src/main/java/org/bonitasoft/engine/authorization/properties/ResourcesPermissionsMapping.java",
    "content": "/**\n * Copyright (C) 2021 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.authorization.properties;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.cache.CacheService;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Anthony Birembaut\n * @author Baptiste Mesta\n * @author Fabio Lombardi\n */\n@Component\n@Order(3)\npublic class ResourcesPermissionsMapping extends ConfigurationFile {\n\n    public static final String RESOURCE_IDS_SEPARATOR = \"/\";\n\n    public static final String API_METHOD_SEPARATOR = \"|\";\n\n    public static final String WILDCARD = \"*\";\n\n    /**\n     * Default name of the preferences file\n     */\n    public static final String PROPERTIES_FILENAME = \"resources-permissions-mapping.properties\";\n\n    @Override\n    protected String getPropertiesFileName() {\n        return PROPERTIES_FILENAME;\n    }\n\n    public ResourcesPermissionsMapping(CacheService cacheService, ConfigurationFilesManager configurationFilesManager) {\n        super(cacheService, configurationFilesManager);\n    }\n\n    public Set<String> getResourcePermissions(final String method, final String apiName, final String resourceName,\n            final List<String> resourceQualifiers) {\n        final String key = buildResourceKey(method, apiName, resourceName, resourceQualifiers);\n        return getPropertyAsSet(key);\n    }\n\n    public Set<String> getResourcePermissionsWithWildCard(final String method, final String apiName,\n            final String resourceName, final List<String> resourceQualifiers) {\n        if (resourceQualifiers != null && resourceQualifiers.size() > 0) {\n            for (int i = resourceQualifiers.size() - 1; i >= 0; i--) {\n                final List<String> resourceQualifiersWithWildCard = getResourceQualifiersWithWildCard(\n                        resourceQualifiers, i);\n                final String key = buildResourceKey(method, apiName, resourceName, resourceQualifiersWithWildCard);\n                final Set<String> permissions = getPropertyAsSet(key);\n                if (!permissions.isEmpty()) {\n                    return permissions;\n                }\n            }\n            final List<String> reducedResourceQualifiers = new ArrayList<>(resourceQualifiers);\n            reducedResourceQualifiers.remove(resourceQualifiers.size() - 1);\n            return getResourcePermissionsWithWildCard(method, apiName, resourceName, reducedResourceQualifiers);\n        }\n        return Collections.emptySet();\n    }\n\n    protected List<String> getResourceQualifiersWithWildCard(final List<String> resourceQualifiers,\n            final int wildCardPosition) {\n        final List<String> resourceQualifiersWithWildCard = new ArrayList<>(resourceQualifiers);\n        resourceQualifiersWithWildCard.set(wildCardPosition, WILDCARD);\n        return resourceQualifiersWithWildCard;\n    }\n\n    protected String buildResourceKey(final String method, final String apiName, final String resourceName,\n            final List<String> resourceQualifiers) {\n        StringBuilder key = new StringBuilder(method + API_METHOD_SEPARATOR + apiName + \"/\" + resourceName);\n        if (resourceQualifiers != null) {\n            for (final String resourceQualifier : resourceQualifiers) {\n                key.append(RESOURCE_IDS_SEPARATOR).append(resourceQualifier);\n            }\n        }\n        return key.toString();\n    }\n\n    public Set<String> getResourcePermissions(final String method, final String apiName, final String resourceName) {\n        return getResourcePermissions(method, apiName, resourceName, null);\n    }\n\n    @Override\n    protected boolean hasCustomVersion() {\n        return true;\n    }\n\n    @Override\n    protected boolean hasInternalVersion() {\n        return true;\n    }\n}\n"
  },
  {
    "path": "services/bonita-authorization/src/main/resources/org/bonitasoft/engine/authorization/properties/dynamic-permissions-checks.properties",
    "content": "# Dynamic checks on resources\n#\n# If a dynamic check is defined on a resource it overrides the static check behavior.\n# dynamic checks are defined like this\n# <method>|<resource>=[<exclusions>, check <class name of the rule>]\n# exclusions is a list of elements like this: <type>|<identifier> where type is user or profile and identifier is the username or the profile name.\n# Special characters like white space must be replaced with their unicode value (For example \\u0020 for the white space)\n#\n# example: to protect a case to only users that can start the process and to william.jobs, walter.bates and all users having the Administrator or Process manager profile\n# POST|bpm/case=[user|william.jobs, user|walter.bates, profile|Administrator, profile|Process\\u0020manager, check|org.bonitasoft.permissions.CasePermissionRule]\n#\n##\n# rules bellow are included by default in Bonita\n#\n# CasePermissionRule\n# Let a user access only cases that he is involved in and start cases that he can start\nGET|bpm/case=[profile|Administrator, check|org.bonitasoft.permissions.CasePermissionRule]\nPUT|bpm/case=[profile|Administrator, check|org.bonitasoft.permissions.CasePermissionRule]\nPOST|bpm/case=[profile|Administrator, check|org.bonitasoft.permissions.CasePermissionRule]\nDELETE|bpm/case=[profile|Administrator, check|org.bonitasoft.permissions.CasePermissionRule]\nGET|bpm/archivedCase=[profile|Administrator, check|org.bonitasoft.permissions.CasePermissionRule]\nGET|bpm/caseInfo=[profile|Administrator, check|org.bonitasoft.permissions.CasePermissionRule]\nGET|bpm/case/*/context=[profile|Administrator, check|org.bonitasoft.permissions.CaseContextPermissionRule]\nGET|bpm/archivedCase/*/context=[profile|Administrator, check|org.bonitasoft.permissions.CaseContextPermissionRule]\n\n\n# CaseVariablePermissionRule\n# Let a user get and update a variable of a case only if he is the process owner\nGET|bpm/caseVariable=[profile|Administrator, check|org.bonitasoft.permissions.CaseVariablePermissionRule]\nPUT|bpm/caseVariable=[profile|Administrator, check|org.bonitasoft.permissions.CaseVariablePermissionRule]\n\n\n# CommentPermissionRule\n# Let a user access only comments on cases that he is involved in\nGET|bpm/comment=[profile|Administrator, check|org.bonitasoft.permissions.CommentPermissionRule]\nPOST|bpm/comment=[profile|Administrator, check|org.bonitasoft.permissions.CommentPermissionRule]\nGET|bpm/archivedComment=[profile|Administrator, check|org.bonitasoft.permissions.CommentPermissionRule]\n\n\n# DocumentPermissionRule\n# Let a user access only document on cases that he is involved in\nGET|bpm/document=[profile|Administrator, check|org.bonitasoft.permissions.DocumentPermissionRule]\nPOST|bpm/document=[profile|Administrator, check|org.bonitasoft.permissions.DocumentPermissionRule]\nPUT|bpm/document=[profile|Administrator, check|org.bonitasoft.permissions.DocumentPermissionRule]\nDELETE|bpm/document=[profile|Administrator, check|org.bonitasoft.permissions.DocumentPermissionRule]\nGET|bpm/archiveddocument=[profile|Administrator, check|org.bonitasoft.permissions.DocumentPermissionRule]\nGET|bpm/archivedCaseDocument=[profile|Administrator, check|org.bonitasoft.permissions.DocumentPermissionRule]\nGET|bpm/caseDocument=[profile|Administrator, check|org.bonitasoft.permissions.DocumentPermissionRule]\nPOST|bpm/caseDocument=[profile|Administrator, check|org.bonitasoft.permissions.DocumentPermissionRule]\nPUT|bpm/caseDocument=[profile|Administrator, check|org.bonitasoft.permissions.DocumentPermissionRule]\nDELETE|bpm/caseDocument=[profile|Administrator, check|org.bonitasoft.permissions.DocumentPermissionRule]\n\n\n# ProcessPermissionRule\n# Let the user do get only on processes he deployed or that he supervised\nGET|bpm/process=[profile|Administrator, check|org.bonitasoft.permissions.ProcessPermissionRule]\nPOST|bpm/process=[profile|Administrator, check|org.bonitasoft.permissions.ProcessPermissionRule]\nPUT|bpm/process=[profile|Administrator, check|org.bonitasoft.permissions.ProcessPermissionRule]\nDELETE|bpm/process=[profile|Administrator, check|org.bonitasoft.permissions.ProcessPermissionRule]\nGET|bpm/process/*/contract=[profile|Administrator, check|org.bonitasoft.permissions.ProcessPermissionRule]\nGET|bpm/processInfo=[profile|Administrator, check|org.bonitasoft.permissions.ProcessPermissionRule]\nGET|bpm/diagram=[profile|Administrator, check|org.bonitasoft.permissions.ProcessPermissionRule]\nPOST|bpm/process/*/instantiation=[profile|Administrator, check|org.bonitasoft.permissions.ProcessInstantiationPermissionRule]\n\n\n# ProcessResolutionProblemPermissionRule\n# Let a user see process resolution problem only if he is process owner\nGET|bpm/processResolutionProblem=[profile|Administrator, check|org.bonitasoft.permissions.ProcessResolutionProblemPermissionRule]\n\n\n# ProcessConfigurationPermissionRule\n# Let a user manage process connectors and parameters only if he is process owner\nGET|bpm/processParameter=[profile|Administrator, check|org.bonitasoft.permissions.ProcessConfigurationPermissionRule]\nGET|bpm/processConnector=[profile|Administrator, check|org.bonitasoft.permissions.ProcessConfigurationPermissionRule]\nPUT|bpm/processConnector=[profile|Administrator, check|org.bonitasoft.permissions.ProcessConfigurationPermissionRule]\n\n\n# ProcessConnectorDependencyPermissionRule\n# Let a user see process connector dependency problem only if he is process owner\nGET|bpm/processConnectorDependency=[profile|Administrator, check|org.bonitasoft.permissions.ProcessConnectorDependencyPermissionRule]\n\n\n# ActorPermissionRule\n# Let a user manage actors only if he is process owner\nGET|bpm/actor=[profile|Administrator, check|org.bonitasoft.permissions.ActorPermissionRule]\nPUT|bpm/actor=[profile|Administrator, check|org.bonitasoft.permissions.ActorPermissionRule]\n\n\n# ActorMemberPermissionRule\n# Let a user add an actorMember only if he is process owner\nGET|bpm/actorMember=[profile|Administrator, check|org.bonitasoft.permissions.ActorMemberPermissionRule]\nPOST|bpm/actorMember=[profile|Administrator, check|org.bonitasoft.permissions.ActorMemberPermissionRule]\nDELETE|bpm/actorMember=[profile|Administrator, profile|Process\\u0020manager, check|org.bonitasoft.permissions.ActorMemberPermissionRule]\n\n\n# ProcessSupervisorPermissionRule\n# Let a user view and add process only if he is process owner\nGET|bpm/processSupervisor=[profile|Administrator, check|org.bonitasoft.permissions.ProcessSupervisorPermissionRule]\nPOST|bpm/processSupervisor=[profile|Administrator, check|org.bonitasoft.permissions.ProcessSupervisorPermissionRule]\nDELETE|bpm/processSupervisor=[profile|Administrator, check|org.bonitasoft.permissions.ProcessSupervisorPermissionRule]\n\n\n# TaskPermissionRule\n# Let a user access only tasks that are assigned or pending to him\nGET|bpm/flowNode=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule]\nPUT|bpm/flowNode=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule]\nGET|bpm/activity=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule]\nPUT|bpm/activity=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule]\nPUT|bpm/activityReplay=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule]\nGET|bpm/task=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule]\nPUT|bpm/task=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule]\nGET|bpm/humanTask=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule]\nPUT|bpm/humanTask=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule]\nGET|bpm/userTask=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule]\nPUT|bpm/userTask=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule]\nGET|bpm/hiddenUserTask=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule]\nPOST|bpm/hiddenUserTask=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule]\nDELETE|bpm/hiddenUserTask=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule]\nGET|bpm/manualTask=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule]\nPOST|bpm/manualTask=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule]\nPUT|bpm/manualTask=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule]\nGET|bpm/archivedFlowNode=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule]\nGET|bpm/archivedActivity=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule]\nGET|bpm/archivedTask=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule]\nGET|bpm/archivedHumanTask=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule]\nGET|bpm/archivedUserTask=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule]\nGET|bpm/archivedManualTask=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule]\nGET|bpm/archivedUserTask/*/context=[profile|Administrator, check|org.bonitasoft.permissions.TaskExecutionPermissionRule]\nGET|bpm/userTask/*/context=[profile|Administrator, check|org.bonitasoft.permissions.TaskExecutionPermissionRule]\nGET|bpm/userTask/*/contract=[profile|Administrator, check|org.bonitasoft.permissions.TaskExecutionPermissionRule]\nPOST|bpm/userTask/*/execution=[profile|Administrator, check|org.bonitasoft.permissions.TaskExecutionPermissionRule]\n\n# ConnectorInstancePermissionRule\n# Let a user see process configuration only if he is process owner\nGET|bpm/connectorInstance=[profile|Administrator, check|org.bonitasoft.permissions.ConnectorInstancePermissionRule]\nPUT|bpm/connectorInstance=[profile|Administrator, profile|Process\\u0020manager, check|org.bonitasoft.permissions.ConnectorInstancePermissionRule]\nGET|bpm/archivedConnectorInstance=[profile|Administrator, check|org.bonitasoft.permissions.ConnectorInstancePermissionRule]\nGET|bpm/connectorFailure=[profile|Administrator, profile|Process\\u0020manager]\n\n# BPM failures\nGET|bpm/failure=[profile|Administrator]\nGET|bpm/archivedFailure=[profile|Administrator]\n\n# UserPermissionRule\n# Let the user access and modify only himself\nGET|identity/user=[profile|Administrator, profile|Process\\u0020manager, check|org.bonitasoft.permissions.UserPermissionRule]\nPOST|identity/user=[profile|Administrator, check|org.bonitasoft.permissions.UserPermissionRule]\nPUT|identity/user=[profile|Administrator, check|org.bonitasoft.permissions.UserPermissionRule]\nGET|identity/personalcontactdata=[profile|Administrator, check|org.bonitasoft.permissions.UserPermissionRule]\nPOST|identity/personalcontactdata=[profile|Administrator, check|org.bonitasoft.permissions.UserPermissionRule]\nPUT|identity/personalcontactdata=[profile|Administrator, check|org.bonitasoft.permissions.UserPermissionRule]\nGET|identity/professionalcontactdata=[profile|Administrator, check|org.bonitasoft.permissions.UserPermissionRule]\nPOST|identity/professionalcontactdata=[profile|Administrator, check|org.bonitasoft.permissions.UserPermissionRule]\nPUT|identity/professionalcontactdata=[profile|Administrator, check|org.bonitasoft.permissions.UserPermissionRule]\n\n\n#ProfilePermissionRule\n# Secure profile related resources\nGET|portal/profile=[profile|Administrator, check|org.bonitasoft.permissions.ProfilePermissionRule]\nPOST|portal/profile=[profile|Administrator, check|org.bonitasoft.permissions.ProfilePermissionRule]\nPUT|portal/profile=[profile|Administrator, check|org.bonitasoft.permissions.ProfilePermissionRule]\nDELETE|portal/profile=[profile|Administrator, check|org.bonitasoft.permissions.ProfilePermissionRule]\n\n\n#ApplicationPermissionRule\n# Secure application resource\nGET|living/application=[profile|Administrator, check|org.bonitasoft.permissions.ApplicationPermissionRule]\n\n\n#ApplicationMenuPermissionRule\n# Secure application menu resource\nGET|living/application-menu=[profile|Administrator, check|org.bonitasoft.permissions.ApplicationMenuPermissionRule]\n\n# Platform information\nGET|system/information=[profile|Administrator]\n\n#Servlets\nGET|portal/documentDownload=[profile|Administrator, check|org.bonitasoft.permissions.DownloadDocumentPermissionRule]\nGET|API/documentDownload=[profile|Administrator, check|org.bonitasoft.permissions.DownloadDocumentPermissionRule]\nGET|portal/custom-page/API/documentDownload=[profile|Administrator, check|org.bonitasoft.permissions.DownloadDocumentPermissionRule]\nGET|portal/formsDocumentDownload=[profile|Administrator, check|org.bonitasoft.permissions.DownloadDocumentPermissionRule]\nGET|portal/downloadDocument=[profile|Administrator, check|org.bonitasoft.permissions.DownloadDocumentPermissionRule]\nGET|portal/formsDocumentImage=[profile|Administrator, check|org.bonitasoft.permissions.DownloadDocumentPermissionRule]\nGET|API/formsDocumentImage=[profile|Administrator, check|org.bonitasoft.permissions.DownloadDocumentPermissionRule]\nGET|portal/custom-page/API/formsDocumentImage=[profile|Administrator, check|org.bonitasoft.permissions.DownloadDocumentPermissionRule]\n"
  },
  {
    "path": "services/bonita-authorization/src/test/java/org/bonitasoft/engine/authorization/PermissionsBuilderTest.java",
    "content": "/**\n * Copyright (C) 2021 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.authorization;\n\nimport static java.util.Arrays.asList;\nimport static java.util.Collections.emptySet;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.*;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.authorization.properties.CompoundPermissionsMapping;\nimport org.bonitasoft.engine.authorization.properties.CustomPermissionsMapping;\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.session.model.SSession;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class PermissionsBuilderTest {\n\n    @Mock\n    private CustomPermissionsMapping customPermissionsMapping;\n    @Mock\n    private CompoundPermissionsMapping compoundPermissionsMapping;\n    @Mock\n    private ApplicationService applicationService;\n\n    private PermissionsBuilder permissionsBuilder;\n\n    @Before\n    public void setUp() {\n        init();\n    }\n\n    private void init() {\n        permissionsBuilder = spy(new PermissionsBuilder(applicationService, customPermissionsMapping,\n                compoundPermissionsMapping));\n    }\n\n    @Test\n    public void should_getPermissions_work_without_profile_nor_custom_user_permissions() throws Exception {\n        doReturn(emptySet()).when(permissionsBuilder).getProfilesPermissions(anyList());\n        doReturn(emptySet()).when(permissionsBuilder).getCustomUserPermissions(any());\n        final SSession session = mock(SSession.class);\n        doReturn(\"Jean-Claude\").when(session).getUserName();\n\n        final Set<String> permissions = permissionsBuilder.getPermissions(session.isTechnicalUser(),\n                session.getProfiles(), session.getUserName());\n\n        assertThat(permissions).as(\"No permissions other than the user should have been returned\")\n                .containsOnly(\"user|Jean-Claude\");\n    }\n\n    @Test\n    public void should_getPermissions_add_profile_and_custom_permissions() throws Exception {\n        doReturn(emptySet()).when(permissionsBuilder).getProfilesPermissions(anyList());\n        doReturn(emptySet()).when(permissionsBuilder).getCustomUserPermissions(anyString());\n        final SSession session = mock(SSession.class);\n        doReturn(\"Loïc\").when(session).getUserName();\n\n        permissionsBuilder.getPermissions(session.isTechnicalUser(), session.getProfiles(), session.getUserName());\n\n        verify(permissionsBuilder).getProfilesPermissions(anyList());\n        verify(permissionsBuilder).getCustomUserPermissions(anyString());\n    }\n\n    @Test\n    public void should_getProfilesPermissions_return_all_types_of_permissions() throws Exception {\n        // given\n        doReturn(List.of(\"custompage_page1\", \"custompage_page2\")).when(applicationService)\n                .getAllPagesForProfile(\"profile1\");\n        doReturn(List.of(\"custompage_page1\", \"custompage_page3\")).when(applicationService)\n                .getAllPagesForProfile(\"profile2\");\n        doReturn(aSet(\"Perm1\", \"Perm2\")).when(compoundPermissionsMapping).getPropertyAsSet(eq(\"custompage_page1\"));\n        doReturn(aSet(\"Perm1\", \"Perm21\")).when(compoundPermissionsMapping).getPropertyAsSet(eq(\"custompage_page2\"));\n        doReturn(aSet(\"Perm31\", \"Perm32\")).when(compoundPermissionsMapping).getPropertyAsSet(eq(\"custompage_page3\"));\n        doReturn(Set.of(\"customprofile_permission11\")).when(customPermissionsMapping)\n                .getPropertyAsSet(\"profile|profile1\");\n        doReturn(Set.of(\"customprofile_permission31\")).when(customPermissionsMapping)\n                .getPropertyAsSet(\"user|Jean-Claude\");\n        doReturn(Set.of(\"customprofile_permission21\", \"customprofile_permission22\")).when(customPermissionsMapping)\n                .getPropertyAsSet(\"profile|profile2\");\n        doReturn(aSet(\"Perm41\", \"Perm42\")).when(compoundPermissionsMapping)\n                .getPropertyAsSet(eq(\"customprofile_permission22\"));\n\n        final SSession session = mock(SSession.class);\n        final List<String> myProfiles = List.of(\"profile1\", \"profile2\");\n        doReturn(myProfiles).when(session).getProfiles();\n        doReturn(\"Jean-Claude\").when(session).getUserName();\n\n        // when\n        final Set<String> permissions = permissionsBuilder.getPermissions(session.isTechnicalUser(),\n                session.getProfiles(), session.getUserName());\n\n        // then\n        assertThat(permissions).containsExactlyInAnyOrder(\"Perm1\", \"Perm2\", \"Perm21\", \"Perm31\", \"Perm32\",\n                \"customprofile_permission11\",\n                \"customprofile_permission21\", \"Perm41\", \"Perm42\", \"profile|profile1\", \"profile|profile2\",\n                \"customprofile_permission31\",\n                \"user|Jean-Claude\");\n    }\n\n    @Test\n    public void should_getCustomPermissions_work_with_compound_permissions() {\n        doReturn(aSet(\"Perm1\", \"Perm2\", \"taskListing\")).when(customPermissionsMapping).getPropertyAsSet(\"user|myUser\");\n        doReturn(aSet(\"Perm3\", \"Perm4\")).when(compoundPermissionsMapping).getPropertyAsSet(\"taskListing\");\n\n        final Set<String> permissions = permissionsBuilder.getCustomPermissions(\"user\", \"myUser\");\n\n        assertThat(permissions).containsOnly(\"Perm1\", \"Perm2\", \"Perm3\", \"Perm4\");\n    }\n\n    @Test\n    public void getPermissions_should_return_empty_list_for_technical_user() throws Exception {\n        // given:\n        final SSession session = mock(SSession.class);\n        doReturn(true).when(session).isTechnicalUser();\n\n        // when:\n        final Set<String> permissions = permissionsBuilder.getPermissions(session.isTechnicalUser(),\n                session.getProfiles(), session.getUserName());\n\n        // then:\n        assertThat(permissions).isEmpty();\n    }\n\n    private Set<String> aSet(String... elements) {\n        return new HashSet<>(asList(elements));\n    }\n}\n"
  },
  {
    "path": "services/bonita-authorization/src/test/java/org/bonitasoft/engine/authorization/properties/ConfigurationFileTest.java",
    "content": "/**\n * Copyright (C) 2021 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.authorization.properties;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.authorization.properties.ConfigurationFile.CONFIGURATION_FILES_CACHE;\nimport static org.bonitasoft.engine.authorization.properties.ConfigurationFilesManager.getProperties;\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Properties;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.cache.CacheService;\nimport org.bonitasoft.engine.commons.io.IOUtil;\nimport org.junit.Before;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ConfigurationFileTest {\n\n    public static final long TENANT_ID = 12L;\n    private static Properties compoundProperties;\n    private static Properties resourcesProperties;\n    private static Properties customProperties;\n\n    private ConfigurationFile resourcesPermissionsMapping;\n    private ConfigurationFile customPermissionsMapping;\n    private ConfigurationFile compoundPermissionsMapping;\n\n    @Mock\n    private CacheService cacheService;\n\n    @Mock\n    private ConfigurationFilesManager configurationFilesManager;\n\n    @BeforeClass\n    public static void init() throws Exception {\n        compoundProperties = getProperties(IOUtil.getAllContentFrom(\n                ConfigurationFileTest.class.getResourceAsStream(\"/compound-permissions-mapping.properties\")));\n        resourcesProperties = getProperties(IOUtil.getAllContentFrom(\n                ConfigurationFileTest.class.getResourceAsStream(\"/resources-permissions-mapping.properties\")));\n        customProperties = getProperties(IOUtil.getAllContentFrom(\n                ConfigurationFileTest.class.getResourceAsStream(\"/custom-permissions-mapping.properties\")));\n    }\n\n    @Before\n    public void setupMocksAndSpies() {\n        resourcesPermissionsMapping = spy(\n                new ResourcesPermissionsMapping(cacheService, configurationFilesManager));\n        doReturn(resourcesProperties).when(resourcesPermissionsMapping).getProperties();\n\n        customPermissionsMapping = spy(\n                new CustomPermissionsMapping(cacheService, configurationFilesManager));\n        doReturn(customProperties).when(customPermissionsMapping).getProperties();\n\n        compoundPermissionsMapping = spy(\n                new CompoundPermissionsMapping(cacheService, configurationFilesManager));\n        doReturn(compoundProperties).when(compoundPermissionsMapping).getProperties();\n    }\n\n    @Test\n    public void should_getProperty_return_the_right_custom_permissions_with_special_characters() {\n        final String customValue = customPermissionsMapping.getProperty(\"profile|HR manager\");\n        assertEquals(\"[ManageProfiles]\", customValue);\n    }\n\n    @Test\n    public void should_getPropertyAsSet_return_the_right_permissions_with_trailing_spaces() {\n        final String value = compoundPermissionsMapping.getProperty(\"caseListingPage\");\n        final Set<String> valueAsList = compoundPermissionsMapping.getPropertyAsSet(\"caseListingPage\");\n\n        assertEquals(\"caseVisualizationWithTrailingSpace\", value);\n        assertThat(valueAsList).containsOnly(\"caseVisualizationWithTrailingSpace\");\n    }\n\n    @Test\n    public void should_getProperty_return_null_with_unknown_permissions() {\n        final String value = compoundPermissionsMapping.getProperty(\"unknownListingPage\");\n        assertThat(value).isNull();\n    }\n\n    @Test\n    public void should_getProperty_return_the_right_compound_permissions() {\n        final String compoundValue = compoundPermissionsMapping.getProperty(\"taskListingPage\");\n        assertEquals(\"[TaskVisualization, CaseVisualization]\", compoundValue);\n    }\n\n    @Test\n    public void should_getProperty_return_the_right_resource_permissions() {\n        final String resourcesValue = resourcesPermissionsMapping.getProperty(\"GET|bpm/identity\");\n        assertEquals(\"[UserVisualization, groupVisualization]\", resourcesValue);\n    }\n\n    @Test\n    public void should_getProperty_return_the_right_custom_permissions() {\n        final String customValue = customPermissionsMapping.getProperty(\"profile|User\");\n        assertEquals(\"[ManageLooknFeel, ManageProfiles]\", customValue);\n    }\n\n    @Test\n    public void should_getPropertyAsSet_return_the_right_permissions_list() {\n        final Set<String> compoundPermissionsList = compoundPermissionsMapping.getPropertyAsSet(\"taskListingPage\");\n        assertThat(compoundPermissionsList).containsOnly(\"TaskVisualization\", \"CaseVisualization\");\n    }\n\n    @Test\n    public void should_getPropertyAsSet_return_the_right_permissions_list_with_single_value() {\n        final Set<String> compoundPermissionsList = compoundPermissionsMapping.getPropertyAsSet(\"processListingPage\");\n        assertThat(compoundPermissionsList).containsOnly(\"processVisualization\");\n    }\n\n    @Test\n    public void getTenantProperties_should_get_from_cache_and_store_to_cache_if_not_already_in() throws Exception {\n        // given:\n        final ResourcesPermissionsMapping configFile = spy(\n                new ResourcesPermissionsMapping(cacheService, configurationFilesManager));\n        final Properties props = new Properties();\n        doReturn(props).when(configurationFilesManager).getTenantProperties(\"resources-permissions-mapping.properties\",\n                false);\n\n        // when:\n        configFile.getProperties();\n\n        // then:\n        verify(cacheService).get(CONFIGURATION_FILES_CACHE, \"resources-permissions-mapping.properties\");\n        verify(cacheService).store(CONFIGURATION_FILES_CACHE, \"resources-permissions-mapping.properties\",\n                props);\n    }\n}\n"
  },
  {
    "path": "services/bonita-authorization/src/test/java/org/bonitasoft/engine/authorization/properties/ConfigurationFilesManagerTest.java",
    "content": "/**\n * Copyright (C) 2021 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.authorization.properties;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.authorization.properties.ConfigurationFilesManager.getProperties;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.verify;\n\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Properties;\n\nimport org.bonitasoft.engine.home.BonitaHomeServer;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Baptiste Mesta\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class ConfigurationFilesManagerTest {\n\n    private static final long TENANT_ID = 543892L;\n    private static final String MY_PROP_INTERNAL_PROPERTIES = \"myProp-internal.properties\";\n    @Mock\n    private BonitaHomeServer bonitaHomeServer;\n    @Spy\n    private ConfigurationFilesManager configurationFilesManager;\n    @Captor\n    private ArgumentCaptor<byte[]> contentCaptor;\n\n    @Before\n    public void before() {\n        doReturn(bonitaHomeServer).when(configurationFilesManager).getConfigurationFilesUtils();\n    }\n\n    @Test\n    public void should_removeProperty_call_update_with_new_content() throws Exception {\n        //given\n        final HashMap<String, Properties> configurationFiles = new HashMap<>();\n        configurationFiles.put(\"configFile1.properties\", getProperties(\"myProp1=authKey\\nmyProp2=passHash\".getBytes()));\n        configurationFiles.put(MY_PROP_INTERNAL_PROPERTIES,\n                getProperties(\"testProperty=testValue\\npropToRemove=willBeRemoved\".getBytes()));\n        doReturn(configurationFiles).when(configurationFilesManager).getTenantConfigurations();\n        //when\n        configurationFilesManager.removeProperty(MY_PROP_INTERNAL_PROPERTIES, \"propToRemove\");\n        //then\n        verify(bonitaHomeServer).updateTenantPortalConfigurationFile(eq(MY_PROP_INTERNAL_PROPERTIES),\n                contentCaptor.capture());\n        assertThat(new String(contentCaptor.getValue())).doesNotContain(\"propToRemove\").contains(\"testProperty\",\n                \"testValue\");\n    }\n\n    @Test\n    public void should_setProperty_call_update_with_new_content() throws Exception {\n        //given\n        doReturn(getProperties(\"testProperty=testValue\\npropToRemove=willBeRemoved\".getBytes()))\n                .when(configurationFilesManager).getTenantPortalConfiguration(MY_PROP_INTERNAL_PROPERTIES);\n        //when\n        configurationFilesManager.setProperty(MY_PROP_INTERNAL_PROPERTIES, \"testProperty\", \"new Value\");\n        //then\n        verify(bonitaHomeServer).updateTenantPortalConfigurationFile(eq(MY_PROP_INTERNAL_PROPERTIES),\n                contentCaptor.capture());\n        assertThat(new String(contentCaptor.getValue())).doesNotContain(\"testValue\").contains(\"testProperty\",\n                \"new Value\");\n    }\n\n    @Test\n    public void getTenantProperties_should_merge_custom_properties_if_exist()\n            throws IOException {\n        //given\n        final Properties defaultProps = new Properties();\n        defaultProps.put(\"defaultKey\", \"defaultValue\");\n        final Properties customProps = new Properties();\n        customProps.put(\"customKey\", \"customValue\");\n        Map<String, Properties> propertiesMap = new HashMap<>();\n        propertiesMap.put(\"toto.properties\", defaultProps);\n        propertiesMap.put(\"toto-custom.properties\", customProps);\n        doReturn(propertiesMap).when(configurationFilesManager).getTenantConfigurations();\n\n        //when\n        final Properties properties = configurationFilesManager.getTenantProperties(\"toto.properties\");\n\n        //then\n        assertThat(properties).containsEntry(\"defaultKey\", \"defaultValue\").containsEntry(\"customKey\", \"customValue\");\n    }\n\n    @Test\n    public void getTenantProperties_should_merge_internal_properties_if_exist()\n            throws IOException {\n        //given\n        final Properties defaultProps = new Properties();\n        defaultProps.put(\"defaultKey\", \"defaultValue\");\n        final Properties internalProps = new Properties();\n        internalProps.put(\"internalKey\", \"internalValue\");\n        Map<String, Properties> propertiesMap = new HashMap<>();\n        propertiesMap.put(\"toto.properties\", defaultProps);\n        propertiesMap.put(\"toto-internal.properties\", internalProps);\n        doReturn(propertiesMap).when(configurationFilesManager).getTenantConfigurations();\n\n        //when\n        final Properties properties = configurationFilesManager.getTenantProperties(\"toto.properties\");\n\n        //then\n        assertThat(properties).containsEntry(\"defaultKey\", \"defaultValue\").containsEntry(\"internalKey\",\n                \"internalValue\");\n    }\n\n    @Test\n    public void getTenantProperties_should_not_fail_if_base_file_does_not_exist()\n            throws IOException {\n        //given\n        final Properties defaultProps = new Properties();\n        defaultProps.put(\"defaultKey\", \"defaultValue\");\n        Map<String, Properties> propertiesMap = new HashMap<>();\n        propertiesMap.put(\"toto.properties\", defaultProps);\n        doReturn(propertiesMap).when(configurationFilesManager).getTenantConfigurations();\n\n        //when\n        final Properties properties = configurationFilesManager.getTenantProperties(\"non-existing.properties\");\n\n        //then\n        assertThat(properties).isEmpty();\n    }\n\n    @Test\n    public void custom_properties_should_overwrite_internal_properties() throws IOException {\n        //given\n        final Properties defaultProps = new Properties();\n        defaultProps.put(\"defaultKey\", \"defaultValue\");\n\n        final Properties internalProps = new Properties();\n        internalProps.put(\"otherKey\", \"someInternallyManagedValue\");\n\n        final Properties customProps = new Properties();\n        final String expectedOverwrittenValue = \"custom_changed_value\";\n        customProps.put(\"otherKey\", expectedOverwrittenValue);\n\n        Map<String, Properties> propertiesMap = new HashMap<>();\n        propertiesMap.put(\"overwrite.properties\", defaultProps);\n        propertiesMap.put(\"overwrite-internal.properties\", internalProps);\n        propertiesMap.put(\"overwrite-custom.properties\", customProps);\n        doReturn(propertiesMap).when(configurationFilesManager).getTenantConfigurations();\n\n        //when\n        final Properties properties = configurationFilesManager.getTenantProperties(\"overwrite.properties\");\n\n        //then\n        assertThat(properties).containsEntry(\"defaultKey\", \"defaultValue\").containsEntry(\"otherKey\",\n                expectedOverwrittenValue);\n    }\n}\n"
  },
  {
    "path": "services/bonita-authorization/src/test/java/org/bonitasoft/engine/authorization/properties/ResourcesPermissionsMappingTest.java",
    "content": "/**\n * Copyright (C) 2021 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.authorization.properties;\n\nimport static java.util.Collections.singletonList;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.authorization.properties.ConfigurationFilesManager.getProperties;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.spy;\n\nimport java.util.List;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.cache.CacheService;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ResourcesPermissionsMappingTest {\n\n    @Mock\n    private CacheService cacheService;\n\n    @Mock\n    private ConfigurationFilesManager configurationFilesManager;\n\n    @Test\n    public void testGetResourcePermission() {\n        //given\n        final String fileContent = \"GET|bpm/process [Process visualization, Process categories, Process actor mapping visualization, Connector visualization]\\n\"\n                + \"POST|bpm/process [Process Deploy]\\n\" +\n                \"POST|bpm/process/6 [Custom permission]\\n\" +\n                \"PUT|bpm/process []\";\n        final ResourcesPermissionsMapping resourcesPermissionsMapping = getResourcesPermissionsMapping(fileContent);\n\n        //when\n        final Set<String> getPermissions = resourcesPermissionsMapping.getResourcePermissions(\"GET\", \"bpm\", \"process\");\n        final Set<String> postPermission = resourcesPermissionsMapping.getResourcePermissions(\"POST\", \"bpm\", \"process\");\n        final Set<String> postOnSinglePermission = resourcesPermissionsMapping.getResourcePermissions(\"POST\", \"bpm\",\n                \"process\", singletonList(\"6\"));\n        final Set<String> putPermissions = resourcesPermissionsMapping.getResourcePermissions(\"PUT\", \"bpm\", \"process\");\n        final Set<String> unknown = resourcesPermissionsMapping.getResourcePermissions(\"unknown\", \"unknown\", \"unknown\",\n                singletonList(\"unknown\"));\n\n        //then\n        assertThat(getPermissions).containsOnly(\"Process visualization\", \"Process categories\",\n                \"Process actor mapping visualization\",\n                \"Connector visualization\");\n        assertThat(postPermission).containsOnly(\"Process Deploy\");\n        assertThat(postOnSinglePermission).containsOnly(\"Custom permission\");\n        assertThat(putPermissions).isEmpty();\n        assertThat(unknown).isEmpty();\n    }\n\n    @Test\n    public void testGetResourcePermissionWithWildCard() {\n        //given\n        final String fileContent = \"POST|bpm/process/* [Process Deploy]\\n\" +\n                \"POST|bpm/process/*/instantiation [Custom permission]\\n\" +\n                \"PUT|bpm/process/*/expression [Expression update]\";\n\n        final ResourcesPermissionsMapping resourcesPermissionsMapping = getResourcesPermissionsMapping(fileContent);\n\n        //when\n        final Set<String> getWithResourcesQualifier = resourcesPermissionsMapping.getResourcePermissionsWithWildCard(\n                \"GET\", \"bpm\", \"process\", List.of(\"6\"));\n        final Set<String> postWithResourcesQualifier = resourcesPermissionsMapping.getResourcePermissionsWithWildCard(\n                \"POST\", \"bpm\", \"process\", List.of(\"6\"));\n        final Set<String> postWithResourcesQualifiers = resourcesPermissionsMapping.getResourcePermissionsWithWildCard(\n                \"POST\", \"bpm\", \"process\", List.of(\"6\", \"instantiation\"));\n        final Set<String> putWithResourcesQualifiers = resourcesPermissionsMapping.getResourcePermissionsWithWildCard(\n                \"PUT\", \"bpm\", \"process\", List.of(\"6\", \"expression\", \"10\"));\n\n        //then\n        assertThat(getWithResourcesQualifier).isEmpty();\n        assertThat(postWithResourcesQualifier).containsOnly(\"Process Deploy\");\n        assertThat(postWithResourcesQualifiers).containsOnly(\"Custom permission\");\n        assertThat(putWithResourcesQualifiers).containsOnly(\"Expression update\");\n    }\n\n    public ResourcesPermissionsMapping getResourcesPermissionsMapping(final String fileContent) {\n        final ResourcesPermissionsMapping resourcesPermissionsMapping = spy(\n                new ResourcesPermissionsMapping(cacheService, configurationFilesManager));\n        doReturn(getProperties(fileContent.getBytes())).when(resourcesPermissionsMapping).getProperties();\n        return resourcesPermissionsMapping;\n    }\n}\n"
  },
  {
    "path": "services/bonita-authorization/src/test/resources/compound-permissions-mapping.properties",
    "content": "processListingPage processVisualization\ncaseListingPage caseVisualizationWithTrailingSpace \ntaskListingPage [TaskVisualization, CaseVisualization]\n\n"
  },
  {
    "path": "services/bonita-authorization/src/test/resources/custom-permissions-mapping.properties",
    "content": "profile|User=[ManageLooknFeel, ManageProfiles]\nprofile|HR\\u0020manager=[ManageProfiles]\n"
  },
  {
    "path": "services/bonita-authorization/src/test/resources/resources-permissions-mapping.properties",
    "content": "GET|bpm/identity [UserVisualization, groupVisualization]\n"
  },
  {
    "path": "services/bonita-builder/build.gradle",
    "content": "\n\ndescription = 'Bonita Builder: Service Implementation'\n"
  },
  {
    "path": "services/bonita-builder/src/main/java/org/bonitasoft/engine/builder/BuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.builder;\n\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.io.Reader;\nimport java.net.URL;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Properties;\n\npublic class BuilderFactory {\n\n    private static final String BUILDER_FACTORIES_DEFAULT_FILE = \"builder-factories.properties\";\n\n    private static final String BUILDER_FACTORIES_EXTENSION_FILE = \"builder-factories-ext.properties\";\n\n    private final Map<String, Object> factoryCache;\n\n    private final Properties properties;\n\n    private static final Object MUTEX = new BuilderFactoryMutex();\n\n    private static BuilderFactory INSTANCE = null;\n\n    private BuilderFactory(final Properties properties) {\n        this.properties = properties;\n        factoryCache = new HashMap<String, Object>();\n    }\n\n    private static final class BuilderFactoryMutex {\n\n    }\n\n    public static BuilderFactory getInstance() {\n        if (INSTANCE == null) {\n            synchronized (MUTEX) {\n                // ensure we do not create many instances of this class\n                if (INSTANCE == null) {\n                    URL defaultFileURL = null;\n                    try {\n                        defaultFileURL = BuilderFactory.class.getResource(BUILDER_FACTORIES_DEFAULT_FILE);\n                        final Properties defaultProperties = getProperties(defaultFileURL);\n                        final Properties allProperties = new Properties(defaultProperties);\n\n                        final URL extensionFileURL = BuilderFactory.class.getResource(BUILDER_FACTORIES_EXTENSION_FILE);\n                        if (extensionFileURL != null) {\n                            final Properties extensionProperties = getProperties(extensionFileURL);\n                            allProperties.putAll(extensionProperties);\n                        }\n\n                        INSTANCE = new BuilderFactory(allProperties);\n\n                    } catch (final Exception e) {\n                        throw new RuntimeException(\"Unable to load builder factories from : fileURL=\" + defaultFileURL);\n                    }\n                }\n            }\n        }\n        return INSTANCE;\n    }\n\n    private synchronized void cacheFactory(final String interfaceName, final String className) {\n        try {\n            if (className == null || \"null\".equals(className)) {\n                throw new Exception(\"Factory implementation of \" + interfaceName + \" is required.\");\n            }\n            final Class<?> clazz = Class.forName(className, true, Thread.currentThread().getContextClassLoader());\n            final Object factory = clazz.newInstance();\n            factoryCache.put(interfaceName, factory);\n        } catch (final Exception e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static <T extends Object> T get(final Class<T> clazz) {\n        final T factoryImplementation = getInstance().getInternalBuilderFactory(clazz);\n        if (factoryImplementation == null) {\n            throw new RuntimeException(\"No factory found for interface: \" + clazz);\n        }\n        return factoryImplementation;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private <T extends Object> T getInternalBuilderFactory(final Class<T> clazz) {\n        if (!factoryCache.containsKey(clazz.getName())) {\n            cacheFactory(clazz.getName(), properties.getProperty(clazz.getName()));\n        }\n        return (T) factoryCache.get(clazz.getName());\n    }\n\n    public static Properties getProperties(final URL url) throws IOException {\n        final InputStreamReader reader = new InputStreamReader(url.openStream());\n        return getProperties(reader);\n    }\n\n    private static Properties getProperties(final Reader reader) throws IOException {\n        final Properties properties = new Properties();\n        try {\n            properties.load(reader);\n            return properties;\n        } finally {\n            reader.close();\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-builder/src/main/resources/org/bonitasoft/engine/builder/builder-factories.properties",
    "content": "org.bonitasoft.engine.actor.mapping.model.SActorLogBuilderFactory = org.bonitasoft.engine.actor.mapping.impl.SActorLogBuilderFactoryImpl\norg.bonitasoft.engine.actor.mapping.model.SActorUpdateBuilderFactory = org.bonitasoft.engine.actor.mapping.model.impl.SActorUpdateBuilderFactoryImpl\norg.bonitasoft.engine.command.model.SCommandLogBuilderFactory = org.bonitasoft.engine.command.model.SCommandLogBuilderFactoryImpl\norg.bonitasoft.engine.command.model.SCommandUpdateBuilderFactory = org.bonitasoft.engine.command.model.SCommandUpdateBuilderFactoryImpl\norg.bonitasoft.engine.core.category.model.builder.SCategoryLogBuilderFactory = org.bonitasoft.engine.core.category.model.builder.impl.SCategoryLogBuilderFactoryImpl\norg.bonitasoft.engine.core.category.model.builder.SCategoryUpdateBuilderFactory = org.bonitasoft.engine.core.category.model.builder.impl.SCategoryUpdateBuilderFactoryImpl\norg.bonitasoft.engine.core.category.model.builder.SProcessCategoryMappingBuilderFactory = org.bonitasoft.engine.core.category.model.builder.impl.SProcessCategoryMappingBuilderFactoryImpl\norg.bonitasoft.engine.core.operation.model.builder.SLeftOperandBuilderFactory = org.bonitasoft.engine.core.operation.model.builder.impl.SLeftOperandBuilderFactoryImpl\norg.bonitasoft.engine.core.operation.model.builder.SOperationBuilderFactory = org.bonitasoft.engine.core.operation.model.builder.impl.SOperationBuilderFactoryImpl\norg.bonitasoft.engine.core.process.comment.model.builder.SCommentLogBuilderFactory = org.bonitasoft.engine.core.process.comment.model.builder.impl.SCommentLogBuilderFactoryImpl\norg.bonitasoft.engine.core.process.definition.model.builder.SActorLogBuilderFactory = org.bonitasoft.engine.actor.mapping.impl.SActorLogBuilderFactoryImpl\norg.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionBuilderFactory = org.bonitasoft.engine.core.process.definition.model.builder.impl.SProcessDefinitionBuilderFactoryImpl\norg.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionDeployInfoUpdateBuilderFactory = org.bonitasoft.engine.core.process.definition.model.builder.impl.SProcessDefinitionDeployInfoUpdateBuilderFactoryImpl\norg.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionLogBuilderFactory = org.bonitasoft.engine.core.process.definition.model.builder.impl.SProcessDefinitionLogBuilderFactoryImpl\norg.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SEndEventDefinitionBuilderFactory = org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.impl.SEndEventDefinitionBuilderFactoryImpl\norg.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SThrowErrorEventTriggerDefinitionBuilderFactory = org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.impl.SThrowErrorEventTriggerDefinitionBuilderFactoryImpl\norg.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SThrowMessageEventTriggerDefinitionBuilderFactory = org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.impl.SThrowMessageEventTriggerDefinitionBuilderFactoryImpl\norg.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SThrowSignalEventTriggerDefinitionBuilderFactory = org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.impl.SThrowSignalEventTriggerDefinitionBuilderFactoryImpl\norg.bonitasoft.engine.core.document.model.builder.SDocumentMappingBuilderFactory = org.bonitasoft.engine.core.document.model.builder.impl.SDocumentMappingBuilderFactoryImpl\norg.bonitasoft.engine.core.document.model.builder.SDocumentMappingLogBuilderFactory = org.bonitasoft.engine.core.document.model.builder.impl.SDocumentMappingLogBuilderFactoryImpl\norg.bonitasoft.engine.core.document.model.builder.SDocumentMappingUpdateBuilderFactory = org.bonitasoft.engine.core.document.model.builder.impl.SDocumentMappingUpdateBuilderFactoryImpl\norg.bonitasoft.engine.core.process.instance.model.archive.builder.SAAutomaticTaskInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.SAAutomaticTaskInstanceBuilderFactoryImpl\norg.bonitasoft.engine.core.process.instance.model.archive.builder.SACallActivityInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.SACallActivityInstanceBuilderFactoryImpl\norg.bonitasoft.engine.core.process.instance.model.archive.builder.SAConnectorInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.SAConnectorInstanceBuilderFactoryImpl\norg.bonitasoft.engine.core.process.instance.model.archive.builder.SAGatewayInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.SAGatewayInstanceBuilderFactoryImpl\norg.bonitasoft.engine.core.process.instance.model.archive.builder.SALoopActivityInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.SALoopActivityInstanceBuilderFactoryImpl\norg.bonitasoft.engine.core.process.instance.model.archive.builder.SAManualTaskInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.SAManualTaskInstanceBuilderFactoryImpl\norg.bonitasoft.engine.core.process.instance.model.archive.builder.SAMultiInstanceActivityInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.SAMultiInstanceActivityInstanceBuilderFactoryImpl\norg.bonitasoft.engine.core.process.instance.model.archive.builder.SANamedElementBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.SANamedElementBuilderFactoryImpl\norg.bonitasoft.engine.core.process.instance.model.archive.builder.SAProcessInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.SAProcessInstanceBuilderFactoryImpl\norg.bonitasoft.engine.core.process.instance.model.archive.builder.SAReceiveTaskInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.SAReceiveTaskInstanceBuilderFactoryImpl\norg.bonitasoft.engine.core.process.instance.model.archive.builder.SASendTaskInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.SASendTaskInstanceBuilderFactoryImpl\norg.bonitasoft.engine.core.process.instance.model.archive.builder.SASubProcessActivityInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.SASubProcessActivityInstanceBuilderFactoryImpl\norg.bonitasoft.engine.core.process.instance.model.archive.builder.SAUserTaskInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.SAUserTaskInstanceBuilderFactoryImpl\norg.bonitasoft.engine.core.process.instance.model.archive.builder.event.SAEndEventInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.event.impl.SAEndEventInstanceBuilderFactoryImpl\norg.bonitasoft.engine.core.process.instance.model.archive.builder.event.SAStartEventInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.event.impl.SAStartEventInstanceBuilderFactoryImpl\norg.bonitasoft.engine.core.process.instance.model.builder.SAutomaticTaskInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.impl.SAutomaticTaskInstanceBuilderFactoryImpl\norg.bonitasoft.engine.core.process.instance.model.builder.SCallActivityInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.impl.SCallActivityInstanceBuilderFactoryImpl\norg.bonitasoft.engine.core.process.instance.model.builder.SConnectorInstanceLogBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.impl.SConnectorInstanceLogBuilderFactoryImpl\norg.bonitasoft.engine.core.process.instance.model.builder.SGatewayInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.impl.SGatewayInstanceBuilderFactoryImpl\norg.bonitasoft.engine.core.process.instance.model.builder.SLoopActivityInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.impl.SLoopActivityInstanceBuilderFactoryImpl\norg.bonitasoft.engine.core.process.instance.model.builder.SManualTaskInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.impl.SManualTaskInstanceBuilderFactoryImpl\norg.bonitasoft.engine.core.process.instance.model.builder.SMultiInstanceActivityInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.impl.SMultiInstanceActivityInstanceBuilderFactoryImpl\norg.bonitasoft.engine.core.process.instance.model.builder.SReceiveTaskInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.impl.SReceiveTaskInstanceBuilderFactoryImpl\norg.bonitasoft.engine.core.process.instance.model.builder.SSendTaskInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.impl.SSendTaskInstanceBuilderFactoryImpl\norg.bonitasoft.engine.core.process.instance.model.builder.SSubProcessActivityInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.impl.SSubProcessActivityInstanceBuilderFactoryImpl\norg.bonitasoft.engine.core.process.instance.model.builder.SUserTaskInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.impl.SUserTaskInstanceBuilderFactoryImpl\norg.bonitasoft.engine.core.process.instance.model.builder.event.SBoundaryEventInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.event.impl.SBoundaryEventInstanceBuilderFactoryImpl\norg.bonitasoft.engine.core.process.instance.model.builder.event.SEndEventInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.event.impl.SEndEventInstanceBuilderFactoryImpl\norg.bonitasoft.engine.core.process.instance.model.builder.event.SIntermediateCatchEventInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.event.impl.SIntermediateCatchEventInstanceBuilderFactoryImpl\norg.bonitasoft.engine.core.process.instance.model.builder.event.SIntermediateThrowEventInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.event.impl.SIntermediateThrowEventInstanceBuilderFactoryImpl\norg.bonitasoft.engine.core.process.instance.model.builder.event.SStartEventInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.event.impl.SStartEventInstanceBuilderFactoryImpl\n#org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SMessageInstanceLogBuilderFactory = null\norg.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingErrorEventBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.event.handling.impl.SWaitingErrorEventBuilderFactoryImpl\norg.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingEventKeyProviderBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.event.handling.impl.SWaitingEventKeyProviderBuilderFactoryImpl\norg.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingMessageEventBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.event.handling.impl.SWaitingMessageEventBuilderFactoryImpl\norg.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingSignalEventBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.event.handling.impl.SWaitingSignalEventBuilderFactoryImpl\norg.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilderFactory = org.bonitasoft.engine.data.definition.model.builder.impl.SDataDefinitionBuilderFactoryImpl\norg.bonitasoft.engine.core.process.definition.model.builder.SBusinessDataDefinitionBuilderFactory = org.bonitasoft.engine.core.process.definition.model.builder.impl.SBusinessDataDefinitionBuilderFactoryImpl\n#org.bonitasoft.engine.data.definition.model.builder.SEnumationDataDefinitionBuilderFactory = null\n#org.bonitasoft.engine.data.definition.model.builder.STextDataDefinitionBuilderFactory = null\norg.bonitasoft.engine.data.definition.model.builder.SXMLDataDefinitionBuilderFactory = org.bonitasoft.engine.data.definition.model.builder.impl.SXMLDataDefinitionBuilderFactoryImpl\norg.bonitasoft.engine.data.instance.model.archive.builder.SADataInstanceLogBuilderFactory = org.bonitasoft.engine.data.instance.model.archive.builder.impl.SADataInstanceLogBuilderFactoryImpl\norg.bonitasoft.engine.dependency.model.builder.SDependencyLogBuilderFactory = org.bonitasoft.engine.dependency.model.builder.impl.SDependencyLogBuilderFactoryImpl\norg.bonitasoft.engine.dependency.model.builder.SDependencyMappingLogBuilderFactory = org.bonitasoft.engine.dependency.model.builder.impl.SDependencyMappingLogBuilderFactoryImpl\norg.bonitasoft.engine.expression.model.builder.SExpressionBuilderFactory = org.bonitasoft.engine.expression.model.builder.impl.SExpressionBuilderFactoryImpl\norg.bonitasoft.engine.identity.model.builder.SContactInfoLogBuilderFactory = org.bonitasoft.engine.identity.model.builder.impl.SContactInfoLogBuilderFactoryImpl\norg.bonitasoft.engine.identity.model.builder.SContactInfoUpdateBuilderFactory = org.bonitasoft.engine.identity.model.builder.impl.SContactInfoUpdateBuilderFactoryImpl\norg.bonitasoft.engine.identity.model.builder.SGroupLogBuilderFactory = org.bonitasoft.engine.identity.model.builder.impl.SGroupLogBuilderFactoryImpl\norg.bonitasoft.engine.identity.model.builder.SGroupUpdateBuilderFactory = org.bonitasoft.engine.identity.model.builder.impl.SGroupUpdateBuilderFactoryImpl\n#org.bonitasoft.engine.identity.model.builder.SIdentityUpdateBuilderFactory = null\n#org.bonitasoft.engine.identity.model.builder.SMembershipLogBuilderFactory = null\norg.bonitasoft.engine.identity.model.builder.SCustomUserInfoDefinitionLogBuilderFactory = org.bonitasoft.engine.identity.model.builder.impl.SCustomUserInfoDefinitionLogBuilderFactoryImpl\norg.bonitasoft.engine.identity.model.builder.SCustomUserInfoDefinitionUpdateBuilderFactory = org.bonitasoft.engine.identity.model.builder.impl.SCustomUserInfoDefinitionUpdateBuilderFactoryImpl\n#org.bonitasoft.engine.identity.model.builder.SProfileMetadataValueLogBuilderFactory = null\norg.bonitasoft.engine.identity.model.builder.SCustomUserInfoValueUpdateBuilderFactory = org.bonitasoft.engine.identity.model.builder.impl.SCustomUserInfoValueUpdateBuilderFactoryImpl\norg.bonitasoft.engine.identity.model.builder.SRoleLogBuilderFactory = org.bonitasoft.engine.identity.model.builder.impl.SRoleLogBuilderFactoryImpl\norg.bonitasoft.engine.identity.model.builder.SRoleUpdateBuilderFactory = org.bonitasoft.engine.identity.model.builder.impl.SRoleUpdateBuilderFactoryImpl\norg.bonitasoft.engine.identity.model.builder.SUserLogBuilderFactory = org.bonitasoft.engine.identity.model.builder.impl.SUserLogBuilderFactoryImpl\norg.bonitasoft.engine.identity.model.builder.SUserMembershipLogBuilderFactory = org.bonitasoft.engine.identity.model.builder.impl.SUserMembershipLogBuilderFactoryImpl\norg.bonitasoft.engine.identity.model.builder.SUserMembershipUpdateBuilderFactory = org.bonitasoft.engine.identity.model.builder.impl.SUserMembershipUpdateBuilderFactoryImpl\norg.bonitasoft.engine.identity.model.builder.SUserUpdateBuilderFactory = org.bonitasoft.engine.identity.model.builder.impl.SUserUpdateBuilderFactoryImpl\norg.bonitasoft.engine.platform.command.model.SPlatformCommandLogBuilderFactory = org.bonitasoft.engine.platform.command.model.impl.SPlatformCommandLogBuilderFactoryImpl\norg.bonitasoft.engine.platform.command.model.SPlatformCommandUpdateBuilderFactory = org.bonitasoft.engine.platform.command.model.impl.SPlatformCommandUpdateBuilderFactoryImpl\norg.bonitasoft.engine.platform.session.model.builder.SPlatformSessionBuilderFactory = org.bonitasoft.engine.platform.session.model.builder.impl.SPlatformSessionBuilderFactoryImpl\norg.bonitasoft.engine.profile.builder.SProfileMemberUpdateBuilderFactory = org.bonitasoft.engine.profile.builder.impl.SProfileMemberUpdateBuilderFactoryImpl\norg.bonitasoft.engine.profile.builder.SProfileUpdateBuilderFactory = org.bonitasoft.engine.profile.builder.impl.SProfileUpdateBuilderFactoryImpl\norg.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisorLogBuilderFactory = org.bonitasoft.engine.supervisor.mapping.model.impl.SProcessSupervisorLogBuilderFactoryImpl\norg.bonitasoft.engine.dependency.model.builder.SPlatformDependencyLogBuilderFactory = org.bonitasoft.engine.dependency.model.builder.impl.SPlatformDependencyLogBuilderFactoryImpl\norg.bonitasoft.engine.dependency.model.builder.SPlatformDependencyMappingLogBuilderFactory = org.bonitasoft.engine.dependency.model.builder.impl.SPlatformDependencyMappingLogBuilderFactoryImpl\norg.bonitasoft.engine.page.SPageLogBuilderFactory = org.bonitasoft.engine.page.impl.SPageLogBuilderFactoryImpl\norg.bonitasoft.engine.page.SPageUpdateBuilderFactory = org.bonitasoft.engine.page.impl.SPageUpdateBuilderFactoryImpl\n## business data\norg.bonitasoft.engine.core.process.instance.model.builder.business.data.SRefBusinessDataInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.impl.business.data.SRefBusinessDataInstanceBuilderFactoryImpl\norg.bonitasoft.engine.core.process.instance.model.builder.business.data.SRefBusinessDataInstanceLogBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.impl.business.data.SRefBusinessDataInstanceLogBuilderFactoryImpl\norg.bonitasoft.engine.core.process.instance.model.archive.builder.business.data.SARefBusinessDataInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.business.data.SARefBusinessDataInstanceBuilderFactoryImpl\n\n\n##Abstract factories, cannot be instantiated\n##org.bonitasoft.engine.core.process.instance.model.archive.builder.SAActivityInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.SAActivityInstanceBuilderFactoryImpl\n##org.bonitasoft.engine.core.process.instance.model.archive.builder.SAFlowElementInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.SAFlowElementInstanceBuilderFactoryImpl\n##org.bonitasoft.engine.core.process.instance.model.archive.builder.SAFlowNodeInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.SAFlowNodeInstanceBuilderFactoryImpl\n##org.bonitasoft.engine.core.process.instance.model.builder.SActivityInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.impl.SActivityInstanceBuilderFactoryImpl\n##org.bonitasoft.engine.core.process.instance.model.builder.SFlowElementInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.impl.SFlowElementInstanceBuilderFactoryImpl\n##org.bonitasoft.engine.core.process.instance.model.builder.SFlowNodeInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.impl.SFlowNodeInstanceBuilderFactoryImpl\n##org.bonitasoft.engine.core.process.instance.model.builder.SHumanTaskInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.impl.SHumanTaskInstanceBuilderFactoryImpl\n##org.bonitasoft.engine.core.process.instance.model.builder.event.SEventInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.event.impl.SEventInstanceBuilderFactoryImpl\n"
  },
  {
    "path": "services/bonita-business-application/build.gradle",
    "content": "\n\ndependencies {\n    api project(':services:bonita-persistence')\n    api project(':services:bonita-events')\n    api project(':services:bonita-log')\n    api project(':services:bonita-builder')\n    testImplementation libs.mockitoCore\n    testImplementation libs.assertj\n\n    annotationProcessor libs.lombok\n    compileOnly libs.lombok\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/ApplicationService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.business.application.model.SApplication;\nimport org.bonitasoft.engine.business.application.model.SApplicationMenu;\nimport org.bonitasoft.engine.business.application.model.SApplicationPage;\nimport org.bonitasoft.engine.business.application.model.SApplicationWithIcon;\nimport org.bonitasoft.engine.commons.TenantLifecycleService;\nimport org.bonitasoft.engine.commons.exceptions.SObjectAlreadyExistsException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectCreationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface ApplicationService extends TenantLifecycleService {\n\n    String APPLICATION = \"APPLICATION\";\n\n    String APPLICATION_PAGE = \"APPLICATION_PAGE\";\n\n    String APPLICATION_MENU = \"APPLICATION_MENU\";\n\n    String DEFAULT_LAYOUT_NAME = \"custompage_layoutBonita\";\n\n    String DEFAULT_THEME_NAME = \"custompage_themeBonita\";\n\n    SApplicationWithIcon createApplication(SApplicationWithIcon application)\n            throws SObjectCreationException, SObjectAlreadyExistsException;\n\n    SApplication getApplication(long applicationId) throws SBonitaReadException, SObjectNotFoundException;\n\n    SApplicationWithIcon getApplicationWithIcon(long applicationId)\n            throws SBonitaReadException, SObjectNotFoundException;\n\n    SApplication getApplicationByToken(String token) throws SBonitaReadException;\n\n    void deleteApplication(long applicationId) throws SObjectModificationException, SObjectNotFoundException;\n\n    void forceDeleteApplication(SApplication application) throws SObjectModificationException;\n\n    SApplicationWithIcon updateApplication(long applicationId, EntityUpdateDescriptor updateDescriptor)\n            throws SObjectModificationException,\n            SObjectAlreadyExistsException, SObjectNotFoundException;\n\n    SApplicationWithIcon updateApplication(SApplicationWithIcon application,\n            EntityUpdateDescriptor updateDescriptor)\n            throws SObjectModificationException,\n            SObjectAlreadyExistsException;\n\n    long getNumberOfApplications(QueryOptions options) throws SBonitaReadException;\n\n    List<SApplication> searchApplications(QueryOptions options) throws SBonitaReadException;\n\n    SApplicationPage createApplicationPage(SApplicationPage applicationPage)\n            throws SObjectCreationException, SObjectAlreadyExistsException;\n\n    SApplicationPage getApplicationPage(String applicationName, String applicationPageToken)\n            throws SBonitaReadException, SObjectNotFoundException;\n\n    SApplicationPage getApplicationPage(long applicationPageId) throws SBonitaReadException, SObjectNotFoundException;\n\n    SApplicationPage getApplicationHomePage(long applicationId) throws SBonitaReadException, SObjectNotFoundException;\n\n    SApplicationPage deleteApplicationPage(long applicationPageId)\n            throws SObjectModificationException, SObjectNotFoundException;\n\n    void deleteApplicationPage(SApplicationPage applicationPage) throws SObjectModificationException;\n\n    long getNumberOfApplicationPages(final QueryOptions options) throws SBonitaReadException;\n\n    List<SApplicationPage> searchApplicationPages(final QueryOptions options) throws SBonitaReadException;\n\n    SApplicationMenu createApplicationMenu(SApplicationMenu applicationMenu) throws SObjectCreationException;\n\n    SApplicationMenu updateApplicationMenu(long applicationMenuId, EntityUpdateDescriptor updateDescriptor)\n            throws SObjectModificationException,\n            SObjectNotFoundException;\n\n    SApplicationMenu updateApplicationMenu(SApplicationMenu applicationMenu, EntityUpdateDescriptor updateDescriptor,\n            boolean organizeIndexes)\n            throws SObjectModificationException;\n\n    SApplicationMenu getApplicationMenu(long applicationMenuId) throws SBonitaReadException, SObjectNotFoundException;\n\n    SApplicationMenu deleteApplicationMenu(long applicationMenuId)\n            throws SObjectModificationException, SObjectNotFoundException;\n\n    void deleteApplicationMenu(SApplicationMenu applicationMenu) throws SObjectModificationException;\n\n    long getNumberOfApplicationMenus(QueryOptions options) throws SBonitaReadException;\n\n    List<SApplicationMenu> searchApplicationMenus(QueryOptions options) throws SBonitaReadException;\n\n    List<String> getAllPagesForProfile(long profileId) throws SBonitaReadException;\n\n    List<String> getAllPagesForProfile(String profile) throws SBonitaReadException;\n\n    int getNextAvailableIndex(Long parentMenuId) throws SBonitaReadException;\n\n    int getLastUsedIndex(Long parentMenuId) throws SBonitaReadException;\n\n    long getNumberOfApplicationsOfUser(long userId, QueryOptions options) throws SBonitaReadException;\n\n    List<SApplication> searchApplicationsOfUser(long userId, QueryOptions options) throws SBonitaReadException;\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/impl/ApplicationServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.impl;\n\nimport static java.lang.String.format;\nimport static org.bonitasoft.engine.business.application.model.SApplicationWithIcon.ALWAYS_MODIFIABLE_FIELDS;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.impl.cleaner.ApplicationDestructor;\nimport org.bonitasoft.engine.business.application.impl.cleaner.ApplicationMenuCleaner;\nimport org.bonitasoft.engine.business.application.impl.cleaner.ApplicationMenuDestructor;\nimport org.bonitasoft.engine.business.application.impl.cleaner.ApplicationPageDestructor;\nimport org.bonitasoft.engine.business.application.impl.converter.MenuIndexConverter;\nimport org.bonitasoft.engine.business.application.model.AbstractSApplication;\nimport org.bonitasoft.engine.business.application.model.SApplication;\nimport org.bonitasoft.engine.business.application.model.SApplicationMenu;\nimport org.bonitasoft.engine.business.application.model.SApplicationPage;\nimport org.bonitasoft.engine.business.application.model.SApplicationWithIcon;\nimport org.bonitasoft.engine.business.application.model.builder.SApplicationLogBuilder;\nimport org.bonitasoft.engine.business.application.model.builder.SApplicationMenuLogBuilder;\nimport org.bonitasoft.engine.business.application.model.builder.SApplicationPageLogBuilder;\nimport org.bonitasoft.engine.business.application.model.builder.impl.SApplicationLogBuilderImpl;\nimport org.bonitasoft.engine.business.application.model.builder.impl.SApplicationMenuLogBuilderImpl;\nimport org.bonitasoft.engine.business.application.model.builder.impl.SApplicationPageLogBuilderImpl;\nimport org.bonitasoft.engine.cache.CacheService;\nimport org.bonitasoft.engine.cache.SCacheException;\nimport org.bonitasoft.engine.cache.configuration.CacheConfigurationBeans;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectAlreadyExistsException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectCreationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity;\nimport org.bonitasoft.engine.queriablelogger.model.builder.ActionType;\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Service\npublic class ApplicationServiceImpl implements ApplicationService {\n\n    public static final int MAX_RESULTS = 1000;\n    private final Recorder recorder;\n    private final ReadPersistenceService persistenceService;\n    private final QueriableLoggerService queriableLoggerService;\n    private final IndexManager indexManager;\n    private final MenuIndexConverter menuIndexConverter;\n    private final ApplicationDestructor applicationDestructor;\n    private final ApplicationPageDestructor applicationPageDestructor;\n    private final ApplicationMenuDestructor applicationMenuDestructor;\n    private final CacheService cacheService;\n\n    @Autowired\n    public ApplicationServiceImpl(Recorder recorder, ReadPersistenceService persistenceService,\n            final QueriableLoggerService queriableLoggerService, CacheService cacheService) {\n        this.recorder = recorder;\n        this.persistenceService = persistenceService;\n        this.queriableLoggerService = queriableLoggerService;\n        this.cacheService = cacheService;\n        indexManager = new IndexManager(new IndexUpdater(this, MAX_RESULTS), new MenuIndexValidator());\n        menuIndexConverter = new MenuIndexConverter(this);\n        final ApplicationMenuCleaner applicationMenuCleaner = new ApplicationMenuCleaner(this);\n        applicationDestructor = new ApplicationDestructor(applicationMenuCleaner);\n        applicationPageDestructor = new ApplicationPageDestructor(applicationMenuCleaner, new HomePageChecker(this));\n        applicationMenuDestructor = new ApplicationMenuDestructor(applicationMenuCleaner);\n    }\n\n    //Visible for tests only\n    ApplicationServiceImpl(Recorder recorder, ReadPersistenceService persistenceService,\n            QueriableLoggerService queriableLoggerService,\n            IndexManager indexManager,\n            MenuIndexConverter menuIndexConverter,\n            ApplicationDestructor applicationDestructor,\n            ApplicationPageDestructor applicationPageDestructor,\n            ApplicationMenuDestructor applicationMenuDestructor, CacheService cacheService) {\n        this.recorder = recorder;\n        this.persistenceService = persistenceService;\n        this.queriableLoggerService = queriableLoggerService;\n        this.cacheService = cacheService;\n        this.indexManager = indexManager;\n        this.menuIndexConverter = menuIndexConverter;\n        this.applicationDestructor = applicationDestructor;\n        this.applicationPageDestructor = applicationPageDestructor;\n        this.applicationMenuDestructor = applicationMenuDestructor;\n    }\n\n    @Override\n    public SApplicationWithIcon createApplication(final SApplicationWithIcon application)\n            throws SObjectCreationException, SObjectAlreadyExistsException {\n        final String methodName = \"createApplication\";\n        final SApplicationLogBuilder logBuilder = getApplicationLogBuilder(ActionType.CREATED,\n                \"Creating application named \" + application.getToken());\n        try {\n            validateApplication(application);\n            recorder.recordInsert(new InsertRecord(application), APPLICATION);\n            log(application.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName);\n        } catch (final SObjectAlreadyExistsException e) {\n            return logAndRetrowException(application.getId(), methodName, logBuilder, e);\n        } catch (final SBonitaException e) {\n            handleCreationException(application, logBuilder, e, methodName);\n        }\n        return application;\n    }\n\n    private void validateApplication(final SApplicationWithIcon application)\n            throws SBonitaReadException, SObjectAlreadyExistsException {\n        validateApplicationToken(application.getToken());\n\n    }\n\n    private void validateApplicationToken(final String applicationToken)\n            throws SBonitaReadException, SObjectAlreadyExistsException {\n        if (hasApplicationWithToken(applicationToken)) {\n            throw new SObjectAlreadyExistsException(\n                    \"An application already exists with token '\" + applicationToken + \"'.\");\n        }\n    }\n\n    private void handleCreationException(final PersistentObject persistentObject,\n            final SPersistenceLogBuilder logBuilder, final Exception e,\n            final String methodName)\n            throws SObjectCreationException {\n        log(persistentObject.getId(), SQueriableLog.STATUS_FAIL, logBuilder, methodName);\n        throw new SObjectCreationException(e);\n    }\n\n    public boolean hasApplicationWithToken(final String name) throws SBonitaReadException {\n        final SApplication application = getApplicationByToken(name);\n        return application != null;\n    }\n\n    @Override\n    public SApplication getApplicationByToken(final String token) throws SBonitaReadException {\n        try {\n            SApplication application = (SApplication) cacheService.get(\n                    CacheConfigurationBeans.APPLICATION_TOKEN_CACHE_NAME,\n                    token);\n            if (application == null) {\n                application = persistenceService\n                        .selectOne(new SelectOneDescriptor<>(\"getApplicationByToken\", Collections\n                                .singletonMap(\"token\", token), SApplication.class));\n                if (application != null) {\n                    cacheService.store(CacheConfigurationBeans.APPLICATION_TOKEN_CACHE_NAME, token, application);\n                }\n            }\n            return application;\n        } catch (SCacheException e) {\n            throw new SBonitaReadException(e);\n        }\n    }\n\n    private <T extends SLogBuilder & HasCRUDEAction> void initializeLogBuilder(final T logBuilder, final String message,\n            final ActionType actionType) {\n        logBuilder.actionStatus(SQueriableLog.STATUS_FAIL).severity(SQueriableLogSeverity.INTERNAL).rawMessage(message);\n        logBuilder.setActionType(actionType);\n    }\n\n    private SApplicationLogBuilder getApplicationLogBuilder(final ActionType actionType, final String message) {\n        final SApplicationLogBuilder logBuilder = new SApplicationLogBuilderImpl();\n        initializeLogBuilder(logBuilder, message, actionType);\n        return logBuilder;\n    }\n\n    private SApplicationPageLogBuilder getApplicationPageLogBuilder(final ActionType actionType, final String message) {\n        final SApplicationPageLogBuilderImpl logBuilder = new SApplicationPageLogBuilderImpl();\n        initializeLogBuilder(logBuilder, message, actionType);\n        return logBuilder;\n    }\n\n    private SApplicationMenuLogBuilder getApplicationMenuLogBuilder(final ActionType actionType, final String message) {\n        final SApplicationMenuLogBuilderImpl logBuilder = new SApplicationMenuLogBuilderImpl();\n        initializeLogBuilder(logBuilder, message, actionType);\n        return logBuilder;\n    }\n\n    private void log(final long objectId, final int sQueriableLogStatus, final SPersistenceLogBuilder logBuilder,\n            final String methodName) {\n        logBuilder.actionScope(String.valueOf(objectId));\n        logBuilder.actionStatus(sQueriableLogStatus);\n        logBuilder.objectId(objectId);\n        final SQueriableLog log = logBuilder.build();\n        if (queriableLoggerService.isLoggable(log.getActionType(), log.getSeverity())) {\n            queriableLoggerService.log(this.getClass().getName(), methodName, log);\n        }\n    }\n\n    @Override\n    public SApplication getApplication(final long applicationId)\n            throws SBonitaReadException, SObjectNotFoundException {\n        SApplication application = persistenceService\n                .selectById(new SelectByIdDescriptor<>(SApplication.class, applicationId));\n        if (application == null) {\n            throw new SObjectNotFoundException(\"No application found with id '\" + applicationId + \"'.\");\n        }\n        return application;\n    }\n\n    @Override\n    public SApplicationWithIcon getApplicationWithIcon(long applicationId)\n            throws SBonitaReadException, SObjectNotFoundException {\n        SApplicationWithIcon application = persistenceService\n                .selectById(new SelectByIdDescriptor<>(SApplicationWithIcon.class, applicationId));\n        if (application == null) {\n            throw new SObjectNotFoundException(\"No application found with id '\" + applicationId + \"'.\");\n        }\n        return application;\n    }\n\n    @Override\n    public void forceDeleteApplication(SApplication application) throws SObjectModificationException {\n        try {\n            deleteApplication(application);\n        } catch (final SBonitaException e) {\n            throw new SObjectModificationException(e);\n        }\n    }\n\n    @Override\n    public void deleteApplication(final long applicationId)\n            throws SObjectModificationException, SObjectNotFoundException {\n        try {\n            final SApplication application = getApplication(applicationId);\n            if (!application.isEditable()) {\n                throw new SObjectModificationException(format(\n                        \"The application '%s' is set as non modifiable. It cannot be deleted.\",\n                        application.getDisplayName()));\n            }\n            deleteApplication(application);\n        } catch (final SObjectNotFoundException e) {\n            throw e;\n        } catch (final SBonitaException e) {\n            throw new SObjectModificationException(e);\n        }\n    }\n\n    private void deleteApplication(SApplication application) throws SBonitaException {\n        if (application.getToken() != null) {\n            cacheService.remove(CacheConfigurationBeans.APPLICATION_TOKEN_CACHE_NAME, application.getToken());\n        }\n        applicationDestructor.onDeleteApplication(application);\n        recorder.recordDelete(new DeleteRecord(application), APPLICATION);\n        log(application.getId(), SQueriableLog.STATUS_OK, getApplicationLogBuilder(ActionType.DELETED,\n                \"Deleting application with id \" + application.getId()), \"deleteApplication\");\n    }\n\n    @Override\n    public SApplicationWithIcon updateApplication(final long applicationId,\n            final EntityUpdateDescriptor updateDescriptor)\n            throws SObjectModificationException, SObjectAlreadyExistsException, SObjectNotFoundException {\n        final String methodName = \"updateApplication\";\n        final SApplicationLogBuilder logBuilder = getApplicationLogBuilder(ActionType.UPDATED,\n                \"Updating application with id \" + applicationId);\n\n        try {\n            final SApplicationWithIcon application = getApplicationWithIcon(applicationId);\n            if (!application.isEditable()) {\n                for (String field : updateDescriptor.getFields().keySet()) {\n                    if (!ALWAYS_MODIFIABLE_FIELDS.contains(field)) {\n                        throw new SObjectModificationException(\"The application is provided.\" +\n                                \" Only the theme, the layout, and the icon can be updated.\");\n                    }\n                }\n            }\n            verifyNewHomePageExists(updateDescriptor);\n            return updateApplication(application, updateDescriptor);\n        } catch (final SObjectNotFoundException | SObjectAlreadyExistsException | SObjectModificationException e) {\n            throw e;\n        } catch (final SBonitaException e) {\n            log(applicationId, SQueriableLog.STATUS_FAIL, logBuilder, methodName);\n            throw new SObjectModificationException(e);\n        }\n    }\n\n    private void verifyNewHomePageExists(final EntityUpdateDescriptor updateDescriptor)\n            throws SBonitaReadException, SObjectModificationException {\n        final Long homePageId = (Long) updateDescriptor.getFields().get(AbstractSApplication.HOME_PAGE_ID);\n        if (homePageId != null) {\n            final SApplicationPage applicationPage = executeGetApplicationPageById(homePageId);\n            if (applicationPage == null) {\n                throw new SObjectModificationException(\n                        \"Invalid home page id: no application page found with id '\" + homePageId + \"'\");\n            }\n        }\n    }\n\n    @Override\n    public SApplicationWithIcon updateApplication(final SApplicationWithIcon application,\n            final EntityUpdateDescriptor updateDescriptor)\n            throws SObjectModificationException, SObjectAlreadyExistsException {\n        final String methodName = \"updateApplication\";\n        final long now = System.currentTimeMillis();\n        final SApplicationLogBuilder logBuilder = getApplicationLogBuilder(ActionType.UPDATED,\n                \"Updating application with id \" + application.getId());\n        try {\n            validateUpdatedFields(updateDescriptor, application);\n            if (application.getToken() != null) {\n                cacheService.remove(CacheConfigurationBeans.APPLICATION_TOKEN_CACHE_NAME, application.getToken());\n            }\n            updateDescriptor.addField(AbstractSApplication.LAST_UPDATE_DATE, now);\n\n            recorder.recordUpdate(UpdateRecord.buildSetFields(application,\n                    updateDescriptor), APPLICATION);\n            log(application.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName);\n            return application;\n        } catch (final SObjectAlreadyExistsException e) {\n            return logAndRetrowException(application.getId(), methodName, logBuilder, e);\n        } catch (final SBonitaException e) {\n            log(application.getId(), SQueriableLog.STATUS_FAIL, logBuilder, methodName);\n            throw new SObjectModificationException(e);\n        }\n    }\n\n    private <T, E extends SBonitaException> T logAndRetrowException(final long objectId, final String methodName,\n            final SPersistenceLogBuilder logBuilder, final E e) throws E {\n        log(objectId, SQueriableLog.STATUS_FAIL, logBuilder, methodName);\n        throw e;\n    }\n\n    private void validateUpdatedFields(final EntityUpdateDescriptor updateDescriptor,\n            final SApplicationWithIcon application)\n            throws SBonitaReadException, SObjectAlreadyExistsException, SObjectModificationException {\n        if (updateDescriptor.getFields().containsKey(AbstractSApplication.TOKEN)\n                && !application.getToken().equals(updateDescriptor.getFields().get(AbstractSApplication.TOKEN))) {\n            validateApplicationToken((String) updateDescriptor.getFields().get(AbstractSApplication.TOKEN));\n        }\n        if (updateDescriptor.getFields().containsKey(AbstractSApplication.LINK)\n                && !Boolean.valueOf(application.isLink())\n                        .equals(updateDescriptor.getFields().get(AbstractSApplication.LINK))) {\n            throw new SObjectModificationException(format(\n                    \"The 'link' nature of application '%s' is not modifiable. You can not switch between legacy and application links.\",\n                    application.getDisplayName()));\n        }\n    }\n\n    @Override\n    public long getNumberOfApplications(final QueryOptions options) throws SBonitaReadException {\n        return persistenceService.getNumberOfEntities(SApplication.class, options, null);\n    }\n\n    @Override\n    public long getNumberOfApplicationsOfUser(long userId, QueryOptions options) throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"userId\", userId);\n        return persistenceService.getNumberOfEntities(SApplication.class, \"OfUser\", options, parameters);\n    }\n\n    @Override\n    public List<SApplication> searchApplications(final QueryOptions options) throws SBonitaReadException {\n        return persistenceService.searchEntity(SApplication.class, options, null);\n    }\n\n    @Override\n    public List<SApplication> searchApplicationsOfUser(long userId, QueryOptions options) throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"userId\", userId);\n        return persistenceService.searchEntity(SApplication.class, \"OfUser\", options, parameters);\n    }\n\n    @Override\n    public SApplicationPage createApplicationPage(final SApplicationPage applicationPage)\n            throws SObjectCreationException, SObjectAlreadyExistsException {\n        final String methodName = \"createApplicationPage\";\n        final SApplicationPageLogBuilder logBuilder = getApplicationPageLogBuilder(ActionType.CREATED,\n                \"Creating application page with token \" + applicationPage.getToken());\n        try {\n            validateApplicationPage(applicationPage);\n            recorder.recordInsert(new InsertRecord(applicationPage), APPLICATION_PAGE);\n            log(applicationPage.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName);\n        } catch (final SObjectAlreadyExistsException e) {\n            return logAndRetrowException(applicationPage.getId(), methodName, logBuilder, e);\n        } catch (final SBonitaException e) {\n            handleCreationException(applicationPage, logBuilder, e, methodName);\n        }\n        return applicationPage;\n    }\n\n    private void validateApplicationPage(final SApplicationPage applicationPage)\n            throws SBonitaReadException, SObjectAlreadyExistsException {\n        final String applicationPageToken = applicationPage.getToken();\n        if (hasApplicationPage(applicationPage.getApplicationId(), applicationPageToken)) {\n            String stb = \"An application page with token '\" + applicationPageToken +\n                    \"' already exists for the application with id '\" + applicationPage.getApplicationId() + \"'\";\n            throw new SObjectAlreadyExistsException(stb);\n        }\n    }\n\n    private boolean hasApplicationPage(final long applicationId, final String name) throws SBonitaReadException {\n        return getApplicationPage(applicationId, name) != null;\n    }\n\n    public SApplicationPage getApplicationPage(final long applicationId, final String applicationPageToken)\n            throws SBonitaReadException {\n        final Map<String, Object> inputParameters = new HashMap<>(2);\n        inputParameters.put(\"applicationId\", applicationId);\n        inputParameters.put(\"applicationPageToken\", applicationPageToken);\n        return persistenceService.selectOne(new SelectOneDescriptor<>(\"getApplicationPageByTokenAndApplicationId\",\n                inputParameters, SApplicationPage.class));\n    }\n\n    @Override\n    public SApplicationPage getApplicationPage(final String applicationToken, final String applicationPageToken)\n            throws SBonitaReadException, SObjectNotFoundException {\n        final Map<String, Object> inputParameters = new HashMap<>(2);\n        inputParameters.put(\"applicationToken\", applicationToken);\n        inputParameters.put(\"applicationPageToken\", applicationPageToken);\n        final SApplicationPage applicationPage = persistenceService\n                .selectOne(new SelectOneDescriptor<>(\"getApplicationPageByTokenAndApplicationToken\", inputParameters,\n                        SApplicationPage.class));\n        if (applicationPage == null) {\n            throw new SObjectNotFoundException(\"No application page found with name '\" + applicationPageToken\n                    + \"' and application token '\" + applicationToken + \"'.\");\n        }\n        return applicationPage;\n    }\n\n    @Override\n    public SApplicationPage getApplicationPage(final long applicationPageId)\n            throws SBonitaReadException, SObjectNotFoundException {\n        final SApplicationPage applicationPage = executeGetApplicationPageById(applicationPageId);\n        if (applicationPage == null) {\n            throw new SObjectNotFoundException(\"No application page found with id '\" + applicationPageId + \"'.\");\n        }\n        return applicationPage;\n    }\n\n    private SApplicationPage executeGetApplicationPageById(final long applicationPageId) throws SBonitaReadException {\n        return persistenceService\n                .selectById(new SelectByIdDescriptor<>(SApplicationPage.class, applicationPageId));\n    }\n\n    @Override\n    public SApplicationPage deleteApplicationPage(final long applicationPageId)\n            throws SObjectModificationException, SObjectNotFoundException {\n        final String methodName = \"deleteApplicationPage\";\n        final SApplicationPageLogBuilder logBuilder = getApplicationPageLogBuilder(ActionType.DELETED,\n                \"Deleting application page with id \"\n                        + applicationPageId);\n        SApplicationPage applicationPage = null;\n        try {\n            applicationPage = getApplicationPage(applicationPageId);\n            deleteApplicationPage(applicationPage);\n        } catch (final SObjectNotFoundException e) {\n            logAndRetrowException(applicationPageId, methodName, logBuilder, e);\n        } catch (final SObjectModificationException e) {\n            throw e;\n        } catch (final SBonitaException e) {\n            throw new SObjectModificationException(e);\n        }\n        return applicationPage;\n    }\n\n    @Override\n    public void deleteApplicationPage(final SApplicationPage applicationPage) throws SObjectModificationException {\n        final String methodName = \"deleteApplicationPage\";\n        final SApplicationPageLogBuilder logBuilder = getApplicationPageLogBuilder(ActionType.DELETED,\n                \"Deleting application page with id \" + applicationPage.getId());\n        try {\n            applicationPageDestructor.onDeleteApplicationPage(applicationPage);\n            recorder.recordDelete(new DeleteRecord(applicationPage), APPLICATION_PAGE);\n            log(applicationPage.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName);\n        } catch (final SObjectModificationException e) {\n            logAndRetrowException(applicationPage.getId(), methodName, logBuilder, e);\n        } catch (final SBonitaException e) {\n            throw new SObjectModificationException(e);\n        }\n    }\n\n    @Override\n    public SApplicationPage getApplicationHomePage(final long applicationId)\n            throws SBonitaReadException, SObjectNotFoundException {\n        final Map<String, Object> inputParameters = new HashMap<>(2);\n        inputParameters.put(\"applicationId\", applicationId);\n        final SApplicationPage applicationPage = persistenceService\n                .selectOne(\n                        new SelectOneDescriptor<>(\"getApplicationHomePage\", inputParameters, SApplicationPage.class));\n        if (applicationPage == null) {\n            throw new SObjectNotFoundException(\"No home page found for application with id '\" + applicationId + \"'.\");\n        }\n        return applicationPage;\n    }\n\n    @Override\n    public long getNumberOfApplicationPages(final QueryOptions options) throws SBonitaReadException {\n        return persistenceService.getNumberOfEntities(SApplicationPage.class, options, null);\n    }\n\n    @Override\n    public List<SApplicationPage> searchApplicationPages(final QueryOptions options) throws SBonitaReadException {\n        return persistenceService.searchEntity(SApplicationPage.class, options, null);\n    }\n\n    @Override\n    public SApplicationMenu createApplicationMenu(final SApplicationMenu applicationMenu)\n            throws SObjectCreationException {\n        final String methodName = \"createApplicationMenu\";\n        final SApplicationMenuLogBuilder logBuilder = getApplicationMenuLogBuilder(ActionType.CREATED,\n                \"Creating application menu with display name \" + applicationMenu.getDisplayName());\n        try {\n            recorder.recordInsert(new InsertRecord(applicationMenu), APPLICATION_MENU);\n            log(applicationMenu.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName);\n        } catch (final SRecorderException e) {\n            handleCreationException(applicationMenu, logBuilder, e, methodName);\n        }\n        return applicationMenu;\n    }\n\n    @Override\n    public SApplicationMenu updateApplicationMenu(final long applicationMenuId,\n            final EntityUpdateDescriptor updateDescriptor)\n            throws SObjectModificationException, SObjectNotFoundException {\n        final String methodName = \"updateApplicationMenu\";\n        final SApplicationMenuLogBuilder logBuilder = getApplicationMenuLogBuilder(ActionType.UPDATED,\n                \"Updating application menu with id \" + applicationMenuId);\n        try {\n            final SApplicationMenu applicationMenu = getApplicationMenu(applicationMenuId);\n            updateApplicationMenu(applicationMenu, updateDescriptor, true);\n            return applicationMenu;\n        } catch (final SObjectNotFoundException e) {\n            return logAndRetrowException(applicationMenuId, methodName, logBuilder, e);\n        } catch (final SBonitaReadException e) {\n            throw new SObjectModificationException(e);\n        }\n    }\n\n    @Override\n    public SApplicationMenu updateApplicationMenu(final SApplicationMenu applicationMenu,\n            final EntityUpdateDescriptor updateDescriptor, final boolean organizeIndexes)\n            throws SObjectModificationException {\n        try {\n            organizeIndexesOnUpdate(applicationMenu, updateDescriptor, organizeIndexes);\n            recorder.recordUpdate(UpdateRecord.buildSetFields(applicationMenu, updateDescriptor), APPLICATION_MENU);\n\n            log(applicationMenu.getId(), SQueriableLog.STATUS_OK, getApplicationMenuLogBuilder(ActionType.UPDATED,\n                    \"Updating application menu with id \" + applicationMenu.getId()), \"updateApplicationMenu\");\n\n            return applicationMenu;\n        } catch (final SBonitaException e) {\n            throw new SObjectModificationException(e);\n        }\n    }\n\n    private void organizeIndexesOnUpdate(final SApplicationMenu applicationMenu,\n            final EntityUpdateDescriptor updateDescriptor, final boolean organizeIndexes)\n            throws SObjectModificationException, SBonitaReadException {\n        final Map<String, Object> fields = updateDescriptor.getFields();\n        if (fields.containsKey(SApplicationMenu.PARENT_ID) && !fields.containsKey(SApplicationMenu.INDEX)) {\n            //we need to force the update of index, as it has change of parent\n            fields.put(SApplicationMenu.INDEX, getNextAvailableIndex((Long) fields.get(SApplicationMenu.PARENT_ID)));\n        }\n        final Integer newIndexValue = (Integer) fields.get(SApplicationMenu.INDEX);\n        if (newIndexValue != null && organizeIndexes) {\n            final MenuIndex oldIndex = menuIndexConverter.toMenuIndex(applicationMenu);\n            final MenuIndex newIndex = menuIndexConverter.toMenuIndex(applicationMenu, updateDescriptor);\n            indexManager.organizeIndexesOnUpdate(oldIndex, newIndex);\n        }\n    }\n\n    @Override\n    public SApplicationMenu getApplicationMenu(final long applicationMenuId)\n            throws SBonitaReadException, SObjectNotFoundException {\n        final SApplicationMenu applicationMenu = persistenceService\n                .selectById(new SelectByIdDescriptor<>(SApplicationMenu.class, applicationMenuId));\n        if (applicationMenu == null) {\n            throw new SObjectNotFoundException(\"No application found with id '\" + applicationMenuId + \"'.\");\n        }\n        return applicationMenu;\n    }\n\n    @Override\n    public SApplicationMenu deleteApplicationMenu(final long applicationMenuId)\n            throws SObjectModificationException, SObjectNotFoundException {\n        final String methodName = \"deleteApplicationMenu\";\n        final SApplicationMenuLogBuilder logBuilder = getApplicationMenuLogBuilder(ActionType.DELETED,\n                \"Deleting application menu with id \" + applicationMenuId);\n        SApplicationMenu applicationMenu = null;\n        try {\n            applicationMenu = getApplicationMenu(applicationMenuId);\n            deleteApplicationMenu(applicationMenu);\n            log(applicationMenu.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName);\n        } catch (final SObjectNotFoundException e) {\n            logAndRetrowException(applicationMenuId, methodName, logBuilder, e);\n        } catch (final SBonitaReadException e) {\n            throw new SObjectModificationException(e);\n        }\n        return applicationMenu;\n    }\n\n    @Override\n    public void deleteApplicationMenu(final SApplicationMenu applicationMenu) throws SObjectModificationException {\n        try {\n            applicationMenuDestructor.onDeleteApplicationMenu(applicationMenu);\n            final int lastUsedIndex = getLastUsedIndex(applicationMenu.getParentId());\n            indexManager.organizeIndexesOnDelete(\n                    new MenuIndex(applicationMenu.getParentId(), applicationMenu.getIndex(), lastUsedIndex));\n            recorder.recordDelete(new DeleteRecord(applicationMenu), APPLICATION_MENU);\n        } catch (final SBonitaException e) {\n            throw new SObjectModificationException(e);\n        }\n    }\n\n    @Override\n    public long getNumberOfApplicationMenus(final QueryOptions options) throws SBonitaReadException {\n        return persistenceService.getNumberOfEntities(SApplicationMenu.class, options, null);\n    }\n\n    @Override\n    public List<SApplicationMenu> searchApplicationMenus(final QueryOptions options) throws SBonitaReadException {\n        return persistenceService.searchEntity(SApplicationMenu.class, options, null);\n    }\n\n    @Override\n    public int getNextAvailableIndex(final Long parentMenuId) throws SBonitaReadException {\n        final int lastIndex = getLastUsedIndex(parentMenuId);\n        return lastIndex + 1;\n    }\n\n    @Override\n    public List<String> getAllPagesForProfile(final long profileId) throws SBonitaReadException {\n        final SelectListDescriptor<String> selectList = new SelectListDescriptor<>(\"getAllPagesForProfile\",\n                Collections.singletonMap(\"profileId\", profileId),\n                SApplicationPage.class, new QueryOptions(0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS));\n        return persistenceService.selectList(selectList);\n    }\n\n    @Override\n    public List<String> getAllPagesForProfile(String profile) throws SBonitaReadException {\n        final SelectListDescriptor<String> selectList = new SelectListDescriptor<>(\"getAllPagesForProfileName\",\n                Collections.singletonMap(\"profileName\", profile),\n                SApplicationPage.class, new QueryOptions(0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS));\n        return persistenceService.selectList(selectList);\n    }\n\n    protected Integer executeGetLastUsedIndexQuery(final Long parentMenuId) throws SBonitaReadException {\n        SelectOneDescriptor<Integer> selectDescriptor;\n        if (parentMenuId == null) {\n            selectDescriptor = new SelectOneDescriptor<>(\"getLastIndexForRootMenu\", Collections.emptyMap(),\n                    SApplicationMenu.class);\n        } else {\n            selectDescriptor = new SelectOneDescriptor<>(\"getLastIndexForChildOf\",\n                    Collections.singletonMap(SApplicationMenu.PARENT_ID, parentMenuId),\n                    SApplicationMenu.class);\n        }\n        return persistenceService.selectOne(selectDescriptor);\n    }\n\n    @Override\n    public int getLastUsedIndex(final Long parentMenuId) throws SBonitaReadException {\n        final Integer lastUsedIndex = executeGetLastUsedIndexQuery(parentMenuId);\n        return lastUsedIndex == null ? 0 : lastUsedIndex;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/impl/HomePageChecker.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.impl;\n\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.model.SApplication;\nimport org.bonitasoft.engine.business.application.model.SApplicationPage;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class HomePageChecker {\n\n    private ApplicationService applicationService;\n\n    public HomePageChecker(ApplicationService applicationService) {\n        this.applicationService = applicationService;\n    }\n\n    public boolean isHomePage(SApplicationPage applicationPage) throws SBonitaReadException, SObjectNotFoundException {\n        SApplication application = applicationService.getApplication(applicationPage.getApplicationId());\n        return application.getHomePageId() != null && applicationPage.getId() == application.getHomePageId();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/impl/IndexManager.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.impl;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class IndexManager {\n\n    private IndexUpdater updater;\n    private MenuIndexValidator validator;\n\n    public IndexManager(IndexUpdater updater, MenuIndexValidator validator) {\n        this.updater = updater;\n        this.validator = validator;\n    }\n\n    public void organizeIndexesOnDelete(MenuIndex deletedMenuIndex)\n            throws SBonitaReadException, SObjectModificationException {\n        updater.decrementIndexes(deletedMenuIndex.getParentId(), deletedMenuIndex.getValue() + 1,\n                deletedMenuIndex.getLastUsedIndex());\n    }\n\n    public void organizeIndexesOnUpdate(MenuIndex oldIndex, MenuIndex newIndex)\n            throws SBonitaReadException, SObjectModificationException {\n        validateNewIndex(oldIndex, newIndex);\n        if (oldIndex.getParentId() == newIndex.getParentId()) {\n            if (newIndex.getValue() < oldIndex.getValue()) {\n                updater.incrementIndexes(oldIndex.getParentId(), newIndex.getValue(), oldIndex.getValue() - 1);\n            } else {\n                updater.decrementIndexes(oldIndex.getParentId(), oldIndex.getValue() + 1, newIndex.getValue());\n            }\n        } else {\n            updater.incrementIndexes(newIndex.getParentId(), newIndex.getValue(), newIndex.getLastUsedIndex());\n            updater.decrementIndexes(oldIndex.getParentId(), oldIndex.getValue() + 1, oldIndex.getLastUsedIndex());\n        }\n    }\n\n    private void validateNewIndex(MenuIndex oldIndex, MenuIndex newIndex) throws SObjectModificationException {\n        List<String> validationProblems = validator.validate(oldIndex, newIndex);\n        if (!validationProblems.isEmpty()) {\n            throw new SObjectModificationException(validationProblems.toString());\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/impl/IndexUpdater.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.impl;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.model.SApplicationMenu;\nimport org.bonitasoft.engine.business.application.model.builder.SApplicationMenuUpdateBuilder;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class IndexUpdater {\n\n    private ApplicationService applicationService;\n    private int maxResults;\n\n    public IndexUpdater(ApplicationService applicationService, int maxResults) {\n        this.applicationService = applicationService;\n        this.maxResults = maxResults;\n    }\n\n    public void incrementIndexes(Long parentId, int from, int to)\n            throws SBonitaReadException, SObjectModificationException {\n        updateIndexes(parentId, from, to, 1);\n    }\n\n    public void decrementIndexes(Long parentId, int from, int to)\n            throws SBonitaReadException, SObjectModificationException {\n        updateIndexes(parentId, from, to, -1);\n    }\n\n    private void updateIndexes(Long parentId, int from, int to, int offSet)\n            throws SObjectModificationException, SBonitaReadException {\n        if (to >= from) {\n            List<SApplicationMenu> menusToUpdate = null;\n            int firstResult = 0;\n            do {\n                menusToUpdate = getCurrentPage(parentId, from, to, firstResult);\n                firstResult += maxResults;\n                updateIndexes(menusToUpdate, offSet);\n            } while (menusToUpdate.size() == maxResults);\n        }\n    }\n\n    private List<SApplicationMenu> getCurrentPage(Long parentId, int from, int to, int firstResult)\n            throws SBonitaReadException {\n        List<OrderByOption> orderBy = Collections\n                .singletonList(new OrderByOption(SApplicationMenu.class, SApplicationMenu.INDEX, OrderByType.ASC));\n        List<FilterOption> filters = Arrays\n                .asList(new FilterOption(SApplicationMenu.class, SApplicationMenu.INDEX, from, to), new FilterOption(\n                        SApplicationMenu.class, SApplicationMenu.PARENT_ID, parentId));\n        QueryOptions options = new QueryOptions(firstResult, maxResults, orderBy, filters, null);\n        return applicationService.searchApplicationMenus(options);\n    }\n\n    private void updateIndexes(List<SApplicationMenu> menusToUpdate, int offSet) throws SObjectModificationException {\n        for (SApplicationMenu menuToUpdate : menusToUpdate) {\n            applicationService.updateApplicationMenu(menuToUpdate,\n                    new SApplicationMenuUpdateBuilder().updateIndex(menuToUpdate.getIndex() + offSet).done(), false);\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/impl/MenuIndex.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.impl;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class MenuIndex {\n\n    private int value;\n\n    private Long parentId;\n\n    private int lastUsedIndex;\n\n    public MenuIndex(Long parentId, int value, int lastUsedIndex) {\n        this.value = value;\n        this.parentId = parentId;\n        this.lastUsedIndex = lastUsedIndex;\n    }\n\n    public int getValue() {\n        return value;\n    }\n\n    public Long getParentId() {\n        return parentId;\n    }\n\n    public int getLastUsedIndex() {\n        return lastUsedIndex;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (!(o instanceof MenuIndex menuIndex))\n            return false;\n\n        if (lastUsedIndex != menuIndex.lastUsedIndex)\n            return false;\n        if (value != menuIndex.value)\n            return false;\n        if (parentId != null ? !parentId.equals(menuIndex.parentId) : menuIndex.parentId != null)\n            return false;\n\n        return true;\n    }\n\n    @Override\n    public int hashCode() {\n        int result = value;\n        result = 31 * result + (parentId != null ? parentId.hashCode() : 0);\n        result = 31 * result + lastUsedIndex;\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/impl/MenuIndexValidator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.impl;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class MenuIndexValidator {\n\n    public List<String> validate(MenuIndex oldIndex, MenuIndex newIndex) {\n        final List<String> problems = new ArrayList<String>(1);\n        int lastValidIndex = getLastValidIndex(oldIndex, newIndex);\n        if (newIndex.getValue() < 1 || newIndex.getValue() > lastValidIndex) {\n            problems.add(new StringBuilder().append(\"Invalid menu index: \").append(newIndex.getValue())\n                    .append(\". It must be between 1 and the number of menu in your application having the same parent. The last valid index for parent \")\n                    .append(newIndex.getParentId()).append(\" is \").append(lastValidIndex).toString());\n        }\n        return problems;\n    }\n\n    private int getLastValidIndex(MenuIndex oldIndex, MenuIndex newIndex) {\n        int lastValidIndex = newIndex.getLastUsedIndex();\n        if (oldIndex.getParentId() != newIndex.getParentId()) {\n            // a new element will be added in this parent\n            lastValidIndex = newIndex.getLastUsedIndex() + 1;\n        }\n        return lastValidIndex;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/impl/cleaner/ApplicationDestructor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.impl.cleaner;\n\nimport org.bonitasoft.engine.business.application.impl.ApplicationServiceImpl;\nimport org.bonitasoft.engine.business.application.impl.filter.ApplicationRelatedMenusFilterBuilder;\nimport org.bonitasoft.engine.business.application.impl.filter.SelectRange;\nimport org.bonitasoft.engine.business.application.model.SApplication;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ApplicationDestructor {\n\n    private ApplicationMenuCleaner applicationMenuCleaner;\n\n    public ApplicationDestructor(ApplicationMenuCleaner applicationMenuCleaner) {\n        this.applicationMenuCleaner = applicationMenuCleaner;\n    }\n\n    public void onDeleteApplication(SApplication application) throws SBonitaException {\n        applicationMenuCleaner.deleteRelatedApplicationMenus(\n                new ApplicationRelatedMenusFilterBuilder(new SelectRange(0, ApplicationServiceImpl.MAX_RESULTS),\n                        application.getId()));\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/impl/cleaner/ApplicationMenuCleaner.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.impl.cleaner;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.impl.filter.FilterBuilder;\nimport org.bonitasoft.engine.business.application.model.SApplicationMenu;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.persistence.QueryOptions;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ApplicationMenuCleaner {\n\n    private ApplicationService applicationService;\n\n    public ApplicationMenuCleaner(ApplicationService applicationService) {\n        this.applicationService = applicationService;\n    }\n\n    public void deleteRelatedApplicationMenus(FilterBuilder filterBuilder) throws SBonitaException {\n        QueryOptions options = filterBuilder.buildQueryOptions();\n        List<SApplicationMenu> relatedMenus;\n        do {\n            relatedMenus = applicationService.searchApplicationMenus(options);\n            for (SApplicationMenu relatedMenu : relatedMenus) {\n                applicationService.deleteApplicationMenu(relatedMenu);\n            }\n        } while (relatedMenus.size() == options.getNumberOfResults());\n\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/impl/cleaner/ApplicationMenuDestructor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.impl.cleaner;\n\nimport org.bonitasoft.engine.business.application.impl.ApplicationServiceImpl;\nimport org.bonitasoft.engine.business.application.impl.filter.ChildrenMenusFilterBuilder;\nimport org.bonitasoft.engine.business.application.impl.filter.SelectRange;\nimport org.bonitasoft.engine.business.application.model.SApplicationMenu;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ApplicationMenuDestructor {\n\n    private ApplicationMenuCleaner applicationMenuCleaner;\n\n    public ApplicationMenuDestructor(ApplicationMenuCleaner applicationMenuCleaner) {\n        this.applicationMenuCleaner = applicationMenuCleaner;\n    }\n\n    public void onDeleteApplicationMenu(SApplicationMenu applicationMenu) throws SBonitaException {\n        applicationMenuCleaner.deleteRelatedApplicationMenus(new ChildrenMenusFilterBuilder(\n                new SelectRange(0, ApplicationServiceImpl.MAX_RESULTS), applicationMenu.getId()));\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/impl/cleaner/ApplicationPageDestructor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.impl.cleaner;\n\nimport org.bonitasoft.engine.business.application.impl.ApplicationServiceImpl;\nimport org.bonitasoft.engine.business.application.impl.HomePageChecker;\nimport org.bonitasoft.engine.business.application.impl.filter.ApplicationPageRelatedMenusFilterBuilder;\nimport org.bonitasoft.engine.business.application.impl.filter.SelectRange;\nimport org.bonitasoft.engine.business.application.model.SApplicationPage;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ApplicationPageDestructor {\n\n    private ApplicationMenuCleaner applicationMenuCleaner;\n    private HomePageChecker homePageChecker;\n\n    public ApplicationPageDestructor(ApplicationMenuCleaner applicationMenuCleaner, HomePageChecker homePageChecker) {\n        this.applicationMenuCleaner = applicationMenuCleaner;\n        this.homePageChecker = homePageChecker;\n    }\n\n    public void onDeleteApplicationPage(SApplicationPage applicationPage) throws SBonitaException {\n        verifyIfIsHomePage(applicationPage);\n        applicationMenuCleaner.deleteRelatedApplicationMenus(new ApplicationPageRelatedMenusFilterBuilder(\n                new SelectRange(0, ApplicationServiceImpl.MAX_RESULTS), applicationPage.getId()));\n    }\n\n    private void verifyIfIsHomePage(SApplicationPage applicationPage)\n            throws SBonitaReadException, SObjectNotFoundException, SObjectModificationException {\n        if (homePageChecker.isHomePage(applicationPage)) {\n            throw new SObjectModificationException(\"The application page with id '\" + applicationPage.getId()\n                    + \"' cannot be deleted because it is set as the application home page\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/impl/converter/MenuIndexConverter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.impl.converter;\n\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.impl.MenuIndex;\nimport org.bonitasoft.engine.business.application.model.SApplicationMenu;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class MenuIndexConverter {\n\n    private ApplicationService applicationService;\n\n    public MenuIndexConverter(ApplicationService applicationService) {\n        this.applicationService = applicationService;\n    }\n\n    public MenuIndex toMenuIndex(SApplicationMenu appMenu) throws SBonitaReadException {\n        int lastUsedIndex = applicationService.getLastUsedIndex(appMenu.getParentId());\n        return new MenuIndex(appMenu.getParentId(), appMenu.getIndex(), lastUsedIndex);\n    }\n\n    public MenuIndex toMenuIndex(SApplicationMenu oldAppMenu, EntityUpdateDescriptor updateDescriptor)\n            throws SBonitaReadException {\n        Long parentId = getParentId(oldAppMenu, updateDescriptor);\n        Integer indexValue = getIndexValue(oldAppMenu, updateDescriptor);\n        int lastUsedIndex = applicationService.getLastUsedIndex(parentId);\n        MenuIndex menuIndex = new MenuIndex(parentId, indexValue, lastUsedIndex);\n        return menuIndex;\n    }\n\n    private Integer getIndexValue(SApplicationMenu oldAppMenu, EntityUpdateDescriptor updateDescriptor) {\n        Integer indexValue;\n        indexValue = (Integer) updateDescriptor.getFields().get(SApplicationMenu.INDEX);\n        if (indexValue == null) {\n            indexValue = oldAppMenu.getIndex();\n        }\n        return indexValue;\n    }\n\n    private Long getParentId(SApplicationMenu oldAppMenu, EntityUpdateDescriptor updateDescriptor) {\n        Long parentId;\n        if (updateDescriptor.getFields().containsKey(SApplicationMenu.PARENT_ID)) {\n            parentId = (Long) updateDescriptor.getFields().get(SApplicationMenu.PARENT_ID);\n        } else {\n            parentId = oldAppMenu.getParentId();\n        }\n        return parentId;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/impl/filter/ApplicationPageRelatedMenusFilterBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.impl.filter;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.business.application.model.SApplicationMenu;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ApplicationPageRelatedMenusFilterBuilder implements FilterBuilder {\n\n    private SelectRange range;\n    private long applicationPageId;\n\n    public ApplicationPageRelatedMenusFilterBuilder(SelectRange range, long applicationPageId) {\n        this.range = range;\n        this.applicationPageId = applicationPageId;\n    }\n\n    @Override\n    public QueryOptions buildQueryOptions() {\n        List<OrderByOption> orderByOptions = Collections\n                .singletonList(new OrderByOption(SApplicationMenu.class, SApplicationMenu.ID, OrderByType.ASC));\n        List<FilterOption> filters = Collections.singletonList(\n                new FilterOption(SApplicationMenu.class, SApplicationMenu.APPLICATION_PAGE_ID, applicationPageId));\n        return new QueryOptions(range.getStartIndex(), range.getMaxResults(), orderByOptions, filters, null);\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (!(o instanceof ApplicationPageRelatedMenusFilterBuilder that))\n            return false;\n\n        if (applicationPageId != that.applicationPageId)\n            return false;\n        if (range != null ? !range.equals(that.range) : that.range != null)\n            return false;\n\n        return true;\n    }\n\n    @Override\n    public int hashCode() {\n        int result = range != null ? range.hashCode() : 0;\n        result = 31 * result + (int) (applicationPageId ^ (applicationPageId >>> 32));\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/impl/filter/ApplicationRelatedMenusFilterBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.impl.filter;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.business.application.model.SApplicationMenu;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ApplicationRelatedMenusFilterBuilder implements FilterBuilder {\n\n    private SelectRange range;\n    private long applicationId;\n\n    public ApplicationRelatedMenusFilterBuilder(SelectRange range, long applicationId) {\n        this.range = range;\n        this.applicationId = applicationId;\n    }\n\n    @Override\n    public QueryOptions buildQueryOptions() {\n        List<OrderByOption> orderByOptions = Collections\n                .singletonList(new OrderByOption(SApplicationMenu.class, SApplicationMenu.ID, OrderByType.ASC));\n        List<FilterOption> filters = new ArrayList<FilterOption>(2);\n        filters.add(new FilterOption(SApplicationMenu.class, SApplicationMenu.APPLICAITON_ID, applicationId));\n        //only too menu will be deleted as children menus will be deleted by the parent\n        filters.add(new FilterOption(SApplicationMenu.class, SApplicationMenu.PARENT_ID, null));\n        return new QueryOptions(range.getStartIndex(), range.getMaxResults(), orderByOptions, filters, null);\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (!(o instanceof ApplicationRelatedMenusFilterBuilder that))\n            return false;\n\n        if (applicationId != that.applicationId)\n            return false;\n        if (range != null ? !range.equals(that.range) : that.range != null)\n            return false;\n\n        return true;\n    }\n\n    @Override\n    public int hashCode() {\n        int result = range != null ? range.hashCode() : 0;\n        result = 31 * result + (int) (applicationId ^ (applicationId >>> 32));\n        return result;\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/impl/filter/ChildrenMenusFilterBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.impl.filter;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\n\nimport org.bonitasoft.engine.business.application.model.SApplicationMenu;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ChildrenMenusFilterBuilder implements FilterBuilder {\n\n    private final SelectRange range;\n    private final long parentId;\n\n    public ChildrenMenusFilterBuilder(SelectRange range, long parentId) {\n        this.range = range;\n        this.parentId = parentId;\n    }\n\n    @Override\n    public QueryOptions buildQueryOptions() {\n        List<OrderByOption> orderByOptions = Collections\n                .singletonList(new OrderByOption(SApplicationMenu.class, SApplicationMenu.ID, OrderByType.ASC));\n        List<FilterOption> filters = Collections\n                .singletonList(new FilterOption(SApplicationMenu.class, SApplicationMenu.PARENT_ID, parentId));\n        return new QueryOptions(range.getStartIndex(), range.getMaxResults(), orderByOptions, filters, null);\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (!(o instanceof ChildrenMenusFilterBuilder that))\n            return false;\n\n        if (parentId != that.parentId)\n            return false;\n        if (!Objects.equals(range, that.range))\n            return false;\n\n        return true;\n    }\n\n    @Override\n    public int hashCode() {\n        int result = range != null ? range.hashCode() : 0;\n        result = 31 * result + (int) (parentId ^ (parentId >>> 32));\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/impl/filter/FilterBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.impl.filter;\n\nimport org.bonitasoft.engine.persistence.QueryOptions;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface FilterBuilder {\n\n    QueryOptions buildQueryOptions();\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/impl/filter/SelectRange.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.impl.filter;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SelectRange {\n\n    private int startIndex;\n    private int maxResults;\n\n    public SelectRange(int startIndex, int maxResults) {\n        this.startIndex = startIndex;\n        this.maxResults = maxResults;\n    }\n\n    public int getStartIndex() {\n        return startIndex;\n    }\n\n    public int getMaxResults() {\n        return maxResults;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (!(o instanceof SelectRange that))\n            return false;\n\n        if (maxResults != that.maxResults)\n            return false;\n        if (startIndex != that.startIndex)\n            return false;\n\n        return true;\n    }\n\n    @Override\n    public int hashCode() {\n        int result = startIndex;\n        result = 31 * result + maxResults;\n        return result;\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/AbstractSApplication.java",
    "content": "/**\n * Copyright (C) 2021 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.model;\n\nimport javax.persistence.Column;\nimport javax.persistence.Id;\nimport javax.persistence.MappedSuperclass;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n@Data\n@NoArgsConstructor\n@MappedSuperclass\npublic abstract class AbstractSApplication implements PersistentObject {\n\n    public static final String ID = \"id\";\n    public static final String TOKEN = \"token\";\n    public static final String DISPLAY_NAME = \"displayName\";\n    public static final String VERSION = \"version\";\n    public static final String DESCRIPTION = \"description\";\n    public static final String ICON_PATH = \"iconPath\";\n    public static final String CREATION_DATE = \"creationDate\";\n    public static final String CREATED_BY = \"createdBy\";\n    public static final String LAST_UPDATE_DATE = \"lastUpdateDate\";\n    public static final String UPDATED_BY = \"updatedBy\";\n    public static final String STATE = \"state\";\n    public static final String HOME_PAGE_ID = \"homePageId\";\n    public static final String PROFILE_ID = \"profileId\";\n    public static final String LAYOUT_ID = \"layoutId\";\n    public static final String THEME_ID = \"themeId\";\n    public static final String ICON_MIME_TYPE = \"iconMimeType\";\n    public static final String EDITABLE = \"editable\";\n    public static final String INTERNAL_PROFILE = \"internalProfile\";\n    public static final String LINK = \"isLink\";\n\n    @Id\n    private long id;\n\n    @Column\n    private String token;\n    @Column\n    private String description;\n    @Column\n    private String version;\n    @Column\n    private String iconPath;\n    @Column\n    private long creationDate;\n    @Column\n    private long createdBy;\n    @Column\n    private long lastUpdateDate;\n    @Column\n    private long updatedBy;\n    @Column\n    private String state;\n    @Column\n    private Long homePageId;\n    @Column\n    private String displayName;\n    @Column\n    private Long profileId;\n    @Column\n    private Long layoutId;\n    @Column\n    private Long themeId;\n    @Column\n    private String iconMimeType;\n    @Column\n    private boolean editable = true;\n    @Column\n    private String internalProfile;\n    // \"link\" is a reserved keyword in some databases\n    @Column\n    private boolean isLink;\n\n    public AbstractSApplication(String token, String displayName, String version, long creationDate,\n            long createdBy, String state, boolean editable) {\n        this.token = token;\n        this.displayName = displayName;\n        this.version = version;\n        this.creationDate = creationDate;\n        lastUpdateDate = creationDate; //at instantiation the creation date is the same as last update date\n        this.createdBy = createdBy;\n        updatedBy = createdBy;\n        this.state = state;\n        this.editable = editable;\n    }\n\n    public boolean hasIcon() {\n        return iconMimeType != null && !iconMimeType.isBlank();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/SApplication.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.model;\n\nimport javax.persistence.Cacheable;\nimport javax.persistence.Entity;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\n@Data\n@ToString(callSuper = true)\n@EqualsAndHashCode(callSuper = true)\n@AllArgsConstructor\n@Entity\n@Table(name = \"business_app\")\n@Cacheable(false)\npublic class SApplication extends AbstractSApplication {\n\n    public SApplication(final String token, final String displayName, final String version, final long creationDate,\n            final long createdBy,\n            final String state, boolean editable) {\n        super(token, displayName, version, creationDate, createdBy, state, editable);\n    }\n\n    public SApplication(final String token, final String displayName, final String version, final long creationDate,\n            final long createdBy,\n            final String state) {\n        super(token, displayName, version, creationDate, createdBy, state, true);\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/SApplicationMenu.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.model;\n\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@Builder\n@Entity\n@Table(name = \"business_app_menu\")\npublic class SApplicationMenu implements PersistentObject {\n\n    public static String ID = \"id\";\n    public static String DISPLAY_NAME = \"displayName\";\n    public static String APPLICAITON_ID = \"applicationId\";\n    public static String APPLICATION_PAGE_ID = \"applicationPageId\";\n    public static String PARENT_ID = \"parentId\";\n    public static String INDEX = \"index\";\n    @Id\n    private long id;\n    @Column\n    private String displayName;\n    @Column\n    private long applicationId;\n    @Column\n    private Long applicationPageId;\n    @Column\n    private Long parentId;\n    @Column(name = \"index_\")\n    private int index;\n\n    public SApplicationMenu(final String displayName, long applicationId, final Long applicationPageId,\n            final int index) {\n        this.displayName = displayName;\n        this.applicationId = applicationId;\n        this.applicationPageId = applicationPageId;\n        this.index = index;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/SApplicationPage.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.model;\n\nimport javax.persistence.Cacheable;\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@Builder\n@Entity\n@Table(name = \"business_app_page\")\n@Cacheable(false)\npublic class SApplicationPage implements PersistentObject {\n\n    public static final String ID = \"id\";\n    public static final String TOKEN = \"token\";\n    public static final String PAGE_ID = \"pageId\";\n    public static final String APPLICATION_ID = \"applicationId\";\n    @Id\n    private long id;\n    @Column\n    private long applicationId;\n    @Column\n    private long pageId;\n    @Column\n    private String token;\n\n    public SApplicationPage(final long applicationId, final long pageId, final String token) {\n        super();\n        this.applicationId = applicationId;\n        this.pageId = pageId;\n        this.token = token;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/SApplicationState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.model;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic enum SApplicationState {\n    ACTIVATED,\n\n    DEACTIVATED\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/SApplicationWithIcon.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.model;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport javax.persistence.Cacheable;\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.ToString;\nimport org.hibernate.annotations.Type;\n\n@Data\n@ToString(callSuper = true)\n@EqualsAndHashCode(callSuper = true)\n@AllArgsConstructor\n@NoArgsConstructor\n@Entity\n@Table(name = \"business_app\")\n@Cacheable(false)\npublic class SApplicationWithIcon extends AbstractSApplication {\n\n    public static final String ICON_CONTENT = \"iconContent\";\n    public static List<String> ALWAYS_MODIFIABLE_FIELDS = Arrays.asList(LAYOUT_ID, THEME_ID, ICON_MIME_TYPE,\n            ICON_CONTENT,\n            UPDATED_BY, LAST_UPDATE_DATE);\n\n    @Type(type = \"materialized_blob\")\n    @Column\n    private byte[] iconContent;\n\n    public SApplicationWithIcon(String token, String displayName, String version,\n            long creationDate, long createdBy, String state, boolean editable) {\n        super(token, displayName, version, creationDate, createdBy, state, editable);\n    }\n\n    public SApplicationWithIcon(String token, String displayName, String version,\n            long creationDate, long createdBy, String state) {\n        super(token, displayName, version, creationDate, createdBy, state, true);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/builder/SApplicationLogBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.model.builder;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SApplicationLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction {\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/builder/SApplicationLogBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.model.builder;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SApplicationLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory {\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/builder/SApplicationMenuLogBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.model.builder;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SApplicationMenuLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction {\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/builder/SApplicationMenuLogBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.model.builder;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SApplicationMenuLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory {\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/builder/SApplicationMenuUpdateBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.model.builder;\n\nimport org.bonitasoft.engine.business.application.model.SApplicationMenu;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SApplicationMenuUpdateBuilder {\n\n    protected EntityUpdateDescriptor descriptor;\n\n    public SApplicationMenuUpdateBuilder() {\n        descriptor = new EntityUpdateDescriptor();\n    }\n\n    public EntityUpdateDescriptor done() {\n        return descriptor;\n    }\n\n    public SApplicationMenuUpdateBuilder updateDisplayName(String displayName) {\n        descriptor.addField(SApplicationMenu.DISPLAY_NAME, displayName);\n        return this;\n    }\n\n    public SApplicationMenuUpdateBuilder updateApplicationPageId(Long applicationPageId) {\n        descriptor.addField(SApplicationMenu.APPLICATION_PAGE_ID, applicationPageId);\n        return this;\n    }\n\n    public SApplicationMenuUpdateBuilder updateIndex(int index) {\n        descriptor.addField(SApplicationMenu.INDEX, index);\n        return this;\n    }\n\n    public SApplicationMenuUpdateBuilder updateParentId(Long parentId) {\n        descriptor.addField(SApplicationMenu.PARENT_ID, parentId);\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/builder/SApplicationMenuUpdateBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.model.builder;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SApplicationMenuUpdateBuilderFactory {\n\n    SApplicationMenuUpdateBuilder createNewInstance();\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/builder/SApplicationPageLogBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.model.builder;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SApplicationPageLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction {\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/builder/SApplicationPageLogBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.model.builder;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SApplicationPageLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory {\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/builder/SApplicationUpdateBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.model.builder;\n\nimport org.bonitasoft.engine.business.application.model.AbstractSApplication;\nimport org.bonitasoft.engine.business.application.model.SApplicationWithIcon;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\npublic class SApplicationUpdateBuilder {\n\n    protected final EntityUpdateDescriptor descriptor;\n\n    public SApplicationUpdateBuilder(final long updaterUserId) {\n        descriptor = new EntityUpdateDescriptor();\n        descriptor.addField(AbstractSApplication.UPDATED_BY, updaterUserId);\n        descriptor.addField(AbstractSApplication.LAST_UPDATE_DATE, System.currentTimeMillis());\n    }\n\n    public EntityUpdateDescriptor done() {\n        return descriptor;\n    }\n\n    public SApplicationUpdateBuilder updateToken(final String token) {\n        descriptor.addField(AbstractSApplication.TOKEN, token);\n        return this;\n    }\n\n    public SApplicationUpdateBuilder updateDisplayName(final String displayName) {\n        descriptor.addField(AbstractSApplication.DISPLAY_NAME, displayName);\n        return this;\n    }\n\n    public SApplicationUpdateBuilder updateVersion(final String version) {\n        descriptor.addField(AbstractSApplication.VERSION, version);\n        return this;\n    }\n\n    public SApplicationUpdateBuilder updateDescription(final String description) {\n        descriptor.addField(AbstractSApplication.DESCRIPTION, description);\n        return this;\n    }\n\n    public SApplicationUpdateBuilder updateIconPath(final String iconPath) {\n        descriptor.addField(AbstractSApplication.ICON_PATH, iconPath);\n        return this;\n    }\n\n    public SApplicationUpdateBuilder updateState(final String state) {\n        descriptor.addField(AbstractSApplication.STATE, state);\n        return this;\n    }\n\n    public SApplicationUpdateBuilder updateProfileId(final Long profileId) {\n        descriptor.addField(AbstractSApplication.PROFILE_ID, profileId);\n        return this;\n    }\n\n    public SApplicationUpdateBuilder updateHomePageId(final Long homePageId) {\n        descriptor.addField(AbstractSApplication.HOME_PAGE_ID, homePageId);\n        return this;\n    }\n\n    public SApplicationUpdateBuilder updateLayoutId(final Long layoutId) {\n        descriptor.addField(AbstractSApplication.LAYOUT_ID, layoutId);\n        return this;\n    }\n\n    public SApplicationUpdateBuilder updateThemeId(final Long themeId) {\n        descriptor.addField(AbstractSApplication.THEME_ID, themeId);\n        return this;\n    }\n\n    public SApplicationUpdateBuilder updateIconMimeType(String mimeType) {\n        descriptor.addField(AbstractSApplication.ICON_MIME_TYPE, mimeType);\n        return this;\n    }\n\n    public SApplicationUpdateBuilder updateIconContent(byte[] content) {\n        descriptor.addField(SApplicationWithIcon.ICON_CONTENT, content);\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/builder/impl/SApplicationLogBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.model.builder.impl;\n\nimport org.bonitasoft.engine.business.application.model.builder.SApplicationLogBuilderFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SApplicationLogBuilderFactoryImpl extends CRUDELogBuilderFactory implements SApplicationLogBuilderFactory {\n\n    public static final int APPLICATION_INDEX = 1;\n\n    public static final String APPLICATION_INDEX_NAME = \"numericIndex2\";\n\n    @Override\n    public String getObjectIdKey() {\n        return APPLICATION_INDEX_NAME;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/builder/impl/SApplicationLogBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.model.builder.impl;\n\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.model.builder.SApplicationLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SApplicationLogBuilderImpl extends CRUDELogBuilder implements SApplicationLogBuilder {\n\n    @Override\n    public SPersistenceLogBuilder objectId(final long objectId) {\n        queriableLogBuilder.numericIndex(SApplicationLogBuilderFactoryImpl.APPLICATION_INDEX, objectId);\n        return this;\n    }\n\n    @Override\n    protected String getActionTypePrefix() {\n        return ApplicationService.APPLICATION;\n    }\n\n    @Override\n    protected void checkExtraRules(final SQueriableLog log) {\n        if (log.getActionStatus() != SQueriableLog.STATUS_FAIL\n                && log.getNumericIndex(SApplicationLogBuilderFactoryImpl.APPLICATION_INDEX) == 0L) {\n            throw new MissingMandatoryFieldsException(\n                    \"Some mandatory fields are missing: business application identifier\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/builder/impl/SApplicationMenuLogBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.model.builder.impl;\n\nimport org.bonitasoft.engine.business.application.model.builder.SApplicationMenuLogBuilderFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SApplicationMenuLogBuilderFactoryImpl extends CRUDELogBuilderFactory\n        implements SApplicationMenuLogBuilderFactory {\n\n    public static final int APPLICATION_MENU_INDEX = 1;\n\n    public static final String APPLICATION_MENU_INDEX_NAME = \"numericIndex2\";\n\n    @Override\n    public String getObjectIdKey() {\n        return APPLICATION_MENU_INDEX_NAME;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/builder/impl/SApplicationMenuLogBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.model.builder.impl;\n\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.model.builder.SApplicationMenuLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SApplicationMenuLogBuilderImpl extends CRUDELogBuilder implements SApplicationMenuLogBuilder {\n\n    @Override\n    public SPersistenceLogBuilder objectId(final long objectId) {\n        queriableLogBuilder.numericIndex(SApplicationMenuLogBuilderFactoryImpl.APPLICATION_MENU_INDEX, objectId);\n        return this;\n    }\n\n    @Override\n    protected String getActionTypePrefix() {\n        return ApplicationService.APPLICATION_MENU;\n    }\n\n    @Override\n    protected void checkExtraRules(final SQueriableLog log) {\n        if (log.getActionStatus() != SQueriableLog.STATUS_FAIL\n                && log.getNumericIndex(SApplicationMenuLogBuilderFactoryImpl.APPLICATION_MENU_INDEX) == 0L) {\n            throw new MissingMandatoryFieldsException(\"Some mandatoryFields are missing: application menu identifier\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/builder/impl/SApplicationPageLogBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.model.builder.impl;\n\nimport org.bonitasoft.engine.business.application.model.builder.SApplicationLogBuilderFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SApplicationPageLogBuilderFactoryImpl extends CRUDELogBuilderFactory\n        implements SApplicationLogBuilderFactory {\n\n    public static final int APPLICATION_PAGE_INDEX = 1;\n\n    public static final String APPLICATION_PAGE_INDEX_NAME = \"numericIndex2\";\n\n    @Override\n    public String getObjectIdKey() {\n        return APPLICATION_PAGE_INDEX_NAME;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/builder/impl/SApplicationPageLogBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.model.builder.impl;\n\nimport org.bonitasoft.engine.business.application.model.builder.SApplicationPageLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SApplicationPageLogBuilderImpl extends CRUDELogBuilder implements SApplicationPageLogBuilder {\n\n    @Override\n    public SPersistenceLogBuilder objectId(final long objectId) {\n        queriableLogBuilder.numericIndex(SApplicationPageLogBuilderFactoryImpl.APPLICATION_PAGE_INDEX, objectId);\n        return this;\n    }\n\n    @Override\n    protected String getActionTypePrefix() {\n        return \"APPLICATION_PAGE\";\n    }\n\n    @Override\n    protected void checkExtraRules(final SQueriableLog log) {\n        if (log.getActionStatus() != SQueriableLog.STATUS_FAIL\n                && log.getNumericIndex(SApplicationPageLogBuilderFactoryImpl.APPLICATION_PAGE_INDEX) == 0L) {\n            throw new MissingMandatoryFieldsException(\"Some mandatory fields are missing: application page identifier\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/main/resources/org/bonitasoft/engine/business/application/impl/hibernate/application.queries.hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\" \"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd\">\n<hibernate-mapping auto-import=\"false\">\n\n    <query name=\"getApplicationByToken\">\n        SELECT application\n        FROM org.bonitasoft.engine.business.application.model.SApplication AS application\n        WHERE token = :token\n    </query>\n\n    <query name=\"getNumberOfSApplication\">\n        SELECT count(application.id)\n        FROM org.bonitasoft.engine.business.application.model.SApplication AS application\n    </query>\n\n    <query name=\"getNumberOfSApplicationOfUser\">\n        SELECT count(DISTINCT application.id)\n        FROM org.bonitasoft.engine.business.application.model.SApplication AS application, org.bonitasoft.engine.profile.model.SProfileMember as profileMember\n        WHERE application.profileId = profileMember.profileId\n        AND (\n            profileMember.userId = :userId\n            OR profileMember.id IN (\n                SELECT profileMember.id\n                FROM org.bonitasoft.engine.profile.model.SProfileMember AS profileMember, org.bonitasoft.engine.identity.model.SUserMembership AS userMember\n                WHERE userMember.userId = :userId\n                AND ((profileMember.groupId = userMember.groupId AND profileMember.roleId &lt;= 0)\n                    OR (profileMember.roleId = userMember.roleId AND profileMember.groupId &lt;= 0)\n                    OR (profileMember.groupId = userMember.groupId AND profileMember.roleId = userMember.roleId)\n                )\n            )\n        )\n    </query>\n\n    <query name=\"searchSApplication\">\n        SELECT application\n        FROM org.bonitasoft.engine.business.application.model.SApplication AS application\n    </query>\n\n    <query name=\"searchSApplicationOfUser\">\n        SELECT DISTINCT application\n        FROM org.bonitasoft.engine.business.application.model.SApplication AS application, org.bonitasoft.engine.profile.model.SProfileMember as profileMember\n        WHERE application.profileId = profileMember.profileId\n        AND (\n            profileMember.userId = :userId\n            OR profileMember.id IN (\n                SELECT profileMember.id\n                FROM org.bonitasoft.engine.profile.model.SProfileMember AS profileMember, org.bonitasoft.engine.identity.model.SUserMembership AS userMember\n                WHERE userMember.userId = :userId\n                AND ((profileMember.groupId = userMember.groupId AND profileMember.roleId &lt;= 0)\n                    OR (profileMember.roleId = userMember.roleId AND profileMember.groupId &lt;= 0)\n                    OR (profileMember.groupId = userMember.groupId AND profileMember.roleId = userMember.roleId)\n                )\n            )\n        )\n    </query>\n\n    <query name=\"getApplicationPageById\">\n        SELECT applicationPage\n        FROM org.bonitasoft.engine.business.application.model.SApplicationPage as applicationPage\n        WHERE id = :id\n    </query>\n\n    <query name=\"getApplicationPageByTokenAndApplicationToken\">\n        SELECT applicationPage\n        FROM org.bonitasoft.engine.business.application.model.SApplicationPage as applicationPage, org.bonitasoft.engine.business.application.model.SApplication as application\n        WHERE applicationPage.applicationId = application.id\n        AND applicationPage.token = :applicationPageToken\n        AND application.token = :applicationToken\n    </query>\n\n    <query name=\"getApplicationPageByTokenAndApplicationId\">\n        SELECT applicationPage\n        FROM org.bonitasoft.engine.business.application.model.SApplicationPage as applicationPage\n        WHERE applicationPage.applicationId = :applicationId\n        AND applicationPage.token = :applicationPageToken\n    </query>\n\n    <query name=\"getApplicationHomePage\">\n        SELECT applicationPage\n        FROM org.bonitasoft.engine.business.application.model.SApplicationPage as applicationPage, org.bonitasoft.engine.business.application.model.SApplication as application\n        WHERE applicationPage.id = application.homePageId\n        AND applicationPage.applicationId = application.id\n        AND application.id = :applicationId\n    </query>\n\n    <query name=\"getNumberOfSApplicationPage\">\n        SELECT count(applicationPage.id)\n        FROM org.bonitasoft.engine.business.application.model.SApplicationPage as applicationPage\n    </query>\n\n    <query name=\"searchSApplicationPage\">\n        SELECT applicationPage\n        FROM org.bonitasoft.engine.business.application.model.SApplicationPage as applicationPage\n    </query>\n\n    <query name=\"getApplicationMenuById\">\n        SELECT applicationMenu\n        FROM org.bonitasoft.engine.business.application.model.SApplicationMenu as applicationMenu\n        WHERE id = :id\n    </query>\n\n    <query name=\"getNumberOfSApplicationMenu\">\n        SELECT count(applicationMenu.id)\n        FROM org.bonitasoft.engine.business.application.model.SApplicationMenu as applicationMenu\n    </query>\n\n    <query name=\"searchSApplicationMenu\">\n        SELECT applicationMenu\n        FROM org.bonitasoft.engine.business.application.model.SApplicationMenu as applicationMenu\n    </query>\n\n    <query name=\"getLastIndexForRootMenu\">\n        SELECT max(applicationMenu.index)\n        FROM org.bonitasoft.engine.business.application.model.SApplicationMenu as applicationMenu\n        WHERE parentId is null\n    </query>\n\n    <query name=\"getLastIndexForChildOf\">\n        SELECT max(applicationMenu.index)\n        FROM org.bonitasoft.engine.business.application.model.SApplicationMenu as applicationMenu\n        WHERE parentId = :parentId\n    </query>\n\n\n    <query name=\"getAllPagesForProfile\">\n        SELECT DISTINCT page.name\n        FROM org.bonitasoft.engine.business.application.model.SApplicationPage as applicationPage,\n        org.bonitasoft.engine.business.application.model.SApplication as application,\n        org.bonitasoft.engine.page.SPage as page\n        WHERE application.profileId = :profileId\n        AND (\n        application.layoutId = page.id\n        OR\n        application.themeId = page.id\n        OR (\n        applicationPage.applicationId = application.id\n        AND applicationPage.pageId = page.id\n        )\n        )\n\n        ORDER BY page.name\n    </query>\n    <query name=\"getAllPagesForProfileName\">\n        SELECT DISTINCT page.name\n        FROM org.bonitasoft.engine.business.application.model.SApplicationPage as applicationPage,\n        org.bonitasoft.engine.business.application.model.SApplication as application,\n        org.bonitasoft.engine.page.SPage as page,\n        org.bonitasoft.engine.profile.model.SProfile as profile\n        WHERE\n        profile.name = :profileName\n        AND application.profileId = profile.id\n        AND ( application.layoutId = page.id\n        OR application.themeId = page.id\n        OR ( applicationPage.applicationId = application.id AND applicationPage.pageId = page.id )\n        )\n\n        ORDER BY page.name\n    </query>\n\n</hibernate-mapping>\n"
  },
  {
    "path": "services/bonita-business-application/src/test/java/org/bonitasoft/engine/business/application/impl/ApplicationServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.impl;\n\nimport static java.util.Collections.singletonMap;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.business.application.ApplicationService.APPLICATION;\nimport static org.junit.Assert.assertThrows;\nimport static org.junit.Assert.fail;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.*;\nimport static org.mockito.Mockito.argThat;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.impl.cleaner.ApplicationDestructor;\nimport org.bonitasoft.engine.business.application.impl.cleaner.ApplicationMenuDestructor;\nimport org.bonitasoft.engine.business.application.impl.cleaner.ApplicationPageDestructor;\nimport org.bonitasoft.engine.business.application.impl.converter.MenuIndexConverter;\nimport org.bonitasoft.engine.business.application.model.SApplication;\nimport org.bonitasoft.engine.business.application.model.SApplicationMenu;\nimport org.bonitasoft.engine.business.application.model.SApplicationPage;\nimport org.bonitasoft.engine.business.application.model.SApplicationState;\nimport org.bonitasoft.engine.business.application.model.SApplicationWithIcon;\nimport org.bonitasoft.engine.business.application.model.builder.SApplicationMenuUpdateBuilder;\nimport org.bonitasoft.engine.business.application.model.builder.SApplicationUpdateBuilder;\nimport org.bonitasoft.engine.cache.CacheService;\nimport org.bonitasoft.engine.commons.exceptions.SObjectAlreadyExistsException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectCreationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.ArgumentMatchers;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ApplicationServiceImplTest {\n\n    private static final int CREATED_BY = 10;\n\n    private static final String APPLICATION_TOKEN = \"app\";\n\n    private static final String APPLICATION_DISPLAY_NAME = \"My app\";\n\n    public static final long LAYOUT_ID = 15L;\n\n    public static final long THEME_ID = 16L;\n\n    @Mock\n    private Recorder recorder;\n\n    @Mock\n    private ReadPersistenceService persistenceService;\n\n    @Mock\n    private QueriableLoggerService queriableLogService;\n\n    @Mock\n    private IndexManager indexManager;\n\n    @Mock\n    private MenuIndexConverter convertor;\n\n    @Mock\n    private ApplicationDestructor applicationDestructor;\n\n    @Mock\n    private ApplicationPageDestructor applicationPageDestructor;\n\n    @Mock\n    private ApplicationMenuDestructor applicationMenuDestructor;\n\n    @Mock\n    private CacheService cacheService;\n\n    private SApplicationWithIcon application;\n\n    private ApplicationServiceImpl applicationServiceImpl;\n\n    @Before\n    public void setUp() {\n        applicationServiceImpl = new ApplicationServiceImpl(recorder, persistenceService, queriableLogService,\n                indexManager, convertor, applicationDestructor, applicationPageDestructor, applicationMenuDestructor,\n                cacheService);\n\n        when(queriableLogService.isLoggable(anyString(), any(SQueriableLogSeverity.class))).thenReturn(true);\n        application = buildApplication(APPLICATION_TOKEN, APPLICATION_DISPLAY_NAME);\n        application.setId(10L);\n    }\n\n    private SApplicationWithIcon buildApplication(final String applicationName, final String applicationDisplayName) {\n        final long currentDate = System.currentTimeMillis();\n        SApplicationWithIcon application = new SApplicationWithIcon();\n        application.setToken(applicationName);\n        application.setDisplayName(applicationDisplayName);\n        application.setVersion(\"1.0\");\n        application.setCreationDate(currentDate);\n        application.setLastUpdateDate(currentDate);\n        application.setCreatedBy(CREATED_BY);\n        application.setState(SApplicationState.ACTIVATED.name());\n        application.setLayoutId(LAYOUT_ID);\n        application.setThemeId(THEME_ID);\n        return application;\n    }\n\n    @Test\n    public void createApplication_should_call_recordInsert_and_return_created_object() throws Exception {\n        //given\n        final InsertRecord record = new InsertRecord(application);\n\n        //when\n        final SApplicationWithIcon createdApplication = applicationServiceImpl.createApplication(application);\n\n        //then\n        assertThat(createdApplication).isEqualTo(application);\n        verify(recorder, times(1)).recordInsert(record, APPLICATION);\n    }\n\n    @Test(expected = SObjectCreationException.class)\n    public void createApplication_should_throw_SObjectCreationException_when_record_insert_throws_Exception()\n            throws Exception {\n        //given\n        doThrow(new SRecorderException(\"\")).when(recorder).recordInsert(any(InsertRecord.class), anyString());\n\n        //when\n        applicationServiceImpl.createApplication(application);\n\n        //then exception\n    }\n\n    @Test\n    public void createApplication_should_throw_SObjectAlreadyExistsException_when_an_application_with_the_same_name_already_exists()\n            throws Exception {\n        //given\n        SApplication app = new SApplication();\n        app.setId(125);\n        given(persistenceService.selectOne(new SelectOneDescriptor<SApplication>(\"getApplicationByToken\",\n                Collections.singletonMap(\"name\",\n                        APPLICATION_TOKEN),\n                SApplication.class))).willReturn(app);\n\n        final SApplicationWithIcon newApp = buildApplication(APPLICATION_TOKEN, APPLICATION_DISPLAY_NAME);\n\n        //when\n        try {\n            applicationServiceImpl.createApplication(newApp);\n            fail(\"Exception expected\");\n        } catch (final SObjectAlreadyExistsException e) {\n            //then\n            assertThat(e.getMessage())\n                    .isEqualTo(\"An application already exists with token '\" + APPLICATION_TOKEN + \"'.\");\n            verify(recorder, never()).recordInsert(any(InsertRecord.class), anyString());\n        }\n\n    }\n\n    @Test\n    public void getApplication_should_return_result_of_persistence_service_selectById() throws Exception {\n        //given\n        SApplication app = new SApplication();\n        app.setId(10L);\n        given(persistenceService.selectById(new SelectByIdDescriptor<>(SApplication.class, 10L)))\n                .willReturn(app);\n\n        //when\n        final SApplication retrievedApp = applicationServiceImpl.getApplication(10L);\n\n        //then\n        assertThat(retrievedApp).isEqualTo(app);\n    }\n\n    @Test\n    public void getApplication_should_throw_SObjectNotFoundException_when_persitence_service_returns_null()\n            throws Exception {\n        //given\n        final long applicationId = 10L;\n        given(persistenceService.selectById(new SelectByIdDescriptor<>(SApplication.class, applicationId)))\n                .willReturn(null);\n\n        //when\n        try {\n            applicationServiceImpl.getApplication(applicationId);\n            fail(\"Exception expected\");\n        } catch (final SObjectNotFoundException e) {\n            //then\n            assertThat(e.getMessage()).isEqualTo(\"No application found with id '\" + applicationId + \"'.\");\n        }\n\n    }\n\n    @Test\n    public void getApplicationByToken_should_return_result_of_persistence_service_getApplicationByToken()\n            throws Exception {\n        //given\n        SApplication app = new SApplication();\n        app.setToken(\"name\");\n        given(persistenceService.selectOne(new SelectOneDescriptor<SApplication>(\"getApplicationByToken\",\n                singletonMap(\"name\", APPLICATION_TOKEN),\n                SApplication.class))).willReturn(app);\n\n        //when\n        SApplication retriedApplication = applicationServiceImpl.getApplicationByToken(APPLICATION_TOKEN);\n\n        //then\n        assertThat(retriedApplication).isEqualTo(app);\n\n    }\n\n    @Test\n    public void deleteApplication_should_call_record_delete() throws Exception {\n        //given\n        final long applicationId = 10L;\n        SApplication app = new SApplication();\n        app.setId(10L);\n        given(persistenceService.selectById(argThat(s -> s.getId() == 10L))).willReturn(app);\n\n        //when\n        applicationServiceImpl.deleteApplication(applicationId);\n\n        //then\n        verify(recorder, times(1)).recordDelete(new DeleteRecord(app), APPLICATION);\n    }\n\n    @Test\n    public void deleteApplication_should_throw_exception_when_targeting_non_editable_app() throws Exception {\n\n        //given\n        final long applicationId = 10L;\n        SApplication app = new SApplication();\n        app.setDisplayName(\"My App\");\n        app.setId(10L);\n        app.setEditable(false);\n        given(persistenceService.selectById(argThat(s -> s.getId() == 10L))).willReturn(app);\n\n        //when\n        String exceptionMessage = assertThrows(\"Not the right exception\", SObjectModificationException.class,\n                () -> applicationServiceImpl.deleteApplication(applicationId)).getMessage();\n\n        //then\n        assertThat(exceptionMessage)\n                .contains(\"The application 'My App' is set as non modifiable. It cannot be deleted\");\n\n    }\n\n    @Test\n    public void deleteApplication_should_call_applicationDestructor() throws Exception {\n        //given\n        final ApplicationServiceImpl applicationService = spy(applicationServiceImpl);\n\n        final long applicationId = 10L;\n        SApplication app = new SApplication();\n        app.setId(10L);\n        doReturn(app).when(applicationService).getApplication(applicationId);\n\n        //when\n        applicationService.deleteApplication(applicationId);\n\n        //then\n        verify(applicationDestructor, times(1)).onDeleteApplication(app);\n    }\n\n    @Test(expected = SObjectNotFoundException.class)\n    public void deleteApplication_should_throw_SObjectNotFoundException_when_no_application_with_the_given_id_is_found()\n            throws Exception {\n        //given\n        final long applicationId = 10L;\n        given(persistenceService.selectById(new SelectByIdDescriptor<>(SApplication.class, applicationId)))\n                .willReturn(null);\n\n        //when\n        applicationServiceImpl.deleteApplication(applicationId);\n\n        //then exception\n    }\n\n    @Test(expected = SObjectModificationException.class)\n    public void deleteApplication_should_throw_SObjectModificationException_when_recorder_throws_SRecorderException()\n            throws Exception {\n        //given\n        given(persistenceService.selectById(new SelectByIdDescriptor<>(SApplication.class, 10L)))\n                .willReturn(new SApplication());\n        doThrow(new SRecorderException(\"\")).when(recorder).recordDelete(any(DeleteRecord.class), anyString());\n\n        //when\n        applicationServiceImpl.deleteApplication(10L);\n\n        //then exception\n    }\n\n    @Test\n    public void getNumberOfApplications_should_return_the_result_of_persitenceService_getNumberOfEntities()\n            throws Exception {\n        //given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long count = 7;\n        given(persistenceService.getNumberOfEntities(SApplication.class, options, null)).willReturn(count);\n\n        //when\n        final long nbOfApp = applicationServiceImpl.getNumberOfApplications(options);\n\n        //then\n        assertThat(nbOfApp).isEqualTo(count);\n    }\n\n    @Test\n    public void searchApplications_should_return_the_result_of_persitenceService_searchEntity() throws Exception {\n        //given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final List<SApplication> applications = new ArrayList<>(1);\n        applications.add(mock(SApplication.class));\n        given(persistenceService.searchEntity(SApplication.class, options, null)).willReturn(applications);\n\n        //when\n        final List<SApplication> retrievedApplications = applicationServiceImpl.searchApplications(options);\n\n        //then\n        assertThat(retrievedApplications).isEqualTo(applications);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void searchApplications_should_throw_SBonitaReadException_when_persistenceSevice_throws_SBonitaReadException()\n            throws Exception {\n        //given\n        final QueryOptions options = new QueryOptions(0, 10);\n        given(applicationServiceImpl.searchApplications(options)).willThrow(new SBonitaReadException(\"\"));\n\n        //when\n        applicationServiceImpl.searchApplications(options);\n\n        //then exception\n    }\n\n    private SApplicationPage buildApplicationPage(final long applicationId, final long pageId, final String name) {\n        return SApplicationPage.builder().applicationId(applicationId).pageId(pageId).token(name).build();\n    }\n\n    private SApplicationPage buildApplicationPage(final long applicationPageId, final long applicationId,\n            final long pageId, final String pageToken) {\n        final SApplicationPage applicationPage = buildApplicationPage(applicationId, pageId, pageToken);\n        applicationPage.setId(applicationPageId);\n        return applicationPage;\n    }\n\n    @Test\n    public void createApplicationPage_should_call_recordInsert_and_return_created_object() throws Exception {\n        //given\n        final SApplicationPage applicationPage = buildApplicationPage(15, 5, 15, \"mainDashBoard\");\n        final InsertRecord record = new InsertRecord(applicationPage);\n\n        //when\n        final SApplicationPage createdApplicationPage = applicationServiceImpl.createApplicationPage(applicationPage);\n\n        //then\n        assertThat(createdApplicationPage).isEqualTo(applicationPage);\n        verify(recorder, times(1)).recordInsert(record, ApplicationService.APPLICATION_PAGE);\n    }\n\n    @Test(expected = SObjectCreationException.class)\n    public void createApplicationPage_should_throw_SObjectCreationException_when_recorder_throws_SBonitaException()\n            throws Exception {\n        //given\n        final SApplicationPage applicationPage = buildApplicationPage(15, 5, 15, \"mainDashBoard\");\n        doThrow(new SRecorderException(\"\")).when(recorder).recordInsert(any(InsertRecord.class), anyString());\n\n        //when\n        applicationServiceImpl.createApplicationPage(applicationPage);\n\n        //then exception\n    }\n\n    @Test(expected = SObjectAlreadyExistsException.class)\n    public void createApplicationPage_should_throw_SObjectAlreadyExistsException_when_an_applicationPage_with_the_same_name_in_the_same_application_exists()\n            throws Exception {\n        //given\n        final SApplicationPage applicationPage = buildApplicationPage(5, 15, \"mainDashBoard\");\n        final Map<String, Object> inputParameters = new HashMap<>(2);\n        inputParameters.put(\"applicationId\", 5);\n        inputParameters.put(\"applicationPageToken\", \"mainDashBoard\");\n        given(persistenceService.selectOne(\n                new SelectOneDescriptor<SApplicationPage>(\"getApplicationPageByTokenAndApplicationId\", inputParameters,\n                        SApplicationPage.class)))\n                .willReturn(applicationPage);\n\n        //when\n        final SApplicationPage applicationPageToCreate = buildApplicationPage(7, 5, 16, \"mainDashBoard\");\n        applicationServiceImpl.createApplicationPage(applicationPageToCreate);\n\n        //then exception\n    }\n\n    @Test\n    public void getApplicationPage_should_return_result_of_persitence_service_selectById() throws Exception {\n        //given\n        final SApplicationPage applicationPage = buildApplicationPage(10, 20, \"myPage\");\n        given(persistenceService.selectById(new SelectByIdDescriptor<>(SApplicationPage.class, 10L))).willReturn(\n                applicationPage);\n\n        //when\n        final SApplicationPage retrievedAppPage = applicationServiceImpl.getApplicationPage(10L);\n\n        //then\n        assertThat(retrievedAppPage).isEqualTo(applicationPage);\n    }\n\n    @Test\n    public void getApplicationPage_by_name_and_appName_should_return_result_of_persistence_service_selectOne()\n            throws Exception {\n        //given\n        final SApplicationPage applicationPage = buildApplicationPage(10, 20, \"myPage\");\n        final Map<String, Object> inputParameters = new HashMap<>(2);\n        inputParameters.put(\"applicationName\", \"app\");\n        inputParameters.put(\"applicationPageToken\", \"firstPage\");\n        given(persistenceService.selectOne(new SelectOneDescriptor<SApplicationPage>(\n                \"getApplicationPageByTokenAndApplicationToken\", inputParameters,\n                SApplicationPage.class))).willReturn(applicationPage);\n\n        //when\n        final SApplicationPage retrievedAppPage = applicationServiceImpl.getApplicationPage(\"app\", \"firstPage\");\n\n        //then\n        assertThat(retrievedAppPage).isEqualTo(applicationPage);\n    }\n\n    @Test(expected = SObjectNotFoundException.class)\n    public void getApplicationPage_by_name_and_appName_should_throw_SObjectNotFoundException_when_persitence_service_selectOne_returns_null()\n            throws Exception {\n        //given\n        given(persistenceService.selectOne(ArgumentMatchers.<SelectOneDescriptor<SApplicationPage>> any()))\n                .willReturn(null);\n\n        //when\n        applicationServiceImpl.getApplicationPage(\"app\", \"firstPage\");\n\n        //then exception\n    }\n\n    @Test\n    public void deleteApplicationPage_should_call_record_delete_with_applicationPage_identified_by_the_given_id()\n            throws Exception {\n        //given\n        final ApplicationServiceImpl applicationService = spy(applicationServiceImpl);\n        final long applicationPageId = 10L;\n        final SApplicationPage applicationPage = buildApplicationPage(applicationPageId, 20, 30, \"myPage\");\n        doReturn(applicationPage).when(applicationService).getApplicationPage(applicationPageId);\n        final long applicationId = 20L;\n\n        //when\n        applicationService.deleteApplicationPage(applicationPageId);\n\n        //then\n        final ArgumentCaptor<DeleteRecord> deleteRecordCaptor = ArgumentCaptor.forClass(DeleteRecord.class);\n        verify(recorder, times(1)).recordDelete(deleteRecordCaptor.capture(), eq(ApplicationService.APPLICATION_PAGE));\n        assertThat(deleteRecordCaptor.getValue().getEntity()).isEqualTo(applicationPage);\n    }\n\n    @Test\n    public void deleteApplicationPage_should_call_applicationPageDestructor() throws Exception {\n        //given\n        final ApplicationServiceImpl applicationService = spy(applicationServiceImpl);\n\n        //application page\n        final long applicationPageId = 10L;\n        final long applicationId = 20L;\n        final SApplicationPage applicationPage = buildApplicationPage(applicationPageId, applicationId, 30, \"myPage\");\n\n        //when\n        applicationService.deleteApplicationPage(applicationPage);\n\n        //then\n        verify(applicationPageDestructor, times(1)).onDeleteApplicationPage(applicationPage);\n    }\n\n    @Test(expected = SObjectModificationException.class)\n    public void deleteApplicationPage_should_throw_SObjectModificationException_when_applicationPageDestructor_throws_SObjectModificationException()\n            throws Exception {\n        //given\n        final ApplicationServiceImpl applicationService = spy(applicationServiceImpl);\n\n        //application page\n        final long applicationPageId = 10L;\n        final long applicationId = 20L;\n        final SApplicationPage applicationPage = buildApplicationPage(applicationPageId, applicationId, 30, \"myPage\");\n        doThrow(new SObjectModificationException()).when(applicationPageDestructor)\n                .onDeleteApplicationPage(applicationPage);\n\n        //when\n        applicationService.deleteApplicationPage(applicationPage);\n\n        //then exception\n    }\n\n    @Test(expected = SObjectNotFoundException.class)\n    public void deleteApplicationPage_should_throw_SObjectNotFound_when_there_is_no_applicationPage_for_the_given_id()\n            throws Exception {\n        //given\n        final long applicationPageId = 10L;\n        given(persistenceService.selectById(new SelectByIdDescriptor<>(SApplicationPage.class, applicationPageId)))\n                .willReturn(null);\n\n        //when\n        applicationServiceImpl.deleteApplicationPage(applicationPageId);\n\n        //then exception\n    }\n\n    @Test(expected = SObjectModificationException.class)\n    public void deleteApplicationPage_should_throw_SObjectModificationException_when_recorder_throws_SRecorderException()\n            throws Exception {\n        //given\n        final long applicationPageId = 10L;\n        final SApplicationPage applicationPage = buildApplicationPage(27, 20, 30, \"myPage\");\n        given(persistenceService.selectById(new SelectByIdDescriptor<>(SApplicationPage.class, applicationPageId)))\n                .willReturn(applicationPage);\n        doThrow(new SRecorderException(\"\")).when(recorder).recordDelete(any(DeleteRecord.class), anyString());\n\n        //when\n        applicationServiceImpl.deleteApplicationPage(applicationPageId);\n\n        //then exception\n    }\n\n    @Test\n    public void updateApplication_should_call_recorder_recordUpdate_and_return_updated_object() throws Exception {\n        //given\n        EntityUpdateDescriptor updateDescriptor = new SApplicationUpdateBuilder(0L)\n                .updateDisplayName(\"new display name\").done();\n\n        long applicationId = 17;\n        given(persistenceService.selectById(new SelectByIdDescriptor<>(SApplicationWithIcon.class, applicationId)))\n                .willReturn(\n                        application);\n\n        //when\n        final SApplicationWithIcon updatedApplication = applicationServiceImpl.updateApplication(applicationId,\n                updateDescriptor);\n\n        //then\n        final UpdateRecord updateRecord = UpdateRecord.buildSetFields(application,\n                updateDescriptor);\n        verify(recorder, times(1)).recordUpdate(updateRecord, APPLICATION);\n        assertThat(updatedApplication).isEqualTo(application);\n    }\n\n    @Test\n    public void update_application_display_name_should_throw_exception_called_on_non_editable_application()\n            throws Exception {\n        //given\n        application.setEditable(false);\n        EntityUpdateDescriptor updateDescriptor = new SApplicationUpdateBuilder(0L)\n                .updateDisplayName(\"new display name\").done();\n\n        long applicationId = 17;\n        given(persistenceService.selectById(new SelectByIdDescriptor<>(SApplicationWithIcon.class, applicationId)))\n                .willReturn(application);\n        //when\n        String exceptionMessage = assertThrows(\"Not the right exception\", SObjectModificationException.class,\n                () -> applicationServiceImpl.updateApplication(applicationId, updateDescriptor)).getMessage();\n        assertThat(exceptionMessage)\n                .contains(\"The application is provided. Only the theme, the layout, and the icon can be updated\");\n\n        //cleanup\n        application.setEditable(true);\n    }\n\n    @Test\n    public void update_non_editable_application_should_allow_to_change_icon_theme_and_layout() throws Exception {\n        //given\n        application.setEditable(false);\n        EntityUpdateDescriptor updateDescriptor = new SApplicationUpdateBuilder(0L)\n                .updateThemeId(1L).updateLayoutId(2L).updateIconMimeType(\"image/jpg\")\n                .updateIconContent(\"toto\".getBytes()).done();\n\n        long applicationId = 17;\n        given(persistenceService.selectById(new SelectByIdDescriptor<>(SApplicationWithIcon.class, applicationId)))\n                .willReturn(application);\n        //when\n        applicationServiceImpl.updateApplication(applicationId, updateDescriptor);\n\n        //then:\n        verify(recorder).recordUpdate(UpdateRecord.buildSetFields(application, updateDescriptor), APPLICATION);\n\n        //cleanup\n        application.setEditable(true);\n    }\n\n    @Test(expected = SObjectModificationException.class)\n    public void updateApplication_should_throw_SObjectModificationException_if_set_homepage_references_invalid_page()\n            throws Exception {\n        //given\n        final SApplicationUpdateBuilder updateBuilder = new SApplicationUpdateBuilder(0L);\n        final long homePageId = 150L;\n        updateBuilder.updateHomePageId(homePageId);\n        final EntityUpdateDescriptor updateDescriptor = updateBuilder.done();\n\n        final int applicationId = 17;\n\n        final SApplicationWithIcon applicationWithIcon = mock(SApplicationWithIcon.class);\n        doReturn(true).when(applicationWithIcon).isEditable();\n        given(persistenceService.selectById(new SelectByIdDescriptor<>(SApplicationWithIcon.class, applicationId)))\n                .willReturn(applicationWithIcon);\n\n        given(persistenceService.selectById(new SelectByIdDescriptor<>(SApplicationPage.class, homePageId)))\n                .willReturn(null);\n\n        //when\n        applicationServiceImpl.updateApplication(applicationId, updateDescriptor);\n\n        //then exception\n    }\n\n    @Test(expected = SObjectNotFoundException.class)\n    public void updateApplication_should_throw_SObjectModificationException_when_recorder_throws_SRecorderException()\n            throws Exception {\n        //given\n        final EntityUpdateDescriptor updateDescriptor = new EntityUpdateDescriptor();\n        updateDescriptor.addField(\"name\", \"newName\");\n        final int applicationId = 17;\n        //when\n        applicationServiceImpl.updateApplication(applicationId, updateDescriptor);\n\n        //then exception\n    }\n\n    @Test(expected = SObjectAlreadyExistsException.class)\n    public void updateApplication_should_throw_SObjectAlreadyExistsException_when_another_application_exists_with_the_same_name()\n            throws Exception {\n        //given\n        final EntityUpdateDescriptor updateDescriptor = new SApplicationUpdateBuilder(0L).updateToken(\"newToken\")\n                .done();\n\n        SApplicationWithIcon applicationToUpdate = new SApplicationWithIcon();\n        applicationToUpdate.setToken(\"oldToken\");\n        doReturn(applicationToUpdate).when(persistenceService).selectById(argThat(s -> s.getId() == 17));\n        SApplication existingApplication = new SApplication();\n        existingApplication.setToken(\"newToken\");\n        doReturn(existingApplication).when(persistenceService)\n                .selectOne(argThat(s -> s.getQueryName().equals(\"getApplicationByToken\") &&\n                        s.getInputParameter(\"token\").equals(\"newToken\")));\n\n        //when\n        applicationServiceImpl.updateApplication(17, updateDescriptor);\n\n        //then exception\n    }\n\n    @Test\n    public void getApplicationHomePage_should_return_result_of_persitence_service_selectOne() throws Exception {\n        //given\n        final SApplicationPage applicationPage = buildApplicationPage(10, 20, \"myPage\");\n        final Map<String, Object> inputParameters = new HashMap<>(2);\n        inputParameters.put(\"applicationId\", 100);\n        given(persistenceService\n                .selectOne(new SelectOneDescriptor<SApplicationPage>(\"getApplicationHomePage\", inputParameters,\n                        SApplicationPage.class)))\n                .willReturn(applicationPage);\n\n        //when\n        final SApplicationPage homePage = applicationServiceImpl.getApplicationHomePage(100);\n\n        //then\n        assertThat(homePage).isEqualTo(applicationPage);\n    }\n\n    @Test(expected = SObjectNotFoundException.class)\n    public void getApplicationHomePage_should_throw_SObjectNotFoundException_when_persitence_service_selectOne_returns_null()\n            throws Exception {\n        //given\n        final Map<String, Object> inputParameters = new HashMap<>(2);\n        inputParameters.put(\"applicationId\", 100);\n        given(persistenceService\n                .selectOne(new SelectOneDescriptor<SApplicationPage>(\"getApplicationHomePage\", inputParameters,\n                        SApplicationPage.class)))\n                .willReturn(null);\n\n        //when\n        applicationServiceImpl.getApplicationHomePage(100);\n\n        //then exception\n    }\n\n    @Test\n    public void getNumberOfApplicationPages_should_return_the_result_of_persitenceService_getNumberOfEntities()\n            throws Exception {\n        //given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long count = 7;\n        given(persistenceService.getNumberOfEntities(SApplicationPage.class, options, null)).willReturn(count);\n\n        //when\n        final long nbOfApp = applicationServiceImpl.getNumberOfApplicationPages(options);\n\n        //then\n        assertThat(nbOfApp).isEqualTo(count);\n    }\n\n    @Test\n    public void searchApplicationPages_should_return_the_result_of_persitenceService_searchEntity() throws Exception {\n        //given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final List<SApplicationPage> applicationPages = new ArrayList<>(1);\n        applicationPages.add(mock(SApplicationPage.class));\n        given(persistenceService.searchEntity(SApplicationPage.class, options, null)).willReturn(applicationPages);\n\n        //when\n        final List<SApplicationPage> retrievedAppPages = applicationServiceImpl.searchApplicationPages(options);\n\n        //then\n        assertThat(retrievedAppPages).isEqualTo(applicationPages);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void searchApplicationPages_should_throw_SBonitaReadException_when_persistenceSevice_throws_SBonitaReadException()\n            throws Exception {\n        //given\n        final QueryOptions options = new QueryOptions(0, 10);\n        given(applicationServiceImpl.searchApplicationPages(options)).willThrow(new SBonitaReadException(\"\"));\n\n        //when\n        applicationServiceImpl.searchApplicationPages(options);\n\n        //then exception\n    }\n\n    @Test\n    public void createApplicationMenu_should_call_recordInsert_and_return_created_object() throws Exception {\n        //given\n        final SApplicationMenu appMenu = buildApplicationMenu(\"main\", 5, 1, 12);\n        appMenu.setId(15);\n\n        //when\n        final SApplicationMenu createdAppMenu = applicationServiceImpl.createApplicationMenu(appMenu);\n\n        //then\n        assertThat(createdAppMenu).isEqualTo(appMenu);\n\n        final ArgumentCaptor<InsertRecord> insertRecordCaptor = ArgumentCaptor.forClass(InsertRecord.class);\n        verify(recorder, times(1)).recordInsert(insertRecordCaptor.capture(), eq(ApplicationService.APPLICATION_MENU));\n        assertThat(insertRecordCaptor.getValue().getEntity()).isEqualTo(appMenu);\n    }\n\n    private SApplicationMenu buildApplicationMenu(final String displayName, final long applicationPageId,\n            final int index, final long applicationId) {\n        return SApplicationMenu.builder().displayName(displayName).applicationId(applicationId)\n                .applicationPageId(applicationPageId).index(index).build();\n    }\n\n    @Test(expected = SObjectCreationException.class)\n    public void createApplicationMenu_should_throw_SObjectCreationException_when_recorder_throws_Exception()\n            throws Exception {\n        //given\n        doThrow(new SRecorderException(\"\")).when(recorder).recordInsert(any(InsertRecord.class), anyString());\n\n        //when\n        applicationServiceImpl.createApplicationMenu(buildApplicationMenu(\"main\", 4, 1, 12));\n\n        //then exception\n    }\n\n    @Test\n    public void updateApplicationMenu_should_call_recorder_recordUpdate_and_return_updated_object() throws Exception {\n        //given\n        final EntityUpdateDescriptor updateDescriptor = new SApplicationMenuUpdateBuilder()\n                .updateDisplayName(\"new display name\").done();\n\n        final SApplicationMenu appMenu = buildApplicationMenu(\"main\", 5, 1, 12);\n        appMenu.setId(17);\n\n        final int applicationMenuId = 17;\n        given(persistenceService.selectById(new SelectByIdDescriptor<>(SApplicationMenu.class, applicationMenuId)))\n                .willReturn(\n                        appMenu);\n\n        //when\n        final SApplicationMenu updatedApplicationMenu = applicationServiceImpl.updateApplicationMenu(applicationMenuId,\n                updateDescriptor);\n\n        //then\n        final UpdateRecord updateRecord = UpdateRecord.buildSetFields(appMenu,\n                updateDescriptor);\n        verify(recorder, times(1)).recordUpdate(updateRecord, ApplicationService.APPLICATION_MENU);\n        assertThat(updatedApplicationMenu).isEqualTo(appMenu);\n    }\n\n    @Test\n    public void updateApplicationMenu_should_organize_indexes_when_field_index_is_updated() throws Exception {\n        //given\n        final ApplicationServiceImpl applicationService = spy(applicationServiceImpl);\n        final int newIndexValue = 1;\n        final int oldIndexValue = 5;\n        final EntityUpdateDescriptor updateDescriptor = new SApplicationMenuUpdateBuilder().updateIndex(newIndexValue)\n                .done();\n\n        final int applicationMenuId = 17;\n        final SApplicationMenu appMenu = buildApplicationMenu(\"main\", 5, oldIndexValue, 12);\n        appMenu.setId(applicationMenuId);\n\n        doReturn(appMenu).when(applicationService).getApplicationMenu(applicationMenuId);\n\n        final MenuIndex newIndex = new MenuIndex(null, newIndexValue, 5);\n        final MenuIndex oldIndex = new MenuIndex(null, oldIndexValue, 6);\n        given(convertor.toMenuIndex(appMenu)).willReturn(oldIndex);\n        given(convertor.toMenuIndex(appMenu, updateDescriptor)).willReturn(newIndex);\n\n        //when\n        applicationService.updateApplicationMenu(applicationMenuId, updateDescriptor);\n\n        //then\n        verify(indexManager, times(1)).organizeIndexesOnUpdate(oldIndex, newIndex);\n    }\n\n    @Test\n    public void updateApplicationMenu_should_force_update_index_when_field_parent_is_updated() throws Exception {\n        //given\n        final ApplicationServiceImpl applicationService = spy(applicationServiceImpl);\n        final EntityUpdateDescriptor updateDescriptor = new SApplicationMenuUpdateBuilder().updateParentId(28L).done();\n\n        final int applicationMenuId = 17;\n        final SApplicationMenu appMenu = buildApplicationMenu(\"main\", 5, 4, 12);\n        appMenu.setId(applicationMenuId);\n\n        doReturn(appMenu).when(applicationService).getApplicationMenu(applicationMenuId);\n\n        final MenuIndex newIndex = new MenuIndex(28L, 6, 5);\n        final MenuIndex oldIndex = new MenuIndex(null, 4, 6);\n        given(convertor.toMenuIndex(appMenu)).willReturn(oldIndex);\n        given(convertor.toMenuIndex(appMenu, updateDescriptor)).willReturn(newIndex);\n        given(applicationService.getLastUsedIndex(28L)).willReturn(5);\n\n        //when\n        applicationService.updateApplicationMenu(applicationMenuId, updateDescriptor);\n\n        //then\n        final ArgumentCaptor<UpdateRecord> updateRecordCaptor = ArgumentCaptor.forClass(UpdateRecord.class);\n        verify(recorder, times(1)).recordUpdate(updateRecordCaptor.capture(), anyString());\n\n        final UpdateRecord updateRecord = updateRecordCaptor.getValue();\n        assertThat(updateRecord.getFields().get(SApplicationMenu.INDEX)).isEqualTo(6);\n    }\n\n    @Test\n    public void updateApplicationMenu_should_not_organize_indexes_when_field_index_is_not_updated() throws Exception {\n        //given\n        final EntityUpdateDescriptor updateDescriptor = new SApplicationMenuUpdateBuilder()\n                .updateDisplayName(\"new display name\").done();\n\n        final SApplicationMenu appMenu = buildApplicationMenu(\"main\", 5, 1, 12);\n        appMenu.setId(17);\n\n        final int applicationMenuId = 17;\n        given(persistenceService.selectById(new SelectByIdDescriptor<>(SApplicationMenu.class, applicationMenuId)))\n                .willReturn(\n                        appMenu);\n\n        //when\n        applicationServiceImpl.updateApplicationMenu(applicationMenuId, updateDescriptor);\n\n        //then\n        verifyNoInteractions(indexManager);\n    }\n\n    @Test(expected = SObjectNotFoundException.class)\n    public void updateApplicationMenu_should_throw_SObjectNotFoundException_when_recorder_returns_null()\n            throws Exception {\n        //given\n        final EntityUpdateDescriptor updateDescriptor = new SApplicationMenuUpdateBuilder()\n                .updateDisplayName(\"new display name\").done();\n\n        final int applicationMenuId = 17;\n        given(persistenceService.selectById(new SelectByIdDescriptor<>(SApplicationMenu.class, applicationMenuId)))\n                .willReturn(\n                        null);\n\n        //when\n        applicationServiceImpl.updateApplicationMenu(applicationMenuId, updateDescriptor);\n\n        //then exception\n    }\n\n    @Test(expected = SObjectModificationException.class)\n    public void updateApplicationMenu_should_throw_SObjectModificationException_when_recorder_throws_SRecorderException()\n            throws Exception {\n        //given\n        final EntityUpdateDescriptor updateDescriptor = new SApplicationMenuUpdateBuilder()\n                .updateDisplayName(\"new display name\").done();\n\n        final SApplicationMenu appMenu = buildApplicationMenu(\"main\", 5, 1, 12);\n        appMenu.setId(17);\n\n        final int applicationMenuId = 17;\n        given(persistenceService.selectById(new SelectByIdDescriptor<>(SApplicationMenu.class, applicationMenuId)))\n                .willReturn(\n                        appMenu);\n        doThrow(new SRecorderException(\"\")).when(recorder).recordUpdate(any(UpdateRecord.class), anyString());\n\n        //when\n        applicationServiceImpl.updateApplicationMenu(applicationMenuId, updateDescriptor);\n\n        //then exception\n    }\n\n    @Test\n    public void getApplicationMenu_by_id_should_return_result_of_persitence_service() throws Exception {\n        //given\n        final SApplicationMenu applicationMenu = buildApplicationMenu(\"main\", 2, 1, 12);\n        final SelectByIdDescriptor<SApplicationMenu> selectDescriptor = new SelectByIdDescriptor<>(\n                SApplicationMenu.class, 3);\n        given(persistenceService.selectById(selectDescriptor)).willReturn(applicationMenu);\n\n        //when\n        final SApplicationMenu retrievedAppMenu = applicationServiceImpl.getApplicationMenu(3);\n\n        //then\n        assertThat(retrievedAppMenu).isEqualTo(applicationMenu);\n    }\n\n    @Test(expected = SObjectNotFoundException.class)\n    public void getApplicationMenu_by_id_should_throw_SObjectNotFoundException_when_persistence_service_returns_null()\n            throws Exception {\n        //given\n        final SelectByIdDescriptor<SApplicationMenu> selectDescriptor = new SelectByIdDescriptor<>(\n                SApplicationMenu.class, 3);\n        given(persistenceService.selectById(selectDescriptor)).willReturn(null);\n\n        //when\n        applicationServiceImpl.getApplicationMenu(3);\n\n        //then exception\n    }\n\n    @Test\n    public void deleteApplicationMenu_should_delete_application_menu_identified_for_the_given_identifier()\n            throws Exception {\n        //given\n        final int applicationMenuId = 3;\n        final SApplicationMenu applicationMenu = buildApplicationMenu(\"main\", 2, 1, 12);\n        applicationMenu.setId(applicationMenuId);\n        final SelectByIdDescriptor<SApplicationMenu> selectDescriptor = new SelectByIdDescriptor<>(\n                SApplicationMenu.class, applicationMenuId);\n        given(persistenceService.selectById(selectDescriptor)).willReturn(applicationMenu);\n        final SelectOneDescriptor<Integer> descriptor = new SelectOneDescriptor<>(\"getLastIndexForRootMenu\",\n                Collections.emptyMap(),\n                SApplicationMenu.class);\n        given(persistenceService.selectOne(descriptor)).willReturn(2);\n\n        //when\n        applicationServiceImpl.deleteApplicationMenu(applicationMenuId);\n\n        //then\n        final ArgumentCaptor<DeleteRecord> deleteRecordCaptor = ArgumentCaptor.forClass(DeleteRecord.class);\n        verify(recorder, times(1)).recordDelete(deleteRecordCaptor.capture(), eq(ApplicationService.APPLICATION_MENU));\n        assertThat(deleteRecordCaptor.getValue().getEntity()).isEqualTo(applicationMenu);\n    }\n\n    @Test\n    public void deleteApplicationMenu_should_call_applicationMenuDestructor() throws Exception {\n        //given\n        final ApplicationServiceImpl applicationService = spy(applicationServiceImpl);\n\n        final int applicationMenuId = 3;\n        final SApplicationMenu applicationMenu = buildApplicationMenu(\"main\", 2, 1, 12);\n        applicationMenu.setId(applicationMenuId);\n\n        //when\n        applicationService.deleteApplicationMenu(applicationMenu);\n\n        //then\n        verify(applicationMenuDestructor, times(1)).onDeleteApplicationMenu(applicationMenu);\n    }\n\n    @Test\n    public void deleteApplicationMenu_without_parent_should_update_indexes() throws Exception {\n        //given\n        final int indexValue = 6;\n        final int lastUsedIndex = 10;\n        final int applicationId = 3;\n        final SApplicationMenu applicationMenu = buildApplicationMenu(\"main\", 2, indexValue, 12);\n        applicationMenu.setId(applicationId);\n        final SelectByIdDescriptor<SApplicationMenu> selectDescriptor = new SelectByIdDescriptor<>(\n                SApplicationMenu.class, applicationId);\n        given(persistenceService.selectById(selectDescriptor)).willReturn(applicationMenu);\n\n        final SelectOneDescriptor<Integer> descriptor = new SelectOneDescriptor<>(\"getLastIndexForRootMenu\",\n                Collections.emptyMap(),\n                SApplicationMenu.class);\n        given(persistenceService.selectOne(descriptor)).willReturn(lastUsedIndex);\n\n        final MenuIndex menuIndex = new MenuIndex(null, indexValue, lastUsedIndex);\n\n        //when\n        applicationServiceImpl.deleteApplicationMenu(applicationId);\n\n        //then\n        verify(indexManager, times(1)).organizeIndexesOnDelete(menuIndex);\n    }\n\n    @Test(expected = SObjectNotFoundException.class)\n    public void deleteApplicationMenu_should_throw_SObjectObjectNotFoundException_if_no_application_menu_is_found_with_the_given_id()\n            throws Exception {\n        //given\n        given(persistenceService.selectById(ArgumentMatchers.<SelectByIdDescriptor<SApplicationMenu>> any()))\n                .willReturn(null);\n\n        //when\n        applicationServiceImpl.deleteApplicationMenu(5);\n\n        //then exception\n    }\n\n    @Test(expected = SObjectModificationException.class)\n    public void deleteApplicationMenu_should_throw_SObjectObjectModificationException_recorder_throws_exception()\n            throws Exception {\n        //given\n        final SApplicationMenu applicationMenu = buildApplicationMenu(\"main\", 1, 1, 12);\n        given(persistenceService.selectById(ArgumentMatchers.<SelectByIdDescriptor<SApplicationMenu>> any()))\n                .willReturn(applicationMenu);\n\n        final SelectOneDescriptor<Integer> descriptor = new SelectOneDescriptor<>(\"getLastIndexForRootMenu\",\n                Collections.emptyMap(),\n                SApplicationMenu.class);\n        given(persistenceService.selectOne(descriptor)).willReturn(2);\n\n        doThrow(new SRecorderException(\"\")).when(recorder).recordDelete(any(DeleteRecord.class), anyString());\n\n        //when\n        applicationServiceImpl.deleteApplicationMenu(5);\n\n        //then exception\n    }\n\n    @Test\n    public void getNumberOfApplicationMenus_should_return_the_result_of_persistenc_service() throws Exception {\n        //given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final long count = 7;\n        given(persistenceService.getNumberOfEntities(SApplicationMenu.class, options, null)).willReturn(count);\n\n        //when\n        final long numberOfMenus = applicationServiceImpl.getNumberOfApplicationMenus(options);\n\n        //then\n        assertThat(numberOfMenus).isEqualTo(count);\n    }\n\n    @Test\n    public void searchApplicationMenus_should_return_the_result_of_persistence_service() throws Exception {\n        //given\n        final QueryOptions options = new QueryOptions(0, 2);\n        final List<SApplicationMenu> applicationMenus = new ArrayList<>(1);\n        applicationMenus.add(mock(SApplicationMenu.class));\n        given(persistenceService.searchEntity(SApplicationMenu.class, options, null)).willReturn(applicationMenus);\n\n        //when\n        final List<SApplicationMenu> retrievedAppMenus = applicationServiceImpl.searchApplicationMenus(options);\n\n        //then\n        assertThat(retrievedAppMenus).isEqualTo(applicationMenus);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void searchApplicationMenus_should_throw_SBonitaSearchException_when_persistence_service_throws_exception()\n            throws Exception {\n        //given\n        final QueryOptions options = new QueryOptions(0, 2);\n        given(persistenceService.searchEntity(SApplicationMenu.class, options, null))\n                .willThrow(new SBonitaReadException(\"\"));\n\n        //when\n        applicationServiceImpl.searchApplicationMenus(options);\n\n        //then exception\n    }\n\n    @Test\n    public void getNextIndex_should_return_getLastUsedIndex_more_one() throws Exception {\n        //given\n        final ApplicationServiceImpl applicationService = spy(applicationServiceImpl);\n        given(applicationService.getLastUsedIndex(4L)).willReturn(7);\n\n        //when\n        final int next = applicationService.getNextAvailableIndex(4L);\n\n        //then\n        assertThat(next).isEqualTo(8);\n    }\n\n    @Test\n    public void executeGetLastUsedIndexQuery_should_use_persistence_service_getLastIndexForRootMenu_if_parent_is_null()\n            throws Exception {\n        //given\n        final SelectOneDescriptor<Integer> descriptor = new SelectOneDescriptor<>(\"getLastIndexForRootMenu\",\n                Collections.emptyMap(),\n                SApplicationMenu.class);\n        given(persistenceService.selectOne(descriptor)).willReturn(1);\n\n        //when\n        final Integer result = applicationServiceImpl.executeGetLastUsedIndexQuery(null);\n\n        //then\n        assertThat(result).isEqualTo(1);\n    }\n\n    @Test\n    public void executeGetLastUsedIndexQuery_should_use_persistence_service_getLastIndexForChildOf_if_parent_is_not_null()\n            throws Exception {\n        //given\n        final long parentId = 10;\n        final SelectOneDescriptor<Integer> descriptor = new SelectOneDescriptor<>(\"getLastIndexForChildOf\",\n                Collections.singletonMap(\n                        SApplicationMenu.PARENT_ID, parentId),\n                SApplicationMenu.class);\n        given(persistenceService.selectOne(descriptor)).willReturn(1);\n\n        //when\n        final Integer result = applicationServiceImpl.executeGetLastUsedIndexQuery(parentId);\n\n        //then\n        assertThat(result).isEqualTo(1);\n    }\n\n    @Test\n    public void getLastUsedIndex_should_return_executeGetLastUsedIndexQuery_if_result_is_not_null() throws Exception {\n        //given\n        final ApplicationServiceImpl applicationService = spy(applicationServiceImpl);\n        given(applicationService.executeGetLastUsedIndexQuery(1L)).willReturn(4);\n\n        //when\n        final int lastUsedIndex = applicationService.getLastUsedIndex(1L);\n\n        //then\n        assertThat(lastUsedIndex).isEqualTo(4);\n    }\n\n    @Test\n    public void getLastUsedIndex_should_return_zero_executeGetLastUsedIndexQuery_if_result_is_null() throws Exception {\n        //given\n        final ApplicationServiceImpl applicationService = spy(applicationServiceImpl);\n        given(applicationService.executeGetLastUsedIndexQuery(1L)).willReturn(null);\n\n        //when\n        final int lastUsedIndex = applicationService.getLastUsedIndex(1L);\n\n        //then\n        assertThat(lastUsedIndex).isEqualTo(0);\n    }\n\n    @Test\n    public void getLastUsedIndex_should_return_zero_when_persistence_service_getLastIndexForChildOf_return_null()\n            throws Exception {\n        //given\n        final SelectOneDescriptor<Integer> descriptor = new SelectOneDescriptor<>(\"getLastIndexForRootMenu\",\n                Collections.emptyMap(),\n                SApplicationMenu.class);\n        given(persistenceService.selectOne(descriptor)).willReturn(null);\n\n        //when\n        final Integer lastUsedIndex = applicationServiceImpl.getLastUsedIndex(null);\n\n        //then\n        assertThat(lastUsedIndex).isEqualTo(0);\n    }\n\n    @Test\n    public void getLastUsedIndex_for_child_menu_should_return_zero_when_persistence_service_getLastIndexForChildOf_return_null()\n            throws Exception {\n        //given\n        final long parentId = 10;\n        final SelectOneDescriptor<Integer> descriptor = new SelectOneDescriptor<>(\"getLastIndexForChildOf\",\n                Collections.singletonMap(\n                        SApplicationMenu.PARENT_ID, parentId),\n                SApplicationMenu.class);\n        given(persistenceService.selectOne(descriptor)).willReturn(null);\n\n        //when\n        final int next = applicationServiceImpl.getLastUsedIndex(parentId);\n\n        //then\n        assertThat(next).isEqualTo(0);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/test/java/org/bonitasoft/engine/business/application/impl/HomePageCheckerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\n\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.model.SApplication;\nimport org.bonitasoft.engine.business.application.model.SApplicationPage;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class HomePageCheckerTest {\n\n    public static final long APPLICATION_ID = 10L;\n    public static final long APPLICATION_PAGE_ID = 2L;\n\n    @Mock\n    private ApplicationService applicationService;\n\n    @InjectMocks\n    private HomePageChecker checker;\n\n    @Mock\n    private SApplication application;\n\n    @Mock\n    private SApplicationPage page;\n\n    @Before\n    public void setUp() throws Exception {\n        given(page.getId()).willReturn(APPLICATION_PAGE_ID);\n        given(page.getApplicationId()).willReturn(APPLICATION_ID);\n        given(applicationService.getApplication(APPLICATION_ID)).willReturn(application);\n    }\n\n    @Test\n    public void isHomePage_should_return_true_if_application_home_page_id_is_equals_to_applicationPage_id()\n            throws Exception {\n        //given\n        given(application.getHomePageId()).willReturn(APPLICATION_PAGE_ID);\n\n        //when\n        boolean isHomePage = checker.isHomePage(page);\n\n        //then\n        assertThat(isHomePage).isTrue();\n    }\n\n    @Test\n    public void isHomePage_should_return_false_if_application_home_page_id_is_different_of_applicationPage_id()\n            throws Exception {\n        //given\n        given(application.getHomePageId()).willReturn(1L);\n\n        //when\n        boolean isHomePage = checker.isHomePage(page);\n\n        //then\n        assertThat(isHomePage).isFalse();\n    }\n\n    @Test\n    public void isHomePage_should_return_false_if_application_home_page_id_is_null() throws Exception {\n        //given\n        given(application.getHomePageId()).willReturn(null);\n\n        //when\n        boolean isHomePage = checker.isHomePage(page);\n\n        //then\n        assertThat(isHomePage).isFalse();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/test/java/org/bonitasoft/engine/business/application/impl/IndexManagerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.impl;\n\nimport static org.mockito.ArgumentMatchers.anyInt;\nimport static org.mockito.ArgumentMatchers.anyLong;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\n\nimport java.util.Arrays;\n\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class IndexManagerTest {\n\n    @Mock\n    private IndexUpdater indexUpdater;\n\n    @Mock\n    private MenuIndexValidator validator;\n\n    @InjectMocks\n    private IndexManager indexManager;\n\n    @Test\n    public void organizeIndexesOnDelete_should_call_decrement_indexes() throws Exception {\n        //given\n        MenuIndex menuIndex = new MenuIndex(1L, 2, 9);\n\n        //when\n        indexManager.organizeIndexesOnDelete(menuIndex);\n\n        //then\n        verify(indexUpdater).decrementIndexes(menuIndex.getParentId(), menuIndex.getValue() + 1,\n                menuIndex.getLastUsedIndex());\n        verify(indexUpdater, never()).incrementIndexes(anyLong(), anyInt(), anyInt());\n    }\n\n    @Test\n    public void organizeIndexesOnUpdate_should_call_increment_index_when_move_up_and_parent_doesnt_change()\n            throws Exception {\n        //given\n        MenuIndex oldIndex = new MenuIndex(1L, 6, 15);\n        MenuIndex newIndex = new MenuIndex(1L, 2, 15);\n\n        //when\n        indexManager.organizeIndexesOnUpdate(oldIndex, newIndex);\n\n        //then\n        verify(indexUpdater).incrementIndexes(oldIndex.getParentId(), newIndex.getValue(), oldIndex.getValue() - 1);\n        verify(indexUpdater, never()).decrementIndexes(anyLong(), anyInt(), anyInt());\n    }\n\n    @Test\n    public void organizeIndexesOnUpdate_should_call_decrement_index_when_move_down_and_parent_doesnt_change()\n            throws Exception {\n        //given\n        MenuIndex oldIndex = new MenuIndex(1L, 1, 15);\n        MenuIndex newIndex = new MenuIndex(1L, 5, 15);\n\n        //when\n        indexManager.organizeIndexesOnUpdate(oldIndex, newIndex);\n\n        //then\n        verify(indexUpdater).decrementIndexes(1L, oldIndex.getValue() + 1, newIndex.getValue());\n        verify(indexUpdater, never()).incrementIndexes(anyLong(), anyInt(), anyInt());\n    }\n\n    @Test\n    public void organizeIndexesOnUpdate_should_call_increment_on_new_parent_and_decrement_on_old_parent_when_parent_changes()\n            throws Exception {\n        MenuIndex oldIndex = new MenuIndex(1L, 2, 15);\n        MenuIndex newIndex = new MenuIndex(2L, 6, 9);\n\n        //when\n        indexManager.organizeIndexesOnUpdate(oldIndex, newIndex);\n\n        //then\n        verify(indexUpdater).decrementIndexes(oldIndex.getParentId(), oldIndex.getValue() + 1,\n                oldIndex.getLastUsedIndex());\n        verify(indexUpdater).incrementIndexes(newIndex.getParentId(), newIndex.getValue(), newIndex.getLastUsedIndex());\n    }\n\n    @Test(expected = SObjectModificationException.class)\n    public void organizeIndexesOnUpdate_should_throws_SObjectModificationException_when_new_menuIndex_is_invalid()\n            throws Exception {\n        //given\n        MenuIndex oldIndex = new MenuIndex(1L, 1, 15);\n        MenuIndex newIndex = new MenuIndex(1L, 5, 4);\n        given(validator.validate(oldIndex, newIndex)).willReturn(Arrays.asList(\"invalid\"));\n\n        //when\n        indexManager.organizeIndexesOnUpdate(oldIndex, newIndex);\n\n        //then exception\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/test/java/org/bonitasoft/engine/business/application/impl/IndexUpdaterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.impl;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyBoolean;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.model.SApplicationMenu;\nimport org.bonitasoft.engine.business.application.model.builder.SApplicationMenuUpdateBuilder;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class IndexUpdaterTest {\n\n    public static final int MAX_RESULTS = 2;\n\n    @Mock\n    private ApplicationService applicationService;\n\n    @Mock\n    private SApplicationMenu menu3;\n\n    @Mock\n    private SApplicationMenu menu4;\n\n    @Mock\n    private SApplicationMenu menu5;\n\n    private IndexUpdater indexUpdater;\n\n    @Before\n    public void setUp() {\n        indexUpdater = new IndexUpdater(applicationService, MAX_RESULTS);\n\n        given(menu3.getIndex()).willReturn(3);\n        given(menu4.getIndex()).willReturn(4);\n        given(menu5.getIndex()).willReturn(5);\n    }\n\n    @Test\n    public void incrementIndexes_should_increment_indexes_of_all_elements_in_the_specified_interval() throws Exception {\n        //given\n        List<OrderByOption> orderBy = Collections\n                .singletonList(new OrderByOption(SApplicationMenu.class, SApplicationMenu.INDEX, OrderByType.ASC));\n        List<FilterOption> filters = Arrays\n                .asList(new FilterOption(SApplicationMenu.class, SApplicationMenu.INDEX, 3, 5), new FilterOption(\n                        SApplicationMenu.class, SApplicationMenu.PARENT_ID, 1L));\n\n        given(applicationService.searchApplicationMenus(new QueryOptions(0, MAX_RESULTS, orderBy, filters, null)))\n                .willReturn(Arrays.asList(menu3, menu4));\n        given(applicationService\n                .searchApplicationMenus(new QueryOptions(MAX_RESULTS, MAX_RESULTS, orderBy, filters, null)))\n                .willReturn(Arrays.asList(menu5));\n\n        //when\n        indexUpdater.incrementIndexes(1L, 3, 5);\n\n        //then\n\n        verify(applicationService).updateApplicationMenu(menu3, getUpdateDescriptorForIndex(4), false);\n        verify(applicationService).updateApplicationMenu(menu4, getUpdateDescriptorForIndex(5), false);\n        verify(applicationService).updateApplicationMenu(menu5, getUpdateDescriptorForIndex(6), false);\n    }\n\n    @Test\n    public void incrementIndexes_should_do_nothing_when_from_is_greater_then_to() throws Exception {\n        //when\n        indexUpdater.incrementIndexes(1L, 4, 3);\n\n        //then\n        verify(applicationService, never()).searchApplicationMenus(any(QueryOptions.class));\n        verify(applicationService, never()).updateApplicationMenu(any(SApplicationMenu.class),\n                any(EntityUpdateDescriptor.class), anyBoolean());\n    }\n\n    private EntityUpdateDescriptor getUpdateDescriptorForIndex(int newIndex) {\n        return new SApplicationMenuUpdateBuilder().updateIndex(newIndex).done();\n    }\n\n    @Test\n    public void decrementIndexes_should_decrement_indexes_of_all_elements_in_the_specified_interval() throws Exception {\n        //given\n        List<OrderByOption> orderBy = Collections\n                .singletonList(new OrderByOption(SApplicationMenu.class, SApplicationMenu.INDEX, OrderByType.ASC));\n        List<FilterOption> filters = Arrays\n                .asList(new FilterOption(SApplicationMenu.class, SApplicationMenu.INDEX, 3, 5), new FilterOption(\n                        SApplicationMenu.class, SApplicationMenu.PARENT_ID, 1L));\n\n        given(applicationService.searchApplicationMenus(new QueryOptions(0, MAX_RESULTS, orderBy, filters, null)))\n                .willReturn(Arrays.asList(menu3, menu4));\n        given(applicationService\n                .searchApplicationMenus(new QueryOptions(MAX_RESULTS, MAX_RESULTS, orderBy, filters, null)))\n                .willReturn(Arrays.asList(menu5));\n\n        //when\n        indexUpdater.decrementIndexes(1L, 3, 5);\n\n        //then\n\n        verify(applicationService).updateApplicationMenu(menu3, getUpdateDescriptorForIndex(2), false);\n        verify(applicationService).updateApplicationMenu(menu4, getUpdateDescriptorForIndex(3), false);\n        verify(applicationService).updateApplicationMenu(menu5, getUpdateDescriptorForIndex(4), false);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/test/java/org/bonitasoft/engine/business/application/impl/MenuIndexValidatorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.List;\n\nimport org.junit.Test;\n\npublic class MenuIndexValidatorTest {\n\n    private MenuIndexValidator validator = new MenuIndexValidator();\n\n    @Test\n    public void isValid_should_return_problem_when_new_index_is_less_than_or_equal_zero() {\n        //given\n        MenuIndex oldIndex = new MenuIndex(null, 2, 5);\n        MenuIndex newIndex = new MenuIndex(null, 0, 5);\n\n        //when\n        List<String> problems = validator.validate(oldIndex, newIndex);\n\n        //then\n        assertThat(problems)\n                .containsExactly(\n                        \"Invalid menu index: 0. It must be between 1 and the number of menu in your application having the same parent. The last valid index for parent null is 5\");\n    }\n\n    @Test\n    public void isValid_should_return_problem_when_parent_is_same_and_new_index_is_greater_than_last_used_index() {\n        //given\n        MenuIndex oldIndex = new MenuIndex(null, 5, 5);\n        MenuIndex newIndex = new MenuIndex(null, 6, 5);\n\n        //when\n        List<String> problems = validator.validate(oldIndex, newIndex);\n\n        //then\n        assertThat(problems)\n                .containsExactly(\n                        \"Invalid menu index: 6. It must be between 1 and the number of menu in your application having the same parent. The last valid index for parent null is 5\");\n    }\n\n    @Test\n    public void isValid_should_return_no_problems_when_parent_is_not_the_same_and_new_index_is_greater_than_last_used_index_by_one() {\n        //given\n        MenuIndex oldIndex = new MenuIndex(null, 5, 5);\n        MenuIndex newIndex = new MenuIndex(2L, 4, 3);\n\n        //when\n        List<String> problems = validator.validate(oldIndex, newIndex);\n\n        //then\n        assertThat(problems).isEmpty();\n    }\n\n    @Test\n    public void isValid_should_return_problem_when_parent_is_not_the_same_and_new_index_is_greater_than_last_used_index_by_more_than_one() {\n        //given\n        MenuIndex oldIndex = new MenuIndex(null, 5, 5);\n        MenuIndex newIndex = new MenuIndex(2L, 5, 3);\n\n        //when\n        List<String> problems = validator.validate(oldIndex, newIndex);\n\n        //then\n        assertThat(problems).containsExactly(\n                \"Invalid menu index: 5. It must be between 1 and the number of menu in your application having the same parent. The last valid index for parent 2 is 4\");\n    }\n\n    @Test\n    public void isValid_should_return_true_when_new_index_is_valid() {\n        //given\n        MenuIndex oldIndex = new MenuIndex(null, 5, 5);\n        MenuIndex newIndex = new MenuIndex(null, 4, 5);\n\n        //when\n        List<String> problems = validator.validate(oldIndex, newIndex);\n\n        //then\n        assertThat(problems).isEmpty();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/test/java/org/bonitasoft/engine/business/application/impl/cleaner/ApplicationDestructorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.impl.cleaner;\n\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\nimport org.bonitasoft.engine.business.application.impl.ApplicationServiceImpl;\nimport org.bonitasoft.engine.business.application.impl.filter.ApplicationRelatedMenusFilterBuilder;\nimport org.bonitasoft.engine.business.application.impl.filter.SelectRange;\nimport org.bonitasoft.engine.business.application.model.SApplication;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ApplicationDestructorTest {\n\n    public static final long APPLICATION_ID = 5L;\n\n    @Mock\n    private ApplicationMenuCleaner applicationMenuCleaner;\n\n    @InjectMocks\n    private ApplicationDestructor applicationDestructor;\n\n    @Test\n    public void onDeleteApplication_should_call_applicationMenuCleaner() throws Exception {\n        //when\n        SApplication application = new SApplication();\n        application.setId(APPLICATION_ID);\n        applicationDestructor.onDeleteApplication(application);\n\n        //then\n        verify(applicationMenuCleaner, times(1)).deleteRelatedApplicationMenus(new ApplicationRelatedMenusFilterBuilder(\n                new SelectRange(0, ApplicationServiceImpl.MAX_RESULTS), APPLICATION_ID));\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/test/java/org/bonitasoft/engine/business/application/impl/cleaner/ApplicationMenuCleanerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.impl.cleaner;\n\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Arrays;\n\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.impl.filter.FilterBuilder;\nimport org.bonitasoft.engine.business.application.model.SApplicationMenu;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ApplicationMenuCleanerTest {\n\n    @Mock\n    private ApplicationService applicationService;\n\n    @Mock\n    private FilterBuilder filterBuilder;\n\n    @Mock\n    private QueryOptions options;\n\n    private static int MAX_RESULTS = 2;\n\n    @InjectMocks\n    private ApplicationMenuCleaner cleaner;\n\n    @Before\n    public void setUp() {\n        given(options.getNumberOfResults()).willReturn(MAX_RESULTS);\n    }\n\n    @Test\n    public void deleteApplicationPage_should_delete_related_applicationMenus() throws Exception {\n        //given\n        SApplicationMenu menu1 = mock(SApplicationMenu.class);\n        SApplicationMenu menu2 = mock(SApplicationMenu.class);\n        SApplicationMenu menu3 = mock(SApplicationMenu.class);\n        given(filterBuilder.buildQueryOptions()).willReturn(options);\n        given(applicationService.searchApplicationMenus(options)).willReturn(Arrays.asList(menu1, menu2))\n                .willReturn(Arrays.asList(menu3));\n\n        //when\n        cleaner.deleteRelatedApplicationMenus(filterBuilder);\n\n        //then\n        verify(applicationService, times(1)).deleteApplicationMenu(menu1);\n        verify(applicationService, times(1)).deleteApplicationMenu(menu2);\n        verify(applicationService, times(1)).deleteApplicationMenu(menu3);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/test/java/org/bonitasoft/engine/business/application/impl/cleaner/ApplicationMenuDestructorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.impl.cleaner;\n\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.*;\n\nimport org.bonitasoft.engine.business.application.impl.ApplicationServiceImpl;\nimport org.bonitasoft.engine.business.application.impl.filter.ChildrenMenusFilterBuilder;\nimport org.bonitasoft.engine.business.application.impl.filter.SelectRange;\nimport org.bonitasoft.engine.business.application.model.SApplicationMenu;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ApplicationMenuDestructorTest {\n\n    @Mock\n    private ApplicationMenuCleaner cleaner;\n\n    @InjectMocks\n    private ApplicationMenuDestructor destructor;\n\n    @Test\n    public void onDeleteApplicationMenu_should_clean_children_menus() throws Exception {\n        //given\n        long applicationMenuId = 3L;\n        SApplicationMenu menu = mock(SApplicationMenu.class);\n        given(menu.getId()).willReturn(applicationMenuId);\n\n        //when\n        destructor.onDeleteApplicationMenu(menu);\n\n        //then\n        verify(cleaner, times(1)).deleteRelatedApplicationMenus(new ChildrenMenusFilterBuilder(\n                new SelectRange(0, ApplicationServiceImpl.MAX_RESULTS), applicationMenuId));\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/test/java/org/bonitasoft/engine/business/application/impl/cleaner/ApplicationPageDestructorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.impl.cleaner;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.*;\n\nimport org.assertj.core.api.Assertions;\nimport org.bonitasoft.engine.business.application.impl.ApplicationServiceImpl;\nimport org.bonitasoft.engine.business.application.impl.HomePageChecker;\nimport org.bonitasoft.engine.business.application.impl.filter.ApplicationPageRelatedMenusFilterBuilder;\nimport org.bonitasoft.engine.business.application.impl.filter.SelectRange;\nimport org.bonitasoft.engine.business.application.model.SApplicationPage;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ApplicationPageDestructorTest {\n\n    public static final long APPLICATION_PAGE_ID = 2L;\n\n    @Mock\n    private ApplicationMenuCleaner applicationMenuCleaner;\n\n    @Mock\n    private HomePageChecker homePageChecker;\n\n    @InjectMocks\n    private ApplicationPageDestructor destructor;\n\n    @Test\n    public void onDeleteApplicationPage_should_call_applicationMenuCleaner() throws Exception {\n        //given\n        SApplicationPage applicationPage = mock(SApplicationPage.class);\n        given(applicationPage.getId()).willReturn(APPLICATION_PAGE_ID);\n\n        //when\n        destructor.onDeleteApplicationPage(applicationPage);\n\n        //then\n        verify(applicationMenuCleaner, times(1)).deleteRelatedApplicationMenus(\n                new ApplicationPageRelatedMenusFilterBuilder(new SelectRange(0, ApplicationServiceImpl.MAX_RESULTS),\n                        APPLICATION_PAGE_ID));\n    }\n\n    @Test\n    public void onDeleteApplicationPage_should_throw_SObjectModificationException_when_homePageChecker_returns_true()\n            throws Exception {\n        //given\n        SApplicationPage applicationPage = mock(SApplicationPage.class);\n        given(applicationPage.getId()).willReturn(APPLICATION_PAGE_ID);\n        given(homePageChecker.isHomePage(applicationPage)).willReturn(true);\n\n        try {\n            //when\n            destructor.onDeleteApplicationPage(applicationPage);\n            Assertions.fail(\"Exception expected\");\n        } catch (SObjectModificationException e) {\n            //then\n            assertThat(e.getMessage()).isEqualTo(\"The application page with id '\" + APPLICATION_PAGE_ID\n                    + \"' cannot be deleted because it is set as the application home page\");\n        }\n\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/test/java/org/bonitasoft/engine/business/application/impl/converter/MenuIndexConverterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.impl.converter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\n\nimport org.bonitasoft.engine.business.application.ApplicationService;\nimport org.bonitasoft.engine.business.application.impl.MenuIndex;\nimport org.bonitasoft.engine.business.application.model.SApplicationMenu;\nimport org.bonitasoft.engine.business.application.model.builder.SApplicationMenuUpdateBuilder;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class MenuIndexConverterTest {\n\n    @Mock\n    private ApplicationService applicationService;\n\n    @InjectMocks\n    private MenuIndexConverter convertor;\n\n    @Test\n    public void toMenuIndex_should_return_a_MenuIndex_based_on_ApplicationMenu_and_set_lastUsedIndex()\n            throws Exception {\n        //given\n        SApplicationMenu appMenu = new SApplicationMenu(\"my menu\", 1, null, 2);\n        appMenu.setParentId(20L);\n        given(applicationService.getLastUsedIndex(appMenu.getParentId())).willReturn(11);\n\n        //when\n        MenuIndex menuIndex = convertor.toMenuIndex(appMenu);\n\n        //then\n        assertThat(menuIndex).isNotNull();\n        assertThat(menuIndex.getValue()).isEqualTo(2);\n        assertThat(menuIndex.getParentId()).isEqualTo(20L);\n        assertThat(menuIndex.getLastUsedIndex()).isEqualTo(11);\n    }\n\n    @Test\n    public void toMenuIndex_with_updateDescriptor_should_reuse_app_menu_parentId_when_parent_doesnt_change()\n            throws Exception {\n        //given\n        SApplicationMenu appMenu = new SApplicationMenu(\"my menu\", 1, null, 2);\n        appMenu.setParentId(20L);\n\n        given(applicationService.getLastUsedIndex(appMenu.getParentId())).willReturn(11);\n\n        //when\n        MenuIndex menuIndex = convertor.toMenuIndex(appMenu, new SApplicationMenuUpdateBuilder().updateIndex(5).done());\n\n        //then\n        assertThat(menuIndex).isNotNull();\n        assertThat(menuIndex.getValue()).isEqualTo(5);\n        assertThat(menuIndex.getParentId()).isEqualTo(20L);\n        assertThat(menuIndex.getLastUsedIndex()).isEqualTo(11);\n    }\n\n    @Test\n    public void toMenuIndex_with_updateDescriptor_should_use_new_parent_when_parent_changes() throws Exception {\n        //given\n        SApplicationMenu appMenu = new SApplicationMenu(\"my menu\", 1, null, 2);\n        appMenu.setParentId(20L);\n\n        given(applicationService.getLastUsedIndex(7L)).willReturn(11);\n\n        //when\n        MenuIndex menuIndex = convertor.toMenuIndex(appMenu,\n                new SApplicationMenuUpdateBuilder().updateIndex(5).updateParentId(7L).done());\n\n        //then\n        assertThat(menuIndex).isNotNull();\n        assertThat(menuIndex.getValue()).isEqualTo(5);\n        assertThat(menuIndex.getParentId()).isEqualTo(7L);\n        assertThat(menuIndex.getLastUsedIndex()).isEqualTo(11);\n    }\n\n    @Test\n    public void toMenuIndex_with_updateDescriptor_should_reuse_app_menu_index_when_index_and_parent_dont_change()\n            throws Exception {\n        //given\n        SApplicationMenu appMenu = new SApplicationMenu(\"my menu\", 1, null, 2);\n        appMenu.setParentId(20L);\n\n        given(applicationService.getLastUsedIndex(4L)).willReturn(11);\n\n        //when\n        MenuIndex menuIndex = convertor.toMenuIndex(appMenu,\n                new SApplicationMenuUpdateBuilder().updateParentId(4L).done());\n\n        //then\n        assertThat(menuIndex).isNotNull();\n        assertThat(menuIndex.getValue()).isEqualTo(2);\n        assertThat(menuIndex.getParentId()).isEqualTo(4L);\n        assertThat(menuIndex.getLastUsedIndex()).isEqualTo(11);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/test/java/org/bonitasoft/engine/business/application/impl/filter/ApplicationPageRelatedMenusFilterBuilderTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.impl.filter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.business.application.model.SApplicationMenu;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.junit.Test;\n\npublic class ApplicationPageRelatedMenusFilterBuilderTest {\n\n    public static final int START_INDEX = 0;\n    public static final int MAX_RESULTS = 10;\n\n    @Test\n    public void buildQueryOptions_should_filter_on_applicationPageId() {\n        //given\n        long applicationPageId = 2L;\n        ApplicationPageRelatedMenusFilterBuilder builder = new ApplicationPageRelatedMenusFilterBuilder(\n                new SelectRange(START_INDEX, MAX_RESULTS), applicationPageId);\n\n        //when\n        QueryOptions options = builder.buildQueryOptions();\n\n        //then\n        assertThat(options).isNotNull();\n        assertThat(options.getFromIndex()).isEqualTo(START_INDEX);\n        assertThat(options.getNumberOfResults()).isEqualTo(MAX_RESULTS);\n        assertThat(options.getOrderByOptions())\n                .containsExactly(new OrderByOption(SApplicationMenu.class, SApplicationMenu.ID, OrderByType.ASC));\n        assertThat(options.getFilters()).containsExactly(\n                new FilterOption(SApplicationMenu.class, SApplicationMenu.APPLICATION_PAGE_ID, applicationPageId));\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/test/java/org/bonitasoft/engine/business/application/impl/filter/ApplicationRelatedMenusFilterBuilderTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.impl.filter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.business.application.model.SApplicationMenu;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.junit.Test;\n\npublic class ApplicationRelatedMenusFilterBuilderTest {\n\n    public static final int START_INDEX = 0;\n    public static final int MAX_RESULTS = 10;\n\n    @Test\n    public void buildQueryOptions_should_filter_on_applicationId() {\n        //given\n        long applicationId = 1;\n        ApplicationRelatedMenusFilterBuilder builder = new ApplicationRelatedMenusFilterBuilder(\n                new SelectRange(START_INDEX, MAX_RESULTS), applicationId);\n\n        //when\n        QueryOptions options = builder.buildQueryOptions();\n\n        //then\n        assertThat(options).isNotNull();\n        assertThat(options.getFromIndex()).isEqualTo(START_INDEX);\n        assertThat(options.getNumberOfResults()).isEqualTo(MAX_RESULTS);\n        assertThat(options.getOrderByOptions())\n                .containsExactly(new OrderByOption(SApplicationMenu.class, SApplicationMenu.ID, OrderByType.ASC));\n        FilterOption appFilter = new FilterOption(SApplicationMenu.class, SApplicationMenu.APPLICAITON_ID,\n                applicationId);\n        FilterOption parentFilter = new FilterOption(SApplicationMenu.class, SApplicationMenu.PARENT_ID, null);\n        assertThat(options.getFilters()).containsExactly(appFilter, parentFilter);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/test/java/org/bonitasoft/engine/business/application/impl/filter/ChildrenMenusFilterBuilderTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.impl.filter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.business.application.model.SApplicationMenu;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.junit.Test;\n\npublic class ChildrenMenusFilterBuilderTest {\n\n    public static final int START_INDEX = 0;\n    public static final int MAX_RESULTS = 10;\n\n    @Test\n    public void build_query_options_should_filter_on_parent_id() {\n        //given\n        long parentId = 4L;\n        ChildrenMenusFilterBuilder builder = new ChildrenMenusFilterBuilder(new SelectRange(START_INDEX, MAX_RESULTS),\n                parentId);\n\n        //when\n        QueryOptions options = builder.buildQueryOptions();\n\n        //then\n        assertThat(options).isNotNull();\n        assertThat(options.getFromIndex()).isEqualTo(START_INDEX);\n        assertThat(options.getNumberOfResults()).isEqualTo(MAX_RESULTS);\n        assertThat(options.getOrderByOptions())\n                .containsExactly(new OrderByOption(SApplicationMenu.class, SApplicationMenu.ID, OrderByType.ASC));\n        assertThat(options.getFilters())\n                .containsExactly(new FilterOption(SApplicationMenu.class, SApplicationMenu.PARENT_ID, parentId));\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/test/java/org/bonitasoft/engine/business/application/impl/filter/SelectRangeTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.impl.filter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.Test;\n\npublic class SelectRangeTest {\n\n    @Test\n    public void getStartIndex_should_return_startIndex_used_in_constructor() {\n        //given\n        SelectRange range = new SelectRange(5, 10);\n\n        //then\n        assertThat(range.getStartIndex()).isEqualTo(5);\n    }\n\n    @Test\n    public void getMaxResults_should_return_maxResults_used_in_constructor() {\n        //given\n        SelectRange range = new SelectRange(5, 10);\n\n        //then\n        assertThat(range.getMaxResults()).isEqualTo(10);\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/test/java/org/bonitasoft/engine/business/application/model/SApplicationWithIconTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.model;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.Test;\n\npublic class SApplicationWithIconTest {\n\n    @Test\n    public void constructor_and_setter_are_ok() {\n        //given\n        long creationDate = System.currentTimeMillis();\n        String state = SApplicationState.ACTIVATED.name();\n        long createdBy = 1L;\n        long homePageId = 5L;\n        long profileId = 10L;\n        long updatedBy = 2L;\n        long layoutId = 20L;\n        long themeId = 21L;\n\n        //when\n        SApplicationWithIcon application = new SApplicationWithIcon(\"token\", \"Name to display\", \"1.0\",\n                creationDate, createdBy, state);\n        application.setDescription(\"This is my application\");\n        application.setLayoutId(layoutId);\n        application.setThemeId(themeId);\n        application.setHomePageId(homePageId);\n        application.setIconPath(\"/icon.jpg\");\n        application.setLastUpdateDate(creationDate + 1);\n        application.setProfileId(profileId);\n        application.setUpdatedBy(updatedBy);\n\n        //then\n        assertThat(application.getToken()).isEqualTo(\"token\");\n        assertThat(application.getDisplayName()).isEqualTo(\"Name to display\");\n        assertThat(application.getVersion()).isEqualTo(\"1.0\");\n        assertThat(application.getCreationDate()).isEqualTo(creationDate);\n        assertThat(application.getCreatedBy()).isEqualTo(createdBy);\n        assertThat(application.getState()).isEqualTo(state);\n        assertThat(application.getDescription()).isEqualTo(\"This is my application\");\n        assertThat(application.getHomePageId()).isEqualTo(homePageId);\n        assertThat(application.getIconPath()).isEqualTo(\"/icon.jpg\");\n        assertThat(application.getLastUpdateDate()).isEqualTo(creationDate + 1);\n        assertThat(application.getProfileId()).isEqualTo(profileId);\n        assertThat(application.getUpdatedBy()).isEqualTo(updatedBy);\n        assertThat(application.getLayoutId()).isEqualTo(layoutId);\n        assertThat(application.getThemeId()).isEqualTo(themeId);;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-application/src/test/java/org/bonitasoft/engine/business/application/model/builder/SApplicationUpdateBuilderTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.application.model.builder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.business.application.model.AbstractSApplication;\nimport org.bonitasoft.engine.business.application.model.SApplicationState;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.junit.Test;\n\npublic class SApplicationUpdateBuilderTest {\n\n    @Test\n    public void constructorShouldUpdateUpdatedByAndLastUpdateDateFields() {\n        //given\n        long homePageId = 4L;\n        long updaterUserId = 17L;\n        long profileId = 20L;\n        long layoutId = 25L;\n        long themeId = 26L;\n\n        //when\n        SApplicationUpdateBuilder builder = new SApplicationUpdateBuilder(updaterUserId);\n        builder.updateDescription(\"new desc\");\n        builder.updateHomePageId(homePageId);\n        builder.updateDisplayName(\"new display name\");\n        builder.updateIconPath(\"/icon.jpg\");\n        builder.updateProfileId(profileId);\n        builder.updateState(SApplicationState.DEACTIVATED.name());\n        builder.updateToken(\"newToken\");\n        builder.updateVersion(\"2.0\");\n        builder.updateLayoutId(layoutId);\n        builder.updateThemeId(themeId);\n\n        //then\n        final EntityUpdateDescriptor desc = builder.done();\n        final Map<String, Object> fields = desc.getFields();\n        assertThat(fields).hasSize(12);\n        assertThat(fields.get(AbstractSApplication.UPDATED_BY)).isEqualTo(updaterUserId);\n        assertThat(fields.get(AbstractSApplication.LAST_UPDATE_DATE)).isNotNull();\n        assertThat(fields.get(AbstractSApplication.DESCRIPTION)).isEqualTo(\"new desc\");\n        assertThat(fields.get(AbstractSApplication.HOME_PAGE_ID)).isEqualTo(homePageId);\n        assertThat(fields.get(AbstractSApplication.DISPLAY_NAME)).isEqualTo(\"new display name\");\n        assertThat(fields.get(AbstractSApplication.ICON_PATH)).isEqualTo(\"/icon.jpg\");\n        assertThat(fields.get(AbstractSApplication.PROFILE_ID)).isEqualTo(profileId);\n        assertThat(fields.get(AbstractSApplication.STATE)).isEqualTo(SApplicationState.DEACTIVATED.name());\n        assertThat(fields.get(AbstractSApplication.TOKEN)).isEqualTo(\"newToken\");\n        assertThat(fields.get(AbstractSApplication.VERSION)).isEqualTo(\"2.0\");\n        assertThat(fields.get(AbstractSApplication.LAYOUT_ID)).isEqualTo(layoutId);\n        assertThat(fields.get(AbstractSApplication.THEME_ID)).isEqualTo(themeId);\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-api/build.gradle",
    "content": "\n\ndependencies {\n    api project(':services:bonita-commons')\n    api libs.javassist\n    testImplementation libs.assertj\n    testImplementation libs.mockitoCore\n    testImplementation libs.logback\n    compileOnly project(':bpm:bonita-common')\n    testImplementation project(':bpm:bonita-common')\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-api/src/main/java/org/bonitasoft/engine/business/data/BusinessDataModelRepository.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data;\n\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\n\n/**\n * @author Colin PUY\n */\npublic interface BusinessDataModelRepository {\n\n    /**\n     * Deploys a Business Data Model / repository\n     *\n     * @param bdmArchive\n     *        the Business Data Model, as a jar containing the Business Object classes to deploy.\n     * @param userId the ID of the user installing the BDM, typically tech admin (id=-1)\n     * @return the version of the BDM just deployed.\n     * @throws SBusinessDataRepositoryDeploymentException\n     *         if a deployment exception occurs.\n     */\n    String install(byte[] bdmArchive, long userId)\n            throws SBusinessDataRepositoryDeploymentException, InvalidBusinessDataModelException;\n\n    /**\n     * Undeploy Business Data Model\n     *\n     * @throws SBusinessDataRepositoryException if error occurs during undeployement\n     */\n    void uninstall() throws SBusinessDataRepositoryException;\n\n    boolean isBDMDeployed();\n\n    /**\n     * Retrieve the client side BDM generated zip.\n     * This zip contains jars with BDM Pojos and DAOs\n     *\n     * @return the zip content\n     * @throws SBusinessDataRepositoryException\n     */\n    byte[] getClientBDMZip() throws SBusinessDataRepositoryException;\n\n    void dropAndUninstall() throws SBusinessDataRepositoryException;\n\n    /**\n     * Returns the currently deployed BDM version, or null if no BDM is deployed.\n     *\n     * @return the currently deployed BDM version, or null if no BDM is deployed.\n     * @throws SBusinessDataRepositoryException\n     *         if the BDM cannot be retrieved.\n     */\n    String getInstalledBDMVersion() throws SBusinessDataRepositoryException;\n\n    /**\n     * Returns the currently deployed Business Object Data Model, or null if no BDM is deployed.\n     *\n     * @return the currently deployed Business Object Data Model, or null if no BDM is deployed.\n     * @throws SBusinessDataRepositoryException\n     *         if the BDM cannot be retrieved.\n     */\n    BusinessObjectModel getBusinessObjectModel() throws SBusinessDataRepositoryException;\n\n    /**\n     * Determine if the given BDM archive is equivalent to the currently deployed BDM.\n     *\n     * @param bdmArchive\n     *        the Business Data Model, as a zip containing the Business Object Model.\n     * @return true If the given BDM archive is the same as the one already deployed. False otherwise.\n     * @throws InvalidBusinessDataModelException if the given BDM archive is invalid\n     * @throws SBusinessDataRepositoryDeploymentException if the server jar generation of the given BDM fails\n     */\n    boolean isDeployed(byte[] bdmArchive)\n            throws InvalidBusinessDataModelException, SBusinessDataRepositoryDeploymentException;\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-api/src/main/java/org/bonitasoft/engine/business/data/BusinessDataRepository.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.bonitasoft.engine.commons.TenantLifecycleService;\n\n/**\n * The BusinessDataRepository service allows to manage Business Data operations. It includes deploy / undeploy of a\n * Business Data Model, search / find / create\n * / update of Business Data entity objects.\n *\n * @author Matthieu Chaffotte\n * @author Emmanuel Duchastenier\n * @see Entity\n */\npublic interface BusinessDataRepository extends TenantLifecycleService {\n\n    /**\n     * Finds an Entity that is defined in a deployed Business Data Model.\n     *\n     * @param entityClass the class of the entity to search for.\n     * @param primaryKey the primary key to search by.\n     * @return the found entity, if any.\n     * @throws SBusinessDataNotFoundException if the Business Data could not be found with the provided primary key.\n     */\n    <T extends Entity> T findById(Class<T> entityClass, Long primaryKey) throws SBusinessDataNotFoundException;\n\n    /**\n     * Finds entities that is defined in a deployed Business Data Model. If a primary key does not match an existing\n     * entity no exception is thrown and nothing\n     * is added in the list.\n     *\n     * @param entityClass the class of the entity to search for.\n     * @param primaryKeys the primary keys.\n     * @return the list of found entities\n     */\n    <T extends Entity> List<T> findByIds(Class<T> entityClass, List<Long> primaryKeys);\n\n    /**\n     * Finds (well-loaded) entities that is defined in a deployed Business Data Model. If a primary key does not match\n     * an existing entity no exception is thrown\n     * and nothing is added in the list.\n     *\n     * @param entityClass the class of the entity to search for.\n     * @param primaryKeys the primary keys.\n     * @return the list of found entities\n     */\n    <T extends Entity> List<T> findByIdentifiers(Class<T> entityClass, List<Long> primaryKeys);\n\n    /**\n     * Finds an Entity that is defined in a deployed Business Data Model, through JPQL query.\n     *\n     * @param resultClass the class of the entity to search for.\n     * @param jpqlQuery the JPQL query string to search the entity.\n     * @param parameters the parameters needed to execute the query.\n     * @return the found entity, if any.\n     * @throws SBusinessDataNotFoundException if the Business Data could not be found with the provided primary key.\n     * @throws NonUniqueResultException if more than one result was found.\n     */\n    <T extends Serializable> T find(Class<T> resultClass, String jpqlQuery, Map<String, Serializable> parameters)\n            throws NonUniqueResultException;\n\n    <T extends Serializable> List<T> findList(Class<T> resultClass, String jpqlQuery,\n            Map<String, Serializable> parameters, int startIndex, int maxResults);\n\n    <T extends Serializable> T findByNamedQuery(String queryName, Class<T> resultClass,\n            Map<String, Serializable> parameters) throws NonUniqueResultException;\n\n    <T extends Serializable> List<T> findListByNamedQuery(String queryName, Class<T> resultClass,\n            Map<String, Serializable> parameters, int startIndex,\n            int maxResults);\n\n    /**\n     * Saves or updates an entity in the Business Data Repository.\n     * <p>\n     * This operation also inserts or updates a data retention tracking record in the Bonita DB\n     * via {@code DataRetentionBdmTrackingRepository} when a BDM entity is created or updated.\n     *\n     * @param entity the entity to save / update.\n     */\n    void persist(Entity entity);\n\n    /**\n     * Removes an entity from the Business Data Repository.\n     * <p>\n     * This operation also deletes the associated data retention tracking record in the Bonita DB\n     * via {@code DataRetentionBdmTrackingService} when a BDM entity is removed.\n     *\n     * @param entity the entity to remove.\n     */\n    void remove(Entity entity);\n\n    /**\n     * Removes the entity with the given persistence ID. Also deletes the associated data\n     * retention tracking record in the Bonita DB.\n     * <p>\n     * Fires a {@code BUSINESS_DATA_DELETED} AOP event on the removed entity, like\n     * {@link #remove(Entity)}.\n     *\n     * @param entityClass the class of the entity to remove\n     * @param persistenceId the persistence ID of the entity to remove\n     * @return the removed entity\n     * @throws SBusinessDataNotFoundException if no entity exists with the given ID\n     */\n    Entity removeById(Class<? extends Entity> entityClass, long persistenceId) throws SBusinessDataNotFoundException;\n\n    /**\n     * Reconnect the given entity with the persistence unit.\n     * <p>\n     * This operation also inserts or updates a data retention tracking record in the Bonita DB\n     * via {@code DataRetentionBdmTrackingRepository} when a BDM entity is created or updated.\n     *\n     * @param entity the entity to reconnect.\n     * @return the connected entity.\n     */\n    Entity merge(Entity entity);\n\n    /**\n     * Retrieves the <code>Set</code> of known Entity class names in this Business Data Repository.\n     *\n     * @return the <code>Set</code> of known Entity class names, as qualified class names.\n     */\n    Set<String> getEntityClassNames();\n\n    /**\n     * Unwraps the Entity if necessary.\n     *\n     * @param wrapped the potential wrapped entity\n     * @return the unwrapped entity\n     */\n    Entity unwrap(final Entity wrapped);\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-api/src/main/java/org/bonitasoft/engine/business/data/BusinessDataService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface BusinessDataService {\n\n    Object callJavaOperation(Object businessObject, Object valueToSetObjectWith, String methodName,\n            String parameterType)\n            throws SBusinessDataNotFoundException, SBusinessDataRepositoryException;\n\n    boolean isBusinessData(Object valueToSetObjectWith);\n\n    Serializable getJsonEntity(String entityClassName, Long identifier, String businessDataURIPattern)\n            throws SBusinessDataNotFoundException, SBusinessDataRepositoryException;\n\n    Serializable getJsonChildEntity(String entityClassName, Long identifier, String childName,\n            String businessDataURIPattern)\n            throws SBusinessDataNotFoundException, SBusinessDataRepositoryException;\n\n    Serializable getJsonQueryEntities(String entityClassName, String queryName,\n            Map<String, Serializable> queryParameters, Integer startIndex,\n            Integer maxResults, String businessDataURIPattern) throws SBusinessDataRepositoryException;\n\n    Serializable getJsonEntities(String entityClassName, List<Long> identifiers, String businessDataURIPattern)\n            throws SBusinessDataRepositoryException;\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-api/src/main/java/org/bonitasoft/engine/business/data/DataRetentionBdmTrackingService.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data;\n\n/**\n * Service that manages data retention tracking records for BDM entity instances.\n * <p>\n * Each tracking record stores the creation and last-modification timestamps of a\n * BDM object instance. These timestamps are consumed by the data retention service\n * to determine whether a BDM object has exceeded its configured retention period.\n * <p>\n * This interface lives in the API module so that it can be injected by modules\n * (e.g. {@code bonita-process-engine}) that do not depend on {@code bonita-persistence}.\n * The implementation delegates to {@code DataRetentionBdmTrackingRepository} in the\n * impl module, wrapping persistence exceptions into {@link SDataRetentionBdmTrackingException}.\n */\npublic interface DataRetentionBdmTrackingService {\n\n    /**\n     * Persists a new tracking record.\n     *\n     * @param dataId the persistence ID of the tracked BDM object instance\n     * @param dataClassname the fully qualified class name of the BDM object type\n     * @throws SDataRetentionBdmTrackingException if the insert fails\n     */\n    void create(long dataId, String dataClassname) throws SDataRetentionBdmTrackingException;\n\n    /**\n     * Upserts the {@code last_modified_at} timestamp for a BDM entity in the\n     * data retention tracking table.\n     * <p>If a tracking record already exists for the given {@code dataId} and\n     * {@code dataClassname}, its {@code lastModifiedAt} is updated to the current time.\n     * Otherwise, a new tracking record is created.\n     *\n     * @param dataId the persistence ID of the BDM entity\n     * @param dataClassname the fully qualified Java class name of the BDM entity\n     * @throws SDataRetentionBdmTrackingException if the upsert fails\n     */\n    void upsert(long dataId, String dataClassname) throws SDataRetentionBdmTrackingException;\n\n    /**\n     * Updates the {@code last_modified_at} column of an existing tracking record.\n     *\n     * @param id the ID of the tracking record to update\n     * @throws SDataRetentionBdmTrackingException if the update fails\n     */\n    void updateLastModifiedDate(long id) throws SDataRetentionBdmTrackingException;\n\n    /**\n     * Deletes the tracking record for a specific BDM entity instance.\n     * If no tracking record exists (e.g. the entity was created before the tracking\n     * feature was deployed), this method does nothing.\n     *\n     * @param dataId the persistence ID of the BDM entity\n     * @param dataClassname the fully qualified Java class name of the BDM entity\n     * @throws SDataRetentionBdmTrackingException if the delete fails\n     */\n    void delete(long dataId, String dataClassname) throws SDataRetentionBdmTrackingException;\n\n    /**\n     * Deletes <b>ALL</b> tracking records.\n     *\n     * @throws SDataRetentionBdmTrackingException if the delete operation fails\n     */\n    void deleteAll() throws SDataRetentionBdmTrackingException;\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-api/src/main/java/org/bonitasoft/engine/business/data/JsonBusinessDataSerializer.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bdm.Entity;\n\npublic interface JsonBusinessDataSerializer {\n\n    String EMPTY_OBJECT = \"{}\";\n\n    /**\n     * Returns whether standard JSON shape is enabled for BDM serialization.\n     * Standard shape returns:\n     * - Scalar/count queries as {\"value\": n} instead of [n]\n     * - Single-entity queries as {...} instead of [{...}]\n     *\n     * @return true if standard shape is enabled, false for legacy array format\n     */\n    boolean isStandardShapeEnabled();\n\n    String serializeEntity(Entity entity, String businessDataURIPattern)\n            throws SBusinessDataRepositorySerializationException;\n\n    String serializeEntities(List<? extends Entity> entities, String businessDataURIPattern)\n            throws SBusinessDataRepositorySerializationException;\n\n    /**\n     * @deprecated Use {@link #serializeScalarResult(List, String, boolean)} instead.\n     *             This method is kept for backward compatibility but will be removed in future versions.\n     */\n    @Deprecated(forRemoval = true)\n    String serializeCountResult(List<Long> list, String entityClassName)\n            throws SBusinessDataRepositorySerializationException;\n\n    /**\n     * Serialize scalar query results (Long, Double, Float, Integer).\n     *\n     * @param list the list containing the scalar result (typically single element)\n     * @param entityClassName the fully qualified name of the entity class\n     * @param useStandardShape if true, returns {\"value\": 10} format; if false, returns [10] format\n     * @return JSON serialized scalar result, e.g., \"[10]\" or \"{\\\"value\\\": 10}\" depending on format\n     * @throws SBusinessDataRepositorySerializationException if an error occurs during serialization\n     */\n    String serializeScalarResult(List<?> list, String entityClassName, boolean useStandardShape)\n            throws SBusinessDataRepositorySerializationException;\n\n    /**\n     * Serialize entity query results handling both single and multiple entity returns.\n     * Standard shape behavior:\n     * - Query defined to return single entity: {...} (object) or {} if empty\n     * - Query defined to return List (multiple results): [{...}] (array) even if only 1 result\n     * Legacy shape behavior (backward compatible):\n     * - All queries return arrays: [{...}] even for single entity queries\n     *\n     * @param entities the list of entities to serialize\n     * @param businessDataURIPattern the URI pattern for generating entity links\n     * @param useStandardShape if true, uses standard shape rules; if false, always returns array\n     * @param queryReturnsMultipleResults if true, the query is designed to return a List (always array output)\n     * @return JSON serialized entity result\n     * @throws SBusinessDataRepositorySerializationException if an error occurs during serialization\n     */\n    String serializeEntityQueryResult(List<? extends Entity> entities, String businessDataURIPattern,\n            boolean useStandardShape, boolean queryReturnsMultipleResults)\n            throws SBusinessDataRepositorySerializationException;\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-api/src/main/java/org/bonitasoft/engine/business/data/NonUniqueResultException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class NonUniqueResultException extends SBonitaException {\n\n    private static final long serialVersionUID = 7573132495695445017L;\n\n    public NonUniqueResultException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-api/src/main/java/org/bonitasoft/engine/business/data/SBusinessDataCrudOperationException.java",
    "content": "/**\n * Copyright (C) 2025 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data;\n\n/**\n * Exception indicating an error occurred during CRUD (Create, Read, Update, Delete) operations on business data.\n * This exception is a specific type of {@link SBusinessDataRepositoryException} and is used to signal issues\n * related to the manipulation of business data records in a repository.\n *\n * @author Emmanuel Duchastenier\n */\npublic class SBusinessDataCrudOperationException extends SBusinessDataRepositoryException {\n\n    public SBusinessDataCrudOperationException(String message) {\n        super(message);\n    }\n\n    public SBusinessDataCrudOperationException(Throwable cause) {\n        super(cause);\n    }\n\n    public SBusinessDataCrudOperationException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n    public BusinessDataCrudOperationException convertToClientException() {\n        return new BusinessDataCrudOperationException(getMessage(), getCause());\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-api/src/main/java/org/bonitasoft/engine/business/data/SBusinessDataNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SBusinessDataNotFoundException extends SBonitaException {\n\n    private static final long serialVersionUID = -4470717601583219790L;\n\n    public SBusinessDataNotFoundException(final String message) {\n        super(message);\n    }\n\n    public SBusinessDataNotFoundException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public BusinessDataNotFoundException convertToClientException() {\n        return new BusinessDataNotFoundException(getMessage(), getCause());\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-api/src/main/java/org/bonitasoft/engine/business/data/SBusinessDataRepositoryDeploymentException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SBusinessDataRepositoryDeploymentException extends SBusinessDataRepositoryException {\n\n    private static final long serialVersionUID = 3729004984895038663L;\n\n    public SBusinessDataRepositoryDeploymentException(final String message) {\n        super(message);\n    }\n\n    public SBusinessDataRepositoryDeploymentException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SBusinessDataRepositoryDeploymentException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-api/src/main/java/org/bonitasoft/engine/business/data/SBusinessDataRepositoryException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SBusinessDataRepositoryException extends SBonitaException {\n\n    private static final long serialVersionUID = -2517115818095061435L;\n\n    public SBusinessDataRepositoryException(final String message) {\n        super(message);\n    }\n\n    public SBusinessDataRepositoryException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SBusinessDataRepositoryException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public BusinessDataRepositoryException convertToClientException() {\n        return new BusinessDataRepositoryException(getMessage(), getCause());\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-api/src/main/java/org/bonitasoft/engine/business/data/SBusinessDataRepositorySerializationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data;\n\npublic class SBusinessDataRepositorySerializationException extends SBusinessDataRepositoryException {\n\n    public SBusinessDataRepositorySerializationException(final String message) {\n        super(message);\n    }\n\n    public SBusinessDataRepositorySerializationException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SBusinessDataRepositorySerializationException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-api/src/main/java/org/bonitasoft/engine/business/data/SDataRetentionBdmTrackingException.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * Checked exception thrown by {@link DataRetentionBdmTrackingService} when a\n * tracking operation (create, upsert, update, delete) fails.\n * <p>Wraps lower-level persistence exceptions so that callers in modules that\n * do not depend on {@code bonita-persistence} can still handle tracking errors.\n */\npublic class SDataRetentionBdmTrackingException extends SBonitaException {\n\n    public SDataRetentionBdmTrackingException(String message) {\n        super(message);\n    }\n\n    public SDataRetentionBdmTrackingException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n    public SDataRetentionBdmTrackingException(Throwable cause) {\n        super(cause);\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-api/src/main/java/org/bonitasoft/engine/business/data/SchemaManager.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data;\n\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * @author Pablo Alonso de Linaje García\n */\npublic interface SchemaManager {\n\n    List<Exception> drop(Set<String> managedClasses);\n\n    List<Exception> update(Set<String> managedClasses);\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-api/src/main/java/org/bonitasoft/engine/business/data/proxy/Capitalizer.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.proxy;\n\npublic class Capitalizer {\n\n    public static String capitalize(final String str) {\n        if (str == null || str.isEmpty()) {\n            return str;\n        }\n        return Character.toUpperCase(str.charAt(0)) + str.substring(1);\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-api/src/main/java/org/bonitasoft/engine/business/data/proxy/EntityGetter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.proxy;\n\nimport java.lang.reflect.Method;\nimport java.lang.reflect.ParameterizedType;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bdm.model.field.Field;\n\n/**\n * Wrapper over entity getter method\n *\n * @author Colin Puy\n */\npublic class EntityGetter {\n\n    private final Method method;\n\n    public EntityGetter(Method method) {\n        checkIsGetter(method);\n        this.method = method;\n    }\n\n    private void checkIsGetter(Method method) {\n        String methodName = method.getName();\n        if (!methodName.startsWith(\"get\") || methodName.length() <= 3) {\n            throw new IllegalArgumentException(methodName + \" is not a valid getter name.\");\n        }\n    }\n\n    public String getSourceEntityName() {\n        return method.getDeclaringClass().getSimpleName();\n    }\n\n    public String getCapitalizedFieldName() {\n        return method.getName().substring(3);\n    }\n\n    public String getReturnTypeClassName() {\n        return getTargetEntityClass().getName();\n    }\n\n    public String getAssociatedNamedQuery() {\n        String targetEntityName = getTargetEntityClass().getSimpleName();\n        return targetEntityName + \".find\" + getCapitalizedFieldName() + \"By\" + getSourceEntityName()\n                + Capitalizer.capitalize(Field.PERSISTENCE_ID);\n    }\n\n    public boolean returnsList() {\n        Class<?> returnTypeClass = method.getReturnType();\n        return List.class.isAssignableFrom(returnTypeClass);\n    }\n\n    public Class<?> getTargetEntityClass() {\n        if (returnsList()) {\n            final ParameterizedType listType = (ParameterizedType) method.getGenericReturnType();\n            final Class<?> type = (Class<?>) listType.getActualTypeArguments()[0];\n            return type;\n        }\n        return method.getReturnType();\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-api/src/main/java/org/bonitasoft/engine/business/data/proxy/ServerLazyLoader.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.proxy;\n\nimport java.io.Serializable;\nimport java.lang.reflect.Method;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bdm.model.field.Field;\nimport org.bonitasoft.engine.business.data.BusinessDataRepository;\nimport org.bonitasoft.engine.business.data.NonUniqueResultException;\n\npublic class ServerLazyLoader {\n\n    private BusinessDataRepository businessDataRepository;\n\n    public ServerLazyLoader(BusinessDataRepository bdBusinessDataRepository) {\n        this.businessDataRepository = bdBusinessDataRepository;\n    }\n\n    public Object load(final Method method, final long persistenceId) {\n        EntityGetter getter = new EntityGetter(method);\n        final Map<String, Serializable> queryParameters = new HashMap<>();\n        queryParameters.put(Field.PERSISTENCE_ID, persistenceId);\n        if (getter.returnsList()) {\n            return businessDataRepository.findListByNamedQuery(getter.getAssociatedNamedQuery(),\n                    (Class<? extends Serializable>) getter.getTargetEntityClass(), queryParameters, 0,\n                    Integer.MAX_VALUE);\n        }\n        try {\n            return businessDataRepository.findByNamedQuery(getter.getAssociatedNamedQuery(),\n                    (Class<? extends Serializable>) getter.getTargetEntityClass(), queryParameters);\n        } catch (NonUniqueResultException e) {\n            // cannot appear\n            throw new RuntimeException();\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-api/src/main/java/org/bonitasoft/engine/business/data/proxy/ServerProxyfier.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.proxy;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.stream.Collectors;\nimport javassist.util.proxy.MethodHandler;\nimport javassist.util.proxy.Proxy;\nimport javassist.util.proxy.ProxyFactory;\n\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.bonitasoft.engine.bdm.lazy.LazyLoaded;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Colin Puy\n * @author Laurent Leseigneur\n */\npublic class ServerProxyfier {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(ServerProxyfier.class);\n\n    private final ServerLazyLoader lazyLoader;\n\n    public ServerProxyfier(final ServerLazyLoader lazyLoader) {\n        this.lazyLoader = lazyLoader;\n    }\n\n    public static boolean isLazyMethodProxyfied(final Entity e) {\n        return ProxyFactory.isProxyClass(e.getClass())\n                && ProxyFactory.getHandler((Proxy) e) instanceof LazyMethodHandler;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public <T extends Entity> T proxify(final T entity) {\n        if (entity == null || isLazyMethodProxyfied(entity)) {\n            return entity;\n        }\n        return (T) proxifyEntity(entity);\n    }\n\n    private Entity proxifyEntity(final Entity entity) {\n        if (entity == null) {\n            return null;\n        }\n\n        final ProxyFactory factory = new ProxyFactory();\n        Class<?> classForProxy = entity.getClass();\n\n        //It's not possible to create a Proxy on a Proxy\n        //Here Entity can already be an Hibernate Proxy\n        if (ProxyFactory.isProxyClass(classForProxy)) {\n            classForProxy = classForProxy.getSuperclass();\n        }\n        factory.setSuperclass(classForProxy);\n        factory.setFilter((Method m) -> true);\n        try {\n            return (Entity) factory.create(new Class<?>[0], new Object[0], new LazyMethodHandler(entity, lazyLoader));\n        } catch (final Exception e) {\n            throw new RuntimeException(\"Error when proxifying object\", e);\n        }\n    }\n\n    // This methods seems unused but is called by reflection, I think:\n    @SuppressWarnings({ \"unchecked\" })\n    public <T extends Entity> List<T> proxify(final List<T> entities) {\n        if (entities == null) {\n            return null;\n        }\n        return (List<T>) proxifyEntities((List<Entity>) entities);\n    }\n\n    private List<Entity> proxifyEntities(final List<Entity> entities) {\n        final List<Entity> proxies = new ArrayList<>();\n        for (final Entity entity : entities) {\n            proxies.add(proxifyEntity(entity));\n        }\n        return proxies;\n    }\n\n    /**\n     * Handler that lazy load values for lazy loading methods that hasn't been loaded\n     */\n    public class LazyMethodHandler implements MethodHandler {\n\n        private final ServerLazyLoader lazyLoader;\n        private final Entity entity;\n\n        public LazyMethodHandler(final Entity entity, final ServerLazyLoader lazyLoader) {\n            this.entity = entity;\n            this.lazyLoader = lazyLoader;\n        }\n\n        public Entity getEntity() {\n            return entity;\n        }\n\n        @Override\n        public Object invoke(final Object self, final Method thisMethod, final Method proceed, final Object[] args)\n                throws Throwable {\n            Object invocationResult;\n            if (isMethodGetterOnLazyLoadedField(thisMethod)) {\n                invocationResult = lazyLoader.load(thisMethod, entity.getPersistenceId());\n            } else {\n                invocationResult = thisMethod.invoke(entity, args);\n            }\n            return proxifyIfNeeded(invocationResult);\n        }\n\n        private boolean isMethodGetterOnLazyLoadedField(final Method thisMethod) {\n            return isGetter(thisMethod) && thisMethod.isAnnotationPresent(LazyLoaded.class);\n        }\n\n        @SuppressWarnings(\"unchecked\")\n        private Object proxifyIfNeeded(final Object invocationResult) {\n            if (isAnEntity(invocationResult)) {\n                return proxifyEntity((Entity) invocationResult);\n            }\n\n            if (isAListOfEntities(invocationResult)) {\n                return proxifyEntities((List<Entity>) invocationResult);\n            }\n            return invocationResult;\n        }\n\n        private boolean isAListOfEntities(final Object invocationResult) {\n            if (invocationResult instanceof List<?> list) {\n                return !list.isEmpty() && list.get(0) instanceof Entity;\n            }\n            return false;\n        }\n\n        private boolean isAnEntity(final Object invocationResult) {\n            return invocationResult instanceof Entity;\n        }\n\n        private boolean isGetter(final Method method) {\n            return method.getName().startsWith(\"get\");\n        }\n\n    }\n\n    public static Entity unProxifyIfNeeded(final Entity entity) {\n        Set<Entity> visitedEntities = new HashSet<>();\n        return unProxifyIfNeeded(entity, visitedEntities);\n    }\n\n    private static Entity unProxifyIfNeeded(Entity entity, Set<Entity> alreadyUnproxyfiedEntities) {\n        Entity detachedEntity = entity;\n        if (entity != null && isLazyMethodProxyfied(entity)) {\n            detachedEntity = ((LazyMethodHandler) ProxyFactory.getHandler((Proxy) entity)).getEntity();\n        }\n        if (detachedEntity == null) {\n            return null;\n        }\n        boolean wasNotAlreadyUnproxified = alreadyUnproxyfiedEntities.add(detachedEntity);\n        // If we just unproxified this entity, we must also unproxify its related Entity fields:\n        if (wasNotAlreadyUnproxified) {\n            final Field[] declaredFields = detachedEntity.getClass().getDeclaredFields();\n            for (final Field field : declaredFields) {\n                try {\n                    if (Entity.class.isAssignableFrom(field.getType())) {\n                        field.setAccessible(true);\n                        field.set(detachedEntity,\n                                unProxifyIfNeeded((Entity) field.get(detachedEntity), alreadyUnproxyfiedEntities));\n                    } else if (List.class.isAssignableFrom(field.getType())) {\n                        field.setAccessible(true);\n                        final List list = (List) field.get(detachedEntity);\n                        if (list != null && !list.isEmpty() && isThereAnyNonNullEntityInTheList(list)) {\n                            final List<Entity> realEntities = ((List<Entity>) list).stream()\n                                    .map(element -> unProxifyIfNeeded(element, alreadyUnproxyfiedEntities))\n                                    .collect(Collectors.toList());\n                            list.clear();\n                            list.addAll(realEntities);\n                            field.set(detachedEntity, list);\n                        }\n                    }\n                } catch (IllegalAccessException e) {\n                    LOGGER.error(String.format(\"Illegal access to field '%s' of Entity '%s'\", field, detachedEntity));\n                }\n            }\n        }\n        return detachedEntity;\n    }\n\n    private static boolean isThereAnyNonNullEntityInTheList(List<?> list) {\n        return list.stream().anyMatch(e -> e != null && Entity.class.isAssignableFrom(e.getClass()));\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-api/src/test/java/org/bonitasoft/engine/business/data/proxy/A.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.proxy;\n\nimport org.bonitasoft.engine.bdm.Entity;\n\npublic class A implements Entity {\n\n    private Long persistenceId;\n    private Long persistenceVersion;\n    private B b;\n    private A a;\n\n    @Override\n    public Long getPersistenceId() {\n        return persistenceId;\n    }\n\n    @Override\n    public Long getPersistenceVersion() {\n        return persistenceVersion;\n    }\n\n    public B getB() {\n        return b;\n    }\n\n    public void setB(B b) {\n        this.b = b;\n    }\n\n    public A getA() {\n        return a;\n    }\n\n    public void setA(A a) {\n        this.a = a;\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-api/src/test/java/org/bonitasoft/engine/business/data/proxy/Address.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.proxy;\n\nimport org.bonitasoft.engine.bdm.Entity;\n\npublic class Address implements Entity {\n\n    private static final long serialVersionUID = 326174219656126594L;\n\n    @Override\n    public Long getPersistenceId() {\n        return 0L;\n    }\n\n    @Override\n    public Long getPersistenceVersion() {\n        return 1L;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-api/src/test/java/org/bonitasoft/engine/business/data/proxy/B.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.proxy;\n\nimport org.bonitasoft.engine.bdm.Entity;\n\npublic class B implements Entity {\n\n    private Long persistenceId;\n    private Long persistenceVersion;\n    private A a;\n\n    @Override\n    public Long getPersistenceId() {\n        return persistenceId;\n    }\n\n    @Override\n    public Long getPersistenceVersion() {\n        return persistenceVersion;\n    }\n\n    public A getA() {\n        return a;\n    }\n\n    public void setA(A a) {\n        this.a = a;\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-api/src/test/java/org/bonitasoft/engine/business/data/proxy/CapitalizerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.proxy;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.Test;\n\npublic class CapitalizerTest {\n\n    @Test\n    public void testCapitalizer() {\n        assertThat(Capitalizer.capitalize(\"name\")).isEqualTo(\"Name\");\n    }\n\n    @Test\n    public void capitalizer_null_value() {\n        assertThat(Capitalizer.capitalize(null)).isEqualTo(null);\n    }\n\n    @Test\n    public void capitalizer_empty_value() {\n        assertThat(Capitalizer.capitalize(\"\")).isEqualTo(\"\");\n    }\n\n    @Test\n    public void capitalizer_1_char_value() {\n        assertThat(Capitalizer.capitalize(\"a\")).isEqualTo(\"A\");\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-api/src/test/java/org/bonitasoft/engine/business/data/proxy/Employee.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.proxy;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bdm.Entity;\n\npublic class Employee implements Entity {\n\n    private static final long serialVersionUID = 4877386043381866907L;\n\n    private Long persistenceId;\n\n    private Long persistenceVersion;\n\n    private String firstName;\n\n    private String lastName;\n\n    private Address address;\n\n    private List<Address> addresses;\n\n    public Employee() {\n        super();\n    }\n\n    public Employee(final Long id, final Long persistenceVersion, final String firstName, final String lastName) {\n        super();\n        persistenceId = id;\n        this.persistenceVersion = persistenceVersion;\n        this.firstName = firstName;\n        this.lastName = lastName;\n    }\n\n    @Override\n    public Long getPersistenceId() {\n        return persistenceId;\n    }\n\n    public void setPersistenceId(final Long id) {\n        persistenceId = id;\n    }\n\n    @Override\n    public Long getPersistenceVersion() {\n        return persistenceVersion;\n    }\n\n    public String getFirstName() {\n        return firstName;\n    }\n\n    public void setFirstName(final String firstName) {\n        this.firstName = firstName;\n    }\n\n    public String getLastName() {\n        return lastName;\n    }\n\n    public void setLastName(final String lastName) {\n        this.lastName = lastName;\n    }\n\n    public Address getAddress() {\n        return address;\n    }\n\n    public void setAddress(final Address address) {\n        this.address = address;\n    }\n\n    public List<Address> getAddresses() {\n        return addresses;\n    }\n\n    public void setAddresses(final List<Address> addresses) {\n        this.addresses = addresses;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-api/src/test/java/org/bonitasoft/engine/business/data/proxy/EntityGetterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.proxy;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.lang.reflect.Method;\n\nimport org.junit.Test;\n\npublic class EntityGetterTest {\n\n    @Test(expected = IllegalArgumentException.class)\n    public void can_only_be_applicable_to_getters() throws Exception {\n        //given\n        Method setter = Employee.class.getMethod(\"setLastName\", String.class);\n\n        //when then\n        new EntityGetter(setter);\n    }\n\n    @Test\n    public void should_be_able_to_retrieve_source_entity_name() throws Exception {\n        //given\n        Method methodOfEmployee = Employee.class.getMethod(\"getAddress\");\n\n        //when\n        String sourceEntityName = new EntityGetter(methodOfEmployee).getSourceEntityName();\n\n        //then\n        assertThat(sourceEntityName).isEqualTo(Employee.class.getSimpleName());\n    }\n\n    @Test\n    public void should_be_able_to_retrieve_target_entity_class_even_if_method_return_a_list() throws Exception {\n        //given\n        Method methodReturningListOfAdresses = Employee.class.getMethod(\"getAddresses\");\n\n        //when\n        final EntityGetter entityGetter = new EntityGetter(methodReturningListOfAdresses);\n\n        //then\n        assertThat(entityGetter.getTargetEntityClass()).isEqualTo(Address.class);\n        assertThat(entityGetter.getReturnTypeClassName())\n                .isEqualTo(\"org.bonitasoft.engine.business.data.proxy.Address\");\n        assertThat(entityGetter.returnsList()).isTrue();\n    }\n\n    @Test\n    public void should_be_able_to_retrieve_capitalized_field_name() throws Exception {\n        //given\n        Method getAdresses = Employee.class.getMethod(\"getAddresses\");\n\n        //when\n        String sourceEntityName = new EntityGetter(getAdresses).getCapitalizedFieldName();\n\n        //then\n        assertThat(sourceEntityName).isEqualTo(\"Addresses\");\n    }\n\n    @Test\n    public void should_return_entity_class_name_if_getter_return_a_unique_object() throws Exception {\n        //given\n        Method uniqueObjectGetter = Employee.class.getMethod(\"getAddress\");\n\n        //when\n        final EntityGetter entityGetter = new EntityGetter(uniqueObjectGetter);\n\n        //then\n        assertThat(entityGetter.getReturnTypeClassName()).isEqualTo(Address.class.getName());\n        assertThat(entityGetter.returnsList()).isFalse();\n\n    }\n\n    @Test\n    public void should_be_able_to_get_associated_named_query() throws Exception {\n        //given\n        Method getManager = Employee.class.getMethod(\"getAddresses\");\n\n        //when\n        String namedQuery = new EntityGetter(getManager).getAssociatedNamedQuery();\n\n        //then\n        assertThat(namedQuery).isEqualTo(\"Address.findAddressesByEmployeePersistenceId\");\n    }\n\n    @Test\n    public void should_be_able_to_determine_if_getter_return_a_list() throws Exception {\n        //given\n        Method multipleObjectGetter = Employee.class.getMethod(\"getAddresses\");\n\n        //when\n        final EntityGetter entityGetter = new EntityGetter(multipleObjectGetter);\n\n        //then\n        assertThat(entityGetter.returnsList()).isTrue();\n        assertThat(entityGetter.getReturnTypeClassName()).isEqualTo(entityGetter.getTargetEntityClass().getName());\n    }\n\n    @Test\n    public void should_be_able_to_determine_if_getter_do_not_return_a_list() throws Exception {\n        //given\n        Method multipleObjectGetter = Employee.class.getMethod(\"getLastName\");\n\n        //when\n        final EntityGetter entityGetter = new EntityGetter(multipleObjectGetter);\n\n        //then\n        assertThat(entityGetter.returnsList()).isFalse();\n        assertThat(entityGetter.getReturnTypeClassName()).isEqualTo(entityGetter.getTargetEntityClass().getName());\n\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-api/src/test/java/org/bonitasoft/engine/business/data/proxy/PersonEntity.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.proxy;\n\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.bonitasoft.engine.bdm.lazy.LazyLoaded;\n\n/**\n * @author Romain Bioteau\n * @author Laurent Leseigneur\n */\npublic class PersonEntity implements Entity {\n\n    public PersonEntity() {\n\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see org.bonitasoft.engine.bdm.Entity#getPersistenceId()\n     */\n    @Override\n    public Long getPersistenceId() {\n        return 1L;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see org.bonitasoft.engine.bdm.Entity#getPersistenceVersion()\n     */\n    @Override\n    public Long getPersistenceVersion() {\n        return null;\n    }\n\n    @LazyLoaded\n    public String getWithLazyLoadedAnnotation() {\n        return \"getWithLazyLoadedAnnotation\";\n    }\n\n    public String getWithoutLazyLoadedAnnotation() {\n        return \"getWithoutLazyLoadedAnnotation\";\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-api/src/test/java/org/bonitasoft/engine/business/data/proxy/ServerLazyLoaderTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.proxy;\n\nimport static org.mockito.Mockito.*;\n\nimport java.io.Serializable;\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.business.data.BusinessDataRepository;\nimport org.bonitasoft.engine.business.data.NonUniqueResultException;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ServerLazyLoaderTest {\n\n    public class Addresses implements Serializable {\n\n        private static final long serialVersionUID = 1L;\n\n        public String getCity() {\n            return \"city\";\n        }\n    }\n\n    public class Employee {\n\n        public Employee() {\n\n        }\n\n        public List<Addresses> getAddresses() {\n            return new ArrayList<>();\n        }\n\n        public String getName() {\n            return \"name\";\n        }\n\n    }\n\n    @Mock\n    private BusinessDataRepository businessDataRepository;\n\n    private ServerLazyLoader serverLazyLoader;\n\n    final long persistenceId = 22L;\n\n    Employee employee = new Employee();\n\n    @Before\n    public void setUp() throws Exception {\n        serverLazyLoader = spy(new ServerLazyLoader(businessDataRepository));\n\n    }\n\n    @Test\n    public void should_load_list_of_objects() throws Exception {\n        //given\n        final Method method = employee.getClass().getMethod(\"getAddresses\");\n\n        //when\n        serverLazyLoader.load(method, persistenceId);\n\n        final String queryName = \"Addresses.findAddressesByEmployeePersistenceId\";\n        final Class<? extends Serializable> resultClass = Addresses.class;\n        final Map<String, Serializable> parameters = new HashMap<>();\n        parameters.put(\"persistenceId\", persistenceId);\n        final int startIndex = 0;\n        final int maxResults = Integer.MAX_VALUE;\n\n        //then\n        verify(businessDataRepository).findListByNamedQuery(queryName, resultClass, parameters, startIndex, maxResults);\n        verify(businessDataRepository, never()).findByNamedQuery(queryName, resultClass, parameters);\n\n    }\n\n    @Test\n    public void should_load_single_object() throws Exception {\n        //given\n        final String queryName = \"String.findNameByEmployeePersistenceId\";\n        final Class<? extends Serializable> resultClass = String.class;\n        final Map<String, Serializable> parameters = new HashMap<>();\n        parameters.put(\"persistenceId\", persistenceId);\n        final int startIndex = 0;\n        final int maxResults = Integer.MAX_VALUE;\n        final Method method = employee.getClass().getMethod(\"getName\");\n\n        //when\n        serverLazyLoader.load(method, persistenceId);\n\n        //then\n        verify(businessDataRepository, never()).findListByNamedQuery(queryName, resultClass, parameters, startIndex,\n                maxResults);\n        verify(businessDataRepository).findByNamedQuery(queryName, resultClass, parameters);\n\n    }\n\n    @Test(expected = RuntimeException.class)\n    public void should_load_single_object_throw_exception() throws Exception {\n        final String queryName = \"String.findNameByEmployeePersistenceId\";\n        final Class<? extends Serializable> resultClass = String.class;\n        final Map<String, Serializable> parameters = new HashMap<>();\n        parameters.put(\"persistenceId\", persistenceId);\n        final Method method = employee.getClass().getMethod(\"getName\");\n\n        //given\n        doThrow(NonUniqueResultException.class).when(businessDataRepository).findByNamedQuery(queryName, resultClass,\n                parameters);\n\n        //when\n        serverLazyLoader.load(method, persistenceId);\n\n        //then exception\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-api/src/test/java/org/bonitasoft/engine/business/data/proxy/ServerProxyfierTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.proxy;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyLong;\nimport static org.mockito.Mockito.*;\n\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.List;\nimport javassist.util.proxy.MethodHandler;\nimport javassist.util.proxy.ProxyFactory;\nimport javassist.util.proxy.ProxyObject;\n\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Romain Bioteau\n * @author Laurent Leseigneur\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class ServerProxyfierTest {\n\n    @Mock\n    private ServerLazyLoader lazyLoader;\n    private ServerProxyfier serverProxyfier;\n\n    @Before\n    public void setUp() throws Exception {\n        serverProxyfier = new ServerProxyfier(lazyLoader);\n    }\n\n    @Test\n    public void should_proxify_an_entity() {\n        final PersonEntity proxy = serverProxyfier.proxify(new PersonEntity());\n\n        assertThat(proxy).isInstanceOf(ProxyObject.class);\n        assertThat(proxy.getClass().getSuperclass()).isEqualTo(PersonEntity.class);\n    }\n\n    @Test\n    public void should_not_reproxify_a_server_proxy() {\n        final PersonEntity originalProxy = serverProxyfier.proxify(new PersonEntity());\n        final PersonEntity proxy = serverProxyfier.proxify(originalProxy);\n\n        assertThat(proxy).isSameAs(originalProxy);\n    }\n\n    @Test\n    public void should_reproxify_an_hibernate_proxy() throws Exception {\n        final ProxyFactory factory = new ProxyFactory();\n        factory.setSuperclass(PersonEntity.class);\n        final Entity aProxy = (Entity) factory.create(new Class[0], new Object[0], new MethodHandler() {\n\n            @Override\n            public Object invoke(final Object self, final Method thisMethod, final Method proceed,\n                    final Object[] args) {\n                return null;\n            }\n        });\n        final PersonEntity proxy = (PersonEntity) serverProxyfier.proxify(aProxy);\n\n        assertThat(proxy).isNotSameAs(aProxy);\n    }\n\n    @Test\n    public void should_call_on_lazy_loaded_getter_use_lazyLoader() throws Exception {\n        //given\n        final PersonEntity personEntity = new PersonEntity();\n\n        final Method method = PersonEntity.class.getMethod(\"getWithLazyLoadedAnnotation\");\n        doReturn(\"lazyResult\").when(lazyLoader).load(any(Method.class), anyLong());\n\n        //when\n        final PersonEntity proxy = serverProxyfier.proxify(personEntity);\n        final String withLazyLoadedAnnotation = proxy.getWithLazyLoadedAnnotation();\n\n        //\n        verify(lazyLoader).load(method, personEntity.getPersistenceId());\n        assertThat(withLazyLoadedAnnotation).isEqualTo(\"lazyResult\");\n    }\n\n    @Test\n    public void should_not_call_lazyLoader() throws Exception {\n        //given\n        final PersonEntity personEntity = new PersonEntity();\n        final Method method = PersonEntity.class.getMethod(\"getWithoutLazyLoadedAnnotation\");\n\n        //when\n        final PersonEntity proxy = serverProxyfier.proxify(personEntity);\n        final String withLazyLoadedAnnotation = proxy.getWithoutLazyLoadedAnnotation();\n\n        //\n        verify(lazyLoader, never()).load(method, personEntity.getPersistenceId());\n        assertThat(withLazyLoadedAnnotation).isEqualTo(\"getWithoutLazyLoadedAnnotation\");\n    }\n\n    @Test\n    public void unProxy_should_not_remove_a_proxy_on_a_null_entity() {\n        final Entity entity = ServerProxyfier.unProxifyIfNeeded(null);\n\n        assertThat(entity).isNull();\n    }\n\n    @Test\n    public void unProxy_should_not_remove_a_proxy_on_an_entity() {\n        final PersonEntity proxy = new PersonEntity();\n\n        final Entity entity = ServerProxyfier.unProxifyIfNeeded(proxy);\n\n        assertThat(ServerProxyfier.isLazyMethodProxyfied(entity)).isFalse();\n    }\n\n    @Test\n    public void unProxy_should_remove_a_proxy_on_an_entity() {\n        final PersonEntity proxy = serverProxyfier.proxify(new PersonEntity());\n\n        final Entity entity = ServerProxyfier.unProxifyIfNeeded(proxy);\n\n        assertThat(ServerProxyfier.isLazyMethodProxyfied(entity)).isFalse();\n    }\n\n    @Test\n    public void should_unproxify_object_with_cycle() {\n        A a = new A();\n        B b = new B();\n        A aProxy = serverProxyfier.proxify(a);\n        B bProxy = serverProxyfier.proxify(b);\n        b.setA(aProxy);\n        a.setB(bProxy);\n\n        A unproxyfied = (A) ServerProxyfier.unProxifyIfNeeded(aProxy);\n\n        assertThat(ServerProxyfier.isLazyMethodProxyfied(unproxyfied)).isFalse();\n        assertThat(ServerProxyfier.isLazyMethodProxyfied(unproxyfied.getB())).isFalse();\n    }\n\n    @Test\n    public void should_unproxify_object_directReferenceToItself() {\n        A a = new A();\n        A aProxy = serverProxyfier.proxify(a);\n        a.setA(aProxy);\n\n        A unproxyfied = (A) ServerProxyfier.unProxifyIfNeeded(aProxy);\n\n        assertThat(ServerProxyfier.isLazyMethodProxyfied(unproxyfied)).isFalse();\n        assertThat(ServerProxyfier.isLazyMethodProxyfied(unproxyfied.getA())).isFalse();\n    }\n\n    @Test\n    public void unProxy_should_remove_a_proxy_on_an_ref_attribute_of_an_entity() {\n        final Address proxy = serverProxyfier.proxify(new Address());\n        final Employee employee = new Employee(10L, 45L, \"John\", \"Doe\");\n        employee.setAddress(proxy);\n\n        final Employee entity = (Employee) ServerProxyfier.unProxifyIfNeeded(employee);\n\n        assertThat(ServerProxyfier.isLazyMethodProxyfied(entity.getAddress())).isFalse();\n    }\n\n    @Test\n    public void unProxy_should_remove_a_proxy_on_a_list_attribute_of_an_entity() {\n        final List<Address> addresses = new ArrayList<>();\n        addresses.add(new Address());\n        addresses.add(serverProxyfier.proxify(new Address()));\n        final Employee employee = new Employee(10L, 45L, \"John\", \"Doe\");\n        employee.setAddresses(addresses);\n\n        final Employee entity = (Employee) ServerProxyfier.unProxifyIfNeeded(employee);\n\n        for (final Address address : entity.getAddresses()) {\n            assertThat(ServerProxyfier.isLazyMethodProxyfied(address)).isFalse();\n        }\n    }\n\n    @Test\n    public void unproxify_should_not_fail_on_a_first_null_object_in_a_list_attribute_of_an_entity() {\n        final List<Address> addresses = new ArrayList<>();\n        addresses.add(null);\n        final Address address = new Address();\n        addresses.add(serverProxyfier.proxify(address));\n        final Employee employee = new Employee(11L, 42L, \"Ol\", \"Delaf\");\n        employee.setAddresses(addresses);\n\n        final Employee entity = (Employee) ServerProxyfier.unProxifyIfNeeded(employee);\n\n        assertThat(entity.getAddresses()).hasSize(2).containsExactly(null, address);\n    }\n\n    @Test\n    public void unproxify_should_not_fail_on_a_later_null_object_in_a_list_attribute_of_an_entity() {\n        final List<Address> addresses = new ArrayList<>();\n        final Address address = new Address();\n        addresses.add(serverProxyfier.proxify(address));\n        addresses.add(null);\n        final Employee employee = new Employee(11L, 42L, \"Ol\", \"Delaf\");\n        employee.setAddresses(addresses);\n\n        final Employee entity = (Employee) ServerProxyfier.unProxifyIfNeeded(employee);\n\n        assertThat(entity.getAddresses()).hasSize(2).containsExactly(address, null);\n    }\n\n    @Test\n    public void unproxify_should_not_fail_when_all_objects_in_a_list_attribute_of_an_entity_are_null() {\n        final List<Address> addresses = new ArrayList<>();\n        addresses.add(null);\n        addresses.add(null);\n        final Employee employee = new Employee(11L, 42L, \"Ol\", \"Delaf\");\n        employee.setAddresses(addresses);\n\n        final Employee entity = (Employee) ServerProxyfier.unProxifyIfNeeded(employee);\n\n        assertThat(entity.getAddresses()).hasSize(2).containsExactly(null, null);\n    }\n\n    @Test\n    public void should_return_null_when_proxifying_a_null_entity() {\n        final Entity proxy = serverProxyfier.proxify((Entity) null);\n\n        assertThat(proxy).isNull();\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-client-resources/README.md",
    "content": "Java files included in this module are added to business data generated code for client side, compiled and included into dao-client.jar\nThey are mainly used for lazy mode and client proxies."
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-client-resources/build.gradle",
    "content": "import org.bonitasoft.engine.gradle.PomUtils\n\nplugins {\n    id 'maven-publish'\n}\n\ndependencies {\n    api project(':bpm:bonita-common')\n    api libs.javassist\n    compileOnly project(':bpm:bonita-client')\n    testImplementation project(':bpm:bonita-client')\n    testImplementation libs.mockitoCore\n    testImplementation libs.assertj\n    testImplementation(libs.hibernateCore) {\n        exclude(module: 'jboss-transaction-api_1.2_spec')\n    }\n    testImplementation libs.lombok\n    testAnnotationProcessor libs.lombok\n}\n\ngroup = 'org.bonitasoft.engine.data'\n\ntasks.register(\"sourcesJar\", Jar) {\n    from sourceSets.main.allJava\n    archiveClassifier = 'sources'\n}\n\ntasks.register(\"javadocJar\", Jar) {\n    from javadoc\n    archiveClassifier = 'javadoc'\n}\n\npublishing {\n    publications {\n        mavenJava(MavenPublication) {\n            from project.components.java\n            artifact project.sourcesJar\n            artifact project.javadocJar\n            pom { pom ->\n                name = \"Bonita Business Data Client Resources\"\n                description = \"Bonita Business Data Client Resources is a library used in BDM client DAO implementation\"\n                PomUtils.pomCommunityPublication(pom)\n            }\n        }\n    }\n}"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-client-resources/src/main/java/org/bonitasoft/engine/bdm/dao/client/resources/BusinessObjectDeserializer.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.dao.client.resources;\n\nimport java.io.IOException;\nimport java.lang.reflect.Type;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.OffsetDateTime;\nimport java.util.List;\n\nimport com.fasterxml.jackson.databind.DeserializationFeature;\nimport com.fasterxml.jackson.databind.JavaType;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.module.SimpleModule;\nimport com.fasterxml.jackson.databind.type.TypeFactory;\nimport org.bonitasoft.engine.bdm.serialization.CustomLocalDateDeserializer;\nimport org.bonitasoft.engine.bdm.serialization.CustomLocalDateTimeDeserializer;\nimport org.bonitasoft.engine.bdm.serialization.CustomOffsetDateTimeDeserializer;\n\n/**\n * @author Romain Bioteau\n */\npublic class BusinessObjectDeserializer {\n\n    private final ObjectMapper mapper;\n    private final TypeFactory typeFactory;\n\n    public BusinessObjectDeserializer() {\n        mapper = new ObjectMapper();\n        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);\n        final SimpleModule customModule = new SimpleModule();\n        customModule.addDeserializer(LocalDate.class, new CustomLocalDateDeserializer());\n        customModule.addDeserializer(LocalDateTime.class, new CustomLocalDateTimeDeserializer());\n        customModule.addDeserializer(OffsetDateTime.class, new CustomOffsetDateTimeDeserializer());\n        mapper.registerModule(customModule);\n        typeFactory = mapper.getTypeFactory();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public <T> T deserialize(final byte[] serializedResult, final Class<T> targetType) throws IOException {\n        return (T) mapper.readValue(serializedResult, createJavaType(targetType));\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public <T> List<T> deserializeList(final byte[] serializedResult, final Class<T> targetType) throws IOException {\n        return (List<T>) mapper.readValue(serializedResult, createListJavaType(targetType));\n    }\n\n    private JavaType createListJavaType(final Type elementType) {\n        return typeFactory.constructCollectionType(List.class, createJavaType(elementType));\n    }\n\n    private JavaType createJavaType(final Type elementType) {\n        return typeFactory.constructType(elementType);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-client-resources/src/main/java/org/bonitasoft/engine/bdm/dao/client/resources/proxy/LazyLoader.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.dao.client.resources.proxy;\n\nimport java.io.Serializable;\nimport java.lang.reflect.Method;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.CommandAPI;\nimport org.bonitasoft.engine.api.TenantAPIAccessor;\nimport org.bonitasoft.engine.bdm.dao.client.resources.BusinessObjectDeserializer;\nimport org.bonitasoft.engine.bdm.dao.client.resources.utils.BDMQueryCommandParameters;\nimport org.bonitasoft.engine.bdm.dao.client.resources.utils.EntityGetter;\nimport org.bonitasoft.engine.exception.BonitaHomeNotSetException;\nimport org.bonitasoft.engine.exception.ServerAPIException;\nimport org.bonitasoft.engine.exception.UnknownAPITypeException;\nimport org.bonitasoft.engine.session.APISession;\n\npublic class LazyLoader {\n\n    private final APISession apiSession;\n    private final BusinessObjectDeserializer deserializer;\n\n    public LazyLoader(final APISession apiSession) {\n        if (apiSession == null) {\n            throw new IllegalArgumentException(\"apiSession cannot be null\");\n        }\n        this.apiSession = apiSession;\n        deserializer = new BusinessObjectDeserializer();\n    }\n\n    public Object load(final Method method, final long persistenceId) {\n        try {\n            EntityGetter getter = new EntityGetter(method);\n            final Map<String, Serializable> commandParameters = BDMQueryCommandParameters\n                    .createCommandParameters(getter, persistenceId);\n            final byte[] serializedResult = (byte[]) getCommandAPI().execute(\"executeBDMQuery\", commandParameters);\n            if (getter.returnsList()) {\n                return deserializer.deserializeList(serializedResult, getter.getTargetEntityClass());\n            }\n            return deserializer.deserialize(serializedResult, getter.getTargetEntityClass());\n        } catch (final Exception e) {\n            throw new IllegalArgumentException(e);\n        }\n    }\n\n    /**\n     * protected for testing\n     */\n    protected CommandAPI getCommandAPI() throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {\n        return TenantAPIAccessor.getCommandAPI(apiSession);\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-client-resources/src/main/java/org/bonitasoft/engine/bdm/dao/client/resources/proxy/Proxyfier.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.dao.client.resources.proxy;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.List;\nimport javassist.util.proxy.MethodFilter;\nimport javassist.util.proxy.MethodHandler;\nimport javassist.util.proxy.ProxyFactory;\n\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.bonitasoft.engine.bdm.lazy.LazyLoaded;\n\n/**\n * @author Colin Puy\n */\npublic class Proxyfier {\n\n    private final LazyLoader lazyLoader;\n\n    public Proxyfier(final LazyLoader lazyLoader) {\n        this.lazyLoader = lazyLoader;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public <T extends Entity> T proxify(final T entity) {\n        return (T) proxifyEntity(entity);\n    }\n\n    private Entity proxifyEntity(final Entity entity) {\n        if (entity == null) {\n            return null;\n        }\n        final ProxyFactory factory = new ProxyFactory();\n        factory.setSuperclass(entity.getClass());\n        factory.setFilter(new AllMethodFilter());\n        try {\n            return (Entity) factory.create(new Class<?>[0], new Object[0], new LazyMethodHandler(entity, lazyLoader));\n        } catch (final Exception e) {\n            throw new RuntimeException(\"Error when proxifying object\", e);\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public <T extends Entity> List<T> proxify(final List<T> entities) {\n        if (entities == null) {\n            return null;\n        }\n        return (List<T>) proxifyEntities((List<Entity>) entities);\n    }\n\n    private List<Entity> proxifyEntities(final List<Entity> entities) {\n        final List<Entity> proxies = new ArrayList<Entity>();\n        for (final Entity entity : entities) {\n            proxies.add(proxifyEntity(entity));\n        }\n        return proxies;\n    }\n\n    /**\n     * Handler that lazy load values for lazy loading methods that hasn't been loaded\n     */\n    private class LazyMethodHandler implements MethodHandler {\n\n        private final LazyLoader lazyloader;\n        private final List<String> alreadyLoaded = new ArrayList<String>();\n        private final Entity entity;\n\n        public LazyMethodHandler(final Entity entity, final LazyLoader lazyloader) {\n            this.entity = entity;\n            this.lazyloader = lazyloader;\n        }\n\n        @Override\n        public Object invoke(final Object self, final Method thisMethod, final Method proceed, final Object[] args)\n                throws Throwable {\n            Object invocationResult = thisMethod.invoke(entity, args);\n\n            if (isGetterOrSetter(thisMethod)) {\n                if (isGetter(thisMethod) && shouldBeLoaded(thisMethod, invocationResult)) {\n                    invocationResult = lazyloader.load(thisMethod, entity.getPersistenceId());\n                    callSetterOnEntity(invocationResult, thisMethod);\n                }\n                alreadyLoaded.add(toFieldName(thisMethod.getName()));\n            }\n\n            return proxifyIfNeeded(invocationResult);\n        }\n\n        private void callSetterOnEntity(final Object invocationResult, final Method getter)\n                throws NoSuchMethodException, SecurityException,\n                IllegalAccessException, IllegalArgumentException, InvocationTargetException {\n            if (invocationResult != null) {\n                final Method setter = getAssociatedSetter(invocationResult, getter);\n                setter.invoke(entity, invocationResult);\n            }\n        }\n\n        private Method getAssociatedSetter(final Object invocationResult, final Method getter)\n                throws NoSuchMethodException, SecurityException {\n            return entity.getClass().getMethod(getter.getName().replaceFirst(\"^get\", \"set\"), getter.getReturnType());\n        }\n\n        @SuppressWarnings(\"unchecked\")\n        private Object proxifyIfNeeded(final Object invocationResult) {\n            if (isAnEntity(invocationResult)) {\n                return proxifyEntity((Entity) invocationResult);\n            }\n\n            if (isAListOfEntities(invocationResult)) {\n                return proxifyEntities((List<Entity>) invocationResult);\n            }\n            return invocationResult;\n        }\n\n        private boolean isAListOfEntities(final Object invocationResult) {\n            if (invocationResult instanceof List<?> list) {\n                if (!list.isEmpty() && list.get(0) instanceof Entity) {\n                    return true;\n                }\n            }\n            return false;\n        }\n\n        private boolean isAnEntity(final Object invocationResult) {\n            return invocationResult instanceof Entity;\n        }\n\n        private boolean shouldBeLoaded(final Method thisMethod, final Object notLazyLoaded) {\n            return thisMethod.isAnnotationPresent(LazyLoaded.class)\n                    && !alreadyLoaded.contains(toFieldName(thisMethod.getName()));\n        }\n\n        private boolean isGetterOrSetter(final Method method) {\n            return isGetter(method) || method.getName().startsWith(\"set\") && method.getName().length() > 3;\n        }\n\n        private boolean isGetter(final Method method) {\n            return method.getName().startsWith(\"get\");\n        }\n\n        private String toFieldName(final String methodName) {\n            if (methodName.startsWith(\"get\") || methodName.startsWith(\"set\") && methodName.length() > 3) {\n                return methodName.substring(3).toLowerCase();\n            }\n            throw new IllegalArgumentException(methodName + \" is not a valid getter or setter name.\");\n        }\n    }\n\n    /**\n     * Filter all methods\n     */\n    private class AllMethodFilter implements MethodFilter {\n\n        @Override\n        public boolean isHandled(final Method m) {\n            return true;\n        }\n\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-client-resources/src/main/java/org/bonitasoft/engine/bdm/dao/client/resources/utils/BDMQueryCommandParameters.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.dao.client.resources.utils;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bdm.model.field.Field;\n\npublic class BDMQueryCommandParameters {\n\n    public static Map<String, Serializable> createCommandParameters(final EntityGetter getter,\n            final long persistenceId) {\n        final Map<String, Serializable> commandParameters = new HashMap<String, Serializable>();\n        commandParameters.put(\"queryName\", getter.getAssociatedNamedQuery());\n        commandParameters.put(\"returnType\", getter.getReturnTypeClassName());\n        commandParameters.put(\"returnsList\", getter.returnsList());\n        commandParameters.put(\"startIndex\", 0);\n        commandParameters.put(\"maxResults\", Integer.MAX_VALUE);\n        final Map<String, Serializable> queryParameters = new HashMap<String, Serializable>();\n        queryParameters.put(Field.PERSISTENCE_ID, persistenceId);\n        commandParameters.put(\"queryParameters\", (Serializable) queryParameters);\n        return commandParameters;\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-client-resources/src/main/java/org/bonitasoft/engine/bdm/dao/client/resources/utils/Capitalizer.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.dao.client.resources.utils;\n\npublic class Capitalizer {\n\n    public static String capitalize(final String str) {\n        if (str == null || str.isEmpty()) {\n            return str;\n        }\n        return Character.toUpperCase(str.charAt(0)) + str.substring(1);\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-client-resources/src/main/java/org/bonitasoft/engine/bdm/dao/client/resources/utils/EntityGetter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.dao.client.resources.utils;\n\nimport java.lang.reflect.Method;\nimport java.lang.reflect.ParameterizedType;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bdm.model.field.Field;\n\n/**\n * Wrapper over entity getter method\n *\n * @author Colin Puy\n */\npublic class EntityGetter {\n\n    private final Method method;\n\n    public EntityGetter(Method method) {\n        checkIsGetter(method);\n        this.method = method;\n    }\n\n    private void checkIsGetter(Method method) {\n        String methodName = method.getName();\n        if (!methodName.startsWith(\"get\") || methodName.length() <= 3) {\n            throw new IllegalArgumentException(methodName + \" is not a valid getter name.\");\n        }\n    }\n\n    public String getSourceEntityName() {\n        return method.getDeclaringClass().getSimpleName();\n    }\n\n    public String getCapitalizedFieldName() {\n        return method.getName().substring(3);\n    }\n\n    public String getReturnTypeClassName() {\n        return getTargetEntityClass().getName();\n    }\n\n    public String getAssociatedNamedQuery() {\n        String targetEntityName = getTargetEntityClass().getSimpleName();\n        return targetEntityName + \".find\" + getCapitalizedFieldName() + \"By\" + getSourceEntityName()\n                + Capitalizer.capitalize(Field.PERSISTENCE_ID);\n    }\n\n    public boolean returnsList() {\n        Class<?> returnTypeClass = method.getReturnType();\n        return List.class.isAssignableFrom(returnTypeClass);\n    }\n\n    public Class<?> getTargetEntityClass() {\n        if (returnsList()) {\n            final ParameterizedType listType = (ParameterizedType) method.getGenericReturnType();\n            final Class<?> type = (Class<?>) listType.getActualTypeArguments()[0];\n            return type;\n        }\n        return method.getReturnType();\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-client-resources/src/test/java/org/bonita/pojo/AddressForTesting.java",
    "content": "/**\n * Copyright (C) 2015 BonitaSoft S.A.\n * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonita.pojo;\n\nimport javax.persistence.Column;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.Id;\nimport javax.persistence.NamedQueries;\nimport javax.persistence.NamedQuery;\nimport javax.persistence.Table;\nimport javax.persistence.Version;\n\n/**\n *\n */\n@javax.persistence.Entity(name = \"Address\")\n@Table(name = \"ADDRESS\")\n@NamedQueries({\n        @NamedQuery(name = \"Address.findByStreet\", query = \"SELECT a\\nFROM Address a\\nWHERE a.street= :street\\nORDER BY a.persistenceId\"),\n        @NamedQuery(name = \"Address.findByCity\", query = \"SELECT a\\nFROM Address a\\nWHERE a.city= :city\\nORDER BY a.persistenceId\"),\n        @NamedQuery(name = \"Address.find\", query = \"SELECT a\\nFROM Address a\\nORDER BY a.persistenceId\"),\n        @NamedQuery(name = \"Address.findAddressesByEmployeePersistenceId\", query = \"SELECT e.addresses\\nFROM Employee e\\nWHERE e.persistenceId= :persistenceId\")\n})\npublic class AddressForTesting implements org.bonitasoft.engine.bdm.Entity {\n\n    private static final long serialVersionUID = 1L;\n\n    @Id\n    @GeneratedValue\n    private Long persistenceId;\n    @Version\n    private Long persistenceVersion;\n    @Column(name = \"STREET\", nullable = true)\n    private String street;\n    @Column(name = \"CITY\", nullable = true)\n    private String city;\n\n    public AddressForTesting() {\n    }\n\n    public void setPersistenceId(Long persistenceId) {\n        this.persistenceId = persistenceId;\n    }\n\n    @Override\n    public Long getPersistenceId() {\n        return persistenceId;\n    }\n\n    public void setPersistenceVersion(Long persistenceVersion) {\n        this.persistenceVersion = persistenceVersion;\n    }\n\n    @Override\n    public Long getPersistenceVersion() {\n        return persistenceVersion;\n    }\n\n    public void setStreet(String street) {\n        this.street = street;\n    }\n\n    public String getStreet() {\n        return street;\n    }\n\n    public void setCity(String city) {\n        this.city = city;\n    }\n\n    public String getCity() {\n        return city;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        final AddressForTesting other = (AddressForTesting) obj;\n        if (persistenceId == null) {\n            if (other.persistenceId != null) {\n                return false;\n            }\n        } else {\n            if (!persistenceId.equals(other.persistenceId)) {\n                return false;\n            }\n        }\n        if (persistenceVersion == null) {\n            if (other.persistenceVersion != null) {\n                return false;\n            }\n        } else {\n            if (!persistenceVersion.equals(other.persistenceVersion)) {\n                return false;\n            }\n        }\n        if (street == null) {\n            if (other.street != null) {\n                return false;\n            }\n        } else {\n            if (!street.equals(other.street)) {\n                return false;\n            }\n        }\n        if (city == null) {\n            if (other.city != null) {\n                return false;\n            }\n        } else {\n            if (!city.equals(other.city)) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        int persistenceIdCode = 0;\n        if (persistenceId != null) {\n            persistenceIdCode = persistenceId.hashCode();\n        }\n        result = prime * result + persistenceIdCode;\n        int persistenceVersionCode = 0;\n        if (persistenceVersion != null) {\n            persistenceVersionCode = persistenceVersion.hashCode();\n        }\n        result = prime * result + persistenceVersionCode;\n        int streetCode = 0;\n        if (street != null) {\n            streetCode = street.hashCode();\n        }\n        result = prime * result + streetCode;\n        int cityCode = 0;\n        if (city != null) {\n            cityCode = city.hashCode();\n        }\n        result = prime * result + cityCode;\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-client-resources/src/test/java/org/bonita/pojo/EmployeeForTesting.java",
    "content": "/**\n * Copyright (C) 2015 BonitaSoft S.A.\n * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonita.pojo;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport javax.persistence.CascadeType;\nimport javax.persistence.Column;\nimport javax.persistence.ElementCollection;\nimport javax.persistence.FetchType;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.Id;\nimport javax.persistence.JoinColumn;\nimport javax.persistence.JoinTable;\nimport javax.persistence.ManyToMany;\nimport javax.persistence.NamedQueries;\nimport javax.persistence.NamedQuery;\nimport javax.persistence.OrderColumn;\nimport javax.persistence.UniqueConstraint;\nimport javax.persistence.Version;\n\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport org.bonitasoft.engine.bdm.lazy.LazyLoaded;\nimport org.hibernate.annotations.Index;\n\n/**\n * Describe a simple employee\n */\n@javax.persistence.Entity(name = \"Employee\")\n@org.hibernate.annotations.Table(appliesTo = \"EMPLOYEE\", indexes = {\n        @Index(name = \"IDX_LSTNM\", columnNames = {\n                \"LASTNAME\"\n        })\n})\n@javax.persistence.Table(name = \"EMPLOYEE\", uniqueConstraints = {\n        @UniqueConstraint(name = \"UK_FL\", columnNames = {\n                \"FIRSTNAME\",\n                \"LASTNAME\"\n        })\n})\n@NamedQueries({\n        @NamedQuery(name = \"Employee.findByFirstNameAndLastName\", query = \"SELECT e\\nFROM Employee e\\nWHERE e.firstName= :firstName\\nAND e.lastName= :lastName\\n\"),\n        @NamedQuery(name = \"Employee.findByFirstName\", query = \"SELECT e\\nFROM Employee e\\nWHERE e.firstName= :firstName\\nORDER BY e.persistenceId\"),\n        @NamedQuery(name = \"Employee.findByLastName\", query = \"SELECT e\\nFROM Employee e\\nWHERE e.lastName= :lastName\\nORDER BY e.persistenceId\"),\n        @NamedQuery(name = \"Employee.find\", query = \"SELECT e\\nFROM Employee e\\nORDER BY e.persistenceId\"),\n        @NamedQuery(name = \"Employee.findByPhoneNumber\", query = \"SELECT e FROM Employee e WHERE :phoneNumber IN ELEMENTS(e.phoneNumbers)\"),\n        @NamedQuery(name = \"Employee.findByFirstNameAndLastNameNewOrder\", query = \"SELECT e FROM Employee e WHERE e.firstName =:firstName AND e.lastName = :lastName ORDER BY e.lastName\"),\n        @NamedQuery(name = \"Employee.findByFirstNameFetchAddresses\", query = \"SELECT e FROM Employee e INNER JOIN FETCH e.addresses WHERE e.firstName =:firstName ORDER BY e.lastName\"),\n        @NamedQuery(name = \"Employee.countEmployee\", query = \"SELECT COUNT(e) FROM Employee e\")\n})\npublic class EmployeeForTesting implements org.bonitasoft.engine.bdm.Entity {\n\n    private static final long serialVersionUID = 6685785209798954931L;\n    @Id\n    @GeneratedValue\n    private Long persistenceId;\n    @Version\n    private Long persistenceVersion;\n    @Column(name = \"FIRSTNAME\", nullable = true, length = 10)\n    private String firstName;\n    @Column(name = \"LASTNAME\", nullable = false)\n    private String lastName;\n    @ElementCollection(fetch = FetchType.EAGER)\n    @OrderColumn\n    @Column(name = \"PHONENUMBERS\", nullable = true, length = 10)\n    private List<String> phoneNumbers = new ArrayList<String>(10);\n    @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)\n    @JoinTable(name = \"EMPLOYEE_ADDRESSES\", joinColumns = {\n            @JoinColumn(name = \"EMPLOYEE_PID\")\n    }, inverseJoinColumns = {\n            @JoinColumn(name = \"ADDRESS_PID\")\n    })\n    @OrderColumn\n    @JsonIgnore\n    private List<org.bonita.pojo.AddressForTesting> addresses = new ArrayList<org.bonita.pojo.AddressForTesting>(10);\n\n    public EmployeeForTesting() {\n    }\n\n    public void setPersistenceId(final Long persistenceId) {\n        this.persistenceId = persistenceId;\n    }\n\n    @Override\n    public Long getPersistenceId() {\n        return persistenceId;\n    }\n\n    public void setPersistenceVersion(final Long persistenceVersion) {\n        this.persistenceVersion = persistenceVersion;\n    }\n\n    @Override\n    public Long getPersistenceVersion() {\n        return persistenceVersion;\n    }\n\n    public void setFirstName(final String firstName) {\n        this.firstName = firstName;\n    }\n\n    public String getFirstName() {\n        return firstName;\n    }\n\n    public void setLastName(final String lastName) {\n        this.lastName = lastName;\n    }\n\n    public String getLastName() {\n        return lastName;\n    }\n\n    public void setPhoneNumbers(final List<String> phoneNumbers) {\n        this.phoneNumbers = phoneNumbers;\n    }\n\n    public List<String> getPhoneNumbers() {\n        return phoneNumbers;\n    }\n\n    public void addToPhoneNumbers(final String addTo) {\n        final List<String> phoneNumbers = getPhoneNumbers();\n        phoneNumbers.add(addTo);\n    }\n\n    public void removeFromPhoneNumbers(final String removeFrom) {\n        final List<?> phoneNumbers = getPhoneNumbers();\n        phoneNumbers.remove(removeFrom);\n    }\n\n    public void setAddresses(final List<org.bonita.pojo.AddressForTesting> addresses) {\n        this.addresses = addresses;\n    }\n\n    @LazyLoaded\n    public List<org.bonita.pojo.AddressForTesting> getAddresses() {\n        return addresses;\n    }\n\n    public void addToAddresses(final org.bonita.pojo.AddressForTesting addTo) {\n        final List<AddressForTesting> addresses = getAddresses();\n        addresses.add(addTo);\n    }\n\n    public void removeFromAddresses(final org.bonita.pojo.AddressForTesting removeFrom) {\n        final List<?> addresses = getAddresses();\n        addresses.remove(removeFrom);\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        final EmployeeForTesting other = (EmployeeForTesting) obj;\n        if (persistenceId == null) {\n            if (other.persistenceId != null) {\n                return false;\n            }\n        } else {\n            if (!persistenceId.equals(other.persistenceId)) {\n                return false;\n            }\n        }\n        if (persistenceVersion == null) {\n            if (other.persistenceVersion != null) {\n                return false;\n            }\n        } else {\n            if (!persistenceVersion.equals(other.persistenceVersion)) {\n                return false;\n            }\n        }\n        if (firstName == null) {\n            if (other.firstName != null) {\n                return false;\n            }\n        } else {\n            if (!firstName.equals(other.firstName)) {\n                return false;\n            }\n        }\n        if (lastName == null) {\n            if (other.lastName != null) {\n                return false;\n            }\n        } else {\n            if (!lastName.equals(other.lastName)) {\n                return false;\n            }\n        }\n        if (phoneNumbers == null) {\n            if (other.phoneNumbers != null) {\n                return false;\n            }\n        } else {\n            if (!phoneNumbers.equals(other.phoneNumbers)) {\n                return false;\n            }\n        }\n        if (addresses == null) {\n            if (other.addresses != null) {\n                return false;\n            }\n        } else {\n            if (!addresses.equals(other.addresses)) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        int persistenceIdCode = 0;\n        if (persistenceId != null) {\n            persistenceIdCode = persistenceId.hashCode();\n        }\n        result = prime * result + persistenceIdCode;\n        int persistenceVersionCode = 0;\n        if (persistenceVersion != null) {\n            persistenceVersionCode = persistenceVersion.hashCode();\n        }\n        result = prime * result + persistenceVersionCode;\n        int firstNameCode = 0;\n        if (firstName != null) {\n            firstNameCode = firstName.hashCode();\n        }\n        result = prime * result + firstNameCode;\n        int lastNameCode = 0;\n        if (lastName != null) {\n            lastNameCode = lastName.hashCode();\n        }\n        result = prime * result + lastNameCode;\n        int phoneNumbersCode = 0;\n        if (phoneNumbers != null) {\n            phoneNumbersCode = phoneNumbers.hashCode();\n        }\n        result = prime * result + phoneNumbersCode;\n        int addressesCode = 0;\n        if (addresses != null) {\n            addressesCode = addresses.hashCode();\n        }\n        result = prime * result + addressesCode;\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-client-resources/src/test/java/org/bonitasoft/engine/bdm/dao/client/resources/BusinessObjectDeserializerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.dao.client.resources;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.OffsetDateTime;\nimport java.time.ZoneOffset;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bdm.proxy.model.Child;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class BusinessObjectDeserializerTest {\n\n    private BusinessObjectDeserializer deserializer;\n\n    @Before\n    public void setUp() {\n        deserializer = new BusinessObjectDeserializer();\n    }\n\n    @Test\n    public void should_deserialize_an_entity() throws Exception {\n        final OffsetDateTime nextAppointment = OffsetDateTime.of(LocalDateTime.of(2019, 12, 12, 10, 30, 0),\n                ZoneOffset.ofHours(2));\n        Child jules = new Child(\"jules\", 1, new Date(), LocalDate.of(2017, 3, 6),\n                LocalDateTime.of(2018, 1, 2, 23, 59, 59), nextAppointment);\n\n        final String julesAsJson = jules.toJson();\n        jules.setNextAppointment(nextAppointment.withOffsetSameInstant(ZoneOffset.UTC));\n        Child deserialized = deserializer.deserialize(julesAsJson.getBytes(), Child.class);\n\n        assertThat(deserialized).isEqualTo(jules);\n    }\n\n    @Test\n    public void should_deserialize_a_list_of_entities() throws Exception {\n        Child jules = new Child(\"jules\", 1);\n        Child manon = new Child(\"manon\", 0);\n        String json = \"[\" + jules.toJson() + \",\" + manon.toJson() + \"]\";\n\n        List<Child> deserialized = deserializer.deserializeList(json.getBytes(), Child.class);\n\n        assertThat(deserialized).containsOnly(jules, manon);\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-client-resources/src/test/java/org/bonitasoft/engine/bdm/dao/client/resources/proxy/LazyLoaderTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.dao.client.resources.proxy;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.mockito.Mockito.*;\n\nimport java.io.Serializable;\nimport java.lang.reflect.Method;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.api.CommandAPI;\nimport org.bonitasoft.engine.bdm.dao.client.resources.utils.BDMQueryCommandParameters;\nimport org.bonitasoft.engine.bdm.dao.client.resources.utils.EntityGetter;\nimport org.bonitasoft.engine.bdm.model.field.Field;\nimport org.bonitasoft.engine.bdm.proxy.model.Child;\nimport org.bonitasoft.engine.bdm.proxy.model.Parent;\nimport org.bonitasoft.engine.session.impl.APISessionImpl;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class LazyLoaderTest {\n\n    @Mock\n    private CommandAPI commandAPI;\n\n    private LazyLoader lazyLoader;\n\n    @Before\n    public void setUp() throws Exception {\n        lazyLoader = spy(new LazyLoader(new APISessionImpl(1, new Date(), 3000, \"john\", 1)));\n        doReturn(commandAPI).when(lazyLoader).getCommandAPI();\n    }\n\n    @Test\n    public void should_load_object_through_command_api() throws Exception {\n        long persistenceId = 22L;\n        Child luce = new Child(\"Luce\", 2);\n        Method getChild = Parent.class.getMethod(\"getChild\");\n        when(commandAPI.execute(\"executeBDMQuery\", parameters(getChild, persistenceId)))\n                .thenReturn(luce.toJson().getBytes());\n\n        Object loadedChild = lazyLoader.load(getChild, persistenceId);\n\n        assertThat(loadedChild).isEqualTo(luce);\n    }\n\n    @Test\n    public void should_load_list_of_objects_through_command_api() throws Exception {\n        long persistenceId = 22L;\n        Child luce = new Child(\"Luce\", 2);\n        Child julien = new Child(\"Julien\", 5);\n        String json = \"[\" + luce.toJson() + \",\" + julien.toJson() + \"]\";\n        Method getChildren = Parent.class.getMethod(\"getChildren\");\n        when(commandAPI.execute(\"executeBDMQuery\", parameters(getChildren, persistenceId))).thenReturn(json.getBytes());\n\n        Object loadedChild = lazyLoader.load(getChildren, persistenceId);\n\n        assertThat(loadedChild).isInstanceOf(List.class);\n        assertThat((List<Child>) loadedChild).containsOnly(luce, julien);\n    }\n\n    @Test\n    public void should_getParameters_return_real_type_when_query_returns_list() throws Exception {\n        long persistenceId = 22L;\n        Method getChildren = Parent.class.getMethod(\"getChildren\");\n\n        //when\n        // use the concrete type as we need a Serializable object in later in the code\n        final Map<String, Serializable> parameters = parameters(getChildren, persistenceId);\n\n        //then\n        final HashMap<String, Serializable> queryParameters = new HashMap<>(); // use the concrete type as we need a Serializable object in later in the code\n\n        queryParameters.put(Field.PERSISTENCE_ID, persistenceId);\n\n        assertThat(parameters).hasSize(6)\n                .containsOnly(\n                        entry(\"queryName\", \"Child.findChildrenByParentPersistenceId\"),\n                        entry(\"returnsList\", true),\n                        entry(\"startIndex\", 0),\n                        entry(\"maxResults\", Integer.MAX_VALUE),\n                        entry(\"returnType\", Child.class.getName()),\n                        entry(\"queryParameters\", queryParameters));\n    }\n\n    private Map<String, Serializable> parameters(Method method, long persistenceId) {\n        return BDMQueryCommandParameters.createCommandParameters(new EntityGetter(method), persistenceId);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-client-resources/src/test/java/org/bonitasoft/engine/bdm/dao/client/resources/proxy/ProxyTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.dao.client.resources.proxy;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport java.lang.reflect.Method;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bdm.proxy.assertion.ProxyAssert;\nimport org.bonitasoft.engine.bdm.proxy.model.TestEntity;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ProxyTest {\n\n    @Mock\n    private LazyLoader lazyLoader;\n\n    @InjectMocks\n    private Proxyfier proxyfier;\n\n    private TestEntity mockLazyLoaderToReturn(final TestEntity entity) {\n        when(lazyLoader.load(any(Method.class), any(Long.class))).thenReturn(entity);\n        return entity;\n    }\n\n    @Test\n    public void should_load_object_when_method_is_lazy_and_object_is_not_loaded() {\n        final TestEntity expectedEntity = mockLazyLoaderToReturn(new TestEntity());\n        final TestEntity entity = proxyfier.proxify(new TestEntity());\n\n        final TestEntity lazyEntity = entity.getLazyEntity();\n\n        verify(lazyLoader).load(any(Method.class), any(Long.class));\n        assertThat(lazyEntity).isEqualTo(expectedEntity);\n    }\n\n    @Test\n    public void should_load_object_when_method_is_lazy_and_object_is_an_empty_list() {\n        final TestEntity entity = proxyfier.proxify(new TestEntity());\n\n        entity.getLazyEntityList();\n\n        verify(lazyLoader).load(any(Method.class), any(Long.class));\n    }\n\n    @Test\n    public void should_return_value_when_it_has_been_already_loaded_before_proxyfication() {\n        final String name = \"this is a preloaded value\";\n        TestEntity entity = new TestEntity();\n        entity.setName(name);\n        entity = proxyfier.proxify(entity);\n\n        final String proxyName = entity.getName();\n\n        verifyNoInteractions(lazyLoader);\n        assertThat(proxyName).isEqualTo(name);\n    }\n\n    @Test\n    public void should_not_load_object_which_has_been_already_lazy_loaded() {\n        final TestEntity expectedEntity = mockLazyLoaderToReturn(new TestEntity());\n        final TestEntity entity = proxyfier.proxify(new TestEntity());\n\n        entity.getLazyEntity();\n        final TestEntity lazyEntity = entity.getLazyEntity();\n\n        verify(lazyLoader, times(1)).load(any(Method.class), any(Long.class));\n        assertThat(lazyEntity).isEqualTo(expectedEntity);\n    }\n\n    @Test\n    public void should_not_load_object_for_a_non_lazy_loading_method() {\n        final TestEntity entity = proxyfier.proxify(new TestEntity());\n\n        entity.getEagerEntity();\n\n        verifyNoInteractions(lazyLoader);\n    }\n\n    @Test\n    public void should_not_load_object_that_has_been_set_by_a_setter() {\n        final TestEntity expectedEntity = new TestEntity();\n        final TestEntity entity = proxyfier.proxify(new TestEntity());\n\n        entity.setLazyEntity(expectedEntity);\n        final TestEntity lazyEntity = entity.getLazyEntity();\n\n        verifyNoInteractions(lazyLoader);\n        assertThat(lazyEntity).isEqualTo(expectedEntity);\n    }\n\n    @Test\n    public void should_return_a_proxy_when_calling_a_getter_returning_an_entity() {\n        final TestEntity entity = proxyfier.proxify(new TestEntity());\n\n        final TestEntity eagerEntity = entity.getEagerEntity();\n\n        ProxyAssert.assertThat(eagerEntity).isAProxy();\n    }\n\n    @Test\n    public void should_not_return_a_proxy_when_calling_a_getter_not_returning_an_entity() {\n        final TestEntity entity = proxyfier.proxify(new TestEntity());\n        entity.setName(\"aName\");\n\n        final String name = entity.getName();\n\n        ProxyAssert.assertThat(name).isNotAProxy();\n    }\n\n    @Test\n    public void should_return_a_list_of_proxies_when_calling_a_getter_returning_a_list_of_entities() {\n        final TestEntity entity = proxyfier.proxify(new TestEntity());\n\n        final List<TestEntity> entities = entity.getEagerEntities();\n\n        for (final TestEntity e : entities) {\n            ProxyAssert.assertThat(e).isAProxy();\n        }\n    }\n\n    @Test\n    public void should_not_return_a_list_of_proxies_when_calling_a_getter_not_returning_a_list_of_entities() {\n        final TestEntity entity = proxyfier.proxify(new TestEntity());\n\n        final List<String> strings = entity.getStrings();\n\n        for (final String string : strings) {\n            ProxyAssert.assertThat(string).isNotAProxy();\n        }\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-client-resources/src/test/java/org/bonitasoft/engine/bdm/dao/client/resources/proxy/ProxyfierTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.dao.client.resources.proxy;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyLong;\nimport static org.mockito.Mockito.doReturn;\n\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.assertj.core.api.Assertions;\nimport org.bonita.pojo.AddressForTesting;\nimport org.bonita.pojo.EmployeeForTesting;\nimport org.bonitasoft.engine.bdm.proxy.assertion.ProxyAssert;\nimport org.bonitasoft.engine.bdm.proxy.model.TestEntity;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ProxyfierTest {\n\n    @Mock\n    private LazyLoader lazyLoader;\n\n    @InjectMocks\n    private Proxyfier proxyfier;\n\n    @Test\n    public void should_return_null_when_entity_is_null() throws Exception {\n        TestEntity entity = null;\n\n        TestEntity proxy = proxyfier.proxify(entity);\n\n        ProxyAssert.assertThat(proxy).isNull();\n    }\n\n    @Test\n    public void should_return_null_when_list_of_entities_is_null() throws Exception {\n        List<TestEntity> entities = null;\n\n        List<TestEntity> proxies = proxyfier.proxify(entities);\n\n        ProxyAssert.assertThat(proxies).isNull();\n    }\n\n    @Test\n    public void should_proxify_an_entity() {\n        final TestEntity entity = new TestEntity();\n\n        final TestEntity proxy = proxyfier.proxify(entity);\n\n        ProxyAssert.assertThat(proxy).isAProxy();\n    }\n\n    @Test\n    public void should_proxify_a_list_of_entities() {\n        final List<TestEntity> entities = Arrays.asList(new TestEntity(),\n                new TestEntity());\n\n        final List<TestEntity> proxies = proxyfier.proxify(entities);\n\n        for (final TestEntity entity : proxies) {\n            ProxyAssert.assertThat(entity).isAProxy();\n        }\n    }\n\n    @Test\n    public void shouldProxyfier_retrieve_list_setter_when_lazyLoader_returns_arraylist() throws Exception {\n\n        //given\n        final AddressForTesting address1 = new AddressForTesting();\n        final AddressForTesting address2 = new AddressForTesting();\n        final List<AddressForTesting> addresses = new ArrayList<AddressForTesting>();\n        addresses.add(address1);\n        addresses.add(address2);\n        doReturn(addresses).when(lazyLoader).load(any(Method.class), anyLong());\n\n        //when\n        final long persistenceId = 1L;\n        final EmployeeForTesting employee = new EmployeeForTesting();\n        employee.setPersistenceId(persistenceId);\n        final EmployeeForTesting proxify = proxyfier.proxify(employee);\n\n        //then\n        final List<?> lazyAddresses = (List<?>) proxify.getClass().getMethod(\"getAddresses\", new Class[0])\n                .invoke(proxify);\n        ProxyAssert.assertThat(proxify).isAProxy();\n        Assertions.assertThat(lazyAddresses).hasSize(2);\n\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-client-resources/src/test/java/org/bonitasoft/engine/bdm/dao/client/resources/utils/BDMQueryCommandParametersTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.dao.client.resources.utils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bdm.proxy.model.Employee;\nimport org.junit.Test;\n\npublic class BDMQueryCommandParametersTest {\n\n    @Test\n    @SuppressWarnings(\"unchecked\")\n    public void should_create_bdm_command_parameters_for_method_returning_a_list() throws Exception {\n        EntityGetter getter = new EntityGetter(Employee.class.getDeclaredMethod(\"getEmployees\"));\n        long persistenceId = 12L;\n\n        Map<String, Serializable> commandParameters = BDMQueryCommandParameters.createCommandParameters(getter,\n                persistenceId);\n\n        assertThat(commandParameters).contains(\n                entry(\"queryName\", \"Employee.findEmployeesByEmployeePersistenceId\"),\n                entry(\"returnType\", Employee.class.getName()),\n                entry(\"returnsList\", Boolean.TRUE),\n                entry(\"startIndex\", 0),\n                entry(\"maxResults\", Integer.MAX_VALUE));\n        assertThat((Map<String, Serializable>) commandParameters.get(\"queryParameters\"))\n                .contains(entry(\"persistenceId\", persistenceId));\n    }\n\n    @Test\n    @SuppressWarnings(\"unchecked\")\n    public void should_createCommandParameters_with_simple_ref_returns_valid_parameters() throws Exception {\n        EntityGetter getter = new EntityGetter(Employee.class.getDeclaredMethod(\"getManager\"));\n        long persistenceId = 5L;\n\n        Map<String, Serializable> commandParameters = BDMQueryCommandParameters.createCommandParameters(getter,\n                persistenceId);\n\n        assertThat(commandParameters).contains(\n                entry(\"queryName\", \"Employee.findManagerByEmployeePersistenceId\"),\n                entry(\"returnType\", Employee.class.getName()),\n                entry(\"returnsList\", Boolean.FALSE),\n                entry(\"startIndex\", 0),\n                entry(\"maxResults\", Integer.MAX_VALUE));\n        assertThat((Map<String, Serializable>) commandParameters.get(\"queryParameters\"))\n                .contains(entry(\"persistenceId\", persistenceId));\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-client-resources/src/test/java/org/bonitasoft/engine/bdm/dao/client/resources/utils/CapitalizerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.dao.client.resources.utils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.Test;\n\npublic class CapitalizerTest {\n\n    @Test\n    public void should_capitalize_a_string() {\n\n        String capitalized = Capitalizer.capitalize(\"uncapitalized\");\n\n        assertThat(capitalized).isEqualTo(\"Uncapitalized\");\n    }\n\n    @Test\n    public void should_do_nothing_for_a_null_string() {\n        String capitalized = Capitalizer.capitalize(null);\n\n        assertThat(capitalized).isNull();\n    }\n\n    @Test\n    public void should_do_nothing_for_an_empty_string() {\n        String capitalized = Capitalizer.capitalize(\"\");\n\n        assertThat(capitalized).isEmpty();\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-client-resources/src/test/java/org/bonitasoft/engine/bdm/dao/client/resources/utils/EntityGetterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.dao.client.resources.utils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.lang.reflect.Method;\n\nimport org.bonitasoft.engine.bdm.proxy.model.Address;\nimport org.bonitasoft.engine.bdm.proxy.model.Employee;\nimport org.junit.Test;\n\npublic class EntityGetterTest {\n\n    @Test(expected = IllegalArgumentException.class)\n    public void can_only_be_applicable_to_getters() throws Exception {\n        //given\n        Method setter = Employee.class.getMethod(\"setManager\", Employee.class);\n\n        //when then\n        new EntityGetter(setter);\n    }\n\n    @Test\n    public void should_be_able_to_retrieve_source_entity_name() throws Exception {\n        //given\n        Method methodOfEmployee = Employee.class.getMethod(\"getManager\");\n\n        //when\n        String sourceEntityName = new EntityGetter(methodOfEmployee).getSourceEntityName();\n\n        //then\n        assertThat(sourceEntityName).isEqualTo(Employee.class.getSimpleName());\n    }\n\n    @Test\n    public void should_be_able_to_retrieve_target_entity_class_even_if_method_return_a_list() throws Exception {\n        //given\n        Method methodReturningListOfAdresses = Employee.class.getMethod(\"getAddresses\");\n\n        //when\n        final EntityGetter entityGetter = new EntityGetter(methodReturningListOfAdresses);\n\n        //then\n        assertThat(entityGetter.getTargetEntityClass()).isEqualTo(Address.class);\n        assertThat(entityGetter.getReturnTypeClassName()).isEqualTo(\"org.bonitasoft.engine.bdm.proxy.model.Address\");\n        assertThat(entityGetter.returnsList()).isTrue();\n    }\n\n    @Test\n    public void should_be_able_to_retrieve_capitalized_field_name() throws Exception {\n        //given\n        Method getAdresses = Employee.class.getMethod(\"getAddresses\");\n\n        //when\n        String sourceEntityName = new EntityGetter(getAdresses).getCapitalizedFieldName();\n\n        //then\n        assertThat(sourceEntityName).isEqualTo(\"Addresses\");\n    }\n\n    @Test\n    public void should_return_entity_class_name_if_getter_return_a_unique_object() throws Exception {\n        //given\n        Method uniqueObjectGetter = Employee.class.getMethod(\"getAddress\");\n\n        //when\n        final EntityGetter entityGetter = new EntityGetter(uniqueObjectGetter);\n\n        //then\n        assertThat(entityGetter.getReturnTypeClassName()).isEqualTo(Address.class.getName());\n        assertThat(entityGetter.returnsList()).isFalse();\n\n    }\n\n    @Test\n    public void should_be_able_to_get_associated_named_query() throws Exception {\n        //given\n        Method getManager = Employee.class.getMethod(\"getManager\");\n\n        //when\n        String namedQuery = new EntityGetter(getManager).getAssociatedNamedQuery();\n\n        //then\n        assertThat(namedQuery).isEqualTo(\"Employee.findManagerByEmployeePersistenceId\");\n    }\n\n    @Test\n    public void should_be_able_to_determine_if_getter_return_a_list() throws Exception {\n        //given\n        Method multipleObjectGetter = Employee.class.getMethod(\"getAddresses\");\n\n        //when\n        final EntityGetter entityGetter = new EntityGetter(multipleObjectGetter);\n\n        //then\n        assertThat(entityGetter.returnsList()).isTrue();\n        assertThat(entityGetter.getReturnTypeClassName()).isEqualTo(entityGetter.getTargetEntityClass().getName());\n    }\n\n    @Test\n    public void should_be_able_to_determine_if_getter_do_not_return_a_list() throws Exception {\n        //given\n        Method multipleObjectGetter = Employee.class.getMethod(\"getManager\");\n\n        //when\n        final EntityGetter entityGetter = new EntityGetter(multipleObjectGetter);\n\n        //then\n        assertThat(entityGetter.returnsList()).isFalse();\n        assertThat(entityGetter.getReturnTypeClassName()).isEqualTo(entityGetter.getTargetEntityClass().getName());\n\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-client-resources/src/test/java/org/bonitasoft/engine/bdm/proxy/assertion/ProxyAssert.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.proxy.assertion;\n\nimport static javassist.util.proxy.ProxyFactory.isProxyClass;\n\nimport org.assertj.core.api.AbstractAssert;\n\npublic class ProxyAssert extends AbstractAssert<ProxyAssert, Object> {\n\n    protected ProxyAssert(Object actual) {\n        super(actual, ProxyAssert.class);\n    }\n\n    public static ProxyAssert assertThat(Object entity) {\n        return new ProxyAssert(entity);\n    }\n\n    public ProxyAssert isAProxy() {\n        isNotNull();\n\n        if (!isProxyClass(actual.getClass())) {\n            failWithMessage(\"Expected <%s> to be a proxy\", actual);\n        }\n        return this;\n    }\n\n    public ProxyAssert isNotAProxy() {\n        isNotNull();\n\n        if (isProxyClass(actual.getClass())) {\n            failWithMessage(\"Expected <%s> to not be a proxy\", actual);\n        }\n        return this;\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-client-resources/src/test/java/org/bonitasoft/engine/bdm/proxy/model/Address.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.proxy.model;\n\nimport org.bonitasoft.engine.bdm.Entity;\n\n@SuppressWarnings(\"serial\")\npublic class Address implements Entity {\n\n    @Override\n    public Long getPersistenceId() {\n        return null;\n    }\n\n    @Override\n    public Long getPersistenceVersion() {\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-client-resources/src/test/java/org/bonitasoft/engine/bdm/proxy/model/Child.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.proxy.model;\n\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.OffsetDateTime;\nimport java.util.Date;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport org.bonitasoft.engine.bdm.Entity;\n\n@Data\n@AllArgsConstructor\npublic class Child implements Entity {\n\n    private static final long serialVersionUID = 3000056129946432830L;\n\n    private String name;\n    private Integer age;\n    private Date oldDate;\n    private LocalDate birthdate;\n    private LocalDateTime localDateTime;\n    private OffsetDateTime nextAppointment;\n\n    public Child() {\n        // Empty constructor required for json serialization\n    }\n\n    public Child(String name, Integer age) {\n        this.name = name;\n        this.age = age;\n    }\n\n    @Override\n    public Long getPersistenceId() {\n        return null;\n    }\n\n    @Override\n    public Long getPersistenceVersion() {\n        return null;\n    }\n\n    public String toJson() {\n        return \"{\\\"name\\\" : \\\"\" + name + \"\\\", \\\"age\\\" : \" + age + \", \\\"oldDate\\\" : \"\n                + (oldDate != null ? oldDate.getTime() : null)\n                + \", \\\"birthdate\\\" : \" + (birthdate != null ? \"\\\"\" + birthdate.toString() + \"\\\"\" : null)\n                + \", \\\"localDateTime\\\" : \"\n                + (localDateTime != null ? \"\\\"\" + localDateTime.toString() + \"\\\"\" : null) + \", \\\"nextAppointment\\\" : \"\n                + (nextAppointment != null ? \"\\\"\"\n                        // leave TimeZone offset as is, if any, as this is the Deserializer job to convert it to UTC:\n                        + nextAppointment.toString() + \"\\\"\" : null)\n                + \" }\";\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-client-resources/src/test/java/org/bonitasoft/engine/bdm/proxy/model/Employee.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.proxy.model;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class Employee {\n\n    private List<Employee> employees = new ArrayList<Employee>(10);\n    private List<Address> addresses = new ArrayList<Address>(10);\n\n    private Employee manager;\n    private Address address;\n\n    public Employee() {\n    }\n\n    public void setEmployees(final List<Employee> employees) {\n        this.employees = employees;\n    }\n\n    public List<Employee> getEmployees() {\n        return employees;\n    }\n\n    public Employee getManager() {\n        return manager;\n    }\n\n    public void setManager(Employee manager) {\n        this.manager = manager;\n    }\n\n    @SuppressWarnings({ \"unchecked\", \"rawtypes\" })\n    public void addToAddresses(final Employee addTo) {\n        final List employees = getEmployees();\n        employees.add(addTo);\n    }\n\n    @SuppressWarnings(\"rawtypes\")\n    public void removeFromAddresses(final Employee removeFrom) {\n        final List employees = getEmployees();\n        employees.remove(removeFrom);\n    }\n\n    public List<Address> getAddresses() {\n        return addresses;\n    }\n\n    public void setAddresses(List<Address> addresses) {\n        this.addresses = addresses;\n    }\n\n    public Address getAddress() {\n        return address;\n    }\n\n    public void setAddress(Address address) {\n        this.address = address;\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-client-resources/src/test/java/org/bonitasoft/engine/bdm/proxy/model/Parent.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.proxy.model;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bdm.Entity;\n\n@SuppressWarnings(\"serial\")\npublic class Parent implements Entity {\n\n    private Child child;\n    private List<Child> children;\n\n    public Child getChild() {\n        return child;\n    }\n\n    public void setChild(Child child) {\n        this.child = child;\n    }\n\n    public List<Child> getChildren() {\n        return children;\n    }\n\n    public void setChildren(List<Child> children) {\n        this.children = children;\n    }\n\n    @Override\n    public Long getPersistenceId() {\n        // TODO Auto-generated method stub\n        return null;\n    }\n\n    @Override\n    public Long getPersistenceVersion() {\n        // TODO Auto-generated method stub\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-client-resources/src/test/java/org/bonitasoft/engine/bdm/proxy/model/TestEntity.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.proxy.model;\n\nimport static java.util.Arrays.asList;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.bonitasoft.engine.bdm.lazy.LazyLoaded;\n\n@SuppressWarnings(\"serial\")\npublic class TestEntity implements Entity {\n\n    private TestEntity entity;\n    private String name;\n\n    public TestEntity getEagerEntity() {\n        return new TestEntity();\n    }\n\n    public void setLazyEntity(final TestEntity entity) {\n        this.entity = entity;\n    }\n\n    @LazyLoaded\n    public TestEntity getLazyEntity() {\n        return entity;\n    }\n\n    @LazyLoaded\n    public List<TestEntity> getLazyEntityList() {\n        return Collections.emptyList();\n    }\n\n    public List<TestEntity> getEagerEntities() {\n        return asList(new TestEntity(), new TestEntity());\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(final String name) {\n        this.name = name;\n    }\n\n    public List<String> getStrings() {\n        return asList(\"aString\", \"anotherString\");\n    }\n\n    @Override\n    public Long getPersistenceId() {\n        return 0L;\n    }\n\n    @Override\n    public Long getPersistenceVersion() {\n        return 0L;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + ((entity == null) ? 0 : entity.hashCode());\n        result = prime * result + ((name == null) ? 0 : name.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj)\n            return true;\n        if (obj == null)\n            return false;\n        if (obj instanceof TestEntity other) {\n            if (entity == null) {\n                if (other.entity != null)\n                    return false;\n            } else if (!entity.equals(other.entity))\n                return false;\n            if (name == null) {\n                if (other.name != null)\n                    return false;\n            } else if (!name.equals(other.name))\n                return false;\n            return true;\n        }\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/build.gradle",
    "content": "import org.bonitasoft.engine.gradle.PomUtils\n\nplugins {\n    id 'com.github.johnrengelman.shadow'\n    id 'maven-publish'\n}\n\ndependencies {\n    api project(':services:bonita-commons')\n    api libs.hibernateCore\n    api(libs.eclipseCompiler)\n    api libs.springCore\n    api libs.javassist\n    api project(':bpm:bonita-common')\n    api libs.commonsLang\n    api libs.commonsText\n    api libs.jaxbCodeModel\n    testImplementation libs.assertj\n    testImplementation libs.mockitoCore\n    testImplementation libs.logback\n    testImplementation testFixtures(project(':bpm:bonita-common'))\n    testImplementation project(':services:bonita-business-data:bonita-business-data-api')\n}\n\ngroup = 'org.bonitasoft.engine.data'\n\n\nsourceSets {\n    main {\n        resources { srcDirs \"src/main/resources\", \"../bonita-business-data-client-resources/src/main/java\" }\n    }\n}\n\ntasks.register(\"sourcesJar\", Jar) {\n    from sourceSets.main.allJava\n    archiveClassifier = 'sources'\n}\n\ntasks.register(\"javadocJar\", Jar) {\n    from javadoc\n    archiveClassifier = 'javadoc'\n}\n\n\npublishing {\n    publications {\n        mavenJava(MavenPublication) { \n            from project.components.java \n            artifact project.sourcesJar\n            artifact project.javadocJar\n            pom { pom ->\n                name = \"Bonita Business Data Generator\"\n                description = \"Bonita Business Data Generator is the library used to generate and compile a Business Data Model\"\n                PomUtils.pomCommunityPublication(pom)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/AbstractBDMCodeGenerator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator;\n\nimport static org.bonitasoft.engine.bdm.validator.rule.QueryParameterValidationRule.FORBIDDEN_PARAMETER_NAMES;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Collection;\n\nimport com.sun.codemodel.JBlock;\nimport com.sun.codemodel.JClass;\nimport com.sun.codemodel.JClassAlreadyExistsException;\nimport com.sun.codemodel.JDefinedClass;\nimport com.sun.codemodel.JExpr;\nimport com.sun.codemodel.JMethod;\nimport com.sun.codemodel.JType;\nimport com.sun.codemodel.JVar;\nimport org.bonitasoft.engine.bdm.BDMQueryUtil;\nimport org.bonitasoft.engine.bdm.BusinessObjectModelValidationException;\nimport org.bonitasoft.engine.bdm.dao.BusinessObjectDAO;\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.model.Query;\nimport org.bonitasoft.engine.bdm.model.QueryParameter;\nimport org.bonitasoft.engine.bdm.model.field.Field;\nimport org.bonitasoft.engine.bdm.model.field.RelationField;\nimport org.bonitasoft.engine.bdm.model.field.SimpleField;\nimport org.bonitasoft.engine.bdm.validator.BusinessObjectModelValidator;\nimport org.bonitasoft.engine.bdm.validator.ValidationStatus;\n\n/**\n * @author Romain Bioteau\n * @author Matthieu Chaffotte\n */\npublic abstract class AbstractBDMCodeGenerator extends CodeGenerator {\n\n    private static final String DAO_SUFFIX = \"DAO\";\n\n    protected static final String DAO_IMPL_SUFFIX = \"DAOImpl\";\n\n    private static final String NEW_INSTANCE_METHOD_NAME = \"newInstance\";\n\n    protected AbstractBDMCodeGenerator() {\n        super();\n    }\n\n    @Override\n    public AbstractBDMCodeGenerator disableRuntimeClassesValidation() {\n        super.disableRuntimeClassesValidation();\n        return this;\n    }\n\n    public void generateBom(final BusinessObjectModel bom, final File destDir)\n            throws IOException, JClassAlreadyExistsException, BusinessObjectModelValidationException,\n            ClassNotFoundException {\n        final BusinessObjectModelValidator validator = new BusinessObjectModelValidator();\n        final ValidationStatus validationStatus = validator.validate(bom);\n        if (!validationStatus.isOk()) {\n            throw new BusinessObjectModelValidationException(validationStatus);\n        }\n        buildJavaModelFromBom(bom);\n        super.generate(destDir);\n    }\n\n    private void buildJavaModelFromBom(final BusinessObjectModel bom)\n            throws JClassAlreadyExistsException, ClassNotFoundException {\n        if (bom == null) {\n            throw new IllegalArgumentException(\"bom is null\");\n        }\n        final EntityCodeGenerator entityCodeGenerator = new EntityCodeGenerator(this, bom);\n        for (final BusinessObject bo : bom.getBusinessObjects()) {\n            final JDefinedClass entity = entityCodeGenerator.addEntity(bo);\n            addDAO(bo, entity);\n        }\n    }\n\n    protected void addNewInstanceMethodBody(final JMethod method, final JDefinedClass entity) {\n        final JBlock methodBody = method.body();\n        for (final JVar param : method.params()) {\n            addNotNullParamCheck(methodBody, param);\n        }\n        final JVar instanceRef = methodBody.decl(entity, \"instance\", JExpr._new(entity));\n        for (final JVar param : method.params()) {\n            callSetter(methodBody, param, instanceRef, entity);\n        }\n        methodBody._return(instanceRef);\n    }\n\n    private void callSetter(final JBlock methodBody, final JVar param, final JVar instanceRef,\n            final JDefinedClass entity) {\n        final JMethod setter = getSetterForParam(param, entity);\n        if (setter == null) {\n            throw new IllegalStateException(\"No setter found for parameter \" + param.name());\n        }\n        methodBody.add(instanceRef.invoke(setter).arg(param));\n    }\n\n    private JMethod getSetterForParam(final JVar param, final JDefinedClass entity) {\n        final String setterName = getSetterName(param);\n        return findMethodWithSignature(param, entity, setterName);\n    }\n\n    private JMethod findMethodWithSignature(final JVar param, final JDefinedClass entity, final String name) {\n        for (final JMethod m : entity.methods()) {\n            if (!m.name().equals(name)) {\n                continue;\n            }\n\n            if (m.params().size() == 1) {\n                final JVar jVar = m.params().get(0);\n                if (jVar.type().equals(param.type()) || jVar.type().fullName().equals(param.type().fullName())) {\n                    return m;\n                }\n            }\n        }\n        return null;\n    }\n\n    private void addNotNullParamCheck(final JBlock methodBody, final JVar param) {\n        methodBody._if(param.eq(JExpr._null()))._then()\n                ._throw(JExpr._new(getModel().ref(IllegalArgumentException.class))\n                        .arg(JExpr.lit(param.name() + \" cannot be null\")));\n    }\n\n    protected abstract void addDAO(final BusinessObject bo, JDefinedClass entity)\n            throws JClassAlreadyExistsException, ClassNotFoundException;\n\n    protected JDefinedClass createDAOInterface(final BusinessObject bo, final JDefinedClass entity)\n            throws JClassAlreadyExistsException {\n        final String daoInterfaceClassName = toDaoInterfaceClassname(bo);\n        final JDefinedClass daoInterface = addInterface(daoInterfaceClassName);\n        addInterface(daoInterface, BusinessObjectDAO.class.getName());\n\n        // Add method signature in interface for provided queries\n        for (final Query q : BDMQueryUtil.createProvidedQueriesForBusinessObject(bo)) {\n            createMethodForQuery(entity, daoInterface, q);\n        }\n\n        // Add method signature in interface for custom queries\n        for (final Query q : bo.getQueries()) {\n            createMethodForQuery(entity, daoInterface, q);\n        }\n\n        createMethodForNewInstance(bo, entity, daoInterface);\n\n        return daoInterface;\n    }\n\n    protected JMethod createMethodForNewInstance(final BusinessObject bo, final JDefinedClass entity,\n            final JDefinedClass daoInterface) {\n        final JMethod newInstanceMethod = addMethodSignature(daoInterface, NEW_INSTANCE_METHOD_NAME, entity);\n        for (final Field field : bo.getFields()) {\n            if (!field.isNullable()) {\n                String typeClassName = null;\n                if (field instanceof SimpleField) {\n                    typeClassName = ((SimpleField) field).getType().getClazz().getName();\n                } else if (field instanceof RelationField) {\n                    typeClassName = ((RelationField) field).getReference().getQualifiedName();\n                }\n                newInstanceMethod.param(getModel().ref(typeClassName), field.getName());\n            }\n        }\n        return newInstanceMethod;\n    }\n\n    protected JMethod createMethodForQuery(final JDefinedClass entity, final JDefinedClass targetClass,\n            final Query query) {\n        final String methodName = query.getName();\n        final JMethod queryMethod = createQueryMethod(entity, targetClass, methodName, query.getReturnType());\n        for (final QueryParameter param : query.getQueryParameters()) {\n            queryMethod.param(getModel().ref(param.getClassName()), param.getName());\n        }\n        addOptionalPaginationParameters(queryMethod, query);\n        return queryMethod;\n    }\n\n    private JMethod createQueryMethod(final JDefinedClass entity, final JDefinedClass targetClass, final String name,\n            final String returnTypeName) {\n        JType returnType;\n        if (returnTypeName.equals(entity.fullName())) {\n            returnType = entity;\n        } else {\n            returnType = getModel().ref(returnTypeName);\n        }\n        final JClass collectionType = getModel().ref(Collection.class.getName());\n        if (returnType instanceof JClass && collectionType.isAssignableFrom((JClass) returnType)) {\n            returnType = ((JClass) returnType).narrow(entity);\n        }\n        return addMethodSignature(targetClass, name, returnType);\n    }\n\n    private String toDaoInterfaceClassname(final BusinessObject bo) {\n        return bo.getQualifiedName() + DAO_SUFFIX;\n    }\n\n    private void addOptionalPaginationParameters(final JMethod queryMethod, final Query query) {\n        if (query.hasMultipleResults()) {\n            for (final String param : FORBIDDEN_PARAMETER_NAMES) {\n                queryMethod.param(getModel().ref(int.class.getName()), param);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/AbstractBDMJarBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator;\n\nimport static org.apache.commons.io.FileUtils.deleteDirectory;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport javax.persistence.Entity;\n\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport org.apache.commons.io.FileUtils;\nimport org.apache.commons.io.filefilter.IOFileFilter;\nimport org.apache.commons.io.filefilter.TrueFileFilter;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.business.data.generator.compiler.JDTCompiler;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException;\nimport org.bonitasoft.engine.commons.io.IOUtil;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic abstract class AbstractBDMJarBuilder {\n\n    private static final Logger log = LoggerFactory.getLogger(AbstractBDMJarBuilder.class);\n    private final JDTCompiler compiler;\n\n    private final AbstractBDMCodeGenerator bdmCodeGenerator;\n\n    @Deprecated\n    protected AbstractBDMJarBuilder(AbstractBDMCodeGenerator bdmCodeGenerator, final JDTCompiler compiler) {\n        this.bdmCodeGenerator = bdmCodeGenerator;\n        this.compiler = compiler;\n    }\n\n    protected AbstractBDMJarBuilder(AbstractBDMCodeGenerator bdmCodeGenerator) {\n        this.bdmCodeGenerator = bdmCodeGenerator;\n        this.compiler = new JDTCompiler();\n    }\n\n    /**\n     * @param bom the business object model to generate the jar from\n     * @param fileFilter\n     *        filter the entries to be added or not in generated jar\n     * @return the content of the generated jar\n     * @throws BDMJarGenerationException if an error occurs during the generation of the jar\n     */\n    public byte[] build(final BusinessObjectModel bom, final IOFileFilter fileFilter) throws BDMJarGenerationException {\n        try {\n            final File tmpBDMDirectory = Files.createTempDirectory(\"bdm\").toFile();\n            try {\n                addSourceFilesToDirectory(bom, tmpBDMDirectory);\n                var additionalClasspath = getCompileDependencies();\n                if (log.isDebugEnabled()) {\n                    log.debug(\"Compiling BDM classes using classpath: {}\",\n                            additionalClasspath.stream()\n                                    .map(File::getName)\n                                    .collect(Collectors.joining(File.pathSeparator)));\n                }\n                compiler.compile(tmpBDMDirectory, tmpBDMDirectory, additionalClasspath.toArray(File[]::new));\n                return generateJar(tmpBDMDirectory, fileFilter);\n            } finally {\n                deleteDirectory(tmpBDMDirectory);\n            }\n        } catch (final Exception e) {\n            throw new BDMJarGenerationException(e);\n        }\n    }\n\n    /**\n     * Add BDM compile dependencies required to compile the BDM classes.\n     * It uses the current classloader to find the required classes and retrieve their corresponding jar files.\n     *\n     * @throws ClassNotFoundException if a required class cannot be found in the current classloader\n     */\n    protected Set<File> getCompileDependencies() throws ClassNotFoundException {\n        var compileDependencies = new HashSet<File>();\n        compileDependencies.add(JDTCompiler.lookupJarContaining(Entity.class));\n        compileDependencies.add(JDTCompiler.lookupJarContaining(org.bonitasoft.engine.bdm.Entity.class));\n        compileDependencies.add(JDTCompiler.lookupJarContaining(JsonIgnore.class));\n        compileDependencies.add(JDTCompiler.lookupJarContaining(\"org.hibernate.annotations.Parameter\"));\n        compileDependencies.add(JDTCompiler.lookupJarContaining(DateConverter.class));\n        compileDependencies.add(JDTCompiler.lookupJarContaining(SBonitaRuntimeException.class));\n        compileDependencies\n                .add(JDTCompiler.lookupJarContaining(\"org.bonitasoft.engine.business.data.BusinessDataRepository\"));\n        return compileDependencies;\n    }\n\n    protected void addSourceFilesToDirectory(final BusinessObjectModel bom, final File directory)\n            throws CodeGenerationException {\n        try {\n            bdmCodeGenerator.generateBom(bom, directory);\n        } catch (Exception e) {\n            throw new CodeGenerationException(\"Error when generating source files for business object model\", e);\n        }\n    }\n\n    private byte[] generateJar(final File directory, final IOFileFilter fileFilter) throws IOException {\n        final Collection<File> files = FileUtils.listFiles(directory, fileFilter, TrueFileFilter.TRUE);\n        final Map<String, byte[]> resources = new HashMap<>();\n        for (final File file : files) {\n            final String relativeName = directory.toURI().relativize(file.toURI()).getPath();\n            final byte[] content = FileUtils.readFileToByteArray(file);\n            resources.put(relativeName, content);\n        }\n        return IOUtil.generateJar(resources);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/BDMJarGenerationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator;\n\npublic class BDMJarGenerationException extends Exception {\n\n    public BDMJarGenerationException(Exception e) {\n        super(e);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/CodeGenerationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator;\n\npublic class CodeGenerationException extends Exception {\n\n    private static final long serialVersionUID = 5686073269279155335L;\n\n    public CodeGenerationException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/CodeGenerator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.io.PrintStream;\nimport java.lang.annotation.Annotation;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Target;\nimport java.lang.management.ManagementFactory;\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport javax.lang.model.SourceVersion;\n\nimport com.sun.codemodel.ClassType;\nimport com.sun.codemodel.CodeWriter;\nimport com.sun.codemodel.JAnnotatable;\nimport com.sun.codemodel.JAnnotationUse;\nimport com.sun.codemodel.JBlock;\nimport com.sun.codemodel.JClass;\nimport com.sun.codemodel.JClassAlreadyExistsException;\nimport com.sun.codemodel.JCodeModel;\nimport com.sun.codemodel.JConditional;\nimport com.sun.codemodel.JDefinedClass;\nimport com.sun.codemodel.JExpr;\nimport com.sun.codemodel.JExpression;\nimport com.sun.codemodel.JFieldRef;\nimport com.sun.codemodel.JFieldVar;\nimport com.sun.codemodel.JMethod;\nimport com.sun.codemodel.JMod;\nimport com.sun.codemodel.JType;\nimport com.sun.codemodel.JVar;\nimport com.sun.codemodel.writer.FileCodeWriter;\nimport com.sun.codemodel.writer.ProgressCodeWriter;\nimport org.apache.commons.text.WordUtils;\nimport org.bonitasoft.engine.bdm.model.field.Field;\nimport org.bonitasoft.engine.bdm.model.field.FieldType;\nimport org.bonitasoft.engine.bdm.model.field.RelationField;\nimport org.bonitasoft.engine.bdm.model.field.SimpleField;\n\n/**\n * @author Romain Bioteau\n * @author Matthieu Chaffotte\n */\npublic class CodeGenerator {\n\n    private final JCodeModel model;\n    protected boolean shouldValidateRuntimeClasses = true;\n\n    public CodeGenerator() {\n        model = new JCodeModel();\n    }\n\n    public CodeGenerator disableRuntimeClassesValidation() {\n        this.shouldValidateRuntimeClasses = false;\n        return this;\n    }\n\n    public void generate(final File destDir) throws IOException {\n        Charset encoding = StandardCharsets.UTF_8;\n        try (PrintStream statusStream = new PrintStream(new NullStream(), false, encoding)) {\n            CodeWriter src = new ProgressCodeWriter(new FileCodeWriter(destDir, encoding.name()),\n                    statusStream);\n            CodeWriter res = new ProgressCodeWriter(new FileCodeWriter(destDir, encoding.name()),\n                    statusStream);\n            model.build(src, res);\n        }\n    }\n\n    public JDefinedClass addClass(final String fullyQualifiedName) throws JClassAlreadyExistsException {\n        if (fullyQualifiedName == null || fullyQualifiedName.isEmpty()) {\n            throw new IllegalArgumentException(\"Classname cannot be null or empty\");\n        }\n        if (!SourceVersion.isName(fullyQualifiedName)) {\n            throw new IllegalArgumentException(\"Classname \" + fullyQualifiedName + \" is not a valid qualified name\");\n        }\n        if (shouldValidateRuntimeClasses) {\n            validateClassNotExistsInRuntime(fullyQualifiedName);\n        }\n        return model._class(fullyQualifiedName);\n    }\n\n    private void validateClassNotExistsInRuntime(final String qualifiedName) {\n        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        try {\n            var clazz = contextClassLoader.loadClass(qualifiedName);\n            // Here the class is found, which is NOT normal! Let's investigate:\n            final StringBuilder message = new StringBuilder(\n                    \"Class \" + qualifiedName + \" already exists in target runtime environment\");\n            final ClassLoader classLoader = clazz.getClassLoader();\n            if (classLoader != null) {\n                if (classLoader instanceof URLClassLoader) {\n                    for (URL url : ((URLClassLoader) classLoader).getURLs()) {\n                        message.append(\"\\n\").append(url.toString());\n                    }\n                } else {\n                    message.append(\"\\nCurrent classloader is NOT an URLClassLoader: \").append(classLoader.toString());\n                }\n            }\n            message.append(\"\\nCurrent JVM Id where the class is found: \")\n                    .append(ManagementFactory.getRuntimeMXBean().getName());\n            message.append(\n                    \"\\nMake sure you did not manually add the jar files bdm-model.jar / bdm-dao.jar somewhere on the classpath.\");\n            message.append(\n                    \"\\nThose jar files are handled by Bonita internally and should not be manipulated outside Bonita.\");\n            throw new IllegalArgumentException(message.toString());\n        } catch (final ClassNotFoundException ignored) {\n            // here is the normal behaviour\n        }\n    }\n\n    public JDefinedClass addInterface(final JDefinedClass definedClass, final String fullyQualifiedName) {\n        return definedClass._implements(model.ref(fullyQualifiedName));\n    }\n\n    public JDefinedClass addInterface(final String fullyQualifiedName) throws JClassAlreadyExistsException {\n        if (!fullyQualifiedName.contains(\".\")) {\n            return model.rootPackage()._class(JMod.PUBLIC, fullyQualifiedName, ClassType.INTERFACE);\n        }\n        return model._class(fullyQualifiedName, ClassType.INTERFACE);\n    }\n\n    public JFieldVar addField(final JDefinedClass definedClass, final String fieldName, final Class<?> type) {\n        validateFieldName(fieldName);\n        if (type == null) {\n            throw new IllegalArgumentException(\"Field type cannot be null\");\n        }\n        return definedClass.field(JMod.PRIVATE, type, fieldName);\n    }\n\n    public JFieldVar addField(final JDefinedClass definedClass, final Field field) {\n        return addField(definedClass, field.getName(), toJavaClass(field));\n    }\n\n    public JFieldVar addField(final JDefinedClass definedClass, final String fieldName, final JClass type) {\n        validateFieldName(fieldName);\n        if (type == null) {\n            throw new IllegalArgumentException(\"Field type cannot be null\");\n        }\n        return definedClass.field(JMod.PRIVATE, type, fieldName);\n    }\n\n    private void validateFieldName(final String fieldName) {\n        if (fieldName == null || fieldName.isEmpty()) {\n            throw new IllegalArgumentException(\"Field name cannot be null or empty\");\n        }\n        if (SourceVersion.isKeyword(fieldName)) {\n            throw new IllegalArgumentException(\"Field \" + fieldName + \" is a resered keyword\");\n        }\n        if (!SourceVersion.isIdentifier(fieldName)) {\n            throw new IllegalArgumentException(\"Field \" + fieldName + \" is not a valid Java identifier\");\n        }\n    }\n\n    @SuppressWarnings(\"rawtypes\")\n    private JClass narrowClass(final Class<? extends Collection> collectionClass, final JClass narrowClass) {\n        final JClass collectionJClass = getModel().ref(collectionClass);\n        return collectionJClass.narrow(narrowClass);\n    }\n\n    JFieldVar addListField(final JDefinedClass entityClass, final Field field) {\n        final JClass fieldClass = toJavaClass(field);\n        final JClass fieldListClass = narrowClass(List.class, fieldClass);\n        final JClass arrayListFieldClazz = narrowClass(ArrayList.class, fieldClass);\n\n        final JFieldVar listFieldVar = entityClass.field(JMod.PRIVATE, fieldListClass, field.getName());\n\n        final JExpression newInstance = JExpr._new(arrayListFieldClazz).arg(JExpr.lit(10));\n        listFieldVar.init(newInstance);\n        return listFieldVar;\n    }\n\n    JClass toJavaClass(final Field field) {\n        if (field instanceof SimpleField) {\n            final Class<?> fieldClass = ((SimpleField) field).getType().getClazz();\n            return getModel().ref(fieldClass);\n        }\n        final String qualifiedName = ((RelationField) field).getReference().getQualifiedName();\n        return getModel().ref(qualifiedName);\n    }\n\n    public JClass toJavaClass(final FieldType type) {\n        return getModel().ref(type.getClazz());\n    }\n\n    void addDefaultConstructor(final JDefinedClass definedClass) {\n        definedClass.constructor(JMod.PUBLIC);\n    }\n\n    public JMethod addSetter(final JDefinedClass definedClass, final JFieldVar field) {\n        final JMethod method = definedClass.method(JMod.PUBLIC, Void.TYPE, getSetterName(field));\n        method.param(field.type(), field.name());\n        method.body().assign(JExpr._this().ref(field.name()), JExpr.ref(field.name()));\n        return method;\n    }\n\n    JMethod addListSetter(final JDefinedClass definedClass, final JFieldVar field) {\n        final JMethod method = definedClass.method(JMod.PUBLIC, Void.TYPE, getSetterName(field));\n        method.param(field.type(), field.name());\n        final JFieldRef thisField = JExpr._this().ref(field.name());\n        final JConditional ifListIsNull = method.body()._if(thisField.eq(JExpr._null()));\n\n        ifListIsNull._then().assign(JExpr._this().ref(field.name()), JExpr.ref(field.name()));\n\n        final JBlock elseBlock = ifListIsNull._else();\n        final JVar copyVar = elseBlock.decl(field.type(), \"copy\",\n                JExpr._new(getModel().ref(ArrayList.class)).arg(field));\n        elseBlock.invoke(JExpr._this().ref(field.name()), \"clear\");\n        elseBlock.invoke(JExpr._this().ref(field.name()), \"addAll\").arg(copyVar);\n\n        return method;\n    }\n\n    public JMethod addGetter(final JDefinedClass definedClass, final JFieldVar field) {\n        final JMethod method = definedClass.method(JMod.PUBLIC, field.type(), getGetterName(field));\n        final JBlock block = method.body();\n        block._return(field);\n        return method;\n    }\n\n    public JMethod addMethodSignature(final JDefinedClass definedClass, final String methodName,\n            final JType returnType) {\n        return definedClass.method(JMod.PUBLIC, returnType, methodName);\n    }\n\n    public JMethod addAddMethod(final JDefinedClass definedClass, final Field field) {\n        return addListMethod(definedClass, field, \"add\", \"addTo\");\n    }\n\n    public JMethod addRemoveMethod(final JDefinedClass definedClass, final Field field) {\n        return addListMethod(definedClass, field, \"remove\", \"removeFrom\");\n    }\n\n    private JMethod addListMethod(final JDefinedClass definedClass, final Field field, final String listMethodName,\n            final String parameterName) {\n        final JClass fieldClass = toJavaClass(field);\n        final StringBuilder builder = new StringBuilder(parameterName);\n        builder.append(WordUtils.capitalize(field.getName()));\n        final JMethod method = definedClass.method(JMod.PUBLIC, void.class, builder.toString());\n        final JVar adderParam = method.param(fieldClass, parameterName);\n        final JBlock body = method.body();\n        final JVar decl = body.decl(getModel().ref(List.class), field.getName(), JExpr.invoke(getGetterName(field)));\n        body.add(decl.invoke(listMethodName).arg(adderParam));\n        return method;\n    }\n\n    private String getGetterName(final JVar field) {\n        final JType type = field.type();\n        final boolean bool = Boolean.class.getName().equals(type.fullName());\n        return getGetterName(bool, field.name());\n    }\n\n    private String getGetterName(final Field field) {\n        final boolean bool = field instanceof SimpleField && FieldType.BOOLEAN.equals(((SimpleField) field).getType())\n                && !field.isCollection();\n        return getGetterName(bool, field.getName());\n    }\n\n    private String getGetterName(final boolean bool, final String fieldName) {\n        final StringBuilder builder = new StringBuilder();\n        if (bool) {\n            builder.append(\"is\");\n        } else {\n            builder.append(\"get\");\n        }\n        builder.append(WordUtils.capitalize(fieldName));\n        return builder.toString();\n    }\n\n    String getSetterName(final JVar field) {\n        return \"set\" + WordUtils.capitalize(field.name());\n    }\n\n    public JCodeModel getModel() {\n        return model;\n    }\n\n    protected JAnnotationUse addAnnotation(final JAnnotatable annotable,\n            final Class<? extends Annotation> annotationType) {\n        final Set<ElementType> supportedElementTypes = getSupportedElementTypes(annotationType);\n        checkAnnotationTarget(annotable, annotationType, supportedElementTypes);\n        return annotable.annotate(model.ref(annotationType));\n    }\n\n    private void checkAnnotationTarget(final JAnnotatable annotable, final Class<? extends Annotation> annotationType,\n            final Set<ElementType> supportedElementTypes) {\n        if (annotable instanceof JClass && !supportedElementTypes.isEmpty()\n                && !supportedElementTypes.contains(ElementType.TYPE)) {\n            throw new IllegalArgumentException(annotationType.getName() + \" is not supported for \" + annotable);\n        }\n        if (annotable instanceof JFieldVar && !supportedElementTypes.isEmpty()\n                && !supportedElementTypes.contains(ElementType.FIELD)) {\n            throw new IllegalArgumentException(annotationType.getName() + \" is not supported for \" + annotable);\n        }\n        if (annotable instanceof JMethod && !supportedElementTypes.isEmpty()\n                && !supportedElementTypes.contains(ElementType.METHOD)) {\n            throw new IllegalArgumentException(annotationType.getName() + \" is not supported for \" + annotable);\n        }\n    }\n\n    private Set<ElementType> getSupportedElementTypes(final Class<? extends Annotation> annotationType) {\n        final Set<ElementType> elementTypes = new HashSet<ElementType>();\n        final Target targetAnnotation = annotationType.getAnnotation(Target.class);\n        if (targetAnnotation != null) {\n            final ElementType[] value = targetAnnotation.value();\n            if (value != null) {\n                for (final ElementType et : value) {\n                    elementTypes.add(et);\n                }\n            }\n        }\n        return elementTypes;\n    }\n\n    private static class NullStream extends OutputStream {\n\n        NullStream() {\n        }\n\n        public void write(int b) throws IOException {\n        }\n\n        public void close() throws IOException {\n        }\n\n        public void flush() throws IOException {\n        }\n\n        public void write(byte[] b, int off, int len) throws IOException {\n        }\n\n        public void write(byte[] b) throws IOException {\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/DateAndTimeConverter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator;\n\nimport java.time.LocalDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.time.format.DateTimeParseException;\n\nimport javax.persistence.AttributeConverter;\nimport javax.persistence.Converter;\n\n/**\n * @author Danila Mazour\n */\n\n@Converter\npublic class DateAndTimeConverter implements AttributeConverter<LocalDateTime, String> {\n\n    @Override\n    public String convertToDatabaseColumn(LocalDateTime localDateTime) {\n        if (localDateTime != null) {\n            return localDateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);\n        } else {\n            return null;\n        }\n    }\n\n    @Override\n    public LocalDateTime convertToEntityAttribute(String s) {\n\n        if (s != null) {\n            try {\n                return LocalDateTime.parse(s, DateTimeFormatter.ISO_LOCAL_DATE_TIME);\n            } catch (DateTimeParseException e) {\n                throw new RuntimeException(\n                        \"Database date & time format must be ISO-8601 compliant ( yyyy-mm-ddThh:mm:ss )\", e);\n            }\n        } else {\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/DateConverter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator;\n\nimport java.time.LocalDate;\nimport java.time.format.DateTimeFormatter;\nimport java.time.format.DateTimeParseException;\n\nimport javax.persistence.AttributeConverter;\nimport javax.persistence.Converter;\n\n/**\n * @author Danila Mazour\n */\n\n@Converter\npublic class DateConverter implements AttributeConverter<LocalDate, String> {\n\n    @Override\n    public String convertToDatabaseColumn(LocalDate localDate) {\n\n        if (localDate != null) {\n            return localDate.toString();\n        } else {\n            return null;\n        }\n    }\n\n    @Override\n    public LocalDate convertToEntityAttribute(String s) {\n\n        if (s != null) {\n            try {\n                return LocalDate.parse(s, DateTimeFormatter.ISO_LOCAL_DATE);\n            } catch (DateTimeParseException e) {\n                throw new RuntimeException(\"Database date format must be ISO-8601 compliant ( yyyy-mm-dd )\", e);\n            }\n        } else {\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/EntityCodeGenerator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator;\n\nimport java.util.List;\n\nimport javax.persistence.*;\n\nimport com.sun.codemodel.JAnnotationArrayMember;\nimport com.sun.codemodel.JAnnotationUse;\nimport com.sun.codemodel.JClassAlreadyExistsException;\nimport com.sun.codemodel.JDefinedClass;\nimport com.sun.codemodel.JFieldVar;\nimport com.sun.codemodel.JMethod;\nimport org.bonitasoft.engine.bdm.BDMQueryUtil;\nimport org.bonitasoft.engine.bdm.lazy.LazyLoaded;\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.model.Index;\nimport org.bonitasoft.engine.bdm.model.Query;\nimport org.bonitasoft.engine.bdm.model.UniqueConstraint;\nimport org.bonitasoft.engine.bdm.model.field.Field;\nimport org.bonitasoft.engine.bdm.model.field.FieldType;\nimport org.bonitasoft.engine.bdm.model.field.RelationField;\nimport org.bonitasoft.engine.bdm.model.field.SimpleField;\nimport org.hibernate.annotations.GenericGenerator;\nimport org.hibernate.annotations.Parameter;\nimport org.hibernate.id.enhanced.SequenceStyleGenerator;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Colin PUY,\n * @author Matthieu Chaffotte\n */\npublic class EntityCodeGenerator {\n\n    private final CodeGenerator codeGenerator;\n\n    private final RelationFieldAnnotator relationFieldAnnotator;\n\n    private final BusinessObjectModel bom;\n\n    protected static final Logger LOGGER = LoggerFactory.getLogger(EntityCodeGenerator.class);\n\n    public EntityCodeGenerator(final CodeGenerator codeGenerator, final BusinessObjectModel bom) {\n        this.codeGenerator = codeGenerator;\n        this.bom = bom;\n        relationFieldAnnotator = new RelationFieldAnnotator(codeGenerator);\n    }\n\n    public JDefinedClass addEntity(final BusinessObject bo) throws JClassAlreadyExistsException {\n        final String qualifiedName = bo.getQualifiedName();\n\n        JDefinedClass entityClass = codeGenerator.addClass(qualifiedName);\n        entityClass = codeGenerator.addInterface(entityClass, org.bonitasoft.engine.bdm.Entity.class.getName());\n        entityClass.javadoc().add(bo.getDescription());\n\n        final JAnnotationUse entityAnnotation = codeGenerator.addAnnotation(entityClass, Entity.class);\n        entityAnnotation.param(\"name\", entityClass.name());\n\n        addIndexAnnotations(bo, entityClass);\n        addUniqueConstraintAnnotations(bo, entityClass);\n        addQueriesAnnotation(bo, entityClass);\n\n        String dbVendor = determineDbVendor();\n        addFieldsAndMethods(bo, entityClass, dbVendor);\n\n        codeGenerator.addDefaultConstructor(entityClass);\n\n        return entityClass;\n    }\n\n    private void addFieldsAndMethods(final BusinessObject bo, final JDefinedClass entityClass, String dbVendor) {\n        addPersistenceIdFieldAndAccessors(entityClass, dbVendor);\n        addPersistenceVersionFieldAndAccessors(entityClass);\n\n        for (final Field field : bo.getFields()) {\n            final JFieldVar fieldVar = addField(entityClass, field, dbVendor);\n            addAccessors(entityClass, fieldVar, field);\n            addModifiers(entityClass, field);\n        }\n    }\n\n    private void addQueriesAnnotation(final BusinessObject bo, final JDefinedClass entityClass) {\n        final JAnnotationUse namedQueriesAnnotation = codeGenerator.addAnnotation(entityClass, NamedQueries.class);\n        final JAnnotationArrayMember valueArray = namedQueriesAnnotation.paramArray(\"value\");\n\n        // Add provided queries\n        for (final Query providedQuery : BDMQueryUtil.createProvidedQueriesForBusinessObject(bo)) {\n            addNamedQuery(entityClass, valueArray, providedQuery.getName(), providedQuery.getContent());\n        }\n\n        // Add method for lazy fields\n        for (final Query query : BDMQueryUtil.createProvidedQueriesForLazyField(bom, bo)) {\n            addNamedQuery(entityClass, valueArray, query.getName(), query.getContent());\n        }\n\n        // Add custom queries\n        for (final Query query : bo.getQueries()) {\n            addNamedQuery(entityClass, valueArray, query.getName(), query.getContent());\n        }\n    }\n\n    private void addUniqueConstraintAnnotations(final BusinessObject businessObject, final JDefinedClass entityClass) {\n        final JAnnotationUse tableAnnotation = codeGenerator.addAnnotation(entityClass, Table.class);\n        tableAnnotation.param(\"name\", entityClass.name().toUpperCase());\n\n        final List<UniqueConstraint> uniqueConstraints = businessObject.getUniqueConstraints();\n        if (!uniqueConstraints.isEmpty()) {\n            final JAnnotationArrayMember uniqueConstraintsArray = tableAnnotation.paramArray(\"uniqueConstraints\");\n            for (final UniqueConstraint uniqueConstraint : uniqueConstraints) {\n                final JAnnotationUse uniqueConstraintAnnotation = uniqueConstraintsArray\n                        .annotate(javax.persistence.UniqueConstraint.class);\n                uniqueConstraintAnnotation.param(\"name\", uniqueConstraint.getName().toUpperCase());\n                final JAnnotationArrayMember columnNamesParamArray = uniqueConstraintAnnotation\n                        .paramArray(\"columnNames\");\n                for (final String fieldName : uniqueConstraint.getFieldNames()) {\n                    columnNamesParamArray.param(getFieldRealColumnName(businessObject, fieldName));\n                }\n            }\n        }\n    }\n\n    private void addIndexAnnotations(final BusinessObject businessObject, final JDefinedClass entityClass) {\n        final List<Index> indexes = businessObject.getIndexes();\n        if (indexes != null && !indexes.isEmpty()) {\n            final JAnnotationUse hibTabAnnotation = codeGenerator.addAnnotation(entityClass,\n                    org.hibernate.annotations.Table.class);\n            hibTabAnnotation.param(\"appliesTo\", entityClass.name().toUpperCase());\n            final JAnnotationArrayMember indexesArray = hibTabAnnotation.paramArray(\"indexes\");\n            for (final Index index : indexes) {\n                final JAnnotationUse indexAnnotation = indexesArray.annotate(org.hibernate.annotations.Index.class);\n                indexAnnotation.param(\"name\", index.getName().toUpperCase());\n                final JAnnotationArrayMember columnParamArray = indexAnnotation.paramArray(\"columnNames\");\n                for (final String fieldName : index.getFieldNames()) {\n                    columnParamArray.param(getFieldRealColumnName(businessObject, fieldName).toUpperCase());\n                }\n            }\n        }\n    }\n\n    /**\n     * get real column name used in database\n     *\n     * @return fieldName for simple fields or reduced name suffix by \"_PID\" when we have an entity relationship\n     */\n    private String getFieldRealColumnName(BusinessObject businessObject, String fieldName) {\n        String columnName;\n        if (businessObject.isARelationField(fieldName)) {\n            columnName = relationFieldAnnotator.getJoinColumnName(fieldName);\n        } else {\n            columnName = fieldName;\n        }\n        return columnName;\n    }\n\n    private void addNamedQuery(final JDefinedClass entityClass, final JAnnotationArrayMember valueArray,\n            final String name, final String content) {\n        final JAnnotationUse nameQueryAnnotation = valueArray.annotate(NamedQuery.class);\n        nameQueryAnnotation.param(\"name\", entityClass.name() + \".\" + name);\n        nameQueryAnnotation.param(\"query\", content);\n    }\n\n    public void addPersistenceIdFieldAndAccessors(final JDefinedClass entityClass, String dbVendor) {\n        final JFieldVar idFieldVar = codeGenerator.addField(entityClass, Field.PERSISTENCE_ID,\n                codeGenerator.toJavaClass(FieldType.LONG));\n        codeGenerator.addAnnotation(idFieldVar, Id.class);\n        JAnnotationUse generateValue = codeGenerator.addAnnotation(idFieldVar, GeneratedValue.class);\n        switch (dbVendor) {\n            case \"h2\":\n            case \"postgres\":\n            case \"oracle\":\n                // This generates the following annotation:\n                // @GeneratedValue(generator = \"default_bonita_seq_generator\")\n                // @GenericGenerator(\n                //      name = \"default_bonita_seq_generator\",\n                //      strategy = \"org.hibernate.id.enhanced.SequenceStyleGenerator\",\n                //      parameters = {\n                //          @Parameter(name = \"sequence_name\", value = \"hibernate_sequence\")\n                //      })\n                // maps to default Hibernate sequence name in database 'hibernate_sequence'\n                generateValue.param(\"generator\", \"default_bonita_seq_generator\");\n                JAnnotationUse genericGenerator = codeGenerator.addAnnotation(idFieldVar, GenericGenerator.class);\n                genericGenerator.param(\"name\", \"default_bonita_seq_generator\");\n                genericGenerator.param(\"strategy\", SequenceStyleGenerator.class.getName());\n                final JAnnotationArrayMember parametersAnnotation = genericGenerator.paramArray(\"parameters\");\n                final JAnnotationUse paramAnnotation = parametersAnnotation.annotate(Parameter.class);\n                paramAnnotation.param(\"name\", \"sequence_name\");\n                paramAnnotation.param(\"value\", \"hibernate_sequence\");\n                break;\n            case \"mysql\":\n            case \"sqlserver\":\n                generateValue.param(\"strategy\", GenerationType.IDENTITY);\n                break;\n        }\n        addAccessors(entityClass, idFieldVar);\n    }\n\n    public void addPersistenceVersionFieldAndAccessors(final JDefinedClass entityClass) {\n        final JFieldVar versionField = codeGenerator.addField(entityClass, Field.PERSISTENCE_VERSION,\n                codeGenerator.toJavaClass(FieldType.LONG));\n        codeGenerator.addAnnotation(versionField, Version.class);\n        addAccessors(entityClass, versionField);\n    }\n\n    public JFieldVar addField(final JDefinedClass entityClass, final Field field, String dbVendor) {\n        JFieldVar fieldVar;\n        if (field.isCollection()) {\n            fieldVar = codeGenerator.addListField(entityClass, field);\n        } else {\n            fieldVar = codeGenerator.addField(entityClass, field.getName(), codeGenerator.toJavaClass(field));\n        }\n        annotateField(entityClass, field, fieldVar, dbVendor);\n        return fieldVar;\n    }\n\n    private void annotateField(final JDefinedClass entityClass, final Field field, final JFieldVar fieldVar,\n            String dbVendor) {\n        if (field instanceof SimpleField) {\n            annotateSimpleField((SimpleField) field, fieldVar, dbVendor);\n        } else if (field instanceof RelationField) {\n            annotateRelationField(entityClass, (RelationField) field, fieldVar);\n        }\n    }\n\n    private void annotateRelationField(final JDefinedClass entityClass, final RelationField rfield,\n            final JFieldVar fieldVar) {\n        relationFieldAnnotator.annotateRelationField(entityClass, rfield, fieldVar);\n    }\n\n    private void annotateSimpleField(final SimpleField sfield, final JFieldVar fieldVar, String dbVendor) {\n        if (sfield.isCollection()) {\n            final JAnnotationUse collectionAnnotation = codeGenerator.addAnnotation(fieldVar, ElementCollection.class);\n            collectionAnnotation.param(\"fetch\", FetchType.EAGER);\n            codeGenerator.addAnnotation(fieldVar, OrderColumn.class);\n        }\n        final JAnnotationUse columnAnnotation = codeGenerator.addAnnotation(fieldVar, Column.class);\n        columnAnnotation.param(\"name\", sfield.getName().toUpperCase());\n        columnAnnotation.param(\"nullable\", sfield.isNullable());\n\n        if (sfield.getType() == FieldType.DATE) {\n            final JAnnotationUse temporalAnnotation = codeGenerator.addAnnotation(fieldVar, Temporal.class);\n            temporalAnnotation.param(\"value\", TemporalType.TIMESTAMP);\n        } else if (FieldType.TEXT == sfield.getType()) {\n            if (isPostgreSQLDialect(dbVendor)) {\n                // Use custom type that forces TEXT instead of OID for PostgreSQL\n                final JAnnotationUse typeAnnotation = codeGenerator.addAnnotation(fieldVar,\n                        org.hibernate.annotations.Type.class);\n                // Use fully qualified class name (SPI doesn't apply to EntityManagerFactory)\n                typeAnnotation.param(\"type\", \"org.bonitasoft.engine.persistence.PostgresMaterializedClobType\");\n            } else {\n                // Other databases: standard @Lob annotation\n                codeGenerator.addAnnotation(fieldVar, Lob.class);\n            }\n        } else if (FieldType.STRING == sfield.getType() && sfield.getLength() != null && sfield.getLength() > 0) {\n            columnAnnotation.param(\"length\", sfield.getLength());\n        } else if (FieldType.LOCALDATE == sfield.getType()) {\n            // 10 = to support ISO-8801 date format:\n            columnAnnotation.param(\"length\", 10);\n            final JAnnotationUse converterAnnotation = codeGenerator.addAnnotation(fieldVar, Convert.class);\n            converterAnnotation.param(\"converter\", DateConverter.class);\n        } else if (FieldType.LOCALDATETIME == sfield.getType()) {\n            columnAnnotation.param(\"length\", 30);\n            final JAnnotationUse converterAnnotation = codeGenerator.addAnnotation(fieldVar, Convert.class);\n            converterAnnotation.param(\"converter\", DateAndTimeConverter.class);\n        } else if (FieldType.OFFSETDATETIME == sfield.getType()) {\n            columnAnnotation.param(\"length\", 30);\n            final JAnnotationUse converterAnnotation = codeGenerator.addAnnotation(fieldVar, Convert.class);\n            converterAnnotation.param(\"converter\", OffsetDateTimeConverter.class);\n        }\n    }\n\n    public void addAccessors(final JDefinedClass entityClass, final JFieldVar fieldVar) {\n        addAccessors(entityClass, fieldVar, null);\n    }\n\n    public void addAccessors(final JDefinedClass entityClass, final JFieldVar fieldVar, final Field field) {\n        if (isCollectionField(field)) {\n            codeGenerator.addListSetter(entityClass, fieldVar);\n        } else {\n            codeGenerator.addSetter(entityClass, fieldVar);\n        }\n        final JMethod getter = codeGenerator.addGetter(entityClass, fieldVar);\n        if (field instanceof RelationField && ((RelationField) field).isLazy()) {\n            getter.annotate(LazyLoaded.class);\n        }\n    }\n\n    protected void addModifiers(final JDefinedClass entityClass, final Field field) {\n        if (isCollectionField(field)) {\n            codeGenerator.addAddMethod(entityClass, field);\n            codeGenerator.addRemoveMethod(entityClass, field);\n        }\n    }\n\n    private boolean isCollectionField(final Field field) {\n        if (field == null) {\n            return false;\n        }\n        final Boolean collection = field.isCollection();\n        return collection != null && collection;\n    }\n\n    private String determineDbVendor() {\n        String dbVendor = System.getProperty(\"sysprop.bonita.bdm.db.vendor\");\n        if (dbVendor != null) {\n            return dbVendor;\n        } else {\n            // The situation is not normally possible at runtime.\n            // here to allow testing without too much code change\n            LOGGER.error(\n                    \"sysprop.bonita.bdm.db.vendor is not set. This should not happen at runtime. Defaulting to h2.\");\n            return \"h2\";\n        }\n    }\n\n    private boolean isPostgreSQLDialect(String dbVendor) {\n        return dbVendor != null && dbVendor.toLowerCase().equals(\"postgres\");\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/ForeignKeyAnnotator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator;\n\nimport javax.persistence.ForeignKey;\n\nimport com.sun.codemodel.JAnnotationUse;\nimport com.sun.codemodel.JDefinedClass;\nimport com.sun.codemodel.JFieldVar;\nimport org.bonitasoft.engine.bdm.model.field.RelationField;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class ForeignKeyAnnotator {\n\n    public void annotateForeignKeyName(JAnnotationUse joinColumn, JDefinedClass entityClass, JFieldVar jFieldVar,\n            RelationField relationField) {\n        JAnnotationUse foreignKey = joinColumn.annotationParam(\"foreignKey\", ForeignKey.class);\n\n        StringBuilder uniqueForeignKeyName = new StringBuilder()\n                .append(\"FK_\")\n                .append(getReducedUniqueName(entityClass, jFieldVar, relationField));\n        foreignKey.param(\"name\", uniqueForeignKeyName.toString());\n    }\n\n    protected int getReducedUniqueName(JDefinedClass entityClass, JFieldVar jFieldVar, RelationField relationField) {\n        final StringBuilder builder = new StringBuilder();\n        builder.append(entityClass.name());\n        builder.append(\"_\");\n        builder.append(jFieldVar.name());\n        builder.append(\"_\");\n        builder.append(relationField.getReference().getSimpleName());\n        return Math.abs(builder.toString().hashCode());\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/JExprHelper.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator;\n\nimport com.sun.codemodel.JExpr;\nimport com.sun.codemodel.JFieldRef;\nimport com.sun.codemodel.JFieldVar;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class JExprHelper {\n\n    /**\n     * Build a {@link JFieldRef} prefixed by {@code 'this.'}\n     *\n     * @param fieldVar field to be referenced via {@code 'this.'}\n     * @return a {@link JFieldRef} prefixed by {@code 'this.'}\n     */\n    public static JFieldRef buildFieldRef(JFieldVar fieldVar) {\n        return JExpr._this().ref(fieldVar);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/OffsetDateTimeConverter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator;\n\nimport java.time.OffsetDateTime;\nimport java.time.ZoneOffset;\nimport java.time.format.DateTimeFormatter;\nimport java.time.format.DateTimeParseException;\n\nimport javax.persistence.AttributeConverter;\n\n/**\n * @author Danila Mazour\n */\npublic class OffsetDateTimeConverter implements AttributeConverter<OffsetDateTime, String> {\n\n    @Override\n    public String convertToDatabaseColumn(OffsetDateTime offsetDateTime) {\n        if (offsetDateTime != null) {\n            return offsetDateTime.withOffsetSameInstant(ZoneOffset.UTC).format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);\n        } else {\n            return null;\n        }\n    }\n\n    @Override\n    public OffsetDateTime convertToEntityAttribute(String dbData) {\n        if (dbData != null) {\n            try {\n                return OffsetDateTime.parse(dbData, DateTimeFormatter.ISO_OFFSET_DATE_TIME);\n            } catch (DateTimeParseException e) {\n                throw new RuntimeException(\n                        \"Database OffsetDate&Time format must be ISO-8601 compliant yyyy-MM-dd'T'HH:mm:ss(.SSS)Z \", e);\n            }\n        } else {\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/PersistenceUnitBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport javax.xml.XMLConstants;\nimport javax.xml.parsers.DocumentBuilder;\nimport javax.xml.parsers.DocumentBuilderFactory;\nimport javax.xml.parsers.ParserConfigurationException;\n\nimport org.w3c.dom.Document;\nimport org.w3c.dom.Element;\nimport org.w3c.dom.Node;\nimport org.w3c.dom.NodeList;\nimport org.xml.sax.SAXException;\n\n/**\n * @author Romain Bioteau\n * @author Matthieu Chaffotte\n */\npublic class PersistenceUnitBuilder {\n\n    private final Document document;\n\n    private final Set<String> classes = new HashSet<String>();\n\n    public PersistenceUnitBuilder() throws ParserConfigurationException, SAXException, IOException {\n        document = initializeDefaultPersistenceDocument();\n    }\n\n    protected Document initializeDefaultPersistenceDocument()\n            throws ParserConfigurationException, SAXException, IOException {\n        final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();\n        documentBuilderFactory.setValidating(false);\n        try {\n            documentBuilderFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, \"\"); // security-compliant\n            documentBuilderFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, \"\"); // security-compliant\n        } catch (IllegalArgumentException e) {\n            //ignored, if not supported by the implementation\n        }\n        final DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();\n        final InputStream is = PersistenceUnitBuilder.class.getResourceAsStream(\"persistence.xml\");\n        try {\n            return documentBuilder.parse(is);\n        } finally {\n            is.close();\n        }\n    }\n\n    public Document done() {\n        insertClasses();\n        return document;\n    }\n\n    protected void insertClasses() {\n        final Node persistenceUnitNode = getPersistenceUnitNode();\n        final Node refChild = ((Element) persistenceUnitNode).getElementsByTagName(\"properties\").item(0);\n        for (final String classname : classes) {\n            final Element classNode = document.createElement(\"class\");\n            classNode.setTextContent(classname);\n            persistenceUnitNode.insertBefore(classNode, refChild);\n        }\n\n    }\n\n    private Node getPersistenceUnitNode() {\n        final NodeList parentElement = document.getElementsByTagName(\"persistence-unit\");\n        return parentElement.item(0);\n    }\n\n    public PersistenceUnitBuilder addClass(final String classname) {\n        classes.add(classname);\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/RelationFieldAnnotator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator;\n\nimport static org.apache.commons.lang3.StringUtils.left;\n\nimport javax.persistence.CascadeType;\nimport javax.persistence.FetchType;\nimport javax.persistence.JoinColumn;\nimport javax.persistence.JoinTable;\nimport javax.persistence.ManyToMany;\nimport javax.persistence.ManyToOne;\nimport javax.persistence.OneToMany;\nimport javax.persistence.OneToOne;\nimport javax.persistence.OrderColumn;\n\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport com.sun.codemodel.JAnnotationArrayMember;\nimport com.sun.codemodel.JAnnotationUse;\nimport com.sun.codemodel.JDefinedClass;\nimport com.sun.codemodel.JFieldVar;\nimport org.bonitasoft.engine.bdm.model.field.RelationField;\nimport org.bonitasoft.engine.bdm.model.field.RelationField.Type;\n\n/**\n * @author Colin PUY\n */\npublic class RelationFieldAnnotator {\n\n    private final CodeGenerator codeGenerator;\n\n    private final ForeignKeyAnnotator foreignKeyAnnotator;\n\n    public RelationFieldAnnotator(final CodeGenerator codeGenerator) {\n        this.codeGenerator = codeGenerator;\n        this.foreignKeyAnnotator = new ForeignKeyAnnotator();\n    }\n\n    public void annotateRelationField(final JDefinedClass entityClass, final RelationField field,\n            final JFieldVar fieldVar) {\n        JAnnotationUse relation = null;\n        if (field.isCollection()) {\n            relation = annotateMultipleReference(entityClass, field, fieldVar);\n        } else {\n            relation = annotateSingleReference(entityClass, field, fieldVar);\n        }\n\n        if (field.isLazy()) {\n            relation.param(\"fetch\", FetchType.LAZY);\n            codeGenerator.addAnnotation(fieldVar, JsonIgnore.class);\n        } else {\n            relation.param(\"fetch\", FetchType.EAGER);\n        }\n\n        if (field.getType() == Type.COMPOSITION) {\n            relation.param(\"cascade\", CascadeType.ALL);\n        } else if (field.getType() == Type.AGGREGATION) {\n            relation.param(\"cascade\", CascadeType.MERGE);\n        }\n\n    }\n\n    private JAnnotationUse annotateSingleReference(JDefinedClass entityClass, final RelationField field,\n            final JFieldVar fieldVar) {\n        JAnnotationUse relation;\n        if (field.getType() == Type.AGGREGATION) {\n            relation = codeGenerator.addAnnotation(fieldVar, ManyToOne.class);\n        } else {\n            relation = codeGenerator.addAnnotation(fieldVar, OneToOne.class);\n            relation.param(\"orphanRemoval\", true);\n        }\n        JAnnotationUse joinColumnAnnotation = addJoinColumn(fieldVar, field.getName());\n        relation.param(\"optional\", field.isNullable());\n        foreignKeyAnnotator.annotateForeignKeyName(joinColumnAnnotation, entityClass, fieldVar, field);\n        return relation;\n    }\n\n    private JAnnotationUse annotateMultipleReference(final JDefinedClass entityClass, final RelationField field,\n            final JFieldVar fieldVar) {\n        JAnnotationUse relation;\n        if (field.getType() == Type.AGGREGATION) {\n            relation = codeGenerator.addAnnotation(fieldVar, ManyToMany.class);\n            addJoinTable(entityClass, field, fieldVar);\n\n        } else {\n            relation = codeGenerator.addAnnotation(fieldVar, OneToMany.class);\n            relation.param(\"orphanRemoval\", true);\n            final JAnnotationUse joinColumn = addJoinColumn(fieldVar, entityClass.name());\n            joinColumn.param(\"nullable\", false);\n        }\n        codeGenerator.addAnnotation(fieldVar, OrderColumn.class);\n        return relation;\n    }\n\n    private void addJoinTable(final JDefinedClass entityClass, final RelationField field, final JFieldVar fieldVar) {\n        final JAnnotationUse joinTable = codeGenerator.addAnnotation(fieldVar, JoinTable.class);\n        joinTable.param(\"name\", getJoinTableName(entityClass.name(), field.getName()));\n\n        final JAnnotationArrayMember joinColumns = joinTable.paramArray(\"joinColumns\");\n        final JAnnotationUse nameQueryAnnotation = joinColumns.annotate(JoinColumn.class);\n        nameQueryAnnotation.param(\"name\", getJoinColumnName(entityClass.name()));\n\n        final JAnnotationArrayMember inverseJoinColumns = joinTable.paramArray(\"inverseJoinColumns\");\n        final JAnnotationUse a = inverseJoinColumns.annotate(JoinColumn.class);\n        a.param(\"name\", getJoinColumnName(field.getReference().getSimpleName()));\n    }\n\n    private JAnnotationUse addJoinColumn(final JFieldVar fieldVar, final String columnName) {\n        final JAnnotationUse joinColumn = codeGenerator.addAnnotation(fieldVar, JoinColumn.class);\n        joinColumn.param(\"name\", getJoinColumnName(columnName));\n        return joinColumn;\n    }\n\n    /**\n     * Split names to 26 char to avoid joinColumn names longer than 30 char\n     * protected for testing\n     */\n    protected String getJoinColumnName(final String entityName) {\n        return left(entityName.toUpperCase(), 26) + \"_PID\";\n    }\n\n    /**\n     * Split names to 14 chars max to avoid joinTable names longer than 30 char (oracle restriction).\n     * protected for testing\n     */\n    protected String getJoinTableName(final String entityName, final String relatedEntityName) {\n        final String name = left(entityName.toUpperCase(), 14);\n        final String refName = left(relatedEntityName.toUpperCase(), 14);\n        return name + \"_\" + refName;\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/client/ClientBDMCodeGenerator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator.client;\n\nimport static org.bonitasoft.engine.bdm.validator.rule.QueryParameterValidationRule.FORBIDDEN_PARAMETER_NAMES;\n\nimport java.io.Serializable;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport com.sun.codemodel.JBlock;\nimport com.sun.codemodel.JCatchBlock;\nimport com.sun.codemodel.JClass;\nimport com.sun.codemodel.JClassAlreadyExistsException;\nimport com.sun.codemodel.JDefinedClass;\nimport com.sun.codemodel.JExpr;\nimport com.sun.codemodel.JExpression;\nimport com.sun.codemodel.JFieldRef;\nimport com.sun.codemodel.JInvocation;\nimport com.sun.codemodel.JMethod;\nimport com.sun.codemodel.JMod;\nimport com.sun.codemodel.JTryBlock;\nimport com.sun.codemodel.JVar;\nimport org.bonitasoft.engine.bdm.BDMQueryUtil;\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.Query;\nimport org.bonitasoft.engine.business.data.generator.AbstractBDMCodeGenerator;\n\n/**\n * @author Romain Bioteau\n * @author Emmanuel Duchastenier\n * @author Matthieu Chaffotte\n */\npublic class ClientBDMCodeGenerator extends AbstractBDMCodeGenerator {\n\n    private static final String CLIENT_RESOURCES_PACKAGES = \"org.bonitasoft.engine.bdm.dao.client.resources\";\n\n    public ClientBDMCodeGenerator() {\n        super();\n    }\n\n    @Override\n    protected void addDAO(final BusinessObject bo, final JDefinedClass entity) throws JClassAlreadyExistsException {\n        final JDefinedClass daoInterface = createDAOInterface(bo, entity);\n        createDAOImpl(bo, entity, daoInterface);\n    }\n\n    private void createDAOImpl(final BusinessObject bo, final JDefinedClass entity, final JDefinedClass daoInterface)\n            throws JClassAlreadyExistsException {\n        final String daoImplClassName = toDaoImplClassname(bo);\n        final JDefinedClass implClass = addClass(daoImplClassName);\n        implClass._implements(daoInterface);\n\n        createConstructor(implClass);\n\n        // Add method for provided queries\n        for (final Query q : BDMQueryUtil.createProvidedQueriesForBusinessObject(bo)) {\n            final JMethod method = createMethodForQuery(entity, implClass, q);\n            addQueryMethodBody(entity.name(), method, q.getName(), entity.fullName(), q.getReturnType());\n        }\n\n        // Add method for queries\n        for (final Query q : bo.getQueries()) {\n            final JMethod method = createMethodForQuery(entity, implClass, q);\n            addQueryMethodBody(entity.name(), method, q.getName(), entity.fullName(), q.getReturnType());\n        }\n\n        final JMethod method = createMethodForNewInstance(bo, entity, implClass);\n        addNewInstanceMethodBody(method, entity);\n    }\n\n    private void createConstructor(final JDefinedClass implClass) {\n        final JClass apiSessionJClass = getModel().ref(\"org.bonitasoft.engine.session.APISession\");\n        implClass.field(JMod.PRIVATE, apiSessionJClass, \"session\");\n\n        final JClass deserializerClass = getModel().ref(CLIENT_RESOURCES_PACKAGES + \".BusinessObjectDeserializer\");\n        implClass.field(JMod.PRIVATE, deserializerClass, \"deserializer\");\n\n        final JClass proxyfierClass = getModel().ref(CLIENT_RESOURCES_PACKAGES + \".proxy.Proxyfier\");\n        implClass.field(JMod.PRIVATE, proxyfierClass, \"proxyfier\");\n\n        final JMethod constructor = implClass.constructor(JMod.PUBLIC);\n        constructor.param(apiSessionJClass, \"session\");\n\n        final JBlock body = constructor.body();\n        body.assign(JExpr.refthis(\"session\"), JExpr.ref(\"session\"));\n        body.assign(JExpr.refthis(\"deserializer\"), JExpr._new(deserializerClass));\n        final JClass lazyLoaderClass = getModel().ref(CLIENT_RESOURCES_PACKAGES + \".proxy.LazyLoader\");\n        final JVar lazyLoaderRef = body.decl(lazyLoaderClass, \"lazyLoader\",\n                JExpr._new(lazyLoaderClass).arg(JExpr.ref(\"session\")));\n        body.assign(JExpr.refthis(\"proxyfier\"), JExpr._new(proxyfierClass).arg(lazyLoaderRef));\n    }\n\n    private void addQueryMethodBody(final String entityName, final JMethod method, final String queryName,\n            final String entityClassName,\n            final String queryReturnType) {\n        final JBlock body = method.body();\n\n        final JTryBlock tryBlock = body._try();\n        final JBlock tryBody = tryBlock.body();\n\n        // Get CommandAPI\n        final JClass tenantApiAccessorClass = getModel().ref(\"org.bonitasoft.engine.api.TenantAPIAccessor\");\n        final JClass commandApiType = getModel().ref(\"org.bonitasoft.engine.api.CommandAPI\");\n        final JVar commandApiRef = tryBody.decl(commandApiType, \"commandApi\",\n                tenantApiAccessorClass.staticInvoke(\"getCommandAPI\").arg(JExpr.ref(\"session\")));\n\n        // Create command parameters\n        JClass mapClass = getModel().ref(Map.class);\n        mapClass = mapClass.narrow(String.class, Serializable.class);\n\n        JClass hashMapClass = getModel().ref(HashMap.class);\n        hashMapClass = hashMapClass.narrow(String.class, Serializable.class);\n        final JVar commandParametersRef = tryBody.decl(mapClass, \"commandParameters\", JExpr._new(hashMapClass));\n        tryBody.invoke(commandParametersRef, \"put\").arg(JExpr.lit(\"queryName\"))\n                .arg(JExpr.lit(entityName + \".\" + queryName));\n\n        // Set if should returns a List or a single value\n        boolean isCollection = false;\n        final JClass collectionClass = getModel().ref(Collection.class);\n        if (method.type() instanceof JClass) {\n            isCollection = collectionClass.isAssignableFrom((JClass) method.type());\n        }\n        tryBody.invoke(commandParametersRef, \"put\").arg(JExpr.lit(\"returnsList\")).arg(JExpr.lit(isCollection));\n\n        if (isCollection) {\n            tryBody.invoke(commandParametersRef, \"put\").arg(JExpr.lit(\"returnType\")).arg(JExpr.lit(entityClassName));\n            for (final String param : FORBIDDEN_PARAMETER_NAMES) {\n                tryBody.invoke(commandParametersRef, \"put\").arg(JExpr.lit(param)).arg(JExpr.ref(param));\n            }\n        } else {\n            tryBody.invoke(commandParametersRef, \"put\").arg(JExpr.lit(\"returnType\")).arg(JExpr.lit(queryReturnType));\n        }\n\n        // Add query parameters\n        addQueryParameters(method, tryBody, mapClass, hashMapClass, commandParametersRef);\n\n        // Execute command\n        final JInvocation executeQuery = commandApiRef.invoke(\"execute\").arg(\"executeBDMQuery\")\n                .arg(commandParametersRef);\n        final JClass byteArrayClass = getModel().ref(byte[].class);\n        final JFieldRef deserializerFieldRef = JExpr.ref(\"deserializer\");\n        final JFieldRef proxyfierFieldRef = JExpr.ref(\"proxyfier\");\n        JExpression entityClassExpression = null;\n        if (isCollection) {\n            entityClassExpression = JExpr.dotclass(getModel().ref(entityClassName));\n        } else {\n            entityClassExpression = JExpr.dotclass(getModel().ref(queryReturnType));\n        }\n        JInvocation deserialize = null;\n        if (isCollection) {\n            deserialize = deserializerFieldRef.invoke(\"deserializeList\").arg(JExpr.cast(byteArrayClass, executeQuery))\n                    .arg(entityClassExpression);\n            tryBody._return(proxyfierFieldRef.invoke(\"proxify\").arg(deserialize));\n        } else if (queryReturnType.equals(entityClassName)) {\n            deserialize = deserializerFieldRef.invoke(\"deserialize\").arg(JExpr.cast(byteArrayClass, executeQuery))\n                    .arg(entityClassExpression);\n            tryBody._return(proxyfierFieldRef.invoke(\"proxify\").arg(deserialize));\n        } else {\n            deserialize = deserializerFieldRef.invoke(\"deserialize\").arg(JExpr.cast(byteArrayClass, executeQuery))\n                    .arg(entityClassExpression);\n            tryBody._return(JExpr.cast(getModel().ref(queryReturnType), deserialize));\n        }\n\n        final JClass exceptionClass = getModel().ref(Exception.class);\n        final JCatchBlock catchBlock = tryBlock._catch(exceptionClass);\n        final JVar param = catchBlock.param(\"e\");\n        final JBlock catchBody = catchBlock.body();\n        final JClass iaeClass = getModel().ref(IllegalArgumentException.class);\n        catchBody._throw(JExpr._new(iaeClass).arg(JExpr.ref(null, param)));\n    }\n\n    protected void addQueryParameters(final JMethod method, final JBlock body, final JClass mapClass,\n            final JClass hashMapClass, final JVar commandParametersRef) {\n        if (!method.params().isEmpty()) {\n            final JVar queryParametersRef = body.decl(mapClass, \"queryParameters\", JExpr._new(hashMapClass));\n            for (final JVar param : method.params()) {\n                if (!FORBIDDEN_PARAMETER_NAMES.contains(param.name())) {\n                    body.invoke(queryParametersRef, \"put\").arg(JExpr.lit(param.name())).arg(param);\n                }\n            }\n            body.invoke(commandParametersRef, \"put\").arg(JExpr.lit(\"queryParameters\"))\n                    .arg(JExpr.cast(getModel().ref(Serializable.class), queryParametersRef));\n        }\n    }\n\n    private String toDaoImplClassname(final BusinessObject bo) {\n        return bo.getQualifiedName() + DAO_IMPL_SUFFIX;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/client/ClientBDMJarBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator.client;\n\nimport java.io.File;\nimport java.util.HashSet;\nimport java.util.Set;\nimport javassist.util.proxy.MethodHandler;\n\nimport com.fasterxml.jackson.core.ObjectCodec;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.model.field.Field;\nimport org.bonitasoft.engine.business.data.generator.AbstractBDMJarBuilder;\nimport org.bonitasoft.engine.business.data.generator.CodeGenerationException;\nimport org.bonitasoft.engine.business.data.generator.compiler.JDTCompiler;\n\n/**\n * @author Romain Bioteau\n */\npublic class ClientBDMJarBuilder extends AbstractBDMJarBuilder {\n\n    private ResourcesLoader resourcesLoader;\n\n    @Deprecated\n    public ClientBDMJarBuilder(final JDTCompiler compiler, ResourcesLoader resourcesLoader) {\n        super(new ClientBDMCodeGenerator(), compiler);\n        this.resourcesLoader = resourcesLoader;\n    }\n\n    public ClientBDMJarBuilder(ResourcesLoader resourcesLoader) {\n        super(new ClientBDMCodeGenerator());\n        this.resourcesLoader = resourcesLoader;\n    }\n\n    @Override\n    protected Set<File> getCompileDependencies() throws ClassNotFoundException {\n        var compileDependencies = new HashSet<>(super.getCompileDependencies());\n        // Add dependencies to compile client jar\n        compileDependencies.add(JDTCompiler.lookupJarContaining(\"org.bonitasoft.engine.api.TenantAPIAccessor\"));\n        compileDependencies.add(JDTCompiler.lookupJarContaining(\n                \"org.bonitasoft.engine.bdm.dao.client.resources.BusinessObjectDeserializer\"));\n        compileDependencies.add(JDTCompiler.lookupJarContaining(MethodHandler.class));\n        compileDependencies.add(JDTCompiler.lookupJarContaining(ObjectMapper.class));\n        compileDependencies.add(JDTCompiler.lookupJarContaining(ObjectCodec.class));\n        compileDependencies.add(JDTCompiler.lookupJarContaining(Field.class));\n        return compileDependencies;\n    }\n\n    @Override\n    protected void addSourceFilesToDirectory(BusinessObjectModel bom, File directory) throws CodeGenerationException {\n        super.addSourceFilesToDirectory(bom, directory);\n        addClientResources(directory);\n    }\n\n    private void addClientResources(final File directory) throws CodeGenerationException {\n        try {\n            resourcesLoader.copyJavaFilesToDirectory(\"org.bonitasoft.engine.bdm.dao.client.resources\", directory);\n        } catch (Exception e) {\n            throw new CodeGenerationException(\"Error when adding compilation dependencies to client jar\", e);\n        }\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/client/ResourcesLoader.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator.client;\n\nimport static org.apache.commons.io.FilenameUtils.getName;\nimport static org.bonitasoft.engine.io.IOUtils.createDirectoryIfNotExists;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.commons.io.FileUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.core.io.Resource;\nimport org.springframework.core.io.support.PathMatchingResourcePatternResolver;\n\npublic class ResourcesLoader {\n\n    public void copyJavaFilesToDirectory(String packageName, File directory) throws IOException {\n        List<URL> javaFileURLs = getJavaFileURLs(packageName);\n        for (URL url : javaFileURLs) {\n            addJavaFileToDirectory(url, packageName, directory);\n        }\n    }\n\n    private static List<URL> getJavaFileURLs(String packageName) throws IOException {\n        String pattern = \"/\" + packageName.replace(\".\", \"/\") + \"/**/*.java\";\n        Resource[] resources = new PathMatchingResourcePatternResolver().getResources(pattern);\n        return getURLs(resources);\n    }\n\n    private static List<URL> getURLs(Resource[] resources) throws IOException {\n        List<URL> classNames = new ArrayList<>();\n        for (Resource resource : resources) {\n            classNames.add(resource.getURL());\n        }\n        return classNames;\n    }\n\n    private static void addJavaFileToDirectory(URL javaFile, String originalPackage, File destDirectory)\n            throws IOException {\n        File packageDirectory = createPackageDirectory(javaFile, originalPackage, destDirectory);\n        File destinationFile = new File(packageDirectory, getName(javaFile.toString()));\n        FileUtils.copyURLToFile(javaFile, destinationFile);\n    }\n\n    private static File createPackageDirectory(URL javaFile, String originalPackage, File destDirectory) {\n        File packageDirectory = new File(destDirectory, packageOf(javaFile.toString(), originalPackage));\n        return createDirectoryIfNotExists(packageDirectory);\n    }\n\n    //@VisibleForTesting\n    static String packageOf(final String javaFile, String originalPackage) {\n        String firstChar = StringUtils.substringBefore(originalPackage, \".\");\n        String className = javaFile.substring(javaFile.indexOf(firstChar));\n        if (className.indexOf(\"!\") != -1) {//support osgi classpath url\n            className = className.substring(className.indexOf(\"!\") + 2, className.length());\n        }\n        return className.substring(0, className.lastIndexOf(\"/\"));\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/compiler/CompilationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator.compiler;\n\n/**\n * @author Colin PUY\n */\npublic class CompilationException extends Exception {\n\n    private static final long serialVersionUID = 726068559808420441L;\n\n    public CompilationException(String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/compiler/DummyCompilationProgress.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator.compiler;\n\nimport org.eclipse.jdt.core.compiler.CompilationProgress;\n\n/**\n * CompilationProgress which do nothing\n * Used by JdtCompiler\n *\n * @author Colin PUY\n */\npublic class DummyCompilationProgress extends CompilationProgress {\n\n    @Override\n    public void worked(int workIncrement, int remainingWork) {\n\n    }\n\n    @Override\n    public void setTaskName(String name) {\n\n    }\n\n    @Override\n    public boolean isCanceled() {\n        return false;\n    }\n\n    @Override\n    public void done() {\n\n    }\n\n    @Override\n    public void begin(int remainingWork) {\n\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/compiler/JDTCompiler.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator.compiler;\n\nimport static java.lang.String.join;\nimport static java.util.Collections.emptyList;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.PrintWriter;\nimport java.net.URISyntaxException;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport org.eclipse.jdt.internal.compiler.batch.Main;\n\n/**\n * Compiler based on JDTCompiler\n *\n * @author Colin PUY\n * @author Matthieu Chaffotte\n */\npublic class JDTCompiler {\n\n    private static final String COMPILER_COMPLIANCE_LEVEL = \"-17\";\n    //Used to keep parameters name in bytecode to allow reflection in DAO\n    private static final String PARAMETERS_NAME_ARG = \"-parameters\";\n\n    public static File lookupJarContaining(Class<?> clazz) {\n        try {\n            File jarFile = Path.of(clazz.getProtectionDomain().getCodeSource().getLocation().toURI()).toFile();\n            if (!jarFile.exists()) {\n                throw new IllegalArgumentException(\"Cannot find jar file for class \" + clazz.getName()\n                        + \". Resolved jar file: \" + jarFile.getAbsolutePath() + \" does not exist.\");\n            }\n            return jarFile;\n        } catch (URISyntaxException e) {\n            throw new IllegalArgumentException(\"Cannot find jar file for class \" + clazz.getName(), e);\n        }\n\n    }\n\n    public static File lookupJarContaining(String className) throws ClassNotFoundException {\n        return lookupJarContaining(Class.forName(className));\n    }\n\n    /**\n     * Compile files in output directory using provided classpath\n     * Put null for classpath argument to take current classpath\n     *\n     * @throws CompilationException\n     *         if compilation errors occurs\n     */\n    @Deprecated\n    public void compile(final Collection<File> filesToBeCompiled, final File outputDirectory,\n            File... additionalClasspath)\n            throws CompilationException {\n        compile(filesToBeCompiled, outputDirectory, additionalClasspath);\n    }\n\n    public void compile(final File srcDirectory, File outputDirectory, File... additionalClasspath)\n            throws CompilationException {\n        Map<String, File> sourceFiles = listJavaFilesAndClasses(srcDirectory);\n        launchCompiler(buildCommandLineArguments(sourceFiles.values(), outputDirectory, additionalClasspath));\n    }\n\n    private Map<String, File> listJavaFilesAndClasses(File srcFile) {\n        Map<String, File> result = new HashMap<>();\n        File[] files = srcFile.listFiles();\n        if (files == null) {\n            return result;\n        }\n        for (File file : files) {\n            doListJavaFilesAndClasses(file, result, emptyList());\n        }\n        return result;\n    }\n\n    private void doListJavaFilesAndClasses(File srcFile, Map<String, File> files, List<String> parentPath) {\n        if (!srcFile.exists()) {\n            return;\n        }\n        if (srcFile.isFile() && srcFile.getName().endsWith(\".java\")) {\n            files.put(join(\".\", getPath(parentPath, srcFile)), srcFile);\n        } else {\n            List<String> path = getPath(parentPath, srcFile);\n            File[] children = srcFile.listFiles();\n            if (children == null) {\n                return;\n            }\n            for (File file : children) {\n                doListJavaFilesAndClasses(file, files, path);\n            }\n        }\n    }\n\n    private List<String> getPath(List<String> parentPath, File srcFile) {\n        List<String> path = new ArrayList<>(parentPath);\n        path.add(srcFile.getName().replace(\".java\", \"\"));\n        return path;\n    }\n\n    private String[] buildCommandLineArguments(final Collection<File> files, final File outputDirectory,\n            File... additionalClasspath) {\n        final List<String> arguments = new ArrayList<>();\n        if (additionalClasspath != null) {\n            arguments.add(\"-classpath\");\n            arguments.add(\n                    Arrays.stream(additionalClasspath)\n                            .map(File::getAbsolutePath)\n                            .collect(Collectors.joining(File.pathSeparator)));\n        }\n        arguments.add(COMPILER_COMPLIANCE_LEVEL);\n        arguments.add(PARAMETERS_NAME_ARG);\n        arguments.addAll(outputDirectoryArguments(outputDirectory));\n        arguments.addAll(filesToBeCompiledArguments(files));\n        return arguments.toArray(new String[0]);\n    }\n\n    private List<String> filesToBeCompiledArguments(final Collection<File> files) {\n        final List<String> arguments = new ArrayList<>(files.size());\n        for (final File file : files) {\n            arguments.add(file.getAbsolutePath());\n        }\n        return arguments;\n    }\n\n    private List<String> outputDirectoryArguments(final File outputDirectory) {\n        if (outputDirectory == null) {\n            return Collections.emptyList();\n        }\n        return Arrays.asList(\"-d\", outputDirectory.getAbsolutePath());\n    }\n\n    private void launchCompiler(final String[] commandLine)\n            throws CompilationException {\n        final PrintWriter outWriter = new PrintWriter(new ByteArrayOutputStream());\n        // closing outwriter since we don't want to see compilation out stream\n        outWriter.close();\n\n        final ByteArrayOutputStream errorStream = new ByteArrayOutputStream();\n\n        try (var errorWriter = new PrintWriter(errorStream)) {\n            doCompilation(commandLine, outWriter, errorStream, errorWriter);\n        }\n    }\n\n    private void doCompilation(final String[] commandLine, final PrintWriter outWriter,\n            final ByteArrayOutputStream errorStream, final PrintWriter errorWriter)\n            throws CompilationException {\n        final Main mainCompiler = new Main(outWriter, errorWriter, false /* systemExit */, null /* options */,\n                new DummyCompilationProgress());\n        final boolean succeeded = mainCompiler.compile(commandLine);\n        if (!succeeded) {\n            throw new CompilationException(errorStream.toString());\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/filter/OnlyDAOImplementationFileFilter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator.filter;\n\nimport java.io.File;\n\nimport org.apache.commons.io.filefilter.AbstractFileFilter;\n\n/**\n * @author Romain Bioteau\n */\npublic class OnlyDAOImplementationFileFilter extends AbstractFileFilter {\n\n    public static final String ORG = \"org\";\n    public static final String BONITASOFT = \"bonitasoft\";\n\n    @Override\n    public boolean accept(final File file) {\n        final String name = file.getName();\n        return name.endsWith(\"DAOImpl.class\")\n                || file.getAbsolutePath()\n                        .contains(new StringBuilder().append(ORG).append(File.separator).append(BONITASOFT).toString());\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/filter/WithoutDAOImplementationFileFilter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator.filter;\n\nimport java.io.File;\n\nimport org.apache.commons.io.filefilter.AbstractFileFilter;\n\n/**\n * @author Romain Bioteau\n */\npublic class WithoutDAOImplementationFileFilter extends AbstractFileFilter {\n\n    @Override\n    public boolean accept(final File file) {\n        return acceptFile(file, \".class\") || acceptFile(file, \".java\");\n    }\n\n    private boolean acceptFile(File file, String fileExtension) {\n        return file.getName().endsWith(fileExtension) && !file.getName().endsWith(\"DAOImpl\" + fileExtension);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/server/ServerBDMCodeGenerator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator.server;\n\nimport static org.bonitasoft.engine.bdm.validator.rule.QueryParameterValidationRule.FORBIDDEN_PARAMETER_NAMES;\n\nimport java.io.Serializable;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport com.sun.codemodel.JBlock;\nimport com.sun.codemodel.JCatchBlock;\nimport com.sun.codemodel.JClass;\nimport com.sun.codemodel.JClassAlreadyExistsException;\nimport com.sun.codemodel.JDefinedClass;\nimport com.sun.codemodel.JExpr;\nimport com.sun.codemodel.JFieldVar;\nimport com.sun.codemodel.JMethod;\nimport com.sun.codemodel.JMod;\nimport com.sun.codemodel.JTryBlock;\nimport com.sun.codemodel.JVar;\nimport org.bonitasoft.engine.bdm.BDMQueryUtil;\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.Query;\nimport org.bonitasoft.engine.business.data.generator.AbstractBDMCodeGenerator;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException;\n\n/**\n * @author Romain Bioteau\n * @author Emmanuel Duchastenier\n */\npublic class ServerBDMCodeGenerator extends AbstractBDMCodeGenerator {\n\n    private static final String SERVER_DAO_PACKAGE_NAME = \"server.\";\n\n    public ServerBDMCodeGenerator() {\n        super();\n    }\n\n    @Override\n    protected void addDAO(final BusinessObject bo, final JDefinedClass entity) throws JClassAlreadyExistsException {\n        final JDefinedClass daoInterface = createDAOInterface(bo, entity);\n        createDAOImpl(bo, entity, daoInterface);\n    }\n\n    private void createDAOImpl(final BusinessObject bo, final JDefinedClass entity, final JDefinedClass daoInterface)\n            throws JClassAlreadyExistsException {\n        final String daoImplClassName = toDaoImplClassname(bo);\n        final JDefinedClass implClass = addClass(daoImplClassName);\n        implClass._implements(daoInterface);\n\n        final JFieldVar businessDataRepository = addConstructor(implClass);\n\n        // Add method for provided queries\n        for (final Query q : BDMQueryUtil.createProvidedQueriesForBusinessObject(bo)) {\n            final JMethod method = createMethodForQuery(entity, implClass, q);\n            addQueryMethodBody(entity, method, q.getName(), businessDataRepository);\n        }\n\n        // Add method for queries\n        for (final Query q : bo.getQueries()) {\n            final JMethod method = createMethodForQuery(entity, implClass, q);\n            addQueryMethodBody(entity, method, q.getName(), businessDataRepository);\n        }\n\n        final JMethod method = createMethodForNewInstance(bo, entity, implClass);\n        addNewInstanceMethodBody(method, entity);\n    }\n\n    protected JFieldVar addConstructor(final JDefinedClass implClass) {\n        final JClass serviceClass = getModel().ref(\"org.bonitasoft.engine.business.data.BusinessDataRepository\");\n        final JFieldVar service = addField(implClass, \"businessDataRepository\", serviceClass);\n        final JMethod constructor = implClass.constructor(JMod.PUBLIC);\n        constructor.param(serviceClass, \"businessDataRepository\");\n        final JBlock body = constructor.body();\n        body.assign(JExpr.refthis(\"businessDataRepository\"), JExpr.ref(\"businessDataRepository\"));\n        return service;\n    }\n\n    private void addQueryMethodBody(final JDefinedClass entity, final JMethod method, final String queryName,\n            final JFieldVar businessDataRepository) {\n        final String entityName = entity.name();\n        final JTryBlock tryBlock = method.body()._try();\n        final JBlock tryBody = tryBlock.body();\n\n        JClass queryParameterMapClass = getModel().ref(Map.class);\n        queryParameterMapClass = queryParameterMapClass.narrow(String.class, Serializable.class);\n        JClass hashMapClass = getModel().ref(HashMap.class);\n        hashMapClass = hashMapClass.narrow(String.class, Serializable.class);\n        final JVar queryParameterMap = tryBody.decl(queryParameterMapClass, \"queryParameters\",\n                JExpr._new(hashMapClass));\n\n        for (final JVar param : method.params()) {\n            if (!FORBIDDEN_PARAMETER_NAMES.contains(param.name())) {\n                tryBody.invoke(queryParameterMap, \"put\").arg(JExpr.lit(param.name())).arg(param);\n            }\n        }\n\n        boolean isCollection = false;\n        final JClass collectionClass = getModel().ref(Collection.class);\n        if (method.type() instanceof JClass) {\n            isCollection = collectionClass.isAssignableFrom((JClass) method.type());\n        }\n        if (isCollection) {\n            final JVar[] listParams = method.listParams();\n            final JVar startIndex = getMethodParam(listParams, BDMQueryUtil.START_INDEX_PARAM_NAME);\n            final JVar maxResults = getMethodParam(listParams, BDMQueryUtil.MAX_RESULTS_PARAM_NAME);\n            if (startIndex == null || maxResults == null) {\n                throw new IllegalArgumentException(\"Neither 'startIndex' nor 'maxResults' parameters should be null\");\n            }\n            tryBody._return(businessDataRepository.invoke(\"findListByNamedQuery\")\n                    .arg(JExpr.lit(entityName + \".\" + queryName)).arg(JExpr.dotclass(entity))\n                    .arg(queryParameterMap).arg(startIndex).arg(maxResults));\n        } else {\n            final JClass returnTypeClass = entity.fullName().equals(method.type().fullName()) ? entity\n                    : getModel().ref(method.type().fullName());\n            tryBody._return(businessDataRepository.invoke(\"findByNamedQuery\")\n                    .arg(JExpr.lit(entityName + \".\" + queryName)).arg(JExpr.dotclass(returnTypeClass))\n                    .arg(queryParameterMap));\n        }\n\n        final JClass exceptionClass = getModel().ref(Exception.class);\n        final JCatchBlock catchBlock = tryBlock._catch(exceptionClass);\n        final JVar param = catchBlock.param(\"e\");\n        final JBlock catchBody = catchBlock.body();\n        final JClass iaeClass = getModel().ref(SBonitaRuntimeException.class);\n        catchBody._throw(JExpr._new(iaeClass).arg(JExpr.ref(null, param)));\n    }\n\n    protected JVar getMethodParam(final JVar[] params, final String paramName) {\n        for (final JVar jVar : params) {\n            if (paramName.equals(jVar.name())) {\n                return jVar;\n            }\n        }\n        return null;\n    }\n\n    protected String toDaoImplClassname(final BusinessObject bo) {\n        return serverDAOQualifiedName(bo.getQualifiedName());\n    }\n\n    private String serverDAOQualifiedName(final String boQualifiedName) {\n        int pointIdx = boQualifiedName.lastIndexOf('.');\n        return boQualifiedName.substring(0, pointIdx + 1) + SERVER_DAO_PACKAGE_NAME\n                + boQualifiedName.substring(pointIdx + 1)\n                + DAO_IMPL_SUFFIX;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/server/ServerBDMJarBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator.server;\n\nimport java.io.File;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bdm.BusinessObjectModelConverter;\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.business.data.generator.AbstractBDMJarBuilder;\nimport org.bonitasoft.engine.business.data.generator.CodeGenerationException;\nimport org.bonitasoft.engine.business.data.generator.PersistenceUnitBuilder;\nimport org.bonitasoft.engine.business.data.generator.compiler.JDTCompiler;\nimport org.bonitasoft.engine.io.IOUtils;\nimport org.w3c.dom.Document;\n\n/**\n * @author Matthieu Chaffotte\n * @author Romain Bioteau\n */\npublic class ServerBDMJarBuilder extends AbstractBDMJarBuilder {\n\n    @Deprecated\n    public ServerBDMJarBuilder(final JDTCompiler compiler) {\n        super(new ServerBDMCodeGenerator(), compiler);\n    }\n\n    public ServerBDMJarBuilder(ServerBDMCodeGenerator generator) {\n        super(generator);\n    }\n\n    @Override\n    protected void addSourceFilesToDirectory(BusinessObjectModel bom, File directory) throws CodeGenerationException {\n        super.addSourceFilesToDirectory(bom, directory);\n        addPersistenceFile(directory, bom);\n        addBOMFile(directory, bom);\n    }\n\n    /**\n     * protected for testing - must be changed\n     */\n    protected void addPersistenceFile(final File directory, final BusinessObjectModel bom)\n            throws CodeGenerationException {\n        try {\n            final List<BusinessObject> entities = bom.getBusinessObjects();\n            final PersistenceUnitBuilder builder = new PersistenceUnitBuilder();\n            for (final BusinessObject businessObject : entities) {\n                builder.addClass(businessObject.getQualifiedName());\n            }\n            final Document document = builder.done();\n            final File metaInf = IOUtils.createSubDirectory(directory, \"META-INF\");\n            IOUtils.saveDocument(document, new File(metaInf, \"persistence.xml\"));\n        } catch (Exception e) {\n            throw new CodeGenerationException(\"Error when generating persistence.xml file\", e);\n        }\n    }\n\n    private void addBOMFile(final File directory, final BusinessObjectModel bom) throws CodeGenerationException {\n        Path file = new File(directory, \"bom.xml\").toPath();\n        try {\n            Files.write(file, new BusinessObjectModelConverter().marshall(bom));\n        } catch (Exception e) {\n            throw new CodeGenerationException(\"Error when adding business object model metadata to server jar\", e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/main/resources/org/bonitasoft/engine/business/data/generator/persistence.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<persistence xmlns=\"http://java.sun.com/xml/ns/persistence\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n             xsi:schemaLocation=\"http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd\" version=\"2.0\">\n\n    <persistence-unit name=\"BDR\" transaction-type=\"JTA\">\n        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>\n        <properties />\n    </persistence-unit>\n\n</persistence>\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/AbstractBDMCodeGeneratorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.bdm.builder.BusinessObjectBuilder.aBO;\nimport static org.bonitasoft.engine.bdm.builder.FieldBuilder.aStringField;\nimport static org.bonitasoft.engine.bdm.builder.FieldBuilder.anIntegerField;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport com.sun.codemodel.JCodeModel;\nimport com.sun.codemodel.JDefinedClass;\nimport com.sun.codemodel.JMethod;\nimport com.sun.codemodel.JType;\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.junit.Test;\n\npublic class AbstractBDMCodeGeneratorTest {\n\n    @Test\n    public void should_createMethodForNewInstance_return_jmethod_with_valid_name_and_parameters() throws Exception {\n        final AbstractBDMCodeGenerator abstractBDMCodeGenerator = mock(AbstractBDMCodeGenerator.class);\n        when(abstractBDMCodeGenerator.createMethodForNewInstance(any(BusinessObject.class), any(JDefinedClass.class),\n                any(JDefinedClass.class)))\n                .thenCallRealMethod();\n        when(abstractBDMCodeGenerator.addMethodSignature(any(JDefinedClass.class), anyString(), any(JType.class)))\n                .thenCallRealMethod();\n        when(abstractBDMCodeGenerator.getModel()).thenReturn(new JCodeModel());\n\n        final BusinessObject businessObject = aBO(\"org.bonita.Employee\")\n                .withField(aStringField(\"name\").notNullable().build())\n                .withField(anIntegerField(\"age\").build()).build();\n\n        final CodeGenerator codeGenerator = new CodeGenerator();\n\n        final JMethod jMethod = abstractBDMCodeGenerator.createMethodForNewInstance(businessObject,\n                codeGenerator.addClass(\"org.bonita.Employee\"),\n                codeGenerator.addInterface(\"org.bonita.EmployeeDAO\"));\n        assertThat(jMethod).isNotNull();\n        assertThat(jMethod.name()).isEqualTo(\"newInstance\");\n        assertThat(jMethod.params()).hasSize(1);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/BOMBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bdm.BusinessObjectModelConverter;\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.model.field.FieldType;\nimport org.bonitasoft.engine.bdm.model.field.SimpleField;\n\npublic class BOMBuilder {\n\n    public static BOMBuilder aBOM() {\n        return new BOMBuilder();\n    }\n\n    public BusinessObjectModel build() {\n        final SimpleField firstName = new SimpleField();\n        firstName.setName(\"firstName\");\n        firstName.setType(FieldType.STRING);\n        final SimpleField lastName = new SimpleField();\n        lastName.setName(\"lastName\");\n        lastName.setType(FieldType.STRING);\n        final BusinessObject employee = new BusinessObject();\n        employee.setQualifiedName(\"com.company.Employee\");\n        employee.addField(firstName);\n        employee.addField(lastName);\n        final BusinessObjectModel bom = new BusinessObjectModel();\n        bom.addBusinessObject(employee);\n        return bom;\n    }\n\n    public BusinessObjectModel buildModelWithMultipleBoolean() {\n        final SimpleField booleans = new SimpleField();\n        booleans.setName(\"booleanList\");\n        booleans.setType(FieldType.BOOLEAN);\n        booleans.setCollection(true);\n\n        final BusinessObject booleansBO = new BusinessObject();\n        booleansBO.setQualifiedName(\"com.company.Demo\");\n        booleansBO.addField(booleans);\n\n        final BusinessObjectModel bom = new BusinessObjectModel();\n        bom.addBusinessObject(booleansBO);\n        return bom;\n    }\n\n    public BusinessObjectModel buildModelWithAllSupportedTypes() {\n        final BusinessObject invoice = new BusinessObject();\n        invoice.setQualifiedName(\"com.company.pojo.ComplexInvoice\");\n        final BusinessObjectModel bom = new BusinessObjectModel();\n        bom.addBusinessObject(invoice);\n        return bom;\n    }\n\n    public BusinessObjectModel buildModelWithConstrainedFields() {\n        final BusinessObject constrained = new BusinessObject();\n        constrained.setQualifiedName(\"com.company.pojo.ConstrainedItem\");\n        final BusinessObjectModel bom = new BusinessObjectModel();\n        bom.addBusinessObject(constrained);\n        return bom;\n    }\n\n    public BusinessObjectModel buildComplex() {\n        final SimpleField firstName = new SimpleField();\n        firstName.setName(\"firstName\");\n        firstName.setType(FieldType.STRING);\n        final SimpleField lastName = new SimpleField();\n        lastName.setName(\"lastName\");\n        lastName.setType(FieldType.STRING);\n        final BusinessObject employee = new BusinessObject();\n        employee.setQualifiedName(\"com.company.Employee\");\n        employee.addField(firstName);\n        employee.addField(lastName);\n\n        employee.addQuery(\"getEmployee\", \"SELECT e FROM Employee e\", List.class.getName());\n        final BusinessObjectModel bom = new BusinessObjectModel();\n        bom.addBusinessObject(employee);\n        return bom;\n    }\n\n    public BusinessObjectModel buildPerson() {\n        final SimpleField nickNames = new SimpleField();\n        nickNames.setName(\"nickNames\");\n        nickNames.setType(FieldType.STRING);\n        nickNames.setLength(Integer.valueOf(15));\n        nickNames.setCollection(Boolean.TRUE);\n\n        final BusinessObject employee = new BusinessObject();\n        employee.setQualifiedName(\"com.company.Person\");\n        employee.addField(nickNames);\n\n        final BusinessObjectModel bom = new BusinessObjectModel();\n        bom.addBusinessObject(employee);\n        return bom;\n    }\n\n    public byte[] buildZip() {\n        final BusinessObjectModelConverter converter = new BusinessObjectModelConverter();\n        try {\n            return converter.zip(build());\n        } catch (final Exception e) {\n            throw new RuntimeException(\"Unable to build BOM zip\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/CodeGeneratorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\nimport javax.persistence.Basic;\nimport javax.persistence.Entity;\nimport javax.persistence.Temporal;\nimport javax.persistence.TemporalType;\n\nimport com.sun.codemodel.JAnnotationUse;\nimport com.sun.codemodel.JClass;\nimport com.sun.codemodel.JDefinedClass;\nimport com.sun.codemodel.JFieldVar;\nimport com.sun.codemodel.JMethod;\nimport org.apache.commons.io.FileUtils;\nimport org.bonitasoft.engine.bdm.model.field.FieldType;\nimport org.bonitasoft.engine.bdm.model.field.SimpleField;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Romain Bioteau\n */\npublic class CodeGeneratorTest {\n\n    private CodeGenerator codeGenerator;\n\n    @Before\n    public void setUp() {\n        codeGenerator = new CodeGenerator();\n    }\n\n    @Test\n    public void shouldAddClass_AddAJDefinedClassInModel_AndReturnIt() throws Exception {\n        final JDefinedClass definedClass = codeGenerator.addClass(\"org.bonitasoft.Entity\");\n        assertThat(definedClass).isNotNull().isInstanceOf(JDefinedClass.class);\n        assertThat(definedClass.name()).isEqualTo(\"Entity\");\n        assertThat(definedClass.fullName()).isEqualTo(\"org.bonitasoft.Entity\");\n        assertThat(codeGenerator.getModel()._getClass(\"org.bonitasoft.Entity\")).isNotNull().isSameAs(definedClass);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void shouldAddClass_ThrowAnIllegalArgumentExcpetionForEmptyName() throws Exception {\n        codeGenerator.addClass(\"\");\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void shouldAddClass_ThrowAnIllegalArgumentExcpetionForNullName() throws Exception {\n        codeGenerator.addClass(null);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void shouldAddClass_ThrowAnIllegalArgumentExcpetionForInvalidName() throws Exception {\n        codeGenerator.addClass(\"org.bonitasoft*.Entity\");\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void shouldAddField_ThrowAnIllegalArgumentExcpetionForEmptyName() throws Exception {\n        final JDefinedClass definedClass = codeGenerator.addClass(\"org.bonitasoft.Entity\");\n        codeGenerator.addField(definedClass, \"\", String.class);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void shouldAddField_ThrowAnIllegalArgumentExcpetionForNullName() throws Exception {\n        final JDefinedClass definedClass = codeGenerator.addClass(\"org.bonitasoft.Entity\");\n        codeGenerator.addField(definedClass, null, String.class);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void shouldAddField_ThrowAnIllegalArgumentExcpetionForInvalidName() throws Exception {\n        final JDefinedClass definedClass = codeGenerator.addClass(\"org.bonitasoft.Entity\");\n        codeGenerator.addField(definedClass, \"enum\", String.class);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void shouldAddField_ThrowAnIllegalArgumentExcpetionForNullClassType() throws Exception {\n        final JDefinedClass definedClass = codeGenerator.addClass(\"org.bonitasoft.Entity\");\n        codeGenerator.addField(definedClass, \"name\", (Class<?>) null);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void shouldAddField_ThrowAnIllegalArgumentExcpetionForNullJType() throws Exception {\n        final JDefinedClass definedClass = codeGenerator.addClass(\"org.bonitasoft.Entity\");\n        codeGenerator.addField(definedClass, \"name\", (JClass) null);\n    }\n\n    @Test\n    public void shouldAddPrivateField_FromClass_AddAJVarFieldInDefinedClass_AndReturnIt() throws Exception {\n        final JDefinedClass definedClass = codeGenerator.addClass(\"org.bonitasoft.Entity\");\n        final JFieldVar privateField = codeGenerator.addField(definedClass, \"name\", String.class);\n        assertThat(privateField).isNotNull().isInstanceOf(JFieldVar.class);\n        assertThat(privateField.name()).isEqualTo(\"name\");\n        assertThat(privateField.type().name()).isEqualTo(String.class.getSimpleName());\n        assertThat(codeGenerator.getModel()._getClass(\"org.bonitasoft.Entity\").fields()).containsEntry(\"name\",\n                privateField);\n    }\n\n    @Test\n    public void shouldAddPrivateField_FromJType_AddAJVarFieldInDefinedClass_AndReturnIt() throws Exception {\n        final JDefinedClass employeeClass = codeGenerator.addClass(\"org.bonitasoft.Employee\");\n        final JDefinedClass definedClass = codeGenerator.addClass(\"org.bonitasoft.Entity\");\n        final JFieldVar privateField = codeGenerator.addField(definedClass, \"employee\", employeeClass);\n        assertThat(privateField).isNotNull().isInstanceOf(JFieldVar.class);\n        assertThat(privateField.name()).isEqualTo(\"employee\");\n        assertThat(privateField.type().name()).isEqualTo(\"Employee\");\n        assertThat(codeGenerator.getModel()._getClass(\"org.bonitasoft.Entity\").fields()).containsEntry(\"employee\",\n                privateField);\n\n        final JClass stringList = codeGenerator.getModel().ref(List.class).narrow(String.class);\n        final JFieldVar collectionField = codeGenerator.addField(definedClass, \"skills\", stringList);\n        assertThat(collectionField).isNotNull();\n        assertThat(collectionField.type().name())\n                .isEqualTo(List.class.getSimpleName() + \"<\" + String.class.getSimpleName() + \">\");\n    }\n\n    @Test\n    public void shouldAddAnnotation_AddAJAnnotation_AndReturnIt() throws Exception {\n        final JDefinedClass definedClass = codeGenerator.addClass(\"org.bonitasoft.Entity\");\n        final JAnnotationUse annotation = codeGenerator.addAnnotation(definedClass, Deprecated.class);\n        assertThat(annotation).isNotNull().isInstanceOf(JAnnotationUse.class);\n        assertThat(annotation.getAnnotationClass().fullName()).isEqualTo(Deprecated.class.getName());\n    }\n\n    @Test\n    public void shouldAddSetter_AddAJMethodInDefinedClass_AndReturnIt() throws Exception {\n        final JDefinedClass definedClass = codeGenerator.addClass(\"org.bonitasoft.Entity\");\n        final JFieldVar privateField = codeGenerator.addField(definedClass, \"name\", String.class);\n        final JMethod setter = codeGenerator.addSetter(definedClass, privateField);\n        assertThat(setter).isNotNull().isInstanceOf(JMethod.class);\n        assertThat(setter.name()).isEqualTo(\"setName\");\n        assertThat(codeGenerator.getModel()._getClass(\"org.bonitasoft.Entity\").methods()).contains(setter);\n    }\n\n    @Test\n    public void shouldAddGetter_AddAJMethodInDefinedClass_AndReturnIt() throws Exception {\n        final JDefinedClass definedClass = codeGenerator.addClass(\"org.bonitasoft.Entity\");\n        final JFieldVar privateField = codeGenerator.addField(definedClass, \"name\", String.class);\n        final JMethod setter = codeGenerator.addGetter(definedClass, privateField);\n        assertThat(setter).isNotNull().isInstanceOf(JMethod.class);\n        assertThat(setter.name()).isEqualTo(\"getName\");\n        assertThat(codeGenerator.getModel()._getClass(\"org.bonitasoft.Entity\").methods()).contains(setter);\n    }\n\n    @Test\n    public void shouldAddGetter_AddAJMethodInDefinedClass_AndReturnIt_ForBoolean() throws Exception {\n        final JDefinedClass definedClass = codeGenerator.addClass(\"org.bonitasoft.Entity\");\n        final JFieldVar privateField = codeGenerator.addField(definedClass, \"married\", Boolean.class);\n        final JMethod getter = codeGenerator.addGetter(definedClass, privateField);\n        assertThat(getter).isNotNull().isInstanceOf(JMethod.class);\n        assertThat(getter.name()).isEqualTo(\"isMarried\");\n        assertThat(codeGenerator.getModel()._getClass(\"org.bonitasoft.Entity\").methods()).contains(getter);\n    }\n\n    @Test\n    public void shouldCheckAnnotationTarget_IsValid() throws Exception {\n        final JDefinedClass definedClass = codeGenerator.addClass(\"org.bonitasoft.Entity\");\n        codeGenerator.addAnnotation(definedClass, Deprecated.class);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void shouldCheckAnnotationTarget_ThrowIllegalArgumentExceptionForType() throws Exception {\n        final JDefinedClass definedClass = codeGenerator.addClass(\"org.bonitasoft.Entity\");\n        codeGenerator.addAnnotation(definedClass, Basic.class);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void shouldCheckAnnotationTarget_ThrowIllegalArgumentExceptionForField() throws Exception {\n        final JDefinedClass definedClass = codeGenerator.addClass(\"org.bonitasoft.Entity\");\n        final JFieldVar privateField = codeGenerator.addField(definedClass, \"name\", String.class);\n        codeGenerator.addAnnotation(privateField, Entity.class);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void shouldCheckAnnotationTarget_ThrowIllegalArgumentExceptionForMethod() throws Exception {\n        final JDefinedClass definedClass = codeGenerator.addClass(\"org.bonitasoft.Entity\");\n        final JFieldVar privateField = codeGenerator.addField(definedClass, \"name\", String.class);\n        codeGenerator.addAnnotation(privateField, Entity.class);\n    }\n\n    @Test\n    public void shoulAddInterface_AddInterface_ToADefinedClass() throws Exception {\n        JDefinedClass definedClass = codeGenerator.addClass(\"org.bonitasoft.Entity\");\n        definedClass = codeGenerator.addInterface(definedClass, Serializable.class.getName());\n        List<JClass> elements = new ArrayList<>();\n        definedClass._implements().forEachRemaining(elements::add);\n        assertThat(elements).contains(codeGenerator.getModel().ref(Serializable.class.getName()));\n    }\n\n    @Test\n    public void shouldGenerate_CreatePojoFile() throws Exception {\n        final JDefinedClass definedClass = codeGenerator.addClass(\"org.bonitasoft.Entity\");\n        final JFieldVar nameField = codeGenerator.addField(definedClass, \"name\", String.class);\n\n        final JClass stringList = codeGenerator.getModel().ref(List.class).narrow(String.class);\n        final JClass booleanList = codeGenerator.getModel().ref(List.class).narrow(Boolean.class);\n        final JClass booleanField = codeGenerator.getModel().ref(Boolean.class);\n\n        final JFieldVar booleanMultipleField = codeGenerator.addField(definedClass, \"bools\", booleanList);\n        final JFieldVar booleanSingleField = codeGenerator.addField(definedClass, \"bool\", booleanField);\n\n        final JFieldVar skillField = codeGenerator.addField(definedClass, \"skills\", stringList);\n        final JFieldVar dateField = codeGenerator.addField(definedClass, \"returnDate\",\n                codeGenerator.getModel().ref(Date.class));\n        final JAnnotationUse tAnnotation = codeGenerator.addAnnotation(dateField, Temporal.class);\n        tAnnotation.param(\"value\", TemporalType.TIMESTAMP);\n\n        codeGenerator.addGetter(definedClass, nameField);\n        codeGenerator.addSetter(definedClass, nameField);\n        codeGenerator.addAnnotation(definedClass, Entity.class);\n\n        codeGenerator.addGetter(definedClass, skillField);\n        codeGenerator.addSetter(definedClass, skillField);\n\n        codeGenerator.addGetter(definedClass, booleanMultipleField);\n        codeGenerator.addSetter(definedClass, booleanMultipleField);\n\n        codeGenerator.addGetter(definedClass, booleanSingleField);\n        codeGenerator.addSetter(definedClass, booleanSingleField);\n\n        final File destDir = createTempDirectory(\"generatedPojo\");\n        try {\n            codeGenerator.generate(destDir);\n            final File rootFolder = new File(destDir, \"org\" + File.separatorChar + \"bonitasoft\");\n            assertThat(rootFolder.listFiles()).isNotEmpty().contains(new File(rootFolder, \"Entity.java\"));\n        } finally {\n            FileUtils.deleteQuietly(destDir);\n        }\n    }\n\n    protected File createTempDirectory(final String tmpDirName) throws IOException {\n        final File destDir = File.createTempFile(tmpDirName, null);\n        destDir.delete();\n        destDir.mkdirs();\n        return destDir;\n    }\n\n    @Test\n    public void should_add_and_remove_generate_on_boolean_list() throws Exception {\n        //given\n        final JDefinedClass definedClass = codeGenerator.addClass(\"org.bonitasoft.Demo\");\n        SimpleField booleanField = new SimpleField();\n        booleanField.setName(\"booleanField\");\n        booleanField.setType(FieldType.BOOLEAN);\n        booleanField.setCollection(true);\n        SimpleField stringField = new SimpleField();\n        stringField.setName(\"stringField\");\n        stringField.setType(FieldType.STRING);\n        stringField.setCollection(true);\n\n        //when\n        codeGenerator.addAddMethod(definedClass, booleanField);\n        codeGenerator.addRemoveMethod(definedClass, booleanField);\n        codeGenerator.addAddMethod(definedClass, stringField);\n        codeGenerator.addRemoveMethod(definedClass, stringField);\n\n        //then\n        final File tempDirectory = createTempDirectory(\"generatedPojo\");\n        try {\n            codeGenerator.generate(tempDirectory);\n            final File rootFolder = new File(tempDirectory, \"org\" + File.separatorChar + \"bonitasoft\");\n            assertThat(rootFolder.listFiles()).isNotEmpty().contains(new File(rootFolder, \"Demo.java\"));\n        } finally {\n            FileUtils.deleteQuietly(tempDirectory);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/CompilableCode.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.io.File;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport javax.tools.JavaCompiler;\nimport javax.tools.JavaFileObject;\nimport javax.tools.StandardJavaFileManager;\nimport javax.tools.ToolProvider;\n\nimport org.bonitasoft.engine.bdm.Entity;\n\n/**\n * @author Romain Bioteau\n */\npublic abstract class CompilableCode {\n\n    protected void assertCompilationSuccessful(final File sourceFileToCompile) {\n        final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();\n        final StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);\n        final Iterable<? extends JavaFileObject> compUnits = fileManager.getJavaFileObjects(sourceFileToCompile);\n        final List<String> optionList = new ArrayList<String>();\n\n        String javaxPersistencefilePath = findJarPath(javax.persistence.Basic.class);\n        String bdmEntityfilePath = findJarPath(Entity.class);\n        optionList\n                .addAll(Arrays.asList(\"-classpath\", javaxPersistencefilePath + File.pathSeparator + bdmEntityfilePath));\n        final Boolean compiled = compiler.getTask(null, fileManager, null, optionList, null, compUnits).call();\n        assertThat(compiled).isTrue();\n    }\n\n    private String findJarPath(final Class<?> clazzToFind) {\n        URL jarURL = clazzToFind.getResource(clazzToFind.getSimpleName() + \".class\");\n        String jarPath = jarURL.getFile();\n        if (jarPath.indexOf(\"!\") == -1) {\n            jarPath = getDotClassParentPath(jarPath);\n        } else {\n            jarPath = jarPath.split(\"!\")[0];\n        }\n        return jarPath;\n    }\n\n    private String getDotClassParentPath(final String completeClassUrl) {\n        int indexOf = completeClassUrl.indexOf(Entity.class.getName().replace('.', File.separatorChar));\n        if (indexOf != -1) {\n            return completeClassUrl.substring(0, indexOf);\n        }\n        File f = new File(completeClassUrl);\n        if (f.exists()) {\n            return f.getParent();\n        }\n        return completeClassUrl;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/DateAndTimeConverterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.time.LocalDateTime;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\n\n/**\n * @author Danila Mazour\n */\npublic class DateAndTimeConverterTest {\n\n    @Test\n    public void convertToDatabaseColumn_should_generate_a_string_ISO_compliant() {\n        DateAndTimeConverter dateAndTimeConverter = new DateAndTimeConverter();\n        LocalDateTime localDateTime = LocalDateTime.of(2017, 2, 22, 13, 00, 00);\n        String dateAndTimeString = dateAndTimeConverter.convertToDatabaseColumn(localDateTime);\n        assertThat(dateAndTimeString).isNotNull();\n        assertThat(dateAndTimeString).isEqualTo(\"2017-02-22T13:00:00\");\n    }\n\n    @Test\n    public void convertToEntityAttribute_should_generate_the_correct_attribute() {\n        DateAndTimeConverter dateAndTimeConverter = new DateAndTimeConverter();\n        String dateAndTimeString = \"2011-11-26T15:02:00.654\";\n        LocalDateTime localDateTime = dateAndTimeConverter.convertToEntityAttribute(dateAndTimeString);\n        assertThat(localDateTime).isNotNull();\n        assertThat(localDateTime.getYear()).isEqualTo(2011);\n        assertThat(localDateTime.getMonthValue()).isEqualTo(11);\n        assertThat(localDateTime.getDayOfMonth()).isEqualTo(26);\n        assertThat(localDateTime.getHour()).isEqualTo(15);\n        assertThat(localDateTime.getMinute()).isEqualTo(2);\n        assertThat(localDateTime.getSecond()).isEqualTo(0);\n        assertThat(localDateTime.getNano()).isEqualTo(654000000);\n    }\n\n    @Test\n    public void dateAndTimeConverter_should_generate_the_same_object_in_and_out() {\n        DateAndTimeConverter dateAndTimeConverter = new DateAndTimeConverter();\n        LocalDateTime localDateTime = LocalDateTime.of(1961, 4, 12, 6, 7, 00);\n        LocalDateTime resultLocalDateTime = dateAndTimeConverter\n                .convertToEntityAttribute(dateAndTimeConverter.convertToDatabaseColumn(localDateTime));\n        assertThat(resultLocalDateTime).isNotNull();\n        assertThat(resultLocalDateTime).isEqualTo(localDateTime);\n    }\n\n    @Test\n    public void convertToDatabaseColumn_should_return_null_when_given_a_null_string() {\n        DateAndTimeConverter dateAndTimeConverter = new DateAndTimeConverter();\n        String localDateinString = dateAndTimeConverter.convertToDatabaseColumn(null);\n        assertThat(localDateinString).isNull();\n    }\n\n    @Test\n    public void convertToEntityAttribute_should_return_null_when_given_a_null_object() {\n        DateAndTimeConverter dateAndTimeConverter = new DateAndTimeConverter();\n        LocalDateTime localDate = dateAndTimeConverter.convertToEntityAttribute(null);\n        assertThat(localDate).isNull();\n    }\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void convertToEntityAttribute_should_throw_an_exception_when_given_a_faulty_string() {\n        DateAndTimeConverter dateAndTimeConverter = new DateAndTimeConverter();\n        expectedException.expect(RuntimeException.class);\n        expectedException\n                .expectMessage(\"Database date & time format must be ISO-8601 compliant ( yyyy-mm-ddThh:mm:ss )\");\n        LocalDateTime localDate = dateAndTimeConverter.convertToEntityAttribute(\"LaLaLand\");\n    }\n\n    @Test\n    public void convertToDatabaseColumn_should_account_up_to_nanoseconds_in_the_generated_string() throws Exception {\n        DateAndTimeConverter dateAndTimeConverter = new DateAndTimeConverter();\n        LocalDateTime localDateTime = LocalDateTime.of(2017, 2, 28, 17, 42, 12, 9649);\n        String dateAndTimeString = dateAndTimeConverter.convertToDatabaseColumn(localDateTime);\n        assertThat(dateAndTimeString).isNotNull();\n        assertThat(dateAndTimeString).isEqualTo(\"2017-02-28T17:42:12.000009649\");\n\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/DateConverterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.time.LocalDate;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\n\n/**\n * @author Danila Mazour\n */\npublic class DateConverterTest {\n\n    @Test\n    public void convertToDatabaseColumn_should_generate_a_string_ISO_compliant() {\n        DateConverter dateConverter = new DateConverter();\n        LocalDate localDate = LocalDate.of(2001, 9, 11);\n        String localDateinString = dateConverter.convertToDatabaseColumn(localDate);\n        assertThat(localDateinString).isNotNull();\n        assertThat(localDateinString).isEqualTo(\"2001-09-11\");\n    }\n\n    @Test\n    public void convertToEntityAttribute_should_generate_the_correct_attribute() {\n        DateConverter dateConverter = new DateConverter();\n        String localDateinString = \"1993-01-19\";\n        LocalDate localDate = dateConverter.convertToEntityAttribute(localDateinString);\n        assertThat(localDate).isNotNull();\n        assertThat(localDate.getMonthValue()).isEqualTo(01);\n        assertThat(localDate.getYear()).isEqualTo(1993);\n        assertThat(localDate.getDayOfMonth()).isEqualTo(19);\n\n    }\n\n    @Test\n    public void dateConverter_should_generate_the_same_object_in_and_out() {\n        DateConverter dateConverter = new DateConverter();\n        LocalDate localDate = LocalDate.of(2017, 02, 24);\n        LocalDate localDateResult = dateConverter\n                .convertToEntityAttribute(dateConverter.convertToDatabaseColumn(localDate));\n        assertThat(localDateResult).isNotNull();\n        assertThat(localDateResult).isEqualTo(localDate);\n    }\n\n    @Test\n    public void convertToDatabaseColumn_should_return_null_when_given_a_null_string() {\n        DateConverter dateConverter = new DateConverter();\n        String localDateinString = dateConverter.convertToDatabaseColumn(null);\n        assertThat(localDateinString).isNull();\n    }\n\n    @Test\n    public void convertToEntityAttribute_should_return_null_when_given_a_null_object() {\n        DateConverter dateConverter = new DateConverter();\n        LocalDate localDate = dateConverter.convertToEntityAttribute(null);\n        assertThat(localDate).isNull();\n    }\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void convertToEntityAttribute_should_throw_an_exception_when_given_a_faulty_string() {\n        DateConverter dateConverter = new DateConverter();\n        expectedException.expect(RuntimeException.class);\n        expectedException.expectMessage(\"Database date format must be ISO-8601 compliant ( yyyy-mm-dd )\");\n        LocalDate localDate = dateConverter.convertToEntityAttribute(\"Logan\");\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/EntityCodeGeneratorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.io.StringWriter;\nimport java.io.Writer;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.OffsetDateTime;\nimport java.util.Collection;\nimport java.util.Date;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.persistence.Column;\nimport javax.persistence.Convert;\nimport javax.persistence.Entity;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.Id;\nimport javax.persistence.Lob;\nimport javax.persistence.NamedQueries;\nimport javax.persistence.Table;\nimport javax.persistence.Temporal;\nimport javax.persistence.Version;\n\nimport com.sun.codemodel.JAnnotationUse;\nimport com.sun.codemodel.JAnnotationValue;\nimport com.sun.codemodel.JClass;\nimport com.sun.codemodel.JClassAlreadyExistsException;\nimport com.sun.codemodel.JDefinedClass;\nimport com.sun.codemodel.JFieldVar;\nimport com.sun.codemodel.JFormatter;\nimport com.sun.codemodel.JMethod;\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.model.field.Field;\nimport org.bonitasoft.engine.bdm.model.field.FieldType;\nimport org.bonitasoft.engine.bdm.model.field.RelationField;\nimport org.bonitasoft.engine.bdm.model.field.SimpleField;\nimport org.hibernate.annotations.GenericGenerator;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class EntityCodeGeneratorTest {\n\n    private static final String EMPLOYEE_QUALIFIED_NAME = \"org.bonitasoft.hr.Employee\";\n    private static final String UNIQUE_RELATION_QUALIFIED_NAME = \"org.bonitasoft.hr.Unique\";\n\n    private EntityCodeGenerator entityCodeGenerator;\n\n    private CodeGenerator codeGenerator;\n\n    private BusinessObjectModel bom = new BusinessObjectModel();\n\n    @Before\n    public void setUp() {\n        codeGenerator = new CodeGenerator();\n        entityCodeGenerator = new EntityCodeGenerator(codeGenerator, bom);\n    }\n\n    @Test\n    public void shouldAddEntity_CreateAValidEntityFromBusinessObject() throws Exception {\n        final BusinessObject employeeBO = new BusinessObject();\n        employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME);\n        entityCodeGenerator.addEntity(employeeBO);\n        final JDefinedClass definedClass = codeGenerator.getModel()._getClass(employeeBO.getQualifiedName());\n        assertThat(definedClass).isNotNull();\n        assertThat(definedClass._package().name()).isEqualTo(\"org.bonitasoft.hr\");\n        final Iterator<JClass> it = definedClass._implements();\n        final JClass jClass = it.next();\n        assertThat(jClass.fullName()).isEqualTo(org.bonitasoft.engine.bdm.Entity.class.getName());\n        assertThat(definedClass.annotations()).hasSize(3);\n        final Iterator<JAnnotationUse> iterator = definedClass.annotations().iterator();\n        final JAnnotationUse entityAnnotation = iterator.next();\n        assertThat(entityAnnotation.getAnnotationClass().fullName()).isEqualTo(Entity.class.getName());\n        assertThat(entityAnnotation.getAnnotationMembers()).hasSize(1);\n\n        final JAnnotationUse tableAnnotation = iterator.next();\n        assertThat(tableAnnotation.getAnnotationClass().fullName()).isEqualTo(Table.class.getName());\n        assertThat(tableAnnotation.getAnnotationMembers()).hasSize(1);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void shouldAddEntity_ThrowAnIllegalArgumentException() throws Exception {\n        final BusinessObject employeeBO = new BusinessObject();\n        employeeBO.setQualifiedName(\"java.lang.String\");\n        entityCodeGenerator.addEntity(employeeBO);\n    }\n\n    @Test\n    public void shouldAddNamedQueries_InDefinedClass() throws Exception {\n        final BusinessObject employeeBO = new BusinessObject();\n        employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME);\n        employeeBO.addQuery(\"getEmployees\", \"SELECT e FROM Employee e\", List.class.getName());\n        final JDefinedClass entity = entityCodeGenerator.addEntity(employeeBO);\n\n        final JAnnotationUse namedQueriesAnnotation = getAnnotation(entity, NamedQueries.class.getName());\n        assertThat(namedQueriesAnnotation).isNotNull();\n        final Map<String, JAnnotationValue> annotationMembers = namedQueriesAnnotation.getAnnotationMembers();\n        assertThat(annotationMembers).hasSize(1);\n    }\n\n    @Test\n    public void shouldAddPersistenceIdFieldAndAccessors_AddPersistenceId() throws Exception {\n        final BusinessObject employeeBO = new BusinessObject();\n        employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME);\n        final JDefinedClass definedClass = codeGenerator.addClass(EMPLOYEE_QUALIFIED_NAME);\n        entityCodeGenerator.addPersistenceIdFieldAndAccessors(definedClass, \"postgres\");\n\n        final JFieldVar idFieldVar = definedClass.fields().get(Field.PERSISTENCE_ID);\n        assertThat(idFieldVar).isNotNull();\n        assertThat(idFieldVar.type()).isEqualTo(codeGenerator.getModel().ref(Long.class.getName()));\n        assertThat(idFieldVar.annotations()).hasSize(3);\n        final Iterator<JAnnotationUse> iterator = idFieldVar.annotations().iterator();\n        JAnnotationUse annotationUse = iterator.next();\n        assertThat(annotationUse.getAnnotationClass().fullName()).isEqualTo(Id.class.getName());\n        annotationUse = iterator.next();\n        assertThat(annotationUse.getAnnotationClass().fullName()).isEqualTo(GeneratedValue.class.getName());\n        annotationUse = iterator.next();\n        assertThat(annotationUse.getAnnotationClass().fullName()).isEqualTo(GenericGenerator.class.getName());\n    }\n\n    @Test\n    public void should_add_sequence_strategy_to_fields_with_generatedValue_in_h2() throws Exception {\n        final BusinessObject employeeBO = new BusinessObject();\n        employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME);\n        final JDefinedClass definedClass = codeGenerator.addClass(EMPLOYEE_QUALIFIED_NAME);\n        entityCodeGenerator.addPersistenceIdFieldAndAccessors(definedClass, \"h2\");\n\n        final JFieldVar idFieldVar = definedClass.fields().get(Field.PERSISTENCE_ID);\n        assertThat(idFieldVar.annotations()).hasSize(3);\n        final Iterator<JAnnotationUse> iterator = idFieldVar.annotations().iterator();\n        iterator.next();\n        JAnnotationUse annotationUse = iterator.next();\n        assertThat(getAnnotationParamValue(annotationUse, \"generator\")).isEqualTo(\"default_bonita_seq_generator\");\n    }\n\n    @Test\n    public void should_add_sequence_strategy_to_fields_with_generatedValue_in_postgres() throws Exception {\n        final BusinessObject employeeBO = new BusinessObject();\n        employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME);\n        final JDefinedClass definedClass = codeGenerator.addClass(EMPLOYEE_QUALIFIED_NAME);\n        entityCodeGenerator.addPersistenceIdFieldAndAccessors(definedClass, \"postgres\");\n\n        final JFieldVar idFieldVar = definedClass.fields().get(Field.PERSISTENCE_ID);\n        assertThat(idFieldVar.annotations()).hasSize(3);\n        final Iterator<JAnnotationUse> iterator = idFieldVar.annotations().iterator();\n        iterator.next();\n        JAnnotationUse annotationUse = iterator.next();\n        assertThat(getAnnotationParamValue(annotationUse, \"generator\")).isEqualTo(\"default_bonita_seq_generator\");\n    }\n\n    @Test\n    public void should_add_sequence_strategy_to_fields_with_generatedValue_in_oracle() throws Exception {\n        final BusinessObject employeeBO = new BusinessObject();\n        employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME);\n        final JDefinedClass definedClass = codeGenerator.addClass(EMPLOYEE_QUALIFIED_NAME);\n        entityCodeGenerator.addPersistenceIdFieldAndAccessors(definedClass, \"oracle\");\n\n        final JFieldVar idFieldVar = definedClass.fields().get(Field.PERSISTENCE_ID);\n        assertThat(idFieldVar.annotations()).hasSize(3);\n        final Iterator<JAnnotationUse> iterator = idFieldVar.annotations().iterator();\n        iterator.next();\n        JAnnotationUse annotationUse = iterator.next();\n        assertThat(getAnnotationParamValue(annotationUse, \"generator\")).isEqualTo(\"default_bonita_seq_generator\");\n    }\n\n    @Test\n    public void should_add_identity_strategy_to_fields_with_generatedValue_in_mysql() throws Exception {\n        final BusinessObject employeeBO = new BusinessObject();\n        employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME);\n        final JDefinedClass definedClass = codeGenerator.addClass(EMPLOYEE_QUALIFIED_NAME);\n        entityCodeGenerator.addPersistenceIdFieldAndAccessors(definedClass, \"mysql\");\n\n        final JFieldVar idFieldVar = definedClass.fields().get(Field.PERSISTENCE_ID);\n        assertThat(idFieldVar.annotations()).hasSize(2);\n        final Iterator<JAnnotationUse> iterator = idFieldVar.annotations().iterator();\n        iterator.next();\n        JAnnotationUse annotationUse = iterator.next();\n        assertThat(getAnnotationParamValue(annotationUse, \"strategy\"))\n                .isEqualTo(\"javax.persistence.GenerationType.IDENTITY\");\n    }\n\n    @Test\n    public void should_add_identity_strategy_to_fields_with_generatedValue_in_SQLServer() throws Exception {\n        final BusinessObject employeeBO = new BusinessObject();\n        employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME);\n        final JDefinedClass definedClass = codeGenerator.addClass(EMPLOYEE_QUALIFIED_NAME);\n        entityCodeGenerator.addPersistenceIdFieldAndAccessors(definedClass, \"sqlserver\");\n\n        final JFieldVar idFieldVar = definedClass.fields().get(Field.PERSISTENCE_ID);\n        assertThat(idFieldVar.annotations()).hasSize(2);\n        final Iterator<JAnnotationUse> iterator = idFieldVar.annotations().iterator();\n        iterator.next();\n        JAnnotationUse annotationUse = iterator.next();\n        assertThat(getAnnotationParamValue(annotationUse, \"strategy\"))\n                .isEqualTo(\"javax.persistence.GenerationType.IDENTITY\");\n    }\n\n    @Test\n    public void shouldAddAccessors_AddAccessorMethods_InDefinedClass() throws Exception {\n        final BusinessObject employeeBO = new BusinessObject();\n        employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME);\n        final SimpleField nameField = new SimpleField();\n        nameField.setName(\"name\");\n        nameField.setType(FieldType.STRING);\n        final JDefinedClass definedClass = codeGenerator.addClass(EMPLOYEE_QUALIFIED_NAME);\n        final JFieldVar basicField = entityCodeGenerator.addField(definedClass, nameField, \"h2\");\n\n        entityCodeGenerator.addAccessors(definedClass, basicField);\n\n        assertThat(definedClass.methods()).hasSize(2);\n        final JMethod setter = (JMethod) definedClass.methods().toArray()[0];\n        assertThat(setter.name()).isEqualTo(\"setName\");\n\n        final JMethod getter = (JMethod) definedClass.methods().toArray()[1];\n        assertThat(getter.name()).isEqualTo(\"getName\");\n    }\n\n    @Test\n    public void shouldAddBasicField_AddAFieldWithTemporalAnnotation_InDefinedClass() throws Exception {\n        final BusinessObject employeeBO = new BusinessObject();\n        employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME);\n        final SimpleField nameField = new SimpleField();\n        nameField.setName(\"name\");\n        nameField.setType(FieldType.DATE);\n        nameField.setNullable(Boolean.FALSE);\n        final JDefinedClass definedClass = codeGenerator.addClass(EMPLOYEE_QUALIFIED_NAME);\n        entityCodeGenerator.addField(definedClass, nameField, \"h2\");\n\n        final JFieldVar nameFieldVar = definedClass.fields().get(\"name\");\n        assertThat(nameFieldVar).isNotNull();\n        assertThat(nameFieldVar.type()).isEqualTo(codeGenerator.getModel().ref(Date.class.getName()));\n        assertThat(nameFieldVar.annotations()).hasSize(2);\n        final Iterator<JAnnotationUse> iterator = nameFieldVar.annotations().iterator();\n        JAnnotationUse annotationUse = iterator.next();\n        assertThat(annotationUse.getAnnotationClass().fullName()).isEqualTo(Column.class.getName());\n\n        final String name = getAnnotationParamValue(annotationUse, \"name\");\n        assertThat(name).isNotNull().isEqualTo(\"NAME\");\n        final String nullable = getAnnotationParamValue(annotationUse, \"nullable\");\n        assertThat(nullable).isNotNull().isEqualTo(\"false\");\n\n        annotationUse = iterator.next();\n        assertThat(annotationUse.getAnnotationClass().fullName()).isEqualTo(Temporal.class.getName());\n        assertThat(annotationUse.getAnnotationMembers()).hasSize(1);\n        final String value = getAnnotationParamValue(annotationUse, \"value\");\n        assertThat(value).isNotNull().isEqualTo(\"javax.persistence.TemporalType.TIMESTAMP\");\n    }\n\n    private String getAnnotationParamValue(final JAnnotationUse annotationUse, final String paramName) {\n        final Map<String, JAnnotationValue> annotationParams = annotationUse.getAnnotationMembers();\n        final JAnnotationValue nullableValue = annotationParams.get(paramName);\n        final StringWriter writer = new StringWriter();\n        nullableValue.generate(new JFormatter(writer));\n        return writer.toString().replace(\"\\\"\", \"\");\n    }\n\n    @Test\n    public void shouldAddBooleanAccessors_AddAccessorMethods_InDefinedClass() throws Exception {\n        final BusinessObject employeeBO = new BusinessObject();\n        employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME);\n        final SimpleField foundField = new SimpleField();\n        foundField.setName(\"found\");\n        foundField.setType(FieldType.BOOLEAN);\n        final JDefinedClass definedClass = codeGenerator.addClass(EMPLOYEE_QUALIFIED_NAME);\n        final JFieldVar basicField = entityCodeGenerator.addField(definedClass, foundField, \"h2\");\n\n        entityCodeGenerator.addAccessors(definedClass, basicField);\n\n        assertThat(definedClass.methods()).hasSize(2);\n        final JMethod setter = (JMethod) definedClass.methods().toArray()[0];\n        assertThat(setter.name()).isEqualTo(\"setFound\");\n\n        final JMethod getter = (JMethod) definedClass.methods().toArray()[1];\n        assertThat(getter.name()).isEqualTo(\"isFound\");\n    }\n\n    @Test\n    public void shouldAddColumnField_CreatePrimitiveAttribute_InDefinedClass() throws Exception {\n        final BusinessObject employeeBO = new BusinessObject();\n        employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME);\n        final SimpleField nameField = new SimpleField();\n        nameField.setName(\"name\");\n        nameField.setType(FieldType.STRING);\n        nameField.setLength(Integer.valueOf(45));\n        final JDefinedClass definedClass = codeGenerator.addClass(EMPLOYEE_QUALIFIED_NAME);\n        entityCodeGenerator.addField(definedClass, nameField, \"h2\");\n\n        final JFieldVar nameFieldVar = definedClass.fields().get(\"name\");\n        assertThat(nameFieldVar).isNotNull();\n        assertThat(nameFieldVar.type()).isEqualTo(codeGenerator.getModel().ref(String.class.getName()));\n        assertThat(nameFieldVar.annotations()).hasSize(1);\n        final JAnnotationUse annotationUse = nameFieldVar.annotations().iterator().next();\n        assertThat(annotationUse.getAnnotationClass().fullName()).isEqualTo(Column.class.getName());\n\n        final String name = getAnnotationParamValue(annotationUse, \"name\");\n        assertThat(name).isNotNull().isEqualTo(\"NAME\");\n        final String nullable = getAnnotationParamValue(annotationUse, \"nullable\");\n        assertThat(nullable).isNotNull().isEqualTo(\"true\");\n        final String length = getAnnotationParamValue(annotationUse, \"length\");\n        assertThat(length).isNotNull().isEqualTo(\"45\");\n    }\n\n    @Test\n    public void shouldAddColumnField() throws Exception {\n        final BusinessObject employeeBO = new BusinessObject();\n        employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME);\n        final SimpleField nameField = new SimpleField();\n        nameField.setName(\"description\");\n        nameField.setType(FieldType.TEXT);\n        final JDefinedClass definedClass = codeGenerator.addClass(EMPLOYEE_QUALIFIED_NAME);\n\n        entityCodeGenerator.addField(definedClass, nameField, \"h2\");\n\n        final JFieldVar nameFieldVar = definedClass.fields().get(\"description\");\n        assertTextField(nameFieldVar);\n    }\n\n    private void assertTextField(final JFieldVar fieldVar) {\n        final Collection<JAnnotationUse> annotations = fieldVar.annotations();\n        assertThat(annotations).hasSize(2);\n        final Iterator<JAnnotationUse> iterator = annotations.iterator();\n        JAnnotationUse annotationUse = iterator.next();\n        assertThat(annotationUse.getAnnotationClass().fullName()).isEqualTo(Column.class.getName());\n        annotationUse = iterator.next();\n        assertThat(annotationUse.getAnnotationClass().fullName()).isEqualTo(Lob.class.getName());\n    }\n\n    @Test\n    public void shouldAddColumnField_WithCustomTypeForPostgreSQL() throws Exception {\n        final BusinessObject employeeBO = new BusinessObject();\n        employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME);\n        final SimpleField descriptionField = new SimpleField();\n        descriptionField.setName(\"description\");\n        descriptionField.setType(FieldType.TEXT);\n        final JDefinedClass definedClass = codeGenerator.addClass(EMPLOYEE_QUALIFIED_NAME);\n\n        entityCodeGenerator.addField(definedClass, descriptionField, \"postgres\");\n\n        final JFieldVar descriptionFieldVar = definedClass.fields().get(\"description\");\n        assertTextFieldForPostgreSQL(descriptionFieldVar);\n    }\n\n    @Test\n    public void shouldAddColumnField_WithLobForMySQL() throws Exception {\n        final BusinessObject employeeBO = new BusinessObject();\n        employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME);\n        final SimpleField descriptionField = new SimpleField();\n        descriptionField.setName(\"description\");\n        descriptionField.setType(FieldType.TEXT);\n        final JDefinedClass definedClass = codeGenerator.addClass(EMPLOYEE_QUALIFIED_NAME);\n\n        entityCodeGenerator.addField(definedClass, descriptionField, \"mysql\");\n\n        final JFieldVar descriptionFieldVar = definedClass.fields().get(\"description\");\n        // For non-PostgreSQL databases, should still use @Lob\n        assertTextField(descriptionFieldVar);\n    }\n\n    private void assertTextFieldForPostgreSQL(final JFieldVar fieldVar) {\n        final Collection<JAnnotationUse> annotations = fieldVar.annotations();\n        assertThat(annotations).hasSize(2);\n        final Iterator<JAnnotationUse> iterator = annotations.iterator();\n        JAnnotationUse annotationUse = iterator.next();\n        assertThat(annotationUse.getAnnotationClass().fullName()).isEqualTo(Column.class.getName());\n        annotationUse = iterator.next();\n        // For PostgreSQL, should use @Type annotation instead of @Lob\n        assertThat(annotationUse.getAnnotationClass().fullName())\n                .isEqualTo(org.hibernate.annotations.Type.class.getName());\n        // Verify the type parameter value\n        assertThat(annotationUse.getAnnotationMembers()).hasSize(1);\n        final String typeValue = getAnnotationParamValue(annotationUse, \"type\");\n        assertThat(typeValue).isEqualTo(\"org.bonitasoft.engine.persistence.PostgresMaterializedClobType\");\n    }\n\n    @Test\n    public void shouldAddPersistenceVersionFieldAndAccessors_AddPersistenceVersion() throws Exception {\n        final BusinessObject employeeBO = new BusinessObject();\n        employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME);\n        final JDefinedClass definedClass = codeGenerator.addClass(EMPLOYEE_QUALIFIED_NAME);\n        entityCodeGenerator.addPersistenceVersionFieldAndAccessors(definedClass);\n\n        final JFieldVar versionFieldVar = definedClass.fields().get(Field.PERSISTENCE_VERSION);\n        assertThat(versionFieldVar).isNotNull();\n        assertThat(versionFieldVar.type()).isEqualTo(codeGenerator.getModel().ref(Long.class.getName()));\n        assertThat(versionFieldVar.annotations()).hasSize(1);\n        final Iterator<JAnnotationUse> iterator = versionFieldVar.annotations().iterator();\n        final JAnnotationUse annotationUse = iterator.next();\n        assertThat(annotationUse.getAnnotationClass().fullName()).isEqualTo(Version.class.getName());\n    }\n\n    public JAnnotationUse getAnnotation(final JDefinedClass definedClass, final String annotationClassName) {\n        final Iterator<JAnnotationUse> iterator = definedClass.annotations().iterator();\n        JAnnotationUse annotation = null;\n        while (annotation == null && iterator.hasNext()) {\n            final JAnnotationUse next = iterator.next();\n            if (next.getAnnotationClass().fullName().equals(annotationClassName)) {\n                annotation = next;\n            }\n        }\n        return annotation;\n    }\n\n    @Test\n    public void should_add_real_columnName_in_unique_key() throws Exception {\n        //given\n        final BusinessObject employeeBO = new BusinessObject();\n        employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME);\n\n        final BusinessObject uniqueRelationBO = new BusinessObject();\n        uniqueRelationBO.setQualifiedName(UNIQUE_RELATION_QUALIFIED_NAME);\n\n        final RelationField relationField = new RelationField();\n        relationField.setName(\"uniqueRelation\");\n        relationField.setReference(uniqueRelationBO);\n        employeeBO.addField(relationField);\n        employeeBO.addUniqueConstraint(\"unique_constraint\", \"uniqueRelation\");\n\n        //when\n        final JDefinedClass jDefinedClass = entityCodeGenerator.addEntity(employeeBO);\n\n        //then\n        final Writer writer = new StringWriter();\n        final JAnnotationUse tableAnnotation = getAnnotation(jDefinedClass, Table.class.getCanonicalName());\n        assertThat(tableAnnotation).as(\"should have Table Annotation\").isNotNull();\n        tableAnnotation.generate(new JFormatter(writer));\n        final String EOL = System.getProperty(\"line.separator\");\n        assertThat(writer.toString()).as(\"should rename relation field to real column name\")\n                .isEqualTo(\"@javax.persistence.Table(name = \\\"EMPLOYEE\\\", uniqueConstraints = {\" + EOL +\n                        \"    @javax.persistence.UniqueConstraint(name = \\\"UNIQUE_CONSTRAINT\\\", columnNames = {\" + EOL +\n                        \"        \\\"UNIQUERELATION_PID\\\"\" + EOL +\n                        \"    })\" + EOL +\n                        \"})\");\n    }\n\n    @Test\n    public void annotateSimpleField_should_generate_the_correct_annotation_for_localDate_type()\n            throws JClassAlreadyExistsException {\n\n        final BusinessObject employeeBO = new BusinessObject();\n        employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME);\n        SimpleField nameField = new SimpleField();\n        nameField.setName(\"name\");\n        nameField.setType(FieldType.LOCALDATE);\n        nameField.setNullable(Boolean.FALSE);\n        final JDefinedClass definedClass = codeGenerator.addClass(EMPLOYEE_QUALIFIED_NAME);\n        entityCodeGenerator.addField(definedClass, nameField, \"h2\");\n        final JFieldVar nameFieldVar = definedClass.fields().get(\"name\");\n\n        assertThat(nameFieldVar).isNotNull();\n        assertThat(nameFieldVar.type()).isEqualTo(codeGenerator.getModel().ref(LocalDate.class.getName()));\n        assertThat(nameFieldVar.annotations().size()).isEqualTo(2);\n        final Iterator<JAnnotationUse> iterator = nameFieldVar.annotations().iterator();\n        JAnnotationUse annotationUse = iterator.next();\n        assertThat(annotationUse.getAnnotationClass().fullName()).isEqualTo(Column.class.getName());\n        final String name = getAnnotationParamValue(annotationUse, \"name\");\n        assertThat(name).isNotNull().isEqualTo(\"NAME\");\n        final String nullable = getAnnotationParamValue(annotationUse, \"nullable\");\n        assertThat(nullable).isNotNull().isEqualTo(\"false\");\n        final int length = Integer.parseInt(getAnnotationParamValue(annotationUse, \"length\"));\n        assertThat(length).isEqualTo(10);\n        annotationUse = iterator.next();\n        assertThat(annotationUse.getAnnotationClass().fullName()).isEqualTo(Convert.class.getName());\n        assertThat(annotationUse.getAnnotationMembers()).hasSize(1);\n        final String value = getAnnotationParamValue(annotationUse, \"converter\");\n        assertThat(value).isNotNull().isEqualTo(\"org.bonitasoft.engine.business.data.generator.DateConverter.class\");\n\n    }\n\n    @Test\n    public void annotateSimpleField_should_generate_the_correct_annotation_for_localDateAndTime_type()\n            throws JClassAlreadyExistsException {\n\n        final BusinessObject employeeBO = new BusinessObject();\n        employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME);\n        SimpleField nameField = new SimpleField();\n        nameField.setName(\"name\");\n        nameField.setType(FieldType.LOCALDATETIME);\n        nameField.setNullable(Boolean.FALSE);\n        final JDefinedClass definedClass = codeGenerator.addClass(EMPLOYEE_QUALIFIED_NAME);\n        entityCodeGenerator.addField(definedClass, nameField, \"h2\");\n        final JFieldVar nameFieldVar = definedClass.fields().get(\"name\");\n\n        assertThat(nameFieldVar).isNotNull();\n        assertThat(nameFieldVar.type()).isEqualTo(codeGenerator.getModel().ref(LocalDateTime.class.getName()));\n        assertThat(nameFieldVar.annotations().size()).isEqualTo(2);\n        final Iterator<JAnnotationUse> iterator = nameFieldVar.annotations().iterator();\n        JAnnotationUse annotationUse = iterator.next();\n        assertThat(annotationUse.getAnnotationClass().fullName()).isEqualTo(Column.class.getName());\n\n        final String name = getAnnotationParamValue(annotationUse, \"name\");\n        assertThat(name).isNotNull().isEqualTo(\"NAME\");\n        final String nullable = getAnnotationParamValue(annotationUse, \"nullable\");\n        assertThat(nullable).isNotNull().isEqualTo(\"false\");\n        final int length = Integer.parseInt(getAnnotationParamValue(annotationUse, \"length\"));\n        assertThat(length).isEqualTo(30);\n        annotationUse = iterator.next();\n        assertThat(annotationUse.getAnnotationClass().fullName()).isEqualTo(Convert.class.getName());\n        assertThat(annotationUse.getAnnotationMembers()).hasSize(1);\n        final String value = getAnnotationParamValue(annotationUse, \"converter\");\n        assertThat(value).isNotNull()\n                .isEqualTo(\"org.bonitasoft.engine.business.data.generator.DateAndTimeConverter.class\");\n    }\n\n    @Test\n    public void annotateSimpleField_should_generate_the_correct_annotation_for_OffsetDateAndTime_type()\n            throws JClassAlreadyExistsException {\n\n        final BusinessObject employeeBO = new BusinessObject();\n        employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME);\n        SimpleField nameField = new SimpleField();\n        nameField.setName(\"reunion\");\n        nameField.setType(FieldType.OFFSETDATETIME);\n        nameField.setNullable(Boolean.FALSE);\n        final JDefinedClass definedClass = codeGenerator.addClass(EMPLOYEE_QUALIFIED_NAME);\n        entityCodeGenerator.addField(definedClass, nameField, \"h2\");\n        final JFieldVar nameFieldVar = definedClass.fields().get(\"reunion\");\n\n        assertThat(nameFieldVar).isNotNull();\n        assertThat(nameFieldVar.type()).isEqualTo(codeGenerator.getModel().ref(OffsetDateTime.class.getName()));\n        assertThat(nameFieldVar.annotations().size()).isEqualTo(2);\n        final Iterator<JAnnotationUse> iterator = nameFieldVar.annotations().iterator();\n        JAnnotationUse annotationUse = iterator.next();\n        assertThat(annotationUse.getAnnotationClass().fullName()).isEqualTo(Column.class.getName());\n\n        final String name = getAnnotationParamValue(annotationUse, \"name\");\n        assertThat(name).isNotNull().isEqualTo(\"REUNION\");\n        final String nullable = getAnnotationParamValue(annotationUse, \"nullable\");\n        assertThat(nullable).isNotNull().isEqualTo(\"false\");\n        final int length = Integer.parseInt(getAnnotationParamValue(annotationUse, \"length\"));\n        assertThat(length).isEqualTo(30);\n        annotationUse = iterator.next();\n        assertThat(annotationUse.getAnnotationClass().fullName()).isEqualTo(Convert.class.getName());\n        assertThat(annotationUse.getAnnotationMembers()).hasSize(1);\n        final String value = getAnnotationParamValue(annotationUse, \"converter\");\n        assertThat(value).isNotNull()\n                .isEqualTo(\"org.bonitasoft.engine.business.data.generator.OffsetDateTimeConverter.class\");\n\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/EntityPojo.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\nimport javax.persistence.CascadeType;\nimport javax.persistence.NamedQueries;\nimport javax.persistence.NamedQuery;\nimport javax.persistence.OneToMany;\nimport javax.persistence.OneToOne;\n\nimport org.bonitasoft.engine.bdm.Entity;\n\n@NamedQueries({\n        @NamedQuery(name = \"EntityPojo.findByFirstName\", query = \"SELECT e\\nFROM Employee e\\nWHERE e.firstName= :firstName\\nORDER BY e.persistenceId\"),\n        @NamedQuery(name = \"EntityPojo.find\", query = \"SELECT e\\nFROM Employee e\\nORDER BY e.persistenceId\"),\n        @NamedQuery(name = \"EntityPojo.countForFind\", query = \"SELECT count(e)\\nFROM Employee e\\n\")\n})\npublic class EntityPojo implements Entity {\n\n    private static final long serialVersionUID = 1L;\n    private String name;\n    private Boolean bool;\n    private Date date;\n    private List<Long> numbers;\n\n    @OneToOne(cascade = CascadeType.MERGE)\n    private Entity aggregationEntity;\n\n    @OneToOne(cascade = CascadeType.ALL)\n    private Entity compositionEntity;\n\n    @OneToOne(cascade = CascadeType.ALL)\n    private Entity nullChildEntity;\n\n    @OneToMany(cascade = CascadeType.MERGE)\n    private List<Entity> aggregationEntities;\n\n    @OneToMany(cascade = CascadeType.ALL)\n    private List<Entity> compositionEntities;\n\n    private Long persistenceId;\n\n    public EntityPojo(final Long persistenceId) {\n        this.persistenceId = persistenceId;\n        aggregationEntities = new ArrayList<>();\n        compositionEntities = new ArrayList<>();\n\n    }\n\n    public EntityPojo() {\n        aggregationEntities = new ArrayList<>();\n        compositionEntities = new ArrayList<>();\n    }\n\n    @Override\n    public Long getPersistenceId() {\n        return persistenceId;\n    }\n\n    @Override\n    public Long getPersistenceVersion() {\n        return 2L;\n    }\n\n    public Date getDate() {\n        return date;\n    }\n\n    public void setDate(final Date date) {\n        this.date = date;\n    }\n\n    public Boolean getBool() {\n        return bool;\n    }\n\n    public void setBool(final Boolean bool) {\n        this.bool = bool;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(final String name) {\n        this.name = name;\n    }\n\n    public List<Long> getNumbers() {\n        return numbers;\n    }\n\n    public void setNumbers(final List<Long> numbers) {\n        this.numbers = numbers;\n    }\n\n    public Entity getCompositionEntity() {\n        return compositionEntity;\n    }\n\n    public void setCompositionEntity(final Entity compositionEntity) {\n        this.compositionEntity = compositionEntity;\n    }\n\n    public Entity getAggregationEntity() {\n        return aggregationEntity;\n    }\n\n    public void setAggregationEntity(final Entity aggregationEntity) {\n        this.aggregationEntity = aggregationEntity;\n    }\n\n    public Entity getNullChildEntity() {\n        return null;\n    }\n\n    public void setNullChildEntity(final Entity nullChildEntity) {\n        this.nullChildEntity = nullChildEntity;\n    }\n\n    public List<Entity> getAggregationEntities() {\n        return aggregationEntities;\n    }\n\n    public void setAggregationEntities(List<Entity> aggregationEntities) {\n        this.aggregationEntities = aggregationEntities;\n    }\n\n    public List<Entity> getCompositionEntities() {\n        return compositionEntities;\n    }\n\n    public void setCompositionEntities(List<Entity> compositionEntities) {\n        this.compositionEntities = compositionEntities;\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/ForeignKeyAnnotatorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.doReturn;\n\nimport java.io.StringWriter;\nimport java.io.Writer;\nimport java.util.Collection;\nimport java.util.Map;\n\nimport javax.persistence.JoinColumn;\n\nimport com.sun.codemodel.JAnnotationUse;\nimport com.sun.codemodel.JAnnotationValue;\nimport com.sun.codemodel.JDefinedClass;\nimport com.sun.codemodel.JFieldVar;\nimport com.sun.codemodel.JFormatter;\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.field.RelationField;\nimport org.bonitasoft.engine.business.data.generator.client.ClientBDMCodeGenerator;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Laurent Leseigneur\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class ForeignKeyAnnotatorTest {\n\n    public static final String EXPECTED_UNIQUE_NAME = \"EntityPojo_fieldName_businessObjectSimpleName\";\n\n    private CodeGenerator codeGenerator;\n\n    private JDefinedClass jDefinedClass;\n\n    private JFieldVar jFieldVar;\n\n    @Mock\n    private RelationField relationField;\n\n    @Mock\n    private BusinessObject businessObject;\n\n    private ForeignKeyAnnotator foreignKeyAnnotator;\n\n    @Before\n    public void before() throws Exception {\n        codeGenerator = new ClientBDMCodeGenerator().disableRuntimeClassesValidation();\n        foreignKeyAnnotator = new ForeignKeyAnnotator();\n        jDefinedClass = codeGenerator.addClass(EntityPojo.class.getName());\n        jFieldVar = codeGenerator.addField(jDefinedClass, \"fieldName\", EntityPojo.class);\n        doReturn(businessObject).when(relationField).getReference();\n        doReturn(\"businessObjectSimpleName\").when(businessObject).getSimpleName();\n    }\n\n    @Test\n    public void should_annotate_with_foreign_key_name() throws Exception {\n        // when\n        var joinColumn = codeGenerator.addAnnotation(jFieldVar, JoinColumn.class);\n        foreignKeyAnnotator.annotateForeignKeyName(joinColumn, jDefinedClass, jFieldVar, relationField);\n\n        // then\n        final Collection<JAnnotationUse> annotations = jFieldVar.annotations();\n        assertThat(annotations).hasSize(1);\n        final JAnnotationUse jAnnotationUse = annotations.iterator().next();\n        final Map<String, JAnnotationValue> annotationMembers = jAnnotationUse.getAnnotationMembers();\n        assertThat(annotationMembers).containsKey(\"foreignKey\");\n        assertThat(jAnnotationUse.getAnnotationClass().fullName()).isEqualTo(JoinColumn.class.getCanonicalName());\n\n        final JAnnotationUse foreignKeyAnnotation = (JAnnotationUse) annotationMembers.get(\"foreignKey\");\n        JAnnotationValue jAnnotationValue = foreignKeyAnnotation.getAnnotationMembers().get(\"name\");\n\n        Writer stringWriter = new StringWriter();\n        JFormatter jFormatter = new JFormatter(stringWriter);\n        jAnnotationValue.generate(jFormatter);\n        final String foreignKeyName = stringWriter.toString();\n        assertThat(foreignKeyName).isEqualTo(\n                \"\\\"FK_\" + foreignKeyAnnotator.getReducedUniqueName(jDefinedClass, jFieldVar, relationField) + \"\\\"\");\n\n        assertThat(foreignKeyName.length()).as(\"should match db vendor name limitation\").isLessThanOrEqualTo(30);\n    }\n\n    @Test\n    public void should_generate_reduced_name() throws Exception {\n        // when\n        final int reducedUniqueName = foreignKeyAnnotator.getReducedUniqueName(jDefinedClass, jFieldVar, relationField);\n\n        // then\n        assertThat(reducedUniqueName).as(\"should be a positive value\").isGreaterThan(0);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/OffsetDateTimeConverterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.time.LocalDateTime;\nimport java.time.OffsetDateTime;\nimport java.time.ZoneOffset;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\n\n/**\n * @author Danila Mazour\n */\npublic class OffsetDateTimeConverterTest {\n\n    @Test\n    public void convertToDatabaseColumn_should_generate_a_string_ISO_compliant() {\n        OffsetDateTimeConverter offsetDateAndTimeConverter = new OffsetDateTimeConverter();\n        OffsetDateTime offsetDateTime = OffsetDateTime.of(LocalDateTime.of(2017, 2, 22, 13, 00, 00),\n                ZoneOffset.ofHours(-5));\n        String offsetDateAndTimeString = offsetDateAndTimeConverter.convertToDatabaseColumn(offsetDateTime);\n        assertThat(offsetDateAndTimeString).isNotNull();\n        assertThat(offsetDateAndTimeString).isEqualTo(\"2017-02-22T18:00:00Z\");\n    }\n\n    @Test\n    public void convertToEntityAttribute_should_generate_the_correct_attribute() {\n        OffsetDateTimeConverter offsetDateAndTimeConverter = new OffsetDateTimeConverter();\n        String offsetDateAndTimeString = \"2011-11-26T15:02:00.654Z\";\n        OffsetDateTime offsetDateTime = offsetDateAndTimeConverter.convertToEntityAttribute(offsetDateAndTimeString);\n        assertThat(offsetDateTime).isNotNull();\n        assertThat(offsetDateTime.getYear()).isEqualTo(2011);\n        assertThat(offsetDateTime.getMonthValue()).isEqualTo(11);\n        assertThat(offsetDateTime.getDayOfMonth()).isEqualTo(26);\n        assertThat(offsetDateTime.getHour()).isEqualTo(15);\n        assertThat(offsetDateTime.getMinute()).isEqualTo(2);\n        assertThat(offsetDateTime.getSecond()).isEqualTo(0);\n        assertThat(offsetDateTime.getNano()).isEqualTo(654000000);\n    }\n\n    @Test\n    public void offsetDateTimeConverter_should_generate_the_same_object_in_and_out() {\n        OffsetDateTimeConverter offsetDateAndTimeConverter = new OffsetDateTimeConverter();\n        OffsetDateTime offsetDateTime = OffsetDateTime.of(LocalDateTime.of(1961, 4, 12, 6, 7, 00),\n                ZoneOffset.ofHours(5));\n        OffsetDateTime resultOffsetDateTime = offsetDateAndTimeConverter\n                .convertToEntityAttribute(offsetDateAndTimeConverter.convertToDatabaseColumn(offsetDateTime))\n                .withOffsetSameInstant(ZoneOffset.ofHours(5));\n        assertThat(resultOffsetDateTime).isNotNull();\n        assertThat(resultOffsetDateTime).isEqualTo(offsetDateTime);\n    }\n\n    @Test\n    public void offsetDateTimeConverter_should_generate_the_same_time_for_OffsetDateTimes_in_and_out() {\n        OffsetDateTimeConverter offsetDateAndTimeConverter = new OffsetDateTimeConverter();\n        OffsetDateTime offsetDateTime = OffsetDateTime.of(LocalDateTime.of(1961, 4, 12, 6, 7, 00),\n                ZoneOffset.ofHours(5));\n        OffsetDateTime resultOffsetDateTime = offsetDateAndTimeConverter\n                .convertToEntityAttribute(offsetDateAndTimeConverter.convertToDatabaseColumn(offsetDateTime));\n        assertThat(offsetDateTime.isEqual(resultOffsetDateTime)).isTrue();\n\n    }\n\n    @Test\n    public void convertToDatabaseColumn_should_return_null_when_given_a_null_string() {\n        OffsetDateTimeConverter offsetDateAndTimeConverter = new OffsetDateTimeConverter();\n        String nullOffsetDateTime = offsetDateAndTimeConverter.convertToDatabaseColumn(null);\n        assertThat(nullOffsetDateTime).isNull();\n    }\n\n    @Test\n    public void convertToEntityAttribute_should_return_null_when_given_a_null_object() {\n        OffsetDateTimeConverter offsetDateAndTimeConverter = new OffsetDateTimeConverter();\n        OffsetDateTime nullOffsetDateTime = offsetDateAndTimeConverter.convertToEntityAttribute(null);\n        assertThat(nullOffsetDateTime).isNull();\n    }\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void convertToEntityAttribute_should_throw_an_exception_when_given_a_faulty_string() {\n        OffsetDateTimeConverter offsetDateAndTimeConverter = new OffsetDateTimeConverter();\n        expectedException.expect(RuntimeException.class);\n        expectedException.expectMessage(\n                \"Database OffsetDate&Time format must be ISO-8601 compliant yyyy-MM-dd'T'HH:mm:ss(.SSS)Z \");\n        OffsetDateTime offsetDateTime = offsetDateAndTimeConverter.convertToEntityAttribute(\"Django\");\n    }\n\n    @Test\n    public void convertToDatabaseColumn_should_modify_the_day_month_and_year_when_switching_to_UTC_in_the_generated_string()\n            throws Exception {\n        OffsetDateTimeConverter offsetDateAndTimeConverter = new OffsetDateTimeConverter();\n        OffsetDateTime offsetDateTime = OffsetDateTime.of(LocalDateTime.of(2017, 1, 1, 7, 42, 12, 9649),\n                ZoneOffset.ofHours(10));\n        String offsetDateAndTimeString = offsetDateAndTimeConverter.convertToDatabaseColumn(offsetDateTime);\n        assertThat(offsetDateAndTimeString).isNotNull();\n        assertThat(offsetDateAndTimeString).isEqualTo(\"2016-12-31T21:42:12.000009649Z\");\n\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/PersistenceUnitBuilderTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.w3c.dom.Document;\nimport org.w3c.dom.NodeList;\n\n/**\n * @author Romain Bioteau\n */\npublic class PersistenceUnitBuilderTest {\n\n    private PersistenceUnitBuilder builder;\n\n    /**\n     * @throws Exception\n     */\n    @Before\n    public void setUp() throws Exception {\n        builder = new PersistenceUnitBuilder();\n    }\n\n    @Test\n    public void shouldDone_WhenNothingIsCalledReturnsDefaultPersistenceUnits() {\n        Document document = builder.done();\n        assertThat(document).isNotNull();\n        assertThat(document.getElementsByTagName(\"persistence-unit\").getLength()).isEqualTo(1);\n        assertThat(document.getElementsByTagName(\"class\").getLength()).isZero();\n    }\n\n    @Test\n    public void shouldAddClass_AddOneClassTagByClassname() throws Exception {\n        builder.addClass(\"org.bonitasoft.businessdata.Employee\").addClass(\"org.bonitasoft.businessdata.LeaveRequesst\");\n        Document document = builder.done();\n        assertThat(document.getElementsByTagName(\"class\").getLength()).isEqualTo(2);\n        NodeList nodeList = document.getElementsByTagName(\"class\");\n        Set<String> classnames = new HashSet<String>();\n        for (int i = 0; i < nodeList.getLength(); i++) {\n            classnames.add(nodeList.item(i).getTextContent());\n        }\n        assertThat(classnames).containsOnly(\"org.bonitasoft.businessdata.Employee\",\n                \"org.bonitasoft.businessdata.LeaveRequesst\");\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/RelationFieldAnnotatorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class RelationFieldAnnotatorTest {\n\n    private RelationFieldAnnotator annotator;\n\n    @Before\n    public void setUp() {\n        annotator = new RelationFieldAnnotator(new CodeGenerator());\n    }\n\n    @Test\n    public void getJoinTableName_should_truncate_names_longer_than_14_chars() {\n        String joinTableName = annotator.getJoinTableName(\"someLongNameLongerThanFourteen\",\n                \"anotherLongNameLongerThanFourteen\");\n\n        assertThat(joinTableName).isEqualTo(\"SOMELONGNAMELO_ANOTHERLONGNAM\");\n    }\n\n    @Test\n    public void getJoinColumnName_should_truncate_names_longer_thab_26_chars() {\n        String joinColumnName = annotator.getJoinColumnName(\"someLongNameLongerThantwentySix\");\n\n        assertThat(joinColumnName).isEqualTo(\"SOMELONGNAMELONGERTHANTWEN_PID\");\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/client/ClientBDMCodeGeneratorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator.client;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.util.Strings.concat;\nimport static org.bonitasoft.engine.bdm.builder.BusinessObjectBuilder.aBO;\nimport static org.bonitasoft.engine.bdm.builder.BusinessObjectModelBuilder.aBOM;\nimport static org.bonitasoft.engine.bdm.builder.FieldBuilder.aRelationField;\nimport static org.bonitasoft.engine.bdm.builder.FieldBuilder.aStringField;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.URISyntaxException;\nimport java.net.URL;\nimport java.util.Date;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.UUID;\n\nimport com.sun.codemodel.JAnnotationUse;\nimport com.sun.codemodel.JDefinedClass;\nimport org.apache.commons.io.FileUtils;\nimport org.assertj.core.util.Files;\nimport org.bonitasoft.engine.bdm.BusinessObjectModelConverter;\nimport org.bonitasoft.engine.bdm.builder.BusinessObjectModelBuilder;\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.model.Query;\nimport org.bonitasoft.engine.bdm.model.QueryParameter;\nimport org.bonitasoft.engine.bdm.model.field.FieldType;\nimport org.bonitasoft.engine.bdm.model.field.RelationField;\nimport org.bonitasoft.engine.bdm.model.field.SimpleField;\nimport org.bonitasoft.engine.business.data.generator.AbstractBDMCodeGenerator;\nimport org.bonitasoft.engine.business.data.generator.CompilableCode;\nimport org.bonitasoft.engine.commons.io.IOUtil;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class ClientBDMCodeGeneratorTest extends CompilableCode {\n\n    private static final String EMPLOYEE_QUALIFIED_NAME = \"com.company.hr.Employee\";\n\n    private AbstractBDMCodeGenerator bdmCodeGenerator;\n\n    private File destDir;\n\n    @Before\n    public void setUp() {\n        bdmCodeGenerator = new ClientBDMCodeGenerator();\n        try {\n            destDir = Files.newTemporaryFolder();\n        } catch (final Exception fe) {\n            System.err.println(\"Seems we cannot create temporary folder. Retrying...\");\n            final String tempFileName = String.valueOf(UUID.randomUUID().getLeastSignificantBits());\n            destDir = Files.newFolder(concat(Files.temporaryFolderPath(), tempFileName));\n        }\n    }\n\n    @After\n    public void tearDown() throws Exception {\n        FileUtils.deleteDirectory(destDir);\n    }\n\n    @Test\n    public void shouldToJavaClass_ReturnIntegerClass() {\n        assertThat(bdmCodeGenerator.toJavaClass(FieldType.INTEGER).name()).isEqualTo(Integer.class.getSimpleName());\n    }\n\n    @Test\n    public void shouldToJavaClass_ReturnStringClass() {\n        assertThat(bdmCodeGenerator.toJavaClass(FieldType.STRING).name()).isEqualTo(String.class.getSimpleName());\n    }\n\n    @Test\n    public void shouldToJavaClass_ReturnLongClass() {\n        assertThat(bdmCodeGenerator.toJavaClass(FieldType.LONG).name()).isEqualTo(Long.class.getSimpleName());\n    }\n\n    @Test\n    public void shouldToJavaClass_ReturnDoubleClass() {\n        assertThat(bdmCodeGenerator.toJavaClass(FieldType.DOUBLE).name()).isEqualTo(Double.class.getSimpleName());\n    }\n\n    @Test\n    public void shouldToJavaClass_ReturnFloatClass() {\n        assertThat(bdmCodeGenerator.toJavaClass(FieldType.FLOAT).name()).isEqualTo(Float.class.getSimpleName());\n    }\n\n    @Test\n    public void shouldToJavaClass_ReturnBooleanClass() {\n        assertThat(bdmCodeGenerator.toJavaClass(FieldType.BOOLEAN).name()).isEqualTo(Boolean.class.getSimpleName());\n    }\n\n    @Test\n    public void shouldToJavaClass_ReturnDateClass() {\n        assertThat(bdmCodeGenerator.toJavaClass(FieldType.DATE).name()).isEqualTo(Date.class.getSimpleName());\n    }\n\n    @Test\n    public void shouldToJavaClass_ReturnStringTextClass() {\n        assertThat(bdmCodeGenerator.toJavaClass(FieldType.TEXT).name()).isEqualTo(String.class.getSimpleName());\n    }\n\n    @Test\n    public void should_AddDao_generate_Dao_interface_with_query_methods_signature() throws Exception {\n        final BusinessObject employeeBO = new BusinessObject();\n        employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME);\n        final SimpleField nameField = new SimpleField();\n        nameField.setName(\"name\");\n        nameField.setType(FieldType.STRING);\n        employeeBO.getFields().add(nameField);\n\n        final BusinessObjectModel bom = new BusinessObjectModel();\n        bom.addBusinessObject(employeeBO);\n        bdmCodeGenerator = new ClientBDMCodeGenerator();\n        bdmCodeGenerator.generateBom(bom, destDir);\n        final String daoContent = readGeneratedDAOInterface();\n        assertThat(daoContent)\n                .contains(\"public List<Employee> findByName(String name, int startIndex, int maxResults)\");\n    }\n\n    @Test\n    public void queryGenerationReturningListShouldAddPaginationParameters() throws Exception {\n        final BusinessObject employeeBO = new BusinessObject();\n        employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME);\n        final SimpleField nameField = new SimpleField();\n        nameField.setName(\"name\");\n        nameField.setType(FieldType.STRING);\n        employeeBO.getFields().add(nameField);\n        final SimpleField ageField = new SimpleField();\n        ageField.setName(\"age\");\n        ageField.setType(FieldType.INTEGER);\n        employeeBO.getFields().add(ageField);\n\n        final Query query = new Query(\"getEmployeesByNameAndAge\",\n                \"SELECT e FROM Employee e WHERE e.name = :myName AND e.age = :miEdad\", List.class.getName());\n        query.addQueryParameter(\"miEdad\", Integer.class.getName());\n        query.addQueryParameter(\"myName\", String.class.getName());\n        employeeBO.getQueries().add(query);\n        final BusinessObjectModel bom = new BusinessObjectModel();\n        bom.addBusinessObject(employeeBO);\n        bdmCodeGenerator = new ClientBDMCodeGenerator();\n        bdmCodeGenerator.generateBom(bom, destDir);\n        final String daoContent = readGeneratedDAOInterface();\n\n        assertThat(daoContent).contains(\n                \"public List<Employee> getEmployeesByNameAndAge(Integer miEdad, String myName, int startIndex, int maxResults)\");\n    }\n\n    protected String getQueryMethodSignature(final Query query, final String queryReturnType,\n            final String businessObjectName, final boolean returnsList) {\n        String signature = \"public \" + getSimpleClassName(queryReturnType) + \"<\"\n                + getSimpleClassName(businessObjectName)\n                + \"> \" + query.getName() + \"(\";\n        boolean first = true;\n        for (final QueryParameter param : query.getQueryParameters()) {\n            signature = appendCommaIfNotFirstParam(signature, first);\n            signature += getSimpleClassName(param.getClassName()) + \" \" + param.getName();\n            first = false;\n        }\n        if (returnsList) {\n            signature = appendCommaIfNotFirstParam(signature, first);\n            signature += \"int startIndex, int maxResults\";\n        }\n        signature += \")\";\n        return signature;\n    }\n\n    protected String appendCommaIfNotFirstParam(final String signature, final boolean first) {\n        String newSignature = signature;\n        if (!first) {\n            newSignature += \", \";\n        }\n        return newSignature;\n    }\n\n    private String getSimpleClassName(final String qualifedClassName) {\n        return qualifedClassName.substring(qualifedClassName.lastIndexOf('.') + 1);\n    }\n\n    @Test\n    public void should_AddDao_generate_Dao_interface_with_unique_constraint_methods_signature() throws Exception {\n        final BusinessObject employeeBO = new BusinessObject();\n        employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME);\n        final SimpleField nameField = new SimpleField();\n        nameField.setName(\"firstName\");\n        nameField.setType(FieldType.STRING);\n\n        final SimpleField lastnameField = new SimpleField();\n        lastnameField.setName(\"lastName\");\n        lastnameField.setType(FieldType.STRING);\n        employeeBO.getFields().add(nameField);\n        employeeBO.getFields().add(lastnameField);\n\n        employeeBO.addUniqueConstraint(\"TOTO\", \"firstName\", \"lastName\");\n        final BusinessObjectModel bom = new BusinessObjectModel();\n        bom.addBusinessObject(employeeBO);\n        bdmCodeGenerator = new ClientBDMCodeGenerator();\n        bdmCodeGenerator.generateBom(bom, destDir);\n    }\n\n    private String readGeneratedDAOInterface() throws IOException {\n        final File daoInterface = getGeneratedFile(EMPLOYEE_QUALIFIED_NAME.replace(\".\", File.separator) + \"DAO.java\");\n        return FileUtils.readFileToString(daoInterface);\n    }\n\n    public JAnnotationUse getAnnotation(final JDefinedClass definedClass, final String annotationClassName) {\n        final Iterator<JAnnotationUse> iterator = definedClass.annotations().iterator();\n        JAnnotationUse annotation = null;\n        while (annotation == null && iterator.hasNext()) {\n            final JAnnotationUse next = iterator.next();\n            if (next.getAnnotationClass().fullName().equals(annotationClassName)) {\n                annotation = next;\n            }\n        }\n        return annotation;\n    }\n\n    @Test\n    public void addIndexAnnotation() throws Exception {\n        final BusinessObjectModel model = new BusinessObjectModel();\n        final SimpleField field = new SimpleField();\n        field.setName(\"firstName\");\n        field.setType(FieldType.STRING);\n        final BusinessObject employeeBO = new BusinessObject();\n        employeeBO.setQualifiedName(\"Employee\");\n        employeeBO.addField(field);\n        employeeBO.addIndex(\"IDX_1\", \"firstName, lastName\");\n        model.addBusinessObject(employeeBO);\n\n        bdmCodeGenerator = new ClientBDMCodeGenerator();\n        bdmCodeGenerator.generateBom(model, destDir);\n\n        assertFilesAreEqual(\"Employee.java\", \"Employee.java.txt\");\n    }\n\n    @Test\n    public void addSimpleReferenceWithComposition() throws Exception {\n        final RelationField aggregation = aRelationField().withName(\"address\").composition().referencing(addressBO())\n                .build();\n        final BusinessObjectModel bom = employeeWithRelations(aggregation);\n\n        bdmCodeGenerator = new ClientBDMCodeGenerator();\n        bdmCodeGenerator.generateBom(bom, destDir);\n\n        assertFilesAreEqual(\"Employee.java\", \"EmployeeSimpleComposition.java.txt\");\n    }\n\n    @Test\n    public void addListReferenceWithComposition() throws Exception {\n        final RelationField eager = aRelationField().withName(\"addresses\").composition().multiple()\n                .referencing(addressBO())\n                .build();\n        final RelationField lazy = aRelationField().withName(\"skills\").composition().multiple().lazy()\n                .referencing(skillBO())\n                .build();\n        final BusinessObjectModel bom = employeeWithRelations(eager, lazy);\n\n        bdmCodeGenerator = new ClientBDMCodeGenerator();\n        bdmCodeGenerator.generateBom(bom, destDir);\n\n        assertFilesAreEqual(\"Employee.java\", \"EmployeeListComposition.java.txt\");\n        assertFilesAreEqual(\"Skill.java\", \"Skill.java.txt\");\n    }\n\n    @Test\n    public void newInstance_is_generated_with_mandatory_fields_in_parameters() throws Exception {\n        final InputStream resourceAsStream = ClientBDMCodeGeneratorTest.class.getResourceAsStream(\"/failing_bdm.zip\");\n        final BusinessObjectModel businessObjectModel = new BusinessObjectModelConverter()\n                .unzip(IOUtil.getAllContentFrom(resourceAsStream));\n\n        bdmCodeGenerator = new ClientBDMCodeGenerator();\n        bdmCodeGenerator.generateBom(businessObjectModel, destDir);\n\n        assertFilesAreEqual(\"com/test/model/PersonneDAOImpl.java\", \"PersonneDAOImpl.java.txt\");\n    }\n\n    @Test\n    public void addSimpleReferenceWithAggregation() throws Exception {\n        final RelationField aggregation = aRelationField().withName(\"address\").aggregation().referencing(addressBO())\n                .build();\n        final BusinessObjectModel bom = employeeWithRelations(aggregation);\n\n        bdmCodeGenerator = new ClientBDMCodeGenerator();\n        bdmCodeGenerator.generateBom(bom, destDir);\n\n        assertFilesAreEqual(\"Employee.java\", \"EmployeeSimpleAggregation.java.txt\");\n    }\n\n    @Test\n    public void addListReferenceWithAggregation() throws Exception {\n        final RelationField aggregationMultiple = aRelationField().withName(\"addresses\").aggregation().multiple()\n                .referencing(addressBO()).build();\n        final BusinessObjectModel bom = employeeWithRelations(aggregationMultiple);\n\n        bdmCodeGenerator = new ClientBDMCodeGenerator();\n        bdmCodeGenerator.generateBom(bom, destDir);\n\n        assertFilesAreEqual(\"Employee.java\", \"EmployeeListAggregation.java.txt\");\n    }\n\n    @Test\n    public void addListReferenceWithLazyAggregation() throws Exception {\n        final BusinessObject addressBO = addressBO();\n        addressBO.addUniqueConstraint(\"city\", \"city\");\n        final RelationField aggregationMultiple = aRelationField().withName(\"addresses\").aggregation().multiple().lazy()\n                .referencing(addressBO).build();\n        final BusinessObjectModel bom = employeeWithRelations(aggregationMultiple);\n\n        bdmCodeGenerator = new ClientBDMCodeGenerator();\n        bdmCodeGenerator.generateBom(bom, destDir);\n\n        assertFilesAreEqual(\"AddressDAO.java\", \"AddressDAOWithLazyReferenceOnEmployee.java.txt\");\n        assertFilesAreEqual(\"AddressDAOImpl.java\", \"AddressDAOImplWithLazyReferenceOnEmployee.java.txt\");\n    }\n\n    @Test\n    public void addList() throws Exception {\n        final BusinessObjectModel model = build();\n\n        bdmCodeGenerator = new ClientBDMCodeGenerator();\n        bdmCodeGenerator.generateBom(model, destDir);\n\n        assertFilesAreEqual(\"Forecast.java\", \"ForecastList.java.txt\");\n    }\n\n    private BusinessObject skillBO() {\n        return aBO(\"Skill\").withField(aStringField(\"skill\").build()).build();\n    }\n\n    private BusinessObjectModel build() {\n        final SimpleField field = new SimpleField();\n        field.setName(\"temperatures\");\n        field.setType(FieldType.DOUBLE);\n        field.setCollection(Boolean.TRUE);\n\n        final BusinessObject forecastBO = new BusinessObject();\n        forecastBO.setQualifiedName(\"Forecast\");\n        forecastBO.addField(field);\n\n        final BusinessObjectModel model = new BusinessObjectModel();\n        model.addBusinessObject(forecastBO);\n        return model;\n    }\n\n    private BusinessObjectModel employeeWithRelations(final RelationField... field) {\n        final BusinessObject employeeBO = employeeBO();\n        BusinessObjectModelBuilder aBom = aBOM().withBO(employeeBO);\n        for (final RelationField relationField : field) {\n            employeeBO.addField(relationField);\n            aBom = aBom.withBO(relationField.getReference());\n        }\n        return aBom.build();\n    }\n\n    private BusinessObject employeeBO() {\n        return aBO(\"Employee\").withField(aStringField(\"firstName\").build()).build();\n    }\n\n    private BusinessObject addressBO() {\n        return aBO(\"Address\").withField(aStringField(\"street\").build()).withField(aStringField(\"city\").build()).build();\n    }\n\n    private void assertFilesAreEqual(final String qualifiedName, final String resourceName) throws Exception {\n        assertThat(getGeneratedFile(qualifiedName)).hasContentEqualTo(getExpectedFile(resourceName));\n    }\n\n    private File getExpectedFile(String resourceName) throws URISyntaxException {\n        File expected;\n        final URL resource = ClientBDMCodeGeneratorTest.class.getResource(resourceName);\n        expected = new File(resource.toURI());\n        return expected;\n    }\n\n    private File getGeneratedFile(String qualifiedName) {\n        return new File(destDir, qualifiedName);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/client/ClientBDMJarBuilderTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator.client;\n\nimport static org.mockito.Mockito.verify;\n\nimport java.io.File;\n\nimport org.apache.commons.io.FileUtils;\nimport org.bonitasoft.engine.business.data.generator.BOMBuilder;\nimport org.bonitasoft.engine.io.IOUtils;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ClientBDMJarBuilderTest {\n\n    @Mock\n    private ResourcesLoader resourcesLoader;\n\n    @InjectMocks\n    private ClientBDMJarBuilder clientBDMJarBuilder;\n\n    @Before\n    public void setUp() {\n        clientBDMJarBuilder = new ClientBDMJarBuilder(resourcesLoader);\n    }\n\n    @Test\n    public void should_add_client_resources_to_compilation_folder() throws Exception {\n        File directory = IOUtils.createTempDirectory(\"bdmtest\");\n\n        clientBDMJarBuilder.addSourceFilesToDirectory(BOMBuilder.aBOM().build(), directory);\n\n        verify(resourcesLoader).copyJavaFilesToDirectory(\"org.bonitasoft.engine.bdm.dao.client.resources\",\n                directory);\n        FileUtils.deleteDirectory(directory);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/client/ResourcesLoaderTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator.client;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.io.File;\n\nimport org.apache.commons.io.FileUtils;\nimport org.bonitasoft.engine.io.IOUtils;\nimport org.junit.Test;\n\npublic class ResourcesLoaderTest {\n\n    @Test\n    public void should_copy_files_from_package_in_classpath_to_destination_directory() throws Exception {\n        String packageName = \"org.bonitasoft.engine.package\";\n        File directory = IOUtils.createTempDirectory(\"bdmtest\");\n\n        new ResourcesLoader().copyJavaFilesToDirectory(packageName, directory);\n\n        assertThat(new File(directory, \"org/bonitasoft/engine/package/Test.java\")).exists();\n        assertThat(new File(directory, \"org/bonitasoft/engine/package/subfolder/Other.java\")).exists();\n        FileUtils.deleteDirectory(directory);\n    }\n\n    @Test\n    public void should_return_the_package_of_in_regular_environment() {\n        assertThat(ResourcesLoader.packageOf(\"jar:file:/bonita_install/folder/com/company/Person.java\",\n                \"com.company\")).isEqualTo(\"com/company\");\n    }\n\n    @Test\n    public void should_return_the_package_of_in_osgi_environment() {\n        assertThat(ResourcesLoader.packageOf(\"jar:file:/bonita_install/jars/my.jar!/com/company/Address.java\",\n                \"com.company\")).isEqualTo(\"com/company\");\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/compiler/JDTCompilerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator.compiler;\n\nimport static java.lang.Thread.currentThread;\nimport static java.util.Arrays.asList;\nimport static org.apache.commons.io.IOUtils.toByteArray;\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.lang.reflect.Method;\nimport java.net.URISyntaxException;\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.List;\n\nimport javax.persistence.Entity;\n\nimport org.bonitasoft.engine.commons.io.IOUtil;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.TemporaryFolder;\n\n/**\n * @author Colin PUY\n * @author Celine Souchet\n */\npublic class JDTCompilerTest {\n\n    private JDTCompiler jdtCompiler;\n\n    private File outputDirectory;\n    @Rule\n    public TemporaryFolder temporaryFolder = new TemporaryFolder();\n\n    @Before\n    public void instanciateCompiler() throws IOException {\n        jdtCompiler = new JDTCompiler();\n        outputDirectory = temporaryFolder.newFolder();\n    }\n\n    @After\n    public void after() throws IOException {\n        IOUtil.deleteDir(outputDirectory);\n    }\n\n    private File getTestResourceAsFile(final String fileName) throws URISyntaxException {\n        final URL resource = JDTCompilerTest.class.getResource(fileName);\n        if (resource == null) {\n            throw new RuntimeException(\"Test resource \" + fileName + \" not found\");\n        }\n        return new File(resource.toURI());\n    }\n\n    @Test\n    public void should_compile_files_in_output_directory() throws Exception {\n        File srcDir = temporaryFolder.newFolder();\n        writeTestSrcDir(srcDir, \"org\", \"bonitasoft\", \"CompilableOne.java\");\n        writeTestSrcDir(srcDir, \"org\", \"bonitasoft\", \"CompilableTwo.java\");\n\n        jdtCompiler.compile(srcDir, outputDirectory);\n\n        assertThat(new File(outputDirectory, \"org/bonitasoft/CompilableOne.class\")).exists();\n        assertThat(new File(outputDirectory, \"org/bonitasoft/CompilableTwo.class\")).exists();\n\n        try (URLClassLoader urlClassLoader = new URLClassLoader(new URL[] { outputDirectory.toURI().toURL() },\n                currentThread().getContextClassLoader())) {\n            final Class<?> compilableOneClass = urlClassLoader.loadClass(\"org.bonitasoft.CompilableOne\");\n            final Method method = compilableOneClass.getMethod(\"setaClassVariable\", int.class);\n            assertThat(method.getParameters()[0].getName()).isEqualTo(\"aClassVariable\");\n        }\n    }\n\n    @Test(expected = CompilationException.class)\n    public void should_throw_exception_if_compilation_errors_occurs() throws Exception {\n        File srcDir = temporaryFolder.newFolder();\n        writeTestSrcDir(srcDir, \"com\", \"bonitasoft\", \"CannotBeResolvedToATypeError.java\");\n\n        jdtCompiler.compile(srcDir, outputDirectory);\n    }\n\n    @Test\n    public void should_show_compilation_errors_in_exception_message() throws Exception {\n        File srcDir = temporaryFolder.newFolder();\n        writeTestSrcDir(srcDir, \"com\", \"bonitasoft\", \"CannotBeResolvedToATypeError.java\");\n\n        try {\n            jdtCompiler.compile(srcDir, outputDirectory);\n        } catch (final CompilationException e) {\n            assertThat(e.getMessage()).contains(\"cannot be resolved to a type\");\n        }\n    }\n\n    @Test\n    public void should_compile_class_with_external_dependencies() throws Exception {\n        File srcDir = temporaryFolder.newFolder();\n        writeTestSrcDir(srcDir, \"DependenciesNeeded.java\");\n        final File externalLib = getTestResourceAsFile(\"external-lib.jar\");\n        jdtCompiler.compile(srcDir, outputDirectory, externalLib, JDTCompiler.lookupJarContaining(Entity.class));\n    }\n\n    @Test\n    public void should_compile_class_with_lowercase_name() throws Exception {\n        File srcDir = temporaryFolder.newFolder();\n        writeTestSrcDir(srcDir, \"org\", \"bonitasoft\", \"employee.java\");\n\n        jdtCompiler.compile(srcDir, outputDirectory);\n\n        assertThat(new File(outputDirectory, \"org/bonitasoft/employee.class\")).exists();\n\n        try (URLClassLoader urlClassLoader = new URLClassLoader(new URL[] { outputDirectory.toURI().toURL() },\n                currentThread().getContextClassLoader())) {\n            final Class<?> compilableOneClass = urlClassLoader.loadClass(\"org.bonitasoft.employee\");\n            final Method method = compilableOneClass.getMethod(\"setaClassVariable\", int.class);\n            assertThat(method.getParameters()[0].getName()).isEqualTo(\"aClassVariable\");\n        }\n    }\n\n    private void writeTestSrcDir(File srcDir, String... packageName) throws IOException {\n        Path path = srcDir.toPath();\n        List<String> strings = asList(packageName);\n        String fileName = strings.get(strings.size() - 1);\n        for (String p : packageName) {\n            path = path.resolve(p);\n        }\n        path.getParent().toFile().mkdirs();\n        Files.write(path, toByteArray(JDTCompilerTest.class.getResourceAsStream(fileName)));\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/filter/OnlyDAOImplementationFileFilterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator.filter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.doReturn;\n\nimport java.io.File;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Romain\n * @author Laurent Leseigneur\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class OnlyDAOImplementationFileFilterTest {\n\n    private OnlyDAOImplementationFileFilter fileFilter;\n\n    @Mock\n    File file;\n\n    /**\n     * @throws Exception\n     */\n    @Before\n    public void setUp() {\n        fileFilter = new OnlyDAOImplementationFileFilter();\n    }\n\n    @Test\n    public void should_accept_return_true() {\n        File f = new File(\"EmployeeDAO.class\");\n        assertThat(fileFilter.accept(f)).isFalse();\n\n        f = new File(\"EmployeeDAOImpl.class\");\n        assertThat(fileFilter.accept(f)).isTrue();\n\n        checkAcceptShouldReturns(\"org\", \"bonitasoft\", \"class\", true);\n        checkAcceptShouldReturns(\"com\", \"bonitasoft\", \"java\", false);\n        checkAcceptShouldReturns(\"com\", \"company\", \"java\", false);\n\n    }\n\n    @Test\n    public void should_accept_return_false() {\n        File f = new File(\"Employee.class\");\n        assertThat(fileFilter.accept(f)).isFalse();\n\n        f = new File(\"Employee.java.txt\");\n        assertThat(fileFilter.accept(f)).isFalse();\n    }\n\n    private void checkAcceptShouldReturns(String domain, String subDomain, String extension, boolean expectedResult) {\n        // given\n        doReturn(\"Employee.\" + extension).when(file).getName();\n        doReturn(domain + File.separatorChar + subDomain + File.separatorChar + \"model\" + File.separatorChar\n                + \"Employee.\" + extension).when(file)\n                .getAbsolutePath();\n\n        // when then\n        assertThat(fileFilter.accept(file))\n                .as(\"should return \" + expectedResult + \" when accepting file \" + file.getAbsolutePath())\n                .isEqualTo(expectedResult);\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/filter/WithoutDAOImplementationFileFilterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator.filter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.doReturn;\n\nimport java.io.File;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class WithoutDAOImplementationFileFilterTest {\n\n    private WithoutDAOImplementationFileFilter fileFilter;\n\n    @Mock\n    File file;\n\n    /**\n     * @throws Exception\n     */\n    @Before\n    public void setUp() {\n        fileFilter = new WithoutDAOImplementationFileFilter();\n    }\n\n    @Test\n    public void should_accept_return_false() {\n        File f = new File(\"EmployeeDAOImpl.class\");\n        assertThat(fileFilter.accept(f)).isFalse();\n\n        f = new File(\"EmployeeDAOImpl.java\");\n        assertThat(fileFilter.accept(f)).isFalse();\n    }\n\n    @Test\n    public void should_accept_exclude_reserved_package() {\n        checkAcceptShouldReturns(\"net\", \"bonitasoft\", \"class\", true);\n        checkAcceptShouldReturns(\"net\", \"bonitasoft\", \"java\", true);\n    }\n\n    private void checkAcceptShouldReturns(String domain, String subDomain, String extension, boolean expectedResult) {\n        // given\n        doReturn(\"Employee.\" + extension).when(file).getName();\n        doReturn(domain + File.separatorChar + subDomain + File.separatorChar + \"model\" + File.separatorChar\n                + \"Employee.\" + extension).when(file)\n                .getAbsolutePath();\n\n        // when then\n        assertThat(fileFilter.accept(file))\n                .as(\"should return \" + expectedResult + \" when accepting file \" + file.getAbsolutePath())\n                .isEqualTo(expectedResult);\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/server/ServerBDMCodeGeneratorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator.server;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Arrays;\n\nimport org.apache.commons.io.FileUtils;\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.model.UniqueConstraint;\nimport org.bonitasoft.engine.bdm.model.field.FieldType;\nimport org.bonitasoft.engine.bdm.model.field.SimpleField;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.TemporaryFolder;\nimport org.junit.runner.RunWith;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Romain Bioteau\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class ServerBDMCodeGeneratorTest {\n\n    private static final String EMPLOYEE_QUALIFIED_NAME = \"com.company.hr.Employee\";\n\n    private static final String SERVER_EMPLOYEE_DAO_IMPL_FILE = \"com/company/hr/server/EmployeeDAOImpl.java\";\n\n    private ServerBDMCodeGenerator serverBDMCodeGenerator;\n\n    @Rule\n    public TemporaryFolder temporaryFolder = new TemporaryFolder();\n\n    private File destDir;\n\n    @Before\n    public void setUp() throws Exception {\n        serverBDMCodeGenerator = new ServerBDMCodeGenerator();\n        destDir = temporaryFolder.newFolder();\n    }\n\n    @Test\n    public void toDaoImplClassnameShouldAddPointServerToLastPackagePart() {\n        final BusinessObject myBo = new BusinessObject();\n        myBo.setQualifiedName(\"com.bonitasoft.business.domain.Stool\");\n\n        final String daoImplClassname = new ServerBDMCodeGenerator().toDaoImplClassname(myBo);\n\n        assertThat(daoImplClassname).isEqualTo(\"com.bonitasoft.business.domain.server.StoolDAOImpl\");\n    }\n\n    @Test\n    public void toDaoImplClassnameShouldAddPointServerOnDefaultPackageBO() {\n        final BusinessObject myBo = new BusinessObject();\n        myBo.setQualifiedName(\"Zucchini\");\n\n        final String daoImplClassname = new ServerBDMCodeGenerator().toDaoImplClassname(myBo);\n\n        assertThat(daoImplClassname).isEqualTo(\"server.ZucchiniDAOImpl\");\n    }\n\n    @Test\n    public void serverDAOImplShouldGenerateConstructorWithBusinessDataRepository() throws Exception {\n        final BusinessObject employeeBO = new BusinessObject();\n        employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME);\n\n        final BusinessObjectModel bom = new BusinessObjectModel();\n        bom.addBusinessObject(employeeBO);\n        final SimpleField aField = new SimpleField();\n        aField.setName(\"aField\");\n        aField.setType(FieldType.STRING);\n        employeeBO.getFields().add(aField);\n        serverBDMCodeGenerator = new ServerBDMCodeGenerator();\n        serverBDMCodeGenerator.generateBom(bom, destDir);\n\n        final String serverDaoImplContent = readGeneratedServerDAOImpl();\n        assertThat(serverDaoImplContent).contains(\n                \"public EmployeeDAOImpl(BusinessDataRepository businessDataRepository)\",\n                \"this.businessDataRepository = businessDataRepository;\");\n    }\n\n    @Test\n    public void serverDAOImplShouldGenerateDefaultFindMethod() throws Exception {\n        final BusinessObject employeeBO = new BusinessObject();\n        employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME);\n\n        final BusinessObjectModel bom = new BusinessObjectModel();\n        bom.addBusinessObject(employeeBO);\n        final SimpleField aField = new SimpleField();\n        aField.setName(\"aField\");\n        aField.setType(FieldType.STRING);\n        employeeBO.getFields().add(aField);\n        serverBDMCodeGenerator = new ServerBDMCodeGenerator();\n        serverBDMCodeGenerator.generateBom(bom, destDir);\n\n        final String serverDaoImplContent = readGeneratedServerDAOImpl();\n        assertThat(serverDaoImplContent).contains(\"public List<Employee> find(int startIndex, int maxResults)\",\n                \"return businessDataRepository.findListByNamedQuery(\\\"Employee.find\\\", Employee.class, queryParameters, startIndex, maxResults);\",\n                \"catch (Exception e)\");\n    }\n\n    @Test\n    public void serverDAOImplShouldGenerate_FindByNamedQuery_ForBOReturningQuery() throws Exception {\n        final BusinessObject employeeBO = new BusinessObject();\n        employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME);\n        final SimpleField nameField = new SimpleField();\n        nameField.setName(\"name\");\n        nameField.setType(FieldType.STRING);\n        employeeBO.getFields().add(nameField);\n        final UniqueConstraint uniqueConstraint = new UniqueConstraint();\n        uniqueConstraint.setName(\"uniqueName\");\n        uniqueConstraint.setFieldNames(Arrays.asList(\"name\"));\n        employeeBO.setUniqueConstraints(Arrays.asList(uniqueConstraint));\n\n        final BusinessObjectModel bom = new BusinessObjectModel();\n        bom.addBusinessObject(employeeBO);\n        serverBDMCodeGenerator = new ServerBDMCodeGenerator();\n        serverBDMCodeGenerator.generateBom(bom, destDir);\n\n        final String serverDaoImplContent = readGeneratedServerDAOImpl();\n        assertThat(serverDaoImplContent).contains(\"public Employee findByName(String name)\",\n                \"return businessDataRepository.findByNamedQuery(\\\"Employee.findByName\\\", Employee.class, queryParameters);\");\n    }\n\n    @Test\n    public void serverDAOImplShouldGenerate_FindListByNamedQuery_ForListOfBOReturningQuery() throws Exception {\n        final BusinessObject employeeBO = new BusinessObject();\n        employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME);\n        final SimpleField nameField = new SimpleField();\n        nameField.setName(\"name\");\n        nameField.setType(FieldType.STRING);\n        employeeBO.getFields().add(nameField);\n\n        final BusinessObjectModel bom = new BusinessObjectModel();\n        bom.addBusinessObject(employeeBO);\n        serverBDMCodeGenerator = new ServerBDMCodeGenerator();\n        serverBDMCodeGenerator.generateBom(bom, destDir);\n\n        final String serverDaoImplContent = readGeneratedServerDAOImpl();\n        assertThat(serverDaoImplContent).contains(\n                \"public List<Employee> findByName(String name, int startIndex, int maxResults)\",\n                \"return businessDataRepository.findListByNamedQuery(\\\"Employee.findByName\\\", Employee.class, queryParameters, startIndex, maxResults);\");\n    }\n\n    @Test\n    public void serverDAOImplShouldGenerateGoodReturnTypeForCustomQuery() throws Exception {\n        final BusinessObject employeeBO = new BusinessObject();\n        employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME);\n        final SimpleField nameField = new SimpleField();\n        nameField.setName(\"name\");\n        nameField.setType(FieldType.STRING);\n        employeeBO.getFields().add(nameField);\n        employeeBO.addQuery(\"howManyAreWe\", \"SELECT count(*) FROM Employee\", Long.class.getName());\n\n        final BusinessObjectModel bom = new BusinessObjectModel();\n        bom.addBusinessObject(employeeBO);\n        serverBDMCodeGenerator = new ServerBDMCodeGenerator();\n        serverBDMCodeGenerator.generateBom(bom, destDir);\n\n        final String serverDaoImplContent = readGeneratedServerDAOImpl();\n        assertThat(serverDaoImplContent).contains(\"public Long howManyAreWe()\",\n                \"return businessDataRepository.findByNamedQuery(\\\"Employee.howManyAreWe\\\", Long.class, queryParameters);\");\n    }\n\n    private String readGeneratedServerDAOImpl() throws IOException {\n        final File daoImplem = new File(destDir, SERVER_EMPLOYEE_DAO_IMPL_FILE);\n        return FileUtils.readFileToString(daoImplem, StandardCharsets.UTF_8);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/server/ServerBDMJarBuilderTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.generator.server;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.verify;\n\nimport java.io.File;\n\nimport org.apache.commons.io.filefilter.TrueFileFilter;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.business.data.generator.BOMBuilder;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.TemporaryFolder;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ServerBDMJarBuilderTest {\n\n    @Mock\n    private BusinessObjectModel bom;\n\n    @Rule\n    public TemporaryFolder temporaryFolder = new TemporaryFolder();\n\n    private File directory;\n    private ServerBDMJarBuilder bdmJarBuilder;\n\n    @Before\n    public void setUp() throws Exception {\n        directory = temporaryFolder.newFolder();\n        bdmJarBuilder = new ServerBDMJarBuilder(new ServerBDMCodeGenerator());\n    }\n\n    @Test\n    public void should_addPersistenceUnittestGetPersistenceFileContentFor() throws Exception {\n        bdmJarBuilder.addPersistenceFile(directory, bom);\n\n        verify(bom).getBusinessObjects();\n        assertThat(directory).isDirectory();\n        final File metaInf = new File(directory, \"META-INF\");\n        assertThat(metaInf).exists();\n        assertThat(new File(metaInf, \"persistence.xml\")).exists();\n    }\n\n    /* Just to test we have no errors in full chain. Must be improved */\n    @Test\n    public void jar_builder_should_go_well_without_errors() throws Exception {\n        bdmJarBuilder.build(BOMBuilder.aBOM().build(), TrueFileFilter.TRUE);\n    }\n\n    @Test\n    public void jar_builder_should_go_well_without_errors_with_queries() throws Exception {\n        final BOMBuilder builder = new BOMBuilder();\n        bdmJarBuilder.build(builder.buildComplex(), TrueFileFilter.TRUE);\n    }\n\n    @Test\n    public void jar_builder_should_go_well_without_errors_with_queries2() throws Exception {\n        bdmJarBuilder.build(BOMBuilder.aBOM().buildPerson(), TrueFileFilter.TRUE);\n    }\n\n    @Test\n    public void jar_builder_should_go_well_with_multipleBoolean() throws Exception {\n        final BOMBuilder builder = new BOMBuilder();\n        bdmJarBuilder.build(builder.buildModelWithMultipleBoolean(), TrueFileFilter.TRUE);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/resources/logback-test.xml",
    "content": "<configuration>\n\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->\n        <encoder>\n            <pattern>|%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <logger name=\"org.bonitasoft\" level=\"INFO\" />\n    <root level=\"WARN\">\n        <appender-ref ref=\"STDOUT\" />\n    </root>\n</configuration>\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/resources/org/bonitasoft/engine/business/data/generator/client/AddressDAOImplWithLazyReferenceOnEmployee.java.txt",
    "content": "import java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport org.bonitasoft.engine.api.CommandAPI;\nimport org.bonitasoft.engine.bdm.dao.client.resources.BusinessObjectDeserializer;\nimport org.bonitasoft.engine.bdm.dao.client.resources.proxy.LazyLoader;\nimport org.bonitasoft.engine.bdm.dao.client.resources.proxy.Proxyfier;\nimport org.bonitasoft.engine.session.APISession;\n\npublic class AddressDAOImpl\n    implements AddressDAO\n{\n\n    private APISession session;\n    private BusinessObjectDeserializer deserializer;\n    private Proxyfier proxyfier;\n\n    public AddressDAOImpl(APISession session) {\n        this.session = session;\n        this.deserializer = new BusinessObjectDeserializer();\n        LazyLoader lazyLoader = new LazyLoader(session);\n        this.proxyfier = new Proxyfier(lazyLoader);\n    }\n\n    public Address findByPersistenceId(Long persistenceId) {\n        try {\n            CommandAPI commandApi = org.bonitasoft.engine.api.TenantAPIAccessor.getCommandAPI(session);\n            Map<String, Serializable> commandParameters = new HashMap<String, Serializable>();\n            commandParameters.put(\"queryName\", \"Address.findByPersistenceId\");\n            commandParameters.put(\"returnsList\", false);\n            commandParameters.put(\"returnType\", \"Address\");\n            Map<String, Serializable> queryParameters = new HashMap<String, Serializable>();\n            queryParameters.put(\"persistenceId\", persistenceId);\n            commandParameters.put(\"queryParameters\", ((Serializable) queryParameters));\n            return proxyfier.proxify(deserializer.deserialize(((byte[]) commandApi.execute(\"executeBDMQuery\", commandParameters)), Address.class));\n        } catch (Exception e) {\n            throw new IllegalArgumentException(e);\n        }\n    }\n\n    public Address findByCity(String city) {\n        try {\n            CommandAPI commandApi = org.bonitasoft.engine.api.TenantAPIAccessor.getCommandAPI(session);\n            Map<String, Serializable> commandParameters = new HashMap<String, Serializable>();\n            commandParameters.put(\"queryName\", \"Address.findByCity\");\n            commandParameters.put(\"returnsList\", false);\n            commandParameters.put(\"returnType\", \"Address\");\n            Map<String, Serializable> queryParameters = new HashMap<String, Serializable>();\n            queryParameters.put(\"city\", city);\n            commandParameters.put(\"queryParameters\", ((Serializable) queryParameters));\n            return proxyfier.proxify(deserializer.deserialize(((byte[]) commandApi.execute(\"executeBDMQuery\", commandParameters)), Address.class));\n        } catch (Exception e) {\n            throw new IllegalArgumentException(e);\n        }\n    }\n\n    public List<Address> findByStreet(String street, int startIndex, int maxResults) {\n        try {\n            CommandAPI commandApi = org.bonitasoft.engine.api.TenantAPIAccessor.getCommandAPI(session);\n            Map<String, Serializable> commandParameters = new HashMap<String, Serializable>();\n            commandParameters.put(\"queryName\", \"Address.findByStreet\");\n            commandParameters.put(\"returnsList\", true);\n            commandParameters.put(\"returnType\", \"Address\");\n            commandParameters.put(\"startIndex\", startIndex);\n            commandParameters.put(\"maxResults\", maxResults);\n            Map<String, Serializable> queryParameters = new HashMap<String, Serializable>();\n            queryParameters.put(\"street\", street);\n            commandParameters.put(\"queryParameters\", ((Serializable) queryParameters));\n            return proxyfier.proxify(deserializer.deserializeList(((byte[]) commandApi.execute(\"executeBDMQuery\", commandParameters)), Address.class));\n        } catch (Exception e) {\n            throw new IllegalArgumentException(e);\n        }\n    }\n\n    public List<Address> find(int startIndex, int maxResults) {\n        try {\n            CommandAPI commandApi = org.bonitasoft.engine.api.TenantAPIAccessor.getCommandAPI(session);\n            Map<String, Serializable> commandParameters = new HashMap<String, Serializable>();\n            commandParameters.put(\"queryName\", \"Address.find\");\n            commandParameters.put(\"returnsList\", true);\n            commandParameters.put(\"returnType\", \"Address\");\n            commandParameters.put(\"startIndex\", startIndex);\n            commandParameters.put(\"maxResults\", maxResults);\n            Map<String, Serializable> queryParameters = new HashMap<String, Serializable>();\n            commandParameters.put(\"queryParameters\", ((Serializable) queryParameters));\n            return proxyfier.proxify(deserializer.deserializeList(((byte[]) commandApi.execute(\"executeBDMQuery\", commandParameters)), Address.class));\n        } catch (Exception e) {\n            throw new IllegalArgumentException(e);\n        }\n    }\n\n    public Long countForFindByStreet(String street) {\n        try {\n            CommandAPI commandApi = org.bonitasoft.engine.api.TenantAPIAccessor.getCommandAPI(session);\n            Map<String, Serializable> commandParameters = new HashMap<String, Serializable>();\n            commandParameters.put(\"queryName\", \"Address.countForFindByStreet\");\n            commandParameters.put(\"returnsList\", false);\n            commandParameters.put(\"returnType\", \"java.lang.Long\");\n            Map<String, Serializable> queryParameters = new HashMap<String, Serializable>();\n            queryParameters.put(\"street\", street);\n            commandParameters.put(\"queryParameters\", ((Serializable) queryParameters));\n            return ((Long) deserializer.deserialize(((byte[]) commandApi.execute(\"executeBDMQuery\", commandParameters)), Long.class));\n        } catch (Exception e) {\n            throw new IllegalArgumentException(e);\n        }\n    }\n\n    public Long countForFind() {\n        try {\n            CommandAPI commandApi = org.bonitasoft.engine.api.TenantAPIAccessor.getCommandAPI(session);\n            Map<String, Serializable> commandParameters = new HashMap<String, Serializable>();\n            commandParameters.put(\"queryName\", \"Address.countForFind\");\n            commandParameters.put(\"returnsList\", false);\n            commandParameters.put(\"returnType\", \"java.lang.Long\");\n            return ((Long) deserializer.deserialize(((byte[]) commandApi.execute(\"executeBDMQuery\", commandParameters)), Long.class));\n        } catch (Exception e) {\n            throw new IllegalArgumentException(e);\n        }\n    }\n\n    public Address newInstance() {\n        Address instance = new Address();\n        return instance;\n    }\n\n}"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/resources/org/bonitasoft/engine/business/data/generator/client/AddressDAOWithLazyReferenceOnEmployee.java.txt",
    "content": "import java.util.List;\nimport org.bonitasoft.engine.bdm.dao.BusinessObjectDAO;\n\npublic interface AddressDAO\n    extends BusinessObjectDAO\n{\n\n\n    public Address findByPersistenceId(Long persistenceId);\n\n    public Address findByCity(String city);\n\n    public List<Address> findByStreet(String street, int startIndex, int maxResults);\n\n    public List<Address> find(int startIndex, int maxResults);\n\n    public Long countForFindByStreet(String street);\n\n    public Long countForFind();\n\n    public Address newInstance();\n\n}"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/resources/org/bonitasoft/engine/business/data/generator/client/Employee.java.txt",
    "content": "import javax.persistence.Column;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.Id;\nimport javax.persistence.NamedQueries;\nimport javax.persistence.NamedQuery;\nimport javax.persistence.Version;\nimport org.hibernate.annotations.GenericGenerator;\nimport org.hibernate.annotations.Index;\nimport org.hibernate.annotations.Parameter;\n\n\n/**\n * \n */\n@javax.persistence.Entity(name = \"Employee\")\n@org.hibernate.annotations.Table(appliesTo = \"EMPLOYEE\", indexes = {\n    @Index(name = \"IDX_1\", columnNames = {\n        \"FIRSTNAME, LASTNAME\"\n    })\n})\n@javax.persistence.Table(name = \"EMPLOYEE\")\n@NamedQueries({\n    @NamedQuery(name = \"Employee.findByPersistenceId\", query = \"SELECT e\\nFROM Employee e\\nWHERE e.persistenceId= :persistenceId\\n\"),\n    @NamedQuery(name = \"Employee.findByFirstName\", query = \"SELECT e\\nFROM Employee e\\nWHERE e.firstName= :firstName\\nORDER BY e.persistenceId\"),\n    @NamedQuery(name = \"Employee.find\", query = \"SELECT e\\nFROM Employee e\\nORDER BY e.persistenceId\"),\n    @NamedQuery(name = \"Employee.countForFindByFirstName\", query = \"SELECT COUNT(e)\\nFROM Employee e\\nWHERE e.firstName= :firstName\\n\"),\n    @NamedQuery(name = \"Employee.countForFind\", query = \"SELECT COUNT(e)\\nFROM Employee e\\n\")\n})\npublic class Employee implements org.bonitasoft.engine.bdm.Entity\n{\n\n    @Id\n    @GeneratedValue(generator = \"default_bonita_seq_generator\")\n    @GenericGenerator(name = \"default_bonita_seq_generator\", strategy = \"org.hibernate.id.enhanced.SequenceStyleGenerator\", parameters = {\n        @Parameter(name = \"sequence_name\", value = \"hibernate_sequence\")\n    })\n    private Long persistenceId;\n    @Version\n    private Long persistenceVersion;\n    @Column(name = \"FIRSTNAME\", nullable = true)\n    private String firstName;\n\n    public Employee() {\n    }\n\n    public void setPersistenceId(Long persistenceId) {\n        this.persistenceId = persistenceId;\n    }\n\n    public Long getPersistenceId() {\n        return persistenceId;\n    }\n\n    public void setPersistenceVersion(Long persistenceVersion) {\n        this.persistenceVersion = persistenceVersion;\n    }\n\n    public Long getPersistenceVersion() {\n        return persistenceVersion;\n    }\n\n    public void setFirstName(String firstName) {\n        this.firstName = firstName;\n    }\n\n    public String getFirstName() {\n        return firstName;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/resources/org/bonitasoft/engine/business/data/generator/client/EmployeeListAggregation.java.txt",
    "content": "import java.util.ArrayList;\nimport java.util.List;\nimport javax.persistence.CascadeType;\nimport javax.persistence.Column;\nimport javax.persistence.FetchType;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.Id;\nimport javax.persistence.JoinColumn;\nimport javax.persistence.JoinTable;\nimport javax.persistence.ManyToMany;\nimport javax.persistence.NamedQueries;\nimport javax.persistence.NamedQuery;\nimport javax.persistence.OrderColumn;\nimport javax.persistence.Table;\nimport javax.persistence.Version;\nimport org.hibernate.annotations.GenericGenerator;\nimport org.hibernate.annotations.Parameter;\n\n\n/**\n * \n */\n@javax.persistence.Entity(name = \"Employee\")\n@Table(name = \"EMPLOYEE\")\n@NamedQueries({\n    @NamedQuery(name = \"Employee.findByPersistenceId\", query = \"SELECT e\\nFROM Employee e\\nWHERE e.persistenceId= :persistenceId\\n\"),\n    @NamedQuery(name = \"Employee.findByFirstName\", query = \"SELECT e\\nFROM Employee e\\nWHERE e.firstName= :firstName\\nORDER BY e.persistenceId\"),\n    @NamedQuery(name = \"Employee.find\", query = \"SELECT e\\nFROM Employee e\\nORDER BY e.persistenceId\"),\n    @NamedQuery(name = \"Employee.countForFindByFirstName\", query = \"SELECT COUNT(e)\\nFROM Employee e\\nWHERE e.firstName= :firstName\\n\"),\n    @NamedQuery(name = \"Employee.countForFind\", query = \"SELECT COUNT(e)\\nFROM Employee e\\n\")\n})\npublic class Employee implements org.bonitasoft.engine.bdm.Entity\n{\n\n    @Id\n    @GeneratedValue(generator = \"default_bonita_seq_generator\")\n    @GenericGenerator(name = \"default_bonita_seq_generator\", strategy = \"org.hibernate.id.enhanced.SequenceStyleGenerator\", parameters = {\n        @Parameter(name = \"sequence_name\", value = \"hibernate_sequence\")\n    })\n    private Long persistenceId;\n    @Version\n    private Long persistenceVersion;\n    @Column(name = \"FIRSTNAME\", nullable = true)\n    private String firstName;\n    @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.MERGE)\n    @JoinTable(name = \"EMPLOYEE_ADDRESSES\", joinColumns = {\n        @JoinColumn(name = \"EMPLOYEE_PID\")\n    }, inverseJoinColumns = {\n        @JoinColumn(name = \"ADDRESS_PID\")\n    })\n    @OrderColumn\n    private List<Address> addresses = new ArrayList<Address>(10);\n\n    public Employee() {\n    }\n\n    public void setPersistenceId(Long persistenceId) {\n        this.persistenceId = persistenceId;\n    }\n\n    public Long getPersistenceId() {\n        return persistenceId;\n    }\n\n    public void setPersistenceVersion(Long persistenceVersion) {\n        this.persistenceVersion = persistenceVersion;\n    }\n\n    public Long getPersistenceVersion() {\n        return persistenceVersion;\n    }\n\n    public void setFirstName(String firstName) {\n        this.firstName = firstName;\n    }\n\n    public String getFirstName() {\n        return firstName;\n    }\n\n    public void setAddresses(List<Address> addresses) {\n        if (this.addresses == null) {\n            this.addresses = addresses;\n        } else {\n            List<Address> copy = new ArrayList(addresses);\n            this.addresses.clear();\n            this.addresses.addAll(copy);\n        }\n    }\n\n    public List<Address> getAddresses() {\n        return addresses;\n    }\n\n    public void addToAddresses(Address addTo) {\n        List addresses = getAddresses();\n        addresses.add(addTo);\n    }\n\n    public void removeFromAddresses(Address removeFrom) {\n        List addresses = getAddresses();\n        addresses.remove(removeFrom);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/resources/org/bonitasoft/engine/business/data/generator/client/EmployeeListComposition.java.txt",
    "content": "import java.util.ArrayList;\nimport java.util.List;\nimport javax.persistence.CascadeType;\nimport javax.persistence.Column;\nimport javax.persistence.FetchType;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.Id;\nimport javax.persistence.JoinColumn;\nimport javax.persistence.NamedQueries;\nimport javax.persistence.NamedQuery;\nimport javax.persistence.OneToMany;\nimport javax.persistence.OrderColumn;\nimport javax.persistence.Table;\nimport javax.persistence.Version;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport org.bonitasoft.engine.bdm.lazy.LazyLoaded;\nimport org.hibernate.annotations.GenericGenerator;\nimport org.hibernate.annotations.Parameter;\n\n\n/**\n * \n */\n@javax.persistence.Entity(name = \"Employee\")\n@Table(name = \"EMPLOYEE\")\n@NamedQueries({\n    @NamedQuery(name = \"Employee.findByPersistenceId\", query = \"SELECT e\\nFROM Employee e\\nWHERE e.persistenceId= :persistenceId\\n\"),\n    @NamedQuery(name = \"Employee.findByFirstName\", query = \"SELECT e\\nFROM Employee e\\nWHERE e.firstName= :firstName\\nORDER BY e.persistenceId\"),\n    @NamedQuery(name = \"Employee.find\", query = \"SELECT e\\nFROM Employee e\\nORDER BY e.persistenceId\"),\n    @NamedQuery(name = \"Employee.countForFindByFirstName\", query = \"SELECT COUNT(e)\\nFROM Employee e\\nWHERE e.firstName= :firstName\\n\"),\n    @NamedQuery(name = \"Employee.countForFind\", query = \"SELECT COUNT(e)\\nFROM Employee e\\n\")\n})\npublic class Employee implements org.bonitasoft.engine.bdm.Entity\n{\n\n    @Id\n    @GeneratedValue(generator = \"default_bonita_seq_generator\")\n    @GenericGenerator(name = \"default_bonita_seq_generator\", strategy = \"org.hibernate.id.enhanced.SequenceStyleGenerator\", parameters = {\n        @Parameter(name = \"sequence_name\", value = \"hibernate_sequence\")\n    })\n    private Long persistenceId;\n    @Version\n    private Long persistenceVersion;\n    @Column(name = \"FIRSTNAME\", nullable = true)\n    private String firstName;\n    @OneToMany(orphanRemoval = true, fetch = FetchType.EAGER, cascade = CascadeType.ALL)\n    @JoinColumn(name = \"EMPLOYEE_PID\", nullable = false)\n    @OrderColumn\n    private List<Address> addresses = new ArrayList<Address>(10);\n    @OneToMany(orphanRemoval = true, fetch = FetchType.LAZY, cascade = CascadeType.ALL)\n    @JoinColumn(name = \"EMPLOYEE_PID\", nullable = false)\n    @OrderColumn\n    @JsonIgnore\n    private List<Skill> skills = new ArrayList<Skill>(10);\n\n    public Employee() {\n    }\n\n    public void setPersistenceId(Long persistenceId) {\n        this.persistenceId = persistenceId;\n    }\n\n    public Long getPersistenceId() {\n        return persistenceId;\n    }\n\n    public void setPersistenceVersion(Long persistenceVersion) {\n        this.persistenceVersion = persistenceVersion;\n    }\n\n    public Long getPersistenceVersion() {\n        return persistenceVersion;\n    }\n\n    public void setFirstName(String firstName) {\n        this.firstName = firstName;\n    }\n\n    public String getFirstName() {\n        return firstName;\n    }\n\n    public void setAddresses(List<Address> addresses) {\n        if (this.addresses == null) {\n            this.addresses = addresses;\n        } else {\n            List<Address> copy = new ArrayList(addresses);\n            this.addresses.clear();\n            this.addresses.addAll(copy);\n        }\n    }\n\n    public List<Address> getAddresses() {\n        return addresses;\n    }\n\n    public void addToAddresses(Address addTo) {\n        List addresses = getAddresses();\n        addresses.add(addTo);\n    }\n\n    public void removeFromAddresses(Address removeFrom) {\n        List addresses = getAddresses();\n        addresses.remove(removeFrom);\n    }\n\n    public void setSkills(List<Skill> skills) {\n        if (this.skills == null) {\n            this.skills = skills;\n        } else {\n            List<Skill> copy = new ArrayList(skills);\n            this.skills.clear();\n            this.skills.addAll(copy);\n        }\n    }\n\n    @LazyLoaded\n    public List<Skill> getSkills() {\n        return skills;\n    }\n\n    public void addToSkills(Skill addTo) {\n        List skills = getSkills();\n        skills.add(addTo);\n    }\n\n    public void removeFromSkills(Skill removeFrom) {\n        List skills = getSkills();\n        skills.remove(removeFrom);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/resources/org/bonitasoft/engine/business/data/generator/client/EmployeeSimpleAggregation.java.txt",
    "content": "import javax.persistence.CascadeType;\nimport javax.persistence.Column;\nimport javax.persistence.FetchType;\nimport javax.persistence.ForeignKey;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.Id;\nimport javax.persistence.JoinColumn;\nimport javax.persistence.ManyToOne;\nimport javax.persistence.NamedQueries;\nimport javax.persistence.NamedQuery;\nimport javax.persistence.Table;\nimport javax.persistence.Version;\nimport org.hibernate.annotations.GenericGenerator;\nimport org.hibernate.annotations.Parameter;\n\n\n/**\n * \n */\n@javax.persistence.Entity(name = \"Employee\")\n@Table(name = \"EMPLOYEE\")\n@NamedQueries({\n    @NamedQuery(name = \"Employee.findByPersistenceId\", query = \"SELECT e\\nFROM Employee e\\nWHERE e.persistenceId= :persistenceId\\n\"),\n    @NamedQuery(name = \"Employee.findByFirstName\", query = \"SELECT e\\nFROM Employee e\\nWHERE e.firstName= :firstName\\nORDER BY e.persistenceId\"),\n    @NamedQuery(name = \"Employee.find\", query = \"SELECT e\\nFROM Employee e\\nORDER BY e.persistenceId\"),\n    @NamedQuery(name = \"Employee.countForFindByFirstName\", query = \"SELECT COUNT(e)\\nFROM Employee e\\nWHERE e.firstName= :firstName\\n\"),\n    @NamedQuery(name = \"Employee.countForFind\", query = \"SELECT COUNT(e)\\nFROM Employee e\\n\")\n})\npublic class Employee implements org.bonitasoft.engine.bdm.Entity\n{\n\n    @Id\n    @GeneratedValue(generator = \"default_bonita_seq_generator\")\n    @GenericGenerator(name = \"default_bonita_seq_generator\", strategy = \"org.hibernate.id.enhanced.SequenceStyleGenerator\", parameters = {\n        @Parameter(name = \"sequence_name\", value = \"hibernate_sequence\")\n    })\n    private Long persistenceId;\n    @Version\n    private Long persistenceVersion;\n    @Column(name = \"FIRSTNAME\", nullable = true)\n    private String firstName;\n    @ManyToOne(optional = true, fetch = FetchType.EAGER, cascade = CascadeType.MERGE)\n    @JoinColumn(name = \"ADDRESS_PID\", foreignKey = @ForeignKey(name = \"FK_157386520\"))\n    private Address address;\n\n    public Employee() {\n    }\n\n    public void setPersistenceId(Long persistenceId) {\n        this.persistenceId = persistenceId;\n    }\n\n    public Long getPersistenceId() {\n        return persistenceId;\n    }\n\n    public void setPersistenceVersion(Long persistenceVersion) {\n        this.persistenceVersion = persistenceVersion;\n    }\n\n    public Long getPersistenceVersion() {\n        return persistenceVersion;\n    }\n\n    public void setFirstName(String firstName) {\n        this.firstName = firstName;\n    }\n\n    public String getFirstName() {\n        return firstName;\n    }\n\n    public void setAddress(Address address) {\n        this.address = address;\n    }\n\n    public Address getAddress() {\n        return address;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/resources/org/bonitasoft/engine/business/data/generator/client/EmployeeSimpleComposition.java.txt",
    "content": "import javax.persistence.CascadeType;\nimport javax.persistence.Column;\nimport javax.persistence.FetchType;\nimport javax.persistence.ForeignKey;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.Id;\nimport javax.persistence.JoinColumn;\nimport javax.persistence.NamedQueries;\nimport javax.persistence.NamedQuery;\nimport javax.persistence.OneToOne;\nimport javax.persistence.Table;\nimport javax.persistence.Version;\nimport org.hibernate.annotations.GenericGenerator;\nimport org.hibernate.annotations.Parameter;\n\n\n/**\n * \n */\n@javax.persistence.Entity(name = \"Employee\")\n@Table(name = \"EMPLOYEE\")\n@NamedQueries({\n    @NamedQuery(name = \"Employee.findByPersistenceId\", query = \"SELECT e\\nFROM Employee e\\nWHERE e.persistenceId= :persistenceId\\n\"),\n    @NamedQuery(name = \"Employee.findByFirstName\", query = \"SELECT e\\nFROM Employee e\\nWHERE e.firstName= :firstName\\nORDER BY e.persistenceId\"),\n    @NamedQuery(name = \"Employee.find\", query = \"SELECT e\\nFROM Employee e\\nORDER BY e.persistenceId\"),\n    @NamedQuery(name = \"Employee.countForFindByFirstName\", query = \"SELECT COUNT(e)\\nFROM Employee e\\nWHERE e.firstName= :firstName\\n\"),\n    @NamedQuery(name = \"Employee.countForFind\", query = \"SELECT COUNT(e)\\nFROM Employee e\\n\")\n})\npublic class Employee implements org.bonitasoft.engine.bdm.Entity\n{\n\n    @Id\n    @GeneratedValue(generator = \"default_bonita_seq_generator\")\n    @GenericGenerator(name = \"default_bonita_seq_generator\", strategy = \"org.hibernate.id.enhanced.SequenceStyleGenerator\", parameters = {\n        @Parameter(name = \"sequence_name\", value = \"hibernate_sequence\")\n    })\n    private Long persistenceId;\n    @Version\n    private Long persistenceVersion;\n    @Column(name = \"FIRSTNAME\", nullable = true)\n    private String firstName;\n    @OneToOne(orphanRemoval = true, optional = true, fetch = FetchType.EAGER, cascade = CascadeType.ALL)\n    @JoinColumn(name = \"ADDRESS_PID\", foreignKey = @ForeignKey(name = \"FK_157386520\"))\n    private Address address;\n\n    public Employee() {\n    }\n\n    public void setPersistenceId(Long persistenceId) {\n        this.persistenceId = persistenceId;\n    }\n\n    public Long getPersistenceId() {\n        return persistenceId;\n    }\n\n    public void setPersistenceVersion(Long persistenceVersion) {\n        this.persistenceVersion = persistenceVersion;\n    }\n\n    public Long getPersistenceVersion() {\n        return persistenceVersion;\n    }\n\n    public void setFirstName(String firstName) {\n        this.firstName = firstName;\n    }\n\n    public String getFirstName() {\n        return firstName;\n    }\n\n    public void setAddress(Address address) {\n        this.address = address;\n    }\n\n    public Address getAddress() {\n        return address;\n    }\n\n}"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/resources/org/bonitasoft/engine/business/data/generator/client/ForecastList.java.txt",
    "content": "import java.util.ArrayList;\nimport java.util.List;\nimport javax.persistence.Column;\nimport javax.persistence.ElementCollection;\nimport javax.persistence.FetchType;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.Id;\nimport javax.persistence.NamedQueries;\nimport javax.persistence.NamedQuery;\nimport javax.persistence.OrderColumn;\nimport javax.persistence.Table;\nimport javax.persistence.Version;\nimport org.hibernate.annotations.GenericGenerator;\nimport org.hibernate.annotations.Parameter;\n\n\n/**\n * \n */\n@javax.persistence.Entity(name = \"Forecast\")\n@Table(name = \"FORECAST\")\n@NamedQueries({\n    @NamedQuery(name = \"Forecast.findByPersistenceId\", query = \"SELECT f\\nFROM Forecast f\\nWHERE f.persistenceId= :persistenceId\\n\"),\n    @NamedQuery(name = \"Forecast.find\", query = \"SELECT f\\nFROM Forecast f\\nORDER BY f.persistenceId\"),\n    @NamedQuery(name = \"Forecast.countForFind\", query = \"SELECT COUNT(f)\\nFROM Forecast f\\n\")\n})\npublic class Forecast implements org.bonitasoft.engine.bdm.Entity\n{\n\n    @Id\n    @GeneratedValue(generator = \"default_bonita_seq_generator\")\n    @GenericGenerator(name = \"default_bonita_seq_generator\", strategy = \"org.hibernate.id.enhanced.SequenceStyleGenerator\", parameters = {\n        @Parameter(name = \"sequence_name\", value = \"hibernate_sequence\")\n    })\n    private Long persistenceId;\n    @Version\n    private Long persistenceVersion;\n    @ElementCollection(fetch = FetchType.EAGER)\n    @OrderColumn\n    @Column(name = \"TEMPERATURES\", nullable = true)\n    private List<Double> temperatures = new ArrayList<Double>(10);\n\n    public Forecast() {\n    }\n\n    public void setPersistenceId(Long persistenceId) {\n        this.persistenceId = persistenceId;\n    }\n\n    public Long getPersistenceId() {\n        return persistenceId;\n    }\n\n    public void setPersistenceVersion(Long persistenceVersion) {\n        this.persistenceVersion = persistenceVersion;\n    }\n\n    public Long getPersistenceVersion() {\n        return persistenceVersion;\n    }\n\n    public void setTemperatures(List<Double> temperatures) {\n        if (this.temperatures == null) {\n            this.temperatures = temperatures;\n        } else {\n            List<Double> copy = new ArrayList(temperatures);\n            this.temperatures.clear();\n            this.temperatures.addAll(copy);\n        }\n    }\n\n    public List<Double> getTemperatures() {\n        return temperatures;\n    }\n\n    public void addToTemperatures(Double addTo) {\n        List temperatures = getTemperatures();\n        temperatures.add(addTo);\n    }\n\n    public void removeFromTemperatures(Double removeFrom) {\n        List temperatures = getTemperatures();\n        temperatures.remove(removeFrom);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/resources/org/bonitasoft/engine/business/data/generator/client/PersonneDAOImpl.java.txt",
    "content": "\npackage com.test.model;\n\nimport java.io.Serializable;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport org.bonitasoft.engine.api.CommandAPI;\nimport org.bonitasoft.engine.bdm.dao.client.resources.BusinessObjectDeserializer;\nimport org.bonitasoft.engine.bdm.dao.client.resources.proxy.LazyLoader;\nimport org.bonitasoft.engine.bdm.dao.client.resources.proxy.Proxyfier;\nimport org.bonitasoft.engine.session.APISession;\n\npublic class PersonneDAOImpl\n    implements PersonneDAO\n{\n\n    private APISession session;\n    private BusinessObjectDeserializer deserializer;\n    private Proxyfier proxyfier;\n\n    public PersonneDAOImpl(APISession session) {\n        this.session = session;\n        this.deserializer = new BusinessObjectDeserializer();\n        LazyLoader lazyLoader = new LazyLoader(session);\n        this.proxyfier = new Proxyfier(lazyLoader);\n    }\n\n    public com.test.model.Personne findByPersistenceId(Long persistenceId) {\n        try {\n            CommandAPI commandApi = org.bonitasoft.engine.api.TenantAPIAccessor.getCommandAPI(session);\n            Map<String, Serializable> commandParameters = new HashMap<String, Serializable>();\n            commandParameters.put(\"queryName\", \"Personne.findByPersistenceId\");\n            commandParameters.put(\"returnsList\", false);\n            commandParameters.put(\"returnType\", \"com.test.model.Personne\");\n            Map<String, Serializable> queryParameters = new HashMap<String, Serializable>();\n            queryParameters.put(\"persistenceId\", persistenceId);\n            commandParameters.put(\"queryParameters\", ((Serializable) queryParameters));\n            return proxyfier.proxify(deserializer.deserialize(((byte[]) commandApi.execute(\"executeBDMQuery\", commandParameters)), com.test.model.Personne.class));\n        } catch (Exception e) {\n            throw new IllegalArgumentException(e);\n        }\n    }\n\n    public com.test.model.Personne findByPrenomAndNomAndBirthDate(String prenom, String nom, Date birthDate) {\n        try {\n            CommandAPI commandApi = org.bonitasoft.engine.api.TenantAPIAccessor.getCommandAPI(session);\n            Map<String, Serializable> commandParameters = new HashMap<String, Serializable>();\n            commandParameters.put(\"queryName\", \"Personne.findByPrenomAndNomAndBirthDate\");\n            commandParameters.put(\"returnsList\", false);\n            commandParameters.put(\"returnType\", \"com.test.model.Personne\");\n            Map<String, Serializable> queryParameters = new HashMap<String, Serializable>();\n            queryParameters.put(\"prenom\", prenom);\n            queryParameters.put(\"nom\", nom);\n            queryParameters.put(\"birthDate\", birthDate);\n            commandParameters.put(\"queryParameters\", ((Serializable) queryParameters));\n            return proxyfier.proxify(deserializer.deserialize(((byte[]) commandApi.execute(\"executeBDMQuery\", commandParameters)), com.test.model.Personne.class));\n        } catch (Exception e) {\n            throw new IllegalArgumentException(e);\n        }\n    }\n\n    public List<com.test.model.Personne> findByPrenom(String prenom, int startIndex, int maxResults) {\n        try {\n            CommandAPI commandApi = org.bonitasoft.engine.api.TenantAPIAccessor.getCommandAPI(session);\n            Map<String, Serializable> commandParameters = new HashMap<String, Serializable>();\n            commandParameters.put(\"queryName\", \"Personne.findByPrenom\");\n            commandParameters.put(\"returnsList\", true);\n            commandParameters.put(\"returnType\", \"com.test.model.Personne\");\n            commandParameters.put(\"startIndex\", startIndex);\n            commandParameters.put(\"maxResults\", maxResults);\n            Map<String, Serializable> queryParameters = new HashMap<String, Serializable>();\n            queryParameters.put(\"prenom\", prenom);\n            commandParameters.put(\"queryParameters\", ((Serializable) queryParameters));\n            return proxyfier.proxify(deserializer.deserializeList(((byte[]) commandApi.execute(\"executeBDMQuery\", commandParameters)), com.test.model.Personne.class));\n        } catch (Exception e) {\n            throw new IllegalArgumentException(e);\n        }\n    }\n\n    public List<com.test.model.Personne> findByNom(String nom, int startIndex, int maxResults) {\n        try {\n            CommandAPI commandApi = org.bonitasoft.engine.api.TenantAPIAccessor.getCommandAPI(session);\n            Map<String, Serializable> commandParameters = new HashMap<String, Serializable>();\n            commandParameters.put(\"queryName\", \"Personne.findByNom\");\n            commandParameters.put(\"returnsList\", true);\n            commandParameters.put(\"returnType\", \"com.test.model.Personne\");\n            commandParameters.put(\"startIndex\", startIndex);\n            commandParameters.put(\"maxResults\", maxResults);\n            Map<String, Serializable> queryParameters = new HashMap<String, Serializable>();\n            queryParameters.put(\"nom\", nom);\n            commandParameters.put(\"queryParameters\", ((Serializable) queryParameters));\n            return proxyfier.proxify(deserializer.deserializeList(((byte[]) commandApi.execute(\"executeBDMQuery\", commandParameters)), com.test.model.Personne.class));\n        } catch (Exception e) {\n            throw new IllegalArgumentException(e);\n        }\n    }\n\n    public List<com.test.model.Personne> findByBirthDate(Date birthDate, int startIndex, int maxResults) {\n        try {\n            CommandAPI commandApi = org.bonitasoft.engine.api.TenantAPIAccessor.getCommandAPI(session);\n            Map<String, Serializable> commandParameters = new HashMap<String, Serializable>();\n            commandParameters.put(\"queryName\", \"Personne.findByBirthDate\");\n            commandParameters.put(\"returnsList\", true);\n            commandParameters.put(\"returnType\", \"com.test.model.Personne\");\n            commandParameters.put(\"startIndex\", startIndex);\n            commandParameters.put(\"maxResults\", maxResults);\n            Map<String, Serializable> queryParameters = new HashMap<String, Serializable>();\n            queryParameters.put(\"birthDate\", birthDate);\n            commandParameters.put(\"queryParameters\", ((Serializable) queryParameters));\n            return proxyfier.proxify(deserializer.deserializeList(((byte[]) commandApi.execute(\"executeBDMQuery\", commandParameters)), com.test.model.Personne.class));\n        } catch (Exception e) {\n            throw new IllegalArgumentException(e);\n        }\n    }\n\n    public List<com.test.model.Personne> find(int startIndex, int maxResults) {\n        try {\n            CommandAPI commandApi = org.bonitasoft.engine.api.TenantAPIAccessor.getCommandAPI(session);\n            Map<String, Serializable> commandParameters = new HashMap<String, Serializable>();\n            commandParameters.put(\"queryName\", \"Personne.find\");\n            commandParameters.put(\"returnsList\", true);\n            commandParameters.put(\"returnType\", \"com.test.model.Personne\");\n            commandParameters.put(\"startIndex\", startIndex);\n            commandParameters.put(\"maxResults\", maxResults);\n            Map<String, Serializable> queryParameters = new HashMap<String, Serializable>();\n            commandParameters.put(\"queryParameters\", ((Serializable) queryParameters));\n            return proxyfier.proxify(deserializer.deserializeList(((byte[]) commandApi.execute(\"executeBDMQuery\", commandParameters)), com.test.model.Personne.class));\n        } catch (Exception e) {\n            throw new IllegalArgumentException(e);\n        }\n    }\n\n    public Long countForFindByPrenom(String prenom) {\n        try {\n            CommandAPI commandApi = org.bonitasoft.engine.api.TenantAPIAccessor.getCommandAPI(session);\n            Map<String, Serializable> commandParameters = new HashMap<String, Serializable>();\n            commandParameters.put(\"queryName\", \"Personne.countForFindByPrenom\");\n            commandParameters.put(\"returnsList\", false);\n            commandParameters.put(\"returnType\", \"java.lang.Long\");\n            Map<String, Serializable> queryParameters = new HashMap<String, Serializable>();\n            queryParameters.put(\"prenom\", prenom);\n            commandParameters.put(\"queryParameters\", ((Serializable) queryParameters));\n            return ((Long) deserializer.deserialize(((byte[]) commandApi.execute(\"executeBDMQuery\", commandParameters)), Long.class));\n        } catch (Exception e) {\n            throw new IllegalArgumentException(e);\n        }\n    }\n\n    public Long countForFindByNom(String nom) {\n        try {\n            CommandAPI commandApi = org.bonitasoft.engine.api.TenantAPIAccessor.getCommandAPI(session);\n            Map<String, Serializable> commandParameters = new HashMap<String, Serializable>();\n            commandParameters.put(\"queryName\", \"Personne.countForFindByNom\");\n            commandParameters.put(\"returnsList\", false);\n            commandParameters.put(\"returnType\", \"java.lang.Long\");\n            Map<String, Serializable> queryParameters = new HashMap<String, Serializable>();\n            queryParameters.put(\"nom\", nom);\n            commandParameters.put(\"queryParameters\", ((Serializable) queryParameters));\n            return ((Long) deserializer.deserialize(((byte[]) commandApi.execute(\"executeBDMQuery\", commandParameters)), Long.class));\n        } catch (Exception e) {\n            throw new IllegalArgumentException(e);\n        }\n    }\n\n    public Long countForFindByBirthDate(Date birthDate) {\n        try {\n            CommandAPI commandApi = org.bonitasoft.engine.api.TenantAPIAccessor.getCommandAPI(session);\n            Map<String, Serializable> commandParameters = new HashMap<String, Serializable>();\n            commandParameters.put(\"queryName\", \"Personne.countForFindByBirthDate\");\n            commandParameters.put(\"returnsList\", false);\n            commandParameters.put(\"returnType\", \"java.lang.Long\");\n            Map<String, Serializable> queryParameters = new HashMap<String, Serializable>();\n            queryParameters.put(\"birthDate\", birthDate);\n            commandParameters.put(\"queryParameters\", ((Serializable) queryParameters));\n            return ((Long) deserializer.deserialize(((byte[]) commandApi.execute(\"executeBDMQuery\", commandParameters)), Long.class));\n        } catch (Exception e) {\n            throw new IllegalArgumentException(e);\n        }\n    }\n\n    public Long countForFind() {\n        try {\n            CommandAPI commandApi = org.bonitasoft.engine.api.TenantAPIAccessor.getCommandAPI(session);\n            Map<String, Serializable> commandParameters = new HashMap<String, Serializable>();\n            commandParameters.put(\"queryName\", \"Personne.countForFind\");\n            commandParameters.put(\"returnsList\", false);\n            commandParameters.put(\"returnType\", \"java.lang.Long\");\n            return ((Long) deserializer.deserialize(((byte[]) commandApi.execute(\"executeBDMQuery\", commandParameters)), Long.class));\n        } catch (Exception e) {\n            throw new IllegalArgumentException(e);\n        }\n    }\n\n    public com.test.model.Personne newInstance(String prenom, String nom, Date birthDate, Adresse adressePersonne) {\n        if (prenom == null) {\n            throw new IllegalArgumentException(\"prenom cannot be null\");\n        }\n        if (nom == null) {\n            throw new IllegalArgumentException(\"nom cannot be null\");\n        }\n        if (birthDate == null) {\n            throw new IllegalArgumentException(\"birthDate cannot be null\");\n        }\n        if (adressePersonne == null) {\n            throw new IllegalArgumentException(\"adressePersonne cannot be null\");\n        }\n        com.test.model.Personne instance = new com.test.model.Personne();\n        instance.setPrenom(prenom);\n        instance.setNom(nom);\n        instance.setBirthDate(birthDate);\n        instance.setAdressePersonne(adressePersonne);\n        return instance;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/resources/org/bonitasoft/engine/business/data/generator/client/Skill.java.txt",
    "content": "import javax.persistence.Column;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.Id;\nimport javax.persistence.NamedQueries;\nimport javax.persistence.NamedQuery;\nimport javax.persistence.Table;\nimport javax.persistence.Version;\nimport org.hibernate.annotations.GenericGenerator;\nimport org.hibernate.annotations.Parameter;\n\n\n/**\n * \n */\n@javax.persistence.Entity(name = \"Skill\")\n@Table(name = \"SKILL\")\n@NamedQueries({\n    @NamedQuery(name = \"Skill.findByPersistenceId\", query = \"SELECT s\\nFROM Skill s\\nWHERE s.persistenceId= :persistenceId\\n\"),\n    @NamedQuery(name = \"Skill.findBySkill\", query = \"SELECT s\\nFROM Skill s\\nWHERE s.skill= :skill\\nORDER BY s.persistenceId\"),\n    @NamedQuery(name = \"Skill.find\", query = \"SELECT s\\nFROM Skill s\\nORDER BY s.persistenceId\"),\n    @NamedQuery(name = \"Skill.countForFindBySkill\", query = \"SELECT COUNT(s)\\nFROM Skill s\\nWHERE s.skill= :skill\\n\"),\n    @NamedQuery(name = \"Skill.countForFind\", query = \"SELECT COUNT(s)\\nFROM Skill s\\n\"),\n    @NamedQuery(name = \"Skill.findSkillsByEmployeePersistenceId\", query = \"SELECT skills_1 FROM Employee employee_0 JOIN employee_0.skills as skills_1 WHERE employee_0.persistenceId= :persistenceId\"),\n    @NamedQuery(name = \"Skill.countForFindSkillsByEmployeePersistenceId\", query = \"SELECT COUNT(skills_1) FROM Employee employee_0 JOIN employee_0.skills as skills_1 WHERE employee_0.persistenceId= :persistenceId\")\n})\npublic class Skill implements org.bonitasoft.engine.bdm.Entity\n{\n\n    @Id\n    @GeneratedValue(generator = \"default_bonita_seq_generator\")\n    @GenericGenerator(name = \"default_bonita_seq_generator\", strategy = \"org.hibernate.id.enhanced.SequenceStyleGenerator\", parameters = {\n        @Parameter(name = \"sequence_name\", value = \"hibernate_sequence\")\n    })\n    private Long persistenceId;\n    @Version\n    private Long persistenceVersion;\n    @Column(name = \"SKILL\", nullable = true)\n    private String skill;\n\n    public Skill() {\n    }\n\n    public void setPersistenceId(Long persistenceId) {\n        this.persistenceId = persistenceId;\n    }\n\n    public Long getPersistenceId() {\n        return persistenceId;\n    }\n\n    public void setPersistenceVersion(Long persistenceVersion) {\n        this.persistenceVersion = persistenceVersion;\n    }\n\n    public Long getPersistenceVersion() {\n        return persistenceVersion;\n    }\n\n    public void setSkill(String skill) {\n        this.skill = skill;\n    }\n\n    public String getSkill() {\n        return skill;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/resources/org/bonitasoft/engine/business/data/generator/compiler/CannotBeResolvedToATypeError.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage com.bonitasoft;\n\n/**\n * Not compilable java file\n */\npublic class UnCompilable implements NotInClassPathInterface {\n\n    \n}"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/resources/org/bonitasoft/engine/business/data/generator/compiler/CompilableOne.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft;\n\npublic class CompilableOne {\n\n    private int aClassVariable;\n\n    public CompilableOne(int aClassVariable) {\n        super();\n        this.aClassVariable = aClassVariable;\n    }\n\n    public int getaClassVariable() {\n        return aClassVariable;\n    }\n\n    public void setaClassVariable(int aClassVariable) {\n        this.aClassVariable = aClassVariable;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/resources/org/bonitasoft/engine/business/data/generator/compiler/CompilableTwo.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft;\n\npublic class CompilableTwo {\n\n    private String aClassVarible;\n\n    public CompilableTwo(String aClassVarible) {\n        super();\n        this.aClassVarible = aClassVarible;\n    }\n\n    public String getaClassVarible() {\n        return aClassVarible;\n    }\n\n    public void setaClassVarible(String aClassVarible) {\n        this.aClassVarible = aClassVarible;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/resources/org/bonitasoft/engine/business/data/generator/compiler/DependenciesNeeded.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage com.bonitasoft;\n\nimport java.io.Externalizable;\n\nimport javax.persistence.Entity;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.GenerationType;\nimport javax.persistence.Id;\nimport org.external.dependency.ExternalInterface;\n\n/**\n * JPA and org.external.dependency.ExternalInterface (located in external-lib.jar) needed to be compiled\n */\n@Entity\npublic class DependenciesNeeded implements ExternalInterface {\n\n    @Id()\n    @GeneratedValue(strategy = GenerationType.AUTO)\n    private String id;\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/resources/org/bonitasoft/engine/business/data/generator/compiler/employee.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft;\n\npublic class employee {\n\n\tprivate int aClassVariable;\n\n\tpublic employee(int aClassVariable) {\n\t        super();\n\t        this.aClassVariable = aClassVariable;\n\t    }\n\n\tpublic int getaClassVariable() {\n\t\treturn aClassVariable;\n\t}\n\n\tpublic void setaClassVariable(int aClassVariable) {\n\t\tthis.aClassVariable = aClassVariable;\n\t}\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/resources/org/bonitasoft/engine/business/data/generator/server/ServerEmployee.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm.server;\n\nimport javax.persistence.Column;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.Id;\nimport javax.persistence.NamedQueries;\nimport javax.persistence.NamedQuery;\nimport javax.persistence.Version;\nimport org.hibernate.annotations.Index;\n\n\n/**\n * \n */\n@javax.persistence.Entity(name = \"Employee\")\n@org.hibernate.annotations.Table(appliesTo = \"EMPLOYEE\", indexes = {\n    @Index(name = \"IDX_1\", columnNames = {\n        \"FIRSTNAME, LASTNAME\"\n    })\n})\n@javax.persistence.Table(name = \"EMPLOYEE\")\n@NamedQueries({\n    @NamedQuery(name = \"Employee.findByFirstName\", query = \"SELECT e\\nFROM Employee e\\nWHERE e.firstName= :firstName\\nORDER BY e.persistenceId\"),\n    @NamedQuery(name = \"Employee.find\", query = \"SELECT e\\nFROM Employee e\\nORDER BY e.persistenceId\")\n})\npublic class Employee\n    implements org.bonitasoft.engine.bdm.Entity\n{\n\n    @Id\n    @GeneratedValue\n    private Long persistenceId;\n    @Version\n    private Long persistenceVersion;\n    @Column(name = \"FIRSTNAME\", nullable = true)\n    private String firstName;\n\n    public Employee() {\n    }\n\n    public void setPersistenceId(Long persistenceId) {\n        this.persistenceId = persistenceId;\n    }\n\n    public Long getPersistenceId() {\n        return persistenceId;\n    }\n\n    public void setPersistenceVersion(Long persistenceVersion) {\n        this.persistenceVersion = persistenceVersion;\n    }\n\n    public Long getPersistenceVersion() {\n        return persistenceVersion;\n    }\n\n    public void setFirstName(String firstName) {\n        this.firstName = firstName;\n    }\n\n    public String getFirstName() {\n        return firstName;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass()!= obj.getClass()) {\n            return false;\n        }\n        Employee other = ((Employee) obj);\n        if (persistenceId == null) {\n            if (other.persistenceId!= null) {\n                return false;\n            }\n        } else {\n            if (!persistenceId.equals(other.persistenceId)) {\n                return false;\n            }\n        }\n        if (persistenceVersion == null) {\n            if (other.persistenceVersion!= null) {\n                return false;\n            }\n        } else {\n            if (!persistenceVersion.equals(other.persistenceVersion)) {\n                return false;\n            }\n        }\n        if (firstName == null) {\n            if (other.firstName!= null) {\n                return false;\n            }\n        } else {\n            if (!firstName.equals(other.firstName)) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        int persistenceIdCode = 0;\n        if (persistenceId!= null) {\n            persistenceIdCode = persistenceId.hashCode();\n        }\n        result = ((prime*result)+ persistenceIdCode);\n        int persistenceVersionCode = 0;\n        if (persistenceVersion!= null) {\n            persistenceVersionCode = persistenceVersion.hashCode();\n        }\n        result = ((prime*result)+ persistenceVersionCode);\n        int firstNameCode = 0;\n        if (firstName!= null) {\n            firstNameCode = firstName.hashCode();\n        }\n        result = ((prime*result)+ firstNameCode);\n        return result;\n    }\n\n}"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/resources/org/bonitasoft/engine/package/Test.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage com.bonitasoft.engine."
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-generator/src/test/resources/org/bonitasoft/engine/package/subfolder/Other.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage com.bonitasoft.engine."
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/build.gradle",
    "content": "plugins {\n    id 'bonita-docker-database'\n    id 'bonita-tests'\n}\n\ndependencies {\n    api project(':bpm:bonita-common')\n    api project(':services:bonita-business-data:bonita-business-data-generator')\n    api project(':services:bonita-classloader')\n    api project(':services:bonita-business-data:bonita-business-data-api')\n    api project(':services:bonita-resources')\n    api project(':services:bonita-transaction')\n    api project(':services:bonita-commons')\n    api(libs.hibernateCore) {\n        exclude(module: 'jboss-transaction-api_1.2_spec')\n    }\n    api libs.commonsLang\n    api project(':services:bonita-classloader')\n    annotationProcessor libs.lombok\n    compileOnly libs.lombok\n\n    // AspectJ and Spring AOP for event aspects\n    api libs.springAop\n    api libs.aspectjWeaver\n\n    testImplementation libs.assertj\n    testImplementation(libs.jsonUnit) {\n        exclude(group: \"org.slf4j\", module: \"slf4j-api\")\n    }\n    testImplementation libs.mockitoCore\n    testImplementation libs.mockitoJunitJupiter\n    testImplementation libs.logback\n    testImplementation libs.narayanaJta\n    testImplementation libs.springTest\n    testImplementation libs.springJdbc\n    testImplementation testFixtures(project(':bpm:bonita-common'))\n    testRuntimeOnly libs.tomcatDbcp\n    testRuntimeOnly libs.h2\n}\n\ndatabaseIntegrationTest { includes '**/*IT.class' }\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/bdm/DateAndTimeConverter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm;\n\nimport java.time.LocalDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.time.format.DateTimeParseException;\n\nimport javax.persistence.AttributeConverter;\nimport javax.persistence.Converter;\n\n/**\n * @author Danila Mazour\n * @deprecated {@link org.bonitasoft.engine.business.data.generator.DateAndTimeConverter} is now used. Keep this class\n *             for backward runtime compatibility\n */\n@Deprecated\n@Converter\npublic class DateAndTimeConverter implements AttributeConverter<LocalDateTime, String> {\n\n    @Override\n    public String convertToDatabaseColumn(LocalDateTime localDateTime) {\n        if (localDateTime != null) {\n            return localDateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);\n        } else {\n            return null;\n        }\n    }\n\n    @Override\n    public LocalDateTime convertToEntityAttribute(String s) {\n\n        if (s != null) {\n            try {\n                return LocalDateTime.parse(s, DateTimeFormatter.ISO_LOCAL_DATE_TIME);\n            } catch (DateTimeParseException e) {\n                throw new RuntimeException(\n                        \"Database date & time format must be ISO-8601 compliant ( yyyy-mm-ddThh:mm:ss )\", e);\n            }\n        } else {\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/bdm/DateConverter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm;\n\nimport java.time.LocalDate;\nimport java.time.format.DateTimeFormatter;\nimport java.time.format.DateTimeParseException;\n\nimport javax.persistence.AttributeConverter;\nimport javax.persistence.Converter;\n\n/**\n * @author Danila Mazour\n * @deprecated {@link org.bonitasoft.engine.business.data.generator.DateConverter} is now used. Keep this class for\n *             backward runtime compatibility\n */\n@Deprecated\n@Converter\npublic class DateConverter implements AttributeConverter<LocalDate, String> {\n\n    @Override\n    public String convertToDatabaseColumn(LocalDate localDate) {\n\n        if (localDate != null) {\n            return localDate.toString();\n        } else {\n            return null;\n        }\n    }\n\n    @Override\n    public LocalDate convertToEntityAttribute(String s) {\n\n        if (s != null) {\n            try {\n                return LocalDate.parse(s, DateTimeFormatter.ISO_LOCAL_DATE);\n            } catch (DateTimeParseException e) {\n                throw new RuntimeException(\"Database date format must be ISO-8601 compliant ( yyyy-mm-dd )\", e);\n            }\n        } else {\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/bdm/OffsetDateTimeConverter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.bdm;\n\nimport java.time.OffsetDateTime;\nimport java.time.ZoneOffset;\nimport java.time.format.DateTimeFormatter;\nimport java.time.format.DateTimeParseException;\n\nimport javax.persistence.AttributeConverter;\n\n/**\n * @author Danila Mazour\n * @deprecated {@link org.bonitasoft.engine.business.data.generator.OffsetDateTimeConverter} is now used. Keep this\n *             class for backward runtime compatibility\n */\n@Deprecated\npublic class OffsetDateTimeConverter implements AttributeConverter<OffsetDateTime, String> {\n\n    @Override\n    public String convertToDatabaseColumn(OffsetDateTime offsetDateTime) {\n        if (offsetDateTime != null) {\n            return offsetDateTime.withOffsetSameInstant(ZoneOffset.UTC).format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);\n        } else {\n            return null;\n        }\n    }\n\n    @Override\n    public OffsetDateTime convertToEntityAttribute(String dbData) {\n        if (dbData != null) {\n            try {\n                return OffsetDateTime.parse(dbData, DateTimeFormatter.ISO_OFFSET_DATE_TIME);\n            } catch (DateTimeParseException e) {\n                throw new RuntimeException(\n                        \"Database OffsetDate&Time format must be ISO-8601 compliant yyyy-MM-dd'T'HH:mm:ss(.SSS)Z \", e);\n            }\n        } else {\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/DataRetentionBdmTrackingRepository.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.business.data.model.SDataRetentionBdmTracking;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.services.SPersistenceException;\n\n/**\n * Repository for CRUD operations on {@link SDataRetentionBdmTracking} entities.\n * <p>\n * Tracking records are stored in the Bonita DB and record the creation and last\n * modification timestamps of BDM object instances for data retention purposes.\n */\npublic interface DataRetentionBdmTrackingRepository {\n\n    /**\n     * Persists a new tracking record.\n     *\n     * @param tracking the tracking record to create\n     * @throws SPersistenceException if the insert fails\n     */\n    void create(SDataRetentionBdmTracking tracking) throws SPersistenceException;\n\n    /**\n     * Updates the {@code last_modified_at} column of an existing tracking record.\n     *\n     * @param tracking the tracking record whose {@code lastModifiedAt} has been set to the new value\n     * @throws SPersistenceException if the update fails\n     */\n    void updateLastModifiedDate(SDataRetentionBdmTracking tracking) throws SPersistenceException;\n\n    /**\n     * Finds a tracking record by BDM object identifier and class name.\n     *\n     * @param dataId the persistence ID of the tracked BDM object instance\n     * @param dataClassname the fully qualified class name of the BDM object type\n     * @return the matching tracking record, or {@code null} if not found\n     * @throws SBonitaReadException if the read operation fails\n     */\n    SDataRetentionBdmTracking getByDataIdAndClassname(long dataId, String dataClassname) throws SBonitaReadException;\n\n    /**\n     * Returns all tracking records for the given BDM class name.\n     *\n     * @param dataClassname the fully qualified class name of the BDM object type\n     * @return the list of matching tracking records, or an empty list if none found\n     * @throws SBonitaReadException if the read operation fails\n     */\n    List<SDataRetentionBdmTracking> getByClassname(String dataClassname) throws SBonitaReadException;\n\n    /**\n     * Returns all tracking records for the given BDM class name whose creation date\n     * is at or before the specified deadline.\n     *\n     * @param dataClassname the fully qualified class name of the BDM object type\n     * @param deadline epoch timestamp (milliseconds) — records with {@code createdAt <= deadline} are returned\n     * @return the list of expired tracking records, or an empty list if none found\n     * @throws SBonitaReadException if the read operation fails\n     */\n    List<SDataRetentionBdmTracking> getExpiredByCreatedDate(String dataClassname, long deadline)\n            throws SBonitaReadException;\n\n    /**\n     * Returns all tracking records for the given BDM class name whose last modification date\n     * is at or before the specified deadline.\n     *\n     * @param dataClassname the fully qualified class name of the BDM object type\n     * @param deadline epoch timestamp (milliseconds) — records with {@code lastModifiedAt <= deadline} are returned\n     * @return the list of expired tracking records, or an empty list if none found\n     * @throws SBonitaReadException if the read operation fails\n     */\n    List<SDataRetentionBdmTracking> getExpiredByLastModifiedDate(String dataClassname, long deadline)\n            throws SBonitaReadException;\n\n    /**\n     * Deletes a single tracking record.\n     *\n     * @param tracking the tracking record to delete\n     * @throws SPersistenceException if the delete operation fails\n     */\n    void delete(SDataRetentionBdmTracking tracking) throws SPersistenceException;\n\n    /**\n     * Deletes the tracking record matching the unique key ({@code dataId}, {@code dataClassname}).\n     *\n     * @param dataId the persistence ID of the tracked BDM object instance\n     * @param dataClassname the fully qualified class name of the BDM object type\n     * @return the number of rows deleted (0 if no matching record was found, 1 otherwise)\n     * @throws SPersistenceException if the delete operation fails\n     */\n    int delete(long dataId, String dataClassname) throws SPersistenceException;\n\n    /**\n     * Deletes all tracking records matching the given filter options.\n     *\n     * @param filterOptions the filter criteria to restrict the deletion.\n     *        <b>Warning:</b> passing an empty list deletes ALL tracking records\n     * @throws SPersistenceException if the delete operation fails\n     */\n    void deleteAll(List<FilterOption> filterOptions) throws SPersistenceException;\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/BdmFieldTypeConverter.java",
    "content": "/**\n * Copyright (C) 2025 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.OffsetDateTime;\nimport java.time.temporal.Temporal;\n\n/**\n * Utility class to convert a value to a specific type if compatible.\n * Notably, number types are converted to the requested type.\n * This class is used by the BDM insert / update methods called by the REST API, so types are converted from JSON,\n * which need specific treatment.\n *\n * @author Emmanuel Duchastenier\n */\npublic class BdmFieldTypeConverter {\n\n    @SuppressWarnings(\"unchecked\")\n    public static <T> T convert(final Object value, final Class<T> targetType) {\n        if (value == null) {\n            return null;\n        }\n        if (targetType.isInstance(value)) {\n            return targetType.cast(value);\n        }\n        if (Number.class.isAssignableFrom(targetType) && value instanceof Number numberValue) {\n            double doubleValue = numberValue.doubleValue();\n            if (targetType == Integer.class) {\n                return (T) Integer.valueOf((int) doubleValue);\n            } else if (targetType == Long.class) {\n                return (T) Long.valueOf((long) doubleValue);\n            } else if (targetType == Float.class) {\n                return (T) Float.valueOf((float) doubleValue);\n            } else if (targetType == Double.class) {\n                if (numberValue instanceof Float) {\n                    // to avoid having extra unwanted precision:\n                    return (T) Double.valueOf(numberValue.toString());\n                }\n                return (T) Double.valueOf(doubleValue);\n            } else if (targetType == Short.class) {\n                return (T) Short.valueOf((short) doubleValue);\n            } else if (targetType == Byte.class) {\n                return (T) Byte.valueOf((byte) doubleValue);\n            }\n        } else if (Temporal.class.isAssignableFrom(targetType) && value instanceof String) {\n            if (targetType == LocalDate.class) {\n                return (T) LocalDate.parse((String) value);\n            } else if (targetType == LocalDateTime.class) {\n                return (T) LocalDateTime.parse((String) value);\n            } else if (targetType == OffsetDateTime.class) {\n                return (T) OffsetDateTime.parse((String) value);\n            }\n        }\n        throw new IllegalArgumentException(\"Cannot convert \" + value.getClass() + \" to \" + targetType);\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/BusinessDataModelRepositoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport static org.apache.commons.lang3.StringUtils.strip;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.URL;\nimport java.security.MessageDigest;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport javax.xml.bind.JAXBException;\n\nimport lombok.RequiredArgsConstructor;\nimport lombok.SneakyThrows;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.io.filefilter.IOFileFilter;\nimport org.apache.commons.io.filefilter.SuffixFileFilter;\nimport org.apache.commons.lang3.exception.ExceptionUtils;\nimport org.bonitasoft.engine.bdm.BusinessObjectModelConverter;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.business.data.BusinessDataModelRepository;\nimport org.bonitasoft.engine.business.data.DataRetentionBdmTrackingService;\nimport org.bonitasoft.engine.business.data.InvalidBusinessDataModelException;\nimport org.bonitasoft.engine.business.data.SBusinessDataRepositoryDeploymentException;\nimport org.bonitasoft.engine.business.data.SBusinessDataRepositoryException;\nimport org.bonitasoft.engine.business.data.SDataRetentionBdmTrackingException;\nimport org.bonitasoft.engine.business.data.SchemaManager;\nimport org.bonitasoft.engine.business.data.generator.AbstractBDMJarBuilder;\nimport org.bonitasoft.engine.business.data.generator.BDMJarGenerationException;\nimport org.bonitasoft.engine.business.data.generator.client.ClientBDMJarBuilder;\nimport org.bonitasoft.engine.business.data.generator.client.ResourcesLoader;\nimport org.bonitasoft.engine.business.data.generator.filter.OnlyDAOImplementationFileFilter;\nimport org.bonitasoft.engine.business.data.generator.filter.WithoutDAOImplementationFileFilter;\nimport org.bonitasoft.engine.business.data.generator.server.ServerBDMCodeGenerator;\nimport org.bonitasoft.engine.business.data.generator.server.ServerBDMJarBuilder;\nimport org.bonitasoft.engine.classloader.ClassLoaderIdentifier;\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.classloader.SClassLoaderException;\nimport org.bonitasoft.engine.commons.io.IOUtil;\nimport org.bonitasoft.engine.dependency.SDependencyException;\nimport org.bonitasoft.engine.dependency.SDependencyNotFoundException;\nimport org.bonitasoft.engine.dependency.impl.TenantDependencyService;\nimport org.bonitasoft.engine.dependency.model.AbstractSDependency;\nimport org.bonitasoft.engine.dependency.model.ScopeType;\nimport org.bonitasoft.engine.io.IOUtils;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.platform.PlatformService;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.resources.STenantResource;\nimport org.bonitasoft.engine.resources.STenantResourceLight;\nimport org.bonitasoft.engine.resources.TenantResourceType;\nimport org.bonitasoft.engine.resources.TenantResourcesService;\nimport org.springframework.stereotype.Service;\nimport org.xml.sax.SAXException;\n\n/**\n * @author Colin PUY\n */\n@Slf4j\n@Service(\"businessDataModelRepository\")\n@RequiredArgsConstructor\npublic class BusinessDataModelRepositoryImpl implements BusinessDataModelRepository {\n\n    private static final String BDR_DEPENDENCY_NAME = \"BDR\";\n    public static final String BDR_DEPENDENCY_FILENAME = BDR_DEPENDENCY_NAME + \".jar\";\n    private static final String CLIENT_BDM_ZIP = \"client-bdm.zip\";\n    private static final String MODEL_JAR_NAME = \"bdm-model.jar\";\n    private static final String DAO_JAR_NAME = \"bdm-dao.jar\";\n    private static final String BOM_NAME = \"bom.zip\";\n\n    private final PlatformService platformService;\n    private final TenantDependencyService dependencyService;\n    private final ClassLoaderService classLoaderService;\n    private final SchemaManager schemaManager;\n    private final TenantResourcesService tenantResourcesService;\n    private final DataRetentionBdmTrackingService dataRetentionBdmTrackingService;\n\n    @Override\n    public byte[] getClientBDMZip() throws SBusinessDataRepositoryException {\n        STenantResource sTenantResource;\n        try {\n            sTenantResource = tenantResourcesService.get(TenantResourceType.BDM, CLIENT_BDM_ZIP);\n        } catch (SBonitaReadException e) {\n            throw new SBusinessDataRepositoryException(e);\n        }\n        if (sTenantResource == null) {\n            throw new SBusinessDataRepositoryException(\"no client-bdm.zip found in tenant resources\");\n        }\n        return sTenantResource.getContent();\n    }\n\n    @Override\n    public String getInstalledBDMVersion() throws SBusinessDataRepositoryException {\n        try {\n            Optional<Long> returnedId = dependencyService.getIdOfDependencyOfArtifactForTenant(BDR_DEPENDENCY_FILENAME);\n            if (returnedId.isPresent()) {\n                return String.valueOf(returnedId.get());\n            }\n        } catch (SBonitaReadException e) {\n            throw new SBusinessDataRepositoryException(e);\n        }\n        return null;\n    }\n\n    @Override\n    public BusinessObjectModel getBusinessObjectModel() throws SBusinessDataRepositoryException {\n        try {\n            byte[] clientBdmZip = getClientBDMZip();\n            final Map<String, byte[]> zipContent = IOUtils.unzip(clientBdmZip);\n            if (zipContent.containsKey(BOM_NAME)) {\n                final byte[] bomZip = zipContent.get(BOM_NAME);\n                return getBusinessObjectModel(bomZip);\n            }\n        } catch (SBusinessDataRepositoryException e) {\n            if (isBDMDeployed()) {\n                // This is not a problem of BDM not deployed, let exception go up:\n                throw e;\n            }\n        } catch (IOException e) {\n            throw new SBusinessDataRepositoryException(e);\n        } catch (InvalidBusinessDataModelException e) {\n            throw new SBusinessDataRepositoryDeploymentException(\"BDM zip file is invalid in database.\", e);\n        }\n        return null;\n    }\n\n    @Override\n    public boolean isBDMDeployed() {\n        try {\n            return dependencyService.getIdOfDependencyOfArtifactForTenant(BDR_DEPENDENCY_FILENAME).isPresent();\n        } catch (SBonitaReadException e) {\n            return false;\n        }\n    }\n\n    @Override\n    public String install(final byte[] bdmZip, long userId)\n            throws SBusinessDataRepositoryDeploymentException, InvalidBusinessDataModelException {\n        final BusinessObjectModel model = getBusinessObjectModel(bdmZip);\n\n        createAndDeployClientBDMZip(model, userId);\n        final long bdmVersion = createAndDeployServerBDMJar(model);\n        return String.valueOf(bdmVersion);\n    }\n\n    protected long createAndDeployServerBDMJar(final BusinessObjectModel model)\n            throws SBusinessDataRepositoryDeploymentException {\n        final byte[] serverBdmJar = generateServerBDMJar(model);\n        try {\n            final AbstractSDependency mappedDependency = dependencyService.createMappedDependency(BDR_DEPENDENCY_NAME,\n                    serverBdmJar, BDR_DEPENDENCY_FILENAME, -1L, ScopeType.TENANT);\n            //refresh classloader now, it is used to update the schema\n            ClassLoaderIdentifier tenantClassLoader = ClassLoaderIdentifier.TENANT;\n            classLoaderService.refreshClassLoaderImmediatelyWithRollback(tenantClassLoader);\n            classLoaderService.refreshClassLoaderOnOtherNodes(tenantClassLoader);\n            //replace the tenant classloader by the one that was just refreshed\n            Thread.currentThread().setContextClassLoader(classLoaderService.getClassLoader(tenantClassLoader));\n            update(model.getBusinessObjectsClassNames());\n            return mappedDependency.getId();\n        } catch (final SDependencyException | SClassLoaderException e) {\n            throw new SBusinessDataRepositoryDeploymentException(e);\n        }\n    }\n\n    protected void update(final Set<String> annotatedClassNames) throws SBusinessDataRepositoryDeploymentException {\n        final List<Exception> exceptions = schemaManager.update(annotatedClassNames);\n        if (!exceptions.isEmpty()) {\n            throw new SBusinessDataRepositoryDeploymentException(\n                    \"Updating schema failed.\\n\" + convertExceptions(exceptions));\n        }\n    }\n\n    String convertExceptions(List<Exception> exceptions) {\n        StringBuilder exceptionMessage = new StringBuilder(\"Error(s) encountered:\");\n        int counter = 1;\n        for (Throwable exception : exceptions) {\n            exceptionMessage.append(\"\\n\").append(counter++).append(\": \")\n                    .append(\n                            ExceptionUtils.getThrowableList(exception).stream()\n                                    .map(throwable -> strip(throwable.toString(), \"\\n\")) // to filter out empty lines\n                                    .collect(Collectors.joining(\"\\ncaused by \")));\n        }\n        return exceptionMessage.toString();\n    }\n\n    void createAndDeployClientBDMZip(final BusinessObjectModel model, long userId)\n            throws SBusinessDataRepositoryDeploymentException {\n        STenantResourceLight accessControl;\n        try {\n            accessControl = tenantResourcesService.getSingleLightResource(TenantResourceType.BDM_ACCESS_CTRL);\n        } catch (SBonitaReadException e) {\n            throw new SBusinessDataRepositoryDeploymentException(e);\n        }\n        if (accessControl != null) {\n            throw new SBusinessDataRepositoryDeploymentException(\n                    \"A BDM Access Control file is installed. Uninstall it before deploying the BDM\");\n        }\n        try {\n            tenantResourcesService.add(CLIENT_BDM_ZIP, TenantResourceType.BDM, generateClientBDMZip(model), userId);\n        } catch (IOException | SRecorderException e) {\n            throw new SBusinessDataRepositoryDeploymentException(e);\n        }\n    }\n\n    protected BusinessObjectModel getBusinessObjectModel(final byte[] bdmZip)\n            throws InvalidBusinessDataModelException {\n        final BusinessObjectModelConverter converter = new BusinessObjectModelConverter();\n        try {\n            return converter.unzip(bdmZip);\n        } catch (IOException | SAXException | JAXBException e) {\n            throw new InvalidBusinessDataModelException(e);\n        }\n    }\n\n    protected byte[] generateServerBDMJar(final BusinessObjectModel model)\n            throws SBusinessDataRepositoryDeploymentException {\n        return generateServerBDMJar(model, true);\n    }\n\n    protected byte[] generateServerBDMJar(final BusinessObjectModel model, boolean validateRuntimeClasses)\n            throws SBusinessDataRepositoryDeploymentException {\n        var generator = new ServerBDMCodeGenerator();\n        if (!validateRuntimeClasses) {\n            generator.disableRuntimeClassesValidation();\n        }\n        final AbstractBDMJarBuilder builder = new ServerBDMJarBuilder(generator);\n        final IOFileFilter classFileAndXmlFileFilter = new SuffixFileFilter(Arrays.asList(\".class\", \".xml\"));\n        try {\n            // Force productVersion to current platform version.\n            // The bom.xml file stored in the generated jar will have the proper product version.\n            // Thus, when comparing these files, a BDM update will be forced if the platform version has changed\n            // between the existing server jar and the new one.\n            model.setProductVersion(platformService.getSPlatformProperties().getPlatformVersion());\n            return builder.build(model, classFileAndXmlFileFilter);\n        } catch (BDMJarGenerationException e) {\n            throw new SBusinessDataRepositoryDeploymentException(e);\n        }\n    }\n\n    protected byte[] generateClientBDMZip(final BusinessObjectModel model)\n            throws SBusinessDataRepositoryDeploymentException, IOException {\n        AbstractBDMJarBuilder builder = new ClientBDMJarBuilder(new ResourcesLoader());\n\n        final Map<String, byte[]> resources = new HashMap<>();\n\n        try {\n            // Build jar with Model\n            byte[] modelJarContent = builder.build(model, new WithoutDAOImplementationFileFilter());\n            resources.put(MODEL_JAR_NAME, modelJarContent);\n\n            // Build jar with DAO\n            builder = new ClientBDMJarBuilder(new ResourcesLoader());\n            final byte[] daoJarContent = builder.build(model, new OnlyDAOImplementationFileFilter());\n            resources.put(DAO_JAR_NAME, daoJarContent);\n        } catch (BDMJarGenerationException e1) {\n            throw new SBusinessDataRepositoryDeploymentException(e1);\n        }\n\n        //Add bom.xml\n        try {\n            resources.put(BOM_NAME, new BusinessObjectModelConverter().zip(model));\n        } catch (final JAXBException | SAXException e) {\n            throw new SBusinessDataRepositoryDeploymentException(e);\n        }\n\n        putResourceFromClassPath(resources, \"example-pom.xml\");\n        putResourceFromClassPath(resources, \"README.md\");\n\n        return IOUtil.generateZip(resources);\n    }\n\n    private void putResourceFromClassPath(Map<String, byte[]> resources, String name) throws IOException {\n        try (InputStream resource = BusinessDataModelRepositoryImpl.class.getResourceAsStream(\"/\" + name)) {\n            resources.put(name, IOUtil.getAllContentFrom(resource));\n        }\n    }\n\n    @Override\n    public void uninstall() throws SBusinessDataRepositoryException {\n        try {\n            dependencyService.deleteDependency(BDR_DEPENDENCY_NAME);\n            classLoaderService.refreshClassLoaderImmediatelyWithRollback(ClassLoaderIdentifier.TENANT);\n            ClassLoader classLoader = classLoaderService.getClassLoader(ClassLoaderIdentifier.TENANT);\n            Thread.currentThread()\n                    .setContextClassLoader(classLoader);\n        } catch (final SDependencyNotFoundException sde) {\n            // do nothing\n        } catch (final SDependencyException | SClassLoaderException e) {\n            throw new SBusinessDataRepositoryException(e);\n        }\n        try {\n            STenantResource clientBDMZip = tenantResourcesService.get(TenantResourceType.BDM, CLIENT_BDM_ZIP);\n            if (clientBDMZip != null) {\n                tenantResourcesService.remove(clientBDMZip);\n            }\n        } catch (SBonitaReadException | SRecorderException e) {\n            throw new SBusinessDataRepositoryException(e);\n        }\n    }\n\n    @Override\n    public void dropAndUninstall() throws SBusinessDataRepositoryException {\n        final URL resource = Thread.currentThread().getContextClassLoader().getResource(\"bom.xml\");\n        if (resource != null) {\n            try {\n                final byte[] content = IOUtil.getAllContentFrom(resource);\n                final BusinessObjectModel model = new BusinessObjectModelConverter().unmarshall(content);\n                final List<Exception> exceptions = schemaManager.drop(model.getBusinessObjectsClassNames());\n                if (!exceptions.isEmpty()) {\n                    if (exceptions.size() == 1) {\n                        throw new SBusinessDataRepositoryDeploymentException(\"Drop of the schema failed.\",\n                                exceptions.get(0));\n                    } else {\n                        throw new SBusinessDataRepositoryDeploymentException(\n                                \"Drop of the schema failed due multiple exceptions: \" + exceptions, exceptions.get(0));\n                    }\n                }\n                uninstall();\n                deleteAllBdmTracking();\n            } catch (final IOException | JAXBException | SAXException ioe) {\n                throw new SBusinessDataRepositoryException(ioe);\n            }\n        }\n    }\n\n    /**\n     * Deletes all BDM tracking records when the BDM is uninstalled.\n     * Tracking data becomes obsolete once the BDM schema is dropped.\n     */\n    private void deleteAllBdmTracking() throws SBusinessDataRepositoryException {\n        try {\n            // Delete all records without any restriction\n            dataRetentionBdmTrackingService.deleteAll();\n        } catch (SDataRetentionBdmTrackingException e) {\n            throw new SBusinessDataRepositoryException(\n                    \"Failed to delete data retention BDM tracking records during BDM uninstall\", e);\n        }\n    }\n\n    @Override\n    public boolean isDeployed(byte[] bdmArchive)\n            throws InvalidBusinessDataModelException, SBusinessDataRepositoryDeploymentException {\n        try {\n            var bdmDependencyId = dependencyService.getIdOfDependencyOfArtifactForTenant(BDR_DEPENDENCY_FILENAME);\n            if (bdmDependencyId.isEmpty()) {\n                log.debug(\"No BDM currently deployed.\");\n                return false;\n            }\n            var existingSha3 = sha256ToHex(dependencyService.getDependency(bdmDependencyId.get()).getValue());\n            var newServerJar = generateServerBDMJar(getBusinessObjectModel(bdmArchive), false);\n            var newSha3 = sha256ToHex(newServerJar);\n            log.debug(\"BDM binary sha3256 comparison: current={}, new={}\", existingSha3, newSha3);\n            return Objects.equals(existingSha3, newSha3);\n        } catch (SDependencyNotFoundException e) {\n            log.error(\"Failed to retrieve existing bdm dependency\", e);\n            return false;\n        } catch (SBonitaReadException e) {\n            log.error(\"Failed to retrieve {} dependency\", BDR_DEPENDENCY_FILENAME, e);\n            return false;\n        }\n    }\n\n    @SneakyThrows\n    private static String sha256ToHex(byte[] source) {\n        final MessageDigest digest = MessageDigest.getInstance(\"SHA3-256\");\n        return bytesToHex(digest.digest(source));\n    }\n\n    private static String bytesToHex(byte[] hash) {\n        StringBuilder hexString = new StringBuilder(2 * hash.length);\n        for (byte b : hash) {\n            hexString.append(String.format(\"%02x\", b));\n        }\n        return hexString.toString();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/BusinessDataReloader.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.bonitasoft.engine.business.data.BusinessDataRepository;\nimport org.bonitasoft.engine.business.data.SBusinessDataNotFoundException;\nimport org.bonitasoft.engine.business.data.proxy.ServerProxyfier;\nimport org.hibernate.proxy.HibernateProxyHelper;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class BusinessDataReloader {\n\n    private final BusinessDataRepository businessDataRepository;\n\n    public BusinessDataReloader(BusinessDataRepository businessDataRepository) {\n        this.businessDataRepository = businessDataRepository;\n    }\n\n    /**\n     * Reloads the {@link Entity} from database using the current {@code Entity} className and persistenceId\n     *\n     * @param entityToReload the entity to be reloaded\n     * @return the {@link Entity} reload from the database\n     * @throws SBusinessDataNotFoundException\n     */\n    public Entity reloadEntity(Entity entityToReload) throws SBusinessDataNotFoundException {\n        final Class realClass = getEntityRealClass(entityToReload);\n        return businessDataRepository.findById(realClass, entityToReload.getPersistenceId());\n    }\n\n    public Class getEntityRealClass(Entity entity) {\n        return HibernateProxyHelper.getClassWithoutInitializingProxy(ServerProxyfier.unProxifyIfNeeded(entity));\n    }\n\n    /**\n     * Reloads the {@link Entity} from database using the current {@code Entity} className and persistenceId if\n     * persistenceId is set. Otherwise returns the\n     * object itself.\n     *\n     * @param entityToReload the entity to be reloaded\n     * @return the {@link Entity} reload from the database if the persistenceId is set or the object itself.\n     * @throws SBusinessDataNotFoundException\n     */\n    public Entity reloadEntitySoftly(Entity entityToReload) throws SBusinessDataNotFoundException {\n        if (entityToReload.getPersistenceId() == null) {\n            return entityToReload;\n        }\n        return reloadEntity(entityToReload);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/BusinessDataRepositoryEventAspect.java",
    "content": "/**\n * Copyright (C) 2025 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport org.aspectj.lang.ProceedingJoinPoint;\nimport org.aspectj.lang.annotation.AfterReturning;\nimport org.aspectj.lang.annotation.Around;\nimport org.aspectj.lang.annotation.Aspect;\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.events.model.SDeleteEvent;\nimport org.bonitasoft.engine.events.model.SEvent;\nimport org.bonitasoft.engine.events.model.SFireEventException;\nimport org.bonitasoft.engine.events.model.SInsertEvent;\nimport org.bonitasoft.engine.events.model.SUpdateEvent;\nimport org.springframework.stereotype.Component;\n\n/**\n * Aspect to handle events for business data repository operations.\n * Fires events on persist, merge, and remove operations.\n * This aspect is enabled in EngineConfiguration class through annotation @EnableAspectJAutoProxy.\n */\n@Aspect\n@Component\npublic class BusinessDataRepositoryEventAspect {\n\n    private final EventService eventService;\n\n    public BusinessDataRepositoryEventAspect(EventService eventService) {\n        this.eventService = eventService;\n    }\n\n    @Around(\"execution(* org.bonitasoft.engine.business.data.BusinessDataRepository.persist(..)) && args(entity)\")\n    public Object aroundPersist(ProceedingJoinPoint target, Entity entity) throws Throwable {\n        return persistOrMerge(target, entity);\n    }\n\n    @Around(\"execution(* org.bonitasoft.engine.business.data.BusinessDataRepository.merge(..)) && args(entity)\")\n    public Object aroundMerge(ProceedingJoinPoint target, Entity entity) throws Throwable {\n        return persistOrMerge(target, entity);\n    }\n\n    private Object persistOrMerge(ProceedingJoinPoint target, Entity entity) throws Throwable {\n        if (entity != null) {\n            var originalPersistenceId = entity.getPersistenceId();\n            Object result = target.proceed();\n            var event = (originalPersistenceId == null) ? new SInsertEvent(getEventType(SEvent.CREATED))\n                    : new SUpdateEvent(getEventType(SEvent.UPDATED));\n            event.setObject(entity);\n            eventService.fireEvent(event);\n            return result;\n        }\n        return null;\n    }\n\n    @AfterReturning(\"execution(* org.bonitasoft.engine.business.data.BusinessDataRepository.remove(..)) && args(entity)\")\n    public void afterRemove(Entity entity) throws SFireEventException {\n        fireDeleteEvent(entity);\n    }\n\n    @AfterReturning(pointcut = \"execution(* org.bonitasoft.engine.business.data.BusinessDataRepository.removeById(..))\", returning = \"entity\")\n    public void afterRemoveById(Entity entity) throws SFireEventException {\n        fireDeleteEvent(entity);\n    }\n\n    private void fireDeleteEvent(Entity entity) throws SFireEventException {\n        var event = new SDeleteEvent(getEventType(SEvent.DELETED));\n        event.setObject(entity);\n        eventService.fireEvent(event);\n    }\n\n    private static String getEventType(String type) {\n        return \"BUSINESS_DATA\" + type;\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/BusinessDataServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport static java.lang.String.format;\n\nimport java.io.Serializable;\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.ParameterizedType;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport javax.persistence.CascadeType;\nimport javax.persistence.ManyToMany;\nimport javax.persistence.ManyToOne;\nimport javax.persistence.NamedQueries;\nimport javax.persistence.NamedQuery;\nimport javax.persistence.OneToMany;\nimport javax.persistence.OneToOne;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.bonitasoft.engine.bdm.BDMQueryUtil;\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.model.Query;\nimport org.bonitasoft.engine.bdm.model.QueryParameter;\nimport org.bonitasoft.engine.bdm.model.field.RelationField.Type;\nimport org.bonitasoft.engine.bpm.businessdata.BusinessDataQueryResult;\nimport org.bonitasoft.engine.bpm.businessdata.impl.BusinessDataQueryMetadataImpl;\nimport org.bonitasoft.engine.bpm.businessdata.impl.BusinessDataQueryResultImpl;\nimport org.bonitasoft.engine.business.data.BusinessDataModelRepository;\nimport org.bonitasoft.engine.business.data.BusinessDataRepository;\nimport org.bonitasoft.engine.business.data.BusinessDataService;\nimport org.bonitasoft.engine.business.data.JsonBusinessDataSerializer;\nimport org.bonitasoft.engine.business.data.NonUniqueResultException;\nimport org.bonitasoft.engine.business.data.SBusinessDataNotFoundException;\nimport org.bonitasoft.engine.business.data.SBusinessDataRepositoryException;\nimport org.bonitasoft.engine.commons.ClassReflector;\nimport org.bonitasoft.engine.commons.JavaMethodInvoker;\nimport org.bonitasoft.engine.commons.TypeConverterUtil;\nimport org.bonitasoft.engine.commons.exceptions.SReflectException;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;\nimport org.springframework.stereotype.Service;\n\n/**\n * Orchestrates Business Data Model (BDM) operations and JSON serialization for REST APIs.\n * <p>\n * This service acts as the main facade for business data operations, coordinating between:\n * <ul>\n * <li>{@link BusinessDataRepository} for entity persistence and query execution</li>\n * <li>{@link JsonBusinessDataSerializer} for JSON serialization with standard/legacy shape support</li>\n * <li>{@link BusinessDataModelRepository} for BDM metadata and query definitions</li>\n * </ul>\n * <p>\n * Key responsibilities:\n * <ul>\n * <li>Execute named queries and serialize results to JSON (via {@link #getJsonQueryEntities})</li>\n * <li>Retrieve entities by ID and serialize them (via {@link #getJsonEntity}, {@link #getJsonEntities})</li>\n * <li>Handle Java operations on entities (setters/getters) with aggregation/composition relationship management</li>\n * <li>Validate query parameters against BDM query definitions</li>\n * <li>Provide count metadata for paginated query results</li>\n * <li>Determine which JSON serialization shape to use (standard vs legacy) based on configuration</li>\n * </ul>\n *\n * @see BusinessDataService\n * @see JsonBusinessDataSerializerImpl for JSON serialization details\n */\n@Service\n@ConditionalOnSingleCandidate(BusinessDataService.class)\npublic class BusinessDataServiceImpl implements BusinessDataService {\n\n    protected final BusinessDataRepository businessDataRepository;\n\n    private final JsonBusinessDataSerializer jsonBusinessDataSerializer;\n\n    protected final BusinessDataModelRepository businessDataModelRepository;\n\n    private final TypeConverterUtil typeConverterUtil;\n\n    private final BusinessDataReloader businessDataReloader;\n\n    private final CountQueryProvider countQueryProvider;\n\n    public BusinessDataServiceImpl(final BusinessDataRepository businessDataRepository,\n            final JsonBusinessDataSerializer jsonBusinessDataSerializer,\n            final BusinessDataModelRepository businessDataModelRepository, final TypeConverterUtil typeConverterUtil,\n            BusinessDataReloader businessDataReloader, CountQueryProvider countQueryProvider) {\n        this.businessDataRepository = businessDataRepository;\n        this.jsonBusinessDataSerializer = jsonBusinessDataSerializer;\n        this.businessDataModelRepository = businessDataModelRepository;\n        this.typeConverterUtil = typeConverterUtil;\n        this.businessDataReloader = businessDataReloader;\n        this.countQueryProvider = countQueryProvider;\n    }\n\n    @Override\n    public boolean isBusinessData(final Object data) {\n        return isEntity(data) || isListOfEntities(data);\n    }\n\n    private boolean isListOfEntities(final Object data) {\n        if (data == null) {\n            return false;\n        }\n        if (!List.class.isAssignableFrom(data.getClass())) {\n            return false;\n        }\n        @SuppressWarnings(\"rawtypes\")\n        final List dataList = (List) data;\n        if (dataList.isEmpty()) {\n            return true;\n        }\n        return isEntity(dataList.get(0));\n    }\n\n    private boolean isEntity(final Object data) {\n        if (data == null) {\n            return false;\n        }\n        return Entity.class.isAssignableFrom(data.getClass());\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public Object callJavaOperation(final Object businessObject, final Object valueToSetObjectWith,\n            final String methodName, final String parameterType)\n            throws SBusinessDataNotFoundException, SBusinessDataRepositoryException {\n        if (businessObject == null) {\n            throw new SBusinessDataNotFoundException(\"business data is null\");\n        }\n        if (isEntity(businessObject)) {\n            return callJavaOperationOnEntity((Entity) businessObject, valueToSetObjectWith, methodName, parameterType);\n        }\n        if (isListOfEntities(businessObject)) {\n            return callJavaOperationOnEntityList((List<Entity>) businessObject, valueToSetObjectWith, methodName,\n                    parameterType);\n        }\n        throw new SBusinessDataRepositoryException(\"not a business data\");\n    }\n\n    private Object callJavaOperationOnEntityList(final List<Entity> businessObject, final Object valueToSetObjectWith,\n            final String methodName,\n            final String parameterType)\n            throws SBusinessDataRepositoryException, SBusinessDataNotFoundException {\n        try {\n            invokeJavaMethod(businessObject, methodName, parameterType, valueToSetObjectWith);\n            return businessObject;\n        } catch (final Exception e) {\n            throw new SBusinessDataRepositoryException(e);\n        }\n    }\n\n    private Object callJavaOperationOnEntity(final Entity businessObject, final Object valueToSetObjectWith,\n            final String methodName,\n            final String parameterType)\n            throws SBusinessDataRepositoryException, SBusinessDataNotFoundException {\n        Entity jpaEntity = businessDataReloader.reloadEntitySoftly(businessObject);\n        final Object valueToSet = loadValueToSet(businessObject, valueToSetObjectWith, methodName);\n        try {\n            invokeJavaMethod(jpaEntity, methodName, parameterType, valueToSet);\n            // TODO Auto-generated method stub\n            return jpaEntity;\n        } catch (final Exception e) {\n            throw new SBusinessDataRepositoryException(e);\n        }\n    }\n\n    protected void invokeJavaMethod(final Object objectToSet, final String methodName, final String parameterType,\n            final Object valueToSet)\n            throws ClassNotFoundException,\n            NoSuchMethodException, IllegalAccessException, InvocationTargetException {\n        final JavaMethodInvoker methodInvoker = new JavaMethodInvoker();\n        methodInvoker.invokeJavaMethod(parameterType, valueToSet, objectToSet, methodName, parameterType);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private Object loadValueToSet(final Entity businessObject, final Object valueToSetObjectWith,\n            final String methodName)\n            throws SBusinessDataNotFoundException, SBusinessDataRepositoryException {\n        Object valueToSet;\n        if (isEntity(valueToSetObjectWith)) {\n            final Type relationType = getRelationType(businessObject, methodName);\n            valueToSet = getPersistedValue((Entity) valueToSetObjectWith, relationType);\n        } else if (isListOfEntities(valueToSetObjectWith)) {\n            final Type relationType = getRelationType(businessObject, methodName);\n            valueToSet = getPersistedValues((List<Entity>) valueToSetObjectWith, relationType);\n        } else {\n            valueToSet = valueToSetObjectWith;\n        }\n        return valueToSet;\n    }\n\n    private List<Long> getPrimaryKeys(final List<Entity> entities) throws SBusinessDataNotFoundException {\n        List<Long> primaryKeys;\n        primaryKeys = new ArrayList<>();\n        for (final Entity entity : entities) {\n            if (entity.getPersistenceId() == null) {\n                throw new SBusinessDataNotFoundException(format(\n                        \"Forbidden instance of %s found. It is only possible to reference persisted instances in an aggregation relation.\",\n                        businessDataReloader.getEntityRealClass(entity).getName()));\n            }\n            primaryKeys.add(entity.getPersistenceId());\n        }\n        return primaryKeys;\n    }\n\n    private Object getPersistedValues(final List<Entity> entities, final Type type)\n            throws SBusinessDataNotFoundException {\n        if (entities.isEmpty()) {\n            return new ArrayList<Entity>();\n        }\n        if (Type.AGGREGATION.equals(type)) {\n            return businessDataRepository.findByIds(businessDataReloader.getEntityRealClass(entities.get(0)),\n                    getPrimaryKeys(entities));\n        } else {\n            return entities;\n        }\n    }\n\n    private Entity getPersistedValue(final Entity entity, final Type type) throws SBusinessDataNotFoundException {\n        if (Type.AGGREGATION.equals(type)) {\n            try {\n                return businessDataReloader.reloadEntity(entity);\n            } catch (SBusinessDataNotFoundException e) {\n                throw new SBusinessDataNotFoundException(format(\n                        \"Forbidden instance of %s found. It is only possible to reference persisted instances in an aggregation relation.\",\n                        businessDataReloader.getEntityRealClass(entity).getName()), e);\n            }\n        } else {\n            return entity;\n        }\n    }\n\n    private Type getRelationType(final Entity businessObject, final String methodName)\n            throws SBusinessDataRepositoryException {\n        final String fieldName = ClassReflector.getFieldName(methodName);\n        Annotation[] annotations;\n        try {\n            annotations = businessObject.getClass().getDeclaredField(fieldName).getAnnotations();\n        } catch (final NoSuchFieldException e) {\n            return null;\n        } catch (final SecurityException e) {\n            throw new SBusinessDataRepositoryException(e);\n        }\n        for (final Annotation annotation : annotations) {\n            final Set<Class<? extends Annotation>> annotationKeySet = getAnnotationKeySet();\n            if (annotationKeySet.contains(annotation.annotationType())) {\n                try {\n                    final Method cascade = annotation.getClass().getMethod(\"cascade\");\n                    final CascadeType[] cascadeTypes = (CascadeType[]) cascade.invoke(annotation);\n                    if (CascadeType.MERGE.equals(cascadeTypes[0])) {\n                        return Type.AGGREGATION;\n                    }\n                    if (CascadeType.ALL.equals(cascadeTypes[0])) {\n                        return Type.COMPOSITION;\n                    }\n                } catch (final Exception e) {\n                    throw new SBusinessDataRepositoryException(e);\n                }\n            }\n        }\n        return null;\n    }\n\n    private Set<Class<? extends Annotation>> getAnnotationKeySet() {\n        // FIXME use custom annotation on methods\n        final Set<Class<? extends Annotation>> annotationKeySet = new HashSet<>();\n        annotationKeySet.add(OneToOne.class);\n        annotationKeySet.add(OneToMany.class);\n        annotationKeySet.add(ManyToMany.class);\n        annotationKeySet.add(ManyToOne.class);\n        return annotationKeySet;\n    }\n\n    @Override\n    public Serializable getJsonEntity(final String entityClassName, final Long identifier,\n            final String businessDataURIPattern)\n            throws SBusinessDataNotFoundException, SBusinessDataRepositoryException {\n        final Class<? extends Entity> entityClass = loadClass(entityClassName);\n        final Entity entity = businessDataRepository.findById(entityClass, identifier);\n        return jsonBusinessDataSerializer.serializeEntity(entity, businessDataURIPattern);\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public Serializable getJsonEntities(final String entityClassName, final List<Long> identifiers,\n            final String businessDataURIPattern)\n            throws SBusinessDataRepositoryException {\n        final Class<? extends Entity> entityClass = loadClass(entityClassName);\n        final List<? extends Entity> entities = businessDataRepository.findByIdentifiers(entityClass, identifiers);\n        return jsonBusinessDataSerializer.serializeEntities(entities, businessDataURIPattern);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public Serializable getJsonChildEntity(final String entityClassName, final Long identifier,\n            final String childFieldName,\n            final String businessDataURIPattern)\n            throws SBusinessDataNotFoundException, SBusinessDataRepositoryException {\n        final Class<? extends Entity> entityClass = loadClass(entityClassName);\n        final Object entity = businessDataRepository.findById(entityClass, identifier);\n\n        Object childEntity;\n        java.lang.reflect.Type getterReturnType;\n        try {\n            final String getterName = ClassReflector.getGetterName(childFieldName);\n            childEntity = ClassReflector.invokeGetter(entity, getterName);\n            getterReturnType = ClassReflector.getGetterReturnType(entityClass, getterName);\n        } catch (final SReflectException e) {\n            throw new SBusinessDataRepositoryException(e);\n        }\n\n        if (childEntity == null) {\n            return JsonBusinessDataSerializer.EMPTY_OBJECT;\n        }\n        if (childEntity instanceof Entity) {\n            final Entity unwrap = businessDataRepository.unwrap((Entity) childEntity);\n            return jsonBusinessDataSerializer.serializeEntity(unwrap, businessDataURIPattern);\n        } else if (childEntity instanceof List) {\n            final Class<?> type = (Class<?>) ((ParameterizedType) getterReturnType).getActualTypeArguments()[0];\n            if (Entity.class.isAssignableFrom(type)) {\n                return jsonBusinessDataSerializer.serializeEntities((List<Entity>) childEntity, businessDataURIPattern);\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public BusinessDataQueryResult getJsonQueryEntities(final String entityClassName, final String queryName,\n            final Map<String, Serializable> parameters,\n            final Integer startIndex, final Integer maxResults, final String businessDataURIPattern)\n            throws SBusinessDataRepositoryException {\n        final Class<? extends Entity> businessDataClass = loadClass(entityClassName);\n        BusinessObject businessObject = getBusinessObjectFromClassName(entityClassName);\n        final Query queryDefinition = getQueryDefinition(businessObject, entityClassName, queryName);\n        final Map<String, Serializable> queryParameters = getQueryParameters(queryDefinition, parameters);\n        final List<? extends Serializable> list = businessDataRepository.findListByNamedQuery(\n                getQualifiedQueryName(businessDataClass, queryName),\n                getQueryReturnType(queryDefinition, entityClassName), queryParameters, startIndex, maxResults);\n\n        BusinessDataQueryMetadataImpl businessDataQueryMetadata = null;\n        final Query countQueryDefinition = getCountQueryDefinition(businessDataClass, businessObject, queryDefinition);\n        if (countQueryDefinition != null) {\n            try {\n                businessDataQueryMetadata = new BusinessDataQueryMetadataImpl(startIndex, maxResults,\n                        (Long) businessDataRepository.findByNamedQuery(\n                                getQualifiedQueryName(businessDataClass, countQueryDefinition.getName()),\n                                getQueryReturnType(countQueryDefinition, entityClassName), queryParameters));\n            } catch (NonUniqueResultException e) {\n                throw new SBusinessDataRepositoryException(\"unable to count results for query \" + queryName);\n            }\n        }\n        Serializable jsonResults;\n\n        boolean useStandardShape = jsonBusinessDataSerializer.isStandardShapeEnabled();\n        if (isScalarQuery(queryDefinition)) { // SCALAR QUERY (Double, Float, Integer, Long)\n            jsonResults = jsonBusinessDataSerializer.serializeScalarResult(list, entityClassName, useStandardShape);\n\n        } else { // ENTITY QUERY (List or single entity)\n            jsonResults = jsonBusinessDataSerializer.serializeEntityQueryResult((List<Entity>) list,\n                    businessDataURIPattern, useStandardShape, queryDefinition.hasMultipleResults());\n        }\n\n        return new BusinessDataQueryResultImpl(jsonResults, businessDataQueryMetadata);\n    }\n\n    private Query getCountQueryDefinition(Class<? extends Entity> businessDataClass, BusinessObject businessObject,\n            Query queryDefinition) {\n        final Query countQueryDefinition = countQueryProvider.getCountQueryDefinition(businessObject, queryDefinition);\n        if (ensureQueryIsDefinedInEntity(businessDataClass, countQueryDefinition)) {\n            return countQueryDefinition;\n        }\n        return null;\n    }\n\n    private boolean isScalarQuery(Query queryDefinition) {\n        String type = queryDefinition.getReturnType();\n        if (type == null) {\n            return false;\n        }\n        return type.equals(\"java.lang.Double\")\n                || type.equals(\"java.lang.Integer\")\n                || type.equals(\"java.lang.Float\")\n                || type.equals(\"java.lang.Long\"); // Includes COUNT queries and other Long scalar queries\n    }\n\n    private boolean ensureQueryIsDefinedInEntity(Class<? extends Entity> businessDataClass,\n            Query countQueryDefinition) {\n        final NamedQueries namedQueries = businessDataClass.getAnnotation(NamedQueries.class);\n        if (namedQueries == null || countQueryDefinition == null) {\n            return false;\n        }\n        for (NamedQuery namedQuery : namedQueries.value()) {\n            if (namedQuery.name().equals(getQualifiedQueryName(businessDataClass, countQueryDefinition.getName()))) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    private Class<? extends Serializable> getQueryReturnType(final Query queryDefinition, final String entityClassName)\n            throws SBusinessDataRepositoryException {\n        if (queryDefinition.hasMultipleResults()) {\n            return loadClass(entityClassName);\n        }\n        try {\n            return (Class<? extends Serializable>) Thread.currentThread().getContextClassLoader()\n                    .loadClass(queryDefinition.getReturnType());\n        } catch (final ClassNotFoundException e) {\n            throw new SBusinessDataRepositoryException(\"unable to load class \" + queryDefinition.getReturnType());\n        }\n    }\n\n    private String getQualifiedQueryName(final Class<? extends Entity> businessDataClass, final String queryName) {\n        return format(\"%s.%s\", businessDataClass.getSimpleName(), queryName);\n    }\n\n    private Map<String, Serializable> getQueryParameters(final Query queryDefinition,\n            final Map<String, Serializable> parameters)\n            throws SBusinessDataRepositoryException {\n        final Set<String> errors = new HashSet<>();\n        final Map<String, Serializable> queryParameters = new HashMap<>();\n        for (final QueryParameter queryParameter : queryDefinition.getQueryParameters()) {\n            if (parameters != null && parameters.containsKey(queryParameter.getName())) {\n                queryParameters.put(queryParameter.getName(),\n                        convertToType(loadSerializableClass(queryParameter.getClassName()),\n                                parameters.get(queryParameter.getName())));\n            } else {\n                errors.add(queryParameter.getName());\n            }\n        }\n        if (!errors.isEmpty()) {\n            final StringBuilder errorMessage = new StringBuilder().append(\"parameter(s) are missing for query named \")\n                    .append(queryDefinition.getName())\n                    .append(\" : \");\n            errorMessage.append(StringUtils.join(errors, \",\"));\n            throw new SBusinessDataRepositoryException(errorMessage.toString());\n        }\n        return queryParameters;\n    }\n\n    private Serializable convertToType(final Class<? extends Serializable> clazz, final Serializable parameterValue) {\n        return (Serializable) typeConverterUtil.convertToType(clazz, parameterValue);\n    }\n\n    private Query getQueryDefinition(BusinessObject businessObject, String className, final String queryName)\n            throws SBusinessDataRepositoryException {\n        final List<Query> allQueries = new ArrayList<>();\n        allQueries.addAll(businessObject.getQueries());\n        allQueries.addAll(BDMQueryUtil.createProvidedQueriesForBusinessObject(businessObject));\n        for (final Query query : allQueries) {\n            if (query.getName().equals(queryName)) {\n                return query;\n            }\n        }\n        throw new SBusinessDataRepositoryException(\n                \"unable to get query \" + queryName + \" for business object \" + className);\n    }\n\n    private BusinessObject getBusinessObjectFromClassName(String className) throws SBusinessDataRepositoryException {\n        BusinessObject businessObject = null;\n        final BusinessObjectModel businessObjectModel = businessDataModelRepository.getBusinessObjectModel();\n        if (businessObjectModel != null) {\n            for (final BusinessObject currentBo : businessObjectModel.getBusinessObjects()) {\n                if (currentBo.getQualifiedName().equals(className)) {\n                    businessObject = currentBo;\n                }\n            }\n        }\n        return businessObject;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected Class<? extends Entity> loadClass(final String returnType) throws SBusinessDataRepositoryException {\n        try {\n            return (Class<? extends Entity>) Thread.currentThread().getContextClassLoader().loadClass(returnType);\n        } catch (final ClassNotFoundException e) {\n            throw new SBusinessDataRepositoryException(e);\n        }\n    }\n\n    protected Class<? extends Serializable> loadSerializableClass(final String className)\n            throws SBusinessDataRepositoryException {\n        try {\n            return (Class<? extends Serializable>) Thread.currentThread().getContextClassLoader().loadClass(className);\n        } catch (final ClassNotFoundException e) {\n            throw new SBusinessDataRepositoryException(\"unable to load class \" + className);\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/CountQueryProvider.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\n\nimport org.bonitasoft.engine.bdm.BDMQueryUtil;\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.Query;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class CountQueryProvider {\n\n    public Query getCountQueryDefinition(BusinessObject businessObject, Query baseQuery) {\n        if (!baseQuery.hasMultipleResults()) {\n            return null;\n        }\n        List<Query> queries = new ArrayList<>();\n        queries.addAll(BDMQueryUtil.createCountProvidedQueriesForBusinessObject(businessObject));\n        queries.addAll(businessObject.getQueries());\n        return findRelatedCountQuery(baseQuery, queries);\n    }\n\n    private Query findRelatedCountQuery(Query baseQuery, List<Query> queryList) {\n        Query countQuery = null;\n        Iterator<Query> iterator = queryList.iterator();\n        while (iterator.hasNext() && countQuery == null) {\n            Query currentQuery = iterator.next();\n            if (Long.class.getName().equals(currentQuery.getReturnType())\n                    && currentQuery.getName().equals(BDMQueryUtil.getCountQueryName(baseQuery.getName()))) {\n                countQuery = currentQuery;\n            }\n        }\n        return countQuery;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/DataRetentionBdmTrackingRepositoryImpl.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport lombok.RequiredArgsConstructor;\nimport org.bonitasoft.engine.business.data.DataRetentionBdmTrackingRepository;\nimport org.bonitasoft.engine.business.data.model.SDataRetentionBdmTracking;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.services.PersistenceService;\nimport org.bonitasoft.engine.services.SPersistenceException;\nimport org.bonitasoft.engine.services.UpdateDescriptor;\nimport org.springframework.stereotype.Repository;\n\n/**\n * Hibernate-based implementation of {@link DataRetentionBdmTrackingRepository}.\n * <p>\n * Uses {@link PersistenceService} directly (not {@code Recorder}) to avoid generating\n * queriable log entries — tracking records are internal bookkeeping for the data retention\n * service, not user-auditable actions.\n * <p>\n * All operations target the Bonita DB (not the Business Data DB).\n */\n@Repository\n@RequiredArgsConstructor\npublic class DataRetentionBdmTrackingRepositoryImpl implements DataRetentionBdmTrackingRepository {\n\n    private final PersistenceService persistenceService;\n\n    @Override\n    public void create(SDataRetentionBdmTracking tracking) throws SPersistenceException {\n        persistenceService.insert(tracking);\n    }\n\n    @Override\n    public void updateLastModifiedDate(SDataRetentionBdmTracking tracking) throws SPersistenceException {\n        persistenceService.update(\n                UpdateDescriptor.buildSetField(tracking, \"lastModifiedAt\", tracking.getLastModifiedAt()));\n    }\n\n    @Override\n    public SDataRetentionBdmTracking getByDataIdAndClassname(long dataId, String dataClassname)\n            throws SBonitaReadException {\n        return persistenceService.selectOne(new SelectOneDescriptor<>(\n                \"getDataRetentionBdmTrackingByDataIdAndClassname\",\n                Map.of(\"dataId\", dataId, \"dataClassname\", dataClassname),\n                SDataRetentionBdmTracking.class));\n    }\n\n    @Override\n    public List<SDataRetentionBdmTracking> getByClassname(String dataClassname) throws SBonitaReadException {\n        return persistenceService.selectList(new SelectListDescriptor<>(\n                \"getDataRetentionBdmTrackingByClassname\",\n                Map.of(\"dataClassname\", dataClassname),\n                SDataRetentionBdmTracking.class,\n                QueryOptions.ALL_RESULTS));\n    }\n\n    @Override\n    public List<SDataRetentionBdmTracking> getExpiredByCreatedDate(String dataClassname, long deadline)\n            throws SBonitaReadException {\n        return persistenceService.selectList(new SelectListDescriptor<>(\n                \"getExpiredTrackingByClassnameAndCreatedAt\",\n                Map.of(\"dataClassname\", dataClassname, \"deadline\", deadline),\n                SDataRetentionBdmTracking.class,\n                QueryOptions.ALL_RESULTS));\n    }\n\n    @Override\n    public List<SDataRetentionBdmTracking> getExpiredByLastModifiedDate(String dataClassname, long deadline)\n            throws SBonitaReadException {\n        return persistenceService.selectList(new SelectListDescriptor<>(\n                \"getExpiredTrackingByClassnameAndLastModifiedAt\",\n                Map.of(\"dataClassname\", dataClassname, \"deadline\", deadline),\n                SDataRetentionBdmTracking.class,\n                QueryOptions.ALL_RESULTS));\n    }\n\n    @Override\n    public void delete(SDataRetentionBdmTracking tracking) throws SPersistenceException {\n        persistenceService.delete(tracking);\n    }\n\n    @Override\n    public int delete(long dataId, String dataClassname) throws SPersistenceException {\n        return persistenceService.update(\"deleteDataRetentionBdmTrackingByDataIdAndClassname\",\n                Map.of(\"dataId\", dataId, \"dataClassname\", dataClassname));\n    }\n\n    @Override\n    public void deleteAll(List<FilterOption> filterOptions) throws SPersistenceException {\n        persistenceService.deleteAll(SDataRetentionBdmTracking.class, filterOptions);\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/DataRetentionBdmTrackingServiceImpl.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport java.util.Collections;\n\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.business.data.DataRetentionBdmTrackingRepository;\nimport org.bonitasoft.engine.business.data.DataRetentionBdmTrackingService;\nimport org.bonitasoft.engine.business.data.SDataRetentionBdmTrackingException;\nimport org.bonitasoft.engine.business.data.model.SDataRetentionBdmTracking;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.services.SPersistenceException;\nimport org.springframework.stereotype.Service;\n\n/**\n * Implementation of {@link DataRetentionBdmTrackingService} that delegates to\n * {@link DataRetentionBdmTrackingRepository} and wraps persistence-layer exceptions\n * into {@link SDataRetentionBdmTrackingException}.\n */\n@Service(\"dataRetentionBdmTrackingService\")\n@Slf4j\n@RequiredArgsConstructor\npublic class DataRetentionBdmTrackingServiceImpl implements DataRetentionBdmTrackingService {\n\n    private final DataRetentionBdmTrackingRepository bdmTrackingRepository;\n\n    @Override\n    public void create(long dataId, String dataClassname) throws SDataRetentionBdmTrackingException {\n        try {\n            long now = System.currentTimeMillis();\n            bdmTrackingRepository.create(\n                    SDataRetentionBdmTracking.builder()\n                            .dataId(dataId)\n                            .dataClassname(dataClassname)\n                            .createdAt(now)\n                            .lastModifiedAt(now)\n                            .build());\n        } catch (SPersistenceException e) {\n            throw new SDataRetentionBdmTrackingException(\n                    \"Failed to create BDM tracking record with data id \" + dataId + \" and classname \" + dataClassname,\n                    e);\n        }\n    }\n\n    @Override\n    public void upsert(long dataId, String dataClassname) throws SDataRetentionBdmTrackingException {\n        try {\n            SDataRetentionBdmTracking tracking = bdmTrackingRepository.getByDataIdAndClassname(dataId, dataClassname);\n            if (tracking != null) {\n                tracking.setLastModifiedAt(System.currentTimeMillis());\n                bdmTrackingRepository.updateLastModifiedDate(tracking);\n            } else {\n                log.debug(\"No tracking record found for {}#{}, creating one\", dataClassname, dataId);\n                create(dataId, dataClassname);\n            }\n        } catch (SBonitaReadException | SPersistenceException e) {\n            throw new SDataRetentionBdmTrackingException(\n                    \"Failed to upsert data retention tracking record for \" + dataClassname + \"#\" + dataId, e);\n        }\n    }\n\n    @Override\n    public void updateLastModifiedDate(long id) throws SDataRetentionBdmTrackingException {\n        try {\n            bdmTrackingRepository.updateLastModifiedDate(\n                    SDataRetentionBdmTracking.builder()\n                            .id(id)\n                            .lastModifiedAt(System.currentTimeMillis())\n                            .build());\n        } catch (SPersistenceException e) {\n            throw new SDataRetentionBdmTrackingException(\"Failed to update BDM tracking record with id \" + id, e);\n        }\n    }\n\n    @Override\n    public void delete(long dataId, String dataClassname) throws SDataRetentionBdmTrackingException {\n        try {\n            int rowsDeleted = bdmTrackingRepository.delete(dataId, dataClassname);\n            if (rowsDeleted == 0) {\n                log.debug(\"No tracking record found for {}#{}, nothing to delete\", dataClassname, dataId);\n            }\n        } catch (SPersistenceException e) {\n            throw new SDataRetentionBdmTrackingException(\n                    \"Failed to delete data retention tracking record for \" + dataClassname + \"#\" + dataId, e);\n        }\n    }\n\n    @Override\n    public void deleteAll() throws SDataRetentionBdmTrackingException {\n        try {\n            bdmTrackingRepository.deleteAll(Collections.emptyList());\n        } catch (SPersistenceException e) {\n            throw new SDataRetentionBdmTrackingException(\"Failed to delete all BDM tracking records\", e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/EntityManagerFactoryAware.java",
    "content": "/**\n * Copyright (C) 2025 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport javax.persistence.EntityManagerFactory;\n\n/**\n * Interface to be implemented by classes that need access to the {@link EntityManagerFactory}.\n * This is typically used in the context of business data repositories.\n * This class was introduced with the use of aspects for business data repository: A proxy class is generated, such that\n * {@link JPABusinessDataRepositoryImpl} cannot be cast directly. Introducing this interface allows\n * JPABusinessDataRepositoryImpl to be cast to this interface (See BDRepositoryLocalIT class).\n */\npublic interface EntityManagerFactoryAware {\n\n    EntityManagerFactory getEntityManagerFactory();\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/JPABusinessDataRepositoryImpl.java",
    "content": "/**\n * Copyright (C) 2015 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Set;\n\nimport javax.persistence.EntityGraph;\nimport javax.persistence.EntityManager;\nimport javax.persistence.EntityManagerFactory;\nimport javax.persistence.NoResultException;\nimport javax.persistence.Persistence;\nimport javax.persistence.PersistenceException;\nimport javax.persistence.TypedQuery;\nimport javax.persistence.criteria.CriteriaBuilder;\nimport javax.persistence.criteria.CriteriaQuery;\nimport javax.persistence.criteria.Root;\nimport javax.persistence.metamodel.EntityType;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.bonitasoft.engine.bdm.model.QueryParameterTypes;\nimport org.bonitasoft.engine.bdm.model.field.Field;\nimport org.bonitasoft.engine.business.data.BusinessDataModelRepository;\nimport org.bonitasoft.engine.business.data.BusinessDataRepository;\nimport org.bonitasoft.engine.business.data.DataRetentionBdmTrackingService;\nimport org.bonitasoft.engine.business.data.NonUniqueResultException;\nimport org.bonitasoft.engine.business.data.SBusinessDataNotFoundException;\nimport org.bonitasoft.engine.business.data.SDataRetentionBdmTrackingException;\nimport org.bonitasoft.engine.classloader.ClassLoaderIdentifier;\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.classloader.SingleClassLoaderListener;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException;\nimport org.bonitasoft.engine.commons.exceptions.SRetryableException;\nimport org.bonitasoft.engine.transaction.STransactionNotFoundException;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.hibernate.Hibernate;\nimport org.hibernate.QueryException;\nimport org.hibernate.boot.archive.scan.internal.DisabledScanner;\nimport org.hibernate.proxy.HibernateProxy;\n\n/**\n * Some of these methods are enriched with aspects to throw events. See BusinessDataRepositoryEventAspect for details.\n *\n * @author Matthieu Chaffotte\n * @author Romain Bioteau\n */\n@Slf4j\npublic class JPABusinessDataRepositoryImpl\n        implements BusinessDataRepository, EntityManagerFactoryAware, SingleClassLoaderListener {\n\n    private static final String BDR_PERSISTENCE_UNIT = \"BDR\";\n\n    private final Map<String, Object> configuration;\n\n    private EntityManagerFactory entityManagerFactory;\n\n    private final ThreadLocal<EntityManager> managers = new ThreadLocal<>();\n\n    private final BusinessDataModelRepository businessDataModelRepository;\n\n    private final UserTransactionService transactionService;\n\n    private final DataRetentionBdmTrackingService dataRetentionBdmTrackingService;\n\n    public JPABusinessDataRepositoryImpl(\n            final UserTransactionService transactionService,\n            final BusinessDataModelRepository businessDataModelRepository,\n            final Map<String, Object> configuration,\n            ClassLoaderService classLoaderService,\n            DataRetentionBdmTrackingService dataRetentionBdmTrackingService) {\n        this.transactionService = transactionService;\n        this.businessDataModelRepository = businessDataModelRepository;\n        this.configuration = new HashMap<>(configuration);\n        this.configuration.put(\"hibernate.archive.scanner\", DisabledScanner.class.getName());\n        classLoaderService.addListener(ClassLoaderIdentifier.TENANT, this);\n        this.dataRetentionBdmTrackingService = dataRetentionBdmTrackingService;\n    }\n\n    @Override\n    public void start() {\n        if (entityManagerFactory == null && businessDataModelRepository.isBDMDeployed()) {\n            log.debug(\"Creating Entity Manager Factory\");\n            recreateEntityManagerFactoryEvenIfExisting();\n        }\n    }\n\n    EntityManagerFactory createEntityManagerFactory() {\n        return Persistence.createEntityManagerFactory(BDR_PERSISTENCE_UNIT, configuration);\n    }\n\n    @Override\n    public void stop() {\n        if (getEntityManagerFactory() != null) {\n            log.debug(\"Closing Entity Manager Factory because service is stopping\");\n            getEntityManagerFactory().close();\n            entityManagerFactory = null;\n            log.debug(\"Entity Manager Factory closed\");\n        }\n    }\n\n    private synchronized void recreateEntityManagerFactoryOnClassLoaderChange(ClassLoader newClassLoader) {\n        if (businessDataModelRepository.isBDMDeployed()) {\n            log.debug(\"Recreating Entity Manager Factory for classloader {}\", newClassLoader);\n            final ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();\n            try {\n                Thread.currentThread().setContextClassLoader(newClassLoader);\n                recreateEntityManagerFactoryEvenIfExisting();\n            } finally {\n                Thread.currentThread().setContextClassLoader(currentClassLoader);\n            }\n            log.debug(\"Entity Manager Factory recreated\");\n        } else {\n            log.debug(\"No BDM deployed. No Entity Manager Factory to recreate.\");\n        }\n    }\n\n    private void recreateEntityManagerFactoryEvenIfExisting() {\n        if (entityManagerFactory != null) {\n            log.warn(\"Entity Manager Factory should be null. Closing it and recreating a new one.\");\n            entityManagerFactory.close();\n        }\n        entityManagerFactory = createEntityManagerFactory();\n        log.debug(\"Recreated Entity Manager Factory: {}\", entityManagerFactory);\n    }\n\n    public EntityManagerFactory getEntityManagerFactory() {\n        if (entityManagerFactory == null) {\n            /*\n             * in case the entity manager factory is reloading inside #recreateEntityManagerFactory\n             * we get it inside a method synchronized with #recreateEntityManagerFactory\n             */\n            return synchronizedGetEntityManagerFactory();\n        }\n        return entityManagerFactory;\n    }\n\n    private synchronized EntityManagerFactory synchronizedGetEntityManagerFactory() {\n        return entityManagerFactory;\n    }\n\n    @Override\n    public void pause() {\n        stop();\n    }\n\n    @Override\n    public void resume() {\n        start();\n    }\n\n    @Override\n    public Set<String> getEntityClassNames() {\n        if (getEntityManagerFactory() == null) {\n            return Collections.emptySet();\n        }\n        final EntityManager em = getEntityManager();\n        final Set<EntityType<?>> entities = em.getMetamodel().getEntities();\n        final Set<String> entityClassNames = new HashSet<>();\n        for (final EntityType<?> entity : entities) {\n            entityClassNames.add(entity.getJavaType().getName());\n        }\n        return entityClassNames;\n    }\n\n    protected EntityManager getEntityManager() {\n        if (getEntityManagerFactory() == null) {\n            throw new IllegalStateException(\"The BDR is not started\");\n        }\n\n        EntityManager manager = managers.get();\n        if (manager != null && !isPartOfCurrentTransaction(manager)) {\n            log.warn(\"Stale BDM EntityManager detected: not part of the current transaction. \"\n                    + \"It will be replaced with a fresh one. \"\n                    + \"This typically happens after a JTA transaction timeout.\");\n            closeQuietly(manager);\n            managers.remove();\n            manager = null;\n        }\n        if (manager == null) {\n            manager = getEntityManagerFactory().createEntityManager();\n            try {\n                transactionService.registerBonitaSynchronization(new RemoveEntityManagerSynchronization(managers));\n            } catch (final STransactionNotFoundException stnfe) {\n                closeQuietly(manager);\n                throw new IllegalStateException(stnfe);\n            }\n            managers.set(manager);\n            log.debug(\"Created new BDM EntityManager for current transaction\");\n        } else {\n            log.debug(\"Reusing existing BDM EntityManager in current transaction.\");\n        }\n        try {\n            manager.joinTransaction();\n        } catch (Exception e) {\n            log.warn(\n                    \"BDM EntityManager failed to join current transaction. This may indicate the transaction is already marked for rollback.\",\n                    e);\n            closeQuietly(manager);\n            managers.remove();\n            throw e;\n        }\n        return manager;\n    }\n\n    private boolean isPartOfCurrentTransaction(EntityManager manager) {\n        try {\n            return manager.isOpen() && manager.isJoinedToTransaction();\n        } catch (Exception e) {\n            log.warn(\"Stale BDM EntityManager detected (exception during transaction state check). \"\n                    + \"A fresh EntityManager will be created for the current transaction.\", e);\n            return false;\n        }\n    }\n\n    private void closeQuietly(EntityManager manager) {\n        try {\n            if (manager.isOpen()) {\n                manager.close();\n            }\n        } catch (Exception e) {\n            log.warn(\"Failed to close stale BDM EntityManager. \"\n                    + \"This may indicate a resource leak if it happens repeatedly.\", e);\n        }\n    }\n\n    @Override\n    public <T extends Entity> T findById(final Class<T> entityClass, final Long primaryKey)\n            throws SBusinessDataNotFoundException {\n        if (primaryKey == null) {\n            throw new SBusinessDataNotFoundException(\n                    \"Impossible to get data of type \" + entityClass.getName() + \" with a null identifier\");\n        }\n        final EntityManager em = getEntityManager();\n        final T entity;\n        try {\n            entity = em.find(entityClass, primaryKey);\n        } catch (final PersistenceException e) {\n            log.debug(\"BDM findById({}, id={}) failed\", entityClass.getSimpleName(), primaryKey, e);\n            //wrap in retryable exception because the issue might come from BDR reloading\n            throw new SRetryableException(e);\n        }\n        if (entity == null) {\n            throw new SBusinessDataNotFoundException(\n                    \"Impossible to get data of type \" + entityClass.getName() + \" with id: \" + primaryKey);\n        }\n        return entity;\n    }\n\n    @Override\n    public <T extends Entity> List<T> findByIds(final Class<T> entityClass, final List<Long> primaryKeys) {\n        if (primaryKeys == null || primaryKeys.isEmpty()) {\n            return new ArrayList<>();\n        }\n        final EntityManager em = getEntityManager();\n        try {\n            final CriteriaBuilder cb = em.getCriteriaBuilder();\n            final CriteriaQuery<T> criteriaQuery = cb.createQuery(entityClass);\n            final Root<T> row = criteriaQuery.from(entityClass);\n            criteriaQuery.select(row).where(row.get(Field.PERSISTENCE_ID).in(primaryKeys));\n            return em.createQuery(criteriaQuery).getResultList();\n        } catch (final PersistenceException e) {\n            //wrap in retryable exception because the issue might come from BDR reloading\n            throw new SRetryableException(e);\n        }\n    }\n\n    @Override\n    public <T extends Entity> List<T> findByIdentifiers(final Class<T> entityClass, final List<Long> primaryKeys) {\n        if (primaryKeys == null || primaryKeys.isEmpty()) {\n            return new ArrayList<>();\n        }\n        final List<T> entities = new ArrayList<>();\n        for (final Long primaryKey : primaryKeys) {\n            try {\n                entities.add(findById(entityClass, primaryKey));\n            } catch (final SBusinessDataNotFoundException e) {\n                // If the business data does not exist, do not add it in the result list in order to have the same behaviour as findByIds\n            }\n        }\n        return entities;\n    }\n\n    protected <T extends Serializable> T find(final Class<T> resultClass, final TypedQuery<T> query,\n            final Map<String, Serializable> parameters)\n            throws NonUniqueResultException {\n        if (query == null) {\n            throw new IllegalArgumentException(\"query is null\");\n        }\n        if (parameters != null) {\n            for (final Entry<String, Serializable> parameter : parameters.entrySet()) {\n                query.setParameter(parameter.getKey(), checkParameterValue(parameter.getValue()));\n            }\n        }\n        try {\n            return query.getSingleResult();\n        } catch (final javax.persistence.NonUniqueResultException nure) {\n            throw new NonUniqueResultException(nure);\n        } catch (final NoResultException e) {\n            return null;\n        }\n    }\n\n    Object checkParameterValue(final Serializable parameterValue) {\n        if (parameterValue != null && !QueryParameterTypes.contains(parameterValue.getClass())) {\n            throw new IllegalArgumentException(String.format(\n                    \"'%s' is not a supported type for a query parameter.\",\n                    parameterValue.getClass().getName()));\n        }\n        if (parameterValue instanceof Object[]) {\n            return Arrays.asList((Object[]) parameterValue);\n        }\n        return parameterValue;\n    }\n\n    @Override\n    public <T extends Serializable> T find(final Class<T> resultClass, final String jpqlQuery,\n            final Map<String, Serializable> parameters)\n            throws NonUniqueResultException {\n        final TypedQuery<T> typedQuery = createTypedQuery(jpqlQuery, resultClass);\n        try {\n            return find(resultClass, typedQuery, parameters);\n        } catch (final PersistenceException e) {\n            throw new SRetryableException(e);\n        }\n    }\n\n    @Override\n    public <T extends Serializable> List<T> findList(final Class<T> resultClass, final String jpqlQuery,\n            final Map<String, Serializable> parameters,\n            final int startIndex, final int maxResults) {\n        final TypedQuery<T> typedQuery = createTypedQuery(jpqlQuery, resultClass);\n        try {\n            return findList(typedQuery, parameters, startIndex, maxResults);\n        } catch (final QueryException e) {\n            throw new IllegalArgumentException(e);\n        } catch (final PersistenceException e) {\n            throw new SRetryableException(e);\n        }\n    }\n\n    @Override\n    public <T extends Serializable> T findByNamedQuery(final String queryName, final Class<T> resultClass,\n            final Map<String, Serializable> parameters)\n            throws NonUniqueResultException {\n        final EntityManager em = getEntityManager();\n        try {\n            final TypedQuery<T> query = em.createNamedQuery(queryName, resultClass);\n            return find(resultClass, query, parameters);\n        } catch (final PersistenceException e) {\n            log.debug(\"BDM findByNamedQuery('{}', {}) failed\", queryName, resultClass.getSimpleName(), e);\n            //wrap in retryable exception because the issue might come from BDR reloading\n            throw new SRetryableException(e);\n        }\n    }\n\n    @Override\n    public <T extends Serializable> List<T> findListByNamedQuery(final String queryName, final Class<T> resultClass,\n            final Map<String, Serializable> parameters, final int startIndex, final int maxResults) {\n        final EntityManager em = getEntityManager();\n        try {\n            final TypedQuery<T> query = em.createNamedQuery(queryName, resultClass);\n            return findList(query, parameters, startIndex, maxResults);\n        } catch (final PersistenceException e) {\n            log.debug(\"BDM findListByNamedQuery('{}', {}) failed\", queryName, resultClass.getSimpleName(), e);\n            //wrap in retryable exception because the issue might come from BDR reloading\n            throw new SRetryableException(e);\n        }\n    }\n\n    private <T> TypedQuery<T> createTypedQuery(final String jpqlQuery, final Class<T> resultClass) {\n        return getEntityManager().createQuery(jpqlQuery, resultClass);\n    }\n\n    protected <T extends Serializable> List<T> findList(final TypedQuery<T> query,\n            final Map<String, Serializable> parameters, final int startIndex,\n            final int maxResults) {\n        if (query == null) {\n            throw new IllegalArgumentException(\"query is null\");\n        }\n        if (maxResults > 0) {\n            if (parameters != null) {\n                for (final Entry<String, Serializable> parameter : parameters.entrySet()) {\n                    query.setParameter(parameter.getKey(), checkParameterValue(parameter.getValue()));\n                }\n            }\n            query.setFirstResult(startIndex);\n            query.setMaxResults(maxResults);\n            return query.getResultList();\n        }\n        return Collections.emptyList();\n    }\n\n    /**\n     * Loads the root entity with an empty {@code EntityGraph} ({@code fetchgraph} hint) to\n     * override {@code FetchType.EAGER} associations, then calls {@code em.remove()}.\n     * <p>\n     * The effect on memory and query shape depends on the relationship type:\n     * <ul>\n     * <li><b>AGGREGATION</b> (no cascade REMOVE — e.g. Invoice → Customer): the referenced\n     * entity is <b>never loaded</b>, since cascade delete does not need it. This is the main\n     * win — without the fetchgraph, EAGER would force loading entities just to discard them.</li>\n     * <li><b>COMPOSITION</b> (cascade REMOVE — e.g. Invoice → InvoiceLine): children are still\n     * loaded into memory because cascade delete requires the collection contents. The benefit\n     * is that they are loaded via a separate SELECT per collection instead of a single wide\n     * JOIN, avoiding cartesian-product row explosion when multiple or deep compositions exist.</li>\n     * </ul>\n     * <p>\n     * The returned entity is used by {@link BusinessDataRepositoryEventAspect} to fire the\n     * {@code BUSINESS_DATA_DELETED} event.\n     */\n    @Override\n    public Entity removeById(Class<? extends Entity> entityClass, long persistenceId)\n            throws SBusinessDataNotFoundException {\n        log.trace(\"Removing entity of type {} with id {} using EntityGraph\", entityClass.getName(), persistenceId);\n        final EntityManager em = getEntityManager();\n        try {\n            // Empty fetchgraph overrides EAGER to LAZY for all associations. Aggregations are\n            // then skipped entirely; compositions are still fetched at cascade time, but via\n            // per-collection SELECTs rather than a single wide JOIN.\n            EntityGraph<?> minimalGraph = em.createEntityGraph(entityClass);\n            Entity entity = em.find(entityClass, persistenceId,\n                    Map.of(\"javax.persistence.fetchgraph\", minimalGraph));\n            if (entity == null) {\n                throw new SBusinessDataNotFoundException(\n                        \"Impossible to get data of type \" + entityClass.getName() + \" with id: \" + persistenceId);\n            }\n            em.remove(entity);\n            // Delete the tracking record linked to the removed BDM entity in the Bonita DB\n            deleteTrackingRecord(persistenceId, entityClass.getName());\n            return entity;\n        } catch (final PersistenceException e) {\n            throw new SRetryableException(\n                    \"Failed to remove entity \" + entityClass.getName() + \" with id \" + persistenceId, e);\n        }\n    }\n\n    @Override\n    public void remove(final Entity entity) {\n        if (entity != null && entity.getPersistenceId() != null) {\n            log.trace(\"Removing entity of type {} with id {}\", entity.getClass().getName(), entity.getPersistenceId());\n            final EntityManager em = getEntityManager();\n            try {\n                em.remove(entity);\n                // Delete the tracking record linked to the removed BDM entity in the Bonita DB\n                deleteTrackingRecord(entity.getPersistenceId(), entity.getClass().getName());\n            } catch (final PersistenceException e) {\n                throw new SRetryableException(e);\n            }\n        } else {\n            log.trace(\"Entity is null or has null id, nothing to remove\");\n        }\n    }\n\n    /**\n     * Deletes the data retention tracking record associated with a removed BDM entity.\n     * <p>\n     * Unlike {@link #trackCreation} and {@link #trackUpdate}, failures are logged\n     * but do not roll back the transaction — the BDM entity removal takes priority.\n     * Any orphan tracking record will be cleaned up later by the data retention job.\n     */\n    private void deleteTrackingRecord(long entityId, String entityClassname) {\n        log.debug(\"Deleting data retention tracking record for removed BDM entity {}#{}\", entityClassname, entityId);\n        try {\n            dataRetentionBdmTrackingService.delete(entityId, entityClassname);\n        } catch (Exception e) {\n            // Ignore exceptions because the BDM entity is already removed, and we don't want to roll back that\n            // removal if tracking deletion fails. The orphan tracking record will be cleaned up later by the data\n            // retention cleanup job.\n            log.warn(\"Failed to delete data retention tracking record for {}#{}\", entityClassname, entityId, e);\n        }\n    }\n\n    @Override\n    public void persist(final Entity entity) {\n        if (entity == null) {\n            log.trace(\"Entity is null, nothing to persist\");\n            return;\n        }\n        log.trace(\"Persisting entity of type {} with id {}\", entity.getClass().getName(), entity.getPersistenceId());\n        try {\n            // Capture whether the entity is new before JPA assigns an ID\n            var isNew = entity.getPersistenceId() == null;\n\n            getEntityManager().persist(entity);\n\n            // Insert or update a data retention tracking record in the Bonita DB\n            if (isNew) {\n                trackCreation(entity.getPersistenceId(), entity.getClass().getName());\n            } else {\n                trackUpdate(entity.getPersistenceId(), entity.getClass().getName());\n            }\n        } catch (final PersistenceException e) {\n            throw new SRetryableException(e);\n        }\n    }\n\n    @Override\n    public Entity merge(final Entity entity) {\n        if (entity == null) {\n            log.trace(\"Entity is null, nothing to merge\");\n            return null;\n        }\n        log.trace(\"Merging entity of type {} with id {}\", entity.getClass().getName(), entity.getPersistenceId());\n        try {\n            // Capture whether the entity is new before JPA assigns an ID\n            var isNew = entity.getPersistenceId() == null;\n            // Capture the real class name before merge, because merge() may return a Hibernate proxy\n            // whose getClass().getName() would be e.g. \"Invoice$HibernateProxyXxx\" instead of \"Invoice\"\n            var entityClassname = entity.getClass().getName();\n\n            Entity merged = getEntityManager().merge(entity);\n\n            // Insert or update a data retention tracking record in the Bonita DB,\n            // use the merged entity which has the JPA-assigned persistenceId\n            if (isNew) {\n                trackCreation(merged.getPersistenceId(), entityClassname);\n            } else {\n                trackUpdate(merged.getPersistenceId(), entityClassname);\n            }\n            return merged;\n        } catch (final PersistenceException e) {\n            throw new SRetryableException(e);\n        }\n    }\n\n    /**\n     * Inserts a tracking record in the Bonita DB for a newly created BDM entity.\n     * Called within the same JTA transaction as the BDM persist/merge, so both\n     * are rolled back together if either fails.\n     */\n    private void trackCreation(long entityId, String entityClassname) {\n        log.debug(\"Tracking creation of new BDM entity {}#{}\", entityClassname, entityId);\n        try {\n            dataRetentionBdmTrackingService.create(entityId, entityClassname);\n        } catch (SDataRetentionBdmTrackingException e) {\n            // Intentionally throwing an unchecked exception to roll back the entire JTA transaction,\n            // including the BDM entity creation. Tracking and BDM data must stay consistent.\n            throw new SBonitaRuntimeException(\"Failed to insert data retention tracking record for \"\n                    + entityClassname + \"#\" + entityId, e);\n        }\n    }\n\n    /**\n     * Updates the {@code lastModifiedAt} timestamp of an existing tracking record\n     * in the Bonita DB. If no record is found (e.g. the entity was created before\n     * the tracking feature was deployed), a new one is created instead.\n     */\n    private void trackUpdate(long entityId, String entityClassname) {\n        log.debug(\"Tracking update of existing BDM entity {}#{}\", entityClassname, entityId);\n        try {\n            dataRetentionBdmTrackingService.upsert(entityId, entityClassname);\n        } catch (SDataRetentionBdmTrackingException e) {\n            // Intentionally throwing an unchecked exception to roll back the entire JTA transaction,\n            // including the BDM entity update. Tracking and BDM data must stay consistent.\n            throw new SBonitaRuntimeException(\"Failed to update data retention tracking record for \"\n                    + entityClassname + \"#\" + entityId, e);\n        }\n    }\n\n    @Override\n    public Entity unwrap(final Entity wrapped) {\n        Entity entity = wrapped;\n        if (entity instanceof HibernateProxy) {\n            Hibernate.initialize(entity);\n            entity = (Entity) ((HibernateProxy) entity).getHibernateLazyInitializer().getImplementation();\n        }\n        return entity;\n    }\n\n    @Override\n    public void onUpdate(ClassLoader newClassLoader) {\n        clearProxyFactoryCache();\n        recreateEntityManagerFactoryOnClassLoaderChange(newClassLoader);\n    }\n\n    private void clearProxyFactoryCache() {\n        log.debug(\"Clearing BDM proxy cache\");\n        try {\n            new ProxyCacheManager().clearCache();\n            log.debug(\"BDM proxy cache cleared\");\n        } catch (NoSuchFieldException | IllegalAccessException e) {\n            throw new SRetryableException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/JsonBusinessDataSerializerImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport java.io.IOException;\nimport java.util.List;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.PropertyAccessor;\nimport com.fasterxml.jackson.databind.ObjectWriter;\nimport com.fasterxml.jackson.databind.module.SimpleModule;\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.bonitasoft.engine.bdm.serialization.BusinessDataObjectMapper;\nimport org.bonitasoft.engine.business.data.JsonBusinessDataSerializer;\nimport org.bonitasoft.engine.business.data.SBusinessDataRepositorySerializationException;\nimport org.bonitasoft.engine.business.data.impl.jackson.EntityBeanSerializerModifier;\nimport org.bonitasoft.engine.business.data.impl.jackson.EntityJacksonAnnotationIntrospector;\nimport org.bonitasoft.engine.business.data.impl.jackson.EntityMixin;\nimport org.bonitasoft.engine.business.data.impl.jackson.utils.LinkUtils;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;\nimport org.springframework.stereotype.Component;\n\n/**\n * Jackson-based serializer for Business Data Model (BDM) entities to JSON.\n * <p>\n * This service is responsible for converting BDM entities and query results into JSON format\n * for REST API responses. It handles two JSON output shapes:\n * <ul>\n * <li><b>Standard shape</b>: Single-entity queries return objects {@code {...}}, scalar queries return\n * {@code { \"value\": X }}</li>\n * <li><b>Legacy shape</b>: All queries return arrays {@code [...]}</li>\n * </ul>\n * <p>\n * The serializer configures Jackson to:\n * <ul>\n * <li>Use field-based introspection (avoiding proxy getters)</li>\n * <li>Generate links for entity relationships</li>\n * <li>Apply custom entity serialization logic via {@link EntityBeanSerializerModifier}</li>\n * </ul>\n * <p>\n * This is a stateless service - the decision of which shape to use is made by the calling service\n * ({@link BusinessDataServiceImpl}) and passed as method parameters.\n *\n * @see JsonBusinessDataSerializer\n * @see BusinessDataServiceImpl for the orchestration layer that determines which shape to use\n */\n@Component\n@ConditionalOnSingleCandidate(JsonBusinessDataSerializer.class)\n@Slf4j\npublic class JsonBusinessDataSerializerImpl extends BusinessDataObjectMapper\n        implements JsonBusinessDataSerializer {\n\n    /**\n     * Property name for the standard shape configuration.\n     */\n    public static final String STANDARD_SHAPE_PROPERTY = \"bonita.runtime.business-data.serialization.standard-shape.enabled\";\n\n    @Value(\"${\" + STANDARD_SHAPE_PROPERTY + \":true}\")\n    private boolean standardShapeEnabled;\n\n    public JsonBusinessDataSerializerImpl() {\n        SimpleModule module = new SimpleModule();\n        module.setSerializerModifier(new EntityBeanSerializerModifier());\n        objectMapper.registerModule(module);\n\n        objectMapper.addMixIn(Entity.class, EntityMixin.class);\n        objectMapper.setAnnotationIntrospector(new EntityJacksonAnnotationIntrospector());\n\n        // Ensure Jackson use only fields to get properties. Reasons:\n        //  - entity can be wrapped in a javassist or hibernate proxy that have additional getters\n        //  - generated getters may not exactly match the field name (getANumber for aNumber field)\n        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);\n        objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);\n    }\n\n    private ObjectWriter newObjectWriter(String uriPattern) {\n        return LinkUtils.putUriPatternIntoContext(objectMapper.writer(), uriPattern);\n    }\n\n    @Override\n    public boolean isStandardShapeEnabled() {\n        return standardShapeEnabled;\n    }\n\n    @Override\n    public String serializeEntity(final Entity entity, final String businessDataURIPattern)\n            throws SBusinessDataRepositorySerializationException {\n        try {\n            if (log.isTraceEnabled()) {\n                log.trace(\"Serializing entity\");\n            }\n            String json = newObjectWriter(businessDataURIPattern).writeValueAsString(entity);\n            if (log.isTraceEnabled()) {\n                log.trace(\"Entity serialization result: {}\", json);\n            }\n            return json;\n        } catch (IOException e) {\n            throw new SBusinessDataRepositorySerializationException(\n                    \"Unable to serialize Entity of type \" + entity.getClass().getSimpleName(), e);\n        }\n    }\n\n    @Override\n    public String serializeEntities(final List<? extends Entity> entities, final String businessDataURIPattern)\n            throws SBusinessDataRepositorySerializationException {\n        try {\n            if (log.isTraceEnabled()) {\n                log.trace(\"Serializing a list of entities\");\n            }\n            String json = newObjectWriter(businessDataURIPattern).writeValueAsString(entities);\n            if (log.isTraceEnabled()) {\n                log.trace(\"List of entities serialization result: {}\", json);\n            }\n            return json;\n        } catch (IOException e) {\n            throw new SBusinessDataRepositorySerializationException(\"Unable to serialize list of Entity\", e);\n        }\n    }\n\n    @Override\n    @Deprecated\n    public String serializeCountResult(List<Long> list, String entityClassName) {\n        String json = \"[\" + list.get(0).toString() + \"]\";\n        if (log.isTraceEnabled()) {\n            log.trace(\"Count serialization result: {}\", json);\n        }\n        return json;\n    }\n\n    @Override\n    public String serializeScalarResult(List<?> list, String entityClassName, boolean useStandardShape) {\n        Object value = (list == null || list.isEmpty()) ? null : list.get(0);\n        String json;\n        if (useStandardShape) {\n            // Standard shape: { \"value\": 10 }\n            json = \"{ \\\"value\\\": \" + (value == null ? \"null\" : value.toString()) + \" }\";\n        } else {\n            // Legacy shape: [ 10 ]\n            json = \"[\" + (value == null ? \"null\" : value.toString()) + \"]\";\n        }\n        if (log.isTraceEnabled()) {\n            log.trace(\"Scalar serialization result: {}\", json);\n        }\n        return json;\n    }\n\n    @Override\n    public String serializeEntityQueryResult(List<? extends Entity> entities, String businessDataURIPattern,\n            boolean useStandardShape, boolean queryReturnsMultipleResults)\n            throws SBusinessDataRepositorySerializationException {\n        if (!useStandardShape || queryReturnsMultipleResults) {\n            // Legacy shape (always array) OR query designed to return List: return array [{...}]\n            return serializeEntities(entities, businessDataURIPattern);\n        }\n        // Standard shape with query designed to return single entity\n        if (entities != null && entities.size() == 1) {\n            // Return object {...}\n            return serializeEntity(entities.get(0), businessDataURIPattern);\n        } else if (entities == null || entities.isEmpty()) {\n            // Return empty object {}\n            return EMPTY_OBJECT;\n        } else {\n            // Multiple results from single-entity query (shouldn't happen normally): return array\n            return serializeEntities(entities, businessDataURIPattern);\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/ProxyCacheManager.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport java.lang.reflect.AccessibleObject;\nimport java.lang.reflect.Field;\nimport java.security.AccessController;\nimport java.security.PrivilegedAction;\nimport java.security.PrivilegedActionException;\nimport java.security.PrivilegedExceptionAction;\nimport java.util.WeakHashMap;\nimport javassist.util.proxy.ProxyFactory;\n\npublic class ProxyCacheManager {\n\n    static void setAccessible(final AccessibleObject ao,\n            final boolean accessible) {\n        if (System.getSecurityManager() == null)\n            ao.setAccessible(accessible);\n        else {\n            AccessController.doPrivileged(new PrivilegedAction() {\n\n                @Override\n                public Object run() {\n                    ao.setAccessible(accessible);\n                    return null;\n                }\n            });\n        }\n    }\n\n    static Field getDeclaredField(final Class clazz, final String name) throws NoSuchFieldException {\n        if (System.getSecurityManager() == null)\n            return clazz.getDeclaredField(name);\n        else {\n            try {\n                return (Field) AccessController\n                        .doPrivileged(new PrivilegedExceptionAction() {\n\n                            @Override\n                            public Object run() throws Exception {\n                                return clazz.getDeclaredField(name);\n                            }\n                        });\n            } catch (final PrivilegedActionException e) {\n                if (e.getCause() instanceof NoSuchFieldException)\n                    throw (NoSuchFieldException) e.getCause();\n\n                throw new RuntimeException(e.getCause());\n            }\n        }\n    }\n\n    static Object get(final Field fld, final Object target)\n            throws IllegalAccessException {\n        if (System.getSecurityManager() == null)\n            return fld.get(target);\n        else {\n            try {\n                return AccessController.doPrivileged(new PrivilegedExceptionAction() {\n\n                    @Override\n                    public Object run() throws Exception {\n                        return fld.get(target);\n                    }\n                });\n            } catch (final PrivilegedActionException e) {\n                if (e.getCause() instanceof NoSuchMethodException)\n                    throw (IllegalAccessException) e.getCause();\n\n                throw new RuntimeException(e.getCause());\n            }\n        }\n    }\n\n    public WeakHashMap/* <Classloader,HashMap<String,ProxyDetails>> */ get()\n            throws NoSuchFieldException, IllegalAccessException {\n        final Field proxyCacheField = getDeclaredField(ProxyFactory.class, \"proxyCache\");\n        setAccessible(proxyCacheField, true);\n        return (WeakHashMap) get(proxyCacheField, null);\n    }\n\n    public void clearCache() throws NoSuchFieldException, IllegalAccessException {\n        final WeakHashMap cache = get();\n        if (cache != null) {\n            cache.clear();\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/RemoveEntityManagerSynchronization.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport javax.persistence.EntityManager;\nimport javax.transaction.Status;\n\nimport org.bonitasoft.engine.transaction.BonitaTransactionSynchronization;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class RemoveEntityManagerSynchronization implements BonitaTransactionSynchronization {\n\n    private static final Logger log = LoggerFactory.getLogger(RemoveEntityManagerSynchronization.class);\n\n    private final ThreadLocal<EntityManager> localManager;\n\n    public RemoveEntityManagerSynchronization(final ThreadLocal<EntityManager> localManager) {\n        super();\n        this.localManager = localManager;\n    }\n\n    @Override\n    public void afterCompletion(final int txState) {\n        if (txState == Status.STATUS_UNKNOWN) {\n            log.error(\"BDM EntityManager cleanup after transaction completed with STATUS_UNKNOWN \"\n                    + \"(heuristic mixed outcome). Some XA resources may have committed while others rolled back. \"\n                    + \"The persistence context is in an undefined state and will be discarded. \"\n                    + \"Manual verification of business data consistency may be required.\");\n        }\n        try {\n            EntityManager entityManager = localManager.get();\n            // Ensure the EntityManager is not already closed before attempting to close it (EntityManager.close() throws\n            // an exception if the EntityManager is already closed)\n            if (entityManager != null && entityManager.isOpen()) {\n                try {\n                    entityManager.close();\n                } catch (Exception e) {\n                    log.warn(\"Failed to close BDM EntityManager during transaction completion (txState={}). \"\n                            + \"This may indicate a resource leak. The EntityManager reference will still be removed \"\n                            + \"from the thread to prevent stale state on the next operation.\",\n                            txState, e);\n                }\n            }\n        } finally {\n            localManager.remove();\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/SchemaManagerReadOnly.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.business.data.SchemaManager;\nimport org.hibernate.HibernateException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Pablo Alonso de Linaje García\n */\npublic class SchemaManagerReadOnly implements SchemaManager {\n\n    private static final Logger log = LoggerFactory.getLogger(SchemaManagerReadOnly.class);\n\n    public SchemaManagerReadOnly() throws HibernateException {\n        log.warn(\"Ready-Only Schema manager. No change will be performed on the BDM DB Schema.\" +\n                \" Please ensure that this update is done before using the new BDM\");\n    }\n\n    @Override\n    public List<Exception> drop(Set<String> managedClasses) {\n        log.warn(\n                \"No drop of BDM DB Schema will be performed. Please ensure that this update is done before using the new BDM\");\n        return new ArrayList<>();\n    }\n\n    @Override\n    public List<Exception> update(Set<String> managedClasses) {\n        log.warn(\n                \"No update of BDM DB Schema will be performed. Please ensure that this update is done before using the new BDM\");\n        return new ArrayList<>();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/SchemaManagerUpdate.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport java.util.EnumSet;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.business.data.SchemaManager;\nimport org.hibernate.HibernateException;\nimport org.hibernate.MappingException;\nimport org.hibernate.boot.Metadata;\nimport org.hibernate.boot.MetadataSources;\nimport org.hibernate.boot.registry.StandardServiceRegistryBuilder;\nimport org.hibernate.internal.util.ReflectHelper;\nimport org.hibernate.tool.hbm2ddl.SchemaExport;\nimport org.hibernate.tool.hbm2ddl.SchemaUpdate;\nimport org.hibernate.tool.schema.TargetType;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SchemaManagerUpdate implements SchemaManager {\n\n    private static final Logger log = LoggerFactory.getLogger(SchemaManagerUpdate.class);\n\n    private final Map<String, Object> configuration;\n\n    public SchemaManagerUpdate(final Map<String, Object> configuration)\n            throws HibernateException {\n        this.configuration = new HashMap<>(configuration);\n        final Object remove = this.configuration.remove(\"hibernate.hbm2ddl.auto\");\n        if (remove != null && log.isInfoEnabled()) {\n            log.info(\"'hibernate.hbm2ddl.auto' is not a valid property so it has been ignored\");\n        }\n    }\n\n    private Metadata buildConfiguration(final Set<String> managedClasses) {\n        MetadataSources metadata = new MetadataSources(\n                new StandardServiceRegistryBuilder()\n                        .applySettings(configuration)\n                        .build());\n        for (final String entity : managedClasses) {\n            metadata.addAnnotatedClass(getMappedClass(entity));\n        }\n        return metadata.buildMetadata();\n    }\n\n    public Class<?> getMappedClass(final String className) throws MappingException {\n        if (className == null) {\n            return null;\n        }\n        try {\n            return ReflectHelper.classForName(className);\n        } catch (final ClassNotFoundException notFound) {\n            throw new MappingException(\"entity class not found: \" + className, notFound);\n        }\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public List<Exception> drop(final Set<String> managedClasses) {\n        log.info(\"Dropping classes: {}\", managedClasses);\n\n        SchemaExport schemaExport = new SchemaExport();\n        schemaExport.drop(EnumSet.of(TargetType.DATABASE), buildConfiguration(managedClasses));\n\n        log.info(\"Drop operation done\");\n        return schemaExport.getExceptions();\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public List<Exception> update(final Set<String> managedClasses) {\n        log.info(\"Updating classes: {}\", managedClasses);\n\n        final SchemaUpdate schemaUpdate = new SchemaUpdate();\n        schemaUpdate.execute(EnumSet.of(TargetType.DATABASE), buildConfiguration(managedClasses));\n\n        log.info(\"Update operation done\");\n        return schemaUpdate.getExceptions();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/jackson/EntityBeanSerializerModifier.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl.jackson;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport javassist.util.proxy.MethodHandler;\nimport javassist.util.proxy.Proxy;\n\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport com.fasterxml.jackson.core.JsonGenerator;\nimport com.fasterxml.jackson.databind.BeanDescription;\nimport com.fasterxml.jackson.databind.JavaType;\nimport com.fasterxml.jackson.databind.JsonSerializer;\nimport com.fasterxml.jackson.databind.SerializationConfig;\nimport com.fasterxml.jackson.databind.SerializerProvider;\nimport com.fasterxml.jackson.databind.ser.BeanPropertyWriter;\nimport com.fasterxml.jackson.databind.ser.BeanSerializerModifier;\nimport org.apache.commons.lang3.ClassUtils;\nimport org.bonitasoft.engine.business.data.impl.jackson.utils.ExtraPropertyUtils;\nimport org.bonitasoft.engine.business.data.impl.jackson.writer.ExtraBeanPropertyWriter;\nimport org.bonitasoft.engine.business.data.impl.jackson.writer.IgnoredPropertyWriter;\nimport org.hibernate.proxy.HibernateProxy;\nimport org.hibernate.proxy.LazyInitializer;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class EntityBeanSerializerModifier extends BeanSerializerModifier {\n\n    private static Logger LOG = LoggerFactory.getLogger(EntityBeanSerializerModifier.class);\n\n    @Override\n    public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc,\n            List<BeanPropertyWriter> beanProperties) {\n        LOG.trace(\"Changing list of property writers for {}\", beanDesc.getClassInfo());\n        List<BeanPropertyWriter> newProperties = new ArrayList<>();\n\n        if (shouldBeIgnored(beanDesc)) {\n            LOG.trace(\"Ignoring all properties of this bean\");\n            return newProperties;\n        }\n\n        for (BeanPropertyWriter beanPropertyWriter : beanProperties) {\n            LOG.trace(\"{}\", beanPropertyWriter);\n            LOG.trace(\"Bean type {}\", beanPropertyWriter.getType());\n\n            if (shouldBeReplacedByLink(beanPropertyWriter)) {\n                LOG.trace(\"Has to be replaced by link\");\n                BeanPropertyWriter ignoredPropertyWriter = new IgnoredPropertyWriter(beanPropertyWriter);\n                LOG.trace(\"Adding only an ignored property writer {}\", ignoredPropertyWriter);\n                newProperties.add(ignoredPropertyWriter);\n            } else {\n                newProperties.add(beanPropertyWriter);\n                if (ExtraPropertyUtils.shouldAddExtraProperty(beanPropertyWriter)) {\n                    LOG.trace(\"Will have an additional property\");\n                    BeanPropertyWriter additionalPropertyWriter = ExtraBeanPropertyWriter.newWriter(beanPropertyWriter);\n                    LOG.trace(\"Adding new property {}\", additionalPropertyWriter);\n                    newProperties.add(additionalPropertyWriter);\n                }\n            }\n        }\n        return newProperties;\n    }\n\n    private static boolean shouldBeReplacedByLink(BeanPropertyWriter propertyWriter) {\n        return propertyWriter != null && propertyWriter.getAnnotation(JsonIgnore.class) != null;\n    }\n\n    private static boolean shouldBeIgnored(BeanDescription beanDescription) {\n        JavaType type = beanDescription.getType();\n        Class<?> rawClass = type.getRawClass();\n        if (LOG.isTraceEnabled()) {\n            LOG.trace(\"Checking if it has to be ignored - {} / {}\", type, rawClass);\n            LOG.trace(\"Interfaces: {}\", getNames(ClassUtils.getAllInterfaces(rawClass)));\n            LOG.trace(\"Superclasses: {}\", getNames(ClassUtils.getAllSuperclasses(rawClass)));\n        }\n        return MethodHandler.class.isAssignableFrom(rawClass) || Proxy.class.isAssignableFrom(rawClass);\n    }\n\n    @Override\n    public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc,\n            JsonSerializer<?> serializer) {\n        if (HibernateProxy.class.isAssignableFrom(beanDesc.getBeanClass())) {\n            LOG.trace(\"Registering HibernateProxy unwrapping serializer for {}\", beanDesc.getBeanClass().getName());\n            return new HibernateProxyUnwrappingSerializer(serializer);\n        }\n        return serializer;\n    }\n\n    private static List<String> getNames(List<Class<?>> classes) {\n        return classes.stream().map(Class::getName).collect(Collectors.toList());\n    }\n\n    private static class HibernateProxyUnwrappingSerializer extends JsonSerializer<HibernateProxy> {\n\n        private final JsonSerializer<HibernateProxy> defaultSerializer;\n\n        @SuppressWarnings(\"unchecked\")\n        HibernateProxyUnwrappingSerializer(JsonSerializer<?> defaultSerializer) {\n            this.defaultSerializer = (JsonSerializer<HibernateProxy>) defaultSerializer;\n        }\n\n        @Override\n        public void serialize(HibernateProxy value, JsonGenerator gen, SerializerProvider serializers)\n                throws IOException {\n            if (value == null) {\n                gen.writeNull();\n                return;\n            }\n            LazyInitializer lazyInitializer = value.getHibernateLazyInitializer();\n            if (!lazyInitializer.isUninitialized()) {\n                Object implementation = lazyInitializer.getImplementation();\n                if (implementation != null) {\n                    LOG.debug(\"Unwrapping initialized HibernateProxy for entity {} (id={}) to actual type {}\",\n                            lazyInitializer.getEntityName(), lazyInitializer.getIdentifier(),\n                            implementation.getClass().getName());\n                    serializers.defaultSerializeValue(implementation, gen);\n                } else {\n                    LOG.debug(\n                            \"HibernateProxy for entity {} (id={}) getImplementation() returned null — serializing proxy as-is\",\n                            lazyInitializer.getEntityName(), lazyInitializer.getIdentifier());\n                    defaultSerializer.serialize(value, gen, serializers);\n                }\n            } else {\n                LOG.debug(\n                        \"Serializing uninitialized HibernateProxy for entity {} (id={}) without unwrapping — JSON may be empty\",\n                        lazyInitializer.getEntityName(), lazyInitializer.getIdentifier());\n                defaultSerializer.serialize(value, gen, serializers);\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/jackson/EntityJacksonAnnotationIntrospector.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl.jackson;\n\nimport com.fasterxml.jackson.databind.ext.Java7Support;\nimport com.fasterxml.jackson.databind.introspect.Annotated;\nimport com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;\n\n/**\n * Do not ignore property annotated with JsonIgnore as we manage this with a links property instead.\n */\npublic class EntityJacksonAnnotationIntrospector extends JacksonAnnotationIntrospector {\n\n    // taken from super class, except that we can directly set the field as we are running in a Java 7+ environment\n    // NOTE: loading of Java7 dependencies is encapsulated by handlers in Java7Support,\n    //  here we do not really need any handling; but for extra-safety use try-catch\n    private static final Java7Support _java7Helper = Java7Support.instance();\n\n    @Override\n    protected boolean _isIgnorable(Annotated a) {\n        Boolean b = _java7Helper.findTransient(a);\n        if (b != null) {\n            return b.booleanValue();\n        }\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/jackson/EntityMixin.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonInclude;\nimport com.fasterxml.jackson.databind.annotation.JsonAppend;\nimport org.bonitasoft.engine.business.data.impl.jackson.writer.LinkPropertyWriter;\n\n@JsonAppend(props = {\n        @JsonAppend.Prop(value = LinkPropertyWriter.class, name = EntityMixin.PROPERTY_LINK, include = JsonInclude.Include.NON_EMPTY)\n})\npublic abstract class EntityMixin {\n\n    public static final String PROPERTY_LINK = \"links\";\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/jackson/serializer/ExtraPropertyStringAbstractSerializer.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl.jackson.serializer;\n\nimport java.io.IOException;\n\nimport com.fasterxml.jackson.core.JsonGenerator;\nimport com.fasterxml.jackson.databind.JsonSerializer;\nimport com.fasterxml.jackson.databind.SerializerProvider;\n\npublic abstract class ExtraPropertyStringAbstractSerializer<T extends Object> extends JsonSerializer<T> {\n\n    public ExtraPropertyStringAbstractSerializer() {\n        // needed by Jackson\n    }\n\n    @Override\n    public void serialize(T value, JsonGenerator gen, SerializerProvider serializers) throws IOException {\n        gen.writeObject(convert(value));\n    }\n\n    protected abstract Object convert(T value);\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/jackson/serializer/ExtraPropertyStringListSerializer.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl.jackson.serializer;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.stream.Collectors;\n\npublic class ExtraPropertyStringListSerializer extends ExtraPropertyStringAbstractSerializer<List> {\n\n    @Override\n    protected Object convert(List value) {\n        return Optional.ofNullable(value).orElseGet(Collections::emptyList)\n                .stream()\n                .map(o -> Objects.toString(o, null))\n                .collect(Collectors.toList());\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/jackson/serializer/ExtraPropertyStringSerializer.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl.jackson.serializer;\n\nimport java.util.Objects;\n\npublic class ExtraPropertyStringSerializer extends ExtraPropertyStringAbstractSerializer<Object> {\n\n    @Override\n    protected Object convert(Object value) {\n        return Objects.toString(value, null);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/jackson/utils/ExtraPropertyUtils.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl.jackson.utils;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport com.fasterxml.jackson.databind.JavaType;\nimport com.fasterxml.jackson.databind.ser.BeanPropertyWriter;\nimport com.fasterxml.jackson.databind.type.CollectionType;\n\npublic class ExtraPropertyUtils {\n\n    private static final String STRING_SUFFIX = \"_string\";\n\n    private static final Set<String> numberTypes;\n\n    static {\n        numberTypes = new HashSet<>();\n        numberTypes.add(Long.class.getCanonicalName());\n        numberTypes.add(Float.class.getCanonicalName());\n        numberTypes.add(Double.class.getCanonicalName());\n    }\n\n    public static String getExtraPropertyName(BeanPropertyWriter propertyWriter) {\n        return propertyWriter.getName().concat(STRING_SUFFIX);\n    }\n\n    public static boolean shouldAddExtraProperty(BeanPropertyWriter writer) {\n        return shouldAddExtraProperty(writer.getType());\n    }\n\n    private static boolean shouldAddExtraProperty(JavaType javaType) {\n        if (javaType.getClass().isAssignableFrom(CollectionType.class)) {\n            CollectionType collectionType = (CollectionType) javaType;\n            return shouldAddExtraProperty(collectionType.getContentType());\n        }\n        return numberTypes.contains(javaType.getRawClass().getCanonicalName());\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/jackson/utils/Link.java",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl.jackson.utils;\n\nimport java.util.Objects;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class Link {\n\n    private final String rel;\n\n    private final String href;\n\n    public Link(final String rel, final String href) {\n        super();\n        this.rel = rel;\n        this.href = href;\n    }\n\n    public String getRel() {\n        return rel;\n    }\n\n    public String getHref() {\n        return href;\n    }\n\n    @Override\n    public String toString() {\n        return \"Link{\" +\n                \"rel='\" + rel + '\\'' +\n                \", href='\" + href + '\\'' +\n                '}';\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (!(o instanceof Link link))\n            return false;\n        return Objects.equals(rel, link.rel) &&\n                Objects.equals(href, link.href);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(rel, href);\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/jackson/utils/LinkUtils.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl.jackson.utils;\n\nimport java.util.LinkedHashSet;\n\nimport com.fasterxml.jackson.databind.ObjectWriter;\nimport com.fasterxml.jackson.databind.SerializerProvider;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class LinkUtils {\n\n    private static Logger LOG = LoggerFactory.getLogger(LinkUtils.class);\n\n    private static final String ATTRIBUTE_KEY_LINK_BASE_NAME = \"IgnoredToLinkSerializer$$ATTRIBUTE_KEY_LINK\";\n    private static final String ATTRIBUTE_KEY_URI_PATTERN = \"IgnoredToLinkSerializer$$PATTERN_URI\";\n\n    public static LinkedHashSet<Link> getLinksFromContext(Object value, SerializerProvider prov) {\n        LOG.trace(\"Retrieving links from context\");\n        String attributeKeyLink = getAttributeKeyLink(value);\n        LinkedHashSet<Link> links = (LinkedHashSet<Link>) prov.getAttribute(attributeKeyLink);\n        if (links == null) {\n            LOG.trace(\"No found links, initialize them in the context\");\n            links = new LinkedHashSet<>();\n            prov.setAttribute(attributeKeyLink, links);\n        }\n        LOG.trace(\"Links: {}\", links);\n        return links;\n    }\n\n    public static void addLinkToContext(Object value, Link link, SerializerProvider prov) {\n        LinkedHashSet<Link> links = getLinksFromContext(value, prov);\n        links.add(link);\n        LOG.trace(\"Added to context: {}\", link);\n    }\n\n    private static String getAttributeKeyLink(Object value) {\n        return ATTRIBUTE_KEY_LINK_BASE_NAME + \"$$\" + value.getClass().getCanonicalName() + \"@\" + value.hashCode();\n    }\n\n    public static String getUriPatternFromContext(SerializerProvider prov) {\n        return (String) prov.getAttribute(ATTRIBUTE_KEY_URI_PATTERN);\n    }\n\n    public static ObjectWriter putUriPatternIntoContext(ObjectWriter writer, String uriPattern) {\n        return writer.withAttribute(ATTRIBUTE_KEY_URI_PATTERN, uriPattern);\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/jackson/writer/ExtraBeanPropertyWriter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl.jackson.writer;\n\nimport com.fasterxml.jackson.databind.JavaType;\nimport com.fasterxml.jackson.databind.JsonSerializer;\nimport com.fasterxml.jackson.databind.PropertyName;\nimport com.fasterxml.jackson.databind.ser.BeanPropertyWriter;\nimport com.fasterxml.jackson.databind.type.CollectionType;\nimport org.bonitasoft.engine.business.data.impl.jackson.serializer.ExtraPropertyStringListSerializer;\nimport org.bonitasoft.engine.business.data.impl.jackson.serializer.ExtraPropertyStringSerializer;\nimport org.bonitasoft.engine.business.data.impl.jackson.utils.ExtraPropertyUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class ExtraBeanPropertyWriter extends BeanPropertyWriter {\n\n    private static Logger LOG = LoggerFactory.getLogger(ExtraBeanPropertyWriter.class);\n\n    private ExtraBeanPropertyWriter(BeanPropertyWriter base) {\n        super(base, new PropertyName(ExtraPropertyUtils.getExtraPropertyName(base)));\n    }\n\n    public static ExtraBeanPropertyWriter newWriter(BeanPropertyWriter base) {\n        LOG.trace(\"Creating new instance\");\n        JavaType initialJavaType = base.getType();\n        LOG.trace(\"Initial java type: {}\", initialJavaType);\n        final JsonSerializer serializer;\n        if (initialJavaType.getClass().isAssignableFrom(CollectionType.class)) {\n            serializer = new ExtraPropertyStringListSerializer();\n        } else {\n            serializer = new ExtraPropertyStringSerializer();\n        }\n\n        ExtraBeanPropertyWriter writer = new ExtraBeanPropertyWriter(base);\n        writer.assignSerializer(serializer);\n        return writer;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/jackson/writer/IgnoredPropertyWriter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl.jackson.writer;\n\nimport java.util.Objects;\n\nimport com.fasterxml.jackson.core.JsonGenerator;\nimport com.fasterxml.jackson.core.JsonStreamContext;\nimport com.fasterxml.jackson.databind.SerializerProvider;\nimport com.fasterxml.jackson.databind.ser.BeanPropertyWriter;\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.bonitasoft.engine.business.data.impl.jackson.utils.Link;\nimport org.bonitasoft.engine.business.data.impl.jackson.utils.LinkUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * This implementation does not serialize fields by calling the JsonGenerator. Instead, it creates and stores link\n * instances in the Jackson per-call context.<br>\n * The actual serialization is delegated to the {@link LinkPropertyWriter} class.\n */\npublic class IgnoredPropertyWriter extends BeanPropertyWriter {\n\n    private static Logger LOG = LoggerFactory.getLogger(IgnoredPropertyWriter.class);\n\n    private final String ignoredFieldName;\n\n    public IgnoredPropertyWriter(BeanPropertyWriter base) {\n        super(base);\n        this.ignoredFieldName = base.getName();\n    }\n\n    @Override\n    public void serializeAsField(Object bean, JsonGenerator gen, SerializerProvider prov) throws Exception {\n        LOG.trace(\"Managing ignored field {} with value {}\", ignoredFieldName, bean);\n\n        Object currentlyProcessedObject = getCurrentObjectFromContext(gen);\n        if (currentlyProcessedObject instanceof Entity) {\n            LOG.trace(\"Parent is an Entity, so managing links\");\n            String uriPattern = LinkUtils.getUriPatternFromContext(prov);\n            LOG.trace(\"URI Pattern retrieved from the context: {}\", uriPattern);\n\n            Link link = new Link(ignoredFieldName,\n                    buildURI((Entity) currentlyProcessedObject, uriPattern, ignoredFieldName));\n            LinkUtils.addLinkToContext(currentlyProcessedObject, link, prov);\n        }\n    }\n\n    private static Object getCurrentObjectFromContext(JsonGenerator gen) {\n        JsonStreamContext outputContext = gen.getOutputContext();\n        Object currentObj = outputContext.getCurrentValue();\n        LOG.trace(\"Current Object from context: {}\", currentObj);\n        return currentObj;\n    }\n\n    private static String buildURI(final Entity parent, final String uriPattern, final String fieldName) {\n        String uri = uriPattern.replace(\"{className}\", parent.getClass().getName());\n        uri = uri.replace(\"{id}\", Objects.toString(parent.getPersistenceId(), null));\n        return uri.replace(\"{field}\", fieldName);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/jackson/writer/LinkPropertyWriter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl.jackson.writer;\n\nimport java.util.LinkedHashSet;\n\nimport com.fasterxml.jackson.core.JsonGenerator;\nimport com.fasterxml.jackson.databind.JavaType;\nimport com.fasterxml.jackson.databind.SerializerProvider;\nimport com.fasterxml.jackson.databind.cfg.MapperConfig;\nimport com.fasterxml.jackson.databind.introspect.AnnotatedClass;\nimport com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;\nimport com.fasterxml.jackson.databind.ser.VirtualBeanPropertyWriter;\nimport com.fasterxml.jackson.databind.util.Annotations;\nimport org.bonitasoft.engine.business.data.impl.jackson.utils.Link;\nimport org.bonitasoft.engine.business.data.impl.jackson.utils.LinkUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class LinkPropertyWriter extends VirtualBeanPropertyWriter {\n\n    private static Logger LOG = LoggerFactory.getLogger(LinkPropertyWriter.class);\n\n    // Needed by Jackson\n    protected LinkPropertyWriter() {\n        super();\n    }\n\n    private LinkPropertyWriter(BeanPropertyDefinition propDef, Annotations ctxtAnn, JavaType type) {\n        super(propDef, ctxtAnn, type);\n    }\n\n    @Override\n    protected Object value(Object bean, JsonGenerator jgen, SerializerProvider prov) {\n        LOG.trace(\"Post processing links for {}\", bean);\n        // valid cast we only apply this writer to Entity bean\n        LinkedHashSet<Link> links = LinkUtils.getLinksFromContext(bean, prov);\n        LOG.trace(\"Retrieved links: {}\", links);\n        return links;\n    }\n\n    @Override\n    public VirtualBeanPropertyWriter withConfig(MapperConfig<?> config,\n            AnnotatedClass declaringClass, BeanPropertyDefinition propDef,\n            JavaType type) {\n        return new LinkPropertyWriter(propDef, declaringClass.getAnnotations(), type);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/model/SDataRetentionBdmTracking.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.model;\n\nimport javax.persistence.Cacheable;\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * Persistent entity that tracks the creation and last modification timestamps\n * of BDM object instances for data retention purposes.\n * <p>Each row records when a specific BDM object instance (identified by its\n * {@code dataId} and {@code dataClassname}) was created and last modified.\n * These timestamps are used by the data retention service to determine whether\n * a BDM object instance has exceeded its configured retention period.\n * <p>Mapped to the {@code data_retention_bdm_tracking} table in the Bonita engine schema.\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@Builder\n@Entity\n@Table(name = \"data_retention_bdm_tracking\")\n// Disabling cache because this entity is not expected to be accessed\n// frequently (read by the data retention service executed by a CRON job)\n@Cacheable(false)\npublic class SDataRetentionBdmTracking implements PersistentObject {\n\n    @Id\n    private long id;\n\n    /**\n     * The persistence ID of the tracked BDM object instance.\n     */\n    @Column(name = \"data_id\", nullable = false)\n    private long dataId;\n\n    /**\n     * Fully qualified Java class name of the BDM object type.\n     * <p>Example: {@code com.company.model.ContratClient}\n     */\n    @Column(name = \"data_classname\", nullable = false)\n    private String dataClassname;\n\n    /**\n     * Epoch timestamp (milliseconds) when the BDM object instance was first created.\n     */\n    @Column(name = \"created_at\", nullable = false)\n    private long createdAt;\n\n    /**\n     * Epoch timestamp (milliseconds) when the BDM object instance was last modified.\n     */\n    @Column(name = \"last_modified_at\", nullable = false)\n    private long lastModifiedAt;\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/model/SDataRetentionConfig.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.model;\n\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.EnumType;\nimport javax.persistence.Enumerated;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * Persistent entity representing a data retention rule configured by a Platform Administrator\n * for a specific BDM object type.\n * <p>Each instance of this class defines when and how BDM object instances of a given Java type\n * should be automatically deleted by the data retention service. One rule exists\n * per BDM object type — the {@code dataClassname} column carries a unique constraint.\n * <p>A rule is considered active as soon as it is persisted. There is no enable/disable toggle:\n * to stop retention processing for an object type, the rule must be deleted entirely.\n * <p>Mapped to the {@code data_retention_config} table in the Bonita engine schema.\n * This table is never present in the BDM schema.\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@Builder\n@Entity\n@Table(name = \"data_retention_config\")\npublic class SDataRetentionConfig implements PersistentObject {\n\n    @Id\n    private long id;\n\n    /**\n     * Fully qualified Java class name of the BDM object type this rule applies to.\n     * <p>Example: {@code com.company.model.ContratClient}\n     * <p>This value is derived from the deployed BDM model and must match an existing\n     * object type at rule creation time. If the BDM is redeployed and this class no longer\n     * exists in the new model, the rule is automatically deleted.\n     * <p>Unique — only one retention rule may exist per BDM object type.\n     */\n    @Column(name = \"data_classname\", nullable = false)\n    private String dataClassname;\n\n    /**\n     * The date field used as the starting point for the retention period calculation.\n     * <p>Possible values:\n     * <ul>\n     * <li>{@code CREATION} — retention is calculated from the date the BDM object\n     * instance was first created, as recorded in {@code data_retention_bdm_tracking.created_at}.\n     * The clock never resets, regardless of subsequent modifications.</li>\n     * <li>{@code LAST_UPDATE} — retention is calculated from the date the BDM object\n     * instance was last modified, as recorded in {@code data_retention_bdm_tracking.last_modified_at}.\n     * The clock resets to zero on every modification.</li>\n     * </ul>\n     *\n     * @see SReferenceDate\n     */\n    @Enumerated(EnumType.STRING)\n    @Column(name = \"reference_date\", nullable = false)\n    private SReferenceDate referenceDate;\n\n    /**\n     * Duration of the retention period, expressed in days.\n     * Must be a positive integer.\n     */\n    @Column(name = \"retention_days\", nullable = false)\n    private int retentionDays;\n\n    @Column(name = \"created_at\", nullable = false)\n    private long createdAt;\n\n    @Column(name = \"updated_at\", nullable = false)\n    private long updatedAt;\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/model/SReferenceDate.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.model;\n\n/**\n * Defines which date field on a BDM object instance is used as the starting point\n * for the data retention period calculation.\n *\n * @see SDataRetentionConfig#getReferenceDate()\n */\npublic enum SReferenceDate {\n\n    /**\n     * Retention is calculated from the date the BDM object instance was first created.\n     * The clock never resets, regardless of subsequent modifications.\n     */\n    CREATION,\n\n    /**\n     * Retention is calculated from the date the BDM object instance was last modified.\n     * The clock resets to zero on every modification.\n     */\n    LAST_UPDATE\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/resources/README.md",
    "content": "# How to use\n\n\nThis zip contains required files to interact with your deployed business object in your application.\n\n\nTo do that:\n\n* Create a new maven project using `example-pom.xml`\n* Extract the two jars `bdm-dao.jar` and `bdm-model.jar` from the zip file, next to your `pom.xml`\n* Make sure the engine is accessible using [the HTTP API](https://documentation.ofelia.com/bonita/latest/configure-client-of-bonita-bpm-engine#_server_configuration_to_accept_http_connection).\n\nHere  is an example on how to retrieve the DAO to find your objects\n```java\n\n// configure APIClient to connect via HTTP\nHashMap<String, String> params = new HashMap<>();\nparams.put(\"server.url\", \"http://localhost:8080/\");\nparams.put(\"application.name\", \"bonita\");\nAPITypeManager.setAPITypeAndParams(ApiAccessType.HTTP, params);\n// connect to the engine\nAPIClient apiClient = new APIClient();\napiClient.login(\"walter.bates\",\"bpm\");\n\n// retrieve an instance of the DAO\nMyBusinessObjectDAO dao = apiClient.getDAO(MyBusinessObjectDAO.class);\n\n// use the DAO\nList<MyBusinessObject> myBusinessObjects = dao.find(0, 100);\n\napiClient.logout();\n```"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/resources/example-pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.mycompagny</groupId>\n    <artifactId>my-application</artifactId>\n    <version>1.0.0</version>\n    <packaging>jar</packaging>\n\n    <properties>\n        <bonita.version>PUT_BONITA_VERSION_HERE</bonita.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>com.mycompagny</groupId>\n            <artifactId>bdm-dao</artifactId>\n            <version>1.0.0</version>\n            <scope>system</scope>\n            <systemPath>${basedir}/bdm-dao.jar</systemPath>\n        </dependency>\n        <dependency>\n            <groupId>com.mycompagny</groupId>\n            <artifactId>bdm-model</artifactId>\n            <version>1.0.0</version>\n            <scope>system</scope>\n            <systemPath>${basedir}/bdm-model.jar</systemPath>\n        </dependency>\n        <dependency>\n            <groupId>org.bonitasoft.engine</groupId>\n            <artifactId>bonita-client</artifactId>\n            <version>${bonita.version}</version>\n        </dependency>\n        <!-- required when using lazy relations-->\n        <dependency>\n            <groupId>org.javassist</groupId>\n            <artifactId>javassist</artifactId>\n            <version>3.18.1-GA</version>\n        </dependency>\n    </dependencies>\n\n</project>"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/main/resources/org/bonitasoft/engine/business/data/model/impl/hibernate/data-retention-bdm-tracking.queries.hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\"\n                                   \"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd\">\n<hibernate-mapping auto-import=\"false\">\n\n  <query name=\"getDataRetentionBdmTrackingByDataIdAndClassname\">\n    SELECT t\n    FROM org.bonitasoft.engine.business.data.model.SDataRetentionBdmTracking AS t\n    WHERE t.dataId = :dataId AND t.dataClassname = :dataClassname\n  </query>\n\n  <query name=\"getDataRetentionBdmTrackingByClassname\">\n    SELECT t\n    FROM org.bonitasoft.engine.business.data.model.SDataRetentionBdmTracking AS t\n    WHERE t.dataClassname = :dataClassname\n  </query>\n\n  <query name=\"getExpiredTrackingByClassnameAndCreatedAt\">\n    SELECT t\n    FROM org.bonitasoft.engine.business.data.model.SDataRetentionBdmTracking AS t\n    WHERE t.dataClassname = :dataClassname AND t.createdAt &lt;= :deadline\n  </query>\n\n  <query name=\"getExpiredTrackingByClassnameAndLastModifiedAt\">\n    SELECT t\n    FROM org.bonitasoft.engine.business.data.model.SDataRetentionBdmTracking AS t\n    WHERE t.dataClassname = :dataClassname AND t.lastModifiedAt &lt;= :deadline\n  </query>\n\n  <query name=\"deleteDataRetentionBdmTrackingByDataIdAndClassname\">\n    DELETE\n    FROM org.bonitasoft.engine.business.data.model.SDataRetentionBdmTracking AS t\n    WHERE t.dataId = :dataId AND t.dataClassname = :dataClassname\n  </query>\n</hibernate-mapping>\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/java/com/company/model/Address.java",
    "content": "/**\n * Copyright (C) 2017 BonitaSoft S.A.\n * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage com.company.model;\n\nimport java.util.List;\n\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.Id;\nimport javax.persistence.Version;\n\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport org.bonitasoft.engine.bdm.Entity;\n\npublic class Address implements Entity {\n\n    @Id\n    @GeneratedValue\n    private Long persistenceId;\n    @Version\n    private Long persistenceVersion;\n\n    private String street;\n\n    private Float number;\n\n    private List<Double> floors;\n\n    @JsonIgnore\n    private String doorCode;\n\n    public void setPersistenceId(Long persistenceId) {\n        this.persistenceId = persistenceId;\n    }\n\n    @Override\n    public Long getPersistenceId() {\n        return persistenceId;\n    }\n\n    @Override\n    public Long getPersistenceVersion() {\n        return persistenceVersion;\n    }\n\n    public void setPersistenceVersion(Long persistenceVersion) {\n        this.persistenceVersion = persistenceVersion;\n    }\n\n    public String getStreet() {\n        return street;\n    }\n\n    public void setStreet(String street) {\n        this.street = street;\n    }\n\n    public String getDoorCode() {\n        return doorCode;\n    }\n\n    public void setDoorCode(String doorCode) {\n        this.doorCode = doorCode;\n    }\n\n    public Float getNumber() {\n        return number;\n    }\n\n    public void setNumber(Float number) {\n        this.number = number;\n    }\n\n    public List<Double> getFloors() {\n        return floors;\n    }\n\n    public void setFloors(List<Double> floors) {\n        this.floors = floors;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/java/com/company/model/Person.java",
    "content": "/**\n * Copyright (C) 2015 BonitaSoft S.A.\n * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage com.company.model;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\nimport javax.persistence.CascadeType;\nimport javax.persistence.Column;\nimport javax.persistence.ElementCollection;\nimport javax.persistence.FetchType;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.Id;\nimport javax.persistence.JoinColumn;\nimport javax.persistence.NamedQueries;\nimport javax.persistence.NamedQuery;\nimport javax.persistence.OneToMany;\nimport javax.persistence.OrderColumn;\nimport javax.persistence.Table;\nimport javax.persistence.Temporal;\nimport javax.persistence.TemporalType;\nimport javax.persistence.Version;\n\n/**\n *\n */\n@javax.persistence.Entity(name = \"Person\")\n@Table(name = \"PERSON\")\n@NamedQueries({\n        @NamedQuery(name = \"Person.findByName\", query = \"SELECT p\\nFROM Person p\\nWHERE p.name= :name\\nORDER BY p.persistenceId\"),\n        @NamedQuery(name = \"Person.findByAge\", query = \"SELECT p\\nFROM Person p\\nWHERE p.age= :age\\nORDER BY p.persistenceId\"),\n        @NamedQuery(name = \"Person.findByBirthday\", query = \"SELECT p\\nFROM Person p\\nWHERE p.birthday= :birthday\\nORDER BY p.persistenceId\"),\n        @NamedQuery(name = \"Person.findByHasMobile\", query = \"SELECT p\\nFROM Person p\\nWHERE p.hasMobile= :hasMobile\\nORDER BY p.persistenceId\"),\n        @NamedQuery(name = \"Person.find\", query = \"SELECT p\\nFROM Person p\\nORDER BY p.persistenceId\"),\n        @NamedQuery(name = \"Person.findById\", query = \"SELECT p \\nFROM Person p \\nWHERE p.persistenceId = :persistenceId\"),\n        @NamedQuery(name = \"Person.findAdults\", query = \"SELECT p \\nFROM Person p \\nWHERE p.age > 18\\nORDER BY p.persistenceId ASC\"),\n        @NamedQuery(name = \"Person.query1\", query = \"SELECT p \\nFROM Person p \\nWHERE p.hasMobile = :hasMobile\\nORDER BY p.persistenceId ASC\"),\n        @NamedQuery(name = \"Person.findByRange\", query = \"SELECT p \\nFROM Person p \\nWHERE p.birthday >= :date1 AND p.birthday <= :date2\\nORDER BY p.persistenceId ASC\")\n})\npublic class Person implements org.bonitasoft.engine.bdm.Entity {\n\n    @Id\n    @GeneratedValue\n    private Long persistenceId;\n    @Version\n    private Long persistenceVersion;\n    @Column(name = \"NAME\", nullable = true, length = 255)\n    private String name;\n    @Column(name = \"AGE\", nullable = true)\n    private Integer age;\n    @OneToMany(orphanRemoval = true, fetch = FetchType.EAGER, cascade = CascadeType.ALL)\n    @JoinColumn(name = \"PERSON_PID\", nullable = false)\n    @OrderColumn\n    private List<com.company.model.Phone> phones = new ArrayList<com.company.model.Phone>(10);\n    @Column(name = \"BIRTHDAY\", nullable = true)\n    @Temporal(TemporalType.TIMESTAMP)\n    private Date birthday;\n    @Column(name = \"HASMOBILE\", nullable = false)\n    private Boolean hasMobile;\n    @ElementCollection(fetch = FetchType.EAGER)\n    @OrderColumn\n    @Column(name = \"BOOLS\", nullable = true)\n    private List<Boolean> bools = new ArrayList<Boolean>(10);\n\n    public Person() {\n    }\n\n    public void setPersistenceId(Long persistenceId) {\n        this.persistenceId = persistenceId;\n    }\n\n    public Long getPersistenceId() {\n        return persistenceId;\n    }\n\n    public void setPersistenceVersion(Long persistenceVersion) {\n        this.persistenceVersion = persistenceVersion;\n    }\n\n    public Long getPersistenceVersion() {\n        return persistenceVersion;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setAge(Integer age) {\n        this.age = age;\n    }\n\n    public Integer getAge() {\n        return age;\n    }\n\n    public void setPhones(List<com.company.model.Phone> phones) {\n        if (this.phones == null) {\n            this.phones = phones;\n        } else {\n            this.phones.clear();\n            this.phones.addAll(phones);\n        }\n    }\n\n    public List<com.company.model.Phone> getPhones() {\n        return phones;\n    }\n\n    public void addToPhones(com.company.model.Phone addTo) {\n        List phones = getPhones();\n        phones.add(addTo);\n    }\n\n    public void removeFromPhones(com.company.model.Phone removeFrom) {\n        List phones = getPhones();\n        phones.remove(removeFrom);\n    }\n\n    public void setBirthday(Date birthday) {\n        this.birthday = birthday;\n    }\n\n    public Date getBirthday() {\n        return birthday;\n    }\n\n    public void setHasMobile(Boolean hasMobile) {\n        this.hasMobile = hasMobile;\n    }\n\n    public Boolean isHasMobile() {\n        return hasMobile;\n    }\n\n    public void setBools(List<Boolean> bools) {\n        if (this.bools == null) {\n            this.bools = bools;\n        } else {\n            this.bools.clear();\n            this.bools.addAll(bools);\n        }\n    }\n\n    public List<Boolean> getBools() {\n        return bools;\n    }\n\n    public void addToBools(Boolean addTo) {\n        List bools = getBools();\n        bools.add(addTo);\n    }\n\n    public void removeFromBools(Boolean removeFrom) {\n        List bools = getBools();\n        bools.remove(removeFrom);\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        Person other = ((Person) obj);\n        if (persistenceId == null) {\n            if (other.persistenceId != null) {\n                return false;\n            }\n        } else {\n            if (!persistenceId.equals(other.persistenceId)) {\n                return false;\n            }\n        }\n        if (persistenceVersion == null) {\n            if (other.persistenceVersion != null) {\n                return false;\n            }\n        } else {\n            if (!persistenceVersion.equals(other.persistenceVersion)) {\n                return false;\n            }\n        }\n        if (name == null) {\n            if (other.name != null) {\n                return false;\n            }\n        } else {\n            if (!name.equals(other.name)) {\n                return false;\n            }\n        }\n        if (age == null) {\n            if (other.age != null) {\n                return false;\n            }\n        } else {\n            if (!age.equals(other.age)) {\n                return false;\n            }\n        }\n        if (phones == null) {\n            if (other.phones != null) {\n                return false;\n            }\n        } else {\n            if (!phones.equals(other.phones)) {\n                return false;\n            }\n        }\n        if (birthday == null) {\n            if (other.birthday != null) {\n                return false;\n            }\n        } else {\n            if (!birthday.equals(other.birthday)) {\n                return false;\n            }\n        }\n        if (hasMobile == null) {\n            if (other.hasMobile != null) {\n                return false;\n            }\n        } else {\n            if (!hasMobile.equals(other.hasMobile)) {\n                return false;\n            }\n        }\n        if (bools == null) {\n            if (other.bools != null) {\n                return false;\n            }\n        } else {\n            if (!bools.equals(other.bools)) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        int persistenceIdCode = 0;\n        if (persistenceId != null) {\n            persistenceIdCode = persistenceId.hashCode();\n        }\n        result = ((prime * result) + persistenceIdCode);\n        int persistenceVersionCode = 0;\n        if (persistenceVersion != null) {\n            persistenceVersionCode = persistenceVersion.hashCode();\n        }\n        result = ((prime * result) + persistenceVersionCode);\n        int nameCode = 0;\n        if (name != null) {\n            nameCode = name.hashCode();\n        }\n        result = ((prime * result) + nameCode);\n        int ageCode = 0;\n        if (age != null) {\n            ageCode = age.hashCode();\n        }\n        result = ((prime * result) + ageCode);\n        int phonesCode = 0;\n        if (phones != null) {\n            phonesCode = phones.hashCode();\n        }\n        result = ((prime * result) + phonesCode);\n        int birthdayCode = 0;\n        if (birthday != null) {\n            birthdayCode = birthday.hashCode();\n        }\n        result = ((prime * result) + birthdayCode);\n        int hasMobileCode = 0;\n        if (hasMobile != null) {\n            hasMobileCode = hasMobile.hashCode();\n        }\n        result = ((prime * result) + hasMobileCode);\n        int boolsCode = 0;\n        if (bools != null) {\n            boolsCode = bools.hashCode();\n        }\n        result = ((prime * result) + boolsCode);\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/java/com/company/model/PersonWithDetails.java",
    "content": "/**\n * Copyright (C) 2017 BonitaSoft S.A.\n * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage com.company.model;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\nimport javassist.util.proxy.MethodHandler;\n\nimport javax.persistence.CascadeType;\nimport javax.persistence.Column;\nimport javax.persistence.ElementCollection;\nimport javax.persistence.FetchType;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.Id;\nimport javax.persistence.JoinColumn;\nimport javax.persistence.NamedQueries;\nimport javax.persistence.NamedQuery;\nimport javax.persistence.OneToMany;\nimport javax.persistence.OneToOne;\nimport javax.persistence.OrderColumn;\nimport javax.persistence.Table;\nimport javax.persistence.Temporal;\nimport javax.persistence.TemporalType;\nimport javax.persistence.Version;\n\nimport com.company.model.javassist.MethodHandlerImpl;\nimport com.company.model.javassist.ProxyImpl;\nimport com.company.model.javassist.ProxyObjectImpl;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\n\n@javax.persistence.Entity(name = \"Person\")\n@Table(name = \"PERSON\")\n@NamedQueries({\n        @NamedQuery(name = \"Person.findByName\", query = \"SELECT p\\nFROM Person p\\nWHERE p.name= :name\\nORDER BY p.persistenceId\"),\n        @NamedQuery(name = \"Person.findByAge\", query = \"SELECT p\\nFROM Person p\\nWHERE p.age= :age\\nORDER BY p.persistenceId\"),\n        @NamedQuery(name = \"Person.findByBirthday\", query = \"SELECT p\\nFROM Person p\\nWHERE p.birthday= :birthday\\nORDER BY p.persistenceId\"),\n        @NamedQuery(name = \"Person.findByHasMobile\", query = \"SELECT p\\nFROM Person p\\nWHERE p.hasMobile= :hasMobile\\nORDER BY p.persistenceId\"),\n        @NamedQuery(name = \"Person.find\", query = \"SELECT p\\nFROM Person p\\nORDER BY p.persistenceId\"),\n        @NamedQuery(name = \"Person.findById\", query = \"SELECT p \\nFROM Person p \\nWHERE p.persistenceId = :persistenceId\"),\n        @NamedQuery(name = \"Person.findAdults\", query = \"SELECT p \\nFROM Person p \\nWHERE p.age > 18\\nORDER BY p.persistenceId ASC\"),\n        @NamedQuery(name = \"Person.query1\", query = \"SELECT p \\nFROM Person p \\nWHERE p.hasMobile = :hasMobile\\nORDER BY p.persistenceId ASC\"),\n        @NamedQuery(name = \"Person.findByRange\", query = \"SELECT p \\nFROM Person p \\nWHERE p.birthday >= :date1 AND p.birthday <= :date2\\nORDER BY p.persistenceId ASC\")\n})\npublic class PersonWithDetails implements org.bonitasoft.engine.bdm.Entity {\n\n    @Id\n    @GeneratedValue\n    private Long persistenceId;\n    @Version\n    private Long persistenceVersion;\n    @Column(name = \"NAME\", nullable = true, length = 255)\n    private String name;\n    @Column(name = \"AGE\", nullable = true)\n    private Integer age;\n\n    @JsonIgnore\n    private Phone secretPhone = null;\n\n    @OneToMany(orphanRemoval = true, fetch = FetchType.EAGER, cascade = CascadeType.ALL)\n    @JoinColumn(name = \"PERSON_PID\", nullable = false)\n    @OrderColumn\n    private List<Phone> phones = new ArrayList<Phone>(10);\n    @Column(name = \"BIRTHDAY\", nullable = true)\n    @Temporal(TemporalType.TIMESTAMP)\n    private Date birthday;\n    @Column(name = \"HASMOBILE\", nullable = false)\n    private Boolean hasMobile;\n    @ElementCollection(fetch = FetchType.EAGER)\n    @OrderColumn\n    @Column(name = \"BOOLS\", nullable = true)\n    private List<Boolean> bools = new ArrayList<Boolean>(10);\n\n    @ElementCollection(fetch = FetchType.LAZY)\n    @OrderColumn\n    @Column(name = \"TOIGNORE\", nullable = true)\n    @JsonIgnore\n    private List<Long> toIgnores = new ArrayList<>(10);\n\n    @OneToOne(orphanRemoval = true, fetch = FetchType.EAGER, cascade = CascadeType.ALL)\n    @JoinColumn(name = \"PERSON_PID\", nullable = false)\n    private Address address1;\n\n    @OneToOne(orphanRemoval = true, fetch = FetchType.EAGER, cascade = CascadeType.ALL)\n    @JoinColumn(name = \"PERSON_PID\", nullable = false)\n    private Address address2;\n\n    private List<Long> toIncludes = new ArrayList<>(10);\n\n    private MethodHandler methodHandlerObject;\n\n    private List<Object> proxys = new ArrayList<>(10);\n\n    public PersonWithDetails() {\n        // needed by jackson (otherwise, add an annotation for constructor)\n    }\n\n    public void setPersistenceId(Long persistenceId) {\n        this.persistenceId = persistenceId;\n    }\n\n    public Long getPersistenceId() {\n        return persistenceId;\n    }\n\n    public void setPersistenceVersion(Long persistenceVersion) {\n        this.persistenceVersion = persistenceVersion;\n    }\n\n    public Long getPersistenceVersion() {\n        return persistenceVersion;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setAge(Integer age) {\n        this.age = age;\n    }\n\n    public Integer getAge() {\n        return age;\n    }\n\n    public void setPhones(List<Phone> phones) {\n        if (this.phones == null) {\n            this.phones = phones;\n        } else {\n            this.phones.clear();\n            this.phones.addAll(phones);\n        }\n    }\n\n    public List<Phone> getPhones() {\n        return phones;\n    }\n\n    public void addToPhones(Phone addTo) {\n        List phones = getPhones();\n        phones.add(addTo);\n    }\n\n    public void removeFromPhones(Phone removeFrom) {\n        List phones = getPhones();\n        phones.remove(removeFrom);\n    }\n\n    public void setBirthday(Date birthday) {\n        this.birthday = birthday;\n    }\n\n    public Date getBirthday() {\n        return birthday;\n    }\n\n    public void setHasMobile(Boolean hasMobile) {\n        this.hasMobile = hasMobile;\n    }\n\n    public Boolean isHasMobile() {\n        return hasMobile;\n    }\n\n    public void setBools(List<Boolean> bools) {\n        if (this.bools == null) {\n            this.bools = bools;\n        } else {\n            this.bools.clear();\n            this.bools.addAll(bools);\n        }\n    }\n\n    public List<Boolean> getBools() {\n        return bools;\n    }\n\n    public void addToBools(Boolean addTo) {\n        List bools = getBools();\n        bools.add(addTo);\n    }\n\n    public void removeFromBools(Boolean removeFrom) {\n        List bools = getBools();\n        bools.remove(removeFrom);\n    }\n\n    public List<Long> getToIgnores() {\n        return toIgnores;\n    }\n\n    public void addToIgnores(Long addTo) {\n        toIgnores.add(addTo);\n    }\n\n    public List<Long> getToIncludes() {\n        return toIncludes;\n    }\n\n    public void setToIncludes(List<Long> toIncludes) {\n        this.toIncludes = toIncludes;\n    }\n\n    public void addToIncludes(Long addTo) {\n        toIncludes.add(addTo);\n    }\n\n    public Boolean getHasMobile() {\n        return hasMobile;\n    }\n\n    public void setToIgnores(List<Long> toIgnores) {\n        this.toIgnores = toIgnores;\n    }\n\n    public Phone getSecretPhone() {\n        return secretPhone;\n    }\n\n    public void setSecretPhone(Phone secretPhone) {\n        this.secretPhone = secretPhone;\n    }\n\n    public Address getAddress1() {\n        return address1;\n    }\n\n    public void setAddress1(Address address1) {\n        this.address1 = address1;\n    }\n\n    public Address getAddress2() {\n        return address2;\n    }\n\n    public void setAddress2(Address address2) {\n        this.address2 = address2;\n    }\n\n    public MethodHandler getMethodHandlerObject() {\n        return methodHandlerObject;\n    }\n\n    public void setMethodHandlerObject(MethodHandler methodHandlerObject) {\n        this.methodHandlerObject = methodHandlerObject;\n    }\n\n    public void addToProxysAsMethodHandler(Object object) {\n        proxys.add(new MethodHandlerImpl(object));\n    }\n\n    public void addToProxysAsProxyImpl(Object object) {\n        proxys.add(new ProxyImpl(object));\n    }\n\n    public void addToProxysAsProxyObjectImpl(Object object) {\n        proxys.add(new ProxyObjectImpl(object));\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/java/com/company/model/Phone.java",
    "content": "/**\n * Copyright (C) 2015 BonitaSoft S.A.\n * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage com.company.model;\n\nimport javax.persistence.Column;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.Id;\nimport javax.persistence.NamedQueries;\nimport javax.persistence.NamedQuery;\nimport javax.persistence.Table;\nimport javax.persistence.Version;\n\n/**\n *\n */\n@javax.persistence.Entity(name = \"Phone\")\n@Table(name = \"PHONE\")\n@NamedQueries({\n        @NamedQuery(name = \"Phone.findByNumber\", query = \"SELECT p\\nFROM Phone p\\nWHERE p.number= :number\\nORDER BY p.persistenceId\"),\n        @NamedQuery(name = \"Phone.find\", query = \"SELECT p\\nFROM Phone p\\nORDER BY p.persistenceId\")\n})\npublic class Phone\n        implements org.bonitasoft.engine.bdm.Entity {\n\n    @Id\n    @GeneratedValue\n    private Long persistenceId;\n    @Version\n    private Long persistenceVersion;\n    @Column(name = \"NUMBER\", nullable = true, length = 255)\n    private String number;\n\n    public Phone() {\n    }\n\n    public void setPersistenceId(Long persistenceId) {\n        this.persistenceId = persistenceId;\n    }\n\n    public Long getPersistenceId() {\n        return persistenceId;\n    }\n\n    public void setPersistenceVersion(Long persistenceVersion) {\n        this.persistenceVersion = persistenceVersion;\n    }\n\n    public Long getPersistenceVersion() {\n        return persistenceVersion;\n    }\n\n    public void setNumber(String number) {\n        this.number = number;\n    }\n\n    public String getNumber() {\n        return number;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        Phone other = ((Phone) obj);\n        if (persistenceId == null) {\n            if (other.persistenceId != null) {\n                return false;\n            }\n        } else {\n            if (!persistenceId.equals(other.persistenceId)) {\n                return false;\n            }\n        }\n        if (persistenceVersion == null) {\n            if (other.persistenceVersion != null) {\n                return false;\n            }\n        } else {\n            if (!persistenceVersion.equals(other.persistenceVersion)) {\n                return false;\n            }\n        }\n        if (number == null) {\n            if (other.number != null) {\n                return false;\n            }\n        } else {\n            if (!number.equals(other.number)) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        int persistenceIdCode = 0;\n        if (persistenceId != null) {\n            persistenceIdCode = persistenceId.hashCode();\n        }\n        result = ((prime * result) + persistenceIdCode);\n        int persistenceVersionCode = 0;\n        if (persistenceVersion != null) {\n            persistenceVersionCode = persistenceVersion.hashCode();\n        }\n        result = ((prime * result) + persistenceVersionCode);\n        int numberCode = 0;\n        if (number != null) {\n            numberCode = number.hashCode();\n        }\n        result = ((prime * result) + numberCode);\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/java/com/company/model/javassist/MethodHandlerImpl.java",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage com.company.model.javassist;\n\nimport java.lang.reflect.Method;\nimport javassist.util.proxy.MethodHandler;\n\npublic class MethodHandlerImpl implements MethodHandler {\n\n    private Object obj;\n\n    public MethodHandlerImpl(Object obj) {\n        this.obj = obj;\n    }\n\n    @Override\n    public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {\n        return obj;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/java/com/company/model/javassist/ProxyImpl.java",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage com.company.model.javassist;\n\nimport javassist.util.proxy.MethodHandler;\nimport javassist.util.proxy.Proxy;\n\npublic class ProxyImpl implements Proxy {\n\n    private Object obj;\n\n    public ProxyImpl(Object obj) {\n        this.obj = obj;\n    }\n\n    @Override\n    public void setHandler(MethodHandler mi) {\n        // do nothing\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/java/com/company/model/javassist/ProxyObjectImpl.java",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n\npackage com.company.model.javassist;\n\nimport javassist.util.proxy.MethodHandler;\nimport javassist.util.proxy.ProxyObject;\n\npublic class ProxyObjectImpl implements ProxyObject {\n\n    private Object obj;\n\n    public ProxyObjectImpl(Object obj) {\n        this.obj = obj;\n    }\n\n    @Override\n    public void setHandler(MethodHandler mi) {\n        // do nothing\n    }\n\n    @Override\n    public MethodHandler getHandler() {\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/java/com/company/pojo/ComplexInvoice.java",
    "content": "/**\n * Copyright (C) 2015 BonitaSoft S.A.\n * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage com.company.pojo;\n\nimport java.util.Date;\n\nimport javax.persistence.Entity;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.Id;\nimport javax.persistence.Lob;\nimport javax.persistence.Version;\n\n@Entity\npublic class ComplexInvoice implements org.bonitasoft.engine.bdm.Entity {\n\n    private static final long serialVersionUID = -230L;\n\n    @Id\n    @GeneratedValue\n    private Long persistenceId;\n\n    @Version\n    private Long persistenceVersion;\n\n    private String string;\n\n    @Lob\n    private String aLongText;\n\n    private byte aByte;\n\n    private char aChar;\n\n    private boolean aBoolean;\n\n    private Date aDate;\n\n    private double aDouble;\n\n    private float aFloat;\n\n    private int anInteger;\n\n    private long aLong;\n\n    private short aShort;\n\n    @Override\n    public Long getPersistenceId() {\n        return persistenceId;\n    }\n\n    @Override\n    public Long getPersistenceVersion() {\n        return persistenceVersion;\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/java/com/company/pojo/ConstrainedItem.java",
    "content": "/**\n * Copyright (C) 2015 BonitaSoft S.A.\n * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage com.company.pojo;\n\nimport java.util.Date;\n\nimport javax.persistence.Entity;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\nimport javax.persistence.Version;\n\n@Entity\n@Table(uniqueConstraints = @javax.persistence.UniqueConstraint(columnNames = { \"bonito\", \"string\" }))\npublic class ConstrainedItem implements org.bonitasoft.engine.bdm.Entity {\n\n    private static final long serialVersionUID = -230L;\n\n    @Id\n    @GeneratedValue\n    private Long persistenceId;\n\n    @Version\n    private Long persistenceVersion;\n\n    private long bonito;\n\n    private String string;\n\n    private Date un_constrained;\n\n    @Override\n    public Long getPersistenceId() {\n        return persistenceId;\n    }\n\n    @Override\n    public Long getPersistenceVersion() {\n        return persistenceVersion;\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/java/com/company/pojo/Employee.java",
    "content": "/**\n * Copyright (C) 2015 BonitaSoft S.A.\n * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage com.company.pojo;\n\nimport javax.persistence.Entity;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.Id;\nimport javax.persistence.Version;\n\n@Entity\npublic class Employee implements org.bonitasoft.engine.bdm.Entity {\n\n    private static final long serialVersionUID = -506130279298072307L;\n\n    @Id\n    @GeneratedValue\n    private Long persistenceId;\n\n    @Version\n    private Long persistenceVersion;\n\n    private String firstName;\n\n    private String lastName;\n\n    protected Employee() {\n        super();\n    }\n\n    public Employee(final long persistenceId, final String firstName, final String lastName) {\n        this(firstName, lastName);\n        this.persistenceId = persistenceId;\n    }\n\n    public Employee(final String firstName, final String lastName) {\n        super();\n        this.firstName = firstName;\n        this.lastName = lastName;\n    }\n\n    public Employee(final Employee employee) {\n        persistenceId = employee.getPersistenceId();\n        persistenceVersion = employee.getPersistenceVersion();\n        firstName = employee.getFirstName();\n        lastName = employee.getLastName();\n    }\n\n    @Override\n    public Long getPersistenceId() {\n        return persistenceId;\n    }\n\n    @Override\n    public Long getPersistenceVersion() {\n        return persistenceVersion;\n    }\n\n    public String getFirstName() {\n        return firstName;\n    }\n\n    public void setFirstName(final String firstName) {\n        this.firstName = firstName;\n    }\n\n    public String getLastName() {\n        return lastName;\n    }\n\n    public void setLastName(final String lastName) {\n        this.lastName = lastName;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + (firstName == null ? 0 : firstName.hashCode());\n        result = prime * result + (persistenceId == null ? 0 : persistenceId.hashCode());\n        result = prime * result + (lastName == null ? 0 : lastName.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        final Employee other = (Employee) obj;\n        if (firstName == null) {\n            if (other.firstName != null) {\n                return false;\n            }\n        } else if (!firstName.equals(other.firstName)) {\n            return false;\n        }\n        if (persistenceId == null) {\n            if (other.persistenceId != null) {\n                return false;\n            }\n        } else if (!persistenceId.equals(other.persistenceId)) {\n            return false;\n        }\n        if (lastName == null) {\n            if (other.lastName != null) {\n                return false;\n            }\n        } else if (!lastName.equals(other.lastName)) {\n            return false;\n        }\n        return true;\n    }\n\n    @Override\n    public String toString() {\n        return \"Employee [persistenceId=\" + persistenceId + \", firstName=\" + firstName + \", lastName=\" + lastName + \"]\";\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/java/com/company/pojo/EmployeeBuilder.java",
    "content": "/**\n * Copyright (C) 2015 BonitaSoft S.A.\n * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage com.company.pojo;\n\npublic class EmployeeBuilder {\n\n    private String firstName = \"aFirstName\";\n\n    private String lastName = \"aLastName\";\n\n    public static EmployeeBuilder anEmployee() {\n        return new EmployeeBuilder();\n    }\n\n    public Employee build() {\n        return new Employee(firstName, lastName);\n    }\n\n    public EmployeeBuilder withFirstName(final String firstName) {\n        this.firstName = firstName;\n        return this;\n    }\n\n    public EmployeeBuilder withLastName(final String lastName) {\n        this.lastName = lastName;\n        return this;\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/java/com/company/pojo/Person.java",
    "content": "/**\n * Copyright (C) 2015 BonitaSoft S.A.\n * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage com.company.pojo;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport javax.persistence.Column;\nimport javax.persistence.ElementCollection;\nimport javax.persistence.FetchType;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.Id;\nimport javax.persistence.OrderColumn;\nimport javax.persistence.Table;\nimport javax.persistence.Version;\n\nimport org.bonitasoft.engine.bdm.Entity;\n\n/**\n *\n */\n@javax.persistence.Entity(name = \"Person\")\n@Table(name = \"PERSON\")\npublic class Person implements Entity {\n\n    private static final long serialVersionUID = 7686491164348658989L;\n\n    @Id\n    @GeneratedValue\n    private Long persistenceId;\n\n    @Version\n    private Long persistenceVersion;\n\n    @ElementCollection(fetch = FetchType.EAGER)\n    @Column(name = \"NICKNAMES\", nullable = true, length = 15)\n    @OrderColumn\n    private List<String> nickNames = new ArrayList<String>(10);\n\n    public Person() {\n        super();\n    }\n\n    public Person(final Person person) {\n        persistenceId = person.getPersistenceId();\n        persistenceVersion = person.getPersistenceVersion();\n        nickNames = person.getNickNames();\n    }\n\n    public void setPersistenceId(final Long persistenceId) {\n        this.persistenceId = persistenceId;\n    }\n\n    @Override\n    public Long getPersistenceId() {\n        return persistenceId;\n    }\n\n    public void setPersistenceVersion(final Long persistenceVersion) {\n        this.persistenceVersion = persistenceVersion;\n    }\n\n    @Override\n    public Long getPersistenceVersion() {\n        return persistenceVersion;\n    }\n\n    public void setNickNames(final List<String> nickNames) {\n        this.nickNames = nickNames;\n    }\n\n    public List<String> getNickNames() {\n        return nickNames;\n    }\n\n    public void addTo(final String addTo) {\n        nickNames.add(addTo);\n    }\n\n    public void removeFrom(final String removeFrom) {\n        nickNames.remove(removeFrom);\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        final Person other = (Person) obj;\n        if (persistenceId == null) {\n            if (other.persistenceId != null) {\n                return false;\n            }\n        } else {\n            if (!persistenceId.equals(other.persistenceId)) {\n                return false;\n            }\n        }\n        if (persistenceVersion == null) {\n            if (other.persistenceVersion != null) {\n                return false;\n            }\n        } else {\n            if (!persistenceVersion.equals(other.persistenceVersion)) {\n                return false;\n            }\n        }\n        if (nickNames == null) {\n            if (other.nickNames != null) {\n                return false;\n            }\n        } else {\n            if (!nickNames.equals(other.nickNames)) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        int persistenceIdCode = 0;\n        if (persistenceId != null) {\n            persistenceIdCode = persistenceId.hashCode();\n        }\n        result = prime * result + persistenceIdCode;\n        int persistenceVersionCode = 0;\n        if (persistenceVersion != null) {\n            persistenceVersionCode = persistenceVersion.hashCode();\n        }\n        result = prime * result + persistenceVersionCode;\n        int nickNamesCode = 0;\n        if (nickNames != null) {\n            nickNamesCode = nickNames.hashCode();\n        }\n        result = prime * result + nickNamesCode;\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/BOMBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.model.field.FieldType;\nimport org.bonitasoft.engine.bdm.model.field.SimpleField;\n\npublic class BOMBuilder {\n\n    public static BOMBuilder aBOM() {\n        return new BOMBuilder();\n    }\n\n    public BusinessObjectModel build() {\n        final SimpleField firstName = new SimpleField();\n        firstName.setName(\"firstName\");\n        firstName.setType(FieldType.STRING);\n        final SimpleField lastName = new SimpleField();\n        lastName.setName(\"lastName\");\n        lastName.setType(FieldType.STRING);\n        final BusinessObject employee = new BusinessObject();\n        employee.setQualifiedName(\"com.company.Employee\");\n        employee.addField(firstName);\n        employee.addField(lastName);\n        final BusinessObjectModel bom = new BusinessObjectModel();\n        bom.addBusinessObject(employee);\n        return bom;\n    }\n\n    public BusinessObjectModel buildModelWithAllSupportedTypes() {\n        final BusinessObject invoice = new BusinessObject();\n        invoice.setQualifiedName(\"com.company.pojo.ComplexInvoice\");\n        final BusinessObjectModel bom = new BusinessObjectModel();\n        bom.addBusinessObject(invoice);\n        return bom;\n    }\n\n    public BusinessObjectModel buildModelWithConstrainedFields() {\n        final BusinessObject constrained = new BusinessObject();\n        constrained.setQualifiedName(\"com.company.pojo.ConstrainedItem\");\n        final BusinessObjectModel bom = new BusinessObjectModel();\n        bom.addBusinessObject(constrained);\n        return bom;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/JpaTestConfiguration.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data;\n\nimport java.util.Map;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class JpaTestConfiguration {\n\n    private Map<String, Object> jpaConfiguration;\n    private Map<String, Object> jpaModelConfiguration;\n\n    public JpaTestConfiguration(Map<String, Object> jpaConfiguration, Map<String, Object> jpaModelConfiguration) {\n        this.jpaConfiguration = jpaConfiguration;\n        this.jpaModelConfiguration = jpaModelConfiguration;\n    }\n\n    public Map<String, Object> getJpaConfiguration() {\n        return jpaConfiguration;\n    }\n\n    public Map<String, Object> getJpaModelConfiguration() {\n        return jpaModelConfiguration;\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/AddNewEmployeeThread.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport javax.transaction.UserTransaction;\n\nimport com.company.pojo.Employee;\nimport org.bonitasoft.engine.business.data.BusinessDataRepository;\n\npublic class AddNewEmployeeThread extends Thread {\n\n    private final BusinessDataRepository repository;\n\n    private final long employeeId;\n\n    public AddNewEmployeeThread(final BusinessDataRepository repository, final long employeeId) {\n        this.repository = repository;\n        this.employeeId = employeeId;\n    }\n\n    @Override\n    public void run() {\n        UserTransaction transaction = com.arjuna.ats.jta.UserTransaction.userTransaction();\n        try {\n            transaction.begin();\n\n            final Employee myEmployee = new Employee(\"John\" + employeeId, \"Doe\");\n            Thread.sleep(150);\n            repository.persist(myEmployee);\n\n            transaction.commit();\n        } catch (final Exception e) {\n            try {\n                transaction.rollback();\n            } catch (final Exception e1) {\n                e.printStackTrace();\n                throw new IllegalArgumentException(e1);\n            }\n            throw new IllegalArgumentException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/AddressHibernateProxy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.mock;\n\nimport java.util.List;\n\nimport com.company.model.Address;\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.hibernate.proxy.HibernateProxy;\nimport org.hibernate.proxy.LazyInitializer;\n\n/**\n * A fake HibernateProxy that wraps an Address entity.\n * <p>\n * This mimics Hibernate's proxy behavior where the proxy class itself has null/empty fields,\n * but the real data is accessible via {@link LazyInitializer#getImplementation()}.\n * <p>\n * Used to reproduce the bug where HibernateProxy entities are serialized as empty objects\n * because {@link org.bonitasoft.engine.business.data.impl.jackson.EntityBeanSerializerModifier#changeProperties}\n * returns an empty property list for HibernateProxy types.\n */\npublic class AddressHibernateProxy implements HibernateProxy, Entity {\n\n    private final Address realAddress;\n    private final boolean initialized;\n    private final LazyInitializer lazyInitializer;\n\n    public AddressHibernateProxy(Address realAddress) {\n        this(realAddress, true);\n    }\n\n    public AddressHibernateProxy(Address realAddress, boolean initialized) {\n        this.realAddress = realAddress;\n        this.initialized = initialized;\n        this.lazyInitializer = createMockLazyInitializer(realAddress, initialized);\n    }\n\n    private static LazyInitializer createMockLazyInitializer(Address realAddress, boolean initialized) {\n        LazyInitializer mockInitializer = mock(LazyInitializer.class);\n        doReturn(Address.class).when(mockInitializer).getPersistentClass();\n        doReturn(realAddress).when(mockInitializer).getImplementation();\n        doReturn(!initialized).when(mockInitializer).isUninitialized();\n        return mockInitializer;\n    }\n\n    @Override\n    public Object writeReplace() {\n        return null;\n    }\n\n    @Override\n    public LazyInitializer getHibernateLazyInitializer() {\n        return lazyInitializer;\n    }\n\n    @Override\n    public Long getPersistenceId() {\n        return realAddress != null ? realAddress.getPersistenceId() : null;\n    }\n\n    @Override\n    public Long getPersistenceVersion() {\n        return realAddress != null ? realAddress.getPersistenceVersion() : null;\n    }\n\n    public String getStreet() {\n        throwIfUninitialized(\"street\");\n        return realAddress != null ? realAddress.getStreet() : null;\n    }\n\n    public Float getNumber() {\n        throwIfUninitialized(\"number\");\n        return realAddress != null ? realAddress.getNumber() : null;\n    }\n\n    public List<Double> getFloors() {\n        throwIfUninitialized(\"floors\");\n        return realAddress != null ? realAddress.getFloors() : null;\n    }\n\n    public String getDoorCode() {\n        throwIfUninitialized(\"doorCode\");\n        return realAddress != null ? realAddress.getDoorCode() : null;\n    }\n\n    private void throwIfUninitialized(String propertyName) {\n        if (!initialized) {\n            throw new org.hibernate.LazyInitializationException(\n                    \"could not initialize proxy - no Session: \" + propertyName);\n        }\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/BdmFieldTypeConverterTest.java",
    "content": "/**\n * Copyright (C) 2025 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport static org.assertj.core.api.Assertions.*;\n\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.OffsetDateTime;\nimport java.time.ZoneOffset;\n\nimport org.junit.jupiter.api.Test;\n\nclass BdmFieldTypeConverterTest {\n\n    @Test\n    void should_convert_float_to_double() {\n        assertThat(BdmFieldTypeConverter.convert(125f, Double.class)).isEqualTo(125d);\n    }\n\n    @Test\n    void should_convert_double_float() {\n        assertThat(BdmFieldTypeConverter.convert(125d, Float.class)).isEqualTo(125f);\n    }\n\n    @Test\n    void should_convert_integer_to_double() {\n        assertThat(BdmFieldTypeConverter.convert(1255598556, Double.class)).isEqualTo(1255598556d);\n    }\n\n    @Test\n    void should_convert_integer_to_long() {\n        assertThat(BdmFieldTypeConverter.convert(9988877, Long.class)).isEqualTo(9988877L);\n    }\n\n    @Test\n    void should_convert_long_to_integer() {\n        assertThat(BdmFieldTypeConverter.convert(9988877L, Integer.class)).isEqualTo(9988877);\n    }\n\n    @Test\n    void should_convert_integer_to_short() {\n        assertThat(BdmFieldTypeConverter.convert(55000, Short.class)).isEqualTo((short) 55000);\n    }\n\n    @Test\n    void should_convert_integer_to_byte() {\n        assertThat(BdmFieldTypeConverter.convert(124, Byte.class)).isEqualTo((byte) 124);\n    }\n\n    @Test\n    void converting_null_should_return_null() {\n        assertThat(BdmFieldTypeConverter.convert(null, Long.class)).isNull();\n    }\n\n    @Test\n    void converting_supported_type_should_return_it_directly_String() {\n        final String someString = \"someString\";\n        assertThat(BdmFieldTypeConverter.convert(someString, String.class)).isSameAs(someString);\n    }\n\n    @Test\n    void converting_supported_type_should_return_it_directly_Float() {\n        Float someFloat = 1.0f;\n        assertThat(BdmFieldTypeConverter.convert(someFloat, Float.class)).isSameAs(someFloat);\n    }\n\n    @Test\n    void should_convert_string_to_localdate() {\n        assertThat(BdmFieldTypeConverter.convert(\"2025-07-17\", LocalDate.class)).isEqualTo(LocalDate.of(2025, 7, 17));\n    }\n\n    @Test\n    void should_convert_string_to_localdatetime() {\n        assertThat(BdmFieldTypeConverter.convert(\"2025-01-12T10:15:30\", LocalDateTime.class))\n                .isEqualTo(LocalDateTime.of(2025, 1, 12, 10, 15, 30));\n    }\n\n    @Test\n    void should_convert_string_to_offsetdatetime() {\n        OffsetDateTime result = BdmFieldTypeConverter.convert(\"2025-11-29T10:15:30+01:00\", OffsetDateTime.class);\n        assertThat(result.toLocalDateTime()).isEqualTo(LocalDateTime.of(2025, 11, 29, 10, 15, 30));\n        assertThat(result.getOffset()).isEqualTo(ZoneOffset.of(\"+01:00\"));\n    }\n\n    @Test\n    void should_throw_exception_for_incompatible_conversion() {\n        assertThatExceptionOfType(IllegalArgumentException.class)\n                .isThrownBy(() -> BdmFieldTypeConverter.convert(\"invalid\", Integer.class))\n                .withMessageContaining(\"Cannot convert\");\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/BusinessDataModelRepositoryImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport static java.util.Collections.singletonList;\nimport static org.assertj.core.api.Assertions.*;\nimport static org.bonitasoft.engine.commons.Pair.pair;\nimport static org.bonitasoft.engine.commons.io.IOUtil.zip;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport java.io.InputStream;\nimport java.sql.SQLException;\nimport java.sql.SQLSyntaxErrorException;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.bonitasoft.engine.BOMBuilder;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.business.data.DataRetentionBdmTrackingService;\nimport org.bonitasoft.engine.business.data.InvalidBusinessDataModelException;\nimport org.bonitasoft.engine.business.data.SBusinessDataRepositoryDeploymentException;\nimport org.bonitasoft.engine.business.data.SBusinessDataRepositoryException;\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.dependency.SDependencyDeletionException;\nimport org.bonitasoft.engine.dependency.SDependencyNotFoundException;\nimport org.bonitasoft.engine.dependency.impl.TenantDependencyService;\nimport org.bonitasoft.engine.dependency.model.SDependency;\nimport org.bonitasoft.engine.dependency.model.ScopeType;\nimport org.bonitasoft.engine.io.IOUtil;\nimport org.bonitasoft.engine.platform.PlatformService;\nimport org.bonitasoft.engine.platform.model.SPlatformProperties;\nimport org.bonitasoft.engine.resources.TenantResourceType;\nimport org.bonitasoft.engine.resources.TenantResourcesService;\nimport org.hibernate.tool.schema.spi.CommandAcceptanceException;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class BusinessDataModelRepositoryImplTest {\n\n    private static final long MEANINGLESS_ARTIFACT_ID = -1L;\n\n    @Mock\n    private TenantDependencyService dependencyService;\n    @Mock\n    private ClassLoaderService classLoaderService;\n\n    @Mock\n    private TenantResourcesService tenantResourcesService;\n\n    @Mock\n    private SchemaManagerUpdate schemaManager;\n\n    @Mock(lenient = true)\n    private PlatformService platformService;\n\n    @Mock(lenient = true)\n    private SPlatformProperties platformProperties;\n\n    @Mock\n    private DataRetentionBdmTrackingService bdmTrackingService;\n\n    private BusinessDataModelRepositoryImpl businessDataModelRepository;\n\n    @Before\n    public void setUp() throws Exception {\n        doReturn(platformProperties).when(platformService).getSPlatformProperties();\n        doReturn(\"1.0\").when(platformProperties).getPlatformVersion();\n        // Prevent uninstall()/install() from nullifying the thread context classloader\n        // via classLoaderService.getClassLoader() returning null (default mock behavior)\n        doReturn(getClass().getClassLoader()).when(classLoaderService).getClassLoader(any());\n        businessDataModelRepository = spy(new BusinessDataModelRepositoryImpl(platformService, dependencyService,\n                classLoaderService, schemaManager, tenantResourcesService, bdmTrackingService));\n    }\n\n    @Test\n    public void should_createAndDeployServerBDMJar_add_dependency_on_bdm_server_jar() throws Exception {\n        BusinessObjectModel bom = BOMBuilder.aBOM().build();\n        doReturn(\"some bytes\".getBytes()).when(businessDataModelRepository).generateServerBDMJar(bom);\n\n        doReturn(mock(SDependency.class)).when(dependencyService).createMappedDependency(\"BDR\", \"some bytes\".getBytes(),\n                \"BDR.jar\", MEANINGLESS_ARTIFACT_ID, ScopeType.TENANT);\n\n        businessDataModelRepository.createAndDeployServerBDMJar(bom);\n\n        verify(dependencyService).createMappedDependency(\"BDR\", \"some bytes\".getBytes(), \"BDR.jar\",\n                MEANINGLESS_ARTIFACT_ID, ScopeType.TENANT);\n    }\n\n    @Test\n    public void should_createAndDeployClientBDMZip_add_resource_on_tenantResourcesService() throws Exception {\n        BusinessObjectModel bom = BOMBuilder.aBOM().build();\n        doReturn(\"some bytes\".getBytes()).when(businessDataModelRepository).generateClientBDMZip(bom);\n\n        businessDataModelRepository.createAndDeployClientBDMZip(bom, 1154222L);\n\n        verify(tenantResourcesService).add(eq(\"client-bdm.zip\"), eq(TenantResourceType.BDM),\n                eq(\"some bytes\".getBytes()),\n                anyLong());\n    }\n\n    @Test\n    public void uninstall_should_delete_a_dependency() throws Exception {\n        businessDataModelRepository.uninstall();\n\n        verify(dependencyService).deleteDependency(\"BDR\");\n    }\n\n    @Test\n    public void uninstall_should_ignore_exception_if_the_dependency_does_not_exist() throws Exception {\n        doThrow(new SDependencyNotFoundException(\"error\")).when(dependencyService).deleteDependency(\"BDR\");\n\n        assertThatNoException().isThrownBy(() -> businessDataModelRepository.uninstall());\n    }\n\n    @Test(expected = SBusinessDataRepositoryException.class)\n    public void uninstall_should_throw_an_exception_if_an_exception_occurs_during_the_dependency_deletion()\n            throws Exception {\n        doThrow(new SDependencyDeletionException(\"error\")).when(dependencyService).deleteDependency(\"BDR\");\n\n        businessDataModelRepository.uninstall();\n    }\n\n    @Test\n    public void should_return_getBusinessObjectModel_return_null_when_no_bdm() throws Exception {\n        //given\n        doReturn(false).when(businessDataModelRepository).isBDMDeployed();\n\n        //when then\n        assertThat(businessDataModelRepository.getBusinessObjectModel()).isNull();\n    }\n\n    @Test\n    public void should_return_getBusinessObjectModel_return_bdm() throws Exception {\n        //given\n        final InputStream resourceAsStream = this.getClass().getResourceAsStream(\"client-bdm.zip\");\n        final byte[] clientBDMZip = IOUtil.getAllContentFrom(resourceAsStream);\n\n        doReturn(clientBDMZip).when(businessDataModelRepository).getClientBDMZip();\n\n        //when\n        final BusinessObjectModel model = businessDataModelRepository.getBusinessObjectModel();\n\n        //then\n        assertThat(model).as(\"should return the business model\").as(\"should return the bom\").isNotNull();\n\n    }\n\n    @Test(expected = SBusinessDataRepositoryDeploymentException.class)\n    public void should_getBusinessObjectModel_throw_exception() throws Exception {\n        //given\n        final InputStream resourceAsStream = this.getClass().getResourceAsStream(\"client-bdm.zip\");\n        final byte[] clientBDMZip = IOUtil.getAllContentFrom(resourceAsStream);\n\n        doReturn(clientBDMZip).when(businessDataModelRepository).getClientBDMZip();\n        doThrow(new InvalidBusinessDataModelException(new Exception())).when(businessDataModelRepository)\n                .getBusinessObjectModel(any(clientBDMZip.getClass()));\n\n        //when then exception\n        businessDataModelRepository.getBusinessObjectModel();\n    }\n\n    @Test\n    public void install_should_pass_userId_to_tenantResourceService() throws Exception {\n        // given:\n        final BusinessObjectModel businessObjectModel = BOMBuilder.aBOM().build();\n        doReturn(businessObjectModel).when(businessDataModelRepository).getBusinessObjectModel(any(byte[].class));\n\n        final byte[] bom = \"some generated bytes\".getBytes();\n        doReturn(bom).when(businessDataModelRepository).generateClientBDMZip(businessObjectModel);\n        doReturn(9L).when(businessDataModelRepository).createAndDeployServerBDMJar(businessObjectModel);\n\n        // when:\n        final long userId = 47L;\n        businessDataModelRepository.install(bom, userId);\n\n        // then:\n        verify(tenantResourcesService).add(\"client-bdm.zip\", TenantResourceType.BDM, bom, userId);\n    }\n\n    @Test(expected = InvalidBusinessDataModelException.class)\n    public void install_should_throw_an_SInvalidBusinessDataModelException_when_zip_is_invalid() throws Exception {\n        // given:\n        final byte[] bom = \"some invalid context\".getBytes();\n\n        // when:\n        businessDataModelRepository.install(bom, 47L);\n    }\n\n    @Test(expected = InvalidBusinessDataModelException.class)\n    public void install_should_throw_an_SInvalidBusinessDataModelException_when_bomXml_is_invalid() throws Exception {\n        // given:\n        final byte[] bom = zip(pair(\"bom.xml\", \"<xml></xml>\".getBytes()));\n\n        // when:\n        businessDataModelRepository.install(bom, 47L);\n    }\n\n    @Test\n    public void getInstalledBDMVersion_should_return_version_number() throws Exception {\n        for (long version : new long[] { 0L, 1L, -1L, 42L, Long.MAX_VALUE, Long.MIN_VALUE }) {\n            // given:\n            doReturn(Optional.of(version)).when(dependencyService)\n                    .getIdOfDependencyOfArtifactForTenant(BusinessDataModelRepositoryImpl.BDR_DEPENDENCY_FILENAME);\n\n            // when:\n            final String installedBDMVersion = businessDataModelRepository.getInstalledBDMVersion();\n\n            // then:\n            assertThat(installedBDMVersion).isEqualTo(String.valueOf(version));\n        }\n    }\n\n    @Test\n    public void update_should_convert_exceptions_to_allow_to_see_entire_root_cause() {\n        // given:\n        doReturn(singletonList(new CommandAcceptanceException(\"Error executing DDL bla bla bla...\",\n                new SQLSyntaxErrorException(\"ORA-02275: une telle contrainte référentielle existe déjà dans la table\",\n                        new Exception(\"Root Oracle Cause\")))))\n                .when(schemaManager)\n                .update(anySet());\n\n        // when - then:\n        assertThatExceptionOfType(SBusinessDataRepositoryDeploymentException.class)\n                .isThrownBy(() -> businessDataModelRepository.update(new HashSet<>()))\n                .withMessageContainingAll(\n                        \"1: org.hibernate.tool.schema.spi.CommandAcceptanceException: Error executing DDL bla bla bla...\",\n                        \"caused by java.sql.SQLSyntaxErrorException\",\n                        \"caused by java.lang.Exception: Root Oracle Cause\");\n    }\n\n    @Test\n    public void update_should_convert_all_exceptions_in_the_list() {\n        // given:\n        doReturn(Arrays.asList(\n                new CommandAcceptanceException(\"Error executing DDL bla bla bla...\",\n                        new SQLSyntaxErrorException(\n                                \"ORA-02275: une telle contrainte référentielle existe déjà dans la table\",\n                                new Exception(\"Root Oracle Cause\"))),\n                new CommandAcceptanceException(\"CommandAcceptanceException bliblibli\",\n                        new SQLSyntaxErrorException(\"Hibernate error\"))))\n                .when(schemaManager)\n                .update(anySet());\n\n        // when - then:\n        assertThatExceptionOfType(SBusinessDataRepositoryDeploymentException.class)\n                .isThrownBy(() -> businessDataModelRepository.update(new HashSet<>()))\n                .withMessageContainingAll(\n                        \"1: org.hibernate.tool.schema.spi.CommandAcceptanceException: Error executing DDL bla bla bla...\",\n                        \"caused by java.lang.Exception: Root Oracle Cause\",\n                        \"2: org.hibernate.tool.schema.spi.CommandAcceptanceException: CommandAcceptanceException bliblibli\",\n                        \"caused by java.sql.SQLSyntaxErrorException: Hibernate error\");\n    }\n\n    @Test\n    public void convertExceptions_should_filter_out_empty_message_lines() {\n        // given:\n        final List<Exception> exceptions = singletonList(new SQLSyntaxErrorException(\n                \"message with trailing carriage return\\n\", new SQLException(\"syntax error\")));\n\n        // when:\n        final String message = businessDataModelRepository.convertExceptions(exceptions);\n\n        // then:\n        assertThat(Arrays.asList(message.split(\"\\n\"))).doesNotContain(\"\");\n    }\n\n    @Test\n    public void isDeployedComparesBdmWithSameContent() throws Exception {\n        // given:\n        var bdmArchive = \"fake archive content\".getBytes();\n        var generatedJarContent = \"fake jar content\".getBytes();\n        when(dependencyService\n                .getIdOfDependencyOfArtifactForTenant(BusinessDataModelRepositoryImpl.BDR_DEPENDENCY_FILENAME))\n                .thenReturn(Optional.of(1L));\n        var deployedBdm = new SDependency();\n        deployedBdm.setValue_(generatedJarContent);\n        when(dependencyService.getDependency(1L)).thenReturn(deployedBdm);\n        doReturn(null).when(businessDataModelRepository).getBusinessObjectModel(bdmArchive);\n        doReturn(generatedJarContent).when(businessDataModelRepository).generateServerBDMJar(any(), eq(false));\n\n        // when:\n        var isDeployed = businessDataModelRepository.isDeployed(bdmArchive);\n\n        // then:\n        assertThat(isDeployed).isTrue();\n    }\n\n    @Test\n    public void isDeployedComparesBdmWithDifferentContent() throws Exception {\n        // given:\n        var bdmArchive = \"fake archive content\".getBytes();\n        var existingJarContent = \"fake existing jar content\".getBytes();\n        var generatedJarContent = \"fake jar content\".getBytes();\n        when(dependencyService\n                .getIdOfDependencyOfArtifactForTenant(BusinessDataModelRepositoryImpl.BDR_DEPENDENCY_FILENAME))\n                .thenReturn(Optional.of(1L));\n        var deployedBdm = new SDependency();\n        deployedBdm.setValue_(existingJarContent);\n        when(dependencyService.getDependency(1L)).thenReturn(deployedBdm);\n        doReturn(null).when(businessDataModelRepository).getBusinessObjectModel(bdmArchive);\n        doReturn(generatedJarContent).when(businessDataModelRepository).generateServerBDMJar(any(), eq(false));\n\n        // when:\n        var isDeployed = businessDataModelRepository.isDeployed(bdmArchive);\n\n        // then:\n        assertThat(isDeployed).isFalse();\n    }\n\n    @Test\n    public void isDeployedWithoutBdmDeployed() throws Exception {\n        // given:\n        var bdmArchive = \"fake archive content\".getBytes();\n        when(dependencyService\n                .getIdOfDependencyOfArtifactForTenant(BusinessDataModelRepositoryImpl.BDR_DEPENDENCY_FILENAME))\n                .thenReturn(Optional.empty());\n\n        // when:\n        var isDeployed = businessDataModelRepository.isDeployed(bdmArchive);\n\n        // then:\n        assertThat(isDeployed).isFalse();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/BusinessDataReloaderTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.bonitasoft.engine.business.data.BusinessDataRepository;\nimport org.bonitasoft.engine.business.data.proxy.ServerLazyLoader;\nimport org.bonitasoft.engine.business.data.proxy.ServerProxyfier;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class BusinessDataReloaderTest {\n\n    @Mock\n    private BusinessDataRepository repository;\n\n    @Mock\n    private ServerLazyLoader lazyLoader;\n\n    @InjectMocks\n    private ServerProxyfier proxyfier;\n\n    @InjectMocks\n    private BusinessDataReloader reloader;\n\n    @Test\n    public void reloadEntity_should_call_findById_on_repository() throws Exception {\n        //given\n        long id = 5L;\n        EntityPojo entity = new EntityPojo(id);\n        EntityPojo entityUpdated = new EntityPojo(6L);\n        given(repository.findById(EntityPojo.class, id)).willReturn(entityUpdated);\n\n        //when\n        Entity reloadedEntity = reloader.reloadEntity(entity);\n\n        //then\n        assertThat(reloadedEntity).isEqualTo(entityUpdated);\n\n    }\n\n    @Test\n    public void reloadEntity_should_use_real_class_to_reload_entity() throws Exception {\n        //given\n        long id = 5L;\n        EntityPojo proxyfiedEntityPojo = proxyfier.proxify(new EntityPojo(id));\n        EntityPojo entityUpdated = new EntityPojo(6L);\n        // use the real class here\n        given(repository.findById(EntityPojo.class, id)).willReturn(entityUpdated);\n\n        //when\n        Entity reloadedEntity = reloader.reloadEntity(proxyfiedEntityPojo);\n\n        //then\n        assertThat(reloadedEntity).isEqualTo(entityUpdated);\n\n    }\n\n    @Test\n    public void reloadEntitySoftly_should_return_entity_itself_when_id_is_not_set() throws Exception {\n        //given\n        EntityPojo entity = new EntityPojo(null);\n\n        //when\n        Entity reloadedEntity = reloader.reloadEntitySoftly(entity);\n\n        //then\n        assertThat(reloadedEntity).isEqualTo(entity);\n        verifyNoInteractions(repository);\n    }\n\n    @Test\n    public void reloadEntitySoftly_should_reload_entity_id_is_set() throws Exception {\n        //given\n        long id = 2L;\n        EntityPojo entity = new EntityPojo(id);\n        EntityPojo entityUpdated = new EntityPojo(3L);\n\n        given(repository.findById(EntityPojo.class, id)).willReturn(entityUpdated);\n\n        //when\n        Entity reloadedEntity = reloader.reloadEntitySoftly(entity);\n\n        //then\n        assertThat(reloadedEntity).isEqualTo(entityUpdated);\n    }\n\n    @Test\n    public void reloadEntitySoftly_should_use_real_class_to_reload_entity() throws Exception {\n        //given\n        long id = 2L;\n        EntityPojo proxyfiedEntity = new EntityPojo(id);\n        EntityPojo entityUpdated = new EntityPojo(3L);\n\n        //use real class\n        given(repository.findById(EntityPojo.class, id)).willReturn(entityUpdated);\n\n        //when\n        Entity reloadedEntity = reloader.reloadEntitySoftly(proxyfiedEntity);\n\n        //then\n        assertThat(reloadedEntity).isEqualTo(entityUpdated);\n    }\n\n    @Test\n    public void getEntityRealClass_should_handle_Bonita_proxy() throws Exception {\n        final Class entityRealClass = reloader.getEntityRealClass(proxyfier.proxify(new EntityPojo(451L)));\n\n        assertThat(entityRealClass).isEqualTo(EntityPojo.class);\n    }\n\n    @Test\n    public void getEntityRealClass_should_handle_Hibernate_proxy() throws Exception {\n        final Class entityRealClass = reloader.getEntityRealClass(new FakeHibernateProxyEntity());\n\n        assertThat(entityRealClass).isEqualTo(EntityPojo.class);\n    }\n\n    @Test\n    public void getEntityRealClass_should_handle_Hibernate_proxy_inside_Bonita_proxy() throws Exception {\n        final Class entityRealClass = reloader.getEntityRealClass(proxyfier.proxify(new FakeHibernateProxyEntity()));\n\n        assertThat(entityRealClass).isEqualTo(EntityPojo.class);\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/BusinessDataRepositoryEventAspectTest.java",
    "content": "/**\n * Copyright (C) 2025 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.*;\n\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.events.model.SDeleteEvent;\nimport org.bonitasoft.engine.events.model.SInsertEvent;\nimport org.bonitasoft.engine.events.model.SUpdateEvent;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nclass BusinessDataRepositoryEventAspectTest {\n\n    private EventService eventService;\n    private BusinessDataRepositoryEventAspect aspect;\n    private Entity entity;\n\n    @BeforeEach\n    void setUp() {\n        eventService = mock(EventService.class);\n        aspect = new BusinessDataRepositoryEventAspect(eventService);\n        entity = mock(Entity.class);\n    }\n\n    @Test\n    void should_fire_insert_event_on_persist_when_id_is_null() throws Throwable {\n        when(entity.getPersistenceId()).thenReturn(null);\n        Object dummyResult = new Object();\n        var joinPoint = mock(org.aspectj.lang.ProceedingJoinPoint.class);\n        when(joinPoint.proceed()).thenReturn(dummyResult);\n\n        Object result = aspect.aroundPersist(joinPoint, entity);\n\n        ArgumentCaptor<SInsertEvent> captor = ArgumentCaptor.forClass(SInsertEvent.class);\n        verify(eventService).fireEvent(captor.capture());\n        SInsertEvent event = captor.getValue();\n        assertThat(event.getType()).isEqualTo(\"BUSINESS_DATA_CREATED\");\n        assertThat(event.getObject()).isEqualTo(entity);\n        assertThat(result).isSameAs(dummyResult);\n    }\n\n    @Test\n    void should_fire_update_event_on_persist_when_id_is_not_null() throws Throwable {\n        when(entity.getPersistenceId()).thenReturn(123L);\n        Object dummyResult = new Object();\n        var joinPoint = mock(org.aspectj.lang.ProceedingJoinPoint.class);\n        when(joinPoint.proceed()).thenReturn(dummyResult);\n\n        Object result = aspect.aroundPersist(joinPoint, entity);\n\n        ArgumentCaptor<SUpdateEvent> captor = ArgumentCaptor.forClass(SUpdateEvent.class);\n        verify(eventService).fireEvent(captor.capture());\n        SUpdateEvent event = captor.getValue();\n        assertThat(event.getType()).isEqualTo(\"BUSINESS_DATA_UPDATED\");\n        assertThat(event.getObject()).isEqualTo(entity);\n        assertThat(result).isSameAs(dummyResult);\n    }\n\n    @Test\n    void should_fire_delete_event_on_remove() throws Exception {\n        aspect.afterRemove(entity);\n        ArgumentCaptor<SDeleteEvent> captor = ArgumentCaptor.forClass(SDeleteEvent.class);\n        verify(eventService).fireEvent(captor.capture());\n        SDeleteEvent event = captor.getValue();\n        assertThat(event.getType()).isEqualTo(\"BUSINESS_DATA_DELETED\");\n        assertThat(event.getObject()).isEqualTo(entity);\n    }\n\n    @Test\n    void should_fire_delete_event_on_removeById() throws Exception {\n        aspect.afterRemoveById(entity);\n        ArgumentCaptor<SDeleteEvent> captor = ArgumentCaptor.forClass(SDeleteEvent.class);\n        verify(eventService).fireEvent(captor.capture());\n        SDeleteEvent event = captor.getValue();\n        assertThat(event.getType()).isEqualTo(\"BUSINESS_DATA_DELETED\");\n        assertThat(event.getObject()).isEqualTo(entity);\n    }\n\n    @Test\n    void should_fire_insert_event_on_merge_when_id_is_null() throws Throwable {\n        when(entity.getPersistenceId()).thenReturn(null);\n        Object dummyResult = new Object();\n        var joinPoint = mock(org.aspectj.lang.ProceedingJoinPoint.class);\n        when(joinPoint.proceed()).thenReturn(dummyResult);\n\n        Object result = aspect.aroundMerge(joinPoint, entity);\n\n        ArgumentCaptor<SInsertEvent> captor = ArgumentCaptor.forClass(SInsertEvent.class);\n        verify(eventService).fireEvent(captor.capture());\n        SInsertEvent event = captor.getValue();\n        assertThat(event.getType()).isEqualTo(\"BUSINESS_DATA_CREATED\");\n        assertThat(event.getObject()).isEqualTo(entity);\n        assertThat(result).isSameAs(dummyResult);\n    }\n\n    @Test\n    void should_fire_update_event_on_merge_when_id_is_not_null() throws Throwable {\n        when(entity.getPersistenceId()).thenReturn(123L);\n        Object dummyResult = new Object();\n        var joinPoint = mock(org.aspectj.lang.ProceedingJoinPoint.class);\n        when(joinPoint.proceed()).thenReturn(dummyResult);\n\n        Object result = aspect.aroundMerge(joinPoint, entity);\n\n        ArgumentCaptor<SUpdateEvent> captor = ArgumentCaptor.forClass(SUpdateEvent.class);\n        verify(eventService).fireEvent(captor.capture());\n        SUpdateEvent event = captor.getValue();\n        assertThat(event.getType()).isEqualTo(\"BUSINESS_DATA_UPDATED\");\n        assertThat(event.getObject()).isEqualTo(entity);\n        assertThat(result).isSameAs(dummyResult);\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/BusinessDataServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport static org.assertj.core.api.Assertions.*;\nimport static org.mockito.Mockito.*;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.bdm.model.Query;\nimport org.bonitasoft.engine.bdm.model.QueryParameter;\nimport org.bonitasoft.engine.bpm.businessdata.BusinessDataQueryMetadata;\nimport org.bonitasoft.engine.bpm.businessdata.BusinessDataQueryResult;\nimport org.bonitasoft.engine.business.data.BusinessDataModelRepository;\nimport org.bonitasoft.engine.business.data.BusinessDataRepository;\nimport org.bonitasoft.engine.business.data.JsonBusinessDataSerializer;\nimport org.bonitasoft.engine.business.data.SBusinessDataNotFoundException;\nimport org.bonitasoft.engine.business.data.SBusinessDataRepositoryException;\nimport org.bonitasoft.engine.business.data.SBusinessDataRepositorySerializationException;\nimport org.bonitasoft.engine.business.data.proxy.ServerLazyLoader;\nimport org.bonitasoft.engine.business.data.proxy.ServerProxyfier;\nimport org.bonitasoft.engine.commons.TypeConverterUtil;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class BusinessDataServiceImplTest {\n\n    public static final String PARAMETER_STRING = \"parameterString\";\n    public static final String PARAMETER_INTEGER = \"parameterInteger\";\n    public static final String PARAMETER_LONG = \"parameterLong\";\n    private static final String PARAMETER_BUSINESSDATA_CLASS_URI_VALUE = \"/businessdata/{className}/{id}/{field}\";\n    private static final String NEW_NAME = \"new name\";\n    private final Entity pojo = new EntityPojo(1L);\n    @Mock\n    JsonBusinessDataSerializer jsonEntitySerializer;\n    @Mock\n    BusinessDataModelRepository businessDataModelRepository;\n    @Mock\n    private BusinessDataRepository businessDataRepository;\n    @Mock\n    private BusinessDataReloader businessDataReloader;\n    @Mock\n    private CountQueryProvider countQueryProvider;\n\n    private BusinessDataServiceImpl businessDataService;\n\n    @Before\n    public void before() {\n        final String[] datePatterns = new String[] { \"yyyy-MM-dd HH:mm:ss\", \"yyyy-MM-dd'T'HH:mm:ss\", \"yyyy-MM-dd\",\n                \"HH:mm:ss\", \"yyyy-MM-dd'T'HH:mm:ss.SSS\" };\n        businessDataService = spy(new BusinessDataServiceImpl(businessDataRepository, jsonEntitySerializer,\n                businessDataModelRepository, new TypeConverterUtil(datePatterns),\n                businessDataReloader, countQueryProvider));\n        // Mock default standard shape enabled to true (matches runtime default)\n        when(jsonEntitySerializer.isStandardShapeEnabled()).thenReturn(true);\n    }\n\n    @Test\n    public void isBusinessDataShouldBeTrue() {\n        assertThat(businessDataService.isBusinessData(pojo)).isTrue();\n    }\n\n    @Test\n    public void isBusinessDataShouldBeTrueWithList() {\n        assertThat(businessDataService.isBusinessData(List.of(pojo))).isTrue();\n        assertThat(businessDataService.isBusinessData(new ArrayList<String>())).isTrue();\n    }\n\n    @Test\n    public void isBusinessDataShouldBeFalseWithList() {\n        assertThat(businessDataService.isBusinessData(\"not a list\")).isFalse();\n        assertThat(businessDataService.isBusinessData(List.of(1L))).isFalse();\n    }\n\n    @Test\n    public void isBusinessDataShouldBeFalse() {\n        final Object pojoObject = new Object();\n        assertThat(businessDataService.isBusinessData(pojoObject)).isFalse();\n    }\n\n    @Test\n    public void isBusinessDataShouldBeFalseWhenDataIsNull() {\n        assertThat(businessDataService.isBusinessData(null)).isFalse();\n\n    }\n\n    @Test\n    public void callJavaOperationShouldThrowExceptionWhenBusinessDataIsNull() {\n        assertThatExceptionOfType(SBusinessDataNotFoundException.class)\n                .isThrownBy(() -> businessDataService.callJavaOperation(null, pojo, \"someMethod\",\n                        String.class.getName()));\n    }\n\n    @Test\n    public void callJavaOperationShouldInvokeListMethod() {\n        final List<Entity> entities = List.of(pojo);\n        assertThatNoException()\n                .isThrownBy(() -> businessDataService.callJavaOperation(entities, entities, \"contains\",\n                        Object.class.getName()));\n    }\n\n    @Test\n    public void callJavaOperationShouldThrowExceptionWhenNotAnEntity() {\n        assertThatExceptionOfType(SBusinessDataRepositoryException.class)\n                .isThrownBy(() -> businessDataService.callJavaOperation(\"not an entity\", null, \"getLengh\",\n                        String.class.getName()));\n    }\n\n    @Test\n    public void callJavaOperationShouldThrowExceptionWhenBusinessDataIsNotFound() throws Exception {\n        //given\n        doThrow(SBusinessDataNotFoundException.class).when(businessDataReloader).reloadEntitySoftly(pojo);\n\n        //when - then\n        assertThatExceptionOfType(SBusinessDataNotFoundException.class)\n                .isThrownBy(() -> businessDataService.callJavaOperation(pojo, pojo, \"getName\",\n                        String.class.getName()));\n    }\n\n    @Test\n    public void callJavaOperationShouldThrowExceptionWhenInvokeFails() {\n        assertThatExceptionOfType(SBusinessDataRepositoryException.class)\n                .isThrownBy(() -> businessDataService.callJavaOperation(pojo, pojo, \"someMethod\",\n                        String.class.getName()));\n    }\n\n    @Test\n    public void callJavaOperationShouldSetValue() throws Exception {\n        //given\n        doReturn(pojo).when(businessDataReloader).reloadEntitySoftly(pojo);\n\n        //when\n        final EntityPojo pojoObject = (EntityPojo) businessDataService.callJavaOperation(pojo, NEW_NAME, \"setName\",\n                String.class.getName());\n\n        assertThat(pojoObject).as(\"should return object\").isNotNull();\n        assertThat(pojoObject.getName()).as(\"should have set name\").isEqualTo(NEW_NAME);\n    }\n\n    @Test\n    public void callJavaOperationSetValueToNull() throws Exception {\n        //given\n        doReturn(pojo).when(businessDataReloader).reloadEntitySoftly(pojo);\n\n        //when\n        businessDataService.callJavaOperation(pojo, NEW_NAME, \"setName\", String.class.getName());\n        final EntityPojo pojoObject = (EntityPojo) businessDataService.callJavaOperation(pojo, null, \"setName\",\n                String.class.getName());\n\n        //then\n        assertThat(pojoObject).as(\"should return object\").isNotNull();\n        assertThat(pojoObject.getName()).as(\"should have set name to null\").isNull();\n    }\n\n    @Test\n    public void callJavaOperationShouldSetEntityComposition() throws Exception {\n        //given\n        final EntityPojo compositionEntity = new EntityPojo(2L);\n\n        doReturn(pojo).when(businessDataReloader).reloadEntitySoftly(pojo);\n\n        //when\n        final EntityPojo pojoObject = (EntityPojo) businessDataService.callJavaOperation(pojo, compositionEntity,\n                \"setCompositionEntity\",\n                Entity.class.getName());\n\n        assertThat(pojoObject).as(\"should return object\").isNotNull();\n        assertThat(pojoObject.getCompositionEntity()).as(\"should have set entity\").isEqualTo(compositionEntity);\n        verify(businessDataReloader, never()).reloadEntity(any(Entity.class));\n    }\n\n    @Test\n    public void callJavaOperationShouldSetEntityAggregation() throws Exception {\n        //given\n        final EntityPojo aggregationEntity = new EntityPojo(2L);\n\n        doReturn(pojo).when(businessDataReloader).reloadEntitySoftly(pojo);\n        doReturn(aggregationEntity).when(businessDataReloader).reloadEntity(aggregationEntity);\n\n        //when\n        final EntityPojo pojoObject = (EntityPojo) businessDataService.callJavaOperation(pojo, aggregationEntity,\n                \"setAggregationEntity\",\n                Entity.class.getName());\n\n        assertThat(pojoObject).as(\"should return object\").isNotNull();\n        assertThat(pojoObject.getAggregationEntity()).as(\"should have set entity\").isEqualTo(aggregationEntity);\n        verify(businessDataReloader).reloadEntity(aggregationEntity);\n    }\n\n    @Test\n    public void callJavaOperationShouldWithProxyfiedEntityShouldUsedRealEntityClass() throws Exception {\n        //given\n        final EntityPojo entity = new EntityPojo(2L);\n        ServerProxyfier proxyfier = new ServerProxyfier(new ServerLazyLoader(businessDataRepository));\n        EntityPojo proxyfiedEntity = proxyfier.proxify(entity);\n\n        doReturn(pojo).when(businessDataReloader).reloadEntitySoftly(pojo);\n\n        //when\n        businessDataService.callJavaOperation(pojo, proxyfiedEntity, \"setAggregationEntity\",\n                Entity.class.getName());\n\n        verify(businessDataReloader).reloadEntitySoftly(pojo);\n\n        ArgumentCaptor<EntityPojo> captor = ArgumentCaptor.forClass(EntityPojo.class);\n        verify(businessDataReloader).reloadEntity(captor.capture());\n        assertThat(captor.getValue().getPersistenceId()).isEqualTo(2L);\n    }\n\n    @Test\n    public void callJavaOperationWithListOfProxyfiedEntitiesShouldUsedRealEntityClass() throws Exception {\n        //given\n        final EntityPojo entity = new EntityPojo(2L);\n        ServerProxyfier proxyfier = new ServerProxyfier(new ServerLazyLoader(businessDataRepository));\n        EntityPojo proxyfiedEntity = proxyfier.proxify(entity);\n\n        doReturn(pojo).when(businessDataReloader).reloadEntitySoftly(pojo);\n        // Cannot specify the real instance (instead of any()) because of proxy object that does not match (Mockito):\n        doReturn(pojo.getClass()).when(businessDataReloader).getEntityRealClass(any(Entity.class));\n\n        //when\n        businessDataService.callJavaOperation(pojo, Collections.singletonList(proxyfiedEntity),\n                \"setAggregationEntities\", List.class.getName());\n\n        verify(businessDataRepository).findByIds(entity.getClass(), Collections.singletonList(2L));\n    }\n\n    @Test\n    public void callJavaOperationShouldLoadEntitiesIfAggregation() throws Exception {\n        //given\n        final Long persistenceId1 = 1562L;\n        final Long persistenceId2 = 9658L;\n        final EntityPojo entity1 = new EntityPojo(persistenceId1);\n        final EntityPojo entity2 = new EntityPojo(persistenceId2);\n        final List<EntityPojo> entities = Arrays.asList(entity1, entity2);\n        final List<Long> keys = Arrays.asList(persistenceId1, persistenceId2);\n\n        doReturn(pojo).when(businessDataReloader).reloadEntitySoftly(pojo);\n        // Cannot specify the real instance (instead of any()) because of proxy object that does not match (Mockito):\n        doReturn(pojo.getClass()).when(businessDataReloader).getEntityRealClass(any(Entity.class));\n        doReturn(entities).when(businessDataRepository).findByIds(EntityPojo.class, keys);\n\n        //when\n        final EntityPojo pojoObject = (EntityPojo) businessDataService.callJavaOperation(pojo, entities,\n                \"setAggregationEntities\", List.class.getName());\n\n        assertThat(pojoObject).as(\"should return object\").isNotNull();\n        assertThat(pojoObject.getAggregationEntities()).as(\"should have set entities\").isEqualTo(entities);\n        verify(businessDataRepository).findByIds(EntityPojo.class, keys);\n    }\n\n    @Test\n    public void callJavaOperationShouldNotLoadEntitiesIfComposition() throws Exception {\n        //given\n        final Long persistenceId1 = 1562L;\n        final Long persistenceId2 = 9658L;\n        final EntityPojo entity1 = new EntityPojo(persistenceId1);\n        final EntityPojo entity2 = new EntityPojo(persistenceId2);\n        final List<EntityPojo> entities = Arrays.asList(entity1, entity2);\n        final List<Long> keys = Arrays.asList(persistenceId1, persistenceId2);\n\n        doReturn(pojo).when(businessDataReloader).reloadEntitySoftly(pojo);\n\n        //when\n        final EntityPojo pojoObject = (EntityPojo) businessDataService.callJavaOperation(pojo, entities,\n                \"setCompositionEntities\", List.class.getName());\n\n        assertThat(pojoObject).as(\"should return object\").isNotNull();\n        assertThat(pojoObject.getCompositionEntities()).as(\"should have set entities\").isEqualTo(entities);\n        verify(businessDataRepository, never()).findByIds(EntityPojo.class, keys);\n    }\n\n    @Test\n    public void callJavaOperationShouldThrowExceptionWhenPersistenceIdIsNull() {\n        //given\n        final Long persistenceId1 = 1562L;\n\n        final EntityPojo entity1 = new EntityPojo(persistenceId1);\n        final EntityPojo entity2 = new EntityPojo(null);\n        final List<EntityPojo> entities = Arrays.asList(entity1, entity2);\n        doReturn(EntityPojo.class).when(businessDataReloader).getEntityRealClass(entity2);\n\n        //when - then\n        assertThatExceptionOfType(SBusinessDataNotFoundException.class)\n                .isThrownBy(() -> businessDataService.callJavaOperation(pojo, entities, \"setAggregationEntities\",\n                        List.class.getName()))\n                .withMessage(\"Forbidden instance of \" + EntityPojo.class.getName() + \" found. \" +\n                        \"It is only possible to reference persisted instances in an aggregation relation.\");\n    }\n\n    @Test\n    public void callJavaOperationWithEmptyList() throws Exception {\n        //given\n        doReturn(pojo).when(businessDataReloader).reloadEntitySoftly(pojo);\n\n        //when\n        final EntityPojo pojoObject = (EntityPojo) businessDataService.callJavaOperation(pojo, List.of(),\n                \"setAggregationEntities\", List.class.getName());\n\n        assertThat(pojoObject).as(\"should return object\").isNotNull();\n        assertThat(pojoObject.getAggregationEntities()).as(\"should have set entities\").isEmpty();\n    }\n\n    @Test\n    public void callJavaOperationShouldSetListValue() throws Exception {\n        //given\n        final List<Long> longs = Arrays.asList(1L, 2L);\n        doReturn(pojo).when(businessDataReloader).reloadEntitySoftly(pojo);\n\n        //when\n        final EntityPojo pojoObject = (EntityPojo) businessDataService.callJavaOperation(pojo, longs, \"setNumbers\",\n                List.class.getName());\n\n        //then\n        assertThat(pojoObject).as(\"should return object\").isNotNull();\n        assertThat(pojoObject.getNumbers()).as(\"should have set list\").hasSize(2).contains(1L, 2L);\n    }\n\n    @Test\n    public void callJavaOperationShouldSetListToNull() throws Exception {\n        //given\n        final List<Long> longs = Arrays.asList(1L, 2L);\n        doReturn(pojo).when(businessDataReloader).reloadEntitySoftly(pojo);\n\n        //when\n        businessDataService.callJavaOperation(pojo, longs, \"setNumbers\", List.class.getName());\n        final EntityPojo pojoObject = (EntityPojo) businessDataService.callJavaOperation(pojo, null, \"setNumbers\",\n                List.class.getName());\n\n        //then\n        assertThat(pojoObject).as(\"should return object\").isNotNull();\n        assertThat(pojoObject.getNumbers()).as(\"should have set list to null\").isNull();\n    }\n\n    @Test\n    public void shouldSetListOnBusinessDataReplaceTheList() throws Exception {\n        //given\n        final Long persistenceId2 = 9658L;\n        final EntityPojo entity1 = new EntityPojo(1562L);\n        final EntityPojo entity2 = new EntityPojo(persistenceId2);\n        final EntityPojo entityPojo = new EntityPojo(1L);\n        entityPojo.getAggregationEntities().add(entity1);\n\n        final List<EntityPojo> newEntities = List.of(entity2);\n        final List<Long> keys2 = List.of(persistenceId2);\n        doReturn(entityPojo).when(businessDataReloader).reloadEntitySoftly(entityPojo);\n        // Cannot specify the real instance (instead of any()) because of proxy object that does not match (Mockito):\n        doReturn(pojo.getClass()).when(businessDataReloader).getEntityRealClass(any(Entity.class));\n        doReturn(newEntities).when(businessDataRepository).findByIds(entity2.getClass(), keys2);\n\n        //when\n        final EntityPojo resultPojo = (EntityPojo) businessDataService.callJavaOperation(entityPojo, newEntities,\n                \"setAggregationEntities\",\n                List.class.getName());\n\n        assertThat(resultPojo).as(\"should return object\").isNotNull();\n        assertThat(resultPojo.getAggregationEntities()).as(\"should have set entities\").containsExactly(entity2);\n\n    }\n\n    @Test\n    public void should_loadClass_find_the_class() throws Exception {\n        //when\n        final Class<? extends Entity> loadClass = businessDataService.loadClass(pojo.getClass().getName());\n\n        //then\n        assertThat(loadClass).isEqualTo(pojo.getClass());\n    }\n\n    @Test\n    public void should_loadClass_throw_exception() {\n        assertThatExceptionOfType(SBusinessDataRepositoryException.class)\n                .isThrownBy(() -> businessDataService.loadClass(\"not a class\"));\n    }\n\n    @Test\n    public void should_getJsonEntity_serialize_entity() throws Exception {\n        //given\n        doReturn(pojo).when(businessDataRepository).findById(pojo.getClass(), pojo.getPersistenceId());\n\n        //when\n        businessDataService.getJsonEntity(pojo.getClass().getName(), pojo.getPersistenceId(),\n                PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n        //then\n        verify(jsonEntitySerializer).serializeEntity(pojo, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n    }\n\n    @Test\n    public void should_getJsonEntity_throw_exception() throws Exception {\n        //given\n        doReturn(pojo).when(businessDataRepository).findById(pojo.getClass(), pojo.getPersistenceId());\n        doThrow(SBusinessDataRepositorySerializationException.class).when(jsonEntitySerializer).serializeEntity(pojo,\n                PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n        //when then exception\n        assertThatExceptionOfType(SBusinessDataRepositoryException.class)\n                .isThrownBy(() -> businessDataService.getJsonEntity(pojo.getClass().getName(), pojo.getPersistenceId(),\n                        PARAMETER_BUSINESSDATA_CLASS_URI_VALUE));\n    }\n\n    @Test\n    public void should_getJsonChildEntity_return_emptyObject() throws Exception {\n        //given\n        final EntityPojo parentEntity = new EntityPojo(1562L);\n        final EntityPojo childEntity = new EntityPojo(156842L);\n        parentEntity.setAggregationEntity(childEntity);\n\n        doReturn(parentEntity).when(businessDataRepository).findById(parentEntity.getClass(),\n                parentEntity.getPersistenceId());\n\n        //when\n        final Serializable jsonChildEntity = businessDataService.getJsonChildEntity(parentEntity.getClass().getName(),\n                parentEntity.getPersistenceId(),\n                \"nullChildEntity\",\n                PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n        //then\n        assertThat(jsonChildEntity).isEqualTo(JsonBusinessDataSerializer.EMPTY_OBJECT);\n\n    }\n\n    @Test\n    public void should_getJsonChildEntity_serialize_entity() throws Exception {\n        //given\n        final EntityPojo parentEntity = new EntityPojo(1562L);\n        final EntityPojo childEntity = new EntityPojo(156842L);\n        parentEntity.setAggregationEntity(childEntity);\n\n        doReturn(parentEntity).when(businessDataRepository).findById(parentEntity.getClass(),\n                parentEntity.getPersistenceId());\n        doReturn(childEntity).when(businessDataRepository).unwrap(childEntity);\n\n        //when\n        businessDataService.getJsonChildEntity(parentEntity.getClass().getName(), parentEntity.getPersistenceId(),\n                \"aggregationEntity\",\n                PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n        //then\n        verify(jsonEntitySerializer).serializeEntity(childEntity, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n    }\n\n    @Test\n    public void should_getJsonChildEntity_serialize_entity_list() throws Exception {\n        //given\n        final EntityPojo parentEntity = new EntityPojo(1562L);\n        final EntityPojo childEntity = new EntityPojo(156842L);\n        parentEntity.getAggregationEntities().add(childEntity);\n\n        doReturn(parentEntity).when(businessDataRepository).findById(parentEntity.getClass(),\n                parentEntity.getPersistenceId());\n\n        //when\n        businessDataService.getJsonChildEntity(parentEntity.getClass().getName(), parentEntity.getPersistenceId(),\n                \"aggregationEntities\",\n                PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n        //then\n        final List<Entity> list = new ArrayList<>();\n        list.add(childEntity);\n        verify(jsonEntitySerializer).serializeEntities(list, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n    }\n\n    @Test\n    public void getJsonQueryEntities_should_return_list_of_entities_as_json() throws Exception {\n        //given\n        final EntityPojo entity = new EntityPojo(1562L);\n        final Map<String, Serializable> parameters = new HashMap<>();\n        parameters.put(PARAMETER_STRING, \"a\");\n        parameters.put(PARAMETER_INTEGER, \"12\");\n        parameters.put(PARAMETER_LONG, \"34\");\n\n        doReturn(entity.getClass()).when(businessDataService).loadClass(entity.getClass().getName());\n\n        final List<Entity> entities = new ArrayList<>();\n        entities.add(entity);\n        doReturn(entities).when(businessDataRepository).findListByNamedQuery(anyString(), any(), anyMap(),\n                anyInt(), anyInt());\n\n        //given\n        final BusinessObjectModel businessObjectModel = getBusinessObjectModel(entity);\n        doReturn(businessObjectModel).when(businessDataModelRepository).getBusinessObjectModel();\n\n        //when\n        businessDataService.getJsonQueryEntities(entity.getClass().getName(), \"query\", parameters, 0, 10,\n                PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n        //then\n        // Query returns String.class (not List), so hasMultipleResults is false\n        verify(jsonEntitySerializer).serializeEntityQueryResult(entities, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE, true,\n                false);\n    }\n\n    @Test\n    public void getJsonQueryEntities_should_return_count_result_as_json() throws Exception {\n\n        //given\n        doReturn(EntityPojo.class).when(businessDataService).loadClass(EntityPojo.class.getName());\n        doReturn(Collections.singletonList(5L)).when(businessDataRepository).findListByNamedQuery(anyString(), any(),\n                anyMap(), anyInt(), anyInt());\n        doReturn(getBusinessObjectModel(new EntityPojo(1562L))).when(businessDataModelRepository)\n                .getBusinessObjectModel();\n\n        //when\n        businessDataService.getJsonQueryEntities(EntityPojo.class.getName(), \"countForFind\", new HashMap<>(), 0, 1,\n                PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n        //then\n        verify(jsonEntitySerializer).serializeScalarResult(Collections.singletonList(5L), EntityPojo.class.getName(),\n                true);\n    }\n\n    @Test\n    public void getJsonQueryEntities_should_throw_exception_when_query_not_found() throws Exception {\n        //given\n        final EntityPojo entity = new EntityPojo(1562L);\n        final Map<String, Serializable> parameters = new HashMap<>();\n        parameters.put(PARAMETER_STRING, \"a\");\n        parameters.put(PARAMETER_INTEGER, \"12\");\n        parameters.put(PARAMETER_LONG, \"34\");\n\n        doReturn(entity.getClass()).when(businessDataService).loadClass(entity.getClass().getName());\n\n        final BusinessObjectModel businessObjectModel = getBusinessObjectModel(entity);\n        doReturn(businessObjectModel).when(businessDataModelRepository).getBusinessObjectModel();\n\n        //when then exception\n        assertThatExceptionOfType(SBusinessDataRepositoryException.class)\n                .isThrownBy(() -> businessDataService.getJsonQueryEntities(entity.getClass().getName(), \"wrongQuery\",\n                        parameters, 0, 10, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE))\n                .withMessage(\"unable to get query wrongQuery for business object \" + EntityPojo.class.getName());\n    }\n\n    @Test\n    public void getJsonQueryEntities_should_find_provided_query() throws Exception {\n\n        //given\n        final EntityPojo entity = new EntityPojo(1562L);\n        final Map<String, Serializable> parameters = new HashMap<>();\n        parameters.put(PARAMETER_STRING, \"a\");\n        parameters.put(PARAMETER_INTEGER, \"12\");\n        parameters.put(PARAMETER_LONG, \"34\");\n\n        doReturn(entity.getClass()).when(businessDataService).loadClass(entity.getClass().getName());\n        final BusinessObjectModel businessObjectModel = getBusinessObjectModel(entity);\n        doReturn(businessObjectModel).when(businessDataModelRepository).getBusinessObjectModel();\n        Query countQuery = new Query(\"countForFind\", \"query\", Long.class.getName());\n        doReturn(countQuery).when(countQueryProvider).getCountQueryDefinition(any(BusinessObject.class),\n                any(Query.class));\n        final long count = 5L;\n        doReturn(count).when(businessDataRepository).findByNamedQuery(eq(\"EntityPojo.countForFind\"), eq(Long.class),\n                anyMap());\n\n        //when\n        final int startIndex = 3;\n        final int maxResults = 10;\n        final BusinessDataQueryResult queryResult = businessDataService.getJsonQueryEntities(\n                entity.getClass().getName(), \"find\", parameters, startIndex,\n                maxResults,\n                PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n        //then\n        verify(businessDataRepository).findByNamedQuery(eq(\"EntityPojo.countForFind\"), eq(Long.class), anyMap());\n        final BusinessDataQueryMetadata businessDataQueryMetadata = queryResult.getBusinessDataQueryMetadata();\n        assertThat(businessDataQueryMetadata)\n                .as(\"should retrieve metadata\")\n                .isNotNull();\n\n        assertThat(businessDataQueryMetadata.getStartIndex()).isEqualTo(startIndex);\n        assertThat(businessDataQueryMetadata.getMaxResults()).isEqualTo(maxResults);\n        assertThat(businessDataQueryMetadata.getCount()).isEqualTo(count);\n\n    }\n\n    @Test\n    public void getJsonQueryEntities_should_check_parameters() throws Exception {\n        //given\n        final EntityPojo entity = new EntityPojo(1562L);\n        doReturn(entity.getClass()).when(businessDataService).loadClass(entity.getClass().getName());\n\n        final BusinessObjectModel businessObjectModel = getBusinessObjectModel(entity);\n        doReturn(businessObjectModel).when(businessDataModelRepository).getBusinessObjectModel();\n\n        //when then exception\n        assertThatExceptionOfType(SBusinessDataRepositoryException.class)\n                .isThrownBy(() -> businessDataService.getJsonQueryEntities(entity.getClass().getName(), \"query\", null,\n                        0, 10, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE))\n                .withMessageContainingAll(\"parameter(s) are missing for query named query :\", PARAMETER_INTEGER,\n                        PARAMETER_STRING, PARAMETER_LONG);\n    }\n\n    private BusinessObjectModel getBusinessObjectModel(final EntityPojo entity) {\n        BusinessObjectModel businessObjectModel;\n        businessObjectModel = new BusinessObjectModel();\n\n        final Query query = new Query(\"query\", \"content\", String.class.getName());\n        query.getQueryParameters().add(new QueryParameter(PARAMETER_STRING, String.class.getName()));\n        query.getQueryParameters().add(new QueryParameter(PARAMETER_INTEGER, Integer.class.getName()));\n        query.getQueryParameters().add(new QueryParameter(PARAMETER_LONG, Long.class.getName()));\n\n        final BusinessObject businessObject = new BusinessObject();\n        businessObject.setQualifiedName(entity.getClass().getName());\n        businessObject.setQueries(List.of(query));\n        businessObjectModel.getBusinessObjects().add(businessObject);\n\n        return businessObjectModel;\n    }\n\n    @Test\n    public void getJsonEntities_should_serialize_entities() throws Exception {\n        final long identifier1 = 1983L;\n        final long identifier2 = 1990L;\n        final EntityPojo pojo1 = new EntityPojo(identifier1);\n        final EntityPojo pojo2 = new EntityPojo(identifier2);\n        final List<Long> identifiers = new ArrayList<>();\n        identifiers.add(identifier1);\n        identifiers.add(identifier2);\n        final List<EntityPojo> pojos = new ArrayList<>();\n        pojos.add(pojo1);\n        pojos.add(pojo2);\n        when(businessDataRepository.findByIdentifiers(EntityPojo.class, identifiers)).thenReturn(pojos);\n\n        businessDataService.getJsonEntities(EntityPojo.class.getName(), identifiers,\n                PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n        verify(jsonEntitySerializer).serializeEntities(pojos, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n    }\n\n    @Test\n    public void getJsonEntities_should_throw_exception_if_the_serialization_fails() throws Exception {\n        final long identifier1 = 1983L;\n        final long identifier2 = 1990L;\n        final EntityPojo pojo1 = new EntityPojo(identifier1);\n        final EntityPojo pojo2 = new EntityPojo(identifier2);\n        final List<Long> identifiers = new ArrayList<>();\n        identifiers.add(identifier1);\n        identifiers.add(identifier2);\n        final List<EntityPojo> pojos = new ArrayList<>();\n        pojos.add(pojo1);\n        pojos.add(pojo2);\n        when(businessDataRepository.findByIdentifiers(EntityPojo.class, identifiers)).thenReturn(pojos);\n        when(jsonEntitySerializer.serializeEntities(pojos, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE))\n                .thenThrow(new SBusinessDataRepositorySerializationException(\"exception\"));\n\n        assertThatExceptionOfType(SBusinessDataRepositoryException.class)\n                .isThrownBy(() -> businessDataService.getJsonEntities(EntityPojo.class.getName(), identifiers,\n                        PARAMETER_BUSINESSDATA_CLASS_URI_VALUE));\n    }\n\n    @Test\n    public void getJsonQueryEntities_should_return_legacy_count_format_when_standardShapeDisabled() throws Exception {\n        when(jsonEntitySerializer.isStandardShapeEnabled()).thenReturn(false);\n\n        final EntityPojo entity = new EntityPojo(1562L);\n        doReturn(entity.getClass()).when(businessDataService).loadClass(entity.getClass().getName());\n\n        // Build a BDM model with the count query\n        final Query countQuery = new Query(\"customcount\", \"SELECT count(w) FROM Watched w\", Long.class.getName());\n        final BusinessObject businessObject = new BusinessObject();\n        businessObject.setQualifiedName(entity.getClass().getName());\n        businessObject.setQueries(List.of(countQuery));\n        final BusinessObjectModel bom = new BusinessObjectModel();\n        bom.setBusinessObjects(List.of(businessObject));\n        doReturn(bom).when(businessDataModelRepository).getBusinessObjectModel();\n\n        final List<Long> countList = Collections.singletonList(10L);\n        doReturn(countList).when(businessDataRepository)\n                .findListByNamedQuery(anyString(), any(), anyMap(), anyInt(), anyInt());\n\n        businessDataService.getJsonQueryEntities(entity.getClass().getName(), \"customcount\", new HashMap<>(), 0, 10,\n                PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n        verify(jsonEntitySerializer).serializeScalarResult(countList, entity.getClass().getName(), false);\n    }\n\n    @Test\n    public void getJsonQueryEntities_should_return_legacy_single_entity_list_format_when_standardShapeDisabled()\n            throws Exception {\n        when(jsonEntitySerializer.isStandardShapeEnabled()).thenReturn(false);\n\n        final EntityPojo entity = new EntityPojo(1562L);\n        doReturn(entity.getClass()).when(businessDataService).loadClass(entity.getClass().getName());\n\n        // Build a BDM model with a query returning one entity\n        final Query singleQuery = new Query(\"findByUserAndRequest\", \"SELECT w FROM Watched w\",\n                entity.getClass().getName());\n        final BusinessObject businessObject = new BusinessObject();\n        businessObject.setQualifiedName(entity.getClass().getName());\n        businessObject.setQueries(List.of(singleQuery));\n        final BusinessObjectModel bom = new BusinessObjectModel();\n        bom.setBusinessObjects(List.of(businessObject));\n        doReturn(bom).when(businessDataModelRepository).getBusinessObjectModel();\n\n        final List<EntityPojo> results = Collections.singletonList(entity);\n        doReturn(results).when(businessDataRepository)\n                .findListByNamedQuery(anyString(), any(), anyMap(), anyInt(), anyInt());\n\n        businessDataService.getJsonQueryEntities(entity.getClass().getName(), \"findByUserAndRequest\", new HashMap<>(),\n                0, 10,\n                PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n        // Legacy mode serializes a single result as a list\n        // Query returns entity class (not List), so hasMultipleResults is false\n        verify(jsonEntitySerializer).serializeEntityQueryResult(results, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE, false,\n                false);\n    }\n\n    @Test\n    public void getJsonQueryEntities_should_return_legacy_list_format_when_standardShapeDisabled() throws Exception {\n        when(jsonEntitySerializer.isStandardShapeEnabled()).thenReturn(false);\n\n        final EntityPojo entity1 = new EntityPojo(1562L);\n        final EntityPojo entity2 = new EntityPojo(2000L);\n        doReturn(entity1.getClass()).when(businessDataService).loadClass(entity1.getClass().getName());\n\n        // Build a BDM model with a list-returning query\n        final Query listQuery = new Query(\"findByCaseId\", \"SELECT w FROM Watched w\", List.class.getName());\n        final BusinessObject businessObject = new BusinessObject();\n        businessObject.setQualifiedName(entity1.getClass().getName());\n        businessObject.setQueries(List.of(listQuery));\n        final BusinessObjectModel bom = new BusinessObjectModel();\n        bom.setBusinessObjects(List.of(businessObject));\n        doReturn(bom).when(businessDataModelRepository).getBusinessObjectModel();\n\n        final List<EntityPojo> results = List.of(entity1, entity2);\n        doReturn(results).when(businessDataRepository)\n                .findListByNamedQuery(anyString(), any(), anyMap(), anyInt(), anyInt());\n\n        businessDataService.getJsonQueryEntities(entity1.getClass().getName(), \"findByCaseId\", new HashMap<>(), 0, 10,\n                PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n        // Legacy list behavior is unchanged\n        // Query returns List.class, so hasMultipleResults is true\n        verify(jsonEntitySerializer).serializeEntityQueryResult(results, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE, false,\n                true);\n    }\n\n    @Test\n    public void getJsonQueryEntities_should_handle_Long_scalar_query_with_standard_shape() throws Exception {\n        when(jsonEntitySerializer.isStandardShapeEnabled()).thenReturn(true);\n\n        final EntityPojo entity = new EntityPojo(1562L);\n        doReturn(entity.getClass()).when(businessDataService).loadClass(entity.getClass().getName());\n\n        final Query countQuery = new Query(\"customcount\", \"SELECT count(w) FROM Watched w\", Long.class.getName());\n        final BusinessObject businessObject = new BusinessObject();\n        businessObject.setQualifiedName(entity.getClass().getName());\n        businessObject.setQueries(List.of(countQuery));\n        final BusinessObjectModel bom = new BusinessObjectModel();\n        bom.setBusinessObjects(List.of(businessObject));\n        doReturn(bom).when(businessDataModelRepository).getBusinessObjectModel();\n\n        final List<Long> countList = Collections.singletonList(10L);\n        doReturn(countList).when(businessDataRepository)\n                .findListByNamedQuery(anyString(), any(), anyMap(), anyInt(), anyInt());\n        doReturn(\"{ \\\"value\\\": 10 }\").when(jsonEntitySerializer)\n                .serializeScalarResult(countList, entity.getClass().getName(), true);\n\n        BusinessDataQueryResult result = businessDataService.getJsonQueryEntities(entity.getClass().getName(),\n                \"customcount\",\n                new HashMap<>(), 0, 10, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n        assertThat(result.getJsonResults().toString()).contains(\"{\", \"value\", \"10\");\n    }\n\n    @Test\n    public void getJsonQueryEntities_should_return_standard_list_format_when_standardShapeEnabled() throws Exception {\n        when(jsonEntitySerializer.isStandardShapeEnabled()).thenReturn(true);\n\n        final EntityPojo entity1 = new EntityPojo(1562L);\n        final EntityPojo entity2 = new EntityPojo(2000L);\n        doReturn(entity1.getClass()).when(businessDataService).loadClass(entity1.getClass().getName());\n\n        final Query listQuery = new Query(\"findByCaseId\", \"SELECT w FROM Watched w\", List.class.getName());\n        final BusinessObject businessObject = new BusinessObject();\n        businessObject.setQualifiedName(entity1.getClass().getName());\n        businessObject.setQueries(List.of(listQuery));\n        final BusinessObjectModel bom = new BusinessObjectModel();\n        bom.setBusinessObjects(List.of(businessObject));\n        doReturn(bom).when(businessDataModelRepository).getBusinessObjectModel();\n\n        final List<EntityPojo> results = List.of(entity1, entity2);\n        doReturn(results).when(businessDataRepository)\n                .findListByNamedQuery(anyString(), any(), anyMap(), anyInt(), anyInt());\n\n        businessDataService.getJsonQueryEntities(entity1.getClass().getName(), \"findByCaseId\", new HashMap<>(), 0, 10,\n                PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n        // Lists remain the same in standard mode\n        // Query returns List.class, so hasMultipleResults is true\n        verify(jsonEntitySerializer).serializeEntityQueryResult(results, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE, true,\n                true);\n    }\n\n    @Test\n    public void getJsonQueryEntities_should_return_standard_single_entity_format_when_standardShapeEnabled()\n            throws Exception {\n        when(jsonEntitySerializer.isStandardShapeEnabled()).thenReturn(true);\n\n        final EntityPojo entity = new EntityPojo(1562L);\n        doReturn(entity.getClass()).when(businessDataService).loadClass(entity.getClass().getName());\n\n        // Build a BDM model with a query returning one entity\n        final Query singleQuery = new Query(\"findByUserAndRequest\", \"SELECT w FROM Watched w\",\n                entity.getClass().getName());\n        final BusinessObject businessObject = new BusinessObject();\n        businessObject.setQualifiedName(entity.getClass().getName());\n        businessObject.setQueries(List.of(singleQuery));\n        final BusinessObjectModel bom = new BusinessObjectModel();\n        bom.setBusinessObjects(List.of(businessObject));\n        doReturn(bom).when(businessDataModelRepository).getBusinessObjectModel();\n\n        final List<EntityPojo> results = Collections.singletonList(entity);\n        doReturn(results).when(businessDataRepository)\n                .findListByNamedQuery(anyString(), any(), anyMap(), anyInt(), anyInt());\n\n        businessDataService.getJsonQueryEntities(entity.getClass().getName(), \"findByUserAndRequest\", new HashMap<>(),\n                0, 10,\n                PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n        // Standard mode serializes a single result as an object (not array)\n        // Query returns entity class (not List), so hasMultipleResults is false\n        verify(jsonEntitySerializer).serializeEntityQueryResult(results, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE, true,\n                false);\n    }\n\n    @Test\n    public void getJsonQueryEntities_should_handle_Double_scalar_query_with_standard_shape() throws Exception {\n        when(jsonEntitySerializer.isStandardShapeEnabled()).thenReturn(true);\n\n        final EntityPojo entity = new EntityPojo(1562L);\n        doReturn(entity.getClass()).when(businessDataService).loadClass(entity.getClass().getName());\n\n        // Build a BDM model with a Double-returning query (e.g., AVG, MAX)\n        final Query avgQuery = new Query(\"averageSalary\", \"SELECT AVG(e.salary) FROM Employee e\",\n                Double.class.getName());\n        final BusinessObject businessObject = new BusinessObject();\n        businessObject.setQualifiedName(entity.getClass().getName());\n        businessObject.setQueries(List.of(avgQuery));\n        final BusinessObjectModel bom = new BusinessObjectModel();\n        bom.setBusinessObjects(List.of(businessObject));\n        doReturn(bom).when(businessDataModelRepository).getBusinessObjectModel();\n\n        final List<Double> avgResult = Collections.singletonList(55000.75);\n        doReturn(avgResult).when(businessDataRepository)\n                .findListByNamedQuery(anyString(), any(), anyMap(), anyInt(), anyInt());\n        doReturn(\"{ \\\"value\\\": 55000.75 }\").when(jsonEntitySerializer)\n                .serializeScalarResult(avgResult, entity.getClass().getName(), true);\n\n        BusinessDataQueryResult result = businessDataService.getJsonQueryEntities(entity.getClass().getName(),\n                \"averageSalary\",\n                new HashMap<>(), 0, 10, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n        assertThat(result.getJsonResults().toString()).contains(\"{\", \"value\", \"55000.75\");\n        verify(jsonEntitySerializer).serializeScalarResult(avgResult, entity.getClass().getName(), true);\n    }\n\n    @Test\n    public void getJsonQueryEntities_should_handle_Float_scalar_query_with_legacy_shape() throws Exception {\n        when(jsonEntitySerializer.isStandardShapeEnabled()).thenReturn(false);\n\n        final EntityPojo entity = new EntityPojo(1562L);\n        doReturn(entity.getClass()).when(businessDataService).loadClass(entity.getClass().getName());\n\n        // Build a BDM model with a Float-returning query\n        final Query maxQuery = new Query(\"maxScore\", \"SELECT MAX(e.score) FROM Employee e\", Float.class.getName());\n        final BusinessObject businessObject = new BusinessObject();\n        businessObject.setQualifiedName(entity.getClass().getName());\n        businessObject.setQueries(List.of(maxQuery));\n        final BusinessObjectModel bom = new BusinessObjectModel();\n        bom.setBusinessObjects(List.of(businessObject));\n        doReturn(bom).when(businessDataModelRepository).getBusinessObjectModel();\n\n        final List<Float> maxResult = Collections.singletonList(98.5f);\n        doReturn(maxResult).when(businessDataRepository)\n                .findListByNamedQuery(anyString(), any(), anyMap(), anyInt(), anyInt());\n\n        businessDataService.getJsonQueryEntities(entity.getClass().getName(), \"maxScore\", new HashMap<>(), 0, 10,\n                PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n        verify(jsonEntitySerializer).serializeScalarResult(maxResult, entity.getClass().getName(), false);\n    }\n\n    @Test\n    public void getJsonQueryEntities_should_handle_Integer_scalar_query_with_standard_shape() throws Exception {\n        when(jsonEntitySerializer.isStandardShapeEnabled()).thenReturn(true);\n\n        final EntityPojo entity = new EntityPojo(1562L);\n        doReturn(entity.getClass()).when(businessDataService).loadClass(entity.getClass().getName());\n\n        // Build a BDM model with an Integer-returning query\n        final Query sumQuery = new Query(\"sumAge\", \"SELECT SUM(e.age) FROM Employee e\", Integer.class.getName());\n        final BusinessObject businessObject = new BusinessObject();\n        businessObject.setQualifiedName(entity.getClass().getName());\n        businessObject.setQueries(List.of(sumQuery));\n        final BusinessObjectModel bom = new BusinessObjectModel();\n        bom.setBusinessObjects(List.of(businessObject));\n        doReturn(bom).when(businessDataModelRepository).getBusinessObjectModel();\n\n        final List<Integer> sumResult = Collections.singletonList(450);\n        doReturn(sumResult).when(businessDataRepository)\n                .findListByNamedQuery(anyString(), any(), anyMap(), anyInt(), anyInt());\n        doReturn(\"{ \\\"value\\\": 450 }\").when(jsonEntitySerializer)\n                .serializeScalarResult(sumResult, entity.getClass().getName(), true);\n\n        BusinessDataQueryResult result = businessDataService.getJsonQueryEntities(entity.getClass().getName(), \"sumAge\",\n                new HashMap<>(), 0, 10, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n        assertThat(result.getJsonResults().toString()).contains(\"{\", \"value\", \"450\");\n        verify(jsonEntitySerializer).serializeScalarResult(sumResult, entity.getClass().getName(), true);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/ConcurrencyIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.*;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\n\nimport javax.sql.DataSource;\nimport javax.transaction.UserTransaction;\n\nimport com.company.pojo.Employee;\nimport org.bonitasoft.engine.business.data.DataRetentionBdmTrackingService;\nimport org.bonitasoft.engine.business.data.JpaTestConfiguration;\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.dependency.impl.TenantDependencyService;\nimport org.bonitasoft.engine.platform.PlatformService;\nimport org.bonitasoft.engine.resources.TenantResourcesService;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@ContextConfiguration(locations = { \"/testContext.xml\" })\npublic class ConcurrencyIT {\n\n    private JPABusinessDataRepositoryImpl businessDataRepository;\n\n    @Autowired\n    @Qualifier(\"businessDataDataSource\")\n    private DataSource datasource;\n\n    @Autowired\n    @Qualifier(\"notManagedBizDataSource\")\n    private DataSource modelDatasource;\n\n    @Autowired\n    @Qualifier(\"jpa-test-configuration\")\n    private JpaTestConfiguration configuration;\n\n    private JdbcTemplate jdbcTemplate;\n\n    private UserTransaction ut;\n\n    private final ClassLoaderService classLoaderService = mock(ClassLoaderService.class);\n\n    @Before\n    public void setUp() throws Exception {\n        if (jdbcTemplate == null) {\n            jdbcTemplate = new JdbcTemplate(datasource);\n        }\n        final SchemaManagerUpdate schemaManager = new SchemaManagerUpdate(configuration.getJpaModelConfiguration());\n        final BusinessDataModelRepositoryImpl businessDataModelRepositoryImpl = spy(\n                new BusinessDataModelRepositoryImpl(mock(PlatformService.class), mock(TenantDependencyService.class),\n                        classLoaderService, schemaManager, mock(TenantResourcesService.class),\n                        mock(DataRetentionBdmTrackingService.class)));\n        final UserTransactionService transactionService = mock(UserTransactionService.class);\n        businessDataRepository = spy(\n                new JPABusinessDataRepositoryImpl(transactionService, businessDataModelRepositoryImpl,\n                        configuration.getJpaConfiguration(), classLoaderService,\n                        mock(DataRetentionBdmTrackingService.class)));\n        doReturn(true).when(businessDataModelRepositoryImpl).isBDMDeployed();\n\n        ut = com.arjuna.ats.jta.UserTransaction.userTransaction();\n        ut.begin();\n\n        final Set<String> classNames = new HashSet<>();\n        classNames.add(Employee.class.getName());\n\n        businessDataModelRepositoryImpl.update(classNames);\n        businessDataRepository.start();\n        ut.commit();\n    }\n\n    @After\n    public void tearDown() {\n        businessDataRepository.stop();\n\n        final JdbcTemplate jdbcTemplate = new JdbcTemplate(modelDatasource);\n        try {\n            jdbcTemplate.update(\"drop table Employee\");\n        } catch (final Exception e) {\n            // ignore drop of non-existing table\n        }\n    }\n\n    @Test\n    public void addConcurrentlyEmployeesShouldCreateAllTheEmployees() throws Exception {\n        final ExecutorService threadPoolExecutor = new ThreadPoolExecutor(5, 10, 5000, TimeUnit.MILLISECONDS,\n                new LinkedBlockingQueue<>());\n        final int expected = 10;\n        for (int i = 0; i < expected; i++) {\n            final Thread thread = new AddNewEmployeeThread(businessDataRepository, i);\n            threadPoolExecutor.submit(thread);\n        }\n        threadPoolExecutor.shutdown();\n        threadPoolExecutor.awaitTermination(3 * expected, TimeUnit.SECONDS);\n\n        ut = com.arjuna.ats.jta.UserTransaction.userTransaction();\n        ut.begin();\n        final List<Employee> employees = businessDataRepository.findList(Employee.class, \"SELECT e FROM Employee e\",\n                null, 0, 100);\n        ut.commit();\n\n        assertThat(employees).hasSize(expected);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/CountQueryProviderTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.bdm.model.BusinessObject;\nimport org.bonitasoft.engine.bdm.model.Query;\nimport org.bonitasoft.engine.bdm.model.assertion.QueryAssert;\nimport org.bonitasoft.engine.bdm.model.field.FieldType;\nimport org.bonitasoft.engine.bdm.model.field.SimpleField;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class CountQueryProviderTest {\n\n    public static final String QUALIFIED_NAME = \"com.corp.Arrival\";\n\n    private CountQueryProvider provider;\n\n    @Before\n    public void setUp() throws Exception {\n        provider = new CountQueryProvider();\n    }\n\n    @Test\n    public void getCountQueryDefinition_should_return_related_count_query_for_generated_queries() throws Exception {\n        //given\n        final BusinessObject bo = buildBusinessObject();\n        Query find = new Query(\"find\", \"\", List.class.getName());\n\n        //when\n        Query countQueryDefinition = provider.getCountQueryDefinition(bo, find);\n\n        //then\n        QueryAssert.assertThat(countQueryDefinition).isNotNull();\n        QueryAssert.assertThat(countQueryDefinition).hasName(\"countForFind\");\n    }\n\n    @Test\n    public void getCountQueryDefinition_should_return_null_when_base_query_is_not_multiple() throws Exception {\n        //given\n        final BusinessObject bo = buildBusinessObject();\n        Query find = new Query(\"findByPeople\", \"\", QUALIFIED_NAME);\n\n        //when\n        Query countQueryDefinition = provider.getCountQueryDefinition(bo, find);\n\n        //then\n        QueryAssert.assertThat(countQueryDefinition).isNull();\n    }\n\n    @Test\n    public void getCountQueryDefinition_should_return_related_query_for_multiple_custom_queries() throws Exception {\n        //given\n        final BusinessObject bo = buildBusinessObject();\n        bo.addQuery(\"firstMultiCustomQuery\", \"\", List.class.getName());\n        Query secondMultiCustomQuery = bo.addQuery(\"secondMultiCustomQuery\", \"\", List.class.getName());\n        bo.addQuery(\"thirdMultiCustomQuery\", \"\", List.class.getName());\n        bo.addQuery(\"countForFirstMultiCustomQuery\", \"\", Long.class.getName());\n        Query countForSecondMultiCustomQuery = bo.addQuery(\"countForSecondMultiCustomQuery\", \"\", Long.class.getName());\n        bo.addQuery(\"countForThirdMultiCustomQuery\", \"\", Long.class.getName());\n\n        //when\n        Query countQueryDefinition = provider.getCountQueryDefinition(bo, secondMultiCustomQuery);\n\n        //then\n        QueryAssert.assertThat(countQueryDefinition).isNotNull();\n        QueryAssert.assertThat(countQueryDefinition).hasName(countForSecondMultiCustomQuery.getName());\n    }\n\n    @Test\n    public void getCountQueryDefinition_should_return_null_when_count_query_does_not_return_a_long() throws Exception {\n        //given\n        final BusinessObject bo = buildBusinessObject();\n        Query secondMultiCustomQuery = bo.addQuery(\"secondMultiCustomQuery\", \"\", List.class.getName());\n        bo.addQuery(\"countForSecondMultiCustomQuery\", \"\", QUALIFIED_NAME);\n\n        //when\n        Query countQueryDefinition = provider.getCountQueryDefinition(bo, secondMultiCustomQuery);\n\n        //then\n        QueryAssert.assertThat(countQueryDefinition).isNull();\n    }\n\n    @Test\n    public void getCountQueryDefinition_should_return_null_when_base_query_returns_single_result() throws Exception {\n        //given\n        final BusinessObject bo = buildBusinessObject();\n        Query secondMultiCustomQuery = bo.addQuery(\"secondMultiCustomQuery\", \"\", QUALIFIED_NAME);\n        bo.addQuery(\"countForSecondMultiCustomQuery\", \"\", Long.class.getName());\n\n        //when\n        Query countQueryDefinition = provider.getCountQueryDefinition(bo, secondMultiCustomQuery);\n\n        //then\n        QueryAssert.assertThat(countQueryDefinition).isNull();\n    }\n\n    private BusinessObject buildBusinessObject() {\n        final BusinessObject bo = new BusinessObject();\n        bo.setQualifiedName(QUALIFIED_NAME);\n        final SimpleField field = new SimpleField();\n        field.setName(\"people\");\n        field.setType(FieldType.INTEGER);\n        bo.addField(field);\n        bo.addUniqueConstraint(\"someName\", \"people\");\n        return bo;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/DataRetentionBdmTrackingServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport static org.assertj.core.api.Assertions.*;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport org.bonitasoft.engine.business.data.DataRetentionBdmTrackingRepository;\nimport org.bonitasoft.engine.business.data.SDataRetentionBdmTrackingException;\nimport org.bonitasoft.engine.business.data.model.SDataRetentionBdmTracking;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.services.SPersistenceException;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\n@ExtendWith(MockitoExtension.class)\nclass DataRetentionBdmTrackingServiceImplTest {\n\n    @Mock\n    private DataRetentionBdmTrackingRepository bdmTrackingRepository;\n\n    @InjectMocks\n    private DataRetentionBdmTrackingServiceImpl service;\n\n    @Test\n    void create_should_insert_tracking_record() throws Exception {\n        //when\n        service.create(42L, \"com.example.Invoice\");\n\n        //then\n        var captor = ArgumentCaptor.forClass(SDataRetentionBdmTracking.class);\n        verify(bdmTrackingRepository).create(captor.capture());\n        var tracking = captor.getValue();\n        assertThat(tracking.getDataId()).isEqualTo(42L);\n        assertThat(tracking.getDataClassname()).isEqualTo(\"com.example.Invoice\");\n        assertThat(tracking.getCreatedAt()).isPositive();\n        assertThat(tracking.getLastModifiedAt()).isEqualTo(tracking.getCreatedAt());\n    }\n\n    @Test\n    void create_should_wrap_persistence_exception() throws Exception {\n        //given\n        doThrow(new SPersistenceException(\"DB error\")).when(bdmTrackingRepository).create(any());\n\n        //when-then\n        assertThatThrownBy(() -> service.create(1L, \"com.example.Invoice\"))\n                .isInstanceOf(SDataRetentionBdmTrackingException.class)\n                .hasMessageContaining(\"Failed to create BDM tracking record\")\n                .hasCauseInstanceOf(SPersistenceException.class);\n    }\n\n    @Test\n    void upsert_should_update_existing_record() throws Exception {\n        //given\n        var existing = SDataRetentionBdmTracking.builder()\n                .id(1L).dataId(42L).dataClassname(\"com.example.Invoice\")\n                .createdAt(1000L).lastModifiedAt(1000L).build();\n        when(bdmTrackingRepository.getByDataIdAndClassname(42L, \"com.example.Invoice\"))\n                .thenReturn(existing);\n\n        //when\n        service.upsert(42L, \"com.example.Invoice\");\n\n        //then\n        verify(bdmTrackingRepository).updateLastModifiedDate(existing);\n        assertThat(existing.getLastModifiedAt()).isGreaterThan(1000L);\n        assertThat(existing.getCreatedAt()).isEqualTo(1000L); // unchanged\n    }\n\n    @Test\n    void upsert_should_create_record_when_missing() throws Exception {\n        //given\n        when(bdmTrackingRepository.getByDataIdAndClassname(42L, \"com.example.Invoice\"))\n                .thenReturn(null);\n\n        //when\n        service.upsert(42L, \"com.example.Invoice\");\n\n        //then\n        var captor = ArgumentCaptor.forClass(SDataRetentionBdmTracking.class);\n        verify(bdmTrackingRepository).create(captor.capture());\n        var tracking = captor.getValue();\n        assertThat(tracking.getDataId()).isEqualTo(42L);\n        assertThat(tracking.getDataClassname()).isEqualTo(\"com.example.Invoice\");\n    }\n\n    @Test\n    void upsert_should_wrap_read_exception() throws Exception {\n        //given\n        when(bdmTrackingRepository.getByDataIdAndClassname(42L, \"com.example.Invoice\"))\n                .thenThrow(new SBonitaReadException(\"DB error\"));\n\n        //when-then\n        assertThatThrownBy(() -> service.upsert(42L, \"com.example.Invoice\"))\n                .isInstanceOf(SDataRetentionBdmTrackingException.class)\n                .hasMessageContaining(\"Failed to upsert data retention tracking record\")\n                .hasCauseInstanceOf(SBonitaReadException.class);\n    }\n\n    @Test\n    void updateLastModifiedDate_should_delegate_to_repository() throws Exception {\n        //when\n        service.updateLastModifiedDate(5L);\n\n        //then\n        var captor = ArgumentCaptor.forClass(SDataRetentionBdmTracking.class);\n        verify(bdmTrackingRepository).updateLastModifiedDate(captor.capture());\n        var tracking = captor.getValue();\n        assertThat(tracking.getId()).isEqualTo(5L);\n        assertThat(tracking.getLastModifiedAt()).isPositive();\n    }\n\n    @Test\n    void updateLastModifiedDate_should_wrap_persistence_exception() throws Exception {\n        //given\n        doThrow(new SPersistenceException(\"DB error\")).when(bdmTrackingRepository).updateLastModifiedDate(any());\n\n        //when-then\n        assertThatThrownBy(() -> service.updateLastModifiedDate(5L))\n                .isInstanceOf(SDataRetentionBdmTrackingException.class)\n                .hasMessageContaining(\"Failed to update BDM tracking record\")\n                .hasCauseInstanceOf(SPersistenceException.class);\n    }\n\n    @Test\n    void delete_should_delegate_to_repository() throws Exception {\n        //given\n        when(bdmTrackingRepository.delete(42L, \"com.example.Invoice\")).thenReturn(1);\n\n        //when\n        service.delete(42L, \"com.example.Invoice\");\n\n        //then\n        verify(bdmTrackingRepository).delete(42L, \"com.example.Invoice\");\n    }\n\n    @Test\n    void delete_should_not_fail_when_no_record_found() throws Exception {\n        //given\n        when(bdmTrackingRepository.delete(42L, \"com.example.Invoice\")).thenReturn(0);\n\n        //when-then — should not throw\n        assertThatNoException().isThrownBy(() -> service.delete(42L, \"com.example.Invoice\"));\n        verify(bdmTrackingRepository).delete(42L, \"com.example.Invoice\");\n    }\n\n    @Test\n    void delete_should_wrap_persistence_exception() throws Exception {\n        //given\n        when(bdmTrackingRepository.delete(42L, \"com.example.Invoice\"))\n                .thenThrow(new SPersistenceException(\"DB error\"));\n\n        //when-then\n        assertThatThrownBy(() -> service.delete(42L, \"com.example.Invoice\"))\n                .isInstanceOf(SDataRetentionBdmTrackingException.class)\n                .hasMessageContaining(\"Failed to delete data retention tracking record\")\n                .hasCauseInstanceOf(SPersistenceException.class);\n    }\n\n    @Test\n    void deleteAll_should_delegate_to_repository() throws Exception {\n        //when\n        service.deleteAll();\n\n        //then\n        verify(bdmTrackingRepository).deleteAll(any());\n    }\n\n    @Test\n    void deleteAll_should_wrap_persistence_exception() throws Exception {\n        //given\n        doThrow(new SPersistenceException(\"DB error\")).when(bdmTrackingRepository).deleteAll(any());\n\n        //when-then\n        assertThatThrownBy(() -> service.deleteAll())\n                .isInstanceOf(SDataRetentionBdmTrackingException.class)\n                .hasMessageContaining(\"Failed to delete all BDM tracking records\")\n                .hasCauseInstanceOf(SPersistenceException.class);\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/EntityPojo.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport java.io.Serial;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\nimport javax.persistence.CascadeType;\nimport javax.persistence.NamedQueries;\nimport javax.persistence.NamedQuery;\nimport javax.persistence.OneToMany;\nimport javax.persistence.OneToOne;\n\nimport org.bonitasoft.engine.bdm.Entity;\n\n@NamedQueries({\n        @NamedQuery(name = \"EntityPojo.findByFirstName\", query = \"SELECT e\\nFROM Employee e\\nWHERE e.firstName= :firstName\\nORDER BY e.persistenceId\"),\n        @NamedQuery(name = \"EntityPojo.find\", query = \"SELECT e\\nFROM Employee e\\nORDER BY e.persistenceId\"),\n        @NamedQuery(name = \"EntityPojo.countForFind\", query = \"SELECT count(e)\\nFROM Employee e\\n\")\n})\npublic class EntityPojo implements Entity {\n\n    @Serial\n    private static final long serialVersionUID = 1L;\n    private String name;\n    private Boolean bool;\n    private Date date;\n    private List<Long> numbers;\n\n    @OneToOne(cascade = CascadeType.MERGE)\n    private Entity aggregationEntity;\n\n    @OneToOne(cascade = CascadeType.ALL)\n    private Entity compositionEntity;\n\n    @OneToOne(cascade = CascadeType.ALL)\n    private Entity nullChildEntity;\n\n    @OneToMany(cascade = CascadeType.MERGE)\n    private List<Entity> aggregationEntities;\n\n    @OneToMany(cascade = CascadeType.ALL)\n    private List<Entity> compositionEntities;\n\n    private Long persistenceId;\n\n    public EntityPojo(final Long persistenceId) {\n        this.persistenceId = persistenceId;\n        aggregationEntities = new ArrayList<>();\n        compositionEntities = new ArrayList<>();\n    }\n\n    public EntityPojo() {\n        aggregationEntities = new ArrayList<>();\n        compositionEntities = new ArrayList<>();\n    }\n\n    @Override\n    public Long getPersistenceId() {\n        return persistenceId;\n    }\n\n    public void setPersistenceId(final Long persistenceId) {\n        this.persistenceId = persistenceId;\n    }\n\n    @Override\n    public Long getPersistenceVersion() {\n        return 2L;\n    }\n\n    public Date getDate() {\n        return date;\n    }\n\n    public void setDate(final Date date) {\n        this.date = date;\n    }\n\n    public Boolean getBool() {\n        return bool;\n    }\n\n    public void setBool(final Boolean bool) {\n        this.bool = bool;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(final String name) {\n        this.name = name;\n    }\n\n    public List<Long> getNumbers() {\n        return numbers;\n    }\n\n    public void setNumbers(final List<Long> numbers) {\n        this.numbers = numbers;\n    }\n\n    public Entity getCompositionEntity() {\n        return compositionEntity;\n    }\n\n    public void setCompositionEntity(final Entity compositionEntity) {\n        this.compositionEntity = compositionEntity;\n    }\n\n    public Entity getAggregationEntity() {\n        return aggregationEntity;\n    }\n\n    public void setAggregationEntity(final Entity aggregationEntity) {\n        this.aggregationEntity = aggregationEntity;\n    }\n\n    public Entity getNullChildEntity() {\n        return null;\n    }\n\n    public void setNullChildEntity(final Entity nullChildEntity) {\n        this.nullChildEntity = nullChildEntity;\n    }\n\n    public List<Entity> getAggregationEntities() {\n        return aggregationEntities;\n    }\n\n    public void setAggregationEntities(List<Entity> aggregationEntities) {\n        this.aggregationEntities = aggregationEntities;\n    }\n\n    public List<Entity> getCompositionEntities() {\n        return compositionEntities;\n    }\n\n    public void setCompositionEntities(List<Entity> compositionEntities) {\n        this.compositionEntities = compositionEntities;\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/EntitySerializerPojo.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.OffsetDateTime;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\nimport javax.persistence.Column;\nimport javax.persistence.Convert;\nimport javax.persistence.ElementCollection;\nimport javax.persistence.FetchType;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.Id;\nimport javax.persistence.Lob;\nimport javax.persistence.OrderColumn;\nimport javax.persistence.Temporal;\nimport javax.persistence.TemporalType;\nimport javax.persistence.Version;\n\nimport org.apache.commons.beanutils.converters.DateTimeConverter;\nimport org.apache.commons.lang3.builder.EqualsBuilder;\nimport org.apache.commons.lang3.builder.HashCodeBuilder;\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.bonitasoft.engine.business.data.generator.DateConverter;\nimport org.bonitasoft.engine.business.data.generator.OffsetDateTimeConverter;\n\npublic class EntitySerializerPojo implements Entity {\n\n    @Id\n    @GeneratedValue\n    private Long persistenceId;\n    @Version\n    private Long persistenceVersion;\n    @Column(name = \"ASTRING\", nullable = true, length = 255)\n    private String aString;\n    @Column(name = \"ABOOLEAN\", nullable = true)\n    private Boolean aBoolean;\n    @Column(name = \"ADATE\", nullable = true)\n    @Temporal(TemporalType.TIMESTAMP)\n    private Date aDate;\n    @Column(name = \"ADOUBLE\", nullable = true)\n    private Double aDouble;\n    @Column(name = \"AFLOAT\", nullable = true)\n    private Float aFloat;\n    @Column(name = \"AINTEGER\", nullable = true)\n    private Integer aInteger;\n    @Column(name = \"ALONG\", nullable = true)\n    private Long aLong;\n    @Column(name = \"ATEXT\", nullable = true)\n    @Lob\n    private String aText;\n    @ElementCollection(fetch = FetchType.EAGER)\n    @OrderColumn\n    @Column(name = \"MANYLONG\", nullable = true)\n    private List<Long> manyLong = new ArrayList<Long>(10);\n    @ElementCollection(fetch = FetchType.EAGER)\n    @OrderColumn\n    @Column(name = \"MANYSTRING\", nullable = true, length = 255)\n    private List<String> manyString = new ArrayList<String>(10);\n\n    @Convert(converter = DateConverter.class)\n    @Column(name = \"LOCALDATE\", nullable = true, length = 10)\n    private LocalDate aLocalDate;\n\n    @Convert(converter = DateTimeConverter.class)\n    @Column(name = \"LOCALDATETIME\", nullable = true, length = 30)\n    private LocalDateTime aLocalDateTime;\n\n    @Convert(converter = OffsetDateTimeConverter.class)\n    @Column(name = \"OFFSETDATETIME\", nullable = true, length = 40)\n    private OffsetDateTime anOffsetDateTime;\n\n    public EntitySerializerPojo() {\n    }\n\n    public OffsetDateTime getAnOffsetDateTime() {\n        return anOffsetDateTime;\n    }\n\n    public void setAnOffsetDateTime(OffsetDateTime anOffsetDateTime) {\n        this.anOffsetDateTime = anOffsetDateTime;\n    }\n\n    public void setPersistenceId(Long persistenceId) {\n        this.persistenceId = persistenceId;\n    }\n\n    public Long getPersistenceId() {\n        return persistenceId;\n    }\n\n    public void setPersistenceVersion(Long persistenceVersion) {\n        this.persistenceVersion = persistenceVersion;\n    }\n\n    public Long getPersistenceVersion() {\n        return persistenceVersion;\n    }\n\n    public void setAString(String aString) {\n        this.aString = aString;\n    }\n\n    public String getAString() {\n        return aString;\n    }\n\n    public void setABoolean(Boolean aBoolean) {\n        this.aBoolean = aBoolean;\n    }\n\n    public Boolean isABoolean() {\n        return aBoolean;\n    }\n\n    public void setADate(Date aDate) {\n        this.aDate = aDate;\n    }\n\n    public LocalDate getALocalDate() {\n        return aLocalDate;\n    }\n\n    public void setALocalDate(LocalDate localDate) {\n        this.aLocalDate = localDate;\n    }\n\n    public Date getADate() {\n        return aDate;\n    }\n\n    public void setADouble(Double aDouble) {\n        this.aDouble = aDouble;\n    }\n\n    public Double getADouble() {\n        return aDouble;\n    }\n\n    public void setAFloat(Float aFloat) {\n        this.aFloat = aFloat;\n    }\n\n    public Float getAFloat() {\n        return aFloat;\n    }\n\n    public void setAInteger(Integer aInteger) {\n        this.aInteger = aInteger;\n    }\n\n    public Integer getAInteger() {\n        return aInteger;\n    }\n\n    public void setALong(Long aLong) {\n        this.aLong = aLong;\n    }\n\n    public Long getALong() {\n        return aLong;\n    }\n\n    public void setAText(String aText) {\n        this.aText = aText;\n    }\n\n    public String getAText() {\n        return aText;\n    }\n\n    public void setManyLong(List<Long> manyLong) {\n        if (this.manyLong == null) {\n            this.manyLong = manyLong;\n        } else {\n            this.manyLong.clear();\n            this.manyLong.addAll(manyLong);\n        }\n    }\n\n    public List<Long> getManyLong() {\n        return manyLong;\n    }\n\n    public void addToManyLong(Long addTo) {\n        List manyLong = getManyLong();\n        manyLong.add(addTo);\n    }\n\n    public void removeFromManyLong(Long removeFrom) {\n        List manyLong = getManyLong();\n        manyLong.remove(removeFrom);\n    }\n\n    public void setManyString(List<String> manyString) {\n        if (this.manyString == null) {\n            this.manyString = manyString;\n        } else {\n            this.manyString.clear();\n            this.manyString.addAll(manyString);\n        }\n    }\n\n    public List<String> getManyString() {\n        return manyString;\n    }\n\n    public void addToManyString(String addTo) {\n        List manyString = getManyString();\n        manyString.add(addTo);\n    }\n\n    public void removeFromManyString(String removeFrom) {\n        List manyString = getManyString();\n        manyString.remove(removeFrom);\n    }\n\n    public LocalDateTime getALocalDateTime() {\n        return aLocalDateTime;\n    }\n\n    public void setALocalDateTime(LocalDateTime aLocalDateTime) {\n        this.aLocalDateTime = aLocalDateTime;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n\n        if (!(o instanceof EntitySerializerPojo that))\n            return false;\n\n        return new EqualsBuilder()\n                .append(getPersistenceId(), that.getPersistenceId())\n                .append(getPersistenceVersion(), that.getPersistenceVersion())\n                .append(aString, that.aString)\n                .append(aBoolean, that.aBoolean)\n                .append(aDate, that.aDate)\n                .append(aDouble, that.aDouble)\n                .append(aFloat, that.aFloat)\n                .append(aInteger, that.aInteger)\n                .append(aLong, that.aLong)\n                .append(aText, that.aText)\n                .append(getManyLong(), that.getManyLong())\n                .append(getManyString(), that.getManyString())\n                .append(aLocalDate, that.aLocalDate)\n                .append(aLocalDateTime, that.aLocalDateTime)\n                .append(getAnOffsetDateTime(), that.getAnOffsetDateTime())\n                .isEquals();\n    }\n\n    @Override\n    public int hashCode() {\n        return new HashCodeBuilder(17, 37)\n                .append(getPersistenceId())\n                .append(getPersistenceVersion())\n                .append(aString)\n                .append(aBoolean)\n                .append(aDate)\n                .append(aDouble)\n                .append(aFloat)\n                .append(aInteger)\n                .append(aLong)\n                .append(aText)\n                .append(getManyLong())\n                .append(getManyString())\n                .append(aLocalDate)\n                .append(aLocalDateTime)\n                .append(getAnOffsetDateTime())\n                .toHashCode();\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/FakeHibernateProxyEntity.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.hibernate.proxy.AbstractLazyInitializer;\nimport org.hibernate.proxy.HibernateProxy;\nimport org.hibernate.proxy.LazyInitializer;\n\n/**\n * author Emmanuel Duchastenier\n */\npublic class FakeHibernateProxyEntity implements HibernateProxy, Entity {\n\n    @Override\n    public Object writeReplace() {\n        return null;\n    }\n\n    @Override\n    public LazyInitializer getHibernateLazyInitializer() {\n        return new AbstractLazyInitializer() {\n\n            @Override\n            public Class getPersistentClass() {\n                return EntityPojo.class;\n            }\n\n        };\n    }\n\n    @Override\n    public Long getPersistenceId() {\n        return null;\n    }\n\n    @Override\n    public Long getPersistenceVersion() {\n        return null;\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/HibernateProxySerializationTest.java",
    "content": "/**\n * Copyright (C) 2026 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;\nimport static org.assertj.core.api.Assertions.assertThatNoException;\n\nimport com.company.model.Address;\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.hibernate.proxy.HibernateProxy;\nimport org.junit.Test;\n\n/**\n * Verifies JSON serialization behavior for BDM entities when Hibernate returns\n * {@link HibernateProxy} instances for relationships or top-level entities.\n * <p>\n * Initialized proxies are unwrapped so their fields serialize as if they were real entities,\n * while uninitialized proxies remain opaque to avoid triggering lazy loading.\n */\npublic class HibernateProxySerializationTest {\n\n    private static final String PARAMETER_BUSINESSDATA_CLASS_URI_VALUE = \"/businessdata/{className}/{id}/{field}\";\n\n    private final JsonBusinessDataSerializerImpl serializer = new JsonBusinessDataSerializerImpl();\n\n    /**\n     * An initialized {@link HibernateProxy} child entity serializes with all its fields.\n     */\n    @Test\n    public void serializeEntity_should_include_child_fields_when_child_is_hibernate_proxy() throws Exception {\n        // given\n        Address realAddress = createAddress(123L, \"Rue Gustave Eiffel\", 32f);\n        AddressHibernateProxy proxyAddress = new AddressHibernateProxy(realAddress);\n\n        PersonWithProxyAddress person = new PersonWithProxyAddress(1L, \"John Doe\");\n        person.setAddress(proxyAddress);\n\n        // when\n        String json = serializer.serializeEntity(person, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n        // then - all fields should be present, including child entity fields\n        assertThatJson(json).node(\"persistenceId\").isEqualTo(1);\n        assertThatJson(json).node(\"name\").isEqualTo(\"John Doe\");\n        assertThatJson(json).node(\"address.persistenceId\").isEqualTo(123);\n        assertThatJson(json).node(\"address.street\").isEqualTo(\"Rue Gustave Eiffel\");\n        assertThatJson(json).node(\"address.number\").isEqualTo(32.0);\n    }\n\n    /**\n     * A non-proxy child entity serializes with all its fields.\n     */\n    @Test\n    public void serializeEntity_should_include_all_fields_when_child_is_not_proxy() throws Exception {\n        // given\n        Address realAddress = createAddress(123L, \"Rue Gustave Eiffel\", 32f);\n\n        PersonWithProxyAddress person = new PersonWithProxyAddress(1L, \"John Doe\");\n        person.setAddress(realAddress);\n\n        // when\n        String json = serializer.serializeEntity(person, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n        // then - all fields should be present\n        assertThatJson(json).node(\"persistenceId\").isEqualTo(1);\n        assertThatJson(json).node(\"name\").isEqualTo(\"John Doe\");\n        assertThatJson(json).node(\"address.persistenceId\").isEqualTo(123);\n        assertThatJson(json).node(\"address.street\").isEqualTo(\"Rue Gustave Eiffel\");\n        assertThatJson(json).node(\"address.number\").isEqualTo(32.0);\n    }\n\n    /**\n     * Manually unwrapping a {@link HibernateProxy} before serialization produces the\n     * same result as letting the serializer unwrap it.\n     */\n    @Test\n    public void serializeEntity_should_include_all_fields_when_proxy_is_manually_unwrapped() throws Exception {\n        // given\n        Address realAddress = createAddress(123L, \"Rue Gustave Eiffel\", 32f);\n        AddressHibernateProxy proxyAddress = new AddressHibernateProxy(realAddress);\n\n        // Unwrap the proxy manually\n        Entity unwrappedAddress = unwrapProxy(proxyAddress);\n\n        PersonWithProxyAddress person = new PersonWithProxyAddress(1L, \"John Doe\");\n        person.setAddress(unwrappedAddress);\n\n        // when\n        String json = serializer.serializeEntity(person, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n        // then - all fields should be present when proxy is unwrapped\n        assertThatJson(json).node(\"persistenceId\").isEqualTo(1);\n        assertThatJson(json).node(\"name\").isEqualTo(\"John Doe\");\n        assertThatJson(json).node(\"address.persistenceId\").isEqualTo(123);\n        assertThatJson(json).node(\"address.street\").isEqualTo(\"Rue Gustave Eiffel\");\n        assertThatJson(json).node(\"address.number\").isEqualTo(32.0);\n    }\n\n    /**\n     * A top-level {@link HibernateProxy} entity serializes with all its fields.\n     */\n    @Test\n    public void serializeEntity_should_include_all_fields_when_entity_is_hibernate_proxy() throws Exception {\n        // given\n        Address realAddress = createAddress(123L, \"Rue Gustave Eiffel\", 32f);\n        AddressHibernateProxy proxyAddress = new AddressHibernateProxy(realAddress);\n\n        // when\n        String json = serializer.serializeEntity(proxyAddress, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n        // then - all fields should be present\n        assertThatJson(json).node(\"persistenceId\").isEqualTo(123);\n        assertThatJson(json).node(\"persistenceVersion\").isEqualTo(1);\n        assertThatJson(json).node(\"street\").isEqualTo(\"Rue Gustave Eiffel\");\n        assertThatJson(json).node(\"number\").isEqualTo(32.0);\n    }\n\n    @Test\n    public void serializeEntity_should_not_unwrap_lazy_uninitialized_proxy() throws Exception {\n        Address realAddress = createAddress(123L, \"Rue Gustave Eiffel\", 32f);\n        AddressHibernateProxy lazyProxy = new AddressHibernateProxy(realAddress, false);\n\n        PersonWithProxyAddress person = new PersonWithProxyAddress(1L, \"John Doe\");\n        person.setAddress(lazyProxy);\n\n        String json = serializer.serializeEntity(person, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n        assertThatJson(json).node(\"persistenceId\").isEqualTo(1);\n        assertThatJson(json).node(\"name\").isEqualTo(\"John Doe\");\n        // LAZY proxy should NOT have its fields unwrapped\n        assertThatJson(json).node(\"address\").isObject();\n        assertThatJson(json).node(\"address.street\").isAbsent();\n    }\n\n    @Test\n    public void serializeEntity_should_not_throw_LazyInitializationException_for_initialized_proxy() throws Exception {\n        Address realAddress = createAddress(123L, \"Rue Gustave Eiffel\", 32f);\n        // initialized=true: proxy delegates to real entity without LazyInitializationException\n        AddressHibernateProxy proxyAddress = new AddressHibernateProxy(realAddress, true);\n\n        PersonWithProxyAddress person = new PersonWithProxyAddress(1L, \"John Doe\");\n        person.setAddress(proxyAddress);\n\n        // should NOT throw LazyInitializationException\n        assertThatNoException()\n                .isThrownBy(() -> serializer.serializeEntity(person, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE));\n    }\n\n    @Test\n    public void serializeEntity_should_serialize_cleanly_when_optional_child_is_null() throws Exception {\n        PersonWithProxyAddress person = new PersonWithProxyAddress(1L, \"John Doe\");\n        person.setAddress(null);\n\n        String json = serializer.serializeEntity(person, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n        assertThatJson(json).node(\"persistenceId\").isEqualTo(1);\n        assertThatJson(json).node(\"name\").isEqualTo(\"John Doe\");\n    }\n\n    @Test\n    public void serializeEntity_should_generate_links_for_json_ignore_field_on_proxy_child() throws Exception {\n        Address realAddress = createAddress(123L, \"Rue Gustave Eiffel\", 32f);\n        realAddress.setDoorCode(\"1234A\");\n        AddressHibernateProxy proxyAddress = new AddressHibernateProxy(realAddress);\n\n        PersonWithProxyAddress person = new PersonWithProxyAddress(1L, \"John Doe\");\n        person.setAddress(proxyAddress);\n\n        String json = serializer.serializeEntity(person, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n        // doorCode should NOT be a direct field (it's @JsonIgnore)\n        assertThatJson(json).node(\"address.doorCode\").isAbsent();\n        // But address should have links for the ignored field\n        assertThatJson(json).node(\"address.links\").isArray().isNotEmpty();\n    }\n\n    @Test\n    public void serializeEntity_should_serialize_collection_fields_on_proxy_entity() throws Exception {\n        Address realAddress = createAddress(123L, \"Rue Gustave Eiffel\", 32f);\n        realAddress.setFloors(java.util.Arrays.asList(0.0, 1.0, 4.0));\n        AddressHibernateProxy proxyAddress = new AddressHibernateProxy(realAddress);\n\n        String json = serializer.serializeEntity(proxyAddress, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n        assertThatJson(json).node(\"street\").isEqualTo(\"Rue Gustave Eiffel\");\n        assertThatJson(json).node(\"floors\").isArray().hasSize(3);\n    }\n\n    @Test\n    public void serializeEntities_should_include_all_fields_when_child_is_proxy() throws Exception {\n        Address realAddress = createAddress(123L, \"Rue Gustave Eiffel\", 32f);\n        AddressHibernateProxy proxyAddress = new AddressHibernateProxy(realAddress);\n\n        PersonWithProxyAddress person = new PersonWithProxyAddress(1L, \"John Doe\");\n        person.setAddress(proxyAddress);\n\n        String json = serializer.serializeEntities(\n                java.util.Collections.singletonList(person), PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n        assertThatJson(json).isArray().hasSize(1);\n        assertThatJson(json).node(\"[0].address.street\").isEqualTo(\"Rue Gustave Eiffel\");\n        assertThatJson(json).node(\"[0].address.number\").isEqualTo(32.0);\n    }\n\n    private static Address createAddress(Long persistenceId, String street, Float number) {\n        Address address = new Address();\n        address.setPersistenceId(persistenceId);\n        address.setPersistenceVersion(1L);\n        address.setStreet(street);\n        address.setNumber(number);\n        return address;\n    }\n\n    private static Entity unwrapProxy(Entity entity) {\n        if (entity instanceof HibernateProxy) {\n            HibernateProxy proxy = (HibernateProxy) entity;\n            return (Entity) proxy.getHibernateLazyInitializer().getImplementation();\n        }\n        return entity;\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/JPABusinessDataRepositoryImplIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport static com.company.pojo.EmployeeBuilder.anEmployee;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.fail;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\nimport static org.mockito.MockitoAnnotations.initMocks;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport javax.sql.DataSource;\nimport javax.transaction.UserTransaction;\n\nimport com.company.pojo.Employee;\nimport com.company.pojo.Person;\nimport org.bonitasoft.engine.business.data.DataRetentionBdmTrackingService;\nimport org.bonitasoft.engine.business.data.JpaTestConfiguration;\nimport org.bonitasoft.engine.business.data.NonUniqueResultException;\nimport org.bonitasoft.engine.business.data.SBusinessDataNotFoundException;\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.dependency.impl.TenantDependencyService;\nimport org.bonitasoft.engine.platform.PlatformService;\nimport org.bonitasoft.engine.resources.TenantResourcesService;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@ContextConfiguration(locations = { \"/testContext.xml\" })\npublic class JPABusinessDataRepositoryImplIT {\n\n    private JPABusinessDataRepositoryImpl businessDataRepository;\n\n    private UserTransactionService transactionService;\n\n    @Autowired\n    @Qualifier(\"businessDataDataSource\")\n    private DataSource datasource;\n\n    @Autowired\n    @Qualifier(\"notManagedBizDataSource\")\n    private DataSource modelDatasource;\n\n    @Autowired\n    @Qualifier(\"jpa-test-configuration\")\n    private JpaTestConfiguration configuration;\n\n    private JdbcTemplate jdbcTemplate;\n\n    private UserTransaction ut;\n\n    private ClassLoaderService classLoaderService = mock(ClassLoaderService.class);\n\n    @Before\n    public void setUp() throws Exception {\n        initMocks(this);\n        if (jdbcTemplate == null) {\n            jdbcTemplate = new JdbcTemplate(datasource);\n        }\n\n        transactionService = mock(UserTransactionService.class);\n\n        final SchemaManagerUpdate schemaManager = new SchemaManagerUpdate(configuration.getJpaModelConfiguration());\n        final BusinessDataModelRepositoryImpl businessDataModelRepositoryImpl = spy(\n                new BusinessDataModelRepositoryImpl(mock(PlatformService.class), mock(TenantDependencyService.class),\n                        classLoaderService, schemaManager, mock(TenantResourcesService.class),\n                        mock(DataRetentionBdmTrackingService.class)));\n        businessDataRepository = spy(\n                new JPABusinessDataRepositoryImpl(transactionService, businessDataModelRepositoryImpl,\n                        configuration.getJpaConfiguration(), classLoaderService,\n                        mock(DataRetentionBdmTrackingService.class)));\n        doReturn(true).when(businessDataModelRepositoryImpl).isBDMDeployed();\n        ut = com.arjuna.ats.jta.UserTransaction.userTransaction();\n        ut.begin();\n\n        final Set<String> classNames = new HashSet<>();\n        classNames.add(Employee.class.getName());\n        classNames.add(Person.class.getName());\n\n        businessDataModelRepositoryImpl.update(classNames);\n        businessDataRepository.start();\n    }\n\n    @After\n    public void tearDown() throws Exception {\n        ut.rollback();\n        businessDataRepository.stop();\n\n        final JdbcTemplate jdbcTemplate = new JdbcTemplate(modelDatasource);\n        for (final String tableName : Arrays.asList(\"Person_nickNames\", \"Employee\", \"PERSON\")) {\n            try {\n                jdbcTemplate.update(\"drop table \" + tableName);\n            } catch (final Exception e) {\n                System.out.println(e.getMessage());\n                // ignore drop of non-existing table\n            }\n        }\n    }\n\n    private Employee addEmployeeToRepository(final Employee employee) {\n        businessDataRepository.persist(employee);\n        return employee;\n    }\n\n    @Test(expected = SBusinessDataNotFoundException.class)\n    public void throwAnExceptionIfTheIdentifierIsNull() throws Exception {\n        businessDataRepository.findById(Employee.class, null);\n    }\n\n    @Test\n    public void findAnEmployeeByPrimaryKey() throws Exception {\n        Employee expectedEmployee = anEmployee().build();\n        expectedEmployee = addEmployeeToRepository(expectedEmployee);\n\n        final Employee employee = businessDataRepository.findById(Employee.class, expectedEmployee.getPersistenceId());\n\n        assertThat(employee).isEqualTo(expectedEmployee);\n    }\n\n    @Test(expected = SBusinessDataNotFoundException.class)\n    public void throwExceptionWhenEmployeeNotFound() throws Exception {\n        businessDataRepository.findById(Employee.class, -145l);\n    }\n\n    @Test\n    public void persistNewEmployeeShouldAddEmployeeInRepository() throws Exception {\n        final Employee employee = anEmployee().build();\n        businessDataRepository.persist(employee);\n\n        final Employee myEmployee = businessDataRepository.findById(Employee.class, employee.getPersistenceId());\n        assertThat(myEmployee).isEqualTo(employee);\n    }\n\n    @Test\n    public void persistANullEmployeeShouldDoNothing() throws Exception {\n        businessDataRepository.persist(null);\n\n        final Long count = businessDataRepository.find(Long.class, \"SELECT COUNT(*) FROM Employee e\", null);\n        assertThat(count).isEqualTo(0);\n    }\n\n    @Test\n    public void findListShouldAcceptParameterizedQuery() throws Exception {\n        final String firstName = \"anyName\";\n        Employee expectedEmployee = anEmployee().withFirstName(firstName).build();\n        expectedEmployee = addEmployeeToRepository(expectedEmployee);\n\n        final Map<String, Serializable> parameters = Collections.singletonMap(\"firstName\", (Serializable) firstName);\n        final Employee matti = businessDataRepository.find(Employee.class,\n                \"FROM Employee e WHERE e.firstName = :firstName\", parameters);\n\n        assertThat(matti).isEqualTo(expectedEmployee);\n    }\n\n    @Test(expected = NonUniqueResultException.class)\n    public void findShouldThrowExceptionWhenSeveralResultsMatch() throws Exception {\n        final String lastName = \"Kangaroo\";\n        addEmployeeToRepository(anEmployee().withLastName(lastName).build());\n        addEmployeeToRepository(anEmployee().withLastName(lastName).build());\n\n        final Map<String, Serializable> parameters = Collections.singletonMap(\"lastName\", (Serializable) lastName);\n        businessDataRepository.find(Employee.class, \"FROM Employee e WHERE e.lastName = :lastName\", parameters);\n    }\n\n    @Test\n    public void should_get_employees_by_id() throws Exception {\n        final String lastName = \"Kangaroo\";\n        Employee emp1 = addEmployeeToRepository(anEmployee().withLastName(lastName).build());\n        Employee emp2 = addEmployeeToRepository(anEmployee().withLastName(lastName).build());\n        Employee emp3 = addEmployeeToRepository(anEmployee().withLastName(lastName).build());\n\n        List<Employee> emps = businessDataRepository.findByIds(Employee.class,\n                Arrays.asList(emp1.getPersistenceId(), emp2.getPersistenceId()));\n\n        assertThat(emps).contains(emp1, emp2);\n        assertThat(emps).doesNotContain(emp3);\n    }\n\n    @Test\n    public void should_return_an_empty_list_when_getting_entities_with_empty_ids_list() throws Exception {\n        ArrayList<Long> emptyIdsList = new ArrayList<>();\n\n        List<Employee> emps = businessDataRepository.findByIds(Employee.class, emptyIdsList);\n\n        assertThat(emps).isEmpty();\n    }\n\n    @Test\n    public void returnNullnWhenFindingAnUnknownEmployee() throws Exception {\n        final Map<String, Serializable> parameters = Collections.singletonMap(\"lastName\",\n                (Serializable) \"Unknown_lastName\");\n        assertThat(\n                businessDataRepository.find(Employee.class, \"FROM Employee e WHERE e.lastName = :lastName\", parameters))\n                .isNull();\n    }\n\n    @Test(expected = IllegalStateException.class)\n    public void throwExceptionWhenUsingBDRWihtoutStartingIt() throws Exception {\n        businessDataRepository.stop();\n\n        businessDataRepository.findById(Employee.class, 124L);\n\n        businessDataRepository.start();\n    }\n\n    @Test\n    public void entityClassNames_is_an_empty_set_if_bdr_is_not_started() {\n        businessDataRepository.stop();\n\n        final Set<String> classNames = businessDataRepository.getEntityClassNames();\n\n        assertThat(classNames).isEmpty();\n    }\n\n    @Test\n    public void updateTwoFieldsInSameTransactionShouldModifySameObject() throws Exception {\n        final Employee originalEmployee = addEmployeeToRepository(anEmployee().build());\n        originalEmployee.setLastName(\"NewLastName\");\n        originalEmployee.setFirstName(\"NewFirstName\");\n\n        final Employee updatedEmployee = businessDataRepository.findById(Employee.class,\n                originalEmployee.getPersistenceId());\n        assertThat(updatedEmployee).isEqualTo(originalEmployee);\n    }\n\n    @Test\n    public void getEntityClassNames_should_return_the_classes_managed_by_the_bdr() {\n        final Set<String> classNames = businessDataRepository.getEntityClassNames();\n\n        assertThat(classNames).containsExactly(Employee.class.getName(), Person.class.getName());\n    }\n\n    @Test(expected = SBusinessDataNotFoundException.class)\n    public void aRemovedEntityShouldNotBeRetrievableAnyLonger() throws SBusinessDataNotFoundException {\n        Employee employee = null;\n        try {\n            employee = addEmployeeToRepository(anEmployee().build());\n\n            businessDataRepository.remove(employee);\n        } catch (final Exception e) {\n            fail(\"Should not fail here\");\n        }\n        if (employee != null) {\n            businessDataRepository.findById(Employee.class, employee.getPersistenceId());\n        }\n    }\n\n    @Test\n    public void remove_should_not_throw_an_exception_with_a_null_entity() {\n        businessDataRepository.remove(null);\n    }\n\n    @Test\n    public void remove_should_not_throw_an_exception_with_an_unknown_entity_without_an_id() {\n        businessDataRepository.remove(anEmployee().build());\n    }\n\n    @Test\n    public void remove_should_not_throw_an_exception_with_an_unknown_entity() {\n        final Employee newEmployee = addEmployeeToRepository(anEmployee().build());\n        businessDataRepository.remove(newEmployee);\n        businessDataRepository.remove(newEmployee);\n    }\n\n    @Test\n    public void findList_should_return_employee_list() {\n        final Employee e1 = addEmployeeToRepository(anEmployee().withFirstName(\"Hannu\").withLastName(\"balou\").build());\n        final Employee e2 = addEmployeeToRepository(anEmployee().withFirstName(\"Aliz\").withLastName(\"akkinen\").build());\n        final Employee e3 = addEmployeeToRepository(\n                anEmployee().withFirstName(\"Jean-Luc\").withLastName(\"akkinen\").build());\n\n        final List<Employee> employees = businessDataRepository.findList(Employee.class,\n                \"SELECT e FROM Employee e ORDER BY e.lastName ASC, e.firstName ASC\",\n                null, 0, 10);\n\n        assertThat(employees).containsExactly(e2, e3, e1);\n    }\n\n    @Test\n    public void findList_should_return_an_empty_list_when_asked_for_no_result() {\n        addEmployeeToRepository(anEmployee().withFirstName(\"Hannu\").withLastName(\"balou\").build());\n        addEmployeeToRepository(anEmployee().withFirstName(\"Aliz\").withLastName(\"akkinen\").build());\n        addEmployeeToRepository(anEmployee().withFirstName(\"Jean-Luc\").withLastName(\"akkinen\").build());\n\n        final List<Employee> employees = businessDataRepository.findList(Employee.class,\n                \"SELECT e FROM Employee e ORDER BY e.lastName ASC, e.firstName ASC\",\n                null, 0, 0);\n\n        assertThat(employees).isEmpty();\n    }\n\n    @Test\n    public void findListShouldReturnEmptyListIfNoResults() {\n        final Map<String, Serializable> parameters = Collections.singletonMap(\"firstName\", (Serializable) \"Jaakko\");\n        final List<Employee> employees = businessDataRepository.findList(Employee.class,\n                \"SELECT e FROM Employee e WHERE e.firstName=:firstName ORDER BY e.lastName, e.firstName\", parameters, 0,\n                10);\n        assertThat(employees).isEmpty();\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void findListShouldThrowAnExceptionIfAtLeastOneQueryParameterIsNotSet() {\n        businessDataRepository.findList(Employee.class,\n                \"SELECT e FROM Employee e WHERE e.firstName=:firstName ORDER BY e.lastName, e.firstName\", null, 0, 10);\n    }\n\n    @Test\n    public void findBasedOnAMultipleAttributeShouldReturnTheEntity() throws Exception {\n        final Person person = new Person();\n        person.addTo(\"John\");\n        person.addTo(\"James\");\n        person.addTo(\"Jack\");\n        businessDataRepository.persist(person);\n\n        final Person actual = businessDataRepository.find(Person.class,\n                \"SELECT p FROM Person p WHERE 'James' IN ELEMENTS(p.nickNames)\",\n                null);\n        assertThat(actual).isEqualTo(person);\n        actual.getNickNames().remove(\"James\");\n\n        final Person actual2 = businessDataRepository.find(Person.class,\n                \"SELECT p FROM Person p WHERE 'James' IN ELEMENTS(p.nickNames)\", null);\n        assertThat(actual2).isNull();\n    }\n\n    @Test\n    public void getEntityManagerAddATransactionSynchroInOrderToCleanTheThreadLocalWhenTheTxIsOver() throws Exception {\n        businessDataRepository.getEntityManager();\n\n        verify(transactionService).registerBonitaSynchronization(any(RemoveEntityManagerSynchronization.class));\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/JPABusinessDataRepositoryImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport static org.assertj.core.api.Assertions.*;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyLong;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.*;\n\nimport java.io.Serializable;\nimport java.lang.reflect.Field;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Map;\n\nimport javax.persistence.EntityGraph;\nimport javax.persistence.EntityManager;\nimport javax.persistence.EntityManagerFactory;\nimport javax.persistence.PersistenceException;\nimport javax.persistence.TypedQuery;\nimport javax.persistence.criteria.CriteriaBuilder;\nimport javax.persistence.criteria.CriteriaQuery;\nimport javax.persistence.criteria.Path;\nimport javax.persistence.criteria.Root;\nimport javax.persistence.criteria.Selection;\n\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.bonitasoft.engine.business.data.BusinessDataModelRepository;\nimport org.bonitasoft.engine.business.data.DataRetentionBdmTrackingService;\nimport org.bonitasoft.engine.business.data.SBusinessDataNotFoundException;\nimport org.bonitasoft.engine.business.data.SDataRetentionBdmTrackingException;\nimport org.bonitasoft.engine.classloader.ClassLoaderIdentifier;\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException;\nimport org.bonitasoft.engine.commons.exceptions.SRetryableException;\nimport org.bonitasoft.engine.transaction.STransactionNotFoundException;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport org.mockito.junit.jupiter.MockitoSettings;\nimport org.mockito.quality.Strictness;\n\n@ExtendWith(MockitoExtension.class)\n@MockitoSettings(strictness = Strictness.LENIENT)\nclass JPABusinessDataRepositoryImplTest {\n\n    private static final long PRIMARY_KEY_1 = 1L;\n\n    private JPABusinessDataRepositoryImpl repository;\n\n    @Mock\n    private UserTransactionService transactionService;\n\n    @Mock\n    private BusinessDataModelRepository businessDataModelRepository;\n\n    @Mock\n    private Map<String, Object> configuration;\n\n    @Mock\n    private ClassLoaderService classLoaderService;\n\n    @Mock\n    private DataRetentionBdmTrackingService bdmTrackingService;\n\n    @Mock\n    EntityManager manager;\n\n    private JPABusinessDataRepositoryImpl realJPABusinessDataRepository;\n\n    @BeforeEach\n    void setUp() {\n        realJPABusinessDataRepository = new JPABusinessDataRepositoryImpl(transactionService,\n                businessDataModelRepository, configuration, classLoaderService, bdmTrackingService);\n        repository = spy(\n                realJPABusinessDataRepository);\n        doReturn(manager).when(repository).getEntityManager();\n        doReturn(true).when(businessDataModelRepository).isBDMDeployed();\n        CriteriaBuilder criteriaBuilder = mock(CriteriaBuilder.class);\n        doReturn(criteriaBuilder).when(manager).getCriteriaBuilder();\n        CriteriaQuery criteriaQuery = mock(CriteriaQuery.class);\n        doReturn(criteriaQuery).when(criteriaBuilder).createQuery(any(Class.class));\n        doReturn(criteriaQuery).when(criteriaQuery).select(any(Selection.class));\n        Root root = mock(Root.class);\n        doReturn(root).when(criteriaQuery).from(any(Class.class));\n        doReturn(mock(Path.class)).when(root).get(anyString());\n    }\n\n    @Test\n    void should_constructor_add_listener_on_classloader() {\n        //then\n        verify(classLoaderService).addListener(ClassLoaderIdentifier.TENANT, realJPABusinessDataRepository);\n    }\n\n    @Test\n    void should_stop_close_entityManagerFactory() {\n        // given\n        EntityManagerFactory entityManagerFactory = mock(EntityManagerFactory.class);\n        doReturn(entityManagerFactory).when(repository).getEntityManagerFactory();\n        //when\n        repository.stop();\n        //then\n        verify(entityManagerFactory).close();\n    }\n\n    @Test\n    void should_onUpdate_recreate_the_entity_manager_factory() {\n        //given\n        EntityManagerFactory entityManagerFactory = mock(EntityManagerFactory.class);\n        doReturn(entityManagerFactory).when(repository).createEntityManagerFactory();\n        repository.start();\n        //when\n        repository.onUpdate(null);\n        //then\n        verify(entityManagerFactory).close();\n        verify(repository, times(2)).createEntityManagerFactory();\n    }\n\n    @Test\n    void onUpdate_should_not_throw_NPE_if_entity_manager_factory_is_null_but_still_recreate_the_factory() {\n        //given\n        EntityManagerFactory entityManagerFactory = mock(EntityManagerFactory.class);\n        doReturn(entityManagerFactory).when(repository).createEntityManagerFactory();\n        //when\n        // entityManagerFactory == null here\n        repository.onUpdate(mock(ClassLoader.class));\n        //then\n        verifyNoInteractions(entityManagerFactory);\n        verify(repository, times(1)).createEntityManagerFactory();\n    }\n\n    @Test\n    void findById_should_not_detach_entities() throws Exception {\n        final Address address1 = new Address(PRIMARY_KEY_1);\n        doReturn(address1).when(manager).find(Address.class, PRIMARY_KEY_1);\n\n        final Address result = repository.findById(Address.class, PRIMARY_KEY_1);\n\n        assertThat(result).isEqualTo(address1);\n        verify(manager, never()).detach(any(Address.class));\n    }\n\n    @Test\n    void findById_should_throw_an_exception_when_not_found() {\n        doReturn(null).when(manager).find(Address.class, PRIMARY_KEY_1);\n\n        assertThatExceptionOfType(SBusinessDataNotFoundException.class)\n                .isThrownBy(() -> repository.findById(Address.class, PRIMARY_KEY_1));\n    }\n\n    @Test\n    void findById_should_throw_an_exception_with_a_null_identifier() {\n        assertThatExceptionOfType(SBusinessDataNotFoundException.class)\n                .isThrownBy(() -> repository.findById(Address.class, null));\n    }\n\n    @Test\n    void findById_should_throw_retryable_when_persistenceException() {\n        //given\n        doThrow(PersistenceException.class).when(manager).find(Address.class, PRIMARY_KEY_1);\n\n        //when/then\n        assertThatExceptionOfType(SRetryableException.class)\n                .isThrownBy(() -> repository.findById(Address.class, PRIMARY_KEY_1));\n    }\n\n    @Test\n    void findByIds_should_throw_retryable_when_persistenceException() {\n        //given\n        doThrow(PersistenceException.class).when(manager).createQuery(any(CriteriaQuery.class));\n\n        //when/then\n        assertThatExceptionOfType(SRetryableException.class)\n                .isThrownBy(() -> repository.findByIds(Address.class, Collections.singletonList(PRIMARY_KEY_1)));\n    }\n\n    @Test\n    void findByIdentifiers_should_throw_retryable_when_persistenceException() {\n        //given\n        doThrow(PersistenceException.class).when(manager).find(Address.class, PRIMARY_KEY_1);\n\n        //when/then\n        assertThatExceptionOfType(SRetryableException.class)\n                .isThrownBy(\n                        () -> repository.findByIdentifiers(Address.class, Collections.singletonList(PRIMARY_KEY_1)));\n    }\n\n    @Test\n    void findByNamedQuery_should_throw_retryable_when_persistenceException() {\n        //given\n        TypedQuery typedQuery = mock(TypedQuery.class);\n        doThrow(PersistenceException.class).when(typedQuery).getSingleResult();\n        doReturn(typedQuery).when(manager).createNamedQuery(anyString(), any(Class.class));\n\n        //when/then\n        assertThatExceptionOfType(SRetryableException.class)\n                .isThrownBy(() -> repository.findByNamedQuery(\"queryName\", Address.class,\n                        Collections.<String, Serializable> emptyMap()));\n    }\n\n    @Test\n    void findListByNamedQuery_should_throw_retryable_when_persistenceException() {\n        //given\n        TypedQuery typedQuery = mock(TypedQuery.class);\n        doThrow(PersistenceException.class).when(typedQuery).getResultList();\n        doReturn(typedQuery).when(manager).createNamedQuery(anyString(), any(Class.class));\n\n        //when/then\n        assertThatExceptionOfType(SRetryableException.class)\n                .isThrownBy(() -> repository.findListByNamedQuery(\"queryName\", Address.class,\n                        Collections.<String, Serializable> emptyMap(), 0, 10));\n    }\n\n    @Test\n    void find_should_throw_retryable_when_persistenceException() {\n        //given\n        TypedQuery typedQuery = mock(TypedQuery.class);\n        doThrow(PersistenceException.class).when(typedQuery).getSingleResult();\n        doReturn(typedQuery).when(manager).createQuery(anyString(), any(Class.class));\n\n        //when/then\n        assertThatExceptionOfType(SRetryableException.class)\n                .isThrownBy(() -> repository.find(Address.class, \"the query as string\",\n                        Collections.<String, Serializable> emptyMap()));\n    }\n\n    @Test\n    void findList_should_throw_retryable_when_persistenceException() {\n        //given\n        TypedQuery typedQuery = mock(TypedQuery.class);\n        doThrow(PersistenceException.class).when(typedQuery).getResultList();\n        doReturn(typedQuery).when(manager).createQuery(anyString(), any(Class.class));\n\n        //when/then\n        assertThatExceptionOfType(SRetryableException.class)\n                .isThrownBy(() -> repository.findList(Address.class, \"the query as string\",\n                        Collections.<String, Serializable> emptyMap(), 0, 10));\n    }\n\n    @Test\n    void persist_should_throw_retryable_exception_in_case_of_persistenceException() {\n        //given\n        doThrow(PersistenceException.class).when(manager).persist(any(Address.class));\n\n        //when/then\n        assertThatExceptionOfType(SRetryableException.class)\n                .isThrownBy(() -> repository.persist(new Address(12)));\n    }\n\n    @Test\n    void persist_should_track_creation_for_new_entity() throws SBonitaException {\n        //given\n        var entity = new EntityPojo(); // persistenceId == null → new entity\n        doAnswer(invocation -> {\n            ((EntityPojo) invocation.getArgument(0)).setPersistenceId(42L);\n            return null;\n        }).when(manager).persist(entity);\n\n        //when\n        repository.persist(entity);\n\n        //then\n        verify(bdmTrackingService).create(42L, EntityPojo.class.getName());\n    }\n\n    @Test\n    void persist_should_upsert_tracking_for_existing_entity() throws SBonitaException {\n        //given\n        var entity = new EntityPojo(99L); // persistenceId != null → existing entity\n\n        //when\n        repository.persist(entity);\n\n        //then\n        verify(bdmTrackingService).upsert(99L, EntityPojo.class.getName());\n        verify(bdmTrackingService, never()).create(anyLong(), anyString());\n    }\n\n    @Test\n    void merge_should_track_creation_for_new_entity() throws SBonitaException {\n        //given\n        var entity = new EntityPojo(); // persistenceId == null → new entity\n        var mergedEntity = new EntityPojo(55L);\n        when(manager.merge(entity)).thenReturn(mergedEntity);\n\n        //when\n        repository.merge(entity);\n\n        //then\n        verify(bdmTrackingService).create(55L, EntityPojo.class.getName());\n    }\n\n    @Test\n    void merge_should_upsert_tracking_for_existing_entity() throws SBonitaException {\n        //given\n        var entity = new EntityPojo(10L); // persistenceId != null → existing entity\n        when(manager.merge(entity)).thenReturn(entity);\n\n        //when\n        repository.merge(entity);\n\n        //then\n        verify(bdmTrackingService).upsert(10L, EntityPojo.class.getName());\n        verify(bdmTrackingService, never()).create(anyLong(), anyString());\n    }\n\n    @Test\n    void persist_null_entity_should_not_track() throws SBonitaException {\n        //when\n        repository.persist(null);\n\n        //then\n        verify(manager, never()).persist(any());\n        verify(bdmTrackingService, never()).create(anyLong(), anyString());\n    }\n\n    @Test\n    void merge_null_entity_should_not_track() throws SBonitaException {\n        //when\n        repository.merge(null);\n\n        //then\n        verify(manager, never()).merge(any());\n        verify(bdmTrackingService, never()).create(anyLong(), anyString());\n    }\n\n    @Test\n    void merge_should_use_original_classname_not_hibernate_proxy() throws SBonitaException {\n        // given — input is a plain EntityPojo, but merge() returns a different type (simulating proxy)\n        var entity = new EntityPojo(); // persistenceId == null → new entity\n        var proxyResult = mock(Entity.class);\n        when(proxyResult.getPersistenceId()).thenReturn(77L);\n        doReturn(proxyResult).when(manager).merge(entity);\n\n        //when\n        repository.merge(entity);\n\n        // then — dataClassname should be EntityPojo (from input), not the mock/proxy class\n        verify(bdmTrackingService).create(77L, EntityPojo.class.getName());\n    }\n\n    @Test\n    void persist_should_throw_SBonitaRuntimeException_when_tracking_fails() throws SBonitaException {\n        //given\n        var entity = new EntityPojo(); // new entity\n        doAnswer(invocation -> {\n            ((EntityPojo) invocation.getArgument(0)).setPersistenceId(7L);\n            return null;\n        }).when(manager).persist(entity);\n        doThrow(new SDataRetentionBdmTrackingException(\"DB error\"))\n                .when(bdmTrackingService).create(anyLong(), anyString());\n\n        //when-then\n        assertThatThrownBy(() -> repository.persist(entity))\n                .isInstanceOf(SBonitaRuntimeException.class)\n                .hasMessageContaining(\"Failed to insert data retention tracking record\")\n                .hasCauseInstanceOf(SDataRetentionBdmTrackingException.class);\n    }\n\n    @Test\n    void merge_should_throw_SBonitaRuntimeException_when_tracking_fails() throws SBonitaException {\n        //given\n        var entity = new EntityPojo(); // new entity\n        var mergedEntity = new EntityPojo(8L);\n        when(manager.merge(entity)).thenReturn(mergedEntity);\n        doThrow(new SDataRetentionBdmTrackingException(\"DB error\"))\n                .when(bdmTrackingService).create(anyLong(), anyString());\n\n        //when-then\n        assertThatThrownBy(() -> repository.merge(entity))\n                .isInstanceOf(SBonitaRuntimeException.class)\n                .hasMessageContaining(\"Failed to insert data retention tracking record\")\n                .hasCauseInstanceOf(SDataRetentionBdmTrackingException.class);\n    }\n\n    @Test\n    void merge_should_throw_retryable_exception_in_case_of_persistenceException() {\n        //given\n        doThrow(PersistenceException.class).when(manager).merge(any(Address.class));\n\n        //when/then\n        assertThatExceptionOfType(SRetryableException.class)\n                .isThrownBy(() -> repository.merge(new Address(12)));\n    }\n\n    @Test\n    void remove_should_throw_retryable_exception_in_case_of_persistenceException() {\n        //given\n        doThrow(PersistenceException.class).when(manager).remove(any(Address.class));\n\n        //when/then\n        assertThatExceptionOfType(SRetryableException.class)\n                .isThrownBy(() -> repository.remove(new Address(12)));\n    }\n\n    @Test\n    void remove_should_delete_tracking_record() throws SBonitaException {\n        //given\n        var entity = new EntityPojo(42L);\n\n        //when\n        repository.remove(entity);\n\n        //then\n        verify(manager).remove(entity);\n        verify(bdmTrackingService).delete(42L, EntityPojo.class.getName());\n    }\n\n    @Test\n    void remove_null_entity_should_not_delete_tracking_record() throws SBonitaException {\n        //when\n        repository.remove(null);\n\n        //then\n        verify(manager, never()).remove(any());\n        verify(bdmTrackingService, never()).delete(anyLong(), anyString());\n    }\n\n    @Test\n    void remove_entity_without_id_should_not_delete_tracking_record() throws SBonitaException {\n        //given\n        var entity = new EntityPojo(); // persistenceId == null\n\n        //when\n        repository.remove(entity);\n\n        //then\n        verify(manager, never()).remove(any());\n        verify(bdmTrackingService, never()).delete(anyLong(), anyString());\n    }\n\n    @Test\n    void remove_should_not_fail_when_tracking_deletion_fails_with_checked_exception() throws SBonitaException {\n        //given\n        var entity = new EntityPojo(42L);\n        doThrow(new SDataRetentionBdmTrackingException(\"DB error\"))\n                .when(bdmTrackingService).delete(anyLong(), anyString());\n\n        //when-then — should not throw\n        assertThatNoException().isThrownBy(() -> repository.remove(entity));\n        // entity was still removed\n        verify(manager).remove(entity);\n    }\n\n    @Test\n    void remove_should_not_fail_when_tracking_deletion_fails_with_runtime_exception() throws SBonitaException {\n        //given\n        var entity = new EntityPojo(42L);\n        doThrow(new RuntimeException(\"unexpected error\"))\n                .when(bdmTrackingService).delete(anyLong(), anyString());\n\n        //when-then — should not throw\n        assertThatNoException().isThrownBy(() -> repository.remove(entity));\n        // entity was still removed\n        verify(manager).remove(entity);\n    }\n\n    @Test\n    void removeById_should_remove_entity_and_delete_tracking_record() throws Exception {\n        //given\n        var entity = new EntityPojo(42L);\n        var entityGraph = mock(EntityGraph.class);\n        doReturn(entityGraph).when(manager).createEntityGraph(EntityPojo.class);\n        var expectedHints = Map.<String, Object> of(\"javax.persistence.fetchgraph\", entityGraph);\n        when(manager.find(EntityPojo.class, 42L, expectedHints)).thenReturn(entity);\n\n        //when\n        var removed = repository.removeById(EntityPojo.class, 42L);\n\n        //then\n        assertThat(removed).isSameAs(entity);\n        verify(manager).find(EntityPojo.class, 42L, expectedHints);\n        verify(manager).remove(entity);\n        verify(bdmTrackingService).delete(42L, EntityPojo.class.getName());\n    }\n\n    @Test\n    void removeById_should_throw_when_entity_not_found() {\n        //given\n        var entityGraph = mock(EntityGraph.class);\n        doReturn(entityGraph).when(manager).createEntityGraph(EntityPojo.class);\n        when(manager.find(eq(EntityPojo.class), eq(999L), anyMap())).thenReturn(null);\n\n        //when-then\n        assertThatExceptionOfType(SBusinessDataNotFoundException.class)\n                .isThrownBy(() -> repository.removeById(EntityPojo.class, 999L));\n        verify(manager, never()).remove(any());\n    }\n\n    @Test\n    void removeById_should_throw_retryable_on_persistence_exception() {\n        //given\n        var entityGraph = mock(EntityGraph.class);\n        doReturn(entityGraph).when(manager).createEntityGraph(EntityPojo.class);\n        when(manager.find(eq(EntityPojo.class), eq(42L), anyMap()))\n                .thenThrow(new PersistenceException(\"db error\"));\n\n        //when-then\n        assertThatExceptionOfType(SRetryableException.class)\n                .isThrownBy(() -> repository.removeById(EntityPojo.class, 42L));\n    }\n\n    @Test\n    void removeById_should_not_fail_when_tracking_deletion_fails() throws Exception {\n        //given\n        var entity = new EntityPojo(42L);\n        var entityGraph = mock(EntityGraph.class);\n        doReturn(entityGraph).when(manager).createEntityGraph(EntityPojo.class);\n        when(manager.find(eq(EntityPojo.class), eq(42L), anyMap())).thenReturn(entity);\n        doThrow(new SDataRetentionBdmTrackingException(\"DB error\"))\n                .when(bdmTrackingService).delete(anyLong(), anyString());\n\n        //when-then — should not throw\n        assertThatNoException().isThrownBy(() -> repository.removeById(EntityPojo.class, 42L));\n        verify(manager).remove(entity);\n    }\n\n    @Test\n    void checkParameterValue_should_transform_query_parameter_values_from_string_array_to_collection() {\n        Object collection = repository\n                .checkParameterValue(new String[] { \"v1\", \"v2\" });\n\n        assertThat((Collection) collection).contains(\"v1\", \"v2\");\n    }\n\n    @Test\n    void checkParameterValue_should_transform_query_parameter_values_from_int_array_to_collection() {\n        Object collection = repository\n                .checkParameterValue(new Integer[] { 1, 2 });\n\n        assertThat((Collection) collection).contains(1, 2);\n    }\n\n    @Test\n    void checkParameterValue_should_transform_query_parameter_values_from_float_array_to_collection() {\n        Object collection = repository\n                .checkParameterValue(new Float[] { 1.2f, 2.0f });\n\n        assertThat((Collection) collection).contains(1.2f, 2.0f);\n    }\n\n    @Test\n    void checkParameterValue_should_transform_query_parameter_values_from_double_array_to_collection() {\n        Object collection = repository\n                .checkParameterValue(new Double[] { 1.2d, 2.0d });\n\n        assertThat((Collection) collection).contains(1.2d, 2.0d);\n    }\n\n    @Test\n    void checkParameterValue_should_transform_query_parameter_values_from_long_array_to_collection() {\n        Object collection = repository\n                .checkParameterValue(new Long[] { 12l, 23456l });\n\n        assertThat((Collection) collection).contains(12l, 23456l);\n    }\n\n    @Test\n    void checkParameterValue_should_check_supported_query_parameter_types() {\n        assertThatExceptionOfType(IllegalArgumentException.class)\n                .isThrownBy(() -> repository.checkParameterValue(new Byte[] { 0x1, 0x2 }));\n    }\n\n    // --- Defensive getEntityManager() tests (BPA-321) ---\n\n    /**\n     * Helper to inject an EntityManager into the private ThreadLocal \"managers\" field\n     * and call the real getEntityManager() method (not the spied version).\n     */\n    private JPABusinessDataRepositoryImpl createRepositoryWithStaleEM(EntityManager staleEM,\n            EntityManager freshEM, EntityManagerFactory emf)\n            throws Exception {\n        JPABusinessDataRepositoryImpl repo = spy(\n                new JPABusinessDataRepositoryImpl(transactionService,\n                        businessDataModelRepository, configuration, classLoaderService, bdmTrackingService));\n        doReturn(emf).when(repo).getEntityManagerFactory();\n\n        // Inject the stale EM into the private ThreadLocal\n        Field managersField = JPABusinessDataRepositoryImpl.class.getDeclaredField(\"managers\");\n        managersField.setAccessible(true);\n        @SuppressWarnings(\"unchecked\")\n        ThreadLocal<EntityManager> managers = (ThreadLocal<EntityManager>) managersField.get(repo);\n        managers.set(staleEM);\n\n        // The EMF will produce a fresh EM when asked\n        doReturn(freshEM).when(emf).createEntityManager();\n\n        return repo;\n    }\n\n    @Test\n    void getEntityManager_should_discard_stale_EM_not_joined_to_transaction() throws Exception {\n        //given\n        EntityManager staleEM = mock(EntityManager.class);\n        doReturn(true).when(staleEM).isOpen();\n        doReturn(false).when(staleEM).isJoinedToTransaction();\n\n        EntityManager freshEM = mock(EntityManager.class);\n        EntityManagerFactory emf = mock(EntityManagerFactory.class);\n\n        JPABusinessDataRepositoryImpl repo = createRepositoryWithStaleEM(staleEM, freshEM, emf);\n\n        //when\n        EntityManager result = repo.getEntityManager();\n\n        //then\n        verify(staleEM).close();\n        verify(emf).createEntityManager();\n        verify(freshEM).joinTransaction();\n        assertThat(result).isSameAs(freshEM);\n    }\n\n    @Test\n    void getEntityManager_should_discard_stale_EM_that_is_no_longer_open() throws Exception {\n        //given\n        EntityManager staleEM = mock(EntityManager.class);\n        doReturn(false).when(staleEM).isOpen();\n\n        EntityManager freshEM = mock(EntityManager.class);\n        EntityManagerFactory emf = mock(EntityManagerFactory.class);\n\n        JPABusinessDataRepositoryImpl repo = createRepositoryWithStaleEM(staleEM, freshEM, emf);\n\n        //when\n        EntityManager result = repo.getEntityManager();\n\n        //then\n        // staleEM.isOpen() returned false, so close() should NOT be called (already closed)\n        verify(staleEM, never()).close();\n        verify(emf).createEntityManager();\n        assertThat(result).isSameAs(freshEM);\n    }\n\n    @Test\n    void getEntityManager_should_discard_stale_EM_that_throws_on_state_check() throws Exception {\n        //given\n        EntityManager staleEM = mock(EntityManager.class);\n        // Simulates a broken EM where even isOpen() throws (completely broken session)\n        doThrow(new PersistenceException(\"Session/EntityManager is closed\")).when(staleEM).isOpen();\n\n        EntityManager freshEM = mock(EntityManager.class);\n        EntityManagerFactory emf = mock(EntityManagerFactory.class);\n\n        JPABusinessDataRepositoryImpl repo = createRepositoryWithStaleEM(staleEM, freshEM, emf);\n\n        //when\n        EntityManager result = repo.getEntityManager();\n\n        //then\n        // isOpen() threw, so closeQuietly catches the exception and moves on\n        verify(staleEM, never()).close();\n        verify(emf).createEntityManager();\n        assertThat(result).isSameAs(freshEM);\n    }\n\n    @Test\n    void getEntityManager_should_reuse_EM_when_joined_to_current_transaction() throws Exception {\n        //given\n        EntityManager activeEM = mock(EntityManager.class);\n        doReturn(true).when(activeEM).isOpen();\n        doReturn(true).when(activeEM).isJoinedToTransaction();\n\n        EntityManagerFactory emf = mock(EntityManagerFactory.class);\n\n        JPABusinessDataRepositoryImpl repo = spy(\n                new JPABusinessDataRepositoryImpl(transactionService,\n                        businessDataModelRepository, configuration, classLoaderService, bdmTrackingService));\n        doReturn(emf).when(repo).getEntityManagerFactory();\n\n        // Inject the active EM\n        Field managersField = JPABusinessDataRepositoryImpl.class.getDeclaredField(\"managers\");\n        managersField.setAccessible(true);\n        @SuppressWarnings(\"unchecked\")\n        ThreadLocal<EntityManager> managers = (ThreadLocal<EntityManager>) managersField.get(repo);\n        managers.set(activeEM);\n\n        //when\n        EntityManager result = repo.getEntityManager();\n\n        //then\n        verify(emf, never()).createEntityManager();\n        verify(activeEM, never()).close();\n        verify(activeEM).joinTransaction();\n        assertThat(result).isSameAs(activeEM);\n\n        // cleanup ThreadLocal\n        managers.remove();\n    }\n\n    @Test\n    void getEntityManager_should_close_new_EM_when_registerBonitaSynchronization_throws() throws Exception {\n        //given\n        EntityManager freshEM = mock(EntityManager.class);\n        doReturn(true).when(freshEM).isOpen();\n        EntityManagerFactory emf = mock(EntityManagerFactory.class);\n        doReturn(freshEM).when(emf).createEntityManager();\n\n        JPABusinessDataRepositoryImpl repo = spy(\n                new JPABusinessDataRepositoryImpl(transactionService,\n                        businessDataModelRepository, configuration, classLoaderService, bdmTrackingService));\n        doReturn(emf).when(repo).getEntityManagerFactory();\n\n        doThrow(new STransactionNotFoundException(\"no active transaction\"))\n                .when(transactionService).registerBonitaSynchronization(any());\n\n        //when\n        assertThatExceptionOfType(IllegalStateException.class)\n                .isThrownBy(repo::getEntityManager)\n                .withCauseInstanceOf(STransactionNotFoundException.class);\n\n        //then\n        verify(freshEM).close();\n    }\n\n    @Test\n    void getEntityManager_should_cleanup_and_rethrow_when_joinTransaction_fails() throws Exception {\n        //given\n        EntityManager freshEM = mock(EntityManager.class);\n        doReturn(true).when(freshEM).isOpen();\n        doThrow(new javax.persistence.TransactionRequiredException(\"TX is rollback-only\"))\n                .when(freshEM).joinTransaction();\n        EntityManagerFactory emf = mock(EntityManagerFactory.class);\n        doReturn(freshEM).when(emf).createEntityManager();\n\n        JPABusinessDataRepositoryImpl repo = spy(\n                new JPABusinessDataRepositoryImpl(transactionService,\n                        businessDataModelRepository, configuration, classLoaderService, bdmTrackingService));\n        doReturn(emf).when(repo).getEntityManagerFactory();\n\n        //when + then\n        assertThatExceptionOfType(javax.persistence.TransactionRequiredException.class)\n                .isThrownBy(repo::getEntityManager)\n                .withMessageContaining(\"TX is rollback-only\");\n\n        verify(freshEM).close();\n\n        // Verify ThreadLocal was cleaned up: a second call should create a fresh EM,\n        // not reuse the poisoned one\n        EntityManager secondEM = mock(EntityManager.class);\n        doReturn(secondEM).when(emf).createEntityManager();\n        doNothing().when(secondEM).joinTransaction();\n\n        EntityManager result = repo.getEntityManager();\n\n        assertThat(result).isSameAs(secondEM);\n        verify(emf, times(2)).createEntityManager();\n    }\n\n    class Address implements Entity {\n\n        private static final long serialVersionUID = 2603989953326533907L;\n\n        private final long persistenceId;\n\n        public Address(final long persistenceId) {\n            this.persistenceId = persistenceId;\n        }\n\n        @Override\n        public Long getPersistenceId() {\n            return persistenceId;\n        }\n\n        @Override\n        public Long getPersistenceVersion() {\n            return 0L;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/JsonBusinessDataSerializerImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;\nimport static org.mockito.Mockito.mock;\n\nimport java.io.IOException;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.OffsetDateTime;\nimport java.time.ZoneOffset;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport java.util.stream.IntStream;\nimport javassist.util.proxy.MethodHandler;\n\nimport com.company.model.Address;\nimport com.company.model.Person;\nimport com.company.model.PersonWithDetails;\nimport com.company.model.Phone;\nimport org.apache.commons.io.IOUtils;\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.junit.Test;\n\npublic class JsonBusinessDataSerializerImplTest {\n\n    private static final String PARAMETER_BUSINESSDATA_CLASS_URI_VALUE = \"/businessdata/{className}/{id}/{field}\";\n\n    private final JsonBusinessDataSerializerImpl jsonBusinessDataSerializer = new JsonBusinessDataSerializerImpl();\n\n    @Test\n    public void entity_should_be_serialized() throws Exception {\n        // given\n        Entity person = initPerson(1L);\n\n        // when\n        final String jsonPerson = jsonBusinessDataSerializer.serializeEntity(person,\n                PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n        // then\n        assertThatJson(jsonPerson).as(\"entity serialization\").isEqualTo(getJsonContent(\"singlePerson.json\"));\n    }\n\n    @Test\n    public void count_result_should_be_serialized() throws Exception {\n\n        // when\n        final String countJson = jsonBusinessDataSerializer.serializeCountResult(Collections.singletonList(59L),\n                Person.class.getName());\n\n        // then\n        assertThatJson(countJson).isEqualTo(\"[59]\");\n    }\n\n    @Test\n    public void entity_with_nested_entity_fields_should_be_serialized() throws Exception {\n        // given\n        PersonWithDetails person = initPersonWithDetails(666L);\n\n        // when\n        final String jsonPerson = jsonBusinessDataSerializer.serializeEntity(person,\n                PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n        // then\n        assertThatJson(jsonPerson).as(\"entity with nested entity fields serialization\")\n                .isEqualTo(getJsonContent(\"personWithDetails.json\"));\n    }\n\n    @Test\n    public void entity_list_should_be_serialized() throws Exception {\n        // given\n        List<Entity> persons = IntStream.range(1, 3).mapToObj(i -> initPerson(i)).collect(Collectors.toList());\n\n        // when\n        final String jsonPersonList = jsonBusinessDataSerializer.serializeEntities(persons,\n                PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n        // then\n        assertThatJson(jsonPersonList).as(\"entity list serialization\").isEqualTo(getJsonContent(\"multiplePerson.json\"));\n    }\n\n    @Test\n    public void serialization_of_entity_should_use_fields_and_not_getters() throws Exception {\n        //given\n        EntitySerializerPojo value = createPojo();\n\n        //when\n        final String serializedEntity = jsonBusinessDataSerializer.serializeEntity(value,\n                PARAMETER_BUSINESSDATA_CLASS_URI_VALUE);\n\n        //then\n        assertThatJson(serializedEntity).as(\"serialization uses fields instead of getters to compute json properties\")\n                .isEqualTo(getJsonContent(\"EntitySerializerPojo.json\"));\n    }\n\n    @Test\n    public void scalar_result_should_be_serialized_in_legacy_format() throws Exception {\n        // when - Long\n        String longJson = jsonBusinessDataSerializer.serializeScalarResult(Collections.singletonList(59L),\n                Person.class.getName(), false);\n        // then\n        assertThatJson(longJson).isEqualTo(\"[59]\");\n\n        // when - Double\n        String doubleJson = jsonBusinessDataSerializer.serializeScalarResult(Collections.singletonList(42.5),\n                Person.class.getName(), false);\n        // then\n        assertThatJson(doubleJson).isEqualTo(\"[42.5]\");\n\n        // when - Integer\n        String intJson = jsonBusinessDataSerializer.serializeScalarResult(Collections.singletonList(100),\n                Person.class.getName(), false);\n        // then\n        assertThatJson(intJson).isEqualTo(\"[100]\");\n    }\n\n    @Test\n    public void scalar_result_should_be_serialized_in_standard_format() throws Exception {\n        // when - Long\n        String longJson = jsonBusinessDataSerializer.serializeScalarResult(Collections.singletonList(59L),\n                Person.class.getName(), true);\n        // then\n        assertThatJson(longJson).isEqualTo(\"{ \\\"value\\\": 59 }\");\n\n        // when - Double\n        String doubleJson = jsonBusinessDataSerializer.serializeScalarResult(Collections.singletonList(42.5),\n                Person.class.getName(), true);\n        // then\n        assertThatJson(doubleJson).isEqualTo(\"{ \\\"value\\\": 42.5 }\");\n\n        // when - null\n        String nullJson = jsonBusinessDataSerializer.serializeScalarResult(Collections.singletonList(null),\n                Person.class.getName(), true);\n        // then\n        assertThatJson(nullJson).isEqualTo(\"{ \\\"value\\\": null }\");\n    }\n\n    @Test\n    public void entity_query_result_should_be_serialized_as_array_in_legacy_mode() throws Exception {\n        // given - single entity\n        Entity person = initPerson(1L);\n\n        // when - legacy mode always returns array regardless of queryReturnsMultipleResults\n        final String jsonSingle = jsonBusinessDataSerializer.serializeEntityQueryResult(\n                Collections.singletonList(person), PARAMETER_BUSINESSDATA_CLASS_URI_VALUE, false, false);\n\n        // then - returns array even for single entity\n        assertThatJson(jsonSingle).isArray();\n        assertThatJson(jsonSingle).node(\"[0]\").isObject();\n    }\n\n    @Test\n    public void entity_query_result_should_be_serialized_as_object_in_standard_mode_for_single_entity_query()\n            throws Exception {\n        // given - single entity from a query designed to return single entity\n        Entity person = initPerson(1L);\n\n        // when - standard mode with query returning single entity (queryReturnsMultipleResults = false)\n        final String jsonSingle = jsonBusinessDataSerializer.serializeEntityQueryResult(\n                Collections.singletonList(person), PARAMETER_BUSINESSDATA_CLASS_URI_VALUE, true, false);\n\n        // then - returns object (not array) for single entity query\n        assertThatJson(jsonSingle).isObject();\n        assertThatJson(jsonSingle).node(\"persistenceId\").isEqualTo(1);\n    }\n\n    @Test\n    public void entity_query_result_should_be_serialized_as_array_in_standard_mode_for_list_query_with_single_result()\n            throws Exception {\n        // given - single entity from a query designed to return List\n        Entity person = initPerson(1L);\n\n        // when - standard mode with query returning List (queryReturnsMultipleResults = true)\n        final String jsonSingle = jsonBusinessDataSerializer.serializeEntityQueryResult(\n                Collections.singletonList(person), PARAMETER_BUSINESSDATA_CLASS_URI_VALUE, true, true);\n\n        // then - returns array even for single result because query is designed to return List\n        assertThatJson(jsonSingle).isArray();\n        assertThatJson(jsonSingle).node(\"[0]\").isObject();\n        assertThatJson(jsonSingle).node(\"[0].persistenceId\").isEqualTo(1);\n    }\n\n    @Test\n    public void entity_query_result_should_be_serialized_as_array_in_standard_mode_for_multiple_entities()\n            throws Exception {\n        // given - multiple entities\n        List<Entity> persons = IntStream.range(1, 3).mapToObj(i -> initPerson(i)).collect(Collectors.toList());\n\n        // when - standard mode with query returning List (queryReturnsMultipleResults = true)\n        final String jsonMultiple = jsonBusinessDataSerializer.serializeEntityQueryResult(persons,\n                PARAMETER_BUSINESSDATA_CLASS_URI_VALUE, true, true);\n\n        // then - returns array for multiple entities\n        assertThatJson(jsonMultiple).isArray();\n        assertThatJson(jsonMultiple).node(\"[0].persistenceId\").isEqualTo(1);\n        assertThatJson(jsonMultiple).node(\"[1].persistenceId\").isEqualTo(2);\n    }\n\n    @Test\n    public void entity_query_result_should_return_empty_object_for_no_results_in_standard_mode_single_entity_query()\n            throws Exception {\n        // when - standard mode with query returning single entity (queryReturnsMultipleResults = false)\n        final String jsonEmpty = jsonBusinessDataSerializer.serializeEntityQueryResult(Collections.emptyList(),\n                PARAMETER_BUSINESSDATA_CLASS_URI_VALUE, true, false);\n\n        // then - returns empty object for single entity query\n        assertThatJson(jsonEmpty).isEqualTo(\"{}\");\n    }\n\n    @Test\n    public void entity_query_result_should_return_empty_array_for_no_results_in_standard_mode_list_query()\n            throws Exception {\n        // when - standard mode with query returning List (queryReturnsMultipleResults = true)\n        final String jsonEmpty = jsonBusinessDataSerializer.serializeEntityQueryResult(Collections.emptyList(),\n                PARAMETER_BUSINESSDATA_CLASS_URI_VALUE, true, true);\n\n        // then - returns empty array for list query\n        assertThatJson(jsonEmpty).isEqualTo(\"[]\");\n    }\n\n    // =================================================================================================================\n    // UTILS\n    // =================================================================================================================\n\n    private static EntitySerializerPojo createPojo() {\n        final EntitySerializerPojo entitySerializerPojo = new EntitySerializerPojo();\n\n        entitySerializerPojo.setPersistenceId(Long.MAX_VALUE);\n        entitySerializerPojo.setPersistenceVersion(1L);\n        entitySerializerPojo.setABoolean(Boolean.TRUE);\n        entitySerializerPojo.setADate(new Date(123L));\n        entitySerializerPojo.setALocalDate(LocalDate.of(2017, 3, 6));\n        entitySerializerPojo.setALocalDateTime(LocalDateTime.of(1945, 5, 8, 12, 31, 17));\n        entitySerializerPojo\n                .setAnOffsetDateTime(OffsetDateTime.of(LocalDateTime.of(1949, 4, 9, 12, 14, 5), ZoneOffset.ofHours(1)));\n        entitySerializerPojo.setADouble(Double.MAX_VALUE);\n        entitySerializerPojo.setAFloat(Float.MAX_VALUE);\n        entitySerializerPojo.setAInteger(Integer.MAX_VALUE);\n        entitySerializerPojo.setALong(Long.MAX_VALUE);\n        entitySerializerPojo.setAString(\"string\");\n        entitySerializerPojo.setAText(\"text\");\n        entitySerializerPojo.addToManyLong(1L);\n        entitySerializerPojo.addToManyLong(2L);\n        entitySerializerPojo.addToManyString(\"abc\");\n        entitySerializerPojo.addToManyString(\"def\");\n\n        return entitySerializerPojo;\n    }\n\n    private static Person initPerson(long persistenceId) {\n        Person person;\n        person = new Person();\n        person.setPersistenceId(persistenceId);\n        person.setPersistenceVersion(2L);\n        person.setName(\"John\");\n        person.setAge(50);\n        person.setBirthday(new Date(123456789L));\n        person.setHasMobile(Boolean.TRUE);\n\n        for (int i = 0; i < 3; i++) {\n            Phone ph = new Phone();\n            ph.setNumber(\"123456\" + i);\n            person.addToPhones(ph);\n        }\n\n        person.addToBools(true);\n        person.addToBools(false);\n        person.addToBools(true);\n        person.addToBools(false);\n\n        return person;\n    }\n\n    private String getJsonContent(String jsonFileName) throws IOException {\n        return new String(IOUtils.toByteArray(this.getClass().getResourceAsStream(jsonFileName)));\n    }\n\n    private static PersonWithDetails initPersonWithDetails(long persistenceId) {\n        PersonWithDetails person = new PersonWithDetails();\n        person.setPersistenceId(persistenceId);\n        person.setPersistenceVersion(2L);\n        person.setName(\"John\");\n        person.setAge(50);\n        person.setBirthday(new Date(123456789L));\n        person.setHasMobile(Boolean.TRUE);\n\n        for (int i = 0; i < 3; i++) {\n            Phone ph = new Phone();\n            ph.setPersistenceVersion(365L + i);\n            ph.setPersistenceId(22365L + i);\n            ph.setNumber(\"123456\" + i);\n            person.addToPhones(ph);\n        }\n\n        person.addToBools(true);\n        person.addToBools(false);\n        person.addToBools(true);\n        person.addToBools(false);\n\n        person.addToIgnores(1L);\n        person.addToIgnores(3L);\n        person.addToIgnores(5L);\n        person.addToIgnores(6L);\n\n        Phone secretPhone = new Phone();\n        secretPhone.setPersistenceId(3615L);\n        secretPhone.setNumber(\"999999\");\n        person.setSecretPhone(secretPhone);\n\n        person.addToIncludes(1235L);\n        person.addToIncludes(6666L);\n        person.addToIncludes(7777L);\n\n        Address address1 = new Address();\n        address1.setPersistenceId(2598L);\n        address1.setPersistenceVersion(99992598L);\n        address1.setStreet(\"Rue Gustave Eiffel\");\n        address1.setNumber(32f);\n        address1.setFloors(Arrays.asList(1d, 4d));\n        address1.setDoorCode(\"my-secret-password\");\n        person.setAddress1(address1);\n\n        Address address2 = new Address();\n        address2.setPersistenceId(358L);\n        address2.setPersistenceVersion(118L);\n        address2.setDoorCode(null);\n        person.setAddress2(address2);\n\n        person.setMethodHandlerObject(mock(MethodHandler.class));\n        person.addToProxysAsMethodHandler(\"I love Maths\");\n        person.addToProxysAsProxyImpl(365L);\n        person.addToProxysAsProxyObjectImpl(secretPhone);\n\n        return person;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/PersonWithProxyAddress.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport javax.persistence.CascadeType;\nimport javax.persistence.FetchType;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.Id;\nimport javax.persistence.OneToOne;\nimport javax.persistence.Version;\n\nimport org.bonitasoft.engine.bdm.Entity;\n\n/**\n * A parent entity with a @OneToOne child relationship to Address.\n * <p>\n * This entity is used to reproduce the bug where a HibernateProxy child entity\n * (address field) is serialized as an empty object {} instead of including its fields.\n * <p>\n * The address field can hold either a real Address or an AddressHibernateProxy.\n */\npublic class PersonWithProxyAddress implements Entity {\n\n    @Id\n    @GeneratedValue\n    private Long persistenceId;\n\n    @Version\n    private Long persistenceVersion;\n\n    private String name;\n\n    @OneToOne(optional = true, fetch = FetchType.EAGER, cascade = CascadeType.ALL)\n    private Entity address;\n\n    public PersonWithProxyAddress() {\n    }\n\n    public PersonWithProxyAddress(Long persistenceId, String name) {\n        this.persistenceId = persistenceId;\n        this.name = name;\n    }\n\n    @Override\n    public Long getPersistenceId() {\n        return persistenceId;\n    }\n\n    public void setPersistenceId(Long persistenceId) {\n        this.persistenceId = persistenceId;\n    }\n\n    @Override\n    public Long getPersistenceVersion() {\n        return persistenceVersion;\n    }\n\n    public void setPersistenceVersion(Long persistenceVersion) {\n        this.persistenceVersion = persistenceVersion;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public Entity getAddress() {\n        return address;\n    }\n\n    public void setAddress(Entity address) {\n        this.address = address;\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/ProxyCacheManagerTest.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.WeakHashMap;\nimport javassist.util.proxy.ProxyFactory;\n\nimport com.company.pojo.Employee;\nimport org.bonitasoft.engine.bdm.Entity;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ProxyCacheManagerTest {\n\n    @Test\n    public void should_retrieve_the_proxyCache_map() throws Exception {\n        final ProxyCacheManager proxyCacheManager = new ProxyCacheManager();\n\n        createProxy(new EntityPojo());\n        createProxy(new EntityPojo());\n        createProxy(new Employee(\"John\", \"Doe\"));\n        createProxy(new Employee(\"Jane\", \"Doe\"));\n\n        WeakHashMap cache = proxyCacheManager.get();\n        assertThat(cache).hasSize(1);\n\n        proxyCacheManager.clearCache();\n        cache = proxyCacheManager.get();\n        assertThat(cache).isEmpty();\n    }\n\n    private Entity createProxy(Entity entity) {\n        final ProxyFactory factory = new ProxyFactory();\n        factory.setSuperclass(entity.getClass());\n        try {\n            return (Entity) factory.create(new Class<?>[0], new Object[0], null);\n        } catch (final Exception e) {\n            throw new RuntimeException(\"Error when proxifying object\", e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/RemoveEntityManagerSynchronizationTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport static javax.transaction.Status.STATUS_COMMITTED;\nimport static javax.transaction.Status.STATUS_ROLLEDBACK;\nimport static javax.transaction.Status.STATUS_UNKNOWN;\nimport static org.assertj.core.api.Assertions.*;\nimport static org.mockito.Mockito.*;\n\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicReference;\n\nimport javax.persistence.EntityManager;\nimport javax.persistence.PersistenceException;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\n/**\n * @author Laurent Leseigneur\n */\n@ExtendWith(MockitoExtension.class)\nclass RemoveEntityManagerSynchronizationTest {\n\n    @Mock\n    EntityManager entityManager;\n\n    @Test\n    void beforeCompletion_should_not_close_entityManager() {\n        //given\n        ThreadLocal<EntityManager> localManager = new ThreadLocal<>();\n        localManager.set(entityManager);\n        RemoveEntityManagerSynchronization removeEntityManagerSynchronization = new RemoveEntityManagerSynchronization(\n                localManager);\n\n        //when\n        removeEntityManagerSynchronization.beforeCompletion();\n\n        //then\n        verify(entityManager, never()).close();\n        assertThat(localManager.get()).as(\"should remove entity manager\").isNotNull();\n    }\n\n    @Test\n    void afterCompletion_should_close_entityManager_on_STATUS_COMMITTED() {\n        //given\n        ThreadLocal<EntityManager> localManager = new ThreadLocal<>();\n        localManager.set(entityManager);\n        doReturn(true).when(entityManager).isOpen();\n        RemoveEntityManagerSynchronization sync = new RemoveEntityManagerSynchronization(localManager);\n\n        //when\n        sync.afterCompletion(STATUS_COMMITTED);\n\n        //then\n        verify(entityManager).close();\n        assertThat(localManager.get()).as(\"should remove entity manager after commit\").isNull();\n    }\n\n    @Test\n    void afterCompletion_should_close_entityManager_on_STATUS_ROLLEDBACK() {\n        //given\n        ThreadLocal<EntityManager> localManager = new ThreadLocal<>();\n        localManager.set(entityManager);\n        doReturn(true).when(entityManager).isOpen();\n        RemoveEntityManagerSynchronization sync = new RemoveEntityManagerSynchronization(localManager);\n\n        //when\n        sync.afterCompletion(STATUS_ROLLEDBACK);\n\n        //then\n        verify(entityManager).close();\n        assertThat(localManager.get()).as(\"should remove entity manager after rollback\").isNull();\n    }\n\n    @Test\n    void afterCompletion_should_close_entityManager_and_cleanup_threadLocal_on_STATUS_UNKNOWN() {\n        //given\n        ThreadLocal<EntityManager> localManager = new ThreadLocal<>();\n        localManager.set(entityManager);\n        doReturn(true).when(entityManager).isOpen();\n        RemoveEntityManagerSynchronization sync = new RemoveEntityManagerSynchronization(localManager);\n\n        //when\n        sync.afterCompletion(STATUS_UNKNOWN);\n\n        //then\n        verify(entityManager).close();\n        assertThat(localManager.get()).as(\"should remove entity manager after heuristic outcome\").isNull();\n    }\n\n    @Test\n    void afterCompletion_should_not_close_already_closed_entityManager() {\n        //given\n        ThreadLocal<EntityManager> localManager = new ThreadLocal<>();\n        localManager.set(entityManager);\n        doReturn(false).when(entityManager).isOpen();\n        RemoveEntityManagerSynchronization sync = new RemoveEntityManagerSynchronization(localManager);\n\n        //when\n        sync.afterCompletion(STATUS_COMMITTED);\n\n        //then — EM is already closed, so close() should not be called again\n        verify(entityManager, never()).close();\n        assertThat(localManager.get()).as(\"should remove entity manager even if already closed\").isNull();\n    }\n\n    @Test\n    void afterCompletion_should_not_throw_NPE_when_threadLocal_is_null() {\n        //given\n        ThreadLocal<EntityManager> localManager = new ThreadLocal<>();\n        localManager.set(null);\n        RemoveEntityManagerSynchronization sync = new RemoveEntityManagerSynchronization(localManager);\n\n        //when\n        sync.afterCompletion(STATUS_COMMITTED);\n\n        //then\n        assertThat(localManager.get()).as(\"should remove entity manager\").isNull();\n    }\n\n    @Test\n    void afterCompletion_should_still_remove_threadLocal_when_close_throws() {\n        //given\n        ThreadLocal<EntityManager> localManager = new ThreadLocal<>();\n        localManager.set(entityManager);\n        doReturn(true).when(entityManager).isOpen();\n        doThrow(new PersistenceException(\"connection reset\")).when(entityManager).close();\n        RemoveEntityManagerSynchronization sync = new RemoveEntityManagerSynchronization(localManager);\n\n        //when - then: close() failure should be ignored, and ThreadLocal is still cleaned up (via finally)\n        assertThatNoException().isThrownBy(() -> sync.afterCompletion(STATUS_COMMITTED));\n        verify(entityManager).close();\n        assertThat(localManager.get())\n                .as(\"ThreadLocal should be cleaned up even when close() throws\")\n                .isNull();\n    }\n\n    /**\n     * Simulates what happens when Narayana's TransactionReaper calls afterCompletion()\n     * on its own reaper thread instead of the application (worker) thread.\n     * Since RemoveEntityManagerSynchronization uses ThreadLocal.get(), the reaper thread\n     * sees null and the EM is never closed — the worker thread's ThreadLocal retains\n     * the stale EntityManager.\n     * This test documents the current known limitation (BPA-321).\n     */\n    @Test\n    void afterCompletion_should_not_close_entityManager_when_called_from_different_thread() throws Exception {\n        //given\n        ThreadLocal<EntityManager> localManager = new ThreadLocal<>();\n        // Set the EM on the current (application) thread\n        localManager.set(entityManager);\n        RemoveEntityManagerSynchronization sync = new RemoveEntityManagerSynchronization(localManager);\n\n        // Simulate: afterCompletion is called on a different thread (the reaper)\n        CountDownLatch reaperDone = new CountDownLatch(1);\n        AtomicReference<EntityManager> reaperThreadLocalValue = new AtomicReference<>();\n        Thread reaperThread = new Thread(() -> {\n            // On the reaper thread, localManager.get() returns null (different thread)\n            reaperThreadLocalValue.set(localManager.get());\n            sync.afterCompletion(STATUS_ROLLEDBACK);\n            reaperDone.countDown();\n        }, \"Simulated-TransactionReaper\");\n\n        //when\n        reaperThread.start();\n        reaperDone.await(5, TimeUnit.SECONDS);\n\n        //then\n        // The reaper thread's ThreadLocal was null — it could not find the EM\n        assertThat(reaperThreadLocalValue.get()).as(\"reaper thread should not see the worker's EM\").isNull();\n        // The EM was never closed because the reaper thread couldn't access it\n        verify(entityManager, never()).close();\n        // The worker thread's ThreadLocal still holds the stale EM (the leak)\n        assertThat(localManager.get())\n                .as(\"worker thread's ThreadLocal should still hold the stale EM (known limitation)\")\n                .isSameAs(entityManager);\n\n        // Cleanup: remove from current thread to avoid test pollution\n        localManager.remove();\n    }\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/SchemaManagerUpdateIT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl;\n\nimport static org.junit.Assert.fail;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.BOMBuilder;\nimport org.bonitasoft.engine.bdm.model.BusinessObjectModel;\nimport org.bonitasoft.engine.business.data.JpaTestConfiguration;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@ContextConfiguration(locations = { \"/testContext.xml\" })\npublic class SchemaManagerUpdateIT {\n\n    @Autowired\n    @Qualifier(\"jpa-test-configuration\")\n    private JpaTestConfiguration configuration;\n\n    private SchemaManagerUpdate schemaManager;\n\n    @Before\n    public void setUp() {\n        schemaManager = new SchemaManagerUpdate(configuration.getJpaModelConfiguration());\n    }\n\n    @Test\n    public void executeUpdateAndDropScriptsShouldWorkWithAllSupportedTypes() {\n        final BusinessObjectModel bom = BOMBuilder.aBOM().buildModelWithAllSupportedTypes();\n        updateAndDropSchema(bom);\n    }\n\n    @Test\n    public void executeUpdateAndDropScriptsShouldSupportConstraints() {\n        final BusinessObjectModel bom = BOMBuilder.aBOM().buildModelWithConstrainedFields();\n        updateAndDropSchema(bom);\n    }\n\n    private void updateAndDropSchema(final BusinessObjectModel bom) {\n        final List<Exception> updateExceptions = schemaManager.update(bom.getBusinessObjectsClassNames());\n        if (!updateExceptions.isEmpty()) {\n            fail(\"Updating schema fails due to: \" + updateExceptions);\n        }\n        final List<Exception> dropExceptions = schemaManager.drop(bom.getBusinessObjectsClassNames());\n        if (!dropExceptions.isEmpty()) {\n            fail(\"Updating schema fails due to: \" + dropExceptions);\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/jackson/serializer/ExtraPropertyStringListSerializerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.business.data.impl.jackson.serializer;\n\nimport static java.util.Arrays.asList;\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.List;\n\nimport org.junit.Test;\n\npublic class ExtraPropertyStringListSerializerTest {\n\n    private ExtraPropertyStringListSerializer serializer = new ExtraPropertyStringListSerializer();\n\n    @Test\n    public void should_convert_null_list_to_empty() throws Exception {\n        assertThat((List<String>) serializer.convert(null)).isEmpty();\n    }\n\n    @Test\n    public void should_convert_list_to_string() throws Exception {\n        assertThat((List<String>) serializer.convert(asList(1L, 2L))).containsExactly(\"1\", \"2\");\n    }\n\n    @Test\n    public void should_convert_list_with_null_elements() throws Exception {\n        assertThat((List<String>) serializer.convert(asList(1L, null, 2L))).containsExactly(\"1\", null, \"2\");\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/resources/META-INF/persistence.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<persistence xmlns=\"http://java.sun.com/xml/ns/persistence\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n             xsi:schemaLocation=\"http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd\" version=\"2.0\">\n\n    <persistence-unit name=\"BDR\" transaction-type=\"JTA\">\n        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>\n        <class>com.company.pojo.Employee</class>\n        <class>com.company.pojo.Person</class>\n        <properties>\n            <property name=\"hibernate.show_sql\" value=\"false\" />\n            <property name=\"hibernate.format_sql\" value=\"true\" />\n            <property name=\"hibernate.transaction.factory_class\" value=\"org.hibernate.engine.transaction.internal.jta.CMTTransactionFactory\" />\n            <property name=\"hibernate.transaction.jta.platform\" value=\"org.bonitasoft.engine.persistence.Narayana5HibernateJtaPlatform\" />\n        </properties>\n    </persistence-unit>\n\n</persistence>\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/resources/datasource/datasource-dependency-h2.xml",
    "content": "<beans xmlns=\"http://www.springframework.org/schema/beans\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://www.springframework.org/schema/beans\n\t   http://www.springframework.org/schema/beans/spring-beans-4.3.xsd\">\n\n\t<bean class=\"org.springframework.beans.factory.config.PropertyPlaceholderConfigurer\">\n\t\t<property name=\"properties\">\n\t\t\t<props>\n\t\t\t\t<prop key=\"db.hibernate.dialect\">org.hibernate.dialect.H2Dialect</prop>\n\t\t\t\t<prop key=\"db.datasource.classname\">org.h2.jdbcx.JdbcDataSource</prop>\n\t\t\t\t<prop key=\"db.url\">jdbc:h2:mem:datamanagement;DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTOCOMMIT=OFF;</prop>\n\t\t\t\t<prop key=\"db.user\">sa</prop>\n\t\t\t\t<prop key=\"db.password\" />\n\t\t\t</props>\n\t\t</property>\n\t</bean>\n\t\n\t<bean id=\"driverProperties\" class=\"org.springframework.beans.factory.config.PropertiesFactoryBean\">\n\t\t<property name=\"properties\">\n\t\t\t<props>\n\t\t\t\t<prop key=\"user\">${db.user}</prop>\n\t\t\t\t<prop key=\"password\">${db.password}</prop>\n\t\t\t\t<prop key=\"URL\">${db.url}</prop>\n\t\t\t</props>\n\t\t</property>\n\t</bean>\n\n</beans>\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/resources/datasource/datasource-dependency-postgres.xml",
    "content": "<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans\n       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd\">\n\n    <bean class=\"org.springframework.beans.factory.config.PropertyPlaceholderConfigurer\">\n        <property name=\"systemPropertiesModeName\" value=\"SYSTEM_PROPERTIES_MODE_OVERRIDE\" />\n        <property name=\"properties\">\n            <props>\n                <prop key=\"db.hibernate.dialect\">org.hibernate.dialect.PostgreSQL10Dialect</prop>\n                <prop key=\"db.datasource.classname\">org.postgresql.xa.PGXADataSource</prop>\n                <prop key=\"db.server.name\">localhost</prop>\n                <prop key=\"db.server.port\">5432</prop>\n                <prop key=\"db.database.name\">bonita</prop>\n                <prop key=\"db.user\">bonita</prop>\n                <prop key=\"db.password\">bonita</prop>\n                <prop key=\"db.url\">jdbc:postgresql://${db.server.name}:${db.server.port}/${db.database.name}</prop>\n            </props>\n        </property>\n    </bean>\n\n    <bean id=\"driverProperties\" class=\"org.springframework.beans.factory.config.PropertiesFactoryBean\">\n        <property name=\"properties\">\n            <props>\n                <prop key=\"user\">${db.user}</prop>\n                <prop key=\"password\">${db.password}</prop>\n                <prop key=\"serverName\">${db.server.name}</prop>\n                <prop key=\"portNumber\">${db.server.port}</prop>\n                <prop key=\"databaseName\">${db.database.name}</prop>\n            </props>\n        </property>\n    </bean>\n\n</beans>\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/resources/logback-test.xml",
    "content": "<configuration>\n\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->\n        <encoder>\n            <pattern>|%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <logger name=\"org.bonitasoft\" level=\"INFO\" />\n    <!-- To remove hibernate annoying warnings: -->\n    <logger name=\"org.hibernate\" level=\"ERROR\" />\n\n    <root level=\"WARN\">\n        <appender-ref ref=\"STDOUT\" />\n    </root>\n    <!-- Show events of synchro repository -->\n    <!-- <logger name=\"org.bonitasoft.engine.test.synchro.SynchroRepository\" level=\"DEBUG\" /> -->\n\n    <!-- Show stack traces when there is error on jobs -->\n    <!-- <logger name=\"org.bonitasoft.engine.scheduler.impl.JobWrapper\" level=\"DEBUG\" /> -->\n\n    <!-- Show lock acquire/release -->\n    <!--<logger name=\"org.bonitasoft.engine.lock.impl.MemoryLockService\" level=\"TRACE\" />-->\n    <!--<logger name=\"org.bonitasoft.engine.execution.work.FailureHandlingBonitaWork\" level=\"TRACE\" />-->\n    <!--<logger name=\"org.bonitasoft.engine.core.process.instance.impl.GatewayInstanceServiceImpl\" level=\"TRACE\" />-->\n    <!--<logger name=\"org.bonitasoft.engine.execution.ProcessExecutorImpl\" level=\"DEBUG\" />-->\n    <!---<logger name=\"org.bonitasoft.engine.execution.FlowNodeExecutorImpl\" level=\"DEBUG\" />-->\n    <!--<logger name=\"org.bonitasoft.engine.core.process.instance.impl.ActivityInstanceServiceImpl\" level=\"DEBUG\" />-->\n    <!--<logger name=\"org.bonitasoft.engine.recorder.impl.RecorderImpl\" level=\"DEBUG\" />-->\n    <!--<logger name=\"org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceServiceImpl\" level=\"DEBUG\" />-->\n    <!--<logger name=\"org.bonitasoft.engine.execution.work.TxBonitaWork\" level=\"DEBUG\" />-->\n    <!--<logger name=\"org.bonitasoft.engine.execution.work.LockProcessInstanceWork\" level=\"DEBUG\" />-->\n    <!--<logger name=\"org.bonitasoft.engine.work.ExecutorWorkService\" level=\"DEBUG\" />-->\n    <!--<logger name=\"org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl\" level=\"ERROR\"/>-->\n\n\n    <!-- Quartz log for job lifecycle -->\n    <!-- <property name=\"baseLogFileName\" value=\"target/log/quartzJobs\" />\n\n    <appender name=\"FILE\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <file>${baseLogFileName}.log</file>\n\n        Support multiple-JVM writing to the same log file\n        <prudent>true</prudent>\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\">\n            <fileNamePattern>${baseLogFileName}-%d{yyyy-MM-dd}.log.gz</fileNamePattern>\n            <maxHistory>30</maxHistory>\n            <cleanHistoryOnStart>true</cleanHistoryOnStart>\n        </rollingPolicy>\n        <encoder>\n            <pattern>|%d{HH:mm:ss.SSS}| %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <logger name=\"org.bonitasoft.engine.scheduler.impl.FileJobListener\" level=\"INFO\">\n        <appender-ref ref=\"FILE\" />\n    </logger> -->\n\n</configuration>\n"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/resources/org/bonitasoft/engine/business/data/impl/EntitySerializerPojo.json",
    "content": "{\n  \"persistenceId\": 9223372036854775807,\n  \"persistenceId_string\": \"9223372036854775807\",\n  \"persistenceVersion\": 1,\n  \"persistenceVersion_string\": \"1\",\n  \"aString\": \"string\",\n  \"aBoolean\": true,\n  \"aDate\": 123,\n  \"anOffsetDateTime\": \"1949-04-09T11:14:05Z\",\n  \"aLocalDate\": \"2017-03-06\",\n  \"aLocalDateTime\": \"1945-05-08T12:31:17\",\n  \"aDouble\": 1.7976931348623157E308,\n  \"aDouble_string\": \"1.7976931348623157E308\",\n  \"aFloat\": 3.4028235E38,\n  \"aFloat_string\": \"3.4028235E38\",\n  \"aInteger\": 2147483647,\n  \"aLong\": 9223372036854775807,\n  \"aLong_string\": \"9223372036854775807\",\n  \"aText\": \"text\",\n  \"manyLong\": [\n    1,\n    2\n  ],\n  \"manyLong_string\": [\n    \"1\",\n    \"2\"\n  ],\n  \"manyString\": [\n    \"abc\",\n    \"def\"\n  ]\n}"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/resources/org/bonitasoft/engine/business/data/impl/multiplePerson.json",
    "content": "[\n  {\n    \"persistenceId\": 1,\n    \"persistenceVersion\": 2,\n    \"persistenceId_string\": \"1\",\n    \"persistenceVersion_string\": \"2\",\n    \"name\": \"John\",\n    \"age\": 50,\n    \"phones\": [\n      {\n        \"persistenceId\": null,\n        \"persistenceVersion\": null,\n        \"persistenceId_string\": null,\n        \"persistenceVersion_string\": null,\n        \"number\": \"1234560\"\n      },\n      {\n        \"persistenceId\": null,\n        \"persistenceVersion\": null,\n        \"persistenceId_string\": null,\n        \"persistenceVersion_string\": null,\n        \"number\": \"1234561\"\n      },\n      {\n        \"persistenceId\": null,\n        \"persistenceVersion\": null,\n        \"persistenceId_string\": null,\n        \"persistenceVersion_string\": null,\n        \"number\": \"1234562\"\n      }\n    ],\n    \"birthday\": 123456789,\n    \"hasMobile\": true,\n    \"bools\": [\n      true,\n      false,\n      true,\n      false\n    ]\n  },\n  {\n    \"persistenceId\": 2,\n    \"persistenceVersion\": 2,\n    \"persistenceId_string\": \"2\",\n    \"persistenceVersion_string\": \"2\",\n    \"name\": \"John\",\n    \"age\": 50,\n    \"phones\": [\n      {\n        \"persistenceId\": null,\n        \"persistenceVersion\": null,\n        \"persistenceId_string\": null,\n        \"persistenceVersion_string\": null,\n        \"number\": \"1234560\"\n      },\n      {\n        \"persistenceId\": null,\n        \"persistenceVersion\": null,\n        \"persistenceId_string\": null,\n        \"persistenceVersion_string\": null,\n        \"number\": \"1234561\"\n      },\n      {\n        \"persistenceId\": null,\n        \"persistenceVersion\": null,\n        \"persistenceId_string\": null,\n        \"persistenceVersion_string\": null,\n        \"number\": \"1234562\"\n      }\n    ],\n    \"birthday\": 123456789,\n    \"hasMobile\": true,\n    \"bools\": [\n      true,\n      false,\n      true,\n      false\n    ]\n  }\n]"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/resources/org/bonitasoft/engine/business/data/impl/personWithDetails.json",
    "content": "{\n  \"persistenceId\": 666,\n  \"persistenceId_string\": \"666\",\n  \"persistenceVersion\": 2,\n  \"persistenceVersion_string\": \"2\",\n  \"name\": \"John\",\n  \"age\": 50,\n  \"phones\": [\n    {\n      \"persistenceId\": 22365,\n      \"persistenceId_string\": \"22365\",\n      \"persistenceVersion\": 365,\n      \"persistenceVersion_string\": \"365\",\n      \"number\": \"1234560\"\n    },\n    {\n      \"persistenceId\": 22366,\n      \"persistenceId_string\": \"22366\",\n      \"persistenceVersion\": 366,\n      \"persistenceVersion_string\": \"366\",\n      \"number\": \"1234561\"\n    },\n    {\n      \"persistenceId\": 22367,\n      \"persistenceId_string\": \"22367\",\n      \"persistenceVersion\": 367,\n      \"persistenceVersion_string\": \"367\",\n      \"number\": \"1234562\"\n    }\n  ],\n  \"birthday\": 123456789,\n  \"hasMobile\": true,\n  \"bools\": [\n    true,\n    false,\n    true,\n    false\n  ],\n  \"address1\": {\n    \"persistenceId\": 2598,\n    \"persistenceId_string\": \"2598\",\n    \"persistenceVersion\": 99992598,\n    \"persistenceVersion_string\": \"99992598\",\n    \"street\": \"Rue Gustave Eiffel\",\n    \"number\": 32.0,\n    \"number_string\": \"32.0\",\n    \"floors\": [\n      1.0,\n      4.0\n    ],\n    \"floors_string\": [\n      \"1.0\",\n      \"4.0\"\n    ],\n    \"links\": [\n      {\n        \"rel\": \"doorCode\",\n        \"href\": \"/businessdata/com.company.model.Address/2598/doorCode\"\n      }\n    ]\n  },\n  \"address2\": {\n    \"persistenceId\": 358,\n    \"persistenceId_string\": \"358\",\n    \"persistenceVersion\": 118,\n    \"persistenceVersion_string\": \"118\",\n    \"street\": null,\n    \"number\": null,\n    \"number_string\": null,\n    \"floors\": null,\n    \"floors_string\": null,\n    \"links\": [\n      {\n        \"rel\": \"doorCode\",\n        \"href\": \"/businessdata/com.company.model.Address/358/doorCode\"\n      }\n    ]\n  },\n  \"toIncludes\": [\n    1235,\n    6666,\n    7777\n  ],\n  \"toIncludes_string\": [\n    \"1235\",\n    \"6666\",\n    \"7777\"\n  ],\n  \"methodHandlerObject\": {},\n  \"proxys\": [\n    {},\n    {},\n    {}\n  ],\n  \"links\": [\n    {\n      \"rel\": \"secretPhone\",\n      \"href\": \"/businessdata/com.company.model.PersonWithDetails/666/secretPhone\"\n    },\n    {\n      \"rel\": \"toIgnores\",\n      \"href\": \"/businessdata/com.company.model.PersonWithDetails/666/toIgnores\"\n    }\n  ]\n}"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/resources/org/bonitasoft/engine/business/data/impl/singlePerson.json",
    "content": "{\n  \"persistenceId\": 1,\n  \"persistenceVersion\": 2,\n  \"persistenceId_string\": \"1\",\n  \"persistenceVersion_string\": \"2\",\n  \"name\": \"John\",\n  \"age\": 50,\n  \"phones\": [\n    {\n      \"persistenceId\": null,\n      \"persistenceVersion\": null,\n      \"persistenceId_string\": null,\n      \"persistenceVersion_string\": null,\n      \"number\": \"1234560\"\n    },\n    {\n      \"persistenceId\": null,\n      \"persistenceVersion\": null,\n      \"persistenceId_string\": null,\n      \"persistenceVersion_string\": null,\n      \"number\": \"1234561\"\n    },\n    {\n      \"persistenceId\": null,\n      \"persistenceVersion\": null,\n      \"persistenceId_string\": null,\n      \"persistenceVersion_string\": null,\n      \"number\": \"1234562\"\n    }\n  ],\n  \"birthday\": 123456789,\n  \"hasMobile\": true,\n  \"bools\": [\n    true,\n    false,\n    true,\n    false\n  ]\n}"
  },
  {
    "path": "services/bonita-business-data/bonita-business-data-impl/src/test/resources/testContext.xml",
    "content": "<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:util=\"http://www.springframework.org/schema/util\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans\n\t   http://www.springframework.org/schema/beans/spring-beans-4.3.xsd\n\n\n\t   http://www.springframework.org/schema/util \n\t   http://www.springframework.org/schema/util/spring-util-4.3.xsd\">\n\n    <import resource=\"classpath:/datasource/datasource-dependency-${sysprop.bonita.db.vendor:h2}.xml\" />\n\n\n    <!-- create Arjuna transaction manager -->\n    <bean id=\"arjunaTransactionManager\" factory-method=\"transactionManager\" class=\"com.arjuna.ats.jta.TransactionManager\" />\n\n    <bean id=\"businessDataDataSource\" class=\"org.apache.tomcat.dbcp.dbcp2.managed.BasicManagedDataSource\">\n        <property name=\"initialSize\" value=\"1\" />\n        <property name=\"maxTotal\" value=\"10\" />\n        <property name=\"url\" value=\"${db.url}\" />\n        <property name=\"username\" value=\"${db.user}\" />\n        <property name=\"password\" value=\"${db.password}\" />\n        <property name=\"transactionManager\" ref=\"arjunaTransactionManager\" />\n    </bean>\n\n    <bean id=\"notManagedBizDataSource\"\n          class=\"org.apache.tomcat.dbcp.dbcp2.BasicDataSource\" destroy-method=\"close\">\n        <property name=\"initialSize\" value=\"1\" />\n        <property name=\"maxTotal\" value=\"10\" />\n        <property name=\"url\" value=\"${db.url}\" />\n        <property name=\"username\" value=\"${db.user}\" />\n        <property name=\"password\" value=\"${db.password}\" />\n    </bean>\n\n    <bean id=\"jpa-test-configuration\" class=\"org.bonitasoft.engine.business.data.JpaTestConfiguration\">\n        <constructor-arg name=\"jpaConfiguration\">\n            <util:map id=\"jpa-configuration\">\n                <entry key=\"hibernate.dialect\" value=\"${db.hibernate.dialect}\" />\n                <entry key=\"hibernate.connection.datasource\" value-ref=\"businessDataDataSource\" />\n            </util:map>\n        </constructor-arg>\n        <constructor-arg name=\"jpaModelConfiguration\">\n            <util:map id=\"jpa-model-configuration\">\n                <entry key=\"hibernate.dialect\" value=\"${db.hibernate.dialect}\" />\n                <entry key=\"hibernate.show_sql\" value=\"false\" />\n                <entry key=\"hibernate.format_sql\" value=\"true\" />\n                <entry key=\"hibernate.connection.datasource\" value-ref=\"notManagedBizDataSource\" />\n            </util:map>\n        </constructor-arg>\n    </bean>\n\n</beans>\n"
  },
  {
    "path": "services/bonita-cache/build.gradle",
    "content": "\n\ndependencies {\n    api libs.ehCache\n    api project(':services:bonita-commons')\n    api project(':services:bonita-session')\n    api libs.springContext\n\n    annotationProcessor libs.lombok\n    compileOnly libs.lombok\n\n    testImplementation libs.mockitoCore\n    testImplementation libs.logback\n    testImplementation libs.systemLambda\n    testImplementation libs.assertj\n}\n"
  },
  {
    "path": "services/bonita-cache/src/main/java/org/bonitasoft/engine/cache/CacheConfiguration.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.cache;\n\nimport lombok.Getter;\nimport lombok.Setter;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@Setter\n@Getter\npublic class CacheConfiguration {\n\n    /**\n     * most implementation support LRU and LFU\n     * some implementation (ehcache) support FIFO also\n     * by default set to LRU\n     */\n    private String evictionPolicy = \"LRU\";\n\n    /**\n     * The time to live is the time elements from this cache will be kept.\n     * After this time the element can be evicted\n     */\n    private long timeToLiveSeconds = 60 * 60;\n\n    /**\n     * the maximum number of elements the cache will keep in memory\n     */\n    private int maxElementsInMemory = 10000;\n\n    /**\n     * @param maxElementsInMemory\n     *        the maxElementsInMemory to set. Zero is an invalid value (infinite).\n     *        If value is set to 0 or less, the value will be reset to 1.\n     */\n    public void setMaxElementsInMemory(final int maxElementsInMemory) {\n        this.maxElementsInMemory = maxElementsInMemory;\n        if (this.maxElementsInMemory <= 0) {\n            this.maxElementsInMemory = 1;\n        }\n    }\n\n    /**\n     * true if the elements are never evicted automatically\n     */\n    private boolean eternal = false;\n\n    /**\n     * Are the elements stored in the cache read more often than written ?\n     */\n    private boolean readIntensive = false;\n\n    /**\n     * Off-heap memory size in megabytes.\n     * Zero means no off-heap storage (heap-only).\n     * <p>\n     * Off-heap storage provides overflow capacity for the heap tier without requiring disk I/O.\n     * It uses native memory (outside JVM heap) which doesn't participate in garbage collection,\n     * reducing GC pressure while maintaining good performance.\n     * </p>\n     * <p>\n     * Example: 512 = 512MB of off-heap memory\n     * </p>\n     * <p>\n     * Note: Off-heap storage requires that cached objects are serializable.\n     * </p>\n     */\n    private int offHeapSizeMB = 0; // Default: no off-heap (heap-only)\n\n    /**\n     * Name of this cache configuration\n     */\n    private String name;\n\n}\n"
  },
  {
    "path": "services/bonita-cache/src/main/java/org/bonitasoft/engine/cache/CacheService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.cache;\n\nimport java.io.Serializable;\nimport java.util.List;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface CacheService {\n\n    /**\n     * Store an object in the cache. If the cache don't exists it will be created.\n     *\n     * @param cacheName The name of the cache in which the object must be stored\n     * @param key The key that will allow to retrieve the object\n     * @param value The object to store\n     * @throws SCacheException Error thrown if has exceptions during the cache store.\n     */\n    void store(String cacheName, Serializable key, Object value) throws SCacheException;\n\n    /**\n     * Remove the element according to the cache name and the key\n     *\n     * @param cacheName the name of the cache to remove from\n     * @param key the key to remove from the cache\n     * @return true if an element was removed\n     * @throws SCacheException Error thrown if has exceptions during the cache removal.\n     */\n    boolean remove(String cacheName, Object key) throws SCacheException;\n\n    /**\n     * Get a cached object.\n     *\n     * @param cacheName\n     *        The name of the cache on which to get the object\n     * @param key\n     *        The key that is used to store the object\n     * @return the cached object, or null if it doesn't exists\n     * @throws SCacheException\n     *         Error thrown if has exceptions during the cache object get.\n     */\n    Object get(String cacheName, Object key) throws SCacheException;\n\n    /**\n     * Get list of keys on a cache.\n     *\n     * @param cacheName\n     *        The name of the cache on which to get the key list\n     * @return the list of keys on the cache, or null if no keys exist\n     * @throws SCacheException if the cache cannot be read\n     */\n    List<Object> getKeys(String cacheName) throws SCacheException;\n\n    /**\n     * Clear the cache named by cacheName\n     *\n     * @param cacheName\n     *        The name of the cache to clear\n     * @return true if no cache was found with the given name\n     * @throws SCacheException\n     *         Error thrown if has exceptions during the cache clear.\n     */\n    boolean clear(String cacheName) throws SCacheException;\n\n    /**\n     * Clear all cache of the service\n     *\n     * @throws SCacheException\n     *         Error thrown if has exceptions during the cache clear.\n     */\n    void clearAll() throws SCacheException;\n\n    /**\n     * Return the size of the cache with cacheName.\n     *\n     * @param cacheName\n     *        The name of cache\n     * @return the size of the named cache\n     * @throws SCacheException\n     *         if no cache is found with that name.\n     */\n    int getCacheSize(String cacheName) throws SCacheException;\n\n    /**\n     * Get the names of all the caches\n     *\n     * @return a list of caches names\n     */\n    List<String> getCachesNames();\n\n    boolean isStopped();\n}\n"
  },
  {
    "path": "services/bonita-cache/src/main/java/org/bonitasoft/engine/cache/SCacheException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.cache;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class SCacheException extends SBonitaException {\n\n    private static final long serialVersionUID = 3608017699323834096L;\n\n    public SCacheException(final String message) {\n        super(message);\n    }\n\n    public SCacheException(final Throwable t) {\n        super(t);\n    }\n\n    public SCacheException(final String message, final Exception t) {\n        super(message, t);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-cache/src/main/java/org/bonitasoft/engine/cache/configuration/CacheConfigurationBeans.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.cache.configuration;\n\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * Spring configuration class that defines cache configuration beans for the Bonita platform.\n * <p>\n * This class provides factory methods for creating {@link org.bonitasoft.engine.cache.CacheConfiguration}\n * beans with their respective properties loaded from application configuration files.\n * Each cache configuration is parameterized through Spring's {@code @Value} annotations,\n * allowing runtime configuration via properties files.\n * </p>\n * <p>\n * The configured caches include:\n * </p>\n * <ul>\n * <li><b>Application Token Cache</b> - Stores application authentication tokens</li>\n * <li><b>Default Platform Cache</b> - General-purpose platform-level cache</li>\n * <li><b>Synchro Service Cache</b> - Synchronization service cache</li>\n * <li><b>Config Files Cache</b> - Configuration files cache</li>\n * <li><b>Connector Cache</b> - Connector definitions and instances</li>\n * <li><b>Process Definition Cache</b> - BPMN process definitions</li>\n * <li><b>Parameter Cache</b> - Process and tenant parameters</li>\n * <li><b>User Filter Cache</b> - User filter definitions</li>\n * <li><b>Groovy Script Cache</b> - Compiled Groovy scripts</li>\n * <li><b>Transient Data Cache</b> - Transient process data</li>\n * </ul>\n *\n * @see org.bonitasoft.engine.cache.CacheConfiguration\n */\n@Configuration\npublic class CacheConfigurationBeans {\n\n    public static final String APPLICATION_TOKEN_CACHE_NAME = \"application-token\";\n\n    /**\n     * Creates the cache configuration for application tokens.\n     * <p>\n     * This cache stores tokens used for applications.\n     * Configuration properties are loaded from {@code bonita.runtime.cache.application-token.*}.\n     * </p>\n     *\n     * @param maxElementsInMemory the maximum number of elements that can be stored in memory\n     * @param eternal whether cached elements never expire\n     * @param evictionPolicy the eviction policy (e.g., \"LRU\", \"LFU\", \"FIFO\")\n     * @param readIntensive whether this cache is optimized for read-intensive workloads\n     * @param timeToLiveSeconds the time-to-live in seconds for cached elements\n     * @param offHeapSizeMB the size of off-heap memory in MB (0 = no off-heap)\n     * @return the configured cache configuration bean\n     */\n    @Bean\n    public org.bonitasoft.engine.cache.CacheConfiguration applicationTokenCacheConfiguration(\n            @Value(\"${bonita.runtime.cache.application-token.maxElementsInMemory:1000}\") final int maxElementsInMemory,\n            @Value(\"${bonita.runtime.cache.application-token.eternal:false}\") final boolean eternal,\n            @Value(\"${bonita.runtime.cache.application-token.evictionPolicy:LRU}\") final String evictionPolicy,\n            @Value(\"${bonita.runtime.cache.application-token.readIntensive:false}\") final boolean readIntensive,\n            @Value(\"${bonita.runtime.cache.application-token.timeToLiveSeconds:3600}\") final int timeToLiveSeconds,\n            @Value(\"${bonita.runtime.cache.application-token.offHeapSizeMB:0}\") final int offHeapSizeMB) {\n        return createCacheConfiguration(APPLICATION_TOKEN_CACHE_NAME, maxElementsInMemory, eternal, evictionPolicy,\n                readIntensive, timeToLiveSeconds, offHeapSizeMB);\n    }\n\n    @Bean\n    public org.bonitasoft.engine.cache.CacheConfiguration defaultCacheConfiguration(\n            @Value(\"${bonita.platform.cache.default.maxElementsInMemory}\") final int maxElementsInMemory,\n            @Value(\"${bonita.platform.cache.default.eternal}\") final boolean eternal,\n            @Value(\"${bonita.platform.cache.default.evictionPolicy}\") final String evictionPolicy,\n            @Value(\"${bonita.platform.cache.default.readIntensive}\") final boolean readIntensive,\n            @Value(\"${bonita.platform.cache.default.timeToLiveSeconds}\") final int timeToLiveSeconds,\n            @Value(\"${bonita.platform.cache.default.offHeapSizeMB:0}\") final int offHeapSizeMB) {\n        return createCacheConfiguration(\"DEFAULT_PLATFORM\", maxElementsInMemory, eternal, evictionPolicy,\n                readIntensive, timeToLiveSeconds, offHeapSizeMB);\n    }\n\n    @Bean\n    public org.bonitasoft.engine.cache.CacheConfiguration synchroServiceCacheConfig(\n            @Value(\"${bonita.platform.cache.synchro.maxElementsInMemory}\") final int maxElementsInMemory,\n            @Value(\"${bonita.platform.cache.synchro.eternal}\") final boolean eternal,\n            @Value(\"${bonita.platform.cache.synchro.evictionPolicy}\") final String evictionPolicy,\n            @Value(\"${bonita.platform.cache.synchro.readIntensive}\") final boolean readIntensive,\n            @Value(\"${bonita.platform.cache.synchro.timeToLiveSeconds}\") final int timeToLiveSeconds,\n            @Value(\"${bonita.platform.cache.synchro.offHeapSizeMB:0}\") final int offHeapSizeMB) {\n        return createCacheConfiguration(\"SYNCHRO_SERVICE_CACHE\", maxElementsInMemory, eternal, evictionPolicy,\n                readIntensive, timeToLiveSeconds, offHeapSizeMB);\n    }\n\n    @Bean\n    public org.bonitasoft.engine.cache.CacheConfiguration configFilesCacheConfig(\n            @Value(\"${bonita.platform.cache.configfiles.maxElementsInMemory}\") final int maxElementsInMemory,\n            @Value(\"${bonita.platform.cache.configfiles.eternal}\") final boolean eternal,\n            @Value(\"${bonita.platform.cache.configfiles.evictionPolicy}\") final String evictionPolicy,\n            @Value(\"${bonita.platform.cache.configfiles.readIntensive}\") final boolean readIntensive,\n            @Value(\"${bonita.platform.cache.configfiles.timeToLiveSeconds}\") final int timeToLiveSeconds,\n            @Value(\"${bonita.platform.cache.configfiles.offHeapSizeMB:0}\") final int offHeapSizeMB) {\n        return createCacheConfiguration(\"CONFIGURATION_FILES_CACHE\", maxElementsInMemory, eternal, evictionPolicy,\n                readIntensive, timeToLiveSeconds, offHeapSizeMB);\n    }\n\n    @Bean\n    public org.bonitasoft.engine.cache.CacheConfiguration connectorCacheConfig(\n            @Value(\"${bonita.tenant.cache.connector.maxElementsInMemory}\") final int maxElementsInMemory,\n            @Value(\"${bonita.tenant.cache.connector.eternal}\") final boolean eternal,\n            @Value(\"${bonita.tenant.cache.connector.evictionPolicy}\") final String evictionPolicy,\n            @Value(\"${bonita.tenant.cache.connector.readIntensive}\") final boolean readIntensive,\n            @Value(\"${bonita.tenant.cache.connector.timeToLiveSeconds}\") final int timeToLiveSeconds,\n            @Value(\"${bonita.tenant.cache.connector.offHeapSizeMB:0}\") final int offHeapSizeMB) {\n        return createCacheConfiguration(\"CONNECTOR\", maxElementsInMemory, eternal, evictionPolicy, readIntensive,\n                timeToLiveSeconds, offHeapSizeMB);\n    }\n\n    @Bean\n    public org.bonitasoft.engine.cache.CacheConfiguration processDefinitionCacheConfig(\n            @Value(\"${bonita.tenant.cache.processdef.maxElementsInMemory}\") final int maxElementsInMemory,\n            @Value(\"${bonita.tenant.cache.processdef.eternal}\") final boolean eternal,\n            @Value(\"${bonita.tenant.cache.processdef.evictionPolicy}\") final String evictionPolicy,\n            @Value(\"${bonita.tenant.cache.processdef.readIntensive}\") final boolean readIntensive,\n            @Value(\"${bonita.tenant.cache.processdef.timeToLiveSeconds}\") final int timeToLiveSeconds,\n            @Value(\"${bonita.tenant.cache.processdef.offHeapSizeMB:0}\") final int offHeapSizeMB) {\n        return createCacheConfiguration(\"_PROCESSDEF\", maxElementsInMemory, eternal, evictionPolicy, readIntensive,\n                timeToLiveSeconds, offHeapSizeMB);\n    }\n\n    @Bean\n    public org.bonitasoft.engine.cache.CacheConfiguration parameterCacheConfig(\n            @Value(\"${bonita.tenant.cache.parameter.maxElementsInMemory}\") final int maxElementsInMemory,\n            @Value(\"${bonita.tenant.cache.parameter.eternal}\") final boolean eternal,\n            @Value(\"${bonita.tenant.cache.parameter.evictionPolicy}\") final String evictionPolicy,\n            @Value(\"${bonita.tenant.cache.parameter.readIntensive}\") final boolean readIntensive,\n            @Value(\"${bonita.tenant.cache.parameter.timeToLiveSeconds}\") final int timeToLiveSeconds,\n            @Value(\"${bonita.tenant.cache.parameter.offHeapSizeMB:0}\") final int offHeapSizeMB) {\n        return createCacheConfiguration(\"parameters\", maxElementsInMemory, eternal, evictionPolicy, readIntensive,\n                timeToLiveSeconds, offHeapSizeMB);\n    }\n\n    @Bean\n    public org.bonitasoft.engine.cache.CacheConfiguration userFilterCacheConfig(\n            @Value(\"${bonita.tenant.cache.userfilter.maxElementsInMemory}\") final int maxElementsInMemory,\n            @Value(\"${bonita.tenant.cache.userfilter.eternal}\") final boolean eternal,\n            @Value(\"${bonita.tenant.cache.userfilter.evictionPolicy}\") final String evictionPolicy,\n            @Value(\"${bonita.tenant.cache.userfilter.readIntensive}\") final boolean readIntensive,\n            @Value(\"${bonita.tenant.cache.userfilter.timeToLiveSeconds}\") final int timeToLiveSeconds,\n            @Value(\"${bonita.tenant.cache.userfilter.offHeapSizeMB:0}\") final int offHeapSizeMB) {\n        return createCacheConfiguration(\"USER_FILTER\", maxElementsInMemory, eternal, evictionPolicy, readIntensive,\n                timeToLiveSeconds, offHeapSizeMB);\n    }\n\n    @Bean\n    public org.bonitasoft.engine.cache.CacheConfiguration groovyScriptCacheConfig(\n            @Value(\"${bonita.tenant.cache.groovy.maxElementsInMemory}\") final int maxElementsInMemory,\n            @Value(\"${bonita.tenant.cache.groovy.eternal}\") final boolean eternal,\n            @Value(\"${bonita.tenant.cache.groovy.evictionPolicy}\") final String evictionPolicy,\n            @Value(\"${bonita.tenant.cache.groovy.readIntensive}\") final boolean readIntensive,\n            @Value(\"${bonita.tenant.cache.groovy.timeToLiveSeconds}\") final int timeToLiveSeconds,\n            @Value(\"${bonita.tenant.cache.groovy.offHeapSizeMB:0}\") final int offHeapSizeMB) {\n        return createCacheConfiguration(\"GROOVY_SCRIPT_CACHE_NAME\", maxElementsInMemory, eternal, evictionPolicy,\n                readIntensive, timeToLiveSeconds, offHeapSizeMB);\n    }\n\n    @Bean\n    public org.bonitasoft.engine.cache.CacheConfiguration transientDataCacheConfig(\n            @Value(\"${bonita.tenant.cache.transientdata.maxElementsInMemory}\") final int maxElementsInMemory,\n            @Value(\"${bonita.tenant.cache.transientdata.eternal}\") final boolean eternal,\n            @Value(\"${bonita.tenant.cache.transientdata.evictionPolicy}\") final String evictionPolicy,\n            @Value(\"${bonita.tenant.cache.transientdata.readIntensive}\") final boolean readIntensive,\n            @Value(\"${bonita.tenant.cache.transientdata.timeToLiveSeconds}\") final int timeToLiveSeconds,\n            @Value(\"${bonita.tenant.cache.transientdata.offHeapSizeMB:0}\") final int offHeapSizeMB) {\n        return createCacheConfiguration(\"transient_data\", maxElementsInMemory, eternal, evictionPolicy, readIntensive,\n                timeToLiveSeconds, offHeapSizeMB);\n    }\n\n    private org.bonitasoft.engine.cache.CacheConfiguration createCacheConfiguration(String name,\n            int maxElementsInMemory, boolean eternal, String evictionPolicy, boolean readIntensive,\n            int timeToLiveSeconds, int offHeapSizeMB) {\n        var cacheConfiguration = new org.bonitasoft.engine.cache.CacheConfiguration();\n        cacheConfiguration.setName(name);\n        cacheConfiguration.setMaxElementsInMemory(maxElementsInMemory);\n        cacheConfiguration.setEternal(eternal);\n        cacheConfiguration.setEvictionPolicy(evictionPolicy);\n        cacheConfiguration.setReadIntensive(readIntensive);\n        cacheConfiguration.setTimeToLiveSeconds(timeToLiveSeconds);\n        cacheConfiguration.setOffHeapSizeMB(offHeapSizeMB);\n        return cacheConfiguration;\n    }\n}\n"
  },
  {
    "path": "services/bonita-cache/src/main/java/org/bonitasoft/engine/cache/ehcache/EhCacheCacheService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.cache.ehcache;\n\nimport static org.ehcache.config.builders.ExpiryPolicyBuilder.timeToLiveExpiration;\n\nimport java.io.Serializable;\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.cache.CacheConfiguration;\nimport org.bonitasoft.engine.cache.CacheService;\nimport org.bonitasoft.engine.cache.SCacheException;\nimport org.bonitasoft.engine.commons.PlatformLifecycleService;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException;\nimport org.ehcache.Cache;\nimport org.ehcache.CacheManager;\nimport org.ehcache.config.builders.CacheConfigurationBuilder;\nimport org.ehcache.config.builders.CacheManagerBuilder;\nimport org.ehcache.config.builders.ResourcePoolsBuilder;\nimport org.ehcache.config.units.EntryUnit;\nimport org.ehcache.config.units.MemoryUnit;\nimport org.ehcache.core.internal.statistics.DefaultStatisticsService;\nimport org.ehcache.core.spi.service.StatisticsService;\nimport org.ehcache.core.statistics.CacheStatistics;\nimport org.ehcache.core.statistics.TierStatistics;\nimport org.ehcache.impl.config.serializer.DefaultSerializerConfiguration;\nimport org.ehcache.impl.config.serializer.DefaultSerializerConfiguration.Type;\nimport org.ehcache.impl.serialization.PlainJavaSerializer;\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Matthieu Chaffotte\n */\n// Must be started before the work service so it has a \"higher\" priority\n@Order(2)\n@Component\n@ConditionalOnSingleCandidate(CacheService.class)\n@Slf4j\npublic class EhCacheCacheService implements CacheService, PlatformLifecycleService {\n\n    protected CacheManager cacheManager;\n\n    protected StatisticsService statisticsService;\n\n    protected final Map<String, org.ehcache.config.CacheConfiguration<Object, Object>> cacheConfigurations;\n\n    private final org.ehcache.config.CacheConfiguration<Object, Object> defaultCacheConfiguration;\n\n    public EhCacheCacheService(List<CacheConfiguration> cacheConfigurations,\n            @Qualifier(\"defaultCacheConfiguration\") CacheConfiguration defaultCacheConfiguration) {\n        this.defaultCacheConfiguration = getEhCacheConfiguration(defaultCacheConfiguration);\n        if (cacheConfigurations != null && !cacheConfigurations.isEmpty()) {\n            this.cacheConfigurations = new HashMap<>(cacheConfigurations.size());\n            for (final CacheConfiguration cacheConfig : cacheConfigurations) {\n                this.cacheConfigurations.put(cacheConfig.getName(), getEhCacheConfiguration(cacheConfig));\n            }\n        } else {\n            this.cacheConfigurations = Collections.emptyMap();\n        }\n    }\n\n    // VisibleForTesting\n    protected Set<String> getCacheConfigurationNames() {\n        return cacheConfigurations.keySet();\n    }\n\n    protected org.ehcache.config.CacheConfiguration<Object, Object> getEhCacheConfiguration(\n            final CacheConfiguration cacheConfig) {\n        // Build resource pools based on configuration\n        // Ehcache 3 disk storage requires explicit serialization configuration which is complex\n        // For simplicity, use heap-only caching or heap + offheap (which doesn't require complex serialization)\n        // If more capacity is needed beyond heap, offheap provides better performance than disk anyway\n        ResourcePoolsBuilder poolsBuilder = ResourcePoolsBuilder.newResourcePoolsBuilder()\n                .heap(cacheConfig.getMaxElementsInMemory(), EntryUnit.ENTRIES);\n\n        // Add off-heap tier if configured (provides overflow capacity without disk I/O)\n        // Off-heap memory is outside JVM heap, so it doesn't participate in GC\n        // This reduces GC pressure while maintaining good performance (all in RAM)\n        if (cacheConfig.getOffHeapSizeMB() > 0) {\n            poolsBuilder = poolsBuilder.offheap(cacheConfig.getOffHeapSizeMB(), MemoryUnit.MB);\n            log.debug(\"Configuring cache '{}' with off-heap storage: {}MB\",\n                    cacheConfig.getName(), cacheConfig.getOffHeapSizeMB());\n        }\n\n        org.ehcache.config.ResourcePools resourcePools = poolsBuilder.build();\n\n        // Note: Disk storage removed to avoid serialization complexity\n        // Ehcache 2 disk overflow is replaced by heap + optional off-heap tiers in Ehcache 3\n\n        // Build cache configuration\n        CacheConfigurationBuilder<Object, Object> builder = CacheConfigurationBuilder\n                .newCacheConfigurationBuilder(Object.class, Object.class, resourcePools);\n\n        // Configure serialization for off-heap storage\n        // Off-heap requires serialization to convert objects to byte arrays for native memory storage\n        if (cacheConfig.getOffHeapSizeMB() > 0) {\n            builder = builder\n                    .withService(new DefaultSerializerConfiguration(PlainJavaSerializer.class, Type.KEY))\n                    .withService(new DefaultSerializerConfiguration(PlainJavaSerializer.class, Type.VALUE));\n        }\n\n        // Set expiry policy\n        if (!cacheConfig.isEternal()) {\n            final long timeToLiveSeconds = cacheConfig.getTimeToLiveSeconds();\n            builder = builder.withExpiry(timeToLiveExpiration(Duration.ofSeconds(timeToLiveSeconds)));\n        }\n\n        return builder.build();\n    }\n\n    @Override\n    public List<String> getCachesNames() {\n        if (cacheManager == null) {\n            return Collections.emptyList();\n        }\n        return new ArrayList<>(cacheManager.getRuntimeConfiguration().getCacheConfigurations().keySet());\n    }\n\n    @Override\n    public synchronized void stop() {\n        shutdownCacheManager();\n    }\n\n    @Override\n    public void pause() {\n        try {\n            clearAll();\n        } catch (final SCacheException sce) {\n            throw new SBonitaRuntimeException(sce);\n        }\n    }\n\n    @Override\n    public void resume() throws SCacheException {\n        if (cacheManager == null) {\n            start();\n        }\n    }\n\n    protected synchronized Cache<Object, Object> createCache(final String cacheName)\n            throws SCacheException {\n        if (cacheManager == null) {\n            throw new SCacheException(\"The cache is not started, call start() on the cache service\");\n        }\n        Cache<Object, Object> cache = cacheManager.getCache(cacheName, Object.class, Object.class);\n        if (cache == null) {\n            final org.ehcache.config.CacheConfiguration<Object, Object> cacheConfiguration = cacheConfigurations\n                    .get(cacheName);\n            final org.ehcache.config.CacheConfiguration<Object, Object> configToUse;\n            if (cacheConfiguration != null) {\n                configToUse = cacheConfiguration;\n            } else {\n                log.warn(\"No specific cache configuration found for cache '{}'. Using default configuration\",\n                        cacheName);\n                configToUse = defaultCacheConfiguration;\n            }\n            // In Ehcache 3, we need to create the cache through the CacheManager\n            // This requires rebuilding the CacheManager with the new cache, or using a mutable manager\n            // For now, we'll create it dynamically - this may require CacheManagerBuilder.build() with persistence\n            cache = cacheManager.createCache(cacheName, configToUse);\n        }\n        return cache;\n    }\n\n    @Override\n    public void store(final String cacheName, final Serializable key, final Object value) throws SCacheException {\n        if (cacheManager == null) {\n            throw new SCacheException(\"The cache is not started, call start() on the cache service\");\n        }\n        try {\n            Cache<Object, Object> cache = cacheManager.getCache(cacheName, Object.class, Object.class);\n            if (cache == null) {\n                cache = createCache(cacheName);\n            }\n            // In Ehcache 3, we directly put key-value without Element wrapper\n            cache.put(key, value);\n        } catch (final IllegalStateException e) {\n            throw new SCacheException(\"The cache '\" + cacheName + \"' is not alive\", e);\n        } catch (final RuntimeException e) {\n            throw new SCacheException(\"Error storing value in cache '\" + cacheName + \"'\", e);\n        }\n    }\n\n    @Override\n    public Object get(final String cacheName, final Object key) throws SCacheException {\n        if (cacheManager == null) {\n            return null;\n        }\n        try {\n            final Cache<Object, Object> cache = cacheManager.getCache(cacheName, Object.class, Object.class);\n            if (cache == null) {\n                // the cache does not exist = the key was not stored\n                return null;\n            }\n            // In Ehcache 3, get() returns the value directly (no Element wrapper)\n            return cache.get(key);\n        } catch (final IllegalStateException e) {\n            throw new SCacheException(\"The cache '\" + cacheName + \"' is not alive\", e);\n        } catch (final Exception e) {\n            throw new SCacheException(\"Error getting value from cache '\" + cacheName + \"'\", e);\n        }\n    }\n\n    @Override\n    public boolean clear(final String cacheName) throws SCacheException {\n        if (cacheManager == null) {\n            return true;\n        }\n        try {\n            final Cache<Object, Object> cache = cacheManager.getCache(cacheName, Object.class, Object.class);\n            if (cache != null) {\n                // In Ehcache 3, clear() is used instead of removeAll()\n                cache.clear();\n            }\n            return cache == null;\n        } catch (final IllegalStateException e) {\n            throw new SCacheException(\"The cache '\" + cacheName + \"' is not alive\", e);\n        } catch (final Exception e) {\n            throw new SCacheException(\"Error clearing cache '\" + cacheName + \"'\", e);\n        }\n    }\n\n    /**\n     * <pre>\n     * getCacheSize(cacheName)\n     *           ↓\n     * [Check cache exists]\n     *           ↓\n     * [Try Statistics API] ← O(1) - FAST\n     *     ├─ Success → Return size from tier statistics\n     *     └─ Failure → Log debug + Fall through\n     *           ↓\n     * [Iterate cache entries] ← O(n) - Fallback\n     *           ↓\n     * Return count\n     * </pre>\n     *\n     * @param cacheName The name of cache\n     */\n    @Override\n    public int getCacheSize(final String cacheName) throws SCacheException {\n        if (cacheManager == null) {\n            return 0;\n        }\n        try {\n            final Cache<Object, Object> cache = cacheManager.getCache(cacheName, Object.class, Object.class);\n            if (cache == null) {\n                return 0;\n            }\n            // Try to use statistics API for efficient size retrieval (O(1) complexity)\n            if (statisticsService != null) {\n                try {\n                    CacheStatistics cacheStatistics = statisticsService.getCacheStatistics(cacheName);\n                    if (cacheStatistics != null) {\n                        Map<String, TierStatistics> tierStatistics = cacheStatistics.getTierStatistics();\n                        if (tierStatistics != null) {\n                            // Sum up entries across all tiers (OnHeap, OffHeap, Disk)\n                            long totalSize = tierStatistics.values().stream()\n                                    .mapToLong(TierStatistics::getMappings)\n                                    .sum();\n                            return (int) totalSize;\n                        }\n                    }\n                } catch (Exception e) {\n                    log.debug(\"Failed to get cache size from statistics for cache '{}', falling back to iteration: {}\",\n                            cacheName, e.getMessage());\n                    // Fall through to iteration method\n                }\n            }\n            // Fallback: iterate through cache entries (O(n) complexity)\n            // This can be expensive for large caches\n            return countEntriesByIteration(cache);\n        } catch (final IllegalStateException e) {\n            throw new SCacheException(\"The cache '\" + cacheName + \"' is not alive\", e);\n        } catch (final RuntimeException e) {\n            throw new SCacheException(\"Error getting size of cache '\" + cacheName + \"'\", e);\n        }\n    }\n\n    private int countEntriesByIteration(Cache<Object, Object> cache) {\n        int count = 0;\n        for (Cache.Entry<Object, Object> ignored : cache) {\n            count++;\n        }\n        return count;\n    }\n\n    @Override\n    public void clearAll() throws SCacheException {\n        if (cacheManager == null) {\n            return;\n        }\n        try {\n            final List<String> cacheNames = getCachesNames();\n            for (final String cacheName : cacheNames) {\n                clear(cacheName);\n            }\n        } catch (final RuntimeException e) {\n            throw new SCacheException(\"Error clearing all caches\", e);\n        }\n    }\n\n    @Override\n    public boolean remove(final String cacheName, final Object key) throws SCacheException {\n        if (cacheManager == null) {\n            return false;\n        }\n        final Cache<Object, Object> cache = cacheManager.getCache(cacheName, Object.class, Object.class);\n        if (cache == null) {\n            return false;\n        }\n        // In Ehcache 3, remove() returns void, so we need to check if key existed first\n        boolean existed = cache.containsKey(key);\n        cache.remove(key);\n        return existed;\n    }\n\n    @Override\n    public List<Object> getKeys(final String cacheName) throws SCacheException {\n        if (cacheManager == null) {\n            return Collections.emptyList();\n        }\n        try {\n            final Cache<Object, Object> cache = cacheManager.getCache(cacheName, Object.class, Object.class);\n            if (cache == null) {\n                return Collections.emptyList();\n            }\n            // In Ehcache 3, we need to iterate over entries to get keys\n            List<Object> keys = new ArrayList<>();\n            for (Cache.Entry<Object, Object> entry : cache) {\n                keys.add(entry.getKey());\n            }\n            return keys;\n        } catch (final IllegalStateException e) {\n            throw new SCacheException(\"The cache '\" + cacheName + \"' is not alive\", e);\n        } catch (final RuntimeException e) {\n            throw new SCacheException(\"Error getting keys from cache '\" + cacheName + \"'\", e);\n        }\n    }\n\n    protected void shutdownCacheManager() {\n        if (cacheManager != null) {\n            // In Ehcache 3, close() is used instead of shutdown()\n            cacheManager.close();\n            cacheManager = null;\n        }\n    }\n\n    @Override\n    public boolean isStopped() {\n        return cacheManager == null;\n    }\n\n    protected String getCacheManagerName() {\n        return \"BONITA\";\n    }\n\n    @Override\n    public synchronized void start() throws SCacheException {\n        if (cacheManager == null) {\n            // Initialize statistics service for efficient cache size retrieval\n            statisticsService = new DefaultStatisticsService();\n            // In Ehcache 3, create a simple heap-only CacheManager with statistics enabled\n            // Disk persistence removed for simplicity (requires complex serialization configuration)\n            cacheManager = CacheManagerBuilder.newCacheManagerBuilder()\n                    .using(statisticsService)\n                    .build(true); // true = initialize immediately\n        }\n    }\n}\n"
  },
  {
    "path": "services/bonita-cache/src/test/java/org/bonitasoft/engine/cache/CacheServiceTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.cache;\n\nimport static com.github.stefanbirkner.systemlambda.SystemLambda.tapSystemOut;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.*;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.cache.ehcache.EhCacheCacheService;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.TestName;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class CacheServiceTest {\n\n    protected static final String SOME_DEFAULT_CACHE_NAME = \"SOME_DEFAULT_CACHE_NAME\";\n\n    protected static final String ETERNAL_CACHE = \"ETERNAL_CACHE\";\n    private static final String TEST1 = \"test1\";\n    private static final String TEST2 = \"test2\";\n    private static final String ONE_ELEMENT_IN_MEMORY_ONLY = \"ONE_ELEMENT_IN_MEMORY_ONLY\";\n    private static final String OFF_HEAP_CACHE = \"OFF_HEAP_CACHE\";\n    private static final String LARGE_OFF_HEAP_CACHE = \"LARGE_OFF_HEAP_CACHE\";\n\n    private final static Logger LOGGER = LoggerFactory.getLogger(CacheServiceTest.class);\n    private static final int ONE_ELEMENT_ONLY_MAX_ELEMENTS_IN_MEMORY = 1;\n\n    private EhCacheCacheService cacheService;\n\n    @Rule\n    public TestName name = new TestName();\n\n    @Before\n    public void setUp() throws Exception {\n        LOGGER.info(\"Testing : {}\", name.getMethodName());\n        cacheService = (EhCacheCacheService) getCacheService();\n        cacheService.clearAll();\n        cacheService.start();\n    }\n\n    @After\n    public void tearDown() {\n        LOGGER.info(\"Tested: {}\", name.getMethodName());\n        cacheService.stop();\n        cacheService = null;\n    }\n\n    protected CacheService getCacheService() {\n        final List<CacheConfiguration> configurationsList = new ArrayList<>(2);\n        final CacheConfiguration cacheConfiguration = new CacheConfiguration();\n        cacheConfiguration.setName(SOME_DEFAULT_CACHE_NAME);\n        cacheConfiguration.setTimeToLiveSeconds(1);\n        cacheConfiguration.setMaxElementsInMemory(200);\n        configurationsList.add(cacheConfiguration);\n\n        final CacheConfiguration cacheConfigurationEternal = new CacheConfiguration();\n        cacheConfigurationEternal.setName(ETERNAL_CACHE);\n        cacheConfigurationEternal.setTimeToLiveSeconds(1);\n        cacheConfigurationEternal.setMaxElementsInMemory(200);\n        cacheConfigurationEternal.setEternal(true);\n        configurationsList.add(cacheConfigurationEternal);\n\n        final CacheConfiguration cacheConfiguration1 = new CacheConfiguration();\n        cacheConfiguration1.setName(TEST1);\n        cacheConfiguration1.setTimeToLiveSeconds(1);\n        cacheConfiguration1.setMaxElementsInMemory(10_000);\n        cacheConfiguration1.setEternal(false);\n        configurationsList.add(cacheConfiguration1);\n\n        final CacheConfiguration cacheConfiguration2 = new CacheConfiguration();\n        cacheConfiguration2.setName(TEST2);\n        cacheConfiguration2.setTimeToLiveSeconds(1);\n        cacheConfiguration2.setMaxElementsInMemory(100_000);\n        cacheConfiguration2.setEternal(false);\n        configurationsList.add(cacheConfiguration2);\n\n        final CacheConfiguration cacheWithOneElementInMemoryOnly = new CacheConfiguration();\n        cacheWithOneElementInMemoryOnly.setName(ONE_ELEMENT_IN_MEMORY_ONLY);\n        cacheWithOneElementInMemoryOnly.setTimeToLiveSeconds(10);\n        cacheWithOneElementInMemoryOnly.setMaxElementsInMemory(ONE_ELEMENT_ONLY_MAX_ELEMENTS_IN_MEMORY);\n        cacheWithOneElementInMemoryOnly.setEternal(false);\n        configurationsList.add(cacheWithOneElementInMemoryOnly);\n\n        final CacheConfiguration offHeapCache = new CacheConfiguration();\n        offHeapCache.setName(OFF_HEAP_CACHE);\n        offHeapCache.setTimeToLiveSeconds(1);\n        offHeapCache.setMaxElementsInMemory(10);\n        offHeapCache.setOffHeapSizeMB(1); // 1MB off-heap\n        offHeapCache.setEternal(false);\n        configurationsList.add(offHeapCache);\n\n        final CacheConfiguration largeOffHeapCache = new CacheConfiguration();\n        largeOffHeapCache.setName(LARGE_OFF_HEAP_CACHE);\n        largeOffHeapCache.setTimeToLiveSeconds(60);\n        largeOffHeapCache.setMaxElementsInMemory(100);\n        largeOffHeapCache.setOffHeapSizeMB(10); // 10MB off-heap\n        largeOffHeapCache.setEternal(false);\n        configurationsList.add(largeOffHeapCache);\n\n        return new EhCacheCacheService(configurationsList, new CacheConfiguration());\n    }\n\n    @Test\n    public void cache_should_expire_entries_after_short_timeout() throws Exception {\n        final String key = \"shortTimeout\";\n        cacheService.store(SOME_DEFAULT_CACHE_NAME, key, new Object());\n        Object object = cacheService.get(SOME_DEFAULT_CACHE_NAME, key);\n        assertNotNull(\"Object should be in cache\", object);\n        Thread.sleep(1020);\n        object = cacheService.get(SOME_DEFAULT_CACHE_NAME, key);\n        assertNull(\"Object should not be in cache any longer\", object);\n    }\n\n    @Test\n    public void eternal_cache_should_not_expire_entries() throws Exception {\n        final String key = \"eternalCacheTest\";\n        final Object value = new Object();\n        cacheService.store(ETERNAL_CACHE, key, value);\n        Object object = cacheService.get(ETERNAL_CACHE, key);\n        assertNotNull(\"Object should be in cache\", object);\n        Thread.sleep(1020);\n        object = cacheService.get(ETERNAL_CACHE, key);\n        assertEquals(\"Object should still be in cache\", value, object);\n    }\n\n    @Test\n    public void cache_store_should_log_when_cache_not_found() throws Exception {\n        // given\n        final String key = \"defaultTimeout\";\n        final String anotherCacheName = \"Should_use_default_config\";\n\n        // when\n        final String log = tapSystemOut(() -> cacheService.store(anotherCacheName, key, \"\"));\n\n        // then\n        assertThat(log)\n                .containsPattern(\"WARN.*No specific cache configuration found for cache 'Should_use_default_config'\");\n\n    }\n\n    @Test\n    public void cache_should_store_simple_object_and_increase_size() throws SCacheException {\n        final int cacheSize = cacheService.getCacheSize(TEST1);\n        final String myObject = \"testObject\";\n        cacheService.store(TEST1, \"test\", myObject);\n        assertEquals(\"cache size did not increased\", cacheSize + 1, cacheService.getCacheSize(TEST1));\n    }\n\n    @Test\n    public void cache_should_retrieve_stored_simple_object() throws SCacheException {\n        final String myObject = \"testObject\";\n        cacheService.store(TEST1, \"test\", myObject);\n        final String inObject = (String) cacheService.get(TEST1, \"test\");\n        assertEquals(\"we didn't retrieve the same object\", myObject, inObject);\n    }\n\n    @Test\n    public void cache_should_store_complex_object_and_increase_size() throws SCacheException {\n        final int cacheSize = cacheService.getCacheSize(TEST1);\n        final ArrayList<Map<String, String>> list = new ArrayList<>();\n        final HashMap<String, String> map = new HashMap<>();\n        map.put(\"bpm\", \"bonita\");\n        list.add(map);\n        cacheService.store(TEST1, \"complex\", list);\n        assertEquals(\"cache size did not increased\", cacheSize + 1, cacheService.getCacheSize(TEST1));\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Test\n    public void cache_should_retrieve_stored_complex_object() throws SCacheException {\n        final ArrayList<Map<String, String>> list = new ArrayList<>();\n        final HashMap<String, String> map = new HashMap<>();\n        map.put(\"bpm\", \"bonita\");\n        list.add(map);\n        cacheService.store(TEST1, \"complex\", list);\n        final Object object = cacheService.get(TEST1, \"complex\");\n        assertNotNull(\"the object does not exists\", object);\n        assertEquals(\"Not the same object\", \"bonita\", ((ArrayList<Map<String, String>>) object).get(0).get(\"bpm\"));\n    }\n\n    @Test\n    public void cache_should_be_cleared_when_clear_called() throws SCacheException {\n        cacheService.store(TEST1, \"test1\", \"test1 value\");\n        assertTrue(\"cache was not empty\", cacheService.getCacheSize(TEST1) > 0);\n        cacheService.clear(TEST1);\n        assertEquals(\"cache was not cleared\", 0, cacheService.getCacheSize(TEST1));\n\n    }\n\n    @Test\n    public void all_caches_should_be_cleared_when_clear_all_called() throws SCacheException {\n        cacheService.store(TEST1, \"test1\", \"test1\");\n        cacheService.store(TEST2, \"test2\", \"test2\");\n        assertTrue(cacheService.getCacheSize(TEST1) > 0);\n        assertTrue(cacheService.getCacheSize(TEST2) > 0);\n\n        cacheService.clearAll();\n\n        assertEquals(\"TEST1 was not cleared\", 0, cacheService.getCacheSize(TEST1));\n        assertEquals(\"TEST2 was not cleared\", 0, cacheService.getCacheSize(TEST2));\n\n    }\n\n    @Test\n    public void storing_null_values_should_fail() {\n        assertThrows(SCacheException.class, () -> cacheService.store(TEST1, \"test2\", null));\n    }\n\n    @Test\n    public void cache_should_not_duplicate_when_storing_same_item_twice() throws SCacheException {\n        cacheService.store(TEST1, \"sameItem\", \"value\");\n        assertEquals(\"first element not added\", 1, cacheService.getCacheSize(TEST1));\n        cacheService.store(TEST1, \"sameItem\", \"value\");\n        assertEquals(\"element added twice\", 1, cacheService.getCacheSize(TEST1));\n    }\n\n    @Test\n    public void cache_should_update_element_value_when_key_already_exists() throws SCacheException {\n        cacheService.store(TEST1, \"sameItem2\", \"value1\");\n        assertEquals(\"first element not added\", 1, cacheService.getCacheSize(TEST1));\n        cacheService.store(TEST1, \"sameItem2\", \"value2\");\n        assertEquals(\"element added 2 times\", 1, cacheService.getCacheSize(TEST1));\n        assertEquals(\"element was not updated\", \"value2\", cacheService.get(TEST1, \"sameItem2\"));\n    }\n\n    @Test\n    public void store_items_with_overflow_should_evict_old_items() throws SCacheException, InterruptedException {\n        for (int i = 0; i < 2; i++) {\n            cacheService.store(ONE_ELEMENT_IN_MEMORY_ONLY, \"testLotOfItems\" + i, \"value\" + i);\n            Thread.sleep(5); // to make sure the Least Recently Used is evicted first\n        }\n\n        assertEquals(\"Not all elements were added with the overflow\", ONE_ELEMENT_ONLY_MAX_ELEMENTS_IN_MEMORY,\n                cacheService.getCacheSize(ONE_ELEMENT_IN_MEMORY_ONLY));\n        assertThat(cacheService.get(ONE_ELEMENT_IN_MEMORY_ONLY, \"testLotOfItems1\")).isEqualTo(\"value1\");\n    }\n\n    @Test\n    public void cache_should_evict_items_when_memory_limit_reached() throws SCacheException {\n        final int j = 2;\n        for (int i = 0; i < j; i++) {\n            cacheService.store(ONE_ELEMENT_IN_MEMORY_ONLY, \"testLotOfItems\" + i, \"value\" + i);\n        }\n\n        assertEquals(\"Too many elements added although limited memory.\", 1,\n                cacheService.getCacheSize(ONE_ELEMENT_IN_MEMORY_ONLY));\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Test\n    public void cache_should_store_objects_by_reference() throws SCacheException {\n        final ArrayList<String> list = new ArrayList<>();\n        cacheService.store(TEST2, \"mylist\", list);\n        list.add(\"kikoo\");\n        final ArrayList<String> cachedList = (ArrayList<String>) cacheService.get(TEST2, \"mylist\");\n        assertEquals(\"object was copied in cached\", 1, cachedList.size());\n        assertEquals(\"'kikoo' should be in cache\", \"kikoo\", cachedList.get(0));\n    }\n\n    @Test\n    public void cache_should_return_all_stored_keys() throws SCacheException {\n        final List<?> keys = cacheService.getKeys(TEST1);\n        assertFalse(keys.contains(\"aKeyThatMustBeHere\"));\n        final int cacheKeySize = keys.size();\n        cacheService.store(TEST1, \"aKeyThatMustBeHere\", \"value1\");\n        final List<?> keys2 = cacheService.getKeys(TEST1);\n        assertEquals(cacheKeySize + 1, keys2.size());\n        assertTrue(keys2.contains(\"aKeyThatMustBeHere\"));\n    }\n\n    @Test\n    public void remove_should_delete_existing_key() throws SCacheException {\n        final String key = \"keyToRemove\";\n        final String value = \"valueToRemove\";\n\n        // Store the key\n        cacheService.store(TEST1, key, value);\n        assertEquals(\"Value should be in cache\", value, cacheService.get(TEST1, key));\n        final int initialSize = cacheService.getCacheSize(TEST1);\n\n        // Remove the key\n        final boolean removed = cacheService.remove(TEST1, key);\n\n        // Verify removal\n        assertTrue(\"remove() should return true for existing key\", removed);\n        assertNull(\"Key should no longer be in cache\", cacheService.get(TEST1, key));\n        assertEquals(\"Cache size should decrease by 1\", initialSize - 1, cacheService.getCacheSize(TEST1));\n    }\n\n    @Test\n    public void remove_should_return_false_for_non_existing_key() throws SCacheException {\n        final String key = \"nonExistentKey\";\n\n        // Try to remove a key that doesn't exist\n        final boolean removed = cacheService.remove(TEST1, key);\n\n        // Verify it returns false\n        assertFalse(\"remove() should return false for non-existent key\", removed);\n    }\n\n    @Test\n    public void remove_should_not_affect_other_keys() throws SCacheException {\n        // Store multiple keys\n        cacheService.store(TEST1, \"key1\", \"value1\");\n        cacheService.store(TEST1, \"key2\", \"value2\");\n        cacheService.store(TEST1, \"key3\", \"value3\");\n\n        // Remove one key\n        cacheService.remove(TEST1, \"key2\");\n\n        // Verify other keys are still present\n        assertEquals(\"key1 should still be in cache\", \"value1\", cacheService.get(TEST1, \"key1\"));\n        assertNull(\"key2 should be removed\", cacheService.get(TEST1, \"key2\"));\n        assertEquals(\"key3 should still be in cache\", \"value3\", cacheService.get(TEST1, \"key3\"));\n    }\n\n    @Test\n    public void remove_should_work_on_different_caches() throws SCacheException {\n        final String key = \"sharedKey\";\n\n        // Store same key in two different caches\n        cacheService.store(TEST1, key, \"value1\");\n        cacheService.store(TEST2, key, \"value2\");\n\n        // Remove from TEST1 only\n        cacheService.remove(TEST1, key);\n\n        // Verify removal only affected TEST1\n        assertNull(\"Key should be removed from TEST1\", cacheService.get(TEST1, key));\n        assertEquals(\"Key should still exist in TEST2\", \"value2\", cacheService.get(TEST2, key));\n    }\n\n    // Off-heap storage tests\n\n    @Test\n    public void offHeapCache_should_store_and_retrieve_simple_objects() throws SCacheException {\n        final String value = \"offHeapTestValue\";\n        cacheService.store(OFF_HEAP_CACHE, \"key1\", value);\n\n        final String retrieved = (String) cacheService.get(OFF_HEAP_CACHE, \"key1\");\n        assertEquals(\"Should retrieve value from off-heap cache\", value, retrieved);\n    }\n\n    @Test\n    public void offHeapCache_should_store_and_retrieve_complex_objects() throws SCacheException {\n        final ArrayList<Map<String, String>> list = new ArrayList<>();\n        final HashMap<String, String> map = new HashMap<>();\n        map.put(\"engine\", \"bonita\");\n        map.put(\"feature\", \"off-heap\");\n        list.add(map);\n\n        cacheService.store(OFF_HEAP_CACHE, \"complexKey\", list);\n\n        @SuppressWarnings(\"unchecked\")\n        final ArrayList<Map<String, String>> retrieved = (ArrayList<Map<String, String>>) cacheService\n                .get(OFF_HEAP_CACHE, \"complexKey\");\n        assertNotNull(\"Complex object should be retrieved from off-heap cache\", retrieved);\n        assertEquals(\"bonita\", retrieved.get(0).get(\"engine\"));\n        assertEquals(\"off-heap\", retrieved.get(0).get(\"feature\"));\n    }\n\n    @Test\n    public void offHeapCache_should_handle_overflow_from_heap_to_offheap() throws SCacheException {\n        // Fill heap (10 elements)\n        for (int i = 0; i < 10; i++) {\n            cacheService.store(OFF_HEAP_CACHE, \"heapKey\" + i, \"heapValue\" + i);\n        }\n\n        // Add more elements that should overflow to off-heap\n        for (int i = 10; i < 20; i++) {\n            cacheService.store(OFF_HEAP_CACHE, \"overflowKey\" + i, \"overflowValue\" + i);\n        }\n\n        // Verify all elements are still accessible (some in heap, some in off-heap)\n        for (int i = 0; i < 20; i++) {\n            final String key = i < 10 ? \"heapKey\" + i : \"overflowKey\" + i;\n            final String expectedValue = i < 10 ? \"heapValue\" + i : \"overflowValue\" + i;\n            final Object retrieved = cacheService.get(OFF_HEAP_CACHE, key);\n            assertThat(retrieved).as(\"Element \" + key + \" should be accessible\").isEqualTo(expectedValue);\n        }\n    }\n\n    @Test\n    public void offHeapCache_should_evict_elements_when_both_tiers_full() throws SCacheException {\n        // Fill both heap and off-heap by adding many elements\n        // Heap: 10 elements, Off-heap: 1MB (can hold many more serialized strings)\n        final int totalElements = 100;\n        for (int i = 0; i < totalElements; i++) {\n            cacheService.store(OFF_HEAP_CACHE, \"key\" + i, \"value\" + i);\n        }\n\n        // Cache should have evicted some elements based on LRU policy\n        final int cacheSize = cacheService.getCacheSize(OFF_HEAP_CACHE);\n        assertThat(cacheSize).isGreaterThan(0).isLessThanOrEqualTo(totalElements);\n    }\n\n    @Test\n    public void offHeapCache_should_respect_ttl() throws Exception {\n        final String key = \"ttlKey\";\n        final String value = \"ttlValue\";\n\n        cacheService.store(OFF_HEAP_CACHE, key, value);\n        assertNotNull(\"Value should be in cache initially\", cacheService.get(OFF_HEAP_CACHE, key));\n\n        // Wait for TTL to expire (OFF_HEAP_CACHE has 1 second TTL)\n        Thread.sleep(1200);\n\n        assertNull(\"Value should be evicted after TTL\", cacheService.get(OFF_HEAP_CACHE, key));\n    }\n\n    @Test\n    public void largeOffHeapCache_should_store_many_elements() throws SCacheException {\n        final int elementCount = 1000;\n\n        for (int i = 0; i < elementCount; i++) {\n            cacheService.store(LARGE_OFF_HEAP_CACHE, \"key\" + i, \"value\" + i);\n        }\n\n        // Verify cache size\n        final int cacheSize = cacheService.getCacheSize(LARGE_OFF_HEAP_CACHE);\n        assertThat(cacheSize).isGreaterThan(0);\n\n        // Spot check some elements\n        assertEquals(\"value0\", cacheService.get(LARGE_OFF_HEAP_CACHE, \"key0\"));\n        assertEquals(\"value500\", cacheService.get(LARGE_OFF_HEAP_CACHE, \"key500\"));\n        assertEquals(\"value999\", cacheService.get(LARGE_OFF_HEAP_CACHE, \"key999\"));\n    }\n\n    @Test\n    public void offHeapCache_should_handle_updates() throws SCacheException {\n        // Ensure cache is empty before starting this test\n        cacheService.clear(OFF_HEAP_CACHE);\n\n        final String key = \"updateKey\";\n        cacheService.store(OFF_HEAP_CACHE, key, \"initialValue\");\n        assertEquals(\"initialValue\", cacheService.get(OFF_HEAP_CACHE, key));\n\n        cacheService.store(OFF_HEAP_CACHE, key, \"updatedValue\");\n        assertEquals(\"Value should be updated in off-heap cache\", \"updatedValue\",\n                cacheService.get(OFF_HEAP_CACHE, key));\n\n        // Cache size should be small (update, not large insert)\n        // Note: With off-heap, statistics may temporarily show 2 if entry moves between tiers\n        int size = cacheService.getCacheSize(OFF_HEAP_CACHE);\n        assertThat(size).as(\"Cache size after update\").isLessThanOrEqualTo(2);\n    }\n\n    @Test\n    public void offHeapCache_should_be_cleared() throws SCacheException {\n        // Add elements\n        for (int i = 0; i < 5; i++) {\n            cacheService.store(OFF_HEAP_CACHE, \"clearKey\" + i, \"clearValue\" + i);\n        }\n\n        assertThat(cacheService.getCacheSize(OFF_HEAP_CACHE)).isGreaterThan(0);\n\n        // Clear cache\n        cacheService.clear(OFF_HEAP_CACHE);\n\n        assertEquals(\"Off-heap cache should be empty after clear\", 0,\n                cacheService.getCacheSize(OFF_HEAP_CACHE));\n    }\n\n    @Test\n    public void offHeapCache_should_store_serializable_objects() throws SCacheException {\n        // Test with a custom serializable object\n        final HashMap<String, Object> complexObject = new HashMap<>();\n        complexObject.put(\"id\", 12345L);\n        complexObject.put(\"name\", \"Process Definition\");\n        complexObject.put(\"version\", \"1.0\");\n        complexObject.put(\"data\", new ArrayList<>(List.of(\"item1\", \"item2\", \"item3\")));\n\n        cacheService.store(OFF_HEAP_CACHE, \"serializableKey\", complexObject);\n\n        @SuppressWarnings(\"unchecked\")\n        final HashMap<String, Object> retrieved = (HashMap<String, Object>) cacheService.get(OFF_HEAP_CACHE,\n                \"serializableKey\");\n        assertNotNull(\"Serializable object should be retrieved\", retrieved);\n        assertEquals(12345L, retrieved.get(\"id\"));\n        assertEquals(\"Process Definition\", retrieved.get(\"name\"));\n        assertEquals(\"1.0\", retrieved.get(\"version\"));\n        @SuppressWarnings(\"unchecked\")\n        final ArrayList<String> data = (ArrayList<String>) retrieved.get(\"data\");\n        assertEquals(3, data.size());\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-cache/src/test/java/org/bonitasoft/engine/cache/ehcache/EhCacheCacheServiceTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.cache.ehcache;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.when;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.cache.CacheConfiguration;\nimport org.bonitasoft.engine.cache.SCacheException;\nimport org.ehcache.Cache;\nimport org.ehcache.CacheManager;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class EhCacheCacheServiceTest {\n\n    private final List<CacheConfiguration> cacheConfigurations = Collections.emptyList();\n\n    @Mock\n    private CacheManager cacheManager;\n\n    @Mock\n    private Cache<Object, Object> cache;\n\n    private EhCacheCacheService cacheService;\n\n    @Before\n    public void setup() {\n        // Create a valid default cache configuration for Ehcache 3\n        CacheConfiguration defaultCacheConfiguration = new CacheConfiguration();\n        defaultCacheConfiguration.setMaxElementsInMemory(1_000);\n\n        cacheService = new EhCacheCacheService(cacheConfigurations, defaultCacheConfiguration) {\n\n            @Override\n            public synchronized void start() {\n                // Mock the cache manager but keep the real statistics service initialization\n                statisticsService = new org.ehcache.core.internal.statistics.DefaultStatisticsService();\n                cacheManager = EhCacheCacheServiceTest.this.cacheManager;\n            }\n        };\n    }\n\n    @Test\n    public void should_getKeys_return_empty_list_when_cache_manager_is_null() throws Exception {\n        final List<Object> keys = cacheService.getKeys(\"unknownCache\");\n\n        assertThat(keys).isEmpty();\n    }\n\n    @Test\n    public void should_getKeys_return_empty_list_when_cache_manager_have_no_cache() throws Exception {\n        cacheService.start();\n\n        final List<Object> keys = cacheService.getKeys(\"unknownCache\");\n\n        assertThat(keys).isEmpty();\n    }\n\n    @Test\n    public void should_getCacheSize_return_zero_when_cache_manager_is_null() throws Exception {\n        final int size = cacheService.getCacheSize(\"unknownCache\");\n\n        assertThat(size).isZero();\n    }\n\n    @Test\n    public void should_getCacheSize_return_zero_when_cache_does_not_exist() throws Exception {\n        cacheService.start();\n        when(cacheManager.getCache(eq(\"unknownCache\"), any(), any())).thenReturn(null);\n\n        final int size = cacheService.getCacheSize(\"unknownCache\");\n\n        assertThat(size).isZero();\n    }\n\n    @Test(expected = SCacheException.class)\n    public void should_getCacheSize_throw_SCacheException_when_cache_is_not_alive() throws Exception {\n        cacheService.start();\n        when(cacheManager.getCache(eq(\"testCache\"), any(), any())).thenReturn(cache);\n        when(cache.iterator()).thenThrow(new IllegalStateException(\"Cache is not alive\"));\n\n        cacheService.getCacheSize(\"testCache\");\n    }\n\n    @Test(expected = SCacheException.class)\n    public void should_getCacheSize_throw_SCacheException_on_runtime_exception() throws Exception {\n        cacheService.start();\n        when(cacheManager.getCache(eq(\"testCache\"), any(), any())).thenReturn(cache);\n        when(cache.iterator()).thenThrow(new RuntimeException(\"Unexpected error\"));\n\n        cacheService.getCacheSize(\"testCache\");\n    }\n\n    @Test\n    public void should_getCacheSize_use_statistics_when_available() throws Exception {\n        // Note: This test verifies that statistics are enabled\n        // The actual CacheServiceTest provides integration-level validation\n        // that the cache size is correctly retrieved\n        cacheService.start();\n\n        // Verify that statisticsService was initialized\n        assertThat(cacheService.statisticsService).as(\"StatisticsService should be initialized\").isNotNull();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-cache/src/test/resources/logback-test.xml",
    "content": "<configuration debug=\"false\" scan=\"false\">\n\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->\n        <encoder>\n            <pattern>|%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <logger name=\"org.bonitasoft\" level=\"INFO\" />\n\n    <root level=\"WARN\">\n        <appender-ref ref=\"STDOUT\" />\n    </root>\n\n</configuration>\n"
  },
  {
    "path": "services/bonita-classloader/build.gradle",
    "content": "\n\ndependencies {\n    api project(':services:bonita-commons')\n    api project(':services:bonita-events')\n    api project(':bpm:bonita-core:bonita-home-server')\n    api project(':services:bonita-transaction')\n    api project(':services:bonita-builder')\n    api project(':services:bonita-persistence')\n    api project(':services:bonita-log')\n    api libs.commonsIO\n    // Dependency on javax.annotations as it is not provided anymore in Java 11:\n    api(libs.javaxAnnotations)\n\n    testImplementation libs.mockitoCore\n    testImplementation libs.assertj\n    testImplementation libs.logback\n    testImplementation libs.systemRules\n\n    annotationProcessor libs.lombok\n    compileOnly libs.lombok\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/classloader/BonitaClassLoader.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.classloader;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.time.Instant;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.StringJoiner;\nimport java.util.UUID;\nimport java.util.stream.Collectors;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.io.FileUtils;\nimport org.bonitasoft.engine.commons.ExceptionUtils;\nimport org.bonitasoft.engine.data.instance.model.impl.XStreamFactory;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Charles Souillard\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\n@Slf4j\npublic class BonitaClassLoader extends URLClassLoader {\n\n    private final ClassLoaderIdentifier id;\n    protected Map<String, File> nonJarResources;\n    // that directory contains the jars and resources given in the constructor\n    private final File temporaryDirectory;\n    private boolean isActive = true;\n    private final Instant creationTime = Instant.now();\n    private final String uuid = generateUUID();\n    private final Set<BonitaClassLoader> children = new HashSet<>();\n\n    BonitaClassLoader(ClassLoaderIdentifier id, ClassLoader parent, Set<File> jars, Map<String, File> nonJarResources,\n            File temporaryDirectory) {\n        super(id.toString(), jars.stream().map(BonitaClassLoader::toURL).toArray(URL[]::new),\n                parent);\n        this.id = id;\n        //TODO: These non-jar resources might be added along with jars without having to do special handling\n        this.nonJarResources = new HashMap<>(nonJarResources);\n        this.temporaryDirectory = temporaryDirectory;\n        if (parent instanceof BonitaClassLoader) {\n            //The parent is not a BonitaClassloader when we are on the Global classloader\n            ((BonitaClassLoader) parent).children.add(this);\n            log.debug(\"Classloader {} added as a child of {}\", this, parent);\n        }\n        if (log.isDebugEnabled()) {\n            log.debug(\"Classloader {} created with \\n jars: {}, \\n nonJarResources: {}\", this,\n                    jars.stream().map(File::getPath).collect(Collectors.joining(\", \")),\n                    nonJarResources.values().stream().map(File::getPath).collect(Collectors.joining(\", \")));\n        }\n    }\n\n    private static String generateUUID() {\n        return UUID.randomUUID().toString();\n    }\n\n    private static URL toURL(File f) {\n        try {\n            return f.toURI().toURL();\n        } catch (MalformedURLException e) {\n            throw new IllegalArgumentException(e);\n        }\n    }\n\n    @Override\n    public InputStream getResourceAsStream(final String name) {\n        InputStream is = getInternalInputStream(name);\n        if (is == null && name.length() > 0 && name.charAt(0) == '/') {\n            is = getInternalInputStream(name.substring(1));\n        }\n        return is;\n    }\n\n    private InputStream getInternalInputStream(final String name) {\n        File classData = nonJarResources.get(name);\n        if (classData != null) {\n            try {\n                return new FileInputStream(classData);\n            } catch (FileNotFoundException e) {\n                throw new RuntimeException(e);\n            }\n        }\n        return super.getResourceAsStream(name);\n    }\n\n    @Override\n    protected Class<?> loadClass(final String name, final boolean resolve) throws ClassNotFoundException {\n        Class<?> c = null;\n        c = findLoadedClass(name);\n        if (c == null) {\n            try {\n                c = findClass(name);\n            } catch (final ClassNotFoundException e) {\n                // ignore\n            } catch (final LinkageError le) {\n                // might be because of a duplicate loading (concurrency loading), retry to find it one time See BS-2483\n                c = findLoadedClass(name);\n                if (c == null) {\n                    // was not because of duplicate loading: throw the exception\n                    throw le;\n                }\n            }\n        }\n        if (c == null) {\n            c = getParent().loadClass(name);\n        }\n        if (resolve) {\n            resolveClass(c);\n        }\n        return c;\n    }\n\n    public void destroy() {\n        XStreamFactory.remove(this);\n        ClassLoader parent = getParent();\n        if (parent instanceof BonitaClassLoader) {\n            //The parent is not a BonitaClassloader when we are on the Global classloader\n            ((BonitaClassLoader) parent).children.remove(this);\n        }\n        try {\n            super.close();\n        } catch (IOException e) {\n            log.warn(\"Unable to close the classloader {}. Some file might still be present in {}. Cause {}\", id,\n                    temporaryDirectory.getAbsolutePath(),\n                    ExceptionUtils.printLightWeightStacktrace(e));\n            log.debug(\"Full cause:\", e);\n        }\n        FileUtils.deleteQuietly(temporaryDirectory);\n        isActive = false;\n        log.debug(\"Destroyed {}\", this);\n    }\n\n    public boolean isDestroyed() {\n        return !isActive;\n    }\n\n    public ClassLoaderIdentifier getIdentifier() {\n        return id;\n    }\n\n    public File getTemporaryFolder() {\n        return temporaryDirectory;\n    }\n\n    public boolean hasChildren() {\n        return !children.isEmpty();\n    }\n\n    public Set<BonitaClassLoader> getChildren() {\n        return Collections.unmodifiableSet(new HashSet<>(children));\n    }\n\n    @Override\n    public String toString() {\n        return new StringJoiner(\", \", BonitaClassLoader.class.getSimpleName() + \"[\", \"]\")\n                .add(\"id=\" + id)\n                .add(\"isActive=\" + isActive)\n                .add(\"creationTime=\" + creationTime)\n                .add(\"uuid='\" + uuid + \"'\")\n                .add(\"children=\" + children.stream().map(BonitaClassLoader::getIdentifier)\n                        .map(ClassLoaderIdentifier::toString).collect(Collectors.joining(\", \", \"[\", \"]\")))\n                .toString();\n    }\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/classloader/BonitaClassLoaderFactory.java",
    "content": "/**\n * Copyright (C) 2021 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.classloader;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URI;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.bonitasoft.engine.commons.io.IOUtil;\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\nimport org.bonitasoft.engine.home.BonitaResource;\n\npublic class BonitaClassLoaderFactory {\n\n    private static final Pattern jarMatcher = Pattern.compile(\".*\\\\.jar\");\n\n    static BonitaClassLoader createClassLoader(Stream<BonitaResource> resources, ClassLoaderIdentifier id,\n            URI temporaryDirectoryUri,\n            ClassLoader parent) throws IOException {\n        File temporaryDirectory = createTemporaryDirectory(temporaryDirectoryUri);\n        Map<String, File> allFiles = writeResourcesOnFileSystem(resources, temporaryDirectory);\n        Set<File> jars = allFiles.entrySet().stream().filter(u -> jarMatcher.matcher(u.getKey()).matches())\n                .map(Map.Entry::getValue).collect(Collectors.toSet());\n        Map<String, File> nonJarResources = allFiles.entrySet().stream()\n                .filter(u -> !jarMatcher.matcher(u.getKey()).matches())\n                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));\n        return new BonitaClassLoader(id, parent, jars, nonJarResources, temporaryDirectory);\n    }\n\n    private static File createTemporaryDirectory(URI temporaryDirectoryUri) throws IOException {\n        Path temporaryDirectory = new File(temporaryDirectoryUri).toPath();\n        if (!Files.exists(temporaryDirectory)) {\n            Files.createDirectory(temporaryDirectory);\n        }\n        return Files.createTempDirectory(temporaryDirectory, \"engine-classloader\").toFile();\n    }\n\n    private static Map<String, File> writeResourcesOnFileSystem(final Stream<BonitaResource> resources,\n            File temporaryDirectory) {\n        return resources.map(resource -> {\n            try {\n                return writeResource(resource, temporaryDirectory);\n            } catch (final IOException e) {\n                throw new BonitaRuntimeException(e);\n            }\n        }).collect(Collectors.toMap(Pair::getKey, Pair::getValue));\n    }\n\n    static Pair<String, File> writeResource(BonitaResource resource, File temporaryDirectory) throws IOException {\n        String name = resource.getName();\n        int i = name.lastIndexOf(\".\");\n        final File file = File.createTempFile(i < 3 ? \"tmp\" : name.substring(0, i), i < 3 ? name : name.substring(i),\n                temporaryDirectory);\n        IOUtil.write(file, resource.getContent());\n        return Pair.of(name, file);\n    }\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/classloader/ClassLoaderIdentifier.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.classloader;\n\nimport java.io.Serializable;\n\nimport lombok.AccessLevel;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport org.bonitasoft.engine.dependency.model.ScopeType;\n\n/**\n * <pre>\n * -            APPLICATION\n * -                 |\n * -               GLOBAL\n * -                 |\n * -      ------- TENANT ------\n * -     |          |         |\n * - PROCESS1 - PROCESS2 - PROCESS3\n * </pre>\n *\n * @author Baptiste Mesta\n * @author Emmanuel Duchastenier\n */\n@Data\n@AllArgsConstructor(access = AccessLevel.PRIVATE)\npublic class ClassLoaderIdentifier implements Serializable {\n\n    public static final ScopeType GLOBAL_TYPE = ScopeType.GLOBAL;\n    public static final long GLOBAL_ID = -1;\n    protected static final int FIXED_TENANT_ID = 1;\n    /**\n     * The APPLICATION classloader is the parent classloader of the GLOBAL classloader. It the one in which bonita is\n     * bootstrapped\n     */\n    public static final ClassLoaderIdentifier APPLICATION = identifier(null, Long.MIN_VALUE);\n    /**\n     * The GLOBAL classloader is the unique one at platform level\n     */\n    public static final ClassLoaderIdentifier GLOBAL = identifier(GLOBAL_TYPE, GLOBAL_ID);\n    public static final ClassLoaderIdentifier TENANT = identifier(ScopeType.TENANT, FIXED_TENANT_ID);\n\n    private ScopeType type;\n    private long id;\n\n    public static ClassLoaderIdentifier identifier(ScopeType scopeType, long id) {\n        return new ClassLoaderIdentifier(scopeType, id);\n    }\n\n    @Override\n    public String toString() {\n        if (this.equals(GLOBAL)) {\n            return \"GLOBAL\";\n        } else if (this.type == ScopeType.TENANT) {\n            return \"TENANT\";\n        } else\n            return type.name() + ':' + id;\n    }\n\n    public boolean isTenantClassloader() {\n        return ScopeType.TENANT.equals(type);\n    }\n\n    public boolean isGlobalClassloader() {\n        return ScopeType.GLOBAL.equals(type);\n    }\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/classloader/ClassLoaderService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.classloader;\n\nimport org.bonitasoft.engine.commons.PlatformLifecycleService;\nimport org.bonitasoft.engine.dependency.impl.TenantDependencyService;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n * @since 6.0\n */\npublic interface ClassLoaderService extends PlatformLifecycleService {\n\n    void registerDependencyService(TenantDependencyService tenantDependencyService);\n\n    /**\n     * Get the local ClassLoader for the given type and id.\n     * If no ClassLoader already exists, a new one is created and initialized.\n     * This initialization is executed in a <b>different thread/transaction</b>.\n     * It eagerly initialize parent classloaders.\n     *\n     * @return the local ClassLoader for the given type and id\n     * @throws SClassLoaderException Error thrown if it's impossible to get a local ClassLoader for the given type and\n     *         id\n     * @param identifier of the classloader to refresh\n     */\n    ClassLoader getClassLoader(ClassLoaderIdentifier identifier) throws SClassLoaderException;\n\n    void removeLocalClassloader(ClassLoaderIdentifier identifier) throws SClassLoaderException;\n\n    /**\n     * add listener on a classloader\n     *\n     * @param identifier the classloader id\n     * @param singleClassLoaderListener the listener to add\n     * @return true if the listener was added\n     */\n    boolean addListener(ClassLoaderIdentifier identifier, SingleClassLoaderListener singleClassLoaderListener);\n\n    /**\n     * @param identifier the classloader id\n     * @param singleClassLoaderListener classloader listener to remove\n     * @return true if the listener was removed\n     */\n    boolean removeListener(ClassLoaderIdentifier identifier, SingleClassLoaderListener singleClassLoaderListener);\n\n    void refreshClassLoaderAfterUpdate(ClassLoaderIdentifier identifier) throws SClassLoaderException;\n\n    void refreshClassLoaderOnOtherNodes(ClassLoaderIdentifier identifier) throws SClassLoaderException;\n\n    /**\n     * This method refreshes in the current thread/transaction the classLoader with the given identifier.\n     * Contrary to refreshClassLoaderImmediately, it creates a synchronization that triggers a reload of the classloader\n     * in case the transaction was rolled back, insuring there is no new loaded class in the classloader after the\n     * rollback.\n     * <p>\n     * e.g. If the classloader was set as the current context classloader, it should be reset like this\n     *\n     * <pre>\n     *  {@code\n     * Thread.currentThread().setContextClassLoader(classLoaderService.getLocalClassLoader(identifier));\n     * }\n     * </pre>\n     *\n     * @param identifier of the classloader to refresh\n     */\n    void refreshClassLoaderImmediatelyWithRollback(ClassLoaderIdentifier identifier) throws SClassLoaderException;\n\n    /**\n     * This method refreshes in the current thread/transaction the classLoader with the given identifier.\n     * It eagerly initializes parents classloaders.\n     * <p>\n     * A new classloader will be created. In order to use the new classloader, references to the old one should be\n     * updated.\n     * <p>\n     * e.g. If the classloader was set as the current context classloader, it should be reset like this\n     *\n     * <pre>\n     *  {@code\n     * Thread.currentThread().setContextClassLoader(classLoaderService.getLocalClassLoader(identifier));\n     * }\n     * </pre>\n     *\n     * @param identifier of the classloader to refresh\n     */\n    void refreshClassLoaderImmediately(ClassLoaderIdentifier identifier) throws SClassLoaderException;\n\n    void removeRefreshClassLoaderSynchronization();\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/classloader/ClassLoaderServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.classloader;\n\nimport java.io.IOException;\nimport java.net.URI;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.TimeoutException;\nimport java.util.function.Function;\nimport java.util.stream.Stream;\n\nimport javax.transaction.Status;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.commons.NullCheckingUtil;\nimport org.bonitasoft.engine.dependency.SDependencyException;\nimport org.bonitasoft.engine.dependency.impl.PlatformDependencyService;\nimport org.bonitasoft.engine.dependency.impl.TenantDependencyService;\nimport org.bonitasoft.engine.dependency.model.ScopeType;\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.events.model.SEvent;\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\nimport org.bonitasoft.engine.home.BonitaHomeServer;\nimport org.bonitasoft.engine.home.BonitaResource;\nimport org.bonitasoft.engine.service.BroadcastService;\nimport org.bonitasoft.engine.service.TaskResult;\nimport org.bonitasoft.engine.transaction.BonitaTransactionSynchronization;\nimport org.bonitasoft.engine.transaction.STransactionNotFoundException;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\n@Slf4j\n@Component(\"classLoaderService\") //id  used by RefreshClassLoaderTask and @InjectedService\npublic class ClassLoaderServiceImpl implements ClassLoaderService {\n\n    private final Object synchroLock = new Object();\n    private final ThreadLocal<RefreshClassloaderSynchronization> currentRefreshTask = new ThreadLocal<>();\n    private final ParentClassLoaderResolver parentClassLoaderResolver;\n    private final Map<ClassLoaderIdentifier, BonitaClassLoader> classLoaders = new ConcurrentHashMap<>();\n    private final Set<PlatformClassLoaderListener> platformClassLoaderListeners = new HashSet<>();\n    private final Map<ClassLoaderIdentifier, Set<SingleClassLoaderListener>> singleClassLoaderListenersMap = Collections\n            .synchronizedMap(new HashMap<>());\n    private boolean shuttingDown = false;\n    private final EventService eventService;\n    private final PlatformDependencyService platformDependencyService;\n    private TenantDependencyService dependencyService;\n    private final UserTransactionService userTransactionService;\n    private final BroadcastService broadcastService;\n    private final ClassLoaderUpdater classLoaderUpdater;\n\n    public ClassLoaderServiceImpl(final ParentClassLoaderResolver parentClassLoaderResolver,\n            @Qualifier(\"platformEventService\") EventService eventService,\n            PlatformDependencyService platformDependencyService,\n            UserTransactionService userTransactionService, BroadcastService broadcastService,\n            ClassLoaderUpdater classLoaderUpdater, List<PlatformClassLoaderListener> platformClassLoaderListeners) {\n        this.parentClassLoaderResolver = parentClassLoaderResolver;\n        this.eventService = eventService;\n        this.platformDependencyService = platformDependencyService;\n        this.userTransactionService = userTransactionService;\n        this.broadcastService = broadcastService;\n        this.classLoaderUpdater = classLoaderUpdater;\n        this.platformClassLoaderListeners.addAll(platformClassLoaderListeners);\n    }\n\n    @Override\n    public void registerDependencyService(TenantDependencyService tenantDependencyService) {\n        dependencyService = tenantDependencyService;\n    }\n\n    private void warnOnShuttingDown(final ClassLoaderIdentifier key) {\n        if (shuttingDown) {\n            log.warn(\"Using local classloader after ClassLoaderService has already shut down: {}\", key);\n        }\n    }\n\n    @Override\n    public BonitaClassLoader getClassLoader(ClassLoaderIdentifier id) {\n        NullCheckingUtil.checkArgsNotNull(id);\n        log.trace(\"Get classloader {}\", id);\n        warnOnShuttingDown(id);\n        // when the classloader is initialized, it is done in another thread/transaction\n        // We might not need to do so.\n        return getOrInitializeClassloader(id, k -> classLoaderUpdater.initializeClassLoader(this, k));\n    }\n\n    public BonitaClassLoader getOrInitializeClassloader(ClassLoaderIdentifier id,\n            Function<ClassLoaderIdentifier, BonitaClassLoader> createClassloaderFunction) {\n        if (!classLoaders.containsKey(id)) {\n            log.trace(\"getOrInitializeClassloader: classloader not found {}, it will be created\", id);\n            //computed before to avoid having nested \"computeIfAbsent\"\n            BonitaClassLoader newClassLoader = createClassloaderFunction.apply(id);\n            BonitaClassLoader classLoader = classLoaders.computeIfAbsent(id, k -> newClassLoader);\n            if (!classLoader.equals(newClassLoader)) {\n                log.debug(\"Due to concurrent initialization, the Classloader created here {} will not be used \" +\n                        \"and will be destroyed. {} is the one that will be used\", newClassLoader, classLoader);\n                newClassLoader.destroy();\n            }\n            return classLoader;\n        } else {\n            return classLoaders.get(id);\n        }\n    }\n\n    ClassLoader getParentClassLoader(ClassLoaderIdentifier identifier) {\n        final ClassLoaderIdentifier parentIdentifier = parentClassLoaderResolver\n                .getParentClassLoaderIdentifier(identifier);\n        if (ClassLoaderIdentifier.APPLICATION.equals(parentIdentifier)) {\n            // Application classloader is the one bootstrapping bonita platform\n            return ClassLoaderServiceImpl.class.getClassLoader();\n        } else {\n            //get or initialize parent in the same thread/transaction\n            return getOrInitializeClassloader(parentIdentifier, k -> {\n                try {\n                    return createClassloader(k);\n                } catch (IOException | SClassLoaderException e) {\n                    throw new BonitaRuntimeException(e);\n                }\n            });\n        }\n    }\n\n    @Override\n    public void removeLocalClassloader(ClassLoaderIdentifier identifier) throws SClassLoaderException {\n        NullCheckingUtil.checkArgsNotNull(identifier);\n        log.debug(\"Removing local classloader with {}\", identifier);\n        BonitaClassLoader localClassLoader = classLoaders.get(identifier);\n        if (localClassLoader != null) {\n            destroyAndRemoveClassLoader(localClassLoader);\n            notifyDestroyed(localClassLoader);\n        }\n    }\n\n    private void destroyAndRemoveClassLoader(BonitaClassLoader localClassLoader) throws SClassLoaderException {\n        if (localClassLoader.hasChildren()) {\n            throw new SClassLoaderException(\n                    \"Unable to delete classloader \" + localClassLoader.getIdentifier() + \" because it has children: \"\n                            + localClassLoader.getChildren());\n        }\n        localClassLoader.destroy();\n        classLoaders.remove(localClassLoader.getIdentifier());\n    }\n\n    private List<BonitaClassLoader> getClassLoaderTreeLeavesFirst(BonitaClassLoader root) {\n        List<BonitaClassLoader> tree = new ArrayList<>();\n        for (BonitaClassLoader child : root.getChildren()) {\n            tree.addAll(getClassLoaderTreeLeavesFirst(child));\n        }\n        tree.add(root);\n        return tree;\n    }\n\n    URI getLocalTemporaryFolder(ClassLoaderIdentifier identifier) throws IOException {\n        if (identifier.isGlobalClassloader() || identifier.isTenantClassloader()) {\n            // For tenant and platform (=GLOBAL), no need to have a sub-folder with the identifier:\n            return BonitaHomeServer.getInstance().getLocalTemporaryFolder(identifier.getType().name());\n        } else {\n            // For process-level classloader, store jar files in sub-folder named with the identifier:\n            return BonitaHomeServer.getInstance().getLocalTemporaryFolder(identifier.getType().name(),\n                    identifier.getId());\n        }\n    }\n\n    BonitaClassLoader createClassloader(ClassLoaderIdentifier id) throws IOException, SClassLoaderException {\n        log.debug(\"Creating classloader {}\", id);\n        BonitaClassLoader classLoader = BonitaClassLoaderFactory.createClassLoader(getDependencies(id), id,\n                getLocalTemporaryFolder(id), getParentClassLoader(id));\n        log.info(\"Created classloader {}: {}\", id, classLoader);\n        return classLoader;\n    }\n\n    @Override\n    public void start() {\n        log.debug(\"Starting classloader service, creating the platform classloader\");\n        shuttingDown = false;\n        //we do not create or destroy the global classloader because it does not point to a bonita classloader\n    }\n\n    @Override\n    public void stop() {\n        log.debug(\"Stopping classloader service, destroying all classloaders\");\n        shuttingDown = true;\n        destroyAllLocalClassLoaders();\n    }\n\n    private void destroyAllLocalClassLoaders() {\n        log.debug(\"Destroying all classloaders\");\n        //remove elements only that don't have children\n        //there is no loop in this so the algorithm finishes\n        BonitaClassLoader global = classLoaders.get(ClassLoaderIdentifier.GLOBAL);\n        if (global == null) {\n            log.debug(\"No ClassLoaders to destroy\");\n            return;\n        }\n        //destroy classloader starting from the leaves to avoid checking for children\n        List<BonitaClassLoader> allClassLoaders = getClassLoaderTreeLeavesFirst(global);\n        for (BonitaClassLoader currentClassLoader : allClassLoaders) {\n            currentClassLoader.destroy();\n            notifyDestroyed(currentClassLoader);\n            if (classLoaders.remove(currentClassLoader.getIdentifier()) == null) {\n                log.warn(\"One classloader of the tree is not present in the list: classloader = {} and list = {}\",\n                        currentClassLoader, classLoaders);\n            }\n        }\n        //the classloader map should be empty at this point. Clearing it to ensure that we never reuse old classloader in that case\n        if (!classLoaders.isEmpty()) {\n\n            log.warn(\"Classloader tree was destroyed but some classloaders were still references in the map {}\",\n                    classLoaders);\n        }\n        classLoaders.clear();\n    }\n\n    @Override\n    public boolean addListener(ClassLoaderIdentifier identifier, SingleClassLoaderListener singleClassLoaderListener) {\n        log.debug(\"Added listener {} on {}\", singleClassLoaderListener, identifier.toString());\n        return getListeners(identifier).add(singleClassLoaderListener);\n    }\n\n    @Override\n    public boolean removeListener(ClassLoaderIdentifier identifier,\n            SingleClassLoaderListener singleClassLoaderListener) {\n        log.debug(\"Removed listener {} on {}\", singleClassLoaderListener, identifier.toString());\n        return getListeners(identifier).remove(singleClassLoaderListener);\n    }\n\n    Set<SingleClassLoaderListener> getListeners(ClassLoaderIdentifier identifier) {\n        return this.singleClassLoaderListenersMap.computeIfAbsent(identifier, k -> new HashSet<>());\n    }\n\n    /**\n     * Notify listeners that the classloader was destroyed\n     * That method do not notify children because we can't destroy a classloader that have children\n     */\n    private void notifyDestroyed(BonitaClassLoader classLoader) {\n        getListeners(classLoader.getIdentifier()).forEach(l -> {\n            log.debug(\"Notify listener that classloader {} was destroyed: {}\", classLoader.getIdentifier(), l);\n            l.onDestroy(classLoader);\n        });\n        platformClassLoaderListeners.forEach(l -> {\n            log.debug(\"Notify listener that platform classloader {} was destroyed: {}\", classLoader.getIdentifier(), l);\n            l.onDestroy(classLoader);\n        });\n    }\n\n    /**\n     * Notify listeners that the classloader was updated\n     * Also notify that children classloader were updated\n     */\n    void notifyUpdated(BonitaClassLoader newClassLoader) {\n        getListeners(newClassLoader.getIdentifier()).forEach(l -> {\n            log.debug(\"Notify listener that classloader {} was updated: {}\", newClassLoader.getIdentifier(), l);\n            l.onUpdate(newClassLoader);\n        });\n        platformClassLoaderListeners.forEach(l -> {\n            log.debug(\"Notify listener that platform classloader {} was updated: {}\", newClassLoader.getIdentifier(),\n                    l);\n            l.onUpdate(newClassLoader);\n        });\n    }\n\n    @Override\n    public void refreshClassLoaderImmediatelyWithRollback(ClassLoaderIdentifier identifier)\n            throws SClassLoaderException {\n        // Register the rollback before refreshing classloader in case refreshClassLoaderImmediately() fails:\n        registerAfterCommitClassloaderUpdate(identifier);\n        refreshClassLoaderImmediately(identifier);\n    }\n\n    @Override\n    public void refreshClassLoaderImmediately(ClassLoaderIdentifier identifier) throws SClassLoaderException {\n        try {\n            log.info(\"Refreshing classloader {}\", identifier.toString());\n            BonitaClassLoader newClassloader = createClassloader(identifier);\n            BonitaClassLoader previous = classLoaders.put(identifier, newClassloader);\n            // Destroy and remove all children classloaders of the `previous` classloader. They need to be recreated\n            if (previous != null) {\n                //destroy classloader starting from the leaves to avoid checking for children\n                for (BonitaClassLoader classLoader : getClassLoaderTreeLeavesFirst(previous)) {\n                    classLoader.destroy();\n                    notifyDestroyed(classLoader);\n                    // remove(key,value) only remove the value if its the one given,\n                    // We do that to avoid removing the one we just added to the map\n                    classLoaders.remove(classLoader.getIdentifier(), classLoader);\n                }\n                log.debug(\"Refreshed classloader {}, {} was replaced by {}\", identifier, previous, classLoaders);\n            } else {\n                log.debug(\"Refreshed classloader {}, There was no classloader, classloader set: {}\", identifier,\n                        classLoaders);\n            }\n            notifyUpdated(newClassloader);\n            final SEvent event = new SEvent(\"ClassLoaderRefreshed\");\n            event.setObject(identifier);\n            eventService.fireEvent(event);\n        } catch (Exception e) {\n            throw new SClassLoaderException(e);\n        }\n    }\n\n    private void registerAfterCommitClassloaderUpdate(ClassLoaderIdentifier identifier) throws SClassLoaderException {\n        try {\n            userTransactionService.registerBonitaSynchronization((BonitaTransactionSynchronization) i -> {\n                if (i != Status.STATUS_COMMITTED) {\n                    log.warn(\n                            \"The transaction was not committed. Refreshing classloader on platform to return to a clean state.\");\n                    classLoaderUpdater.refreshClassloaders(this, Collections.singleton(identifier));\n                }\n            });\n        } catch (STransactionNotFoundException e) {\n            throw new SClassLoaderException(e);\n        }\n    }\n\n    Stream<BonitaResource> getDependencies(ClassLoaderIdentifier identifier) throws SClassLoaderException {\n        Stream<BonitaResource> resources;\n        try {\n            if (ScopeType.GLOBAL == identifier.getType()) {\n                resources = platformDependencyService.getDependenciesResources(identifier.getType(),\n                        identifier.getId());\n            } else {\n                if (dependencyService == null) {\n                    log.warn(\"No dependency service is initialized. Initializing empty classloader\");\n                    return Stream.empty();\n                }\n                resources = dependencyService.getDependenciesResources(identifier.getType(), identifier.getId());\n            }\n        } catch (SDependencyException e) {\n            throw new SClassLoaderException(e);\n        }\n        return resources;\n    }\n\n    @Override\n    public void refreshClassLoaderAfterUpdate(ClassLoaderIdentifier identifier) throws SClassLoaderException {\n        try {\n            registerRefreshOnAllNodes(identifier);\n        } catch (STransactionNotFoundException e) {\n            throw new SClassLoaderException(e);\n        }\n    }\n\n    @Override\n    public void refreshClassLoaderOnOtherNodes(ClassLoaderIdentifier identifier) throws SClassLoaderException {\n        try {\n            userTransactionService\n                    .registerBonitaSynchronization((BonitaTransactionSynchronization) transactionState -> {\n\n                        if (transactionState != Status.STATUS_COMMITTED) {\n                            return;\n                        }\n                        Map<String, TaskResult<Void>> execute;\n                        try {\n                            execute = broadcastService.executeOnOthersAndWait(new RefreshClassLoaderTask(identifier));\n                        } catch (TimeoutException | ExecutionException\n                                | InterruptedException e) {\n                            throw new BonitaRuntimeException(e);\n                        }\n                        for (Map.Entry<String, TaskResult<Void>> resultEntry : execute.entrySet()) {\n                            if (resultEntry.getValue().isError()) {\n                                throw new IllegalStateException(resultEntry.getValue().getThrowable());\n                            }\n                        }\n                    });\n        } catch (STransactionNotFoundException e) {\n            throw new SClassLoaderException(e);\n        }\n    }\n\n    private void registerRefreshOnAllNodes(ClassLoaderIdentifier identifier) throws STransactionNotFoundException {\n        synchronized (synchroLock) {\n            RefreshClassloaderSynchronization refreshTaskSynchronization = currentRefreshTask.get();\n            if (refreshTaskSynchronization == null) {\n                RefreshClassLoaderTask callable = new RefreshClassLoaderTask(identifier);\n                refreshTaskSynchronization = new RefreshClassloaderSynchronization(this, broadcastService, callable,\n                        classLoaderUpdater, identifier);\n                userTransactionService.registerBonitaSynchronization(refreshTaskSynchronization);\n                currentRefreshTask.set(refreshTaskSynchronization);\n            } else {\n                refreshTaskSynchronization.addClassloaderToRefresh(identifier);\n            }\n        }\n    }\n\n    @Override\n    public void removeRefreshClassLoaderSynchronization() {\n        currentRefreshTask.remove();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/classloader/ClassLoaderUpdater.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.classloader;\n\nimport java.util.Set;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.TimeoutException;\n\nimport javax.annotation.PostConstruct;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException;\nimport org.bonitasoft.engine.service.BonitaTaskExecutor;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.stereotype.Component;\n\n/**\n * This class is responsible for triggering the refresh of classloaders in separated threads/transactions\n * It is useful when refreshing classloader at the end of the transaction when one transaction is still active and\n * we need to open a new one.\n */\n@Slf4j\n@Component\nclass ClassLoaderUpdater {\n\n    final static long DEFAULT_CLASSLOADER_INITIALIZATION_TIMEOUT_MINUTES = 5L;\n\n    private final BonitaTaskExecutor bonitaTaskExecutor;\n    private final UserTransactionService userTransactionService;\n\n    @Value(\"${bonita.runtime.classloader.initialization.timeout-minutes:-1}\")\n    private long classLoaderInitializationTimeoutMinutes;\n\n    public ClassLoaderUpdater(BonitaTaskExecutor bonitaTaskExecutor, UserTransactionService userTransactionService) {\n        this.bonitaTaskExecutor = bonitaTaskExecutor;\n        this.userTransactionService = userTransactionService;\n    }\n\n    public void refreshClassloaders(ClassLoaderServiceImpl classLoaderService, Set<ClassLoaderIdentifier> ids) {\n        execute(() -> {\n            for (ClassLoaderIdentifier id : ids) {\n                classLoaderService.refreshClassLoaderImmediately(id);\n            }\n            return null;\n        });\n    }\n\n    BonitaClassLoader initializeClassLoader(ClassLoaderServiceImpl classLoaderService,\n            ClassLoaderIdentifier identifier) {\n        log.debug(\"Request creation of classloader in an other thread: {}. A {} minutes timeout will be used.\",\n                identifier, classLoaderInitializationTimeoutMinutes);\n        return execute(() -> classLoaderService.createClassloader(identifier));\n    }\n\n    private <T> T execute(Callable<T> callable) {\n        Future<T> execute = bonitaTaskExecutor.execute(inTransaction(callable));\n        try {\n            return execute.get(classLoaderInitializationTimeoutMinutes, TimeUnit.MINUTES);\n        } catch (InterruptedException | ExecutionException e) {\n            throw new SBonitaRuntimeException(\"Unable to refresh the classloaders\", e);\n        } catch (TimeoutException toe) {\n            throw new SBonitaRuntimeException(\"Unable to refresh the classloaders within \"\n                    + classLoaderInitializationTimeoutMinutes\n                    + \" minutes. You may want to adjust the timeout using either the \"\n                    + \"bonita.runtime.classloader.initialization.timeout-minutes property or the \"\n                    + \"BONITA_RUNTIME_CLASSLOADER_INITIALIZATION_TIMEOUT_MINUTES environment variable.\"\n                    + \"Make sure the value is within [1..XA_TRANSACTION_TIMEOUT].\",\n                    toe);\n        }\n    }\n\n    private <T> Callable<T> inTransaction(Callable<T> callable) {\n        return () -> userTransactionService.executeInTransaction(callable);\n    }\n\n    @PostConstruct\n    void validateClassLoaderInitializationTimeoutMinutes() {\n        if (classLoaderInitializationTimeoutMinutes <= 0) {\n            classLoaderInitializationTimeoutMinutes = DEFAULT_CLASSLOADER_INITIALIZATION_TIMEOUT_MINUTES;\n        }\n        log.debug(\n                \"Using a timeout of {} minutes for class loader initialization or refresh. This can be configured \"\n                        + \"via the bonita.runtime.classloader.initialization.timeout-minutes property or the \"\n                        + \"BONITA_RUNTIME_CLASSLOADER_INITIALIZATION_TIMEOUT_MINUTES environment variable. \"\n                        + \"Make sure the value is within [1..XA_TRANSACTION_TIMEOUT].\",\n                classLoaderInitializationTimeoutMinutes);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/classloader/ParentClassLoaderResolver.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.classloader;\n\nimport org.bonitasoft.engine.dependency.model.ScopeType;\nimport org.bonitasoft.engine.exception.BonitaRuntimeException;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class ParentClassLoaderResolver {\n\n    /**\n     * @return the key of the parent or null if it is the global\n     */\n    public ClassLoaderIdentifier getParentClassLoaderIdentifier(ClassLoaderIdentifier childId) {\n        if (ScopeType.PROCESS.equals(childId.getType())) {\n            // We should not depend on the session to know what is the parent of a classloader\n            return ClassLoaderIdentifier.TENANT;\n        } else if (ScopeType.TENANT.equals(childId.getType())) {\n            return ClassLoaderIdentifier.GLOBAL;\n        } else if (ClassLoaderIdentifier.GLOBAL.equals(childId)) {\n            return ClassLoaderIdentifier.APPLICATION;\n        } else {\n            throw new BonitaRuntimeException(\"unable to find a parent for type: \" + childId);\n        }\n    }\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/classloader/PlatformClassLoaderListener.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.classloader;\n\n/**\n * Listen to events happening to all classloaders (all tenants, platform-wide)\n */\npublic interface PlatformClassLoaderListener {\n\n    void onUpdate(ClassLoader newClassLoader);\n\n    void onDestroy(ClassLoader oldClassLoader);\n\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/classloader/RefreshClassLoaderTask.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.classloader;\n\nimport java.io.Serializable;\nimport java.util.concurrent.Callable;\n\nimport org.bonitasoft.engine.service.InjectedService;\nimport org.bonitasoft.engine.transaction.TransactionService;\n\n/**\n * @author Baptiste Mesta\n */\npublic class RefreshClassLoaderTask implements Callable<Void>, Serializable {\n\n    private final ClassLoaderIdentifier id;\n    private ClassLoaderService classLoaderService;\n    private TransactionService transactionService;\n\n    public RefreshClassLoaderTask(ClassLoaderIdentifier id) {\n        this.id = id;\n    }\n\n    @Override\n    public Void call() throws Exception {\n        transactionService.executeInTransaction(() -> {\n            getClassLoaderService().refreshClassLoaderImmediately(id);\n            return null;\n        });\n        return null;\n    }\n\n    public ClassLoaderService getClassLoaderService() {\n        return classLoaderService;\n    }\n\n    @InjectedService\n    public void setClassLoaderService(ClassLoaderService classLoaderService) {\n        this.classLoaderService = classLoaderService;\n    }\n\n    @InjectedService\n    public void setTransactionService(TransactionService transactionService) {\n        this.transactionService = transactionService;\n    }\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/classloader/RefreshClassloaderSynchronization.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.classloader;\n\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.TimeoutException;\n\nimport javax.transaction.Status;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException;\nimport org.bonitasoft.engine.service.BroadcastService;\nimport org.bonitasoft.engine.service.TaskResult;\nimport org.bonitasoft.engine.transaction.BonitaTransactionSynchronization;\n\n/**\n * @author Baptiste Mesta\n */\nclass RefreshClassloaderSynchronization implements BonitaTransactionSynchronization {\n\n    private final ClassLoaderServiceImpl classLoaderService;\n    private final ClassLoaderUpdater classLoaderUpdater;\n    private final BroadcastService broadcastService;\n    private final RefreshClassLoaderTask callable;\n    private final Set<ClassLoaderIdentifier> identifiers = new HashSet<>();\n\n    public RefreshClassloaderSynchronization(ClassLoaderServiceImpl classLoaderService,\n            BroadcastService broadcastService,\n            RefreshClassLoaderTask callable,\n            ClassLoaderUpdater classLoaderUpdater,\n            ClassLoaderIdentifier identifier) {\n        this.classLoaderService = classLoaderService;\n        this.classLoaderUpdater = classLoaderUpdater;\n        this.broadcastService = broadcastService;\n        this.callable = callable;\n        addClassloaderToRefresh(identifier);\n    }\n\n    @Override\n    public void afterCompletion(final int txState) {\n        classLoaderService.removeRefreshClassLoaderSynchronization();\n        if (txState == Status.STATUS_COMMITTED) {\n            //we use the ClassLoaderUpdater to refresh those classloader in an other thread/transaction.\n            //This can't be done in the current thread because we are still executing afterCompletion transactionSync\n            classLoaderUpdater.refreshClassloaders(classLoaderService, identifiers);\n            refreshClassLoaderOnOtherNodes();\n        }\n    }\n\n    private void refreshClassLoaderOnOtherNodes() {\n        try {\n            Map<String, TaskResult<Void>> execute = broadcastService.executeOnOthersAndWait(callable);\n            for (Map.Entry<String, TaskResult<Void>> resultEntry : execute.entrySet()) {\n                if (resultEntry.getValue().isError()) {\n                    throw new IllegalStateException(resultEntry.getValue().getThrowable());\n                }\n            }\n        } catch (InterruptedException | ExecutionException | TimeoutException e) {\n            throw new SBonitaRuntimeException(\"Unable to refresh the classloaders on all nodes: \" + identifiers, e);\n        }\n    }\n\n    //Testing purpose only\n    Set<ClassLoaderIdentifier> getIdentifiers() {\n        return identifiers;\n    }\n\n    void addClassloaderToRefresh(ClassLoaderIdentifier id) {\n        identifiers.add(id);\n    }\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/classloader/SClassLoaderException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.classloader;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class SClassLoaderException extends SBonitaException {\n\n    private static final long serialVersionUID = 6760479336490227757L;\n\n    public SClassLoaderException(final String message) {\n        super(message);\n    }\n\n    public SClassLoaderException(final Throwable t) {\n        super(t);\n    }\n\n    public SClassLoaderException(final String message, final Exception e) {\n        super(message, e);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/classloader/SingleClassLoaderListener.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.classloader;\n\n/**\n * Listens to events occurring on a single tenant-level classloader.\n *\n * @author Baptiste Mesta\n */\npublic interface SingleClassLoaderListener {\n\n    default void onUpdate(ClassLoader newClassLoader) {\n    }\n\n    default void onDestroy(ClassLoader oldClassLoader) {\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/classloader/listeners/ClassReflectorClearer.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.classloader.listeners;\n\nimport org.bonitasoft.engine.classloader.PlatformClassLoaderListener;\nimport org.bonitasoft.engine.commons.ClassReflector;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Baptiste Mesta\n */\n@Component\npublic class ClassReflectorClearer implements PlatformClassLoaderListener {\n\n    @Override\n    public void onUpdate(ClassLoader newClassLoader) {\n        ClassReflector.clearCache();\n    }\n\n    @Override\n    public void onDestroy(ClassLoader oldClassLoader) {\n        ClassReflector.clearCache();\n    }\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/classloader/listeners/JacksonCacheClearer.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.classloader.listeners;\n\nimport com.fasterxml.jackson.databind.type.TypeFactory;\nimport org.bonitasoft.engine.classloader.PlatformClassLoaderListener;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class JacksonCacheClearer implements PlatformClassLoaderListener {\n\n    @Override\n    public void onUpdate(ClassLoader newClassLoader) {\n        TypeFactory.defaultInstance().clearCache();\n    }\n\n    @Override\n    public void onDestroy(ClassLoader oldClassLoader) {\n        TypeFactory.defaultInstance().clearCache();\n    }\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/ArtifactAccessor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency;\n\nimport org.bonitasoft.engine.dependency.model.ScopeType;\n\npublic interface ArtifactAccessor {\n\n    boolean artifactExists(ScopeType scopeType, long artifactId);\n\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/DependencyService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.stream.Stream;\n\nimport org.bonitasoft.engine.dependency.model.AbstractSDependency;\nimport org.bonitasoft.engine.dependency.model.DependencyContent;\nimport org.bonitasoft.engine.dependency.model.SDependencyMapping;\nimport org.bonitasoft.engine.dependency.model.ScopeType;\nimport org.bonitasoft.engine.home.BonitaResource;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @since 6.0\n */\npublic interface DependencyService {\n\n    String DEPENDENCY = \"DEPENDENCY\";\n\n    String DEPENDENCYMAPPING = \"DEPENDENCYMAPPING\";\n\n    /**\n     * Delete the specific dependency\n     *\n     * @param dependency\n     *        The dependency will be deleted\n     * @throws SDependencyNotFoundException\n     *         Error thrown if the dependency not found.\n     * @throws SDependencyDeletionException\n     *         Error thrown if has exception during the dependency deletion.\n     */\n    void deleteDependency(AbstractSDependency dependency) throws SDependencyException;\n\n    /**\n     * Delete the dependency specified by name\n     *\n     * @param name\n     * @throws SDependencyNotFoundException\n     *         Error thrown if no dependency have a name corresponding to the parameter.\n     * @throws SDependencyDeletionException\n     *         Error thrown if has exception during the dependency deletion.\n     */\n    void deleteDependency(String name) throws SDependencyException;\n\n    /**\n     * Get dependency by its id\n     *\n     * @param id\n     *        Identifier of dependency\n     * @return\n     * @throws SDependencyNotFoundException\n     *         Error thrown if no dependency have an id corresponding to the parameter.\n     */\n    AbstractSDependency getDependency(long id) throws SDependencyNotFoundException;\n\n    /**\n     * Get only the content and file name of a dependency.\n     * This object will not be connected to the hibernate session and therefore will avoid\n     * issues related to dirty checking mechanism, see https://bonitasoft.atlassian.net/browse/BS-19262\n     *\n     * @param id of the dependency\n     * @return an object containing the file content and name\n     * @throws SDependencyNotFoundException\n     */\n    DependencyContent getDependencyContentOnly(long id) throws SDependencyNotFoundException, SBonitaReadException;\n\n    /**\n     * Get dependencies for the specified ids\n     *\n     * @param ids\n     *        Identifiers of dependencies\n     * @return a list of SDependency object\n     * @throws SDependencyException\n     */\n    List<AbstractSDependency> getDependencies(Collection<Long> ids) throws SDependencyException;\n\n    /**\n     * Get all dependencyMappings for specific the queryOptions\n     *\n     * @param queryOptions\n     *        QueryOptions object, it contains some query conditions.\n     * @return a list of SDependencyMapping objects\n     * @throws SDependencyException\n     */\n    List<SDependencyMapping> getDependencyMappings(QueryOptions queryOptions) throws SDependencyException;\n\n    Stream<BonitaResource> getDependenciesResources(ScopeType type, long id) throws SDependencyException;\n\n    /**\n     * Get all dependency ids for specific artifact\n     *\n     * @param artifactId\n     *        Identifier of artifact\n     * @param artifactType\n     *        Type of artifact\n     * @param startIndex\n     * @param maxResult\n     * @return a list of Long objects\n     * @throws SDependencyException\n     */\n    List<Long> getDependencyIds(long artifactId, ScopeType artifactType, int startIndex, int maxResult)\n            throws SDependencyException;\n\n    /**\n     * @param id\n     * @param type\n     * @throws SDependencyException\n     */\n    void deleteDependencies(long id, ScopeType type) throws SDependencyException;\n\n    AbstractSDependency createMappedDependency(String name, byte[] jarContent, String fileName, long artifactId,\n            ScopeType scopeType) throws SDependencyException;\n\n    AbstractSDependency updateDependencyOfArtifact(String name, byte[] jarContent, String fileName, long artifactId,\n            ScopeType scopeType) throws SDependencyException;\n\n    AbstractSDependency getDependencyOfArtifact(long artifactId, ScopeType artifactType, String fileName)\n            throws SBonitaReadException;\n\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/SDependencyAlreadyExistsException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SDependencyAlreadyExistsException extends SDependencyException {\n\n    private static final long serialVersionUID = -4974628261438631313L;\n\n    public SDependencyAlreadyExistsException(final String message) {\n        super(message);\n    }\n\n    public SDependencyAlreadyExistsException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SDependencyAlreadyExistsException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/SDependencyCreationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SDependencyCreationException extends SDependencyException {\n\n    private static final long serialVersionUID = 6262111905648333928L;\n\n    public SDependencyCreationException(final String message) {\n        super(message);\n    }\n\n    public SDependencyCreationException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SDependencyCreationException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/SDependencyDeletionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SDependencyDeletionException extends SDependencyException {\n\n    private static final long serialVersionUID = 5542868086645864234L;\n\n    public SDependencyDeletionException(final String message) {\n        super(message);\n    }\n\n    public SDependencyDeletionException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SDependencyDeletionException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/SDependencyException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SDependencyException extends SBonitaException {\n\n    private static final long serialVersionUID = 8403122121819176217L;\n\n    public SDependencyException(final String message) {\n        super(message);\n    }\n\n    public SDependencyException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SDependencyException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/SDependencyMappingNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SDependencyMappingNotFoundException extends SDependencyException {\n\n    private static final long serialVersionUID = -1886189816198433722L;\n\n    public SDependencyMappingNotFoundException(final String message) {\n        super(message);\n    }\n\n    public SDependencyMappingNotFoundException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SDependencyMappingNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/SDependencyNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SDependencyNotFoundException extends SDependencyException {\n\n    private static final long serialVersionUID = 6276746788016462991L;\n\n    public SDependencyNotFoundException(final String message) {\n        super(message);\n    }\n\n    public SDependencyNotFoundException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SDependencyNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/impl/AbstractDependencyService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency.impl;\n\nimport static org.bonitasoft.engine.home.BonitaResource.resource;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Stream;\n\nimport org.bonitasoft.engine.commons.NullCheckingUtil;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException;\nimport org.bonitasoft.engine.dependency.DependencyService;\nimport org.bonitasoft.engine.dependency.SDependencyDeletionException;\nimport org.bonitasoft.engine.dependency.SDependencyException;\nimport org.bonitasoft.engine.dependency.SDependencyNotFoundException;\nimport org.bonitasoft.engine.dependency.model.AbstractSDependency;\nimport org.bonitasoft.engine.dependency.model.SAbstractDependencyMapping;\nimport org.bonitasoft.engine.dependency.model.ScopeType;\nimport org.bonitasoft.engine.home.BonitaResource;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\n\n/**\n * @author Baptiste Mesta\n */\npublic abstract class AbstractDependencyService implements DependencyService {\n\n    protected static final int BATCH_SIZE = 100;\n    private ReadPersistenceService persistenceService;\n\n    public AbstractDependencyService(ReadPersistenceService persistenceService) {\n        this.persistenceService = persistenceService;\n    }\n\n    protected abstract void delete(AbstractSDependency dependency) throws SDependencyDeletionException;\n\n    protected abstract List<AbstractSDependency> getDependencies(QueryOptions queryOptions) throws SDependencyException;\n\n    protected abstract AbstractSDependency getDependency(String name)\n            throws SDependencyNotFoundException, SDependencyDeletionException;\n\n    @Override\n    public void deleteDependency(final String name) throws SDependencyException {\n        deleteDependency(getDependency(name));\n    }\n\n    @Override\n    public Stream<BonitaResource> getDependenciesResources(final ScopeType type, final long id)\n            throws SDependencyException {\n        List<Long> dependencyIds = getDependencyIds(id, type, 0, Integer.MAX_VALUE);\n        return dependencyIds.stream()\n                .map(dependencyId -> {\n                    try {\n                        // get only the content of the dependency to avoid having connected objects\n                        return getDependencyContentOnly(dependencyId);\n                    } catch (SDependencyNotFoundException | SBonitaReadException e) {\n                        throw new SBonitaRuntimeException(e);\n                    }\n                })\n                .map(dependency -> resource(dependency.getFileName(), dependency.getContent()));\n    }\n\n    protected abstract void createDependencyMapping(SAbstractDependencyMapping dependencyMapping)\n            throws SDependencyException;\n\n    protected abstract void deleteDependencyMapping(SAbstractDependencyMapping dependencyMapping)\n            throws SDependencyException;\n\n    protected abstract List<SAbstractDependencyMapping> getDependencyMappings(long dependencyId,\n            QueryOptions queryOptions) throws SDependencyException;\n\n    @Override\n    public List<Long> getDependencyIds(final long artifactId, final ScopeType artifactType, final int startIndex,\n            final int maxResult)\n            throws SDependencyException {\n        NullCheckingUtil.checkArgsNotNull(artifactId, artifactType, startIndex, maxResult);\n        final QueryOptions queryOptions = new QueryOptions(startIndex, maxResult);\n        try {\n            final Map<String, Object> parameters = new HashMap<>();\n            parameters.put(\"artifactId\", artifactId);\n            parameters.put(\"artifactType\", artifactType);\n            final SelectListDescriptor<Long> desc = getSelectDescriptorForDependencyIds(queryOptions, parameters);\n            return persistenceService.selectList(desc);\n        } catch (final SBonitaReadException e) {\n            throw new SDependencyException(\"Can't get dependencies\", e);\n        }\n    }\n\n    protected abstract SelectListDescriptor<Long> getSelectDescriptorForDependencyIds(QueryOptions queryOptions,\n            Map<String, Object> parameters);\n\n    protected abstract QueryOptions getDefaultQueryOptionForDependencyMapping();\n\n    @Override\n    public void deleteDependency(final AbstractSDependency dependency) throws SDependencyException {\n        for (SAbstractDependencyMapping dependencyMapping : getDependencyMappings(dependency.getId(),\n                getDefaultQueryOptionForDependencyMapping())) {\n            deleteDependencyMapping(dependencyMapping);\n        }\n        delete(dependency);\n    }\n\n    @Override\n    public void deleteDependencies(final long id, final ScopeType type) throws SDependencyException {\n        int fromIndex = 0;\n        List<Long> dependencyIds = getDependencyIds(id, type, fromIndex, BATCH_SIZE);\n        while (!dependencyIds.isEmpty()) {\n            for (final Long dependencyId : dependencyIds) {\n                final List<SAbstractDependencyMapping> dependencyMappings = getDependencyMappings(dependencyId,\n                        getDefaultQueryOptionForDependencyMapping());\n                if (dependencyMappings.size() == 1) {// only when the dependency is linked only to on element\n                    final SAbstractDependencyMapping dependencyMapping = dependencyMappings.get(0);\n                    deleteDependencyMapping(dependencyMapping);\n                    deleteDependency(getDependency(dependencyId));\n                } else {\n                    fromIndex++;\n                }\n            }\n            dependencyIds = getDependencyIds(id, type, fromIndex, BATCH_SIZE);\n        }\n    }\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/impl/DependencyServiceRegistrator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency.impl;\n\nimport javax.annotation.PostConstruct;\n\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class DependencyServiceRegistrator {\n\n    private final TenantDependencyService tenantDependencyService;\n    private final ClassLoaderService classLoaderService;\n\n    public DependencyServiceRegistrator(TenantDependencyService tenantDependencyService,\n            ClassLoaderService classLoaderService) {\n        this.tenantDependencyService = tenantDependencyService;\n        this.classLoaderService = classLoaderService;\n    }\n\n    @PostConstruct\n    private void registerDependencyService() {\n        classLoaderService.registerDependencyService(tenantDependencyService);\n    }\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/impl/PlatformDependencyService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency.impl;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\n\nimport org.bonitasoft.engine.commons.NullCheckingUtil;\nimport org.bonitasoft.engine.dependency.SDependencyCreationException;\nimport org.bonitasoft.engine.dependency.SDependencyDeletionException;\nimport org.bonitasoft.engine.dependency.SDependencyException;\nimport org.bonitasoft.engine.dependency.SDependencyNotFoundException;\nimport org.bonitasoft.engine.dependency.model.AbstractSDependency;\nimport org.bonitasoft.engine.dependency.model.DependencyContent;\nimport org.bonitasoft.engine.dependency.model.SAbstractDependencyMapping;\nimport org.bonitasoft.engine.dependency.model.SDependency;\nimport org.bonitasoft.engine.dependency.model.SDependencyMapping;\nimport org.bonitasoft.engine.dependency.model.SPlatformDependency;\nimport org.bonitasoft.engine.dependency.model.SPlatformDependencyMapping;\nimport org.bonitasoft.engine.dependency.model.ScopeType;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.services.PersistenceService;\nimport org.bonitasoft.engine.services.SPersistenceException;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class PlatformDependencyService extends AbstractDependencyService {\n\n    private final PersistenceService platformPersistenceService;\n\n    public PlatformDependencyService(final PersistenceService platformPersistenceService) {\n        super(platformPersistenceService);\n        this.platformPersistenceService = platformPersistenceService;\n    }\n\n    @Override\n    public List<AbstractSDependency> getDependencies(final Collection<Long> ids) throws SDependencyException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"ids\", ids);\n        final QueryOptions queryOptions = new QueryOptions(0, ids.size(), SPlatformDependency.class, \"id\",\n                OrderByType.ASC);\n        try {\n            return platformPersistenceService.selectList(new SelectListDescriptor<>(\"getPlatformDependenciesById\",\n                    parameters, SPlatformDependency.class, queryOptions));\n        } catch (final SBonitaReadException bre) {\n            throw new SDependencyException(bre);\n        }\n    }\n\n    @Override\n    protected void delete(AbstractSDependency dependency) throws SDependencyDeletionException {\n        try {\n            platformPersistenceService.delete(dependency);\n        } catch (final SPersistenceException pe) {\n            throw new SDependencyDeletionException(pe);\n        }\n    }\n\n    @Override\n    protected List<AbstractSDependency> getDependencies(QueryOptions queryOptions) throws SDependencyException {\n        List<AbstractSDependency> dependencies;\n        try {\n            dependencies = platformPersistenceService.selectList(new SelectListDescriptor<>(\"getPlatformDependencies\",\n                    Collections.emptyMap(), SPlatformDependency.class, queryOptions));\n        } catch (final SBonitaReadException bre) {\n            throw new SDependencyException(bre);\n        }\n        return dependencies;\n    }\n\n    @Override\n    public AbstractSDependency getDependency(final long id) throws SDependencyNotFoundException {\n        final SelectByIdDescriptor<SPlatformDependency> selectByIdDescriptor = new SelectByIdDescriptor<>(\n                SPlatformDependency.class, id);\n        try {\n            final SPlatformDependency sDependency = platformPersistenceService.selectById(selectByIdDescriptor);\n            if (sDependency == null) {\n                throw new SDependencyNotFoundException(\"No dependency exists using id: \" + id);\n            }\n            return sDependency;\n        } catch (final SBonitaReadException bre) {\n            throw new SDependencyNotFoundException(bre);\n        }\n    }\n\n    @Override\n    public DependencyContent getDependencyContentOnly(final long id)\n            throws SDependencyNotFoundException, SBonitaReadException {\n        NullCheckingUtil.checkArgsNotNull(id);\n        SelectOneDescriptor<DependencyContent> desc = new SelectOneDescriptor<>(\"getPlatformDependencyContentOnly\",\n                Collections.singletonMap(\"id\", id), SPlatformDependency.class, DependencyContent.class);\n        return Optional.ofNullable(platformPersistenceService.selectOne(desc))\n                .orElseThrow(() -> new SDependencyNotFoundException(\"Can't get content of dependency with id: \" + id));\n    }\n\n    @Override\n    protected AbstractSDependency getDependency(final String name) throws SDependencyNotFoundException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"name\", name);\n        try {\n            final SPlatformDependency sDependency = platformPersistenceService.selectOne(new SelectOneDescriptor<>(\n                    \"getPlatformDependencyByName\", parameters, SPlatformDependency.class));\n            if (sDependency == null) {\n                throw new SDependencyNotFoundException(\"No dependency exists using name: \" + name);\n            }\n            return sDependency;\n        } catch (final SBonitaReadException bre) {\n            throw new SDependencyNotFoundException(bre);\n        }\n    }\n\n    @Override\n    protected void createDependencyMapping(final SAbstractDependencyMapping dependencyMapping)\n            throws SDependencyException {\n        try {\n            platformPersistenceService.insert(dependencyMapping);\n        } catch (final SPersistenceException pe) {\n            throw new SDependencyException(pe);\n        }\n    }\n\n    @Override\n    protected void deleteDependencyMapping(final SAbstractDependencyMapping dependencyMapping)\n            throws SDependencyException {\n        try {\n            platformPersistenceService.delete(dependencyMapping);\n        } catch (final SPersistenceException pe) {\n            throw new SDependencyException(pe);\n        }\n    }\n\n    @Override\n    public List<SDependencyMapping> getDependencyMappings(final QueryOptions queryOptions) throws SDependencyException {\n        try {\n            return platformPersistenceService\n                    .selectList(new SelectListDescriptor<>(\"getPlatformDependencyMappings\", null,\n                            SPlatformDependencyMapping.class, queryOptions));\n        } catch (final SBonitaReadException e) {\n            throw new SDependencyException(\"can't get dependency mappings\", e);\n        }\n    }\n\n    @Override\n    protected List<SAbstractDependencyMapping> getDependencyMappings(final long dependencyId,\n            final QueryOptions queryOptions) throws SDependencyException {\n        try {\n            final Map<String, Object> parameters = new HashMap<>();\n            parameters.put(\"dependencyId\", dependencyId);\n            final SelectListDescriptor<SAbstractDependencyMapping> desc = new SelectListDescriptor<>(\n                    \"getPlatformDependencyMappingsByDependency\",\n                    parameters, SPlatformDependencyMapping.class, queryOptions);\n            return platformPersistenceService.selectList(desc);\n        } catch (final SBonitaReadException e) {\n            throw new SDependencyException(\"can't get dependency mappings by dependencyId: \" + dependencyId, e);\n        }\n    }\n\n    @Override\n    protected SelectListDescriptor<Long> getSelectDescriptorForDependencyIds(QueryOptions queryOptions,\n            Map<String, Object> parameters) {\n        return new SelectListDescriptor<>(\"getPlatformDependencyIds\", parameters, SPlatformDependency.class,\n                Long.class, queryOptions);\n    }\n\n    @Override\n    public AbstractSDependency createMappedDependency(String name, byte[] jarContent, String fileName, long artifactId,\n            ScopeType scopeType)\n            throws SDependencyException {\n        final SPlatformDependency sDependency = new SPlatformDependency(name, fileName, jarContent);\n        NullCheckingUtil.checkArgsNotNull(sDependency);\n        try {\n            platformPersistenceService.insert(sDependency);\n        } catch (final SPersistenceException pe) {\n            throw new SDependencyCreationException(pe);\n        }\n        final SPlatformDependencyMapping sDependencyMapping = new SPlatformDependencyMapping(artifactId, scopeType,\n                sDependency.getId());\n        createDependencyMapping(sDependencyMapping);\n        return sDependency;\n    }\n\n    @Override\n    public SDependency getDependencyOfArtifact(long artifactId, ScopeType artifactType, String fileName) {\n        return null;\n    }\n\n    @Override\n    protected QueryOptions getDefaultQueryOptionForDependencyMapping() {\n        return new QueryOptions(0, 100, SPlatformDependencyMapping.class, \"id\", OrderByType.ASC);\n    }\n\n    @Override\n    public SDependency updateDependencyOfArtifact(String name, byte[] jarContent, String fileName, long artifactId,\n            ScopeType scopeType) {\n        throw new UnsupportedOperationException(\"NYI\");\n    }\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/impl/TenantDependencyService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency.impl;\n\nimport static java.util.Collections.emptyMap;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\n\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.CollectionUtil;\nimport org.bonitasoft.engine.commons.NullCheckingUtil;\nimport org.bonitasoft.engine.dependency.SDependencyCreationException;\nimport org.bonitasoft.engine.dependency.SDependencyDeletionException;\nimport org.bonitasoft.engine.dependency.SDependencyException;\nimport org.bonitasoft.engine.dependency.SDependencyNotFoundException;\nimport org.bonitasoft.engine.dependency.model.AbstractSDependency;\nimport org.bonitasoft.engine.dependency.model.DependencyContent;\nimport org.bonitasoft.engine.dependency.model.SAbstractDependencyMapping;\nimport org.bonitasoft.engine.dependency.model.SDependency;\nimport org.bonitasoft.engine.dependency.model.SDependencyMapping;\nimport org.bonitasoft.engine.dependency.model.ScopeType;\nimport org.bonitasoft.engine.dependency.model.builder.SDependencyBuilderFactory;\nimport org.bonitasoft.engine.dependency.model.builder.SDependencyLogBuilder;\nimport org.bonitasoft.engine.dependency.model.builder.SDependencyLogBuilderFactory;\nimport org.bonitasoft.engine.dependency.model.builder.SDependencyMappingLogBuilder;\nimport org.bonitasoft.engine.dependency.model.builder.SDependencyMappingLogBuilderFactory;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity;\nimport org.bonitasoft.engine.queriablelogger.model.builder.ActionType;\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class TenantDependencyService extends AbstractDependencyService {\n\n    private final ReadPersistenceService persistenceService;\n    private final Recorder recorder;\n    private final QueriableLoggerService queriableLoggerService;\n\n    public TenantDependencyService(final ReadPersistenceService persistenceService, final Recorder recorder,\n            final QueriableLoggerService queriableLoggerService) {\n        super(persistenceService);\n        this.persistenceService = persistenceService;\n        this.recorder = recorder;\n        this.queriableLoggerService = queriableLoggerService;\n    }\n\n    private SDependencyLogBuilder getQueriableLog(final ActionType actionType, final String message) {\n        final SDependencyLogBuilder logBuilder = BuilderFactory.get(SDependencyLogBuilderFactory.class)\n                .createNewInstance();\n        initializeLogBuilder(logBuilder, message);\n        updateLog(actionType, logBuilder);\n        return logBuilder;\n    }\n\n    private <T extends SLogBuilder> void initializeLogBuilder(final T logBuilder, final String message) {\n        logBuilder.actionStatus(SQueriableLog.STATUS_FAIL).severity(SQueriableLogSeverity.INTERNAL).rawMessage(message);\n    }\n\n    private <T extends HasCRUDEAction> void updateLog(final ActionType actionType, final T logBuilder) {\n        logBuilder.setActionType(actionType);\n    }\n\n    private SDependencyMappingLogBuilder getQueriableLog(final ActionType actionType, final String message,\n            final SAbstractDependencyMapping dependencyMapping) {\n        final SDependencyMappingLogBuilder logBuilder = BuilderFactory.get(SDependencyMappingLogBuilderFactory.class)\n                .createNewInstance();\n        initializeLogBuilder(logBuilder, message);\n        updateLog(actionType, logBuilder);\n        logBuilder.dependencyId(dependencyMapping.getDependencyId());\n        logBuilder.objectId(dependencyMapping.getId());\n        return logBuilder;\n    }\n\n    @Override\n    protected List<AbstractSDependency> getDependencies(QueryOptions queryOptions) throws SDependencyException {\n        List<AbstractSDependency> dependencies;\n        try {\n            dependencies = persistenceService.selectList(new SelectListDescriptor<>(\"getDependencies\", null,\n                    AbstractSDependency.class, queryOptions));\n        } catch (final SBonitaReadException e) {\n            throw new SDependencyException(\"Can't get dependencies\", e);\n        }\n        return dependencies;\n    }\n\n    @Override\n    protected SDependency getDependency(String name) throws SDependencyNotFoundException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"name\", name);\n        final SelectOneDescriptor<SDependency> desc = new SelectOneDescriptor<>(\"getDependencyByName\", parameters,\n                SDependency.class);\n        final SDependency sDependency;\n        try {\n            sDependency = persistenceService.selectOne(desc);\n        } catch (SBonitaReadException e) {\n            throw new SDependencyNotFoundException(\"Dependency with name \" + name + \" does not exist.\");\n        }\n        if (sDependency == null) {\n            throw new SDependencyNotFoundException(\"Dependency with name \" + name + \" does not exist.\");\n        }\n        return sDependency;\n    }\n\n    @Override\n    protected List<SAbstractDependencyMapping> getDependencyMappings(final long dependencyId,\n            final QueryOptions queryOptions) throws SDependencyException {\n        NullCheckingUtil.checkArgsNotNull(dependencyId, queryOptions);\n        try {\n            final Map<String, Object> parameters = new HashMap<>();\n            parameters.put(\"dependencyId\", dependencyId);\n            final SelectListDescriptor<SAbstractDependencyMapping> desc = new SelectListDescriptor<>(\n                    \"getDependencyMappingsByDependency\", parameters,\n                    SDependencyMapping.class, queryOptions);\n            return persistenceService.selectList(desc);\n        } catch (final SBonitaReadException e) {\n            throw new SDependencyException(\"Can't get dependency mappings by dependencyId: \" + dependencyId, e);\n        }\n    }\n\n    @Override\n    protected QueryOptions getDefaultQueryOptionForDependencyMapping() {\n        return new QueryOptions(0, 100, SDependencyMapping.class, \"id\", OrderByType.ASC);\n    }\n\n    @Override\n    protected void delete(AbstractSDependency dependency) throws SDependencyDeletionException {\n        NullCheckingUtil.checkArgsNotNull(dependency);\n        final SDependencyLogBuilder logBuilder = getQueriableLog(ActionType.DELETED,\n                \"Deleting a dependency named \" + dependency.getName());\n        try {\n            delete(dependency, DEPENDENCY);\n            log(dependency.getId(), SQueriableLog.STATUS_OK, logBuilder, \"deleteDependency\");\n        } catch (final SRecorderException e) {\n            log(dependency.getId(), SQueriableLog.STATUS_FAIL, logBuilder, \"deleteDependency\");\n            throw new SDependencyDeletionException(\"Can't delete dependency\" + dependency, e);\n        }\n    }\n\n    private void delete(PersistentObject object, String eventType) throws SRecorderException {\n        recorder.recordDelete(new DeleteRecord(object), eventType);\n    }\n\n    @Override\n    public void deleteDependencyMapping(final SAbstractDependencyMapping dependencyMapping)\n            throws SDependencyException {\n        NullCheckingUtil.checkArgsNotNull(dependencyMapping);\n        final SDependencyMappingLogBuilder logBuilder = getQueriableLog(ActionType.DELETED,\n                \"Deleting a dependency mapping\", dependencyMapping);\n        try {\n            delete(dependencyMapping, DEPENDENCYMAPPING);\n            log(dependencyMapping.getId(), SQueriableLog.STATUS_OK, logBuilder, \"deleteDependencyMapping\");\n        } catch (final SRecorderException e) {\n            log(dependencyMapping.getId(), SQueriableLog.STATUS_FAIL, logBuilder, \"deleteDependencyMapping\");\n            throw new SDependencyException(\"Can't delete dependency mapping\" + dependencyMapping, e);\n        }\n    }\n\n    @Override\n    public List<AbstractSDependency> getDependencies(final Collection<Long> ids) throws SDependencyException {\n        NullCheckingUtil.checkArgsNotNull(ids);\n        try {\n            final SelectListDescriptor<AbstractSDependency> desc = new SelectListDescriptor<>(\"getDependenciesByIds\",\n                    CollectionUtil.buildSimpleMap(\"ids\",\n                            ids),\n                    SDependency.class, QueryOptions.countQueryOptions());\n            return persistenceService.selectList(desc);\n        } catch (final SBonitaReadException e) {\n            throw new SDependencyException(\"Can't get dependencies\", e);\n        }\n    }\n\n    @Override\n    protected SelectListDescriptor<Long> getSelectDescriptorForDependencyIds(QueryOptions queryOptions,\n            Map<String, Object> parameters) {\n        if (parameters.get(\"artifactType\") == ScopeType.TENANT) {\n            return new SelectListDescriptor<>(\"getDependencyIdsForTenant\", emptyMap(), SDependencyMapping.class,\n                    Long.class, queryOptions);\n        } else {\n            return new SelectListDescriptor<>(\"getDependencyIds\", parameters, SDependencyMapping.class, Long.class,\n                    queryOptions);\n        }\n    }\n\n    @Override\n    public List<SDependencyMapping> getDependencyMappings(final QueryOptions queryOptions) throws SDependencyException {\n        NullCheckingUtil.checkArgsNotNull(queryOptions);\n        try {\n            return persistenceService.selectList(new SelectListDescriptor<>(\n                    \"getDependencyMappings\", null, SDependencyMapping.class, queryOptions));\n        } catch (final SBonitaReadException e) {\n            throw new SDependencyException(\"Can't get dependency mappings\", e);\n        }\n    }\n\n    @Override\n    public SDependency getDependency(final long id) throws SDependencyNotFoundException {\n        NullCheckingUtil.checkArgsNotNull(id);\n        try {\n            final SelectByIdDescriptor<SDependency> desc = new SelectByIdDescriptor<>(SDependency.class, id);\n            final SDependency sDependency = persistenceService.selectById(desc);\n            if (sDependency == null) {\n                throw new SDependencyNotFoundException(\"Can't get dependency with id: \" + id);\n            }\n            return sDependency;\n        } catch (final SBonitaReadException e) {\n            throw new SDependencyNotFoundException(\"Can't get dependency with id: \" + id, e);\n        }\n    }\n\n    @Override\n    public DependencyContent getDependencyContentOnly(final long id)\n            throws SDependencyNotFoundException, SBonitaReadException {\n        NullCheckingUtil.checkArgsNotNull(id);\n        SelectOneDescriptor<DependencyContent> desc = new SelectOneDescriptor<>(\"getDependencyContentOnly\",\n                Collections.singletonMap(\"id\", id), SDependency.class, DependencyContent.class);\n        return Optional.ofNullable(persistenceService.selectOne(desc))\n                .orElseThrow(() -> new SDependencyNotFoundException(\"Can't get content of dependency with id: \" + id));\n    }\n\n    private void log(final long objectId, final int sQueriableLogStatus, final SPersistenceLogBuilder logBuilder,\n            final String callerMethodName) {\n        logBuilder.actionScope(String.valueOf(objectId));\n        logBuilder.actionStatus(sQueriableLogStatus);\n        logBuilder.objectId(objectId);\n        final SQueriableLog log = logBuilder.build();\n        if (queriableLoggerService.isLoggable(log.getActionType(), log.getSeverity())) {\n            queriableLoggerService.log(this.getClass().getName(), callerMethodName, log);\n        }\n    }\n\n    public SDependency createMappedDependency(String name, byte[] jarContent, String fileName, long artifactId,\n            ScopeType scopeType)\n            throws SDependencyException {\n        final SDependency sDependency = createDependency(name, jarContent, fileName, artifactId, scopeType);\n        createDependencyMapping(artifactId, scopeType, sDependency);\n        return sDependency;\n    }\n\n    public SDependency updateDependencyOfArtifact(String name, byte[] jarContent, String fileName, long artifactId,\n            ScopeType scopeType)\n            throws SDependencyException {\n        try {\n            final SDependency sDependency = getDependencyOfArtifact(artifactId, scopeType, fileName);\n            if (sDependency == null) {\n                throw new SDependencyNotFoundException(\"unable to find dependency \" + fileName + \" on artifact: \"\n                        + artifactId + \" with type \" + scopeType);\n            }\n            recorder.recordUpdate(\n                    UpdateRecord.buildSetFields(sDependency, Collections.singletonMap(\"value_\", jarContent)),\n                    DEPENDENCY);\n            return sDependency;\n        } catch (SBonitaReadException | SRecorderException e) {\n            throw new SDependencyException(e);\n        }\n    }\n\n    private SDependency createDependency(String name, byte[] jarContent, String fileName, long artifactId,\n            ScopeType scopeType)\n            throws SDependencyCreationException {\n        final SDependency sDependency = new SDependencyBuilderFactory().createNewInstance(name, artifactId, scopeType,\n                fileName, jarContent);\n        final SDependencyLogBuilder logBuilder = getQueriableLog(ActionType.CREATED,\n                \"Creating a dependency with name \" + sDependency.getName());\n        NullCheckingUtil.checkArgsNotNull(sDependency);\n        try {\n            insert(sDependency, DEPENDENCY);\n        } catch (final SRecorderException e) {\n            log(sDependency.getId(), SQueriableLog.STATUS_FAIL, logBuilder, \"createDependency\");\n            throw new SDependencyCreationException(\"Can't create dependency \" + sDependency, e);\n        }\n        return sDependency;\n    }\n\n    private void insert(PersistentObject object, String eventType) throws SRecorderException {\n        recorder.recordInsert(new InsertRecord(object), eventType);\n    }\n\n    private void createDependencyMapping(long artifactId, ScopeType scopeType, SDependency sDependency)\n            throws SDependencyException {\n        final SDependencyMapping sDependencyMapping = new SDependencyMapping(artifactId, scopeType,\n                sDependency.getId());\n        createDependencyMapping(sDependencyMapping);\n    }\n\n    @Override\n    protected void createDependencyMapping(SAbstractDependencyMapping dependencyMapping) throws SDependencyException {\n        final SDependencyMappingLogBuilder logBuilder1 = getQueriableLog(ActionType.CREATED,\n                \"Creating a dependency mapping\", dependencyMapping);\n        NullCheckingUtil.checkArgsNotNull(dependencyMapping);\n        try {\n            insert(dependencyMapping, DEPENDENCYMAPPING);\n            log(dependencyMapping.getId(), SQueriableLog.STATUS_OK, logBuilder1, \"createDependencyMapping\");\n        } catch (final SRecorderException e) {\n            log(dependencyMapping.getId(), SQueriableLog.STATUS_FAIL, logBuilder1, \"createDependencyMapping\");\n            throw new SDependencyException(\"Can't create dependency mapping\" + dependencyMapping, e);\n        }\n    }\n\n    @Override\n    public SDependency getDependencyOfArtifact(long artifactId, ScopeType artifactType, String fileName)\n            throws SBonitaReadException {\n        if (artifactType == ScopeType.TENANT) {\n            return getTenantDependencyByFilename(fileName);\n        } else {\n            final Map<String, Object> inputParameters = new HashMap<>(3);\n            inputParameters.put(\"artifactId\", artifactId);\n            inputParameters.put(\"artifactType\", artifactType);\n            inputParameters.put(\"fileName\", fileName);\n            return persistenceService\n                    .selectOne(\n                            new SelectOneDescriptor<>(\"getDependencyOfArtifact\", inputParameters, SDependency.class));\n        }\n    }\n\n    public SDependency getTenantDependencyByFilename(String fileName)\n            throws SBonitaReadException {\n        final Map<String, Object> inputParameters = new HashMap<>();\n        inputParameters.put(\"fileName\", fileName);\n        return persistenceService\n                .selectOne(new SelectOneDescriptor<>(\"getTenantDependencyByFilename\", inputParameters,\n                        SDependency.class));\n    }\n\n    public Optional<Long> getIdOfDependencyOfArtifactForTenant(String fileName)\n            throws SBonitaReadException {\n        final Map<String, Object> inputParameters = new HashMap<>(3);\n        inputParameters.put(\"fileName\", fileName);\n        Long idOfDependencyOfArtifact = persistenceService.selectOne(\n                new SelectOneDescriptor<>(\"getIdOfDependencyOfArtifactForTenant\", inputParameters, SDependency.class));\n        return Optional.ofNullable(idOfDependencyOfArtifact);\n    }\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/AbstractSDependency.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency.model;\n\nimport javax.persistence.Id;\nimport javax.persistence.MappedSuperclass;\n\nimport lombok.AccessLevel;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.Getter;\nimport lombok.NoArgsConstructor;\nimport lombok.ToString;\nimport lombok.experimental.SuperBuilder;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@SuperBuilder\n@MappedSuperclass\npublic abstract class AbstractSDependency implements PersistentObject {\n\n    public static final String DESCRIPTION = \"description\";\n    public static final String FILE_NAME = \"fileName\";\n    public static final String ID = \"id\";\n    public static final String NAME = \"name\";\n    public static final String VALUE = \"value_\";\n\n    @Id\n    private long id;\n    private String name;\n    private String fileName;\n    private String description;\n    @ToString.Exclude\n    @EqualsAndHashCode.Exclude\n    @Getter(AccessLevel.NONE)\n    private byte[] value_;\n\n    protected AbstractSDependency(final String name, final String fileName, final byte[] value) {\n        super();\n        this.name = name;\n        this.fileName = fileName;\n        this.value_ = value;\n    }\n\n    public byte[] getValue() {\n        return value_;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/DependencyContent.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency.model;\n\npublic class DependencyContent {\n\n    private String fileName;\n    private byte[] content;\n\n    public DependencyContent(String fileName, byte[] content) {\n        this.fileName = fileName;\n        this.content = content;\n    }\n\n    public byte[] getContent() {\n        return content;\n    }\n\n    public void setContent(byte[] content) {\n        this.content = content;\n    }\n\n    public String getFileName() {\n        return fileName;\n    }\n\n    public void setFileName(String fileName) {\n        this.fileName = fileName;\n    }\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/SAbstractDependencyMapping.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency.model;\n\nimport javax.persistence.EnumType;\nimport javax.persistence.Enumerated;\nimport javax.persistence.Id;\nimport javax.persistence.MappedSuperclass;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@SuperBuilder\n@MappedSuperclass\npublic abstract class SAbstractDependencyMapping implements PersistentObject {\n\n    @Id\n    private long id;\n    private long artifactId;\n    @Enumerated(EnumType.STRING)\n    private ScopeType artifactType;\n    private long dependencyId;\n\n    protected SAbstractDependencyMapping(final long artifactId, final ScopeType artifactType, final long dependencyId) {\n        super();\n        this.artifactId = artifactId;\n        this.artifactType = artifactType;\n        this.dependencyId = dependencyId;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/SDependency.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency.model;\n\nimport javax.persistence.Entity;\nimport javax.persistence.Table;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n@Data\n@EqualsAndHashCode(callSuper = true)\n@NoArgsConstructor\n@SuperBuilder\n@Entity\n@Table(name = \"dependency\")\npublic class SDependency extends AbstractSDependency {\n\n    public SDependency(final String name, final String fileName, final byte[] value) {\n        super(name, fileName, value);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/SDependencyMapping.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency.model;\n\nimport javax.persistence.Cacheable;\nimport javax.persistence.Entity;\nimport javax.persistence.Table;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n@Data\n@EqualsAndHashCode(callSuper = true)\n@NoArgsConstructor\n@SuperBuilder\n@Entity\n@Table(name = \"dependencymapping\")\n@Cacheable(false)\npublic class SDependencyMapping extends SAbstractDependencyMapping {\n\n    private static final long MEANINGLESS_ID = -1L;\n\n    public SDependencyMapping(final long artifactId, final ScopeType artifactType, final long dependencyId) {\n        super(artifactId, artifactType, dependencyId);\n        // Need to set it afterwards because the call to super() MUST be the first statement\n        if (ScopeType.TENANT == artifactType) {\n            // If the scope is TENANT, the artifactId is meaningless:\n            setArtifactId(MEANINGLESS_ID);\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/SPlatformDependency.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency.model;\n\nimport javax.persistence.Entity;\nimport javax.persistence.Table;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n@Data\n@EqualsAndHashCode(callSuper = true)\n@NoArgsConstructor\n@SuperBuilder\n@Entity\n@Table(name = \"pdependency\")\npublic class SPlatformDependency extends AbstractSDependency {\n\n    public SPlatformDependency(final String name, final String fileName, final byte[] value) {\n        super(name, fileName, value);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/SPlatformDependencyMapping.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency.model;\n\nimport javax.persistence.Cacheable;\nimport javax.persistence.Entity;\nimport javax.persistence.Table;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\n\n@Data\n@EqualsAndHashCode(callSuper = true)\n@NoArgsConstructor\n@SuperBuilder\n@Entity\n@Table(name = \"pdependencymapping\")\n@Cacheable(false)\npublic class SPlatformDependencyMapping extends SAbstractDependencyMapping {\n\n    public SPlatformDependencyMapping(final long artifactId, final ScopeType artifactType, final long dependencyId) {\n        super(artifactId, artifactType, dependencyId);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/ScopeType.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency.model;\n\n/**\n * @author Celine Souchet\n */\npublic enum ScopeType {\n    /**\n     * The dependency is map with a process instance.\n     */\n    PROCESS,\n\n    /**\n     * The dependency is map with a tenant.\n     */\n    TENANT,\n\n    /**\n     * For the platform dependency.\n     */\n    GLOBAL\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/builder/SDependencyBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency.model.builder;\n\nimport org.bonitasoft.engine.dependency.model.SDependency;\nimport org.bonitasoft.engine.dependency.model.ScopeType;\n\npublic class SDependencyBuilderFactory {\n\n    public SDependency createNewInstance(final String name, final long artifactId, final ScopeType artifactType,\n            final String fileName,\n            final byte[] value) {\n        if (artifactType == ScopeType.PROCESS) {\n            return new SDependency(artifactId + \"_\" + name, fileName, value);\n        }\n        return new SDependency(name, fileName, value);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/builder/SDependencyLogBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency.model.builder;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\n\n/**\n * @author Yanyan Liu\n */\npublic interface SDependencyLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction {\n\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/builder/SDependencyLogBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency.model.builder;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory;\n\n/**\n * @author Yanyan Liu\n */\npublic interface SDependencyLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory {\n\n    SDependencyLogBuilder createNewInstance();\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/builder/SDependencyMappingLogBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency.model.builder;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\n\n/**\n * @author Yanyan Liu\n */\npublic interface SDependencyMappingLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction {\n\n    SDependencyMappingLogBuilder dependencyId(final long dependencyId);\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/builder/SDependencyMappingLogBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency.model.builder;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory;\n\n/**\n * @author Yanyan Liu\n */\npublic interface SDependencyMappingLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory {\n\n    SDependencyMappingLogBuilder createNewInstance();\n\n    String getDependencyIdKey();\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/builder/SPlatformDependencyBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency.model.builder;\n\nimport org.bonitasoft.engine.dependency.model.SPlatformDependency;\n\npublic interface SPlatformDependencyBuilder {\n\n    SPlatformDependencyBuilder setDescription(final String description);\n\n    SPlatformDependency done();\n\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/builder/SPlatformDependencyLogBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency.model.builder;\n\n/**\n * @author Yanyan Liu\n */\npublic interface SPlatformDependencyLogBuilderFactory extends SDependencyLogBuilderFactory {\n\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/builder/SPlatformDependencyMappingLogBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency.model.builder;\n\n/**\n * @author Yanyan Liu\n */\npublic interface SPlatformDependencyMappingLogBuilderFactory extends SDependencyMappingLogBuilderFactory {\n\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/builder/impl/SDependencyLogBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency.model.builder.impl;\n\nimport org.bonitasoft.engine.dependency.model.builder.SDependencyLogBuilder;\nimport org.bonitasoft.engine.dependency.model.builder.SDependencyLogBuilderFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory;\n\n/**\n * @author Yanyan Liu\n */\npublic class SDependencyLogBuilderFactoryImpl extends CRUDELogBuilderFactory implements SDependencyLogBuilderFactory {\n\n    @Override\n    public SDependencyLogBuilder createNewInstance() {\n        return new SDependencyLogBuilderImpl();\n    }\n\n    @Override\n    public String getObjectIdKey() {\n        return SDependencyLogIndexesMapper.DEPENDENCY_INDEX_NAME;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/builder/impl/SDependencyLogBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency.model.builder.impl;\n\nimport org.bonitasoft.engine.dependency.model.builder.SDependencyLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException;\n\n/**\n * @author Yanyan Liu\n */\npublic class SDependencyLogBuilderImpl extends CRUDELogBuilder implements SDependencyLogBuilder {\n\n    private static final String PREFIX = \"DEPENDENCY\";\n\n    @Override\n    public SPersistenceLogBuilder objectId(final long objectId) {\n        queriableLogBuilder.numericIndex(SDependencyLogIndexesMapper.DEPENDENCY_INDEX, objectId);\n        return this;\n    }\n\n    @Override\n    protected String getActionTypePrefix() {\n        return PREFIX;\n    }\n\n    @Override\n    protected void checkExtraRules(final SQueriableLog log) {\n        if (log.getActionStatus() != SQueriableLog.STATUS_FAIL) {\n            if (log.getNumericIndex(SDependencyLogIndexesMapper.DEPENDENCY_INDEX) == 0L) {\n                throw new MissingMandatoryFieldsException(\"Some mandatory fields are missing: \" + \"Dependency Id\");\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/builder/impl/SDependencyLogIndexesMapper.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency.model.builder.impl;\n\n/**\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n */\npublic class SDependencyLogIndexesMapper {\n\n    public static final int DEPENDENCY_INDEX = 0;\n\n    public static final int DEPENDENCY_MAPPING_INDEX = 1;\n\n    public static final String DEPENDENCY_INDEX_NAME = \"numericIndex1\";\n\n    public static final String DEPENDENCY_MAPPING_INDEX_NAME = \"numericIndex2\";\n\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/builder/impl/SDependencyMappingLogBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency.model.builder.impl;\n\nimport org.bonitasoft.engine.dependency.model.builder.SDependencyMappingLogBuilder;\nimport org.bonitasoft.engine.dependency.model.builder.SDependencyMappingLogBuilderFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory;\n\n/**\n * @author Yanyan Liu\n */\npublic class SDependencyMappingLogBuilderFactoryImpl extends CRUDELogBuilderFactory\n        implements SDependencyMappingLogBuilderFactory {\n\n    @Override\n    public SDependencyMappingLogBuilder createNewInstance() {\n        return new SDependencyMappingLogBuilderImpl();\n    }\n\n    @Override\n    public String getObjectIdKey() {\n        return SDependencyLogIndexesMapper.DEPENDENCY_MAPPING_INDEX_NAME;\n    }\n\n    @Override\n    public String getDependencyIdKey() {\n        return SDependencyLogIndexesMapper.DEPENDENCY_INDEX_NAME;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/builder/impl/SDependencyMappingLogBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency.model.builder.impl;\n\nimport org.bonitasoft.engine.dependency.model.builder.SDependencyMappingLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException;\n\n/**\n * @author Yanyan Liu\n */\npublic class SDependencyMappingLogBuilderImpl extends CRUDELogBuilder implements SDependencyMappingLogBuilder {\n\n    private static final String PREFIX = \"DEPENDENCY_MAPPING\";\n\n    @Override\n    public SPersistenceLogBuilder objectId(final long objectId) {\n        queriableLogBuilder.numericIndex(SDependencyLogIndexesMapper.DEPENDENCY_MAPPING_INDEX, objectId);\n        return this;\n    }\n\n    @Override\n    protected String getActionTypePrefix() {\n        return PREFIX;\n    }\n\n    @Override\n    protected void checkExtraRules(final SQueriableLog log) {\n        if (log.getNumericIndex(SDependencyLogIndexesMapper.DEPENDENCY_INDEX) == 0L) {\n            throw new MissingMandatoryFieldsException(\"Some mandatory fields are missing: \" + \"Dependency Id\");\n        }\n        if (log.getActionStatus() != SQueriableLog.STATUS_FAIL) {\n            if (log.getNumericIndex(SDependencyLogIndexesMapper.DEPENDENCY_MAPPING_INDEX) == 0L) {\n                throw new MissingMandatoryFieldsException(\n                        \"Some mandatory fields are missing: \" + \"Dependency Mapping Id\");\n            }\n        }\n    }\n\n    @Override\n    public SDependencyMappingLogBuilder dependencyId(final long dependencyId) {\n        queriableLogBuilder.numericIndex(SDependencyLogIndexesMapper.DEPENDENCY_INDEX, dependencyId);\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/builder/impl/SPlatformDependencyLogBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency.model.builder.impl;\n\nimport org.bonitasoft.engine.dependency.model.builder.SDependencyLogBuilder;\nimport org.bonitasoft.engine.dependency.model.builder.SPlatformDependencyLogBuilderFactory;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SPlatformDependencyLogBuilderFactoryImpl extends SDependencyLogBuilderFactoryImpl\n        implements SPlatformDependencyLogBuilderFactory {\n\n    @Override\n    public SDependencyLogBuilder createNewInstance() {\n        return new SDependencyLogBuilderImpl();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/builder/impl/SPlatformDependencyLogIndexesMapper.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency.model.builder.impl;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SPlatformDependencyLogIndexesMapper extends SDependencyLogIndexesMapper {\n\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/builder/impl/SPlatformDependencyMappingLogBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency.model.builder.impl;\n\nimport org.bonitasoft.engine.dependency.model.builder.SDependencyMappingLogBuilder;\nimport org.bonitasoft.engine.dependency.model.builder.SPlatformDependencyMappingLogBuilderFactory;\n\n/**\n * @author Yanyan Liu\n */\npublic class SPlatformDependencyMappingLogBuilderFactoryImpl extends SDependencyMappingLogBuilderFactoryImpl\n        implements SPlatformDependencyMappingLogBuilderFactory {\n\n    @Override\n    public SDependencyMappingLogBuilder createNewInstance() {\n        return new SDependencyMappingLogBuilderImpl();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/main/resources/org/bonitasoft/engine/dependency/model/impl/hibernate/dependency.queries.hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\"\n        \"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd\">\n<hibernate-mapping auto-import=\"false\">\n\n    <query name=\"getDependencyByName\">\n        SELECT dependency\n        FROM org.bonitasoft.engine.dependency.model.SDependency AS dependency\n        WHERE dependency.name = :name\n    </query>\n\n    <query name=\"getDependenciesByIds\" read-only=\"true\">\n        SELECT dependency\n        FROM org.bonitasoft.engine.dependency.model.SDependency AS dependency\n        WHERE dependency.id IN (:ids)\n        ORDER BY dependency.id ASC\n    </query>\n\n    <!-- get only the content of the dependency using a 'new' to avoid having a connected object-->\n    <query name=\"getDependencyContentOnly\">\n        SELECT new org.bonitasoft.engine.dependency.model.DependencyContent(dependency.fileName, dependency.value_)\n        FROM org.bonitasoft.engine.dependency.model.SDependency AS dependency\n        WHERE dependency.id = :id\n    </query>\n\n    <query name=\"getDependencies\" read-only=\"true\">\n        SELECT dependency\n        FROM org.bonitasoft.engine.dependency.model.SDependency AS dependency\n    </query>\n\n    <query name=\"getDependencyMapping\">\n        SELECT dependencymapping\n        FROM org.bonitasoft.engine.dependency.model.SDependencyMapping AS dependencymapping\n        WHERE dependencymapping.id = :id\n    </query>\n\n    <query name=\"getDependencyMappings\">\n        SELECT dependencymapping\n        FROM org.bonitasoft.engine.dependency.model.SDependencyMapping AS dependencymapping\n    </query>\n\n    <query name=\"getDependencyMappingsByDependency\">\n        SELECT dependencymapping\n        FROM org.bonitasoft.engine.dependency.model.SDependencyMapping AS dependencymapping\n        WHERE dependencymapping.dependencyId = :dependencyId\n    </query>\n\n    <query name=\"getDependencyIds\">\n        SELECT dependencymapping.dependencyId\n        FROM org.bonitasoft.engine.dependency.model.SDependencyMapping AS dependencymapping\n        WHERE dependencymapping.artifactId = :artifactId\n        AND dependencymapping.artifactType = :artifactType\n        ORDER BY dependencymapping.dependencyId\n    </query>\n\n    <query name=\"getDependencyIdsForTenant\">\n        SELECT dependencymapping.dependencyId\n        FROM org.bonitasoft.engine.dependency.model.SDependencyMapping AS dependencymapping\n        WHERE dependencymapping.artifactType = 'TENANT'\n        ORDER BY dependencymapping.dependencyId\n    </query>\n\n    <query name=\"getDependencyOfArtifact\">\n        SELECT dependency\n        FROM org.bonitasoft.engine.dependency.model.SDependency AS dependency,\n        org.bonitasoft.engine.dependency.model.SDependencyMapping AS dependencymapping\n        WHERE dependencymapping.artifactId = :artifactId\n        AND dependencymapping.artifactType = :artifactType\n        AND dependencymapping.dependencyId = dependency.id\n        AND dependency.fileName = :fileName\n    </query>\n\n    <query name=\"getTenantDependencyByFilename\">\n        SELECT dependency\n        FROM org.bonitasoft.engine.dependency.model.SDependency AS dependency,\n        org.bonitasoft.engine.dependency.model.SDependencyMapping AS dependencymapping\n        WHERE dependencymapping.artifactType = 'TENANT'\n        AND dependencymapping.dependencyId = dependency.id\n        AND dependency.fileName = :fileName\n    </query>\n\n    <query name=\"getIdOfDependencyOfArtifact\">\n        SELECT dependency.id\n        FROM org.bonitasoft.engine.dependency.model.SDependency AS dependency,\n             org.bonitasoft.engine.dependency.model.SDependencyMapping AS dependencymapping\n        WHERE dependencymapping.artifactId = :artifactId\n        AND dependencymapping.artifactType = :artifactType\n        AND dependencymapping.dependencyId = dependency.id\n        AND dependency.fileName = :fileName\n    </query>\n\n    <query name=\"getIdOfDependencyOfArtifactForTenant\">\n        SELECT dependency.id\n        FROM org.bonitasoft.engine.dependency.model.SDependency AS dependency,\n             org.bonitasoft.engine.dependency.model.SDependencyMapping AS dependencymapping\n        WHERE dependencymapping.artifactType = 'TENANT'\n        AND dependencymapping.dependencyId = dependency.id\n        AND dependency.fileName = :fileName\n    </query>\n\n</hibernate-mapping>\n"
  },
  {
    "path": "services/bonita-classloader/src/main/resources/org/bonitasoft/engine/dependency/model/impl/hibernate/platform-dependency.queries.hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\" \"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd\">\n<hibernate-mapping auto-import=\"false\">\n\n\n\n\t<query name=\"getPlatformDependencyByName\">\n\t\tSELECT pdependency\n\t\tFROM org.bonitasoft.engine.dependency.model.SPlatformDependency AS pdependency\n\t\tWHERE pdependency.name = :name\n\t</query>\n\n\t<query name=\"getPlatformDependenciesById\" read-only=\"true\">\n\t\tSELECT pdependency\n\t\tFROM org.bonitasoft.engine.dependency.model.SPlatformDependency AS pdependency\n\t\tWHERE pdependency.id IN (:ids)\n\t</query>\n  \n\t<query name=\"getPlatformDependencies\" read-only=\"true\">\n\t\tSELECT pdependency\n\t\tFROM org.bonitasoft.engine.dependency.model.SPlatformDependency AS pdependency\n\t</query>\n\n\t<query name=\"getPlatformDependencyContentOnly\">\n\t\tSELECT new org.bonitasoft.engine.dependency.model.DependencyContent(pdependency.fileName, pdependency.value_)\n\t\tFROM org.bonitasoft.engine.dependency.model.SPlatformDependency AS pdependency\n\t\tWHERE pdependency.id = :id\n\t</query>\n\t<query name=\"getPlatformDependencyMappings\">\n\t\tSELECT pdependencymapping\n\t\tFROM org.bonitasoft.engine.dependency.model.SPlatformDependencyMapping AS pdependencymapping\n\t</query>\n\n\t<query name=\"getPlatformDepencencyMappingsByArtifact\">\n\t\tSELECT pdependencymapping\n\t\tFROM org.bonitasoft.engine.dependency.model.SPlatformDependencyMapping AS pdependencymapping\n\t\tWHERE pdependencymapping.artifactId = :artifactId\n\t\tAND pdependencymapping.artifactType = :artifactType\n\t</query>\n\n\t<query name=\"getPlatformDependencyMappingsByDependency\">\n    \tSELECT pdependencymapping\n    \tFROM org.bonitasoft.engine.dependency.model.SPlatformDependencyMapping AS pdependencymapping\n    \tWHERE pdependencymapping.dependencyId = :dependencyId\n\t</query>\n\n\t<query name=\"getPlatformDependencyIds\">\n\t\tSELECT pdependencymapping.dependencyId\n\t\tFROM org.bonitasoft.engine.dependency.model.SPlatformDependencyMapping AS pdependencymapping\n\t\tWHERE pdependencymapping.artifactId = :artifactId\n\t\tAND pdependencymapping.artifactType = :artifactType\n\t\tORDER BY pdependencymapping.id ASC\n\t</query>\n\n</hibernate-mapping>\n"
  },
  {
    "path": "services/bonita-classloader/src/test/java/org/bonitasoft/engine/classloader/BonitaClassLoaderTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.classloader;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.GLOBAL;\nimport static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier;\nimport static org.bonitasoft.engine.dependency.model.ScopeType.PROCESS;\nimport static org.bonitasoft.engine.home.BonitaResource.resource;\nimport static org.bonitasoft.engine.io.IOUtil.generateJar;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.lang.reflect.InvocationTargetException;\nimport java.net.URL;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Future;\nimport java.util.stream.Stream;\n\nimport com.thoughtworks.xstream.XStream;\nimport org.apache.commons.io.FileUtils;\nimport org.bonitasoft.engine.commons.JavaMethodInvoker;\nimport org.bonitasoft.engine.data.instance.model.impl.XStreamFactory;\nimport org.bonitasoft.engine.home.BonitaResource;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.rules.TemporaryFolder;\n\npublic class BonitaClassLoaderTest {\n\n    @Rule\n    public TemporaryFolder temporaryFolder = new TemporaryFolder();\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n    private ClassLoader testClassLoader;\n    private int idCounter = 1;\n\n    @Before\n    public void before() {\n        testClassLoader = Thread.currentThread().getContextClassLoader();\n    }\n\n    @After\n    public void after() {\n        Thread.currentThread().setContextClassLoader(testClassLoader);\n    }\n\n    @Test\n    public void destroyShouldRemoveAllScopeFolderAndItsContent() throws IOException {\n        final BonitaClassLoader bonitaClassLoader = BonitaClassLoaderFactory.createClassLoader(\n                Stream.of(resource(\"myJar.jar\", \"Salut le monde\".getBytes())), identifier(PROCESS, 154L),\n                temporaryFolder.newFolder().toURI(), BonitaClassLoader.class.getClassLoader());\n        File temporaryFolder = bonitaClassLoader.getTemporaryFolder();\n        assertThat(temporaryFolder).as(\"bonitaClassLoader tempDir:%s should exists after bonitaClassLoader creation\",\n                temporaryFolder.getAbsolutePath())\n                .exists();\n        // when\n        bonitaClassLoader.destroy();\n\n        // then\n        assertThat(temporaryFolder).as(\"bonitaClassLoader tempDir:%s should not exists after bonitaClassLoader release\",\n                temporaryFolder.getAbsolutePath())\n                .doesNotExist();\n    }\n\n    @Test\n    public void should_create_second_classloader_use_other_folder() throws Exception {\n        //given\n        File tempFolder = temporaryFolder.newFolder();\n        //when\n        BonitaClassLoader classLoader1 = BonitaClassLoaderFactory.createClassLoader(\n                Stream.of(resource(\"myJar1.jar\", \"content\".getBytes())), identifier(PROCESS, 12L), tempFolder.toURI(),\n                BonitaClassLoaderTest.class.getClassLoader());\n        BonitaClassLoader classLoader2 = BonitaClassLoaderFactory.createClassLoader(\n                Stream.of(resource(\"myJar2.jar\", \"content\".getBytes())), identifier(PROCESS, 13L), tempFolder.toURI(),\n                BonitaClassLoaderTest.class.getClassLoader());\n        //then\n        assertThat(classLoader1.getTemporaryFolder().getAbsolutePath())\n                .isNotEqualTo(classLoader2.getTemporaryFolder().getAbsolutePath());\n        assertThat(classLoader1.getTemporaryFolder().getParentFile()).isEqualTo(tempFolder);\n        assertThat(classLoader2.getTemporaryFolder().getParentFile()).isEqualTo(tempFolder);\n    }\n\n    @Test\n    public void should_be_able_to_get_resources_inside_jars() throws Exception {\n        final BonitaClassLoader bonitaClassLoader = BonitaClassLoaderFactory.createClassLoader(\n                Stream.of(resource(\"UOSFaasApplication.jar\",\n                        FileUtils.readFileToByteArray(new File(\"src/test/resources/UOSFaasApplication.jar\")))),\n                identifier(PROCESS, 154L), temporaryFolder.newFolder().toURI(),\n                testClassLoader);\n\n        URL url = bonitaClassLoader.getResource(\"au/edu/sydney/faas/applicationstudent/StudentInformation.class\");\n        assertThat(url).isNotNull();\n        assertThat(url.toString())\n                .containsIgnoringCase(\n                        \"!/au/edu/sydney/faas/applicationstudent/StudentInformation.class\");\n\n        bonitaClassLoader.destroy();\n    }\n\n    @Test\n    public void should_be_able_to_use_the_JavaMethodInvoker_concurrently_on_a_BonitaClassLoader() throws Exception {\n        final BonitaClassLoader bonitaClassLoader = BonitaClassLoaderFactory.createClassLoader(\n                Stream.of(resource(\"UOSFaasApplication.jar\",\n                        FileUtils.readFileToByteArray(new File(\"src/test/resources/UOSFaasApplication.jar\")))),\n                identifier(PROCESS, 154L), temporaryFolder.newFolder().toURI(),\n                testClassLoader);\n\n        final Object objectToInvokeJavaMethodOn = bonitaClassLoader\n                .loadClass(\"au.edu.sydney.faas.applicationstudent.StudentRequest\")\n                .getConstructors()[0].newInstance();\n        final Object valueToSetObjectWith = bonitaClassLoader\n                .loadClass(\"au.edu.sydney.faas.applicationstudent.StudentInformation\")\n                .getConstructors()[0].newInstance();\n\n        ExecutorService executor = Executors.newSingleThreadExecutor(r -> {\n            Thread t = new Thread(r);\n            t.setContextClassLoader(bonitaClassLoader);\n            return t;\n        });\n\n        Future<Object> jmiFuture = executor.submit(() -> {\n            try {\n                JavaMethodInvoker jmi = new JavaMethodInvoker();\n                jmi.invokeJavaMethod(\"au.edu.sydney.faas.applicationstudent.StudentInformation\",\n                        valueToSetObjectWith, objectToInvokeJavaMethodOn,\n                        \"setStudentInformation\", \"au.edu.sydney.faas.applicationstudent.StudentInformation\");\n            } catch (Exception e) {\n                throw new RuntimeException(e);\n            }\n            return null;\n        });\n        jmiFuture.get();\n\n        // To clean\n        bonitaClassLoader.destroy();\n    }\n\n    @Test\n    public void destroy_should_update_XStream_instance() throws Exception {\n        BonitaClassLoader classLoader = BonitaClassLoaderFactory.createClassLoader(Stream.empty(), GLOBAL,\n                temporaryFolder.newFolder().toURI(), testClassLoader);\n        Thread.currentThread().setContextClassLoader(classLoader);\n        // retrieve the XStream instance related to this class loader\n        XStream xStreamBeforeDestroy = XStreamFactory.getXStream();\n\n        classLoader.destroy();\n\n        // the XStream instance retrieved after destroy must have changed\n        XStream xStreamAfterDestroy = XStreamFactory.getXStream();\n        assertThat(xStreamAfterDestroy).isNotSameAs(xStreamBeforeDestroy);\n    }\n\n    @Test\n    public void should_be_able_to_replace_class_with_same_name_in_different_classloader() throws Exception {\n        BonitaClassLoader parent = createClassloader(testClassLoader);\n        BonitaClassLoader c1 = createClassloader(parent, resource(\"jar1.jar\", generateJar(\"Hello\",\n                \"public class Hello{\",\n                \"public String there(){\",\n                \"return \\\"hello\\\";\",\n                \"}\",\n                \"}\")));\n        // Class.forName keep a reference in the classloader. We can't override that, it's an native method\n        // We removed VirtualClassLoader to support that use case:\n        assertThat(invoke(Class.forName(\"Hello\", false, c1), \"there\")).isEqualTo(\"hello\");\n        assertThat(invoke(c1.loadClass(\"Hello\"), \"there\")).isEqualTo(\"hello\");\n\n        BonitaClassLoader c2 = createClassloader(resource(\"jar2.jar\", generateJar(\"Hello\",\n                \"public class Hello{\",\n                \"public String there(){\",\n                \"return \\\"hello there\\\";\",\n                \"}\",\n                \"}\")));\n\n        assertThat(invoke(Class.forName(\"Hello\", false, c2), \"there\")).isEqualTo(\"hello there\");\n        assertThat(invoke(c2.loadClass(\"Hello\"), \"there\")).isEqualTo(\"hello there\");\n        c1.destroy();\n        c2.destroy();\n        parent.destroy();\n    }\n\n    @Test\n    public void should_be_able_to_replace_implementation_of_parent_classloader() throws Exception {\n        BonitaClassLoader parent1 = createClassloader(testClassLoader, resource(\"lib.jar\", generateJar(\"ParentLib\",\n                \"public class ParentLib {\",\n                \"   public String getVersion(){\",\n                \"       return \\\"1.0\\\";\",\n                \"   }\",\n                \"}\")));\n\n        byte[] childJar = generateJar(\"Child\",\n                \"public class Child {\",\n                \"   public String getVersion() throws Exception {\",\n                \"       Class parentLibClass = Class.forName(\\\"ParentLib\\\");\",\n                \"       return \\\"Version of the lib in parent is \\\" + parentLibClass.getMethod(\\\"getVersion\\\").invoke(parentLibClass.newInstance());\",\n                \"   }\",\n                \"}\");\n        BonitaClassLoader child1 = createClassloader(parent1, resource(\"child.jar\", childJar));\n\n        assertThat(invoke(child1.loadClass(\"Child\"), \"getVersion\")).isEqualTo(\"Version of the lib in parent is 1.0\");\n\n        //We recreate a hierarchy of classloader. We removed VirtualClassLoader so there is no way to change a parent classloader.\n        BonitaClassLoader parent2 = createClassloader(resource(\"lib.jar\", generateJar(\"ParentLib\",\n                \"public class ParentLib {\",\n                \"   public String getVersion(){\",\n                \"       return \\\"2.0\\\";\",\n                \"   }\",\n                \"}\")));\n        BonitaClassLoader child2 = createClassloader(parent2, resource(\"child.jar\", childJar));\n        assertThat(invoke(child2.loadClass(\"Child\"), \"getVersion\")).isEqualTo(\"Version of the lib in parent is 2.0\");\n    }\n\n    private BonitaClassLoader createClassloader(BonitaResource... resources) throws IOException {\n        return createClassloader(testClassLoader, resources);\n    }\n\n    private BonitaClassLoader createClassloader(ClassLoader parent, BonitaResource... resources) throws IOException {\n        return BonitaClassLoaderFactory.createClassLoader(Stream.of(resources), identifier(PROCESS, idCounter++),\n                temporaryFolder.newFolder().toURI(),\n                parent);\n    }\n\n    protected Object invoke(Class<?> class1, String name)\n            throws IllegalAccessException, InvocationTargetException, NoSuchMethodException, InstantiationException {\n        return class1.getMethod(name).invoke(class1.newInstance());\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/test/java/org/bonitasoft/engine/classloader/ClassLoaderServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.classloader;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\nimport static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.*;\nimport static org.bonitasoft.engine.dependency.model.ScopeType.PROCESS;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Arrays;\n\nimport org.bonitasoft.engine.dependency.impl.PlatformDependencyService;\nimport org.bonitasoft.engine.dependency.impl.TenantDependencyService;\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.service.BroadcastService;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.SystemOutRule;\nimport org.junit.rules.TemporaryFolder;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Baptiste Mesta\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class ClassLoaderServiceImplTest {\n\n    @Rule\n    public SystemOutRule systemOutRule = new SystemOutRule().enableLog();\n\n    @Mock\n    private EventService eventService;\n    @Mock\n    private PlatformDependencyService platformDependencyService;\n    @Mock\n    private TenantDependencyService tenantDependencyService;\n    @Mock\n    private UserTransactionService userTransactionService;\n    @Mock\n    private BroadcastService broadcastService;\n    @Mock\n    private ClassLoaderUpdater classLoaderUpdater;\n\n    @Mock\n    private PlatformClassLoaderListener platformClassLoaderListener1;\n    @Mock\n    private PlatformClassLoaderListener platformClassLoaderListener2;\n\n    @Captor\n    private ArgumentCaptor<RefreshClassloaderSynchronization> synchronizationArgumentCaptor;\n\n    private ClassLoaderServiceImpl classLoaderService;\n    private ClassLoader testClassLoader;\n    private BonitaClassLoader processClassLoader;\n    private MyClassLoaderListener myClassLoaderListener;\n\n    @Rule\n    public TemporaryFolder temporaryFolder = new TemporaryFolder();\n    private final long PROCESS_ID = 12;\n\n    @Before\n    public void before() throws Exception {\n        classLoaderService = new ClassLoaderServiceImpl(new ParentClassLoaderResolver(),\n                eventService,\n                platformDependencyService, userTransactionService, broadcastService,\n                classLoaderUpdater, Arrays.asList(platformClassLoaderListener1, platformClassLoaderListener2));\n\n        when(classLoaderUpdater.initializeClassLoader(eq(classLoaderService), any()))\n                .thenAnswer(a -> classLoaderService.createClassloader(a.getArgument(1)));\n        classLoaderService.registerDependencyService(tenantDependencyService);\n        processClassLoader = classLoaderService.getClassLoader(identifier(PROCESS, PROCESS_ID));\n        classLoaderService.getClassLoader(TENANT);\n        testClassLoader = Thread.currentThread().getContextClassLoader();\n        Thread.currentThread().setContextClassLoader(processClassLoader);\n        myClassLoaderListener = new MyClassLoaderListener();\n        temporaryFolder.create();\n    }\n\n    @After\n    public void after() {\n        Thread.currentThread().setContextClassLoader(testClassLoader);\n    }\n\n    @Test\n    public void should_addListener_add_on_specified_classloader_do_not_call_on_others() {\n        //given\n        classLoaderService.addListener(TENANT, myClassLoaderListener);\n        //when\n        processClassLoader.destroy();\n        //then\n        assertThat(myClassLoaderListener.isOnDestroyCalled()).isFalse();\n    }\n\n    @Test\n    public void should_addListener_add_on_specified_classloader_call_listener() throws SClassLoaderException {\n        //given\n        classLoaderService.addListener(identifier(PROCESS, PROCESS_ID), myClassLoaderListener);\n        //when\n        classLoaderService.removeLocalClassloader(identifier(PROCESS, PROCESS_ID));\n        //then\n        assertThat(myClassLoaderListener.isOnDestroyCalled()).isTrue();\n    }\n\n    @Test\n    public void should_not_be_able_to_destroy_classloader_having_children() {\n        assertThatThrownBy(() -> classLoaderService.removeLocalClassloader(TENANT))\n                .hasMessageContaining(\n                        \"Unable to delete classloader TENANT because it has children: [BonitaClassLoader[id=PROCESS:12\");\n    }\n\n    @Test\n    public void should_removeListener_remove_the_listener() {\n        //given\n        classLoaderService.addListener(TENANT, myClassLoaderListener);\n        classLoaderService.removeListener(TENANT, myClassLoaderListener);\n        //when\n        processClassLoader.destroy();\n        //then\n        assertThat(myClassLoaderListener.isOnDestroyCalled()).isFalse();\n    }\n\n    @Test\n    public void should_refreshClassLoader_call_replace_classloader() throws Exception {\n        //given\n        classLoaderService.addListener(identifier(PROCESS, PROCESS_ID), myClassLoaderListener);\n        //when\n        classLoaderService.refreshClassLoaderImmediately(identifier(PROCESS, PROCESS_ID));\n        //then\n        assertThat(myClassLoaderListener.isOnUpdateCalled()).isTrue();\n        assertThat(myClassLoaderListener.isOnDestroyCalled()).isTrue();\n    }\n\n    @Test\n    public void should_stop_destroy_all_classloaders() throws Exception {\n        //given\n        classLoaderService.addListener(identifier(PROCESS, PROCESS_ID), myClassLoaderListener);\n        classLoaderService.addListener(TENANT, myClassLoaderListener);\n        classLoaderService.getClassLoader(identifier(PROCESS, 125));\n        classLoaderService.addListener(identifier(PROCESS, 125), myClassLoaderListener);\n        classLoaderService.getClassLoader(identifier(PROCESS, 126));\n        classLoaderService.addListener(identifier(PROCESS, 126), myClassLoaderListener);\n        classLoaderService.getClassLoader(identifier(PROCESS, 127));\n        classLoaderService.addListener(identifier(PROCESS, 127), myClassLoaderListener);\n        classLoaderService.getClassLoader(identifier(PROCESS, 128));\n        classLoaderService.addListener(identifier(PROCESS, 128), myClassLoaderListener);\n        classLoaderService.getClassLoader(identifier(PROCESS, 129));\n        classLoaderService.addListener(identifier(PROCESS, 129), myClassLoaderListener);\n        classLoaderService.getClassLoader(identifier(PROCESS, 130));\n        classLoaderService.addListener(identifier(PROCESS, 130), myClassLoaderListener);\n        //when\n        classLoaderService.stop();\n        //then\n        assertThat(myClassLoaderListener.getOnUpdateCalled()).isEqualTo(0);\n        assertThat(myClassLoaderListener.getOnDestroyCalled()).isEqualTo(8);\n    }\n\n    @Test\n    public void should_removeLocalClassLoader_call_destroy() throws Exception {\n        //given\n        classLoaderService.addListener(identifier(PROCESS, PROCESS_ID), myClassLoaderListener);\n        classLoaderService.addListener(TENANT, myClassLoaderListener);\n        //when\n        classLoaderService.removeLocalClassloader(identifier(PROCESS, PROCESS_ID));\n\n        //then\n        assertThat(myClassLoaderListener.getOnDestroyCalled()).isEqualTo(1);\n    }\n\n    @Test(expected = SClassLoaderException.class)\n    public void should_removeLocalClassLoader_throw_exception_if_parent_not_removed() throws Exception {\n        //given\n        classLoaderService.getClassLoader(identifier(PROCESS, 17));//second classloader\n        //when\n        classLoaderService.removeLocalClassloader(identifier(PROCESS, PROCESS_ID));\n        classLoaderService.removeLocalClassloader(TENANT);\n    }\n\n    @Test\n    public void should_removeLocalClassLoader_work_if_remove_in_right_order() throws Exception {\n        //given\n        //when\n        classLoaderService.removeLocalClassloader(identifier(PROCESS, PROCESS_ID));\n        classLoaderService.removeLocalClassloader(TENANT);\n    }\n\n    @Test\n    public void should_getLocalClassLoader_create_expected_hierarchy() {\n        //given\n        BonitaClassLoader localClassLoader = classLoaderService.getClassLoader(identifier(PROCESS, PROCESS_ID));\n        //when\n\n        assertThat(localClassLoader.getIdentifier()).isEqualTo(identifier(PROCESS, PROCESS_ID));\n        ClassLoader parent = localClassLoader.getParent();\n        assertThat(parent).isInstanceOf(BonitaClassLoader.class);\n        assertThat(((BonitaClassLoader) parent).getIdentifier())\n                .isEqualTo(TENANT);\n        ClassLoader global = parent.getParent();\n        assertThat(global).isInstanceOf(BonitaClassLoader.class);\n        assertThat(((BonitaClassLoader) global).getIdentifier()).isEqualTo(ClassLoaderIdentifier.GLOBAL);\n        ClassLoader root = global.getParent();\n        assertThat(root).isNotInstanceOf(BonitaClassLoader.class);\n    }\n\n    @Test\n    public void should_globalListeners_be_called_on_destroy() throws Exception {\n        //given\n        classLoaderService.getClassLoader(identifier(PROCESS, 17)); //second classloader\n        //when\n        classLoaderService.removeLocalClassloader(identifier(PROCESS, PROCESS_ID));\n        classLoaderService.removeLocalClassloader(identifier(PROCESS, 17));\n\n        //then\n        verify(platformClassLoaderListener1, times(2)).onDestroy(any(BonitaClassLoader.class));\n        verify(platformClassLoaderListener2, times(2)).onDestroy(any(BonitaClassLoader.class));\n    }\n\n    @Test\n    public void should_globalListeners_be_called_on_update() throws Exception {\n        //given\n        classLoaderService.getClassLoader(identifier(PROCESS, 17));//second classloader\n        //when\n        classLoaderService.refreshClassLoaderImmediately(identifier(PROCESS, PROCESS_ID));\n        classLoaderService.refreshClassLoaderImmediately(identifier(PROCESS, 17));\n\n        //then\n        verify(platformClassLoaderListener1, times(2)).onUpdate(\n                any(BonitaClassLoader.class));\n        verify(platformClassLoaderListener2, times(2)).onUpdate(\n                any(BonitaClassLoader.class));\n    }\n\n    @Test\n    public void should_refresh_classloader_after_transaction() throws Exception {\n        doNothing().when(userTransactionService).registerBonitaSynchronization(synchronizationArgumentCaptor.capture());\n\n        classLoaderService.refreshClassLoaderAfterUpdate(identifier(PROCESS, 42));\n\n        assertThat(synchronizationArgumentCaptor.getValue()).isInstanceOf(RefreshClassloaderSynchronization.class);\n    }\n\n    @Test\n    public void should_register_only_one_synchronization_when_refreshing_multiple_classloader_in_the_same_transaction()\n            throws Exception {\n        doNothing().when(userTransactionService).registerBonitaSynchronization(synchronizationArgumentCaptor.capture());\n\n        classLoaderService.refreshClassLoaderAfterUpdate(identifier(PROCESS, 42));\n        classLoaderService.refreshClassLoaderAfterUpdate(identifier(PROCESS, 42));\n        classLoaderService.refreshClassLoaderAfterUpdate(identifier(PROCESS, 42));\n        classLoaderService.refreshClassLoaderAfterUpdate(identifier(PROCESS, 42));\n\n        assertThat(synchronizationArgumentCaptor.getAllValues()).hasSize(1);\n    }\n\n    @Test\n    public void should_refresh_multiple_classloaders_after_transaction() throws Exception {\n        doNothing().when(userTransactionService).registerBonitaSynchronization(synchronizationArgumentCaptor.capture());\n\n        classLoaderService.refreshClassLoaderAfterUpdate(identifier(PROCESS, 41));\n        classLoaderService.refreshClassLoaderAfterUpdate(identifier(PROCESS, 42));\n        classLoaderService.refreshClassLoaderAfterUpdate(identifier(PROCESS, 43));\n        classLoaderService.refreshClassLoaderAfterUpdate(identifier(PROCESS, 42));\n\n        assertThat(synchronizationArgumentCaptor.getAllValues()).hasSize(1);\n        assertThat(synchronizationArgumentCaptor.getValue().getIdentifiers())\n                .containsExactlyInAnyOrder(identifier(PROCESS, 41L), identifier(PROCESS, 42L),\n                        identifier(PROCESS, 43L));\n    }\n\n    @Test\n    public void should_refresh_classloader_after_transaction_once_per_transaction() throws Exception {\n        doNothing().when(userTransactionService).registerBonitaSynchronization(synchronizationArgumentCaptor.capture());\n\n        classLoaderService.refreshClassLoaderAfterUpdate(identifier(PROCESS, 42));\n        // Simulate an end + begin of a transaction:\n        classLoaderService.removeRefreshClassLoaderSynchronization();\n        classLoaderService.refreshClassLoaderAfterUpdate(identifier(PROCESS, 42));\n\n        assertThat(synchronizationArgumentCaptor.getAllValues()).hasSize(2);\n    }\n\n    @Test\n    public void should_initialize_process_class_loader_when_getting_it() {\n        classLoaderService.getClassLoader(identifier(PROCESS, 42));\n\n        verify(classLoaderUpdater).initializeClassLoader(classLoaderService, identifier(PROCESS, 42));\n    }\n\n    @Test\n    public void should_initialize_only_once_classloader() {\n\n        classLoaderService.getClassLoader(identifier(PROCESS, 42));\n        classLoaderService.getClassLoader(identifier(PROCESS, 42));\n\n        verify(classLoaderUpdater, times(1)).initializeClassLoader(classLoaderService, identifier(PROCESS, 42));\n    }\n\n    @Test\n    public void should_not_initialize_classloader_when_adding_and_removing_listener() {\n        SingleClassLoaderListener singleClassLoaderListener = mock(SingleClassLoaderListener.class);\n\n        assertThat(classLoaderService.addListener(TENANT, singleClassLoaderListener)).isTrue();\n        assertThat(classLoaderService.removeListener(TENANT, singleClassLoaderListener)).isTrue();\n\n        verify(classLoaderUpdater, never()).initializeClassLoader(classLoaderService, TENANT);\n    }\n\n    @Test\n    public void should_add_and_remove_listeners_for_one_classloader() throws Exception {\n        //given\n        SingleClassLoaderListener classLoaderListener1 = new SingleClassLoaderListener() {\n        };\n        SingleClassLoaderListener classLoaderListener2 = new SingleClassLoaderListener() {\n        };\n        classLoaderService.addListener(TENANT, classLoaderListener1);\n        classLoaderService.addListener(TENANT, classLoaderListener2);\n        //when\n        classLoaderService.removeListener(TENANT, classLoaderListener1);\n        //then\n        assertThat(classLoaderService.getListeners(TENANT)).containsExactly(classLoaderListener2);\n    }\n\n    @Test\n    public void should_call_destroy_on_all_class_loader_when_refreshing_a_parent_classloader() throws Exception {\n        MyClassLoaderListener process12Listener = new MyClassLoaderListener();\n        classLoaderService.addListener(identifier(PROCESS, 12), process12Listener);\n        MyClassLoaderListener process13Listener = new MyClassLoaderListener();\n        classLoaderService.addListener(identifier(PROCESS, 13), process13Listener);\n        MyClassLoaderListener tenantListener = new MyClassLoaderListener();\n        classLoaderService.addListener(TENANT, tenantListener);\n        MyClassLoaderListener globalListener = new MyClassLoaderListener();\n        classLoaderService.addListener(GLOBAL, globalListener);\n\n        classLoaderService.getClassLoader(identifier(PROCESS, 12));\n        classLoaderService.getClassLoader(identifier(PROCESS, 13));\n\n        classLoaderService.refreshClassLoaderImmediately(GLOBAL);\n\n        //process and tenant classloaders are only destroyed\n        assertThat(process12Listener.isOnDestroyCalled()).isTrue();\n        assertThat(process12Listener.isOnUpdateCalled()).isFalse();\n        assertThat(process13Listener.isOnDestroyCalled()).isTrue();\n        assertThat(process13Listener.isOnUpdateCalled()).isFalse();\n        assertThat(tenantListener.isOnDestroyCalled()).isTrue();\n        assertThat(tenantListener.isOnUpdateCalled()).isFalse();\n        //global classloader is destroyed and updated\n        assertThat(globalListener.isOnDestroyCalled()).isTrue();\n        assertThat(globalListener.isOnUpdateCalled()).isTrue();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/test/java/org/bonitasoft/engine/classloader/ClassLoaderUpdaterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.classloader;\n\nimport static java.util.concurrent.CompletableFuture.completedFuture;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier;\nimport static org.bonitasoft.engine.dependency.model.ScopeType.PROCESS;\nimport static org.mockito.Mockito.*;\n\nimport java.util.HashSet;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.TimeoutException;\n\nimport org.apache.commons.lang3.reflect.FieldUtils;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException;\nimport org.bonitasoft.engine.service.BonitaTaskExecutor;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnit;\nimport org.mockito.junit.MockitoRule;\n\npublic class ClassLoaderUpdaterTest {\n\n    @Rule\n    public MockitoRule mockitoRule = MockitoJUnit.rule();\n\n    @Mock\n    private UserTransactionService userTransactionService;\n    @Mock\n    private BonitaTaskExecutor bonitaTaskExecutor;\n    @Captor\n    private ArgumentCaptor<Callable<?>> callableGivenToTheTaskExecutor;\n    @Captor\n    private ArgumentCaptor<Callable<?>> callableGivenToTheTransactionService;\n    @Mock\n    private ClassLoaderServiceImpl classLoaderService;\n    @InjectMocks\n    private ClassLoaderUpdater classLoaderUpdater;\n\n    @Test\n    public void should_refresh_classloaders_in_transaction() throws Exception {\n        doReturn(completedFuture(null)).when(bonitaTaskExecutor).execute(callableGivenToTheTaskExecutor.capture());\n        doReturn(null).when(userTransactionService)\n                .executeInTransaction(callableGivenToTheTransactionService.capture());\n\n        HashSet<ClassLoaderIdentifier> ids = new HashSet<>();\n        ids.add(ClassLoaderIdentifier.TENANT);\n        ids.add(identifier(PROCESS, 45L));\n        classLoaderUpdater.refreshClassloaders(classLoaderService, ids);\n\n        callableGivenToTheTaskExecutor.getValue().call();\n        callableGivenToTheTransactionService.getValue().call();\n\n        verify(classLoaderService).refreshClassLoaderImmediately(ClassLoaderIdentifier.TENANT);\n        verify(classLoaderService).refreshClassLoaderImmediately(identifier(PROCESS, 45L));\n    }\n\n    @Test\n    public void should_throw_SBonitaRuntimeException_on_timeout_in_execute() throws Exception {\n        // Make executor return a Future whose timed get throws TimeoutException\n        Future future = mock(Future.class);\n        doReturn(future).when(bonitaTaskExecutor).execute((Callable<Object>) any());\n        doThrow(new TimeoutException(\"simulated timeout\"))\n                .when(future).get(anyLong(), any(TimeUnit.class));\n\n        assertThatExceptionOfType(SBonitaRuntimeException.class)\n                .isThrownBy(\n                        () -> classLoaderUpdater.initializeClassLoader(classLoaderService, identifier(PROCESS, 45L)))\n                .withCauseExactlyInstanceOf(TimeoutException.class)\n                .withMessageStartingWith(\"Unable to refresh the classloaders\");\n    }\n\n    @Test\n    public void should_keep_positive_timeout_value_when_validating() throws Exception {\n        // given\n        setTimeoutValue(classLoaderUpdater, 10L);\n\n        // when\n        classLoaderUpdater.validateClassLoaderInitializationTimeoutMinutes();\n\n        // then\n        assertThat(getTimeoutValue(classLoaderUpdater)).isEqualTo(10L);\n    }\n\n    @Test\n    public void should_reset_to_default_when_timeout_is_zero() throws Exception {\n        // given\n        setTimeoutValue(classLoaderUpdater, 0L);\n\n        // when\n        classLoaderUpdater.validateClassLoaderInitializationTimeoutMinutes();\n\n        // then\n        assertThat(getTimeoutValue(classLoaderUpdater)).isEqualTo(5L);\n    }\n\n    @Test\n    public void should_reset_to_default_when_timeout_is_negative() throws Exception {\n        // given\n        setTimeoutValue(classLoaderUpdater, -5L);\n\n        // when\n        classLoaderUpdater.validateClassLoaderInitializationTimeoutMinutes();\n\n        // then\n        assertThat(getTimeoutValue(classLoaderUpdater)).isEqualTo(5L);\n    }\n\n    @Test\n    public void should_keep_minimum_positive_value_when_validating() throws Exception {\n        // given\n        setTimeoutValue(classLoaderUpdater, 1L);\n\n        // when\n        classLoaderUpdater.validateClassLoaderInitializationTimeoutMinutes();\n\n        // then\n        assertThat(getTimeoutValue(classLoaderUpdater)).isEqualTo(1L);\n    }\n\n    @Test\n    public void should_keep_large_timeout_value_when_validating() throws Exception {\n        // given\n        setTimeoutValue(classLoaderUpdater, 120L);\n\n        // when\n        classLoaderUpdater.validateClassLoaderInitializationTimeoutMinutes();\n\n        // then\n        assertThat(getTimeoutValue(classLoaderUpdater)).isEqualTo(120L);\n    }\n\n    private void setTimeoutValue(ClassLoaderUpdater updater, long value) throws Exception {\n        FieldUtils.writeField(updater, \"classLoaderInitializationTimeoutMinutes\", value, true);\n    }\n\n    private long getTimeoutValue(ClassLoaderUpdater updater) throws Exception {\n        return (long) FieldUtils.readField(updater, \"classLoaderInitializationTimeoutMinutes\", true);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/test/java/org/bonitasoft/engine/classloader/MyClassLoaderListener.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.classloader;\n\n/**\n * @author Baptiste Mesta\n */\nclass MyClassLoaderListener implements SingleClassLoaderListener {\n\n    int onDestroyCalled = 0;\n    int onUpdateCalled = 0;\n\n    @Override\n    public void onUpdate(ClassLoader newClassLoader) {\n        onUpdateCalled++;\n    }\n\n    @Override\n    public void onDestroy(ClassLoader oldClassLoader) {\n        onDestroyCalled++;\n    }\n\n    public boolean isOnDestroyCalled() {\n        return onDestroyCalled > 0;\n    }\n\n    public boolean isOnUpdateCalled() {\n        return onUpdateCalled > 0;\n    }\n\n    public int getOnDestroyCalled() {\n        return onDestroyCalled;\n    }\n\n    public int getOnUpdateCalled() {\n        return onUpdateCalled;\n    }\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/test/java/org/bonitasoft/engine/classloader/ParentClassLoaderResolverTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.classloader;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier;\nimport static org.bonitasoft.engine.dependency.model.ScopeType.PROCESS;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Baptiste Mesta\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class ParentClassLoaderResolverTest {\n\n    @InjectMocks\n    private ParentClassLoaderResolver parentClassLoaderResolver;\n\n    @Test\n    public void should_getParentClassLoaderIdentifier_return_global_on_tenant_classloader() {\n        //given\n        final ClassLoaderIdentifier childId = ClassLoaderIdentifier.TENANT;\n        //when\n        final ClassLoaderIdentifier parentClassLoaderIdentifier = parentClassLoaderResolver\n                .getParentClassLoaderIdentifier(childId);\n        //then\n        assertThat(parentClassLoaderIdentifier).isEqualTo(ClassLoaderIdentifier.GLOBAL);\n    }\n\n    @Test\n    public void should_getParentClassLoaderIdentifier_return_tenant() {\n        //given\n        final ClassLoaderIdentifier childId = identifier(PROCESS, 124);\n        //when\n        final ClassLoaderIdentifier parentClassLoaderIdentifier = parentClassLoaderResolver\n                .getParentClassLoaderIdentifier(childId);\n        //then\n        assertThat(parentClassLoaderIdentifier).isEqualTo(ClassLoaderIdentifier.TENANT);\n    }\n\n    @Test\n    public void should_give_APPLICATION_for_parent_of_global_classloader() {\n        ClassLoaderIdentifier parentClassLoaderIdentifier = parentClassLoaderResolver\n                .getParentClassLoaderIdentifier(ClassLoaderIdentifier.GLOBAL);\n\n        assertThat(parentClassLoaderIdentifier).isEqualTo(ClassLoaderIdentifier.APPLICATION);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/test/java/org/bonitasoft/engine/classloader/RefreshClassloaderSynchronizationTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.classloader;\n\nimport static java.util.Collections.emptyMap;\nimport static java.util.Collections.singleton;\nimport static javax.transaction.Status.*;\nimport static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport org.bonitasoft.engine.dependency.model.ScopeType;\nimport org.bonitasoft.engine.service.BroadcastService;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnit;\nimport org.mockito.junit.MockitoRule;\n\npublic class RefreshClassloaderSynchronizationTest {\n\n    @Rule\n    public MockitoRule mockitoRule = MockitoJUnit.rule();\n\n    @Mock\n    private BroadcastService broadcastService;\n    @Mock\n    private RefreshClassLoaderTask refreshClassLoaderTask;\n    @Mock\n    private ClassLoaderServiceImpl classLoaderService;\n    @Mock\n    private ClassLoaderUpdater classLoaderUpdater;\n    private RefreshClassloaderSynchronization refreshClassloaderSynchronization;\n\n    @Before\n    public void before() {\n        refreshClassloaderSynchronization = new RefreshClassloaderSynchronization(classLoaderService, broadcastService,\n                refreshClassLoaderTask, classLoaderUpdater, identifier(ScopeType.PROCESS, 111L));\n    }\n\n    @Test\n    public void should_remove_the_synchronization_when_its_executed() {\n\n        refreshClassloaderSynchronization.afterCompletion(STATUS_NO_TRANSACTION);\n\n        verify(classLoaderService).removeRefreshClassLoaderSynchronization();\n    }\n\n    @Test\n    public void should_refresh_classloader_using_classLoaderUpdater() throws Exception {\n        doReturn(emptyMap()).when(broadcastService).executeOnOthersAndWait(any());\n\n        refreshClassloaderSynchronization.afterCompletion(STATUS_COMMITTED);\n\n        verify(classLoaderUpdater).refreshClassloaders(classLoaderService,\n                singleton(identifier(ScopeType.PROCESS, 111L)));\n    }\n\n    @Test\n    public void should_refresh_classloader_on_other_nodes_after_commit() throws Exception {\n        doReturn(emptyMap()).when(broadcastService).executeOnOthersAndWait(any());\n\n        refreshClassloaderSynchronization.afterCompletion(STATUS_COMMITTED);\n\n        verify(broadcastService).executeOnOthersAndWait(refreshClassLoaderTask);\n    }\n\n    @Test\n    public void should_not_refresh_classloader_when_transaction_is_not_committed() {\n        refreshClassloaderSynchronization.afterCompletion(STATUS_MARKED_ROLLBACK);\n\n        verifyNoInteractions(broadcastService);\n        verifyNoInteractions(classLoaderUpdater);\n    }\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/test/java/org/bonitasoft/engine/classloader/listeners/ClassReflectorClearerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.classloader.listeners;\n\nimport org.assertj.core.api.Assertions;\nimport org.bonitasoft.engine.commons.ClassReflector;\nimport org.junit.Test;\n\n/**\n * @author Baptiste Mesta\n */\npublic class ClassReflectorClearerTest {\n\n    @Test\n    public void testOnUpdate() throws Exception {\n        ClassReflector.getMethod(this.getClass(), \"testOnUpdate\");\n\n        Assertions.assertThat(ClassReflector.getCacheSize()).isPositive();\n        new ClassReflectorClearer().onUpdate(null);\n        Assertions.assertThat(ClassReflector.getCacheSize()).isZero();\n    }\n\n    @Test\n    public void testOnDestroy() throws Exception {\n        ClassReflector.getMethod(this.getClass(), \"testOnUpdate\");\n\n        Assertions.assertThat(ClassReflector.getCacheSize()).isPositive();\n        new ClassReflectorClearer().onDestroy(null);\n        Assertions.assertThat(ClassReflector.getCacheSize()).isZero();\n\n    }\n}\n"
  },
  {
    "path": "services/bonita-classloader/src/test/java/org/bonitasoft/engine/dependency/impl/TenantDependencyServiceTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.dependency.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.dependency.model.ScopeType.PROCESS;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.dependency.SDependencyException;\nimport org.bonitasoft.engine.dependency.SDependencyNotFoundException;\nimport org.bonitasoft.engine.dependency.model.SDependency;\nimport org.bonitasoft.engine.dependency.model.SDependencyMapping;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentMatchers;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Mockito;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Celine Souchet\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class TenantDependencyServiceTest {\n\n    @Mock\n    private ReadPersistenceService persistenceService;\n    @Mock\n    private Recorder recorder;\n    @Mock\n    private QueriableLoggerService queriableLoggerService;\n\n    @InjectMocks\n    private TenantDependencyService tenantDependencyService;\n\n    /**\n     * Test method for {@link TenantDependencyService#getDependency(long)}.\n     */\n    @Test\n    public final void getDependencyById() throws SBonitaReadException, SDependencyNotFoundException {\n        final SDependency sDependency = mock(SDependency.class);\n        when(persistenceService.selectById(ArgumentMatchers.<SelectByIdDescriptor<SDependency>> any()))\n                .thenReturn(sDependency);\n\n        Assert.assertEquals(sDependency, tenantDependencyService.getDependency(456L));\n    }\n\n    @Test(expected = SDependencyNotFoundException.class)\n    public final void getDependencyByIdNotExists() throws SBonitaReadException, SDependencyNotFoundException {\n        when(persistenceService.selectById(ArgumentMatchers.<SelectByIdDescriptor<SDependency>> any()))\n                .thenReturn(null);\n\n        tenantDependencyService.getDependency(456L);\n    }\n\n    @Test(expected = SDependencyNotFoundException.class)\n    public final void getDependencyByIdThrowException() throws SBonitaReadException, SDependencyNotFoundException {\n        when(persistenceService.selectById(ArgumentMatchers.<SelectByIdDescriptor<SDependency>> any()))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        tenantDependencyService.getDependency(456L);\n    }\n\n    /**\n     * Test method for {@link TenantDependencyService#getDependencies(java.util.Collection)}.\n     */\n    @Test\n    public final void getDependenciesByIds() throws SBonitaReadException, SDependencyException {\n        final List<SDependency> sDependencies = new ArrayList<>();\n        when(persistenceService.selectList(ArgumentMatchers.<SelectListDescriptor<SDependency>> any()))\n                .thenReturn(sDependencies);\n\n        Assert.assertEquals(sDependencies, tenantDependencyService.getDependencies(Collections.singletonList(456L)));\n    }\n\n    @Test(expected = SDependencyException.class)\n    public final void getDependenciesByIdsThrowException() throws SBonitaReadException, SDependencyException {\n        when(persistenceService.selectList(ArgumentMatchers.<SelectListDescriptor<SDependency>> any()))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        tenantDependencyService.getDependencies(Collections.singletonList(456L));\n    }\n\n    /**\n     * Test method for {@link TenantDependencyService#getDependencies(org.bonitasoft.engine.persistence.QueryOptions)}.\n     */\n    @Test\n    public final void getDependenciesWithOptions() throws SBonitaReadException, SDependencyException {\n        final List<SDependency> sDependencies = new ArrayList<>();\n        when(persistenceService.selectList(ArgumentMatchers.<SelectListDescriptor<SDependency>> any()))\n                .thenReturn(sDependencies);\n\n        final QueryOptions options = new QueryOptions(0, 10);\n        Assert.assertEquals(sDependencies, tenantDependencyService.getDependencies(options));\n    }\n\n    @Test(expected = SDependencyException.class)\n    public final void getDependenciesWithOptionsThrowException() throws SBonitaReadException, SDependencyException {\n        when(persistenceService.selectList(ArgumentMatchers.<SelectListDescriptor<SDependency>> any()))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        final QueryOptions options = new QueryOptions(0, 10);\n        tenantDependencyService.getDependencies(options);\n    }\n\n    @Test\n    public final void getDependencyIds() throws SBonitaReadException, SDependencyException {\n        final List<SDependency> sDependencies = new ArrayList<>();\n        when(persistenceService.selectList(ArgumentMatchers.<SelectListDescriptor<SDependency>> any()))\n                .thenReturn(sDependencies);\n\n        assertThat(tenantDependencyService.getDependencyIds(54156L, PROCESS, 1, 100)).isEqualTo(sDependencies);\n    }\n\n    @Test(expected = SDependencyException.class)\n    public final void getDependencyIdsThrowException() throws SBonitaReadException, SDependencyException {\n        when(persistenceService.selectList(ArgumentMatchers.<SelectListDescriptor<SDependency>> any()))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        tenantDependencyService.getDependencyIds(54156L, PROCESS, 1, 100);\n    }\n\n    /**\n     * Test method for\n     * {@link TenantDependencyService#getDependencyMappings(long, org.bonitasoft.engine.persistence.QueryOptions)}.\n     */\n    @Test\n    public final void getDependencyMappingsWithDependencyIdAndQueryOptions()\n            throws SBonitaReadException, SDependencyException {\n        final List<SDependencyMapping> sDependencyMappings = new ArrayList<>();\n        when(persistenceService.selectList(ArgumentMatchers.<SelectListDescriptor<SDependencyMapping>> any()))\n                .thenReturn(sDependencyMappings);\n\n        final QueryOptions options = new QueryOptions(0, 10);\n        Assert.assertEquals(sDependencyMappings, tenantDependencyService.getDependencyMappings(54156L, options));\n    }\n\n    @Test(expected = SDependencyException.class)\n    public final void getDependencyMappingsWithDependencyIdAndQueryOptionsThrowException()\n            throws SBonitaReadException, SDependencyException {\n        when(persistenceService.selectList(ArgumentMatchers.<SelectListDescriptor<SDependency>> any()))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        final QueryOptions options = new QueryOptions(0, 10);\n        tenantDependencyService.getDependencyMappings(54156L, options);\n    }\n\n    /**\n     * Test method for\n     * {@link TenantDependencyService#getDependencyMappings(org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test\n    public final void getDependencyMappingsWithOptions() throws SBonitaReadException, SDependencyException {\n        final List<SDependencyMapping> sDependencyMappings = new ArrayList<>();\n        when(persistenceService.selectList(ArgumentMatchers.<SelectListDescriptor<SDependencyMapping>> any()))\n                .thenReturn(sDependencyMappings);\n\n        final QueryOptions options = new QueryOptions(0, 10);\n        Assert.assertEquals(sDependencyMappings, tenantDependencyService.getDependencyMappings(options));\n    }\n\n    @Test(expected = SDependencyException.class)\n    public final void getDependencyMappingsWithOptionsThrowException()\n            throws SBonitaReadException, SDependencyException {\n        when(persistenceService.selectList(ArgumentMatchers.<SelectListDescriptor<SDependency>> any()))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        final QueryOptions options = new QueryOptions(0, 10);\n        tenantDependencyService.getDependencyMappings(options);\n    }\n\n    @Test(expected = SDependencyNotFoundException.class)\n    public void deleteDependencyByNonExistingNameShouldThrowSDependencyNotFoundException() throws Exception {\n        when(persistenceService.selectOne(ArgumentMatchers.<SelectOneDescriptor<SDependency>> any())).thenReturn(null);\n        tenantDependencyService.deleteDependency(\"notFound\");\n    }\n\n    @Test(expected = SDependencyNotFoundException.class)\n    public void deleteDependencyWithReadExceptionShouldThrowSDependencyNotFoundException() throws Exception {\n        Mockito.doThrow(new SBonitaReadException(\"\")).when(persistenceService)\n                .selectOne(ArgumentMatchers.<SelectOneDescriptor<SDependency>> any());\n        tenantDependencyService.deleteDependency(\"notFound\");\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-command/build.gradle",
    "content": "\n\ndependencies {\n    api project(':services:bonita-log')\n    api project(':services:bonita-commons')\n    api project(':services:bonita-builder')\n    api project(':services:bonita-events')\n    api project(':services:bonita-persistence')\n    testImplementation libs.assertj\n    testImplementation libs.mockitoCore\n    testImplementation libs.logback\n\n    annotationProcessor libs.lombok\n    compileOnly libs.lombok\n}\n"
  },
  {
    "path": "services/bonita-command/src/main/java/org/bonitasoft/engine/command/CommandService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.command.model.SCommand;\nimport org.bonitasoft.engine.command.model.SCommandCriterion;\nimport org.bonitasoft.engine.commons.TenantLifecycleService;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Matthieu Chaffotte\n * @since 6.0\n */\npublic interface CommandService extends TenantLifecycleService {\n\n    String COMMAND = \"COMMAND\";\n\n    /**\n     * Create command by given command\n     *\n     * @param command\n     *        command without Id\n     * @throws SCommandAlreadyExistsException\n     *         Error thrown when relative command already exists\n     * @throws SCommandCreationException\n     *         Error thrown if has exceptions during the creating command.\n     */\n    void create(SCommand command) throws SCommandAlreadyExistsException, SCommandCreationException;\n\n    /**\n     * Delete command by given command name\n     *\n     * @param name\n     *        Name of command which will be deleted\n     * @throws SCommandNotFoundException\n     *         Error thrown if no command have name corresponding to the parameter.\n     * @throws SCommandDeletionException\n     *         Error thrown if has exception during the deleting command.\n     */\n    void delete(String name) throws SCommandNotFoundException, SCommandDeletionException;\n\n    /**\n     * Delete all commands\n     *\n     * @throws SCommandDeletionException\n     *         Error thrown if has exception during the deleting command.\n     */\n    void deleteAll() throws SCommandDeletionException;\n\n    /**\n     * Get command by given name\n     *\n     * @param name\n     *        Name of command\n     * @return a command object\n     * @throws SCommandNotFoundException\n     *         Error thrown if no command have name corresponding to the parameter.\n     */\n    SCommand get(String name) throws SCommandNotFoundException;\n\n    /**\n     * Retrieves a paginated list of commands, The returned list is paginated\n     *\n     * @param startIndex\n     *        Start index of command record\n     * @param maxResults\n     *        Number of commands we want to get. Maximum number of commands returned.\n     * @param sort\n     *        The criterion used to sort the retried commands\n     * @return a list of command objects\n     * @throws SCommandGettingException\n     *         Error thrown if has exception during the command getting.\n     */\n    List<SCommand> getAllCommands(int startIndex, int maxResults, SCommandCriterion sort)\n            throws SCommandGettingException;\n\n    /**\n     * Update the command by its id\n     *\n     * @param command\n     *        The command will be updated\n     * @param updateDescriptor\n     *        The description for update command\n     * @throws SCommandNotFoundException\n     *         Error thrown if no command have name corresponding to the parameter.\n     * @throws SCommandUpdateException\n     *         Error thrown if has exception during the command updating.\n     */\n    void update(SCommand command, EntityUpdateDescriptor updateDescriptor)\n            throws SCommandNotFoundException, SCommandUpdateException;\n\n    /**\n     * Retrieves a paginated list of commands with System is false\n     *\n     * @param startIndex\n     *        Start index of command record\n     * @param maxResults\n     *        Number of commands we want to get. Maximum number of commands returned.\n     * @param sCommandCriterion\n     *        The criterion used to sort the retried commands\n     * @return A list of command objects\n     * @throws SCommandGettingException\n     *         Error thrown if has exception during the command getting.\n     * @since 6.0\n     */\n    List<SCommand> getUserCommands(int startIndex, int maxResults, SCommandCriterion sCommandCriterion)\n            throws SCommandGettingException;\n\n    /**\n     * Get command by given id\n     *\n     * @param commandId\n     *        identifier of command\n     * @return a command object\n     * @throws SCommandNotFoundException\n     *         Error thrown if no command have name corresponding to the parameter.\n     * @author Yanyan Liu\n     */\n    SCommand get(final long commandId) throws SCommandNotFoundException;\n\n    /**\n     * Delete command by given command id\n     *\n     * @param commandId\n     *        identifier of command which will be deleted\n     * @throws SCommandNotFoundException\n     *         Error thrown if no command have name corresponding to the parameter.\n     * @throws SCommandDeletionException\n     *         Error thrown if has exception during the deleting command.\n     * @author Yanyan Liu\n     */\n    void delete(long commandId) throws SCommandNotFoundException, SCommandDeletionException;\n\n    /**\n     * Get total number of commands according to the specific criteria\n     *\n     * @param options\n     *        search criteria\n     * @return total number of commands corresponding to the specific criteria\n     * @throws SBonitaReadException\n     * @author Yanyan Liu\n     */\n    long getNumberOfCommands(QueryOptions options) throws SBonitaReadException;\n\n    /**\n     * Get a list of commands according to the specific criteria\n     *\n     * @param options\n     *        search criteria\n     * @return a list of command objects\n     * @throws SBonitaReadException\n     * @author Yanyan Liu\n     */\n    List<SCommand> searchCommands(QueryOptions options) throws SBonitaReadException;\n\n}\n"
  },
  {
    "path": "services/bonita-command/src/main/java/org/bonitasoft/engine/command/SCommandAlreadyExistsException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SCommandAlreadyExistsException extends SBonitaException {\n\n    private static final long serialVersionUID = -3017980604615886777L;\n\n    public SCommandAlreadyExistsException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SCommandAlreadyExistsException(final String message) {\n        super(message);\n    }\n\n    public SCommandAlreadyExistsException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-command/src/main/java/org/bonitasoft/engine/command/SCommandCreationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SCommandCreationException extends SBonitaException {\n\n    private static final long serialVersionUID = 634050853254691398L;\n\n    public SCommandCreationException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SCommandCreationException(final String message) {\n        super(message);\n    }\n\n    public SCommandCreationException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-command/src/main/java/org/bonitasoft/engine/command/SCommandDeletionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SCommandDeletionException extends SBonitaException {\n\n    private static final long serialVersionUID = -1160779784480102515L;\n\n    public SCommandDeletionException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SCommandDeletionException(final String message) {\n        super(message);\n    }\n\n    public SCommandDeletionException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-command/src/main/java/org/bonitasoft/engine/command/SCommandGettingException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SCommandGettingException extends SBonitaException {\n\n    private static final long serialVersionUID = -4051373026112211800L;\n\n    public SCommandGettingException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SCommandGettingException(final String message) {\n        super(message);\n    }\n\n    public SCommandGettingException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-command/src/main/java/org/bonitasoft/engine/command/SCommandNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SCommandNotFoundException extends SBonitaException {\n\n    private static final long serialVersionUID = -3017980604615886777L;\n\n    public SCommandNotFoundException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SCommandNotFoundException(final String message) {\n        super(message);\n    }\n\n    public SCommandNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-command/src/main/java/org/bonitasoft/engine/command/SCommandUpdateException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SCommandUpdateException extends SBonitaException {\n\n    private static final long serialVersionUID = 8667781514957065049L;\n\n    public SCommandUpdateException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SCommandUpdateException(final String message) {\n        super(message);\n    }\n\n    public SCommandUpdateException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-command/src/main/java/org/bonitasoft/engine/command/api/impl/CommandDeployment.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.api.impl;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Data\n@NoArgsConstructor\npublic class CommandDeployment {\n\n    private String name;\n    private String description;\n    private String implementation;\n\n    public CommandDeployment(final String name, final String description, final String implementation) {\n        this.name = name;\n        this.description = description;\n        this.implementation = implementation;\n    }\n}\n"
  },
  {
    "path": "services/bonita-command/src/main/java/org/bonitasoft/engine/command/api/impl/CommandProvider.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.api.impl;\n\nimport java.util.Collections;\nimport java.util.List;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class CommandProvider {\n\n    private List<CommandDeployment> defaultCommands;\n\n    public void setDefaultCommands(final List<CommandDeployment> defaultCommands) {\n        this.defaultCommands = defaultCommands;\n    }\n\n    public List<CommandDeployment> getDefaultCommands() {\n        if (defaultCommands == null) {\n            return Collections.emptyList();\n        }\n        return defaultCommands;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-command/src/main/java/org/bonitasoft/engine/command/api/impl/CommandServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.api.impl;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.command.CommandService;\nimport org.bonitasoft.engine.command.SCommandAlreadyExistsException;\nimport org.bonitasoft.engine.command.SCommandCreationException;\nimport org.bonitasoft.engine.command.SCommandDeletionException;\nimport org.bonitasoft.engine.command.SCommandGettingException;\nimport org.bonitasoft.engine.command.SCommandNotFoundException;\nimport org.bonitasoft.engine.command.SCommandUpdateException;\nimport org.bonitasoft.engine.command.api.record.SelectDescriptorBuilder;\nimport org.bonitasoft.engine.command.model.SCommand;\nimport org.bonitasoft.engine.command.model.SCommandCriterion;\nimport org.bonitasoft.engine.command.model.SCommandLogBuilder;\nimport org.bonitasoft.engine.command.model.SCommandLogBuilderFactory;\nimport org.bonitasoft.engine.command.model.SCommandUpdateBuilderImpl;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity;\nimport org.bonitasoft.engine.queriablelogger.model.builder.ActionType;\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\n\n/**\n * @author Zhang Bole\n * @author Matthieu Chaffotte\n * @author Hongwen Zang\n * @author Celine Souchet\n */\n@Slf4j\npublic class CommandServiceImpl implements CommandService {\n\n    public static final int FETCH_SIZE = 1000;\n    private final ReadPersistenceService persistenceService;\n\n    private final Recorder recorder;\n\n    private final EventService eventService;\n\n    private final QueriableLoggerService queriableLoggerService;\n    private final CommandProvider defaultCommandProvider;\n    private final int fetchSize;\n\n    public CommandServiceImpl(final ReadPersistenceService persistenceService, final Recorder recorder,\n            final EventService eventService, final QueriableLoggerService queriableLoggerService,\n            CommandProvider defaultCommandProvider) {\n        this(persistenceService, recorder, eventService, queriableLoggerService, defaultCommandProvider,\n                FETCH_SIZE);\n    }\n\n    public CommandServiceImpl(final ReadPersistenceService persistenceService, final Recorder recorder,\n            final EventService eventService, final QueriableLoggerService queriableLoggerService,\n            CommandProvider defaultCommandProvider, int fetchSize) {\n        super();\n        this.persistenceService = persistenceService;\n        this.recorder = recorder;\n        this.eventService = eventService;\n        this.queriableLoggerService = queriableLoggerService;\n        this.defaultCommandProvider = defaultCommandProvider;\n        this.fetchSize = fetchSize;\n    }\n\n    private SCommandLogBuilder getQueriableLog(final ActionType actionType, final String message) {\n        final SCommandLogBuilder logBuilder = BuilderFactory.get(SCommandLogBuilderFactory.class).createNewInstance();\n        this.initializeLogBuilder(logBuilder, message);\n        this.updateLog(actionType, logBuilder);\n        return logBuilder;\n    }\n\n    private <T extends SLogBuilder> void initializeLogBuilder(final T logBuilder, final String message) {\n        logBuilder.actionStatus(SQueriableLog.STATUS_FAIL).severity(SQueriableLogSeverity.INTERNAL).rawMessage(message);\n    }\n\n    private <T extends HasCRUDEAction> void updateLog(final ActionType actionType, final T logBuilder) {\n        logBuilder.setActionType(actionType);\n    }\n\n    @Override\n    public void create(final SCommand command) throws SCommandAlreadyExistsException, SCommandCreationException {\n        try {\n            this.get(command.getName());\n            throw new SCommandAlreadyExistsException(\"Command '\" + command.getName() + \"' already exists\");\n        } catch (final SCommandNotFoundException e) {\n            final SCommandLogBuilder logBuilder = getQueriableLog(ActionType.CREATED,\n                    \"Creating a new command with name \" + command.getName());\n            try {\n                recorder.recordInsert(new InsertRecord(command), COMMAND);\n                log(command.getId(), SQueriableLog.STATUS_OK, logBuilder, \"create\");\n            } catch (final SRecorderException re) {\n                log(command.getId(), SQueriableLog.STATUS_FAIL, logBuilder, \"create\");\n                throw new SCommandCreationException(re);\n            }\n        }\n    }\n\n    @Override\n    public void delete(final long commandId) throws SCommandNotFoundException, SCommandDeletionException {\n        final SCommandLogBuilder logBuilder = getQueriableLog(ActionType.DELETED,\n                \"Deleting command with id \" + commandId);\n        final SCommand command = this.get(commandId);\n        delete(command, logBuilder);\n    }\n\n    protected void delete(final SCommand command, final SCommandLogBuilder logBuilder)\n            throws SCommandDeletionException {\n        try {\n            recorder.recordDelete(new DeleteRecord(command), COMMAND);\n            log(command.getId(), SQueriableLog.STATUS_OK, logBuilder, \"delete\");\n        } catch (final SRecorderException re) {\n            log(command.getId(), SQueriableLog.STATUS_FAIL, logBuilder, \"delete\");\n            throw new SCommandDeletionException(re);\n        }\n    }\n\n    @Override\n    public void delete(final String commandName) throws SCommandNotFoundException, SCommandDeletionException {\n        final SCommandLogBuilder logBuilder = getQueriableLog(ActionType.DELETED,\n                \"Deleting command with name \" + commandName);\n        final SCommand command = this.get(commandName);\n        delete(command, logBuilder);\n    }\n\n    @Override\n    public void deleteAll() throws SCommandDeletionException {\n        List<SCommand> commands;\n        try {\n            do {\n                commands = getAllCommands(0, 1000, SCommandCriterion.NAME_ASC);\n                for (final SCommand command : commands) {\n                    final SCommandLogBuilder logBuilder = getQueriableLog(ActionType.DELETED,\n                            \"Deleting command with name \" + command.getName());\n                    delete(command, logBuilder);\n                }\n            } while (!commands.isEmpty());\n        } catch (final SCommandGettingException scge) {\n            throw new SCommandDeletionException(scge);\n        }\n    }\n\n    @Override\n    public SCommand get(final String commandName) throws SCommandNotFoundException {\n        try {\n            final SelectOneDescriptor<SCommand> descriptor = SelectDescriptorBuilder.getCommandByName(commandName);\n            final SCommand scommand = persistenceService.selectOne(descriptor);\n            if (scommand == null) {\n                throw new SCommandNotFoundException(\"command '\" + commandName + \"' does not exist\");\n            }\n            return scommand;\n        } catch (final SBonitaReadException e) {\n            throw new SCommandNotFoundException(\"Cannot get command: \" + commandName, e);\n        }\n    }\n\n    @Override\n    public List<SCommand> getAllCommands(final int startIndex, final int maxResults,\n            final SCommandCriterion sCommandCriterion) throws SCommandGettingException {\n        OrderByType orderByType;\n        switch (sCommandCriterion) {\n            case NAME_ASC:\n                orderByType = OrderByType.ASC;\n                break;\n            case NAME_DESC:\n                orderByType = OrderByType.DESC;\n                break;\n            default:\n                throw new IllegalStateException();\n        }\n        try {\n            return persistenceService.selectList(SelectDescriptorBuilder.getCommands(\"name\", orderByType,\n                    startIndex, maxResults));\n        } catch (final SBonitaReadException e) {\n            throw new SCommandGettingException(\"can't get the commands\", e);\n        }\n    }\n\n    @Override\n    public void update(final SCommand command, final EntityUpdateDescriptor updateDescriptor)\n            throws SCommandUpdateException {\n        final SCommandLogBuilder logBuilder = getQueriableLog(ActionType.UPDATED,\n                \"Updating command with name \" + command.getName());\n        try {\n            recorder.recordUpdate(UpdateRecord.buildSetFields(command, updateDescriptor), COMMAND);\n            log(command.getId(), SQueriableLog.STATUS_OK, logBuilder, \"update\");\n        } catch (final SRecorderException re) {\n            log(command.getId(), SQueriableLog.STATUS_FAIL, logBuilder, \"update\");\n            throw new SCommandUpdateException(re);\n        }\n    }\n\n    @Override\n    public List<SCommand> getUserCommands(final int startIndex, final int maxResults,\n            final SCommandCriterion sCommandCriterion)\n            throws SCommandGettingException {\n        OrderByType orderByType;\n        switch (sCommandCriterion) {\n            case NAME_ASC:\n                orderByType = OrderByType.ASC;\n                break;\n            case NAME_DESC:\n                orderByType = OrderByType.DESC;\n                break;\n            default:\n                throw new IllegalStateException();\n        }\n        try {\n            return persistenceService.selectList(SelectDescriptorBuilder.getUserCommands(\"name\",\n                    orderByType, startIndex, maxResults));\n        } catch (final SBonitaReadException e) {\n            throw new SCommandGettingException(\"can't get the commands\", e);\n        }\n    }\n\n    @Override\n    public SCommand get(final long commandId) throws SCommandNotFoundException {\n        final SelectByIdDescriptor<SCommand> selectByIdDescriptor = SelectDescriptorBuilder.getCommandById(commandId);\n        try {\n            final SCommand command = persistenceService.selectById(selectByIdDescriptor);\n            if (command == null) {\n                throw new SCommandNotFoundException(commandId + \" does not refer to any command\");\n            }\n            return command;\n        } catch (final SBonitaReadException bre) {\n            throw new SCommandNotFoundException(bre);\n        }\n    }\n\n    @Override\n    public long getNumberOfCommands(final QueryOptions options) throws SBonitaReadException {\n        return persistenceService.getNumberOfEntities(SCommand.class, options, null);\n    }\n\n    @Override\n    public List<SCommand> searchCommands(final QueryOptions options) throws SBonitaReadException {\n        return persistenceService.searchEntity(SCommand.class, options, null);\n    }\n\n    private void log(final long objectId, final int sQueriableLogStatus, final SPersistenceLogBuilder logBuilder,\n            final String callerClassName) {\n        logBuilder.actionScope(String.valueOf(objectId));\n        logBuilder.actionStatus(sQueriableLogStatus);\n        logBuilder.objectId(objectId);\n        final SQueriableLog log = logBuilder.build();\n        if (queriableLoggerService.isLoggable(log.getActionType(), log.getSeverity())) {\n            queriableLoggerService.log(this.getClass().getName(), callerClassName, log);\n        }\n    }\n\n    @Override\n    public void start() throws SBonitaException {\n        Map<String, CommandDeployment> commandDeployments = toCommandDeploymentMap(\n                defaultCommandProvider.getDefaultCommands());\n        Map<String, SCommand> availableSystemCommands = getAllAvailableSystemCommands();\n        createAndUpdateCommands(commandDeployments, availableSystemCommands);\n        deleteSystemCommandsNotPresentInDeployments(commandDeployments, availableSystemCommands);\n    }\n\n    private void createAndUpdateCommands(final Map<String, CommandDeployment> commandDeployements,\n            final Map<String, SCommand> availableSystemCommands) throws SBonitaException {\n        for (String commandName : commandDeployements.keySet()) {\n            if (!availableSystemCommands.containsKey(commandName)) {\n                createMissingCommand(commandDeployements.get(commandName));\n            } else {\n                SCommand sCommand = availableSystemCommands.get(commandName);\n                updateExistingCommandIfNecessary(sCommand, commandDeployements.get(commandName));\n            }\n        }\n    }\n\n    private void updateExistingCommandIfNecessary(final SCommand command, final CommandDeployment commandDeployment)\n            throws SCommandUpdateException {\n        SCommandUpdateBuilderImpl updateBuilder = new SCommandUpdateBuilderImpl();\n        if (!command.getDescription().equals(commandDeployment.getDescription())) {\n            updateBuilder.updateDescription(commandDeployment.getDescription());\n        }\n        if (!command.getImplementation().equals(commandDeployment.getImplementation())) {\n            updateBuilder.updateImplementation(commandDeployment.getImplementation());\n        }\n        EntityUpdateDescriptor updateDescriptor = updateBuilder.done();\n        if (!updateDescriptor.getFields().isEmpty()) {\n            if (log.isInfoEnabled()) {\n                log.info(\"Updating system command. Before update: {}. After update: {}\", command, commandDeployment);\n            }\n            update(command, updateDescriptor);\n        }\n    }\n\n    private void createMissingCommand(final CommandDeployment commandDeployment)\n            throws SCommandAlreadyExistsException,\n            SCommandCreationException {\n        log.debug(\"Creating missing system command: {}\", commandDeployment);\n\n        create(SCommand.builder()\n                .name(commandDeployment.getName())\n                .description(commandDeployment.getDescription())\n                .implementation(commandDeployment.getImplementation())\n                .isSystem(true).build());\n    }\n\n    private void deleteSystemCommandsNotPresentInDeployments(final Map<String, CommandDeployment> commandDeployments,\n            final Map<String, SCommand> availableSystemCommands) throws SCommandDeletionException {\n\n        for (String commandName : availableSystemCommands.keySet()) {\n            if (!commandDeployments.containsKey(commandName)) {\n                final SCommandLogBuilder logBuilder = getQueriableLog(ActionType.DELETED,\n                        \"Deleting command with name \" + commandName);\n                SCommand command = availableSystemCommands.get(commandName);\n                if (log.isInfoEnabled()) {\n                    log.info(\"The following system command is not used any more and will be deleted: {}\", command);\n                }\n                delete(command, logBuilder);\n            }\n        }\n    }\n\n    private Map<String, SCommand> getAllAvailableSystemCommands() throws SBonitaReadException {\n        Map<String, SCommand> commands = new HashMap<>();\n        List<SCommand> currentPage;\n        int fromIndex = 0;\n        do {\n            currentPage = getSystemCommands(fromIndex, fetchSize);\n            commands.putAll(toCommandMap(getSystemCommands(fromIndex, fetchSize)));\n            fromIndex += fetchSize;\n        } while (currentPage.size() == fetchSize);\n        return commands;\n    }\n\n    private List<SCommand> getSystemCommands(int fromIndex, int maxResults) throws SBonitaReadException {\n        QueryOptions queryOptions = new QueryOptions(fromIndex, maxResults,\n                Collections.singletonList(new OrderByOption(SCommand.class, SCommand.ID,\n                        OrderByType.ASC)),\n                Collections.singletonList(new FilterOption(SCommand.class, SCommand.SYSTEM, true)), null);\n        return searchCommands(queryOptions);\n    }\n\n    private Map<String, SCommand> toCommandMap(List<SCommand> commands) {\n        Map<String, SCommand> map = new HashMap<>(commands.size());\n        for (SCommand command : commands) {\n            map.put(command.getName(), command);\n        }\n        return map;\n    }\n\n    private Map<String, CommandDeployment> toCommandDeploymentMap(List<CommandDeployment> commandDeployments) {\n        Map<String, CommandDeployment> map = new HashMap<>(commandDeployments.size());\n        for (CommandDeployment deployment : commandDeployments) {\n            map.put(deployment.getName(), deployment);\n        }\n        return map;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-command/src/main/java/org/bonitasoft/engine/command/api/record/SelectDescriptorBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.api.record;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.command.model.SCommand;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\n\n/**\n * @author Bole Zhang\n * @author Matthieu Chaffotte\n * @author Emmanuel Duchastenier\n */\npublic class SelectDescriptorBuilder {\n\n    public static SelectOneDescriptor<SCommand> getCommandByName(final String commandName) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"name\", commandName);\n        return new SelectOneDescriptor<>(\"getCommandByName\", parameters, SCommand.class);\n    }\n\n    public static SelectListDescriptor<SCommand> getCommands(final String field, final OrderByType order,\n            final int fromIndex, final int numberOfElements) {\n        final Map<String, Object> parameters = Collections.emptyMap();\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfElements, SCommand.class, field, order);\n        return new SelectListDescriptor<>(\"getCommands\", parameters, SCommand.class, queryOptions);\n    }\n\n    public static SelectListDescriptor<SCommand> getUserCommands(final String field, final OrderByType order,\n            final int fromIndex, final int numberOfElements) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"isSystem\", false);\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfElements, SCommand.class, field, order);\n        return new SelectListDescriptor<>(\"getUserCommands\", parameters, SCommand.class, queryOptions);\n    }\n\n    public static SelectByIdDescriptor<SCommand> getCommandById(final long commandId) {\n        return new SelectByIdDescriptor<>(SCommand.class, commandId);\n    }\n}\n"
  },
  {
    "path": "services/bonita-command/src/main/java/org/bonitasoft/engine/command/model/CommandUpdateBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.model;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface CommandUpdateBuilder {\n\n}\n"
  },
  {
    "path": "services/bonita-command/src/main/java/org/bonitasoft/engine/command/model/SCommand.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.model;\n\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@Builder\n@Entity\n@Table(name = \"command\")\npublic class SCommand implements PersistentObject {\n\n    public static final String ID = \"id\";\n    public static final String NAME = \"name\";\n    public static final String DESCRIPTION = \"description\";\n    public static final String IMPLEMENTATION = \"implementation\";\n    public static final String SYSTEM = \"isSystem\";\n    @Id\n    private long id;\n    @Column\n    private boolean isSystem;\n    @Column\n    private String name;\n    @Column\n    private String description;\n    @Column\n    private String implementation;\n}\n"
  },
  {
    "path": "services/bonita-command/src/main/java/org/bonitasoft/engine/command/model/SCommandCriterion.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.model;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic enum SCommandCriterion {\n\n    NAME_ASC, NAME_DESC;\n\n}\n"
  },
  {
    "path": "services/bonita-command/src/main/java/org/bonitasoft/engine/command/model/SCommandLogBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.model;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface SCommandLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction {\n\n}\n"
  },
  {
    "path": "services/bonita-command/src/main/java/org/bonitasoft/engine/command/model/SCommandLogBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.model;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface SCommandLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory {\n\n    SCommandLogBuilder createNewInstance();\n\n}\n"
  },
  {
    "path": "services/bonita-command/src/main/java/org/bonitasoft/engine/command/model/SCommandLogBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.model;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SCommandLogBuilderFactoryImpl extends CRUDELogBuilderFactory implements SCommandLogBuilderFactory {\n\n    @Override\n    public SCommandLogBuilder createNewInstance() {\n        return new SCommandLogBuilderImpl();\n    }\n\n    @Override\n    public String getObjectIdKey() {\n        return SCommandLogIndexesMapper.COMMAND_INDEX_NAME;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-command/src/main/java/org/bonitasoft/engine/command/model/SCommandLogBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.model;\n\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SCommandLogBuilderImpl extends CRUDELogBuilder implements SCommandLogBuilder {\n\n    @Override\n    public SPersistenceLogBuilder objectId(final long objectId) {\n        this.queriableLogBuilder.numericIndex(SCommandLogIndexesMapper.COMMAND_INDEX, objectId);\n        return this;\n    }\n\n    @Override\n    protected String getActionTypePrefix() {\n        return \"COMMAND\";\n    }\n\n    @Override\n    protected void checkExtraRules(final SQueriableLog log) {\n        if (log.getActionStatus() != SQueriableLog.STATUS_FAIL) {\n            if (log.getNumericIndex(SCommandLogIndexesMapper.COMMAND_INDEX) == 0L) {\n                throw new MissingMandatoryFieldsException(\"Some mandatory fields are missing: command id \");\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-command/src/main/java/org/bonitasoft/engine/command/model/SCommandLogIndexesMapper.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.model;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SCommandLogIndexesMapper {\n\n    public static final int COMMAND_INDEX = 0;\n\n    public static final String COMMAND_INDEX_NAME = \"numericIndex1\";\n\n}\n"
  },
  {
    "path": "services/bonita-command/src/main/java/org/bonitasoft/engine/command/model/SCommandUpdateBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.model;\n\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface SCommandUpdateBuilder {\n\n    SCommandUpdateBuilder updateName(String name);\n\n    SCommandUpdateBuilder updateDescription(String description);\n\n    SCommandUpdateBuilder updateImplementation(String implementation);\n\n    EntityUpdateDescriptor done();\n\n}\n"
  },
  {
    "path": "services/bonita-command/src/main/java/org/bonitasoft/engine/command/model/SCommandUpdateBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.model;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface SCommandUpdateBuilderFactory {\n\n    SCommandUpdateBuilder createNewInstance();\n\n}\n"
  },
  {
    "path": "services/bonita-command/src/main/java/org/bonitasoft/engine/command/model/SCommandUpdateBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.model;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SCommandUpdateBuilderFactoryImpl implements SCommandUpdateBuilderFactory {\n\n    public SCommandUpdateBuilder createNewInstance() {\n        return new SCommandUpdateBuilderImpl();\n    }\n}\n"
  },
  {
    "path": "services/bonita-command/src/main/java/org/bonitasoft/engine/command/model/SCommandUpdateBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.model;\n\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SCommandUpdateBuilderImpl implements SCommandUpdateBuilder {\n\n    private final EntityUpdateDescriptor descriptor;\n\n    public SCommandUpdateBuilderImpl() {\n        super();\n        descriptor = new EntityUpdateDescriptor();\n    }\n\n    @Override\n    public SCommandUpdateBuilder updateName(final String name) {\n        this.descriptor.addField(SCommand.NAME, name);\n        return this;\n    }\n\n    @Override\n    public SCommandUpdateBuilder updateDescription(final String description) {\n        this.descriptor.addField(SCommand.DESCRIPTION, description);\n        return this;\n    }\n\n    @Override\n    public SCommandUpdateBuilder updateImplementation(final String implementation) {\n        descriptor.addField(SCommand.IMPLEMENTATION, implementation);\n        return this;\n    }\n\n    @Override\n    public EntityUpdateDescriptor done() {\n        return this.descriptor;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-command/src/main/resources/org/bonitasoft/engine/command/model/impl/hibernate/command.queries.hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\" \"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd\">\n<hibernate-mapping auto-import=\"false\">\n\n\n\t<query name=\"getCommandByName\">\n\t\tSELECT command\n\t\tFROM org.bonitasoft.engine.command.model.SCommand AS command\n\t\tWHERE command.name = :name\n\t</query>\n\n\t<query name=\"getCommands\">\n\t\tSELECT command\n\t\tFROM org.bonitasoft.engine.command.model.SCommand AS command\n\t</query>\n\n\t<query name=\"getUserCommands\">\n\t\tSELECT command\n\t\tFROM org.bonitasoft.engine.command.model.SCommand AS command\n\t\tWHERE command.isSystem = :isSystem\n\t</query>\n\t\n\t<query name=\"getNumberOfSCommand\">\n\t\tSELECT COUNT(*)\n\t\tFROM org.bonitasoft.engine.command.model.SCommand AS command\n\t</query>\n\t\n\t<query name=\"searchSCommand\">\n\t\tSELECT command\n\t\tFROM org.bonitasoft.engine.command.model.SCommand AS command\n\t</query>\n\t\n</hibernate-mapping>\n"
  },
  {
    "path": "services/bonita-command/src/test/java/org/bonitasoft/engine/command/api/impl/CommandServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.api.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.*;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.command.SCommandGettingException;\nimport org.bonitasoft.engine.command.SCommandNotFoundException;\nimport org.bonitasoft.engine.command.api.record.SelectDescriptorBuilder;\nimport org.bonitasoft.engine.command.comparator.CommandComparator;\nimport org.bonitasoft.engine.command.model.SCommand;\nimport org.bonitasoft.engine.command.model.SCommandCriterion;\nimport org.bonitasoft.engine.command.model.SCommandLogBuilder;\nimport org.bonitasoft.engine.command.model.SCommandUpdateBuilderImpl;\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Celine Souchet\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class CommandServiceImplTest {\n\n    public static final int FETCH_SIZE = 2;\n    @Mock\n    private Recorder recorder;\n\n    @Mock\n    private ReadPersistenceService persistence;\n\n    @Mock\n    private EventService eventService;\n\n    @Mock\n    private QueriableLoggerService queriableLoggerService;\n\n    @Mock\n    private CommandProvider commandProvider;\n\n    private CommandServiceImpl commandServiceImpl;\n\n    @Before\n    public final void setUp() {\n        commandServiceImpl = new CommandServiceImpl(persistence, recorder, eventService, queriableLoggerService,\n                commandProvider, FETCH_SIZE);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.command.api.impl.CommandServiceImpl#getAllCommands(int, int, org.bonitasoft.engine.command.model.SCommandCriterion)}.\n     *\n     * @throws SCommandGettingException\n     * @throws SBonitaReadException\n     */\n    @Test\n    public final void getAllCommands() throws SCommandGettingException, SBonitaReadException {\n        // Given\n        final List<SCommand> sCommands = new ArrayList<SCommand>();\n        final String field = \"name\";\n        final OrderByType orderByType = OrderByType.ASC;\n        final int startIndex = 0;\n        final int maxResults = 1;\n        when(persistence.selectList(SelectDescriptorBuilder.getCommands(field, orderByType, startIndex, maxResults)))\n                .thenReturn(sCommands);\n\n        // When\n        final List<SCommand> allCommands = commandServiceImpl.getAllCommands(startIndex, maxResults,\n                SCommandCriterion.NAME_ASC);\n\n        // Then\n        Assert.assertEquals(sCommands, allCommands);\n    }\n\n    @Test(expected = SCommandGettingException.class)\n    public final void getAllCommandsThrowException() throws SCommandGettingException, SBonitaReadException {\n        // Given\n        final String field = \"name\";\n        final OrderByType orderByType = OrderByType.ASC;\n        final int startIndex = 0;\n        final int maxResults = 1;\n        when(persistence.selectList(SelectDescriptorBuilder.getCommands(field, orderByType, startIndex, maxResults)))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        // When\n        commandServiceImpl.getAllCommands(startIndex, maxResults, SCommandCriterion.NAME_DESC);\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.command.api.impl.CommandServiceImpl#get(long)}.\n     *\n     * @throws SCommandNotFoundException\n     * @throws SBonitaReadException\n     */\n    @Test\n    public final void getById() throws SCommandNotFoundException, SBonitaReadException {\n        // Given\n        final SCommand sCommand = mock(SCommand.class);\n        final long commandId = 456L;\n        when(persistence.selectById(SelectDescriptorBuilder.getCommandById(commandId))).thenReturn(sCommand);\n\n        // When\n        final SCommand actual = commandServiceImpl.get(commandId);\n\n        // Then\n        Assert.assertEquals(sCommand, actual);\n    }\n\n    @Test(expected = SCommandNotFoundException.class)\n    public final void getByIdNotExists() throws SBonitaReadException, SCommandNotFoundException {\n        // Given\n        final long commandId = 456L;\n        when(persistence.selectById(SelectDescriptorBuilder.getCommandById(commandId))).thenReturn(null);\n\n        // When\n        commandServiceImpl.get(commandId);\n    }\n\n    @Test(expected = SCommandNotFoundException.class)\n    public final void getByIdThrowException() throws SBonitaReadException, SCommandNotFoundException {\n        // Given\n        final long commandId = 456L;\n        when(persistence.selectById(SelectDescriptorBuilder.getCommandById(commandId)))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        // When\n        commandServiceImpl.get(commandId);\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.command.api.impl.CommandServiceImpl#get(java.lang.String)}.\n     *\n     * @throws SCommandNotFoundException\n     * @throws SBonitaReadException\n     */\n    @Test\n    public final void getByName() throws SCommandNotFoundException, SBonitaReadException {\n        // Given\n        final SCommand sCommand = mock(SCommand.class);\n        final String commandName = \"name\";\n        when(persistence.selectOne(SelectDescriptorBuilder.getCommandByName(commandName))).thenReturn(sCommand);\n\n        // When\n        final SCommand actual = commandServiceImpl.get(commandName);\n\n        // Then\n        Assert.assertEquals(sCommand, actual);\n    }\n\n    @Test(expected = SCommandNotFoundException.class)\n    public final void getByNameNotExists() throws SBonitaReadException, SCommandNotFoundException {\n        // Given\n        final String commandName = \"name\";\n        when(persistence.selectOne(SelectDescriptorBuilder.getCommandByName(commandName))).thenReturn(null);\n\n        // When\n        commandServiceImpl.get(commandName);\n    }\n\n    @Test(expected = SCommandNotFoundException.class)\n    public final void getByNameThrowException() throws SBonitaReadException, SCommandNotFoundException {\n        // Given\n        final String commandName = \"name\";\n        when(persistence.selectOne(SelectDescriptorBuilder.getCommandByName(commandName)))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        // When\n        commandServiceImpl.get(commandName);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.command.api.impl.CommandServiceImpl#getNumberOfCommands(org.bonitasoft.engine.persistence.QueryOptions)}.\n     *\n     * @throws SBonitaReadException\n     * @throws SBonitaReadException\n     */\n    @Test\n    public final void getNumberOfCommands() throws SBonitaReadException {\n        // Given\n        final long numberOfCommands = 54165L;\n        final QueryOptions options = mock(QueryOptions.class);\n        when(persistence.getNumberOfEntities(SCommand.class, options, null)).thenReturn(numberOfCommands);\n\n        // When\n        final long result = commandServiceImpl.getNumberOfCommands(options);\n\n        // Then\n        Assert.assertEquals(numberOfCommands, result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public final void getNumberOfCommandsThrowException() throws SBonitaReadException {\n        // Given\n        final QueryOptions options = mock(QueryOptions.class);\n        when(persistence.getNumberOfEntities(SCommand.class, options, null)).thenThrow(new SBonitaReadException(\"\"));\n\n        // When\n        commandServiceImpl.getNumberOfCommands(options);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.command.api.impl.CommandServiceImpl#getUserCommands(int, int, org.bonitasoft.engine.command.model.SCommandCriterion)}.\n     *\n     * @throws SCommandGettingException\n     * @throws SBonitaReadException\n     */\n    @Test\n    public final void getUserCommands() throws SCommandGettingException, SBonitaReadException {\n        // Given\n        final List<SCommand> sCommands = new ArrayList<SCommand>();\n        final String field = \"name\";\n        final OrderByType orderByType = OrderByType.ASC;\n        final int startIndex = 0;\n        final int maxResults = 1;\n        when(persistence\n                .selectList(SelectDescriptorBuilder.getUserCommands(field, orderByType, startIndex, maxResults)))\n                .thenReturn(sCommands);\n\n        // When\n        final List<SCommand> userCommands = commandServiceImpl.getUserCommands(startIndex, maxResults,\n                SCommandCriterion.NAME_ASC);\n\n        // Then\n        Assert.assertEquals(sCommands, userCommands);\n    }\n\n    @Test(expected = SCommandGettingException.class)\n    public final void getUserCommandsThrowException() throws SCommandGettingException, SBonitaReadException {\n        // Given\n        final String field = \"name\";\n        final OrderByType orderByType = OrderByType.ASC;\n        final int startIndex = 0;\n        final int maxResults = 1;\n        when(persistence\n                .selectList(SelectDescriptorBuilder.getUserCommands(field, orderByType, startIndex, maxResults)))\n                .thenThrow(\n                        new SBonitaReadException(\"\"));\n\n        // When\n        commandServiceImpl.getUserCommands(startIndex, maxResults, SCommandCriterion.NAME_DESC);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.command.api.impl.CommandServiceImpl#searchCommands(org.bonitasoft.engine.persistence.QueryOptions)}.\n     *\n     * @throws SBonitaReadException\n     * @throws SBonitaReadException\n     */\n    @Test\n    public final void searchCommands() throws SBonitaReadException {\n        // Given\n        final List<SCommand> sCommands = new ArrayList<SCommand>();\n        final QueryOptions options = mock(QueryOptions.class);\n        when(persistence.searchEntity(SCommand.class, options, null)).thenReturn(sCommands);\n\n        // When\n        final List<SCommand> searchCommands = commandServiceImpl.searchCommands(options);\n\n        // Then\n        Assert.assertEquals(sCommands, searchCommands);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public final void searchCommandsThrowException() throws SBonitaReadException {\n        // Given\n        final QueryOptions options = mock(QueryOptions.class);\n        when(persistence.searchEntity(SCommand.class, options, null)).thenThrow(new SBonitaReadException(\"\"));\n\n        // When\n        commandServiceImpl.searchCommands(options);\n    }\n\n    @Test\n    public void create_should_call_record_insert() throws Exception {\n        //given\n\n        //when\n\n        //then\n    }\n\n    @Test\n    public void start_should_add_missing_system_commands() throws Exception {\n        //given\n        CommandDeployment firstDeploy = new CommandDeployment(\"first\", \"the first command\", \"com.company.FirstCommand\");\n        CommandDeployment secondDeploy = new CommandDeployment(\"second\", \"the second command\",\n                \"com.company.SecondCommand\");\n        CommandDeployment thirdDeploy = new CommandDeployment(\"third\", \"the third command\", \"com.company.ThirdCommand\");\n        CommandDeployment fourthDeploy = new CommandDeployment(\"fourth\", \"the fourth command\",\n                \"com.company.FourthCommand\");\n        CommandDeployment fifthDeploy = new CommandDeployment(\"fifth\", \"the fifth command\", \"com.company.FifthCommand\");\n        CommandDeployment sixthDeploy = new CommandDeployment(\"sixth\", \"the sixth command\", \"com.company.sixthCommand\");\n\n        SCommand first = SCommand.builder().name(\"first\").description(\"the first command\")\n                .implementation(\"com.company.FirstCommand\").build();\n        SCommand second = SCommand.builder().name(\"second\").description(\"the second command\")\n                .implementation(\"com.company.SecondCommand\").build();\n        SCommand third = SCommand.builder().name(\"third\").description(\"the third command\")\n                .implementation(\"com.company.ThirdCommand\").build();\n        SCommand fourth = SCommand.builder().name(\"fourth\").description(\"the fourth command\")\n                .implementation(\"com.company.FourthCommand\").build();\n        SCommand fifth = SCommand.builder().name(\"fifth\").description(\"the fifth command\")\n                .implementation(\"com.company.FifthCommand\").build();\n        SCommand sixth = SCommand.builder().name(\"sixth\").description(\"the sixth command\")\n                .implementation(\"com.company.sixthCommand\").build();\n        given(commandProvider.getDefaultCommands()).willReturn(\n                Arrays.asList(firstDeploy, secondDeploy, thirdDeploy, fourthDeploy, fifthDeploy, sixthDeploy));\n\n        CommandServiceImpl mockedCommandService = spy(commandServiceImpl);\n        given(mockedCommandService.searchCommands(getQueryOptions(0, FETCH_SIZE)))\n                .willReturn(Arrays.asList(first, third));\n        given(mockedCommandService.searchCommands(getQueryOptions(2, FETCH_SIZE))).willReturn(Arrays.asList(sixth));\n        doNothing().when(mockedCommandService).create(any(SCommand.class));\n\n        //when\n        mockedCommandService.start();\n\n        //then\n        ArgumentCaptor<SCommand> commandArgumentCaptor = ArgumentCaptor.forClass(SCommand.class);\n        verify(mockedCommandService, times(3)).create(commandArgumentCaptor.capture());\n        assertThat(commandArgumentCaptor.getAllValues()).usingElementComparator(new CommandComparator())\n                .contains(second, fourth, fifth);\n\n    }\n\n    @Test\n    public void start_should_delete_system_commands_not_present_in_default_commands() throws Exception {\n        //given\n        CommandDeployment secondDeploy = new CommandDeployment(\"second\", \"the second command\",\n                \"com.company.SecondCommand\");\n        CommandDeployment fifthDeploy = new CommandDeployment(\"fifth\", \"the fifth command\", \"com.company.FifthCommand\");\n\n        SCommand first = SCommand.builder().name(\"first\").description(\"the first command\")\n                .implementation(\"com.company.FirstCommand\").build();\n        SCommand second = SCommand.builder().name(\"second\").description(\"the second command\")\n                .implementation(\"com.company.SecondCommand\").build();\n        SCommand third = SCommand.builder().name(\"third\").description(\"the third command\")\n                .implementation(\"com.company.ThirdCommand\").build();\n        SCommand fourth = SCommand.builder().name(\"fourth\").description(\"the fourth command\")\n                .implementation(\"com.company.FourthCommand\").build();\n        SCommand fifth = SCommand.builder().name(\"fifth\").description(\"the fifth command\")\n                .implementation(\"com.company.FifthCommand\").build();\n        given(commandProvider.getDefaultCommands()).willReturn(Arrays.asList(secondDeploy, fifthDeploy));\n\n        CommandServiceImpl mockedCommandService = spy(commandServiceImpl);\n        given(mockedCommandService.searchCommands(getQueryOptions(0, FETCH_SIZE)))\n                .willReturn(Arrays.asList(first, second));\n        given(mockedCommandService.searchCommands(getQueryOptions(2, FETCH_SIZE)))\n                .willReturn(Arrays.asList(third, fourth));\n        given(mockedCommandService.searchCommands(getQueryOptions(4, FETCH_SIZE))).willReturn(Arrays.asList(fifth));\n        doNothing().when(mockedCommandService).delete(any(SCommand.class), any(SCommandLogBuilder.class));\n\n        //when\n        mockedCommandService.start();\n\n        //then\n        ArgumentCaptor<SCommand> commandArgumentCaptor = ArgumentCaptor.forClass(SCommand.class);\n        verify(mockedCommandService, times(3)).delete(commandArgumentCaptor.capture(), any(SCommandLogBuilder.class));\n        assertThat(commandArgumentCaptor.getAllValues()).usingElementComparator(new CommandComparator()).contains(first,\n                third, fourth);\n\n    }\n\n    @Test\n    public void start_should_update_existing_system_commands() throws Exception {\n        //given\n        CommandDeployment firstDeploy = new CommandDeployment(\"first\", \"the first command with a modified description\",\n                \"com.company.FirstCommand\");\n        CommandDeployment secondDeploy = new CommandDeployment(\"second\", \"the second command\",\n                \"com.company.SecondCommand\");\n        CommandDeployment thirdDeploy = new CommandDeployment(\"third\", \"the third command\",\n                \"com.company.ThirdCommandModified\");\n\n        SCommand first = SCommand.builder().name(\"first\").description(\"the first command\")\n                .implementation(\"com.company.FirstCommand\").build();\n        SCommand second = SCommand.builder().name(\"second\").description(\"the second command\")\n                .implementation(\"com.company.SecondCommand\").build();\n        SCommand third = SCommand.builder().name(\"third\").description(\"the third command\")\n                .implementation(\"com.company.ThirdCommand\").build();\n        given(commandProvider.getDefaultCommands()).willReturn(Arrays.asList(firstDeploy, secondDeploy, thirdDeploy));\n\n        CommandServiceImpl mockedCommandService = spy(commandServiceImpl);\n        given(mockedCommandService.searchCommands(getQueryOptions(0, FETCH_SIZE)))\n                .willReturn(Arrays.asList(first, second));\n        given(mockedCommandService.searchCommands(getQueryOptions(2, FETCH_SIZE))).willReturn(Arrays.asList(third));\n        doNothing().when(mockedCommandService).update(any(SCommand.class), any(EntityUpdateDescriptor.class));\n\n        //when\n        mockedCommandService.start();\n\n        //then\n        ArgumentCaptor<SCommand> commandCaptor = ArgumentCaptor.forClass(SCommand.class);\n        ArgumentCaptor<EntityUpdateDescriptor> updateDescriptorCaptor = ArgumentCaptor\n                .forClass(EntityUpdateDescriptor.class);\n        verify(mockedCommandService, times(2)).update(commandCaptor.capture(), updateDescriptorCaptor.capture());\n        assertThat(commandCaptor.getAllValues()).usingElementComparator(new CommandComparator()).contains(first, third);\n        assertThat(updateDescriptorCaptor.getAllValues()).contains(\n                new SCommandUpdateBuilderImpl().updateDescription(\"the first command with a modified description\")\n                        .done(),\n                new SCommandUpdateBuilderImpl().updateImplementation(\"com.company.ThirdCommandModified\").done());\n\n    }\n\n    private QueryOptions getQueryOptions(final int fromIndex, int maxResults) {\n        return new QueryOptions(fromIndex, maxResults,\n                Collections.singletonList(new OrderByOption(SCommand.class, \"id\", OrderByType.ASC)),\n                Collections.singletonList(new FilterOption(SCommand.class, \"isSystem\", true)), null);\n    }\n}\n"
  },
  {
    "path": "services/bonita-command/src/test/java/org/bonitasoft/engine/command/comparator/CommandComparator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.command.comparator;\n\nimport java.util.Comparator;\n\nimport org.bonitasoft.engine.command.model.SCommand;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class CommandComparator implements Comparator<SCommand> {\n\n    @Override\n    public int compare(final SCommand o1, final SCommand o2) {\n        if (o1.getName().equals(o2.getName()) && o1.getDescription().equals(o2.getDescription())\n                && o1.getImplementation().equals(o2.getImplementation())) {\n            return 0;\n        }\n        return 1;\n    }\n}\n"
  },
  {
    "path": "services/bonita-commons/build.gradle",
    "content": "import org.bonitasoft.engine.gradle.PomUtils\n\ndependencies {\n    api project(':bpm:bonita-common')\n    api(libs.commonsBeanUtils) {\n        exclude(module: 'commons-collections')\n    }\n    api libs.commonsIO\n    api libs.jakartaActivation\n    api libs.commonsLang\n    api libs.commonsText\n    api(libs.xstream)\n    api libs.micrometerCore\n    api libs.springContext\n    api libs.springBootAutoconfigure\n    api libs.slf4jApi\n    testImplementation libs.assertj\n    testImplementation libs.mockitoCore\n    testImplementation libs.systemRules\n    testImplementation libs.systemLambda\n    testImplementation libs.logback\n\n    annotationProcessor libs.lombok\n    compileOnly libs.lombok\n}\n\ndescription = 'Bonita Engine Util Classes'\n\ntasks.register(\"sourcesJar\", Jar) {\n    from sourceSets.main.allJava\n    archiveClassifier = 'sources'\n}\n\ntasks.register(\"javadocJar\", Jar) {\n    from javadoc\n    archiveClassifier = 'javadoc'\n}\n\n\npublishing {\n    publications {\n        mavenJava(MavenPublication) {\n            from project.components.java\n            artifact project.sourcesJar\n            artifact project.javadocJar\n            pom { pom ->\n                name = \"Bonita Business Data Generator\"\n                description = 'Bonita Engine Util Classes'\n                PomUtils.pomCommunityPublication(pom)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/DeepRegexFileFilter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\nimport java.io.File;\nimport java.util.regex.Pattern;\n\nimport org.apache.commons.io.filefilter.AbstractFileFilter;\nimport org.bonitasoft.engine.commons.StringUtils;\n\n/**\n * Identical to {@link org.apache.commons.io.filefilter.RegexFileFilter}, but accept files based on the complete file\n * path and name, not only its name.\n *\n * @author Emmanuel Duchastenier\n */\npublic class DeepRegexFileFilter extends AbstractFileFilter {\n\n    /**\n     * The regular expression pattern that will be used to match filenames\n     */\n    private final Pattern regExPattern;\n\n    public DeepRegexFileFilter(final String pattern) {\n        if (pattern == null) {\n            throw new IllegalArgumentException(\"Pattern is missing\");\n        }\n        regExPattern = Pattern.compile(pattern);\n    }\n\n    public DeepRegexFileFilter(final File parentDir, final String pattern) {\n        this(StringUtils.uniformizePathPattern(parentDir.getAbsolutePath()) + \"/\" + pattern);\n    }\n\n    @Override\n    public boolean accept(final File file) {\n        final String fullName = StringUtils.uniformizePathPattern(file.getAbsolutePath());\n        return regExPattern.matcher(fullName).matches();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/Experimental.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\nimport static java.lang.annotation.ElementType.*;\nimport static java.lang.annotation.RetentionPolicy.RUNTIME;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.Target;\n\n/**\n * An experimental SPI that can be used but might change in any version of Bonita.\n * Also no guarantee is provided on the correct behavior of this SPI.\n *\n * @author Baptiste Mesta.\n */\n@Retention(value = RUNTIME)\n@Target(value = { TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, PACKAGE })\npublic @interface Experimental {\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/Profiles.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\npublic class Profiles {\n\n    public static final String CLUSTER = \"cluster\";\n    public static final String NOT_IN_CLUSTER = \"!cluster\";\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/ClassDataUtil.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\nimport org.bonitasoft.engine.commons.io.IOUtil;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ClassDataUtil {\n\n    public static byte[] getClassData(final Class<?> clazz) throws IOException {\n        if (clazz == null) {\n            final String message = \"Class is null\";\n            throw new IOException(message);\n        }\n        final String resource = clazz.getName().replace('.', '/') + \".class\";\n        final InputStream inputStream = clazz.getClassLoader().getResourceAsStream(resource);\n        byte[] data = null;\n        try {\n            if (inputStream == null) {\n                throw new IOException(\n                        \"Impossible to get stream from class: \" + clazz.getName() + \", className= \" + resource);\n            }\n            data = IOUtil.getAllContentFrom(inputStream);\n        } finally {\n            if (inputStream != null) {\n                inputStream.close();\n            }\n        }\n        return data;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/ClassReflector.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Type;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.commons.text.WordUtils;\nimport org.bonitasoft.engine.commons.exceptions.SReflectException;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Laurent Leseigneur\n */\npublic class ClassReflector {\n\n    private static final String EMPTY = \"\";\n\n    private static final String SET = \"set\";\n\n    private static final String IS = \"is\";\n\n    private static final String GET = \"get\";\n\n    private static final Map<String, Method> methods;\n\n    static {\n        methods = new HashMap<>();\n    }\n\n    private static final Object MUTEX = new Object();\n\n    public static Collection<Method> getAccessibleGetters(final Class<?> clazz) {\n        final Collection<Method> methods = new HashSet<>();\n        for (final Method method : clazz.getMethods()) {\n            if (isAGetterMethod(method)) {\n                methods.add(method);\n            }\n        }\n        return methods;\n    }\n\n    public static <T> Class<T> getClass(final Class<T> clazz, final String className) throws SReflectException {\n        try {\n            return (Class<T>) Class.forName(className);\n        } catch (final Exception e) {\n            throw new SReflectException(e);\n        }\n    }\n\n    public static <T> T getObject(final Class<T> clazz, final String className) throws SReflectException {\n        try {\n            return getClass(clazz, className).newInstance();\n        } catch (final Exception e) {\n            throw new SReflectException(e);\n        }\n    }\n\n    public static <T> Constructor<T> getConstructor(final Class<T> clazz, final Class<?>... parameterTypes)\n            throws SReflectException {\n        try {\n            return clazz.getConstructor(parameterTypes);\n        } catch (final Exception e) {\n            throw new SReflectException(e);\n        }\n    }\n\n    public static <T> Constructor<T> getConstructor(final Class<T> clazz, final String className,\n            final Class<?>... parameterTypes) throws SReflectException {\n        try {\n            return getClass(clazz, className).getConstructor(parameterTypes);\n        } catch (final Exception e) {\n            throw new SReflectException(e);\n        }\n    }\n\n    public static <T> T getInstance(final Constructor<T> constructor, final Object... parameters)\n            throws SReflectException {\n        try {\n            return constructor.newInstance(parameters);\n        } catch (final Exception e) {\n            throw new SReflectException(e);\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public static <T> T invokeGetter(final Object entity, final String getterName) throws SReflectException {\n        try {\n            final Method getter = getMethod(entity.getClass(), getterName);\n            return (T) getter.invoke(entity, (Object[]) null);\n        } catch (final Exception e) {\n            throw new SReflectException(e);\n        }\n    }\n\n    public static void invokeSetter(final Object entity, final String setterName, final Class<?> parameterType,\n            final Object parameterValue)\n            throws SReflectException {\n        try {\n            final Method setter = getMethod(entity.getClass(), setterName, parameterType);\n            setter.invoke(entity, parameterValue);\n        } catch (final Exception e) {\n            throw new SReflectException(e);\n        }\n    }\n\n    public static Method getMethod(final Class<?> clazz, final String methodName, final Class<?>... parameterTypes)\n            throws NoSuchMethodException {\n        final StringBuilder stringBuilder = new StringBuilder();\n        stringBuilder.append(clazz.hashCode());\n        stringBuilder.append(':');\n        stringBuilder.append(clazz.getName());\n        stringBuilder.append('.');\n        stringBuilder.append(methodName);\n        stringBuilder.append('(');\n        if (parameterTypes != null) {\n            for (final Class<?> class1 : parameterTypes) {\n                stringBuilder.append(class1.getName());\n                stringBuilder.append(',');\n            }\n        }\n        stringBuilder.append(')');\n        final String key = stringBuilder.toString();\n        putIfAbsent(clazz, methodName, key, parameterTypes);\n        return methods.get(key);\n    }\n\n    private static void putIfAbsent(final Class<?> clazz, final String methodName, final String key,\n            final Class<?>... parameterTypes)\n            throws NoSuchMethodException {\n        if (!methods.containsKey(key)) {\n            synchronized (MUTEX) {\n                // ensure that key was not put before between check and lock\n                if (!methods.containsKey(key)) {\n                    methods.put(key, clazz.getMethod(methodName, parameterTypes));\n                }\n            }\n        }\n    }\n\n    public static Method getMethodByName(final Class<?> clazz, final String methodName) {\n        final String key = clazz.getName() + '.' + methodName;\n        putIfAbsent(clazz, methodName, key);\n        return methods.get(key);\n    }\n\n    private static void putIfAbsent(final Class<?> clazz, final String methodName, final String key) {\n        if (!methods.containsKey(key)) {\n            synchronized (MUTEX) {\n                // ensure that key was not put before between check and lock\n                if (!methods.containsKey(key)) {\n                    final Method method = getFirstMethodWithName(clazz, methodName);\n                    methods.put(key, method);\n                }\n            }\n        }\n    }\n\n    public static Method getFirstMethodWithName(final Class<?> clazz, final String methodName) {\n        Method selectedMethod = null;\n        for (final Method method : clazz.getMethods()) {\n            if (method.getName().equals(methodName)) {\n                selectedMethod = method;\n                break;\n            }\n        }\n        return selectedMethod;\n    }\n\n    public static Object invokeMethodByName(final Object entity, final String methodName,\n            final Object... parameterValues) throws SReflectException {\n        final Class<?> clazz = entity.getClass();\n        // no check on parameters\n        final Method methodToInvoke = getMethodByName(clazz, methodName);\n        if (methodToInvoke == null) {\n            throw new SReflectException(\n                    \"unable to find a method with name '\" + methodName + \"' within class \" + clazz.getName());\n        }\n        try {\n            return methodToInvoke.invoke(entity, parameterValues);\n        } catch (final Exception e) {\n            throw new SReflectException(e);\n        }\n    }\n\n    public static Object invokeMethod(final Object entity, final String methodName, final Class<?> parameterType,\n            final Object parameterValue)\n            throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {\n        final Method method = getMethod(entity.getClass(), methodName, parameterType);\n        return method.invoke(entity, parameterValue);\n    }\n\n    public static Object invokeMethod(final Object entity, final String methodName, final Class<?>[] parameterType,\n            final Object[] parameterValue)\n            throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {\n        final Method method = getMethod(entity.getClass(), methodName, parameterType);\n        return method.invoke(entity, parameterValue);\n    }\n\n    private static boolean isWrapped(final Class<?> a, final Class<?> b) {\n        return a.equals(int.class) && b.equals(Integer.class) || a.equals(double.class) && b.equals(Double.class)\n                || a.equals(boolean.class)\n                        && b.equals(Boolean.class)\n                || a.equals(char.class) && b.equals(Character.class) || a.equals(long.class) && b.equals(Long.class)\n                || a.equals(short.class) && b.equals(Short.class) || a.equals(float.class) && b.equals(Float.class)\n                || a.equals(byte.class)\n                        && b.equals(Byte.class);\n    }\n\n    public static Method getCompatibleMethod(final Class<?> clazz, final String methodName,\n            final Class<?>... paramTypes) throws SReflectException {\n        try {\n            return clazz.getMethod(methodName, paramTypes);\n        } catch (final Exception e) {\n            if (paramTypes != null) {\n                final Method[] methods = clazz.getMethods();\n                for (final Method method : methods) {\n                    if (methodName.equals(method.getName())) {\n                        final Class<?>[] types = method.getParameterTypes();\n                        boolean check = true;\n                        if (!(types.length == paramTypes.length)) {\n                            throw new SReflectException(\"wrong parameters\");\n                        }\n                        for (int i = 0; i < types.length; i++) {\n                            if (!(types[i].isAssignableFrom(paramTypes[i]) || paramTypes[i].isAssignableFrom(types[i])\n                                    || isWrapped(types[i], paramTypes[i]))) {\n                                check = false;\n                                break;\n                            }\n                        }\n                        if (check) {\n                            return method;\n                        }\n                    }\n                }\n            }\n            throw new SReflectException(e);\n        }\n    }\n\n    public static Type getGetterReturnType(final Class<?> classConnector, final String getterName)\n            throws SReflectException {\n        Method m;\n        try {\n            m = getMethod(classConnector, getterName);\n        } catch (final Exception e) {\n            throw new SReflectException(e);\n        }\n        return m.getGenericReturnType();\n    }\n\n    public static Method[] getDeclaredSetters(final Class<?> clazz) {\n        final List<Method> setters = new ArrayList<>();\n        final Method[] methods = clazz.getDeclaredMethods();\n        for (final Method method : methods) {\n            if (isASetterMethod(method)) {\n                setters.add(method);\n            }\n        }\n        return setters.toArray(new Method[setters.size()]);\n    }\n\n    public static Method[] getDeclaredGetters(final Class<?> clazz) {\n        final List<Method> getters = new ArrayList<>();\n        final Method[] methods = clazz.getDeclaredMethods();\n        for (final Method method : methods) {\n            if (isAGetterMethod(method)) {\n                getters.add(method);\n            }\n        }\n        return getters.toArray(new Method[getters.size()]);\n    }\n\n    public static boolean isAGetterMethod(final Method method) {\n        final String methodName = method.getName();\n        return (methodName.startsWith(GET) || methodName.startsWith(IS)) && method.getParameterTypes().length == 0\n                && !Void.class.equals(method.getReturnType());\n    }\n\n    public static boolean isASetterMethod(final Method method) {\n        final String methodName = method.getName();\n        return methodName.startsWith(SET) && \"void\".equals(method.getReturnType().toString())\n                && method.getParameterTypes().length == 1;\n    }\n\n    public static String getGetterName(final String fieldName) {\n        return \"get\" + WordUtils.capitalize(fieldName);\n    }\n\n    public static String getGetterName(final String fieldName, final Class<?> fieldType) {\n        return getGetterPrefix(fieldType) + WordUtils.capitalize(fieldName);\n    }\n\n    private static String getGetterPrefix(Class<?> fieldType) {\n        if (fieldType.isAssignableFrom(Boolean.class)) {\n            return IS;\n        }\n        return GET;\n\n    }\n\n    public static String getFieldName(final String methodName) {\n        int cut = 4;\n        if (methodName.startsWith(IS)) {\n            cut = 3;\n        }\n        if (methodName.length() < cut) {\n            return EMPTY;\n        }\n        final String end = methodName.substring(cut);\n        final char c = methodName.charAt(cut - 1);\n        final String begin = String.valueOf(c).toLowerCase();\n        return begin.concat(end);\n    }\n\n    /**\n     * call a setter by reflection\n     * support pointed notation like pojo.child.name\n     *\n     * @param object\n     *        object on with to call the setter\n     * @param fieldName\n     * @param parameterValue\n     * @throws SReflectException\n     */\n    public static void setField(Object object, String fieldName, Object parameterValue) throws SReflectException {\n        String[] getters = fieldName.split(\"\\\\.\");\n        int i;\n        for (i = 0; i < getters.length - 1; i++) {\n            object = invokeMethodByName(object, getGetterName(getters[i]));\n        }\n\n        invokeMethodByName(object, getSetterName(getters[i]), parameterValue);\n\n    }\n\n    private static String getSetterName(String getter) {\n        return \"set\" + WordUtils.capitalize(getter);\n    }\n\n    public static void clearCache() {\n        methods.clear();\n    }\n\n    public static int getCacheSize() {\n        return methods.size();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/CollectionUtil.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\nimport java.util.stream.IntStream;\n\npublic class CollectionUtil {\n\n    public static Map<String, Object> buildSimpleMap(final String key, final Object value) {\n        final Map<String, Object> result = new HashMap<String, Object>();\n        result.put(key, value);\n        return result;\n    }\n\n    public static <T> List<T> emptyOrUnmodifiable(final List<T> list) {\n        return list == null ? Collections.<T> emptyList() : Collections.unmodifiableList(list);\n    }\n\n    public static <T> List<List<T>> split(List<T> source, int chunkSize) {\n        if (chunkSize <= 0) {\n            throw new IllegalArgumentException(\"can't split a list with a chunkSize = \" + chunkSize);\n        }\n        int size = source.size();\n        if (size <= 0) {\n            return Collections.emptyList();\n        }\n        return IntStream.range(0, size / chunkSize + (size % chunkSize == 0 ? 0 : 1))\n                .mapToObj(n -> source.subList(n * chunkSize, Math.min(size, (n + 1) * chunkSize)))\n                .collect(Collectors.toList());\n    }\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/Container.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons;\n\nimport org.apache.commons.lang3.builder.EqualsBuilder;\nimport org.apache.commons.lang3.builder.HashCodeBuilder;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class Container {\n\n    private long id;\n\n    private String type;\n\n    public Container(final long id, final String type) {\n        this.id = id;\n        this.type = type;\n    }\n\n    public long getId() {\n        return id;\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n\n        if (o == null || getClass() != o.getClass())\n            return false;\n\n        Container container = (Container) o;\n\n        return new EqualsBuilder()\n                .append(id, container.id)\n                .append(type, container.type)\n                .isEquals();\n    }\n\n    @Override\n    public int hashCode() {\n        return new HashCodeBuilder(17, 37)\n                .append(id)\n                .append(type)\n                .toHashCode();\n    }\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/EnumToObjectConvertible.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons;\n\n/**\n * Represent an enum which enum values can be convertible to other types.\n * One method: fromEnum, returns an object value, result of the conversion of that enum\n *\n * @author Emmanuel Duchastenier\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic interface EnumToObjectConvertible {\n\n    /**\n     * Convert this enum to an Object value\n     *\n     * @return The object corresponding to the Enum\n     * @since 6.0\n     */\n    int fromEnum();\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/ExceptionUtils.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.apache.commons.lang3.StringUtils;\n\npublic class ExceptionUtils {\n\n    public static String printLightWeightStacktrace(Throwable exception) {\n        return printLightWeightStacktrace(exception, 5);\n    }\n\n    public static String printLightWeightStacktrace(Throwable exception, int numberOfFrames) {\n        List<Throwable> throwableList = org.apache.commons.lang3.exception.ExceptionUtils.getThrowableList(exception);\n        if (throwableList.isEmpty()) {\n            return null;\n        }\n        Collections.reverse(throwableList);\n        Throwable rootCause = throwableList.get(0);\n        return throwableList.stream().map(ExceptionUtils::print).collect(Collectors.joining(\"\\n\\twrapped by \"))\n                + \"\\n exception was generated here:\" + getStacktrace(rootCause, numberOfFrames);\n    }\n\n    private static String getStacktrace(Throwable t, int numberOfFrames) {\n        String[] stackFrames = org.apache.commons.lang3.exception.ExceptionUtils.getStackFrames(t);\n        return Arrays.stream(stackFrames).skip(1).limit(numberOfFrames).collect(Collectors.joining(\"\\n\"));\n    }\n\n    protected static String print(Throwable e) {\n        return e.getClass().getName() + \": \" + StringUtils.defaultString(e.getMessage());\n    }\n\n    public static String printRootCauseOnly(Throwable exception) {\n        return print(org.apache.commons.lang3.exception.ExceptionUtils.getRootCause(exception));\n    }\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/JavaMethodInvoker.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.apache.commons.beanutils.MethodUtils;\n\n/**\n * Invokes a method on a Java Object.\n *\n * @author Emmanuel Duchastenier\n * @author Matthieu Chaffotte\n */\npublic class JavaMethodInvoker {\n\n    protected static Map<String, Class<?>> primitiveTypes;\n\n    private static Map<String, Class<?>> autoboxableTypes;\n\n    static {\n        primitiveTypes = new HashMap<String, Class<?>>(8);\n        primitiveTypes.put(\"char\", char.class);\n        primitiveTypes.put(\"byte\", byte.class);\n        primitiveTypes.put(\"long\", long.class);\n        primitiveTypes.put(\"int\", int.class);\n        primitiveTypes.put(\"float\", float.class);\n        primitiveTypes.put(\"double\", double.class);\n        primitiveTypes.put(\"short\", short.class);\n        primitiveTypes.put(\"boolean\", boolean.class);\n\n        autoboxableTypes = new HashMap<String, Class<?>>(8);\n        autoboxableTypes.put(Character.class.getName(), char.class);\n        autoboxableTypes.put(Byte.class.getName(), byte.class);\n        autoboxableTypes.put(Long.class.getName(), long.class);\n        autoboxableTypes.put(Integer.class.getName(), int.class);\n        autoboxableTypes.put(Float.class.getName(), float.class);\n        autoboxableTypes.put(Double.class.getName(), double.class);\n        autoboxableTypes.put(Short.class.getName(), short.class);\n        autoboxableTypes.put(Boolean.class.getName(), boolean.class);\n    }\n\n    protected Class<?> getClassOrPrimitiveClass(final String type) throws ClassNotFoundException {\n        if (primitiveTypes.containsKey(type)) {\n            return primitiveTypes.get(type);\n        }\n        return Thread.currentThread().getContextClassLoader().loadClass(type);\n    }\n\n    public Object invokeJavaMethod(final String typeOfValueToSet, final Object valueToSetObjectWith,\n            final Object objectToInvokeJavaMethodOn,\n            final String operator, final String operatorParameterClassName)\n            throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException,\n            IllegalArgumentException, InvocationTargetException {\n        final Class<?> expressionResultType = getClassOrPrimitiveClass(typeOfValueToSet);\n        final Class<?> dataType = Thread.currentThread().getContextClassLoader()\n                .loadClass(objectToInvokeJavaMethodOn.getClass().getName());\n        final Method method = MethodUtils.getMatchingAccessibleMethod(dataType, operator,\n                new Class[] { getClassOrPrimitiveClass(operatorParameterClassName) });\n        if (method != null) {\n            final Object o = dataType.cast(objectToInvokeJavaMethodOn);\n            method.invoke(o, expressionResultType.cast(valueToSetObjectWith));\n            return o;\n        } else {\n            throw new NoSuchMethodException(\n                    dataType.toGenericString() + \".\" + operator + \"(\" + operatorParameterClassName + \").\");\n        }\n    }\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/LifecycleService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface LifecycleService {\n\n    /**\n     * Start the service\n     *\n     * @throws SBonitaException\n     */\n    default void start() throws SBonitaException {\n\n    }\n\n    default void stop() throws SBonitaException {\n\n    }\n\n    /**\n     * Temporary halt the execution of this service.\n     */\n    default void pause() throws SBonitaException {\n\n    }\n\n    /**\n     * resume the execution the service\n     */\n\n    default void resume() throws SBonitaException {\n\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/LogUtil.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons;\n\n/**\n * This class is for Add call to techinalLoggerService.log with severity TRACE in all Service Implementation public\n * method\n *\n * @author Hongwen Zang\n * @author Matthieu Chaffotte\n * @since 6.0\n */\npublic class LogUtil {\n\n    /**\n     * Get the log message on enter of the method\n     *\n     * @param classes\n     *        class name\n     * @param methodName\n     *        method name\n     * @return log message with String type\n     */\n    public static String getLogBeforeMethod(final Object classes, final String methodName) {\n        final String serviceName = classes.toString();\n        return \"Executing method \" + methodName + \" on Service \" + serviceName;\n    }\n\n    /**\n     * Get the log message before quitting the method\n     *\n     * @param classes\n     *        class name\n     * @param methodName\n     *        method name\n     * @return log message with String type\n     */\n    public static String getLogAfterMethod(final Object classes, final String methodName) {\n        final String serviceName = classes.toString();\n        return \"Quitting method \" + methodName + \" on Service \" + serviceName;\n    }\n\n    /**\n     * Get the log message in each catch block\n     *\n     * @param classes\n     *        class name\n     * @param methodName\n     *        method name\n     * @param e\n     *        Exception\n     * @return log message with String type\n     */\n    public static String getLogOnExceptionMethod(final Object classes, final String methodName, final Exception e) {\n        final String serviceName = classes.toString();\n        return \"Quitting method \" + methodName + \" on Service \" + serviceName + \" with exception \" + e.getMessage();\n    }\n\n    /**\n     * Get the log message in each catch block\n     *\n     * @param classes\n     * @param methodName\n     * @param exceptionMessage\n     * @return log message with String type\n     */\n    public static String getLogOnExceptionMethod(final Object classes, final String methodName,\n            final String exceptionMessage) {\n        final String serviceName = classes.toString();\n        return \"Quitting method \" + methodName + \" on Service \" + serviceName + \" with exception\" + exceptionMessage;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/NullCheckingUtil.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons;\n\nimport java.util.BitSet;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class NullCheckingUtil {\n\n    /**\n     * Check that the given parameters are not null.\n     * This method should only be used to check that some parameters given to a\n     * given method are not null. The exception message tries its best to produce\n     * a helpful message by scanning the stack trace.\n     *\n     * @param params\n     *        the parameters to check\n     * @throws IllegalArgumentException\n     *         if at least one of the parameters is null\n     */\n    public static void checkArgsNotNull(final Object... params) {\n        checkArgsNotNull(1, params);\n    }\n\n    /**\n     * Check that the given parameters are not null.\n     * This method should only be used to check that some parameters given to a\n     * given method are not null. The exception message tries its best to produce\n     * a helpful message by scanning the stack trace.\n     *\n     * @param offset\n     *        the offset to use in the stack trace to produce error message\n     * @param params\n     *        the parameters to check\n     * @throws IllegalArgumentException\n     *         if at least one of the parameters is null\n     */\n    private static void checkArgsNotNull(final int offset, final Object... params) {\n        final NullCheckResult result = findNull(params);\n        if (result.hasNull()) {\n            // Guess the signature of the caller\n            final StackTraceElement callerSTE = getCaller(offset + 1);\n            final String className = callerSTE.getClassName();\n            final String methodName = callerSTE.getMethodName();\n            final StringBuilder sb = new StringBuilder();\n            for (int i = 0; i < result.getSize(); i++) {\n                if (result.isNull(i)) {\n                    sb.append(\"null\");\n                } else {\n                    sb.append(params[i].getClass().getName());\n                }\n                if (i < result.getSize() - 1) {\n                    sb.append(\", \");\n                }\n            }\n\n            final StringBuilder messageStb = new StringBuilder(\"Some parameters are null in \");\n            messageStb.append(className);\n            messageStb.append('.');\n            messageStb.append(methodName);\n            messageStb.append(\"(): \");\n            messageStb.append(sb.toString());\n            throw new IllegalArgumentException(messageStb.toString());\n        }\n    }\n\n    /**\n     * Find null parameters in the given list.\n     * This method returns a {@link NullCheckResult}.\n     *\n     * @param params\n     *        the parameters to check\n     * @return a {@link NullCheckResult} representing null parameters.\n     * @see NullCheckResult\n     */\n    public static NullCheckResult findNull(final Object... params) {\n        if (params == null) {\n            final BitSet bitSet = new BitSet(1);\n            bitSet.set(0);\n            return new NullCheckResult(bitSet, 1);\n        }\n        final BitSet bitSet = new BitSet(params.length);\n        for (int i = 0; i < params.length; i++) {\n            bitSet.set(i, params[i] == null);\n        }\n        return new NullCheckResult(bitSet, params.length);\n    }\n\n    /**\n     * Return the StackTraceElement at the given offset from this method\n     * invocation.\n     *\n     * @param offset\n     * @return a StackTraceElement\n     */\n    public static StackTraceElement getCaller(final int offset) {\n        final StackTraceElement[] stes = Thread.currentThread().getStackTrace();\n        StackTraceElement callerSTE = null;\n        for (int i = 0; i < stes.length - offset - 1; i++) {\n            if (stes[i].getClassName().equals(NullCheckingUtil.class.getName())\n                    && stes[i].getMethodName().equals(\"getCaller\")) {\n                callerSTE = stes[i + 1 + offset];\n                break;\n            }\n        }\n        badStateIfNull(callerSTE, \"Ouch! Can't get the stack trace back to the caller of this method!\");\n        return callerSTE;\n    }\n\n    /**\n     * This method throw an IllegalStateException if the given parameter is null\n     *\n     * @param valueToCheck\n     *        the value to check\n     * @param msg\n     *        the message for the thrown exception\n     * @see IllegalStateException\n     */\n    public static void badStateIfNull(final Object valueToCheck, final String msg) {\n        badStateIfTrue(valueToCheck == null, msg);\n    }\n\n    /**\n     * This method throw an IllegalStateException if the given parameter is true\n     *\n     * @param valueToCheck\n     *        the value to check\n     * @param msg\n     *        the message for the thrown exception\n     * @see IllegalStateException\n     */\n    public static void badStateIfTrue(final boolean valueToCheck, final String msg) {\n        if (valueToCheck) {\n            throw new IllegalStateException(msg);\n        }\n    }\n\n    /**\n     * Represents null value returned by {@link #findNull(Object...)}.\n     *\n     * @see #findNull(Object...)\n     */\n    public static class NullCheckResult {\n\n        private final int size;\n\n        private final BitSet bitSet;\n\n        NullCheckResult(final BitSet bitSet, final int size) {\n            this.bitSet = bitSet;\n            this.size = size;\n        }\n\n        /**\n         * Returns true if some parameters given to {@link #findNull(Object...)} were null.\n         *\n         * @return true if some parameters given to {@link #findNull(Object...)} were null.\n         * @see #findNull(Object...)\n         */\n        public boolean hasNull() {\n            return bitSet.cardinality() != 0;\n        }\n\n        /**\n         * Returns the number of parameters given to {@link #findNull(Object...)}\n         *\n         * @return the number of parameters given to {@link #findNull(Object...)}\n         * @see #findNull(Object...)\n         */\n        public int getSize() {\n            return size;\n        }\n\n        /**\n         * Returns true if the i th parameter given to {@link #findNull(Object...)} was null.\n         *\n         * @param i\n         *        the rank of the parameter given to {@link #findNull(Object...)}.\n         * @return true if the i th parameter given to {@link #findNull(Object...)} was null.\n         */\n        public boolean isNull(final int i) {\n            return bitSet.get(i);\n        }\n    }\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/Pair.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author Baptiste Mesta\n */\npublic class Pair<L, R> implements Serializable, Map.Entry<L, R> {\n\n    private static final long serialVersionUID = 1L;\n\n    private L left;\n\n    private R right;\n\n    public Pair(final L left, final R right) {\n        this.left = left;\n        this.right = right;\n    }\n\n    public static <L, R> Pair<L, R> of(final L left, final R right) {\n        return new Pair<L, R>(left, right);\n    }\n\n    public static <L, R> Pair<L, R> pair(final L left, final R right) {\n        return of(left, right);\n    }\n\n    public static <L, R> Map<L, R> mapOf(Pair<L, R>... entries) {\n        Map<L, R> map = new HashMap<>();\n        for (Pair<L, R> entry : entries) {\n            map.put(entry.getKey(), entry.getValue());\n        }\n        return map;\n    }\n\n    public L getLeft() {\n        return left;\n    }\n\n    public R getRight() {\n        return right;\n    }\n\n    @Override\n    public final L getKey() {\n        return getLeft();\n    }\n\n    @Override\n    public R getValue() {\n        return getRight();\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + (left == null ? 0 : left.hashCode());\n        result = prime * result + (right == null ? 0 : right.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        @SuppressWarnings(\"unchecked\")\n        final Pair<L, R> other = (Pair<L, R>) obj;\n        if (left == null) {\n            if (other.left != null) {\n                return false;\n            }\n        } else if (!left.equals(other.left)) {\n            return false;\n        }\n        if (right == null) {\n            if (other.right != null) {\n                return false;\n            }\n        } else if (!right.equals(other.right)) {\n            return false;\n        }\n        return true;\n    }\n\n    @Override\n    public String toString() {\n        return \"(\" + getLeft() + \",\" + getRight() + \")\";\n    }\n\n    @Override\n    public R setValue(final R right) {\n        final R oldRight = this.right;\n        this.right = right;\n        return oldRight;\n    }\n\n    public L setKey(final L left) {\n        final L oldLeft = this.left;\n        this.left = left;\n        return oldLeft;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/PlatformLifecycleService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface PlatformLifecycleService extends LifecycleService {\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/PlatformRestartHandler.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface PlatformRestartHandler {\n\n    void execute() throws SBonitaException;\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/StringUtils.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons;\n\n/**\n * String manipulation utilitary class.\n *\n * @author Emmanuel Duchastenier\n */\npublic class StringUtils {\n\n    /**\n     * Replaces all \"\\\" character with \"/\" character, and ensures that there is no double \"/\".\n     *\n     * @param path the path-like string to clean.\n     * @return the cleaned path-like string.\n     */\n    public static String uniformizePathPattern(final String path) {\n        return path.replaceAll(\"\\\\\\\\\", \"/\").replaceAll(\"/{2,}\", \"/\");\n    }\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/SystemOperationUtil.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SystemOperationUtil {\n\n    /**\n     * The line separator as defined by the property <code>line.separator</code>.\n     */\n    public static final String LINE_SEPARATOR = System.getProperty(\"line.separator\");\n\n    public static boolean isOnWindows() {\n        return System.getProperty(\"os.name\").contains(\"Windows\");\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/TenantLifecycleService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface TenantLifecycleService extends LifecycleService {\n\n    /**\n     * This method performs actions to initialize a TenantLifecycleService\n     * This method is called when creating a new tenant and when starting the platform.\n     *\n     * @throws SBonitaException\n     */\n    default void init() throws SBonitaException {\n        // nothing\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/TypeConverterUtil.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons;\n\nimport java.io.Serializable;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.OffsetDateTime;\nimport java.time.ZonedDateTime;\nimport java.time.format.DateTimeParseException;\nimport java.util.Date;\nimport java.util.TimeZone;\n\nimport org.apache.commons.beanutils.ConversionException;\nimport org.apache.commons.beanutils.ConvertUtilsBean;\nimport org.apache.commons.beanutils.converters.AbstractConverter;\nimport org.apache.commons.beanutils.converters.DateConverter;\n\n/**\n * @author Laurent Leseigneur\n */\n\npublic class TypeConverterUtil {\n\n    private ConvertUtilsBean convertUtilsBean;\n\n    public TypeConverterUtil(String[] datePatterns) {\n        convertUtilsBean = new ConvertUtilsBean();\n        final DateConverter dateConverter = new DateConverter();\n        dateConverter.setPatterns(datePatterns);\n        dateConverter.setTimeZone(TimeZone.getTimeZone(\"GMT\"));\n        convertUtilsBean.register(dateConverter, Date.class);\n        convertUtilsBean.register(new LocalDateTimeConverter(), LocalDateTime.class);\n        convertUtilsBean.register(new LocalDateConverter(), LocalDate.class);\n        convertUtilsBean.register(new OffsetDateTimeConverter(), OffsetDateTime.class);\n    }\n\n    public Object convertToType(Class<? extends Serializable> clazz, Serializable parameterValue) {\n        try {\n            return convertUtilsBean.convert(parameterValue, clazz);\n        } catch (ConversionException e) {\n            throw new IllegalArgumentException(\"unable to parse '\" + parameterValue + \"' to type \" + clazz.getName());\n        }\n    }\n\n    private class LocalDateConverter extends AbstractConverter {\n\n        static final int MAX_LOCAL_DATE_LENGTH = 10;\n\n        @Override\n        protected <T> T convertToType(Class<T> type, Object value) throws Throwable {\n            if (!(value instanceof String valueAsString)) {\n                throw conversionException(type, value);\n            }\n            if (valueAsString.length() > MAX_LOCAL_DATE_LENGTH) {\n                valueAsString = valueAsString.substring(0, MAX_LOCAL_DATE_LENGTH);\n            }\n            return type.cast(LocalDate.parse(valueAsString));\n        }\n\n        @Override\n        protected Class<?> getDefaultType() {\n            return LocalDate.class;\n        }\n    }\n\n    private class LocalDateTimeConverter extends AbstractConverter {\n\n        @Override\n        protected <T> T convertToType(Class<T> type, Object value) throws Throwable {\n            if (!(value instanceof String paramValueString)) {\n                throw conversionException(type, value);\n            }\n            try {\n                return type.cast(LocalDateTime.parse(paramValueString));\n            } catch (DateTimeParseException e) {\n                //We drop the timezone info from the String:\n                return type.cast(ZonedDateTime.parse(paramValueString).toLocalDateTime());\n            }\n        }\n\n        @Override\n        protected Class<?> getDefaultType() {\n            return LocalDateTime.class;\n        }\n    }\n\n    private class OffsetDateTimeConverter extends AbstractConverter {\n\n        @Override\n        protected <T> T convertToType(Class<T> type, Object value) throws Throwable {\n            if (!(value instanceof String)) {\n                throw conversionException(type, value);\n            }\n            return type.cast(OffsetDateTime.parse((CharSequence) value));\n        }\n\n        @Override\n        protected Class<?> getDefaultType() {\n            return OffsetDateTime.class;\n        }\n    }\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/ExceptionContext.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons.exceptions;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\npublic interface ExceptionContext {\n\n    Map<SExceptionContext, Serializable> getContext();\n\n    void setProcessDefinitionIdOnContext(final long id);\n\n    void setProcessDefinitionNameOnContext(final String name);\n\n    void setProcessDefinitionVersionOnContext(final String version);\n\n    void setProcessInstanceIdOnContext(final long id);\n\n    void setRootProcessInstanceIdOnContext(final long id);\n\n    void setConnectorDefinitionIdOnContext(final String id);\n\n    void setConnectorInputOnContext(String inputName);\n\n    void setConnectorNameOnContext(String name);\n\n    void setConnectorImplementationClassNameOnContext(final String name);\n\n    void setConnectorDefinitionVersionOnContext(final String version);\n\n    void setConnectorActivationEventOnContext(final String activationEvent);\n\n    void setConnectorInstanceIdOnContext(final long id);\n\n    void setFlowNodeDefinitionIdOnContext(final long id);\n\n    void setFlowNodeInstanceIdOnContext(final long id);\n\n    void setFlowNodeNameOnContext(final String name);\n\n    void setMessageInstanceNameOnContext(final String name);\n\n    void setMessageInstanceTargetProcessOnContext(final String name);\n\n    void setMessageInstanceTargetFlowNodeOnContext(final String name);\n\n    void setWaitingMessageEventTypeOnContext(final String eventType);\n\n    void setDocumentIdOnContext(final long id);\n\n    void setUserIdOnContext(final long userId);\n\n    void setGroupIdOnContext(final long groupId);\n\n    void setRoleIdOnContext(final long roleId);\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SAlreadyExistsException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons.exceptions;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SAlreadyExistsException extends SBonitaException {\n\n    public SAlreadyExistsException(String s) {\n        super(s);\n    }\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SBonitaException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons.exceptions;\n\nimport java.io.Serializable;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.TreeMap;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic abstract class SBonitaException extends Exception implements ExceptionContext, ScopedException {\n\n    private static final long serialVersionUID = -500856379312027085L;\n\n    private final String exceptionId;\n\n    private final Object[] arguments;\n\n    private String scope = ScopedException.UNKNOWN_SCOPE;\n\n    private final Map<SExceptionContext, Serializable> context = new TreeMap<SExceptionContext, Serializable>();\n\n    /**\n     * Default constructor\n     */\n    protected SBonitaException() {\n        this((Object[]) null);\n    }\n\n    /**\n     * @param arguments\n     */\n    protected SBonitaException(final Object... arguments) {\n        super();\n        exceptionId = this.getClass().getName();\n        this.arguments = arguments;\n    }\n\n    /**\n     * @param message\n     */\n    protected SBonitaException(final String message) {\n        super(message);\n        exceptionId = this.getClass().getName();\n        arguments = null;\n    }\n\n    protected SBonitaException(final String message, String scope) {\n        super(message);\n        this.scope = scope;\n        exceptionId = this.getClass().getName();\n        arguments = null;\n    }\n\n    /**\n     * @param message\n     * @param cause\n     */\n    protected SBonitaException(final String message, final Throwable cause) {\n        super(message, cause);\n        exceptionId = this.getClass().getName();\n        arguments = null;\n        if (cause instanceof SBonitaException e && e.getContext() != null) {\n            this.context.putAll(e.getContext());\n        }\n        if (cause instanceof ScopedException e) {\n            this.scope = e.getScope();\n        }\n    }\n\n    protected SBonitaException(final String message, String scope, final Throwable cause) {\n        super(message, cause);\n        exceptionId = this.getClass().getName();\n        arguments = null;\n        this.scope = scope;\n        if (cause instanceof SBonitaException e && e.getContext() != null) {\n            this.context.putAll(e.getContext());\n        }\n    }\n\n    /**\n     * @param cause\n     */\n    protected SBonitaException(final Throwable cause) {\n        this(cause, (Object[]) null);\n        if (cause instanceof ScopedException e) {\n            this.scope = e.getScope();\n        }\n    }\n\n    /**\n     * @param cause\n     * @param arguments\n     */\n    protected SBonitaException(final Throwable cause, final Object... arguments) {\n        super(cause);\n        exceptionId = this.getClass().getName();\n        this.arguments = arguments;\n        if (cause instanceof SBonitaException e && e.getContext() != null) {\n            this.context.putAll(e.getContext());\n        }\n        if (cause instanceof ScopedException e) {\n            this.scope = e.getScope();\n        }\n    }\n\n    @Override\n    public String getScope() {\n        return scope;\n    }\n\n    public void setScope(String scope) {\n        this.scope = scope;\n    }\n\n    /**\n     * This exception id is used to find potential causes\n     *\n     * @return the Id of the exception\n     */\n    public String getExceptionId() {\n        return exceptionId;\n    }\n\n    /**\n     * @return\n     */\n    public Object[] getParameters() {\n        return arguments;\n    }\n\n    /**\n     * @return The context of the exception\n     * @since 6.3\n     */\n    @Override\n    public Map<SExceptionContext, Serializable> getContext() {\n        return context;\n    }\n\n    /**\n     * @param id\n     *        The identifier of the process definition to set\n     * @since 6.3\n     */\n    @Override\n    public void setProcessDefinitionIdOnContext(final long id) {\n        context.put(SExceptionContext.PROCESS_DEFINITION_ID, id);\n    }\n\n    /**\n     * @param name\n     *        The name of the process definition to set\n     * @since 6.3\n     */\n    @Override\n    public void setProcessDefinitionNameOnContext(final String name) {\n        context.put(SExceptionContext.PROCESS_NAME, name);\n    }\n\n    /**\n     * @param version\n     *        The version of the process definition to set\n     * @since 6.3\n     */\n    @Override\n    public void setProcessDefinitionVersionOnContext(final String version) {\n        context.put(SExceptionContext.PROCESS_VERSION, version);\n    }\n\n    /**\n     * @param id\n     *        The identifier of the process instance to set\n     * @since 6.3\n     */\n    @Override\n    public void setProcessInstanceIdOnContext(final long id) {\n        context.put(SExceptionContext.PROCESS_INSTANCE_ID, id);\n    }\n\n    /**\n     * @param id\n     *        The identifier of the root process instance to set\n     * @since 6.3\n     */\n    @Override\n    public void setRootProcessInstanceIdOnContext(final long id) {\n        context.put(SExceptionContext.ROOT_PROCESS_INSTANCE_ID, id);\n    }\n\n    /**\n     * @param id\n     *        The identifier of the connector definition\n     * @since 6.3\n     */\n    @Override\n    public void setConnectorDefinitionIdOnContext(final String id) {\n        context.put(SExceptionContext.CONNECTOR_DEFINITION_ID, id);\n    }\n\n    /**\n     * @param name\n     *        The class name of the implementation of the connector definition to set\n     * @since 6.3\n     */\n    @Override\n    public void setConnectorImplementationClassNameOnContext(final String name) {\n        context.put(SExceptionContext.CONNECTOR_IMPLEMENTATION_CLASS_NAME, name);\n    }\n\n    /**\n     * @param version\n     *        The version of the connector definition\n     * @since 6.3\n     */\n    @Override\n    public void setConnectorDefinitionVersionOnContext(final String version) {\n        context.put(SExceptionContext.CONNECTOR_DEFINITION_VERSION, version);\n    }\n\n    /**\n     * @param activationEvent\n     *        The event which activates the connector to set\n     * @since 6.3\n     */\n    @Override\n    public void setConnectorActivationEventOnContext(final String activationEvent) {\n        context.put(SExceptionContext.CONNECTOR_ACTIVATION_EVENT, activationEvent);\n    }\n\n    /**\n     * @param id\n     *        The identifier of the connector instance to set\n     * @since 6.3\n     */\n    @Override\n    public void setConnectorInstanceIdOnContext(final long id) {\n        context.put(SExceptionContext.CONNECTOR_INSTANCE_ID, id);\n    }\n\n    /**\n     * @param id\n     *        The identifier of the flow node definition to set\n     * @since 6.3\n     */\n    @Override\n    public void setFlowNodeDefinitionIdOnContext(final long id) {\n        context.put(SExceptionContext.FLOW_NODE_DEFINITION_ID, id);\n    }\n\n    /**\n     * @param id\n     *        The identifier of the flow node instance to set\n     * @since 6.3\n     */\n    @Override\n    public void setFlowNodeInstanceIdOnContext(final long id) {\n        context.put(SExceptionContext.FLOW_NODE_INSTANCE_ID, id);\n    }\n\n    /**\n     * @param name\n     *        The name of the flow node to set\n     * @since 6.3\n     */\n    @Override\n    public void setFlowNodeNameOnContext(final String name) {\n        context.put(SExceptionContext.FLOW_NODE_NAME, name);\n    }\n\n    /**\n     * @param name\n     *        The name of the message instance to set\n     * @since 6.3\n     */\n    @Override\n    public void setMessageInstanceNameOnContext(final String name) {\n        context.put(SExceptionContext.MESSAGE_INSTANCE_NAME, name);\n    }\n\n    /**\n     * @param name\n     *        The target process name of the message instance to set\n     * @since 6.3\n     */\n    @Override\n    public void setMessageInstanceTargetProcessOnContext(final String name) {\n        context.put(SExceptionContext.MESSAGE_INSTANCE_TARGET_PROCESS_NAME, name);\n    }\n\n    /**\n     * @param name\n     *        The target flow node name of the message instance to set\n     * @since 6.3\n     */\n    @Override\n    public void setMessageInstanceTargetFlowNodeOnContext(final String name) {\n        context.put(SExceptionContext.MESSAGE_INSTANCE_TARGET_FLOW_NODE_NAME, name);\n    }\n\n    /**\n     * @param eventType\n     *        The event type of the waiting message instance to set\n     * @since 6.3\n     */\n    @Override\n    public void setWaitingMessageEventTypeOnContext(final String eventType) {\n        context.put(SExceptionContext.WAITING_MESSAGE_INSTANCE_TYPE, eventType);\n    }\n\n    /**\n     * @param id\n     *        The identifier of the document\n     * @since 6.3\n     */\n    @Override\n    public void setDocumentIdOnContext(final long id) {\n        context.put(SExceptionContext.DOCUMENT_ID, id);\n    }\n\n    /**\n     * @param userId\n     *        The identifier of the user\n     * @since 6.3\n     */\n    @Override\n    public void setUserIdOnContext(final long userId) {\n        context.put(SExceptionContext.USER_ID, userId);\n    }\n\n    /**\n     * @param groupId\n     *        The identifier of the group\n     * @since 6.3\n     */\n    @Override\n    public void setGroupIdOnContext(final long groupId) {\n        context.put(SExceptionContext.GROUP_ID, groupId);\n    }\n\n    /**\n     * @param roleId\n     *        The identifier of the role\n     * @since 6.3\n     */\n    @Override\n    public void setRoleIdOnContext(final long roleId) {\n        context.put(SExceptionContext.ROLE_ID, roleId);\n    }\n\n    @Override\n    public void setConnectorInputOnContext(String inputName) {\n        context.put(SExceptionContext.CONNECTOR_INPUT_NAME, inputName);\n    }\n\n    @Override\n    public void setConnectorNameOnContext(String name) {\n        context.put(SExceptionContext.CONNECTOR_NAME, name);\n    }\n\n    @Override\n    public String getMessage() {\n        final StringBuilder stringBuilder = new StringBuilder();\n        appendContextMessage(stringBuilder);\n        appendCauseMessage(stringBuilder);\n        return stringBuilder.toString();\n    }\n\n    private void appendCauseMessage(final StringBuilder stringBuilder) {\n        String message = super.getMessage();\n        if (message != null && message.isEmpty() && getCause() != null) {\n            message = getCause().getMessage();\n        }\n        if (message != null && !message.trim().isEmpty()) {\n            stringBuilder.append(message);\n        }\n    }\n\n    private void appendContextMessage(final StringBuilder stringBuilder) {\n        if (context != null && !context.isEmpty()) {\n            for (final Entry<SExceptionContext, Serializable> entry : context.entrySet()) {\n                stringBuilder.append(entry.getKey()).append(\"=\").append(entry.getValue()).append(\" | \");\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SBonitaRuntimeException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons.exceptions;\n\nimport java.io.Serializable;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.TreeMap;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class SBonitaRuntimeException extends RuntimeException implements ExceptionContext, ScopedException {\n\n    private static final long serialVersionUID = 7268935639620676043L;\n\n    private final Map<SExceptionContext, Serializable> context = new TreeMap<>();\n    private String scope = ScopedException.UNKNOWN_SCOPE;\n\n    public SBonitaRuntimeException(final Throwable cause) {\n        super(cause);\n        if (cause instanceof SBonitaException e && e.getContext() != null) {\n            this.context.putAll(e.getContext());\n        }\n        if (cause instanceof ScopedException e) {\n            this.scope = e.getScope();\n        }\n    }\n\n    public SBonitaRuntimeException(final String message, final Throwable cause) {\n        super(message, cause);\n        if (cause instanceof SBonitaException e && e.getContext() != null) {\n            this.context.putAll(e.getContext());\n        }\n        if (cause instanceof ScopedException e) {\n            this.scope = e.getScope();\n        }\n    }\n\n    public SBonitaRuntimeException(final String message, String scope, final Throwable cause) {\n        super(message, cause);\n        this.scope = scope;\n        if (cause instanceof SBonitaException e && e.getContext() != null) {\n            this.context.putAll(e.getContext());\n        }\n    }\n\n    public SBonitaRuntimeException(final String message) {\n        super(message);\n    }\n\n    @Override\n    public String getScope() {\n        return this.scope;\n    }\n\n    /**\n     * @return The context of the exception\n     * @since 6.3\n     */\n    public Map<SExceptionContext, Serializable> getContext() {\n        return context;\n    }\n\n    /**\n     * @param id\n     *        The identifier of the process definition to set\n     * @since 6.3\n     */\n    @Override\n    public void setProcessDefinitionIdOnContext(final long id) {\n        context.put(SExceptionContext.PROCESS_DEFINITION_ID, id);\n    }\n\n    /**\n     * @param name\n     *        The name of the process definition to set\n     * @since 6.3\n     */\n    @Override\n    public void setProcessDefinitionNameOnContext(final String name) {\n        context.put(SExceptionContext.PROCESS_NAME, name);\n    }\n\n    /**\n     * @param version\n     *        The version of the process definition to set\n     * @since 6.3\n     */\n    @Override\n    public void setProcessDefinitionVersionOnContext(final String version) {\n        context.put(SExceptionContext.PROCESS_VERSION, version);\n    }\n\n    /**\n     * @param id\n     *        The identifier of the process instance to set\n     * @since 6.3\n     */\n    @Override\n    public void setProcessInstanceIdOnContext(final long id) {\n        context.put(SExceptionContext.PROCESS_INSTANCE_ID, id);\n    }\n\n    /**\n     * @param id\n     *        The identifier of the root process instance to set\n     * @since 6.3\n     */\n    @Override\n    public void setRootProcessInstanceIdOnContext(final long id) {\n        context.put(SExceptionContext.ROOT_PROCESS_INSTANCE_ID, id);\n    }\n\n    /**\n     * @param id\n     *        The identifier of the connector definition\n     * @since 6.3\n     */\n    @Override\n    public void setConnectorDefinitionIdOnContext(final String id) {\n        context.put(SExceptionContext.CONNECTOR_DEFINITION_ID, id);\n    }\n\n    @Override\n    public void setConnectorImplementationClassNameOnContext(String name) {\n        context.put(SExceptionContext.CONNECTOR_IMPLEMENTATION_CLASS_NAME, name);\n    }\n\n    /**\n     * @param version\n     *        The version of the connector definition\n     * @since 6.3\n     */\n    @Override\n    public void setConnectorDefinitionVersionOnContext(final String version) {\n        context.put(SExceptionContext.CONNECTOR_DEFINITION_VERSION, version);\n    }\n\n    /**\n     * @param activationEvent\n     *        The event which activates the connector to set\n     * @since 6.3\n     */\n    @Override\n    public void setConnectorActivationEventOnContext(final String activationEvent) {\n        context.put(SExceptionContext.CONNECTOR_ACTIVATION_EVENT, activationEvent);\n    }\n\n    /**\n     * @param id\n     *        The identifier of the connector instance to set\n     * @since 6.3\n     */\n    @Override\n    public void setConnectorInstanceIdOnContext(final long id) {\n        context.put(SExceptionContext.CONNECTOR_INSTANCE_ID, id);\n    }\n\n    /**\n     * @param id\n     *        The identifier of the flow node definition to set\n     * @since 6.3\n     */\n    @Override\n    public void setFlowNodeDefinitionIdOnContext(final long id) {\n        context.put(SExceptionContext.FLOW_NODE_DEFINITION_ID, id);\n    }\n\n    /**\n     * @param id\n     *        The identifier of the flow node instance to set\n     * @since 6.3\n     */\n    @Override\n    public void setFlowNodeInstanceIdOnContext(final long id) {\n        context.put(SExceptionContext.FLOW_NODE_INSTANCE_ID, id);\n    }\n\n    /**\n     * @param name\n     *        The name of the flow node to set\n     * @since 6.3\n     */\n    @Override\n    public void setFlowNodeNameOnContext(final String name) {\n        context.put(SExceptionContext.FLOW_NODE_NAME, name);\n    }\n\n    /**\n     * @param name\n     *        The name of the message instance to set\n     * @since 6.3\n     */\n    @Override\n    public void setMessageInstanceNameOnContext(final String name) {\n        context.put(SExceptionContext.MESSAGE_INSTANCE_NAME, name);\n    }\n\n    /**\n     * @param name\n     *        The target process name of the message instance to set\n     * @since 6.3\n     */\n    @Override\n    public void setMessageInstanceTargetProcessOnContext(final String name) {\n        context.put(SExceptionContext.MESSAGE_INSTANCE_TARGET_PROCESS_NAME, name);\n    }\n\n    /**\n     * @param name\n     *        The target flow node name of the message instance to set\n     * @since 6.3\n     */\n    @Override\n    public void setMessageInstanceTargetFlowNodeOnContext(final String name) {\n        context.put(SExceptionContext.MESSAGE_INSTANCE_TARGET_FLOW_NODE_NAME, name);\n    }\n\n    /**\n     * @param eventType\n     *        The event type of the waiting message instance to set\n     * @since 6.3\n     */\n    @Override\n    public void setWaitingMessageEventTypeOnContext(final String eventType) {\n        context.put(SExceptionContext.WAITING_MESSAGE_INSTANCE_TYPE, eventType);\n    }\n\n    @Override\n    public void setDocumentIdOnContext(long id) {\n        context.put(SExceptionContext.DOCUMENT_ID, id);\n    }\n\n    @Override\n    public void setUserIdOnContext(long userId) {\n        context.put(SExceptionContext.USER_ID, userId);\n    }\n\n    @Override\n    public void setGroupIdOnContext(long groupId) {\n        context.put(SExceptionContext.GROUP_ID, groupId);\n    }\n\n    @Override\n    public void setRoleIdOnContext(long roleId) {\n        context.put(SExceptionContext.ROLE_ID, roleId);\n    }\n\n    @Override\n    public void setConnectorInputOnContext(String inputName) {\n        context.put(SExceptionContext.CONNECTOR_INPUT_NAME, inputName);\n    }\n\n    @Override\n    public void setConnectorNameOnContext(String name) {\n        context.put(SExceptionContext.CONNECTOR_NAME, name);\n    }\n\n    @Override\n    public String getMessage() {\n        final StringBuilder stringBuilder = new StringBuilder();\n        appendContextMessage(stringBuilder);\n        appendCauseMessage(stringBuilder);\n        return stringBuilder.toString();\n    }\n\n    private void appendCauseMessage(final StringBuilder stringBuilder) {\n        String message = super.getMessage();\n        if (message != null && message.isEmpty() && getCause() != null) {\n            message = getCause().getMessage();\n        }\n        if (message != null && !message.trim().isEmpty()) {\n            stringBuilder.append(message);\n        }\n    }\n\n    private void appendContextMessage(final StringBuilder stringBuilder) {\n        if (!context.isEmpty()) {\n            for (final Entry<SExceptionContext, Serializable> entry : context.entrySet()) {\n                stringBuilder.append(entry.getKey());\n                stringBuilder.append(\"=\");\n                stringBuilder.append(entry.getValue());\n                stringBuilder.append(\" | \");\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SDeletionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons.exceptions;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SDeletionException extends SBonitaException {\n\n    private static final long serialVersionUID = 1L;\n\n    public SDeletionException() {\n    }\n\n    public SDeletionException(Object... arguments) {\n        super(arguments);\n    }\n\n    public SDeletionException(String message) {\n        super(message);\n    }\n\n    public SDeletionException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n    public SDeletionException(Throwable cause) {\n        super(cause);\n    }\n\n    public SDeletionException(Throwable cause, Object... arguments) {\n        super(cause, arguments);\n    }\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SExceptionContext.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons.exceptions;\n\n/**\n * To define the context of an exception in the message.\n *\n * @author Celine Souchet\n */\npublic enum SExceptionContext {\n    /**\n     * Corresponding to the identifier of the process definition\n     */\n    PROCESS_DEFINITION_ID,\n    /**\n     * Corresponding to the name of the process definition\n     */\n    PROCESS_NAME,\n    /**\n     * Corresponding to the version of the process definition\n     */\n    PROCESS_VERSION,\n    /**\n     * Corresponding to the identifier of the process instance\n     */\n    PROCESS_INSTANCE_ID,\n    /**\n     * Corresponding to the identifier of the root process instance\n     */\n    ROOT_PROCESS_INSTANCE_ID,\n    /**\n     * Corresponding to the identifier of the flow node definition\n     */\n    FLOW_NODE_DEFINITION_ID,\n    /**\n     * Corresponding to the identifier of the flow node instance\n     */\n    FLOW_NODE_INSTANCE_ID,\n    /**\n     * Corresponding to the name of the flow node\n     */\n    FLOW_NODE_NAME,\n    /**\n     * Corresponding to the name of the Message Instance\n     */\n    MESSAGE_INSTANCE_NAME,\n    /**\n     * Corresponding to the target process name of the Message Instance\n     */\n    MESSAGE_INSTANCE_TARGET_PROCESS_NAME,\n    /**\n     * Corresponding to the target flow node name of the Message Instance\n     */\n    MESSAGE_INSTANCE_TARGET_FLOW_NODE_NAME,\n    /**\n     * Corresponding to the event type of the Waiting Message Instance\n     */\n    WAITING_MESSAGE_INSTANCE_TYPE,\n    /**\n     * Corresponding to the name of the connector\n     */\n    CONNECTOR_NAME,\n    /**\n     * Corresponding to the identifier of the connector definition\n     */\n    CONNECTOR_DEFINITION_ID,\n    /**\n     * Corresponding to the name of the input of the connector\n     */\n    CONNECTOR_INPUT_NAME,\n    /**\n     * Corresponding to the class name of the implementation of the connector definition\n     */\n    CONNECTOR_IMPLEMENTATION_CLASS_NAME,\n    /**\n     * Corresponding to the version of the connector definition\n     */\n    CONNECTOR_DEFINITION_VERSION,\n    /**\n     * Corresponding to the event which activates the connector\n     */\n    CONNECTOR_ACTIVATION_EVENT,\n    /**\n     * Corresponding to the identifier of the connector instance\n     */\n    CONNECTOR_INSTANCE_ID,\n    /**\n     * Corresponding to the identifier of the user\n     */\n    USER_ID,\n    /**\n     * Corresponding to the identifier of the group\n     */\n    GROUP_ID,\n    /**\n     * Corresponding to the identifier of the role\n     */\n    ROLE_ID,\n    /**\n     * Corresponding to the identifier of the document\n     */\n    DOCUMENT_ID,\n    /**\n     * Corresponding to the name of the transition\n     */\n    TRANSITION_NAME,\n    /**\n     * Corresponding to the target flownode name of the transition\n     */\n    TRANSITION_TARGET_FLOWNODE_NAME;\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SExecutionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons.exceptions;\n\n/**\n * when something went wrong during an execution\n *\n * @author Baptiste Mesta\n */\npublic class SExecutionException extends SBonitaException {\n\n    public SExecutionException(String message) {\n        super(message);\n    }\n\n    public SExecutionException(Throwable t) {\n        super(t);\n    }\n\n    public SExecutionException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n    public SExecutionException(SBonitaException e) {\n        super(e);\n    }\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SLifecycleException.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons.exceptions;\n\n/**\n * Exception happening during a life cycle operation such as start/stop of tenant services\n */\npublic class SLifecycleException extends SBonitaException {\n\n    public SLifecycleException(String message, Exception cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SObjectAlreadyExistsException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons.exceptions;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SObjectAlreadyExistsException extends SBonitaException {\n\n    private static final long serialVersionUID = -4043938145910922261L;\n\n    /**\n     *\n     */\n    public SObjectAlreadyExistsException() {\n        super();\n    }\n\n    /**\n     * @param arguments\n     */\n    public SObjectAlreadyExistsException(final Object... arguments) {\n        super(arguments);\n    }\n\n    /**\n     * @param message\n     * @param cause\n     */\n    public SObjectAlreadyExistsException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    /**\n     * @param message\n     */\n    public SObjectAlreadyExistsException(final String message) {\n        super(message);\n    }\n\n    /**\n     * @param cause\n     * @param arguments\n     */\n    public SObjectAlreadyExistsException(final Throwable cause, final Object... arguments) {\n        super(cause, arguments);\n    }\n\n    /**\n     * @param cause\n     */\n    public SObjectAlreadyExistsException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SObjectCreationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons.exceptions;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SObjectCreationException extends SBonitaException {\n\n    private static final long serialVersionUID = -4043938145910922261L;\n\n    /**\n     *\n     */\n    public SObjectCreationException() {\n        super();\n    }\n\n    /**\n     * @param arguments\n     */\n    public SObjectCreationException(final Object... arguments) {\n        super(arguments);\n    }\n\n    /**\n     * @param message\n     * @param cause\n     */\n    public SObjectCreationException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    /**\n     * @param message\n     */\n    public SObjectCreationException(final String message) {\n        super(message);\n    }\n\n    /**\n     * @param cause\n     * @param arguments\n     */\n    public SObjectCreationException(final Throwable cause, final Object... arguments) {\n        super(cause, arguments);\n    }\n\n    /**\n     * @param cause\n     */\n    public SObjectCreationException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SObjectModificationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons.exceptions;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SObjectModificationException extends SBonitaException {\n\n    private static final long serialVersionUID = 7771832630450258316L;\n\n    /**\n     *\n     */\n    public SObjectModificationException() {\n        super();\n    }\n\n    /**\n     * @param arguments\n     */\n    public SObjectModificationException(final Object... arguments) {\n        super(arguments);\n    }\n\n    /**\n     * @param message\n     * @param cause\n     */\n    public SObjectModificationException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    /**\n     * @param message\n     */\n    public SObjectModificationException(final String message) {\n        super(message);\n    }\n\n    /**\n     * @param cause\n     * @param arguments\n     */\n    public SObjectModificationException(final Throwable cause, final Object... arguments) {\n        super(cause, arguments);\n    }\n\n    /**\n     * @param cause\n     */\n    public SObjectModificationException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SObjectNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons.exceptions;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SObjectNotFoundException extends SBonitaException {\n\n    private static final long serialVersionUID = -4043938145910922261L;\n\n    /**\n     *\n     */\n    public SObjectNotFoundException() {\n        super();\n    }\n\n    /**\n     * @param arguments\n     */\n    public SObjectNotFoundException(final Object... arguments) {\n        super(arguments);\n    }\n\n    /**\n     * @param message\n     * @param cause\n     */\n    public SObjectNotFoundException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    /**\n     * @param message\n     */\n    public SObjectNotFoundException(final String message) {\n        super(message);\n    }\n\n    /**\n     * @param cause\n     * @param arguments\n     */\n    public SObjectNotFoundException(final Throwable cause, final Object... arguments) {\n        super(cause, arguments);\n    }\n\n    /**\n     * @param cause\n     */\n    public SObjectNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SObjectReadException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons.exceptions;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SObjectReadException extends SBonitaException {\n\n    private static final long serialVersionUID = -183875236263807679L;\n\n    /**\n     *\n     */\n    public SObjectReadException() {\n        super();\n    }\n\n    /**\n     * @param arguments\n     */\n    public SObjectReadException(final Object... arguments) {\n        super(arguments);\n    }\n\n    /**\n     * @param message\n     * @param cause\n     */\n    public SObjectReadException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    /**\n     * @param message\n     */\n    public SObjectReadException(final String message) {\n        super(message);\n    }\n\n    /**\n     * @param cause\n     * @param arguments\n     */\n    public SObjectReadException(final Throwable cause, final Object... arguments) {\n        super(cause, arguments);\n    }\n\n    /**\n     * @param cause\n     */\n    public SObjectReadException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SReflectException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons.exceptions;\n\npublic class SReflectException extends SBonitaException {\n\n    private static final long serialVersionUID = 3908092138962641331L;\n\n    public SReflectException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SReflectException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SReflectException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SRetryableException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons.exceptions;\n\nimport java.io.Serial;\n\n/**\n * Runtime exception signaling that the failed operation may succeed on retry.\n * <p>\n * This exception does not carry retry semantics in its type hierarchy — retryability\n * is determined at runtime by {@code DefaultExceptionRetryabilityEvaluator}, which is\n * configured in {@code bonita-community.xml} with a list of exception classes to retry.\n * {@code SRetryableException} is registered in that list by default.\n * <p>\n * In the Work execution layer ({@code RetryingWorkExecutorService}), throwing this\n * exception triggers automatic retry of the failed Work. Outside that context (e.g.\n * scheduled jobs, direct API calls), callers catch it as a regular runtime exception.\n */\npublic class SRetryableException extends SBonitaRuntimeException {\n\n    @Serial\n    private static final long serialVersionUID = -2007050544702839857L;\n\n    public SRetryableException(final Exception cause) {\n        super(cause);\n    }\n\n    public SRetryableException(String message) {\n        super(message);\n    }\n\n    public SRetryableException(String message, Exception cause) {\n        super(message, cause);\n    }\n\n    @Override\n    public synchronized Exception getCause() {\n        return (Exception) super.getCause();\n    }\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SV6FormsDeployException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons.exceptions;\n\n/**\n * Thrown when a process fails to deploy because of V6 form presence in the bar to deploy.\n *\n * @author Danila Mazour\n * @since 7.8.0\n */\npublic class SV6FormsDeployException extends SObjectCreationException {\n\n    /**\n     * @param message\n     */\n    public SV6FormsDeployException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/ScopedException.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons.exceptions;\n\npublic interface ScopedException {\n\n    String UNKNOWN_SCOPE = \"UNKNOWN\";\n    String GENERAL_INFORMATION = \"General information\";\n    String OPERATION = \"Operation\";\n    String EVENT = \"Event\";\n    String ITERATION = \"Iteration\";\n    String CONNECTOR = \"Connector\";\n    String DATA = \"Data initialization\";\n    String ACTOR_MAPPING = \"Actor mapping\";\n    String OUTGOING_TRANSITION = \"Outgoing transition\";\n\n    /**\n     * Describe the scope of the exception with a human-readable category.\n     *\n     * @return The scope of the exception\n     */\n    default String getScope() {\n        return UNKNOWN_SCOPE;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/io/IOUtil.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons.io;\n\nimport java.io.BufferedInputStream;\nimport java.io.BufferedOutputStream;\nimport java.io.BufferedReader;\nimport java.io.BufferedWriter;\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\nimport java.io.FileReader;\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.io.Writer;\nimport java.math.BigInteger;\nimport java.net.URI;\nimport java.net.URL;\nimport java.nio.file.attribute.FileTime;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.time.Instant;\nimport java.time.LocalDateTime;\nimport java.time.ZoneOffset;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Optional;\nimport java.util.Properties;\nimport java.util.Scanner;\nimport java.util.jar.JarEntry;\nimport java.util.jar.JarInputStream;\nimport java.util.jar.JarOutputStream;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipInputStream;\nimport java.util.zip.ZipOutputStream;\n\nimport javax.activation.MimetypesFileTypeMap;\nimport javax.xml.XMLConstants;\nimport javax.xml.transform.OutputKeys;\nimport javax.xml.transform.Transformer;\nimport javax.xml.transform.TransformerException;\nimport javax.xml.transform.TransformerFactory;\nimport javax.xml.transform.dom.DOMSource;\nimport javax.xml.transform.stream.StreamResult;\n\nimport org.apache.commons.io.FileUtils;\nimport org.bonitasoft.engine.commons.ClassDataUtil;\nimport org.bonitasoft.engine.commons.NullCheckingUtil;\nimport org.bonitasoft.engine.commons.Pair;\nimport org.w3c.dom.Document;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class IOUtil {\n\n    private static final String LINE_SEPARATOR = System.getProperty(\"line.separator\");\n\n    public static final String TMP_DIRECTORY = System.getProperty(\"java.io.tmpdir\");\n\n    public static final MimetypesFileTypeMap MIMETYPES_FILE_TYPE_MAP = new MimetypesFileTypeMap();\n\n    static {\n        //on jdk 8 there is no png by default in mime types\n        IOUtil.MIMETYPES_FILE_TYPE_MAP.addMimeTypes(\"image/png\\t\\tpng PNG\");\n        IOUtil.MIMETYPES_FILE_TYPE_MAP.addMimeTypes(\"image/gif\\t\\tgif GIF\");\n        IOUtil.MIMETYPES_FILE_TYPE_MAP.addMimeTypes(\"image/jpeg\\t\\tjpeg jpg jpe JPG\");\n    }\n    private static final int BUFFER_SIZE = 100000;\n\n    public static final String FILE_ENCODING = \"UTF-8\";\n\n    private IOUtil() {\n        // For Sonar\n    }\n\n    public static List<String> getClassNameList(final byte[] jarContent) throws IOException {\n        final List<String> classes = new ArrayList<>(10);\n        JarInputStream stream = null;\n        ByteArrayInputStream byteArrayInputStream = null;\n        try {\n            byteArrayInputStream = new ByteArrayInputStream(jarContent);\n            stream = new JarInputStream(byteArrayInputStream);\n            JarEntry nextJarEntry = null;\n            while ((nextJarEntry = stream.getNextJarEntry()) != null) {\n                final String name = nextJarEntry.getName();\n                if (name.endsWith(\".class\")) {\n                    classes.add(toQualifiedClassName(name));\n                }\n            }\n        } finally {\n            if (stream != null) {\n                stream.close();\n            }\n            if (byteArrayInputStream != null) {\n                byteArrayInputStream.close();\n            }\n        }\n        return classes;\n    }\n\n    private static String toQualifiedClassName(final String name) {\n        return name.replace('/', '.').replaceAll(\".class\", \"\");\n    }\n\n    public static void write(final File file, final byte[] fileContent) throws IOException {\n        NullCheckingUtil.checkArgsNotNull(file, fileContent);\n        if (!file.exists()) {\n            file.getParentFile().mkdirs();\n            file.createNewFile();\n        }\n        final OutputStream os = new FileOutputStream(file);\n        try {\n            os.write(fileContent);\n            os.flush();\n        } finally {\n            os.close();\n        }\n    }\n\n    public static byte[] generateJar(final Class<?>... classes) throws IOException {\n        return generateJar(getResources(classes));\n    }\n\n    public static Map<String, byte[]> getResources(final Class<?>... classes) throws IOException {\n        if (classes == null || classes.length == 0) {\n            final String message = \"No classes available\";\n            throw new IOException(message);\n        }\n        final Map<String, byte[]> resources = new HashMap<>();\n        for (final Class<?> clazz : classes) {\n            resources.put(clazz.getName().replace(\".\", \"/\") + \".class\", ClassDataUtil.getClassData(clazz));\n            for (final Class<?> internalClass : clazz.getDeclaredClasses()) {\n                resources.put(internalClass.getName().replace(\".\", \"/\") + \".class\",\n                        ClassDataUtil.getClassData(internalClass));\n            }\n        }\n        return resources;\n    }\n\n    public static byte[] generateJar(final Map<String, byte[]> resources) throws IOException {\n        if (resources == null || resources.isEmpty()) {\n            throw new IOException(\"No resources available\");\n        }\n\n        try (var baos = new ByteArrayOutputStream();\n                var jarOutStream = new JarOutputStream(new BufferedOutputStream(baos));) {\n            for (final Map.Entry<String, byte[]> resource : resources.entrySet()) {\n                var entry = new JarEntry(resource.getKey());\n                // Force entry timestamp to an arbitrary date to ease jar content comparison\n                entry.setCreationTime(FileTime.from(Instant.ofEpochMilli(0)));\n                entry.setLastModifiedTime(FileTime.from(Instant.ofEpochMilli(0)));\n                entry.setLastAccessTime(FileTime.from(Instant.ofEpochMilli(0)));\n                entry.setTimeLocal(LocalDateTime.ofEpochSecond(0, 0, ZoneOffset.UTC));\n                entry.setTime(0);\n                jarOutStream.putNextEntry(entry);\n                jarOutStream.write(resource.getValue());\n            }\n            jarOutStream.flush();\n            baos.flush();\n            baos.close();\n            jarOutStream.close();\n            return baos.toByteArray();\n        }\n    }\n\n    public static byte[] generateZip(final Map<String, byte[]> resources) throws IOException {\n        if (resources == null || resources.isEmpty()) {\n            throw new IOException(\"No resources available\");\n        }\n\n        ByteArrayOutputStream baos = null;\n        ZipOutputStream zipOutStream = null;\n        BufferedOutputStream bufferedOutputStream = null;\n        try {\n            baos = new ByteArrayOutputStream();\n            bufferedOutputStream = new BufferedOutputStream(baos);\n            zipOutStream = new ZipOutputStream(bufferedOutputStream);\n            for (final Map.Entry<String, byte[]> resource : resources.entrySet()) {\n                zipOutStream.putNextEntry(new ZipEntry(resource.getKey()));\n                zipOutStream.write(resource.getValue());\n            }\n            zipOutStream.flush();\n            baos.flush();\n        } finally {\n            if (zipOutStream != null) {\n                zipOutStream.close();\n            }\n            if (baos != null) {\n                baos.close();\n            }\n            if (bufferedOutputStream != null) {\n                bufferedOutputStream.close();\n            }\n        }\n\n        return baos.toByteArray();\n    }\n\n    /**\n     * Return the whole underlying stream content into a single String.\n     * Warning: the whole content of stream will be kept in memory!! Use with\n     * care!\n     *\n     * @param in\n     *        the stream to read\n     * @return the whole content of the stream in a single String.\n     * @throws IOException\n     *         if an I/O exception occurs\n     */\n    public static byte[] getAllContentFrom(final InputStream in) throws IOException {\n        if (in == null) {\n            throw new IOException(\"The InputStream is null!\");\n        }\n        final byte[] buffer = new byte[BUFFER_SIZE];\n        final byte[] resultArray;\n        BufferedInputStream bis = null;\n        ByteArrayOutputStream result = null;\n\n        try {\n            bis = new BufferedInputStream(in);\n            result = new ByteArrayOutputStream();\n            int amountRead;\n            while ((amountRead = bis.read(buffer)) > 0) {\n                result.write(buffer, 0, amountRead);\n            }\n            resultArray = result.toByteArray();\n            result.flush();\n\n        } finally {\n            if (bis != null) {\n                bis.close();\n            }\n            if (result != null) {\n                result.close();\n            }\n        }\n        return resultArray;\n    }\n\n    /**\n     * Read the contents from the given FileInputStream. Return the result as a String.\n     *\n     * @param inputStream\n     *        the stream to read from\n     * @return the content read from the inputStream, as a String\n     */\n    public static String read(final InputStream inputStream) {\n        final Scanner scanner = new Scanner(inputStream, FILE_ENCODING);\n        final StringBuilder text = new StringBuilder();\n        try {\n            boolean isFirst = true;\n            while (scanner.hasNextLine()) {\n                if (isFirst) {\n                    text.append(scanner.nextLine());\n                } else {\n                    text.append(LINE_SEPARATOR + scanner.nextLine());\n                }\n                isFirst = false;\n            }\n        } finally {\n            scanner.close();\n        }\n        return text.toString();\n    }\n\n    /**\n     * Read the contents of the given file.\n     *\n     * @param file\n     */\n    public static String read(final File file) throws IOException {\n        final FileInputStream inputStream = new FileInputStream(file);\n        try {\n            return read(inputStream);\n        } finally {\n            inputStream.close();\n        }\n    }\n\n    /**\n     * Equivalent to {@link #getAllContentFrom(InputStream) getAllContentFrom(new\n     * FileInputStream(file))};\n     *\n     * @param file\n     *        the file to read\n     * @return the whole content of the file in a single String.\n     * @throws IOException\n     *         If an I/O exception occurs\n     */\n    public static byte[] getAllContentFrom(final File file) throws IOException {\n        InputStream in = null;\n        try {\n            in = new FileInputStream(file);\n            return getAllContentFrom(in);\n        } finally {\n            if (in != null) {\n                in.close();\n            }\n        }\n    }\n\n    /**\n     * Return the whole underlying stream content into a single String.\n     * Warning: the whole content of stream will be kept in memory!! Use with\n     * care!\n     *\n     * @param url\n     *        the URL to read\n     * @return the whole content of the stream in a single String.\n     * @throws IOException\n     *         if an I/O exception occurs\n     */\n    public static byte[] getAllContentFrom(final URL url) throws IOException {\n        final InputStream in = url.openStream();\n        try {\n            return getAllContentFrom(in);\n        } finally {\n            in.close();\n        }\n    }\n\n    public static boolean deleteDir(final File dir) throws IOException {\n        return deleteDir(dir, 1, 0);\n    }\n\n    public static boolean deleteDir(final File dir, final int attempts, final long sleepTime) throws IOException {\n        boolean result = true;\n        if (!dir.exists()) {\n            return true; //already deleted\n        }\n        if (!dir.isDirectory()) {\n            throw new IOException(\"Unable to delete directory: \" + dir + \", it is not a directory\");\n        }\n        for (final File file : dir.listFiles()) {\n            if (file.isDirectory()) {\n                result &= deleteDir(file, attempts, sleepTime);\n            } else {\n                result &= deleteFile(file, attempts, sleepTime);\n            }\n        }\n        return result && deleteFile(dir, attempts, sleepTime);\n    }\n\n    public static boolean deleteFile(final File file, final int attempts, final long sleepTime) {\n        int retries = attempts;\n        while (retries > 0) {\n            if (file.delete()) {\n                break;\n            }\n            retries--;\n            try {\n                Thread.sleep(sleepTime);\n            } catch (final InterruptedException e) {\n            }\n        }\n        return retries > 0;\n    }\n\n    public static void writeFile(final File file, final String fileContent) throws IOException {\n        if (file == null) {\n            throw new IllegalArgumentException(\"File should not be null.\");\n        }\n        if (!file.exists()) {\n            throw new FileNotFoundException(\"File does not exist: \" + file);\n        }\n        if (!file.isFile()) {\n            throw new IllegalArgumentException(\"Should not be a directory: \" + file);\n        }\n        if (!file.canWrite()) {\n            throw new IllegalArgumentException(\"File cannot be written: \" + file);\n        }\n\n        // use buffering\n        final Writer output = new BufferedWriter(new FileWriter(file));\n        try {\n            output.write(fileContent);\n        } finally {\n            output.close();\n        }\n    }\n\n    public static byte[] zip(final Map<String, byte[]> files) throws IOException {\n        final ByteArrayOutputStream baos = new ByteArrayOutputStream();\n        final ZipOutputStream zos = new ZipOutputStream(baos);\n        try {\n            for (final Entry<String, byte[]> file : files.entrySet()) {\n                zos.putNextEntry(new ZipEntry(file.getKey()));\n                zos.write(file.getValue());\n                zos.flush();\n                zos.closeEntry();\n            }\n        } finally {\n            zos.close();\n            baos.close();\n        }\n        return baos.toByteArray();\n    }\n\n    public static byte[] zip(final Pair<String, byte[]>... files) throws IOException {\n        final ByteArrayOutputStream baos = new ByteArrayOutputStream();\n        final ZipOutputStream zos = new ZipOutputStream(baos);\n        try {\n            for (final Entry<String, byte[]> file : files) {\n                zos.putNextEntry(new ZipEntry(file.getKey()));\n                zos.write(file.getValue());\n                zos.flush();\n                zos.closeEntry();\n            }\n        } finally {\n            zos.close();\n            baos.close();\n        }\n        return baos.toByteArray();\n    }\n\n    public static final Map<String, byte[]> unzip(final byte[] zipFile) throws IOException {\n        final ByteArrayInputStream bais = new ByteArrayInputStream(zipFile);\n        final ZipInputStream zipInputstream = new ZipInputStream(bais);\n        ZipEntry zipEntry = null;\n        final Map<String, byte[]> files = new HashMap<>();\n        try {\n            while ((zipEntry = zipInputstream.getNextEntry()) != null) {\n                final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();\n                int bytesRead;\n                final byte[] buffer = new byte[BUFFER_SIZE];\n                while ((bytesRead = zipInputstream.read(buffer)) > -1) {\n                    byteArrayOutputStream.write(buffer, 0, bytesRead);\n                }\n                files.put(zipEntry.getName(), byteArrayOutputStream.toByteArray());\n            }\n        } finally {\n            zipInputstream.close();\n        }\n        return files;\n    }\n\n    private static void writeZipInputToFile(final ZipInputStream zipInputstream, final File outputFile)\n            throws FileNotFoundException, IOException {\n        // The input is a file. An FileOutputStream is created to write the content of the new file.\n        mkdirs(outputFile.getParentFile());\n\n        try {\n            final FileOutputStream fileOutputStream = new FileOutputStream(outputFile);\n            try {\n                // The contents of the new file, that is read from the ZipInputStream using a buffer (byte []), is written.\n                int bytesRead;\n                final byte[] buffer = new byte[BUFFER_SIZE];\n                while ((bytesRead = zipInputstream.read(buffer)) > -1) {\n                    fileOutputStream.write(buffer, 0, bytesRead);\n                }\n            } finally {\n                fileOutputStream.close();\n            }\n        } catch (final IOException ioe) {\n            // In case of error, the file is deleted\n            outputFile.delete();\n            throw ioe;\n        }\n    }\n\n    private static boolean mkdirs(final File file) {\n        if (!file.exists()) {\n            return file.mkdirs();\n        }\n        return true;\n    }\n\n    public static byte[] getZipEntryContent(final String entryName, final InputStream inputStream) throws IOException {\n        final ZipInputStream zipInputstream = new ZipInputStream(inputStream);\n        ZipEntry zipEntry = null;\n        try {\n            while ((zipEntry = zipInputstream.getNextEntry()) != null) {\n                if (!entryName.equals(zipEntry.getName())) {\n                    continue;\n                }\n                return getBytes(zipInputstream);\n            }\n        } finally {\n            zipInputstream.close();\n        }\n        throw new IOException(\"Entry \" + entryName + \" does not exists in the zip file\");\n    }\n\n    public static byte[] getBytes(ZipInputStream zipInputstream) throws IOException {\n        final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();\n        try {\n            int bytesRead;\n            final byte[] buffer = new byte[BUFFER_SIZE];\n            while ((bytesRead = zipInputstream.read(buffer)) > -1) {\n                byteArrayOutputStream.write(buffer, 0, bytesRead);\n            }\n            return byteArrayOutputStream.toByteArray();\n        } finally {\n            byteArrayOutputStream.close();\n        }\n    }\n\n    public static byte[] getZipEntryContent(final String entryName, final byte[] zipFile) throws IOException {\n        return getZipEntryContent(entryName, new ByteArrayInputStream(zipFile));\n    }\n\n    public static byte[] toByteArray(final Document document) throws IOException, TransformerException {\n        if (document == null) {\n            throw new IllegalArgumentException(\"Document should not be null.\");\n        }\n        final TransformerFactory transformerFactory = TransformerFactory.newInstance();\n        try {\n            transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, \"\"); // security-compliant\n            transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, \"\"); // security-compliant\n        } catch (IllegalArgumentException e) {\n            //ignored, if not supported by the implementation\n        }\n        final Transformer tf = transformerFactory.newTransformer();\n        tf.setOutputProperty(OutputKeys.ENCODING, FILE_ENCODING);\n        tf.setOutputProperty(OutputKeys.INDENT, \"yes\");\n        try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {\n            tf.transform(new DOMSource(document), new StreamResult(out));\n            return out.toByteArray();\n        }\n    }\n\n    public static byte[] addJarEntry(final byte[] jarToUpdate, final String entryName, final byte[] entryContent)\n            throws IOException {\n        final byte[] buffer = new byte[4096];\n        try (ByteArrayInputStream bais = new ByteArrayInputStream(jarToUpdate);\n                JarInputStream jis = new JarInputStream(bais); ByteArrayOutputStream out = new ByteArrayOutputStream();\n                JarOutputStream jos = new JarOutputStream(out)) {\n            JarEntry inEntry;\n            while ((inEntry = (JarEntry) jis.getNextEntry()) != null) {\n                if (!inEntry.getName().equals(entryName)) {\n                    jos.putNextEntry(new JarEntry(inEntry));\n                } else {\n                    throw new IllegalArgumentException(\"Jar entry \" + entryName + \" already exists in jar to update\");\n                }\n                int len;\n                while ((len = jis.read(buffer)) > 0) {\n                    jos.write(buffer, 0, len);\n                }\n                jos.flush();\n            }\n            final JarEntry entry = new JarEntry(entryName);\n            jos.putNextEntry(entry);\n            jos.write(entryContent);\n            jos.closeEntry();\n            jos.finish();\n            out.flush();\n            return out.toByteArray();\n        }\n    }\n\n    public static byte[] getPropertyAsString(final Properties prop, final String comment) throws IOException {\n        final ByteArrayOutputStream out = new ByteArrayOutputStream();\n        prop.store(out, comment);\n        return out.toByteArray();\n    }\n\n    public static String readResource(String fileName) throws IOException {\n        final String xmlContent;\n        final InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName);\n        if (inputStream == null) {\n            return null;\n        }\n        try {\n            xmlContent = read(inputStream);\n        } finally {\n            inputStream.close();\n        }\n        return xmlContent;\n    }\n\n    public static String md5(byte[] content) throws NoSuchAlgorithmException {\n        MessageDigest md5 = MessageDigest.getInstance(\"MD5\");\n        return new BigInteger(1, md5.digest(content)).toString(16);\n    }\n\n    public static void writeMD5(File file, byte[] bytes) throws NoSuchAlgorithmException, IOException {\n        write(file, md5(bytes).getBytes());\n    }\n\n    public static boolean checkMD5(File md5File, byte[] contentToCheck) throws NoSuchAlgorithmException {\n        if (!md5File.exists()) {\n            return false;\n        }\n        try {\n            return read(md5File).equals(md5(contentToCheck));\n        } catch (IOException e) {\n            return false;\n        }\n    }\n\n    public static void updatePropertyValue(File propertiesFile, final Map<String, String> pairs) throws IOException {\n        final BufferedReader br = new BufferedReader(new FileReader(propertiesFile));\n        try {\n            StringBuilder sb = new StringBuilder();\n            String line = br.readLine();\n\n            while (line != null) {\n                String lineToWrite = line;\n                if (!line.startsWith(\"#\")) {\n                    //it is not a comment\n                    final int splitCharIndex = line.indexOf(\"=\");\n                    if (splitCharIndex >= 0) {\n                        final String key = line.substring(0, splitCharIndex);\n                        //this is a key-value pair\n                        if (pairs.containsKey(key.trim())) {\n                            String value = pairs.get(key.trim());\n                            value = value.replace(\"\\\\\", \"\\\\\\\\\");\n                            lineToWrite = key + \"=\" + value;\n                        }\n                    }\n                }\n                sb.append(lineToWrite);\n                sb.append(System.lineSeparator());\n                line = br.readLine();\n            }\n            IOUtil.writeFile(propertiesFile, sb.toString());\n        } finally {\n            if (br != null) {\n                br.close();\n            }\n        }\n    }\n\n    public static String getContentTypeForIcon(String iconFilename) {\n        String contentType = MIMETYPES_FILE_TYPE_MAP.getContentType(iconFilename);\n        if (!contentType.startsWith(\"image\")) {\n            throw new IllegalArgumentException(\"An icon can't have mimetype \" + contentType);\n        }\n        return contentType;\n    }\n\n    /**\n     * return the content of the file from the classpath, the file must be at the root of the classpath\n     *\n     * @param fileName name of the file\n     * @return the optional, or empty if there is no file with that name\n     * @throws IOException when we are unable to read the file from the classpath\n     */\n    public static Optional<byte[]> getFileContent(final String fileName) throws IOException {\n        try (final InputStream inputStream = Thread.currentThread().getContextClassLoader()\n                .getResourceAsStream(fileName)) {\n            if (inputStream == null) {\n                return Optional.empty();\n            }\n            return Optional.of(getAllContentFrom(inputStream));\n        }\n    }\n\n    // Copied from org.bonitasoft.engine.io.IOUtil, as it is used by client applications (bonita-web-sp)\n    public static File createTempDirectory(final URI directoryPath) {\n        final File tmpDir = new File(directoryPath);\n        tmpDir.setReadable(true);\n        tmpDir.setWritable(true);\n\n        mkdirs(tmpDir);\n\n        FileUtils.isSymlink(tmpDir);\n\n        try {\n\n            Runtime.getRuntime().addShutdownHook(new Thread(() -> {\n                try {\n                    final boolean deleted = deleteDir(tmpDir);\n                    if (!deleted) {\n                        System.err.println(\n                                \"Unable to delete directory: \" + tmpDir + \". Trying with an alternative force delete.\");\n                        FileUtils.forceDelete(tmpDir);\n                    }\n                } catch (final IOException e) {\n                    throw new RuntimeException(e);\n                }\n            }));\n        } catch (IllegalStateException ignored) {\n            // happen in case of hook already registered and when shutting down\n        }\n        return tmpDir;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/io/PropertiesManager.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons.io;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.FileReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.io.Reader;\nimport java.net.URL;\nimport java.util.Properties;\n\n/**\n * @author Matthieu Chaffotte\n * @author Frederic Bouquet\n * @author Celine Souchet\n */\npublic class PropertiesManager {\n\n    private PropertiesManager() {\n        // For Sonar\n    }\n\n    public static void saveProperties(final Properties properties, final String pathName) throws IOException {\n        saveProperties(properties, new File(pathName));\n    }\n\n    public static void saveProperties(final Properties properties, final File file) throws IOException {\n        final FileOutputStream outputStream = new FileOutputStream(file);\n        try {\n            properties.store(outputStream, \"Storing modified properties\");\n        } finally {\n            outputStream.close();\n        }\n    }\n\n    public static Properties getProperties(final URL url) throws IOException {\n        final InputStreamReader reader = new InputStreamReader(url.openStream());\n        try {\n            return getProperties(reader);\n        } finally {\n            reader.close();\n        }\n    }\n\n    public static Properties getProperties(final String fileName) throws IOException {\n        return getProperties(new File(fileName));\n    }\n\n    public static Properties getProperties(final File file) throws IOException {\n        final FileReader reader = new FileReader(file);\n        try {\n            return getProperties(reader);\n        } finally {\n            reader.close();\n        }\n    }\n\n    private static Properties getProperties(final Reader reader) throws IOException {\n        final Properties properties = new Properties();\n        properties.load(reader);\n        return properties;\n    }\n\n    public static void savePropertiesToXML(final Properties properties, final File file) throws IOException {\n        final FileOutputStream outputStream = new FileOutputStream(file);\n        try {\n            properties.storeToXML(outputStream, \"Storing modified properties\", \"UTF-8\");\n        } finally {\n            outputStream.close();\n        }\n    }\n\n    public static Properties getPropertiesFromXML(final File file) throws IOException {\n        final Properties properties = new Properties();\n        final FileInputStream fileInputStream = new FileInputStream(file);\n        try {\n            properties.loadFromXML(fileInputStream);\n            return properties;\n        } finally {\n            fileInputStream.close();\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/time/DefaultEngineClock.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons.time;\n\nimport java.time.Instant;\n\n/**\n * This is the default engine clock that return the real system time\n *\n * @author Baptiste Mesta.\n */\npublic class DefaultEngineClock implements EngineClock {\n\n    @Override\n    public Instant now() {\n        return Instant.now();\n    }\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/time/EngineClock.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons.time;\n\nimport java.time.Instant;\n\n/**\n * The {@link EngineClock} give the current time to the engine.\n * It can be overridden mainly for testing purpose\n *\n * @author Baptiste Mesta.\n */\npublic interface EngineClock {\n\n    Instant now();\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/time/FixedEngineClock.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons.time;\n\nimport java.time.Instant;\nimport java.time.temporal.TemporalUnit;\n\n/**\n * This is a fixed clock with some methods to change this fixed time\n * Only used for testing purpose\n *\n * @author Baptiste Mesta.\n */\npublic class FixedEngineClock implements EngineClock {\n\n    private Instant now;\n\n    public FixedEngineClock(Instant now) {\n        this.now = now;\n    }\n\n    @Override\n    public Instant now() {\n        return now;\n    }\n\n    public void setNow(Instant now) {\n        this.now = now;\n    }\n\n    public void addTime(long amountToAdd, TemporalUnit unit) {\n        now = now.plus(amountToAdd, unit);\n    }\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/transaction/TransactionContent.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons.transaction;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface TransactionContent {\n\n    void execute() throws SBonitaException;\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/transaction/TransactionContentWithResult.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons.transaction;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface TransactionContentWithResult<T> extends TransactionContent {\n\n    T getResult();\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/data/instance/model/impl/OffsetDateTimeXStreamConverter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model.impl;\n\nimport java.time.OffsetDateTime;\nimport java.time.ZoneOffset;\nimport java.time.format.DateTimeFormatter;\nimport java.time.format.DateTimeParseException;\n\nimport com.thoughtworks.xstream.converters.basic.AbstractSingleValueConverter;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class OffsetDateTimeXStreamConverter extends AbstractSingleValueConverter {\n\n    public boolean canConvert(Class type) {\n        return OffsetDateTime.class.equals(type);\n    }\n\n    public String toString(Object source) {\n        return source == null ? null : ((OffsetDateTime) source).withOffsetSameInstant(ZoneOffset.UTC)\n                .format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);\n    }\n\n    public Object fromString(String str) {\n        try {\n            return OffsetDateTime.parse(str).withOffsetSameInstant(ZoneOffset.UTC);\n        } catch (DateTimeParseException e) {\n            throw new RuntimeException(\"OffsetDateTime failed to parse the incoming string\", e);\n        }\n    }\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/data/instance/model/impl/XStreamFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model.impl;\n\nimport java.util.Map;\nimport java.util.WeakHashMap;\n\nimport com.thoughtworks.xstream.XStream;\nimport com.thoughtworks.xstream.security.AnyTypePermission;\nimport org.bonitasoft.engine.xml.XStreamDenyList;\n\npublic class XStreamFactory {\n\n    private static final Map<ClassLoader, XStream> XSTREAM_MAP = new WeakHashMap<>();\n\n    public static XStream getXStream() {\n        final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();\n        return XSTREAM_MAP.computeIfAbsent(classLoader, cl -> {\n            var xStream = new XStream();\n            // Make the deserialization loose to avoid issues like in RUNTIME-1884\n            xStream.ignoreUnknownElements();\n            xStream.addPermission(AnyTypePermission.ANY);\n            // Block known deserialization gadget chain libraries to prevent RCE.\n            // A strict allowlist is not possible here because process variables can contain any Serializable type.\n            xStream.denyTypesByWildcard(XStreamDenyList.getDenyPatterns());\n            // Even though xStream now supports Java 8 date types, Bonita needs to convert offset date-time to UTC, by contract:\n            xStream.registerConverter(new OffsetDateTimeXStreamConverter());\n            return xStream;\n        });\n    }\n\n    /**\n     * Removes the XStream object related from given ClassLoader from the cache\n     *\n     * @param classLoader classLoader related to the XStreamObject to be removed.\n     */\n    public static void remove(ClassLoader classLoader) {\n        XSTREAM_MAP.remove(classLoader);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/home/Folder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.home;\n\nimport static org.bonitasoft.engine.commons.io.IOUtil.*;\n\nimport java.io.File;\nimport java.io.FileFilter;\nimport java.io.IOException;\nimport java.net.URI;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.apache.commons.io.FileUtils;\nimport org.apache.commons.io.filefilter.FileFileFilter;\n\n/**\n * @author Charles Souillard\n */\npublic class Folder {\n\n    private final File folder;\n\n    public Folder(final File folder) throws IOException {\n        if (folder == null) {\n            throw new IOException(\"Folder is null\");\n        }\n        this.folder = folder;\n    }\n\n    public Folder(final Folder folder, final String subFolder) throws IOException {\n        if (folder == null) {\n            throw new IOException(\"Folder is null\");\n        }\n        this.folder = new File(folder.getFile(), subFolder);\n    }\n\n    public File getFile() {\n        return this.folder;\n    }\n\n    private void checkFolderExists() throws IOException {\n        if (!folder.exists()) {\n            throw new IOException(\"Folder denoted by path \" + folder.getAbsolutePath() + \" does not exist.\");\n        }\n        if (!folder.isDirectory()) {\n            throw new IOException(\"Folder denoted by path \" + folder.getAbsolutePath() + \" is not a folder.\");\n        }\n    }\n\n    public void delete() throws IOException {\n        //System.err.println(\"DELETING FOLDER: \" + folder);\n        checkFolderExists();\n        deleteDir(folder);\n    }\n\n    public File getFile(final String name) throws IOException {\n        checkFolderExists();\n        return new File(folder, name);\n    }\n\n    public File newFile(final String name) throws IOException {\n        checkFolderExists();\n        final File newFile = new File(folder, name);\n        if (!newFile.createNewFile()) {\n            throw new IOException(\"File \" + newFile.getAbsolutePath() + \" cannot be created\");\n        }\n        return newFile;\n    }\n\n    public void create() throws IOException {\n        if (!folder.getParentFile().exists()) {\n            throw new IOException(\"Folder denoted by path \" + folder.getAbsolutePath()\n                    + \" cannot be created as its parent does not exist.\");\n        }\n        if (!folder.getParentFile().isDirectory()) {\n            throw new IOException(\"Folder denoted by path \" + folder.getAbsolutePath()\n                    + \" cannot be created as its parent is not a folder.\");\n        }\n        folder.mkdir();\n    }\n\n    public void copyTo(Folder destFolder) throws IOException {\n        checkFolderExists();\n        destFolder.create();\n        FileUtils.copyDirectory(this.getFile(), destFolder.getFile());\n    }\n\n    public Map<String, byte[]> listFilesAsResources() throws IOException {\n        checkFolderExists();\n        final Map<String, byte[]> resources = new HashMap<>();\n        final File[] files = this.folder.listFiles((FileFilter) FileFileFilter.FILE);\n        if (files != null) {\n            for (File file : files) {\n                resources.put(file.getName(), getFileContent(file));\n            }\n        }\n        return resources;\n    }\n\n    protected byte[] getFileContent(File file) throws IOException {\n        return getAllContentFrom(file);\n    }\n\n    @Override\n    public String toString() {\n        final StringBuilder sb = new StringBuilder();\n        sb.append(\"Folder{folder=\");\n        sb.append(folder);\n        sb.append(\" --- exists:\");\n        sb.append(folder.exists());\n        sb.append(\" --- is directory:\");\n        sb.append(folder.isDirectory());\n        sb.append('}');\n        return sb.toString();\n    }\n\n    public boolean exists() {\n        return this.folder.exists();\n    }\n\n    public URI toURI() throws IOException {\n        checkFolderExists();\n        return this.folder.toURI();\n    }\n\n    public Folder createIfNotExists() throws IOException {\n        if (!this.folder.exists()) {\n            create();\n        }\n        return this;\n    }\n\n    public void createAsTemporaryFolder() {\n        createTempDirectory(this.folder.toURI());\n    }\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/home/FolderMgr.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.home;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.lang.management.ManagementFactory;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Charles Souillard\n * @author Emmanuel Duchastenier\n */\npublic class FolderMgr {\n\n    public static final Logger LOGGER = LoggerFactory.getLogger(FolderMgr.class);\n    public static final String TEMP_FOLDER_NAME_PREFIX = \"bonita_engine_\";\n\n    private static Folder getFolder(final File baseFolder, final String subFolder) throws IOException {\n        return new Folder(new Folder(baseFolder), subFolder);\n    }\n\n    static Folder getFolder(final Folder baseFolder, final String subFolder) throws IOException {\n        return new Folder(baseFolder, subFolder);\n    }\n\n    static Folder getTempFolder() throws IOException {\n        File systemTempFolder = new File(System.getProperty(\"java.io.tmpdir\"));\n        final Folder tempFolder = getFolder(systemTempFolder, TEMP_FOLDER_NAME_PREFIX + getJvmName());\n        if (!tempFolder.exists()) {\n            warnIfSomeTempFolderAlreadyExists(systemTempFolder);\n            tempFolder.createAsTemporaryFolder();\n        }\n        return tempFolder;\n    }\n\n    private static void warnIfSomeTempFolderAlreadyExists(File systemTempFolder) {\n        File[] files = systemTempFolder\n                .listFiles((dir, name) -> dir.isDirectory() && name.startsWith(TEMP_FOLDER_NAME_PREFIX));\n        List<File> temporaryFolders = Arrays.asList(files != null ? files : new File[0]);\n        if (!temporaryFolders.isEmpty()) {\n            LOGGER.warn(\"The following temporary folders were not deleted on the previous shutdown. \" +\n                    \"This can happen when your JVM crashed or if you did not properly stop the platform.\");\n            LOGGER.warn(\"Delete these folders to free up space:\");\n            for (File temporaryFolder : temporaryFolders) {\n                LOGGER.warn(temporaryFolder.getAbsolutePath());\n            }\n        }\n\n    }\n\n    static Folder getPlatformTempFolder() throws IOException {\n        return getFolder(getTempFolder(), \"platform\").createIfNotExists();\n    }\n\n    public static Folder getLicensesFolder() throws IOException {\n        return getFolder(getTempFolder(), \"licenses\");\n    }\n\n    private static Folder getPlatformClassLoaderFolder() throws IOException {\n        return getFolder(getPlatformTempFolder(), \"classloaders\").createIfNotExists();\n    }\n\n    private static String getJvmName() {\n        return ManagementFactory.getRuntimeMXBean().getName();\n    }\n\n    static Folder getPlatformGlobalClassLoaderFolder() throws IOException {\n        final Folder globalFolder = getFolder(getPlatformClassLoaderFolder(), \"global\");\n        globalFolder.createIfNotExists();\n        return globalFolder;\n    }\n\n    private static Folder getPlatformLocalClassLoaderFolder() throws IOException {\n        final Folder localFolder = getFolder(getPlatformClassLoaderFolder(), \"local\");\n        localFolder.createIfNotExists();\n        return localFolder;\n    }\n\n    static Folder getPlatformLocalClassLoaderFolder(String artifactType) throws IOException {\n        final Folder localFolder = getPlatformLocalClassLoaderFolder();\n        final Folder artifactTypeFolder = getFolder(localFolder, artifactType);\n        artifactTypeFolder.createIfNotExists();\n        return artifactTypeFolder;\n    }\n\n    static Folder getPlatformLocalClassLoaderFolder(String artifactType, long artifactId) throws IOException {\n        final Folder artifactTypeFolder = getPlatformLocalClassLoaderFolder(artifactType);\n        final Folder artifactIdFolder = getFolder(artifactTypeFolder, Long.toString(artifactId));\n        artifactIdFolder.createIfNotExists();\n        return artifactIdFolder;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/mdc/AbstractMDC.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.mdc;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Optional;\n\nimport org.slf4j.MDC;\n\n/**\n * The base class for our Mapped Diagnostic Contexts (MDC).\n *\n * @author Vincent Hemery\n */\npublic abstract class AbstractMDC implements AutoCloseable, MDCConstants {\n\n    private Map<String, String> entriesToRestore;\n\n    public AbstractMDC(Map<String, String> contextValues) {\n        if (contextValues != null) {\n            /*\n             * We do not prevent nested MDCs with conflicting values,\n             * as it may be legit e.g. in case of an ActivityCall\n             * (which executes other flow nodes inside the parent flow node).\n             * So we allow erasing any value in the context,\n             * but also remember to restore the context in its original state at closure.\n             */\n            // remember entries to restore/remove\n            Map<String, String> oldMap = Optional.ofNullable(MDC.getCopyOfContextMap())\n                    .orElseGet(Collections::emptyMap);\n            entriesToRestore = new HashMap<>(contextValues.size());\n            contextValues.forEach((k, newValue) -> {\n                var valueToRestore = oldMap.getOrDefault(k, null);\n                entriesToRestore.put(k, valueToRestore);\n            });\n            // store new values\n            contextValues.forEach((key, value) -> {\n                if (value != null) {\n                    MDC.put(key, value);\n                }\n            });\n        } else {\n            entriesToRestore = Collections.emptyMap();\n        }\n    }\n\n    @Override\n    public void close() {\n        // restore previous context\n        entriesToRestore.forEach((k, v) -> {\n            if (v == null) {\n                MDC.remove(k);\n            } else {\n                MDC.put(k, v);\n            }\n        });\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/mdc/FlowNodeInstanceMDC.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.mdc;\n\nimport java.util.Map;\nimport java.util.Optional;\n\npublic class FlowNodeInstanceMDC extends AbstractMDC implements AutoCloseable, MDCConstants {\n\n    /**\n     * Build a flow node execution context when no user is involved (e.g. service task).\n     *\n     * @param flowNodeInstanceId the id of the flow node instance\n     * @param processDefinitionId the id of the process definition\n     * @param processInstanceId the id of the process instance / case\n     * @param rootProcessInstanceId the id of the root process instance / case\n     */\n    public FlowNodeInstanceMDC(long flowNodeInstanceId, long processDefinitionId, long processInstanceId,\n            long rootProcessInstanceId) {\n        this(flowNodeInstanceId, Optional.empty(), Optional.empty(), processDefinitionId, processInstanceId,\n                rootProcessInstanceId);\n    }\n\n    /**\n     * Build a flow node execution context when a user is authenticated.\n     *\n     * @param flowNodeInstanceId the id of the flow node instance\n     * @param executerId the id of the user executing the flow node\n     * @param substituteUserId the id of the admin user activating the flow node for the executer\n     * @param processDefinitionId the id of the process definition\n     * @param processInstanceId the id of the process instance / case\n     * @param rootProcessInstanceId the id of the root process instance / case\n     */\n    public FlowNodeInstanceMDC(long flowNodeInstanceId, long executerId, long substituteUserId,\n            long processDefinitionId, long processInstanceId, long rootProcessInstanceId) {\n        this(flowNodeInstanceId, Optional.of(executerId), Optional.of(substituteUserId), processDefinitionId,\n                processInstanceId, rootProcessInstanceId);\n    }\n\n    /**\n     * Build a flow node execution context.\n     *\n     * @param flowNodeInstanceId the id of the flow node instance\n     * @param executerId the eventual id of the user executing the flow node\n     * @param substituteUserId the eventual id of the admin user activating the flow node for the executer\n     * @param processDefinitionId the id of the process definition\n     * @param processInstanceId the id of the process instance / case\n     * @param rootProcessInstanceId the id of the root process instance / case\n     */\n    public FlowNodeInstanceMDC(long flowNodeInstanceId, Optional<Long> executerId, Optional<Long> substituteUserId,\n            long processDefinitionId, long processInstanceId, long rootProcessInstanceId) {\n        super(buildContextMap(flowNodeInstanceId, executerId, substituteUserId, processDefinitionId, processInstanceId,\n                rootProcessInstanceId));\n    }\n\n    private static Map<String, String> buildContextMap(long flowNodeInstanceId, Optional<Long> executerId,\n            Optional<Long> substituteUserId, long processDefinitionId, long processInstanceId,\n            long rootProcessInstanceId) {\n        if (!substituteUserId.equals(executerId)) {\n            // executed by substituteUserId for executerId\n            return Map.of(\n                    FLOW_NODE_INSTANCE_ID, String.valueOf(flowNodeInstanceId),\n                    USER_ID, executerId.map(String::valueOf).orElse(null),\n                    SUBSTITUTE_USER_ID, substituteUserId.map(String::valueOf).orElse(null),\n                    PROCESS_DEFINITION_ID, String.valueOf(processDefinitionId),\n                    PROCESS_INSTANCE_ID, String.valueOf(processInstanceId),\n                    ROOT_PROCESS_INSTANCE_ID, String.valueOf(rootProcessInstanceId));\n        } else if (executerId.isPresent()) {\n            // no need for SUBSTITUTE_USER_ID\n            return Map.of(\n                    FLOW_NODE_INSTANCE_ID, String.valueOf(flowNodeInstanceId),\n                    USER_ID, executerId.map(String::valueOf).orElse(null),\n                    PROCESS_DEFINITION_ID, String.valueOf(processDefinitionId),\n                    PROCESS_INSTANCE_ID, String.valueOf(processInstanceId),\n                    ROOT_PROCESS_INSTANCE_ID, String.valueOf(rootProcessInstanceId));\n        } else {\n            // no authenticated user\n            return Map.of(\n                    FLOW_NODE_INSTANCE_ID, String.valueOf(flowNodeInstanceId),\n                    PROCESS_DEFINITION_ID, String.valueOf(processDefinitionId),\n                    PROCESS_INSTANCE_ID, String.valueOf(processInstanceId),\n                    ROOT_PROCESS_INSTANCE_ID, String.valueOf(rootProcessInstanceId));\n        }\n    }\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/mdc/MDCConstants.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.mdc;\n\n/**\n * <p>These constants are used as logging keys for Mapped Diagnostic Context (MDC).</p>\n * <p>The <code>REQUEST_*</code> constants are the same as in ch.qos.logback.classic.ClassicConstants (but with no\n * dependency):\n * <ul><li>req.remoteHost</li><li>req.userAgent</li><li>req.requestURI</li><li>req.queryString</li><li>req.requestURL</li><li>req.method</li><li>req.xForwardedFor</li></ul>\n * </p>\n */\npublic interface MDCConstants {\n\n    /** The technical ID of the concerned process instance (a.k.a. case ID). */\n    String PROCESS_INSTANCE_ID = \"processInstanceId\";\n    /** The technical ID of the concerned root process instance (a.k.a. root case ID). */\n    String ROOT_PROCESS_INSTANCE_ID = \"rootProcessInstanceId\";\n    /** The technical ID of the process definition. */\n    String PROCESS_DEFINITION_ID = \"processDefinitionId\";\n    /** The technical ID of the executing flow node instance in the process. */\n    String FLOW_NODE_INSTANCE_ID = \"flowNodeInstanceId\";\n    /** The technical ID of the user who performed the current operation. */\n    String USER_ID = \"userId\";\n    /** The technical ID of the admin user who triggered an operation for another user. */\n    String SUBSTITUTE_USER_ID = \"substituteUserId\";\n    /** The ID of the JPA transaction. */\n    String TRANSACTION_ID = \"txUid\";\n    /** The ID of the HTTP request. */\n    String REQUEST_ID = \"req.requestId\";\n    /** The correlation ID identifying a larger operation than the HTTP request. */\n    String CORRELATION_REQUEST_ID = \"req.correlationId\";\n\n    public static final String REQUEST_REMOTE_HOST_MDC_KEY = \"req.remoteHost\";\n    public static final String REQUEST_USER_AGENT_MDC_KEY = \"req.userAgent\";\n    public static final String REQUEST_REQUEST_URI = \"req.requestURI\";\n    public static final String REQUEST_QUERY_STRING = \"req.queryString\";\n    public static final String REQUEST_REQUEST_URL = \"req.requestURL\";\n    public static final String REQUEST_METHOD = \"req.method\";\n    public static final String REQUEST_X_FORWARDED_FOR = \"req.xForwardedFor\";\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/mdc/MDCHelper.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.mdc;\n\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.WeakHashMap;\nimport java.util.concurrent.Callable;\nimport java.util.function.Supplier;\n\nimport org.slf4j.MDC;\n\n/**\n * <p>Provides usefull methods for working with MDC.</p>\n * <p>Use the various {@link #tryWithMDC} methods to execute a runnable or a callable with MDC information in the\n * context.<br/>\n * You may provide a context {@link Supplier} directly, or a key object for which a {@link Supplier} was previously\n * registered with {@link #supplyMDC}\n * methods.</p>\n * <p>Use the appropriate Runnable or Callable implementation dependending on the number of Exception you want to\n * throw.<br/>\n * When used with lambdas, lambda will take only the common exception superclass. So instead of writing\n *\n * <pre>\n * Supplier&lt;AbstractMDC&gt; mdc = () -&gt; new AbstractMDC(Map.of(key, value)) {};\n * MDCHelper.tryWithMDC(mdc, ()->{\n *     // throws Exception1 or Exception2\n *     ...\n * });\n * </pre>\n *\n * You should declare the type and write\n *\n * <pre>\n * Supplier&lt;AbstractMDC&gt; mdc = () -&gt; new AbstractMDC(Map.of(key, value)) {};\n * CheckedRunnable2&lt;Exception1, Exception2&gt; run = ()->{\n *     // throws Exception1 or Exception2\n *     ...\n * };\n * MDCHelper.tryWithMDC(mdc, run);\n * </pre>\n *\n * <p>When an exception is thrown within a {@link #tryWithMDC} method and not caught by the Runnable or Callable, the\n * current context is registered with\n * {@link #supplyMDC} for the exception.<br/>\n * So calling {@link #tryWithMDC(Object, CheckedRunnable4)} with the exception as <code>usingObject</code> key will\n * allow you to log the exception with the\n * original context.<br/>\n * You can even call this method when no context was supplied, as it would have no noticeable effect.</p>\n *\n * @see AbstractMDC the base implementation for all MDC classes\n */\npublic class MDCHelper {\n\n    /**\n     * Like {@link Runnable} but throwing 4 checked exceptions\n     */\n    @FunctionalInterface\n    public interface CheckedRunnable4<E1 extends Throwable, E2 extends Throwable, E3 extends Throwable, E4 extends Throwable> {\n\n        void run() throws E1, E2, E3, E4;\n    }\n\n    /**\n     * Like {@link Runnable} but throwing 3 checked exceptions\n     */\n    @FunctionalInterface\n    public interface CheckedRunnable3<E1 extends Throwable, E2 extends Throwable, E3 extends Throwable>\n            extends CheckedRunnable4<E1, E2, E3, E1> {\n    };\n\n    /**\n     * Like {@link Runnable} but throwing 2 checked exceptions\n     */\n    @FunctionalInterface\n    public interface CheckedRunnable2<E1 extends Throwable, E2 extends Throwable>\n            extends CheckedRunnable3<E1, E2, E1> {\n    };\n\n    /**\n     * Like {@link Runnable} but throwing a checked exception\n     */\n    @FunctionalInterface\n    public interface CheckedRunnable<E extends Throwable>\n            extends CheckedRunnable2<E, E> {\n    };\n\n    /**\n     * Like {@link Callable} but throwing 4 checked exceptions\n     */\n    @FunctionalInterface\n    public interface CheckedCallable4<V, E1 extends Throwable, E2 extends Throwable, E3 extends Throwable, E4 extends Throwable> {\n\n        V call() throws E1, E2, E3, E4;\n    }\n\n    /**\n     * Like {@link Callable} but throwing 3 checked exceptions\n     */\n    @FunctionalInterface\n    public interface CheckedCallable3<V, E1 extends Throwable, E2 extends Throwable, E3 extends Throwable>\n            extends CheckedCallable4<V, E1, E2, E3, E1> {\n    }\n\n    /**\n     * Like {@link Callable} but throwing 2 checked exceptions\n     */\n    @FunctionalInterface\n    public interface CheckedCallable2<V, E1 extends Throwable, E2 extends Throwable>\n            extends CheckedCallable3<V, E1, E2, E1> {\n    }\n\n    /**\n     * Like {@link Callable} but throwing a checked exception\n     */\n    @FunctionalInterface\n    public interface CheckedCallable<V, E extends Throwable> extends CheckedCallable2<V, E, E> {\n    }\n\n    private static Map<Object, MDCHelper> instances = new WeakHashMap<>();\n\n    /** Builds the MDC */\n    private Supplier<? extends AbstractMDC> supplier;\n\n    private MDCHelper(Supplier<? extends AbstractMDC> mdcSupplier) {\n        supplier = mdcSupplier;\n    }\n\n    /**\n     * Make a supplier that will get the current context, to create a similar MDC in its time (usually in another\n     * thread)\n     *\n     * @return MDC supplier holding information from current context in this thread\n     */\n    public static Supplier<? extends AbstractMDC> makeCurrentContextSupplier() {\n        var contextMap = MDC.getCopyOfContextMap();\n        return () -> new AbstractMDC(contextMap) {\n        };\n    }\n\n    /**\n     * Supply a MDC to an object that may use it later.\n     *\n     * @param mdcSupplier supplies a MDC\n     * @param usingObject the object that will need MDC\n     */\n    public static void supplyMDC(Supplier<? extends AbstractMDC> mdcSupplier, Object usingObject) {\n        supplyMDC(mdcSupplier, usingObject, true);\n    }\n\n    /**\n     * Supply a MDC to an object that may use it later.\n     *\n     * @param mdcSupplier supplies a MDC\n     * @param usingObject the object that will need MDC\n     * @param overwrite whether to overwrite an existing supplier\n     */\n    public static void supplyMDC(Supplier<? extends AbstractMDC> mdcSupplier, Object usingObject, boolean overwrite) {\n        if (overwrite) {\n            instances.put(usingObject, new MDCHelper(mdcSupplier));\n        } else {\n            // do not build a new Helper when one is already present\n            instances.computeIfAbsent(usingObject, k -> new MDCHelper(mdcSupplier));\n        }\n    }\n\n    /**\n     * Get an MDC, to encapsulate in a try with.\n     *\n     * @param usingObject the object using the MDC, for which a supplier may have been provided\n     * @return the supplied MDC or null\n     */\n    public static AbstractMDC getMDC(Object usingObject) {\n        var helper = getHelper(usingObject);\n        return helper.map(h -> h.supplier).map(Supplier::get).orElse(null);\n    }\n\n    /**\n     * Get helper associated to using object.\n     *\n     * @param usingObject the object using the MDC, for which a supplier may have been provided\n     * @return the associated helper\n     */\n    private static Optional<MDCHelper> getHelper(Object usingObject) {\n        var helper = Optional.ofNullable(instances.remove(usingObject));\n        // when not present, try and take the context associated to the cause exception if available\n        if (!helper.isPresent() && usingObject instanceof Throwable) {\n            var cause = ((Throwable) usingObject).getCause();\n            return getHelper(cause);\n        }\n        return helper;\n    }\n\n    /**\n     * Try and invoke a callable with MDC\n     *\n     * @param <V> the result type\n     * @param mdcSupplier supplies a MDC\n     * @param callable the callable with result\n     * @return the callable result\n     * @throws E exception in callable\n     */\n    public static <V, E1 extends Throwable, E2 extends Throwable, E3 extends Throwable, E4 extends Throwable> V tryWithMDC(\n            Supplier<? extends AbstractMDC> mdcSupplier,\n            CheckedCallable4<V, E1, E2, E3, E4> callable) throws E1, E2, E3, E4 {\n        try (var mdc = mdcSupplier.get()) {\n            try {\n                return callable.call();\n            } catch (Throwable exception) {\n                // attach context to exception before rethrowing it\n                supplyMDC(makeCurrentContextSupplier(), exception, false);\n                throw exception;\n            }\n        }\n    }\n\n    /**\n     * Try and invoke a callable with MDC\n     *\n     * @param <V> the result type\n     * @param usingObject the object using the MDC, for which a supplier may have been provided\n     * @param callable the callable with result\n     * @return the callable result\n     * @throws E exception in callable\n     */\n    public static <V, E1 extends Throwable, E2 extends Throwable, E3 extends Throwable, E4 extends Throwable> V tryWithMDC(\n            Object usingObject, CheckedCallable4<V, E1, E2, E3, E4> callable) throws E1, E2, E3, E4 {\n        try (var mdc = getMDC(usingObject)) {\n            try {\n                return callable.call();\n            } catch (Throwable exception) {\n                // attach context to exception before rethrowing it\n                supplyMDC(makeCurrentContextSupplier(), exception, false);\n                throw exception;\n            }\n        }\n    }\n\n    /**\n     * Try and invoke a runnable with MDC\n     *\n     * @param mdcSupplier supplies a MDC\n     * @param runnable the runnable (without result)\n     * @throws E exception in runnable\n     */\n    public static <E1 extends Throwable, E2 extends Throwable, E3 extends Throwable, E4 extends Throwable> void tryWithMDC(\n            Supplier<? extends AbstractMDC> mdcSupplier,\n            CheckedRunnable4<E1, E2, E3, E4> runnable)\n            throws E1, E2, E3, E4 {\n        CheckedCallable4<Void, E1, E2, E3, E4> callable = () -> {\n            runnable.run();\n            return null;\n        };\n        tryWithMDC(mdcSupplier, callable);\n    }\n\n    /**\n     * Try and invoke a runnable with MDC\n     *\n     * @param usingObject the object using the MDC, for which a supplier may have been provided\n     * @param runnable the runnable (without result)\n     * @throws E exception in runnable\n     */\n    public static <E1 extends Throwable, E2 extends Throwable, E3 extends Throwable, E4 extends Throwable> void tryWithMDC(\n            Object usingObject, CheckedRunnable4<E1, E2, E3, E4> runnable) throws E1, E2, E3, E4 {\n        CheckedCallable4<Void, E1, E2, E3, E4> callable = () -> {\n            runnable.run();\n            return null;\n        };\n        tryWithMDC(usingObject, callable);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/mdc/MDCTransmitingThreadPoolExecutor.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.mdc;\n\nimport java.util.concurrent.BlockingQueue;\nimport java.util.concurrent.RejectedExecutionHandler;\nimport java.util.concurrent.ThreadFactory;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * A {@link ThreadPoolExecutor} that transmits the MDC to the executing thread\n *\n * @author Vincent Hemery\n */\npublic class MDCTransmitingThreadPoolExecutor extends ThreadPoolExecutor {\n\n    public MDCTransmitingThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,\n            BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {\n        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);\n    }\n\n    @Override\n    public void execute(Runnable command) {\n        var mdcSupplier = MDCHelper.makeCurrentContextSupplier();\n        super.execute(() -> MDCHelper.tryWithMDC(mdcSupplier, command::run));\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/mdc/ProcessInstanceMDC.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.mdc;\n\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\n\npublic class ProcessInstanceMDC extends AbstractMDC implements AutoCloseable, MDCConstants {\n\n    /**\n     * Build a process instance context.\n     *\n     * @param processInstanceId the id of the process instance / case\n     * @param executerId the id of process starter execution\n     * @param substituteUserId the id of substitute process starter execution\n     * @param processDefinitionId the id of the process definition\n     * @param rootProcessInstanceId the id of the root process instance / case\n     */\n    public ProcessInstanceMDC(long processInstanceId,\n            Optional<Long> executerId,\n            Optional<Long> substituteUserId,\n            long processDefinitionId,\n            long rootProcessInstanceId) {\n        super(buildContextMap(executerId, substituteUserId, processDefinitionId, processInstanceId,\n                rootProcessInstanceId));\n    }\n\n    private static Map<String, String> buildContextMap(Optional<Long> executerId,\n            Optional<Long> substituteUserId, long processDefinitionId, long processInstanceId,\n            long rootProcessInstanceId) {\n        if (!substituteUserId.equals(executerId)) {\n            // executed by substituteUserId for executerId\n            return Map.of(\n                    USER_ID, Objects.requireNonNull(executerId.map(String::valueOf).orElse(null)),\n                    SUBSTITUTE_USER_ID, Objects.requireNonNull(substituteUserId.map(String::valueOf).orElse(null)),\n                    PROCESS_DEFINITION_ID, String.valueOf(processDefinitionId),\n                    PROCESS_INSTANCE_ID, String.valueOf(processInstanceId),\n                    ROOT_PROCESS_INSTANCE_ID, String.valueOf(rootProcessInstanceId));\n        } else if (executerId.isPresent()) {\n            // no need for SUBSTITUTE_USER_ID\n            return Map.of(\n                    USER_ID, executerId.map(String::valueOf).orElse(null),\n                    PROCESS_DEFINITION_ID, String.valueOf(processDefinitionId),\n                    PROCESS_INSTANCE_ID, String.valueOf(processInstanceId),\n                    ROOT_PROCESS_INSTANCE_ID, String.valueOf(rootProcessInstanceId));\n        } else {\n            // no authenticated user\n            return Map.of(\n                    PROCESS_DEFINITION_ID, String.valueOf(processDefinitionId),\n                    PROCESS_INSTANCE_ID, String.valueOf(processInstanceId),\n                    ROOT_PROCESS_INSTANCE_ID, String.valueOf(rootProcessInstanceId));\n        }\n    }\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/mdc/UserIdMDC.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.mdc;\n\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.function.Supplier;\n\npublic class UserIdMDC extends AbstractMDC {\n\n    /**\n     * Build MDC with user id.\n     *\n     * @param userId authenticated user id\n     */\n    public UserIdMDC(Long userId) {\n        super(Map.of(MDCConstants.USER_ID, Long.toString(userId)));\n    }\n\n    /**\n     * Get a supplier from an optional user id\n     *\n     * @param userId authenticated user id or empty\n     * @return supplier supplies a valid MDC or <code>null</code>\n     */\n    public static Supplier<UserIdMDC> supplierFromOptionalId(Optional<Long> userId) {\n        return () -> userId.map(UserIdMDC::new).orElse(null);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/monitoring/DefaultExecutorServiceMetricsProvider.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.monitoring;\n\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.ThreadPoolExecutor;\n\nimport io.micrometer.core.instrument.MeterRegistry;\nimport io.micrometer.core.instrument.binder.jvm.ExecutorServiceMetrics;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class DefaultExecutorServiceMetricsProvider implements ExecutorServiceMetricsProvider {\n\n    @Override\n    public ExecutorService bind(MeterRegistry meterRegistry, ThreadPoolExecutor executorService,\n            String executorServiceName) {\n        return ExecutorServiceMetrics.monitor(meterRegistry, executorService, executorServiceName,\n                List.of());\n    }\n\n    @Override\n    public void bindMetricsOnly(MeterRegistry meterRegistry, ThreadPoolExecutor executorService,\n            String executorServiceName) {\n        new ExecutorServiceMetrics(executorService, executorServiceName, List.of()).bindTo(meterRegistry);\n    }\n\n    @Override\n    public void unbind(MeterRegistry meterRegistry, String executorServiceName) {\n        //right now, there is no unbind method on the MeterBinder, manually unbind them\n        Optional.ofNullable(meterRegistry.find(\"executor\").timer()).ifPresent(meterRegistry::remove);\n        Optional.ofNullable(meterRegistry.find(\"executor.active\").gauge()).ifPresent(meterRegistry::remove);\n        Optional.ofNullable(meterRegistry.find(\"executor.pool.size\").gauge())\n                .ifPresent(meterRegistry::remove);\n        Optional.ofNullable(meterRegistry.find(\"executor.pool.max\").gauge())\n                .ifPresent(meterRegistry::remove);\n        Optional.ofNullable(meterRegistry.find(\"executor.pool.core\").gauge())\n                .ifPresent(meterRegistry::remove);\n        Optional.ofNullable(meterRegistry.find(\"executor.queue.remaining\").gauge())\n                .ifPresent(meterRegistry::remove);\n        Optional.ofNullable(meterRegistry.find(\"executor.queued\").gauge()).ifPresent(meterRegistry::remove);\n        Optional.ofNullable(meterRegistry.find(\"executor.completed\").functionCounter())\n                .ifPresent(meterRegistry::remove);\n        Optional.ofNullable(meterRegistry.find(\"executor.idle\").timer()).ifPresent(meterRegistry::remove);\n    }\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/monitoring/ExecutorServiceMetricsProvider.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.monitoring;\n\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.ThreadPoolExecutor;\n\nimport io.micrometer.core.instrument.MeterRegistry;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic interface ExecutorServiceMetricsProvider {\n\n    /**\n     * bind the executor service to the registry, only support ThreadPool right now, see\n     * {@link ExecutorServiceMetricsProvider#unbind(MeterRegistry, String)}\n     *\n     * @return the monitored executor service with monitoring on execution time\n     */\n    ExecutorService bind(MeterRegistry meterRegistry, ThreadPoolExecutor executorService, String executorServiceName);\n\n    /**\n     * bind the executor service to the registry, only support ThreadPool right now, see\n     * {@link ExecutorServiceMetricsProvider#unbind(MeterRegistry, String)}\n     * This will only bind statisctics of the Threadpool, and not time taks.\n     */\n    void bindMetricsOnly(MeterRegistry meterRegistry, ThreadPoolExecutor executorService, String executorServiceName);\n\n    /**\n     * unbind all metrics of the named executor service from the meter registry\n     */\n    void unbind(MeterRegistry meterRegistry, String executorServiceName);\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/monitoring/NoOpExecutorServiceMetricsProvider.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.monitoring;\n\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.ThreadPoolExecutor;\n\nimport io.micrometer.core.instrument.MeterRegistry;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class NoOpExecutorServiceMetricsProvider implements ExecutorServiceMetricsProvider {\n\n    @Override\n    public ExecutorService bind(MeterRegistry meterRegistry, ThreadPoolExecutor executorService,\n            String executorServiceName) {\n        return executorService;\n    }\n\n    @Override\n    public void bindMetricsOnly(MeterRegistry meterRegistry, ThreadPoolExecutor executorService,\n            String executorServiceName) {\n    }\n\n    @Override\n    public void unbind(MeterRegistry meterRegistry, String executorServiceName) {\n    }\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/properties/BonitaConfigProperty.java",
    "content": "/**\n * Copyright (C) 2025 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.properties;\n\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * Represents a Bonita configuration property. It supports properties passed as Java System property (like\n * -Dmy.custom-property.subproperty=MY_VALUE) or as environment variables (like MY_CUSTOMPROPERTY_SUBPROPERTY=MY_VALUE).\n * If both are defined, System Property has precedence.\n *\n * @author Emmanuel Duchastenier\n */\n@Slf4j\npublic abstract class BonitaConfigProperty {\n\n    /*\n     * System property version of the property (lowercase, with dots):\n     */\n    protected final String propertyKey;\n\n    /*\n     * Display name for logs\n     */\n    protected final String displayName;\n\n    // A simple \"cache\" to avoid logging the same property multiple times\n    private static final Set<String> alreadyLoggedProperties = ConcurrentHashMap.newKeySet();\n\n    /**\n     * @param displayName the display name of the property, used in logs\n     * @param propertyKey the \"system property\" version of the property, typically in lowercase and with dots.\n     */\n    public BonitaConfigProperty(String displayName, String propertyKey) {\n        this.displayName = displayName;\n        this.propertyKey = propertyKey;\n    }\n\n    protected void logInitializationMessagesIfFirstTime() {\n        if (alreadyLoggedProperties.add(this.propertyKey)) {\n            log.info(getInitializationMessage());\n        }\n    }\n\n    abstract String getInitializationMessage();\n\n    protected String envPropertyKey() {\n        return propertyKey.toUpperCase().replace(\".\", \"_\").replaceAll(\"-\", \"\");\n    }\n\n    protected String getProperty(String defaultValue) {\n        return System.getProperty(propertyKey, System.getenv().getOrDefault(envPropertyKey(), defaultValue));\n    }\n\n    // for test reset\n    static void clearLoggedProperties() {\n        alreadyLoggedProperties.clear();\n    }\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/properties/BonitaProperty.java",
    "content": "/**\n * Copyright (C) 2025 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.properties;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.springframework.core.annotation.AliasFor;\n\n/**\n * Annotation to be used on fields to inject a property value from the environment.\n * It works very much like the <code>&#064;Value</code> annotation from Spring.\n * Can be provided with a list of deprecated property names. If so, when used, the annotation processor will first look\n * for the\n * deprecated properties and if found, will log a warning.\n * Eg:\n *\n * <pre class=\"code\">\n *\n * &#064;BonitaProperty(name = \"my.property\", deprecated = { \"old.my.property\", \"older.my.property\" })\n * private String myProperty;\n * </pre>\n *\n * In simple cases where no deprecated properties are defined, the name attribute can be omitted (and passed a default\n * unnamed 'value' attribute):\n *\n * <pre class=\"code\">\n *\n * &#064;BonitaProperty(\"my.property\")\n * private String myProperty;\n * </pre>\n *\n * @see BonitaPropertyAnnotationProcessor how this Annotation is processed\n */\n@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER })\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface BonitaProperty {\n\n    @AliasFor(\"name\")\n    String value() default \"\";\n\n    @AliasFor(\"value\")\n    String name() default \"\";\n\n    String[] deprecated() default {};\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/properties/BonitaPropertyAnnotationProcessor.java",
    "content": "/**\n * Copyright (C) 2025 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.properties;\n\nimport java.lang.reflect.Field;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.InvalidPropertyException;\nimport org.springframework.beans.factory.BeanInitializationException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.config.BeanPostProcessor;\nimport org.springframework.core.env.Environment;\nimport org.springframework.stereotype.Component;\n\n/**\n * Spring {@link BeanPostProcessor} that parses {@link BonitaProperty} and sets the value of the annotated field.\n * If a deprecated property name is detected, then a warning message is issued.\n *\n * @see BonitaProperty\n */\n@Component\n@Slf4j\npublic class BonitaPropertyAnnotationProcessor implements BeanPostProcessor {\n\n    @Autowired\n    private Environment environment;\n\n    @Override\n    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {\n        for (Field field : bean.getClass().getDeclaredFields()) {\n            if (field.isAnnotationPresent(BonitaProperty.class)) {\n                BonitaProperty bonitaProperty = field.getAnnotation(BonitaProperty.class);\n                String propertyName = bonitaProperty.value();\n                if (propertyName.isEmpty()) {\n                    propertyName = bonitaProperty.name();\n                }\n                if (propertyName.isEmpty()) {\n                    throw new InvalidPropertyException(bean.getClass(), \"unset property name\",\n                            \"@BonitaProperty 'name' or 'value' attribute is mandatory\");\n                }\n                String[] deprecated = bonitaProperty.deprecated();\n\n                String resolvedValue = null;\n                for (String deprecatedPropertyName : deprecated) {\n                    resolvedValue = environment.getProperty(deprecatedPropertyName);\n                    if (resolvedValue != null) {\n                        log.warn(\"Warning: property '{}' is deprecated. Please use '{}' instead\",\n                                deprecatedPropertyName, propertyName);\n                        break;\n                    }\n                }\n                if (resolvedValue == null) {\n                    resolvedValue = environment.getProperty(propertyName);\n                }\n\n                // Set the field value if necessary\n                field.setAccessible(true);\n                try {\n                    field.set(bean, resolvedValue);\n                } catch (IllegalAccessException e) {\n                    throw new BeanInitializationException(\n                            \"Failed to set BonitaProperty-annotated field value for bean \" + beanName, e);\n                }\n            }\n        }\n        return bean;\n    }\n\n    @Override\n    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {\n        return bean;\n    }\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/properties/BooleanProperty.java",
    "content": "/**\n * Copyright (C) 2021 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.properties;\n\nimport static java.lang.String.format;\nimport static java.lang.String.valueOf;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class BooleanProperty extends BonitaConfigProperty {\n\n    private final boolean propertyValue;\n\n    public BooleanProperty(String displayName, String propertyKey, boolean defaultValue) {\n        super(displayName, propertyKey);\n        propertyValue = Boolean.parseBoolean(getProperty(valueOf(defaultValue)));\n        logInitializationMessagesIfFirstTime();\n    }\n\n    public boolean isEnabled() {\n        return propertyValue;\n    }\n\n    @Override\n    String getInitializationMessage() {\n        return format(\"%s %s, you may %s it using env property %s or System property -D%s [=true/false]\",\n                displayName, isEnabled() ? \"enabled\" : \"disabled\", isEnabled() ? \"disable\" : \"enable\", envPropertyKey(),\n                propertyKey);\n    }\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/properties/CustomValueConfig.java",
    "content": "/**\n * Copyright (C) 2025 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.properties;\n\nimport java.util.Set;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class CustomValueConfig {\n\n    @Bean\n    public AutowiredAnnotationBeanPostProcessor customAutowiredAnnotationBeanPostProcessor() {\n        AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();\n        processor.setAutowiredAnnotationTypes(Set.of(BonitaProperty.class, Value.class, Autowired.class));\n        return processor;\n    }\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/properties/StringProperty.java",
    "content": "/**\n * Copyright (C) 2025 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.properties;\n\nimport static java.lang.String.format;\n\npublic class StringProperty extends BonitaConfigProperty {\n\n    private final String propertyValue;\n\n    public StringProperty(String displayName, String propertyKey, String defaultValue) {\n        super(displayName, propertyKey);\n        propertyValue = getProperty(defaultValue);\n        logInitializationMessagesIfFirstTime();\n    }\n\n    public String getValue() {\n        return propertyValue;\n    }\n\n    @Override\n    String getInitializationMessage() {\n        return format(\"%s %s, you may set it using env property %s or System property -D%s\", displayName, propertyValue,\n                envPropertyKey(), propertyKey);\n    }\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/service/BonitaTaskExecutor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service;\n\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Future;\n\nimport org.bonitasoft.engine.commons.PlatformLifecycleService;\n\n/**\n * simply hold a cached thread pool executor to handle common asynchronous tasks\n */\npublic class BonitaTaskExecutor implements PlatformLifecycleService {\n\n    private ExecutorService bonitaTaskExecutor;\n\n    private void checkStarted() {\n        if (bonitaTaskExecutor == null) {\n            throw new IllegalStateException(this.getClass().getName() + \" is not running\");\n        }\n    }\n\n    public Future<?> execute(RunnableWithException runnable) {\n        return execute(() -> {\n            runnable.run();\n            return null;\n        });\n    }\n\n    public <T> Future<T> execute(Callable<T> callable) {\n        checkStarted();\n        return bonitaTaskExecutor.submit(callable);\n    }\n\n    @Override\n    public void start() {\n        if (bonitaTaskExecutor == null) {\n            bonitaTaskExecutor = Executors.newCachedThreadPool(r -> new Thread(r, \"BonitaTaskExecutor\"));\n        }\n    }\n\n    @Override\n    public void stop() {\n        if (bonitaTaskExecutor != null) {\n            bonitaTaskExecutor.shutdown();\n            bonitaTaskExecutor = null;\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/service/BroadcastService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service;\n\nimport java.util.Map;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.TimeoutException;\n\n/**\n * Service allowing to broadcast a call made on services to the other nodes of a Cluster\n *\n * @author Baptiste Mesta\n */\npublic interface BroadcastService {\n\n    /**\n     * Broadcast the execution of a callable on other nodes and returns immediately\n     * a future holding the execution result on each node (once available).\n     * <br/>\n     * The callable will be executed using a platform level session on other nodes.\n     *\n     * @param callable\n     *        callable that will be executed on all nodes except the current one\n     * @param <T>\n     *        type of the returned value\n     * @return\n     *         a future of a map containing the name of the node and the result of the callable\n     */\n    <T> Future<Map<String, TaskResult<T>>> executeOnOthers(Callable<T> callable);\n\n    <T> Map<String, TaskResult<T>> executeOnOthersAndWait(Callable<T> callable)\n            throws TimeoutException, InterruptedException, ExecutionException;\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/service/InjectedService.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\n/**\n * to tell the engine that a setter is used to inject services\n *\n * @author Baptiste Mesta\n */\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface InjectedService {\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/service/RunnableWithException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service;\n\npublic interface RunnableWithException {\n\n    void run() throws Exception;\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/service/ServicesLookup.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface ServicesLookup {\n\n    /**\n     * lookup for a service on the platform\n     *\n     * @param serviceName\n     *        name of the service, it is the id of the bean in the spring context\n     * @param <T>\n     *        type of the service to return\n     * @return\n     *         the service\n     */\n    <T> T lookupService(String serviceName);\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/service/ServicesResolver.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\n\nimport org.apache.commons.text.WordUtils;\n\n/**\n * @author Baptiste Mesta\n */\npublic class ServicesResolver {\n\n    private final ServicesLookup servicesLookup;\n\n    public ServicesResolver(ServicesLookup servicesLookup) {\n        this.servicesLookup = servicesLookup;\n    }\n\n    public void injectServices(Object target) throws InvocationTargetException, IllegalAccessException {\n\n        final Method[] methods = target.getClass().getMethods();\n        for (final Method method : methods) {\n            if (method.getAnnotation(InjectedService.class) != null) {\n                String serviceName = WordUtils.uncapitalize(method.getName().substring(3));\n                final Object lookup = servicesLookup.lookupService(serviceName);\n                method.invoke(target, lookup);\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/main/java/org/bonitasoft/engine/service/TaskResult.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service;\n\nimport java.util.Objects;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * result of the execution of a task using the broadcast service\n *\n * @author Baptiste Mesta\n */\npublic class TaskResult<T> {\n\n    private Throwable throwable;\n\n    private T result;\n\n    private Long timeout;\n\n    private TimeUnit timeunit;\n\n    public TaskResult(final Throwable e) {\n        this.throwable = e;\n    }\n\n    public TaskResult(final T result) {\n        this.result = result;\n    }\n\n    public TaskResult(final Long timeout, final TimeUnit timeunit) {\n        this.timeout = timeout;\n        this.timeunit = timeunit;\n    }\n\n    public static <T> TaskResult<T> error(final Throwable e) {\n        return new TaskResult<T>(e);\n    }\n\n    public static <T> TaskResult<T> ok(final T result) {\n        return new TaskResult<T>(result);\n    }\n\n    public static <T> TaskResult<T> timeout(final long timeout, final TimeUnit timeunit) {\n        return new TaskResult<T>(timeout, timeunit);\n    }\n\n    public boolean isError() {\n        return throwable != null;\n    }\n\n    public boolean isOk() {\n        return !isError() && !isTimeout();\n    }\n\n    public boolean isTimeout() {\n        return timeout != null;\n    }\n\n    /**\n     * @return the result\n     */\n    public T getResult() {\n        return result;\n    }\n\n    /**\n     * @return the throwable\n     */\n    public Throwable getThrowable() {\n        return throwable;\n    }\n\n    /**\n     * @return the timeout\n     */\n    public Long getTimeout() {\n        return timeout;\n    }\n\n    /**\n     * @return the time unit\n     */\n    public TimeUnit getTimeunit() {\n        return timeunit;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (o == null || getClass() != o.getClass())\n            return false;\n        TaskResult<?> that = (TaskResult<?>) o;\n        return Objects.equals(throwable, that.throwable) &&\n                Objects.equals(result, that.result) &&\n                Objects.equals(timeout, that.timeout) &&\n                timeunit == that.timeunit;\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(throwable, result, timeout, timeunit);\n    }\n\n    @Override\n    public String toString() {\n        return \"TaskResult{\" +\n                \"throwable=\" + throwable +\n                \", result=\" + result +\n                \", timeout=\" + timeout +\n                \", timeunit=\" + timeunit +\n                '}';\n    }\n}\n"
  },
  {
    "path": "services/bonita-commons/src/test/java/org/bonitasoft/engine/DeepRegexFileFilterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.io.File;\n\nimport org.junit.Test;\n\npublic class DeepRegexFileFilterTest {\n\n    @Test\n    public void acceptShouldWorkForMatchingPattern() {\n        final String pattern = \"folder/sub/.*\\\\.txt\";\n        final String parentPatternPathname = \"/media/drive/some_folder\";\n        assertThat(new DeepRegexFileFilter(new File(parentPatternPathname), pattern)\n                .accept(new File(\"/media/drive/some_folder/folder/sub/matchingFile.txt\")))\n                .isTrue();\n    }\n\n    @Test\n    public void acceptShouldWorkForMatchingPatternOnFolderWithTrailingSlash() {\n        final String pattern = \"folder/sub/.*\\\\.txt\";\n        final String parentPatternPathname = \"/home/some_folder/\";\n        assertThat(new DeepRegexFileFilter(new File(parentPatternPathname), pattern)\n                .accept(new File(\"/home/some_folder/folder/sub/matchingFile.txt\")))\n                .isTrue();\n    }\n\n    @Test\n    public void acceptShouldRejectNonMatchingFile() {\n        final String pattern = \"folder/sub/.*\\\\.txt\";\n        final String parentPatternPathname = \"/home/some_folder/\";\n        assertThat(new DeepRegexFileFilter(new File(parentPatternPathname), pattern)\n                .accept(new File(\"/home/some_folder/folder/sub/someReport.pdf\")))\n                .isFalse();\n    }\n\n    @Test\n    public void acceptShouldMatchDeepSubFolders() {\n        final String pattern = \"folder/.*\\\\.txt\";\n        final String parentPatternPathname = \"/home\";\n        assertThat(new DeepRegexFileFilter(new File(parentPatternPathname), pattern)\n                .accept(new File(\"/home/folder/sub/sub2/fileHiddenInDeepFolder.txt\")))\n                .isTrue();\n    }\n\n    @Test\n    public void acceptShouldNotMatchSlashBeginingPatterns() {\n        final String pattern = \"/folder/.*\\\\.txt\";\n        final String parentPatternPathname = \"/home\";\n        assertThat(new DeepRegexFileFilter(new File(parentPatternPathname), pattern)\n                .accept(new File(\"/home/folder/sub/sub2/fileHiddenInDeepFolder.txt\")))\n                .isFalse();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/test/java/org/bonitasoft/engine/commons/ClassReflectorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Type;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Date;\n\nimport org.bonitasoft.engine.commons.exceptions.SReflectException;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nclass ClassReflectorTest {\n\n    private static final String NOT_A_METHOD = \"not a method\";\n\n    private static final String NOT_A_CLASS = \"not a class\";\n\n    private Pojo pojo;\n\n    @BeforeEach\n    void before() {\n        pojo = new Pojo();\n    }\n\n    @Test\n    void testGetGetterName() {\n        // can't work with boolean since field name type is unknown\n        assertThat(ClassReflector.getGetterName(\"longs\")).isEqualTo(\"getLongs\");\n        assertThat(ClassReflector.getGetterName(\"bigChoices\")).isEqualTo(\"getBigChoices\");\n\n    }\n\n    @Test\n    void testGetGetterMethod() {\n        assertThat(ClassReflector.getGetterName(\"bigChoice\", Boolean.class)).isEqualTo(\"isBigChoice\");\n        assertThat(ClassReflector.getGetterName(\"longs\", Long.class)).isEqualTo(\"getLongs\");\n        assertThat(ClassReflector.getGetterName(\"bigChoices\", ArrayList.class)).isEqualTo(\"getBigChoices\");\n\n    }\n\n    @Test\n    void testGetterReturnType() throws Exception {\n        assertThat(ClassReflector.getGetterReturnType(pojo.getClass(), \"getDate\")).isEqualTo(Date.class);\n    }\n\n    @Test\n    void testGetAccessibleGetters() {\n        final Collection<Method> accessibleGetters = ClassReflector.getAccessibleGetters(pojo.getClass());\n\n        // isChoice, getDate, getClass, getLongs, getBigChoice, getBigChoices\n        assertThat(accessibleGetters).hasSize(7);\n    }\n\n    @Test\n    void testGetClass() throws Exception {\n        final Class<? extends Pojo> class1 = ClassReflector.getClass(pojo.getClass(), pojo.getClass().getName());\n        assertThat(class1).isEqualTo(pojo.getClass());\n    }\n\n    @Test\n    void testGetClassException() {\n        assertThrows(SReflectException.class, () -> ClassReflector.getClass(pojo.getClass(), NOT_A_CLASS));\n    }\n\n    @Test\n    void testGetObject() throws Exception {\n        assertThat(ClassReflector.getObject(pojo.getClass(), pojo.getClass().getName())).isNotNull();\n    }\n\n    @Test\n    void testGetObjectException() {\n        assertThrows(SReflectException.class, () -> ClassReflector.getObject(pojo.getClass(), NOT_A_CLASS));\n    }\n\n    @Test\n    void testGetConstructor() throws Exception {\n        ClassReflector.getConstructor(pojo.getClass(), pojo.getClass().getName());\n    }\n\n    @Test\n    void testGetConstructorException() {\n        assertThrows(SReflectException.class,\n                () -> assertThat(ClassReflector.getConstructor(pojo.getClass(), NOT_A_CLASS)).isNotNull());\n    }\n\n    @Test\n    void testGetConstructorNoClassName() throws Exception {\n        final Constructor<? extends Pojo> constructor = ClassReflector.getConstructor(pojo.getClass());\n        assertThat(constructor).isNotNull();\n    }\n\n    @Test\n    void testGetConstructorNoClassNameException() {\n        assertThrows(SReflectException.class, () -> ClassReflector.getConstructor(pojo.getClass(), String.class));\n    }\n\n    @Test\n    void testGetInstance() throws Exception {\n        ClassReflector.getInstance(ClassReflector.getConstructor(pojo.getClass()));\n    }\n\n    @Test\n    void testGetInstanceException() {\n        assertThrows(SReflectException.class,\n                () -> ClassReflector.getInstance(ClassReflector.getConstructor(pojo.getClass()), String.class));\n    }\n\n    @Test\n    void testInvokeGetter() throws Exception {\n        final Date date = new Date();\n        pojo.setDate(date);\n        final Object invokeGetter = ClassReflector.invokeGetter(pojo, \"getDate\");\n        assertThat(invokeGetter).isEqualTo(date);\n    }\n\n    @Test\n    void testInvokeGetterException() {\n        assertThrows(SReflectException.class, () -> ClassReflector.invokeGetter(pojo, NOT_A_METHOD));\n    }\n\n    @Test\n    void testInvokeSetter() throws Exception {\n        final Date date = new Date();\n        ClassReflector.invokeSetter(pojo, \"setDate\", date.getClass(), date);\n        assertThat(pojo.getDate()).isEqualTo(date);\n    }\n\n    @Test\n    void testInvokeSetterException() {\n        assertThrows(SReflectException.class,\n                () -> ClassReflector.invokeSetter(pojo, NOT_A_METHOD, Date.class, new Date()));\n    }\n\n    @Test\n    void testGetMethod() throws Exception {\n        final Method method = ClassReflector.getMethod(pojo.getClass(), \"getDate\");\n        assertThat(method).isEqualTo(ClassReflector.getMethodByName(pojo.getClass(), \"getDate\"));\n    }\n\n    @Test\n    void testInvokeMethodByName() throws Exception {\n        final Date date = new Date();\n        ClassReflector.invokeMethodByName(pojo, \"setDate\", date);\n        assertThat(pojo.getDate()).isEqualTo(date);\n    }\n\n    @Test\n    void testInvokeMethodByName_should_throw_exception_on_method_name() {\n        assertThrows(SReflectException.class, () -> ClassReflector.invokeMethodByName(pojo, NOT_A_METHOD, new Date()));\n    }\n\n    @Test\n    void testInvokeMethodByName_should_throw_exception_on_bad_parameter() {\n        assertThrows(SReflectException.class, () -> ClassReflector.invokeMethodByName(pojo, \"setDate\", \"not a date\"));\n    }\n\n    @Test\n    void testInvokeMethod() throws Exception {\n        final Date date = new Date();\n        ClassReflector.invokeMethod(pojo, \"setDate\", Date.class, date);\n        assertThat(pojo.getDate()).isEqualTo(date);\n    }\n\n    @Test\n    void testInvokeMethodWithParams() throws Exception {\n        final Class<?>[] parameterType = new Class<?>[] { String.class, Integer.class };\n        final Object[] parameterValues = new Object[] { \"string\", 1 };\n\n        final Object result = ClassReflector.invokeMethod(pojo, \"twoParamMethod\", parameterType, parameterValues);\n        assertThat(result.toString()).isEqualTo(\"string*1\");\n    }\n\n    @Test\n    void testGetCompatibleMethod() throws Exception {\n        final Method compatibleMethod = ClassReflector.getCompatibleMethod(pojo.getClass(), \"setChoice\", Boolean.class);\n        assertThat(compatibleMethod).isNotNull();\n\n    }\n\n    @Test\n    void testGetCompatibleMethod_with_existing_method() throws Exception {\n        final Method compatibleMethod = ClassReflector.getCompatibleMethod(pojo.getClass(), \"isChoice\");\n        assertThat(compatibleMethod).isNotNull();\n\n    }\n\n    @Test\n    void testGetCompatibleMethod_with_wrong_parameters_type() {\n        assertThrows(SReflectException.class,\n                () -> ClassReflector.getCompatibleMethod(pojo.getClass(), \"setChoice\", String.class));\n    }\n\n    @Test\n    void testGetCompatibleMethod_with_wrong_parameters_count() {\n        assertThrows(SReflectException.class,\n                () -> ClassReflector.getCompatibleMethod(pojo.getClass(), \"setChoice\", String.class, Date.class));\n    }\n\n    @Test\n    void testGetCompatibleMethod_with_wrong_null_parameters() {\n        assertThrows(SReflectException.class,\n                () -> ClassReflector.getCompatibleMethod(pojo.getClass(), \"setChoice\", (Class<?>[]) null));\n    }\n\n    @Test\n    void testGetCompatibleMethod_with_parameters() throws Exception {\n        final Method compatibleMethod = ClassReflector.getCompatibleMethod(pojo.getClass(), \"twoParamMethod\",\n                String.class, Integer.class);\n        assertThat(compatibleMethod).isNotNull();\n    }\n\n    @Test\n    void testGetGetterReturnType() throws Exception {\n        final Type result = ClassReflector.getGetterReturnType(pojo.getClass(), \"getDate\");\n        assertThat(result).isEqualTo(Date.class);\n    }\n\n    @Test\n    void testGetGetterReturnType_should_throw_exception() {\n        assertThrows(SReflectException.class, () -> ClassReflector.getGetterReturnType(pojo.getClass(), NOT_A_METHOD));\n    }\n\n    @Test\n    void testGetDeclaredSetters() {\n        final Method[] declaredSetters = ClassReflector.getDeclaredSetters(pojo.getClass());\n\n        // setDate,setChoice,setLongs,setBigChoice,setBigChoices\n        assertThat(declaredSetters).hasSize(6);\n\n        for (final Method method : declaredSetters) {\n            assertThat(ClassReflector.isAGetterMethod(method)).isFalse();\n            assertThat(ClassReflector.isASetterMethod(method)).isTrue();\n        }\n    }\n\n    @Test\n    void testGetDeclaredGetters() {\n        final Method[] declaredSetters = ClassReflector.getDeclaredGetters(pojo.getClass());\n\n        // isChoice, getDate, getLongs, getBigChoice, getBigChoices\n        assertThat(declaredSetters).hasSize(6);\n\n        for (final Method method : declaredSetters) {\n            assertThat(ClassReflector.isAGetterMethod(method)).isTrue();\n            assertThat(ClassReflector.isASetterMethod(method)).isFalse();\n        }\n    }\n\n    @Test\n    void testGetFieldName() {\n        assertThat(ClassReflector.getFieldName(\"isChoice\")).isEqualTo(\"choice\");\n        assertThat(ClassReflector.getFieldName(\"getDate\")).isEqualTo(\"date\");\n        assertThat(ClassReflector.getFieldName(\"get\")).isEqualTo(\"\");\n    }\n\n    @Test\n    void testSetField_on_sub_object() throws Exception {\n        Date date = new Date();\n        pojo.setChild(new Pojo());\n        ClassReflector.setField(pojo, \"child.date\", date);\n        assertThat(pojo.getChild().getDate()).isEqualTo(date);\n    }\n\n    @Test\n    void testSetField_on_object() throws Exception {\n        Pojo parameterValue = new Pojo();\n        ClassReflector.setField(pojo, \"child\", parameterValue);\n        assertThat(pojo.getChild()).isEqualTo(parameterValue);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/test/java/org/bonitasoft/engine/commons/CollectionUtilTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons;\n\nimport static java.util.Arrays.asList;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.*;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport java.util.stream.IntStream;\n\nimport org.junit.jupiter.api.Test;\n\nclass CollectionUtilTest {\n\n    @Test\n    void should_emptyOrUnmodifiable_return_unmodifiable_list() {\n        ArrayList<String> list = new ArrayList<>();\n        list.add(\"plop\");\n        List<String> result = CollectionUtil.emptyOrUnmodifiable(list);\n\n        assertEquals(\"plop\", result.get(0));\n        assertEquals(1, result.size());\n        assertThrows(UnsupportedOperationException.class, () -> result.add(\"plop2\"));\n    }\n\n    @Test\n    void should_emptyOrUnmodifiable_return_empty_list() {\n        List<String> result = CollectionUtil.emptyOrUnmodifiable(null);\n        assertTrue(result.isEmpty());\n    }\n\n    @Test\n    void should_split_a_list_of_9_elements_in_lists_of_3() {\n        List<Integer> original = asList(1, 2, 3, 4, 5, 6, 7, 8, 9);\n\n        List<List<Integer>> split = CollectionUtil.split(original, 3);\n\n        assertThat(split).containsExactly(asList(1, 2, 3), asList(4, 5, 6), asList(7, 8, 9));\n    }\n\n    @Test\n    void should_split_a_list() {\n        List<Integer> original = asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);\n\n        List<List<Integer>> split = CollectionUtil.split(original, 3);\n\n        assertThat(split).containsExactly(asList(1, 2, 3), asList(4, 5, 6), asList(7, 8, 9), asList(10));\n    }\n\n    @Test\n    void should_split_a_list_in_lists_of_2() {\n        List<Integer> original = asList(1, 2, 3, 4, 5, 6, 7, 8, 9);\n\n        List<List<Integer>> split = CollectionUtil.split(original, 2);\n\n        assertThat(split).containsExactly(asList(1, 2), asList(3, 4), asList(5, 6), asList(7, 8), asList(9));\n    }\n\n    @Test\n    void should_split_a_list_of_110_elements_in_lists_of_100() {\n        List<Integer> original = IntStream.range(0, 110).boxed().collect(Collectors.toList());\n\n        List<List<Integer>> split = CollectionUtil.split(original, 100);\n\n        assertThat(split.get(0)).isEqualTo(IntStream.range(0, 100).boxed().collect(Collectors.toList()));\n        assertThat(split.get(1)).isEqualTo(IntStream.range(100, 110).boxed().collect(Collectors.toList()));\n    }\n\n    @Test\n    void should_split_an_empty_list() {\n        List<Integer> original = Collections.emptyList();\n\n        List<List<Integer>> split = CollectionUtil.split(original, 3);\n\n        assertThat(split).isEmpty();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/test/java/org/bonitasoft/engine/commons/ContainerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.jupiter.api.Test;\n\nclass ContainerTest {\n\n    @Test\n    void constructor_should_create_container_based_on_given_information() {\n        //when\n        Container container = new Container(10, \"cType\");\n\n        //then\n        assertThat(container.getId()).isEqualTo(10);\n        assertThat(container.getType()).isEqualTo(\"cType\");\n    }\n}\n"
  },
  {
    "path": "services/bonita-commons/src/test/java/org/bonitasoft/engine/commons/ExceptionUtilsTest.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException;\nimport org.junit.Test;\n\npublic class ExceptionUtilsTest {\n\n    @Test\n    public void should_print_root_cause_of_an_exception() {\n        Exception exception = new Exception(\"direct exception\",\n                new Exception(\"intermediate exception\", new SBonitaRuntimeException(\"This is the root cause\")));\n\n        String rootCause = ExceptionUtils.printRootCauseOnly(exception);\n\n        assertThat(rootCause)\n                .isEqualTo(\"org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException: This is the root cause\");\n    }\n\n    @Test\n    public void should_print_root_cause_of_an_exception_when_there_is_no_root_cause() {\n        Exception exception = new Exception(\"direct exception with no root cause\");\n\n        String rootCause = ExceptionUtils.printRootCauseOnly(exception);\n\n        assertThat(rootCause).isEqualTo(\"java.lang.Exception: direct exception with no root cause\");\n    }\n\n    @Test\n    public void should_print_lightweight_stacktrace() {\n        Exception exception = doSomeBusiness();\n\n        String lightWeightStacktrace = ExceptionUtils.printLightWeightStacktrace(exception, 3);\n\n        assertThat(lightWeightStacktrace).startsWith(\n                \"org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException: This is the root cause\\n\" +\n                        \"\\twrapped by java.lang.Exception: intermediate exception\\n\" +\n                        \"\\twrapped by java.lang.Exception: Wrap all other exceptions\\n\" +\n                        \" exception was generated here:\\tat org.bonitasoft.engine.commons.ExceptionUtilsTest.methodThatCauseRootException(ExceptionUtilsTest.java:\");\n\n    }\n\n    protected Exception doSomeBusiness() {\n        try {\n            doSomeBusiness2();\n        } catch (Exception e) {\n            return new Exception(\"Wrap all other exceptions\", e);\n        }\n        return null;\n    }\n\n    protected void doSomeBusiness2() throws Exception {\n        try {\n            methodThatCauseRootException();\n        } catch (SBonitaRuntimeException e) {\n            throw new Exception(\"intermediate exception\", e);\n        }\n    }\n\n    protected void methodThatCauseRootException() {\n        throw new SBonitaRuntimeException(\"This is the root cause\");\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/test/java/org/bonitasoft/engine/commons/JavaMethodInvokerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nclass JavaMethodInvokerTest {\n\n    @Test\n    void invokeJavaMethodShouldNotModifyObjectReference() throws Exception {\n        // given:\n        final JavaMethodInvoker invoker = new JavaMethodInvoker();\n        final User user = new User(\"Jo la frite\");\n        final String initialUserReference = user.toString();\n\n        // when:\n        invoker.invokeJavaMethod(String.class.getName(), \"Manu\", user, \"setName\", String.class.getName());\n\n        // then:\n        final String newUserReference = user.toString();\n        assertThat(newUserReference).isEqualTo(initialUserReference);\n    }\n\n    class User {\n\n        private String name;\n\n        public User(final String name) {\n            this.name = name;\n        }\n\n        public void setName(final String name) {\n            this.name = name;\n        }\n    }\n\n    public class MyClass {\n\n        private int thing = 0;\n\n        public void setThing(final int thing) {\n            this.thing = thing;\n        }\n\n        int getThing() {\n            return thing;\n        }\n\n    }\n\n    @Test\n    void invokeJavaMethod_should_update_a_list() throws Exception {\n        final JavaMethodInvoker invoker = new JavaMethodInvoker();\n        final List<User> users = new ArrayList<User>();\n        final List<User> createdUsers = new ArrayList<User>();\n        createdUsers.add(new User(\"Matti\"));\n\n        invoker.invokeJavaMethod(createdUsers.getClass().getName(), createdUsers, users, \"addAll\",\n                List.class.getName());\n\n        assertThat(users).isEqualTo(createdUsers);\n    }\n\n    @Test\n    void invokeJavaMethod_should_use_autoboxing() throws Exception {\n        final MyClass myData = new MyClass();\n\n        final JavaMethodInvoker invoker = new JavaMethodInvoker();\n\n        final MyClass object = (MyClass) invoker.invokeJavaMethod(Integer.class.getName(), 83, myData, \"setThing\",\n                \"int\");\n\n        assertThat(object.getThing()).isEqualTo(83);\n    }\n\n    @Test\n    void invokeJavaMethod_should_throw_nosuchmethodexception() throws Exception {\n\n        assertThatThrownBy(\n                () -> {\n                    final MyClass myData = new MyClass();\n\n                    final JavaMethodInvoker invoker = new JavaMethodInvoker();\n\n                    final MyClass object = (MyClass) invoker.invokeJavaMethod(String.class.getName(), \"A STRING\",\n                            myData, \"setDoesNotExistMethod\",\n                            \"java.lang.String\");\n                })\n                .isInstanceOf(NoSuchMethodException.class);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/test/java/org/bonitasoft/engine/commons/Pojo.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons;\n\nimport java.util.Date;\nimport java.util.List;\n\npublic class Pojo {\n\n    private boolean choice;\n\n    private Boolean bigChoice;\n\n    private List<Boolean> bigChoices;\n\n    private Date date;\n\n    private List<Long> longs;\n\n    private Pojo child;\n\n    public Pojo() {\n        //\n    }\n\n    public boolean isChoice() {\n        return choice;\n    }\n\n    public void setChoice(final boolean choice) {\n        this.choice = choice;\n    }\n\n    public Date getDate() {\n        return date;\n    }\n\n    public void setDate(final Date date) {\n        this.date = date;\n    }\n\n    public List<Long> getLongs() {\n        return longs;\n    }\n\n    public void setLongs(final List<Long> longs) {\n        this.longs = longs;\n    }\n\n    public Boolean getBigChoice() {\n        return bigChoice;\n    }\n\n    public void setBigChoice(final Boolean bigChoice) {\n        this.bigChoice = bigChoice;\n    }\n\n    public List<Boolean> getBigChoices() {\n        return bigChoices;\n    }\n\n    public void setBigChoices(final List<Boolean> bigChoices) {\n        this.bigChoices = bigChoices;\n    }\n\n    public String notAGetter() {\n        return null;\n    }\n\n    public String twoParamMethod(final String a, final Integer i) {\n        return String.format(\"%s*%d\", a, i);\n    }\n\n    public Pojo getChild() {\n        return child;\n    }\n\n    public void setChild(Pojo child) {\n        this.child = child;\n    }\n}\n"
  },
  {
    "path": "services/bonita-commons/src/test/java/org/bonitasoft/engine/commons/StringUtilsTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.jupiter.api.Test;\n\nclass StringUtilsTest {\n\n    @Test\n    void uniformizePathPatternShouldChangeAllBackslashesToForwardSlashes() {\n        final String uniformized = StringUtils.uniformizePathPattern(\"C:\\\\toto\\\\my path\\\\my\\\\/file.bak.txt\");\n        assertThat(uniformized).isEqualTo(\"C:/toto/my path/my/file.bak.txt\");\n    }\n\n    @Test\n    void uniformizePathPatternShouldLeaveNoDoubleSeparator() {\n        final String uniformized = StringUtils\n                .uniformizePathPattern(\"C:///toto//my path/////full_slashes/my file.bak.txt\");\n        assertThat(uniformized).isEqualTo(\"C:/toto/my path/full_slashes/my file.bak.txt\");\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/test/java/org/bonitasoft/engine/commons/TypeConvertUtilTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\n\nimport java.math.BigDecimal;\nimport java.util.Date;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nclass TypeConvertUtilTest {\n\n    private TypeConverterUtil typeConverterUtil;\n\n    @BeforeEach\n    void before() {\n        String[] datePatterns = new String[] { \"yyyy-MM-dd HH:mm:ss\", \"yyyy-MM-dd'T'HH:mm:ss\", \"yyyy-MM-dd\", \"HH:mm:ss\",\n                \"yyyy-MM-dd'T'HH:mm:ss.SSS\" };\n        typeConverterUtil = new TypeConverterUtil(datePatterns);\n    }\n\n    @Test\n    void should_convert_primitives() {\n\n        assertThat((Long) typeConverterUtil.convertToType(Long.class, \"15\")).isEqualTo(15L);\n\n        assertThat((Integer) typeConverterUtil.convertToType(Integer.class, \"16\")).isEqualTo(16);\n\n        assertThat((Float) typeConverterUtil.convertToType(Float.class, \"-17.596\")).isEqualTo(-17.596F);\n\n        final BigDecimal bigDecimal = new BigDecimal(12.3650000000000002131628207280300557613372802734375);\n        assertThat((BigDecimal) typeConverterUtil.convertToType(BigDecimal.class,\n                \"12.3650000000000002131628207280300557613372802734375\")).isEqualTo(bigDecimal);\n\n        assertThat((Boolean) typeConverterUtil.convertToType(Boolean.class, \"true\")).isTrue();\n        assertThat((Boolean) typeConverterUtil.convertToType(Boolean.class, \"false\")).isFalse();\n\n        checkDateConvert(\"2015-08-06T22:00:00.000\", 1438898400000L);\n        checkDateConvert(\"2015-01-31\", 1422662400000L);\n        checkDateConvert(\"2015-01-31 23:15:59\", 1422746159000L);\n        checkDateConvert(\"2015-01-31T23:15:59\", 1422746159000L);\n        checkDateConvert(\"2015-01-31T23:15:59.001\", 1422746159001L);\n        checkDateConvert(\"12:00:00\", 43200000L);\n\n    }\n\n    @Test\n    void dateConvert_should_fail() {\n        assertThrows(IllegalArgumentException.class, () -> typeConverterUtil.convertToType(Date.class, \"not a date\"),\n                \"unable to parse 'not a date' to type java.util.Date\");\n    }\n\n    private void checkDateConvert(String dateToConvert, long expectedDateAsLong) {\n        final Date expectedDate = new Date(expectedDateAsLong);\n        final Date returnedDate = (Date) typeConverterUtil.convertToType(Date.class, dateToConvert);\n        assertThat(returnedDate).as(\"error while converting date:\" + dateToConvert + \" (assuming provided date is GMT)\")\n                .hasTime(expectedDateAsLong).isEqualTo(expectedDate);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/test/java/org/bonitasoft/engine/commons/TypeConverterUtilTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\n\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.OffsetDateTime;\nimport java.time.ZoneOffset;\nimport java.util.Date;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * @author Emmanuel Duchastenier\n */\nclass TypeConverterUtilTest {\n\n    private final TypeConverterUtil typeConverterUtil = new TypeConverterUtil(null);\n\n    @Test\n    void convertToType_should_convert_to_LocalDate() {\n        final Object date = typeConverterUtil.convertToType(LocalDate.class, \"2014-07-14\");\n\n        // then:\n        assertThat(date).isEqualTo(LocalDate.of(2014, 7, 14));\n    }\n\n    @Test\n    void getDefaultType_should_truncate_extra_LocalDate_string_characters() {\n        final Object date = typeConverterUtil.convertToType(LocalDate.class, \"2015-07-14TOTO12:00\");\n\n        // then:\n        assertThat(date).isEqualTo(LocalDate.of(2015, 7, 14));\n    }\n\n    @Test\n    void getDefaultType_should_gracefully_handle_null_values() {\n        assertThrows(IllegalArgumentException.class, () -> typeConverterUtil.convertToType(Date.class, null),\n                \"unable to parse\");\n    }\n\n    @Test\n    void convertToType_should_convert_to_LocalDateTime() {\n        final Object date = typeConverterUtil.convertToType(LocalDateTime.class, \"2014-07-14T17:42:01\");\n\n        // then:\n        assertThat(date).isEqualTo(LocalDateTime.of(2014, 7, 14, 17, 42, 1));\n    }\n\n    @Test\n    void getDefaultType_should_truncate_extra_LocalDateTime_string_characters() {\n        final Object date = typeConverterUtil.convertToType(LocalDateTime.class, \"2015-07-14T12:00:07+03:00\");\n\n        // then:\n        assertThat(date).isEqualTo(LocalDateTime.of(2015, 7, 14, 12, 0, 7));\n    }\n\n    @Test\n    void convertToType_should_convert_to_OffsetDateTime() {\n        final Object date = typeConverterUtil.convertToType(OffsetDateTime.class, \"2014-07-14T17:42:01Z\");\n\n        // then:\n        assertThat(date).isEqualTo(OffsetDateTime.of(2014, 7, 14, 17, 42, 1, 0, ZoneOffset.UTC));\n    }\n\n    @Test\n    void getDefaultType_should_throw_exception_for_wrong_date_format() {\n        assertThrows(IllegalArgumentException.class,\n                () -> typeConverterUtil.convertToType(OffsetDateTime.class, \"2015-07-14T12:00:07+03:00:17:15:123.000\"),\n                \"unable to parse\");\n    }\n\n    @Test\n    void getDefaultType_should_throw_exception_for_wrong_LocalDate_type() {\n        assertThrows(IllegalArgumentException.class,\n                () -> typeConverterUtil.convertToType(LocalDate.class, 225L),\n                \"unable to parse\");\n    }\n\n    @Test\n    void getDefaultType_should_throw_exception_for_wrong_LocalDateTime_type() {\n        assertThrows(IllegalArgumentException.class,\n                () -> typeConverterUtil.convertToType(LocalDateTime.class, new Date()),\n                \"unable to parse\");\n    }\n\n    @Test\n    void getDefaultType_should_throw_exception_for_wrong_OffsetDateTime_type() {\n        assertThrows(IllegalArgumentException.class,\n                () -> typeConverterUtil.convertToType(OffsetDateTime.class, (byte) 0),\n                \"unable to parse\");\n    }\n}\n"
  },
  {
    "path": "services/bonita-commons/src/test/java/org/bonitasoft/engine/commons/io/IOUtilTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.commons.io;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Properties;\nimport java.util.jar.JarEntry;\nimport java.util.jar.JarInputStream;\n\nimport javax.xml.parsers.DocumentBuilder;\nimport javax.xml.parsers.DocumentBuilderFactory;\n\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.TemporaryFolder;\nimport org.w3c.dom.Document;\n\npublic class IOUtilTest {\n\n    @Rule\n    public TemporaryFolder tempFolderRule = new TemporaryFolder();\n\n    @Test\n    public void getClassNameList() throws Exception {\n        // given:\n        final byte[] jarContent = getFromResources(\"bdr-jar.bak\");\n\n        // when:\n        final List<String> classNameList = IOUtil.getClassNameList(jarContent);\n\n        // then:\n        assertThat(classNameList).containsOnly(\"org.bonita.pojo.Employee\");\n    }\n\n    private byte[] getFromResources(String name) throws IOException {\n        InputStream resourceAsStream = getClass().getResourceAsStream(name);\n        if (resourceAsStream == null) {\n            throw new IllegalStateException(String.format(\"no resource %s found\", name));\n        }\n        return IOUtil.getAllContentFrom(resourceAsStream);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void shouldToByteArray_ThrowIllegalArgumentException_ForNullDocument() throws Exception {\n        final Document document = null;\n        IOUtil.toByteArray(document);\n    }\n\n    @Test\n    public void shouldToByteArray_ForDocumentReturnAByteArray() throws Exception {\n        final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();\n        documentBuilderFactory.setValidating(false);\n        final DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();\n\n        Document document;\n        try (InputStream is = IOUtilTest.class.getResourceAsStream(\"persistence.xml\")) {\n            document = documentBuilder.parse(is);\n        }\n        final byte[] byteArray = IOUtil.toByteArray(document);\n        assertThat(byteArray).isNotNull();\n    }\n\n    @Test\n    public void shouldAddJarEntry_AddAnEntryInExistingJar() throws Exception {\n        final byte[] jarContent = getFromResources(\"bdr-jar.bak\");\n        final byte[] entryContent = getFromResources(\"persistence.xml\");\n        final String entryName = \"META-INF/myNewEntry.xml\";\n        final byte[] updatedJar = IOUtil.addJarEntry(jarContent, entryName, entryContent);\n        assertThat(updatedJar).isNotNull();\n\n        final ByteArrayInputStream bais = new ByteArrayInputStream(updatedJar);\n        final JarInputStream jis = new JarInputStream(bais);\n        JarEntry entry;\n        final Map<String, byte[]> entryNames = new HashMap<>();\n        final byte[] buffer = new byte[4096];\n        while ((entry = jis.getNextJarEntry()) != null) {\n            if (!entry.isDirectory()) {\n                final ByteArrayOutputStream baos = new ByteArrayOutputStream();\n                int len;\n                while ((len = jis.read(buffer)) > 0) {\n                    baos.write(buffer, 0, len);\n                }\n                baos.close();\n                entryNames.put(entry.getName(), baos.toByteArray());\n            }\n        }\n        jis.close();\n        assertThat(entryNames.keySet()).contains(entryName);\n        assertThat(entryNames.get(entryName)).isEqualTo(entryContent);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void shouldAddJarEntry_ThrowIllegalArgumentExceptionIfEntryAlreadyExists() throws Exception {\n        final byte[] jarContent = getFromResources(\"bdr-jar.bak\");\n        final byte[] entryContent = getFromResources(\"persistence.xml\");\n        final String entryName = \"META-INF/persistence.xml\";\n        IOUtil.addJarEntry(jarContent, entryName, entryContent);\n    }\n\n    @Test\n    public void testUpdatePropertyValue() throws IOException {\n        final Properties properties = new Properties();\n        properties.put(\"key1\", \"value1\");\n        properties.put(\"key2\", \"value2\");\n        properties.put(\"key3\", \"value3\");\n\n        final File file = tempFolderRule.newFile(\"testPropertiesFile\");\n        PropertiesManager.saveProperties(properties, file);\n        final String updatedValue2 = \"@\\\\[||sfgf23465\";\n        final Map<String, String> pairs = new HashMap<>();\n        pairs.put(\"key2\", updatedValue2);\n        IOUtil.updatePropertyValue(file, pairs);\n\n        final Properties updatedProperties = PropertiesManager.getProperties(file);\n        Assert.assertEquals(updatedValue2, updatedProperties.get(\"key2\"));\n\n    }\n\n    @Test\n    public void getFileContent_should_return_content_of_file() throws Exception {\n        Optional<byte[]> fileContent = IOUtil.getFileContent(\"myFile.txt\");\n\n        assertThat(fileContent).get().isEqualTo(\"some content\".getBytes());\n    }\n\n    @Test\n    public void getFileContent_should_return_empty_when_file_does_not_exists() throws Exception {\n        Optional<byte[]> fileContent = IOUtil.getFileContent(\"myFile2.txt\");\n\n        assertThat(fileContent).isNotPresent();\n    }\n\n    @Test\n    public void getContentTypeForIcon_should_throw_exception_when_not_an_image() {\n        assertThat(IOUtil.getContentTypeForIcon(\"a.png\")).isEqualTo(\"image/png\");\n        assertThat(IOUtil.getContentTypeForIcon(\"a.jpg\")).isEqualTo(\"image/jpeg\");\n    }\n\n    @Test\n    public void getContentTypeForIcon_should_return_the_image_content() {\n        assertThatThrownBy(() -> IOUtil.getContentTypeForIcon(\"\")).isInstanceOf(IllegalArgumentException.class);\n        assertThatThrownBy(() -> IOUtil.getContentTypeForIcon(\"a.b\")).isInstanceOf(IllegalArgumentException.class);\n        assertThatThrownBy(() -> IOUtil.getContentTypeForIcon(\"a.exe\")).isInstanceOf(IllegalArgumentException.class);\n        assertThatThrownBy(() -> IOUtil.getContentTypeForIcon(\"a\")).isInstanceOf(IllegalArgumentException.class);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/test/java/org/bonitasoft/engine/home/FolderMgrTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.home;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.io.File;\n\nimport org.assertj.core.api.Assertions;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.RestoreSystemProperties;\nimport org.junit.contrib.java.lang.system.SystemOutRule;\nimport org.junit.rules.TemporaryFolder;\n\n/**\n * @author Baptiste Mesta\n * @author Emmanuel Duchastenier\n */\npublic class FolderMgrTest {\n\n    @Rule\n    public RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties();\n    @Rule\n    public SystemOutRule systemOutRule = new SystemOutRule().enableLog();\n    @Rule\n    public TemporaryFolder temporaryFolder = new TemporaryFolder();\n\n    @Test\n    public void getPlatformGlobalClassLoaderFolder_should_create_all_parents() throws Exception {\n        final Folder platformGlobalClassLoaderFolder = FolderMgr.getPlatformGlobalClassLoaderFolder();\n        assertThat(platformGlobalClassLoaderFolder.getFile()).exists().isDirectory();\n    }\n\n    @Test\n    public void getTempFolder_should_WARN_when_old_folder_still_exists() throws Exception {\n        //given\n        File tempFolder = temporaryFolder.newFolder();\n        System.setProperty(\"java.io.tmpdir\", tempFolder.getAbsolutePath());\n        File bonita_engine_old1 = new File(tempFolder, \"bonita_engine_old1\");\n        File bonita_engine_old2 = new File(tempFolder, \"bonita_engine_old2\");\n        bonita_engine_old1.mkdir();\n        bonita_engine_old2.mkdir();\n        //when\n        Folder tempFolder1 = FolderMgr.getTempFolder();\n        //then\n        Assertions.assertThat(systemOutRule.getLog()).contains(\"Delete these folders to free up space:\");\n        Assertions.assertThat(systemOutRule.getLog()).contains(bonita_engine_old1.getAbsolutePath());\n        Assertions.assertThat(systemOutRule.getLog()).contains(bonita_engine_old2.getAbsolutePath());\n        Assertions.assertThat(systemOutRule.getLog()).doesNotContain(tempFolder1.getFile().getName());\n\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/test/java/org/bonitasoft/engine/home/FolderTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.home;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport java.io.File;\nimport java.io.FileFilter;\nimport java.io.IOException;\nimport java.util.Map;\n\nimport org.apache.commons.io.filefilter.FileFileFilter;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * author Emmanuel Duchastenier\n */\npublic class FolderTest {\n\n    //    @Spy\n    private File f;\n\n    //    @InjectMocks\n    private Folder folder;\n\n    @Before\n    public void setUp() throws IOException {\n        //        MockitoAnnotations.initMocks(this);\n        f = spy(new File(\"/tmp/non-existent\"));\n        folder = spy(new Folder(f));\n    }\n\n    @Test\n    public void listFilesAsResources_should_ignore_subFolders() throws Exception {\n        final File file = mock(File.class);\n        doReturn(\"file\").when(file).getName();\n        doReturn(true).when(file).isFile();\n        final File directory = mock(File.class);\n        doReturn(false).when(directory).isFile();\n        File[] filesAndFolders = new File[] { file, directory };\n        doReturn(filesAndFolders).when(f).listFiles(any(FileFilter.class));\n        doReturn(filesAndFolders).when(f).listFiles();\n        doReturn(new File[] { file }).when(f).listFiles((FileFilter) FileFileFilter.FILE);\n        doReturn(true).when(f).exists();\n        doReturn(true).when(f).isDirectory();\n        doReturn(new byte[1]).when(folder).getFileContent(file);\n        doThrow(new RuntimeException(\"Directories should be filtered by Folder.listFilesAsResources() method\"))\n                .when(folder).getFileContent(directory);\n\n        // should not fail:\n        final Map<String, byte[]> map = folder.listFilesAsResources();\n\n        verify(folder).getFileContent(file);\n        verify(folder, times(0)).getFileContent(directory);\n\n        assertThat(map).hasSize(1);\n        assertThat(map.get(\"file\")).isNotNull();\n    }\n}\n"
  },
  {
    "path": "services/bonita-commons/src/test/java/org/bonitasoft/engine/mdc/AbstractMDCTest.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.mdc;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.io.ByteArrayOutputStream;\nimport java.util.Map;\n\nimport ch.qos.logback.classic.Logger;\nimport ch.qos.logback.classic.LoggerContext;\nimport ch.qos.logback.classic.encoder.PatternLayoutEncoder;\nimport ch.qos.logback.classic.spi.ILoggingEvent;\nimport ch.qos.logback.core.OutputStreamAppender;\nimport org.assertj.core.api.Assertions;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\nimport org.slf4j.LoggerFactory;\nimport org.slf4j.MDC;\n\n/**\n * @author Vincent Hemery\n */\npublic class AbstractMDCTest {\n\n    private static Logger log;\n    private static ByteArrayOutputStream logStream;\n\n    @BeforeClass\n    public static void configureLogger() {\n        LoggerContext logCtx = (LoggerContext) LoggerFactory.getILoggerFactory();\n        PatternLayoutEncoder logEncoder = new PatternLayoutEncoder();\n        logEncoder.setContext(logCtx);\n        logEncoder.setPattern(\"%-12date{YYYY-MM-dd HH:mm:ss.SSS} %-5level – %msg {%X}%n\");\n        logEncoder.start();\n\n        OutputStreamAppender<ILoggingEvent> logStreamAppender = new OutputStreamAppender<>();\n        logStreamAppender.setContext(logCtx);\n        logStreamAppender.setName(\"console\");\n        logStreamAppender.setEncoder(logEncoder);\n        logStreamAppender.setImmediateFlush(true);\n        logStream = new ByteArrayOutputStream();\n        logStreamAppender.setOutputStream(logStream);\n        logStreamAppender.start();\n\n        log = logCtx.getLogger(AbstractMDCTest.class);\n        log.addAppender(logStreamAppender);\n    }\n\n    @Test\n    public void nestedMdc_should_temporarilyOverrideContext() {\n        try (var firstMdc = new AbstractMDC(Map.of(\"key\", \"value1\")) {\n        }) {\n            try (var secondMdc = new AbstractMDC(Map.of(\"key\", \"value2\")) {\n            }) {\n                assertThat(MDC.get(\"key\")).isEqualTo(\"value2\");\n            }\n            assertThat(MDC.get(\"key\")).isEqualTo(\"value1\");\n        }\n    }\n\n    @Test\n    public void mdcClose_should_clearContext() {\n        // given\n        Map<String, String> ctxMap = Map.of(\"key\", \"value1\", \"other\", \"value2\");\n        for (int i = 0; i < 2; i++) {\n            // when\n            try (var mdc = new AbstractMDC(ctxMap) {\n            }) {\n                // then\n                assertThat(MDC.get(\"key\")).isEqualTo(\"value1\");\n                assertThat(MDC.get(\"other\")).isEqualTo(\"value2\");\n                // when\n            }\n            // then\n            assertThat(MDC.get(\"key\")).isNull();\n            assertThat(MDC.get(\"other\")).isNull();\n            // when\n            logStream.reset();\n            log.info(\"This is an empty context test\");\n            // then\n            var str = logStream.toString();\n            Assertions.assertThat(str).contains(\"This is an empty context test {}\");\n        }\n    }\n\n    @Test\n    public void mdc_should_log() {\n        // given\n        Map<String, String> ctxMap = Map.of(MDCConstants.USER_ID, \"2\", MDCConstants.SUBSTITUTE_USER_ID, \"1\");\n        try (var mdc = new AbstractMDC(ctxMap) {\n        }) {\n            // when\n            logStream.reset();\n            log.info(\"This is a test\");\n            // then\n            var str = logStream.toString();\n            Assertions.assertThat(str).contains(\"This is a test {\");\n            ctxMap.forEach((k, v) -> Assertions.assertThat(str).contains(k + \"=\" + v));\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/test/java/org/bonitasoft/engine/monitoring/DefaultExecutorServiceMetricsProviderTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.monitoring;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.concurrent.ArrayBlockingQueue;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\n\nimport io.micrometer.core.instrument.MeterRegistry;\nimport io.micrometer.core.instrument.simple.SimpleMeterRegistry;\nimport org.junit.Test;\n\npublic class DefaultExecutorServiceMetricsProviderTest {\n\n    private ThreadPoolExecutor executorService = new ThreadPoolExecutor(1, 1, 100, TimeUnit.MILLISECONDS,\n            new ArrayBlockingQueue<>(10));\n    private MeterRegistry meterRegistry = new SimpleMeterRegistry();\n    private DefaultExecutorServiceMetricsProvider defaultExecutorServiceMetricsProvider = new DefaultExecutorServiceMetricsProvider();\n\n    @Test\n    public void should_register_metrics_when_binding_the_threadpool() {\n        defaultExecutorServiceMetricsProvider.bind(meterRegistry, executorService, \"my-executor\");\n\n        assertThat(meterRegistry.getMeters()).hasSize(9);\n    }\n\n    @Test\n    public void should_have_no_more_metrics_when_we_unbind_the_executor() {\n        defaultExecutorServiceMetricsProvider.bind(meterRegistry, executorService, \"my-executor\");\n\n        defaultExecutorServiceMetricsProvider.unbind(meterRegistry, \"my-executor\");\n\n        assertThat(meterRegistry.getMeters()).hasSize(0);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-commons/src/test/java/org/bonitasoft/engine/properties/BooleanPropertyTest.java",
    "content": "/**\n * Copyright (C) 2021 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.properties;\n\nimport static com.github.stefanbirkner.systemlambda.SystemLambda.restoreSystemProperties;\nimport static com.github.stefanbirkner.systemlambda.SystemLambda.tapSystemOut;\nimport static com.github.stefanbirkner.systemlambda.SystemLambda.withEnvironmentVariable;\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\n/**\n * @author Emmanuel Duchastenier\n */\nclass BooleanPropertyTest {\n\n    @BeforeEach\n    void setUp() {\n        // Reset the static cache of already logged properties before each test\n        BooleanProperty.clearLoggedProperties();\n    }\n\n    @Test\n    void booleanProperty_should_take_System_property_if_set() throws Exception {\n        restoreSystemProperties(() -> {\n            final String propertyKey = \"my.feature.enable\";\n            System.setProperty(propertyKey, \"false\");\n            Boolean enabled = withEnvironmentVariable(propertyKey, \"true\")\n                    .execute(() -> new BooleanProperty(\"Some feature\", propertyKey, true).isEnabled());\n            assertThat(enabled).isFalse();\n        });\n    }\n\n    @Test\n    void booleanProperty_should_take_envVar_if_no_System_property_if_set() throws Exception {\n        final String systemPropertyKey = \"my.super-cool.feature.enabled\";\n        final String envPropertyKey = \"MY_SUPERCOOL_FEATURE_ENABLED\";\n        Boolean enabled = withEnvironmentVariable(envPropertyKey, \"false\")\n                .execute(() -> new BooleanProperty(\"Some feature\", systemPropertyKey, true).isEnabled());\n        assertThat(enabled).isFalse();\n    }\n\n    @Test\n    void booleanProperty_should_take_default_value_if_no_System_property_nor_env_variable_if_set() {\n        assertThat(new BooleanProperty(\"Some feature\", \"some.key\", false).isEnabled()).isFalse();\n    }\n\n    @Test\n    void initialization_message_should_be_logged_once_only() throws Exception {\n        String log = tapSystemOut(() -> new BooleanProperty(\"my boolean property\", \"my.boolean.property\", false));\n        assertThat(log).contains(\n                \"my boolean property disabled, you may enable it using env property MY_BOOLEAN_PROPERTY or System property -Dmy.boolean.property [=true/false]\");\n\n        // should not log again:\n        log = tapSystemOut(() -> new BooleanProperty(\"my property\", \"my.boolean.property\", false));\n        assertThat(log).doesNotContain(\"my.property\");\n    }\n}\n"
  },
  {
    "path": "services/bonita-commons/src/test/java/org/bonitasoft/engine/properties/StringPropertyTest.java",
    "content": "/**\n * Copyright (C) 2025 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.properties;\n\nimport static com.github.stefanbirkner.systemlambda.SystemLambda.tapSystemOut;\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nclass StringPropertyTest {\n\n    @BeforeEach\n    void setUp() {\n        // Reset the static cache of already logged properties before each test\n        StringProperty.clearLoggedProperties();\n    }\n\n    @Test\n    void initialization_message_should_be_logged_once_only() throws Exception {\n        String log = tapSystemOut(() -> new StringProperty(\"my property\", \"my.property\", \"default value\"));\n        assertThat(log).contains(\n                \"my property default value, you may set it using env property MY_PROPERTY or System property -Dmy.property\");\n\n        // should not log again:\n        log = tapSystemOut(() -> new StringProperty(\"my property\", \"my.property\", \"default value\"));\n        assertThat(log).doesNotContain(\"my.property\");\n    }\n}\n"
  },
  {
    "path": "services/bonita-commons/src/test/java/org/bonitasoft/engine/service/ServicesResolverTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.service;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.when;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\n/**\n * @author Baptiste Mesta\n */\n@ExtendWith(MockitoExtension.class)\nclass ServicesResolverTest {\n\n    @Mock\n    private ServicesLookup servicesLookup;\n    @InjectMocks\n    private ServicesResolver servicesResolver;\n\n    private final class BeanThatNeedMyService {\n\n        private Object myService;\n\n        @InjectedService\n        public void setMyService(final Object myService) {\n            this.myService = myService;\n        }\n\n        Object getMyService() {\n            return myService;\n        }\n\n        public String getName() {\n            return null;\n        }\n\n        public String getDescription() {\n            return null;\n        }\n\n        public void execute() {\n        }\n\n        public void setAttributes(final Map<String, Serializable> attributes) {\n        }\n    }\n\n    @Test\n    void should_injectService_inject_setter_having_the_annotation() throws Exception {\n        final BeanThatNeedMyService beanThatNeedMyService = new BeanThatNeedMyService();\n\n        final Object myService = new Object();\n        when(servicesLookup.lookupService(\"myService\")).thenReturn(myService);\n\n        servicesResolver.injectServices(beanThatNeedMyService);\n\n        assertThat(beanThatNeedMyService.getMyService()).isEqualTo(myService);\n    }\n}\n"
  },
  {
    "path": "services/bonita-commons/src/test/resources/myFile.txt",
    "content": "some content"
  },
  {
    "path": "services/bonita-commons/src/test/resources/org/bonitasoft/engine/commons/io/persistence.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<persistence xmlns=\"http://java.sun.com/xml/ns/persistence\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n             xsi:schemaLocation=\"http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd\" version=\"2.0\">\n\n    <persistence-unit name=\"BDR\" transaction-type=\"JTA\">\n        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>\n        <jta-data-source>java:/comp/env/jdbc/PGDS1</jta-data-source>\n        <class>com.bonitasoft.pojo.Employee</class>\n        <!-- \t\t<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode> -->\n        <!-- \t\t<validation-mode>CALLBACK</validation-mode> -->\n        <properties>\n            <property name=\"hibernate.dialect\" value=\"org.hibernate.dialect.H2Dialect\" />\n            <property name=\"hibernate.hbm2ddl.auto\" value=\"create\" />\n            <property name=\"hibernate.show_sql\" value=\"false\" />\n            <property name=\"hibernate.format_sql\" value=\"true\" />\n            <property name=\"hibernate.transaction.manager_lookup_class\" value=\"org.hibernate.transaction.BTMTransactionManagerLookup\" />\n        </properties>\n    </persistence-unit>\n</persistence>\n"
  },
  {
    "path": "services/bonita-connector-executor/build.gradle",
    "content": "plugins {\n    id('bonita-tests')\n}\n\ndependencies {\n    runtimeOnly libs.logback\n    api project(':services:bonita-commons')\n    api project(':services:bonita-time-tracker')\n    api project(':services:bonita-session')\n    testImplementation libs.assertj\n    testImplementation libs.mockitoCore\n    testImplementation libs.mockitoJunitJupiter\n    testImplementation libs.systemRules\n    annotationProcessor libs.lombok\n    compileOnly libs.lombok\n}\n"
  },
  {
    "path": "services/bonita-connector-executor/src/main/java/org/bonitasoft/engine/connector/AbstractSConnector.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connector;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author Feng Hui\n */\npublic abstract class AbstractSConnector implements SConnector {\n\n    private final Map<String, Object> inputParameters;\n\n    private final Map<String, Object> outputParameters;\n\n    public AbstractSConnector() {\n        inputParameters = new HashMap<>();\n        outputParameters = new HashMap<>();\n    }\n\n    @Override\n    public final void setInputParameters(final Map<String, Object> parameters) {\n        inputParameters.putAll(parameters);\n    }\n\n    protected Object getInputParameter(final String paramName) {\n        Object obj = null;\n        if (inputParameters.containsKey(paramName)) {\n            obj = inputParameters.get(paramName);\n        }\n        return obj;\n    }\n\n    protected void setOutputParameter(final String paramName, final Object value) {\n        outputParameters.put(paramName, value);\n    }\n\n    protected Map<String, Object> getOutputParameters() {\n        return outputParameters;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-connector-executor/src/main/java/org/bonitasoft/engine/connector/BonitaConnectorExecutorFactory.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connector;\n\nimport java.util.concurrent.ThreadPoolExecutor;\n\npublic interface BonitaConnectorExecutorFactory {\n\n    ThreadPoolExecutor create();\n\n}\n"
  },
  {
    "path": "services/bonita-connector-executor/src/main/java/org/bonitasoft/engine/connector/ConnectorExecutionResult.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connector;\n\nimport java.util.Map;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\n\n@Data\n@AllArgsConstructor\npublic class ConnectorExecutionResult {\n\n    public static class ConnectorExecutionResultBuilder {\n\n        private ConnectorExecutionResult connectorExecutionResult;\n\n        ConnectorExecutionResultBuilder(Map<String, Object> outputs) {\n            connectorExecutionResult = new ConnectorExecutionResult(outputs);\n        }\n\n        public ConnectorExecutionResult tookMillis(long executionTimeMillis) {\n            connectorExecutionResult.executionTimeMillis = executionTimeMillis;\n            return connectorExecutionResult;\n        }\n\n    }\n\n    public static ConnectorExecutionResultBuilder result(Map<String, Object> outputs) {\n        return new ConnectorExecutionResultBuilder(outputs);\n    }\n\n    private long executionTimeMillis;\n    private Map<String, Object> outputs;\n\n    private ConnectorExecutionResult(Map<String, Object> outputs) {\n        this.outputs = outputs;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-connector-executor/src/main/java/org/bonitasoft/engine/connector/ConnectorExecutor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connector;\n\nimport java.util.Map;\nimport java.util.concurrent.CompletableFuture;\n\nimport org.bonitasoft.engine.commons.TenantLifecycleService;\nimport org.bonitasoft.engine.connector.exception.SConnectorException;\n\n/**\n * @author Feng Hui\n * @author Matthieu Chaffotte\n * @since 6.0\n */\npublic interface ConnectorExecutor extends TenantLifecycleService {\n\n    /**\n     * Executes a connector.\n     *\n     * @param sConnector\n     *        The connector will be executed\n     * @param inputParameters\n     *        The input parameters of connector\n     * @param classLoader\n     *        The classLoader within the connector will be executed\n     * @return a completable future with the result\n     * @throws SConnectorException\n     *         Error thrown when error occurs in connector executing\n     */\n    CompletableFuture<ConnectorExecutionResult> execute(SConnector sConnector, Map<String, Object> inputParameters,\n            ClassLoader classLoader) throws SConnectorException;\n\n    /**\n     * call disconnect method of the connector\n     *\n     * @param sConnector\n     * @throws SConnectorException\n     */\n    void disconnect(SConnector sConnector) throws SConnectorException;\n}\n"
  },
  {
    "path": "services/bonita-connector-executor/src/main/java/org/bonitasoft/engine/connector/SConnector.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connector;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.connector.exception.SConnectorException;\nimport org.bonitasoft.engine.connector.exception.SConnectorValidationException;\n\n/**\n * @author Feng Hui\n */\npublic interface SConnector {\n\n    /**\n     * Set the input parameter for a connector.\n     *\n     * @param parameters\n     *        parameters is a map with parameter names and their value.\n     */\n    void setInputParameters(Map<String, Object> parameters);\n\n    /**\n     * Validate the input parameters. Check the parameters types and boundaries.\n     *\n     * @throws SConnectorValidationException\n     */\n    void validate() throws SConnectorValidationException;\n\n    /**\n     * Execute the connector.\n     *\n     * @return the connector outputs map corresponding to the output definition.\n     * @throws SConnectorException\n     */\n    Map<String, Object> execute() throws SConnectorException;\n\n    /**\n     * Called by the engine before the connector is executed\n     * This method can be implemented by connectors to handle here opening of connections like database connection\n     *\n     * @throws SConnectorException\n     */\n    void connect() throws SConnectorException;\n\n    /**\n     * Called by the engine after the connector and its output operations are executed\n     * This method can be implemented by connectors to close connections here.\n     * The typical use of this is to be able to return connected objects that will be used in output operation and then\n     * disconnect them.\n     *\n     * @throws SConnectorException\n     */\n    void disconnect() throws SConnectorException;\n}\n"
  },
  {
    "path": "services/bonita-connector-executor/src/main/java/org/bonitasoft/engine/connector/exception/SConnectorException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connector.exception;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Feng Hui\n */\npublic class SConnectorException extends SBonitaException {\n\n    private static final long serialVersionUID = -3113075377405323282L;\n\n    public SConnectorException(String message) {\n        super(message);\n    }\n\n    public SConnectorException(Throwable t) {\n        super(t);\n    }\n\n    public SConnectorException(String message, Exception e) {\n        super(message, e);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-connector-executor/src/main/java/org/bonitasoft/engine/connector/exception/SConnectorValidationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connector.exception;\n\n/**\n * @author Feng Hui\n * @author Celine Souchet\n */\npublic class SConnectorValidationException extends SConnectorException {\n\n    private static final long serialVersionUID = -7025831546419799447L;\n\n    public SConnectorValidationException(String message) {\n        super(message);\n    }\n\n    public SConnectorValidationException(Throwable t) {\n        super(t);\n    }\n\n    public SConnectorValidationException(String message, Exception e) {\n        super(message, e);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-connector-executor/src/main/java/org/bonitasoft/engine/connector/exception/SInvalidEvaluationConnectorConditionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connector.exception;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * Thrown when the evaluation of the condition of the connector instance is invalid.\n * The class SInvalidEvaluationConnectorConditionException is a form of Throwable that indicates conditions that a\n * reasonable application might want to catch.\n * The class SInvalidEvaluationConnectorConditionException that is not also subclasses of {@link RuntimeException} are\n * checked exceptions.\n * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by\n * the execution of the method or\n * constructor and propagate outside the method or constructor boundary.\n *\n * @author Celine Souchet\n */\npublic class SInvalidEvaluationConnectorConditionException extends SBonitaException {\n\n    private static final long serialVersionUID = -7035298849808114112L;\n\n    /**\n     * Constructs a new exception with the two conditions to compare\n     *\n     * @param condition1\n     *        The first condition\n     * @param condition2\n     *        The second condition\n     */\n    public SInvalidEvaluationConnectorConditionException(final int condition1, final int condition2) {\n        super(condition1 + \" is not equal to \" + condition2 + \" .\");\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-connector-executor/src/main/java/org/bonitasoft/engine/connector/impl/ConnectorExecutorImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connector.impl;\n\nimport static org.bonitasoft.engine.connector.ConnectorExecutionResult.result;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicLong;\nimport java.util.stream.Collectors;\n\nimport io.micrometer.core.instrument.Counter;\nimport io.micrometer.core.instrument.Gauge;\nimport io.micrometer.core.instrument.MeterRegistry;\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException;\nimport org.bonitasoft.engine.connector.BonitaConnectorExecutorFactory;\nimport org.bonitasoft.engine.connector.ConnectorExecutionResult;\nimport org.bonitasoft.engine.connector.ConnectorExecutor;\nimport org.bonitasoft.engine.connector.SConnector;\nimport org.bonitasoft.engine.connector.exception.SConnectorException;\nimport org.bonitasoft.engine.monitoring.ExecutorServiceMetricsProvider;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.bonitasoft.engine.sessionaccessor.SessionIdNotSetException;\nimport org.bonitasoft.engine.tracking.TimeTracker;\nimport org.bonitasoft.engine.tracking.TimeTrackerRecords;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;\nimport org.springframework.stereotype.Component;\n\n/**\n * Execute connectors directly\n *\n * @author Baptiste Mesta\n * @author Celine Souchet\n * @author Matthieu Chaffotte\n */\n@Slf4j\n@Component\n@ConditionalOnSingleCandidate(ConnectorExecutor.class)\npublic class ConnectorExecutorImpl implements ConnectorExecutor {\n\n    public static final String NUMBER_OF_CONNECTORS_PENDING = \"bonita.bpmengine.connector.pending\";\n    public static final String NUMBER_OF_CONNECTORS_RUNNING = \"bonita.bpmengine.connector.running\";\n    public static final String NUMBER_OF_CONNECTORS_EXECUTED = \"bonita.bpmengine.connector.executed\";\n    public static final String CONNECTORS_UNIT = \"connectors\";\n\n    private ExecutorService executorService;\n    private final BonitaConnectorExecutorFactory bonitaConnectorExecutorFactory;\n    private final SessionAccessor sessionAccessor;\n    private final SessionService sessionService;\n\n    private final TimeTracker timeTracker;\n    private final MeterRegistry meterRegistry;\n    private final ExecutorServiceMetricsProvider executorServiceMetricsProvider;\n\n    private final AtomicLong runningWorks = new AtomicLong();\n    private Counter executedWorkCounter;\n    private Gauge numberOfConnectorsPending;\n    private Gauge numberOfConnectorsRunning;\n\n    public ConnectorExecutorImpl(final SessionAccessor sessionAccessor,\n            final SessionService sessionService,\n            final TimeTracker timeTracker,\n            final MeterRegistry meterRegistry,\n            ExecutorServiceMetricsProvider executorServiceMetricsProvider,\n            BonitaConnectorExecutorFactory bonitaConnectorExecutorFactory) {\n        this.sessionAccessor = sessionAccessor;\n        this.sessionService = sessionService;\n        this.timeTracker = timeTracker;\n        this.meterRegistry = meterRegistry;\n        this.executorServiceMetricsProvider = executorServiceMetricsProvider;\n        this.bonitaConnectorExecutorFactory = bonitaConnectorExecutorFactory;\n    }\n\n    @Override\n    public CompletableFuture<ConnectorExecutionResult> execute(final SConnector sConnector,\n            final Map<String, Object> inputParameters, final ClassLoader classLoader) throws SConnectorException {\n        if (executorService == null) {\n            throw new SConnectorException(\"Unable to execute a connector, if the node is not started. Start it first\");\n        }\n\n        ExecuteConnectorCallable task = new ExecuteConnectorCallable(inputParameters, sConnector,\n                classLoader);\n        return execute(sConnector, task);\n    }\n\n    protected CompletableFuture<ConnectorExecutionResult> execute(SConnector sConnector,\n            InterruptibleCallable<Map<String, Object>> task) {\n        return CompletableFuture.supplyAsync(() -> {\n            try {\n                return wrapForStats(task).call();\n            } catch (Throwable e) {\n                disconnectSilently(sConnector);\n                throw new SBonitaRuntimeException(e);\n            }\n        }, executorService);\n    }\n\n    private Callable<ConnectorExecutionResult> wrapForStats(final Callable<Map<String, Object>> task) {\n        return () -> {\n            runningWorks.incrementAndGet();\n            try {\n                long startTime = System.currentTimeMillis();\n                Map<String, Object> call = task.call();\n                executedWorkCounter.increment();\n                return result(call).tookMillis(System.currentTimeMillis() - startTime);\n            } finally {\n                runningWorks.decrementAndGet();\n            }\n        };\n    }\n\n    private void track(final TimeTrackerRecords recordName, final long startTime, final SConnector sConnector,\n            final Map<String, Object> inputParameters) {\n        if (timeTracker.isTrackable(recordName)) {\n            final long endTime = System.currentTimeMillis();\n            final StringBuilder desc = new StringBuilder();\n            desc.append(\"Connector: \");\n            desc.append(sConnector);\n            desc.append(\" - \");\n            desc.append(\"inputParameters: \");\n            desc.append(inputParameters);\n            timeTracker.track(recordName, desc.toString(), endTime - startTime);\n        }\n    }\n\n    void disconnectSilently(final SConnector sConnector) {\n        try {\n            sConnector.disconnect();\n        } catch (final Exception t) {\n            log.warn(\"An error occurred while disconnecting the connector: {}\", sConnector, t);\n        }\n    }\n\n    @Override\n    public void disconnect(final SConnector sConnector) throws SConnectorException {\n        try {\n            sConnector.disconnect();\n        } catch (final SConnectorException e) {\n            throw e;\n        } catch (final Exception t) {\n            throw new SConnectorException(t);\n        }\n    }\n\n    /**\n     * @author Baptiste Mesta\n     */\n    public final class ExecuteConnectorCallable implements InterruptibleCallable<Map<String, Object>> {\n\n        private final Map<String, Object> inputParameters;\n\n        private final SConnector sConnector;\n\n        private final ClassLoader loader;\n        private Thread thread;\n        private boolean interrupted;\n        private boolean completed;\n\n        private ExecuteConnectorCallable(final Map<String, Object> inputParameters, final SConnector sConnector,\n                final ClassLoader loader) {\n            this.inputParameters = inputParameters;\n            this.sConnector = sConnector;\n            this.loader = loader;\n        }\n\n        @Override\n        public Map<String, Object> call() throws Exception {\n            log.debug(\"Start execution of connector {}\", sConnector.getClass());\n            if (interrupted) {\n                throw new InterruptedException();\n            }\n            final long startTime = System.currentTimeMillis();\n\n            //Fix Classloading issue with ThreadLocal implementation of SessionAccessor\n            Thread.currentThread().setContextClassLoader(loader);\n\n            sConnector.setInputParameters(inputParameters);\n            try {\n                thread = Thread.currentThread();\n                sConnector.validate();\n                sConnector.connect();\n                return sConnector.execute();\n            } finally {\n                thread = null;\n                completed = true;\n                log.info(\"Finish execution of connector {}\", sConnector.getClass());\n                // in case a session has been created: see ConnectorAPIAccessorImpl\n                try {\n                    final long sessionId = sessionAccessor.getSessionId();\n                    sessionAccessor.deleteSessionId();\n                    sessionService.deleteSession(sessionId);\n                } catch (final SessionIdNotSetException e) {\n                    // nothing, no session has been created\n                }\n                track(TimeTrackerRecords.EXECUTE_CONNECTOR_CALLABLE, startTime, sConnector, inputParameters);\n            }\n        }\n\n        @Override\n        public void interrupt() {\n            interrupted = true;\n            if (thread != null) {\n                StackTraceElement[] stackTrace = thread.getStackTrace();\n                String stack = Arrays.stream(stackTrace).map(StackTraceElement::toString)\n                        .collect(Collectors.joining(\"\\n\"));\n                log.warn(\n                        \"Interrupt thread of connector {}, thread is {}, {}, connectors was doing :\\n {}, activate debug logs to have the full execution stacktrace.\",\n                        sConnector.getClass(), thread.getName(), thread.getId(), stackTrace[0].toString());\n                log.debug(\"Interrupt thread of connector {}, thread is {}, {}, stack is:\\n {}\",\n                        sConnector.getClass(), thread.getName(), thread.getId(), stack);\n                thread.interrupt();\n            }\n        }\n\n        @Override\n        public boolean isCompleted() {\n            return completed;\n        }\n    }\n\n    @Override\n    public void start() {\n        if (executorService == null) {\n            var threadPoolExecutor = bonitaConnectorExecutorFactory.create();\n            executorService = executorServiceMetricsProvider\n                    .bind(meterRegistry,\n                            threadPoolExecutor,\n                            \"bonita-connector-executor\");\n            numberOfConnectorsPending = Gauge\n                    .builder(NUMBER_OF_CONNECTORS_PENDING, threadPoolExecutor.getQueue(), Collection::size)\n                    .baseUnit(CONNECTORS_UNIT).description(\"Connectors pending in the execution queue\")\n                    .register(meterRegistry);\n            numberOfConnectorsRunning = Gauge.builder(NUMBER_OF_CONNECTORS_RUNNING, runningWorks, AtomicLong::get)\n                    .baseUnit(CONNECTORS_UNIT).description(\"Connectors currently executing\")\n                    .register(meterRegistry);\n            executedWorkCounter = Counter.builder(NUMBER_OF_CONNECTORS_EXECUTED)\n                    .baseUnit(CONNECTORS_UNIT)\n                    .description(\"Total connectors executed since last server start\")\n                    .register(meterRegistry);\n        }\n    }\n\n    // For unit tests\n    ExecutorService getExecutorService() {\n        return executorService;\n    }\n\n    @Override\n    public void stop() {\n        if (executorService != null) {\n            meterRegistry.remove(executedWorkCounter);\n            meterRegistry.remove(numberOfConnectorsRunning);\n            meterRegistry.remove(numberOfConnectorsPending);\n            executorServiceMetricsProvider.unbind(meterRegistry, \"bonita-connector-executor\");\n\n            executorService.shutdown();\n            try {\n                if (!executorService.awaitTermination(5000, TimeUnit.MILLISECONDS)) {\n                    log.warn(\"Timeout (5s) trying to stop the connector executor thread pool.\");\n                }\n            } catch (final InterruptedException e) {\n                log.warn(\"Error while stopping the connector executor thread pool.\", e);\n            }\n            executorService = null;\n        }\n    }\n\n    @Override\n    public void pause() {\n        stop();\n    }\n\n    @Override\n    public void resume() {\n        start();\n    }\n}\n"
  },
  {
    "path": "services/bonita-connector-executor/src/main/java/org/bonitasoft/engine/connector/impl/ConnectorExecutorThreadFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connector.impl;\n\nimport java.util.concurrent.ThreadFactory;\nimport java.util.concurrent.atomic.AtomicInteger;\n\n/**\n * @author Baptiste Mesta\n */\npublic class ConnectorExecutorThreadFactory implements ThreadFactory {\n\n    private static AtomicInteger nbThread = new AtomicInteger(1);\n\n    private final String name;\n\n    public ConnectorExecutorThreadFactory(final String name) {\n        this.name = name;\n    }\n\n    @Override\n    public Thread newThread(final Runnable runnable) {\n        return new Thread(runnable, name + \"-\" + nbThread.getAndIncrement());\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-connector-executor/src/main/java/org/bonitasoft/engine/connector/impl/ConnectorSingleThreadExecutorFactory.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connector.impl;\n\nimport static java.util.concurrent.TimeUnit.MILLISECONDS;\n\nimport java.util.concurrent.ArrayBlockingQueue;\nimport java.util.concurrent.RejectedExecutionException;\nimport java.util.concurrent.RejectedExecutionHandler;\nimport java.util.concurrent.ThreadPoolExecutor;\n\nimport org.bonitasoft.engine.connector.BonitaConnectorExecutorFactory;\nimport org.bonitasoft.engine.mdc.MDCTransmitingThreadPoolExecutor;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;\nimport org.springframework.stereotype.Component;\n\n@Component\n@ConditionalOnSingleCandidate(BonitaConnectorExecutorFactory.class)\npublic class ConnectorSingleThreadExecutorFactory implements BonitaConnectorExecutorFactory {\n\n    private final int queueCapacity;\n\n    public ConnectorSingleThreadExecutorFactory(@Value(\"${bonita.tenant.connector.queueCapacity}\") int queueCapacity) {\n        this.queueCapacity = queueCapacity;\n    }\n\n    @Override\n    public ThreadPoolExecutor create() {\n        return new MDCTransmitingThreadPoolExecutor(1, 1, 0L, MILLISECONDS,\n                new ArrayBlockingQueue<>(queueCapacity), new ConnectorExecutorThreadFactory(\"ConnectorExecutor\"),\n                new QueueRejectedExecutionHandler());\n    }\n\n    public static class QueueRejectedExecutionHandler implements RejectedExecutionHandler {\n\n        private static final Logger log = LoggerFactory.getLogger(QueueRejectedExecutionHandler.class);\n\n        @Override\n        public void rejectedExecution(final Runnable task, final ThreadPoolExecutor executor) {\n            log.warn(\"The work was rejected. Requeue work : {}\", task);\n            try {\n                executor.getQueue().put(task);\n            } catch (final InterruptedException e) {\n                throw new RejectedExecutionException(\"Queuing \" + task + \" got interrupted.\", e);\n            }\n        }\n\n    }\n}\n"
  },
  {
    "path": "services/bonita-connector-executor/src/main/java/org/bonitasoft/engine/connector/impl/InterruptibleCallable.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connector.impl;\n\nimport java.util.concurrent.Callable;\n\npublic interface InterruptibleCallable<T> extends Callable<T> {\n\n    void interrupt();\n\n    boolean isCompleted();\n}\n"
  },
  {
    "path": "services/bonita-connector-executor/src/test/java/org/bonitasoft/engine/connector/impl/ConnectorExecutionTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connector.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Future;\n\nimport io.micrometer.core.instrument.simple.SimpleMeterRegistry;\nimport org.bonitasoft.engine.connector.AbstractSConnector;\nimport org.bonitasoft.engine.connector.ConnectorExecutionResult;\nimport org.bonitasoft.engine.connector.exception.SConnectorException;\nimport org.bonitasoft.engine.connector.exception.SConnectorValidationException;\nimport org.bonitasoft.engine.monitoring.DefaultExecutorServiceMetricsProvider;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.bonitasoft.engine.tracking.TimeTracker;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ConnectorExecutionTest {\n\n    @Mock\n    private SessionAccessor sessionAccessor;\n\n    @Mock\n    private SessionService sessionService;\n\n    @Mock\n    private TimeTracker timeTracker;\n\n    private ConnectorExecutorImpl connectorExecutor;\n\n    @Before\n    public void setUp() {\n        connectorExecutor = new ConnectorExecutorImpl(sessionAccessor,\n                sessionService,\n                timeTracker,\n                new SimpleMeterRegistry(),\n                new DefaultExecutorServiceMetricsProvider(),\n                new ConnectorSingleThreadExecutorFactory(10));\n        connectorExecutor.start();\n    }\n\n    @After\n    public void tearDown() {\n        connectorExecutor.stop();\n    }\n\n    @Test\n    public void should_execute_a_simple_connector() throws Exception {\n        connectorExecutor.execute(new ResourceConnector(\"test2\"), null, new ResourceClassLoader(\"test2\"));\n    }\n\n    @Test\n    public void should_execute_connectors_concurrently() throws Exception {\n        final Callable<Void> task = buildConnectorExecutionCallable(\"test\");\n        final Callable<Void> task2 = buildConnectorExecutionCallable(\"test2\");\n\n        final ExecutorService service = Executors.newFixedThreadPool(25);\n        final List<Callable<Void>> tasks1 = Collections.nCopies(50, task);\n        final List<Callable<Void>> tasks2 = Collections.nCopies(50, task2);\n\n        final List<Callable<Void>> tasks = new ArrayList<>(tasks1);\n        tasks.addAll(tasks2);\n\n        final List<Future<Void>> all = service.invokeAll(tasks);\n        service.shutdown();\n\n        for (final Future<Void> future : all) {\n            future.get();\n        }\n    }\n\n    @Test\n    public void should_return_execution_time_of_the_connector() throws Exception {\n        ConnectorExecutionResult connectorExecutionResult = connectorExecutor\n                .execute(new SleepConnector(50), Collections.emptyMap(), Thread.currentThread().getContextClassLoader())\n                .get();\n\n        assertThat(connectorExecutionResult.getExecutionTimeMillis()).isGreaterThanOrEqualTo(50).isLessThan(1000);\n    }\n\n    private Callable<Void> buildConnectorExecutionCallable(final String resourceName) {\n        return () -> {\n            connectorExecutor.execute(new ResourceConnector(resourceName), null, new ResourceClassLoader(resourceName));\n            return null;\n        };\n    }\n\n    private static class SleepConnector extends AbstractSConnector {\n\n        private int sleepMillis;\n\n        public SleepConnector(int sleepMillis) {\n            this.sleepMillis = sleepMillis;\n        }\n\n        @Override\n        public void validate() throws SConnectorValidationException {\n        }\n\n        @Override\n        public Map<String, Object> execute() throws SConnectorException {\n            try {\n                sleepMillis = 100;\n                Thread.sleep(sleepMillis);\n            } catch (InterruptedException e) {\n                throw new SConnectorException(e);\n            }\n            return null;\n        }\n\n        @Override\n        public void connect() throws SConnectorException {\n        }\n\n        @Override\n        public void disconnect() throws SConnectorException {\n        }\n    }\n}\n"
  },
  {
    "path": "services/bonita-connector-executor/src/test/java/org/bonitasoft/engine/connector/impl/ConnectorExecutorImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connector.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.fail;\nimport static org.mockito.Mockito.doThrow;\nimport static org.mockito.Mockito.verify;\n\nimport java.time.Duration;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.TimeUnit;\n\nimport io.micrometer.core.instrument.Clock;\nimport io.micrometer.core.instrument.simple.SimpleMeterRegistry;\nimport org.bonitasoft.engine.connector.AbstractSConnector;\nimport org.bonitasoft.engine.connector.ConnectorExecutionResult;\nimport org.bonitasoft.engine.connector.SConnector;\nimport org.bonitasoft.engine.connector.exception.SConnectorException;\nimport org.bonitasoft.engine.monitoring.DefaultExecutorServiceMetricsProvider;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.bonitasoft.engine.tracking.TimeTracker;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.SystemOutRule;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ConnectorExecutorImplTest {\n\n    @Mock\n    private SessionAccessor sessionAccessor;\n\n    @Mock\n    private SessionService sessionService;\n\n    @Mock\n    private SConnector connector;\n\n    @Mock\n    private TimeTracker timeTracker;\n\n    private ConnectorExecutorImpl connectorExecutorImpl;\n\n    private SimpleMeterRegistry meterRegistry;\n\n    @Rule\n    public SystemOutRule systemOutRule = new SystemOutRule().enableLog().muteForSuccessfulTests();\n\n    @Before\n    public void before() {\n        meterRegistry = new SimpleMeterRegistry(\n                // So that micrometer updates its counters every 1 ms:\n                k -> k.equals(\"simple.step\") ? Duration.ofMillis(1).toString() : null,\n                Clock.SYSTEM);\n        connectorExecutorImpl = new ConnectorExecutorImpl(sessionAccessor, sessionService,\n                timeTracker,\n                meterRegistry,\n                new DefaultExecutorServiceMetricsProvider(),\n                new ConnectorSingleThreadExecutorFactory(1));\n\n        connectorExecutorImpl.start();\n    }\n\n    @Test\n    public void should_execute_submit_callable() throws Exception {\n        SConnector connector = new SConnector() {\n\n            @Override\n            public void setInputParameters(Map<String, Object> parameters) {\n\n            }\n\n            @Override\n            public void validate() {\n\n            }\n\n            @Override\n            public Map<String, Object> execute() {\n                return Collections.singletonMap(\"result\", \"resultValue\");\n            }\n\n            @Override\n            public void connect() {\n\n            }\n\n            @Override\n            public void disconnect() {\n\n            }\n        };\n        final ConnectorExecutionResult result = connectorExecutorImpl.execute(connector,\n                Collections.singletonMap(\"key\", \"value\"), Thread.currentThread().getContextClassLoader())\n                .get(100, TimeUnit.MILLISECONDS);\n\n        assertThat(result.getOutputs().size()).isEqualTo(1);\n        assertThat(result.getOutputs().get(\"result\")).isEqualTo(\"resultValue\");\n    }\n\n    @Test(expected = SConnectorException.class)\n    public void should_execute_throw_exception_when_not_started() throws Exception {\n        // given\n        connectorExecutorImpl.stop();\n        // when\n        connectorExecutorImpl.execute(connector, Collections.singletonMap(\"key\", \"value\"),\n                Thread.currentThread().getContextClassLoader());\n    }\n\n    @Test\n    public void should_disconnect_call_disconnect_on_connector() throws Exception {\n        // when\n        connectorExecutorImpl.disconnect(connector);\n        // then\n        verify(connector).disconnect();\n    }\n\n    @Test\n    public void should_disconnect_rethrow_connector_exceptions() throws Exception {\n        // given\n        final SConnectorException exception = new SConnectorException(\"myException\");\n        doThrow(exception).when(connector).disconnect();\n        // when\n        try {\n            connectorExecutorImpl.disconnect(connector);\n            fail(\"should have thrown the exception\");\n        } catch (final SConnectorException e) {\n            // then\n            assertThat(e).isEqualTo(exception);\n        }\n    }\n\n    @Test\n    public void should_disconnectSilently_only_logException() throws Exception {\n        // given\n        final SConnectorException exception = new SConnectorException(\"myException\");\n        doThrow(exception).when(connector).disconnect();\n        // when\n        systemOutRule.clearLog();\n        connectorExecutorImpl.disconnectSilently(connector);\n        // then\n        assertThat(systemOutRule.getLog())\n                .contains(\"An error occurred while disconnecting the connector: \" + connector);\n    }\n\n    @Test\n    public void should_stop_await_termination_of_thread_pool() {\n        ExecutorService executorService = connectorExecutorImpl.getExecutorService();\n\n        connectorExecutorImpl.stop();\n\n        assertThat(executorService.isShutdown()).isTrue();\n    }\n\n    @Test\n    public void pause_should_await_termination_of_thread_pool() {\n        ExecutorService executorService = connectorExecutorImpl.getExecutorService();\n\n        connectorExecutorImpl.stop();\n\n        assertThat(executorService.isShutdown()).isTrue();\n    }\n\n    @Test\n    public void start_should_await_termination_of_thread_pool() {\n        // when\n        // start in before\n\n        // then\n        assertThat(connectorExecutorImpl.getExecutorService()).as(\"The executor service must be not null.\").isNotNull();\n    }\n\n    @Test\n    public void resume_should_await_termination_of_thread_pool() {\n        // when\n        connectorExecutorImpl.pause();\n        connectorExecutorImpl.resume();\n\n        // then\n        assertThat(connectorExecutorImpl.getExecutorService()).as(\"The executor service must be not null.\").isNotNull();\n    }\n\n    @Test\n    public void should_update_connectors_counters_when_adding_a_connector_with_immediate_execution() throws Exception {\n        //when:\n        executeAConnector();\n        TimeUnit.MILLISECONDS.sleep(50); // give some time to consider the connector to process\n\n        //then:\n        assertThat(meterRegistry.find(ConnectorExecutorImpl.NUMBER_OF_CONNECTORS_EXECUTED).counter().count())\n                .as(\"Executed connectors number\").isEqualTo(1);\n        assertThat(meterRegistry.find(ConnectorExecutorImpl.NUMBER_OF_CONNECTORS_RUNNING).gauge().value())\n                .as(\"Running connectors number\").isEqualTo(0);\n        assertThat(meterRegistry.find(ConnectorExecutorImpl.NUMBER_OF_CONNECTORS_PENDING).gauge().value())\n                .as(\"Pending connectors number\").isEqualTo(0);\n    }\n\n    @Test\n    public void should_reset_and_have_counters_after_pause_and_resume() throws Exception {\n        //when:\n        executeAConnector();\n        TimeUnit.MILLISECONDS.sleep(50); // give some time to consider the connector to process\n        assertThat(meterRegistry.find(ConnectorExecutorImpl.NUMBER_OF_CONNECTORS_EXECUTED).counter().count())\n                .as(\"Executed connectors number\").isEqualTo(1);\n        assertThat(meterRegistry.find(\"executor.completed\").functionCounter().count()).isEqualTo(1);\n        connectorExecutorImpl.pause();\n\n        assertThat(meterRegistry.getMeters()).hasSize(0);\n\n        connectorExecutorImpl.resume();\n        executeAConnector();\n        executeAConnector();\n        TimeUnit.MILLISECONDS.sleep(50); // give some time to consider the connector to process\n\n        //then:\n        assertThat(meterRegistry.find(ConnectorExecutorImpl.NUMBER_OF_CONNECTORS_EXECUTED).counter().count())\n                .as(\"Executed connectors number\").isEqualTo(2);\n        assertThat(meterRegistry.find(\"executor.completed\").functionCounter().count()).isEqualTo(2);\n\n    }\n\n    private void executeAConnector()\n            throws InterruptedException, java.util.concurrent.ExecutionException, SConnectorException {\n        connectorExecutorImpl\n                .execute(new LocalSConnector(-1), new HashMap<>(), Thread.currentThread().getContextClassLoader())\n                .get();\n    }\n\n    @Test\n    public void should_update_connectors_counters_when_enqueuing_connectors_with_long_processing_time()\n            throws Exception {\n        connectorExecutorImpl.execute(new LocalSConnector(2), new HashMap<>(),\n                Thread.currentThread().getContextClassLoader());\n        connectorExecutorImpl.execute(new LocalSConnector(2), new HashMap<>(),\n                Thread.currentThread().getContextClassLoader());\n        TimeUnit.MILLISECONDS.sleep(50); // give some time to consider the connector to process\n\n        //then:  one is in queue (only one thread to execute connectors) and one is pending\n        assertThat(meterRegistry.find(ConnectorExecutorImpl.NUMBER_OF_CONNECTORS_EXECUTED).counter().count())\n                .as(\"Executed connectors number\").isEqualTo(0);\n        assertThat(meterRegistry.find(ConnectorExecutorImpl.NUMBER_OF_CONNECTORS_RUNNING).gauge().value())\n                .as(\"Running connectors number\").isEqualTo(1);\n        assertThat(meterRegistry.find(ConnectorExecutorImpl.NUMBER_OF_CONNECTORS_PENDING).gauge().value())\n                .as(\"Pending connectors number\").isEqualTo(1);\n    }\n\n    @Test\n    public void createExecutorService_should_register_ExecutorServiceMetrics() {\n        assertThat(\n                meterRegistry.find(\"executor.pool.size\")\n                        .tag(\"name\", \"bonita-connector-executor\")\n                        .gauge())\n                .isNotNull();\n    }\n\n    // =================================================================================================================\n    // UTILS\n    // =================================================================================================================\n\n    private static class LocalSConnector extends AbstractSConnector {\n\n        private final long sleepPeriodInSeconds;\n\n        private LocalSConnector(long sleepPeriodInSeconds) {\n            this.sleepPeriodInSeconds = sleepPeriodInSeconds;\n        }\n\n        @Override\n        public void validate() {\n            // do nothing\n        }\n\n        @Override\n        public Map<String, Object> execute() {\n            if (sleepPeriodInSeconds > 0) {\n                try {\n                    TimeUnit.SECONDS.sleep(sleepPeriodInSeconds);\n                } catch (InterruptedException e) {\n                    throw new RuntimeException(e);\n                }\n            }\n            return new HashMap<>();\n        }\n\n        @Override\n        public void connect() {\n            // do nothing\n        }\n\n        @Override\n        public void disconnect() {\n            // do nothing\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-connector-executor/src/test/java/org/bonitasoft/engine/connector/impl/ConnectorExecutorSingleThreadTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connector.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\n\nimport java.time.Duration;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.TimeUnit;\n\nimport io.micrometer.core.instrument.Clock;\nimport io.micrometer.core.instrument.simple.SimpleMeterRegistry;\nimport org.bonitasoft.engine.connector.AbstractSConnector;\nimport org.bonitasoft.engine.connector.ConnectorExecutionResult;\nimport org.bonitasoft.engine.monitoring.DefaultExecutorServiceMetricsProvider;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.bonitasoft.engine.tracking.TimeTracker;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\n/**\n * Tests for ConnectorExecutorImpl with pool size 1 (single thread).\n * Documents that the pool correctly replaces its worker thread after connector exceptions,\n * so that subsequent connectors are not stuck forever.\n */\n@ExtendWith(MockitoExtension.class)\nclass ConnectorExecutorSingleThreadTest {\n\n    private static final long TENANT_ID = 12L;\n\n    @Mock\n    private SessionAccessor sessionAccessor;\n\n    @Mock\n    private SessionService sessionService;\n\n    @Mock\n    private TimeTracker timeTracker;\n\n    private ConnectorExecutorImpl connectorExecutorImpl;\n    private SimpleMeterRegistry meterRegistry;\n\n    @BeforeEach\n    void setUp() {\n        meterRegistry = new SimpleMeterRegistry(\n                // So that micrometer updates its counters every 1 ms:\n                k -> k.equals(\"simple.step\") ? Duration.ofMillis(1).toString() : null,\n                Clock.SYSTEM);\n        connectorExecutorImpl = new ConnectorExecutorImpl(sessionAccessor, sessionService,\n                timeTracker, meterRegistry, new DefaultExecutorServiceMetricsProvider(),\n                new ConnectorSingleThreadExecutorFactory(1));\n        connectorExecutorImpl.start();\n    }\n\n    @AfterEach\n    void tearDown() {\n        connectorExecutorImpl.stop();\n    }\n\n    @Test\n    void execute_should_continue_processing_after_connector_exception() throws Exception {\n        // Execute a failing connector\n        assertThatThrownBy(() -> connectorExecutorImpl\n                .execute(new FailingSConnector(), new HashMap<>(),\n                        Thread.currentThread().getContextClassLoader())\n                .get(5, TimeUnit.SECONDS))\n                .isInstanceOf(ExecutionException.class);\n\n        // Execute a successful connector — must succeed even with pool size 1\n        ConnectorExecutionResult result = connectorExecutorImpl\n                .execute(new LocalSConnector(), new HashMap<>(),\n                        Thread.currentThread().getContextClassLoader())\n                .get(5, TimeUnit.SECONDS);\n\n        assertThat(result.getOutputs()).containsEntry(\"result\", \"success\");\n    }\n\n    @Test\n    void execute_should_complete_future_exceptionally_on_connector_failure() {\n        assertThatThrownBy(() -> connectorExecutorImpl\n                .execute(new FailingSConnector(), new HashMap<>(),\n                        Thread.currentThread().getContextClassLoader())\n                .get(5, TimeUnit.SECONDS))\n                .isInstanceOf(ExecutionException.class)\n                .hasCauseInstanceOf(RuntimeException.class)\n                .hasMessageContaining(\"connector failure\");\n    }\n\n    @Test\n    void execute_should_maintain_accurate_metrics_after_exception() throws Exception {\n        // Execute a failing connector\n        assertThatThrownBy(() -> connectorExecutorImpl\n                .execute(new FailingSConnector(), new HashMap<>(),\n                        Thread.currentThread().getContextClassLoader())\n                .get(5, TimeUnit.SECONDS))\n                .isInstanceOf(ExecutionException.class);\n\n        // Execute a successful connector\n        connectorExecutorImpl\n                .execute(new LocalSConnector(), new HashMap<>(),\n                        Thread.currentThread().getContextClassLoader())\n                .get(5, TimeUnit.SECONDS);\n\n        TimeUnit.MILLISECONDS.sleep(50); // give micrometer time to update counters\n\n        // Note: unlike the worker pool, the connector executor only counts successful executions\n        var counter = meterRegistry.find(ConnectorExecutorImpl.NUMBER_OF_CONNECTORS_EXECUTED).counter();\n        assertThat(counter)\n                .as(\"Counter for number of connectors executed should be present\")\n                .isNotNull();\n        assertThat(counter.count())\n                .as(\"Only the successful connector should be counted as executed\")\n                .isEqualTo(1);\n    }\n\n    // =================================================================================================================\n    // UTILS\n    // =================================================================================================================\n\n    private static class LocalSConnector extends AbstractSConnector {\n\n        @Override\n        public void validate() {\n        }\n\n        @Override\n        public Map<String, Object> execute() {\n            return Collections.singletonMap(\"result\", \"success\");\n        }\n\n        @Override\n        public void connect() {\n        }\n\n        @Override\n        public void disconnect() {\n        }\n    }\n\n    private static class FailingSConnector extends AbstractSConnector {\n\n        @Override\n        public void validate() {\n        }\n\n        @Override\n        public Map<String, Object> execute() {\n            throw new RuntimeException(\"connector failure\");\n        }\n\n        @Override\n        public void connect() {\n        }\n\n        @Override\n        public void disconnect() {\n        }\n    }\n}\n"
  },
  {
    "path": "services/bonita-connector-executor/src/test/java/org/bonitasoft/engine/connector/impl/ResourceClassLoader.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connector.impl;\n\npublic class ResourceClassLoader extends ClassLoader {\n\n    private final String resourceName;\n\n    public ResourceClassLoader(final String resourceName) {\n        this.resourceName = resourceName;\n    }\n\n    @Override\n    public Class<?> loadClass(final String name) throws ClassNotFoundException {\n        if (resourceName.equals(name)) {\n            return null;\n        } else {\n            return super.loadClass(name);\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-connector-executor/src/test/java/org/bonitasoft/engine/connector/impl/ResourceConnector.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.connector.impl;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.connector.SConnector;\nimport org.bonitasoft.engine.connector.exception.SConnectorException;\nimport org.bonitasoft.engine.connector.exception.SConnectorValidationException;\n\npublic class ResourceConnector implements SConnector {\n\n    private final String resourceName;\n\n    public ResourceConnector(final String resourceName) {\n        this.resourceName = resourceName;\n    }\n\n    @Override\n    public void setInputParameters(final Map<String, Object> parameters) {\n        // Nothing to do\n    }\n\n    @Override\n    public void validate() throws SConnectorValidationException {\n        // Nothing to do\n    }\n\n    @Override\n    public Map<String, Object> execute() throws SConnectorException {\n        try {\n            Thread.currentThread().getContextClassLoader().loadClass(resourceName);\n            return null;\n        } catch (final ClassNotFoundException cnfe) {\n            throw new SConnectorException(cnfe);\n        }\n    }\n\n    @Override\n    public void connect() throws SConnectorException {\n        // Nothing to do\n    }\n\n    @Override\n    public void disconnect() throws SConnectorException {\n        // Nothing to do\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-connector-executor/src/test/resources/logback-test.xml",
    "content": "<configuration>\n\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->\n        <encoder>\n            <pattern>|%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <logger name=\"org.bonitasoft\" level=\"INFO\" />\n    <logger name=\"com.bonitasoft\" level=\"INFO\" />\n    <logger name=\"com.bonitasoft.engine.business.data.impl\" level=\"TRACE\" />\n\n    <root level=\"WARN\">\n        <appender-ref ref=\"STDOUT\" />\n    </root>\n</configuration>"
  },
  {
    "path": "services/bonita-data-definition/build.gradle",
    "content": "\n\ndependencies { api project(':services:bonita-expression') }\n"
  },
  {
    "path": "services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/SDataDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.definition.model;\n\nimport java.io.Serializable;\n\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Zhao Na\n * @author Matthieu Chaffotte\n */\npublic interface SDataDefinition extends Serializable {\n\n    String getName();\n\n    String getDescription();\n\n    String getClassName();\n\n    Boolean isTransientData();\n\n    SExpression getDefaultValueExpression();\n}\n"
  },
  {
    "path": "services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/STextDataDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.definition.model;\n\n/**\n * @author Zhao Na\n */\npublic interface STextDataDefinition extends SDataDefinition {\n\n    boolean isLongText();\n\n}\n"
  },
  {
    "path": "services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/SXMLDataDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.definition.model;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SXMLDataDefinition extends SDataDefinition {\n\n    String getNamespace();\n\n    String getElement();\n\n}\n"
  },
  {
    "path": "services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/builder/AbstractSDataDefinitionBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.definition.model.builder;\n\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Zhao Na\n * @author Matthieu Chaffotte\n */\npublic interface AbstractSDataDefinitionBuilder {\n\n    SDataDefinitionBuilder setName(String name);\n\n    SDataDefinitionBuilder setDescription(String description);\n\n    SDataDefinitionBuilder setTransient(boolean transientData);\n\n    SDataDefinitionBuilder setDefaultValue(SExpression expression);\n\n}\n"
  },
  {
    "path": "services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/builder/SDataDefinitionBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.definition.model.builder;\n\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\n\n/**\n * @author Zhao Na\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic interface SDataDefinitionBuilder extends AbstractSDataDefinitionBuilder {\n\n    SDataDefinitionBuilder setAsLongText(boolean value);\n\n    SDataDefinition done();\n\n}\n"
  },
  {
    "path": "services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/builder/SDataDefinitionBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.definition.model.builder;\n\n/**\n * @author Zhao Na\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic interface SDataDefinitionBuilderFactory {\n\n    SDataDefinitionBuilder createNewInstance(String name, String className);\n\n    SDataDefinitionBuilder createNewTextData(final String name);\n\n}\n"
  },
  {
    "path": "services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/builder/SEnumationDataDefinitionBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.definition.model.builder;\n\nimport java.util.List;\n\n/**\n * @author Zhao Na\n * @author Matthieu Chaffotte\n */\npublic interface SEnumationDataDefinitionBuilder extends AbstractSDataDefinitionBuilder {\n\n    SDataDefinitionBuilder enumarationValues(List<?> enumerationValues);\n\n}\n"
  },
  {
    "path": "services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/builder/SEnumationDataDefinitionBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.definition.model.builder;\n\n/**\n * @author Zhao Na\n * @author Matthieu Chaffotte\n */\npublic interface SEnumationDataDefinitionBuilderFactory extends AbstractSDataDefinitionBuilder {\n\n    SDataDefinitionBuilder createNewInstance(String name);\n\n}\n"
  },
  {
    "path": "services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/builder/STextDataDefinitionBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.definition.model.builder;\n\n/**\n * @author Zhao Na\n * @author Elias Ricken de Medeiros\n */\npublic interface STextDataDefinitionBuilder {\n\n    SDataDefinitionBuilder setAsLongText(boolean value);\n\n}\n"
  },
  {
    "path": "services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/builder/STextDataDefinitionBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.definition.model.builder;\n\n/**\n * @author Zhao Na\n * @author Elias Ricken de Medeiros\n */\npublic interface STextDataDefinitionBuilderFactory {\n\n    SDataDefinitionBuilder setAsLongText(boolean value);\n\n}\n"
  },
  {
    "path": "services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/builder/SXMLDataDefinitionBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.definition.model.builder;\n\nimport org.bonitasoft.engine.data.definition.model.SXMLDataDefinition;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Zhao Na\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic interface SXMLDataDefinitionBuilder {\n\n    SXMLDataDefinitionBuilder setDescription(String description);\n\n    SXMLDataDefinitionBuilder setTransient(boolean transientData);\n\n    SXMLDataDefinitionBuilder setDefaultValue(SExpression expression);\n\n    SXMLDataDefinitionBuilder setNamespace(final String namespace);\n\n    SXMLDataDefinitionBuilder setElement(final String element);\n\n    SXMLDataDefinition done();\n\n}\n"
  },
  {
    "path": "services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/builder/SXMLDataDefinitionBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.definition.model.builder;\n\n/**\n * @author Zhao Na\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic interface SXMLDataDefinitionBuilderFactory {\n\n    SXMLDataDefinitionBuilder createNewXMLData(final String name);\n\n}\n"
  },
  {
    "path": "services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/builder/impl/SDataDefinitionBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.definition.model.builder.impl;\n\nimport org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilder;\nimport org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilderFactory;\nimport org.bonitasoft.engine.data.definition.model.impl.SDataDefinitionImpl;\nimport org.bonitasoft.engine.data.definition.model.impl.STextDefinitionImpl;\n\n/**\n * @author Zhao Na\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic class SDataDefinitionBuilderFactoryImpl implements SDataDefinitionBuilderFactory {\n\n    @Override\n    public SDataDefinitionBuilder createNewTextData(final String name) {\n        final STextDefinitionImpl dataDefinitionImpl = new STextDefinitionImpl();\n        dataDefinitionImpl.setName(name);\n        dataDefinitionImpl.setClassName(String.class.getName());\n        return new SDataDefinitionBuilderImpl(dataDefinitionImpl);\n    }\n\n    @Override\n    public SDataDefinitionBuilder createNewInstance(final String name, final String className) {\n        final SDataDefinitionImpl dataDefinitionImpl = new SDataDefinitionImpl();\n        dataDefinitionImpl.setName(name);\n        dataDefinitionImpl.setClassName(className);\n        return new SDataDefinitionBuilderImpl(dataDefinitionImpl);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/builder/impl/SDataDefinitionBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.definition.model.builder.impl;\n\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\nimport org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilder;\nimport org.bonitasoft.engine.data.definition.model.impl.SDataDefinitionImpl;\nimport org.bonitasoft.engine.data.definition.model.impl.STextDefinitionImpl;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Zhao Na\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic class SDataDefinitionBuilderImpl implements SDataDefinitionBuilder {\n\n    private final SDataDefinitionImpl dataDefinitionImpl;\n\n    public SDataDefinitionBuilderImpl(final SDataDefinitionImpl dataDefinitionImpl) {\n        super();\n        this.dataDefinitionImpl = dataDefinitionImpl;\n    }\n\n    @Override\n    public SDataDefinition done() {\n        return dataDefinitionImpl;\n    }\n\n    public SDataDefinitionBuilder setAsLongText(final boolean value) {\n        if (dataDefinitionImpl instanceof STextDefinitionImpl\n                && dataDefinitionImpl.getClassName().equals(String.class.getName())) {\n            ((STextDefinitionImpl) dataDefinitionImpl).setIsLongText(value);\n        }\n        return this;\n    }\n\n    @Override\n    public SDataDefinitionBuilder setName(final String name) {\n        dataDefinitionImpl.setName(name);\n        return this;\n    }\n\n    @Override\n    public SDataDefinitionBuilder setDescription(final String description) {\n        dataDefinitionImpl.setDescription(description);\n        return this;\n    }\n\n    @Override\n    public SDataDefinitionBuilder setTransient(final boolean transientData) {\n        dataDefinitionImpl.setTransientData(transientData);\n        return this;\n    }\n\n    @Override\n    public SDataDefinitionBuilder setDefaultValue(final SExpression expression) {\n        dataDefinitionImpl.setDefaultValueExpression(expression);\n        return this;\n    }\n\n    public static SDataDefinitionBuilder getInstance() {\n        return new SDataDefinitionBuilderImpl(null);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/builder/impl/SXMLDataDefinitionBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.definition.model.builder.impl;\n\nimport org.bonitasoft.engine.data.definition.model.builder.SXMLDataDefinitionBuilder;\nimport org.bonitasoft.engine.data.definition.model.builder.SXMLDataDefinitionBuilderFactory;\nimport org.bonitasoft.engine.data.definition.model.impl.SXMLDataDefinitionImpl;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic class SXMLDataDefinitionBuilderFactoryImpl implements SXMLDataDefinitionBuilderFactory {\n\n    @Override\n    public SXMLDataDefinitionBuilder createNewXMLData(final String name) {\n        final SXMLDataDefinitionImpl dataDefinitionImpl = new SXMLDataDefinitionImpl();\n        dataDefinitionImpl.setName(name);\n        dataDefinitionImpl.setClassName(String.class.getName());\n        return new SXMLDataDefinitionBuilderImpl(dataDefinitionImpl);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/builder/impl/SXMLDataDefinitionBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.definition.model.builder.impl;\n\nimport org.bonitasoft.engine.data.definition.model.SXMLDataDefinition;\nimport org.bonitasoft.engine.data.definition.model.builder.SXMLDataDefinitionBuilder;\nimport org.bonitasoft.engine.data.definition.model.impl.SXMLDataDefinitionImpl;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic class SXMLDataDefinitionBuilderImpl implements SXMLDataDefinitionBuilder {\n\n    private final SXMLDataDefinitionImpl dataDefinitionImpl;\n\n    public SXMLDataDefinitionBuilderImpl(\n            SXMLDataDefinitionImpl dataDefinitionImpl) {\n        super();\n        this.dataDefinitionImpl = dataDefinitionImpl;\n    }\n\n    @Override\n    public SXMLDataDefinitionBuilder setDescription(final String description) {\n        dataDefinitionImpl.setDescription(description);\n        return this;\n    }\n\n    @Override\n    public SXMLDataDefinitionBuilder setTransient(final boolean transientData) {\n        dataDefinitionImpl.setTransientData(transientData);\n        return this;\n    }\n\n    @Override\n    public SXMLDataDefinitionBuilder setDefaultValue(final SExpression expression) {\n        dataDefinitionImpl.setDefaultValueExpression(expression);\n        return this;\n    }\n\n    @Override\n    public SXMLDataDefinitionBuilder setNamespace(final String namespace) {\n        dataDefinitionImpl.setNamespace(namespace);\n        return this;\n    }\n\n    @Override\n    public SXMLDataDefinitionBuilder setElement(final String element) {\n        dataDefinitionImpl.setElement(element);\n        return this;\n    }\n\n    @Override\n    public SXMLDataDefinition done() {\n        return dataDefinitionImpl;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/impl/SDataDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.definition.model.impl;\n\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Zhao Na\n * @author Matthieu Chaffotte\n */\npublic class SDataDefinitionImpl implements SDataDefinition {\n\n    private static final long serialVersionUID = 1L;\n\n    private String name;\n\n    private String description;\n\n    private boolean transientData;\n\n    private String className;\n\n    private SExpression defaultValueExpression;\n\n    public SDataDefinitionImpl() {\n        super();\n    }\n\n    @Override\n    public String getName() {\n        return name;\n    }\n\n    @Override\n    public String getDescription() {\n        return description;\n    }\n\n    @Override\n    public String getClassName() {\n        return className;\n    }\n\n    @Override\n    public Boolean isTransientData() {\n        return transientData;\n    }\n\n    @Override\n    public SExpression getDefaultValueExpression() {\n        return defaultValueExpression;\n    }\n\n    public void setTransientData(final boolean transientData) {\n        this.transientData = transientData;\n    }\n\n    public void setName(final String name) {\n        this.name = name;\n    }\n\n    public void setDescription(final String description) {\n        this.description = description;\n    }\n\n    public void setDefaultValueExpression(final SExpression defaultValueExpression) {\n        this.defaultValueExpression = defaultValueExpression;\n    }\n\n    public void setClassName(final String className) {\n        this.className = className;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/impl/STextDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.definition.model.impl;\n\nimport org.bonitasoft.engine.data.definition.model.STextDataDefinition;\n\n/**\n * @author Zhao Na\n */\npublic class STextDefinitionImpl extends SDataDefinitionImpl implements STextDataDefinition {\n\n    private static final long serialVersionUID = 1L;\n\n    private boolean isLongText;\n\n    @Override\n    public boolean isLongText() {\n        return isLongText;\n    }\n\n    public void setIsLongText(final boolean isLongText) {\n        this.isLongText = isLongText;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/impl/SXMLDataDefinitionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.definition.model.impl;\n\nimport org.bonitasoft.engine.data.definition.model.SXMLDataDefinition;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SXMLDataDefinitionImpl extends SDataDefinitionImpl implements SXMLDataDefinition {\n\n    private static final long serialVersionUID = 1L;\n\n    private String namespace;\n\n    private String element;\n\n    @Override\n    public String getNamespace() {\n        return namespace;\n    }\n\n    @Override\n    public String getElement() {\n        return element;\n    }\n\n    public void setNamespace(final String namespace) {\n        this.namespace = namespace;\n    }\n\n    public void setElement(final String element) {\n        this.element = element;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/build.gradle",
    "content": "\n\ndependencies {\n    api project(':services:bonita-expression')\n    api project(':services:bonita-builder')\n    api project(':services:bonita-persistence')\n    api project(':services:bonita-data-definition')\n    api project(':services:bonita-commons')\n    api project(':services:bonita-events')\n    api project(':services:bonita-log')\n    api project(':services:bonita-archive')\n    testImplementation libs.assertj\n    testImplementation libs.mockitoCore\n    testImplementation libs.logback\n    testImplementation libs.systemRules\n\n    annotationProcessor libs.lombok\n    compileOnly libs.lombok\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/api/DataContainer.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.api;\n\nimport java.util.Objects;\n\n/**\n * @author Baptiste Mesta\n */\npublic class DataContainer {\n\n    private long id;\n    private String type;\n\n    public DataContainer(long id, String type) {\n        this.id = id;\n        this.type = type;\n    }\n\n    public long getId() {\n        return id;\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (o == null || getClass() != o.getClass())\n            return false;\n        DataContainer that = (DataContainer) o;\n        return id == that.id &&\n                Objects.equals(type, that.type);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(id, type);\n    }\n\n    @Override\n    public String toString() {\n        return \"DataContainer{\" +\n                \"id=\" + id +\n                \", type='\" + type + '\\'' +\n                '}';\n    }\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/api/DataInstanceContainer.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.api;\n\n/**\n * @author Feng Hui\n * @author Celine Souchet\n */\npublic enum DataInstanceContainer {\n\n    PROCESS_INSTANCE,\n\n    ACTIVITY_INSTANCE,\n\n    MESSAGE_INSTANCE\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/api/DataInstanceService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.api;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.data.instance.exception.SDataInstanceException;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.SADataInstance;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Zhao Na\n * @author Elias Ricken de Medeiros\n * @author Feng Hui\n * @author Matthieu Chaffotte\n * @since 6.0\n */\npublic interface DataInstanceService {\n\n    // just insert dataInstance to DB\n    /**\n     * Create dataInstance in DB for given dataInstance\n     *\n     * @param dataInstance\n     *        SDataInstance object\n     * @throws SDataInstanceException\n     */\n    void createDataInstance(final SDataInstance dataInstance) throws SDataInstanceException;\n\n    /**\n     * Update the specific dataInstance according to the given descriptor\n     *\n     * @param dataInstance\n     *        SDataInstance object will be updated\n     * @param descriptor\n     *        Update description\n     * @throws SDataInstanceException\n     */\n    void updateDataInstance(final SDataInstance dataInstance, final EntityUpdateDescriptor descriptor)\n            throws SDataInstanceException;\n\n    /**\n     * Delete the specific dataInstance\n     *\n     * @param dataInstance\n     *        SDataInstance object will be deleted\n     * @throws SDataInstanceException\n     */\n    void deleteDataInstance(final SDataInstance dataInstance) throws SDataInstanceException;\n\n    /**\n     * Get dataInstance by its id\n     *\n     * @param dataInstanceId\n     *        Identifier of dataInstance\n     * @return a SDataInstance object\n     * @throws SDataInstanceException\n     */\n    SDataInstance getDataInstance(final long dataInstanceId) throws SDataInstanceException;\n\n    /**\n     * Get dataInstance visible in the specific container\n     *\n     * @param dataName\n     *        Name of data instance\n     * @param containerId\n     *        Identifier of container\n     * @param containerType\n     *        Type of container, e.g process instance, activity instance and so on.\n     * @return a SDataInstance object\n     * @throws SDataInstanceException\n     */\n    SDataInstance getDataInstance(final String dataName, final long containerId, final String containerType,\n            final ParentContainerResolver parentContainerResolver) throws SDataInstanceException;\n\n    /**\n     * Get dataInstances visible in the specific container for given names\n     *\n     * @param dataNames\n     *        A list of names of data instances\n     * @param containerId\n     *        Identifier of container\n     * @param containerType\n     *        Type of container, e.g process instance, activity instance and so on.\n     * @return a list of SDataInstance objects\n     * @throws SDataInstanceException\n     */\n    List<SDataInstance> getDataInstances(final List<String> dataNames, final long containerId,\n            final String containerType,\n            final ParentContainerResolver parentContainerResolver) throws SDataInstanceException;\n\n    /**\n     * Get all dataInstances visible in the specific container\n     *\n     * @param containerId\n     *        Identifier of container\n     * @param containerType\n     *        Type of container, e.g process instance, activity instance and so on.\n     * @return\n     * @throws SDataInstanceException\n     */\n    List<SDataInstance> getDataInstances(final long containerId, final String containerType,\n            final ParentContainerResolver parentContainerResolver, final int fromIndex, final int numberOfResults)\n            throws SDataInstanceException;\n\n    /**\n     * Get the local dataInstance by name in a certain container, the dataInstance is existed in this container\n     *\n     * @param dataName\n     *        Name of dataInstance\n     * @param containerId\n     *        Identifier of container\n     * @param containerType\n     *        Type of container, e.g process instance, activity instance and so on.\n     * @return an SDataInstance object\n     * @throws SDataInstanceException\n     */\n    SDataInstance getLocalDataInstance(String dataName, long containerId, String containerType)\n            throws SDataInstanceException;\n\n    /**\n     * Get a list of local dataInstances for the specific container, those dataInstances must belong to the specified\n     * container. This method is paginated.\n     *\n     * @param containerId\n     *        Identifier of container\n     * @param containerType\n     *        Type of container, e.g process instance, activity instance and so on.\n     * @return a list of SDataInstance objects\n     * @throws SDataInstanceException\n     */\n    List<SDataInstance> getLocalDataInstances(long containerId, String containerType, int fromIndex,\n            int numberOfResults) throws SDataInstanceException;\n\n    /**\n     * Get SADataInstance object for specific dataInstance at the specific time\n     *\n     * @param sourceObjectId\n     *        Identifier of data instance which has been archived\n     * @param time\n     *        The archive time\n     * @return an SADataInstance object\n     * @throws SDataInstanceException\n     */\n    SADataInstance getSADataInstance(long sourceObjectId, long time) throws SDataInstanceException;\n\n    /**\n     * Get SADataInstance object archived in the specific time for name specified dataInstance in a container\n     *\n     * @param containerId\n     *        Identifier of container\n     * @param containerType\n     *        Type of container, e.g process instance, activity instance and so on.\n     * @param dataName\n     *        Name of data instance\n     * @param time\n     *        The archive time\n     * @return an SADataInstance object\n     * @throws SDataInstanceException\n     */\n    SADataInstance getSADataInstance(long containerId, String containerType,\n            final ParentContainerResolver parentContainerResolver, String dataName, long time)\n            throws SDataInstanceException;\n\n    /**\n     * Get all SADataInstance objects archived after specific time for specific dataInstance in a container\n     *\n     * @param containerId\n     *        Identifier of container\n     * @param containerType\n     *        Type of container, e.g process instance, activity instance and so on.\n     * @param dataNames\n     *        Name of data\n     * @param time\n     *        The archive time\n     * @return a list of SADataInstance objects\n     * @throws SDataInstanceException\n     */\n    List<SADataInstance> getSADataInstances(long containerId, String containerType,\n            final ParentContainerResolver parentContainerResolver, List<String> dataNames, long time)\n            throws SDataInstanceException;\n\n    /**\n     * Get number of dataInstance for specified container\n     *\n     * @param containerId\n     *        Identifier of container\n     * @param containerType\n     *        Type of container, e.g process instance, activity instance and so on.\n     * @return the number of dataInstances\n     * @throws SDataInstanceException\n     */\n    long getNumberOfDataInstances(long containerId, String containerType,\n            final ParentContainerResolver parentContainerResolver) throws SDataInstanceException;\n\n    /**\n     * Gets the last archived SADataInstance object for the named data in the container.\n     *\n     * @param dataName\n     *        the name of the data\n     * @param containerId\n     *        the identifier of the container\n     * @param containerType\n     *        the type of the container\n     * @return the last archived SADataInstance\n     * @throws SDataInstanceException\n     */\n    SADataInstance getLastSADataInstance(String dataName, long containerId, String containerType,\n            final ParentContainerResolver parentContainerResolver) throws SDataInstanceException;\n\n    /**\n     * Gets the last archived SADataInstance objects of the container.\n     *\n     * @param containerId\n     *        the identifier of the container\n     * @param containerType\n     *        the type of the container\n     * @param startIndex\n     * @param maxResults\n     * @return the last archived SADataInstance\n     * @throws SDataInstanceException\n     */\n    List<SADataInstance> getLastLocalSADataInstances(long containerId, String containerType, int startIndex,\n            int maxResults) throws SDataInstanceException;\n\n    /**\n     * Get the local SADataInstances for this element\n     *\n     * @param containerId\n     * @param containerType\n     * @param fromIndex\n     * @param maxResults\n     * @return\n     * @throws SDataInstanceException\n     */\n    List<SADataInstance> getLocalSADataInstances(long containerId, String containerType, int fromIndex, int maxResults)\n            throws SDataInstanceException;\n\n    /**\n     * Delete all local archived data instances for a specified container\n     *\n     * @param containerId\n     * @param dataInstanceContainerType\n     * @throws SDataInstanceException\n     * @since 6.1\n     */\n    void deleteLocalArchivedDataInstances(long containerId, String dataInstanceContainerType)\n            throws SDataInstanceException;\n\n    /**\n     * Delete all local archived data instances for multiple containers having the same type\n     *\n     * @param containerIds containers\n     * @param dataInstanceContainerType type of the containers\n     * @throws SDataInstanceException\n     * @since 7.8\n     */\n    void deleteLocalArchivedDataInstances(List<Long> containerIds, String dataInstanceContainerType)\n            throws SDataInstanceException;\n\n    /**\n     * Delete all local active data instances for a specified container\n     *\n     * @param containerId\n     * @param dataInstanceContainerType\n     * @param dataPresent\n     * @throws SDataInstanceException\n     * @since 6.1\n     */\n    void deleteLocalDataInstances(long containerId, String dataInstanceContainerType, boolean dataPresent)\n            throws SDataInstanceException;\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/api/ParentContainerResolver.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.api;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectReadException;\n\npublic interface ParentContainerResolver {\n\n    List<DataContainer> getContainerHierarchy(final DataContainer currentContainer)\n            throws SObjectNotFoundException, SObjectReadException;\n\n    List<DataContainer> getArchivedContainerHierarchy(final DataContainer currentContainer)\n            throws SObjectNotFoundException, SObjectReadException;\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/api/impl/ArchivedDataInContainersComparator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.api.impl;\n\nimport java.util.Comparator;\nimport java.util.List;\n\nimport org.bonitasoft.engine.data.instance.api.DataContainer;\nimport org.bonitasoft.engine.data.instance.model.archive.SADataInstance;\n\n/**\n * The purpose of the comparator is to order data in a list starting with the most recent one in the 'closer' container\n * <p>\n * a container is closer than an other one if it is defined before in the container hierarchy.\n * <p>\n * we first order on containers then in a same containers 2 data are ordered using their archive dates.\n * <p>\n * if sorted using this comparator, a list will have as its first element the most recent data in the highest container.\n * This is useful in order to get the last archive version of a data in a current context.\n *\n * @author Baptiste Mesta\n */\nclass ArchivedDataInContainersComparator implements Comparator<SADataInstance> {\n\n    private final List<DataContainer> containerHierarchy;\n\n    ArchivedDataInContainersComparator(List<DataContainer> containerHierarchy) {\n        this.containerHierarchy = containerHierarchy;\n    }\n\n    @Override\n    public int compare(SADataInstance data1, SADataInstance data2) {\n        final DataContainer data1Container = new DataContainer(data1.getContainerId(), data1.getContainerType());\n        final DataContainer data2Container = new DataContainer(data2.getContainerId(), data2.getContainerType());\n        if (areInTheSameContainer(data1Container, data2Container)) {\n            return compareUsingArchiveDateInverted(data1, data2);\n        }\n        return compareUsingContainersOrder(data1Container, data2Container);\n    }\n\n    private int compareUsingContainersOrder(DataContainer o1Container, DataContainer o2Container) {\n        return Integer.compare(containerHierarchy.indexOf(o1Container), containerHierarchy.indexOf(o2Container));\n    }\n\n    private int compareUsingArchiveDateInverted(SADataInstance o1, SADataInstance o2) {\n        return Long.compare(o2.getArchiveDate(), o1.getArchiveDate());\n    }\n\n    private boolean areInTheSameContainer(DataContainer o1Container, DataContainer o2Container) {\n        return containerHierarchy.indexOf(o1Container) == containerHierarchy.indexOf(o2Container);\n    }\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/api/impl/DataInContainersComparator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.api.impl;\n\nimport java.util.Comparator;\nimport java.util.List;\n\nimport org.bonitasoft.engine.data.instance.api.DataContainer;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\n\n/**\n * The purpose of the comparator is to order data in a list starting with the one in the 'closer' container\n * <p>\n * a container is closer than an other one if it is defined before in the container hierarchy.\n * <p>\n * if sorted using this comparator, a list will have as its first element the data in the closer container.\n * This is useful in order to get the data in a current context with scope shadowing.\n *\n * @author Baptiste Mesta\n */\nclass DataInContainersComparator implements Comparator<SDataInstance> {\n\n    private final List<DataContainer> containerHierarchy;\n\n    public DataInContainersComparator(List<DataContainer> containerHierarchy) {\n        this.containerHierarchy = containerHierarchy;\n    }\n\n    @Override\n    public int compare(SDataInstance o1, SDataInstance o2) {\n        final DataContainer o1Container = new DataContainer(o1.getContainerId(), o1.getContainerType());\n        final DataContainer o2Container = new DataContainer(o2.getContainerId(), o2.getContainerType());\n        return containerHierarchy.indexOf(o1Container) - containerHierarchy.indexOf(o2Container);\n    }\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/api/impl/DataInstanceServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.api.impl;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.archive.ArchiveInsertRecord;\nimport org.bonitasoft.engine.archive.ArchiveService;\nimport org.bonitasoft.engine.commons.CollectionUtil;\nimport org.bonitasoft.engine.commons.LogUtil;\nimport org.bonitasoft.engine.commons.NullCheckingUtil;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectReadException;\nimport org.bonitasoft.engine.data.instance.api.DataContainer;\nimport org.bonitasoft.engine.data.instance.api.DataInstanceService;\nimport org.bonitasoft.engine.data.instance.api.ParentContainerResolver;\nimport org.bonitasoft.engine.data.instance.exception.SCreateDataInstanceException;\nimport org.bonitasoft.engine.data.instance.exception.SDataInstanceException;\nimport org.bonitasoft.engine.data.instance.exception.SDataInstanceNotFoundException;\nimport org.bonitasoft.engine.data.instance.exception.SDataInstanceReadException;\nimport org.bonitasoft.engine.data.instance.exception.SDeleteDataInstanceException;\nimport org.bonitasoft.engine.data.instance.exception.SUpdateDataInstanceException;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.SADataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.builder.SADataInstanceBuilder;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\n\n/**\n * General mechanism for lookup is to look in specific flow node to search a data instance. When referring to \"local\"\n * data instance, it means the lookup is\n * performed only on the specific element, and not on inherited data for parent containers.\n *\n * @author Zhao Na\n * @author Elias Ricken de Medeiros\n * @author Feng Hui\n * @author Hongwen Zang\n * @author Matthieu Chaffotte\n * @author Baptiste Mesta: include data instance data source directly here\n */\n@Slf4j\npublic class DataInstanceServiceImpl implements DataInstanceService {\n\n    private static final String DATA_INSTANCE = \"DATA_INSTANCE\";\n\n    protected final Recorder recorder;\n\n    protected final ReadPersistenceService persistenceService;\n\n    protected final ArchiveService archiveService;\n\n    public DataInstanceServiceImpl(final Recorder recorder, final ReadPersistenceService persistenceService,\n            final ArchiveService archiveService) {\n        this.recorder = recorder;\n        this.persistenceService = persistenceService;\n        this.archiveService = archiveService;\n    }\n\n    private void archiveDataInstance(final SDataInstance sDataInstance) throws SDataInstanceException {\n        if (!sDataInstance.isTransientData()) {\n            try {\n                final SADataInstance saDataInstance = new SADataInstanceBuilder().createNewInstance(sDataInstance);\n                final ArchiveInsertRecord archiveInsertRecord = new ArchiveInsertRecord(saDataInstance);\n                archiveService.recordInsert(System.currentTimeMillis(), archiveInsertRecord);\n            } catch (final SRecorderException e) {\n                logOnExceptionMethod(\"updateDataInstance\", e);\n                throw new SDataInstanceException(\"Unable to create SADataInstance\", e);\n            }\n        }\n    }\n\n    @Override\n    public SDataInstance getDataInstance(final String dataName, final long containerId, final String containerType,\n            final ParentContainerResolver parentContainerResolver) throws SDataInstanceException {\n        NullCheckingUtil.checkArgsNotNull(dataName, containerType);\n\n        final String queryName = \"getDataInstancesWithNames\";\n        final Map<String, Object> inputParameters = new HashMap<String, Object>();\n        inputParameters.put(\"dataNames\", Collections.singletonList(dataName));\n        final List<SDataInstance> dataInstances = getSDatainstanceOfContainers(containerId, containerType,\n                parentContainerResolver, queryName, inputParameters);\n        if (dataInstances.size() == 0) {\n            throw new SDataInstanceNotFoundException(\n                    \"DataInstance with name not found: [name: \" + dataName + \", container type: \" + containerType\n                            + \", container id: \" + containerId + ']');\n        } else if (dataInstances.size() > 1) {\n            //should never happen but in case...\n            throw new SDataInstanceReadException(\n                    \"Several data have been retrieved for: [name: \" + dataName + \", container type: \" + containerType\n                            + \", container id: \" + containerId + ']');\n        } else {\n            return dataInstances.get(0);\n        }\n    }\n\n    @Override\n    public List<SDataInstance> getDataInstances(final long containerId, final String containerType,\n            final ParentContainerResolver parentContainerResolver, final int fromIndex, final int numberOfResults)\n            throws SDataInstanceException {\n        NullCheckingUtil.checkArgsNotNull(containerType);\n\n        final String queryName = \"getDataInstances\";\n        final Map<String, Object> inputParameters = new HashMap<String, Object>();\n        final List<SDataInstance> dataInstances = getSDatainstanceOfContainers(containerId, containerType,\n                parentContainerResolver, queryName, inputParameters);\n\n        //apply pagination here because we could not do the request only in database cause of a post data's processing to remove duplicate variable\n        final int startIndex = Math.max(0, fromIndex);\n        final int toIndex = Math.min(dataInstances.size(), fromIndex + numberOfResults);\n\n        if (toIndex > startIndex) {\n            return dataInstances.subList(startIndex, toIndex);\n        }\n        return Collections.emptyList();\n    }\n\n    private Map<String, List<Long>> buildContainersMap(final List<DataContainer> containerHierarchy,\n            final Map<String, Object> inputParameters) {\n        final Map<String, List<Long>> containers = new HashMap<String, List<Long>>();\n        for (DataContainer container : containerHierarchy) {\n            final String containerTypeKey = container.getType();\n            if (!containers.containsKey(containerTypeKey)) {\n                containers.put(containerTypeKey, new ArrayList<Long>());\n                inputParameters.put(\"containerType\" + containers.size(), containerTypeKey);\n                inputParameters.put(\"containerType\" + containers.size() + \"Ids\", containers.get(containerTypeKey));\n            }\n            containers.get(containerTypeKey).add(container.getId());\n        }\n        return containers;\n    }\n\n    private List<SDataInstance> getSDatainstanceOfContainers(long containerId, String containerType,\n            ParentContainerResolver parentContainerResolver,\n            String queryName, Map<String, Object> inputParameters)\n            throws SDataInstanceNotFoundException, SDataInstanceReadException {\n        //getAllContainers from me to root\n        final List<DataContainer> containerHierarchy;\n        try {\n            containerHierarchy = parentContainerResolver\n                    .getContainerHierarchy(new DataContainer(containerId, containerType));\n        } catch (SObjectNotFoundException | SObjectReadException e) {\n            throw new SDataInstanceNotFoundException(e);\n        }\n\n        final Map<String, List<Long>> containers = buildContainersMap(containerHierarchy, inputParameters);\n\n        //get all data of any possible containers\n        List<SDataInstance> dataInstances;\n        try {\n            dataInstances = persistenceService.selectList(\n                    new SelectListDescriptor<SDataInstance>(getDynamicContainersQueryName(queryName, containers.size()),\n                            inputParameters, SDataInstance.class,\n                            new QueryOptions(0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS)));\n        } catch (final SBonitaReadException e) {\n            throw new SDataInstanceReadException(\"Unable to check if a data instance already exists: \" + e.getMessage(),\n                    e);\n        }\n\n        //order the retrieved list by container level\n        Collections.sort(dataInstances, new DataInContainersComparator(containerHierarchy));\n\n        //remove duplicates\n        Set<String> alreadyUsedNames = new HashSet<String>();\n        final Iterator<SDataInstance> it = dataInstances.iterator();\n        while (it.hasNext()) {\n            SDataInstance current = it.next();\n            if (alreadyUsedNames.contains(current.getName())) {\n                it.remove();\n            } else {\n                alreadyUsedNames.add(current.getName());\n            }\n        }\n        return dataInstances;\n    }\n\n    @Override\n    public SDataInstance getLocalDataInstance(final String dataName, final long containerId, final String containerType)\n            throws SDataInstanceReadException {\n        final SDataInstance dataInstance = internalGetLocalDataInstance(dataName, containerId, containerType);\n        if (dataInstance == null) {\n            throw new SDataInstanceReadException(\"No data instance found\");\n        }\n        return dataInstance;\n    }\n\n    private SDataInstance internalGetLocalDataInstance(final String dataName, final long containerId,\n            final String containerType)\n            throws SDataInstanceReadException {\n        NullCheckingUtil.checkArgsNotNull(dataName, containerType);\n        final Map<String, Object> paraMap = CollectionUtil.buildSimpleMap(SDataInstance.NAME, dataName);\n        paraMap.put(SDataInstance.CONTAINER_ID, containerId);\n        paraMap.put(SDataInstance.CONTAINER_TYPE, containerType);\n\n        try {\n            return persistenceService\n                    .selectOne(new SelectOneDescriptor<SDataInstance>(\"getDataInstancesByNameAndContainer\",\n                            paraMap,\n                            SDataInstance.class, SDataInstance.class));\n        } catch (final SBonitaReadException e) {\n            throw new SDataInstanceReadException(\"Unable to check if a data instance already exists: \" + e.getMessage(),\n                    e);\n        }\n    }\n\n    @Override\n    public List<SDataInstance> getLocalDataInstances(final long containerId, final String containerType,\n            final int fromIndex, final int numberOfResults)\n            throws SDataInstanceReadException {\n        NullCheckingUtil.checkArgsNotNull(containerType);\n        final Map<String, Object> paraMap = CollectionUtil.buildSimpleMap(SDataInstance.CONTAINER_ID, containerId);\n        final OrderByOption orderByOption = new OrderByOption(SDataInstance.class, SDataInstance.ID, OrderByType.ASC);\n        paraMap.put(SDataInstance.CONTAINER_TYPE, containerType);\n\n        try {\n            return persistenceService.selectList(new SelectListDescriptor<SDataInstance>(\"getDataInstancesByContainer\",\n                    paraMap, SDataInstance.class,\n                    SDataInstance.class, new QueryOptions(fromIndex, numberOfResults, Arrays.asList(orderByOption))));\n        } catch (final SBonitaReadException e) {\n            throw new SDataInstanceReadException(\n                    \"Unable to check if a data instance already exists for the data container of type \" + containerType\n                            + \" with id \"\n                            + containerId + \" for reason: \" + e.getMessage(),\n                    e);\n        }\n    }\n\n    private List<SADataInstance> getSADatainstanceOfContainers(long containerId, String containerType,\n            ParentContainerResolver parentContainerResolver,\n            String queryName, Map<String, Object> inputParameters)\n            throws SDataInstanceReadException, SObjectReadException, SObjectNotFoundException, SBonitaReadException {\n        //getAllContainers from me to root\n        final List<DataContainer> containerHierarchy;\n        containerHierarchy = parentContainerResolver\n                .getArchivedContainerHierarchy(new DataContainer(containerId, containerType));\n        final Map<String, List<Long>> containers = buildContainersMap(containerHierarchy, inputParameters);\n        //get all data of any of th possible containers\n        List<SADataInstance> dataInstances;\n        dataInstances = persistenceService.selectList(new SelectListDescriptor<SADataInstance>(\n                getDynamicContainersQueryName(queryName, containers.size()),\n                inputParameters, SADataInstance.class, new QueryOptions(0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS)));\n\n        //order the retrieved list by container level and by archive date\n        Collections.sort(dataInstances, new ArchivedDataInContainersComparator(containerHierarchy));\n        //remove duplicates\n        Set<String> alreadyUsedNames = new HashSet<>();\n        final Iterator<SADataInstance> it = dataInstances.iterator();\n        while (it.hasNext()) {\n            SADataInstance current = it.next();\n            if (alreadyUsedNames.contains(current.getName())) {\n                it.remove();\n            } else {\n                alreadyUsedNames.add(current.getName());\n            }\n        }\n        return dataInstances;\n    }\n\n    private String getDynamicContainersQueryName(String queryName, long nbOfContainers) {\n        return queryName + \"Of\" + nbOfContainers + \"Containers\";\n    }\n\n    @Override\n    public SADataInstance getSADataInstance(final long containerId, final String containerType,\n            final ParentContainerResolver parentContainerResolver, final String dataName, final long time)\n            throws SDataInstanceReadException {\n        logBeforeMethod(\"getSADataInstance\");\n        final String queryName = \"getArchivedDataInstancesWithNames\";\n        final Map<String, Object> inputParameters = new HashMap<>();\n        inputParameters.put(\"dataNames\", Collections.singletonList(dataName));\n        inputParameters.put(\"time\", time);\n        final List<SADataInstance> dataInstances;\n        try {\n            dataInstances = getSADatainstanceOfContainers(containerId, containerType, parentContainerResolver,\n                    queryName, inputParameters);\n        } catch (SObjectReadException | SObjectNotFoundException | SBonitaReadException e) {\n            throw new SDataInstanceReadException(e);\n        }\n        if (dataInstances.size() == 0) {\n            throw new SDataInstanceReadException(\n                    \"DataInstance with name not found: [name: \" + dataName + \", container type: \" + containerType\n                            + \", container id: \" + containerId + ']');\n        } else {\n            return dataInstances.get(0);\n        }\n    }\n\n    @Override\n    public SADataInstance getSADataInstance(final long sourceObjectId, final long time)\n            throws SDataInstanceReadException {\n        logBeforeMethod(\"getSADataInstance\");\n        try {\n            final ReadPersistenceService readPersistenceService = archiveService\n                    .getDefinitiveArchiveReadPersistenceService();\n            final Map<String, Object> parameters = new HashMap<String, Object>(2);\n            parameters.put(\"dataInstanceId\", sourceObjectId);\n            parameters.put(\"time\", time);\n            final SADataInstance saDataInstance = readPersistenceService\n                    .selectOne(new SelectOneDescriptor<SADataInstance>(\n                            \"getSADataInstanceByDataInstanceIdAndArchiveDate\", parameters, SADataInstance.class));\n            logAfterMethod(\"getSADataInstance\");\n            return saDataInstance;\n        } catch (final SBonitaReadException e) {\n            logOnExceptionMethod(\"getSADataInstance\", e);\n            throw new SDataInstanceReadException(\"Unable to read SADataInstance\", e);\n        }\n    }\n\n    @Override\n    public SADataInstance getLastSADataInstance(final String dataName, final long containerId,\n            final String containerType,\n            final ParentContainerResolver parentContainerResolver) throws SDataInstanceException {\n        logBeforeMethod(\"getLastSADataInstance\");\n        SADataInstance saDataInstance = getSADataInstance(containerId, containerType, parentContainerResolver, dataName,\n                System.currentTimeMillis());\n        if (saDataInstance == null) {\n            throw new SDataInstanceNotFoundException(\"No archived data instance found for data:\" + dataName\n                    + \" in container: \" + containerType + \" \" + containerId);\n        }\n        return saDataInstance;\n    }\n\n    @Override\n    public List<SADataInstance> getSADataInstances(final long containerId, final String containerType,\n            final ParentContainerResolver parentContainerResolver, final List<String> dataNames, final long time)\n            throws SDataInstanceReadException {\n        logBeforeMethod(\"getSADataInstances\");\n        if (dataNames.isEmpty()) {\n            return Collections.emptyList();\n        }\n        final String queryName = \"getArchivedDataInstancesWithNames\";\n        final Map<String, Object> inputParameters = new HashMap<String, Object>();\n        inputParameters.put(\"time\", time);\n        inputParameters.put(\"dataNames\", dataNames);\n        try {\n            return getSADatainstanceOfContainers(containerId, containerType, parentContainerResolver, queryName,\n                    inputParameters);\n        } catch (SObjectReadException | SObjectNotFoundException | SBonitaReadException e) {\n            throw new SDataInstanceReadException(e);\n        }\n    }\n\n    @Override\n    public List<SADataInstance> getLastLocalSADataInstances(final long containerId, final String containerType,\n            final int startIndex, final int maxResults)\n            throws SDataInstanceReadException {\n        logBeforeMethod(\"getLastLocalSADataInstances\");\n        try {\n            final ReadPersistenceService readPersistenceService = archiveService\n                    .getDefinitiveArchiveReadPersistenceService();\n            final Map<String, Object> parameters = new HashMap<String, Object>(2);\n            parameters.put(\"containerId\", containerId);\n            parameters.put(\"containerType\", containerType);\n            final List<SADataInstance> saDataInstances = readPersistenceService\n                    .selectList(new SelectListDescriptor<SADataInstance>(\n                            \"getLastLocalSADataInstances\", parameters, SADataInstance.class,\n                            new QueryOptions(startIndex, maxResults)));\n            logAfterMethod(\"getLastLocalSADataInstances\");\n            return saDataInstances;\n        } catch (final SBonitaReadException e) {\n            logOnExceptionMethod(\"getLastLocalSADataInstances\", e);\n            throw new SDataInstanceReadException(\"Unable to read SADataInstance\", e);\n        }\n    }\n\n    @Override\n    public long getNumberOfDataInstances(final long containerId, final String containerType,\n            final ParentContainerResolver parentContainerResolver) throws SDataInstanceReadException {\n        logBeforeMethod(\"getNumberOfDataInstances\");\n        final List<SDataInstance> dataInstances;\n        try {\n            dataInstances = getDataInstances(containerId, containerType, parentContainerResolver, 0,\n                    QueryOptions.UNLIMITED_NUMBER_OF_RESULTS);\n        } catch (SDataInstanceException e) {\n            throw new SDataInstanceReadException(e);\n        }\n        logAfterMethod(\"getNumberOfDataInstances\");\n        return dataInstances.size();\n    }\n\n    @Override\n    public List<SDataInstance> getDataInstances(final List<String> dataNames, final long containerId,\n            final String containerType,\n            final ParentContainerResolver parentContainerResolver)\n            throws SDataInstanceException {\n        logBeforeMethod(\"getDataInstances\");\n        NullCheckingUtil.checkArgsNotNull(dataNames, containerType);\n        if (dataNames.isEmpty()) {\n            return Collections.emptyList();\n        }\n\n        final String queryName = \"getDataInstancesWithNames\";\n        final Map<String, Object> inputParameters = new HashMap<String, Object>();\n        inputParameters.put(\"dataNames\", dataNames);\n        return getSDatainstanceOfContainers(containerId, containerType, parentContainerResolver, queryName,\n                inputParameters);\n    }\n\n    @Override\n    public List<SADataInstance> getLocalSADataInstances(final long containerId, final String containerType,\n            final int fromIndex, final int numberOfResults)\n            throws SDataInstanceReadException {\n        logBeforeMethod(\"getLocalSADataInstances\");\n        try {\n            final ReadPersistenceService readPersistenceService = archiveService\n                    .getDefinitiveArchiveReadPersistenceService();\n            final Map<String, Object> parameters = new HashMap<String, Object>(2);\n            parameters.put(\"containerId\", containerId);\n            parameters.put(\"containerType\", containerType);\n            final List<SADataInstance> saDataInstances = readPersistenceService\n                    .selectList(new SelectListDescriptor<SADataInstance>(\"getLocalSADataInstances\",\n                            parameters, SADataInstance.class, new QueryOptions(fromIndex, numberOfResults)));\n            logAfterMethod(\"getLocalSADataInstances\");\n            return saDataInstances;\n        } catch (final SBonitaReadException e) {\n            logOnExceptionMethod(\"getLocalSADataInstances\", e);\n            throw new SDataInstanceReadException(\"Unable to read SADataInstance\", e);\n        }\n    }\n\n    @Override\n    public void deleteLocalArchivedDataInstances(final long containerId, final String containerType)\n            throws SDataInstanceException {\n\n        HashMap<String, Object> map = new HashMap<>();\n        map.put(\"containerId\", containerId);\n        map.put(\"containerType\", containerType);\n        try {\n            archiveService.deleteFromQuery(\"deleteLocalSADataInstances\", map);\n        } catch (SRecorderException e) {\n            throw new SDataInstanceException(e);\n        }\n    }\n\n    @Override\n    public void deleteLocalArchivedDataInstances(List<Long> containerIds, final String containerType)\n            throws SDataInstanceException {\n        HashMap<String, Object> map = new HashMap<>();\n        map.put(\"containerIds\", containerIds);\n        map.put(\"containerType\", containerType);\n        try {\n            archiveService.deleteFromQuery(\"deleteLocalSADataInstancesOfContainers\", map);\n        } catch (SRecorderException e) {\n            throw new SDataInstanceException(e);\n        }\n    }\n\n    @Override\n    public void deleteLocalDataInstances(final long containerId, final String dataInstanceContainerType,\n            final boolean dataPresent)\n            throws SDataInstanceException {\n        if (dataPresent) {\n            final int deleteBatchSize = 80;\n            List<SDataInstance> sDataInstances = getLocalDataInstances(containerId, dataInstanceContainerType, 0,\n                    deleteBatchSize);\n            while (sDataInstances.size() > 0) {\n                for (final SDataInstance sDataInstance : sDataInstances) {\n                    deleteDataInstance(sDataInstance);\n                }\n                sDataInstances = getLocalDataInstances(containerId, dataInstanceContainerType, 0, deleteBatchSize);\n            }\n        }\n    }\n\n    private void logBeforeMethod(final String methodName) {\n        if (log.isTraceEnabled()) {\n            log.trace(LogUtil.getLogBeforeMethod(this.getClass(), methodName));\n        }\n    }\n\n    private void logAfterMethod(final String methodName) {\n        if (log.isTraceEnabled()) {\n            log.trace(LogUtil.getLogAfterMethod(this.getClass(), methodName));\n        }\n    }\n\n    private void logOnExceptionMethod(final String methodName, final Exception e) {\n        if (log.isTraceEnabled()) {\n            log.trace(LogUtil.getLogOnExceptionMethod(this.getClass(), methodName, e));\n        }\n    }\n\n    @Override\n    public void createDataInstance(final SDataInstance dataInstance) throws SDataInstanceException {\n        try {\n            recorder.recordInsert(new InsertRecord(dataInstance), DATA_INSTANCE);\n        } catch (final SRecorderException e) {\n            throw new SCreateDataInstanceException(\"Impossible to create data instance.\", e);\n        }\n        archiveDataInstance(dataInstance);\n    }\n\n    @Override\n    public void updateDataInstance(final SDataInstance dataInstance, final EntityUpdateDescriptor descriptor)\n            throws SDataInstanceException {\n        NullCheckingUtil.checkArgsNotNull(dataInstance);\n        try {\n            recorder.recordUpdate(UpdateRecord.buildSetFields(dataInstance, descriptor), DATA_INSTANCE);\n        } catch (final SRecorderException e) {\n            throw new SUpdateDataInstanceException(\n                    \"Impossible to update data instance '\" + dataInstance.getName() + \"': \" + e.getMessage(), e);\n        }\n        archiveDataInstance(dataInstance);\n    }\n\n    @Override\n    public void deleteDataInstance(final SDataInstance dataInstance) throws SDataInstanceException {\n        NullCheckingUtil.checkArgsNotNull(dataInstance);\n        try {\n            recorder.recordDelete(new DeleteRecord(dataInstance), DATA_INSTANCE);\n        } catch (final SRecorderException e) {\n            throw new SDeleteDataInstanceException(\"Impossible to delete data instance\", e);\n        }\n    }\n\n    @Override\n    public SDataInstance getDataInstance(final long dataInstanceId) throws SDataInstanceException {\n        NullCheckingUtil.checkArgsNotNull(dataInstanceId);\n        try {\n            final SelectByIdDescriptor<SDataInstance> selectDescriptor = new SelectByIdDescriptor<SDataInstance>(\n                    SDataInstance.class,\n                    dataInstanceId);\n            final SDataInstance dataInstance = persistenceService.selectById(selectDescriptor);\n            if (dataInstance == null) {\n                throw new SDataInstanceNotFoundException(\"Cannot get the data instance with id \" + dataInstanceId);\n            }\n            return dataInstance;\n        } catch (final SBonitaReadException e) {\n            throw new SDataInstanceReadException(\"Cannot get the data instance with id \" + dataInstanceId, e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/exception/SCreateDataInstanceException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.exception;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SCreateDataInstanceException extends SDataInstanceException {\n\n    private static final long serialVersionUID = 3429369002565466683L;\n\n    public SCreateDataInstanceException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SCreateDataInstanceException(final String message) {\n        super(message);\n    }\n\n    public SCreateDataInstanceException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/exception/SDataInstanceException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.exception;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SDataInstanceException extends SBonitaException {\n\n    private static final long serialVersionUID = -4274958205096504600L;\n\n    public SDataInstanceException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SDataInstanceException(final String message) {\n        super(message);\n    }\n\n    public SDataInstanceException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/exception/SDataInstanceNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.exception;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SDataInstanceNotFoundException extends SDataInstanceException {\n\n    private static final long serialVersionUID = 8088523217807814937L;\n\n    public SDataInstanceNotFoundException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SDataInstanceNotFoundException(final String message) {\n        super(message);\n    }\n\n    public SDataInstanceNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/exception/SDataInstanceReadException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.exception;\n\n/**\n * @author Celine Souchet\n */\npublic class SDataInstanceReadException extends SDataInstanceException {\n\n    private static final long serialVersionUID = -4274958205096504600L;\n\n    public SDataInstanceReadException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SDataInstanceReadException(final String message) {\n        super(message);\n    }\n\n    public SDataInstanceReadException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/exception/SDeleteDataInstanceException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.exception;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SDeleteDataInstanceException extends SDataInstanceException {\n\n    private static final long serialVersionUID = 4352318021179590101L;\n\n    public SDeleteDataInstanceException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SDeleteDataInstanceException(final String message) {\n        super(message);\n    }\n\n    public SDeleteDataInstanceException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/exception/SUpdateDataInstanceException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.exception;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SUpdateDataInstanceException extends SDataInstanceException {\n\n    private static final long serialVersionUID = 5535287890003619850L;\n\n    public SUpdateDataInstanceException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SUpdateDataInstanceException(final String message) {\n        super(message);\n    }\n\n    public SUpdateDataInstanceException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/SBlobDataInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model;\n\nimport java.io.Serializable;\n\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\nimport org.hibernate.annotations.Type;\n\n/**\n * @author Zhao Na\n * @author Matthieu Chaffotte\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@SuperBuilder\n@Entity\n@DiscriminatorValue(\"SBlobDataInstanceImpl\")\npublic final class SBlobDataInstance extends SDataInstance {\n\n    @Column(name = \"blobValue\")\n    @Type(type = \"materialized_blob\")\n    private byte[] value;\n\n    public SBlobDataInstance(final SDataDefinition dataDefinition) {\n        super(dataDefinition);\n    }\n\n    @Override\n    public void setValue(final Serializable value) {\n        this.value = (byte[]) value;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/SBooleanDataInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model;\n\nimport java.io.Serializable;\n\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\n\n/**\n * @author Zhao Na\n * @author Matthieu Chaffotte\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@SuperBuilder\n@Entity\n@DiscriminatorValue(\"SBooleanDataInstanceImpl\")\npublic class SBooleanDataInstance extends SDataInstance {\n\n    @Column(name = \"booleanValue\")\n    private Boolean value;\n\n    public SBooleanDataInstance(final SDataDefinition dataDefinition) {\n        super(dataDefinition);\n    }\n\n    @Override\n    public void setValue(final Serializable value) {\n        this.value = (Boolean) value;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/SDataInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model;\n\nimport java.io.Serializable;\n\nimport javax.persistence.DiscriminatorColumn;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Inheritance;\nimport javax.persistence.InheritanceType;\nimport javax.persistence.Table;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\nimport org.bonitasoft.engine.data.instance.model.exceptions.SDataInstanceNotWellFormedException;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n@Data\n@NoArgsConstructor\n@SuperBuilder\n@Entity\n@Inheritance(strategy = InheritanceType.SINGLE_TABLE)\n@DiscriminatorColumn(name = \"DISCRIMINANT\")\n@Table(name = \"data_instance\")\npublic abstract class SDataInstance implements PersistentObject {\n\n    public static final String ID = \"id\";\n    public static final String NAME = \"name\";\n    public static final String DESCRIPTION = \"description\";\n    public static final String VALUE = \"value\";\n    public static final String CONTAINER_ID = \"containerId\";\n    public static final String CONTAINER_TYPE = \"containerType\";\n    @Id\n    private long id;\n    private String name;\n    private String description;\n    private boolean transientData;\n    private String className;\n    private long containerId;\n    private String containerType;\n\n    protected SDataInstance(final SDataDefinition dataDefinition) {\n        name = dataDefinition.getName();\n        description = dataDefinition.getDescription();\n        transientData = dataDefinition.isTransientData();\n        className = dataDefinition.getClassName();\n    }\n\n    public abstract void setValue(Serializable value);\n\n    public abstract Serializable getValue();\n\n    public Boolean isTransientData() {\n        return transientData;\n    }\n\n    public void setDataTypeClassName(final String className) {\n        this.className = className;\n    }\n\n    /**\n     * Check if the data is well formed\n     *\n     * @throws org.bonitasoft.engine.data.instance.model.exceptions.SDataInstanceNotWellFormedException\n     *         thrown if the data is not well formed\n     */\n    public void validate() throws SDataInstanceNotWellFormedException {\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/SDataInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\nimport org.bonitasoft.engine.data.definition.model.STextDataDefinition;\nimport org.bonitasoft.engine.data.definition.model.SXMLDataDefinition;\nimport org.bonitasoft.engine.data.instance.model.exceptions.SDataInstanceNotWellFormedException;\n\n/**\n * @author Zhao Na\n */\npublic class SDataInstanceBuilder {\n\n    private final SDataInstance dataInstance;\n\n    public static SDataInstanceBuilder createNewInstance(final SDataDefinition dataDefinition) {\n        final String className = dataDefinition.getClassName();\n        SDataInstance dataInstance;\n        if (dataDefinition instanceof STextDataDefinition) {\n            dataInstance = getTextDataInstance((STextDataDefinition) dataDefinition);\n        } else if (dataDefinition instanceof SXMLDataDefinition) {\n            dataInstance = new SXMLDataInstance((SXMLDataDefinition) dataDefinition);\n        } else {\n            if (Integer.class.getName().equals(className)) {\n                dataInstance = new SIntegerDataInstance(dataDefinition);\n            } else if (Long.class.getName().equals(className)) {\n                dataInstance = new SLongDataInstance(dataDefinition);\n            } else if (String.class.getName().equals(className)) {\n                dataInstance = new SShortTextDataInstance(dataDefinition);\n            } else if (Boolean.class.getName().equals(className)) {\n                dataInstance = new SBooleanDataInstance(dataDefinition);\n            } else if (Double.class.getName().equals(className)) {\n                dataInstance = new SDoubleDataInstance(dataDefinition);\n            } else if (Float.class.getName().equals(className)) {\n                dataInstance = new SFloatDataInstance(dataDefinition);\n            } else if (byte[].class.getName().equals(className)) {\n                dataInstance = new SBlobDataInstance(dataDefinition);\n            } else if (Date.class.getName().equals(className)) {\n                dataInstance = new SDateDataInstance(dataDefinition);\n            } else {\n                dataInstance = new SXMLObjectDataInstance(dataDefinition);\n            }\n        }\n        return new SDataInstanceBuilder(dataInstance);\n    }\n\n    public static SDataInstance createNewInstance(final SDataDefinition dataDefinition, long containerId,\n            String containerType, Serializable value) throws SDataInstanceNotWellFormedException {\n        return createNewInstance(dataDefinition).setContainerId(containerId).setContainerType(containerType)\n                .setValue(value).done();\n    }\n\n    private static SDataInstance getTextDataInstance(final STextDataDefinition dataDefinition) {\n        SDataInstance dataInstance;\n        if (dataDefinition.isLongText()) {\n            dataInstance = new SLongTextDataInstance(dataDefinition);\n        } else {\n            dataInstance = new SShortTextDataInstance(dataDefinition);\n        }\n        dataInstance.setValue(null);\n        return dataInstance;\n    }\n\n    private SDataInstanceBuilder(final SDataInstance dataInstance) {\n        super();\n        this.dataInstance = dataInstance;\n    }\n\n    public SDataInstanceBuilder setValue(final Serializable value) {\n        dataInstance.setValue(value);\n        return this;\n    }\n\n    public SDataInstanceBuilder setContainerId(final long containerId) {\n        dataInstance.setContainerId(containerId);\n        return this;\n    }\n\n    public SDataInstanceBuilder setContainerType(final String containerType) {\n        dataInstance.setContainerType(containerType);\n        return this;\n    }\n\n    public SDataInstance done() throws SDataInstanceNotWellFormedException {\n        dataInstance.validate();\n        return dataInstance;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/SDateDataInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\nimport org.hibernate.annotations.Type;\n\n/**\n * @author Zhao Na\n * @author Matthieu Chaffotte\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@SuperBuilder\n@Entity\n@DiscriminatorValue(\"SDateDataInstanceImpl\")\npublic class SDateDataInstance extends SDataInstance {\n\n    @Column(name = \"longValue\")\n    @Type(type = \"org.bonitasoft.engine.persistence.DateStoredAsLongUserType\")\n    private Date value;\n\n    public SDateDataInstance(final SDataDefinition dataDefinition) {\n        super(dataDefinition);\n    }\n\n    @Override\n    public void setValue(final Serializable value) {\n        this.value = (Date) value;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/SDoubleDataInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model;\n\nimport java.io.Serializable;\n\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\n\n/**\n * @author Zhao Na\n * @author Matthieu Chaffotte\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@SuperBuilder\n@Entity\n@DiscriminatorValue(\"SDoubleDataInstanceImpl\")\npublic class SDoubleDataInstance extends SDataInstance {\n\n    @Column(name = \"doubleValue\")\n    private Double value;\n\n    public SDoubleDataInstance(final SDataDefinition dataDefinition) {\n        super(dataDefinition);\n    }\n\n    @Override\n    public void setValue(final Serializable value) {\n        this.value = (Double) value;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/SFloatDataInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model;\n\nimport java.io.Serializable;\n\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\n\n/**\n * @author Celine Souchet\n * @author Matthieu Chaffotte\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@SuperBuilder\n@Entity\n@DiscriminatorValue(\"SFloatDataInstanceImpl\")\npublic class SFloatDataInstance extends SDataInstance {\n\n    @Column(name = \"floatValue\")\n    private Float value;\n\n    public SFloatDataInstance(final SDataDefinition dataDefinition) {\n        super(dataDefinition);\n    }\n\n    @Override\n    public void setValue(final Serializable value) {\n        this.value = (Float) value;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/SIntegerDataInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model;\n\nimport java.io.Serializable;\n\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\n\n/**\n * @author Zhao Na\n * @author Matthieu Chaffotte\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@SuperBuilder\n@Entity\n@DiscriminatorValue(\"SIntegerDataInstanceImpl\")\npublic class SIntegerDataInstance extends SDataInstance {\n\n    @Column(name = \"intValue\")\n    private Integer value;\n\n    public SIntegerDataInstance(final SDataDefinition dataDefinition) {\n        super(dataDefinition);\n    }\n\n    @Override\n    public Integer getValue() {\n        return value;\n    }\n\n    @Override\n    public void setValue(final Serializable value) {\n        this.value = (Integer) value;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/SLongDataInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model;\n\nimport java.io.Serializable;\n\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\n\n/**\n * @author Zhao Na\n * @author Matthieu Chaffotte\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@SuperBuilder\n@Entity\n@DiscriminatorValue(\"SLongDataInstanceImpl\")\npublic class SLongDataInstance extends SDataInstance {\n\n    @Column(name = \"longValue\")\n    private Long value;\n\n    public SLongDataInstance(final SDataDefinition dataDefinition) {\n        super(dataDefinition);\n    }\n\n    @Override\n    public void setValue(final Serializable value) {\n        this.value = (Long) value;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/SLongTextDataInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model;\n\nimport java.io.Serializable;\n\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\nimport org.hibernate.annotations.Type;\n\n/**\n * @author Zhao Na\n * @author Matthieu Chaffotte\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@SuperBuilder\n@Entity\n@DiscriminatorValue(\"SLongTextDataInstanceImpl\")\npublic class SLongTextDataInstance extends SDataInstance {\n\n    @Column(name = \"clobValue\")\n    @Type(type = \"materialized_clob\")\n    private String value;\n\n    public SLongTextDataInstance(final SDataDefinition dataDefinition) {\n        super(dataDefinition);\n    }\n\n    @Override\n    public void setValue(final Serializable value) {\n        this.value = (String) value;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/SShortTextDataInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model;\n\nimport java.io.Serializable;\n\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\nimport org.bonitasoft.engine.data.instance.model.exceptions.SDataInstanceNotWellFormedException;\n\n/**\n * @author Zhao Na\n * @author Matthieu Chaffotte\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@SuperBuilder\n@Entity\n@DiscriminatorValue(\"SShortTextDataInstanceImpl\")\npublic class SShortTextDataInstance extends SDataInstance {\n\n    public static final int MAX_LENGTH = 255;\n    @Column(name = \"shortTextValue\")\n    private String value;\n\n    public SShortTextDataInstance(final SDataDefinition dataDefinition) {\n        super(dataDefinition);\n    }\n\n    @Override\n    public void setValue(final Serializable value) {\n        this.value = (String) value;\n    }\n\n    @Override\n    public void validate() throws SDataInstanceNotWellFormedException {\n        if (getValue() != null && getValue().length() > MAX_LENGTH) {\n            throw new SDataInstanceNotWellFormedException(\n                    \"Data \" + getName() + \" must not be longer than \" + MAX_LENGTH + \" characters\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/SXMLDataInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model;\n\nimport java.io.Serializable;\n\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\nimport org.bonitasoft.engine.data.definition.model.SXMLDataDefinition;\nimport org.hibernate.annotations.Type;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@SuperBuilder\n@Entity\n@DiscriminatorValue(\"SXMLDataInstanceImpl\")\npublic class SXMLDataInstance extends SDataInstance {\n\n    @Column(name = \"clobValue\")\n    @Type(type = \"materialized_clob\")\n    private String value;\n    @Column\n    private String namespace;\n    @Column\n    private String element;\n\n    public SXMLDataInstance(final SXMLDataDefinition dataDefinition) {\n        super(dataDefinition);\n        namespace = dataDefinition.getNamespace();\n        element = dataDefinition.getElement();\n    }\n\n    @Override\n    public void setValue(final Serializable value) {\n        this.value = (String) value;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/SXMLObjectDataInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model;\n\nimport java.io.Serializable;\n\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\nimport org.bonitasoft.engine.data.instance.model.impl.XStreamFactory;\nimport org.hibernate.annotations.Type;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@SuperBuilder\n@Entity\n@DiscriminatorValue(\"SXMLObjectDataInstanceImpl\")\npublic final class SXMLObjectDataInstance extends SDataInstance {\n\n    @Column(name = \"clobValue\")\n    @Type(type = \"materialized_clob\")\n    private String value;\n\n    public SXMLObjectDataInstance(final SDataDefinition dataDefinition) {\n        super(dataDefinition);\n    }\n\n    @Override\n    public Serializable getValue() {\n        return revert(value);\n    }\n\n    @Override\n    public void setValue(final Serializable value) {\n        this.value = convert(value);\n    }\n\n    private String convert(final Serializable value) {\n        return XStreamFactory.getXStream().toXML(value);\n    }\n\n    private Serializable revert(final String value) {\n        if (value != null) {\n            return (Serializable) XStreamFactory.getXStream().fromXML(value);\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/archive/SABlobDataInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model.archive;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectOutputStream;\nimport java.io.ObjectStreamClass;\nimport java.io.Serializable;\n\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.hibernate.annotations.Type;\n\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@SuperBuilder\n@Entity\n@DiscriminatorValue(\"SABlobDataInstanceImpl\")\npublic class SABlobDataInstance extends SADataInstance {\n\n    @Column(name = \"blobValue\")\n    @Type(type = \"materialized_blob\")\n    private byte[] value;\n\n    public SABlobDataInstance(final SDataInstance sDataInstance) {\n        super(sDataInstance);\n        setValue(sDataInstance.getValue());\n    }\n\n    @Override\n    public Serializable getValue() {\n        return revert(value);\n    }\n\n    @Override\n    public void setValue(final Serializable value) {\n        this.value = convert(value);\n    }\n\n    @Override\n    public Class<? extends PersistentObject> getPersistentObjectInterface() {\n        return SDataInstance.class;\n    }\n\n    private byte[] convert(final Serializable value) {\n        ObjectOutputStream oos = null;\n        try {\n            final ByteArrayOutputStream baos = new ByteArrayOutputStream();\n            oos = new ObjectOutputStream(baos);\n            oos.writeObject(value);\n            oos.flush();\n            return baos.toByteArray();\n        } catch (final IOException ioe) {\n            throw new SBonitaRuntimeException(ioe);\n        } finally {\n            try {\n                if (oos != null) {\n                    oos.close();\n                }\n            } catch (final IOException ioe) {\n                throw new SBonitaRuntimeException(ioe);\n            }\n        }\n    }\n\n    private Serializable revert(final byte[] bytes) {\n        if (bytes == null) {\n            return null;\n        }\n        ObjectInputStream ois = null;\n        try {\n            final ByteArrayInputStream bais = new ByteArrayInputStream(bytes);\n            ois = new ObjectInputStream(bais) {\n\n                @Override\n                protected Class<?> resolveClass(final ObjectStreamClass desc) throws ClassNotFoundException {\n                    final String className = desc.getName();\n                    final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();\n                    return Class.forName(className, true, classLoader);\n                }\n            };\n            return (Serializable) ois.readObject();\n        } catch (final IOException ioe) {\n            throw new SBonitaRuntimeException(ioe);\n        } catch (final ClassNotFoundException cnfe) {\n            throw new SBonitaRuntimeException(cnfe);\n        } finally {\n            if (ois != null) {\n                try {\n                    ois.close();\n                } catch (final IOException ioe) {\n                    throw new SBonitaRuntimeException(ioe);\n                }\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/archive/SABooleanDataInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model.archive;\n\nimport java.io.Serializable;\n\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\n\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@SuperBuilder\n@Entity\n@DiscriminatorValue(\"SABooleanDataInstanceImpl\")\npublic class SABooleanDataInstance extends SADataInstance {\n\n    @Column(name = \"booleanValue\")\n    private Boolean value;\n\n    public SABooleanDataInstance(final SDataInstance sDataInstance) {\n        super(sDataInstance);\n        value = (Boolean) sDataInstance.getValue();\n    }\n\n    @Override\n    public Boolean getValue() {\n        return value;\n    }\n\n    @Override\n    public void setValue(final Serializable value) {\n        this.value = (Boolean) value;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/archive/SADataInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model.archive;\n\nimport java.io.Serializable;\n\nimport javax.persistence.DiscriminatorColumn;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Inheritance;\nimport javax.persistence.InheritanceType;\nimport javax.persistence.Table;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\nimport org.bonitasoft.engine.persistence.ArchivedPersistentObject;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n@Data\n@NoArgsConstructor\n@SuperBuilder\n@Entity\n@Inheritance(strategy = InheritanceType.SINGLE_TABLE)\n@DiscriminatorColumn(name = \"DISCRIMINANT\")\n@Table(name = \"arch_data_instance\")\npublic abstract class SADataInstance implements ArchivedPersistentObject {\n\n    @Id\n    private long id;\n    private String name;\n    private String description;\n    private boolean transientData;\n    private String className;\n    private long containerId;\n    private String containerType;\n    private long archiveDate;\n    private long sourceObjectId;\n\n    protected SADataInstance(final SDataInstance sDataInstance) {\n        name = sDataInstance.getName();\n        description = sDataInstance.getDescription();\n        transientData = sDataInstance.isTransientData();\n        className = sDataInstance.getClassName();\n        containerId = sDataInstance.getContainerId();\n        containerType = sDataInstance.getContainerType();\n        sourceObjectId = sDataInstance.getId();\n    }\n\n    public abstract Serializable getValue();\n\n    public abstract void setValue(Serializable value);\n\n    @Override\n    public Class<? extends PersistentObject> getPersistentObjectInterface() {\n        return SDataInstance.class;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/archive/SADateDataInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model.archive;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\nimport org.hibernate.annotations.Type;\n\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@SuperBuilder\n@Entity\n@DiscriminatorValue(\"SADateDataInstanceImpl\")\npublic class SADateDataInstance extends SADataInstance {\n\n    @Column(name = \"longValue\")\n    @Type(type = \"org.bonitasoft.engine.persistence.DateStoredAsLongUserType\")\n    private Date value;\n\n    public SADateDataInstance(final SDataInstance sDataInstance) {\n        super(sDataInstance);\n        value = (Date) sDataInstance.getValue();\n    }\n\n    @Override\n    public Date getValue() {\n        return value;\n    }\n\n    @Override\n    public void setValue(final Serializable value) {\n        this.value = (Date) value;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/archive/SADoubleDataInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model.archive;\n\nimport java.io.Serializable;\n\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\n\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@SuperBuilder\n@Entity\n@DiscriminatorValue(\"SADoubleDataInstanceImpl\")\npublic class SADoubleDataInstance extends SADataInstance {\n\n    @Column(name = \"doubleValue\")\n    private Double value;\n\n    public SADoubleDataInstance(final SDataInstance sDataInstance) {\n        super(sDataInstance);\n        value = (Double) sDataInstance.getValue();\n    }\n\n    @Override\n    public Double getValue() {\n        return value;\n    }\n\n    @Override\n    public void setValue(final Serializable value) {\n        this.value = (Double) value;\n    }\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/archive/SAFloatDataInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model.archive;\n\nimport java.io.Serializable;\n\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\n\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@SuperBuilder\n@Entity\n@DiscriminatorValue(\"SAFloatDataInstanceImpl\")\npublic class SAFloatDataInstance extends SADataInstance {\n\n    @Column(name = \"floatValue\")\n    private Float value;\n\n    public SAFloatDataInstance(final SDataInstance sDataInstance) {\n        super(sDataInstance);\n        value = (Float) sDataInstance.getValue();\n    }\n\n    @Override\n    public Float getValue() {\n        return value;\n    }\n\n    @Override\n    public void setValue(final Serializable value) {\n        this.value = (Float) value;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/archive/SAIntegerDataInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model.archive;\n\nimport java.io.Serializable;\n\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\n\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@SuperBuilder\n@Entity\n@DiscriminatorValue(\"SAIntegerDataInstanceImpl\")\npublic class SAIntegerDataInstance extends SADataInstance {\n\n    @Column(name = \"intValue\")\n    private Integer value;\n\n    public SAIntegerDataInstance(final SDataInstance sDataInstance) {\n        super(sDataInstance);\n        value = (Integer) sDataInstance.getValue();\n    }\n\n    @Override\n    public Integer getValue() {\n        return value;\n    }\n\n    @Override\n    public void setValue(final Serializable value) {\n        this.value = (Integer) value;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/archive/SALongDataInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model.archive;\n\nimport java.io.Serializable;\n\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\n\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@SuperBuilder\n@Entity\n@DiscriminatorValue(\"SALongDataInstanceImpl\")\npublic class SALongDataInstance extends SADataInstance {\n\n    @Column(name = \"longValue\")\n    private Long value;\n\n    public SALongDataInstance(final SDataInstance sDataInstance) {\n        super(sDataInstance);\n        value = (Long) sDataInstance.getValue();\n    }\n\n    @Override\n    public Long getValue() {\n        return value;\n    }\n\n    @Override\n    public void setValue(final Serializable value) {\n        this.value = (Long) value;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/archive/SALongTextDataInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model.archive;\n\nimport java.io.Serializable;\n\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\nimport org.hibernate.annotations.Type;\n\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@SuperBuilder\n@Entity\n@DiscriminatorValue(\"SALongTextDataInstanceImpl\")\npublic class SALongTextDataInstance extends SADataInstance {\n\n    @Column(name = \"clobValue\")\n    @Type(type = \"materialized_clob\")\n    private String value;\n\n    public SALongTextDataInstance(final SDataInstance sDataInstance) {\n        super(sDataInstance);\n        value = (String) sDataInstance.getValue();\n    }\n\n    @Override\n    public String getValue() {\n        return value;\n    }\n\n    @Override\n    public void setValue(final Serializable value) {\n        this.value = (String) value;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/archive/SAShortTextDataInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model.archive;\n\nimport java.io.Serializable;\n\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\n\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@SuperBuilder\n@Entity\n@DiscriminatorValue(\"SAShortTextDataInstanceImpl\")\npublic class SAShortTextDataInstance extends SADataInstance {\n\n    @Column(name = \"shortTextValue\")\n    private String value;\n\n    public SAShortTextDataInstance(final SDataInstance sDataInstance) {\n        super(sDataInstance);\n        value = (String) sDataInstance.getValue();\n    }\n\n    @Override\n    public Serializable getValue() {\n        return value;\n    }\n\n    @Override\n    public void setValue(final Serializable value) {\n        this.value = (String) value;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/archive/SAXMLDataInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model.archive;\n\nimport java.io.Serializable;\n\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\nimport org.hibernate.annotations.Type;\n\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@SuperBuilder\n@Entity\n@DiscriminatorValue(\"SAXMLDataInstanceImpl\")\npublic class SAXMLDataInstance extends SADataInstance {\n\n    @Column(name = \"clobValue\")\n    @Type(type = \"materialized_clob\")\n    private String value;\n    private String namespace;\n    private String element;\n\n    public SAXMLDataInstance(final SDataInstance sDataInstance) {\n        super(sDataInstance);\n        value = (String) sDataInstance.getValue();\n    }\n\n    @Override\n    public Serializable getValue() {\n        return value;\n    }\n\n    @Override\n    public void setValue(final Serializable value) {\n        this.value = (String) value;\n    }\n\n    public String getNamespace() {\n        return namespace;\n    }\n\n    public void setNamespace(final String namespace) {\n        this.namespace = namespace;\n    }\n\n    public String getElement() {\n        return element;\n    }\n\n    public void setElement(final String element) {\n        this.element = element;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/archive/SAXMLObjectDataInstance.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model.archive;\n\nimport java.io.Serializable;\n\nimport javax.persistence.Column;\nimport javax.persistence.DiscriminatorValue;\nimport javax.persistence.Entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\nimport org.bonitasoft.engine.data.instance.model.impl.XStreamFactory;\nimport org.hibernate.annotations.Type;\n\n@Data\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@SuperBuilder\n@Entity\n@DiscriminatorValue(\"SAXMLObjectDataInstanceImpl\")\npublic final class SAXMLObjectDataInstance extends SADataInstance {\n\n    @Column(name = \"clobValue\")\n    @Type(type = \"materialized_clob\")\n    private String value;\n\n    public SAXMLObjectDataInstance(final SDataInstance sDataInstance) {\n        super(sDataInstance);\n        setValue(sDataInstance.getValue());\n    }\n\n    @Override\n    public Serializable getValue() {\n        if (value != null) {\n            return (Serializable) XStreamFactory.getXStream().fromXML(value);\n        }\n        return null;\n    }\n\n    @Override\n    public void setValue(final Serializable value) {\n        this.value = XStreamFactory.getXStream().toXML(value);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/archive/builder/SADataInstanceBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model.archive.builder;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.data.instance.model.SBlobDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SLongTextDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SShortTextDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SXMLDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SXMLObjectDataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.SABlobDataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.SABooleanDataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.SADataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.SADateDataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.SADoubleDataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.SAFloatDataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.SAIntegerDataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.SALongDataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.SALongTextDataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.SAShortTextDataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.SAXMLDataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.SAXMLObjectDataInstance;\n\npublic class SADataInstanceBuilder {\n\n    public SADataInstance createNewInstance(final SDataInstance sDataInstance) {\n        final String className = sDataInstance.getClassName();\n        SADataInstance saDataInstance = null;\n        if (sDataInstance instanceof SShortTextDataInstance) {\n            saDataInstance = new SAShortTextDataInstance(sDataInstance);\n        } else if (sDataInstance instanceof SLongTextDataInstance) {\n            saDataInstance = new SALongTextDataInstance(sDataInstance);\n        } else if (sDataInstance instanceof SXMLDataInstance) {\n            saDataInstance = new SAXMLDataInstance(sDataInstance);\n        } else if (sDataInstance instanceof SBlobDataInstance) {\n            saDataInstance = new SABlobDataInstance(sDataInstance);\n        } else if (sDataInstance instanceof SXMLObjectDataInstance) {\n            saDataInstance = new SAXMLObjectDataInstance(sDataInstance);\n        } else {\n            if (Integer.class.getName().equals(className)) {\n                saDataInstance = new SAIntegerDataInstance(sDataInstance);\n            } else if (Long.class.getName().equals(className)) {\n                saDataInstance = new SALongDataInstance(sDataInstance);\n            } else if (Boolean.class.getName().equals(className)) {\n                saDataInstance = new SABooleanDataInstance(sDataInstance);\n            } else if (Date.class.getName().equals(className)) {\n                saDataInstance = new SADateDataInstance(sDataInstance);\n            } else if (Double.class.getName().equals(className)) {\n                saDataInstance = new SADoubleDataInstance(sDataInstance);\n            } else if (Float.class.getName().equals(className)) {\n                saDataInstance = new SAFloatDataInstance(sDataInstance);\n            }\n        }\n        return saDataInstance;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/archive/builder/SADataInstanceLogBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model.archive.builder;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\n\n/**\n * @author Feng Hui\n */\npublic interface SADataInstanceLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction {\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/archive/builder/SADataInstanceLogBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model.archive.builder;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory;\n\n/**\n * @author Feng Hui\n */\npublic interface SADataInstanceLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory {\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/archive/builder/impl/SADataInstanceLogBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model.archive.builder.impl;\n\nimport org.bonitasoft.engine.data.instance.model.archive.builder.SADataInstanceLogBuilderFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory;\n\n/**\n * @author Feng Hui\n * @author Matthieu Chaffotte\n */\npublic class SADataInstanceLogBuilderFactoryImpl extends CRUDELogBuilderFactory\n        implements SADataInstanceLogBuilderFactory {\n\n    private static final String SA_DATA_INSTANCE_INDEX_NAME = \"numericIndex1\";\n\n    @Override\n    public String getObjectIdKey() {\n        return SA_DATA_INSTANCE_INDEX_NAME;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/archive/builder/impl/SADataInstanceLogBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model.archive.builder.impl;\n\nimport org.bonitasoft.engine.data.instance.model.archive.builder.SADataInstanceLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException;\n\n/**\n * @author Feng Hui\n * @author Matthieu Chaffotte\n */\npublic class SADataInstanceLogBuilderImpl extends CRUDELogBuilder implements SADataInstanceLogBuilder {\n\n    private static final String SA_DATA_INSTANCE = \"SA_DATA_INSTANCE\";\n\n    private static final int SA_DATA_INSTANCE_INDEX = 0;\n\n    @Override\n    public SPersistenceLogBuilder objectId(final long objectId) {\n        queriableLogBuilder.numericIndex(SA_DATA_INSTANCE_INDEX, objectId);\n        return this;\n    }\n\n    @Override\n    protected String getActionTypePrefix() {\n        return SA_DATA_INSTANCE;\n    }\n\n    @Override\n    protected void checkExtraRules(final SQueriableLog log) {\n        if (log.getActionStatus() != SQueriableLog.STATUS_FAIL && log.getNumericIndex(SA_DATA_INSTANCE_INDEX) == 0L) {\n            throw new MissingMandatoryFieldsException(\"Some mandatory fields are missing: \" + \"data instance Id\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/exceptions/SDataInstanceNotWellFormedException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model.exceptions;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * This exception is thrown if a SDataInstance is not well formed.\n * For example: when a string gets a too long value.\n *\n * @Author: Frederic Bouquet\n */\npublic class SDataInstanceNotWellFormedException extends SBonitaException {\n\n    private static final long serialVersionUID = 8447390597638287376L;\n\n    public SDataInstanceNotWellFormedException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/resources/org/bonitasoft/engine/data/instance/model/impl/hibernate/archived.data.instance.queries.hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\"\n                                   \"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd\">\n<hibernate-mapping auto-import=\"false\">\n\n\t<query name=\"getSADataInstanceByDataInstanceIdAndArchiveDate\">\n\t\tSELECT saDataInstance\n\t\tFROM org.bonitasoft.engine.data.instance.model.archive.SADataInstance AS saDataInstance\n\t\tWHERE saDataInstance.sourceObjectId = :dataInstanceId\n\t\tAND :time >= saDataInstance.archiveDate  \n\t\tORDER BY archiveDate DESC\n\t</query>\n\n\t<query name=\"getSADataInstancesByDataInstanceIdAndArchiveDate\">\n\t\tSELECT saDataInstance\n\t\tFROM org.bonitasoft.engine.data.instance.model.archive.SADataInstance AS saDataInstance\n\t\tWHERE saDataInstance.sourceObjectId IN (:dataInstanceIds)\n        AND saDataInstance.id IN(\n\t\t\tSELECT max(sadi.id)\n\t\t\tFROM org.bonitasoft.engine.data.instance.model.archive.SADataInstance AS sadi\n\t\t\tWHERE :time >= sadi.archiveDate \n\t\t\tGROUP BY sadi.sourceObjectId\n\t\t)\n\t</query>\n\n\t<query name=\"getSADataInstanceByDataInstanceId\">\n\t\tSELECT saDataInstance\n\t\tFROM org.bonitasoft.engine.data.instance.model.archive.SADataInstance AS saDataInstance\n\t\tWHERE saDataInstance.sourceObjectId = :dataInstanceId\n\t</query>\n\n\t<query name=\"getLastSADataInstanceByDataInstanceId\">\n\t\tSELECT sa\n\t\tFROM org.bonitasoft.engine.data.instance.model.archive.SADataInstance AS sa\n\t\tWHERE sa.sourceObjectId = :dataInstanceId\n\t\tORDER BY sa.archiveDate DESC\n\t</query>\n\n\t<query name=\"getLastSADataInstanceByContainer\">\n\t\tSELECT sa\n\t\tFROM org.bonitasoft.engine.data.instance.model.archive.SADataInstance AS sa\n\t\tWHERE sa.name = :dataName \n\t\tAND sa.containerId = :containerId\n\t\tAND sa.containerType = :containerType\n\t\tORDER BY sa.archiveDate DESC\n\t</query>\n\n\t<query name=\"getLocalSADataInstances\">\n\t\tSELECT sa\n\t\tFROM org.bonitasoft.engine.data.instance.model.archive.SADataInstance AS sa\n\t\tWHERE sa.containerId = :containerId\n\t\tAND sa.containerType = :containerType\n\t\tORDER BY sa.id\n\t</query>\n\n\t<query name=\"getLastLocalSADataInstances\">\n\t\tSELECT sa\n\t\tFROM org.bonitasoft.engine.data.instance.model.archive.SADataInstance AS sa\n\t\tWHERE sa.containerId = :containerId\n\t\tAND sa.containerType = :containerType\n\t\tAND sa.archiveDate IN (\n\t\t\tSELECT MAX(archiveDate)\n\t\t\tFROM org.bonitasoft.engine.data.instance.model.archive.SADataInstance\n\t\t\tWHERE containerId = :containerId\n\t\t\tAND containerType = :containerType\n\t\t\tAND name = sa.name\n\t\t\tGROUP BY name\n\t\t)\n\t\tORDER BY sa.archiveDate DESC\n\t</query>\n\n    <query name=\"getArchivedDataInstancesWithNamesOf1Containers\">\n        SELECT dataInstance\n        FROM org.bonitasoft.engine.data.instance.model.archive.SADataInstance AS dataInstance\n        WHERE\n        dataInstance.name in (:dataNames)\n        AND\n        dataInstance.archiveDate &lt;= :time\n        AND\n        (\n        dataInstance.containerType = :containerType1 AND dataInstance.containerId in(:containerType1Ids)\n        )\n    </query>\n\n    <query name=\"getArchivedDataInstancesWithNamesOf2Containers\">\n        SELECT dataInstance\n        FROM org.bonitasoft.engine.data.instance.model.archive.SADataInstance AS dataInstance\n        WHERE\n        dataInstance.name in (:dataNames)\n        AND\n        dataInstance.archiveDate &lt;= :time\n        AND\n        (\n        dataInstance.containerType = :containerType1 AND dataInstance.containerId in(:containerType1Ids)\n        OR\n        dataInstance.containerType = :containerType2 AND dataInstance.containerId in(:containerType2Ids)\n        )\n    </query>\n\n    <query name=\"getArchivedDataInstancesWithNamesOf3Containers\">\n        SELECT dataInstance\n        FROM org.bonitasoft.engine.data.instance.model.archive.SADataInstance AS dataInstance\n        WHERE\n        dataInstance.name in (:dataNames)\n        AND\n        dataInstance.archiveDate &lt;= :time\n        AND\n        (\n        dataInstance.containerType = :containerType1 AND dataInstance.containerId in(:containerType1Ids)\n        OR\n        dataInstance.containerType = :containerType2 AND dataInstance.containerId in(:containerType2Ids)\n        OR\n        dataInstance.containerType = :containerType3 AND dataInstance.containerId in(:containerType3Ids)\n        )\n    </query>\n\n\t<query name=\"deleteLocalSADataInstances\">\n\t\tDELETE\n\t\tFROM org.bonitasoft.engine.data.instance.model.archive.SADataInstance AS dataInstance\n\t\tWHERE dataInstance.containerType = :containerType AND dataInstance.containerId = :containerId\n\t</query>\n\n\t<query name=\"deleteLocalSADataInstancesOfContainers\">\n\t\tDELETE\n\t\tFROM org.bonitasoft.engine.data.instance.model.archive.SADataInstance AS dataInstance\n\t\tWHERE dataInstance.containerType = :containerType AND dataInstance.containerId IN (:containerIds)\n\n\t</query>\n\n</hibernate-mapping>\n"
  },
  {
    "path": "services/bonita-data-instance/src/main/resources/org/bonitasoft/engine/data/instance/model/impl/hibernate/data.instance.queries.hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\" \"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd\">\n<hibernate-mapping auto-import=\"false\">\n\n    <query name=\"getDataInstanceByIds\">\n        SELECT dataInstance\n        FROM org.bonitasoft.engine.data.instance.model.SDataInstance AS dataInstance\n        WHERE dataInstance.id IN (:ids)\n        ORDER BY dataInstance.id\n    </query>\n\n    <query name=\"getDataInstancesByNameAndContainer\">\n        SELECT dataInstance\n        FROM org.bonitasoft.engine.data.instance.model.SDataInstance AS dataInstance\n        WHERE dataInstance.name = :name\n        AND dataInstance.containerId = :containerId\n        AND dataInstance.containerType = :containerType\n    </query>\n\n    <query name=\"getDataInstancesByContainer\">\n        SELECT dataInstance\n        FROM org.bonitasoft.engine.data.instance.model.SDataInstance AS dataInstance\n        WHERE dataInstance.containerId = :containerId\n        AND dataInstance.containerType = :containerType\n    </query>\n\n\n    <query name=\"getNumberOfDataInstancesForContainer\">\n        SELECT count(dataInstance.id)\n        FROM org.bonitasoft.engine.data.instance.model.SDataInstance AS dataInstance\n        WHERE dataInstance.containerId = :containerId\n        AND dataInstance.containerType = :containerType\n    </query>\n\n    <query name=\"getDataInstancesOf1Containers\">\n        SELECT dataInstance\n        FROM org.bonitasoft.engine.data.instance.model.SDataInstance AS dataInstance\n        WHERE\n        dataInstance.containerType = :containerType1 AND dataInstance.containerId in(:containerType1Ids)\n    </query>\n\n    <query name=\"getDataInstancesOf2Containers\">\n        SELECT dataInstance\n        FROM org.bonitasoft.engine.data.instance.model.SDataInstance AS dataInstance\n        WHERE\n        dataInstance.containerType = :containerType1 AND dataInstance.containerId in(:containerType1Ids)\n        OR\n        dataInstance.containerType = :containerType2 AND dataInstance.containerId in(:containerType2Ids)\n    </query>\n\n    <query name=\"getDataInstancesOf3Containers\">\n        SELECT dataInstance\n        FROM org.bonitasoft.engine.data.instance.model.SDataInstance AS dataInstance\n        WHERE\n        dataInstance.containerType = :containerType1 AND dataInstance.containerId in(:containerType1Ids)\n        OR\n        dataInstance.containerType = :containerType2 AND dataInstance.containerId in(:containerType2Ids)\n        OR\n        dataInstance.containerType = :containerType3 AND dataInstance.containerId in(:containerType3Ids)\n    </query>\n\n    <query name=\"getDataInstancesWithNamesOf1Containers\">\n        SELECT dataInstance\n        FROM org.bonitasoft.engine.data.instance.model.SDataInstance AS dataInstance\n        WHERE\n        dataInstance.name in (:dataNames)\n        AND\n        (\n        dataInstance.containerType = :containerType1 AND dataInstance.containerId in(:containerType1Ids)\n        )\n    </query>\n\n    <query name=\"getDataInstancesWithNamesOf2Containers\">\n        SELECT dataInstance\n        FROM org.bonitasoft.engine.data.instance.model.SDataInstance AS dataInstance\n        WHERE\n        dataInstance.name in (:dataNames)\n        AND\n        (\n        dataInstance.containerType = :containerType1 AND dataInstance.containerId in(:containerType1Ids)\n        OR\n        dataInstance.containerType = :containerType2 AND dataInstance.containerId in(:containerType2Ids)\n        )\n    </query>\n\n    <query name=\"getDataInstancesWithNamesOf3Containers\">\n        SELECT dataInstance\n        FROM org.bonitasoft.engine.data.instance.model.SDataInstance AS dataInstance\n        WHERE\n        dataInstance.name in (:dataNames)\n        AND\n        (\n        dataInstance.containerType = :containerType1 AND dataInstance.containerId in(:containerType1Ids)\n        OR\n        dataInstance.containerType = :containerType2 AND dataInstance.containerId in(:containerType2Ids)\n        OR\n        dataInstance.containerType = :containerType3 AND dataInstance.containerId in(:containerType3Ids)\n        )\n    </query>\n\n\n</hibernate-mapping>\n"
  },
  {
    "path": "services/bonita-data-instance/src/test/java/org/bonitasoft/engine/data/instance/api/impl/ArchivedDataInContainersComparatorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.api.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.text.DateFormat;\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.UUID;\n\nimport org.bonitasoft.engine.data.instance.api.DataContainer;\nimport org.bonitasoft.engine.data.instance.model.archive.SADataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.SALongTextDataInstance;\nimport org.junit.Test;\n\n/**\n * @author Baptiste Mesta\n */\npublic class ArchivedDataInContainersComparatorTest {\n\n    private ArchivedDataInContainersComparator comparator = new ArchivedDataInContainersComparator(\n            Arrays.asList(new DataContainer(1L, \"SUBTASK\"),\n                    new DataContainer(2L, \"TASK\"),\n                    new DataContainer(3L, \"PROC\")));\n    private DateFormat formatter = new SimpleDateFormat(\"dd/MM/yy\");\n\n    private SADataInstance data(long containerId, String containerType, String archivedDate) throws ParseException {\n        SALongTextDataInstance sLongTextDataInstance = new SALongTextDataInstance();\n        sLongTextDataInstance.setId(UUID.randomUUID().getLeastSignificantBits());\n        sLongTextDataInstance.setContainerId(containerId);\n        sLongTextDataInstance.setContainerType(containerType);\n        sLongTextDataInstance.setArchiveDate(formatter.parse(archivedDate).getTime());\n        return sLongTextDataInstance;\n    }\n\n    @Test\n    public void should_compare_return_0_when_same_date_and_container() throws Exception {\n        //given\n        SADataInstance data1 = data(1L, \"SUBTASK\", \"27/05/1987\");\n        SADataInstance data2 = data(1L, \"SUBTASK\", \"27/05/1987\");\n        //then\n        assertThat(comparator.compare(data1, data2)).isEqualTo(0);\n        assertThat(comparator.compare(data2, data1)).isEqualTo(0);\n    }\n\n    @Test\n    public void should_sort_in_same_container_return_the_most_recent_first() throws Exception {\n        //given\n        SADataInstance data1 = data(1L, \"SUBTASK\", \"27/05/1987\");\n        SADataInstance data2 = data(1L, \"SUBTASK\", \"28/05/1987\");\n        List<SADataInstance> list = Arrays.asList(data1, data2);\n        //when\n        Collections.sort(list, comparator);\n        //then\n        assertThat(list).containsExactly(data2, data1);\n    }\n\n    @Test\n    public void should_compare_very_different_date_in_same_container() throws Exception {\n        //given\n        SADataInstance data1 = data(1L, \"SUBTASK\", \"01/01/0001\");\n        SADataInstance data2 = data(1L, \"SUBTASK\", \"28/05/3000\");\n        List<SADataInstance> list = Arrays.asList(data1, data2);\n        //when\n        Collections.sort(list, comparator);\n        //then\n        assertThat(list).containsExactly(data2, data1);\n    }\n\n    @Test\n    public void should_sort_in_different_container_using_same_date_return_closer_data_first() throws Exception {\n        //given\n        SADataInstance data1 = data(1L, \"SUBTASK\", \"27/05/1987\");\n        SADataInstance data2 = data(2L, \"TASK\", \"27/05/1987\");\n        SADataInstance data3 = data(3L, \"PROC\", \"27/05/1987\");\n        List<SADataInstance> list = Arrays.asList(data3, data1, data2);\n        //when\n        Collections.sort(list, comparator);\n        //then\n        assertThat(list).containsExactly(data1, data2, data3);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/test/java/org/bonitasoft/engine/data/instance/api/impl/DataInContainersComparatorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.api.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.text.ParseException;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.UUID;\n\nimport org.bonitasoft.engine.data.instance.api.DataContainer;\nimport org.bonitasoft.engine.data.instance.model.archive.SADataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.SALongTextDataInstance;\nimport org.junit.Test;\n\n/**\n * @author Baptiste Mesta\n */\npublic class DataInContainersComparatorTest {\n\n    private ArchivedDataInContainersComparator comparator = new ArchivedDataInContainersComparator(\n            Arrays.asList(new DataContainer(1L, \"SUBTASK\"),\n                    new DataContainer(2L, \"TASK\"),\n                    new DataContainer(3L, \"PROC\")));\n\n    private SADataInstance data(long containerId, String containerType) throws ParseException {\n        SALongTextDataInstance sLongTextDataInstance = new SALongTextDataInstance();\n        sLongTextDataInstance.setId(UUID.randomUUID().getLeastSignificantBits());\n        sLongTextDataInstance.setContainerId(containerId);\n        sLongTextDataInstance.setContainerType(containerType);\n        return sLongTextDataInstance;\n    }\n\n    @Test\n    public void should_compare_return_0_when_same_container() throws Exception {\n        //given\n        SADataInstance data1 = data(1L, \"SUBTASK\");\n        SADataInstance data2 = data(1L, \"SUBTASK\");\n        //then\n        assertThat(comparator.compare(data1, data2)).isEqualTo(0);\n        assertThat(comparator.compare(data2, data1)).isEqualTo(0);\n    }\n\n    @Test\n    public void should_sort_in_different_container_return_closer_data_first() throws Exception {\n        //given\n        SADataInstance data1 = data(1L, \"SUBTASK\");\n        SADataInstance data2 = data(2L, \"TASK\");\n        SADataInstance data3 = data(3L, \"PROC\");\n        List<SADataInstance> list = Arrays.asList(data3, data1, data2);\n        //when\n        Collections.sort(list, comparator);\n        //then\n        assertThat(list).containsExactly(data1, data2, data3);\n    }\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/test/java/org/bonitasoft/engine/data/instance/api/impl/DataInstanceServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.api.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.anyLong;\nimport static org.mockito.Mockito.*;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.archive.ArchiveInsertRecord;\nimport org.bonitasoft.engine.archive.ArchiveService;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.data.instance.api.DataContainer;\nimport org.bonitasoft.engine.data.instance.api.ParentContainerResolver;\nimport org.bonitasoft.engine.data.instance.exception.SDataInstanceReadException;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SLongTextDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SShortTextDataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.SADataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.SALongTextDataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.SAShortTextDataInstance;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.ArgumentMatchers;\nimport org.mockito.Captor;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class DataInstanceServiceImplTest {\n\n    @Mock\n    private Recorder recorder;\n    @Mock\n    private ReadPersistenceService persistenceService;\n    @Mock\n    private ArchiveService archiveService;\n\n    @Mock\n    private ParentContainerResolver parentContainerResolver;\n    @Captor\n    private ArgumentCaptor<ArchiveInsertRecord> archiveInsertRecordArgumentCaptor;\n    @InjectMocks\n    private DataInstanceServiceImpl dataInstanceServiceImpl;\n\n    @Test(expected = SDataInstanceReadException.class)\n    public final void should_throw_read_exception_when_persistence_service_has_read_exception()\n            throws SBonitaException {\n        dataInstanceServiceImpl.getLastSADataInstance(\"kaupunki\", 1, \"PROCESS_INSTANCE\", parentContainerResolver);\n    }\n\n    @Test\n    public final void getLastSADataInstancesFromContainer() throws SBonitaException {\n        final List<SADataInstance> archiveInstances = Collections.emptyList();\n        doReturn(persistenceService).when(archiveService).getDefinitiveArchiveReadPersistenceService();\n        doReturn(archiveInstances).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SADataInstance>> any());\n\n        final List<SADataInstance> dataInstances = dataInstanceServiceImpl.getLastLocalSADataInstances(1,\n                \"PROCESS_INSTANCE\", 0, 10);\n        Assert.assertEquals(archiveInstances, dataInstances);\n    }\n\n    @Test\n    public final void getEmptyLastSADataInstancesFromContainer() throws SBonitaException {\n        doReturn(persistenceService).when(archiveService).getDefinitiveArchiveReadPersistenceService();\n        doReturn(Collections.emptyList()).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SADataInstance>> any());\n\n        final List<SADataInstance> dataInstances = dataInstanceServiceImpl.getLastLocalSADataInstances(1,\n                \"PROCESS_INSTANCE\", 0, 10);\n        Assert.assertEquals(Collections.emptyList(), dataInstances);\n    }\n\n    @Test(expected = SDataInstanceReadException.class)\n    public final void getLastSADataInstancesFromContainerThrowsAnExceptionDueToProblemOnPersistenceService()\n            throws SBonitaException {\n        final List<SADataInstance> archiveInstances = Collections.emptyList();\n        doReturn(persistenceService).when(archiveService).getDefinitiveArchiveReadPersistenceService();\n        doThrow(new SBonitaReadException(\"moustache\")).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SADataInstance>> any());\n\n        final List<SADataInstance> dataInstances = dataInstanceServiceImpl.getLastLocalSADataInstances(1,\n                \"PROCESS_INSTANCE\", 0, 10);\n        Assert.assertEquals(archiveInstances, dataInstances);\n    }\n\n    @Test\n    public final void should_archive_the_first_value_of_a_data_when_creating_it() throws Exception {\n        //given\n        SShortTextDataInstance dataInstance = new SShortTextDataInstance();\n        dataInstance.setValue(\"theValue\");\n        //when\n        dataInstanceServiceImpl.createDataInstance(dataInstance);\n        //then\n        verify(archiveService).recordInsert(anyLong(), archiveInsertRecordArgumentCaptor.capture());\n        final SAShortTextDataInstance entity = (SAShortTextDataInstance) archiveInsertRecordArgumentCaptor.getValue()\n                .getEntity();\n        assertThat(entity.getValue()).isEqualTo(\"theValue\");\n    }\n\n    @Test\n    public final void should_archive_new_value_on_data_value_update() throws Exception {\n        //given\n        SShortTextDataInstance dataInstance = new SShortTextDataInstance();\n        //set directly to \"theNewValue\" because the set is done by the persistence service\n        dataInstance.setValue(\"theNewValue\");\n        EntityUpdateDescriptor updateDescriptor = new EntityUpdateDescriptor();\n        updateDescriptor.addField(\"value\", \"theNewValue\");\n        //when\n        dataInstanceServiceImpl.updateDataInstance(dataInstance, updateDescriptor);\n        //then\n        verify(archiveService).recordInsert(anyLong(), archiveInsertRecordArgumentCaptor.capture());\n        final SAShortTextDataInstance entity = (SAShortTextDataInstance) archiveInsertRecordArgumentCaptor.getValue()\n                .getEntity();\n        assertThat(entity.getValue()).isEqualTo(\"theNewValue\");\n    }\n\n    @Test\n    public void should_return_the_last_version_of_an_archived_data_for_long_running_process() throws Exception {\n        //see bug BS-15990\n        List<SADataInstance> dataInstances = new ArrayList<>();\n        dataInstances.add(createArchDataInstance(1, 66L, \"TASK\", 1475800000000L, \"VALUE1\"));\n        dataInstances.add(createArchDataInstance(2, 66L, \"TASK\", 1478080000000L, \"VALUE2\"));\n        dataInstances.add(createArchDataInstance(3, 66L, \"TASK\", 1478081000000L, \"VALUE3\"));\n        dataInstances.add(createArchDataInstance(4, 66L, \"TASK\", 1478087736549L, \"VALUE4\"));\n        doReturn(dataInstances).when(persistenceService).selectList(any(SelectListDescriptor.class));\n        //order the retrieved list by container level and by archive date\n        SADataInstance dataInstance = dataInstanceServiceImpl.getLastSADataInstance(\"testData\", 1L, \"TASK\",\n                parentContainerResolver);\n        //should return the last version\n        assertThat(dataInstance.getValue()).isEqualTo(\"VALUE4\");\n    }\n\n    @Test\n    public void should_return_the_last_version_of_an_archived_data() throws Exception {\n        //see bug BS-15990\n        List<SADataInstance> dataInstances = new ArrayList<>();\n        dataInstances.add(createArchDataInstance(1, 66L, \"TASK\", 1475800000000L, \"VALUE1\"));\n        dataInstances.add(createArchDataInstance(2, 66L, \"TASK\", 1475800000001L, \"VALUE2\"));\n        dataInstances.add(createArchDataInstance(3, 66L, \"TASK\", 1475800000002L, \"VALUE3\"));\n        dataInstances.add(createArchDataInstance(4, 66L, \"TASK\", 1475800000003L, \"VALUE4\"));\n        doReturn(dataInstances).when(persistenceService).selectList(any(SelectListDescriptor.class));\n        //order the retrieved list by container level and by archive date\n        SADataInstance dataInstance = dataInstanceServiceImpl.getLastSADataInstance(\"testData\", 1L, \"TASK\",\n                parentContainerResolver);\n        //should return the last version\n        assertThat(dataInstance.getValue()).isEqualTo(\"VALUE4\");\n    }\n\n    @Test\n    public void should_return_the_archived_data_in_the_up_most_container() throws Exception {\n        //given\n        List<SADataInstance> dataInstances = new ArrayList<>();\n        dataInstances.add(createArchDataInstance(1, 66L, \"PROC\", 1475800000002L, \"PROC_VALUE\"));\n        dataInstances.add(createArchDataInstance(2, 67L, \"TASK\", 1475800000001L, \"TASK_VALUE\"));\n        dataInstances.add(createArchDataInstance(2, 68L, \"SUBTASK\", 1475800000000L, \"SUBTASK_VALUE\"));\n        doReturn(dataInstances).when(persistenceService).selectList(any(SelectListDescriptor.class));\n        doReturn(Arrays.asList(new DataContainer(68L, \"SUBTASK\"), new DataContainer(67L, \"TASK\"),\n                new DataContainer(66L, \"PROC\"))).when(parentContainerResolver)\n                .getArchivedContainerHierarchy(new DataContainer(68L, \"SUBTASK\"));\n        //when\n        SADataInstance dataInstance = dataInstanceServiceImpl.getLastSADataInstance(\"testData\", 68L, \"SUBTASK\",\n                parentContainerResolver);\n        //then\n        assertThat(dataInstance.getValue()).isEqualTo(\"SUBTASK_VALUE\");\n    }\n\n    private SALongTextDataInstance createArchDataInstance(long id, long containerId, String containerType,\n            long archiveDate, String value) {\n        SALongTextDataInstance dataInstance = new SALongTextDataInstance();\n        dataInstance.setId(id);\n        dataInstance.setContainerId(containerId);\n        dataInstance.setContainerType(containerType);\n        dataInstance.setArchiveDate(archiveDate);\n        dataInstance.setValue(value);\n        return dataInstance;\n    }\n\n    private SLongTextDataInstance createDataInstance(long id, long containerId, String containerType, String value) {\n        SLongTextDataInstance dataInstance = new SLongTextDataInstance();\n        dataInstance.setId(id);\n        dataInstance.setContainerId(containerId);\n        dataInstance.setContainerType(containerType);\n        dataInstance.setValue(value);\n        return dataInstance;\n    }\n\n    @Test\n    public void should_return_the_data_in_the_up_most_container() throws Exception {\n        //given\n        List<SDataInstance> dataInstances = new ArrayList<>();\n        dataInstances.add(createDataInstance(1, 66L, \"PROC\", \"PROC_VALUE\"));\n        dataInstances.add(createDataInstance(2, 67L, \"TASK\", \"TASK_VALUE\"));\n        dataInstances.add(createDataInstance(2, 68L, \"SUBTASK\", \"SUBTASK_VALUE\"));\n        doReturn(dataInstances).when(persistenceService).selectList(any(SelectListDescriptor.class));\n        doReturn(Arrays.asList(new DataContainer(68L, \"SUBTASK\"), new DataContainer(67L, \"TASK\"),\n                new DataContainer(66L, \"PROC\"))).when(parentContainerResolver)\n                .getContainerHierarchy(new DataContainer(68L, \"SUBTASK\"));\n        //when\n        SDataInstance dataInstance = dataInstanceServiceImpl.getDataInstance(\"testData\", 68L, \"SUBTASK\",\n                parentContainerResolver);\n        //then\n        assertThat(dataInstance.getValue()).isEqualTo(\"SUBTASK_VALUE\");\n    }\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/test/java/org/bonitasoft/engine/data/instance/model/archive/impl/SAXMLDataInstanceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model.archive.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.io.Serializable;\n\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.SAXMLDataInstance;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class SAXMLDataInstanceImplTest {\n\n    @Mock\n    private SDataInstance dataInstance;\n\n    @Test\n    public void SAXMLDataInstanceImpl_should_return_given_value() {\n        //given\n        final Serializable givenValue = new String(\"serializable value\");\n        final SAXMLDataInstance sxmlObjectDataInstanceImpl = new SAXMLDataInstance(dataInstance);\n\n        //when\n        sxmlObjectDataInstanceImpl.setValue(givenValue);\n        final Serializable returnedValue = sxmlObjectDataInstanceImpl.getValue();\n\n        //then\n        assertThat(returnedValue).as(\"should be equal to given value\").isEqualTo(givenValue);\n    }\n\n    @Test\n    public void SAXMLDataInstanceImpl_should_return_null_when_no_given_value() {\n        //given\n        final SAXMLDataInstance sxmlObjectDataInstanceImpl = new SAXMLDataInstance(dataInstance);\n\n        //when\n        final Serializable returnedValue = sxmlObjectDataInstanceImpl.getValue();\n\n        //then\n        assertThat(returnedValue).as(\"should be not null\").isNull();\n    }\n\n    @Test\n    public void nameSpaceTest() {\n        //given\n        final String expected = \"namspace\";\n        final SAXMLDataInstance sxmlObjectDataInstanceImpl = new SAXMLDataInstance();\n\n        //when\n        sxmlObjectDataInstanceImpl.setNamespace(expected);\n        final String returnedValue = sxmlObjectDataInstanceImpl.getNamespace();\n\n        //then\n        assertThat(returnedValue).as(\"should be not null\").isSameAs(expected);\n    }\n\n    @Test\n    public void elementTest() {\n        //given\n        final String expected = \"namspace\";\n        final SAXMLDataInstance sxmlObjectDataInstanceImpl = new SAXMLDataInstance();\n\n        //when\n        sxmlObjectDataInstanceImpl.setElement(expected);\n        final String returnedValue = sxmlObjectDataInstanceImpl.getElement();\n\n        //then\n        assertThat(returnedValue).as(\"should be not null\").isSameAs(expected);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/test/java/org/bonitasoft/engine/data/instance/model/archive/impl/SAXMLObjectDataInstanceTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model.archive.impl;\n\nimport static org.junit.Assert.assertEquals;\n\nimport org.bonitasoft.engine.data.instance.model.SXMLObjectDataInstance;\nimport org.bonitasoft.engine.data.instance.model.archive.SAXMLObjectDataInstance;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Celine Souchet\n */\npublic class SAXMLObjectDataInstanceTest {\n\n    private SXMLObjectDataInstance sDataInstance;\n\n    @Before\n    public void setUp() {\n        sDataInstance = new SXMLObjectDataInstance();\n        sDataInstance.setValue(1);\n        sDataInstance.setClassName(Long.class.getName());\n        sDataInstance.setContainerId(2);\n        sDataInstance.setContainerType(\"containerType\");\n        sDataInstance.setDataTypeClassName(String.class.getName());\n        sDataInstance.setDescription(\"description\");\n        sDataInstance.setId(3);\n        sDataInstance.setName(\"name\");\n        sDataInstance.setTransientData(false);\n        sDataInstance.setValue(\"value\");\n    }\n\n    /**\n     * Test method for\n     * {@link SAXMLObjectDataInstance#SAXMLObjectDataInstance(org.bonitasoft.engine.data.instance.model.SDataInstance)}\n     * .\n     */\n    @Test\n    public final void sAXMLObjectDataInstanceImplSDataInstance() {\n        final SAXMLObjectDataInstance saxmlObjectDataInstance = new SAXMLObjectDataInstance(sDataInstance);\n        assertEquals(sDataInstance.getClassName(), saxmlObjectDataInstance.getClassName());\n        assertEquals(sDataInstance.getContainerId(), saxmlObjectDataInstance.getContainerId());\n        assertEquals(sDataInstance.getContainerType(), saxmlObjectDataInstance.getContainerType());\n        assertEquals(sDataInstance.getDescription(), saxmlObjectDataInstance.getDescription());\n        assertEquals(sDataInstance.getId(), saxmlObjectDataInstance.getSourceObjectId());\n        assertEquals(sDataInstance.getName(), saxmlObjectDataInstance.getName());\n        assertEquals(sDataInstance.getValue(), saxmlObjectDataInstance.getValue());\n    }\n\n    /**\n     * Test method for {@link SAXMLObjectDataInstance#getValue()}.\n     * Test method for {@link SAXMLObjectDataInstance#setValue(java.io.Serializable)}.\n     */\n    @Test\n    public final void getSetValue() {\n        final SAXMLObjectDataInstance saxmlObjectDataInstance = new SAXMLObjectDataInstance();\n        saxmlObjectDataInstance.setValue(\"plop\");\n        assertEquals(\"plop\", saxmlObjectDataInstance.getValue());\n    }\n\n    /**\n     * Test method for {@link SAXMLObjectDataInstance#getValue()}.\n     * Test method for {@link SAXMLObjectDataInstance#setValue(java.io.Serializable)}.\n     */\n    @Test\n    public final void getValueShouldBeNull() {\n        final SAXMLObjectDataInstance saxmlObjectDataInstance = new SAXMLObjectDataInstance();\n        assertEquals(null, saxmlObjectDataInstance.getValue());\n    }\n\n    @Test\n    public final void getSetId() {\n        final SAXMLObjectDataInstance saxmlObjectDataInstance = new SAXMLObjectDataInstance();\n        saxmlObjectDataInstance.setId(64);\n        assertEquals(64, saxmlObjectDataInstance.getId());\n    }\n\n    @Test\n    public final void getSetName() {\n        final SAXMLObjectDataInstance saxmlObjectDataInstance = new SAXMLObjectDataInstance();\n        saxmlObjectDataInstance.setName(\"plop5\");\n        assertEquals(\"plop5\", saxmlObjectDataInstance.getName());\n    }\n\n    @Test\n    public final void getDescription() {\n        final SAXMLObjectDataInstance saxmlObjectDataInstance = new SAXMLObjectDataInstance();\n        saxmlObjectDataInstance.setDescription(\"plop8\");\n        assertEquals(\"plop8\", saxmlObjectDataInstance.getDescription());\n    }\n\n    @Test\n    public final void isSetTransientData() {\n        final SAXMLObjectDataInstance saxmlObjectDataInstance = new SAXMLObjectDataInstance();\n        saxmlObjectDataInstance.setTransientData(true);\n        assertEquals(true, saxmlObjectDataInstance.isTransientData());\n    }\n\n    @Test\n    public final void getSetClassName() {\n        final SAXMLObjectDataInstance saxmlObjectDataInstance = new SAXMLObjectDataInstance();\n        saxmlObjectDataInstance.setClassName(\"plipou\");\n        assertEquals(\"plipou\", saxmlObjectDataInstance.getClassName());\n    }\n\n    @Test\n    public final void getSetContainerId() {\n        final SAXMLObjectDataInstance saxmlObjectDataInstance = new SAXMLObjectDataInstance();\n        saxmlObjectDataInstance.setContainerId(14);\n        assertEquals(14, saxmlObjectDataInstance.getContainerId());\n    }\n\n    @Test\n    public final void getSetContainerType() {\n        final SAXMLObjectDataInstance saxmlObjectDataInstance = new SAXMLObjectDataInstance();\n        saxmlObjectDataInstance.setContainerType(\"plipou95\");\n        assertEquals(\"plipou95\", saxmlObjectDataInstance.getContainerType());\n    }\n\n    @Test\n    public final void getArchiveDate() {\n        final SAXMLObjectDataInstance saxmlObjectDataInstance = new SAXMLObjectDataInstance();\n        saxmlObjectDataInstance.setArchiveDate(83);\n        assertEquals(83, saxmlObjectDataInstance.getArchiveDate());\n    }\n\n    @Test\n    public final void getSetSourceObjectId() {\n        final SAXMLObjectDataInstance saxmlObjectDataInstance = new SAXMLObjectDataInstance();\n        saxmlObjectDataInstance.setSourceObjectId(85);\n        assertEquals(85, saxmlObjectDataInstance.getSourceObjectId());\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/test/java/org/bonitasoft/engine/data/instance/model/builder/impl/SDataInstanceBuilderFactoryImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model.builder.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\nimport org.bonitasoft.engine.data.definition.model.STextDataDefinition;\nimport org.bonitasoft.engine.data.definition.model.SXMLDataDefinition;\nimport org.bonitasoft.engine.data.instance.model.SBlobDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SBooleanDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SDataInstanceBuilder;\nimport org.bonitasoft.engine.data.instance.model.SDateDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SDoubleDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SFloatDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SIntegerDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SLongDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SLongTextDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SShortTextDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SXMLDataInstance;\nimport org.bonitasoft.engine.data.instance.model.SXMLObjectDataInstance;\nimport org.bonitasoft.engine.data.instance.model.exceptions.SDataInstanceNotWellFormedException;\nimport org.junit.Test;\n\npublic class SDataInstanceBuilderFactoryImplTest {\n\n    @Test\n    public void createNewInstanceHandlesDateType() throws Exception {\n        testSpecificDataType(Date.class.getName(), SDateDataInstance.class);\n    }\n\n    @Test\n    public void createNewInstanceHandlesIntegerType() throws Exception {\n        testSpecificDataType(Integer.class.getName(), SIntegerDataInstance.class);\n    }\n\n    @Test\n    public void createNewInstanceHandlesLongType() throws Exception {\n        testSpecificDataType(Long.class.getName(), SLongDataInstance.class);\n    }\n\n    @Test\n    public void createNewInstanceHandlesStringType() throws Exception {\n        testSpecificDataType(String.class.getName(), SShortTextDataInstance.class);\n    }\n\n    @Test\n    public void createNewInstanceHandlesDoubleType() throws Exception {\n        testSpecificDataType(Double.class.getName(), SDoubleDataInstance.class);\n    }\n\n    @Test\n    public void createNewInstanceHandlesFloatType() throws Exception {\n        testSpecificDataType(Float.class.getName(), SFloatDataInstance.class);\n    }\n\n    @Test\n    public void createNewInstanceHandlesByteArrayAsBLOBType() throws Exception {\n        testSpecificDataType(byte[].class.getName(), SBlobDataInstance.class);\n    }\n\n    @Test\n    public void createNewInstanceHandlesBooleanType() throws Exception {\n        testSpecificDataType(Boolean.class.getName(), SBooleanDataInstance.class);\n    }\n\n    @Test\n    public void createNewInstanceHandlesCustomTypeAsXMLObject() throws Exception {\n        testSpecificDataType(Object.class.getName(), SXMLObjectDataInstance.class);\n    }\n\n    private void testSpecificDataType(final String dataTypeName, final Class<? extends SDataInstance> dataInstanceClass)\n            throws SDataInstanceNotWellFormedException {\n        final SDataDefinition dataDefinition = mock(SDataDefinition.class);\n        when(dataDefinition.getClassName()).thenReturn(dataTypeName);\n\n        final SDataInstanceBuilder newInstance = SDataInstanceBuilder.createNewInstance(dataDefinition);\n\n        assertThat(newInstance.done()).isInstanceOf(dataInstanceClass);\n    }\n\n    @Test\n    public void createNewInstanceHandlesXMLType() throws Exception {\n        assertThat(SDataInstanceBuilder.createNewInstance(mock(SXMLDataDefinition.class)).done())\n                .isInstanceOf(SXMLDataInstance.class);\n    }\n\n    @Test\n    public void createNewInstanceHandlesShortTextType() throws Exception {\n        assertThat(SDataInstanceBuilder.createNewInstance(mock(STextDataDefinition.class)).done()).isInstanceOf(\n                SShortTextDataInstance.class);\n    }\n\n    @Test\n    public void createNewInstanceHandlesLongTextType() throws Exception {\n        final STextDataDefinition dataDef = mock(STextDataDefinition.class);\n        when(dataDef.isLongText()).thenReturn(true);\n        assertThat(SDataInstanceBuilder.createNewInstance(dataDef).done()).isInstanceOf(SLongTextDataInstance.class);\n    }\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/test/java/org/bonitasoft/engine/data/instance/model/impl/OffsetDateTimeXStreamConverterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.time.LocalDateTime;\nimport java.time.OffsetDateTime;\nimport java.time.ZoneOffset;\n\nimport org.junit.Test;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class OffsetDateTimeXStreamConverterTest {\n\n    @Test\n    public void toString_should_OffsetDateTime_converted_to_UTC() throws Exception {\n        // given:\n        final OffsetDateTimeXStreamConverter offsetDateTimeConverter = new OffsetDateTimeXStreamConverter();\n        final OffsetDateTime offsetDateTime = OffsetDateTime.of(LocalDateTime.of(1973, 10, 17, 13, 42, 0),\n                ZoneOffset.ofHours(-4));\n\n        // when:\n        final String toString = offsetDateTimeConverter.toString(offsetDateTime);\n\n        // then:\n        assertThat(toString).isEqualTo(\"1973-10-17T17:42:00Z\");\n    }\n\n    @Test\n    public void fromString_should_reset_offset_to_UTC() throws Exception {\n        // given:\n        String dateAsString = \"1973-10-17T11:50:00-02:00\";\n        final OffsetDateTimeXStreamConverter offsetDateTimeConverter = new OffsetDateTimeXStreamConverter();\n\n        // when:\n        final Object offsetDateTime = offsetDateTimeConverter.fromString(dateAsString);\n\n        // then:\n        assertThat(offsetDateTime)\n                .isEqualTo(OffsetDateTime.of(LocalDateTime.of(1973, 10, 17, 13, 50, 0), ZoneOffset.UTC));\n    }\n\n    @Test\n    public void toString_should_return_null_for_null_input() throws Exception {\n        // given:\n        final OffsetDateTimeXStreamConverter offsetDateTimeConverter = new OffsetDateTimeXStreamConverter();\n\n        // when:\n        final String toString = offsetDateTimeConverter.toString(null);\n\n        // then:\n        assertThat(toString).isNull();\n    }\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/test/java/org/bonitasoft/engine/data/instance/model/impl/SXMLObjectDataInstanceTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.io.Serializable;\n\nimport org.bonitasoft.engine.data.definition.model.SDataDefinition;\nimport org.bonitasoft.engine.data.instance.model.SXMLObjectDataInstance;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class SXMLObjectDataInstanceTest {\n\n    @Mock\n    private SDataDefinition dataDefinition;\n\n    @Test\n    public void SXMLObjectDataInstanceImpl_should_return_given_value() {\n        //given\n        final Serializable givenValue = new String(\"serializable value\");\n\n        final SXMLObjectDataInstance sxmlObjectDataInstance = new SXMLObjectDataInstance(dataDefinition);\n\n        //when\n        sxmlObjectDataInstance.setValue(givenValue);\n        final Serializable returnedValue = sxmlObjectDataInstance.getValue();\n\n        //then\n        assertThat(returnedValue).as(\"should be equal to given value\").isEqualTo(givenValue);\n    }\n\n    @Test\n    public void SXMLObjectDataInstanceImpl_should_return_null_when_no_given_value() {\n        //given\n        final SXMLObjectDataInstance sxmlObjectDataInstance = new SXMLObjectDataInstance(dataDefinition);\n\n        //when\n        final Serializable returnedValue = sxmlObjectDataInstance.getValue();\n\n        //then\n        assertThat(returnedValue).as(\"should be null\").isNull();\n    }\n}\n"
  },
  {
    "path": "services/bonita-data-instance/src/test/java/org/bonitasoft/engine/data/instance/model/impl/XStreamFactoryTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.data.instance.model.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\n\nimport java.io.Serializable;\n\nimport com.thoughtworks.xstream.XStream;\nimport com.thoughtworks.xstream.security.ForbiddenClassException;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.RestoreSystemProperties;\nimport org.junit.contrib.java.lang.system.SystemErrRule;\n\npublic class XStreamFactoryTest {\n\n    @Rule\n    public RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties();\n\n    @Test\n    public void should_provide_xstream() {\n        //when\n        final XStream xStream = XStreamFactory.getXStream();\n\n        //then\n        assertThat(xStream).as(\"should provide xstream instance\").isNotNull();\n    }\n\n    @Test\n    public void should_create_xstream_only_once_in_same_classLoader() {\n        //when\n        final XStream xStream = XStreamFactory.getXStream();\n        final XStream xStream2 = XStreamFactory.getXStream();\n\n        //then\n        assertThat(xStream2).as(\"should provide the xstream instance when we are in the same classloader\")\n                .isSameAs(xStream);\n    }\n\n    @Rule\n    public SystemErrRule systemErrRule = new SystemErrRule().enableLog();\n\n    @Test\n    public void xStream_should_not_warn_about_security() throws Exception {\n        // given:\n        final XStream xStream = XStreamFactory.getXStream();\n\n        // when:\n        xStream.fromXML(\n                \"<org.bonitasoft.engine.commons.Container><id>17</id><type>executable</type></org.bonitasoft.engine.commons.Container>\");\n\n        // then:\n        assertThat(systemErrRule.getLog()).doesNotContain(\"XStream is probably vulnerable\");\n    }\n\n    @Test\n    public void xStream_should_ignoreUnknownElements() throws Exception {\n        // given:\n        final XStream xStream = XStreamFactory.getXStream();\n        var myData = new MyDataObject(\"Romain\", \"nope\");\n        var serialized = xStream.toXML(myData);\n        // Artificially add a field from serialized object\n        serialized = serialized.replace(\"<password>nope</password>\",\n                \"<password>nope</password>\\n<birthDate>1985-04-02</birthDate>\");\n\n        // when:\n        MyDataObject deserialized = (MyDataObject) xStream.fromXML(serialized);\n\n        // then:\n        assertThat(deserialized.getUsername()).isEqualTo(\"Romain\");\n        assertThat(deserialized.getPassword()).isEqualTo(\"nope\");\n    }\n\n    @Test\n    public void xStream_should_reject_commons_collections4_gadget_chain_types() {\n        // given:\n        final XStream xStream = XStreamFactory.getXStream();\n        String maliciousXml = \"<org.apache.commons.collections4.comparators.TransformingComparator>\"\n                + \"<decorated/><transformer/>\"\n                + \"</org.apache.commons.collections4.comparators.TransformingComparator>\";\n\n        // when / then:\n        assertThatThrownBy(() -> xStream.fromXML(maliciousXml))\n                .isInstanceOf(ForbiddenClassException.class);\n    }\n\n    @Test\n    public void xStream_should_reject_javax_script_gadget_chain_types() {\n        // given:\n        final XStream xStream = XStreamFactory.getXStream();\n        String maliciousXml = \"<javax.script.ScriptEngineManager/>\";\n\n        // when / then:\n        assertThatThrownBy(() -> xStream.fromXML(maliciousXml))\n                .isInstanceOf(ForbiddenClassException.class);\n    }\n\n    @Test\n    public void xStream_should_reject_java_net_URL_gadget_chain_types() {\n        // given:\n        final XStream xStream = XStreamFactory.getXStream();\n        String maliciousXml = \"<java.net.URL>\"\n                + \"<protocol>http</protocol>\"\n                + \"<host>attacker.example.com</host>\"\n                + \"<port>80</port>\"\n                + \"<file>/</file>\"\n                + \"</java.net.URL>\";\n\n        // when / then:\n        assertThatThrownBy(() -> xStream.fromXML(maliciousXml))\n                .isInstanceOf(ForbiddenClassException.class);\n    }\n\n    @Test\n    public void xStream_should_use_custom_deny_list_from_system_property() {\n        // given: custom deny list via system property\n        XStreamFactory.remove(Thread.currentThread().getContextClassLoader());\n        System.setProperty(\"bonita.xstream.deny.packages\", \"java.awt.**\");\n\n        // when:\n        final XStream xStream = XStreamFactory.getXStream();\n\n        // then: type matching the custom deny pattern should be rejected\n        assertThatThrownBy(() -> xStream.fromXML(\"<java.awt.Color/>\"))\n                .isInstanceOf(ForbiddenClassException.class);\n\n        // cleanup: remove cached instance so other tests get fresh defaults\n        XStreamFactory.remove(Thread.currentThread().getContextClassLoader());\n    }\n\n    static class MyDataObject implements Serializable {\n\n        private String username;\n\n        private String password;\n\n        public MyDataObject(String username, String password) {\n            this.username = username;\n            this.password = password;\n        }\n\n        public String getPassword() {\n            return password;\n        }\n\n        public void setPassword(String password) {\n            this.password = password;\n        }\n\n        public String getUsername() {\n            return username;\n        }\n\n        public void setUsername(String username) {\n            this.username = username;\n        }\n\n    }\n}\n"
  },
  {
    "path": "services/bonita-events/build.gradle",
    "content": "\n\ndependencies {\n    api project(':services:bonita-commons')\n    testImplementation libs.assertj\n    testImplementation libs.mockitoCore\n    testImplementation libs.logback\n}\n\ntasks.register(\"testsJar\", Jar) {\n    archiveClassifier = 'tests'\n    from(sourceSets.test.output)\n}\n\ngroup = 'org.bonitasoft.engine.events'\ndescription = 'Bonita Event'\npublishing {\n    publications {\n        mavenJava(MavenPublication) {\n            from project.components.java\n            artifact testsJar\n        }\n    }\n}\n"
  },
  {
    "path": "services/bonita-events/src/main/java/org/bonitasoft/engine/events/EventActionType.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.events;\n\n/**\n * @author Baptiste Mesta\n */\npublic enum EventActionType {\n\n    CREATED, UPDATED, DELETED\n\n}\n"
  },
  {
    "path": "services/bonita-events/src/main/java/org/bonitasoft/engine/events/EventService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.events;\n\nimport java.util.Set;\n\nimport org.bonitasoft.engine.events.model.HandlerRegistrationException;\nimport org.bonitasoft.engine.events.model.HandlerUnregistrationException;\nimport org.bonitasoft.engine.events.model.SEvent;\nimport org.bonitasoft.engine.events.model.SFireEventException;\nimport org.bonitasoft.engine.events.model.SHandler;\n\n/**\n * This is the manager of all the events triggered by other services. Handlers are registered into the Event service.\n * When a\n * service fire an event, it calls the right handler corresponding to the given Event.\n *\n * @author Christophe Havard\n * @author Baptiste Mesta\n * @since 6.0\n */\npublic interface EventService {\n\n    /**\n     * Fire the specified Event to the registered handlers.\n     *\n     * @param event\n     *        A specific Event\n     */\n    void fireEvent(final SEvent event) throws SFireEventException;\n\n    /**\n     * Allows to check if an handler is listening to this event type\n     *\n     * @param eventType\n     *        the type of the event\n     * @return\n     *         true if an handler is interested by the event having type eventType\n     */\n    boolean hasHandlers(final String eventType, EventActionType actionType);\n\n    /**\n     * Add the given handler to the Event Manager's handlers list.\n     *\n     * @param eventType The type of the event the handler is interested in.\n     * @param userHandler\n     *        The handler to register in the Event Manager\n     * @throws HandlerRegistrationException\n     */\n    void addHandler(final String eventType, final SHandler<SEvent> userHandler) throws HandlerRegistrationException;\n\n    /**\n     * Registers a handler for the given event type only if a handler with the same identifier\n     * is not already registered. This ensures idempotent registration in clustered environments\n     * where multiple nodes may attempt to register the same handler.\n     *\n     * @param eventType the event type to register the handler for\n     * @param handler the handler to register\n     * @throws HandlerRegistrationException if registration fails\n     * @since 10.0.9\n     */\n    void registerHandlerIfNotExists(String eventType, SHandler<SEvent> handler) throws HandlerRegistrationException;\n\n    /**\n     * Remove the given handler from the Event Service's handlers lists.\n     *\n     * @param handler\n     *        The handler to remove\n     */\n    void removeAllHandlers(final SHandler<SEvent> handler) throws HandlerUnregistrationException;\n\n    /**\n     * Remove the given handler from the given event type filter\n     *\n     * @param handler\n     *        The handler to remove from the given event type\n     */\n    void removeHandler(final String eventType, final SHandler<SEvent> handler) throws HandlerUnregistrationException;\n\n    /**\n     * Retrieve the list of all registered Handlers or the given EventType\n     */\n    Set<SHandler<SEvent>> getHandlers(String eventType);\n\n}\n"
  },
  {
    "path": "services/bonita-events/src/main/java/org/bonitasoft/engine/events/impl/AbstractEventServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.events.impl;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.commons.ExceptionUtils;\nimport org.bonitasoft.engine.events.EventActionType;\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.events.model.HandlerRegistrationException;\nimport org.bonitasoft.engine.events.model.HandlerUnregistrationException;\nimport org.bonitasoft.engine.events.model.SEvent;\nimport org.bonitasoft.engine.events.model.SFireEventException;\nimport org.bonitasoft.engine.events.model.SHandler;\nimport org.slf4j.Logger;\n\npublic abstract class AbstractEventServiceImpl implements EventService {\n\n    protected abstract Logger getLogger();\n\n    protected AbstractEventServiceImpl() {\n    }\n\n    /**\n     * Fire the given Event only to interested handlers\n     */\n    @Override\n    public void fireEvent(final SEvent event) throws SFireEventException {\n        if (event == null) {\n            throw new SFireEventException(\"Event is null\");\n        }\n        // if at least 1 eventFilter contains a group of handlers for the given event type\n        if (containsHandlerFor(event.getType())) {\n            // retrieve the handler list concerned by the given event\n            final Collection<SHandler<SEvent>> handlers = getHandlersFor(event.getType());\n\n            if (!handlers.isEmpty()) {\n                if (getLogger().isTraceEnabled()) {\n                    getLogger().trace(\"Found {} for event {}. All handlers: {}\", handlers.size(), event.getType(),\n                            handlers);\n                }\n                SFireEventException sFireEventException = null;\n                for (final SHandler<SEvent> handler : handlers) {\n                    // for each handler, I check if it's interested or not by the given event\n                    try {\n                        if (handler.isInterested(event)) {\n                            handler.execute(event);\n                        }\n                    } catch (final Exception e) {\n                        if (sFireEventException == null) {\n                            sFireEventException = new SFireEventException(\"Unable to execute some handler.\", e);\n                        }\n                        sFireEventException.addHandlerException(e);\n                        if (getLogger().isDebugEnabled()) {\n                            getLogger().debug(\"Handler failed {}\", ExceptionUtils.printLightWeightStacktrace(e));\n                        }\n                    }\n                }\n                if (sFireEventException != null) {\n                    throw sFireEventException;\n                }\n            }\n        }\n    }\n\n    protected abstract Collection<SHandler<SEvent>> getHandlersFor(final String type);\n\n    protected abstract boolean containsHandlerFor(final String type);\n\n    /**\n     * No handler duplication in a list for a given event type\n     */\n    @Override\n    public final void addHandler(final String eventType, final SHandler<SEvent> handler)\n            throws HandlerRegistrationException {\n        if (handler != null && eventType != null) {\n            addHandlerFor(eventType, handler);\n        } else {\n            throw new HandlerRegistrationException(\n                    \"One of the parameters is null : \" + \" eventType: \" + eventType + \" handler:\" + handler);\n        }\n    }\n\n    @Override\n    public void registerHandlerIfNotExists(String eventType, SHandler<SEvent> handler)\n            throws HandlerRegistrationException {\n        // Check if a handler with the same identifier already exists\n        boolean alreadyRegistered = getHandlers(eventType).stream()\n                .anyMatch(h -> h.getIdentifier().equals(handler.getIdentifier()));\n\n        if (!alreadyRegistered) {\n            addHandler(eventType, handler);\n            getLogger().debug(\"Registered handler {} for event type {}\", handler.getIdentifier(), eventType);\n        } else {\n            getLogger().debug(\"Handler {} already registered for event type {}. Ignoring registration.\",\n                    handler.getIdentifier(), eventType);\n        }\n    }\n\n    protected abstract void addHandlerFor(String eventType, SHandler<SEvent> handler)\n            throws HandlerRegistrationException;\n\n    @Override\n    public final void removeAllHandlers(final SHandler<SEvent> handler) throws HandlerUnregistrationException {\n        if (handler == null) {\n            throw new HandlerUnregistrationException(\"Unable to remove a null handler\");\n        }\n        removeAllHandlersFor(handler);\n    }\n\n    protected abstract void removeAllHandlersFor(SHandler<SEvent> handler);\n\n    @Override\n    public final void removeHandler(final String eventType, final SHandler<SEvent> h)\n            throws HandlerUnregistrationException {\n        if (h == null || eventType == null) {\n            throw new HandlerUnregistrationException(\"Unable to remove a null handler\");\n        }\n        removeHandlerFor(eventType, h);\n    }\n\n    protected abstract void removeHandlerFor(final String eventType, final SHandler<SEvent> h)\n            throws HandlerUnregistrationException;\n\n    @Override\n    public final Set<SHandler<SEvent>> getHandlers(final String eventType) {\n        final Collection<SHandler<SEvent>> handlers = getHandlersFor(eventType);\n        if (handlers == null) {\n            return Collections.emptySet();\n        }\n        return new HashSet<>(handlers);\n    }\n\n    @Override\n    public final boolean hasHandlers(final String eventType, final EventActionType actionType) {\n        String key = eventType;\n        if (actionType != null) {\n            switch (actionType) {\n                case CREATED:\n                    key += SEvent.CREATED;\n                    break;\n                case DELETED:\n                    key += SEvent.DELETED;\n                    break;\n                case UPDATED:\n                    key += SEvent.UPDATED;\n                    break;\n                default:\n                    return false;\n            }\n        }\n        return containsHandlerFor(key);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-events/src/main/java/org/bonitasoft/engine/events/impl/EventServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.events.impl;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.events.model.HandlerRegistrationException;\nimport org.bonitasoft.engine.events.model.HandlerUnregistrationException;\nimport org.bonitasoft.engine.events.model.SEvent;\nimport org.bonitasoft.engine.events.model.SHandler;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Christophe Havard\n * @author Matthieu Chaffotte\n * @author Laurent Vaills\n */\n\npublic class EventServiceImpl extends AbstractEventServiceImpl {\n\n    private final Logger logger = LoggerFactory.getLogger(EventServiceImpl.class);\n    /**\n     * Contains a list of all events type and their registered handlers\n     */\n    protected Map<String, List<SHandler<SEvent>>> registeredHandlers;\n\n    @Override\n    protected Logger getLogger() {\n        return logger;\n    }\n\n    public EventServiceImpl() {\n        super();\n        registeredHandlers = new HashMap<String, List<SHandler<SEvent>>>();\n    }\n\n    @Override\n    protected boolean containsHandlerFor(final String key) {\n        return registeredHandlers.containsKey(key);\n    }\n\n    @Override\n    protected Collection<SHandler<SEvent>> getHandlersFor(final String eventType) {\n        return registeredHandlers.get(eventType);\n    }\n\n    @Override\n    protected void addHandlerFor(final String eventType, final SHandler<SEvent> handler)\n            throws HandlerRegistrationException {\n        // check if the given event type is already registered in the Event Service\n        if (containsHandlerFor(eventType)) {\n            // if the handler already exists for the same eventType, an Exception is thrown\n            final List<SHandler<SEvent>> handlers = registeredHandlers.get(eventType);\n\n            // Check if another handler of the same class is already registered\n            for (SHandler<SEvent> tmpHandler : handlers) {\n                if (tmpHandler.getIdentifier().equals(handler.getIdentifier())) {\n                    throw new HandlerRegistrationException(\"The handler with identifier \" + tmpHandler.getIdentifier()\n                            + \" is already registered for the event \" + eventType);\n                }\n            }\n\n            handlers.add(handler);\n        } else {\n            // if the given type doesn't already exist in the eventFilters list, we create it\n            final List<SHandler<SEvent>> newHandlerList = new ArrayList<SHandler<SEvent>>(3);\n            newHandlerList.add(handler);\n            registeredHandlers.put(eventType, newHandlerList);\n        }\n    }\n\n    @Override\n    protected void removeAllHandlersFor(final SHandler<SEvent> handler) {\n        for (final String eventType : registeredHandlers.keySet()) {\n            try {\n                removeHandler(eventType, handler);\n            } catch (HandlerUnregistrationException e) {\n                // Nothing to do.\n            }\n        }\n    }\n\n    @Override\n    protected void removeHandlerFor(final String eventType, final SHandler<SEvent> h)\n            throws HandlerUnregistrationException {\n        boolean removed = false;\n        Collection<SHandler<SEvent>> handlers = getHandlersFor(eventType);\n        if (handlers != null) {\n            Iterator<SHandler<SEvent>> it = handlers.iterator();\n            while (!removed && it.hasNext()) {\n                SHandler<SEvent> handler = it.next();\n                if (h.getIdentifier().equals(handler.getIdentifier())) {\n                    it.remove();\n                    removed = true;\n                }\n            }\n        }\n        if (!removed) {\n            throw new HandlerUnregistrationException(\"Handler did not exists\");\n        }\n    }\n}\n"
  },
  {
    "path": "services/bonita-events/src/main/java/org/bonitasoft/engine/events/model/HandlerRegistrationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.events.model;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Christophe Havard\n */\npublic class HandlerRegistrationException extends SBonitaException {\n\n    private static final long serialVersionUID = -8854372636800811528L;\n\n    public HandlerRegistrationException() {\n        super();\n    }\n\n    public HandlerRegistrationException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public HandlerRegistrationException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-events/src/main/java/org/bonitasoft/engine/events/model/HandlerUnregistrationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.events.model;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Christophe Havard\n */\npublic class HandlerUnregistrationException extends SBonitaException {\n\n    private static final long serialVersionUID = -6460850460472780165L;\n\n    public HandlerUnregistrationException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "services/bonita-events/src/main/java/org/bonitasoft/engine/events/model/SDeleteEvent.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.events.model;\n\n/**\n * Represent the deletion of a persisted element\n */\npublic class SDeleteEvent extends SEvent {\n\n    public SDeleteEvent(String type) {\n        super(type);\n    }\n}\n"
  },
  {
    "path": "services/bonita-events/src/main/java/org/bonitasoft/engine/events/model/SEvent.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.events.model;\n\nimport org.apache.commons.lang3.builder.EqualsBuilder;\nimport org.apache.commons.lang3.builder.HashCodeBuilder;\nimport org.apache.commons.lang3.builder.ToStringBuilder;\nimport org.apache.commons.lang3.builder.ToStringStyle;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SEvent {\n\n    public static final String UPDATED = \"_UPDATED\";\n    public static final String CREATED = \"_CREATED\";\n    public static final String DELETED = \"_DELETED\";\n    private final String type;\n    private Object object;\n\n    public SEvent(String type) {\n        this.type = type;\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    /**\n     * Retrieve the object which the event occurred on\n     */\n    public Object getObject() {\n        return object;\n    }\n\n    /**\n     * Set the object passed inside the fired Event\n     */\n    public void setObject(Object object) {\n        this.object = object;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (o == null || getClass() != o.getClass())\n            return false;\n        SEvent sEvent = (SEvent) o;\n        return new EqualsBuilder()\n                .append(type, sEvent.type)\n                .append(object, sEvent.object)\n                .isEquals();\n    }\n\n    @Override\n    public int hashCode() {\n        return new HashCodeBuilder(17, 37)\n                .append(type)\n                .append(object)\n                .toHashCode();\n    }\n\n    @Override\n    public String toString() {\n        return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)\n                .append(\"type\", type)\n                .append(\"object\", object)\n                .toString();\n    }\n}\n"
  },
  {
    "path": "services/bonita-events/src/main/java/org/bonitasoft/engine/events/model/SFireEventException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.events.model;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Christophe Havard\n * @author Celine Souchet\n */\npublic class SFireEventException extends SBonitaException {\n\n    private static final long serialVersionUID = 752699780388742999L;\n\n    private List<Exception> handlerExceptions = new ArrayList<>();\n\n    public SFireEventException(String message) {\n        super(message);\n    }\n\n    public SFireEventException(final String message, Exception firstCause) {\n        super(message, firstCause);\n    }\n\n    public void addHandlerException(final Exception e) {\n        handlerExceptions.add(e);\n    }\n\n    public List<Exception> getHandlerExceptions() {\n        return handlerExceptions;\n    }\n\n    @Override\n    public String getMessage() {\n        return super.getMessage() + handlerExceptions.stream()\n                .map(e -> e.getClass().getName() + \": \" + e.getMessage())\n                .collect(Collectors.joining(\"\\n\", \" [\", \"]\"));\n    }\n}\n"
  },
  {
    "path": "services/bonita-events/src/main/java/org/bonitasoft/engine/events/model/SHandler.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.events.model;\n\nimport java.io.Serializable;\n\n/**\n * @author Christophe Havard\n * @author Matthieu Chaffotte\n */\npublic interface SHandler<T extends SEvent> extends Serializable {\n\n    /**\n     * Performs the action corresponding to the given Event\n     */\n    void execute(T event) throws SHandlerExecutionException;\n\n    /**\n     * Precise if the current Handler is interested by the given event.\n     * If so, it could run the execute(SEvent e) method.\n     */\n    boolean isInterested(T event);\n\n    /**\n     * Returns a unique identifier for each instance of the Handler\n     */\n    String getIdentifier();\n}\n"
  },
  {
    "path": "services/bonita-events/src/main/java/org/bonitasoft/engine/events/model/SHandlerExecutionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.events.model;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SHandlerExecutionException extends SBonitaException {\n\n    private static final long serialVersionUID = 297807701528986021L;\n\n    public SHandlerExecutionException(final SBonitaException e) {\n        super(e);\n    }\n\n    public SHandlerExecutionException(final String message, final SBonitaException cause) {\n        super(message, cause);\n    }\n\n    public SHandlerExecutionException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SHandlerExecutionException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-events/src/main/java/org/bonitasoft/engine/events/model/SInsertEvent.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.events.model;\n\npublic class SInsertEvent extends SEvent {\n\n    public SInsertEvent(String type) {\n        super(type);\n    }\n}\n"
  },
  {
    "path": "services/bonita-events/src/main/java/org/bonitasoft/engine/events/model/SUpdateEvent.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.events.model;\n\nimport java.util.Map;\n\npublic class SUpdateEvent extends SEvent {\n\n    private Object oldObject;\n    private Map<String, Object> updatedFields;\n\n    public SUpdateEvent(String type) {\n        super(type);\n    }\n\n    @Deprecated\n    public Object getOldObject() {\n        return oldObject;\n    }\n\n    public Map<String, Object> getUpdatedFields() {\n        return updatedFields;\n    }\n\n    @Deprecated\n    public void setOldObject(Object oldObject) {\n        this.oldObject = oldObject;\n    }\n\n    public void setUpdatedFields(Map<String, Object> updatedFields) {\n        this.updatedFields = updatedFields;\n    }\n}\n"
  },
  {
    "path": "services/bonita-events/src/test/java/org/bonitasoft/engine/events/EventServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.events;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.events.impl.EventServiceImpl;\nimport org.bonitasoft.engine.events.model.HandlerRegistrationException;\nimport org.bonitasoft.engine.events.model.HandlerUnregistrationException;\nimport org.bonitasoft.engine.events.model.SFireEventException;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Laurent Vaills\n */\npublic class EventServiceImplTest {\n\n    protected EventService instantiateEventServiceImplementation() {\n        return new EventServiceImpl();\n    }\n\n    private EventService eventSvc;\n\n    // Test events\n    private final String EVT_INTERESTING = \"INTERESTING\";\n\n    private final String EVT_IRRELEVANT = \"IRRELEVANT\";\n\n    @Before\n    public void beforeEachTest() {\n        eventSvc = instantiateEventServiceImplementation();\n    }\n\n    @Test(expected = SFireEventException.class)\n    public void fireNullEvent() throws Exception {\n        eventSvc.fireEvent(null);\n    }\n\n    @Test(expected = HandlerRegistrationException.class)\n    public void addHandlerNull() throws Exception {\n        eventSvc.addHandler(EVT_INTERESTING, null);\n    }\n\n    @Test(expected = HandlerRegistrationException.class)\n    public void addTwiceTheSameHandler() throws Exception {\n        final TestHandler h = new TestHandler();\n        eventSvc.addHandler(EVT_INTERESTING, h);\n        assertThat(eventSvc.getHandlers(EVT_INTERESTING)).hasSize(1);\n        eventSvc.addHandler(EVT_INTERESTING, h);\n    }\n\n    @Test\n    public void addHandlerInEventFilters() throws Exception {\n        final TestHandler h = new TestHandler();\n        eventSvc.addHandler(EVT_INTERESTING, h);\n        assertThat(eventSvc.getHandlers(EVT_INTERESTING)).contains(h);\n        eventSvc.removeHandler(EVT_INTERESTING, h);\n    }\n\n    @Test\n    public void addNewTypeInRegisteredHandlers() throws Exception {\n        assertThat(eventSvc.getHandlers(EVT_INTERESTING)).isEmpty();\n\n        final TestHandler h = new TestHandler();\n        eventSvc.addHandler(EVT_INTERESTING, h);\n        assertThat(eventSvc.getHandlers(EVT_INTERESTING)).isNotEmpty();\n\n        eventSvc.removeHandler(EVT_INTERESTING, h);\n    }\n\n    @Test(expected = HandlerUnregistrationException.class)\n    public void removeUnknownHandler() throws Exception {\n        final TestHandler h = new TestHandler();\n        eventSvc.removeHandler(EVT_INTERESTING, h);\n    }\n\n    @Test\n    public void removeHandler() throws Exception {\n        final TestHandler h = new TestHandler();\n        eventSvc.addHandler(EVT_INTERESTING, h);\n        assertThat(eventSvc.getHandlers(EVT_INTERESTING)).hasSize(1);\n\n        eventSvc.removeHandler(EVT_INTERESTING, h);\n        assertThat(eventSvc.getHandlers(EVT_INTERESTING)).isEmpty();\n    }\n\n    @Test\n    public void isEventFiltered()\n            throws HandlerRegistrationException, SFireEventException, HandlerUnregistrationException {\n        // Register handler on a given event type.\n        final TestHandlerCallback h = new TestHandlerCallback();\n        eventSvc.addHandler(EVT_INTERESTING, h);\n\n        // Fire 2 different events\n        final TestEvent interesting = new TestEvent(EVT_INTERESTING);\n        final TestEvent irrelevant = new TestEvent(EVT_IRRELEVANT);\n        eventSvc.fireEvent(interesting);\n        eventSvc.fireEvent(irrelevant);\n\n        // Check that only \"interesting\" events have been received by the registered handler\n        assertThat(irrelevant.isFlagged()).isFalse();\n        assertThat(interesting.isFlagged()).isTrue();\n\n        eventSvc.removeHandler(EVT_INTERESTING, h);\n    }\n\n    @Test\n    public void isEventReceivedByHandler()\n            throws SFireEventException, HandlerRegistrationException, HandlerUnregistrationException {\n        final TestHandlerCallback h = new TestHandlerCallback();\n        final TestEvent interesting = new TestEvent(EVT_INTERESTING);\n\n        eventSvc.addHandler(EVT_INTERESTING, h);\n        eventSvc.fireEvent(interesting);\n\n        assertThat(interesting.isFlagged()).isTrue();\n        eventSvc.removeHandler(EVT_INTERESTING, h);\n    }\n\n    @Test\n    public void getAllHandlersByEvent() throws HandlerRegistrationException, HandlerUnregistrationException {\n        assertThat(eventSvc.getHandlers(EVT_INTERESTING)).isEmpty();\n\n        // add 2 different handlers for 1 event type\n        final TestHandler h1 = new TestHandler();\n        final TestHandler h2 = new TestHandler();\n\n        eventSvc.addHandler(EVT_INTERESTING, h1);\n        eventSvc.addHandler(EVT_INTERESTING, h2);\n\n        // now i check if the evtList contains my both handlers\n        assertThat(eventSvc.getHandlers(EVT_INTERESTING)).isNotEmpty();\n\n        eventSvc.removeHandler(EVT_INTERESTING, h1);\n        eventSvc.removeHandler(EVT_INTERESTING, h2);\n    }\n\n    @Test\n    public void registerHandlerIfNotExists_shouldRegisterWhenNoHandlerExists() throws HandlerRegistrationException {\n        assertThat(eventSvc.getHandlers(EVT_INTERESTING)).isEmpty();\n\n        final TestHandler handler = new TestHandler();\n        eventSvc.registerHandlerIfNotExists(EVT_INTERESTING, handler);\n\n        assertThat(eventSvc.getHandlers(EVT_INTERESTING)).hasSize(1);\n        assertThat(eventSvc.getHandlers(EVT_INTERESTING)).contains(handler);\n    }\n\n    @Test\n    public void registerHandlerIfNotExists_shouldNotRegisterWhenHandlerAlreadyExists()\n            throws HandlerRegistrationException {\n        final TestHandler handler = new TestHandler();\n\n        // Register handler first time\n        eventSvc.registerHandlerIfNotExists(EVT_INTERESTING, handler);\n        assertThat(eventSvc.getHandlers(EVT_INTERESTING)).hasSize(1);\n\n        // Try to register same handler again (same identifier)\n        eventSvc.registerHandlerIfNotExists(EVT_INTERESTING, handler);\n\n        // Should still have only 1 handler\n        assertThat(eventSvc.getHandlers(EVT_INTERESTING)).hasSize(1);\n    }\n\n    @Test\n    public void registerHandlerIfNotExists_shouldRegisterDifferentHandlers() throws HandlerRegistrationException {\n        // Create handlers with explicit different identifiers\n        final TestHandler handler1 = new TestHandler(\"handler-1\");\n        final TestHandler handler2 = new TestHandler(\"handler-2\");\n\n        eventSvc.registerHandlerIfNotExists(EVT_INTERESTING, handler1);\n        eventSvc.registerHandlerIfNotExists(EVT_INTERESTING, handler2);\n\n        // Should have 2 handlers with different identifiers\n        assertThat(eventSvc.getHandlers(EVT_INTERESTING)).hasSize(2);\n    }\n\n    @Test\n    public void registerHandlerIfNotExists_shouldBeIdempotentAcrossMultipleCalls()\n            throws HandlerRegistrationException {\n        final TestHandler handler = new TestHandler();\n\n        // Register multiple times\n        eventSvc.registerHandlerIfNotExists(EVT_INTERESTING, handler);\n        eventSvc.registerHandlerIfNotExists(EVT_INTERESTING, handler);\n        eventSvc.registerHandlerIfNotExists(EVT_INTERESTING, handler);\n\n        // Should still have only 1 handler\n        assertThat(eventSvc.getHandlers(EVT_INTERESTING)).hasSize(1);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-events/src/test/java/org/bonitasoft/engine/events/TestEvent.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.events;\n\nimport org.bonitasoft.engine.events.model.SEvent;\n\nclass TestEvent extends SEvent {\n\n    private boolean flagged = false;\n\n    TestEvent(String type) {\n        super(type);\n    }\n\n    void flag() {\n        this.flagged = true;\n    }\n\n    boolean isFlagged() {\n        return flagged;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-events/src/test/java/org/bonitasoft/engine/events/TestHandler.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.events;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.UUID;\n\nimport org.bonitasoft.engine.events.model.SEvent;\nimport org.bonitasoft.engine.events.model.SHandler;\n\n/**\n * @author Christophe Havard\n */\npublic class TestHandler implements SHandler<SEvent> {\n\n    private static final long serialVersionUID = 1L;\n\n    private boolean isCalled = false;\n\n    private List<SEvent> receivedEvent;\n\n    private final String identifier;\n\n    public TestHandler() {\n        this(UUID.randomUUID().toString());\n    }\n\n    public TestHandler(final String identifier) {\n        this.identifier = identifier;\n    }\n\n    @Override\n    public void execute(final SEvent event) {\n        setCalled(true);\n        if (receivedEvent == null) {\n            receivedEvent = new ArrayList<SEvent>();\n        }\n        receivedEvent.add(event);\n    }\n\n    public void setCalled(final boolean isCalled) {\n        this.isCalled = isCalled;\n    }\n\n    public boolean isCalled() {\n        return isCalled;\n    }\n\n    public List<SEvent> getReceivedEvents() {\n        return receivedEvent;\n    }\n\n    @Override\n    public boolean isInterested(final SEvent event) {\n        return \"INTERESTING\".equals(event.getType());\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + ((identifier == null) ? 0 : identifier.hashCode());\n        result = prime * result + (isCalled ? 1231 : 1237);\n        result = prime * result + ((receivedEvent == null) ? 0 : receivedEvent.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        TestHandler other = (TestHandler) obj;\n        if (identifier == null) {\n            if (other.identifier != null) {\n                return false;\n            }\n        } else if (!identifier.equals(other.identifier)) {\n            return false;\n        }\n        if (isCalled != other.isCalled) {\n            return false;\n        }\n        if (receivedEvent == null) {\n            if (other.receivedEvent != null) {\n                return false;\n            }\n        } else if (!receivedEvent.equals(other.receivedEvent)) {\n            return false;\n        }\n        return true;\n    }\n\n    @Override\n    public String getIdentifier() {\n        return identifier;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-events/src/test/java/org/bonitasoft/engine/events/TestHandlerCallback.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.events;\n\nimport java.util.UUID;\n\nimport org.bonitasoft.engine.events.model.SEvent;\nimport org.bonitasoft.engine.events.model.SHandler;\n\n/**\n * @author Christophe Havard\n */\npublic class TestHandlerCallback implements SHandler<SEvent> {\n\n    private static final long serialVersionUID = 1L;\n\n    private final String identifier;\n\n    public TestHandlerCallback() {\n        this(UUID.randomUUID().toString());\n    }\n\n    public TestHandlerCallback(final String identifier) {\n        this.identifier = identifier;\n    }\n\n    @Override\n    public void execute(final SEvent event) {\n        TestEvent testEvent = (TestEvent) event;\n        testEvent.flag();\n    }\n\n    @Override\n    public boolean isInterested(final SEvent event) {\n        return \"INTERESTING\".equals(event.getType());\n    }\n\n    @Override\n    public String getIdentifier() {\n        return identifier;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/build.gradle",
    "content": "\n\ndependencies {\n    api project(':services:bonita-session')\n    api project(':services:bonita-commons')\n    api project(':services:bonita-cache')\n    api project(':services:bonita-time-tracker')\n    api project(':services:bonita-classloader')\n    compileOnly libs.lombok\n    api libs.bundles.groovy\n    api libs.commonsLang\n    api libs.commonsText\n    testImplementation libs.logback\n    testImplementation libs.mockitoCore\n    testImplementation libs.assertj\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/ContainerState.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression;\n\n/**\n * @author Celine Souchet\n */\npublic enum ContainerState {\n    ARCHIVED, ACTIVE;\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/ExpressionExecutor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\n/**\n * @author Zhao Na\n */\npublic interface ExpressionExecutor {\n\n    Serializable evaluate(final String expressionContent, final Map<String, Object> map);\n\n    Boolean validate(final String expressionContent);\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/ExpressionExecutorStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.ExpressionKind;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * Allow to evaluate one kind of expression\n * the kind of expression that this evaluator is responsible for is define by the {@link #getExpressionKind()} Client\n * implements this interface in order to add\n * a new kind of expression\n *\n * @author Zhao Na\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic interface ExpressionExecutorStrategy {\n\n    String DEFINITION_ID = \"processDefinitionId\";// hum should not be process here\n\n    String CONTAINER_ID_KEY = \"containerId\";\n\n    String CONTAINER_TYPE_KEY = \"containerType\";\n\n    String INTERPRETER_GROOVY = \"GROOVY\";\n\n    String TYPE_CONSTANT = \"TYPE_CONSTANT\";\n\n    String TYPE_INPUT = \"TYPE_INPUT\";\n\n    String TYPE_READ_ONLY_SCRIPT = \"TYPE_READ_ONLY_SCRIPT\";\n\n    String TYPE_READ_ONLY_CONDITION_SCRIPT = \"TYPE_READ_ONLY__CONDITION_SCRIPT\";\n\n    String TYPE_VARIABLE = \"TYPE_VARIABLE\";\n\n    String TYPE_TRANSIENT_VARIABLE = \"TYPE_TRANSIENT_VARIABLE\";\n\n    String TYPE_PATTERN = \"TYPE_PATTERN\";\n\n    String TYPE_JAVA_METHOD_CALL = \"TYPE_JAVA_METHOD_CALL\";\n\n    String TYPE_PARAMETER = \"TYPE_PARAMETER\";\n\n    String TYPE_DOCUMENT = \"TYPE_DOCUMENT\";\n\n    String TYPE_DOCUMENT_LIST = \"TYPE_DOCUMENT_LIST\";\n\n    String TYPE_ENGINE_CONSTANT = \"TYPE_ENGINE_CONSTANT\";\n\n    String TYPE_LIST = \"TYPE_LIST\";\n\n    String TYPE_XPATH_READ = \"TYPE_XPATH_READ\";\n\n    String TYPE_BUSINESS_DATA = \"TYPE_BUSINESS_DATA\";\n\n    String TYPE_BUSINESS_DATA_REFERENCE = \"TYPE_BUSINESS_DATA_REFERENCE\";\n\n    String TYPE_BUSINESS_OBJECT_DAO = \"TYPE_BUSINESS_OBJECT_DAO\";\n\n    String TYPE_QUERY_BUSINESS_DATA = \"TYPE_QUERY_BUSINESS_DATA\";\n\n    String TYPE_CONTRACT_INPUT = \"TYPE_CONTRACT_INPUT\";\n\n    ExpressionKind KIND_CONSTANT = new ExpressionKind(TYPE_CONSTANT);\n\n    ExpressionKind KIND_READ_ONLY_SCRIPT_GROOVY = new ExpressionKind(TYPE_READ_ONLY_SCRIPT, INTERPRETER_GROOVY);\n\n    ExpressionKind KIND_READ_ONLY_CONDITION_SCRIPT_GROOVY = new ExpressionKind(TYPE_READ_ONLY_CONDITION_SCRIPT,\n            INTERPRETER_GROOVY);\n\n    ExpressionKind KIND_INPUT = new ExpressionKind(TYPE_INPUT);\n\n    ExpressionKind KIND_VARIABLE = new ExpressionKind(TYPE_VARIABLE);\n\n    ExpressionKind KIND_TRANSIENT_VARIABLE = new ExpressionKind(TYPE_TRANSIENT_VARIABLE);\n\n    ExpressionKind KIND_PATTERN = new ExpressionKind(TYPE_PATTERN);\n\n    ExpressionKind KIND_JAVA_METHOD_CALL = new ExpressionKind(TYPE_JAVA_METHOD_CALL);\n\n    ExpressionKind KIND_PARAMETER = new ExpressionKind(TYPE_PARAMETER);\n\n    ExpressionKind KIND_DOCUMENT = new ExpressionKind(TYPE_DOCUMENT);\n\n    ExpressionKind KIND_DOCUMENT_LIST = new ExpressionKind(TYPE_DOCUMENT_LIST);\n\n    ExpressionKind KIND_ENGINE_CONSTANT = new ExpressionKind(TYPE_ENGINE_CONSTANT);\n\n    ExpressionKind KIND_LIST = new ExpressionKind(TYPE_LIST);\n\n    ExpressionKind KIND_XPATH_READ = new ExpressionKind(TYPE_XPATH_READ);\n\n    ExpressionKind KIND_BUSINESS_DATA = new ExpressionKind(TYPE_BUSINESS_DATA);\n\n    ExpressionKind KIND_BUSINESS_DATA_REFERENCE = new ExpressionKind(TYPE_BUSINESS_DATA_REFERENCE);\n\n    ExpressionKind KIND_BUSINESS_OBJECT_DAO = new ExpressionKind(TYPE_BUSINESS_OBJECT_DAO);\n\n    ExpressionKind KIND_QUERY_BUSINESS_DATA = new ExpressionKind(TYPE_QUERY_BUSINESS_DATA);\n\n    ExpressionKind KIND_CONTRACT_INPUT = new ExpressionKind(TYPE_CONTRACT_INPUT);\n\n    /**\n     * This list must contain only types with no dependencies\n     */\n    List<ExpressionKind> NO_DEPENDENCY_EXPRESSION_EVALUATION_ORDER = Arrays\n            .asList(KIND_ENGINE_CONSTANT, KIND_VARIABLE, KIND_CONSTANT, KIND_INPUT,\n                    KIND_PARAMETER, KIND_DOCUMENT, KIND_BUSINESS_DATA, KIND_BUSINESS_OBJECT_DAO,\n                    KIND_CONTRACT_INPUT /*\n                                         * , KIND_PATTERN, KIND_READ_ONLY_SCRIPT_GROOVY,\n                                         * KIND_LIST\n                                         */);\n\n    /**\n     * @param expression\n     *        the expression to evaluate\n     * @param context\n     *        map containing the result of the evaluation of dependencies\n     *        and also informations about the context of evaluation given by {@link #CONTAINER_ID_KEY} and\n     *        {@link #CONTAINER_TYPE_KEY}\n     * @return\n     *         the result of the evaluation of the expression of appropriate type\n     * @throws SExpressionEvaluationException\n     * @throws SExpressionDependencyMissingException\n     */\n    Object evaluate(SExpression expression, Map<String, Object> context, Map<Integer, Object> resolvedExpressions,\n            ContainerState containerState)\n            throws SExpressionEvaluationException, SExpressionDependencyMissingException;\n\n    /**\n     * Validate the expression, an exception is thrown it is invalid\n     *\n     * @param expression\n     *        the expression to validate\n     * @throws SInvalidExpressionException\n     *         if the exception is invalid\n     * @since 6.0\n     */\n    void validate(SExpression expression) throws SInvalidExpressionException;\n\n    ExpressionKind getExpressionKind();\n\n    List<Object> evaluate(List<SExpression> expressions, Map<String, Object> context,\n            Map<Integer, Object> resolvedExpressions, ContainerState containerState)\n            throws SExpressionEvaluationException, SExpressionDependencyMissingException;\n\n    /**\n     * Should we put the evaluated expressions of this strategy in the evaluation context?\n     */\n    boolean mustPutEvaluatedExpressionInContext();\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/ExpressionExecutorStrategyProvider.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression;\n\nimport java.util.List;\n\npublic class ExpressionExecutorStrategyProvider {\n\n    public ExpressionExecutorStrategyProvider(ExpressionService expressionService,\n            List<ExpressionExecutorStrategy> expressionStrategies) {\n        expressionService.setExpressionExecutorStrategy(expressionStrategies);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/ExpressionService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.ExpressionKind;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Zhao Na\n * @author Baptiste Mesta\n * @author Celine Souchet\n * @since 6.0\n */\npublic interface ExpressionService {\n\n    /**\n     * Evaluate the specific expression\n     *\n     * @param expression\n     *        the expression will be evaluated\n     * @return the evaluated expression result\n     * @throws SExpressionTypeUnknownException\n     * @throws SExpressionEvaluationException\n     * @throws SExpressionDependencyMissingException\n     * @throws SInvalidExpressionException\n     */\n    Object evaluate(SExpression expression, Map<Integer, Object> resolvedExpressions, ContainerState containerState)\n            throws SExpressionTypeUnknownException,\n            SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException;\n\n    /**\n     * Evaluate the specific expression with dependency values\n     *\n     * @param expression\n     *        the expression will be evaluated\n     * @param dependencyValues\n     *        the dependency values, it may contain values for expression\n     * @return the evaluated expression result\n     * @throws SExpressionTypeUnknownException\n     * @throws SExpressionEvaluationException\n     * @throws SExpressionDependencyMissingException\n     * @throws SInvalidExpressionException\n     */\n    Object evaluate(SExpression expression, Map<String, Object> dependencyValues,\n            Map<Integer, Object> resolvedExpressions, ContainerState containerState)\n            throws SExpressionTypeUnknownException, SExpressionEvaluationException,\n            SExpressionDependencyMissingException, SInvalidExpressionException;\n\n    /**\n     * Evaluate type specified expressions with dependency values\n     *\n     * @param expressionKind\n     *        the expression kind to indicate which type of ExpressionExecutorStrategy will be used to evaluate the\n     *        expressions\n     * @param expressions\n     *        a list of expressions to be evaluated\n     * @param dependencyValues\n     *        the dependency values for the expressions, it may contain value informations for expressions\n     * @return a list of evaluated expression results\n     * @throws SExpressionTypeUnknownException\n     * @throws SExpressionEvaluationException\n     * @throws SExpressionDependencyMissingException\n     * @throws SInvalidExpressionException\n     */\n    List<Object> evaluate(ExpressionKind expressionKind, List<SExpression> expressions,\n            Map<String, Object> dependencyValues,\n            Map<Integer, Object> resolvedExpressions, ContainerState containerState)\n            throws SExpressionTypeUnknownException, SExpressionEvaluationException,\n            SExpressionDependencyMissingException, SInvalidExpressionException;\n\n    /**\n     * Should we throw exception if the real return type is incompatible with the declared one?\n     *\n     * @return true if an exception must be thrown if check fails, false otherwise.\n     */\n    boolean mustCheckExpressionReturnType();\n\n    /**\n     * Should an expression result of a specified {@link ExpressionKind} must be put in the evaluation context?\n     *\n     * @param expressionKind\n     *        the {@link ExpressionKind}\n     */\n    boolean mustPutEvaluatedExpressionInContext(ExpressionKind expressionKind);\n\n    void setExpressionExecutorStrategy(List<ExpressionExecutorStrategy> expressionStrategies);\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/NonEmptyContentExpressionExecutorStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression;\n\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic abstract class NonEmptyContentExpressionExecutorStrategy implements ExpressionExecutorStrategy {\n\n    @Override\n    public void validate(final SExpression expression) throws SInvalidExpressionException {\n        if (expression == null) {\n            throw new SInvalidExpressionException(\"The expression cannot be null.\", null);\n        }\n        final String expressionContent = expression.getContent();\n\n        if (expressionContent == null || expressionContent.trim().isEmpty()) {\n            throw new SInvalidExpressionException(\n                    \"The expression content cannot be null or empty. Expression : \" + expression, expression.getName());\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/exception/SExpressionDependencyMissingException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.exception;\n\n/**\n * @author Zhao Na\n */\npublic class SExpressionDependencyMissingException extends SExpressionException {\n\n    private static final long serialVersionUID = 7265041910539116858L;\n\n    public SExpressionDependencyMissingException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SExpressionDependencyMissingException(final String message) {\n        super(message);\n    }\n\n    public SExpressionDependencyMissingException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/exception/SExpressionEvaluationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.exception;\n\n/**\n * @author Zhao Na\n * @author Celine Souchet\n */\npublic class SExpressionEvaluationException extends SExpressionException {\n\n    private static final long serialVersionUID = 2040156586924261425L;\n\n    private final String expressionName;\n\n    /**\n     * @param message\n     * @param cause\n     * @param expressionName\n     *        The expression's name that failed on the evaluation.\n     */\n    public SExpressionEvaluationException(final String message, final Throwable cause, final String expressionName) {\n        super(message, cause);\n        this.expressionName = expressionName;\n    }\n\n    /**\n     * @param message\n     * @param expressionName\n     *        The expression's name that failed on the evaluation.\n     */\n    public SExpressionEvaluationException(final String message, final String expressionName) {\n        super(message);\n        this.expressionName = expressionName;\n    }\n\n    /**\n     * @param cause\n     * @param expressionName\n     *        The expression's name that failed on the evaluation.\n     */\n    public SExpressionEvaluationException(final Throwable cause, final String expressionName) {\n        super(cause);\n        this.expressionName = expressionName;\n    }\n\n    /**\n     * Return null or empty string, if the context of evaluation is wrong.\n     *\n     * @return The expression's name that failed on the evaluation.\n     */\n    public String getExpressionName() {\n        return expressionName;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/exception/SExpressionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.exception;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Zhao Na\n */\npublic class SExpressionException extends SBonitaException {\n\n    private static final long serialVersionUID = 1689881771691541843L;\n\n    public SExpressionException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SExpressionException(final String message) {\n        super(message);\n    }\n\n    public SExpressionException(final Throwable cause) {\n        super(cause);\n    }\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/exception/SExpressionTypeUnknownException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.exception;\n\n/**\n * @author Zhao Na\n */\npublic class SExpressionTypeUnknownException extends SExpressionException {\n\n    private static final long serialVersionUID = 7904793231822823603L;\n\n    public SExpressionTypeUnknownException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SExpressionTypeUnknownException(final String message) {\n        super(message);\n    }\n\n    public SExpressionTypeUnknownException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/exception/SInvalidExpressionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.exception;\n\n/**\n * @author Zhao Na\n * @author Celine Souchet\n */\npublic class SInvalidExpressionException extends SExpressionException {\n\n    private static final long serialVersionUID = 5287292772348995383L;\n\n    private final String expressionName;\n\n    public SInvalidExpressionException(final String message, final Throwable cause, final String expressionName) {\n        super(message, cause);\n        this.expressionName = expressionName;\n    }\n\n    public SInvalidExpressionException(final String message, final String expressionName) {\n        super(message);\n        this.expressionName = expressionName;\n    }\n\n    public SInvalidExpressionException(final Throwable cause, final String expressionName) {\n        super(cause);\n        this.expressionName = expressionName;\n    }\n\n    /**\n     * Return null or empty string, if the context of evaluation is wrong.\n     *\n     * @return The expression's name that failed on the evaluation.\n     */\n    public String getExpressionName() {\n        return expressionName;\n    }\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/ConditionExpressionExecutorStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.expression.ContainerState;\nimport org.bonitasoft.engine.expression.NonEmptyContentExpressionExecutorStrategy;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.impl.condition.BinaryComparatorExecutor;\nimport org.bonitasoft.engine.expression.impl.condition.LogicalComplementExecutor;\nimport org.bonitasoft.engine.expression.model.ExpressionKind;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class ConditionExpressionExecutorStrategy extends NonEmptyContentExpressionExecutorStrategy {\n\n    public static final String LOGICAL_COMPLEMENT_OPERATOR = \"!\";\n\n    public static final String NOT_EQUALS_COMPARATOR = \"!=\";\n\n    public static final String EQUALS_COMPARATOR = \"==\";\n\n    public static final String GREATER_THAN_OR_EQUALS_COMPARATOR = \">=\";\n\n    public static final String lESS_THAN_OR_EQUALS_COMPARATOR = \"<=\";\n\n    public static final String GREATER_THAN_COMPARATOR = \">\";\n\n    public static final String LESS_THAN_COMPARATOR = \"<\";\n\n    private final List<String> validOperators;\n\n    private final LogicalComplementExecutor logicalComplementExecutor;\n    private final BinaryComparatorExecutor binaryComparatorExecutor;\n\n    public ConditionExpressionExecutorStrategy(LogicalComplementExecutor logicalComplementExecutor,\n            BinaryComparatorExecutor binaryComparatorExecutor) {\n        this.logicalComplementExecutor = logicalComplementExecutor;\n        this.binaryComparatorExecutor = binaryComparatorExecutor;\n        validOperators = new ArrayList<String>(7);\n        validOperators.add(LESS_THAN_COMPARATOR);\n        validOperators.add(GREATER_THAN_COMPARATOR);\n        validOperators.add(lESS_THAN_OR_EQUALS_COMPARATOR);\n        validOperators.add(GREATER_THAN_OR_EQUALS_COMPARATOR);\n        validOperators.add(EQUALS_COMPARATOR);\n        validOperators.add(NOT_EQUALS_COMPARATOR);\n        validOperators.add(LOGICAL_COMPLEMENT_OPERATOR);\n    }\n\n    @Override\n    public Object evaluate(final SExpression expression, final Map<String, Object> context,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState) throws SExpressionEvaluationException {\n        final String content = expression.getContent();\n        Object result;\n        if (LOGICAL_COMPLEMENT_OPERATOR.equals(content)) {\n            result = logicalComplementExecutor.evaluate(resolvedExpressions, expression);\n        } else {\n            result = binaryComparatorExecutor.evaluate(resolvedExpressions, expression);\n        }\n        return result;\n    }\n\n    @Override\n    public void validate(final SExpression expression) throws SInvalidExpressionException {\n        super.validate(expression);\n        if (!validOperators.contains(expression.getContent())) {\n            throw new SInvalidExpressionException(\n                    \"The content of expression must be among: \" + validOperators + \" for expression: \"\n                            + expression.toString(),\n                    expression.getName());\n        }\n    }\n\n    @Override\n    public ExpressionKind getExpressionKind() {\n        return new ExpressionKind(SExpression.TYPE_CONDITION);\n    }\n\n    @Override\n    public List<Object> evaluate(final List<SExpression> expressions, final Map<String, Object> context,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState)\n            throws SExpressionEvaluationException {\n        final List<Object> evaluatedExpressions = new ArrayList<Object>(expressions.size());\n        for (final SExpression sExpression : expressions) {\n            evaluatedExpressions.add(evaluate(sExpression, context, resolvedExpressions, containerState));\n        }\n        return evaluatedExpressions;\n    }\n\n    @Override\n    public boolean mustPutEvaluatedExpressionInContext() {\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/ConstantExpressionExecutorStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl;\n\nimport java.io.Serializable;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.OffsetDateTime;\nimport java.util.ArrayList;\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.TimeZone;\n\nimport org.bonitasoft.engine.expression.ContainerState;\nimport org.bonitasoft.engine.expression.ExpressionExecutorStrategy;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.ExpressionKind;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Zhao na\n * @author Baptiste Mesta\n * @author Celine Souchet\n * @author Matthieu Chaffotte\n */\npublic class ConstantExpressionExecutorStrategy implements ExpressionExecutorStrategy {\n\n    private static final String REGEX_PARSE_DATE = \"(\\\\d{4})(-([01]\\\\d)((-([0-3]\\\\d)(T(\\\\d\\\\d):(\\\\d\\\\d)(((:(\\\\d\\\\d))?(\\\\.(\\\\d\\\\d))?(([\\\\+-])(\\\\d\\\\d):(\\\\d\\\\d))?)?)?)?)?)?)?\";\n\n    @Override\n    public void validate(final SExpression expression) throws SInvalidExpressionException {\n        if (\"\".equals(expression.getContent().trim())) {\n            throw new SInvalidExpressionException(\"The expression content cannot be empty. Expression : \" + expression,\n                    expression.getName());\n        }\n    }\n\n    @Override\n    public ExpressionKind getExpressionKind() {\n        return KIND_CONSTANT;\n    }\n\n    @Override\n    public Object evaluate(final SExpression expression, final Map<String, Object> context,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState) throws SExpressionEvaluationException {\n        final String expressionContent = expression.getContent();\n        Serializable result;\n        final String returnType = expression.getReturnType();\n        // here need to improve\n        try {\n            if (Boolean.class.getName().equals(returnType)) {\n                result = Boolean.parseBoolean(expressionContent);\n            } else if (Long.class.getName().equals(returnType)) {\n                result = Long.parseLong(expressionContent);\n            } else if (Double.class.getName().equals(returnType)) {\n                result = Double.parseDouble(expressionContent);\n            } else if (Float.class.getName().equals(returnType)) {\n                result = Float.parseFloat(expressionContent);\n            } else if (Integer.class.getName().equals(returnType)) {\n                result = Integer.parseInt(expressionContent);\n            } else if (String.class.getName().equals(returnType)) {\n                result = expressionContent;\n            } else if (Date.class.getName().equals(returnType)) { // \"2013-01-02T02:42:12.17+02:00\"\n                result = parseDate(expressionContent);\n            } else if (LocalDate.class.getName().equals(returnType)) {\n                result = LocalDate.parse(expressionContent);\n            } else if (LocalDateTime.class.getName().equals(returnType)) {\n                result = LocalDateTime.parse(expressionContent);\n            } else if (OffsetDateTime.class.getName().equals(returnType)) {\n                result = OffsetDateTime.parse(expressionContent);\n            } else {\n                throw new SExpressionEvaluationException(\n                        \"Unknown return type: \" + returnType + \" for expression \" + expression.getName() + \" : \"\n                                + expressionContent,\n                        expression.getName());\n            }\n        } catch (final NumberFormatException e) {\n            throw new SExpressionEvaluationException(\n                    \"The content of the expression \\\"\" + expression.getName() + \"\\\" is not a number :\"\n                            + expressionContent,\n                    e,\n                    expression.getName());\n        }\n        return result;\n    }\n\n    @Override\n    public List<Object> evaluate(final List<SExpression> expressions, final Map<String, Object> context,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState) throws SExpressionEvaluationException {\n        final List<Object> list = new ArrayList<>(expressions.size());\n        for (final SExpression expression : expressions) {\n            list.add(evaluate(expression, context, resolvedExpressions, containerState));\n        }\n        return list;\n    }\n\n    @Override\n    public boolean mustPutEvaluatedExpressionInContext() {\n        return false;\n    }\n\n    /**\n     * @param dateToParse the date to parse, as a String\n     * @return null if not a Date, new Date with properties is ISO format is recognized\n     */\n    private Date parseDate(final String dateToParse) {\n        if (dateToParse.matches(REGEX_PARSE_DATE)) {\n            final Calendar calendar = Calendar.getInstance();\n            final String year = dateToParse.replaceFirst(REGEX_PARSE_DATE, \"$1\");\n            if (year != null && !year.isEmpty() && Integer.valueOf(year) > 1900) {\n                calendar.set(Calendar.YEAR, Integer.valueOf(year));\n            }\n\n            final String month = dateToParse.replaceFirst(REGEX_PARSE_DATE, \"$3\");\n            if (month != null && !month.isEmpty() && Integer.valueOf(month) < 13) {\n                // MONTH value from 0 to 11\n                calendar.set(Calendar.MONTH, Integer.valueOf(month) - 1);\n            }\n\n            final String day = dateToParse.replaceFirst(REGEX_PARSE_DATE, \"$6\");\n            if (day != null && !day.isEmpty() && Integer.valueOf(day) < 32) {\n                calendar.set(Calendar.DAY_OF_MONTH, Integer.valueOf(day));\n            }\n\n            final String hour = dateToParse.replaceFirst(REGEX_PARSE_DATE, \"$8\");\n            if (hour != null && !hour.isEmpty() && Integer.valueOf(hour) < 24) {\n                calendar.set(Calendar.HOUR_OF_DAY, Integer.valueOf(hour));\n            }\n\n            final String minutes = dateToParse.replaceFirst(REGEX_PARSE_DATE, \"$9\");\n            if (minutes != null && !minutes.isEmpty() && Integer.valueOf(minutes) < 60) {\n                calendar.set(Calendar.MINUTE, Integer.valueOf(minutes));\n            }\n\n            final String seconds = dateToParse.replaceFirst(REGEX_PARSE_DATE, \"$13\");\n            if (seconds != null && !seconds.isEmpty() && Integer.valueOf(seconds) < 60) {\n                calendar.set(Calendar.SECOND, Integer.valueOf(seconds));\n            }\n\n            final String fractional = dateToParse.replaceFirst(REGEX_PARSE_DATE, \"$15\");\n            if (fractional != null && !fractional.isEmpty() && Integer.valueOf(fractional) < 60) {\n                calendar.set(Calendar.MILLISECOND, Integer.valueOf(fractional));\n            }\n\n            final String tzSign = dateToParse.replaceFirst(REGEX_PARSE_DATE, \"$17\");\n            final String tzHour = dateToParse.replaceFirst(REGEX_PARSE_DATE, \"$18\");\n            final String tzMinutes = dateToParse.replaceFirst(REGEX_PARSE_DATE, \"$19\");\n            final TimeZone tz = TimeZone.getTimeZone(\"GMT\" + tzSign + tzHour + tzMinutes);\n            if (!tzSign.isEmpty() && !tzHour.isEmpty() && !tzMinutes.isEmpty() && tz != null) {\n                calendar.setTimeZone(tz);\n            }\n\n            return calendar.getTime();\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/ExpressionServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.commons.LogUtil;\nimport org.bonitasoft.engine.expression.ContainerState;\nimport org.bonitasoft.engine.expression.ExpressionExecutorStrategy;\nimport org.bonitasoft.engine.expression.ExpressionService;\nimport org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.ExpressionKind;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.tracking.TimeTracker;\nimport org.bonitasoft.engine.tracking.TimeTrackerRecords;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Zhao na\n * @author Emmanuel Duchastenier\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\n\npublic class ExpressionServiceImpl implements ExpressionService {\n\n    private static final Logger log = LoggerFactory.getLogger(ExpressionServiceImpl.class);\n    private final Map<ExpressionKind, ExpressionExecutorStrategy> expressionExecutorsMap = new HashMap<>();\n    private boolean checkExpressionReturnType = false;\n\n    private final TimeTracker timeTracker;\n\n    public ExpressionServiceImpl(final boolean checkExpressionReturnType,\n            final TimeTracker timeTracker) {\n        super();\n        this.checkExpressionReturnType = checkExpressionReturnType;\n        this.timeTracker = timeTracker;\n    }\n\n    @Override\n    public void setExpressionExecutorStrategy(List<ExpressionExecutorStrategy> expressionExecutors) {\n        for (final ExpressionExecutorStrategy expressionExecutorStrategy : expressionExecutors) {\n            expressionExecutorsMap.put(expressionExecutorStrategy.getExpressionKind(), expressionExecutorStrategy);\n        }\n    }\n\n    @Override\n    public Object evaluate(final SExpression expression, final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState) throws SExpressionTypeUnknownException, SExpressionEvaluationException,\n            SExpressionDependencyMissingException, SInvalidExpressionException {\n        return evaluate(expression, new HashMap<String, Object>(1), resolvedExpressions, containerState);\n    }\n\n    @Override\n    public Object evaluate(final SExpression expression, final Map<String, Object> dependencyValues,\n            final Map<Integer, Object> resolvedExpressions, final ContainerState containerState)\n            throws SExpressionTypeUnknownException, SExpressionEvaluationException,\n            SExpressionDependencyMissingException, SInvalidExpressionException {\n        if (log.isTraceEnabled()) {\n            log.trace(LogUtil.getLogBeforeMethod(this.getClass(), \"evaluate\"));\n        }\n\n        final ExpressionExecutorStrategy expressionExecutorStrategy = getStrategy(expression.getExpressionKind());\n        validateExpression(expressionExecutorStrategy, expression);\n\n        Object expressionResult = null;\n        final long startTime = System.currentTimeMillis();\n        try {\n            expressionResult = expressionExecutorStrategy.evaluate(expression, dependencyValues, resolvedExpressions,\n                    containerState);\n        } finally {\n            if (timeTracker.isTrackable(TimeTrackerRecords.EVALUATE_EXPRESSION)) {\n                final long endTime = System.currentTimeMillis();\n                timeTracker.track(TimeTrackerRecords.EVALUATE_EXPRESSION, \"Expression: \" + expression + \" - \"\n                        + \"dependencyValues: \" + dependencyValues + \" - \" + \"strategy: \" + expressionExecutorStrategy,\n                        endTime - startTime);\n            }\n        }\n        if (mustCheckExpressionReturnType()) {\n            new ReturnTypeChecker().checkReturnType(expression, expressionResult, dependencyValues);\n        }\n\n        if (log.isTraceEnabled()) {\n            log.trace(LogUtil.getLogAfterMethod(this.getClass(), \"evaluate\"));\n        }\n\n        return expressionResult;\n    }\n\n    private void validateExpression(final ExpressionExecutorStrategy expressionExecutorStrategy,\n            final SExpression expression) throws SInvalidExpressionException {\n        try {\n            // this will throw exception if the expression is invalid\n            expressionExecutorStrategy.validate(expression);\n        } catch (final SInvalidExpressionException e) {\n            log.trace(LogUtil.getLogOnExceptionMethod(this.getClass(),\n                    \"evaluate\", \"Invalid Expression : \" + expression.getContent()));\n\n            throw e;\n        }\n    }\n\n    private ExpressionExecutorStrategy getStrategy(final ExpressionKind expressionKind)\n            throws SExpressionTypeUnknownException {\n        final ExpressionExecutorStrategy expressionExecutorStrategy = expressionExecutorsMap.get(expressionKind);\n        if (expressionExecutorStrategy == null) {\n            log.trace(LogUtil.getLogOnExceptionMethod(this.getClass(),\n                    \"evaluate\", \"Unable to find an executor for expression type \" + expressionKind));\n\n            throw new SExpressionTypeUnknownException(\n                    \"Unable to find an executor for expression type \" + expressionKind);\n        }\n        return expressionExecutorStrategy;\n    }\n\n    @Override\n    public List<Object> evaluate(final ExpressionKind expressionKind, final List<SExpression> expressions,\n            final Map<String, Object> dependencyValues, final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState) throws SExpressionTypeUnknownException, SExpressionEvaluationException,\n            SExpressionDependencyMissingException {\n        if (log.isTraceEnabled()) {\n            log.trace(LogUtil.getLogBeforeMethod(this.getClass(), \"evaluate\"));\n        }\n\n        final ExpressionExecutorStrategy expressionExecutorStrategy = getStrategy(expressionKind);\n\n        List<Object> list = null;\n        final long startTime = System.currentTimeMillis();\n        try {\n            list = expressionExecutorStrategy.evaluate(expressions, dependencyValues, resolvedExpressions,\n                    containerState);\n        } finally {\n            if (timeTracker.isTrackable(TimeTrackerRecords.EVALUATE_EXPRESSIONS)) {\n                final long endTime = System.currentTimeMillis();\n                timeTracker.track(TimeTrackerRecords.EVALUATE_EXPRESSIONS, \"Expressions: \" + expressions + \" - \"\n                        + \"dependencyValues: \" + dependencyValues + \" - \" + \"strategy: \" + expressionExecutorStrategy,\n                        endTime - startTime);\n            }\n        }\n        if (list == null || list.size() != expressions.size()) {\n            final String exceptionMessage = \"Result list size \" + (list == null ? 0 : list.size())\n                    + \" is different from expression list size \" + expressions.size();\n            if (log.isTraceEnabled()) {\n                log.trace(LogUtil.getLogOnExceptionMethod(this.getClass(), \"evaluate\", exceptionMessage));\n            }\n\n            throw new SExpressionEvaluationException(exceptionMessage, null);\n        }\n        if (mustCheckExpressionReturnType()) {\n            for (int i = 0; i < list.size(); i++) {\n                new ReturnTypeChecker().checkReturnType(expressions.get(i), list.get(i), dependencyValues);\n            }\n        }\n        if (log.isTraceEnabled()) {\n            log.trace(LogUtil.getLogAfterMethod(this.getClass(), \"evaluate\"));\n        }\n\n        return list;\n    }\n\n    @Override\n    public boolean mustCheckExpressionReturnType() {\n        return checkExpressionReturnType;\n    }\n\n    @Override\n    public boolean mustPutEvaluatedExpressionInContext(final ExpressionKind expressionKind) {\n        return expressionExecutorsMap.get(expressionKind).mustPutEvaluatedExpressionInContext();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/GroovyScriptConditionExpressionExecutorStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl;\n\nimport java.util.Map;\n\nimport org.bonitasoft.engine.cache.CacheService;\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.expression.ContainerState;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.model.ExpressionKind;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\npublic class GroovyScriptConditionExpressionExecutorStrategy extends GroovyScriptExpressionExecutorCacheStrategy {\n\n    public GroovyScriptConditionExpressionExecutorStrategy(CacheService cacheService,\n            ClassLoaderService classLoaderService) {\n        super(cacheService, classLoaderService);\n    }\n\n    @Override\n    public ExpressionKind getExpressionKind() {\n        return KIND_READ_ONLY_CONDITION_SCRIPT_GROOVY;\n    }\n\n    @Override\n    public Object evaluate(SExpression expression, Map<String, Object> context,\n            Map<Integer, Object> resolvedExpressions, ContainerState containerState)\n            throws SExpressionEvaluationException {\n        Object result = super.evaluate(expression, context, resolvedExpressions, containerState);\n        return result instanceof Boolean ? result : result != null;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/GroovyScriptExpressionExecutorCacheStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl;\n\nimport static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier;\n\nimport java.security.AccessController;\nimport java.security.PrivilegedAction;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport groovy.lang.Binding;\nimport groovy.lang.GroovyCodeSource;\nimport groovy.lang.GroovyRuntimeException;\nimport groovy.lang.GroovyShell;\nimport groovy.lang.MissingPropertyException;\nimport groovy.lang.Script;\nimport org.bonitasoft.engine.cache.CacheService;\nimport org.bonitasoft.engine.cache.SCacheException;\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.classloader.SClassLoaderException;\nimport org.bonitasoft.engine.classloader.SingleClassLoaderListener;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException;\nimport org.bonitasoft.engine.dependency.model.ScopeType;\nimport org.bonitasoft.engine.expression.ContainerState;\nimport org.bonitasoft.engine.expression.NonEmptyContentExpressionExecutorStrategy;\nimport org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.model.ExpressionKind;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.codehaus.groovy.runtime.InvokerHelper;\nimport org.codehaus.groovy.runtime.typehandling.GroovyCastException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class GroovyScriptExpressionExecutorCacheStrategy extends NonEmptyContentExpressionExecutorStrategy\n        implements SingleClassLoaderListener {\n\n    private static final Logger log = LoggerFactory.getLogger(GroovyScriptExpressionExecutorCacheStrategy.class);\n    public static final String GROOVY_SCRIPT_CACHE_NAME = \"GROOVY_SCRIPT_CACHE_NAME\";\n\n    public static final String SCRIPT_KEY = \"SCRIPT_\";\n    public static final String COERCION_SCRIPT_KEY = \"COERCION_SCRIPT_\";\n    public static final String SHELL_KEY = \"SHELL_\";\n\n    private final CacheService cacheService;\n\n    private final ClassLoaderService classLoaderService;\n    private static final AtomicLong counter = new AtomicLong();\n\n    public GroovyScriptExpressionExecutorCacheStrategy(final CacheService cacheService,\n            final ClassLoaderService classLoaderService) {\n        this.cacheService = cacheService;\n        this.classLoaderService = classLoaderService;\n    }\n\n    private String generateScriptName() {\n        return String.format(\"BScript%s.groovy\", counter.incrementAndGet());\n    }\n\n    Class getScriptFromCache(final String expressionContent, final Long definitionId)\n            throws SCacheException, SClassLoaderException {\n        if (definitionId == null) {\n            throw new SBonitaRuntimeException(\"Unable to evaluate expression without a definitionId\");\n        }\n        final GroovyShell shell = getShell(definitionId);\n\n        GroovyCodeSource gcs = getOrCreateGroovyCodeSource(SCRIPT_KEY + expressionContent.hashCode(),\n                expressionContent);\n        // parse the groovy source code with cache set to true\n        return shell.getClassLoader().parseClass(gcs, true);\n    }\n\n    private GroovyCodeSource getOrCreateGroovyCodeSource(String key, String scriptContent) throws SCacheException {\n        GroovyCodeSource gcs = (GroovyCodeSource) cacheService.get(GROOVY_SCRIPT_CACHE_NAME, key);\n\n        if (gcs == null) {\n            gcs = AccessController\n                    .doPrivileged((PrivilegedAction<GroovyCodeSource>) () -> new GroovyCodeSource(scriptContent,\n                            generateScriptName(), GroovyShell.DEFAULT_CODE_BASE));\n            cacheService.store(GROOVY_SCRIPT_CACHE_NAME, key, gcs);\n        }\n        return gcs;\n    }\n\n    GroovyShell getShell(final Long definitionId) throws SClassLoaderException, SCacheException {\n        String key = SHELL_KEY + definitionId;\n        GroovyShell shell = (GroovyShell) cacheService.get(GROOVY_SCRIPT_CACHE_NAME, key);\n        if (shell == null) {\n            ClassLoader classLoader = getClassLoaderForShell(definitionId);\n            log.debug(\"Create a new groovy classloader for {} {}\", definitionId, classLoader);\n\n            shell = new GroovyShell(classLoader);\n            cacheService.store(GROOVY_SCRIPT_CACHE_NAME, key, shell);\n        }\n        return shell;\n    }\n\n    private ClassLoader getClassLoaderForShell(Long definitionId) throws SClassLoaderException {\n        ClassLoader classLoader;\n        if (definitionId == null) {\n            classLoader = Thread.currentThread().getContextClassLoader();\n            //do not has listener, should not happen...\n            if (log.isDebugEnabled()) {\n                IllegalStateException illegalStateException = new IllegalStateException();\n                log.debug(\"Creating a shell without definition id, might cause issue when reloading classes {}\",\n                        illegalStateException.getMessage());\n            }\n        } else {\n            classLoader = classLoaderService.getClassLoader(identifier(ScopeType.PROCESS, definitionId));\n            classLoaderService.addListener(identifier(ScopeType.PROCESS, definitionId), this);\n        }\n        return classLoader;\n    }\n\n    @Override\n    public Object evaluate(final SExpression expression, final Map<String, Object> context,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState) throws SExpressionEvaluationException {\n        final String expressionContent = expression.getContent();\n        final String expressionName = expression.getName();\n        try {\n            final Binding binding = new Binding(context);\n            Long definitionId = (Long) context.get(DEFINITION_ID);\n            final Script script = InvokerHelper\n                    .createScript(getScriptFromCache(expressionContent, definitionId), binding);\n            script.setBinding(binding);\n            return coerceResult(getShell(definitionId), script.run(), expression.getReturnType());\n        } catch (final MissingPropertyException e) {\n            final String property = e.getProperty();\n            throw new SExpressionEvaluationException(\"Expression \" + expressionName + \" with content = <\"\n                    + expressionContent + \"> depends on \" + property\n                    + \" is neither defined in the script nor in dependencies.\", e, expressionName);\n        } catch (final GroovyRuntimeException e) {\n            throw new SExpressionEvaluationException(e, expressionName);\n        } catch (final SCacheException e) {\n            throw new SExpressionEvaluationException(\n                    \"Problem accessing the Script Cache from GroovyScriptExpressionExecutorCacheStrategy.\", e,\n                    expressionName);\n        } catch (final SClassLoaderException e) {\n            throw new SExpressionEvaluationException(\n                    \"Unable to retrieve the correct classloader to execute the groovy script : \" + expression, e,\n                    expressionName);\n        } catch (final Throwable e) {\n            //catch throwable because we do not handle contents of scripts\n            String message = e.getMessage();\n            if (message == null || message.isEmpty()) {\n                message = \"No message\";\n            }\n            throw new SExpressionEvaluationException(\n                    \"Groovy script throws an exception of type \" + e.getClass() + \" with message = \" + message\n                            + System.lineSeparator() + \"Expression : \" + expression,\n                    e, expressionName);\n        }\n    }\n\n    @Override\n    public void onUpdate(ClassLoader newClassLoader) {\n        log.debug(\"Groovy cache cleared after update on {}\", newClassLoader);\n        clearCache();\n    }\n\n    @Override\n    public void onDestroy(ClassLoader oldClassLoader) {\n        log.debug(\"Groovy cache cleared after destroy of {}\", oldClassLoader);\n        clearCache();\n    }\n\n    private void clearCache() {\n        try {\n            cacheService.clear(GROOVY_SCRIPT_CACHE_NAME);\n        } catch (SCacheException e) {\n            log.error(\n                    \"error while clearing the cache of the groovy script executor strategy, you might have classloading issue, restart the server if it's the case\",\n                    e);\n        }\n    }\n\n    @Override\n    public ExpressionKind getExpressionKind() {\n        return KIND_READ_ONLY_SCRIPT_GROOVY;\n    }\n\n    @Override\n    public List<Object> evaluate(final List<SExpression> expressions, final Map<String, Object> context,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState)\n            throws SExpressionEvaluationException, SExpressionDependencyMissingException {\n        final List<Object> list = new ArrayList<>(expressions.size());\n        for (final SExpression expression : expressions) {\n            list.add(evaluate(expression, context, resolvedExpressions, containerState));\n        }\n        return list;\n    }\n\n    /**\n     * Execute a Groovy expression that coerce the result into the returnType\n     *\n     * @param shell, the Groovy shell use for script evaluation\n     * @param result, the evaluation result\n     * @param returnType, expected expression return type\n     * @return the result with the expected type or a {@link GroovyCastException} if the coercion fails\n     * @throws ClassNotFoundException\n     */\n    protected Object coerceResult(GroovyShell shell, Object result, String returnType)\n            throws ClassNotFoundException, SCacheException {\n        if (result == null) {\n            return null;\n        }\n        String resultClassName = result.getClass().getName();\n        if (Objects.equals(resultClassName, returnType) // Already in the expected type\n                || ReturnTypeChecker.isConvertible(returnType, resultClassName)) { // Bonita specific type conversion\n            return result;\n        }\n        String scriptContent = String.format(\"result as %s\",\n                returnType.startsWith(\"[\") ? canonicalClassName(returnType) : returnType);\n\n        GroovyCodeSource gcs = getOrCreateGroovyCodeSource(COERCION_SCRIPT_KEY + returnType, scriptContent);\n        Binding binding = new Binding();\n        binding.setVariable(\"result\", result);\n        Script script = InvokerHelper\n                .createScript(shell.getClassLoader().parseClass(gcs, true), binding);\n        return script.run();\n    }\n\n    private String canonicalClassName(String returnType) throws ClassNotFoundException {\n        return Class.forName(returnType).getCanonicalName();\n    }\n\n    @Override\n    public boolean mustPutEvaluatedExpressionInContext() {\n        return false;\n    }\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/InputExpressionExecutorStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.expression.ContainerState;\nimport org.bonitasoft.engine.expression.NonEmptyContentExpressionExecutorStrategy;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.model.ExpressionKind;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Zhao na\n * @author Emmanuel Duchastenier\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class InputExpressionExecutorStrategy extends NonEmptyContentExpressionExecutorStrategy {\n\n    @Override\n    public Object evaluate(final SExpression expression, final Map<String, Object> context,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState) throws SExpressionEvaluationException {\n        if (context != null && !context.isEmpty()) {\n            final String key = expression.getContent();\n            if (context.containsKey(key)) {\n                return context.get(key);\n            }\n        }\n        throw new SExpressionEvaluationException(\n                \"No value found for mandatory expression '\" + expression.getContent() + \"' of type Input Expression\",\n                expression.getName());\n    }\n\n    @Override\n    public ExpressionKind getExpressionKind() {\n        return KIND_INPUT;\n    }\n\n    @Override\n    public List<Object> evaluate(final List<SExpression> expressions, final Map<String, Object> context,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState) throws SExpressionEvaluationException {\n        final List<Object> list = new ArrayList<Object>(expressions.size());\n        for (final SExpression expression : expressions) {\n            list.add(evaluate(expression, context, resolvedExpressions, containerState));\n        }\n        return list;\n    }\n\n    @Override\n    public boolean mustPutEvaluatedExpressionInContext() {\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/JavaMethodCallExpressionExecutorStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.commons.ClassReflector;\nimport org.bonitasoft.engine.commons.exceptions.SReflectException;\nimport org.bonitasoft.engine.expression.ContainerState;\nimport org.bonitasoft.engine.expression.NonEmptyContentExpressionExecutorStrategy;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.ExpressionKind;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic class JavaMethodCallExpressionExecutorStrategy extends NonEmptyContentExpressionExecutorStrategy {\n\n    @Override\n    public Object evaluate(final SExpression expression, final Map<String, Object> context,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState) throws SExpressionEvaluationException {\n        final SExpression dependency = expression.getDependencies().get(0);\n        final Object object = resolvedExpressions.get(dependency.getDiscriminant());\n        try {\n            return ClassReflector.invokeGetter(object, expression.getContent());\n        } catch (final SReflectException e) {\n            throw new SExpressionEvaluationException(e, expression.getName());\n        }\n    }\n\n    @Override\n    public void validate(final SExpression expression) throws SInvalidExpressionException {\n        super.validate(expression);\n        if (expression.getDependencies() == null || expression.getDependencies().size() != 1) {\n            throw new SInvalidExpressionException(\"An expression of type \" + TYPE_JAVA_METHOD_CALL\n                    + \" must have exactly one dependency. This dependency represents the object where the method will be called. Expression :\"\n                    + expression,\n                    expression.getName());\n        }\n    }\n\n    @Override\n    public ExpressionKind getExpressionKind() {\n        return KIND_JAVA_METHOD_CALL;\n    }\n\n    @Override\n    public List<Object> evaluate(final List<SExpression> expressions, final Map<String, Object> context,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState) throws SExpressionEvaluationException {\n        final List<Object> result = new ArrayList<Object>(2);\n        for (final SExpression expression : expressions) {\n            result.add(evaluate(expression, context, resolvedExpressions, containerState));\n        }\n        return result;\n    }\n\n    @Override\n    public boolean mustPutEvaluatedExpressionInContext() {\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/ListExpressionExecutorStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.expression.ContainerState;\nimport org.bonitasoft.engine.expression.ExpressionExecutorStrategy;\nimport org.bonitasoft.engine.expression.model.ExpressionKind;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * Evaluate a list of SExpression, represented by the {@link SExpression#getDependencies()} method. The result is a\n * {@link java.util.List} of Serializable\n * objects.\n *\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n */\npublic class ListExpressionExecutorStrategy implements ExpressionExecutorStrategy {\n\n    @Override\n    public void validate(final SExpression expression) {\n        // nothing to validate, as Business logic resides in dependencies:\n    }\n\n    @Override\n    public ExpressionKind getExpressionKind() {\n        return KIND_LIST;\n    }\n\n    @Override\n    public Serializable evaluate(final SExpression expression, final Map<String, Object> context,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState) {\n        final List<Object> result = new ArrayList<Object>(expression.getDependencies().size());\n        for (final SExpression exp : expression.getDependencies()) {\n            result.add(resolvedExpressions.get(exp.getDiscriminant()));\n        }\n        // Let's put this ListExpression ExpressionResult in the list of resolved dependencies:\n        resolvedExpressions.put(expression.getDiscriminant(), result);\n        return (Serializable) result;\n    }\n\n    @Override\n    public List<Object> evaluate(final List<SExpression> expressions, final Map<String, Object> context,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState) {\n        final List<Object> list = new ArrayList<Object>(expressions.size());\n        for (final SExpression expression : expressions) {\n            list.add(evaluate(expression, context, resolvedExpressions, containerState));\n        }\n        return list;\n    }\n\n    @Override\n    public boolean mustPutEvaluatedExpressionInContext() {\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/PatternExpressionExecutorStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.commons.text.StringSubstitutor;\nimport org.bonitasoft.engine.expression.ContainerState;\nimport org.bonitasoft.engine.expression.NonEmptyContentExpressionExecutorStrategy;\nimport org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException;\nimport org.bonitasoft.engine.expression.model.ExpressionKind;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * <code>ExpressionExecutorStrategy</code> to evaluate a 'Pattern', that is a String message into which some parameters\n * are replaced by their values.\n *\n * @author Emmanuel Duchastenier\n */\npublic class PatternExpressionExecutorStrategy extends NonEmptyContentExpressionExecutorStrategy {\n\n    @Override\n    public Serializable evaluate(final SExpression expression, final Map<String, Object> context,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState) throws SExpressionDependencyMissingException {\n        final List<SExpression> dependencies = expression.getDependencies();\n        final Map<String, Object> values = new HashMap<String, Object>(dependencies.size());\n        for (final SExpression exp : dependencies) {\n            final String name = exp.getName();\n            final Object value = resolvedExpressions.get(exp.getDiscriminant());\n            if (value == null) {\n                throw new SExpressionDependencyMissingException(\"Expression dependency not found: \" + name);\n            }\n            values.put(name, value);\n        }\n        final StringSubstitutor stringSubstitutor = new StringSubstitutor(values);\n        return stringSubstitutor.replace(expression.getContent());\n    }\n\n    @Override\n    public ExpressionKind getExpressionKind() {\n        return KIND_PATTERN;\n    }\n\n    @Override\n    public List<Object> evaluate(final List<SExpression> expressions, final Map<String, Object> context,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState) throws SExpressionDependencyMissingException {\n        final List<Object> list = new ArrayList<Object>(expressions.size());\n        for (final SExpression expression : expressions) {\n            list.add(evaluate(expression, context, resolvedExpressions, containerState));\n        }\n        return list;\n    }\n\n    @Override\n    public boolean mustPutEvaluatedExpressionInContext() {\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/ReturnTypeChecker.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl;\n\nimport static java.lang.String.format;\nimport static java.util.Arrays.asList;\nimport static java.util.Optional.ofNullable;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class ReturnTypeChecker {\n\n    private static final String CONTAINER_ID = \"containerId\";\n\n    private static final String CONTAINER_TYPE = \"containerType\";\n\n    private static final String ACTIVITY_INSTANCE_SCOPE = \"ACTIVITY_INSTANCE\";\n\n    private static final String PROCESS_INSTANCE_SCOPE = \"PROCESS_INSTANCE\";\n\n    /**\n     * Describes types that can be be converted later to other types.<br>\n     * <li>key: the type which can be converted to\n     * <li>values: the input types that can be converted to the <code>key</code> type\n     */\n    private static final Map<String, List<String>> CONVERTIBLE_TYPES = new HashMap<>();\n    static {\n        CONVERTIBLE_TYPES.put(\"org.bonitasoft.engine.bpm.document.Document\",\n                asList(\"org.bonitasoft.engine.bpm.contract.FileInputValue\"));\n        CONVERTIBLE_TYPES.put(\"org.bonitasoft.engine.bpm.document.DocumentValue\",\n                asList(\"org.bonitasoft.engine.bpm.contract.FileInputValue\"));\n    }\n\n    /**\n     * Check if the declared return type is compatible with the real Expression evaluation return type. If the result of\n     * the Expression evaluation is null, then this method returns <code>true</code>.\n     *\n     * @param expression\n     *        the evaluated expression\n     * @param result\n     *        the expression result to check\n     * @throws SExpressionEvaluationException\n     *         if the condition is not fulfilled, does nothing otherwise\n     */\n    public void checkReturnType(final SExpression expression, final Object result, final Map<String, Object> context)\n            throws SExpressionEvaluationException {\n        if (result == null) {\n            return;\n        }\n        final String declaredReturnType = expression.getReturnType();\n        final String evaluatedClassName = result.getClass().getName();\n        if (!evaluatedClassName.equals(declaredReturnType)) {\n            if (isConvertible(declaredReturnType, evaluatedClassName)) {\n                return;\n            }\n\n            try {\n                final String expressionName = expression.getName();\n                try {\n                    final Class<?> declaredReturnedClass = getClazz(declaredReturnType);\n                    final Class<?> evaluatedClass = result.getClass();\n                    if (!declaredReturnedClass.isAssignableFrom(evaluatedClass)) {\n                        throw new SExpressionEvaluationException(format(\n                                \"Declared return type %s is not compatible with evaluated type %s for expression %s\",\n                                declaredReturnedClass, evaluatedClass, expressionName), expressionName);\n                    }\n                } catch (final ClassNotFoundException e) {\n                    throw new SExpressionEvaluationException(\n                            format(\"Unknown declared return type %s for expression %s\", declaredReturnType,\n                                    expressionName),\n                            e, expressionName);\n                }\n            } catch (final SExpressionEvaluationException e) {\n                if (isContextOnActivity(context)) {\n                    e.setFlowNodeInstanceIdOnContext((Long) context.get(CONTAINER_ID));\n                }\n                if (isContextOnProcess(context)) {\n                    e.setProcessInstanceIdOnContext((Long) context.get(CONTAINER_ID));\n                }\n                throw e;\n            }\n        }\n    }\n\n    private static Class<?> getClazz(String type) throws ClassNotFoundException {\n        return Thread.currentThread().getContextClassLoader().loadClass(type);\n    }\n\n    public static boolean isConvertible(String targetType, String sourceType) {\n        return ofNullable(CONVERTIBLE_TYPES.get(targetType)).map(targets -> targets.contains(sourceType)).orElse(false);\n    }\n\n    private static boolean isContextOnProcess(final Map<String, Object> context) {\n        return context.containsKey(CONTAINER_TYPE) && PROCESS_INSTANCE_SCOPE.equals(context.get(CONTAINER_TYPE));\n    }\n\n    private static boolean isContextOnActivity(final Map<String, Object> context) {\n        return context.containsKey(CONTAINER_TYPE) && ACTIVITY_INSTANCE_SCOPE.equals(context.get(CONTAINER_TYPE));\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/XPathReadExpressionExecutorStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl;\n\nimport java.io.IOException;\nimport java.io.StringReader;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.xml.XMLConstants;\nimport javax.xml.namespace.QName;\nimport javax.xml.parsers.DocumentBuilder;\nimport javax.xml.parsers.DocumentBuilderFactory;\nimport javax.xml.parsers.ParserConfigurationException;\nimport javax.xml.xpath.XPath;\nimport javax.xml.xpath.XPathConstants;\nimport javax.xml.xpath.XPathExpression;\nimport javax.xml.xpath.XPathExpressionException;\nimport javax.xml.xpath.XPathFactory;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException;\nimport org.bonitasoft.engine.expression.ContainerState;\nimport org.bonitasoft.engine.expression.ExpressionExecutorStrategy;\nimport org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.ExpressionKind;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.w3c.dom.Document;\nimport org.w3c.dom.Node;\nimport org.w3c.dom.NodeList;\nimport org.xml.sax.InputSource;\nimport org.xml.sax.SAXException;\n\n/**\n * Supported return types are:\n * <ul>\n * <li>java.lang.String</li>\n * <li>java.lang.Double</li>\n * <li>java.lang.Boolean</li>\n * <li>java.lang.Long</li>\n * <li>java.lang.Integer</li>\n * <li>java.lang.Float</li>\n * <li>org.w3c.dom.Node</li>\n * <li>org.w3c.dom.NodeList</li>\n * </ul>\n *\n * @author Emmanuel Duchastenier\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class XPathReadExpressionExecutorStrategy implements ExpressionExecutorStrategy {\n\n    @Override\n    public Object evaluate(final SExpression expression, final Map<String, Object> context,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState)\n            throws SExpressionEvaluationException, SExpressionDependencyMissingException {\n        if (expression.getDependencies().size() != 1 || expression.getDependencies().get(0) == null) {\n            throw new SExpressionDependencyMissingException(\n                    \"XPathReadExpressionExecutorStrategy must have exactly one dependency\");\n        }\n\n        final String expressionName = expression.getName();\n        final String returnType = expression.getReturnType();\n        final String messageForException = \"Error evaluating expression \" + expression\n                + \" with strategy XPathReadExpressionExecutorStrategy\";\n        try {\n            final QName qname = getXPathConstants(returnType);\n            if (qname == null) {\n                throw new SExpressionEvaluationException(\n                        \"XPathReadExpressionExecutorStrategy return type not supported: \" + expression.getReturnType(),\n                        expressionName);\n            }\n            final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();\n            try {\n                factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, \"\"); // security-compliant\n                factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, \"\"); // security-compliant\n            } catch (IllegalArgumentException e) {\n                //ignored, if not supported by the implementation\n            }\n            final DocumentBuilder builder = factory.newDocumentBuilder();\n            // Check has already been done above:\n            final SExpression dep = expression.getDependencies().get(0);\n            final String xmlContent = (String) resolvedExpressions.get(dep.getDiscriminant());\n            if (xmlContent == null || xmlContent.isEmpty()) {\n                throw new SExpressionEvaluationException(\"The content of the xml is nul or empty: \" + expression,\n                        expressionName);\n            }\n            final Document document = builder.parse(new InputSource(new StringReader(xmlContent)));\n            final XPathFactory xpFactory = XPathFactory.newInstance();\n            final XPath xpath = xpFactory.newXPath();\n            final XPathExpression exp = xpath.compile(expression.getContent());\n            return transType(exp.evaluate(document, qname), returnType);\n        } catch (final XPathExpressionException | SBonitaRuntimeException | IOException | SAXException\n                | ParserConfigurationException e) {\n            throw new SExpressionEvaluationException(messageForException, e, expressionName);\n        }\n    }\n\n    private Object transType(final Object result, final String returnType) {\n        try {\n            if (Boolean.class.getName().equals(returnType)) {\n                return result != null && (\"true\".equalsIgnoreCase((String) result) || \"1\".equals(result));\n            }\n            if (Long.class.getName().equals(returnType)) {\n                return Long.parseLong((String) result);\n            }\n            if (Double.class.getName().equals(returnType)) {\n                return Double.parseDouble((String) result);\n            }\n            if (Float.class.getName().equals(returnType)) {\n                return Float.parseFloat((String) result);\n            }\n            if (Integer.class.getName().equals(returnType)) {\n                return Integer.parseInt((String) result);\n            }\n            if (String.class.getName().equals(returnType)) {\n                return result;\n            }\n        } catch (final NumberFormatException e) {\n            throw new SBonitaRuntimeException(\"Wrong format for \" + returnType + \" value was \" + result, e);\n        }\n        return result;\n    }\n\n    private QName getXPathConstants(final String expReturnType) {\n        if (String.class.getName().equals(expReturnType)) {\n            return XPathConstants.STRING;\n        } else if (Long.class.getName().equals(expReturnType) || Double.class.getName().equals(expReturnType)\n                || Float.class.getName().equals(expReturnType)\n                || Integer.class.getName().equals(expReturnType)) {\n            return XPathConstants.STRING;\n        } else if (Boolean.class.getName().equals(expReturnType)) {\n            return XPathConstants.STRING;\n        } else if (Node.class.getName().equals(expReturnType)) {\n            return XPathConstants.NODE;\n        } else if (NodeList.class.getName().equals(expReturnType)) {\n            return XPathConstants.NODESET;\n        }\n        return null;\n    }\n\n    @Override\n    public void validate(final SExpression expression) throws SInvalidExpressionException {\n        if (expression == null) {\n            throw new SInvalidExpressionException(\"The expression cannot be null.\", null);\n        }\n        final String expressionContent = expression.getContent();\n        if (expressionContent == null) {\n            throw new SInvalidExpressionException(\"The expression content cannot be null : \" + expression,\n                    expression.getName());\n        }\n    }\n\n    @Override\n    public ExpressionKind getExpressionKind() {\n        return KIND_XPATH_READ;\n    }\n\n    @Override\n    public List<Object> evaluate(final List<SExpression> expressions, final Map<String, Object> context,\n            final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState)\n            throws SExpressionEvaluationException, SExpressionDependencyMissingException {\n        final List<Object> list = new ArrayList<>(expressions.size());\n        for (final SExpression expression : expressions) {\n            list.add(evaluate(expression, context, resolvedExpressions, containerState));\n        }\n        return list;\n    }\n\n    @Override\n    public boolean mustPutEvaluatedExpressionInContext() {\n        // false because expression can't be referenced using it's content\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/condition/BinaryComparator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl.condition;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface BinaryComparator {\n\n    public <T> Boolean evaluate(final T left, final T right) throws SComparisonException;\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/condition/BinaryComparatorExecutor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl.condition;\n\nimport java.math.BigDecimal;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.impl.ConditionExpressionExecutorStrategy;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class BinaryComparatorExecutor {\n\n    private final BinaryComparatorMapper mapper;\n\n    private final Set<String> numericTypes;\n\n    public BinaryComparatorExecutor(BinaryComparatorMapper mapper) {\n        this.mapper = mapper;\n\n        numericTypes = new HashSet<String>();\n        numericTypes.add(Integer.class.getName());\n        numericTypes.add(Long.class.getName());\n        numericTypes.add(Double.class.getName());\n        numericTypes.add(Float.class.getName());\n        numericTypes.add(Short.class.getName());\n        numericTypes.add(Byte.class.getName());\n        numericTypes.add(int.class.getName());\n        numericTypes.add(long.class.getName());\n        numericTypes.add(double.class.getName());\n        numericTypes.add(float.class.getName());\n        numericTypes.add(short.class.getName());\n        numericTypes.add(byte.class.getName());\n    }\n\n    public Boolean evaluate(final Map<Integer, Object> resolvedExpressions, SExpression expression)\n            throws SExpressionEvaluationException {\n        validate(expression);\n        BinaryComparator evaluator = mapper.getEvaluator(expression.getContent());\n        if (evaluator == null) {\n            throw new SExpressionEvaluationException(\n                    \"Unable to find evaluator for operator '\" + expression.getContent() + \"'\", expression.getName());\n        }\n        SExpression leftExpression = expression.getDependencies().get(0);\n        SExpression rightExpression = expression.getDependencies().get(1);\n        final Object resolvedLeftExpr = transtypeIfApplicable(leftExpression.getReturnType(),\n                resolvedExpressions.get(leftExpression.getDiscriminant()));\n        final Object resolvedRightExpr = transtypeIfApplicable(rightExpression.getReturnType(),\n                resolvedExpressions.get(rightExpression.getDiscriminant()));\n\n        try {\n            return evaluator.evaluate(resolvedLeftExpr, resolvedRightExpr);\n        } catch (SComparisonException e) {\n            throw new SExpressionEvaluationException(\"Unable to evaluate expression '\" + expression.getName() + \"'\", e,\n                    expression.getName());\n        }\n    }\n\n    /*\n     * Transtype integer to long and float to double\n     * see bug ENGINE-1261\n     */\n    protected Object transtypeIfApplicable(final String type, final Object object) {\n        if (object == null) {\n            return null;\n        }\n        if (numericTypes.contains(type)) {\n            return new BigDecimal(String.valueOf(object));\n        }\n        return object;\n    }\n\n    private void validate(SExpression expression) throws SExpressionEvaluationException {\n        validateNumberOfDependencies(expression);\n        validateReturnType(expression);\n    }\n\n    private void validateReturnType(final SExpression expression) throws SExpressionEvaluationException {\n        List<SExpression> dependencies = expression.getDependencies();\n        if (!areReturnTypeCompatible(dependencies.get(0).getReturnType(), dependencies.get(1).getReturnType(),\n                expression.getContent())) {\n            throw new SExpressionEvaluationException(\n                    \"The two dependencies of expression '\" + expression.getContent()\n                            + \"' must have the same return type.\",\n                    expression.getName());\n        }\n    }\n\n    private void validateNumberOfDependencies(final SExpression expression) throws SExpressionEvaluationException {\n        int expectedNbOfDep = 2;\n        int actualNbOfDep = expression.getDependencies().size();\n        if (actualNbOfDep != expectedNbOfDep) {\n            StringBuilder stb = new StringBuilder();\n            stb.append(\"The expression '\");\n            stb.append(expression.getContent());\n            stb.append(\"' has \");\n            stb.append(actualNbOfDep);\n            stb.append(\" dependencies, but it must have exactly \");\n            stb.append(expectedNbOfDep);\n            stb.append(\" dependencies.\");\n            throw new SExpressionEvaluationException(stb.toString(), expression.getName());\n        }\n    }\n\n    protected boolean areReturnTypeCompatible(String leftReturnType, String rightReturnType, final String content) {\n        return (numericTypes.contains(leftReturnType) && numericTypes.contains(rightReturnType))\n                || leftReturnType.equals(rightReturnType)\n                || ConditionExpressionExecutorStrategy.EQUALS_COMPARATOR.equals(content);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/condition/BinaryComparatorMapper.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl.condition;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.expression.impl.ConditionExpressionExecutorStrategy;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class BinaryComparatorMapper {\n\n    private Map<String, BinaryComparator> evaluators;\n\n    public BinaryComparatorMapper(EqualityComparator equalityComparator, InequalityComparator inequalityComparator) {\n        evaluators = new HashMap<String, BinaryComparator>();\n        evaluators.put(ConditionExpressionExecutorStrategy.EQUALS_COMPARATOR, new EqualsComparator(equalityComparator));\n        evaluators.put(ConditionExpressionExecutorStrategy.NOT_EQUALS_COMPARATOR,\n                new DifferentComparator(equalityComparator));\n        evaluators.put(ConditionExpressionExecutorStrategy.GREATER_THAN_COMPARATOR,\n                new GreaterThanComparator(inequalityComparator));\n        evaluators.put(ConditionExpressionExecutorStrategy.GREATER_THAN_OR_EQUALS_COMPARATOR,\n                new GreaterThanOrEqualsComparator(inequalityComparator));\n        evaluators.put(ConditionExpressionExecutorStrategy.LESS_THAN_COMPARATOR,\n                new LessThanComparator(inequalityComparator));\n        evaluators.put(ConditionExpressionExecutorStrategy.lESS_THAN_OR_EQUALS_COMPARATOR,\n                new LessThanOrEqualsComparator(inequalityComparator));\n\n    }\n\n    public BinaryComparator getEvaluator(String operator) {\n        return evaluators.get(operator);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/condition/DifferentComparator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl.condition;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class DifferentComparator implements BinaryComparator {\n\n    private final EqualityComparator equalityComparator;\n\n    public DifferentComparator(EqualityComparator equalityComparator) {\n        this.equalityComparator = equalityComparator;\n    }\n\n    @Override\n    public <T> Boolean evaluate(final T left, final T right) {\n        return !equalityComparator.areEquals(left, right);\n    }\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/condition/EqualityComparator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl.condition;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class EqualityComparator {\n\n    public <T> Boolean areEquals(final T left, final T right) {\n        if (left == null) {\n            return right == null;\n        }\n        return left.equals(right);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/condition/EqualsComparator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl.condition;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class EqualsComparator implements BinaryComparator {\n\n    private final EqualityComparator equalityComparator;\n\n    public EqualsComparator(EqualityComparator equalityComparator) {\n        this.equalityComparator = equalityComparator;\n    }\n\n    @Override\n    public <T> Boolean evaluate(final T left, final T right) {\n        return equalityComparator.areEquals(left, right);\n    }\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/condition/GreaterThanComparator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl.condition;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class GreaterThanComparator implements BinaryComparator {\n\n    private final InequalityComparator comparator;\n\n    public GreaterThanComparator(InequalityComparator comparator) {\n        this.comparator = comparator;\n    }\n\n    @Override\n    public <T> Boolean evaluate(final T left, final T right) throws SComparisonException {\n        Integer compare = comparator.compareTo(left, right);\n        return compare == null ? null : compare > 0;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/condition/GreaterThanOrEqualsComparator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl.condition;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class GreaterThanOrEqualsComparator implements BinaryComparator {\n\n    private final InequalityComparator comparator;\n\n    public GreaterThanOrEqualsComparator(InequalityComparator comparator) {\n        this.comparator = comparator;\n    }\n\n    @Override\n    public <T> Boolean evaluate(final T left, final T right) throws SComparisonException {\n        Integer compare = comparator.compareTo(left, right);\n        return compare == null ? null : compare >= 0;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/condition/InequalityComparator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl.condition;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class InequalityComparator {\n\n    public <T> Integer compareTo(T left, T right) throws SComparisonException {\n        if (left == null || right == null) {\n            return null;\n        }\n        if (!(left instanceof Comparable) || !(right instanceof Comparable)) {\n            throw new SComparisonException(\n                    \"The following class must implement java.lang.Comparable: \" + left.getClass().getName());\n        }\n        return compare((Comparable) left, (Comparable) right);\n    }\n\n    private <T extends Comparable<Object>, R extends Comparable<Object>> Integer compare(final T left, final R right) {\n        return left.compareTo(right);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/condition/LessThanComparator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl.condition;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class LessThanComparator implements BinaryComparator {\n\n    private final InequalityComparator comparator;\n\n    public LessThanComparator(InequalityComparator comparator) {\n        this.comparator = comparator;\n    }\n\n    @Override\n    public <T> Boolean evaluate(final T left, final T right) throws SComparisonException {\n        Integer compare = comparator.compareTo(left, right);\n        return compare == null ? null : compare < 0;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/condition/LessThanOrEqualsComparator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl.condition;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class LessThanOrEqualsComparator implements BinaryComparator {\n\n    private final InequalityComparator comparator;\n\n    public LessThanOrEqualsComparator(InequalityComparator comparator) {\n        this.comparator = comparator;\n    }\n\n    @Override\n    public <T> Boolean evaluate(final T left, final T right) throws SComparisonException {\n        Integer compare = comparator.compareTo(left, right);\n        return compare == null ? null : compare <= 0;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/condition/LogicalComplementExecutor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl.condition;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.impl.ConditionExpressionExecutorStrategy;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class LogicalComplementExecutor {\n\n    public Boolean evaluate(final Map<Integer, Object> resolvedExpressions, SExpression expression)\n            throws SExpressionEvaluationException {\n        validate(expression);\n        List<SExpression> dependencies = expression.getDependencies();\n        Boolean sourceValue = (Boolean) resolvedExpressions.get(dependencies.get(0).getDiscriminant());\n        return sourceValue != null ? !sourceValue : null;\n    }\n\n    private void validate(SExpression expression) throws SExpressionEvaluationException {\n        List<SExpression> dependencies = expression.getDependencies();\n        if (dependencies.size() != 1) {\n            throw new SExpressionEvaluationException(\n                    \"The expression '\" + ConditionExpressionExecutorStrategy.LOGICAL_COMPLEMENT_OPERATOR\n                            + \"' must have exactly 1 dependency.\",\n                    expression.getName());\n        }\n        if (!Boolean.class.getName().equals(dependencies.get(0).getReturnType())) {\n            StringBuilder stb = new StringBuilder();\n            stb.append(\"The dependency of expression '\");\n            stb.append(ConditionExpressionExecutorStrategy.LOGICAL_COMPLEMENT_OPERATOR);\n            stb.append(\"' must have the return type \");\n            stb.append(Boolean.class.getName());\n            stb.append(\", but \");\n            stb.append(dependencies.get(0).getReturnType());\n            stb.append(\" was found.\");\n            throw new SExpressionEvaluationException(stb.toString(),\n                    expression.getName());\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/condition/SComparisonException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl.condition;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SComparisonException extends SBonitaException {\n\n    public SComparisonException(final String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/model/ExpressionKind.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.model;\n\nimport java.io.Serializable;\n\n/**\n * Used to identify a kind of expression\n * e.g.\n * for constant kind is : [ type = TYPE_CONSTANT, interpreter = null ]\n * for groovy kind is : [ type = TYPE_READ_SCRIPT, interpreter = GROOVY ]\n *\n * @author Baptiste Mesta\n */\npublic class ExpressionKind implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    public static final String NONE = \"NONE\";\n\n    private String interpreter = NONE;\n\n    private String type;\n\n    public ExpressionKind() {\n    }\n\n    public ExpressionKind(final String type) {\n        this.type = type;\n        interpreter = NONE;\n    }\n\n    public ExpressionKind(final String type, final String interpreter) {\n        this.type = type;\n        this.interpreter = interpreter == null || interpreter.isEmpty() ? NONE : interpreter;\n    }\n\n    public String getExpressionType() {\n        return type;\n    }\n\n    public String getInterpreter() {\n        return interpreter;\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    public void setType(final String type) {\n        this.type = type;\n    }\n\n    public void setInterpreter(final String interpreter) {\n        this.interpreter = interpreter == null || interpreter.isEmpty() ? NONE : interpreter;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + (interpreter == null ? 0 : interpreter.hashCode());\n        result = prime * result + (type == null ? 0 : type.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        final ExpressionKind other = (ExpressionKind) obj;\n        if (interpreter == null) {\n            if (other.interpreter != null) {\n                return false;\n            }\n        } else if (!interpreter.equals(other.interpreter)) {\n            return false;\n        }\n        if (type == null) {\n            if (other.type != null) {\n                return false;\n            }\n        } else if (!type.equals(other.type)) {\n            return false;\n        }\n        return true;\n    }\n\n    @Override\n    public String toString() {\n        final StringBuilder builder = new StringBuilder();\n        builder.append(\"ExpressionKind [interpreter=\");\n        builder.append(interpreter);\n        builder.append(\", type=\");\n        builder.append(type);\n        builder.append(\"]\");\n        return builder.toString();\n    }\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/model/SExpression.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.model;\n\nimport java.io.Serializable;\nimport java.util.List;\n\n/**\n * @author Zhao na\n * @author Emmanuel Duchastenier\n * @author Matthieu Chaffotte\n */\npublic interface SExpression extends Serializable {\n\n    String TYPE_CONSTANT = \"TYPE_CONSTANT\";\n\n    String TYPE_VARIABLE = \"TYPE_VARIABLE\";\n\n    String TYPE_PATTERN = \"TYPE_PATTERN\";\n\n    String TYPE_READ_ONLY_SCRIPT = \"TYPE_READ_ONLY_SCRIPT\";\n\n    String TYPE_READ_WRITE_SCRIPT = \"TYPE_READ_WRITE_SCRIPT\";\n\n    String TYPE_PARAMETER = \"TYPE_PARAMETER\";\n\n    String TYPE_I18N = \"TYPE_I18N\";\n\n    String GROOVY = \"GROOVY\";\n\n    String JAVASCRIPT = \"JAVASCRIPT\";\n\n    String TYPE_INPUT = \"TYPE_INPUT\";\n\n    String TYPE_LIST = \"TYPE_LIST\";\n\n    String TYPE_CONDITION = \"TYPE_CONDITION\";\n\n    String getName();\n\n    String getContent();\n\n    String getExpressionType();\n\n    ExpressionKind getExpressionKind();\n\n    String getReturnType();\n\n    String getInterpreter();\n\n    List<SExpression> getDependencies();\n\n    boolean hasDependencies();\n\n    int getDiscriminant();\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/model/SExpressionType.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.model;\n\n/**\n * FIXME: this class is only used in tests. Delete it.\n *\n * @author Zhao na\n * @author Emmanuel Duchastenier\n */\npublic enum SExpressionType {\n\n    TYPE_CONSTANT, TYPE_VARIABLE, TYPE_PATTERN, TYPE_READ_WRITE_SCRIPT, // Optional\n    TYPE_PARAMETER, TYPE_I18N, TYPE_READ_ONLY_SCRIPT_GROOVY, TYPE_READ_ONLY_SCRIPT_JAVASCRIPT, TYPE_INPUT, TYPE_XPATH_READ\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/model/builder/SExpressionBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.model.builder;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Zhao na\n */\npublic interface SExpressionBuilder {\n\n    SExpressionBuilder setName(String name);\n\n    SExpressionBuilder setContent(String content);\n\n    SExpressionBuilder setExpressionType(String expressionType);\n\n    SExpressionBuilder setReturnType(final String returnType);\n\n    SExpressionBuilder setInterpreter(final String interpreter);\n\n    SExpressionBuilder setDependencies(final List<SExpression> dependencies);\n\n    SExpression done() throws SInvalidExpressionException;\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/model/builder/SExpressionBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.model.builder;\n\n/**\n * @author Zhao na\n */\npublic interface SExpressionBuilderFactory {\n\n    SExpressionBuilder createNewInstance();\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/model/builder/impl/SExpressionBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.model.builder.impl;\n\nimport org.bonitasoft.engine.expression.model.builder.SExpressionBuilder;\nimport org.bonitasoft.engine.expression.model.builder.SExpressionBuilderFactory;\nimport org.bonitasoft.engine.expression.model.impl.SExpressionImpl;\n\n/**\n * @author Zhao Na\n */\npublic class SExpressionBuilderFactoryImpl implements SExpressionBuilderFactory {\n\n    @Override\n    public SExpressionBuilder createNewInstance() {\n        final SExpressionImpl expression = new SExpressionImpl();\n        return new SExpressionBuilderImpl(expression);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/model/builder/impl/SExpressionBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.model.builder.impl;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.expression.model.builder.SExpressionBuilder;\nimport org.bonitasoft.engine.expression.model.impl.SExpressionImpl;\n\n/**\n * @author Zhao Na\n * @author Celine Souchet\n */\npublic class SExpressionBuilderImpl implements SExpressionBuilder {\n\n    private final SExpressionImpl expression;\n\n    public SExpressionBuilderImpl(final SExpressionImpl expression) {\n        super();\n        this.expression = expression;\n    }\n\n    @Override\n    public SExpression done() throws SInvalidExpressionException {\n        if (expression.getReturnType() == null) {\n            throw new SInvalidExpressionException(\"Expression return type must be set.\", expression.getName());\n        }\n        return expression;\n    }\n\n    @Override\n    public SExpressionBuilder setName(final String name) {\n        expression.setName(name);\n        return this;\n    }\n\n    @Override\n    public SExpressionBuilder setContent(final String content) {\n        expression.setContent(content);\n        return this;\n    }\n\n    @Override\n    public SExpressionBuilder setExpressionType(final String expressionType) {\n        expression.setExpressionType(expressionType);\n        return this;\n    }\n\n    @Override\n    public SExpressionBuilder setReturnType(final String returnType) {\n        expression.setReturnType(returnType);\n        return this;\n    }\n\n    @Override\n    public SExpressionBuilder setInterpreter(final String interpreter) {\n        expression.setInterpreter(interpreter);\n        return this;\n    }\n\n    @Override\n    public SExpressionBuilder setDependencies(final List<SExpression> dependencies) {\n        expression.setDependencies(dependencies);\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/model/impl/SExpressionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.model.impl;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\n\nimport org.bonitasoft.engine.expression.model.ExpressionKind;\nimport org.bonitasoft.engine.expression.model.SExpression;\n\n/**\n * @author Zhao Na\n * @author Matthieu Chaffotte\n * @author Emmanuel Duchastenier\n * @author Baptiste Mesta\n */\npublic class SExpressionImpl implements SExpression {\n\n    private static final long serialVersionUID = 1L;\n\n    private String name;\n\n    private String content;\n\n    private String returnType;\n\n    private List<SExpression> dependencies;\n\n    private final ExpressionKind expressionKind = new ExpressionKind();\n\n    public SExpressionImpl() {\n        super();\n    }\n\n    public SExpressionImpl(final String name, final String content, final String expressionType,\n            final String returnType, final String interpreter,\n            final List<SExpression> dependencies) {\n        this.name = name;\n        this.content = content;\n        expressionKind.setType(expressionType);\n        this.returnType = returnType;\n        expressionKind.setInterpreter(interpreter);\n        this.dependencies = dependencies;\n    }\n\n    public void setContent(final String content) {\n        this.content = content;\n    }\n\n    public void setExpressionType(final String expressionType) {\n        expressionKind.setType(expressionType);\n    }\n\n    public void setReturnType(final String returnType) {\n        this.returnType = returnType;\n    }\n\n    public void setInterpreter(final String interpreter) {\n        expressionKind.setInterpreter(interpreter);\n    }\n\n    public void setDependencies(final List<SExpression> dependencies) {\n        this.dependencies = dependencies;\n    }\n\n    @Override\n    public String getName() {\n        return name;\n    }\n\n    public void setName(final String name) {\n        this.name = name;\n    }\n\n    @Override\n    public String getContent() {\n        return content;\n    }\n\n    @Override\n    public String getExpressionType() {\n        return expressionKind.getExpressionType();\n    }\n\n    @Override\n    public String getReturnType() {\n        return returnType;\n    }\n\n    @Override\n    public String getInterpreter() {\n        return expressionKind.getInterpreter();\n    }\n\n    @Override\n    public List<SExpression> getDependencies() {\n        if (dependencies == null) {\n            return Collections.emptyList();\n        }\n        return dependencies;\n    }\n\n    @Override\n    public boolean hasDependencies() {\n        return !getDependencies().isEmpty();\n    }\n\n    @Override\n    public ExpressionKind getExpressionKind() {\n        return expressionKind;\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(name, content, returnType, dependencies, expressionKind);\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (!(o instanceof SExpressionImpl that))\n            return false;\n        return Objects.equals(name, that.name) &&\n                Objects.equals(content, that.content) &&\n                Objects.equals(returnType, that.returnType) &&\n                Objects.equals(dependencies, that.dependencies) &&\n                Objects.equals(expressionKind, that.expressionKind);\n    }\n\n    @Override\n    public String toString() {\n        final int maxLen = 5;\n        final StringBuilder builder = new StringBuilder();\n        builder.append(\"SExpressionImpl [name=\");\n        builder.append(name);\n        builder.append(\", content=\");\n        builder.append(content);\n        builder.append(\", returnType=\");\n        builder.append(returnType);\n        builder.append(\", dependencies=\");\n        builder.append(dependencies != null ? dependencies.subList(0, Math.min(dependencies.size(), maxLen)) : null);\n        builder.append(\", expressionKind=\");\n        builder.append(expressionKind);\n        builder.append(\"]\");\n        return builder.toString();\n    }\n\n    @Override\n    public int getDiscriminant() {\n        return hashCode();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/exception/SExpressionEvaluationExceptionTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.exception;\n\nimport static org.junit.Assert.assertEquals;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Celine Souchet\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class SExpressionEvaluationExceptionTest {\n\n    @Mock\n    private Throwable cause;\n\n    private final String message = \"message\";\n\n    private final String expressionName = \"plop\";\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.expression.exception.SExpressionEvaluationException#getExpressionName()}.\n     */\n    @Test\n    public final void return_expression_name_when_use_constructor_with_cause() {\n        final SExpressionEvaluationException sExpressionEvaluationException = new SExpressionEvaluationException(cause,\n                expressionName);\n\n        final String result = sExpressionEvaluationException.getExpressionName();\n        assertEquals(expressionName, result);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.expression.exception.SExpressionEvaluationException#getExpressionName()}.\n     */\n    @Test\n    public final void return_expression_name_when_use_constructor_with_message() {\n        final SExpressionEvaluationException sExpressionEvaluationException = new SExpressionEvaluationException(\n                message, expressionName);\n\n        final String result = sExpressionEvaluationException.getExpressionName();\n        assertEquals(expressionName, result);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.expression.exception.SExpressionEvaluationException#getExpressionName()}.\n     */\n    @Test\n    public final void return_expression_name_when_use_constructor_with_message_and_cause() {\n        final SExpressionEvaluationException sExpressionEvaluationException = new SExpressionEvaluationException(\n                message, cause, expressionName);\n\n        final String result = sExpressionEvaluationException.getExpressionName();\n        assertEquals(expressionName, result);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/exception/SInvalidExpressionExceptionTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.exception;\n\nimport static org.junit.Assert.assertEquals;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Celine Souchet\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class SInvalidExpressionExceptionTest {\n\n    @Mock\n    private Throwable cause;\n\n    private final String expressionName = \"expressionName\";\n\n    private final String message = \"message\";\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.expression.exception.SInvalidExpressionException#getExpressionName()}.\n     */\n    @Test\n    public final void return_expression_name_when_use_constructor_with_cause() {\n        final SInvalidExpressionException sExpressionEvaluationException = new SInvalidExpressionException(cause,\n                expressionName);\n\n        final String result = sExpressionEvaluationException.getExpressionName();\n        assertEquals(expressionName, result);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.expression.exception.SInvalidExpressionException#getExpressionName()}.\n     */\n    @Test\n    public final void return_expression_name_when_use_constructor_with_message() {\n        final SInvalidExpressionException sExpressionEvaluationException = new SInvalidExpressionException(message,\n                expressionName);\n\n        final String result = sExpressionEvaluationException.getExpressionName();\n        assertEquals(expressionName, result);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.expression.exception.SInvalidExpressionException#getExpressionName()}.\n     */\n    @Test\n    public final void return_expression_name_when_use_constructor_with_message_and_cause() {\n        final SInvalidExpressionException sExpressionEvaluationException = new SInvalidExpressionException(message,\n                cause, expressionName);\n\n        final String result = sExpressionEvaluationException.getExpressionName();\n        assertEquals(expressionName, result);\n    }\n}\n"
  },
  {
    "path": "services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/ConditionExpressionExecutorStrategyTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.expression.ContainerState;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.impl.condition.BinaryComparatorExecutor;\nimport org.bonitasoft.engine.expression.impl.condition.LogicalComplementExecutor;\nimport org.bonitasoft.engine.expression.model.ExpressionKind;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.expression.model.impl.SExpressionImpl;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Baptiste Mesta\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class ConditionExpressionExecutorStrategyTest {\n\n    @InjectMocks\n    private ConditionExpressionExecutorStrategy strategy;\n\n    private ArrayList<SExpression> dependFirstLtSecond;\n\n    private ArrayList<SExpression> dependFirstGtSecond;\n\n    private ArrayList<SExpression> dependEquals;\n\n    private HashMap<Integer, Object> resolvedDependencies;\n\n    @Mock\n    private LogicalComplementExecutor logicalComplementExecutor;\n\n    @Mock\n    private BinaryComparatorExecutor binaryComparatorExecutor;\n\n    @Before\n    public void initialiseDependencies() {\n\n        final SExpression constExpr1 = buildExpression(\"5\", SExpression.TYPE_CONSTANT, Integer.class.getName(), null,\n                null);\n        final SExpression constExpr2 = buildExpression(\"7\", SExpression.TYPE_CONSTANT, Integer.class.getName(), null,\n                null);\n        final SExpression constExpr3 = buildExpression(\"2\", SExpression.TYPE_CONSTANT, Integer.class.getName(), null,\n                null);\n\n        dependFirstLtSecond = new ArrayList<SExpression>(2);\n        dependFirstLtSecond.add(constExpr1);\n        dependFirstLtSecond.add(constExpr2);\n\n        dependFirstGtSecond = new ArrayList<SExpression>(2);\n        dependFirstGtSecond.add(constExpr2);\n        dependFirstGtSecond.add(constExpr1);\n\n        dependEquals = new ArrayList<SExpression>(2);\n        dependEquals.add(constExpr1);\n        dependEquals.add(constExpr1);\n\n        resolvedDependencies = new HashMap<Integer, Object>(2);\n        resolvedDependencies.put(constExpr1.getDiscriminant(), 5);\n        resolvedDependencies.put(constExpr2.getDiscriminant(), 7);\n        resolvedDependencies.put(constExpr3.getDiscriminant(), 2);\n    }\n\n    private SExpression buildExpression(final String content, final String expressionType, final String returnType,\n            final String interpreter,\n            final List<SExpression> dependencies) {\n        final SExpressionImpl eb = new SExpressionImpl();\n        eb.setName(content);\n        eb.setContent(content);\n        eb.setExpressionType(expressionType);\n        eb.setInterpreter(interpreter);\n        eb.setReturnType(returnType);\n        eb.setDependencies(dependencies);\n        return eb;\n    }\n\n    protected List<Object> evaluate(final List<SExpression> expression, final Map<Integer, Object> resolvedExpressions,\n            final ContainerState containerState)\n            throws SExpressionEvaluationException {\n        return strategy.evaluate(expression, new HashMap<String, Object>(0), resolvedExpressions, containerState);\n    }\n\n    @Test\n    public void evaluate_lstOfExpressions_should_return_list_of_results() throws Exception {\n        //given\n        final SExpression expr1 = buildExpression(\"!=\", SExpression.TYPE_CONDITION, Boolean.class.getName(), null,\n                dependFirstGtSecond);\n        final SExpression expr2 = buildExpression(\"!=\", SExpression.TYPE_CONDITION, Boolean.class.getName(), null,\n                dependFirstLtSecond);\n        final SExpression expr3 = buildExpression(\"!=\", SExpression.TYPE_CONDITION, Boolean.class.getName(), null,\n                dependEquals);\n\n        Map<String, Object> context = Collections.emptyMap();\n        ConditionExpressionExecutorStrategy mockedStrategy = spy(strategy);\n        given(mockedStrategy.evaluate(expr1, context, resolvedDependencies, ContainerState.ACTIVE)).willReturn(true);\n        given(mockedStrategy.evaluate(expr2, context, resolvedDependencies, ContainerState.ACTIVE)).willReturn(false);\n        given(mockedStrategy.evaluate(expr3, context, resolvedDependencies, ContainerState.ACTIVE)).willReturn(null);\n\n        //when\n        List<Object> resolvedExpressions = mockedStrategy.evaluate(Arrays.asList(expr1, expr2, expr3), context,\n                resolvedDependencies, ContainerState.ACTIVE);\n\n        //then\n        assertThat(resolvedExpressions).containsExactly(true, false, null);\n    }\n\n    @Test\n    public void evaluate_should_return_result_of_LogicalComplementExecutor_when_content_is_logical_complement_operator()\n            throws Exception {\n        //given\n        Map<Integer, Object> resolvedExpressions = Collections.emptyMap();\n        SExpression expression = mock(SExpression.class);\n        given(expression.getContent()).willReturn(ConditionExpressionExecutorStrategy.LOGICAL_COMPLEMENT_OPERATOR);\n\n        given(logicalComplementExecutor.evaluate(resolvedExpressions, expression)).willReturn(true);\n\n        //when\n        Object value = strategy.evaluate(expression, new HashMap<String, Object>(0), resolvedExpressions,\n                ContainerState.ACTIVE);\n\n        //then\n        assertThat(value).isEqualTo(true);\n    }\n\n    @Test\n    public void evaluate_should_return_result_of_BinaryComparatorExecutor_when_content_is_a_binary_operator()\n            throws Exception {\n        //given\n        Map<Integer, Object> resolvedExpressions = Collections.emptyMap();\n        SExpression expression = mock(SExpression.class);\n        given(expression.getContent()).willReturn(ConditionExpressionExecutorStrategy.GREATER_THAN_COMPARATOR);\n\n        given(binaryComparatorExecutor.evaluate(resolvedExpressions, expression)).willReturn(true);\n\n        //when\n        Object value = strategy.evaluate(expression, new HashMap<String, Object>(0), resolvedExpressions,\n                ContainerState.ACTIVE);\n\n        //then\n        assertThat(value).isEqualTo(true);\n    }\n\n    @Test\n    public void mustPutEvaluatedExpressionInContext_should_return_false() throws Exception {\n        //given\n\n        //when\n        boolean value = strategy.mustPutEvaluatedExpressionInContext();\n\n        //then\n        assertThat(value).isFalse();\n    }\n\n    @Test\n    public void getExpressionKind_should_return() throws Exception {\n        //given\n\n        //when\n        ExpressionKind kind = strategy.getExpressionKind();\n\n        //then\n        assertThat(kind.getInterpreter()).isEqualTo(ExpressionKind.NONE);\n        assertThat(kind.getType()).isEqualTo(SExpression.TYPE_CONDITION);\n    }\n\n    @Test(expected = SInvalidExpressionException.class)\n    public void validate_should_throw_exception_if_expression_is_null() throws Exception {\n        //given\n\n        //when\n        strategy.validate(null);\n\n        //then exception\n    }\n\n    @Test(expected = SInvalidExpressionException.class)\n    public void validate_should_throw_exception_if_expression_content_is_null() throws Exception {\n        //given\n        SExpression expression = mock(SExpression.class);\n        given(expression.getContent()).willReturn(null);\n\n        //when\n        strategy.validate(expression);\n\n        //then exception\n    }\n\n    @Test(expected = SInvalidExpressionException.class)\n    public void validate_should_throw_exception_if_expression_content_is_an_invalid_operator() throws Exception {\n        //given\n        SExpression expression = mock(SExpression.class);\n        given(expression.getContent()).willReturn(\"^\");\n\n        //when\n        strategy.validate(expression);\n\n        //then exception\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/ConstantExpressionExecutorStrategyTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.assertNotNull;\n\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.Month;\nimport java.time.OffsetDateTime;\nimport java.time.ZoneOffset;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.bonitasoft.engine.expression.ContainerState;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.expression.model.impl.SExpressionImpl;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Celine Souchet\n */\npublic class ConstantExpressionExecutorStrategyTest {\n\n    private ConstantExpressionExecutorStrategy strategy;\n\n    @Before\n    public void setup() {\n        strategy = new ConstantExpressionExecutorStrategy();\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.expression.impl.ConstantExpressionExecutorStrategy#evaluate(org.bonitasoft.engine.expression.model.SExpression, java.util.Map, java.util.Map)}\n     * .\n     *\n     * @throws SExpressionEvaluationException\n     */\n    @Test\n    public final void evaluateDate() throws SExpressionEvaluationException {\n        final SExpression sExpression = buildExpression(\"2013-07-18T14:49:26.86+02:00\", SExpression.TYPE_CONSTANT,\n                Date.class.getName(), null, null);\n\n        final Date result = (Date) strategy.evaluate(sExpression, null, null, ContainerState.ACTIVE);\n        assertNotNull(result);\n    }\n\n    @Test\n    public final void evaluateDateWithoutTimeZone() throws SExpressionEvaluationException {\n        final SExpression sExpression = buildExpression(\"2013-07-18T14:49:26.86\", SExpression.TYPE_CONSTANT,\n                Date.class.getName(), null, null);\n\n        final Date result = (Date) strategy.evaluate(sExpression, null, null, ContainerState.ACTIVE);\n        assertNotNull(result);\n    }\n\n    @Test\n    public final void evaluateDateWithoutMilliseconds() throws SExpressionEvaluationException {\n        final SExpression sExpression = buildExpression(\"2013-07-18T14:49:26+02:00\", SExpression.TYPE_CONSTANT,\n                Date.class.getName(), null, null);\n\n        final Date result = (Date) strategy.evaluate(sExpression, null, null, ContainerState.ACTIVE);\n        assertNotNull(result);\n    }\n\n    @Test\n    public void should_evaluate_local_date() throws Exception {\n        final SExpression sExpression = buildExpression(\"2013-07-18\", SExpression.TYPE_CONSTANT,\n                LocalDate.class.getName(), null, null);\n\n        final LocalDate result = (LocalDate) strategy.evaluate(sExpression, null, null, ContainerState.ACTIVE);\n        assertThat(result.getYear()).isEqualTo(2013);\n        assertThat(result.getMonth()).isEqualTo(Month.JULY);\n        assertThat(result.getDayOfMonth()).isEqualTo(18);\n    }\n\n    @Test\n    public void should_evaluate_local_date_time() throws Exception {\n        final SExpression sExpression = buildExpression(\"2013-07-18T12:00:00\", SExpression.TYPE_CONSTANT,\n                LocalDateTime.class.getName(), null, null);\n\n        final LocalDateTime result = (LocalDateTime) strategy.evaluate(sExpression, null, null, ContainerState.ACTIVE);\n        assertThat(result.getYear()).isEqualTo(2013);\n        assertThat(result.getMonth()).isEqualTo(Month.JULY);\n        assertThat(result.getDayOfMonth()).isEqualTo(18);\n        assertThat(result.getHour()).isEqualTo(12);\n        assertThat(result.getMinute()).isEqualTo(0);\n        assertThat(result.getSecond()).isEqualTo(0);\n    }\n\n    @Test\n    public void should_evaluate_offset_date_time() throws Exception {\n        final SExpression sExpression = buildExpression(\"2007-12-03T10:15:30+01:00\", SExpression.TYPE_CONSTANT,\n                OffsetDateTime.class.getName(), null, null);\n\n        final OffsetDateTime result = (OffsetDateTime) strategy.evaluate(sExpression, null, null,\n                ContainerState.ACTIVE);\n        assertThat(result.getYear()).isEqualTo(2007);\n        assertThat(result.getMonth()).isEqualTo(Month.DECEMBER);\n        assertThat(result.getDayOfMonth()).isEqualTo(3);\n        assertThat(result.getHour()).isEqualTo(10);\n        assertThat(result.getMinute()).isEqualTo(15);\n        assertThat(result.getSecond()).isEqualTo(30);\n        assertThat(result.getOffset()).isEqualTo(ZoneOffset.ofHours(1));\n    }\n\n    private SExpression buildExpression(final String content, final String expressionType, final String returnType,\n            final String interpreter,\n            final List<SExpression> dependencies) {\n        final SExpressionImpl eb = new SExpressionImpl();\n        eb.setName(content);\n        eb.setContent(content);\n        eb.setExpressionType(expressionType);\n        eb.setInterpreter(interpreter);\n        eb.setReturnType(returnType);\n        eb.setDependencies(dependencies);\n        return eb;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/ExpressionServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\n\nimport org.bonitasoft.engine.expression.ContainerState;\nimport org.bonitasoft.engine.expression.ExpressionExecutorStrategy;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.tracking.TimeTracker;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@RunWith(MockitoJUnitRunner.class)\n@SuppressWarnings(\"javadoc\")\npublic class ExpressionServiceImplTest {\n\n    @Mock\n    private ExpressionExecutorStrategy expressionExecutorStrategy;\n\n    private ExpressionServiceImpl expressionService;\n\n    @Before\n    public void setUp() {\n    }\n\n    @Test\n    public void evaluateInvalidExpressionFailsAtValidationStep() throws Exception {\n        final SExpression expression = mock(SExpression.class);\n        final TimeTracker timeTracker = mock(TimeTracker.class);\n        expressionService = new ExpressionServiceImpl(true, timeTracker);\n        expressionService.setExpressionExecutorStrategy(Arrays.asList(expressionExecutorStrategy));\n        expressionService.evaluate(expression, Collections.<String, Object> singletonMap(\"processDefinitionId\", 546l),\n                new HashMap<Integer, Object>(0), ContainerState.ACTIVE);\n        verify(expressionExecutorStrategy, times(1)).validate(any(SExpression.class));\n    }\n}\n"
  },
  {
    "path": "services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/GroovyScriptConditionExpressionExecutorStrategyTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl;\n\nimport static java.util.Collections.emptyMap;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.doReturn;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.cache.CacheConfiguration;\nimport org.bonitasoft.engine.cache.ehcache.EhCacheCacheService;\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.expression.ContainerState;\nimport org.bonitasoft.engine.expression.ExpressionExecutorStrategy;\nimport org.bonitasoft.engine.expression.model.impl.SExpressionImpl;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class GroovyScriptConditionExpressionExecutorStrategyTest {\n\n    @Mock\n    private ClassLoaderService classLoaderService;\n\n    private EhCacheCacheService cacheService;\n\n    private GroovyScriptConditionExpressionExecutorStrategy executorStrategy;\n\n    private Map<String, Object> context;\n\n    @Before\n    public void setup() throws Exception {\n        // Create valid cache configurations for Ehcache 3 (heap-only)\n        final CacheConfiguration cacheConfiguration = new CacheConfiguration();\n        cacheConfiguration.setName(\"GROOVY_SCRIPT_CACHE_NAME\");\n        cacheConfiguration.setMaxElementsInMemory(1000);\n\n        final CacheConfiguration defaultCacheConfiguration = new CacheConfiguration();\n        defaultCacheConfiguration.setMaxElementsInMemory(1000);\n\n        final List<CacheConfiguration> cacheConfigurations = Collections.singletonList(cacheConfiguration);\n        cacheService = new EhCacheCacheService(cacheConfigurations, defaultCacheConfiguration);\n        cacheService.start();\n        executorStrategy = new GroovyScriptConditionExpressionExecutorStrategy(cacheService, classLoaderService);\n        doReturn(GroovyScriptExpressionExecutorCacheStrategyTest.class.getClassLoader()).when(classLoaderService)\n                .getClassLoader(any());\n        context = new HashMap<>();\n        context.put(ExpressionExecutorStrategy.DEFINITION_ID, 123456789L);\n    }\n\n    @After\n    public void stop() {\n        cacheService.stop();\n    }\n\n    @Test\n    public void should_return_a_true_boolean_value() throws Exception {\n        //given\n        final SExpressionImpl expression = new SExpressionImpl(\"myExpr\", \"'toto'\", null, \"java.lang.Boolean\", null,\n                Collections.emptyList());\n        // when\n        final Object evaluate = executorStrategy.evaluate(expression, context, emptyMap(), ContainerState.ACTIVE);\n\n        // then\n        assertThat(evaluate).isEqualTo(true);\n    }\n\n    @Test\n    public void should_return_a_false_boolean_value() throws Exception {\n        //given\n        context.put(\"toto\", null);\n        final SExpressionImpl expression = new SExpressionImpl(\"myExpr\", \"toto\", null, \"java.lang.Boolean\", null,\n                Collections.emptyList());\n        // when\n        final Object evaluate = executorStrategy.evaluate(expression, context, emptyMap(), ContainerState.ACTIVE);\n\n        // then\n        assertThat(evaluate).isEqualTo(false);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/GroovyScriptExpressionExecutorCacheStrategyTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl;\n\nimport static java.util.Collections.emptyMap;\nimport static java.util.Collections.singletonMap;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.expression.ExpressionExecutorStrategy.DEFINITION_ID;\nimport static org.hamcrest.core.Is.is;\nimport static org.hamcrest.core.IsInstanceOf.instanceOf;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.doReturn;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport groovy.lang.GroovyShell;\nimport org.bonitasoft.engine.bpm.contract.FileInputValue;\nimport org.bonitasoft.engine.bpm.document.Document;\nimport org.bonitasoft.engine.bpm.document.DocumentValue;\nimport org.bonitasoft.engine.cache.CacheConfiguration;\nimport org.bonitasoft.engine.cache.SCacheException;\nimport org.bonitasoft.engine.cache.ehcache.EhCacheCacheService;\nimport org.bonitasoft.engine.classloader.ClassLoaderService;\nimport org.bonitasoft.engine.classloader.SClassLoaderException;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException;\nimport org.bonitasoft.engine.expression.ContainerState;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.expression.model.builder.SExpressionBuilder;\nimport org.bonitasoft.engine.expression.model.builder.impl.SExpressionBuilderFactoryImpl;\nimport org.bonitasoft.engine.expression.model.impl.SExpressionImpl;\nimport org.codehaus.groovy.runtime.typehandling.GroovyCastException;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class GroovyScriptExpressionExecutorCacheStrategyTest {\n\n    @Mock\n    private ClassLoaderService classLoaderService;\n\n    private EhCacheCacheService cacheService;\n\n    private GroovyScriptExpressionExecutorCacheStrategy groovyScriptExpressionExecutorCacheStrategy;\n\n    private Class script2;\n    private Map<String, Object> context;\n\n    @Before\n    public void setup() throws Exception {\n        // Create valid cache configurations for Ehcache 3 (heap-only)\n        final CacheConfiguration cacheConfiguration = new CacheConfiguration();\n        cacheConfiguration.setName(\"GROOVY_SCRIPT_CACHE_NAME\");\n        cacheConfiguration.setMaxElementsInMemory(1000);\n\n        final CacheConfiguration defaultCacheConfiguration = new CacheConfiguration();\n        defaultCacheConfiguration.setMaxElementsInMemory(1000);\n\n        final List<CacheConfiguration> cacheConfigurations = Collections.singletonList(cacheConfiguration);\n        cacheService = new EhCacheCacheService(cacheConfigurations, defaultCacheConfiguration);\n        cacheService.start();\n        groovyScriptExpressionExecutorCacheStrategy = new GroovyScriptExpressionExecutorCacheStrategy(cacheService,\n                classLoaderService);\n        doReturn(GroovyScriptExpressionExecutorCacheStrategyTest.class.getClassLoader()).when(classLoaderService)\n                .getClassLoader(any());\n        context = new HashMap<>();\n        context.put(DEFINITION_ID, 123456789L);\n    }\n\n    @After\n    public void teardown() {\n        cacheService.stop();\n    }\n\n    @Test\n    public void should_getShell_return_a_shell_for_each_definition() throws Exception {\n        // given\n\n        // when\n        final GroovyShell shell1 = groovyScriptExpressionExecutorCacheStrategy.getShell(12L);\n        final GroovyShell shell2 = groovyScriptExpressionExecutorCacheStrategy.getShell(13L);\n\n        // then\n        assertThat(shell1).isNotNull();\n        assertThat(shell2).isNotNull();\n        assertThat(shell1).isNotEqualTo(shell2);\n    }\n\n    @Test\n    public void should_update_on_classloader_listener_clear_shell_cache() throws Exception {\n        // given\n\n        // when\n        final GroovyShell shell1 = groovyScriptExpressionExecutorCacheStrategy.getShell(12L);\n        groovyScriptExpressionExecutorCacheStrategy.onUpdate(null);\n        final GroovyShell shell2 = groovyScriptExpressionExecutorCacheStrategy.getShell(12L);\n\n        // then\n        assertThat(shell1).isNotNull();\n        assertThat(shell2).isNotNull();\n        assertThat(shell1).isNotEqualTo(shell2);\n    }\n\n    @Test\n    public void should_destroy_on_classloader_listener_clear_shell_cache() throws Exception {\n        // given\n\n        // when\n        final GroovyShell shell1 = groovyScriptExpressionExecutorCacheStrategy.getShell(12L);\n        groovyScriptExpressionExecutorCacheStrategy.onDestroy(null);\n        final GroovyShell shell2 = groovyScriptExpressionExecutorCacheStrategy.getShell(12L);\n\n        // then\n        assertThat(shell1).isNotNull();\n        assertThat(shell2).isNotNull();\n        assertThat(shell1).isNotEqualTo(shell2);\n    }\n\n    @Test\n    public void should_getShell_return_same_shell_for_1_definition() throws Exception {\n        // when\n        final GroovyShell shell1 = groovyScriptExpressionExecutorCacheStrategy.getShell(12L);\n        final GroovyShell shell2 = groovyScriptExpressionExecutorCacheStrategy.getShell(12L);\n\n        // then\n        assertThat(shell1).isNotNull();\n        assertThat(shell1).isEqualTo(shell2);\n    }\n\n    @Test\n    public void should_getScriptFromCache_return_a_the_same_script_class_if_in_same_definition() throws Exception {\n        // when\n        final Class script1 = groovyScriptExpressionExecutorCacheStrategy.getScriptFromCache(\"MyScriptContent\", 12L);\n        final Class script2 = groovyScriptExpressionExecutorCacheStrategy.getScriptFromCache(\"MyScriptContent\", 12L);\n\n        // then\n        assertThat(script1).isNotNull();\n        assertThat(script1).isEqualTo(script2);\n\n    }\n\n    @Test\n    public void should_getScriptFromCache_should_cache_only_once_when_on_different_threads() throws Exception {\n        // when\n        final Class script1 = groovyScriptExpressionExecutorCacheStrategy.getScriptFromCache(\"MyScriptContent\", 12L);\n\n        Thread thread = new Thread(new Runnable() {\n\n            @Override\n            public void run() {\n                try {\n                    script2 = groovyScriptExpressionExecutorCacheStrategy.getScriptFromCache(\"MyScriptContent\", 12L);\n                } catch (SCacheException | SClassLoaderException e) {\n                    e.printStackTrace();\n                }\n            }\n        });\n        thread.start();\n        thread.join();\n\n        // then\n        assertThat(script1).isNotNull();\n        assertThat(script2).isNotNull();\n        assertThat(script1).isEqualTo(script2);\n\n    }\n\n    @Test\n    public void should_getScriptFromCache_return_different_script_if_different_definition() throws Exception {\n        // when\n        final Class script1 = groovyScriptExpressionExecutorCacheStrategy.getScriptFromCache(\"MyScriptContent\", 12L);\n        final Class script2 = groovyScriptExpressionExecutorCacheStrategy.getScriptFromCache(\"MyScriptContent\", 13L);\n\n        // then\n        assertThat(script1).isNotNull();\n        assertThat(script2).isNotNull();\n        assertThat(script1).isNotEqualTo(script2);\n    }\n\n    @Test\n    public void should_getScriptFromCache_return_different_script_if_content_is_different_definition()\n            throws Exception {\n        // when\n        final Class script1 = groovyScriptExpressionExecutorCacheStrategy.getScriptFromCache(\"MyScriptContent1\", 12L);\n        final Class script2 = groovyScriptExpressionExecutorCacheStrategy.getScriptFromCache(\"MyScriptContent2\", 12L);\n\n        // then\n        assertThat(script1).isNotNull();\n        assertThat(script2).isNotNull();\n        assertThat(script1).isNotEqualTo(script2);\n    }\n\n    @Test(expected = SBonitaRuntimeException.class)\n    public void should_not_put_in_cache_script_without_definition_id() throws Exception {\n\n        // when\n        groovyScriptExpressionExecutorCacheStrategy.getScriptFromCache(\"MyScriptContent1\", null);\n\n        // then\n        //exception\n    }\n\n    @Test\n    public void should_evaluate_return_the_evaluation() throws Exception {\n        //given\n        final SExpressionImpl expression = new SExpressionImpl(\"myExpr\", \"'toto'\", null, \"java.lang.String\", null,\n                Collections.emptyList());\n        // when\n        final Object evaluate = groovyScriptExpressionExecutorCacheStrategy.evaluate(expression, context,\n                Collections.emptyMap(),\n                ContainerState.ACTIVE);\n\n        // then\n        assertThat(evaluate).isEqualTo(\"toto\");\n    }\n\n    @Test(expected = SExpressionEvaluationException.class)\n    public void should_evaluate_throw_SExpressionEvaluationException_when_script_throws_Error() throws Exception {\n        //given\n        final SExpressionImpl expression = new SExpressionImpl(\"myExpr\", \"throw new java.lang.NoClassDefFoundError()\",\n                null, \"java.lang.String\", null,\n                Collections.emptyList());\n        // when\n        groovyScriptExpressionExecutorCacheStrategy.evaluate(expression, context,\n                Collections.emptyMap(), ContainerState.ACTIVE);\n\n        // then\n        //exception\n    }\n\n    @Test(expected = SExpressionEvaluationException.class)\n    public void should_evaluate_throw_SExpressionEvaluationException_when_script_throws_Throwable() throws Exception {\n        //given\n        final SExpressionImpl expression = new SExpressionImpl(\"myExpr\", \"throw new java.lang.Throwable()\", null,\n                \"java.lang.String\", null,\n                Collections.emptyList());\n        // when\n        groovyScriptExpressionExecutorCacheStrategy.evaluate(expression, context,\n                Collections.emptyMap(), ContainerState.ACTIVE);\n        // then\n        //exception\n    }\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void evaluation_with_DefaultGroovyMethod_size_should_return_4()\n            throws SExpressionEvaluationException, SInvalidExpressionException {\n        String content = \"import static org.codehaus.groovy.runtime.DefaultGroovyMethods.*\\n\"\n                + \"size('test'.toCharArray())\";\n        SExpression expression = integerExpression(content);\n        Object value = groovyScriptExpressionExecutorCacheStrategy.evaluate(expression,\n                singletonMap(DEFINITION_ID, 42L), emptyMap(), null);\n        assertThat(value)\n                .isInstanceOf(Integer.class)\n                .isEqualTo(4);\n    }\n\n    @Test\n    public void evaluation_with_string_size_should_return_4()\n            throws SExpressionEvaluationException, SInvalidExpressionException {\n        String content = \"new StringBuffer('test').size()\";\n        SExpression expression = integerExpression(content);\n        Object value = groovyScriptExpressionExecutorCacheStrategy.evaluate(expression,\n                singletonMap(DEFINITION_ID, 42L), emptyMap(), null);\n        assertThat(value)\n                .isInstanceOf(Integer.class)\n                .isEqualTo(4);\n    }\n\n    private static SExpressionBuilder expressionBuilder() {\n        return new SExpressionBuilderFactoryImpl().createNewInstance().setName(\"test\");\n    }\n\n    private static SExpression integerExpression(String content) throws SInvalidExpressionException {\n        return expressionBuilder().setContent(content).setReturnType(Integer.class.getName()).done();\n    }\n\n    @Test\n    public void evaluation_with_jsonBuilder_toString_should_work_on_java_8_and_java_11()\n            throws SExpressionEvaluationException, SInvalidExpressionException {\n        String content = \"import groovy.json.JsonBuilder\\n\"\n                + \"new JsonBuilder('hello').toString()\";\n        SExpression expression = expressionBuilder().setContent(content).setReturnType(String.class.getName()).done();\n        Object value = groovyScriptExpressionExecutorCacheStrategy.evaluate(expression,\n                singletonMap(DEFINITION_ID, 42L), emptyMap(), null);\n        assertThat(value)\n                .isInstanceOf(String.class)\n                .isEqualTo(\"\\\"hello\\\"\");\n    }\n\n    @Test\n    public void evaluation_should_coerce_to_string_return_type()\n            throws SExpressionEvaluationException, SInvalidExpressionException {\n        String content = \"1\";\n\n        SExpression expression = expressionBuilder().setContent(content).setReturnType(String.class.getName()).done();\n        Object stringValue = groovyScriptExpressionExecutorCacheStrategy.evaluate(expression,\n                singletonMap(DEFINITION_ID, 42L), emptyMap(),\n                null);\n        assertThat(stringValue)\n                .isInstanceOf(String.class)\n                .isEqualTo(\"1\");\n    }\n\n    @Test\n    public void evaluation_should_coerce_groovy_string_to_string_return_type()\n            throws SExpressionEvaluationException, SInvalidExpressionException {\n        String content = \"\\\"\\\"\\\"Hello ${firstName}\\\"\\\"\\\"\";\n\n        Map<String, Object> context = new HashMap<>();\n        context.put(DEFINITION_ID, 42L);\n        context.put(\"firstName\", \"Romain\");\n        SExpression expression = expressionBuilder().setContent(content).setReturnType(String.class.getName()).done();\n        Object stringValue = groovyScriptExpressionExecutorCacheStrategy.evaluate(expression, context, emptyMap(),\n                null);\n        assertThat(stringValue)\n                .isInstanceOf(String.class)\n                .isEqualTo(\"Hello Romain\");\n    }\n\n    @Test\n    public void evaluation_should_coerce_to_integer_return_type()\n            throws SExpressionEvaluationException, SInvalidExpressionException {\n        String content = \"1\";\n\n        SExpression expression = expressionBuilder().setContent(content).setReturnType(Integer.class.getName()).done();\n        Object intergerValue = groovyScriptExpressionExecutorCacheStrategy.evaluate(expression,\n                singletonMap(DEFINITION_ID, 42L), emptyMap(),\n                null);\n        assertThat(intergerValue)\n                .isInstanceOf(Integer.class)\n                .isEqualTo(1);\n    }\n\n    @Test\n    public void evaluation_should_coerce_to_double_return_type()\n            throws SExpressionEvaluationException, SInvalidExpressionException {\n        String content = \"1\";\n\n        SExpression expression = expressionBuilder().setContent(content).setReturnType(Double.class.getName()).done();\n        Object doubleValue = groovyScriptExpressionExecutorCacheStrategy.evaluate(expression,\n                singletonMap(DEFINITION_ID, 42L), emptyMap(),\n                null);\n        assertThat(doubleValue)\n                .isInstanceOf(Double.class)\n                .isEqualTo(1d);\n    }\n\n    @Test\n    public void evaluation_should_coerce_thruthy_expression_to_boolean_return_type()\n            throws SExpressionEvaluationException, SInvalidExpressionException {\n        String thruthyContent = \"1\"; // 1 is truthy\n\n        SExpression expression = expressionBuilder().setContent(thruthyContent).setReturnType(Boolean.class.getName())\n                .done();\n        Object value = groovyScriptExpressionExecutorCacheStrategy.evaluate(expression,\n                singletonMap(DEFINITION_ID, 42L), emptyMap(), null);\n        assertThat(value)\n                .isInstanceOf(Boolean.class)\n                .isEqualTo(true);\n\n    }\n\n    @Test\n    public void evaluation_should_coerce_falsy_expression_to_boolean_return_type()\n            throws SExpressionEvaluationException, SInvalidExpressionException {\n        String falsyContent = \"0\"; // 0 is falsy\n\n        SExpression expression = expressionBuilder().setContent(falsyContent).setReturnType(Boolean.class.getName())\n                .done();\n        Object value = groovyScriptExpressionExecutorCacheStrategy.evaluate(expression,\n                singletonMap(DEFINITION_ID, 42L), emptyMap(), null);\n        assertThat(value)\n                .isInstanceOf(Boolean.class)\n                .isEqualTo(false);\n\n    }\n\n    @Test\n    public void evaluation_should_coerce_list_expression_to_array_return_type()\n            throws SExpressionEvaluationException, SInvalidExpressionException {\n        String falsyContent = \"[1,2,3]\";\n\n        SExpression expression = expressionBuilder().setContent(falsyContent).setReturnType(String[].class.getName())\n                .done();\n        Object value = groovyScriptExpressionExecutorCacheStrategy.evaluate(expression,\n                singletonMap(DEFINITION_ID, 42L), emptyMap(), null);\n        assertThat(value)\n                .isInstanceOf(String[].class)\n                .isEqualTo(new String[] { \"1\", \"2\", \"3\" });\n\n    }\n\n    @Test\n    public void evaluation_should_not_coerce_FileInputValue_to_Document_return_type()\n            throws SExpressionEvaluationException, SInvalidExpressionException {\n        String content = \"document\";\n\n        SExpression expression = expressionBuilder().setContent(content).setReturnType(Document.class.getName())\n                .done();\n        Map<String, Object> context = new HashMap<>();\n        FileInputValue fileInputValue = new FileInputValue(\"someDoc\", null);\n        context.put(\"document\", fileInputValue);\n        context.put(DEFINITION_ID, 42L);\n        Object value = groovyScriptExpressionExecutorCacheStrategy.evaluate(expression, context, emptyMap(), null);\n        assertThat(value)\n                .isInstanceOf(FileInputValue.class)\n                .isEqualTo(fileInputValue);\n    }\n\n    @Test\n    public void evaluation_should_not_coerce_FileInputValue_to_DocumentValue_return_type()\n            throws SExpressionEvaluationException, SInvalidExpressionException {\n        String content = \"documentValue\";\n\n        SExpression expression = expressionBuilder().setContent(content).setReturnType(DocumentValue.class.getName())\n                .done();\n        Map<String, Object> context = new HashMap<>();\n        FileInputValue fileInputValue = new FileInputValue(\"someDoc\", null);\n        context.put(\"documentValue\", fileInputValue);\n        context.put(DEFINITION_ID, 42L);\n        Object value = groovyScriptExpressionExecutorCacheStrategy.evaluate(expression, context, emptyMap(), null);\n        assertThat(value)\n                .isInstanceOf(FileInputValue.class)\n                .isEqualTo(fileInputValue);\n    }\n\n    @Test\n    public void evaluation_should_not_coerce_null_result()\n            throws SExpressionEvaluationException, SInvalidExpressionException {\n        String content = \"null\";\n\n        SExpression expression = expressionBuilder().setContent(content).setReturnType(String.class.getName())\n                .done();\n        Object value = groovyScriptExpressionExecutorCacheStrategy.evaluate(expression,\n                singletonMap(DEFINITION_ID, 42L), emptyMap(), null);\n        assertThat(value).isNull();\n    }\n\n    @Test\n    public void evaluation_should_throw_a_GroovyCastException_when_coercing_incompatible_types()\n            throws SExpressionEvaluationException, SInvalidExpressionException {\n        String notAList = \"[1]\"; // cannot be casted into Long\n\n        SExpression expression = expressionBuilder().setContent(notAList).setReturnType(Long.class.getName()).done();\n\n        expectedException.expectCause(is(instanceOf(GroovyCastException.class)));\n        groovyScriptExpressionExecutorCacheStrategy.evaluate(expression, singletonMap(DEFINITION_ID, 42L), emptyMap(),\n                null);\n    }\n\n    @Test\n    public void evaluation_should_coerce_string_into_long()\n            throws SExpressionEvaluationException, SInvalidExpressionException {\n        String content = \"'123'\";\n\n        SExpression expression = expressionBuilder().setContent(content).setReturnType(Long.class.getName()).done();\n\n        Object value = groovyScriptExpressionExecutorCacheStrategy.evaluate(expression,\n                singletonMap(DEFINITION_ID, 42L), emptyMap(), null);\n        assertThat(value).isEqualTo(123L);\n    }\n\n    @Test\n    public void evaluation_should_coerce_double_into_long()\n            throws SExpressionEvaluationException, SInvalidExpressionException {\n        String content = \"123d\";\n\n        SExpression expression = expressionBuilder().setContent(content).setReturnType(Long.class.getName()).done();\n\n        Object value = groovyScriptExpressionExecutorCacheStrategy.evaluate(expression,\n                singletonMap(DEFINITION_ID, 42L), emptyMap(), null);\n        assertThat(value).isEqualTo(123L);\n    }\n\n    @Test\n    public void evaluation_should_throw_a_NumberFormatException_when_string_cannot_be_formatted()\n            throws SExpressionEvaluationException, SInvalidExpressionException {\n        String notAList = \"'123a'\"; // cannot be coerce into Long\n\n        SExpression expression = expressionBuilder().setContent(notAList).setReturnType(Long.class.getName()).done();\n\n        expectedException.expectCause(is(instanceOf(NumberFormatException.class)));\n        groovyScriptExpressionExecutorCacheStrategy.evaluate(expression, singletonMap(DEFINITION_ID, 42L), emptyMap(),\n                null);\n    }\n}\n"
  },
  {
    "path": "services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/JavaMethodCallExpressionExecutorStrategyTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl;\n\nimport static org.junit.Assert.assertEquals;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.expression.ContainerState;\nimport org.bonitasoft.engine.expression.ExpressionExecutorStrategy;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.expression.model.impl.SExpressionImpl;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class JavaMethodCallExpressionExecutorStrategyTest {\n\n    private final JavaMethodCallExpressionExecutorStrategy methodCall = new JavaMethodCallExpressionExecutorStrategy();\n\n    private Order order;\n\n    private SExpression orderDep;\n\n    private Map<Integer, Object> orderResolvedExp;\n\n    private List<Integer> list;\n\n    private SExpression listDep;\n\n    private Map<Integer, Object> listResolvedExp;\n\n    @Before\n    public void setUp() {\n        list = new ArrayList<Integer>(2);\n        list.add(1);\n        list.add(2);\n        listDep = new SExpressionImpl(\"dep\", \"myValues\", ExpressionExecutorStrategy.TYPE_VARIABLE, List.class.getName(),\n                null, null);\n        listResolvedExp = new HashMap<Integer, Object>(1);\n        listResolvedExp.put(listDep.getDiscriminant(), list);\n\n        order = new Order(\"32, rue Gustave Eiffel - 38500 - Grenoble\", 123L);\n        orderDep = new SExpressionImpl(\"dep\", \"order\", ExpressionExecutorStrategy.TYPE_VARIABLE, Order.class.getName(),\n                null, null);\n        orderResolvedExp = new HashMap<Integer, Object>(1);\n        orderResolvedExp.put(orderDep.getDiscriminant(), order);\n    }\n\n    @Test\n    public void testJavaMethodCallOfSuperClass() throws Exception {\n        final SExpression expression = new SExpressionImpl(\"exp1\", \"toString\",\n                ExpressionExecutorStrategy.TYPE_JAVA_METHOD_CALL, String.class.getName(), null,\n                Collections.singletonList(listDep));\n\n        final Object result = methodCall.evaluate(expression, Collections.<String, Object> emptyMap(), listResolvedExp,\n                ContainerState.ACTIVE);\n        assertEquals(list.toString(), result);\n    }\n\n    @Test\n    public void testCallMethodOnClientObjectReturningString() throws Exception {\n        final SExpression expression = new SExpressionImpl(\"exp1\", \"getShippingAddress\",\n                ExpressionExecutorStrategy.TYPE_JAVA_METHOD_CALL,\n                String.class.getName(), null, Collections.singletonList(orderDep));\n\n        final Object result = methodCall.evaluate(expression, Collections.<String, Object> emptyMap(), orderResolvedExp,\n                ContainerState.ACTIVE);\n        assertEquals(order.getShippingAddress(), result);\n    }\n\n    @Test\n    public void testCallMethodOnClientObjectReturningLong() throws Exception {\n        final SExpression expression = new SExpressionImpl(\"exp1\", \"getReferenceNumber\",\n                ExpressionExecutorStrategy.TYPE_JAVA_METHOD_CALL,\n                Long.class.getName(), null, Collections.singletonList(orderDep));\n\n        final Object result = methodCall.evaluate(expression, Collections.<String, Object> emptyMap(), orderResolvedExp,\n                ContainerState.ACTIVE);\n        assertEquals(order.getReferenceNumber(), result);\n    }\n\n    @Test\n    public void testEvaluateListOfExpressions() throws Exception {\n        final List<SExpression> expressions = new ArrayList<SExpression>(2);\n        expressions.add(new SExpressionImpl(\"exp1\", \"getShippingAddress\",\n                ExpressionExecutorStrategy.TYPE_JAVA_METHOD_CALL, String.class.getName(), null,\n                Collections.singletonList(orderDep)));\n        expressions.add(new SExpressionImpl(\"exp1\", \"getReferenceNumber\",\n                ExpressionExecutorStrategy.TYPE_JAVA_METHOD_CALL, Long.class.getName(), null,\n                Collections.singletonList(orderDep)));\n\n        final List<Object> result = methodCall.evaluate(expressions, Collections.<String, Object> emptyMap(),\n                orderResolvedExp, ContainerState.ACTIVE);\n        assertEquals(order.getShippingAddress(), result.get(0));\n        assertEquals(order.getReferenceNumber(), result.get(1));\n    }\n\n    @Test(expected = SInvalidExpressionException.class)\n    public void testDepencenciesCannotBeNull() throws Exception {\n        final SExpression expression = new SExpressionImpl(\"exp1\", \"getReferenceNumber\",\n                ExpressionExecutorStrategy.TYPE_JAVA_METHOD_CALL,\n                Long.class.getName(), null, null);\n\n        methodCall.validate(expression);\n    }\n\n    @Test(expected = SInvalidExpressionException.class)\n    public void testDepencenciesCannotBeEmpty() throws Exception {\n        final SExpression expression = new SExpressionImpl(\"exp1\", \"getReferenceNumber\",\n                ExpressionExecutorStrategy.TYPE_JAVA_METHOD_CALL,\n                Long.class.getName(), null, Collections.<SExpression> emptyList());\n        methodCall.validate(expression);\n    }\n\n    @Test(expected = SInvalidExpressionException.class)\n    public void testDepencenciesCannotHasMoreThanOneElement() throws Exception {\n        final List<SExpression> dependencies = new ArrayList<SExpression>(2);\n        dependencies.add(orderDep);\n        dependencies.add(listDep);\n\n        final SExpression expression = new SExpressionImpl(\"exp1\", \"getReferenceNumber\",\n                ExpressionExecutorStrategy.TYPE_JAVA_METHOD_CALL,\n                Long.class.getName(), null, dependencies);\n\n        methodCall.validate(expression);\n    }\n\n    @Test(expected = SInvalidExpressionException.class)\n    public void testExpressionCannotBeNull() throws Exception {\n        methodCall.validate(null);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/Order.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl;\n\npublic class Order {\n\n    private final String shippingAddress;\n\n    private final long referenceNumber;\n\n    public Order(final String shippingAddress, final long referenceNumber) {\n        super();\n        this.shippingAddress = shippingAddress;\n        this.referenceNumber = referenceNumber;\n    }\n\n    public String getShippingAddress() {\n        return shippingAddress;\n    }\n\n    public long getReferenceNumber() {\n        return referenceNumber;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/PatternExpressionExecutorStrategyTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl;\n\nimport static org.junit.Assert.assertArrayEquals;\nimport static org.junit.Assert.assertEquals;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.expression.ContainerState;\nimport org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.expression.model.SExpressionType;\nimport org.bonitasoft.engine.expression.model.impl.SExpressionImpl;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Baptiste Mesta\n */\npublic class PatternExpressionExecutorStrategyTest {\n\n    private PatternExpressionExecutorStrategy strategy;\n\n    @Before\n    public void setup() {\n        strategy = new PatternExpressionExecutorStrategy();\n    }\n\n    private void patternTest(final String expressionContent, final String result, final List<String> dependencyNames,\n            final List<Integer> dependencyContent)\n            throws SExpressionDependencyMissingException {\n        final SExpressionImpl expression = new SExpressionImpl(\"pattern\", expressionContent,\n                SExpressionType.TYPE_PATTERN.name(), String.class.getName(), null,\n                getIntegerExpressions(dependencyNames, dependencyContent));\n        final Map<Integer, Object> resolvedExpressions = getResolvedExpressionMap(expression);\n        final HashMap<String, Object> dependencyValues = new HashMap<String, Object>(1);\n        assertEquals(result,\n                strategy.evaluate(expression, dependencyValues, resolvedExpressions, ContainerState.ACTIVE));\n    }\n\n    @Test\n    public void patternTestWithMultipleExpressions() throws SExpressionDependencyMissingException {\n        final SExpression expression1 = new SExpressionImpl(\"pattern1\", \"${bla} ${bla} test\",\n                SExpressionType.TYPE_PATTERN.name(), String.class.getName(),\n                null, getIntegerExpressions(Arrays.asList(\"bla\"), Arrays.asList(12)));\n        final SExpression expression2 = new SExpressionImpl(\"pattern1\", \"${bla} ${bli} test\",\n                SExpressionType.TYPE_PATTERN.name(), String.class.getName(),\n                null, getIntegerExpressions(Arrays.asList(\"bla\", \"bli\"), Arrays.asList(12, 13)));\n        final Map<Integer, Object> resolvedExpressions = getResolvedExpressionMap(expression1, expression2);\n        final HashMap<String, Object> dependencyValues = new HashMap<String, Object>(1);\n        final List<Object> evaluate = strategy.evaluate(Arrays.asList(expression1, expression2), dependencyValues,\n                resolvedExpressions, ContainerState.ACTIVE);\n        assertArrayEquals(new Object[] { \"12 12 test\", \"12 13 test\" }, evaluate.toArray());\n    }\n\n    @Test(expected = SExpressionDependencyMissingException.class)\n    public void patternTestWithMissingValue() throws SExpressionDependencyMissingException {\n        final SExpression expression1 = new SExpressionImpl(\"pattern1\", \"${bla} ${bla} test\",\n                SExpressionType.TYPE_PATTERN.name(), String.class.getName(),\n                null, getIntegerExpressions(Arrays.asList(\"bla\"), Arrays.asList(12)));\n        final Map<Integer, Object> resolvedExpressions = new HashMap<Integer, Object>(1);\n        final HashMap<String, Object> dependencyValues = new HashMap<String, Object>(1);\n        strategy.evaluate(Arrays.asList(expression1), dependencyValues, resolvedExpressions, ContainerState.ACTIVE);\n    }\n\n    /**\n     * @param expression\n     * @return\n     */\n    private Map<Integer, Object> getResolvedExpressionMap(final SExpression... expressions) {\n        final HashMap<Integer, Object> hashMap = new HashMap<Integer, Object>();\n        for (final SExpression expression : expressions) {\n            final List<SExpression> dependencies = expression.getDependencies();\n            for (final SExpression sExpression : dependencies) {\n                hashMap.put(sExpression.getDiscriminant(), Integer.valueOf(sExpression.getContent()));\n            }\n        }\n        return hashMap;\n    }\n\n    /**\n     * @param dependencyNames\n     * @param dependencyContent\n     * @return\n     */\n    private List<SExpression> getIntegerExpressions(final List<String> dependencyNames,\n            final List<Integer> dependencyContent) {\n        final ArrayList<SExpression> arrayList = new ArrayList<SExpression>(dependencyNames.size());\n        final Iterator<Integer> contentIterator = dependencyContent.iterator();\n        for (final Iterator<String> nameIterator = dependencyNames.iterator(); nameIterator.hasNext();) {\n            arrayList.add(getIntegerExpression(nameIterator.next(), contentIterator.next()));\n        }\n        return arrayList;\n    }\n\n    private SExpression getIntegerExpression(final String name, final Integer content) {\n        return new SExpressionImpl(name, String.valueOf(content), SExpression.TYPE_CONSTANT, Integer.class.getName(),\n                null, null);\n    }\n\n    @Test\n    public void testPatternWithEscape() throws Exception {\n        patternTest(\"ahah bla ${bla}\", \"ahah bla 12\", Arrays.asList(\"bla\"), Arrays.asList(12));\n    }\n\n    @Test\n    public void testPatternWithNotInDependencies() throws Exception {\n        patternTest(\"ahah bla bla\", \"ahah bla bla\", Arrays.asList(\"blo\"), Arrays.asList(12));\n    }\n\n    @Test\n    public void testPatternWithSpaces() throws Exception {\n        patternTest(\"${bla} ${blablabla}  ${bla} ${bla}a bla${bla} bla\", \"12 ${blablabla}  12 12a bla12 bla\",\n                Arrays.asList(\"bla\"), Arrays.asList(12));\n    }\n\n    @Test\n    public void testPatternWithSpacesAndLineBreak() throws Exception {\n        patternTest(\"${bla} ${bla}${bla}${bla}  ${bla} ${bla}a ${bla}\\n${bla} ${bla}\", \"12 121212  12 12a 12\\n12 12\",\n                Arrays.asList(\"bla\"), Arrays.asList(12));\n    }\n\n    @Test\n    public void testPatternMultipleReplacements() throws Exception {\n        patternTest(\"${bla} ${blo} ${blu}${bla}${bla}\", \"1 2 311\", Arrays.asList(\"bla\", \"blo\", \"blu\"),\n                Arrays.asList(1, 2, 3));\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/ReturnTypeCheckerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.catchThrowable;\n\nimport java.nio.charset.StandardCharsets;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.bpm.contract.FileInputValue;\nimport org.bonitasoft.engine.commons.exceptions.SExceptionContext;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.expression.model.impl.SExpressionImpl;\nimport org.junit.Test;\n\npublic class ReturnTypeCheckerTest {\n\n    @Test\n    public void checkReturnType_should_skip_processing_if_result_is_null() throws SExpressionEvaluationException {\n        new ReturnTypeChecker().checkReturnType(null, null, null);\n    }\n\n    @Test\n    public void checkReturnType_should_detect_result_and_expression_return_type_incompatibility() {\n        // given:\n        final SExpression expression = expression(\"expressionName\", \"java.util.List\");\n\n        // when:\n        Throwable thrown = catchThrowable(\n                () -> new ReturnTypeChecker().checkReturnType(expression, \"a String expression result\",\n                        Collections.emptyMap()));\n\n        //then:\n        assertThat(thrown).isInstanceOf(SExpressionEvaluationException.class)\n                .hasMessageContaining(\"expressionName\")\n                .hasMessageContaining(\"java.util.List\")\n                .hasMessageContaining(\"java.lang.String\");\n    }\n\n    @Test\n    public void checkReturnType_should_allow_result_and_expression_return_type_that_are_convertible() throws Exception {\n        final SExpression expression = expression(\"expressionName\", \"org.bonitasoft.engine.bpm.document.Document\");\n        final FileInputValue result = new FileInputValue(\"name.txt\", \"a content\".getBytes(StandardCharsets.UTF_8));\n\n        new ReturnTypeChecker().checkReturnType(expression, result, Collections.emptyMap());\n    }\n\n    @Test\n    public void checkReturnType_should_fill_exception_context_with_activity_instance_id_if_any() {\n        // given:\n        final SExpression expression = expression(\"expressionName\", \"InvalidClassName_causing_ClassNotFound\");\n        final Map<String, Object> context = new HashMap<>();\n        final long activityInstanceId = 1499999L;\n        context.put(\"containerType\", \"ACTIVITY_INSTANCE\");\n        context.put(\"containerId\", activityInstanceId);\n\n        // when:\n        Throwable thrown = catchThrowable(\n                () -> new ReturnTypeChecker().checkReturnType(expression, \"some evaluated expression result\", context));\n\n        // then:\n        assertThat(thrown).isInstanceOf(SExpressionEvaluationException.class)\n                .hasMessageContaining(\"expressionName\")\n                .hasMessageContaining(\"InvalidClassName_causing_ClassNotFound\");\n        assertThat(((SExpressionEvaluationException) thrown).getContext().get(SExceptionContext.FLOW_NODE_INSTANCE_ID))\n                .isEqualTo(activityInstanceId);\n    }\n\n    @Test\n    public void checkReturnType_should_fill_exception_context_with_process_instance_id_if_any() {\n        // given:\n        final SExpression expression = expression(\"expressionName\", \"InvalidClassName_causing_ClassNotFound\");\n        final Map<String, Object> context = new HashMap<>();\n        final long processInstanceId = 8779L;\n        context.put(\"containerType\", \"PROCESS_INSTANCE\");\n        context.put(\"containerId\", processInstanceId);\n\n        // when:\n        Throwable thrown = catchThrowable(\n                () -> new ReturnTypeChecker().checkReturnType(expression, \"some evaluated expression result\", context));\n\n        // then:\n        assertThat(thrown).isInstanceOf(SExpressionEvaluationException.class)\n                .hasMessageContaining(\"expressionName\")\n                .hasMessageContaining(\"InvalidClassName_causing_ClassNotFound\");\n        assertThat(((SExpressionEvaluationException) thrown).getContext().get(SExceptionContext.PROCESS_INSTANCE_ID))\n                .isEqualTo(processInstanceId);\n    }\n\n    // =================================================================================================================\n    // UTILS\n    // =================================================================================================================\n\n    private static SExpression expression(String name, String returnType) {\n        return new SExpressionImpl(name, \"not_used_here\", null, returnType, null, null);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/XPathReadExpressionExecutorStrategyTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertTrue;\n\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.commons.io.IOUtil;\nimport org.bonitasoft.engine.expression.ContainerState;\nimport org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException;\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.exception.SExpressionException;\nimport org.bonitasoft.engine.expression.exception.SInvalidExpressionException;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.expression.model.SExpressionType;\nimport org.bonitasoft.engine.expression.model.impl.SExpressionImpl;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.w3c.dom.Node;\nimport org.w3c.dom.NodeList;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class XPathReadExpressionExecutorStrategyTest {\n\n    private XPathReadExpressionExecutorStrategy strategy;\n\n    private final String XML_CONTENT_BOOKS;\n\n    private final String XML_CONTENT_AUTHOR;\n\n    /**\n     * @throws IOException\n     */\n    public XPathReadExpressionExecutorStrategyTest() throws IOException {\n        XML_CONTENT_AUTHOR = new String(IOUtil.getAllContentFrom(this.getClass().getResourceAsStream(\"/authors.xml\")));\n        XML_CONTENT_BOOKS = new String(IOUtil.getAllContentFrom(this.getClass().getResourceAsStream(\"/books.xml\")));\n\n    }\n\n    @Before\n    public void setup() {\n        strategy = new XPathReadExpressionExecutorStrategy();\n    }\n\n    private String getXPathType() {\n        return SExpressionType.TYPE_XPATH_READ.name();\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.expression.impl.XPathReadExpressionExecutorStrategy#evaluate(org.bonitasoft.engine.expression.model.SExpression, java.util.Map, java.util.Map)}\n     *\n     * @throws SExpressionException\n     */\n    @Test\n    public void evaluateXpathReturnsStringAttr() throws SExpressionException {\n        final String obj = evaluate(XML_CONTENT_AUTHOR, String.class, \"//article/@nom\");\n        assertEquals(\"XPath\", obj);\n    }\n\n    @Test\n    public void evaluateXPathExpressionWithDataAsContent() throws SExpressionException {\n        final String dataName = \"myVariable\";\n        final SExpressionImpl dep = new SExpressionImpl(dataName, dataName, SExpressionType.TYPE_VARIABLE.name(),\n                String.class.getName(), null, null);\n        final String xPathSelector = \"//article/@nom\";\n        final SExpression expression = new SExpressionImpl(\"expName1\", xPathSelector, getXPathType(),\n                String.class.getName(), null,\n                Arrays.<SExpression> asList(dep));\n\n        final Map<Integer, Object> resolvedExpressions = new HashMap<Integer, Object>(1);\n        resolvedExpressions.put(dep.getDiscriminant(), XML_CONTENT_AUTHOR);\n        final HashMap<String, Object> dependencyValues = new HashMap<String, Object>(1);\n        final Object result = strategy.evaluate(expression, dependencyValues, resolvedExpressions,\n                ContainerState.ACTIVE);\n        assertEquals(\"XPath\", result);\n    }\n\n    @Test\n    public void evaluateXpathReturnsStringNode() throws SExpressionException {\n        final String obj = evaluate(XML_CONTENT_BOOKS, String.class, \"//book[@id='bk101']/title\");\n        assertEquals(\"XML Developer's Guide\", obj);\n    }\n\n    @Test(expected = SExpressionEvaluationException.class)\n    public void evaluateXpathInvalidReturnType() throws SExpressionException {\n        final SExpressionImpl dep = new SExpressionImpl(null, XML_CONTENT_AUTHOR, SExpressionType.TYPE_CONSTANT.name(),\n                String.class.getName(), null, null);\n        final SExpression expression = new SExpressionImpl(\"expName1\", \"//article\", getXPathType(), \"InvalidReturnType\",\n                null, Arrays.<SExpression> asList(dep));\n        final Map<Integer, Object> resolvedExpressions = getResolvedExpressionMap(expression);\n        final HashMap<String, Object> dependencyValues = new HashMap<String, Object>(1);\n        strategy.evaluate(expression, dependencyValues, resolvedExpressions, ContainerState.ACTIVE);\n    }\n\n    @Test(expected = SExpressionEvaluationException.class)\n    public void evaluateXpathWrongReturnType() throws SExpressionException {\n        evaluate(XML_CONTENT_BOOKS, Integer.class, \"//book[@id='bk101']/title\");\n    }\n\n    @Test(expected = SExpressionEvaluationException.class)\n    public void evaluateEnptyContent() throws Exception {\n        try {\n            evaluate(\"\", Integer.class, \"//book[@id='bk101']/title\");\n        } catch (final Exception e) {\n            throw e;\n        }\n    }\n\n    @Test\n    public void evaluateXpathReturnsBoolean() throws SExpressionException {\n        final Boolean ownedText = evaluate(XML_CONTENT_BOOKS, Boolean.class, \"//catalog/book[@id='bk101']/owned\");\n        assertTrue(ownedText);\n        final Boolean ownedNumeric = evaluate(XML_CONTENT_BOOKS, Boolean.class, \"//catalog/book[@id='bk102']/owned\");\n        assertTrue(ownedNumeric);\n        final Boolean notOwnedText = evaluate(XML_CONTENT_BOOKS, Boolean.class, \"//catalog/book[@id='bk103']/owned\");\n        assertFalse(notOwnedText);\n        final Boolean notOwnedNumeric = evaluate(XML_CONTENT_BOOKS, Boolean.class, \"//catalog/book[@id='bk104']/owned\");\n        assertFalse(notOwnedNumeric);\n    }\n\n    @Test\n    public void evaluateXpathReturnsLong() throws SExpressionException {\n        final Long quantity = evaluate(XML_CONTENT_BOOKS, Long.class, \"//catalog/book[@id='bk101']/quantity\");\n        assertEquals(123456789l, quantity.longValue());\n    }\n\n    @Test\n    public void evaluateXpathReturnsInteger() throws SExpressionException {\n        final Integer quantity = evaluate(XML_CONTENT_BOOKS, Integer.class, \"//catalog/book[@id='bk101']/quantity\");\n        assertEquals(123456789, quantity.intValue());\n    }\n\n    @Test\n    public void evaluateXpathReturnsFloat() throws SExpressionException {\n        final Float quantity = evaluate(XML_CONTENT_BOOKS, Float.class, \"//catalog/book[@id='bk101']/price\");\n        assertEquals(Float.valueOf(44.95f), quantity);\n    }\n\n    @Test\n    public void evaluateXpathReturnsDouble() throws SExpressionException {\n        final Double authorCount = evaluate(XML_CONTENT_BOOKS, Double.class, \"//catalog/book[@id='bk101']/price\");\n        assertEquals(Double.valueOf(44.95d), authorCount);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private <T> T evaluate(final String content, final Class<T> returnType, final String selector)\n            throws SExpressionEvaluationException,\n            SExpressionDependencyMissingException {\n        final SExpressionImpl dep = new SExpressionImpl(null, content, SExpressionType.TYPE_CONSTANT.name(),\n                String.class.getName(), null, null);\n        final SExpression expression = new SExpressionImpl(\"expName1\", selector, getXPathType(), returnType.getName(),\n                null, Arrays.<SExpression> asList(dep));\n        final Map<Integer, Object> resolvedExpressions = getResolvedExpressionMap(expression);\n        final HashMap<String, Object> dependencyValues = new HashMap<String, Object>(1);\n        final Object result = strategy.evaluate(expression, dependencyValues, resolvedExpressions,\n                ContainerState.ACTIVE);\n        return (T) result;\n    }\n\n    @Test\n    public void evaluateXpathReturnsNode() throws SExpressionException {\n        final Node obj = evaluate(XML_CONTENT_AUTHOR, Node.class, \"//article\");\n        assertEquals(\"article\", obj.getNodeName());\n    }\n\n    @Test\n    public void evaluateXpathReturnsNodeList() throws SExpressionException {\n        final NodeList nodeList = evaluate(XML_CONTENT_AUTHOR, NodeList.class, \"//article/auteurs/auteur/nom\");\n        assertEquals(2, nodeList.getLength());\n        assertEquals(\"Dupont\", nodeList.item(0).getTextContent());\n        assertEquals(\"Dubois\", nodeList.item(1).getTextContent());\n    }\n\n    @Test(expected = SExpressionDependencyMissingException.class)\n    public void evaluateInvalidDependencies() throws SExpressionException {\n        final SExpression expression = new SExpressionImpl(\"expName1\", \"//article/@nom\", getXPathType(),\n                String.class.getName(), null, null);\n        final Map<Integer, Object> resolvedExpressions = new HashMap<Integer, Object>(0);\n        final HashMap<String, Object> dependencyValues = new HashMap<String, Object>(0);\n        strategy.evaluate(expression, dependencyValues, resolvedExpressions, ContainerState.ACTIVE);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.expression.impl.XPathReadExpressionExecutorStrategy#validate(SExpression)}.\n     */\n    @Test(expected = SInvalidExpressionException.class)\n    public void validateNullExpressionContent() throws Exception {\n        final SExpressionImpl dep = new SExpressionImpl(null, XML_CONTENT_AUTHOR, SExpressionType.TYPE_CONSTANT.name(),\n                String.class.getName(), null, null);\n        final SExpression expression = new SExpressionImpl(\"expName1\", null, getXPathType(), Double.class.getName(),\n                null, Arrays.<SExpression> asList(dep));\n        strategy.validate(expression);\n    }\n\n    private Map<Integer, Object> getResolvedExpressionMap(final SExpression... expressions) {\n        final HashMap<Integer, Object> hashMap = new HashMap<Integer, Object>();\n        for (final SExpression expression : expressions) {\n            final List<SExpression> dependencies = expression.getDependencies();\n            for (final SExpression dependency : dependencies) {\n                hashMap.put(dependency.getDiscriminant(), dependency.getContent());\n            }\n        }\n        return hashMap;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/condition/BinaryComparatorExecutorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl.condition;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.fail;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\nimport java.math.BigDecimal;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.expression.model.impl.SExpressionImpl;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class BinaryComparatorExecutorTest {\n\n    @Mock\n    BinaryComparatorMapper mapper;\n\n    @Mock\n    BinaryComparator evaluator;\n\n    @InjectMocks\n    private BinaryComparatorExecutor manager;\n\n    @Test\n    public void evaluate_should_return_the_result_of_selected_evaluator() throws Exception {\n        //given\n        SExpression left = buildIntegerExpression(5);\n        SExpression right = buildIntegerExpression(4);\n        Map<Integer, Object> resolvedExpressions = new HashMap<Integer, Object>();\n        resolvedExpressions.put(left.getDiscriminant(), 5);\n        resolvedExpressions.put(right.getDiscriminant(), 4);\n\n        SExpression comparisonExpression = buildComparisonExpression(left, right, \">\");\n        given(mapper.getEvaluator(\">\")).willReturn(evaluator);\n        given(evaluator.evaluate(new BigDecimal(5), new BigDecimal(4))).willReturn(true);\n\n        //when\n        Boolean value = manager.evaluate(resolvedExpressions, comparisonExpression);\n\n        //then\n        assertThat(value).isTrue();\n    }\n\n    @Test\n    public void evaluate_should_throw_SExpressionEvaluationException_when_no_evaluator_is_found() throws Exception {\n        //given\n        SExpression left = buildIntegerExpression(5);\n        SExpression right = buildIntegerExpression(4);\n        String operator = \">\";\n        SExpression comparisonExpression = buildComparisonExpression(left, right, operator);\n        given(mapper.getEvaluator(operator)).willReturn(null);\n\n        try {\n            //when\n            manager.evaluate(Collections.<Integer, Object> emptyMap(), comparisonExpression);\n            fail(\"exception expected\");\n        } catch (SExpressionEvaluationException e) {\n            //then\n            assertThat(e.getMessage()).isEqualTo(\"Unable to find evaluator for operator '\" + operator + \"'\");\n        }\n\n    }\n\n    @Test\n    public void evaluate_should_throw_SExpressionEvaluationException_when_number_there_are_not_exactly_2_dependencies()\n            throws Exception {\n        //given\n        SExpression expression = mock(SExpression.class);\n        String operator = \">\";\n        given(expression.getContent()).willReturn(operator);\n        given(expression.getName()).willReturn(operator);\n        given(expression.getDependencies())\n                .willReturn(Arrays.asList(mock(SExpression.class), mock(SExpression.class), mock(SExpression.class)));\n\n        try {\n            //when\n            manager.evaluate(Collections.<Integer, Object> emptyMap(), expression);\n            fail(\"exception expected\");\n        } catch (SExpressionEvaluationException e) {\n            //then\n            assertThat(e.getMessage()).isEqualTo(\n                    \"The expression '\" + expression.getContent() + \"' has \" + expression.getDependencies().size()\n                            + \" dependencies, but it must have exactly \" + 2 + \" dependencies.\");\n        }\n\n    }\n\n    @Test\n    public void evaluate_should_throw_SExpressionEvaluationException_when_return_types_are_incompatible()\n            throws Exception {\n        //given\n\n        SExpression left = buildIntegerExpression(5);\n        SExpression right = buildBooleanExpression(true);\n        String operator = \">\";\n        SExpression comparisonExpression = buildComparisonExpression(left, right, operator);\n\n        try {\n            //when\n            manager.evaluate(Collections.<Integer, Object> emptyMap(), comparisonExpression);\n            fail(\"exception expected\");\n        } catch (SExpressionEvaluationException e) {\n            //then\n            assertThat(e.getMessage())\n                    .isEqualTo(\"The two dependencies of expression '\" + operator + \"' must have the same return type.\");\n        }\n\n    }\n\n    @Test\n    public void evaluate_should_throw_SExpressionEvaluationException_when_evaluator_throws_SComparisonException()\n            throws Exception {\n        //given\n        String operator = \">\";\n        given(mapper.getEvaluator(operator)).willReturn(evaluator);\n        given(evaluator.evaluate(new BigDecimal(5), new BigDecimal(5))).willThrow(new SComparisonException(\"\"));\n\n        SExpression left = buildIntegerExpression(5);\n        SExpression right = buildIntegerExpression(5);\n\n        Map<Integer, Object> resolvedExpressions = new HashMap<Integer, Object>();\n        resolvedExpressions.put(left.getDiscriminant(), 5);\n        resolvedExpressions.put(right.getDiscriminant(), 5);\n        SExpression comparisonExpression = buildComparisonExpression(left, right, operator);\n\n        try {\n            //when\n            manager.evaluate(resolvedExpressions, comparisonExpression);\n            fail(\"exception expected\");\n        } catch (SExpressionEvaluationException e) {\n            //then\n            assertThat(e.getMessage())\n                    .isEqualTo(\"Unable to evaluate expression '\" + comparisonExpression.getName() + \"'\");\n        }\n\n    }\n\n    @Test\n    public void areCompatible_should_return_true_when_two_parameters_has_the_same_return_type() throws Exception {\n        //given\n\n        //when\n        boolean value = manager.areReturnTypeCompatible(String.class.getName(), String.class.getName(), \">\");\n\n        //then\n        assertThat(value).isTrue();\n    }\n\n    @Test\n    public void areCompatible_should_return_false_when_two_parameters_has_different_return_types() throws Exception {\n        //given\n\n        //when\n        boolean value = manager.areReturnTypeCompatible(String.class.getName(), Integer.class.getName(), \">\");\n\n        //then\n        assertThat(value).isFalse();\n    }\n\n    @Test\n    public void areCompatible_should_return_true_when_two_parameters_has_different_return_types_but_operator_is_equals_to()\n            throws Exception {\n        //given\n\n        //when\n        boolean value = manager.areReturnTypeCompatible(String.class.getName(), Integer.class.getName(), \"==\");\n\n        //then\n        assertThat(value).isTrue();\n    }\n\n    @Test\n    public void areCompatible_should_return_true_when_two_parameters_has_different_return_types_but_both_are_numeric()\n            throws Exception {\n        //given\n\n        //when\n        boolean value = manager.areReturnTypeCompatible(Long.class.getName(), Integer.class.getName(), \">\");\n\n        //then\n        assertThat(value).isTrue();\n    }\n\n    @Test\n    public void transtype_integer_should_return_bigDecimal() throws Exception {\n        //given\n\n        //when\n        Object value = manager.transtypeIfApplicable(Integer.class.getName(), 5);\n\n        //then\n        assertThat(value).isInstanceOf(BigDecimal.class);\n        assertThat(value).isEqualTo(new BigDecimal(5));\n    }\n\n    @Test\n    public void transtype_long_should_return_bigDecimal() throws Exception {\n        //given\n\n        //when\n        Object value = manager.transtypeIfApplicable(Long.class.getName(), 5L);\n\n        //then\n        assertThat(value).isInstanceOf(BigDecimal.class);\n        assertThat(value).isEqualTo(new BigDecimal(5));\n    }\n\n    @Test\n    public void transtype_float_should_return_bigDecimal() throws Exception {\n        //given\n\n        //when\n        Object value = manager.transtypeIfApplicable(Float.class.getName(), 5.2f);\n\n        //then\n        assertThat(value).isInstanceOf(BigDecimal.class);\n        assertThat(value).isEqualTo(new BigDecimal(\"5.2\"));\n    }\n\n    @Test\n    public void transtype_double_should_return_bigDecimal() throws Exception {\n        //given\n\n        //when\n        Object value = manager.transtypeIfApplicable(Double.class.getName(), 5.2d);\n\n        //then\n        assertThat(value).isInstanceOf(BigDecimal.class);\n        assertThat(value).isEqualTo(new BigDecimal(\"5.2\"));\n    }\n\n    @Test\n    public void transtype_null_should_return_null() throws Exception {\n        //given\n\n        //when\n        Object value = manager.transtypeIfApplicable(Integer.class.getName(), null);\n\n        //then\n        assertThat(value).isNull();\n    }\n\n    @Test\n    public void transtype_non_numeric_should_return_same_object() throws Exception {\n        //given\n\n        //when\n        Object value = manager.transtypeIfApplicable(String.class.getName(), \"just a string\");\n\n        //then\n        assertThat(value).isInstanceOf(String.class);\n        assertThat(value).isEqualTo(\"just a string\");\n    }\n\n    private SExpression buildIntegerExpression(int value) {\n        final SExpressionImpl expression = new SExpressionImpl();\n        String strValue = String.valueOf(value);\n        expression.setName(strValue);\n        expression.setContent(strValue);\n        expression.setExpressionType(SExpression.TYPE_CONSTANT);\n        expression.setReturnType(Integer.class.getName());\n        expression.setDependencies(Collections.<SExpression> emptyList());\n        return expression;\n    }\n\n    private SExpression buildComparisonExpression(SExpression leftExpression, SExpression rightExpression,\n            String operator) {\n        final SExpressionImpl expression = new SExpressionImpl();\n        expression.setName(\"compare\");\n        expression.setContent(operator);\n        expression.setExpressionType(SExpression.TYPE_CONDITION);\n        expression.setReturnType(Boolean.class.getName());\n        expression.setDependencies(Arrays.asList(leftExpression, rightExpression));\n        return expression;\n    }\n\n    private SExpression buildBooleanExpression(boolean value) {\n        final SExpressionImpl expression = new SExpressionImpl();\n        String strValue = String.valueOf(value);\n        expression.setName(strValue);\n        expression.setContent(strValue);\n        expression.setExpressionType(SExpression.TYPE_CONSTANT);\n        expression.setReturnType(Boolean.class.getName());\n        expression.setDependencies(Collections.<SExpression> emptyList());\n        return expression;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/condition/BinaryComparatorMapperTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl.condition;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class BinaryComparatorMapperTest {\n\n    private BinaryComparatorMapper mapper;\n\n    @Before\n    public void setUp() throws Exception {\n        mapper = new BinaryComparatorMapper(new EqualityComparator(), new InequalityComparator());\n    }\n\n    @Test\n    public void getEvaluator_for_equals_operator_should_return_EqualsEvaluator() throws Exception {\n        //given\n\n        //when\n        BinaryComparator evaluator = mapper.getEvaluator(\"==\");\n\n        //then\n        assertThat(evaluator).isInstanceOf(EqualsComparator.class);\n    }\n\n    @Test\n    public void getEvaluator_for_Different_operator_should_return_DifferentEvaluator() throws Exception {\n        //given\n\n        //when\n        BinaryComparator evaluator = mapper.getEvaluator(\"!=\");\n\n        //then\n        assertThat(evaluator).isInstanceOf(DifferentComparator.class);\n    }\n\n    @Test\n    public void getEvaluator_for_greaterThan_operator_should_return_GreaterThanEvaluator() throws Exception {\n        //given\n\n        //when\n        BinaryComparator evaluator = mapper.getEvaluator(\">\");\n\n        //then\n        assertThat(evaluator).isInstanceOf(GreaterThanComparator.class);\n    }\n\n    @Test\n    public void getEvaluator_for_greaterThanOrEquals_operator_should_return_GreaterThanOrEqualsEvaluator()\n            throws Exception {\n        //given\n\n        //when\n        BinaryComparator evaluator = mapper.getEvaluator(\">=\");\n\n        //then\n        assertThat(evaluator).isInstanceOf(GreaterThanOrEqualsComparator.class);\n    }\n\n    @Test\n    public void getEvaluator_for_LessThan_operator_should_return_LessThanEvaluator() throws Exception {\n        //given\n\n        //when\n        BinaryComparator evaluator = mapper.getEvaluator(\"<\");\n\n        //then\n        assertThat(evaluator).isInstanceOf(LessThanComparator.class);\n    }\n\n    @Test\n    public void getEvaluator_for_LessThanOrEquals_operator_should_return_LessThanOrEqualsEvaluator() throws Exception {\n        //given\n\n        //when\n        BinaryComparator evaluator = mapper.getEvaluator(\"<=\");\n\n        //then\n        assertThat(evaluator).isInstanceOf(LessThanOrEqualsComparator.class);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/condition/BinaryComparatorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl.condition;\n\nimport org.junit.Test;\n\npublic class BinaryComparatorTest {\n\n    @Test\n    public void evaluate() throws Exception {\n        //given\n\n        //when\n\n        //then\n    }\n}\n"
  },
  {
    "path": "services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/condition/DifferentComparatorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl.condition;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class DifferentComparatorTest {\n\n    @Mock\n    private EqualityComparator comparator;\n\n    @InjectMocks\n    private DifferentComparator evaluator;\n\n    @Test\n    public void evaluate_should_return_true_when_comparator_returns_false() throws Exception {\n        //given\n        given(comparator.areEquals(\"a\", \"b\")).willReturn(false);\n\n        //when\n        Boolean value = evaluator.evaluate(\"a\", \"b\");\n\n        //then\n        assertThat(value).isTrue();\n    }\n\n    @Test\n    public void evaluate_should_return_false_when_comparator_returns_true() throws Exception {\n        //given\n        given(comparator.areEquals(\"a\", \"a\")).willReturn(true);\n\n        //when\n        Boolean value = evaluator.evaluate(\"a\", \"a\");\n\n        //then\n        assertThat(value).isFalse();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/condition/EqualityComparatorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl.condition;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class EqualityComparatorTest {\n\n    private EqualityComparator comparator;\n\n    @Before\n    public void setUp() throws Exception {\n        comparator = new EqualityComparator();\n    }\n\n    @Test\n    public void areEquals_should_return_true_when_two_parameters_are_null() throws Exception {\n        //given\n\n        //when\n        Boolean equals = comparator.areEquals(null, null);\n\n        //then\n        assertThat(equals).isTrue();\n    }\n\n    @Test\n    public void areEquals_should_return_false_when_first_parameter_is_null_and_second_one_is_not_null()\n            throws Exception {\n        //given\n\n        //when\n        Boolean equals = comparator.areEquals(null, \"not null\");\n\n        //then\n        assertThat(equals).isFalse();\n    }\n\n    @Test\n    public void areEquals_should_return_false_when_first_parameter_is_not_null_and_second_one_is_null()\n            throws Exception {\n        //given\n\n        //when\n        Boolean equals = comparator.areEquals(\"not null\", null);\n\n        //then\n        assertThat(equals).isFalse();\n    }\n\n    @Test\n    public void areEquals_should_return_true_when_two_parameters_are_equals() throws Exception {\n        //given\n\n        //when\n        Boolean equals = comparator.areEquals(\"equal\", \"equal\");\n\n        //then\n        assertThat(equals).isTrue();\n    }\n\n    @Test\n    public void areEquals_should_return_false_when_two_parameters_are_different() throws Exception {\n        //given\n\n        //when\n        Boolean equals = comparator.areEquals(\"not null\", \"not null, but different\");\n\n        //then\n        assertThat(equals).isFalse();\n    }\n\n    @Test\n    public void can_compare_different_types() throws Exception {\n        //given\n\n        //when\n        Boolean equals = comparator.areEquals(\"not null\", 5);\n\n        //then\n        assertThat(equals).isFalse();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/condition/EqualsComparatorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl.condition;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class EqualsComparatorTest {\n\n    @Mock\n    private EqualityComparator comparator;\n\n    @InjectMocks\n    private EqualsComparator evaluator;\n\n    @Test\n    public void evaluate_should_return_true_when_comparator_return_true() throws Exception {\n        //given\n        given(comparator.areEquals(\"a\", \"a\")).willReturn(true);\n\n        //when\n        Boolean value = evaluator.evaluate(\"a\", \"a\");\n\n        //then\n        assertThat(value).isTrue();\n    }\n\n    @Test\n    public void evaluate_should_return_false_when_comparator_return_false() throws Exception {\n        //given\n        given(comparator.areEquals(\"a\", \"b\")).willReturn(false);\n\n        //when\n        Boolean value = evaluator.evaluate(\"a\", \"b\");\n\n        //then\n        assertThat(value).isFalse();\n    }\n}\n"
  },
  {
    "path": "services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/condition/GreaterThanComparatorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl.condition;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class GreaterThanComparatorTest {\n\n    @Mock\n    private InequalityComparator comparator;\n\n    @InjectMocks\n    private GreaterThanComparator evaluator;\n\n    @Test\n    public void evaluate_should_return_true_when_compare_is_greater_than_zero() throws Exception {\n        //given\n        given(comparator.compareTo(\"a\", \"b\")).willReturn(1);\n\n        //when\n        Boolean value = evaluator.evaluate(\"a\", \"b\");\n\n        //then\n        assertThat(value).isTrue();\n    }\n\n    @Test\n    public void evaluate_should_return_false_when_compare_is_zero() throws Exception {\n        //given\n        given(comparator.compareTo(\"a\", \"b\")).willReturn(0);\n\n        //when\n        Boolean value = evaluator.evaluate(\"a\", \"b\");\n\n        //then\n        assertThat(value).isFalse();\n    }\n\n    @Test\n    public void evaluate_should_return_false_when_compare_is_less_than_zero() throws Exception {\n        //given\n        given(comparator.compareTo(\"a\", \"b\")).willReturn(-1);\n\n        //when\n        Boolean value = evaluator.evaluate(\"a\", \"b\");\n\n        //then\n        assertThat(value).isFalse();\n    }\n\n    @Test\n    public void evaluate_should_return_null_when_compare_is_null() throws Exception {\n        //given\n        given(comparator.compareTo(\"a\", \"b\")).willReturn(null);\n\n        //when\n        Boolean value = evaluator.evaluate(\"a\", \"b\");\n\n        //then\n        assertThat(value).isNull();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/condition/GreaterThanOrEqualsComparatorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl.condition;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class GreaterThanOrEqualsComparatorTest {\n\n    @Mock\n    private InequalityComparator comparator;\n\n    @InjectMocks\n    private GreaterThanOrEqualsComparator evaluator;\n\n    @Test\n    public void evaluate_should_return_true_when_compare_is_greater_than_zero() throws Exception {\n        //given\n        given(comparator.compareTo(\"a\", \"b\")).willReturn(1);\n\n        //when\n        Boolean value = evaluator.evaluate(\"a\", \"b\");\n\n        //then\n        assertThat(value).isTrue();\n    }\n\n    @Test\n    public void evaluate_should_return_true_when_compare_is_zero() throws Exception {\n        //given\n        given(comparator.compareTo(\"a\", \"b\")).willReturn(0);\n\n        //when\n        Boolean value = evaluator.evaluate(\"a\", \"b\");\n\n        //then\n        assertThat(value).isTrue();\n    }\n\n    @Test\n    public void evaluate_should_return_false_when_compare_is_less_than_zero() throws Exception {\n        //given\n        given(comparator.compareTo(\"a\", \"b\")).willReturn(-1);\n\n        //when\n        Boolean value = evaluator.evaluate(\"a\", \"b\");\n\n        //then\n        assertThat(value).isFalse();\n    }\n\n    @Test\n    public void evaluate_should_return_null_when_compare_is_null() throws Exception {\n        //given\n        given(comparator.compareTo(\"a\", \"b\")).willReturn(null);\n\n        //when\n        Boolean value = evaluator.evaluate(\"a\", \"b\");\n\n        //then\n        assertThat(value).isNull();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/condition/InequalityComparatorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl.condition;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class InequalityComparatorTest {\n\n    private InequalityComparator comparator;\n\n    @Before\n    public void setUp() throws Exception {\n        comparator = new InequalityComparator();\n    }\n\n    @Test\n    public void compareTo_should_return_positive_integer_when_left_is_greater_than_right() throws Exception {\n        //given\n\n        //when\n        Integer value = comparator.compareTo(5, 4);\n\n        //then\n        assertThat(value).isGreaterThan(0);\n    }\n\n    @Test\n    public void compareTo_should_return_negative_integer_when_left_is_greater_than_right() throws Exception {\n        //given\n\n        //when\n        Integer value = comparator.compareTo(3, 4);\n\n        //then\n        assertThat(value).isLessThan(0);\n    }\n\n    @Test\n    public void compareTo_should_return_null_when_first_parameter_is_null() throws Exception {\n        //given\n\n        //when\n        Integer value = comparator.compareTo(null, 4);\n\n        //then\n        assertThat(value).isNull();\n    }\n\n    @Test\n    public void compareTo_should_return_null_when_second_parameter_is_null() throws Exception {\n        //given\n\n        //when\n        Integer value = comparator.compareTo(3, null);\n\n        //then\n        assertThat(value).isNull();\n    }\n\n    @Test(expected = SComparisonException.class)\n    public void compareTo_should_return_throw_SComparisonException_when_parameters_does_not_implement_comparable()\n            throws Exception {\n        //given\n\n        //when\n        comparator.compareTo(new NotComparableClass(), new NotComparableClass());\n\n        //then exception\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/condition/LessThanComparatorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl.condition;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class LessThanComparatorTest {\n\n    @Mock\n    private InequalityComparator comparator;\n\n    @InjectMocks\n    private LessThanComparator evaluator;\n\n    @Test\n    public void evaluate_should_return_true_when_compare_is_less_than_zero() throws Exception {\n        //given\n        given(comparator.compareTo(\"a\", \"b\")).willReturn(-1);\n\n        //when\n        Boolean value = evaluator.evaluate(\"a\", \"b\");\n\n        //then\n        assertThat(value).isTrue();\n    }\n\n    @Test\n    public void evaluate_should_return_false_when_compare_is_zero() throws Exception {\n        //given\n        given(comparator.compareTo(\"a\", \"b\")).willReturn(0);\n\n        //when\n        Boolean value = evaluator.evaluate(\"a\", \"b\");\n\n        //then\n        assertThat(value).isFalse();\n    }\n\n    @Test\n    public void evaluate_should_return_false_when_compare_is_greater_than_zero() throws Exception {\n        //given\n        given(comparator.compareTo(\"a\", \"b\")).willReturn(1);\n\n        //when\n        Boolean value = evaluator.evaluate(\"a\", \"b\");\n\n        //then\n        assertThat(value).isFalse();\n    }\n\n    @Test\n    public void evaluate_should_return_null_when_compare_is_null() throws Exception {\n        //given\n        given(comparator.compareTo(\"a\", \"b\")).willReturn(null);\n\n        //when\n        Boolean value = evaluator.evaluate(\"a\", \"b\");\n\n        //then\n        assertThat(value).isNull();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/condition/LessThanOrEqualsComparatorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl.condition;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class LessThanOrEqualsComparatorTest {\n\n    @Mock\n    private InequalityComparator comparator;\n\n    @InjectMocks\n    private LessThanOrEqualsComparator evaluator;\n\n    @Test\n    public void evaluate_should_return_true_when_compare_is_less_than_zero() throws Exception {\n        //given\n        given(comparator.compareTo(\"a\", \"b\")).willReturn(-1);\n\n        //when\n        Boolean value = evaluator.evaluate(\"a\", \"b\");\n\n        //then\n        assertThat(value).isTrue();\n    }\n\n    @Test\n    public void evaluate_should_return_true_when_compare_is_zero() throws Exception {\n        //given\n        given(comparator.compareTo(\"a\", \"b\")).willReturn(0);\n\n        //when\n        Boolean value = evaluator.evaluate(\"a\", \"b\");\n\n        //then\n        assertThat(value).isTrue();\n    }\n\n    @Test\n    public void evaluate_should_return_false_when_compare_is_greater_than_zero() throws Exception {\n        //given\n        given(comparator.compareTo(\"a\", \"b\")).willReturn(1);\n\n        //when\n        Boolean value = evaluator.evaluate(\"a\", \"b\");\n\n        //then\n        assertThat(value).isFalse();\n    }\n\n    @Test\n    public void evaluate_should_return_null_when_compare_is_null() throws Exception {\n        //given\n        given(comparator.compareTo(\"a\", \"b\")).willReturn(null);\n\n        //when\n        Boolean value = evaluator.evaluate(\"a\", \"b\");\n\n        //then\n        assertThat(value).isNull();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/condition/LogicalComplementExecutorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl.condition;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.fail;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.expression.exception.SExpressionEvaluationException;\nimport org.bonitasoft.engine.expression.impl.ConditionExpressionExecutorStrategy;\nimport org.bonitasoft.engine.expression.model.SExpression;\nimport org.bonitasoft.engine.expression.model.impl.SExpressionImpl;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class LogicalComplementExecutorTest {\n\n    @InjectMocks\n    private LogicalComplementExecutor logicalComplementExecutor;\n\n    @Test\n    public void evaluate_should_return_true_when_the_resolution_of_first_dependency_is_false() throws Exception {\n        //given\n        SExpression sourceExpression = buildBooleanExpression(false);\n        Map<Integer, Object> resolvedExpressions = Collections\n                .<Integer, Object> singletonMap(sourceExpression.getDiscriminant(), false);\n        SExpression logicalComplementExpression = buildLogicalComplementExpression(sourceExpression);\n\n        //when\n        Boolean value = logicalComplementExecutor.evaluate(resolvedExpressions, logicalComplementExpression);\n\n        //then\n        assertThat(value).isTrue();\n    }\n\n    @Test\n    public void evaluate_should_return_false_when_the_resolution_of_first_dependency_is_true() throws Exception {\n        //given\n        SExpression sourceExpression = buildBooleanExpression(true);\n        Map<Integer, Object> resolvedExpressions = Collections\n                .<Integer, Object> singletonMap(sourceExpression.getDiscriminant(), true);\n        SExpression logicalComplementExpression = buildLogicalComplementExpression(sourceExpression);\n\n        //when\n        Boolean value = logicalComplementExecutor.evaluate(resolvedExpressions, logicalComplementExpression);\n\n        //then\n        assertThat(value).isFalse();\n    }\n\n    @Test\n    public void evaluate_should_return_null_when_the_resolution_of_first_dependency_is_null() throws Exception {\n        //given\n        SExpression sourceExpression = buildBooleanExpression(true);\n        Map<Integer, Object> resolvedExpressions = Collections.emptyMap();\n        SExpression logicalComplementExpression = buildLogicalComplementExpression(sourceExpression);\n\n        //when\n        Boolean value = logicalComplementExecutor.evaluate(resolvedExpressions, logicalComplementExpression);\n\n        //then\n        assertThat(value).isNull();\n    }\n\n    @Test\n    public void evaluate_should_throws_SExpressionEvaluationException_when_dependencies_has_size_different_of_one()\n            throws Exception {\n        //given\n\n        List<SExpression> dependencies = Arrays.asList(mock(SExpression.class), mock(SExpression.class));\n\n        SExpression expression = mock(SExpression.class);\n        given(expression.getName()).willReturn(\"my expr\");\n        given(expression.getDependencies()).willReturn(dependencies);\n\n        try {\n            //when\n            logicalComplementExecutor.evaluate(Collections.<Integer, Object> emptyMap(), expression);\n            fail(\"Exception expected\");\n        } catch (SExpressionEvaluationException e) {\n            //then\n            assertThat(e.getMessage()).isEqualTo(\n                    \"The expression '\" + ConditionExpressionExecutorStrategy.LOGICAL_COMPLEMENT_OPERATOR\n                            + \"' must have exactly 1 dependency.\");\n            assertThat(e.getExpressionName()).isEqualTo(\"my expr\");\n        }\n\n    }\n\n    @Test\n    public void evaluate_should_throws_SExpressionEvaluationException_when_dependency_does_not_return_a_boolean()\n            throws Exception {\n        //given\n        SExpression sourceExpression = mock(SExpression.class);\n        given(sourceExpression.getReturnType()).willReturn(Integer.class.getName());\n\n        SExpression expression = mock(SExpression.class);\n        given(expression.getName()).willReturn(\"my expr\");\n        given(expression.getDependencies()).willReturn(Collections.singletonList(sourceExpression));\n\n        try {\n            //when\n            logicalComplementExecutor.evaluate(Collections.<Integer, Object> emptyMap(), expression);\n            fail(\"Exception expected\");\n        } catch (SExpressionEvaluationException e) {\n            //then\n            assertThat(e.getMessage()).isEqualTo(\n                    \"The dependency of expression '!' must have the return type java.lang.Boolean, but java.lang.Integer was found.\");\n            assertThat(e.getExpressionName()).isEqualTo(\"my expr\");\n        }\n\n    }\n\n    private SExpression buildBooleanExpression(boolean value) {\n        final SExpressionImpl expression = new SExpressionImpl();\n        String strValue = String.valueOf(value);\n        expression.setName(strValue);\n        expression.setContent(strValue);\n        expression.setExpressionType(SExpression.TYPE_CONSTANT);\n        expression.setReturnType(Boolean.class.getName());\n        expression.setDependencies(Collections.<SExpression> emptyList());\n        return expression;\n    }\n\n    private SExpression buildLogicalComplementExpression(SExpression sourceExpression) {\n        final SExpressionImpl expression = new SExpressionImpl();\n        expression.setName(\"not\");\n        expression.setContent(ConditionExpressionExecutorStrategy.LOGICAL_COMPLEMENT_OPERATOR);\n        expression.setExpressionType(SExpression.TYPE_CONDITION);\n        expression.setReturnType(Boolean.class.getName());\n        expression.setDependencies(Collections.singletonList(sourceExpression));\n        return expression;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/condition/NotComparableClass.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.expression.impl.condition;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class NotComparableClass {\n}\n"
  },
  {
    "path": "services/bonita-expression/src/test/resources/authors.xml",
    "content": "<racine>\n\t<encyclopedie nom=\"Wikipedia\" site=\"http://fr.wikipedia.org\">\n\t\t<article nom=\"XPath\">\n\t\t\t<auteurs>\n\t\t\t\t<auteur>\n\t\t\t\t\t<nom>Dupont</nom>\n\t\t\t\t</auteur>\n\t\t\t\t<auteur>\n\t\t\t\t\t<nom>Dubois</nom>\n\t\t\t\t</auteur>\n\t\t\t</auteurs>\n\t\t</article>\n\t</encyclopedie>\n</racine>"
  },
  {
    "path": "services/bonita-expression/src/test/resources/books.xml",
    "content": "<?xml version=\"1.0\"?>\n<catalog>\n   <book id=\"bk101\">\n      <author>Gambardella, Matthew</author>\n      <title>XML Developer's Guide</title>\n      <genre>Computer</genre>\n      <price>44.95</price>\n      <publish_date>2000-10-01</publish_date>\n      <description>An in-depth look at creating applications \n      with XML.</description>\n      <owned>true</owned>\n      <quantity>123456789</quantity>\n   </book>\n   <book id=\"bk102\">\n      <author>Ralls, Kim</author>\n      <title>Midnight Rain</title>\n      <genre>Fantasy</genre>\n      <price>5.95</price>\n      <publish_date>2000-12-16</publish_date>\n      <description>A former architect battles corporate zombies, \n      an evil sorceress, and her own childhood to become queen \n      of the world.</description>\n      <owned>1</owned>\n   </book>\n   <book id=\"bk103\">\n      <author>Corets, Eva</author>\n      <title>Maeve Ascendant</title>\n      <genre>Fantasy</genre>\n      <price>5.95</price>\n      <publish_date>2000-11-17</publish_date>\n      <description>After the collapse of a nanotechnology \n      society in England, the young survivors lay the \n      foundation for a new society.</description>\n      <owned>false</owned>\n   </book>\n   <book id=\"bk104\">\n      <author>Corets, Eva</author>\n      <title>Oberon's Legacy</title>\n      <genre>Fantasy</genre>\n      <price>5.95</price>\n      <publish_date>2001-03-10</publish_date>\n      <description>In post-apocalypse England, the mysterious \n      agent known only as Oberon helps to create a new life \n      for the inhabitants of London. Sequel to Maeve \n      Ascendant.</description>\n      <owned>0</owned>\n   </book>\n   <book id=\"bk105\">\n      <author>Corets, Eva</author>\n      <title>The Sundered Grail</title>\n      <genre>Fantasy</genre>\n      <price>5.95</price>\n      <publish_date>2001-09-10</publish_date>\n      <description>The two daughters of Maeve, half-sisters, \n      battle one another for control of England. Sequel to \n      Oberon's Legacy.</description>\n   </book>\n   <book id=\"bk106\">\n      <author>Randall, Cynthia</author>\n      <title>Lover Birds</title>\n      <genre>Romance</genre>\n      <price>4.95</price>\n      <publish_date>2000-09-02</publish_date>\n      <description>When Carla meets Paul at an ornithology \n      conference, tempers fly as feathers get ruffled.</description>\n   </book>\n   <book id=\"bk107\">\n      <author>Thurman, Paula</author>\n      <title>Splish Splash</title>\n      <genre>Romance</genre>\n      <price>4.95</price>\n      <publish_date>2000-11-02</publish_date>\n      <description>A deep sea diver finds true love twenty \n      thousand leagues beneath the sea.</description>\n   </book>\n   <book id=\"bk108\">\n      <author>Knorr, Stefan</author>\n      <title>Creepy Crawlies</title>\n      <genre>Horror</genre>\n      <price>4.95</price>\n      <publish_date>2000-12-06</publish_date>\n      <description>An anthology of horror stories about roaches,\n      centipedes, scorpions  and other insects.</description>\n   </book>\n   <book id=\"bk109\">\n      <author>Kress, Peter</author>\n      <title>Paradox Lost</title>\n      <genre>Science Fiction</genre>\n      <price>6.95</price>\n      <publish_date>2000-11-02</publish_date>\n      <description>After an inadvertant trip through a Heisenberg\n      Uncertainty Device, James Salway discovers the problems \n      of being quantum.</description>\n   </book>\n   <book id=\"bk110\">\n      <author>O'Brien, Tim</author>\n      <title>Microsoft .NET: The Programming Bible</title>\n      <genre>Computer</genre>\n      <price>36.95</price>\n      <publish_date>2000-12-09</publish_date>\n      <description>Microsoft's .NET initiative is explored in \n      detail in this deep programmer's reference.</description>\n   </book>\n   <book id=\"bk111\">\n      <author>O'Brien, Tim</author>\n      <title>MSXML3: A Comprehensive Guide</title>\n      <genre>Computer</genre>\n      <price>36.95</price>\n      <publish_date>2000-12-01</publish_date>\n      <description>The Microsoft MSXML3 parser is covered in \n      detail, with attention to XML DOM interfaces, XSLT processing, \n      SAX and more.</description>\n   </book>\n   <book id=\"bk112\">\n      <author>Galos, Mike</author>\n      <title>Visual Studio 7: A Comprehensive Guide</title>\n      <genre>Computer</genre>\n      <price>49.95</price>\n      <publish_date>2001-04-16</publish_date>\n      <description>Microsoft Visual Studio 7 is explored in depth,\n      looking at how Visual Basic, Visual C++, C#, and ASP+ are \n      integrated into a comprehensive development \n      environment.</description>\n   </book>\n</catalog>"
  },
  {
    "path": "services/bonita-expression/src/test/resources/logback-test.xml",
    "content": "<configuration debug=\"false\" scan=\"false\">\n\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->\n        <encoder>\n            <pattern>|%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <logger name=\"org.bonitasoft\" level=\"INFO\" />\n\n    <root level=\"WARN\">\n        <appender-ref ref=\"STDOUT\" />\n    </root>\n\n</configuration>\n"
  },
  {
    "path": "services/bonita-identity/build.gradle",
    "content": "\n\ndependencies {\n    api project(':bpm:bonita-common')\n    api project(':services:bonita-log')\n    api project(':services:bonita-builder')\n    api project(':services:bonita-persistence')\n    api project(':services:bonita-commons')\n    api project(':services:bonita-events')\n    api libs.jakartaActivation\n    testImplementation libs.mockitoCore\n    testImplementation libs.assertj\n\n    annotationProcessor libs.lombok\n    compileOnly libs.lombok\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/IconService.java",
    "content": "/**\n * Copyright (C) 2021 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport java.util.Optional;\n\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.SRecorderException;\n\npublic interface IconService {\n\n    String EVENT_NAME = \"ICON\";\n\n    Optional<Long> replaceIcon(String iconFilename, byte[] iconContent, Long iconIdToReplace)\n            throws SBonitaReadException, SRecorderException;\n\n    SIcon createIcon(String iconFilename, byte[] iconContent) throws SRecorderException;\n\n    SIcon getIcon(Long id) throws SBonitaReadException;\n\n    void deleteIcon(Long iconId) throws SBonitaReadException, SRecorderException;\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/IconServiceImpl.java",
    "content": "/**\n * Copyright (C) 2021 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport java.util.Optional;\n\nimport org.bonitasoft.engine.commons.io.IOUtil;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.services.PersistenceService;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class IconServiceImpl implements IconService {\n\n    private final Recorder recorder;\n    private final PersistenceService persistenceService;\n\n    public IconServiceImpl(Recorder recorder, PersistenceService persistenceService) {\n        this.recorder = recorder;\n        this.persistenceService = persistenceService;\n    }\n\n    @Override\n    public Optional<Long> replaceIcon(String iconFilename, byte[] iconContent, Long iconIdToReplace)\n            throws SBonitaReadException, SRecorderException {\n        deleteIcon(iconIdToReplace);\n        if (iconContent != null) {\n            return Optional.of(createIcon(iconFilename, iconContent).getId());\n        }\n        return Optional.empty();\n    }\n\n    @Override\n    public SIcon createIcon(String iconFilename, byte[] iconContent) throws SRecorderException {\n        if (iconFilename == null || iconFilename.isBlank() || iconContent == null || iconContent.length == 0) {\n            throw new IllegalArgumentException(\"Unable to create an icon without filename or content\");\n        }\n        SIcon entity = new SIcon(IOUtil.getContentTypeForIcon(iconFilename), iconContent);\n        recorder.recordInsert(new InsertRecord(entity), EVENT_NAME);\n        return entity;\n    }\n\n    @Override\n    public SIcon getIcon(Long iconId) throws SBonitaReadException {\n        if (iconId == null) {\n            return null;\n        }\n        return persistenceService.selectById(new SelectByIdDescriptor<>(SIcon.class, iconId));\n    }\n\n    @Override\n    public void deleteIcon(Long iconId) throws SBonitaReadException, SRecorderException {\n        if (iconId == null) {\n            return;\n        }\n\n        SIcon icon = getIcon(iconId);\n        if (icon == null) {\n            return;\n        }\n        recorder.recordDelete(new DeleteRecord(icon), EVENT_NAME);\n    }\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/IdentityService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.identity.model.SContactInfo;\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoValue;\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.identity.model.SRole;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.identity.model.SUserMembership;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * This service allow to manage the users and organizations and membership and the relations between them.\n *\n * @author Anthony Birembaut, Baptiste Mesta, Matthieu Chaffotte\n * @since 6.0\n */\npublic interface IdentityService {\n\n    String GROUP = \"GROUP\";\n    String CUSTOM_USER_INFO_DEFINITION = \"CUSTOM_USER_INFO_DEFINITION\";\n    String CUSTOM_USER_INFO_VALUE = \"CUSTOM_USER_INFO_VALUE\";\n    String ROLE = \"ROLE\";\n    String USER = \"USER\";\n    String USER_LOGIN = \"USER_LOGIN\";\n    String USER_CONTACT_INFO = \"USER_CONTACT_INFO\";\n    String USERMEMBERSHIP = \"USERMEMBERSHIP\";\n    String ICON = \"ICON\";\n\n    /**\n     * Gets the {@link SRole} given by its identifier.\n     *\n     * @param roleId\n     *        the role identifier\n     * @return the role of the given id\n     * @throws SRoleNotFoundException\n     *         occurs when the roleId does not refer to any role.\n     */\n    SRole getRole(long roleId) throws SRoleNotFoundException;\n\n    /**\n     * Get the {@link SRole} given by its name.\n     *\n     * @param roleName\n     *        The name of role\n     * @return the role of the given name\n     * @throws SRoleNotFoundException\n     *         occurs when the roleName does not refer to any role.\n     */\n    SRole getRoleByName(String roleName) throws SRoleNotFoundException;\n\n    /**\n     * Get total number of {@link SRole} for this tenant\n     *\n     * @return the total number of roles for this tenant\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     */\n    long getNumberOfRoles() throws SIdentityException;\n\n    /**\n     * Get a {@link List} of {@link SRole} by their identifiers\n     *\n     * @param roleIds\n     *        the role identifiers\n     * @return a list of {@link SRole} objects\n     * @throws SRoleNotFoundException\n     *         Occurs when roleId does not refer to any role.\n     */\n    List<SRole> getRoles(List<Long> roleIds) throws SRoleNotFoundException;\n\n    /**\n     * Get a {@link List} of {@link SRole} in specific interval\n     * If the number of existing results are lower than the number asked, all results from the given index are\n     * retrieved.\n     *\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param numberOfRoles\n     *        Number of result to retrieve\n     * @return a list of SRole objects\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     */\n    List<SRole> getRoles(int fromIndex, int numberOfRoles) throws SIdentityException;\n\n    /**\n     * Get a {@link List} of {@link SRole} in specific interval, sorted by <i>field</i> attribute in the given\n     * {@link OrderByType} <i>order</i>.\n     * <p> For instance, getRoles(0,10,\"displayName\", OrderByType.DESC) returns the 10 first roles sorted by\n     * &quot;displayName&quot; in desc order\n     * <p> If the number of existing results are lower than the number asked, all results from the given index are\n     * retrieved.\n     * <p> check {@link SRoleBuilderFactory} to get a list of available field keys to sort\n     *\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param numberOfRoles\n     *        Number of result to retrieve\n     * @param field\n     *        The field used by the sort\n     * @param order\n     *        ASC or DESC\n     * @return a list of paginated SRole objects\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     * @see SRoleBuilderFactory to get a list of available field keys to use\n     */\n    List<SRole> getRoles(int fromIndex, int numberOfRoles, String field, OrderByType order) throws SIdentityException;\n\n    /**\n     * Get the {@link SGroup} by its path\n     *\n     * @param groupPath\n     *        The group path\n     * @return the group\n     * @throws SGroupNotFoundException\n     *         Occurs when the groupPath does not refer to any group.\n     */\n    SGroup getGroupByPath(String groupPath) throws SGroupNotFoundException;\n\n    /**\n     * Get {@link SGroup} by its identifier\n     *\n     * @param groupId\n     *        The group identifier\n     * @return the group\n     * @throws SGroupNotFoundException\n     *         occurs when the groupId does not refer to any group.\n     */\n    SGroup getGroup(long groupId) throws SGroupNotFoundException;\n\n    /**\n     * Get total number of {@link SGroup} in the current tenant\n     *\n     * @return the total number of {@link SGroup}\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     */\n    long getNumberOfGroups() throws SIdentityException;\n\n    /**\n     * Get a {@link List} of {@link SGroup} for specific groupIds\n     *\n     * @param groupIds\n     *        The group identifiers\n     * @return a {@link List} of {@link SGroup} object\n     * @throws SGroupNotFoundException\n     *         occurs when no given group identifiers refer to any group.\n     */\n    List<SGroup> getGroups(List<Long> groupIds) throws SGroupNotFoundException;\n\n    /**\n     * Get a {@link List} of {@link SGroup} in a specific interval, this is used for pagination\n     * If the number of existing results are lower than the number asked, all results from the given index are\n     * retrieved.\n     *\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param numberOfGroups\n     *        Number of result to retrieve\n     * @return a {@link List} of {@link SGroup}\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     */\n    List<SGroup> getGroups(int fromIndex, int numberOfGroups) throws SIdentityException;\n\n    /**\n     * Get a {@link List} of {@link SGroup} in specific interval, sorted by <i>field</i> attribute in the given\n     * {@link OrderByType} <i>order</i>.\n     * <p> For instance, getGroups(0,10,\"displayName\", OrderByType.DESC) returns the 10 first {@link SGroup} sorted by\n     * &quot;displayName&quot; in desc order\n     * <p> If the number of existing results are lower than the number asked, all results from the given index are\n     * retrieved.\n     * <p> check {@link SGroupBuilderFactory} to get a list of available field keys to sort\n     *\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param numberOfGroups\n     *        Number of result to retrieve\n     * @param field\n     *        The field used to sort\n     * @param order\n     *        ASC or DESC\n     * @return a {@link List} of paginated {@link SGroup} objects\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     * @see SGroupBuilderFactory\n     */\n    List<SGroup> getGroups(int fromIndex, int numberOfGroups, String field, OrderByType order)\n            throws SIdentityException;\n\n    /**\n     * Get number of child groups for the group identified by the given id\n     *\n     * @param parentGroupId\n     *        The parent group identifier\n     * @return the number of child groups\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     */\n    long getNumberOfGroupChildren(long parentGroupId) throws SIdentityException;\n\n    /**\n     * Get a {@link List} child {@link SGroup} in a specific interval for specific group. This is used for pagination\n     * If the number of existing results are lower than the number asked, all results from the given index are\n     * retrieved.\n     *\n     * @param parentGroupId\n     *        The parent group identifier\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param numberOfGroups\n     *        Number of result to retrieve\n     * @return a {@link List} of child {@link SGroup}\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     */\n    List<SGroup> getGroupChildren(long parentGroupId, int fromIndex, int numberOfGroups) throws SIdentityException;\n\n    /**\n     * Get a {@link List} of child {@link SGroup} in specific interval, sorted by <i>field</i> attribute in the given\n     * {@link OrderByType} <i>order</i>.\n     * <p> For instance, getGroupChildren(0,10,\"displayName\", OrderByType.DESC) returns the 10 first child\n     * {@link SGroup} of the parent {@link SGroup}\n     * identified by <i>parentGroupId</i> sorted by &quot;displayName&quot; in desc order\n     * <p> If the number of existing results are lower than the number asked, all results from the given index are\n     * retrieved.\n     * <p> check {@link SGroupBuilderFactory} to get a list of available field keys to sort\n     *\n     * @param parentGroupId\n     *        The parent group identifier\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param numberOfGroups\n     *        Number of result to retrieve\n     * @param field\n     *        The field used to sort\n     * @param order\n     *        ASC or DESC\n     * @return a {@link List} of child {@link SGroup}\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     */\n    List<SGroup> getGroupChildren(long parentGroupId, int fromIndex, int numberOfGroups, String field,\n            OrderByType order) throws SIdentityException;\n\n    /**\n     * Get {@link SUser} by its id\n     *\n     * @param userId\n     *        The user identifier\n     * @return the user\n     * @throws SUserNotFoundException\n     *         occurs when the userId does not refer to any user.\n     */\n    SUser getUser(long userId) throws SUserNotFoundException;\n\n    /**\n     * Checks whether the couple user/password is valid.\n     *\n     * @param user\n     *        the user\n     * @param password\n     *        the password\n     * @return true if the couple user/password is valid; false otherwise\n     */\n    boolean checkCredentials(SUser user, String password);\n\n    /**\n     * Get {@link SUser} by its name\n     *\n     * @param username\n     *        The user name\n     * @return the user\n     * @throws SUserNotFoundException\n     *         occurs when the user name does not refer to any user.\n     */\n    SUser getUserByUserName(String username) throws SUserNotFoundException;\n\n    /**\n     * Get total number of {@link SUser} on the current tenant\n     *\n     * @return the total number of users\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     */\n    long getNumberOfUsers() throws SIdentityException;\n\n    /**\n     * Get {@link SUser} by their userIds\n     *\n     * @param userIds\n     *        A {@link List} of user identifiers\n     * @return a {@link List} of {@link SUser}\n     * @throws SUserNotFoundException\n     *         occurs when none of the given userIds refer to any user.\n     */\n    List<SUser> getUsers(List<Long> userIds) throws SUserNotFoundException;\n\n    /**\n     * retrieve a {@link List} of {@link SUser} from their names.\n     *\n     * @param userNames\n     *        the list of user names\n     * @return a {@link List} of {@link SUser}\n     * @throws SIdentityException\n     *         If an exception occurs when retrieving the users\n     */\n    List<SUser> getUsersByUsername(List<String> userNames) throws SIdentityException;\n\n    /**\n     * Get a {@link List} of {@link SUser} from a specific interval.\n     * If the number of existing results are lower than the number asked, all results from the given index are\n     * retrieved.\n     *\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param numberOfUsers\n     *        Number of result to retrieve\n     * @return a {@link List} of {@link SUser}\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     */\n    List<SUser> getUsers(int fromIndex, int numberOfUsers) throws SIdentityException;\n\n    /**\n     * Get a {@link List} of child {@link SUser} from specific interval, sorted by <i>field</i> attribute in the given\n     * {@link OrderByType} <i>order</i> order.\n     * <p> For instance, getUsers(0,10,\"displayName\", OrderByType.DESC) returns the 10 first {@link SUser} sorted by\n     * &quot;displayName&quot; in desc order\n     * <p> If the number of existing results are lower than the number asked, all results from the given index are\n     * retrieved.\n     * <p> check {@link SUser} to get a list of available field keys to sort\n     *\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param numberOfUsers\n     *        Number of result we want to get. Maximum number of result returned.\n     * @param field\n     *        The field used by the order\n     * @param order\n     *        ASC or DESC\n     * @return a {@link List} of paginated {@link SUser}\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     */\n    List<SUser> getUsers(int fromIndex, int numberOfUsers, String field, OrderByType order) throws SIdentityException;\n\n    /**\n     * Get all users (both active and inactive) managed by a specific manager from a specific interval.\n     * If the number of existing results are lower than the number asked, all results from the given index are\n     * retrieved.\n     *\n     * @param managerId\n     *        The manager identifier, actually it is user identifier\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param numberMaxOfUsers\n     *        Number of result to retrieve\n     * @param field\n     *        The field used to sort\n     * @param order\n     *        ASC or DESC\n     * @return a {@link List} of {@link SUser}\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     */\n    List<SUser> getUsersWithManager(long managerId, int fromIndex, int numberMaxOfUsers, String field,\n            OrderByType order) throws SIdentityException;\n\n    /**\n     * Get all active users managed by a specific manager from a specific interval.\n     * If the number of existing results are lower than the number asked, all results from the given index are\n     * retrieved.\n     *\n     * @param managerId\n     *        The manager identifier, actually it is user identifier\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param numberMaxOfUsers\n     *        Number of result to retrieve\n     * @param field\n     *        The field used to sort\n     * @param order\n     *        ASC or DESC\n     * @return a {@link List} of {@link SUser}\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     */\n    List<SUser> getActiveUsersWithManager(long managerId, int fromIndex, int numberMaxOfUsers, String field,\n            OrderByType order) throws SIdentityException;\n\n    /**\n     * Get all inactive users managed by a specific manager from a specific interval.\n     * If the number of existing results are lower than the number asked, all results from the given index are\n     * retrieved.\n     *\n     * @param managerId\n     *        The manager identifier, actually it is user identifier\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param numberMaxOfUsers\n     *        Number of result to retrieve\n     * @param field\n     *        The field used to sort\n     * @param order\n     *        ASC or DESC\n     * @return a {@link List} of {@link SUser}\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     */\n    List<SUser> getInactiveUsersWithManager(long managerId, int fromIndex, int numberMaxOfUsers, String field,\n            OrderByType order) throws SIdentityException;\n\n    /**\n     * Get total number of users for the given role on the current tenant\n     *\n     * @param roleId\n     *        The identifier of role\n     * @return total number of users related to the given role\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     */\n    long getNumberOfUsersByRole(long roleId) throws SIdentityException;\n\n    /**\n     * Get a {@link List} of {@link SUser} from a specific interval for the given {@link SRole} identifier.\n     * If the number of existing results are lower than the number asked, all results from the given index are\n     * retrieved.\n     * Note that this method returns both active and inactive users.\n     *\n     * @param roleId\n     *        The identifier of the {@link SRole} of the {@link SUser} to retrieve\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param numberOfUsers\n     *        Number of result to retrieve\n     * @return a {@link List} of {@link SUser}\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     */\n    List<SUser> getUsersWithRole(long roleId, int fromIndex, int numberOfUsers) throws SIdentityException;\n\n    /**\n     * Get a {@link List} of active {@link SUser} from a specific interval for the given {@link SRole} identifier.\n     * If the number of existing results are lower than the number asked, all results from the given index are\n     * retrieved.\n     *\n     * @param roleId\n     *        The identifier of the {@link SRole} of the {@link SUser} to retrieve\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param numberOfUsers\n     *        Number of result to retrieve\n     * @return a {@link List} of {@link SUser}\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     */\n    List<SUser> getActiveUsersWithRole(long roleId, int fromIndex, int numberOfUsers) throws SIdentityException;\n\n    /**\n     * Get a {@link List} of inactive {@link SUser} from a specific interval for the given {@link SRole} identifier.\n     * If the number of existing results are lower than the number asked, all results from the given index are\n     * retrieved.\n     *\n     * @param roleId\n     *        The identifier of the {@link SRole} of the {@link SUser} to retrieve\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param numberOfUsers\n     *        Number of result to retrieve\n     * @return a {@link List} of {@link SUser}\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     */\n    List<SUser> getInactiveUsersWithRole(long roleId, int fromIndex, int numberOfUsers) throws SIdentityException;\n\n    /**\n     * Get a {@link List} of {@link SUser} for given {@link SRole} from specific interval, sorted by <i>field</i>\n     * attribute in the given {@link OrderByType}\n     * <i>order</i> order.\n     * <p> For instance, getUsersWithRole(1,0,10,\"displayName\", OrderByType.DESC) returns the 10 first {@link SUser} of\n     * the {@link SRole} with id '1' sorted by\n     * &quot;displayName&quot; in desc order\n     * <p> If the number of existing results are lower than the number asked, all results from the given index are\n     * retrieved.\n     * <p> check {@link SUser} to get a list of available field keys to sort\n     *\n     * @param roleId\n     *        The identifier of the {@link SRole}\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param numberOfUsers\n     *        Number of result to retrieve\n     * @param field\n     *        The field used to sort\n     * @param order\n     *        ASC or DESC\n     * @return a {@link List} of {@link SUser}\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     */\n    List<SUser> getUsersWithRole(long roleId, int fromIndex, int numberOfUsers, String field, OrderByType order)\n            throws SIdentityException;\n\n    /**\n     * Get a {@link List} of active {@link SUser} for given {@link SRole} from specific interval, sorted by <i>field</i>\n     * attribute in the given\n     * {@link OrderByType}\n     * <i>order</i> order.\n     * <p> For instance, getActiveUsersWithRole(1,0,10,\"displayName\", OrderByType.DESC) returns the 10 first\n     * {@link SUser} of the {@link SRole} with id '1'\n     * sorted\n     * by\n     * &quot;displayName&quot; in desc order\n     * <p> If the number of existing results are lower than the number asked, all results from the given index are\n     * retrieved.\n     * <p> check {@link SUser} to get a list of available field keys to sort\n     *\n     * @param roleId\n     *        The identifier of the {@link SRole}\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param numberOfUsers\n     *        Number of result to retrieve\n     * @param field\n     *        The field used to sort\n     * @param order\n     *        ASC or DESC\n     * @return a {@link List} of {@link SUser}\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     */\n    List<SUser> getActiveUsersWithRole(long roleId, int fromIndex, int numberOfUsers, String field, OrderByType order)\n            throws SIdentityException;\n\n    /**\n     * Get a {@link List} of Inactive {@link SUser} for given {@link SRole} from specific interval, sorted by\n     * <i>field</i> attribute in the given\n     * {@link OrderByType}\n     * <i>order</i> order.\n     * <p> For instance, getInactiveUsersWithRole(1,0,10,\"displayName\", OrderByType.DESC) returns the 10 first\n     * {@link SUser} of the {@link SRole} with id '1'\n     * sorted by\n     * &quot;displayName&quot; in desc order\n     * <p> If the number of existing results are lower than the number asked, all results from the given index are\n     * retrieved.\n     * <p> check {@link SUser} to get a list of available field keys to sort\n     *\n     * @param roleId\n     *        The identifier of the {@link SRole}\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param numberOfUsers\n     *        Number of result to retrieve\n     * @param field\n     *        The field used to sort\n     * @param order\n     *        ASC or DESC\n     * @return a {@link List} of {@link SUser}\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     */\n    List<SUser> getInactiveUsersWithRole(long roleId, int fromIndex, int numberOfUsers, String field, OrderByType order)\n            throws SIdentityException;\n\n    /**\n     * Get total number of users for the given {@link SGroup}\n     *\n     * @param groupId\n     *        The identifier of the {@link SGroup}\n     * @return total number of users in the given group\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     */\n    long getNumberOfUsersByGroup(long groupId) throws SIdentityException;\n\n    /**\n     * Get a {@link List} of Active {@link SUser} for given {@link SGroup} from specific interval, sorted by\n     * <i>field</i> attribute in the given\n     * {@link OrderByType}\n     * <i>order</i> order.\n     * <p> For instance, getActiveUsersInGroup(1,0,10,\"displayName\", OrderByType.DESC) returns the 10 first\n     * {@link SUser} of the {@link SGroup} with id '1'\n     * sorted by\n     * &quot;displayName&quot; in desc order\n     * <p> If the number of existing results are lower than the number asked, all results from the given index are\n     * retrieved.\n     * <p> check {@link SUser} to get a list of available field keys to sort\n     *\n     * @param groupId\n     *        Identifier of the {@link SGroup}\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param numberOfUsers\n     *        Number of result to retrieve\n     * @param field\n     *        The field used to sort\n     * @param order\n     *        ASC or DESC\n     * @return a {@link List} of {@link SUser}\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     */\n    List<SUser> getActiveUsersInGroup(long groupId, int fromIndex, int numberOfUsers, String field, OrderByType order)\n            throws SIdentityException;\n\n    /**\n     * Get a {@link List} of Inactive {@link SUser} for given {@link SGroup} from specific interval, sorted by\n     * <i>field</i> attribute in the given\n     * {@link OrderByType}\n     * <i>order</i> order.\n     * <p> For instance, getInactiveUsersInGroup(1,0,10,\"displayName\", OrderByType.DESC) returns the 10 first\n     * {@link SUser} of the {@link SGroup} with id '1'\n     * sorted by\n     * &quot;displayName&quot; in desc order\n     * <p> If the number of existing results are lower than the number asked, all results from the given index are\n     * retrieved.\n     * <p> check {@link SUser} to get a list of available field keys to sort\n     *\n     * @param groupId\n     *        Identifier of the {@link SGroup}\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param numberOfUsers\n     *        Number of result to retrieve\n     * @param field\n     *        The field used to sort\n     * @param order\n     *        ASC or DESC\n     * @return a {@link List} of {@link SUser}\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     */\n    List<SUser> getInactiveUsersInGroup(long groupId, int fromIndex, int numberOfUsers, String field, OrderByType order)\n            throws SIdentityException;\n\n    /**\n     * Get a {@link List} of {@link SUser} for given {@link SGroup} from specific interval, sorted by <i>field</i>\n     * attribute in the given {@link OrderByType}\n     * <i>order</i> order.\n     * <p> For instance, getUsersInGroup(1,0,10,\"displayName\", OrderByType.DESC) returns the 10 first {@link SUser} of\n     * the {@link SGroup} with id '1' sorted by\n     * &quot;displayName&quot; in desc order\n     * <p> If the number of existing results are lower than the number asked, all results from the given index are\n     * retrieved.\n     * <p> check {@link SUser} to get a list of available field keys to sort\n     *\n     * @param groupId\n     *        Identifier of the {@link SGroup}\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param numberOfUsers\n     *        Number of result to retrieve\n     * @param field\n     *        The field used to sort\n     * @param order\n     *        ASC or DESC\n     * @return a {@link List} of {@link SUser}\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     */\n    List<SUser> getUsersInGroup(long groupId, int fromIndex, int numberOfUsers, String field, OrderByType order)\n            throws SIdentityException;\n\n    /**\n     * Get a {@link List} of {@link SUserMembership} for given group from specific interval\n     * <p> If the number of existing results are lower than the number asked, all results from the given index are\n     * retrieved.\n     *\n     * @param groupId\n     *        Identifier of the group\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param numberOfResults\n     *        Number of result to retrieve\n     * @return a {@link List} of {@link SUserMembership}\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     */\n    List<SUserMembership> getUserMembershipsOfGroup(long groupId, int fromIndex, int numberOfResults)\n            throws SIdentityException;\n\n    /**\n     * Get a {@link List} of {@link SUserMembership} for given role from specific interval\n     * <p> If the number of existing results are lower than the number asked, all results from the given index are\n     * retrieved.\n     *\n     * @param roleId\n     *        Identifier of the role\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param numberOfResults\n     *        Number of result to retrieve\n     * @return a {@link List} of {@link SUserMembership}\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     */\n    List<SUserMembership> getUserMembershipsOfRole(long roleId, int fromIndex, int numberOfResults)\n            throws SIdentityException;\n\n    /**\n     * Get {@link SUserMembership} by given id\n     *\n     * @param userMembershipId\n     *        The identifier of userMembership\n     * @return the {@link SUserMembership} of the given id\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem or if the {@link SUserMembership} does not exists\n     */\n    SUserMembership getUserMembership(long userMembershipId) throws SIdentityException;\n\n    /**\n     * Get {@link SUserMembership} of specific {@link SUser}, {@link SGroup} and {@link SRole}\n     *\n     * @param userId\n     *        The user's identifier\n     * @param groupId\n     *        The group's identifier\n     * @param roleId\n     *        The role's identifier\n     * @return the {@link SUserMembership}\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem or if the {@link SUserMembership} does not exists\n     */\n    SUserMembership getUserMembership(long userId, long groupId, long roleId) throws SIdentityException;\n\n    /**\n     * Get the {@link SUserMembership} of the specific user, group and role without userName, groupName and roleName\n     * being set.\n     *\n     * @param userId\n     *        The user's identifier\n     * @param groupId\n     *        The group's identifier\n     * @param roleId\n     *        The role's identifier\n     * @return the lightened {@link SUserMembership}\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem or if the {@link SUserMembership} does not exists\n     */\n    SUserMembership getLightUserMembership(long userId, long groupId, long roleId) throws SIdentityException;\n\n    /**\n     * Get a {@link List} of {@link SUserMembership} of the given identifiers if they exists\n     *\n     * @param userMembershipIds\n     *        The {@link SUserMembership}'s identifiers\n     * @return a {@link List} of {@link SUserMembership}\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem or if none identifiers match an existing\n     *         {@link SUserMembership}\n     */\n    List<SUserMembership> getUserMemberships(List<Long> userMembershipIds) throws SIdentityException;\n\n    /**\n     * Get a {@link List} of {@link SUserMembership} from specific interval\n     * <p> If the number of existing results are lower than the number asked, all results from the given index are\n     * retrieved.\n     *\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param numberOfResults\n     *        Number of result to retrieve\n     * @return a {@link List} of {@link SUserMembership}\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     */\n    List<SUserMembership> getUserMemberships(int fromIndex, int numberOfResults) throws SIdentityException;\n\n    /**\n     * Get a {@link List} of {@link SUserMembership} from specific interval, sorted by <i>field</i> attribute in the\n     * given {@link OrderByType} <i>order</i>\n     * order.\n     * <p> For instance, getUserMemberships(0,10,\"id\", OrderByType.DESC) returns the 10 first {@link SUserMembership}\n     * sorted by\n     * &quot;displayName&quot; in desc order\n     * <p> If the number of existing results are lower than the number asked, all results from the given index are\n     * retrieved.\n     * <p> check {@link SUserMembershipBuilderFactory} to check available field keys to sort\n     *\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param numberOfUserMemberships\n     *        Number of result to retrieve\n     * @param field\n     *        The field used to sort\n     * @param order\n     *        ASC or DESC\n     * @return a {@link List} of {@link SUserMembership}\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     * @see SUserMembershipBuilderFactory\n     */\n    List<SUserMembership> getUserMemberships(int fromIndex, int numberOfUserMemberships, OrderByOption orderByOption)\n            throws SIdentityException;\n\n    /**\n     * Get {@link SCustomUserInfoDefinition} by its id\n     *\n     * @param customUserInfoDefinitionId\n     *        The {@link SCustomUserInfoDefinition}'s identifier\n     * @return the customUserInfoDefinition\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     */\n    SCustomUserInfoDefinition getCustomUserInfoDefinition(long customUserInfoDefinitionId) throws SIdentityException;\n\n    /**\n     * Get {@link SCustomUserInfoValue} by its id\n     *\n     * @param customUserInfoValueId\n     *        The identifier of the custom user info value\n     * @return the {@link SCustomUserInfoValue}\n     * @throws SCustomUserInfoValueNotFoundException\n     *         if no custom user info value is found for the given id\n     * @throws SCustomUserInfoValueReadException\n     *         if an exception occurs while trying to get the custom user info value\n     */\n    SCustomUserInfoValue getCustomUserInfoValue(long customUserInfoValueId)\n            throws SCustomUserInfoValueNotFoundException, SCustomUserInfoValueReadException;\n\n    /**\n     * Get a {@link SCustomUserInfoDefinition} by its name\n     *\n     * @param name\n     *        The name of the {@link SCustomUserInfoDefinition} to retrieve\n     * @return the {@link SCustomUserInfoDefinition} identified by the given name\n     * @throws SCustomUserInfoDefinitionNotFoundException\n     *         if there is no custom user info definition for the given name\n     * @throws SCustomUserInfoDefinitionReadException\n     *         if an exception occurs when trying to retrieve the custom user info definition\n     */\n    SCustomUserInfoDefinition getCustomUserInfoDefinitionByName(String name)\n            throws SCustomUserInfoDefinitionNotFoundException,\n            SCustomUserInfoDefinitionReadException;\n\n    /**\n     * Verify if there is a {@link SCustomUserInfoDefinition} of the given name\n     *\n     * @param name\n     *        The {@link SCustomUserInfoDefinition}'s name to check\n     * @return the {@link SCustomUserInfoDefinition} of the given name\n     * @throws SCustomUserInfoDefinitionNotFoundException\n     *         if there is no custom user info definition for the given name\n     * @throws SCustomUserInfoDefinitionReadException\n     *         if an exception occurs when trying to retrieve the custom user info definition\n     */\n    boolean hasCustomUserInfoDefinition(String name) throws SCustomUserInfoDefinitionReadException;\n\n    /**\n     * Get total number of {@link SCustomUserInfoDefinition} of the current tenant\n     *\n     * @return the total number of {@link SCustomUserInfoDefinition} of the current tenant\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     */\n    long getNumberOfCustomUserInfoDefinition() throws SIdentityException;\n\n    /**\n     * Get total number of {@link SCustomUserInfoValue} of the current tenant matching the criterias of the given\n     * {@link QueryOptions}\n     *\n     * @param options\n     *        The {@link QueryOptions} containing filtering conditions\n     * @return the total number of custom user info value\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     */\n    long getNumberOfCustomUserInfoValue(QueryOptions options) throws SBonitaReadException;\n\n    /**\n     * Get a {@link List} of {@link SCustomUserInfoDefinition} of the given identifiers\n     *\n     * @param customUserInfoDefinitionIds\n     *        A {@link List} of {@link SCustomUserInfoDefinition} identifiers\n     * @return a {@link List} of {@link SCustomUserInfoDefinition}\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem or if none identifiers match an existing\n     *         {@link SCustomUserInfoDefinition}\n     */\n    List<SCustomUserInfoDefinition> getCustomUserInfoDefinitions(List<Long> customUserInfoDefinitionIds)\n            throws SIdentityException;\n\n    /**\n     * Get a {@link List} of {@link SCustomUserInfoValue} of the given identifiers\n     *\n     * @param customUserInfoValueIds\n     *        A {@link List} of {@link SCustomUserInfoValue} identifiers\n     * @return A {@link List} of {@link SCustomUserInfoValue}\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem or if none identifiers match an existing\n     *         {@link SCustomUserInfoDefinition}\n     */\n    List<SCustomUserInfoValue> getCustomUserInfoValues(List<Long> customUserInfoValueIds) throws SIdentityException;\n\n    /**\n     * Get a {@link List} of {@link SCustomUserInfoDefinition} from specific interval\n     * <p> If the number of existing results are lower than the number asked, all results from the given index are\n     * retrieved.\n     *\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param maxResults\n     *        Number of result to retrieve\n     * @return a {@link List} of {@link SCustomUserInfoDefinition}\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     */\n    List<SCustomUserInfoDefinition> getCustomUserInfoDefinitions(int fromIndex, int maxResults)\n            throws SIdentityException;\n\n    /**\n     * Retrieve a {@link List} of user identifiers from specific interval. The returned user's ids have their associated\n     * user's userInfo of the given\n     * name <i>userInfoName</i> matches the given value <i>userInfoValue</i> partially or not depending on the\n     * <i>usePartialMatch</i> parameter.\n     * <p> If the number of existing results are lower than the number asked, all results from the given index are\n     * retrieved.\n     *\n     * @param userInfoName\n     *        The user's information name.\n     * @param userInfoValue\n     *        The user's information value.\n     * @param usePartialMatch\n     *        Defines whether the custom user information value should use a partial match.\n     * @param fromIndex\n     *        The index of the first record to be retrieved. First record has index 0.\n     * @param maxResults\n     *        The max results to be retrieved.\n     * @return the list identifiers of users matching the given user's information name & value.\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     * @since 6.3.2\n     */\n    List<Long> getUserIdsWithCustomUserInfo(String userInfoName, String userInfoValue, boolean usePartialMatch,\n            int fromIndex, int maxResults)\n            throws SIdentityException;\n\n    /**\n     * Search {@link SCustomUserInfoValue} matching the criterias of the given {@link QueryOptions}\n     *\n     * @param options\n     *        The QueryOptions object containing some query conditions\n     * @return a list of SCustomUserInfoValue objects\n     * @throws SBonitaReadException\n     *         occurs on persistence layer access problem or if search parameters are not correct\n     */\n    List<SCustomUserInfoValue> searchCustomUserInfoValue(QueryOptions options) throws SBonitaReadException;\n\n    /**\n     * Get SCustomUserInfoValues of definitions for a user\n     */\n    List<SCustomUserInfoValue> getCustomUserInfoValueOfUserAndDefinitions(long userId, List<Long> definitionsIds)\n            throws SBonitaReadException;\n\n    /**\n     * Get {@link SUserMembership} for a specific interval for a given user\n     *\n     * @param userId\n     *        The user's identifier\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param numberOfMemberships\n     *        Number of result to retrieve\n     * @return a {@link List} of {@link SUserMembership}\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     */\n    List<SUserMembership> getUserMembershipsOfUser(long userId, int fromIndex, int numberOfMemberships)\n            throws SIdentityException;\n\n    /**\n     * Get a {@link List} of {@link SUserMembership} of a given user from specific interval, sorted by <i>field</i>\n     * attribute in the given {@link OrderByType}\n     * <i>sortOrder</i> order.\n     * <p> For instance, getUserMembershipsOfUser(1,0,10,\"id\", OrderByType.DESC) returns the 10 first\n     * {@link SUserMembership} of the given user sorted by\n     * &quot;id&quot; in desc order\n     * <p> If the number of existing results are lower than the number asked, all results from the given index are\n     * retrieved.\n     * <p> check {@link SUserMembershipBuilderFactory} to check available field keys to sort\n     *\n     * @param userId\n     *        The identifier of user\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param numberOfMemberships\n     *        Number of result to retrieve\n     * @param field\n     *        The field user to do order\n     * @param sortOrder\n     *        ASC or DESC\n     * @return a {@link List} of {@link SUserMembership}\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     * @see SUserMembershipBuilderFactory\n     */\n    List<SUserMembership> getUserMembershipsOfUser(long userId, int fromIndex, int numberOfMemberships, String field,\n            OrderByType sortOrder)\n            throws SIdentityException;\n\n    /**\n     * Get a {@link List} of {@link SUserMembership} of a given user from specific interval, sorted by default field in\n     * the given {@link OrderByType}\n     * <i>sortOrder</i> order.\n     * <p> For instance, getUserMembershipsOfUser(1,0,10, OrderByType.DESC) returns the 10 first {@link SUserMembership}\n     * of the given user sorted by\n     * the default field in desc order\n     * <p> If the number of existing results are lower than the number asked, all results from the given index are\n     * retrieved.\n     *\n     * @param userId\n     *        The identifier of user\n     * @param fromIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param numberPerPage\n     *        Number of result o retrieve\n     * @param sortOrder\n     *        OrderByOption object containing order information\n     * @return a list of SUserMembership objects\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     */\n    List<SUserMembership> getUserMembershipsOfUser(long userId, int fromIndex, int numberPerPage,\n            OrderByOption sortOrder) throws SIdentityException;\n\n    /**\n     * Get total number of {@link SUserMembership} for a specific user\n     *\n     * @param userId\n     *        The user's identifier\n     * @return total number of userMemberships for the specific user\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     */\n    long getNumberOfUserMembershipsOfUser(long userId) throws SIdentityException;\n\n    /**\n     * Get total number of userMemberships of the current tenant\n     *\n     * @return total number of userMemberships\n     * @throws SIdentityException\n     *         occurs on persistence layer access problem\n     */\n    long getNumberOfUserMemberships() throws SIdentityException;\n\n    /**\n     * Create {@link SUser} and store it to persistence layer\n     *\n     * @param user\n     *        The user to create and store\n     * @return the created user with its identifier set\n     * @throws SUserCreationException\n     *         occurs on persistence layer access problem\n     */\n    SUser createUser(SUser user) throws SUserCreationException;\n\n    /**\n     * Update {@link SUser} according to the {@link EntityUpdateDescriptor}\n     *\n     * @param user\n     *        The {@link SUser} to update\n     * @param descriptor\n     *        The {@link SUser} contents to update\n     * @throws SUserUpdateException\n     *         occurs on persistence layer access problem\n     */\n    void updateUser(SUser user, EntityUpdateDescriptor descriptor) throws SUserUpdateException;\n\n    /**\n     * Update {@link SUser} according to the {@link EntityUpdateDescriptor}\n     *\n     * @param user\n     *        The {@link SUser} to update\n     * @param descriptor\n     *        The {@link SUser} contents to update\n     * @param isPasswordEncrypted\n     *        allow to know if the password given in the {@link EntityUpdateDescriptor} is encrypted or not\n     * @throws SUserUpdateException\n     */\n    @Deprecated\n    void updateUser(SUser user, EntityUpdateDescriptor descriptor, boolean isPasswordEncrypted)\n            throws SUserUpdateException;\n\n    /**\n     * Create custom user info definition in DB for a server given custom user info definition\n     *\n     * @param customUserInfo\n     *        SCustomUserInfoDefinition object\n     * @throws SCustomUserInfoDefinitionAlreadyExistsException\n     *         TODO\n     * @throws SCustomUserInfoDefinitionCreationException\n     *         TODO\n     */\n    SCustomUserInfoDefinition createCustomUserInfoDefinition(SCustomUserInfoDefinition customUserInfo)\n            throws SCustomUserInfoDefinitionAlreadyExistsException,\n            SCustomUserInfoDefinitionCreationException;\n\n    /**\n     * Update customUserInfoDefinition according to the descriptor\n     *\n     * @param customUserInfo\n     *        The customUserInfoDefinition will be updated\n     * @param descriptor\n     *        The update description\n     * @throws SIdentityException\n     */\n    void updateCustomUserInfoDefinition(SCustomUserInfoDefinition customUserInfo, EntityUpdateDescriptor descriptor)\n            throws SIdentityException;\n\n    /**\n     * Create profileMetadataValue in DB for give profileMetadataValue object\n     *\n     * @param customUserInfo\n     *        A profileMetadataValue object\n     * @throws SIdentityException\n     */\n    SCustomUserInfoValue createCustomUserInfoValue(SCustomUserInfoValue customUserInfo) throws SIdentityException;\n\n    /**\n     * Update profileMetadataValue according to the descriptor\n     *\n     * @param customUserInfo\n     *        The profileMetadataValue will be updated\n     * @param descriptor\n     *        The update description\n     * @throws SIdentityException\n     */\n    void updateCustomUserInfoValue(SCustomUserInfoValue customUserInfo, EntityUpdateDescriptor descriptor)\n            throws SIdentityException;\n\n    /**\n     * Create role in DB for the given role\n     *\n     * @param role\n     *        A role object\n     * @param iconFilename\n     * @param iconContent\n     * @throws SIdentityException\n     */\n    void createRole(SRole role, String iconFilename, byte[] iconContent) throws SIdentityException;\n\n    /**\n     * Update role according to the descriptor\n     *\n     * @param role\n     *        The role will be updated\n     * @param descriptor\n     *        The update description\n     * @param iconUpdater\n     * @throws SIdentityException\n     */\n    SRole updateRole(SRole role, EntityUpdateDescriptor descriptor, EntityUpdateDescriptor iconUpdater)\n            throws SIdentityException;\n\n    /**\n     * Create group in DB for the given group object\n     *\n     * @param group\n     *        A group object\n     * @param iconFileName\n     * @param iconContent\n     * @throws SGroupCreationException\n     */\n    void createGroup(SGroup group, String iconFileName, byte[] iconContent) throws SGroupCreationException;\n\n    /**\n     * Update group according to the descriptor\n     *\n     * @param group\n     *        The group will be updated\n     * @param descriptor\n     *        The update description\n     * @param iconUpdater\n     * @throws SIdentityException\n     */\n    void updateGroup(SGroup group, EntityUpdateDescriptor descriptor, EntityUpdateDescriptor iconUpdater)\n            throws SIdentityException;\n\n    /**\n     * Create userMembership in DB for the given userMembership object\n     *\n     * @param userMembership\n     *        A userMembership object\n     * @throws SUserMembershipCreationException\n     */\n    void createUserMembership(SUserMembership userMembership) throws SUserMembershipCreationException;\n\n    /**\n     * Update userMembership according to the descriptor\n     *\n     * @param userMembership\n     *        The userMembership will be updated\n     * @param descriptor\n     *        The update description\n     * @throws SIdentityException\n     */\n    void updateUserMembership(SUserMembership userMembership, EntityUpdateDescriptor descriptor)\n            throws SIdentityException;\n\n    /**\n     * Delete the specific user\n     *\n     * @param user\n     *        The user will be deleted\n     * @throws SUserDeletionException\n     */\n    void deleteUser(SUser user) throws SUserDeletionException;\n\n    /**\n     * Delete user by its id\n     *\n     * @param userId\n     *        The identifier of user\n     * @throws SUserDeletionException\n     */\n    void deleteUser(long userId) throws SUserDeletionException;\n\n    /**\n     * Delete all users for the connected tenant\n     *\n     * @throws SUserDeletionException\n     * @since 6.1\n     */\n    void deleteAllUsers() throws SUserDeletionException;\n\n    /**\n     * Delete the specific custom user info\n     *\n     * @param metadataDefinition\n     *        The custom user info object will be deleted\n     * @throws SIdentityException\n     */\n    void deleteCustomUserInfoDefinition(SCustomUserInfoDefinition metadataDefinition) throws SIdentityException;\n\n    /**\n     * Delete the id specified custom user info\n     *\n     * @param customUserInfoDefinitionId\n     *        The identifier of custom user info\n     * @throws SIdentityException\n     */\n    void deleteCustomUserInfoDefinition(long customUserInfoDefinitionId) throws SIdentityException;\n\n    /**\n     * Delete the specific profileMetadataValue\n     *\n     * @param customUserInfo\n     *        The profileMetadataValue object will be deleted\n     * @throws SIdentityException\n     */\n    void deleteCustomUserInfoValue(SCustomUserInfoValue customUserInfo) throws SIdentityException;\n\n    /**\n     * Delete the id specified profileMetadataValue\n     *\n     * @param customUserInfoValueId\n     *        The identifier of profileMetadataValue\n     * @throws SIdentityException\n     */\n    void deleteCustomUserInfoValue(long customUserInfoValueId) throws SIdentityException;\n\n    /**\n     * Delete the specific role\n     *\n     * @param role\n     *        The role will be deleted\n     * @throws SRoleDeletionException\n     */\n    void deleteRole(SRole role) throws SRoleDeletionException;\n\n    /**\n     * Delete the id specified role\n     *\n     * @param roleId\n     *        The role identifier\n     * @throws SRoleNotFoundException\n     *         Error occurs when no role found with the specific roleId\n     * @throws SRoleDeletionException\n     */\n    void deleteRole(long roleId) throws SRoleNotFoundException, SRoleDeletionException;\n\n    /**\n     * Delete all roles for the connected tenant\n     *\n     * @throws SRoleDeletionException\n     * @since 6.1\n     */\n    void deleteAllRoles() throws SRoleDeletionException;\n\n    /**\n     * Delete the specific group\n     *\n     * @param group\n     *        The group will be deleted\n     * @throws SGroupDeletionException\n     */\n    void deleteGroup(SGroup group) throws SGroupDeletionException;\n\n    /**\n     * Delete the id specified group\n     *\n     * @param groupId\n     *        The identifier of group\n     * @throws SGroupNotFoundException\n     *         Error occurs when no group found with the specific groupId\n     * @throws SGroupDeletionException\n     */\n    void deleteGroup(long groupId) throws SGroupNotFoundException, SGroupDeletionException;\n\n    /**\n     * Delete all groups for the connected tenant\n     *\n     * @throws SGroupDeletionException\n     * @since 6.1\n     */\n    void deleteAllGroups() throws SGroupDeletionException;\n\n    /**\n     * Delete the specific userMembership\n     *\n     * @param userMembership\n     *        The userMembership will be deleted\n     * @throws SMembershipDeletionException\n     */\n    void deleteUserMembership(SUserMembership userMembership) throws SMembershipDeletionException;\n\n    /**\n     * Delete the specific light userMembership\n     *\n     * @param userMembership\n     * @throws SMembershipDeletionException\n     * @since 6.1\n     */\n    void deleteLightUserMembership(SUserMembership userMembership) throws SMembershipDeletionException;\n\n    /**\n     * Delete the id specified userMembership\n     *\n     * @param userMembershipId\n     *        The identifier of userMembership\n     * @throws SMembershipDeletionException\n     */\n    void deleteUserMembership(long userMembershipId) throws SMembershipDeletionException;\n\n    /**\n     * Delete all user memberships for the connected tenant\n     *\n     * @throws SMembershipDeletionException\n     * @since 6.1\n     */\n    void deleteAllUserMemberships() throws SMembershipDeletionException;\n\n    /**\n     * Get total number of users according to specific query options\n     *\n     * @param options\n     *        The QueryOptions object containing some query conditions\n     * @return the satisfied user number\n     * @throws SBonitaReadException\n     */\n    long getNumberOfUsers(QueryOptions options) throws SBonitaReadException;\n\n    /**\n     * Search users according to specific query options\n     *\n     * @param options\n     *        The QueryOptions object containing some query conditions\n     * @return a list of SUser objects\n     * @throws SBonitaReadException\n     */\n    List<SUser> searchUsers(QueryOptions options) throws SBonitaReadException;\n\n    /**\n     * Get total number of roles according to specific query options\n     *\n     * @param options\n     *        The QueryOptions object containing some query conditions\n     * @return the satisfied role number\n     * @throws SBonitaReadException\n     */\n    long getNumberOfRoles(QueryOptions options) throws SBonitaReadException;\n\n    /**\n     * Search roles according to specific query options\n     *\n     * @param options\n     *        The QueryOptions object containing some query conditions\n     * @return a list of SRole objects\n     * @throws SBonitaReadException\n     */\n    List<SRole> searchRoles(QueryOptions options) throws SBonitaReadException;\n\n    /**\n     * Get total number of groups according to specific query options\n     *\n     * @param options\n     *        The QueryOptions object containing some query conditions\n     * @return the group number\n     * @throws SBonitaReadException\n     */\n    long getNumberOfGroups(QueryOptions options) throws SBonitaReadException;\n\n    /**\n     * Search groups according to specific query options\n     *\n     * @param options\n     *        The QueryOptions object containing some query conditions\n     * @return a list of SGroup objects\n     * @throws SBonitaReadException\n     */\n    List<SGroup> searchGroups(QueryOptions options) throws SBonitaReadException;\n\n    /**\n     * Get total number of userMemberships contains specific group and role\n     *\n     * @param groupId\n     *        The identifier of group\n     * @param roleId\n     *        The identifier of role\n     * @return the number of userMemberships\n     * @throws SIdentityException\n     */\n    long getNumberOfUsersByMembership(long groupId, long roleId) throws SIdentityException;\n\n    /**\n     * Get light userMembership by its id\n     *\n     * @param userMembershipId\n     *        The identifier of userMembership\n     * @return a SUserMembership object without userName, groupName and roleName\n     * @throws SIdentityException\n     */\n    SUserMembership getLightUserMembership(long userMembershipId) throws SIdentityException;\n\n    /**\n     * Get light userMembership in a specific interval, this is used for pagination\n     *\n     * @param startIndex\n     *        Index of the record to be retrieved from. First record has index 0\n     * @param numberOfElements\n     *        Number of result we want to get. Maximum number of result returned.\n     * @return a list of SUserMembership objects without userName, groupName and roleName\n     * @throws SIdentityException\n     */\n    List<SUserMembership> getLightUserMemberships(int startIndex, int numberOfElements) throws SIdentityException;\n\n    /**\n     * delete children groups of the given group if there is some\n     *\n     * @param groupId\n     *        The index of the group to delete\n     * @throws SGroupDeletionException\n     * @throws SGroupNotFoundException\n     */\n    List<Long> deleteChildrenGroup(long groupId) throws SGroupDeletionException, SGroupNotFoundException;\n\n    /**\n     * Return the user contact info for a specific user.\n     *\n     * @param userId\n     *        the ID of the user to retrieve the contact info from\n     * @param isPersonal\n     *        Do we want personal contact information (or professional) ?\n     * @return the corresponding SContactInfo, if found\n     * @throws SIdentityException\n     *         if a Read problem occurred\n     */\n    SContactInfo getUserContactInfo(long userId, boolean isPersonal) throws SIdentityException;\n\n    /**\n     * Create user contact information for given data\n     *\n     * @param contactInfo\n     *        The user contact information object\n     * @return The contact info created\n     * @throws SUserCreationException\n     */\n    SContactInfo createUserContactInfo(SContactInfo contactInfo) throws SUserCreationException;\n\n    /**\n     * Update user contact information according to the descriptor\n     *\n     * @param contactInfo\n     *        The user contact information to be updated\n     * @param descriptor\n     *        The update description\n     * @throws SUserUpdateException\n     */\n    void updateUserContactInfo(SContactInfo contactInfo, EntityUpdateDescriptor descriptor) throws SIdentityException;\n\n    /**\n     * Create user in DB for given user and\n     *\n     * @param user\n     *        The user object\n     * @throws SUserCreationException\n     */\n    @Deprecated\n    SUser createUserWithoutEncryptingPassword(SUser user) throws SUserCreationException;\n\n    /**\n     * Update the user having id userId according to the entity update descriptors in parameters\n     *\n     * @param userId\n     *        the user to update\n     * @param userUpdateDescriptor\n     *        the entity update descriptor of the user itself\n     * @param personalDataUpdateDescriptor\n     *        the entity update descriptor for the personal data\n     * @param professionalDataUpdateDescriptor\n     *        the entity update descriptor for the professional data\n     * @return the updated user\n     * @throws SIdentityException\n     */\n    SUser updateUser(long userId, EntityUpdateDescriptor userUpdateDescriptor,\n            EntityUpdateDescriptor personalDataUpdateDescriptor,\n            EntityUpdateDescriptor professionalDataUpdateDescriptor, EntityUpdateDescriptor iconUpdater)\n            throws SIdentityException;\n\n    /**\n     * create a new user in database along with its contact data\n     *\n     * @param sUser\n     *        the user to persist\n     * @param personalContactInfo\n     *        its personal contact info\n     * @param proContactInfo\n     *        its professional contact info\n     * @param iconFilename\n     *        original name of the icon, used to determine the mime type\n     * @param iconContent\n     *        content of the icon\n     * @return the user persisted\n     * @throws SUserCreationException\n     */\n    SUser createUser(SUser sUser, SContactInfo personalContactInfo, SContactInfo proContactInfo, String iconFilename,\n            byte[] iconContent)\n            throws SUserCreationException;\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SCustomUserInfoDefinitionAlreadyExistsException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\n/**\n * Identity related exception indicating that the {@link SCustomUserInfoDefinition} already exists\n *\n * @author Elias Ricken de Medeiros\n */\npublic class SCustomUserInfoDefinitionAlreadyExistsException extends SIdentityException {\n\n    private static final long serialVersionUID = 1061806810101480038L;\n\n    private final String definitionName;\n\n    /**\n     * creates a new instance with a given definition Name that already exists\n     *\n     * @param definitionName the definition name that already exists\n     */\n    public SCustomUserInfoDefinitionAlreadyExistsException(final String definitionName) {\n        super(\"A custom user info definition already exists with name '\" + definitionName + \"'\");\n        this.definitionName = definitionName;\n    }\n\n    /**\n     * @return the definition name that already exists\n     */\n    public String getDefinitionName() {\n        return definitionName;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SCustomUserInfoDefinitionCreationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SCustomUserInfoDefinitionCreationException extends SIdentityException {\n\n    private static final long serialVersionUID = -429177983510812485L;\n\n    private String definitionName;\n\n    public SCustomUserInfoDefinitionCreationException(String definitionName, Throwable cause) {\n        super(\"Unable to create cutom user info definition with name '\" + definitionName + \"'\", cause);\n        this.definitionName = definitionName;\n    }\n\n    public String getDefinitionName() {\n        return definitionName;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SCustomUserInfoDefinitionNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SCustomUserInfoDefinitionNotFoundException extends SIdentityException {\n\n    private static final long serialVersionUID = 1410274244862871978L;\n\n    private String definitionName;\n\n    public SCustomUserInfoDefinitionNotFoundException(String definitionName) {\n        super(\"No custom user info value found with name '\" + definitionName + \"'\");\n        this.definitionName = definitionName;\n    }\n\n    public String getDefinitionName() {\n        return definitionName;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SCustomUserInfoDefinitionReadException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SCustomUserInfoDefinitionReadException extends SIdentityException {\n\n    private static final long serialVersionUID = -674750082481752577L;\n\n    private String definitionName;\n\n    public SCustomUserInfoDefinitionReadException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SCustomUserInfoDefinitionReadException(String definitionName, final Throwable cause) {\n        super(\"Can't get the custom user info definition with name \" + definitionName, cause);\n        this.definitionName = definitionName;\n    }\n\n    public String getDefinitionName() {\n        return definitionName;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SCustomUserInfoValueNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SCustomUserInfoValueNotFoundException extends SIdentityException {\n\n    private static final long serialVersionUID = 2269304406690155075L;\n\n    public SCustomUserInfoValueNotFoundException(long id) {\n        super(\"No custom user info value found with id '\" + id + \"'\");\n    }\n\n    public SCustomUserInfoValueNotFoundException(long definitionId, long userId) {\n        super(\"No custom user info value found with definition id '\" + definitionId + \"' and user id '\" + userId + \"'\");\n    }\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SCustomUserInfoValueReadException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SCustomUserInfoValueReadException extends SIdentityException {\n\n    private static final long serialVersionUID = -9066126826360443609L;\n\n    public SCustomUserInfoValueReadException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SGroupCreationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SGroupCreationException extends SIdentityException {\n\n    private static final long serialVersionUID = -5737090607568879160L;\n\n    public SGroupCreationException(final String message) {\n        super(message);\n    }\n\n    public SGroupCreationException(final Throwable e) {\n        super(e);\n    }\n\n    public SGroupCreationException(final String message, final Throwable e) {\n        super(message, e);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SGroupDeletionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SGroupDeletionException extends SIdentityException {\n\n    private static final long serialVersionUID = -5737090607568879160L;\n\n    public SGroupDeletionException(final String message) {\n        super(message);\n    }\n\n    public SGroupDeletionException(final Throwable e) {\n        super(e);\n    }\n\n    public SGroupDeletionException(final String message, final Throwable e) {\n        super(message, e);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SGroupNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SGroupNotFoundException extends SIdentityException {\n\n    private static final long serialVersionUID = -184772534030377163L;\n\n    public SGroupNotFoundException(final String message) {\n        super(message);\n    }\n\n    public SGroupNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SGroupNotFoundException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SIcon.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.hibernate.annotations.Type;\n\n/**\n * @author Baptiste Mesta\n */\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\n@Entity\n@Table(name = \"icon\")\npublic class SIcon implements PersistentObject {\n\n    @Id\n    private long id;\n    @Column(name = \"mimetype\")\n    private String mimeType;\n    @Type(type = \"materialized_blob\")\n    @Column(name = \"content\")\n    private byte[] content;\n\n    public SIcon(String mimeType, byte[] content) {\n        this.mimeType = mimeType;\n        this.content = content;\n    }\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SIdentityException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * Exception related to the identity module\n *\n * @author Baptiste Mesta\n */\npublic class SIdentityException extends SBonitaException {\n\n    private static final long serialVersionUID = 7615655279956204016L;\n\n    /**\n     * @see SBonitaException#SBonitaException(Object...)\n     */\n    public SIdentityException(final Object... parameters) {\n        super(parameters);\n    }\n\n    /**\n     * @see SBonitaException#SBonitaException(Throwable)\n     */\n    public SIdentityException(final Throwable cause) {\n        super(cause);\n    }\n\n    /**\n     * @see SBonitaException#SBonitaException(String, Throwable)\n     */\n    public SIdentityException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    /**\n     * @see SBonitaException#SBonitaException(String)\n     */\n    public SIdentityException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SMembershipCreationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SMembershipCreationException extends SIdentityException {\n\n    private static final long serialVersionUID = -5737090607568879160L;\n\n    public SMembershipCreationException(final String message) {\n        super(message);\n    }\n\n    public SMembershipCreationException(final Throwable e) {\n        super(e);\n    }\n\n    public SMembershipCreationException(final String message, final Throwable e) {\n        super(message, e);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SMembershipDeletionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SMembershipDeletionException extends SIdentityException {\n\n    private static final long serialVersionUID = -5737090607568879160L;\n\n    public SMembershipDeletionException(final String message) {\n        super(message);\n    }\n\n    public SMembershipDeletionException(final Throwable e) {\n        super(e);\n    }\n\n    public SMembershipDeletionException(final String message, final Throwable e) {\n        super(message, e);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SRoleDeletionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SRoleDeletionException extends SIdentityException {\n\n    private static final long serialVersionUID = -2601746060361773240L;\n\n    public SRoleDeletionException(final String message) {\n        super(message);\n    }\n\n    public SRoleDeletionException(final Throwable e) {\n        super(e);\n    }\n\n    public SRoleDeletionException(final String message, final Throwable e) {\n        super(message, e);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SRoleNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SRoleNotFoundException extends SIdentityException {\n\n    private static final long serialVersionUID = -2601746060361773240L;\n\n    public SRoleNotFoundException(final String message) {\n        super(message);\n    }\n\n    public SRoleNotFoundException(final Throwable e) {\n        super(e);\n    }\n\n    public SRoleNotFoundException(final String message, final Throwable e) {\n        super(message, e);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SUserCreationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SUserCreationException extends SIdentityException {\n\n    private static final long serialVersionUID = -236864152193578968L;\n\n    public SUserCreationException(final String message) {\n        super(message);\n    }\n\n    public SUserCreationException(final Throwable e) {\n        super(e);\n    }\n\n    public SUserCreationException(final String message, final Throwable e) {\n        super(message, e);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SUserDeletionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SUserDeletionException extends SIdentityException {\n\n    private static final long serialVersionUID = -788582886409946761L;\n\n    public SUserDeletionException(final String message) {\n        super(message);\n    }\n\n    public SUserDeletionException(final Throwable e) {\n        super(e);\n    }\n\n    public SUserDeletionException(final String message, final Throwable e) {\n        super(message, e);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SUserMembershipCreationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SUserMembershipCreationException extends SIdentityException {\n\n    private static final long serialVersionUID = 543585701760706593L;\n\n    public SUserMembershipCreationException(final String message) {\n        super(message);\n    }\n\n    public SUserMembershipCreationException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SUserMembershipCreationException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SUserMembershipNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SUserMembershipNotFoundException extends SIdentityException {\n\n    private static final long serialVersionUID = -4179049965591664132L;\n\n    public SUserMembershipNotFoundException(final String message) {\n        super(message);\n    }\n\n    public SUserMembershipNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SUserMembershipNotFoundException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SUserNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport org.bonitasoft.engine.identity.model.SUser;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class SUserNotFoundException extends SIdentityException {\n\n    private static final long serialVersionUID = -8190385127379005323L;\n\n    public SUserNotFoundException(final SUser user) {\n        this(user.getUserName());\n        setUserIdOnContext(user.getId());\n    }\n\n    public SUserNotFoundException(final long userId) {\n        super(\"Can't find the user\");\n        setUserIdOnContext(userId);\n    }\n\n    public SUserNotFoundException(final String userName) {\n        super(\"?\", userName);\n    }\n\n    public SUserNotFoundException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SUserNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SUserUpdateException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SUserUpdateException extends SIdentityException {\n\n    private static final long serialVersionUID = 618893893652667252L;\n\n    public SUserUpdateException(final String message) {\n        super(message);\n    }\n\n    public SUserUpdateException(final Throwable e) {\n        super(e);\n    }\n\n    public SUserUpdateException(final String message, final Throwable e) {\n        super(message, e);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/impl/CredentialsEncrypter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.impl;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface CredentialsEncrypter {\n\n    String hash(final String password);\n\n    boolean check(final String password, final String hashPassword);\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/impl/IdentityServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.impl;\n\nimport java.util.*;\n\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.identity.*;\nimport org.bonitasoft.engine.identity.model.*;\nimport org.bonitasoft.engine.identity.model.builder.*;\nimport org.bonitasoft.engine.identity.recorder.SelectDescriptorBuilder;\nimport org.bonitasoft.engine.persistence.*;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity;\nimport org.bonitasoft.engine.queriablelogger.model.builder.ActionType;\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.*;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\nimport org.springframework.stereotype.Service;\n\n/**\n * Default implementation of the Identity service\n *\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Bole Zhang\n * @author Hongwen Zang\n * @author Celine Souchet\n */\n@Service(\"identityService\")\npublic class IdentityServiceImpl implements IdentityService {\n\n    private final ReadPersistenceService persistenceService;\n    private final Recorder recorder;\n    private final QueriableLoggerService queriableLoggerService;\n    private final CredentialsEncrypter encrypter;\n    private final IconService iconService;\n\n    public IdentityServiceImpl(ReadPersistenceService persistenceService, Recorder recorder,\n            QueriableLoggerService queriableLoggerService,\n            CredentialsEncrypter encrypter, IconService iconService) {\n        this.persistenceService = persistenceService;\n        this.recorder = recorder;\n        this.queriableLoggerService = queriableLoggerService;\n        this.encrypter = encrypter;\n        this.iconService = iconService;\n    }\n\n    @Override\n    public void createGroup(final SGroup group, String iconFileName, byte[] iconContent)\n            throws SGroupCreationException {\n        final String methodName = \"createGroup\";\n        final long objectId = group.getId();\n        final SGroupLogBuilder logBuilder = getGroupLog(ActionType.CREATED,\n                \"Adding a new group with name \" + group.getName());\n        try {\n            if (iconFileName != null && iconContent != null) {\n                SIcon icon = iconService.createIcon(iconFileName, iconContent);\n                group.setIconId(icon.getId());\n            }\n            final InsertRecord insertRecord = new InsertRecord(group);\n            recorder.recordInsert(insertRecord, GROUP);\n            final int status = SQueriableLog.STATUS_OK;\n            log(insertRecord.getEntity().getId(), status, logBuilder, methodName);\n        } catch (final SRecorderException re) {\n            final int status = SQueriableLog.STATUS_FAIL;\n            log(objectId, status, logBuilder, methodName);\n            throw new SGroupCreationException(re);\n        }\n    }\n\n    @Override\n    public SCustomUserInfoDefinition createCustomUserInfoDefinition(final SCustomUserInfoDefinition customUserInfo)\n            throws SCustomUserInfoDefinitionAlreadyExistsException, SCustomUserInfoDefinitionCreationException {\n        final String methodName = \"createCustomUserInfoDefinition\";\n        final SCustomUserInfoDefinitionLogBuilder logBuilder = getSCustomUserInfoDefinitionLog(ActionType.CREATED,\n                \"Adding a custom user info with name \"\n                        + customUserInfo.getName());\n        try {\n            throwExceptionIfAlreadyExists(customUserInfo);\n            recorder.recordInsert(new InsertRecord(customUserInfo), CUSTOM_USER_INFO_DEFINITION);\n            log(customUserInfo.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName);\n            return customUserInfo;\n        } catch (final SRecorderException | SBonitaReadException e) {\n            throw handleCustomUserInfoDefinitionCreationFailure(customUserInfo, methodName, logBuilder, e);\n        }\n    }\n\n    private void throwExceptionIfAlreadyExists(final SCustomUserInfoDefinition customUserInfo)\n            throws SBonitaReadException,\n            SCustomUserInfoDefinitionAlreadyExistsException {\n        final SCustomUserInfoDefinition storedDef = getCustomUserInfoDefinitionWithoutCheck(customUserInfo.getName());\n        if (storedDef != null) {\n            throw new SCustomUserInfoDefinitionAlreadyExistsException(customUserInfo.getName());\n        }\n    }\n\n    private SCustomUserInfoDefinitionCreationException handleCustomUserInfoDefinitionCreationFailure(\n            final SCustomUserInfoDefinition customUserInfo,\n            final String methodName, final SCustomUserInfoDefinitionLogBuilder logBuilder,\n            final SBonitaException exception) {\n        log(customUserInfo.getId(), SQueriableLog.STATUS_FAIL, logBuilder, methodName);\n        return new SCustomUserInfoDefinitionCreationException(customUserInfo.getName(), exception);\n    }\n\n    @Override\n    public SCustomUserInfoValue createCustomUserInfoValue(final SCustomUserInfoValue customUserInfo)\n            throws SIdentityException {\n        try {\n            recorder.recordInsert(new InsertRecord(customUserInfo), CUSTOM_USER_INFO_VALUE);\n            return customUserInfo;\n        } catch (final SRecorderException e) {\n            throw new SIdentityException(\"Can't add custom user info value \" + customUserInfo, e);\n        }\n    }\n\n    @Override\n    public void createRole(final SRole role, String iconFilename, byte[] iconContent) throws SIdentityException {\n        final String methodName = \"createRole\";\n        final SRoleLogBuilder logBuilder = getRoleLog(ActionType.CREATED,\n                \"Adding a new role with name \" + role.getName());\n        try {\n            if (iconFilename != null && iconContent != null) {\n                SIcon icon = iconService.createIcon(iconFilename, iconContent);\n                role.setIconId(icon.getId());\n            }\n            recorder.recordInsert(new InsertRecord(role), ROLE);\n            log(role.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName);\n        } catch (final SRecorderException e) {\n            log(role.getId(), SQueriableLog.STATUS_FAIL, logBuilder, methodName);\n            throw new SIdentityException(\"Can't add role \" + role, e);\n        }\n    }\n\n    @Override\n    public SUser createUser(final SUser user) throws SUserCreationException {\n        final String methodName = \"createUser\";\n        final String hash = encrypter.hash(user.getPassword());\n        final SUser hashedUser = new SUser(user).toBuilder().password(hash).build();\n        return createUser(user, methodName, hashedUser);\n    }\n\n    @Override\n    @Deprecated\n    public SUser createUserWithoutEncryptingPassword(final SUser user) throws SUserCreationException {\n        final String methodName = \"createUserWithoutEncryptingPassword\";\n        final SUser hashedUser = new SUser(user).toBuilder().build();\n        return createUser(user, methodName, hashedUser);\n    }\n\n    private SUser createUser(final SUser user, final String methodName, final SUser hashedUser)\n            throws SUserCreationException {\n        final String message = \"Adding a new user with user name \" + user.getUserName() + \", first name \"\n                + user.getFirstName() + \", last name \"\n                + user.getLastName();\n        final SUserLogBuilder logBuilder = getUserLog(ActionType.CREATED, message);\n        try {\n            insertUser(hashedUser);\n            insertUserLogin(methodName, hashedUser, logBuilder);\n            return hashedUser;\n        } catch (final SRecorderException re) {\n            log(hashedUser.getId(), SQueriableLog.STATUS_FAIL, logBuilder, methodName);\n            throw new SUserCreationException(re);\n        }\n    }\n\n    private void insertUserLogin(String methodName, SUser hashedUser, SUserLogBuilder logBuilder)\n            throws SRecorderException {\n        SUserLogin sUserLogin = new SUserLogin();\n        hashedUser.setSUserLogin(sUserLogin);\n        sUserLogin.setSUser(hashedUser);\n        sUserLogin.setId(hashedUser.getId());\n        recorder.recordInsert(new InsertRecord(sUserLogin), USER_LOGIN);\n        log(hashedUser.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName);\n    }\n\n    private void insertUser(SUser hashedUser) throws SRecorderException {\n        final InsertRecord insertRecord = new InsertRecord(hashedUser);\n        recorder.recordInsert(insertRecord, USER);\n    }\n\n    @Override\n    public SContactInfo createUserContactInfo(final SContactInfo contactInfo) throws SUserCreationException {\n        final String methodName = \"createUserContactInfo\";\n        final String message = \"Adding a new user contact information for user with id \" + contactInfo.getUserId();\n        final SContactInfoLogBuilder logBuilder = getUserContactInfoLog(ActionType.CREATED, message, contactInfo);\n        try {\n            recorder.recordInsert(new InsertRecord(contactInfo), USER_CONTACT_INFO);\n            log(contactInfo.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName);\n            return contactInfo;\n        } catch (final SRecorderException re) {\n            log(contactInfo.getId(), SQueriableLog.STATUS_FAIL, logBuilder, methodName);\n            throw new SUserCreationException(re);\n        }\n    }\n\n    @Override\n    public void createUserMembership(final SUserMembership userMembership) throws SUserMembershipCreationException {\n        final String methodName = \"createUserMembership\";\n        final String message = \"Adding a new user membership for user \" + userMembership.getUsername() + \" with role \"\n                + userMembership.getRoleName()\n                + \" in group \" + userMembership.getGroupName();\n\n        final SUserMembershipLogBuilder logBuilder = getUserMembershipLog(ActionType.CREATED, message, userMembership);\n        try {\n            recorder.recordInsert(new InsertRecord(userMembership), USERMEMBERSHIP);\n            log(userMembership.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName);\n        } catch (final SRecorderException re) {\n            log(userMembership.getId(), SQueriableLog.STATUS_FAIL, logBuilder, methodName);\n            throw new SUserMembershipCreationException(re);\n        }\n    }\n\n    @Override\n    public void deleteGroup(final long groupId) throws SGroupNotFoundException, SGroupDeletionException {\n        final SGroup group = getGroup(groupId);\n        this.deleteGroup(group);\n    }\n\n    @Override\n    public void deleteGroup(final SGroup group) throws SGroupDeletionException {\n        final SGroupLogBuilder logBuilder = getGroupLog(ActionType.DELETED, \"Deleting group \" + group.getName());\n        try {\n            if (group.getIconId() != null) {\n                iconService.deleteIcon(group.getIconId());\n            }\n            recorder.recordDelete(new DeleteRecord(group), GROUP);\n            log(group.getId(), SQueriableLog.STATUS_OK, logBuilder, \"deleteGroup\");\n        } catch (final SRecorderException | SBonitaReadException re) {\n            log(group.getId(), SQueriableLog.STATUS_FAIL, logBuilder, \"deleteGroup\");\n            throw new SGroupDeletionException(re);\n        }\n    }\n\n    @Override\n    public void deleteAllGroups() throws SGroupDeletionException {\n        try {\n            final DeleteAllRecord record = new DeleteAllRecord(SGroup.class, null);\n            recorder.recordDeleteAll(record);\n        } catch (final SRecorderException e) {\n            throw new SGroupDeletionException(\"Can't delete all groups.\", e);\n        }\n    }\n\n    @Override\n    public List<Long> deleteChildrenGroup(final long groupId) throws SGroupDeletionException, SGroupNotFoundException {\n        final ArrayList<Long> deletedGroups = new ArrayList<>();\n        try {\n            List<SGroup> childrenGroup;\n            final int nbGroup = 20;\n            while (!(childrenGroup = getGroupChildren(groupId, 0, nbGroup)).isEmpty()) {\n                for (final SGroup sGroup : childrenGroup) {\n                    deletedGroups.addAll(deleteChildrenGroup(sGroup.getId()));\n                    deletedGroups.add(sGroup.getId());\n                    deleteGroup(sGroup);\n                }\n            }\n        } catch (final SGroupNotFoundException e) {\n            throw e;\n        } catch (final SIdentityException e) {\n            throw new SGroupDeletionException(e);\n        }\n        return deletedGroups;\n    }\n\n    private <T extends SLogBuilder> void initializeLogBuilder(final T logBuilder, final String message) {\n        logBuilder.actionStatus(SQueriableLog.STATUS_FAIL).severity(SQueriableLogSeverity.INTERNAL).rawMessage(message);\n    }\n\n    private <T extends HasCRUDEAction> void updateLog(final ActionType actionType, final T logBuilder) {\n        logBuilder.setActionType(actionType);\n    }\n\n    private SGroupLogBuilder getGroupLog(final ActionType actionType, final String message) {\n        final SGroupLogBuilder logBuilder = BuilderFactory.get(SGroupLogBuilderFactory.class).createNewInstance();\n        this.initializeLogBuilder(logBuilder, message);\n        this.updateLog(actionType, logBuilder);\n        return logBuilder;\n    }\n\n    private SRoleLogBuilder getRoleLog(final ActionType actionType, final String message) {\n        final SRoleLogBuilder logBuilder = BuilderFactory.get(SRoleLogBuilderFactory.class).createNewInstance();\n        this.initializeLogBuilder(logBuilder, message);\n        this.updateLog(actionType, logBuilder);\n        return logBuilder;\n    }\n\n    private SCustomUserInfoDefinitionLogBuilder getSCustomUserInfoDefinitionLog(final ActionType actionType,\n            final String message) {\n        final SCustomUserInfoDefinitionLogBuilder logBuilder = BuilderFactory\n                .get(SCustomUserInfoDefinitionLogBuilderFactory.class).createNewInstance();\n        this.initializeLogBuilder(logBuilder, message);\n        this.updateLog(actionType, logBuilder);\n        return logBuilder;\n    }\n\n    SUserLogBuilder getUserLog(final ActionType actionType, final String message) {\n        final SUserLogBuilder logBuilder = BuilderFactory.get(SUserLogBuilderFactory.class).createNewInstance();\n        this.initializeLogBuilder(logBuilder, message);\n        this.updateLog(actionType, logBuilder);\n        return logBuilder;\n    }\n\n    private SContactInfoLogBuilder getUserContactInfoLog(final ActionType actionType, final String message,\n            final SContactInfo contactInfo) {\n        final SContactInfoLogBuilder logBuilder = BuilderFactory.get(SContactInfoLogBuilderFactory.class)\n                .createNewInstance();\n        this.initializeLogBuilder(logBuilder, message);\n        this.updateLog(actionType, logBuilder);\n        logBuilder.setContactInfoUserId(contactInfo.getUserId());\n        return logBuilder;\n    }\n\n    private SUserMembershipLogBuilder getUserMembershipLog(final ActionType actionType, final String message,\n            final SUserMembership userMemberShip) {\n        final SUserMembershipLogBuilder logBuilder = BuilderFactory.get(SUserMembershipLogBuilderFactory.class)\n                .createNewInstance();\n        this.initializeLogBuilder(logBuilder, message);\n        this.updateLog(actionType, logBuilder);\n        logBuilder.identityUserId(userMemberShip.getUserId());\n        logBuilder.roleID(userMemberShip.getRoleId());\n        logBuilder.groupId(userMemberShip.getGroupId());\n        return logBuilder;\n    }\n\n    @Override\n    public void deleteCustomUserInfoDefinition(final long customUserInfoDefinitionId) throws SIdentityException {\n        this.deleteCustomUserInfoDefinition(getCustomUserInfoDefinition(customUserInfoDefinitionId));\n    }\n\n    @Override\n    public void deleteCustomUserInfoDefinition(final SCustomUserInfoDefinition info) throws SIdentityException {\n        final String methodName = \"deleteCustomUserInfoDefinition\";\n        final SCustomUserInfoDefinitionLogBuilder logBuilder = getSCustomUserInfoDefinitionLog(ActionType.DELETED,\n                \"Deleting profile custom user info definition with name \" + info.getName());\n        try {\n            recorder.recordDelete(new DeleteRecord(info), CUSTOM_USER_INFO_DEFINITION);\n            log(info.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName);\n        } catch (final SRecorderException e) {\n            log(info.getId(), SQueriableLog.STATUS_FAIL, logBuilder, methodName);\n            throw new SIdentityException(\"Can't delete profile custom user info definition \" + info, e);\n        }\n    }\n\n    @Override\n    public void deleteCustomUserInfoValue(final long customUserInfoValueId) throws SIdentityException {\n        this.deleteCustomUserInfoValue(getCustomUserInfoValue(customUserInfoValueId));\n    }\n\n    @Override\n    public void deleteCustomUserInfoValue(final SCustomUserInfoValue customUserInfo) throws SIdentityException {\n        try {\n            recorder.recordDelete(new DeleteRecord(customUserInfo), CUSTOM_USER_INFO_VALUE);\n        } catch (final SRecorderException e) {\n            throw new SIdentityException(\"Can't delete custom user info value\" + customUserInfo, e);\n        }\n    }\n\n    @Override\n    public void deleteRole(final long roleId) throws SRoleNotFoundException, SRoleDeletionException {\n        final SRole role = getRole(roleId);\n        this.deleteRole(role);\n    }\n\n    @Override\n    public void deleteRole(final SRole role) throws SRoleDeletionException {\n        final String methodName = \"deleteRole\";\n        final SRoleLogBuilder logBuilder = getRoleLog(ActionType.DELETED, \"Deleting role with name \" + role.getName());\n        try {\n            if (role.getIconId() != null) {\n                iconService.deleteIcon(role.getIconId());\n            }\n            recorder.recordDelete(new DeleteRecord(role), ROLE);\n            log(role.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName);\n        } catch (final SRecorderException | SBonitaReadException re) {\n            log(role.getId(), SQueriableLog.STATUS_FAIL, logBuilder, methodName);\n            throw new SRoleDeletionException(re);\n        }\n    }\n\n    @Override\n    public void deleteAllRoles() throws SRoleDeletionException {\n        try {\n            final DeleteAllRecord record = new DeleteAllRecord(SRole.class, null);\n            recorder.recordDeleteAll(record);\n        } catch (final SRecorderException e) {\n            throw new SRoleDeletionException(\"Can't delete all roles.\", e);\n        }\n    }\n\n    @Override\n    public void deleteUser(final long userId) throws SUserDeletionException {\n        SUser user;\n        try {\n            user = getUser(userId);\n            if (user.getIconId() != null) {\n                iconService.deleteIcon(user.getIconId());\n            }\n            deleteUser(user);\n        } catch (final SUserNotFoundException e) {\n            // ignored, let's switch to the next one\n        } catch (SRecorderException | SBonitaReadException e) {\n            throw new SUserDeletionException(e);\n        }\n    }\n\n    @Override\n    public void deleteUser(final SUser user) throws SUserDeletionException {\n        final String methodName = \"deleteUser\";\n        final SUserLogBuilder logBuilder = getUserLog(ActionType.DELETED,\n                \"Deleting user with username \" + user.getUserName());\n        try {\n            recorder.recordDelete(new DeleteRecord(user), USER);\n            log(user.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName);\n        } catch (final SRecorderException re) {\n            log(user.getId(), SQueriableLog.STATUS_FAIL, logBuilder, methodName);\n            throw new SUserDeletionException(re);\n        }\n    }\n\n    @Override\n    public void deleteAllUsers() throws SUserDeletionException {\n        try {\n            final DeleteAllRecord record = new DeleteAllRecord(SUser.class, null);\n            recorder.recordDeleteAll(record);\n            final DeleteAllRecord record2 = new DeleteAllRecord(SUserLogin.class, null);\n            recorder.recordDeleteAll(record2);\n        } catch (final SRecorderException e) {\n            throw new SUserDeletionException(\"Can't delete all users.\", e);\n        }\n    }\n\n    @Override\n    public SUserMembership getLightUserMembership(final long userMembershipId) throws SIdentityException {\n        try {\n            final SUserMembership selectOne = persistenceService\n                    .selectById(SelectDescriptorBuilder.getLightElementById(SUserMembership.class,\n                            \"SUserMembership\", userMembershipId));\n            if (selectOne == null) {\n                throw new SIdentityException(\"Can't get the userMembership with id \" + userMembershipId, null);\n            }\n            return selectOne;\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the user membership with id \" + userMembershipId, e);\n        }\n    }\n\n    @Override\n    public void deleteUserMembership(SUserMembership userMembership) throws SMembershipDeletionException {\n        try {\n            // fat object, hibernate won't delete id\n            if (userMembership.getGroupName() != null || userMembership.getUsername() != null\n                    || userMembership.getRoleName() != null) {\n                userMembership = getLightUserMembership(userMembership.getId());\n            }\n            deleteLightUserMembership(userMembership);\n        } catch (final SIdentityException e) {\n            throw new SMembershipDeletionException(\"Can't delete membership \" + userMembership, e);\n        }\n    }\n\n    @Override\n    public void deleteUserMembership(final long id) throws SMembershipDeletionException {\n        try {\n            final SUserMembership userMembership = getLightUserMembership(id);\n            deleteLightUserMembership(userMembership);\n        } catch (final SIdentityException e) {\n            throw new SMembershipDeletionException(\"Can't delete membership with id \" + id, e);\n        }\n    }\n\n    @Override\n    public void deleteLightUserMembership(final SUserMembership userMembership) throws SMembershipDeletionException {\n        final String methodName = \"deleteLightUserMembership\";\n        final String message = \"Deleting user membership for user \" + userMembership.getUsername() + \" with role \"\n                + userMembership.getRoleName()\n                + \" in group \" + userMembership.getGroupName();\n        final SUserMembershipLogBuilder logBuilder = getUserMembershipLog(ActionType.DELETED, message, userMembership);\n        try {\n            recorder.recordDelete(new DeleteRecord(userMembership), USERMEMBERSHIP);\n            log(userMembership.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName);\n        } catch (final SRecorderException e) {\n            log(userMembership.getId(), SQueriableLog.STATUS_FAIL, logBuilder, methodName);\n            throw new SMembershipDeletionException(\"Can't delete membership \" + userMembership, e);\n        }\n    }\n\n    @Override\n    public void deleteAllUserMemberships() throws SMembershipDeletionException {\n        try {\n            final DeleteAllRecord record = new DeleteAllRecord(SUserMembership.class, null);\n            recorder.recordDeleteAll(record);\n        } catch (final SRecorderException e) {\n            throw new SMembershipDeletionException(\"Can't delete all user memberships.\", e);\n        }\n    }\n\n    @Override\n    public SGroup getGroup(final long groupId) throws SGroupNotFoundException {\n        try {\n            final SGroup group = persistenceService\n                    .selectById(SelectDescriptorBuilder.getElementById(SGroup.class, \"Group\", groupId));\n            if (group == null) {\n                throw new SGroupNotFoundException(\"No group exists with id: \" + groupId);\n            }\n            return group;\n        } catch (final SBonitaReadException bre) {\n            throw new SGroupNotFoundException(bre);\n        }\n    }\n\n    @Override\n    public List<SGroup> getGroupChildren(final long groupId, final int fromIndex, final int numberOfGroups)\n            throws SIdentityException {\n        try {\n            final SGroup group = getGroup(groupId);\n            return persistenceService\n                    .selectList(SelectDescriptorBuilder.getChildrenOfGroup(group, fromIndex, numberOfGroups));\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the children of the group\", e);\n        }\n    }\n\n    @Override\n    public List<SGroup> getGroupChildren(final long groupId, final int fromIndex, final int numberOfGroups,\n            final String field, final OrderByType order)\n            throws SIdentityException {\n        try {\n            final SGroup group = getGroup(groupId);\n            return persistenceService\n                    .selectList(SelectDescriptorBuilder.getChildrenOfGroup(group, field, order, fromIndex,\n                            numberOfGroups));\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the children of the group\", e);\n        }\n    }\n\n    @Override\n    public List<SGroup> getGroups(final int fromIndex, final int numberOfGroups) throws SIdentityException {\n        try {\n            return persistenceService\n                    .selectList(SelectDescriptorBuilder.getElements(SGroup.class, \"Group\", fromIndex, numberOfGroups));\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the groups\", e);\n        }\n    }\n\n    @Override\n    public List<SGroup> getGroups(final int fromIndex, final int numberOfGroups, final String field,\n            final OrderByType order) throws SIdentityException {\n        try {\n            return persistenceService\n                    .selectList(SelectDescriptorBuilder.getElements(SGroup.class, \"Group\", field, order, fromIndex,\n                            numberOfGroups));\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the groups\", e);\n        }\n    }\n\n    @Override\n    public List<SGroup> getGroups(final List<Long> groupIds) throws SGroupNotFoundException {\n        if (groupIds == null || groupIds.isEmpty()) {\n            return Collections.emptyList();\n        }\n        try {\n            return persistenceService\n                    .selectList(SelectDescriptorBuilder.getElementsByIds(SGroup.class, \"Group\", groupIds));\n        } catch (final SBonitaReadException e) {\n            throw new SGroupNotFoundException(e);\n        }\n    }\n\n    @Override\n    public List<SUserMembership> getUserMemberships(final int fromIndex, final int numberOfResult,\n            final OrderByOption orderByOption)\n            throws SIdentityException {\n        try {\n            List<SUserMembership> listSUserMembership;\n            if (orderByOption.getClazz() == SRole.class) {\n                listSUserMembership = persistenceService\n                        .selectList(SelectDescriptorBuilder.getUserMembershipsWithRole(new QueryOptions(fromIndex,\n                                numberOfResult, Collections.singletonList(orderByOption))));\n            } else if (orderByOption.getClazz() == SGroup.class) {\n                listSUserMembership = persistenceService\n                        .selectList(SelectDescriptorBuilder.getUserMembershipsWithGroup(new QueryOptions(fromIndex,\n                                numberOfResult, Collections.singletonList(orderByOption))));\n            } else {\n                listSUserMembership = persistenceService\n                        .selectList(SelectDescriptorBuilder.getElements(SUserMembership.class, \"UserMembership\",\n                                new QueryOptions(fromIndex, numberOfResult, Collections.singletonList(orderByOption))));\n            }\n            return listSUserMembership;\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the memberships\", e);\n        }\n    }\n\n    @Override\n    public List<SUserMembership> getUserMembershipsOfGroup(final long groupId, final int startIndex,\n            final int maxResults) throws SIdentityException {\n        try {\n            return persistenceService.selectList(SelectDescriptorBuilder.getUserMembershipsByGroup(groupId, startIndex,\n                    maxResults));\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the users memberships the group \" + groupId, e);\n        }\n    }\n\n    @Override\n    public List<SUserMembership> getUserMembershipsOfRole(final long roleId, final int startIndex, final int maxResults)\n            throws SIdentityException {\n        try {\n            return persistenceService.selectList(SelectDescriptorBuilder.getUserMembershipsByRole(roleId, startIndex,\n                    maxResults));\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the memberships having the role \" + roleId, e);\n        }\n    }\n\n    @Override\n    public long getNumberOfGroupChildren(final long parentGroupId) throws SIdentityException {\n        try {\n            final SGroup parentGroup = getGroup(parentGroupId);\n            return persistenceService\n                    .selectOne(SelectDescriptorBuilder.getNumberOfGroupChildren(parentGroup.getPath()));\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the number children of group\", e);\n        }\n    }\n\n    @Override\n    public long getNumberOfGroups() throws SIdentityException {\n        try {\n            return persistenceService.selectOne(SelectDescriptorBuilder.getNumberOfElement(\"SGroup\", SGroup.class));\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the number of group\", e);\n        }\n    }\n\n    @Override\n    public long getNumberOfCustomUserInfoDefinition() throws SIdentityException {\n        try {\n            return persistenceService.selectOne(SelectDescriptorBuilder.getNumberOfElement(\"CustomUserInfoDefinition\",\n                    SCustomUserInfoDefinition.class));\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the number of custom user info\", e);\n        }\n    }\n\n    @Override\n    public long getNumberOfCustomUserInfoValue(final QueryOptions options) throws SBonitaReadException {\n        try {\n            return persistenceService.getNumberOfEntities(SCustomUserInfoValue.class, options, null);\n        } catch (final SBonitaReadException e) {\n            throw new SBonitaReadException(e);\n        }\n    }\n\n    @Override\n    public long getNumberOfRoles() throws SIdentityException {\n        try {\n            return persistenceService.selectOne(SelectDescriptorBuilder.getNumberOfElement(\"SRole\", SRole.class));\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the number of role\", e);\n        }\n    }\n\n    @Override\n    public long getNumberOfUsers() throws SIdentityException {\n        try {\n            return persistenceService.selectOne(SelectDescriptorBuilder.getNumberOfElement(\"SUser\", SUser.class));\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the number of user\", e);\n        }\n    }\n\n    @Override\n    public long getNumberOfUserMembershipsOfUser(final long userId) throws SIdentityException {\n        try {\n            return persistenceService.selectOne(SelectDescriptorBuilder.getNumberOfUserMembershipsOfUser(userId));\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the number of usermemberships having the user \" + userId, e);\n        }\n    }\n\n    @Override\n    public long getNumberOfUsersByGroup(final long groupId) throws SIdentityException {\n        try {\n            return persistenceService.selectOne(SelectDescriptorBuilder.getNumberOfUsersByGroup(groupId));\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the number of users having the group \" + groupId, e);\n        }\n    }\n\n    @Override\n    public long getNumberOfUsersByRole(final long roleId) throws SIdentityException {\n        try {\n            return persistenceService.selectOne(SelectDescriptorBuilder.getNumberOfUsersByRole(roleId));\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the number of users having the role \" + roleId, e);\n        }\n    }\n\n    @Override\n    public long getNumberOfUsersByMembership(final long groupId, final long roleId) throws SIdentityException {\n        try {\n            return persistenceService.selectOne(SelectDescriptorBuilder.getNumberOfUsersByMembership(groupId, roleId));\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\n                    \"Can't get the number of users having the membership with group:\" + groupId + \" and role:\" + roleId,\n                    e);\n        }\n    }\n\n    @Override\n    public SCustomUserInfoDefinition getCustomUserInfoDefinitionByName(final String name)\n            throws SCustomUserInfoDefinitionNotFoundException,\n            SCustomUserInfoDefinitionReadException {\n        SCustomUserInfoDefinition definition;\n        try {\n            definition = getCustomUserInfoDefinitionWithoutCheck(name);\n        } catch (final SBonitaReadException e) {\n            throw new SCustomUserInfoDefinitionReadException(name, e);\n        }\n        if (definition == null) {\n            throw new SCustomUserInfoDefinitionNotFoundException(name);\n        }\n        return definition;\n    }\n\n    @Override\n    public boolean hasCustomUserInfoDefinition(final String name) throws SCustomUserInfoDefinitionReadException {\n        SCustomUserInfoDefinition definition;\n        try {\n            definition = getCustomUserInfoDefinitionWithoutCheck(name);\n        } catch (final SBonitaReadException e) {\n            throw new SCustomUserInfoDefinitionReadException(name, e);\n        }\n        return definition != null;\n    }\n\n    private SCustomUserInfoDefinition getCustomUserInfoDefinitionWithoutCheck(final String name)\n            throws SBonitaReadException {\n        return persistenceService.selectOne(SelectDescriptorBuilder.getCustomUserInfoDefinitionByName(name));\n    }\n\n    @Override\n    public List<SCustomUserInfoDefinition> getCustomUserInfoDefinitions(final int fromIndex, final int maxResults)\n            throws SIdentityException {\n        try {\n            return persistenceService.selectList(SelectDescriptorBuilder.getElements(\n                    SCustomUserInfoDefinition.class, \"CustomUserInfoDefinition\", fromIndex, maxResults));\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the custom user info definitions\", e);\n        }\n    }\n\n    @Override\n    public List<Long> getUserIdsWithCustomUserInfo(final String userInfoName, String userInfoValue,\n            final boolean usePartialMatch, final int fromIndex,\n            final int maxResults) throws SIdentityException {\n        try {\n            String queryName;\n            if (usePartialMatch) {\n                queryName = \"getUserIdsWithCustomUserInfoContains\";\n                userInfoValue = \"%\" + userInfoValue + \"%\";\n            } else {\n                queryName = \"getUserIdsWithCustomUserInfo\";\n            }\n            final Map<String, Object> parameters = new HashMap<>(2);\n            parameters.put(\"userInfoName\", userInfoName);\n            parameters.put(\"userInfoValue\", userInfoValue);\n            final SelectListDescriptor<Long> descriptor = new SelectListDescriptor<>(queryName, parameters, SUser.class,\n                    Long.class,\n                    new QueryOptions(fromIndex, maxResults));\n            return persistenceService.selectList(descriptor);\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the custom user info definitions\", e);\n        }\n    }\n\n    @Override\n    public SCustomUserInfoDefinition getCustomUserInfoDefinition(final long customUserInfoDefinitionId)\n            throws SIdentityException {\n        try {\n            final SCustomUserInfoDefinition selectOne = persistenceService\n                    .selectById(SelectDescriptorBuilder.getElementById(SCustomUserInfoDefinition.class,\n                            \"CustomUserInfoDefinition\", customUserInfoDefinitionId));\n            if (selectOne == null) {\n                throw new SIdentityException(\n                        \"Can't get the custom user info definition with id \" + customUserInfoDefinitionId, null);\n            }\n            return selectOne;\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\n                    \"Can't get the custom user info definition with id \" + customUserInfoDefinitionId, e);\n        }\n    }\n\n    @Override\n    public List<SCustomUserInfoDefinition> getCustomUserInfoDefinitions(final List<Long> customUserInfoDefinitionIds)\n            throws SIdentityException {\n        if (customUserInfoDefinitionIds == null || customUserInfoDefinitionIds.isEmpty()) {\n            return Collections.emptyList();\n        }\n        try {\n            return persistenceService.selectList(SelectDescriptorBuilder.getElementsByIds(\n                    SCustomUserInfoDefinition.class, \"SCustomUserInfoDefinition\", customUserInfoDefinitionIds));\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get custom user info definitions with ids \"\n                    + Arrays.toString(customUserInfoDefinitionIds.toArray()), e);\n        }\n    }\n\n    @Override\n    public SCustomUserInfoValue getCustomUserInfoValue(final long customUserInfoValueId)\n            throws SCustomUserInfoValueNotFoundException,\n            SCustomUserInfoValueReadException {\n        try {\n            final SCustomUserInfoValue selectOne = persistenceService\n                    .selectById(SelectDescriptorBuilder.getElementById(SCustomUserInfoValue.class,\n                            \"SCustomUserInfoValue\", customUserInfoValueId));\n            if (selectOne == null) {\n                throw new SCustomUserInfoValueNotFoundException(customUserInfoValueId);\n            }\n            return selectOne;\n        } catch (final SBonitaReadException e) {\n            throw new SCustomUserInfoValueReadException(e);\n        }\n    }\n\n    @Override\n    public List<SCustomUserInfoValue> getCustomUserInfoValues(final List<Long> customUserInfoValueIds)\n            throws SIdentityException {\n        if (customUserInfoValueIds == null || customUserInfoValueIds.isEmpty()) {\n            return Collections.emptyList();\n        }\n        try {\n            return persistenceService.selectList(\n                    SelectDescriptorBuilder.getElementsByIds(SCustomUserInfoValue.class, \"SCustomUserInfoValue\",\n                            customUserInfoValueIds));\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\n                    \"Can't get custom user info values with ids \" + Arrays.toString(customUserInfoValueIds.toArray()),\n                    e);\n        }\n    }\n\n    @Override\n    public SRole getRole(final long roleId) throws SRoleNotFoundException {\n        try {\n            final SRole selectOne = persistenceService\n                    .selectById(SelectDescriptorBuilder.getElementById(SRole.class, \"Role\", roleId));\n            if (selectOne == null) {\n                throw new SRoleNotFoundException(\"The role with id= \" + roleId + \" does not exist\");\n            }\n            return selectOne;\n        } catch (final SBonitaReadException e) {\n            throw new SRoleNotFoundException(e);\n        }\n    }\n\n    @Override\n    public SRole getRoleByName(final String roleName) throws SRoleNotFoundException {\n        try {\n            final SRole role = persistenceService.selectOne(SelectDescriptorBuilder.getRoleByName(roleName));\n            if (role == null) {\n                throw new SRoleNotFoundException(\"The role named \" + roleName + \" does not exist\");\n            }\n            return role;\n        } catch (final SBonitaReadException e) {\n            throw new SRoleNotFoundException(e);\n        }\n    }\n\n    @Override\n    public SGroup getGroupByPath(final String groupPath) throws SGroupNotFoundException {\n        SelectOneDescriptor<SGroup> descriptor;\n        final int lastIndexOf = groupPath.lastIndexOf('/');\n        if (lastIndexOf > 0) {\n            final String groupName = groupPath.substring(lastIndexOf + 1);\n            final String parentPath = groupPath.substring(0, lastIndexOf);\n            descriptor = SelectDescriptorBuilder.getGroupByPath(parentPath, groupName);\n        } else if (lastIndexOf == 0) {\n            final String groupName = groupPath.substring(lastIndexOf + 1);\n            descriptor = SelectDescriptorBuilder.getGroupByName(groupName);\n        } else {\n            descriptor = SelectDescriptorBuilder.getGroupByName(groupPath);\n        }\n        try {\n            final SGroup group = persistenceService.selectOne(descriptor);\n            if (group == null) {\n                throw new SGroupNotFoundException(\"The group '\" + groupPath + \"' does not exist\");\n            }\n            return group;\n        } catch (final SBonitaReadException bre) {\n            throw new SGroupNotFoundException(bre);\n        }\n    }\n\n    @Override\n    public List<SRole> getRoles(final int fromIndex, final int numberOfRoles) throws SIdentityException {\n        try {\n            return persistenceService\n                    .selectList(SelectDescriptorBuilder.getElements(SRole.class, \"Role\", fromIndex, numberOfRoles));\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the roles\", e);\n        }\n    }\n\n    @Override\n    public List<SRole> getRoles(final int fromIndex, final int numberOfRoles, final String field,\n            final OrderByType order) throws SIdentityException {\n        try {\n            return persistenceService\n                    .selectList(SelectDescriptorBuilder.getElements(SRole.class, \"Role\", field, order, fromIndex,\n                            numberOfRoles));\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the roles\", e);\n        }\n    }\n\n    @Override\n    public List<SRole> getRoles(final List<Long> roleIds) throws SRoleNotFoundException {\n        if (roleIds == null || roleIds.isEmpty()) {\n            return Collections.emptyList();\n        }\n        try {\n            return persistenceService\n                    .selectList(SelectDescriptorBuilder.getElementsByIds(SRole.class, \"Role\", roleIds));\n        } catch (final SBonitaReadException e) {\n            throw new SRoleNotFoundException(e);\n        }\n    }\n\n    @Override\n    public SUser getUser(final long userId) throws SUserNotFoundException {\n        try {\n            final SUser user = persistenceService\n                    .selectById(SelectDescriptorBuilder.getElementById(SUser.class, \"User\", userId));\n\n            if (user == null) {\n                throw new SUserNotFoundException(userId);\n            }\n            return user;\n        } catch (final SBonitaReadException e) {\n            throw new SUserNotFoundException(\"Cannot get user with id: \" + userId, e);\n        }\n    }\n\n    @Override\n    public SContactInfo getUserContactInfo(final long userId, final boolean isPersonal) throws SIdentityException {\n        try {\n            return persistenceService.selectOne(SelectDescriptorBuilder.getUserContactInfo(userId, isPersonal));\n        } catch (final SBonitaReadException e) {\n            throw new SUserNotFoundException(\"Cannot get user contact info for user id: \" + userId, e);\n        }\n    }\n\n    @Override\n    public SUser getUserByUserName(final String userName) throws SUserNotFoundException {\n        try {\n            final SUser user = persistenceService.selectOne(SelectDescriptorBuilder.getUserByUserName(userName));\n            if (user == null) {\n                throw new SUserNotFoundException(userName);\n            }\n            return user;\n        } catch (final SBonitaReadException e) {\n            throw new SUserNotFoundException(\"Cannot get user: \" + userName, e);\n        }\n    }\n\n    @Override\n    public SUserMembership getUserMembership(final long userMembershipId) throws SIdentityException {\n        try {\n            final Map<String, Object> parameters = Collections.singletonMap(\"id\", (Object) userMembershipId);\n            final SelectOneDescriptor<SUserMembership> desc = new SelectOneDescriptor<>(\"getSUserMembershipById\",\n                    parameters,\n                    SUserMembership.class);\n            final SUserMembership selectOne = persistenceService.selectOne(desc);\n            if (selectOne == null) {\n                throw new SIdentityException(\"Can't get the userMembership with id \" + userMembershipId, null);\n            }\n            return selectOne;\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the user membership with id \" + userMembershipId, e);\n        }\n    }\n\n    @Override\n    public List<SUserMembership> getUserMemberships(final List<Long> userMembershipIds) throws SIdentityException {\n        List<Long> localUserMembershipIds = userMembershipIds;\n        if (localUserMembershipIds == null || localUserMembershipIds.isEmpty()) {\n            localUserMembershipIds = Collections.emptyList();\n        }\n        try {\n            return persistenceService.selectList(SelectDescriptorBuilder.getElementsByIds(SUserMembership.class,\n                    \"SUserMembership\", localUserMembershipIds));\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\n                    \"Can't get users memberships with ids \" + Arrays.toString(localUserMembershipIds.toArray()), e);\n        }\n    }\n\n    @Override\n    public List<SUser> getUsers(final int fromIndex, final int numberOfUsers) throws SIdentityException {\n        try {\n            return persistenceService\n                    .selectList(SelectDescriptorBuilder.getElements(SUser.class, \"User\", fromIndex, numberOfUsers));\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the users\", e);\n        }\n    }\n\n    @Override\n    public List<SUser> getUsers(final int fromIndex, final int numberOfUsers, final String field,\n            final OrderByType order) throws SIdentityException {\n        try {\n            return persistenceService\n                    .selectList(SelectDescriptorBuilder.getElements(SUser.class, \"User\", field, order, fromIndex,\n                            numberOfUsers));\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the users\", e);\n        }\n    }\n\n    @Override\n    public List<SUser> getUsers(final List<Long> userIds) throws SUserNotFoundException {\n        if (userIds == null || userIds.isEmpty()) {\n            return Collections.emptyList();\n        }\n        try {\n            return persistenceService\n                    .selectList(SelectDescriptorBuilder.getElementsByIds(SUser.class, \"User\", userIds));\n        } catch (final SBonitaReadException e) {\n            throw new SUserNotFoundException(e);\n        }\n    }\n\n    @Override\n    public List<SUser> getUsersByUsername(final List<String> userNames) throws SIdentityException {\n        if (userNames == null || userNames.isEmpty()) {\n            return Collections.emptyList();\n        }\n        try {\n            final QueryOptions queryOptions = new QueryOptions(0, userNames.size(), SUser.class, \"userName\",\n                    OrderByType.ASC);\n            final Map<String, Object> parameters = Collections.singletonMap(\"userNames\", (Object) userNames);\n            return persistenceService.selectList(\n                    new SelectListDescriptor<SUser>(\"getUsersByName\", parameters, SUser.class, queryOptions));\n        } catch (final SBonitaReadException e) {\n            throw new SUserNotFoundException(e);\n        }\n    }\n\n    @Override\n    public List<SUser> getUsersInGroup(final long groupId, final int fromIndex, final int numberOfUsers,\n            final String field, final OrderByType order)\n            throws SIdentityException {\n        try {\n            if (order != null) {\n                return persistenceService\n                        .selectList(SelectDescriptorBuilder.getUsersByGroup(groupId, field, order, fromIndex,\n                                numberOfUsers));\n            } else {\n                return persistenceService\n                        .selectList(SelectDescriptorBuilder.getUsersByGroup(groupId, fromIndex, numberOfUsers));\n            }\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the users having the group \" + groupId, e);\n        }\n    }\n\n    @Override\n    public List<SUser> getActiveUsersInGroup(final long groupId, final int fromIndex, final int numberOfUsers,\n            final String field, final OrderByType order)\n            throws SIdentityException {\n        try {\n            if (order != null) {\n                return persistenceService\n                        .selectList(SelectDescriptorBuilder.getActiveUsersByGroup(groupId, field, order, fromIndex,\n                                numberOfUsers));\n            } else {\n                return persistenceService\n                        .selectList(SelectDescriptorBuilder.getActiveUsersByGroup(groupId, fromIndex, numberOfUsers));\n            }\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the active users having the group \" + groupId, e);\n        }\n    }\n\n    @Override\n    public List<SUser> getInactiveUsersInGroup(final long groupId, final int fromIndex, final int numberOfUsers,\n            final String field, final OrderByType order)\n            throws SIdentityException {\n\n        try {\n            if (order != null) {\n                return persistenceService\n                        .selectList(SelectDescriptorBuilder.getInactiveUsersByGroup(groupId, field, order, fromIndex,\n                                numberOfUsers));\n            } else {\n                return persistenceService\n                        .selectList(SelectDescriptorBuilder.getInactiveUsersByGroup(groupId, fromIndex, numberOfUsers));\n            }\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the inactive users having the group \" + groupId, e);\n        }\n    }\n\n    @Override\n    public List<SUser> getUsersWithManager(long managerId, int fromIndex, int numberMaxOfUsers, String field,\n            OrderByType order) throws SIdentityException {\n        try {\n            final QueryOptions queryOptions;\n            if (order != null) {\n                queryOptions = new QueryOptions(fromIndex, numberMaxOfUsers, SUser.class, field, order);\n            } else {\n                queryOptions = new QueryOptions(fromIndex, numberMaxOfUsers, SUser.class, \"id\", OrderByType.DESC);\n            }\n            return persistenceService.selectList(SelectDescriptorBuilder.getUsersWithManager(managerId, queryOptions));\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the users having the manager \" + managerId, e);\n        }\n    }\n\n    @Override\n    public List<SUser> getActiveUsersWithManager(long managerId, int fromIndex, int numberMaxOfUsers, String field,\n            OrderByType order)\n            throws SIdentityException {\n        try {\n            final QueryOptions queryOptions;\n            if (order != null) {\n                queryOptions = new QueryOptions(fromIndex, numberMaxOfUsers, SUser.class, field, order);\n            } else {\n                queryOptions = new QueryOptions(fromIndex, numberMaxOfUsers, SUser.class, \"id\", OrderByType.DESC);\n            }\n            return persistenceService\n                    .selectList(SelectDescriptorBuilder.getActiveUsersWithManager(managerId, queryOptions));\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the active users having the manager \" + managerId, e);\n        }\n    }\n\n    @Override\n    public List<SUser> getInactiveUsersWithManager(long managerId, int fromIndex, int numberMaxOfUsers, String field,\n            OrderByType order)\n            throws SIdentityException {\n        try {\n            final QueryOptions queryOptions;\n            if (order != null) {\n                queryOptions = new QueryOptions(fromIndex, numberMaxOfUsers, SUser.class, field, order);\n            } else {\n                queryOptions = new QueryOptions(fromIndex, numberMaxOfUsers, SUser.class, \"id\", OrderByType.DESC);\n            }\n            return persistenceService\n                    .selectList(SelectDescriptorBuilder.getInactiveUsersWithManager(managerId, queryOptions));\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the inactive users having the manager \" + managerId, e);\n        }\n    }\n\n    @Override\n    public List<SUser> getUsersWithRole(final long roleId, final int fromIndex, final int numberOfUsers)\n            throws SIdentityException {\n        try {\n            return persistenceService\n                    .selectList(SelectDescriptorBuilder.getUsersWithRole(roleId, fromIndex, numberOfUsers));\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the users having the role \" + roleId, e);\n        }\n    }\n\n    @Override\n    public List<SUser> getActiveUsersWithRole(long roleId, int fromIndex, int numberOfUsers) throws SIdentityException {\n        try {\n            return persistenceService\n                    .selectList(SelectDescriptorBuilder.getActiveUsersWithRole(roleId, fromIndex, numberOfUsers));\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the active users having the role \" + roleId, e);\n        }\n    }\n\n    @Override\n    public List<SUser> getInactiveUsersWithRole(long roleId, int fromIndex, int numberOfUsers)\n            throws SIdentityException {\n        try {\n            return persistenceService\n                    .selectList(SelectDescriptorBuilder.getInactiveUsersWithRole(roleId, fromIndex, numberOfUsers));\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the inactive users having the role \" + roleId, e);\n        }\n    }\n\n    @Override\n    public List<SUser> getUsersWithRole(final long roleId, final int fromIndex, final int numberOfUsers,\n            final String field, final OrderByType order)\n            throws SIdentityException {\n        try {\n            if (order != null) {\n                return persistenceService\n                        .selectList(SelectDescriptorBuilder.getUsersWithRole(roleId, field, order, fromIndex,\n                                numberOfUsers));\n            } else {\n                return persistenceService\n                        .selectList(SelectDescriptorBuilder.getUsersWithRole(roleId, fromIndex, numberOfUsers));\n            }\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the users having the role \" + roleId, e);\n        }\n    }\n\n    @Override\n    public List<SUser> getActiveUsersWithRole(final long roleId, final int fromIndex, final int numberOfUsers,\n            final String field, final OrderByType order)\n            throws SIdentityException {\n        try {\n            if (order != null) {\n                return persistenceService\n                        .selectList(SelectDescriptorBuilder.getActiveUsersWithRole(roleId, field, order, fromIndex,\n                                numberOfUsers));\n            } else {\n                return persistenceService\n                        .selectList(SelectDescriptorBuilder.getActiveUsersWithRole(roleId, fromIndex, numberOfUsers));\n            }\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the active users having the role \" + roleId, e);\n        }\n    }\n\n    @Override\n    public List<SUser> getInactiveUsersWithRole(final long roleId, final int fromIndex, final int numberOfUsers,\n            final String field, final OrderByType order)\n            throws SIdentityException {\n        try {\n            if (order != null) {\n                return persistenceService\n                        .selectList(SelectDescriptorBuilder.getInactiveUsersWithRole(roleId, field, order, fromIndex,\n                                numberOfUsers));\n            } else {\n                return persistenceService\n                        .selectList(SelectDescriptorBuilder.getInactiveUsersWithRole(roleId, fromIndex, numberOfUsers));\n            }\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the inactive users having the role \" + roleId, e);\n        }\n    }\n\n    @Override\n    public void updateGroup(final SGroup group, final EntityUpdateDescriptor descriptor,\n            EntityUpdateDescriptor iconUpdater) throws SIdentityException {\n        final SGroupLogBuilder logBuilder = getGroupLog(ActionType.UPDATED, \"Updating the group\");\n        try {\n            updateIcon(descriptor, group.getIconId(), iconUpdater);\n            recorder.recordUpdate(UpdateRecord.buildSetFields(group, descriptor), GROUP);\n            log(group.getId(), SQueriableLog.STATUS_OK, logBuilder, \"updateGroup\");\n        } catch (final SRecorderException e) {\n            log(group.getId(), SQueriableLog.STATUS_FAIL, logBuilder, \"updateGroup\");\n            throw new SIdentityException(\"Can't update group \" + group, e);\n        }\n    }\n\n    @Override\n    public void updateCustomUserInfoDefinition(final SCustomUserInfoDefinition customUserInfo,\n            final EntityUpdateDescriptor descriptor)\n            throws SIdentityException {\n        final String methodName = \"updateCustomUserInfoDefinition\";\n        final SCustomUserInfoDefinitionLogBuilder logBuilder = getSCustomUserInfoDefinitionLog(ActionType.UPDATED,\n                \"Updating the custom user info definition with name \" + customUserInfo.getName());\n        try {\n            recorder.recordUpdate(UpdateRecord.buildSetFields(customUserInfo, descriptor), CUSTOM_USER_INFO_DEFINITION);\n            log(customUserInfo.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName);\n        } catch (final SRecorderException e) {\n            log(customUserInfo.getId(), SQueriableLog.STATUS_FAIL, logBuilder, methodName);\n            throw new SIdentityException(\"Can't update custom user info definition \" + customUserInfo, e);\n        }\n    }\n\n    @Override\n    public void updateCustomUserInfoValue(final SCustomUserInfoValue customUserInfo,\n            final EntityUpdateDescriptor descriptor) throws SIdentityException {\n        try {\n            recorder.recordUpdate(UpdateRecord.buildSetFields(customUserInfo, descriptor), CUSTOM_USER_INFO_VALUE);\n        } catch (final SRecorderException e) {\n            throw new SIdentityException(\"Can't update custom user info definition \" + customUserInfo, e);\n        }\n    }\n\n    @Override\n    public SRole updateRole(final SRole role, final EntityUpdateDescriptor descriptor,\n            EntityUpdateDescriptor iconUpdater) throws SIdentityException {\n        final String methodName = \"updateRole\";\n        final SRoleLogBuilder logBuilder = getRoleLog(ActionType.UPDATED,\n                \"Updating the role with name \" + role.getName());\n        try {\n            updateIcon(descriptor, role.getIconId(), iconUpdater);\n            recorder.recordUpdate(UpdateRecord.buildSetFields(role, descriptor), ROLE);\n            log(role.getId(), SQueriableLog.STATUS_FAIL, logBuilder, methodName);\n        } catch (final SRecorderException e) {\n            log(role.getId(), SQueriableLog.STATUS_FAIL, logBuilder, methodName);\n            throw new SIdentityException(\"Can't update role \" + role, e);\n        }\n        return role;\n    }\n\n    @Override\n    public void updateUser(final SUser user, final EntityUpdateDescriptor descriptor) throws SUserUpdateException {\n        updateUser(user, descriptor, false);\n    }\n\n    @Deprecated\n    @Override\n    public void updateUser(final SUser user, final EntityUpdateDescriptor descriptor, final boolean isPasswordEncrypted)\n            throws SUserUpdateException {\n        final String methodName = \"updateUser\";\n        if (!isPasswordEncrypted) {\n            final String password = (String) descriptor.getFields().get(\"password\");\n            if (password != null) {\n                final String hash = encrypter.hash(password);\n                descriptor.getFields().put(\"password\", hash);\n            }\n        }\n        final SUserLogBuilder logBuilder = getUserLog(ActionType.UPDATED, \"Updating user with user name \" +\n                user.getUserName() +\n                \", first name \" +\n                user.getFirstName() +\n                \", last name \" +\n                user.getLastName());\n        try {\n            recorder.recordUpdate(UpdateRecord.buildSetFields(user, descriptor), USER);\n            log(user.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName);\n        } catch (final SRecorderException re) {\n            log(user.getId(), SQueriableLog.STATUS_FAIL, logBuilder, methodName);\n            throw new SUserUpdateException(re);\n        }\n    }\n\n    @Override\n    public void updateUserContactInfo(final SContactInfo contactInfo, final EntityUpdateDescriptor descriptor)\n            throws SIdentityException {\n        final String methodName = \"updateUserContactInfo\";\n        final SContactInfoLogBuilder logBuilder = getUserContactInfoLog(ActionType.UPDATED,\n                \"Updating \" + (contactInfo.isPersonal() ? \"personal\" : \"professional\")\n                        + \" user contact Info for user with Id \" +\n                        contactInfo.getUserId(),\n                contactInfo);\n        try {\n            recorder.recordUpdate(UpdateRecord.buildSetFields(contactInfo, descriptor), USER_CONTACT_INFO);\n            log(contactInfo.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName);\n        } catch (final SRecorderException re) {\n            log(contactInfo.getId(), SQueriableLog.STATUS_FAIL, logBuilder, methodName);\n            throw new SUserUpdateException(re);\n        }\n    }\n\n    @Override\n    public void updateUserMembership(final SUserMembership userMembership, final EntityUpdateDescriptor descriptor)\n            throws SIdentityException {\n        final String methodName = \"updateUserMembership\";\n        final SUserMembershipLogBuilder logBuilder = getUserMembershipLog(ActionType.UPDATED,\n                \"Updating user membership for user \" +\n                        userMembership.getUsername() +\n                        \" with role \" +\n                        userMembership.getRoleName() +\n                        \" in group \" +\n                        userMembership.getGroupName(),\n                userMembership);\n        try {\n            recorder.recordUpdate(UpdateRecord.buildSetFields(userMembership, descriptor), USERMEMBERSHIP);\n            log(userMembership.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName);\n        } catch (final SRecorderException e) {\n            log(userMembership.getId(), SQueriableLog.STATUS_FAIL, logBuilder, methodName);\n            throw new SIdentityException(\"Can't update user membership \" + userMembership, e);\n        }\n    }\n\n    @Override\n    public List<SUserMembership> getUserMemberships(final int fromIndex, final int numberOfUserMemberships)\n            throws SIdentityException {\n        final SelectListDescriptor<SUserMembership> descriptor = SelectDescriptorBuilder.getElements(\n                SUserMembership.class, \"UserMembership\", fromIndex,\n                numberOfUserMemberships);\n        try {\n            return persistenceService.selectList(descriptor);\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the user memberships\", e);\n        }\n    }\n\n    @Override\n    public List<SUserMembership> getUserMembershipsOfUser(final long userId, final int fromIndex,\n            final int numberOfUsers) throws SIdentityException {\n        try {\n            final SelectListDescriptor<SUserMembership> descriptor = SelectDescriptorBuilder\n                    .getUserMembershipsOfUser(userId, fromIndex, numberOfUsers);\n            return persistenceService.selectList(descriptor);\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the memberships having the user \" + userId, e);\n        }\n    }\n\n    @Override\n    public List<SUserMembership> getUserMembershipsOfUser(final long userId, final int fromIndex,\n            final int numberOfMemberships, final String field,\n            final OrderByType order) throws SIdentityException {\n        try {\n            final SelectListDescriptor<SUserMembership> descriptor = SelectDescriptorBuilder.getUserMembershipsOfUser(\n                    userId, field, order, fromIndex,\n                    numberOfMemberships);\n            return persistenceService.selectList(descriptor);\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the memberships having the user\" + userId, e);\n        }\n    }\n\n    @Override\n    public List<SUserMembership> getUserMembershipsOfUser(final long userId, final int fromIndex,\n            final int numberPerPage, final OrderByOption orderByOption)\n            throws SIdentityException {\n        try {\n            final SelectListDescriptor<SUserMembership> descriptor = SelectDescriptorBuilder\n                    .getUserMembershipsOfUser(userId, new QueryOptions(fromIndex,\n                            numberPerPage, Collections.singletonList(orderByOption)));\n            return persistenceService.selectList(descriptor);\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the memberships having the user\" + userId, e);\n        }\n    }\n\n    @Override\n    public SUserMembership getUserMembership(final long userId, final long groupId, final long roleId)\n            throws SIdentityException {\n        final SelectOneDescriptor<SUserMembership> descriptor = SelectDescriptorBuilder.getUserMembership(userId,\n                groupId, roleId);\n        try {\n            return getUserMembership(userId, groupId, roleId, descriptor);\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the userMembership with userId = \" + userId + \", groupId = \"\n                    + groupId + \", roleId = \" + roleId, e);\n        }\n    }\n\n    @Override\n    public SUserMembership getLightUserMembership(final long userId, final long groupId, final long roleId)\n            throws SIdentityException {\n        final SelectOneDescriptor<SUserMembership> descriptor = SelectDescriptorBuilder.getLightUserMembership(userId,\n                groupId, roleId);\n        try {\n            return getUserMembership(userId, groupId, roleId, descriptor);\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the userMembership with userId = \" + userId + \", groupId = \"\n                    + groupId + \", roleId = \" + roleId, e);\n        }\n    }\n\n    private SUserMembership getUserMembership(final long userId, final long groupId, final long roleId,\n            final SelectOneDescriptor<SUserMembership> descriptor)\n            throws SBonitaReadException, SIdentityException {\n        final SUserMembership sUserMembership = persistenceService.selectOne(descriptor);\n        if (sUserMembership == null) {\n            throw new SIdentityException(\"Can't get the userMembership with userId = \" + userId + \", groupId = \"\n                    + groupId + \", roleId = \" + roleId);\n        }\n        return sUserMembership;\n    }\n\n    @Override\n    public long getNumberOfUserMemberships() throws SIdentityException {\n        try {\n            return persistenceService\n                    .selectOne(SelectDescriptorBuilder.getNumberOfElement(\"UserMembership\", SUserMembership.class));\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the number of user membership\", e);\n        }\n    }\n\n    @Override\n    public List<SUserMembership> getLightUserMemberships(final int startIndex, final int numberOfElements)\n            throws SIdentityException {\n        final SelectListDescriptor<SUserMembership> descriptor = SelectDescriptorBuilder.getElements(\n                SUserMembership.class, \"LightUserMembership\", startIndex,\n                numberOfElements);\n        try {\n            return persistenceService.selectList(descriptor);\n        } catch (final SBonitaReadException e) {\n            throw new SIdentityException(\"Can't get the user memberships\", e);\n        }\n    }\n\n    @Override\n    public long getNumberOfUsers(final QueryOptions options) throws SBonitaReadException {\n        return persistenceService.getNumberOfEntities(SUser.class, options, null);\n    }\n\n    @Override\n    public List<SUser> searchUsers(final QueryOptions options) throws SBonitaReadException {\n        return persistenceService.searchEntity(SUser.class, options, null);\n    }\n\n    @Override\n    public long getNumberOfRoles(final QueryOptions options) throws SBonitaReadException {\n        return persistenceService.getNumberOfEntities(SRole.class, options, null);\n    }\n\n    @Override\n    public List<SRole> searchRoles(final QueryOptions options) throws SBonitaReadException {\n        return persistenceService.searchEntity(SRole.class, options, null);\n    }\n\n    @Override\n    public long getNumberOfGroups(final QueryOptions options) throws SBonitaReadException {\n        return persistenceService.getNumberOfEntities(SGroup.class, options, null);\n    }\n\n    @Override\n    public List<SGroup> searchGroups(final QueryOptions options) throws SBonitaReadException {\n        return persistenceService.searchEntity(SGroup.class, options, null);\n    }\n\n    private void log(final long objectId, final int sQueriableLogStatus, final SPersistenceLogBuilder logBuilder,\n            final String methodName) {\n        logBuilder.actionScope(String.valueOf(objectId));\n        logBuilder.actionStatus(sQueriableLogStatus);\n        logBuilder.objectId(objectId);\n        final SQueriableLog log = logBuilder.build();\n        if (queriableLoggerService.isLoggable(log.getActionType(), log.getSeverity())) {\n            queriableLoggerService.log(this.getClass().getName(), methodName, log);\n        }\n    }\n\n    @Override\n    public boolean checkCredentials(final SUser user, final String password) {\n        final String hashPassword = user.getPassword();\n        return encrypter.check(password, hashPassword);\n    }\n\n    @Override\n    public List<SCustomUserInfoValue> searchCustomUserInfoValue(final QueryOptions options)\n            throws SBonitaReadException {\n        return persistenceService.searchEntity(SCustomUserInfoValue.class, options, null);\n    }\n\n    @Override\n    public List<SCustomUserInfoValue> getCustomUserInfoValueOfUserAndDefinitions(long userId, List<Long> definitionsIds)\n            throws SBonitaReadException {\n        HashMap<String, Object> parameters = new HashMap<>();\n        parameters.put(\"userId\", userId);\n        parameters.put(\"definitionIds\", definitionsIds);\n        return persistenceService.selectList(new SelectListDescriptor<>(\n                \"getCustomUserInfoValueOfUserAndDefinitions\",\n                parameters,\n                SCustomUserInfoValue.class,\n                QueryOptions.ALL_RESULTS));\n    }\n\n    @Override\n    public SUser updateUser(long userId, EntityUpdateDescriptor userUpdateDescriptor,\n            EntityUpdateDescriptor personalDataUpdateDescriptor,\n            EntityUpdateDescriptor professionalDataUpdateDescriptor, EntityUpdateDescriptor iconUpdater)\n            throws SIdentityException {\n        // User change\n        SUser sUser = getUser(userId);\n        updateIcon(userUpdateDescriptor, sUser.getIconId(), iconUpdater);\n        if (userUpdateDescriptor != null && !userUpdateDescriptor.getFields().isEmpty()) {\n            updateUser(sUser, userUpdateDescriptor);\n        }\n\n        // Personal data change\n        if (personalDataUpdateDescriptor != null && !personalDataUpdateDescriptor.getFields().isEmpty()) {\n            SContactInfo persoContactInfo = getUserContactInfo(userId, true);\n            if (persoContactInfo == null) {\n                persoContactInfo = SContactInfo.builder().userId(userId).personal(true).build();\n                createUserContactInfo(persoContactInfo);\n            }\n            updateUserContactInfo(persoContactInfo, personalDataUpdateDescriptor);\n        }\n\n        // Professional data change\n        if (professionalDataUpdateDescriptor != null && !professionalDataUpdateDescriptor.getFields().isEmpty()) {\n            SContactInfo professContactInfo = getUserContactInfo(userId, false);\n            if (professContactInfo == null) {\n                professContactInfo = SContactInfo.builder().userId(userId).personal(false).build();\n                createUserContactInfo(professContactInfo);\n            }\n            updateUserContactInfo(professContactInfo, professionalDataUpdateDescriptor);\n        }\n        return sUser;\n    }\n\n    private void updateIcon(EntityUpdateDescriptor updateDescriptor,\n            Long iconId, EntityUpdateDescriptor iconUpdater) throws SIdentityException {\n        try {\n            if (iconUpdater != null && !iconUpdater.getFields().isEmpty()) {\n                updateDescriptor.addField(\"iconId\",\n                        iconService.replaceIcon((String) iconUpdater.getFields().get(\"filename\"),\n                                (byte[]) iconUpdater.getFields().get(\"content\"), iconId).orElse(null));\n            }\n        } catch (SBonitaReadException | SRecorderException e) {\n            throw new SIdentityException(e);\n        }\n    }\n\n    @Override\n    public SUser createUser(SUser sUser, SContactInfo personalContactInfo, SContactInfo proContactInfo,\n            String iconFilename, byte[] iconContent)\n            throws SUserCreationException {\n        if (iconFilename != null && iconContent != null) {\n            try {\n                SIcon icon = iconService.createIcon(iconFilename, iconContent);\n                sUser.setIconId(icon.getId());\n            } catch (SRecorderException e) {\n                throw new SUserCreationException(e);\n            }\n        }\n        SUser user = createUser(sUser);\n        if (personalContactInfo != null) {\n            SContactInfo sContactInfo = new SContactInfo(personalContactInfo);\n            sContactInfo.setUserId(user.getId());\n            createUserContactInfo(sContactInfo);\n        }\n        if (proContactInfo != null) {\n            SContactInfo sContactInfo = new SContactInfo(proContactInfo);\n            sContactInfo.setUserId(user.getId());\n            createUserContactInfo(sContactInfo);\n        }\n        return user;\n    }\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/impl/MD5CredentialsEncrypter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.impl;\n\nimport org.bonitasoft.engine.digest.DigestUtils;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class MD5CredentialsEncrypter implements CredentialsEncrypter {\n\n    @Override\n    public String hash(final String password) {\n        final byte[] hash = DigestUtils.md5(password);\n        return DigestUtils.encodeBase64AsUtf8String(hash);\n    }\n\n    @Override\n    public boolean check(final String password, final String hashPassword) {\n        final String hashedPassword = hash(password);\n        return hashedPassword.equals(hashPassword);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/SContactInfo.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model;\n\nimport javax.persistence.Cacheable;\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * User contact info: can be personal or professional contact information.\n *\n * @author Emmanuel Duchastenier\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@Builder\n@Entity\n@Table(name = \"user_contactinfo\")\n@Cacheable(false)\npublic class SContactInfo implements PersistentObject {\n\n    public static final String ID = \"id\";\n    public static final String WEBSITE = \"website\";\n    public static final String COUNTRY = \"country\";\n    public static final String STATE = \"state\";\n    public static final String CITY = \"city\";\n    public static final String ZIP_CODE = \"zipCode\";\n    public static final String ADDRESS = \"address\";\n    public static final String ROOM = \"room\";\n    public static final String BUILDING = \"building\";\n    public static final String FAX_NUMBER = \"faxNumber\";\n    public static final String MOBILE_NUMBER = \"mobileNumber\";\n    public static final String PHONE_NUMBER = \"phoneNumber\";\n    public static final String EMAIL = \"email\";\n    public static final String IS_PERSONAL = \"personal\";\n    @Id\n    private long id;\n    @Column\n    private Long userId;\n    @Column\n    private String email;\n    @Column(name = \"phone\")\n    private String phoneNumber;\n    @Column(name = \"mobile\")\n    private String mobileNumber;\n    @Column(name = \"fax\")\n    private String faxNumber;\n    @Column\n    private String building;\n    @Column\n    private String room;\n    @Column\n    private String address;\n    @Column\n    private String zipCode;\n    @Column\n    private String city;\n    @Column\n    private String state;\n    @Column\n    private String country;\n    @Column\n    private String website;\n    @Column\n    private boolean personal;\n\n    public SContactInfo(final SContactInfo contactInfo) {\n        this();\n        userId = contactInfo.getUserId();\n        personal = contactInfo.isPersonal();\n        address = contactInfo.getAddress();\n        building = contactInfo.getBuilding();\n        city = contactInfo.getCity();\n        country = contactInfo.getCountry();\n        email = contactInfo.getEmail();\n        faxNumber = contactInfo.getFaxNumber();\n        mobileNumber = contactInfo.getMobileNumber();\n        phoneNumber = contactInfo.getPhoneNumber();\n        room = contactInfo.getRoom();\n        state = contactInfo.getState();\n        website = contactInfo.getWebsite();\n        zipCode = contactInfo.getZipCode();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/SCustomUserInfoDefinition.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model;\n\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Anthony Birembaut\n * @author Baptiste Mesta\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@Builder\n@Entity\n@Table(name = \"custom_usr_inf_def\")\npublic class SCustomUserInfoDefinition implements PersistentObject {\n\n    public static final String ID = \"id\";\n    public static final String NAME = \"name\";\n    public static final String DESCRIPTION = \"description\";\n    public static final String DISPLAY_NAME = \"displayName\";\n    @Id\n    private long id;\n    @Column\n    private String name;\n    @Column\n    private String description;\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/SCustomUserInfoValue.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model;\n\nimport javax.persistence.Cacheable;\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Anthony Birembaut\n * @author Baptiste Mesta\n * @author Elias Ricken de Medeiros\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@Builder\n@Entity\n@Table(name = \"custom_usr_inf_val\")\n@Cacheable(false)\npublic class SCustomUserInfoValue implements PersistentObject {\n\n    public static final String ID = \"id\";\n    public static final String USER_ID = \"userId\";\n    public static final String DEFINITION_ID = \"definitionId\";\n    public static final String VALUE = \"value\";\n    @Id\n    private long id;\n    @Column\n    protected long userId;\n    @Column\n    protected long definitionId;\n    @Column\n    protected String value;\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/SGroup.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model;\n\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Anthony Birembaut\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@Builder\n@Entity\n@Table(name = \"group_\")\npublic class SGroup implements PersistentObject, SHavingIcon {\n\n    public static final String PARENT_PATH = \"parentPath\";\n    public static final String ID = \"id\";\n    public static final String NAME = \"name\";\n    public static final String DESCRIPTION = \"description\";\n    public static final String DISPLAY_NAME = \"displayName\";\n    public static final String CREATED_BY = \"createdBy\";\n    public static final String CREATION_DATE = \"creationDate\";\n    public static final String LAST_UPDATE = \"lastUpdate\";\n    @Id\n    private long id;\n    @Column\n    private String name;\n    @Column\n    private String description;\n    @Column\n    private String displayName;\n    @Column\n    private String parentPath;\n    @Column\n    private long createdBy;\n    @Column\n    private long creationDate;\n    @Column\n    private long lastUpdate;\n    @Column(name = \"iconid\")\n    private Long iconId;\n\n    public String getPath() {\n        if (parentPath == null) {\n            return \"/\" + getName();\n        }\n        return parentPath + \"/\" + getName();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/SHavingIcon.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface SHavingIcon {\n\n    Long getIconId();\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/SRole.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model;\n\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Anthony Birembaut\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@Builder\n@Entity\n@Table(name = \"role\")\npublic class SRole implements PersistentObject, SHavingIcon {\n\n    public static final String ID = \"id\";\n    public static final String NAME = \"name\";\n    public static final String DESCRIPTION = \"description\";\n    public static final String DISPLAY_NAME = \"displayName\";\n    public static final String ICON_PATH = \"iconPath\";\n    public static final String CREATED_BY = \"createdBy\";\n    public static final String CREATION_DATE = \"creationDate\";\n    public static final String LAST_UPDATE = \"lastUpdate\";\n\n    @Id\n    private long id;\n    @Column(name = \"name\")\n    private String name;\n    @Column(name = \"description\")\n    private String description;\n    @Column(name = \"displayName\")\n    private String displayName;\n    @Column(name = \"createdBy\")\n    private long createdBy;\n    @Column(name = \"creationDate\")\n    private long creationDate;\n    @Column(name = \"lastUpdate\")\n    private long lastUpdate;\n    @Column(name = \"iconid\")\n    private Long iconId;\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/SUser.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model;\n\nimport javax.persistence.CascadeType;\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.OneToOne;\nimport javax.persistence.PrimaryKeyJoinColumn;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.ToString;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Anthony Birembaut\n * @author Baptiste Mesta\n * @author Yanyan Liu\n * @author Emmanuel Duchastenier\n * @author Celine Souchet\n */\n@Data\n@ToString(exclude = \"password\")\n@NoArgsConstructor\n@AllArgsConstructor\n@Builder(toBuilder = true)\n@Entity\n@Table(name = \"user_\")\npublic class SUser implements PersistentObject, SHavingIcon {\n\n    public static final String ID = \"id\";\n    public static final String MANAGER_USER_ID = \"managerUserId\";\n    public static final String JOB_TITLE = \"jobTitle\";\n    public static final String TITLE = \"title\";\n    public static final String LAST_NAME = \"lastName\";\n    public static final String FIRST_NAME = \"firstName\";\n    public static final String USER_NAME = \"userName\";\n    public static final String PASSWORD = \"password\";\n    public static final String LAST_UPDATE = \"lastUpdate\";\n    public static final String LAST_CONNECTION = \"lastConnection\";\n    public static final String CREATED_BY = \"createdBy\";\n    public static final String CREATION_DATE = \"creationDate\";\n    public static final String ENABLED = \"enabled\";\n    @Id\n    private long id;\n    @Column\n    private String firstName;\n    @Column\n    private String lastName;\n    @Column\n    private String password;\n    @Column\n    private String userName;\n    @Column\n    private long managerUserId;\n    @Column\n    private String title;\n    @Column\n    private String jobTitle;\n    @Column\n    private long creationDate;\n    @Column\n    private long createdBy;\n    @Column\n    private long lastUpdate;\n    @Column\n    private boolean enabled;\n    @OneToOne(cascade = CascadeType.ALL)\n    @PrimaryKeyJoinColumn\n    private SUserLogin sUserLogin;\n    @Column(name = \"iconid\")\n    private Long iconId;\n\n    public SUser(final SUser user) {\n        firstName = user.getFirstName();\n        lastName = user.getLastName();\n        password = user.getPassword();\n        userName = user.getUserName();\n        jobTitle = user.getJobTitle();\n        managerUserId = user.getManagerUserId();\n        createdBy = user.getCreatedBy();\n        creationDate = user.getCreationDate();\n        lastUpdate = user.getLastUpdate();\n        title = user.getTitle();\n        enabled = user.isEnabled();\n        iconId = user.getIconId();\n    }\n\n    // called by reflection\n    public void setLastConnection(final Long lastConnection) {\n        sUserLogin.setLastConnection(lastConnection);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/SUserLogin.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model;\n\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.OneToOne;\nimport javax.persistence.PrimaryKeyJoinColumn;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.ToString;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Baptiste Mesta\n */\n@Data\n@NoArgsConstructor\n@ToString(exclude = \"sUser\")\n@EqualsAndHashCode(exclude = \"sUser\")\n@Builder\n@AllArgsConstructor\n@Entity\n@Table(name = \"user_login\")\npublic class SUserLogin implements PersistentObject {\n\n    @Id\n    private long id;\n    @Column\n    private Long lastConnection;\n    @OneToOne\n    @PrimaryKeyJoinColumn\n    private SUser sUser;\n\n    public SUserLogin(Long lastConnection) {\n        this.lastConnection = lastConnection;\n    }\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/SUserMembership.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model;\n\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Anthony Birembaut\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@Builder\n@Entity\n@Table(name = \"user_membership\")\npublic class SUserMembership implements PersistentObject {\n\n    public static final String ID = \"id\";\n    public static final String USER_ID = \"userId\";\n    public static final String ROLE_ID = \"roleId\";\n    public static final String GROUP_ID = \"groupId\";\n    public static final String ASSIGNED_BY = \"assignedBy\";\n    public static final String ASSIGNED_DATE = \"assignedDate\";\n    @Id\n    private long id;\n    @Column\n    private long roleId;\n    @Column\n    private long groupId;\n    @Column\n    private long userId;\n    @Column\n    private long assignedBy;\n    @Column\n    private long assignedDate;\n    private transient String groupParentPath;\n    private transient String roleName;\n    private transient String groupName;\n    private transient String username;\n\n    public SUserMembership(final long userId, final long groupId, final long roleId) {\n        this.userId = userId;\n        this.groupId = groupId;\n        this.roleId = roleId;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SContactInfoLogBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\n\n/**\n * @author Emmanuel Duchastenier\n * @author Matthieu Chaffotte\n */\npublic interface SContactInfoLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction {\n\n    SPersistenceLogBuilder setContactInfoUserId(long userId);\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SContactInfoLogBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory;\n\n/**\n * @author Emmanuel Duchastenier\n * @author Matthieu Chaffotte\n */\npublic interface SContactInfoLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory {\n\n    @Override\n    SContactInfoLogBuilder createNewInstance();\n\n    String getContactInfoUserIdKey();\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SContactInfoUpdateBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic interface SContactInfoUpdateBuilder extends SIdentityUpdateBuilder {\n\n    SContactInfoUpdateBuilder updateEmail(final String email);\n\n    SContactInfoUpdateBuilder updatePhoneNumber(final String phoneNumber);\n\n    SContactInfoUpdateBuilder updateMobileNumber(final String mobileNumber);\n\n    SContactInfoUpdateBuilder updateFaxNumber(final String faxNumber);\n\n    SContactInfoUpdateBuilder updateBuilding(final String building);\n\n    SContactInfoUpdateBuilder updateRoom(final String room);\n\n    SContactInfoUpdateBuilder updateAddress(final String address);\n\n    SContactInfoUpdateBuilder updateZipCode(final String zipCode);\n\n    SContactInfoUpdateBuilder updateCity(final String city);\n\n    SContactInfoUpdateBuilder updateState(final String state);\n\n    SContactInfoUpdateBuilder updateCountry(final String country);\n\n    SContactInfoUpdateBuilder updateWebsite(final String website);\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SContactInfoUpdateBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic interface SContactInfoUpdateBuilderFactory extends SIdentityUpdateBuilderFactory {\n\n    @Override\n    SContactInfoUpdateBuilder createNewInstance();\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SCustomUserInfoDefinitionLogBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\n\n/**\n * @author Yanyan Liu\n */\npublic interface SCustomUserInfoDefinitionLogBuilder extends HasCRUDEAction, SPersistenceLogBuilder {\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SCustomUserInfoDefinitionLogBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory;\n\n/**\n * @author Yanyan Liu\n */\npublic interface SCustomUserInfoDefinitionLogBuilderFactory\n        extends HasCRUDEActionFactory, SPersistenceLogBuilderFactory {\n\n    @Override\n    SCustomUserInfoDefinitionLogBuilder createNewInstance();\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SCustomUserInfoDefinitionUpdateBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface SCustomUserInfoDefinitionUpdateBuilder extends SIdentityUpdateBuilder {\n\n    SCustomUserInfoDefinitionUpdateBuilder updateName(final String name);\n\n    SCustomUserInfoDefinitionUpdateBuilder updateDisplayName(final String displayName);\n\n    SCustomUserInfoDefinitionUpdateBuilder updateDescription(final String description);\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SCustomUserInfoDefinitionUpdateBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface SCustomUserInfoDefinitionUpdateBuilderFactory extends SIdentityUpdateBuilderFactory {\n\n    @Override\n    SCustomUserInfoDefinitionUpdateBuilder createNewInstance();\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SCustomUserInfoValueUpdateBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface SCustomUserInfoValueUpdateBuilder extends SIdentityUpdateBuilder {\n\n    SCustomUserInfoValueUpdateBuilder updateValue(final String value);\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SCustomUserInfoValueUpdateBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface SCustomUserInfoValueUpdateBuilderFactory extends SIdentityUpdateBuilderFactory {\n\n    @Override\n    SCustomUserInfoValueUpdateBuilder createNewInstance();\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SGroupLogBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\n\n/**\n * @author Yanyan Liu\n */\npublic interface SGroupLogBuilder extends HasCRUDEAction, SPersistenceLogBuilder {\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SGroupLogBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory;\n\n/**\n * @author Yanyan Liu\n */\npublic interface SGroupLogBuilderFactory extends HasCRUDEActionFactory, SPersistenceLogBuilderFactory {\n\n    @Override\n    SGroupLogBuilder createNewInstance();\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SGroupUpdateBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface SGroupUpdateBuilder extends SIdentityUpdateBuilder {\n\n    SGroupUpdateBuilder updateName(final String name);\n\n    SGroupUpdateBuilder updateDisplayName(final String displayName);\n\n    SGroupUpdateBuilder updateDescription(final String description);\n\n    SGroupUpdateBuilder updateParentPath(final String parentPath);\n\n    SGroupUpdateBuilder updateCreatedBy(final long createdBy);\n\n    SGroupUpdateBuilder updateCreationDate(final long creationDate);\n\n    SGroupUpdateBuilder updateLastUpdate(final long lastUpdate);\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SGroupUpdateBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface SGroupUpdateBuilderFactory extends SIdentityUpdateBuilderFactory {\n\n    @Override\n    SGroupUpdateBuilder createNewInstance();\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SIdentityUpdateBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder;\n\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface SIdentityUpdateBuilder {\n\n    EntityUpdateDescriptor done();\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SIdentityUpdateBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface SIdentityUpdateBuilderFactory {\n\n    SIdentityUpdateBuilder createNewInstance();\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SMembershipLogBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\n\n/**\n * @author Yanyan Liu\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic interface SMembershipLogBuilder extends HasCRUDEAction, SPersistenceLogBuilder {\n\n    SMembershipLogBuilder roleName(final String roleName);\n\n    SMembershipLogBuilder groupPath(final String groupPath);\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SMembershipLogBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\n\n/**\n * @author Yanyan Liu\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic interface SMembershipLogBuilderFactory extends HasCRUDEAction, SPersistenceLogBuilder {\n\n    String getRoleNameKey();\n\n    String getGroupPathKey();\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SProfileMetadataValueLogBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\n\n/**\n * @author Yanyan Liu\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic interface SProfileMetadataValueLogBuilder extends HasCRUDEAction, SPersistenceLogBuilder {\n\n    SProfileMetadataValueLogBuilder identityUserName(final String userName);\n\n    SProfileMetadataValueLogBuilder metadataName(final String metadataName);\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SProfileMetadataValueLogBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory;\n\n/**\n * @author Yanyan Liu\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic interface SProfileMetadataValueLogBuilderFactory extends HasCRUDEActionFactory, SPersistenceLogBuilderFactory {\n\n    String getIdentityUserNameKey();\n\n    String getCustomUserInfoDefinitionNameKey();\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SRoleLogBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\n\n/**\n * @author Yanyan Liu\n */\npublic interface SRoleLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction {\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SRoleLogBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory;\n\n/**\n * @author Yanyan Liu\n */\npublic interface SRoleLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory {\n\n    @Override\n    SRoleLogBuilder createNewInstance();\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SRoleUpdateBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder;\n\n/**\n * @author Baptiste Mesta\n * @author Bole Zhang\n * @author Matthieu Chaffotte\n */\npublic interface SRoleUpdateBuilder extends SIdentityUpdateBuilder {\n\n    SRoleUpdateBuilder updateName(final String name);\n\n    SRoleUpdateBuilder updateDisplayName(final String displayName);\n\n    SRoleUpdateBuilder updateDescription(final String description);\n\n    SRoleUpdateBuilder updateCreatedBy(final long createdBy);\n\n    SRoleUpdateBuilder updateCreationDate(final long creationDate);\n\n    SRoleUpdateBuilder updateLastUpdate(final long lastUpdate);\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SRoleUpdateBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder;\n\n/**\n * @author Baptiste Mesta\n * @author Bole Zhang\n * @author Matthieu Chaffotte\n */\npublic interface SRoleUpdateBuilderFactory extends SIdentityUpdateBuilderFactory {\n\n    @Override\n    SRoleUpdateBuilder createNewInstance();\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SUserLogBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\n\n/**\n * @author Yanyan Liu\n */\npublic interface SUserLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction {\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SUserLogBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory;\n\n/**\n * @author Yanyan Liu\n */\npublic interface SUserLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory {\n\n    @Override\n    SUserLogBuilder createNewInstance();\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SUserMembershipLogBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\n\n/**\n * @author Yanyan Liu\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic interface SUserMembershipLogBuilder extends HasCRUDEAction, SPersistenceLogBuilder {\n\n    SUserMembershipLogBuilder roleID(final long roleId);\n\n    SUserMembershipLogBuilder groupId(final long groupId);\n\n    SUserMembershipLogBuilder identityUserId(final long userId);\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SUserMembershipLogBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory;\n\n/**\n * @author Yanyan Liu\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic interface SUserMembershipLogBuilderFactory extends HasCRUDEActionFactory, SPersistenceLogBuilderFactory {\n\n    @Override\n    SUserMembershipLogBuilder createNewInstance();\n\n    String getRoleNameKey();\n\n    String getGroupPathKey();\n\n    String getIdentityUserNameKey();\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SUserMembershipUpdateBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface SUserMembershipUpdateBuilder extends SIdentityUpdateBuilder {\n\n    SUserMembershipUpdateBuilder updateUserId(final long userId);\n\n    SUserMembershipUpdateBuilder updateGroupId(final long groupId);\n\n    SUserMembershipUpdateBuilder updateRoleId(final long roleId);\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SUserMembershipUpdateBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface SUserMembershipUpdateBuilderFactory extends SIdentityUpdateBuilderFactory {\n\n    @Override\n    SUserMembershipUpdateBuilder createNewInstance();\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SUserUpdateBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder;\n\n/**\n * @author Baptiste Mesta\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic interface SUserUpdateBuilder extends SIdentityUpdateBuilder {\n\n    SUserUpdateBuilder updateUserName(final String userName);\n\n    SUserUpdateBuilder updatePassword(final String password);\n\n    SUserUpdateBuilder updateFirstName(final String firstName);\n\n    SUserUpdateBuilder updateLastName(final String lastName);\n\n    SUserUpdateBuilder updateTitle(final String title);\n\n    SUserUpdateBuilder updateJobTitle(final String jobTitle);\n\n    SUserUpdateBuilder updateManagerUserId(final long managerUserId);\n\n    SUserUpdateBuilder updateLastUpdate(final long lastUpdate);\n\n    SUserUpdateBuilder updateLastConnection(final long lastConnection);\n\n    SUserUpdateBuilder updateEnabled(final boolean enabled);\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SUserUpdateBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder;\n\n/**\n * @author Baptiste Mesta\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic interface SUserUpdateBuilderFactory extends SIdentityUpdateBuilderFactory {\n\n    @Override\n    SUserUpdateBuilder createNewInstance();\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SContactInfoLogBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder.impl;\n\nimport org.bonitasoft.engine.identity.model.builder.SContactInfoLogBuilder;\nimport org.bonitasoft.engine.identity.model.builder.SContactInfoLogBuilderFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory;\n\n/**\n * @author Emmanuel Duchastenier\n * @author Matthieu Chaffotte\n */\npublic class SContactInfoLogBuilderFactoryImpl extends CRUDELogBuilderFactory implements SContactInfoLogBuilderFactory {\n\n    public static final int USER_CONTACT_INFO_INDEX = 1;\n\n    public static final int USER_CONTACT_INFO_USERID_INDEX = 2;\n\n    public static final String USER_CONTACT_INFO_INDEX_NAME = \"numericIndex2\";\n\n    public static final String USER_CONTACT_INFO_USERID_INDEX_NAME = \"numericIndex3\";\n\n    @Override\n    public SContactInfoLogBuilder createNewInstance() {\n        return new SContactInfoLogBuilderImpl();\n    }\n\n    @Override\n    public String getObjectIdKey() {\n        return USER_CONTACT_INFO_INDEX_NAME;\n    }\n\n    @Override\n    public String getContactInfoUserIdKey() {\n        return USER_CONTACT_INFO_USERID_INDEX_NAME;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SContactInfoLogBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder.impl;\n\nimport org.bonitasoft.engine.identity.model.builder.SContactInfoLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException;\n\n/**\n * @author Emmanuel Duchastenier\n * @author Matthieu Chaffotte\n */\npublic class SContactInfoLogBuilderImpl extends CRUDELogBuilder implements SContactInfoLogBuilder {\n\n    private static final String PREFIX = \"IDENTITY_USER_CONTACT_INFO\";\n\n    @Override\n    public SPersistenceLogBuilder objectId(final long objectId) {\n        queriableLogBuilder.numericIndex(SContactInfoLogBuilderFactoryImpl.USER_CONTACT_INFO_INDEX, objectId);\n        return this;\n    }\n\n    @Override\n    public SPersistenceLogBuilder setContactInfoUserId(final long userId) {\n        queriableLogBuilder.numericIndex(SContactInfoLogBuilderFactoryImpl.USER_CONTACT_INFO_USERID_INDEX, userId);\n        return this;\n    }\n\n    @Override\n    protected String getActionTypePrefix() {\n        return PREFIX;\n    }\n\n    @Override\n    protected void checkExtraRules(final SQueriableLog log) {\n        if (log.getActionStatus() != SQueriableLog.STATUS_FAIL\n                && log.getNumericIndex(SContactInfoLogBuilderFactoryImpl.USER_CONTACT_INFO_USERID_INDEX) == 0L) {\n            throw new MissingMandatoryFieldsException(\n                    \"Some mandatory fields are missing: Identity Contact info User Id\");\n        }\n    }\n\n    public static SContactInfoLogBuilder getInstance() {\n        return new SContactInfoLogBuilderImpl();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SContactInfoUpdateBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder.impl;\n\nimport org.bonitasoft.engine.identity.model.builder.SContactInfoUpdateBuilder;\nimport org.bonitasoft.engine.identity.model.builder.SContactInfoUpdateBuilderFactory;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Emmanuel Duchastenier\n * @author Matthieu Chaffotte\n */\npublic class SContactInfoUpdateBuilderFactoryImpl implements SContactInfoUpdateBuilderFactory {\n\n    @Override\n    public SContactInfoUpdateBuilder createNewInstance() {\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        return new SContactInfoUpdateBuilderImpl(descriptor);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SContactInfoUpdateBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder.impl;\n\nimport org.bonitasoft.engine.identity.model.SContactInfo;\nimport org.bonitasoft.engine.identity.model.builder.SContactInfoUpdateBuilder;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Emmanuel Duchastenier\n * @author Matthieu Chaffotte\n */\npublic class SContactInfoUpdateBuilderImpl implements SContactInfoUpdateBuilder {\n\n    private final EntityUpdateDescriptor descriptor;\n\n    public SContactInfoUpdateBuilderImpl(final EntityUpdateDescriptor descriptor) {\n        super();\n        this.descriptor = descriptor;\n    }\n\n    @Override\n    public EntityUpdateDescriptor done() {\n        return descriptor;\n    }\n\n    @Override\n    public SContactInfoUpdateBuilder updateEmail(final String email) {\n        descriptor.addField(SContactInfo.EMAIL, email);\n        return this;\n    }\n\n    @Override\n    public SContactInfoUpdateBuilder updatePhoneNumber(final String phoneNumber) {\n        descriptor.addField(SContactInfo.PHONE_NUMBER, phoneNumber);\n        return this;\n    }\n\n    @Override\n    public SContactInfoUpdateBuilder updateMobileNumber(final String mobileNumber) {\n        descriptor.addField(SContactInfo.MOBILE_NUMBER, mobileNumber);\n        return this;\n    }\n\n    @Override\n    public SContactInfoUpdateBuilder updateFaxNumber(final String faxNumber) {\n        descriptor.addField(SContactInfo.FAX_NUMBER, faxNumber);\n        return this;\n    }\n\n    @Override\n    public SContactInfoUpdateBuilder updateBuilding(final String building) {\n        descriptor.addField(SContactInfo.BUILDING, building);\n        return this;\n    }\n\n    @Override\n    public SContactInfoUpdateBuilder updateRoom(final String room) {\n        descriptor.addField(SContactInfo.ROOM, room);\n        return this;\n    }\n\n    @Override\n    public SContactInfoUpdateBuilder updateAddress(final String address) {\n        descriptor.addField(SContactInfo.ADDRESS, address);\n        return this;\n    }\n\n    @Override\n    public SContactInfoUpdateBuilder updateZipCode(final String zipCode) {\n        descriptor.addField(SContactInfo.ZIP_CODE, zipCode);\n        return this;\n    }\n\n    @Override\n    public SContactInfoUpdateBuilder updateCity(final String city) {\n        descriptor.addField(SContactInfo.CITY, city);\n        return this;\n    }\n\n    @Override\n    public SContactInfoUpdateBuilder updateState(final String state) {\n        descriptor.addField(SContactInfo.STATE, state);\n        return this;\n    }\n\n    @Override\n    public SContactInfoUpdateBuilder updateCountry(final String country) {\n        descriptor.addField(SContactInfo.COUNTRY, country);\n        return this;\n    }\n\n    @Override\n    public SContactInfoUpdateBuilder updateWebsite(final String website) {\n        descriptor.addField(SContactInfo.WEBSITE, website);\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SCustomUserInfoDefinitionLogBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder.impl;\n\nimport org.bonitasoft.engine.identity.model.builder.SCustomUserInfoDefinitionLogBuilder;\nimport org.bonitasoft.engine.identity.model.builder.SCustomUserInfoDefinitionLogBuilderFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory;\n\n/**\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n */\npublic class SCustomUserInfoDefinitionLogBuilderFactoryImpl extends CRUDELogBuilderFactory\n        implements SCustomUserInfoDefinitionLogBuilderFactory {\n\n    public static final int SPROFILE_METADATA_DEFINITION_INDEX = 1;\n\n    public static final String SPROFILE_METADATA_DEFINITION_INDEX_NAME = \"numericIndex2\";\n\n    @Override\n    public SCustomUserInfoDefinitionLogBuilder createNewInstance() {\n        return new SCustomUserInfoDefinitionLogBuilderImpl();\n    }\n\n    @Override\n    public String getObjectIdKey() {\n        return SPROFILE_METADATA_DEFINITION_INDEX_NAME;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SCustomUserInfoDefinitionLogBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder.impl;\n\nimport org.bonitasoft.engine.identity.model.builder.SCustomUserInfoDefinitionLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException;\n\n/**\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n */\npublic class SCustomUserInfoDefinitionLogBuilderImpl extends CRUDELogBuilder\n        implements SCustomUserInfoDefinitionLogBuilder {\n\n    private static final String PREFIX = \"IDENTITY_CUSTOM_USER_INFO_DEFINITION\";\n\n    @Override\n    public SPersistenceLogBuilder objectId(final long objectId) {\n        queriableLogBuilder.numericIndex(\n                SCustomUserInfoDefinitionLogBuilderFactoryImpl.SPROFILE_METADATA_DEFINITION_INDEX, objectId);\n        return this;\n    }\n\n    @Override\n    protected String getActionTypePrefix() {\n        return PREFIX;\n    }\n\n    @Override\n    protected void checkExtraRules(final SQueriableLog log) {\n        if (log.getActionStatus() != SQueriableLog.STATUS_FAIL && log.getNumericIndex(\n                SCustomUserInfoDefinitionLogBuilderFactoryImpl.SPROFILE_METADATA_DEFINITION_INDEX) == 0L) {\n            throw new MissingMandatoryFieldsException(\n                    \"Some mandatory fields are missing: \" + \"Identity SCustomUserInfoDefinition Id\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SCustomUserInfoDefinitionUpdateBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder.impl;\n\nimport org.bonitasoft.engine.identity.model.builder.SCustomUserInfoDefinitionUpdateBuilder;\nimport org.bonitasoft.engine.identity.model.builder.SCustomUserInfoDefinitionUpdateBuilderFactory;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic class SCustomUserInfoDefinitionUpdateBuilderFactoryImpl\n        implements SCustomUserInfoDefinitionUpdateBuilderFactory {\n\n    @Override\n    public SCustomUserInfoDefinitionUpdateBuilder createNewInstance() {\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        return new SCustomUserInfoDefinitionUpdateBuilderImpl(descriptor);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SCustomUserInfoDefinitionUpdateBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder.impl;\n\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.model.builder.SCustomUserInfoDefinitionUpdateBuilder;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic class SCustomUserInfoDefinitionUpdateBuilderImpl implements SCustomUserInfoDefinitionUpdateBuilder {\n\n    private final EntityUpdateDescriptor descriptor;\n\n    public SCustomUserInfoDefinitionUpdateBuilderImpl(final EntityUpdateDescriptor descriptor) {\n        super();\n        this.descriptor = descriptor;\n    }\n\n    @Override\n    public EntityUpdateDescriptor done() {\n        return this.descriptor;\n    }\n\n    @Override\n    public SCustomUserInfoDefinitionUpdateBuilder updateName(final String name) {\n        this.descriptor.addField(SCustomUserInfoDefinition.NAME, name);\n        return this;\n    }\n\n    @Override\n    public SCustomUserInfoDefinitionUpdateBuilder updateDisplayName(final String displayName) {\n        this.descriptor.addField(SCustomUserInfoDefinition.DISPLAY_NAME, displayName);\n        return this;\n    }\n\n    @Override\n    public SCustomUserInfoDefinitionUpdateBuilder updateDescription(final String description) {\n        this.descriptor.addField(SCustomUserInfoDefinition.DESCRIPTION, description);\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SCustomUserInfoValueUpdateBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder.impl;\n\nimport org.bonitasoft.engine.identity.model.builder.SCustomUserInfoValueUpdateBuilder;\nimport org.bonitasoft.engine.identity.model.builder.SCustomUserInfoValueUpdateBuilderFactory;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic class SCustomUserInfoValueUpdateBuilderFactoryImpl implements SCustomUserInfoValueUpdateBuilderFactory {\n\n    @Override\n    public SCustomUserInfoValueUpdateBuilder createNewInstance() {\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        return new SCustomUserInfoValueUpdateBuilderImpl(descriptor);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SCustomUserInfoValueUpdateBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder.impl;\n\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoValue;\nimport org.bonitasoft.engine.identity.model.builder.SCustomUserInfoValueUpdateBuilder;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic class SCustomUserInfoValueUpdateBuilderImpl implements SCustomUserInfoValueUpdateBuilder {\n\n    private final EntityUpdateDescriptor descriptor;\n\n    public SCustomUserInfoValueUpdateBuilderImpl(final EntityUpdateDescriptor descriptor) {\n        super();\n        this.descriptor = descriptor;\n    }\n\n    @Override\n    public EntityUpdateDescriptor done() {\n        return this.descriptor;\n    }\n\n    @Override\n    public SCustomUserInfoValueUpdateBuilder updateValue(final String value) {\n        this.descriptor.addField(SCustomUserInfoValue.VALUE, value);\n        return this;\n    }\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SGroupLogBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder.impl;\n\nimport org.bonitasoft.engine.identity.model.builder.SGroupLogBuilder;\nimport org.bonitasoft.engine.identity.model.builder.SGroupLogBuilderFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory;\n\n/**\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n */\npublic class SGroupLogBuilderFactoryImpl extends CRUDELogBuilderFactory implements SGroupLogBuilderFactory {\n\n    public static final int GROUP_INDEX = 2;\n\n    public static final String GROUP_INDEX_NAME = \"numericIndex3\";\n\n    @Override\n    public SGroupLogBuilder createNewInstance() {\n        return new SGroupLogBuilderImpl();\n    }\n\n    @Override\n    public String getObjectIdKey() {\n        return GROUP_INDEX_NAME;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SGroupLogBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder.impl;\n\nimport org.bonitasoft.engine.identity.model.builder.SGroupLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException;\n\n/**\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n */\npublic class SGroupLogBuilderImpl extends CRUDELogBuilder implements SGroupLogBuilder {\n\n    private static final String PREFIX = \"IDENTITY_GROUP\";\n\n    @Override\n    public SPersistenceLogBuilder objectId(final long objectId) {\n        queriableLogBuilder.numericIndex(SGroupLogBuilderFactoryImpl.GROUP_INDEX, objectId);\n        return this;\n    }\n\n    @Override\n    protected String getActionTypePrefix() {\n        return PREFIX;\n    }\n\n    @Override\n    protected void checkExtraRules(final SQueriableLog log) {\n        if (log.getActionStatus() != SQueriableLog.STATUS_FAIL\n                && log.getNumericIndex(SGroupLogBuilderFactoryImpl.GROUP_INDEX) == 0L) {\n            throw new MissingMandatoryFieldsException(\"Some mandatory fields are missing: \" + \"Identity Group Id\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SGroupUpdateBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder.impl;\n\nimport org.bonitasoft.engine.identity.model.builder.SGroupUpdateBuilder;\nimport org.bonitasoft.engine.identity.model.builder.SGroupUpdateBuilderFactory;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic class SGroupUpdateBuilderFactoryImpl implements SGroupUpdateBuilderFactory {\n\n    @Override\n    public SGroupUpdateBuilder createNewInstance() {\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        return new SGroupUpdateBuilderImpl(descriptor);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SGroupUpdateBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder.impl;\n\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.identity.model.builder.SGroupUpdateBuilder;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic class SGroupUpdateBuilderImpl implements SGroupUpdateBuilder {\n\n    private static final String PARENT_PATH = \"parentPath\";\n\n    private final EntityUpdateDescriptor descriptor;\n\n    public SGroupUpdateBuilderImpl(final EntityUpdateDescriptor descriptor) {\n        super();\n        this.descriptor = descriptor;\n    }\n\n    @Override\n    public EntityUpdateDescriptor done() {\n        return descriptor;\n    }\n\n    @Override\n    public SGroupUpdateBuilder updateName(final String name) {\n        descriptor.addField(SGroup.NAME, name);\n        return this;\n    }\n\n    @Override\n    public SGroupUpdateBuilder updateDisplayName(final String displayName) {\n        descriptor.addField(SGroup.DISPLAY_NAME, displayName);\n        return this;\n    }\n\n    @Override\n    public SGroupUpdateBuilder updateDescription(final String description) {\n        descriptor.addField(SGroup.DESCRIPTION, description);\n        return this;\n    }\n\n    @Override\n    public SGroupUpdateBuilder updateParentPath(final String parentPath) {\n        descriptor.addField(PARENT_PATH, parentPath);\n        return this;\n    }\n\n    @Override\n    public SGroupUpdateBuilder updateCreatedBy(final long createdBy) {\n        descriptor.addField(SGroup.CREATED_BY, createdBy);\n        return this;\n    }\n\n    @Override\n    public SGroupUpdateBuilder updateCreationDate(final long creationDate) {\n        descriptor.addField(SGroup.CREATION_DATE, creationDate);\n        return this;\n    }\n\n    @Override\n    public SGroupUpdateBuilder updateLastUpdate(final long lastUpdate) {\n        descriptor.addField(SGroup.LAST_UPDATE, lastUpdate);\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SRoleLogBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder.impl;\n\nimport org.bonitasoft.engine.identity.model.builder.SRoleLogBuilder;\nimport org.bonitasoft.engine.identity.model.builder.SRoleLogBuilderFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory;\n\n/**\n * @author Yanyan Liu\n * @author Matthieu Chafotte\n */\npublic class SRoleLogBuilderFactoryImpl extends CRUDELogBuilderFactory implements SRoleLogBuilderFactory {\n\n    public static final int ROLE_INDEX = 0;\n\n    public static final String ROLE_INDEX_NAME = \"numericIndex1\";\n\n    @Override\n    public SRoleLogBuilder createNewInstance() {\n        return new SRoleLogBuilderImpl();\n    }\n\n    @Override\n    public String getObjectIdKey() {\n        return ROLE_INDEX_NAME;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SRoleLogBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder.impl;\n\nimport org.bonitasoft.engine.identity.model.builder.SRoleLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException;\n\n/**\n * @author Yanyan Liu\n * @author Matthieu Chafotte\n */\npublic class SRoleLogBuilderImpl extends CRUDELogBuilder implements SRoleLogBuilder {\n\n    private static final String PREFIX = \"IDENTITY_ROLE\";\n\n    @Override\n    protected String getActionTypePrefix() {\n        return PREFIX;\n    }\n\n    @Override\n    protected void checkExtraRules(final SQueriableLog log) {\n        if (log.getActionStatus() != SQueriableLog.STATUS_FAIL\n                && log.getNumericIndex(SRoleLogBuilderFactoryImpl.ROLE_INDEX) == 0L) {\n            throw new MissingMandatoryFieldsException(\"Some mandatory fields are missing: \" + \"Identity Role Id\");\n        }\n    }\n\n    @Override\n    public SPersistenceLogBuilder objectId(final long objectId) {\n        queriableLogBuilder.numericIndex(SRoleLogBuilderFactoryImpl.ROLE_INDEX, objectId);\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SRoleUpdateBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder.impl;\n\nimport org.bonitasoft.engine.identity.model.builder.SRoleUpdateBuilder;\nimport org.bonitasoft.engine.identity.model.builder.SRoleUpdateBuilderFactory;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Baptiste Mesta\n * @author Bole Zhang\n * @author Matthieu Chaffotte\n */\npublic class SRoleUpdateBuilderFactoryImpl implements SRoleUpdateBuilderFactory {\n\n    @Override\n    public SRoleUpdateBuilder createNewInstance() {\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        return new SRoleUpdateBuilderImpl(descriptor);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SRoleUpdateBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder.impl;\n\nimport org.bonitasoft.engine.identity.model.SRole;\nimport org.bonitasoft.engine.identity.model.builder.SRoleUpdateBuilder;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Baptiste Mesta\n * @author Bole Zhang\n * @author Matthieu Chaffotte\n */\npublic class SRoleUpdateBuilderImpl implements SRoleUpdateBuilder {\n\n    private final EntityUpdateDescriptor descriptor;\n\n    public SRoleUpdateBuilderImpl(final EntityUpdateDescriptor descriptor) {\n        super();\n        this.descriptor = descriptor;\n    }\n\n    @Override\n    public EntityUpdateDescriptor done() {\n        return descriptor;\n    }\n\n    @Override\n    public SRoleUpdateBuilder updateName(final String name) {\n        descriptor.addField(SRole.NAME, name);\n        return this;\n    }\n\n    @Override\n    public SRoleUpdateBuilder updateDisplayName(final String displayName) {\n        descriptor.addField(SRole.DISPLAY_NAME, displayName);\n        return this;\n    }\n\n    @Override\n    public SRoleUpdateBuilder updateDescription(final String description) {\n        descriptor.addField(SRole.DESCRIPTION, description);\n        return this;\n    }\n\n    @Override\n    public SRoleUpdateBuilder updateCreatedBy(final long createdBy) {\n        descriptor.addField(SRole.CREATED_BY, createdBy);\n        return this;\n    }\n\n    @Override\n    public SRoleUpdateBuilder updateCreationDate(final long creationDate) {\n        descriptor.addField(SRole.CREATION_DATE, creationDate);\n        return this;\n    }\n\n    @Override\n    public SRoleUpdateBuilder updateLastUpdate(final long lastUpdate) {\n        descriptor.addField(SRole.LAST_UPDATE, lastUpdate);\n        return this;\n    }\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SUserLogBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder.impl;\n\nimport org.bonitasoft.engine.identity.model.builder.SUserLogBuilder;\nimport org.bonitasoft.engine.identity.model.builder.SUserLogBuilderFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory;\n\n/**\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n */\npublic class SUserLogBuilderFactoryImpl extends CRUDELogBuilderFactory implements SUserLogBuilderFactory {\n\n    public static final int USER_INDEX = 1;\n\n    public static final String USER_INDEX_NAME = \"numericIndex2\";\n\n    @Override\n    public SUserLogBuilder createNewInstance() {\n        return new SUserLogBuilderImpl();\n    }\n\n    @Override\n    public String getObjectIdKey() {\n        return USER_INDEX_NAME;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SUserLogBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder.impl;\n\nimport org.bonitasoft.engine.identity.model.builder.SUserLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException;\n\n/**\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n */\npublic class SUserLogBuilderImpl extends CRUDELogBuilder implements SUserLogBuilder {\n\n    private static final String PREFIX = \"IDENTITY_USER\";\n\n    @Override\n    public SPersistenceLogBuilder objectId(final long objectId) {\n        queriableLogBuilder.numericIndex(SUserLogBuilderFactoryImpl.USER_INDEX, objectId);\n        return this;\n    }\n\n    @Override\n    protected String getActionTypePrefix() {\n        return PREFIX;\n    }\n\n    @Override\n    protected void checkExtraRules(final SQueriableLog log) {\n        if (log.getActionStatus() != SQueriableLog.STATUS_FAIL\n                && log.getNumericIndex(SUserLogBuilderFactoryImpl.USER_INDEX) == 0L) {\n            throw new MissingMandatoryFieldsException(\"Some mandatory fields are missing: Identity User Id\");\n        }\n    }\n\n    public static SUserLogBuilder getInstance() {\n        return new SUserLogBuilderImpl();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SUserMembershipLogBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder.impl;\n\nimport org.bonitasoft.engine.identity.model.builder.SUserMembershipLogBuilder;\nimport org.bonitasoft.engine.identity.model.builder.SUserMembershipLogBuilderFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory;\n\n/**\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n */\npublic class SUserMembershipLogBuilderFactoryImpl extends CRUDELogBuilderFactory\n        implements SUserMembershipLogBuilderFactory {\n\n    public static final int USER_MEMBERSHIP_INDEX = 4;\n\n    public static final String USER_MEMBERSHIP_INDEX_NAME = \"numericIndex5\";\n\n    public static final int ROLE_INDEX = 3;\n\n    public static final String ROLE_INDEX_NAME = \"textualIndex4\";\n\n    public static final int USER_INDEX = 1;\n\n    public static final String USER_INDEX_NAME = \"textualIndex2\";\n\n    public static final int GROUP_INDEX = 2;\n\n    public static final String GROUP_INDEX_NAME = \"textualIndex3\";\n\n    @Override\n    public SUserMembershipLogBuilder createNewInstance() {\n        return new SUserMembershipLogBuilderImpl();\n    }\n\n    @Override\n    public String getObjectIdKey() {\n        return USER_MEMBERSHIP_INDEX_NAME;\n    }\n\n    @Override\n    public String getRoleNameKey() {\n        return ROLE_INDEX_NAME;\n    }\n\n    @Override\n    public String getIdentityUserNameKey() {\n        return USER_INDEX_NAME;\n    }\n\n    @Override\n    public String getGroupPathKey() {\n        return GROUP_INDEX_NAME;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SUserMembershipLogBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder.impl;\n\nimport org.bonitasoft.engine.identity.model.builder.SUserMembershipLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException;\n\n/**\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n */\npublic class SUserMembershipLogBuilderImpl extends CRUDELogBuilder implements SUserMembershipLogBuilder {\n\n    private static final String PREFIX = \"IDENTITY_USER_MEMBERSHIP\";\n\n    @Override\n    public SPersistenceLogBuilder objectId(final long objectId) {\n        queriableLogBuilder.numericIndex(SUserMembershipLogBuilderFactoryImpl.USER_MEMBERSHIP_INDEX, objectId);\n        return this;\n    }\n\n    @Override\n    protected String getActionTypePrefix() {\n        return PREFIX;\n    }\n\n    @Override\n    protected void checkExtraRules(final SQueriableLog log) {\n        if (log.getActionStatus() != SQueriableLog.STATUS_FAIL\n                && log.getNumericIndex(SUserMembershipLogBuilderFactoryImpl.USER_MEMBERSHIP_INDEX) == 0L) {\n            throw new MissingMandatoryFieldsException(\"Some mandatory fields are missing: \" + \"UserMembership Id\");\n        }\n\n        if (log.getNumericIndex(SUserMembershipLogBuilderFactoryImpl.ROLE_INDEX) == 0L) {\n            throw new MissingMandatoryFieldsException(\"Some mandatory fields are missing: \" + \"Role Id\");\n        }\n\n        if (log.getNumericIndex(SUserMembershipLogBuilderFactoryImpl.USER_INDEX) == 0L) {\n            throw new MissingMandatoryFieldsException(\"Some mandatory fields are missing: \" + \"User Id\");\n        }\n    }\n\n    @Override\n    public SUserMembershipLogBuilder roleID(final long roleId) {\n        queriableLogBuilder.numericIndex(SUserMembershipLogBuilderFactoryImpl.ROLE_INDEX, roleId);\n        return this;\n    }\n\n    @Override\n    public SUserMembershipLogBuilder identityUserId(final long userId) {\n        queriableLogBuilder.numericIndex(SUserMembershipLogBuilderFactoryImpl.USER_INDEX, userId);\n        return this;\n    }\n\n    @Override\n    public SUserMembershipLogBuilder groupId(final long groupId) {\n        queriableLogBuilder.numericIndex(SUserMembershipLogBuilderFactoryImpl.GROUP_INDEX, groupId);\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SUserMembershipUpdateBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder.impl;\n\nimport org.bonitasoft.engine.identity.model.builder.SUserMembershipUpdateBuilder;\nimport org.bonitasoft.engine.identity.model.builder.SUserMembershipUpdateBuilderFactory;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic class SUserMembershipUpdateBuilderFactoryImpl implements SUserMembershipUpdateBuilderFactory {\n\n    @Override\n    public SUserMembershipUpdateBuilder createNewInstance() {\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        return new SUserMembershipUpdateBuilderImpl(descriptor);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SUserMembershipUpdateBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder.impl;\n\nimport org.bonitasoft.engine.identity.model.SUserMembership;\nimport org.bonitasoft.engine.identity.model.builder.SUserMembershipUpdateBuilder;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic class SUserMembershipUpdateBuilderImpl implements SUserMembershipUpdateBuilder {\n\n    private final EntityUpdateDescriptor descriptor;\n\n    public SUserMembershipUpdateBuilderImpl(final EntityUpdateDescriptor descriptor) {\n        super();\n        this.descriptor = descriptor;\n    }\n\n    @Override\n    public EntityUpdateDescriptor done() {\n        return descriptor;\n    }\n\n    @Override\n    public SUserMembershipUpdateBuilder updateUserId(final long userId) {\n        descriptor.addField(SUserMembership.USER_ID, userId);\n        return this;\n    }\n\n    @Override\n    public SUserMembershipUpdateBuilder updateGroupId(final long groupId) {\n        descriptor.addField(SUserMembership.GROUP_ID, groupId);\n        return this;\n    }\n\n    @Override\n    public SUserMembershipUpdateBuilder updateRoleId(final long roleId) {\n        descriptor.addField(SUserMembership.ROLE_ID, roleId);\n        return this;\n    }\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SUserUpdateBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder.impl;\n\nimport org.bonitasoft.engine.identity.model.builder.SUserUpdateBuilder;\nimport org.bonitasoft.engine.identity.model.builder.SUserUpdateBuilderFactory;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Baptiste Mesta\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class SUserUpdateBuilderFactoryImpl implements SUserUpdateBuilderFactory {\n\n    @Override\n    public SUserUpdateBuilder createNewInstance() {\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        return new SUserUpdateBuilderImpl(descriptor);\n    }\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SUserUpdateBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model.builder.impl;\n\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.identity.model.builder.SUserUpdateBuilder;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Baptiste Mesta\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class SUserUpdateBuilderImpl implements SUserUpdateBuilder {\n\n    private final EntityUpdateDescriptor descriptor;\n\n    public SUserUpdateBuilderImpl(final EntityUpdateDescriptor descriptor) {\n        super();\n        this.descriptor = descriptor;\n    }\n\n    public static SUserUpdateBuilder updateBuilder() {\n        return new SUserUpdateBuilderImpl(new EntityUpdateDescriptor());\n    }\n\n    @Override\n    public EntityUpdateDescriptor done() {\n        return descriptor;\n    }\n\n    @Override\n    public SUserUpdateBuilder updateUserName(final String username) {\n        descriptor.addField(SUser.USER_NAME, username);\n        return this;\n    }\n\n    @Override\n    public SUserUpdateBuilder updatePassword(final String password) {\n        descriptor.addField(SUser.PASSWORD, password);\n        return this;\n    }\n\n    @Override\n    public SUserUpdateBuilder updateFirstName(final String firstName) {\n        descriptor.addField(SUser.FIRST_NAME, firstName);\n        return this;\n    }\n\n    @Override\n    public SUserUpdateBuilder updateLastName(final String lastName) {\n        descriptor.addField(SUser.LAST_NAME, lastName);\n        return this;\n    }\n\n    @Override\n    public SUserUpdateBuilder updateTitle(final String title) {\n        descriptor.addField(SUser.TITLE, title);\n        return this;\n    }\n\n    @Override\n    public SUserUpdateBuilder updateJobTitle(final String jobTitle) {\n        descriptor.addField(SUser.JOB_TITLE, jobTitle);\n        return this;\n    }\n\n    @Override\n    public SUserUpdateBuilder updateManagerUserId(final long managerUserId) {\n        descriptor.addField(SUser.MANAGER_USER_ID, managerUserId);\n        return this;\n    }\n\n    @Override\n    public SUserUpdateBuilder updateLastUpdate(final long lastUpdate) {\n        descriptor.addField(SUser.LAST_UPDATE, lastUpdate);\n        return this;\n    }\n\n    @Override\n    public SUserUpdateBuilder updateLastConnection(final long lastConnection) {\n        descriptor.addField(SUser.LAST_CONNECTION, lastConnection);\n        return this;\n    }\n\n    @Override\n    public SUserUpdateBuilder updateEnabled(final boolean enabled) {\n        descriptor.addField(SUser.ENABLED, enabled);\n        return this;\n    }\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/package-info.java",
    "content": "/**\n * Copyright (C) 2015 BonitaSoft S.A.\n * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\n/**\n * <p>Manages information about an organization, that is, the set of users who can act in processes. Handles creation,\n * modification, and deletion of\n * organizations, groups, roles, memberships, and users.</p>\n * <p>The identity service relies upon database access and produce database access event\n * {@link org.bonitasoft.engine.events.EventService}</p>\n *\n * @see org.bonitasoft.engine.identity.IdentityService\n * @since 6.0.0\n */\n\npackage org.bonitasoft.engine.identity;\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/recorder/SelectDescriptorBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.recorder;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.identity.model.SContactInfo;\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.identity.model.SRole;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.identity.model.SUserMembership;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\n\n/**\n * @author Matthieu Chaffotte\n * @author Bole Zhang\n */\npublic class SelectDescriptorBuilder {\n\n    public static SelectListDescriptor<SGroup> getChildrenOfGroup(final SGroup group, final int fromIndex,\n            final int numberOfGroups) {\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfGroups, SGroup.class, \"id\",\n                OrderByType.ASC);\n        return getChildrenOfGroup(group, queryOptions);\n    }\n\n    public static SelectListDescriptor<SGroup> getChildrenOfGroup(final SGroup group, final QueryOptions queryOptions) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"groupPath\", group.getPath());\n        return new SelectListDescriptor<>(\"getChildrenOfGroup\", parameters, SGroup.class, queryOptions);\n    }\n\n    public static SelectListDescriptor<SGroup> getChildrenOfGroup(final SGroup group, final String field,\n            final OrderByType order, final int fromIndex,\n            final int numberOfGroups) {\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfGroups, SGroup.class, field, order);\n        return getChildrenOfGroup(group, queryOptions);\n    }\n\n    public static <T extends PersistentObject> SelectByIdDescriptor<T> getElementById(final Class<T> clazz,\n            final String elementName, final long id) {\n        return new SelectByIdDescriptor<>(clazz, id);\n    }\n\n    public static <T extends PersistentObject> SelectListDescriptor<T> getElements(final Class<T> clazz,\n            final String elementName, final int fromIndex,\n            final int numberOfElements) {\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfElements);\n        return getElements(clazz, elementName, queryOptions);\n    }\n\n    public static <T extends PersistentObject> SelectListDescriptor<T> getElements(final Class<T> clazz,\n            final String elementName,\n            final QueryOptions queryOptions) {\n        final Map<String, Object> parameters = Collections.emptyMap();\n        return new SelectListDescriptor<>(\"get\" + elementName + \"s\", parameters, clazz, queryOptions);\n    }\n\n    public static <T extends PersistentObject> SelectListDescriptor<T> getElements(final Class<T> clazz,\n            final String elementName, final String field,\n            final OrderByType order, final int fromIndex, final int numberOfElements) {\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfElements, clazz, field, order);\n        return getElements(clazz, elementName, queryOptions);\n    }\n\n    public static <T extends PersistentObject> SelectListDescriptor<T> getElementsByIds(final Class<T> clazz,\n            final String elementName,\n            final Collection<Long> ids) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"ids\", ids);\n        final int maxResults = ids != null ? ids.size() : 0;\n        return new SelectListDescriptor<>(\"get\" + elementName + \"sByIds\", parameters, clazz,\n                new QueryOptions(0, maxResults));\n    }\n\n    public static SelectOneDescriptor<SGroup> getGroupByName(final String groupName) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"name\", groupName);\n        return new SelectOneDescriptor<>(\"getGroupByName\", parameters, SGroup.class);\n    }\n\n    public static SelectOneDescriptor<SGroup> getGroupByPath(final String parentPath, final String groupName) {\n        final Map<String, Object> parameters = new HashMap<>();\n        parameters.put(\"name\", groupName);\n        parameters.put(\"parentPath\", parentPath);\n        return new SelectOneDescriptor<>(\"getGroupByNameAndPath\", parameters, SGroup.class);\n    }\n\n    public static <T extends PersistentObject> SelectByIdDescriptor<T> getLightElementById(final Class<T> clazz,\n            final String elementName, final long id) {\n        return new SelectByIdDescriptor<>(clazz, id);\n    }\n\n    public static SelectOneDescriptor<SUserMembership> getLightUserMembership(final long userId, final long groupId,\n            final long roleId) {\n        final Map<String, Object> parameters = new HashMap<>();\n        parameters.put(\"userId\", userId);\n        parameters.put(\"roleId\", roleId);\n        parameters.put(\"groupId\", groupId);\n        return new SelectOneDescriptor<>(\"getLightUserMembershipWithIds\", parameters,\n                SUserMembership.class);\n    }\n\n    public static SelectOneDescriptor<SCustomUserInfoDefinition> getCustomUserInfoDefinitionByName(final String name) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"name\", name);\n        return new SelectOneDescriptor<>(\"getCustomUserInfoDefinitionByName\", parameters,\n                SCustomUserInfoDefinition.class);\n    }\n\n    public static SelectOneDescriptor<Long> getNumberOfElement(final String elementName,\n            final Class<? extends PersistentObject> clazz) {\n        final Map<String, Object> emptyMap = Collections.emptyMap();\n        return new SelectOneDescriptor<>(\"getNumberOf\" + elementName, emptyMap, clazz, Long.class);\n    }\n\n    public static SelectOneDescriptor<Long> getNumberOfGroupChildren(final String groupParentPath) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"parentPath\", groupParentPath);\n        return new SelectOneDescriptor<>(\"getNumberOfGroupChildren\", parameters, SGroup.class, Long.class);\n    }\n\n    public static SelectOneDescriptor<Long> getNumberOfUserMembershipsOfUser(final long userId) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"userId\", userId);\n        return new SelectOneDescriptor<>(\"getNumberOfUserMembershipsOfUser\", parameters, SUserMembership.class,\n                Long.class);\n    }\n\n    public static SelectOneDescriptor<Long> getNumberOfUsersByGroup(final long groupId) {\n        final Map<String, Object> parameters = new HashMap<>();\n        parameters.put(\"groupId\", groupId);\n        return new SelectOneDescriptor<>(\"getNumberOfUsersByGroup\", parameters, SUser.class, Long.class);\n    }\n\n    public static SelectOneDescriptor<Long> getNumberOfUsersByMembership(final long groupId, final long roleId) {\n        final Map<String, Object> parameters = new HashMap<>();\n        parameters.put(\"roleId\", roleId);\n        parameters.put(\"groupId\", groupId);\n        return new SelectOneDescriptor<>(\"getNumberOfUsersByMembership\", parameters, SUser.class, Long.class);\n    }\n\n    public static SelectOneDescriptor<Long> getNumberOfUsersByRole(final long roleId) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"roleId\", roleId);\n        return new SelectOneDescriptor<>(\"getNumberOfUsersByRole\", parameters, SUser.class, Long.class);\n    }\n\n    public static SelectOneDescriptor<SRole> getRoleByName(final String roleName) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"name\", roleName);\n        return new SelectOneDescriptor<>(\"getRoleByName\", parameters, SRole.class);\n    }\n\n    public static SelectOneDescriptor<SUser> getUserByUserName(final String userName) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"userName\", userName);\n        return new SelectOneDescriptor<>(\"getUserByUserName\", parameters, SUser.class);\n    }\n\n    public static SelectOneDescriptor<SContactInfo> getUserContactInfo(final long userId, final boolean isPersonal) {\n        final Map<String, Object> parameters = new HashMap<>();\n        parameters.put(\"userId\", userId);\n        parameters.put(\"personal\", isPersonal);\n        return new SelectOneDescriptor<>(\"getUserContactInfo\", parameters, SContactInfo.class);\n    }\n\n    public static SelectOneDescriptor<SUserMembership> getUserMembership(final long userId, final long groupId,\n            final long roleId) {\n        final Map<String, Object> parameters = new HashMap<>();\n        parameters.put(\"userId\", userId);\n        parameters.put(\"roleId\", roleId);\n        parameters.put(\"groupId\", groupId);\n        return new SelectOneDescriptor<>(\"getUserMembershipWithIds\", parameters, SUserMembership.class);\n    }\n\n    public static SelectListDescriptor<SUserMembership> getUserMembershipsByGroup(final long groupId,\n            final int startIndex, final int maxResults) {\n        final Map<String, Object> parameters = new HashMap<>();\n        parameters.put(\"groupId\", groupId);\n        return new SelectListDescriptor<>(\"getUserMembershipsByGroup\", parameters, SUserMembership.class,\n                new QueryOptions(startIndex,\n                        maxResults));\n    }\n\n    public static SelectListDescriptor<SUserMembership> getUserMembershipsByRole(final long roleId,\n            final int startIndex, final int maxResults) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"roleId\", roleId);\n        return new SelectListDescriptor<>(\"getUserMembershipsByRole\", parameters, SUserMembership.class,\n                new QueryOptions(startIndex, maxResults));\n    }\n\n    public static SelectListDescriptor<SUserMembership> getUserMembershipsOfUser(final long userId, final int fromIndex,\n            final int numberOfMemberships) {\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfMemberships,\n                List.of(new OrderByOption(SUserMembership.class, \"id\",\n                        OrderByType.ASC)));\n        return getUserMembershipsOfUser(userId, queryOptions);\n    }\n\n    public static SelectListDescriptor<SUserMembership> getUserMembershipsOfUser(final long userId,\n            final QueryOptions queryOptions) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"userId\", userId);\n        return new SelectListDescriptor<>(\"getUserMembershipsOfUser\", parameters, SUserMembership.class,\n                queryOptions);\n    }\n\n    public static SelectListDescriptor<SUserMembership> getUserMembershipsOfUser(final long userId, final String field,\n            final OrderByType order,\n            final int fromIndex, final int numberOfMemberships) {\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfMemberships, SUserMembership.class, field,\n                order);\n        return getUserMembershipsOfUser(userId, queryOptions);\n    }\n\n    public static SelectListDescriptor<SUserMembership> getUserMembershipsWithGroup(final QueryOptions queryOptions) {\n        final Map<String, Object> parameters = Collections.emptyMap();\n        return new SelectListDescriptor<>(\"getUserMembershipsWithGroup\", parameters,\n                SUserMembership.class, queryOptions);\n    }\n\n    public static SelectListDescriptor<SUserMembership> getUserMembershipsWithRole(final QueryOptions queryOptions) {\n        final Map<String, Object> parameters = Collections.emptyMap();\n        return new SelectListDescriptor<>(\"getUserMembershipsWithRole\", parameters,\n                SUserMembership.class, queryOptions);\n    }\n\n    public static SelectListDescriptor<SUser> getUsersByGroup(final long groupId, final int fromIndex,\n            final int numberOfUsers) {\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfUsers);\n        return getUsersByGroup(groupId, queryOptions);\n    }\n\n    private static SelectListDescriptor<SUser> getUsersByGroup(final long groupId, final QueryOptions queryOptions) {\n        final Map<String, Object> parameters = new HashMap<>();\n        parameters.put(\"groupId\", groupId);\n        return new SelectListDescriptor<>(\"getUsersInGroup\", parameters, SUser.class, queryOptions);\n    }\n\n    public static SelectListDescriptor<SUser> getActiveUsersByGroup(final long groupId, final int fromIndex,\n            final int numberOfUsers) {\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfUsers);\n        return getActiveUsersByGroup(groupId, queryOptions);\n    }\n\n    public static SelectListDescriptor<SUser> getInactiveUsersByGroup(final long groupId, final int fromIndex,\n            final int numberOfUsers) {\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfUsers);\n        return getInactiveUsersByGroup(groupId, queryOptions);\n    }\n\n    private static SelectListDescriptor<SUser> getInactiveUsersByGroup(final long groupId,\n            final QueryOptions queryOptions) {\n        final Map<String, Object> parameters = new HashMap<>();\n        parameters.put(\"groupId\", groupId);\n        parameters.put(\"enabled\", false);\n        return new SelectListDescriptor<>(\"getUsersInGroupWithEnabledParameter\", parameters, SUser.class, queryOptions);\n    }\n\n    private static SelectListDescriptor<SUser> getActiveUsersByGroup(final long groupId,\n            final QueryOptions queryOptions) {\n        final Map<String, Object> parameters = new HashMap<>();\n        parameters.put(\"groupId\", groupId);\n        parameters.put(\"enabled\", true);\n        return new SelectListDescriptor<>(\"getUsersInGroupWithEnabledParameter\", parameters, SUser.class, queryOptions);\n    }\n\n    public static SelectListDescriptor<SUser> getActiveUsersByGroup(final long groupId, final String field,\n            final OrderByType order, final int fromIndex,\n            final int numberOfUsers) {\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfUsers, SUser.class, field, order);\n        return getActiveUsersByGroup(groupId, queryOptions);\n    }\n\n    public static SelectListDescriptor<SUser> getInactiveUsersByGroup(final long groupId, final String field,\n            final OrderByType order, final int fromIndex,\n            final int numberOfUsers) {\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfUsers, SUser.class, field, order);\n        return getInactiveUsersByGroup(groupId, queryOptions);\n    }\n\n    public static SelectListDescriptor<SUser> getUsersByGroup(final long groupId, final String field,\n            final OrderByType order, final int fromIndex,\n            final int numberOfUsers) {\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfUsers, SUser.class, field, order);\n        return getUsersByGroup(groupId, queryOptions);\n    }\n\n    public static SelectListDescriptor<SUser> getUsersWithManager(final long managerUserId,\n            final QueryOptions queryOptions) {\n        final Map<String, Object> parameters = new HashMap<>();\n        parameters.put(\"managerUserId\", managerUserId);\n        return new SelectListDescriptor<>(\"getUsersWithManager\", parameters, SUser.class, queryOptions);\n    }\n\n    public static SelectListDescriptor<SUser> getActiveUsersWithManager(long managerUserId, QueryOptions queryOptions) {\n        final Map<String, Object> parameters = new HashMap<>();\n        parameters.put(\"managerUserId\", managerUserId);\n        parameters.put(\"enabled\", true);\n        return new SelectListDescriptor<>(\"getUsersWithManagerWithEnabledParameter\", parameters, SUser.class,\n                queryOptions);\n    }\n\n    public static SelectListDescriptor<SUser> getInactiveUsersWithManager(long managerUserId,\n            QueryOptions queryOptions) {\n        final Map<String, Object> parameters = new HashMap<>();\n        parameters.put(\"managerUserId\", managerUserId);\n        parameters.put(\"enabled\", false);\n        return new SelectListDescriptor<>(\"getUsersWithManagerWithEnabledParameter\", parameters, SUser.class,\n                queryOptions);\n    }\n\n    public static SelectListDescriptor<SUser> getUsersByMembership(final long groupId, final long roleId) {\n        return getUsersByMembership(groupId, roleId, null);\n    }\n\n    public static SelectListDescriptor<SUser> getUsersByMembership(final long groupId, final long roleId,\n            final int fromIndex, final int numberOfUsers) {\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfUsers, SUser.class, \"id\",\n                OrderByType.DESC); // FIXME should have \"id\" here\n        return getUsersByMembership(groupId, roleId, queryOptions);\n    }\n\n    private static SelectListDescriptor<SUser> getUsersByMembership(final long groupId, final long roleId,\n            final QueryOptions queryOptions) {\n        final Map<String, Object> parameters = new HashMap<>();\n        parameters.put(\"roleId\", roleId);\n        parameters.put(\"groupId\", groupId);\n        return new SelectListDescriptor<>(\"getUsersByMembership\", parameters, SUser.class, queryOptions);\n    }\n\n    public static SelectListDescriptor<SUser> getUsersByMembership(final long groupId, final long roleId,\n            final String field, final OrderByType order,\n            final int fromIndex, final int numberOfUsers) {\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfUsers, SUser.class, field, order);\n        return getUsersByMembership(groupId, roleId, queryOptions);\n    }\n\n    public static SelectListDescriptor<SUser> getUsersWithRole(final long roleId) {\n        return getUsersWithRole(roleId, null);\n    }\n\n    public static SelectListDescriptor<SUser> getUsersWithRole(final long roleId, final int fromIndex,\n            final int numberOfUsers) {\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfUsers);\n        return getUsersWithRole(roleId, queryOptions);\n    }\n\n    public static SelectListDescriptor<SUser> getActiveUsersWithRole(long roleId, int fromIndex, int numberOfUsers) {\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfUsers);\n        return getActiveUsersWithRole(roleId, queryOptions);\n    }\n\n    public static SelectListDescriptor<SUser> getInactiveUsersWithRole(long roleId, int fromIndex, int numberOfUsers) {\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfUsers);\n        return getInactiveUsersWithRole(roleId, queryOptions);\n    }\n\n    public static SelectListDescriptor<SUser> getUsersWithRole(final long roleId, final QueryOptions queryOptions) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"roleId\", roleId);\n        return new SelectListDescriptor<>(\"getUsersWithRole\", parameters, SUser.class, queryOptions);\n    }\n\n    public static SelectListDescriptor<SUser> getActiveUsersWithRole(long roleId, final QueryOptions queryOptions) {\n        final Map<String, Object> parameters = new HashMap<>();\n        parameters.put(\"roleId\", roleId);\n        parameters.put(\"enabled\", true);\n        return new SelectListDescriptor<>(\"getUsersWithRoleWithEnabledParameter\", parameters, SUser.class,\n                queryOptions);\n    }\n\n    public static SelectListDescriptor<SUser> getInactiveUsersWithRole(long roleId, final QueryOptions queryOptions) {\n        final Map<String, Object> parameters = new HashMap<>();\n        parameters.put(\"roleId\", roleId);\n        parameters.put(\"enabled\", false);\n        return new SelectListDescriptor<>(\"getUsersWithRoleWithEnabledParameter\", parameters, SUser.class,\n                queryOptions);\n    }\n\n    public static SelectListDescriptor<SUser> getUsersWithRole(final long roleId, final String field,\n            final OrderByType order, final int fromIndex,\n            final int numberOfUsers) {\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfUsers, SUser.class, field, order);\n        return getUsersWithRole(roleId, queryOptions);\n    }\n\n    public static SelectListDescriptor<SUser> getActiveUsersWithRole(final long roleId, final String field,\n            final OrderByType order, final int fromIndex,\n            final int numberOfUsers) {\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfUsers, SUser.class, field, order);\n        return getActiveUsersWithRole(roleId, queryOptions);\n    }\n\n    public static SelectListDescriptor<SUser> getInactiveUsersWithRole(final long roleId, final String field,\n            final OrderByType order, final int fromIndex,\n            final int numberOfUsers) {\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfUsers, SUser.class, field, order);\n        return getInactiveUsersWithRole(roleId, queryOptions);\n    }\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/recorder/UserRecordType.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.recorder;\n\n/**\n * List all possible change of elements of the identity service\n *\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic enum UserRecordType {\n\n    DELETE_USER, ADD_USER, UPDATE_USER_PASSWORD, ADD_PROFILE_METADATA_DEFINITION, ADD_PROFILE_METADATA_VALUE, DELETE_PROFILE_METADATA_DEFINITION, DELETE_PROFILE_METADATA_VALUE, DELETE_ROLE, ADD_GROUP, ADD_ROLE, DELETE_GROUP, DELETE_USER_MEMBERSHIP, setUsersMemberships, UPDATE_USER, UPDATE_ROLE, UPDATE_GROUP, ADD_USER_MEMBERSHIP, UPDTAE_PROFILE_METADATA_DEFINITION, UPDATE_PROFILE_METADATA_VALUE, UPDATE_USER_MEMBERSHIP;\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/main/resources/org/bonitasoft/engine/identity/model/impl/hibernate/identity.queries.hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\" \"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd\">\n<hibernate-mapping auto-import=\"false\">\n\n\t<query name=\"getUserByUserName\">\n\t\tSELECT user\n\t\tFROM org.bonitasoft.engine.identity.model.SUser AS user\n\t\tWHERE user.userName = :userName\n\t</query>\n\n\t<query name=\"getAllUsers\">\n\t\tSELECT user\n\t\tFROM org.bonitasoft.engine.identity.model.SUser as user\n\t\tORDER BY user.userName\n\t</query>\n\n\t<query name=\"getAllUsersOderByFirstNameAsc\">\n\t\tSELECT user\n\t\tFROM org.bonitasoft.engine.identity.model.SUser as user\n\t\tORDER BY user.firstName ASC\n\t</query>\n\n\t<query name=\"getAllUsersOderByLastNameAsc\">\n\t\tSELECT user\n\t\tFROM org.bonitasoft.engine.identity.model.SUser as user\n\t\tORDER BY user.lastName ASC\n\t</query>\n\n\t<query name=\"getAllUsersOderByUserNameAsc\">\n\t\tSELECT user\n\t\tFROM org.bonitasoft.engine.identity.model.SUser as user\n\t\tORDER BY user.userName ASC\n\t</query>\n\n\t<query name=\"getAllUsersOderByFirtNameDesc\">\n\t\tSELECT user\n\t\tFROM org.bonitasoft.engine.identity.model.SUser as user\n\t\tORDER BY user.firstName DESC\n\t</query>\n\n\t<query name=\"getAllUsersOderByLastNameDesc\">\n\t\tSELECT user\n\t\tFROM org.bonitasoft.engine.identity.model.SUser as user\n\t\tORDER BY user.lastName DESC\n\t</query>\n\n\t<query name=\"getAllUsersOderByUserNameDesc\">\n\t\tSELECT user\n\t\tFROM org.bonitasoft.engine.identity.model.SUser as user\n\t\tORDER BY user.userName DESC\n\t</query>\n\n\t<query name=\"getRoleByName\">\n\t\tSELECT role\n\t\tFROM org.bonitasoft.engine.identity.model.SRole AS role\n\t\tWHERE role.name = :name\n\t</query>\n\n\t<query name=\"getGroupByName\">\n\t\tSELECT group_\n\t\tFROM org.bonitasoft.engine.identity.model.SGroup AS group_\n\t\tWHERE group_.name = :name\n\t\tAND group_.parentPath IS NULL\n\t</query>\n\n\t<query name=\"getGroupByNameAndPath\">\n\t\tSELECT group_\n\t\tFROM org.bonitasoft.engine.identity.model.SGroup AS group_\n\t\tWHERE group_.name = :name\n\t\tAND group_.parentPath = :parentPath\n\t</query>\n\n\t<query name=\"getCustomUserInfoDefinitionByName\">\n        SELECT custom_usr_inf_def\n        FROM org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition AS custom_usr_inf_def\n        WHERE custom_usr_inf_def.name = :name\n    </query>\n\n\t<!--\tUsers -->\n\t<query name=\"getUsersByIds\">\n\t\tSELECT user\n\t\tFROM org.bonitasoft.engine.identity.model.SUser AS user\n\t\tWHERE user.id IN (:ids)\n\t\tORDER BY user.userName\n\t</query>\n\n\t<query name=\"getUsersByName\">\n\t\tSELECT user\n\t\tFROM org.bonitasoft.engine.identity.model.SUser AS user\n\t\tWHERE user.userName IN (:userNames)\n\t</query>\n\n\t<query name=\"getUsers\">\n\t\tSELECT user\n\t\tFROM org.bonitasoft.engine.identity.model.SUser AS user\n\t</query>\n\n\t<query name=\"getUsersWithRole\">\n\t\tSELECT DISTINCT user\n\t\tFROM org.bonitasoft.engine.identity.model.SUser AS user,\n\t\t     org.bonitasoft.engine.identity.model.SUserMembership AS user_membership\n\t\tWHERE user.id = user_membership.userId\n\t\tAND user_membership.roleId = :roleId\n \t</query>\n\n\t<query name=\"getUsersWithRoleWithEnabledParameter\">\n        SELECT DISTINCT user\n        FROM org.bonitasoft.engine.identity.model.SUser AS user,\n        org.bonitasoft.engine.identity.model.SUserMembership AS user_membership\n        WHERE user.id = user_membership.userId\n        AND user.enabled = :enabled\n        AND user_membership.roleId = :roleId\n    </query>\n\n    <query name=\"getUsersInGroup\">\n        SELECT DISTINCT user\n        FROM org.bonitasoft.engine.identity.model.SUser AS user,\n        org.bonitasoft.engine.identity.model.SUserMembership AS user_membership\n        WHERE user_membership.groupId = :groupId\n        AND user.id = user_membership.userId\n    </query>\n\n    <query name=\"getUsersInGroupWithEnabledParameter\">\n        SELECT DISTINCT user\n        FROM org.bonitasoft.engine.identity.model.SUser AS user,\n        org.bonitasoft.engine.identity.model.SUserMembership AS user_membership\n        WHERE user_membership.groupId = :groupId\n        AND user.enabled = :enabled\n        AND user.id = user_membership.userId\n    </query>\n\n    <query name=\"getUsersByMembership\">\n        SELECT DISTINCT user\n        FROM org.bonitasoft.engine.identity.model.SUser AS user,\n        org.bonitasoft.engine.identity.model.SUserMembership AS user_membership\n        WHERE user_membership.groupId = :groupId\n        AND user_membership.roleId = :roleId\n        AND user.id = user_membership.userId\n    </query>\n\n    <query name=\"getUsersWithManager\">\n        SELECT user\n        FROM org.bonitasoft.engine.identity.model.SUser as user\n        WHERE user.managerUserId = :managerUserId\n    </query>\n\n\t<query name=\"getUsersWithManagerWithEnabledParameter\">\n        SELECT user\n        FROM org.bonitasoft.engine.identity.model.SUser as user\n        WHERE user.managerUserId = :managerUserId\n        AND user.enabled = :enabled\n    </query>\n\n    <query name=\"getUserContactInfo\">\n        SELECT contactinfo\n        FROM org.bonitasoft.engine.identity.model.SContactInfo as contactinfo\n        WHERE userId = :userId\n        AND personal = :personal\n    </query>\n\n\t<!--\t Roles -->\n\t<query name=\"getRoles\">\n\t\tSELECT role\n\t\tFROM org.bonitasoft.engine.identity.model.SRole AS role\n\t</query>\n\n\n\t<query name=\"getRolesByIds\">\n\t\tSELECT role\n\t\tFROM org.bonitasoft.engine.identity.model.SRole AS role\n\t\tWHERE role.id IN (:ids)\n\t\tORDER BY role.name\n\t</query>\n\n\t<!--\t Groups -->\n\t<query name=\"getGroupsByIds\">\n\t\tSELECT group_\n\t\tFROM org.bonitasoft.engine.identity.model.SGroup AS group_\n\t\tWHERE group_.id IN (:ids)\n\t\tORDER BY group_.name\n\t</query>\n\n\t<query name=\"getGroups\">\n\t\tSELECT group_\n\t\tFROM org.bonitasoft.engine.identity.model.SGroup AS group_\n\t</query>\n\n\t<query name=\"getChildrenOfGroup\">\n\t\tSELECT group_\n\t\tFROM org.bonitasoft.engine.identity.model.SGroup AS group_\n\t\tWHERE group_.parentPath = :groupPath\n\t</query>\n\n\t<query name=\"getUserMembershipsByGroup\">\n\t\tSELECT new org.bonitasoft.engine.identity.model.SUserMembership(\n\t\t\tuser_membership.id,\n\t\t\tuser_membership.roleId,\n\t\t\tuser_membership.groupId,\n\t\t\tuser_membership.userId,\n\t\t\tuser_membership.assignedBy,\n\t\t\tuser_membership.assignedDate,\n\t\t\tgroup_.parentPath,\n\t\t\trole.name,\n\t\t\tgroup_.name,\n\t\t\tuser.userName\n\t\t)\n\t\tFROM org.bonitasoft.engine.identity.model.SUserMembership AS user_membership,\n\t\t\torg.bonitasoft.engine.identity.model.SRole AS role,\n\t\t\torg.bonitasoft.engine.identity.model.SGroup AS group_,\n\t\t\torg.bonitasoft.engine.identity.model.SUser AS user\n\t\tWHERE user_membership.roleId = role.id\n\t\tAND user_membership.groupId = group_.id\n\t\tAND user_membership.userId = user.id\n\t\tAND user_membership.groupId = :groupId\n\n\t</query>\n\n\t<query name=\"getUserMembershipsByRole\">\n\t\tSELECT new org.bonitasoft.engine.identity.model.SUserMembership(\n\t\t\tuser_membership.id,\n\t\t\tuser_membership.roleId,\n\t\t\tuser_membership.groupId,\n\t\t\tuser_membership.userId,\n\t\t\tuser_membership.assignedBy,\n\t\t\tuser_membership.assignedDate,\n\t\t\tgroup_.parentPath,\n\t\t\trole.name,\n\t\t\tgroup_.name,\n\t\t\tuser.userName\n\t\t)\n\t\tFROM org.bonitasoft.engine.identity.model.SUserMembership AS user_membership,\n\t\t\torg.bonitasoft.engine.identity.model.SRole AS role,\n\t\t\torg.bonitasoft.engine.identity.model.SGroup AS group_,\n\t\t\torg.bonitasoft.engine.identity.model.SUser AS user\n\t\tWHERE user_membership.roleId = role.id\n\t\tAND user_membership.groupId = group_.id\n\t\tAND user_membership.userId = user.id\n\t\tAND user_membership.roleId = :roleId\n\t</query>\n\n\t<query name=\"getUserMembershipsOfUser\">\n\t\tSELECT new org.bonitasoft.engine.identity.model.SUserMembership(\n\t\t\tuser_membership.id,\n\t\t\tuser_membership.roleId,\n\t\t\tuser_membership.groupId,\n\t\t\tuser_membership.userId,\n\t\t\tuser_membership.assignedBy,\n\t\t\tuser_membership.assignedDate,\n\t\t\tgroup_.parentPath,\n\t\t\trole.name,\n\t\t\tgroup_.name,\n\t\t\tuser.userName\n\t\t)\n\t\tFROM org.bonitasoft.engine.identity.model.SUserMembership AS user_membership,\n\t\t\torg.bonitasoft.engine.identity.model.SRole AS role,\n\t\t\torg.bonitasoft.engine.identity.model.SGroup AS group_,\n\t\t\torg.bonitasoft.engine.identity.model.SUser AS user\n\t\tWHERE user_membership.roleId = role.id\n\t\tAND user_membership.groupId = group_.id\n\t\tAND user_membership.userId = user.id\n\t\tAND user_membership.userId = :userId\n\t</query>\n\n\t<query name=\"getUserMembershipWithIds\">\n\t\tSELECT new org.bonitasoft.engine.identity.model.SUserMembership(\n\t\t\tuser_membership.id,\n\t\t\tuser_membership.roleId,\n\t\t\tuser_membership.groupId,\n\t\t\tuser_membership.userId,\n\t\t\tuser_membership.assignedBy,\n\t\t\tuser_membership.assignedDate,\n\t\t\tgroup_.parentPath,\n\t\t\trole.name,\n\t\t\tgroup_.name,\n\t\t\tuser.userName\n\t\t)\n\t\tFROM org.bonitasoft.engine.identity.model.SUserMembership AS user_membership,\n\t\t\torg.bonitasoft.engine.identity.model.SRole AS role,\n\t\t\torg.bonitasoft.engine.identity.model.SGroup AS group_,\n\t\t\torg.bonitasoft.engine.identity.model.SUser AS user\n\t\tWHERE user_membership.roleId = role.id\n\t\tAND user_membership.groupId = group_.id\n\t\tAND user_membership.userId = user.id\n\t\tAND user_membership.roleId = :roleId\n\t\tAND user_membership.groupId = :groupId\n\t\tAND user_membership.userId = :userId\n\t</query>\n\n\t<query name=\"getLightUserMembershipWithIds\">\n\t\tSELECT user_membership\n\t\tFROM org.bonitasoft.engine.identity.model.SUserMembership AS user_membership\n\t\tWHERE user_membership.roleId = :roleId\n\t\tAND user_membership.groupId = :groupId\n\t\tAND user_membership.userId = :userId\n\t</query>\n\n<!-- User memberships  -->\n\t<query name=\"getUserMemberships\">\n\t\tSELECT new org.bonitasoft.engine.identity.model.SUserMembership(\n\t\t\tuser_membership.id,\n\t\t\tuser_membership.roleId,\n\t\t\tuser_membership.groupId,\n\t\t\tuser_membership.userId,\n\t\t\tuser_membership.assignedBy,\n\t\t\tuser_membership.assignedDate,\n\t\t\tgroup_.parentPath,\n\t\t\trole.name,\n\t\t\tgroup_.name,\n\t\t\tuser.userName\n\t\t)\n\t\tFROM org.bonitasoft.engine.identity.model.SUserMembership AS user_membership,\n\t\t\torg.bonitasoft.engine.identity.model.SRole AS role,\n\t\t\torg.bonitasoft.engine.identity.model.SGroup AS group_,\n\t\t\torg.bonitasoft.engine.identity.model.SUser AS user\n\t\tWHERE user_membership.roleId = role.id AND user_membership.groupId = group_.id AND user_membership.userId = user.id\n\t</query>\n\n\t<query name=\"getLightUserMemberships\">\n\t\tSELECT user_membership\n\t\tFROM org.bonitasoft.engine.identity.model.SUserMembership AS user_membership\n\t</query>\n\n\t<query name=\"getSUserMembershipById\">\n\t\tSELECT new org.bonitasoft.engine.identity.model.SUserMembership(\n\t\t\tuser_membership.id,\n\t\t\tuser_membership.roleId,\n\t\t\tuser_membership.groupId,\n\t\t\tuser_membership.userId,\n\t\t\tuser_membership.assignedBy,\n\t\t\tuser_membership.assignedDate,\n\t\t\tgroup_.parentPath,\n\t\t\trole.name,\n\t\t\tgroup_.name,\n\t\t\tuser.userName\n\t\t)\n\t\tFROM org.bonitasoft.engine.identity.model.SUserMembership AS user_membership,\n\t\t\torg.bonitasoft.engine.identity.model.SRole AS role,\n\t\t\torg.bonitasoft.engine.identity.model.SGroup AS group_,\n\t\t\torg.bonitasoft.engine.identity.model.SUser AS user\n\t\tWHERE user_membership.roleId = role.id AND user_membership.groupId = group_.id AND user_membership.userId = user.id  AND user_membership.id = :id\n\t</query>\n\n\t<query name=\"getLightSUserMembershipById\">\n\t\tSELECT user_membership\n\t\tFROM org.bonitasoft.engine.identity.model.SUserMembership AS user_membership\n\t\tWHERE user_membership.id = :id\n\t</query>\n\n<!--\t custom user info -->\n\t<query name=\"getCustomUserInfoDefinitionById\">\n        SELECT custom_usr_inf_def\n        FROM org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition AS custom_usr_inf_def\n        WHERE custom_usr_inf_def.id = :id\n    </query>\n\n\t<query name=\"getCustomUserInfoDefinitions\">\n        SELECT custom_usr_inf_def\n        FROM org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition AS custom_usr_inf_def\n        ORDER BY name\n    </query>\n\n<!--\tget number of -->\n\t<query name=\"getNumberOfSUser\">\n\t\tSELECT COUNT(user.id)\n\t\tFROM org.bonitasoft.engine.identity.model.SUser AS user\n\t</query>\n\n\t<query name=\"getNumberOfUsersByGroup\">\n\t\tSELECT COUNT(user_membership.id)\n\t\tFROM org.bonitasoft.engine.identity.model.SUserMembership AS user_membership\n\t\tWHERE user_membership.groupId = :groupId\n\t</query>\n\n\t<query name=\"getNumberOfUsersByRole\">\n\t\tSELECT COUNT(user_membership.id)\n\t\tFROM org.bonitasoft.engine.identity.model.SUserMembership AS user_membership\n\t\tWHERE user_membership.roleId = :roleId\n\t</query>\n\n\t<query name=\"getNumberOfUserMembership\">\n\t\tSELECT COUNT(user_membership.id)\n\t\tFROM org.bonitasoft.engine.identity.model.SUserMembership AS user_membership\n\t</query>\n\n\t<query name=\"getNumberOfUserMembershipsOfUser\">\n\t\tSELECT COUNT(user_membership.id)\n\t\tFROM org.bonitasoft.engine.identity.model.SUserMembership AS user_membership\n\t\tWHERE user_membership.userId = :userId\n\t</query>\n\n\t<query name=\"getNumberOfUsersByMembership\">\n\t\tSELECT COUNT(user_membership.id)\n\t\tFROM org.bonitasoft.engine.identity.model.SUserMembership AS user_membership\n\t\tWHERE user_membership.roleId = :roleId\n\t\tAND user_membership.groupId = :groupId\n\t</query>\n\n\t<query name=\"getNumberOfSRole\">\n\t\tSELECT COUNT(role.id)\n\t\tFROM org.bonitasoft.engine.identity.model.SRole AS role\n\t</query>\n\n\t<query name=\"getNumberOfSGroup\">\n\t\tSELECT COUNT(group_.id)\n\t\tFROM org.bonitasoft.engine.identity.model.SGroup AS group_\n\t</query>\n\n\t<query name=\"getNumberOfGroupChildren\">\n\t\tSELECT COUNT(group_.id)\n\t\tFROM org.bonitasoft.engine.identity.model.SGroup AS group_\n\t\tWHERE group_.parentPath = :parentPath\n\t</query>\n\n\t<query name=\"getNumberOfCustomUserInfoDefinition\">\n        SELECT COUNT(custom_usr_inf_def.id)\n        FROM org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition AS custom_usr_inf_def\n    </query>\n\n    <query name=\"getNumberOfSCustomUserInfoValue\">\n        SELECT COUNT(custom_usr_inf_val.id)\n        FROM org.bonitasoft.engine.identity.model.SCustomUserInfoValue AS custom_usr_inf_val\n    </query>\n\n\t<query name=\"searchSUser\">\n\t\tSELECT user\n\t\tFROM org.bonitasoft.engine.identity.model.SUser AS user\n\t</query>\n\n\t<query name=\"getNumberOfSUserwithSUserMembership\">\n\t\tSELECT COUNT(DISTINCT user.id)\n\t\tFROM org.bonitasoft.engine.identity.model.SUser AS user,\n\t\t     org.bonitasoft.engine.identity.model.SUserMembership AS user_membership\n\t\tWHERE user.id = user_membership.userId\n\t</query>\n\n\t<query name=\"searchSUserwithSUserMembership\">\n\t\tSELECT DISTINCT user\n\t\tFROM org.bonitasoft.engine.identity.model.SUser AS user,\n\t\t     org.bonitasoft.engine.identity.model.SUserMembership AS user_membership\n\t\tWHERE user.id = user_membership.userId\n\t</query>\n\n\t<query name=\"searchSGroup\">\n\t\tSELECT group_\n\t\tFROM org.bonitasoft.engine.identity.model.SGroup AS group_\n\t</query>\n\n\t<query name=\"searchSCustomUserInfoDefinition\">\n        SELECT custom_usr_inf_def\n        FROM org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition AS custom_usr_inf_def\n    </query>\n\n\t<query name=\"searchSCustomUserInfoValue\">\n\t\tSELECT custom_usr_inf_val\n\t\tFROM org.bonitasoft.engine.identity.model.SCustomUserInfoValue AS custom_usr_inf_val\n\t</query>\n\n\t<query name=\"getCustomUserInfoValueOfUserAndDefinitions\">\n\t\tSELECT custom_usr_inf_val\n\t\tFROM org.bonitasoft.engine.identity.model.SCustomUserInfoValue AS custom_usr_inf_val\n\t\tWHERE custom_usr_inf_val.userId = :userId\n\t\tAND custom_usr_inf_val.definitionId IN (:definitionIds)\n\t</query>\n\n\t<query name=\"searchSRole\">\n\t\tSELECT role\n  \t\tFROM org.bonitasoft.engine.identity.model.SRole AS role\n\t</query>\n\n\t<query name=\"searchUserMembership\">\n\t\tSELECT new org.bonitasoft.engine.identity.model.SUserMembership(\n\t\t\tuser_membership.id,\n\t\t\tuser_membership.roleId,\n\t\t\tuser_membership.groupId,\n\t\t\tuser_membership.userId,\n\t\t\tuser_membership.assignedBy,\n\t\t\tuser_membership.assignedDate,\n\t\t\tgroup_.parentPath,\n\t\t\trole.name,\n\t\t\tgroup_.name,\n\t\t\tuser.userName\n\t\t)\n\t\tFROM org.bonitasoft.engine.identity.model.SUserMembership AS user_membership,\n\t\t\torg.bonitasoft.engine.identity.model.SRole AS role,\n\t\t\torg.bonitasoft.engine.identity.model.SGroup AS group_,\n\t\t\torg.bonitasoft.engine.identity.model.SUser AS user\n\t\tWHERE user_membership.roleId = role.id\n\t\tAND user_membership.groupId = group_.id\n\t\tAND user_membership.userId = user.id\n\t</query>\n\n\t<query name=\"getUserIdsWithCustomUserInfo\">\n\t\tSELECT user.id\n  \t\tFROM org.bonitasoft.engine.identity.model.SUser AS user,\n  \t\t\torg.bonitasoft.engine.identity.model.SCustomUserInfoDefinition AS infoDef,\n  \t\t\torg.bonitasoft.engine.identity.model.SCustomUserInfoValue AS infoValue\n \t\tWHERE infoValue.userId = user.id\n \t\t\tAND infoValue.definitionId = infoDef.id\n \t\t\tAND infoDef.name = :userInfoName\n \t\t\tAND infoValue.value = :userInfoValue\n \t\tORDER BY user.id\n\t</query>\n\n\t<query name=\"getUserIdsWithCustomUserInfoContains\">\n\t\tSELECT user.id\n  \t\tFROM org.bonitasoft.engine.identity.model.SUser AS user,\n  \t\t\torg.bonitasoft.engine.identity.model.SCustomUserInfoDefinition AS infoDef,\n  \t\t\torg.bonitasoft.engine.identity.model.SCustomUserInfoValue AS infoValue\n \t\tWHERE infoValue.userId = user.id\n \t\t\tAND infoValue.definitionId = infoDef.id\n \t\t\tAND infoDef.name = :userInfoName\n \t\t\tAND infoValue.value like :userInfoValue\n \t\tORDER BY user.id\n\t</query>\n\n</hibernate-mapping>\n"
  },
  {
    "path": "services/bonita-identity/src/test/java/org/bonitasoft/engine/identity/IconServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2021 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.argThat;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\nimport java.util.Optional;\n\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.recorder.model.Record;\nimport org.bonitasoft.engine.services.PersistenceService;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class IconServiceImplTest {\n\n    @Mock\n    Recorder recorder;\n    @Mock\n    PersistenceService persistenceService;\n    @Captor\n    ArgumentCaptor<? extends Record> record;\n    @InjectMocks\n    IconServiceImpl iconService;\n\n    @Test\n    public void should_create_icon_with_specific_mimetype() throws Exception {\n        iconService.createIcon(\"myAvatar12.png\", new byte[] { 1, 2, 3 });\n\n        //the file name is not kept, do not verify it\n        verify(recorder).recordInsert(((InsertRecord) record.capture()), eq(\"ICON\"));\n        assertThat(((SIcon) record.getValue().getEntity())).satisfies(\n                entity -> {\n                    assertThat(entity.getMimeType()).isEqualTo(\"image/png\");\n                    assertThat(entity.getContent()).isEqualTo(new byte[] { 1, 2, 3 });\n                });\n    }\n\n    @Test\n    public void should_replace_existing_icon() throws Exception {\n        SIcon previousIcon = new SIcon(42L, \"image/jpeg\", new byte[] { 1, 2, 3 });\n        doReturn(previousIcon).when(persistenceService)\n                .selectById(new SelectByIdDescriptor<>(SIcon.class, 42L));\n\n        Optional<Long> newIconId = iconService.replaceIcon(\"myAvatar12.png\", \"contents\".getBytes(), 42L);\n\n        verify(recorder).recordInsert(((InsertRecord) record.capture()), eq(\"ICON\"));\n        assertThat(((SIcon) record.getValue().getEntity())).satisfies(\n                entity -> {\n                    assertThat(entity.getMimeType()).isEqualTo(\"image/png\");\n                    assertThat(entity.getContent()).isEqualTo(\"contents\".getBytes());\n                });\n        verify(recorder).recordDelete(argThat(r -> r.getEntity().equals(previousIcon)), eq(\"ICON\"));\n        assertThat(newIconId).get().isEqualTo(0L);//id is set by reflection by the persistence service\n    }\n\n    @Test\n    public void should_replace_non_existing_icon() throws Exception {\n        Optional<Long> newIconId = iconService.replaceIcon(\"myAvatar12.png\", \"contents\".getBytes(), null);\n\n        verify(recorder).recordInsert((any()), eq(\"ICON\"));\n        verifyNoMoreInteractions(recorder);\n        assertThat(newIconId).get().isEqualTo(0L);//id is set by reflection by the persistence service\n    }\n\n    @Test\n    public void should_remove_existing_icon() throws Exception {\n        SIcon previousIcon = new SIcon(42L, \"image/jpeg\", new byte[] { 1, 2, 3 });\n        doReturn(previousIcon).when(persistenceService)\n                .selectById(new SelectByIdDescriptor<>(SIcon.class, 42L));\n\n        Optional<Long> newIconId = iconService.replaceIcon(null, null, 42L);\n\n        verify(recorder).recordDelete(argThat(r -> r.getEntity().equals(previousIcon)), eq(\"ICON\"));\n        verifyNoMoreInteractions(recorder);\n        assertThat(newIconId).isNotPresent();\n    }\n\n    @Test\n    public void should_delete_icon_when_it_exists() throws Exception {\n        doReturn(new SIcon(42L, \"image/jpeg\", new byte[] { 1, 2, 3 })).when(persistenceService)\n                .selectById(new SelectByIdDescriptor<>(SIcon.class, 42L));\n\n        iconService.deleteIcon(42L);\n\n        verify(recorder).recordDelete(argThat(r -> r.getEntity().getId() == 42L), eq(\"ICON\"));\n    }\n\n    @Test\n    public void should_not_delete_icon_when_it_does_not_exists() throws Exception {\n        iconService.deleteIcon(42L);\n\n        verifyNoInteractions(recorder);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/test/java/org/bonitasoft/engine/identity/impl/IdentityServiceImplForCustomUserInfoTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.fail;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.ArgumentMatchers.nullable;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.identity.IdentityService;\nimport org.bonitasoft.engine.identity.SCustomUserInfoDefinitionAlreadyExistsException;\nimport org.bonitasoft.engine.identity.SCustomUserInfoDefinitionCreationException;\nimport org.bonitasoft.engine.identity.SCustomUserInfoDefinitionNotFoundException;\nimport org.bonitasoft.engine.identity.SCustomUserInfoDefinitionReadException;\nimport org.bonitasoft.engine.identity.SCustomUserInfoValueNotFoundException;\nimport org.bonitasoft.engine.identity.SCustomUserInfoValueReadException;\nimport org.bonitasoft.engine.identity.SIdentityException;\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition;\nimport org.bonitasoft.engine.identity.model.SCustomUserInfoValue;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.identity.recorder.SelectDescriptorBuilder;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.ArgumentMatchers;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class IdentityServiceImplForCustomUserInfoTest {\n\n    private static final long CUSTOM_USER_INFO_DEFINITION_ID = 10L;\n\n    @Mock\n    private ReadPersistenceService persistenceService;\n\n    @Mock\n    private Recorder recorder;\n\n    @Mock\n    private QueriableLoggerService queriableLoggerService;\n\n    @Mock\n    private EventService eventService;\n\n    @Mock\n    private CredentialsEncrypter encrypter;\n\n    @Mock\n    private SCustomUserInfoValue userInfoValue;\n\n    @Mock\n    private SCustomUserInfoDefinition userInfoDef;\n\n    @InjectMocks\n    private IdentityServiceImpl identityServiceImpl;\n\n    private static String DEFAULT_NAME = \"skill\";\n\n    @Before\n    public void setUp() {\n        when(userInfoDef.getId()).thenReturn(CUSTOM_USER_INFO_DEFINITION_ID);\n        when(userInfoDef.getName()).thenReturn(DEFAULT_NAME);\n    }\n\n    @Test\n    public void createCustomUserInfoDefinition_should_call_recorder_insert() throws Exception {\n        // when\n        identityServiceImpl.createCustomUserInfoDefinition(userInfoDef);\n\n        // then\n        verify(recorder, times(1)).recordInsert(eq(new InsertRecord(userInfoDef)),\n                eq(IdentityService.CUSTOM_USER_INFO_DEFINITION));\n    }\n\n    @Test\n    public void createCustomUserInfoDefinition_should_throw_SCustomUserInfoDefinitionCreationException_when_recorder_throws_SRecorderException()\n            throws Exception {\n        // given\n        final SRecorderException recorderException = new SRecorderException(\"\");\n        doThrow(recorderException).when(recorder).recordInsert(any(InsertRecord.class), nullable(String.class));\n\n        try {\n            // when\n            identityServiceImpl.createCustomUserInfoDefinition(userInfoDef);\n            fail(\"Creation exception expected\");\n        } catch (final SCustomUserInfoDefinitionCreationException e) {\n            // then\n            assertThat(e.getDefinitionName()).isEqualTo(DEFAULT_NAME);\n            assertThat(e.getCause()).isEqualTo(recorderException);\n        }\n\n    }\n\n    @Test\n    public void createCustomUserInfoDefinition_should_throw_SCustomUserInfoDefinitionAlreadyExistsException_when_persistence_service_returns_result_for_check()\n            throws Exception {\n        // given\n        final SCustomUserInfoDefinition duplicateDef = mock(SCustomUserInfoDefinition.class);\n        given(duplicateDef.getName()).willReturn(DEFAULT_NAME);\n        given(persistenceService.selectOne(SelectDescriptorBuilder.getCustomUserInfoDefinitionByName(DEFAULT_NAME)))\n                .willReturn(userInfoDef);\n\n        try {\n            // when\n            identityServiceImpl.createCustomUserInfoDefinition(duplicateDef);\n            fail(\"Already exists exception found\");\n        } catch (final SCustomUserInfoDefinitionAlreadyExistsException e) {\n            // then\n            assertThat(e.getDefinitionName()).isEqualTo(DEFAULT_NAME);\n            verify(recorder, never()).recordInsert(any(InsertRecord.class), anyString());\n        }\n\n    }\n\n    @Test\n    public void getCustomUserInfoDefinitionByName_should_return_result_of_persistence_service() throws Exception {\n        // given\n        given(persistenceService.selectOne(SelectDescriptorBuilder.getCustomUserInfoDefinitionByName(DEFAULT_NAME)))\n                .willReturn(userInfoDef);\n\n        // when\n        final SCustomUserInfoDefinition retrievedDef = identityServiceImpl\n                .getCustomUserInfoDefinitionByName(DEFAULT_NAME);\n\n        // then\n        assertThat(retrievedDef).isEqualTo(userInfoDef);\n    }\n\n    @Test\n    public void getCustomUserInfoDefinitionByName_should_throw_SCustomUserInfoDefinitionNotFoundException_when_persistence_service_returns_null()\n            throws Exception {\n        // given\n        given(persistenceService.selectOne(SelectDescriptorBuilder.getCustomUserInfoDefinitionByName(DEFAULT_NAME)))\n                .willReturn(null);\n\n        try {\n            // when\n            identityServiceImpl.getCustomUserInfoDefinitionByName(DEFAULT_NAME);\n            fail(\"Not found exception expected\");\n        } catch (final SCustomUserInfoDefinitionNotFoundException e) {\n            // then\n            assertThat(e.getDefinitionName()).isEqualTo(DEFAULT_NAME);\n        }\n    }\n\n    @Test\n    public void getCustomUserInfoDefinitionByName_should_throw_SCustomUserInfoDefinitionReadException_when_persistence_service_throws_SBonitaReadException()\n            throws Exception {\n        // given\n        final SBonitaReadException persistenceException = new SBonitaReadException(\"\");\n        given(persistenceService.selectOne(SelectDescriptorBuilder.getCustomUserInfoDefinitionByName(DEFAULT_NAME)))\n                .willThrow(persistenceException);\n\n        try {\n            // when\n            identityServiceImpl.getCustomUserInfoDefinitionByName(DEFAULT_NAME);\n            fail(\"Read exception expected\");\n        } catch (final SCustomUserInfoDefinitionReadException e) {\n            // then\n            assertThat(e.getDefinitionName()).isEqualTo(DEFAULT_NAME);\n            assertThat(e.getCause()).isEqualTo(persistenceException);\n        }\n    }\n\n    @Test\n    public void hasCustomUserInfoDefinition_should_return_true_if_persistence_service_finds_an_element()\n            throws Exception {\n        // given\n        given(persistenceService.selectOne(SelectDescriptorBuilder.getCustomUserInfoDefinitionByName(DEFAULT_NAME)))\n                .willReturn(userInfoDef);\n\n        // when\n        final boolean hasDef = identityServiceImpl.hasCustomUserInfoDefinition(DEFAULT_NAME);\n\n        // then\n        assertThat(hasDef).isTrue();\n    }\n\n    @Test\n    public void hasCustomUserInfoDefinition_should_throw_SCustomUserInfoDefinitionReadException_when_persistence_service_throws_SBonitaReadException()\n            throws Exception {\n        // given\n        final SBonitaReadException persistenceException = new SBonitaReadException(\"\");\n        given(persistenceService.selectOne(SelectDescriptorBuilder.getCustomUserInfoDefinitionByName(DEFAULT_NAME)))\n                .willThrow(persistenceException);\n\n        try {\n            // when\n            identityServiceImpl.hasCustomUserInfoDefinition(DEFAULT_NAME);\n            fail(\"Read exception expected\");\n        } catch (final SCustomUserInfoDefinitionReadException e) {\n            // then\n            assertThat(e.getDefinitionName()).isEqualTo(DEFAULT_NAME);\n            assertThat(e.getCause()).isEqualTo(persistenceException);\n        }\n    }\n\n    @Test\n    public void searchCustomUserInfoValues_should_return_values_from_persistence_service() throws Exception {\n        // given\n        final QueryOptions queryOptions = new QueryOptions(0, 10);\n        final List<SCustomUserInfoValue> persistenceResult = Arrays.asList(userInfoValue);\n        given(persistenceService.searchEntity(SCustomUserInfoValue.class, queryOptions, null))\n                .willReturn(persistenceResult);\n\n        // when\n        final List<SCustomUserInfoValue> userInfoValues = identityServiceImpl.searchCustomUserInfoValue(queryOptions);\n\n        // then\n        assertThat(userInfoValues).isEqualTo(persistenceResult);\n    }\n\n    @Test(\n            // then\n            expected = SBonitaReadException.class)\n    public void searchCustomUserInfoValues_should_throw_exception_SBonitaReadException_when_persistence_service_throws_SBonitaReadException()\n            throws Exception {\n        // given\n        final QueryOptions queryOptions = new QueryOptions(0, 10);\n        given(persistenceService.searchEntity(SCustomUserInfoValue.class, queryOptions, null))\n                .willThrow(new SBonitaReadException(\"\"));\n\n        // when\n        identityServiceImpl.searchCustomUserInfoValue(queryOptions);\n    }\n\n    @Test\n    public void createCustomUserInfoValue_should_call_recorder_insert() throws Exception {\n        // given\n        final ArgumentCaptor<InsertRecord> recordCaptor = ArgumentCaptor.forClass(InsertRecord.class);\n\n        // when\n        identityServiceImpl.createCustomUserInfoValue(userInfoValue);\n\n        // then\n        verify(recorder, times(1)).recordInsert(recordCaptor.capture(), eq(IdentityService.CUSTOM_USER_INFO_VALUE));\n        assertThat(recordCaptor.getValue().getEntity()).isEqualTo(userInfoValue);\n    }\n\n    @Test(expected = SIdentityException.class)\n    public void createCustomUserInfoValue_should_throw_SIdentityException_when_recorder_throws_SRecorderException()\n            throws Exception {\n        // given\n        doThrow(SRecorderException.class).when(recorder).recordInsert(any(InsertRecord.class), nullable(String.class));\n\n        // when\n        identityServiceImpl.createCustomUserInfoValue(userInfoValue);\n    }\n\n    @Test\n    public void deleteCustomUserInfoValue_should_call_recorder_delete() throws Exception {\n        // given\n        final ArgumentCaptor<DeleteRecord> recordCaptor = ArgumentCaptor.forClass(DeleteRecord.class);\n\n        // when\n        identityServiceImpl.deleteCustomUserInfoValue(userInfoValue);\n\n        // then\n        verify(recorder, times(1)).recordDelete(recordCaptor.capture(), eq(IdentityService.CUSTOM_USER_INFO_VALUE));\n        assertThat(recordCaptor.getValue().getEntity()).isEqualTo(userInfoValue);\n    }\n\n    @Test(expected = SIdentityException.class)\n    public void deleteCustomUserInfoValue_should_throw_SIdentityException_when_recorder_throws_SRecorderException()\n            throws Exception {\n        // given\n        doThrow(SRecorderException.class).when(recorder).recordDelete(any(DeleteRecord.class), nullable(String.class));\n\n        // when\n        identityServiceImpl.deleteCustomUserInfoValue(userInfoValue);\n    }\n\n    @Test\n    public void getCustomUserInfoValue_shoul_return_value_returned_by_persistence_service() throws Exception {\n        final long id = 10;\n        // given\n        final SelectByIdDescriptor<SCustomUserInfoValue> selector = SelectDescriptorBuilder.getElementById(\n                SCustomUserInfoValue.class, \"SCustomUserInfoValue\",\n                id);\n        given(persistenceService.selectById(selector)).willReturn(userInfoValue);\n\n        // when\n        final SCustomUserInfoValue retriveidUserInfo = identityServiceImpl.getCustomUserInfoValue(id);\n\n        // then\n        assertThat(retriveidUserInfo).isEqualTo(userInfoValue);\n    }\n\n    @Test(expected = SCustomUserInfoValueNotFoundException.class)\n    public void getCustomUserInfoValue_shoul_throw_SCustomUserInfoValueNotFoundException_when_persistence_service_returns_null()\n            throws Exception {\n        final long id = 10;\n        // given\n        final SelectByIdDescriptor<SCustomUserInfoValue> selector = SelectDescriptorBuilder.getElementById(\n                SCustomUserInfoValue.class, \"SCustomUserInfoValue\",\n                id);\n        given(persistenceService.selectById(selector)).willReturn(null);\n\n        // when\n        identityServiceImpl.getCustomUserInfoValue(id);\n    }\n\n    @Test(expected = SCustomUserInfoValueReadException.class)\n    public void getCustomUserInfoValue_shoul_throw_SCustomUserInfoValueReadException_when_persistence_service_throws_SBonitaReadException()\n            throws Exception {\n        final long id = 10;\n        // given\n        final SelectByIdDescriptor<SCustomUserInfoValue> selector = SelectDescriptorBuilder.getElementById(\n                SCustomUserInfoValue.class, \"SCustomUserInfoValue\",\n                id);\n        given(persistenceService.selectById(selector)).willThrow(new SBonitaReadException(\"\"));\n\n        // when\n        identityServiceImpl.getCustomUserInfoValue(id);\n    }\n\n    @Test\n    public void updateCustomUserInfoValue_should_call_recorder_update() throws Exception {\n        // given\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        final ArgumentCaptor<UpdateRecord> recordCaptor = ArgumentCaptor.forClass(UpdateRecord.class);\n\n        // when\n        identityServiceImpl.updateCustomUserInfoValue(userInfoValue, descriptor);\n\n        // then\n        verify(recorder, times(1)).recordUpdate(recordCaptor.capture(), eq(IdentityService.CUSTOM_USER_INFO_VALUE));\n        assertThat(recordCaptor.getValue().getEntity()).isEqualTo(userInfoValue);\n    }\n\n    @Test\n    public void getUserIdsWithCustomUserInfo_should_use_query_getUserIdsWithCustomUserInfo_when_no_partial_match()\n            throws Exception {\n        //given\n        final Map<String, Object> parameters = new HashMap<String, Object>(2);\n        parameters.put(\"userInfoName\", DEFAULT_NAME);\n        parameters.put(\"userInfoValue\", \"Java\");\n        final SelectListDescriptor<Long> descriptor = new SelectListDescriptor<Long>(\"getUserIdsWithCustomUserInfo\",\n                parameters, SUser.class, Long.class,\n                new QueryOptions(0, 10));\n        given(persistenceService.selectList(descriptor)).willReturn(Arrays.asList(10L, 20L));\n\n        //when\n        final List<Long> userIds = identityServiceImpl.getUserIdsWithCustomUserInfo(DEFAULT_NAME, \"Java\", false, 0, 10);\n\n        //then\n        assertThat(userIds).containsExactly(10L, 20L);\n    }\n\n    @Test\n    public void getUserIdsWithCustomUserInfo_should_use_query_getUserIdsWithCustomUserInfoContains_when_partial_match()\n            throws Exception {\n        //given\n        final Map<String, Object> parameters = new HashMap<String, Object>(2);\n        parameters.put(\"userInfoName\", DEFAULT_NAME);\n        parameters.put(\"userInfoValue\", \"Java\");\n        final SelectListDescriptor<Long> descriptor = new SelectListDescriptor<Long>(\n                \"getUserIdsWithCustomUserInfoContains\", parameters, SUser.class,\n                Long.class,\n                new QueryOptions(0, 10));\n        given(persistenceService.selectList(descriptor)).willReturn(Arrays.asList(10L, 20L));\n\n        //when\n        final List<Long> userIds = identityServiceImpl.getUserIdsWithCustomUserInfo(DEFAULT_NAME, \"Java\", true, 0, 10);\n\n        //then\n        assertThat(userIds).containsExactly(10L, 20L);\n    }\n\n    @Test(expected = SIdentityException.class)\n    //then\n    public void getUserIdsWithCustomUserInfo_should_throw_SIdentityException_when_persistence_service_throws_exception()\n            throws Exception {\n        //given\n        given(persistenceService.selectList(ArgumentMatchers.<SelectListDescriptor<Long>> any()))\n                .willThrow(new SBonitaReadException(\"\"));\n\n        //when\n        identityServiceImpl.getUserIdsWithCustomUserInfo(DEFAULT_NAME, \"Java\", false, 0, 10);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/test/java/org/bonitasoft/engine/identity/impl/IdentityServiceImplForGroupTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.impl;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.util.Arrays;\nimport java.util.Collections;\n\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.identity.SGroupNotFoundException;\nimport org.bonitasoft.engine.identity.SIdentityException;\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.identity.recorder.SelectDescriptorBuilder;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.ArgumentMatchers;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class IdentityServiceImplForGroupTest {\n\n    @Mock\n    private Recorder recorder;\n\n    @Mock\n    private ReadPersistenceService persistenceService;\n\n    @Mock\n    private EventService eventService;\n\n    @InjectMocks\n    private IdentityServiceImpl identityServiceImpl;\n\n    @Before\n    public void setUp() {\n        MockitoAnnotations.initMocks(this);\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getNumberOfGroupChildren(long)}.\n     */\n    @Test\n    public void getNumberOfGroupChildren() throws Exception {\n        final SGroup group = mock(SGroup.class);\n        when(group.getParentPath()).thenReturn(\"/thePath\");\n        when(persistenceService.selectById(SelectDescriptorBuilder.getElementById(SGroup.class, \"Group\", 123l)))\n                .thenReturn(group);\n        when(persistenceService.selectOne(SelectDescriptorBuilder.getNumberOfGroupChildren(\"/thePath\")))\n                .thenReturn(12l);\n\n        assertEquals(12l, identityServiceImpl.getNumberOfGroupChildren(123l));\n    }\n\n    @Test(expected = SIdentityException.class)\n    public void getNumberOfGroupChildrenThrowException() throws Exception {\n        final SGroup group = mock(SGroup.class);\n        when(group.getParentPath()).thenReturn(\"/thePath\");\n        when(persistenceService.selectById(SelectDescriptorBuilder.getElementById(SGroup.class, \"Group\", 123l)))\n                .thenReturn(group);\n        when(persistenceService.selectOne(SelectDescriptorBuilder.getNumberOfGroupChildren(\"/thePath\")))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        identityServiceImpl.getNumberOfGroupChildren(123l);\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getGroupChildren(long, int, int)}.\n     */\n    @Test\n    public void getGroupChildrenPaginatedById() throws Exception {\n        final SGroup group = mock(SGroup.class);\n        final SGroup child = mock(SGroup.class);\n        when(persistenceService.selectById(SelectDescriptorBuilder.getElementById(SGroup.class, \"Group\", 123l)))\n                .thenReturn(group);\n        when(persistenceService.selectList(SelectDescriptorBuilder.getChildrenOfGroup(group, 0, 10)))\n                .thenReturn(Collections.singletonList(child));\n\n        assertEquals(child, identityServiceImpl.getGroupChildren(123l, 0, 10).get(0));\n    }\n\n    @Test(expected = SIdentityException.class)\n    public void getGroupChildrenPaginatedByIdThrowException() throws Exception {\n        final SGroup group = mock(SGroup.class);\n        when(persistenceService.selectById(SelectDescriptorBuilder.getElementById(SGroup.class, \"Group\", 123l)))\n                .thenReturn(group);\n        when(persistenceService.selectList(SelectDescriptorBuilder.getChildrenOfGroup(group, 0, 10)))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        identityServiceImpl.getGroupChildren(123l, 0, 10);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getGroupChildren(long, int, int, java.lang.String, org.bonitasoft.engine.persistence.OrderByType)}\n     * .\n     */\n    @Test\n    public void getGroupChildrenPaginatedByIdWithOrder() throws Exception {\n        final SGroup group = mock(SGroup.class);\n        final SGroup child = mock(SGroup.class);\n        when(persistenceService.selectById(SelectDescriptorBuilder.getElementById(SGroup.class, \"Group\", 123l)))\n                .thenReturn(group);\n        when(persistenceService\n                .selectList(SelectDescriptorBuilder.getChildrenOfGroup(group, \"name\", OrderByType.ASC, 0, 10)))\n                .thenReturn(\n                        Collections.singletonList(child));\n\n        assertEquals(child, identityServiceImpl.getGroupChildren(123l, 0, 10, \"name\", OrderByType.ASC).get(0));\n    }\n\n    @Test(expected = SIdentityException.class)\n    public void getGroupChildrenPaginatedByIdWithOrderThrowException() throws Exception {\n        final SGroup group = mock(SGroup.class);\n        when(persistenceService.selectById(SelectDescriptorBuilder.getElementById(SGroup.class, \"Group\", 123l)))\n                .thenReturn(group);\n        when(persistenceService\n                .selectList(SelectDescriptorBuilder.getChildrenOfGroup(group, \"name\", OrderByType.ASC, 0, 10)))\n                .thenThrow(\n                        new SBonitaReadException(\"\"));\n\n        identityServiceImpl.getGroupChildren(123l, 0, 10, \"name\", OrderByType.ASC);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getNumberOfGroups(org.bonitasoft.engine.persistence.QueryOptions)}.\n     */\n    @Test\n    public void getNumberOfGroupsWithOptions() throws Exception {\n        final QueryOptions options = new QueryOptions(0, 10);\n        when(persistenceService.getNumberOfEntities(SGroup.class, options, null)).thenReturn(125l);\n\n        assertEquals(125, identityServiceImpl.getNumberOfGroups(options));\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getNumberOfGroupsWithOptionsThrowException() throws Exception {\n        final QueryOptions options = new QueryOptions(0, 10);\n        when(persistenceService.getNumberOfEntities(SGroup.class, options, null))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        identityServiceImpl.getNumberOfGroups(options);\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getNumberOfGroups()}.\n     */\n    @Test\n    public void getNumberOfGroups() throws Exception {\n        when(persistenceService.selectOne(SelectDescriptorBuilder.getNumberOfElement(\"SGroup\", SGroup.class)))\n                .thenReturn(125l);\n\n        assertEquals(125, identityServiceImpl.getNumberOfGroups());\n    }\n\n    @Test(expected = SIdentityException.class)\n    public void getNumberOfGroupsThrowException() throws Exception {\n        when(persistenceService.selectOne(SelectDescriptorBuilder.getNumberOfElement(\"SGroup\", SGroup.class)))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        identityServiceImpl.getNumberOfGroups();\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#searchGroups(org.bonitasoft.engine.persistence.QueryOptions)}.\n     */\n    @Test\n    public void searchGroups() throws Exception {\n        final QueryOptions options = new QueryOptions(0, 10);\n        final SGroup group = mock(SGroup.class);\n        when(persistenceService.searchEntity(SGroup.class, options, null)).thenReturn(Collections.singletonList(group));\n\n        assertEquals(group, identityServiceImpl.searchGroups(options).get(0));\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void searchGroupsThrowException() throws Exception {\n        final QueryOptions options = new QueryOptions(0, 10);\n        when(persistenceService.searchEntity(SGroup.class, options, null)).thenThrow(new SBonitaReadException(\"\"));\n\n        identityServiceImpl.searchGroups(options);\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getGroup(long)}.\n     */\n    @Test\n    public void getGroup() throws Exception {\n        final SGroup group = mock(SGroup.class);\n        when(persistenceService.selectById(SelectDescriptorBuilder.getElementById(SGroup.class, \"Group\", 123l)))\n                .thenReturn(group);\n\n        assertEquals(group, identityServiceImpl.getGroup(123l));\n    }\n\n    @Test(expected = SIdentityException.class)\n    public void getGroupThrowException() throws Exception {\n        when(persistenceService.selectById(SelectDescriptorBuilder.getElementById(SGroup.class, \"Group\", 123l)))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        identityServiceImpl.getGroup(123l);\n    }\n\n    @Test(expected = SGroupNotFoundException.class)\n    public void getGroupNotExist() throws Exception {\n        final SGroup group = mock(SGroup.class);\n\n        assertEquals(group, identityServiceImpl.getGroup(123l));\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getGroupByPath(java.lang.String)}.\n     */\n    @Test\n    public void getGroupByPath() throws Exception {\n        final SGroup group = mock(SGroup.class);\n        when(persistenceService.selectOne(SelectDescriptorBuilder.getGroupByName(\"path\"))).thenReturn(group);\n\n        assertEquals(group, identityServiceImpl.getGroupByPath(\"/path\"));\n    }\n\n    @Test(expected = SGroupNotFoundException.class)\n    public void getGroupByPathNotExist() throws Exception {\n        final SGroup group = mock(SGroup.class);\n\n        assertEquals(group, identityServiceImpl.getGroupByPath(\"/path\"));\n    }\n\n    @Test\n    public void getGroupByPathWithNoSlash() throws Exception {\n        final SGroup group = mock(SGroup.class);\n        when(persistenceService.selectOne(SelectDescriptorBuilder.getGroupByName(\"path\"))).thenReturn(group);\n\n        assertEquals(group, identityServiceImpl.getGroupByPath(\"path\"));\n    }\n\n    @Test\n    public void getGroupByPathThatIsNotRoot() throws Exception {\n        final SGroup group = mock(SGroup.class);\n        when(persistenceService.selectOne(SelectDescriptorBuilder.getGroupByPath(\"/path\", \"subPath\")))\n                .thenReturn(group);\n\n        assertEquals(group, identityServiceImpl.getGroupByPath(\"/path/subPath\"));\n    }\n\n    @Test(expected = SIdentityException.class)\n    public void getGroupByPathThrowException() throws Exception {\n        when(persistenceService.selectOne(ArgumentMatchers.<SelectOneDescriptor<SGroup>> any()))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        identityServiceImpl.getGroupByPath(\"path\");\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getGroups(java.util.List)}.\n     */\n    @Test\n    public final void getGroupsByIds() throws SBonitaReadException, SGroupNotFoundException {\n        final SGroup group = mock(SGroup.class);\n        when(persistenceService\n                .selectList(SelectDescriptorBuilder.getElementsByIds(SGroup.class, \"Group\", Arrays.asList(123l))))\n                .thenReturn(\n                        Arrays.asList(group));\n\n        assertEquals(group, identityServiceImpl.getGroups(Arrays.asList(123l)).get(0));\n    }\n\n    @Test\n    public void getGroupsByNullIds() throws Exception {\n        assertTrue(identityServiceImpl.getGroups(null).isEmpty());\n    }\n\n    @Test\n    public void getGroupsByEmptyIds() throws Exception {\n        assertTrue(identityServiceImpl.getGroups(Collections.<Long> emptyList()).isEmpty());\n    }\n\n    @Test(expected = SIdentityException.class)\n    public void getGroupsByIdsThrowException() throws Exception {\n        when(persistenceService\n                .selectList(SelectDescriptorBuilder.getElementsByIds(SGroup.class, \"Group\", Arrays.asList(123l))))\n                .thenThrow(\n                        new SBonitaReadException(\"\"));\n\n        identityServiceImpl.getGroups(Arrays.asList(123l));\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getGroups(int, int)}.\n     */\n    @Test\n    public final void getGroupsPaginated() throws SBonitaReadException, SIdentityException {\n        final SGroup group = mock(SGroup.class);\n        when(persistenceService.selectList(SelectDescriptorBuilder.getElements(SGroup.class, \"Group\", 0, 10)))\n                .thenReturn(Arrays.asList(group));\n\n        assertEquals(group, identityServiceImpl.getGroups(0, 10).get(0));\n    }\n\n    @Test(expected = SIdentityException.class)\n    public void getGroupsPaginatedThrowException() throws Exception {\n        when(persistenceService.selectList(SelectDescriptorBuilder.getElements(SGroup.class, \"Group\", 0, 10)))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        identityServiceImpl.getGroups(0, 10);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getGroups(int, int, java.lang.String, org.bonitasoft.engine.persistence.OrderByType)}.\n     */\n    @Test\n    public void getGroupsPaginatedWithOrder() throws Exception {\n        final SGroup group = mock(SGroup.class);\n        when(persistenceService\n                .selectList(SelectDescriptorBuilder.getElements(SGroup.class, \"Group\", \"name\", OrderByType.ASC, 0, 10)))\n                .thenReturn(\n                        Arrays.asList(group));\n\n        assertEquals(group, identityServiceImpl.getGroups(0, 10, \"name\", OrderByType.ASC).get(0));\n    }\n\n    @Test(expected = SIdentityException.class)\n    public void getGroupsPaginatedWithOrderThrowException() throws Exception {\n        when(persistenceService\n                .selectList(SelectDescriptorBuilder.getElements(SGroup.class, \"Group\", \"name\", OrderByType.ASC, 0, 10)))\n                .thenThrow(\n                        new SBonitaReadException(\"\"));\n\n        identityServiceImpl.getGroups(0, 10, \"name\", OrderByType.ASC);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/test/java/org/bonitasoft/engine/identity/impl/IdentityServiceImplForUserMembershipTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.impl;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.doThrow;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.identity.SIdentityException;\nimport org.bonitasoft.engine.identity.model.SGroup;\nimport org.bonitasoft.engine.identity.model.SRole;\nimport org.bonitasoft.engine.identity.model.SUserMembership;\nimport org.bonitasoft.engine.identity.recorder.SelectDescriptorBuilder;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.ArgumentMatchers;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class IdentityServiceImplForUserMembershipTest {\n\n    @Mock\n    private Recorder recorder;\n\n    @Mock\n    private ReadPersistenceService persistenceService;\n\n    @Mock\n    private EventService eventService;\n\n    @InjectMocks\n    private IdentityServiceImpl identityServiceImpl;\n\n    @Before\n    public void setUp() {\n        MockitoAnnotations.initMocks(this);\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getLightUserMembership(long)}.\n     */\n    @Test\n    public final void getLightUserMembershipById() throws SBonitaReadException, SIdentityException {\n        final SUserMembership userMembership = mock(SUserMembership.class);\n        final long userMembershipId = 546L;\n        doReturn(userMembership).when(persistenceService)\n                .selectById(SelectDescriptorBuilder.getLightElementById(SUserMembership.class,\n                        \"SUserMembership\", userMembershipId));\n\n        assertEquals(userMembership, identityServiceImpl.getLightUserMembership(userMembershipId));\n    }\n\n    @Test(expected = SIdentityException.class)\n    public final void getLightUserMembershipByIdNotExist() throws SBonitaReadException, SIdentityException {\n        final long userMembershipId = 546L;\n        doReturn(null).when(persistenceService)\n                .selectById(SelectDescriptorBuilder.getLightElementById(SUserMembership.class,\n                        \"SUserMembership\", userMembershipId));\n\n        identityServiceImpl.getLightUserMembership(userMembershipId);\n    }\n\n    @Test(expected = SIdentityException.class)\n    public final void getLightUserMembershipByIdThrowException() throws SBonitaReadException, SIdentityException {\n        final long userMembershipId = 546L;\n        doThrow(new SBonitaReadException(\"\")).when(persistenceService)\n                .selectById(SelectDescriptorBuilder.getLightElementById(SUserMembership.class,\n                        \"SUserMembership\", userMembershipId));\n\n        identityServiceImpl.getLightUserMembership(userMembershipId);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getLightUserMembership(long, long, long)}.\n     */\n    @Test\n    public final void getLightUserMembershipByUserAndGroupAndRole() throws SBonitaReadException, SIdentityException {\n        final SUserMembership userMembership = mock(SUserMembership.class);\n        final long userId = 546L;\n        final long groupId = 565L;\n        final long roleId = 54L;\n        doReturn(userMembership).when(persistenceService)\n                .selectOne(SelectDescriptorBuilder.getLightUserMembership(userId, groupId, roleId));\n\n        assertEquals(userMembership, identityServiceImpl.getLightUserMembership(userId, groupId, roleId));\n    }\n\n    @Test(expected = SIdentityException.class)\n    public final void getLightUserMembershipByUserAndGroupAndRoleNotExist()\n            throws SBonitaReadException, SIdentityException {\n        final long userId = 546L;\n        final long groupId = 565L;\n        final long roleId = 54L;\n        doReturn(null).when(persistenceService)\n                .selectOne(SelectDescriptorBuilder.getLightUserMembership(userId, groupId, roleId));\n\n        identityServiceImpl.getLightUserMembership(userId, groupId, roleId);\n    }\n\n    @Test(expected = SIdentityException.class)\n    public final void getLightUserMembershipByUserAndGroupAndRoleThrowException()\n            throws SBonitaReadException, SIdentityException {\n        final long userId = 546L;\n        final long groupId = 565L;\n        final long roleId = 54L;\n        doThrow(new SBonitaReadException(\"\")).when(persistenceService)\n                .selectOne(SelectDescriptorBuilder.getLightUserMembership(userId, groupId, roleId));\n\n        identityServiceImpl.getLightUserMembership(userId, groupId, roleId);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getLightUserMemberships(int, int)}.\n     */\n    @Test\n    public final void getLightUserMembershipsPaginated() throws SBonitaReadException, SIdentityException {\n        final SUserMembership userMembership = mock(SUserMembership.class);\n        final List<SUserMembership> userMemberships = Collections.singletonList(userMembership);\n        final int startIndex = 546;\n        final int numberOfElements = 565;\n        doReturn(userMemberships).when(persistenceService).selectList(\n                SelectDescriptorBuilder.getElements(SUserMembership.class, \"LightUserMembership\", startIndex,\n                        numberOfElements));\n\n        assertEquals(userMemberships, identityServiceImpl.getLightUserMemberships(startIndex, numberOfElements));\n    }\n\n    @Test(expected = SIdentityException.class)\n    public final void getLightUserMembershipsPaginatedThrowException() throws SBonitaReadException, SIdentityException {\n        final int startIndex = 546;\n        final int numberOfElements = 565;\n        doThrow(new SBonitaReadException(\"\")).when(persistenceService).selectList(\n                SelectDescriptorBuilder.getElements(SUserMembership.class, \"LightUserMembership\", startIndex,\n                        numberOfElements));\n\n        identityServiceImpl.getLightUserMemberships(startIndex, numberOfElements);\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getNumberOfUserMemberships()}.\n     */\n    @Test\n    public final void getNumberOfUserMemberships() throws SBonitaReadException, SIdentityException {\n        final long numberOfUserMemberships = 3;\n        doReturn(numberOfUserMemberships).when(persistenceService).selectOne(\n                SelectDescriptorBuilder.getNumberOfElement(\"UserMembership\", SUserMembership.class));\n\n        assertEquals(numberOfUserMemberships, identityServiceImpl.getNumberOfUserMemberships());\n    }\n\n    @Test(expected = SIdentityException.class)\n    public final void getNumberOfUserMembershipsThrowException() throws SBonitaReadException, SIdentityException {\n        doThrow(new SBonitaReadException(\"\")).when(persistenceService).selectOne(\n                SelectDescriptorBuilder.getNumberOfElement(\"UserMembership\", SUserMembership.class));\n\n        identityServiceImpl.getNumberOfUserMemberships();\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getNumberOfUserMembershipsOfUser(long)}.\n     */\n    @Test\n    public final void getNumberOfUserMembershipsOfUser() throws SBonitaReadException, SIdentityException {\n        final long numberOfUserMemberships = 3;\n        final long userId = 554L;\n        doReturn(numberOfUserMemberships).when(persistenceService)\n                .selectOne(SelectDescriptorBuilder.getNumberOfUserMembershipsOfUser(userId));\n\n        assertEquals(numberOfUserMemberships, identityServiceImpl.getNumberOfUserMembershipsOfUser(userId));\n    }\n\n    @Test(expected = SIdentityException.class)\n    public final void getNumberOfUserMembershipsOfUserThrowException() throws SBonitaReadException, SIdentityException {\n        final long userId = 554L;\n        doThrow(new SBonitaReadException(\"\")).when(persistenceService)\n                .selectOne(SelectDescriptorBuilder.getNumberOfUserMembershipsOfUser(userId));\n\n        identityServiceImpl.getNumberOfUserMembershipsOfUser(userId);\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getUserMembership(long)}.\n     */\n    @Test\n    public final void getUserMembershipById() throws SBonitaReadException, SIdentityException {\n        final SUserMembership userMembership = mock(SUserMembership.class);\n        doReturn(userMembership).when(persistenceService)\n                .selectOne(ArgumentMatchers.<SelectOneDescriptor<SUserMembership>> any());\n\n        assertEquals(userMembership, identityServiceImpl.getUserMembership(546L));\n    }\n\n    @Test(expected = SIdentityException.class)\n    public final void getUserMembershipByIdNotExist() throws SBonitaReadException, SIdentityException {\n        doReturn(null).when(persistenceService)\n                .selectOne(ArgumentMatchers.<SelectOneDescriptor<SUserMembership>> any());\n\n        identityServiceImpl.getUserMembership(546L);\n    }\n\n    @Test(expected = SIdentityException.class)\n    public final void getUserMembershipByIdThrowException() throws SBonitaReadException, SIdentityException {\n        doThrow(new SBonitaReadException(\"\")).when(persistenceService)\n                .selectOne(ArgumentMatchers.<SelectOneDescriptor<SUserMembership>> any());\n\n        identityServiceImpl.getUserMembership(546L);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getUserMembership(long, long, long)}.\n     */\n    @Test\n    public final void getUserMembershipByUserAndGroupAndRole() throws SBonitaReadException, SIdentityException {\n        final SUserMembership userMembership = mock(SUserMembership.class);\n        doReturn(userMembership).when(persistenceService)\n                .selectOne(ArgumentMatchers.<SelectOneDescriptor<SUserMembership>> any());\n\n        assertEquals(userMembership, identityServiceImpl.getUserMembership(546L, 565L, 54L));\n    }\n\n    @Test(expected = SIdentityException.class)\n    public final void getUserMembershipByUserAndGroupAndRoleNotExist() throws SBonitaReadException, SIdentityException {\n        doReturn(null).when(persistenceService)\n                .selectOne(ArgumentMatchers.<SelectOneDescriptor<SUserMembership>> any());\n\n        identityServiceImpl.getUserMembership(546L, 565L, 54L);\n    }\n\n    @Test(expected = SIdentityException.class)\n    public final void getUserMembershipByUserAndGroupAndRoleThrowException()\n            throws SBonitaReadException, SIdentityException {\n        doThrow(new SBonitaReadException(\"\")).when(persistenceService)\n                .selectOne(ArgumentMatchers.<SelectOneDescriptor<SUserMembership>> any());\n\n        identityServiceImpl.getUserMembership(546L, 565L, 54L);\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getUserMembershipsOfGroup(long)}.\n     */\n    @Test\n    public void getUserMembershipsOfGroup() throws Exception {\n        final SUserMembership userMembership = mock(SUserMembership.class);\n        when(persistenceService.selectList(SelectDescriptorBuilder.getUserMembershipsByGroup(1l, 0, 20)))\n                .thenReturn(Collections.singletonList(userMembership));\n\n        final List<SUserMembership> userMemberships = identityServiceImpl.getUserMembershipsOfGroup(1l, 0, 20);\n\n        assertEquals(userMembership, userMemberships.get(0));\n    }\n\n    @Test(expected = SIdentityException.class)\n    public void getUserMembershipsOfGroupThrowException() throws Exception {\n        when(persistenceService.selectList(SelectDescriptorBuilder.getUserMembershipsByGroup(1l, 0, 20)))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        identityServiceImpl.getUserMembershipsOfGroup(1l, 0, 20);\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getUserMembershipsOfRole(long)}.\n     */\n    @Test\n    public void getUserMembershipsOfRole() throws Exception {\n        final SUserMembership userMembership = mock(SUserMembership.class);\n        when(persistenceService.selectList(SelectDescriptorBuilder.getUserMembershipsByRole(1l, 0, 20)))\n                .thenReturn(Collections.singletonList(userMembership));\n\n        final List<SUserMembership> userMemberships = identityServiceImpl.getUserMembershipsOfRole(1l, 0, 20);\n\n        assertEquals(userMembership, userMemberships.get(0));\n    }\n\n    @Test(expected = SIdentityException.class)\n    public void getUserMembershipsOfRoleThrowException() throws Exception {\n        when(persistenceService.selectList(SelectDescriptorBuilder.getUserMembershipsByRole(1l, 0, 20)))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        identityServiceImpl.getUserMembershipsOfRole(1l, 0, 20);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getUserMemberships(int, int, org.bonitasoft.engine.persistence.OrderByOption)}.\n     */\n    @Test\n    public void getUserMembershipsPaginatedWithOrder() throws Exception {\n        final SUserMembership userMembership = mock(SUserMembership.class);\n        final OrderByOption orderByOption = new OrderByOption(SUserMembership.class, \"username\", OrderByType.ASC);\n        doReturn(Collections.singletonList(userMembership)).when(persistenceService)\n                .selectList(\n                        SelectDescriptorBuilder.getElements(SUserMembership.class, \"UserMembership\",\n                                new QueryOptions(0, 10, Collections.singletonList(orderByOption))));\n\n        final List<SUserMembership> userMemberships = identityServiceImpl.getUserMemberships(0, 10, orderByOption);\n\n        assertEquals(userMembership, userMemberships.get(0));\n    }\n\n    @Test(expected = SIdentityException.class)\n    public void getUserMembershipsPaginatedWithOrderThrowException() throws Exception {\n        final OrderByOption orderByOption = new OrderByOption(SUserMembership.class, \"username\", OrderByType.ASC);\n        when(\n                persistenceService\n                        .selectList(SelectDescriptorBuilder.getElements(SUserMembership.class, \"UserMembership\",\n                                new QueryOptions(0, 10, Collections.singletonList(orderByOption)))))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        identityServiceImpl.getUserMemberships(0, 10, orderByOption);\n    }\n\n    @Test\n    public void getUserMembershipsOrderByRole() throws Exception {\n        final SUserMembership userMembership = mock(SUserMembership.class);\n        final OrderByOption orderByOption = new OrderByOption(SRole.class, \"name\", OrderByType.ASC);\n        when(\n                persistenceService\n                        .selectList(SelectDescriptorBuilder.getUserMembershipsWithRole(new QueryOptions(0, 10,\n                                Collections\n                                        .singletonList(orderByOption)))))\n                .thenReturn(Collections.singletonList(userMembership));\n\n        final List<SUserMembership> userMemberships = identityServiceImpl.getUserMemberships(0, 10, orderByOption);\n\n        assertEquals(userMembership, userMemberships.get(0));\n    }\n\n    @Test\n    public void getUserMembershipsOrderByGroup() throws Exception {\n        final SUserMembership userMembership = mock(SUserMembership.class);\n        final OrderByOption orderByOption = new OrderByOption(SGroup.class, \"name\", OrderByType.ASC);\n        when(\n                persistenceService\n                        .selectList(SelectDescriptorBuilder.getUserMembershipsWithGroup(new QueryOptions(0, 10,\n                                Collections\n                                        .singletonList(orderByOption)))))\n                .thenReturn(Collections.singletonList(userMembership));\n\n        final List<SUserMembership> userMemberships = identityServiceImpl.getUserMemberships(0, 10, orderByOption);\n\n        assertEquals(userMembership, userMemberships.get(0));\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getUserMemberships(int, int)}.\n     */\n    @Test\n    public final void getUserMembershipsPaginated() throws SBonitaReadException, SIdentityException {\n        final SUserMembership userMembership = mock(SUserMembership.class);\n        final List<SUserMembership> userMemberships = Collections.singletonList(userMembership);\n        doReturn(userMemberships).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SUserMembership>> any());\n\n        assertEquals(userMemberships, identityServiceImpl.getUserMemberships(0, 10));\n    }\n\n    @Test(expected = SIdentityException.class)\n    public final void getUserMembershipsPaginatedThrowException() throws SBonitaReadException, SIdentityException {\n        doThrow(new SBonitaReadException(\"\")).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SUserMembership>> any());\n\n        identityServiceImpl.getUserMemberships(0, 10);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/test/java/org/bonitasoft/engine/identity/impl/IdentityServiceImplForUserTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.identity.IconService;\nimport org.bonitasoft.engine.identity.SIcon;\nimport org.bonitasoft.engine.identity.SIdentityException;\nimport org.bonitasoft.engine.identity.SUserNotFoundException;\nimport org.bonitasoft.engine.identity.model.SUser;\nimport org.bonitasoft.engine.identity.model.SUserLogin;\nimport org.bonitasoft.engine.identity.model.builder.SUserLogBuilder;\nimport org.bonitasoft.engine.identity.recorder.SelectDescriptorBuilder;\nimport org.bonitasoft.engine.persistence.*;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.builder.ActionType;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.*;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class IdentityServiceImplForUserTest {\n\n    public static final long USER_ID = 6543L;\n    public static final long ICON_ID = 5247890L;\n    public static final long NEW_ICON_ID = 4328976L;\n    @Mock\n    private CredentialsEncrypter encrypter;\n    @Mock\n    private Recorder recorder;\n    @Mock\n    private ReadPersistenceService persistenceService;\n    @Mock\n    private IconService iconService;\n    @Mock\n    private EventService eventService;\n\n    @Spy\n    @InjectMocks\n    private IdentityServiceImpl identityServiceImpl;\n    @Captor\n    private ArgumentCaptor<InsertRecord> insertRecordArgumentCaptor;\n    @Captor\n    private ArgumentCaptor<UpdateRecord> updateRecordArgumentCaptor;\n    @Captor\n    private ArgumentCaptor<DeleteRecord> deleteRecordArgumentCaptor;\n    @Mock\n    private SUserLogBuilder sUserLogBuilder;\n    @Mock\n    private SQueriableLog log;\n    @Mock\n    private QueriableLoggerService queriableLoggerService;\n\n    @Before\n    public void setUp() throws SRecorderException {\n        doReturn(sUserLogBuilder).when(identityServiceImpl).getUserLog(any(ActionType.class), anyString());\n        doReturn(log).when(sUserLogBuilder).build();\n        SIcon newIcon = new SIcon(\"\", null);\n        newIcon.setId(NEW_ICON_ID);\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getNumberOfUsers()}.\n     */\n    @Test\n    public void getNumberOfUsers() throws Exception {\n        when(persistenceService.selectOne(ArgumentMatchers.<SelectOneDescriptor<Long>> any())).thenReturn(1L);\n        Assert.assertEquals(1L, identityServiceImpl.getNumberOfUsers());\n\n        verifyNoInteractions(recorder);\n    }\n\n    @Test(expected = SIdentityException.class)\n    public void getNumberOfUsersThrowException() throws Exception {\n        when(persistenceService.selectOne(ArgumentMatchers.<SelectOneDescriptor<SUser>> any()))\n                .thenThrow(new SBonitaReadException(\"\"));\n        identityServiceImpl.getNumberOfUsers();\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getNumberOfUsers(org.bonitasoft.engine.persistence.QueryOptions)}.\n     */\n    @Test\n    public void getNumberOfUsersWithOptions() throws Exception {\n        final QueryOptions options = new QueryOptions(0, 10);\n\n        when(persistenceService.getNumberOfEntities(SUser.class, options, null)).thenReturn(1L);\n        Assert.assertEquals(1L, identityServiceImpl.getNumberOfUsers(options));\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getNumberOfUsersWithOptionsThrowException() throws Exception {\n        final QueryOptions options = new QueryOptions(0, 10);\n\n        when(persistenceService.getNumberOfEntities(SUser.class, options, null))\n                .thenThrow(new SBonitaReadException(\"\"));\n        identityServiceImpl.getNumberOfUsers(options);\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getNumberOfUsersByGroup(long)}.\n     */\n    @Test\n    public void getNumberOfUsersByGroup() throws Exception {\n        final long groupId = 1;\n\n        when(persistenceService.selectOne(SelectDescriptorBuilder.getNumberOfUsersByGroup(groupId))).thenReturn(3L);\n        Assert.assertEquals(3L, identityServiceImpl.getNumberOfUsersByGroup(groupId));\n    }\n\n    @Test(expected = SIdentityException.class)\n    public void getNumberOfUsersByGroupThrowException() throws Exception {\n        final long groupId = 1;\n\n        when(persistenceService.selectOne(SelectDescriptorBuilder.getNumberOfUsersByGroup(groupId)))\n                .thenThrow(new SBonitaReadException(\"\"));\n        identityServiceImpl.getNumberOfUsersByGroup(groupId);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getNumberOfUsersByMembership(long, long)}.\n     */\n    @Test\n    public void getNumberOfUsersByMembership() throws Exception {\n        final long groupId = 1;\n        final long roleId = 2;\n\n        when(persistenceService.selectOne(SelectDescriptorBuilder.getNumberOfUsersByMembership(groupId, roleId)))\n                .thenReturn(3L);\n        Assert.assertEquals(3L, identityServiceImpl.getNumberOfUsersByMembership(groupId, roleId));\n    }\n\n    @Test(expected = SIdentityException.class)\n    public void getNumberOfUsersByMembershipThrowException() throws Exception {\n        final long groupId = 1;\n        final long roleId = 2;\n\n        when(persistenceService.selectOne(SelectDescriptorBuilder.getNumberOfUsersByMembership(groupId, roleId)))\n                .thenThrow(new SBonitaReadException(\"\"));\n        identityServiceImpl.getNumberOfUsersByMembership(groupId, roleId);\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getNumberOfUsersByRole(long)}.\n     */\n    @Test\n    public void getNumberOfUsersByRole() throws Exception {\n        final long roleId = 2;\n\n        when(persistenceService.selectOne(SelectDescriptorBuilder.getNumberOfUsersByRole(roleId))).thenReturn(3L);\n        Assert.assertEquals(3L, identityServiceImpl.getNumberOfUsersByRole(roleId));\n    }\n\n    @Test(expected = SIdentityException.class)\n    public void getNumberOfUsersByRoleThrowException() throws Exception {\n        final long roleId = 2;\n\n        when(persistenceService.selectOne(SelectDescriptorBuilder.getNumberOfUsersByRole(roleId)))\n                .thenThrow(new SBonitaReadException(\"\"));\n        identityServiceImpl.getNumberOfUsersByRole(roleId);\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getUser(long)}.\n     */\n    @Test\n    public void getUserById() throws SBonitaReadException, SUserNotFoundException {\n        final long userId = 1;\n        final SUser sUser = mock(SUser.class);\n        when(persistenceService.selectById(SelectDescriptorBuilder.getElementById(SUser.class, \"User\", userId)))\n                .thenReturn(sUser);\n\n        Assert.assertEquals(sUser, identityServiceImpl.getUser(userId));\n    }\n\n    @Test(expected = SUserNotFoundException.class)\n    public void getUserByIdNotExist() throws SBonitaReadException, SUserNotFoundException {\n        final long userId = 455;\n        doReturn(null).when(persistenceService)\n                .selectById(SelectDescriptorBuilder.getElementById(SUser.class, \"User\", userId));\n\n        identityServiceImpl.getUser(userId);\n    }\n\n    @Test(expected = SUserNotFoundException.class)\n    public void getUserByIdThrowException() throws SBonitaReadException, SUserNotFoundException {\n        final long userId = 1;\n        doThrow(new SBonitaReadException(\"\")).when(persistenceService)\n                .selectById(SelectDescriptorBuilder.getElementById(SUser.class, \"User\", userId));\n\n        identityServiceImpl.getUser(userId);\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getUsers(java.util.List)}.\n     */\n    @Test\n    public void getUsersByIds() throws Exception {\n        final SUser sUser1 = mock(SUser.class);\n        final SUser sUser2 = mock(SUser.class);\n        final SUser sUser3 = mock(SUser.class);\n        final List<SUser> users = Arrays.asList(sUser1, sUser2, sUser3);\n        final List<Long> ids = Arrays.asList(1l, 2l, 3l);\n        when(persistenceService.selectList(SelectDescriptorBuilder.getElementsByIds(SUser.class, \"User\", ids)))\n                .thenReturn(users);\n\n        Assert.assertEquals(users, identityServiceImpl.getUsers(ids));\n    }\n\n    @Test\n    public void getUsersByNullIds() throws Exception {\n        assertTrue(identityServiceImpl.getUsers(null).isEmpty());\n    }\n\n    @Test\n    public void getUsersByEmptyIds() throws Exception {\n        assertTrue(identityServiceImpl.getUsers(Collections.<Long> emptyList()).isEmpty());\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Test(expected = SUserNotFoundException.class)\n    public void getUsersByIdsThrowException() throws Exception {\n        when(persistenceService.selectList(any(SelectListDescriptor.class))).thenThrow(\n                new SBonitaReadException(\"plop\"));\n\n        identityServiceImpl.getUsers(Collections.singletonList(1L));\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Test\n    public void getUsersByNames() throws Exception {\n        final SUser sUser1 = mock(SUser.class);\n        final SUser sUser2 = mock(SUser.class);\n        final List<SUser> users = Arrays.asList(sUser1, sUser2);\n        final List<String> names = Arrays.asList(\"matti\", \"marja\", \"taina\");\n        doReturn(users).when(persistenceService).selectList(any(SelectListDescriptor.class));\n        Assert.assertEquals(users, identityServiceImpl.getUsersByUsername(names));\n    }\n\n    @Test\n    public void getUsersByNullNames() throws Exception {\n        assertTrue(identityServiceImpl.getUsersByUsername(null).isEmpty());\n    }\n\n    @Test\n    public void getUsersByEmptyNames() throws Exception {\n        assertTrue(identityServiceImpl.getUsersByUsername(Collections.<String> emptyList()).isEmpty());\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Test(expected = SIdentityException.class)\n    public void getUsersByNamesThrowException() throws Exception {\n        when(persistenceService.selectList(any(SelectListDescriptor.class)))\n                .thenThrow(new SBonitaReadException(\"plop\"));\n        identityServiceImpl.getUsersByUsername(Arrays.asList(\"hannu\"));\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#searchUsers(org.bonitasoft.engine.persistence.QueryOptions)}.\n     */\n    @Test\n    public void searchUsers() throws Exception {\n        final QueryOptions options = new QueryOptions(0, 10);\n        final SUser user = mock(SUser.class);\n        when(persistenceService.searchEntity(SUser.class, options, null)).thenReturn(Collections.singletonList(user));\n\n        assertEquals(user, identityServiceImpl.searchUsers(options).get(0));\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void searchUsersThrowException() throws SBonitaReadException {\n        final QueryOptions options = new QueryOptions(0, 10);\n        doThrow(new SBonitaReadException(\"\")).when(persistenceService).searchEntity(SUser.class, options, null);\n\n        identityServiceImpl.searchUsers(options).get(0);\n    }\n\n    @Test\n    public void should_create_user_with_icon_and_userLogin() throws Exception {\n        //given\n        SUser sUser = new SUser();\n        sUser.setId(NEW_ICON_ID);\n        SIcon sIcon = new SIcon(\"image/jpeg\", \"iconContent\".getBytes());\n        sIcon.setId(NEW_ICON_ID);\n        when(iconService.createIcon(any(), any())).thenReturn(sIcon);\n        //when\n        identityServiceImpl.createUser(sUser, null, null, \"test.jpg\", \"iconContent\".getBytes());\n        //then\n\n        verify(recorder, times(1))\n                .recordInsert(argThat(perInsertRecord -> (perInsertRecord.getEntity() instanceof SUser)\n                        && ((SUser) perInsertRecord.getEntity()).getIconId() == NEW_ICON_ID), eq(\"USER\"));\n        verify(recorder, times(1)).recordInsert(\n                argThat(perInsertRecord -> perInsertRecord.getEntity() instanceof SUserLogin), eq(\"USER_LOGIN\"));\n        verify(iconService).createIcon(\"test.jpg\", \"iconContent\".getBytes());\n\n    }\n\n    @Test\n    public void should_updateUser_create_the_icon_if_it_does_not_exists() throws Exception {\n        //given\n        haveUser();\n        //when\n        EntityUpdateDescriptor iconUpdateDescriptor = new EntityUpdateDescriptor();\n        iconUpdateDescriptor.addField(\"filename\", \"theNewIcon.gif\");\n        iconUpdateDescriptor.addField(\"content\", \"theContent\".getBytes());\n        identityServiceImpl.updateUser(USER_ID, new EntityUpdateDescriptor(), null, null, iconUpdateDescriptor);\n        //then\n        verify(recorder).recordUpdate(argThat(updateRecord -> updateRecord.getEntity() instanceof SUser\n                && updateRecord.getFields().containsKey(\"iconId\")), nullable(String.class));\n        verify(iconService).replaceIcon(\"theNewIcon.gif\", \"theContent\".getBytes(), null);\n    }\n\n    @Test\n    public void should_updateUser_create_new_icon_if_it_exists() throws Exception {\n        //given\n        SUser sUser = haveUser();\n        SIcon sIcon = haveIcon(sUser);\n        //when\n        EntityUpdateDescriptor iconUpdateDescriptor = new EntityUpdateDescriptor();\n        iconUpdateDescriptor.addField(\"filename\", \"theNewIcon.jpg\");\n        iconUpdateDescriptor.addField(\"content\", \"updated content\".getBytes());\n\n        identityServiceImpl.updateUser(USER_ID, new EntityUpdateDescriptor(), null, null, iconUpdateDescriptor);\n        //then\n        verify(recorder).recordUpdate(updateRecordArgumentCaptor.capture(), nullable(String.class));\n        verify(iconService).replaceIcon(\"theNewIcon.jpg\", \"updated content\".getBytes(), sIcon.getId());\n    }\n\n    @Test\n    public void should_update_user_and_invoke_replace_icon_with_icon_content_null() throws Exception {\n        //given\n        SUser sUser = haveUser();\n        SIcon sIcon = haveIcon(sUser);\n        //when\n        EntityUpdateDescriptor iconUpdateDescriptor = new EntityUpdateDescriptor();\n        iconUpdateDescriptor.addField(\"filename\", \"filename\");\n        iconUpdateDescriptor.addField(\"content\", null);\n        identityServiceImpl.updateUser(USER_ID, new EntityUpdateDescriptor(), null, null, iconUpdateDescriptor);\n        //then\n        verify(recorder).recordUpdate(argThat(updateRecord -> updateRecord.getEntity() instanceof SUser\n                && updateRecord.getFields().containsKey(\"iconId\")), nullable(String.class));\n        verify(iconService).replaceIcon(\"filename\", null, sIcon.getId());\n        verifyNoMoreInteractions(iconService);\n    }\n\n    private SUser haveUser() throws SUserNotFoundException {\n        SUser sUser = new SUser();\n        sUser.setId(USER_ID);\n        doReturn(sUser).when(identityServiceImpl).getUser(USER_ID);\n        return sUser;\n    }\n\n    @Test\n    public void should_deleteUser_delete_the_icon_if_it_exists() throws Exception {\n        //given\n        SUser sUser = haveUser();\n        SIcon icon = haveIcon(sUser);\n        //when\n        identityServiceImpl.deleteUser(USER_ID);\n        //then\n        verify(recorder).recordDelete(new DeleteRecord(sUser), \"USER\");\n        verify(iconService).deleteIcon(icon.getId());\n    }\n\n    @Test\n    public void should_deleteUser_not_delete_the_icon_if_it_does_not_exists() throws Exception {\n        //given\n        SUser sUser = haveUser();\n        //when\n        identityServiceImpl.deleteUser(USER_ID);\n        //then\n        verify(recorder, times(1)).recordDelete(deleteRecordArgumentCaptor.capture(), nullable(String.class));\n        assertThat(deleteRecordArgumentCaptor.getAllValues()).extracting(\"entity\").containsOnly(sUser);\n    }\n\n    private SIcon haveIcon(SUser sUser) throws SBonitaReadException {\n        sUser.setIconId(ICON_ID);\n        SIcon icon = new SIcon(\"image/gif\", \"theContent\".getBytes());\n        icon.setId(ICON_ID);\n        return icon;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/test/java/org/bonitasoft/engine/identity/impl/MD5CredentialsEncrypterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.Test;\n\npublic class MD5CredentialsEncrypterTest {\n\n    @Test\n    public void hash_should_hash_password_using_md5() {\n        final MD5CredentialsEncrypter encrypter = new MD5CredentialsEncrypter();\n        final String hash = encrypter.hash(\"123ñ\");\n        assertThat(hash).isEqualTo(\"jTIqcNwrbMbZJEf20AFxAQ==\");\n    }\n\n    @Test\n    public void check_should_compare_hash_password_with_a_clear_password() {\n        final MD5CredentialsEncrypter encrypter = new MD5CredentialsEncrypter();\n        final boolean match = encrypter.check(\"123ñ\", \"jTIqcNwrbMbZJEf20AFxAQ==\");\n        assertThat(match).isTrue();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-identity/src/test/java/org/bonitasoft/engine/identity/model/SUserTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.identity.model;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.Test;\n\npublic class SUserTest {\n\n    @Test\n    public void toString_should_not_display_password() throws Exception {\n        //given\n        final SUser sUser = new SUser();\n        sUser.setUserName(\"walter.bates\");\n        sUser.setPassword(\"bpm\");\n\n        //when\n        final String string = sUser.toString();\n\n        //then\n        assertThat(string).as(\"should not display password!\").doesNotContain(\"password\").doesNotContain(\"bpm\");\n    }\n}\n"
  },
  {
    "path": "services/bonita-incident/build.gradle",
    "content": "dependencies {\n    api project(':bpm:bonita-common')\n    api project(':bpm:bonita-core:bonita-home-server')\n    testImplementation libs.mockitoCore\n    testImplementation libs.systemRules\n    testRuntimeOnly libs.logback\n    testImplementation libs.assertj\n}\n"
  },
  {
    "path": "services/bonita-incident/src/main/java/org/bonitasoft/engine/incident/FileLoggerIncidentHandler.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.incident;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.slf4j.Marker;\nimport org.slf4j.MarkerFactory;\n\n/**\n * Report incident in a log file located inside the bonita home\n * Is a tenant service must be declared in tenant part\n *\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\n\npublic class FileLoggerIncidentHandler implements IncidentHandler {\n\n    Logger logger = LoggerFactory.getLogger(FileLoggerIncidentHandler.class);\n\n    public FileLoggerIncidentHandler() {\n    }\n\n    @Override\n    public void handle(final Incident incident) {\n        Marker INCIDENT = MarkerFactory.getMarker(\"INCIDENT\");\n        logger.error(INCIDENT, \"An incident occurred: {}\", incident.getDescription());\n        logger.error(INCIDENT, \"Exception was:\", incident.getCause());\n        logger.error(INCIDENT, \"We were unable to handle the failure on the elements because of\",\n                incident.getExceptionWhenHandlingFailure());\n        final String recoveryProcedure = incident.getRecoveryProcedure();\n        if (recoveryProcedure != null && !recoveryProcedure.isEmpty()) {\n            logger.error(INCIDENT, \"Procedure to recover: {}\", recoveryProcedure);\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-incident/src/main/java/org/bonitasoft/engine/incident/Incident.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.incident;\n\n/**\n * @author Baptiste Mesta\n */\npublic class Incident {\n\n    private final String description;\n\n    private final String recoveryProcedure;\n\n    private final Throwable cause;\n\n    private final Throwable exceptionWhenHandlingFailure;\n\n    public Incident(final String description, final String recoveryProcedure, final Throwable cause,\n            final Throwable exceptionWhenHandlingFailure) {\n        super();\n        this.description = description;\n        this.recoveryProcedure = recoveryProcedure;\n        this.cause = cause;\n        this.exceptionWhenHandlingFailure = exceptionWhenHandlingFailure;\n    }\n\n    public String getDescription() {\n        return description;\n    }\n\n    public String getRecoveryProcedure() {\n        return recoveryProcedure;\n    }\n\n    public Throwable getCause() {\n        return cause;\n    }\n\n    public Throwable getExceptionWhenHandlingFailure() {\n        return exceptionWhenHandlingFailure;\n    }\n\n    @Override\n    public String toString() {\n        return \"Incident [description=\" + description + \", recoveryProcedure=\" + recoveryProcedure + \"]\";\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-incident/src/main/java/org/bonitasoft/engine/incident/IncidentHandler.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.incident;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface IncidentHandler {\n\n    void handle(Incident incident);\n\n}\n"
  },
  {
    "path": "services/bonita-incident/src/main/java/org/bonitasoft/engine/incident/IncidentService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.incident;\n\n/**\n * Service that report incidents to an administrator\n *\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface IncidentService {\n\n    /**\n     * Report an incident\n     *\n     * @param incident the incident to be reported\n     */\n    void report(Incident incident);\n\n}\n"
  },
  {
    "path": "services/bonita-incident/src/main/java/org/bonitasoft/engine/incident/IncidentServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.incident;\n\nimport java.util.List;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Baptsite Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\n\npublic class IncidentServiceImpl implements IncidentService {\n\n    private final Logger logger = LoggerFactory.getLogger(IncidentServiceImpl.class);\n    private final List<IncidentHandler> handlers;\n\n    public IncidentServiceImpl(final List<IncidentHandler> handlers) {\n        this.handlers = handlers;\n    }\n\n    @Override\n    public void report(final Incident incident) {\n        for (final IncidentHandler handler : handlers) {\n            try {\n                handler.handle(incident);\n            } catch (final Exception t) {\n                logger.error(\"Unable to report an incident using the handler {} incident was {}\", handler, incident);\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-incident/src/test/java/org/bonitasoft/engine/incident/BeforeAllLoggerInitializer.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.incident;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * This class is used to set System Property before the logger initializes.\n * This class must be initialized before everything else in all test classes of this module !\n * Otherwise, the logger is initialized before the System Property is set, resulting in the logback conf not aware of\n * the System Property value.\n *\n * @author Emmanuel Duchastenier\n */\npublic class BeforeAllLoggerInitializer {\n\n    static final String INCIDENT_LOG_PATH = \"build/log/\";\n\n    static final String INCIDENT_LOG_PATH_PROP_NAME = \"INCIDENT_LOG_PATH\";\n\n    static {\n        System.setProperty(INCIDENT_LOG_PATH_PROP_NAME, INCIDENT_LOG_PATH);\n        LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);\n    }\n\n    public static void initialize() {\n    }\n}\n"
  },
  {
    "path": "services/bonita-incident/src/test/java/org/bonitasoft/engine/incident/FileLoggerIncidentHandlerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.incident;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.incident.BeforeAllLoggerInitializer.INCIDENT_LOG_PATH;\nimport static org.bonitasoft.engine.incident.BeforeAllLoggerInitializer.INCIDENT_LOG_PATH_PROP_NAME;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\n\nimport org.bonitasoft.engine.io.IOUtil;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.SystemOutRule;\nimport org.junit.runner.RunWith;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class FileLoggerIncidentHandlerTest {\n\n    static {\n        BeforeAllLoggerInitializer.initialize();\n    }\n\n    @Rule\n    public final SystemOutRule systemOutRule = new SystemOutRule().enableLog().muteForSuccessfulTests();\n\n    @Before\n    public void init() throws IOException {\n        Files.createDirectories(Path.of(INCIDENT_LOG_PATH));\n    }\n\n    @After\n    public void clean() throws IOException {\n        IOUtil.deleteDir(new File(INCIDENT_LOG_PATH));\n    }\n\n    @Test\n    public void should_write_into_file_when_handle_incident() throws Exception {\n        //Given\n        long tenantId = 1;\n\n        String cause = \"an unexpected exception\";\n        String handlingFailure = \"unable to handle failure\";\n\n        String recovery = \"recovery\";\n        String description = \"test\";\n        final Incident incident = new Incident(description, recovery, new Exception(cause),\n                new Exception(handlingFailure));\n        FileLoggerIncidentHandler fileLoggerIncidentHandler = new FileLoggerIncidentHandler();\n\n        //When\n        fileLoggerIncidentHandler.handle(incident);\n\n        String expectedDescription = \"An incident occurred: \" + description;\n        String expectedRecoveryProcedureMessage = \"Procedure to recover: \" + recovery;\n\n        // as defined in logback.xml all logs are logged in system out, so we check that incidents are also logged to the console.\n        assertThat(systemOutRule.getLog())\n                .contains(expectedDescription)\n                .contains(cause)\n                .contains(handlingFailure)\n                .contains(expectedRecoveryProcedureMessage);\n\n        // as defined in logback.xml, logs marked with INCIDENT should be logged also in a specific file.\n        String log = Files.readString(new File(INCIDENT_LOG_PATH + \"/incidents.log\").toPath());\n\n        assertThat(log).contains(expectedDescription)\n                .contains(cause)\n                .contains(handlingFailure)\n                .contains(expectedRecoveryProcedureMessage);\n\n        System.clearProperty(INCIDENT_LOG_PATH_PROP_NAME);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-incident/src/test/java/org/bonitasoft/engine/incident/IncidentServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.incident;\n\nimport static org.mockito.ArgumentMatchers.nullable;\nimport static org.mockito.Mockito.*;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class IncidentServiceImplTest {\n\n    static {\n        BeforeAllLoggerInitializer.initialize();\n    }\n\n    private IncidentHandler handler1;\n\n    private IncidentHandler handler2;\n\n    private IncidentService incidentService;\n\n    @Before\n    public void before() {\n        final List<IncidentHandler> handlers = new ArrayList<>(2);\n        handler1 = mock(IncidentHandler.class);\n        handlers.add(handler1);\n        handler2 = mock(IncidentHandler.class);\n        handlers.add(handler2);\n        incidentService = new IncidentServiceImpl(handlers);\n    }\n\n    @Test\n    public void reportCallAllHandlers() {\n        final Incident incident = new Incident(\"test\", \"recovery\", null, null);\n        incidentService.report(incident);\n        verify(handler1, times(1)).handle(incident);\n        verify(handler2, times(1)).handle(incident);\n    }\n\n    @Test\n    public void reportCallAllHandlersEvenIfFirstThrowException() {\n        doThrow(new RuntimeException()).when(handler1).handle(nullable(Incident.class));\n        final Incident incident = new Incident(\"test\", \"recovery\", null, null);\n        incidentService.report(incident);\n        verify(handler2, times(1)).handle(incident);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-incident/src/test/resources/logback.xml",
    "content": "<configuration debug=\"false\" scan=\"false\">\n\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->\n        <encoder>\n            <pattern>%d{HH:mm:ss.SSS}|%-5level|%logger{16}| %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <logger name=\"org.bonitasoft\" level=\"INFO\" />\n    <logger name=\"org.bonitasoft.platform\" level=\"DEBUG\" />\n\n    <!--TRACE level displays all relevant jdbc transaction information-->\n    <!--<logger name=\"org.springframework\" level=\"TRACE\" />-->\n\n    <logger name=\"org.springframework\" level=\"WARN\" />\n    <logger name=\"org.springframework.jdbc\" level=\"INFO\" />\n    <logger name=\"org.springframework.jdbc.support\" level=\"WARN\" />\n\n    <logger name=\"org.springframework.jdbc.datasource.init.ScriptUtils\" level=\"OFF\" />\n\n\n    <!-- appender to log all logs marked as INCIDENT to specific file -->\n    <appender name=\"INCIDENT_FILE\" class=\"ch.qos.logback.core.FileAppender\">\n        <!-- the filter element -->\n        <filter class=\"ch.qos.logback.core.filter.EvaluatorFilter\">\n            <evaluator class=\"ch.qos.logback.classic.boolex.OnMarkerEvaluator\">\n                <marker>INCIDENT</marker>\n            </evaluator>\n            <onMismatch>DENY</onMismatch>\n        </filter>\n        <file>${INCIDENT_LOG_PATH}/incidents.log</file>\n        <encoder>\n            <pattern>%d{HH:mm:ss.SSS}|%-5level|%logger{16}| %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <root level=\"WARN\">\n        <appender-ref ref=\"STDOUT\" />\n        <appender-ref ref=\"INCIDENT_FILE\" />\n    </root>\n\n</configuration>\n"
  },
  {
    "path": "services/bonita-lock/build.gradle",
    "content": "\n\ndependencies {\n    api project(':services:bonita-commons')\n    api project(':services:bonita-session')\n    testImplementation libs.assertj\n    testImplementation libs.logback\n}\n"
  },
  {
    "path": "services/bonita-lock/src/main/java/org/bonitasoft/engine/lock/BonitaLock.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.lock;\n\nimport java.util.Objects;\n\npublic class BonitaLock {\n\n    private final String objectType;\n    private final long objectToLockId;\n\n    public BonitaLock(final String objectType, final long objectToLockId) {\n        super();\n        this.objectType = objectType;\n        this.objectToLockId = objectToLockId;\n    }\n\n    public String getObjectType() {\n        return objectType;\n    }\n\n    public long getObjectToLockId() {\n        return objectToLockId;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (o == null || getClass() != o.getClass())\n            return false;\n        BonitaLock that = (BonitaLock) o;\n        return objectToLockId == that.objectToLockId &&\n                Objects.equals(objectType, that.objectType);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(objectType, objectToLockId);\n    }\n\n    @Override\n    public String toString() {\n        return \"BonitaLock[\" + objectType + \":\" + objectToLockId + \"]\";\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-lock/src/main/java/org/bonitasoft/engine/lock/LockService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.lock;\n\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.locks.ReentrantReadWriteLock;\n\n/**\n * This service allows to synchronize access to a resource using ReadWrite lock pattern.\n * Creating an exclusive lock allows to be the only one at a time accessing the resource.\n *\n * @see ReentrantReadWriteLock\n * @author Elias Ricken de Medeiros\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface LockService {\n\n    void unlock(BonitaLock lock) throws SLockException;\n\n    /**\n     * Acquire the lock for the object having type and id in parameters<br>\n     * This method wait for the lock to be available\n     *\n     * @param objectToLockId\n     * @param objectType\n     * @return the lock\n     * @throws SLockException when we were unable to lock due to an unexpected error\n     * @throws SLockTimeoutException when we were unable to lock due to a timeout\n     */\n    BonitaLock lock(long objectToLockId, String objectType) throws SLockException, SLockTimeoutException;\n\n    /**\n     * Acquire the lock for the object having type and id in parameters waiting maximum timeout<br>\n     * This method wait for the lock to be available. If it becomes available before the timeout expires the returns the\n     * obtained lock, else returns null\n     *\n     * @param objectToLockId\n     * @param objectType\n     * @param timeout\n     * @param timeUnit\n     * @return the obtained lock if it has been acquired before the timeout expires or null if the timeout has expired.\n     * @throws SLockException when we were unable to lock (not because of the timeout)\n     */\n    BonitaLock tryLock(long objectToLockId, String objectType, long timeout, TimeUnit timeUnit)\n            throws SLockException;\n}\n"
  },
  {
    "path": "services/bonita-lock/src/main/java/org/bonitasoft/engine/lock/MemoryLockService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.lock;\n\nimport static java.util.Collections.synchronizedMap;\nimport static java.util.concurrent.TimeUnit.SECONDS;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.locks.ReentrantLock;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;\nimport org.springframework.stereotype.Component;\n\n@Component\n@ConditionalOnSingleCandidate(LockService.class)\npublic class MemoryLockService implements LockService {\n\n    private final Logger logger = LoggerFactory.getLogger(MemoryLockService.class);\n\n    // need to have a synchronized map to synchronize the get with map modifications\n    private final Map<String, ReentrantLock> locks = synchronizedMap(new HashMap<>());\n    private final int lockTimeoutSeconds;\n\n    public MemoryLockService(@Value(\"${bonita.platform.lock.memory.timeout}\") int lockTimeoutSeconds) {\n        this.lockTimeoutSeconds = lockTimeoutSeconds;\n    }\n\n    @Override\n    public void unlock(BonitaLock lock) throws SLockException {\n        String key = buildKey(lock.getObjectToLockId(), lock.getObjectType());\n        locks.computeIfPresent(key, (k, l) -> {\n            if (l.hasQueuedThreads()) {\n                logger.debug(\"Lock released {}, keeping it, some other threads are requesting it\", lock);\n                l.unlock();\n                return l;\n            }\n            if (l.getHoldCount() > 1) {\n                logger.debug(\"Lock released {}, keeping it, it was locked multiple times by the current thread\", lock);\n                l.unlock();\n                return l;\n            } else {\n                logger.debug(\"Lock released {}, removing it, no other thread is requesting it\", lock);\n                l.unlock();\n                return null;\n            }\n        });\n    }\n\n    private String buildKey(final long objectToLockId, final String objectType) {\n        return String.format(\"%s_%s\", objectType, objectToLockId);\n    }\n\n    @Override\n    public BonitaLock lock(long objectToLockId, String objectType)\n            throws SLockException, SLockTimeoutException {\n        BonitaLock bonitaLock = tryLock(objectToLockId, objectType, lockTimeoutSeconds, SECONDS);\n        if (bonitaLock == null) {\n            throw new SLockTimeoutException(String.format(\"Unable to acquire lock %s,%s in %s seconds\",\n                    objectToLockId, objectType, lockTimeoutSeconds));\n        }\n        return bonitaLock;\n    }\n\n    private ReentrantLock createLock(long objectToLockId, String objectType) {\n        String key = buildKey(objectToLockId, objectType);\n        return locks.computeIfAbsent(key, k -> {\n            ReentrantLock lock = new ReentrantLock();\n            logger.debug(\"Created new lock for key {}\", key);\n            return lock;\n        });\n    }\n\n    @Override\n    public BonitaLock tryLock(long objectToLockId, String objectType, long timeout, TimeUnit timeUnit)\n            throws SLockException {\n        String key = buildKey(objectToLockId, objectType);\n        ReentrantLock lock = createLock(objectToLockId, objectType);\n        try {\n            if (lock.tryLock(timeout, timeUnit)) {\n                //this get need to be synchronized with the unlock that can change the map\n                ReentrantLock currentLockInTheMap = locks.get(key);\n                if (!lock.equals(currentLockInTheMap)) {\n                    //lock was taken but someone replace it in the map, get it next time\n                    // this can happen when the lock is removed just between `createLock` and `tryLock`\n                    // retry it...\n                    logger.debug(\n                            \"Lock for key {} was acquired but it was replaced due to a race condition. We will retry.\",\n                            key);\n                    lock.unlock();\n                    return tryLock(objectToLockId, objectType, timeout, timeUnit);\n                }\n\n                logger.debug(\"Locked acquired for key {}\", key);\n                return new BonitaLock(objectType, objectToLockId);\n            } else {\n                logger.debug(\"Locked was not acquired for key {}\", key);\n                return null;\n            }\n        } catch (InterruptedException e) {\n            throw new SLockException(\"interrupted while trying to get the lock\", e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-lock/src/main/java/org/bonitasoft/engine/lock/SLockException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.lock;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SLockException extends SBonitaException {\n\n    private static final long serialVersionUID = -3762406542199357594L;\n\n    public SLockException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SLockException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SLockException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "services/bonita-lock/src/main/java/org/bonitasoft/engine/lock/SLockTimeoutException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.lock;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\npublic class SLockTimeoutException extends SBonitaException {\n\n    public SLockTimeoutException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "services/bonita-lock/src/test/java/org/bonitasoft/engine/lock/MemoryLockServiceTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.lock;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.*;\n\nimport java.util.Random;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Semaphore;\nimport java.util.concurrent.TimeUnit;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Baptiste Mesta\n */\npublic class MemoryLockServiceTest {\n\n    private ExecutorService executorService = Executors.newSingleThreadExecutor();\n\n    private final class LockThread extends Thread {\n\n        private BonitaLock lock;\n\n        private final int id;\n\n        private final String type;\n\n        public LockThread(final int id, final String type) {\n            this.id = id;\n            this.type = type;\n        }\n\n        @Override\n        public void run() {\n            try {\n                lock = memoryLockService.lock(id, type);\n            } catch (final SLockException | SLockTimeoutException e) {\n                // NOTHING\n            }\n        }\n\n        public boolean isLockObtained() {\n            return lock != null;\n        }\n    }\n\n    private final class TryLockThread extends Thread {\n\n        private BonitaLock lock;\n\n        private final int id;\n\n        private final String type;\n\n        private final Semaphore semaphore;\n\n        public TryLockThread(final int id, final String type, final Semaphore semaphore) {\n            this.id = id;\n            this.type = type;\n            this.semaphore = semaphore;\n        }\n\n        @Override\n        public void run() {\n            try {\n                semaphore.acquire();\n                lock = memoryLockService.tryLock(id, type, 20, TimeUnit.SECONDS);\n                semaphore.acquire();\n                memoryLockService.unlock(lock);\n                lock = null;\n            } catch (final InterruptedException e) {\n                e.printStackTrace();\n            } catch (SLockException e) {\n                e.printStackTrace();\n            }\n        }\n\n        public void waitForLockToBeObtained(final long maxTime) throws InterruptedException {\n            final long initialTimestamp = System.currentTimeMillis();\n            while (maxTime >= System.currentTimeMillis() - initialTimestamp) {\n                if (lock != null) {\n                    return;\n                }\n                Thread.sleep(20);\n            }\n            throw new InterruptedException(\"Lock not obtained after waiting \" + maxTime + \" ms\");\n        }\n    }\n\n    private MemoryLockService memoryLockService;\n\n    @Before\n    public void before() {\n        memoryLockService = new MemoryLockService(1);\n    }\n\n    @Test\n    public void testUnlock() throws Exception {\n\n        final BonitaLock lock = memoryLockService.lock(5, \"a\");\n        final LockThread lockThread = new LockThread(5, \"a\");\n        lockThread.start();\n        Thread.sleep(100);\n        memoryLockService.unlock(lock);\n        lockThread.join(1200);\n        assertTrue(\"should not be able to lock\", lockThread.isLockObtained());\n    }\n\n    @Test\n    public void should_lock_multiple_times_on_the_same_thread() throws Exception {\n        BonitaLock bonitaLock = memoryLockService.lock(123, \"abc\");\n        memoryLockService.lock(123, \"abc\");\n\n        //Unable to lock in an other thread\n        assertThat(tryLockInAnOtherThread(123, \"abc\")).isNull();\n\n        //Unable to lock in an other thread: the lock is still hold once by the current thread\n        memoryLockService.unlock(bonitaLock);\n        assertThat(tryLockInAnOtherThread(123, \"abc\")).isNull();\n\n        //Able to unlock the thread release all holds\n        memoryLockService.unlock(bonitaLock);\n        assertThat(tryLockInAnOtherThread(123, \"abc\")).isNotNull();\n    }\n\n    private BonitaLock tryLockInAnOtherThread(int objectToLockId, String abc)\n            throws InterruptedException, java.util.concurrent.ExecutionException {\n        return executorService\n                .submit(() -> memoryLockService.tryLock(objectToLockId, abc, 10, TimeUnit.MILLISECONDS))\n                .get();\n    }\n\n    @Test\n    public void testLock() throws Exception {\n        memoryLockService.lock(3, \"a\");\n        final LockThread lockThread = new LockThread(4, \"a\");\n        lockThread.start();\n        lockThread.join(100);\n        assertTrue(\"should be able to lock\", lockThread.isLockObtained());\n    }\n\n    @Test\n    public void testLockTimeout() throws Exception {\n        memoryLockService.lock(2, \"a\");\n        final LockThread lockThread = new LockThread(2, \"a\");\n        lockThread.start();\n        lockThread.join(1200);\n        assertFalse(\"should not be able to lock\", lockThread.isLockObtained());\n    }\n\n    /*\n     * Issue with lock removed from map too early: the lock is removed from the map but a thread have a reference on it\n     * T1 have the lock L1\n     * T2 fait trylock et est blocké\n     * T1 release\n     * T2 block L1\n     * T1 enleve de la map\n     * T3 appel tryLock\n     * T3 lock L2\n     */\n    @Test\n    public void testRemoveLockFromMap() throws Exception {\n        int rd = new Random().nextInt();\n        final Semaphore s1 = new Semaphore(1);\n        final Semaphore s2 = new Semaphore(1);\n        final Semaphore s3 = new Semaphore(1);\n        final TryLockThread t1 = new TryLockThread(rd, \"t\", s1);\n        final TryLockThread t2 = new TryLockThread(rd, \"t\", s2);\n        final TryLockThread t3 = new TryLockThread(rd, \"t\", s3);\n        s1.acquire();\n        s2.acquire();\n        s3.acquire();\n        t1.start();\n        t2.start();\n        t3.start();\n        // state : S1 ∈ T, S2 ∈ T, S3 ∈ T, S1 <- T1, S2 <- T2, S3 <- T3, BL free\n        s1.release();\n        Thread.sleep(5);\n        // t1 take the lock | state : S1 ∈ T1, S2 ∈ T, S3 ∈ T, S1 <- T1, S2 <- T2, S3 <- T3, BL ∈ T1\n        s2.release();\n        Thread.sleep(5);\n        // t2 is locked | state : S1 ∈ T1, S2 ∈ T2, S3 ∈ T, S1 <- T1, S3 <- T3, BL ∈ T1, BL <- T1\n        s1.release();\n        Thread.sleep(20);\n        // t1 unlock & t2 have the lock | state : S1, S2 ∈ T2, S3 ∈ T, S3 <- T3, BL ∈ T2\n        t2.waitForLockToBeObtained(500);\n        s3.release();\n        Thread.sleep(20);\n        // t3 try acquire | state : S1, S2 ∈ T2, S3 ∈ T3, BL ∈ T2, BL <- T3\n        // t3 should not be able to lock\n        try {\n            t3.waitForLockToBeObtained(500);\n            fail();\n        } catch (final InterruptedException ignored) {\n        }\n        s2.release();\n        Thread.sleep(20);\n        // now it should be able to have it | state : S1, S2, S3 ∈ T3, BL ∈ T3\n        t3.waitForLockToBeObtained(500);\n        s3.release();\n        // state : S1, S2, S3, BL\n    }\n}\n"
  },
  {
    "path": "services/bonita-log/build.gradle",
    "content": "\n\ndependencies {\n    api project(':services:bonita-session')\n    api project(':services:bonita-commons')\n    api project(':services:bonita-persistence')\n    api project(':services:bonita-builder')\n    api project(':services:bonita-platform')\n    api project(':services:bonita-transaction')\n    testImplementation libs.assertj\n    testImplementation libs.mockitoCore\n    testImplementation libs.logback\n\n    annotationProcessor libs.lombok\n    compileOnly libs.lombok\n}\n"
  },
  {
    "path": "services/bonita-log/src/main/java/org/bonitasoft/engine/queriablelogger/model/SQueriableLog.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.queriablelogger.model;\n\nimport java.util.Calendar;\nimport java.util.Date;\n\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.EnumType;\nimport javax.persistence.Enumerated;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n@Entity\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\n@Builder(toBuilder = true)\n@Table(name = \"queriable_log\")\npublic class SQueriableLog implements PersistentObject {\n\n    public static final int STATUS_FAIL = 0;\n    public static final int STATUS_OK = 1;\n    public static final String TIME_STAMP = \"timeStamp\";\n    public static final String YEAR = \"year\";\n    public static final String MONTH = \"month\";\n    public static final String DAY_OF_YEAR = \"dayOfYear\";\n    public static final String WEEK_OF_YEAR = \"weekOfYear\";\n    public static final String USERID = \"userId\";\n    public static final String THREAD_NUMBER = \"threadNumber\";\n    public static final String CLUSTER_NODE = \"clusterNode\";\n    public static final String PRODUCT_VERSION = \"productVersion\";\n    public static final String SEVERITY = \"severity\";\n    public static final String ACTION_TYPE = \"actionType\";\n    public static final String ACTION_SCOPE = \"actionScope\";\n    public static final String ACTION_STATUS = \"actionStatus\";\n    public static final String RAW_MESSAGE = \"rawMessage\";\n    public static final String CALLER_CLASS_NAME = \"callerClassName\";\n    public static final String CALLER_METHOD_NAME = \"callerMethodName\";\n    public static final String NUMERIC_INDEX1 = \"numericIndex1\";\n    public static final String NUMERIC_INDEX2 = \"numericIndex2\";\n    public static final String NUMERIC_INDEX3 = \"numericIndex3\";\n    public static final String NUMERIC_INDEX4 = \"numericIndex4\";\n    public static final String NUMERIC_INDEX5 = \"numericIndex5\";\n\n    @Id\n    private long id;\n    @Column(name = \"log_timestamp\")\n    private long timeStamp;\n    @Column(name = \"whatYear\")\n    private int year;\n    @Column(name = \"whatMonth\")\n    private int month;\n    private int dayOfYear;\n    private int weekOfYear;\n    private String userId;\n    @Builder.Default\n    private long threadNumber = Thread.currentThread().getId();\n    private String clusterNode;\n    private String productVersion;\n    @Enumerated(EnumType.STRING)\n    private SQueriableLogSeverity severity;\n    private String actionType;\n    private String actionScope;\n    @Builder.Default\n    private int actionStatus = -1;\n    @Column(name = \"RAWMESSAGE\")\n    private String rawMessage;\n    private String callerClassName;\n    private String callerMethodName;\n    @Builder.Default\n    private long numericIndex1 = -1;\n    @Builder.Default\n    private long numericIndex2 = -1;\n    @Builder.Default\n    private long numericIndex3 = -1;\n    @Builder.Default\n    private long numericIndex4 = -1;\n    @Builder.Default\n    private long numericIndex5 = -1;\n\n    public static class SQueriableLogBuilder {\n\n        public SQueriableLogBuilder initializeNow() {\n            final Date date = new Date();\n            final Calendar calendar = Calendar.getInstance();\n            calendar.setTime(date);\n            timeStamp(date.getTime());\n            year(calendar.get(Calendar.YEAR));\n            month(calendar.get(Calendar.MONTH) + 1);\n            weekOfYear(calendar.get(Calendar.WEEK_OF_YEAR));\n            dayOfYear(calendar.get(Calendar.DAY_OF_YEAR));\n            return this;\n        }\n    }\n\n    public long getNumericIndex(final int pos) {\n        return switch (pos) {\n            case 0 -> numericIndex1;\n            case 1 -> numericIndex2;\n            case 2 -> numericIndex3;\n            case 3 -> numericIndex4;\n            case 4 -> numericIndex5;\n            default -> throw new IllegalStateException();\n        };\n    }\n\n    public void setNumericIndex(final int pos, final long value) {\n        switch (pos) {\n            case 0:\n                numericIndex1 = value;\n                break;\n            case 1:\n                numericIndex2 = value;\n                break;\n            case 2:\n                numericIndex3 = value;\n                break;\n            case 3:\n                numericIndex4 = value;\n                break;\n            case 4:\n                numericIndex5 = value;\n                break;\n            default:\n                throw new IllegalStateException();\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-log/src/main/java/org/bonitasoft/engine/queriablelogger/model/SQueriableLogSeverity.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.queriablelogger.model;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic enum SQueriableLogSeverity {\n\n    // TODO FIXME use an int for the database\n    BUSINESS, INTERNAL;\n\n}\n"
  },
  {
    "path": "services/bonita-log/src/main/java/org/bonitasoft/engine/queriablelogger/model/builder/ActionType.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.queriablelogger.model.builder;\n\npublic enum ActionType {\n    CREATED, UPDATED, DELETED, EXECUTED, STARTED, STOPPED, SCHEDULED, PAUSED, RESUMED, RESCHEDULED, CLEANED_UP\n}\n"
  },
  {
    "path": "services/bonita-log/src/main/java/org/bonitasoft/engine/queriablelogger/model/builder/HasCRUDEAction.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.queriablelogger.model.builder;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Nicolas Chabanoles\n * @author Matthieu Chaffotte\n */\npublic interface HasCRUDEAction {\n\n    SLogBuilder setActionType(final ActionType actionType);\n\n}\n"
  },
  {
    "path": "services/bonita-log/src/main/java/org/bonitasoft/engine/queriablelogger/model/builder/HasCRUDEActionFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.queriablelogger.model.builder;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Nicolas Chabanoles\n * @author Matthieu Chaffotte\n */\npublic interface HasCRUDEActionFactory {\n\n}\n"
  },
  {
    "path": "services/bonita-log/src/main/java/org/bonitasoft/engine/queriablelogger/model/builder/SLogBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.queriablelogger.model.builder;\n\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Nicolas Chabanoles\n * @author Matthieu Chaffotte\n */\npublic interface SLogBuilder {\n\n    SLogBuilder userId(String userId);\n\n    SLogBuilder clusterNode(String clusterNode);\n\n    SLogBuilder productVersion(String productVersion);\n\n    SLogBuilder severity(SQueriableLogSeverity severity);\n\n    SLogBuilder actionScope(String scope);\n\n    SLogBuilder actionStatus(int status);\n\n    SLogBuilder rawMessage(String rawMessage);\n\n    SLogBuilder callerClassName(String callerClassName);\n\n    SLogBuilder callerMethodName(String callerMethodName);\n\n    SQueriableLog build();\n\n}\n"
  },
  {
    "path": "services/bonita-log/src/main/java/org/bonitasoft/engine/queriablelogger/model/builder/SLogBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.queriablelogger.model.builder;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Nicolas Chabanoles\n * @author Matthieu Chaffotte\n */\npublic interface SLogBuilderFactory {\n\n    SLogBuilder createNewInstance();\n\n}\n"
  },
  {
    "path": "services/bonita-log/src/main/java/org/bonitasoft/engine/queriablelogger/model/builder/SPersistenceLogBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.queriablelogger.model.builder;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SPersistenceLogBuilder extends SLogBuilder {\n\n    SPersistenceLogBuilder objectId(final long objectId);\n\n}\n"
  },
  {
    "path": "services/bonita-log/src/main/java/org/bonitasoft/engine/queriablelogger/model/builder/SPersistenceLogBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.queriablelogger.model.builder;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SPersistenceLogBuilderFactory extends SLogBuilderFactory {\n\n    String getObjectIdKey();\n\n}\n"
  },
  {
    "path": "services/bonita-log/src/main/java/org/bonitasoft/engine/queriablelogger/model/builder/SQueriableLogBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.queriablelogger.model.builder;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Nicolas Chabanoles\n */\npublic class SQueriableLogBuilder implements SLogBuilder {\n\n    private final SQueriableLog.SQueriableLogBuilder builder;\n\n    public SQueriableLogBuilder() {\n        this.builder = SQueriableLog.builder().initializeNow();\n    }\n\n    @Override\n    public SQueriableLogBuilder userId(final String userId) {\n        builder.userId(userId);\n        return this;\n    }\n\n    @Override\n    public SQueriableLogBuilder clusterNode(final String clusterNode) {\n        builder.clusterNode(clusterNode);\n        return this;\n    }\n\n    @Override\n    public SQueriableLogBuilder productVersion(final String productVersion) {\n        builder.productVersion(productVersion);\n        return this;\n    }\n\n    @Override\n    public SQueriableLogBuilder severity(final SQueriableLogSeverity severity) {\n        builder.severity(severity);\n        return this;\n    }\n\n    public SQueriableLogBuilder actionType(final String actionType) {\n        builder.actionType(actionType);\n        return this;\n    }\n\n    @Override\n    public SQueriableLogBuilder actionScope(final String scope) {\n        builder.actionScope(scope);\n        return this;\n    }\n\n    @Override\n    public SQueriableLogBuilder actionStatus(final int status) {\n        builder.actionStatus(status);\n        return this;\n    }\n\n    @Override\n    public SQueriableLogBuilder rawMessage(final String rawMessage) {\n        builder.rawMessage(rawMessage);\n        return this;\n    }\n\n    @Override\n    public SQueriableLogBuilder callerClassName(final String callerClassName) {\n        builder.callerClassName(callerClassName);\n        return this;\n    }\n\n    @Override\n    public SQueriableLogBuilder callerMethodName(final String callerMethodName) {\n        builder.callerMethodName(callerMethodName);\n        return this;\n    }\n\n    public SQueriableLogBuilder numericIndex(final int pos, final long value) {\n        switch (pos) {\n            case 0:\n                builder.numericIndex1(value);\n                break;\n            case 1:\n                builder.numericIndex2(value);\n                break;\n            case 2:\n                builder.numericIndex3(value);\n                break;\n            case 3:\n                builder.numericIndex4(value);\n                break;\n            case 4:\n                builder.numericIndex5(value);\n                break;\n            default:\n                throw new IllegalStateException();\n        }\n        return this;\n    }\n\n    @Override\n    public SQueriableLog build() {\n        final List<String> problems = checkMandatoryFields();\n        if (problems.size() > 0) {\n            throw new MissingMandatoryFieldsException(\"Some mandatory fields are missing: \" + problems);\n        }\n        return builder.build();\n    }\n\n    private List<String> checkMandatoryFields() {\n        SQueriableLog entity = builder.build();\n        final List<String> problems = new ArrayList<String>();\n        if (entity.getSeverity() == null) {\n            problems.add(\"severity\");\n        }\n        if (entity.getActionType() == null) {\n            problems.add(\"actionType\");\n        }\n        if (SQueriableLog.STATUS_FAIL != entity.getActionStatus() && entity.getActionScope() == null) {\n            problems.add(\"actionScope\");\n        }\n        if (entity.getActionStatus() != 0 && entity.getActionStatus() != 1) {\n            problems.add(\"actionStatus (must be 0, for fail or 1, for ok)\");\n        }\n        if (entity.getRawMessage() == null) {\n            problems.add(\"rawMessage\");\n        }\n        return problems;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-log/src/main/java/org/bonitasoft/engine/queriablelogger/model/builder/impl/CRUDELogBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.queriablelogger.model.builder.impl;\n\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity;\nimport org.bonitasoft.engine.queriablelogger.model.builder.ActionType;\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SQueriableLogBuilder;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Nicolas Chabanoles\n * @author Matthieu Chaffotte\n */\npublic abstract class CRUDELogBuilder implements HasCRUDEAction, SLogBuilder {\n\n    protected SQueriableLogBuilder queriableLogBuilder;\n\n    private static final String SEPARATOR = \"_\";\n\n    protected CRUDELogBuilder() {\n        queriableLogBuilder = new SQueriableLogBuilder();\n    }\n\n    protected abstract String getActionTypePrefix();\n\n    @Override\n    public SLogBuilder setActionType(final ActionType actionType) {\n        queriableLogBuilder.actionType(getActionTypePrefix() + SEPARATOR + actionType.name());\n        return this;\n    }\n\n    @Override\n    public SLogBuilder userId(final String userId) {\n        queriableLogBuilder.userId(userId);\n        return this;\n    }\n\n    @Override\n    public SLogBuilder clusterNode(final String clusterNode) {\n        queriableLogBuilder.clusterNode(clusterNode);\n        return this;\n    }\n\n    @Override\n    public SLogBuilder productVersion(final String productVersion) {\n        queriableLogBuilder.productVersion(productVersion);\n        return this;\n    }\n\n    @Override\n    public SLogBuilder severity(final SQueriableLogSeverity severity) {\n        queriableLogBuilder.severity(severity);\n        return this;\n    }\n\n    @Override\n    public SLogBuilder actionScope(final String scope) {\n        queriableLogBuilder.actionScope(scope);\n        return this;\n    }\n\n    @Override\n    public SLogBuilder actionStatus(final int status) {\n        queriableLogBuilder.actionStatus(status);\n        return this;\n    }\n\n    @Override\n    public SLogBuilder rawMessage(final String rawMessage) {\n        queriableLogBuilder.rawMessage(rawMessage);\n        return this;\n    }\n\n    @Override\n    public SLogBuilder callerClassName(final String callerClassName) {\n        queriableLogBuilder.callerClassName(callerClassName);\n        return this;\n    }\n\n    @Override\n    public SLogBuilder callerMethodName(final String callerMethodName) {\n        queriableLogBuilder.callerMethodName(callerMethodName);\n        return this;\n    }\n\n    @Override\n    public SQueriableLog build() {\n        final SQueriableLog log = queriableLogBuilder.build();\n        checkExtraRules(log);\n        return log;\n    }\n\n    protected abstract void checkExtraRules(final SQueriableLog log);\n}\n"
  },
  {
    "path": "services/bonita-log/src/main/java/org/bonitasoft/engine/queriablelogger/model/builder/impl/CRUDELogBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.queriablelogger.model.builder.impl;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SLogBuilderFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SQueriableLogBuilder;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Nicolas Chabanoles\n * @author Matthieu Chaffotte\n */\npublic abstract class CRUDELogBuilderFactory implements HasCRUDEActionFactory, SLogBuilderFactory {\n\n    public CRUDELogBuilderFactory() {\n\n    }\n\n    @Override\n    public SLogBuilder createNewInstance() {\n        return new SQueriableLogBuilder();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-log/src/main/java/org/bonitasoft/engine/queriablelogger/model/builder/impl/MissingMandatoryFieldsException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.queriablelogger.model.builder.impl;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class MissingMandatoryFieldsException extends RuntimeException {\n\n    private static final long serialVersionUID = 6207633035429372416L;\n\n    public MissingMandatoryFieldsException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-log/src/main/java/org/bonitasoft/engine/services/IllegalIndexPositionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.services;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class IllegalIndexPositionException extends SBonitaException {\n\n    private static final long serialVersionUID = 1L;\n\n    public IllegalIndexPositionException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-log/src/main/java/org/bonitasoft/engine/services/QueriableLogSessionProvider.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.services;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic interface QueriableLogSessionProvider {\n\n    /**\n     * Get the logged user\n     *\n     * @return the logged user\n     */\n    String getUserId();\n\n    /**\n     * Get the cluster node when the engine is running in a cluster.\n     *\n     * @return the cluster node\n     */\n    String getClusterNode();\n\n}\n"
  },
  {
    "path": "services/bonita-log/src/main/java/org/bonitasoft/engine/services/QueriableLoggerService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.services;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity;\n\n/**\n * @author Charles Souillard\n * @author Celine Souchet\n */\npublic interface QueriableLoggerService {\n\n    /**\n     * Log the given queriable logs. Only logs having the action type and the severity configured\n     * to as loggable will be logged\n     *\n     * @param queriableLogs\n     * @since 6.0\n     */\n    void log(String callerClassName, String callerMethodName, SQueriableLog... queriableLogs);\n\n    /**\n     * Verify if the given action type and severity are loggable\n     *\n     * @param actionType\n     *        the action type\n     * @param severity\n     *        the severity\n     * @return true if the log is active for the given action type and severity; false otherwise\n     * @since 6.0\n     */\n    boolean isLoggable(final String actionType, final SQueriableLogSeverity severity);\n\n    /**\n     * Get the queriable log from its id.\n     *\n     * @param logId\n     * @return the queriable log\n     * @throws SQueriableLogException\n     * @since 6.0\n     */\n    SQueriableLog getLog(long logId) throws SQueriableLogNotFoundException, SBonitaReadException;\n\n    /**\n     * Get total number of queriable logs\n     *\n     * @return the number of queriable logs\n     * @since 6.0\n     */\n    long getNumberOfLogs() throws SBonitaReadException;\n\n    /**\n     * Get the queriable logs having the given value for the given int index\n     *\n     * @param fromIndex\n     *        first result to be considered(>=0)\n     * @param numberOfLogs\n     *        the max number of queriable logs to be returned (>=0)\n     * @param field\n     * @param order\n     * @return the queriable logs having the given value for the given int index\n     * @throws SQueriableLogException\n     * @since 6.0\n     */\n    List<SQueriableLog> getLogs(int startIndex, int maxResults, final String field, final OrderByType order)\n            throws SBonitaReadException;\n\n    /**\n     * Gets the queriable logs number matching to the given QueryOptions.\n     *\n     * @param QueryOptions\n     *        The criterion used to search sQueriableLog\n     * @return queriable logs number matching to the given searchOptions.\n     * @throws SBonitaReadException\n     * @since 6.0\n     */\n    long getNumberOfLogs(final QueryOptions searchOptions) throws SBonitaReadException;\n\n    /**\n     * Gets the queriable logs matching to the given searchOptions.\n     *\n     * @param searchOptions\n     *        The criterion used to search sQueriableLog\n     * @return logs list matching to the given searchOptions.\n     * @throws SBonitaReadException\n     * @since 6.0\n     */\n    List<SQueriableLog> searchLogs(final QueryOptions searchOptions) throws SBonitaReadException;\n\n}\n"
  },
  {
    "path": "services/bonita-log/src/main/java/org/bonitasoft/engine/services/QueriableLoggerStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.services;\n\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n * @author Matthieu Chaffotte\n */\npublic interface QueriableLoggerStrategy {\n\n    boolean isLoggable(String actionType, SQueriableLogSeverity severity);\n\n}\n"
  },
  {
    "path": "services/bonita-log/src/main/java/org/bonitasoft/engine/services/SQueriableLogException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.services;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SQueriableLogException extends SBonitaException {\n\n    private static final long serialVersionUID = -1052948025332283376L;\n\n    public SQueriableLogException(final String message) {\n        super(message);\n    }\n\n    public SQueriableLogException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SQueriableLogException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-log/src/main/java/org/bonitasoft/engine/services/SQueriableLogNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.services;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SQueriableLogNotFoundException extends SBonitaException {\n\n    private static final long serialVersionUID = -7562889822565946111L;\n\n    public SQueriableLogNotFoundException(final long logId) {\n        super(\"The log identified by :\" + logId + \" does not exist\");\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-log/src/main/java/org/bonitasoft/engine/services/impl/BatchLogSynchronization.java",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.services.impl;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.services.PersistenceService;\nimport org.bonitasoft.engine.services.SPersistenceException;\nimport org.bonitasoft.engine.transaction.BonitaTransactionSynchronization;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n */\nclass BatchLogSynchronization implements BonitaTransactionSynchronization {\n\n    private final PersistenceService persistenceService;\n    private final QueriableLoggerImpl queriableLogger;\n    private final List<SQueriableLog> logs = new ArrayList<>();\n\n    public BatchLogSynchronization(PersistenceService persistenceService, QueriableLoggerImpl queriableLogger) {\n        super();\n        this.persistenceService = persistenceService;\n        this.queriableLogger = queriableLogger;\n    }\n\n    @Override\n    public void afterCompletion(final int transactionState) {\n        queriableLogger.clearSynchronization();\n    }\n\n    @Override\n    public void beforeCompletion() {\n        if (!logs.isEmpty()) {\n            try {\n                persistenceService.insertInBatch(logs);\n                // this is mandatory (probably because we are in a synchronization)\n                persistenceService.flushStatements();\n            } catch (final SPersistenceException e) {\n                throw new SBonitaRuntimeException(e);\n            }\n        }\n    }\n\n    public void addLog(final SQueriableLog sQueriableLog) {\n        logs.add(sQueriableLog);\n    }\n\n    List<SQueriableLog> getLogs() {\n        return logs;\n    }\n}\n"
  },
  {
    "path": "services/bonita-log/src/main/java/org/bonitasoft/engine/services/impl/QueriableLogSessionProviderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.services.impl;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.commons.ExceptionUtils;\nimport org.bonitasoft.engine.services.QueriableLogSessionProvider;\nimport org.bonitasoft.engine.session.SSessionNotFoundException;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.session.model.SSession;\nimport org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor;\nimport org.bonitasoft.engine.sessionaccessor.SessionIdNotSetException;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\n@Slf4j\npublic class QueriableLogSessionProviderImpl implements QueriableLogSessionProvider {\n\n    private final SessionService sessionService;\n\n    private final ReadSessionAccessor sessionAccessor;\n\n    private final ThreadLocal<SSession> localSession = new ThreadLocal<>();\n\n    public QueriableLogSessionProviderImpl(final SessionService sessionService,\n            final ReadSessionAccessor sessionAccessor) {\n        this.sessionService = sessionService;\n        this.sessionAccessor = sessionAccessor;\n    }\n\n    private SSession getSession() {\n        SSession session = localSession.get();\n        try {\n            if (session == null || session.getId() != sessionAccessor.getSessionId()) {\n                long sessionId;\n                sessionId = sessionAccessor.getSessionId();\n                session = sessionService.getSession(sessionId);\n                localSession.set(session);\n            }\n        } catch (final SessionIdNotSetException e) {\n            // system: no session\n            return null;\n        } catch (final SSessionNotFoundException e) {\n            log.warn(ExceptionUtils.printLightWeightStacktrace(e));\n        }\n        return session;\n    }\n\n    @Override\n    public String getUserId() {\n        final SSession session = getSession();\n        if (session != null) {\n            return session.getUserName();\n        }\n        return SessionService.SYSTEM;\n    }\n\n    @Override\n    public String getClusterNode() {\n        final SSession session = getSession();\n        if (session != null) {\n            return session.getClusterNode();\n        }\n        return \"\";\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-log/src/main/java/org/bonitasoft/engine/services/impl/QueriableLogUpdater.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.services.impl;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.platform.PlatformService;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.services.QueriableLogSessionProvider;\n\n/**\n * @author Elias Ricken de Medeiros\n */\n@Slf4j\npublic class QueriableLogUpdater {\n\n    private static final int MAX_MESSAGE_LENGTH = 255;\n    private final QueriableLogSessionProvider sessionProvider;\n    private final PlatformService platformService;\n\n    public QueriableLogUpdater(final QueriableLogSessionProvider sessionProvider,\n            final PlatformService platformService) {\n        this.sessionProvider = sessionProvider;\n        this.platformService = platformService;\n    }\n\n    public SQueriableLog buildFinalLog(final String callerClassName, final String callerMethodName,\n            final SQueriableLog sQueriableLog) {\n        final SQueriableLog.SQueriableLogBuilder builder = sQueriableLog.toBuilder();\n\n        final String rawMessage = sQueriableLog.getRawMessage();\n        if (rawMessage.length() > MAX_MESSAGE_LENGTH) {\n            final String truncatedMessage = rawMessage.substring(0, MAX_MESSAGE_LENGTH);\n            builder.rawMessage(truncatedMessage);\n            if (log.isInfoEnabled()) {\n                final StringBuilder stb = new StringBuilder();\n                stb.append(\"The queriable log message is too long and will be truncated to \");\n                stb.append(MAX_MESSAGE_LENGTH);\n                stb.append(\" characters. The original message is '\");\n                stb.append(rawMessage);\n                stb.append(\"'. It will be truncated to '\");\n                stb.append(truncatedMessage);\n                stb.append(\"'\");\n                log.info(stb.toString());\n            }\n        }\n        return builder.callerClassName(callerClassName).callerMethodName(callerMethodName)\n                .userId(sessionProvider.getUserId()).clusterNode(sessionProvider.getClusterNode())\n                .productVersion(platformService.getSPlatformProperties().getPlatformVersion())\n                .build();\n\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-log/src/main/java/org/bonitasoft/engine/services/impl/QueriableLoggerImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.services.impl;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.platform.PlatformService;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity;\nimport org.bonitasoft.engine.services.PersistenceService;\nimport org.bonitasoft.engine.services.QueriableLogSessionProvider;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\nimport org.bonitasoft.engine.services.QueriableLoggerStrategy;\nimport org.bonitasoft.engine.services.SQueriableLogNotFoundException;\nimport org.bonitasoft.engine.transaction.STransactionNotFoundException;\nimport org.bonitasoft.engine.transaction.TransactionService;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Bole Zhang\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic class QueriableLoggerImpl implements QueriableLoggerService {\n\n    private final PersistenceService persistenceService;\n    private final QueriableLoggerStrategy loggerStrategy;\n    private final TransactionService transactionService;\n\n    private final QueriableLogUpdater logUpdater;\n    private final ThreadLocal<BatchLogSynchronization> synchronizations = new ThreadLocal<>();\n\n    public QueriableLoggerImpl(PersistenceService persistenceService,\n            TransactionService transactionService,\n            QueriableLoggerStrategy loggerStrategy,\n            QueriableLogSessionProvider sessionProvider,\n            PlatformService platformService) {\n        this.transactionService = transactionService;\n        this.persistenceService = persistenceService;\n        this.loggerStrategy = loggerStrategy;\n        logUpdater = new QueriableLogUpdater(sessionProvider, platformService);\n    }\n\n    @Override\n    public long getNumberOfLogs() throws SBonitaReadException {\n        final Map<String, Object> emptyMap = Collections.emptyMap();\n        return persistenceService\n                .selectOne(new SelectOneDescriptor<>(\"getNumberOfLogs\", emptyMap, SQueriableLog.class, Long.class));\n    }\n\n    @Override\n    public List<SQueriableLog> getLogs(final int startIndex, final int maxResults, final String field,\n            final OrderByType order) throws SBonitaReadException {\n        return persistenceService.selectList(\n                new SelectListDescriptor<>(\"getLogs\", null, SQueriableLog.class,\n                        new QueryOptions(startIndex, maxResults, SQueriableLog.class, field, order)));\n    }\n\n    @Override\n    public long getNumberOfLogs(final QueryOptions searchOptions) throws SBonitaReadException {\n        return persistenceService.getNumberOfEntities(SQueriableLog.class, searchOptions, null);\n    }\n\n    @Override\n    public List<SQueriableLog> searchLogs(final QueryOptions searchOptions) throws SBonitaReadException {\n        return persistenceService.searchEntity(SQueriableLog.class, searchOptions, null);\n    }\n\n    @Override\n    public void log(final String callerClassName, final String callerMethodName, final SQueriableLog... queriableLogs) {\n        if (queriableLogs.length == 0) {\n            return;\n        }\n        BatchLogSynchronization synchronization = getBatchLogSynchronization();\n        for (SQueriableLog log : queriableLogs) {\n            if (isLoggable(log.getActionType(), log.getSeverity())) {\n                log = logUpdater.buildFinalLog(callerClassName, callerMethodName, log);\n                synchronization.addLog(log);\n            }\n        }\n    }\n\n    private synchronized BatchLogSynchronization getBatchLogSynchronization() {\n        BatchLogSynchronization synchronization = synchronizations.get();\n        if (synchronization == null) {\n            synchronization = new BatchLogSynchronization(persistenceService, this);\n            synchronizations.set(synchronization);\n            registerSynchronization(synchronization);\n        }\n        return synchronization;\n    }\n\n    private void registerSynchronization(BatchLogSynchronization synchro) {\n        try {\n            transactionService.registerBonitaSynchronization(synchro);\n        } catch (STransactionNotFoundException e) {\n            throw new SBonitaRuntimeException(e);\n        }\n    }\n\n    void clearSynchronization() {\n        synchronizations.remove();\n    }\n\n    @Override\n    public boolean isLoggable(final String actionType, final SQueriableLogSeverity severity) {\n        return loggerStrategy.isLoggable(actionType, severity);\n    }\n\n    @Override\n    public SQueriableLog getLog(final long logId) throws SQueriableLogNotFoundException, SBonitaReadException {\n        final SQueriableLog selectOne = persistenceService\n                .selectById(new SelectByIdDescriptor<>(SQueriableLog.class, logId));\n        if (selectOne == null) {\n            throw new SQueriableLogNotFoundException(logId);\n        }\n        return selectOne;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-log/src/main/java/org/bonitasoft/engine/services/impl/SimpleQueriableLoggerStrategy.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.services.impl;\n\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity;\nimport org.bonitasoft.engine.services.QueriableLoggerStrategy;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n * @author Matthieu Chaffotte\n */\npublic class SimpleQueriableLoggerStrategy implements QueriableLoggerStrategy {\n\n    private final boolean loggable = System.getProperty(\"org.bonitasoft.engine.services.queryablelog.disable\") == null;\n\n    @Override\n    public boolean isLoggable(final String actionType, final SQueriableLogSeverity severity) {\n        return loggable;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-log/src/main/resources/org/bonitasoft/engine/queriablelogger/model/impl/hibernate/queriablelogger.queries.hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\"\n                                   \"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd\">\n<hibernate-mapping auto-import=\"false\">\n\n\t<query name=\"searchQueriableLog\">\n\t\tSELECT log\n\t\tFROM org.bonitasoft.engine.queriablelogger.model.SQueriableLog AS log\n\t</query>\n\n\t<query name=\"deleteAllSQueriableLogImpl\">\n\t\tDELETE FROM org.bonitasoft.engine.queriablelogger.model.SQueriableLog\n\t</query>\n\n\t<query name=\"getNumberOfLogs\">\n\t\tSELECT COUNT(log.id)\n\t\tFROM org.bonitasoft.engine.queriablelogger.model.SQueriableLog AS log\n\t</query>\n\n\t<query name=\"getLogs\">\n\t\tSELECT log\n\t\tFROM org.bonitasoft.engine.queriablelogger.model.SQueriableLog AS log\n\t</query>\n\n\t<query name=\"searchSQueriableLog\">\n\t\tSELECT log\n\t\tFROM org.bonitasoft.engine.queriablelogger.model.SQueriableLog AS log\n\t</query>\n\n\t<query name=\"getNumberOfSQueriableLog\">\n\t\tSELECT COUNT(log.id)\n\t\tFROM org.bonitasoft.engine.queriablelogger.model.SQueriableLog AS log\n\t</query>\n\n</hibernate-mapping>\n"
  },
  {
    "path": "services/bonita-log/src/test/java/org/bonitasoft/engine/queriablelogger/model/SQueriableLogTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.queriablelogger.model;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNull;\nimport static org.junit.Assert.assertTrue;\n\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.TestName;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SQueriableLogTest {\n\n    private final static Logger LOGGER = LoggerFactory.getLogger(SQueriableLogTest.class);\n\n    private SQueriableLog queriableLog;\n\n    @Rule\n    public TestName name = new TestName();\n\n    @Before\n    public void setUp() {\n        LOGGER.info(\"Testing : {}\", name.getMethodName());\n        queriableLog = SQueriableLog.builder().initializeNow().build();\n    }\n\n    @After\n    public void tearDown() {\n        LOGGER.info(\"Tested: {}\", name.getMethodName());\n        queriableLog = null;\n    }\n\n    @Test\n    public void testSetID() {\n        assertEquals(0, queriableLog.getId());\n        queriableLog.setId(123L);\n        assertEquals(123L, queriableLog.getId());\n    }\n\n    @Test\n    public void testSetDateElements() {\n        assertTrue(queriableLog.getYear() > 0);\n        assertTrue(queriableLog.getMonth() > 0);\n        assertTrue(queriableLog.getDayOfYear() > 0);\n        assertTrue(queriableLog.getWeekOfYear() > 0);\n    }\n\n    @Test\n    public void testSetUsername() {\n        assertEquals(null, queriableLog.getUserId());\n        queriableLog.setUserId(\"john\");\n        assertEquals(\"john\", queriableLog.getUserId());\n    }\n\n    @Test\n    public void testSetThreadID() {\n        assertEquals(Thread.currentThread().getId(), queriableLog.getThreadNumber());\n    }\n\n    @Test\n    public void testSetClusterNode() {\n        assertEquals(null, queriableLog.getClusterNode());\n        queriableLog.setClusterNode(\"node1\");\n        assertEquals(\"node1\", queriableLog.getClusterNode());\n    }\n\n    @Test\n    public void testSetProductVersion() {\n        assertEquals(null, queriableLog.getProductVersion());\n        queriableLog.setProductVersion(\"SP-5.4.1\");\n        assertEquals(\"SP-5.4.1\", queriableLog.getProductVersion());\n    }\n\n    @Test\n    public void testSetSeverity() {\n        assertEquals(null, queriableLog.getSeverity());\n        queriableLog.setSeverity(SQueriableLogSeverity.BUSINESS);\n        assertEquals(SQueriableLogSeverity.BUSINESS, queriableLog.getSeverity());\n    }\n\n    @Test\n    public void testSetActionType() {\n        assertEquals(null, queriableLog.getActionType());\n        queriableLog.setActionType(\"excecute_connector\");\n        assertEquals(\"excecute_connector\", queriableLog.getActionType());\n    }\n\n    @Test\n    public void testSetActionScope() {\n        assertEquals(null, queriableLog.getActionScope());\n        queriableLog.setActionScope(\"setVariable\");\n        assertEquals(\"setVariable\", queriableLog.getActionScope());\n    }\n\n    @Test\n    public void testSetActionStatus() {\n        assertEquals(-1, queriableLog.getActionStatus());\n        queriableLog.setActionStatus(1);\n        assertEquals(1, queriableLog.getActionStatus());\n    }\n\n    @Test\n    public void testSetRawMessage() {\n        assertEquals(null, queriableLog.getRawMessage());\n        queriableLog.setRawMessage(\"successfully executed\");\n        assertEquals(\"successfully executed\", queriableLog.getRawMessage());\n    }\n\n    @Test\n    public void testSetNumericIndexes() {\n        final int numberOfNumericIndexes = 5;\n        for (int i = 0; i < numberOfNumericIndexes; i++) {\n            assertEquals(-1L, queriableLog.getNumericIndex(i));\n        }\n\n        for (int i = 0; i < numberOfNumericIndexes; i++) {\n            queriableLog.setNumericIndex(i, i);\n            assertEquals(i, queriableLog.getNumericIndex(i));\n        }\n    }\n\n    @Test(expected = IllegalStateException.class)\n    public void testSetNumericIndexOutOfRange() {\n        queriableLog.setNumericIndex(10, 10);\n    }\n\n    @Test(expected = IllegalStateException.class)\n    public void getNumericIndexOutOfRange() {\n        queriableLog.getNumericIndex(10);\n    }\n\n    @Test\n    public void testSetCallerClassName() {\n        assertNull(queriableLog.getCallerClassName());\n        queriableLog.setCallerClassName(\"RecorderImpl.class\");\n        assertEquals(\"RecorderImpl.class\", queriableLog.getCallerClassName());\n    }\n\n    @Test\n    public void testSetCallerMethodName() {\n        assertNull(queriableLog.getCallerMethodName());\n        queriableLog.setCallerMethodName(\"insertRecord\");\n        assertEquals(\"insertRecord\", queriableLog.getCallerMethodName());\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-log/src/test/java/org/bonitasoft/engine/services/impl/QueriableLogSessionProviderImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.services.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.doReturn;\n\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.session.model.SSession;\nimport org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class QueriableLogSessionProviderImplTest {\n\n    /**\n     *\n     */\n    private static final long SESSION_ID = 5010L;\n\n    @Mock\n    private SessionService sessionService;\n\n    @Mock\n    private ReadSessionAccessor sessionAccessor;\n\n    @Mock\n    private SSession session;\n\n    @InjectMocks\n    private QueriableLogSessionProviderImpl sessionProvider;\n\n    @Before\n    public void setUp() throws Exception {\n        doReturn(SESSION_ID).when(sessionAccessor).getSessionId();\n    }\n\n    @Test\n    public void getUserId_should_return_session_user_name_if_there_is_a_session() throws Exception {\n        //given\n        doReturn(session).when(sessionService).getSession(SESSION_ID);\n        doReturn(\"john\").when(session).getUserName();\n\n        //when\n        String userId = sessionProvider.getUserId();\n\n        //then\n        assertThat(userId).isEqualTo(\"john\");\n    }\n\n    @Test\n    public void getUserId_should_return_system_if_there_is_no_session() throws Exception {\n        //given\n        doReturn(null).when(sessionService).getSession(SESSION_ID);\n\n        //when\n        String userId = sessionProvider.getUserId();\n\n        //then\n        assertThat(userId).isEqualTo(\"system\");\n    }\n\n    @Test\n    public void getClusterNode_should_return_the_session_cluster_node_if_there_is_a_session() throws Exception {\n        //given\n        doReturn(session).when(sessionService).getSession(SESSION_ID);\n        doReturn(\"aNode\").when(session).getClusterNode();\n\n        //when\n        String clusterNode = sessionProvider.getClusterNode();\n\n        //then\n        assertThat(clusterNode).isEqualTo(\"aNode\");\n    }\n\n    @Test\n    public void getClusterNode_should_return_empty_string_if_there_is_no_session() throws Exception {\n        //given\n        doReturn(null).when(sessionService).getSession(SESSION_ID);\n\n        //when\n        String clusterNode = sessionProvider.getClusterNode();\n\n        //then\n        assertThat(clusterNode).isEmpty();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-log/src/test/java/org/bonitasoft/engine/services/impl/QueriableLogUpdaterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.services.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\nimport org.bonitasoft.engine.platform.PlatformService;\nimport org.bonitasoft.engine.platform.model.SPlatformProperties;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity;\nimport org.bonitasoft.engine.services.QueriableLogSessionProvider;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class QueriableLogUpdaterTest {\n\n    @Mock\n    private QueriableLogSessionProvider sessionProvider;\n\n    @Mock\n    private PlatformService platformService;\n\n    private SQueriableLog log;\n\n    @InjectMocks\n    private QueriableLogUpdater updater;\n\n    @Before\n    public void setUp() {\n        given(sessionProvider.getUserId()).willReturn(\"user\");\n        given(sessionProvider.getClusterNode()).willReturn(\"node1\");\n\n        final SPlatformProperties properties = mock(SPlatformProperties.class);\n        given(platformService.getSPlatformProperties()).willReturn(properties);\n        given(properties.getPlatformVersion()).willReturn(\"platform.version\");\n\n        log = getLogBuilderWithMandatoryFields();\n    }\n\n    private SQueriableLog getLogBuilderWithMandatoryFields() {\n        final SQueriableLog log = SQueriableLog.builder().build();\n        log.setSeverity(SQueriableLogSeverity.INTERNAL);\n        log.setActionType(\"insert\");\n        log.setActionStatus(SQueriableLog.STATUS_OK);\n        log.setActionScope(\"scope\");\n        log.setRawMessage(\"message\");\n        return log;\n    }\n\n    @Test\n    public void buildFinalLog_should_add_meta_information() {\n        //when\n        final SQueriableLog finalLog = updater.buildFinalLog(\"class\", \"method\", log);\n\n        //then\n        assertThat(finalLog.getCallerClassName()).isEqualTo(\"class\");\n        assertThat(finalLog.getCallerMethodName()).isEqualTo(\"method\");\n        assertThat(finalLog.getUserId()).isEqualTo(\"user\");\n        assertThat(finalLog.getClusterNode()).isEqualTo(\"node1\");\n        assertThat(finalLog.getProductVersion()).isEqualTo(\"platform.version\");\n    }\n\n    @Test\n    public void raw_message_should_be_trunked_to_255_characters() {\n        //given\n        final String base = \"base message \";\n        final StringBuilder stb = new StringBuilder();\n        while (stb.length() <= 255) {\n            stb.append(base);\n        }\n        final SQueriableLog log = getLogBuilderWithMandatoryFields();\n        log.setRawMessage(stb.toString());\n\n        //when\n        final SQueriableLog finalLog = updater.buildFinalLog(\"class\", \"method\", log);\n\n        //then\n        assertThat(finalLog.getRawMessage()).hasSize(255);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-log/src/test/java/org/bonitasoft/engine/services/impl/QueriableLoggerImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.services.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.platform.PlatformService;\nimport org.bonitasoft.engine.platform.model.SPlatformProperties;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity;\nimport org.bonitasoft.engine.services.PersistenceService;\nimport org.bonitasoft.engine.services.QueriableLogSessionProvider;\nimport org.bonitasoft.engine.services.QueriableLoggerStrategy;\nimport org.bonitasoft.engine.transaction.TransactionService;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class QueriableLoggerImplTest {\n\n    private static final String FIRST_ACTION = \"first_action\";\n\n    private static final String SECOND_ACTION = \"sencond_action\";\n\n    @Mock\n    private PersistenceService persistenceService;\n    @Mock\n    private TransactionService transactionService;\n    @Mock\n    private QueriableLoggerStrategy loggerStrategy;\n    @Mock\n    private QueriableLogSessionProvider sessionProvider;\n\n    private SQueriableLog log1 = SQueriableLog.builder().build();\n    private SQueriableLog log2 = SQueriableLog.builder().build();\n    @Mock\n    private PlatformService platformService;\n    @Captor\n    private ArgumentCaptor<SelectOneDescriptor<?>> selectOneCaptor;\n    @Captor\n    private ArgumentCaptor<SelectByIdDescriptor<?>> selectByIdCaptor;\n    @Captor\n    private ArgumentCaptor<SelectListDescriptor<?>> selectListCaptor;\n    @Captor\n    private ArgumentCaptor<BatchLogSynchronization> synchroCaptor;\n    @InjectMocks\n    private QueriableLoggerImpl logService;\n\n    @Before\n    public void setUp() {\n        SPlatformProperties platformProperties = mock(SPlatformProperties.class);\n        log1.setActionType(FIRST_ACTION);\n        log1.setSeverity(SQueriableLogSeverity.INTERNAL);\n        log1.setRawMessage(\"msg\");\n\n        log2.setActionType(SECOND_ACTION);\n        log2.setSeverity(SQueriableLogSeverity.INTERNAL);\n        log2.setRawMessage(\"new action msg\");\n\n        doReturn(platformProperties).when(platformService).getSPlatformProperties();\n        doReturn(\"6.3\").when(platformProperties).getPlatformVersion();\n\n    }\n\n    @Test\n    public void isLoggable_should_return_true_if_strategy_is_loggable() {\n        // given\n        doReturn(true).when(loggerStrategy).isLoggable(FIRST_ACTION, SQueriableLogSeverity.INTERNAL);\n\n        // when\n        boolean loggable = logService.isLoggable(FIRST_ACTION, SQueriableLogSeverity.INTERNAL);\n\n        // then\n        assertThat(loggable).isTrue();\n    }\n\n    @Test\n    public void isLoggable_should_return_false_if_strategy_is_not_loggable() {\n        // given\n        doReturn(false).when(loggerStrategy).isLoggable(FIRST_ACTION, SQueriableLogSeverity.INTERNAL);\n\n        // when\n        boolean loggable = logService.isLoggable(FIRST_ACTION, SQueriableLogSeverity.INTERNAL);\n\n        // then\n        assertThat(loggable).isFalse();\n    }\n\n    @Test\n    public void calling_log_must_insert_log_if_isLogable() throws Exception {\n        // given\n        doReturn(true).when(loggerStrategy).isLoggable(FIRST_ACTION, SQueriableLogSeverity.INTERNAL);\n        doReturn(\"walter.bates\").when(sessionProvider).getUserId();\n        doReturn(\"node1\").when(sessionProvider).getClusterNode();\n\n        // when\n        logService.log(\"CallerClass\", \"callerMethod\", log1);\n\n        // then\n        verify(transactionService).registerBonitaSynchronization(synchroCaptor.capture());\n        BatchLogSynchronization value = synchroCaptor.getValue();\n        assertThat(value.getLogs()).hasSize(1);\n        SQueriableLog insertedLog = value.getLogs().get(0);\n        assertThat(insertedLog.getActionType()).isEqualTo(FIRST_ACTION);\n        assertThat(insertedLog.getClusterNode()).isEqualTo(\"node1\");\n        assertThat(insertedLog.getCallerClassName()).isEqualTo(\"CallerClass\");\n        assertThat(insertedLog.getCallerMethodName()).isEqualTo(\"callerMethod\");\n        assertThat(insertedLog.getProductVersion()).isEqualTo(\"6.3\");\n        assertThat(insertedLog.getUserId()).isEqualTo(\"walter.bates\");\n    }\n\n    @Test\n    public void calling_log_must_not_insert_log_if_is_not_logable() throws Exception {\n        // given\n        doReturn(false).when(loggerStrategy).isLoggable(FIRST_ACTION, SQueriableLogSeverity.INTERNAL);\n\n        // when\n        logService.log(\"CallerClass\", \"callerMethod\", log1);\n\n        // then\n        verify(persistenceService, never()).insert(log1);\n    }\n\n    @Test\n    public void calling_log_must_insert_all_logable_logs() throws Exception {\n        // given\n        doReturn(true).when(loggerStrategy).isLoggable(FIRST_ACTION, SQueriableLogSeverity.INTERNAL);\n        doReturn(true).when(loggerStrategy).isLoggable(SECOND_ACTION, SQueriableLogSeverity.INTERNAL);\n\n        // when\n        logService.log(\"CallerClass\", \"callerMethod\", log1, log2);\n\n        // then\n        verify(transactionService).registerBonitaSynchronization(synchroCaptor.capture());\n        BatchLogSynchronization value = synchroCaptor.getValue();\n        assertThat(value.getLogs()).extracting(\"actionType\").containsExactly(FIRST_ACTION, SECOND_ACTION);\n    }\n\n    @Test\n    public void getNumberOfLogs_should_return_number_of_logs_from_persistence_service() throws Exception {\n        // given\n        doReturn(15L).when(persistenceService).selectOne(selectOneCaptor.capture());\n\n        // when\n        long numberOfLogs = logService.getNumberOfLogs();\n\n        // then\n        assertThat(numberOfLogs).isEqualTo(15);\n        assertThat(selectOneCaptor.getValue().getQueryName()).isEqualTo(\"getNumberOfLogs\");\n    }\n\n    @Test\n    public void getLogs_should_return_logs_from_persitence_service() throws Exception {\n        // given\n        List<SQueriableLog> persistenceLogs = Arrays.asList(log1, log2);\n        doReturn(persistenceLogs).when(persistenceService).selectList(selectListCaptor.capture());\n\n        // when\n        List<SQueriableLog> logs = logService.getLogs(5, 10, \"id\", OrderByType.ASC);\n\n        // then\n        assertThat(logs).isEqualTo(persistenceLogs);\n        SelectListDescriptor<?> selectListDescriptor = selectListCaptor.getValue();\n        assertThat(selectListDescriptor.getPageSize()).isEqualTo(10);\n        assertThat(selectListDescriptor.getStartIndex()).isEqualTo(5);\n    }\n\n    @Test\n    public void getNumberOfEntities_should_return_number_of_entities_from_persistence_service() throws Exception {\n        // given\n        QueryOptions queryOptions = new QueryOptions(0, 10);\n        doReturn(20L).when(persistenceService).getNumberOfEntities(SQueriableLog.class, queryOptions, null);\n\n        // when\n        long numberOfLogs = logService.getNumberOfLogs(queryOptions);\n\n        // then\n        assertThat(numberOfLogs).isEqualTo(20L);\n    }\n\n    @Test\n    public void searchLogs_should_return_logs_from_persistence_service() throws Exception {\n        // given\n        QueryOptions queryOptions = new QueryOptions(0, 10);\n        List<SQueriableLog> persistenceLogs = Arrays.asList(log1, log2);\n        doReturn(persistenceLogs).when(persistenceService).searchEntity(SQueriableLog.class, queryOptions, null);\n\n        // when\n        List<SQueriableLog> logs = logService.searchLogs(queryOptions);\n\n        // then\n        assertThat(logs).isEqualTo(persistenceLogs);\n    }\n\n    @Test\n    public void getLog_should_return_log_from_persistence_service() throws Exception {\n        // given\n        doReturn(log1).when(persistenceService).selectById(any());\n\n        // when\n        SQueriableLog log = logService.getLog(100L);\n\n        // then\n        assertThat(log).isEqualTo(log1);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-log/src/test/java/org/bonitasoft/engine/services/impl/SimpleQueriableLoggerStrategyTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.services.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class SimpleQueriableLoggerStrategyTest {\n\n    private static final String DISABLE_LOG_KEY = \"org.bonitasoft.engine.services.queryablelog.disable\";\n\n    @Before\n    public void setUp() {\n        System.clearProperty(DISABLE_LOG_KEY);\n    }\n\n    @Test\n    public void isLogable_shoud_return_false_if_system_property_queryablelogDisable_is_null() {\n        System.setProperty(DISABLE_LOG_KEY, \"true\");\n        SimpleQueriableLoggerStrategy strategy = new SimpleQueriableLoggerStrategy();\n        assertThat(strategy.isLoggable(\"anyAction\", SQueriableLogSeverity.BUSINESS)).isFalse();\n    }\n\n    @Test\n    public void isLogable_shoud_return_true_if_system_property_queryablelogDisable_is_not_null() {\n        SimpleQueriableLoggerStrategy strategy = new SimpleQueriableLoggerStrategy();\n        assertThat(strategy.isLoggable(\"anyAction\", SQueriableLogSeverity.BUSINESS)).isTrue();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-log/src/test/resources/logback-test.xml",
    "content": "<configuration debug=\"false\" scan=\"false\">\n\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->\n        <encoder>\n            <pattern>|%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <logger name=\"org.bonitasoft\" level=\"INFO\" />\n\n    <root level=\"WARN\">\n        <appender-ref ref=\"STDOUT\" />\n    </root>\n\n</configuration>\n"
  },
  {
    "path": "services/bonita-page/build.gradle",
    "content": "dependencies {\n    api project(':services:bonita-log')\n    api project(':services:bonita-builder')\n    api project(':services:bonita-events')\n    api project(':services:bonita-persistence')\n    api project(':services:bonita-commons')\n    api project(':services:bonita-authorization')\n    api project(':bpm:bonita-common')\n\n    testImplementation libs.assertj\n    testImplementation libs.mockitoCore\n    testImplementation libs.logback\n    testImplementation testFixtures(project(':bpm:bonita-common'))\n\n    annotationProcessor libs.lombok\n    compileOnly libs.lombok\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/java/org/bonitasoft/engine/page/AbstractSPage.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport javax.persistence.Id;\nimport javax.persistence.MappedSuperclass;\n\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.SuperBuilder;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n@Data\n@SuperBuilder\n@NoArgsConstructor\n@MappedSuperclass\npublic class AbstractSPage implements PersistentObject {\n\n    @Id\n    private long id;\n\n    private String name;\n    private String description;\n    private String displayName;\n    private long installationDate;\n    private long installedBy;\n    private boolean provided;\n    @Builder.Default\n    private boolean editable = true;\n    @Builder.Default\n    private boolean removable = true;\n    private long lastModificationDate;\n    private long lastUpdatedBy;\n    private String contentName;\n    private String contentType;\n    private long processDefinitionId;\n\n    /**\n     * A MD5 sum hash of the content of the page.\n     * It is used only for verifying if a provided page has change or not.\n     * It is not used (and not filled) for user pages\n     */\n    private String pageHash;\n\n    public AbstractSPage(final AbstractSPage sPage) {\n        name = sPage.getName();\n        description = sPage.getDescription();\n        displayName = sPage.getDisplayName();\n        installationDate = sPage.getInstallationDate();\n        installedBy = sPage.getInstalledBy();\n        provided = sPage.isProvided();\n        lastModificationDate = sPage.getLastModificationDate();\n        lastUpdatedBy = sPage.getLastUpdatedBy();\n        contentName = sPage.getContentName();\n        contentType = sPage.getContentType();\n        processDefinitionId = sPage.getProcessDefinitionId();\n        editable = sPage.isEditable();\n        removable = sPage.isRemovable();\n        pageHash = sPage.getPageHash();\n    }\n\n    public AbstractSPage(final String name, final long installationDate, final long installedBy, final boolean provided,\n            final String contentName) {\n        this.name = name;\n        this.installationDate = installationDate;\n        this.installedBy = installedBy;\n        this.provided = provided;\n        this.contentType = SContentType.PAGE;\n        this.contentName = contentName;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/java/org/bonitasoft/engine/page/AuthorizationRule.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.commons.exceptions.SExecutionException;\n\n/**\n * Represent a rule to execute to grant access or not.\n * author Emmanuel Duchastenier\n */\npublic interface AuthorizationRule {\n\n    /**\n     * Execute this rule and, according to the context, says whether the rule is valid.\n     *\n     * @param key the page mapping key\n     * @param context the information necessary to execute this rule.\n     * @return true if allowed, false otherwise.If determination cannot be fulfilled, an Exception should be thrown.\n     * @throws SExecutionException exception thrown if authorization cannot be determined.\n     */\n    boolean isAllowed(String key, Map<String, Serializable> context) throws SExecutionException;\n\n    /**\n     * @return the identifier for this authorization rule\n     */\n    String getId();\n\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/java/org/bonitasoft/engine/page/PageMappingService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.commons.exceptions.SDeletionException;\nimport org.bonitasoft.engine.commons.exceptions.SExecutionException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectCreationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\n/**\n * Service that allows to map an arbitrary String key to a (Custom) Page or URL.\n * Also allows to process the URL or Page return by providing an URLAdapter implementation that will be called\n * automatically.\n * Also allows to execute Authorization checkings by specifying rules to execute before returning the Page or URL.\n *\n * @see URLAdapter\n * @author Baptiste Mesta\n * @author Emmanuel Duchastenier\n * @author Matthieu Chaffotte\n */\npublic interface PageMappingService {\n\n    /**\n     * @param key the key used to retrieve the mapping\n     * @param pageId the id of the custom page\n     * @param authorizationRules the names of the authorization rules to execute\n     * @return the created page mapping\n     * @throws SObjectCreationException when there is an issue while creating this object\n     * @since 7.0.0\n     */\n    SPageMapping create(String key, Long pageId, List<String> authorizationRules) throws SObjectCreationException;\n\n    /**\n     * @param key the key used to retrieve the mapping\n     * @param url the external URL the mapping points to\n     * @param urlAdapter the name of the URL adapter that transform the URL in case of an external URL. i.e. it can add\n     *        parameters\n     * @param authorizationRules the names of the authorization rules to execute\n     * @return the created page mapping\n     * @throws SObjectCreationException when there is an issue while creating this object\n     * @since 7.0.0\n     */\n    SPageMapping create(String key, String url, String urlAdapter, List<String> authorizationRules)\n            throws SObjectCreationException;\n\n    /**\n     * @param key the key of the page mapping to retrieve\n     * @return the page mapping having this key\n     * @throws SObjectNotFoundException when there is no mapping having this key\n     */\n    SPageMapping get(String key) throws SObjectNotFoundException, SBonitaReadException;\n\n    /**\n     * @param pageMapping\n     * @param context\n     * @param executeAuthorizationRules\n     * @return\n     * @throws SExecutionException\n     * @throws SAuthorizationException\n     */\n    SPageURL resolvePageURL(SPageMapping pageMapping, Map<String, Serializable> context,\n            boolean executeAuthorizationRules) throws SExecutionException, SAuthorizationException;\n\n    /**\n     * delete this page mapping\n     *\n     * @param SPageMapping the page mapping to delete\n     */\n    void delete(SPageMapping SPageMapping) throws SDeletionException;\n\n    /**\n     * update the given page mapping\n     *\n     * @param pageMapping the pageMapping to update\n     * @param pageId the id of the page or null\n     * @throws SObjectModificationException\n     */\n    void update(SPageMapping pageMapping, Long pageId)\n            throws SObjectModificationException, SObjectNotFoundException, SBonitaReadException;\n\n    /**\n     * update the given page mapping\n     *\n     * @param pageMapping the pageMapping to update\n     * @param url the URL or null\n     * @param urlAdapter the new URL adapter to use\n     * @throws SObjectModificationException\n     */\n    void update(SPageMapping pageMapping, String url, String urlAdapter)\n            throws SObjectModificationException, SObjectNotFoundException, SBonitaReadException;\n\n    /**\n     * Gets the paginated mappings of the page.\n     *\n     * @param pageId the page identifier\n     * @param startIndex the start index\n     * @param maxResults the max results\n     * @return the paginated mappings of the page\n     * @throws SBonitaReadException\n     */\n    List<SPageMapping> get(long pageId, int startIndex, int maxResults) throws SBonitaReadException;\n\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/java/org/bonitasoft/engine/page/PageService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport java.util.List;\nimport java.util.Properties;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectAlreadyExistsException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectCreationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Baptiste Mesta\n * @author Laurent Leseigneur\n */\npublic interface PageService {\n\n    String PROPERTIES_FILE_NAME = \"page.properties\";\n\n    String PROPERTIES_DISPLAY_NAME = \"displayName\";\n\n    String PROPERTIES_DESCRIPTION = \"description\";\n\n    String PROPERTIES_CONTENT_TYPE = \"contentType\";\n\n    String PROPERTIES_NAME = \"name\";\n\n    String PAGE = \"PAGE\";\n\n    /**\n     * add a page using the zip in parameters and the given properties\n     *\n     * @param page\n     * @param content\n     * @return\n     * @throws SObjectCreationException\n     * @throws SObjectAlreadyExistsException\n     * @throws SInvalidPageZipException\n     * @throws SInvalidPageTokenException\n     */\n    SPage addPage(SPage page, byte[] content)\n            throws SObjectCreationException, SObjectAlreadyExistsException, SInvalidPageZipException,\n            SInvalidPageTokenException;\n\n    SPage insertPage(SPage page, byte[] content) throws SObjectAlreadyExistsException, SObjectCreationException;\n\n    SPage checkIfPageAlreadyExists(SPage page) throws SBonitaReadException;\n\n    SPage getPage(long pageId) throws SBonitaReadException, SObjectNotFoundException;\n\n    SPage getPageByName(String pageName) throws SBonitaReadException;\n\n    SPage buildPage(byte[] content, String contentName, long userId, boolean provided, boolean removable,\n            boolean editable) throws SInvalidPageZipException, SInvalidPageTokenException;\n\n    /**\n     * Read the content of a page in a zip\n     *\n     * @param content\n     *        the page content\n     * @return\n     *         the properties of the page stored in the page.properties\n     * @throws SInvalidPageZipMissingIndexException\n     *         if the page is missing an index.html or Index.groovy\n     * @throws SInvalidPageZipMissingAPropertyException\n     *         if the page is missing mandatory field in the page.properties\n     * @throws SInvalidPageZipInconsistentException\n     *         if the zip is not a valid zip file or unreadable\n     * @throws SInvalidPageZipMissingPropertiesException\n     */\n    Properties readPageZip(final byte[] content)\n            throws SInvalidPageZipMissingIndexException, SInvalidPageZipMissingAPropertyException,\n            SInvalidPageZipInconsistentException, SInvalidPageZipMissingPropertiesException, SInvalidPageTokenException;\n\n    long getNumberOfPages(QueryOptions options) throws SBonitaReadException;\n\n    void deletePage(long pageId) throws SObjectModificationException, SObjectNotFoundException;\n\n    byte[] getPageContent(long pageId) throws SBonitaReadException, SObjectNotFoundException;\n\n    List<SPage> searchPages(QueryOptions options) throws SBonitaReadException;\n\n    SPage updatePage(long pageId, EntityUpdateDescriptor updateDescriptor) throws SObjectModificationException,\n            SObjectAlreadyExistsException, SInvalidPageTokenException;\n\n    void updatePageContent(long pageId, byte[] content, String contentName) throws SBonitaException;\n\n    /**\n     * add a page using the zip in parameters, it get all informations from the page.properties file contain inside the\n     * zip\n     *\n     * @param content\n     * @param userId\n     * @return\n     * @throws SObjectCreationException\n     * @throws SObjectAlreadyExistsException\n     * @throws SInvalidPageZipException\n     * @throws SInvalidPageTokenException\n     */\n    SPage addPage(final byte[] content, final String contentName, long userId)\n            throws SObjectCreationException, SObjectAlreadyExistsException,\n            SInvalidPageZipException,\n            SInvalidPageTokenException;\n\n    /**\n     * get a page attached to a process\n     *\n     * @param name\n     * @param processDefinitionId\n     * @return\n     * @throws SBonitaReadException\n     */\n    SPage getPageByNameAndProcessDefinitionId(String name, long processDefinitionId) throws SBonitaReadException;\n\n    /**\n     * get a list of page attached to a process\n     *\n     * @param processDefinitionId\n     * @param fromIndex\n     * @param numberOfResults\n     * @return\n     * @throws SBonitaReadException\n     */\n    List<SPage> getPageByProcessDefinitionId(long processDefinitionId, int fromIndex, int numberOfResults)\n            throws SBonitaReadException;\n\n    void updatePageContent(long pageId, byte[] content, String contentName, SPageUpdateBuilder pageUpdateBuilder)\n            throws SObjectAlreadyExistsException, SObjectModificationException, SInvalidPageZipException,\n            SInvalidPageTokenException;\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/java/org/bonitasoft/engine/page/PageServiceListener.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport org.bonitasoft.engine.commons.exceptions.SDeletionException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectCreationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\n/**\n * @author Laurent Leseigneur\n * @author Matthieu Chaffotte\n * @since 7.0\n */\npublic interface PageServiceListener {\n\n    void pageInserted(SPage page, byte[] content) throws SObjectCreationException;\n\n    void pageDeleted(SPage page) throws SDeletionException, SBonitaReadException;\n\n    void pageUpdated(AbstractSPage page, byte[] content) throws SObjectModificationException;\n\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/java/org/bonitasoft/engine/page/SAuthorizationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\n/**\n * Thrown when access to a given page or URL is not allowed for a given key.\n * author Emmanuel Duchastenier\n */\npublic class SAuthorizationException extends Exception {\n\n    public SAuthorizationException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/java/org/bonitasoft/engine/page/SContentType.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\n/**\n * @author Laurent Leseigneur\n */\npublic interface SContentType {\n\n    String PAGE = \"page\";\n\n    String FORM = \"form\";\n\n    String API_EXTENSION = \"apiExtension\";\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/java/org/bonitasoft/engine/page/SInvalidPageTokenException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\npublic class SInvalidPageTokenException extends SBonitaException {\n\n    public SInvalidPageTokenException(final String message) {\n        super(message);\n    }\n\n    private static final long serialVersionUID = 7161910286756340441L;\n\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/java/org/bonitasoft/engine/page/SInvalidPageZipException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport java.io.IOException;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * content of the page is not valid\n *\n * @author Baptiste Mesta\n */\npublic abstract class SInvalidPageZipException extends SBonitaException {\n\n    private static final long serialVersionUID = -7263291210428082852L;\n\n    public SInvalidPageZipException(final String message) {\n        super(message);\n    }\n\n    public SInvalidPageZipException(final String string, final IOException e) {\n        super(string, e);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/java/org/bonitasoft/engine/page/SInvalidPageZipInconsistentException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport java.io.IOException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SInvalidPageZipInconsistentException extends SInvalidPageZipException {\n\n    public SInvalidPageZipInconsistentException(String message) {\n        super(message);\n    }\n\n    public SInvalidPageZipInconsistentException(String string, IOException e) {\n        super(string, e);\n    }\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/java/org/bonitasoft/engine/page/SInvalidPageZipMissingAPropertyException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SInvalidPageZipMissingAPropertyException extends SInvalidPageZipException {\n\n    private String fields;\n\n    public SInvalidPageZipMissingAPropertyException(String fields) {\n        super(\"Missing fields in the page.properties: \" + fields);\n        this.fields = fields;\n    }\n\n    public String getFields() {\n        return fields;\n    }\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/java/org/bonitasoft/engine/page/SInvalidPageZipMissingIndexException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SInvalidPageZipMissingIndexException extends SInvalidPageZipException {\n\n    public SInvalidPageZipMissingIndexException() {\n        super(\"Missing Index.groovy or index.html\");\n    }\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/java/org/bonitasoft/engine/page/SInvalidPageZipMissingPropertiesException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SInvalidPageZipMissingPropertiesException extends SInvalidPageZipException {\n\n    public SInvalidPageZipMissingPropertiesException() {\n        super(\"Missing page.properties\");\n    }\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/java/org/bonitasoft/engine/page/SPage.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport javax.persistence.Entity;\nimport javax.persistence.Table;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.ToString;\nimport lombok.experimental.SuperBuilder;\n\n/**\n * @author Matthieu Chaffotte\n */\n@Data\n@ToString(callSuper = true)\n@EqualsAndHashCode(callSuper = true)\n@SuperBuilder\n@NoArgsConstructor\n@Entity\n@Table(name = \"page\")\npublic class SPage extends AbstractSPage {\n\n    public static final String NAME = \"name\";\n    public static final String DESCRIPTION = \"description\";\n    public static final String INSTALLATION_DATE = \"installationDate\";\n    public static final String INSTALLED_BY = \"installedBy\";\n    public static final String CONTENT_TYPE = \"contentType\";\n    public static final String PROCESS_DEFINITION_ID = \"processDefinitionId\";\n    public static final String ID = \"id\";\n    public static final String PROVIDED = \"provided\";\n    public static final String DISPLAY_NAME = \"displayName\";\n    public static final String LAST_MODIFICATION_DATE = \"lastModificationDate\";\n    public static final String LAST_UPDATE_BY = \"lastUpdateBy\";\n\n    public SPage(final String name, final String description, final String displayName, final long installationDate,\n            final long installedBy,\n            final boolean provided, final long lastModificationDate, final long lastUpdatedBy,\n            final String contentName) {\n        super(name, installationDate, installedBy, provided, contentName);\n        setDescription(description);\n        setDisplayName(displayName);\n        setProvided(provided);\n        setLastModificationDate(lastModificationDate);\n        setLastUpdatedBy(lastUpdatedBy);\n    }\n\n    public SPage(final String name, final String description, final String displayName, final long installationDate,\n            final long installedBy,\n            final boolean provided, boolean editable, final long lastModificationDate,\n            final long lastUpdatedBy,\n            final String contentName) {\n        this(name, installationDate, installedBy, provided, contentName);\n        setDescription(description);\n        setDisplayName(displayName);\n        setProvided(provided);\n        setLastModificationDate(lastModificationDate);\n        setLastUpdatedBy(lastUpdatedBy);\n        setEditable(editable);\n    }\n\n    public SPage(final SPage sPage) {\n        super(sPage);\n    }\n\n    public SPage(final String name, final long installationDate, final long installedBy, final boolean provided,\n            final String contentName) {\n        setName(name);\n        setInstallationDate(installationDate);\n        setInstalledBy(installedBy);\n        setProvided(provided);\n        setContentName(contentName);\n        setContentType(SContentType.PAGE);\n    }\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/java/org/bonitasoft/engine/page/SPageLogBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface SPageLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction {\n\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/java/org/bonitasoft/engine/page/SPageLogBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface SPageLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory {\n\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/java/org/bonitasoft/engine/page/SPageMapping.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.StringTokenizer;\n\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\nimport javax.persistence.Transient;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Baptiste Mesta\n */\n@Entity\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\n@EqualsAndHashCode(exclude = \"authorizationRules\")\n@Table(name = \"page_mapping\")\npublic class SPageMapping implements PersistentObject {\n\n    public static final String COMMA_DELIMITER = \",\";\n\n    @Id\n    private long id;\n\n    @Column(name = \"key_\")\n    private String key;\n    @Column(name = \"pageid\")\n    private Long pageId;\n    private String url;\n    @Column(name = \"urladapter\")\n    private String urlAdapter;\n    @Column(name = \"lastupdatedate\")\n    private long lastUpdateDate;\n    @Column(name = \"lastupdatedby\")\n    private long lastUpdatedBy;\n\n    @Column(name = \"page_authoriz_rules\")\n    private String pageAuthorizRules;\n\n    @Transient\n    @Builder.Default\n    private List<String> authorizationRules = new ArrayList<>();\n\n    private void parseRules() {\n        if (pageAuthorizRules != null) {\n            authorizationRules.clear();\n            for (StringTokenizer stringTk = new StringTokenizer(pageAuthorizRules, COMMA_DELIMITER, false); stringTk\n                    .hasMoreTokens();) {\n                String rule = stringTk.nextToken();\n                authorizationRules.add(rule);\n            }\n        }\n    }\n\n    private void buildRulesAsString() {\n        pageAuthorizRules = null;\n        if (authorizationRules != null && !authorizationRules.isEmpty()) {\n            pageAuthorizRules = \"\";\n            for (String authorizationRule : authorizationRules) {\n                pageAuthorizRules += (authorizationRule + COMMA_DELIMITER);\n            }\n        }\n    }\n\n    public List<String> getPageAuthorizationRules() {\n        parseRules(); // Need to do it here because Hibernate does not call setter to set fields from DB to Object\n        return authorizationRules;\n    }\n\n    public void setPageAuthorizationRules(List<String> rules) {\n        authorizationRules = rules;\n        buildRulesAsString();\n    }\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/java/org/bonitasoft/engine/page/SPageURL.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\n/**\n * author Emmanuel Duchastenier\n */\npublic class SPageURL {\n\n    private String url;\n\n    private Long pageId;\n\n    public String getUrl() {\n        return url;\n    }\n\n    public Long getPageId() {\n        return pageId;\n    }\n\n    public SPageURL(String url, Long pageId) {\n        this.url = url;\n        this.pageId = pageId;\n    }\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/java/org/bonitasoft/engine/page/SPageUpdateBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\npublic interface SPageUpdateBuilder {\n\n    EntityUpdateDescriptor done();\n\n    /**\n     * @deprecated since bonita 7.13 the update of name is no more supported, it will not be take into account on the\n     *             page update\n     */\n    @Deprecated\n    SPageUpdateBuilder updateName(String value);\n\n    SPageUpdateBuilder updateDescription(String value);\n\n    SPageUpdateBuilder updateDisplayName(String value);\n\n    SPageUpdateBuilder updateLastModificationDate(long currentTimeMillis);\n\n    SPageUpdateBuilder updateLastUpdatedBy(long userId);\n\n    SPageUpdateBuilder updateContentName(String value);\n\n    SPageUpdateBuilder updateContentType(String contentType);\n\n    SPageUpdateBuilder updateProcessDefinitionId(Long processDedfinitionId);\n\n    SPageUpdateBuilder updatePageHash(String hash);\n\n    SPageUpdateBuilder markNonProvided();\n\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/java/org/bonitasoft/engine/page/SPageUpdateBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\npublic interface SPageUpdateBuilderFactory {\n\n    SPageUpdateBuilder createNewInstance(EntityUpdateDescriptor descriptor);\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/java/org/bonitasoft/engine/page/SPageUpdateContentBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\npublic interface SPageUpdateContentBuilder {\n\n    EntityUpdateDescriptor done();\n\n    SPageUpdateContentBuilder updateContent(byte[] content);\n\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/java/org/bonitasoft/engine/page/SPageWithContent.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport javax.persistence.Entity;\nimport javax.persistence.Table;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.ToString;\nimport lombok.experimental.SuperBuilder;\nimport org.hibernate.annotations.Type;\n\n@Data\n@SuperBuilder\n@EqualsAndHashCode(callSuper = true)\n@ToString(callSuper = true, exclude = \"content\")\n@NoArgsConstructor\n@Entity\n@Table(name = \"page\")\npublic class SPageWithContent extends AbstractSPage {\n\n    @Type(type = \"materialized_blob\")\n    private byte[] content;\n\n    public SPageWithContent(final SPage sPage, final byte[] pagContent) {\n        super(sPage);\n        setContent(pagContent);\n    }\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/java/org/bonitasoft/engine/page/URLAdapter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.commons.exceptions.SExecutionException;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface URLAdapter {\n\n    /**\n     * adapt an url based on a context\n     *\n     * @param url the base url\n     * @param key the url key\n     * @param context the provided context\n     * @return the new url\n     * @throws SExecutionException when the URL rewriting fails\n     */\n    String adapt(String url, String key, Map<String, Serializable> context) throws SExecutionException;\n\n    /**\n     * @return the identifier for this url adapter\n     */\n    String getId();\n\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/java/org/bonitasoft/engine/page/impl/ApiExtensionPageServiceListenerImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page.impl;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Properties;\n\nimport org.bonitasoft.engine.commons.exceptions.SDeletionException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectCreationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.page.AbstractSPage;\nimport org.bonitasoft.engine.page.PageMappingService;\nimport org.bonitasoft.engine.page.PageServiceListener;\nimport org.bonitasoft.engine.page.SContentType;\nimport org.bonitasoft.engine.page.SInvalidPageZipMissingPropertiesException;\nimport org.bonitasoft.engine.page.SPage;\nimport org.bonitasoft.engine.page.SPageMapping;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\n\n/**\n * @author Laurent Leseigneur\n * @author Matthieu Chaffotte\n */\npublic class ApiExtensionPageServiceListenerImpl implements PageServiceListener {\n\n    private static final int MAX_RESULTS = 100;\n\n    private final PageMappingService pageMappingService;\n\n    private final SPageContentHelper helper;\n\n    public ApiExtensionPageServiceListenerImpl(final PageMappingService pageMappingService) {\n        this(pageMappingService, new SPageContentHelper());\n    }\n\n    public ApiExtensionPageServiceListenerImpl(final PageMappingService pageMappingService,\n            final SPageContentHelper helper) {\n        super();\n        this.pageMappingService = pageMappingService;\n        this.helper = helper;\n    }\n\n    @Override\n    public void pageInserted(final SPage sPage, final byte[] content) throws SObjectCreationException {\n        if (SContentType.API_EXTENSION.equals(sPage.getContentType())) {\n            try {\n                addPageMapping(sPage, helper.loadPageProperties(content));\n            } catch (final IOException | SInvalidPageZipMissingPropertiesException e) {\n                throw new SObjectCreationException(e);\n            }\n        }\n    }\n\n    private void addPageMapping(final SPage page, final Properties apiProperties)\n            throws SObjectCreationException, IOException,\n            SInvalidPageZipMissingPropertiesException {\n        final List<String> mappings = getKeysOfPageMappings(apiProperties);\n        for (final String mapping : mappings) {\n            pageMappingService.create(mapping, page.getId(), Collections.<String> emptyList());\n        }\n    }\n\n    private String getMappingKey(final String method, final String pathTemplate) {\n        return new StringBuilder().append(\"apiExtension|\").append(method).append(\"|\").append(pathTemplate).toString();\n    }\n\n    private String getRequiredProperty(final Properties properties, final String propertyName)\n            throws SObjectCreationException {\n        final String property = (String) properties.get(propertyName);\n        if (property == null || property.trim().length() == 0) {\n            throw new SObjectCreationException(\"the property '\" + propertyName + \"' is missing or is empty\");\n        }\n        return property.trim();\n    }\n\n    @Override\n    public void pageDeleted(final SPage page) throws SBonitaReadException, SDeletionException {\n        if (SContentType.API_EXTENSION.equals(page.getContentType())) {\n            List<SPageMapping> mappings;\n            do {\n                mappings = pageMappingService.get(page.getId(), 0, MAX_RESULTS);\n                for (final SPageMapping mapping : mappings) {\n                    pageMappingService.delete(mapping);\n                }\n            } while (mappings.size() == MAX_RESULTS);\n        }\n    }\n\n    @Override\n    public void pageUpdated(final AbstractSPage page, final byte[] content) throws SObjectModificationException {\n        if (SContentType.API_EXTENSION.equals(page.getContentType())) {\n            try {\n                updateMappings(page, helper.loadPageProperties(content));\n            } catch (SBonitaReadException | SDeletionException | SObjectCreationException\n                    | SInvalidPageZipMissingPropertiesException | IOException e) {\n                throw new SObjectModificationException(e);\n            }\n        }\n    }\n\n    private void updateMappings(final AbstractSPage page, final Properties apiProperties)\n            throws SObjectCreationException, SBonitaReadException, SDeletionException {\n        final List<String> keys = getKeysOfPageMappings(apiProperties);\n        final List<String> existingKeys = new ArrayList<>();\n        List<SPageMapping> mappings;\n        do {\n            mappings = pageMappingService.get(page.getId(), 0, MAX_RESULTS);\n            for (final SPageMapping mapping : mappings) {\n                if (keys.contains(mapping.getKey())) {\n                    existingKeys.add(mapping.getKey());\n                } else {\n                    pageMappingService.delete(mapping);\n                }\n            }\n        } while (mappings.size() == MAX_RESULTS);\n        keys.removeAll(existingKeys);\n        for (final String key : keys) {\n            pageMappingService.create(key, page.getId(), Collections.<String> emptyList());\n        }\n    }\n\n    private List<String> getKeysOfPageMappings(final Properties apiProperties) throws SObjectCreationException {\n        final List<String> keys = new ArrayList<>();\n        final String apiExtensions = getRequiredProperty(apiProperties, \"apiExtensions\");\n        final String[] resourceNames = apiExtensions.split(\",\");\n        for (final String resource : resourceNames) {\n            final String resourceName = resource.trim();\n            final String method = getRequiredProperty(apiProperties, resourceName + \".method\");\n            String pathTemplate = getRequiredProperty(apiProperties, resourceName + \".pathTemplate\");\n            if (pathTemplate != null && pathTemplate.startsWith(\"/\")) {\n                pathTemplate = pathTemplate.substring(1);\n            }\n            try {\n                getRequiredProperty(apiProperties, resourceName + \".className\");\n            } catch (SObjectCreationException e) { // if the property 'className' isn't present, then the rest api might be used in legacy mode (!= compiled mode) -> we look for the property 'classFileName'\n                getRequiredProperty(apiProperties, resourceName + \".classFileName\");\n            }\n            getRequiredProperty(apiProperties, resourceName + \".permissions\");\n            keys.add(getMappingKey(method, pathTemplate));\n        }\n        return keys;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/java/org/bonitasoft/engine/page/impl/AuthorizationRulesInjector.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page.impl;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.page.AuthorizationRule;\n\n/**\n * author Emmanuel Duchastenier\n */\npublic class AuthorizationRulesInjector {\n\n    private List<AuthorizationRule> authorizationRules;\n    private PageMappingServiceImpl pageMappingService;\n\n    public AuthorizationRulesInjector(PageMappingServiceImpl pageMappingService) {\n        this.pageMappingService = pageMappingService;\n    }\n\n    /**\n     * Called by Spring automatically\n     *\n     * @param authorizationRules\n     */\n    public void setAuthorizationRules(List<AuthorizationRule> authorizationRules) {\n        this.authorizationRules = authorizationRules;\n    }\n\n    public void injectAuthorizationRules() {\n        if (authorizationRules != null) {\n            pageMappingService.setAuthorizationRules(authorizationRules);\n        }\n    }\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/java/org/bonitasoft/engine/page/impl/PageMappingServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page.impl;\n\nimport java.io.Serializable;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.commons.exceptions.SDeletionException;\nimport org.bonitasoft.engine.commons.exceptions.SExecutionException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectCreationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.page.AuthorizationRule;\nimport org.bonitasoft.engine.page.PageMappingService;\nimport org.bonitasoft.engine.page.SAuthorizationException;\nimport org.bonitasoft.engine.page.SPageMapping;\nimport org.bonitasoft.engine.page.SPageURL;\nimport org.bonitasoft.engine.page.URLAdapter;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.bonitasoft.engine.session.SSessionNotFoundException;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor;\nimport org.bonitasoft.engine.sessionaccessor.SessionIdNotSetException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class PageMappingServiceImpl implements PageMappingService {\n\n    public static final String PAGE_MAPPING = \"PAGE_MAPPING\";\n    private final Recorder recorder;\n    private final ReadPersistenceService persistenceService;\n    private final SessionService sessionService;\n    private final ReadSessionAccessor sessionAccessor;\n    private final Map<String, URLAdapter> urlAdapterMap;\n    private final Map<String, AuthorizationRule> authorizationRuleMap;\n\n    public PageMappingServiceImpl(final Recorder recorder, final ReadPersistenceService persistenceService,\n            final SessionService sessionService,\n            final ReadSessionAccessor sessionAccessor) {\n        this.recorder = recorder;\n        this.persistenceService = persistenceService;\n        this.sessionService = sessionService;\n        this.sessionAccessor = sessionAccessor;\n        urlAdapterMap = new HashMap<>();\n        authorizationRuleMap = new HashMap<>();\n    }\n\n    public void setURLAdapters(final List<URLAdapter> urlAdapters) {\n        for (final URLAdapter urlAdapter : urlAdapters) {\n            urlAdapterMap.put(urlAdapter.getId(), urlAdapter);\n        }\n    }\n\n    public void setAuthorizationRules(final List<AuthorizationRule> authorizationRules) {\n        for (final AuthorizationRule authorizationRule : authorizationRules) {\n            authorizationRuleMap.put(authorizationRule.getId(), authorizationRule);\n        }\n    }\n\n    @Override\n    public SPageMapping create(final String key, final Long pageId, final List<String> authorizationRules)\n            throws SObjectCreationException {\n        SPageMapping pageMapping;\n        try {\n            pageMapping = findMapping(key);\n        } catch (final SBonitaReadException e) {\n            throw new SObjectCreationException(String.format(\"Failed to get page mapping %s\", key), e);\n        }\n        if (pageMapping == null) {\n            final SPageMapping entity = new SPageMapping();\n            entity.setPageId(pageId);\n            entity.setPageAuthorizationRules(authorizationRules);\n            entity.setKey(key);\n            return insert(entity);\n        }\n        throw new SObjectCreationException(\n                String.format(\"Mapping key %s already exists for page with id %s\", key, pageMapping.getPageId()));\n    }\n\n    SPageMapping insert(final SPageMapping entity) throws SObjectCreationException {\n        try {\n            recorder.recordInsert(new InsertRecord(entity), PAGE_MAPPING);\n        } catch (final SRecorderException e) {\n            throw new SObjectCreationException(e);\n        }\n        return entity;\n    }\n\n    @Override\n    public SPageMapping create(final String key, final String url, final String urlAdapter,\n            final List<String> authorizationRules)\n            throws SObjectCreationException {\n        SPageMapping pageMapping = null;\n        try {\n            pageMapping = findMapping(key);\n        } catch (final SBonitaReadException e) {\n            throw new SObjectCreationException(String.format(\"Failed to get page mapping %s\", key), e);\n        }\n        if (pageMapping == null) {\n            final SPageMapping entity = new SPageMapping();\n            entity.setUrl(url);\n            entity.setUrlAdapter(urlAdapter);\n            entity.setPageAuthorizationRules(authorizationRules);\n            entity.setKey(key);\n            return insert(entity);\n        }\n        throw new SObjectCreationException(\n                String.format(\"Mapping key %s already exists for page with id %s\", key, pageMapping.getPageId()));\n    }\n\n    @Override\n    public SPageMapping get(final String key) throws SObjectNotFoundException, SBonitaReadException {\n        final SPageMapping sPageMapping = findMapping(key);\n        if (sPageMapping == null) {\n            throw new SObjectNotFoundException(\"No page mapping found with key \" + key);\n        }\n        return sPageMapping;\n    }\n\n    private SPageMapping findMapping(final String key) throws SBonitaReadException {\n        return persistenceService.selectOne(new SelectOneDescriptor<SPageMapping>(\"getPageMappingByKey\", Collections\n                .<String, Object> singletonMap(\"key\", key), SPageMapping.class));\n    }\n\n    @Override\n    public SPageURL resolvePageURL(final SPageMapping pageMapping, final Map<String, Serializable> context,\n            final boolean executeAuthorizationRules)\n            throws SExecutionException, SAuthorizationException {\n        if (executeAuthorizationRules) {\n            final List<String> pageAuthorizationRules = pageMapping.getPageAuthorizationRules();\n            if (!isAllowedToAccess(pageMapping, context, pageAuthorizationRules)) {\n                throw new SAuthorizationException(\n                        \"Access to Page or URL with key \" + pageMapping.getKey() + \" is not allowed\");\n            }\n        }\n        String url = pageMapping.getUrl();\n        final String urlAdapter = pageMapping.getUrlAdapter();\n        if (urlAdapter != null) {\n            url = getUrlAdapter(urlAdapter).adapt(url, pageMapping.getKey(), context);\n        }\n        return new SPageURL(url, pageMapping.getPageId());\n    }\n\n    protected boolean isAllowedToAccess(final SPageMapping pageMapping, final Map<String, Serializable> context,\n            final List<String> pageAuthorizationRules)\n            throws SExecutionException {\n        boolean authorized = true;\n        for (final String rule : pageAuthorizationRules) {\n            final AuthorizationRule authorizationRule = authorizationRuleMap.get(rule);\n            if (authorizationRule == null) {\n                throw new SExecutionException(\n                        \"Authorization rule \" + rule + \" is not known. Cannot check if authorized or not.\");\n            }\n            if (authorizationRule.isAllowed(pageMapping.getKey(), context)) {\n                return true;\n            } else {\n                authorized = false;\n            }\n        }\n        return authorized;\n    }\n\n    private URLAdapter getUrlAdapter(final String urlAdapterName) throws SExecutionException {\n        final URLAdapter urlAdapter = urlAdapterMap.get(urlAdapterName);\n        if (urlAdapter == null) {\n            throw new SExecutionException(\n                    \"unable to execute the url adapter \" + urlAdapterName + \" because it does not exists\");\n        }\n        return urlAdapter;\n    }\n\n    @Override\n    public void delete(final SPageMapping sPageMapping) throws SDeletionException {\n        try {\n            recorder.recordDelete(new DeleteRecord(sPageMapping), PAGE_MAPPING);\n        } catch (final SRecorderException e) {\n            throw new SDeletionException(\"Unable to delete the page mapping with key \" + sPageMapping.getKey(), e);\n        }\n    }\n\n    @Override\n    public void update(final SPageMapping pageMapping, final Long pageId)\n            throws SObjectModificationException, SObjectNotFoundException, SBonitaReadException {\n        update(pageMapping, pageId, null, null);\n\n    }\n\n    void update(final SPageMapping pageMapping, final Long pageId, final String url, final String urlAdapter)\n            throws SObjectNotFoundException, SBonitaReadException,\n            SObjectModificationException {\n        try {\n            update(pageMapping, getEntityUpdateDescriptor(pageId, url, urlAdapter));\n        } catch (SSessionNotFoundException | SessionIdNotSetException | SRecorderException e) {\n            throw new SObjectModificationException(e);\n        }\n    }\n\n    @Override\n    public void update(final SPageMapping pageMapping, final String url, final String urlAdapter)\n            throws SObjectModificationException, SObjectNotFoundException,\n            SBonitaReadException {\n        update(pageMapping, null, url, urlAdapter);\n\n    }\n\n    EntityUpdateDescriptor getEntityUpdateDescriptor(final Long pageId, final String url, final String urlAdapter)\n            throws SSessionNotFoundException, SessionIdNotSetException {\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        descriptor.addField(\"pageId\", pageId);\n        descriptor.addField(\"url\", url);\n        descriptor.addField(\"urlAdapter\", urlAdapter);\n        descriptor.addField(\"lastUpdatedBy\", getSessionUserId());\n        descriptor.addField(\"lastUpdateDate\", System.currentTimeMillis());\n        return descriptor;\n    }\n\n    private long getSessionUserId() throws SSessionNotFoundException, SessionIdNotSetException {\n        return sessionService.getLoggedUserFromSession(sessionAccessor);\n    }\n\n    void update(final SPageMapping pageMapping, final EntityUpdateDescriptor descriptor)\n            throws SObjectNotFoundException, SBonitaReadException, SRecorderException {\n        recorder.recordUpdate(UpdateRecord.buildSetFields(pageMapping, descriptor), PAGE_MAPPING);\n    }\n\n    @Override\n    public List<SPageMapping> get(final long pageId, final int startIndex, final int maxResults)\n            throws SBonitaReadException {\n        final QueryOptions options = new QueryOptions(startIndex, maxResults);\n        final SelectListDescriptor<SPageMapping> listDescriptor = new SelectListDescriptor<SPageMapping>(\n                \"getPageMappingByPageId\",\n                Collections.<String, Object> singletonMap(\"pageId\", pageId), SPageMapping.class, options);\n        return persistenceService.selectList(listDescriptor);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/java/org/bonitasoft/engine/page/impl/PageServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page.impl;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.IOException;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Properties;\nimport java.util.Set;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.authorization.PermissionService;\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.exceptions.SDeletionException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectAlreadyExistsException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectCreationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.commons.io.IOUtil;\nimport org.bonitasoft.engine.page.AbstractSPage;\nimport org.bonitasoft.engine.page.PageService;\nimport org.bonitasoft.engine.page.PageServiceListener;\nimport org.bonitasoft.engine.page.SContentType;\nimport org.bonitasoft.engine.page.SInvalidPageTokenException;\nimport org.bonitasoft.engine.page.SInvalidPageZipException;\nimport org.bonitasoft.engine.page.SInvalidPageZipInconsistentException;\nimport org.bonitasoft.engine.page.SInvalidPageZipMissingAPropertyException;\nimport org.bonitasoft.engine.page.SInvalidPageZipMissingIndexException;\nimport org.bonitasoft.engine.page.SInvalidPageZipMissingPropertiesException;\nimport org.bonitasoft.engine.page.SPage;\nimport org.bonitasoft.engine.page.SPageLogBuilder;\nimport org.bonitasoft.engine.page.SPageUpdateBuilder;\nimport org.bonitasoft.engine.page.SPageUpdateBuilderFactory;\nimport org.bonitasoft.engine.page.SPageWithContent;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadOnlySelectByIdDescriptor;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity;\nimport org.bonitasoft.engine.queriablelogger.model.builder.ActionType;\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.util.DigestUtils;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\n@Slf4j\n@Service(\"pageService\")\npublic class PageServiceImpl implements PageService {\n\n    private static final String QUERY_GET_PAGE_BY_NAME = \"getPageByName\";\n\n    private static final String QUERY_GET_PAGE_BY_NAME_AND_PROCESS_DEFINITION_ID = \"getPageByNameAndProcessDefinitionId\";\n\n    private static final String QUERY_GET_PAGE_BY_PROCESS_DEFINITION_ID = \"getPageByProcessDefinitionId\";\n\n    private static final String METHOD_DELETE_PAGE = \"deletePage\";\n\n    private static final String METHOD_NAME_ADD_PAGE = \"addPage\";\n\n    private static final String METHOD_UPDATE_PAGE = \"updatePage\";\n\n    public static final String PAGE_TOKEN_PREFIX = \"custompage_\";\n\n    public static final String INDEX_GROOVY = \"Index.groovy\";\n\n    public static final String INDEX_HTML = \"index.html\";\n\n    public static final String RESOURCES_INDEX_HTML = \"resources/index.html\";\n\n    private static final String API_EXTENSIONS = \"apiExtensions\";\n\n    // Refers to a compiled class inside a jar, used by REST API extensions in compiled mode\n    private static final String COMPILED_CLASS_NAME = \"className\";\n\n    // Refers to a Groovy source file, used by REST API extensions in legacy mode (compilation is performed at runtime)\n    private static final String GROOVY_CLASS_FILENAME = \"classFileName\";\n\n    private static final String THEME_CSS = \"resources/theme.css\";\n\n    private final ReadPersistenceService persistenceService;\n\n    private final Recorder recorder;\n\n    private final QueriableLoggerService queriableLoggerService;\n\n    private final ReadSessionAccessor sessionAccessor;\n    private final SessionService sessionService;\n    private final PermissionService permissionService;\n\n    private final SPageContentHelper helper;\n\n    private List<PageServiceListener> pageServiceListeners;\n\n    public PageServiceImpl(final ReadPersistenceService persistenceService, final Recorder recorder,\n            final QueriableLoggerService queriableLoggerService, ReadSessionAccessor sessionAccessor,\n            SessionService sessionService, PermissionService permissionService) {\n        this.persistenceService = persistenceService;\n        this.recorder = recorder;\n        this.queriableLoggerService = queriableLoggerService;\n        this.sessionAccessor = sessionAccessor;\n        this.sessionService = sessionService;\n        this.permissionService = permissionService;\n        helper = new SPageContentHelper();\n    }\n\n    @Override\n    public SPage addPage(final SPage page, final byte[] content)\n            throws SObjectCreationException, SObjectAlreadyExistsException,\n            SInvalidPageZipException, SInvalidPageTokenException {\n        try {\n            final Map<String, byte[]> zipContent = unzip(content);\n            var pageProperties = getPreviousPageProperties(content);\n            if (isAnAPIExtension(pageProperties)) {\n                checkApiControllerExists(zipContent, pageProperties);\n            } else {\n                checkZipContainsRequiredEntries(zipContent);\n            }\n            checkPageNameIsValid(page.getName(), page.isProvided());\n            checkPageDisplayNameIsValid(page.getDisplayName());\n            return insertPage(page, content);\n        } catch (final IOException e) {\n            throw new SInvalidPageZipInconsistentException(\"Error while reading zip file\", e);\n        }\n    }\n\n    // @VisibleForTesting\n    Map<String, byte[]> unzip(byte[] content) throws IOException {\n        return IOUtil.unzip(content);\n    }\n\n    @Override\n    public SPage addPage(final byte[] content, final String contentName, final long userId)\n            throws SObjectCreationException, SObjectAlreadyExistsException, SInvalidPageZipException,\n            SInvalidPageTokenException {\n        SPage sPage = buildPage(content, contentName, userId, false, true, true);\n        return insertPage(sPage, content);\n    }\n\n    @Override\n    public SPage getPageByNameAndProcessDefinitionId(final String name, final long processDefinitionId)\n            throws SBonitaReadException {\n        final Map<String, Object> inputParameters = new HashMap<>();\n        inputParameters.put(\"pageName\", name);\n        inputParameters.put(\"processDefinitionId\", processDefinitionId);\n        return persistenceService.selectOne(new SelectOneDescriptor<>(QUERY_GET_PAGE_BY_NAME_AND_PROCESS_DEFINITION_ID,\n                inputParameters, SPage.class));\n    }\n\n    @Override\n    public List<SPage> getPageByProcessDefinitionId(final long processDefinitionId, final int fromIndex,\n            final int numberOfResults)\n            throws SBonitaReadException {\n        final Map<String, Object> inputParameters = new HashMap<>();\n        inputParameters.put(\"processDefinitionId\", processDefinitionId);\n        final OrderByOption orderByOption = new OrderByOption(SPage.class, SPageFields.PAGE_NAME, OrderByType.ASC);\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfResults,\n                Collections.singletonList(orderByOption));\n        return persistenceService.selectList(\n                new SelectListDescriptor<>(QUERY_GET_PAGE_BY_PROCESS_DEFINITION_ID, inputParameters, SPage.class,\n                        queryOptions));\n    }\n\n    @Override\n    public SPage buildPage(final byte[] content, final String contentName, final long userId, final boolean provided,\n            boolean removable, boolean editable) throws SInvalidPageZipException,\n            SInvalidPageTokenException {\n        final Properties pageProperties = readPageZip(content, provided);\n        long currentTime = System.currentTimeMillis();\n        return SPage.builder().name(pageProperties.getProperty(PageService.PROPERTIES_NAME))\n                .description(pageProperties.getProperty(PageService.PROPERTIES_DESCRIPTION))\n                .displayName(pageProperties.getProperty(PageService.PROPERTIES_DISPLAY_NAME))\n                .installationDate(currentTime).installedBy(userId).provided(provided)\n                .lastModificationDate(currentTime).lastUpdatedBy(userId)\n                .removable(removable).editable(editable)\n                .contentName(contentName)\n                .contentType(pageProperties.getProperty(PROPERTIES_CONTENT_TYPE, SContentType.PAGE)).build();\n\n    }\n\n    private void addPermissionsFromPageProperties(String pageName, Properties pageProperties) {\n        permissionService.addPermissions(pageName, pageProperties);\n    }\n\n    private void removePermissionsDeclaredInPageProperties(Properties pageProperties) {\n        permissionService.removePermissions(pageProperties);\n    }\n\n    @Override\n    public Properties readPageZip(final byte[] content)\n            throws SInvalidPageZipMissingIndexException, SInvalidPageZipMissingAPropertyException,\n            SInvalidPageZipInconsistentException, SInvalidPageZipMissingPropertiesException,\n            SInvalidPageTokenException {\n        return readPageZip(content, false);\n    }\n\n    Properties readPageZip(final byte[] content, final boolean provided)\n            throws SInvalidPageZipMissingIndexException, SInvalidPageZipMissingAPropertyException,\n            SInvalidPageZipInconsistentException, SInvalidPageZipMissingPropertiesException,\n            SInvalidPageTokenException {\n        final Properties pageProperties;\n        if (content == null) {\n            throw new SInvalidPageZipInconsistentException(\"Content can't be null\");\n        }\n        try {\n            pageProperties = getPreviousPageProperties(content);\n            final Map<String, byte[]> zipContent = unzip(content);\n            if (isAnAPIExtension(pageProperties)) {\n                checkApiControllerExists(zipContent, pageProperties);\n            } else {\n                checkZipContainsRequiredEntries(zipContent);\n            }\n            checkPageNameIsValid(pageProperties.getProperty(PageService.PROPERTIES_NAME), provided);\n            checkPageDisplayNameIsValid(pageProperties.getProperty(PageService.PROPERTIES_DISPLAY_NAME));\n        } catch (final IOException e) {\n            throw new SInvalidPageZipInconsistentException(\"Error while reading zip file\", e);\n        }\n        return pageProperties;\n    }\n\n    /**\n     * For each declared API, the following verifications are performed:\n     * - If a property 'className' is defined, then we assume that the required jar file is present in the lib folder\n     * and no\n     * further verification is performed.\n     * - If the property 'className' isn't defined, then we look for the property 'classFileName' (legacy mode). The\n     * property\n     * points to a Groovy source file, which will be compiled at runtime. We verify that this Groovy file exists in the\n     * archive.\n     */\n    private void checkApiControllerExists(Map<String, byte[]> zipContent, Properties pageProperties)\n            throws SInvalidPageZipInconsistentException, SInvalidPageZipMissingAPropertyException {\n        final Set<String> entrySet = zipContent.keySet();\n        final String declaredApis = pageProperties.getProperty(API_EXTENSIONS);\n        if (declaredApis == null || declaredApis.isEmpty()) {\n            throw new SInvalidPageZipMissingAPropertyException(API_EXTENSIONS);\n        }\n        final String[] apis = declaredApis.split(\",\");\n        for (final String api : apis) {\n            String className = pageProperties.getProperty(api.trim() + \".\" + COMPILED_CLASS_NAME);\n            if (className == null) {\n                final String classFileName = pageProperties.getProperty(api.trim() + \".\" + GROOVY_CLASS_FILENAME);\n                if (classFileName == null || classFileName.isEmpty()) {\n                    throw new SInvalidPageZipMissingAPropertyException(api.trim() + \".\" + GROOVY_CLASS_FILENAME);\n                }\n                if (!entrySet.contains(classFileName.trim())) {\n                    throw new SInvalidPageZipInconsistentException(\n                            String.format(\"RestAPIController %s has not been found in archive.\", classFileName.trim()));\n                }\n            }\n        }\n    }\n\n    private boolean isAnAPIExtension(Properties pageProperties) {\n        return Objects.equals(SContentType.API_EXTENSION, pageProperties.get(PageService.PROPERTIES_CONTENT_TYPE));\n    }\n\n    @Override\n    public SPage insertPage(final SPage page, final byte[] content)\n            throws SObjectAlreadyExistsException, SObjectCreationException {\n        final SPageLogBuilder logBuilder = getPageLog(ActionType.CREATED,\n                \"Adding a new page with name \" + page.getName());\n        try {\n            final SPageWithContent pageContent = new SPageWithContent(page, content);\n            final SPage pageByName = checkIfPageAlreadyExists(page);\n            if (null != pageByName) {\n                initiateLogBuilder(page.getId(), SQueriableLog.STATUS_FAIL, logBuilder, METHOD_NAME_ADD_PAGE);\n                throwAlreadyExistsException(pageByName.getName());\n            }\n            recorder.recordInsert(new InsertRecord(pageContent), PAGE);\n            addPermissionsFromPageProperties(page.getName(), getPreviousPageProperties(content));\n            page.setId(pageContent.getId());\n            notifyPageInsert(page, content);\n            return page;\n        } catch (SRecorderException | SBonitaReadException | IOException\n                | SInvalidPageZipMissingPropertiesException re) {\n            throw new SObjectCreationException(re);\n        }\n    }\n\n    private void notifyPageInsert(final SPage page, final byte[] content) throws SObjectCreationException {\n        for (final PageServiceListener pageServiceListener : pageServiceListeners) {\n            pageServiceListener.pageInserted(page, content);\n        }\n\n    }\n\n    @Override\n    public SPage checkIfPageAlreadyExists(final SPage page) throws SBonitaReadException {\n        SPage existingPage;\n        if (page.getProcessDefinitionId() > 0) {\n            existingPage = getPageByNameAndProcessDefinitionId(page.getName(), page.getProcessDefinitionId());\n        } else {\n            existingPage = getPageByName(page.getName());\n        }\n        return existingPage;\n    }\n\n    private void checkPageDisplayNameIsValid(final String displayName) throws SInvalidPageZipMissingAPropertyException {\n        if (displayName == null || displayName.length() == 0) {\n            throw new SInvalidPageZipMissingAPropertyException(PageService.PROPERTIES_DISPLAY_NAME);\n        }\n    }\n\n    private void checkPageNameIsValid(final String name, final boolean provided) throws SInvalidPageTokenException {\n        if (name == null || name.isEmpty() || !provided && !name.matches(PAGE_TOKEN_PREFIX + \"\\\\p{Alnum}+\")) {\n            throw new SInvalidPageTokenException(\n                    \"Page name is not valid, it must contains only alpha numeric characters and start with \"\n                            + PAGE_TOKEN_PREFIX);\n        }\n    }\n\n    void checkZipContainsRequiredEntries(final Map<String, byte[]> zipContent)\n            throws SInvalidPageZipMissingIndexException {\n        final Set<String> entrySet = zipContent.keySet();\n        for (final String entry : entrySet) {\n            if (INDEX_GROOVY.equals(entry)\n                    || INDEX_HTML.equalsIgnoreCase(entry)\n                    || RESOURCES_INDEX_HTML.equalsIgnoreCase(entry)\n                    || THEME_CSS.equalsIgnoreCase(entry)) {\n                return;\n            }\n        }\n        throw new SInvalidPageZipMissingIndexException();\n    }\n\n    SPageLogBuilder getPageLog(final ActionType actionType, final String message) {\n        final SPageLogBuilder logBuilder = new SPageLogBuilderImpl();\n        this.initializeLogBuilder(logBuilder, message);\n        this.updateLog(actionType, logBuilder);\n        return logBuilder;\n    }\n\n    @Override\n    public SPage getPage(final long pageId) throws SBonitaReadException, SObjectNotFoundException {\n        final SPage page = persistenceService.selectById(new SelectByIdDescriptor<>(SPage.class, pageId));\n        if (page == null) {\n            throw new SObjectNotFoundException(\"Page with id \" + pageId + \" not found\");\n        }\n        return page;\n    }\n\n    @Override\n    public SPage getPageByName(final String pageName) throws SBonitaReadException {\n        return persistenceService\n                .selectOne(new SelectOneDescriptor<>(QUERY_GET_PAGE_BY_NAME, Collections.singletonMap(\"pageName\",\n                        pageName), SPage.class));\n    }\n\n    @Override\n    public long getNumberOfPages(final QueryOptions options) throws SBonitaReadException {\n        return persistenceService.getNumberOfEntities(SPage.class, options, null);\n    }\n\n    @Override\n    public List<SPage> searchPages(final QueryOptions options) throws SBonitaReadException {\n        return persistenceService.searchEntity(SPage.class, options, null);\n    }\n\n    @Override\n    public void deletePage(final long pageId) throws SObjectModificationException, SObjectNotFoundException {\n        try {\n            final SPage page = getPage(pageId);\n            deletePageIfRemovable(page);\n        } catch (final SBonitaReadException sbe) {\n            throw new SObjectModificationException(sbe);\n        }\n    }\n\n    private void deletePageIfRemovable(final SPage sPage) throws SObjectModificationException {\n        if (!sPage.isRemovable()) {\n            throw new SObjectModificationException(\n                    \"The page '\" + sPage.getName() + \"' cannot be deleted because it is non-removable\");\n        } else {\n            deletePage(sPage);\n        }\n    }\n\n    private void deletePage(final SPage sPage) throws SObjectModificationException {\n        final SPageLogBuilder logBuilder = getPageLog(ActionType.DELETED, \"Deleting page named: \" + sPage.getName());\n        try {\n            // Need to read previous version permissions from page properties before deleting the page:\n            Properties pageProperties = getPreviousPageProperties(sPage);\n\n            for (final PageServiceListener pageServiceListener : pageServiceListeners) {\n                pageServiceListener.pageDeleted(sPage);\n            }\n            recorder.recordDelete(new DeleteRecord(sPage), PAGE);\n            removePermissionsDeclaredInPageProperties(pageProperties);\n            initiateLogBuilder(sPage.getId(), SQueriableLog.STATUS_OK, logBuilder,\n                    METHOD_DELETE_PAGE);\n        } catch (SRecorderException | SBonitaReadException | SDeletionException | SObjectNotFoundException\n                | SInvalidPageZipMissingPropertiesException | IOException re) {\n            initiateLogBuilder(sPage.getId(), SQueriableLog.STATUS_FAIL, logBuilder, METHOD_DELETE_PAGE);\n            throw new SObjectModificationException(re);\n        }\n    }\n\n    private <T extends SLogBuilder> void initializeLogBuilder(final T logBuilder, final String message) {\n        logBuilder.actionStatus(SQueriableLog.STATUS_FAIL).severity(SQueriableLogSeverity.INTERNAL).rawMessage(message);\n    }\n\n    private <T extends HasCRUDEAction> void updateLog(final ActionType actionType, final T logBuilder) {\n        logBuilder.setActionType(actionType);\n    }\n\n    void initiateLogBuilder(final long objectId, final int sQueriableLogStatus, final SPersistenceLogBuilder logBuilder,\n            final String methodName) {\n        logBuilder.actionScope(String.valueOf(objectId));\n        logBuilder.actionStatus(sQueriableLogStatus);\n        logBuilder.objectId(objectId);\n        final SQueriableLog log = logBuilder.build();\n        if (queriableLoggerService.isLoggable(log.getActionType(), log.getSeverity())) {\n            queriableLoggerService.log(this.getClass().getName(), methodName, log);\n        }\n    }\n\n    @Override\n    public byte[] getPageContent(final long pageId) throws SBonitaReadException, SObjectNotFoundException {\n        final SPageWithContent page = persistenceService\n                .selectById(new ReadOnlySelectByIdDescriptor<>(SPageWithContent.class, pageId));\n        if (page == null) {\n            throw new SObjectNotFoundException(\"Page with id \" + pageId + \" not found\");\n        }\n        final byte[] content = page.getContent();\n        try {\n            final Map<String, byte[]> contentAsMap = unzip(content);\n            final byte[] bytes = contentAsMap.get(PROPERTIES_FILE_NAME);\n            final Properties pageProperties = new Properties();\n            if (bytes != null) {\n                pageProperties.load(new ByteArrayInputStream(bytes));\n            }\n            pageProperties.put(PROPERTIES_NAME, page.getName());\n            pageProperties.put(PROPERTIES_DISPLAY_NAME, page.getDisplayName());\n            if (page.getDescription() != null) {\n                pageProperties.put(PROPERTIES_DESCRIPTION, page.getDescription());\n            }\n            contentAsMap.put(PROPERTIES_FILE_NAME,\n                    IOUtil.getPropertyAsString(pageProperties, \"The name must start with 'custompage_'\"));\n\n            return IOUtil.zip(contentAsMap);\n        } catch (final IOException e) {\n            throw new SBonitaReadException(\"the page is not a valid zip file\", e);\n        }\n    }\n\n    @Override\n    public SPage updatePage(final long pageId, final EntityUpdateDescriptor entityUpdateDescriptor)\n            throws SObjectModificationException, SObjectAlreadyExistsException, SInvalidPageTokenException {\n        try {\n\n            final SPage sPage = persistenceService.selectById(new SelectByIdDescriptor<>(SPage.class, pageId));\n            updatePage(entityUpdateDescriptor, sPage);\n            return sPage;\n        } catch (SBonitaReadException e) {\n            throw new SObjectModificationException(e);\n        }\n    }\n\n    AbstractSPage updatePage(EntityUpdateDescriptor entityUpdateDescriptor, AbstractSPage sPage)\n            throws SObjectModificationException, SObjectAlreadyExistsException {\n        long pageId = sPage.getId();\n        final SPageLogBuilder logBuilder = getPageLog(ActionType.UPDATED, \"Update a page with id \" + pageId);\n        final String logMethodName = METHOD_UPDATE_PAGE;\n        try {\n            if (!sPage.isEditable() && !isSystemSession()) { // Only non-editable pages can be updated (by the system)\n                throw new SObjectModificationException(\n                        \"The page '\" + sPage.getName() + \"' cannot be modified because it is not modifiable\");\n            }\n            if (entityUpdateDescriptor.getFields().containsKey(SPageFields.PAGE_PROCESS_DEFINITION_ID)) {\n                checkPageDuplicateForProcessDefinition(sPage, logBuilder, Long.parseLong(\n                        entityUpdateDescriptor.getFields().get(SPageFields.PAGE_PROCESS_DEFINITION_ID).toString()));\n            }\n            recorder.recordUpdate(UpdateRecord.buildSetFields(sPage, entityUpdateDescriptor), PAGE);\n            initiateLogBuilder(pageId, SQueriableLog.STATUS_OK, logBuilder, logMethodName);\n\n            return sPage;\n        } catch (SRecorderException | SBonitaReadException e) {\n            initiateLogBuilder(pageId, SQueriableLog.STATUS_FAIL, logBuilder, logMethodName);\n            throw new SObjectModificationException(e);\n        }\n    }\n\n    // @VisibleForTesting\n    protected boolean isSystemSession() {\n        return sessionService.getLoggedUserFromSession(sessionAccessor) == SessionService.SYSTEM_ID;\n    }\n\n    protected void checkPageDuplicateForProcessDefinition(\n            final AbstractSPage sPage, final SPageLogBuilder logBuilder, long sPageProcessDefinitionId)\n            throws SBonitaReadException, SObjectAlreadyExistsException {\n\n        String sPageName = sPage.getName();\n        if (sPageProcessDefinitionId > 0) {\n            SPage page = getPageByNameAndProcessDefinitionId(sPageName, sPageProcessDefinitionId);\n\n            if (page != null && page.getId() != sPage.getId()) {\n                initiateLogBuilder(sPage.getId(), SQueriableLog.STATUS_FAIL, logBuilder, METHOD_UPDATE_PAGE);\n                throwAlreadyExistsException(page.getName());\n            }\n        }\n    }\n\n    private void throwAlreadyExistsException(final String pageName) throws SObjectAlreadyExistsException {\n        throw new SObjectAlreadyExistsException(\"page with name \" + pageName);\n    }\n\n    @Override\n    public void updatePageContent(final long pageId, final byte[] content, final String contentName)\n            throws SObjectModificationException,\n            SInvalidPageZipException, SInvalidPageTokenException, SObjectAlreadyExistsException {\n        updatePageContent(pageId, content, contentName, null);\n    }\n\n    @Override\n    public void updatePageContent(long pageId, byte[] content, String contentName, SPageUpdateBuilder pageUpdateBuilder)\n            throws SObjectModificationException, SInvalidPageZipException, SInvalidPageTokenException,\n            SObjectAlreadyExistsException {\n\n        final SPageLogBuilder logBuilder = getPageLog(ActionType.UPDATED, \"Update a page with name \" + pageId);\n        final Properties pageProperties = readPageZip(content, false);\n        final SPageWithContent sPageContent;\n        try {\n            sPageContent = persistenceService.selectById(new SelectByIdDescriptor<>(SPageWithContent.class, pageId));\n            Properties previousPageProperties;\n            if (sPageContent.getContent() == null || sPageContent.getContent().length == 0) {\n                previousPageProperties = null;\n            } else {\n                // Need to read previous version permissions from page properties before deleting the page:\n                try {\n                    previousPageProperties = getPreviousPageProperties(sPageContent.getContent());\n                } catch (SInvalidPageZipMissingPropertiesException | IOException e) {\n                    previousPageProperties = null;\n                }\n            }\n            EntityUpdateDescriptor entityUpdateDescriptor = new EntityUpdateDescriptor();\n            entityUpdateDescriptor.addField(\"content\", content);\n            recorder.recordUpdate(UpdateRecord.buildSetFields(sPageContent, entityUpdateDescriptor), PAGE);\n\n            // remove previous permissions before adding the new ones:\n            if (previousPageProperties != null) {\n                // this can happen when update-tool puts empty content in new inserted pages,\n                // so that Engine startup update the page content, as it is detected as newer:\n                removePermissionsDeclaredInPageProperties(previousPageProperties);\n            }\n            addPermissionsFromPageProperties(pageProperties.getProperty(PROPERTIES_NAME), pageProperties);\n\n            initiateLogBuilder(pageId, SQueriableLog.STATUS_OK, logBuilder, METHOD_UPDATE_PAGE);\n        } catch (SRecorderException | SBonitaReadException re) {\n            initiateLogBuilder(pageId, SQueriableLog.STATUS_FAIL, logBuilder, METHOD_UPDATE_PAGE);\n            throw new SObjectModificationException(re);\n        }\n\n        if (pageUpdateBuilder == null) {\n            pageUpdateBuilder = BuilderFactory.get(SPageUpdateBuilderFactory.class)\n                    .createNewInstance(new EntityUpdateDescriptor());\n        }\n        if (contentName == null) {\n            pageUpdateBuilder.updateContentName(sPageContent.getContentName());\n        } else {\n            pageUpdateBuilder.updateContentName(contentName);\n        }\n        pageUpdateBuilder.updateDescription(pageProperties.getProperty(PROPERTIES_DESCRIPTION));\n        pageUpdateBuilder.updateDisplayName(pageProperties.getProperty(PROPERTIES_DISPLAY_NAME));\n        pageUpdateBuilder.updateContentType(pageProperties.getProperty(PROPERTIES_CONTENT_TYPE, SContentType.PAGE));\n        if (sPageContent.isProvided()) {\n            //update the md5 sum of the page only if it is provided\n            pageUpdateBuilder.updatePageHash(DigestUtils.md5DigestAsHex(content));\n        }\n        final AbstractSPage sPage = updatePage(pageUpdateBuilder.done(), sPageContent);\n        for (final PageServiceListener pageServiceListener : pageServiceListeners) {\n            pageServiceListener.pageUpdated(sPage, content);\n        }\n    }\n\n    Properties getPreviousPageProperties(SPage sPage) throws IOException, SInvalidPageZipMissingPropertiesException,\n            SBonitaReadException, SObjectNotFoundException {\n        return getPreviousPageProperties(getPageContent(sPage.getId()));\n    }\n\n    Properties getPreviousPageProperties(byte[] content) throws IOException, SInvalidPageZipMissingPropertiesException {\n        return helper.loadPageProperties(content);\n    }\n\n    @Autowired\n    public void setPageServiceListeners(final List<PageServiceListener> pageServiceListeners) {\n        this.pageServiceListeners = pageServiceListeners;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/java/org/bonitasoft/engine/page/impl/SPageContentFields.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page.impl;\n\npublic interface SPageContentFields {\n\n    String PAGE_ID = \"id\";\n\n    String PAGE_CONTENT = \"content\";\n\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/java/org/bonitasoft/engine/page/impl/SPageContentHelper.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page.impl;\n\nimport java.io.IOException;\nimport java.io.StringReader;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Map;\nimport java.util.Properties;\n\nimport org.bonitasoft.engine.commons.io.IOUtil;\nimport org.bonitasoft.engine.page.PageService;\nimport org.bonitasoft.engine.page.SInvalidPageZipMissingPropertiesException;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class SPageContentHelper {\n\n    public Properties loadPageProperties(final byte[] zipContent)\n            throws IOException, SInvalidPageZipMissingPropertiesException {\n        final Map<String, byte[]> content = IOUtil.unzip(zipContent);\n        return loadPageProperties(content);\n    }\n\n    public Properties loadPageProperties(final Map<String, byte[]> content)\n            throws SInvalidPageZipMissingPropertiesException, IOException {\n        final byte[] pagePropertiesContent = content.get(PageService.PROPERTIES_FILE_NAME);\n        if (pagePropertiesContent == null) {\n            throw new SInvalidPageZipMissingPropertiesException();\n        }\n\n        final Properties pageProperties = new Properties();\n        try (StringReader reader = new StringReader(new String(pagePropertiesContent, StandardCharsets.UTF_8))) {\n            pageProperties.load(reader);\n        }\n        return pageProperties;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/java/org/bonitasoft/engine/page/impl/SPageFields.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page.impl;\n\npublic interface SPageFields {\n\n    String PAGE_ID = \"id\";\n\n    String PAGE_NAME = \"name\";\n\n    String PAGE_DISPLAY_NAME = \"displayName\";\n\n    String PAGE_DESCRIPTION = \"description\";\n\n    String PAGE_LAST_MODIFICATION_DATE = \"lastModificationDate\";\n\n    String PAGE_LAST_UPDATED_BY = \"lastUpdatedBy\";\n\n    String PAGE_CONTENT_NAME = \"contentName\";\n\n    String PAGE_CONTENT_TYPE = \"contentType\";\n\n    String PAGE_PROCESS_DEFINITION_ID = \"processDefinitionId\";\n\n    String PAGE_HASH = \"pageHash\";\n\n    String IS_PROVIDED = \"provided\";\n\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/java/org/bonitasoft/engine/page/impl/SPageLogBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page.impl;\n\nimport org.bonitasoft.engine.page.SPageLogBuilderFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SPageLogBuilderFactoryImpl extends CRUDELogBuilderFactory implements SPageLogBuilderFactory {\n\n    public static final int PAGE_INDEX = 1;\n\n    public static final String PAGE_INDEX_NAME = \"numericIndex2\";\n\n    @Override\n    public String getObjectIdKey() {\n        return PAGE_INDEX_NAME;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/java/org/bonitasoft/engine/page/impl/SPageLogBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page.impl;\n\nimport org.bonitasoft.engine.page.SPageLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SPageLogBuilderImpl extends CRUDELogBuilder implements SPageLogBuilder {\n\n    private static final String PREFIX = \"PAGE\";\n\n    @Override\n    public SPersistenceLogBuilder objectId(final long objectId) {\n        queriableLogBuilder.numericIndex(SPageLogBuilderFactoryImpl.PAGE_INDEX, objectId);\n        return this;\n    }\n\n    @Override\n    protected String getActionTypePrefix() {\n        return PREFIX;\n    }\n\n    @Override\n    protected void checkExtraRules(final SQueriableLog log) {\n        if (log.getActionStatus() != SQueriableLog.STATUS_FAIL\n                && log.getNumericIndex(SPageLogBuilderFactoryImpl.PAGE_INDEX) == 0L) {\n            throw new MissingMandatoryFieldsException(\"Some mandatory fields are missing: page identifier\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/java/org/bonitasoft/engine/page/impl/SPageUpdateBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page.impl;\n\nimport org.bonitasoft.engine.page.SPageUpdateBuilder;\nimport org.bonitasoft.engine.page.SPageUpdateBuilderFactory;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\npublic class SPageUpdateBuilderFactoryImpl implements SPageUpdateBuilderFactory {\n\n    @Override\n    public SPageUpdateBuilder createNewInstance(EntityUpdateDescriptor descriptor) {\n        return new SPageUpdateBuilderImpl(descriptor);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/java/org/bonitasoft/engine/page/impl/SPageUpdateBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page.impl;\n\nimport org.bonitasoft.engine.page.SPageUpdateBuilder;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\npublic class SPageUpdateBuilderImpl implements SPageUpdateBuilder {\n\n    private final EntityUpdateDescriptor descriptor;\n\n    public SPageUpdateBuilderImpl(final EntityUpdateDescriptor descriptor) {\n        super();\n        this.descriptor = descriptor;\n    }\n\n    @Override\n    public SPageUpdateBuilder updateName(final String value) {\n        descriptor.addField(SPageFields.PAGE_NAME, value);\n        return this;\n    }\n\n    @Override\n    public SPageUpdateBuilder updateDescription(final String value) {\n        descriptor.addField(SPageFields.PAGE_DESCRIPTION, value);\n        return this;\n    }\n\n    @Override\n    public SPageUpdateBuilder updateDisplayName(final String value) {\n        descriptor.addField(SPageFields.PAGE_DISPLAY_NAME, value);\n        return this;\n    }\n\n    @Override\n    public SPageUpdateBuilder updateLastModificationDate(final long currentTimeMillis) {\n        descriptor.addField(SPageFields.PAGE_LAST_MODIFICATION_DATE, currentTimeMillis);\n        return this;\n    }\n\n    @Override\n    public SPageUpdateBuilder updateLastUpdatedBy(final long userId) {\n        descriptor.addField(SPageFields.PAGE_LAST_UPDATED_BY, userId);\n        return this;\n    }\n\n    @Override\n    public SPageUpdateBuilder updateContentName(final String value) {\n        descriptor.addField(SPageFields.PAGE_CONTENT_NAME, value);\n        return this;\n    }\n\n    @Override\n    public SPageUpdateBuilder updateContentType(String contentType) {\n        descriptor.addField(SPageFields.PAGE_CONTENT_TYPE, contentType);\n        return this;\n    }\n\n    @Override\n    public SPageUpdateBuilder updateProcessDefinitionId(Long processDefinitionId) {\n        descriptor.addField(SPageFields.PAGE_PROCESS_DEFINITION_ID, processDefinitionId);\n        return this;\n    }\n\n    @Override\n    public SPageUpdateBuilder updatePageHash(String hash) {\n        descriptor.addField(SPageFields.PAGE_HASH, hash);\n        return this;\n    }\n\n    @Override\n    public SPageUpdateBuilder markNonProvided() {\n        descriptor.addField(SPageFields.IS_PROVIDED, false);\n        return this;\n    }\n\n    @Override\n    public EntityUpdateDescriptor done() {\n        return descriptor;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/java/org/bonitasoft/engine/page/impl/SPageUpdateContentBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page.impl;\n\nimport org.bonitasoft.engine.page.SPageUpdateContentBuilder;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\npublic class SPageUpdateContentBuilderImpl implements SPageUpdateContentBuilder {\n\n    private final EntityUpdateDescriptor descriptor;\n\n    public SPageUpdateContentBuilderImpl(final EntityUpdateDescriptor descriptor) {\n        super();\n        this.descriptor = descriptor;\n    }\n\n    @Override\n    public EntityUpdateDescriptor done() {\n        return descriptor;\n    }\n\n    @Override\n    public SPageUpdateContentBuilder updateContent(byte[] content) {\n        descriptor.addField(\"content\", content);\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/java/org/bonitasoft/engine/page/impl/UrlAdapterInjector.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page.impl;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.page.URLAdapter;\n\n/**\n * @author Baptiste Mesta\n */\npublic class UrlAdapterInjector {\n\n    private List<URLAdapter> urlAdapters;\n    private PageMappingServiceImpl pageMappingService;\n\n    public UrlAdapterInjector(PageMappingServiceImpl pageMappingService) {\n        this.pageMappingService = pageMappingService;\n    }\n\n    public void setURLAdapters(List<URLAdapter> urlAdapters) {\n        this.urlAdapters = urlAdapters;\n    }\n\n    public void injectUrlAdapters() {\n        if (urlAdapters != null) {\n            pageMappingService.setURLAdapters(urlAdapters);\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-page/src/main/resources/org/bonitasoft/engine/page/impl/hibernate/page.queries.hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\" \"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd\">\n<hibernate-mapping auto-import=\"false\">\n\n   <query name=\"getPageById\">\n\t   SELECT page\n\t   FROM org.bonitasoft.engine.page.SPage AS page\n\t   WHERE page.id = :id\n   </query>\n\n   <query name=\"getPageByName\">\n\t   SELECT page\n\t   FROM org.bonitasoft.engine.page.SPage AS page\n\t   WHERE page.name = :pageName\n       AND  page.processDefinitionId = 0\n   </query>\n\n    <query name=\"getPageByNameAndProcessDefinitionId\">\n        SELECT  page\n        FROM    org.bonitasoft.engine.page.SPage AS page\n        WHERE   page.name = :pageName\n        AND     page.processDefinitionId = :processDefinitionId\n    </query>\n\n    <query name=\"getPageByProcessDefinitionId\">\n        SELECT  page\n        FROM    org.bonitasoft.engine.page.SPage AS page\n        WHERE   page.processDefinitionId = :processDefinitionId\n    </query>\n\n    <query name=\"getNumberOfSPage\">\n\t\tSELECT COUNT(page.id)\n\t\tFROM org.bonitasoft.engine.page.SPage AS page\n\t</query>\n\n\t<query name=\"searchSPage\">\n\t\tSELECT page\n\t\tFROM org.bonitasoft.engine.page.SPage AS page\n\t</query>\n\n\t<query name=\"getPageContent\">\n\t\tSELECT pagecontent\n\t\tFROM org.bonitasoft.engine.page.SPageWithContent AS pagecontent\n\t\tWHERE pagecontent.id = :id\n\t</query>\n\n\t<query name=\"getPageMappingByKey\">\n\t\tSELECT pagemapping\n\t\tFROM org.bonitasoft.engine.page.SPageMapping AS pagemapping\n\t\tWHERE pagemapping.key = :key\n\t</query>\n\n\t<query name=\"getPageMappingByPageId\">\n\t\tSELECT pagemapping\n\t\tFROM org.bonitasoft.engine.page.SPageMapping AS pagemapping\n\t\tWHERE pagemapping.pageId = :pageId\n\t</query>\n\n</hibernate-mapping>\n"
  },
  {
    "path": "services/bonita-page/src/test/java/org/bonitasoft/engine/page/impl/ApiExtensionPageServiceListenerImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page.impl;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.doThrow;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\nimport static org.mockito.Mockito.when;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Properties;\n\nimport org.bonitasoft.engine.commons.exceptions.SDeletionException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectCreationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.page.PageMappingService;\nimport org.bonitasoft.engine.page.SContentType;\nimport org.bonitasoft.engine.page.SInvalidPageZipMissingPropertiesException;\nimport org.bonitasoft.engine.page.SPage;\nimport org.bonitasoft.engine.page.SPageMapping;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ApiExtensionPageServiceListenerImplTest {\n\n    @Mock\n    private PageMappingService pageMappingService;\n\n    @Mock\n    private SPageContentHelper helper;\n\n    @InjectMocks\n    private ApiExtensionPageServiceListenerImpl listener;\n\n    private SPage buildPage(final long id) {\n        final SPage page = new SPage();\n        page.setId(id);\n        page.setContentType(SContentType.API_EXTENSION);\n        return page;\n    }\n\n    @Rule\n    public ExpectedException exception = ExpectedException.none();\n\n    @Test\n    public void pageInserted_should_only_care_of_api_extension() throws Exception {\n        final SPage page = new SPage();\n        page.setId(2L);\n        page.setContentType(SContentType.PAGE);\n        final byte[] content = new byte[] { 1, 0, 0 };\n\n        listener.pageInserted(page, content);\n\n        verifyNoInteractions(pageMappingService, helper);\n    }\n\n    @Test\n    public void pageInserted_should_add_api_resources_in_compiled_mode() throws Exception {\n        final long pageId = 1983L;\n        final SPage page = buildPage(pageId);\n        final byte[] content = new byte[] { 1, 0, 0 };\n        final Properties properties = new Properties();\n        properties.setProperty(\"apiExtensions\", \"employee, address\");\n        properties.setProperty(\"employee.method\", \"GET\");\n        properties.setProperty(\"employee.pathTemplate\", \"employees\");\n        properties.setProperty(\"employee.className\", \"com.company.Index\");\n        properties.setProperty(\"employee.permissions\", \"myPermission\");\n        properties.setProperty(\"address.method\", \"GET\");\n        properties.setProperty(\"address.pathTemplate\", \"employees/{employeeId}/address\");\n        properties.setProperty(\"address.className\", \"com.company.Index1\");\n        properties.setProperty(\"address.permissions\", \"myPermission\");\n        when(helper.loadPageProperties(content)).thenReturn(properties);\n\n        listener.pageInserted(page, content);\n\n        verify(pageMappingService).create(\"apiExtension|GET|employees\", pageId, Collections.<String> emptyList());\n        verify(pageMappingService).create(\"apiExtension|GET|employees/{employeeId}/address\", pageId,\n                Collections.<String> emptyList());\n        verifyNoMoreInteractions(pageMappingService);\n    }\n\n    @Test\n    public void pageInserted_should_add_api_resources_with_absolute_path() throws Exception {\n        final long pageId = 1983L;\n        final SPage page = buildPage(pageId);\n        final byte[] content = new byte[] { 1, 0, 0 };\n        final Properties properties = new Properties();\n        properties.setProperty(\"apiExtensions\", \"employee, address\");\n        properties.setProperty(\"employee.method\", \"GET\");\n        properties.setProperty(\"employee.pathTemplate\", \"employees\");\n        properties.setProperty(\"employee.className\", \"com.company.Index\");\n        properties.setProperty(\"employee.permissions\", \"myPermission\");\n        properties.setProperty(\"address.method\", \"GET\");\n        properties.setProperty(\"address.pathTemplate\", \"/employees/{employeeId}/address\");\n        properties.setProperty(\"address.className\", \"com.company.Index1\");\n        properties.setProperty(\"address.permissions\", \"myPermission\");\n        when(helper.loadPageProperties(content)).thenReturn(properties);\n\n        listener.pageInserted(page, content);\n\n        verify(pageMappingService).create(\"apiExtension|GET|employees\", pageId, Collections.<String> emptyList());\n        verify(pageMappingService).create(\"apiExtension|GET|employees/{employeeId}/address\", pageId,\n                Collections.<String> emptyList());\n        verifyNoMoreInteractions(pageMappingService);\n    }\n\n    @Test\n    public void pageInserted_should_add_api_resources_in_legacy_mode() throws Exception {\n        final long pageId = 1983L;\n        final SPage page = buildPage(pageId);\n        final byte[] content = new byte[] { 1, 0, 0 };\n        final Properties properties = new Properties();\n        properties.setProperty(\"apiExtensions\", \"employee, address\");\n        properties.setProperty(\"employee.method\", \"GET\");\n        properties.setProperty(\"employee.pathTemplate\", \"employees\");\n        properties.setProperty(\"employee.classFileName\", \"Index.groovy\");\n        properties.setProperty(\"employee.permissions\", \"myPermission\");\n        properties.setProperty(\"address.method\", \"GET\");\n        properties.setProperty(\"address.pathTemplate\", \"employees/{employeeId}/address\");\n        properties.setProperty(\"address.classFileName\", \"Index1.groovy\");\n        properties.setProperty(\"address.permissions\", \"myPermission\");\n        when(helper.loadPageProperties(content)).thenReturn(properties);\n\n        listener.pageInserted(page, content);\n\n        verify(pageMappingService).create(\"apiExtension|GET|employees\", pageId, Collections.<String> emptyList());\n        verify(pageMappingService).create(\"apiExtension|GET|employees/{employeeId}/address\", pageId,\n                Collections.<String> emptyList());\n        verifyNoMoreInteractions(pageMappingService);\n    }\n\n    @Test\n    public void pageInserted_should_throw_an_exception_if_the_file_does_not_exists() throws Exception {\n        //given\n        final SPage page = buildPage(10L);\n        final byte[] content = new byte[] { 1, 0, 0 };\n        when(helper.loadPageProperties(content)).thenThrow(new SInvalidPageZipMissingPropertiesException());\n\n        //then\n        exception.expect(SObjectCreationException.class);\n        exception.expectMessage(\"Missing page.properties\");\n\n        //when\n        listener.pageInserted(page, content);\n    }\n\n    @Test\n    public void pageInserted_should_throw_an_exception_if_an_io_exception_occurs() throws Exception {\n        //given\n        final SPage page = buildPage(10L);\n        final byte[] content = new byte[] { 1, 0, 0 };\n        when(helper.loadPageProperties(content)).thenThrow(new IOException());\n\n        //then\n        exception.expect(SObjectCreationException.class);\n\n        //when\n        listener.pageInserted(page, content);\n\n    }\n\n    @Test\n    public void pageInserted_should_throw_an_exception_if_the_resource_path_template_is_missing() throws Exception {\n        //given\n        final long pageId = 1983L;\n        final SPage page = buildPage(pageId);\n        final byte[] content = new byte[] { 1, 0, 0 };\n        final Properties properties = new Properties();\n        properties.setProperty(\"apiExtensions\", \"employee\");\n        properties.setProperty(\"employee.method\", \"GET\");\n        properties.setProperty(\"employee.classFileName\", \"Index.groovy\");\n        when(helper.loadPageProperties(content)).thenReturn(properties);\n\n        //then\n        exception.expect(SObjectCreationException.class);\n        exception.expectMessage(\"the property 'employee.pathTemplate' is missing or is empty\");\n\n        //when\n        listener.pageInserted(page, content);\n    }\n\n    @Test\n    public void pageInserted_should_throw_an_exception_if_the_resource_path_template_is_empty() throws Exception {\n        //given\n        final long pageId = 1983L;\n        final SPage page = buildPage(pageId);\n        final byte[] content = new byte[] { 1, 0, 0 };\n        final Properties properties = new Properties();\n        properties.setProperty(\"apiExtensions\", \"employee\");\n        properties.setProperty(\"employee.method\", \"GET\");\n        properties.setProperty(\"employee.pathTemplate\", \"   \");\n        properties.setProperty(\"employee.classFileName\", \"Index.groovy\");\n        when(helper.loadPageProperties(content)).thenReturn(properties);\n\n        //then\n        exception.expect(SObjectCreationException.class);\n        exception.expectMessage(\"the property 'employee.pathTemplate' is missing or is empty\");\n\n        //when\n        listener.pageInserted(page, content);\n    }\n\n    @Test\n    public void pageInserted_should_throw_an_exception_if_the_method_is_missing() throws Exception {\n        //given\n        final long pageId = 1983L;\n        final SPage page = buildPage(pageId);\n        final byte[] content = new byte[] { 1, 0, 0 };\n        final Properties properties = new Properties();\n        properties.setProperty(\"apiExtensions\", \"employee\");\n        properties.setProperty(\"employee.pathTemplate\", \"employees/{id}\");\n        properties.setProperty(\"employee.classFileName\", \"Index.groovy\");\n        when(helper.loadPageProperties(content)).thenReturn(properties);\n\n        //then\n        exception.expect(SObjectCreationException.class);\n        exception.expectMessage(\"the property 'employee.method' is missing or is empty\");\n\n        //when\n        listener.pageInserted(page, content);\n    }\n\n    @Test\n    public void pageInserted_should_throw_an_exception_if_the_class_file_name_is_missing() throws Exception {\n        //given\n        final long pageId = 1983L;\n        final SPage page = buildPage(pageId);\n        final byte[] content = new byte[] { 1, 0, 0 };\n        final Properties properties = new Properties();\n        properties.setProperty(\"apiExtensions\", \"employee \");\n        properties.setProperty(\"employee.method\", \"GET\");\n        properties.setProperty(\"employee.pathTemplate\", \"employees\");\n        when(helper.loadPageProperties(content)).thenReturn(properties);\n\n        //then\n        exception.expect(SObjectCreationException.class);\n        exception.expectMessage(\"the property 'employee.classFileName' is missing or is empty\");\n\n        //when\n        listener.pageInserted(page, content);\n    }\n\n    @Test\n    public void pageInserted_should_throw_an_exception_if_the_permissions_are_missing() throws Exception {\n        //given\n        final long pageId = 1983L;\n        final SPage page = buildPage(pageId);\n        final byte[] content = new byte[] { 1, 0, 0 };\n        final Properties properties = new Properties();\n        properties.setProperty(\"apiExtensions\", \"employee \");\n        properties.setProperty(\"employee.method\", \"GET\");\n        properties.setProperty(\"employee.pathTemplate\", \"employees\");\n        properties.setProperty(\"employee.classFileName\", \"Index.groovy\");\n        when(helper.loadPageProperties(content)).thenReturn(properties);\n\n        //then\n        exception.expect(SObjectCreationException.class);\n        exception.expectMessage(\"the property 'employee.permissions' is missing or is empty\");\n\n        //when\n        listener.pageInserted(page, content);\n    }\n\n    @Test\n    public void pageInserted_should_throw_an_exception_if_api_extension_is_missing() throws Exception {\n        final long pageId = 1983L;\n        final SPage page = buildPage(pageId);\n        final byte[] content = new byte[] { 1, 0, 0 };\n        final Properties properties = new Properties();\n        properties.setProperty(\"employee.method\", \"GET\");\n        properties.setProperty(\"employee.classFileName\", \"Index.groovy\");\n        when(helper.loadPageProperties(content)).thenReturn(properties);\n\n        //then\n        exception.expect(SObjectCreationException.class);\n        exception.expectMessage(\"the property 'apiExtensions' is missing or is empty\");\n\n        //when\n        listener.pageInserted(page, content);\n    }\n\n    @Test\n    public void pageDeleted_should_only_care_of_api_extension() throws Exception {\n        final SPage page = new SPage();\n        page.setId(2L);\n        page.setContentType(SContentType.PAGE);\n\n        listener.pageDeleted(page);\n\n        verifyNoInteractions(pageMappingService, helper);\n    }\n\n    @Test\n    public void pageDeleted_should_delete_all_mappings() throws Exception {\n        //given\n        final long pageId = 10L;\n        final SPage page = buildPage(pageId);\n        final List<SPageMapping> mappings = buildPageMappings(100);\n        final List<SPageMapping> mappings2 = buildPageMappings(53);\n        when(pageMappingService.get(pageId, 0, 100)).thenReturn(mappings, mappings2);\n\n        //when\n        listener.pageDeleted(page);\n\n        //then\n        verify(pageMappingService, times(153)).delete(any(SPageMapping.class));\n    }\n\n    private List<SPageMapping> buildPageMappings(final int numberOfResults) {\n        final List<SPageMapping> mappings = new ArrayList<>();\n        for (int i = 0; i < numberOfResults; i++) {\n            mappings.add(new SPageMapping());\n        }\n        return mappings;\n    }\n\n    @Test\n    public void pageDeleted_should_throw_an_exception_when_an_exception_occurs_when_getting_mappings()\n            throws Exception {\n        //given\n        final long pageId = 10L;\n        final SPage page = buildPage(pageId);\n        when(pageMappingService.get(pageId, 0, 100)).thenThrow(new SBonitaReadException(\"exception\"));\n\n        //then\n        exception.expect(SBonitaReadException.class);\n        exception.expectMessage(\"exception\");\n\n        //when\n        listener.pageDeleted(page);\n    }\n\n    @Test\n    public void pageDeleted_should_throw_an_exception_when_an_exception_occurs_when_deleting_mappings()\n            throws Exception {\n        //given\n        final long pageId = 10L;\n        final SPage page = buildPage(pageId);\n        final List<SPageMapping> mappings = buildPageMappings(10);\n        when(pageMappingService.get(pageId, 0, 100)).thenReturn(mappings);\n        doThrow(new SDeletionException(\"message\")).when(pageMappingService).delete(any(SPageMapping.class));\n\n        //then\n        exception.expect(SDeletionException.class);\n        exception.expectMessage(\"message\");\n\n        //when\n        listener.pageDeleted(page);\n    }\n\n    @Test\n    public void pageUpdated_should_delete_old_mappings_and_add_new_ones() throws Exception {\n        final long pageId = 10L;\n        final SPage page = buildPage(pageId);\n        final byte[] content = new byte[] { 1, 0, 0 };\n        final Properties properties = new Properties();\n        properties.setProperty(\"apiExtensions\", \"employees, employee \");\n        properties.setProperty(\"employees.method\", \"GET\");\n        properties.setProperty(\"employees.pathTemplate\", \"employees\");\n        properties.setProperty(\"employees.classFileName\", \"Index.groovy\");\n        properties.setProperty(\"employees.permissions\", \"myPermission\");\n        properties.setProperty(\"employee.method\", \"PUT\");\n        properties.setProperty(\"employee.pathTemplate\", \"employees\");\n        properties.setProperty(\"employee.classFileName\", \"Index.groovy\");\n        properties.setProperty(\"employee.permissions\", \"myPermission\");\n        when(helper.loadPageProperties(content)).thenReturn(properties);\n        final SPageMapping pageMapping1 = buildPageMapping(\"apiExtension|POST|employees\");\n        final SPageMapping pageMapping2 = buildPageMapping(\"apiExtension|GET|employees\");\n        final List<SPageMapping> mappings = new ArrayList<>();\n        mappings.add(pageMapping1);\n        mappings.add(pageMapping2);\n        when(pageMappingService.get(pageId, 0, 100)).thenReturn(mappings);\n\n        listener.pageUpdated(page, content);\n\n        verify(pageMappingService).delete(pageMapping1);\n        verify(pageMappingService, never()).delete(pageMapping2);\n        verify(pageMappingService).create(\"apiExtension|PUT|employees\", pageId, Collections.<String> emptyList());\n        verify(pageMappingService, never()).create(pageMapping2.getKey(), pageId, Collections.<String> emptyList());\n    }\n\n    private SPageMapping buildPageMapping(final String key) {\n        final SPageMapping pageMapping = new SPageMapping();\n        pageMapping.setKey(key);\n        return pageMapping;\n    }\n\n    @Test\n    public void pageUpdated_should_throw_an_update_exception_if_an_internal_exception_occurs() throws Exception {\n        //given\n        final long pageId = 10L;\n        final SPage page = buildPage(pageId);\n        final byte[] content = new byte[] { 1, 0, 0 };\n        when(helper.loadPageProperties(content)).thenThrow(new IOException(\"exception\"));\n\n        //then\n        exception.expect(SObjectModificationException.class);\n        exception.expectMessage(\"exception\");\n\n        //when\n        listener.pageUpdated(page, content);\n    }\n\n    @Test\n    public void pageUpdated_should_only_care_of_api_extension() throws Exception {\n        final SPage page = new SPage();\n        page.setId(2L);\n        page.setContentType(SContentType.PAGE);\n        final byte[] content = new byte[] { 1, 0, 0 };\n\n        listener.pageUpdated(page, content);\n\n        verifyNoInteractions(pageMappingService, helper);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-page/src/test/java/org/bonitasoft/engine/page/impl/PageMappingServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.util.Lists.newArrayList;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Collections;\n\nimport org.bonitasoft.engine.commons.exceptions.SObjectCreationException;\nimport org.bonitasoft.engine.page.SPageMapping;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class PageMappingServiceImplTest {\n\n    @Mock\n    private Recorder recorder;\n\n    @Mock\n    private ReadPersistenceService persistenceService;\n\n    @Mock\n    private SessionService sessionService;\n\n    @Mock\n    private ReadSessionAccessor sessionAccessor;\n\n    @InjectMocks\n    private PageMappingServiceImpl pageMappingService;\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void get_should_return_page_mappings() throws Exception {\n        final QueryOptions options = new QueryOptions(0, 2);\n        final SelectListDescriptor<SPageMapping> listDescriptor = new SelectListDescriptor<SPageMapping>(\n                \"getPageMappingByPageId\",\n                Collections.<String, Object> singletonMap(\"pageId\", 1983L), SPageMapping.class, options);\n\n        pageMappingService.get(1983L, 0, 2);\n\n        verify(persistenceService).selectList(listDescriptor);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void get_should_throw_an_exception() throws Exception {\n        when(persistenceService.selectList(any(SelectListDescriptor.class)))\n                .thenThrow(new SBonitaReadException(\"exception\"));\n\n        pageMappingService.get(1983L, 0, 2);\n    }\n\n    @Test\n    public void should_throw_a_SObjectCreationException_when_create_a_page_mapping_with_a_key_that_already_exists()\n            throws Exception {\n        final SPageMapping pageMapping = mock(SPageMapping.class);\n        when(pageMapping.getPageId()).thenReturn(5L);\n        when(persistenceService.selectOne(new SelectOneDescriptor<SPageMapping>(\"getPageMappingByKey\", Collections\n                .<String, Object> singletonMap(\"key\", \"aMappinKey\"), SPageMapping.class))).thenReturn(pageMapping);\n\n        expectedException.expect(SObjectCreationException.class);\n        expectedException.expectMessage(\"Mapping key aMappinKey already exists for page with id 5\");\n\n        pageMappingService.create(\"aMappinKey\", 1L, newArrayList(\"rule1\"));\n    }\n\n    @Test\n    public void should_throw_a_SObjectCreationException_when_create_a_page_mapping_with_a_key_that_already_exists_for_an_url()\n            throws Exception {\n        final SPageMapping pageMapping = mock(SPageMapping.class);\n        when(pageMapping.getPageId()).thenReturn(5L);\n        when(persistenceService.selectOne(new SelectOneDescriptor<SPageMapping>(\"getPageMappingByKey\", Collections\n                .<String, Object> singletonMap(\"key\", \"aMappinKey\"), SPageMapping.class))).thenReturn(pageMapping);\n\n        expectedException.expect(SObjectCreationException.class);\n        expectedException.expectMessage(\"Mapping key aMappinKey already exists for page with id 5\");\n\n        pageMappingService.create(\"aMappinKey\", \"http://bonitasoft.com\", \"adapter\", newArrayList(\"rule1\"));\n    }\n\n    @Test\n    public void should_create_a_page_mapping_for_a_given_page() throws Exception {\n        when(persistenceService.selectOne(new SelectOneDescriptor<SPageMapping>(\"getPageMappingByKey\", Collections\n                .<String, Object> singletonMap(\"key\", \"aMappinKey\"), SPageMapping.class))).thenReturn(null);\n\n        pageMappingService.create(\"aMappinKey\", 1L, newArrayList(\"rule1\"));\n        final ArgumentCaptor<InsertRecord> captor = ArgumentCaptor.forClass(InsertRecord.class);\n\n        verify(recorder).recordInsert(captor.capture(), anyString());\n        final InsertRecord insertRecord = captor.getValue();\n        final PersistentObject entity = insertRecord.getEntity();\n        assertThat(entity).isInstanceOf(SPageMapping.class);\n        final SPageMapping mapping = (SPageMapping) entity;\n        assertThat(mapping.getKey()).isEqualTo(\"aMappinKey\");\n        assertThat(mapping.getPageId()).isEqualTo(1L);\n        assertThat(mapping.getPageAuthorizationRules()).containsOnly(\"rule1\");\n    }\n\n    @Test\n    public void should_create_a_page_mapping_for_a_given_url() throws Exception {\n        when(persistenceService.selectOne(new SelectOneDescriptor<SPageMapping>(\"getPageMappingByKey\", Collections\n                .<String, Object> singletonMap(\"key\", \"aMappinKey\"), SPageMapping.class))).thenReturn(null);\n\n        pageMappingService.create(\"aMappinKey\", \"http://bonitasoft.com\", \"adapter\", newArrayList(\"rule1\"));\n        final ArgumentCaptor<InsertRecord> captor = ArgumentCaptor.forClass(InsertRecord.class);\n\n        verify(recorder).recordInsert(captor.capture(), anyString());\n        final InsertRecord insertRecord = captor.getValue();\n        final PersistentObject entity = insertRecord.getEntity();\n        assertThat(entity).isInstanceOf(SPageMapping.class);\n        final SPageMapping mapping = (SPageMapping) entity;\n        assertThat(mapping.getKey()).isEqualTo(\"aMappinKey\");\n        assertThat(mapping.getUrl()).isEqualTo(\"http://bonitasoft.com\");\n        assertThat(mapping.getUrlAdapter()).isEqualTo(\"adapter\");\n        assertThat(mapping.getPageAuthorizationRules()).containsOnly(\"rule1\");\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-page/src/test/java/org/bonitasoft/engine/page/impl/PageServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page.impl;\n\nimport static java.util.Collections.singletonList;\nimport static org.assertj.core.api.Assertions.*;\nimport static org.bonitasoft.engine.commons.Pair.pair;\nimport static org.bonitasoft.engine.io.FileAndContentUtils.file;\nimport static org.bonitasoft.engine.io.FileAndContentUtils.zip;\nimport static org.junit.Assert.assertNull;\nimport static org.junit.Assert.assertThrows;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.*;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.IOException;\nimport java.io.UnsupportedEncodingException;\nimport java.time.Instant;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Properties;\n\nimport org.bonitasoft.engine.authorization.PermissionService;\nimport org.bonitasoft.engine.commons.Pair;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectAlreadyExistsException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.commons.io.IOUtil;\nimport org.bonitasoft.engine.page.AbstractSPage;\nimport org.bonitasoft.engine.page.PageService;\nimport org.bonitasoft.engine.page.PageServiceListener;\nimport org.bonitasoft.engine.page.SContentType;\nimport org.bonitasoft.engine.page.SInvalidPageTokenException;\nimport org.bonitasoft.engine.page.SInvalidPageZipException;\nimport org.bonitasoft.engine.page.SInvalidPageZipInconsistentException;\nimport org.bonitasoft.engine.page.SInvalidPageZipMissingAPropertyException;\nimport org.bonitasoft.engine.page.SInvalidPageZipMissingIndexException;\nimport org.bonitasoft.engine.page.SInvalidPageZipMissingPropertiesException;\nimport org.bonitasoft.engine.page.SPage;\nimport org.bonitasoft.engine.page.SPageLogBuilder;\nimport org.bonitasoft.engine.page.SPageWithContent;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadOnlySelectByIdDescriptor;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.queriablelogger.model.builder.ActionType;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\nimport org.mockito.stubbing.Answer;\nimport org.springframework.util.DigestUtils;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class PageServiceImplTest {\n\n    private static final String PAGE_PROPERTIES = \"page.properties\";\n\n    private static final String INDEX_HTML = \"index.html\";\n\n    private static final String INDEX_GROOVY = \"Index.groovy\";\n\n    private static final String THEME_CSS = \"resources/theme.css\";\n\n    private static final String CONTENT_NAME = \"content.zip\";\n\n    private static final boolean PROVIDED_TRUE = true;\n\n    private static final int INSTALLED_BY_ID = 45;\n\n    private static final int INSTALLATION_DATE_AS_LONG = 123456;\n\n    private static final String PAGE_NAME = \"custompage_pageName\";\n\n    public static final long PROCESS_DEFINITION_ID = 846L;\n\n    public static final long USER_ID = 98989L;\n\n    @Mock\n    private Recorder recorder;\n\n    @Mock\n    private ReadPersistenceService readPersistenceService;\n\n    @Mock\n    private QueryOptions queryOptions;\n\n    @Mock\n    private QueriableLoggerService queriableLoggerService;\n\n    @Mock\n    private SPageLogBuilder pageLogBuilder;\n    @Mock\n    private ReadSessionAccessor sessionAccessor;\n    @Mock\n    private SessionService sessionService;\n    @Mock\n    private PermissionService permissionService;\n\n    @Mock\n    private EntityUpdateDescriptor entityUpdateDescriptor;\n    @Captor\n    private ArgumentCaptor<EntityUpdateDescriptor> entityUpdateDescriptorCaptor;\n\n    private PageServiceImpl pageServiceImpl;\n\n    @Mock\n    PageServiceListener apiExtensionPageServiceListener;\n    @Captor\n    ArgumentCaptor<SPage> pageArgumentCaptor;\n\n    @Before\n    public void before() {\n        pageServiceImpl = spy(\n                new PageServiceImpl(readPersistenceService, recorder, queriableLoggerService, sessionAccessor,\n                        sessionService, permissionService));\n        doReturn(pageLogBuilder).when(pageServiceImpl).getPageLog(any(ActionType.class), anyString());\n        doNothing().when(pageServiceImpl).initiateLogBuilder(anyLong(), anyInt(), any(SPersistenceLogBuilder.class),\n                anyString());\n\n        final List<PageServiceListener> listeners = singletonList(apiExtensionPageServiceListener);\n        pageServiceImpl.setPageServiceListeners(listeners);\n    }\n\n    @Test\n    public void getNumberOfPages() throws SBonitaException {\n        // given\n        final long expected = 50;\n        when(readPersistenceService.getNumberOfEntities(SPage.class, queryOptions, null)).thenReturn(expected);\n\n        // when\n        final long numberOfPages = pageServiceImpl.getNumberOfPages(queryOptions);\n\n        // then\n        Assert.assertEquals(expected, numberOfPages);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getNumberOfPagesThrowsException() throws SBonitaException {\n        // given\n        // when\n        when(readPersistenceService.getNumberOfEntities(SPage.class, queryOptions, null))\n                .thenThrow(new SBonitaReadException(\"ouch!\"));\n        pageServiceImpl.getNumberOfPages(queryOptions);\n\n        // then\n        // exception;\n    }\n\n    @Test(expected = SInvalidPageTokenException.class)\n    public void createPage_should_throw_exception_when_name_is_empty() throws SBonitaException, IOException {\n\n        final long pageId = 15;\n        final SPage pageWithEmptyName = new SPage(\"\", 123456, 45, true, CONTENT_NAME);\n        pageWithEmptyName.setDisplayName(\"plop\");\n        pageWithEmptyName.setId(pageId);\n        pageServiceImpl.addPage(pageWithEmptyName, validPageContent(\"plop\"));\n\n    }\n\n    @Test(expected = SObjectAlreadyExistsException.class)\n    public void addPage_should_throw_exception_when_already_exist() throws Exception {\n\n        // given\n        final SPage newPage = new SPage(PAGE_NAME, INSTALLATION_DATE_AS_LONG, INSTALLED_BY_ID, PROVIDED_TRUE,\n                CONTENT_NAME);\n        newPage.setDisplayName(\"plop\");\n        // when\n        when(pageServiceImpl.getPageByName(PAGE_NAME)).thenReturn(newPage);\n        pageServiceImpl.addPage(newPage, validPageContent(PAGE_NAME));\n\n        // then exception\n\n    }\n\n    @Test(expected = SObjectAlreadyExistsException.class)\n    public void should_create_page_throw_exception_when_name_exists() throws Exception {\n\n        // given\n        final SPage newPage = new SPage(PAGE_NAME, 123456, 45, true, CONTENT_NAME);\n        newPage.setDisplayName(\"display Name\");\n\n        // when\n        when(pageServiceImpl.getPageByName(PAGE_NAME)).thenReturn(newPage);\n        final byte[] validContent = validPageContent(PAGE_NAME);\n        pageServiceImpl.addPage(newPage, validContent);\n\n        // then exception\n\n    }\n\n    @Test\n    public void should_create_page_with_process_scope_when_name_exists() throws Exception {\n\n        // given\n        final SPage newPage = new SPage(PAGE_NAME, 123456, 45, false, CONTENT_NAME);\n        newPage.setDisplayName(\"display Name\");\n        final byte[] validContent = validPageContent(PAGE_NAME);\n        when(pageServiceImpl.getPageByName(PAGE_NAME)).thenReturn(null);\n        pageServiceImpl.addPage(newPage, validContent);\n\n        // when\n        final SPage newProcessPage = new SPage(PAGE_NAME, 123456, 45, false, CONTENT_NAME);\n        newProcessPage.setContentType(SContentType.FORM);\n        newProcessPage.setProcessDefinitionId(PROCESS_DEFINITION_ID);\n        newProcessPage.setDisplayName(\"display Name\");\n        final SPage insertedPage = pageServiceImpl.addPage(newProcessPage, validContent);\n\n        //then\n        assertThat(insertedPage).isNotNull().isEqualToComparingFieldByField(newProcessPage);\n\n    }\n\n    @Test(expected = SObjectAlreadyExistsException.class)\n    public void should_create_page_with_processDefinitionId_throw_exception_when_name_exists() throws Exception {\n\n        // given\n        final SPage newPage = new SPage(PAGE_NAME, 123456, 45, true, CONTENT_NAME);\n        newPage.setContentType(SContentType.FORM);\n        newPage.setProcessDefinitionId(PROCESS_DEFINITION_ID);\n        newPage.setDisplayName(\"display Name\");\n\n        // when\n        when(pageServiceImpl.getPageByNameAndProcessDefinitionId(PAGE_NAME, PROCESS_DEFINITION_ID)).thenReturn(newPage);\n        final byte[] validContent = validPageContent(PAGE_NAME);\n        pageServiceImpl.addPage(newPage, validContent);\n\n        // then exception\n\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private byte[] validPageContent(final String pageName) throws IOException {\n        return IOUtil.zip(pair(\"Index.groovy\", \"content of the groovy\".getBytes()),\n                pair(PAGE_PROPERTIES, (\"name=custompage_\" + pageName\n                        + \"\\ndisplayName=mypage display name\\ndescription=mypage description\\n\").getBytes()));\n    }\n\n    @Test\n    public void getPage() throws SBonitaException {\n        final long pageId = 15;\n        final SPage expected = new SPage(\"page1\", 123456, 45, true, CONTENT_NAME);\n        expected.setId(pageId);\n        when(readPersistenceService.selectById(new SelectByIdDescriptor<>(SPage.class, pageId))).thenReturn(expected);\n        // when\n        final SPage page = pageServiceImpl.getPage(pageId);\n        // then\n        Assert.assertEquals(expected, page);\n    }\n\n    @Test(expected = SObjectNotFoundException.class)\n    public void getPageThrowsPageNotFoundException() throws SBonitaException {\n\n        final long pageId = 15;\n        final SPage expected = new SPage(\"page1\", 123456, 45, true, CONTENT_NAME);\n        expected.setId(pageId);\n        when(readPersistenceService.selectById(new SelectByIdDescriptor<>(SPage.class, pageId))).thenReturn(null);\n\n        pageServiceImpl.getPage(pageId);\n    }\n\n    @Test\n    public void getPageByNameReturnsNullWhenNotFound() throws SBonitaException {\n        // given: page does not exists\n        // when\n        final SPage pageByName = pageServiceImpl.getPageByName(\"unknown\");\n        // then\n        assertNull(pageByName);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getPageThrowsException() throws SBonitaException {\n\n        final long pageId = 15;\n        final SPage expected = new SPage(\"page1\", 123456, 45, true, CONTENT_NAME);\n        expected.setId(pageId);\n        when(readPersistenceService.selectById(new SelectByIdDescriptor<>(SPage.class, pageId))).thenThrow(\n                new SBonitaReadException(\"ouch!\"));\n\n        pageServiceImpl.getPage(pageId);\n    }\n\n    @Test\n    public void getPageContent_should_add_properties_in_the_zip() throws SBonitaException, IOException {\n        // given: a zip without properties\n        final SPageWithContent page = new SPageWithContent();\n        page.setName(\"mypage\");\n        page.setDescription(\"mypage description\");\n        page.setDisplayName(\"mypage display name\");\n        page.setId(12);\n        final byte[] content = IOUtil.zip(Collections.singletonMap(\"Index.groovy\", \"content of the groovy\".getBytes()));\n        page.setContent(content);\n        doReturn(page).when(readPersistenceService)\n                .selectById(new ReadOnlySelectByIdDescriptor<>(SPageWithContent.class, 12));\n\n        // when\n        final byte[] result = pageServiceImpl.getPageContent(12);\n\n        // then\n        final Map<String, byte[]> unzip = IOUtil.unzip(result);\n        assertThat(unzip.size()).isEqualTo(2);\n        final Properties pageProperties = new Properties();\n        pageProperties.load(new ByteArrayInputStream(unzip.get(PAGE_PROPERTIES)));\n\n        assertThat(pageProperties.get(\"name\")).isEqualTo(\"mypage\");\n        assertThat(pageProperties.get(\"displayName\")).isEqualTo(\"mypage display name\");\n        assertThat(pageProperties.get(\"description\")).isEqualTo(\"mypage description\");\n    }\n\n    @Test\n    public void getPageContent_should_add_properties_in_the_zip_with_non_mandatory_metadata()\n            throws SBonitaException, IOException {\n        final SPageWithContent page = new SPageWithContent();\n        page.setName(\"mypage\");\n        page.setDisplayName(\"mypage display name\");\n        //no description\n        page.setId(12);\n        page.setContent(IOUtil.zip(Collections.singletonMap(\"Index.groovy\", \"content of the groovy\".getBytes())));\n        doReturn(page).when(readPersistenceService)\n                .selectById(new ReadOnlySelectByIdDescriptor<>(SPageWithContent.class, 12));\n\n        // when\n        final byte[] result = pageServiceImpl.getPageContent(12);\n\n        // then\n        final Properties pageProperties = new Properties();\n        pageProperties.load(new ByteArrayInputStream(IOUtil.unzip(result).get(PAGE_PROPERTIES)));\n\n        assertThat(pageProperties.get(\"name\")).isEqualTo(\"mypage\");\n        assertThat(pageProperties.get(\"displayName\")).isEqualTo(\"mypage display name\");\n        assertThat(pageProperties.get(\"description\")).isNull();\n    }\n\n    @Test\n    public void getPageContent_should_update_properties_in_the_zip_if_exists_and_keep_others()\n            throws SBonitaException, IOException {\n        // given: a zip with outdated properties\n        final SPageWithContent page = new SPageWithContent();\n        page.setName(\"mypageUpdated\");\n        page.setDescription(\"mypageUpdated description\");\n        page.setDisplayName(\"mypageUpdated display name\");\n        page.setId(12);\n\n        @SuppressWarnings(\"unchecked\")\n        final byte[] content = IOUtil.zip(\n                pair(\"Index.groovy\", \"content of the groovy\".getBytes()),\n                pair(PAGE_PROPERTIES,\n                        \"name=custompage_mypage\\ndisplayName=mypage display name\\ndescription=mypage description\\naCustomProperty=plop\\n\"\n                                .getBytes()));\n        page.setContent(content);\n        doReturn(page).when(readPersistenceService)\n                .selectById(new ReadOnlySelectByIdDescriptor<>(SPageWithContent.class, 12));\n\n        // when\n        final byte[] result = pageServiceImpl.getPageContent(12);\n\n        // then\n        final Map<String, byte[]> unzip = IOUtil.unzip(result);\n        assertThat(unzip.size()).isEqualTo(2);\n        final Properties pageProperties = new Properties();\n        pageProperties.load(new ByteArrayInputStream(unzip.get(PAGE_PROPERTIES)));\n\n        assertThat(pageProperties.get(\"name\")).isEqualTo(\"mypageUpdated\");\n        assertThat(pageProperties.get(\"displayName\")).isEqualTo(\"mypageUpdated display name\");\n        assertThat(pageProperties.get(\"description\")).isEqualTo(\"mypageUpdated description\");\n        assertThat(pageProperties.get(\"aCustomProperty\")).isEqualTo(\"plop\");\n    }\n\n    @Test(expected = SObjectNotFoundException.class)\n    public void should_getPageContent_throw_not_found() throws SBonitaException {\n        pageServiceImpl.getPageContent(12);\n    }\n\n    @Test\n    public void deletePage_should_call_delete_on_recorder() throws Exception {\n        final long pageId = 15;\n        final SPage expected = new SPage(\"page1\", 123456, 45, true, CONTENT_NAME);\n        expected.setId(pageId);\n\n        doReturn(new Properties()).when(pageServiceImpl).getPreviousPageProperties(expected);\n\n        doNothing().when(recorder).recordDelete(any(DeleteRecord.class), nullable(String.class));\n        doReturn(expected).when(pageServiceImpl).getPage(pageId);\n\n        pageServiceImpl.deletePage(pageId);\n\n        verify(recorder, times(1)).recordDelete(any(DeleteRecord.class), nullable(String.class));\n\n    }\n\n    @Test\n    public void deletePageThrowsPageNotFoundException() throws Exception {\n        final long pageId = 15L;\n        final SPage expected = new SPage(\"page1\", 123456, 45, true, CONTENT_NAME);\n        expected.setId(pageId);\n\n        doReturn(new Properties()).when(pageServiceImpl).getPreviousPageProperties(expected);\n        doThrow(new SRecorderException(\"ouch !\")).when(recorder).recordDelete(any(DeleteRecord.class),\n                nullable(String.class));\n        when(readPersistenceService.selectById(new SelectByIdDescriptor<>(SPage.class, pageId))).thenReturn(expected);\n\n        assertThrows(SObjectModificationException.class, () -> pageServiceImpl.deletePage(pageId));\n    }\n\n    @Test\n    public void updatePageContent_should_check_zip_content() {\n        assertThrows(SInvalidPageZipException.class,\n                () -> pageServiceImpl.updatePageContent(15L, \"aaa\".getBytes(), CONTENT_NAME));\n    }\n\n    @Test\n    public void addPage_should_check_zip_content() {\n        // given\n        final SPage sPage = new SPage(\"page1\", 123456, 45, false, CONTENT_NAME);\n        final byte[] content = \"invalid content\".getBytes();\n\n        assertThrows(SInvalidPageZipException.class, () -> pageServiceImpl.addPage(sPage, content));\n    }\n\n    @Test\n    public void zipTest_not_a_zip() {\n        assertThrows(SInvalidPageZipException.class, () -> pageServiceImpl.readPageZip(\"badContent\".getBytes(), false));\n    }\n\n    @Test\n    public void zipTest_Bad_Content() throws Exception {\n        // given\n        final byte[] content = IOUtil.zip(pair(\"aFile.txt\", \"hello\".getBytes()),\n                pair(PAGE_PROPERTIES,\n                        \"name=custompage_mypage\\ndisplayName=My Page\\ndescription=mypage description\\n\\ncontentType=page\"\n                                .getBytes()));\n\n        // when\n        assertThrows(\"Missing Index.groovy or index.html\", SInvalidPageZipMissingIndexException.class,\n                () -> pageServiceImpl.readPageZip(content, false));\n\n        // then\n        // exception\n    }\n\n    @Test\n    public void should_throw_an_SInvalidPageZipMissingAPropertyException_for_apiExtension_without_apis()\n            throws Exception {\n        // given\n        final byte[] content = IOUtil.zip(pair(\"MyController.groovy\", \"content of the groovy\".getBytes()),\n                pair(PAGE_PROPERTIES,\n                        \"name=custompage_mypage\\ndisplayName=My Page\\ndescription=mypage description\\n\\ncontentType=apiExtension\"\n                                .getBytes()));\n\n        // when\n        assertThrows(\"Missing fields in the page.properties: apiExtensions\",\n                SInvalidPageZipMissingAPropertyException.class, () -> pageServiceImpl.readPageZip(content, false));\n    }\n\n    @Test\n    public void should_throw_an_SInvalidPageZipMissingAPropertyException_for_api_extension_without_classFilename()\n            throws Exception {\n        // given\n        final byte[] content = IOUtil.zip(pair(\"MyController.groovy\", \"content of the groovy\".getBytes()),\n                pair(PAGE_PROPERTIES,\n                        \"name=custompage_mypage\\ndisplayName=My Page\\ndescription=mypage description\\n\\ncontentType=apiExtension\\napiExtensions=myApi\"\n                                .getBytes()));\n\n        // when\n        assertThrows(\"Missing fields in the page.properties: myApi.classFileName\",\n                SInvalidPageZipMissingAPropertyException.class, () -> pageServiceImpl.readPageZip(content, false));\n    }\n\n    @Test\n    public void should_throw_an_SInvalidPageZipInconsistentException_for_api_extension_with_classFilename_not_in_archive()\n            throws Exception {\n        // given\n        final byte[] content = IOUtil.zip(Collections.singletonMap(PAGE_PROPERTIES,\n                \"name=custompage_mypage\\ndisplayName=My Page\\ndescription=mypage description\\n\\ncontentType=apiExtension\\napiExtensions=myApi\\nmyApi.classFileName=MyController.groovy\"\n                        .getBytes()));\n\n        // when\n        assertThrows(\"RestAPIController MyController.groovy has not been found in archive.\",\n                SInvalidPageZipInconsistentException.class, () -> pageServiceImpl.readPageZip(content, false));\n    }\n\n    @Test\n    public void should_throw_an_SInvalidPageZipMissingAPropertyException_for_api_extension_with_empty_classFilename()\n            throws Exception {\n        // given\n        final byte[] content = IOUtil.zip(pair(\"MyController.groovy\", \"content of the groovy\".getBytes()),\n                pair(PAGE_PROPERTIES,\n                        \"name=custompage_mypage\\ndisplayName=My Page\\ndescription=mypage description\\n\\ncontentType=apiExtension\\napiExtensions=myApi\\nmyApi.classFileName=\"\n                                .getBytes()));\n\n        // when\n        assertThrows(\"Missing fields in the page.properties: myApi.classFileName\",\n                SInvalidPageZipMissingAPropertyException.class, () -> pageServiceImpl.readPageZip(content, false));\n    }\n\n    @Test\n    public void should_throw_an_SInvalidPageZipMissingAPropertyException_if_apiExtensions_is_empty() throws Exception {\n        // given\n        final byte[] content = IOUtil.zip(pair(\"MyController.groovy\", \"content of the groovy\".getBytes()),\n                pair(PAGE_PROPERTIES,\n                        \"name=custompage_mypage\\ndisplayName=My Page\\ndescription=mypage description\\n\\ncontentType=apiExtension\\napiExtensions=\"\n                                .getBytes()));\n\n        // when\n        assertThrows(\"Missing fields in the page.properties: apiExtensions\",\n                SInvalidPageZipMissingAPropertyException.class, () -> pageServiceImpl.readPageZip(content, false));\n    }\n\n    @Test\n    public void should_read_page_with_an_api_extension() throws Exception {\n        // given\n        final byte[] content = IOUtil.zip(pair(\"MyController.groovy\", \"content of the groovy\".getBytes()),\n                pair(PAGE_PROPERTIES,\n                        \"name=custompage_mypage\\ndisplayName=My Page\\ndescription=mypage description\\n\\ncontentType=apiExtension\\napiExtensions=myApi\\nmyApi.classFileName=MyController.groovy\"\n                                .getBytes()));\n\n        // when\n        pageServiceImpl.readPageZip(content, false);\n    }\n\n    @Test\n    public void should_read_page_with_many_api_extensions() throws Exception {\n        // given\n        final byte[] content = IOUtil.zip(pair(\"MyController.groovy\", \"content of the groovy\".getBytes()),\n                pair(\"org/MyOtherController.groovy\", \"content of the groovy\".getBytes()),\n                pair(PAGE_PROPERTIES,\n                        \"name=custompage_mypage\\ndisplayName=My Page\\ndescription=mypage description\\n\\ncontentType=apiExtension\\napiExtensions=myApi, myOtherApi\\nmyApi.classFileName=MyController.groovy\\nmyOtherApi.classFileName=org/MyOtherController.groovy\"\n                                .getBytes()));\n\n        // when\n        pageServiceImpl.readPageZip(content, false);\n    }\n\n    @Test\n    public void zipTest_Throws_exception() throws Exception {\n        // given\n        final byte[] content = IOUtil.zip(pair(\"aFile.txt\", \"hello\".getBytes()), pair(PAGE_PROPERTIES,\n                \"name=custompage_mypage\\ndisplayName=My Page\\ndescription=mypage description\\n\\ncontentType=page\"\n                        .getBytes()));\n        doThrow(SInvalidPageZipMissingIndexException.class).when(pageServiceImpl)\n                .checkZipContainsRequiredEntries(anyMap());\n\n        //when\n        Throwable throwable = catchThrowable(() -> pageServiceImpl.readPageZip(content, false));\n\n        //then\n        assertThat(throwable)\n                .isExactlyInstanceOf(SInvalidPageZipMissingIndexException.class);\n    }\n\n    @Test\n    public void zipTest_Content_7_0_With_index_html_in_resources_folder() throws Exception {\n        // given\n        final Map<String, byte[]> zipContent = Collections.singletonMap(\"resources/index.html\", \"hello\".getBytes());\n\n        // when\n        pageServiceImpl.checkZipContainsRequiredEntries(zipContent);\n\n        // then\n    }\n\n    @Test\n    public void zipTest_valid_Groovy() throws Exception {\n\n        // given\n        @SuppressWarnings(\"unchecked\")\n        final byte[] content = IOUtil.zip(getIndexGroovyContentPair(),\n                getPagePropertiesContentPair());\n\n        // when then\n        pageServiceImpl.readPageZip(content, false);\n\n        // expected no exception\n\n    }\n\n    @Test\n    public void zipTest_page_properties_invalid_name() throws Exception {\n        // given\n        @SuppressWarnings(\"unchecked\")\n        final byte[] content = IOUtil.zip(getIndexGroovyContentPair(),\n                pair(PAGE_PROPERTIES,\n                        \"name=mypage\\ndisplayName=mypage display name\\ndescription=mypage description\\n\".getBytes()));\n\n        // when then\n        assertThrows(SInvalidPageTokenException.class, () -> pageServiceImpl.readPageZip(content, false));\n\n    }\n\n    @Test\n    public void zipTest_page_properties_no_name() throws Exception {\n        // given\n        @SuppressWarnings(\"unchecked\")\n        final byte[] content = IOUtil.zip(getIndexGroovyContentPair(),\n                pair(PAGE_PROPERTIES, \"displayName=mypage display name\\ndescription=mypage description\\n\".getBytes()));\n\n        // when then\n        assertThrows(SInvalidPageTokenException.class, () -> pageServiceImpl.readPageZip(content, false));\n\n    }\n\n    @Test\n    public void zipTest_page_properties_invalid_display_name() throws Exception {\n        // given\n        @SuppressWarnings(\"unchecked\")\n        final byte[] content = IOUtil.zip(getIndexGroovyContentPair(),\n                pair(PAGE_PROPERTIES,\n                        \"name=custompage_mypage\\ndisplayName=\\ndescription=mypage description\\n\".getBytes()));\n\n        // when then\n        assertThrows(SInvalidPageZipException.class, () -> pageServiceImpl.readPageZip(content, false));\n    }\n\n    @Test\n    public void zipTest_page_properties_no_display_name() throws Exception {\n        // given\n        @SuppressWarnings(\"unchecked\")\n        final byte[] content = IOUtil.zip(getIndexGroovyContentPair(),\n                pair(PAGE_PROPERTIES, \"name=custompage_mypage\\ndescription=mypage description\\n\".getBytes()));\n\n        // when\n        assertThrows(\"Missing fields in the page.properties: \" + PageService.PROPERTIES_DISPLAY_NAME,\n                SInvalidPageZipMissingAPropertyException.class, () -> pageServiceImpl.readPageZip(content, false));\n\n        // then exception\n\n    }\n\n    @Test\n    public void zipTestGroovyWithWrongName() throws Exception {\n        // given\n        @SuppressWarnings(\"unchecked\")\n        final byte[] content = IOUtil.zip(pair(\"index.groovy\", \"content of the groovy\".getBytes()),\n                getPagePropertiesContentPair());\n\n        // when then\n        assertThrows(SInvalidPageZipMissingIndexException.class, () -> pageServiceImpl.readPageZip(content, false));\n    }\n\n    @Test\n    public void zipTest_valid_Html() throws Exception {\n        // given\n        @SuppressWarnings(\"unchecked\")\n        final byte[] content = IOUtil.zip(pair(INDEX_HTML, \"content of the groovy\".getBytes()),\n                pair(PAGE_PROPERTIES,\n                        \"name=custompage_mypage\\ndisplayName=mypage final display name\\ndescription=final mypage description\\n\"\n                                .getBytes()));\n\n        // when then\n        pageServiceImpl.readPageZip(content, false);\n\n    }\n\n    @Test\n    public void zipTest_valid_Theme() throws Exception {\n        // given\n        @SuppressWarnings(\"unchecked\")\n        final byte[] content = IOUtil.zip(pair(THEME_CSS, \"h1 { font-size:14pt; }\".getBytes()),\n                pair(PAGE_PROPERTIES,\n                        \"name=custompage_mypage\\ndisplayName=mypage final display name\\ndescription=final mypage description\\n\"\n                                .getBytes()));\n\n        // when then\n        pageServiceImpl.readPageZip(content, false);\n\n    }\n\n    @Test\n    public void zipTest_no_page_properties() throws Exception {\n        // given\n        @SuppressWarnings(\"unchecked\")\n        final byte[] content = IOUtil.zip(pair(INDEX_HTML, \"content of the groovy\".getBytes()));\n\n        // when then\n        assertThrows(\"Missing page.properties\", SInvalidPageZipMissingPropertiesException.class,\n                () -> pageServiceImpl.readPageZip(content, false));\n\n    }\n\n    @Test\n    public void checkPageContentIsValid_null() {\n        assertThrows(SInvalidPageZipException.class, () -> pageServiceImpl.readPageZip(null, false));\n    }\n\n    @Test\n    public void checkPageContentIsValid_badZip() {\n        assertThrows(SInvalidPageZipException.class, () -> pageServiceImpl.readPageZip(\"not a zip\".getBytes(), false));\n    }\n\n    @Test\n    public void checkPageContentIsValid_validZip() throws Exception {\n        // given\n        @SuppressWarnings(\"unchecked\")\n        final byte[] content = IOUtil.zip(getIndexGroovyContentPair(),\n                getPagePropertiesContentPair());\n        // when\n        pageServiceImpl.readPageZip(content, false);\n\n        // then no exception\n\n    }\n\n    @Test\n    public void should_redPageZip_call_the_internal_with_provided_false()\n            throws SInvalidPageTokenException, SInvalidPageZipInconsistentException,\n            SInvalidPageZipMissingAPropertyException, SInvalidPageZipMissingPropertiesException,\n            SInvalidPageZipMissingIndexException {\n        final byte[] content = { 0, 1, 2 };\n        doReturn(null).when(pageServiceImpl).readPageZip(content, false);\n\n        //when\n        pageServiceImpl.readPageZip(content);\n\n        //then\n        verify(pageServiceImpl).readPageZip(content, false);\n    }\n\n    @Test\n    public void should_add_page_throw_exception_when_invalid_zip() throws Exception {\n        //given\n        final SPage sPage = new SPage(\"page\", 123456, 45, true, CONTENT_NAME);\n        final byte[] badContent = \"not_a_zip\".getBytes();\n        doThrow(IOException.class).when(pageServiceImpl).unzip(badContent);\n\n        //when\n        Throwable throwable = catchThrowable(() -> pageServiceImpl.addPage(sPage, badContent));\n\n        //then\n        assertThat(throwable)\n                .isExactlyInstanceOf(SInvalidPageZipInconsistentException.class)\n                .hasMessage(\"Error while reading zip file\")\n                .hasCauseExactlyInstanceOf(IOException.class);\n    }\n\n    @Test\n    public void should_add_page_insertPage() throws Exception {\n        //given\n        final SPage sPage = new SPage(\"page\", 123456, 45, true, CONTENT_NAME);\n        sPage.setDisplayName(\"displayName1\");\n        final byte[] content = IOUtil.zip(getIndexGroovyContentPair(),\n                getPagePropertiesContentPair());\n\n        //when\n        pageServiceImpl.addPage(sPage, content);\n\n        //then\n        verify(pageServiceImpl).insertPage(sPage, content);\n    }\n\n    @Test\n    public void should_add_page_with_correct_fields() throws Exception {\n        //given\n        byte[] content = IOUtil.zip(getIndexGroovyContentPair(),\n                getPagePropertiesContentPair());\n\n        doAnswer(invocation -> invocation.getArguments()[0]).when(pageServiceImpl)\n                .insertPage(pageArgumentCaptor.capture(), any());\n\n        Instant now = Instant.now();\n        //when\n        SPage insertedPage = pageServiceImpl.addPage(content, CONTENT_NAME, 45);\n\n        //then\n        long pageDate = insertedPage.getLastModificationDate();\n        SPage expected = new SPage(\"custompage_mypage\", \"mypage description\", \"mypage display name\",\n                pageDate, 45, false, pageDate, 45, CONTENT_NAME);\n        expected.setRemovable(true);\n        expected.setEditable(true);\n\n        assertThat(insertedPage).isEqualTo(expected);\n        assertThat(insertedPage.getLastModificationDate()).isGreaterThanOrEqualTo(now.toEpochMilli());\n        List<SPage> insertedPages = pageArgumentCaptor.getAllValues();\n        assertThat(insertedPages).hasSize(1).containsExactly(expected);\n    }\n\n    @Test\n    public void should_add_page_return_default_content_type() throws Exception {\n        //given\n        final byte[] content1 = IOUtil.zip(getIndexGroovyContentPair(), getPagePropertiesContentPair());\n\n        //when\n        final SPage insertedPage1 = pageServiceImpl.addPage(content1, CONTENT_NAME, USER_ID);\n\n        //then\n        SPageAssert.assertThat(insertedPage1).hasContentType(SContentType.PAGE);\n\n    }\n\n    @Test\n    public void should_rest_api_extension_record_page_mapping() throws Exception {\n        //given\n        final byte[] content = IOUtil.zip(getIndexGroovyContentPair(),\n                getPagePropertiesContentPair(\"contentType=\" + SContentType.API_EXTENSION, \"apiExtensions=myApi\",\n                        \"myApi.classFileName=Index.groovy\"));\n\n        //when\n        final SPage insertedPage = pageServiceImpl.addPage(content, CONTENT_NAME, USER_ID);\n\n        //then\n        SPageAssert.assertThat(insertedPage).hasContentType(SContentType.API_EXTENSION);\n    }\n\n    @Test\n    public void should_read_rest_api_extension_in_compile_mode() throws Exception {\n        //given\n        final byte[] content = IOUtil.zip(getIndexGroovyContentPair(),\n                getPagePropertiesContentPair(\"contentType=\" + SContentType.API_EXTENSION, \"apiExtensions=myApi\",\n                        \"myApi.className=com.company.Index\"));\n\n        //when\n        final SPage insertedPage = pageServiceImpl.addPage(content, CONTENT_NAME, USER_ID);\n\n        //then\n        SPageAssert.assertThat(insertedPage).hasContentType(SContentType.API_EXTENSION);\n    }\n\n    private Pair<String, byte[]> getPagePropertiesContentPair(final String... otherProperties)\n            throws UnsupportedEncodingException {\n        final StringBuilder stringBuilder = new StringBuilder()\n                .append(\"name=custompage_mypage\\ndisplayName=mypage display name\\ndescription=mypage description\\n\");\n        for (final String property : otherProperties) {\n            stringBuilder.append(property).append(\"\\n\");\n        }\n\n        return pair(PAGE_PROPERTIES, stringBuilder.toString().getBytes(\"UTF-8\"));\n    }\n\n    private Pair<String, byte[]> getIndexGroovyContentPair() {\n        return pair(INDEX_GROOVY, \"content of the groovy\".getBytes());\n    }\n\n    @Test\n    public void addPage_should_execute_listener() throws Exception {\n        final byte[] content = IOUtil.zip(getIndexGroovyContentPair(),\n                getPagePropertiesContentPair(\"contentType=\" + SContentType.PAGE));\n\n        final SPage insertedPage = pageServiceImpl.addPage(content, CONTENT_NAME, USER_ID);\n\n        verify(apiExtensionPageServiceListener).pageInserted(insertedPage, content);\n    }\n\n    @Test\n    public void updatePage_should_not_execute_listener() throws Exception {\n        final SPage page = new SPage(\"name\", 10201983L, 2005L, false, \"contentName\");\n        when(readPersistenceService.selectById(any(SelectByIdDescriptor.class))).thenReturn(page);\n\n        pageServiceImpl.updatePage(page.getId(), entityUpdateDescriptor);\n\n        verifyNoInteractions(apiExtensionPageServiceListener);\n    }\n\n    @Test\n    public void updatePage_should_execute_listener() throws Exception {\n        final SPage page = new SPage(\"name\", 10201983L, 2005L, false, \"contentName\");\n        page.setId(45L);\n        final byte[] content = IOUtil.zip(getIndexGroovyContentPair(),\n                getPagePropertiesContentPair(\"contentType=\" + SContentType.PAGE));\n        final SPageWithContent pageContent = new SPageWithContent(page, content);\n\n        when(readPersistenceService.selectById(new SelectByIdDescriptor<>(SPageWithContent.class, page.getId())))\n                .thenReturn(pageContent);\n\n        pageServiceImpl.updatePageContent(page.getId(), content, \"contentName\");\n\n        verify(apiExtensionPageServiceListener).pageUpdated(pageContent, content);\n    }\n\n    @Test\n    public void updatePageContent_should_update_page_content_type() throws Exception {\n        verifyPageUpdateContent(getPagePropertiesContentPair(\"contentType=\" + SContentType.FORM), SContentType.FORM);\n    }\n\n    @Test\n    public void updatePageContent_should_update_to_default_content_type() throws Exception {\n        verifyPageUpdateContent(getPagePropertiesContentPair(), SContentType.PAGE);\n    }\n\n    protected void verifyPageUpdateContent(Pair<String, byte[]> pagePropertiesContentPair,\n            final String expectedContentType) throws Exception {\n        //given\n        final SPage sPage = new SPage(\"name\", 10201983L, 2005L, false, \"contentName\");\n        sPage.setId(45L);\n        final byte[] content = IOUtil.zip(getIndexGroovyContentPair(), pagePropertiesContentPair);\n        final SPageWithContent pageContent = new SPageWithContent();\n        pageContent.setContent(content);\n        when(readPersistenceService.selectById(new SelectByIdDescriptor<>(SPageWithContent.class, sPage.getId())))\n                .thenReturn(pageContent);\n\n        //then\n        doAnswer((Answer<Object>) invocation -> {\n\n            final EntityUpdateDescriptor entityUpdateDescriptor = (EntityUpdateDescriptor) invocation\n                    .getArguments()[0];\n            assertThat(entityUpdateDescriptor.getFields()).containsOnly(\n                    entry(\"description\", \"mypage description\"),\n                    entry(\"contentName\", \"contentName\"),\n                    entry(\"displayName\", \"mypage display name\"),\n                    entry(\"contentType\", expectedContentType));\n            return null;\n        }).when(pageServiceImpl).updatePage(any(EntityUpdateDescriptor.class), any(AbstractSPage.class));\n\n        //when\n        pageServiceImpl.updatePageContent(sPage.getId(), content, \"contentName\");\n    }\n\n    @Test\n    public void should_update_page_content_hash_of_provided_page() throws Exception {\n        SPage page = new SPage(\"userPage\", 10201983L, 2005L, false, \"contentName\");\n        page.setId(55L);\n        page.setProvided(true);\n\n        byte[] content = IOUtil.zip(getIndexGroovyContentPair(),\n                getPagePropertiesContentPair(\"contentType=\" + SContentType.PAGE));\n        SPageWithContent pageWithContent = new SPageWithContent(page, content);\n        pageWithContent.setProvided(true);\n\n        when(readPersistenceService.selectById(new SelectByIdDescriptor<>(SPageWithContent.class, page.getId())))\n                .thenReturn(pageWithContent);\n        doReturn(pageWithContent).when(pageServiceImpl).updatePage(entityUpdateDescriptorCaptor.capture(),\n                eq(pageWithContent));\n\n        pageServiceImpl.updatePageContent(page.getId(), content, \"contentName\");\n\n        assertThat(entityUpdateDescriptorCaptor.getValue().getFields())\n                .contains(entry(\"pageHash\", DigestUtils.md5DigestAsHex(content)));\n    }\n\n    @Test\n    public void should_not_update_page_content_hash_of_not_provided_pages() throws Exception {\n        SPage page = new SPage(\"userPage\", 10201983L, 2005L, false, \"contentName\");\n        page.setId(55L);\n        final byte[] zip = IOUtil.zip(getIndexGroovyContentPair(),\n                getPagePropertiesContentPair(\"contentType=\" + SContentType.PAGE));\n        final SPageWithContent pageWithContent = new SPageWithContent();\n        pageWithContent.setContent(zip);\n        when(readPersistenceService.selectById(new SelectByIdDescriptor<>(SPageWithContent.class, page.getId())))\n                .thenReturn(pageWithContent);\n        doReturn(page).when(pageServiceImpl).updatePage(entityUpdateDescriptorCaptor.capture(), any());\n\n        pageServiceImpl.updatePageContent(page.getId(), zip, \"contentName\");\n\n        assertThat(entityUpdateDescriptorCaptor.getValue().getFields()).doesNotContainKey(\"pageHash\");\n    }\n\n    @Test\n    public void deletePage_should_execute_listener() throws Exception {\n        final SPage page = new SPage(\"name\", 10201983L, 2005L, false, \"contentName\");\n        when(readPersistenceService.selectById(new SelectByIdDescriptor<>(SPage.class, 1983L)))\n                .thenReturn(page);\n        doReturn(new Properties()).when(pageServiceImpl).getPreviousPageProperties(page);\n\n        pageServiceImpl.deletePage(1983L);\n\n        verify(apiExtensionPageServiceListener).pageDeleted(page);\n    }\n\n    @Test\n    public void deletePage_on_non_removable_page_should_raise_exception() throws Exception {\n        //given\n        final SPage page = new SPage(\"a page name\", 10201983L, 2005L, false, \"contentName\");\n        page.setRemovable(false);\n        when(readPersistenceService.selectById(any())).thenReturn(page);\n\n        //when\n        String exceptionMessage = assertThrows(\"Not the right exception\", SObjectModificationException.class,\n                () -> pageServiceImpl.deletePage(1983L)).getMessage();\n\n        //then\n        assertThat(exceptionMessage).contains(\"The page 'a page name' cannot be deleted because it is non-removable\");\n    }\n\n    @Test\n    public void update_non_editable_should_raise_exception_when_session_is_not_system() throws Exception {\n        //given\n        final SPage page = new SPage(\"a page name\", 10201983L, 2005L, /* boolean provided: */false, \"contentName\");\n        page.setEditable(false);\n        when(readPersistenceService.selectById(any())).thenReturn(page);\n        doReturn(false).when(pageServiceImpl).isSystemSession();\n\n        //when\n        String exceptionMessage = assertThrows(\"Not the right exception\", SObjectModificationException.class,\n                () -> pageServiceImpl.updatePage(page.getId(), entityUpdateDescriptor)).getMessage();\n\n        //then\n        assertThat(exceptionMessage)\n                .contains(\"The page 'a page name' cannot be modified because it is not modifiable\");\n    }\n\n    @Test\n    public void update_non_editable_page_should_update_content_when_session_is_system() throws Exception {\n        //given\n        final SPage page = new SPage(\"the page\", 999888777L, 2021L, /* boolean provided: */true, \"contentName\");\n        page.setEditable(false);\n        when(readPersistenceService.selectById(any())).thenReturn(page);\n        doReturn(true).when(pageServiceImpl).isSystemSession();\n\n        //when\n        pageServiceImpl.updatePage(page.getId(), entityUpdateDescriptor);\n\n        //then\n        verify(recorder).recordUpdate(any(), any());\n    }\n\n    @Test\n    public void add_page_should_call_permission_service_with_the_parsed_properties() throws Exception {\n        // given:\n        SPage page = new SPage();\n        page.setName(\"custompage_test\");\n        page.setDisplayName(\"My Custom Page\");\n        byte[] zip = zip(\n                file(\"page.properties\", \"name=custompage_test\\ncontentType=page\"),\n                file(\"resources/index.html\", \"someContent\"));\n        Properties properties = new Properties();\n        properties.put(\"name\", \"custompage_test\");\n        properties.put(\"contentType\", \"page\");\n\n        // when:\n        pageServiceImpl.addPage(page, zip);\n\n        // then:\n        verify(permissionService).addPermissions(page.getName(), properties);\n    }\n\n    @Test\n    public void add_page_from_content_should_call_permission_service_with_the_parsed_properties() throws Exception {\n        // given:\n        SPage page = new SPage();\n        page.setName(\"custompage_test\");\n        page.setDisplayName(\"My Custom Page\");\n        byte[] zip = zip(\n                file(\"page.properties\", \"name=custompage_test\\ncontentType=page\\ndisplayName=My Custom page\"),\n                file(\"resources/index.html\", \"someContent\"));\n        Properties properties = new Properties();\n        properties.put(\"name\", \"custompage_test\");\n        properties.put(\"contentType\", \"page\");\n        properties.put(\"displayName\", \"My Custom page\");\n\n        // when:\n        pageServiceImpl.addPage(page, zip);\n\n        // then:\n        verify(permissionService).addPermissions(page.getName(), properties);\n    }\n\n    @Test\n    public void update_page_should_call_permission_service_with_the_parsed_properties() throws Exception {\n        // given:\n        SPage page = new SPage();\n        page.setId(174L);\n        page.setName(\"custompage_test\");\n        page.setDisplayName(\"My Custom Page\");\n        byte[] zip = zip(\n                file(\"page.properties\", \"name=custompage_test\\ncontentType=page\\ndisplayName=My Custom page\"),\n                file(\"resources/index.html\", \"someContent\"));\n        SPageWithContent sPageContent = new SPageWithContent();\n        sPageContent.setContent(zip);\n\n        Properties properties = new Properties();\n        properties.put(\"name\", \"custompage_test\");\n        properties.put(\"contentType\", \"page\");\n        properties.put(\"displayName\", \"My Custom page\");\n\n        when(readPersistenceService.selectById(new SelectByIdDescriptor<>(SPageWithContent.class, 174L)))\n                .thenReturn(sPageContent);\n\n        // when:\n        pageServiceImpl.updatePageContent(174L, zip, \"myNewContent.zip\");\n\n        // then:\n        verify(permissionService).addPermissions(page.getName(), properties);\n    }\n\n    @Test\n    public void updatePageContent_should_not_try_to_remove_permissions_if_previous_page_content_was_empty()\n            throws Exception {\n        final SPage page = new SPage(\"name\", 10201983L, 2005L, false, \"contentName\");\n        page.setId(45L);\n        final SPageWithContent previousPageContent = new SPageWithContent(page, new byte[] {});\n        when(readPersistenceService.selectById(new SelectByIdDescriptor<>(SPageWithContent.class, page.getId())))\n                .thenReturn(previousPageContent);\n\n        final byte[] newContent = IOUtil.zip(getIndexGroovyContentPair(),\n                getPagePropertiesContentPair(\"contentType=\" + SContentType.PAGE));\n\n        pageServiceImpl.updatePageContent(page.getId(), newContent, \"contentName\");\n\n        verify(permissionService, never()).removePermissions(any());\n    }\n\n    @Test\n    public void updatePageContent_should_not_try_to_remove_permissions_if_previous_page_content_was_null()\n            throws Exception {\n        final SPage page = new SPage(\"name\", 10201983L, 2005L, false, \"contentName\");\n        page.setId(45L);\n        final SPageWithContent previousPageContent = new SPageWithContent(page, null);\n        when(readPersistenceService.selectById(new SelectByIdDescriptor<>(SPageWithContent.class, page.getId())))\n                .thenReturn(previousPageContent);\n\n        final byte[] newContent = IOUtil.zip(getIndexGroovyContentPair(),\n                getPagePropertiesContentPair(\"contentType=\" + SContentType.PAGE));\n\n        pageServiceImpl.updatePageContent(page.getId(), newContent, \"contentName\");\n\n        verify(permissionService, never()).removePermissions(any());\n\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-page/src/test/java/org/bonitasoft/engine/page/impl/SPageAssert.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page.impl;\n\nimport static java.lang.String.format;\n\nimport org.assertj.core.api.AbstractAssert;\nimport org.bonitasoft.engine.page.SPage;\n\n/**\n * {@link SPage} specific assertions - Generated by CustomAssertionGenerator.\n */\npublic class SPageAssert extends AbstractAssert<SPageAssert, SPage> {\n\n    /**\n     * Creates a new </code>{@link SPageAssert}</code> to make assertions on actual SPage.\n     *\n     * @param actual the SPage we want to make assertions on.\n     */\n    public SPageAssert(SPage actual) {\n        super(actual, SPageAssert.class);\n    }\n\n    /**\n     * An entry point for SPageAssert to follow AssertJ standard <code>assertThat()</code> statements.<br>\n     * With a static import, one's can write directly : <code>assertThat(mySPage)</code> and get specific assertion with\n     * code completion.\n     *\n     * @param actual the SPage we want to make assertions on.\n     * @return a new </code>{@link SPageAssert}</code>\n     */\n    public static SPageAssert assertThat(SPage actual) {\n        return new SPageAssert(actual);\n    }\n\n    /**\n     * Verifies that the actual SPage's contentName is equal to the given one.\n     *\n     * @param contentName the given contentName to compare the actual SPage's contentName to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPage's contentName is not equal to the given one.\n     */\n    public SPageAssert hasContentName(String contentName) {\n        // check that actual SPage we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> contentName to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                contentName, actual.getContentName());\n\n        // check\n        if (!actual.getContentName().equals(contentName)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual SPage's contentType is equal to the given one.\n     *\n     * @param contentType the given contentType to compare the actual SPage's contentType to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPage's contentType is not equal to the given one.\n     */\n    public SPageAssert hasContentType(String contentType) {\n        // check that actual SPage we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> contentType to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                contentType, actual.getContentType());\n\n        // check\n        if (!actual.getContentType().equals(contentType)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual SPage's description is equal to the given one.\n     *\n     * @param description the given description to compare the actual SPage's description to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPage's description is not equal to the given one.\n     */\n    public SPageAssert hasDescription(String description) {\n        // check that actual SPage we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> description to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                description, actual.getDescription());\n\n        // check\n        if (!actual.getDescription().equals(description)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual SPage's displayName is equal to the given one.\n     *\n     * @param displayName the given displayName to compare the actual SPage's displayName to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPage's displayName is not equal to the given one.\n     */\n    public SPageAssert hasDisplayName(String displayName) {\n        // check that actual SPage we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> displayName to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                displayName, actual.getDisplayName());\n\n        // check\n        if (!actual.getDisplayName().equals(displayName)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual SPage's installationDate is equal to the given one.\n     *\n     * @param installationDate the given installationDate to compare the actual SPage's installationDate to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPage's installationDate is not equal to the given one.\n     */\n    public SPageAssert hasInstallationDate(long installationDate) {\n        // check that actual SPage we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> installationDate to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                installationDate, actual.getInstallationDate());\n\n        // check\n        if (actual.getInstallationDate() != installationDate) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual SPage's installedBy is equal to the given one.\n     *\n     * @param installedBy the given installedBy to compare the actual SPage's installedBy to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPage's installedBy is not equal to the given one.\n     */\n    public SPageAssert hasInstalledBy(long installedBy) {\n        // check that actual SPage we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> installedBy to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                installedBy, actual.getInstalledBy());\n\n        // check\n        if (actual.getInstalledBy() != installedBy) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual SPage's lastModificationDate is equal to the given one.\n     *\n     * @param lastModificationDate the given lastModificationDate to compare the actual SPage's lastModificationDate to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPage's lastModificationDate is not equal to the given one.\n     */\n    public SPageAssert hasLastModificationDate(long lastModificationDate) {\n        // check that actual SPage we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> lastModificationDate to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                lastModificationDate, actual.getLastModificationDate());\n\n        // check\n        if (actual.getLastModificationDate() != lastModificationDate) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual SPage's lastUpdatedBy is equal to the given one.\n     *\n     * @param lastUpdatedBy the given lastUpdatedBy to compare the actual SPage's lastUpdatedBy to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPage's lastUpdatedBy is not equal to the given one.\n     */\n    public SPageAssert hasLastUpdatedBy(long lastUpdatedBy) {\n        // check that actual SPage we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> lastUpdatedBy to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                lastUpdatedBy, actual.getLastUpdatedBy());\n\n        // check\n        if (actual.getLastUpdatedBy() != lastUpdatedBy) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual SPage's name is equal to the given one.\n     *\n     * @param name the given name to compare the actual SPage's name to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPage's name is not equal to the given one.\n     */\n    public SPageAssert hasName(String name) {\n        // check that actual SPage we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> name to be:\\n  <%s>\\n but was:\\n  <%s>\", actual, name,\n                actual.getName());\n\n        // check\n        if (!actual.getName().equals(name)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual SPage's processDefinitionId is equal to the given one.\n     *\n     * @param processDefinitionId the given processDefinitionId to compare the actual SPage's processDefinitionId to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPage's processDefinitionId is not equal to the given one.\n     */\n    public SPageAssert hasProcessDefinitionId(long processDefinitionId) {\n        // check that actual SPage we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> processDefinitionId to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                processDefinitionId, actual.getProcessDefinitionId());\n\n        // check\n        if (actual.getProcessDefinitionId() != processDefinitionId) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual SPage is provided.\n     *\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPage is not provided.\n     */\n    public SPageAssert isProvided() {\n        // check that actual SPage we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"Expected actual SPage to be provided but was not.\", actual);\n\n        // check\n        if (!actual.isProvided())\n            throw new AssertionError(errorMessage);\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual SPage is not provided.\n     *\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPage is provided.\n     */\n    public SPageAssert isNotProvided() {\n        // check that actual SPage we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"Expected actual SPage not to be provided but was.\", actual);\n\n        // check\n        if (actual.isProvided())\n            throw new AssertionError(errorMessage);\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-page/src/test/java/org/bonitasoft/engine/page/impl/SPageImplAssert.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page.impl;\n\nimport static java.lang.String.format;\n\nimport org.assertj.core.api.AbstractAssert;\nimport org.bonitasoft.engine.page.SPage;\n\n/**\n * {@link SPage} specific assertions - Generated by CustomAssertionGenerator.\n */\npublic class SPageImplAssert extends AbstractAssert<SPageImplAssert, SPage> {\n\n    /**\n     * Creates a new </code>{@link SPageImplAssert}</code> to make assertions on actual SPageImpl.\n     *\n     * @param actual the SPageImpl we want to make assertions on.\n     */\n    public SPageImplAssert(SPage actual) {\n        super(actual, SPageImplAssert.class);\n    }\n\n    /**\n     * An entry point for SPageImplAssert to follow AssertJ standard <code>assertThat()</code> statements.<br>\n     * With a static import, one's can write directly : <code>assertThat(mySPageImpl)</code> and get specific assertion\n     * with code completion.\n     *\n     * @param actual the SPageImpl we want to make assertions on.\n     * @return a new </code>{@link SPageImplAssert}</code>\n     */\n    public static SPageImplAssert assertThat(SPage actual) {\n        return new SPageImplAssert(actual);\n    }\n\n    /**\n     * Verifies that the actual SPageImpl's contentName is equal to the given one.\n     *\n     * @param contentName the given contentName to compare the actual SPageImpl's contentName to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPageImpl's contentName is not equal to the given one.\n     */\n    public SPageImplAssert hasContentName(String contentName) {\n        // check that actual SPageImpl we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> contentName to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                contentName, actual.getContentName());\n\n        // check\n        if (!actual.getContentName().equals(contentName)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual SPageImpl's contentType is equal to the given one.\n     *\n     * @param contentType the given contentType to compare the actual SPageImpl's contentType to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPageImpl's contentType is not equal to the given one.\n     */\n    public SPageImplAssert hasContentType(String contentType) {\n        // check that actual SPageImpl we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> contentType to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                contentType, actual.getContentType());\n\n        // check\n        if (!actual.getContentType().equals(contentType)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual SPageImpl's description is equal to the given one.\n     *\n     * @param description the given description to compare the actual SPageImpl's description to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPageImpl's description is not equal to the given one.\n     */\n    public SPageImplAssert hasDescription(String description) {\n        // check that actual SPageImpl we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> description to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                description, actual.getDescription());\n\n        // check\n        if (!actual.getDescription().equals(description)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual SPageImpl's displayName is equal to the given one.\n     *\n     * @param displayName the given displayName to compare the actual SPageImpl's displayName to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPageImpl's displayName is not equal to the given one.\n     */\n    public SPageImplAssert hasDisplayName(String displayName) {\n        // check that actual SPageImpl we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> displayName to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                displayName, actual.getDisplayName());\n\n        // check\n        if (!actual.getDisplayName().equals(displayName)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual SPageImpl's id is equal to the given one.\n     *\n     * @param id the given id to compare the actual SPageImpl's id to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPageImpl's id is not equal to the given one.\n     */\n    public SPageImplAssert hasId(long id) {\n        // check that actual SPageImpl we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> id to be:\\n  <%s>\\n but was:\\n  <%s>\", actual, id,\n                actual.getId());\n\n        // check\n        if (actual.getId() != id) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual SPageImpl's installationDate is equal to the given one.\n     *\n     * @param installationDate the given installationDate to compare the actual SPageImpl's installationDate to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPageImpl's installationDate is not equal to the given one.\n     */\n    public SPageImplAssert hasInstallationDate(long installationDate) {\n        // check that actual SPageImpl we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> installationDate to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                installationDate, actual.getInstallationDate());\n\n        // check\n        if (actual.getInstallationDate() != installationDate) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual SPageImpl's installedBy is equal to the given one.\n     *\n     * @param installedBy the given installedBy to compare the actual SPageImpl's installedBy to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPageImpl's installedBy is not equal to the given one.\n     */\n    public SPageImplAssert hasInstalledBy(long installedBy) {\n        // check that actual SPageImpl we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> installedBy to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                installedBy, actual.getInstalledBy());\n\n        // check\n        if (actual.getInstalledBy() != installedBy) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual SPageImpl's lastModificationDate is equal to the given one.\n     *\n     * @param lastModificationDate the given lastModificationDate to compare the actual SPageImpl's lastModificationDate\n     *        to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPageImpl's lastModificationDate is not equal to the given one.\n     */\n    public SPageImplAssert hasLastModificationDate(long lastModificationDate) {\n        // check that actual SPageImpl we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> lastModificationDate to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                lastModificationDate, actual.getLastModificationDate());\n\n        // check\n        if (actual.getLastModificationDate() != lastModificationDate) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual SPageImpl's lastUpdatedBy is equal to the given one.\n     *\n     * @param lastUpdatedBy the given lastUpdatedBy to compare the actual SPageImpl's lastUpdatedBy to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPageImpl's lastUpdatedBy is not equal to the given one.\n     */\n    public SPageImplAssert hasLastUpdatedBy(long lastUpdatedBy) {\n        // check that actual SPageImpl we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> lastUpdatedBy to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                lastUpdatedBy, actual.getLastUpdatedBy());\n\n        // check\n        if (actual.getLastUpdatedBy() != lastUpdatedBy) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual SPageImpl's name is equal to the given one.\n     *\n     * @param name the given name to compare the actual SPageImpl's name to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPageImpl's name is not equal to the given one.\n     */\n    public SPageImplAssert hasName(String name) {\n        // check that actual SPageImpl we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> name to be:\\n  <%s>\\n but was:\\n  <%s>\", actual, name,\n                actual.getName());\n\n        // check\n        if (!actual.getName().equals(name)) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual SPageImpl's processDefinitionId is equal to the given one.\n     *\n     * @param processDefinitionId the given processDefinitionId to compare the actual SPageImpl's processDefinitionId\n     *        to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPageImpl's processDefinitionId is not equal to the given one.\n     */\n    public SPageImplAssert hasProcessDefinitionId(long processDefinitionId) {\n        // check that actual SPageImpl we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"\\nExpected <%s> processDefinitionId to be:\\n  <%s>\\n but was:\\n  <%s>\", actual,\n                processDefinitionId, actual.getProcessDefinitionId());\n\n        // check\n        if (actual.getProcessDefinitionId() != processDefinitionId) {\n            throw new AssertionError(errorMessage);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual SPageImpl is provided.\n     *\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPageImpl is not provided.\n     */\n    public SPageImplAssert isProvided() {\n        // check that actual SPageImpl we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"Expected actual SPageImpl to be provided but was not.\", actual);\n\n        // check\n        if (!actual.isProvided())\n            throw new AssertionError(errorMessage);\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual SPageImpl is not provided.\n     *\n     * @return this assertion object.\n     * @throws AssertionError - if the actual SPageImpl is provided.\n     */\n    public SPageImplAssert isNotProvided() {\n        // check that actual SPageImpl we want to make assertions on is not null.\n        isNotNull();\n\n        // we overrides the default error message with a more explicit one\n        String errorMessage = format(\"Expected actual SPageImpl not to be provided but was.\", actual);\n\n        // check\n        if (actual.isProvided())\n            throw new AssertionError(errorMessage);\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-page/src/test/java/org/bonitasoft/engine/page/impl/SPageImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.bonitasoft.engine.page.SContentType;\nimport org.bonitasoft.engine.page.SPage;\nimport org.junit.Test;\n\npublic class SPageImplTest {\n\n    private static final String DESCRIPTION = \"description\";\n\n    private static final boolean PROVIDED = true;\n\n    private static final String NAME = \"name\";\n\n    private static final String DISPLAY_NAME = \"display name\";\n\n    public static final long PROCESS_DEFINITION_ID = 456789L;\n    public static final long INSTALLATION_DATE = 1L;\n    public static final long LAST_MODIFICATION_DATE = 2L;\n    public static final String CONTENT_ZIP = \"content.zip\";\n    private static final long LAST_UPDATED_BY = 3L;\n    public static final long INSTALLED_BY = 4L;\n    public static final long TENANT_ID = 5L;\n    public static final long ID = 6L;\n\n    @Test\n    public void should_set_all_fields() {\n        //given\n        final SPage sPage = new SPage(NAME, INSTALLATION_DATE, INSTALLED_BY, PROVIDED, CONTENT_ZIP);\n        sPage.setId(ID);\n        sPage.setDisplayName(DISPLAY_NAME);\n        sPage.setDescription(DESCRIPTION);\n        sPage.setLastModificationDate(LAST_MODIFICATION_DATE);\n        sPage.setLastUpdatedBy(LAST_UPDATED_BY);\n\n        //when then\n        SPageImplAssert.assertThat(sPage)\n                .hasName(NAME)\n                .hasDisplayName(DISPLAY_NAME)\n                .isProvided()\n                .hasDescription(DESCRIPTION)\n                .hasInstallationDate(INSTALLATION_DATE)\n                .hasLastModificationDate(LAST_MODIFICATION_DATE)\n                .hasContentName(CONTENT_ZIP)\n                .hasContentType(SContentType.PAGE)\n                .hasLastUpdatedBy(LAST_UPDATED_BY)\n                .hasInstalledBy(INSTALLED_BY)\n                .hasId(ID);\n\n    }\n\n    @Test\n    public void should_set_all_fields_from_sPage() {\n        //given\n        final SPage sPage = new SPage(NAME, DESCRIPTION, DISPLAY_NAME, INSTALLATION_DATE, INSTALLED_BY, PROVIDED,\n                LAST_MODIFICATION_DATE,\n                LAST_UPDATED_BY,\n                CONTENT_ZIP);\n\n        //when\n        SPage sPage2 = new SPage(sPage);\n\n        //then\n        assertThat(sPage2).isEqualToComparingFieldByField(sPage);\n    }\n\n    @Test\n    public void should_set_content_type_and_process_definition() {\n        //given\n        final SPage sPage = new SPage(NAME, DESCRIPTION, DISPLAY_NAME, INSTALLATION_DATE, INSTALLED_BY, PROVIDED,\n                LAST_MODIFICATION_DATE,\n                LAST_UPDATED_BY,\n                CONTENT_ZIP);\n\n        //when\n        sPage.setProcessDefinitionId(PROCESS_DEFINITION_ID);\n        sPage.setContentType(SContentType.FORM);\n\n        //then\n        SPageImplAssert.assertThat(sPage)\n                .hasProcessDefinitionId(PROCESS_DEFINITION_ID)\n                .hasContentType(SContentType.FORM);\n        assertThat(sPage.toString()).contains(String.valueOf(PROCESS_DEFINITION_ID)).contains(SContentType.FORM);\n\n    }\n}\n"
  },
  {
    "path": "services/bonita-page/src/test/java/org/bonitasoft/engine/page/impl/SPageMappingImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.Arrays;\n\nimport org.bonitasoft.engine.page.SPageMapping;\nimport org.junit.Test;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SPageMappingImplTest {\n\n    @Test\n    public void testEquals() {\n        SPageMapping sPageMapping1 = new SPageMapping();\n        SPageMapping sPageMapping2 = new SPageMapping();\n\n        setValues(sPageMapping1, sPageMapping2);\n\n        assertThat(sPageMapping1).isEqualTo(sPageMapping2);\n    }\n\n    @Test\n    public void testHashCode() {\n        SPageMapping sPageMapping1 = new SPageMapping();\n        SPageMapping sPageMapping2 = new SPageMapping();\n\n        setValues(sPageMapping1, sPageMapping2);\n\n        assertThat(sPageMapping1.hashCode()).isEqualTo(sPageMapping2.hashCode());\n\n    }\n\n    @Test\n    public void testToString() {\n        SPageMapping sPageMapping1 = new SPageMapping();\n        SPageMapping sPageMapping2 = new SPageMapping();\n\n        setValues(sPageMapping1, sPageMapping2);\n\n        assertThat(sPageMapping1.toString()).isEqualTo(sPageMapping2.toString());\n    }\n\n    void setValues(SPageMapping sPageMapping1, SPageMapping sPageMapping2) {\n        sPageMapping1.setKey(\"myKey\");\n        sPageMapping1.setUrlAdapter(\"urlAdapter\");\n        sPageMapping1.setPageAuthorizRules(\"net.comp.Rule\");\n        sPageMapping1.setUrl(\"myUrl\");\n        sPageMapping1.setPageId(11L);\n        sPageMapping2.setKey(\"myKey\");\n        sPageMapping2.setUrlAdapter(\"urlAdapter\");\n        sPageMapping2.setPageAuthorizRules(\"net.comp.Rule\");\n        sPageMapping2.setUrl(\"myUrl\");\n        sPageMapping2.setPageId(11L);\n    }\n\n    @Test\n    public void parseRulesShouldSetListOfRules() {\n        SPageMapping mapping = new SPageMapping();\n        mapping.setPageAuthorizRules(\",,toto,titi,tutu,\");\n        assertThat(mapping.getPageAuthorizationRules()).containsExactly(\"toto\", \"titi\", \"tutu\");\n    }\n\n    @Test\n    public void buildRulesAsStringShouldConcatRulesWithComma() {\n        SPageMapping mapping = new SPageMapping();\n        mapping.setPageAuthorizationRules(Arrays.asList(\"toto\", \"titi\", \"tata\"));\n        assertThat(mapping.getPageAuthorizRules()).contains(\"toto,titi,tata\");\n    }\n}\n"
  },
  {
    "path": "services/bonita-page/src/test/java/org/bonitasoft/engine/page/impl/SPageMappingServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.page.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.anyMap;\nimport static org.mockito.Mockito.anyString;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.doThrow;\nimport static org.mockito.Mockito.eq;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.nullable;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.commons.exceptions.SDeletionException;\nimport org.bonitasoft.engine.commons.exceptions.SExecutionException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectCreationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.page.AuthorizationRule;\nimport org.bonitasoft.engine.page.SAuthorizationException;\nimport org.bonitasoft.engine.page.SPageMapping;\nimport org.bonitasoft.engine.page.SPageURL;\nimport org.bonitasoft.engine.page.URLAdapter;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Baptiste Mesta\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class SPageMappingServiceImplTest {\n\n    public static final long PAGE_ID = 2812l;\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Mock\n    private Recorder recorder;\n\n    @Mock\n    private ReadPersistenceService persistenceService;\n\n    @Mock\n    private SessionService sessionService;\n\n    @Mock\n    private ReadSessionAccessor readSessionAccessor;\n\n    @InjectMocks\n    private PageMappingServiceImpl pageMappingService;\n\n    @Before\n    public void before() {\n        URLAdapter urlAdapter = new URLAdapter() {\n\n            @Override\n            public String adapt(String url, String key, Map<String, Serializable> context) {\n                return url + \"_adapted_\" + context.size();\n            }\n\n            @Override\n            public String getId() {\n                return \"testAdapter\";\n            }\n        };\n        pageMappingService.setURLAdapters(Collections.singletonList(urlAdapter));\n    }\n\n    @Test\n    public void should_create_return_the_created_element() throws Exception {\n        //given\n        //when\n        final List<String> authorizationRules = new ArrayList<>(2);\n        authorizationRules.add(\"toto\");\n        authorizationRules.add(\"titi\");\n        SPageMapping sPageMapping = pageMappingService.create(\"theKey\", PAGE_ID, authorizationRules);\n        //then\n        assertThat(sPageMapping).isNotNull();\n        assertThat(sPageMapping.getKey()).isEqualTo(\"theKey\");\n        assertThat(sPageMapping.getPageId()).isEqualTo(PAGE_ID);\n        assertThat(sPageMapping.getUrl()).isEqualTo(null);\n        assertThat(sPageMapping.getPageAuthorizationRules()).isEqualTo(authorizationRules);\n\n        final ArgumentCaptor<InsertRecord> insertRecord = ArgumentCaptor.forClass(InsertRecord.class);\n        verify(recorder).recordInsert(insertRecord.capture(), eq(\"PAGE_MAPPING\"));\n\n        assertThat(insertRecord.getValue().getEntity()).isEqualTo(sPageMapping);\n    }\n\n    @Test\n    public void should_create_throw_creation_exception() throws Exception {\n        //given\n        doThrow(SRecorderException.class).when(recorder).recordInsert(any(InsertRecord.class), anyString());\n        //when\n        expectedException.expect(SObjectCreationException.class);\n        pageMappingService.create(\"theKey\", PAGE_ID, Collections.<String> emptyList());\n    }\n\n    @Test\n    public void should_create_return_external_mapping() throws Exception {\n        //given\n        //when\n        SPageMapping sPageMapping = pageMappingService.create(\"theKey\", \"http://www.mycompagny.com/aResource/page.html\",\n                \"myAdapter\",\n                Collections.<String> emptyList());\n        //then\n        assertThat(sPageMapping).isNotNull();\n        assertThat(sPageMapping.getKey()).isEqualTo(\"theKey\");\n        assertThat(sPageMapping.getUrl()).isEqualTo(\"http://www.mycompagny.com/aResource/page.html\");\n        assertThat(sPageMapping.getPageId()).isNull();\n        assertThat(sPageMapping.getUrlAdapter()).isEqualTo(\"myAdapter\");\n\n        final ArgumentCaptor<InsertRecord> insertRecord = ArgumentCaptor.forClass(InsertRecord.class);\n        verify(recorder).recordInsert(insertRecord.capture(), eq(\"PAGE_MAPPING\"));\n\n        assertThat(insertRecord.getValue().getEntity()).isEqualTo(sPageMapping);\n    }\n\n    @Test\n    public void should_get_return_the_object() throws Exception {\n        SPageMapping pageMapping = new SPageMapping();\n        pageMapping.setKey(\"myKey\");\n        doReturn(pageMapping).when(persistenceService).selectOne(any(SelectOneDescriptor.class));\n\n        SPageMapping pageHavingMyKey = pageMappingService.get(\"myKey\");\n\n        assertThat(pageHavingMyKey).isEqualTo(pageMapping);\n    }\n\n    @Test\n    public void should_get_throw_not_found() throws Exception {\n        expectedException.expect(SObjectNotFoundException.class);\n        pageMappingService.get(\"unknown\");\n    }\n\n    @Test\n    public void should_delete_call_the_recorder() throws Exception {\n        //given\n        SPageMapping sPageMapping = new SPageMapping();\n\n        //when\n        pageMappingService.delete(sPageMapping);\n\n        //then\n        final ArgumentCaptor<DeleteRecord> deleteRecord = ArgumentCaptor.forClass(DeleteRecord.class);\n        verify(recorder).recordDelete(deleteRecord.capture(), eq(\"PAGE_MAPPING\"));\n        assertThat(deleteRecord.getValue().getEntity()).isEqualTo(sPageMapping);\n    }\n\n    @Test\n    public void should_delete_throw_exception() throws Exception {\n        //given\n        doThrow(SRecorderException.class).when(recorder).recordDelete(any(DeleteRecord.class), anyString());\n        //when\n        expectedException.expect(SDeletionException.class);\n        pageMappingService.delete(new SPageMapping());\n    }\n\n    @Test\n    public void should_update_pageId_set_null_on_url() throws Exception {\n        //given\n        SPageMapping pageMapping = new SPageMapping();\n        pageMapping.setKey(\"myKey\");\n\n        //when\n        pageMappingService.update(pageMapping, 124l);\n\n        //then\n        ArgumentCaptor<UpdateRecord> updateRecord = ArgumentCaptor.forClass(UpdateRecord.class);\n        verify(recorder).recordUpdate(updateRecord.capture(), eq(\"PAGE_MAPPING\"));\n        assertThat(updateRecord.getValue().getEntity()).isEqualTo(pageMapping);\n        assertThat(updateRecord.getValue().getFields()).contains(entry(\"pageId\", 124l), entry(\"url\", null),\n                entry(\"urlAdapter\", null),\n                entry(\"lastUpdatedBy\", 0L));\n\n    }\n\n    @Test\n    public void should_update_throw_exception() throws Exception {\n        //given\n        SPageMapping pageMapping = new SPageMapping();\n        pageMapping.setKey(\"myKey\");\n        doThrow(SRecorderException.class).when(recorder).recordUpdate(any(UpdateRecord.class), anyString());\n        //when\n        expectedException.expect(SObjectModificationException.class);\n        pageMappingService.update(pageMapping, 124l);\n    }\n\n    @Test\n    public void should_update_url_set_null_on_pageId() throws Exception {\n        //given\n        SPageMapping pageMapping = new SPageMapping();\n        pageMapping.setKey(\"myKey\");\n\n        //when\n        pageMappingService.update(pageMapping, \"myNewUrl\", \"urlAdapter\");\n\n        //then\n        ArgumentCaptor<UpdateRecord> updateRecord = ArgumentCaptor.forClass(UpdateRecord.class);\n        verify(recorder).recordUpdate(updateRecord.capture(), eq(\"PAGE_MAPPING\"));\n        assertThat(updateRecord.getValue().getEntity()).isEqualTo(pageMapping);\n        assertThat(updateRecord.getValue().getFields()).contains(entry(\"pageId\", null), entry(\"url\", \"myNewUrl\"),\n                entry(\"urlAdapter\", \"urlAdapter\"),\n                entry(\"lastUpdatedBy\", 0L));\n    }\n\n    @Test\n    public void should_resolveUrl_return_page_id() throws Exception {\n        SPageMapping pageMapping = new SPageMapping();\n        pageMapping.setPageId(56l);\n\n        SPageURL sPageURL = pageMappingService.resolvePageURL(pageMapping,\n                Collections.<String, Serializable> emptyMap(), true);\n\n        assertThat(sPageURL.getPageId()).isEqualTo(56l);\n        assertThat(sPageURL.getUrl()).isEqualTo(null);\n    }\n\n    @Test\n    public void should_resolveUrl_return_url_with_no_adapter() throws Exception {\n        SPageMapping pageMapping = new SPageMapping();\n        pageMapping.setUrl(\"theUrl\");\n\n        SPageURL sPageURL = pageMappingService.resolvePageURL(pageMapping,\n                Collections.<String, Serializable> emptyMap(), true);\n\n        assertThat(sPageURL.getPageId()).isEqualTo(null);\n        assertThat(sPageURL.getUrl()).isEqualTo(\"theUrl\");\n    }\n\n    @Test\n    public void should_resolveUrl_return_call_adapter_with_no_url() throws Exception {\n        SPageMapping pageMapping = new SPageMapping();\n        pageMapping.setUrl(null);\n        pageMapping.setUrlAdapter(\"testAdapter\");\n\n        SPageURL sPageURL = pageMappingService.resolvePageURL(pageMapping,\n                Collections.<String, Serializable> emptyMap(), true);\n\n        assertThat(sPageURL.getPageId()).isEqualTo(null);\n        assertThat(sPageURL.getUrl()).isEqualTo(\"null_adapted_0\");\n    }\n\n    @Test\n    public void should_resolveUrl_return_url_should_execute_adapter() throws Exception {\n        SPageMapping pageMapping = new SPageMapping();\n        pageMapping.setUrl(\"theUrl\");\n        pageMapping.setUrlAdapter(\"testAdapter\");\n\n        SPageURL sPageURL = pageMappingService.resolvePageURL(pageMapping,\n                Collections.<String, Serializable> singletonMap(\"test\", \"test\"), true);\n\n        assertThat(sPageURL.getPageId()).isEqualTo(null);\n        assertThat(sPageURL.getUrl()).isEqualTo(\"theUrl_adapted_1\");/* 1 is the map size */\n    }\n\n    @Test(expected = SExecutionException.class)\n    public void should_resolveUrl_with_unknown_adapter() throws Exception {\n        SPageMapping pageMapping = new SPageMapping();\n        pageMapping.setUrl(\"theUrl\");\n        pageMapping.setUrlAdapter(\"unknown\");\n\n        pageMappingService.resolvePageURL(pageMapping, Collections.<String, Serializable> singletonMap(\"test\", \"test\"),\n                true);\n    }\n\n    class ValidRule implements AuthorizationRule {\n\n        private String validRuleKey;\n\n        public ValidRule(String validRuleKey) {\n            this.validRuleKey = validRuleKey;\n        }\n\n        @Override\n        public boolean isAllowed(String key, Map<String, Serializable> context) {\n            return true;\n        }\n\n        @Override\n        public String getId() {\n            return validRuleKey;\n        }\n    }\n\n    class NeverValidRule implements AuthorizationRule {\n\n        private String invalidRuleKey;\n\n        public NeverValidRule(String invalidRuleKey) {\n            this.invalidRuleKey = invalidRuleKey;\n        }\n\n        @Override\n        public boolean isAllowed(String key, Map<String, Serializable> context) {\n            return false;\n        }\n\n        @Override\n        public String getId() {\n            return invalidRuleKey;\n        }\n    }\n\n    @Test\n    public void resolvePageURL_shouldExecuteAuthorizationRule() throws Exception {\n        SPageMapping pageMapping = new SPageMapping();\n        final String validRuleKey = \"validRule\";\n        final List<String> rules = new ArrayList<>(1);\n        rules.add(validRuleKey);\n        pageMapping.setPageAuthorizationRules(rules);\n\n        final AuthorizationRule authorizationRule = spy(new ValidRule(validRuleKey));\n        pageMappingService.setAuthorizationRules(Collections.singletonList(authorizationRule));\n\n        pageMappingService.resolvePageURL(pageMapping, Collections.emptyMap(), true);\n\n        verify(authorizationRule).isAllowed(nullable(String.class), anyMap());\n    }\n\n    @Test\n    public void resolvePageURL_shouldNotExecuteAuthorizationRule() throws Exception {\n        SPageMapping pageMapping = new SPageMapping();\n        final String validRuleKey = \"validRule\";\n        final List<String> rules = new ArrayList<>(1);\n        rules.add(validRuleKey);\n        pageMapping.setPageAuthorizationRules(rules);\n\n        final AuthorizationRule authorizationRule = spy(new ValidRule(validRuleKey));\n        pageMappingService.setAuthorizationRules(Collections.singletonList(authorizationRule));\n\n        pageMappingService.resolvePageURL(pageMapping, Collections.emptyMap(), false);\n\n        verify(authorizationRule, never()).isAllowed(nullable(String.class), anyMap());\n    }\n\n    @Test(expected = SAuthorizationException.class)\n    public void resolvePageURL_shouldThrowExceptionIfAuthorizationRuleInvalid() throws Exception {\n        SPageMapping pageMapping = new SPageMapping();\n        final String invalidRuleKey = \"invalidRuleKey\";\n        final List<String> rules = new ArrayList<>(1);\n        rules.add(invalidRuleKey);\n        pageMapping.setPageAuthorizationRules(rules);\n\n        final AuthorizationRule authorizationRule = new NeverValidRule(invalidRuleKey);\n        pageMappingService.setAuthorizationRules(Arrays.<AuthorizationRule> asList(authorizationRule));\n\n        pageMappingService.resolvePageURL(pageMapping, Collections.<String, Serializable> emptyMap(), true);\n    }\n\n    @Test\n    public void resolvePageURL_shouldAllowAsSoonAsOneRuleIsValid() throws Exception {\n        SPageMapping pageMapping = new SPageMapping();\n        final String invalidRuleKey = \"invalidRule\";\n        final String validKey = \"validRule\";\n        final String invalidRuleKey2 = \"invalidRule2\";\n        final List<String> rules = new ArrayList<>(2);\n        rules.add(invalidRuleKey);\n        rules.add(validKey);\n        pageMapping.setPageAuthorizationRules(rules);\n\n        final NeverValidRule neverValidRule = spy(new NeverValidRule(invalidRuleKey));\n        final ValidRule validRule = spy(new ValidRule(validKey));\n        final NeverValidRule neverValidRule2 = spy(new NeverValidRule(invalidRuleKey2));\n        pageMappingService.setAuthorizationRules(Arrays.<AuthorizationRule> asList(neverValidRule, validRule));\n\n        pageMappingService.resolvePageURL(pageMapping, Collections.emptyMap(), true);\n\n        verify(neverValidRule).isAllowed(nullable(String.class), anyMap());\n        verify(validRule).isAllowed(nullable(String.class), anyMap());\n        verifyNoInteractions(neverValidRule2);\n    }\n\n    @Test\n    public void resolvePageURL_shouldThrowExecutionExceptionIfAuthorizationRuleIsNotKnown() throws Exception {\n        SPageMapping pageMapping = new SPageMapping();\n        final String unknownRuleKey = \"unknownRuleKey\";\n        final List<String> rules = new ArrayList<>(1);\n        rules.add(unknownRuleKey);\n        pageMapping.setPageAuthorizationRules(rules);\n\n        expectedException.expect(SExecutionException.class);\n        expectedException.expectMessage(\"Authorization rule \" + unknownRuleKey);\n\n        pageMappingService.resolvePageURL(pageMapping, Collections.<String, Serializable> emptyMap(), true);\n    }\n}\n"
  },
  {
    "path": "services/bonita-persistence/build.gradle",
    "content": "dependencies {\n    api libs.commonsLang\n    api project(':services:bonita-events')\n    api(libs.hibernateCore) {\n        exclude(module: \"jboss-transaction-api_1.2_spec\")\n        exclude(group: \"javax.activation\") //replaced by jakarta\n    }\n    api libs.jakartaActivation\n    api project(':services:bonita-session')\n    api project(':services:bonita-commons')\n    api project(':services:bonita-lock')\n    api libs.slf4jApi\n\n    implementation(libs.javaxAnnotations)\n\n    compileOnly libs.jakartaTransactionApi\n\n    annotationProcessor libs.lombok\n    compileOnly libs.lombok\n\n    testImplementation libs.mockitoCore\n    testImplementation libs.assertj\n    testImplementation libs.systemRules\n\n    testAnnotationProcessor libs.lombok\n    testImplementation libs.lombok\n\n    testRuntimeOnly libs.logback\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/AbstractSelectDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport java.util.Map;\n\n/**\n * @author Charles Souillard\n * @author Matthieu Chaffotte\n */\npublic abstract class AbstractSelectDescriptor<T> {\n\n    private final String queryName;\n\n    private final Class<? extends PersistentObject> entityType;\n\n    private final Class<T> returnType;\n\n    public AbstractSelectDescriptor(final String queryName, final Class<? extends PersistentObject> entityType,\n            final Class<T> returnType) {\n        this.entityType = entityType;\n        this.queryName = queryName;\n        this.returnType = returnType;\n    }\n\n    public String getQueryName() {\n        return queryName;\n    }\n\n    public Class<? extends PersistentObject> getEntityType() {\n        return entityType;\n    }\n\n    public Class<T> getReturnType() {\n        return returnType;\n    }\n\n    @Override\n    public String toString() {\n        return \"AbstractSelectDescriptor [entityType=\" + entityType + \", queryName=\" + queryName + \", returnType=\"\n                + returnType + \"]\";\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + ((entityType == null) ? 0 : entityType.hashCode());\n        result = prime * result + ((queryName == null) ? 0 : queryName.hashCode());\n        result = prime * result + ((returnType == null) ? 0 : returnType.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        final AbstractSelectDescriptor<?> other = (AbstractSelectDescriptor<?>) obj;\n        if (entityType == null) {\n            if (other.entityType != null) {\n                return false;\n            }\n        } else if (!entityType.equals(other.entityType)) {\n            return false;\n        }\n        if (queryName == null) {\n            if (other.queryName != null) {\n                return false;\n            }\n        } else if (!queryName.equals(other.queryName)) {\n            return false;\n        }\n        if (returnType == null) {\n            if (other.returnType != null) {\n                return false;\n            }\n        } else if (!returnType.equals(other.returnType)) {\n            return false;\n        }\n        return true;\n    }\n\n    public abstract Map<String, Object> getInputParameters();\n\n    public abstract int getStartIndex();\n\n    public abstract int getPageSize();\n\n    public abstract boolean hasAFilter();\n\n    public abstract QueryOptions getQueryOptions();\n\n    public abstract boolean hasOrderByParameters();\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/AbstractSelectWithParametersDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author Charles Souillard\n * @author Matthieu Chaffotte\n */\npublic abstract class AbstractSelectWithParametersDescriptor<T> extends AbstractSelectDescriptor<T> {\n\n    private Map<String, Object> inputParameters;\n\n    public AbstractSelectWithParametersDescriptor(final String queryName, final Map<String, Object> inputParameters,\n            final Class<? extends PersistentObject> entityType) {\n        super(queryName, entityType, (Class<T>) entityType);\n        this.inputParameters = inputParameters;\n    }\n\n    public AbstractSelectWithParametersDescriptor(final String queryName, final Map<String, Object> inputParameters,\n            final Class<? extends PersistentObject> entityType, final Class<T> returnType) {\n        super(queryName, entityType, returnType);\n        this.inputParameters = inputParameters;\n    }\n\n    public Map<String, Object> getInputParameters() {\n        if (this.inputParameters == null) {\n            return Collections.unmodifiableMap(new HashMap<String, Object>());\n        }\n        return Collections.unmodifiableMap(inputParameters);\n    }\n\n    public Object getInputParameter(final String key) {\n        return getInputParameters().get(key);\n    }\n\n    public void addInputParameter(final String key, final Object value) {\n        if (inputParameters == null) {\n            this.inputParameters = new HashMap<String, Object>();\n        }\n        this.inputParameters.put(key, value);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/ArchivedPersistentObject.java",
    "content": "/**\n * Copyright (C) 2025 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\n/**\n * Interface for all {@link ArchivedPersistentObject}.\n *\n * @see PersistentObject\n */\npublic interface ArchivedPersistentObject extends PersistentObject {\n\n    long getArchiveDate();\n\n    /**\n     * Get the source object id from which this <code>ArchivedPersistentObject</code> originates.\n     *\n     * @return the id of the source object\n     */\n    long getSourceObjectId();\n\n    Class<? extends PersistentObject> getPersistentObjectInterface();\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/Atomikos3HibernateJtaPlatform.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport javax.transaction.TransactionManager;\nimport javax.transaction.UserTransaction;\n\nimport org.hibernate.engine.transaction.jta.platform.internal.AbstractJtaPlatform;\n\n/**\n * @author Charles Souillard\n */\npublic class Atomikos3HibernateJtaPlatform extends AbstractJtaPlatform {\n\n    private static final long serialVersionUID = 4893085097625997082L;\n\n    private final TransactionManager transactionManager;\n\n    public Atomikos3HibernateJtaPlatform() throws Exception {\n        final Class<?> userTransactionManagerClass = Class.forName(\"com.atomikos.icatch.jta.UserTransactionManager\");\n        this.transactionManager = (TransactionManager) userTransactionManagerClass.newInstance();\n    }\n\n    @Override\n    protected TransactionManager locateTransactionManager() {\n        return this.transactionManager;\n\n    }\n\n    @Override\n    protected UserTransaction locateUserTransaction() {\n        return (UserTransaction) this.transactionManager;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/ConfigurationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Charles Souillard\n */\npublic class ConfigurationException extends SBonitaException {\n\n    private static final long serialVersionUID = 6612093958515809124L;\n\n    public ConfigurationException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/CustomDataTypesRegistration.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport lombok.Getter;\nimport org.hibernate.boot.SessionFactoryBuilder;\nimport org.hibernate.boot.spi.MetadataImplementor;\nimport org.hibernate.boot.spi.SessionFactoryBuilderFactory;\nimport org.hibernate.boot.spi.SessionFactoryBuilderImplementor;\nimport org.hibernate.type.BasicType;\nimport org.slf4j.LoggerFactory;\n\npublic class CustomDataTypesRegistration implements SessionFactoryBuilderFactory {\n\n    private static final org.slf4j.Logger logger = LoggerFactory.getLogger(CustomDataTypesRegistration.class);\n\n    @Getter\n    private static Set<BasicType> typeOverrides = new HashSet<>();\n\n    @Override\n    public SessionFactoryBuilder getSessionFactoryBuilder(final MetadataImplementor metadata,\n            final SessionFactoryBuilderImplementor defaultBuilder) {\n        for (BasicType typeOverride : typeOverrides) {\n            logger.debug(\"Registering custom Hibernate data type {}\", typeOverride);\n            metadata.getTypeResolver().registerTypeOverride(typeOverride);\n        }\n        return defaultBuilder;\n    }\n\n    public static void addTypeOverride(BasicType type) {\n        typeOverrides.add(type);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/DateStoredAsLongUserType.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport java.io.Serializable;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.Date;\n\nimport org.hibernate.HibernateException;\nimport org.hibernate.engine.spi.SharedSessionContractImplementor;\nimport org.hibernate.type.LongType;\nimport org.hibernate.usertype.UserType;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class DateStoredAsLongUserType implements UserType, Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    private static final LongType type = new LongType();\n\n    private static int[] sqlTypes = new int[] { type.sqlType() };\n\n    @Override\n    public Class<?> returnedClass() {\n        return Date.class;\n    }\n\n    public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner)\n            throws HibernateException, SQLException {\n        final Object identifier = type.get(rs, names[0], session);\n        if (identifier == null) {\n            return null;\n        }\n        return new Date((Long) identifier);\n    }\n\n    @Override\n    public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session)\n            throws HibernateException, SQLException {\n        try {\n            if (value == null) {\n                st.setNull(index, type.sqlType());\n            } else {\n                type.set(st, ((Date) value).getTime(), index, session);\n            }\n        } catch (final Exception e) {\n        }\n\n    }\n\n    @Override\n    public int[] sqlTypes() {\n        return sqlTypes;\n    }\n\n    @Override\n    public Object assemble(final Serializable cached, final Object owner) {\n        return cached;\n    }\n\n    @Override\n    public Object deepCopy(final Object value) {\n        return value;\n    }\n\n    @Override\n    public Serializable disassemble(final Object value) {\n        return (Serializable) value;\n    }\n\n    @Override\n    public boolean equals(final Object x, final Object y) {\n        return x == y;\n    }\n\n    @Override\n    public int hashCode(final Object x) {\n        return x.hashCode();\n    }\n\n    @Override\n    public boolean isMutable() {\n        return false;\n    }\n\n    @Override\n    public Object replace(final Object original, final Object target, final Object owner) {\n        return original;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/DefaultOrderByBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class DefaultOrderByBuilder implements OrderByBuilder {\n\n    @Override\n    public void appendOrderBy(StringBuilder builder, String fieldName, OrderByType orderByType) {\n        builder.append(fieldName).append(\" \").append(orderByType.getSqlKeyword());\n\n    }\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/DefaultQueryOptions.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\n/**\n * @author Charles Souillard\n */\npublic class DefaultQueryOptions {\n\n    private static final QueryOptions queryOptions = new QueryOptions(0, 20);\n\n    public static QueryOptions getInstance() {\n        return queryOptions;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/FilterOption.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport java.io.Serializable;\n\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.bonitasoft.engine.persistence.search.FilterOperationType;\n\n/**\n * @author Matthieu Chaffotte\n * @author Emmanuel Duchastenier\n */\n@EqualsAndHashCode\n@ToString\npublic class FilterOption implements Serializable {\n\n    private static final long serialVersionUID = -5043588187864921664L;\n\n    private Class<? extends PersistentObject> persistentClass;\n\n    private String fieldName;\n\n    private Object value;\n\n    private Object to;\n\n    private Object from;\n\n    private FilterOperationType operationType;\n\n    public FilterOption(final Class<? extends PersistentObject> persistentClass, final String fieldName,\n            final Object value,\n            final FilterOperationType operatorType) {\n        this(persistentClass, fieldName);\n        this.value = value;\n        operationType = operatorType;\n    }\n\n    public FilterOption(final Class<? extends PersistentObject> persistentClass, final String fieldName,\n            final Object value) {\n        // EQUALS operation by default:\n        this(persistentClass, fieldName, value, FilterOperationType.EQUALS);\n    }\n\n    public FilterOption(final Class<? extends PersistentObject> persistentClass, final String fieldName) {\n        this.persistentClass = persistentClass;\n        this.fieldName = fieldName;\n    }\n\n    public FilterOption(final Class<? extends PersistentObject> persistentClass, final String fieldName,\n            final Object from, final Object to) {\n        // EQUALS operation by default:\n        this(persistentClass, fieldName, null, FilterOperationType.BETWEEN);\n        this.from = from;\n        this.to = to;\n    }\n\n    public FilterOption(final FilterOperationType operatorType) {\n        operationType = operatorType;\n    }\n\n    public Class<? extends PersistentObject> getPersistentClass() {\n        return persistentClass;\n    }\n\n    public String getFieldName() {\n        return fieldName;\n    }\n\n    public Object getValue() {\n        return value;\n    }\n\n    public Object getFrom() {\n        return from;\n    }\n\n    public void setFrom(final Object from) {\n        this.from = from;\n    }\n\n    public Object getTo() {\n        return to;\n    }\n\n    public FilterOption like(final Object value) {\n        this.value = value;\n        operationType = FilterOperationType.LIKE;\n        return this;\n    }\n\n    public FilterOption between(final Object from, final Object to) {\n        this.from = from;\n        this.to = to;\n        operationType = FilterOperationType.BETWEEN;\n        return this;\n    }\n\n    public FilterOperationType getFilterOperationType() {\n        return operationType;\n    }\n\n    public static FilterOption or() {\n        return new FilterOption(FilterOperationType.OR);\n    }\n\n    public static FilterOption and() {\n        return new FilterOption(FilterOperationType.AND);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/HQLQueryBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport java.util.Collection;\nimport java.util.Map;\n\nimport org.hibernate.Session;\nimport org.hibernate.query.ParameterMetadata;\nimport org.hibernate.query.Query;\nimport org.hibernate.query.QueryParameter;\nimport org.hibernate.type.CustomType;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Baptiste Mesta\n */\npublic class HQLQueryBuilder<T> extends QueryBuilder<T> {\n\n    private static final Logger logger = LoggerFactory.getLogger(HQLQueryBuilder.class);\n\n    HQLQueryBuilder(Session session, Query baseQuery, OrderByBuilder orderByBuilder,\n            Map<String, String> classAliasMappings,\n            char likeEscapeCharacter,\n            OrderByCheckingMode orderByCheckingMode,\n            SelectListDescriptor<T> selectDescriptor) {\n        super(session, baseQuery, orderByBuilder, classAliasMappings, likeEscapeCharacter,\n                orderByCheckingMode, selectDescriptor);\n    }\n\n    @Override\n    Query rebuildQuery(AbstractSelectDescriptor<T> selectDescriptor, Session session, Query query) {\n        Query generatedQuery = session.createQuery(stringQueryBuilder.toString());\n        ParameterMetadata parameterMetadata = generatedQuery.getParameterMetadata();\n        for (Map.Entry<String, Object> parameter : getQueryParameters().entrySet()) {\n            if (parameter.getValue() instanceof Collection) {\n                generatedQuery.setParameterList(parameter.getKey(), (Collection) parameter.getValue());\n            } else {\n                generatedQuery.setParameter(parameter.getKey(),\n                        convertParameterValueToFieldType(parameter, parameterMetadata));\n            }\n        }\n        return generatedQuery;\n    }\n\n    private Object convertParameterValueToFieldType(Map.Entry<String, Object> parameter,\n            ParameterMetadata parameterMetadata) {\n        QueryParameter<Object> namedParameterDescriptor = parameterMetadata.getQueryParameter(parameter.getKey());\n        Object convertedParameterValue = parameter.getValue();\n        if (convertedParameterValue != null) {\n            String parameterValueType = convertedParameterValue.getClass().getSimpleName();\n            String expectedType = namedParameterDescriptor.getHibernateType().getName();\n            if (!expectedType.equals(parameterValueType.toLowerCase())) {\n                logger.debug(\"Trying to convert from {} to expected type {} \", parameterValueType, expectedType);\n                switch (expectedType) {\n                    case \"long\":\n                        convertedParameterValue = convertToLong(parameterValueType.toLowerCase(),\n                                convertedParameterValue);\n                        break;\n                    case \"string\":\n                        convertedParameterValue = convertToString(parameterValueType.toLowerCase(),\n                                convertedParameterValue);\n                        break;\n                    case \"org.hibernate.type.EnumType\":\n                        convertedParameterValue = convertToEnum(parameterValueType.toLowerCase(),\n                                convertedParameterValue,\n                                (((CustomType) namedParameterDescriptor.getHibernateType())\n                                        .getUserType()).returnedClass());\n                        break;\n                    default:\n                        logger.debug(\"Not converting from {} to {}\", parameterValueType, expectedType);\n                        break;\n                }\n            }\n        }\n        return convertedParameterValue;\n    }\n\n    private Object convertToEnum(String parameterValueType, Object parameterValue,\n            Class<? extends Enum> expectedTypeClass) {\n        if (parameterValueType.equals(\"string\")) {\n            return Enum.valueOf(expectedTypeClass, (String) parameterValue);\n        }\n        return parameterValue;\n    }\n\n    private Long convertToLong(String parameterValueType, Object parameterValue) {\n        if (parameterValueType.equals(\"string\")) {\n            return Long.valueOf((String) parameterValue);\n        } else if (parameterValueType.equals(\"integer\")) {\n            return Long.valueOf((Integer) parameterValue);\n        } else {\n            throw new IllegalArgumentException(\n                    \"Unsupported type \" + parameterValueType + \", cannot convert it to 'long'.\");\n        }\n    }\n\n    private String convertToString(String parameterValueType, Object parameterValue) {\n        if (parameterValueType.equals(\"long\")) {\n            return String.valueOf(parameterValue);\n        } else if (parameterValueType.equals(\"integer\")) {\n            return String.valueOf(parameterValue);\n        } else {\n            throw new IllegalArgumentException(\n                    \"Unsupported type \" + parameterValueType + \", cannot convert it to 'String'\");\n        }\n    }\n\n    @Override\n    protected void addConstantsAsParameters(Query query) {\n        // nothing to do, Native queries require to inject constant parameters here\n    }\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/HibernateConfigurationProvider.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Properties;\n\nimport org.bonitasoft.engine.services.Vendor;\nimport org.hibernate.SessionFactory;\n\n/**\n * @author Celine Souchet\n */\npublic interface HibernateConfigurationProvider {\n\n    Map<String, String> getClassAliasMappings();\n\n    List<String> getMappingExclusions();\n\n    Map<String, String> getCacheQueries();\n\n    void bootstrap(Properties extraHibernateProperties);\n\n    Vendor getVendor();\n\n    SessionFactory getSessionFactory();\n\n    List<Class<? extends PersistentObject>> getMappedClasses();\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/HibernateConfigurationProviderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport static org.bonitasoft.engine.services.Vendor.POSTGRES;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Properties;\n\nimport javax.persistence.SharedCacheMode;\n\nimport lombok.Getter;\nimport org.bonitasoft.engine.services.Vendor;\nimport org.hibernate.Interceptor;\nimport org.hibernate.SessionFactory;\nimport org.hibernate.boot.Metadata;\nimport org.hibernate.boot.MetadataBuilder;\nimport org.hibernate.boot.MetadataSources;\nimport org.hibernate.boot.SessionFactoryBuilder;\nimport org.hibernate.boot.registry.BootstrapServiceRegistryBuilder;\nimport org.hibernate.boot.registry.StandardServiceRegistry;\nimport org.hibernate.boot.registry.StandardServiceRegistryBuilder;\nimport org.hibernate.mapping.PersistentClass;\nimport org.hibernate.service.ServiceRegistry;\nimport org.hibernate.type.BasicType;\n\n/**\n * @author Charles Souillard\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class HibernateConfigurationProviderImpl implements HibernateConfigurationProvider {\n\n    private final HibernateResourcesConfigurationProvider hibernateResourcesConfigurationProvider;\n    protected final Properties properties;\n    private final List<String> mappingExclusions;\n    @Getter\n    protected Vendor vendor;\n    @Getter\n    private SessionFactory sessionFactory;\n    @Getter\n    private final List<Class<? extends PersistentObject>> mappedClasses = new ArrayList<>();\n\n    public HibernateConfigurationProviderImpl(final Properties properties,\n            final HibernateResourcesConfigurationProvider hibernateResourcesConfigurationProvider,\n            final List<String> mappingExclusions) {\n        this.properties = properties;\n        this.hibernateResourcesConfigurationProvider = hibernateResourcesConfigurationProvider;\n        this.mappingExclusions = mappingExclusions;\n    }\n\n    @Override\n    public Map<String, String> getClassAliasMappings() {\n        return hibernateResourcesConfigurationProvider.getClassAliasMappings();\n    }\n\n    @Override\n    public List<String> getMappingExclusions() {\n        return Collections.unmodifiableList(mappingExclusions);\n    }\n\n    @Override\n    public Map<String, String> getCacheQueries() {\n        return null;\n    }\n\n    @Override\n    public void bootstrap(Properties extraHibernateProperties) {\n        StandardServiceRegistryBuilder standardRegistryBuilder = new StandardServiceRegistryBuilder(\n                new BootstrapServiceRegistryBuilder().build());\n        Properties allProps = gatherAllProperties(extraHibernateProperties, standardRegistryBuilder);\n        StandardServiceRegistry standardRegistry = standardRegistryBuilder.build();\n\n        this.vendor = Vendor.fromHibernateDialectProperty(allProps.getProperty(\"hibernate.dialect\"));\n        setCustomHibernateDataTypesAndProperties();\n\n        Metadata metadata = buildHibernateMetadata(standardRegistry);\n\n        this.sessionFactory = applyInterceptors(metadata, allProps).build();\n\n        for (PersistentClass entityBinding : metadata.getEntityBindings()) {\n            mappedClasses.add(entityBinding.getMappedClass());\n        }\n    }\n\n    /**\n     * Set custom Hibernate data types using {@link CustomDataTypesRegistration} and set Hibernate system properties.\n     */\n    protected void setCustomHibernateDataTypesAndProperties() {\n        switch (vendor) {\n            case POSTGRES -> {\n                CustomDataTypesRegistration.addTypeOverride(new PostgresMaterializedBlobType());\n                CustomDataTypesRegistration.addTypeOverride(new PostgresMaterializedClobType());\n                CustomDataTypesRegistration.addTypeOverride(new PostgresXMLType());\n            }\n            case OTHER -> CustomDataTypesRegistration.addTypeOverride(new XMLType());\n            default -> throw new IllegalStateException(\"Unsupported vendor: \" + vendor);\n        }\n    }\n\n    /**\n     * Build the hibernate metadata from the provided resources and entities.\n     *\n     * @param standardRegistry the standard registry of services needed to create metadata sources\n     * @return the hibernate metadata\n     */\n    private Metadata buildHibernateMetadata(ServiceRegistry standardRegistry) {\n        MetadataSources metadataSources = new MetadataSources(standardRegistry) {\n\n            @Override\n            public MetadataBuilder getMetadataBuilder() {\n                MetadataBuilder metadataBuilder = super.getMetadataBuilder();\n                for (BasicType typeOverride : CustomDataTypesRegistration.getTypeOverrides()) {\n                    metadataBuilder.applyBasicType(typeOverride);\n                }\n                applyCacheMode(metadataBuilder);\n                return metadataBuilder;\n            }\n        };\n        metadataSources.addPackage(\"org.bonitasoft.engine.persistence\");\n        for (final String resource : hibernateResourcesConfigurationProvider.getResources()) {\n            metadataSources.addResource(resource);\n        }\n        for (Class entity : hibernateResourcesConfigurationProvider.getEntities()) {\n            metadataSources.addAnnotatedClass(entity);\n        }\n        return metadataSources.buildMetadata();\n    }\n\n    /**\n     * Apply interceptors to a new session factory.\n     *\n     * @param metadata the hibernate metadata\n     * @param allProps the hibernate properties\n     * @return a new session factory builder\n     * @throws IllegalStateException if an interceptor class is unknown or cannot be instantiated\n     */\n    protected SessionFactoryBuilder applyInterceptors(Metadata metadata, Properties allProps)\n            throws IllegalStateException {\n        SessionFactoryBuilder sessionFactoryBuilder = metadata.getSessionFactoryBuilder();\n        final String className = allProps.getProperty(\"hibernate.interceptor\");\n        if (className != null && !className.isEmpty()) {\n            try {\n                sessionFactoryBuilder.applyInterceptor(\n                        (Interceptor) Class.forName(className).getDeclaredConstructor().newInstance());\n            } catch (final ReflectiveOperationException e) {\n                throw new IllegalStateException(\"Unknown interceptor class \" + className, e);\n            }\n        }\n        if (vendor == POSTGRES) {\n            sessionFactoryBuilder.applyInterceptor(new PostgresInterceptor());\n        }\n        return sessionFactoryBuilder;\n    }\n\n    protected void applyCacheMode(MetadataBuilder metadataBuilder) {\n        metadataBuilder.applySharedCacheMode(SharedCacheMode.NONE);\n    }\n\n    protected Properties gatherAllProperties(Properties extraHibernateProperties,\n            StandardServiceRegistryBuilder standardRegistryBuilder) {\n        Properties allProps = new Properties();\n        allProps.putAll(properties);\n        allProps.putAll(extraHibernateProperties);\n\n        for (Map.Entry<Object, Object> prop : allProps.entrySet()) {\n            standardRegistryBuilder.applySetting(prop.getKey().toString(), prop.getValue());\n        }\n        return allProps;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/HibernateMetricsBinder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport org.hibernate.SessionFactory;\n\npublic interface HibernateMetricsBinder {\n\n    void bindMetrics(SessionFactory sessionFactory);\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/HibernatePersistenceService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.SortedSet;\nimport java.util.TreeSet;\n\nimport javax.annotation.PreDestroy;\n\nimport lombok.Getter;\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.commons.ClassReflector;\nimport org.bonitasoft.engine.commons.exceptions.SRetryableException;\nimport org.bonitasoft.engine.persistence.search.FilterOperationType;\nimport org.bonitasoft.engine.sequence.SequenceManager;\nimport org.bonitasoft.engine.services.PersistenceService;\nimport org.bonitasoft.engine.services.SPersistenceException;\nimport org.bonitasoft.engine.services.UpdateDescriptor;\nimport org.hibernate.AssertionFailure;\nimport org.hibernate.HibernateException;\nimport org.hibernate.Session;\nimport org.hibernate.SessionFactory;\nimport org.hibernate.StaleStateException;\nimport org.hibernate.exception.LockAcquisitionException;\nimport org.hibernate.query.Query;\nimport org.hibernate.stat.Statistics;\nimport org.slf4j.Logger;\n\n/**\n * Hibernate implementation of the persistence service\n *\n * @author Charles Souillard\n * @author Nicolas Chabanoles\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @author Laurent Vaills\n * @author Guillaume Rosinosky\n */\n@Slf4j\npublic class HibernatePersistenceService implements PersistenceService {\n\n    @Getter\n    private final SessionFactory sessionFactory;\n\n    @Getter\n    private final Map<String, String> classAliasMappings;\n\n    @Getter // for testing purposes\n    private final Map<String, String> cacheQueries;\n\n    private final List<Class<? extends PersistentObject>> classMapping;\n\n    private final List<String> mappingExclusions;\n    private final Statistics statistics;\n    private final SequenceManager sequenceManager;\n    private int stat_display_count;\n    private final QueryBuilderFactory queryBuilderFactory;\n\n    public HibernatePersistenceService(final HibernateConfigurationProvider hbmConfigurationProvider,\n            final Properties extraHibernateProperties, final SequenceManager sequenceManager,\n            final QueryBuilderFactory queryBuilderFactory, HibernateMetricsBinder hibernateMetricsBinder) {\n        this.sequenceManager = sequenceManager;\n        hbmConfigurationProvider.bootstrap(extraHibernateProperties);\n        sessionFactory = hbmConfigurationProvider.getSessionFactory();\n\n        this.queryBuilderFactory = queryBuilderFactory;\n        statistics = sessionFactory.getStatistics();\n        classMapping = hbmConfigurationProvider.getMappedClasses();\n        classAliasMappings = hbmConfigurationProvider.getClassAliasMappings();\n        mappingExclusions = hbmConfigurationProvider.getMappingExclusions();\n        cacheQueries = hbmConfigurationProvider.getCacheQueries();\n\n        hibernateMetricsBinder.bindMetrics(getSessionFactory());\n    }\n\n    /**\n     * Log synthetic information about cache every 10.000 sessions, if hibernate.gather_statistics, is enabled.\n     */\n    private void logStats() {\n        if (!statistics.isStatisticsEnabled() || !getLogger().isInfoEnabled()) {\n            return;\n        }\n        if (stat_display_count == 10 || stat_display_count == 100 || stat_display_count == 1000\n                || stat_display_count % 10000 == 0) {\n            final long query_cache_hit = statistics.getQueryCacheHitCount();\n            final long query_cache_miss = statistics.getQueryCacheMissCount();\n            final long query_cache_put = statistics.getQueryCachePutCount();\n            final long level_2_cache_hit = statistics.getSecondLevelCacheHitCount();\n            final long level_2_cache_miss = statistics.getSecondLevelCacheMissCount();\n            final long level_2_put = statistics.getSecondLevelCachePutCount();\n\n            getLogger().info(\"Query Cache Ratio \"\n                    + (int) ((double) query_cache_hit / (query_cache_hit + query_cache_miss) * 100) + \"% \"\n                    + query_cache_hit + \" hits \" + query_cache_miss\n                    + \" miss \" + query_cache_put + \" puts\");\n            getLogger().info(\"2nd Level Cache Ratio \"\n                    + (int) ((double) level_2_cache_hit / (level_2_cache_hit + level_2_cache_miss) * 100) + \"% \"\n                    + level_2_cache_hit + \" hits \"\n                    + level_2_cache_miss + \" miss \" + level_2_put + \" puts\");\n        }\n        stat_display_count++;\n    }\n\n    protected Session getSession() throws SPersistenceException {\n        logStats();\n        try {\n            return sessionFactory.getCurrentSession();\n        } catch (final HibernateException e) {\n            throw new SPersistenceException(e);\n        }\n    }\n\n    public void flushStatements() throws SPersistenceException {\n        getSession().flush();\n    }\n\n    @Override\n    public <T extends PersistentObject> void delete(final T entity) throws SPersistenceException {\n        if (getLogger().isDebugEnabled()) {\n            getLogger().debug(\"Deleting instance of class {} with id={}\", entity.getClass().getSimpleName(),\n                    entity.getId());\n        }\n        final Session session = getSession();\n        try {\n            if (session.contains(entity)) {\n                session.delete(entity);\n            } else {\n                final Class<? extends PersistentObject> mappedClass = getMappedClass(entity.getClass());\n                // Deletion must be performed on the session entity and not on a potential transitional entity.\n                final Object pe = session.get(mappedClass, entity.getId());\n                session.delete(pe);\n            }\n        } catch (final AssertionFailure | LockAcquisitionException | StaleStateException e) {\n            throw new SRetryableException(e);\n        } catch (final HibernateException e) {\n            throw new SPersistenceException(e);\n        }\n    }\n\n    @Override\n    public int update(final String updateQueryName) throws SPersistenceException {\n        return update(updateQueryName, null);\n    }\n\n    @Override\n    public int update(final String updateQueryName, final Map<String, Object> inputParameters)\n            throws SPersistenceException {\n        final Query query = getSession().getNamedQuery(updateQueryName);\n        try {\n            if (inputParameters != null) {\n                setParameters(query, inputParameters);\n            }\n\n            return query.executeUpdate();\n        } catch (final HibernateException he) {\n            throw new SPersistenceException(he);\n        }\n    }\n\n    @Override\n    public void refresh(final PersistentObject entity) throws SPersistenceException {\n        if (entity != null) {\n            try {\n                getSession().refresh(entity);\n            } catch (final HibernateException e) {\n                throw new SPersistenceException(\"Failed to refresh entity \"\n                        + entity.getClass().getSimpleName() + \" (id=\" + entity.getId() + \")\", e);\n            }\n        }\n    }\n\n    @Override\n    public void deleteAll(final Class<? extends PersistentObject> entityClass) throws SPersistenceException {\n        final Class<? extends PersistentObject> mappedClass = getMappedClass(entityClass);\n        final Query query = getSession().getNamedQuery(\"deleteAll\" + mappedClass.getSimpleName());\n        try {\n            query.executeUpdate();\n        } catch (final AssertionFailure | LockAcquisitionException | StaleStateException e) {\n            throw new SRetryableException(e);\n        } catch (final HibernateException he) {\n            throw new SPersistenceException(he);\n        }\n    }\n\n    @Override\n    public <T extends PersistentObject> T insert(final T entity) throws SPersistenceException {\n        final Class<? extends PersistentObject> entityClass = entity.getClass();\n        checkClassMapping(entityClass);\n        final Session session = getSession();\n        setId(entity);\n        try {\n            session.save(entity);\n            return entity;\n        } catch (final AssertionFailure | LockAcquisitionException | StaleStateException e) {\n            throw new SRetryableException(e);\n        } catch (final HibernateException he) {\n            throw new SPersistenceException(he);\n        }\n    }\n\n    @Override\n    public <T extends PersistentObject> List<T> insertInBatch(final List<T> entities) throws SPersistenceException {\n        if (!entities.isEmpty()) {\n            final Session session = getSession();\n            for (final PersistentObject entity : entities) {\n                final Class<? extends PersistentObject> entityClass = entity.getClass();\n                checkClassMapping(entityClass);\n                setId(entity);\n                session.save(entity);\n            }\n        }\n        return entities;\n    }\n\n    @Override\n    public void update(final UpdateDescriptor updateDescriptor) throws SPersistenceException {\n        // FIXME: deal with disconnected objects:\n        final Class<? extends PersistentObject> entityClass = updateDescriptor.getEntity().getClass();\n        checkClassMapping(entityClass);\n        final PersistentObject entity = updateDescriptor.getEntity();\n        final Session session = getSession();\n        if (!session.contains(entity)) {\n            throw new SPersistenceException(\"The object cannot be updated because it's disconnected \" + entity);\n        }\n        for (final Map.Entry<String, Object> field : updateDescriptor.getFields().entrySet()) {\n            setField(entity, field.getKey(), field.getValue());\n        }\n    }\n\n    private void setField(final PersistentObject entity, final String fieldName, final Object parameterValue)\n            throws SPersistenceException {\n        Long id = null;\n        try {\n            id = entity.getId();\n            ClassReflector.setField(entity, fieldName, parameterValue);\n        } catch (final Exception e) {\n            throw new SPersistenceException(\"Problem while updating entity: \" + entity + \" with id: \" + id, e);\n        }\n    }\n\n    @Override\n    public <T> T selectOne(final SelectOneDescriptor<T> selectDescriptor) throws SBonitaReadException {\n        try {\n            return selectOne(getSession(), selectDescriptor);\n        } catch (final SPersistenceException e) {\n            throw new SBonitaReadException(e, selectDescriptor);\n        }\n    }\n\n    Class<? extends PersistentObject> getMappedClass(final Class<? extends PersistentObject> entityClass)\n            throws SPersistenceException {\n        if (classMapping.contains(entityClass)) {\n            return entityClass;\n        }\n        throw new SPersistenceException(\"Unable to locate class \" + entityClass + \" in Hibernate configuration\");\n    }\n\n    private void checkClassMapping(final Class<? extends PersistentObject> entityClass) throws SPersistenceException {\n        if (!classMapping.contains(entityClass) && !mappingExclusions.contains(entityClass.getName())) {\n            throw new SPersistenceException(\"Unable to locate class \" + entityClass + \" in Hibernate configuration\");\n        }\n    }\n\n    @Override\n    public <T extends PersistentObject> T selectById(final SelectByIdDescriptor<T> selectDescriptor)\n            throws SBonitaReadException {\n        try {\n            final Session session = getSession();\n            final T object = this.selectById(session, selectDescriptor);\n            if (selectDescriptor.isReadOnly()) {\n                disconnectEntityFromSession(session, object);\n            }\n            return object;\n        } catch (final SPersistenceException e) {\n            throw new SBonitaReadException(e, selectDescriptor);\n        }\n    }\n\n    private static <T> void disconnectEntityFromSession(Session session, T object) {\n        if (object != null) {\n            session.evict(object);\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    <T extends PersistentObject> T selectObjectById(final Session session,\n            final SelectByIdDescriptor<T> selectDescriptor)\n            throws SBonitaReadException {\n        Class<? extends PersistentObject> mappedClass;\n        try {\n            mappedClass = getMappedClass(selectDescriptor.getEntityType());\n        } catch (final SPersistenceException e) {\n            throw new SBonitaReadException(e);\n        }\n        try {\n            return (T) session.get(mappedClass, selectDescriptor.getId());\n        } catch (final AssertionFailure | LockAcquisitionException | StaleStateException e) {\n            throw new SRetryableException(e);\n        } catch (final HibernateException he) {\n            throw new SBonitaReadException(he);\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    <T extends PersistentObject> T selectById(final Session session, final SelectByIdDescriptor<T> selectDescriptor)\n            throws SBonitaReadException {\n        return selectObjectById(session, selectDescriptor);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private <T> T selectOne(final Session session, final SelectOneDescriptor<T> selectDescriptor)\n            throws SBonitaReadException {\n        try {\n            checkClassMapping(selectDescriptor.getEntityType());\n        } catch (final SPersistenceException e) {\n            throw new SBonitaReadException(e);\n        }\n        final Query query = session.getNamedQuery(selectDescriptor.getQueryName());\n        setQueryCache(query, selectDescriptor.getQueryName());\n        final Map<String, Object> parameters = selectDescriptor.getInputParameters();\n        if (parameters != null) {\n            setParameters(query, parameters);\n        }\n        query.setMaxResults(1);\n        try {\n            return disconnectIfReadOnly((T) query.uniqueResult(), query, session);\n        } catch (final AssertionFailure | LockAcquisitionException | StaleStateException e) {\n            throw new SRetryableException(e);\n        } catch (final HibernateException he) {\n            throw new SBonitaReadException(he);\n        }\n    }\n\n    private boolean isCacheEnabled(String queryName) {\n        return cacheQueries != null && cacheQueries.containsKey(queryName);\n    }\n\n    private void setQueryCache(final Query query, final String name) {\n        if (isCacheEnabled(name)) {\n            query.setCacheable(true);\n        }\n    }\n\n    @Override\n    public <T> List<T> selectList(final SelectListDescriptor<T> selectDescriptor) throws SBonitaReadException {\n        try {\n            final Class<? extends PersistentObject> entityClass = selectDescriptor.getEntityType();\n            checkClassMapping(entityClass);\n\n            final Session session = getSession();\n\n            org.hibernate.query.Query query = queryBuilderFactory.createQueryBuilderFor(session, selectDescriptor)\n                    .cache(isCacheEnabled(selectDescriptor.getQueryName()))\n                    .build();\n\n            @SuppressWarnings(\"unchecked\")\n            final List<T> list = query.list();\n            if (list != null) {\n                disconnectIfReadOnly(list, query, session);\n                return list;\n            }\n            return Collections.emptyList();\n        } catch (final AssertionFailure | LockAcquisitionException | StaleStateException e) {\n            throw new SRetryableException(e);\n        } catch (final HibernateException | SPersistenceException e) {\n            throw new SBonitaReadException(e, selectDescriptor);\n        }\n    }\n\n    private static <T> void disconnectIfReadOnly(List<T> list, Query query, Session session) {\n        if (query.isReadOnly()) {\n            for (T t : list) {\n                disconnectEntityFromSession(session, t);\n            }\n        }\n    }\n\n    private static <T> T disconnectIfReadOnly(T object, Query query, Session session) {\n        if (query.isReadOnly()) {\n            disconnectEntityFromSession(session, object);\n        }\n        return object;\n    }\n\n    private void setParameters(final Query query, final Map<String, Object> inputParameters) {\n        for (final Map.Entry<String, Object> entry : inputParameters.entrySet()) {\n            final Object value = entry.getValue();\n            if (value instanceof Collection<?>) {\n                query.setParameterList(entry.getKey(), (Collection<?>) value);\n            } else {\n                query.setParameter(entry.getKey(), value);\n            }\n        }\n    }\n\n    @Override\n    public void delete(final long id, final Class<? extends PersistentObject> entityClass)\n            throws SPersistenceException {\n        final Class<? extends PersistentObject> mappedClass = getMappedClass(entityClass);\n        final Query query = getSession().getNamedQuery(\"delete\" + mappedClass.getSimpleName());\n        query.setParameter(\"id\", id);\n        try {\n            query.executeUpdate();\n        } catch (final AssertionFailure | LockAcquisitionException | StaleStateException e) {\n            throw new SRetryableException(e);\n        } catch (final HibernateException he) {\n            throw new SPersistenceException(he);\n        }\n    }\n\n    @Override\n    public void delete(final List<Long> ids, final Class<? extends PersistentObject> entityClass)\n            throws SPersistenceException {\n        final Class<? extends PersistentObject> mappedClass = getMappedClass(entityClass);\n        final Query query = getSession().getNamedQuery(\"deleteByIds\" + mappedClass.getSimpleName());\n        query.setParameterList(\"ids\", ids);\n        try {\n            query.executeUpdate();\n        } catch (final AssertionFailure | LockAcquisitionException | StaleStateException e) {\n            throw new SRetryableException(e);\n        } catch (final HibernateException sse) {\n            throw new SPersistenceException(sse);\n        }\n    }\n\n    @PreDestroy\n    public void destroy() {\n        getLogger().info(\n                \"Closing Hibernate session factory of \" + getClass().getName());\n        sessionFactory.close();\n    }\n\n    @Override\n    public <T extends PersistentObject> long getNumberOfEntities(final Class<T> entityClass, final QueryOptions options,\n            final Map<String, Object> parameters)\n            throws SBonitaReadException {\n        return getNumberOfEntities(entityClass, null, options, parameters);\n    }\n\n    @Override\n    public <T extends PersistentObject> long getNumberOfEntities(final Class<T> entityClass, final String querySuffix,\n            final QueryOptions options,\n            final Map<String, Object> parameters) throws SBonitaReadException {\n        List<FilterOption> filters;\n        if (options == null) {\n            filters = Collections.emptyList();\n        } else {\n            filters = options.getFilters();\n        }\n        final String queryName = getQueryName(\"getNumberOf\", querySuffix, entityClass, filters);\n\n        final SelectListDescriptor<Long> descriptor = new SelectListDescriptor<>(queryName, parameters, entityClass,\n                Long.class, options);\n        return selectList(descriptor).get(0);\n    }\n\n    @Override\n    public <T extends PersistentObject> List<T> searchEntity(final Class<T> entityClass, final QueryOptions options,\n            final Map<String, Object> parameters)\n            throws SBonitaReadException {\n        return searchEntity(entityClass, null, options, parameters);\n    }\n\n    @Override\n    public <T extends PersistentObject> List<T> searchEntity(final Class<T> entityClass, final String querySuffix,\n            final QueryOptions options,\n            final Map<String, Object> parameters) throws SBonitaReadException {\n        final String queryName = getQueryName(\"search\", querySuffix, entityClass, options.getFilters());\n        final SelectListDescriptor<T> descriptor = new SelectListDescriptor<>(queryName, parameters, entityClass,\n                options);\n        return selectList(descriptor);\n    }\n\n    private <T extends PersistentObject> String getQueryName(final String prefix, final String suffix,\n            final Class<T> entityClass,\n            final List<FilterOption> filters) {\n        final SortedSet<String> query = new TreeSet<>();\n        for (final FilterOption filter : filters) {\n            // if filter is just an operator, PersistentClass is not defined:\n            if (filter.getPersistentClass() != null) {\n                query.add(filter.getPersistentClass().getSimpleName());\n            }\n        }\n        final String searchOnClassName = entityClass.getSimpleName();\n        query.remove(searchOnClassName);\n        final StringBuilder builder = new StringBuilder(prefix);\n        builder.append(searchOnClassName);\n        if (!query.isEmpty()) {\n            builder.append(\"with\");\n        }\n        for (final String entity : query) {\n            builder.append(entity);\n        }\n        if (suffix != null) {\n            builder.append(suffix);\n        }\n        return builder.toString();\n    }\n\n    protected void setId(final PersistentObject entity) throws SPersistenceException {\n        if (entity == null) {\n            return;\n        }\n        // if this entity has no id, set it\n        Long id = null;\n        try {\n            id = entity.getId();\n        } catch (final Exception e) {\n            // this is a new object to save\n        }\n        if (id == null || id == -1 || id == 0) {\n            try {\n                id = sequenceManager.getNextId(entity.getClass().getName());\n                ClassReflector.invokeSetter(entity, \"setId\", long.class, id);\n            } catch (final Exception e) {\n                throw new SPersistenceException(\"Problem while saving entity: \" + entity + \" with id: \" + id, e);\n            }\n        }\n    }\n\n    protected Logger getLogger() {\n        return log;\n    }\n\n    @Override\n    public void deleteAll(final Class<? extends PersistentObject> entityClass, final List<FilterOption> filters)\n            throws SPersistenceException {\n        final Session session = getSession();\n        final String entityClassName = entityClass.getCanonicalName();\n\n        boolean hasFilters = filters != null && !filters.isEmpty();\n        Map<String, Object> parameters = new HashMap<>();\n        String baseQuery = \"DELETE FROM \" + entityClassName + \" \"\n                + (hasFilters ? getClassAliasMappings().get(entityClassName) : \"\");\n\n        if (hasFilters) {\n            if (filters.stream().anyMatch(f -> f.getFilterOperationType() == FilterOperationType.LIKE)) {\n                throw new IllegalStateException(\"Delete queries do not support queries with LIKE\");\n            }\n            QueryGeneratorForFilters.QueryGeneratedFilters whereClause = new QueryGeneratorForFilters(\n                    getClassAliasMappings(), '%'/* there is no 'like' in these delete queries */)\n                    .generate(filters);\n            parameters.putAll(whereClause.getParameters());\n            baseQuery += \" WHERE ( \" + whereClause.getFilters() + \" )\";\n        }\n        Query query = session.createQuery(baseQuery);\n        parameters.forEach(query::setParameter);\n        query.executeUpdate();\n        if (getLogger().isDebugEnabled()) {\n            getLogger().debug(\"Deleting all instances of class {} matching the provided filters\",\n                    entityClass.getSimpleName());\n        }\n    }\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/HibernateResourcesConfigurationProvider.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * @author Celine Souchet\n */\npublic interface HibernateResourcesConfigurationProvider {\n\n    void setHbmResources(final List<HibernateResourcesProvider> resources);\n\n    Set<Class> getEntities();\n\n    Set<String> getResources();\n\n    Map<String, String> getClassAliasMappings();\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/HibernateResourcesConfigurationProviderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * @author Charles Souillard\n * @author Celine Souchet\n * @author Matthieu Chaffotte\n */\npublic class HibernateResourcesConfigurationProviderImpl implements HibernateResourcesConfigurationProvider {\n\n    private final Set<String> resources = new HashSet<>();\n\n    private final Map<String, String> classAliasMappings = new HashMap<>();\n    private Set<Class> entities = new HashSet<>();\n\n    public HibernateResourcesConfigurationProviderImpl() {\n        super();\n    }\n\n    @Override\n    public void setHbmResources(final List<HibernateResourcesProvider> resources) {\n        for (final HibernateResourcesProvider hibernateResourcesProvider : resources) {\n            for (final String resource : hibernateResourcesProvider.getResources()) {\n                this.resources.add(resource);\n            }\n            classAliasMappings.putAll(hibernateResourcesProvider.getClassAliasMappings());\n            for (String entity : hibernateResourcesProvider.getEntities()) {\n                try {\n                    entities.add(Class.forName(entity));\n                } catch (ClassNotFoundException e) {\n                    throw new IllegalArgumentException(String.format(\"Entity class %s not found\", entity));\n                }\n            }\n        }\n    }\n\n    @Override\n    public Set<Class> getEntities() {\n        return entities;\n    }\n\n    @Override\n    public Set<String> getResources() {\n        return resources;\n    }\n\n    @Override\n    public Map<String, String> getClassAliasMappings() {\n        return classAliasMappings;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/HibernateResourcesProvider.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class HibernateResourcesProvider {\n\n    private Set<String> resources = new HashSet<>();\n    private Set<String> entities = new HashSet<>();\n\n    private Map<? extends String, ? extends String> classAliasMappings = new HashMap<>();\n\n    public void setResources(final Set<String> resources) {\n        final Set<String> hashSet = new HashSet<>(resources.size());\n        for (final String resource : resources) {\n            hashSet.add(resource.trim());\n        }\n        this.resources = hashSet;\n    }\n\n    public void setEntities(Set<String> resources) {\n        entities.addAll(resources);\n    }\n\n    public Set<String> getResources() {\n        return resources;\n    }\n\n    public Map<? extends String, ? extends String> getClassAliasMappings() {\n        return classAliasMappings;\n    }\n\n    public void setClassAliasMappings(final Map<? extends String, ? extends String> classAliasMappings) {\n        this.classAliasMappings = classAliasMappings;\n    }\n\n    public Set<String> getEntities() {\n        return entities;\n    }\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/JNDIBitronixJtaPlatform.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport javax.transaction.TransactionManager;\n\nimport org.hibernate.engine.transaction.jta.platform.internal.BitronixJtaPlatform;\n\n/**\n * @author Laurent Vaills\n */\npublic class JNDIBitronixJtaPlatform extends BitronixJtaPlatform {\n\n    private static final long serialVersionUID = 4893085097625997082L;\n\n    @Override\n    protected TransactionManager locateTransactionManager() {\n        // Force the lookup to JNDI to find the TransactionManager : since we share it between\n        // Hibernate and Quartz, I prefer to force the JNDI lookup in order to be sure that\n        // they are using the same instance.\n        return (TransactionManager) jndiService().locate(\"java:comp/UserTransaction\");\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/Narayana5HibernateJtaPlatform.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport java.lang.reflect.Method;\n\nimport javax.transaction.TransactionManager;\nimport javax.transaction.UserTransaction;\n\nimport org.hibernate.engine.transaction.jta.platform.internal.AbstractJtaPlatform;\n\n/**\n * @author Charles Souillard\n */\npublic class Narayana5HibernateJtaPlatform extends AbstractJtaPlatform {\n\n    private static final long serialVersionUID = 4893085097625997082L;\n\n    private final TransactionManager transactionManager;\n\n    private final Method getUserTransactionMethod;\n\n    private final Object jtaEnvironmentBeanInstance;\n\n    public Narayana5HibernateJtaPlatform() throws Exception {\n        final Class<?> jtaPropertyManagerClass = Class.forName(\"com.arjuna.ats.jta.common.jtaPropertyManager\");\n        final Method getJTAEnvironmentBeanMethod = jtaPropertyManagerClass.getMethod(\"getJTAEnvironmentBean\");\n        jtaEnvironmentBeanInstance = getJTAEnvironmentBeanMethod.invoke(null);\n        final Class<?> jtaEnvironmentBeanClass = jtaEnvironmentBeanInstance.getClass();\n\n        final Method getTransactionManagerMethod = jtaEnvironmentBeanClass.getMethod(\"getTransactionManager\");\n        transactionManager = (TransactionManager) getTransactionManagerMethod.invoke(jtaEnvironmentBeanInstance);\n\n        getUserTransactionMethod = jtaEnvironmentBeanClass.getMethod(\"getUserTransaction\");\n    }\n\n    @Override\n    protected TransactionManager locateTransactionManager() {\n        return transactionManager;\n    }\n\n    @Override\n    protected UserTransaction locateUserTransaction() {\n        try {\n            return (UserTransaction) getUserTransactionMethod.invoke(jtaEnvironmentBeanInstance);\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/OrderAndField.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\n/**\n * @author Baptiste Mesta\n */\npublic class OrderAndField {\n\n    private OrderByType order;\n\n    private String field;\n\n    public OrderAndField(final OrderByType order, final String field) {\n        super();\n        this.order = order;\n        this.field = field;\n    }\n\n    public OrderByType getOrder() {\n        return order;\n    }\n\n    public void setOrder(final OrderByType order) {\n        this.order = order;\n    }\n\n    public String getField() {\n        return field;\n    }\n\n    public void setField(final String field) {\n        this.field = field;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + (field == null ? 0 : field.hashCode());\n        result = prime * result + (order == null ? 0 : order.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        final OrderAndField other = (OrderAndField) obj;\n        if (field == null) {\n            if (other.field != null) {\n                return false;\n            }\n        } else if (!field.equals(other.field)) {\n            return false;\n        }\n        if (order != other.order) {\n            return false;\n        }\n        return true;\n    }\n\n    @Override\n    public String toString() {\n        return \"OrderAndField [order=\" + order + \", field=\" + field + \"]\";\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/OrderByBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\n/**\n * @author Laurent Leseigneur\n */\npublic interface OrderByBuilder {\n\n    void appendOrderBy(StringBuilder builder, String fieldName, OrderByType orderByType);\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/OrderByCheckingMode.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\n/**\n * It's used when we check if the queries who return a list have an \"Order by\" clause.\n *\n * @author Celine Souchet\n * @version 6.4.0\n * @since 6.4.0\n */\npublic enum OrderByCheckingMode {\n\n    /**\n     * Do nothing\n     */\n    NONE,\n\n    /**\n     * Log a warning message\n     */\n    WARNING,\n\n    /**\n     * Throw an IllegalArgumentException\n     */\n    STRICT\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/OrderByOption.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport java.io.Serializable;\n\n/**\n * @author Charles Souillard\n */\npublic class OrderByOption implements Serializable {\n\n    private static final long serialVersionUID = -3903433577282357734L;\n\n    private final Class<? extends PersistentObject> clazz;\n\n    private final String fieldName;\n\n    private final OrderByType orderByType;\n\n    public OrderByOption(final Class<? extends PersistentObject> clazz, final String fieldName,\n            final OrderByType orderByType) {\n        super();\n        this.clazz = clazz;\n        this.fieldName = fieldName;\n        this.orderByType = orderByType;\n    }\n\n    public Class<? extends PersistentObject> getClazz() {\n        return clazz;\n    }\n\n    public String getFieldName() {\n        return fieldName;\n    }\n\n    public OrderByType getOrderByType() {\n        return orderByType;\n    }\n\n    @Override\n    public String toString() {\n        return \"OrderByOption [clazz=\" + clazz + \", fieldName=\" + fieldName + \", orderByType=\" + orderByType + \"]\";\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (!(o instanceof OrderByOption that))\n            return false;\n\n        if (clazz != null ? !clazz.equals(that.clazz) : that.clazz != null)\n            return false;\n        if (fieldName != null ? !fieldName.equals(that.fieldName) : that.fieldName != null)\n            return false;\n        return orderByType == that.orderByType;\n\n    }\n\n    @Override\n    public int hashCode() {\n        int result = clazz != null ? clazz.hashCode() : 0;\n        result = 31 * result + (fieldName != null ? fieldName.hashCode() : 0);\n        result = 31 * result + (orderByType != null ? orderByType.hashCode() : 0);\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/OrderByType.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\n/**\n * @author Charles Souillard\n */\npublic enum OrderByType {\n\n    ASC, DESC, ASC_NULLS_LAST, DESC_NULLS_FIRST, ASC_NULLS_FIRST, DESC_NULLS_LAST;\n\n    private String sqlKeyword;\n\n    static {\n        ASC.sqlKeyword = \"ASC\";\n        DESC.sqlKeyword = \"DESC\";\n        ASC_NULLS_LAST.sqlKeyword = \"ASC NULLS LAST\";\n        DESC_NULLS_FIRST.sqlKeyword = \"DESC NULLS FIRST\";\n        ASC_NULLS_FIRST.sqlKeyword = \"ASC NULLS FIRST\";\n        DESC_NULLS_LAST.sqlKeyword = \"DESC NULLS LAST\";\n\n    }\n\n    public String getSqlKeyword() {\n        return sqlKeyword;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/PersistentObject.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport java.io.Serializable;\n\n/**\n * Interface for all {@link PersistentObject}.\n *\n * @author Emmanuel Duchastenier\n */\npublic interface PersistentObject extends Serializable {\n\n    long getId();\n\n    void setId(long id);\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/PostgresInterceptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport org.hibernate.EmptyInterceptor;\n\n/**\n * Make search case insensitive in postgres by using `ilike` instead of `like`\n */\npublic class PostgresInterceptor extends EmptyInterceptor {\n\n    private static final long serialVersionUID = -6720122264417020259L;\n\n    @Override\n    public String onPrepareStatement(final String sql) {\n        return sql\n                //replace like by ilike in sql generated without prepared statements\n                .replace(\"like '\", \"ilike '\").replace(\"LIKE '\", \"ilike '\")\n                //replace like by ilike in sql generated with prepared statements\n                .replace(\"like ?\", \"ilike ?\").replace(\"LIKE ?\", \"ilike ?\");\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/PostgresMaterializedBlobType.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport org.hibernate.type.AbstractSingleColumnStandardBasicType;\nimport org.hibernate.type.descriptor.java.PrimitiveByteArrayTypeDescriptor;\nimport org.hibernate.type.descriptor.sql.BinaryTypeDescriptor;\n\n/**\n * @author Guillaume Rosinosky\n */\npublic class PostgresMaterializedBlobType extends AbstractSingleColumnStandardBasicType<byte[]> {\n\n    public PostgresMaterializedBlobType() {\n        // mapping binary to byte[] (for bytea type in Postgres)\n        super(BinaryTypeDescriptor.INSTANCE, PrimitiveByteArrayTypeDescriptor.INSTANCE);\n    }\n\n    @Override\n    public String getName() {\n        return \"materialized_blob\";\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/PostgresMaterializedClobType.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport org.hibernate.type.AbstractSingleColumnStandardBasicType;\nimport org.hibernate.type.descriptor.java.StringTypeDescriptor;\nimport org.hibernate.type.descriptor.sql.LongVarcharTypeDescriptor;\n\n/**\n * Custom Hibernate type for PostgreSQL TEXT fields.\n * In Hibernate 5.6+, PostgreSQL CLOB mapping changed from TEXT to OID type,\n * which causes issues with large text storage. This type forces TEXT columns\n * by using LongVarcharTypeDescriptor which maps to TEXT in PostgreSQL.\n *\n * @author Guillaume Rosinosky\n */\npublic class PostgresMaterializedClobType extends AbstractSingleColumnStandardBasicType<String> {\n\n    public PostgresMaterializedClobType() {\n        // Use LongVarcharTypeDescriptor which maps to TEXT type in PostgreSQL (not OID)\n        super(LongVarcharTypeDescriptor.INSTANCE, StringTypeDescriptor.INSTANCE);\n    }\n\n    @Override\n    public String getName() {\n        return \"materialized_clob\";\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/PostgresXMLType.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport java.io.Serializable;\n\nimport org.hibernate.type.AbstractSingleColumnStandardBasicType;\nimport org.hibernate.type.descriptor.sql.VarcharTypeDescriptor;\n\npublic class PostgresXMLType\n        extends AbstractSingleColumnStandardBasicType<Serializable> {\n\n    public static final PostgresXMLType INSTANCE = new PostgresXMLType();\n\n    public PostgresXMLType() {\n        // forcing VARCHAR to String as there is no real CLOB in PSQL\n        super(VarcharTypeDescriptor.INSTANCE, new XMLTypeDescriptor());\n    }\n\n    public String getName() {\n        return \"xml_blob\";\n    }\n\n    @Override\n    protected boolean registerUnderJavaType() {\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/QueryBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport static java.util.Collections.emptySet;\n\nimport java.util.*;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.hibernate.Session;\nimport org.hibernate.query.Query;\n\n/**\n * @author Baptiste Mesta\n */\n@Slf4j\nabstract class QueryBuilder<T> {\n\n    private final Query baseQuery;\n    private final OrderByCheckingMode orderByCheckingMode;\n    private final AbstractSelectDescriptor<T> selectDescriptor;\n    private final QueryGeneratorForFilters queryGeneratorForFilters;\n    private final QueryGeneratorForSearchTerm queryGeneratorForSearchTerm;\n    private final QueryGeneratorForOrderBy queryGeneratorForOrderBy;\n    StringBuilder stringQueryBuilder;\n    private Map<String, String> classAliasMappings;\n    private Session session;\n    private boolean cacheEnabled;\n    private Map<String, Object> parameters = new HashMap<>();\n\n    QueryBuilder(Session session, Query baseQuery, OrderByBuilder orderByBuilder,\n            Map<String, String> classAliasMappings,\n            char likeEscapeCharacter, OrderByCheckingMode orderByCheckingMode,\n            AbstractSelectDescriptor<T> selectDescriptor) {\n        this.session = session;\n        this.classAliasMappings = classAliasMappings;\n        stringQueryBuilder = new StringBuilder(baseQuery.getQueryString());\n        this.baseQuery = baseQuery;\n        this.orderByCheckingMode = orderByCheckingMode;\n        this.selectDescriptor = selectDescriptor;\n        this.queryGeneratorForFilters = new QueryGeneratorForFilters(classAliasMappings,\n                likeEscapeCharacter);\n        this.queryGeneratorForSearchTerm = new QueryGeneratorForSearchTerm(likeEscapeCharacter);\n        this.queryGeneratorForOrderBy = new QueryGeneratorForOrderBy(classAliasMappings, orderByBuilder);\n\n    }\n\n    public String getQuery() {\n        return stringQueryBuilder.toString();\n    }\n\n    void appendFilters(List<FilterOption> filters, SearchFields multipleFilter) {\n        Set<String> specificFilters = emptySet();\n        if (!filters.isEmpty()) {\n            if (!hasWHEREInRootQuery(stringQueryBuilder.toString())) {\n                stringQueryBuilder.append(\" WHERE (\");\n            } else {\n                stringQueryBuilder.append(\" AND (\");\n            }\n            QueryGeneratorForFilters.QueryGeneratedFilters whereClause = queryGeneratorForFilters.generate(filters);\n            specificFilters = whereClause.getSpecificFilters();\n            stringQueryBuilder.append(whereClause.getFilters());\n            stringQueryBuilder.append(\")\");\n            parameters.putAll(whereClause.getParameters());\n        }\n        if (multipleFilter != null && multipleFilter.getTerms() != null && !multipleFilter.getTerms().isEmpty()) {\n            handleMultipleFilters(stringQueryBuilder, multipleFilter, specificFilters);\n        }\n    }\n\n    static boolean hasWHEREInRootQuery(String query) {\n        // We simply remove all blocks that are in parenthesis in order to remove all subqueries\n        // Then we check if there is the `where` word here\n        return removeAllParenthesisBlocks(query.toLowerCase()).contains(\"where\");\n    }\n\n    private static String removeAllParenthesisBlocks(String q) {\n        StringBuilder stringBuilder = new StringBuilder(q.length());\n        int depthCounter = 0;\n        for (char c : q.toCharArray()) {\n            switch (c) {\n                case '(':\n                    depthCounter++;\n                    break;\n                case ')':\n                    depthCounter--;\n                    break;\n                default:\n                    if (depthCounter == 0) {\n                        stringBuilder.append(c);\n                    }\n            }\n        }\n        return stringBuilder.toString();\n    }\n\n    private void handleMultipleFilters(final StringBuilder builder, final SearchFields multipleFilter,\n            final Set<String> specificFilters) {\n        final Map<Class<? extends PersistentObject>, Set<String>> allTextFields = multipleFilter.getFields();\n        final Set<String> fields = new HashSet<>();\n        for (final Map.Entry<Class<? extends PersistentObject>, Set<String>> entry : allTextFields.entrySet()) {\n            final String alias = classAliasMappings.get(entry.getKey().getName());\n            for (final String field : entry.getValue()) {\n                fields.add(alias + '.' + field);\n            }\n        }\n        fields.removeAll(specificFilters);\n\n        if (!fields.isEmpty()) {\n            final List<String> terms = multipleFilter.getTerms();\n            applyFiltersOnQuery(builder, fields, terms);\n        }\n    }\n\n    private void applyFiltersOnQuery(final StringBuilder queryBuilder, final Set<String> fields,\n            final List<String> terms) {\n        if (!hasWHEREInRootQuery(queryBuilder.toString())) {\n            queryBuilder.append(\" WHERE \");\n        } else {\n            queryBuilder.append(\" AND \");\n        }\n        queryBuilder.append(\"(\");\n\n        QueryGeneratorForSearchTerm.QueryGeneratedSearchTerms result = queryGeneratorForSearchTerm.generate(fields,\n                terms);\n        queryBuilder.append(result.getSearch());\n\n        queryBuilder.append(\")\");\n        parameters.putAll(result.getParameters());\n    }\n\n    void appendOrderByClause(List<OrderByOption> orderByOptions, Class<? extends PersistentObject> entityType)\n            throws SBonitaReadException {\n        String result = queryGeneratorForOrderBy.generate(orderByOptions, entityType);\n        stringQueryBuilder.append(result);\n    }\n\n    boolean hasChanged() {\n        return !baseQuery.getQueryString().equals(stringQueryBuilder.toString());\n    }\n\n    abstract Query rebuildQuery(AbstractSelectDescriptor<T> selectDescriptor, Session session, Query query);\n\n    void manageFiltersAndParameters(AbstractSelectDescriptor<T> selectDescriptor)\n            throws SBonitaReadException {\n        if (selectDescriptor.hasAFilter()) {\n            final QueryOptions queryOptions = selectDescriptor.getQueryOptions();\n            appendFilters(queryOptions.getFilters(), queryOptions.getMultipleFilter());\n        }\n        if (selectDescriptor.hasOrderByParameters()) {\n            appendOrderByClause(selectDescriptor.getQueryOptions().getOrderByOptions(),\n                    selectDescriptor.getEntityType());\n        }\n    }\n\n    public QueryBuilder cache(boolean cacheEnabled) {\n        this.cacheEnabled = cacheEnabled;\n        return this;\n    }\n\n    private void setParameters(final Query query, final Map<String, Object> inputParameters) {\n        for (final Map.Entry<String, Object> entry : inputParameters.entrySet()) {\n            final Object value = entry.getValue();\n            if (value instanceof Collection<?>) {\n                query.setParameterList(entry.getKey(), (Collection<?>) value);\n            } else {\n                query.setParameter(entry.getKey(), value);\n            }\n        }\n    }\n\n    public Query build() throws SBonitaReadException {\n        manageFiltersAndParameters(selectDescriptor);\n        Query query = baseQuery;\n        if (hasChanged()) {\n            query = rebuildQuery(selectDescriptor, session, baseQuery);\n        }\n        addConstantsAsParameters(query);\n        setParameters(query, selectDescriptor.getInputParameters());\n        query.setFirstResult(selectDescriptor.getStartIndex());\n        query.setMaxResults(selectDescriptor.getPageSize());\n        query.setCacheable(cacheEnabled);\n        checkOrderByClause(query);\n        return query;\n    }\n\n    protected abstract void addConstantsAsParameters(Query query);\n\n    private void checkOrderByClause(final Query query) {\n        if (!query.getQueryString().toLowerCase().contains(\"order by\")) {\n            switch (orderByCheckingMode) {\n                case NONE:\n                    break;\n                case WARNING:\n                    log.warn(\n                            \"Query '{}' does not contain 'ORDER BY' clause. It's better to modify your query to order\" +\n                                    \" the result, especially if you use the pagination.\",\n                            query.getQueryString());\n                    break;\n                case STRICT:\n                default:\n                    throw new IllegalArgumentException(\"Query \" + query.getQueryString()\n                            + \" does not contain 'ORDER BY' clause hence is not allowed. Please specify ordering before re-sending the query\");\n            }\n        }\n    }\n\n    /*\n     * escape for other things than like\n     */\n    static String escapeString(final String term) {\n        // 1) escape ' character by adding another ' character\n        return term.replaceAll(\"'\", \"''\");\n    }\n\n    /*\n     * escape for like\n     */\n    static String escapeTerm(final String term, String likeEscapeCharacter) {\n        // 1) protect escape character if this character is used in data\n        // 2) escape % character (sql query wildcard) by adding escape character\n        // 3) escape _ character (sql query wildcard) by adding escape character\n        return term\n                .replace(likeEscapeCharacter, likeEscapeCharacter + likeEscapeCharacter)\n                .replace(\"%\", likeEscapeCharacter + \"%\")\n                .replace(\"_\", likeEscapeCharacter + \"_\");\n    }\n\n    Map<String, Object> getQueryParameters() {\n        return parameters;\n    }\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/QueryBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport java.util.Map;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.hibernate.Session;\nimport org.hibernate.query.NativeQuery;\nimport org.hibernate.query.Query;\n\n/**\n * @author Baptiste Mesta\n */\n@Slf4j\npublic class QueryBuilderFactory {\n\n    private OrderByCheckingMode orderByCheckingMode;\n    private OrderByBuilder orderByBuilder = new DefaultOrderByBuilder();\n    private Map<String, String> classAliasMappings;\n    private char likeEscapeCharacter;\n\n    public QueryBuilderFactory(OrderByCheckingMode orderByCheckingMode, Map<String, String> classAliasMappings,\n            char likeEscapeCharacter)\n            throws Exception {\n        this.orderByCheckingMode = orderByCheckingMode;\n        this.classAliasMappings = classAliasMappings;\n        this.likeEscapeCharacter = likeEscapeCharacter;\n    }\n\n    public <T> QueryBuilder createQueryBuilderFor(Session session,\n            SelectListDescriptor<T> selectDescriptor) {\n        Query query = session.getNamedQuery(selectDescriptor.getQueryName());\n        if (query instanceof NativeQuery) {\n            return new SQLQueryBuilder<>(session, query, orderByBuilder, classAliasMappings,\n                    likeEscapeCharacter,\n                    orderByCheckingMode, selectDescriptor);\n        } else {\n            return new HQLQueryBuilder<>(session, query, orderByBuilder, classAliasMappings, likeEscapeCharacter,\n                    orderByCheckingMode, selectDescriptor);\n        }\n    }\n\n    public void setOrderByBuilder(OrderByBuilder orderByBuilder) {\n        this.orderByBuilder = orderByBuilder;\n    }\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/QueryGeneratorForFilters.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport static org.bonitasoft.engine.persistence.QueryBuilder.escapeTerm;\nimport static org.bonitasoft.engine.persistence.search.FilterOperationType.L_PARENTHESIS;\nimport static org.bonitasoft.engine.persistence.search.FilterOperationType.R_PARENTHESIS;\nimport static org.bonitasoft.engine.persistence.search.FilterOperationType.isNormalOperator;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport org.bonitasoft.engine.persistence.search.FilterOperationType;\n\nclass QueryGeneratorForFilters {\n\n    private Map<String, String> classAliasMappings;\n    private String likeEscapeCharacter;\n    private int parameterCounter = 1;\n    private Map<String, Object> parameters = new HashMap<>();\n\n    QueryGeneratorForFilters(Map<String, String> classAliasMappings, char likeEscapeCharacter) {\n        this.classAliasMappings = classAliasMappings;\n        this.likeEscapeCharacter = String.valueOf(likeEscapeCharacter);\n    }\n\n    private StringBuilder appendFilterClause(final StringBuilder clause, final FilterOption filterOption) {\n        final FilterOperationType type = filterOption.getFilterOperationType();\n        StringBuilder completeField = null;\n        if (filterOption.getPersistentClass() != null) {\n            completeField = new StringBuilder(classAliasMappings.get(filterOption.getPersistentClass().getName()))\n                    .append('.').append(\n                            filterOption.getFieldName());\n        }\n        Object fieldValue = filterOption.getValue();\n        switch (type) {\n            case EQUALS:\n                if (fieldValue == null) {\n                    clause.append(completeField).append(\" IS NULL\");\n                } else {\n                    clause.append(completeField).append(\" = \").append(createParameter(fieldValue));\n                }\n                break;\n            case GREATER:\n                clause.append(completeField).append(\" > \").append(createParameter(fieldValue));\n                break;\n            case GREATER_OR_EQUALS:\n                clause.append(completeField).append(\" >= \").append(createParameter(fieldValue));\n                break;\n            case LESS:\n                clause.append(completeField).append(\" < \").append(createParameter(fieldValue));\n                break;\n            case LESS_OR_EQUALS:\n                clause.append(completeField).append(\" <= \").append(createParameter(fieldValue));\n                break;\n            case DIFFERENT:\n                if (fieldValue == null) {\n                    clause.append(completeField).append(\" IS NOT NULL\");\n                } else {\n                    clause.append(completeField).append(\" != \").append(createParameter(fieldValue));\n                }\n                break;\n            case BETWEEN:\n                // eg. ('fromValue' <= p.myField AND p.myField <= 'toValue')\n                clause.append(\"(\").append(createParameter(filterOption.getFrom())).append(\" <= \").append(completeField);\n                clause.append(\" AND \").append(completeField).append(\" <= \")\n                        .append(createParameter(filterOption.getTo())).append(\")\");\n                break;\n            case LIKE:\n                clause.append(completeField).append(\" LIKE \")\n                        .append(createParameter(\n                                \"%\" + escapeTerm((String) filterOption.getValue(), likeEscapeCharacter) + \"%\"))\n                        .append(\" ESCAPE '\").append(likeEscapeCharacter).append(\"'\");\n                break;\n            case L_PARENTHESIS:\n                clause.append(\" (\");\n                break;\n            case R_PARENTHESIS:\n                clause.append(\" )\");\n                break;\n            case AND:\n                clause.append(\" AND \");\n                break;\n            case OR:\n                clause.append(\" OR \");\n                break;\n            default:\n                break;\n        }\n        return completeField;\n    }\n\n    private String createParameter(Object fieldValue) {\n        final String parameterName = \"f\" + parameterCounter++;\n        parameters.put(parameterName, fieldValue);\n        return \":\" + parameterName;\n    }\n\n    /**\n     * generate a HQL/SQL condition given the filters\n     *\n     * @return a tuple containing the genereted where clause and the fields it filters on\n     */\n    public QueryGeneratedFilters generate(List<FilterOption> filters) {\n        Set<String> specificFilters = new HashSet<>();\n        FilterOption previousFilter = null;\n        StringBuilder filtersStringBuilder = new StringBuilder();\n        for (final FilterOption filterOption : filters) {\n            if (previousFilter != null) {\n                final FilterOperationType prevOp = previousFilter.getFilterOperationType();\n                final FilterOperationType currOp = filterOption.getFilterOperationType();\n                // Auto add AND if previous operator was normal op or ')' and that current op is normal op or '(' :\n                if ((isNormalOperator(prevOp) || prevOp == R_PARENTHESIS)\n                        && (isNormalOperator(currOp) || currOp == L_PARENTHESIS)) {\n                    filtersStringBuilder.append(\" AND \");\n                }\n            }\n            final StringBuilder aliasBuilder = appendFilterClause(filtersStringBuilder, filterOption);\n            if (aliasBuilder != null) {\n                specificFilters.add(aliasBuilder.toString());\n            }\n            previousFilter = filterOption;\n        }\n        return new QueryGeneratedFilters(filtersStringBuilder.toString(), specificFilters, parameters);\n    }\n\n    @Data\n    @AllArgsConstructor\n    static final class QueryGeneratedFilters {\n\n        private String filters;\n        private Set<String> specificFilters;\n        private Map<String, Object> parameters;\n    }\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/QueryGeneratorForOrderBy.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport java.util.List;\nimport java.util.Map;\n\npublic class QueryGeneratorForOrderBy {\n\n    private Map<String, String> classAliasMappings;\n    private OrderByBuilder orderByBuilder;\n\n    public QueryGeneratorForOrderBy(Map<String, String> classAliasMappings, OrderByBuilder orderByBuilder) {\n\n        this.classAliasMappings = classAliasMappings;\n        this.orderByBuilder = orderByBuilder;\n    }\n\n    void appendClassAlias(final StringBuilder builder, final Class<? extends PersistentObject> clazz)\n            throws SBonitaReadException {\n        final String className = clazz.getName();\n        final String classAlias = classAliasMappings.get(className);\n        if (classAlias == null || classAlias.trim().isEmpty()) {\n            throw new SBonitaReadException(\"No class alias found for class \" + className);\n        }\n        builder.append(classAlias);\n        builder.append('.');\n    }\n\n    String generate(List<OrderByOption> orderByOptions, Class<? extends PersistentObject> entityType)\n            throws SBonitaReadException {\n        StringBuilder stringBuilder = new StringBuilder();\n        stringBuilder.append(\" ORDER BY \");\n        boolean startWithComma = false;\n        boolean sortedById = false;\n        for (final OrderByOption orderByOption : orderByOptions) {\n            if (startWithComma) {\n                stringBuilder.append(',');\n            }\n            StringBuilder fieldNameBuilder = new StringBuilder();\n            final Class<? extends PersistentObject> clazz = orderByOption.getClazz();\n            if (clazz != null) {\n                appendClassAlias(fieldNameBuilder, clazz);\n            }\n            final String fieldName = orderByOption.getFieldName();\n            if (\"id\".equalsIgnoreCase(fieldName) || \"sourceObjectId\".equalsIgnoreCase(fieldName)) {\n                sortedById = true;\n            }\n            fieldNameBuilder.append(fieldName);\n            orderByBuilder.appendOrderBy(stringBuilder, fieldNameBuilder.toString(),\n                    orderByOption.getOrderByType());\n            startWithComma = true;\n        }\n        if (!sortedById) {\n            if (startWithComma) {\n                stringBuilder.append(',');\n            }\n            appendClassAlias(stringBuilder, entityType);\n            stringBuilder.append(\"id\");\n            stringBuilder.append(' ');\n            stringBuilder.append(\"ASC\");\n        }\n        return stringBuilder.toString();\n    }\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/QueryGeneratorForSearchTerm.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport static org.bonitasoft.engine.persistence.QueryBuilder.escapeTerm;\n\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\n\nclass QueryGeneratorForSearchTerm {\n\n    private String likeEscapeCharacter;\n    private int parameterCounter = 1;\n    private Map<String, Object> parameters = new HashMap<>();\n\n    QueryGeneratorForSearchTerm(char likeEscapeCharacter) {\n        this.likeEscapeCharacter = String.valueOf(likeEscapeCharacter);\n    }\n\n    private String createParameter(Object fieldValue) {\n        final String parameterName = \"s\" + parameterCounter++;\n        parameters.put(parameterName, fieldValue);\n        return \":\" + parameterName;\n    }\n\n    /**\n     * Get like clause for given term with escaped sql query wildcards and escape character\n     */\n    private String buildLikeEscapeClause(String term) {\n        return \" LIKE \" + createParameter(term)\n                + \" ESCAPE '\" + likeEscapeCharacter + \"'\";\n    }\n\n    void buildLikeClauseForOneFieldOneTerm(final StringBuilder queryBuilder, final String currentField,\n            final String currentTerm) {\n        // We do not want to search for %currentTerm% to ensure we can use Lucene-like library.\n        queryBuilder.append(currentField)\n                .append(buildLikeEscapeClause(\"%\" + escapeTerm(currentTerm, likeEscapeCharacter) + \"%\"));\n    }\n\n    private void buildLikeClauseForOneFieldMultipleTerms(final StringBuilder queryBuilder, final String currentField,\n            final List<String> terms) {\n        final Iterator<String> termIterator = terms.iterator();\n        while (termIterator.hasNext()) {\n            final String currentTerm = termIterator.next();\n\n            buildLikeClauseForOneFieldOneTerm(queryBuilder, currentField, currentTerm);\n\n            if (termIterator.hasNext()) {\n                queryBuilder.append(\" OR \");\n            }\n        }\n    }\n\n    QueryGeneratedSearchTerms generate(Set<String> fields, List<String> terms) {\n        StringBuilder stringBuilder = new StringBuilder();\n        final Iterator<String> fieldIterator = fields.iterator();\n        while (fieldIterator.hasNext()) {\n            buildLikeClauseForOneFieldMultipleTerms(stringBuilder, fieldIterator.next(), terms);\n            if (fieldIterator.hasNext()) {\n                stringBuilder.append(\" OR \");\n            }\n        }\n        return new QueryGeneratedSearchTerms(stringBuilder.toString(), parameters);\n    }\n\n    @Data\n    @AllArgsConstructor\n    static final class QueryGeneratedSearchTerms {\n\n        private String search;\n        private Map<String, Object> parameters;\n    }\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/QueryOptions.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\n/**\n * @author Charles Souillard\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n */\npublic class QueryOptions implements Serializable {\n\n    private static final long serialVersionUID = 8923754215920928153L;\n\n    private final int fromIndex;\n\n    private final int numberOfResults;\n\n    private final List<FilterOption> filters;\n\n    private final SearchFields multipleFilter;\n\n    private final List<OrderByOption> orderByOptions;\n\n    public static final int UNLIMITED_NUMBER_OF_RESULTS = Integer.MAX_VALUE;\n\n    public static final QueryOptions ALL_RESULTS = new QueryOptions(0, UNLIMITED_NUMBER_OF_RESULTS);\n    public static final QueryOptions ONE_RESULT = new QueryOptions(0, 1);\n\n    public QueryOptions(final QueryOptions queryOptions) {\n        super();\n        fromIndex = queryOptions.getFromIndex();\n        numberOfResults = queryOptions.getNumberOfResults();\n        orderByOptions = queryOptions.getOrderByOptions();\n        filters = queryOptions.getFilters();\n        multipleFilter = queryOptions.getMultipleFilter();\n    }\n\n    /**\n     * Just for get number of elements on a table, or if the request is already ordered\n     */\n    public QueryOptions(final int fromIndex, final int numberOfResults) {\n        super();\n        this.fromIndex = fromIndex;\n        this.numberOfResults = numberOfResults;\n        orderByOptions = Collections.emptyList();\n        filters = Collections.emptyList();\n        multipleFilter = null;\n    }\n\n    public QueryOptions(final int fromIndex, final int numberOfResults, final List<OrderByOption> orderByOptions) {\n        super();\n        this.fromIndex = fromIndex;\n        this.numberOfResults = numberOfResults;\n        this.orderByOptions = orderByOptions;\n        filters = Collections.emptyList();\n        multipleFilter = null;\n    }\n\n    public QueryOptions(final int fromIndex, final int numberOfResults, final List<OrderByOption> orderByOptions,\n            final List<FilterOption> filters,\n            final SearchFields multipleFilter) {\n        super();\n        this.fromIndex = fromIndex;\n        this.numberOfResults = numberOfResults;\n        this.orderByOptions = orderByOptions;\n        this.filters = filters;\n        this.multipleFilter = multipleFilter;\n    }\n\n    /**\n     * Just for get number of elements on a table\n     */\n    public QueryOptions(final List<FilterOption> filters, final SearchFields multipleFilter) {\n        this(0, UNLIMITED_NUMBER_OF_RESULTS, Collections.<OrderByOption> emptyList(), filters, multipleFilter);\n    }\n\n    public QueryOptions(final int fromIndex, final int numberOfResults, final Class<? extends PersistentObject> clazz,\n            final String fieldName,\n            final OrderByType orderByType) {\n        super();\n        this.fromIndex = fromIndex;\n        this.numberOfResults = numberOfResults;\n        if (fieldName == null || orderByType == null) {\n            orderByOptions = Collections.emptyList();\n        } else {\n            orderByOptions = new ArrayList<OrderByOption>();\n            orderByOptions.add(new OrderByOption(clazz, fieldName, orderByType));\n        }\n        filters = Collections.emptyList();\n        multipleFilter = null;\n    }\n\n    @Deprecated\n    public QueryOptions(final List<OrderByOption> orderByOptions) {\n        super();\n        fromIndex = 0;\n        numberOfResults = UNLIMITED_NUMBER_OF_RESULTS;\n        this.orderByOptions = orderByOptions;\n        filters = Collections.emptyList();\n        multipleFilter = null;\n    }\n\n    @Deprecated\n    public QueryOptions(final Class<? extends PersistentObject> clazz, final String fieldName,\n            final OrderByType orderByType) {\n        super();\n        fromIndex = 0;\n        numberOfResults = UNLIMITED_NUMBER_OF_RESULTS;\n        orderByOptions = new ArrayList<OrderByOption>();\n        orderByOptions.add(new OrderByOption(clazz, fieldName, orderByType));\n        filters = Collections.emptyList();\n        multipleFilter = null;\n    }\n\n    public int getFromIndex() {\n        return fromIndex;\n    }\n\n    public int getNumberOfResults() {\n        return numberOfResults;\n    }\n\n    public List<FilterOption> getFilters() {\n        return filters;\n    }\n\n    public SearchFields getMultipleFilter() {\n        return multipleFilter;\n    }\n\n    public List<OrderByOption> getOrderByOptions() {\n        return orderByOptions;\n    }\n\n    public boolean hasOrderByOptions() {\n        return orderByOptions != null && !orderByOptions.isEmpty();\n    }\n\n    /**\n     * Just for get number of elements on a table\n     */\n    public static QueryOptions countQueryOptions() {\n        return ALL_RESULTS;\n    }\n\n    public static QueryOptions getNextPage(final QueryOptions queryOptions) {\n        return new QueryOptions(queryOptions.getFromIndex() + queryOptions.getNumberOfResults(),\n                queryOptions.getNumberOfResults(),\n                queryOptions.getOrderByOptions(), queryOptions.getFilters(), queryOptions.getMultipleFilter());\n    }\n\n    @Override\n    public String toString() {\n        return \"QueryOptions [fromIndex=\" + fromIndex + \", numberOfResults=\" + numberOfResults + \", orderByOptions=\"\n                + orderByOptions + \"]\";\n    }\n\n    public boolean hasAFilter() {\n        return filters != null && !filters.isEmpty() || multipleFilter != null;\n    }\n\n    @Override\n    public boolean equals(final Object o) {\n        if (this == o) {\n            return true;\n        }\n        if (!(o instanceof QueryOptions that)) {\n            return false;\n        }\n\n        if (fromIndex != that.fromIndex) {\n            return false;\n        }\n        if (numberOfResults != that.numberOfResults) {\n            return false;\n        }\n        if (filters != null ? !filters.equals(that.filters) : that.filters != null) {\n            return false;\n        }\n        if (multipleFilter != null ? !multipleFilter.equals(that.multipleFilter) : that.multipleFilter != null) {\n            return false;\n        }\n        if (orderByOptions != null ? !orderByOptions.equals(that.orderByOptions) : that.orderByOptions != null) {\n            return false;\n        }\n\n        return true;\n    }\n\n    @Override\n    public int hashCode() {\n        int result = fromIndex;\n        result = 31 * result + numberOfResults;\n        result = 31 * result + (filters != null ? filters.hashCode() : 0);\n        result = 31 * result + (multipleFilter != null ? multipleFilter.hashCode() : 0);\n        result = 31 * result + (orderByOptions != null ? orderByOptions.hashCode() : 0);\n        return result;\n    }\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/ReadOnlySelectByIdDescriptor.java",
    "content": "/**\n * Copyright (C) 2017 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic class ReadOnlySelectByIdDescriptor<T extends PersistentObject> extends SelectByIdDescriptor<T> {\n\n    public ReadOnlySelectByIdDescriptor(Class<T> entityType, long id) {\n        super(entityType, id, true);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/ReadPersistenceService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.commons.exceptions.SRetryableException;\n\n/**\n * @author Charles Souillard\n * @author Matthieu Chaffotte\n */\npublic interface ReadPersistenceService {\n\n    /**\n     * @param selectDescriptor\n     * @return\n     * @throws SBonitaReadException\n     * @throws SRetryableException\n     */\n    <T extends PersistentObject> T selectById(SelectByIdDescriptor<T> selectDescriptor) throws SBonitaReadException;\n\n    /**\n     * @param selectDescriptor\n     * @return\n     * @throws SBonitaReadException\n     * @throws SRetryableException\n     */\n    <T> T selectOne(SelectOneDescriptor<T> selectDescriptor) throws SBonitaReadException;\n\n    /**\n     * @param selectDescriptor\n     * @return\n     * @throws SBonitaReadException\n     * @throws SRetryableException\n     */\n    <T> List<T> selectList(SelectListDescriptor<T> selectDescriptor) throws SBonitaReadException;\n\n    /**\n     * @param entityClass\n     * @param options\n     * @param parameters\n     * @return\n     * @throws SBonitaReadException\n     * @throws SRetryableException\n     */\n    <T extends PersistentObject> long getNumberOfEntities(Class<T> entityClass, QueryOptions options,\n            Map<String, Object> parameters)\n            throws SBonitaReadException;\n\n    /**\n     * @param entityClass\n     * @param querySuffix\n     * @param options\n     * @param parameters\n     * @return\n     * @throws SBonitaReadException\n     * @throws SRetryableException\n     */\n    <T extends PersistentObject> long getNumberOfEntities(Class<T> entityClass, String querySuffix,\n            QueryOptions options, Map<String, Object> parameters)\n            throws SBonitaReadException;\n\n    /**\n     * @param entityClass\n     * @param options\n     * @param parameters\n     * @return\n     * @throws SBonitaReadException\n     * @throws SRetryableException\n     */\n    <T extends PersistentObject> List<T> searchEntity(Class<T> entityClass, QueryOptions options,\n            Map<String, Object> parameters)\n            throws SBonitaReadException;\n\n    /**\n     * @param entityClass\n     *        class of the object we want to search on\n     * @param querySuffix\n     *        Used to define customized search query\n     * @param options\n     *        query options\n     * @param parameters\n     * @return\n     * @throws SBonitaReadException\n     * @throws SRetryableException\n     */\n    <T extends PersistentObject> List<T> searchEntity(Class<T> entityClass, String querySuffix, QueryOptions options,\n            Map<String, Object> parameters)\n            throws SBonitaReadException;\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/SBonitaReadException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Charles Souillard\n */\npublic class SBonitaReadException extends SBonitaException {\n\n    private static final long serialVersionUID = 1L;\n\n    private final AbstractSelectDescriptor<?> selectDescriptor;\n\n    public SBonitaReadException(final Throwable cause, final AbstractSelectDescriptor<?> selectDescriptor) {\n        super(cause);\n        this.selectDescriptor = selectDescriptor;\n    }\n\n    public SBonitaReadException(final String message, final Throwable cause,\n            final AbstractSelectDescriptor<?> selectDescriptor) {\n        super(message, cause);\n        this.selectDescriptor = selectDescriptor;\n    }\n\n    public SBonitaReadException(final String message) {\n        super(message);\n        selectDescriptor = null;\n    }\n\n    public SBonitaReadException(final Throwable cause) {\n        super(cause);\n        selectDescriptor = null;\n    }\n\n    public SBonitaReadException(final String message, final Throwable cause) {\n        super(message, cause);\n        selectDescriptor = null;\n    }\n\n    public AbstractSelectDescriptor<?> getSelectDescriptor() {\n        return selectDescriptor;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/SQLQueryBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.hibernate.Session;\nimport org.hibernate.engine.query.spi.sql.NativeSQLQueryReturn;\nimport org.hibernate.engine.query.spi.sql.NativeSQLQueryRootReturn;\nimport org.hibernate.engine.query.spi.sql.NativeSQLQueryScalarReturn;\nimport org.hibernate.query.NativeQuery;\nimport org.hibernate.query.Query;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SQLQueryBuilder<T> extends QueryBuilder<T> {\n\n    public static final String TRUE_VALUE_PARAMETER = \"trueValue\";\n    private static Map<String, String> hqlToSqlAlias = new HashMap<>();\n\n    static {\n        hqlToSqlAlias.put(\"user\", \"user_\");\n    }\n\n    SQLQueryBuilder(Session session, Query baseQuery,\n            OrderByBuilder orderByBuilder,\n            Map<String, String> classAliasMappings, char likeEscapeCharacter,\n            OrderByCheckingMode orderByCheckingMode,\n            SelectListDescriptor<T> selectDescriptor) {\n        super(session, baseQuery, orderByBuilder, classAliasMappings, likeEscapeCharacter,\n                orderByCheckingMode,\n                selectDescriptor);\n    }\n\n    public void addConstantsAsParameters(Query sqlQuery) {\n        if (sqlQuery.getQueryString().contains(\":\" + TRUE_VALUE_PARAMETER)) {\n            // there is no need to convert the true value to a integer for Oracle and Sqlserver, hibernate does that already.\n            sqlQuery.setParameter(TRUE_VALUE_PARAMETER, true);\n        }\n    }\n\n    private String replaceHQLAliasesBySQLAliases(String builtQuery) {\n        for (Map.Entry<String, String> aliasToReplace : hqlToSqlAlias.entrySet()) {\n            if (builtQuery.contains(aliasToReplace.getKey() + \".\")) {\n                builtQuery = builtQuery.replace(aliasToReplace.getKey() + \".\", aliasToReplace.getValue() + \".\");\n            }\n        }\n        return builtQuery;\n    }\n\n    @Override\n    Query rebuildQuery(AbstractSelectDescriptor<T> selectDescriptor, Session session, Query query) {\n        String builtQuery = stringQueryBuilder.toString();\n        builtQuery = replaceHQLAliasesBySQLAliases(builtQuery);\n        NativeQuery generatedSqlQuery = session.createSQLQuery(builtQuery);\n        for (Map.Entry<String, Object> parameter : getQueryParameters().entrySet()) {\n            generatedSqlQuery.setParameter(parameter.getKey(), parameter.getValue());\n        }\n        for (NativeSQLQueryReturn queryReturn : (List<NativeSQLQueryReturn>) ((NativeQuery) query)\n                .getQueryReturns()) {\n            if (queryReturn instanceof NativeSQLQueryScalarReturn) {\n                generatedSqlQuery.addScalar(((NativeSQLQueryScalarReturn) queryReturn).getColumnAlias(),\n                        ((NativeSQLQueryScalarReturn) queryReturn).getType());\n            } else if (queryReturn instanceof NativeSQLQueryRootReturn) {\n                generatedSqlQuery.addEntity(((NativeSQLQueryRootReturn) queryReturn).getAlias(),\n                        ((NativeSQLQueryRootReturn) queryReturn).getReturnEntityName());\n            } else {\n                throw new IllegalStateException(\n                        \"Not yet implemented. Query return type \" + queryReturn.getClass().getName());\n            }\n        }\n        return generatedSqlQuery;\n    }\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/SQLTransformer.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\n/**\n * @author Baptiste Mesta\n */\npublic abstract class SQLTransformer {\n\n    public SQLTransformer() {\n    }\n\n    /**\n     * @return\n     */\n    public String getDeleteScript() {\n        return null;\n    }\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/SearchFields.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SearchFields implements Serializable {\n\n    private static final long serialVersionUID = -9157314460334148998L;\n\n    private final List<String> terms;\n\n    private final Map<Class<? extends PersistentObject>, Set<String>> fields;\n\n    public SearchFields(final List<String> terms, final Map<Class<? extends PersistentObject>, Set<String>> fields) {\n        super();\n        this.terms = terms;\n        this.fields = fields;\n    }\n\n    public List<String> getTerms() {\n        return terms;\n    }\n\n    public Map<Class<? extends PersistentObject>, Set<String>> getFields() {\n        return fields;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/SelectByIdDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport static java.util.Collections.emptyMap;\nimport static org.bonitasoft.engine.persistence.QueryOptions.ONE_RESULT;\n\nimport java.util.Map;\n\n/**\n * @author Charles Souillard\n * @author Matthieu Chaffotte\n * @author Emmanuel Duchastenier\n */\npublic class SelectByIdDescriptor<T extends PersistentObject> extends AbstractSelectDescriptor<T> {\n\n    private final long id;\n    private boolean readonly;\n\n    public SelectByIdDescriptor(final Class<T> entityType, final long id) {\n        this(entityType, id, false);\n    }\n\n    public SelectByIdDescriptor(final Class<T> entityType, final long id, final boolean readonly) {\n        super(null, entityType, entityType);\n        this.id = id;\n        this.readonly = readonly;\n    }\n\n    public long getId() {\n        return id;\n    }\n\n    boolean isReadOnly() {\n        return readonly;\n    }\n\n    @Override\n    public Map<String, Object> getInputParameters() {\n        return emptyMap();\n    }\n\n    @Override\n    public int getStartIndex() {\n        return 0;\n    }\n\n    @Override\n    public int getPageSize() {\n        return 1;\n    }\n\n    @Override\n    public boolean hasAFilter() {\n        return false;\n    }\n\n    @Override\n    public QueryOptions getQueryOptions() {\n        return ONE_RESULT;\n    }\n\n    @Override\n    public boolean hasOrderByParameters() {\n        return false;\n    }\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/SelectListDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport java.util.Map;\n\n/**\n * @author Charles Souillard\n * @author Matthieu Chaffotte\n */\npublic class SelectListDescriptor<T> extends AbstractSelectWithParametersDescriptor<T> {\n\n    private final QueryOptions queryOptions;\n\n    public SelectListDescriptor(final String queryName, final Map<String, Object> inputParameters,\n            final Class<? extends PersistentObject> entityType,\n            final QueryOptions queryOptions) {\n        super(queryName, inputParameters, entityType, (Class<T>) entityType);\n        if (queryOptions != null) {\n            this.queryOptions = queryOptions;\n        } else {\n            throw new IllegalArgumentException(\"Need to have a query option to paginate and order the results.\");\n        }\n    }\n\n    public SelectListDescriptor(final String queryName, final Map<String, Object> inputParameters,\n            final Class<? extends PersistentObject> entityType,\n            final Class<T> returnType, final QueryOptions queryOptions) {\n        super(queryName, inputParameters, entityType, returnType);\n        if (queryOptions != null) {\n            this.queryOptions = queryOptions;\n        } else {\n            throw new IllegalArgumentException(\"Need to have a query option to paginate and order the results.\");\n        }\n    }\n\n    public QueryOptions getQueryOptions() {\n        return queryOptions;\n    }\n\n    public boolean hasOrderByParameters() {\n        return queryOptions.hasOrderByOptions();\n    }\n\n    public int getStartIndex() {\n        return queryOptions.getFromIndex();\n    }\n\n    public int getPageSize() {\n        return queryOptions.getNumberOfResults();\n    }\n\n    public boolean hasAFilter() {\n        return queryOptions.hasAFilter();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/SelectOneDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport java.util.Map;\n\n/**\n * @author Charles Souillard\n */\npublic final class SelectOneDescriptor<T> extends AbstractSelectWithParametersDescriptor<T> {\n\n    public SelectOneDescriptor(final String queryName, final Map<String, Object> inputParameters,\n            final Class<? extends PersistentObject> entityType) {\n        super(queryName, inputParameters, entityType, (Class<T>) entityType);\n    }\n\n    public SelectOneDescriptor(final String queryName, final Map<String, Object> inputParameters,\n            final Class<? extends PersistentObject> entityType,\n            final Class<T> returnType) {\n        super(queryName, inputParameters, entityType, returnType);\n    }\n\n    @Override\n    public int getStartIndex() {\n        return 0;\n    }\n\n    @Override\n    public int getPageSize() {\n        return 1;\n    }\n\n    @Override\n    public boolean hasAFilter() {\n        return false;\n    }\n\n    @Override\n    public QueryOptions getQueryOptions() {\n        return QueryOptions.ONE_RESULT;\n    }\n\n    @Override\n    public boolean hasOrderByParameters() {\n        return false;\n    }\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/XMLType.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport java.io.Serializable;\n\nimport org.hibernate.type.AbstractSingleColumnStandardBasicType;\nimport org.hibernate.type.descriptor.sql.ClobTypeDescriptor;\n\npublic class XMLType extends AbstractSingleColumnStandardBasicType<Serializable> {\n\n    public static final XMLType INSTANCE = new XMLType();\n\n    public XMLType() {\n        super(ClobTypeDescriptor.DEFAULT, new XMLTypeDescriptor());\n    }\n\n    public String getName() {\n        return \"xml_blob\";\n    }\n\n    @Override\n    protected boolean registerUnderJavaType() {\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/XMLTypeDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport java.io.Reader;\nimport java.io.Serializable;\nimport java.io.StringReader;\nimport java.sql.Clob;\nimport java.sql.SQLException;\n\nimport org.bonitasoft.engine.data.instance.model.impl.XStreamFactory;\nimport org.hibernate.HibernateException;\nimport org.hibernate.engine.jdbc.CharacterStream;\nimport org.hibernate.engine.jdbc.internal.CharacterStreamImpl;\nimport org.hibernate.type.descriptor.WrapperOptions;\nimport org.hibernate.type.descriptor.java.AbstractTypeDescriptor;\nimport org.hibernate.type.descriptor.java.DataHelper;\n\npublic class XMLTypeDescriptor\n        extends AbstractTypeDescriptor<Serializable> {\n\n    public XMLTypeDescriptor() {\n        super(Serializable.class);\n    }\n\n    @Override\n    public boolean areEqual(Serializable one, Serializable another) {\n        if (one == another) {\n            return true;\n        }\n        if (one == null || another == null) {\n            return false;\n        }\n        return one.equals(another);\n    }\n\n    @Override\n    public String toString(Serializable value) {\n        return XStreamFactory.getXStream().toXML(value);\n    }\n\n    @Override\n    public Serializable fromString(String string) {\n        return ((Serializable) XStreamFactory.getXStream().fromXML((string)));\n    }\n\n    @SuppressWarnings({ \"unchecked\" })\n    @Override\n    public <X> X unwrap(Serializable value, Class<X> type, WrapperOptions options) {\n\n        if (value == null) {\n            return null;\n        }\n        if (String.class.isAssignableFrom(type)) {\n            return (X) toString(value);\n        }\n        if (Reader.class.isAssignableFrom(type)) {\n            return (X) new StringReader(toString(value));\n        }\n        if (CharacterStream.class.isAssignableFrom(type)) {\n            return (X) new CharacterStreamImpl(toString(value));\n        }\n        if (Clob.class.isAssignableFrom(type)) {\n            return (X) options.getLobCreator().createClob(toString(value));\n        }\n        if (DataHelper.isNClob(type)) {\n            return (X) options.getLobCreator().createNClob(toString(value));\n        }\n        throw unknownUnwrap(type);\n    }\n\n    @Override\n    public <X> Serializable wrap(X value, WrapperOptions options) {\n        if (value == null) {\n            return null;\n        }\n        if (String.class.isAssignableFrom(value.getClass())) {\n            return fromString((String) value);\n        }\n        if (Reader.class.isInstance(value)) {\n            return fromReader((Reader) value);\n        }\n        if (Clob.class.isAssignableFrom(value.getClass())) {\n            return fromReader(extractReader((Clob) value));\n        }\n        throw unknownWrap(value.getClass());\n    }\n\n    private Reader extractReader(Clob value) {\n        Reader reader;\n        try {\n            reader = value.getCharacterStream();\n        } catch (SQLException e) {\n            throw new HibernateException(\"Unable to get the Clob value\", e);\n        }\n        return reader;\n    }\n\n    private Serializable fromReader(Reader characterStream) {\n        return (Serializable) XStreamFactory.getXStream().fromXML(characterStream);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/search/FilterOperationType.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence.search;\n\n/**\n * @author Emmanuel Duchastenier\n */\npublic enum FilterOperationType {\n\n    BETWEEN, EQUALS, LIKE, GREATER, LESS, GREATER_OR_EQUALS, LESS_OR_EQUALS, DIFFERENT, L_PARENTHESIS, R_PARENTHESIS, AND, OR;\n\n    public static boolean isNormalOperator(final FilterOperationType type) {\n        return type == BETWEEN || type == EQUALS || type == LIKE || type == GREATER || type == LESS\n                || type == GREATER_OR_EQUALS\n                || type == LESS_OR_EQUALS || type == DIFFERENT;\n    }\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/recorder/Recorder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.recorder;\n\nimport org.bonitasoft.engine.recorder.model.DeleteAllRecord;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\n\npublic interface Recorder {\n\n    /**\n     * Add a record to database\n     *\n     * @param record\n     *        the record for insert\n     * @param type\n     * @throws SRecorderException\n     * @since 6.0\n     */\n    void recordInsert(InsertRecord record, String type) throws SRecorderException;\n\n    /**\n     * Delete a record from database\n     *\n     * @param record\n     *        the record for insert\n     * @param type\n     * @throws SRecorderException\n     * @since 6.0\n     */\n    void recordDelete(DeleteRecord record, String type) throws SRecorderException;\n\n    /**\n     * Update a record from database\n     *\n     * @param record\n     *        the record for insert\n     * @param type\n     * @throws SRecorderException\n     * @since 6.0\n     */\n    void recordUpdate(UpdateRecord record, String type) throws SRecorderException;\n\n    /**\n     * Update a record from database with a named query\n     * If no rows have been updated the event is not thrown\n     *\n     * @param record\n     *        the record for insert\n     * @param type\n     *        Object type\n     * @param query\n     *        NamedQuery to be used\n     * @return number of updated rows\n     * @throws SRecorderException\n     * @since 7.6\n     */\n    int recordUpdateWithQuery(final UpdateRecord record, String type, String query) throws SRecorderException;\n\n    /**\n     * Delete all records for a table from database, for the connected tenant\n     *\n     * @param record\n     *        table to clean\n     * @throws SRecorderException\n     * @since 6.1\n     */\n    void recordDeleteAll(DeleteAllRecord record) throws SRecorderException;\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/recorder/SRecorderException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.recorder;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SRecorderException extends SBonitaException {\n\n    private static final long serialVersionUID = 1L;\n\n    public SRecorderException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SRecorderException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/recorder/impl/RecorderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.recorder.impl;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.commons.ExceptionUtils;\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.events.model.SDeleteEvent;\nimport org.bonitasoft.engine.events.model.SEvent;\nimport org.bonitasoft.engine.events.model.SFireEventException;\nimport org.bonitasoft.engine.events.model.SInsertEvent;\nimport org.bonitasoft.engine.events.model.SUpdateEvent;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteAllRecord;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.bonitasoft.engine.services.PersistenceService;\nimport org.bonitasoft.engine.services.UpdateDescriptor;\n\n/**\n * @author Charles Souillard\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\n@Slf4j\npublic class RecorderImpl implements Recorder {\n\n    private final PersistenceService persistenceService;\n\n    private final EventService eventService;\n\n    public RecorderImpl(final PersistenceService persistenceService,\n            final EventService eventService) {\n        this.persistenceService = persistenceService;\n        this.eventService = eventService;\n    }\n\n    @Override\n    public void recordInsert(final InsertRecord insertRecord, String type) throws SRecorderException {\n        try {\n            var entity = persistenceService.insert(insertRecord.getEntity());\n            eventService.fireEvent(createInsertEvent(entity, type));\n        } catch (final Exception e) {\n            logExceptionsFromHandlers(e);\n            throw new SRecorderException(e);\n        }\n    }\n\n    private SInsertEvent createInsertEvent(PersistentObject entity, String type) {\n        SInsertEvent sInsertEvent = new SInsertEvent(type + SEvent.CREATED);\n        sInsertEvent.setObject(entity);\n        return sInsertEvent;\n    }\n\n    private SDeleteEvent createDeleteEvent(PersistentObject entity, String type) {\n        SDeleteEvent sDeleteEvent = new SDeleteEvent(type + SEvent.DELETED);\n        sDeleteEvent.setObject(entity);\n        return sDeleteEvent;\n    }\n\n    private SUpdateEvent createUpdateEvent(PersistentObject entity, Map<String, Object> updatedFields, String type) {\n        SUpdateEvent sUpdateEvent = new SUpdateEvent(type + SEvent.UPDATED);\n        sUpdateEvent.setObject(entity);\n        sUpdateEvent.setUpdatedFields(updatedFields);\n        return sUpdateEvent;\n    }\n\n    @Override\n    public void recordDelete(final DeleteRecord deleteRecord, String type) throws SRecorderException {\n        try {\n            persistenceService.delete(deleteRecord.getEntity());\n            eventService.fireEvent(createDeleteEvent(deleteRecord.getEntity(), type));\n        } catch (final Exception e) {\n            logExceptionsFromHandlers(e);\n            throw new SRecorderException(e);\n        }\n    }\n\n    @Override\n    public void recordDeleteAll(final DeleteAllRecord deleteAllRecord) throws SRecorderException {\n        try {\n            persistenceService.deleteAll(deleteAllRecord.entityClass(), deleteAllRecord.filters());\n        } catch (final Exception e) {\n            logExceptionsFromHandlers(e);\n            throw new SRecorderException(e);\n        }\n    }\n\n    @Override\n    public void recordUpdate(final UpdateRecord updateRecord, String type) throws SRecorderException {\n        final UpdateDescriptor desc = UpdateDescriptor.buildSetFields(updateRecord.getEntity(),\n                updateRecord.getFields());\n        try {\n            persistenceService.update(desc);\n            eventService.fireEvent(createUpdateEvent(updateRecord.getEntity(), updateRecord.getFields(), type));\n        } catch (final Exception e) {\n            logExceptionsFromHandlers(e);\n            throw new SRecorderException(e);\n        }\n    }\n\n    @Override\n    public int recordUpdateWithQuery(final UpdateRecord updateRecord, String type, String query)\n            throws SRecorderException {\n\n        try {\n            int updateCount = persistenceService.update(query, updateRecord.getFields());\n            if (updateCount > 0)\n                eventService.fireEvent(createUpdateEvent(updateRecord.getEntity(), updateRecord.getFields(), type));\n            return updateCount;\n        } catch (final Exception e) {\n            logExceptionsFromHandlers(e);\n            throw new SRecorderException(e);\n        }\n    }\n\n    private void logExceptionsFromHandlers(final Exception e) {\n        if (!(e instanceof SFireEventException)) {\n            return;\n        }\n        if (log.isDebugEnabled()) {\n            final List<Exception> handlerExceptions = ((SFireEventException) e).getHandlerExceptions();\n            for (Exception exception : handlerExceptions) {\n                log.debug(\"error while executing handler e {}\", ExceptionUtils.printLightWeightStacktrace(exception));\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/recorder/model/DeleteAllRecord.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.recorder.model;\n\nimport static java.util.Collections.unmodifiableList;\nimport static java.util.Objects.requireNonNullElseGet;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Celine Souchet\n */\npublic record DeleteAllRecord(Class<? extends PersistentObject> entityClass, List<FilterOption> filters) {\n\n    @Override\n    public List<FilterOption> filters() {\n        return unmodifiableList(requireNonNullElseGet(filters, ArrayList::new));\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/recorder/model/DeleteRecord.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.recorder.model;\n\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\npublic class DeleteRecord extends Record {\n\n    public DeleteRecord(final PersistentObject entity) {\n        super(entity);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/recorder/model/EntityUpdateDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.recorder.model;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport lombok.EqualsAndHashCode;\n\n/**\n * @author Baptiste Mesta\n */\n@EqualsAndHashCode\npublic class EntityUpdateDescriptor {\n\n    Map<String, Object> fields = new HashMap<>();\n\n    public Map<String, Object> getFields() {\n        return fields;\n    }\n\n    public EntityUpdateDescriptor addField(final String name, final Object value) {\n        fields.put(name, value);\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"EntityUpdateDescriptor [fields=\" + fields + \"]\";\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/recorder/model/InsertRecord.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.recorder.model;\n\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\npublic class InsertRecord extends Record {\n\n    public InsertRecord(final PersistentObject entity) {\n        super(entity);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/recorder/model/Record.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.recorder.model;\n\nimport org.bonitasoft.engine.commons.NullCheckingUtil;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\npublic abstract class Record {\n\n    private final PersistentObject entity; // ServerProcessInstance, CaseInstance\n\n    public Record(final PersistentObject entity) {\n        NullCheckingUtil.checkArgsNotNull(entity);\n        this.entity = entity;\n    }\n\n    public PersistentObject getEntity() {\n        return entity;\n    }\n\n    @Override\n    public String toString() {\n        return \"Record [entity=\" + entity + \"]\";\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + ((entity == null) ? 0 : entity.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        final Record other = (Record) obj;\n        if (entity == null) {\n            if (other.entity != null) {\n                return false;\n            }\n        } else if (!entity.equals(other.entity)) {\n            return false;\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/recorder/model/UpdateRecord.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.recorder.model;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.commons.NullCheckingUtil;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Charles Souillard\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n * @author Baptiste Mesta\n */\npublic final class UpdateRecord extends Record {\n\n    private Map<String, Object> fields;\n\n    private UpdateRecord(final PersistentObject entity) {\n        super(entity);\n    }\n\n    public static UpdateRecord buildSetFields(final PersistentObject entity, final Map<String, Object> fields) {\n        final UpdateRecord updateRecord = new UpdateRecord(entity);\n        updateRecord.addFields(fields);\n        return updateRecord;\n    }\n\n    public static UpdateRecord buildSetFields(final PersistentObject entity, final EntityUpdateDescriptor descriptor) {\n        NullCheckingUtil.checkArgsNotNull(descriptor);\n        final UpdateRecord updateRecord = new UpdateRecord(entity);\n        updateRecord.addFields(descriptor.getFields());\n        return updateRecord;\n    }\n\n    public void addField(final String fieldName, final Object fieldValue) {\n        if (fields == null) {\n            fields = new HashMap<String, Object>();\n        }\n        fields.put(fieldName, fieldValue);\n    }\n\n    public void addFields(final Map<String, Object> fields) {\n        if (this.fields == null) {\n            this.fields = new HashMap<String, Object>();\n        }\n        this.fields.putAll(fields);\n    }\n\n    public Map<String, Object> getFields() {\n        return fields;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/sequence/SequenceDAO.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.sequence;\n\nimport java.sql.Connection;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\n\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\n\npublic class SequenceDAO {\n\n    static final String NEXT_ID = \"nextid\";\n    static final String SELECT_BY_ID = \"SELECT * FROM sequence WHERE id = ?\";\n\n    static final String UPDATE_SEQUENCE = \"UPDATE sequence SET nextId = ? WHERE id = ?\";\n\n    private final Connection connection;\n\n    public SequenceDAO(Connection connection) {\n        this.connection = connection;\n    }\n\n    protected void updateSequence(long nextSequenceId, long id) throws SQLException {\n        try (PreparedStatement updateSequencePreparedStatement = connection.prepareStatement(UPDATE_SEQUENCE)) {\n            updateSequencePreparedStatement.setObject(1, nextSequenceId);\n            updateSequencePreparedStatement.setObject(2, id);\n            updateSequencePreparedStatement.executeUpdate();\n        }\n    }\n\n    protected long selectById(long id) throws SQLException, SObjectNotFoundException {\n        PreparedStatement selectByIdPreparedStatement = null;\n        ResultSet resultSet = null;\n        try {\n            selectByIdPreparedStatement = connection.prepareStatement(SELECT_BY_ID);\n            selectByIdPreparedStatement.setLong(1, id);\n            resultSet = selectByIdPreparedStatement.executeQuery();\n            return getNextId(id, resultSet);\n        } finally {\n            if (selectByIdPreparedStatement != null) {\n                selectByIdPreparedStatement.close();\n            }\n            if (resultSet != null) {\n                resultSet.close();\n            }\n        }\n    }\n\n    private long getNextId(final long id, final ResultSet resultSet)\n            throws SQLException, SObjectNotFoundException {\n        try {\n            if (resultSet.next()) {\n                final long nextId = resultSet.getLong(NEXT_ID);\n\n                if (resultSet.wasNull()) {\n                    throw new SQLException(\"Did not expect a null value for the column \" + NEXT_ID);\n                }\n\n                if (resultSet.next()) {\n                    throw new SQLException(\n                            \"Did not expect more than one value for id: \" + id);\n                }\n\n                return nextId;\n            }\n        } finally {\n            closeResultSet(resultSet);\n        }\n        throw new SObjectNotFoundException(\"Found no row for id: \" + id);\n    }\n\n    private void closeResultSet(final ResultSet resultSet) {\n        try {\n            if (resultSet != null) {\n                resultSet.close();\n            }\n        } catch (final SQLException e) {\n            // can't do anything\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/sequence/SequenceManager.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.sequence;\n\nimport org.bonitasoft.engine.commons.exceptions.SObjectModificationException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\n\n/**\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface SequenceManager {\n\n    long getNextId(String entityName) throws SObjectNotFoundException, SObjectModificationException;\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/sequence/SequenceManagerImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.sequence;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Optional;\n\nimport javax.sql.DataSource;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.lock.BonitaLock;\nimport org.bonitasoft.engine.lock.LockService;\nimport org.bonitasoft.engine.lock.SLockException;\nimport org.bonitasoft.engine.lock.SLockTimeoutException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Charles Souillard\n * @author Matthieu Chaffotte\n * @author Baptiste Mesta\n * @author Emmanuel Duchastenier\n */\n@Component\npublic class SequenceManagerImpl implements SequenceManager {\n\n    private static final Logger logger = LoggerFactory.getLogger(SequenceManagerImpl.class);\n\n    static final String SEQUENCE = \"SEQUENCE\";\n\n    private final Map<Long, SequenceRange> sequences = new HashMap<>();\n    private final Map<Long, Integer> sequenceIdToRangeSize;\n    private final SequenceMappingProvider sequenceMappingProvider;\n    private final Map<String, Long> classNameToSequenceId;\n\n    private final int retries;\n\n    private final int delay;\n\n    private final int delayFactor;\n\n    private final DataSource datasource;\n\n    private final LockService lockService;\n\n    public SequenceManagerImpl(final LockService lockService, final SequenceMappingProvider sequenceMappingProvider,\n            @Qualifier(\"bonitaNonXaDataSource\") final DataSource datasource,\n            @Value(\"${bonita.platform.sequence.retries}\") final int retries,\n            @Value(\"${bonita.platform.sequence.delay}\") final int delay,\n            @Value(\"${bonita.platform.sequence.delayFactor}\") final int delayFactor) {\n        this.lockService = lockService;\n        this.sequenceMappingProvider = sequenceMappingProvider;\n        this.sequenceIdToRangeSize = getSequenceIdToRangeSizeMap();\n        this.classNameToSequenceId = getClassNameToSequenceIdMap();\n        this.retries = retries;\n        this.delay = delay;\n        this.delayFactor = delayFactor;\n        this.datasource = datasource;\n    }\n\n    private Map<String, Long> getClassNameToSequenceIdMap() {\n        final Map<String, Long> result = new HashMap<>();\n        for (SequenceMapping sequenceMapping : sequenceMappingProvider.getSequenceMappings()) {\n            for (String className : sequenceMapping.getClassNames()) {\n                result.put(className, sequenceMapping.getSequenceId());\n            }\n        }\n        return result;\n    }\n\n    private Map<Long, Integer> getSequenceIdToRangeSizeMap() {\n        final Map<Long, Integer> result = new HashMap<>();\n        for (SequenceMapping sequenceMapping : sequenceMappingProvider.getSequenceMappings()) {\n            final long sequenceId = sequenceMapping.getSequenceId();\n            if (result.containsKey(sequenceId)) {\n                throw new RuntimeException(\"SequenceMapping for id <\" + sequenceId\n                        + \"> is duplicated. Please make sure there is only one configuration for this sequence.\");\n            }\n            result.put(sequenceId, sequenceMapping.getRangeSize());\n        }\n        return result;\n    }\n\n    @Override\n    public long getNextId(final String entityName) throws SObjectNotFoundException {\n        final long sequenceId = getSequenceId(entityName);\n        SequenceRange sequence = getSequence(sequenceId);\n        Optional<Long> nextAvailableId = sequence.getNextAvailableId();\n        if (nextAvailableId.isPresent()) {\n            return nextAvailableId.get();\n        }\n        //synchronize on the sequence object itself (we will read/update only on this one)\n        synchronized (sequence) {\n            nextAvailableId = sequence.getNextAvailableId();\n            int loopCounter = 0;\n            // set a max number of retries to 100:\n            while (nextAvailableId.isEmpty() && loopCounter < 100) {\n                if (loopCounter > 0) {\n                    logger.debug(\"Could not get an Id after updating to next range. Retrying...\");\n                }\n                sequence.updateToNextRange(setNewRange(sequenceId));\n                nextAvailableId = sequence.getNextAvailableId();\n                loopCounter++;\n            }\n            return nextAvailableId.orElseThrow(\n                    () -> new IllegalStateException(\"No new available id found for sequence \" + entityName));\n        }\n    }\n\n    private SequenceRange getSequence(Long sequenceId) {\n        if (!sequences.containsKey(sequenceId)) {\n            synchronized (this) {\n                if (!sequences.containsKey(sequenceId)) {\n                    sequences.put(sequenceId, new SequenceRange(sequenceIdToRangeSize.get(sequenceId)));\n                }\n            }\n        }\n        return sequences.get(sequenceId);\n    }\n\n    private Long getSequenceId(String entityName) throws SObjectNotFoundException {\n        final Long sequenceId = classNameToSequenceId.get(entityName);\n        if (sequenceId == null) {\n            throw new SObjectNotFoundException(\"No sequence id found for \" + entityName);\n        }\n        return sequenceId;\n    }\n\n    /**\n     * get the next available id of a sequence and update in database its value\n     *\n     * @return the next available id of the sequence\n     */\n    private long setNewRange(final long sequenceId) throws SObjectNotFoundException {\n        BonitaLock lock = createLock(sequenceId);\n        Exception lastException = null;\n        try {\n            int attempt = 1;\n            long sleepTime = delay;\n            while (attempt <= retries) {\n                if (attempt > 1) {\n                    logger.info(\"Retry #{} to retrieve next sequence id of sequence {}\", attempt, sequenceId);\n                }\n                Connection connection = getConnection();\n                try {\n                    connection.setAutoCommit(false);\n                    SequenceDAO sequenceDAO = createDao(connection);\n                    long nextAvailableId = sequenceDAO.selectById(sequenceId);\n                    sequenceDAO.updateSequence(nextAvailableId + sequenceIdToRangeSize.get(sequenceId), sequenceId);\n                    connection.commit();\n                    return nextAvailableId;\n                } catch (final SObjectNotFoundException t) {\n                    rollback(connection);\n                    throw t;\n                } catch (final Exception t) {\n                    attempt++;\n                    rollback(connection);\n                    lastException = t;\n                    manageException(attempt, sleepTime, t);\n                    sleepTime *= delayFactor;\n                } finally {\n                    close(connection);\n                }\n            }\n        } finally {\n            unlock(lock);\n        }\n\n        throw new SObjectNotFoundException(\n                \"Unable to get a sequence id for \" + sequenceId, lastException);\n    }\n\n    private void unlock(BonitaLock lock) {\n        try {\n            lockService.unlock(lock);\n        } catch (SLockException e) {\n            throw new SBonitaRuntimeException(\n                    \"Unable to unlock the lock require to get next id of sequences from database\", e);\n        }\n    }\n\n    private BonitaLock createLock(long sequenceId) {\n        BonitaLock lock;\n        try {\n            lock = lockService.lock(sequenceId, SEQUENCE);\n        } catch (SLockException | SLockTimeoutException e) {\n            throw new SBonitaRuntimeException(\n                    \"Unable to acquire lock in order to update get the next id from database of the sequence \"\n                            + sequenceId,\n                    e);\n        }\n        return lock;\n    }\n\n    private void close(Connection connection) {\n        try {\n            connection.close();\n        } catch (final SQLException e) {\n            throw new SBonitaRuntimeException(\n                    \"Next id of sequence correctly updated, but unable to close the connection\", e);\n        }\n    }\n\n    private Connection getConnection() {\n        try {\n            return datasource.getConnection();\n        } catch (SQLException e) {\n            throw new SBonitaRuntimeException(\"Unable to acquire connection to retrieve next id of the sequence\", e);\n        }\n    }\n\n    private void rollback(Connection connection) {\n        try {\n            connection.rollback();\n        } catch (final SQLException e) {\n            throw new SBonitaRuntimeException(\n                    \"Unable to rollback the transaction that get/update next sequence id from database\", e);\n        }\n    }\n\n    SequenceDAO createDao(Connection connection) {\n        return new SequenceDAO(connection);\n    }\n\n    private static void manageException(int attempt, final long sleepTime, final Exception t) {\n        logger.error(\"Unable to retrieve and update sequence in database because: {}.\" +\n                \"( attempt #{} ). Will sleep {} millis before retrying. \", t.getMessage(), attempt, sleepTime);\n        logger.debug(\"Cause:\", t);\n        try {\n            Thread.sleep(sleepTime);\n        } catch (final InterruptedException ignored) {\n            logger.error(\"Interrupted while sleeping before retry\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/sequence/SequenceMapping.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.sequence;\n\nimport java.util.Collections;\nimport java.util.Set;\n\n/**\n * @author Charles Souillard\n */\npublic class SequenceMapping {\n\n    private final Set<String> classNames;\n    private final long sequenceId;\n    private final int rangeSize;\n\n    public SequenceMapping(String className, long sequenceId, final int rangeSize) {\n        this(Collections.singleton(className), sequenceId, rangeSize);\n    }\n\n    public SequenceMapping(Set<String> classNames, long sequenceId, final int rangeSize) {\n        this.classNames = classNames;\n        this.sequenceId = sequenceId;\n        this.rangeSize = rangeSize;\n    }\n\n    public long getSequenceId() {\n        return sequenceId;\n    }\n\n    public Set<String> getClassNames() {\n        return classNames;\n    }\n\n    public int getRangeSize() {\n        return rangeSize;\n    }\n\n    @Override\n    public String toString() {\n        return \"SequenceMapping{\" +\n                \"sequenceId=\" + sequenceId +\n                \", classNames='\" + classNames + '\\'' +\n                \", rangeSize=\" + rangeSize +\n                '}';\n    }\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/sequence/SequenceMappingProvider.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.sequence;\n\nimport java.util.List;\n\nimport lombok.Getter;\nimport lombok.Setter;\n\n/**\n * @author Charles Souillard\n */\n@Setter\n@Getter\npublic class SequenceMappingProvider {\n\n    private List<SequenceMapping> sequenceMappings;\n\n    public SequenceMappingProvider() {\n    }\n\n    @Override\n    public String toString() {\n        return \"SequenceMappingProvider{sequenceMappings=\" + sequenceMappings + '}';\n    }\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/sequence/SequenceRange.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.sequence;\n\nimport java.util.Optional;\nimport java.util.concurrent.atomic.AtomicLong;\n\npublic class SequenceRange {\n\n    private AtomicLong nextAvailableId;\n    private long lastIdInRange;\n    private final int rangeSize;\n\n    public SequenceRange(int rangeSize) {\n        this.rangeSize = rangeSize;\n    }\n\n    public Optional<Long> getNextAvailableId() {\n        if (nextAvailableId == null) {\n            // Range is not initialized yet:\n            return Optional.empty();\n        }\n        long nextId = nextAvailableId.getAndUpdate(current -> {\n            if (current == -1 || current >= lastIdInRange) {\n                return -1; // -1 means no more Id available\n            } else {\n                return current + 1;\n            }\n        });\n        if (nextId < 0) {\n            return Optional.empty();\n        }\n        return Optional.of(nextId);\n    }\n\n    public void updateToNextRange(long nextAvailableIdFromDatabase) {\n        nextAvailableId = new AtomicLong(nextAvailableIdFromDatabase);\n        lastIdInRange = nextAvailableIdFromDatabase + rangeSize - 1;\n    }\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/sequence/exceptions/SequenceManagerException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.sequence.exceptions;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SequenceManagerException extends SBonitaException {\n\n    private static final long serialVersionUID = 8661525835047162806L;\n\n    /**\n     *\n     */\n    public SequenceManagerException() {\n        super();\n    }\n\n    /**\n     * @param arguments\n     */\n    public SequenceManagerException(final Object... arguments) {\n        super(arguments);\n    }\n\n    /**\n     * @param message\n     * @param cause\n     */\n    public SequenceManagerException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    /**\n     * @param message\n     */\n    public SequenceManagerException(final String message) {\n        super(message);\n    }\n\n    /**\n     * @param cause\n     * @param arguments\n     */\n    public SequenceManagerException(final Throwable cause, final Object... arguments) {\n        super(cause, arguments);\n    }\n\n    /**\n     * @param cause\n     */\n    public SequenceManagerException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/services/PersistenceService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.services;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\n\n/**\n * @author Charles Souillard\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n * @author Matthieu Chaffotte\n */\npublic interface PersistenceService extends ReadPersistenceService {\n\n    // on save, the service MUST generate a unique ID and set it in id attribute if this attribute is equals to -1\n    // else keep the already set id\n\n    /**\n     * Add a record into the table by given PersistentObject.\n     *\n     * @since 6.0\n     */\n    <T extends PersistentObject> T insert(final T entity) throws SPersistenceException;\n\n    /**\n     *\n     */\n    <T extends PersistentObject> List<T> insertInBatch(final List<T> entities) throws SPersistenceException;\n\n    /**\n     * Delete a record from the table by given PersistentObject.\n     *\n     * @since 6.0\n     */\n    <T extends PersistentObject> void delete(final T entity) throws SPersistenceException;\n\n    /**\n     * Delete all records belong to the given entity class from the table.\n     *\n     * @param entityClass The class which extends PersistentObject\n     * @since 6.0\n     */\n    void deleteAll(final Class<? extends PersistentObject> entityClass) throws SPersistenceException;\n\n    /**\n     * Executes a query update.\n     *\n     * @param updateQueryName the name of the declared query that represent the update.\n     * @return the number of updated rows, as returned by the underlining persistence implementation.\n     * @throws SPersistenceException if a persistence problem occurs when executing the update query.\n     */\n    int update(final String updateQueryName) throws SPersistenceException;\n\n    /**\n     * Executes a query update.\n     */\n    int update(String updateQueryName, Map<String, Object> inputParameters) throws SPersistenceException;\n\n    /**\n     * Delete all elements of a specific table\n     *\n     * @param entityClass Entity class corresponding to the table to empty\n     * @param filters filter options to restrict the deletion\n     * @since 6.1\n     */\n    void deleteAll(Class<? extends PersistentObject> entityClass, List<FilterOption> filters)\n            throws SPersistenceException;\n\n    /**\n     */\n    void update(final UpdateDescriptor desc) throws SPersistenceException;\n\n    void flushStatements() throws SPersistenceException;\n\n    /**\n     * Refresh a persistent object from the database, reloading its state.\n     * This is useful after executing bulk HQL updates (via named queries) to synchronize\n     * the in-memory managed entity with the actual database state, preventing Hibernate's\n     * dirty-checking from overwriting the atomic SQL update with stale values.\n     * The entity remains managed in the session after refresh.\n     *\n     * @param entity the entity to refresh from the database\n     */\n    void refresh(final PersistentObject entity) throws SPersistenceException;\n\n    /**\n     * Delete a record from the table by id and its class type.\n     *\n     * @param id entity's id\n     * @param entityClass The class which extends PersistentObject\n     * @since 6.0\n     */\n    void delete(final long id, final Class<? extends PersistentObject> entityClass) throws SPersistenceException;\n\n    /**\n     * Delete records from the table.\n     *\n     * @param ids A list contains entity ids\n     * @param entityClass The class which extends PersistentObject\n     * @since 6.0\n     */\n    void delete(final List<Long> ids, final Class<? extends PersistentObject> entityClass) throws SPersistenceException;\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/services/SPersistenceException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.services;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Charles Souillard\n */\npublic class SPersistenceException extends SBonitaException {\n\n    private static final long serialVersionUID = -3172887017042060160L;\n\n    public SPersistenceException(final Throwable t) {\n        super(t);\n    }\n\n    public SPersistenceException(final String message) {\n        super(message);\n    }\n\n    public SPersistenceException(final String message, final Throwable t) {\n        super(message, t);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/services/UpdateDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.services;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Charles Souillard\n * @author Emmanuel Duchastenier\n */\npublic class UpdateDescriptor {\n\n    private static final String ID = \"id\";\n\n    private final PersistentObject entity;\n\n    private Map<String, Object> fields;\n\n    public UpdateDescriptor(final PersistentObject entity) {\n        super();\n        this.entity = entity;\n    }\n\n    public static UpdateDescriptor buildSetField(final PersistentObject entity, final String fieldName,\n            final Object fieldValue) {\n        final UpdateDescriptor updateDescriptor = new UpdateDescriptor(entity);\n        updateDescriptor.addField(fieldName, fieldValue);\n        return updateDescriptor;\n    }\n\n    public static UpdateDescriptor buildSetFields(final PersistentObject entity, final Map<String, Object> fields) {\n        final UpdateDescriptor updateDescriptor = new UpdateDescriptor(entity);\n        updateDescriptor.addFields(fields);\n        return updateDescriptor;\n    }\n\n    public void addField(final String fieldName, final Object fieldValue) {\n        if (fields == null) {\n            fields = new HashMap<String, Object>();\n        }\n        if (ID.equalsIgnoreCase(fieldName)) {\n            throw new RuntimeException(\"Updating an object's \" + ID + \" field is forbidden\");\n        }\n        fields.put(fieldName, fieldValue);\n    }\n\n    public void addFields(final Map<String, Object> fields) {\n        if (this.fields == null) {\n            this.fields = new HashMap<String, Object>();\n        }\n        if (fields.containsKey(ID)) {\n            throw new RuntimeException(\"Updating an object's \" + ID + \" field is forbidden\");\n        }\n        this.fields.putAll(fields);\n    }\n\n    public PersistentObject getEntity() {\n        return entity;\n    }\n\n    public Map<String, Object> getFields() {\n        if (fields == null) {\n            return Collections.emptyMap();\n        }\n        return fields;\n    }\n\n    @Override\n    public String toString() {\n        return \"UpdateDescriptor [entity=\" + entity + \", fields=\" + fields + \"]\";\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/java/org/bonitasoft/engine/services/Vendor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.services;\n\nimport static org.apache.commons.lang3.StringUtils.containsIgnoreCase;\n\nimport java.sql.DatabaseMetaData;\nimport java.sql.SQLException;\n\nimport org.hibernate.cfg.Configuration;\n\n/**\n * @author Baptiste Mesta\n */\npublic enum Vendor {\n\n    ORACLE, SQLSERVER, POSTGRES, MYSQL, OTHER;\n\n    /**\n     * Get database vendor from databases metadata\n     */\n    public static Vendor fromDatabaseMetadata(DatabaseMetaData metadata) throws SQLException {\n        if (metadata != null) {\n            String productName = metadata.getDatabaseProductName();\n            if (containsIgnoreCase(productName, \"Oracle\")) {\n                return ORACLE;\n            }\n            if (containsIgnoreCase(productName, \"Microsoft SQL Server\")) {\n                return SQLSERVER;\n            }\n        }\n        return OTHER;\n    }\n\n    /**\n     * Get database vendor from databases metadata\n     */\n    public static Vendor fromHibernateConfiguration(Configuration configuration) {\n        String hibernateDialect = configuration.getProperty(\"hibernate.dialect\");\n        return fromHibernateDialectProperty(hibernateDialect);\n    }\n\n    public static Vendor fromHibernateDialectProperty(String hibernateDialect) {\n        if (hibernateDialect != null) {\n            if (hibernateDialect.toLowerCase().contains(\"postgresql\")) {\n                return POSTGRES;\n            } else if (hibernateDialect.toLowerCase().contains(\"sqlserver\")) {\n                return SQLSERVER;\n            } else if (hibernateDialect.toLowerCase().contains(\"oracle\")) {\n                return ORACLE;\n            } else if (hibernateDialect.toLowerCase().contains(\"mysql\")) {\n                return MYSQL;\n            }\n        }\n        return OTHER;\n    }\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/main/resources/META-INF/services/org.hibernate.boot.spi.SessionFactoryBuilderFactory",
    "content": "org.bonitasoft.engine.persistence.CustomDataTypesRegistration"
  },
  {
    "path": "services/bonita-persistence/src/test/java/org/bonitasoft/engine/persistence/DefaultOrderByBuilderTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class DefaultOrderByBuilderTest {\n\n    StringBuilder builder;\n\n    DefaultOrderByBuilder defaultOrderByBuilder;\n\n    @Before\n    public void before() {\n        //given\n        builder = new StringBuilder();\n        defaultOrderByBuilder = new DefaultOrderByBuilder();\n    }\n\n    @Test\n    public void should_sort_asc() {\n        //when\n        defaultOrderByBuilder.appendOrderBy(builder, \"field\", OrderByType.ASC);\n\n        //then\n        assertThat(builder.toString()).isEqualTo(\"field ASC\");\n    }\n\n    @Test\n    public void should_sort_desc() {\n        //when\n        defaultOrderByBuilder.appendOrderBy(builder, \"field\", OrderByType.DESC);\n\n        //then\n        assertThat(builder.toString()).isEqualTo(\"field DESC\");\n    }\n\n    @Test\n    public void should_sort_asc_with_nulls_first() {\n        //when\n        defaultOrderByBuilder.appendOrderBy(builder, \"field\", OrderByType.ASC_NULLS_FIRST);\n\n        //then\n        assertThat(builder.toString()).isEqualTo(\"field ASC NULLS FIRST\");\n    }\n\n    @Test\n    public void should_sort_asc_with_nulls_last() {\n        //when\n        defaultOrderByBuilder.appendOrderBy(builder, \"field\", OrderByType.ASC_NULLS_LAST);\n\n        //then\n        assertThat(builder.toString()).isEqualTo(\"field ASC NULLS LAST\");\n    }\n\n    @Test\n    public void should_sort_desc_with_nulls_first() {\n        //when\n        defaultOrderByBuilder.appendOrderBy(builder, \"field\", OrderByType.DESC_NULLS_FIRST);\n\n        //then\n        assertThat(builder.toString()).isEqualTo(\"field DESC NULLS FIRST\");\n    }\n\n    @Test\n    public void should_sort_desc_with_nulls_last() {\n        //when\n        defaultOrderByBuilder.appendOrderBy(builder, \"field\", OrderByType.DESC_NULLS_LAST);\n\n        //then\n        assertThat(builder.toString()).isEqualTo(\"field DESC NULLS LAST\");\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/test/java/org/bonitasoft/engine/persistence/OrderByTypeTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.Arrays;\n\nimport org.junit.Test;\n\n/**\n * @author Laurent Leseigneur\n */\npublic class OrderByTypeTest {\n\n    @Test\n    public void should_have_a_sql_keyword_for_each_orderBy() {\n        for (OrderByType orderByType : Arrays.asList(OrderByType.values())) {\n            assertThat(orderByType.getSqlKeyword()).as(\"should have a sql valid key word\").isNotNull();\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/test/java/org/bonitasoft/engine/persistence/QueryBuilderTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport static java.util.Collections.emptyMap;\nimport static java.util.Collections.singletonList;\nimport static java.util.Collections.singletonMap;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.TreeSet;\n\nimport org.bonitasoft.engine.commons.EnumToObjectConvertible;\nimport org.bonitasoft.engine.persistence.search.FilterOperationType;\nimport org.hibernate.Session;\nimport org.hibernate.query.Query;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.SystemOutRule;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnit;\nimport org.mockito.junit.MockitoRule;\n\n/**\n * @author Baptiste Mesta\n */\npublic class QueryBuilderTest {\n\n    private static final char LIKE_ESCAPE_CHARACTER = '§';\n    public Map<String, String> classAliasMappings = singletonMap(TestObject.class.getName(), \"testObj\");\n\n    @Rule\n    public SystemOutRule systemOutRule = new SystemOutRule().enableLog();\n    @Rule\n    public MockitoRule mockitoRule = MockitoJUnit.rule();\n    @Mock\n    private Session session;\n    @Mock\n    private Query query;\n\n    @Before\n    public void before() {\n        when(session.createQuery(anyString())).thenAnswer(a -> {\n            Query mock = mock(Query.class);\n            doReturn(a.getArgument(0)).when(mock).getQueryString();\n            return mock;\n        });\n    }\n\n    @Test\n    public void should_hasChanged_return_false_if_query_has_not_changed() {\n        //given\n        QueryBuilder queryBuilder = createQueryBuilder(\"SELECT TOTO FROM STUFF\");\n        //when\n        //then\n        assertThat(queryBuilder.hasChanged()).isFalse();\n    }\n\n    private QueryBuilder createQueryBuilder(String baseQuery) {\n        return createQueryBuilder(baseQuery, null, OrderByCheckingMode.NONE);\n    }\n\n    private HQLQueryBuilder createQueryBuilder(String baseQuery, SelectListDescriptor<TestObject> selectListDescriptor,\n            OrderByCheckingMode orderByCheckingMode) {\n        doReturn(baseQuery).when(query).getQueryString();\n        return new HQLQueryBuilder<>(session, query, new DefaultOrderByBuilder(),\n                classAliasMappings,\n                LIKE_ESCAPE_CHARACTER, orderByCheckingMode, selectListDescriptor);\n    }\n\n    @Test\n    public void should_hasChanged_return_true_if_query_has_changed() throws Exception {\n        //given\n        QueryBuilder queryBuilder = createQueryBuilder(\"SELECT TOTO FROM STUFF\");\n        //when\n        queryBuilder.appendOrderByClause(\n                Collections.singletonList(new OrderByOption(TestObject.class, \"theValue\", OrderByType.ASC)),\n                TestObject.class);\n        //then\n        assertThat(queryBuilder.hasChanged()).isTrue();\n    }\n\n    @Test\n    public void should_generate_query_with_order_by() throws Exception {\n        //given\n        QueryBuilder queryBuilder = createQueryBuilder(\"SELECT testObj.* FROM test_object testObj\");\n        //when\n        queryBuilder.appendOrderByClause(\n                Collections.singletonList(new OrderByOption(TestObject.class, \"theValue\", OrderByType.ASC)),\n                TestObject.class);\n        //then\n        assertThat(queryBuilder.getQuery())\n                .isEqualTo(\"SELECT testObj.* FROM test_object testObj ORDER BY testObj.theValue ASC,testObj.id ASC\");\n    }\n\n    @Test\n    public void should_generate_query_with_multiple_order_by() throws Exception {\n        //given\n        QueryBuilder queryBuilder = createQueryBuilder(\"SELECT testObj.* FROM test_object testObj\");\n        //when\n        queryBuilder.appendOrderByClause(Arrays.asList(new OrderByOption(TestObject.class, \"theValue\", OrderByType.ASC),\n                new OrderByOption(TestObject.class, \"id\", OrderByType.ASC),\n                new OrderByOption(TestObject.class, \"lastName\", OrderByType.DESC_NULLS_LAST)),\n                TestObject.class);\n        //then\n        assertThat(queryBuilder.getQuery())\n                .isEqualTo(\n                        \"SELECT testObj.* FROM test_object testObj ORDER BY testObj.theValue ASC,testObj.id ASC,testObj.lastName DESC NULLS LAST\");\n\n    }\n\n    @Test\n    public void should_generate_query_with_filter() {\n        //given\n        QueryBuilder queryBuilder = createQueryBuilder(\"SELECT testObj.* FROM test_object testObj\");\n        //when\n        queryBuilder.appendFilters(Collections.singletonList(new FilterOption(TestObject.class, \"theValue\", 12)), null);\n        //then\n        assertThat(queryBuilder.getQuery())\n                .isEqualTo(\"SELECT testObj.* FROM test_object testObj WHERE (testObj.theValue = :f1)\");\n        assertThat(queryBuilder.getQueryParameters().get(\"f1\")).isEqualTo(12);\n    }\n\n    @Test\n    public void should_generate_query_with_multiple_filters() {\n        //given\n        QueryBuilder queryBuilder = createQueryBuilder(\"SELECT testObj.* FROM test_object testObj\");\n        //when\n        queryBuilder.appendFilters(\n                Arrays.asList(new FilterOption(TestObject.class, \"age\", 25),\n                        new FilterOption(TestObject.class, \"lastname\", \"John\")),\n                null);\n        //then\n        assertThat(queryBuilder.getQuery()).isEqualTo(\n                \"SELECT testObj.* FROM test_object testObj WHERE (testObj.age = :f1 AND testObj.lastname = :f2)\");\n        assertThat(queryBuilder.getQueryParameters().get(\"f1\")).isEqualTo(25);\n        assertThat(queryBuilder.getQueryParameters().get(\"f2\")).isEqualTo(\"John\");\n    }\n\n    @Test\n    public void should_generate_query_with_filter_on_query_containing_filters_already() {\n        //given\n        QueryBuilder queryBuilder = createQueryBuilder(\n                \"SELECT testObj.* FROM test_object testObj WHERE testObj.enabled = true\");\n        //when\n        queryBuilder.appendFilters(Collections.singletonList(new FilterOption(TestObject.class, \"theValue\", 12)), null);\n        //then\n        assertThat(queryBuilder.getQuery()).isEqualTo(\n                \"SELECT testObj.* FROM test_object testObj WHERE testObj.enabled = true AND (testObj.theValue = :f1)\");\n        assertThat(queryBuilder.getQueryParameters().get(\"f1\")).isEqualTo(12);\n    }\n\n    @Test\n    public void should_generate_query_with_filter_and_order_clause() throws Exception {\n        //given\n        QueryBuilder queryBuilder = createQueryBuilder(\n                \"SELECT testObj.* FROM test_object testObj WHERE testObj.enabled = true\");\n        //when\n        queryBuilder.appendFilters(Collections.singletonList(new FilterOption(TestObject.class, \"theValue\", 12)), null);\n        queryBuilder.appendOrderByClause(\n                Collections.singletonList(new OrderByOption(TestObject.class, \"theValue\", OrderByType.ASC)),\n                TestObject.class);\n        //then\n        assertThat(queryBuilder.getQuery()).isEqualTo(\n                \"SELECT testObj.* FROM test_object testObj WHERE testObj.enabled = true AND (testObj.theValue = :f1) ORDER BY testObj.theValue ASC,testObj.id ASC\");\n        assertThat(queryBuilder.getQueryParameters().get(\"f1\")).isEqualTo(12);\n    }\n\n    @Test\n    public void should_generate_query_with_search_term() {\n        //given\n        QueryBuilder queryBuilder = createQueryBuilder(\"SELECT testObj.* FROM test_object testObj\");\n        //when\n        queryBuilder.appendFilters(Collections.emptyList(),\n                new SearchFields(Collections.singletonList(\"toto\"),\n                        Collections.singletonMap(TestObject.class,\n                                aSet(\"field1\", \"field2\"))));\n        //then\n        assertThat(queryBuilder.getQuery()).matches(\n                \"SELECT testObj\\\\.\\\\* FROM test_object testObj WHERE \\\\(testObj.field1 LIKE :s1 ESCAPE '§' OR testObj.field2 LIKE :s2 ESCAPE '§'\\\\)\");\n        assertThat(queryBuilder.getQueryParameters().get(\"s1\")).isEqualTo(\"%toto%\");\n        assertThat(queryBuilder.getQueryParameters().get(\"s2\")).isEqualTo(\"%toto%\");\n    }\n\n    @Test\n    public void should_generate_query_with_multiple_search_terms() {\n        //given\n        QueryBuilder queryBuilder = createQueryBuilder(\"SELECT testObj.* FROM test_object testObj\");\n        //when\n        queryBuilder.appendFilters(Collections.emptyList(),\n                new SearchFields(Arrays.asList(\"toto\", \"tata\"),\n                        Collections.singletonMap(TestObject.class,\n                                aSet(\"field1\", \"field2\"))));\n        //then\n        assertThat(queryBuilder.getQuery()).isEqualTo(\n                \"SELECT testObj.* FROM test_object testObj WHERE (testObj.field1 LIKE :s1 ESCAPE '§' \" +\n                        \"OR testObj.field1 LIKE :s2 ESCAPE '§' \" +\n                        \"OR testObj.field2 LIKE :s3 ESCAPE '§' \" +\n                        \"OR testObj.field2 LIKE :s4 ESCAPE '§')\");\n        assertThat(queryBuilder.getQueryParameters().get(\"s1\")).isEqualTo(\"%toto%\");\n        assertThat(queryBuilder.getQueryParameters().get(\"s2\")).isEqualTo(\"%tata%\");\n        assertThat(queryBuilder.getQueryParameters().get(\"s3\")).isEqualTo(\"%toto%\");\n        assertThat(queryBuilder.getQueryParameters().get(\"s4\")).isEqualTo(\"%tata%\");\n    }\n\n    @Test\n    public void should_generate_query_with_search_term_with_word_search() {\n        //given\n        QueryBuilder queryBuilder = createQueryBuilder(\"SELECT testObj.* FROM test_object testObj\");\n        //when\n        queryBuilder.appendFilters(Collections.emptyList(),\n                new SearchFields(Collections.singletonList(\"toto\"),\n                        Collections.singletonMap(TestObject.class,\n                                aSet(\"field1\", \"field2\"))));\n        //then\n        assertThat(queryBuilder.getQuery()).isEqualTo(\n                \"SELECT testObj.* FROM test_object testObj WHERE \" +\n                        \"(testObj.field1 LIKE :s1 ESCAPE '§' OR testObj.field2 LIKE :s2 ESCAPE '§')\");\n        assertThat(queryBuilder.getQueryParameters().get(\"s1\")).isEqualTo(\"%toto%\");\n        assertThat(queryBuilder.getQueryParameters().get(\"s2\")).isEqualTo(\"%toto%\");\n    }\n\n    private Set<String> aSet(String... fields) {\n        return new TreeSet<>(Arrays.asList(fields));\n    }\n\n    @Test\n    public void should_generate_query_with_search_term_and_filters() {\n        //given\n        QueryBuilder queryBuilder = createQueryBuilder(\"SELECT testObj.* FROM test_object testObj\");\n        //when\n        queryBuilder.appendFilters(Collections.singletonList(new FilterOption(TestObject.class, \"field1\", \"tata\")),\n                new SearchFields(Collections.singletonList(\"toto\"),\n                        Collections.singletonMap(TestObject.class, aSet(\"field1\", \"field2\"))));\n        //then\n        assertThat(queryBuilder.getQuery()).isEqualTo(\n                \"SELECT testObj.* FROM test_object testObj WHERE (testObj.field1 = :f1) AND (testObj.field2 LIKE :s1 ESCAPE '§')\");\n        assertThat(queryBuilder.getQueryParameters().get(\"f1\")).isEqualTo(\"tata\");\n        assertThat(queryBuilder.getQueryParameters().get(\"s1\")).isEqualTo(\"%toto%\");\n    }\n\n    @Test\n    public void should_escape_special_chars_with_escape_character_in_search_terms() {\n        //given\n        QueryBuilder queryBuilder = createQueryBuilder(\"SELECT testObj.* FROM test_object testObj\");\n        //when\n        queryBuilder.appendFilters(Collections.emptyList(),\n                new SearchFields(Collections.singletonList(\"the'value%with_special:_§§\"),\n                        Collections.singletonMap(TestObject.class, aSet(\"field1\"))));\n        //then\n        assertThat(queryBuilder.getQuery())\n                .isEqualTo(\n                        \"SELECT testObj.* FROM test_object testObj WHERE (testObj.field1 LIKE :s1 ESCAPE '§')\");\n        assertThat(queryBuilder.getQueryParameters().get(\"s1\")).isEqualTo(\"%the'value§%with§_special:§_§§§§%\");\n    }\n\n    @Test\n    public void should_generate_query_with_greater_or_equals_filter() {\n        //given\n        QueryBuilder queryBuilder = createQueryBuilder(\"SELECT testObj.* FROM test_object testObj\");\n        //when\n        queryBuilder.appendFilters(\n                Collections.singletonList(\n                        new FilterOption(TestObject.class, \"age\", 25, FilterOperationType.GREATER_OR_EQUALS)),\n                null);\n        //then\n        assertThat(queryBuilder.getQuery())\n                .isEqualTo(\"SELECT testObj.* FROM test_object testObj WHERE (testObj.age >= :f1)\");\n        assertThat(queryBuilder.getQueryParameters().get(\"f1\")).isEqualTo(25);\n    }\n\n    @Test\n    public void should_generate_query_with_greater_filter() throws Exception {\n        //given\n        QueryBuilder queryBuilder = createQueryBuilder(\"SELECT testObj.* FROM test_object testObj\");\n        //when\n        queryBuilder.appendFilters(\n                Collections.singletonList(new FilterOption(TestObject.class, \"age\", 25, FilterOperationType.GREATER)),\n                null);\n        //then\n        assertThat(queryBuilder.getQuery())\n                .isEqualTo(\"SELECT testObj.* FROM test_object testObj WHERE (testObj.age > :f1)\");\n        assertThat(queryBuilder.getQueryParameters().get(\"f1\")).isEqualTo(25);\n    }\n\n    @Test\n    public void should_generate_query_with_less_filter() {\n        //given\n        QueryBuilder queryBuilder = createQueryBuilder(\"SELECT testObj.* FROM test_object testObj\");\n        //when\n        queryBuilder.appendFilters(\n                Collections.singletonList(new FilterOption(TestObject.class, \"age\", 25, FilterOperationType.LESS)),\n                null);\n        //then\n        assertThat(queryBuilder.getQuery())\n                .isEqualTo(\"SELECT testObj.* FROM test_object testObj WHERE (testObj.age < :f1)\");\n        assertThat(queryBuilder.getQueryParameters().get(\"f1\")).isEqualTo(25);\n    }\n\n    private QueryBuilder createBaseQueryBuilder() {\n        return createQueryBuilder(\"SELECT testObj.* FROM test_object testObj\");\n    }\n\n    @Test\n    public void should_generate_query_with_less_or_equals_filter() {\n        //given\n        QueryBuilder queryBuilder = createQueryBuilder(\"SELECT testObj.* FROM test_object testObj\");\n        //when\n        queryBuilder.appendFilters(\n                Collections.singletonList(\n                        new FilterOption(TestObject.class, \"age\", 25, FilterOperationType.LESS_OR_EQUALS)),\n                null);\n        //then\n        assertThat(queryBuilder.getQuery())\n                .isEqualTo(\"SELECT testObj.* FROM test_object testObj WHERE (testObj.age <= :f1)\");\n        assertThat(queryBuilder.getQueryParameters().get(\"f1\")).isEqualTo(25);\n    }\n\n    @Test\n    public void should_generate_query_with_different_filter() {\n        //given\n        QueryBuilder queryBuilder = createQueryBuilder(\"SELECT testObj.* FROM test_object testObj\");\n        //when\n        queryBuilder.appendFilters(\n                Collections.singletonList(new FilterOption(TestObject.class, \"age\", 25, FilterOperationType.DIFFERENT)),\n                null);\n        //then\n        assertThat(queryBuilder.getQuery())\n                .isEqualTo(\"SELECT testObj.* FROM test_object testObj WHERE (testObj.age != :f1)\");\n        assertThat(queryBuilder.getQueryParameters().get(\"f1\")).isEqualTo(25);\n    }\n\n    @Test\n    public void should_generate_query_with_between_filter() {\n        //given\n        QueryBuilder queryBuilder = createQueryBuilder(\"SELECT testObj.* FROM test_object testObj\");\n        //when\n        queryBuilder.appendFilters(Collections.singletonList(new FilterOption(TestObject.class, \"age\", 25, 27)), null);\n        //then\n        assertThat(queryBuilder.getQuery()).isEqualTo(\n                \"SELECT testObj.* FROM test_object testObj WHERE ((:f1 <= testObj.age AND testObj.age <= :f2))\");\n        assertThat(queryBuilder.getQueryParameters().get(\"f1\")).isEqualTo(25);\n        assertThat(queryBuilder.getQueryParameters().get(\"f2\")).isEqualTo(27);\n    }\n\n    @Test\n    public void should_generate_query_with_parenthesis_filter() {\n        //given\n        QueryBuilder queryBuilder = createQueryBuilder(\"SELECT testObj.* FROM test_object testObj\");\n        //when\n        queryBuilder.appendFilters(Arrays.asList(new FilterOption(TestObject.class, \"age\", 12),\n                new FilterOption(FilterOperationType.AND),\n                new FilterOption(FilterOperationType.L_PARENTHESIS),\n                new FilterOption(TestObject.class, \"lastname\", \"john\"),\n                new FilterOption(FilterOperationType.OR),\n                new FilterOption(TestObject.class, \"lastname\", \"jack\"),\n                new FilterOption(FilterOperationType.R_PARENTHESIS)), null);\n        //then\n        assertThat(queryBuilder.getQuery())\n                .isEqualTo(\n                        \"SELECT testObj.* FROM test_object testObj WHERE (testObj.age = :f1 AND  (testObj.lastname = :f2 OR testObj.lastname = :f3 ))\");\n        assertThat(queryBuilder.getQueryParameters().get(\"f1\")).isEqualTo(12);\n        assertThat(queryBuilder.getQueryParameters().get(\"f2\")).isEqualTo(\"john\");\n        assertThat(queryBuilder.getQueryParameters().get(\"f3\")).isEqualTo(\"jack\");\n    }\n\n    @Test\n    public void should_generate_query_with_like_filter() {\n        //given\n        QueryBuilder queryBuilder = createQueryBuilder(\"SELECT testObj.* FROM test_object testObj\");\n        //when\n        queryBuilder.appendFilters(\n                Collections.singletonList(\n                        new FilterOption(TestObject.class, \"lastname\", \"jack\", FilterOperationType.LIKE)),\n                null);\n        //then\n        assertThat(queryBuilder.getQuery())\n                .isEqualTo(\"SELECT testObj.* FROM test_object testObj WHERE (testObj.lastname LIKE :f1 ESCAPE '§')\");\n        assertThat(queryBuilder.getQueryParameters().get(\"f1\")).isEqualTo(\"%jack%\");\n    }\n\n    @Test\n    public void should_generate_query_with_equals_filter_and_null_value() {\n        //given\n        QueryBuilder queryBuilder = createQueryBuilder(\"SELECT testObj.* FROM test_object testObj\");\n        //when\n        queryBuilder.appendFilters(\n                Collections.singletonList(\n                        new FilterOption(TestObject.class, \"lastname\", null, FilterOperationType.EQUALS)),\n                null);\n        //then\n        assertThat(queryBuilder.getQuery())\n                .isEqualTo(\"SELECT testObj.* FROM test_object testObj WHERE (testObj.lastname IS NULL)\");\n    }\n\n    @Test\n    public void should_generate_query_with_filter_having_convertible_value() {\n        //given\n        QueryBuilder queryBuilder = createQueryBuilder(\"SELECT testObj.* FROM test_object testObj\");\n        //when\n        queryBuilder.appendFilters(\n                Collections.singletonList(\n                        new FilterOption(TestObject.class, \"lastname\", TEST_ENUM.TEST1, FilterOperationType.EQUALS)),\n                null);\n        //then\n        assertThat(queryBuilder.getQuery())\n                .isEqualTo(\"SELECT testObj.* FROM test_object testObj WHERE (testObj.lastname = :f1)\");\n        assertThat(queryBuilder.getQueryParameters().get(\"f1\")).isEqualTo(TEST_ENUM.TEST1);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void should_throw_exception_if_class_is_not_mapped_in_filters() throws Exception {\n        //given\n        QueryBuilder queryBuilder = createQueryBuilder(\"SELECT testObj.* FROM test_object testObj\");\n        //when\n        queryBuilder.appendOrderByClause(\n                Collections.singletonList(new OrderByOption(PersistentObject.class, \"theValue\", OrderByType.ASC)),\n                PersistentObject.class);\n        queryBuilder.getQuery();\n    }\n\n    private enum TEST_ENUM implements EnumToObjectConvertible {\n\n        TEST1;\n\n        @Override\n        public int fromEnum() {\n            return ordinal();\n        }\n    }\n\n    @Test\n    public final void should_log_nothing_when_no_ORDER_BY_clause_in_query_and_no_checking_mode() throws Exception {\n\n        HQLQueryBuilder queryBuilder = createQueryBuilder(\"SELECT testObj.* FROM test_object testObj\",\n                descriptorWithoutOrderBy(), OrderByCheckingMode.NONE);\n\n        queryBuilder.build();\n\n        assertThat(systemOutRule.getLog()).isEmpty();\n    }\n\n    @Test\n    public final void should_log_nothing_when_no_ORDER_BY_clause_in_query_and_checking_mode_is_NONE()\n            throws Exception {\n        HQLQueryBuilder queryBuilder = createQueryBuilder(\"SELECT testObj.* FROM test_object testObj\",\n                descriptorWithoutOrderBy(), OrderByCheckingMode.NONE);\n\n        queryBuilder.build();\n\n        assertThat(systemOutRule.getLog()).isEmpty();\n    }\n\n    @Test\n    public final void should_log_when_no_ORDER_BY_clause_in_query_and_checking_mode_is_WARNING() throws Exception {\n        HQLQueryBuilder queryBuilder = createQueryBuilder(\"SELECT testObj.* FROM test_object testObj\",\n                descriptorWithoutOrderBy(), OrderByCheckingMode.WARNING);\n\n        queryBuilder.build();\n\n        assertThat(systemOutRule.getLog())\n                .contains(\"Query 'SELECT testObj.* FROM test_object testObj' does not contain 'ORDER BY' clause\");\n    }\n\n    @Test\n    public final void should_log_nothing_when_ORDER_BY_clause_in_query_and_checking_mode_is_STRICT()\n            throws Exception {\n        HQLQueryBuilder queryBuilder = createQueryBuilder(\"SELECT testObj.* FROM test_object testObj\",\n                descriptorWithOrderBy(), OrderByCheckingMode.NONE);\n\n        queryBuilder.build();\n\n        assertThat(systemOutRule.getLog()).isEmpty();\n    }\n\n    private SelectListDescriptor<TestObject> descriptorWithOrderBy() {\n        return new SelectListDescriptor<>(\"someQuery\", emptyMap(), TestObject.class, new QueryOptions(0, 100,\n                singletonList(new OrderByOption(TestObject.class, \"someField\", OrderByType.ASC))));\n    }\n\n    private SelectListDescriptor<TestObject> descriptorWithoutOrderBy() {\n        return new SelectListDescriptor<>(\"someQuery\", emptyMap(), TestObject.class, new QueryOptions(0, 100));\n    }\n\n    @Test\n    public void should_escapeString_escape_quote() {\n        new QueryGeneratorForFilters(emptyMap(), '%');\n        // 1) escape ' character by adding another ' character\n        final String s = QueryBuilder.escapeString(\"toto'toto\");\n\n        assertThat(s).isEqualTo(\"toto''toto\");\n    }\n\n    @Test\n    public void should_escapeString_do_not_escape_like_wildcard() {\n        new QueryGeneratorForFilters(emptyMap(), '%');\n        // 1) escape ' character by adding another ' character\n        final String s = QueryBuilder.escapeString(\"%to'to%t_oto%\");\n\n        assertThat(s).isEqualTo(\"%to''to%t_oto%\");\n    }\n\n    @Test\n    public void should_detect_WHERE_when_in_root_query() {\n        assertThat(QueryBuilder.hasWHEREInRootQuery(\"\" +\n                \"Select * \\n\" +\n                \"from user_\\n\" +\n                \"WHERE\\n\" +\n                \"something\\n\" +\n                \"ORDER BY name\\n\")).isTrue();\n    }\n\n    @Test\n    public void should_detect_WHERE_when_in_root_query_when_lowercase() {\n        assertThat(QueryBuilder.hasWHEREInRootQuery(\"\" +\n                \"Select * \\n\" +\n                \"from user_\\n\" +\n                \"where\\n\" +\n                \"something\\n\" +\n                \"ORDER BY name\\n\")).isTrue();\n    }\n\n    @Test\n    public void should_not_detect_WHERE_when_only_in_sub_query() {\n        assertThat(QueryBuilder.hasWHEREInRootQuery(\"\" +\n                \"Select * \\n\" +\n                \"from (\\n\" +\n                \"   Select * \\n\" +\n                \"   from user_\\n\" +\n                \"   WHERE\\n\" +\n                \"   something)\\n\" +\n                \")\\n\" +\n                \"ORDER BY name\")).isFalse();\n    }\n\n    @Test\n    public void should_not_detect_WHERE_when_not_present() {\n        assertThat(QueryBuilder.hasWHEREInRootQuery(\"\" +\n                \"Select * \\n\" +\n                \"from user_\\n\" +\n                \"ORDER BY name\\n\")).isFalse();\n    }\n\n    @Test\n    public void should_detect_WHERE_when_select_has_parenthesis() {\n        assertThat(QueryBuilder.hasWHEREInRootQuery(\"\" +\n                \"Select count(*) \\n\" +\n                \"from user_\\n\" +\n                \"WHERE (something)\\n\" +\n                \"ORDER BY name\\n\")).isTrue();\n    }\n\n    @Test\n    public void should_not_detect_WHERE_with_subqueries() {\n        assertThat(QueryBuilder.hasWHEREInRootQuery(\"\" +\n                \"Select * \\n\" +\n                \"from (\\n\" +\n                \"   Select * \\n\" +\n                \"   from user_\\n\" +\n                \"   WHERE\\n\" +\n                \"   something\\n\" +\n                \") user_\\n\" +\n                \"WHERE \\n (somethings)\")).isTrue();\n    }\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/test/java/org/bonitasoft/engine/persistence/QueryGeneratorForFiltersTest.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport static java.util.Collections.singletonMap;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\n\nimport java.util.Arrays;\nimport java.util.Collections;\n\nimport org.bonitasoft.engine.persistence.search.FilterOperationType;\nimport org.junit.Test;\n\npublic class QueryGeneratorForFiltersTest {\n\n    @Test\n    public void should_generate_query_with_one_boolean_filter() throws Exception {\n        QueryGeneratorForFilters generator = new QueryGeneratorForFilters(\n                singletonMap(TestObject.class.getName(), \"testObject\"), '%');\n\n        QueryGeneratorForFilters.QueryGeneratedFilters whereClause = generator.generate(Collections.singletonList(\n                new FilterOption(TestObject.class, \"enabled\", true, FilterOperationType.EQUALS)));\n\n        assertThat(whereClause.getFilters()).isEqualTo(\"testObject.enabled = :f1\");\n        assertThat(whereClause.getParameters()).containsOnly(entry(\"f1\", true));\n        assertThat(whereClause.getSpecificFilters()).containsOnly(\"testObject.enabled\");\n    }\n\n    @Test\n    public void should_generate_query_with_multiple_filters() throws Exception {\n        QueryGeneratorForFilters generator = new QueryGeneratorForFilters(\n                singletonMap(TestObject.class.getName(), \"testObject\"), '%');\n\n        QueryGeneratorForFilters.QueryGeneratedFilters whereClause = generator.generate(Arrays.asList(\n                new FilterOption(TestObject.class, \"enabled\", true, FilterOperationType.EQUALS),\n                new FilterOption(TestObject.class, \"name\", \"john\", FilterOperationType.EQUALS)));\n\n        assertThat(whereClause.getFilters()).isEqualTo(\"testObject.enabled = :f1 AND testObject.name = :f2\");\n        assertThat(whereClause.getParameters()).containsOnly(entry(\"f1\", true), entry(\"f2\", \"john\"));\n        assertThat(whereClause.getSpecificFilters()).containsOnly(\"testObject.enabled\", \"testObject.name\");\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/test/java/org/bonitasoft/engine/persistence/QueryGeneratorForSearchTermTest.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport static java.util.Collections.singletonList;\nimport static java.util.stream.Collectors.toSet;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\n\nimport java.util.stream.Stream;\n\nimport org.junit.Test;\n\npublic class QueryGeneratorForSearchTermTest {\n\n    @Test\n    public void should_getQueryFilters_append_OR_clause_when_wordSearch_is_enabled() {\n        QueryGeneratorForSearchTerm generator = new QueryGeneratorForSearchTerm('$');\n\n        QueryGeneratorForSearchTerm.QueryGeneratedSearchTerms query = generator\n                .generate(Stream.of(\"field1\").collect(toSet()), singletonList(\"termA\"));\n\n        assertThat(query.getSearch()).isEqualTo(\"field1 LIKE :s1 ESCAPE '$'\");\n        assertThat(query.getParameters()).containsOnly(\n                entry(\"s1\", \"%termA%\"));\n    }\n\n    @Test\n    public void should_escape_special_chars() {\n        QueryGeneratorForSearchTerm generator = new QueryGeneratorForSearchTerm('@');\n\n        QueryGeneratorForSearchTerm.QueryGeneratedSearchTerms query = generator\n                .generate(Stream.of(\"field1\").collect(toSet()), singletonList(\"100%\"));\n\n        assertThat(query.getSearch()).isEqualTo(\"field1 LIKE :s1 ESCAPE '@'\");\n        assertThat(query.getParameters()).containsOnly(\n                entry(\"s1\", \"%100@%%\"));\n    }\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/test/java/org/bonitasoft/engine/persistence/QueryOptionsTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.junit.Test;\n\npublic class QueryOptionsTest {\n\n    @Test\n    public void getNextPageShouldPreserveOrderOptions() throws Exception {\n        // given:\n        final QueryOptions queryOptions = new QueryOptions(0, 10,\n                list(new OrderByOption(PersistentObject.class, \"fieldName\", OrderByType.ASC)),\n                list(new FilterOption(PersistentObject.class, \"fieldName\")), null);\n\n        // when:\n        final QueryOptions nextPage = QueryOptions.getNextPage(queryOptions);\n\n        // then:\n        assertThat(nextPage.getOrderByOptions()).isNotNull();\n        assertThat(nextPage.getOrderByOptions()).hasSize(1);\n    }\n\n    @Test\n    public void getNextPageShouldPreserveFilters() throws Exception {\n        // given:\n        final QueryOptions queryOptions = new QueryOptions(0, 10,\n                list(new OrderByOption(PersistentObject.class, \"fieldName\", OrderByType.ASC)),\n                list(new FilterOption(PersistentObject.class, \"fieldName\")), null);\n\n        // when:\n        final QueryOptions nextPage = QueryOptions.getNextPage(queryOptions);\n\n        // then:\n        assertThat(nextPage.getFilters()).isNotNull();\n        assertThat(nextPage.getFilters()).hasSize(1);\n    }\n\n    @Test\n    public void getNextPageShouldPreserveSearchFields() throws Exception {\n        // given:\n        final QueryOptions queryOptions = new QueryOptions(0, 10,\n                list(new OrderByOption(PersistentObject.class, \"fieldName\", OrderByType.ASC)),\n                list(new FilterOption(PersistentObject.class, \"fieldName\")), new SearchFields(null, null));\n\n        // when:\n        final QueryOptions nextPage = QueryOptions.getNextPage(queryOptions);\n\n        // then:\n        assertThat(nextPage.getMultipleFilter()).isNotNull();\n    }\n\n    private List list(final Object o) {\n        return Arrays.asList(o);\n    }\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/test/java/org/bonitasoft/engine/persistence/SQLQueryBuilderTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport static java.util.Collections.singletonMap;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.verify;\n\nimport java.util.Map;\n\nimport org.hibernate.query.NativeQuery;\nimport org.hibernate.query.Query;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Baptiste Mesta\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class SQLQueryBuilderTest {\n\n    @Mock\n    private NativeQuery mockedQuery;\n    @Mock\n    private Query query;\n    private static final char LIKE_ESCAPE_CHARACTER = '§';\n    private Map<String, String> classAliasMappings = singletonMap(TestObject.class.getName(), \"testObj\");\n\n    private SQLQueryBuilder createQueryBuilder(String baseQuery) {\n        doReturn(baseQuery).when(query).getQueryString();\n        return new SQLQueryBuilder(null, query, new DefaultOrderByBuilder(),\n                classAliasMappings,\n                LIKE_ESCAPE_CHARACTER, OrderByCheckingMode.NONE, null);\n    }\n\n    @Test\n    public void should_generate_query_with_boolean_parameter() throws Exception {\n        //given\n        String baseQuery = \"SELECT testObj.* FROM test_object testObj WHERE testObj.enabled = :trueValue\";\n        doReturn(baseQuery).when(mockedQuery).getQueryString();\n        SQLQueryBuilder queryBuilder = createQueryBuilder(baseQuery);\n        //when\n        queryBuilder.addConstantsAsParameters(mockedQuery);\n        //then\n        verify(mockedQuery).setParameter(\"trueValue\", true);\n    }\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/test/java/org/bonitasoft/engine/persistence/SelectListDescriptorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\nimport java.util.Collections;\n\nimport org.junit.Test;\n\n/**\n * @author Celine Souchet\n */\npublic class SelectListDescriptorTest {\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.persistence.SelectListDescriptor#SelectListDescriptor(java.lang.String, java.util.Map, java.lang.Class, org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test(expected = IllegalArgumentException.class)\n    public final void throw_exception_when_contruct_object_without_query_options() {\n        new SelectListDescriptor<Object>(\"queryName\", Collections.<String, Object> emptyMap(), PersistentObject.class,\n                null);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.persistence.SelectListDescriptor#SelectListDescriptor(java.lang.String, java.util.Map, java.lang.Class, java.lang.Class, org.bonitasoft.engine.persistence.QueryOptions)}\n     * .\n     */\n    @Test(expected = IllegalArgumentException.class)\n    public final void throw_exception_when_contruct_object_without_query_options2() {\n        new SelectListDescriptor<Object>(\"queryName\", Collections.<String, Object> emptyMap(), PersistentObject.class,\n                Object.class, null);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/test/java/org/bonitasoft/engine/persistence/TestObject.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.persistence;\n\n/**\n * @author Baptiste Mesta\n */\npublic class TestObject implements PersistentObject {\n\n    @Override\n    public long getId() {\n        return 0;\n    }\n\n    @Override\n    public void setId(long id) {\n\n    }\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/test/java/org/bonitasoft/engine/recorder/impl/RecorderImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.recorder.impl;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.argThat;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport java.util.Collections;\nimport java.util.UUID;\n\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.events.model.SEvent;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.bonitasoft.engine.services.PersistenceService;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.AdditionalAnswers;\nimport org.mockito.ArgumentMatcher;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Baptiste Mesta.\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class RecorderImplTest {\n\n    @Mock\n    private PersistenceService persistenceService;\n    @Mock\n    private EventService eventService;\n    @InjectMocks\n    private RecorderImpl recorder;\n\n    @Test\n    public void should_fire_event_when_recording_an_insert() throws Exception {\n        when(persistenceService.insert(any())).thenAnswer(AdditionalAnswers.returnsFirstArg());\n        MyPersistentObject entity = entity();\n        recorder.recordInsert(insertRecord(entity), \"theEvent\");\n\n        verify(eventService).fireEvent(argThat(match(\"theEvent_CREATED\", entity)));\n    }\n\n    @Test\n    public void should_fire_event_when_recording_an_update() throws Exception {\n        MyPersistentObject entity = entity();\n        recorder.recordUpdate(updateRecord(entity), \"theEvent\");\n\n        verify(eventService).fireEvent(argThat(match(\"theEvent_UPDATED\", entity)));\n    }\n\n    @Test\n    public void should_fire_event_when_recording_a_delete() throws Exception {\n        MyPersistentObject entity = entity();\n        recorder.recordDelete(deleteRecord(entity), \"theEvent\");\n\n        verify(eventService).fireEvent(argThat(match(\"theEvent_DELETED\", entity)));\n    }\n\n    protected ArgumentMatcher<SEvent> match(String type, Object entity) {\n        return sEvent -> sEvent.getType().equals(type) && sEvent.getObject().equals(entity);\n    }\n\n    private InsertRecord insertRecord(PersistentObject entity) {\n        return new InsertRecord(entity);\n    }\n\n    private UpdateRecord updateRecord(PersistentObject entity) {\n        return UpdateRecord.buildSetFields(entity, Collections.emptyMap());\n    }\n\n    private DeleteRecord deleteRecord(PersistentObject entity) {\n        return new DeleteRecord(entity);\n    }\n\n    private MyPersistentObject entity() {\n        return new MyPersistentObject();\n    }\n\n    private static class MyPersistentObject implements PersistentObject {\n\n        private final long id = UUID.randomUUID().getLeastSignificantBits();\n\n        @Override\n        public long getId() {\n            return id;\n        }\n\n        @Override\n        public void setId(long id) {\n        }\n\n    }\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/test/java/org/bonitasoft/engine/sequence/SequenceDAOTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.sequence;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\nimport static org.bonitasoft.engine.sequence.SequenceDAO.NEXT_ID;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.verify;\n\nimport java.sql.Connection;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\n\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnit;\nimport org.mockito.junit.MockitoRule;\n\npublic class SequenceDAOTest {\n\n    @Rule\n    public MockitoRule mockitoRule = MockitoJUnit.rule();\n    @Mock\n    private Connection connection;\n    @Mock\n    private PreparedStatement preparedStatement;\n    @Mock\n    private ResultSet resultSet;\n    @InjectMocks\n    private SequenceDAO sequenceDAO;\n\n    @Before\n    public void before() {\n        sequenceDAO = new SequenceDAO(connection);\n    }\n\n    @Test\n    public void should_getNextId_from_database() throws Exception {\n        doReturn(preparedStatement).when(connection).prepareStatement(SequenceDAO.SELECT_BY_ID);\n        doReturn(resultSet).when(preparedStatement).executeQuery();\n        doReturn(true, false).when(resultSet).next();\n        doReturn(2349L).when(resultSet).getLong(NEXT_ID);\n\n        long nextId = sequenceDAO.selectById(123L);\n\n        verify(preparedStatement).setLong(1, 123L);\n        assertThat(nextId).isEqualTo(2349);\n    }\n\n    @Test\n    public void should_getNextId_fail_when_there_is_more_than_one_result() throws Exception {\n        doReturn(preparedStatement).when(connection).prepareStatement(SequenceDAO.SELECT_BY_ID);\n        doReturn(resultSet).when(preparedStatement).executeQuery();\n        doReturn(true, true, false).when(resultSet).next();\n\n        assertThatThrownBy(() -> sequenceDAO.selectById(123L))\n                .isInstanceOf(SQLException.class)\n                .hasMessage(\"Did not expect more than one value for id: 123\");\n    }\n\n    @Test\n    public void should_getNextId_fail_when_there_is_no_result() throws Exception {\n        doReturn(preparedStatement).when(connection).prepareStatement(SequenceDAO.SELECT_BY_ID);\n        doReturn(resultSet).when(preparedStatement).executeQuery();\n        doReturn(false).when(resultSet).next();\n\n        assertThatThrownBy(() -> sequenceDAO.selectById(123L))\n                .isInstanceOf(SObjectNotFoundException.class)\n                .hasMessage(\"Found no row for id: 123\");\n    }\n\n    @Test\n    public void should_getNextId_fail_when_the_result_is_null() throws Exception {\n        doReturn(preparedStatement).when(connection).prepareStatement(SequenceDAO.SELECT_BY_ID);\n        doReturn(resultSet).when(preparedStatement).executeQuery();\n        doReturn(true, false).when(resultSet).next();\n        doReturn(true).when(resultSet).wasNull();\n\n        assertThatThrownBy(() -> sequenceDAO.selectById(123L))\n                .isInstanceOf(SQLException.class)\n                .hasMessage(\"Did not expect a null value for the column nextid\");\n    }\n\n    @Test\n    public void should_update_database_with_next_sequence_id() throws Exception {\n        doReturn(preparedStatement).when(connection).prepareStatement(SequenceDAO.UPDATE_SEQUENCE);\n        doReturn(1).when(preparedStatement).executeUpdate();\n\n        sequenceDAO.updateSequence(11233L, 123L);\n\n        verify(preparedStatement).setObject(1, 11233L);\n        verify(preparedStatement).setObject(2, 123L);\n    }\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/test/java/org/bonitasoft/engine/sequence/SequenceManagerImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.sequence;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\nimport static org.mockito.ArgumentMatchers.anyLong;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.Mockito.*;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport javax.sql.DataSource;\n\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.lock.BonitaLock;\nimport org.bonitasoft.engine.lock.LockService;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.mockito.InOrder;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnit;\nimport org.mockito.junit.MockitoRule;\n\npublic class SequenceManagerImplTest {\n\n    private static final String OBJECT_w_2 = \"OBJECT_w_2\";\n    private static final String OBJECT_w_5 = \"OBJECT_w_5\";\n    private static final String OBJECT_w_100 = \"OBJECT_w_100\";\n    private static final String OBJECT_w_1000 = \"OBJECT_w_1000\";\n    private static final long SEQUENCE_w_2 = 553L;\n    private static final long SEQUENCE_w_5 = 554L;\n    private static final long SEQUENCE_w_100 = 555L;\n    private static final long SEQUENCE_w_1000 = 556L;\n    private static final int RETRIES = 2;\n\n    @Rule\n    public MockitoRule mockitoRule = MockitoJUnit.rule();\n\n    @Mock\n    private SequenceDAO sequenceDAO;\n    @Mock\n    private LockService lockService;\n    @Mock\n    private BonitaLock lock;\n    @Mock\n    private DataSource dataSource;\n    @Mock\n    private Connection connection;\n    @Mock\n    private SequenceMappingProvider sequenceMappingProvider;\n\n    private SequenceManagerImpl sequenceManager;\n\n    @Before\n    public void before() throws Exception {\n        doReturn(connection).when(dataSource).getConnection();\n        final List<SequenceMapping> sequenceMappings = List.of(\n                seq(OBJECT_w_2, SEQUENCE_w_2, 2),\n                seq(OBJECT_w_5, SEQUENCE_w_5, 5),\n                seq(OBJECT_w_100, SEQUENCE_w_100, 100),\n                seq(OBJECT_w_1000, SEQUENCE_w_1000, 1000));\n\n        doReturn(sequenceMappings).when(sequenceMappingProvider).getSequenceMappings();\n\n        doReturn(lock).when(lockService).lock(anyLong(), anyString());\n\n        sequenceManager = new SequenceManagerImpl(lockService, sequenceMappingProvider,\n                dataSource, RETRIES, 1, 1) {\n\n            @Override\n            SequenceDAO createDao(Connection connection) {\n                return sequenceDAO;\n            }\n        };\n    }\n\n    private SequenceMapping seq(String className, long sequenceId, int rangeSize) {\n        return new SequenceMapping(className, sequenceId, rangeSize);\n    }\n\n    @Test\n    public void should_get_next_id_from_database() throws Exception {\n        doReturn(100L).when(sequenceDAO).selectById(SEQUENCE_w_100);\n\n        long nextId = sequenceManager.getNextId(OBJECT_w_100);\n\n        assertThat(nextId).isEqualTo(100);\n    }\n\n    @Test\n    public void should_get_next_id_once_all_ids_is_range_are_used() throws Exception {\n        doReturn(100L, 200L).when(sequenceDAO).selectById(SEQUENCE_w_5);\n\n        assertThat(sequenceManager.getNextId(OBJECT_w_5)).isEqualTo(100);\n        assertThat(sequenceManager.getNextId(OBJECT_w_5)).isEqualTo(101);\n        assertThat(sequenceManager.getNextId(OBJECT_w_5)).isEqualTo(102);\n        assertThat(sequenceManager.getNextId(OBJECT_w_5)).isEqualTo(103);\n        assertThat(sequenceManager.getNextId(OBJECT_w_5)).isEqualTo(104);\n\n        //all ids are taken, ask for new ones\n        assertThat(sequenceManager.getNextId(OBJECT_w_5)).isEqualTo(200);\n    }\n\n    @Test\n    public void should_get_next_id_once_all_ids_in_range_are_used_for_each_object() throws Exception {\n        doReturn(100L, 200L).when(sequenceDAO).selectById(SEQUENCE_w_5);\n        doReturn(1100L, 1200L).when(sequenceDAO).selectById(SEQUENCE_w_2);\n\n        assertThat(sequenceManager.getNextId(OBJECT_w_5)).isEqualTo(100);\n        assertThat(sequenceManager.getNextId(OBJECT_w_2)).isEqualTo(1100);\n        assertThat(sequenceManager.getNextId(OBJECT_w_5)).isEqualTo(101);\n        assertThat(sequenceManager.getNextId(OBJECT_w_2)).isEqualTo(1101);\n        assertThat(sequenceManager.getNextId(OBJECT_w_5)).isEqualTo(102);\n        assertThat(sequenceManager.getNextId(OBJECT_w_2)).isEqualTo(1200);\n        assertThat(sequenceManager.getNextId(OBJECT_w_5)).isEqualTo(103);\n        assertThat(sequenceManager.getNextId(OBJECT_w_5)).isEqualTo(104);\n        assertThat(sequenceManager.getNextId(OBJECT_w_5)).isEqualTo(200);\n    }\n\n    @Test\n    public void high_concurrency_should_always_return_a_next_id() throws Exception {\n        // given:\n        doReturn(100L).when(sequenceDAO).selectById(SEQUENCE_w_2);\n\n        // when:\n        final RunnableWithFailures runnable = new RunnableWithFailures();\n        List<Thread> threads = new ArrayList<>();\n        for (int i = 0; i < 100; i++) {\n            threads.add(new Thread(runnable));\n        }\n        for (Thread thread : threads) {\n            thread.start();\n        }\n        for (Thread thread : threads) {\n            thread.join();\n        }\n\n        // then:\n        assertThat(runnable.failures).isEqualTo(0);\n    }\n\n    private class RunnableWithFailures implements Runnable {\n\n        int failures = 0;\n\n        @Override\n        public void run() {\n            try {\n                for (int i = 0; i < 100; i++) {\n                    sequenceManager.getNextId(OBJECT_w_2);\n                }\n            } catch (SObjectNotFoundException | IllegalStateException e) {\n                failures++;\n            }\n        }\n    }\n\n    @Test\n    public void should_update_sequence_inside_a_lock() throws Exception {\n        doReturn(100L).when(sequenceDAO).selectById(SEQUENCE_w_5);\n        InOrder inOrder = inOrder(lockService, connection, sequenceDAO);\n\n        sequenceManager.getNextId(OBJECT_w_5);\n\n        inOrder.verify(lockService).lock(SEQUENCE_w_5, SequenceManagerImpl.SEQUENCE);\n        inOrder.verify(sequenceDAO).selectById(SEQUENCE_w_5);\n        inOrder.verify(sequenceDAO).updateSequence(105L, SEQUENCE_w_5);\n        inOrder.verify(connection).commit();\n        inOrder.verify(lockService).unlock(lock);\n    }\n\n    @Test\n    public void should_update_sequence_with_id_of_next_range() throws Exception {\n        doReturn(100L).when(sequenceDAO).selectById(SEQUENCE_w_5);\n        doReturn(100L).when(sequenceDAO).selectById(SEQUENCE_w_1000);\n\n        sequenceManager.getNextId(OBJECT_w_5);\n        sequenceManager.getNextId(OBJECT_w_1000);\n\n        verify(sequenceDAO).updateSequence(105L, SEQUENCE_w_5);\n        verify(sequenceDAO).updateSequence(1100L, SEQUENCE_w_1000);\n    }\n\n    @Test\n    public void should_retry_to_update_sequence_when_select_of_next_id_fails() throws Exception {\n        doThrow(new SQLException(\"SQL error\")).doReturn(200L).when(sequenceDAO).selectById(SEQUENCE_w_5);\n\n        long nextId = sequenceManager.getNextId(OBJECT_w_5);\n\n        assertThat(nextId).isEqualTo(200L);\n    }\n\n    @Test\n    public void should_retry_to_update_sequence_when_update_of_next_range_fails() throws Exception {\n        doReturn(200L).when(sequenceDAO).selectById(SEQUENCE_w_5);\n        doThrow(new SQLException(\"SQL error\")).doNothing().when(sequenceDAO).updateSequence(205, SEQUENCE_w_5);\n\n        long nextId = sequenceManager.getNextId(OBJECT_w_5);\n\n        assertThat(nextId).isEqualTo(200L);\n    }\n\n    @Test\n    public void should_retry_to_update_sequence_when_commit_fails() throws Exception {\n        doReturn(200L).when(sequenceDAO).selectById(SEQUENCE_w_5);\n        doNothing().when(sequenceDAO).updateSequence(205, SEQUENCE_w_5);\n        doThrow(new SQLException(\"commit error\")).doNothing().when(connection).commit();\n\n        long nextId = sequenceManager.getNextId(OBJECT_w_5);\n\n        assertThat(nextId).isEqualTo(200L);\n    }\n\n    @Test\n    public void should_fail_to_update_sequence_when_select_of_next_id_fails() throws Exception {\n        doThrow(new SQLException(\"SQL error\")).doThrow(new SQLException(\"SQL error\")).doReturn(200L).when(sequenceDAO)\n                .selectById(SEQUENCE_w_5);\n\n        assertThatThrownBy(() -> sequenceManager.getNextId(OBJECT_w_5))\n                .hasMessage(\"Unable to get a sequence id for 554\");\n    }\n\n    @Test\n    public void should_fail_to_update_sequence_when_update_of_next_range_fails() throws Exception {\n        doReturn(200L).when(sequenceDAO).selectById(SEQUENCE_w_5);\n        doThrow(new SQLException(\"SQL error\")).doThrow(new SQLException(\"SQL error\")).doNothing().when(sequenceDAO)\n                .updateSequence(205, SEQUENCE_w_5);\n\n        assertThatThrownBy(() -> sequenceManager.getNextId(OBJECT_w_5))\n                .hasMessage(\"Unable to get a sequence id for 554\");\n    }\n\n    @Test\n    public void should_fail_to_update_sequence_when_commit_fails() throws Exception {\n        doReturn(200L).when(sequenceDAO).selectById(SEQUENCE_w_5);\n        doNothing().when(sequenceDAO).updateSequence(205, SEQUENCE_w_5);\n        doThrow(new SQLException(\"commit error\")).doThrow(new SQLException(\"commit error\")).doNothing().when(connection)\n                .commit();\n\n        assertThatThrownBy(() -> sequenceManager.getNextId(OBJECT_w_5))\n                .hasMessage(\"Unable to get a sequence id for 554\");\n    }\n\n    @Test\n    public void should_not_increase_next_id_when_we_are_unable_to_commit_transaction() throws Exception {\n        doReturn(200L).when(sequenceDAO).selectById(SEQUENCE_w_5);\n        doNothing().when(sequenceDAO).updateSequence(205, SEQUENCE_w_5);\n        doThrow(new SQLException(\"commit error\")).when(connection).commit();\n\n        assertThatThrownBy(() -> sequenceManager.getNextId(OBJECT_w_5))\n                .hasMessage(\"Unable to get a sequence id for 554\");\n\n        // should also fail and not return 200 (saved in memory)\n        assertThatThrownBy(() -> sequenceManager.getNextId(OBJECT_w_5))\n                .hasMessage(\"Unable to get a sequence id for 554\");\n    }\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/test/java/org/bonitasoft/engine/sequence/SequenceRangeTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.sequence;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.Optional;\n\nimport org.junit.Test;\n\npublic class SequenceRangeTest {\n\n    @Test\n    public void should_give_next_id_in_the_sequence() {\n        SequenceRange sequenceRange = new SequenceRange(4);\n        sequenceRange.updateToNextRange(10);\n\n        Optional<Long> nextAvailableId = sequenceRange.getNextAvailableId();\n\n        assertThat(nextAvailableId).isPresent().contains(10L);\n    }\n\n    @Test\n    public void should_not_give_id_if_sequence_is_not_initialized() {\n        SequenceRange sequenceRange = new SequenceRange(4);\n\n        Optional<Long> nextAvailableId = sequenceRange.getNextAvailableId();\n\n        assertThat(nextAvailableId).isNotPresent();\n    }\n\n    @Test\n    public void should_give_all_if_from_range() {\n        SequenceRange sequenceRange = new SequenceRange(3);\n        sequenceRange.updateToNextRange(1);\n\n        assertThat(sequenceRange.getNextAvailableId()).isPresent().contains(1L);\n        assertThat(sequenceRange.getNextAvailableId()).isPresent().contains(2L);\n        assertThat(sequenceRange.getNextAvailableId()).isPresent().contains(3L);\n        assertThat(sequenceRange.getNextAvailableId()).isNotPresent();\n    }\n\n    @Test\n    public void should_not_give_id_when_sequence_is_completed() {\n        SequenceRange sequenceRange = new SequenceRange(2);\n        sequenceRange.updateToNextRange(1);\n\n        assertThat(sequenceRange.getNextAvailableId()).isPresent();\n        assertThat(sequenceRange.getNextAvailableId()).isPresent();\n\n        assertThat(sequenceRange.getNextAvailableId()).isNotPresent();\n        assertThat(sequenceRange.getNextAvailableId()).isNotPresent();\n        assertThat(sequenceRange.getNextAvailableId()).isNotPresent();\n    }\n}\n"
  },
  {
    "path": "services/bonita-persistence/src/test/resources/logback-test.xml",
    "content": "<configuration>\n\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->\n        <encoder>\n            <pattern>|%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <logger name=\"org.bonitasoft\" level=\"INFO\" />\n    <logger name=\"org.bonitasoft.engine.sequence\" level=\"DEBUG\" />\n\n    <root level=\"WARN\">\n        <appender-ref ref=\"STDOUT\" />\n    </root>\n</configuration>\n"
  },
  {
    "path": "services/bonita-platform/build.gradle",
    "content": "dependencies {\n    api project(':services:bonita-cache')\n    api project(':services:bonita-builder')\n    api project(':services:bonita-persistence')\n    api project(':services:bonita-commons')\n    testImplementation project(':services:bonita-events')\n    testImplementation(libs.mockitoCore)\n    testImplementation(libs.assertj)\n    testImplementation(libs.logback)\n\n    annotationProcessor(libs.lombok)\n    compileOnly(libs.lombok)\n}\n\ngroup = 'org.bonitasoft.engine.platform'\n\ndef generatePlatformProperties = tasks.register(\"generatePlatformProperties\") {\n    def outputDir = layout.buildDirectory.dir(\"generated/main/resources/org/bonitasoft/engine/platform/model/impl\")\n    def outputFile = layout.buildDirectory.file(\"generated/main/resources/org/bonitasoft/engine/platform/model/impl/platform.properties\")\n    def projectVersion = project.version\n\n    doFirst{\n        outputDir.get().asFile.mkdirs()\n        outputFile.get().asFile.text = \"version $projectVersion\"\n    }\n    inputs.property \"version\", projectVersion\n    outputs.file outputFile\n}\ntasks.processResources.dependsOn generatePlatformProperties\nsourceSets {\n    main {\n        resources {\n            srcDir(layout.buildDirectory.dir(\"generated/main/resources\"))\n        }\n    }\n}\n"
  },
  {
    "path": "services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/PlatformRetriever.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform;\n\nimport org.bonitasoft.engine.platform.exception.SPlatformNotFoundException;\nimport org.bonitasoft.engine.platform.model.SPlatform;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface PlatformRetriever {\n\n    SPlatform getPlatform() throws SPlatformNotFoundException;\n}\n"
  },
  {
    "path": "services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/PlatformService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform;\n\nimport org.bonitasoft.engine.platform.exception.*;\nimport org.bonitasoft.engine.platform.model.SPlatform;\nimport org.bonitasoft.engine.platform.model.SPlatformProperties;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Charles Souillard\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic interface PlatformService {\n\n    String PLATFORM = \"PLATFORM\";\n\n    /**\n     * Retrieve the platform from the cache\n     * No need to be in a transaction\n     *\n     * @return sPlatform\n     * @throws SPlatformNotFoundException\n     *         occurs when the identifier does not refer to an existing sPlatform\n     * @since 6.0\n     */\n    SPlatform getPlatform() throws SPlatformNotFoundException;\n\n    /**\n     * Set status of the tenant into activated\n     *\n     * @throws STenantNotFoundException occurs when the identifier does not refer to an existing sTenant\n     * @throws STenantActivationException occurs when an exception is thrown during activating sTenant\n     * @since 6.0\n     */\n    void resumeServices() throws STenantNotFoundException, STenantActivationException, SPlatformNotFoundException,\n            SPlatformUpdateException;\n\n    /**\n     * update status of tenant object to PAUSED\n     */\n    void pauseServices() throws STenantUpdateException, STenantNotFoundException, SPlatformNotFoundException,\n            SPlatformUpdateException;\n\n    /**\n     * Return true if the platform is created, else return false.\n     *\n     * @return true or false\n     * @since 6.0\n     */\n    boolean isPlatformCreated();\n\n    /**\n     * Return the platform properties\n     *\n     * @return The platform properties\n     * @since 6.1\n     */\n    SPlatformProperties getSPlatformProperties();\n\n    void updatePlatform(EntityUpdateDescriptor descriptor) throws SPlatformUpdateException;\n}\n"
  },
  {
    "path": "services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/exception/SDeletingActivatedTenantException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.exception;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SDeletingActivatedTenantException extends SBonitaException {\n\n    private static final long serialVersionUID = 5003988198140435562L;\n\n    public SDeletingActivatedTenantException() {\n        super();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/exception/SPlatformDeletionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.exception;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * Exception related to the platform module\n *\n * @author Charles Souillard\n */\npublic class SPlatformDeletionException extends SBonitaException {\n\n    private static final long serialVersionUID = 7615655279956204016L;\n\n    public SPlatformDeletionException(final String message) {\n        super(message);\n    }\n\n    public SPlatformDeletionException(final Throwable e) {\n        super(e);\n    }\n\n    public SPlatformDeletionException(final String message, final Throwable e) {\n        super(message, e);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/exception/SPlatformNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.exception;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * Exception related to the platform module\n *\n * @author Charles Souillard\n */\npublic class SPlatformNotFoundException extends SBonitaException {\n\n    private static final long serialVersionUID = 7615655279956204016L;\n\n    public SPlatformNotFoundException(final String message) {\n        super(message);\n    }\n\n    public SPlatformNotFoundException(final Throwable e) {\n        super(e);\n    }\n\n    public SPlatformNotFoundException(final String message, final Throwable e) {\n        super(message, e);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/exception/SPlatformUpdateException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.exception;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * Exception related to the platform module\n *\n * @author Charles Souillard\n */\npublic class SPlatformUpdateException extends SBonitaException {\n\n    private static final long serialVersionUID = 7615655279956204016L;\n\n    public SPlatformUpdateException(final String message) {\n        super(message);\n    }\n\n    public SPlatformUpdateException(final Throwable e) {\n        super(e);\n    }\n\n    public SPlatformUpdateException(final String message, final Throwable e) {\n        super(message, e);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/exception/STenantActivationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.exception;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * Exception related to the platform module\n *\n * @author Lu Kai\n */\npublic class STenantActivationException extends SBonitaException {\n\n    private static final long serialVersionUID = 5049049035847825930L;\n\n    public STenantActivationException(final String message) {\n        super(message);\n    }\n\n    public STenantActivationException(final Throwable e) {\n        super(e);\n    }\n\n    public STenantActivationException(final String message, final Throwable e) {\n        super(message, e);\n    }\n}\n"
  },
  {
    "path": "services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/exception/STenantAlreadyExistException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.exception;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * Exception related to the platform module\n *\n * @author Charles Souillard\n */\npublic class STenantAlreadyExistException extends SBonitaException {\n\n    private static final long serialVersionUID = 7615655279956204016L;\n\n    public STenantAlreadyExistException(final String message) {\n        super(message);\n    }\n\n    public STenantAlreadyExistException(final Throwable e) {\n        super(e);\n    }\n\n    public STenantAlreadyExistException(final String message, final Throwable e) {\n        super(message, e);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/exception/STenantCreationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.exception;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * Exception related to the platform module\n *\n * @author Charles Souillard\n */\npublic class STenantCreationException extends SBonitaException {\n\n    private static final long serialVersionUID = 7615655279956204016L;\n\n    public STenantCreationException(final String message) {\n        super(message);\n    }\n\n    public STenantCreationException(final Throwable e) {\n        super(e);\n    }\n\n    public STenantCreationException(final String message, final Throwable e) {\n        super(message, e);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/exception/STenantDeactivationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.exception;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * Exception related to the platform module\n *\n * @author Lu Kai\n */\npublic class STenantDeactivationException extends SBonitaException {\n\n    private static final long serialVersionUID = 5049049035847825930L;\n\n    public STenantDeactivationException(final String message) {\n        super(message);\n    }\n\n    public STenantDeactivationException(final Throwable e) {\n        super(e);\n    }\n\n    public STenantDeactivationException(final String message, final Throwable e) {\n        super(message, e);\n    }\n}\n"
  },
  {
    "path": "services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/exception/STenantDeletionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.exception;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * Exception related to the platform module\n *\n * @author Charles Souillard\n */\npublic class STenantDeletionException extends SBonitaException {\n\n    private static final long serialVersionUID = 7615655279956204016L;\n\n    public STenantDeletionException(final String message) {\n        super(message);\n    }\n\n    public STenantDeletionException(final Throwable e) {\n        super(e);\n    }\n\n    public STenantDeletionException(final String message, final Throwable e) {\n        super(message, e);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/exception/STenantException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.exception;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * Exception related to the platform module\n *\n * @author Charles Souillard\n */\npublic class STenantException extends SBonitaException {\n\n    private static final long serialVersionUID = 7615655279956204016L;\n\n    public STenantException(final String message) {\n        super(message);\n    }\n\n    public STenantException(final Throwable e) {\n        super(e);\n    }\n\n    public STenantException(final String message, final Throwable e) {\n        super(message, e);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/exception/STenantNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.exception;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * Exception related to the platform module\n *\n * @author Charles Souillard\n */\npublic class STenantNotFoundException extends SBonitaException {\n\n    private static final long serialVersionUID = 7615655279956204016L;\n\n    public STenantNotFoundException(final String message) {\n        super(message);\n    }\n\n    public STenantNotFoundException(final Throwable e) {\n        super(e);\n    }\n\n    public STenantNotFoundException(final String message, final Throwable e) {\n        super(message, e);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/exception/STenantUpdateException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.exception;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * Exception related to the platform module\n *\n * @author Charles Souillard\n */\npublic class STenantUpdateException extends SBonitaException {\n\n    private static final long serialVersionUID = 7615655279956204016L;\n\n    public STenantUpdateException(final String message, final Throwable e) {\n        super(message, e);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/impl/PlatformRetrieverImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.impl;\n\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.platform.PlatformRetriever;\nimport org.bonitasoft.engine.platform.exception.SPlatformNotFoundException;\nimport org.bonitasoft.engine.platform.model.SPlatform;\nimport org.bonitasoft.engine.services.PersistenceService;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class PlatformRetrieverImpl implements PlatformRetriever {\n\n    private final PersistenceService platformPersistenceService;\n\n    private static final String QUERY_GET_PLATFORM = \"getPlatform\";\n\n    public PlatformRetrieverImpl(final PersistenceService platformPersistenceService) {\n        this.platformPersistenceService = platformPersistenceService;\n    }\n\n    @Override\n    public SPlatform getPlatform() throws SPlatformNotFoundException {\n        try {\n            SPlatform platform = platformPersistenceService\n                    .selectOne(new SelectOneDescriptor<>(QUERY_GET_PLATFORM, null, SPlatform.class));\n            if (platform == null) {\n                throw new SPlatformNotFoundException(\"No platform found\");\n            }\n            return platform;\n        } catch (final SBonitaReadException e) {\n            throw new SPlatformNotFoundException(\"Unable to check if a platform already exists : \" + e.getMessage(), e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/impl/PlatformServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.impl;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.platform.PlatformRetriever;\nimport org.bonitasoft.engine.platform.PlatformService;\nimport org.bonitasoft.engine.platform.exception.*;\nimport org.bonitasoft.engine.platform.model.SPlatform;\nimport org.bonitasoft.engine.platform.model.SPlatformProperties;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.bonitasoft.engine.services.PersistenceService;\nimport org.bonitasoft.engine.services.SPersistenceException;\nimport org.bonitasoft.engine.services.UpdateDescriptor;\n\n/**\n * @author Charles Souillard\n * @author Celine Souchet\n */\n@Slf4j\npublic class PlatformServiceImpl implements PlatformService {\n\n    private final PersistenceService platformPersistenceService;\n    private final SPlatformProperties sPlatformProperties;\n    private final Recorder recorder;\n    private final PlatformRetriever platformRetriever;\n\n    public PlatformServiceImpl(final PersistenceService platformPersistenceService, PlatformRetriever platformRetriever,\n            final Recorder recorder, final SPlatformProperties sPlatformProperties) {\n        this.platformPersistenceService = platformPersistenceService;\n        this.sPlatformProperties = sPlatformProperties;\n        this.recorder = recorder;\n        this.platformRetriever = platformRetriever;\n    }\n\n    @Override\n    public SPlatform getPlatform() throws SPlatformNotFoundException {\n        try {\n            return platformRetriever.getPlatform();\n        } catch (SPlatformNotFoundException e) {\n            throw e;\n        } catch (final Exception e) {\n            throw new SPlatformNotFoundException(\"Unable to check if a platform already exists : \" + e.getMessage(), e);\n        }\n    }\n\n    // FIXME: Not necessary anymore, as platform is always created by ScriptExecutor at startup\n    @Override\n    public boolean isPlatformCreated() {\n        try {\n            getPlatform();\n            return true;\n        } catch (final SPlatformNotFoundException e) {\n            return false;\n        }\n    }\n\n    @Override\n    public void resumeServices() throws SPlatformNotFoundException, SPlatformUpdateException {\n        final SPlatform platform = getPlatform();\n        final UpdateDescriptor desc = new UpdateDescriptor(platform);\n        desc.addField(SPlatform.MAINTENANCE_ENABLED, false);\n        try {\n            platformPersistenceService.update(desc);\n        } catch (final SPersistenceException e) {\n            throw new SPlatformUpdateException(\"Problem while activating services\", e);\n        }\n    }\n\n    @Override\n    public void pauseServices() throws SPlatformNotFoundException, SPlatformUpdateException {\n        final SPlatform platform = getPlatform();\n        final UpdateDescriptor desc = new UpdateDescriptor(platform);\n        desc.addField(SPlatform.MAINTENANCE_ENABLED, true);\n        try {\n            platformPersistenceService.update(desc);\n        } catch (final SPersistenceException e) {\n            throw new SPlatformUpdateException(\"Unable to update platform status in database.\", e);\n        }\n    }\n\n    @Override\n    public SPlatformProperties getSPlatformProperties() {\n        return sPlatformProperties;\n    }\n\n    @Override\n    public void updatePlatform(final EntityUpdateDescriptor descriptor) throws SPlatformUpdateException {\n        try {\n            recorder.recordUpdate(UpdateRecord.buildSetFields(getPlatform(), descriptor), PLATFORM);\n        } catch (final SRecorderException | SPlatformNotFoundException e) {\n            throw new SPlatformUpdateException(\"Problem while updating platform: \", e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/model/SPlatform.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.model;\n\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.hibernate.annotations.Type;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@Builder\n@Entity\n@Table(name = \"platform\")\npublic class SPlatform implements PersistentObject {\n\n    public static final String CREATED_BY = \"createdBy\";\n    public static final String CREATED = \"created\";\n    public static final String ID = \"id\";\n    public static final String INITIAL_VERSION = \"initialVersion\";\n    public static final String PREVIOUS_VERSION = \"previousVersion\";\n    public static final String VERSION = \"version\";\n    public static final String INFORMATION = \"information\";\n    public static final String APPLICATION_VERSION = \"applicationVersion\";\n    public static final String MAINTENANCE_MESSAGE = \"maintenanceMessage\";\n    public static final String MAINTENANCE_MESSAGE_ACTIVE = \"maintenanceMessageActive\";\n    public static final String MAINTENANCE_ENABLED = \"maintenanceEnabled\";\n\n    public static final String PAUSED = \"PAUSED\";\n    private static final String RESUMED = \"RESUMED\";\n\n    @Id\n    private long id;\n    private long created;\n    @Column(name = \"created_by\")\n    private String createdBy;\n    @Column(name = \"initial_bonita_version\")\n    private String initialBonitaVersion;\n    @Column(name = \"version\")\n    private String dbSchemaVersion;\n    @Type(type = \"materialized_clob\")\n    private String information;\n    @Column(name = \"application_version\")\n    private String applicationVersion;\n    @Column(name = \"maintenance_message\")\n    private String maintenanceMessage;\n    @Column(name = \"maintenance_message_active\")\n    private boolean maintenanceMessageActive;\n    @Column(name = \"maintenance_enabled\")\n    private boolean maintenanceEnabled;\n\n    public SPlatform(final String dbSchemaVersion, final String initialBonitaVersion, final String applicationVersion,\n            final String maintenanceMessage, final boolean maintenanceMessageActive,\n            final String createdBy, final long created, final boolean maintenanceEnabled) {\n        this.dbSchemaVersion = dbSchemaVersion;\n        this.initialBonitaVersion = initialBonitaVersion;\n        this.applicationVersion = applicationVersion;\n        this.maintenanceMessage = maintenanceMessage;\n        this.maintenanceMessageActive = maintenanceMessageActive;\n        this.createdBy = createdBy;\n        this.created = created;\n        this.maintenanceEnabled = maintenanceEnabled;\n    }\n\n    public String getPausedStatus() {\n        return this.maintenanceEnabled ? PAUSED : RESUMED;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/model/SPlatformProperties.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.model;\n\n/**\n * @author Celine Souchet\n */\npublic interface SPlatformProperties {\n\n    String getPlatformVersion();\n\n}\n"
  },
  {
    "path": "services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/model/builder/SPlatformUpdateBuilder.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.model.builder;\n\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Haroun EL ALAMI\n */\npublic interface SPlatformUpdateBuilder {\n\n    String APPLICATION_VERSION = \"applicationVersion\";\n\n    String MAINTENANCE_MESSAGE = \"maintenanceMessage\";\n\n    String MAINTENANCE_MESSAGE_ACTIVE = \"maintenanceMessageActive\";\n\n    SPlatformUpdateBuilder setApplicationVersion(String version);\n\n    SPlatformUpdateBuilder setMaintenanceMessageActive(boolean state);\n\n    SPlatformUpdateBuilder setMaintenanceMessage(String name);\n\n    EntityUpdateDescriptor done();\n\n}\n"
  },
  {
    "path": "services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/model/builder/impl/SPlatformUpdateBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.model.builder.impl;\n\nimport lombok.Builder;\nimport org.bonitasoft.engine.platform.model.builder.SPlatformUpdateBuilder;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Haroun EL ALAMI\n */\n@Builder\npublic class SPlatformUpdateBuilderImpl implements SPlatformUpdateBuilder {\n\n    protected final EntityUpdateDescriptor descriptor;\n\n    public SPlatformUpdateBuilderImpl(final EntityUpdateDescriptor descriptor) {\n        this.descriptor = descriptor;\n    }\n\n    @Override\n    public SPlatformUpdateBuilder setApplicationVersion(String version) {\n        descriptor.addField(APPLICATION_VERSION, version);\n        return this;\n    }\n\n    @Override\n    public SPlatformUpdateBuilder setMaintenanceMessage(final String name) {\n        descriptor.addField(MAINTENANCE_MESSAGE, name);\n        return this;\n    }\n\n    @Override\n    public SPlatformUpdateBuilder setMaintenanceMessageActive(final boolean state) {\n        descriptor.addField(MAINTENANCE_MESSAGE_ACTIVE, state);\n        return this;\n    }\n\n    @Override\n    public EntityUpdateDescriptor done() {\n        return descriptor;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/model/impl/SPlatformPropertiesImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.model.impl;\n\nimport java.io.IOException;\nimport java.net.URL;\nimport java.util.Properties;\n\nimport org.bonitasoft.engine.commons.io.PropertiesManager;\nimport org.bonitasoft.engine.platform.model.SPlatformProperties;\n\n/**\n * @author Celine Souchet\n */\npublic class SPlatformPropertiesImpl implements SPlatformProperties {\n\n    private final String platformVersion;\n\n    public SPlatformPropertiesImpl() throws IOException {\n        final URL resource = SPlatformPropertiesImpl.class.getResource(\"platform.properties\");\n        final Properties properties = PropertiesManager.getProperties(resource);\n        platformVersion = (String) properties.get(\"version\");\n    }\n\n    public SPlatformPropertiesImpl(String platformVersion) {\n        this.platformVersion = platformVersion;\n    }\n\n    @Override\n    public String getPlatformVersion() {\n        return platformVersion;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform/src/main/resources/org/bonitasoft/engine/platform/model/impl/hibernate/platform.queries.hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\"\n                                   \"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd\">\n<hibernate-mapping auto-import=\"false\">\n\n  <query name=\"getPlatform\">\n    SELECT platform\n    FROM org.bonitasoft.engine.platform.model.SPlatform AS platform\n  </query>\n\n</hibernate-mapping>\n"
  },
  {
    "path": "services/bonita-platform/src/main/resources/org/bonitasoft/engine/platform/model/impl/platform.properties",
    "content": "version ${project.version}"
  },
  {
    "path": "services/bonita-platform/src/test/java/org/bonitasoft/engine/platform/PlatformServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform;\n\nimport static org.hamcrest.core.IsEqual.equalTo;\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.ArgumentMatchers.argThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.platform.exception.SPlatformNotFoundException;\nimport org.bonitasoft.engine.platform.impl.PlatformServiceImpl;\nimport org.bonitasoft.engine.platform.model.SPlatform;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.services.PersistenceService;\nimport org.bonitasoft.engine.services.UpdateDescriptor;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class PlatformServiceImplTest {\n\n    @Mock\n    private PersistenceService persistenceService;\n\n    @Mock\n    private PlatformRetriever platformRetriever;\n\n    @Mock\n    private Recorder recorder;\n\n    @InjectMocks\n    private PlatformServiceImpl platformServiceImpl;\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public final void getPlatform_should_throw_SPlatformNotFoundException_when_platformRetriever_throws_SPlatformNotFoundException()\n            throws SBonitaException {\n        //given\n        SPlatformNotFoundException exception = new SPlatformNotFoundException(\"Not found\");\n        given(platformRetriever.getPlatform()).willThrow(exception);\n\n        //then\n        expectedException.expect(SPlatformNotFoundException.class);\n        expectedException.expectMessage(equalTo(\"Not found\"));\n\n        //when\n        platformServiceImpl.getPlatform();\n    }\n\n    @Test\n    public final void isPlatformCreated() throws SBonitaException {\n        final SPlatform sPlatform = buildPlatform();\n        when(platformServiceImpl.getPlatform()).thenReturn(sPlatform);\n\n        assertTrue(platformServiceImpl.isPlatformCreated());\n    }\n\n    @Test\n    public void pause_should_update_platform_state_to_PAUSED() throws SBonitaException {\n        // when\n        platformServiceImpl.pauseServices();\n\n        // then\n        verify(persistenceService).update(argThat(this::updateOnlyStatus));\n    }\n\n    private boolean updateOnlyStatus(UpdateDescriptor u) {\n        return u.getFields().size() == 1\n                && u.getFields().containsKey(SPlatform.MAINTENANCE_ENABLED)\n                && u.getFields().containsValue(true);\n    }\n\n    private SPlatform buildPlatform() {\n        return new SPlatform(\"1.0\", \"0.5\", \"0.0.0\", null, false, \"me\", 654687344687645L, false);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform/src/test/java/org/bonitasoft/engine/platform/impl/PlatformRetrieverImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.hamcrest.core.IsEqual.equalTo;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.platform.exception.SPlatformNotFoundException;\nimport org.bonitasoft.engine.platform.model.SPlatform;\nimport org.bonitasoft.engine.services.PersistenceService;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class PlatformRetrieverImplTest {\n\n    @Mock\n    private PersistenceService persistenceService;\n\n    @InjectMocks\n    private PlatformRetrieverImpl platformRetriever;\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public final void getPlatform_should_return_result_of_persistence_service() throws SBonitaException {\n        SPlatform platform = mock(SPlatform.class);\n        given(persistenceService.selectOne(new SelectOneDescriptor<SPlatform>(\"getPlatform\", null, SPlatform.class)))\n                .willReturn(platform);\n\n        //when\n        SPlatform retrievedPlatform = platformRetriever.getPlatform();\n        assertThat(retrievedPlatform).isEqualTo(platform);\n    }\n\n    @Test\n    public final void getPlatform_should_throw_SPlatformNotFoundException_when_persistence_service_returns_null()\n            throws SBonitaException {\n        given(persistenceService.selectOne(new SelectOneDescriptor<SPlatform>(\"getPlatform\", null, SPlatform.class)))\n                .willReturn(null);\n\n        //then\n        expectedException.expect(SPlatformNotFoundException.class);\n        expectedException.expectMessage(equalTo(\"No platform found\"));\n\n        //when\n        platformRetriever.getPlatform();\n    }\n\n    @Test\n    public final void getPlatform_should_throw_SPlatformNotFoundException_when_persistence_service_throws_Exception()\n            throws SBonitaException {\n        SBonitaReadException exception = new SBonitaReadException(\"Unable to access database\");\n        given(persistenceService.selectOne(new SelectOneDescriptor<SPlatform>(\"getPlatform\", null, SPlatform.class)))\n                .willThrow(exception);\n\n        //then\n        expectedException.expect(SPlatformNotFoundException.class);\n        expectedException.expectMessage(\"Unable to check if a platform already exists : \");\n        expectedException.expectCause(equalTo(exception));\n\n        //when\n        platformRetriever.getPlatform();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform-authentication/build.gradle",
    "content": "\n\ndependencies {\n    api project(':services:bonita-commons')\n    testImplementation libs.mockitoCore\n    testImplementation libs.logback\n}\n"
  },
  {
    "path": "services/bonita-platform-authentication/src/main/java/org/bonitasoft/engine/platform/authentication/PlatformAuthenticationService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.authentication;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic interface PlatformAuthenticationService {\n\n    /**\n     * @param username\n     *        The username of the platform\n     * @param passwordHash\n     *        The hashed password of the platform\n     * @throws SInvalidUserException\n     * @throws SInvalidPasswordException\n     */\n    void checkUserCredentials(final String username, final String passwordHash)\n            throws SInvalidUserException, SInvalidPasswordException;\n\n}\n"
  },
  {
    "path": "services/bonita-platform-authentication/src/main/java/org/bonitasoft/engine/platform/authentication/SInvalidPasswordException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.authentication;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SInvalidPasswordException extends SPlatformAuthenticationException {\n\n    private static final long serialVersionUID = 2784260274211036580L;\n\n    public SInvalidPasswordException(final String message) {\n        super(message);\n    }\n\n    public SInvalidPasswordException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SInvalidPasswordException(final Object... arguments) {\n        super(arguments);\n    }\n\n    public SInvalidPasswordException(final Throwable cause, final Object... arguments) {\n        super(cause, arguments);\n    }\n\n    public SInvalidPasswordException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform-authentication/src/main/java/org/bonitasoft/engine/platform/authentication/SInvalidUserException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.authentication;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SInvalidUserException extends SPlatformAuthenticationException {\n\n    private static final long serialVersionUID = 2784260274211036580L;\n\n    public SInvalidUserException(final String message) {\n        super(message);\n    }\n\n    public SInvalidUserException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SInvalidUserException(final Object... arguments) {\n        super(arguments);\n    }\n\n    public SInvalidUserException(final Throwable cause, final Object... arguments) {\n        super(cause, arguments);\n    }\n\n    public SInvalidUserException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform-authentication/src/main/java/org/bonitasoft/engine/platform/authentication/SPlatformAuthenticationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.authentication;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic abstract class SPlatformAuthenticationException extends SBonitaException {\n\n    private static final long serialVersionUID = 7204454677855421061L;\n\n    public SPlatformAuthenticationException(final String message) {\n        super(message);\n    }\n\n    public SPlatformAuthenticationException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SPlatformAuthenticationException(final Object... arguments) {\n        super(arguments);\n    }\n\n    public SPlatformAuthenticationException(final Throwable cause, final Object... arguments) {\n        super(cause, arguments);\n    }\n\n    public SPlatformAuthenticationException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform-authentication/src/main/java/org/bonitasoft/engine/platform/authentication/impl/PlatformAuthenticationServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.authentication.impl;\n\nimport org.bonitasoft.engine.commons.LogUtil;\nimport org.bonitasoft.engine.platform.authentication.PlatformAuthenticationService;\nimport org.bonitasoft.engine.platform.authentication.SInvalidPasswordException;\nimport org.bonitasoft.engine.platform.authentication.SInvalidUserException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class PlatformAuthenticationServiceImpl implements PlatformAuthenticationService {\n\n    private Logger logger = LoggerFactory.getLogger(PlatformAuthenticationServiceImpl.class);\n    private static final String USERNAME = \"platformAdmin\";\n\n    private static final String PASSWORD = \"platform\";\n\n    public PlatformAuthenticationServiceImpl() {\n        super();\n    }\n\n    @Override\n    public void checkUserCredentials(final String username, final String password)\n            throws SInvalidUserException, SInvalidPasswordException {\n        final String methodName = \"checkUserCredentials\";\n        if (logger.isTraceEnabled()) {\n            logger.trace(\n                    LogUtil.getLogBeforeMethod(this.getClass(), methodName));\n        }\n        // FIXME read user and password from a configuration file\n        if (!USERNAME.equals(username)) {\n            logOnExceptionMethod(username, methodName);\n            throw new SInvalidUserException(\"Invalid user : \" + username);\n        }\n        if (!PASSWORD.equals(password)) {\n            logOnExceptionMethod(username, methodName);\n            throw new SInvalidPasswordException(\"Invalid password\");\n        }\n        if (logger.isTraceEnabled()) {\n            logger.trace(\n                    LogUtil.getLogAfterMethod(this.getClass(), methodName));\n        }\n    }\n\n    private void logOnExceptionMethod(final String username, final String methodName) {\n        if (logger.isTraceEnabled()) {\n            logger.trace(\n                    LogUtil.getLogOnExceptionMethod(this.getClass(), methodName, \"Invalid user : \" + username));\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform-authentication/src/test/java/org/bonitasoft/engine/platform/authentication/impl/PlatformAuthenticationServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.authentication.impl;\n\nimport org.bonitasoft.engine.platform.authentication.SInvalidPasswordException;\nimport org.bonitasoft.engine.platform.authentication.SInvalidUserException;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.InjectMocks;\nimport org.mockito.MockitoAnnotations;\n\n/**\n * @author Celine Souchet\n */\npublic class PlatformAuthenticationServiceImplTest {\n\n    @InjectMocks\n    private PlatformAuthenticationServiceImpl platformAuthenticationServiceImpl;\n\n    @Before\n    public void setUp() {\n        MockitoAnnotations.initMocks(this);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.platform.authentication.impl.PlatformAuthenticationServiceImpl#checkUserCredentials(java.lang.String, java.lang.String)}.\n     */\n    @Test\n    public final void checkUserCredentials() throws SInvalidUserException, SInvalidPasswordException {\n        platformAuthenticationServiceImpl.checkUserCredentials(\"platformAdmin\", \"platform\");\n    }\n\n    @Test(expected = SInvalidUserException.class)\n    public final void checkUserCredentialsWithBadUserName() throws SInvalidUserException, SInvalidPasswordException {\n        platformAuthenticationServiceImpl.checkUserCredentials(\"plop\", \"platform\");\n    }\n\n    @Test(expected = SInvalidPasswordException.class)\n    public final void checkUserCredentialsWithBadPassword() throws SInvalidUserException, SInvalidPasswordException {\n        platformAuthenticationServiceImpl.checkUserCredentials(\"platformAdmin\", \"plop\");\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform-command/build.gradle",
    "content": "\n\ndependencies {\n    api project(':services:bonita-log')\n    api project(':services:bonita-commons')\n    api project(':services:bonita-persistence')\n    testImplementation libs.mockitoCore\n    testImplementation libs.logback\n\n    annotationProcessor libs.lombok\n    compileOnly libs.lombok\n}\n"
  },
  {
    "path": "services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/PlatformCommandService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.command;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.platform.command.model.SPlatformCommand;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Zhang Bole\n * @author Emmanuel Duchastenier\n */\npublic interface PlatformCommandService {\n\n    /**\n     * Create a sPlatformCommand\n     *\n     * @param command\n     * @throws SPlatformCommandAlreadyExistsException\n     *         occurs when the sPlatformCommand has already been taken\n     * @throws SPlatformCommandCreationException\n     *         occurs when an exception is thrown during sPlatformCommand creation\n     * @throws SPlatformCommandGettingException\n     *         occurs when an exception is thrown during getting sPlatformCommand\n     * @since 6.0\n     */\n    void create(SPlatformCommand command) throws SPlatformCommandAlreadyExistsException,\n            SPlatformCommandCreationException, SPlatformCommandGettingException;\n\n    /**\n     * Delete a sPlatformCommand from its name\n     *\n     * @param name\n     *        the platform name\n     * @throws SPlatformCommandNotFoundException\n     *         occurs when the identifier does not refer to an existing sPlatformCommand\n     * @throws SPlatformCommandDeletionException\n     *         occurs when an exception is thrown during sPlatformCommand creation\n     * @throws SPlatformCommandGettingException\n     *         occurs when an exception is thrown during getting sPlatformCommand\n     * @since 6.0\n     */\n    void delete(String name) throws SPlatformCommandNotFoundException, SPlatformCommandDeletionException,\n            SPlatformCommandGettingException;\n\n    /**\n     * Delete all sPlatformCommands\n     *\n     * @throws SPlatformCommandDeletionException\n     *         occurs when an exception is thrown during sPlatformCommand creation\n     * @since 6.0\n     */\n    void deleteAll() throws SPlatformCommandDeletionException;\n\n    /**\n     * Get sPlatformCommand by its name\n     *\n     * @param name\n     * @return an entity of sPlatformCommand\n     * @throws SPlatformCommandNotFoundException\n     *         occurs when the identifier does not refer to an existing sPlatformCommand\n     * @throws SPlatformCommandGettingException\n     *         occurs when an exception is thrown during getting sPlatformCommand\n     * @since 6.0\n     */\n    SPlatformCommand getPlatformCommand(String name)\n            throws SPlatformCommandNotFoundException, SPlatformCommandGettingException;\n\n    /**\n     * Get the sPlatformCommand having the given value for the given int index\n     *\n     * @param queryOptions\n     *        criteria\n     * @return a list of sPlatformCommand\n     * @throws SPlatformCommandGettingException\n     *         occurs when an exception is thrown during getting sPlatformCommand\n     * @since 6.0\n     */\n    List<SPlatformCommand> getPlatformCommands(QueryOptions queryOptions) throws SPlatformCommandGettingException;\n\n    /**\n     * Update a sPlatformCommand with given sPlatformCommand and new content.\n     *\n     * @param command\n     * @param updateDescriptor\n     * @throws SPlatformCommandNotFoundException\n     *         occurs when the identifier does not refer to an existing sPlatformCommand\n     * @throws SPlatformCommandUpdateException\n     *         occurs when an exception is thrown during sPlatformCommand update\n     * @since 6.0\n     */\n    void update(SPlatformCommand command, EntityUpdateDescriptor updateDescriptor)\n            throws SPlatformCommandNotFoundException, SPlatformCommandUpdateException;\n\n}\n"
  },
  {
    "path": "services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/SPlatformCommandAlreadyExistsException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.command;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Zhang Bole\n */\npublic class SPlatformCommandAlreadyExistsException extends SBonitaException {\n\n    private static final long serialVersionUID = -3017980604615886777L;\n\n    public SPlatformCommandAlreadyExistsException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SPlatformCommandAlreadyExistsException(final String message) {\n        super(message);\n    }\n\n    public SPlatformCommandAlreadyExistsException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/SPlatformCommandCreationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.command;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Zhang Bole\n */\npublic class SPlatformCommandCreationException extends SBonitaException {\n\n    private static final long serialVersionUID = 634050853254691398L;\n\n    public SPlatformCommandCreationException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SPlatformCommandCreationException(final String message) {\n        super(message);\n    }\n\n    public SPlatformCommandCreationException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/SPlatformCommandDeletionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.command;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Zhang Bole\n */\npublic class SPlatformCommandDeletionException extends SBonitaException {\n\n    private static final long serialVersionUID = -1160779784480102515L;\n\n    public SPlatformCommandDeletionException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SPlatformCommandDeletionException(final String message) {\n        super(message);\n    }\n\n    public SPlatformCommandDeletionException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/SPlatformCommandGettingException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.command;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Zhang Bole\n */\npublic class SPlatformCommandGettingException extends SBonitaException {\n\n    private static final long serialVersionUID = -4051373026112211800L;\n\n    public SPlatformCommandGettingException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SPlatformCommandGettingException(final String message) {\n        super(message);\n    }\n\n    public SPlatformCommandGettingException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/SPlatformCommandNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.command;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Zhang Bole\n */\npublic class SPlatformCommandNotFoundException extends SBonitaException {\n\n    private static final long serialVersionUID = -3017980604615886777L;\n\n    public SPlatformCommandNotFoundException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SPlatformCommandNotFoundException(final String message) {\n        super(message);\n    }\n\n    public SPlatformCommandNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/SPlatformCommandUpdateException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.command;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Zhang Bole\n */\npublic class SPlatformCommandUpdateException extends SBonitaException {\n\n    private static final long serialVersionUID = 8667781514957065049L;\n\n    public SPlatformCommandUpdateException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SPlatformCommandUpdateException(final String message) {\n        super(message);\n    }\n\n    public SPlatformCommandUpdateException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/impl/PlatformCommandServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.command.impl;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.commons.NullCheckingUtil;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.platform.command.PlatformCommandService;\nimport org.bonitasoft.engine.platform.command.SPlatformCommandAlreadyExistsException;\nimport org.bonitasoft.engine.platform.command.SPlatformCommandCreationException;\nimport org.bonitasoft.engine.platform.command.SPlatformCommandDeletionException;\nimport org.bonitasoft.engine.platform.command.SPlatformCommandGettingException;\nimport org.bonitasoft.engine.platform.command.SPlatformCommandNotFoundException;\nimport org.bonitasoft.engine.platform.command.SPlatformCommandUpdateException;\nimport org.bonitasoft.engine.platform.command.model.SPlatformCommand;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.services.PersistenceService;\nimport org.bonitasoft.engine.services.SPersistenceException;\nimport org.bonitasoft.engine.services.UpdateDescriptor;\n\n/**\n * @author Zhang Bole\n * @author Matthieu Chaffotte\n */\n@Slf4j\npublic class PlatformCommandServiceImpl implements PlatformCommandService {\n\n    private final PersistenceService platformPersistenceService;\n\n    public PlatformCommandServiceImpl(final PersistenceService platformPersistenceService) {\n        super();\n        this.platformPersistenceService = platformPersistenceService;\n    }\n\n    @Override\n    public void create(final SPlatformCommand platformCommand)\n            throws SPlatformCommandAlreadyExistsException, SPlatformCommandCreationException,\n            SPlatformCommandGettingException {\n        NullCheckingUtil.checkArgsNotNull(platformCommand);\n        SPlatformCommand existedPlatformCommand = null;\n        try {\n            existedPlatformCommand = getPlatformCommand(platformCommand.getName());\n        } catch (final SPlatformCommandNotFoundException ignored) {\n        } finally {\n            if (existedPlatformCommand != null) {\n                throw new SPlatformCommandAlreadyExistsException(\"platformCommand already existed\");\n            }\n        }\n        try {\n            platformPersistenceService.insert(platformCommand);\n        } catch (final SPersistenceException pe) {\n            throw new SPlatformCommandCreationException(pe);\n        }\n    }\n\n    public void deletePlatformCommand(final SPlatformCommand sPlatformCommand)\n            throws SPlatformCommandDeletionException {\n        try {\n            platformPersistenceService.delete(sPlatformCommand);\n        } catch (final SPersistenceException pe) {\n            throw new SPlatformCommandDeletionException(pe);\n        }\n    }\n\n    @Override\n    public void delete(final String platformCommandName)\n            throws SPlatformCommandNotFoundException, SPlatformCommandDeletionException,\n            SPlatformCommandGettingException {\n        final SPlatformCommand sPlatformCommand = getPlatformCommand(platformCommandName);\n        deletePlatformCommand(sPlatformCommand);\n    }\n\n    @Override\n    public void deleteAll() throws SPlatformCommandDeletionException {\n        final QueryOptions queryOptions = new QueryOptions(0, 100, SPlatformCommand.class, \"id\", OrderByType.ASC);\n        List<SPlatformCommand> sPlatformCommands = null;\n        do {\n            try {\n                sPlatformCommands = getPlatformCommands(queryOptions);\n            } catch (final SPlatformCommandGettingException e) {\n                throw new SPlatformCommandDeletionException(e);\n            }\n            for (final SPlatformCommand sPlatformCommand : sPlatformCommands) {\n                deletePlatformCommand(sPlatformCommand);\n            }\n        } while (sPlatformCommands.size() == queryOptions.getNumberOfResults());\n    }\n\n    @Override\n    public List<SPlatformCommand> getPlatformCommands(final QueryOptions queryOptions)\n            throws SPlatformCommandGettingException {\n        final Map<String, Object> parameters = Collections.emptyMap();\n        try {\n            return platformPersistenceService\n                    .selectList(new SelectListDescriptor<SPlatformCommand>(\n                            \"getPlatformCommands\", parameters, SPlatformCommand.class, queryOptions));\n        } catch (final SBonitaReadException bre) {\n            throw new SPlatformCommandGettingException(bre);\n        }\n    }\n\n    @Override\n    public SPlatformCommand getPlatformCommand(final String platformCommandName)\n            throws SPlatformCommandNotFoundException, SPlatformCommandGettingException {\n        final Map<String, Object> parameters = Collections.singletonMap(\"name\", platformCommandName);\n        try {\n            final SPlatformCommand sPlatformCommand = platformPersistenceService\n                    .selectOne(new SelectOneDescriptor<SPlatformCommand>(\n                            \"getPlatformCommandByName\", parameters, SPlatformCommand.class));\n            if (sPlatformCommand == null) {\n                throw new SPlatformCommandNotFoundException(\n                        \"No platformCommand exists using name: \" + platformCommandName);\n            }\n            return sPlatformCommand;\n        } catch (final SBonitaReadException bre) {\n            throw new SPlatformCommandGettingException(bre);\n        }\n    }\n\n    @Override\n    public void update(final SPlatformCommand platformCommand, final EntityUpdateDescriptor updateDescriptor)\n            throws SPlatformCommandUpdateException {\n        final UpdateDescriptor desc = new UpdateDescriptor(platformCommand);\n        desc.addFields(updateDescriptor.getFields());\n        try {\n            platformPersistenceService.update(desc);\n        } catch (final SPersistenceException pe) {\n            throw new SPlatformCommandUpdateException(pe);\n        }\n    }\n}\n"
  },
  {
    "path": "services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/model/SPlatformCommand.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.command.model;\n\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Zhang Bole\n */\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\n@Entity\n@Table(name = \"platformCommand\")\npublic class SPlatformCommand implements PersistentObject {\n\n    public static final String ID = \"id\";\n    public static final String NAME = \"name\";\n    public static final String DESCRIPTION = \"description\";\n    public static final String IMPLEMENTATION = \"implementation\";\n    @Id\n    private long id;\n    private String name;\n    private String description;\n    private String implementation;\n\n    public SPlatformCommand(final String name, final String description, final String implementation) {\n        super();\n        this.name = name;\n        this.description = description;\n        this.implementation = implementation;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/model/SPlatformCommandCriterion.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.command.model;\n\n/**\n * @author Zhang Bole\n */\npublic enum SPlatformCommandCriterion {\n\n    NAME_ASC, NAME_DESC;\n\n}\n"
  },
  {
    "path": "services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/model/SPlatformCommandLogBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.command.model;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\n\n/**\n * @author Zhang Bole\n */\npublic interface SPlatformCommandLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction {\n\n}\n"
  },
  {
    "path": "services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/model/SPlatformCommandLogBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.command.model;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory;\n\n/**\n * @author Zhang Bole\n */\npublic interface SPlatformCommandLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory {\n\n    SPlatformCommandLogBuilder createNewInstance();\n\n}\n"
  },
  {
    "path": "services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/model/SPlatformCommandUpdateBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.command.model;\n\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Zhang Bole\n */\npublic interface SPlatformCommandUpdateBuilder {\n\n    SPlatformCommandUpdateBuilder updateName(String name);\n\n    SPlatformCommandUpdateBuilder updateDescription(String description);\n\n    EntityUpdateDescriptor done();\n\n}\n"
  },
  {
    "path": "services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/model/SPlatformCommandUpdateBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.command.model;\n\n/**\n * @author Zhang Bole\n */\npublic interface SPlatformCommandUpdateBuilderFactory {\n\n    SPlatformCommandUpdateBuilder createNewInstance();\n\n}\n"
  },
  {
    "path": "services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/model/impl/SPlatformCommandLogBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.command.model.impl;\n\nimport org.bonitasoft.engine.platform.command.model.SPlatformCommandLogBuilder;\nimport org.bonitasoft.engine.platform.command.model.SPlatformCommandLogBuilderFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory;\n\n/**\n * @author Zhang Bole\n */\npublic class SPlatformCommandLogBuilderFactoryImpl extends CRUDELogBuilderFactory\n        implements SPlatformCommandLogBuilderFactory {\n\n    @Override\n    public SPlatformCommandLogBuilder createNewInstance() {\n        return new SPlatformCommandLogBuilderImpl();\n    }\n\n    @Override\n    public String getObjectIdKey() {\n        return SPlatformCommandLogIndexesMapper.COMMAND_INDEX_NAME;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/model/impl/SPlatformCommandLogBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.command.model.impl;\n\nimport org.bonitasoft.engine.platform.command.model.SPlatformCommandLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException;\n\n/**\n * @author Zhang Bole\n */\npublic class SPlatformCommandLogBuilderImpl extends CRUDELogBuilder implements SPlatformCommandLogBuilder {\n\n    @Override\n    public SPersistenceLogBuilder objectId(final long objectId) {\n        this.queriableLogBuilder.numericIndex(SPlatformCommandLogIndexesMapper.COMMAND_INDEX, objectId);\n        return this;\n    }\n\n    @Override\n    protected String getActionTypePrefix() {\n        return \"COMMAND\";\n    }\n\n    @Override\n    protected void checkExtraRules(final SQueriableLog log) {\n        if (log.getActionStatus() != SQueriableLog.STATUS_FAIL) {\n            if (log.getNumericIndex(SPlatformCommandLogIndexesMapper.COMMAND_INDEX) == 0L) {\n                throw new MissingMandatoryFieldsException(\"Some mandatory fields are missing: \" + \"Category Id\");\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/model/impl/SPlatformCommandLogIndexesMapper.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.command.model.impl;\n\n/**\n * @author Zhang Bole\n * @author Matthieu Chaffotte\n */\npublic class SPlatformCommandLogIndexesMapper {\n\n    public static final int COMMAND_INDEX = 0;\n\n    public static final String COMMAND_INDEX_NAME = \"numericIndex1\";\n\n}\n"
  },
  {
    "path": "services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/model/impl/SPlatformCommandUpdateBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.command.model.impl;\n\nimport org.bonitasoft.engine.platform.command.model.SPlatformCommandUpdateBuilder;\nimport org.bonitasoft.engine.platform.command.model.SPlatformCommandUpdateBuilderFactory;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Zhang Bole\n */\npublic class SPlatformCommandUpdateBuilderFactoryImpl implements SPlatformCommandUpdateBuilderFactory {\n\n    public SPlatformCommandUpdateBuilder createNewInstance() {\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        return new SPlatformCommandUpdateBuilderImpl(descriptor);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/model/impl/SPlatformCommandUpdateBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.command.model.impl;\n\nimport org.bonitasoft.engine.platform.command.model.SPlatformCommand;\nimport org.bonitasoft.engine.platform.command.model.SPlatformCommandUpdateBuilder;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Zhang Bole\n */\npublic class SPlatformCommandUpdateBuilderImpl implements SPlatformCommandUpdateBuilder {\n\n    private final EntityUpdateDescriptor descriptor;\n\n    public SPlatformCommandUpdateBuilderImpl(final EntityUpdateDescriptor descriptor) {\n        super();\n        this.descriptor = descriptor;\n    }\n\n    @Override\n    public SPlatformCommandUpdateBuilder updateName(final String name) {\n        this.descriptor.addField(SPlatformCommand.NAME, name);\n        return this;\n    }\n\n    @Override\n    public SPlatformCommandUpdateBuilder updateDescription(final String description) {\n        this.descriptor.addField(SPlatformCommand.DESCRIPTION, description);\n        return this;\n    }\n\n    @Override\n    public EntityUpdateDescriptor done() {\n        return this.descriptor;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/recorder/SelectDescriptorBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.command.recorder;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.platform.command.model.SPlatformCommand;\n\n/**\n * @author Bole Zhang\n */\npublic class SelectDescriptorBuilder {\n\n    public static SelectOneDescriptor<SPlatformCommand> getPlatformCommandByName(final String platformCommandName) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"name\", (Object) platformCommandName);\n        return new SelectOneDescriptor<SPlatformCommand>(\"getPlatformCommandByName\", parameters,\n                SPlatformCommand.class);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform-command/src/main/resources/org/bonitasoft/engine/platform/command/model/impl/hibernate/platformCommand.queries.hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\" \"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd\">\n<hibernate-mapping auto-import=\"false\">\n\n\n\t<query name=\"getPlatformCommandByName\">\n\t\tSELECT platformCommand\n\t\tFROM org.bonitasoft.engine.platform.command.model.SPlatformCommand AS platformCommand\n\t\tWHERE platformCommand.name = :name\n\t</query>\n\n\t<query name=\"getPlatformCommands\">\n\t\tSELECT platformCommand\n\t\tFROM org.bonitasoft.engine.platform.command.model.SPlatformCommand AS platformCommand\n\t</query>\n\n</hibernate-mapping>\n"
  },
  {
    "path": "services/bonita-platform-command/src/test/java/org/bonitasoft/engine/platform/command/impl/PlatformCommandServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.command.impl;\n\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.platform.command.SPlatformCommandGettingException;\nimport org.bonitasoft.engine.platform.command.SPlatformCommandNotFoundException;\nimport org.bonitasoft.engine.platform.command.model.SPlatformCommand;\nimport org.bonitasoft.engine.services.PersistenceService;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.ArgumentMatchers;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\n/**\n * @author Celine Souchet\n */\npublic class PlatformCommandServiceImplTest {\n\n    @Mock\n    private PersistenceService persistenceService;\n\n    @InjectMocks\n    private PlatformCommandServiceImpl platformCommandServiceImpl;\n\n    @Before\n    public void setUp() {\n        MockitoAnnotations.initMocks(this);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.platform.command.impl.PlatformCommandServiceImpl#getPlatformCommand(java.lang.String)}.\n     */\n    @Test\n    public final void getPlatformCommandByName()\n            throws SBonitaReadException, SPlatformCommandNotFoundException, SPlatformCommandGettingException {\n        final SPlatformCommand sPlatformCommand = mock(SPlatformCommand.class);\n        when(persistenceService.selectOne(ArgumentMatchers.<SelectOneDescriptor<SPlatformCommand>> any()))\n                .thenReturn(sPlatformCommand);\n\n        Assert.assertEquals(sPlatformCommand, platformCommandServiceImpl.getPlatformCommand(\"name\"));\n    }\n\n    @Test(expected = SPlatformCommandNotFoundException.class)\n    public final void getPlatformCommandByNameNotExists()\n            throws SBonitaReadException, SPlatformCommandNotFoundException, SPlatformCommandGettingException {\n        when(persistenceService.selectOne(ArgumentMatchers.<SelectOneDescriptor<SPlatformCommand>> any()))\n                .thenReturn(null);\n\n        platformCommandServiceImpl.getPlatformCommand(\"name\");\n    }\n\n    @Test(expected = SPlatformCommandGettingException.class)\n    public final void getPlatformCommandByNameThrowException()\n            throws SBonitaReadException, SPlatformCommandNotFoundException, SPlatformCommandGettingException {\n        when(persistenceService.selectOne(ArgumentMatchers.<SelectOneDescriptor<SPlatformCommand>> any()))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        platformCommandServiceImpl.getPlatformCommand(\"name\");\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.platform.command.impl.PlatformCommandServiceImpl#getPlatformCommands(org.bonitasoft.engine.persistence.QueryOptions)}.\n     */\n    @Test\n    public final void getPlatformCommandsWithOptions() throws SBonitaReadException, SPlatformCommandGettingException {\n        final List<SPlatformCommand> sPlatformCommands = new ArrayList<SPlatformCommand>();\n        sPlatformCommands.add(mock(SPlatformCommand.class));\n        final QueryOptions options = new QueryOptions(0, 10);\n        when(persistenceService.selectList(ArgumentMatchers.<SelectListDescriptor<SPlatformCommand>> any()))\n                .thenReturn(sPlatformCommands);\n\n        Assert.assertEquals(sPlatformCommands, platformCommandServiceImpl.getPlatformCommands(options));\n    }\n\n    @Test(expected = SPlatformCommandGettingException.class)\n    public final void getPlatformCommandsWithOptionsThrowException()\n            throws SBonitaReadException, SPlatformCommandGettingException {\n        final QueryOptions options = new QueryOptions(0, 10);\n        when(persistenceService.selectList(ArgumentMatchers.<SelectListDescriptor<SPlatformCommand>> any()))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        platformCommandServiceImpl.getPlatformCommands(options);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform-session/build.gradle",
    "content": "\n\ndependencies {\n    api project(':services:bonita-commons')\n    api project(':services:bonita-builder')\n}\n"
  },
  {
    "path": "services/bonita-platform-session/src/main/java/org/bonitasoft/engine/platform/session/PlatformSessionProvider.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.session;\n\nimport org.bonitasoft.engine.platform.session.model.SPlatformSession;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface PlatformSessionProvider {\n\n    void addSession(SPlatformSession session) throws SSessionAlreadyExistsException;\n\n    void removeSession(long sessionId) throws SSessionNotFoundException;\n\n    SPlatformSession getSession(long sessionId) throws SSessionNotFoundException;\n\n    void updateSession(SPlatformSession session) throws SSessionNotFoundException;\n\n}\n"
  },
  {
    "path": "services/bonita-platform-session/src/main/java/org/bonitasoft/engine/platform/session/PlatformSessionService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.session;\n\nimport org.bonitasoft.engine.platform.session.model.SPlatformSession;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic interface PlatformSessionService {\n\n    /**\n     * Create a new session for the given user;\n     *\n     * @param username\n     *        user name\n     * @return a new session\n     * @throws SSessionException\n     *         if some error arrives while creating the session\n     * @@since 6.0\n     */\n    SPlatformSession createSession(String username) throws SSessionException;\n\n    /**\n     * Delete a session having the given id\n     *\n     * @param sessionId\n     *        the session's id\n     * @throws SSessionNotFoundException\n     *         if no session exists for the given id\n     * @@since 6.0\n     */\n    void deleteSession(long sessionId) throws SSessionNotFoundException;\n\n    /**\n     * Verify if a session is valid\n     *\n     * @param sessionId\n     *        the session's id\n     * @return true if the session is valid, false otherwise\n     * @since 6.0\n     */\n    boolean isValid(long sessionId) throws SSessionNotFoundException;\n\n    /**\n     * Retrieve a session by its id\n     *\n     * @param sessionId\n     *        the session's id\n     * @return the session associated to the given id\n     * @throws SSessionNotFoundException\n     *         if no session exists for the given id\n     * @since 6.0\n     */\n    SPlatformSession getSession(long sessionId) throws SSessionNotFoundException;\n\n    /**\n     * Define how long new created sessions will be valid. This does not affect already created session\n     *\n     * @param duration\n     *        session's duration\n     * @since 6.0\n     */\n    void setSessionDuration(long duration);\n\n    /**\n     * Retrieve the default sessions's duration\n     *\n     * @return the default sessions's duration\n     * @since 6.0\n     */\n    long getDefaultSessionDuration();\n\n    /**\n     * Retrieve the duration of new created sessions. If no duration was specified, the default duration will be used\n     *\n     * @return the duration of new created sessions\n     * @since 6.0\n     */\n    long getSessionsDuration();\n\n    /**\n     * Update the expiration and the last update dates of the session.\n     *\n     * @param sessionId\n     *        the session's id\n     * @throws SSessionException\n     * @since 6.0\n     */\n    void renewSession(long sessionId) throws SSessionException;\n\n}\n"
  },
  {
    "path": "services/bonita-platform-session/src/main/java/org/bonitasoft/engine/platform/session/SSessionAlreadyExistsException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.session;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SSessionAlreadyExistsException extends SSessionException {\n\n    private static final long serialVersionUID = -3954242079366628148L;\n\n    public SSessionAlreadyExistsException(final String message) {\n        super(message);\n    }\n\n    public SSessionAlreadyExistsException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SSessionAlreadyExistsException(final Object... arguments) {\n        super(arguments);\n    }\n\n    public SSessionAlreadyExistsException(final Throwable cause, final Object... arguments) {\n        super(cause, arguments);\n    }\n\n    public SSessionAlreadyExistsException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform-session/src/main/java/org/bonitasoft/engine/platform/session/SSessionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.session;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SSessionException extends SBonitaException {\n\n    private static final long serialVersionUID = 7690456826693549647L;\n\n    public SSessionException(final String message) {\n        super(message);\n    }\n\n    public SSessionException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SSessionException(final Object... arguments) {\n        super(arguments);\n    }\n\n    public SSessionException(final Throwable cause, final Object... arguments) {\n        super(cause, arguments);\n    }\n\n    public SSessionException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform-session/src/main/java/org/bonitasoft/engine/platform/session/SSessionNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.session;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SSessionNotFoundException extends SSessionException {\n\n    private static final long serialVersionUID = -5995789561034211161L;\n\n    public SSessionNotFoundException(final String message) {\n        super(message);\n    }\n\n    public SSessionNotFoundException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SSessionNotFoundException(final Object... arguments) {\n        super(arguments);\n    }\n\n    public SSessionNotFoundException(final Throwable cause, final Object... arguments) {\n        super(cause, arguments);\n    }\n\n    public SSessionNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform-session/src/main/java/org/bonitasoft/engine/platform/session/impl/PlatformSessionIdGenerator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.session.impl;\n\nimport static java.lang.Math.abs;\n\nimport java.security.SecureRandom;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic class PlatformSessionIdGenerator {\n\n    private static volatile SecureRandom numberGenerator = null;\n\n    public static long getNextId() {\n        SecureRandom ng = numberGenerator;\n        if (ng == null) {\n            numberGenerator = ng = new SecureRandom();\n        }\n        return abs(ng.nextLong());\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform-session/src/main/java/org/bonitasoft/engine/platform/session/impl/PlatformSessionProviderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.session.impl;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.platform.session.PlatformSessionProvider;\nimport org.bonitasoft.engine.platform.session.SSessionAlreadyExistsException;\nimport org.bonitasoft.engine.platform.session.SSessionNotFoundException;\nimport org.bonitasoft.engine.platform.session.model.SPlatformSession;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\n@Component\n@ConditionalOnSingleCandidate(PlatformSessionProvider.class)\npublic final class PlatformSessionProviderImpl implements PlatformSessionProvider {\n\n    private static final Map<Long, SPlatformSession> platformSessions;\n\n    static {\n        platformSessions = new HashMap<>();\n    }\n\n    public PlatformSessionProviderImpl() {\n    }\n\n    @Override\n    public synchronized void addSession(final SPlatformSession session) throws SSessionAlreadyExistsException {\n        final long id = session.getId();\n        if (platformSessions.containsKey(id)) {\n            throw new SSessionAlreadyExistsException(\"A session wih id \\\"\" + id + \"\\\" already exists\");\n        }\n        platformSessions.put(id, session);\n    }\n\n    @Override\n    public void removeSession(final long sessionId) throws SSessionNotFoundException {\n        if (!platformSessions.containsKey(sessionId)) {\n            throw new SSessionNotFoundException(\"No session found with id \\\"\" + sessionId + \"\\\"\");\n        }\n        platformSessions.remove(sessionId);\n    }\n\n    @Override\n    public SPlatformSession getSession(final long sessionId) throws SSessionNotFoundException {\n        if (!platformSessions.containsKey(sessionId)) {\n            throw new SSessionNotFoundException(\"No session found with id \\\"\" + sessionId + \"\\\"\");\n        }\n        return platformSessions.get(sessionId);\n    }\n\n    @Override\n    public void updateSession(final SPlatformSession session) throws SSessionNotFoundException {\n        final long id = session.getId();\n        if (!platformSessions.containsKey(id)) {\n            throw new SSessionNotFoundException(\"No session found with id \\\"\" + id + \"\\\"\");\n        }\n        platformSessions.put(id, session);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform-session/src/main/java/org/bonitasoft/engine/platform/session/impl/PlatformSessionServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.session.impl;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.commons.ClassReflector;\nimport org.bonitasoft.engine.commons.exceptions.SReflectException;\nimport org.bonitasoft.engine.platform.session.PlatformSessionProvider;\nimport org.bonitasoft.engine.platform.session.PlatformSessionService;\nimport org.bonitasoft.engine.platform.session.SSessionException;\nimport org.bonitasoft.engine.platform.session.SSessionNotFoundException;\nimport org.bonitasoft.engine.platform.session.model.SPlatformSession;\nimport org.bonitasoft.engine.platform.session.model.builder.SPlatformSessionBuilderFactory;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\n\n@Component\npublic class PlatformSessionServiceImpl implements PlatformSessionService {\n\n    private Logger logger = LoggerFactory.getLogger(PlatformSessionServiceImpl.class);\n    private static final long DEFAULT_SESSION_DURATION = 3600000;\n\n    private final PlatformSessionProvider platformSessionProvider;\n\n    private long sessionDuration = DEFAULT_SESSION_DURATION;\n\n    public PlatformSessionServiceImpl(\n            PlatformSessionProvider platformSessionProvider) {\n        this.platformSessionProvider = platformSessionProvider;\n    }\n\n    @Override\n    public SPlatformSession createSession(final String username) throws SSessionException {\n        final long sessionId = PlatformSessionIdGenerator.getNextId();\n        final long duration = getSessionsDuration();\n        final SPlatformSession session = BuilderFactory.get(SPlatformSessionBuilderFactory.class)\n                .createNewInstance(sessionId, duration, username).done();\n        platformSessionProvider.addSession(session);\n        return session;\n    }\n\n    @Override\n    public void deleteSession(final long sessionId) throws SSessionNotFoundException {\n        platformSessionProvider.removeSession(sessionId);\n    }\n\n    @Override\n    public boolean isValid(final long sessionId) throws SSessionNotFoundException {\n        final SPlatformSession session = platformSessionProvider.getSession(sessionId);\n        final Date now = new Date();\n        return session.getExpirationDate().after(now);\n    }\n\n    @Override\n    public SPlatformSession getSession(final long sessionId) throws SSessionNotFoundException {\n        final SPlatformSession session = platformSessionProvider.getSession(sessionId);\n        return BuilderFactory.get(SPlatformSessionBuilderFactory.class).copy(session);\n    }\n\n    @Override\n    public void setSessionDuration(final long duration) {\n        if (duration <= 0) {\n            throw new IllegalArgumentException(\"The duration must be greater then 0\");\n        }\n        sessionDuration = duration;\n    }\n\n    @Override\n    public long getDefaultSessionDuration() {\n        return DEFAULT_SESSION_DURATION;\n    }\n\n    @Override\n    public long getSessionsDuration() {\n        return sessionDuration;\n    }\n\n    @Override\n    public void renewSession(final long sessionId) throws SSessionException {\n        final SPlatformSession session = getSession(sessionId);\n        try {\n            ClassReflector.invokeSetter(session, \"setLastRenewDate\", Date.class, new Date());\n            platformSessionProvider.updateSession(session);\n        } catch (final SReflectException e) {\n            throw new SSessionException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform-session/src/main/java/org/bonitasoft/engine/platform/session/model/SPlatformSession.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.session.model;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SPlatformSession extends Serializable {\n\n    /**\n     * Gets the session's id\n     *\n     * @return\n     */\n    long getId();\n\n    /**\n     * Gets the session's creation date (GMT+0)\n     *\n     * @return\n     */\n    Date getCreationDate();\n\n    /**\n     * Gets the session's last renew date (GMT+0)\n     *\n     * @return\n     */\n    Date getLastRenewDate();\n\n    /**\n     * Gets the session's duration\n     *\n     * @return\n     */\n    long getDuration();\n\n    /**\n     * Gets the session's expiration date (GMT+0)\n     *\n     * @return\n     */\n    Date getExpirationDate();\n\n    /**\n     * Gets the user name associated to this session\n     *\n     * @return\n     */\n    String getUserName();\n\n    /**\n     * Gets the user id associated to this session\n     *\n     * @return the user Id for this session\n     */\n    long getUserId();\n\n}\n"
  },
  {
    "path": "services/bonita-platform-session/src/main/java/org/bonitasoft/engine/platform/session/model/builder/SPlatformSessionBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.session.model.builder;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.platform.session.model.SPlatformSession;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SPlatformSessionBuilder {\n\n    SPlatformSessionBuilder lastRenewDate(final Date lastRenewDate);\n\n    SPlatformSession done();\n\n}\n"
  },
  {
    "path": "services/bonita-platform-session/src/main/java/org/bonitasoft/engine/platform/session/model/builder/SPlatformSessionBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.session.model.builder;\n\nimport org.bonitasoft.engine.platform.session.model.SPlatformSession;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface SPlatformSessionBuilderFactory {\n\n    SPlatformSession copy(SPlatformSession session);\n\n    SPlatformSessionBuilder createNewInstance(final long id, final long duration, final String username);\n\n}\n"
  },
  {
    "path": "services/bonita-platform-session/src/main/java/org/bonitasoft/engine/platform/session/model/builder/impl/SPlatformSessionBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.session.model.builder.impl;\n\nimport org.bonitasoft.engine.platform.session.model.SPlatformSession;\nimport org.bonitasoft.engine.platform.session.model.builder.SPlatformSessionBuilder;\nimport org.bonitasoft.engine.platform.session.model.builder.SPlatformSessionBuilderFactory;\nimport org.bonitasoft.engine.platform.session.model.impl.SPlatformSessionImpl;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SPlatformSessionBuilderFactoryImpl implements SPlatformSessionBuilderFactory {\n\n    @Override\n    public SPlatformSessionBuilder createNewInstance(final long id, final long duration, final String username) {\n        final SPlatformSessionImpl entity = new SPlatformSessionImpl(id, username);\n        entity.setDuration(duration);\n        return new SPlatformSessionBuilderImpl(entity);\n    }\n\n    @Override\n    public SPlatformSession copy(final SPlatformSession session) {\n        return new SPlatformSessionImpl(session);\n    }\n}\n"
  },
  {
    "path": "services/bonita-platform-session/src/main/java/org/bonitasoft/engine/platform/session/model/builder/impl/SPlatformSessionBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.session.model.builder.impl;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.platform.session.model.SPlatformSession;\nimport org.bonitasoft.engine.platform.session.model.builder.SPlatformSessionBuilder;\nimport org.bonitasoft.engine.platform.session.model.impl.SPlatformSessionImpl;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SPlatformSessionBuilderImpl implements SPlatformSessionBuilder {\n\n    private final SPlatformSessionImpl entity;\n\n    public SPlatformSessionBuilderImpl(final SPlatformSessionImpl entity) {\n        super();\n        this.entity = entity;\n    }\n\n    @Override\n    public SPlatformSessionBuilder lastRenewDate(final Date lastRenewDate) {\n        this.entity.setLastRenewDate(lastRenewDate);\n        return this;\n    }\n\n    @Override\n    public SPlatformSession done() {\n        final Date now = new Date();\n        this.entity.setCreationDate(now);\n        this.entity.setLastRenewDate(now);\n        return this.entity;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-platform-session/src/main/java/org/bonitasoft/engine/platform/session/model/impl/SPlatformSessionImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.platform.session.model.impl;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.platform.session.model.SPlatformSession;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic class SPlatformSessionImpl implements SPlatformSession {\n\n    private static final long serialVersionUID = 1L;\n\n    private final long id;\n\n    private Date creationDate;\n\n    private long duration;\n\n    private Date lastRenewDate;\n\n    private final String userName;\n\n    private long userId;\n\n    public SPlatformSessionImpl(final long id, final String username) {\n        this.id = id;\n        this.userName = username;\n    }\n\n    public SPlatformSessionImpl(final SPlatformSession session) {\n        id = session.getId();\n        creationDate = session.getCreationDate();\n        duration = session.getDuration();\n        lastRenewDate = session.getLastRenewDate();\n        userName = session.getUserName();\n        userId = session.getUserId();\n    }\n\n    @Override\n    public long getId() {\n        return id;\n    }\n\n    @Override\n    public Date getCreationDate() {\n        return creationDate;\n    }\n\n    @Override\n    public Date getLastRenewDate() {\n        return lastRenewDate;\n    }\n\n    @Override\n    public long getDuration() {\n        return duration;\n    }\n\n    @Override\n    public Date getExpirationDate() {\n        return new Date(this.lastRenewDate.getTime() + duration);\n    }\n\n    @Override\n    public String getUserName() {\n        return userName;\n    }\n\n    @Override\n    public long getUserId() {\n        return userId;\n    }\n\n    public void setLastRenewDate(final Date lastRenewDate) {\n        this.lastRenewDate = lastRenewDate;\n    }\n\n    public void setDuration(final long duration) {\n        this.duration = duration;\n    }\n\n    public void setCreationDate(final Date creationDate) {\n        this.creationDate = creationDate;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + (creationDate == null ? 0 : creationDate.hashCode());\n        result = prime * result + (int) (duration ^ duration >>> 32);\n        result = prime * result + (int) (id ^ id >>> 32);\n        result = prime * result + (lastRenewDate == null ? 0 : lastRenewDate.hashCode());\n        result = prime * result + (int) (userId ^ userId >>> 32);\n        result = prime * result + (userName == null ? 0 : userName.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        final SPlatformSessionImpl other = (SPlatformSessionImpl) obj;\n        if (creationDate == null) {\n            if (other.creationDate != null) {\n                return false;\n            }\n        } else if (!creationDate.equals(other.creationDate)) {\n            return false;\n        }\n        if (duration != other.duration) {\n            return false;\n        }\n        if (id != other.id) {\n            return false;\n        }\n        if (lastRenewDate == null) {\n            if (other.lastRenewDate != null) {\n                return false;\n            }\n        } else if (!lastRenewDate.equals(other.lastRenewDate)) {\n            return false;\n        }\n        if (userId != other.userId) {\n            return false;\n        }\n        if (userName == null) {\n            if (other.userName != null) {\n                return false;\n            }\n        } else if (!userName.equals(other.userName)) {\n            return false;\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-profile/build.gradle",
    "content": "\n\ndependencies {\n    api project(':services:bonita-commons')\n    api project(':services:bonita-persistence')\n    api project(':services:bonita-log')\n    api project(':services:bonita-session')\n    api project(':services:bonita-events')\n    api project(':services:bonita-builder')\n    api project(':services:bonita-identity')\n    testImplementation libs.mockitoCore\n\n    annotationProcessor libs.lombok\n    compileOnly libs.lombok\n}\n"
  },
  {
    "path": "services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/ProfileService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.profile.exception.profile.SProfileCreationException;\nimport org.bonitasoft.engine.profile.exception.profile.SProfileDeletionException;\nimport org.bonitasoft.engine.profile.exception.profile.SProfileNotFoundException;\nimport org.bonitasoft.engine.profile.exception.profile.SProfileUpdateException;\nimport org.bonitasoft.engine.profile.exception.profilemember.SProfileMemberCreationException;\nimport org.bonitasoft.engine.profile.exception.profilemember.SProfileMemberDeletionException;\nimport org.bonitasoft.engine.profile.exception.profilemember.SProfileMemberNotFoundException;\nimport org.bonitasoft.engine.profile.model.SProfile;\nimport org.bonitasoft.engine.profile.model.SProfileMember;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\npublic interface ProfileService {\n\n    String PROFILE = \"PROFILE\";\n\n    String PROFILE_MEMBER = \"PROFILE_MEMBER\";\n\n    /**\n     * Get profile by its id\n     *\n     * @param profileId\n     * @return sProfile\n     * @throws SProfileNotFoundException\n     *         occurs when the identifier does not refer to an existing sProfile\n     * @since 6.0\n     */\n    SProfile getProfile(long profileId) throws SProfileNotFoundException;\n\n    /**\n     * Get all profiles by given ids\n     *\n     * @param profileIds\n     *        a list contains many profile id\n     * @return\n     * @throws SProfileNotFoundException\n     *         occurs when the identifier does not refer to an existing sProfile\n     * @since 6.0\n     */\n    List<SProfile> getProfiles(List<Long> profileIds) throws SProfileNotFoundException;\n\n    /**\n     * Add a new profile\n     *\n     * @param profile\n     * @return sProfile\n     * @throws SProfileCreationException\n     *         occurs when an exception is thrown during sProfile creation\n     * @since 6.0\n     */\n    SProfile createProfile(SProfile profile) throws SProfileCreationException;\n\n    /**\n     * Update profile by given profile and new content\n     *\n     * @param profile\n     * @param descriptor\n     * @return The updated profile\n     * @throws SProfileUpdateException\n     *         occurs when an exception is thrown during sProfile update\n     * @since 6.0\n     */\n    SProfile updateProfile(SProfile profile, EntityUpdateDescriptor descriptor) throws SProfileUpdateException;\n\n    /**\n     * Delete profile by given sProfile\n     *\n     * @param profile\n     * @throws SProfileNotFoundException\n     *         occurs when the identifier does not refer to an existing sProfile\n     * @throws SProfileDeletionException\n     *         occurs when an exception is thrown during sProfile deletion\n     * @since 6.0\n     */\n    void deleteProfile(SProfile profile)\n            throws SProfileNotFoundException, SProfileDeletionException,\n            SProfileMemberDeletionException;\n\n    /**\n     * Delete profile by its id\n     *\n     * @param profileId\n     * @throws SProfileNotFoundException\n     *         occurs when the identifier does not refer to an existing sProfile\n     * @throws SProfileDeletionException\n     *         occurs when an exception is thrown during sProfile deletion\n     * @since 6.0\n     */\n    void deleteProfile(long profileId)\n            throws SProfileNotFoundException, SProfileDeletionException,\n            SProfileMemberDeletionException;\n\n    /**\n     * Get all profiles of the user by userId\n     *\n     * @param userId\n     * @param fromIndex\n     *        first result to be considered(>=0)\n     * @param numberOfElements\n     *        the max number of elementss to be returned (>=0)\n     * @param field\n     * @param order\n     * @return A list of sProfile\n     * @throws SBonitaReadException\n     * @since 6.0\n     */\n    List<SProfile> searchProfilesOfUser(long userId, int fromIndex, int numberOfElements, String field,\n            OrderByType order) throws SBonitaReadException;\n\n    List<SProfile> getProfilesOfUser(long userId) throws SBonitaReadException;\n\n    /**\n     * Add a user to exist profile\n     *\n     * @param profileId\n     *        the identifier of profile\n     * @param userId\n     *        the identifier of user\n     * @param firstName\n     * @param lastName\n     * @param userName\n     * @return sProfileMember\n     * @throws SProfileMemberCreationException\n     * @since 6.0\n     */\n    SProfileMember addUserToProfile(long profileId, long userId, String firstName, String lastName, String userName)\n            throws SProfileMemberCreationException;\n\n    /**\n     * Add a group to exist profile\n     *\n     * @param profileId\n     *        the identifier of profile\n     * @param groupId\n     *        the identifier of group\n     * @param groupName\n     * @param parentPath\n     * @return sProfileMember\n     * @throws SProfileMemberCreationException\n     *         TODO\n     * @since 6.0\n     */\n    SProfileMember addGroupToProfile(long profileId, long groupId, String groupName, String parentPath)\n            throws SProfileMemberCreationException;\n\n    /**\n     * Add a role to exist profile\n     *\n     * @param profileId\n     *        the identifier of profile\n     * @param roleId\n     *        the identifier of role\n     * @param roleName\n     * @return sProfileMember\n     * @throws SProfileMemberCreationException\n     *         TODO\n     * @since 6.0\n     */\n    SProfileMember addRoleToProfile(long profileId, long roleId, String roleName)\n            throws SProfileMemberCreationException;\n\n    /**\n     * Add a role and a group to exist profile\n     *\n     * @param profileId\n     *        the identifier of profile\n     * @param roleId\n     *        the identifier of role\n     * @param groupId\n     *        the identifier of group\n     * @param roleName\n     * @param groupName\n     * @param groupParentPath\n     * @return sProfileMember\n     * @throws SProfileMemberCreationException\n     *         TODO\n     * @since 6.0\n     */\n    SProfileMember addRoleAndGroupToProfile(long profileId, long roleId, long groupId, String roleName,\n            String groupName, String groupParentPath)\n            throws SProfileMemberCreationException;\n\n    /**\n     * Get all sProfileMembers with same user having the given value for the given int index\n     *\n     * @param userId\n     *        the identifier of user\n     * @param fromIndex\n     *        first result to be considered(>=0)\n     * @param numberOfElements\n     *        the max number of emelents to be returned (>=0)\n     * @param field\n     * @param order\n     * @return sProfileMember\n     * @throws SBonitaReadException\n     *         occurs when an exception is thrown during sProfileMember creation\n     * @since 6.0\n     */\n    List<SProfileMember> getProfileMembersOfUser(final long userId, int fromIndex, int numberOfElements, String field,\n            OrderByType order)\n            throws SBonitaReadException;\n\n    /**\n     * Get all sProfileMembers with same group having the given value for the given int index\n     *\n     * @param groupId\n     *        the identifier of group\n     * @param fromIndex\n     *        first result to be considered(>=0)\n     * @param numberOfElements\n     *        the max number of elements to be returned (>=0)\n     * @param field\n     * @param order\n     * @return sProfileMember\n     * @throws SBonitaReadException\n     *         occurs when an exception is thrown during sProfileMember creation\n     * @since 6.0\n     */\n    List<SProfileMember> getProfileMembersOfGroup(final long groupId, int fromIndex, int numberOfElements, String field,\n            OrderByType order)\n            throws SBonitaReadException;\n\n    /**\n     * Get all sProfileMembers with same group having the given value for the given int index\n     *\n     * @param roleId\n     *        the identifier of role\n     * @param fromIndex\n     *        first result to be considered(>=0)\n     * @param numberOfElements\n     *        the max number of elements to be returned (>=0)\n     * @param field\n     * @param order\n     * @return sProfileMember\n     * @throws SBonitaReadException\n     *         occurs when an exception is thrown during sProfileMember creation\n     * @since 6.0\n     */\n    List<SProfileMember> getProfileMembersOfRole(final long roleId, int fromIndex, int numberOfElements, String field,\n            OrderByType order)\n            throws SBonitaReadException;\n\n    /**\n     * Delete a profile member by its id\n     *\n     * @param profileMemberId\n     * @throws SProfileMemberDeletionException\n     *         occurs when an exception is thrown during sProfileMember deletion\n     * @throws SProfileMemberNotFoundException\n     *         occurs when the identifier does not refer to an existing sProfileMember\n     * @since 6.0\n     */\n    void deleteProfileMember(long profileMemberId)\n            throws SProfileMemberDeletionException, SProfileMemberNotFoundException;\n\n    /**\n     * Delete a profile member by given sProfile member\n     *\n     * @param profileMember\n     * @throws SProfileMemberDeletionException\n     *         occurs when an exception is thrown during sProfileMember deletion\n     * @since 6.0\n     */\n    void deleteProfileMember(SProfileMember profileMember) throws SProfileMemberDeletionException;\n\n    /**\n     * Get the total number of profile members\n     *\n     * @param querySuffix\n     *        the suffix of query string\n     * @param countOptions\n     *        The criterion used to search profileMembers\n     * @return the total number of profile members\n     * @throws SBonitaReadException\n     * @since 6.0\n     */\n    long getNumberOfProfileMembers(String querySuffix, final QueryOptions countOptions) throws SBonitaReadException;\n\n    /**\n     * Get all sProfileMember by profileId and queryOptions\n     *\n     * @param querySuffix\n     *        the suffix of query string\n     * @return all sProfileMember by profileId and queryOptions\n     * @throws SBonitaReadException\n     * @since 6.0\n     */\n    List<SProfileMember> searchProfileMembers(String querySuffix, final QueryOptions searchOptions)\n            throws SBonitaReadException;\n\n    /**\n     * Get the total number of sProfileMember by a list contains profileIds\n     *\n     * @param profileIds\n     * @return A list of <profileId, NumberOfMemberForThatProfile>\n     * @throws SBonitaReadException\n     * @since 6.0\n     */\n    List<SProfileMember> getProfileMembers(List<Long> profileIds) throws SBonitaReadException;\n\n    /**\n     * Get profile by given name\n     *\n     * @param profileName\n     * @return sProfile\n     * @throws SProfileNotFoundException\n     *         occurs when the identifier does not refer to an existing sProfile\n     * @since 6.0\n     */\n    SProfile getProfileByName(String profileName) throws SProfileNotFoundException, SBonitaReadException;\n\n    /**\n     * Get a list of profileMembers by the given profileId\n     *\n     * @param profileId\n     * @return a list of profileMembers\n     * @throws SProfileMemberNotFoundException\n     */\n    List<SProfileMember> getProfileMembers(long profileId, QueryOptions queryOptions)\n            throws SProfileMemberNotFoundException;\n\n    /**\n     * Delete all profile members for the connected tenant\n     *\n     * @throws SProfileMemberDeletionException\n     * @since 6.1\n     */\n    void deleteAllProfileMembers() throws SProfileMemberDeletionException;\n\n    /**\n     * @param queryOptions\n     * @return\n     */\n    long getNumberOfProfiles(QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * @param queryOptions\n     * @return\n     */\n    List<SProfile> searchProfiles(QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * @param profile\n     * @throws SProfileMemberDeletionException\n     * @since 6.3.1\n     */\n    void deleteAllProfileMembersOfProfile(SProfile profile) throws SProfileMemberDeletionException;\n\n    /**\n     * @param profileMemberId\n     * @return\n     * @throws SProfileMemberNotFoundException\n     * @since 6.3.1\n     */\n    SProfileMember getProfileMemberWithoutDisplayName(final long profileMemberId)\n            throws SProfileMemberNotFoundException;\n\n    /**\n     * updates profile metaData fields lastUpdateDate and lastUpdatedBy for a given profile\n     *\n     * @param profileId\n     * @throws SProfileUpdateException\n     *         when given profileId is not found\n     */\n    void updateProfileMetaData(final long profileId) throws SProfileUpdateException;\n\n}\n"
  },
  {
    "path": "services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/builder/SProfileMemberUpdateBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile.builder;\n\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Celine Souchet\n */\npublic interface SProfileMemberUpdateBuilder {\n\n    SProfileMemberUpdateBuilder setGroupId(final long groupId);\n\n    SProfileMemberUpdateBuilder setRoleId(final long roleId);\n\n    SProfileMemberUpdateBuilder setUserId(long userId);\n\n    SProfileMemberUpdateBuilder setDisplayNamePart1(String displayNamePart1);\n\n    SProfileMemberUpdateBuilder setDisplayNamePart2(String displayNamePart2);\n\n    SProfileMemberUpdateBuilder setDisplayNamePart3(String displayNamePart3);\n\n    EntityUpdateDescriptor done();\n\n}\n"
  },
  {
    "path": "services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/builder/SProfileMemberUpdateBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile.builder;\n\n/**\n * @author Celine Souchet\n */\npublic interface SProfileMemberUpdateBuilderFactory {\n\n}\n"
  },
  {
    "path": "services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/builder/SProfileUpdateBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile.builder;\n\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Celine Souchet\n */\npublic interface SProfileUpdateBuilder {\n\n    EntityUpdateDescriptor done();\n\n    SProfileUpdateBuilder setName(String name);\n\n    SProfileUpdateBuilder setDescription(String description);\n\n    SProfileUpdateBuilder setIconPath(String iconPath);\n\n    SProfileUpdateBuilder setLastUpdateDate(long lastUpdateDate);\n\n    SProfileUpdateBuilder setLastUpdatedBy(long lastUpdatedBy);\n\n}\n"
  },
  {
    "path": "services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/builder/SProfileUpdateBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile.builder;\n\n/**\n * @author Celine Souchet\n */\npublic interface SProfileUpdateBuilderFactory {\n\n    SProfileUpdateBuilder createNewInstance();\n\n}\n"
  },
  {
    "path": "services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/builder/impl/SProfileLogBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile.builder.impl;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SProfileLogBuilderFactoryImpl extends CRUDELogBuilderFactory implements SPersistenceLogBuilderFactory {\n\n    public static final int PROFILE_INDEX = 1;\n\n    public static final String PROFILE_INDEX_NAME = \"numericIndex2\";\n\n    @Override\n    public String getObjectIdKey() {\n        return PROFILE_INDEX_NAME;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/builder/impl/SProfileLogBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile.builder.impl;\n\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SProfileLogBuilderImpl extends CRUDELogBuilder implements SPersistenceLogBuilder {\n\n    private static final String PREFIX = \"PROFILE\";\n\n    @Override\n    public SPersistenceLogBuilder objectId(final long objectId) {\n        queriableLogBuilder.numericIndex(SProfileLogBuilderFactoryImpl.PROFILE_INDEX, objectId);\n        return this;\n    }\n\n    @Override\n    protected String getActionTypePrefix() {\n        return PREFIX;\n    }\n\n    @Override\n    protected void checkExtraRules(final SQueriableLog log) {\n        if (log.getActionStatus() != SQueriableLog.STATUS_FAIL\n                && log.getNumericIndex(SProfileLogBuilderFactoryImpl.PROFILE_INDEX) == 0L) {\n            throw new MissingMandatoryFieldsException(\"Some mandatory fields are missing: \" + \"Profile id\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/builder/impl/SProfileMemberLogBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile.builder.impl;\n\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic class SProfileMemberLogBuilderFactoryImpl extends CRUDELogBuilderFactory\n        implements SPersistenceLogBuilderFactory {\n\n    public static final String PROFILE_MEMBER_INDEX_NAME = \"numericIndex3\";\n\n    // TODO: add index for user, group and role\n\n    @Override\n    public String getObjectIdKey() {\n        return PROFILE_MEMBER_INDEX_NAME;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/builder/impl/SProfileMemberLogBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile.builder.impl;\n\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\npublic class SProfileMemberLogBuilderImpl extends CRUDELogBuilder implements SPersistenceLogBuilder {\n\n    private static final String PREFIX = \"PROFILE_MEMBER\";\n\n    private static final int PROFILE_MEMBER_INDEX = 2;\n\n    // TODO: add index for user, group and role\n\n    @Override\n    public SPersistenceLogBuilder objectId(final long objectId) {\n        queriableLogBuilder.numericIndex(PROFILE_MEMBER_INDEX, objectId);\n        return this;\n    }\n\n    @Override\n    protected String getActionTypePrefix() {\n        return PREFIX;\n    }\n\n    @Override\n    protected void checkExtraRules(final SQueriableLog log) {\n        if (log.getActionStatus() != SQueriableLog.STATUS_FAIL && log.getNumericIndex(PROFILE_MEMBER_INDEX) == 0L) {\n            throw new MissingMandatoryFieldsException(\"Some mandatory fields are missing: \" + \"Profile member id\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/builder/impl/SProfileMemberUpdateBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile.builder.impl;\n\nimport org.bonitasoft.engine.profile.builder.SProfileMemberUpdateBuilder;\nimport org.bonitasoft.engine.profile.model.SProfileMember;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Celine Souchet\n */\npublic class SProfileMemberUpdateBuilderFactoryImpl implements SProfileMemberUpdateBuilder {\n\n    protected final EntityUpdateDescriptor descriptor;\n\n    public SProfileMemberUpdateBuilderFactoryImpl() {\n        descriptor = new EntityUpdateDescriptor();\n    }\n\n    @Override\n    public EntityUpdateDescriptor done() {\n        return descriptor;\n    }\n\n    @Override\n    public SProfileMemberUpdateBuilder setGroupId(final long groupId) {\n        descriptor.addField(SProfileMember.GROUP_ID, groupId);\n        return this;\n    }\n\n    @Override\n    public SProfileMemberUpdateBuilder setRoleId(final long roleId) {\n        descriptor.addField(SProfileMember.ROLE_ID, roleId);\n        return this;\n    }\n\n    @Override\n    public SProfileMemberUpdateBuilder setUserId(final long userId) {\n        descriptor.addField(SProfileMember.USER_ID, userId);\n        return this;\n    }\n\n    @Override\n    public SProfileMemberUpdateBuilder setDisplayNamePart1(final String displayNamePart1) {\n        descriptor.addField(SProfileMember.DISPLAY_NAME_PART1, displayNamePart1);\n        return this;\n    }\n\n    @Override\n    public SProfileMemberUpdateBuilder setDisplayNamePart2(final String displayNamePart2) {\n        descriptor.addField(SProfileMember.DISPLAY_NAME_PART2, displayNamePart2);\n        return this;\n    }\n\n    @Override\n    public SProfileMemberUpdateBuilder setDisplayNamePart3(final String displayNamePart3) {\n        descriptor.addField(SProfileMember.DISPLAY_NAME_PART3, displayNamePart3);\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/builder/impl/SProfileMemberUpdateBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile.builder.impl;\n\nimport org.bonitasoft.engine.profile.builder.SProfileMemberUpdateBuilder;\nimport org.bonitasoft.engine.profile.model.SProfileMember;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Celine Souchet\n */\npublic class SProfileMemberUpdateBuilderImpl implements SProfileMemberUpdateBuilder {\n\n    protected final EntityUpdateDescriptor descriptor;\n\n    public SProfileMemberUpdateBuilderImpl() {\n        descriptor = new EntityUpdateDescriptor();\n    }\n\n    @Override\n    public EntityUpdateDescriptor done() {\n        return descriptor;\n    }\n\n    @Override\n    public SProfileMemberUpdateBuilder setGroupId(final long groupId) {\n        descriptor.addField(SProfileMember.GROUP_ID, groupId);\n        return this;\n    }\n\n    @Override\n    public SProfileMemberUpdateBuilder setRoleId(final long roleId) {\n        descriptor.addField(SProfileMember.ROLE_ID, roleId);\n        return this;\n    }\n\n    @Override\n    public SProfileMemberUpdateBuilder setUserId(final long userId) {\n        descriptor.addField(SProfileMember.USER_ID, userId);\n        return this;\n    }\n\n    @Override\n    public SProfileMemberUpdateBuilder setDisplayNamePart1(final String displayNamePart1) {\n        descriptor.addField(SProfileMember.DISPLAY_NAME_PART1, displayNamePart1);\n        return this;\n    }\n\n    @Override\n    public SProfileMemberUpdateBuilder setDisplayNamePart2(final String displayNamePart2) {\n        descriptor.addField(SProfileMember.DISPLAY_NAME_PART2, displayNamePart2);\n        return this;\n    }\n\n    @Override\n    public SProfileMemberUpdateBuilder setDisplayNamePart3(final String displayNamePart3) {\n        descriptor.addField(SProfileMember.DISPLAY_NAME_PART3, displayNamePart3);\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/builder/impl/SProfileUpdateBuilderFactoryImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile.builder.impl;\n\nimport org.bonitasoft.engine.profile.builder.SProfileUpdateBuilder;\nimport org.bonitasoft.engine.profile.builder.SProfileUpdateBuilderFactory;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Celine Souchet\n */\npublic class SProfileUpdateBuilderFactoryImpl implements SProfileUpdateBuilderFactory {\n\n    public SProfileUpdateBuilder createNewInstance() {\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        return new SProfileUpdateBuilderImpl(descriptor);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/builder/impl/SProfileUpdateBuilderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile.builder.impl;\n\nimport org.bonitasoft.engine.profile.builder.SProfileUpdateBuilder;\nimport org.bonitasoft.engine.profile.model.SProfile;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\n\n/**\n * @author Celine Souchet\n */\npublic class SProfileUpdateBuilderImpl implements SProfileUpdateBuilder {\n\n    protected final EntityUpdateDescriptor descriptor;\n\n    public SProfileUpdateBuilderImpl(final EntityUpdateDescriptor descriptor) {\n        this.descriptor = descriptor;\n    }\n\n    @Override\n    public SProfileUpdateBuilder setName(final String name) {\n        descriptor.addField(SProfile.NAME, name);\n        return this;\n    }\n\n    @Override\n    public SProfileUpdateBuilder setDescription(final String description) {\n        descriptor.addField(SProfile.DESCRIPTION, description);\n        return this;\n    }\n\n    @Override\n    public SProfileUpdateBuilder setIconPath(final String iconPath) {\n        descriptor.addField(SProfile.ICON_PATH, iconPath);\n        return this;\n    }\n\n    @Override\n    public SProfileUpdateBuilder setLastUpdateDate(final long lastUpdateDate) {\n        descriptor.addField(SProfile.LAST_UPDATE_DATE, lastUpdateDate);\n        return this;\n    }\n\n    @Override\n    public SProfileUpdateBuilder setLastUpdatedBy(final long lastUpdatedBy) {\n        descriptor.addField(SProfile.LAST_UPDATED_BY, lastUpdatedBy);\n        return this;\n    }\n\n    @Override\n    public EntityUpdateDescriptor done() {\n        return descriptor;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/exception/profile/SProfileAlreadyExistsException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile.exception.profile;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SProfileAlreadyExistsException extends SBonitaException {\n\n    private static final long serialVersionUID = 2613865245278954372L;\n\n    public SProfileAlreadyExistsException(final String message) {\n        super(message);\n    }\n\n    public SProfileAlreadyExistsException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SProfileAlreadyExistsException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/exception/profile/SProfileCreationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile.exception.profile;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SProfileCreationException extends SBonitaException {\n\n    private static final long serialVersionUID = 5352601047167402771L;\n\n    public SProfileCreationException(final String message) {\n        super(message);\n    }\n\n    public SProfileCreationException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SProfileCreationException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/exception/profile/SProfileDeletionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile.exception.profile;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SProfileDeletionException extends SBonitaException {\n\n    private static final long serialVersionUID = 3465858452506324242L;\n\n    public SProfileDeletionException(final String message) {\n        super(message);\n    }\n\n    public SProfileDeletionException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SProfileDeletionException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/exception/profile/SProfileException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile.exception.profile;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SProfileException extends SBonitaException {\n\n    private static final long serialVersionUID = -4589920305652820696L;\n\n    public SProfileException(final String message) {\n        super(message);\n    }\n\n    public SProfileException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SProfileException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/exception/profile/SProfileGettingException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile.exception.profile;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SProfileGettingException extends SBonitaException {\n\n    private static final long serialVersionUID = 1089348511424383086L;\n\n    public SProfileGettingException(final String message) {\n        super(message);\n    }\n\n    public SProfileGettingException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SProfileGettingException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/exception/profile/SProfileNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile.exception.profile;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SProfileNotFoundException extends SBonitaException {\n\n    private static final long serialVersionUID = -6787110496892352279L;\n\n    public SProfileNotFoundException(final String message) {\n        super(message);\n    }\n\n    public SProfileNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SProfileNotFoundException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/exception/profile/SProfileUpdateException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile.exception.profile;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SProfileUpdateException extends SBonitaException {\n\n    private static final long serialVersionUID = 4179176029668815474L;\n\n    public SProfileUpdateException(final String message) {\n        super(message);\n    }\n\n    public SProfileUpdateException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SProfileUpdateException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/exception/profilemember/SProfileMemberCreationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile.exception.profilemember;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Celine Souchet\n */\npublic class SProfileMemberCreationException extends SBonitaException {\n\n    private static final long serialVersionUID = 5352601047167402771L;\n\n    public SProfileMemberCreationException(final String message) {\n        super(message);\n    }\n\n    public SProfileMemberCreationException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SProfileMemberCreationException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/exception/profilemember/SProfileMemberDeletionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile.exception.profilemember;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SProfileMemberDeletionException extends SBonitaException {\n\n    private static final long serialVersionUID = -733208211528042662L;\n\n    public SProfileMemberDeletionException(final String message) {\n        super(message);\n    }\n\n    public SProfileMemberDeletionException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SProfileMemberDeletionException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/exception/profilemember/SProfileMemberException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile.exception.profilemember;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SProfileMemberException extends SBonitaException {\n\n    private static final long serialVersionUID = -4589920305652820696L;\n\n    public SProfileMemberException(final String message) {\n        super(message);\n    }\n\n    public SProfileMemberException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SProfileMemberException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/exception/profilemember/SProfileMemberNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile.exception.profilemember;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SProfileMemberNotFoundException extends SBonitaException {\n\n    private static final long serialVersionUID = -5699697434595954926L;\n\n    public SProfileMemberNotFoundException(final String message) {\n        super(message);\n    }\n\n    public SProfileMemberNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n    public SProfileMemberNotFoundException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/impl/ProfileServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile.impl;\n\nimport java.util.*;\n\nimport org.bonitasoft.engine.commons.NullCheckingUtil;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.persistence.*;\nimport org.bonitasoft.engine.profile.ProfileService;\nimport org.bonitasoft.engine.profile.builder.impl.SProfileLogBuilderImpl;\nimport org.bonitasoft.engine.profile.builder.impl.SProfileMemberLogBuilderImpl;\nimport org.bonitasoft.engine.profile.exception.profile.SProfileCreationException;\nimport org.bonitasoft.engine.profile.exception.profile.SProfileDeletionException;\nimport org.bonitasoft.engine.profile.exception.profile.SProfileNotFoundException;\nimport org.bonitasoft.engine.profile.exception.profile.SProfileUpdateException;\nimport org.bonitasoft.engine.profile.exception.profilemember.SProfileMemberCreationException;\nimport org.bonitasoft.engine.profile.exception.profilemember.SProfileMemberDeletionException;\nimport org.bonitasoft.engine.profile.exception.profilemember.SProfileMemberNotFoundException;\nimport org.bonitasoft.engine.profile.model.SProfile;\nimport org.bonitasoft.engine.profile.model.SProfileMember;\nimport org.bonitasoft.engine.profile.persistence.SelectDescriptorBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLog;\nimport org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity;\nimport org.bonitasoft.engine.queriablelogger.model.builder.ActionType;\nimport org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SLogBuilder;\nimport org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.*;\nimport org.bonitasoft.engine.services.PersistenceService;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor;\n\n/**\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n */\npublic class ProfileServiceImpl implements ProfileService {\n\n    private final PersistenceService persistenceService;\n\n    private final Recorder recorder;\n\n    private final QueriableLoggerService queriableLoggerService;\n\n    private final SessionService sessionService;\n\n    private final ReadSessionAccessor sessionAccessor;\n\n    public ProfileServiceImpl(PersistenceService persistenceService, Recorder recorder,\n            QueriableLoggerService queriableLoggerService,\n            ReadSessionAccessor sessionAccessor, SessionService sessionService) {\n        super();\n        this.persistenceService = persistenceService;\n        this.recorder = recorder;\n        this.queriableLoggerService = queriableLoggerService;\n        this.sessionAccessor = sessionAccessor;\n        this.sessionService = sessionService;\n    }\n\n    @Override\n    public SProfile createProfile(final SProfile profile) throws SProfileCreationException {\n        final SProfileLogBuilderImpl logBuilder = getSProfileLog(ActionType.CREATED, \"Adding a new profile\");\n        try {\n            recorder.recordInsert(new InsertRecord(profile), PROFILE);\n            log(profile.getId(), SQueriableLog.STATUS_OK, logBuilder, \"createProfile\");\n            return profile;\n        } catch (final SRecorderException re) {\n            log(profile.getId(), SQueriableLog.STATUS_FAIL, logBuilder, \"createProfile\");\n            throw new SProfileCreationException(re);\n        }\n    }\n\n    @Override\n    public SProfile getProfile(final long profileId) throws SProfileNotFoundException {\n        try {\n            final SelectByIdDescriptor<SProfile> descriptor = SelectDescriptorBuilder.getElementById(SProfile.class,\n                    profileId);\n            final SProfile profile = persistenceService.selectById(descriptor);\n            if (profile == null) {\n                throw new SProfileNotFoundException(\"No profile exists with id: \" + profileId);\n            }\n            return profile;\n        } catch (final SBonitaReadException e) {\n            throw new SProfileNotFoundException(e);\n        }\n    }\n\n    @Override\n    public SProfile getProfileByName(final String profileName) throws SProfileNotFoundException, SBonitaReadException {\n        final SelectOneDescriptor<SProfile> descriptor = SelectDescriptorBuilder\n                .getElementByNameDescriptor(SProfile.class, \"Profile\", profileName);\n        final SProfile profile = persistenceService.selectOne(descriptor);\n        if (profile == null) {\n            throw new SProfileNotFoundException(\"No profile exists with name: \" + profileName);\n        }\n        return profile;\n    }\n\n    @Override\n    public List<SProfile> getProfiles(final List<Long> profileIds) throws SProfileNotFoundException {\n        final List<SProfile> profiles = new ArrayList<>();\n        if (profileIds != null) {\n            for (final Long profileId : profileIds) {\n                final SProfile profile = getProfile(profileId);\n                profiles.add(profile);\n            }\n        }\n        return profiles;\n    }\n\n    @Override\n    public SProfile updateProfile(final SProfile sProfile, final EntityUpdateDescriptor descriptor)\n            throws SProfileUpdateException {\n        NullCheckingUtil.checkArgsNotNull(sProfile);\n        final SProfileLogBuilderImpl logBuilder = getSProfileLog(ActionType.UPDATED, \"Updating profile\");\n        try {\n            recorder.recordUpdate(UpdateRecord.buildSetFields(sProfile, descriptor), PROFILE);\n            log(sProfile.getId(), SQueriableLog.STATUS_OK, logBuilder, \"updateProfile\");\n        } catch (final SRecorderException re) {\n            log(sProfile.getId(), SQueriableLog.STATUS_FAIL, logBuilder, \"updateProfile\");\n            throw new SProfileUpdateException(re);\n        }\n        return sProfile;\n    }\n\n    @Override\n    public void deleteProfile(final SProfile profile)\n            throws SProfileDeletionException, SProfileMemberDeletionException {\n        NullCheckingUtil.checkArgsNotNull(profile);\n        final SProfileLogBuilderImpl logBuilder = getSProfileLog(ActionType.DELETED, \"Deleting profile\");\n        try {\n            deleteAllProfileMembersOfProfile(profile);\n            recorder.recordDelete(new DeleteRecord(profile), PROFILE);\n            log(profile.getId(), SQueriableLog.STATUS_OK, logBuilder, \"deleteProfile\");\n        } catch (final SRecorderException re) {\n            log(profile.getId(), SQueriableLog.STATUS_FAIL, logBuilder, \"deleteProfile\");\n            throw new SProfileDeletionException(re);\n        }\n    }\n\n    @Override\n    public void deleteAllProfileMembersOfProfile(final SProfile profile) throws SProfileMemberDeletionException {\n        final QueryOptions queryOptions = new QueryOptions(0, 100, SProfileMember.class, \"id\", OrderByType.ASC);\n        try {\n            List<SProfileMember> sProfileMembers;\n            do {\n                sProfileMembers = getProfileMembers(profile.getId(), queryOptions);\n                for (final SProfileMember profileUser : sProfileMembers) {\n                    deleteProfileMember(profileUser);\n                }\n            } while (!sProfileMembers.isEmpty());\n        } catch (final SProfileMemberNotFoundException e) {\n            throw new SProfileMemberDeletionException(e);\n        }\n    }\n\n    @Override\n    public void deleteProfile(final long profileId)\n            throws SProfileNotFoundException, SProfileDeletionException,\n            SProfileMemberDeletionException {\n        final SProfile profile = getProfile(profileId);\n        deleteProfile(profile);\n    }\n\n    private SProfileMember buildProfileMember(final long profileId, final String displayNamePart1,\n            final String displayNamePart2,\n            final String displayNamePart3) {\n        return SProfileMember.builder().profileId(profileId)\n                .displayNamePart1(displayNamePart1)\n                .displayNamePart2(displayNamePart2)\n                .displayNamePart1(displayNamePart3).build();\n    }\n\n    @Override\n    public SProfileMember addUserToProfile(final long profileId, final long userId, final String firstName,\n            final String lastName, final String userName)\n            throws SProfileMemberCreationException {\n        final SProfileMember profileMember = buildProfileMember(profileId, firstName, lastName, userName);\n        profileMember.setUserId(userId);\n        createProfileMember(profileMember);\n        return profileMember;\n    }\n\n    private void createProfileMember(final SProfileMember profileMember) throws SProfileMemberCreationException {\n        final String message = \"Adding a new profile member\";\n        final SProfileMemberLogBuilderImpl logBuilder = getProfileMemberLog(ActionType.CREATED, message);\n        try {\n            recorder.recordInsert(new InsertRecord(profileMember), PROFILE_MEMBER);\n            log(profileMember.getId(), SQueriableLog.STATUS_OK, logBuilder, \"insertProfileMember\");\n        } catch (final SRecorderException re) {\n            log(profileMember.getId(), SQueriableLog.STATUS_FAIL, logBuilder, \"insertProfileMember\");\n            throw new SProfileMemberCreationException(re);\n        }\n    }\n\n    @Override\n    public SProfileMember addGroupToProfile(final long profileId, final long groupId, final String groupName,\n            final String parentPath)\n            throws SProfileMemberCreationException {\n        final SProfileMember profileMember = buildProfileMember(profileId, groupName, parentPath, null);\n        profileMember.setGroupId(groupId);\n        createProfileMember(profileMember);\n        return profileMember;\n    }\n\n    @Override\n    public SProfileMember addRoleToProfile(final long profileId, final long roleId, final String roleName)\n            throws SProfileMemberCreationException {\n        final SProfileMember profileMember = buildProfileMember(profileId, roleName, null, null);\n        profileMember.setRoleId(roleId);\n        createProfileMember(profileMember);\n        return profileMember;\n    }\n\n    @Override\n    public SProfileMember addRoleAndGroupToProfile(final long profileId, final long roleId, final long groupId,\n            final String roleName, final String groupName,\n            final String groupParentPath) throws SProfileMemberCreationException {\n        final SProfileMember profileMember = buildProfileMember(profileId, roleName, groupName, groupParentPath);\n        profileMember.setGroupId(groupId);\n        profileMember.setRoleId(roleId);\n        createProfileMember(profileMember);\n        return profileMember;\n    }\n\n    @Override\n    public void deleteProfileMember(final long profileMemberId)\n            throws SProfileMemberDeletionException, SProfileMemberNotFoundException {\n        final SProfileMember profileMember = getProfileMemberWithoutDisplayName(profileMemberId);\n        deleteProfileMember(profileMember);\n    }\n\n    @Override\n    public SProfileMember getProfileMemberWithoutDisplayName(final long profileMemberId)\n            throws SProfileMemberNotFoundException {\n        final SelectByIdDescriptor<SProfileMember> selectByIdDescriptor = SelectDescriptorBuilder\n                .getProfileMemberWithoutDisplayName(profileMemberId);\n        try {\n            final SProfileMember profileMember = persistenceService.selectById(selectByIdDescriptor);\n            if (profileMember == null) {\n                throw new SProfileMemberNotFoundException(profileMemberId + \" does not refer to any profile member\");\n            }\n            return profileMember;\n        } catch (final SBonitaReadException bre) {\n            throw new SProfileMemberNotFoundException(bre);\n        }\n    }\n\n    @Override\n    public void deleteProfileMember(final SProfileMember profileMember) throws SProfileMemberDeletionException {\n        final String message = \"Deleting profile member for userId \" + profileMember.getUserId() + \" with roleId \"\n                + profileMember.getRoleId() + \" in groupId \"\n                + profileMember.getGroupId();\n        final SProfileMemberLogBuilderImpl logBuilder = getProfileMemberLog(ActionType.DELETED, message);\n        try {\n            recorder.recordDelete(new DeleteRecord(profileMember), PROFILE_MEMBER);\n            log(profileMember.getId(), SQueriableLog.STATUS_OK, logBuilder, \"deleteProfileMember\");\n        } catch (final SRecorderException re) {\n            log(profileMember.getId(), SQueriableLog.STATUS_FAIL, logBuilder, \"deleteProfileMember\");\n            throw new SProfileMemberDeletionException(re);\n        }\n    }\n\n    @Override\n    public List<SProfileMember> getProfileMembersOfUser(final long userId, final int fromIndex,\n            final int numberOfElements, final String field,\n            final OrderByType order) throws SBonitaReadException {\n        final SelectListDescriptor<SProfileMember> descriptor = SelectDescriptorBuilder.getDirectProfileMembersOfUser(\n                userId, field, order, fromIndex,\n                numberOfElements);\n        return persistenceService.selectList(descriptor);\n    }\n\n    @Override\n    public List<SProfileMember> getProfileMembersOfGroup(final long groupId, final int fromIndex,\n            final int numberOfElements, final String field,\n            final OrderByType order) throws SBonitaReadException {\n        final SelectListDescriptor<SProfileMember> descriptor = SelectDescriptorBuilder.getDirectProfileMembersOfGroup(\n                groupId, field, order, fromIndex,\n                numberOfElements);\n        return persistenceService.selectList(descriptor);\n    }\n\n    @Override\n    public List<SProfileMember> getProfileMembersOfRole(final long roleId, final int fromIndex,\n            final int numberOfElements, final String field,\n            final OrderByType order) throws SBonitaReadException {\n        final SelectListDescriptor<SProfileMember> descriptor = SelectDescriptorBuilder.getDirectProfileMembersOfRole(\n                roleId, field, order, fromIndex,\n                numberOfElements);\n        return persistenceService.selectList(descriptor);\n    }\n\n    @Override\n    public List<SProfile> searchProfilesOfUser(final long userId, final int fromIndex, final int numberOfElements,\n            final String field, final OrderByType order)\n            throws SBonitaReadException {\n        final SelectListDescriptor<SProfile> descriptor = SelectDescriptorBuilder.getProfilesOfUser(userId, fromIndex,\n                numberOfElements, field, order);\n        return persistenceService.selectList(descriptor);\n    }\n\n    @Override\n    public List<SProfile> getProfilesOfUser(long userId) throws SBonitaReadException {\n        return searchProfilesOfUser(userId, 0, Integer.MAX_VALUE, null, null);\n    }\n\n    @Override\n    public List<SProfileMember> getProfileMembers(final long profileId, final QueryOptions queryOptions)\n            throws SProfileMemberNotFoundException {\n        try {\n            final SelectListDescriptor<SProfileMember> descriptor = SelectDescriptorBuilder\n                    .getSProfileMembersWithoutDisplayName(profileId, queryOptions);\n            return persistenceService.selectList(descriptor);\n        } catch (final SBonitaReadException bre) {\n            throw new SProfileMemberNotFoundException(bre);\n        }\n    }\n\n    @Override\n    public List<SProfileMember> searchProfileMembers(final String querySuffix, final QueryOptions queryOptions)\n            throws SBonitaReadException {\n        return persistenceService.searchEntity(SProfileMember.class, querySuffix, queryOptions, null);\n    }\n\n    @Override\n    public long getNumberOfProfileMembers(final String querySuffix, final QueryOptions countOptions)\n            throws SBonitaReadException {\n        return persistenceService.getNumberOfEntities(SProfileMember.class, querySuffix, countOptions, null);\n    }\n\n    @Override\n    public List<SProfileMember> getProfileMembers(final List<Long> profileIds) throws SBonitaReadException {\n        if (profileIds == null || profileIds.isEmpty()) {\n            return Collections.emptyList();\n        }\n        final QueryOptions queryOptions = new QueryOptions(0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS,\n                SProfileMember.class, \"id\", OrderByType.ASC);\n        final Map<String, Object> emptyMap = Collections.singletonMap(\"profileIds\", profileIds);\n        return persistenceService.selectList(new SelectListDescriptor<>(\"getProfileMembersFromProfileIds\",\n                emptyMap, SProfileMember.class, queryOptions));\n    }\n\n    private void log(final long objectId, final int sQueriableLogStatus, final SPersistenceLogBuilder logBuilder,\n            final String callerMethodName) {\n        logBuilder.actionScope(String.valueOf(objectId));\n        logBuilder.actionStatus(sQueriableLogStatus);\n        logBuilder.objectId(objectId);\n        final SQueriableLog log = logBuilder.build();\n        if (queriableLoggerService.isLoggable(log.getActionType(), log.getSeverity())) {\n            queriableLoggerService.log(this.getClass().getName(), callerMethodName, log);\n        }\n    }\n\n    @Override\n    public void deleteAllProfileMembers() throws SProfileMemberDeletionException {\n        try {\n            final DeleteAllRecord record = new DeleteAllRecord(SProfileMember.class, null);\n            recorder.recordDeleteAll(record);\n        } catch (final SRecorderException e) {\n            throw new SProfileMemberDeletionException(\"Can't delete all profile members.\", e);\n        }\n    }\n\n    @Override\n    public long getNumberOfProfiles(final QueryOptions queryOptions) throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.emptyMap();\n        return persistenceService.getNumberOfEntities(SProfile.class, queryOptions, parameters);\n    }\n\n    @Override\n    public List<SProfile> searchProfiles(final QueryOptions queryOptions) throws SBonitaReadException {\n        final Map<String, Object> parameters = Collections.emptyMap();\n        return persistenceService.searchEntity(SProfile.class, queryOptions, parameters);\n    }\n\n    private SProfileLogBuilderImpl getSProfileLog(final ActionType actionType, final String message) {\n        final SProfileLogBuilderImpl logBuilder = new SProfileLogBuilderImpl();\n        this.initializeLogBuilder(logBuilder, message);\n        this.updateLog(actionType, logBuilder);\n        return logBuilder;\n    }\n\n    private SProfileMemberLogBuilderImpl getProfileMemberLog(final ActionType actionType, final String message) {\n        final SProfileMemberLogBuilderImpl logBuilder = new SProfileMemberLogBuilderImpl();\n        this.initializeLogBuilder(logBuilder, message);\n        this.updateLog(actionType, logBuilder);\n        return logBuilder;\n    }\n\n    private <T extends SLogBuilder> void initializeLogBuilder(final T logBuilder, final String message) {\n        logBuilder.actionStatus(SQueriableLog.STATUS_FAIL).severity(SQueriableLogSeverity.INTERNAL).rawMessage(message);\n    }\n\n    private <T extends HasCRUDEAction> void updateLog(final ActionType actionType, final T logBuilder) {\n        logBuilder.setActionType(actionType);\n    }\n\n    @Override\n    public void updateProfileMetaData(final long profileId) throws SProfileUpdateException {\n        long userId;\n        try {\n            userId = getSessionUserId();\n            final Map<String, Object> params = new HashMap<>();\n            params.put(\"lastUpdateDate\", System.currentTimeMillis());\n            params.put(\"lastUpdatedBy\", userId);\n            params.put(\"id\", profileId);\n            persistenceService.update(\"updateLastUpdateProfile\", params);\n        } catch (final SBonitaException e) {\n            throw new SProfileUpdateException(e);\n        }\n\n    }\n\n    private long getSessionUserId() {\n        return sessionService.getLoggedUserFromSession(sessionAccessor);\n    }\n}\n"
  },
  {
    "path": "services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/model/SProfile.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile.model;\n\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@Builder(toBuilder = true)\n@Entity\n@Table(name = \"profile\")\npublic class SProfile implements PersistentObject {\n\n    public static final String PROFILE_IDS = \"profileIds\";\n    public static final String ICON_PATH = \"iconPath\";\n    public static final String DESCRIPTION = \"description\";\n    public static final String NAME = \"name\";\n    public static final String ID = \"id\";\n    public static final String IS_DEFAULT = \"isDefault\";\n    public static final String CREATION_DATE = \"creationDate\";\n    public static final String CREATED_BY = \"createdBy\";\n    public static final String LAST_UPDATE_DATE = \"lastUpdateDate\";\n    public static final String LAST_UPDATED_BY = \"lastUpdatedBy\";\n    @Id\n    private long id;\n    @Column\n    private boolean isDefault;\n    @Column\n    private String name;\n    @Column\n    private String description;\n    @Column\n    private long creationDate;\n    @Column\n    private long createdBy;\n    @Column\n    private long lastUpdateDate;\n    @Column\n    private long lastUpdatedBy;\n\n    public SProfile(final SProfile profile) {\n        super();\n        id = profile.getId();\n        isDefault = profile.isDefault();\n        name = profile.getName();\n        description = profile.getDescription();\n        creationDate = profile.getCreationDate();\n        createdBy = profile.getCreatedBy();\n        lastUpdateDate = profile.getLastUpdateDate();\n        lastUpdatedBy = profile.getLastUpdatedBy();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/model/SProfileMember.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile.model;\n\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@Builder\n@Entity\n@Table(name = \"profilemember\")\npublic class SProfileMember implements PersistentObject {\n\n    public static final String DISPLAY_NAME_PART3 = \"displayNamePart3\";\n    public static final String DISPLAY_NAME_PART2 = \"displayNamePart2\";\n    public static final String DISPLAY_NAME_PART1 = \"displayNamePart1\";\n    public static final String ROLE_ID = \"roleId\";\n    public static final String GROUP_ID = \"groupId\";\n    public static final String USER_ID = \"userId\";\n    public static final String PROFILE_ID = \"profileId\";\n    public static final String ID = \"id\";\n    @Id\n    private long id;\n    @Column\n    private long profileId;\n    @Column\n    @Builder.Default\n    private long userId = -1;\n    @Column\n    @Builder.Default\n    private long groupId = -1;\n    @Column\n    @Builder.Default\n    private long roleId = -1;\n    private transient String displayNamePart1;\n    private transient String displayNamePart2;\n    private transient String displayNamePart3;\n}\n"
  },
  {
    "path": "services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/persistence/SelectDescriptorBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile.persistence;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.profile.model.SProfile;\nimport org.bonitasoft.engine.profile.model.SProfileMember;\n\n/**\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n */\npublic class SelectDescriptorBuilder {\n\n    private static final String ROLE_ID = \"roleId\";\n\n    private static final String GROUP_ID = \"groupId\";\n\n    private static final String USER_ID = \"userId\";\n\n    private static final String PROFILE_ID = \"profileId\";\n\n    public static <T extends PersistentObject> SelectByIdDescriptor<T> getElementById(final Class<T> clazz,\n            final long id) {\n        return new SelectByIdDescriptor<>(clazz, id);\n    }\n\n    public static <T extends PersistentObject> SelectOneDescriptor<T> getElementByNameDescriptor(final Class<T> clazz,\n            final String elementName,\n            final String name) {\n        final Map<String, Object> parameters = Collections.singletonMap(\"name\", name);\n        return new SelectOneDescriptor<>(\"get\" + elementName + \"ByName\", parameters, clazz);\n    }\n\n    public static SelectListDescriptor<SProfileMember> getDirectProfileMembersOfUser(final long userId,\n            final String field, final OrderByType order,\n            final int fromIndex, final int numberOfElements) {\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfElements, SProfileMember.class, field,\n                order);\n        final Map<String, Object> parameters = new HashMap<>(1);\n        parameters.put(USER_ID, userId);\n        return new SelectListDescriptor<>(\"getDirectProfileMembersOfUser\", parameters, SProfileMember.class,\n                queryOptions);\n    }\n\n    public static SelectListDescriptor<SProfileMember> getDirectProfileMembersOfGroup(final long groupId,\n            final String field, final OrderByType order,\n            final int fromIndex, final int numberOfElements) {\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfElements, SProfileMember.class, field,\n                order);\n        final Map<String, Object> parameters = new HashMap<>(1);\n        parameters.put(GROUP_ID, groupId);\n        return new SelectListDescriptor<>(\"getDirectProfileMembersOfGroup\", parameters, SProfileMember.class,\n                queryOptions);\n    }\n\n    public static SelectListDescriptor<SProfileMember> getDirectProfileMembersOfRole(final long roleId,\n            final String field, final OrderByType order,\n            final int fromIndex, final int numberOfElements) {\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfElements, SProfileMember.class, field,\n                order);\n        final Map<String, Object> parameters = new HashMap<>(1);\n        parameters.put(ROLE_ID, roleId);\n        return new SelectListDescriptor<>(\"getDirectProfileMembersOfRole\", parameters, SProfileMember.class,\n                queryOptions);\n    }\n\n    public static SelectListDescriptor<SProfile> getProfilesOfUser(final long userId, final int fromIndex,\n            final int numberOfElements, final String field,\n            final OrderByType order) {\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfElements, SProfile.class, field, order);\n        final Map<String, Object> parameters = Collections.singletonMap(USER_ID, userId);\n        return new SelectListDescriptor<>(\"getProfilesOfUser\", parameters, SProfile.class, queryOptions);\n    }\n\n    public static SelectListDescriptor<SProfile> getProfilesWithNavigationOfUser(long userId, int fromIndex,\n            int numberOfElements, String field, OrderByType order) {\n        final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfElements, SProfile.class, field, order);\n        final Map<String, Object> parameters = Collections.singletonMap(USER_ID, userId);\n        return new SelectListDescriptor<>(\"getProfilesWithNavigationOfUser\", parameters, SProfile.class, queryOptions);\n    }\n\n    public static SelectListDescriptor<SProfileMember> getSProfileMembersWithoutDisplayName(final long profileId,\n            final QueryOptions queryOptions) {\n        final Map<String, Object> parameters = Collections.singletonMap(PROFILE_ID, profileId);\n        return new SelectListDescriptor<>(\"getSProfileMembersWithoutDisplayName\", parameters, SProfileMember.class,\n                queryOptions);\n    }\n\n    public static SelectByIdDescriptor<SProfileMember> getProfileMemberWithoutDisplayName(final long profileMemberId) {\n        return new SelectByIdDescriptor<>(SProfileMember.class, profileMemberId);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-profile/src/main/resources/org/bonitasoft/engine/profile/model/impl/hibernate/profile.queries.hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\" \"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd\">\n<hibernate-mapping auto-import=\"false\">\n\n   <query name=\"getProfileByName\">\n\t\tSELECT profile\n\t\tFROM org.bonitasoft.engine.profile.model.SProfile AS profile\n\t\tWHERE profile.name = :name\n\t</query>\n\n\t<query name=\"getNumberOfSProfile\">\n\t\tSELECT COUNT(profile.id)\n\t\tFROM org.bonitasoft.engine.profile.model.SProfile AS profile\n\t</query>\n\n\t<query name=\"searchSProfile\">\n\t\tSELECT profile\n\t\tFROM org.bonitasoft.engine.profile.model.SProfile AS profile\n\t</query>\n\n\t<query name=\"getDirectProfileMembersOfUser\">\n\t\tSELECT pm\n\t\tFROM org.bonitasoft.engine.profile.model.SProfileMember AS pm\n\t\tWHERE pm.userId = :userId\n\t</query>\n\n\t<query name=\"getDirectProfileMembersOfGroup\">\n\t\tSELECT pm\n\t\tFROM org.bonitasoft.engine.profile.model.SProfileMember AS pm\n\t\tWHERE pm.groupId = :groupId\n\t</query>\n\n\t<query name=\"getDirectProfileMembersOfRole\">\n\t\tSELECT pm\n\t\tFROM org.bonitasoft.engine.profile.model.SProfileMember AS pm\n\t\tWHERE pm.roleId = :roleId\n\t</query>\n\n\t<query name=\"getSProfileMembersWithoutDisplayName\">\n\t\tSELECT pm \n\t\tFROM org.bonitasoft.engine.profile.model.SProfileMember AS pm \n\t\tWHERE pm.profileId = :profileId\n\t</query>\n\n\t<query name=\"searchSProfileMemberwithSUserForUser\">\n\t\tSELECT new org.bonitasoft.engine.profile.model.SProfileMember(pm.id, pm.profileId, pm.userId, pm.groupId, pm.roleId, user.firstName AS displayNamePart1, user.lastName AS displayNamePart2, user.userName AS displayNamePart3)\n\t\tFROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, \n\t\t\torg.bonitasoft.engine.identity.model.SUser AS user\n\t\tWHERE pm.userId = user.id\n\t</query>\n\n\t<query name=\"searchSProfileMemberForUser\">\n\t\tSELECT new org.bonitasoft.engine.profile.model.SProfileMember(pm.id, pm.profileId, pm.userId, pm.groupId, pm.roleId, user.firstName AS displayNamePart1, user.lastName AS displayNamePart2, user.userName AS displayNamePart3)\n\t\tFROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, \n\t\t\torg.bonitasoft.engine.identity.model.SUser AS user\n\t\tWHERE pm.userId = user.id\n\t</query>\n\n\t<query name=\"getNumberOfSProfileMemberwithSUserForUser\">\n\t\tSELECT COUNT(*)\n\t\tFROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, \n\t\t\torg.bonitasoft.engine.identity.model.SUser AS user\n\t\tWHERE pm.userId = user.id\n\t</query>\n\n\t<query name=\"getNumberOfSProfileMemberForUser\">\n\t\tSELECT COUNT(*)\n\t\tFROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, \n\t\t\torg.bonitasoft.engine.identity.model.SUser AS user\n\t\tWHERE pm.userId = user.id\n\t\tAND pm.userId &gt;0\n\t</query>\n\n\t<query name=\"searchSProfileMemberwithSGroupForGroup\">\n\t\tSELECT new org.bonitasoft.engine.profile.model.SProfileMember(pm.id, pm.profileId, pm.userId, pm.groupId, pm.roleId, group_.name AS displayNamePart1, group_.parentPath AS displayNamePart2, ''  AS displayNamePart3)\n\t\tFROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, \n\t\t\torg.bonitasoft.engine.identity.model.SGroup AS group_\n\t\tWHERE pm.groupId = group_.id\n\t\tAND pm.roleId = -1\n\t</query>\n\n\t<query name=\"searchSProfileMemberForGroup\">\n\t\tSELECT new org.bonitasoft.engine.profile.model.SProfileMember(pm.id, pm.profileId, pm.userId, pm.groupId, pm.roleId, group_.name AS displayNamePart1, group_.parentPath AS displayNamePart2, ''  AS displayNamePart3)\n\t\tFROM org.bonitasoft.engine.profile.model.SProfileMember AS pm,\n\t\t\torg.bonitasoft.engine.identity.model.SGroup AS group_\n\t\tWHERE pm.groupId = group_.id\n\t\tAND pm.roleId = -1\n\t</query>\n\n\t<query name=\"getNumberOfSProfileMemberwithSGroupForGroup\">\n\t\tSELECT COUNT(pm.id) \n\t\tFROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, \n\t\t\torg.bonitasoft.engine.identity.model.SGroup AS group_\n\t\tWHERE pm.groupId = group_.id\n\t\tAND pm.roleId = -1\n\t</query>\n\n\t<query name=\"getNumberOfSProfileMemberForGroup\">\n\t\tSELECT COUNT(pm.id) \n\t\tFROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, \n\t\t\torg.bonitasoft.engine.identity.model.SGroup AS group_\n\t\tWHERE pm.groupId = group_.id\n\t\tAND pm.roleId = -1\n\t</query>\n\n\t<query name=\"searchSProfileMemberwithSRoleForRole\">\n\t\tSELECT new org.bonitasoft.engine.profile.model.SProfileMember(pm.id, pm.profileId, pm.userId, pm.groupId, pm.roleId, role.name AS displayNamePart1, '' AS displayNamePart2, '' AS displayNamePart3)\n\t\tFROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, \n\t\t\torg.bonitasoft.engine.identity.model.SRole AS role\n\t\tWHERE pm.roleId = role.id\n\t\tAND pm.groupId = -1\n\t</query>\n\n\t<query name=\"searchSProfileMemberForRole\">\n\t\tSELECT new org.bonitasoft.engine.profile.model.SProfileMember(pm.id, pm.profileId, pm.userId, pm.groupId, pm.roleId, role.name AS displayNamePart1, '' AS displayNamePart2, '' AS displayNamePart3)\n\t\tFROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, \n\t\t\torg.bonitasoft.engine.identity.model.SRole AS role\n\t\tWHERE pm.roleId = role.id\n\t\tAND pm.groupId = -1\n\t</query>\n\n\t<query name=\"getNumberOfSProfileMemberwithSRoleForRole\">\n\t\tSELECT  COUNT(pm.id)\n\t\tFROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, \n\t\t\torg.bonitasoft.engine.identity.model.SRole AS role\n\t\tWHERE  pm.roleId = role.id\n\t\tAND pm.groupId = -1\n\t</query>\n\n\t<query name=\"getNumberOfSProfileMemberForRole\">\n\t\tSELECT  COUNT(pm.id)\n\t\tFROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, \n\t\t\torg.bonitasoft.engine.identity.model.SRole AS role\n\t\tWHERE  pm.roleId = role.id \n\t\tAND pm.groupId = -1\n\t</query>\n\n\t<query name=\"searchSProfileMemberwithSGroupwithSRoleForRoleAndGroup\">\n\t\tSELECT new org.bonitasoft.engine.profile.model.SProfileMember(pm.id, pm.profileId, pm.userId, pm.groupId, pm.roleId, role.name AS displayNamePart1, group_.name AS displayNamePart2, group_.parentPath AS displayNamePart3)\n\t\tFROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, \n\t\t\torg.bonitasoft.engine.identity.model.SRole AS role,\n\t\t\torg.bonitasoft.engine.identity.model.SGroup AS group_\n\t\tWHERE  pm.roleId = role.id\n\t\tAND pm.groupId = group_.id\n\t</query>\n\n\t<query name=\"searchSProfileMemberwithSGroupForRoleAndGroup\">\n\t\tSELECT new org.bonitasoft.engine.profile.model.SProfileMember(pm.id, pm.profileId, pm.userId, pm.groupId, pm.roleId, role.name AS displayNamePart1, group_.name AS displayNamePart2, group_.parentPath AS displayNamePart3)\n\t\tFROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, \n\t\t\torg.bonitasoft.engine.identity.model.SRole AS role,\n\t\t\torg.bonitasoft.engine.identity.model.SGroup AS group_\n\t\tWHERE  pm.roleId = role.id\n\t\tAND pm.groupId = group_.id\n\t</query>\n\n\t<query name=\"searchSProfileMemberwithSRoleForRoleAndGroup\">\n\t\tSELECT new org.bonitasoft.engine.profile.model.SProfileMember(pm.id, pm.profileId, pm.userId, pm.groupId, pm.roleId, role.name AS displayNamePart1, group_.name AS displayNamePart2, group_.parentPath AS displayNamePart3)\n\t\tFROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, \n\t\t\torg.bonitasoft.engine.identity.model.SRole AS role,\n\t\t\torg.bonitasoft.engine.identity.model.SGroup AS group_\n\t\tWHERE  pm.roleId = role.id\n\t\tAND pm.groupId = group_.id\n\t</query>\n\n\t<query name=\"searchSProfileMemberForRoleAndGroup\">\n\t\tSELECT new org.bonitasoft.engine.profile.model.SProfileMember(pm.id, pm.profileId, pm.userId, pm.groupId, pm.roleId, role.name AS displayNamePart1, group_.name AS displayNamePart2, group_.parentPath AS displayNamePart3)\n\t\tFROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, \n\t\t\torg.bonitasoft.engine.identity.model.SRole AS role,\n\t\t\torg.bonitasoft.engine.identity.model.SGroup AS group_\n\t\tWHERE  pm.roleId = role.id\n\t\tAND pm.groupId = group_.id\n\t</query>\n\n\t<query name=\"getNumberOfSProfileMemberwithSGroupwithSRoleForRoleAndGroup\">\n\t\tSELECT  COUNT(pm.id) \n\t\tFROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, \n\t\t\torg.bonitasoft.engine.identity.model.SRole AS role,\n\t\t\torg.bonitasoft.engine.identity.model.SGroup AS group_\n\t\tWHERE pm.roleId = role.id\n\t\tAND pm.groupId = group_.id\n\t</query>\n\n\t<query name=\"getNumberOfSProfileMemberwithSRoleForRoleAndGroup\">\n\t\tSELECT  COUNT(pm.id) \n\t\tFROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, \n\t\t\torg.bonitasoft.engine.identity.model.SRole AS role,\n\t\t\torg.bonitasoft.engine.identity.model.SGroup AS group_\n\t\tWHERE pm.roleId = role.id\n\t\tAND pm.groupId = group_.id\n\t</query>\n\n\t<query name=\"getNumberOfSProfileMemberwithSGroupForRoleAndGroup\">\n\t\tSELECT  COUNT(pm.id) \n\t\tFROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, \n\t\t\torg.bonitasoft.engine.identity.model.SRole AS role,\n\t\t\torg.bonitasoft.engine.identity.model.SGroup AS group_\n\t\tWHERE pm.roleId = role.id\n\t\tAND pm.groupId = group_.id\n\t</query>\n\n\t<query name=\"getNumberOfSProfileMemberForRoleAndGroup\">\n\t\tSELECT  COUNT(pm.id) \n\t\tFROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, \n\t\t\torg.bonitasoft.engine.identity.model.SRole AS role,\n\t\t\torg.bonitasoft.engine.identity.model.SGroup AS group_\n\t\tWHERE pm.roleId = role.id\n\t\tAND pm.groupId = group_.id\n\t</query>\n\n\t<query name=\"getProfilesOfUser\">\n\t\tSELECT DISTINCT profile\n\t\tFROM org.bonitasoft.engine.profile.model.SProfile AS profile,\n\t\t     org.bonitasoft.engine.profile.model.SProfileMember AS pm\n\t\tWHERE pm.profileId = profile.id\n\t\tAND (\n\t\t\t(pm.userId = :userId AND pm.roleId = -1 AND pm.groupId = -1)\n\t\t\tOR pm.id IN (\n\t\t\t\tSELECT pm.id\n\t\t\t\tFROM org.bonitasoft.engine.profile.model.SProfileMember AS pm,\n\t\t\t\t     org.bonitasoft.engine.identity.model.SUserMembership AS um\n\t\t\t\tWHERE um.userId = :userId \n\t\t\t\tAND (\n\t\t\t\t\t(pm.groupId = um.groupId AND pm.roleId = -1)\n\t\t\t\t\tOR (pm.roleId = um.roleId AND pm.groupId = -1)\n\t\t\t\t\tOR (pm.groupId = um.groupId AND pm.roleId = um.roleId)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t</query>\n\n\t<query name=\"getNumberOfProfileMemberOfProfile\">\n\t\tSELECT COUNT(pm.id)\n\t\tFROM org.bonitasoft.engine.profile.model.SProfileMember AS pm\n\t\tWHERE pm.profileId = :profileId\n\t</query>\n\n\t<query name=\"getProfileMembersFromProfileIds\">\n\t\tSELECT pm\n\t\tFROM org.bonitasoft.engine.profile.model.SProfileMember AS pm\n\t\tWHERE pm.profileId IN (:profileIds)\n\t</query>\n\n    <query name=\"updateLastUpdateProfile\">\n        UPDATE org.bonitasoft.engine.profile.model.SProfile AS p\n        SET p.lastUpdateDate = :lastUpdateDate,\n        p.lastUpdatedBy = :lastUpdatedBy\n        WHERE p.id = :id\n    </query>\n\n</hibernate-mapping>\n"
  },
  {
    "path": "services/bonita-profile/src/test/java/org/bonitasoft/engine/profile/impl/ProfileServiceImplForProfileTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.profile.impl;\n\nimport static org.junit.Assert.*;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.nullable;\nimport static org.mockito.Mockito.*;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.builder.BuilderFactory;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.profile.builder.SProfileUpdateBuilder;\nimport org.bonitasoft.engine.profile.builder.SProfileUpdateBuilderFactory;\nimport org.bonitasoft.engine.profile.exception.profile.SProfileCreationException;\nimport org.bonitasoft.engine.profile.exception.profile.SProfileDeletionException;\nimport org.bonitasoft.engine.profile.exception.profile.SProfileNotFoundException;\nimport org.bonitasoft.engine.profile.exception.profile.SProfileUpdateException;\nimport org.bonitasoft.engine.profile.exception.profilemember.SProfileMemberDeletionException;\nimport org.bonitasoft.engine.profile.model.SProfile;\nimport org.bonitasoft.engine.profile.model.SProfileMember;\nimport org.bonitasoft.engine.profile.persistence.SelectDescriptorBuilder;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.bonitasoft.engine.services.PersistenceService;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.session.model.SSession;\nimport org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentMatchers;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Celine Souchet\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class ProfileServiceImplForProfileTest {\n\n    @Mock\n    private PersistenceService persistenceService;\n\n    @Mock\n    private QueriableLoggerService queriableLoggerService;\n\n    @Mock\n    private Recorder recorder;\n\n    @Mock\n    private SessionService sessionService;\n\n    @Mock\n    private ReadSessionAccessor readSessionAccessor;\n\n    @Mock\n    private SSession sSession;\n\n    @Mock\n    private SProfile sProfile;\n\n    @InjectMocks\n    private ProfileServiceImpl profileServiceImpl;\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.profile.impl.ProfileServiceImpl#getNumberOfProfiles(org.bonitasoft.engine.persistence.QueryOptions)}.\n     */\n    @Test\n    public void getNumberOfProfilesWithOptions() throws Exception {\n        final QueryOptions options = new QueryOptions(0, 10);\n        when(persistenceService.getNumberOfEntities(SProfile.class, options, Collections.emptyMap())).thenReturn(1L);\n\n        assertEquals(1L, profileServiceImpl.getNumberOfProfiles(options));\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getNumberOfProfilesWithOptionsThrowException() throws Exception {\n        final QueryOptions options = new QueryOptions(0, 10);\n        when(persistenceService.getNumberOfEntities(SProfile.class, options, Collections.emptyMap()))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        profileServiceImpl.getNumberOfProfiles(options);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.profile.impl.ProfileServiceImpl#searchProfiles(org.bonitasoft.engine.persistence.QueryOptions)}.\n     */\n    @Test\n    public void searchProfilesWithOptions() throws Exception {\n        final QueryOptions options = new QueryOptions(0, 10);\n        when(persistenceService.searchEntity(SProfile.class, options, Collections.emptyMap()))\n                .thenReturn(new ArrayList<>());\n\n        assertNotNull(profileServiceImpl.searchProfiles(options));\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void searchProfilesWithOptionsThrowException() throws Exception {\n        final QueryOptions options = new QueryOptions(0, 10);\n        when(persistenceService.searchEntity(SProfile.class, options, Collections.emptyMap()))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        profileServiceImpl.searchProfiles(options);\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.profile.impl.ProfileServiceImpl#getProfile(long)}.\n     */\n    @Test\n    public void getProfileById() throws Exception {\n        final SProfile sProfile = mock(SProfile.class);\n\n        doReturn(sProfile).when(persistenceService).selectById(ArgumentMatchers.<SelectByIdDescriptor<SProfile>> any());\n\n        assertEquals(sProfile, profileServiceImpl.getProfile(1));\n    }\n\n    @Test(expected = SProfileNotFoundException.class)\n    public void getNoProfileById() throws Exception {\n        when(persistenceService.selectById(ArgumentMatchers.<SelectByIdDescriptor<SProfile>> any())).thenReturn(null);\n\n        profileServiceImpl.getProfile(1);\n    }\n\n    @Test(expected = SProfileNotFoundException.class)\n    public void getProfileByIdThrowException() throws Exception {\n        when(persistenceService.selectById(ArgumentMatchers.<SelectByIdDescriptor<SProfile>> any()))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        profileServiceImpl.getProfile(1);\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.profile.impl.ProfileServiceImpl#getProfileByName(java.lang.String)}.\n     */\n    @Test\n    public void getProfileByName() throws Exception {\n        final String profileName = \"plop\";\n        final SProfile sProfile = mock(SProfile.class);\n\n        doReturn(sProfile).when(persistenceService).selectOne(ArgumentMatchers.<SelectOneDescriptor<SProfile>> any());\n\n        assertEquals(sProfile, profileServiceImpl.getProfileByName(profileName));\n    }\n\n    @Test(expected = SProfileNotFoundException.class)\n    public void getNoProfileByName() throws Exception {\n        when(persistenceService.selectOne(ArgumentMatchers.<SelectOneDescriptor<SProfile>> any())).thenReturn(null);\n\n        profileServiceImpl.getProfileByName(\"plop\");\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getProfileByNameThrowException() throws Exception {\n        when(persistenceService.selectOne(any())).thenThrow(new SBonitaReadException(\"\"));\n\n        profileServiceImpl.getProfileByName(\"plop\");\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.profile.impl.ProfileServiceImpl#getProfiles(java.util.List)}.\n     *\n     * @throws SProfileNotFoundException\n     * @throws SBonitaReadException\n     */\n    @Test\n    public final void getProfiles() throws SProfileNotFoundException, SBonitaReadException {\n        final List<Long> profileIds = new ArrayList<>();\n        profileIds.add(1L);\n        profileIds.add(2L);\n\n        final List<SProfile> sProfiles = new ArrayList<>();\n        final SProfile sProfile = mock(SProfile.class);\n        sProfiles.add(sProfile);\n        sProfiles.add(sProfile);\n\n        doReturn(sProfile).when(persistenceService).selectById(ArgumentMatchers.<SelectByIdDescriptor<SProfile>> any());\n\n        assertEquals(sProfiles, profileServiceImpl.getProfiles(profileIds));\n    }\n\n    @Test(expected = SProfileNotFoundException.class)\n    public final void getNoProfiles() throws SProfileNotFoundException, SBonitaReadException {\n        final List<Long> profileIds = new ArrayList<>();\n        profileIds.add(1L);\n\n        doReturn(null).when(persistenceService).selectById(ArgumentMatchers.<SelectByIdDescriptor<SProfile>> any());\n\n        profileServiceImpl.getProfiles(profileIds);\n    }\n\n    @Test\n    public final void getProfilesWithEmptyList() throws SProfileNotFoundException {\n        final List<Long> profileIds = new ArrayList<>();\n\n        assertTrue(profileServiceImpl.getProfiles(profileIds).isEmpty());\n    }\n\n    @Test\n    public final void getProfilesWithNullList() throws SProfileNotFoundException {\n        assertTrue(profileServiceImpl.getProfiles(null).isEmpty());\n    }\n\n    @Test\n    public final void getProfilesOfUser() throws SBonitaReadException {\n        final List<SProfile> sProfiles = new ArrayList<>();\n        final SProfile sProfile = mock(SProfile.class);\n        sProfiles.add(sProfile);\n\n        doReturn(sProfiles).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SProfile>> any());\n\n        assertEquals(sProfiles, profileServiceImpl.searchProfilesOfUser(1, 0, 10, \"name\", OrderByType.ASC));\n    }\n\n    @Test\n    public final void getNoProfilesOfUser() throws SBonitaReadException {\n        final List<SProfile> sProfiles = new ArrayList<>();\n\n        doReturn(sProfiles).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SProfile>> any());\n\n        assertEquals(sProfiles, profileServiceImpl.searchProfilesOfUser(1, 0, 10, \"name\", OrderByType.ASC));\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public final void getProfilesOfUserThrowException() throws SBonitaReadException {\n        doThrow(new SBonitaReadException(\"plop\")).when(persistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SProfile>> any());\n\n        profileServiceImpl.searchProfilesOfUser(1, 0, 10, \"name\", OrderByType.ASC);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.profile.impl.ProfileServiceImpl#createProfile(org.bonitasoft.engine.profile.model.SProfile)}.\n     *\n     * @throws SProfileCreationException\n     */\n    @Test\n    public final void createProfile() throws SProfileCreationException {\n        final SProfile sProfile = mock(SProfile.class);\n        doReturn(1L).when(sProfile).getId();\n\n        final SProfile result = profileServiceImpl.createProfile(sProfile);\n        assertNotNull(result);\n        assertEquals(sProfile, result);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public final void createNullProfile() throws Exception {\n        profileServiceImpl.createProfile(null);\n    }\n\n    @Test(expected = SProfileCreationException.class)\n    public final void createProfileThrowException() throws SRecorderException, SProfileCreationException {\n        final SProfile sProfile = mock(SProfile.class);\n        doReturn(1L).when(sProfile).getId();\n\n        doThrow(new SRecorderException(\"plop\")).when(recorder).recordInsert(any(InsertRecord.class),\n                nullable(String.class));\n\n        profileServiceImpl.createProfile(sProfile);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.profile.impl.ProfileServiceImpl#updateProfile(org.bonitasoft.engine.profile.model.SProfile, org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor)}\n     *\n     * @throws SBonitaReadException\n     * @throws SRecorderException\n     * @throws SProfileUpdateException\n     */\n    @Test\n    public final void updateProfile() throws SProfileUpdateException {\n        final SProfile sProfile = mock(SProfile.class);\n        doReturn(3L).when(sProfile).getId();\n        final SProfileUpdateBuilder sProfileUpdateBuilder = BuilderFactory.get(SProfileUpdateBuilderFactory.class)\n                .createNewInstance();\n        sProfileUpdateBuilder.setDescription(\"newDescription\").setName(\"newName\");\n\n        final SProfile result = profileServiceImpl.updateProfile(sProfile, sProfileUpdateBuilder.done());\n        assertNotNull(result);\n        assertEquals(sProfile, result);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public final void updateNullProfile() throws SProfileUpdateException {\n        final SProfileUpdateBuilder sProfileUpdateBuilder = BuilderFactory.get(SProfileUpdateBuilderFactory.class)\n                .createNewInstance();\n\n        profileServiceImpl.updateProfile(null, sProfileUpdateBuilder.done());\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public final void updateProfileWithNullDescriptor() throws SProfileUpdateException {\n        final SProfile sProfile = mock(SProfile.class);\n\n        profileServiceImpl.updateProfile(sProfile, null);\n    }\n\n    @Test(expected = SProfileUpdateException.class)\n    public final void updateProfileThrowException() throws SRecorderException, SProfileUpdateException {\n        final SProfile sProfile = mock(SProfile.class);\n        doReturn(3L).when(sProfile).getId();\n        final SProfileUpdateBuilder sProfileUpdateBuilder = BuilderFactory.get(SProfileUpdateBuilderFactory.class)\n                .createNewInstance();\n        sProfileUpdateBuilder.setDescription(\"newDescription\").setName(\"newName\");\n\n        doThrow(new SRecorderException(\"plop\")).when(recorder).recordUpdate(any(UpdateRecord.class),\n                nullable(String.class));\n\n        profileServiceImpl.updateProfile(sProfile, sProfileUpdateBuilder.done());\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.profile.impl.ProfileServiceImpl#deleteProfile(long)}.\n     */\n    @Test\n    public final void deleteProfileById() throws Exception {\n        final SProfile sProfile = mock(SProfile.class);\n        doReturn(3L).when(sProfile).getId();\n\n        final List<SProfileMember> sProfileMembers = new ArrayList<>();\n        final SProfileMember sProfileMember = mock(SProfileMember.class);\n        doReturn(4L).when(sProfileMember).getId();\n        sProfileMembers.add(sProfileMember);\n\n        doReturn(sProfile).when(persistenceService).selectById(ArgumentMatchers.<SelectByIdDescriptor<SProfile>> any());\n        final QueryOptions queryOptions = new QueryOptions(0, 10, SProfile.class, \"id\", OrderByType.ASC);\n        doReturn(sProfileMembers).doReturn(new ArrayList<SProfileMember>()).when(persistenceService)\n                .selectList(SelectDescriptorBuilder.getSProfileMembersWithoutDisplayName(3L, queryOptions));\n\n        profileServiceImpl.deleteProfile(1L);\n    }\n\n    @Test(expected = SProfileNotFoundException.class)\n    public final void deleteNoProfileById() throws SBonitaReadException, SProfileDeletionException,\n            SProfileNotFoundException,\n            SProfileMemberDeletionException {\n        when(persistenceService.selectById(ArgumentMatchers.<SelectByIdDescriptor<SProfile>> any())).thenReturn(null);\n\n        profileServiceImpl.deleteProfile(1);\n    }\n\n    @Test(expected = SProfileDeletionException.class)\n    public void deleteProfileByIdThrowException() throws Exception {\n        final SProfile sProfile = mock(SProfile.class);\n        doReturn(3L).when(sProfile).getId();\n\n        doReturn(sProfile).when(persistenceService).selectById(any());\n        doThrow(new SRecorderException(\"\")).when(recorder).recordDelete(any(DeleteRecord.class),\n                nullable(String.class));\n\n        profileServiceImpl.deleteProfile(1);\n    }\n\n    @Test\n    public final void deleteProfileByIdWithNoMember()\n            throws SProfileNotFoundException, SProfileDeletionException, SBonitaReadException,\n            SProfileMemberDeletionException {\n        final SProfile sProfile = mock(SProfile.class);\n        doReturn(3L).when(sProfile).getId();\n\n        doReturn(sProfile).when(persistenceService).selectById(ArgumentMatchers.<SelectByIdDescriptor<SProfile>> any());\n        final QueryOptions queryOptions = new QueryOptions(0, 10, SProfile.class, \"id\", OrderByType.ASC);\n        doReturn(new ArrayList<SProfileMember>()).when(persistenceService).selectList(\n                SelectDescriptorBuilder.getSProfileMembersWithoutDisplayName(3, queryOptions));\n\n        profileServiceImpl.deleteProfile(1);\n    }\n\n    /**\n     * Test method for\n     * {@link org.bonitasoft.engine.profile.impl.ProfileServiceImpl#deleteProfile(org.bonitasoft.engine.profile.model.SProfile)}.\n     */\n    @Test\n    public final void deleteProfileByObject() throws Exception {\n        final SProfile sProfile = mock(SProfile.class);\n        doReturn(3L).when(sProfile).getId();\n\n        final List<SProfileMember> sProfileMembers = new ArrayList<>();\n        final SProfileMember sProfileMember = mock(SProfileMember.class);\n        doReturn(4L).when(sProfileMember).getId();\n        sProfileMembers.add(sProfileMember);\n\n        final QueryOptions queryOptions = new QueryOptions(0, 10, SProfile.class, \"id\", OrderByType.ASC);\n        doReturn(sProfileMembers).doReturn(new ArrayList<SProfileMember>()).when(persistenceService)\n                .selectList(SelectDescriptorBuilder.getSProfileMembersWithoutDisplayName(3, queryOptions));\n\n        profileServiceImpl.deleteProfile(sProfile);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public final void deleteNoProfileByObject() throws Exception {\n        profileServiceImpl.deleteProfile(null);\n    }\n\n    @Test(expected = SProfileDeletionException.class)\n    public void deleteProfileByObjectThrowException() throws Exception {\n        doReturn(3L).when(sProfile).getId();\n        doThrow(new SRecorderException(\"\")).when(recorder).recordDelete(any(DeleteRecord.class),\n                nullable(String.class));\n\n        profileServiceImpl.deleteProfile(sProfile);\n    }\n\n    @Test\n    public final void deleteProfileByObjectWithNoMember() throws Exception {\n        doReturn(3L).when(sProfile).getId();\n\n        final QueryOptions queryOptions = new QueryOptions(0, 10, SProfile.class, \"id\", OrderByType.ASC);\n        doReturn(new ArrayList<SProfileMember>()).when(persistenceService).selectList(\n                SelectDescriptorBuilder.getSProfileMembersWithoutDisplayName(3L, queryOptions));\n\n        profileServiceImpl.deleteProfile(sProfile);\n    }\n}\n"
  },
  {
    "path": "services/bonita-resources/build.gradle",
    "content": "\n\ndependencies {\n    api project(':services:bonita-persistence')\n    api project(':services:bonita-commons')\n    testImplementation libs.mockitoCore\n    testImplementation libs.systemRules\n    testImplementation libs.assertj\n    testRuntimeOnly libs.logback\n    annotationProcessor libs.lombok\n    compileOnly libs.lombok\n\n}\n"
  },
  {
    "path": "services/bonita-resources/src/main/java/org/bonitasoft/engine/resources/AbstractSBARResource.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.resources;\n\nimport javax.persistence.Column;\nimport javax.persistence.EnumType;\nimport javax.persistence.Enumerated;\nimport javax.persistence.Id;\nimport javax.persistence.MappedSuperclass;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Baptiste Mesta\n */\n@Data\n@NoArgsConstructor\n@MappedSuperclass\npublic class AbstractSBARResource implements PersistentObject {\n\n    @Id\n    private long id;\n\n    protected String name;\n\n    @Enumerated(EnumType.STRING)\n    protected BARResourceType type;\n\n    @Column(name = \"process_id\")\n    protected long processDefinitionId;\n}\n"
  },
  {
    "path": "services/bonita-resources/src/main/java/org/bonitasoft/engine/resources/AbstractSTenantResource.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.resources;\n\nimport javax.persistence.EnumType;\nimport javax.persistence.Enumerated;\nimport javax.persistence.Id;\nimport javax.persistence.MappedSuperclass;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Baptiste Mesta\n */\n@Data\n@NoArgsConstructor\n@MappedSuperclass\npublic class AbstractSTenantResource implements PersistentObject {\n\n    @Id\n    private long id;\n\n    protected String name;\n    protected long lastUpdatedBy;\n    protected long lastUpdateDate;\n\n    @Enumerated(EnumType.STRING)\n    protected TenantResourceType type;\n\n    @Enumerated(EnumType.STRING)\n    protected STenantResourceState state;\n\n    public AbstractSTenantResource(String name, TenantResourceType type, long lastUpdatedBy, long lastUpdateDate,\n            STenantResourceState state) {\n        this.name = name;\n        this.type = type;\n        this.lastUpdatedBy = lastUpdatedBy;\n        this.lastUpdateDate = lastUpdateDate;\n        this.state = state;\n    }\n}\n"
  },
  {
    "path": "services/bonita-resources/src/main/java/org/bonitasoft/engine/resources/BARResourceType.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.resources;\n\n/**\n * @author Baptiste Mesta\n */\npublic enum BARResourceType {\n    DOCUMENT, EXTERNAL, CONNECTOR, USER_FILTER\n}\n"
  },
  {
    "path": "services/bonita-resources/src/main/java/org/bonitasoft/engine/resources/ProcessResourcesService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.resources;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.SRecorderException;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface ProcessResourcesService {\n\n    String BAR_RESOURCE = \"BAR_RESOURCE\";\n\n    void add(long processDefinitionId, String name, BARResourceType type, byte[] content) throws SRecorderException;\n\n    void update(AbstractSBARResource resource, byte[] content) throws SRecorderException;\n\n    void removeAll(long processDefinitionId, BARResourceType external) throws SBonitaReadException, SRecorderException;\n\n    List<SBARResource> get(long processDefinitionId, BARResourceType type, int from, int numberOfElements)\n            throws SBonitaReadException;\n\n    long count(long processDefinitionId, BARResourceType type) throws SBonitaReadException;\n\n    SBARResource get(long processDefinitionId, BARResourceType type, String name) throws SBonitaReadException;\n\n    void remove(AbstractSBARResource resource) throws SRecorderException;\n}\n"
  },
  {
    "path": "services/bonita-resources/src/main/java/org/bonitasoft/engine/resources/ProcessResourcesServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.resources;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\n\n/**\n * @author Baptiste Mesta\n */\npublic class ProcessResourcesServiceImpl implements ProcessResourcesService {\n\n    private final Recorder recorder;\n    private final ReadPersistenceService persistenceService;\n\n    public ProcessResourcesServiceImpl(Recorder recorder, ReadPersistenceService persistenceService) {\n        this.recorder = recorder;\n        this.persistenceService = persistenceService;\n    }\n\n    @Override\n    public void add(long processDefinitionId, String name, BARResourceType type, byte[] content)\n            throws SRecorderException {\n        SBARResource resource = new SBARResource(name, type, processDefinitionId, content);\n        recorder.recordInsert(new InsertRecord(resource), BAR_RESOURCE);\n    }\n\n    @Override\n    public void update(AbstractSBARResource resource, byte[] content) throws SRecorderException {\n        recorder.recordUpdate(UpdateRecord.buildSetFields(resource, Collections.singletonMap(\"content\", content)),\n                BAR_RESOURCE);\n    }\n\n    @Override\n    public void removeAll(long processDefinitionId, BARResourceType type)\n            throws SBonitaReadException, SRecorderException {\n        List<SBARResourceLight> resources;\n        while (!(resources = getLight(processDefinitionId, type, 0, 100)).isEmpty()) {\n            for (SBARResourceLight resource : resources) {\n                remove(resource);\n            }\n        }\n    }\n\n    public List<SBARResourceLight> getLight(long processDefinitionId, BARResourceType type, int from,\n            int numberOfElements) throws SBonitaReadException {\n        Map<String, Object> inputParameters = new HashMap<>(2);\n        inputParameters.put(\"processDefinitionId\", processDefinitionId);\n        inputParameters.put(\"type\", type);\n        return persistenceService.selectList(new SelectListDescriptor<SBARResourceLight>(\"getBARResourcesLightOfType\",\n                inputParameters, SBARResourceLight.class,\n                new QueryOptions(from, numberOfElements)));\n    }\n\n    @Override\n    public List<SBARResource> get(long processDefinitionId, BARResourceType type, int from, int numberOfElements)\n            throws SBonitaReadException {\n        Map<String, Object> inputParameters = new HashMap<>(2);\n        inputParameters.put(\"processDefinitionId\", processDefinitionId);\n        inputParameters.put(\"type\", type);\n        return persistenceService.selectList(\n                new SelectListDescriptor<SBARResource>(\"getBARResourcesOfType\", inputParameters, SBARResource.class,\n                        new QueryOptions(from, numberOfElements)));\n    }\n\n    @Override\n    public long count(long processDefinitionId, BARResourceType type) throws SBonitaReadException {\n        Map<String, Object> inputParameters = new HashMap<>(2);\n        inputParameters.put(\"processDefinitionId\", processDefinitionId);\n        inputParameters.put(\"type\", type);\n        return persistenceService.selectOne(\n                new SelectOneDescriptor<Long>(\"getNumberOfBARResourcesOfType\", inputParameters, SBARResource.class));\n    }\n\n    @Override\n    public SBARResource get(long processDefinitionId, BARResourceType type, String name) throws SBonitaReadException {\n        Map<String, Object> inputParameters = new HashMap<>(2);\n        inputParameters.put(\"processDefinitionId\", processDefinitionId);\n        inputParameters.put(\"type\", type);\n        inputParameters.put(\"name\", name);\n        return persistenceService.selectOne(\n                new SelectOneDescriptor<SBARResource>(\"getBARResource\", inputParameters, SBARResource.class));\n    }\n\n    @Override\n    public void remove(AbstractSBARResource resource) throws SRecorderException {\n        recorder.recordDelete(new DeleteRecord(resource), BAR_RESOURCE);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-resources/src/main/java/org/bonitasoft/engine/resources/SBARResource.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.resources;\n\nimport javax.persistence.Entity;\nimport javax.persistence.Table;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.ToString;\nimport org.hibernate.annotations.Type;\n\n/**\n * @author Baptiste Mesta\n */\n@Data\n@ToString(callSuper = true, exclude = { \"content\" })\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@Table(name = \"bar_resource\")\npublic class SBARResource extends AbstractSBARResource {\n\n    @Type(type = \"materialized_blob\")\n    private byte[] content;\n\n    public SBARResource(String name, BARResourceType type, long processDefinitionId, byte[] content) {\n        this.name = name;\n        this.type = type;\n        this.processDefinitionId = processDefinitionId;\n        this.content = content;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-resources/src/main/java/org/bonitasoft/engine/resources/SBARResourceLight.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.resources;\n\nimport javax.persistence.Entity;\nimport javax.persistence.Table;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.ToString;\n\n/**\n * @author Baptiste Mesta\n */\n@Data\n@ToString(callSuper = true)\n@EqualsAndHashCode(callSuper = true)\n@NoArgsConstructor\n@Entity\n@Table(name = \"bar_resource\")\npublic class SBARResourceLight extends AbstractSBARResource {\n}\n"
  },
  {
    "path": "services/bonita-resources/src/main/java/org/bonitasoft/engine/resources/STenantResource.java",
    "content": "/**\n * Copyright (C) 2016 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.resources;\n\nimport javax.persistence.Entity;\nimport javax.persistence.Table;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.ToString;\nimport org.hibernate.annotations.Type;\n\n/**\n * @author Baptiste Mesta\n */\n@Data\n@ToString(callSuper = true, exclude = { \"content\" })\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\n@Entity\n@Table(name = \"tenant_resource\")\npublic class STenantResource extends AbstractSTenantResource {\n\n    @Type(type = \"materialized_blob\")\n    private byte[] content;\n\n    public STenantResource(String name, TenantResourceType type, byte[] content, long lastUpdatedBy,\n            long lastUpdateDate, STenantResourceState state) {\n        super(name, type, lastUpdatedBy, lastUpdateDate, state);\n        this.content = content;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-resources/src/main/java/org/bonitasoft/engine/resources/STenantResourceLight.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.resources;\n\nimport javax.persistence.Entity;\nimport javax.persistence.Table;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\n\n/**\n * @author Baptiste Mesta\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\n@NoArgsConstructor\n@Entity\n@Table(name = \"tenant_resource\")\npublic class STenantResourceLight extends AbstractSTenantResource {\n\n    public STenantResourceLight(String name, TenantResourceType type, long lastUpdatedBy, long lastUpdateDate,\n            STenantResourceState state) {\n        super(name, type, lastUpdatedBy, lastUpdateDate, state);\n    }\n}\n"
  },
  {
    "path": "services/bonita-resources/src/main/java/org/bonitasoft/engine/resources/STenantResourceState.java",
    "content": "/**\n * Copyright (C) 2017-2018 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.resources;\n\n/**\n * @author Danila Mazour\n */\npublic enum STenantResourceState {\n    INSTALLED, INSTALLING\n}\n"
  },
  {
    "path": "services/bonita-resources/src/main/java/org/bonitasoft/engine/resources/TenantResourceType.java",
    "content": "/**\n * Copyright (C) 2016-2018 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.resources;\n\n/**\n * @author Baptiste Mesta\n */\npublic enum TenantResourceType {\n    //max 16 chars (DB)\n    BDM, BDM_ACCESS_CTRL\n\n}\n"
  },
  {
    "path": "services/bonita-resources/src/main/java/org/bonitasoft/engine/resources/TenantResourcesService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.resources;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.SRecorderException;\n\n/**\n * @author Baptiste Mesta\n */\npublic interface TenantResourcesService {\n\n    String TENANT_RESOURCE = \"TENANT_RESOURCE\";\n\n    void add(String name, TenantResourceType type, byte[] content, long userId) throws SRecorderException;\n\n    void removeAll(TenantResourceType external) throws SBonitaReadException, SRecorderException;\n\n    List<STenantResource> get(TenantResourceType type, int from, int numberOfElements) throws SBonitaReadException;\n\n    long count(TenantResourceType type) throws SBonitaReadException;\n\n    long count(TenantResourceType type, String name) throws SBonitaReadException;\n\n    STenantResource get(TenantResourceType type, String name) throws SBonitaReadException;\n\n    /**\n     * Returns a single STenantResourceLight of the given type. This is the responsibility of the caller to only call\n     * this method when he / she is sure that there are not more than one result of the query.\n     * If the result is non unique, a SBonitaReadException is thrown.\n     *\n     * @param type the type of the resource to filter\n     * @return the found resource if unique, null if none found\n     * @throws SBonitaReadException if non unique result, or other Hibernate exception is issued\n     */\n    STenantResourceLight getSingleLightResource(TenantResourceType type) throws SBonitaReadException;\n\n    void remove(AbstractSTenantResource resource) throws SRecorderException;\n}\n"
  },
  {
    "path": "services/bonita-resources/src/main/java/org/bonitasoft/engine/resources/TenantResourcesServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.resources;\n\nimport static org.bonitasoft.engine.resources.STenantResourceState.INSTALLED;\n\nimport java.time.Instant;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.persistence.SelectOneDescriptor;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\n\n/**\n * @author Baptiste Mesta\n */\n@Slf4j\npublic class TenantResourcesServiceImpl implements TenantResourcesService {\n\n    private final Recorder recorder;\n    private final ReadPersistenceService persistenceService;\n\n    public TenantResourcesServiceImpl(Recorder recorder, ReadPersistenceService persistenceService) {\n        this.recorder = recorder;\n        this.persistenceService = persistenceService;\n    }\n\n    @Override\n    public void add(String name, TenantResourceType type, byte[] content, long userId) throws SRecorderException {\n        if (content != null && content.length > 0) {\n            STenantResource resource = new STenantResource(name, type, content, userId, Instant.now().toEpochMilli(),\n                    INSTALLED);\n            recorder.recordInsert(new InsertRecord(resource), TENANT_RESOURCE);\n        } else {\n            log.warn(\n                    \"Tenant resource file contains an empty file {} that will be ignored. Check that this is not a mistake.\",\n                    name);\n        }\n    }\n\n    @Override\n    public void removeAll(TenantResourceType type) throws SBonitaReadException, SRecorderException {\n        List<STenantResourceLight> resources;\n        while (!(resources = getLight(type, 0, 100)).isEmpty()) {\n            for (STenantResourceLight resource : resources) {\n                remove(resource);\n            }\n        }\n    }\n\n    public List<STenantResourceLight> getLight(TenantResourceType type, int from, int numberOfElements)\n            throws SBonitaReadException {\n        Map<String, Object> inputParameters = new HashMap<>();\n        inputParameters.put(\"type\", type);\n        return persistenceService\n                .selectList(new SelectListDescriptor<>(\"getTenantResourcesLightOfType\",\n                        inputParameters, STenantResourceLight.class,\n                        new QueryOptions(from, numberOfElements)));\n    }\n\n    @Override\n    public List<STenantResource> get(TenantResourceType type, int from, int numberOfElements)\n            throws SBonitaReadException {\n        Map<String, Object> inputParameters = new HashMap<>();\n        inputParameters.put(\"type\", type);\n        return persistenceService.selectList(\n                new SelectListDescriptor<>(\"getTenantResourcesOfType\", inputParameters,\n                        STenantResource.class,\n                        new QueryOptions(from, numberOfElements)));\n    }\n\n    @Override\n    public long count(TenantResourceType type) throws SBonitaReadException {\n        Map<String, Object> inputParameters = new HashMap<>();\n        inputParameters.put(\"type\", type);\n        return persistenceService.selectOne(new SelectOneDescriptor<>(\"getNumberOfTenantResourcesOfType\",\n                inputParameters, STenantResource.class));\n    }\n\n    @Override\n    public long count(TenantResourceType type, String name) throws SBonitaReadException {\n        Map<String, Object> inputParameters = new HashMap<>();\n        inputParameters.put(\"type\", type);\n        inputParameters.put(\"name\", name);\n        return persistenceService.selectOne(new SelectOneDescriptor<>(\"getNumberOfTenantResourcesOfTypeAndName\",\n                inputParameters, STenantResource.class));\n    }\n\n    @Override\n    public STenantResource get(TenantResourceType type, String name) throws SBonitaReadException {\n        Map<String, Object> inputParameters = new HashMap<>();\n        inputParameters.put(\"type\", type);\n        inputParameters.put(\"name\", name);\n        return persistenceService.selectOne(\n                new SelectOneDescriptor<>(\"getTenantResource\", inputParameters, STenantResource.class));\n    }\n\n    @Override\n    public STenantResourceLight getSingleLightResource(TenantResourceType type) throws SBonitaReadException {\n        Map<String, Object> inputParameters = new HashMap<>();\n        inputParameters.put(\"type\", type);\n        return persistenceService.selectOne(new SelectOneDescriptor<>(\n                \"getTenantResourcesLightOfType\", inputParameters, STenantResourceLight.class));\n    }\n\n    @Override\n    public void remove(AbstractSTenantResource resource) throws SRecorderException {\n        recorder.recordDelete(new DeleteRecord(resource), TENANT_RESOURCE);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-resources/src/main/resources/org/bonitasoft/engine/resources/hibernate/resources.queries.hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\" \"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd\">\n<hibernate-mapping auto-import=\"false\">\n\n    <query name=\"getBARResourcesLightOfType\">\n        SELECT bar\n        FROM org.bonitasoft.engine.resources.SBARResourceLight AS bar\n        WHERE bar.processDefinitionId= :processDefinitionId\n        AND bar.type = :type\n    </query>\n    <query name=\"getBARResourcesOfType\">\n        SELECT bar\n        FROM org.bonitasoft.engine.resources.SBARResource AS bar\n        WHERE bar.processDefinitionId= :processDefinitionId\n        AND bar.type = :type\n    </query>\n    <query name=\"getNumberOfBARResourcesOfType\">\n        SELECT count(bar.id)\n        FROM org.bonitasoft.engine.resources.SBARResource AS bar\n        WHERE bar.processDefinitionId= :processDefinitionId\n        AND bar.type = :type\n    </query>\n\n    <query name=\"getBARResource\">\n        SELECT bar\n        FROM org.bonitasoft.engine.resources.SBARResource AS bar\n        WHERE bar.processDefinitionId= :processDefinitionId\n        AND bar.type = :type\n        AND bar.name = :name\n    </query>\n\n    <query name=\"getTenantResourcesLightOfType\">\n        SELECT r\n        FROM org.bonitasoft.engine.resources.STenantResourceLight AS r\n        WHERE r.type = :type\n    </query>\n    <query name=\"getTenantResourcesOfType\">\n        SELECT r\n        FROM org.bonitasoft.engine.resources.STenantResource AS r\n        WHERE r.type = :type\n    </query>\n    <query name=\"getNumberOfTenantResourcesOfType\">\n        SELECT count(r.id)\n        FROM org.bonitasoft.engine.resources.STenantResource AS r\n        WHERE r.type = :type\n    </query>\n    <query name=\"getNumberOfTenantResourcesOfTypeAndName\">\n        SELECT count(r.id)\n        FROM org.bonitasoft.engine.resources.STenantResource AS r\n        WHERE r.type = :type\n        AND r.name = :name\n    </query>\n\n    <query name=\"getTenantResource\">\n        SELECT r\n        FROM org.bonitasoft.engine.resources.STenantResource AS r\n        WHERE r.type = :type\n        AND r.name = :name\n    </query>\n\n</hibernate-mapping>\n"
  },
  {
    "path": "services/bonita-resources/src/test/java/org/bonitasoft/engine/resources/TenantResourcesServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.resources;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.SystemOutRule;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Emmanuel Duchastenier\n */\n@RunWith(MockitoJUnitRunner.Silent.class)\npublic class TenantResourcesServiceImplTest {\n\n    @Rule\n    public final SystemOutRule systemOutRule = new SystemOutRule().enableLog().muteForSuccessfulTests();\n    @Mock\n    Recorder recorder;\n    @Mock\n    ReadPersistenceService persistenceService;\n\n    @InjectMocks\n    TenantResourcesServiceImpl tenantResourcesService;\n\n    @Before\n    public void initMocks() throws Exception {\n        doNothing().when(recorder).recordInsert(any(InsertRecord.class), anyString());\n    }\n\n    @Test\n    public void add_should_log_message_and_ignore_null_file_content() throws Exception {\n        // when\n        tenantResourcesService.add(\"resourceName\", TenantResourceType.BDM, null, -1);\n\n        // then\n        verifyNoInteractions(recorder);\n    }\n\n    @Test\n    public void add_should_log_message_and_ignore_empty_file_content() throws Exception {\n        // when\n        tenantResourcesService.add(\"resourceName\", TenantResourceType.BDM, new byte[] {}, -1);\n\n        // then\n        verifyNoInteractions(recorder);\n    }\n\n    @Test\n    public void add_should_work_for_valid_file_content() throws Exception {\n        // when\n        systemOutRule.clearLog();\n        tenantResourcesService.add(\"resourceName\", TenantResourceType.BDM, \"someValidContent\".getBytes(), -1);\n\n        // then\n        verify(recorder).recordInsert(any(InsertRecord.class), nullable(String.class));\n        assertThat(systemOutRule.getLog()).isEmpty();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-resources/src/test/resources/logback-test.xml",
    "content": "<configuration>\n\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->\n        <encoder>\n            <pattern>|%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <logger name=\"org.bonitasoft.engine.resources.TenantResourcesServiceImpl\" level=\"TRACE\" />\n\n    <root level=\"INFO\">\n        <appender-ref ref=\"STDOUT\" />\n    </root>\n\n</configuration>\n"
  },
  {
    "path": "services/bonita-scheduler/build.gradle",
    "content": "dependencies {\n    api project(':services:bonita-session')\n    api project(':services:bonita-log')\n    api project(':services:bonita-builder')\n    api project(':services:bonita-events')\n    api project(':services:bonita-persistence')\n    api project(':services:bonita-commons')\n    api project(':services:bonita-transaction')\n    api project(':services:bonita-incident')\n    api(libs.quartz) {\n        exclude(group: \"com.mchange\") // c3p0 + transitive dep mchange-commons-java, because we do not use\n        // default Quartz connection provider, as we have our own\n        exclude(module: \"HikariCP-java7\") // Same reason\n    }\n    api libs.micrometerCore\n    testImplementation libs.assertj\n    testImplementation libs.mockitoCore\n    testImplementation libs.logback\n\n    annotationProcessor libs.lombok\n    compileOnly libs.lombok\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/BonitaJobListener.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\npublic interface BonitaJobListener extends Serializable {\n\n    String BOS_JOB = \"bosJob\";\n\n    String JOB_DESCRIPTOR_ID = \"jobDescriptorId\";\n\n    String JOB_TYPE = \"jobType\";\n\n    String JOB_NAME = \"jobName\";\n\n    String JOB_GROUP = \"jobGroup\";\n\n    String TRIGGER_NAME = \"triggerName\";\n\n    String TRIGGER_GROUP = \"triggerGroup\";\n\n    String TRIGGER_PREVIOUS_FIRE_TIME = \"triggerPreviousFireTime\";\n\n    String TRIGGER_NEXT_FIRE_TIME = \"triggerNextFireTime\";\n\n    String REFIRE_COUNT = \"refireCount\";\n\n    String JOB_DATAS = \"jobDatas\";\n\n    String JOB_RESULT = \"jobResult\";\n\n    void jobToBeExecuted();\n\n    void jobExecutionVetoed();\n\n    void jobWasExecuted(Map<String, Serializable> context, Exception jobException);\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/JobChecker.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic interface JobChecker {\n\n    boolean isLoggable(StatelessJob job);\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/JobIdentifier.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler;\n\nimport java.io.Serializable;\n\nimport lombok.EqualsAndHashCode;\nimport lombok.Getter;\n\n/**\n * @author Baptiste Mesta\n */\n@Getter\n@EqualsAndHashCode\npublic class JobIdentifier implements Serializable {\n\n    private static final long serialVersionUID = 5950749851853932753L;\n\n    private final long id;\n\n    private final String jobName;\n\n    public JobIdentifier(final long jobId, final String jobName) {\n        id = jobId;\n        this.jobName = jobName;\n    }\n\n    @Override\n    public String toString() {\n        return \"jobName=\" + jobName + \";jobId=\" + id;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/JobService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler;\n\nimport java.util.List;\n\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.scheduler.exception.failedJob.SFailedJobReadException;\nimport org.bonitasoft.engine.scheduler.exception.jobDescriptor.SJobDescriptorCreationException;\nimport org.bonitasoft.engine.scheduler.exception.jobDescriptor.SJobDescriptorDeletionException;\nimport org.bonitasoft.engine.scheduler.exception.jobDescriptor.SJobDescriptorNotFoundException;\nimport org.bonitasoft.engine.scheduler.exception.jobDescriptor.SJobDescriptorReadException;\nimport org.bonitasoft.engine.scheduler.exception.jobLog.SJobLogCreationException;\nimport org.bonitasoft.engine.scheduler.exception.jobLog.SJobLogDeletionException;\nimport org.bonitasoft.engine.scheduler.exception.jobLog.SJobLogUpdatingException;\nimport org.bonitasoft.engine.scheduler.exception.jobParameter.SJobParameterCreationException;\nimport org.bonitasoft.engine.scheduler.exception.jobParameter.SJobParameterDeletionException;\nimport org.bonitasoft.engine.scheduler.exception.jobParameter.SJobParameterNotFoundException;\nimport org.bonitasoft.engine.scheduler.exception.jobParameter.SJobParameterReadException;\nimport org.bonitasoft.engine.scheduler.model.SFailedJob;\nimport org.bonitasoft.engine.scheduler.model.SJobDescriptor;\nimport org.bonitasoft.engine.scheduler.model.SJobLog;\nimport org.bonitasoft.engine.scheduler.model.SJobParameter;\n\n/**\n * @author Celine Souchet\n * @since 6.1\n */\npublic interface JobService {\n\n    String JOB_DESCRIPTOR = \"JOB_DESCRIPTOR\";\n\n    String JOB_PARAMETER = \"JOB_PARAMETER\";\n\n    String JOB_LOG = \"JOB_LOG\";\n\n    /**\n     * Create a new job descriptor\n     *\n     * @param sJobDescriptor JobDescriptor to create\n     * @return The created jobDescriptor\n     * @throws SJobDescriptorCreationException\n     * @since 6.1\n     */\n    SJobDescriptor createJobDescriptor(SJobDescriptor sJobDescriptor)\n            throws SJobDescriptorCreationException;\n\n    /**\n     * Delete the specified job descriptor\n     *\n     * @param id Identifier of job descriptor to delete\n     * @throws SJobDescriptorReadException\n     * @throws SJobDescriptorNotFoundException\n     * @throws SJobDescriptorDeletionException\n     * @since 6.1\n     */\n    void deleteJobDescriptor(long id)\n            throws SJobDescriptorNotFoundException, SJobDescriptorReadException, SJobDescriptorDeletionException;\n\n    /**\n     * Delete the specified job descriptor\n     *\n     * @param sJobDescriptor JobDescriptor to delete\n     * @throws SJobDescriptorDeletionException\n     * @since 6.1\n     */\n    void deleteJobDescriptor(SJobDescriptor sJobDescriptor) throws SJobDescriptorDeletionException;\n\n    /**\n     * Delete all job descriptors\n     *\n     * @throws SJobDescriptorDeletionException\n     * @since 6.4\n     */\n    void deleteAllJobDescriptors() throws SJobDescriptorDeletionException;\n\n    /**\n     * Delete a job descriptor corresponding to the given job name\n     *\n     * @param jobName name of job we want the jobDsecriptor to be deleted\n     * @since 6.3\n     */\n    void deleteJobDescriptorByJobName(String jobName) throws SJobDescriptorDeletionException;\n\n    /**\n     * Get a specific job descriptor\n     *\n     * @param id Identifier of job descriptor\n     * @return Null if the job descriptor doesn't exist, else the {@link SJobDescriptor} corresponding to the identifier\n     * @throws SJobDescriptorReadException\n     * @since 6.1\n     */\n    SJobDescriptor getJobDescriptor(long id) throws SJobDescriptorReadException;\n\n    /**\n     * Get total number of job descriptors\n     *\n     * @param queryOptions a map of specific parameters of a query\n     * @return total number of job logs\n     * @throws SBonitaReadException\n     */\n    long getNumberOfJobDescriptors(QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * Search all job descriptors according to specific criteria\n     *\n     * @param queryOptions a map of specific parameters of a query\n     * @return A list of SJobParameter objects\n     * @throws SBonitaReadException\n     * @since 6.1\n     */\n    List<SJobDescriptor> searchJobDescriptors(QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * Create new job parameters\n     *\n     * @param parameters JobParameters to create\n     * @param jobDescriptorId Identifier of job descriptor\n     * @return\n     * @throws SJobParameterCreationException\n     * @since 6.2\n     */\n    List<SJobParameter> createJobParameters(List<SJobParameter> parameters, long jobDescriptorId)\n            throws SJobParameterCreationException;\n\n    /**\n     * Delete jobs parameters corresponding to the job descriptor, if exist. After, create new job parameters\n     *\n     * @param jobDescriptorId\n     * @param parameters\n     * @return A list of new SJobParameter objects\n     * @throws SJobParameterCreationException\n     * @since 6.1\n     */\n    List<SJobParameter> setJobParameters(long jobDescriptorId, List<SJobParameter> parameters)\n            throws SJobParameterCreationException;\n\n    /**\n     * Create a new job parameter\n     *\n     * @param sJobParameter JobParameter to create\n     * @param jobDescriptorId Identifier of job descriptor\n     * @return\n     * @throws SJobParameterCreationException\n     * @since 6.2\n     */\n    SJobParameter createJobParameter(SJobParameter sJobParameter, long jobDescriptorId)\n            throws SJobParameterCreationException;\n\n    /**\n     * Delete the specified job parameter\n     *\n     * @param id Identifier of job parameter to delete\n     * @throws SJobParameterReadException\n     * @throws SJobParameterNotFoundException\n     * @throws SJobParameterDeletionException\n     * @since 6.1\n     */\n    void deleteJobParameter(long id)\n            throws SJobParameterNotFoundException, SJobParameterReadException, SJobParameterDeletionException;\n\n    /**\n     * Delete the specified job parameter\n     *\n     * @param sJobParameter JobParameter to delete\n     * @throws SJobParameterDeletionException\n     * @since 6.1\n     */\n    void deleteJobParameter(SJobParameter sJobParameter) throws SJobParameterDeletionException;\n\n    /**\n     * Get a specific job parameter\n     *\n     * @param id Identifier of job parameter\n     * @return\n     * @throws SJobParameterReadException\n     * @throws SJobParameterNotFoundException\n     * @since 6.1\n     */\n    SJobParameter getJobParameter(long id) throws SJobParameterNotFoundException, SJobParameterReadException;\n\n    /**\n     * get parameters of a job\n     */\n    List<SJobParameter> getJobParameters(Long jobDescriptorId) throws SBonitaReadException;\n\n    /**\n     * Create a new job log\n     *\n     * @param sJobLog JobLog to create\n     * @return\n     * @throws SJobLogCreationException\n     * @since 6.2\n     */\n    SJobLog createJobLog(SJobLog sJobLog) throws SJobLogCreationException;\n\n    /**\n     * Delete the specified job log\n     *\n     * @param id Identifier of job log to delete\n     * @throws SBonitaReadException\n     * @throws SJobLogDeletionException\n     * @since 6.1\n     */\n    void deleteJobLog(long id) throws SJobLogDeletionException, SBonitaReadException;\n\n    /**\n     * Delete the specified job log\n     *\n     * @param sJobLog JobLog to delete\n     * @throws SJobLogDeletionException\n     * @since 6.1\n     */\n    void deleteJobLog(SJobLog sJobLog) throws SJobLogDeletionException;\n\n    /**\n     * Get a specific job log\n     *\n     * @param id Identifier of job log\n     * @return\n     * @throws SBonitaReadException\n     * @since 6.1\n     */\n    SJobLog getJobLog(long id) throws SBonitaReadException;\n\n    /**\n     * Get total number of job logs\n     *\n     * @param queryOptions a map of specific parameters of a query\n     * @return total number of job logs\n     * @throws SBonitaReadException\n     */\n    long getNumberOfJobLogs(QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * Search all job logs according to specific criteria\n     *\n     * @param queryOptions a map of specific parameters of a query\n     * @return A list of SJobLog objects\n     * @throws SBonitaReadException\n     * @since 6.1\n     */\n    List<SJobLog> searchJobLogs(QueryOptions queryOptions) throws SBonitaReadException;\n\n    /**\n     * Get list of failed jobs\n     *\n     * @param startIndex\n     * @param maxResults\n     * @return A list of SFailedJob objects\n     * @throws SFailedJobReadException\n     * @since 6.2\n     */\n    List<SFailedJob> getFailedJobs(int startIndex, int maxResults) throws SFailedJobReadException;\n\n    /**\n     * Update a {@link SJobLog}\n     *\n     * @param jobLog The log to update\n     * @param descriptor\n     * @since 6.4.0\n     */\n    void updateJobLog(SJobLog jobLog, EntityUpdateDescriptor descriptor) throws SJobLogUpdatingException;\n\n    /**\n     * Delete all {@link SJobLog} of a specific {@link SJobDescriptor}\n     *\n     * @param jobDescriptorId The identifier of the {@link SJobDescriptor}\n     * @throws SBonitaReadException\n     * @throws SJobLogDeletionException\n     * @since 6.4.0\n     */\n    void deleteJobLogs(long jobDescriptorId) throws SJobLogDeletionException, SBonitaReadException;\n\n    /**\n     * Get all {@link SJobLog} of a specific {@link SJobDescriptor}\n     *\n     * @param jobDescriptorId The identifier of the {@link SJobDescriptor}\n     * @param fromIndex The index of the first element of the list\n     * @param maxResults The nulber max of elements of the list\n     * @return A list of {@link SJobLog}\n     * @throws SBonitaReadException\n     * @since 6.4.0\n     */\n    List<SJobLog> getJobLogs(long jobDescriptorId, int fromIndex, int maxResults) throws SBonitaReadException;\n\n    /**\n     * log an error on a job\n     *\n     * @param jobException the exception\n     * @param jobDescriptorId the id of the job\n     * @throws SBonitaReadException\n     * @throws SJobLogUpdatingException\n     * @throws SJobLogCreationException\n     * @throws SJobDescriptorReadException\n     * @since 7.2.0\n     */\n    void logJobError(final Throwable jobException, final Long jobDescriptorId)\n            throws SBonitaReadException, SJobLogUpdatingException,\n            SJobLogCreationException, SJobDescriptorReadException;\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/SchedulerExecutor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport org.bonitasoft.engine.scheduler.exception.SSchedulerException;\nimport org.bonitasoft.engine.scheduler.impl.SchedulerServiceImpl;\nimport org.bonitasoft.engine.scheduler.trigger.Trigger;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic interface SchedulerExecutor {\n\n    boolean isStarted() throws SSchedulerException;\n\n    boolean isShutdown() throws SSchedulerException;\n\n    /**\n     * Note that once a scheduler is shutdown, it cannot be restarted without being re-instantiated.\n     *\n     * @throws SSchedulerException\n     * @since 6.4.0\n     */\n    void start() throws SSchedulerException;\n\n    /**\n     * Note that once a scheduler is shutdown, it cannot be restarted without being re-instantiated.\n     *\n     * @throws SSchedulerException\n     * @since 6.4.0\n     */\n    void shutdown() throws SSchedulerException;\n\n    boolean mayFireAgain(String jobName) throws SSchedulerException;\n\n    void rescheduleErroneousTriggers() throws SSchedulerException;\n\n    boolean delete(String jobName) throws SSchedulerException;\n\n    void deleteJobs() throws SSchedulerException;\n\n    List<String> getJobs() throws SSchedulerException;\n\n    void setBOSSchedulerService(SchedulerServiceImpl schedulerService);\n\n    void schedule(long jobId, String jobName, Trigger trigger, boolean disallowConcurrentExecution)\n            throws SSchedulerException;\n\n    void executeAgain(long jobId, String jobName, boolean disallowConcurrentExecution,\n            int delayInMillis) throws SSchedulerException;\n\n    void pauseJobs() throws SSchedulerException;\n\n    void resumeJobs() throws SSchedulerException;\n\n    /**\n     * Remove (delete) the <code>{@link org.quartz.Trigger}</code> with the given key, and store the new given one -\n     * which must be associated\n     * with the same job (the new trigger must have the job name specified)\n     * - however, the new trigger need not have the same name as the old trigger.\n     *\n     * @param triggerName\n     *        The name of the trigger to replace\n     * @param triggerStartTime\n     *        The start date of the new trigger\n     * @return <code>null</code> if a <code>Trigger</code> with the given\n     *         name was not found and removed from the store (and the\n     *         new trigger is therefore not stored), otherwise\n     *         the first fire time of the newly scheduled trigger is returned.\n     * @throws SSchedulerException\n     * @since 6.4.0\n     */\n    Date rescheduleJob(String triggerName, Date triggerStartTime) throws SSchedulerException;\n\n    /**\n     * Check if a job exists.\n     *\n     * @param jobName\n     *        The name of the job\n     * @return True if the job exists, else False.\n     * @throws SSchedulerException\n     * @since 6.4.0\n     */\n    boolean isExistingJob(String jobName) throws SSchedulerException;\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/SchedulerService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.PlatformLifecycleService;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.scheduler.exception.SSchedulerException;\nimport org.bonitasoft.engine.scheduler.model.SJobDescriptor;\nimport org.bonitasoft.engine.scheduler.model.SJobParameter;\nimport org.bonitasoft.engine.scheduler.trigger.Trigger;\n\n/**\n * @author Matthieu Chaffotte\n * @since 6.0\n */\npublic interface SchedulerService extends PlatformLifecycleService {\n\n    /**\n     * This service will fire the following events :\n     * <ul>\n     * <li>SCHEDULER_STARTED = \"SCHEDULER_STARTED\"</li>\n     * <li>SCHEDULER_STOPPED = \"SCHEDULER_STOPPED\"</li>\n     * <li>JOB_FAILED = \"JOB_FAILED\"</li>\n     * </ul>\n     */\n    String SCHEDULER_STARTED = \"SCHEDULER_STARTED\";\n\n    String SCHEDULER_STOPPED = \"SCHEDULER_STOPPED\";\n\n    String JOB_FAILED = \"JOB_FAILED\";\n\n    /**\n     * Checks whether the service is started.\n     *\n     * @return true if the service is started; false otherwise.\n     * @throws SSchedulerException\n     *         if an exception occurs.\n     */\n    boolean isStarted() throws SSchedulerException;\n\n    /**\n     * Checks whether the service is shutdown.\n     *\n     * @return true if the service is shutdown; false otherwise.\n     * @throws SSchedulerException\n     */\n    boolean isStopped() throws SSchedulerException;\n\n    /**\n     * Schedules a job.\n     *\n     * @param jobDescriptor\n     * @param trigger\n     * @throws SSchedulerException\n     *         if an exception occurs.\n     */\n    void schedule(SJobDescriptor jobDescriptor, Trigger trigger) throws SSchedulerException;\n\n    /**\n     * Schedules a job.\n     *\n     * @param jobDescriptor\n     * @param parameters\n     * @param trigger\n     * @throws SSchedulerException\n     *         if an exception occurs.\n     */\n    void schedule(SJobDescriptor jobDescriptor, List<SJobParameter> parameters, Trigger trigger)\n            throws SSchedulerException;\n\n    /**\n     * Execute a job again\n     *\n     * @param jobDescriptorId the job to re execute\n     * @param delayInMillis\n     */\n    void executeAgain(long jobDescriptorId, int delayInMillis) throws SSchedulerException;\n\n    /**\n     * Retry a job once\n     * In addition to executing the job again, this will also delete failed job logs\n     *\n     * @param jobDescriptorId the job to retry\n     */\n    void retryJobThatFailed(long jobDescriptorId) throws SSchedulerException;\n\n    /**\n     * Change parameters of a job and retry it once\n     * In addition to executing the job again, this will also delete failed job logs\n     *\n     * @param jobDescriptorId the job to retry\n     * @param parameters the new parameters for the job\n     */\n    void retryJobThatFailed(long jobDescriptorId, List<SJobParameter> parameters) throws SSchedulerException;\n\n    /**\n     * Deletes a job according to its name.\n     *\n     * @param jobName\n     *        the job name\n     * @return true if delete a job, otherwise return false.\n     * @throws SSchedulerException\n     *         if an exception occurs.\n     */\n    boolean delete(String jobName) throws SSchedulerException;\n\n    /**\n     * Deletes all jobs.\n     *\n     * @throws SSchedulerException\n     *         if an exception occurs.\n     */\n    void deleteJobs() throws SSchedulerException;\n\n    /**\n     * Get all jobs\n     *\n     * @return all jobs\n     * @throws SSchedulerException\n     *         if an exception occurs.\n     */\n    List<String> getJobs() throws SSchedulerException;\n\n    void rescheduleErroneousTriggers() throws SSchedulerException;\n\n    /**\n     * Pause all running jobs\n     *\n     * @throws SSchedulerException\n     */\n    void pauseJobs() throws SSchedulerException;\n\n    /**\n     * Resume all paused jobs\n     *\n     * @throws SSchedulerException\n     */\n    void resumeJobs() throws SSchedulerException;\n\n    /**\n     * Remove (delete) the <code>Trigger</code> with the\n     * given key, and store the new given one - which must be associated\n     * with the same job (the new trigger must have the job name specified)\n     * - however, the new trigger need not have the same name as the old trigger.\n     *\n     * @param triggerName\n     *        The name of the trigger to replace\n     * @param triggerStartTime\n     *        The start date of the new trigger\n     * @return <code>null</code> if a <code>Trigger</code> with the given\n     *         name was not found and removed from the store (and the\n     *         new trigger is therefore not stored), otherwise\n     *         the first fire time of the newly scheduled trigger is returned.\n     * @throws SSchedulerException\n     * @since 6.4.0\n     */\n    Date rescheduleJob(String triggerName, Date triggerStartTime) throws SSchedulerException;\n\n    /**\n     * Note that once a scheduler is shutdown, it cannot be restarted without being re-instantiated.\n     *\n     * @throws SSchedulerException\n     * @since 6.4.0\n     */\n    @Override\n    void start() throws SBonitaException;\n\n    /**\n     * Note that once a scheduler is shutdown, it cannot be restarted without being re-instantiated.\n     *\n     * @throws SSchedulerException\n     * @since 6.4.0\n     */\n    @Override\n    void stop() throws SBonitaException;\n\n    /**\n     * Check if a job exists.\n     *\n     * @param jobName\n     *        The name of the job\n     * @return True if the job exists, else False.\n     * @throws SSchedulerException\n     * @since 6.4.0\n     */\n    boolean isExistingJob(String jobName) throws SSchedulerException;\n\n    boolean mayFireAgain(String jobName) throws SSchedulerException;\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/StatelessJob.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.events.model.SFireEventException;\nimport org.bonitasoft.engine.scheduler.exception.SJobConfigurationException;\nimport org.bonitasoft.engine.scheduler.exception.SJobExecutionException;\n\n/**\n * Interface of a scheduled job. A job is classified using a name and a group name. A job has a unique name and group\n * name. It\n * fires the following events :\n * <ul>\n * <li>JOB_EXECUTING = \"JOB_EXECUTING\"</li>\n * <li>JOB_COMPLETED = \"JOB_COMPLETED\"</li>\n * </ul>\n *\n * @author Matthieu Chaffotte\n */\npublic interface StatelessJob extends Serializable {\n\n    String JOB_EXECUTING = \"JOB_EXECUTING\";\n\n    String JOB_COMPLETED = \"JOB_COMPLETED\";\n\n    String JOB_DESCRIPTOR_ID = \"JOB_DESCRIPTOR_ID\";\n\n    /**\n     * Gets the job name.\n     *\n     * @return the job name\n     * @since 6.0\n     */\n    String getName();\n\n    /**\n     * Gets the description of the job.\n     *\n     * @return the job description\n     * @since 6.0\n     */\n    String getDescription();\n\n    /**\n     * Execute the content of the job.\n     *\n     * @throws SJobExecutionException\n     *         if an exception occurs\n     * @throws SFireEventException\n     * @since 6.0\n     */\n    void execute() throws SJobExecutionException, SFireEventException;\n\n    /**\n     * This method is called by the scheduler service before the execution of the job\n     *\n     * @param attributes\n     *        key is the name of the attribute\n     *        value is the value of the attribute\n     * @throws SJobConfigurationException\n     * @since 6.0\n     */\n    void setAttributes(Map<String, Serializable> attributes) throws SJobConfigurationException;\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/exception/SJobConfigurationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.exception;\n\n/**\n * @author Baptiste Mesta\n */\npublic class SJobConfigurationException extends SSchedulerException {\n\n    private static final long serialVersionUID = -226779259333121029L;\n\n    public SJobConfigurationException(final String message) {\n        super(message);\n    }\n\n    public SJobConfigurationException(final Exception e) {\n        super(e);\n    }\n\n    public SJobConfigurationException(final String message, final Exception exception) {\n        super(message, exception);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/exception/SJobExecutionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.exception;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SJobExecutionException extends SSchedulerException {\n\n    private static final long serialVersionUID = -226779259333121029L;\n\n    public SJobExecutionException(final String message) {\n        super(message);\n    }\n\n    public SJobExecutionException(final Throwable e) {\n        super(e);\n    }\n\n    public SJobExecutionException(final String message, final Exception exception) {\n        super(message, exception);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/exception/SJobListenerExecutionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.exception;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Celine Souchet\n */\npublic class SJobListenerExecutionException extends SBonitaException {\n\n    private static final long serialVersionUID = 9185581023740886000L;\n\n    public SJobListenerExecutionException(final String message) {\n        super(message);\n    }\n\n    public SJobListenerExecutionException(final Throwable e) {\n        super(e);\n    }\n\n    public SJobListenerExecutionException(final String message, final Exception e) {\n        super(message, e);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/exception/SSchedulerException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.exception;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class SSchedulerException extends SBonitaException {\n\n    private static final long serialVersionUID = 9185581023740886000L;\n\n    public SSchedulerException(final String message) {\n        super(message);\n    }\n\n    public SSchedulerException(final Throwable e) {\n        super(e);\n    }\n\n    public SSchedulerException(final String message, final Exception e) {\n        super(message, e);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/exception/failedJob/SFailedJobReadException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.exception.failedJob;\n\nimport org.bonitasoft.engine.scheduler.exception.SSchedulerException;\n\n/**\n * @author Celine Souchet\n */\npublic class SFailedJobReadException extends SSchedulerException {\n\n    private static final long serialVersionUID = -226779259333121029L;\n\n    public SFailedJobReadException(final String message) {\n        super(message);\n    }\n\n    public SFailedJobReadException(final Exception e) {\n        super(e);\n    }\n\n    public SFailedJobReadException(final String message, final Exception exception) {\n        super(message, exception);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/exception/jobDescriptor/SJobDescriptorCreationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.exception.jobDescriptor;\n\nimport org.bonitasoft.engine.scheduler.exception.SSchedulerException;\n\n/**\n * @author Celine Souchet\n */\npublic class SJobDescriptorCreationException extends SSchedulerException {\n\n    private static final long serialVersionUID = -226779259333121029L;\n\n    public SJobDescriptorCreationException(final String message) {\n        super(message);\n    }\n\n    public SJobDescriptorCreationException(final Exception e) {\n        super(e);\n    }\n\n    public SJobDescriptorCreationException(final String message, final Exception exception) {\n        super(message, exception);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/exception/jobDescriptor/SJobDescriptorDeletionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.exception.jobDescriptor;\n\nimport org.bonitasoft.engine.scheduler.exception.SSchedulerException;\n\n/**\n * @author Celine Souchet\n */\npublic class SJobDescriptorDeletionException extends SSchedulerException {\n\n    private static final long serialVersionUID = -226779259333121029L;\n\n    public SJobDescriptorDeletionException(final String message) {\n        super(message);\n    }\n\n    public SJobDescriptorDeletionException(final Exception e) {\n        super(e);\n    }\n\n    public SJobDescriptorDeletionException(final String message, final Exception exception) {\n        super(message, exception);\n    }\n\n    public SJobDescriptorDeletionException(final long id) {\n        super(\"Can't find job descriptor with id = \" + id);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/exception/jobDescriptor/SJobDescriptorNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.exception.jobDescriptor;\n\nimport org.bonitasoft.engine.scheduler.exception.SSchedulerException;\n\n/**\n * @author Celine Souchet\n */\npublic class SJobDescriptorNotFoundException extends SSchedulerException {\n\n    private static final long serialVersionUID = -226779259333121029L;\n\n    public SJobDescriptorNotFoundException(final String message) {\n        super(message);\n    }\n\n    public SJobDescriptorNotFoundException(final Exception e) {\n        super(e);\n    }\n\n    public SJobDescriptorNotFoundException(final String message, final Exception exception) {\n        super(message, exception);\n    }\n\n    public SJobDescriptorNotFoundException(final long id) {\n        super(\"Can't find job descriptor with id = \" + id);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/exception/jobDescriptor/SJobDescriptorReadException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.exception.jobDescriptor;\n\nimport org.bonitasoft.engine.scheduler.exception.SSchedulerException;\n\n/**\n * @author Celine Souchet\n */\npublic class SJobDescriptorReadException extends SSchedulerException {\n\n    private static final long serialVersionUID = -226779259333121029L;\n\n    public SJobDescriptorReadException(final String message) {\n        super(message);\n    }\n\n    public SJobDescriptorReadException(final Exception e) {\n        super(e);\n    }\n\n    public SJobDescriptorReadException(final String message, final Exception exception) {\n        super(message, exception);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/exception/jobLog/SJobLogCreationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.exception.jobLog;\n\nimport org.bonitasoft.engine.scheduler.exception.SSchedulerException;\n\n/**\n * @author Celine Souchet\n */\npublic class SJobLogCreationException extends SSchedulerException {\n\n    private static final long serialVersionUID = -226779259333121029L;\n\n    public SJobLogCreationException(final String message) {\n        super(message);\n    }\n\n    public SJobLogCreationException(final Exception e) {\n        super(e);\n    }\n\n    public SJobLogCreationException(final String message, final Exception exception) {\n        super(message, exception);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/exception/jobLog/SJobLogDeletionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.exception.jobLog;\n\nimport org.bonitasoft.engine.scheduler.exception.SSchedulerException;\n\n/**\n * @author Celine Souchet\n */\npublic class SJobLogDeletionException extends SSchedulerException {\n\n    private static final long serialVersionUID = -226779259333121029L;\n\n    public SJobLogDeletionException(final String message) {\n        super(message);\n    }\n\n    public SJobLogDeletionException(final Exception e) {\n        super(e);\n    }\n\n    public SJobLogDeletionException(final String message, final Exception exception) {\n        super(message, exception);\n    }\n\n    public SJobLogDeletionException(final long id) {\n        super(\"Can't delete job log with id = \" + id);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/exception/jobLog/SJobLogNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.exception.jobLog;\n\nimport org.bonitasoft.engine.scheduler.exception.SSchedulerException;\n\n/**\n * @author Celine Souchet\n */\npublic class SJobLogNotFoundException extends SSchedulerException {\n\n    private static final long serialVersionUID = -226779259333121029L;\n\n    public SJobLogNotFoundException(final String message) {\n        super(message);\n    }\n\n    public SJobLogNotFoundException(final Exception e) {\n        super(e);\n    }\n\n    public SJobLogNotFoundException(final String message, final Exception exception) {\n        super(message, exception);\n    }\n\n    public SJobLogNotFoundException(long id) {\n        super(\"Can't find job log with id = \" + id);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/exception/jobLog/SJobLogReadException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.exception.jobLog;\n\nimport org.bonitasoft.engine.scheduler.exception.SSchedulerException;\n\n/**\n * @author Celine Souchet\n */\npublic class SJobLogReadException extends SSchedulerException {\n\n    private static final long serialVersionUID = -226779259333121029L;\n\n    public SJobLogReadException(final String message) {\n        super(message);\n    }\n\n    public SJobLogReadException(final Exception e) {\n        super(e);\n    }\n\n    public SJobLogReadException(final String message, final Exception exception) {\n        super(message, exception);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/exception/jobLog/SJobLogUpdatingException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.exception.jobLog;\n\nimport org.bonitasoft.engine.scheduler.exception.SSchedulerException;\n\n/**\n * @author Celine Souchet\n */\npublic class SJobLogUpdatingException extends SSchedulerException {\n\n    private static final long serialVersionUID = -226779259333121029L;\n\n    public SJobLogUpdatingException(final String message) {\n        super(message);\n    }\n\n    public SJobLogUpdatingException(final Exception e) {\n        super(e);\n    }\n\n    public SJobLogUpdatingException(final String message, final Exception exception) {\n        super(message, exception);\n    }\n\n    public SJobLogUpdatingException(final long id) {\n        super(\"Can't update job log with id = \" + id);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/exception/jobParameter/SJobParameterCreationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.exception.jobParameter;\n\nimport org.bonitasoft.engine.scheduler.exception.SSchedulerException;\n\n/**\n * @author Celine Souchet\n */\npublic class SJobParameterCreationException extends SSchedulerException {\n\n    private static final long serialVersionUID = -226779259333121029L;\n\n    public SJobParameterCreationException(final String message) {\n        super(message);\n    }\n\n    public SJobParameterCreationException(final Exception e) {\n        super(e);\n    }\n\n    public SJobParameterCreationException(final String message, final Exception exception) {\n        super(message, exception);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/exception/jobParameter/SJobParameterDeletionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.exception.jobParameter;\n\nimport org.bonitasoft.engine.scheduler.exception.SSchedulerException;\n\n/**\n * @author Celine Souchet\n */\npublic class SJobParameterDeletionException extends SSchedulerException {\n\n    private static final long serialVersionUID = -226779259333121029L;\n\n    public SJobParameterDeletionException(final String message) {\n        super(message);\n    }\n\n    public SJobParameterDeletionException(final Exception e) {\n        super(e);\n    }\n\n    public SJobParameterDeletionException(final String message, final Exception exception) {\n        super(message, exception);\n    }\n\n    public SJobParameterDeletionException(final long id) {\n        super(\"Can't find job descriptor with id = \" + id);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/exception/jobParameter/SJobParameterNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.exception.jobParameter;\n\nimport org.bonitasoft.engine.scheduler.exception.SSchedulerException;\n\n/**\n * @author Celine Souchet\n */\npublic class SJobParameterNotFoundException extends SSchedulerException {\n\n    private static final long serialVersionUID = -226779259333121029L;\n\n    public SJobParameterNotFoundException(final String message) {\n        super(message);\n    }\n\n    public SJobParameterNotFoundException(final Exception e) {\n        super(e);\n    }\n\n    public SJobParameterNotFoundException(final String message, final Exception exception) {\n        super(message, exception);\n    }\n\n    public SJobParameterNotFoundException(long id) {\n        super(\"Can't find job parameter with id = \" + id);\n    }\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/exception/jobParameter/SJobParameterReadException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.exception.jobParameter;\n\nimport org.bonitasoft.engine.scheduler.exception.SSchedulerException;\n\n/**\n * @author Celine Souchet\n */\npublic class SJobParameterReadException extends SSchedulerException {\n\n    private static final long serialVersionUID = -226779259333121029L;\n\n    public SJobParameterReadException(final String message) {\n        super(message);\n    }\n\n    public SJobParameterReadException(final Exception e) {\n        super(e);\n    }\n\n    public SJobParameterReadException(final String message, final Exception exception) {\n        super(message, exception);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/impl/AbstractQuartzJob.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.impl;\n\nimport org.bonitasoft.engine.commons.exceptions.SRetryableException;\nimport org.bonitasoft.engine.scheduler.JobIdentifier;\nimport org.bonitasoft.engine.scheduler.StatelessJob;\nimport org.bonitasoft.engine.scheduler.exception.SSchedulerException;\nimport org.quartz.JobDataMap;\nimport org.quartz.JobDetail;\nimport org.quartz.JobExecutionContext;\nimport org.quartz.JobExecutionException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Wraps a Bonita job.\n *\n * @author Matthieu Chaffotte\n * @author Baptsite Mesta\n * @author Celine Souchet\n */\npublic abstract class AbstractQuartzJob implements org.quartz.Job {\n\n    private static Logger logger = LoggerFactory.getLogger(AbstractQuartzJob.class);\n\n    private StatelessJob bosJob;\n    private SchedulerServiceImpl schedulerService;\n    private JobDetail jobDetail;\n\n    @Override\n    public void execute(final JobExecutionContext context) throws JobExecutionException {\n        final JobIdentifier jobIdentifier = getJobIdentifier(jobDetail.getJobDataMap());\n        try {\n            bosJob = retrieveJob(jobIdentifier);\n            bosJob.execute();\n        } catch (final SRetryableException e) {\n            logger.info(\"Job {} failed but it will be retried {}\", jobIdentifier, e.getMessage());\n            try {\n                schedulerService.executeAgain(jobIdentifier.getId(), 5000);\n            } catch (SSchedulerException ex) {\n                throw new JobExecutionException(\"Unable to reschedule job that could be retried\", ex);\n            }\n        } catch (final Throwable e) {\n            throw new JobExecutionException(e);\n        }\n    }\n\n    private StatelessJob retrieveJob(JobIdentifier jobIdentifier) throws JobExecutionException {\n        try {\n            return schedulerService.getPersistedJob(jobIdentifier);\n        } catch (final Throwable t) {\n            throw new JobExecutionException(\"unable to create the BOS job\", t);\n        }\n    }\n\n    private JobIdentifier getJobIdentifier(JobDataMap jobDataMap) {\n        final long jobId = Long.parseLong((String) jobDataMap.get(\"jobId\"));\n        final String jobName = (String) jobDataMap.get(\"jobName\");\n        return new JobIdentifier(jobId, jobName);\n    }\n\n    StatelessJob getBosJob() {\n        return bosJob;\n    }\n\n    void setSchedulerService(SchedulerServiceImpl schedulerService) {\n        this.schedulerService = schedulerService;\n    }\n\n    void setJobDetails(JobDetail jobDetail) {\n        this.jobDetail = jobDetail;\n    }\n\n    JobDetail getJobDetail() {\n        return jobDetail;\n    }\n\n    SchedulerServiceImpl getSchedulerService() {\n        return schedulerService;\n    }\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/impl/BonitaJobStoreCMT.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.impl;\n\nimport java.sql.Connection;\n\nimport org.quartz.JobDetail;\nimport org.quartz.JobPersistenceException;\nimport org.quartz.Trigger.CompletedExecutionInstruction;\nimport org.quartz.impl.jdbcjobstore.JobStoreCMT;\nimport org.quartz.spi.OperableTrigger;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class BonitaJobStoreCMT extends JobStoreCMT {\n\n    @Override\n    protected void triggeredJobComplete(final Connection conn, final OperableTrigger trigger, final JobDetail jobDetail,\n            final CompletedExecutionInstruction triggerInstCode) throws JobPersistenceException {\n        super.triggeredJobComplete(conn, trigger, jobDetail, triggerInstCode);\n        if (CompletedExecutionInstruction.SET_TRIGGER_ERROR.equals(triggerInstCode)\n                || CompletedExecutionInstruction.SET_ALL_JOB_TRIGGERS_ERROR.equals(triggerInstCode)) {\n            // the super method logs already this message but only in info level. (should be logged as error)\n            if (!getLog().isInfoEnabled()) {\n                getLog().error(\"All triggers of Job \" + trigger.getKey() + \" set to ERROR state.\");\n            }\n            getLog().error(\n                    \"In order to restart the triggers, you can either restart your node or call the platformAPI.rescheduleErroneousTriggers method.\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/impl/BonitaScheduler.java",
    "content": "/**\n * Copyright (C) 2022 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.impl;\n\nimport org.quartz.core.QuartzScheduler;\nimport org.quartz.impl.StdScheduler;\n\n/**\n * Custom Quartz scheduler implementation that exposes the internal QuartzScheduler.\n */\npublic class BonitaScheduler extends StdScheduler {\n\n    private QuartzScheduler quartzScheduler;\n\n    public BonitaScheduler(QuartzScheduler sched) {\n        super(sched);\n        this.quartzScheduler = sched;\n    }\n\n    public QuartzScheduler getQuartzScheduler() {\n        return quartzScheduler;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/impl/BonitaSchedulerFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.impl;\n\nimport java.util.Properties;\n\nimport org.quartz.Scheduler;\nimport org.quartz.SchedulerException;\nimport org.quartz.core.QuartzScheduler;\nimport org.quartz.core.QuartzSchedulerResources;\nimport org.quartz.impl.StdSchedulerFactory;\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Baptiste Mesta\n */\n@Component\n@ConditionalOnSingleCandidate(BonitaSchedulerFactory.class)\npublic class BonitaSchedulerFactory extends StdSchedulerFactory {\n\n    private SchedulerServiceImpl schedulerService;\n\n    public BonitaSchedulerFactory(@Qualifier(\"quartzProperties\") Properties props) throws SchedulerException {\n        super(props);\n    }\n\n    @Override\n    public Scheduler getScheduler() throws SchedulerException {\n        final Scheduler scheduler = super.getScheduler();\n        scheduler.setJobFactory(new TransactionalSimpleJobFactory(schedulerService));\n        return scheduler;\n    }\n\n    public void setBOSSchedulerService(final SchedulerServiceImpl schedulerService) {\n        this.schedulerService = schedulerService;\n    }\n\n    @Override\n    protected Scheduler instantiate(QuartzSchedulerResources rsrcs, QuartzScheduler qs) {\n        return new BonitaScheduler(qs);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/impl/ConcurrentQuartzJob.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.impl;\n\npublic class ConcurrentQuartzJob extends AbstractQuartzJob {\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/impl/JDBCJobListener.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.impl;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.scheduler.BonitaJobListener;\nimport org.bonitasoft.engine.scheduler.JobService;\nimport org.bonitasoft.engine.scheduler.SchedulerService;\nimport org.quartz.JobExecutionException;\n\n/**\n * @author Celine Souchet\n * @author Matthieu Chaffotte\n * @author Elias Ricken de Medeiros\n */\n@Slf4j\npublic class JDBCJobListener implements BonitaJobListener {\n\n    private final JobService jobService;\n    private SchedulerService schedulerService;\n\n    public JDBCJobListener(final JobService jobService,\n            SchedulerService schedulerService) {\n        super();\n        this.jobService = jobService;\n        this.schedulerService = schedulerService;\n    }\n\n    @Override\n    public void jobToBeExecuted() {\n        // nothing to do\n    }\n\n    @Override\n    public void jobExecutionVetoed() {\n        // nothing to do\n    }\n\n    @Override\n    public void jobWasExecuted(final Map<String, Serializable> context, final Exception jobException) {\n        if (jobException != null) {\n            if (jobException instanceof JobExecutionException jobExecutionException\n                    && jobExecutionException.refireImmediately()) {\n                log.debug(\"An exception occurs during the job execution but it will be retried.\", jobException);\n            } else {\n                log.warn(\"An exception occurs during the job execution.\", jobException);\n            }\n            return;\n        }\n        Long jobDescriptorId = (Long) context.get(JOB_DESCRIPTOR_ID);\n        if (isNullOrEmpty(jobDescriptorId)) {\n            log.warn(\"A quartz job was executed but is not a bonita Job, context: {}\", context);\n            return;\n        }\n        if (context.get(TRIGGER_NEXT_FIRE_TIME) == null) {\n\n            //will not fire again\n            deleteJobDescriptor(context, jobDescriptorId);\n        }\n    }\n\n    private void deleteJobDescriptor(Map<String, Serializable> context, Long jobDescriptorId) {\n        try {\n            //delete job only if there is no other trigger\n            boolean mayFireAgain = schedulerService.mayFireAgain((String) context.get(JOB_NAME));\n\n            log.debug(\"{} job descriptor of job {} because it may {}fire again.\",\n                    mayFireAgain ? \"Keeping\" : \"Deleting\", context.get(JOB_NAME), mayFireAgain ? \"\" : \"not \");\n            if (!mayFireAgain) {\n                jobService.deleteJobDescriptor(jobDescriptorId);\n            }\n        } catch (final Exception e) {\n            log.warn(\"Unable to delete job descriptor {} of job {}\", jobDescriptorId, context.get(JOB_NAME), e);\n        }\n    }\n\n    private boolean isNullOrEmpty(final Long id) {\n        return id == null || id == 0;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/impl/JobServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.impl;\n\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.persistence.FilterOption;\nimport org.bonitasoft.engine.persistence.OrderByOption;\nimport org.bonitasoft.engine.persistence.OrderByType;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.bonitasoft.engine.scheduler.JobService;\nimport org.bonitasoft.engine.scheduler.exception.failedJob.SFailedJobReadException;\nimport org.bonitasoft.engine.scheduler.exception.jobDescriptor.SJobDescriptorCreationException;\nimport org.bonitasoft.engine.scheduler.exception.jobDescriptor.SJobDescriptorDeletionException;\nimport org.bonitasoft.engine.scheduler.exception.jobDescriptor.SJobDescriptorReadException;\nimport org.bonitasoft.engine.scheduler.exception.jobLog.SJobLogCreationException;\nimport org.bonitasoft.engine.scheduler.exception.jobLog.SJobLogDeletionException;\nimport org.bonitasoft.engine.scheduler.exception.jobLog.SJobLogUpdatingException;\nimport org.bonitasoft.engine.scheduler.exception.jobParameter.SJobParameterCreationException;\nimport org.bonitasoft.engine.scheduler.exception.jobParameter.SJobParameterDeletionException;\nimport org.bonitasoft.engine.scheduler.exception.jobParameter.SJobParameterNotFoundException;\nimport org.bonitasoft.engine.scheduler.exception.jobParameter.SJobParameterReadException;\nimport org.bonitasoft.engine.scheduler.model.SFailedJob;\nimport org.bonitasoft.engine.scheduler.model.SJobDescriptor;\nimport org.bonitasoft.engine.scheduler.model.SJobLog;\nimport org.bonitasoft.engine.scheduler.model.SJobParameter;\nimport org.bonitasoft.engine.scheduler.recorder.SelectDescriptorBuilder;\n\n/**\n * @author Celine Souchet\n * @author Matthieu Chaffotte\n */\n@Slf4j\npublic class JobServiceImpl implements JobService {\n\n    private final EventService eventService;\n\n    private final Recorder recorder;\n\n    private final ReadPersistenceService readPersistenceService;\n\n    public JobServiceImpl(final EventService eventService, final Recorder recorder,\n            final ReadPersistenceService readPersistenceService) {\n        this.readPersistenceService = readPersistenceService;\n        this.eventService = eventService;\n        this.recorder = recorder;\n    }\n\n    @Override\n    public SJobDescriptor createJobDescriptor(final SJobDescriptor sJobDescriptor)\n            throws SJobDescriptorCreationException {\n        if (sJobDescriptor == null) {\n            throw new IllegalArgumentException(\"The job descriptor is null\");\n        } else if (sJobDescriptor.getJobName() == null) {\n            throw new IllegalArgumentException(\"The job name is null\");\n        }\n\n        final SJobDescriptor sJobDescriptorToRecord = new SJobDescriptor(sJobDescriptor.getJobClassName(),\n                sJobDescriptor.getJobName(),\n                sJobDescriptor.getDescription());\n\n        try {\n            create(sJobDescriptorToRecord, JOB_DESCRIPTOR);\n        } catch (final SRecorderException sre) {\n            throw new SJobDescriptorCreationException(sre);\n        }\n        return sJobDescriptorToRecord;\n    }\n\n    @Override\n    public void deleteJobDescriptor(final long id) throws SJobDescriptorReadException, SJobDescriptorDeletionException {\n        final SJobDescriptor sJobDescriptor = getJobDescriptor(id);\n        if (sJobDescriptor == null) {\n            if (log.isTraceEnabled()) {\n                final StringBuilder stringBuilder = new StringBuilder();\n                stringBuilder.append(\"jobDescriptor with id\");\n                stringBuilder.append(id);\n                stringBuilder.append(\" already deleted, ignore it\");\n                log.trace(stringBuilder.toString());\n            }\n        } else {\n            deleteJobDescriptor(sJobDescriptor);\n        }\n    }\n\n    @Override\n    public void deleteJobDescriptor(final SJobDescriptor sJobDescriptor) throws SJobDescriptorDeletionException {\n        if (sJobDescriptor == null) {\n            throw new IllegalArgumentException(\"The job descriptor is null\");\n        }\n        try {\n            delete(sJobDescriptor, JOB_DESCRIPTOR);\n        } catch (final SBonitaException e) {\n            throw new SJobDescriptorDeletionException(e);\n        }\n    }\n\n    @Override\n    public SJobDescriptor getJobDescriptor(final long id) throws SJobDescriptorReadException {\n        try {\n            final SJobDescriptor sJobDescriptor = readPersistenceService\n                    .selectById(SelectDescriptorBuilder.getElementById(SJobDescriptor.class,\n                            \"SJobDescriptor\", id));\n            return sJobDescriptor;\n        } catch (final SBonitaReadException sbre) {\n            throw new SJobDescriptorReadException(sbre);\n        }\n    }\n\n    @Override\n    public long getNumberOfJobDescriptors(final QueryOptions queryOptions) throws SBonitaReadException {\n        return readPersistenceService.getNumberOfEntities(SJobDescriptor.class, queryOptions, null);\n    }\n\n    @Override\n    public List<SJobDescriptor> searchJobDescriptors(final QueryOptions queryOptions) throws SBonitaReadException {\n        return readPersistenceService.searchEntity(SJobDescriptor.class, queryOptions, null);\n    }\n\n    @Override\n    public List<SJobParameter> createJobParameters(final List<SJobParameter> sJobParameters, final long jobDescriptorId)\n            throws SJobParameterCreationException {\n        final List<SJobParameter> createdSJobParameters = new ArrayList<>();\n        if (sJobParameters != null) {\n            for (final SJobParameter sJobParameter : sJobParameters) {\n                createdSJobParameters.add(createJobParameter(sJobParameter, jobDescriptorId));\n            }\n        }\n        return createdSJobParameters;\n    }\n\n    @Override\n    public List<SJobParameter> setJobParameters(final long jobDescriptorId, final List<SJobParameter> parameters)\n            throws SJobParameterCreationException {\n        deleteAllJobParameters(jobDescriptorId);\n        return createJobParameters(parameters, jobDescriptorId);\n    }\n\n    protected void deleteAllJobParameters(final long jobDescriptorId) throws SJobParameterCreationException {\n        try {\n            for (SJobParameter jobParameter : getJobParameters(jobDescriptorId)) {\n                deleteJobParameter(jobParameter);\n            }\n        } catch (final SBonitaException sbe) {\n            throw new SJobParameterCreationException(sbe);\n        }\n    }\n\n    @Override\n    public SJobParameter createJobParameter(final SJobParameter sJobParameter, final long jobDescriptorId)\n            throws SJobParameterCreationException {\n        if (sJobParameter == null) {\n            throw new IllegalArgumentException(\"The job descriptor is null\");\n        }\n\n        final SJobParameter sJobParameterToRecord = SJobParameter.builder()\n                .key(sJobParameter.getKey())\n                .value(sJobParameter.getValue()).jobDescriptorId(jobDescriptorId).build();\n\n        try {\n            create(sJobParameterToRecord, JOB_PARAMETER);\n        } catch (final SRecorderException sre) {\n            throw new SJobParameterCreationException(sre);\n        }\n        return sJobParameter;\n    }\n\n    @Override\n    public void deleteJobParameter(final long id)\n            throws SJobParameterNotFoundException, SJobParameterReadException, SJobParameterDeletionException {\n        final SJobParameter sJobParameter = getJobParameter(id);\n        deleteJobParameter(sJobParameter);\n    }\n\n    @Override\n    public void deleteJobParameter(final SJobParameter sJobParameter) throws SJobParameterDeletionException {\n        try {\n            delete(sJobParameter, JOB_PARAMETER);\n        } catch (final SBonitaException e) {\n            throw new SJobParameterDeletionException(e);\n        }\n    }\n\n    @Override\n    public SJobParameter getJobParameter(final long id)\n            throws SJobParameterNotFoundException, SJobParameterReadException {\n        try {\n            final SJobParameter sJobParameter = readPersistenceService\n                    .selectById(SelectDescriptorBuilder.getElementById(SJobParameter.class, \"SJobParameter\",\n                            id));\n            if (sJobParameter == null) {\n                throw new SJobParameterNotFoundException(id);\n            }\n            return sJobParameter;\n        } catch (final SBonitaReadException sbre) {\n            throw new SJobParameterReadException(sbre);\n        }\n    }\n\n    @Override\n    public List<SJobParameter> getJobParameters(Long jobDescriptorId) throws SBonitaReadException {\n        Map<String, Object> parameters = Collections.singletonMap(\"jobDescriptorId\", jobDescriptorId);\n        return readPersistenceService.selectList(new SelectListDescriptor<>(\"getJobParameters\", parameters,\n                SJobParameter.class, QueryOptions.countQueryOptions()));\n    }\n\n    @Override\n    public SJobLog createJobLog(final SJobLog sJobLog) throws SJobLogCreationException {\n        try {\n            create(sJobLog, JOB_LOG);\n        } catch (final SRecorderException sre) {\n            throw new SJobLogCreationException(sre);\n        }\n        return sJobLog;\n    }\n\n    @Override\n    public void deleteJobLog(final long id) throws SJobLogDeletionException, SBonitaReadException {\n        final SJobLog sJobLog = getJobLog(id);\n        if (sJobLog != null) {\n            deleteJobLog(sJobLog);\n        }\n    }\n\n    @Override\n    public void deleteJobLog(final SJobLog sJobLog) throws SJobLogDeletionException {\n        try {\n            delete(sJobLog, JOB_LOG);\n        } catch (final SBonitaException e) {\n            throw new SJobLogDeletionException(e);\n        }\n    }\n\n    @Override\n    public void deleteJobLogs(final long jobDescriptorId) throws SJobLogDeletionException, SBonitaReadException {\n        List<SJobLog> jobLogs = getJobLogs(jobDescriptorId, 0, 100);\n        while (!jobLogs.isEmpty()) {\n            deleteJobLogs(jobLogs);\n            jobLogs = getJobLogs(jobDescriptorId, 0, 100);\n        }\n    }\n\n    private void deleteJobLogs(final List<SJobLog> jobLogs) throws SJobLogDeletionException {\n        for (final SJobLog sJobLog : jobLogs) {\n            deleteJobLog(sJobLog);\n        }\n    }\n\n    @Override\n    public List<SJobLog> getJobLogs(final long jobDescriptorId, final int fromIndex, final int maxResults)\n            throws SBonitaReadException {\n        final FilterOption filter = new FilterOption(SJobLog.class, \"jobDescriptorId\", jobDescriptorId);\n        final OrderByOption orderByOption = new OrderByOption(SJobLog.class, \"jobDescriptorId\", OrderByType.ASC);\n        final QueryOptions options = new QueryOptions(fromIndex, maxResults, Arrays.asList(orderByOption),\n                Arrays.asList(filter), null);\n        return searchJobLogs(options);\n    }\n\n    @Override\n    public SJobLog getJobLog(final long id) throws SBonitaReadException {\n        return readPersistenceService.selectById(SelectDescriptorBuilder.getElementById(SJobLog.class, \"SJobLog\", id));\n    }\n\n    @Override\n    public long getNumberOfJobLogs(final QueryOptions queryOptions) throws SBonitaReadException {\n        return readPersistenceService.getNumberOfEntities(SJobLog.class, queryOptions, null);\n    }\n\n    @Override\n    public List<SJobLog> searchJobLogs(final QueryOptions queryOptions) throws SBonitaReadException {\n        return readPersistenceService.searchEntity(SJobLog.class, queryOptions, null);\n    }\n\n    private void delete(final PersistentObject persistentObject, final String eventType)\n            throws SRecorderException {\n        recorder.recordDelete(new DeleteRecord(persistentObject), eventType);\n    }\n\n    private void create(final PersistentObject persistentObject, final String eventType)\n            throws SRecorderException {\n        recorder.recordInsert(new InsertRecord(persistentObject), eventType);\n    }\n\n    @Override\n    public List<SFailedJob> getFailedJobs(final int startIndex, final int maxResults) throws SFailedJobReadException {\n        final QueryOptions queryOptions = new QueryOptions(startIndex, maxResults);\n        try {\n            return readPersistenceService.selectList(SelectDescriptorBuilder.getFailedJobs(queryOptions));\n        } catch (final SBonitaReadException sbre) {\n            throw new SFailedJobReadException(sbre);\n        }\n    }\n\n    @Override\n    public void deleteJobDescriptorByJobName(final String jobName) throws SJobDescriptorDeletionException {\n        final List<FilterOption> filters = new ArrayList<>();\n        filters.add(new FilterOption(SJobDescriptor.class, \"jobName\", jobName));\n        final List<OrderByOption> orders = Arrays\n                .asList(new OrderByOption(SJobDescriptor.class, \"id\", OrderByType.ASC));\n        final QueryOptions queryOptions = new QueryOptions(0, 1, orders, filters, null);\n        try {\n            final List<SJobDescriptor> jobDescriptors = searchJobDescriptors(queryOptions);\n            if (!jobDescriptors.isEmpty()) {\n                deleteJobDescriptor(jobDescriptors.get(0));\n            }\n        } catch (final SBonitaReadException e) {\n            throw new SJobDescriptorDeletionException(\n                    \"Job \" + jobName + \" not found, can't delete corresponding job descriptor\");\n        }\n    }\n\n    @Override\n    public void deleteAllJobDescriptors() throws SJobDescriptorDeletionException {\n        final List<FilterOption> filters = new ArrayList<>();\n        final QueryOptions queryOptions = new QueryOptions(0, 100, null, filters, null);\n        try {\n            final List<SJobDescriptor> jobDescriptors = searchJobDescriptors(queryOptions);\n            for (final SJobDescriptor sJobDescriptor : jobDescriptors) {\n                deleteJobDescriptor(sJobDescriptor);\n            }\n        } catch (final SBonitaReadException e) {\n            throw new SJobDescriptorDeletionException(e);\n        }\n    }\n\n    @Override\n    public void updateJobLog(final SJobLog jobLog, final EntityUpdateDescriptor descriptor)\n            throws SJobLogUpdatingException {\n        try {\n            recorder.recordUpdate(UpdateRecord.buildSetFields(jobLog, descriptor), JOB_LOG);\n        } catch (final SRecorderException e) {\n            throw new SJobLogUpdatingException(e);\n        }\n    }\n\n    @Override\n    public void logJobError(final Throwable jobException, final Long jobDescriptorId)\n            throws SBonitaReadException, SJobLogUpdatingException,\n            SJobLogCreationException, SJobDescriptorReadException {\n        final List<SJobLog> jobLogs = getJobLogs(jobDescriptorId, 0, 1);\n        if (!jobLogs.isEmpty()) {\n            final SJobLog jobLog = jobLogs.get(0);\n            final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n            final StringWriter exceptionWriter = new StringWriter();\n            jobException.printStackTrace(new PrintWriter(exceptionWriter));\n            descriptor.addField(\"lastMessage\", exceptionWriter.toString());\n            descriptor.addField(\"lastUpdateDate\", System.currentTimeMillis());\n            descriptor.addField(\"retryNumber\", jobLog.getRetryNumber() + 1);\n            updateJobLog(jobLog, descriptor);\n        } else {\n            createJobLog(jobException, jobDescriptorId);\n        }\n    }\n\n    public void createJobLog(final Throwable jobException, final Long jobDescriptorId)\n            throws SJobLogCreationException, SJobDescriptorReadException {\n        SJobDescriptor jobDescriptor = getJobDescriptor(jobDescriptorId);\n        if (jobDescriptor != null) {\n            final SJobLog jobLog = new SJobLog(jobDescriptorId);\n            jobLog.setLastMessage(getStackTrace(jobException));\n            jobLog.setRetryNumber(0L);\n            jobLog.setLastUpdateDate(System.currentTimeMillis());\n            createJobLog(jobLog);\n        } else {\n            log.warn(\"Impossible to mark the job with id '\"\n                    + jobDescriptorId\n                    + \"' as failed because no job was found for this identifier. It was probably removed just after its failure and before this action.\");\n        }\n    }\n\n    private String getStackTrace(final Throwable jobException) {\n        final StringWriter exceptionWriter = new StringWriter();\n        jobException.printStackTrace(new PrintWriter(exceptionWriter));\n        return exceptionWriter.toString();\n    }\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/impl/JobWrapper.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.impl;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.commons.exceptions.SRetryableException;\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.events.model.SEvent;\nimport org.bonitasoft.engine.events.model.SFireEventException;\nimport org.bonitasoft.engine.scheduler.JobIdentifier;\nimport org.bonitasoft.engine.scheduler.JobService;\nimport org.bonitasoft.engine.scheduler.StatelessJob;\nimport org.bonitasoft.engine.scheduler.exception.SJobConfigurationException;\nimport org.bonitasoft.engine.scheduler.exception.SJobExecutionException;\nimport org.bonitasoft.engine.services.PersistenceService;\nimport org.bonitasoft.engine.transaction.BonitaTransactionSynchronization;\nimport org.bonitasoft.engine.transaction.STransactionException;\nimport org.bonitasoft.engine.transaction.STransactionNotFoundException;\nimport org.bonitasoft.engine.transaction.TransactionService;\n\n/**\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\n@Slf4j\npublic class JobWrapper implements StatelessJob {\n\n    @Serial\n    private static final long serialVersionUID = 7145451610635400449L;\n\n    private final StatelessJob statelessJob;\n\n    private final SEvent jobExecuting;\n\n    private final SEvent jobCompleted;\n\n    private final EventService eventService;\n\n    private final JobIdentifier jobIdentifier;\n\n    private final TransactionService transactionService;\n\n    private final PersistenceService persistenceService;\n\n    private final JobService jobService;\n\n    public JobWrapper(final JobIdentifier jobIdentifier, final StatelessJob statelessJob,\n            final EventService eventService, final TransactionService transactionService,\n            PersistenceService persistenceService, JobService jobService) {\n        this.jobIdentifier = jobIdentifier;\n        this.statelessJob = statelessJob;\n        this.eventService = eventService;\n        this.transactionService = transactionService;\n        this.persistenceService = persistenceService;\n        this.jobService = jobService;\n        jobExecuting = new SEvent(JOB_EXECUTING);\n        jobCompleted = new SEvent(JOB_COMPLETED);\n    }\n\n    @Override\n    public String getName() {\n        return jobIdentifier.getJobName();\n    }\n\n    @Override\n    public String getDescription() {\n        return statelessJob.getDescription();\n    }\n\n    @Override\n    public void execute() throws SJobExecutionException, SFireEventException {\n        try {\n            if (eventService.hasHandlers(JOB_EXECUTING, null)) {\n                jobExecuting.setObject(this);\n                eventService.fireEvent(jobExecuting);\n            }\n\n            log.debug(\"Start execution of {}\", statelessJob.getName());\n            statelessJob.execute();\n            //make sure hibernate flush everything we did before going back to quartz code\n            persistenceService.flushStatements();\n\n            log.debug(\"Finished execution of {}\", statelessJob.getName());\n        } catch (final SRetryableException e) {\n            throw e;\n        } catch (final Throwable e) {\n            handleFailure(e);\n            //throw an exception: if it's a \"one shot\" timer it should delete the timer trigger only if job succeeded (see TimerEventTriggerJobListener)\n            throw new SJobExecutionException(e);\n        } finally {\n            if (eventService.hasHandlers(JOB_COMPLETED, null)) {\n                jobCompleted.setObject(this);\n                eventService.fireEvent(jobCompleted);\n            }\n        }\n    }\n\n    void handleFailure(Throwable e) {\n        log.error(\"Error while executing job {} : {}\", jobIdentifier, e.getMessage(), e);\n        try {\n            registerFailInAnOtherThread(e, jobIdentifier);\n            transactionService.setRollbackOnly();\n        } catch (STransactionException | STransactionNotFoundException e1) {\n            log.error(\"Unable to rollback transaction after fail on job  {}\", jobIdentifier.getId(), e);\n        }\n    }\n\n    private void registerFailInAnOtherThread(final Throwable jobException, final JobIdentifier jobIdentifier)\n            throws STransactionNotFoundException {\n        transactionService.registerBonitaSynchronization((BonitaTransactionSynchronization) txState -> {\n            Thread thread = new Thread(() -> {\n                try {\n                    transactionService.executeInTransaction(() -> {\n                        jobService.logJobError(jobException, jobIdentifier.getId());\n                        return null;\n                    });\n                } catch (Exception e) {\n                    log.error(\"Error while registering the error for the job {}\", jobIdentifier.getId(), e);\n                    log.error(\"job exception was \", jobException);\n                }\n            }, \"Job error handler\");\n\n            thread.start();\n\n            try {\n                thread.join();\n            } catch (InterruptedException e) {\n                log.error(\"Thread to log error on job {} interrupted\", jobIdentifier.getId(), e);\n            }\n        });\n    }\n\n    @Override\n    public void setAttributes(final Map<String, Serializable> attributes) throws SJobConfigurationException {\n        statelessJob.setAttributes(attributes);\n    }\n\n    public StatelessJob getStatelessJob() {\n        return statelessJob;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/impl/MonitoringJobListener.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.impl;\n\nimport java.io.Serializable;\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport io.micrometer.core.instrument.Counter;\nimport io.micrometer.core.instrument.Gauge;\nimport io.micrometer.core.instrument.MeterRegistry;\nimport org.bonitasoft.engine.scheduler.BonitaJobListener;\n\npublic class MonitoringJobListener implements BonitaJobListener {\n\n    private static final long serialVersionUID = 2830540082890033377L;\n\n    public static final String JOB_JOBS_RUNNING = \"bonita.bpmengine.job.running\";\n    public static final String JOB_JOBS_EXECUTED = \"bonita.bpmengine.job.executed\";\n\n    private AtomicLong runningJobs;\n    private Counter executedCounter;\n\n    private final MeterRegistry meterRegistry;\n\n    public MonitoringJobListener(MeterRegistry meterRegistry) {\n        this.meterRegistry = meterRegistry;\n    }\n\n    @Override\n    public void jobToBeExecuted() {\n        initializeOrGetRunningJob().incrementAndGet();\n    }\n\n    private AtomicLong initializeOrGetRunningJob() {\n        if (runningJobs == null) {\n            synchronized (this) {\n                runningJobs = new AtomicLong();\n                Gauge.builder(JOB_JOBS_RUNNING, runningJobs, AtomicLong::get)\n                        .baseUnit(\"jobs\")\n                        .description(\"Number of jobs currently running\")\n                        .register(meterRegistry);\n            }\n        }\n        return runningJobs;\n    }\n\n    private Counter initializeOrGetExecutedJobs() {\n        if (executedCounter == null) {\n            synchronized (this) {\n                executedCounter = meterRegistry.counter(JOB_JOBS_EXECUTED);\n            }\n        }\n        return executedCounter;\n    }\n\n    @Override\n    public void jobExecutionVetoed() {\n        initializeOrGetRunningJob().decrementAndGet();\n    }\n\n    @Override\n    public void jobWasExecuted(final Map<String, Serializable> context, final Exception jobException) {\n        initializeOrGetRunningJob().decrementAndGet();\n        initializeOrGetExecutedJobs().increment();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/impl/NonConcurrentQuartzJob.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.impl;\n\nimport org.quartz.DisallowConcurrentExecution;\n\n@DisallowConcurrentExecution\npublic class NonConcurrentQuartzJob extends AbstractQuartzJob {\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/impl/QuartzJobListener.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.impl;\n\nimport static org.bonitasoft.engine.scheduler.BonitaJobListener.*;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.Callable;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.scheduler.BonitaJobListener;\nimport org.bonitasoft.engine.scheduler.StatelessJob;\nimport org.bonitasoft.engine.scheduler.model.SJobData;\nimport org.bonitasoft.engine.scheduler.model.impl.SJobDataImpl;\nimport org.quartz.Job;\nimport org.quartz.JobDetail;\nimport org.quartz.JobExecutionContext;\nimport org.quartz.JobExecutionException;\nimport org.quartz.JobKey;\nimport org.quartz.JobListener;\nimport org.quartz.Trigger;\nimport org.quartz.TriggerKey;\n\n@Slf4j\npublic class QuartzJobListener implements JobListener {\n\n    private final List<BonitaJobListener> bonitaJobListeners;\n\n    QuartzJobListener(final List<BonitaJobListener> bonitaJobListeners) {\n        this.bonitaJobListeners = bonitaJobListeners;\n    }\n\n    @Override\n    public String getName() {\n        return \"QuartzJobListener\";\n    }\n\n    List<BonitaJobListener> getBonitaJobListeners() {\n        return bonitaJobListeners;\n    }\n\n    @Override\n    public void jobToBeExecuted(final JobExecutionContext context) {\n        executeCallable(() -> {\n            for (final BonitaJobListener bonitaJobListener : bonitaJobListeners) {\n                bonitaJobListener.jobToBeExecuted();\n            }\n            return null;\n        });\n    }\n\n    @Override\n    public void jobExecutionVetoed(final JobExecutionContext context) {\n        executeCallable(() -> {\n            for (final BonitaJobListener bonitaJobListener : bonitaJobListeners) {\n                bonitaJobListener.jobExecutionVetoed();\n            }\n            return null;\n        });\n    }\n\n    @Override\n    public void jobWasExecuted(final JobExecutionContext context, final JobExecutionException jobException) {\n        final Map<String, Serializable> mapContext = buildMapContext(context);\n        executeCallable(() -> {\n            for (final BonitaJobListener bonitaJobListener : bonitaJobListeners) {\n                bonitaJobListener.jobWasExecuted(mapContext, jobException);\n            }\n            return null;\n        });\n    }\n\n    private void executeCallable(Callable<Void> callable) {\n        try {\n            callable.call();\n        } catch (Throwable e) {\n            log.warn(\"Unable to execute job listener\", e);\n        }\n    }\n\n    private Long getJobDescriptorId(final JobDetail jobDetail) {\n        return Long.valueOf((String) jobDetail.getJobDataMap().getWrappedMap().get(\"jobId\"));\n    }\n\n    private StatelessJob getBosJob(final JobExecutionContext context) {\n        final Job instance = context.getJobInstance();\n        if (instance instanceof AbstractQuartzJob job) {\n            return job.getBosJob();\n        }\n        return null;\n    }\n\n    private List<SJobData> getJobDataValueAndType(final JobDetail jobDetail) {\n        final Set<Map.Entry<String, Object>> entries = jobDetail.getJobDataMap().getWrappedMap().entrySet();\n        final List<SJobData> jobDatas = new ArrayList<SJobData>(entries.size());\n        for (final Map.Entry<String, Object> entry : entries) {\n            jobDatas.add(new SJobDataImpl(entry));\n        }\n        return jobDatas;\n    }\n\n    private String getJobType(final Job job) {\n        final String jobType;\n        final Class<? extends Job> jobClass = job.getClass();\n        if (AbstractQuartzJob.class.isAssignableFrom(jobClass)) {\n            final StatelessJob bosJob = ((AbstractQuartzJob) job).getBosJob();\n            if (bosJob != null) {\n                if (bosJob instanceof JobWrapper) {\n                    jobType = ((JobWrapper) bosJob).getStatelessJob().getClass().getName();\n                } else {\n                    jobType = bosJob.getClass().getName();\n                }\n            } else {\n                return \"null\";\n            }\n        } else {\n            jobType = jobClass.getName();\n        }\n        return jobType;\n    }\n\n    private Map<String, Serializable> buildMapContext(final JobExecutionContext context) {\n        final JobDetail jobDetail = context.getJobDetail();\n        final Trigger trigger = context.getTrigger();\n        final TriggerKey triggerKey = trigger.getKey();\n        final JobKey jobKey = jobDetail.getKey();\n\n        final Map<String, Serializable> mapContext = new HashMap<>();\n        mapContext.put(BOS_JOB, getBosJob(context));\n        mapContext.put(JOB_DESCRIPTOR_ID, getJobDescriptorId(jobDetail));\n        mapContext.put(JOB_TYPE, getJobType(context.getJobInstance()));\n        mapContext.put(JOB_NAME, jobKey.getName());\n        mapContext.put(JOB_GROUP, jobKey.getGroup());\n        mapContext.put(TRIGGER_NAME, triggerKey.getName());\n        mapContext.put(TRIGGER_GROUP, triggerKey.getGroup());\n        mapContext.put(TRIGGER_PREVIOUS_FIRE_TIME, trigger.getPreviousFireTime());\n        mapContext.put(TRIGGER_NEXT_FIRE_TIME, trigger.getNextFireTime());\n        mapContext.put(REFIRE_COUNT, context.getRefireCount());\n        mapContext.put(JOB_DATAS, (Serializable) getJobDataValueAndType(jobDetail));\n        mapContext.put(JOB_RESULT, String.valueOf(context.getResult()));\n        return mapContext;\n    }\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/impl/QuartzSchedulerExecutor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.impl;\n\nimport static org.quartz.JobKey.jobKey;\nimport static org.quartz.impl.matchers.GroupMatcher.anyJobGroup;\n\nimport java.time.Instant;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.UUID;\n\nimport javax.transaction.Status;\n\nimport org.bonitasoft.engine.commons.ExceptionUtils;\nimport org.bonitasoft.engine.scheduler.BonitaJobListener;\nimport org.bonitasoft.engine.scheduler.SchedulerExecutor;\nimport org.bonitasoft.engine.scheduler.exception.SSchedulerException;\nimport org.bonitasoft.engine.scheduler.trigger.CronTrigger;\nimport org.bonitasoft.engine.scheduler.trigger.Trigger;\nimport org.bonitasoft.engine.transaction.BonitaTransactionSynchronization;\nimport org.bonitasoft.engine.transaction.STransactionNotFoundException;\nimport org.bonitasoft.engine.transaction.TransactionService;\nimport org.quartz.CronScheduleBuilder;\nimport org.quartz.JobBuilder;\nimport org.quartz.JobDetail;\nimport org.quartz.JobKey;\nimport org.quartz.ListenerManager;\nimport org.quartz.Scheduler;\nimport org.quartz.SchedulerException;\nimport org.quartz.Trigger.TriggerState;\nimport org.quartz.TriggerBuilder;\nimport org.quartz.TriggerKey;\nimport org.quartz.core.QuartzScheduler;\nimport org.quartz.impl.matchers.GroupMatcher;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Matthieu Chaffotte\n * @author Yanyan Liu\n * @author Celine Souchet\n */\npublic class QuartzSchedulerExecutor implements SchedulerExecutor {\n\n    private final Logger logger = LoggerFactory.getLogger(QuartzSchedulerExecutor.class);\n\n    private Scheduler scheduler;\n\n    private final BonitaSchedulerFactory schedulerFactory;\n\n    private final TransactionService transactionService;\n\n    private final boolean useOptimization;\n\n    private QuartzScheduler quartzScheduler;\n\n    private List<BonitaJobListener> jobListeners = new ArrayList<>();\n\n    public QuartzSchedulerExecutor(final BonitaSchedulerFactory schedulerFactory,\n            final TransactionService transactionService, final boolean useOptimization) {\n        this.transactionService = transactionService;\n        this.useOptimization = useOptimization;\n        this.schedulerFactory = schedulerFactory;\n    }\n\n    // autowired\n    public void setJobListeners(List<BonitaJobListener> jobListeners) {\n        this.jobListeners = jobListeners;\n    }\n\n    @Override\n    public void setBOSSchedulerService(final SchedulerServiceImpl schedulerService) {\n        schedulerFactory.setBOSSchedulerService(schedulerService);\n    }\n\n    @Override\n    public void schedule(final long jobId, final String jobName, final Trigger trigger,\n            final boolean disallowConcurrentExecution)\n            throws SSchedulerException {\n        try {\n            checkSchedulerState();\n            final JobDetail jobDetail = createJobDetails(jobId, jobName, disallowConcurrentExecution);\n            final JobKey jobKey = jobDetail.getKey();\n            final org.quartz.Trigger quartzTrigger = getQuartzTrigger(trigger, jobKey.getName());\n            scheduler.scheduleJob(jobDetail, quartzTrigger);\n            if (useOptimization) {\n                transactionService.registerBonitaSynchronization(\n                        new NotifyQuartzOfNewTrigger(trigger.getStartDate().getTime(), quartzScheduler));\n            }\n        } catch (final Exception e) {\n            throw new SSchedulerException(e);\n        }\n    }\n\n    private static final class NotifyQuartzOfNewTrigger implements BonitaTransactionSynchronization {\n\n        private final long time;\n\n        private final QuartzScheduler quartzScheduler;\n\n        public NotifyQuartzOfNewTrigger(final long time, final QuartzScheduler quartzScheduler) {\n            super();\n            this.time = time;\n            this.quartzScheduler = quartzScheduler;\n        }\n\n        @Override\n        public void afterCompletion(final int txState) {\n            if (Status.STATUS_COMMITTED == txState) {\n                if (quartzScheduler != null) {\n                    quartzScheduler.getSchedulerSignaler().signalSchedulingChange(time);\n                }\n            }\n        }\n    }\n\n    private JobDetail createJobDetails(final long jobId, final String jobName,\n            final boolean disallowConcurrentExecution) {\n        Class<? extends AbstractQuartzJob> jobClass;\n        if (disallowConcurrentExecution) {\n            jobClass = NonConcurrentQuartzJob.class;\n        } else {\n            jobClass = ConcurrentQuartzJob.class;\n        }\n        final JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName).build();\n        jobDetail.getJobDataMap().put(\"jobId\", String.valueOf(jobId));\n        jobDetail.getJobDataMap().put(\"jobName\", jobName);\n        return jobDetail;\n    }\n\n    @Override\n    public void executeAgain(final long jobId, final String jobName, final boolean disallowConcurrentExecution,\n            int delayInMillis) throws SSchedulerException {\n        checkSchedulerState();\n        try {\n            JobDetail jobDetail = scheduler.getJobDetail(new JobKey(jobName));\n            if (jobDetail == null) {\n                logger.debug(\"Re-execute job {} named {}, there was no quartz job and no triggers left \" +\n                        \"(one shot triggered that failed and was deleted)\", jobId, jobName);\n                // The quartz job itself was deleted because the trigger that failed was the only one and was a one shot trigger\n                scheduler.scheduleJob(createJobDetails(jobId, jobName, disallowConcurrentExecution),\n                        createOneShotTrigger(jobName, delayInMillis));\n            } else {\n                List<? extends org.quartz.Trigger> triggersOfJob = scheduler.getTriggersOfJob(jobDetail.getKey());\n                // We retrieve the first trigger that will not fire again\n                Optional<? extends org.quartz.Trigger> firstTriggerThatWillNotFire = triggersOfJob.stream()\n                        .filter(t -> !t.mayFireAgain()).findFirst();\n                if (firstTriggerThatWillNotFire.isPresent()) {\n                    // if there is one, we reschedule the job by replacing it\n                    logger.debug(\"Re-execute job {} named {}, reuse existing trigger {} because it will not fire again.\"\n                            + \"(most likely a one shot trigger that failed and was not correctly deleted)\",\n                            jobId, jobName, firstTriggerThatWillNotFire.get());\n                    scheduler.rescheduleJob(firstTriggerThatWillNotFire.get().getKey(),\n                            createOneShotTrigger(jobName, delayInMillis));\n                } else {\n                    // in the other case we create a new trigger to schedule the job (it means other triggers are likely cron triggers)\n                    logger.debug(\"Re-execute job {} named {}, create a new trigger for that. \" +\n                            \"(The job that failed was most likely triggered by a cron trigger)\", jobId, jobName);\n                    scheduler.scheduleJob(createOneShotTrigger(jobName, delayInMillis));\n                }\n            }\n            if (useOptimization) {\n                transactionService.registerBonitaSynchronization(\n                        new NotifyQuartzOfNewTrigger(System.currentTimeMillis(), quartzScheduler));\n            }\n        } catch (final Exception e) {\n            throw new SSchedulerException(e);\n        }\n    }\n\n    private org.quartz.Trigger createOneShotTrigger(String jobName, int delayInMillis) {\n        return TriggerBuilder.newTrigger()\n                .withIdentity(\"OneShotTrigger\" + UUID.randomUUID().getLeastSignificantBits())\n                .forJob(jobName)\n                .startAt(new Date(Instant.now().plusMillis(delayInMillis).toEpochMilli())).build();\n    }\n\n    org.quartz.Trigger getQuartzTrigger(final Trigger trigger, final String jobName) {\n        final TriggerBuilder<? extends org.quartz.Trigger> triggerBuilder;\n        final TriggerBuilder<org.quartz.Trigger> base = TriggerBuilder.newTrigger().forJob(jobName)\n                .withIdentity(trigger.getName())\n                .startNow();\n        if (trigger instanceof CronTrigger cronTrigger) {\n            final CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder\n                    .cronSchedule(cronTrigger.getExpression());\n            switch (cronTrigger.getMisfireHandlingPolicy()) {\n                case NONE:\n                    cronScheduleBuilder.withMisfireHandlingInstructionDoNothing();\n                    break;\n                case ALL:\n                    cronScheduleBuilder.withMisfireHandlingInstructionIgnoreMisfires();\n                    break;\n                case ONE:\n                    cronScheduleBuilder.withMisfireHandlingInstructionFireAndProceed();\n                    break;\n                default:\n                    throw new IllegalStateException();\n            }\n            triggerBuilder = base.withSchedule(cronScheduleBuilder).endAt(cronTrigger.getEndDate());\n        } else {\n            triggerBuilder = base.startAt(trigger.getStartDate());\n        }\n        return triggerBuilder.withPriority(trigger.getPriority()).build();\n    }\n\n    @Override\n    public boolean isStarted() throws SSchedulerException {\n        try {\n            return scheduler != null && scheduler.isStarted() && !scheduler.isShutdown();\n        } catch (final SchedulerException e) {\n            throw new SSchedulerException(e);\n        }\n    }\n\n    @Override\n    public boolean isShutdown() throws SSchedulerException {\n        try {\n            return scheduler != null && scheduler.isShutdown();\n        } catch (final SchedulerException e) {\n            throw new SSchedulerException(e);\n        }\n    }\n\n    @Override\n    public void start() throws SSchedulerException {\n        try {\n            if (isStarted()) {\n                throw new SSchedulerException(\"The scheduler is already started.\");\n            }\n            if (scheduler == null || scheduler.isShutdown()) {\n                try {\n                    scheduler = schedulerFactory.getScheduler();\n                } catch (final SchedulerException e) {\n                    throw new SSchedulerException(e);\n                }\n            }\n\n            scheduler.start();\n            addListeners();\n            if (useOptimization) {\n                if (scheduler instanceof BonitaScheduler) {\n                    quartzScheduler = ((BonitaScheduler) scheduler).getQuartzScheduler();\n                } else {\n                    logger.warn(\n                            \"Cannot access the QuartzScheduler implementation from {}. Scheduler optimization is disabled.\",\n                            scheduler.getClass().getName());\n                }\n            }\n\n        } catch (final SchedulerException e) {\n            throw new SSchedulerException(e);\n        }\n    }\n\n    @Override\n    public void shutdown() throws SSchedulerException {\n        try {\n            if (scheduler != null && !scheduler.isShutdown()) {\n                scheduler.shutdown(true);\n            }\n        } catch (final SchedulerException e) {\n            throw new SSchedulerException(e);\n        }\n    }\n\n    private void checkSchedulerState() throws SSchedulerException {\n        try {\n            if (scheduler == null || scheduler.isShutdown()) {\n                throw new SSchedulerException(\"The scheduler is not started\");\n            }\n            if (!transactionService.isTransactionActive()) {\n                throw new SSchedulerException(\"The scheduler cannot be used without opening a transaction first\");\n            }\n        } catch (SchedulerException e) {\n            throw new SSchedulerException(\"The scheduler is not started\", e);\n        }\n    }\n\n    @Override\n    public boolean delete(final String jobName) throws SSchedulerException {\n        try {\n            checkSchedulerState();\n            final JobKey jobKey = jobKey(jobName);\n            scheduler.pauseJob(jobKey);\n            return scheduler.deleteJob(jobKey);\n        } catch (final SchedulerException e) {\n            throw new SSchedulerException(e);\n        }\n    }\n\n    @Override\n    public void deleteJobs() throws SSchedulerException {\n        try {\n            checkSchedulerState();\n            final Set<JobKey> jobNames = scheduler.getJobKeys(anyJobGroup());\n            for (final JobKey jobKey : jobNames) {\n                scheduler.pauseJob(jobKey);\n                scheduler.deleteJob(jobKey);\n            }\n        } catch (final SchedulerException e) {\n            throw new SSchedulerException(e);\n        }\n    }\n\n    @Override\n    public boolean isExistingJob(final String jobName) throws SSchedulerException {\n        try {\n            checkSchedulerState();\n            final JobKey jobKey = jobKey(jobName);\n            return scheduler.getJobDetail(jobKey) != null;\n        } catch (final SchedulerException e) {\n            throw new SSchedulerException(e);\n        }\n    }\n\n    @Override\n    public List<String> getJobs() throws SSchedulerException {\n        try {\n            checkSchedulerState();\n            final Set<JobKey> jobKeys = scheduler.getJobKeys(anyJobGroup());\n            final List<String> jobsNames = new ArrayList<>(jobKeys.size());\n            for (final JobKey jobKey : jobKeys) {\n                jobsNames.add(jobKey.getName());\n            }\n            return jobsNames;\n        } catch (final SchedulerException e) {\n            throw new SSchedulerException(e);\n        }\n    }\n\n    @Override\n    public boolean mayFireAgain(final String jobName) throws SSchedulerException {\n        try {\n            checkSchedulerState();\n            List<? extends org.quartz.Trigger> triggersOfJob = scheduler\n                    .getTriggersOfJob(new JobKey(jobName));\n            return triggersOfJob.stream()\n                    .anyMatch(org.quartz.Trigger::mayFireAgain);\n        } catch (final SchedulerException e) {\n            throw new SSchedulerException(e);\n        }\n    }\n\n    @Override\n    public void rescheduleErroneousTriggers() throws SSchedulerException {\n        checkSchedulerState();\n        try {\n            for (final TriggerKey triggerKey : scheduler.getTriggerKeys(GroupMatcher.anyTriggerGroup())) {\n                if (TriggerState.ERROR.equals(scheduler.getTriggerState(triggerKey))) {\n                    scheduler.pauseTrigger(triggerKey);\n                    scheduler.resumeTrigger(triggerKey);\n                }\n            }\n        } catch (final SchedulerException e) {\n            throw new SSchedulerException(e);\n        }\n    }\n\n    @Override\n    public void pauseJobs() throws SSchedulerException {\n        checkSchedulerState();\n        try {\n            scheduler.pauseTriggers(GroupMatcher.anyTriggerGroup());\n        } catch (final SchedulerException e) {\n            throw new SSchedulerException(\"Unable to put jobs in pause\", e);\n        }\n    }\n\n    @Override\n    public void resumeJobs() throws SSchedulerException {\n        checkSchedulerState();\n        try {\n            scheduler.resumeTriggers(GroupMatcher.anyTriggerGroup());\n        } catch (final SchedulerException e) {\n            throw new SSchedulerException(\"Unable to resume jobs\", e);\n        }\n    }\n\n    @Override\n    public Date rescheduleJob(final String triggerName, final Date triggerStartTime)\n            throws SSchedulerException {\n        checkSchedulerState();\n        final TriggerKey triggerKey = new TriggerKey(triggerName);\n        try {\n            final org.quartz.Trigger oldTrigger = scheduler.getTrigger(triggerKey);\n            final org.quartz.Trigger newTrigger = oldTrigger.getTriggerBuilder().startAt(triggerStartTime).build();\n            Date date = scheduler.rescheduleJob(triggerKey, newTrigger);\n            if (useOptimization) {\n                try {\n                    transactionService.registerBonitaSynchronization(\n                            new NotifyQuartzOfNewTrigger(triggerStartTime.getTime(), quartzScheduler));\n                } catch (STransactionNotFoundException e) {\n                    logger.error(\"Unable to register synchronization to optimize Quartz rescheduling, {}\",\n                            ExceptionUtils.printLightWeightStacktrace(e));\n                }\n            }\n            return date;\n        } catch (final SchedulerException e) {\n            throw new SSchedulerException(\"Can't get the trigger \" + triggerKey, e);\n        }\n    }\n\n    // For tests only\n    public Set<String> getPausedTriggerGroups() throws SSchedulerException {\n        try {\n            return quartzScheduler.getPausedTriggerGroups();\n        } catch (SchedulerException e) {\n            throw new SSchedulerException(e);\n        }\n    }\n\n    private void addListeners() throws SSchedulerException {\n        try {\n            final ListenerManager listenerManager = scheduler.getListenerManager();\n            listenerManager.addJobListener(new QuartzJobListener(jobListeners));\n        } catch (final SchedulerException e) {\n            throw new SSchedulerException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/impl/SchedulerServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.impl;\n\nimport java.io.Serializable;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.Callable;\nimport java.util.stream.Collectors;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException;\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.events.model.SEvent;\nimport org.bonitasoft.engine.events.model.SFireEventException;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.scheduler.JobIdentifier;\nimport org.bonitasoft.engine.scheduler.JobService;\nimport org.bonitasoft.engine.scheduler.SchedulerExecutor;\nimport org.bonitasoft.engine.scheduler.SchedulerService;\nimport org.bonitasoft.engine.scheduler.StatelessJob;\nimport org.bonitasoft.engine.scheduler.exception.SSchedulerException;\nimport org.bonitasoft.engine.scheduler.exception.jobLog.SJobLogDeletionException;\nimport org.bonitasoft.engine.scheduler.model.SJobDescriptor;\nimport org.bonitasoft.engine.scheduler.model.SJobParameter;\nimport org.bonitasoft.engine.scheduler.trigger.Trigger;\nimport org.bonitasoft.engine.service.ServicesResolver;\nimport org.bonitasoft.engine.services.PersistenceService;\nimport org.bonitasoft.engine.transaction.TransactionService;\n\n/**\n * @author Matthieu Chaffotte\n * @author Baptiste Mesta\n * @author Elias Ricken de Medeiros\n * @author Yanyan Liu\n * @author Celine Souchet\n */\n@Slf4j\npublic class SchedulerServiceImpl implements SchedulerService {\n\n    private final SchedulerExecutor schedulerExecutor;\n\n    private final JobService jobService;\n\n    private final EventService eventService;\n\n    private final SEvent schedulerStarted;\n\n    private final SEvent schedulerStopped;\n\n    private final SEvent jobFailed;\n\n    private final TransactionService transactionService;\n\n    private final ServicesResolver servicesResolver;\n\n    private final PersistenceService persistenceService;\n\n    /**\n     * Create a new instance of scheduler service.\n     */\n    public SchedulerServiceImpl(final SchedulerExecutor schedulerExecutor, final JobService jobService,\n            final EventService eventService, final TransactionService transactionService,\n            final ServicesResolver servicesResolver, final PersistenceService persistenceService) {\n        this.schedulerExecutor = schedulerExecutor;\n        this.jobService = jobService;\n        this.servicesResolver = servicesResolver;\n        this.persistenceService = persistenceService;\n        schedulerStarted = new SEvent(SCHEDULER_STARTED);\n        schedulerStopped = new SEvent(SCHEDULER_STOPPED);\n        jobFailed = new SEvent(JOB_FAILED);\n        this.eventService = eventService;\n        this.transactionService = transactionService;\n        schedulerExecutor.setBOSSchedulerService(this);\n    }\n\n    @Override\n    public void schedule(final SJobDescriptor jobDescriptor, final Trigger trigger) throws SSchedulerException {\n        final SJobDescriptor createdJobDescriptor = createJobDescriptor(jobDescriptor, Collections.emptyList());\n        internalSchedule(createdJobDescriptor, trigger);\n    }\n\n    @Override\n    public void schedule(final SJobDescriptor jobDescriptor, final List<SJobParameter> parameters,\n            final Trigger trigger) throws SSchedulerException {\n        if (trigger == null) {\n            throw new SSchedulerException(\"The trigger is null\");\n        }\n        final SJobDescriptor createdJobDescriptor = createJobDescriptor(jobDescriptor, parameters);\n        internalSchedule(createdJobDescriptor, trigger);\n    }\n\n    @Override\n    public void executeAgain(final long jobDescriptorId, int delayInMillis) throws SSchedulerException {\n        final SJobDescriptor jobDescriptor = jobService.getJobDescriptor(jobDescriptorId);\n        schedulerExecutor.executeAgain(jobDescriptorId, jobDescriptor.getJobName(),\n                false, delayInMillis);\n    }\n\n    @Override\n    public void retryJobThatFailed(long jobDescriptorId) throws SSchedulerException {\n        final SJobDescriptor jobDescriptor = jobService.getJobDescriptor(jobDescriptorId);\n        deleteFailedJobs(jobDescriptorId);\n        schedulerExecutor.executeAgain(jobDescriptorId, jobDescriptor.getJobName(),\n                false, 0);\n    }\n\n    @Override\n    public void retryJobThatFailed(final long jobDescriptorId, final List<SJobParameter> parameters)\n            throws SSchedulerException {\n        final SJobDescriptor jobDescriptor = jobService.getJobDescriptor(jobDescriptorId);\n        jobService.setJobParameters(jobDescriptor.getId(), parameters);\n        deleteFailedJobs(jobDescriptorId);\n        schedulerExecutor.executeAgain(jobDescriptorId, jobDescriptor.getJobName(),\n                false, 0);\n    }\n\n    private void deleteFailedJobs(long jobDescriptorId) throws SSchedulerException {\n        try {\n            jobService.deleteJobLogs(jobDescriptorId);\n        } catch (SJobLogDeletionException | SBonitaReadException e) {\n            throw new SSchedulerException(\"Unable to delete failed jobs logs\", e);\n        }\n    }\n\n    private SJobDescriptor createJobDescriptor(final SJobDescriptor sJobDescriptor,\n            final List<SJobParameter> parameters) throws SSchedulerException {\n        try {\n            final SJobDescriptor createdJobDescriptor = jobService.createJobDescriptor(sJobDescriptor);\n            jobService.createJobParameters(parameters, createdJobDescriptor.getId());\n            return createdJobDescriptor;\n        } catch (final SBonitaException sbe) {\n            throw new SSchedulerException(sbe);\n        }\n    }\n\n    private void internalSchedule(final SJobDescriptor jobDescriptor, final Trigger trigger)\n            throws SSchedulerException {\n        try {\n            schedulerExecutor.schedule(jobDescriptor.getId(), jobDescriptor.getJobName(), trigger,\n                    false);\n        } catch (final Throwable e) {\n            log.error(\"\", e);\n            try {\n                eventService.fireEvent(jobFailed);\n            } catch (final SFireEventException e1) {\n                log.error(\"\", e1);\n            }\n            throw new SSchedulerException(e);\n        }\n    }\n\n    @Override\n    public boolean isStarted() throws SSchedulerException {\n        return schedulerExecutor.isStarted();\n    }\n\n    @Override\n    public boolean isStopped() throws SSchedulerException {\n        return schedulerExecutor.isShutdown();\n    }\n\n    @Override\n    public void start() throws SSchedulerException, SFireEventException {\n        log.info(\"Start scheduler\");\n        schedulerExecutor.start();\n        eventService.fireEvent(schedulerStarted);\n    }\n\n    @Override\n    public void stop() throws SSchedulerException, SFireEventException {\n        schedulerExecutor.shutdown();\n        eventService.fireEvent(schedulerStopped);\n    }\n\n    @Override\n    public void pauseJobs() throws SSchedulerException {\n        schedulerExecutor.pauseJobs();\n    }\n\n    @Override\n    public void resumeJobs() throws SSchedulerException {\n        schedulerExecutor.resumeJobs();\n    }\n\n    @Override\n    public boolean delete(final String jobName) throws SSchedulerException {\n        final boolean delete = schedulerExecutor.delete(jobName);\n        jobService.deleteJobDescriptorByJobName(jobName);\n        return delete;\n    }\n\n    @Override\n    public void deleteJobs() throws SSchedulerException {\n        schedulerExecutor.deleteJobs();\n        jobService.deleteAllJobDescriptors();\n    }\n\n    @Override\n    public List<String> getJobs() throws SSchedulerException {\n        return schedulerExecutor.getJobs();\n    }\n\n    /**\n     * Get the persisted job from the database inside its own transaction.\n     *\n     * @return the newly created job\n     * @throws SSchedulerException if the job cannot be created successfully\n     */\n    StatelessJob getPersistedJob(final JobIdentifier jobIdentifier) throws SSchedulerException {\n        try {\n            return transactionService.executeInTransaction(new PersistedJobCallable(jobIdentifier));\n        } catch (final Exception e) {\n            throw new SSchedulerException(e);\n        }\n    }\n\n    private class PersistedJobCallable implements Callable<JobWrapper> {\n\n        private final JobIdentifier jobIdentifier;\n\n        PersistedJobCallable(final JobIdentifier jobIdentifier) {\n            this.jobIdentifier = jobIdentifier;\n        }\n\n        @Override\n        public JobWrapper call() throws Exception {\n            final SJobDescriptor sJobDescriptor = jobService.getJobDescriptor(jobIdentifier.getId());\n            if (sJobDescriptor == null) {\n                throw new SObjectNotFoundException(String\n                        .format(\"The job %s does not exist anymore. It might be already executed\", jobIdentifier));\n            }\n            final String jobClassName = sJobDescriptor.getJobClassName();\n            final Class<?> jobClass = Class.forName(jobClassName);\n            final StatelessJob statelessJob = (StatelessJob) jobClass.newInstance();\n\n            Map<String, Serializable> parameters = jobService.getJobParameters(jobIdentifier.getId())\n                    .stream()\n                    .collect(Collectors.toMap(SJobParameter::getKey, SJobParameter::getValue));\n            parameters.put(StatelessJob.JOB_DESCRIPTOR_ID, jobIdentifier.getId());\n            statelessJob.setAttributes(parameters);\n            if (servicesResolver != null) {\n                servicesResolver.injectServices(statelessJob);\n            }\n            return new JobWrapper(jobIdentifier, statelessJob, eventService, transactionService, persistenceService,\n                    jobService);\n        }\n    }\n\n    @Override\n    public void pause() throws SBonitaException {\n        pauseJobs();\n    }\n\n    @Override\n    public void resume() throws SBonitaException {\n        resumeJobs();\n    }\n\n    @Override\n    public void rescheduleErroneousTriggers() throws SSchedulerException {\n        schedulerExecutor.rescheduleErroneousTriggers();\n    }\n\n    @Override\n    public Date rescheduleJob(final String triggerName, final Date triggerStartTime)\n            throws SSchedulerException {\n        return schedulerExecutor.rescheduleJob(triggerName, triggerStartTime);\n    }\n\n    @Override\n    public boolean isExistingJob(final String jobName) throws SSchedulerException {\n        return schedulerExecutor.isExistingJob(jobName);\n    }\n\n    @Override\n    public boolean mayFireAgain(String jobName) throws SSchedulerException {\n        return schedulerExecutor.mayFireAgain(jobName);\n    }\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/impl/TransactionalSimpleJobFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.impl;\n\nimport org.quartz.Job;\nimport org.quartz.Scheduler;\nimport org.quartz.SchedulerException;\nimport org.quartz.simpl.SimpleJobFactory;\nimport org.quartz.spi.TriggerFiredBundle;\n\n/**\n * Job factory that inject the transaction service\n * Must modify this to inject the configuration service instead\n *\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n * @author Celine Souchet\n */\npublic final class TransactionalSimpleJobFactory extends SimpleJobFactory {\n\n    private final SchedulerServiceImpl schedulerService;\n\n    public TransactionalSimpleJobFactory(final SchedulerServiceImpl schedulerService) {\n        this.schedulerService = schedulerService;\n    }\n\n    @Override\n    public Job newJob(final TriggerFiredBundle bundle, final Scheduler scheduler) throws SchedulerException {\n        final Job newJob = super.newJob(bundle, scheduler);\n        if (newJob instanceof AbstractQuartzJob) {\n            ((AbstractQuartzJob) newJob).setJobDetails(bundle.getJobDetail());\n            ((AbstractQuartzJob) newJob).setSchedulerService(schedulerService);\n        }\n        return newJob;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/model/SFailedJob.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.model;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface SFailedJob {\n\n    long getJobDescriptorId();\n\n    String getJobName();\n\n    String getDescription();\n\n    String getLastMessage();\n\n    int getNumberOfFailures();\n\n    long getLastUpdateDate();\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/model/SJobData.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.model;\n\nimport java.io.Serializable;\n\n/**\n * @author Celine Souchet\n * @version 6.4.0\n * @since 6.4.0\n */\npublic interface SJobData extends Serializable {\n\n    String getKey();\n\n    Object getValue();\n\n    String getClassOfValue();\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/model/SJobDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.model;\n\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@Builder\n@Entity\n@Table(name = \"job_desc\")\npublic class SJobDescriptor implements PersistentObject {\n\n    public static final String JOB_NAME = \"jobName\";\n    public static final String JOB_CLASS_NAME = \"jobClassName\";\n    public static final String ID = \"id\";\n    public static final String DESCRIPTION = \"description\";\n    @Id\n    private long id;\n    private String jobClassName;\n    private String jobName;\n    private String description;\n\n    public SJobDescriptor(final String jobClassName, final String jobName, final String description) {\n        super();\n        this.jobClassName = jobClassName;\n        this.jobName = jobName;\n        this.description = description;\n    }\n\n    public SJobDescriptor(final String jobClassName, final String jobName) {\n        super();\n        this.jobClassName = jobClassName;\n        this.jobName = jobName;\n    }\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/model/SJobLog.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.model;\n\nimport javax.persistence.Cacheable;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Celine Souchet\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@Builder\n@Entity\n@Table(name = \"job_log\")\n@Cacheable(false)\npublic class SJobLog implements PersistentObject {\n\n    @Id\n    private long id;\n    private long jobDescriptorId;\n    private long retryNumber;\n    private Long lastUpdateDate;\n    private String lastMessage;\n\n    public SJobLog(final long jobDescriptorId) {\n        super();\n        this.jobDescriptorId = jobDescriptorId;\n    }\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/model/SJobParameter.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.model;\n\nimport java.io.Serializable;\n\nimport javax.persistence.Cacheable;\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.hibernate.annotations.Type;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@Builder\n@Entity\n@Table(name = \"job_param\")\n@Cacheable(false)\npublic class SJobParameter implements PersistentObject {\n\n    public static final String JOB_DESCRIPTOR_ID = \"jobDescriptorId\";\n    public static final String KEY = \"key\";\n    public static final String VALUE = \"value\";\n    @Id\n    private long id;\n    private long jobDescriptorId;\n    @Column(name = \"key_\")\n    private String key;\n    @Column(name = \"value_\")\n    @Type(type = \"serializable\")\n    private Serializable value;\n\n    public SJobParameter(final String key, final Serializable value) {\n        this.key = key;\n        this.value = value;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/model/impl/SFailedJobImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.model.impl;\n\nimport lombok.Data;\nimport org.bonitasoft.engine.scheduler.model.SFailedJob;\n\n/**\n * @author Matthieu Chaffotte\n */\n@Data\npublic class SFailedJobImpl implements SFailedJob {\n\n    private final long jobDescriptorId;\n    private final String jobName;\n    private final String description;\n    private final int retryNumber;\n    private final long lastUpdateDate;\n    private final String lastMessage;\n\n    public SFailedJobImpl(final long jobDescriptorId, final String jobName, final String description, long retryNumber,\n            final long lastUpdateDate,\n            final String lastMessage) {\n        this.jobDescriptorId = jobDescriptorId;\n        this.jobName = jobName;\n        this.description = description;\n        this.retryNumber = (int) retryNumber;\n        this.lastUpdateDate = lastUpdateDate;\n        this.lastMessage = lastMessage;\n    }\n\n    @Override\n    public int getNumberOfFailures() {\n        // we want the number of failures, not the number of retry\n        return retryNumber + 1;\n    }\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/model/impl/SJobDataImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.model.impl;\n\nimport java.util.Map.Entry;\n\nimport lombok.Data;\nimport org.bonitasoft.engine.scheduler.model.SJobData;\n\n/**\n * @author Celine Souchet\n * @version 6.4.0\n * @since 6.4.0\n */\n@Data\npublic class SJobDataImpl implements SJobData {\n\n    private final String key;\n    private final Object value;\n    final String classOfValue;\n\n    public SJobDataImpl(final Entry<String, Object> jobData) {\n        key = jobData.getKey();\n        value = jobData.getValue();\n        classOfValue = value.getClass().getName();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/recorder/JobDescriptorRecordType.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.recorder;\n\n/**\n * List all possible change of elements of the scheduler service\n *\n * @author Baptiste Mesta\n */\npublic enum JobDescriptorRecordType {\n    addJobDescriptor, addJobParameter,\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/recorder/SelectDescriptorBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.recorder;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.persistence.PersistentObject;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.scheduler.model.SFailedJob;\nimport org.bonitasoft.engine.scheduler.model.SJobDescriptor;\n\n/**\n * @author Celine Souchet\n */\npublic class SelectDescriptorBuilder {\n\n    // FIXME put in a common model\n    public static <T extends PersistentObject> SelectByIdDescriptor<T> getElementById(final Class<T> clazz,\n            final String elementName, final long id) {\n        return new SelectByIdDescriptor<T>(clazz, id);\n    }\n\n    public static SelectListDescriptor<SFailedJob> getFailedJobs(final QueryOptions queryOptions) {\n        final Map<String, Object> parameters = Collections.emptyMap();\n        return new SelectListDescriptor<SFailedJob>(\"getFailedJobs\", parameters, SJobDescriptor.class, queryOptions);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/trigger/CronTrigger.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.trigger;\n\nimport java.util.Date;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface CronTrigger extends Trigger {\n\n    /**\n     * Returns the expression as a unix-like cron expressions\n     * \"* * * * * ?\"\n     * 1. seconds\n     * 2. minutes\n     * 3. hours\n     * 4. day of the month\n     * 5. month\n     * 6 day of the week\n     * 7 year (optional)\n     *\n     * @return a unix-like CRON expression\n     */\n    String getExpression();\n\n    /**\n     * Returns when the trigger ends. It can be null which means that the trigger never ends\n     *\n     * @return\n     */\n    Date getEndDate();\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/trigger/OneShotTrigger.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.trigger;\n\nimport java.util.Date;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class OneShotTrigger implements Trigger {\n\n    private final String name;\n\n    private final Date startDate;\n\n    private final int priority;\n\n    private final MisfireRestartPolicy misfireHandlingPolicy;\n\n    public OneShotTrigger(final String name, final Date startDate, final int priority) {\n        this(name, startDate, priority, MisfireRestartPolicy.ALL);\n    }\n\n    public OneShotTrigger(final String name, final Date startDate, final int priority,\n            final MisfireRestartPolicy misfireHandlingPolicy) {\n        this.name = name;\n        this.startDate = startDate;\n        this.priority = priority;\n        this.misfireHandlingPolicy = misfireHandlingPolicy;\n    }\n\n    public OneShotTrigger(final String name, final Date startDate, final MisfireRestartPolicy misfireHandlingPolicy) {\n        this(name, startDate, 5, misfireHandlingPolicy);\n    }\n\n    public OneShotTrigger(final String name, final Date startDate) {\n        this(name, startDate, 5, MisfireRestartPolicy.ALL);\n    }\n\n    @Override\n    public String getName() {\n        return name;\n    }\n\n    @Override\n    public Date getStartDate() {\n        return startDate;\n    }\n\n    @Override\n    public int getPriority() {\n        return priority;\n    }\n\n    @Override\n    public MisfireRestartPolicy getMisfireHandlingPolicy() {\n        return misfireHandlingPolicy;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/trigger/Trigger.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.trigger;\n\nimport java.util.Date;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic interface Trigger {\n\n    /**\n     * Specify what to do when some job were not triggered in time.\n     */\n    public enum MisfireRestartPolicy {\n        /**\n         * Restart now all instances that were missed\n         */\n        ALL,\n\n        /**\n         * Restart now only one instance that was not trigger and continue normally\n         */\n        ONE,\n\n        /**\n         * No instance that were not trigger in time are restarted\n         */\n        NONE\n    }\n\n    /**\n     * Gets the name of the trigger\n     *\n     * @return the name of the trigger\n     * @since 6.0\n     */\n    String getName();\n\n    /**\n     * Returns when the trigger must start\n     *\n     * @return a date when the trigger must start\n     * @since 6.0\n     */\n    Date getStartDate();\n\n    /**\n     * The trigger of the highest priority will be executed first.\n     *\n     * @return the trigger's priority\n     * @since 6.0\n     */\n    int getPriority();\n\n    /**\n     * Tell the scheduler how to handle jobs that were not executed in time.\n     *\n     * @return the MisfireHandlingPolicy for this trigger\n     */\n    MisfireRestartPolicy getMisfireHandlingPolicy();\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/trigger/UnixCronTrigger.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.trigger;\n\nimport java.util.Date;\n\n/**\n * @author Matthieu Chaffotte\n */\npublic class UnixCronTrigger extends OneShotTrigger implements CronTrigger {\n\n    private final String expression;\n\n    private final Date endDate;\n\n    public UnixCronTrigger(final String name, final Date startDate, final String expression) {\n        super(name, startDate);\n        this.expression = expression;\n        this.endDate = null;\n    }\n\n    public UnixCronTrigger(final String name, final Date startDate, final String expression,\n            final MisfireRestartPolicy misfireHandlingPolicy) {\n        super(name, startDate, misfireHandlingPolicy);\n        this.expression = expression;\n        this.endDate = null;\n    }\n\n    @Override\n    public String getExpression() {\n        return this.expression;\n    }\n\n    @Override\n    public Date getEndDate() {\n        return this.endDate;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/main/resources/org/bonitasoft/engine/scheduler/impl/hibernate/schedulerimpl.queries.hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\"\n                                   \"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd\">\n<hibernate-mapping auto-import=\"false\">\n\n\t<!-- Job Descriptor -->\n\t<query name=\"getNumberOfSJobDescriptor\">\n\t\tSELECT COUNT(jd)\n\t\tFROM org.bonitasoft.engine.scheduler.model.SJobDescriptor AS jd\n\t</query>\n\n\t<query name=\"searchSJobDescriptor\">\n\t\tSELECT jd\n\t\tFROM org.bonitasoft.engine.scheduler.model.SJobDescriptor AS jd\n\t</query>\n\n\t<!-- Job Parameter -->\n\t<query name=\"getNumberOfSJobParameter\">\n\t\tSELECT COUNT(jp.id)\n\t\tFROM org.bonitasoft.engine.scheduler.model.SJobParameter AS jp\n\t</query>\n\t<query name=\"getJobParameters\">\n\t\tSELECT jp\n\t\tFROM org.bonitasoft.engine.scheduler.model.SJobParameter AS jp\n\t\tWHERE jp.jobDescriptorId = :jobDescriptorId\n\t</query>\n\n\t<!-- Job Log -->\n\t<query name=\"getNumberOfSJobLog\">\n\t\tSELECT COUNT(jl.id)\n\t\tFROM org.bonitasoft.engine.scheduler.model.SJobLog AS jl\n\t</query>\n\n\t<query name=\"searchSJobLog\">\n\t\tSELECT jl\n\t\tFROM org.bonitasoft.engine.scheduler.model.SJobLog AS jl\n\t</query>\n\n\t<!-- Failed Job -->\n\t<query name=\"getFailedJobs\">\n\t\tSELECT new org.bonitasoft.engine.scheduler.model.impl.SFailedJobImpl(jd.id, jd.jobName, jd.description, jl.retryNumber, jl.lastUpdateDate, jl.lastMessage)\n\t\tFROM org.bonitasoft.engine.scheduler.model.SJobLog AS jl,\n\t\t     org.bonitasoft.engine.scheduler.model.SJobDescriptor AS jd\n\t\tWHERE jd.id = jl.jobDescriptorId\n\t\tORDER BY jl.lastUpdateDate ASC\n\t</query>\n\n</hibernate-mapping>\n"
  },
  {
    "path": "services/bonita-scheduler/src/test/java/org/bonitasoft/engine/scheduler/impl/AbstractQuartzJobTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.fail;\nimport static org.bonitasoft.engine.scheduler.impl.JobUtils.createJobDetails;\nimport static org.bonitasoft.engine.scheduler.impl.JobUtils.jobThatFails;\nimport static org.bonitasoft.engine.scheduler.impl.JobUtils.jobThatThrowASRetryableException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.verify;\n\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnit;\nimport org.mockito.junit.MockitoRule;\nimport org.quartz.JobExecutionException;\n\n/**\n * @author Baptiste Mesta\n */\npublic class AbstractQuartzJobTest {\n\n    @Rule\n    public MockitoRule mockitoRule = MockitoJUnit.rule();\n\n    @Mock\n    private SchedulerServiceImpl schedulerService;\n\n    private final ConcurrentQuartzJob abstractQuartzJob = new ConcurrentQuartzJob();\n\n    @Before\n    public void before() {\n        abstractQuartzJob.setSchedulerService(schedulerService);\n        abstractQuartzJob.setJobDetails(createJobDetails(2));\n    }\n\n    @Test\n    public void should_not_unschedule_job_on_exception() throws Exception {\n        // job should never throw an exception and be handled by the JobWrapper\n        // we do not unschedule the job in that case. we don't want to lose the job\n        doReturn(jobThatFails()).when(schedulerService).getPersistedJob(any());\n\n        try {\n            abstractQuartzJob.execute(null);\n            fail(\"should throw exception\");\n        } catch (JobExecutionException e) {\n            assertThat(e.unscheduleFiringTrigger()).isFalse();\n            assertThat(e.refireImmediately()).isFalse();\n        }\n    }\n\n    @Test\n    public void should_retry_job_that_failed_with_SRetryable() throws Exception {\n        doReturn(jobThatThrowASRetryableException()).when(schedulerService).getPersistedJob(any());\n        abstractQuartzJob.setSchedulerService(schedulerService);\n        abstractQuartzJob.setJobDetails(createJobDetails(2));\n\n        abstractQuartzJob.execute(null);\n\n        verify(schedulerService).executeAgain(2, 5000);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/test/java/org/bonitasoft/engine/scheduler/impl/FailingJob.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.impl;\n\nimport org.quartz.Job;\nimport org.quartz.JobExecutionContext;\nimport org.quartz.JobExecutionException;\n\npublic class FailingJob implements Job {\n\n    private FailingJob() {\n    }\n\n    @Override\n    public void execute(final JobExecutionContext context) throws JobExecutionException {\n        throw new JobExecutionException();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/test/java/org/bonitasoft/engine/scheduler/impl/JDBCJobListenerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.impl;\n\nimport static org.bonitasoft.engine.scheduler.BonitaJobListener.TRIGGER_NEXT_FIRE_TIME;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\nimport java.io.Serializable;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.scheduler.BonitaJobListener;\nimport org.bonitasoft.engine.scheduler.JobService;\nimport org.bonitasoft.engine.scheduler.SchedulerService;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Celine Souchet\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class JDBCJobListenerTest {\n\n    private static final Long JOB_DESCRIPTOR_ID = 50L;\n    private final Map<String, Serializable> context = new HashMap<>();\n    @Mock\n    private JobService jobService;\n    @Mock\n    private SchedulerService schedulerService;\n    private JDBCJobListener jdbcJobListener;\n\n    @Before\n    public void setUp() {\n        context.put(BonitaJobListener.JOB_DESCRIPTOR_ID, JOB_DESCRIPTOR_ID);\n        jdbcJobListener = new JDBCJobListener(jobService, schedulerService);\n    }\n\n    @Test\n    public void should_delete_job_descriptor_when_job_may_not_fire_again() throws Exception {\n        // Given\n        context.put(BonitaJobListener.JOB_DESCRIPTOR_ID, JOB_DESCRIPTOR_ID);\n        context.put(BonitaJobListener.JOB_NAME, \"myJob\");\n        doReturn(false).when(schedulerService).mayFireAgain(\"myJob\");\n\n        // When\n        jdbcJobListener.jobWasExecuted(context, null);\n\n        // Then\n        verify(jobService).deleteJobDescriptor(JOB_DESCRIPTOR_ID);\n    }\n\n    @Test\n    public void should_not_delete_job_descriptor_when_job_may_not_fire_again_but_there_is_other_triggers()\n            throws Exception {\n        // Given\n        context.put(BonitaJobListener.JOB_DESCRIPTOR_ID, JOB_DESCRIPTOR_ID);\n        context.put(BonitaJobListener.JOB_NAME, \"myJob\");\n        doReturn(true).when(schedulerService).mayFireAgain(\"myJob\");\n\n        // When\n        jdbcJobListener.jobWasExecuted(context, null);\n\n        // Then\n        verify(jobService, never()).deleteJobDescriptor(JOB_DESCRIPTOR_ID);\n    }\n\n    @Test\n    public void should_not_delete_job_descriptor_when_job_may_fire_again() throws Exception {\n        // Given\n        context.put(BonitaJobListener.JOB_DESCRIPTOR_ID, JOB_DESCRIPTOR_ID);\n        context.put(TRIGGER_NEXT_FIRE_TIME, new Date());\n\n        // When\n        jdbcJobListener.jobWasExecuted(context, null);\n\n        // Then\n        verify(jobService, never()).deleteJobDescriptor(JOB_DESCRIPTOR_ID);\n    }\n\n    @Test\n    public void should_not_delete_job_descriptor_when_job_failed() throws Exception {\n        // Given\n        context.put(BonitaJobListener.JOB_DESCRIPTOR_ID, JOB_DESCRIPTOR_ID);\n        context.put(TRIGGER_NEXT_FIRE_TIME, new Date());\n\n        // When\n        jdbcJobListener.jobWasExecuted(context, new IllegalStateException());\n\n        // Then\n        verify(jobService, never()).deleteJobDescriptor(JOB_DESCRIPTOR_ID);\n    }\n\n    @Test\n    public void jobToBeExecuted_should_do_nothing() {\n        // When\n        jdbcJobListener.jobToBeExecuted();\n\n        // Then\n        verifyNoInteractions(jobService);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/test/java/org/bonitasoft/engine/scheduler/impl/JobServiceImplForFailedJobTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.impl;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Collections;\n\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectListDescriptor;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.scheduler.exception.failedJob.SFailedJobReadException;\nimport org.bonitasoft.engine.scheduler.model.SFailedJob;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentMatchers;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Celine Souchet\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class JobServiceImplForFailedJobTest {\n\n    @Mock\n    private EventService eventService;\n\n    @Mock\n    private QueriableLoggerService queriableLoggerService;\n\n    @Mock\n    private ReadPersistenceService readPersistenceService;\n\n    @Mock\n    private Recorder recorder;\n\n    @InjectMocks\n    private JobServiceImpl jobServiceImpl;\n\n    @Test\n    public final void getFailedJobs() throws SBonitaReadException, SFailedJobReadException {\n        // Given\n        final SFailedJob sFailedJob = mock(SFailedJob.class);\n        when(readPersistenceService.selectList(ArgumentMatchers.<SelectListDescriptor<SFailedJob>> any()))\n                .thenReturn(Collections.singletonList(sFailedJob));\n\n        // When\n        final SFailedJob result = jobServiceImpl.getFailedJobs(0, 10).get(0);\n\n        // Then\n        assertEquals(sFailedJob, result);\n        verify(readPersistenceService).selectList(ArgumentMatchers.<SelectListDescriptor<SFailedJob>> any());\n    }\n\n    @Test(expected = SFailedJobReadException.class)\n    public void getFailedJobs_should_throw_exception_when_persistenceService_failed()\n            throws SBonitaReadException, SFailedJobReadException {\n        doThrow(new SBonitaReadException(\"\")).when(readPersistenceService)\n                .selectList(ArgumentMatchers.<SelectListDescriptor<SFailedJob>> any());\n\n        jobServiceImpl.getFailedJobs(0, 10);\n    }\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/test/java/org/bonitasoft/engine/scheduler/impl/JobServiceImplForJobDescriptorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.impl;\n\nimport static java.util.Arrays.asList;\nimport static org.junit.Assert.*;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.scheduler.exception.jobDescriptor.SJobDescriptorCreationException;\nimport org.bonitasoft.engine.scheduler.exception.jobDescriptor.SJobDescriptorDeletionException;\nimport org.bonitasoft.engine.scheduler.exception.jobDescriptor.SJobDescriptorReadException;\nimport org.bonitasoft.engine.scheduler.model.SJobDescriptor;\nimport org.bonitasoft.engine.scheduler.recorder.SelectDescriptorBuilder;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentMatchers;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Celine Souchet\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class JobServiceImplForJobDescriptorTest {\n\n    @Mock\n    private EventService eventService;\n\n    @Mock\n    private QueriableLoggerService queriableLoggerService;\n\n    @Mock\n    private ReadPersistenceService readPersistenceService;\n\n    @Mock\n    private Recorder recorder;\n\n    @Spy\n    @InjectMocks\n    private JobServiceImpl jobServiceImpl;\n\n    @Test\n    public final void createJobDescriptor_should_return_jobDescriptor()\n            throws SJobDescriptorCreationException, SRecorderException {\n        // Given\n        final SJobDescriptor sJobDescriptor = mock(SJobDescriptor.class);\n        doReturn(\"plop\").when(sJobDescriptor).getJobName();\n        doNothing().when(recorder).recordInsert(any(InsertRecord.class), nullable(String.class));\n\n        // When\n        final SJobDescriptor result = jobServiceImpl.createJobDescriptor(sJobDescriptor);\n\n        // Then\n        assertNotNull(result);\n        assertEquals(sJobDescriptor.getJobName(), result.getJobName());\n        assertEquals(sJobDescriptor.getDescription(), result.getDescription());\n        assertEquals(sJobDescriptor.getJobClassName(), result.getJobClassName());\n        verify(recorder, times(1)).recordInsert(any(InsertRecord.class), nullable(String.class));\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void createJobDescriptor_should_throw_an_exception_if_the_descriptor_is_null() throws Exception {\n        jobServiceImpl.createJobDescriptor(null);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void createJobDescriptor_should_throw_an_exception_if_the_descriptor_name_is_null() throws Exception {\n        // Given\n        final SJobDescriptor jobDescriptor = mock(SJobDescriptor.class);\n\n        // When\n        jobServiceImpl.createJobDescriptor(jobDescriptor);\n    }\n\n    @Test(expected = SJobDescriptorCreationException.class)\n    public void createJobDescriptor_should_throw_exception_when_recorder_failed() throws Exception {\n        //given\n        doThrow(new SRecorderException(\"plop\")).when(recorder).recordInsert(any(InsertRecord.class),\n                nullable(String.class));\n        final SJobDescriptor sJobDescriptor = mock(SJobDescriptor.class);\n        when(sJobDescriptor.getJobName()).thenReturn(\"jobName\");\n\n        //when\n        jobServiceImpl.createJobDescriptor(sJobDescriptor);\n\n        //then exception\n\n    }\n\n    @Test\n    public final void deleteJobDescriptor_by_id_should_delete_job_descriptor() throws Exception {\n        //Given\n        final SJobDescriptor sJobDescriptor = mock(SJobDescriptor.class);\n\n        doReturn(sJobDescriptor).when(readPersistenceService)\n                .selectById(ArgumentMatchers.<SelectByIdDescriptor<SJobDescriptor>> any());\n\n        // When\n        jobServiceImpl.deleteJobDescriptor(3);\n\n        //Then\n        verify(jobServiceImpl, times(1)).deleteJobDescriptor(sJobDescriptor);\n    }\n\n    @Test\n    public final void deleteJobDescriptor_by_id_should_do_nothing_when_job_descriptor_doesnt_exist() throws Exception {\n        // Given\n        when(readPersistenceService.selectById(ArgumentMatchers.<SelectByIdDescriptor<SJobDescriptor>> any()))\n                .thenReturn(null);\n\n        // When\n        jobServiceImpl.deleteJobDescriptor(1);\n\n        // Then\n        verify(jobServiceImpl, never()).deleteJobDescriptor(any(SJobDescriptor.class));\n    }\n\n    @Test\n    public final void deleteJobDescriptor_by_id_should_do_nothing_when_job_descriptor_doesnt_exist_without_log()\n            throws Exception {\n        // Given\n        when(readPersistenceService.selectById(ArgumentMatchers.<SelectByIdDescriptor<SJobDescriptor>> any()))\n                .thenReturn(null);\n\n        // When\n        jobServiceImpl.deleteJobDescriptor(1);\n\n        // Then\n        verify(jobServiceImpl, never()).deleteJobDescriptor(any(SJobDescriptor.class));\n    }\n\n    @Test(expected = SJobDescriptorDeletionException.class)\n    public void deleteJobDescriptor_by_id_should_throw_exception_when_recorder_failed() throws Exception {\n        // Given\n        final SJobDescriptor sJobDescriptor = mock(SJobDescriptor.class);\n\n        doReturn(sJobDescriptor).when(readPersistenceService).selectById(any());\n        doThrow(new SRecorderException(\"\")).when(recorder).recordDelete(any(DeleteRecord.class),\n                nullable(String.class));\n\n        // When\n        jobServiceImpl.deleteJobDescriptor(3);\n    }\n\n    @Test\n    public void deleteJobDescriptor_by_name_should_delete_job_descriptor() throws Exception {\n        //Given\n        final SJobDescriptor sJobDescriptor = mock(SJobDescriptor.class);\n        final List<SJobDescriptor> jobDescriptors = Collections.singletonList(sJobDescriptor);\n        doReturn(jobDescriptors).when(jobServiceImpl).searchJobDescriptors(any(QueryOptions.class));\n\n        //when\n        jobServiceImpl.deleteJobDescriptorByJobName(\"jobName\");\n\n        //then\n        verify(jobServiceImpl, times(1)).deleteJobDescriptor(any(SJobDescriptor.class));\n    }\n\n    @Test\n    public void deleteJobDescriptor_by_name_should_do_nothing_when_no_job_descriptor() throws Exception {\n        //Given\n        doReturn(Collections.EMPTY_LIST).when(jobServiceImpl).searchJobDescriptors(any(QueryOptions.class));\n\n        //when\n        jobServiceImpl.deleteJobDescriptorByJobName(\"jobName\");\n\n        //then\n        verify(jobServiceImpl, never()).deleteJobDescriptor(any(SJobDescriptor.class));\n    }\n\n    @Test(expected = SJobDescriptorDeletionException.class)\n    public void deleteJobDescriptor_by_name_should_throw_exception_when_searchJobDescriptors_failed() throws Exception {\n        //Given\n        doThrow(new SBonitaReadException(\"toto\")).when(jobServiceImpl).searchJobDescriptors(any(QueryOptions.class));\n\n        //when\n        jobServiceImpl.deleteJobDescriptorByJobName(\"jobName\");\n    }\n\n    @Test(expected = SJobDescriptorDeletionException.class)\n    public void deleteAllJobDescriptors_should_throw_exception_when_searchEntity_failed() throws Exception {\n        //Given\n        when(readPersistenceService.searchEntity(eq(SJobDescriptor.class), any(QueryOptions.class),\n                nullable(Map.class))).thenThrow(\n                        new SBonitaReadException(\"error\"));\n\n        //When\n        jobServiceImpl.deleteAllJobDescriptors();\n    }\n\n    @Test\n    public void deleteAllJobDescriptors_should_delete_all_job_descriptors() throws Exception {\n        SJobDescriptor sJobDescriptor = new SJobDescriptor();\n        when(readPersistenceService.searchEntity(eq(SJobDescriptor.class), any(QueryOptions.class),\n                nullable(Map.class))).thenReturn(\n                        asList(new SJobDescriptor(), new SJobDescriptor()));\n\n        jobServiceImpl.deleteAllJobDescriptors();\n\n        verify(jobServiceImpl, times(2)).deleteJobDescriptor(sJobDescriptor);\n    }\n\n    @Test\n    public final void deleteJobDescriptor_by_object_should_delete_job_descriptor()\n            throws SRecorderException, SJobDescriptorDeletionException {\n        // Given\n        final SJobDescriptor sJobDescriptor = mock(SJobDescriptor.class);\n\n        doNothing().when(recorder).recordDelete(any(DeleteRecord.class), nullable(String.class));\n\n        // When\n        jobServiceImpl.deleteJobDescriptor(sJobDescriptor);\n\n        //Then\n        verify(recorder, times(1)).recordDelete(any(DeleteRecord.class), nullable(String.class));\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public final void deleteJobDescriptor_by_object_should_throw_exception_when_job_descriptor_is_null()\n            throws SJobDescriptorDeletionException {\n        jobServiceImpl.deleteJobDescriptor(null);\n    }\n\n    @Test\n    public void getJobDescriptor_by_id_should_return_jobDescriptor()\n            throws SBonitaReadException, SJobDescriptorReadException {\n        // Given\n        final long jobDescriptorId = 1;\n        final SJobDescriptor sJobDescriptor = mock(SJobDescriptor.class);\n        when(readPersistenceService.selectById(\n                SelectDescriptorBuilder.getElementById(SJobDescriptor.class, \"SJobDescriptor\", jobDescriptorId)))\n                .thenReturn(\n                        sJobDescriptor);\n\n        // When\n        final SJobDescriptor jobDescriptor = jobServiceImpl.getJobDescriptor(jobDescriptorId);\n\n        // Then\n        Assert.assertEquals(sJobDescriptor, jobDescriptor);\n    }\n\n    @Test\n    public void getJobDescriptor_by_id__should_return_null_when_no_job_descriptor()\n            throws SBonitaReadException, SJobDescriptorReadException {\n        // Given\n        final long jobDescriptorId = 455;\n        doReturn(null).when(readPersistenceService).selectById(\n                SelectDescriptorBuilder.getElementById(SJobDescriptor.class, \"SJobDescriptor\", jobDescriptorId));\n\n        // When\n        final SJobDescriptor result = jobServiceImpl.getJobDescriptor(jobDescriptorId);\n\n        // Then\n        assertNull(result);\n    }\n\n    @Test(expected = SJobDescriptorReadException.class)\n    public void getJobDescriptor_by_id_should_throw_exception_when_selectById_failed()\n            throws SBonitaReadException, SJobDescriptorReadException {\n        final long jobDescriptorId = 1;\n        doThrow(new SBonitaReadException(\"\")).when(readPersistenceService).selectById(\n                SelectDescriptorBuilder.getElementById(SJobDescriptor.class, \"SJobDescriptor\", jobDescriptorId));\n\n        jobServiceImpl.getJobDescriptor(jobDescriptorId);\n    }\n\n    @Test\n    public void getNumberOfJobDescriptors() throws SBonitaReadException, SBonitaReadException {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        when(readPersistenceService.getNumberOfEntities(SJobDescriptor.class, options, null)).thenReturn(1L);\n\n        // When\n        final long numberOfJobDescriptors = jobServiceImpl.getNumberOfJobDescriptors(options);\n\n        // Then\n        Assert.assertEquals(1L, numberOfJobDescriptors);\n        verifyNoInteractions(recorder);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getNumberOfJobDescriptors_should_throw_exception() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        when(readPersistenceService.getNumberOfEntities(SJobDescriptor.class, options, null))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        // When\n        jobServiceImpl.getNumberOfJobDescriptors(options);\n    }\n\n    @Test\n    public void searchJobDescriptors() throws SBonitaReadException, SBonitaReadException {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final SJobDescriptor sJobDescriptor = mock(SJobDescriptor.class);\n        when(readPersistenceService.searchEntity(SJobDescriptor.class, options, null))\n                .thenReturn(Collections.singletonList(sJobDescriptor));\n\n        // When\n        final SJobDescriptor result = jobServiceImpl.searchJobDescriptors(options).get(0);\n\n        // Then\n        assertEquals(sJobDescriptor, result);\n        verify(readPersistenceService).searchEntity(SJobDescriptor.class, options, null);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void searchJobDescriptors_should_throw_exception() throws SBonitaReadException, SBonitaReadException {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        doThrow(new SBonitaReadException(\"\")).when(readPersistenceService).searchEntity(SJobDescriptor.class, options,\n                null);\n\n        // When\n        jobServiceImpl.searchJobDescriptors(options).get(0);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/test/java/org/bonitasoft/engine/scheduler/impl/JobServiceImplForJobLogTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.*;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.ArgumentMatchers.nullable;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.persistence.QueryOptions;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.recorder.model.UpdateRecord;\nimport org.bonitasoft.engine.scheduler.exception.jobLog.SJobLogCreationException;\nimport org.bonitasoft.engine.scheduler.exception.jobLog.SJobLogDeletionException;\nimport org.bonitasoft.engine.scheduler.exception.jobLog.SJobLogUpdatingException;\nimport org.bonitasoft.engine.scheduler.model.SJobDescriptor;\nimport org.bonitasoft.engine.scheduler.model.SJobLog;\nimport org.bonitasoft.engine.scheduler.recorder.SelectDescriptorBuilder;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.ArgumentMatchers;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Celine Souchet\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class JobServiceImplForJobLogTest {\n\n    @Mock\n    private EventService eventService;\n    @Mock\n    private QueriableLoggerService queriableLoggerService;\n    @Mock\n    private ReadPersistenceService readPersistenceService;\n    @Mock\n    private Recorder recorder;\n    @Spy\n    @InjectMocks\n    private JobServiceImpl jobServiceImpl;\n\n    @Test\n    public final void createJobLog() throws Exception {\n        // Given\n        final SJobLog sJobLog = mock(SJobLog.class);\n\n        doNothing().when(recorder).recordInsert(any(InsertRecord.class), nullable(String.class));\n\n        // When\n        final SJobLog result = jobServiceImpl.createJobLog(sJobLog);\n\n        // Then\n        assertNotNull(result);\n        assertEquals(sJobLog, result);\n        verify(recorder).recordInsert(any(InsertRecord.class), nullable(String.class));\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public final void createNullJobLog() throws Exception {\n        jobServiceImpl.createJobLog(null);\n    }\n\n    @Test(expected = SJobLogCreationException.class)\n    public final void createJobLog_should_throw_exception_when_recorder_failed()\n            throws SJobLogCreationException, SRecorderException {\n        // Given\n        final SJobLog sJobLog = mock(SJobLog.class);\n\n        doThrow(new SRecorderException(\"plop\")).when(recorder).recordInsert(any(InsertRecord.class),\n                nullable(String.class));\n\n        // When\n        jobServiceImpl.createJobLog(sJobLog);\n    }\n\n    @Test\n    public final void deleteJobLog_by_id() throws Exception {\n        // Given\n        final SJobLog sJobLog = mock(SJobLog.class);\n\n        doReturn(sJobLog).when(readPersistenceService)\n                .selectById(ArgumentMatchers.<SelectByIdDescriptor<SJobLog>> any());\n        doNothing().when(recorder).recordDelete(any(DeleteRecord.class), nullable(String.class));\n\n        // When\n        jobServiceImpl.deleteJobLog(3);\n\n        // Then\n        verify(recorder).recordDelete(any(DeleteRecord.class), nullable(String.class));\n    }\n\n    @Test\n    public final void deleteJobLog_by_id_should_do_nothing_when_job_log_doesnt_exist()\n            throws SBonitaReadException, SJobLogDeletionException {\n        // Given\n        when(readPersistenceService.selectById(ArgumentMatchers.<SelectByIdDescriptor<SJobLog>> any()))\n                .thenReturn(null);\n\n        // When\n        jobServiceImpl.deleteJobLog(1);\n\n        // Then\n        verify(jobServiceImpl, never()).deleteJobLog(any(SJobLog.class));\n    }\n\n    @Test(expected = SJobLogDeletionException.class)\n    public void deleteJobLog_by_id_should_throw_exception_when_recorder_failed() throws Exception {\n        final SJobLog sJobLog = mock(SJobLog.class);\n\n        doReturn(sJobLog).when(readPersistenceService)\n                .selectById(ArgumentMatchers.<SelectByIdDescriptor<SJobLog>> any());\n        doThrow(new SRecorderException(\"\")).when(recorder).recordDelete(any(DeleteRecord.class),\n                nullable(String.class));\n\n        jobServiceImpl.deleteJobLog(3);\n    }\n\n    @Test\n    public final void deleteJobLog_by_object() throws SRecorderException, SJobLogDeletionException {\n        // Given\n        final SJobLog sJobLog = mock(SJobLog.class);\n\n        doNothing().when(recorder).recordDelete(any(DeleteRecord.class), nullable(String.class));\n\n        // When\n        jobServiceImpl.deleteJobLog(sJobLog);\n\n        // Then\n        verify(recorder).recordDelete(any(DeleteRecord.class), nullable(String.class));\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public final void deleteJobLog_by_object_should_throw_exception_when_parameter_is_null()\n            throws SJobLogDeletionException {\n        jobServiceImpl.deleteJobLog(null);\n    }\n\n    @Test(expected = SJobLogDeletionException.class)\n    public void deleteJobLog_by_object_should_throw_exception_when_recorder_failed() throws Exception {\n        // Given\n        final SJobLog sJobLog = mock(SJobLog.class);\n\n        doThrow(new SRecorderException(\"\")).when(recorder).recordDelete(any(DeleteRecord.class),\n                nullable(String.class));\n\n        // When\n        jobServiceImpl.deleteJobLog(sJobLog);\n    }\n\n    @Test\n    public void deleteJobLogs() throws Exception {\n        // Given\n        final long jobDescriptorId = 9L;\n        final SJobLog sJobLog = mock(SJobLog.class);\n        final List<SJobLog> jobLogs = Collections.singletonList(sJobLog);\n\n        doReturn(jobLogs).doReturn(Collections.EMPTY_LIST).when(jobServiceImpl).getJobLogs(jobDescriptorId, 0, 100);\n        doNothing().when(jobServiceImpl).deleteJobLog(any(SJobLog.class));\n\n        // When\n        jobServiceImpl.deleteJobLogs(jobDescriptorId);\n    }\n\n    @Test(expected = SJobLogDeletionException.class)\n    public void deleteJobLogs_should_throw_exception_when_deleteJobLogs_failed() throws Exception {\n        // Given\n        final long jobDescriptorId = 9L;\n        final SJobLog sJobLog = mock(SJobLog.class);\n        final List<SJobLog> jobLogs = Collections.singletonList(sJobLog);\n\n        doReturn(jobLogs).when(jobServiceImpl).getJobLogs(jobDescriptorId, 0, 100);\n        doThrow(SJobLogDeletionException.class).when(jobServiceImpl).deleteJobLog(any(SJobLog.class));\n\n        // When\n        jobServiceImpl.deleteJobLogs(jobDescriptorId);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void deleteJobLogs_should_throw_exception_when_getJobLogs_failed() throws Exception {\n        // Given\n        final long jobDescriptorId = 9L;\n        doThrow(SBonitaReadException.class).when(jobServiceImpl).getJobLogs(jobDescriptorId, 0, 100);\n\n        // When\n        jobServiceImpl.deleteJobLogs(jobDescriptorId);\n    }\n\n    @Test\n    public void getJobLog() throws SBonitaReadException {\n        // Given\n        final long jobLogId = 1;\n        final SJobLog sJobLog = mock(SJobLog.class);\n        when(readPersistenceService\n                .selectById(SelectDescriptorBuilder.getElementById(SJobLog.class, \"SJobLog\", jobLogId)))\n                .thenReturn(sJobLog);\n\n        // When\n        final SJobLog result = jobServiceImpl.getJobLog(jobLogId);\n\n        // Then\n        Assert.assertEquals(sJobLog, result);\n    }\n\n    @Test\n    public void getJobLog_should_throw_exception_when_not_exist() throws SBonitaReadException {\n        // Given\n        final long jobLogId = 455;\n        doReturn(null).when(readPersistenceService)\n                .selectById(SelectDescriptorBuilder.getElementById(SJobLog.class, \"SJobLog\", jobLogId));\n\n        // When\n        final SJobLog jobLog = jobServiceImpl.getJobLog(jobLogId);\n\n        // Then\n        assertNull(jobLog);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getJobLog_should_throw_exception_when_persistenceService_failed() throws SBonitaReadException {\n        // Given\n        final long jobLogId = 1;\n        doThrow(new SBonitaReadException(\"\")).when(readPersistenceService).selectById(\n                SelectDescriptorBuilder.getElementById(SJobLog.class, \"SJobLog\", jobLogId));\n\n        // When\n        jobServiceImpl.getJobLog(jobLogId);\n    }\n\n    @Test\n    public void getJobLogs_should_call_searchJobLogs() throws Exception {\n        // Given\n        final long jobDescriptorId = 9L;\n        final int fromIndex = 0;\n        final int maxResults = 10;\n\n        // When\n        jobServiceImpl.getJobLogs(jobDescriptorId, fromIndex, maxResults);\n\n        // Then\n        verify(jobServiceImpl).searchJobLogs(any(QueryOptions.class));\n    }\n\n    @Test\n    public void getNumberOfJobLogs() throws SBonitaReadException, SBonitaReadException {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n\n        when(readPersistenceService.getNumberOfEntities(SJobLog.class, options, null)).thenReturn(1L);\n\n        // When\n        final long numberOfJobLogs = jobServiceImpl.getNumberOfJobLogs(options);\n\n        // Then\n        Assert.assertEquals(1L, numberOfJobLogs);\n        verifyNoInteractions(recorder);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void getNumberOfJobLog_should_throw_exception_when_persistenceService_failed() throws Exception {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n\n        when(readPersistenceService.getNumberOfEntities(SJobLog.class, options, null))\n                .thenThrow(new SBonitaReadException(\"\"));\n\n        // When\n        jobServiceImpl.getNumberOfJobLogs(options);\n    }\n\n    @Test\n    public void searchJobLogs() throws SBonitaReadException, SBonitaReadException {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        final SJobLog sJobLog = mock(SJobLog.class);\n        when(readPersistenceService.searchEntity(SJobLog.class, options, null))\n                .thenReturn(Collections.singletonList(sJobLog));\n\n        // When\n        final SJobLog result = jobServiceImpl.searchJobLogs(options).get(0);\n\n        // Then\n        assertEquals(sJobLog, result);\n    }\n\n    @Test(expected = SBonitaReadException.class)\n    public void searchJobLog_should_throw_exception_when_persistenceService_failed()\n            throws SBonitaReadException, SBonitaReadException {\n        // Given\n        final QueryOptions options = new QueryOptions(0, 10);\n        doThrow(new SBonitaReadException(\"\")).when(readPersistenceService).searchEntity(SJobLog.class, options, null);\n\n        // When\n        jobServiceImpl.searchJobLogs(options).get(0);\n    }\n\n    @Test\n    public void updateJobLog_should_update_job_log() throws Exception {\n        // Given\n        final SJobLog jobLog = new SJobLog();\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n\n        // When\n        jobServiceImpl.updateJobLog(jobLog, descriptor);\n\n        // Then\n        verify(recorder).recordUpdate(any(UpdateRecord.class), anyString());\n    }\n\n    @Test(expected = SJobLogUpdatingException.class)\n    public void updateJobLog_should_throw_exception_when_recorder_failed() throws Exception {\n        // Given\n        final SJobLog jobLog = new SJobLog();\n        final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor();\n        doThrow(new SRecorderException(\"plop\")).when(recorder).recordUpdate(any(UpdateRecord.class),\n                nullable(String.class));\n\n        // When\n        jobServiceImpl.updateJobLog(jobLog, descriptor);\n    }\n\n    @Test\n    public void jobWasExecuted_should_update_jobLog_on_exception_if_previous_joblog() throws Exception {\n        // Given\n        final SJobLog jobLog = mock(SJobLog.class);\n        doReturn(1L).when(jobLog).getRetryNumber();\n        final List<SJobLog> jobLogs = Collections.singletonList((SJobLog) jobLog);\n\n        doReturn(jobLogs).when(jobServiceImpl).getJobLogs(5L, 0, 1);\n\n        long before = System.currentTimeMillis();\n\n        // When\n        Exception jobException = new Exception(\"theException\");\n        jobServiceImpl.logJobError(jobException, 5L);\n\n        // Then\n        ArgumentCaptor<EntityUpdateDescriptor> captor = ArgumentCaptor.forClass(EntityUpdateDescriptor.class);\n        verify(jobServiceImpl).updateJobLog(eq(jobLogs.get(0)), captor.capture());\n\n        EntityUpdateDescriptor updateDescriptor = captor.getValue();\n        assertThat((String) updateDescriptor.getFields().get(\"lastMessage\")).contains(jobException.getMessage());\n        assertThat((Long) updateDescriptor.getFields().get(\"lastUpdateDate\")).isGreaterThanOrEqualTo(before);\n        assertThat(updateDescriptor.getFields().get(\"retryNumber\")).isEqualTo(2L);\n    }\n\n    @Test\n    public void createJobLog_should_call_job_service_createJobLog_when_related_job_descriptor_exists()\n            throws Exception {\n        //given\n        Exception exception = new Exception(\"Missing mandatory parameter\");\n        long jobDescriptorId = 4L;\n        long before = System.currentTimeMillis();\n        given(jobServiceImpl.getJobDescriptor(jobDescriptorId)).willReturn(mock(SJobDescriptor.class));\n\n        //when\n        jobServiceImpl.createJobLog(exception, jobDescriptorId);\n\n        //then\n        ArgumentCaptor<SJobLog> captor = ArgumentCaptor.forClass(SJobLog.class);\n        verify(jobServiceImpl).createJobLog(captor.capture());\n        SJobLog jobLog = captor.getValue();\n        assertThat(jobLog.getRetryNumber()).isEqualTo(0);\n        assertThat(jobLog.getJobDescriptorId()).isEqualTo(jobDescriptorId);\n        assertThat(jobLog.getLastMessage()).contains(exception.getMessage());\n        assertThat(jobLog.getLastUpdateDate()).isGreaterThanOrEqualTo(before);\n    }\n\n    @Test\n    public void createJobLog_should_not_create_job_log_when_related_job_descriptor_does_not_exist() throws Exception {\n        //given\n        Exception exception = new Exception(\"Missing mandatory parameter\");\n        long jobDescriptorId = 4L;\n        given(jobServiceImpl.getJobDescriptor(jobDescriptorId)).willReturn(null);\n\n        //when\n        jobServiceImpl.createJobLog(exception, jobDescriptorId);\n\n        //then\n        verify(jobServiceImpl, never()).createJobLog(any(SJobLog.class));\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/test/java/org/bonitasoft/engine/scheduler/impl/JobServiceImplForJobParameterTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.impl;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.doThrow;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.nullable;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.assertj.core.api.Assertions;\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.persistence.ReadPersistenceService;\nimport org.bonitasoft.engine.persistence.SBonitaReadException;\nimport org.bonitasoft.engine.persistence.SelectByIdDescriptor;\nimport org.bonitasoft.engine.recorder.Recorder;\nimport org.bonitasoft.engine.recorder.SRecorderException;\nimport org.bonitasoft.engine.recorder.model.DeleteRecord;\nimport org.bonitasoft.engine.recorder.model.InsertRecord;\nimport org.bonitasoft.engine.scheduler.exception.jobParameter.SJobParameterCreationException;\nimport org.bonitasoft.engine.scheduler.exception.jobParameter.SJobParameterDeletionException;\nimport org.bonitasoft.engine.scheduler.exception.jobParameter.SJobParameterNotFoundException;\nimport org.bonitasoft.engine.scheduler.exception.jobParameter.SJobParameterReadException;\nimport org.bonitasoft.engine.scheduler.model.SJobParameter;\nimport org.bonitasoft.engine.scheduler.recorder.SelectDescriptorBuilder;\nimport org.bonitasoft.engine.services.QueriableLoggerService;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentMatchers;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Celine Souchet\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class JobServiceImplForJobParameterTest {\n\n    @Mock\n    private EventService eventService;\n\n    @Mock\n    private QueriableLoggerService queriableLoggerService;\n\n    @Mock\n    private ReadPersistenceService readPersistenceService;\n\n    @Mock\n    private Recorder recorder;\n\n    @Spy\n    @InjectMocks\n    private JobServiceImpl jobServiceImpl;\n\n    /**\n     * method for\n     * {@link org.bonitasoft.engine.scheduler.impl.JobServiceImpl#createJobParameters(java.util.List, long)}.\n     *\n     * @throws SJobParameterCreationException\n     * @throws SRecorderException\n     */\n    @Test\n    public final void createJobParameters() throws SJobParameterCreationException {\n        final long jobDescriptorId = 9;\n        final SJobParameter sJobParameter = mock(SJobParameter.class);\n\n        final List<SJobParameter> result = jobServiceImpl.createJobParameters(Collections.singletonList(sJobParameter),\n                jobDescriptorId);\n        assertNotNull(result);\n        assertEquals(1, result.size());\n        assertEquals(sJobParameter, result.get(0));\n    }\n\n    @Test\n    public final void createJobParameters_with_null_parameters_should_return_empty_list() throws Exception {\n        // Given\n        final long jobDescriptorId = 9;\n\n        // When\n        final List<SJobParameter> result = jobServiceImpl.createJobParameters(null, jobDescriptorId);\n\n        // Then\n        assertNotNull(result);\n        assertTrue(result.isEmpty());\n    }\n\n    @Test\n    public final void createJobParameter_with_empty_list_should_return_empty_list() throws Exception {\n        // Given\n        final long jobDescriptorId = 9;\n\n        // When\n        final List<SJobParameter> result = jobServiceImpl.createJobParameters(Collections.emptyList(),\n                jobDescriptorId);\n\n        // Then\n        assertNotNull(result);\n        assertTrue(result.isEmpty());\n    }\n\n    @Test(expected = SJobParameterCreationException.class)\n    public final void createJobParameters_should_throw_exception_when_recorder_failed()\n            throws SJobParameterCreationException, SRecorderException {\n        // Given\n        final long jobDescriptorId = 9;\n        final SJobParameter sJobParameter = mock(SJobParameter.class);\n\n        doThrow(new SRecorderException(\"plop\")).when(recorder).recordInsert(any(InsertRecord.class),\n                nullable(String.class));\n\n        // When\n        jobServiceImpl.createJobParameters(Collections.singletonList(sJobParameter), jobDescriptorId);\n    }\n\n    /**\n     * method for\n     * {@link org.bonitasoft.engine.scheduler.impl.JobServiceImpl#createJobParameter(org.bonitasoft.engine.scheduler.model.SJobParameter, long)}.\n     *\n     * @throws SJobParameterCreationException\n     * @throws SRecorderException\n     */\n    @Test\n    public final void createJobParameter() throws SJobParameterCreationException {\n        final long jobDescriptorId = 9;\n        final SJobParameter sJobParameter = mock(SJobParameter.class);\n\n        final SJobParameter result = jobServiceImpl.createJobParameter(sJobParameter, jobDescriptorId);\n        assertNotNull(result);\n        assertEquals(sJobParameter, result);\n    }\n\n    @Test\n    public final void createJobParameter_with_null_parameters_should_throw_exception() {\n        // Given\n        final long jobDescriptorId = 9;\n\n        // When\n        assertThatExceptionOfType(IllegalArgumentException.class)\n                .isThrownBy(() -> jobServiceImpl.createJobParameter(null, jobDescriptorId));\n    }\n\n    @Test\n    public final void createJobParameter_should_throw_exception_when_recorder_failed() throws Exception {\n        // Given\n        final long jobDescriptorId = 9;\n        final SJobParameter sJobParameter = mock(SJobParameter.class);\n\n        doThrow(new SRecorderException(\"plop\")).when(recorder).recordInsert(any(InsertRecord.class),\n                nullable(String.class));\n\n        // When\n        assertThatExceptionOfType(SJobParameterCreationException.class)\n                .isThrownBy(() -> jobServiceImpl.createJobParameter(sJobParameter, jobDescriptorId));\n    }\n\n    /**\n     * method for {@link org.bonitasoft.engine.scheduler.impl.JobServiceImpl#deleteJobParameter(long)}.\n     *\n     * @throws SBonitaReadException\n     * @throws SRecorderException\n     * @throws SJobParameterDeletionException\n     * @throws SJobParameterReadException\n     * @throws SJobParameterNotFoundException\n     */\n    @Test\n    public final void deleteJobParameter_by_id() throws Exception {\n        // Given\n        final SJobParameter sJobParameter = mock(SJobParameter.class);\n\n        doReturn(sJobParameter).when(readPersistenceService)\n                .selectById(ArgumentMatchers.<SelectByIdDescriptor<SJobParameter>> any());\n\n        // When\n        jobServiceImpl.deleteJobParameter(3);\n\n        // Then\n        verify(jobServiceImpl).deleteJobParameter(sJobParameter);\n        verify(recorder).recordDelete(any(DeleteRecord.class), nullable(String.class));\n    }\n\n    @Test(expected = SJobParameterNotFoundException.class)\n    public final void deleteNotExistingJobParameter_by_id() throws Exception {\n        // Given\n        when(readPersistenceService.selectById(ArgumentMatchers.<SelectByIdDescriptor<SJobParameter>> any()))\n                .thenReturn(null);\n\n        // When\n        jobServiceImpl.deleteJobParameter(1);\n    }\n\n    @Test(expected = SJobParameterDeletionException.class)\n    public void deleteJobParameter_by_id_should_throw_exception_when_recorder_failed() throws Exception {\n        // Given\n        final SJobParameter sJobParameter = mock(SJobParameter.class);\n\n        doReturn(sJobParameter).when(readPersistenceService)\n                .selectById(ArgumentMatchers.<SelectByIdDescriptor<SJobParameter>> any());\n        doThrow(new SRecorderException(\"\")).when(recorder).recordDelete(any(DeleteRecord.class),\n                nullable(String.class));\n\n        // When\n        jobServiceImpl.deleteJobParameter(3);\n    }\n\n    /**\n     * method for\n     * {@link org.bonitasoft.engine.scheduler.impl.JobServiceImpl#deleteJobParameter(org.bonitasoft.engine.scheduler.model.SJobParameter)}.\n     *\n     * @throws SRecorderException\n     * @throws SJobParameterDeletionException\n     */\n    @Test\n    public final void deleteJobParameter_by_object() throws SRecorderException, SJobParameterDeletionException {\n        final SJobParameter sJobParameter = mock(SJobParameter.class);\n\n        jobServiceImpl.deleteJobParameter(sJobParameter);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public final void deleteNullJobParameter_by_object() throws SJobParameterDeletionException {\n        jobServiceImpl.deleteJobParameter(null);\n    }\n\n    @Test(expected = SJobParameterDeletionException.class)\n    public void deleteJobParameter_by_object_should_throw_exception_when_recorder_failed() throws Exception {\n        final SJobParameter sJobParameter = mock(SJobParameter.class);\n\n        doThrow(new SRecorderException(\"\")).when(recorder).recordDelete(any(DeleteRecord.class),\n                nullable(String.class));\n\n        jobServiceImpl.deleteJobParameter(sJobParameter);\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.scheduler.impl.JobServiceImpl#getJobParameter(long)}.\n     *\n     * @throws SJobParameterReadException\n     * @throws SJobParameterNotFoundException\n     * @throws SBonitaReadException\n     */\n    @Test\n    public void getJobParameter_by_id() throws Exception {\n        // Given\n        final long jobParameterId = 1;\n        final SJobParameter sJobParameter = mock(SJobParameter.class);\n        when(readPersistenceService.selectById(\n                SelectDescriptorBuilder.getElementById(SJobParameter.class, \"SJobParameter\", jobParameterId)))\n                .thenReturn(\n                        sJobParameter);\n\n        // When\n        final SJobParameter result = jobServiceImpl.getJobParameter(jobParameterId);\n\n        // Then\n        Assert.assertEquals(sJobParameter, result);\n    }\n\n    @Test(expected = SJobParameterNotFoundException.class)\n    public void getJobParameter_by_id_should_throw_exception_when_not_exist()\n            throws SBonitaReadException, SJobParameterNotFoundException,\n            SJobParameterReadException {\n        // Given\n        final long jobParameterId = 455;\n        doReturn(null).when(readPersistenceService).selectById(\n                SelectDescriptorBuilder.getElementById(SJobParameter.class, \"SJobParameter\", jobParameterId));\n\n        // When\n        jobServiceImpl.getJobParameter(jobParameterId);\n    }\n\n    @Test(expected = SJobParameterReadException.class)\n    public void getJobParameter_by_id_should_throw_exception_when_persistenceService_failed() throws Exception {\n        // Given\n        final long jobParameterId = 1;\n        doThrow(new SBonitaReadException(\"\")).when(readPersistenceService).selectById(\n                SelectDescriptorBuilder.getElementById(SJobParameter.class, \"SJobParameter\", jobParameterId));\n\n        // When\n        jobServiceImpl.getJobParameter(jobParameterId);\n    }\n\n    @Test\n    public void should_list_parameters() throws Exception {\n        when(readPersistenceService.selectList(any()))\n                .thenReturn(Collections.singletonList(SJobParameter.builder().key(\"key\").value(\"value\").build()));\n\n        List<SJobParameter> result = jobServiceImpl.getJobParameters(123L);\n\n        Assertions.assertThat(result.stream().collect(Collectors.toMap(SJobParameter::getKey, SJobParameter::getValue)))\n                .containsOnly(entry(\"key\", \"value\"));\n    }\n\n    /**\n     * method for\n     * {@link org.bonitasoft.engine.scheduler.impl.JobServiceImpl#setJobParameters(long, java.util.List)}.\n     */\n    @Test\n    public final void setJobParameters() throws Exception {\n        // Given\n        final long jobDescriptorId = 8;\n        final SJobParameter sJobParameter = mock(SJobParameter.class);\n\n        final List<SJobParameter> sJobParameters = Collections.singletonList(sJobParameter);\n        doReturn(sJobParameters).when(readPersistenceService).selectList(any());\n\n        // When\n        final List<SJobParameter> result = jobServiceImpl.setJobParameters(jobDescriptorId, sJobParameters);\n\n        // Then\n        assertEquals(sJobParameters, result);\n        verify(jobServiceImpl).deleteAllJobParameters(jobDescriptorId);\n        verify(jobServiceImpl, times(sJobParameters.size())).deleteJobParameter(sJobParameter);\n        verify(jobServiceImpl, times(sJobParameters.size())).createJobParameter(sJobParameter, jobDescriptorId);\n    }\n\n    @Test\n    public void setJobParametersWithEmptyList() throws Exception {\n        // Given\n        final long jobDescriptorId = 8;\n\n        // When\n        final List<SJobParameter> result = jobServiceImpl.setJobParameters(jobDescriptorId, Collections.emptyList());\n\n        // Then\n        assertNotNull(result);\n        assertTrue(result.isEmpty());\n    }\n\n    @Test\n    public void setJobParametersWithNullList() throws Exception {\n        // Given\n        final long jobDescriptorId = 8;\n\n        // When\n        final List<SJobParameter> result = jobServiceImpl.setJobParameters(jobDescriptorId, null);\n\n        // Then\n        assertNotNull(result);\n        assertTrue(result.isEmpty());\n    }\n\n    @Test\n    public void setJobParameters_should_throw_exception_when_search_failed()\n            throws SBonitaReadException {\n        // Given\n        final long jobDescriptorId = 8;\n        final SJobParameter sJobParameter = mock(SJobParameter.class);\n\n        doThrow(new SBonitaReadException(\"\")).when(readPersistenceService).selectList(any());\n\n        // When\n        assertThatExceptionOfType(SJobParameterCreationException.class)\n                .isThrownBy(() -> jobServiceImpl.setJobParameters(jobDescriptorId,\n                        Collections.singletonList(sJobParameter)));\n    }\n\n    @Test\n    public void setJobParameters_should_throw_exception_when_delete_failed() throws Exception {\n        // Given\n        final long jobDescriptorId = 8;\n        final SJobParameter sJobParameter = mock(SJobParameter.class);\n\n        doReturn(Collections.singletonList(sJobParameter)).when(readPersistenceService).selectList(any());\n        doThrow(new SRecorderException(\"\")).when(recorder).recordDelete(any(DeleteRecord.class),\n                nullable(String.class));\n\n        // When\n        assertThatExceptionOfType(SJobParameterCreationException.class).isThrownBy(\n                () -> jobServiceImpl.setJobParameters(jobDescriptorId, Collections.singletonList(sJobParameter)));\n    }\n\n    @Test\n    public final void setJobParameters_should_throw_exception_when_create_failed() throws Exception {\n        // Given\n        final long jobDescriptorId = 8;\n        final SJobParameter sJobParameter = mock(SJobParameter.class);\n\n        doThrow(new SRecorderException(\"plop\")).when(recorder).recordInsert(any(InsertRecord.class),\n                nullable(String.class));\n\n        // When\n        assertThatExceptionOfType(SJobParameterCreationException.class).isThrownBy(\n                () -> jobServiceImpl.setJobParameters(jobDescriptorId, Collections.singletonList(sJobParameter)));\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/test/java/org/bonitasoft/engine/scheduler/impl/JobUtils.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.impl;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.commons.exceptions.SRetryableException;\nimport org.bonitasoft.engine.events.model.SFireEventException;\nimport org.bonitasoft.engine.scheduler.StatelessJob;\nimport org.bonitasoft.engine.scheduler.exception.SJobConfigurationException;\nimport org.bonitasoft.engine.scheduler.exception.SJobExecutionException;\nimport org.quartz.JobDataMap;\nimport org.quartz.impl.JobDetailImpl;\n\npublic class JobUtils {\n\n    static JobDetailImpl createJobDetails(long jobDescriptorId) {\n        JobDetailImpl jobDetail = new JobDetailImpl();\n        jobDetail.setName(\"someName\");\n        HashMap<String, Object> map = new HashMap<>();\n        map.put(\"jobId\", String.valueOf(jobDescriptorId));\n        JobDataMap jobDataMap = new JobDataMap(map);\n        jobDetail.setJobDataMap(jobDataMap);\n        return jobDetail;\n    }\n\n    static StatelessJob jobThatFails() {\n        return new StatelessJob() {\n\n            @Override\n            public String getName() {\n                return null;\n            }\n\n            @Override\n            public String getDescription() {\n                return null;\n            }\n\n            @Override\n            public void execute() throws SJobExecutionException, SFireEventException {\n                throw new SJobExecutionException(\"exception\");\n            }\n\n            @Override\n            public void setAttributes(Map<String, Serializable> attributes) throws SJobConfigurationException {\n\n            }\n        };\n    }\n\n    static StatelessJob jobThatSucceed() {\n        return new StatelessJob() {\n\n            @Override\n            public String getName() {\n                return null;\n            }\n\n            @Override\n            public String getDescription() {\n                return null;\n            }\n\n            @Override\n            public void execute() throws SJobExecutionException, SFireEventException {\n                throw new SJobExecutionException(\"exception\");\n            }\n\n            @Override\n            public void setAttributes(Map<String, Serializable> attributes) throws SJobConfigurationException {\n\n            }\n        };\n    }\n\n    static StatelessJob jobThatThrowASRetryableException() {\n        return new StatelessJob() {\n\n            @Override\n            public String getName() {\n                return null;\n            }\n\n            @Override\n            public String getDescription() {\n                return null;\n            }\n\n            @Override\n            public void execute() throws SJobExecutionException, SFireEventException {\n                throw new SRetryableException(\"exception\");\n            }\n\n            @Override\n            public void setAttributes(Map<String, Serializable> attributes) throws SJobConfigurationException {\n\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/test/java/org/bonitasoft/engine/scheduler/impl/JobWrapperTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.fail;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.doThrow;\nimport static org.mockito.Mockito.verify;\n\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.scheduler.JobIdentifier;\nimport org.bonitasoft.engine.scheduler.JobService;\nimport org.bonitasoft.engine.scheduler.StatelessJob;\nimport org.bonitasoft.engine.scheduler.exception.SJobExecutionException;\nimport org.bonitasoft.engine.services.PersistenceService;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.bonitasoft.engine.transaction.BonitaTransactionSynchronization;\nimport org.bonitasoft.engine.transaction.TransactionService;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Baptiste Mesta\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class JobWrapperTest {\n\n    private JobWrapper jobWrapper;\n    @Mock\n    private EventService eventService;\n    @Mock\n    private SessionAccessor sessionAccessor;\n    @Mock\n    private TransactionService transactionService;\n    @Mock\n    private PersistenceService persistenceService;\n    @Mock\n    private JobService jobService;\n    @Mock\n    private StatelessJob job;\n\n    @Before\n    public void before() {\n        jobWrapper = new JobWrapper(new JobIdentifier(145, \"MyJob\"), job, eventService,\n                transactionService, persistenceService, jobService);\n    }\n\n    @Test\n    public void should_executeJob_execute_the_job() throws Exception {\n        //when\n        jobWrapper.execute();\n        //then\n        verify(job).execute();\n\n    }\n\n    @Test\n    public void should_executeJob_call_flush_on_persistence() throws Exception {\n        //when\n        jobWrapper.execute();\n        //then\n        verify(persistenceService).flushStatements();\n    }\n\n    @Test\n    public void should_execute_call_rollback_on_error() throws Exception {\n        //given\n        doThrow(new RuntimeException()).when(job).execute();\n        //when\n        try {\n\n            jobWrapper.execute();\n            fail(\"should have thrown exception\");\n        } catch (SJobExecutionException e) {\n            assertThat(e.getCause()).isInstanceOf(RuntimeException.class);\n        }\n        //then\n        verify(transactionService).setRollbackOnly();\n\n    }\n\n    @Test\n    public void should_handleFailure_register_synchro() throws Exception {\n        //given\n\n        //when\n        jobWrapper.handleFailure(new RuntimeException());\n\n        //then\n        verify(transactionService).registerBonitaSynchronization(any(BonitaTransactionSynchronization.class));\n\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/test/java/org/bonitasoft/engine/scheduler/impl/LogJob.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.impl;\n\nimport org.quartz.Job;\nimport org.quartz.JobExecutionContext;\n\npublic class LogJob implements Job {\n\n    @Override\n    public void execute(final JobExecutionContext context) {\n        System.out.println(\"It works !!!!!!!!!!!! \");\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/test/java/org/bonitasoft/engine/scheduler/impl/MonitoringJobListenerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.scheduler.impl.MonitoringJobListener.JOB_JOBS_EXECUTED;\nimport static org.bonitasoft.engine.scheduler.impl.MonitoringJobListener.JOB_JOBS_RUNNING;\n\nimport java.time.Duration;\n\nimport io.micrometer.core.instrument.Clock;\nimport io.micrometer.core.instrument.MeterRegistry;\nimport io.micrometer.core.instrument.simple.SimpleMeterRegistry;\nimport org.junit.Test;\n\npublic class MonitoringJobListenerTest {\n\n    private final MeterRegistry meterRegistry = new SimpleMeterRegistry(\n            // So that micrometer updates its counters every 1 ms:\n            k -> k.equals(\"simple.step\") ? Duration.ofMillis(1).toString() : null,\n            Clock.SYSTEM);\n    private final MonitoringJobListener monitoringJobListener = new MonitoringJobListener(meterRegistry);\n\n    @Test\n    public void should_count_executing_jobs() {\n        monitoringJobListener.jobToBeExecuted();\n        monitoringJobListener.jobToBeExecuted();\n        monitoringJobListener.jobToBeExecuted();\n        monitoringJobListener.jobWasExecuted(null, null);\n\n        var gauge = meterRegistry.find(JOB_JOBS_RUNNING).gauge();\n        assertThat(gauge).isNotNull();\n        assertThat(gauge.value()).isEqualTo(2);\n    }\n\n    @Test\n    public void should_count_executed_jobs() {\n        monitoringJobListener.jobToBeExecuted();\n        monitoringJobListener.jobToBeExecuted();\n        monitoringJobListener.jobToBeExecuted();\n        monitoringJobListener.jobWasExecuted(null, null);\n        monitoringJobListener.jobWasExecuted(null, null);\n\n        var counter = meterRegistry.find(JOB_JOBS_EXECUTED).counter();\n        assertThat(counter).isNotNull();\n        assertThat(counter.count()).isEqualTo(2);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/test/java/org/bonitasoft/engine/scheduler/impl/QuartzJobListenerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.impl;\n\nimport static java.util.Collections.singletonList;\nimport static org.bonitasoft.engine.scheduler.impl.JobUtils.createJobDetails;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Date;\n\nimport org.bonitasoft.engine.scheduler.BonitaJobListener;\nimport org.bonitasoft.engine.scheduler.exception.SSchedulerException;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\nimport org.quartz.DateBuilder.IntervalUnit;\nimport org.quartz.Job;\nimport org.quartz.JobBuilder;\nimport org.quartz.JobDataMap;\nimport org.quartz.JobDetail;\nimport org.quartz.JobExecutionContext;\nimport org.quartz.Scheduler;\nimport org.quartz.impl.JobExecutionContextImpl;\nimport org.quartz.impl.RemoteScheduler;\nimport org.quartz.impl.calendar.WeeklyCalendar;\nimport org.quartz.impl.triggers.CalendarIntervalTriggerImpl;\nimport org.quartz.spi.OperableTrigger;\nimport org.quartz.spi.TriggerFiredBundle;\n\n/**\n * @author Celine Souchet\n * @version 6.4.0\n * @since 6.4.0\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class QuartzJobListenerTest {\n\n    private QuartzJobListener quartzJobListener;\n\n    @Mock\n    private BonitaJobListener bonitaJobListener;\n\n    private JobExecutionContext context;\n    @Mock\n    private SchedulerServiceImpl schedulerService;\n\n    @Before\n    public void setUp() {\n        quartzJobListener = new QuartzJobListener(singletonList(bonitaJobListener));\n\n        final Scheduler scheduler = new RemoteScheduler(\"schedId\", \"host\", 1589);\n        final JobDataMap jobDataMap = new JobDataMap();\n        jobDataMap.getWrappedMap().put(\"jobId\", \"96\");\n        final JobDetail jobDetail = JobBuilder.newJob().withIdentity(\"jobName\", \"jobGroup\").setJobData(jobDataMap)\n                .ofType(LogJob.class).build();\n\n        final OperableTrigger trigger = new CalendarIntervalTriggerImpl(\"name\", \"group\", \"jobName\", \"jobGroup\",\n                new Date(), new Date(), IntervalUnit.DAY, 6);\n        final TriggerFiredBundle firedBundle = new TriggerFiredBundle(jobDetail, trigger, new WeeklyCalendar(), true,\n                new Date(), new Date(),\n                new Date(), new Date());\n        final Job job = new LogJob();\n        context = new JobExecutionContextImpl(scheduler, firedBundle, job);\n    }\n\n    /**\n     * Test method for {@link QuartzJobListener#jobToBeExecuted(org.quartz.JobExecutionContext)}.\n     */\n    @Test\n    public final void jobToBeExecuted() {\n\n        // When\n        quartzJobListener.jobToBeExecuted(context);\n\n        // then\n        verify(bonitaJobListener).jobToBeExecuted();\n    }\n\n    @Test\n    public final void jobExecutionVetoed() {\n        // Given\n        final Scheduler scheduler = new RemoteScheduler(\"schedId\", \"host\", 1589);\n        final JobDataMap jobDataMap = new JobDataMap();\n        jobDataMap.getWrappedMap().put(\"jobId\", \"96\");\n        final JobDetail jobDetail = JobBuilder.newJob().withIdentity(\"jobName\", \"jobGroup\").setJobData(jobDataMap)\n                .ofType(LogJob.class).build();\n\n        final OperableTrigger trigger = new CalendarIntervalTriggerImpl(\"name\", \"group\", \"jobName\", \"jobGroup\",\n                new Date(), new Date(), IntervalUnit.DAY, 6);\n        final TriggerFiredBundle firedBundle = new TriggerFiredBundle(jobDetail, trigger, new WeeklyCalendar(), true,\n                new Date(), new Date(),\n                new Date(), new Date());\n        final ConcurrentQuartzJob job = new ConcurrentQuartzJob();\n        job.setSchedulerService(schedulerService);\n        job.setJobDetails(createJobDetails(2));\n        final JobExecutionContext context = new JobExecutionContextImpl(scheduler, firedBundle, job);\n\n        // When\n        quartzJobListener.jobExecutionVetoed(context);\n\n        // then\n        verify(bonitaJobListener).jobExecutionVetoed();\n    }\n\n    @Test\n    public final void jobWasExecuted() {\n        // When\n        quartzJobListener.jobWasExecuted(context, null);\n\n        // then\n        verify(bonitaJobListener).jobWasExecuted(anyMap(), nullable(SSchedulerException.class));\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/test/java/org/bonitasoft/engine/scheduler/impl/QuartzSchedulerExecutorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.impl;\n\nimport static java.util.Arrays.asList;\nimport static java.util.Collections.singletonList;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.junit.Assert.*;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.argThat;\nimport static org.mockito.Mockito.*;\nimport static org.quartz.JobKey.jobKey;\n\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.scheduler.BonitaJobListener;\nimport org.bonitasoft.engine.scheduler.exception.SSchedulerException;\nimport org.bonitasoft.engine.scheduler.trigger.OneShotTrigger;\nimport org.bonitasoft.engine.scheduler.trigger.Trigger;\nimport org.bonitasoft.engine.scheduler.trigger.Trigger.MisfireRestartPolicy;\nimport org.bonitasoft.engine.scheduler.trigger.UnixCronTrigger;\nimport org.bonitasoft.engine.transaction.BonitaTransactionSynchronization;\nimport org.bonitasoft.engine.transaction.TransactionService;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\nimport org.quartz.CronTrigger;\nimport org.quartz.JobBuilder;\nimport org.quartz.JobDetail;\nimport org.quartz.JobKey;\nimport org.quartz.ListenerManager;\nimport org.quartz.Scheduler;\nimport org.quartz.SchedulerException;\nimport org.quartz.Trigger.TriggerState;\nimport org.quartz.TriggerBuilder;\nimport org.quartz.TriggerKey;\nimport org.quartz.impl.matchers.GroupMatcher;\nimport org.quartz.impl.triggers.SimpleTriggerImpl;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class QuartzSchedulerExecutorTest {\n\n    private static final String JOB_NAME = \"jobName\";\n\n    private static final long JOB_ID = 1L;\n\n    @Mock\n    private BonitaSchedulerFactory schedulerFactory;\n\n    @Mock\n    private TransactionService transactionService;\n\n    @Mock\n    private Scheduler scheduler;\n    @Mock\n    private ListenerManager listenerManager;\n\n    private final JobDetail jobDetail = JobBuilder.newJob(ConcurrentQuartzJob.class).withIdentity(JOB_NAME).build();\n\n    @Mock\n    private org.quartz.Trigger trigger1;\n    @Mock\n    private org.quartz.Trigger trigger2;\n\n    private QuartzSchedulerExecutor quartzSchedulerExecutor;\n\n    @Before\n    public void before() throws Exception {\n        when(schedulerFactory.getScheduler()).thenReturn(scheduler);\n        when(scheduler.getListenerManager()).thenReturn(listenerManager);\n        when(transactionService.isTransactionActive()).thenReturn(true);\n        quartzSchedulerExecutor = initQuartzScheduler(false);\n    }\n\n    private QuartzSchedulerExecutor initQuartzScheduler(final boolean useOptimization) throws SSchedulerException {\n        final QuartzSchedulerExecutor quartz = new QuartzSchedulerExecutor(schedulerFactory, transactionService,\n                useOptimization);\n        quartz.start();\n        return quartz;\n    }\n\n    private Set<JobKey> newSet(final JobKey... jobKeys) {\n        final HashSet<JobKey> set = new HashSet<>();\n        set.addAll(asList(jobKeys));\n        return set;\n    }\n\n    @Test\n    public void pauseTriggers_should_pause_jobs() throws Exception {\n        quartzSchedulerExecutor.pauseJobs();\n\n        verify(scheduler).pauseTriggers(GroupMatcher.anyTriggerGroup());\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Test(expected = SSchedulerException.class)\n    public void pauseJobs_should_throw_exception_if_error_occurs() throws Exception {\n        doThrow(SchedulerException.class).when(scheduler).pauseTriggers(any(GroupMatcher.class));\n\n        quartzSchedulerExecutor.pauseJobs();\n    }\n\n    @Test\n    public void resumeJobs_should_resume_jobs() throws Exception {\n        quartzSchedulerExecutor.resumeJobs();\n\n        verify(scheduler).resumeTriggers(GroupMatcher.anyTriggerGroup());\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Test(expected = SSchedulerException.class)\n    public void resumeJobs_should_throw_exception_if_error_occurs_when_resuming_jobs() throws Exception {\n        doThrow(SchedulerException.class).when(scheduler).resumeTriggers(any(GroupMatcher.class));\n\n        quartzSchedulerExecutor.resumeJobs();\n    }\n\n    @Test\n    public void getQuartzTrigger_should_getQuartzTrigger_with_restart_ALL_have_a_ignore_misfire_policy() {\n        // given\n        final Date triggerEndTime = new Date(System.currentTimeMillis() + 10000);\n        final UnixCronTrigger unixCronTrigger = new UnixCronTrigger(\"MyTrigger\", triggerEndTime, \"0/5 * * * * ??\",\n                MisfireRestartPolicy.ALL);\n\n        // when\n        final CronTrigger quartzTrigger = (CronTrigger) quartzSchedulerExecutor.getQuartzTrigger(unixCronTrigger,\n                \"MyJob\");\n\n        // then\n        assertEquals(CronTrigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY, quartzTrigger.getMisfireInstruction());\n    }\n\n    @Test\n    public void getQuartzTrigger_should_getQuartzTrigger_with_restart_NONE_have_a_do_nothing_misfire_policy() {\n        // given\n        final Date triggerEndTime = new Date(System.currentTimeMillis() + 10000);\n        final UnixCronTrigger unixCronTrigger = new UnixCronTrigger(\"MyTrigger\", triggerEndTime, \"0/5 * * * * ??\",\n                MisfireRestartPolicy.NONE);\n\n        // when\n        final CronTrigger quartzTrigger = (CronTrigger) quartzSchedulerExecutor.getQuartzTrigger(unixCronTrigger,\n                \"MyJob\");\n\n        // then\n        assertEquals(CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING, quartzTrigger.getMisfireInstruction());\n    }\n\n    @Test\n    public void getQuartzTrigger_should_getQuartzTrigger_with_restart_ONE_have_a_fire_once_misfire_policy() {\n        // given\n        final Date triggerEndTime = new Date(System.currentTimeMillis() + 10000);\n        final UnixCronTrigger unixCronTrigger = new UnixCronTrigger(\"MyTrigger\", triggerEndTime, \"0/5 * * * * ??\",\n                MisfireRestartPolicy.ONE);\n\n        // when\n        final CronTrigger quartzTrigger = (CronTrigger) quartzSchedulerExecutor.getQuartzTrigger(unixCronTrigger,\n                \"MyJob\");\n\n        // then\n        assertEquals(CronTrigger.MISFIRE_INSTRUCTION_FIRE_ONCE_NOW, quartzTrigger.getMisfireInstruction());\n    }\n\n    @Test\n    public void delete_job_should_pause_and_delete_job_for_a_given_name() throws Exception {\n        // Given\n        final JobKey jobKey = jobKey(\"aName\");\n\n        // When\n        quartzSchedulerExecutor.delete(jobKey.getName());\n\n        // Then\n        verify(scheduler).pauseJob(jobKey);\n        verify(scheduler).deleteJob(jobKey);\n    }\n\n    @Test\n    public void delete_should_throw_exception_when_error_occurs_in_job_deletion() throws Exception {\n        final JobKey job = jobKey(\"aName\");\n        when(scheduler.deleteJob(job)).thenThrow(new SchedulerException());\n\n        assertThatExceptionOfType(SSchedulerException.class)\n                .isThrownBy(() -> quartzSchedulerExecutor.delete(job.getName()))\n                .withCauseInstanceOf(SchedulerException.class);\n    }\n\n    @Test\n    public void deleteJobs_should_delete_jobs() throws Exception {\n        final JobKey toBeDeleted = jobKey(\"job1\");\n        final JobKey toBeDeletedAlso = jobKey(\"job2\");\n        when(scheduler.getJobKeys(GroupMatcher.anyJobGroup())).thenReturn(newSet(toBeDeleted, toBeDeletedAlso));\n\n        quartzSchedulerExecutor.deleteJobs();\n\n        verify(scheduler).deleteJob(toBeDeleted);\n        verify(scheduler).deleteJob(toBeDeletedAlso);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Test(expected = SSchedulerException.class)\n    public void deleteJobs_should_throw_exception_when_error_occurs_in_jobs_deletion() throws Exception {\n        doThrow(SchedulerException.class).when(scheduler).getJobKeys(any(GroupMatcher.class));\n\n        quartzSchedulerExecutor.deleteJobs();\n    }\n\n    @Test\n    public void getJobs_should_get_job_names() throws Exception {\n        final JobKey toBeRetrieved = jobKey(\"job1\");\n        final JobKey toBeRetrievedAlso = jobKey(\"job2\");\n        when(scheduler.getJobKeys(GroupMatcher.anyJobGroup())).thenReturn(newSet(toBeRetrieved, toBeRetrievedAlso));\n\n        final List<String> jobs = quartzSchedulerExecutor.getJobs();\n\n        assertThat(jobs).containsOnly(toBeRetrieved.getName(), toBeRetrievedAlso.getName());\n    }\n\n    @Test\n    public void getJobs_should_throw_exception_when_error_occurs_on_job_names_fetching() throws Exception {\n        doThrow(SchedulerException.class).when(scheduler).getJobKeys(GroupMatcher.anyJobGroup());\n\n        assertThatExceptionOfType(SSchedulerException.class)\n                .isThrownBy(() -> quartzSchedulerExecutor.getJobs())\n                .withCauseInstanceOf(SchedulerException.class);\n    }\n\n    @Test\n    public void getJobs_should_get_job_names_for_all_group_name() throws Exception {\n        final JobKey job1 = jobKey(\"job1\", Scheduler.DEFAULT_GROUP);\n        final JobKey job2 = jobKey(\"job2\", Scheduler.DEFAULT_GROUP);\n        final JobKey job3 = jobKey(\"job3\", \"anotherGroup\");\n        doReturn(newSet(job1, job2, job3)).when(scheduler).getJobKeys(GroupMatcher.anyJobGroup());\n\n        final List<String> jobs = quartzSchedulerExecutor.getJobs();\n\n        assertThat(jobs).containsOnly(job1.getName(), job2.getName(), job3.getName());\n    }\n\n    @Test(expected = SSchedulerException.class)\n    public void start_should_not_start_twice() throws Exception {\n        // given\n        doReturn(true).when(scheduler).isStarted();\n\n        // when\n        quartzSchedulerExecutor.start();\n    }\n\n    @Test\n    public void schedule_disallowConcurrentExecution() throws Exception {\n        scheduleJob(quartzSchedulerExecutor, true, 0);\n    }\n\n    @Test\n    public void schedule_allowConcurrentExecution() throws Exception {\n        scheduleJob(quartzSchedulerExecutor, false, 0);\n    }\n\n    @Test\n    public void schedule_with_optimisation() throws Exception {\n        final QuartzSchedulerExecutor executor = quartzSchedulerExecutor = initQuartzScheduler(true);\n\n        scheduleJob(executor, true, 1);\n    }\n\n    private void scheduleJob(final QuartzSchedulerExecutor executor, final boolean disallowConcurrentExecution,\n            final int expectedOptimizationCall)\n            throws Exception {\n        // when\n        executor.schedule(1L, JOB_NAME, new OneShotTrigger(\"oneShot\", new Date(System.currentTimeMillis())),\n                disallowConcurrentExecution);\n\n        // then\n        verify(scheduler).scheduleJob(any(JobDetail.class), any(org.quartz.Trigger.class));\n        verify(transactionService, times(expectedOptimizationCall))\n                .registerBonitaSynchronization(any(BonitaTransactionSynchronization.class));\n    }\n\n    @Test\n    public void schedule_with_exception() throws Exception {\n        // given\n        doThrow(SchedulerException.class).when(scheduler).scheduleJob(any(JobDetail.class),\n                any(org.quartz.Trigger.class));\n\n        // when and then exception\n        assertThatExceptionOfType(SSchedulerException.class)\n                .isThrownBy(() -> quartzSchedulerExecutor.schedule(1L, JOB_NAME,\n                        new OneShotTrigger(\"oneShot\", new Date(System.currentTimeMillis())), true))\n                .withCauseInstanceOf(SchedulerException.class);\n    }\n\n    @Test\n    public void schedule_should_use_default_group_in_job_details() throws Exception {\n        final Trigger trigger = new OneShotTrigger(\"trigger\", new Date(), 1, MisfireRestartPolicy.NONE);\n\n        quartzSchedulerExecutor.schedule(10L, \"myJob\", trigger, true);\n\n        final ArgumentCaptor<JobDetail> jobDetailCaptor = ArgumentCaptor.forClass(JobDetail.class);\n        verify(scheduler).scheduleJob(jobDetailCaptor.capture(), any(org.quartz.Trigger.class));\n        final String group = jobDetailCaptor.getValue().getKey().getGroup();\n        assertThat(group).isEqualTo(Scheduler.DEFAULT_GROUP);\n    }\n\n    @Test\n    public void executeAgain_should_schedule_job_when_no_more_job_is_registered() throws Exception {\n        // given\n        doReturn(null).when(scheduler).getJobDetail(any(JobKey.class));\n\n        // when\n        quartzSchedulerExecutor.executeAgain(JOB_ID, JOB_NAME, true, 5000);\n\n        // then: create a trigger and a job details\n        verify(scheduler, never()).scheduleJob(any(org.quartz.Trigger.class));\n        verify(scheduler).scheduleJob(any(JobDetail.class), any(org.quartz.Trigger.class));\n        verify(scheduler, never()).rescheduleJob(any(org.quartz.TriggerKey.class), any(org.quartz.Trigger.class));\n    }\n\n    @Test\n    public void executeAgain_should_schedule_job_when_job_do_not_have_a_trigger_anymore() throws Exception {\n        // given\n        doReturn(jobDetail).when(scheduler).getJobDetail(any(JobKey.class));\n        doReturn(Collections.emptyList()).when(scheduler).getTriggersOfJob(any(JobKey.class));\n\n        // when\n        quartzSchedulerExecutor.executeAgain(JOB_ID, JOB_NAME, true, 5000);\n\n        // then: create a new trigger to execute it\n        verify(scheduler).scheduleJob(any(org.quartz.Trigger.class));\n        verify(scheduler, never()).scheduleJob(any(JobDetail.class), any(org.quartz.Trigger.class));\n        verify(scheduler, never()).rescheduleJob(any(org.quartz.TriggerKey.class), any(org.quartz.Trigger.class));\n    }\n\n    @Test\n    public void executeAgain_should_schedule_job_when_job_have_no_trigger_that_may_not_fire_again() throws Exception {\n        // given\n        doReturn(jobDetail).when(scheduler).getJobDetail(any(JobKey.class));\n        doReturn(asList(triggerThatMayFireAgain(), triggerThatMayFireAgain())).when(scheduler)\n                .getTriggersOfJob(any(JobKey.class));\n\n        // when\n        quartzSchedulerExecutor.executeAgain(JOB_ID, JOB_NAME, true, 5000);\n\n        // then: create a new trigger to execute it\n        verify(scheduler).scheduleJob(any(org.quartz.Trigger.class));\n        verify(scheduler, never()).scheduleJob(any(JobDetail.class), any(org.quartz.Trigger.class));\n        verify(scheduler, never()).rescheduleJob(any(org.quartz.TriggerKey.class), any(org.quartz.Trigger.class));\n    }\n\n    private SimpleTriggerImpl triggerThatMayFireAgain() {\n        SimpleTriggerImpl simpleTrigger = (SimpleTriggerImpl) TriggerBuilder.newTrigger()\n                .withIdentity(JOB_NAME).build();\n        simpleTrigger.setNextFireTime(new Date());\n        return simpleTrigger;\n    }\n\n    @Test\n    public void executeAgain_should_reschedule_job_when_job_have_at_least_one_trigger_may_not_fire_again()\n            throws Exception {\n        // given\n        doReturn(jobDetail).when(scheduler).getJobDetail(any(JobKey.class));\n        doReturn(asList(triggerThatMayFireAgain(), triggerThatMayNotFireAgain())).when(scheduler)\n                .getTriggersOfJob(any(JobKey.class));\n\n        // when\n        quartzSchedulerExecutor.executeAgain(JOB_ID, JOB_NAME, true, 5000);\n\n        // then: create a new trigger to execute it\n        verify(scheduler, never()).scheduleJob(any(org.quartz.Trigger.class));\n        verify(scheduler, never()).scheduleJob(any(JobDetail.class), any(org.quartz.Trigger.class));\n        verify(scheduler).rescheduleJob(any(org.quartz.TriggerKey.class), any(org.quartz.Trigger.class));\n    }\n\n    private org.quartz.Trigger triggerThatMayNotFireAgain() {\n        return TriggerBuilder.newTrigger().withIdentity(JOB_NAME).build();\n    }\n\n    @Test\n    public void executeAgain_should_schedule_job_when_job_have_a_trigger_that_may_not_fire_again() throws Exception {\n        // given\n        doReturn(jobDetail).when(scheduler).getJobDetail(any(JobKey.class));\n        doReturn(singletonList(triggerThatMayNotFireAgain())).when(scheduler).getTriggersOfJob(any(JobKey.class));\n\n        // when\n        quartzSchedulerExecutor.executeAgain(JOB_ID, JOB_NAME, true, 5000);\n\n        // then: update the trigger ( reschedule )\n        verify(scheduler, never()).scheduleJob(any(org.quartz.Trigger.class));\n        verify(scheduler, never()).scheduleJob(any(JobDetail.class), any(org.quartz.Trigger.class));\n        verify(scheduler).rescheduleJob(any(org.quartz.TriggerKey.class), any(org.quartz.Trigger.class));\n    }\n\n    @Test\n    public void executeAgain_should_schedule_job_when_job_have_a_trigger_that_may_fire_again() throws Exception {\n        // given\n        doReturn(jobDetail).when(scheduler).getJobDetail(any(JobKey.class));\n        doReturn(singletonList(triggerThatMayFireAgain()))\n                .when(scheduler).getTriggersOfJob(any(JobKey.class));\n\n        // when\n        quartzSchedulerExecutor.executeAgain(JOB_ID, JOB_NAME, true, 5000);\n\n        // then: create a new trigger to execute it\n        verify(scheduler).scheduleJob(any(org.quartz.Trigger.class));\n        verify(scheduler, never()).scheduleJob(any(JobDetail.class), any(org.quartz.Trigger.class));\n        verify(scheduler, never()).rescheduleJob(any(org.quartz.TriggerKey.class), any(org.quartz.Trigger.class));\n    }\n\n    @Test\n    public void executeAgain_using_optimization() throws Exception {\n        // given\n        quartzSchedulerExecutor = initQuartzScheduler(true);\n        doReturn(jobDetail).when(scheduler).getJobDetail(any(JobKey.class));\n\n        // when\n        quartzSchedulerExecutor.executeAgain(JOB_ID, JOB_NAME, true, 5000);\n\n        // then\n        verify(transactionService).registerBonitaSynchronization(any(BonitaTransactionSynchronization.class));\n    }\n\n    @Test\n    public void mayFireAgain_should_return_true_when_some_trigger_may_fire_again() throws Exception {\n        when(trigger1.mayFireAgain()).thenReturn(false);\n        when(trigger2.mayFireAgain()).thenReturn(true);\n        doReturn(asList(trigger1, trigger2)).when(scheduler).getTriggersOfJob(any(JobKey.class));\n\n        boolean mayFireAgain = quartzSchedulerExecutor.mayFireAgain(JOB_NAME);\n\n        assertThat(mayFireAgain).isTrue();\n    }\n\n    @Test\n    public void mayFireAgain_should_return_false_when_no_trigger_may_fire_again() throws Exception {\n        when(trigger1.mayFireAgain()).thenReturn(false);\n        when(trigger2.mayFireAgain()).thenReturn(false);\n        doReturn(asList(trigger1, trigger2)).when(scheduler).getTriggersOfJob(any(JobKey.class));\n\n        boolean mayFireAgain = quartzSchedulerExecutor.mayFireAgain(JOB_NAME);\n\n        assertThat(mayFireAgain).isFalse();\n    }\n\n    @Test\n    public void rescheduleErroneousTriggers_should_throw_exception() throws Exception {\n        // given\n        doThrow(SchedulerException.class).when(scheduler).getTriggerKeys(GroupMatcher.anyTriggerGroup());\n\n        // when and then exception\n        assertThatExceptionOfType(SSchedulerException.class)\n                .isThrownBy(() -> quartzSchedulerExecutor.rescheduleErroneousTriggers())\n                .withCauseInstanceOf(SchedulerException.class);\n    }\n\n    @Test\n    public void rescheduleErroneousTriggers_should_pause_and_resume_trigger() throws Exception {\n        check_reschedule(TriggerState.ERROR, 1);\n    }\n\n    @Test\n    public void rescheduleErroneousTriggers_should_not_reschedule() throws Exception {\n        check_reschedule(TriggerState.COMPLETE, 0);\n    }\n\n    private void check_reschedule(final TriggerState triggerState, final int expectedNumberOfInvocations)\n            throws SchedulerException, SSchedulerException {\n        final Set<TriggerKey> triggerKeys = new HashSet<>();\n        final TriggerKey triggerKey = new TriggerKey(\"name\");\n        triggerKeys.add(triggerKey);\n\n        // given\n        doReturn(triggerState).when(scheduler).getTriggerState(triggerKey);\n        doReturn(triggerKeys).when(scheduler).getTriggerKeys(GroupMatcher.anyTriggerGroup());\n\n        // when\n        quartzSchedulerExecutor.rescheduleErroneousTriggers();\n\n        // then\n        verify(scheduler, times(expectedNumberOfInvocations)).pauseTrigger(any(TriggerKey.class));\n        verify(scheduler, times(expectedNumberOfInvocations)).resumeTrigger(any(TriggerKey.class));\n    }\n\n    @Test(expected = SSchedulerException.class)\n    public void is_started_should_throw_exception() throws Exception {\n        // given\n        doThrow(SchedulerException.class).when(scheduler).isStarted();\n\n        // when\n        quartzSchedulerExecutor.isStarted();\n\n        // then exception\n    }\n\n    @Test\n    public void is_started_should_be_true_when_scheduler_is_stated() throws Exception {\n        checkIsStarted(true, true, false);\n\n    }\n\n    @Test\n    public void is_started_should_be_false_when_scheduler_is_shutdown() throws Exception {\n        checkIsStarted(false, false, true);\n\n    }\n\n    @Test\n    public void is_started_should_be_false_when_scheduler_is_in_transitionnal_state() throws Exception {\n        checkIsStarted(false, true, true);\n\n    }\n\n    private void checkIsStarted(final boolean expectedResponse, final boolean schedulerStartStatus,\n            final boolean schedulerSchutdownStatus)\n            throws SchedulerException, SSchedulerException {\n        // given\n        doReturn(schedulerStartStatus).when(scheduler).isStarted();\n        doReturn(schedulerSchutdownStatus).when(scheduler).isShutdown();\n\n        // when\n        final boolean started = quartzSchedulerExecutor.isStarted();\n\n        // then exception\n        assertThat(started).isEqualTo(expectedResponse);\n    }\n\n    @Test\n    public void is_shutdown_should_throw_exception() throws Exception {\n        // given\n        doThrow(SchedulerException.class).when(scheduler).isShutdown();\n\n        // when and then exception\n        assertThatExceptionOfType(SSchedulerException.class)\n                .isThrownBy(() -> quartzSchedulerExecutor.isShutdown())\n                .withCauseInstanceOf(SchedulerException.class);\n    }\n\n    @Test\n    public void is_shutdown_should_be_true() throws Exception {\n        doReturn(true).when(scheduler).isShutdown();\n\n        final boolean started = quartzSchedulerExecutor.isShutdown();\n\n        assertThat(started).isTrue();\n    }\n\n    @Test\n    public void is_shutdown_should_be_false() throws Exception {\n        doReturn(false).when(scheduler).isShutdown();\n\n        boolean started = quartzSchedulerExecutor.isShutdown();\n\n        assertThat(started).isFalse();\n    }\n\n    @Test\n    public void rescheduleJob_should_throw_exception_when_rescheduleJob_failed() throws Exception {\n        // Given\n        final org.quartz.Trigger trigger = mock(org.quartz.Trigger.class);\n        doReturn(TriggerBuilder.newTrigger()).when(trigger).getTriggerBuilder();\n        doReturn(trigger).when(scheduler).getTrigger(any(TriggerKey.class));\n        doThrow(SchedulerException.class).when(scheduler).rescheduleJob(any(TriggerKey.class),\n                any(org.quartz.Trigger.class));\n\n        // When\n        assertThatExceptionOfType(SSchedulerException.class)\n                .isThrownBy(() -> quartzSchedulerExecutor.rescheduleJob(\"triggerName\", new Date()))\n                .withCauseInstanceOf(SchedulerException.class);\n    }\n\n    @Test\n    public void rescheduleJob_should_rescheduleJob() throws Exception {\n        // Given\n        final org.quartz.Trigger trigger = mock(org.quartz.Trigger.class);\n        doReturn(TriggerBuilder.newTrigger()).when(trigger).getTriggerBuilder();\n        doReturn(trigger).when(scheduler).getTrigger(any(TriggerKey.class));\n\n        // When\n        quartzSchedulerExecutor.rescheduleJob(\"triggerName\", new Date());\n\n        // Then\n        verify(scheduler).rescheduleJob(any(TriggerKey.class), any(org.quartz.Trigger.class));\n    }\n\n    @Test\n    public void isExistingJob_should_throw_exception_when_getJobDetail_failed() throws Exception {\n        // Given\n        doThrow(SchedulerException.class).when(scheduler).getJobDetail(any(JobKey.class));\n\n        // When\n        assertThatExceptionOfType(SSchedulerException.class)\n                .isThrownBy(() -> quartzSchedulerExecutor.isExistingJob(\"name\"))\n                .withCauseInstanceOf(SchedulerException.class);\n    }\n\n    @Test\n    public void isExistingJob_should_throw_exception_when_scheduler_is_shutdown() throws Exception {\n        // Given\n        doReturn(true).when(scheduler).isShutdown();\n\n        // When\n        assertThatExceptionOfType(SSchedulerException.class)\n                .isThrownBy(() -> quartzSchedulerExecutor.isExistingJob(\"name\"))\n                .withMessage(\"The scheduler is not started\");\n    }\n\n    @Test\n    public void isExistingJob_should_return_false_if_doesnt_exist_in_quartz() throws Exception {\n        // Given\n        doReturn(null).when(scheduler).getJobDetail(any(JobKey.class));\n\n        // When\n        final boolean existingJob = quartzSchedulerExecutor.isExistingJob(\"name\");\n\n        // Then\n        assertFalse(existingJob);\n    }\n\n    @Test\n    public void isExistingJob_should_return_true_if_exists_in_quartz() throws Exception {\n        // Given\n        doReturn(mock(JobDetail.class)).when(scheduler).getJobDetail(any(JobKey.class));\n\n        // When\n        final boolean existingJob = quartzSchedulerExecutor.isExistingJob(\"name\");\n\n        // Then\n        assertTrue(existingJob);\n    }\n\n    @Test(expected = SSchedulerException.class)\n    public void shutdown_should_throw_exception_when_scheduler_failed() throws Exception {\n        // Given\n        doThrow(SchedulerException.class).when(scheduler).shutdown(true);\n\n        quartzSchedulerExecutor.shutdown();\n    }\n\n    @Test\n    public void should_register_listeners_on_start() throws Exception {\n        BonitaJobListener listener1 = mock(BonitaJobListener.class);\n        BonitaJobListener listener2 = mock(BonitaJobListener.class);\n        quartzSchedulerExecutor = new QuartzSchedulerExecutor(schedulerFactory, transactionService, false);\n        quartzSchedulerExecutor.setJobListeners(asList(listener1, listener2));\n\n        quartzSchedulerExecutor.start();\n\n        verify(listenerManager).addJobListener(argThat((quartzListener) -> ((QuartzJobListener) quartzListener)\n                .getBonitaJobListeners().containsAll(asList(listener1, listener2))));\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/test/java/org/bonitasoft/engine/scheduler/impl/ReturnLogBuilder.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.impl;\n\nimport org.mockito.internal.stubbing.defaultanswers.ReturnsMocks;\nimport org.mockito.invocation.InvocationOnMock;\nimport org.mockito.stubbing.Answer;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class ReturnLogBuilder implements Answer<Object> {\n\n    private Object object;\n\n    public ReturnLogBuilder() {\n    }\n\n    public void setObject(final Object object) {\n        this.object = object;\n    }\n\n    @Override\n    public Object answer(final InvocationOnMock invocation) throws Throwable {\n        if (\"done\".equals(invocation.getMethod().getName())) {\n            return new ReturnsMocks().answer(invocation);\n        }\n        return object;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/test/java/org/bonitasoft/engine/scheduler/impl/SchedulerServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.impl;\n\nimport static java.util.Arrays.asList;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyLong;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.*;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Random;\n\nimport org.bonitasoft.engine.events.EventService;\nimport org.bonitasoft.engine.events.model.SEvent;\nimport org.bonitasoft.engine.scheduler.JobService;\nimport org.bonitasoft.engine.scheduler.SchedulerExecutor;\nimport org.bonitasoft.engine.scheduler.exception.SSchedulerException;\nimport org.bonitasoft.engine.scheduler.exception.jobDescriptor.SJobDescriptorCreationException;\nimport org.bonitasoft.engine.scheduler.model.SJobDescriptor;\nimport org.bonitasoft.engine.scheduler.model.SJobParameter;\nimport org.bonitasoft.engine.scheduler.trigger.Trigger;\nimport org.bonitasoft.engine.service.ServicesResolver;\nimport org.bonitasoft.engine.services.PersistenceService;\nimport org.bonitasoft.engine.transaction.TransactionService;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class SchedulerServiceImplTest {\n\n    private static final long JOB_DESCRIPTOR_ID = 32187L;\n\n    private SchedulerServiceImpl schedulerService;\n    @Mock\n    private SchedulerExecutor schedulerExecutor;\n    @Mock\n    private JobService jobService;\n    @Mock\n    private EventService eventService;\n    @Mock\n    private PersistenceService persistenceService;\n    @Mock\n    private ServicesResolver servicesResolver;\n    @Mock\n    private TransactionService transactionService;\n\n    @Before\n    public void setUp() {\n        transactionService = mock(TransactionService.class);\n\n        schedulerService = new SchedulerServiceImpl(schedulerExecutor, jobService, eventService,\n                transactionService, servicesResolver, persistenceService);\n    }\n\n    @Test\n    public void isStarted_return_true_if_schedulorExecutor_is_started() throws Exception {\n        when(schedulerExecutor.isStarted()).thenReturn(true);\n\n        final boolean started = schedulerService.isStarted();\n\n        assertThat(started).isTrue();\n    }\n\n    @Test\n    public void isStarted_return_false_if_schedulorExecutor_is_not_started() throws Exception {\n        when(schedulerExecutor.isStarted()).thenReturn(false);\n\n        final boolean started = schedulerService.isStarted();\n\n        assertThat(started).isFalse();\n    }\n\n    @Test\n    public void isStopped_return_true_if_executor_is_shutodown() throws Exception {\n        when(schedulerExecutor.isShutdown()).thenReturn(true);\n\n        final boolean stopped = schedulerService.isStopped();\n\n        assertThat(stopped).isTrue();\n    }\n\n    @Test\n    public void isStopped_return_false_if_executor_is_not_shutodown() throws Exception {\n        when(schedulerExecutor.isShutdown()).thenReturn(false);\n\n        final boolean stopped = schedulerService.isStopped();\n\n        assertThat(stopped).isFalse();\n    }\n\n    @Test\n    public void start_start_schedulerexecutor_and_fire_a_start_event() throws Exception {\n        schedulerService.start();\n\n        verify(schedulerExecutor).start();\n        verify(eventService).fireEvent(any(SEvent.class));\n    }\n\n    @Test\n    public void stop_shutdown_schedulerexecutor_and_fire_a_stop_event() throws Exception {\n        schedulerService.stop();\n\n        verify(schedulerExecutor).shutdown();\n        verify(eventService).fireEvent(any(SEvent.class));\n    }\n\n    @Test\n    public void delete_delete_job_and_jobDescription() throws Exception {\n        final String jobName = \"aJobName\";\n\n        schedulerService.delete(jobName);\n\n        verify(schedulerExecutor).delete(jobName);\n        verify(jobService).deleteJobDescriptorByJobName(jobName);\n    }\n\n    @Test\n    public void delete_return_schedulerexecutor_deletion_status() throws Exception {\n        final boolean expectedDeletionStatus = new Random().nextBoolean();\n        final String jobName = \"jobName\";\n        when(schedulerExecutor.delete(jobName)).thenReturn(expectedDeletionStatus);\n\n        final boolean deletionStatus = schedulerService.delete(jobName);\n\n        assertThat(deletionStatus).isEqualTo(expectedDeletionStatus);\n    }\n\n    @Test(expected = SSchedulerException.class)\n    public void cannot_execute_a_job_with_a_null_trigger() throws Exception {\n        final SJobDescriptor jobDescriptor = mock(SJobDescriptor.class);\n        final List<SJobParameter> parameters = new ArrayList<>();\n\n        schedulerService.schedule(jobDescriptor, parameters, null);\n    }\n\n    @Test(expected = SSchedulerException.class)\n    public void cannot_schedule_a_null_job() throws Exception {\n        final Trigger trigger = mock(Trigger.class);\n        when(jobService.createJobDescriptor(nullable(SJobDescriptor.class)))\n                .thenThrow(new SJobDescriptorCreationException(\"\"));\n\n        schedulerService.schedule(null, trigger);\n    }\n\n    @Test\n    public void rescheduleErroneousTriggers_call_same_method_in_schedulerexecutor() throws Exception {\n        schedulerService.rescheduleErroneousTriggers();\n\n        verify(schedulerExecutor).rescheduleErroneousTriggers();\n    }\n\n    @Test\n    public void should_pauseJobs_call_schedulerExecutor() throws Exception {\n        schedulerService.resumeJobs();\n        verify(schedulerExecutor).resumeJobs();\n    }\n\n    @Test\n    public void should_pauseJobs_call_schedulerExecutor_rethrow_exception() throws Exception {\n        doThrow(new SSchedulerException(\"My exception\")).when(schedulerExecutor).resumeJobs();\n\n        assertThatExceptionOfType(SSchedulerException.class)\n                .isThrownBy(() -> schedulerService.resumeJobs())\n                .withMessage(\"My exception\");\n    }\n\n    @Test\n    public void schedule_should_store_jobDescriptor_store_parameters_and_call_executor_schedule()\n            throws Exception {\n        // given\n        final long jogDescriptorId = 7L;\n        final String jobName = \"myJob\";\n        final SJobDescriptor jobDescriptor = mock(SJobDescriptor.class);\n        given(jobDescriptor.getId()).willReturn(jogDescriptorId);\n        given(jobDescriptor.getJobName()).willReturn(jobName);\n        given(jobService.createJobDescriptor(jobDescriptor)).willReturn(jobDescriptor);\n        final Trigger trigger = mock(Trigger.class);\n        final List<SJobParameter> parameters = Collections.singletonList(mock(SJobParameter.class));\n\n        // when\n        schedulerService.schedule(jobDescriptor, parameters, trigger);\n\n        // then\n        verify(jobService).createJobDescriptor(jobDescriptor);\n        verify(jobService).createJobParameters(parameters, jogDescriptorId);\n        verify(schedulerExecutor).schedule(jogDescriptorId, jobName, trigger, false);\n    }\n\n    @Test\n    public void should_delete_all_jobs() throws Exception {\n        schedulerService.deleteJobs();\n\n        verify(schedulerExecutor).deleteJobs();\n        verify(jobService).deleteAllJobDescriptors();\n    }\n\n    @Test\n    public void rescheduleJob_should_call_rescheduleJob() throws Exception {\n        // Given\n        final String triggerName = \"triggerName\";\n        final Date triggerStartTime = new Date();\n\n        // When\n        schedulerService.rescheduleJob(triggerName, triggerStartTime);\n\n        // Then\n        verify(schedulerExecutor).rescheduleJob(triggerName, triggerStartTime);\n    }\n\n    @Test\n    public void should_execute_again_an_existing_job() throws Exception {\n        SJobDescriptor jobDescriptor = SJobDescriptor.builder().jobClassName(\"jobClassName\")\n                .jobName(\"jobName\")\n                .id(JOB_DESCRIPTOR_ID).build();\n        doReturn(jobDescriptor)\n                .when(jobService).getJobDescriptor(JOB_DESCRIPTOR_ID);\n\n        schedulerService.executeAgain(JOB_DESCRIPTOR_ID, 5000);\n\n        verify(jobService, never()).setJobParameters(anyLong(), any());\n        verify(schedulerExecutor).executeAgain(JOB_DESCRIPTOR_ID, \"jobName\", false, 5000);\n        verify(jobService, never()).deleteJobLogs(JOB_DESCRIPTOR_ID);\n    }\n\n    @Test\n    public void should_retry_a_job_that_failed() throws Exception {\n        SJobDescriptor jobDescriptor = SJobDescriptor.builder().jobClassName(\"jobClassName\")\n                .jobName(\"jobName\")\n                .id(JOB_DESCRIPTOR_ID).build();\n        doReturn(jobDescriptor)\n                .when(jobService).getJobDescriptor(JOB_DESCRIPTOR_ID);\n\n        schedulerService.retryJobThatFailed(JOB_DESCRIPTOR_ID);\n\n        verify(jobService, never()).setJobParameters(anyLong(), any());\n        verify(schedulerExecutor).executeAgain(JOB_DESCRIPTOR_ID, \"jobName\", false, 0);\n        verify(jobService).deleteJobLogs(JOB_DESCRIPTOR_ID);\n    }\n\n    @Test\n    public void should_retry_a_job_that_failed_while_changing_parameters() throws Exception {\n        SJobDescriptor jobDescriptor = SJobDescriptor.builder().jobClassName(\"jobClassName\")\n                .jobName(\"jobName\")\n                .id(JOB_DESCRIPTOR_ID).build();\n        doReturn(jobDescriptor)\n                .when(jobService).getJobDescriptor(JOB_DESCRIPTOR_ID);\n        List<SJobParameter> parameters = asList(SJobParameter.builder().key(\"a\").value(1).build(),\n                SJobParameter.builder().key(\"b\").value(2).build());\n\n        schedulerService.retryJobThatFailed(JOB_DESCRIPTOR_ID, parameters);\n\n        verify(jobService).setJobParameters(JOB_DESCRIPTOR_ID, parameters);\n        verify(schedulerExecutor).executeAgain(JOB_DESCRIPTOR_ID, \"jobName\", false, 0);\n        verify(jobService).deleteJobLogs(JOB_DESCRIPTOR_ID);\n    }\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/test/java/org/bonitasoft/engine/scheduler/impl/TransactionalSimpleJobFactoryTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.scheduler.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.scheduler.impl.JobUtils.createJobDetails;\nimport static org.mockito.BDDMockito.given;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\nimport org.quartz.Job;\nimport org.quartz.Scheduler;\nimport org.quartz.impl.JobDetailImpl;\nimport org.quartz.spi.TriggerFiredBundle;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class TransactionalSimpleJobFactoryTest {\n\n    @Mock\n    private SchedulerServiceImpl schedulerServiceImpl;\n    @Mock\n    private TriggerFiredBundle bundle;\n    @Mock\n    private Scheduler scheduler;\n\n    @InjectMocks\n    TransactionalSimpleJobFactory factory;\n\n    @Test\n    public void should_inject_scheduler_and_job_details_on_new_job() throws Exception {\n        JobDetailImpl jobDetails = createJobDetails(2);\n        jobDetails.setJobClass(ConcurrentQuartzJob.class);\n        given(bundle.getJobDetail()).willReturn(jobDetails);\n\n        Job newJob = factory.newJob(bundle, scheduler);\n\n        assertThat(newJob).isInstanceOf(AbstractQuartzJob.class);\n        assertThat(((AbstractQuartzJob) newJob).getSchedulerService()).isEqualTo(schedulerServiceImpl);\n        assertThat(((AbstractQuartzJob) newJob).getJobDetail()).isEqualTo(jobDetails);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-scheduler/src/test/resources/logback-test.xml",
    "content": "<configuration>\n\n\t<appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n\t\t<!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->\n\t\t<encoder>\n\t\t\t<pattern>|%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg%n</pattern>\n\t\t</encoder>\n\t</appender>\n\n\t<logger name=\"org.bonitasoft\" level=\"INFO\" />\n\t<logger name=\"org.quartz.simpl.RAMJobStore\" level=\"INFO\" />\n\n\t<root level=\"WARN\">\n\t\t<appender-ref ref=\"STDOUT\" />\n\t</root>\n\t<!-- Show events of synchro repository -->\n\t<!-- <logger name=\"org.bonitasoft.engine.test.synchro.SynchroRepository\" level=\"DEBUG\" /> -->\n\t<!-- Show stack traces when there is error on jobs -->\n\t<!-- <logger name=\"org.bonitasoft.engine.scheduler.impl.JobWrapper\" level=\"DEBUG\" /> -->\n\t<!-- Show lock acquire/release -->\n<!-- \t<logger name=\"org.bonitasoft.engine.lock.impl.MemoryLockService\" level=\"TRACE\" /> -->\n<!-- \t<logger naorg.bonitasoft.engine.execution.work.InSessionBonitaWorkWork\" level=\"DEBUG\" /> -->\n\n\n\t<!-- Quartz log for job lifecycle -->\n\t<!-- <property name=\"baseLogFileName\" value=\"target/log/quartzJobs\" />\n\n\t<appender name=\"FILE\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n\t\t<file>${baseLogFileName}.log</file>\n\n\t\tSupport multiple-JVM writing to the same log file\n\t\t<prudent>true</prudent>\n\t\t<rollingPolicy class=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\">\n\t\t\t<fileNamePattern>${baseLogFileName}-%d{yyyy-MM-dd}.log.gz</fileNamePattern>\n\t\t\t<maxHistory>30</maxHistory>\n\t\t\t<cleanHistoryOnStart>true</cleanHistoryOnStart>\n\t\t</rollingPolicy>\n\t\t<encoder>\n\t\t\t<pattern>|%d{HH:mm:ss.SSS}| %msg%n</pattern>\n\t\t</encoder>\n\t</appender>\n\n\t<logger name=\"org.bonitasoft.engine.scheduler.impl.FileJobListener\" level=\"INFO\">\n\t\t<appender-ref ref=\"FILE\" />\n\t</logger> -->\n\t\n</configuration>\n"
  },
  {
    "path": "services/bonita-session/build.gradle",
    "content": "\n\ndependencies {\n    api project(':services:bonita-commons')\n    api project(':services:bonita-builder')\n    testImplementation libs.assertj\n    testImplementation libs.mockitoCore\n    testImplementation libs.logback\n\n    annotationProcessor libs.lombok\n    compileOnly libs.lombok\n}\n"
  },
  {
    "path": "services/bonita-session/src/main/java/org/bonitasoft/engine/session/SSessionAlreadyExistsException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.session;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SSessionAlreadyExistsException extends SSessionException {\n\n    private static final long serialVersionUID = -3954242079366628148L;\n\n    public SSessionAlreadyExistsException(final String message) {\n        super(message);\n    }\n\n    public SSessionAlreadyExistsException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SSessionAlreadyExistsException(final Object... arguments) {\n        super(arguments);\n    }\n\n    public SSessionAlreadyExistsException(final Throwable cause, final Object... arguments) {\n        super(cause, arguments);\n    }\n\n    public SSessionAlreadyExistsException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-session/src/main/java/org/bonitasoft/engine/session/SSessionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.session;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SSessionException extends SBonitaException {\n\n    private static final long serialVersionUID = 7690456826693549647L;\n\n    public SSessionException(final String message) {\n        super(message);\n    }\n\n    public SSessionException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SSessionException(final Object... arguments) {\n        super(arguments);\n    }\n\n    public SSessionException(final Throwable cause, final Object... arguments) {\n        super(cause, arguments);\n    }\n\n    public SSessionException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-session/src/main/java/org/bonitasoft/engine/session/SSessionNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.session;\n\n/**\n * @author Elias Ricken de Medeiros\n */\npublic class SSessionNotFoundException extends SSessionException {\n\n    private static final long serialVersionUID = -5995789561034211161L;\n\n    public SSessionNotFoundException(final String message) {\n        super(message);\n    }\n\n    public SSessionNotFoundException(final String message, final Throwable cause) {\n        super(message, cause);\n    }\n\n    public SSessionNotFoundException(final Object... arguments) {\n        super(arguments);\n    }\n\n    public SSessionNotFoundException(final Throwable cause, final Object... arguments) {\n        super(cause, arguments);\n    }\n\n    public SSessionNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-session/src/main/java/org/bonitasoft/engine/session/SessionProvider.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.session;\n\nimport org.bonitasoft.engine.session.model.SSession;\n\npublic interface SessionProvider {\n\n    void updateSession(SSession session) throws SSessionNotFoundException;\n\n    void cleanInvalidSessions();\n\n    void removeSessions();\n\n    SSession getSession(final long sessionId) throws SSessionNotFoundException;\n\n    void removeSession(final long sessionId) throws SSessionNotFoundException;\n\n    void addSession(final SSession session) throws SSessionAlreadyExistsException;\n\n    void deleteSessions(boolean keepTechnicalSessions);\n\n}\n"
  },
  {
    "path": "services/bonita-session/src/main/java/org/bonitasoft/engine/session/SessionService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.session;\n\nimport java.util.List;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.session.model.SSession;\nimport org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Feng Hui\n * @author Matthieu Chaffotte\n */\npublic interface SessionService {\n\n    /**\n     * name of the system user in the session\n     */\n    String SYSTEM = \"system\";\n    /**\n     * ID of the system\n     * when something is done by the system, if a user id is required, this id is given\n     */\n    long SYSTEM_ID = -1L;\n\n    /**\n     * Create a new session for the given user;\n     *\n     * @param userName userName\n     * @return a new session\n     * @throws SSessionException\n     *         if some error arrives while creating the session\n     * @since 6.0\n     */\n    SSession createSession(String userName) throws SSessionException;\n\n    SSession createSession(long userId, String userName, boolean technicalUser) throws SSessionException;\n\n    SSession createSession(long userId, String userName, boolean technicalUser, List<String> profiles,\n            Set<String> permissions)\n            throws SSessionException;\n\n    /**\n     * Delete a session having the given id\n     *\n     * @param sessionId\n     *        the session's id\n     * @throws SSessionNotFoundException\n     *         if no session exists for the given id\n     * @since 6.0\n     */\n    void deleteSession(final long sessionId) throws SSessionNotFoundException;\n\n    /**\n     * Delete all invalid sessions\n     *\n     * @since 6.0\n     */\n    void cleanInvalidSessions();\n\n    /**\n     * Verify if a session is valid\n     *\n     * @param sessionId\n     *        the session's id\n     * @return true if the session is valid, false otherwise\n     * @throws SSessionNotFoundException\n     *         if no session exists for the given id\n     * @since 6.0\n     */\n    boolean isValid(long sessionId) throws SSessionNotFoundException;\n\n    /**\n     * Retrieve a session by its id\n     *\n     * @param sessionId\n     *        the session's id\n     * @return the session associated to the given id\n     * @throws SSessionNotFoundException\n     *         if no session exists for the given id\n     * @since 6.0\n     */\n    SSession getSession(long sessionId) throws SSessionNotFoundException;\n\n    /**\n     * @param sessionAccessor\n     *        the sessionAccessor that contains the current session\n     * @return the logged user or -1 if there is no session\n     * @since 6.4\n     */\n    long getLoggedUserFromSession(ReadSessionAccessor sessionAccessor);\n\n    /**\n     * Define how long, in milliseconds, the created sessions will be valid. This does not affect already created\n     * session\n     *\n     * @param duration\n     * @since 6.0\n     */\n    void setSessionDuration(long duration);\n\n    /**\n     * Retrieve the default sessions's duration, in milliseconds.\n     *\n     * @return the default sessions's duration\n     * @since 6.0\n     */\n    long getDefaultSessionDuration();\n\n    /**\n     * Retrieve the duration, in milliseconds, of new created session. If no duration was specified, the default\n     * duration will be used\n     *\n     * @return the duration of new created session.\n     * @since 6.0\n     */\n    long getSessionDuration();\n\n    /**\n     * Update the expiration and the last update dates of the session.\n     *\n     * @param sessionId\n     *        the session id\n     * @throws SSessionException\n     *         if some error arrives while creating the session\n     * @since 6.0\n     */\n    void renewSession(long sessionId) throws SSessionException;\n\n    /**\n     * Deletes all the sessions.\n     */\n    void deleteSessions();\n\n    /**\n     * Delete all sessions\n     */\n    void deleteAllSessions();\n\n    /**\n     * Delete all sessions of a tenant except the one of the technical user\n     */\n    void deleteSessionsExceptTechnicalUser();\n\n}\n"
  },
  {
    "path": "services/bonita-session/src/main/java/org/bonitasoft/engine/session/impl/AbstractSessionProvider.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.session.impl;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.session.SSessionAlreadyExistsException;\nimport org.bonitasoft.engine.session.SSessionNotFoundException;\nimport org.bonitasoft.engine.session.SessionProvider;\nimport org.bonitasoft.engine.session.model.SSession;\n\n/**\n * @author Baptiste Mesta\n */\npublic abstract class AbstractSessionProvider implements SessionProvider {\n\n    protected abstract Map<Long, SSession> getSessions();\n\n    protected SSession putSession(final SSession session, final long id) {\n        return getSessions().put(id, session);\n    }\n\n    @Override\n    public synchronized void addSession(final SSession session) throws SSessionAlreadyExistsException {\n        final long id = session.getId();\n        if (getSessions().containsKey(id)) {\n            throw new SSessionAlreadyExistsException(\"A session wih id \\\"\" + id + \"\\\" already exists\");\n        }\n        putSession(session, id);\n    }\n\n    @Override\n    public synchronized void removeSession(final long sessionId) throws SSessionNotFoundException {\n        final SSession session = getSessions().remove(sessionId);\n        if (session == null) {\n            throw new SSessionNotFoundException(\"No session found with id \\\"\" + sessionId + \"\\\"\");\n        }\n    }\n\n    @Override\n    public synchronized SSession getSession(final long sessionId) throws SSessionNotFoundException {\n        final SSession session = getSessions().get(sessionId);\n        if (session == null) {\n            throw new SSessionNotFoundException(\"No session found with id \\\"\" + sessionId + \"\\\"\");\n        }\n        return session;\n    }\n\n    @Override\n    public synchronized void updateSession(final SSession session) throws SSessionNotFoundException {\n        final long id = session.getId();\n        if (!getSessions().containsKey(id)) {\n            throw new SSessionNotFoundException(\"No session found with id \\\"\" + id + \"\\\"\");\n        }\n        putSession(session, id);\n    }\n\n    @Override\n    public synchronized void cleanInvalidSessions() {\n        final List<Long> invalidSessionIds = new ArrayList<Long>();\n        for (final SSession session : getSessions().values()) {\n            if (!session.isValid()) {\n                invalidSessionIds.add(session.getId());\n            }\n        }\n        for (final Long invalidSessionId : invalidSessionIds) {\n            getSessions().remove(invalidSessionId);\n        }\n    }\n\n    @Override\n    public synchronized void removeSessions() {\n        getSessions().clear();\n    }\n\n    @Override\n    public synchronized void deleteSessions(final boolean keepTechnicalSessions) {\n        getSessions().values().removeIf(sSession -> !keepTechnicalSessions || !sSession.isTechnicalUser());\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-session/src/main/java/org/bonitasoft/engine/session/impl/SessionIdGenerator.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.session.impl;\n\nimport static java.lang.Math.abs;\n\nimport java.security.SecureRandom;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n * @author Baptiste Mesta\n */\npublic class SessionIdGenerator {\n\n    private static volatile SecureRandom numberGenerator = null;\n\n    public static long getNextId() {\n        SecureRandom ng = numberGenerator;\n        if (ng == null) {\n            numberGenerator = ng = new SecureRandom();\n        }\n        return abs(ng.nextLong());\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-session/src/main/java/org/bonitasoft/engine/session/impl/SessionProviderImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.session.impl;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.bonitasoft.engine.session.SessionProvider;\nimport org.bonitasoft.engine.session.model.SSession;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;\nimport org.springframework.stereotype.Component;\n\n@Component\n@ConditionalOnSingleCandidate(SessionProvider.class)\npublic final class SessionProviderImpl extends AbstractSessionProvider {\n\n    private final Map<Long, SSession> sessions = new HashMap<>();\n\n    @Override\n    protected Map<Long, SSession> getSessions() {\n        return sessions;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-session/src/main/java/org/bonitasoft/engine/session/impl/SessionServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.session.impl;\n\nimport static java.util.Collections.emptyList;\nimport static java.util.Collections.emptySet;\n\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Set;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.session.SSessionException;\nimport org.bonitasoft.engine.session.SSessionNotFoundException;\nimport org.bonitasoft.engine.session.SessionProvider;\nimport org.bonitasoft.engine.session.SessionService;\nimport org.bonitasoft.engine.session.model.SSession;\nimport org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor;\nimport org.bonitasoft.engine.sessionaccessor.SessionIdNotSetException;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Matthieu Chaffotte\n */\n@Slf4j\npublic class SessionServiceImpl implements SessionService {\n\n    private static final long DEFAULT_SESSION_DURATION = 3600000;\n\n    private long sessionDuration = DEFAULT_SESSION_DURATION;\n\n    private final SessionProvider sessionProvider;\n\n    private final String applicationName;\n\n    public SessionServiceImpl(final SessionProvider sessionProvider, final String applicationName) {\n        this.sessionProvider = sessionProvider;\n        this.applicationName = applicationName;\n    }\n\n    @Override\n    public SSession createSession(final String userName) throws SSessionException {\n        return this.createSession(-1, userName, false);\n    }\n\n    @Override\n    public SSession createSession(final long userId, final String userName,\n            final boolean isTechnicalUser) throws SSessionException {\n        return createSession(userId, userName, isTechnicalUser, emptyList(), emptySet());\n    }\n\n    @Override\n    public SSession createSession(final long userId, final String userName,\n            final boolean isTechnicalUser, List<String> profiles, Set<String> permissions) throws SSessionException {\n        final long id = SessionIdGenerator.getNextId();\n        Date now = new Date();\n        SSession session = SSession.builder()\n                .id(id)\n                .duration(sessionDuration)\n                .userName(userName)\n                .applicationName(applicationName)\n                .userId(userId)\n                .technicalUser(isTechnicalUser)\n                .creationDate(now)\n                .lastRenewDate(now)\n                .profiles(profiles)\n                .userPermissions(permissions)\n                .build();\n        sessionProvider.addSession(session);\n        if (log.isTraceEnabled()) {\n            log.trace(\"CreateSession with username = <{}>, id = <{}>\", userName, id);\n        }\n        return session;\n    }\n\n    @Override\n    public void deleteSession(final long sessionId) throws SSessionNotFoundException {\n        sessionProvider.removeSession(sessionId);\n    }\n\n    @Override\n    public boolean isValid(final long sessionId) throws SSessionNotFoundException {\n        return sessionProvider.getSession(sessionId).getExpirationDate().getTime() > new Date().getTime();\n    }\n\n    @Override\n    public SSession getSession(final long sessionId) throws SSessionNotFoundException {\n        return sessionProvider.getSession(sessionId).toBuilder().build();\n    }\n\n    @Override\n    public long getLoggedUserFromSession(ReadSessionAccessor sessionAccessor) {\n        try {\n            long sessionId = sessionAccessor.getSessionId();\n            return sessionProvider.getSession(sessionId).getUserId();\n        } catch (SessionIdNotSetException | SSessionNotFoundException e) {\n            return -1;\n        }\n    }\n\n    @Override\n    public void setSessionDuration(final long duration) {\n        if (duration <= 0) {\n            throw new IllegalArgumentException(\"The duration must be greater than 0\");\n        }\n        sessionDuration = duration;\n        log.debug(\"Session duration set to {}\", sessionDuration);\n    }\n\n    @Override\n    public long getDefaultSessionDuration() {\n        return DEFAULT_SESSION_DURATION;\n    }\n\n    @Override\n    public long getSessionDuration() {\n        return sessionDuration;\n    }\n\n    @Override\n    public void renewSession(final long sessionId) throws SSessionException {\n        final SSession session = getSession(sessionId);\n        SSession updatedSession = session.toBuilder().lastRenewDate(new Date()).build();\n        sessionProvider.updateSession(updatedSession);\n    }\n\n    @Override\n    public void cleanInvalidSessions() {\n        sessionProvider.cleanInvalidSessions();\n    }\n\n    @Override\n    public void deleteAllSessions() {\n        sessionProvider.deleteSessions(false/* don't keep technical user */);\n    }\n\n    @Override\n    public void deleteSessionsExceptTechnicalUser() {\n        sessionProvider.deleteSessions(true/* keep technical user */);\n    }\n\n    @Override\n    public void deleteSessions() {\n        sessionProvider.removeSessions();\n        if (log.isTraceEnabled()) {\n            log.trace(\"Sessions were deleted.\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-session/src/main/java/org/bonitasoft/engine/session/model/SSession.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.session.model;\n\nimport java.io.Serializable;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Set;\n\nimport lombok.AccessLevel;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.Setter;\n\n/**\n * @author Elias Ricken de Medeiros\n * @author Yanyan Liu\n * @author Matthieu Chaffotte\n */\n@Data\n@Setter(AccessLevel.PRIVATE) // to ensure fields are immutable\n@Builder(toBuilder = true)\npublic class SSession implements Serializable {\n\n    private final long id;\n    /**\n     * creation date (GMT+0)\n     */\n    private Date creationDate;\n    private long duration;\n\n    /**\n     * last renew date (GMT+0)\n     */\n    @Setter(AccessLevel.PUBLIC) // for cluster testing\n    private Date lastRenewDate;\n    private String userName;\n    private long userId;\n    //FIXME: remove it, it looks like it is never set\n    private String clusterNode;\n    private String applicationName;\n    /**\n     * true if the user is the technical user: false otherwise\n     */\n    private boolean technicalUser;\n    private List<String> profiles;\n    @Builder.Default\n    private Set<String> userPermissions = Collections.emptySet();\n\n    /**\n     * @return the expiration date (GMT+0)\n     */\n    public Date getExpirationDate() {\n        return new Date(lastRenewDate.getTime() + duration);\n    }\n\n    /**\n     * @return true if the session is still valid\n     */\n    public boolean isValid() {\n        return getExpirationDate().getTime() > System.currentTimeMillis();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-session/src/main/java/org/bonitasoft/engine/sessionaccessor/ReadSessionAccessor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.sessionaccessor;\n\n/**\n * @author Yanyan Liu\n */\npublic interface ReadSessionAccessor {\n\n    /**\n     * Get current session id\n     *\n     * @return the identifier of current session\n     * @throws SessionIdNotSetException\n     *         if no session exists for the given id, throw exception\n     * @since 6.0\n     */\n    long getSessionId() throws SessionIdNotSetException;\n\n}\n"
  },
  {
    "path": "services/bonita-session/src/main/java/org/bonitasoft/engine/sessionaccessor/SessionAccessor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.sessionaccessor;\n\n/**\n * @author Yanyan Liu\n */\npublic interface SessionAccessor extends ReadSessionAccessor {\n\n    void setSessionId(long sessionId);\n\n    void deleteSessionId();\n\n}\n"
  },
  {
    "path": "services/bonita-session/src/main/java/org/bonitasoft/engine/sessionaccessor/SessionIdNotSetException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.sessionaccessor;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Yanyan Liu\n */\npublic class SessionIdNotSetException extends SBonitaException {\n\n    private static final long serialVersionUID = -5884549926492597499L;\n\n    public SessionIdNotSetException(final String message) {\n        super(message);\n    }\n\n    public SessionIdNotSetException(final Throwable e) {\n        super(e);\n    }\n\n    public SessionIdNotSetException(final String message, final Throwable e) {\n        super(message, e);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-session/src/main/java/org/bonitasoft/engine/sessionaccessor/ThreadLocalSessionAccessor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.sessionaccessor;\n\nimport org.springframework.stereotype.Component;\n\n/**\n * Store and access the current session id of the current thread\n */\n@Component\npublic class ThreadLocalSessionAccessor implements SessionAccessor {\n\n    private final ThreadLocal<Long> sessionData = new ThreadLocal<>();\n\n    @Override\n    public long getSessionId() throws SessionIdNotSetException {\n        Long sessionId = sessionData.get();\n        if (sessionId == null) {\n            throw new SessionIdNotSetException(\"No session set.\");\n        }\n        return sessionId;\n    }\n\n    @Override\n    public void setSessionId(long sessionId) {\n        if (sessionId <= 0) {\n            throw new IllegalArgumentException(\"Session id is invalid: \" + sessionId);\n        }\n        sessionData.set(sessionId);\n    }\n\n    @Override\n    public void deleteSessionId() {\n        sessionData.remove();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-session/src/test/java/org/bonitasoft/engine/session/impl/SessionProviderImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.session.impl;\n\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\nimport static org.junit.Assert.assertNotNull;\n\nimport org.bonitasoft.engine.session.SSessionNotFoundException;\nimport org.bonitasoft.engine.session.SessionProvider;\nimport org.bonitasoft.engine.session.model.SSession;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class SessionProviderImplTest {\n\n    private final SessionProvider sessionProvider = new SessionProviderImpl();\n\n    @Before\n    public void cleanSession() {\n        //session provider have a static map of sessions\n        sessionProvider.removeSessions();\n    }\n\n    @Test\n    public void testAddSession() throws Exception {\n        sessionProvider.addSession(SSession.builder().id(12L).userName(\"john\").userId(12).build());\n        assertNotNull(sessionProvider.getSession(12));\n    }\n\n    @Test\n    public void testRemoveSession() throws Exception {\n        sessionProvider.addSession(SSession.builder().id(12L).userName(\"john\").userId(12).build());\n        sessionProvider.removeSession(12L);\n        assertThatThrownBy(() -> sessionProvider.getSession(12L)).isInstanceOf(SSessionNotFoundException.class);\n    }\n\n    @Test\n    public void get_non_existing_session_should_throw_exception() {\n        assertThatThrownBy(() -> sessionProvider.getSession(10L)).isInstanceOf(SSessionNotFoundException.class);\n    }\n\n    @Test\n    public void should_deleteSessions_keep_technical_sessions() throws Exception {\n        sessionProvider.removeSessions();\n        sessionProvider.addSession(SSession.builder().id(54L).userName(\"john\").userId(12).build());\n        sessionProvider.addSession(SSession.builder().id(55L).userName(\"john\").userId(12).technicalUser(true).build());\n        sessionProvider.deleteSessions(true /* keep technical sessions */);\n        sessionProvider.getSession(55);\n        assertThatThrownBy(() -> sessionProvider.getSession(54L)).isInstanceOf(SSessionNotFoundException.class);\n    }\n\n    @Test\n    public void should_delete_all_sessions() throws Exception {\n        sessionProvider.removeSessions();\n        sessionProvider.addSession(SSession.builder().id(55L).userName(\"tech\").userId(13).technicalUser(true).build());\n        sessionProvider.addSession(SSession.builder().id(56L).userName(\"john\").userId(14).build());\n        sessionProvider.deleteSessions(false /* keep technical sessions */);\n\n        assertThatThrownBy(() -> sessionProvider.getSession(55L)).isInstanceOf(SSessionNotFoundException.class);\n        assertThatThrownBy(() -> sessionProvider.getSession(56L)).isInstanceOf(SSessionNotFoundException.class);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-session/src/test/java/org/bonitasoft/engine/session/impl/SessionServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.session.impl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.Mockito.*;\n\nimport org.bonitasoft.engine.session.SSessionNotFoundException;\nimport org.bonitasoft.engine.session.SessionProvider;\nimport org.bonitasoft.engine.session.model.SSession;\nimport org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor;\nimport org.bonitasoft.engine.sessionaccessor.SessionIdNotSetException;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Celine Souchet\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class SessionServiceImplTest {\n\n    private static final long SESSION_ID = 1258L;\n    private static final String USER_NAME = \"john\";\n    private static final long USER_ID = 58L;\n\n    @Mock\n    private SessionProvider sessionProvider;\n\n    @Mock\n    private ReadSessionAccessor sessionAccessor;\n\n    @InjectMocks\n    private SessionServiceImpl sessionServiceImpl;\n    private SSession sSession;\n\n    @Before\n    public void setUp() {\n        sSession = SSession.builder().id(SESSION_ID).userName(USER_NAME).userId(USER_ID).build();\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.session.impl.SessionServiceImpl#getDefaultSessionDuration()}.\n     */\n    @Test\n    public final void getDefaultSessionDuration() {\n        assertEquals(3600000, sessionServiceImpl.getDefaultSessionDuration());\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.session.impl.SessionServiceImpl#getSessionDuration()}.\n     */\n    @Test\n    public final void getSessionDuration() {\n        assertEquals(3600000, sessionServiceImpl.getSessionDuration());\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.session.impl.SessionServiceImpl#cleanInvalidSessions()}.\n     */\n    @Test\n    public final void cleanInvalidSessions() {\n        sessionServiceImpl.cleanInvalidSessions();\n        verify(sessionProvider, times(1)).cleanInvalidSessions();\n    }\n\n    /**\n     * Test method for {@link org.bonitasoft.engine.session.impl.SessionServiceImpl#deleteSessions()}.\n     */\n    @Test\n    public final void deleteSessions() {\n        sessionServiceImpl.deleteSessions();\n        verify(sessionProvider, times(1)).removeSessions();\n    }\n\n    @Test\n    public final void deleteAllSessions() {\n        sessionServiceImpl.deleteAllSessions();\n        verify(sessionProvider, times(1)).deleteSessions(false);\n    }\n\n    @Test\n    public final void deleteSessionsExceptTechnicalUser() {\n        sessionServiceImpl.deleteSessionsExceptTechnicalUser();\n        verify(sessionProvider, times(1)).deleteSessions(true);\n    }\n\n    @Test\n    public final void should_getLoggedUserFromSession_return_user_id_when_there_is_a_session() throws Exception {\n        //given\n        doReturn(SESSION_ID).when(sessionAccessor).getSessionId();\n        doReturn(sSession).when(sessionProvider).getSession(SESSION_ID);\n        //when\n        final long loggedUserFromSession = sessionServiceImpl.getLoggedUserFromSession(sessionAccessor);\n        //when\n        assertThat(loggedUserFromSession).isEqualTo(USER_ID);\n    }\n\n    @Test\n    public final void should_getLoggedUserFromSession_return_minus_1_when_there_is_no_session()\n            throws SessionIdNotSetException {\n        //given\n        doThrow(SessionIdNotSetException.class).when(sessionAccessor).getSessionId();\n        //when\n        final long loggedUserFromSession = sessionServiceImpl.getLoggedUserFromSession(sessionAccessor);\n        //when\n        assertThat(loggedUserFromSession).isEqualTo(-1);\n\n    }\n\n    @Test\n    public final void should_getLoggedUserFromSession_return_minus_1_when_the_session_is_not_found()\n            throws SessionIdNotSetException, SSessionNotFoundException {\n        //given\n        doReturn(SESSION_ID).when(sessionAccessor).getSessionId();\n        doThrow(SSessionNotFoundException.class).when(sessionProvider).getSession(SESSION_ID);\n        //when\n        final long loggedUserFromSession = sessionServiceImpl.getLoggedUserFromSession(sessionAccessor);\n        //when\n        assertThat(loggedUserFromSession).isEqualTo(-1);\n\n    }\n}\n"
  },
  {
    "path": "services/bonita-session/src/test/java/org/bonitasoft/engine/sessionaccessor/ThreadLocalSessionAccessorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.sessionaccessor;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.Test;\n\npublic class ThreadLocalSessionAccessorTest {\n\n    private final ThreadLocalSessionAccessor threadLocalSessionAccessor = new ThreadLocalSessionAccessor();\n\n    @Test\n    public void should_set_session_id() throws Exception {\n        threadLocalSessionAccessor.setSessionId(12);\n\n        assertThat(threadLocalSessionAccessor.getSessionId()).isEqualTo(12);\n    }\n\n    @Test(expected = SessionIdNotSetException.class)\n    public void should_throw_SessionIfNotSet_when_session_is_not_set() throws Exception {\n        threadLocalSessionAccessor.getSessionId();\n    }\n\n    @Test(expected = SessionIdNotSetException.class)\n    public void should_throw_SessionIfNotSet_when_only_tenant_is_set() throws Exception {\n        threadLocalSessionAccessor.getSessionId();\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void should_throw_IllegalArgumentException_when_session_id_negative() {\n        threadLocalSessionAccessor.setSessionId(-1);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void should_throw_IllegalArgumentException_when_session_id_0() {\n        threadLocalSessionAccessor.setSessionId(0);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-temporary-content/build.gradle",
    "content": "\n\ndependencies {\n    api project(':services:bonita-persistence')\n    api project(':services:bonita-commons')\n    testImplementation libs.mockitoCore\n    testImplementation libs.systemRules\n    testImplementation libs.assertj\n    testRuntimeOnly libs.logback\n    annotationProcessor libs.lombok\n    compileOnly libs.lombok\n\n}\n"
  },
  {
    "path": "services/bonita-temporary-content/src/main/java/org/bonitasoft/engine/temporary/content/STemporaryContent.java",
    "content": "/**\n * Copyright (C) 2023 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.temporary.content;\n\nimport java.sql.Blob;\nimport java.util.UUID;\n\nimport javax.persistence.*;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.apache.commons.io.FilenameUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.bonitasoft.engine.persistence.PersistentObject;\n\n/**\n * @author Haroun EL ALAMI\n */\n@Data\n@NoArgsConstructor\n@Entity\n@Inheritance(strategy = InheritanceType.SINGLE_TABLE)\n@Table(name = \"temporary_content\")\n@Cacheable(false)\npublic class STemporaryContent implements PersistentObject {\n\n    @Id\n    private long id;\n\n    /**\n     * Date when the temporary content was added\n     */\n    @Column\n    private long creationDate;\n\n    /**\n     * Unique identifier sent to the client after upload is complete to retrieve the file\n     */\n    @Column(name = \"key_\")\n    private String key;\n\n    /**\n     * Original uploaded file name\n     */\n    @Column\n    private String fileName;\n\n    /**\n     * The mimeType of the file\n     */\n    @Column\n    private String mimeType;\n\n    /**\n     * Content of the file uploaded (accessed with Streams)\n     */\n    @Lob\n    private Blob content;\n\n    public STemporaryContent(final String fileName, Blob content, String mimeType) {\n        super();\n        String fileExtension = FilenameUtils.getExtension(fileName);\n        String keyUUID = UUID.randomUUID().toString();\n        this.key = StringUtils.isNotBlank(fileExtension) ? keyUUID + \".\" + fileExtension : keyUUID;\n        this.fileName = fileName;\n        this.content = content;\n        this.creationDate = System.currentTimeMillis();\n        this.mimeType = mimeType;\n    }\n}\n"
  },
  {
    "path": "services/bonita-temporary-content/src/main/resources/org/bonitasoft/engine/temporary/content/hibernate/temporary.content.queries.hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\" \"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd\">\n<hibernate-mapping auto-import=\"false\">\n\n    <query name=\"getTemporaryResource\">\n        SELECT u\n        FROM org.bonitasoft.engine.temporary.content.STemporaryContent AS u\n        WHERE u.key = :key\n    </query>\n\n    <query name=\"cleanOutDatedTemporaryResources\">\n            DELETE\n            FROM org.bonitasoft.engine.temporary.content.STemporaryContent AS u\n            WHERE u.creationDate &lt; :creationDate\n    </query>\n\n</hibernate-mapping>\n"
  },
  {
    "path": "services/bonita-temporary-content/src/test/resources/logback-test.xml",
    "content": "<configuration>\n\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->\n        <encoder>\n            <pattern>|%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <root level=\"INFO\">\n        <appender-ref ref=\"STDOUT\" />\n    </root>\n\n</configuration>\n"
  },
  {
    "path": "services/bonita-time-tracker/build.gradle",
    "content": "\n\ndependencies {\n    api project(':services:bonita-commons')\n    api libs.commonsCollections\n    testImplementation libs.mockitoCore\n    testImplementation libs.assertj\n    testImplementation libs.logback\n}\n\ndescription = 'Bonita Time Tracker'\n"
  },
  {
    "path": "services/bonita-time-tracker/src/main/java/org/bonitasoft/engine/tracking/AbstractFlushEventListener.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tracking;\n\n/**\n * @author Charles Souillard\n */\npublic abstract class AbstractFlushEventListener implements FlushEventListener {\n\n    private boolean active = false;\n\n    protected AbstractFlushEventListener(final boolean activateByDefault) {\n        this.active = activateByDefault;\n    }\n\n    @Override\n    public String getStatus() {\n        return getName() + \": \\n\" + \"active: \" + isActive();\n    }\n\n    @Override\n    public String getName() {\n        return this.getClass().getName();\n    }\n\n    @Override\n    public boolean isActive() {\n        return this.active;\n    }\n\n    @Override\n    public void activate() {\n        this.active = true;\n        notifyStartTracking();\n    }\n\n    @Override\n    public void deactivate() {\n        this.active = false;\n        notifyStopTracking();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-time-tracker/src/main/java/org/bonitasoft/engine/tracking/Clock.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tracking;\n\npublic interface Clock {\n\n    boolean sleep(final long millis) throws InterruptedException;\n\n}\n"
  },
  {
    "path": "services/bonita-time-tracker/src/main/java/org/bonitasoft/engine/tracking/FlushEvent.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tracking;\n\nimport java.util.Collections;\nimport java.util.List;\n\npublic final class FlushEvent {\n\n    private final List<Record> records;\n\n    private final long flushTime;\n\n    public FlushEvent(final long flushTime, final List<Record> records) {\n        if (records != null) {\n            this.records = records;\n        } else {\n            this.records = Collections.emptyList();\n        }\n        this.flushTime = flushTime;\n    }\n\n    public List<Record> getRecords() {\n        return this.records;\n    }\n\n    public long getFlushTime() {\n        return this.flushTime;\n    }\n\n    @Override\n    public String toString() {\n        return \"FlushEvent{\" +\n                \"records.size=\" + this.records.size() +\n                \", flushTime=\" + this.flushTime +\n                '}';\n    }\n}\n"
  },
  {
    "path": "services/bonita-time-tracker/src/main/java/org/bonitasoft/engine/tracking/FlushEventListener.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tracking;\n\npublic interface FlushEventListener {\n\n    FlushEventListenerResult flush(final FlushEvent flushEvent) throws Exception;\n\n    String getStatus();\n\n    String getName();\n\n    boolean isActive();\n\n    void notifyStopTracking();\n\n    void activate();\n\n    void deactivate();\n\n    void notifyStartTracking();\n}\n"
  },
  {
    "path": "services/bonita-time-tracker/src/main/java/org/bonitasoft/engine/tracking/FlushEventListenerResult.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tracking;\n\npublic class FlushEventListenerResult {\n\n    private final FlushEvent flushEvent;\n\n    public FlushEventListenerResult(final FlushEvent flushEvent) {\n        super();\n        this.flushEvent = flushEvent;\n    }\n\n    public FlushEvent getFlushEvent() {\n        return this.flushEvent;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-time-tracker/src/main/java/org/bonitasoft/engine/tracking/FlushResult.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tracking;\n\nimport java.util.List;\n\n/**\n * @author Charles Souillard\n */\npublic class FlushResult {\n\n    private final long flushTime;\n\n    private final List<FlushEventListenerResult> flushEventListenerResults;\n\n    public FlushResult(final long flushTime, final List<FlushEventListenerResult> flushEventListenerResults) {\n        this.flushTime = flushTime;\n        this.flushEventListenerResults = flushEventListenerResults;\n    }\n\n    public List<FlushEventListenerResult> getFlushEventListenerResults() {\n        return this.flushEventListenerResults;\n    }\n\n    public long getFlushTime() {\n        return this.flushTime;\n    }\n}\n"
  },
  {
    "path": "services/bonita-time-tracker/src/main/java/org/bonitasoft/engine/tracking/FlushThread.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tracking;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class FlushThread extends Thread {\n\n    private static final Logger log = LoggerFactory.getLogger(FlushThread.class);\n    private final TimeTracker timeTracker;\n\n    public FlushThread(final TimeTracker timeTracker) {\n        super(\"Bonita-TimeTracker-FlushThread\");\n        this.timeTracker = timeTracker;\n    }\n\n    @Override\n    public void run() {\n        log.info(\"Starting \" + getName() + \"...\");\n        long lastFlushTimestamp = System.currentTimeMillis();\n        while (true) {\n            final long now = System.currentTimeMillis();\n            try {\n                final long sleepTime = getSleepTime(now, lastFlushTimestamp);\n                log.debug(\"FlushThread: sleeping for: \" + sleepTime + \"ms\");\n                this.timeTracker.getClock().sleep(sleepTime);\n            } catch (final InterruptedException e) {\n                // Make sure to propagate the interruption to cleanly stop the current thread.\n                Thread.currentThread().interrupt();\n                break;\n            }\n            lastFlushTimestamp = flush(now);\n        }\n        log.info(getName() + \" stopped.\");\n    }\n\n    long getSleepTime(final long now, final long lastFlushTimestamp) throws InterruptedException {\n        final long flushDuration = now - lastFlushTimestamp;\n        return this.timeTracker.getFlushIntervalInMS() - flushDuration;\n    }\n\n    long flush(final long now) {\n        try {\n            final FlushResult flushResult = this.timeTracker.flush();\n            return flushResult.getFlushTime();\n        } catch (final Exception e) {\n            log.warn(\"Exception caught while flushing: \" + e.getMessage(), e);\n        }\n        return now;\n    }\n\n    public boolean isStarted() {\n        return isAlive();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-time-tracker/src/main/java/org/bonitasoft/engine/tracking/Record.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tracking;\n\npublic class Record {\n\n    private final long timestamp;\n\n    private final TimeTrackerRecords name;\n\n    private final String description;\n\n    private final long duration;\n\n    public Record(final long timestamp, final TimeTrackerRecords name, final String description, final long duration) {\n        super();\n        this.timestamp = timestamp;\n        this.name = name;\n        this.description = description;\n        this.duration = duration;\n    }\n\n    public long getTimestamp() {\n        return this.timestamp;\n    }\n\n    public TimeTrackerRecords getName() {\n        return this.name;\n    }\n\n    public String getDescription() {\n        return this.description;\n    }\n\n    public long getDuration() {\n        return this.duration;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-time-tracker/src/main/java/org/bonitasoft/engine/tracking/ThreadSleepClockImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tracking;\n\npublic class ThreadSleepClockImpl implements Clock {\n\n    @Override\n    public boolean sleep(final long millis) throws InterruptedException {\n        Thread.sleep(millis);\n        return true;\n    }\n}\n"
  },
  {
    "path": "services/bonita-time-tracker/src/main/java/org/bonitasoft/engine/tracking/TimeTracker.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tracking;\n\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport org.apache.commons.collections4.queue.CircularFifoQueue;\nimport org.bonitasoft.engine.commons.TenantLifecycleService;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class TimeTracker implements TenantLifecycleService {\n\n    private static final Logger log = LoggerFactory.getLogger(TimeTracker.class);\n    private final Set<TimeTrackerRecords> activatedRecords;\n    private FlushThread flushThread;\n    private final Map<String, FlushEventListener> flushEventListeners;\n    private final Queue<Record> records;\n    private final Clock clock;\n\n    private long flushIntervalInMS;\n    private boolean startTracking = false;\n\n    private boolean serviceStarted;\n    private long lastFlushTimestamp = 0L;\n\n    public TimeTracker(\n            final boolean startTracking,\n            final List<FlushEventListener> flushEventListeners,\n            final int maxSize,\n            final int flushIntervalInSeconds,\n            final String... activatedRecords) {\n        this(new ThreadSleepClockImpl(), startTracking, flushEventListeners, maxSize,\n                flushIntervalInSeconds * 1000, activatedRecords);\n    }\n\n    public TimeTracker(\n            final Clock clock,\n            final boolean startTracking,\n            final List<FlushEventListener> flushEventListeners,\n            final int maxSize,\n            final int flushIntervalInMS,\n            final String... activatedRecords) {\n        super();\n        this.startTracking = startTracking;\n        this.clock = clock;\n        this.flushIntervalInMS = flushIntervalInMS;\n        this.records = new CircularFifoQueue<>(maxSize);\n        this.serviceStarted = false;\n        this.flushEventListeners = new ConcurrentHashMap<>();\n        if (flushEventListeners != null) {\n            for (final FlushEventListener flushEventListener : flushEventListeners) {\n                final String name = flushEventListener.getName();\n                if (this.flushEventListeners.containsKey(name)) {\n                    log.error(\"Duplicate entry for flushEventListener with name: \" + name);\n                }\n                this.flushEventListeners.put(name, flushEventListener);\n            }\n        }\n\n        if (activatedRecords == null || activatedRecords.length == 0) {\n            this.activatedRecords = Collections.emptySet();\n        } else {\n            this.activatedRecords = new HashSet<>();\n            for (final String activatedRecord : activatedRecords) {\n                this.activatedRecords.add(TimeTrackerRecords.valueOf(activatedRecord));\n            }\n        }\n        log.info(getStatus());\n    }\n\n    /**\n     * get the list of Active Listener\n     */\n    public List<FlushEventListener> getActiveFlushEventListeners() {\n        final List<FlushEventListener> active = new ArrayList<>();\n        for (final FlushEventListener flushEventListener : this.flushEventListeners.values()) {\n            if (flushEventListener.isActive()) {\n                active.add(flushEventListener);\n            }\n        }\n        return active;\n    }\n\n    /**\n     * return all Event Listeners, active or not\n     */\n    public List<FlushEventListener> getFlushEventListeners() {\n        return new ArrayList(this.flushEventListeners.values());\n    }\n\n    /**\n     * reference a new flushEventListener. The key of the reference is the flushEventListener.name().\n     * If a listener exist with this name, it will be replaced.\n     */\n    public void addFlushEventListener(final FlushEventListener flushEventListener) {\n        this.flushEventListeners.put(flushEventListener.getName(), flushEventListener);\n    }\n\n    /**\n     * remove a flush event listener\n     */\n    public void removeFlushEventListener(final String flushEventListenerName) {\n        this.flushEventListeners.remove(flushEventListenerName);\n    }\n\n    public boolean activateFlushEventListener(final String flushEventListenerName) {\n        final FlushEventListener flushEventListener = this.flushEventListeners.get(flushEventListenerName);\n        if (flushEventListener == null) {\n            return false;\n        }\n        flushEventListener.activate();\n        return true;\n    }\n\n    public boolean deactivateFlushEventListener(final String flushEventListenerName) {\n        final FlushEventListener flushEventListener = this.flushEventListeners.get(flushEventListenerName);\n        if (flushEventListener == null) {\n            return false;\n        }\n        flushEventListener.deactivate();\n        return true;\n    }\n\n    public void activateRecord(final TimeTrackerRecords activatedRecord) {\n        this.activatedRecords.add(activatedRecord);\n    }\n\n    public void deactivatedRecord(final TimeTrackerRecords activatedRecord) {\n        this.activatedRecords.remove(activatedRecord);\n    }\n\n    public Set<TimeTrackerRecords> getActivatedRecords() {\n        return Collections.unmodifiableSet(this.activatedRecords);\n    }\n\n    public void startTracking() {\n        if (!this.serviceStarted) {\n            log.warn(\"Cannot start Time tracker tracking because service is not started.\");\n            return;\n        }\n        this.startTracking = true;\n        internalStartTracking();\n    }\n\n    public void stopTracking() {\n        this.startTracking = false;\n        internalStopTracking();\n    }\n\n    FlushThread createFlushThread() {\n        return new FlushThread(this);\n    }\n\n    private void internalStartTracking() {\n        if (this.startTracking) {\n            log.warn(\"Starting Time tracker tracking...\");\n            this.flushThread = createFlushThread();\n            this.flushThread.start();\n            for (final FlushEventListener listener : getActiveFlushEventListeners()) {\n                listener.notifyStartTracking();\n            }\n            log.warn(\n                    \"Time tracker tracking is activated. This may not be used in production as performances may be strongly impacted.\");\n        }\n    }\n\n    private void internalStopTracking() {\n        if (isTracking()) {\n            log.warn(\"Stopping Time tracker tracking...\");\n            if (this.flushThread.isStarted()) {\n                this.flushThread.interrupt();\n                try {\n                    // Wait for the thread to die\n                    this.flushThread.join();\n                } catch (final InterruptedException e) {\n                    // We want this thread to be interrupted. No need to do extra work here, we are in the desired state.\n                }\n            }\n            for (final FlushEventListener listener : getActiveFlushEventListeners()) {\n                listener.notifyStopTracking();\n            }\n            log.warn(\"Time tracker tracking is deactivated.\");\n        }\n    }\n\n    public boolean isTracking() {\n        return this.flushThread != null && this.flushThread.isStarted();\n    }\n\n    public long getFlushIntervalInMS() {\n        return this.flushIntervalInMS;\n    }\n\n    public void setFlushIntervalInSeconds(final long flushIntervalInSeconds) {\n        this.flushIntervalInMS = flushIntervalInSeconds * 1000;\n    }\n\n    public void setFlushIntervalInMS(final long flushIntervalInMS) {\n        this.flushIntervalInMS = flushIntervalInMS;\n    }\n\n    public Clock getClock() {\n        return this.clock;\n    }\n\n    public String getStatus() {\n        final StringBuilder sb = new StringBuilder();\n        sb.append(\"-----\");\n        sb.append(\"\\n\");\n\n        sb.append(\"Time Tracker '\");\n        sb.append(this.getClass().getName());\n        sb.append(\"':\");\n        sb.append(\"\\n\");\n\n        sb.append(\"  - trackingEnabled: \");\n        sb.append(isTracking());\n        sb.append(\"\\n\");\n\n        sb.append(\"  - flushIntervalInSeconds: \");\n        sb.append(this.flushIntervalInMS);\n        sb.append(\"\\n\");\n\n        sb.append(\"  - activatedRecords: \");\n        for (final TimeTrackerRecords activatedRecord : this.activatedRecords) {\n            sb.append(activatedRecord.name());\n            sb.append(\" \");\n        }\n        sb.append(\"\\n\");\n\n        sb.append(\"  - flushEventListeners: \");\n        for (final FlushEventListener flushEventListener : this.flushEventListeners.values()) {\n            sb.append(flushEventListener.getStatus());\n            sb.append(\" \");\n        }\n        sb.append(\"\\n\");\n\n        sb.append(\"  - records.size: \");\n        sb.append(this.records.size());\n        sb.append(\"\\n\");\n\n        sb.append(\"  - last flush occurrence: \");\n        sb.append(new Date(this.lastFlushTimestamp).toString());\n        sb.append(\"\\n\");\n\n        sb.append(\"\\n\");\n        sb.append(\"-----\");\n        return sb.toString();\n    }\n\n    public boolean isTrackable(final TimeTrackerRecords recordName) {\n        return isTracking() && this.activatedRecords.contains(recordName);\n    }\n\n    public void track(final TimeTrackerRecords recordName, final String recordDescription, final long duration) {\n        if (!isTrackable(recordName)) {\n            return;\n        }\n        final long timestamp = System.currentTimeMillis();\n        final Record record = new Record(timestamp, recordName, recordDescription, duration);\n        log.debug(\"Tracking record: \" + record);\n        synchronized (this) {\n            this.records.add(record);\n        }\n    }\n\n    public FlushResult flush() {\n        log.info(\"Flushing...\");\n        this.lastFlushTimestamp = System.currentTimeMillis();\n        final List<FlushEventListenerResult> flushEventListenerResults = new ArrayList<>();\n        final FlushResult flushResult = new FlushResult(this.lastFlushTimestamp, flushEventListenerResults);\n        final List<Record> records;\n        synchronized (this) {\n            records = getRecordsCopy();\n            clearRecords();\n        }\n        final FlushEvent flushEvent = new FlushEvent(this.lastFlushTimestamp, records);\n\n        flushListeners(flushEvent, flushEventListenerResults);\n        log.info(\"Flush finished: \" + flushEvent);\n        return flushResult;\n    }\n\n    void flushListeners(final FlushEvent flushEvent, final List<FlushEventListenerResult> flushEventListenerResults) {\n        if (this.flushEventListeners == null) {\n            return;\n        }\n        for (final FlushEventListener listener : getActiveFlushEventListeners()) {\n            try {\n                final FlushEventListenerResult flushEventListenerResult = listener.flush(flushEvent);\n                flushEventListenerResults.add(flushEventListenerResult);\n            } catch (final Exception e) {\n                log.warn(\"Exception while flushing: \" + flushEvent + \" on listener \" + listener);\n            }\n        }\n    }\n\n    public List<Record> getRecordsCopy() {\n        return Arrays.asList(this.records.toArray(new Record[this.records.size()]));\n    }\n\n    public void clearRecords() {\n        this.records.clear();\n    }\n\n    @Override\n    public void start() {\n        if (this.serviceStarted) {\n            return;\n        }\n        log.info(\"Starting TimeTracker...\");\n        this.serviceStarted = true;\n        internalStartTracking();\n        log.info(\"TimeTracker started.\");\n    }\n\n    @Override\n    public void stop() {\n        if (!this.serviceStarted) {\n            return;\n        }\n        log.info(\"Stopping TimeTracker...\");\n        this.serviceStarted = false;\n        internalStopTracking();\n        log.info(\"TimeTracker stopped.\");\n    }\n\n    @Override\n    public void pause() {\n        stop();\n    }\n\n    @Override\n    public void resume() {\n        start();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-time-tracker/src/main/java/org/bonitasoft/engine/tracking/TimeTrackerRecords.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tracking;\n\npublic enum TimeTrackerRecords {\n\n    /**\n     * this key is used to track the connector execution (execute method only, not in/out parameters\n     * processing) including the pool submission (that may have\n     * additional impact if the pool is full).\n     */\n    EXECUTE_CONNECTOR_INCLUDING_POOL_SUBMIT,\n\n    /**\n     * this key is used to track the connector execution (execute method only, not in/out parameters\n     * processing), without potential pool submission impact\n     */\n    EXECUTE_CONNECTOR_CALLABLE,\n\n    /**\n     * this key is used to track connector output parameters processing only (not pooling, not input,\n     * not execute)\n     */\n    EXECUTE_CONNECTOR_OUTPUT_OPERATIONS,\n\n    /**\n     * this key is used to track connector input parameters processing only (not pooling, not execute,\n     * not output)\n     */\n    EXECUTE_CONNECTOR_INPUT_EXPRESSIONS,\n\n    /**\n     * this key is used to track only the call to disconnect on a connector\n     */\n    EXECUTE_CONNECTOR_DISCONNECT,\n\n    /**\n     * this key is used to track the whole connector execution including pooling, input, execute,\n     * output and disconnect\n     */\n    EXECUTE_CONNECTOR_WORK,\n\n    /**\n     * this key is used to track the whole expression evaluation including its context. See\n     * ExpressionResolver.\n     */\n    EVALUATE_EXPRESSION_INCLUDING_CONTEXT,\n\n    /**\n     * this key is used to track the expression evaluation \"only\", assuming the context is already\n     * evaluated if necessary. See ExpressionService.\n     */\n    EVALUATE_EXPRESSION,\n\n    /**\n     * this key is used to track the expression evaluation \"only\", assuming the context is already\n     * evaluated if necessary. Evaluates a set of expression in one\n     * measure. See ExpressionService.\n     */\n    EVALUATE_EXPRESSIONS,\n\n}\n"
  },
  {
    "path": "services/bonita-time-tracker/src/main/java/org/bonitasoft/engine/tracking/csv/CSVFlushEventListener.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tracking.csv;\n\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.Calendar;\nimport java.util.GregorianCalendar;\nimport java.util.List;\n\nimport org.bonitasoft.engine.tracking.AbstractFlushEventListener;\nimport org.bonitasoft.engine.tracking.FlushEvent;\nimport org.bonitasoft.engine.tracking.Record;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class CSVFlushEventListener extends AbstractFlushEventListener {\n\n    private static final Logger log = LoggerFactory.getLogger(CSVFlushEventListener.class);\n    private final String csvSeparator;\n\n    private final String outputFolder;\n\n    public static final String FILE_PREFIX = \"bonita_timetracker_\";\n\n    public static final String FILE_SUFFIX = \".csv\";\n\n    public CSVFlushEventListener(final boolean activateAtStart, final String outputFolder, final String csvSeparator) {\n        super(activateAtStart);\n        this.outputFolder = outputFolder;\n        this.csvSeparator = csvSeparator;\n\n        final File outputFolderFile = new File(outputFolder);\n        if (!outputFolderFile.exists()) {\n            throw new RuntimeException(\"Output folder does not exist: \" + outputFolder);\n        }\n        if (!outputFolderFile.isDirectory()) {\n            throw new RuntimeException(\"Output folder is not a directory: \" + outputFolder);\n        }\n    }\n\n    @Override\n    public CSVFlushEventListenerResult flush(final FlushEvent flushEvent) throws Exception {\n        final long flushTime = flushEvent.getFlushTime();\n        final List<Record> records = flushEvent.getRecords();\n        final List<List<String>> csvContent = new ArrayList<List<String>>();\n        for (final Record record : records) {\n            final List<String> row = getRow(record);\n            csvContent.add(row);\n        }\n\n        final File outputFile = getDayFile(flushTime, this.outputFolder, FILE_PREFIX, FILE_SUFFIX);\n\n        if (!outputFile.exists()) {\n            log.info(\"Generating new csv file to: {} \", outputFile);\n            CSVUtil.writeCSVRow(outputFile, getHeaderRow(), this.csvSeparator);\n        } else {\n            log.info(\"Reusing csv file: {}\", outputFile);\n        }\n        CSVUtil.writeCSVRows(outputFile, csvContent, this.csvSeparator);\n        return new CSVFlushEventListenerResult(flushEvent, outputFile);\n    }\n\n    @Override\n    public String getStatus() {\n        String status = super.getStatus() + \"\\n\";\n        status += \"outputFolder: \" + this.outputFolder + \"\\n\";\n        return status;\n    }\n\n    @Override\n    public void notifyStopTracking() {\n        //nothing to do\n    }\n\n    @Override\n    public void notifyStartTracking() {\n        //nothing to do\n    }\n\n    private List<String> getRow(final Record record) {\n        final long timestamp = record.getTimestamp();\n        final GregorianCalendar cal = new GregorianCalendar();\n        cal.setTimeInMillis(timestamp);\n        final int year = cal.get(Calendar.YEAR);\n        final int month = cal.get(Calendar.MONTH) + 1;\n        final int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH);\n        final int hourOfDay = cal.get(Calendar.HOUR_OF_DAY);\n        final int minute = cal.get(Calendar.MINUTE);\n        final int second = cal.get(Calendar.SECOND);\n        final int millisecond = cal.get(Calendar.MILLISECOND);\n\n        final List<String> row = new ArrayList<String>();\n        row.add(String.valueOf(timestamp));\n        row.add(String.valueOf(year));\n        row.add(String.valueOf(month));\n        row.add(String.valueOf(dayOfMonth));\n        row.add(String.valueOf(hourOfDay));\n        row.add(String.valueOf(minute));\n        row.add(String.valueOf(second));\n        row.add(String.valueOf(millisecond));\n        row.add(String.valueOf(record.getDuration()));\n        row.add(record.getName().name());\n        row.add(record.getDescription());\n        return row;\n    }\n\n    private List<String> getHeaderRow() {\n        final List<String> header = new ArrayList<String>();\n        header.add(\"timestamp\");\n        header.add(\"year\");\n        header.add(\"month\");\n        header.add(\"dayOfMonth\");\n        header.add(\"hourOfDay\");\n        header.add(\"minute\");\n        header.add(\"second\");\n        header.add(\"millisecond\");\n        header.add(\"duration\");\n        header.add(\"name\");\n        header.add(\"description\");\n        return header;\n    }\n\n    private String getIntOnTwoNumbers(final int i) {\n        if (i < 10) {\n            return \"0\" + i;\n        }\n        return Integer.toString(i);\n    }\n\n    private File getDayFile(final long time, final String folder, final String filePrefix, final String fileSuffix) {\n        final StringBuilder sb = new StringBuilder();\n        final GregorianCalendar c = new GregorianCalendar();\n        c.setTimeInMillis(time);\n\n        sb.append(getIntOnTwoNumbers(c.get(Calendar.YEAR)));\n        sb.append(\"_\");\n        sb.append(getIntOnTwoNumbers(c.get(Calendar.MONTH) + 1));\n        sb.append(\"_\");\n        sb.append(getIntOnTwoNumbers(c.get(Calendar.DAY_OF_MONTH)));\n\n        final String timestamp = sb.toString();\n        final String fileName = filePrefix + timestamp + fileSuffix;\n\n        return new File(folder, fileName);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-time-tracker/src/main/java/org/bonitasoft/engine/tracking/csv/CSVFlushEventListenerResult.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tracking.csv;\n\nimport java.io.File;\n\nimport org.bonitasoft.engine.tracking.FlushEvent;\nimport org.bonitasoft.engine.tracking.FlushEventListenerResult;\n\npublic class CSVFlushEventListenerResult extends FlushEventListenerResult {\n\n    private final File outputFile;\n\n    public CSVFlushEventListenerResult(final FlushEvent flushEvent, final File outputFile) {\n        super(flushEvent);\n        this.outputFile = outputFile;\n    }\n\n    public File getOutputFile() {\n        return this.outputFile;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-time-tracker/src/main/java/org/bonitasoft/engine/tracking/csv/CSVUtil.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tracking.csv;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Scanner;\n\npublic class CSVUtil {\n\n    public static List<List<String>> readCSV(final boolean excludeHeader, final File csvFile, final String csvSeparator)\n            throws FileNotFoundException {\n        final List<List<String>> array = new ArrayList<List<String>>();\n        final InputStream inputStream = new FileInputStream(csvFile);\n        final Scanner scanner = new Scanner(inputStream);\n        try {\n            while (scanner.hasNextLine()) {\n                final String line = scanner.nextLine();\n                final List<String> lineElements = new ArrayList<String>();\n                lineElements.addAll(Arrays.asList(line.split(csvSeparator)));\n                array.add(lineElements);\n            }\n        } finally {\n            scanner.close();\n            try {\n                inputStream.close();\n            } catch (final IOException e) {\n                throw new RuntimeException(e);\n            }\n        }\n\n        if (excludeHeader) {\n            array.remove(0);\n        }\n        return array;\n    }\n\n    public static void writeCSVRow(final FileWriter writer, final List<String> row, final String csvSeparator)\n            throws IOException {\n        boolean first = true;\n        for (final String value : row) {\n            if (first) {\n                writer.append(value);\n                first = false;\n            } else {\n                writer.append(csvSeparator);\n                writer.append(value);\n            }\n        }\n        writer.append(\"\\n\");\n        writer.flush();\n    }\n\n    public static void writeCSVRow(final File file, final List<String> row, final String csvSeparator)\n            throws IOException {\n        final FileWriter writer = new FileWriter(file, true);\n        writeCSVRow(writer, row, csvSeparator);\n        writer.close();\n    }\n\n    public static void writeCSVRows(final File file, final List<List<String>> array, final String csvSeparator)\n            throws IOException {\n        final FileWriter writer = new FileWriter(file, true);\n        for (final List<String> row : array) {\n            writeCSVRow(writer, row, csvSeparator);\n        }\n        writer.close();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-time-tracker/src/main/java/org/bonitasoft/engine/tracking/memory/DayRecord.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tracking.memory;\n\nimport java.util.ArrayList;\nimport java.util.Calendar;\nimport java.util.GregorianCalendar;\nimport java.util.List;\nimport java.util.Queue;\n\nimport org.apache.commons.collections4.queue.CircularFifoQueue;\nimport org.bonitasoft.engine.tracking.Record;\n\n/**\n * @author Charles Souillard\n */\npublic class DayRecord {\n\n    private final String dayKey;\n    private final Queue<Record> records;\n\n    public DayRecord(final long timestamp, final int maxSize) {\n        this.dayKey = getDayKey(timestamp);\n        this.records = new CircularFifoQueue(maxSize);\n    }\n\n    public String getDayKey() {\n        return this.dayKey;\n    }\n\n    public List<Record> getRecordsCopy() {\n        return new ArrayList<>(this.records);\n    }\n\n    public boolean isExpectedDayKey(final long timestamp) {\n        final String expectedDayKey = getDayKey(timestamp);\n        return this.dayKey.equals(expectedDayKey);\n    }\n\n    private String getDayKey(final long timestamp) {\n        final GregorianCalendar cal = new GregorianCalendar();\n        cal.setTimeInMillis(timestamp);\n        final int year = cal.get(Calendar.YEAR);\n        final int month = cal.get(Calendar.MONTH) + 1;\n        final int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH);\n\n        return String.valueOf(year) + String.valueOf(month) + String.valueOf(dayOfMonth);\n    }\n\n    public void addRecords(final List<Record> newRecords) {\n        this.records.addAll(newRecords);\n    }\n\n    @Override\n    public String toString() {\n        return \"DayRecord{\" +\n                \"dayKey='\" + this.dayKey + '\\'' +\n                \", records.size=\" + this.records.size() +\n                '}';\n    }\n}\n"
  },
  {
    "path": "services/bonita-time-tracker/src/main/java/org/bonitasoft/engine/tracking/memory/MemoryFlushEventListener.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tracking.memory;\n\nimport org.bonitasoft.engine.tracking.AbstractFlushEventListener;\nimport org.bonitasoft.engine.tracking.FlushEvent;\nimport org.bonitasoft.engine.tracking.FlushEventListenerResult;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class MemoryFlushEventListener extends AbstractFlushEventListener {\n\n    private static final Logger log = LoggerFactory.getLogger(MemoryFlushEventListener.class);\n    private final int maxSize;\n    private DayRecord dayRecord;\n\n    public MemoryFlushEventListener(final boolean activateAtStart, final int maxSize) {\n        super(activateAtStart);\n        this.maxSize = maxSize;\n    }\n\n    @Override\n    public synchronized FlushEventListenerResult flush(final FlushEvent flushEvent) throws Exception {\n\n        if (flushEvent.getRecords().size() == 0) {\n            return new FlushEventListenerResult(flushEvent);\n        }\n\n        log.debug(\"Reusing csv file: FlushEvent received with {}   records.\", flushEvent.getRecords().size());\n\n        final long flushTime = flushEvent.getFlushTime();\n        if (this.dayRecord == null || !this.dayRecord.isExpectedDayKey(flushTime)) {\n            this.dayRecord = new DayRecord(flushTime, this.maxSize);\n        }\n        this.dayRecord.addRecords(flushEvent.getRecords());\n\n        log.info(\"Adding '{}' records to DayRecord with dayKey '{}'\", flushEvent.getRecords().size(),\n                this.dayRecord.getDayKey());\n\n        return new FlushEventListenerResult(flushEvent);\n    }\n\n    @Override\n    public String getStatus() {\n        String status = super.getStatus() + \"\\n\";\n        if (this.dayRecord == null) {\n            status += \"No DayRecord registered in memory.\";\n        } else {\n            status += \", dayRecord: \" + this.dayRecord.toString();\n        }\n        return status;\n    }\n\n    @Override\n    public void notifyStopTracking() {\n        this.dayRecord = null;\n    }\n\n    @Override\n    public void notifyStartTracking() {\n        //nothing to do\n    }\n\n    public synchronized DayRecord getDayRecord() {\n        return this.dayRecord;\n    }\n\n    public synchronized void clear() {\n        this.dayRecord = null;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-time-tracker/src/test/java/org/bonitasoft/engine/tracking/AbstractFlushEventListenerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tracking;\n\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class AbstractFlushEventListenerTest extends AbstractTimeTrackerTest {\n\n    @Test\n    public void should_deactivate_notifyStopTracking() {\n        final AbstractFlushEventListener listener = spy(new AbstractFlushEventListener(true) {\n\n            @Override\n            public FlushEventListenerResult flush(final FlushEvent flushEvent) throws Exception {\n                return null;\n            }\n\n            @Override\n            public void notifyStopTracking() {\n\n            }\n\n            @Override\n            public void notifyStartTracking() {\n\n            }\n        });\n        listener.deactivate();\n        verify(listener, times(1)).notifyStopTracking();\n    }\n\n    @Test\n    public void should_activate_notifyStartTracking() {\n        final AbstractFlushEventListener listener = spy(new AbstractFlushEventListener(true) {\n\n            @Override\n            public FlushEventListenerResult flush(final FlushEvent flushEvent) throws Exception {\n                return null;\n            }\n\n            @Override\n            public void notifyStopTracking() {\n\n            }\n\n            @Override\n            public void notifyStartTracking() {\n\n            }\n        });\n        listener.activate();\n        verify(listener, times(1)).notifyStartTracking();\n    }\n}\n"
  },
  {
    "path": "services/bonita-time-tracker/src/test/java/org/bonitasoft/engine/tracking/AbstractTimeTrackerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tracking;\n\npublic class AbstractTimeTrackerTest {\n\n    protected void checkRecord(final Record expected, final Record actual) {\n        RecordAssert.assertThat(expected).isEqualTo(actual);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-time-tracker/src/test/java/org/bonitasoft/engine/tracking/FlushThreadTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tracking;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.ArgumentMatchers.anyLong;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport java.util.ArrayList;\n\nimport org.junit.Test;\n\npublic class FlushThreadTest {\n\n    @SuppressWarnings(\"unchecked\")\n    @Test\n    public void should_flush_thread_flush_until_interruption() throws Exception {\n        final TimeTracker timeTracker = mock(TimeTracker.class);\n        final Clock clock = mock(Clock.class);\n        final FlushResult flushResult = new FlushResult(System.currentTimeMillis(), new ArrayList<>());\n\n        when(timeTracker.getClock()).thenReturn(clock);\n        when(timeTracker.getFlushIntervalInMS()).thenReturn(0L);\n        when(timeTracker.flush()).thenReturn(flushResult);\n        when(clock.sleep(anyLong())).thenReturn(true).thenReturn(true).thenReturn(true)\n                .thenThrow(InterruptedException.class);\n\n        final FlushThread flushThread = new FlushThread(timeTracker);\n        flushThread.start();\n        // wait max 1 minute to not freeze CI in case of a bug\n        flushThread.join(60000);\n        verify(timeTracker, times(3)).flush();\n        verify(clock, times(4)).sleep(anyLong());\n    }\n\n    @Test\n    public void should_flush_thread_flush_on_latest_flush_interval() throws Exception {\n        final TimeTracker timeTracker = mock(TimeTracker.class);\n        final FlushThread flushThread = new FlushThread(timeTracker);\n        final long now = 10;\n        final long lastFlushTimestamp = 9;\n\n        when(timeTracker.getFlushIntervalInMS()).thenReturn(4L);\n        assertEquals(3, flushThread.getSleepTime(now, lastFlushTimestamp));\n\n        when(timeTracker.getFlushIntervalInMS()).thenReturn(3L);\n        assertEquals(2, flushThread.getSleepTime(now, lastFlushTimestamp));\n    }\n\n    @Test\n    public void should_last_flush_timestamp_move_to_now() {\n        final TimeTracker timeTracker = mock(TimeTracker.class);\n        final FlushThread flushThread = new FlushThread(timeTracker);\n        final long now = 10;\n\n        when(timeTracker.flush()).thenThrow(RuntimeException.class);\n        final long lastFlushTimestamp = flushThread.flush(now);\n        assertEquals(now, lastFlushTimestamp);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-time-tracker/src/test/java/org/bonitasoft/engine/tracking/RecordAssert.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tracking;\n\nimport org.assertj.core.api.AbstractAssert;\nimport org.assertj.core.util.Objects;\n\n/**\n * {@link Record} specific assertions - Generated by CustomAssertionGenerator.\n */\npublic class RecordAssert extends AbstractAssert<RecordAssert, Record> {\n\n    /**\n     * Creates a new <code>{@link RecordAssert}</code> to make assertions on actual Record.\n     *\n     * @param actual the Record we want to make assertions on.\n     */\n    public RecordAssert(final Record actual) {\n        super(actual, RecordAssert.class);\n    }\n\n    /**\n     * An entry point for RecordAssert to follow AssertJ standard <code>assertThat()</code>\n     * statements.<br>\n     * With a static import, one can write directly: <code>assertThat(myRecord)</code> and get\n     * specific assertion with code completion.\n     *\n     * @param actual the Record we want to make assertions on.\n     * @return a new <code>{@link RecordAssert}</code>\n     */\n    public static RecordAssert assertThat(final Record actual) {\n        return new RecordAssert(actual);\n    }\n\n    /**\n     * Verifies that the actual Record's description is equal to the given one.\n     *\n     * @param description the given description to compare the actual Record's description to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual Record's description is not equal to the given one.\n     */\n    public RecordAssert hasDescription(final String description) {\n        // check that actual Record we want to make assertions on is not null.\n        isNotNull();\n\n        // overrides the default error message with a more explicit one\n        final String assertjErrorMessage = \"\\nExpecting description of:\\n  <%s>\\nto be:\\n  <%s>\\nbut was:\\n  <%s>\";\n\n        // null safe check\n        final String actualDescription = this.actual.getDescription();\n        if (!Objects.areEqual(actualDescription, description)) {\n            failWithMessage(assertjErrorMessage, this.actual, description, actualDescription);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual Record's duration is equal to the given one.\n     *\n     * @param duration the given duration to compare the actual Record's duration to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual Record's duration is not equal to the given one.\n     */\n    public RecordAssert hasDuration(final long duration) {\n        // check that actual Record we want to make assertions on is not null.\n        isNotNull();\n\n        // overrides the default error message with a more explicit one\n        final String assertjErrorMessage = \"\\nExpecting duration of:\\n  <%s>\\nto be:\\n  <%s>\\nbut was:\\n  <%s>\";\n\n        // check\n        final long actualDuration = this.actual.getDuration();\n        if (actualDuration != duration) {\n            failWithMessage(assertjErrorMessage, this.actual, duration, actualDuration);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual Record's name is equal to the given one.\n     *\n     * @param name the given name to compare the actual Record's name to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual Record's name is not equal to the given one.\n     */\n    public RecordAssert hasName(final TimeTrackerRecords name) {\n        // check that actual Record we want to make assertions on is not null.\n        isNotNull();\n\n        // overrides the default error message with a more explicit one\n        final String assertjErrorMessage = \"\\nExpecting name of:\\n  <%s>\\nto be:\\n  <%s>\\nbut was:\\n  <%s>\";\n\n        // null safe check\n        final TimeTrackerRecords actualName = this.actual.getName();\n        if (!Objects.areEqual(actualName, name)) {\n            failWithMessage(assertjErrorMessage, this.actual, name, actualName);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n    /**\n     * Verifies that the actual Record's timestamp is equal to the given one.\n     *\n     * @param timestamp the given timestamp to compare the actual Record's timestamp to.\n     * @return this assertion object.\n     * @throws AssertionError - if the actual Record's timestamp is not equal to the given one.\n     */\n    public RecordAssert hasTimestamp(final long timestamp) {\n        // check that actual Record we want to make assertions on is not null.\n        isNotNull();\n\n        // overrides the default error message with a more explicit one\n        final String assertjErrorMessage = \"\\nExpecting timestamp of:\\n  <%s>\\nto be:\\n  <%s>\\nbut was:\\n  <%s>\";\n\n        // check\n        final long actualTimestamp = this.actual.getTimestamp();\n        if (actualTimestamp != timestamp) {\n            failWithMessage(assertjErrorMessage, this.actual, timestamp, actualTimestamp);\n        }\n\n        // return the current assertion for method chaining\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-time-tracker/src/test/java/org/bonitasoft/engine/tracking/ThreadSleepClockImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tracking;\n\nimport static org.junit.Assert.assertTrue;\n\nimport org.junit.Test;\n\npublic class ThreadSleepClockImplTest {\n\n    @Test\n    public void should_sleep_given_time() throws Exception {\n        final ThreadSleepClockImpl clock = new ThreadSleepClockImpl();\n        final long millis = 10L;\n        final long startTime = System.currentTimeMillis();\n        clock.sleep(millis);\n        final long endTime = System.currentTimeMillis();\n        assertTrue(endTime - startTime >= millis);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-time-tracker/src/test/java/org/bonitasoft/engine/tracking/TimeTrackerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tracking;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.*;\nimport static org.mockito.Mockito.*;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.bonitasoft.engine.tracking.memory.MemoryFlushEventListener;\nimport org.junit.After;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.ArgumentMatchers;\nimport org.mockito.Mock;\nimport org.mockito.Mockito;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class TimeTrackerTest extends AbstractTimeTrackerTest {\n\n    private TimeTracker tracker;\n\n    @Mock\n    private FlushThread flushThread;\n\n    private static final TimeTrackerRecords REC = TimeTrackerRecords.EVALUATE_EXPRESSION;\n\n    private static final TimeTrackerRecords REC1 = TimeTrackerRecords.EVALUATE_EXPRESSION_INCLUDING_CONTEXT;\n    private static final TimeTrackerRecords REC2 = TimeTrackerRecords.EXECUTE_CONNECTOR_CALLABLE;\n    private static final TimeTrackerRecords REC3 = TimeTrackerRecords.EXECUTE_CONNECTOR_DISCONNECT;\n    private static final TimeTrackerRecords INACTIVATED_REC = TimeTrackerRecords.EXECUTE_CONNECTOR_OUTPUT_OPERATIONS;\n\n    @After\n    public void tearDown() {\n        // Make sure to clean tracker threads by stopping tracker.\n        if (this.tracker != null) {\n            this.tracker.stop();\n        }\n    }\n\n    TimeTracker createTimeTracker(final Clock clock,\n            final List<FlushEventListener> listeners, final boolean enabled, final int maxSize,\n            final int flushIntervalInSeconds, final TimeTrackerRecords rec) {\n        return new TimeTracker(clock, enabled, listeners, maxSize, flushIntervalInSeconds, rec.name()) {\n\n            @Override\n            FlushThread createFlushThread() {\n                return TimeTrackerTest.this.flushThread;\n            }\n        };\n    }\n\n    TimeTracker createTimeTracker(final boolean enabled, final List<FlushEventListener> flushEventListeners,\n            final int maxSize,\n            final int flushIntervalInSeconds, final TimeTrackerRecords... records) {\n        final List<String> recordsAsString = new ArrayList<>();\n        for (final TimeTrackerRecords record : records) {\n            recordsAsString.add(record.name());\n        }\n        return new TimeTracker(enabled, flushEventListeners, maxSize, flushIntervalInSeconds,\n                recordsAsString.toArray(new String[records.length])) {\n\n            @Override\n            FlushThread createFlushThread() {\n                return TimeTrackerTest.this.flushThread;\n            }\n        };\n    }\n\n    @Test\n    public void should_isTrackable_returns_false_if_not_started() {\n        this.tracker = createTimeTracker(true, null, 10, 2, REC);\n        assertFalse(this.tracker.isTrackable(REC));\n    }\n\n    @Test\n    public void isTrackable() {\n        when(this.flushThread.isStarted()).thenReturn(true);\n        this.tracker = createTimeTracker(true, null, 10, 2, REC1, REC2);\n        this.tracker.start();\n        assertTrue(this.tracker.isTrackable(REC1));\n        assertTrue(this.tracker.isTrackable(REC2));\n        assertFalse(this.tracker.isTrackable(REC3));\n        this.tracker.stop();\n    }\n\n    @Test\n    public void trackRecords() {\n        when(this.flushThread.isStarted()).thenReturn(true);\n        this.tracker = createTimeTracker(true, null, 10, 2, REC1, REC2);\n        this.tracker.start();\n        this.tracker.track(REC1, \"rec11Desc\", 100);\n        this.tracker.track(REC1, \"rec12Desc\", 200);\n        this.tracker.track(REC2, \"rec2Desc\", 300);\n        this.tracker.track(INACTIVATED_REC, \"blabla\", 1000);\n        final Map<TimeTrackerRecords, List<Record>> records = mapRecords(this.tracker.getRecordsCopy());\n        assertEquals(2, records.size());\n        assertEquals(2, records.get(REC1).size());\n        assertEquals(1, records.get(REC2).size());\n\n        final List<Record> rec1s = records.get(REC1);\n\n        final Record rec11 = rec1s.get(0);\n        assertEquals(REC1, rec11.getName());\n        assertEquals(\"rec11Desc\", rec11.getDescription());\n        assertEquals(100L, rec11.getDuration());\n\n        final Record rec12 = rec1s.get(1);\n        assertEquals(REC1, rec12.getName());\n        assertEquals(\"rec12Desc\", rec12.getDescription());\n        assertEquals(200L, rec12.getDuration());\n\n        final List<Record> rec2s = records.get(REC2);\n        final Record rec2 = rec2s.get(0);\n        assertEquals(REC2, rec2.getName());\n        assertEquals(\"rec2Desc\", rec2.getDescription());\n        assertEquals(300L, rec2.getDuration());\n        this.tracker.stop();\n    }\n\n    @Test\n    public void should_not_track_when_not_enabled() {\n        this.tracker = createTimeTracker(false, null, 10, 2, REC1, REC2);\n        this.tracker.start();\n        this.tracker.track(REC1, \"rec11Desc\", 100);\n        this.tracker.track(REC1, \"rec12Desc\", 200);\n        this.tracker.track(REC2, \"rec2Desc\", 300);\n        this.tracker.track(INACTIVATED_REC, \"blabla\", 1000);\n\n        assertTrue(this.tracker.getRecordsCopy().isEmpty());\n    }\n\n    @Test\n    public void timestamp() throws Exception {\n        when(this.flushThread.isStarted()).thenReturn(true);\n        this.tracker = createTimeTracker(true, null, 10, 2, REC1);\n        this.tracker.start();\n        this.tracker.track(REC1, \"desc2\", 100);\n        Thread.sleep(2);\n        this.tracker.track(REC1, \"desc2\", 200);\n        final Map<TimeTrackerRecords, List<Record>> records = mapRecords(this.tracker.getRecordsCopy());\n        assertEquals(1, records.size());\n        assertEquals(2, records.get(REC1).size());\n\n        final List<Record> rec1s = records.get(REC1);\n\n        assertTrue(rec1s.get(0).getTimestamp() < rec1s.get(1).getTimestamp());\n        this.tracker.stop();\n    }\n\n    private Map<TimeTrackerRecords, List<Record>> mapRecords(final List<Record> records) {\n        final Map<TimeTrackerRecords, List<Record>> result = new HashMap<>();\n        if (records != null) {\n            for (final Record record : records) {\n                final TimeTrackerRecords name = record.getName();\n                if (!result.containsKey(name)) {\n                    result.put(name, new ArrayList<>());\n                }\n                result.get(name).add(record);\n            }\n        }\n        return result;\n    }\n\n    @Test\n    public void should_build_do_not_start_tracking() {\n        this.tracker = createTimeTracker(true, null, 10, 2);\n        assertFalse(this.flushThread.isStarted());\n    }\n\n    @Test\n    public void should_start_flush_thread_if_thread_is_stopped() {\n        doReturn(false).when(this.flushThread).isStarted();\n        this.tracker = createTimeTracker(true, null, 10, 2);\n        this.tracker.start();\n        verify(this.flushThread).start();\n    }\n\n    @Test\n    public void should_stop_flush_thread_is_running() {\n        when(this.flushThread.isStarted()).thenReturn(true);\n        this.tracker = createTimeTracker(true, null, 10, 2);\n        this.tracker.start();\n        this.tracker.stop();\n        verify(this.flushThread).interrupt();\n    }\n\n    @Test\n    public void should_not_stop_flush_thread_is_running() {\n        this.tracker = createTimeTracker(true, null, 10, 2);\n        this.tracker.stop();\n        verify(this.flushThread, never()).interrupt();\n    }\n\n    @Test\n    public void should_flush_ignore_flush_listeners_exceptions() throws Exception {\n        final Clock clock = mock(Clock.class);\n\n        final FlushEventListener listener1 = mock(FlushEventListener.class);\n        when(listener1.getName()).thenReturn(\"listener1\");\n        when(listener1.isActive()).thenReturn(true);\n        final FlushEventListener listener2 = mock(FlushEventListener.class);\n        when(listener2.getName()).thenReturn(\"listener2\");\n        when(listener2.isActive()).thenReturn(true);\n        final FlushEventListener listener3 = mock(FlushEventListener.class);\n        when(listener3.getName()).thenReturn(\"listener3\");\n        when(listener3.isActive()).thenReturn(true);\n\n        Mockito.when(listener2.flush(ArgumentMatchers.any(FlushEvent.class))).thenThrow(new Exception());\n\n        final List<FlushEventListener> listeners = new ArrayList<>();\n        listeners.add(listener1);\n        listeners.add(listener2);\n        listeners.add(listener3);\n\n        this.tracker = createTimeTracker(clock, listeners, true, 10, 1, REC);\n\n        this.tracker.track(REC, \"desc\", 10);\n\n        this.tracker.flush();\n\n        verify(listener1, times(1)).flush(ArgumentMatchers.any(FlushEvent.class));\n        verify(listener2, times(1)).flush(ArgumentMatchers.any(FlushEvent.class));\n        verify(listener3, times(1)).flush(ArgumentMatchers.any(FlushEvent.class));\n    }\n\n    @Test\n    public void should_flush_call_all_flush_listeners() throws Exception {\n        final Clock clock = mock(Clock.class);\n\n        final FlushEventListener listener1 = mock(FlushEventListener.class);\n        when(listener1.getName()).thenReturn(\"listener1\");\n        when(listener1.isActive()).thenReturn(true);\n        final FlushEventListener listener2 = mock(FlushEventListener.class);\n        when(listener2.getName()).thenReturn(\"listener2\");\n        when(listener2.isActive()).thenReturn(true);\n\n        final List<FlushEventListener> listeners = new ArrayList<>();\n        listeners.add(listener1);\n        listeners.add(listener2);\n\n        this.tracker = createTimeTracker(clock, listeners, true, 10, 1, REC);\n\n        this.tracker.track(REC, \"desc\", 10);\n\n        this.tracker.flush();\n\n        verify(listener1, times(1)).flush(ArgumentMatchers.any(FlushEvent.class));\n        verify(listener2, times(1)).flush(ArgumentMatchers.any(FlushEvent.class));\n    }\n\n    @Test\n    public void rollingRecords() {\n        when(this.flushThread.isStarted()).thenReturn(true);\n        this.tracker = createTimeTracker(true, null, 2, 2, REC);\n        this.tracker.start();\n        this.tracker.track(REC, \"rec1\", 100);\n        assertEquals(1, this.tracker.getRecordsCopy().size());\n        this.tracker.track(REC, \"rec2\", 100);\n        assertEquals(2, this.tracker.getRecordsCopy().size());\n        this.tracker.track(REC, \"rec3\", 100);\n        assertEquals(2, this.tracker.getRecordsCopy().size());\n        assertEquals(\"rec2\", this.tracker.getRecordsCopy().get(0).getDescription());\n        assertEquals(\"rec3\", this.tracker.getRecordsCopy().get(1).getDescription());\n        this.tracker.stop();\n    }\n\n    @Test\n    public void testGetActiveListeners() {\n        final FlushEventListener listener1 = mock(FlushEventListener.class);\n        when(listener1.getName()).thenReturn(\"listener1\");\n        when(listener1.isActive()).thenReturn(true);\n        final FlushEventListener listener2 = mock(FlushEventListener.class);\n        when(listener2.getName()).thenReturn(\"listener2\");\n        when(listener2.isActive()).thenReturn(false);\n\n        final List<FlushEventListener> flushEventListeners = new ArrayList<>();\n        flushEventListeners.add(listener1);\n        flushEventListeners.add(listener2);\n\n        this.tracker = createTimeTracker(true, flushEventListeners, 2, 2, REC);\n\n        assertEquals(1, this.tracker.getActiveFlushEventListeners().size());\n    }\n\n    @Test\n    public void testActivateListeners() {\n        final FlushEventListener listener1 = mock(FlushEventListener.class);\n        when(listener1.getName()).thenReturn(\"listener1\");\n        final FlushEventListener listener2 = mock(FlushEventListener.class);\n        when(listener2.getName()).thenReturn(\"listener2\");\n\n        final List<FlushEventListener> flushEventListeners = new ArrayList<>();\n        flushEventListeners.add(listener1);\n        flushEventListeners.add(listener2);\n\n        this.tracker = createTimeTracker(true, flushEventListeners, 2, 2, REC);\n\n        this.tracker.activateFlushEventListener(\"listener2\");\n        verify(listener2, times(1)).activate();\n    }\n\n    @Test\n    public void testDeactivateListeners() {\n        final FlushEventListener listener1 = mock(FlushEventListener.class);\n        when(listener1.getName()).thenReturn(\"listener1\");\n\n        final List<FlushEventListener> flushEventListeners = new ArrayList<>();\n        flushEventListeners.add(listener1);\n\n        this.tracker = createTimeTracker(true, flushEventListeners, 2, 2, REC);\n\n        this.tracker.deactivateFlushEventListener(\"listener1\");\n        verify(listener1, times(1)).deactivate();\n    }\n\n    @Test\n    public void testActivatedRecords() {\n        this.tracker = createTimeTracker(true, null, 2, 2, REC);\n\n        assertEquals(1, this.tracker.getActivatedRecords().size());\n\n        this.tracker.activateRecord(REC2);\n        assertEquals(2, this.tracker.getActivatedRecords().size());\n\n        //no duplicate\n        this.tracker.activateRecord(REC2);\n        assertEquals(2, this.tracker.getActivatedRecords().size());\n\n        this.tracker.deactivatedRecord(REC);\n        assertEquals(1, this.tracker.getActivatedRecords().size());\n    }\n\n    @Test\n    public void testFlushInterval() {\n        this.tracker = createTimeTracker(true, null, 2, 1, REC);\n\n        assertEquals(1000, this.tracker.getFlushIntervalInMS());\n\n        this.tracker.setFlushIntervalInMS(111);\n        assertEquals(111, this.tracker.getFlushIntervalInMS());\n\n        this.tracker.setFlushIntervalInSeconds(10);\n        assertEquals(10000, this.tracker.getFlushIntervalInMS());\n    }\n\n    @Test\n    public void should_stop_tracking_interrupt_flush_thread_and_listeners() {\n        final FlushEventListener listener1 = mock(FlushEventListener.class);\n        when(listener1.getName()).thenReturn(\"listener1\");\n        when(listener1.isActive()).thenReturn(true);\n        final FlushEventListener listener2 = mock(FlushEventListener.class);\n        when(listener2.getName()).thenReturn(\"listener2\");\n        when(listener2.isActive()).thenReturn(true);\n\n        final List<FlushEventListener> flushEventListeners = new ArrayList<>();\n        flushEventListeners.add(listener1);\n        flushEventListeners.add(listener2);\n\n        this.tracker = createTimeTracker(true, flushEventListeners, 2, 2, REC);\n        this.tracker.start();\n        when(this.flushThread.isStarted()).thenReturn(true);\n\n        this.tracker.stopTracking();\n        verify(this.flushThread, times(1)).interrupt();\n        verify(listener1, times(1)).notifyStopTracking();\n        verify(listener2, times(1)).notifyStopTracking();\n    }\n\n    @Test\n    public void should_start_tracking_start_flush_thread_and_listeners() {\n        final FlushEventListener listener1 = mock(FlushEventListener.class);\n        when(listener1.getName()).thenReturn(\"listener1\");\n        when(listener1.isActive()).thenReturn(true);\n        final FlushEventListener listener2 = mock(FlushEventListener.class);\n        when(listener2.getName()).thenReturn(\"listener2\");\n        when(listener2.isActive()).thenReturn(true);\n\n        final List<FlushEventListener> flushEventListeners = new ArrayList<>();\n        flushEventListeners.add(listener1);\n        flushEventListeners.add(listener2);\n\n        this.tracker = createTimeTracker(true, flushEventListeners, 2, 2, REC);\n        when(this.flushThread.isStarted()).thenReturn(false);\n        this.tracker.start();\n\n        this.tracker.startTracking();\n\n        verify(this.flushThread, times(2)).start();\n        verify(listener1, times(2)).notifyStartTracking();\n        verify(listener2, times(2)).notifyStartTracking();\n    }\n\n    @Test\n    public void should_pause_resume_without_error() {\n\n        //given\n        this.tracker = new TimeTracker(true, new LinkedList<>(), 500, 1, (String[]) null);\n\n        //when\n        this.tracker.start();\n        int i = 0;\n        try {\n            // Error may not occur on the first execution. I did reproduce it in 4 to 8 executions.\n            // To make myself confident, I execute the test 100 times.\n            // Current implementation is deterministic, but previous one was not. Hence the loop in the test.\n            while (i < 100) {\n                i++;\n                this.tracker.pause();\n                this.tracker.resume();\n                //No errors expected\n            }\n        } finally {\n            System.err.println(\"Test ended after iteration: \" + i);\n            this.tracker.stop();\n        }\n    }\n\n    @Test\n    public void should_start_method_be_reentrant() {\n\n        //given\n        this.tracker = new TimeTracker(true, new LinkedList<>(), 500, 1, (String[]) null);\n        final Set<Thread> initialThreadSet = Thread.getAllStackTraces().keySet();\n        this.tracker.start();\n        //expect\n        final Set<Thread> runningTimeTrackerThreadSet = Thread.getAllStackTraces().keySet();\n        assertThat(initialThreadSet.size() + 1).isEqualTo(runningTimeTrackerThreadSet.size());\n\n        //when\n        this.tracker.start();\n\n        //then\n        final Set<Thread> stillRunningTimeTrackerThreadSet = Thread.getAllStackTraces().keySet();\n        assertThat(runningTimeTrackerThreadSet).isEqualTo(stillRunningTimeTrackerThreadSet);\n    }\n\n    @Test\n    public void isTracking_should_be_false_if_tracking_not_started() {\n        //given\n        this.tracker = new TimeTracker(true, new LinkedList<>(), 500, 1, (String[]) null);\n        //when\n        final boolean trackingStatus = this.tracker.isTracking();\n        //then\n        assertThat(trackingStatus).as(\"Tracking status must be FALSE if tracker has not yet been started.\").isFalse();\n    }\n\n    @Test\n    public void isTracking_should_be_true_if_tracking_is_started() {\n        //given\n        this.tracker = new TimeTracker(true, new LinkedList<>(), 500, 1, (String[]) null);\n        try {\n            this.tracker.start();\n\n            //when\n            final boolean trackingStatus = this.tracker.isTracking();\n\n            //then\n            assertThat(trackingStatus).as(\"Tracking status must be TRUE after start method has been called.\").isTrue();\n        } finally {\n            this.tracker.stop();\n        }\n    }\n\n    @Test\n    public void should_not_leave_unused_threads_when_stopped() {\n        //given\n        this.tracker = new TimeTracker(true, new LinkedList<>(), 500, 1, (String[]) null);\n        final Set<Thread> beforeTimeTrackerStartedThreadSet = Thread.getAllStackTraces().keySet();\n        this.tracker.start();\n\n        //when\n        this.tracker.stop();\n\n        //then\n        final Set<Thread> afterTimeTrackerStoppedThreadSet = Thread.getAllStackTraces().keySet();\n        afterTimeTrackerStoppedThreadSet.removeAll(beforeTimeTrackerStartedThreadSet);\n\n        //There should be no more threads than the ones existing prior to tracker startup\n        assertThat(afterTimeTrackerStoppedThreadSet).isEqualTo(Collections.emptySet());\n    }\n\n    @Test\n    public void should_add_remove_listeners() {\n        // create a tracker\n        this.tracker = new TimeTracker(true, new LinkedList<>(), 500, 1);\n\n        // no active listener\n        assertThat(this.tracker.getActiveFlushEventListeners()).isEmpty();\n\n        // add a new listener\n        final FlushEventListener flushEvent = new MemoryFlushEventListener(true, 10);\n\n        this.tracker.addFlushEventListener(flushEvent);\n        assertThat(this.tracker.getActiveFlushEventListeners()).containsOnly(flushEvent);\n\n        // remove the same listener\n        this.tracker.removeFlushEventListener(flushEvent.getName());\n        assertThat(this.tracker.getActiveFlushEventListeners()).isEmpty();\n    }\n\n    @Test\n    public void should_have_one_listener_based_on_listener_name() {\n        // create a tracker\n        this.tracker = new TimeTracker(true, new LinkedList<>(), 500, 1);\n        // add a new listener\n        final FlushEventListener flushEvent1 = new MemoryFlushEventListener(true, 10);\n        final FlushEventListener flushEvent2 = new MemoryFlushEventListener(true, 10);\n\n        this.tracker.addFlushEventListener(flushEvent1);\n        this.tracker.addFlushEventListener(flushEvent2);\n        // must have only one time\n        assertThat(this.tracker.getActiveFlushEventListeners()).containsOnlyOnce(flushEvent2);\n    }\n}\n"
  },
  {
    "path": "services/bonita-time-tracker/src/test/java/org/bonitasoft/engine/tracking/csv/CSVFlushEventListenerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tracking.csv;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.io.File;\nimport java.util.Arrays;\nimport java.util.Calendar;\nimport java.util.GregorianCalendar;\nimport java.util.List;\n\nimport org.bonitasoft.engine.tracking.AbstractTimeTrackerTest;\nimport org.bonitasoft.engine.tracking.FlushEvent;\nimport org.bonitasoft.engine.tracking.Record;\nimport org.bonitasoft.engine.tracking.RecordAssert;\nimport org.bonitasoft.engine.tracking.TimeTrackerRecords;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.rules.TemporaryFolder;\nimport org.junit.runner.RunWith;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class CSVFlushEventListenerTest extends AbstractTimeTrackerTest {\n\n    private static final TimeTrackerRecords TIME_TRACKER_RECORDS = TimeTrackerRecords.EVALUATE_EXPRESSION;\n\n    @Rule\n    public TemporaryFolder temporaryFolder = new TemporaryFolder();\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void should_work_if_output_folder_is_a_folder() throws Exception {\n        new CSVFlushEventListener(true, this.temporaryFolder.newFolder().getAbsolutePath(), \";\");\n    }\n\n    @Test\n    public void should_fail_if_output_folder_unknown() {\n        //then\n        this.expectedException.expect(RuntimeException.class);\n        this.expectedException.expectMessage(\"Output folder does not exist\");\n\n        //when\n        new CSVFlushEventListener(true, \"unknownFolder\", \";\");\n    }\n\n    @Test\n    public void should_fail_if_outputfolder_is_a_file() throws Exception {\n        //then\n        this.expectedException.expect(RuntimeException.class);\n        this.expectedException.expectMessage(\"Output folder is not a directory\");\n\n        //when\n        new CSVFlushEventListener(true, this.temporaryFolder.newFile().getAbsolutePath(), \";\");\n    }\n\n    @Test\n    public void flushedCsv() throws Exception {\n        //given\n        final CSVFlushEventListener csvFlushEventListener = new CSVFlushEventListener(true,\n                this.temporaryFolder.newFolder().getAbsolutePath(), \";\");\n        final Record rec1 = new Record(System.currentTimeMillis(), TIME_TRACKER_RECORDS, \"rec1Desc\", 100);\n        final Record rec2 = new Record(System.currentTimeMillis(), TIME_TRACKER_RECORDS, \"rec2Desc\", 200);\n\n        //when\n        final CSVFlushEventListenerResult csvFlushResult = csvFlushEventListener\n                .flush(new FlushEvent(System.currentTimeMillis(), Arrays.asList(rec1, rec2)));\n        final File csvFile = csvFlushResult.getOutputFile();\n\n        //then\n        final List<List<String>> csvValues = CSVUtil.readCSV(true, csvFile, \";\");\n        assertThat(csvValues).as(\"should contains 2 records\").hasSize(2);\n        checkCSVRecord(rec1, csvValues.get(0));\n        checkCSVRecord(rec2, csvValues.get(1));\n\n        final List<Record> records = csvFlushResult.getFlushEvent().getRecords();\n        assertThat(records).as(\"should contains 2 records\").hasSize(2);\n        checkRecord(rec1, records.get(0));\n        checkRecord(rec2, records.get(1));\n    }\n\n    private void checkCSVRecord(final Record record, final List<String> csvValues) {\n        // timestamp, year, month, day, hour, minute, second, millisecond, duration, name, description]\n        assertThat(csvValues).hasSize(11);\n\n        final long timestamp = record.getTimestamp();\n        final GregorianCalendar cal = new GregorianCalendar();\n        cal.setTimeInMillis(timestamp);\n        final int year = cal.get(Calendar.YEAR);\n        final int month = cal.get(Calendar.MONTH) + 1;\n        final int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH);\n        final int hourOfDay = cal.get(Calendar.HOUR_OF_DAY);\n        final int minute = cal.get(Calendar.MINUTE);\n        final int second = cal.get(Calendar.SECOND);\n        final int millisecond = cal.get(Calendar.MILLISECOND);\n\n        RecordAssert.assertThat(record)\n                .hasTimestamp(Long.valueOf(csvValues.get(0)).longValue())\n                .hasDuration(Long.valueOf(csvValues.get(8)).longValue())\n                .hasDescription(csvValues.get(10))\n                .hasName(TimeTrackerRecords.EVALUATE_EXPRESSION);\n\n        assertThat(timestamp).isEqualTo(Long.valueOf(csvValues.get(0)).longValue());\n        assertThat(year).isEqualTo(Integer.valueOf(csvValues.get(1)).intValue());\n        assertThat(month).isEqualTo(Integer.valueOf(csvValues.get(2)).intValue());\n        assertThat(dayOfMonth).isEqualTo(Integer.valueOf(csvValues.get(3)).intValue());\n        assertThat(hourOfDay).isEqualTo(Integer.valueOf(csvValues.get(4)).intValue());\n        assertThat(minute).isEqualTo(Integer.valueOf(csvValues.get(5)).intValue());\n        assertThat(second).isEqualTo(Integer.valueOf(csvValues.get(6)).intValue());\n        assertThat(millisecond).isEqualTo(Integer.valueOf(csvValues.get(7)).intValue());\n\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-time-tracker/src/test/java/org/bonitasoft/engine/tracking/memory/MemoryFlushEventListenerTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.tracking.memory;\n\nimport static org.junit.Assert.assertEquals;\n\nimport java.util.Arrays;\n\nimport org.bonitasoft.engine.tracking.FlushEvent;\nimport org.bonitasoft.engine.tracking.Record;\nimport org.bonitasoft.engine.tracking.TimeTrackerRecords;\nimport org.junit.Test;\n\n/**\n * @author Charles Souillard\n */\npublic class MemoryFlushEventListenerTest {\n\n    private static final TimeTrackerRecords REC = TimeTrackerRecords.EVALUATE_EXPRESSION;\n\n    @Test\n    public void should_day_record_keep_all_records_of_subsequent_flush() throws Exception {\n        final MemoryFlushEventListener listener = new MemoryFlushEventListener(true, 10);\n        final Record rec1 = new Record(System.currentTimeMillis(), REC, \"rec1Desc\", 100);\n        listener.flush(new FlushEvent(System.currentTimeMillis(), Arrays.asList(rec1)));\n        assertEquals(1, listener.getDayRecord().getRecordsCopy().size());\n        final Record rec2 = new Record(System.currentTimeMillis(), REC, \"rec2Desc\", 200);\n        listener.flush(new FlushEvent(System.currentTimeMillis(), Arrays.asList(rec2)));\n        assertEquals(2, listener.getDayRecord().getRecordsCopy().size());\n    }\n\n    @Test\n    public void should_day_record_never_exceed_maxSize() throws Exception {\n\n        final MemoryFlushEventListener listener = new MemoryFlushEventListener(true, 1);\n        final Record rec1 = new Record(System.currentTimeMillis(), REC, \"rec1Desc\", 100);\n        listener.flush(new FlushEvent(System.currentTimeMillis(), Arrays.asList(rec1)));\n        assertEquals(1, listener.getDayRecord().getRecordsCopy().size());\n        final Record rec2 = new Record(System.currentTimeMillis(), REC, \"rec2Desc\", 200);\n        listener.flush(new FlushEvent(System.currentTimeMillis(), Arrays.asList(rec2)));\n        assertEquals(1, listener.getDayRecord().getRecordsCopy().size());\n    }\n}\n"
  },
  {
    "path": "services/bonita-transaction/build.gradle",
    "content": "dependencies {\n    api project(':services:bonita-commons')\n    api libs.jakartaTransactionApi\n\n    annotationProcessor libs.lombok\n    compileOnly libs.lombok\n\n    testImplementation libs.mockitoCore\n    testImplementation libs.assertj\n    testImplementation libs.narayanaJta\n    testImplementation libs.jbossLogging\n    testRuntimeOnly libs.logback\n}\n"
  },
  {
    "path": "services/bonita-transaction/src/main/java/org/bonitasoft/engine/transaction/BonitaTransactionSynchronization.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.transaction;\n\nimport javax.transaction.Synchronization;\n\n@FunctionalInterface\npublic interface BonitaTransactionSynchronization extends Synchronization {\n\n    @Override\n    default void beforeCompletion() {\n    }\n\n    @Override\n    void afterCompletion(int i);\n}\n"
  },
  {
    "path": "services/bonita-transaction/src/main/java/org/bonitasoft/engine/transaction/JTATransactionServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.transaction;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport javax.transaction.*;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.bonitasoft.engine.commons.ExceptionUtils;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException;\nimport org.bonitasoft.engine.mdc.MDCConstants;\nimport org.slf4j.MDC;\n\n@Slf4j\npublic class JTATransactionServiceImpl implements TransactionService {\n\n    private final TransactionManager txManager;\n\n    private final AtomicLong numberOfActiveTransactions = new AtomicLong(0);\n\n    private final ThreadLocal<TransactionServiceContext> txContextThreadLocal;\n    private XAResourceRetriever xaResourceRetriever;\n\n    public JTATransactionServiceImpl(final TransactionManager txManager) {\n        this.txManager = txManager;\n        txContextThreadLocal = new TransactionServiceContextThreadLocal();\n        xaResourceRetriever = new XAResourceRetriever();\n    }\n\n    protected JTATransactionServiceImpl(final TransactionManager txManager,\n            XAResourceRetriever xaResourceRetriever) {\n        this.xaResourceRetriever = xaResourceRetriever;\n        this.txManager = txManager;\n        txContextThreadLocal = new TransactionServiceContextThreadLocal();\n    }\n\n    @Override\n    public void begin() throws STransactionCreationException {\n        final TransactionServiceContext txContext = getTransactionServiceContext();\n        try {\n            int status = txManager.getStatus();\n            checkForNestedBonitaTransaction(txContext, status);\n            clearPreviousTransaction(status);\n            initTxContext(txContext, status);\n            if (txContext.externallyManaged) {\n                //do not open transaction because it was open externally\n                return;\n            }\n            if (log.isTraceEnabled()) {\n                log.trace(\n                        \"Beginning transaction in thread \" + Thread.currentThread().getId() + \" \"\n                                + txManager.getTransaction());\n                txContext.stackTraceThatMadeLastBegin = generateCurrentStack();\n            }\n            txManager.begin();\n            Optional<String> transactionId = getTransactionId(txManager.getTransaction());\n            // we don't use AbstractMDC here, as we have the guarantee that complete() will close the transaction.\n            transactionId.ifPresent(id -> MDC.put(MDCConstants.TRANSACTION_ID, id));\n            handleNumberOfActiveTransactions();\n        } catch (final STransactionCreationException e) {\n            resetTxContext(txContext);\n            throw e;\n        } catch (final Throwable e) {\n            resetTxContext(txContext);\n            throw new STransactionCreationException(e);\n        }\n    }\n\n    private Optional<String> getTransactionId(Transaction transaction) {\n        /*\n         * Transaction is typically an instance of com.arjuna.ats.jta.transaction.Transaction with get_uid method.\n         * Be lenient on implementation class as long as we have the get_uid method...\n         */\n        try {\n            var method = transaction.getClass().getMethod(\"get_uid\");\n            return Optional.of(method.invoke(transaction).toString());\n        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException\n                | InvocationTargetException e) {\n            log.warn(\n                    \"Transaction {} is an instance of {} which does not have an accessible 'get_uid' method like com.arjuna.ats.jta.transaction.Transaction\",\n                    transaction, transaction.getClass().getName());\n            return Optional.empty();\n        }\n    }\n\n    private void clearPreviousTransaction(int status) throws STransactionCreationException {\n        if (status != Status.STATUS_ACTIVE && status != Status.STATUS_NO_TRANSACTION) {\n            //the transaction is in an inconsistent state, we try to rollback\n            log.warn(\n                    \"Starting a new transaction on the thread but there is already a transaction is state \" + status +\n                            \". Will try to call rollback on the transaction manager to cleanup the thread from this transaction.\");\n            try {\n                txManager.rollback();\n            } catch (SystemException e) {\n                throw new STransactionCreationException(\n                        \"A transaction was already associated with the thread. We tried to \" +\n                                \" rollback it but without success. If the transaction manager does not disassociate the thread with the transaction,\"\n                                +\n                                \" restart the server. Status of the transaction was \" + status,\n                        e);\n            }\n        }\n    }\n\n    private void handleNumberOfActiveTransactions() throws RollbackException, SystemException {\n        numberOfActiveTransactions.getAndIncrement();\n        txManager.getTransaction()\n                .registerSynchronization(new DecrementNumberOfActiveTransactionsSynchronization(this));\n    }\n\n    private void initTxContext(TransactionServiceContext txContext, int status) {\n        txContext.externallyManaged = (status == Status.STATUS_ACTIVE);\n        txContext.isInScopeOfBonitaTransaction = true;\n        txContext.beforeCommitCallables.clear();\n    }\n\n    private void checkForNestedBonitaTransaction(TransactionServiceContext txContext, int status)\n            throws STransactionCreationException {\n        if (txContext.isInScopeOfBonitaTransaction) {\n            String message = \"We do not support nested calls to the transaction service. Current state is: \" + status\n                    + \". \";\n            if (log.isTraceEnabled()) {\n                message += \"Last begin made by: \" + txContext.stackTraceThatMadeLastBegin;\n            }\n            throw new STransactionCreationException(message);\n        }\n    }\n\n    private String generateCurrentStack() {\n        StringBuilder sb = new StringBuilder();\n        sb.append(this.getClass().getName()).append(\"\\nNew transaction started by: \");\n        final StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();\n        for (int i = 3; i < stackTraceElements.length; i++) {\n            sb.append(\"\\n        at \");\n            sb.append(stackTraceElements[i]);\n        }\n        return sb.toString();\n    }\n\n    @Override\n    public void complete() throws STransactionCommitException, STransactionRollbackException {\n        // Depending of the txManager status we either commit or rollback.\n        final TransactionServiceContext txContext = getTransactionServiceContext();\n        // Capture TX identity early\n        String txId = null;\n        try {\n            txId = String.valueOf(txManager.getTransaction());\n            log.trace(\"Completing transaction in thread {} (txId={})\", Thread.currentThread().getId(), txId);\n            final int status = txManager.getStatus();\n            if (status == Status.STATUS_NO_TRANSACTION) {\n                throw new STransactionCommitException(\"No transaction started.\");\n            }\n            if (txContext.externallyManaged) {\n                return; // We do not manage the transaction boundaries\n            }\n            if (status == Status.STATUS_MARKED_ROLLBACK) {\n                log.trace(\"Rolling back transaction in thread {} (txId={})\", Thread.currentThread().getId(), txId);\n                txManager.rollback();\n            } else {\n                try {\n                    executeBeforeCommitCallables(txContext);\n                } finally {\n                    //even if there is an issue in before commit callables execution, we always call the commit to ensure end of the tx\n                    txManager.commit();\n                }\n            }\n        } catch (final SystemException | HeuristicMixedException | HeuristicRollbackException | RollbackException e) {\n            log.warn(\"Transaction commit failed in thread {} (txId={}). \"\n                    + \"The 2-phase commit may have completed but a post-commit synchronization threw an exception. \"\n                    + \"Root cause: {}\",\n                    Thread.currentThread().getId(), txId, ExceptionUtils.printRootCauseOnly(e));\n            log.debug(\"Full exception for transaction commit failure:\", e);\n            throw new STransactionCommitException(e);\n        } finally {\n            MDC.remove(MDCConstants.TRANSACTION_ID);\n            resetTxContext(txContext);\n        }\n    }\n\n    TransactionServiceContext getTransactionServiceContext() {\n        return txContextThreadLocal.get();\n    }\n\n    private void executeBeforeCommitCallables(TransactionServiceContext txContext) throws STransactionCommitException {\n        final List<Callable<Void>> callables = txContext.beforeCommitCallables;\n        for (final Callable<Void> callable : callables) {\n            try {\n                callable.call();\n            } catch (Exception e) {\n                throw new STransactionCommitException(\"Exception while executing callable in beforeCommit phase\", e);\n            }\n        }\n    }\n\n    void resetTxContext(TransactionServiceContext txContext) {\n        txContext.isInScopeOfBonitaTransaction = false;\n        txContext.externallyManaged = false;\n        txContext.beforeCommitCallables.clear();\n        txContext.stackTraceThatMadeLastBegin = null;\n    }\n\n    @Override\n    public boolean isTransactionActive() {\n        try {\n            return txManager.getStatus() == Status.STATUS_ACTIVE;\n        } catch (final Throwable e) {\n            log.warn(\n                    \"Unable to check if there is an active transaction {}\", e.getMessage());\n            return false;\n        }\n    }\n\n    @Override\n    public void setRollbackOnly() throws STransactionException {\n        try {\n            txManager.setRollbackOnly();\n        } catch (final IllegalStateException | SystemException e) {\n            throw new STransactionException(e);\n        }\n    }\n\n    @Override\n    public boolean isRollbackOnly() throws STransactionException {\n        try {\n            return txManager.getStatus() == Status.STATUS_MARKED_ROLLBACK;\n        } catch (final SystemException e) {\n            throw new STransactionException(\"Error while trying to get the transaction's status.\", e);\n        }\n    }\n\n    @Override\n    public void registerBonitaSynchronization(final Synchronization txSync)\n            throws STransactionNotFoundException {\n        try {\n            final Transaction transaction = txManager.getTransaction();\n            if (transaction == null) {\n                throw new STransactionNotFoundException(\"No active transaction.\");\n            }\n            transaction.registerSynchronization(txSync);\n        } catch (final IllegalStateException | SystemException | RollbackException e) {\n            throw new STransactionNotFoundException(e);\n        }\n    }\n\n    @Override\n    public void registerBeforeCommitCallable(final Callable<Void> callable) throws STransactionNotFoundException {\n        try {\n            final Transaction transaction = txManager.getTransaction();\n            if (transaction == null) {\n                throw new STransactionNotFoundException(\"No active transaction\");\n            }\n            getTransactionServiceContext().beforeCommitCallables.add(callable);\n        } catch (final IllegalStateException | SystemException e) {\n            throw new STransactionNotFoundException(e.getMessage());\n        }\n    }\n\n    @Override\n    public <T> T executeInTransaction(final Callable<T> callable) throws Exception {\n        begin();\n        try {\n            return callable.call();\n        } catch (final Exception e) {\n            log(callable, e);\n            setRollbackOnly();\n            throw e;\n        } catch (final Throwable t) {\n            log(callable, t);\n            setRollbackOnly();\n            throw new SBonitaRuntimeException(t);\n        } finally {\n            complete();\n        }\n    }\n\n    private <T> void log(Callable<T> callable, Throwable e) {\n        if (log.isDebugEnabled()) {\n            log.debug(\n                    \"Setting rollbackOnly on current transaction because callable '{}' has thrown an exception: {} \" +\n                            \"cause {}\",\n                    callable, e.getMessage(), ExceptionUtils.printLightWeightStacktrace(e));\n        }\n    }\n\n    @Override\n    public long getNumberOfActiveTransactions() {\n        return numberOfActiveTransactions.get();\n    }\n\n    private static class DecrementNumberOfActiveTransactionsSynchronization implements Synchronization {\n\n        private final JTATransactionServiceImpl txService;\n\n        public DecrementNumberOfActiveTransactionsSynchronization(final JTATransactionServiceImpl txService) {\n            this.txService = txService;\n        }\n\n        @Override\n        public void beforeCompletion() {\n            // Nothing to do\n        }\n\n        @Override\n        public void afterCompletion(final int status) {\n            // Whatever the status, decrement the number of active transactions\n            txService.numberOfActiveTransactions.getAndDecrement();\n        }\n    }\n\n    static class TransactionServiceContext {\n\n        /*\n         * this flag means that we already called begin on the bonita transaction service whether or not the transaction\n         * is managed externally\n         */\n        boolean isInScopeOfBonitaTransaction = false;\n        /*\n         * true when the transaction was open outside of bonita\n         */\n        boolean externallyManaged = false;\n\n        /**\n         * We maintain a list of Callables that must be executed just before the real commit (and before the\n         * beforeCompletion method is called), so that we\n         * ensure\n         * that Hibernate has not already flushed its session.\n         */\n        List<Callable<Void>> beforeCommitCallables = new ArrayList<>();\n\n        /**\n         * for tracing only\n         */\n        String stackTraceThatMadeLastBegin;\n    }\n\n    private static class TransactionServiceContextThreadLocal extends ThreadLocal<TransactionServiceContext> {\n\n        @Override\n        protected TransactionServiceContext initialValue() {\n            return new TransactionServiceContext();\n        }\n    }\n\n    @Override\n    public Optional<Boolean> hasMultipleResources() {\n        try {\n            return xaResourceRetriever.retrieveResources(txManager.getTransaction()).map(List::size)\n                    .map(size -> size > 1);\n        } catch (SystemException e) {\n            throw new RuntimeException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "services/bonita-transaction/src/main/java/org/bonitasoft/engine/transaction/SBadTransactionStateException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.transaction;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\npublic class SBadTransactionStateException extends SBonitaException {\n\n    private static final long serialVersionUID = 1L;\n\n    public SBadTransactionStateException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-transaction/src/main/java/org/bonitasoft/engine/transaction/STransactionCommitException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.transaction;\n\nimport java.util.List;\n\npublic class STransactionCommitException extends STransactionException {\n\n    private static final long serialVersionUID = 8650941405945187337L;\n\n    public STransactionCommitException(final String message, final List<Throwable> commitExceptions) {\n        super(message, commitExceptions);\n    }\n\n    public STransactionCommitException(final String message, final Throwable throwable) {\n        super(message, throwable);\n    }\n\n    public STransactionCommitException(final Throwable cause) {\n        super(cause);\n    }\n\n    public STransactionCommitException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "services/bonita-transaction/src/main/java/org/bonitasoft/engine/transaction/STransactionCreationException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.transaction;\n\npublic class STransactionCreationException extends STransactionException {\n\n    private static final long serialVersionUID = 1L;\n\n    public STransactionCreationException(final Throwable t) {\n        super(t);\n    }\n\n    public STransactionCreationException(final String message) {\n        super(message);\n    }\n\n    public STransactionCreationException(String message, Exception exception) {\n        super(message, exception);\n    }\n}\n"
  },
  {
    "path": "services/bonita-transaction/src/main/java/org/bonitasoft/engine/transaction/STransactionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.transaction;\n\nimport java.io.PrintStream;\nimport java.io.PrintWriter;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\npublic class STransactionException extends SBonitaException {\n\n    private static final long serialVersionUID = 8650941405945187337L;\n\n    private final List<Throwable> exceptions;\n\n    public STransactionException(final String message, final List<Throwable> commitExceptions) {\n        super(message);\n        exceptions = Collections.unmodifiableList(commitExceptions);\n    }\n\n    public STransactionException(final String message, final Throwable throwable) {\n        super(message, throwable);\n        exceptions = Collections.<Throwable> emptyList();\n    }\n\n    public STransactionException(final Throwable throwable) {\n        super(throwable);\n        exceptions = Collections.<Throwable> emptyList();\n    }\n\n    public STransactionException(final String message) {\n        super(message);\n        exceptions = Collections.<Throwable> emptyList();\n    }\n\n    public List<Throwable> getCommitExceptions() {\n        return exceptions;\n    }\n\n    @Override\n    public void printStackTrace() {\n        super.printStackTrace();\n        for (final Throwable throwable : exceptions) {\n            throwable.printStackTrace();\n        }\n    }\n\n    @Override\n    public void printStackTrace(final PrintStream s) {\n        super.printStackTrace(s);\n        for (final Throwable throwable : exceptions) {\n            throwable.printStackTrace(s);\n        }\n    }\n\n    @Override\n    public void printStackTrace(final PrintWriter s) {\n        super.printStackTrace(s);\n        for (final Throwable throwable : exceptions) {\n            throwable.printStackTrace(s);\n        }\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see java.lang.Throwable#getCause()\n     */\n    @Override\n    public Throwable getCause() {\n        Throwable cause = super.getCause();\n        if (cause == null && !exceptions.isEmpty()) {\n            cause = exceptions.get(0);\n        }\n        return cause;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-transaction/src/main/java/org/bonitasoft/engine/transaction/STransactionNotFoundException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.transaction;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Charles Souillard\n * @author Celine Souchet\n */\npublic class STransactionNotFoundException extends SBonitaException {\n\n    private static final long serialVersionUID = -8750663884514717732L;\n\n    private static final String DEFAULT_MESSAGE = \"No transaction found.\";\n\n    public STransactionNotFoundException() {\n        super(DEFAULT_MESSAGE);\n    }\n\n    public STransactionNotFoundException(final String message) {\n        super(message);\n    }\n\n    public STransactionNotFoundException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-transaction/src/main/java/org/bonitasoft/engine/transaction/STransactionResourceException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.transaction;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\npublic class STransactionResourceException extends SBonitaException {\n\n    private static final long serialVersionUID = 2652979339415320810L;\n\n    public STransactionResourceException(final String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-transaction/src/main/java/org/bonitasoft/engine/transaction/STransactionRollbackException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.transaction;\n\nimport java.util.List;\n\npublic class STransactionRollbackException extends STransactionException {\n\n    private static final long serialVersionUID = 1L;\n\n    public STransactionRollbackException(final String message, final List<Throwable> commitExceptions) {\n        super(message, commitExceptions);\n    }\n\n    public STransactionRollbackException(final String message, final Throwable throwable) {\n        super(message, throwable);\n    }\n\n    public STransactionRollbackException(final Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-transaction/src/main/java/org/bonitasoft/engine/transaction/TransactionService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.transaction;\n\n/**\n * @author Matthieu Chaffotte\n * @author Laurent Vaills\n */\npublic interface TransactionService extends UserTransactionService {\n\n    /**\n     * Create a new transaction and associate it with the current thread.\n     *\n     * @throws STransactionCreationException\n     */\n    void begin() throws STransactionCreationException;\n\n    /**\n     * Complete the transaction : either commit or rollback.\n     *\n     * @throws STransactionCommitException\n     * @throws STransactionRollbackException\n     */\n    void complete() throws STransactionCommitException, STransactionRollbackException;\n\n    /**\n     * Modify the transaction associated with the current thread such that\n     * the only possible outcome of the transaction is to roll back the\n     * transaction.\n     *\n     * @throws IllegalStateException Thrown if the current thread is\n     *         not associated with a transaction.\n     * @throws STransactionException Thrown if the transaction manager\n     *         encounters an unexpected error condition.\n     */\n    void setRollbackOnly() throws STransactionException;\n\n    boolean isRollbackOnly() throws STransactionException;\n\n    /**\n     * Get the number of active transactions (i.e. transactions that opened but not yet completed or rolledback).\n     * A transaction that was just mark as \"rollbackOnly\" is considered as an active one.\n     *\n     * @return the number of active transactions\n     */\n    long getNumberOfActiveTransactions();\n\n}\n"
  },
  {
    "path": "services/bonita-transaction/src/main/java/org/bonitasoft/engine/transaction/UserTransactionService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.transaction;\n\nimport java.util.Optional;\nimport java.util.concurrent.Callable;\n\nimport javax.transaction.Synchronization;\n\npublic interface UserTransactionService {\n\n    /**\n     * Execute the given callable inside a transaction.\n     *\n     * @param callable\n     * @return\n     * @throws Exception\n     */\n    <T> T executeInTransaction(Callable<T> callable) throws Exception;\n\n    /**\n     * Register a synchronization object for the transaction currently\n     * associated with the target object. The transaction manager invokes\n     * the beforeCompletion method prior to starting the two-phase transaction\n     * commit process. After the transaction is completed, the transaction\n     * manager invokes the afterCompletion method.\n     *\n     * @param txSync\n     *        The Synchronization object for the transaction associated\n     *        with the target object.\n     * @exception RollbackException\n     *            Thrown to indicate that\n     *            the transaction has been marked for rollback only.\n     * @exception IllegalStateException\n     *            Thrown if the transaction in the\n     *            target object is in the prepared state or the transaction is\n     *            inactive.\n     * @exception STransactionNotFoundException\n     *            Thrown if the transaction manager\n     *            encounters an unexpected error condition.\n     */\n    void registerBonitaSynchronization(Synchronization txSync) throws STransactionNotFoundException;\n\n    void registerBeforeCommitCallable(Callable<Void> callable) throws STransactionNotFoundException;\n\n    boolean isTransactionActive();\n\n    Optional<Boolean> hasMultipleResources();\n\n}\n"
  },
  {
    "path": "services/bonita-transaction/src/main/java/org/bonitasoft/engine/transaction/XAResourceRetriever.java",
    "content": "/**\n * Copyright (C) 2021 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.transaction;\n\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport javax.transaction.Transaction;\nimport javax.transaction.xa.XAResource;\n\nimport lombok.extern.slf4j.Slf4j;\n\n@Slf4j\npublic class XAResourceRetriever {\n\n    private final Map<String, Optional<Method>> cache = new HashMap<>();\n\n    public Optional<List<XAResource>> retrieveResources(Transaction transaction) {\n        return cache.computeIfAbsent(transaction.getClass().getName(), key -> getMethodByReflection(transaction))\n                .map(method -> getResources(transaction, method))\n                .filter(s -> !s.isEmpty())\n                .map(ArrayList::new);\n    }\n\n    private Optional<Method> getMethodByReflection(Transaction transaction) {\n        try {\n            return Optional.of(transaction.getClass().getMethod(\"getResources\"));\n        } catch (Exception e) {\n            log.warn(\"Unable to find method to retrieve resources attached to the current transaction, \" +\n                    \"we will not try to reattempt to find the method until next restart of the server\", e);\n            return Optional.empty();\n        }\n    }\n\n    protected Set<XAResource> getResources(Transaction transaction, Method getResources) {\n        try {\n            return ((Map) getResources.invoke(transaction)).keySet();\n        } catch (Exception e) {\n            log.warn(\"Unable to retrieve resources attached to the current transaction, \" +\n                    \"we will not try to reattempt to retrieve those resources until next restart of the server\", e);\n            cache.put(transaction.getClass().getName(), Optional.empty());\n            return Collections.emptySet();\n\n        }\n\n    }\n}\n"
  },
  {
    "path": "services/bonita-transaction/src/test/java/org/bonitasoft/engine/transaction/JTATransactionServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.transaction;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.fail;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Optional;\nimport java.util.concurrent.Callable;\n\nimport javax.transaction.Status;\nimport javax.transaction.Synchronization;\nimport javax.transaction.SystemException;\nimport javax.transaction.Transaction;\nimport javax.transaction.TransactionManager;\nimport javax.transaction.xa.XAResource;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InOrder;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class JTATransactionServiceImplTest {\n\n    @Mock\n    TransactionManager txManager;\n    @Mock\n    Transaction transaction;\n    @Mock\n    XAResourceRetriever xaResourceRetriever;\n    @InjectMocks\n    private JTATransactionServiceImpl txService;\n    @Mock\n    private Callable<Object> txContent;\n    @Mock\n    private Callable<Void> beforeCommitCallable;\n\n    @Before\n    public void before() throws Exception {\n        doReturn(transaction).when(txManager).getTransaction();\n    }\n\n    @Test\n    public void beginTransaction() throws Exception {\n        when(txManager.getStatus()).thenReturn(Status.STATUS_NO_TRANSACTION).thenReturn(Status.STATUS_ACTIVE);\n        when(txManager.getTransaction()).thenReturn(mock(Transaction.class));\n\n        txService.begin();\n        verify(txManager, times(1)).begin();\n        assertEquals(1, txService.getNumberOfActiveTransactions());\n    }\n\n    @Test(expected = STransactionCreationException.class)\n    public void should_throw_exception_if_call_is_nested() throws Exception {\n        when(txManager.getStatus()).thenReturn(Status.STATUS_ACTIVE);\n\n        JTATransactionServiceImpl txService = new JTATransactionServiceImpl(txManager);\n\n        txService.begin();\n        txService.begin();\n    }\n\n    @Test\n    public void should_fail_if_begin_fails() throws Exception {\n        when(txManager.getStatus()).thenReturn(Status.STATUS_NO_TRANSACTION);\n        doThrow(new SystemException(\"Mocked\")).when(txManager).begin();\n\n        try {\n            txService.begin();\n            fail(\"Thanks to the mock an exception must have been thrown\");\n        } catch (STransactionCreationException e) {\n            verify(txManager, times(1)).begin();\n            assertEquals(0, txService.getNumberOfActiveTransactions());\n        }\n    }\n\n    @Test\n    public void should_increment_tx_counter_when_begin() throws Exception {\n        doReturn(Status.STATUS_NO_TRANSACTION).when(txManager).getStatus();\n\n        txService.begin();\n\n        assertThat(txService.getNumberOfActiveTransactions()).isEqualTo(1);\n    }\n\n    @Test\n    public void setRollbackOnly_call_setRollbackOnly_on_txManager() throws Exception {\n        txService.setRollbackOnly();\n        verify(txManager).setRollbackOnly();\n    }\n\n    @Test\n    public void should_executeInTransaction_with_fail_on_execute_do_not_make_other_tx_to_fail() throws Exception {\n        when(txManager.getStatus()).thenReturn(Status.STATUS_NO_TRANSACTION).thenReturn(Status.STATUS_ACTIVE);\n        when(txManager.getTransaction()).thenReturn(mock(Transaction.class));\n\n        JTATransactionServiceImpl txService = spy(new JTATransactionServiceImpl(txManager));\n        Callable<?> callable = mock(Callable.class);\n\n        txService.executeInTransaction(callable);\n\n        verify(txManager).begin();\n        verify(callable).call();\n        verify(txManager).commit();\n    }\n\n    /**\n     * The method call has to be executed between a transaction.\n     *\n     * @throws Exception\n     */\n    @Test\n    public void testExecuteInTransactionWithRollback() throws Exception {\n        // First to allow to start the transaction, then to force to call rollback\n        when(txManager.getStatus()).thenReturn(Status.STATUS_NO_TRANSACTION).thenReturn(Status.STATUS_MARKED_ROLLBACK);\n        when(txManager.getTransaction()).thenReturn(mock(Transaction.class));\n\n        Callable<?> callable = mock(Callable.class);\n        when(callable.call()).thenThrow(new Exception(\"Mocked exception\"));\n\n        try {\n            txService.executeInTransaction(callable);\n            fail(\"An exception should have been thrown.\");\n        } catch (Exception e) {\n        }\n        verify(txManager).begin();\n        verify(callable).call();\n        verify(txManager).setRollbackOnly();\n        verify(txManager).rollback();\n    }\n\n    @Test\n    public void should_not_register_synchronization_to_decrement_numberOfActiveTransaction_when_tx_managed_externally()\n            throws Exception {\n        doReturn(Status.STATUS_ACTIVE).when(txManager).getStatus();\n\n        txService.begin();\n\n        //we do not register synchro on number of transaction when managed externally because we cannot decrement correctly the counter at the end of the bonita tx\n        verify(transaction, never()).registerSynchronization(any(Synchronization.class));\n    }\n\n    @Test\n    public void should_not_increment_tx_counter_when_managed_externally() throws Exception {\n        doReturn(Status.STATUS_ACTIVE).when(txManager).getStatus();\n\n        txService.begin();\n\n        //we do not register synchro on number of transaction when managed externally because we cannot decrement correctly the counter at the end of the bonita tx\n        assertThat(txService.getNumberOfActiveTransactions()).isEqualTo(0);\n    }\n\n    @Test\n    public void should_register_synchronization_to_decrement_numberOfActiveTransaction() throws Exception {\n        doReturn(Status.STATUS_NO_TRANSACTION).when(txManager).getStatus();\n\n        txService.begin();\n\n        verify(transaction).registerSynchronization(any(Synchronization.class));\n    }\n\n    @Test\n    public void should_call_beforeCommitCallable_on_complete() throws Exception {\n        doReturn(Status.STATUS_ACTIVE).when(txManager).getStatus();\n        txService.registerBeforeCommitCallable(beforeCommitCallable);\n\n        txService.complete();\n\n        verify(beforeCommitCallable).call();\n    }\n\n    @Test\n    public void should_not_call_beforeCommitCallable_when_transaction_is_rollbacked() throws Exception {\n        doReturn(Status.STATUS_MARKED_ROLLBACK).when(txManager).getStatus();\n        txService.registerBeforeCommitCallable(beforeCommitCallable);\n\n        txService.complete();\n\n        verify(beforeCommitCallable, never()).call();\n    }\n\n    @Test\n    public void should_reset_transaction_context_when_error_on_commit() throws Exception {\n        doReturn(Status.STATUS_NO_TRANSACTION).doReturn(Status.STATUS_ACTIVE).when(txManager).getStatus();\n        doThrow(SystemException.class).when(txManager).commit();\n\n        try {\n            txService.begin();\n            txService.complete();\n            fail();\n        } catch (STransactionCommitException ignored) {\n        }\n        assertThat(txService.getTransactionServiceContext().isInScopeOfBonitaTransaction).isFalse();\n    }\n\n    @Test\n    public void should_reset_transaction_context_when_error_on_begin() throws Exception {\n        doReturn(Status.STATUS_NO_TRANSACTION).doReturn(Status.STATUS_ACTIVE).when(txManager).getStatus();\n        doThrow(SystemException.class).when(txManager).begin();\n\n        try {\n            txService.begin();\n            fail();\n        } catch (STransactionCreationException ignored) {\n        }\n        assertThat(txService.getTransactionServiceContext().isInScopeOfBonitaTransaction).isFalse();\n    }\n\n    @Test\n    public void should_commit_even_when_exception_on_commit_callable() throws Exception {\n        //given\n        doReturn(Status.STATUS_ACTIVE).when(txManager).getStatus();\n        txService.registerBeforeCommitCallable(beforeCommitCallable);\n        doThrow(Exception.class).when(beforeCommitCallable).call();\n        //when\n        try {\n            txService.complete();\n            fail();\n        } catch (STransactionCommitException ignored) {\n        }\n        //then\n        verify(txManager).commit();\n    }\n\n    @Test\n    public void should_not_call_begin_if_transaction_is_externally_managed() throws Exception {\n        //given\n        doReturn(Status.STATUS_ACTIVE).when(txManager).getStatus();\n        //when\n        txService.begin();\n        //then\n        verify(txManager, never()).begin();\n    }\n\n    @Test\n    public void should_call_begin_when_tx_manager_do_not_have_transaction() throws Exception {\n        //given\n        doReturn(Status.STATUS_NO_TRANSACTION).when(txManager).getStatus();\n        //when\n        txService.begin();\n        //then\n        verify(txManager).begin();\n    }\n\n    @Test\n    public void should_not_call_commit_if_transaction_is_externally_managed() throws Exception {\n        //given\n        doReturn(Status.STATUS_ACTIVE).when(txManager).getStatus();\n        txService.getTransactionServiceContext().externallyManaged = true;\n        //when\n        txService.complete();\n        //then\n        verify(txManager, never()).commit();\n    }\n\n    @Test\n    public void should_call_commit_on_transaction_manager() throws Exception {\n        //given\n        doReturn(Status.STATUS_ACTIVE).when(txManager).getStatus();\n        //when\n        txService.complete();\n        //then\n        verify(txManager).commit();\n    }\n\n    @Test\n    public void should_call_rollback_when_status_is_rollback_only() throws Exception {\n        //given\n        doReturn(Status.STATUS_MARKED_ROLLBACK).when(txManager).getStatus();\n        //when\n        txService.complete();\n        //then\n        verify(txManager).rollback();\n    }\n\n    @Test(expected = STransactionCommitException.class)\n    public void should_throw_exception_when_complete_on_no_transaction() throws Exception {\n        doReturn(Status.STATUS_NO_TRANSACTION).when(txManager).getStatus();\n\n        txService.complete();\n    }\n\n    @Test\n    public void should_begin_execute_callable_and_complete() throws Exception {\n        //given\n        doReturn(Status.STATUS_NO_TRANSACTION).doReturn(Status.STATUS_ACTIVE).when(txManager).getStatus();\n        //when\n        txService.executeInTransaction(txContent);\n        //then\n        InOrder inOrder = inOrder(txManager, txContent);\n        inOrder.verify(txManager).begin();\n        inOrder.verify(txContent).call();\n        inOrder.verify(txManager).commit();\n    }\n\n    @Test\n    public void should_trace_call_when_TRACE_active() throws Exception {\n        doReturn(Status.STATUS_NO_TRANSACTION).doReturn(Status.STATUS_ACTIVE).when(txManager).getStatus();\n        TransactionService txService = new JTATransactionServiceImpl(txManager);\n\n        in_an_other_method_to_verify_the_stacktrace_contains_this_method_name(txService);\n\n        try {\n            txService.begin();\n            fail();\n        } catch (STransactionCreationException e) {\n            assertThat(e.getMessage())\n                    .contains(\"in_an_other_method_to_verify_the_stacktrace_contains_this_method_name\");\n        }\n    }\n\n    private void in_an_other_method_to_verify_the_stacktrace_contains_this_method_name(TransactionService txService)\n            throws STransactionCreationException {\n        txService.begin();\n    }\n\n    @Test\n    public void isTransactionActive_should_return_true_when_there_is_an_active_transaction() throws Exception {\n        doReturn(Status.STATUS_ACTIVE).when(txManager).getStatus();\n\n        boolean active = txService.isTransactionActive();\n\n        assertThat(active).isTrue();\n    }\n\n    @Test\n    public void isTransactionActive_should_return_false_when_transaction_manager_throws_exception() throws Exception {\n        doThrow(SystemException.class).when(txManager).getStatus();\n\n        boolean active = txService.isTransactionActive();\n\n        assertThat(active).isFalse();\n    }\n\n    @Test\n    public void isTransactionActive_should_return_true_when_there_is_no_active_transaction() throws Exception {\n        doReturn(Status.STATUS_UNKNOWN).when(txManager).getStatus();\n\n        boolean active = txService.isTransactionActive();\n\n        assertThat(active).isFalse();\n    }\n\n    @Test\n    public void hasMultipleResources_should_have_no_value_when_xaResourceRetriever_gives_no_resources()\n            throws Exception {\n        when(xaResourceRetriever.retrieveResources(any())).thenReturn(Optional.empty());\n\n        Optional<Boolean> hasMultipleResources = txService.hasMultipleResources();\n\n        assertThat(hasMultipleResources).isEmpty();\n    }\n\n    @Test\n    public void hasMultipleResources_should_return_false_when_transaction_has_one_resource() {\n        doReturn(Optional.of(Collections.singletonList(mock(XAResource.class)))).when(xaResourceRetriever)\n                .retrieveResources(any());\n\n        Optional<Boolean> hasMultipleResources = txService.hasMultipleResources();\n\n        assertThat(hasMultipleResources.get()).isFalse();\n    }\n\n    @Test\n    public void hasMultipleResources_should_return_true_when_transaction_has_multiple_resource() {\n        doReturn(Optional.of(Arrays.asList(mock(XAResource.class), mock(XAResource.class)))).when(xaResourceRetriever)\n                .retrieveResources(any());\n\n        Optional<Boolean> hasMultipleResources = txService.hasMultipleResources();\n\n        assertThat(hasMultipleResources.get()).isTrue();\n    }\n\n    @Test(expected = RuntimeException.class)\n    public void hasMultipleResources_should_throw_runtime_exception_when_getTransaction_throws_SystemException()\n            throws SystemException {\n        doThrow(SystemException.class).when(txManager).getTransaction();\n\n        txService.hasMultipleResources();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-transaction/src/test/java/org/bonitasoft/engine/transaction/TransactionLifeCycleTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.transaction;\n\nimport static javax.transaction.Status.STATUS_COMMITTED;\nimport static javax.transaction.Status.STATUS_ROLLEDBACK;\nimport static org.junit.Assert.*;\n\nimport javax.transaction.Status;\nimport javax.transaction.TransactionManager;\n\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\n\npublic class TransactionLifeCycleTest {\n\n    private static TransactionManager transactionManager;\n    private TransactionService txService;\n\n    @BeforeClass\n    public static void setupTransactionManager() {\n        transactionManager = com.arjuna.ats.jta.TransactionManager.transactionManager();\n    }\n\n    @Before\n    public void before() {\n        txService = new JTATransactionServiceImpl(transactionManager);\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    @After\n    public void closeTransactions() throws Exception {\n        // Do not forget to close the transaction\n        if (txService.isTransactionActive()) {\n            txService.complete();\n        }\n    }\n\n    @Test\n    public void testActiveTransactionState() throws Exception {\n        txService.begin();\n        assertEquals(Status.STATUS_ACTIVE, transactionManager.getStatus());\n    }\n\n    @Test\n    public void testCommittedTransactionState() throws Exception {\n        TxSync sync = new TxSync();\n        txService.begin();\n        txService.registerBonitaSynchronization(sync);\n        txService.complete();\n\n        assertEquals(STATUS_COMMITTED, sync.getTxCompletionState());\n        assertEquals(Status.STATUS_NO_TRANSACTION, transactionManager.getStatus());\n    }\n\n    @Test\n    public void testRollbackOnlyTransactionState() throws Exception {\n        txService.begin();\n        txService.setRollbackOnly();\n\n        assertEquals(Status.STATUS_MARKED_ROLLBACK, transactionManager.getStatus());\n        txService.complete();\n    }\n\n    @Test\n    public void testRolledbackTransactionState() throws Exception {\n        TxSync sync = new TxSync();\n        txService.begin();\n        txService.registerBonitaSynchronization(sync);\n        txService.setRollbackOnly();\n        txService.complete();\n\n        assertEquals(STATUS_ROLLEDBACK, sync.getTxCompletionState());\n        assertEquals(Status.STATUS_NO_TRANSACTION, transactionManager.getStatus());\n    }\n\n    @Test\n    public void testGetState1() throws Exception {\n        TxSync sync = new TxSync();\n        txService.begin();\n        txService.registerBonitaSynchronization(sync);\n        assertEquals(Status.STATUS_ACTIVE, transactionManager.getStatus());\n\n        txService.setRollbackOnly();\n        assertEquals(Status.STATUS_MARKED_ROLLBACK, transactionManager.getStatus());\n\n        txService.complete();\n        assertEquals(STATUS_ROLLEDBACK, sync.getTxCompletionState());\n        assertEquals(Status.STATUS_NO_TRANSACTION, transactionManager.getStatus());\n    }\n\n    @Test\n    public void testGetState2() throws Exception {\n        TxSync sync = new TxSync();\n        txService.begin();\n        txService.registerBonitaSynchronization(sync);\n        assertEquals(Status.STATUS_ACTIVE, transactionManager.getStatus());\n\n        txService.complete();\n        assertEquals(STATUS_COMMITTED, sync.getTxCompletionState());\n        assertEquals(Status.STATUS_NO_TRANSACTION, transactionManager.getStatus());\n    }\n\n    @Test\n    public void testTransactionIsRollbackOnly() throws Exception {\n        txService.begin();\n        txService.setRollbackOnly();\n        assertTrue(txService.isRollbackOnly());\n        txService.complete();\n    }\n\n    @Test\n    public void testSetRollbackOnlyOnActiveTx() throws Exception {\n        txService.begin();\n        txService.setRollbackOnly();\n        assertEquals(Status.STATUS_MARKED_ROLLBACK, transactionManager.getStatus());\n        txService.complete();\n    }\n\n    @Test\n    public void testSetRollbackOnlyOnCommitedTx() throws Exception {\n        TxSync sync = new TxSync();\n        txService.begin();\n        txService.registerBonitaSynchronization(sync);\n        txService.complete();\n        try {\n            txService.setRollbackOnly();\n            fail(\"Impossible to call setRollbackOnly on a tx with state COMMITTED\");\n        } catch (final STransactionException e) {\n            assertEquals(STATUS_COMMITTED, sync.getTxCompletionState());\n            assertEquals(Status.STATUS_NO_TRANSACTION, transactionManager.getStatus());\n        }\n    }\n\n    @Test\n    public void testBeginActiveTx() throws Exception {\n        txService.begin();\n        try {\n            txService.begin();\n            fail(\"Impossible to begin a tx with state ACTIVE\");\n        } catch (final STransactionCreationException e) {\n            assertEquals(Status.STATUS_ACTIVE, transactionManager.getStatus());\n        }\n    }\n\n    @Test\n    public void testBeginRollbackOnlyTx() throws Exception {\n        txService.begin();\n        txService.setRollbackOnly();\n        try {\n            txService.begin();\n            System.out.println(\"++++\" + txService.getNumberOfActiveTransactions());\n            fail(\"Impossible to begin a tx with state ROLLBACKONLY\");\n        } catch (final STransactionCreationException e) {\n            assertEquals(Status.STATUS_MARKED_ROLLBACK, transactionManager.getStatus());\n        } finally {\n            txService.complete();\n        }\n    }\n\n    // This is a dumb implementation of the BonitaTransactionSynchronization interface just to\n    // keep a reference to transaction completion's state.\n    private static class TxSync implements BonitaTransactionSynchronization {\n\n        private int txCompletionState;\n\n        public TxSync() {\n        }\n\n        public int getTxCompletionState() {\n            return txCompletionState;\n        }\n\n        @Override\n        public void afterCompletion(final int txState) {\n            this.txCompletionState = txState;\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-transaction/src/test/java/org/bonitasoft/engine/transaction/TransactionServiceTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.transaction;\n\nimport static org.junit.Assert.assertEquals;\n\nimport java.util.concurrent.CountDownLatch;\n\nimport javax.transaction.TransactionManager;\n\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\n\npublic class TransactionServiceTest {\n\n    private static TransactionManager transactionManager;\n    private TransactionService txService;\n\n    @BeforeClass\n    public static void setupTransactionManager() {\n        transactionManager = com.arjuna.ats.jta.TransactionManager.transactionManager();\n    }\n\n    @Before\n    public void before() {\n        txService = new JTATransactionServiceImpl(transactionManager);\n    }\n\n    @After\n    public void afterTest() {\n        try {\n            txService.complete();\n        } catch (final Exception e) {\n            // Do nothing\n        }\n    }\n\n    @Test(expected = STransactionCreationException.class)\n    public void testCantCreateATransactionWithActiveTx() throws Exception {\n        txService.begin();\n        txService.begin();\n    }\n\n    @Test(expected = STransactionCreationException.class)\n    public void testCantCreateATransactionWithCreatedTx() throws Exception {\n        txService.begin();\n        txService.begin();\n    }\n\n    @Test(expected = STransactionCreationException.class)\n    public void testCantCreateATransactionWithRollbackOnlyTx() throws Exception {\n        txService.begin();\n        txService.setRollbackOnly();\n\n        try {\n            txService.begin();\n        } finally {\n            txService.complete();\n        }\n    }\n\n    @Test\n    public void getNumberOfActiveTransactionsWithTransactionOpen() throws Exception {\n        txService.begin();\n        assertEquals(1, txService.getNumberOfActiveTransactions());\n        txService.complete();\n        assertEquals(0, txService.getNumberOfActiveTransactions());\n    }\n\n    @Test\n    public void getNumberOfActiveTransactionsWithTransactionMarkedAsRollback() throws Exception {\n        txService.begin();\n        assertEquals(1, txService.getNumberOfActiveTransactions());\n        txService.setRollbackOnly();\n        assertEquals(1, txService.getNumberOfActiveTransactions());\n        txService.complete();\n        assertEquals(0, txService.getNumberOfActiveTransactions());\n    }\n\n    @Test\n    public void getNumberOfActiveTransactionsWith2TransactionsOpen() throws Exception {\n        final CountDownLatch lock1 = new CountDownLatch(1);\n        final CountDownLatch lock2 = new CountDownLatch(1);\n\n        final CountDownLatch startSignal = new CountDownLatch(2);\n\n        final TransactionWorker txWorker1 = new TransactionWorker(lock1, txService, startSignal);\n        final TransactionWorker txWorker2 = new TransactionWorker(lock2, txService, startSignal);\n\n        final Thread t1 = new Thread(txWorker1);\n        t1.setName(\"txWorker1\");\n        final Thread t2 = new Thread(txWorker2);\n        t2.setName(\"txWorker2\");\n\n        t1.start();\n        t2.start();\n        startSignal.await();\n        assertEquals(2, txService.getNumberOfActiveTransactions());\n\n        lock1.countDown();\n        t1.join();\n        assertEquals(1, txService.getNumberOfActiveTransactions());\n\n        lock2.countDown();\n        t2.join();\n        assertEquals(0, txService.getNumberOfActiveTransactions());\n    }\n\n    private static class TransactionWorker implements Runnable {\n\n        private final CountDownLatch lock;\n\n        private final TransactionService transactionService;\n\n        private final CountDownLatch startSignal;\n\n        TransactionWorker(final CountDownLatch lock, final TransactionService transactionService,\n                final CountDownLatch startSignal) {\n            this.lock = lock;\n            this.transactionService = transactionService;\n            this.startSignal = startSignal;\n        }\n\n        @Override\n        public void run() {\n            try {\n                transactionService.begin();\n                // Signal the caller that we are have started and the transaction has begun\n                startSignal.countDown();\n                // Wait until the caller tells us to\n                lock.await();\n            } catch (final Exception e) {\n                throw new RuntimeException(e);\n            } finally {\n                try {\n                    transactionService.complete();\n                } catch (final STransactionCommitException e) {\n                    e.printStackTrace();\n                } catch (final STransactionRollbackException e) {\n                    e.printStackTrace();\n                }\n            }\n\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-transaction/src/test/java/org/bonitasoft/engine/transaction/TransactionSynchronizationTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.transaction;\n\nimport static org.junit.Assert.*;\n\nimport javax.transaction.Status;\nimport javax.transaction.TransactionManager;\n\nimport org.bonitasoft.engine.transaction.synchronization.SimpleSynchronization;\nimport org.bonitasoft.engine.transaction.synchronization.StaticSynchronization;\nimport org.bonitasoft.engine.transaction.synchronization.StaticSynchronizationResult;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\n\npublic class TransactionSynchronizationTest {\n\n    private static TransactionManager transactionManager;\n    private TransactionService txService;\n\n    @BeforeClass\n    public static void setupTransactionManager() {\n        transactionManager = com.arjuna.ats.jta.TransactionManager.transactionManager();\n    }\n\n    @Before\n    public void before() {\n        txService = new JTATransactionServiceImpl(transactionManager);\n    }\n\n    @After\n    public void closeTransactions() throws Exception {\n        if (txService.isTransactionActive()) {\n            txService.complete();\n        }\n    }\n\n    @Test\n    public void testSimpleRegisterSynchronization() throws Exception {\n        txService.begin();\n\n        final SimpleSynchronization simpleSynchronization = new SimpleSynchronization();\n\n        txService.registerBonitaSynchronization(simpleSynchronization);\n\n        assertFalse(simpleSynchronization.isBeforeCompletion());\n        assertFalse(simpleSynchronization.isAfterCompletion());\n        assertEquals(Status.STATUS_NO_TRANSACTION, simpleSynchronization.getAfterCompletionStatus());\n\n    }\n\n    private void testSynchronizationStatus(final boolean rollback, final int expectedStatus)\n            throws Exception {\n        txService.begin();\n\n        final SimpleSynchronization[] synchronizations = new SimpleSynchronization[] { new SimpleSynchronization(),\n                new SimpleSynchronization() };\n\n        for (final SimpleSynchronization sync : synchronizations) {\n            txService.registerBonitaSynchronization(sync);\n        }\n\n        for (final SimpleSynchronization sync : synchronizations) {\n            assertFalse(sync.isBeforeCompletion());\n            assertFalse(sync.isAfterCompletion());\n            assertEquals(Status.STATUS_NO_TRANSACTION, sync.getAfterCompletionStatus());\n        }\n\n        if (rollback) {\n            txService.setRollbackOnly();\n        }\n        txService.complete();\n\n        for (final SimpleSynchronization sync : synchronizations) {\n            assertEquals(!rollback, sync.isBeforeCompletion());\n            assertTrue(sync.isAfterCompletion());\n            assertEquals(expectedStatus, sync.getAfterCompletionStatus());\n        }\n    }\n\n    @Test\n    public void testSynchronizationStatusOnCommit() throws Exception {\n        testSynchronizationStatus(false, Status.STATUS_COMMITTED);\n    }\n\n    @Test\n    public void testSynchronizationStatusOnRollback() throws Exception {\n        testSynchronizationStatus(true, Status.STATUS_ROLLEDBACK);\n    }\n\n    private void testRegisteredSynchronizationsOrder(final boolean rollback,\n            final StaticSynchronization... synchronizations) throws Exception {\n        // in fact the same on commit or rollback... Synchronizations are always called\n        txService.begin();\n\n        StaticSynchronizationResult.reset();\n\n        final StringBuilder beforeBuilder = new StringBuilder();\n        final StringBuilder afterBuilder = new StringBuilder();\n        for (final StaticSynchronization synchronization : synchronizations) {\n            txService.registerBonitaSynchronization(synchronization);\n\n            if (!rollback) {\n                beforeBuilder.append(synchronization.getBeforeCompletionComment());\n            }\n            afterBuilder.append(synchronization.getAfterCompletionComment());\n        }\n\n        final String expected = beforeBuilder.toString() + afterBuilder.toString();\n\n        if (rollback) {\n            txService.setRollbackOnly();\n        }\n        txService.complete();\n\n        assertEquals(expected, StaticSynchronizationResult.COMMENT);\n    }\n\n    private void testRegisteredSynchronizationsOrderOnFailure(final boolean rollback, final boolean failOnBefore,\n            final boolean failOnAfter) throws Exception {\n        testRegisteredSynchronizationsOrder(rollback, new StaticSynchronization(1, failOnBefore, failOnAfter),\n                new StaticSynchronization(2),\n                new StaticSynchronization(3));\n\n        testRegisteredSynchronizationsOrder(rollback, new StaticSynchronization(1), new StaticSynchronization(2,\n                failOnBefore, failOnAfter),\n                new StaticSynchronization(3));\n\n        testRegisteredSynchronizationsOrder(rollback, new StaticSynchronization(1), new StaticSynchronization(2),\n                new StaticSynchronization(3, failOnBefore,\n                        failOnAfter));\n\n        testRegisteredSynchronizationsOrder(rollback, new StaticSynchronization(1, failOnBefore, failOnAfter),\n                new StaticSynchronization(2, failOnBefore,\n                        failOnAfter),\n                new StaticSynchronization(3, failOnBefore, failOnAfter));\n\n    }\n\n    @Test(expected = STransactionCommitException.class)\n    public void testRegisteredSynchronizationsOrderOnFailureDuringBeforeCompletionOnCommit() throws Exception {\n        testRegisteredSynchronizationsOrderOnFailure(false, true, false);\n    }\n\n    @Test(expected = STransactionCommitException.class)\n    public void testRegisteredSynchronizationsOrderOnFailureDuringBeforeAndAfterCompletionOnCommit() throws Exception {\n        testRegisteredSynchronizationsOrderOnFailure(false, true, true);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-transaction/src/test/java/org/bonitasoft/engine/transaction/XAResourceRetrieverTest.java",
    "content": "/**\n * Copyright (C) 2021 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.transaction;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.*;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\n\nimport javax.transaction.Transaction;\nimport javax.transaction.xa.XAResource;\n\nimport com.arjuna.ats.internal.jta.xa.TxInfo;\nimport org.junit.Test;\n\npublic class XAResourceRetrieverTest {\n\n    @Test\n    public void should_return_2_resources_when_arjuna_transaction_manager_has_2_resources() {\n        com.arjuna.ats.jta.transaction.Transaction transaction = mock(com.arjuna.ats.jta.transaction.Transaction.class);\n        Map<XAResource, TxInfo> xaResourceTxInfoMap = new HashMap<>();\n        xaResourceTxInfoMap.put(mock(XAResource.class), mock(TxInfo.class));\n        xaResourceTxInfoMap.put(mock(XAResource.class), mock(TxInfo.class));\n        doReturn(xaResourceTxInfoMap).when(transaction).getResources();\n\n        Optional<List<XAResource>> xaResources = new XAResourceRetriever().retrieveResources(transaction);\n\n        assertThat(xaResources.get()).hasSize(2);\n    }\n\n    @Test\n    public void should_return_1_resource_when_arjuna_transaction_manager_has_1_resource() {\n        com.arjuna.ats.jta.transaction.Transaction transaction = mock(com.arjuna.ats.jta.transaction.Transaction.class);\n        Map<XAResource, TxInfo> xaResourceTxInfoMap = new HashMap<>();\n        xaResourceTxInfoMap.put(mock(XAResource.class), mock(TxInfo.class));\n        doReturn(xaResourceTxInfoMap).when(transaction).getResources();\n\n        Optional<List<XAResource>> xaResources = new XAResourceRetriever().retrieveResources(transaction);\n\n        assertThat(xaResources.get()).hasSize(1);\n    }\n\n    @Test\n    public void should_return_no_value_when_transaction_manager_is_not_arjuna() throws NoSuchMethodException {\n        XAResourceRetriever xaResourceRetriever = new XAResourceRetriever();\n\n        Transaction transaction = mock(Transaction.class);\n\n        Optional<List<XAResource>> xaResources = xaResourceRetriever.retrieveResources(transaction);\n        assertThat(xaResources).isEmpty();\n    }\n\n    @Test\n    public void should_return_no_value_resource_when_transaction_object_is_not_arjuna() {\n        com.arjuna.ats.jta.transaction.Transaction transaction = mock(com.arjuna.ats.jta.transaction.Transaction.class);\n        doThrow(new ClassCastException()).when(transaction).getResources();\n\n        Optional<List<XAResource>> xaResources = new XAResourceRetriever().retrieveResources(transaction);\n\n        assertThat(xaResources).isEmpty();\n    }\n\n    @Test\n    public void should_not_retry_getResource_when_first_call_failed() {\n        com.arjuna.ats.jta.transaction.Transaction transaction = mock(com.arjuna.ats.jta.transaction.Transaction.class);\n        doThrow(new ClassCastException()).when(transaction).getResources();\n\n        XAResourceRetriever xaResourceRetriever = new XAResourceRetriever();\n\n        assertThat(xaResourceRetriever.retrieveResources(transaction)).isEmpty();\n        assertThat(xaResourceRetriever.retrieveResources(transaction)).isEmpty();\n\n        verify(transaction, times(1)).getResources();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-transaction/src/test/java/org/bonitasoft/engine/transaction/synchronization/SimpleSynchronization.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.transaction.synchronization;\n\nimport javax.transaction.Status;\n\nimport org.bonitasoft.engine.transaction.BonitaTransactionSynchronization;\n\npublic class SimpleSynchronization implements BonitaTransactionSynchronization {\n\n    private boolean beforeCompletion = false;\n\n    private boolean afterCompletion = false;\n\n    private int afterCompletionStatus = Status.STATUS_NO_TRANSACTION;\n\n    private final boolean failOnBeforeCompletion;\n\n    private final boolean failOnAfterCompletion;\n\n    public SimpleSynchronization() {\n        this.failOnBeforeCompletion = false;\n        this.failOnAfterCompletion = false;\n    }\n\n    @Override\n    public void beforeCompletion() {\n        if (this.failOnBeforeCompletion) {\n            throw new RuntimeException();\n        }\n        this.beforeCompletion = true;\n    }\n\n    @Override\n    public void afterCompletion(final int status) {\n        if (this.failOnAfterCompletion) {\n            throw new RuntimeException();\n        }\n        this.afterCompletion = true;\n        this.afterCompletionStatus = status;\n    }\n\n    public boolean isBeforeCompletion() {\n        return this.beforeCompletion;\n    }\n\n    public boolean isAfterCompletion() {\n        return this.afterCompletion;\n    }\n\n    public int getAfterCompletionStatus() {\n        return this.afterCompletionStatus;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-transaction/src/test/java/org/bonitasoft/engine/transaction/synchronization/StaticSynchronization.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.transaction.synchronization;\n\nimport org.bonitasoft.engine.transaction.BonitaTransactionSynchronization;\n\npublic class StaticSynchronization implements BonitaTransactionSynchronization {\n\n    private final String beforeCompletionComment;\n\n    private final String afterCompletionComment;\n\n    private boolean failOnBefore = false;\n\n    private boolean failOnAfter = false;\n\n    public StaticSynchronization(final int id) {\n        super();\n        this.beforeCompletionComment = \"sync\" + id + \"Before\";\n        this.afterCompletionComment = \"sync\" + id + \"After\";\n    }\n\n    public StaticSynchronization(final int id, final boolean failOnBefore, final boolean failOnAfter) {\n        this(id);\n        this.failOnBefore = failOnBefore;\n        this.failOnAfter = failOnAfter;\n    }\n\n    public String getBeforeCompletionComment() {\n        return this.beforeCompletionComment;\n    }\n\n    public String getAfterCompletionComment() {\n        return this.afterCompletionComment;\n    }\n\n    @Override\n    public void beforeCompletion() {\n        StaticSynchronizationResult.COMMENT += this.beforeCompletionComment;\n        if (this.failOnBefore) {\n            throw new RuntimeException();\n        }\n    }\n\n    @Override\n    public void afterCompletion(final int status) {\n        StaticSynchronizationResult.COMMENT += this.afterCompletionComment;\n        if (this.failOnAfter) {\n            throw new RuntimeException();\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-transaction/src/test/java/org/bonitasoft/engine/transaction/synchronization/StaticSynchronizationResult.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.transaction.synchronization;\n\npublic class StaticSynchronizationResult {\n\n    public static String COMMENT = \"\";\n\n    public static void reset() {\n        COMMENT = \"\";\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-transaction/src/test/resources/logback-test.xml",
    "content": "<configuration debug=\"false\" scan=\"false\">\n\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->\n        <encoder>\n            <pattern>|%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <logger name=\"org.bonitasoft.engine.transaction.JTATransactionServiceImpl\" level=\"TRACE\" />\n\n    <root level=\"WARN\">\n        <appender-ref ref=\"STDOUT\" />\n    </root>\n\n</configuration>\n"
  },
  {
    "path": "services/bonita-work/build.gradle",
    "content": "\n\ndependencies {\n    api project(':services:bonita-session')\n    api project(':services:bonita-incident')\n    api project(':services:bonita-commons')\n    api project(':services:bonita-transaction')\n    api libs.springJdbc\n    api libs.springContext\n    testImplementation libs.awaitility\n    testImplementation libs.assertj\n    testImplementation libs.mockitoCore\n    testImplementation libs.mockitoJunitJupiter\n    testImplementation libs.systemRules\n    testRuntimeOnly libs.logback\n}\n"
  },
  {
    "path": "services/bonita-work/src/main/java/org/bonitasoft/engine/work/BonitaExecutorService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work;\n\nimport java.util.concurrent.Future;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * This is the interface we use to wrap the ThreadPool that execute works\n *\n * @author Julien Reboul\n * @author Baptiste Mesta\n */\npublic interface BonitaExecutorService {\n\n    /**\n     * clear the queue of work\n     */\n    void clearAllQueues();\n\n    /**\n     * shutdown and handle the queue properly\n     */\n    void shutdownAndEmptyQueue();\n\n    /**\n     * Execute the work described by the work descriptor\n     *\n     * @param work\n     */\n    Future<?> submit(WorkDescriptor work);\n\n    boolean awaitTermination(long workTerminationTimeout, TimeUnit seconds) throws InterruptedException;\n\n    ThreadPoolExecutor getExecutor();\n\n}\n"
  },
  {
    "path": "services/bonita-work/src/main/java/org/bonitasoft/engine/work/BonitaExecutorServiceFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work;\n\n/**\n * A factory to create executor service that will be given to the WorkService\n *\n * @author Baptiste Mesta\n */\npublic interface BonitaExecutorServiceFactory {\n\n    /**\n     * Create a bonita executor service with the given {@link WorkExecutionCallback}\n     *\n     * @param workExecutionCallback this callback will be executed when a work complete.\n     * @return the {@link BonitaExecutorService}\n     */\n    BonitaExecutorService createExecutorService(WorkExecutionCallback workExecutionCallback);\n\n    default void unbind() {\n    }\n}\n"
  },
  {
    "path": "services/bonita-work/src/main/java/org/bonitasoft/engine/work/BonitaRunnable.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work;\n\nimport java.io.Serializable;\n\n/**\n * A runnable that notifies a listener of its state\n *\n * @author Baptiste Mesta\n */\npublic abstract class BonitaRunnable implements Runnable, Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    private final long tenantId;\n\n    public BonitaRunnable(final long tenantId) {\n        this.tenantId = tenantId;\n    }\n\n    @Override\n    public void run() {\n        innerRun();\n    }\n\n    public abstract void innerRun();\n\n    public abstract void cancel();\n\n    public long getTenantId() {\n        return tenantId;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-work/src/main/java/org/bonitasoft/engine/work/BonitaWork.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work;\n\nimport java.util.Map;\nimport java.util.UUID;\nimport java.util.concurrent.CompletableFuture;\n\nimport org.apache.commons.lang3.builder.EqualsBuilder;\nimport org.apache.commons.lang3.builder.HashCodeBuilder;\n\n/**\n * @author Baptiste Mesta\n */\npublic abstract class BonitaWork {\n\n    private final String uuid = UUID.randomUUID().toString();\n\n    protected long tenantId;\n\n    private BonitaWork parentWork;\n\n    public String getUuid() {\n        return uuid;\n    }\n\n    public abstract String getDescription();\n\n    /**\n     * @return\n     *         how to restart the work if it fails\n     */\n    public String getRecoveryProcedure() {\n        return \"No recovery procedure.\";\n    }\n\n    /**\n     * Execution code of the work\n     *\n     * @param context\n     *        a map of context that can be filled by a work to be given to a wrapped work\n     * @throws Exception\n     */\n    public abstract CompletableFuture<Void> work(Map<String, Object> context) throws Exception;\n\n    public abstract void handleFailure(Throwable e, Map<String, Object> context) throws Exception;\n\n    /**\n     * @return true if the RecoveryService can recover this kind of failure\n     */\n    public boolean canBeRecoveredByTheRecoveryMechanism() {\n        return false;\n    }\n\n    public void setParent(final BonitaWork parentWork) {\n        this.parentWork = parentWork;\n    }\n\n    public BonitaWork getParent() {\n        return parentWork;\n    }\n\n    protected BonitaWork getRootWork() {\n        BonitaWork rootWork = this;\n        while (rootWork.getParent() != null) {\n            rootWork = rootWork.getParent();\n        }\n        return rootWork;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (o == null || getClass() != o.getClass())\n            return false;\n        BonitaWork work = (BonitaWork) o;\n        return new EqualsBuilder()\n                .append(uuid, work.uuid)\n                .append(parentWork, work.parentWork)\n                .isEquals();\n    }\n\n    @Override\n    public int hashCode() {\n        return new HashCodeBuilder(17, 37)\n                .append(uuid)\n                .append(parentWork)\n                .toHashCode();\n    }\n}\n"
  },
  {
    "path": "services/bonita-work/src/main/java/org/bonitasoft/engine/work/BonitaWorkExecutorFactory.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work;\n\nimport java.util.concurrent.ThreadPoolExecutor;\n\npublic interface BonitaWorkExecutorFactory {\n\n    ThreadPoolExecutor create();\n\n}\n"
  },
  {
    "path": "services/bonita-work/src/main/java/org/bonitasoft/engine/work/DefaultBonitaExecutorService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work;\n\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.CompletionException;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport io.micrometer.core.instrument.Counter;\nimport io.micrometer.core.instrument.Gauge;\nimport io.micrometer.core.instrument.MeterRegistry;\nimport org.bonitasoft.engine.commons.time.EngineClock;\nimport org.bonitasoft.engine.mdc.MDCHelper;\nimport org.bonitasoft.engine.work.audit.WorkExecutionAuditor;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class DefaultBonitaExecutorService implements BonitaExecutorService {\n\n    private static final Logger log = LoggerFactory.getLogger(DefaultBonitaExecutorService.class);\n    public static final String NUMBER_OF_WORKS_PENDING = \"bonita.bpmengine.work.pending\";\n    public static final String NUMBER_OF_WORKS_RUNNING = \"bonita.bpmengine.work.running\";\n    public static final String NUMBER_OF_WORKS_EXECUTED = \"bonita.bpmengine.work.executed\";\n    public static final String WORKS_UNIT = \"works\";\n\n    private final WorkFactory workFactory;\n    private final EngineClock engineClock;\n    private final WorkExecutionCallback workExecutionCallback;\n    private final WorkExecutionAuditor workExecutionAuditor;\n    private final MeterRegistry meterRegistry;\n\n    private final AtomicLong runningWorks = new AtomicLong();\n    private final Counter executedWorkCounter;\n    private final Gauge numberOfWorksPending;\n    private final Gauge numberOfWorksRunning;\n    private final ThreadPoolExecutor executor;\n\n    public DefaultBonitaExecutorService(final ThreadPoolExecutor executor,\n            final WorkFactory workFactory,\n            final EngineClock engineClock,\n            final WorkExecutionCallback workExecutionCallback,\n            final WorkExecutionAuditor workExecutionAuditor,\n            final MeterRegistry meterRegistry) {\n        this.executor = executor;\n        this.workFactory = workFactory;\n        this.engineClock = engineClock;\n        this.workExecutionCallback = workExecutionCallback;\n        this.workExecutionAuditor = workExecutionAuditor;\n        this.meterRegistry = meterRegistry;\n\n        numberOfWorksPending = Gauge.builder(NUMBER_OF_WORKS_PENDING, executor.getQueue(), Collection::size)\n                .baseUnit(WORKS_UNIT).description(\"Works pending in the execution queue\")\n                .register(meterRegistry);\n        numberOfWorksRunning = Gauge.builder(NUMBER_OF_WORKS_RUNNING, runningWorks, AtomicLong::get)\n                .baseUnit(WORKS_UNIT).description(\"Works currently executing\")\n                .register(meterRegistry);\n        executedWorkCounter = Counter.builder(NUMBER_OF_WORKS_EXECUTED)\n                .baseUnit(WORKS_UNIT).description(\"total works executed since last server start\")\n                .register(meterRegistry);\n    }\n\n    public ThreadPoolExecutor getExecutor() {\n        return executor;\n    }\n\n    @Override\n    public void clearAllQueues() {\n        executor.getQueue().clear();\n    }\n\n    @Override\n    public void shutdownAndEmptyQueue() {\n        executor.shutdown();\n        log.info(\"Clearing queue of work, had {} elements\", executor.getQueue().size());\n        executor.getQueue().clear();\n        meterRegistry.remove(numberOfWorksPending);\n        meterRegistry.remove(numberOfWorksRunning);\n        meterRegistry.remove(executedWorkCounter);\n    }\n\n    @Override\n    public Future<?> submit(WorkDescriptor work) {\n        return executor.submit(() -> {\n            if (isRequiringDelayedExecution(work)) {\n                // Future implementation should use a real delay e.g. using a ScheduledThreadPoolExecutor\n                // Will be executed later\n                submit(work);\n                return;\n            }\n            work.incrementExecutionCount();\n            workExecutionAuditor.detectAbnormalExecutionAndNotify(work);\n\n            BonitaWork bonitaWork = workFactory.create(work);\n            HashMap<String, Object> context = new HashMap<>();\n            CompletableFuture<Void> asyncResult;\n            runningWorks.incrementAndGet();\n            try {\n                asyncResult = bonitaWork.work(context);\n            } catch (Exception e) {\n                executedWorkCounter.increment();\n                runningWorks.decrementAndGet();\n                MDCHelper.tryWithMDC(e, () -> workExecutionCallback.onFailure(work, bonitaWork, context, e));\n                return;\n            }\n\n            asyncResult.handle((result, error) -> {\n                executedWorkCounter.increment();\n                runningWorks.decrementAndGet();\n                if (error != null) {\n                    if (error instanceof CompletionException) {\n                        error = error.getCause();\n                    }\n                    workExecutionCallback.onFailure(work, bonitaWork, context, error);\n                } else {\n                    workExecutionCallback.onSuccess(work);\n                }\n                return null;\n            });\n        });\n    }\n\n    @Override\n    public boolean awaitTermination(long workTerminationTimeout, TimeUnit seconds) throws InterruptedException {\n        return executor.awaitTermination(workTerminationTimeout, seconds);\n    }\n\n    private boolean isRequiringDelayedExecution(WorkDescriptor work) {\n        return work.getExecutionThreshold() != null && work.getExecutionThreshold().isAfter(engineClock.now());\n    }\n}\n"
  },
  {
    "path": "services/bonita-work/src/main/java/org/bonitasoft/engine/work/DefaultBonitaExecutorServiceFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work;\n\nimport java.util.concurrent.ThreadPoolExecutor;\n\nimport io.micrometer.core.instrument.MeterRegistry;\nimport org.bonitasoft.engine.commons.time.EngineClock;\nimport org.bonitasoft.engine.monitoring.ExecutorServiceMetricsProvider;\nimport org.bonitasoft.engine.work.audit.WorkExecutionAuditor;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\n\n/**\n * Use ThreadPoolExecutor as ExecutorService\n * The handling of threads relies on the JVM\n * The rules to create new thread are:\n * - If the number of threads is less than the corePoolSize, create a new Thread to run a new task.\n * - If the number of threads is equal (or greater than) the corePoolSize, put the task into the queue.\n * - If the queue is full, and the number of threads is less than the maxPoolSize, create a new thread to run tasks in.\n * - If the queue is full, and the number of threads is greater than or equal to maxPoolSize, reject the task.\n * When the current number of threads are > than corePoolSize, they are kept idle during keepAliveTimeSeconds\n *\n * @author Baptiste Mesta\n */\n\n@Component(\"bonitaExecutorServiceFactory\")\npublic class DefaultBonitaExecutorServiceFactory implements BonitaExecutorServiceFactory {\n\n    private static final String BONITA_WORK_EXECUTOR = \"bonita-work-executor\";\n    private final Logger logger = LoggerFactory.getLogger(DefaultBonitaExecutorServiceFactory.class);\n\n    private final MeterRegistry meterRegistry;\n    private final ExecutorServiceMetricsProvider executorServiceMetricsProvider;\n    private final BonitaWorkExecutorFactory bonitaWorkExecutorFactory;\n    private final EngineClock engineClock;\n    private final WorkFactory workFactory;\n    private final WorkExecutionAuditor workExecutionAuditor;\n\n    public DefaultBonitaExecutorServiceFactory(\n            MeterRegistry meterRegistry,\n            EngineClock engineClock,\n            WorkFactory workFactory,\n            WorkExecutionAuditor workExecutionAuditor,\n            ExecutorServiceMetricsProvider executorServiceMetricsProvider,\n            BonitaWorkExecutorFactory bonitaWorkExecutorFactory) {\n        this.meterRegistry = meterRegistry;\n        this.workFactory = workFactory;\n        this.workExecutionAuditor = workExecutionAuditor;\n        this.engineClock = engineClock;\n        this.executorServiceMetricsProvider = executorServiceMetricsProvider;\n        this.bonitaWorkExecutorFactory = bonitaWorkExecutorFactory;\n    }\n\n    @Override\n    public BonitaExecutorService createExecutorService(WorkExecutionCallback workExecutionCallback) {\n        final ThreadPoolExecutor bonitaThreadPoolExecutor = bonitaWorkExecutorFactory.create();\n        final BonitaExecutorService bonitaExecutorService = new DefaultBonitaExecutorService(bonitaThreadPoolExecutor,\n                workFactory,\n                engineClock,\n                workExecutionCallback,\n                workExecutionAuditor,\n                meterRegistry);\n        logger.info(\n                \"Creating a new Thread pool to handle works: {}\", bonitaThreadPoolExecutor);\n\n        //TODO this returns the timed executor service, this should be used instead of the BonitaExecutorService but we should change it everywhere\n        executorServiceMetricsProvider\n                .bindMetricsOnly(meterRegistry, bonitaThreadPoolExecutor, BONITA_WORK_EXECUTOR);\n        return bonitaExecutorService;\n    }\n\n    @Override\n    public void unbind() {\n        executorServiceMetricsProvider.unbind(meterRegistry, BONITA_WORK_EXECUTOR);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-work/src/main/java/org/bonitasoft/engine/work/DefaultExceptionRetryabilityEvaluator.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work;\n\nimport static javax.transaction.xa.XAException.XA_RBTIMEOUT;\nimport static javax.transaction.xa.XAException.XA_RBTRANSIENT;\nimport static org.bonitasoft.engine.work.ExceptionRetryabilityEvaluator.Retryability.NOT_RETRYABLE;\nimport static org.bonitasoft.engine.work.ExceptionRetryabilityEvaluator.Retryability.RETRYABLE;\nimport static org.bonitasoft.engine.work.ExceptionRetryabilityEvaluator.Retryability.UNCERTAIN_COMPLETION_OF_COMMIT;\n\nimport java.lang.reflect.Method;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Optional;\n\nimport javax.transaction.xa.XAException;\n\nimport org.bonitasoft.engine.transaction.STransactionCommitException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.dao.DataAccessException;\nimport org.springframework.dao.RecoverableDataAccessException;\nimport org.springframework.dao.TransientDataAccessException;\nimport org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator;\n\n/**\n * @author Baptiste Mesta.\n */\n\npublic class DefaultExceptionRetryabilityEvaluator implements ExceptionRetryabilityEvaluator {\n\n    private static final Logger logger = LoggerFactory.getLogger(DefaultExceptionRetryabilityEvaluator.class);\n    private static final List<Integer> XA_RETRYABLE = Arrays.asList(XA_RBTRANSIENT, XA_RBTIMEOUT);\n    private HashSet<Class<? extends Throwable>> exceptionClassesToRetry;\n    private HashSet<Class<? extends Throwable>> exceptionClassesToNotRetry;\n\n    public DefaultExceptionRetryabilityEvaluator(List<String> exceptionClassesToRetry,\n            List<String> exceptionClassesToNotRetry) {\n        this.exceptionClassesToRetry = toSetOfClasses(exceptionClassesToRetry);\n        this.exceptionClassesToNotRetry = toSetOfClasses(exceptionClassesToNotRetry);\n    }\n\n    private HashSet<Class<? extends Throwable>> toSetOfClasses(List<String> exceptionClassesToRetry) {\n        ClassLoader classLoader = this.getClass().getClassLoader();\n        HashSet<Class<? extends Throwable>> classSet = new HashSet<>();\n        for (String className : exceptionClassesToRetry) {\n            classSet.add(getCheckedThrowable(classLoader, className));\n        }\n        return classSet;\n    }\n\n    private Class<? extends Throwable> getCheckedThrowable(ClassLoader classLoader, String className) {\n        Class<?> throwable;\n        try {\n            throwable = classLoader.loadClass(className);\n        } catch (ClassNotFoundException e) {\n            throw new IllegalArgumentException(String.format(\"The class %s is not found\", className), e);\n        }\n        if (!Throwable.class.isAssignableFrom(throwable)) {\n            throw new IllegalArgumentException(String.format(\"The class %s is not a Throwable\", className));\n        }\n        return (Class<? extends Throwable>) throwable;\n    }\n\n    @Override\n    public Retryability evaluateRetryability(Throwable thrown) {\n        List<Throwable> exceptions = getAllCauses(thrown);\n        Optional<Throwable> notRetryableException = exceptions.stream().filter(this::isKnownAsNotRetryable).findFirst();\n        if (notRetryableException.isPresent()) {\n            logger.debug(\"Exception is known as not retryable: {}\", print(exceptions, notRetryableException.get()));\n            return NOT_RETRYABLE;\n        }\n        Optional<Throwable> retryableException = exceptions.stream().filter(this::isRetryable).findFirst();\n        if (!retryableException.isPresent()) {\n            logger.debug(\"No retryable exception found: {}\", print(exceptions));\n            return NOT_RETRYABLE;\n        }\n\n        Throwable theRetryableException = retryableException.get();\n        if (isSqlConnectionIssue(theRetryableException)\n                && exceptions.stream().anyMatch(e -> e instanceof STransactionCommitException)) {\n            logger.debug(\n                    \"Retryable exception found but exception happened on commit, uncertain completion of the transaction: {}\",\n                    print(exceptions, theRetryableException));\n            return UNCERTAIN_COMPLETION_OF_COMMIT;\n        } else {\n            logger.debug(\"Retryable exception found: {}\", print(exceptions, theRetryableException));\n            return RETRYABLE;\n        }\n    }\n\n    private boolean isRetryable(Throwable thrown) {\n        if (isInListedRetriableException(thrown)) {\n            return true;\n        }\n        if (thrown instanceof SQLException) {\n            DataAccessException dataAccessException = new SQLErrorCodeSQLExceptionTranslator().translate(\"\", \"\",\n                    (SQLException) thrown);\n            if (dataAccessException instanceof RecoverableDataAccessException\n                    || dataAccessException instanceof TransientDataAccessException) {\n                return true;\n            }\n        }\n        if (thrown instanceof XAException) {\n            int errorCode = ((XAException) thrown).errorCode;\n            if (XA_RETRYABLE.contains(errorCode)) {\n                return true;\n            }\n        }\n        return isSqlConnectionIssue(thrown);\n    }\n\n    private List<Throwable> getAllCauses(Throwable thrown) {\n        List<Throwable> allExceptions = new ArrayList<>();\n        while (thrown != null) {\n            allExceptions.add(thrown);\n            List<Exception> exceptions = getExceptions(thrown, \"getExceptions\", \"getHandlerExceptions\");\n            if (!exceptions.isEmpty()) {\n                for (Exception exception : exceptions) {\n                    allExceptions.addAll(getAllCauses(exception));\n                }\n            }\n            thrown = thrown.getCause();\n        }\n        return allExceptions;\n    }\n\n    private boolean isSqlConnectionIssue(Throwable thrown) {\n        String name = thrown.getClass().getSimpleName().toLowerCase();\n        String message = thrown.getMessage() != null ? thrown.getMessage().toLowerCase() : \"\";\n        return (name.contains(\"sql\") || name.contains(\"jdbc\"))\n                && (message.contains(\"connection\") || message.contains(\"timeout\") || message.contains(\"i/o error\"));\n    }\n\n    private String print(List<Throwable> throwables, Throwable highlighted) {\n        StringBuilder stringBuilder = new StringBuilder(\"Exceptions:\\n\");\n        for (Throwable throwable : throwables) {\n            stringBuilder.append(\" ↳\");\n            stringBuilder.append(throwable.getClass().getName());\n            if (highlighted != null && throwable.getClass().equals(highlighted.getClass())) {\n                stringBuilder.append(\": \");\n                stringBuilder.append(throwable.getMessage());\n                stringBuilder.append(\" ☚\");\n            }\n            stringBuilder.append('\\n');\n        }\n        return stringBuilder.toString();\n    }\n\n    private String print(List<Throwable> throwables) {\n        return print(throwables, null);\n    }\n\n    private boolean isInListedRetriableException(Throwable e) {\n        return isListedIn(e, exceptionClassesToRetry);\n    }\n\n    private boolean isKnownAsNotRetryable(Throwable e) {\n        return isListedIn(e, exceptionClassesToNotRetry);\n    }\n\n    private boolean isListedIn(Throwable e, HashSet<Class<? extends Throwable>> exceptions) {\n        for (Class<? extends Throwable> throwable : exceptions) {\n            if (throwable.isInstance(e)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    private List<Exception> getExceptions(Throwable thrown, String... methodNames) {\n        for (String methodName : methodNames) {\n            try {\n                //for bitronix PhaseException and FireEventExceptions\n                Method getExceptions = thrown.getClass().getDeclaredMethod(methodName);\n                return (List<Exception>) getExceptions.invoke(thrown);\n            } catch (Throwable ignored) {\n            }\n        }\n        return Collections.emptyList();\n    }\n}\n"
  },
  {
    "path": "services/bonita-work/src/main/java/org/bonitasoft/engine/work/ExceptionRetryabilityEvaluator.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work;\n\n/**\n * @author Baptiste Mesta.\n */\npublic interface ExceptionRetryabilityEvaluator {\n\n    enum Retryability {\n        NOT_RETRYABLE, RETRYABLE, UNCERTAIN_COMPLETION_OF_COMMIT\n    }\n\n    Retryability evaluateRetryability(Throwable thrown);\n}\n"
  },
  {
    "path": "services/bonita-work/src/main/java/org/bonitasoft/engine/work/FailureCallback.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work;\n\nimport java.util.Map;\n\n/**\n * @author Baptiste Mesta.\n */\n@FunctionalInterface\npublic interface FailureCallback {\n\n    void onFailure(WorkDescriptor workDescriptor, BonitaWork bonitaWork, Map<String, Object> context, Exception thrown);\n}\n"
  },
  {
    "path": "services/bonita-work/src/main/java/org/bonitasoft/engine/work/LockException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work;\n\npublic class LockException extends Exception {\n\n    public LockException(String message, Exception e) {\n        super(message, e);\n    }\n\n    public LockException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "services/bonita-work/src/main/java/org/bonitasoft/engine/work/LockTimeoutException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work;\n\n/**\n * @author Baptiste Mesta.\n */\npublic class LockTimeoutException extends LockException {\n\n    public LockTimeoutException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "services/bonita-work/src/main/java/org/bonitasoft/engine/work/RetryingWorkExecutorService.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work;\n\nimport static java.lang.String.format;\nimport static org.bonitasoft.engine.commons.ExceptionUtils.printLightWeightStacktrace;\nimport static org.bonitasoft.engine.commons.ExceptionUtils.printRootCauseOnly;\n\nimport java.time.Instant;\nimport java.util.Map;\nimport java.util.Random;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport io.micrometer.core.instrument.Gauge;\nimport io.micrometer.core.instrument.MeterRegistry;\nimport org.bonitasoft.engine.commons.time.EngineClock;\nimport org.bonitasoft.engine.incident.Incident;\nimport org.bonitasoft.engine.incident.IncidentService;\nimport org.bonitasoft.engine.mdc.MDCHelper;\nimport org.bonitasoft.engine.work.audit.WorkExecutionAuditor;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Baptiste Mesta.\n */\n@Component\n\n@ConditionalOnSingleCandidate(WorkExecutorService.class)\npublic class RetryingWorkExecutorService implements WorkExecutorService, WorkExecutionCallback {\n\n    Logger logger = LoggerFactory.getLogger(RetryingWorkExecutorService.class);\n    public static final String NUMBER_OF_WORKS_RETRIED = \"bonita.bpmengine.work.retried\";\n    private final EngineClock engineClock;\n    private final int maxRetry;\n    private final WorkExecutionAuditor workExecutionAuditor;\n    private int delay;\n    private final double delayFactor;\n    private final ExceptionRetryabilityEvaluator exceptionRetryabilityEvaluator;\n    private final AtomicLong retriedWorks = new AtomicLong();\n    private final BonitaExecutorServiceFactory bonitaExecutorServiceFactory;\n    private final long workTerminationTimeout;\n    private BonitaExecutorService executor;\n    private final IncidentService incidentService;\n    public int numberOfFramesToLogInExceptions = 3;\n    private final Random random = new Random();\n\n    public RetryingWorkExecutorService(BonitaExecutorServiceFactory bonitaExecutorServiceFactory,\n            EngineClock engineClock,\n            @Value(\"${bonita.tenant.work.terminationTimeout}\") long workTerminationTimeout,\n            @Value(\"${bonita.tenant.work.maxRetry}\") int maxRetry,\n            @Value(\"${bonita.tenant.work.retry.delay}\") int delay,\n            @Value(\"${bonita.tenant.work.retry.factor}\") double delayFactor,\n            ExceptionRetryabilityEvaluator exceptionRetryabilityEvaluator,\n            WorkExecutionAuditor workExecutionAuditor,\n            MeterRegistry meterRegistry,\n            IncidentService incidentService) {\n        this.bonitaExecutorServiceFactory = bonitaExecutorServiceFactory;\n        this.engineClock = engineClock;\n        this.workTerminationTimeout = workTerminationTimeout;\n        this.maxRetry = maxRetry;\n        this.delay = delay;\n        this.delayFactor = delayFactor;\n        this.exceptionRetryabilityEvaluator = exceptionRetryabilityEvaluator;\n        this.workExecutionAuditor = workExecutionAuditor;\n        this.incidentService = incidentService;\n        Gauge.builder(NUMBER_OF_WORKS_RETRIED, retriedWorks, AtomicLong::get)\n                .baseUnit(\"works\")\n                .description(\"Works currently waiting for execution that have been retried at least once\")\n                .register(meterRegistry);\n    }\n\n    @Value(\"${bonita.tenant.work.exceptionsNumberOfFrameToLog:3}\")\n    public void setNumberOfFramesToLogInExceptions(int numberOfFramesToLogInExceptions) {\n        this.numberOfFramesToLogInExceptions = numberOfFramesToLogInExceptions;\n    }\n\n    @Override\n    public void onSuccess(WorkDescriptor work) {\n        if (work.getRetryCount() > 0) {\n            logger.info(\"Work {} was successfully retried after {} tries.\", work, work.getRetryCount());\n            retriedWorks.decrementAndGet();\n        }\n        logger.debug(\"Completed work {}\", work);\n        workExecutionAuditor.notifySuccess(work);\n    }\n\n    @Override\n    public void onFailure(WorkDescriptor work, BonitaWork bonitaWork, Map<String, Object> context, Throwable thrown) {\n        MDCHelper.tryWithMDC(thrown, () -> {\n            if (thrown instanceof LockException) {\n                if (thrown instanceof LockTimeoutException) {\n                    //Can happen frequently, only log in debug\n                    logger.debug(\"Tried to execute the work, but it was unable to acquire a lock {}\", work);\n                } else {\n                    //Caused\n                    logger.warn(\"Tried to execute the work, but it was unable to acquire a lock {}\",\n                            bonitaWork.getDescription(), thrown);\n                }\n                execute(work);\n                return;\n            }\n            logger.debug(\"Work {} failed because of \", work, thrown);\n            switch (exceptionRetryabilityEvaluator.evaluateRetryability(thrown)) {\n                case NOT_RETRYABLE:\n                    if (thrown instanceof SWorkPreconditionException) {\n                        logger.warn(\"Work was not executed because preconditions were not met, {} : {}\",\n                                bonitaWork.getDescription(), thrown.getMessage());\n                        decrementRetryCounterIfNeeded(work);\n                    } else {\n                        logger.warn(\"Work {} failed. The element will be marked as failed. Exception is: {}\",\n                                bonitaWork.getDescription(),\n                                printLightWeightStacktrace(thrown, numberOfFramesToLogInExceptions));\n                        handleFailure(work, bonitaWork, context, thrown);\n                    }\n                    break;\n                case UNCERTAIN_COMPLETION_OF_COMMIT:\n                    // Do the same as retryable but add a warning log\n                    logger.warn(\n                            \"Work {} has failed and will be retried but the issue happened during the commit. We are uncertain that the \"\n                                    +\n                                    \"commit was really completed. If the retry fails with a SWorkPreconditionException it might indicate that \"\n                                    +\n                                    \"the work was already completed and the recovery mechanism will restart it. No manual action is required.\",\n                            bonitaWork.getDescription());\n                case RETRYABLE:\n                    if (work.getRetryCount() < maxRetry) {\n                        long delayInMillis = getDelayInMillis(work.getRetryCount());\n                        logger.warn(\n                                \"Work {} failed because of {}. It will be retried. Attempt {} of {} with a delay of {} ms\",\n                                bonitaWork.getDescription(),\n                                printRootCauseOnly(thrown),\n                                work.getRetryCount() + 1,\n                                maxRetry, delayInMillis);\n                        incrementRetryCounterIfNeeded(work);\n                        retry(work, delayInMillis);\n                    } else {\n                        logger.warn(\"Work {} failed. It has already been retried {} times. \" +\n                                \"No more retries will be attempted, it will be marked as failed. Exception is: {}\",\n                                bonitaWork.getDescription(), maxRetry,\n                                printLightWeightStacktrace(thrown, numberOfFramesToLogInExceptions));\n                        handleFailure(work, bonitaWork, context, thrown);\n                    }\n                    break;\n                default:\n                    throw new IllegalStateException(\n                            \"Unexpected value: \" + exceptionRetryabilityEvaluator.evaluateRetryability(thrown));\n            }\n        });\n    }\n\n    public void handleFailure(WorkDescriptor work, BonitaWork bonitaWork, Map<String, Object> context,\n            Throwable thrown) {\n        decrementRetryCounterIfNeeded(work);\n        try {\n            bonitaWork.handleFailure(thrown, context);\n        } catch (Exception e) {\n            if (bonitaWork.canBeRecoveredByTheRecoveryMechanism()) {\n                logger.warn(\"Work {} failed and we were not able to mark the element as failed. \" +\n                        \"However, the element can be recovered by the recovery mechanism, it will be recovered automatically \"\n                        +\n                        \"the next time the recovery is executed. We were not able to mark it as failed because of {} \",\n                        bonitaWork.getDescription(), printLightWeightStacktrace(e, numberOfFramesToLogInExceptions));\n                logger.debug(\"Unable to put the element as failed because:\", e);\n            } else {\n                logger.warn(\n                        \"Work {} failed and we were not able to mark the element as failed. An incident will be reported. \"\n                                +\n                                \"We were not able to mark it as failed because of {}\",\n                        bonitaWork.getDescription(), printLightWeightStacktrace(e, numberOfFramesToLogInExceptions));\n                incidentService.report(\n                        new Incident(bonitaWork.getDescription(), bonitaWork.getRecoveryProcedure(), thrown, e));\n            }\n        }\n    }\n\n    private void incrementRetryCounterIfNeeded(WorkDescriptor work) {\n        if (work.getRetryCount() == 0) {\n            retriedWorks.incrementAndGet();\n        }\n    }\n\n    private void decrementRetryCounterIfNeeded(WorkDescriptor work) {\n        if (work.getRetryCount() > 0) {\n            retriedWorks.decrementAndGet();\n        }\n    }\n\n    private void retry(WorkDescriptor work, long delayInMillis) {\n        Instant mustBeExecutedAfter = engineClock.now().plusMillis(delayInMillis);\n        work.incrementRetryCount();\n        work.mustBeExecutedAfter(mustBeExecutedAfter);\n        execute(work);\n    }\n\n    private long getDelayInMillis(int retryCount) {\n        //we multiply by the delay each time\n        // first time is delay\n        // second time is delay * delayFactor\n        // third time is delay * delayFactor * delayFactor\n        // and so on\n        // Also add a random scatter delay to avoid retry \"waves\"\n        // when several jobs fail & are retried at exactly the same time, causing further fails...\n        // see RUNTIME-302 for more details\n        double factor = Math.pow(delayFactor, retryCount);\n        return Math.round((delay * factor) * (random.nextFloat() + 1));\n    }\n\n    //For testing purpose\n    int getDelay() {\n        return delay;\n    }\n\n    //For testing purpose\n    void setDelay(int delay) {\n        this.delay = delay;\n    }\n\n    @Override\n    public void execute(WorkDescriptor work) {\n        if (!isStopped()) {\n            logger.debug(\"Submitted work\");\n            executor.submit(work);\n        } else {\n            logger.debug(\"Ignored work submission (service stopped) {}\", work);\n        }\n    }\n\n    @Override\n    public synchronized void stop() {\n        // we don't throw exception just stop it and log if something happens\n        try {\n            if (isStopped()) {\n                return;\n            }\n            bonitaExecutorServiceFactory.unbind();\n            shutdownExecutor();\n            awaitTermination();\n        } catch (final SWorkException e) {\n            if (e.getCause() != null) {\n                logger.warn(e.getMessage(), e.getCause());\n            } else {\n                logger.warn(e.getMessage());\n            }\n        }\n    }\n\n    @Override\n    public synchronized void start() {\n        if (isStopped()) {\n            executor = bonitaExecutorServiceFactory.createExecutorService(this);\n        }\n    }\n\n    @Override\n    public synchronized void pause() throws SWorkException {\n        if (isStopped()) {\n            return;\n        }\n        bonitaExecutorServiceFactory.unbind();\n        shutdownExecutor();\n        // completely clear the queue because it's a global pause\n        executor.clearAllQueues();\n        awaitTermination();\n    }\n\n    @Override\n    public synchronized void resume() {\n        start();\n    }\n\n    private void awaitTermination() throws SWorkException {\n        try {\n            if (!executor.awaitTermination(workTerminationTimeout, TimeUnit.SECONDS)) {\n                throw new SWorkException(format(\"Waited termination of all work %ds but all tasks were not finished\",\n                        workTerminationTimeout));\n            }\n        } catch (final InterruptedException e) {\n            throw new SWorkException(\"Interrupted while stopping the work service\", e);\n        }\n        executor = null;\n    }\n\n    private void shutdownExecutor() {\n        executor.shutdownAndEmptyQueue();\n        logger.info(\"Stopped executor service\");\n    }\n\n    public boolean isStopped() {\n        return executor == null;\n    }\n\n    @Override\n    public void notifyNodeStopped(String nodeName) {\n    }\n}\n"
  },
  {
    "path": "services/bonita-work/src/main/java/org/bonitasoft/engine/work/SWorkException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Charles Souillard\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class SWorkException extends SBonitaException {\n\n    private static final long serialVersionUID = 1L;\n\n    public SWorkException(final String message, final Throwable t) {\n        super(message, t);\n    }\n\n    public SWorkException(final Throwable t) {\n        super(t);\n    }\n\n    public SWorkException(final String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "services/bonita-work/src/main/java/org/bonitasoft/engine/work/SWorkPreconditionException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * Happens when a work could not be executed because its preconditions where not met.\n * e.g. NotifyChildFinishWork is executed on an unexisting or not finished flow node\n */\npublic class SWorkPreconditionException extends SBonitaException {\n\n    public SWorkPreconditionException(String message, Exception cause) {\n        super(message, cause);\n    }\n\n    public SWorkPreconditionException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "services/bonita-work/src/main/java/org/bonitasoft/engine/work/SWorkRegisterException.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\n\n/**\n * @author Charles Souillard\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class SWorkRegisterException extends SBonitaException {\n\n    private static final long serialVersionUID = 1L;\n\n    public SWorkRegisterException(final String message, final Throwable t) {\n        super(message, t);\n    }\n\n    /**\n     * @param string\n     */\n    public SWorkRegisterException(final String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "services/bonita-work/src/main/java/org/bonitasoft/engine/work/SuccessCallback.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work;\n\n/**\n * @author Baptiste Mesta.\n */\n@FunctionalInterface\npublic interface SuccessCallback {\n\n    void onSuccess(WorkDescriptor workDescriptor);\n}\n"
  },
  {
    "path": "services/bonita-work/src/main/java/org/bonitasoft/engine/work/WorkDescriptor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work;\n\nimport static org.apache.commons.lang3.builder.ToStringStyle.SHORT_PREFIX_STYLE;\n\nimport java.io.Serializable;\nimport java.time.Instant;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.UUID;\n\nimport org.apache.commons.lang3.builder.EqualsBuilder;\nimport org.apache.commons.lang3.builder.HashCodeBuilder;\nimport org.apache.commons.lang3.builder.ToStringBuilder;\n\n/**\n * @author Baptiste Mesta.\n */\npublic class WorkDescriptor implements Serializable {\n\n    private final String uuid = UUID.randomUUID().toString();\n    private final String type;\n    private final Map<String, Serializable> parameters;\n    private int retryCount = 0;\n    private Instant executionThreshold;\n    private int executionCount = 0;\n    private Instant registrationDate;\n    private boolean abnormalExecutionDetected = false;\n\n    public WorkDescriptor(String type) {\n        this.type = type;\n        this.parameters = new HashMap<>();\n    }\n\n    public String getUuid() {\n        return uuid;\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    public Serializable getParameter(String key) {\n        if (!parameters.containsKey(key)) {\n            throw new IllegalStateException(\n                    String.format(\"Parameter %s is not set on the work descriptor %s\", key, this));\n        }\n        return parameters.get(key);\n    }\n\n    public Long getLong(String key) {\n        return (Long) getParameter(key);\n    }\n\n    public Integer getInteger(String key) {\n        return (Integer) getParameter(key);\n    }\n\n    public Boolean getBoolean(String key) {\n        return (Boolean) getParameter(key);\n    }\n\n    public String getString(String key) {\n        return (String) getParameter(key);\n    }\n\n    public static WorkDescriptor create(String type) {\n        return new WorkDescriptor(type);\n    }\n\n    public WorkDescriptor withParameter(String key, Serializable value) {\n        parameters.put(key, value);\n        return this;\n    }\n\n    public Instant getExecutionThreshold() {\n        return executionThreshold;\n    }\n\n    public WorkDescriptor mustBeExecutedAfter(Instant mustBeExecutedAfter) {\n        this.executionThreshold = mustBeExecutedAfter;\n        return this;\n    }\n\n    public int getRetryCount() {\n        return retryCount;\n    }\n\n    public void incrementRetryCount() {\n        retryCount++;\n    }\n\n    public int getExecutionCount() {\n        return executionCount;\n    }\n\n    public void incrementExecutionCount() {\n        executionCount++;\n    }\n\n    public void setRegistrationDate(Instant registrationDate) {\n        this.registrationDate = registrationDate;\n    }\n\n    public Instant getRegistrationDate() {\n        return registrationDate;\n    }\n\n    public WorkDescriptor abnormalExecutionDetected() {\n        this.abnormalExecutionDetected = true;\n        return this;\n    }\n\n    public boolean isAbnormalExecutionDetected() {\n        return abnormalExecutionDetected;\n    }\n\n    public String getDescription() {\n        return new ToStringBuilder(this, SHORT_PREFIX_STYLE)\n                .append(type)\n                .append(\"parameters\", parameters).toString();\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o)\n            return true;\n        if (o == null || getClass() != o.getClass())\n            return false;\n        WorkDescriptor that = (WorkDescriptor) o;\n        return new EqualsBuilder()\n                .append(retryCount, that.retryCount)\n                .append(uuid, that.uuid)\n                .append(type, that.type)\n                .append(parameters, that.parameters)\n                .append(executionThreshold, that.executionThreshold)\n                .append(executionCount, that.executionCount)\n                .append(registrationDate, that.registrationDate)\n                .append(abnormalExecutionDetected, that.abnormalExecutionDetected)\n                .isEquals();\n    }\n\n    @Override\n    public int hashCode() {\n        return new HashCodeBuilder(17, 37)\n                .append(uuid)\n                .append(type)\n                .append(parameters)\n                .append(retryCount)\n                .append(executionThreshold)\n                .append(executionCount)\n                .append(registrationDate)\n                .append(abnormalExecutionDetected)\n                .toHashCode();\n    }\n\n    @Override\n    public String toString() {\n        return new ToStringBuilder(this, SHORT_PREFIX_STYLE)\n                .append(\"uuid\", uuid)\n                .append(\"type\", type)\n                .append(\"parameters\", parameters)\n                .append(\"retryCount\", retryCount)\n                .append(\"executionThreshold\", executionThreshold)\n                .append(\"executionCount\", executionCount)\n                .append(\"registrationDate\", registrationDate)\n                .append(\"abnormalExecutionDetected\", abnormalExecutionDetected)\n                .toString();\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-work/src/main/java/org/bonitasoft/engine/work/WorkExecutionCallback.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work;\n\nimport java.util.Map;\n\n/**\n * this a callback called when a work finish.\n *\n * @author Baptiste Mesta.\n */\npublic interface WorkExecutionCallback {\n\n    void onSuccess(WorkDescriptor workDescriptor);\n\n    void onFailure(WorkDescriptor work, BonitaWork bonitaWork, Map<String, Object> context,\n            Throwable thrown);\n\n}\n"
  },
  {
    "path": "services/bonita-work/src/main/java/org/bonitasoft/engine/work/WorkExecutorService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work;\n\nimport org.bonitasoft.engine.commons.TenantLifecycleService;\n\n/**\n * Trigger the asynchronous execution of a work\n *\n * @author Baptiste Mesta\n */\npublic interface WorkExecutorService extends TenantLifecycleService {\n\n    boolean isStopped();\n\n    void notifyNodeStopped(String nodeName);\n\n    void execute(WorkDescriptor work);\n}\n"
  },
  {
    "path": "services/bonita-work/src/main/java/org/bonitasoft/engine/work/WorkFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work;\n\n/**\n * @author Baptiste Mesta.\n */\npublic interface WorkFactory {\n\n    BonitaWork create(WorkDescriptor workDescriptor);\n\n}\n"
  },
  {
    "path": "services/bonita-work/src/main/java/org/bonitasoft/engine/work/WorkService.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work;\n\nimport org.bonitasoft.engine.commons.TenantLifecycleService;\n\n/**\n * This service allows register the execution of a work at the end of the current transaction\n *\n * @author Charles Souillard\n * @author Baptiste Mesta\n * @author Matthieu Chaffotte\n */\npublic interface WorkService extends TenantLifecycleService {\n\n    /**\n     * This operation MUST be called within an active transaction. If no active transaction is found, a\n     * SWorkRegisterException is thrown\n     *\n     * @param workDescriptor\n     * @throws SWorkRegisterException\n     * @since 6.0\n     */\n    void registerWork(WorkDescriptor workDescriptor) throws SWorkRegisterException;\n\n    /**\n     * @return true if the work service is stopped\n     * @since 6.3\n     */\n    boolean isStopped();\n\n}\n"
  },
  {
    "path": "services/bonita-work/src/main/java/org/bonitasoft/engine/work/WorkServiceImpl.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work;\n\nimport org.bonitasoft.engine.commons.time.EngineClock;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.bonitasoft.engine.transaction.STransactionNotFoundException;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.stereotype.Service;\n\n/**\n * Directly calls the WorkExecutorService\n *\n * @author Charles Souillard\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\n\n@Service(\"workService\")\npublic class WorkServiceImpl implements WorkService {\n\n    private final Logger log = LoggerFactory.getLogger(WorkServiceImpl.class);\n    private final UserTransactionService transactionService;\n    private final SessionAccessor sessionAccessor;\n    private final WorkExecutorService workExecutorService;\n    private final EngineClock engineClock;\n    private int workDelayOnMultipleXAResource;\n\n    public WorkServiceImpl(UserTransactionService transactionService,\n            SessionAccessor sessionAccessor,\n            WorkExecutorService workExecutorService,\n            EngineClock engineClock,\n            @Value(\"${bonita.tenant.work.${db.vendor}.delayOnMultipleXAResource:0}\") int workDelayOnMultipleXAResource) {\n        this.transactionService = transactionService;\n        this.sessionAccessor = sessionAccessor;\n        this.workExecutorService = workExecutorService;\n        this.engineClock = engineClock;\n        this.workDelayOnMultipleXAResource = workDelayOnMultipleXAResource;\n    }\n\n    @Override\n    public void registerWork(WorkDescriptor workDescriptor) throws SWorkRegisterException {\n        if (isStopped()) {\n            log.warn(\"Tried to register work {}, but the work service is stopped.\", workDescriptor);\n            return;\n        }\n        workDescriptor.setRegistrationDate(engineClock.now());\n        log.debug(\"Registering work {}\", workDescriptor);\n        createAndRegisterNewSynchronization(workDescriptor);\n        log.debug(\"Work registered\");\n    }\n\n    private WorkSynchronization createAndRegisterNewSynchronization(WorkDescriptor workDescriptor)\n            throws SWorkRegisterException {\n        WorkSynchronization synchro = new WorkSynchronization(transactionService, workExecutorService,\n                workDescriptor, workDelayOnMultipleXAResource);\n        try {\n            transactionService.registerBonitaSynchronization(synchro);\n        } catch (final STransactionNotFoundException e) {\n            throw new SWorkRegisterException(e.getMessage(), e);\n        }\n        return synchro;\n    }\n\n    @Override\n    public boolean isStopped() {\n        // the executor must handle elements when it's shutting down\n        return workExecutorService.isStopped();\n    }\n\n    @Override\n    public synchronized void stop() {\n\n    }\n\n    @Override\n    public synchronized void start() {\n        if (workDelayOnMultipleXAResource > 0) {\n            log.info(\n                    \"The property 'delayOnMultipleXAResource' is set to {} ms, that delay will be added when triggering works from a transaction having multiple XA resources. This is the case when the BDM is updated.\",\n                    workDelayOnMultipleXAResource);\n        }\n    }\n\n    @Override\n    public synchronized void resume() {\n        start();\n    }\n}\n"
  },
  {
    "path": "services/bonita-work/src/main/java/org/bonitasoft/engine/work/WorkSingleThreadPoolExecutorFactory.java",
    "content": "/**\n * Copyright (C) 2024 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work;\n\nimport java.util.concurrent.*;\n\nimport org.bonitasoft.engine.mdc.MDCTransmitingThreadPoolExecutor;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;\nimport org.springframework.stereotype.Component;\n\n@Component\n@ConditionalOnSingleCandidate(BonitaWorkExecutorFactory.class)\npublic class WorkSingleThreadPoolExecutorFactory implements BonitaWorkExecutorFactory {\n\n    private final int queueCapacity;\n\n    public WorkSingleThreadPoolExecutorFactory(\n            @Value(\"${bonita.tenant.work.queueCapacity}\") int queueCapacity) {\n        this.queueCapacity = queueCapacity;\n    }\n\n    @Override\n    public ThreadPoolExecutor create() {\n        return new SingleThreadPoolExecutor(new ArrayBlockingQueue<>(queueCapacity),\n                new WorkerThreadFactory(\"Bonita-Worker\"));\n    }\n\n    public static class SingleThreadPoolExecutor extends MDCTransmitingThreadPoolExecutor {\n\n        public SingleThreadPoolExecutor(final BlockingQueue<Runnable> workQueue,\n                final ThreadFactory threadFactory) {\n            super(1, 1, 0, TimeUnit.MILLISECONDS, workQueue, threadFactory, new QueueRejectedExecutionHandler());\n        }\n\n        @Override\n        public Future<?> submit(final Runnable task) {\n            // only submit if not shutdown\n            if (!isShutdown()) {\n                return super.submit(task);\n\n            }\n            return null;\n        }\n\n    }\n\n    public static class QueueRejectedExecutionHandler implements RejectedExecutionHandler {\n\n        private static final Logger logger = LoggerFactory.getLogger(QueueRejectedExecutionHandler.class);\n\n        @Override\n        public void rejectedExecution(final Runnable task, final ThreadPoolExecutor executor) {\n            if (executor.isShutdown()) {\n                logger.info(\n                        \"Tried to run work {} but the work service is shutdown. work will be restarted with the node\",\n                        task);\n            } else {\n                throw new RejectedExecutionException(\n                        \"Unable to run the task \"\n                                + task\n                                + \"\\n your work queue is full you might consider changing your configuration to scale more. See parameter 'bonita.tenant.work.queueCapacity' in bonita-tenant-community.properties configuration files.\");\n            }\n        }\n\n    }\n}\n"
  },
  {
    "path": "services/bonita-work/src/main/java/org/bonitasoft/engine/work/WorkSynchronization.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work;\n\nimport java.time.Instant;\n\nimport javax.transaction.Status;\n\nimport org.bonitasoft.engine.transaction.BonitaTransactionSynchronization;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class WorkSynchronization implements BonitaTransactionSynchronization {\n\n    private static final Logger LOG = LoggerFactory.getLogger(WorkSynchronization.class);\n\n    private final WorkDescriptor work;\n\n    private final WorkExecutorService workExecutorService;\n\n    private final UserTransactionService transactionService;\n    private final int workDelayOnMultipleXAResource;\n\n    WorkSynchronization(final UserTransactionService transactionService, final WorkExecutorService workExecutorService,\n            WorkDescriptor work, int workDelayOnMultipleXAResource) {\n        this.transactionService = transactionService;\n        this.workDelayOnMultipleXAResource = workDelayOnMultipleXAResource;\n        this.work = work;\n        this.workExecutorService = workExecutorService;\n    }\n\n    WorkDescriptor getWork() {\n        return work;\n    }\n\n    @Override\n    public void afterCompletion(final int transactionStatus) {\n        if (Status.STATUS_COMMITTED == transactionStatus) {\n            if (workDelayOnMultipleXAResource > 0) {\n                transactionService.hasMultipleResources().ifPresentOrElse(\n                        hasMultiple -> {\n                            if (Boolean.TRUE.equals(hasMultiple)) {\n                                work.mustBeExecutedAfter(Instant.now().plusMillis(workDelayOnMultipleXAResource));\n                            }\n                        },\n                        // to be safe, if we are unable to know if there are multiple resources, we add the delay anyway.\n                        () -> work.mustBeExecutedAfter(Instant.now().plusMillis(workDelayOnMultipleXAResource)));\n            }\n            workExecutorService.execute(work);\n        } else {\n            LOG.debug(\"Transaction completion with state {} != COMMITTED. Not triggering the work: {}\",\n                    transactionStatus, work);\n        }\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-work/src/main/java/org/bonitasoft/engine/work/WorkerThreadFactory.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work;\n\nimport java.util.concurrent.ThreadFactory;\nimport java.util.concurrent.atomic.AtomicInteger;\n\n/**\n * @author Baptiste Mesta\n * @author Celine Souchet\n */\npublic class WorkerThreadFactory implements ThreadFactory {\n\n    private final AtomicInteger nbThread = new AtomicInteger(1);\n\n    private final String name;\n\n    private final int padding;\n\n    public WorkerThreadFactory(final String name, final int maximumPoolSize) {\n        this.name = name;\n        this.padding = guessPadding(maximumPoolSize);\n    }\n\n    public WorkerThreadFactory(final String name) {\n        this(name, 1);\n    }\n\n    /**\n     * @param maximumPoolSize\n     */\n    static int guessPadding(final int maximumPoolSize) {\n        int tmpPadding = 0;\n        int poolSize = maximumPoolSize;\n        while (poolSize > 0) {\n            poolSize /= 10;\n            tmpPadding++;\n        }\n        return tmpPadding;\n    }\n\n    @Override\n    public Thread newThread(final Runnable runnable) {\n        final StringBuilder builder = new StringBuilder();\n        builder.append(name);\n        builder.append(\"-\");\n        builder.append(\"%0\");\n        builder.append(padding);\n        builder.append(\"d\");\n        final String format = String.format(builder.toString(), nbThread.getAndIncrement());\n        return new Thread(runnable, format);\n    }\n}\n"
  },
  {
    "path": "services/bonita-work/src/main/java/org/bonitasoft/engine/work/audit/AuditListener.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work.audit;\n\nimport org.bonitasoft.engine.work.WorkDescriptor;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\n\n/**\n * Default implementation backed to a slf4j logger.\n */\n@Component\npublic class AuditListener {\n\n    private static final Logger WORK_AUDIT = LoggerFactory.getLogger(\"BONITA_WORK_AUDIT.EXECUTION\");\n\n    public void detectionStarted(WorkDescriptor work) {\n        WORK_AUDIT.debug(\"Start detection for execution #{} of {}\", work.getExecutionCount(), work);\n    }\n\n    public void abnormalExecutionStatusDetected(WorkDescriptor work, ExecutionStatus executionStatus) {\n        WORK_AUDIT.warn(\"Potential abnormal execution detected - cause {}. {}\", executionStatus, work);\n    }\n\n    public void success(WorkDescriptor work) {\n        WORK_AUDIT.info(\"Work successfully executed. {}\", work);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-work/src/main/java/org/bonitasoft/engine/work/audit/ExecutionStatus.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work.audit;\n\npublic enum ExecutionStatus {\n\n    OK(true), TOO_MANY_EXECUTIONS(false), TOO_MUCH_TIME_ELAPSED_SINCE_REGISTRATION(false);\n\n    private final boolean isNormalExecution;\n\n    ExecutionStatus(boolean isNormalExecution) {\n        this.isNormalExecution = isNormalExecution;\n    }\n\n    public boolean isNormalExecution() {\n        return isNormalExecution;\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-work/src/main/java/org/bonitasoft/engine/work/audit/WorkExecutionAuditor.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work.audit;\n\nimport static org.bonitasoft.engine.work.audit.ExecutionStatus.*;\n\nimport java.time.Duration;\nimport java.time.temporal.ChronoUnit;\n\nimport org.bonitasoft.engine.commons.time.EngineClock;\nimport org.bonitasoft.engine.work.WorkDescriptor;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class WorkExecutionAuditor {\n\n    private static final Logger log = LoggerFactory.getLogger(WorkExecutionAuditor.class);\n\n    private final EngineClock engineClock;\n    private final AuditListener auditListener;\n\n    private boolean activated = true;\n    private final int executionCountThreshold;\n    private final Duration executionCountDurationThreshold;\n    private final Duration registrationDurationElapsedThreshold;\n\n    public WorkExecutionAuditor(EngineClock engineClock,\n            AuditListener auditListener,\n            RegistrationDurationElapsedCheckConfig registrationDurationElapsedCheckConfig,\n            ExecutionCountCheckConfig executionCountCheckConfig) {\n        this.engineClock = engineClock;\n        this.auditListener = auditListener;\n        this.registrationDurationElapsedThreshold = registrationDurationElapsedCheckConfig.duration;\n        this.executionCountThreshold = executionCountCheckConfig.executionCountThreshold;\n        this.executionCountDurationThreshold = executionCountCheckConfig.executionDurationThreshold;\n    }\n\n    @Value(\"${bonita.tenant.work.audit.activated:true}\")\n    public void setActivated(boolean activated) {\n        this.activated = activated;\n    }\n\n    /**\n     * Notify the listener in case of abnormal execution. Cases considered as 'abnormal execution'\n     * <ul>\n     * <li>large number of executions after a duration threshold since registration has elapsed</li>\n     * <li>large duration since the work has been registered</li>\n     * </ul>\n     * <b>NOTE</b>: the listener receives the 'abnormal execution' status only once to avoid flooding it.\n     *\n     * @param work the work descriptor to inspect\n     */\n    public void detectAbnormalExecutionAndNotify(WorkDescriptor work) {\n        if (!activated) {\n            return;\n        }\n        auditListener.detectionStarted(work);\n        if (work.getRegistrationDate() == null) {\n            log.warn(\"No registration date available, unable to detect abnormal work execution status. {}\", work);\n            return;\n        }\n\n        if (!work.isAbnormalExecutionDetected()) {\n            final ExecutionStatus executionStatus = executionStatus(work);\n            if (!executionStatus.isNormalExecution()) {\n                work.abnormalExecutionDetected();\n                auditListener.abnormalExecutionStatusDetected(work, executionStatus);\n            }\n        }\n    }\n\n    /**\n     * Only notify if execution has been detected as abnormal\n     */\n    public void notifySuccess(WorkDescriptor work) {\n        if (work.isAbnormalExecutionDetected()) {\n            auditListener.success(work);\n        }\n    }\n\n    // Visible for Testing\n    ExecutionStatus executionStatus(WorkDescriptor work) {\n        Duration durationSinceWorkRegistration = Duration.between(work.getRegistrationDate(), engineClock.now());\n        if (durationSinceWorkRegistration.compareTo(executionCountDurationThreshold) >= 0\n                && work.getExecutionCount() >= executionCountThreshold) {\n            return TOO_MANY_EXECUTIONS;\n        }\n        if (durationSinceWorkRegistration.compareTo(registrationDurationElapsedThreshold) >= 0) {\n            return TOO_MUCH_TIME_ELAPSED_SINCE_REGISTRATION;\n        }\n\n        return OK;\n    }\n\n    @Component\n    public static class RegistrationDurationElapsedCheckConfig {\n\n        private final Duration duration;\n\n        public RegistrationDurationElapsedCheckConfig(\n                @Value(\"${bonita.tenant.work.audit.abnormal.execution.threshold.elapsed_duration_since_registration_amount:30}\") int amount,\n                @Value(\"${bonita.tenant.work.audit.abnormal.execution.threshold.elapsed_duration_since_registration_unit:MINUTES}\") ChronoUnit unit) {\n            this.duration = Duration.of(amount, unit);\n        }\n\n    }\n\n    @Component\n    public static class ExecutionCountCheckConfig {\n\n        private final int executionCountThreshold;\n        private final Duration executionDurationThreshold;\n\n        public ExecutionCountCheckConfig(\n                @Value(\"${bonita.tenant.work.audit.abnormal.execution.threshold.execution_count:10}\") int executionCountThreshold,\n                @Value(\"${bonita.tenant.work.audit.abnormal.execution.threshold.execution_count_duration_amount:10}\") int durationThresholdAmount,\n                @Value(\"${bonita.tenant.work.audit.abnormal.execution.threshold.execution_count_duration_unit:MINUTES}\") ChronoUnit durationThresholdUnit) {\n            this.executionCountThreshold = executionCountThreshold;\n            this.executionDurationThreshold = Duration.of(durationThresholdAmount, durationThresholdUnit);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-work/src/test/java/org/bonitasoft/engine/work/DefaultBonitaExecutorServiceFactoryTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\n\nimport io.micrometer.core.instrument.simple.SimpleMeterRegistry;\nimport org.bonitasoft.engine.commons.time.DefaultEngineClock;\nimport org.bonitasoft.engine.monitoring.DefaultExecutorServiceMetricsProvider;\nimport org.bonitasoft.engine.work.audit.WorkExecutionAuditor;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class DefaultBonitaExecutorServiceFactoryTest {\n\n    @Mock\n    private WorkFactory workFactory;\n    @Mock\n    private WorkExecutionCallback workExecutionCallback;\n\n    @Test\n    public void createExecutorService_should_register_ExecutorServiceMetrics() {\n        // given:\n        final SimpleMeterRegistry meterRegistry = new SimpleMeterRegistry();\n        DefaultBonitaExecutorServiceFactory defaultBonitaExecutorServiceFactory = new DefaultBonitaExecutorServiceFactory(\n                meterRegistry,\n                new DefaultEngineClock(),\n                workFactory,\n                mock(WorkExecutionAuditor.class),\n                new DefaultExecutorServiceMetricsProvider(),\n                new WorkSingleThreadPoolExecutorFactory(10));\n\n        // when:\n        defaultBonitaExecutorServiceFactory.createExecutorService(workExecutionCallback);\n\n        // then:\n        assertThat(\n                meterRegistry.find(\"executor.pool.size\")\n                        .tag(\"name\", \"bonita-work-executor\")\n                        .gauge())\n                .isNotNull();\n    }\n\n    @Test\n    public void should_not_have_metrics_when_unbind_is_called() {\n        // given:\n        final SimpleMeterRegistry meterRegistry = new SimpleMeterRegistry();\n        DefaultBonitaExecutorServiceFactory defaultBonitaExecutorServiceFactory = new DefaultBonitaExecutorServiceFactory(\n                meterRegistry,\n                new DefaultEngineClock(),\n                workFactory,\n                mock(WorkExecutionAuditor.class),\n                new DefaultExecutorServiceMetricsProvider(),\n                new WorkSingleThreadPoolExecutorFactory(10));\n\n        // when:\n        defaultBonitaExecutorServiceFactory.createExecutorService(workExecutionCallback);\n        defaultBonitaExecutorServiceFactory.unbind();\n\n        // then:\n        assertThat(\n                meterRegistry.find(\"executor.pool.size\")\n                        .tag(\"name\", \"bonita-work-executor\")\n                        .gauge())\n                .isNull();\n    }\n}\n"
  },
  {
    "path": "services/bonita-work/src/test/java/org/bonitasoft/engine/work/DefaultBonitaExecutorServiceTest.java",
    "content": "/**\n * Copyright (C) 2017-2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work;\n\nimport static java.time.temporal.ChronoUnit.SECONDS;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.awaitility.Awaitility.await;\n\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Map;\nimport java.util.concurrent.*;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport io.micrometer.core.instrument.Clock;\nimport io.micrometer.core.instrument.Gauge;\nimport io.micrometer.core.instrument.simple.SimpleMeterRegistry;\nimport org.bonitasoft.engine.commons.time.FixedEngineClock;\nimport org.bonitasoft.engine.work.audit.WorkExecutionAuditor;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnit;\nimport org.mockito.junit.MockitoRule;\n\n/**\n * @author Baptiste Mesta.\n */\npublic class DefaultBonitaExecutorServiceTest {\n\n    @Rule\n    public MockitoRule mockitoRule = MockitoJUnit.rule();\n    @Mock\n    private WorkExecutionAuditor workExecutionAuditor;\n    private final MyWorkExecutionCallback workExecutionCallback = new MyWorkExecutionCallback();\n    private DefaultBonitaExecutorService bonitaExecutorService;\n    private final FixedEngineClock engineClock = new FixedEngineClock(Instant.now());\n    private final WorkFactory workFactory = new LocalWorkFactory(2);\n    private final SimpleMeterRegistry meterRegistry = new SimpleMeterRegistry(\n            // So that micrometer updates its counters every 1 ms:\n            k -> k.equals(\"simple.step\") ? Duration.ofMillis(1).toString() : null,\n            Clock.SYSTEM);\n\n    @Before\n    public void before() {\n        var threadPoolExecutor = new WorkSingleThreadPoolExecutorFactory.SingleThreadPoolExecutor(\n                new LinkedBlockingQueue<>(10),\n                new WorkerThreadFactory(\"test-worker\", 1));\n        bonitaExecutorService = new DefaultBonitaExecutorService(threadPoolExecutor, workFactory, engineClock,\n                workExecutionCallback, workExecutionAuditor,\n                meterRegistry);\n    }\n\n    @Test\n    public void should_call_on_success_callback_when_work_executed_properly() {\n        WorkDescriptor workDescriptor = WorkDescriptor.create(\"NORMAL\");\n\n        bonitaExecutorService.submit(workDescriptor);\n\n        await().until(workExecutionCallback::isOnSuccessCalled);\n        assertThat(workExecutionCallback.isOnFailureCalled()).isFalse();\n    }\n\n    @Test\n    public void should_call_on_failure_callback_when_work_executed_properly() {\n        WorkDescriptor workDescriptor = WorkDescriptor.create(\"EXCEPTION\");\n\n        bonitaExecutorService.submit(workDescriptor);\n\n        await().until(workExecutionCallback::isOnFailureCalled);\n        assertThat(workExecutionCallback.isOnSuccessCalled()).isFalse();\n    }\n\n    @Test\n    public void should_execute_work_after_specified_date() {\n        WorkDescriptor workDescriptor = WorkDescriptor.create(\"NORMAL\");\n        workDescriptor.mustBeExecutedAfter(Instant.now().plus(5, SECONDS));\n\n        bonitaExecutorService.submit(workDescriptor);\n\n        //should still not be executed\n        engineClock.addTime(1, SECONDS);\n        await().atLeast(50, TimeUnit.MILLISECONDS)\n                .untilAsserted(() -> assertThat(workExecutionCallback.isOnSuccessCalled()).isFalse());\n\n        //add time, work should be executed\n        engineClock.addTime(5, SECONDS);\n        await().until(workExecutionCallback::isOnSuccessCalled);\n    }\n\n    @Test\n    public void should_update_meter_when_work_executes() {\n        Gauge currentWorkQueue = meterRegistry.find(DefaultBonitaExecutorService.NUMBER_OF_WORKS_PENDING).gauge();\n        for (int i = 0; i <= 4; i++) {\n            WorkDescriptor workDescriptor = WorkDescriptor.create(\"SLEEP\");\n            bonitaExecutorService.submit(workDescriptor);\n        }\n        await().untilAsserted(() -> assertThat(currentWorkQueue.value()).isPositive());\n    }\n\n    @Test\n    public void should_update_works_counters_when_enqueuing_workDescriptor_with_long_processing_time()\n            throws Exception {\n\n        //when:\n        bonitaExecutorService.submit(WorkDescriptor.create(\"NORMAL\"));\n        bonitaExecutorService.submit(WorkDescriptor.create(\"SLEEP\"));\n        bonitaExecutorService.submit(WorkDescriptor.create(\"SLEEP\"));\n        bonitaExecutorService.submit(WorkDescriptor.create(\"SLEEP\"));\n        bonitaExecutorService.submit(WorkDescriptor.create(\"SLEEP\"));\n        bonitaExecutorService.submit(WorkDescriptor.create(\"NORMAL\"));\n        TimeUnit.MILLISECONDS.sleep(50); // give some time to the executor to process the work\n\n        //then:\n        // 1 executed because normal work is submitted first.\n        assertThat(meterRegistry.find(DefaultBonitaExecutorService.NUMBER_OF_WORKS_EXECUTED).counter().count())\n                .as(\"Executed works number\").isEqualTo(1);\n        // 1 running works because we have 1 threads in the pool and sleeping works wait for 2s to execute:\n        assertThat(meterRegistry.find(DefaultBonitaExecutorService.NUMBER_OF_WORKS_RUNNING).gauge().value())\n                .as(\"Running works number\").isEqualTo(1);\n        // 4 pending works because the single thread is busy, so the last 4 works are in the queue:\n        assertThat(meterRegistry.find(DefaultBonitaExecutorService.NUMBER_OF_WORKS_PENDING).gauge().value())\n                .as(\"Pending works number\").isEqualTo(4);\n    }\n\n    @Test\n    public void should_have_no_meters_after_shutdown() {\n        var work = bonitaExecutorService.submit(WorkDescriptor.create(\"NORMAL\"));\n        await().until(work::isDone);\n\n        bonitaExecutorService.shutdownAndEmptyQueue();\n\n        assertThat(meterRegistry.getMeters()).isEmpty();\n    }\n\n    @Test\n    public void should_call_on_success_callback_only_when_async_work_executed_properly() {\n        WorkDescriptor workDescriptor = WorkDescriptor.create(\"ASYNC\");\n\n        var work = bonitaExecutorService.submit(workDescriptor);\n        await().until(work::isDone);\n\n        assertThat(workExecutionCallback.isOnSuccessCalled()).isFalse();\n        assertThat(workExecutionCallback.isOnFailureCalled()).isFalse();\n\n        await().until(workExecutionCallback::isOnSuccessCalled);\n        assertThat(workExecutionCallback.isOnFailureCalled()).isFalse();\n    }\n\n    @Test\n    public void should_call_on_failure_callback_ony_when_async_work_executed_properly() {\n        WorkDescriptor workDescriptor = WorkDescriptor.create(\"ASYNC_EXCEPTION\");\n\n        var work = bonitaExecutorService.submit(workDescriptor);\n        await().until(work::isDone);\n\n        assertThat(workExecutionCallback.isOnSuccessCalled()).isFalse();\n        assertThat(workExecutionCallback.isOnFailureCalled()).isFalse();\n\n        await().until(workExecutionCallback::isOnFailureCalled);\n        assertThat(workExecutionCallback.isOnSuccessCalled()).isFalse();\n        assertThat(workExecutionCallback.getThrown()).hasMessage(\"my exception\").isInstanceOf(SWorkException.class);\n    }\n\n    // =================================================================================================================\n    // UTILS\n    // =================================================================================================================\n\n    private static class MyWorkExecutionCallback implements WorkExecutionCallback {\n\n        private final AtomicBoolean onSuccessCalled = new AtomicBoolean(false);\n        private final AtomicBoolean onFailureCalled = new AtomicBoolean(false);\n        private Throwable thrown;\n\n        @Override\n        public void onSuccess(WorkDescriptor workDescriptor) {\n            onSuccessCalled.set(true);\n        }\n\n        @Override\n        public void onFailure(WorkDescriptor work, BonitaWork bonitaWork, Map<String, Object> context,\n                Throwable thrown) {\n            this.thrown = thrown;\n            onFailureCalled.set(true);\n        }\n\n        public boolean isOnSuccessCalled() {\n            return onSuccessCalled.get();\n        }\n\n        public boolean isOnFailureCalled() {\n            return onFailureCalled.get();\n        }\n\n        public Throwable getThrown() {\n            return thrown;\n        }\n    }\n\n    private record LocalWorkFactory(long workSleepPeriodInSeconds) implements WorkFactory {\n\n        @Override\n        public BonitaWork create(WorkDescriptor workDescriptor) {\n            return new BonitaWork() {\n\n                @Override\n                public String getDescription() {\n                    return workDescriptor.toString();\n                }\n\n                @Override\n                public CompletableFuture<Void> work(Map<String, Object> context) throws Exception {\n                    switch (workDescriptor.getType()) {\n                        case \"EXCEPTION\":\n                            throw new Exception(\"classic exception\");\n                        case \"SLEEP\":\n                            TimeUnit.SECONDS.sleep(workSleepPeriodInSeconds);\n                            break;\n                        case \"ASYNC\":\n                            return CompletableFuture.supplyAsync(() -> {\n                                try {\n                                    TimeUnit.MILLISECONDS.sleep(200);\n                                } catch (InterruptedException ignored) {\n                                }\n                                return null;\n                            }, Executors.newSingleThreadExecutor());\n                        case \"ASYNC_EXCEPTION\":\n                            return CompletableFuture.supplyAsync(() -> {\n                                try {\n                                    TimeUnit.MILLISECONDS.sleep(200);\n                                } catch (InterruptedException ignored) {\n                                }\n                                throw new CompletionException(new SWorkException(\"my exception\"));\n                            }, Executors.newSingleThreadExecutor());\n                        case \"NORMAL\":\n                        default:\n                    }\n                    return CompletableFuture.completedFuture(null);\n                }\n\n                @Override\n                public void handleFailure(Throwable e, Map<String, Object> context) {\n                    // do nothing\n                }\n            };\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-work/src/test/java/org/bonitasoft/engine/work/DefaultExceptionRetryabilityEvaluatorTest.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.work.ExceptionRetryabilityEvaluator.Retryability.NOT_RETRYABLE;\nimport static org.bonitasoft.engine.work.ExceptionRetryabilityEvaluator.Retryability.RETRYABLE;\nimport static org.bonitasoft.engine.work.ExceptionRetryabilityEvaluator.Retryability.UNCERTAIN_COMPLETION_OF_COMMIT;\n\nimport java.io.IOException;\nimport java.net.PortUnreachableException;\nimport java.sql.SQLException;\nimport java.sql.SQLTransientException;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport javax.transaction.xa.XAException;\n\nimport org.bonitasoft.engine.commons.exceptions.SRetryableException;\nimport org.bonitasoft.engine.transaction.STransactionCommitException;\nimport org.junit.Test;\nimport org.springframework.jdbc.CannotGetJdbcConnectionException;\n\n/**\n * @author Baptiste Mesta.\n */\npublic class DefaultExceptionRetryabilityEvaluatorTest {\n\n    private DefaultExceptionRetryabilityEvaluator defaultExceptionRetryabilityEvaluator;\n\n    @Test\n    public void should_be_retryable_if_exception_is_retryable() {\n        initWith(SRetryableException.class);\n\n        ExceptionRetryabilityEvaluator.Retryability shouldBeRetried = defaultExceptionRetryabilityEvaluator\n                .evaluateRetryability(new SRetryableException(new IllegalStateException(\"a retryable exception\")));\n\n        assertThat(shouldBeRetried).isEqualTo(RETRYABLE);\n    }\n\n    @Test\n    public void should_be_retryable_if_some_exception_in_hierarchy_is_retryable() {\n        initWith(SRetryableException.class);\n\n        ExceptionRetryabilityEvaluator.Retryability shouldBeRetried = defaultExceptionRetryabilityEvaluator\n                .evaluateRetryability(new IllegalStateException(\n                        (new SRetryableException(new IllegalStateException(\"a retryable exception\")))));\n\n        assertThat(shouldBeRetried).isEqualTo(RETRYABLE);\n    }\n\n    private static class MySRetryableException extends SRetryableException {\n\n        public MySRetryableException() {\n            super(new IllegalArgumentException());\n        }\n    }\n\n    @Test\n    public void should_be_retryable_if_some_exception_is_extending_a_retryable() {\n        initWith(SRetryableException.class);\n\n        SRetryableException myRetryable = new MySRetryableException();\n        ExceptionRetryabilityEvaluator.Retryability shouldBeRetried = defaultExceptionRetryabilityEvaluator\n                .evaluateRetryability(new IllegalStateException(\n                        (myRetryable)));\n\n        assertThat(shouldBeRetried).isEqualTo(RETRYABLE);\n    }\n\n    @Test\n    public void should_be_retryable_if_exception_is_retryable_with_multiple_exception_in_configuration() {\n        initWith(SRetryableException.class, PortUnreachableException.class);\n\n        ExceptionRetryabilityEvaluator.Retryability shouldBeRetried = defaultExceptionRetryabilityEvaluator\n                .evaluateRetryability(new IllegalStateException(\n                        (new PortUnreachableException(\"unreached port\"))));\n\n        assertThat(shouldBeRetried).isEqualTo(RETRYABLE);\n    }\n\n    @Test\n    public void should_be_not_retryable_if_exception_is_not_a_retryable() {\n        initWith(SRetryableException.class);\n\n        ExceptionRetryabilityEvaluator.Retryability shouldBeRetried = defaultExceptionRetryabilityEvaluator\n                .evaluateRetryability(new IllegalStateException(\"a retryable exception\"));\n\n        assertThat(shouldBeRetried).isEqualTo(NOT_RETRYABLE);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void should_constructor_fail_if_classname_do_not_exists() {\n        new DefaultExceptionRetryabilityEvaluator(Collections.singletonList(\"unkown class\"), Collections.emptyList());\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void should_constructor_fail_if_class_is_not_throwable() {\n        new DefaultExceptionRetryabilityEvaluator(Collections.singletonList(\"java.util.List\"), Collections.emptyList());\n    }\n\n    @Test\n    public void should_be_not_retryable_if_contains_retryable_exception_with_caused_by_blacklisted_exception() {\n        initWithBlackListed(SRetryableException.class, IllegalStateException.class);\n\n        ExceptionRetryabilityEvaluator.Retryability shouldBeRetried = defaultExceptionRetryabilityEvaluator\n                .evaluateRetryability(new SRetryableException(new IllegalStateException()));\n\n        assertThat(shouldBeRetried).isEqualTo(NOT_RETRYABLE);\n    }\n\n    @Test\n    public void should_be_not_retryable_if_contains_blacklisted_exception_even_if_cause_is_retryable() {\n        initWithBlackListed(SRetryableException.class, IllegalStateException.class);\n\n        ExceptionRetryabilityEvaluator.Retryability shouldBeRetried = defaultExceptionRetryabilityEvaluator\n                .evaluateRetryability(new IllegalStateException(new SRetryableException(new IOException())));\n\n        assertThat(shouldBeRetried).isEqualTo(NOT_RETRYABLE);\n    }\n\n    @Test\n    public void should_retry_sql_connection_exceptions() {\n        initWith(SRetryableException.class);\n\n        assertThat(defaultExceptionRetryabilityEvaluator\n                .evaluateRetryability(new IllegalStateException(new SQLException(\"connection issue\"))))\n                .isEqualTo(RETRYABLE);\n        assertThat(defaultExceptionRetryabilityEvaluator\n                .evaluateRetryability(new CannotGetJdbcConnectionException(\"I/O error\"))).isEqualTo(RETRYABLE);\n    }\n\n    @Test\n    public void should_be_UNCERTAIN_COMPLETION_OF_COMMIT_when_retryable_exception_happen_on_commit() {\n        initWith(SRetryableException.class);\n\n        assertThat(defaultExceptionRetryabilityEvaluator\n                .evaluateRetryability(new STransactionCommitException(new SQLException(\"connection issue\"))))\n                .isEqualTo(UNCERTAIN_COMPLETION_OF_COMMIT);\n    }\n\n    @Test\n    public void should_not_be_UNCERTAIN_COMPLETION_OF_COMMIT_when_retryable_exception_happen_on_commit_but_wat_not_connection_issue() {\n        initWith(SRetryableException.class);\n\n        assertThat(defaultExceptionRetryabilityEvaluator.evaluateRetryability(\n                new STransactionCommitException(new SRetryableException(new IOException(\"some file issue\")))))\n                .isEqualTo(RETRYABLE);\n    }\n\n    @Test\n    public void should_retry_retryable_xa_exceptions() {\n        initWithBlackListed(SRetryableException.class, IllegalStateException.class);\n\n        assertThat(\n                defaultExceptionRetryabilityEvaluator.evaluateRetryability(new XAException(XAException.XA_RBTIMEOUT)))\n                .isEqualTo(RETRYABLE);\n    }\n\n    @Test\n    public void should_retry_when_exception_is_nested() {\n        initWithBlackListed(SQLTransientException.class, IllegalStateException.class);\n\n        assertThat(defaultExceptionRetryabilityEvaluator\n                .evaluateRetryability(new ExceptionThatHasNested(new SQLTransientException()))).isEqualTo(RETRYABLE);\n    }\n\n    @SafeVarargs\n    private final void initWith(Class<? extends Throwable>... throwables) {\n        defaultExceptionRetryabilityEvaluator = new DefaultExceptionRetryabilityEvaluator(toListNames(throwables),\n                Collections.emptyList());\n    }\n\n    @SafeVarargs\n    private final void initWithBlackListed(Class<? extends Throwable> toRetry,\n            Class<? extends Throwable>... blackListed) {\n        defaultExceptionRetryabilityEvaluator = new DefaultExceptionRetryabilityEvaluator(\n                toListNames(toRetry), toListNames(blackListed));\n    }\n\n    private List<String> toListNames(Class<? extends Throwable>... blackListed) {\n        return Arrays.asList(blackListed).stream().map(Class::getName).collect(Collectors.toList());\n    }\n\n    class ExceptionThatHasNested extends Exception {\n\n        List<Throwable> exceptions;\n\n        ExceptionThatHasNested(Throwable... exceptions) {\n            super(\"exception with nested\");\n            this.exceptions = Arrays.asList(exceptions);\n        }\n\n        public List<Throwable> getExceptions() {\n            return exceptions;\n        }\n    }\n}\n"
  },
  {
    "path": "services/bonita-work/src/test/java/org/bonitasoft/engine/work/RetryingWorkExecutorServiceTest.java",
    "content": "/**\n * Copyright (C) 2020 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work;\n\nimport static java.util.Collections.emptyMap;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.work.ExceptionRetryabilityEvaluator.Retryability.*;\nimport static org.bonitasoft.engine.work.RetryingWorkExecutorService.NUMBER_OF_WORKS_RETRIED;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.*;\n\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.concurrent.TimeUnit;\n\nimport io.micrometer.core.instrument.Clock;\nimport io.micrometer.core.instrument.MeterRegistry;\nimport io.micrometer.core.instrument.simple.SimpleMeterRegistry;\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.exceptions.SRetryableException;\nimport org.bonitasoft.engine.commons.time.FixedEngineClock;\nimport org.bonitasoft.engine.incident.IncidentService;\nimport org.bonitasoft.engine.transaction.STransactionCommitException;\nimport org.bonitasoft.engine.work.audit.WorkExecutionAuditor;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.contrib.java.lang.system.SystemOutRule;\nimport org.mockito.InOrder;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnit;\nimport org.mockito.junit.MockitoRule;\n\n/**\n * @author Baptiste Mesta.\n */\npublic class RetryingWorkExecutorServiceTest {\n\n    private static final int MAX_RETRY = 4;\n    private static final int WORK_TERMINATION_TIMEOUT = 30;\n    private static final int DELAY = 1000;\n    private static final int DELAY_FACTOR = 2;\n\n    @Rule\n    public SystemOutRule systemOutRule = new SystemOutRule().enableLog();\n    @Rule\n    public MockitoRule mockitoRule = MockitoJUnit.rule();\n    private RetryingWorkExecutorService workExecutorService;\n    @Mock\n    private BonitaExecutorServiceFactory bonitaExecutorServiceFactory;\n    @Mock(lenient = true)\n    private BonitaExecutorService bonitaExecutorService;\n    @Mock\n    private BonitaWork bonitaWork;\n    private final WorkDescriptor workDescriptor = WorkDescriptor.create(\"myWork\");\n    @Mock\n    private WorkExecutionAuditor workExecutionAuditor;\n    @Mock\n    private IncidentService incidentService;\n    @Mock(lenient = true)\n    private ExceptionRetryabilityEvaluator retryabilityEvaluator;\n    private final FixedEngineClock engineClock = new FixedEngineClock(Instant.EPOCH);\n\n    private final MeterRegistry meterRegistry = new SimpleMeterRegistry(\n            // So that micrometer updates its counters every 1 ms:\n            k -> k.equals(\"simple.step\") ? Duration.ofMillis(1).toString() : null,\n            Clock.SYSTEM);\n\n    @Before\n    public void before() throws Exception {\n        doReturn(bonitaExecutorService).when(bonitaExecutorServiceFactory).createExecutorService(any());\n        doReturn(RETRYABLE).when(retryabilityEvaluator)\n                .evaluateRetryability(argThat(t -> t instanceof SRetryableException));\n        doReturn(NOT_RETRYABLE).when(retryabilityEvaluator)\n                .evaluateRetryability(argThat(t -> !(t instanceof SRetryableException)));\n        workExecutorService = new RetryingWorkExecutorService(\n                bonitaExecutorServiceFactory, engineClock, WORK_TERMINATION_TIMEOUT, MAX_RETRY, DELAY, DELAY_FACTOR,\n                retryabilityEvaluator,\n                workExecutionAuditor, meterRegistry, incidentService);\n        doReturn(true).when(bonitaExecutorService).awaitTermination(anyLong(), any(TimeUnit.class));\n        workExecutorService.start();\n    }\n\n    @Test\n    public void should_submit_work_on_the_executor() throws Exception {\n\n        workExecutorService.execute(workDescriptor);\n\n        verify(bonitaExecutorService).submit(eq(workDescriptor));\n    }\n\n    @Test\n    public void should_pause_shutdown_ThreadPool_and_clear_queue() throws InterruptedException, SBonitaException {\n        final InOrder inOrder = inOrder(bonitaExecutorService);\n        // given\n        workExecutorService.start();\n\n        // when\n        workExecutorService.pause();\n\n        // then\n        inOrder.verify(bonitaExecutorService).shutdownAndEmptyQueue();\n        inOrder.verify(bonitaExecutorService).clearAllQueues();\n        inOrder.verify(bonitaExecutorService).awaitTermination(anyLong(), any(TimeUnit.class));\n    }\n\n    @Test\n    public void should_stop_shutdown_ThreadPool_and_not_clear_queue() throws InterruptedException {\n        final InOrder inOrder = inOrder(bonitaExecutorService);\n        // given\n        workExecutorService.start();\n\n        // when\n        workExecutorService.stop();\n\n        // then\n        inOrder.verify(bonitaExecutorService).shutdownAndEmptyQueue();\n        inOrder.verify(bonitaExecutorService).awaitTermination(anyLong(), any(TimeUnit.class));\n    }\n\n    @Test\n    public void pauseShouldStopWorkservice() throws SBonitaException {\n        // given\n        workExecutorService.start();\n\n        // when\n        workExecutorService.pause();\n\n        // then\n        assertThat(workExecutorService.isStopped()).as(\"WorkService should be deactivated\").isTrue();\n    }\n\n    @Test\n    public void resumeShouldAllowToExecuteWork() throws SBonitaException {\n        // given\n        workExecutorService.start();\n        workExecutorService.pause();\n\n        // when\n        workExecutorService.resume();\n        workExecutorService.execute(workDescriptor);\n\n        // then\n        verify(bonitaExecutorService, times(1)).submit(eq(workDescriptor));\n    }\n\n    @Test\n    public void should_start_do_nothing_when_already_started() {\n        // given\n        workExecutorService.start();\n\n        // when\n        workExecutorService.start();\n\n        // then: will only be started one time\n        verify(bonitaExecutorServiceFactory, times(1)).createExecutorService(workExecutorService);\n    }\n\n    @Test\n    public void should_stop_do_nothing_when_already_stopped() {\n        // given\n        workExecutorService.start();\n        workExecutorService.stop();\n\n        // when\n        workExecutorService.stop();\n\n        // then: will only be started one time\n        verify(bonitaExecutorService, times(1)).shutdownAndEmptyQueue();\n    }\n\n    @Test(expected = SWorkException.class)\n    public void should_pause_throw_exception_on_timeout() throws SWorkException, InterruptedException {\n        // given\n        workExecutorService.start();\n        doReturn(false).when(bonitaExecutorService).awaitTermination(anyLong(), any(TimeUnit.class));\n\n        // when\n        workExecutorService.pause();\n\n        // then: exception\n    }\n\n    @Test\n    public void should_stop_do_not_throw_exception_on_timeout() throws InterruptedException {\n        // given\n        workExecutorService.start();\n        doReturn(false).when(bonitaExecutorService).awaitTermination(anyLong(), any(TimeUnit.class));\n\n        // when\n        workExecutorService.stop();\n\n        // then: will only be started one time\n        assertThat(systemOutRule.getLog()).contains(\"Waited\");\n    }\n\n    @Test(expected = SWorkException.class)\n    public void should_pause_throw_exception_on_interrupted() throws InterruptedException, SBonitaException {\n        // given\n        workExecutorService.start();\n        doThrow(InterruptedException.class).when(bonitaExecutorService).awaitTermination(anyLong(),\n                any(TimeUnit.class));\n\n        // when\n        workExecutorService.pause();\n\n        // then: exception\n    }\n\n    @Test\n    public void should_stop_do_not_throw_exception_on_interrupted() throws InterruptedException {\n        // given\n        workExecutorService.start();\n        doThrow(InterruptedException.class).when(bonitaExecutorService).awaitTermination(anyLong(),\n                any(TimeUnit.class));\n\n        // when\n        workExecutorService.stop();\n\n        // then: will only be started one time\n\n        assertThat(systemOutRule.getLog()).contains(\"Interrupted\").contains(\"InterruptedException\");\n    }\n\n    @Test\n    public void checkStartStatus() {\n        // when\n        workExecutorService.start();\n\n        // then\n        assertThat(workExecutorService.isStopped()).isFalse();\n    }\n\n    @Test\n    public void checkStopStatus() {\n        // given\n        workExecutorService.start();\n\n        // when\n        workExecutorService.stop();\n\n        // then\n        assertThat(workExecutorService.isStopped()).isTrue();\n\n    }\n\n    @Test\n    public void should_reexecute_work_when_it_fails_because_of_lock_timeout() {\n        workExecutorService.onFailure(workDescriptor, bonitaWork, Collections.emptyMap(),\n                new LockTimeoutException(\"lock timeout\"));\n\n        verify(bonitaExecutorService).submit(eq(workDescriptor));\n    }\n\n    @Test\n    public void should_warn_and_reexecute_work_when_it_fails_because_of_lock_exception() {\n        workExecutorService.onFailure(workDescriptor, bonitaWork, Collections.emptyMap(),\n                new LockException(\"lock timeout\", new Exception()));\n\n        verify(bonitaExecutorService).submit(eq(workDescriptor));\n        assertThat(systemOutRule.getLog()).contains(\"WARN\");\n    }\n\n    @Test\n    public void should_log_on_success() {\n        workExecutorService.onSuccess(workDescriptor);\n\n        assertThat(systemOutRule.getLog()).contains(\"Completed work \");\n    }\n\n    @Test\n    public void should_await_specified_time_when_stopping_the_executor() throws Exception {\n\n        workExecutorService.stop();\n\n        verify(bonitaExecutorService).awaitTermination(WORK_TERMINATION_TIMEOUT, TimeUnit.SECONDS);\n    }\n\n    @Test\n    public void should_ignored_work_because_of_precondition_not_verified() {\n        workExecutorService.onFailure(workDescriptor, bonitaWork, Collections.emptyMap(),\n                new SWorkPreconditionException(\"My precondition\"));\n\n        verifyNoMoreInteractions(bonitaExecutorService);\n        assertThat(systemOutRule.getLog()).contains(\"Work was not executed because preconditions were not met,\");\n    }\n\n    @Test\n    public void should_execute_work_normally() {\n        WorkDescriptor workDescriptor = WorkDescriptor.create(\"MY_WORK\");\n\n        workExecutorService.execute(workDescriptor);\n\n        verify(bonitaExecutorService).submit(eq(workDescriptor));\n    }\n\n    @Test\n    public void should_reattempt_work_on_accepted_failure() throws Exception {\n        WorkDescriptor workDescriptor = WorkDescriptor.create(\"MY_WORK\");\n\n        workExecutorService.onFailure(workDescriptor, bonitaWork, emptyMap(),\n                new SRetryableException(new Exception(\"rootCause\")));\n\n        verify(bonitaExecutorService).submit(eq(workDescriptor));\n        verify(bonitaWork, never()).handleFailure(nullable(Exception.class), anyMap());\n        assertThat(workDescriptor.getRetryCount()).isEqualTo(1);\n    }\n\n    @Test\n    public void should_reattempt_work_on_when_issue_happened_during_commit() throws Exception {\n        WorkDescriptor workDescriptor = WorkDescriptor.create(\"MY_WORK\");\n        STransactionCommitException duringCommitException = new STransactionCommitException(\"exception during commit\");\n        doReturn(UNCERTAIN_COMPLETION_OF_COMMIT).when(retryabilityEvaluator)\n                .evaluateRetryability(duringCommitException);\n\n        workExecutorService.onFailure(workDescriptor, bonitaWork, emptyMap(), duringCommitException);\n\n        verify(bonitaExecutorService).submit(eq(workDescriptor));\n        verify(bonitaWork, never()).handleFailure(nullable(Exception.class), anyMap());\n        assertThat(workDescriptor.getRetryCount()).isEqualTo(1);\n    }\n\n    @Test\n    public void should_reset_retry_counter_on_non_retryable_failure() {\n        WorkDescriptor workDescriptor = WorkDescriptor.create(\"A_WORK\");\n\n        workExecutorService.onFailure(workDescriptor, bonitaWork, emptyMap(),\n                new SRetryableException(new Exception(\"root_cause\")));\n        assertThat(getNumberOfRetries()).isEqualTo(1);\n\n        workExecutorService.onFailure(workDescriptor, bonitaWork, emptyMap(),\n                new SRetryableException(new Exception(\"other_cause\")));\n        assertThat(getNumberOfRetries())\n                .as(\"Should not update counter for work that has already be counted\").isEqualTo(1);\n\n        workExecutorService.onFailure(workDescriptor, bonitaWork, emptyMap(),\n                new Exception(\"not retry-able\"));\n        assertThat(getNumberOfRetries()).isEqualTo(0);\n    }\n\n    @Test\n    public void should_reset_retry_counter_on_precondition_issue() {\n        WorkDescriptor workDescriptor = WorkDescriptor.create(\"A_WORK\");\n\n        workExecutorService.onFailure(workDescriptor, bonitaWork, emptyMap(),\n                new SRetryableException(new Exception(\"root_cause\")));\n        assertThat(getNumberOfRetries()).isEqualTo(1);\n\n        workExecutorService.onFailure(workDescriptor, bonitaWork, emptyMap(),\n                new SWorkPreconditionException(\"precondition\"));\n        assertThat(getNumberOfRetries()).isEqualTo(0);\n    }\n\n    @Test\n    public void should_reset_retry_counter_on_success() {\n        WorkDescriptor workDescriptor = WorkDescriptor.create(\"A_WORK\");\n\n        workExecutorService.onFailure(workDescriptor, bonitaWork, emptyMap(),\n                new SRetryableException(new Exception(\"root_cause\")));\n        assertThat(getNumberOfRetries()).isEqualTo(1);\n\n        workExecutorService.onSuccess(workDescriptor);\n        assertThat(getNumberOfRetries()).isEqualTo(0);\n    }\n\n    @Test\n    public void should_not_reattempt_work_on_other_failure() throws Exception {\n        WorkDescriptor workDescriptor = WorkDescriptor.create(\"MY_WORK\");\n\n        Exception rootCause = new Exception(\"rootCause\");\n        Map<String, Object> context = emptyMap();\n        workExecutorService.onFailure(workDescriptor, bonitaWork, context,\n                rootCause);\n\n        verify(bonitaExecutorService, never()).submit(eq(workDescriptor));\n        verify(bonitaWork).handleFailure(rootCause, context);\n        assertThat(workDescriptor.getRetryCount()).isEqualTo(0);\n    }\n\n    @Test\n    public void should_report_incident_when_we_are_unable_to_handle_the_failure_and_the_work_is_not_handled_by_the_recovery()\n            throws Exception {\n        WorkDescriptor workDescriptor = WorkDescriptor.create(\"MY_WORK\");\n        doThrow(new Exception(\"HandleFailureError\")).when(bonitaWork).handleFailure(any(), anyMap());\n\n        Exception rootCause = new Exception(\"rootCause\");\n        Map<String, Object> context = emptyMap();\n        workExecutorService.onFailure(workDescriptor, bonitaWork, context,\n                rootCause);\n\n        verify(bonitaWork).handleFailure(any(), anyMap());\n        verify(incidentService).report(any());\n    }\n\n    @Test\n    public void should_do_nothing_when_we_are_unable_to_handle_the_failure_and_the_work_is_handled_by_the_recovery()\n            throws Exception {\n        WorkDescriptor workDescriptor = WorkDescriptor.create(\"MY_WORK\");\n        doThrow(new Exception(\"HandleFailureError\")).when(bonitaWork).handleFailure(any(), anyMap());\n        doReturn(true).when(bonitaWork).canBeRecoveredByTheRecoveryMechanism();\n\n        Map<String, Object> context = emptyMap();\n        workExecutorService.onFailure(workDescriptor, bonitaWork, context, new Exception(\"rootCause\"));\n\n        verify(bonitaWork).handleFailure(any(), anyMap());\n        verify(incidentService, never()).report(any());\n    }\n\n    @Test\n    public void should_not_reattempt_work_when_retry_count_is_exceeded() throws Exception {\n        WorkDescriptor workDescriptor = WorkDescriptor.create(\"MY_WORK\");\n        //max retry is 3\n        workDescriptor.incrementRetryCount();\n        workDescriptor.incrementRetryCount();\n        workDescriptor.incrementRetryCount();\n        workDescriptor.incrementRetryCount();\n\n        Map<String, Object> context = emptyMap();\n        SRetryableException exception = new SRetryableException(new Exception(\"rootCause\"));\n        workExecutorService.onFailure(workDescriptor, bonitaWork, context,\n                exception);\n\n        verify(bonitaExecutorService, never()).submit(eq(workDescriptor));\n        verify(bonitaWork).handleFailure(exception, context);\n    }\n\n    @Test\n    public void should_set_the_date_after_delay_on_work_when_retrying() {\n        WorkDescriptor workDescriptor = WorkDescriptor.create(\"MY_WORK\");\n        SRetryableException exception = new SRetryableException(new Exception(\"rootCause\"));\n        Instant now = engineClock.now();\n\n        workExecutorService.onFailure(workDescriptor, bonitaWork, emptyMap(),\n                exception);\n\n        verify(bonitaExecutorService).submit(eq(workDescriptor));\n        assertThat(workDescriptor.getExecutionThreshold()).isAfterOrEqualTo(now.plusMillis(DELAY));\n        assertThat(workDescriptor.getExecutionThreshold()).isBeforeOrEqualTo(now.plusMillis(DELAY + DELAY));\n    }\n\n    @Test\n    public void should_log_the_delay_used_to_retry_the_work() {\n        WorkDescriptor workDescriptor = WorkDescriptor.create(\"MY_WORK\");\n        SRetryableException exception = new SRetryableException(new Exception(\"rootCause\"));\n        Instant now = engineClock.now();\n\n        systemOutRule.clearLog();\n        workExecutorService.onFailure(workDescriptor, bonitaWork, emptyMap(),\n                exception);\n\n        verify(bonitaExecutorService).submit(workDescriptor);\n        assertThat(workDescriptor.getExecutionThreshold()).isAfterOrEqualTo(now.plusMillis(DELAY));\n        assertThat(workDescriptor.getExecutionThreshold()).isBeforeOrEqualTo(now.plusMillis(DELAY + DELAY));\n        assertThat(systemOutRule.getLog()).contains(\"It will be retried. Attempt 1 of 4 with a delay of \"\n                + Duration.between(now, workDescriptor.getExecutionThreshold()).toMillis());\n    }\n\n    @Test\n    public void should_set_the_date_after_delay_and_factor_on_work_when_retrying_multiple_times() {\n        SRetryableException exception = new SRetryableException(new Exception(\"rootCause\"));\n        Instant now = engineClock.now();\n\n        workExecutorService.onFailure(workDescriptor, bonitaWork, emptyMap(), exception);\n        workExecutorService.onFailure(workDescriptor, bonitaWork, emptyMap(), exception);\n        workExecutorService.onFailure(workDescriptor, bonitaWork, emptyMap(), exception);\n\n        verify(bonitaExecutorService, times(3)).submit(eq(workDescriptor));\n        //date after delay is: delay * factor * factor because there is 3 retry\n        assertThat(workDescriptor.getExecutionThreshold())\n                .isAfterOrEqualTo(now.plusMillis(DELAY * DELAY_FACTOR * DELAY_FACTOR));\n        assertThat(workDescriptor.getExecutionThreshold())\n                .isBeforeOrEqualTo(now.plusMillis(DELAY * DELAY_FACTOR * DELAY_FACTOR * 2));\n    }\n\n    @Test\n    public void should_just_requeue_work_when_the_failure_is_a_work_lock_issue() throws Exception {\n        Exception rootCause = new LockTimeoutException(\"rootCause\");\n        Map<String, Object> context = emptyMap();\n        workExecutorService.onFailure(workDescriptor, bonitaWork, context,\n                rootCause);\n\n        verify(bonitaExecutorService).submit(eq(workDescriptor));\n        verify(bonitaWork, never()).handleFailure(rootCause, context);\n        assertThat(workDescriptor.getRetryCount()).isEqualTo(0);\n    }\n\n    @Test\n    public void should_abandon_execution_when_failure_is_a_precondition_issue() throws Exception {\n        Exception rootCause = new SWorkPreconditionException(\"precondition failed\");\n        Map<String, Object> context = emptyMap();\n        workExecutorService.onFailure(workDescriptor, bonitaWork, context,\n                rootCause);\n\n        verifyNoInteractions(bonitaExecutorService);\n        verify(bonitaWork, never()).handleFailure(rootCause, emptyMap());\n        assertThat(workDescriptor.getRetryCount()).isEqualTo(0);\n    }\n\n    @Test\n    public void should_retry_PreconditionException_when_retryability_is_RETRYABLE() throws Exception {\n        Exception rootCause = new SWorkPreconditionException(\"precondition failed\");\n        doReturn(RETRYABLE).when(retryabilityEvaluator).evaluateRetryability(rootCause);\n\n        workExecutorService.onFailure(workDescriptor, bonitaWork, emptyMap(), rootCause);\n\n        verify(bonitaExecutorService).submit(eq(workDescriptor));\n        verify(bonitaWork, never()).handleFailure(rootCause, emptyMap());\n        assertThat(workDescriptor.getRetryCount()).isEqualTo(1);\n    }\n\n    @Test\n    public void should_retry_PreconditionException_when_retryability_is_UNCERTAIN_COMPLETION_OF_COMMIT()\n            throws Exception {\n        Exception rootCause = new SWorkPreconditionException(\"precondition failed\");\n        doReturn(UNCERTAIN_COMPLETION_OF_COMMIT).when(retryabilityEvaluator).evaluateRetryability(rootCause);\n\n        workExecutorService.onFailure(workDescriptor, bonitaWork, emptyMap(), rootCause);\n\n        verify(bonitaExecutorService).submit(eq(workDescriptor));\n        verify(bonitaWork, never()).handleFailure(rootCause, emptyMap());\n        assertThat(workDescriptor.getRetryCount()).isEqualTo(1);\n    }\n\n    protected double getNumberOfRetries() {\n        return meterRegistry.find(NUMBER_OF_WORKS_RETRIED).gauge().value();\n    }\n}\n"
  },
  {
    "path": "services/bonita-work/src/test/java/org/bonitasoft/engine/work/SingleThreadPoolExecutorTest.java",
    "content": "/**\n * Copyright (C) 2017-2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.awaitility.Awaitility.await;\n\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Map;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.CompletionException;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport io.micrometer.core.instrument.Clock;\nimport io.micrometer.core.instrument.simple.SimpleMeterRegistry;\nimport org.bonitasoft.engine.commons.time.FixedEngineClock;\nimport org.bonitasoft.engine.work.audit.WorkExecutionAuditor;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\n/**\n * Tests for ThreadPoolExecutor with pool size 1 (single thread).\n * Documents that the pool correctly replaces its worker thread after exceptions,\n * so that subsequent work is not stuck forever.\n */\n@ExtendWith(MockitoExtension.class)\nclass SingleThreadPoolExecutorTest {\n\n    private static final long TENANT_ID = 13L;\n\n    @Mock\n    private WorkExecutionAuditor workExecutionAuditor;\n    private final ResettableWorkExecutionCallback workExecutionCallback = new ResettableWorkExecutionCallback();\n    private DefaultBonitaExecutorService bonitaExecutorService;\n    private final FixedEngineClock engineClock = new FixedEngineClock(Instant.now());\n    private final WorkFactory workFactory = new LocalWorkFactory(2);\n    private final SimpleMeterRegistry meterRegistry = new SimpleMeterRegistry(\n            // So that micrometer updates its counters every 1 ms:\n            k -> k.equals(\"simple.step\") ? Duration.ofMillis(1).toString() : null,\n            Clock.SYSTEM);\n\n    @BeforeEach\n    void setUp() {\n        var threadPoolExecutor = new WorkSingleThreadPoolExecutorFactory.SingleThreadPoolExecutor(\n                new LinkedBlockingQueue<>(10),\n                new WorkerThreadFactory(\"test-worker\", 1));\n        bonitaExecutorService = new DefaultBonitaExecutorService(threadPoolExecutor, workFactory, engineClock,\n                workExecutionCallback, workExecutionAuditor, meterRegistry);\n    }\n\n    @AfterEach\n    void tearDown() {\n        bonitaExecutorService.shutdownAndEmptyQueue();\n    }\n\n    @Test\n    void submit_should_continue_processing_after_sync_exception() {\n        // Submit failing work\n        bonitaExecutorService.submit(WorkDescriptor.create(\"EXCEPTION\"));\n        await().until(workExecutionCallback::isOnFailureCalled);\n\n        // Reset and submit normal work — must succeed even with pool size 1\n        workExecutionCallback.reset();\n        bonitaExecutorService.submit(WorkDescriptor.create(\"NORMAL\"));\n        await().until(workExecutionCallback::isOnSuccessCalled);\n    }\n\n    @Test\n    void submit_should_preserve_work_failure_handling() {\n        bonitaExecutorService.submit(WorkDescriptor.create(\"EXCEPTION\"));\n        await().until(workExecutionCallback::isOnFailureCalled);\n\n        assertThat(workExecutionCallback.getThrown())\n                .isInstanceOf(Exception.class)\n                .hasMessageContaining(\"classic exception\");\n\n        // Verify pool still works\n        workExecutionCallback.reset();\n        bonitaExecutorService.submit(WorkDescriptor.create(\"NORMAL\"));\n        await().until(workExecutionCallback::isOnSuccessCalled);\n    }\n\n    @Test\n    void submit_should_maintain_accurate_metrics_after_exception() {\n        // Submit failing work\n        bonitaExecutorService.submit(WorkDescriptor.create(\"EXCEPTION\"));\n        await().until(workExecutionCallback::isOnFailureCalled);\n\n        // Submit normal work\n        workExecutionCallback.reset();\n        bonitaExecutorService.submit(WorkDescriptor.create(\"NORMAL\"));\n        await().until(workExecutionCallback::isOnSuccessCalled);\n\n        assertThat(meterRegistry.find(DefaultBonitaExecutorService.NUMBER_OF_WORKS_EXECUTED).counter().count())\n                .as(\"Both works (failed + successful) should be counted as executed\")\n                .isEqualTo(2);\n    }\n\n    @Test\n    void submit_should_continue_processing_after_async_exception() {\n        // Submit async failing work\n        bonitaExecutorService.submit(WorkDescriptor.create(\"ASYNC_EXCEPTION\"));\n        await().until(workExecutionCallback::isOnFailureCalled);\n\n        // Reset and submit normal work — must succeed even with pool size 1\n        workExecutionCallback.reset();\n        bonitaExecutorService.submit(WorkDescriptor.create(\"NORMAL\"));\n        await().until(workExecutionCallback::isOnSuccessCalled);\n    }\n\n    // =================================================================================================================\n    // UTILS\n    // =================================================================================================================\n\n    private static class ResettableWorkExecutionCallback implements WorkExecutionCallback {\n\n        private final AtomicBoolean onSuccessCalled = new AtomicBoolean(false);\n        private final AtomicBoolean onFailureCalled = new AtomicBoolean(false);\n        private volatile Throwable thrown;\n\n        @Override\n        public void onSuccess(WorkDescriptor workDescriptor) {\n            onSuccessCalled.set(true);\n        }\n\n        @Override\n        public void onFailure(WorkDescriptor work, BonitaWork bonitaWork, Map<String, Object> context,\n                Throwable thrown) {\n            this.thrown = thrown;\n            onFailureCalled.set(true);\n        }\n\n        boolean isOnSuccessCalled() {\n            return onSuccessCalled.get();\n        }\n\n        boolean isOnFailureCalled() {\n            return onFailureCalled.get();\n        }\n\n        Throwable getThrown() {\n            return thrown;\n        }\n\n        void reset() {\n            onSuccessCalled.set(false);\n            onFailureCalled.set(false);\n            thrown = null;\n        }\n    }\n\n    private static class LocalWorkFactory implements WorkFactory {\n\n        private final long workSleepPeriodInSeconds;\n\n        private LocalWorkFactory(long workSleepPeriodInSeconds) {\n            this.workSleepPeriodInSeconds = workSleepPeriodInSeconds;\n        }\n\n        @Override\n        public BonitaWork create(WorkDescriptor workDescriptor) {\n            return new BonitaWork() {\n\n                @Override\n                public String getDescription() {\n                    return workDescriptor.toString();\n                }\n\n                @Override\n                public CompletableFuture<Void> work(Map<String, Object> context) throws Exception {\n                    switch (workDescriptor.getType()) {\n                        case \"EXCEPTION\":\n                            throw new Exception(\"classic exception\");\n                        case \"SLEEP\":\n                            TimeUnit.SECONDS.sleep(workSleepPeriodInSeconds);\n                            break;\n                        case \"ASYNC\":\n                            return CompletableFuture.supplyAsync(() -> {\n                                try {\n                                    TimeUnit.MILLISECONDS.sleep(200);\n                                } catch (InterruptedException ignored) {\n                                }\n                                return null;\n                            }, Executors.newSingleThreadExecutor());\n                        case \"ASYNC_EXCEPTION\":\n                            return CompletableFuture.supplyAsync(() -> {\n                                try {\n                                    TimeUnit.MILLISECONDS.sleep(200);\n                                } catch (InterruptedException ignored) {\n                                }\n                                throw new CompletionException(new SWorkException(\"my exception\"));\n                            }, Executors.newSingleThreadExecutor());\n                        case \"NORMAL\":\n                        default:\n                    }\n                    return CompletableFuture.completedFuture(null);\n                }\n\n                @Override\n                public void handleFailure(Throwable e, Map<String, Object> context) {\n                    // do nothing\n                }\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "services/bonita-work/src/test/java/org/bonitasoft/engine/work/WorkServiceImplTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.*;\n\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\n\nimport org.bonitasoft.engine.commons.exceptions.SBonitaException;\nimport org.bonitasoft.engine.commons.time.EngineClock;\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class WorkServiceImplTest {\n\n    private WorkServiceImpl workService;\n    @Mock\n    private UserTransactionService transactionService;\n    @Mock\n    private SessionAccessor sessionAccessor;\n    @Mock\n    private WorkExecutorService workExecutorService;\n    @Mock\n    private EngineClock engineClock;\n\n    @Before\n    public void before() {\n        workService = new WorkServiceImpl(transactionService, sessionAccessor, workExecutorService,\n                engineClock, 0);\n    }\n\n    @Test\n    public void should_register_work_set_the_work_registration_instant()\n            throws SBonitaException {\n        Instant registrationInstant = Instant.now().minus(25, ChronoUnit.HOURS);\n        doReturn(registrationInstant).when(engineClock).now();\n        // given\n        WorkDescriptor workDescriptor = WorkDescriptor.create(\"MY_WORK\");\n\n        // when\n        workService.registerWork(workDescriptor);\n\n        // then\n        assertThat(workDescriptor.getRegistrationDate()).isEqualTo(registrationInstant);\n    }\n\n    @Test\n    public void should_register_a_new_synchronization_for_each_work() throws SBonitaException {\n        // given\n        WorkDescriptor workDescriptor1 = WorkDescriptor.create(\"MY_WORK1\");\n        WorkDescriptor workDescriptor2 = WorkDescriptor.create(\"MY_WORK2\");\n\n        // when\n        workService.registerWork(workDescriptor1);\n        workService.registerWork(workDescriptor2);\n\n        // then\n        verify(transactionService).registerBonitaSynchronization(\n                argThat(s -> ((WorkSynchronization) s).getWork().getType().equals(\"MY_WORK1\")));\n        verify(transactionService).registerBonitaSynchronization(\n                argThat(s -> ((WorkSynchronization) s).getWork().getType().equals(\"MY_WORK2\")));\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-work/src/test/java/org/bonitasoft/engine/work/WorkSynchronizationTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work;\n\nimport static javax.transaction.Status.STATUS_COMMITTED;\nimport static javax.transaction.Status.STATUS_ROLLEDBACK;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Optional;\n\nimport org.bonitasoft.engine.sessionaccessor.SessionAccessor;\nimport org.bonitasoft.engine.transaction.UserTransactionService;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.MockitoJUnitRunner;\n\n/**\n * @author Baptiste Mesta.\n */\n@RunWith(MockitoJUnitRunner.class)\npublic class WorkSynchronizationTest {\n\n    @Mock\n    private WorkExecutorService workExecutorService;\n    @Mock\n    private SessionAccessor sessionAccessor;\n    @Mock\n    private UserTransactionService userTransactionService;\n\n    private final WorkDescriptor workDescriptor1 = WorkDescriptor.create(\"myWork1\");\n\n    @Test\n    public void should_submit_work_on_commit() {\n        WorkSynchronization workSynchronization = new WorkSynchronization(userTransactionService, workExecutorService,\n                workDescriptor1, 0);\n\n        workSynchronization.afterCompletion(STATUS_COMMITTED);\n\n        verify(workExecutorService).execute(workDescriptor1);\n    }\n\n    @Test\n    public void should_not_submit_work_on_transaction_not_in_committed_state() {\n        WorkSynchronization workSynchronization = new WorkSynchronization(userTransactionService, workExecutorService,\n                workDescriptor1, 0);\n        workSynchronization.afterCompletion(STATUS_ROLLEDBACK);\n\n        verify(workExecutorService, never()).execute(workDescriptor1);\n    }\n\n    @Test\n    public void should_not_add_delay_when_the_workDelayOnMultipleXAResource_equal_0() {\n        WorkSynchronization workSynchronization = new WorkSynchronization(userTransactionService, workExecutorService,\n                workDescriptor1, 0);\n\n        workSynchronization.afterCompletion(STATUS_COMMITTED);\n        assertThat(workDescriptor1.getExecutionThreshold()).isNull();\n        verify(workExecutorService).execute(workDescriptor1);\n    }\n\n    @Test\n    public void should_add_delay_when_the_workDelayOnMultipleXAResource_greater_than_0_and_multiple_resources() {\n        WorkSynchronization workSynchronization = new WorkSynchronization(userTransactionService, workExecutorService,\n                workDescriptor1, 10);\n        when(userTransactionService.hasMultipleResources()).thenReturn(Optional.of(true));\n\n        workSynchronization.afterCompletion(STATUS_COMMITTED);\n        assertThat(workDescriptor1.getExecutionThreshold()).isNotNull();\n        verify(workExecutorService).execute(workDescriptor1);\n    }\n\n    @Test\n    public void should_not_add_delay_when_the_workDelayOnMultipleXAResource_greater_than_0_and_no_multiple_resources() {\n        WorkSynchronization workSynchronization = new WorkSynchronization(userTransactionService, workExecutorService,\n                workDescriptor1, 10);\n        when(userTransactionService.hasMultipleResources()).thenReturn(Optional.of(false));\n\n        workSynchronization.afterCompletion(STATUS_COMMITTED);\n        assertThat(workDescriptor1.getExecutionThreshold()).isNull();\n        verify(workExecutorService).execute(workDescriptor1);\n    }\n\n    @Test\n    public void should_add_delay_when_the_workDelayOnMultipleXAResource_greater_than_0_and_multiple_resources_not_defined() {\n        WorkSynchronization workSynchronization = new WorkSynchronization(userTransactionService, workExecutorService,\n                workDescriptor1, 10);\n        when(userTransactionService.hasMultipleResources()).thenReturn(Optional.empty());\n\n        workSynchronization.afterCompletion(STATUS_COMMITTED);\n        assertThat(workDescriptor1.getExecutionThreshold()).isNotNull();\n        verify(workExecutorService).execute(workDescriptor1);\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-work/src/test/java/org/bonitasoft/engine/work/WorkerThreadFactoryTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work;\n\nimport static org.bonitasoft.engine.work.WorkerThreadFactory.guessPadding;\nimport static org.junit.Assert.assertEquals;\n\nimport org.junit.Test;\n\npublic class WorkerThreadFactoryTest {\n\n    @Test\n    public void newThreadStartsWithName() {\n        WorkerThreadFactory threadFactory = new WorkerThreadFactory(\"foo\", 1);\n        Thread newThread = threadFactory.newThread(buildRunnable());\n        assertEquals(\"foo-1\", newThread.getName());\n    }\n\n    @Test\n    public void newThreadNameNumberIsPadded() {\n        WorkerThreadFactory threadFactory = new WorkerThreadFactory(\"foo\", 100);\n        Thread newThread = threadFactory.newThread(buildRunnable());\n        assertEquals(\"foo-001\", newThread.getName());\n    }\n\n    @Test\n    public void testPadding() {\n        assertEquals(1, guessPadding(1));\n        assertEquals(2, guessPadding(10));\n        assertEquals(3, guessPadding(100));\n        assertEquals(4, guessPadding(1000));\n    }\n\n    /**\n     * @return\n     */\n    private Runnable buildRunnable() {\n        return new Runnable() {\n\n            @Override\n            public void run() {\n                // TODO Auto-generated method stub\n\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "services/bonita-work/src/test/java/org/bonitasoft/engine/work/audit/WorkExecutionAuditorTest.java",
    "content": "/**\n * Copyright (C) 2019 Bonitasoft S.A.\n * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble\n * This library is free software; you can redistribute it and/or modify it under the terms\n * of the GNU Lesser General Public License as published by the Free Software Foundation\n * version 2.1 of the License.\n * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n * See the GNU Lesser General Public License for more details.\n * You should have received a copy of the GNU Lesser General Public License along with this\n * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth\n * Floor, Boston, MA 02110-1301, USA.\n **/\npackage org.bonitasoft.engine.work.audit;\n\nimport static java.time.Instant.now;\nimport static java.time.temporal.ChronoUnit.*;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.bonitasoft.engine.work.audit.ExecutionStatus.*;\nimport static org.mockito.Mockito.*;\n\nimport java.time.Instant;\nimport java.util.stream.IntStream;\n\nimport org.bonitasoft.engine.commons.time.FixedEngineClock;\nimport org.bonitasoft.engine.work.WorkDescriptor;\nimport org.bonitasoft.engine.work.audit.WorkExecutionAuditor.ExecutionCountCheckConfig;\nimport org.bonitasoft.engine.work.audit.WorkExecutionAuditor.RegistrationDurationElapsedCheckConfig;\nimport org.junit.Test;\nimport org.mockito.InOrder;\n\npublic class WorkExecutionAuditorTest {\n\n    // =================================================================================================================\n    // execution status computation\n    // =================================================================================================================\n\n    @Test\n    public void should_execution_status_be_ok_when_no_threshold_is_reached() {\n        // given:\n        WorkExecutionAuditor auditor = new WorkExecutionAuditor(new FixedEngineClock(now()), new AuditListener(),\n                new RegistrationDurationElapsedCheckConfig(3, DAYS), new ExecutionCountCheckConfig(50, 3, DAYS));\n\n        // when:\n        final ExecutionStatus executionStatus = auditor.executionStatus(workDescriptor(now(), 10));\n\n        // then:\n        assertThat(executionStatus).isEqualTo(OK);\n    }\n\n    @Test\n    public void should_execution_status_be_ko_when_a_large_number_of_executions_have_been_performed() {\n        // given:\n        WorkExecutionAuditor auditor = new WorkExecutionAuditor(new FixedEngineClock(now()), new AuditListener(),\n                new RegistrationDurationElapsedCheckConfig(3, DAYS), new ExecutionCountCheckConfig(50, 3, MILLIS));\n\n        // when:\n        final ExecutionStatus executionStatus = auditor.executionStatus(workDescriptor(now().minus(12, MINUTES), 200));\n\n        // then:\n        assertThat(executionStatus).isEqualTo(TOO_MANY_EXECUTIONS);\n    }\n\n    @Test\n    public void should_execution_status_be_ok_when_a_large_number_of_executions_have_been_performed_but_execution_count_duration_threshold_is_not_elapsed() {\n        // given:\n        WorkExecutionAuditor auditor = new WorkExecutionAuditor(new FixedEngineClock(now()), new AuditListener(),\n                new RegistrationDurationElapsedCheckConfig(3, DAYS), new ExecutionCountCheckConfig(50, 3, HOURS));\n\n        // when:\n        final ExecutionStatus executionStatus = auditor.executionStatus(workDescriptor(now().minus(12, MINUTES), 200));\n\n        // then:\n        assertThat(executionStatus).isEqualTo(OK);\n    }\n\n    @Test\n    public void should_execution_status_be_ko_when_too_much_time_elapsed_since_work_registration() {\n        // given:\n        final Instant registrationDate = now().minus(12, MINUTES);\n        WorkExecutionAuditor auditor = new WorkExecutionAuditor(new FixedEngineClock(registrationDate.plusSeconds(250)),\n                new AuditListener(), new RegistrationDurationElapsedCheckConfig(30, MILLIS),\n                new ExecutionCountCheckConfig(50, 3, DAYS));\n        // when:\n        final ExecutionStatus executionStatus = auditor.executionStatus(workDescriptor(registrationDate, 1));\n\n        // then:\n        assertThat(executionStatus).isEqualTo(TOO_MUCH_TIME_ELAPSED_SINCE_REGISTRATION);\n    }\n\n    // =================================================================================================================\n    // detection of abnormal execution\n    // =================================================================================================================\n\n    @Test\n    public void should_trigger_listener_when_execution_status_is_abnormal() {\n        //given:\n        final AuditListener auditListener = mock(AuditListener.class);\n        WorkExecutionAuditor auditor = auditorDetectingTooMuchExecutions(auditListener);\n        final WorkDescriptor work = workDescriptor(now().minus(12, MINUTES), 10_000);\n\n        //when:\n        auditor.detectAbnormalExecutionAndNotify(work);\n\n        //then:\n        final InOrder inOrder = inOrder(auditListener);\n        inOrder.verify(auditListener).detectionStarted(work);\n        inOrder.verify(auditListener).abnormalExecutionStatusDetected(work, TOO_MANY_EXECUTIONS);\n        inOrder.verifyNoMoreInteractions();\n    }\n\n    @Test\n    public void should_not_trigger_listener_when_work_has_already_been_detected_with_abnormal_execution() {\n        //given:\n        final AuditListener auditListener = mock(AuditListener.class);\n        WorkExecutionAuditor auditor = auditorDetectingTooMuchExecutions(auditListener);\n        final WorkDescriptor work = workDescriptor();\n\n        //when:\n        auditor.detectAbnormalExecutionAndNotify(work);\n\n        //then:\n        final InOrder inOrder1stRun = inOrder(auditListener);\n        inOrder1stRun.verify(auditListener).detectionStarted(work);\n        inOrder1stRun.verify(auditListener).abnormalExecutionStatusDetected(work, TOO_MANY_EXECUTIONS);\n        inOrder1stRun.verifyNoMoreInteractions();\n\n        //when:\n        reset(auditListener); // forget about previous calls\n        auditor.detectAbnormalExecutionAndNotify(work);\n\n        //then:\n        final InOrder inOrder2ndRun = inOrder(auditListener);\n        inOrder2ndRun.verify(auditListener).detectionStarted(work);\n        inOrder2ndRun.verifyNoMoreInteractions();\n    }\n\n    // =================================================================================================================\n    // notify success\n    // =================================================================================================================\n\n    @Test\n    public void should_not_notify_success_when_execution_is_normal() {\n        //given:\n        final AuditListener auditListener = mock(AuditListener.class);\n        final WorkDescriptor work = abnormallyExecutedWorkDescriptor();\n\n        //when:\n        auditorDetectingTooMuchExecutions(auditListener).notifySuccess(work);\n\n        //then:\n        verify(auditListener).success(work);\n        verifyNoMoreInteractions(auditListener);\n    }\n\n    @Test\n    public void should_notify_success_when_execution_is_normal() {\n        //given:\n        final AuditListener auditListener = mock(AuditListener.class);\n\n        //when:\n        auditorDetectingTooMuchExecutions(auditListener).notifySuccess(workDescriptor());\n\n        //then:\n        verifyNoMoreInteractions(auditListener);\n    }\n\n    // =================================================================================================================\n    // UTILS\n    // =================================================================================================================\n\n    private static WorkDescriptor workDescriptor(Instant registrationDate, int executionCount) {\n        WorkDescriptor workDescriptor = WorkDescriptor.create(\"NORMAL\");\n        workDescriptor.setRegistrationDate(registrationDate);\n        IntStream.rangeClosed(1, executionCount).forEach(i -> workDescriptor.incrementExecutionCount());\n        return workDescriptor;\n    }\n\n    private static WorkDescriptor abnormallyExecutedWorkDescriptor() {\n        return workDescriptor().abnormalExecutionDetected();\n    }\n\n    private static WorkDescriptor workDescriptor() {\n        return workDescriptor(now().minus(10, SECONDS), 3);\n    }\n\n    private static WorkExecutionAuditor auditorDetectingTooMuchExecutions(AuditListener auditListener) {\n        return new WorkExecutionAuditor(\n                new FixedEngineClock(now().plus(250, DAYS)),\n                auditListener,\n                new RegistrationDurationElapsedCheckConfig(30, MILLIS),\n                new ExecutionCountCheckConfig(0, 3, MILLIS));\n    }\n\n}\n"
  },
  {
    "path": "services/bonita-work/src/test/resources/logback-test.xml",
    "content": "<configuration>\n\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->\n        <encoder>\n            <pattern>|%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <!--<logger name=\"org.bonitasoft\" level=\"INFO\" />-->\n    <logger name=\"org.bonitasoft.engine.work\" level=\"INFO\" />\n    <logger name=\"org.bonitasoft.engine.work.RetryingWorkExecutorService\" level=\"DEBUG\" />\n    <logger name=\"BONITA_WORK_AUDIT.EXECUTION\" level=\"INFO\" />\n\n    <root level=\"WARN\">\n        <appender-ref ref=\"STDOUT\" />\n    </root>\n\n</configuration>\n"
  },
  {
    "path": "settings.gradle",
    "content": "rootProject.name = 'bonita-engine'\n\n\napply from: 'engine-settings.gradle'\n"
  }
]